Class: Pikuri::Agent::Configurator

Inherits:
Object
  • Object
show all
Defined in:
lib/pikuri/agent/configurator.rb

Overview

Build-time collector yielded into the Pikuri::Agent.new block. Hosts and Extension implementations call its methods to declare additional tools, listeners, system-prompt snippets, on_close handlers, and extension instances; #initialize drains the collected state into the agent’s final wiring before returning.

Why this exists

Splits “configure the agent” from “the agent’s runtime state”. Hosts can write a block that reads cleanly:

Pikuri::Agent.new(transport: ..., system_prompt: ...) do |c|
  c.add_listener Pikuri::Agent::Listener::Terminal.new
  c.add_tool     Pikuri::Tool::CALCULATOR
  c.add_extension Pikuri::Skill::Extension.new(catalog: catalog)
end

Extensions implement their configure© hook against the same type, so the call sites for “block users add stuff” and “extensions add stuff” share one API.

Lifecycle

One Configurator per Agent.new invocation. The Configurator is constructed inside #initialize, yielded to the block (if any), then its collected state is drained by the Agent body. The Configurator instance is discarded once Agent.new returns — it carries no runtime state.

Defined Under Namespace

Classes: SubAgentRequest

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(transport:, system_prompt_base:, name:, streaming:, step_limit:, cancellable:, interloper:) ⇒ Configurator

Returns a new instance of Configurator.

Parameters:



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/pikuri/agent/configurator.rb', line 117

def initialize(transport:, system_prompt_base:, name:, streaming:,
               step_limit:, cancellable:, interloper:)
  @transport = transport
  @system_prompt_base = system_prompt_base
  @name = name
  @streaming = streaming
  @step_limit = step_limit
  @cancellable = cancellable
  @interloper = interloper

  @tools = []
  @listeners = []
  @system_prompt_additions = []
  @on_close_handlers = []
  @extensions = []
end

Instance Attribute Details

#cancellableControl::Cancellable? (readonly)

Returns cancellation control passed to the Agent ctor, or nil. Extensions that run sub-LLM calls during configure (e.g. an MCP description synthesizer) share this so a user cancel during boot propagates correctly.

Returns:

  • (Control::Cancellable, nil)

    cancellation control passed to the Agent ctor, or nil. Extensions that run sub-LLM calls during configure (e.g. an MCP description synthesizer) share this so a user cancel during boot propagates correctly.



64
65
66
# File 'lib/pikuri/agent/configurator.rb', line 64

def cancellable
  @cancellable
end

#extensionsArray<#configure> (readonly)

Returns extension instances added via #add_extension, in declaration order. The Agent ctor walks this list and calls bind(self) on each after wiring is complete.

