Class: Insika::Tools::Subagent

Inherits:
RubyLLM::Tool
  • Object
show all
Defined in:
lib/insika/tools/subagent.rb

Overview

In-process delegation to a CHILD agent — the Flue session.task() primitive. A system tool (like remember/load_skill): wired by the ChatBuilder ONLY when profile.subagents is present, so require "ruby_llm" stays in this file (loaded lazily in create_chat). NOT enveloped (system tool) — in the synchronous mode the child lives in the parent's envelope and re-runs on the parent's resume.

It holds no delegation logic itself: execute reads the parent TurnState (the subagents allowlist + resolved model for inheritance + depth) and hands off to Executor#run_subagent, which spawns the isolated child turn and returns its result. The child result = its text + the linked child session id (R3).

Instance Method Summary collapse

Constructor Details

#initialize(runner:, state:) ⇒ Subagent

Returns a new instance of Subagent.



35
36
37
38
39
40
# File 'lib/insika/tools/subagent.rb', line 35

def initialize(runner:, state:)
  @runner = runner
  @state = state
  @allowed = Array(state.profile.subagents).map(&:to_s)
  super()
end

Instance Method Details

#descriptionObject

The parent's allowlist is per-TURN data, so it is named per instance: the ids go into the description AND as an enum on agent. Measured, not guessed — with only "must be one this agent may spawn" in the schema, a real provider answered "let me check which agents are available" and then did the work itself instead of delegating. A model cannot call what it cannot name.



48
49
50
51
52
# File 'lib/insika/tools/subagent.rb', line 48

def description
  return super if @allowed.empty?

  "#{super} Agents you may spawn: #{@allowed.join(', ')}."
end

#execute(agent:, message:, async: false) ⇒ Object

The child result is returned to the model as the tool result. On error we return { error: } (never raise) — a bad agent/depth/child failure is a message to the model, not a turn-killer (parity with A2ARemote).



61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/insika/tools/subagent.rb', line 61

def execute(agent:, message:, async: false)
  result = @runner.run_subagent(agent: agent.to_s, message: message.to_s,
                                parent_state: @state, async: async == true)
  return { error: result[:error] } if result[:error]

  # async dispatch: the ack (the child result arrives later as a new turn).
  return { dispatched: true, agent: result[:agent], session_id: result[:session_id] } if result[:dispatched]

  # sync: link the child session id alongside the text so a multi-step parent
  # can reference it and the transcript stays auditable (R3).
  { text: result[:text], session_id: result[:session_id] }
end

#nameObject

otherwise RubyLLM derives "insika--tools--subagent" from the class name.



33
# File 'lib/insika/tools/subagent.rb', line 33

def name = "spawn_subagent"

#params_schemaObject



54
55
56
# File 'lib/insika/tools/subagent.rb', line 54

def params_schema
  @agent_enum_schema ||= Insika::Tools::AgentEnum.inject(super, @allowed, path: %i[agent])
end