Class: Pikuri::Agent::Configurator
- Inherits:
-
Object
- Object
- Pikuri::Agent::Configurator
- 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, extension instances, and persona-flavored sub-agents; #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::WEB_SEARCH
c.add_tool Pikuri::Tool::WEB_SCRAPE
c.add_tool Pikuri::Tool::FETCH
c.add_extension Pikuri::Skill::Extension.new(catalog: catalog)
end
Two tool pools: regular vs. sub-agent-only
#add_tool registers a tool the parent agent can call: it lands in #tools and gets handed to ruby_llm via chat.with_tool. #add_sub_agent_tool registers a tool the parent cannot call — it lands in #sub_agent_tools and is never sent to ruby_llm for the parent, but is visible to SubAgent::Extension‘s persona-tool-name resolution. The use case is the lethal-trifecta defense in Code::Bash::Sandbox terms: keep network tools (web_search / web_scrape / fetch) off the parent so a prompt- injected file read cannot egress through the parent’s own tools, while still letting the researcher persona reach them via the agent delegation tool. See SECURITY.md §“Defense: capability boundaries via sub-agents”.
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.
Instance Attribute Summary collapse
-
#cancellable ⇒ Control::Cancellable?
readonly
Cancellation control passed to the Agent ctor, or
nil. -
#extensions ⇒ Array<#configure>
readonly
Extension instances added via #add_extension, in declaration order.
-
#id ⇒ String
readonly
This agent’s unique identifier; empty for the main agent, persona-rooted (e.g. “researcher 0”) for sub-agents.
-
#interloper ⇒ Control::Interloper?
readonly
Mid-loop user-input queue passed to the Agent ctor, or
nil. -
#listeners ⇒ Array<Listener::Base>
readonly
Listeners added via #add_listener, in declaration order.
-
#on_close_handlers ⇒ Array<Proc>
readonly
on_closehandlers added via #on_close, in declaration order. -
#step_limit ⇒ Control::StepLimit?
readonly
Step-budget control passed to the Agent ctor, or
nil. -
#streaming ⇒ Boolean
readonly
truewhen the agent opted into chunk-level streaming. -
#sub_agent_tools ⇒ Array<Tool>
readonly
Tools added via #add_sub_agent_tool, in declaration order.
-
#system_prompt_additions ⇒ Array<String>
readonly
System-prompt snippets added via #append_system_prompt, in declaration order.
-
#system_prompt_base ⇒ String
readonly
The
system_prompt:kwarg passed to #initialize, untouched. -
#tools ⇒ Array<Tool>
readonly
Tools added via #add_tool, in declaration order.
-
#transport ⇒ Agent::ChatTransport
readonly
Same transport the Agent will use.
Instance Method Summary collapse
-
#add_extension(extension) ⇒ void
Register an extension.
-
#add_listener(listener) ⇒ void
Append a listener to the agent’s listener list.
-
#add_listeners(listeners) ⇒ void
Append several listeners at once.
-
#add_sub_agent_tool(tool) ⇒ void
Append a tool to the sub-agent-only pool.
-
#add_tool(tool) ⇒ void
Append a tool to the agent’s static tool list.
-
#add_tools(tools) ⇒ void
Append several tools at once.
-
#append_system_prompt(snippet) ⇒ void
Append a snippet to the system prompt.
-
#initialize(transport:, system_prompt_base:, id:, streaming:, step_limit:, cancellable:, interloper:) ⇒ Configurator
constructor
A new instance of Configurator.
-
#on_close { ... } ⇒ void
Register a handler called by #close.
Constructor Details
#initialize(transport:, system_prompt_base:, id:, streaming:, step_limit:, cancellable:, interloper:) ⇒ Configurator
Returns a new instance of Configurator.
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/pikuri/agent/configurator.rb', line 130 def initialize(transport:, system_prompt_base:, id:, streaming:, step_limit:, cancellable:, interloper:) @transport = transport @system_prompt_base = system_prompt_base @id = id @streaming = streaming @step_limit = step_limit @cancellable = cancellable @interloper = interloper @tools = [] @sub_agent_tools = [] @listeners = [] @system_prompt_additions = [] @on_close_handlers = [] @extensions = [] end |
Instance Attribute Details
#cancellable ⇒ Control::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.
82 83 84 |
# File 'lib/pikuri/agent/configurator.rb', line 82 def cancellable @cancellable end |
#extensions ⇒ Array<#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.
121 122 123 |
# File 'lib/pikuri/agent/configurator.rb', line 121 def extensions @extensions end |
#id ⇒ String (readonly)
Returns this agent’s unique identifier; empty for the main agent, persona-rooted (e.g. “researcher 0”) for sub-agents.
67 68 69 |
# File 'lib/pikuri/agent/configurator.rb', line 67 def id @id end |
#interloper ⇒ Control::Interloper? (readonly)
Returns mid-loop user-input queue passed to the Agent ctor, or nil.
86 87 88 |
# File 'lib/pikuri/agent/configurator.rb', line 86 def interloper @interloper end |
#listeners ⇒ Array<Listener::Base> (readonly)
Returns listeners added via #add_listener, in declaration order. Drained by Pikuri::Agent#initialize.
104 105 106 |
# File 'lib/pikuri/agent/configurator.rb', line 104 def listeners @listeners end |
#on_close_handlers ⇒ Array<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.
115 116 117 |
# File 'lib/pikuri/agent/configurator.rb', line 115 def on_close_handlers @on_close_handlers end |
#step_limit ⇒ Control::StepLimit? (readonly)
Returns step-budget control passed to the Agent ctor, or nil.
75 76 77 |
# File 'lib/pikuri/agent/configurator.rb', line 75 def step_limit @step_limit end |
#streaming ⇒ Boolean (readonly)
Returns true when the agent opted into chunk-level streaming.
71 72 73 |
# File 'lib/pikuri/agent/configurator.rb', line 71 def streaming @streaming end |
#sub_agent_tools ⇒ Array<Tool> (readonly)
Returns tools added via #add_sub_agent_tool, in declaration order. Drained by Pikuri::Agent#initialize but not registered with ruby_llm — invisible to the parent LLM, available only to sub-agents through SubAgent::Extension‘s persona-tool-name resolution. See the class header.
99 100 101 |
# File 'lib/pikuri/agent/configurator.rb', line 99 def sub_agent_tools @sub_agent_tools end |
#system_prompt_additions ⇒ Array<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.
110 111 112 |
# File 'lib/pikuri/agent/configurator.rb', line 110 def system_prompt_additions @system_prompt_additions end |
#system_prompt_base ⇒ String (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.
63 64 65 |
# File 'lib/pikuri/agent/configurator.rb', line 63 def system_prompt_base @system_prompt_base end |
#tools ⇒ Array<Tool> (readonly)
Returns tools added via #add_tool, in declaration order. Drained by Pikuri::Agent#initialize and registered with ruby_llm so the parent LLM can call them.
91 92 93 |
# File 'lib/pikuri/agent/configurator.rb', line 91 def tools @tools end |
#transport ⇒ Agent::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).
56 57 58 |
# File 'lib/pikuri/agent/configurator.rb', line 56 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.
225 226 227 228 229 |
# File 'lib/pikuri/agent/configurator.rb', line 225 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.
183 184 185 186 |
# File 'lib/pikuri/agent/configurator.rb', line 183 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.
193 194 195 196 |
# File 'lib/pikuri/agent/configurator.rb', line 193 def add_listeners(listeners) listeners.each { |l| @listeners << l } nil end |
#add_sub_agent_tool(tool) ⇒ void
This method returns an undefined value.
Append a tool to the sub-agent-only pool. The parent LLM never sees it; only sub-agents whose persona tool_names include the tool’s name get it in their toolset. See the class header for the trifecta-defense rationale.
174 175 176 177 |
# File 'lib/pikuri/agent/configurator.rb', line 174 def add_sub_agent_tool(tool) @sub_agent_tools << tool nil end |
#add_tool(tool) ⇒ void
This method returns an undefined value.
Append a tool to the agent’s static tool list.
152 153 154 155 |
# File 'lib/pikuri/agent/configurator.rb', line 152 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.
162 163 164 165 |
# File 'lib/pikuri/agent/configurator.rb', line 162 def add_tools(tools) tools.each { |t| @tools << t } 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.
208 209 210 211 |
# File 'lib/pikuri/agent/configurator.rb', line 208 def append_system_prompt(snippet) @system_prompt_additions << snippet 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.
237 238 239 240 241 242 |
# File 'lib/pikuri/agent/configurator.rb', line 237 def on_close(&blk) raise ArgumentError, 'on_close requires a block' unless block_given? @on_close_handlers << blk nil end |