Returns:

  • (Array<#configure>)

    extension instances added via #add_extension, in declaration order. The Agent ctor walks this list and calls bind(self) on each after wiring is complete.



94
95
96
# File 'lib/pikuri/agent/configurator.rb', line 94

def extensions
  @extensions
end

#interloperControl::Interloper? (readonly)

Returns mid-loop user-input queue passed to the Agent ctor, or nil.

Returns:



68
69
70
# File 'lib/pikuri/agent/configurator.rb', line 68

def interloper
  @interloper
end

#listenersArray<Listener::Base> (readonly)

Returns listeners added via #add_listener, in declaration order. Drained by Pikuri::Agent#initialize.

Returns:



77
78
79
# File 'lib/pikuri/agent/configurator.rb', line 77

def listeners
  @listeners
end

#nameString (readonly)

Returns this agent’s identifier; empty for the main agent, hierarchical (+“sub_agent 0_1”+) for sub-agents.

Returns:

  • (String)

    this agent’s identifier; empty for the main agent, hierarchical (+“sub_agent 0_1”+) for sub-agents.



49
50
51
# File 'lib/pikuri/agent/configurator.rb', line 49

def name
  @name
end

#on_close_handlersArray<Proc> (readonly)

Returns on_close handlers added via #on_close, in declaration order. Fired by Pikuri::Agent#close in LIFO order with per-handler rescue.

Returns:

  • (Array<Proc>)

    on_close handlers added via #on_close, in declaration order. Fired by Pikuri::Agent#close in LIFO order with per-handler rescue.



88
89
90
# File 'lib/pikuri/agent/configurator.rb', line 88

def on_close_handlers
  @on_close_handlers
end

#step_limitControl::StepLimit? (readonly)

Returns step-budget control passed to the Agent ctor, or nil.

Returns:



57
58
59
# File 'lib/pikuri/agent/configurator.rb', line 57

def step_limit
  @step_limit
end

#streamingBoolean (readonly)

Returns true when the agent opted into chunk-level streaming.

Returns:

  • (Boolean)

    true when the agent opted into chunk-level streaming.



53
54
55
# File 'lib/pikuri/agent/configurator.rb', line 53

def streaming
  @streaming
end

#sub_agent_requestSubAgentRequest? (readonly)

Returns set when the block called #allow_sub_agent; nil otherwise. The Agent ctor uses this to decide whether to create a Tool::SubAgent instance after the chat is wired (so the SubAgent tool’s parent.tools snapshot doesn’t include itself —recursion guard).

Returns:

  • (SubAgentRequest, nil)

    set when the block called #allow_sub_agent; nil otherwise. The Agent ctor uses this to decide whether to create a Tool::SubAgent instance after the chat is wired (so the SubAgent tool’s parent.tools snapshot doesn’t include itself —recursion guard).



102
103
104
# File 'lib/pikuri/agent/configurator.rb', line 102

def sub_agent_request
  @sub_agent_request
end

#system_prompt_additionsArray<String> (readonly)

Returns system-prompt snippets added via #append_system_prompt, in declaration order. Joined with double-newline separators between the base prompt and each snippet by Pikuri::Agent#initialize.

Returns:



83
84
85
# File 'lib/pikuri/agent/configurator.rb', line 83

def system_prompt_additions
  @system_prompt_additions
end

#system_prompt_baseString (readonly)

Returns the system_prompt: kwarg passed to Pikuri::Agent#initialize, untouched. Extensions append to the prompt via #append_system_prompt rather than mutating this; the attribute exists so a peek at the base is available for diagnostics.

Returns:

  • (String)

    the system_prompt: kwarg passed to Pikuri::Agent#initialize, untouched. Extensions append to the prompt via #append_system_prompt rather than mutating this; the attribute exists so a peek at the base is available for diagnostics.



45
46
47
# File 'lib/pikuri/agent/configurator.rb', line 45

def system_prompt_base
  @system_prompt_base
end

#toolsArray<Tool> (readonly)

Returns tools added via #add_tool, in declaration order. Drained by Pikuri::Agent#initialize.

Returns:



72
73
74
# File 'lib/pikuri/agent/configurator.rb', line 72

def tools
  @tools
end

#transportAgent::ChatTransport (readonly)

Returns same transport the Agent will use. Extensions read this to wire helpers consistently (e.g. an MCP description-synthesizer that calls the same model the agent itself uses).

Returns:

  • (Agent::ChatTransport)

    same transport the Agent will use. Extensions read this to wire helpers consistently (e.g. an MCP description-synthesizer that calls the same model the agent itself uses).



38
39
40
# File 'lib/pikuri/agent/configurator.rb', line 38

def transport
  @transport
end

Instance Method Details

#add_extension(extension) ⇒ void

This method returns an undefined value.

Register an extension. The extension’s configure(self) is called immediately so source-order matches execution-order. The instance is also retained for the bind(agent) sweep that runs at the end of Pikuri::Agent#initialize.

Extensions must implement both configure and bind. The easy way is to include Pikuri::Agent::Extension — that mixes in empty defaults for both, so an extension overrides only what it cares about and leaves the other as a no-op.

Parameters:

  • extension (Extension, #configure, #bind)

    extension instance



204
205
206
207
208
# File 'lib/pikuri/agent/configurator.rb', line 204

def add_extension(extension)
  @extensions << extension
  extension.configure(self)
  nil
end

#add_listener(listener) ⇒ void

This method returns an undefined value.

Append a listener to the agent’s listener list.

Parameters:



159
160
161
162
# File 'lib/pikuri/agent/configurator.rb', line 159

def add_listener(listener)
  @listeners << listener
  nil
end

#add_listeners(listeners) ⇒ void

This method returns an undefined value.

Append several listeners at once. Accepts any enumerable (Array, ListenerList, …); declaration order is preserved. Sole intended caller today is Tool::SubAgent, which seeds a sub-agent’s Configurator from the parent’s listener list (run through ListenerList#for_sub_agent).

Parameters:



172
173
174
175
# File 'lib/pikuri/agent/configurator.rb', line 172

def add_listeners(listeners)
  listeners.each { |l| @listeners << l }
  nil
end

#add_tool(tool) ⇒ void

This method returns an undefined value.

Append a tool to the agent’s static tool list.

Parameters:



138
139
140
141
# File 'lib/pikuri/agent/configurator.rb', line 138

def add_tool(tool)
  @tools << tool
  nil
end

#add_tools(tools) ⇒ void

This method returns an undefined value.

Append several tools at once. Equivalent to calling #add_tool for each element of tools; declaration order is preserved. Sole intended caller today is Tool::SubAgent, which seeds a sub-agent’s Configurator from the parent’s tool snapshot.

Parameters:

  • tools (Enumerable<Tool>)


150
151
152
153
# File 'lib/pikuri/agent/configurator.rb', line 150

def add_tools(tools)
  tools.each { |t| @tools << t }
  nil
end

#allow_sub_agent(max_steps: 10) ⇒ void

This method returns an undefined value.

Enable the sub_agent tool on this agent. Records the max_steps budget for sub-agent runs; the Agent ctor reads #sub_agent_request after the block returns and constructs the actual Tool::SubAgent so its snapshot of the parent’s tool list doesn’t include itself (recursion guard).

Sub-agents inherit the parent’s tool list (minus sub_agent itself), augmented system prompt, listeners (via for_sub_agent per listener), controls (per the per-control rule), and the parent’s extension list. Each inherited extension’s bind(sub_agent) fires during the sub-agent’s construction — that’s how MCP’s per-agent connect tool ends up keyed to the sub-agent rather than the parent.

Parameters:

Raises:

  • (RuntimeError)

    if called more than once on the same Configurator.



262
263
264
265
266
267
# File 'lib/pikuri/agent/configurator.rb', line 262

def allow_sub_agent(max_steps: 10)
  raise 'allow_sub_agent may only be called once per agent' if @sub_agent_request

  @sub_agent_request = SubAgentRequest.new(max_steps: max_steps)
  nil
end

#append_system_prompt(snippet) ⇒ void

This method returns an undefined value.

Append a snippet to the system prompt. Snippets are joined to the base prompt with double-newline separators.

Used by extensions to register <available_skills>, <available_mcps>, and similar advertisement blocks. Block users typically pass the full system prompt via the ctor’s system_prompt: kwarg instead.

Parameters:

  • snippet (String)


187
188
189
190
# File 'lib/pikuri/agent/configurator.rb', line 187

def append_system_prompt(snippet)
  @system_prompt_additions << snippet
  nil
end

#inherit_extensions(extensions) ⇒ void

This method returns an undefined value.

Retain a list of already-configured extensions for the bind sweep without re-running configure. Sole intended caller is Tool::SubAgent, which seeds a sub-agent’s Configurator from the parent’s extension list — the parent already drove configure and its system-prompt snippets / static tools are inherited by the sub-agent verbatim through #add_tools + #add_listeners + the augmented system_prompt; re-running configure on the sub-agent would double up those contributions. The bind(sub_agent) sweep in Pikuri::Agent#initialize still fires on each inherited extension so per-agent state (MCP’s per-agent connect tool, for example) gets installed fresh on the sub-agent.

Parameters:



225
226
227
228
# File 'lib/pikuri/agent/configurator.rb', line 225

def inherit_extensions(extensions)
  extensions.each { |ext| @extensions << ext }
  nil
end

#on_close { ... } ⇒ void

This method returns an undefined value.

Register a handler called by Pikuri::Agent#close. Handlers fire in LIFO order, each inside its own rescue — same semantics as ensure-block cleanup discipline.

Yields:

  • called with no arguments at close time

Raises:

  • (ArgumentError)


236
237
238
239
240
241
# File 'lib/pikuri/agent/configurator.rb', line 236

def on_close(&blk)
  raise ArgumentError, 'on_close requires a block' unless block_given?

  @on_close_handlers << blk
  nil
end