Module: Protege::ToolScoped

Extended by:
ActiveSupport::Concern
Included in:
Persona
Defined in:
app/models/concerns/protege/tool_scoped.rb

Overview

Per-persona tool scoping — mixed into Persona to bound which tools an agent may use.

A persona's effective tools are the code-declared grant (the ceiling, set by the ClassMethods#tools macro) minus the operator's disabled_tool_ids override, and never wider than the grant. Two layers, deliberately different in reach:

  • The grant lives on a class_attribute so it survives Active Record's schema-loading reloads (the same reason Persona._display_name is one). Unlike the resolver chains — per-subclass +@ivar+s that never inherit — the grant inherits down the STI tree, so a base persona narrows a whole family's ceiling unless a subclass declares its own. A nil grant means "every registered tool", so an unconfigured persona keeps the full catalogue (the pre-scoping behaviour).
  • disabled_tool_ids is a dashboard-editable JSON array on the row (text column; SQLite has no native JSON type), normalised to a clean string array so it is never nil. It can only subtract from the grant, never add to it.

#available_tools resolves both layers against the live registry, so a stale grant or disabled id — naming a tool since deleted from the code — is simply skipped rather than conjuring or breaking a tool.

Instance Method Summary collapse

Instance Method Details

#available_tool_idsArray<Symbol>

The ids of this persona's effective tools — a convenience over #available_tools for UI and tests.

Returns:

  • (Array<Symbol>)

    the effective tool ids



74
75
76
# File 'app/models/concerns/protege/tool_scoped.rb', line 74

def available_tool_ids
  available_tools.map(&:id)
end

#available_toolsArray<Class>

Return the tool classes this persona may currently use — its effective, scoped catalogue.

Computed as one filter over the live registry (+ToolMixin.registered+): drop anything the operator has switched off in disabled_tool_ids, then keep only what the subclass's grant permits (or everything, when the grant is nil). Iterating the registry rather than the grant means a grant id with no matching registered tool is simply skipped — a stale scope can never conjure or break a tool.

Returns:

  • (Array<Class>)

    the tool classes offered to the model for this persona



62
63
64
65
66
67
68
69
# File 'app/models/concerns/protege/tool_scoped.rb', line 62

def available_tools
  disabled = disabled_tool_ids.map(&:to_sym)
  grant    = self.class.tool_grant

  ToolMixin.registered
           .reject { |tool| disabled.include?(tool.id) }
           .select { |tool| grant.nil? || grant.include?(tool.id) }
end