Async subagents for pi — spawn, orchestrate, and manage sub-agent sessions in multiplexer panes. Fully non-blocking — the main agent keeps working while subagents run in the background.
interactive_compressed.mp4
Call subagent() and it returns immediately. The sub-agent runs in its own terminal pane. A live widget above the input shows all running agents with their current state — active, quiet, stalled, or starting. When a sub-agent finishes, its result is steered back into the main session as an async notification — triggering a new turn so the agent can process it.
╭─ Subagents ────────────────────────── 2 running ─╮
│ 00:23 Scout: Auth (scout) active · 8 msgs │
│ 00:45 Scout: DB (scout) quiet 2m │
╰──────────────────────────────────────────────────╯
For parallel execution, just call subagent multiple times — they all run concurrently:
subagent({ name: "Scout: Auth", agent: "scout", task: "Analyze auth module" });
subagent({ name: "Scout: DB", agent: "scout", task: "Map database schema" });
// Both return immediately, results steer back independentlypi install git:github.com/HazAT/pi-interactive-subagentsSupported multiplexers:
Start pi inside one of them:
cmux pi
# or
tmux new -A -s pi 'pi'
# or
zellij --session pi # then run: pi
# or
# just run pi inside WezTerm — no wrapper neededOptional: set PI_SUBAGENT_MUX=cmux|tmux|zellij|wezterm to force a specific backend.
If your shell startup is slow and subagent commands sometimes get dropped before the prompt is ready, set PI_SUBAGENT_SHELL_READY_DELAY_MS to a higher value (defaults to 500):
export PI_SUBAGENT_SHELL_READY_DELAY_MS=2500Subagents — 4 main-session tools + 3 commands, plus 1 subagent-only tool:
| Tool | Description |
|---|---|
subagent |
Spawn a sub-agent in a dedicated multiplexer pane (async — returns immediately) |
subagent_interrupt |
Interrupt a running Pi-backed subagent's current turn |
subagents_list |
List available agent definitions |
subagent_resume |
Resume a previous sub-agent session (async) |
| Command | Description |
|---|---|
/plan |
Start a full planning workflow |
/iterate |
Fork into a subagent for quick fixes |
/subagent <agent> <task> |
Spawn a named agent directly |
| Agent | Model | Role |
|---|---|---|
| planner | Opus (medium thinking) | Brainstorming — clarifies requirements, explores approaches, writes plans, creates todos |
| scout | Haiku | Fast codebase reconnaissance — maps files, patterns, conventions |
| worker | Sonnet | Implements tasks from todos — writes code, runs tests, makes polished commits |
| reviewer | Opus (medium thinking) | Reviews code for bugs, security issues, correctness |
| visual-tester | Sonnet | Visual QA via Chrome CDP — screenshots, responsive testing, interaction testing |
Agent discovery follows priority: project-local (.pi/agents/) > global (~/.pi/agent/agents/) > package-bundled. Override any bundled agent by placing your own version in the higher-priority location.
1. Agent calls subagent() → returns immediately ("started")
2. Sub-agent runs in mux pane → widget shows live status
3. User keeps chatting → main session fully interactive
4. Sub-agent finishes → result steered back as a normal completion/failure
5. Main agent processes result → continues with new context
Multiple subagents run concurrently — each steers its result back independently as it finishes. The live widget above the input tracks all running agents:
╭─ Subagents ─────────────────────────── 3 running ─╮
│ 01:23 Scout: Auth (scout) active · 15 msgs │
│ 00:45 Researcher (researcher) stalled 4m │
│ 00:12 Scout: DB (scout) starting… │
╰───────────────────────────────────────────────────╯
Completion messages render with a colored background and are expandable with Ctrl+O to show the full summary and session file path.
The widget tracks each sub-agent's progress and labels it with a coarse state:
starting— launched, but no progress observed yetactive— recent progress observedquiet— still running, but no recent progressstalled— no progress for an extended periodrunning— fallback for backends without progress tracking (e.g. Claude)
These labels are derived from session-file activity. The defaultCadenceSeconds setting controls the idle-time thresholds: with the default of 60, a run becomes quiet after about 1 minute without progress and stalled after about 3 minutes. When a run enters stalled or recovers from it, the parent agent receives a steer message so it can react. All other status transitions stay in the widget only.
Status defaults come from config.json in the extension directory. Copy config.json.example to get started:
cp config.json.example config.json{
"status": {
"enabled": true,
"defaultCadenceSeconds": 60
}
}config.json is gitignored so local overrides don't get committed. You can also override per run:
subagent({
name: "Scout",
agent: "scout",
statusCadenceSeconds: 30,
task: "Analyze the auth module",
});// Named agent with defaults from agent definition
subagent({ name: "Scout", agent: "scout", task: "Analyze the codebase..." });
// Force a full-context fork for this spawn
subagent({ name: "Iterate", fork: true, task: "Fix the bug where..." });
// Agent defaults can choose a different session-mode via frontmatter
subagent({ name: "Planner", agent: "planner", task: "Work through the design with me" });
// Custom working directory
subagent({ name: "Designer", agent: "game-designer", cwd: "agents/game-designer", task: "..." });
// Override the status classification window for this run
subagent({ name: "Scout", agent: "scout", statusCadenceSeconds: 30, task: "..." });| Parameter | Type | Default | Description |
|---|---|---|---|
name |
string | required | Display name (shown in widget and pane title) |
task |
string | required | Task prompt for the sub-agent |
agent |
string | — | Load defaults from agent definition |
fork |
boolean | false |
Force the full-context fork mode for this spawn, overriding any agent session-mode frontmatter |
model |
string | — | Override agent's default model |
systemPrompt |
string | — | Append to system prompt |
skills |
string | — | Comma-separated skill names |
tools |
string | — | Comma-separated tool names |
cwd |
string | — | Working directory for the sub-agent (see Role Folders) |
statusCadenceSeconds |
number | config default | Idle-time window for status classification (has a minimum floor) |
Use subagent_interrupt to cancel the active turn of a running Pi-backed subagent:
subagent_interrupt({ id: "abcd1234" });
// or
subagent_interrupt({ name: "Scout" });This sends Escape to the child pane, cancelling the in-progress model turn. The subagent session stays alive — the pane, session file, and background polling all remain intact. After the interrupt, the widget shows the child as quiet. If the child makes new progress later, it returns to active; completion, failure, and caller_ping still flow through normally.
This is a turn-level interrupt, not a method for forcibly terminating a subagent session.
Note: Only Pi-backed subagents are supported. Claude-backed runs will return an error.
The caller_ping tool lets a subagent request help from its parent agent. When called, the child session exits and the parent receives a notification with the help message. The parent can then resume the child session with a response using subagent_resume.
Parameters:
message(required): What you need help with
Interaction flow:
- Child calls
caller_ping({ message: "Not sure which schema to use" }) - Child session exits (like
subagent_done) - Parent receives a steer notification: "Sub-agent Worker needs help: Not sure which schema to use"
- Parent resumes the child session via
subagent_resumewith the response - Child picks up where it left off with the parent's guidance
Example:
// Inside a worker subagent
await caller_ping({
message: "Found two conflicting migration files — should I use v1 or v2?"
});
// Session exits here. Parent receives the ping, then resumes this session
// with guidance like "Use v2, v1 is deprecated"Note:
caller_pingis only available inside subagent contexts. Calling it from a standalone pi session returns an error.
The /plan command orchestrates a full planning-to-implementation pipeline.
/plan Add a dark mode toggle to the settings page
Phase 1: Investigation → Quick codebase scan
Phase 2: Planning → Interactive planner subagent (user collaborates)
Phase 3: Review Plan → Confirm todos, adjust if needed
Phase 4: Execute → Scout + sequential workers implement todos
Phase 5: Review → Reviewer subagent checks all changes
Tab/window titles update to show current phase:
🔍 Investigating: dark mode → 💬 Planning: dark mode
→ 🔨 Executing: 1/3 → 🔎 Reviewing → ✅ Done
For quick, focused work without polluting the main session's context.
/iterate Fix the off-by-one error in the pagination logic
This always forks the current session into a subagent with full conversation context. It does not inherit an agent default session-mode. Make the fix, verify it, and exit to return. The main session gets a summary of what was done.
Place a .md file in .pi/agents/ (project) or ~/.pi/agent/agents/ (global):
---
name: my-agent
description: Does something specific
model: anthropic/claude-sonnet-4-6
thinking: minimal
tools: read, bash, edit, write
session-mode: lineage-only
spawning: false
---
# My Agent
You are a specialized agent that does X...| Field | Type | Description |
|---|---|---|
name |
string | Agent name (used in agent: "my-agent") |
description |
string | Shown in subagents_list output |
model |
string | Default model (e.g. anthropic/claude-sonnet-4-6) |
thinking |
string | Thinking level: minimal, medium, high |
tools |
string | Comma-separated native pi tools only: read, bash, edit, write, grep, find, ls |
skills |
string | Comma-separated skill names to auto-load |
session-mode |
string | Default child-session mode: standalone, lineage-only, or fork |
spawning |
boolean | Set false to deny all subagent-spawning tools |
deny-tools |
string | Comma-separated extension tool names to deny |
auto-exit |
boolean | Auto-shutdown when the agent finishes its turn — no subagent_done call needed. If the user sends any input, auto-exit is permanently disabled and the user takes over the session. Recommended for autonomous agents (scout, worker); not for interactive ones (planner). |
cwd |
string | Default working directory (absolute or relative to project root) |
disable-model-invocation |
boolean | Hide this agent from discovery surfaces like subagents_list. The agent still remains directly invokable by explicit name via subagent({ agent: "name", ... }). |
Discovery still resolves precedence before visibility filtering. If a project-local hidden agent has the same name as a visible global or bundled agent, the hidden project agent wins and the lower-precedence agent does not appear in subagents_list.
Choose how a subagent session starts:
standalone— default fresh session with no lineage link to the callerlineage-only— fresh blank child session withparentSessionlinkage, but no copied turns from the callerfork— linked child session seeded with the caller's prior conversation context
lineage-only is useful when you want session discovery and fork lineage UX to show the relationship later, but you do not want the child to inherit the parent's turns.
fork: true on the tool call always forces the fork mode for that specific spawn. /iterate uses this explicit override on purpose.
---
name: planner
session-mode: lineage-only
---When set to true, the agent session shuts down automatically as soon as the agent finishes its turn — no explicit subagent_done call is needed.
Behavior:
- The session closes after the agent's final message (on the
agent_endevent) - If the user sends any input before the agent finishes, auto-exit is permanently disabled for that session — the user takes over interactively
- The modeHint injected into the agent's task is adjusted accordingly: autonomous agents see "Complete your task autonomously." rather than instructions to call
subagent_done
When to use:
- ✅ Autonomous agents (scout, worker, reviewer) that run to completion
- ❌ Interactive agents (planner, iterate) where the user drives the session
---
name: scout
auto-exit: true
---By default, every sub-agent can spawn further sub-agents. Control this with frontmatter:
Denies all subagent lifecycle tools (subagent, subagent_interrupt, subagents_list, subagent_resume):
---
name: worker
spawning: false
---Fine-grained control over individual extension tools:
---
name: focused-agent
deny-tools: subagent
---| Agent | spawning |
Rationale |
|---|---|---|
| planner | (default) | Legitimately spawns scouts for investigation |
| worker | false |
Should implement tasks, not delegate |
| researcher | false |
Should research, not spawn |
| reviewer | false |
Should review, not spawn |
| scout | false |
Should gather context, not spawn |
The cwd parameter lets sub-agents start in a specific directory with its own configuration:
project/
├── agents/
│ ├── game-designer/
│ │ └── CLAUDE.md ← "You are a game designer..."
│ ├── sre/
│ │ ├── CLAUDE.md ← "You are an SRE specialist..."
│ │ └── .pi/skills/ ← SRE-specific skills
│ └── narrative/
│ └── CLAUDE.md ← "You are a narrative designer..."
subagent({ name: "Game Designer", cwd: "agents/game-designer", task: "Design the combat system" });
subagent({ name: "SRE", cwd: "agents/sre", task: "Review deployment pipeline" });Set a default cwd in agent frontmatter:
---
name: game-designer
cwd: ./agents/game-designer
spawning: false
---Every sub-agent session displays a compact tools widget showing available and denied tools. Toggle with Ctrl+J:
[scout] — 12 tools · 4 denied (Ctrl+J) ← collapsed
[scout] — 12 available (Ctrl+J to collapse) ← expanded
read, bash, edit, write, todo, ...
denied: subagent, subagents_list, ...
cmux pi
# or
tmux new -A -s pi 'pi'
# or
zellij --session pi # then run: pi
# or
# just run pi inside WezTermOptional backend override:
export PI_SUBAGENT_MUX=cmux # or tmux, zellij, weztermThe sub-agent status supervision and turn-only interruption features were inspired by RepoPrompt's sub-agent snapshot polling and run cancellation features.
MIT