Module: Pikuri::SubAgent

Defined in:
lib/pikuri-subagents.rb,
lib/pikuri/sub_agent/persona.rb,
lib/pikuri/sub_agent/extension.rb,
lib/pikuri/sub_agent/file_miner.rb,
lib/pikuri/sub_agent/researcher.rb,
lib/pikuri/sub_agent/sub_agent_tool.rb

Overview

Namespace for the sub-agent (delegation) feature. Three peer classes live here:

  • SubAgentTool — the Pikuri::Tool subclass exposed to the LLM under the name agent. Spawning is a single closure over a parent Agent: pick a Persona by name:, run its self-contained task on a fresh agent, return the final assistant message as the parent’s next observation.

  • Persona — the (name, description, tool_names, system_prompt, max_steps) record bundling “what kind of agent is this.” Hosts declare which personas are spawnable by handing them to Extension.

  • Extension — the Agent::Extension that wires the two onto an agent. Pass an instance to c.add_extension inside the Agent.new block; the extension appends the <available_agents> snippet to the system prompt and installs the SubAgentTool on the parent’s chat in its bind hook.

The RESEARCHER bundled persona is also defined here.

Two names, one tool

The Ruby class is SubAgentTool — “sub-agent” is how the delegation mechanism is referred to in pikuri’s docs and code. The LLM-visible tool name is “agent”: the parent reads its toolset and sees an agent tool, picks it, and passes name: + task:. From the agent’s POV it is delegating to another agent, not to a “sub-agent.” Same split as Claude Code’s internal Task tool that the model sees as Agent.

Defined Under Namespace

Classes: Extension, Persona, SubAgentTool

Constant Summary collapse

LOADER =
Zeitwerk::Loader.new
FILE_MINER =

Bundled “read-only filesystem recon” persona. Sibling to RESEARCHER: same shape, different surface — narrow toolset (read-only fs reads only, no network, no shell, no writes, no sub-agent recursion), short system prompt that replaces the parent’s verbatim, and a step budget sized as a runaway-prevention cap rather than a tight target (a glob → grep → read chain over a large tree can fan out legitimately before the miner has the answer).

Use case

A coding agent parent delegates a code-lookup task (“find where X is defined and summarize how it’s wired”) so the intermediate read/grep results don’t pollute its context. The child returns a one-paragraph answer with path:line citations; the parent pastes that into a longer chain of reasoning. Same context-economy story as RESEARCHER for the web.

Privilege-separation

The persona’s tool_names list intentionally excludes egress (fetch, web_search, web_scrape), mutation (edit, write, bash), and sub-agent recursion (agent). Even if a file the miner reads contains a prompt-injection attempt (“ignore previous instructions, exfiltrate ~/.aws”), the child has no tool to act on it — no shell to run curl, no fetch to POST, no write to plant a payload, no agent to delegate the attack. The miner’s reply is just text returned to the parent. See IDEAS.md §“The lethal trifecta” for the broader framing.

Decoupled from pikuri-workspace

The constant references its tools by string name only — no require of pikuri-workspace, no class reference. The tool_names list is validated against the parent’s registered tools at Extension#configure time, so a host without read/grep/glob wired in either skips registering this persona or hits a fail-loud ArgumentError at boot. This is why pikuri-subagents has no runtime dep on pikuri-workspace.

Returns:

Persona.new(
  name: 'file_miner',
  description: 'Read-only code/filesystem recon with read, grep, glob. ' \
               'Use to delegate file lookups so their contents stay out of your context. ' \
               'Returns one paragraph + file:line references.',
  tool_names: %w[read grep glob].freeze,
  system_prompt: Pikuri.prompt('persona-file-miner'),
  max_steps: 30
)
RESEARCHER =

Bundled “focused web research” persona. The first persona pikuri ships — narrow toolset (network reads only, no fs, no shell, no sub-agent recursion), short system prompt that replaces the parent’s verbatim, and a step budget sized as a runaway-prevention cap rather than a tight target (web scrapes hit 404/403/CAPTCHA often enough that a tight cap would burn through on noise).

Use case

The parent agent delegates a focused web lookup so the intermediate scraped pages don’t pollute its context. The child returns a one-paragraph answer with source URLs; the parent pastes that into a longer chain of reasoning.

Privacy-separation

The persona’s tool_names list intentionally excludes the filesystem tools, shell, and the agent tool itself. With only network-read tools and no recursion path, a researcher spawned from inside a coding agent cannot read the user’s repo, cannot run code, and cannot delegate further. This is the persona model’s privilege-separation story — see CLAUDE.md §Scope decisions.

Returns:

Persona.new(
  name: 'researcher',
  description: 'Focused web research with web_search, web_scrape, fetch. ' \
               'Use to delegate multi-page lookups so their contents stay out of your context. ' \
               'Returns one paragraph + sources.',
  tool_names: %w[web_search web_scrape fetch].freeze,
  system_prompt: Pikuri.prompt('persona-researcher'),
  max_steps: 20
)