Class: Tools::SpawnSpecialist

Inherits:
Base
  • Object
show all
Includes:
SubagentPrompts
Defined in:
lib/tools/spawn_specialist.rb

Overview

Spawns a named specialist sub-agent from the agent registry. The specialist has a predefined system prompt and tool set defined in its Markdown definition file under agents/.

Nickname assignment is handled by the Melete::Runner which runs synchronously at spawn time, generating a unique nickname based on the task — same as generic sub-agents.

Results are delivered through natural text messages routed by Events::Subscribers::SubagentMessageRouter.

Constant Summary

Constants included from SubagentPrompts

Tools::SubagentPrompts::COMMUNICATION_INSTRUCTION, Tools::SubagentPrompts::IDENTITY_TEMPLATE, Tools::SubagentPrompts::PROMPT_GUIDELINES

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

schema, truncation_threshold

Constructor Details

#initialize(session:, agent_registry: nil, tool_use_id: nil) ⇒ SpawnSpecialist

Returns a new instance of SpawnSpecialist.

Parameters:

  • session (Session)

    the parent session spawning the specialist

  • agent_registry (Agents::Registry, nil) (defaults to: nil)

    injectable for testing

  • tool_use_id (String, nil) (defaults to: nil)

    the invoking spawn_specialist tool_call’s pairing id, captured so the spawn pair can later be located by the HUD visibility sweep in Mneme::Runner



69
70
71
72
73
# File 'lib/tools/spawn_specialist.rb', line 69

def initialize(session:, agent_registry: nil, tool_use_id: nil, **)
  @session = session
  @agent_registry = agent_registry || Agents::Registry.instance
  @tool_use_id = tool_use_id
end

Class Method Details

.descriptionObject

Builds description dynamically to include available specialists.



23
24
25
26
27
28
29
30
31
32
33
# File 'lib/tools/spawn_specialist.rb', line 23

def self.description
  base = "Need a specific skill set for the job? Bring in a specialist. " \
    "Its messages appear as tool responses in your conversation. " \
    "Prefix its nickname with @ to send instructions."

  registry = Agents::Registry.instance
  return base unless registry.any?

  specialist_list = registry.catalog.map { |name, desc| "- #{name}: #{desc}" }.join("\n")
  "#{base}\n\nAvailable specialists:\n#{specialist_list}"
end

.input_schemaObject

Builds input schema dynamically to include named agent enum.



40
41
42
43
44
45
46
47
48
49
# File 'lib/tools/spawn_specialist.rb', line 40

def self.input_schema
  {
    type: "object",
    properties: {
      name: name_property,
      task: {type: "string", description: "State the goal — the specialist knows its method."}
    },
    required: %w[name task]
  }
end

.prompt_guidelinesObject



37
# File 'lib/tools/spawn_specialist.rb', line 37

def self.prompt_guidelines = SubagentPrompts::PROMPT_GUIDELINES

.prompt_snippetObject



35
# File 'lib/tools/spawn_specialist.rb', line 35

def self.prompt_snippet = "Bring in a specialist by skill set. Reachable later via @."

.tool_nameObject



20
# File 'lib/tools/spawn_specialist.rb', line 20

def self.tool_name = "spawn_specialist"

Instance Method Details

#execute(input) ⇒ String, Hash{Symbol => String}

Creates a child session with the specialist’s predefined prompt and tools, pins the task as a Goal, and enqueues the task as the child’s first user_message PendingMessage — which kicks the standard inbound pipeline (Melete → (Mneme) → StartProcessing →DrainJob) so the specialist self-starts the same way a human-typed message would. Returns immediately after Melete completes.

Parameters:

  • input (Hash<String, Object>)

    with “name” and “task”

Returns:

  • (String)

    confirmation with child session ID

  • (Hash{Symbol => String})

    with :error key on validation failure



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/tools/spawn_specialist.rb', line 85

def execute(input)
  task = input["task"].to_s.strip
  name = input["name"].to_s.strip

  return {error: "Name cannot be blank"} if name.empty?
  return {error: "Task cannot be blank"} if task.empty?

  definition = @agent_registry.get(name)
  return {error: "Unknown agent: #{name}"} unless definition

  child = spawn_child(definition, task)
  nickname = child.name
  "Specialist #{nickname} spawned (session #{child.id}). " \
    "Its messages will appear in your conversation. " \
    "To address it, prefix its name with @ in your message."
end