Agents

Identity, model, tools, inheritance, and orchestrating other agents.

An agent is a YAML file. Its name is its identity everywhere: in --agent, in agentName, in another agent's subagents list, in from:, and in traces. The filename means nothing.

agents/agent.yaml
name: agent
description: Research an account and produce a sourced brief.

model:
  name: anthropic/claude-sonnet-4-6   # required once resolved
  thinking_level: high

tools: [read, write, bash]   # an allowlist: nothing else reaches the model
skills: [research]           # by skill name, not by path
subagents: [reviewer]        # exposes them; it does not define them

system_instructions:
  mode: append               # append to SYSTEM.md, or replace it
  content: |
    Verify every material claim against a source you read
    in this session.

The nine keys above are the whole vocabulary: name, from, description, model, tools, skills, subagents, mcp and system_instructions. Anything else fails the file at launch.

Instructions

SYSTEM.md is the package-wide instruction layer, and a root agent and every subagent start from it. Each agent's system_instructions then specializes it. mode: append composes after what it inherited; mode: replace discards both the inherited instructions and Pi's base prompt.

Derive agents with from:

agents/reviewer.yaml
name: reviewer
from: agent                # an exact agent name, not a filename

model:
  thinking_level: high     # merges by key, because name is not restated

tools: [read]              # replaces the inherited allowlist entirely
subagents: []              # clears it, so this one cannot delegate

system_instructions:
  mode: append             # composes after the base agent's instructions
  content: Independently review the evidence. Do not make changes.

Chains may be several levels deep. A missing base or a cycle is a validation error at launch. Inheritance and delegation are separate: from: inherits a definition, and it does not make that agent reachable. A root must still name it in subagents.

Inheritance rules

FieldDerived-agent behavior
descriptionChild replaces base; omission inherits
modelMerges by key, unless the child declares model.name, which replaces the whole block
toolsChild array replaces the allowlist; [] clears it
skillsChild array replaces the selection; [] clears it
subagentsChild array replaces visibility; [] clears it
mcpA declared child block replaces the whole inherited policy
system_instructionsappend composes after the base; replace discards it

Arrays never merge item by item. A derived agent declaring tools: [read] receives only read, whatever its base listed. The model exception catches people out: restating model.name in a child drops the inherited temperature, retries and provider sections with it.

Model configuration

model:
  name: anthropic/claude-sonnet-4-6
  thinking_level: medium
  temperature: 0.2
  max_tokens: 4096
  cache_retention: short        # none | short | long
  timeout_ms: 60000
  max_retries: 2
  max_retry_delay_ms: 8000
  providers:
    anthropic:
      betas: [context-1m]
    openrouter:
      routing:
        order: [anthropic]
        sort: throughput

Those nine keys are the whole set. All are optional except name, and each merges by key along a from: chain.

Orchestrating other agents

There is no separate kind of agent for delegated work. Every agent in a package can be invoked directly, and the same file becomes a tool the moment a lead agent names it in subagents.

TUI
pi --recipe . --agent reviewer   # invoke it directly
agents/agent.yaml
subagents:
  - reviewer        # or hand the same agent to a lead, as a tool
  - fact-checker

subagents is the last of those nine keys and the whole orchestration declaration. Recipes registers a single agent tool that accepts only those names. An agent with an empty or absent list gets no agent tool at all, so nothing orchestrates anything you did not say it could. Never list agent in tools; the subagents list is what enables it.

The agent tool

Starting a run returns a run id immediately instead of blocking, which is what lets a lead agent keep working while the agents it started run alongside it.

ActionWhat it does
omitted, or startRuns the agent name with prompt, and returns a run id
statusThe state of one run, or of every run with no id
waitBlocks until that run settles, then returns its output
messageSends text into a run
interruptStops the current turn but keeps the run
closeEnds the run and releases it

label distinguishes concurrent runs of the same agent, which is what lets a lead start four at once and still tell the results apart. message does two useful things depending on when it lands: sent to a running agent it steers at the next message boundary rather than interrupting a tool call, and sent to one that has settled it resumes that same session with its context intact.

From your own process the same runs are readable on the handle:

// Live and settled child runs, with status, label and current tool.
for (const run of handle.agentRuns.list()) {
  console.log(run.invocation_name, run.label, run.status);
}

What a child gets

  • The same resolved snapshot as the root, so both read one source of truth.
  • The child agent's own model, tools, skills and instructions.
  • The root's model and credential binding, so nothing is configured twice.
  • Its own MCP state. A parent does not inherit a child's connections, or the reverse.
  • No agent tool. Delegation is one level deep, so a run cannot fan out without bound.

Children run in-process by default with nothing to configure. A host can supply a durable, cross-process implementation of the same contract without touching the recipe. See Deploying.

Three rules worth stating twice

  • Package extensions load for every agent session, root and delegated. What each agent can *see* is still only its own tools.
  • MCP access is the intersection of package permission and agent selection, never the union.
  • A host cannot silently override the model an agent declared.