pi-multi-agent-mode
Codex-style collaboration for Pi: spawn_agent, followup_task, send_message, wait_agent, list_agents, and interrupt_agent. Named agent trees, configurable child model, history forks, mailboxes, and a packaged multi-agent-mode skill.
Package details
Install pi-multi-agent-mode from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-multi-agent-mode- Package
pi-multi-agent-mode- Version
0.1.0- Published
- Sep 21, 2026
- Downloads
- 195/mo · 15/wk
- Author
- kennyfrc
- License
- MIT
- Types
- extension, skill
- Size
- 123.9 KB
- Dependencies
- 1 dependency · 3 peers
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
],
"skills": [
"./skills"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-multi-agent-mode
Codex V2's six collaboration tools, adapted to in-process Pi sessions. The
model tool parameter is absent. The user selects the child
provider and model in a config file.
Install
pi install npm:pi-multi-agent-mode
Then run /reload or restart Pi. The package ships the multi-agent-mode
skill. Run /skill:multi-agent-mode <task> to load and submit it in one step.
Configure
Create ~/.pi/agent/extensions/pi-multi-agent-mode.json:
{
"models": [
{ "provider": "hyper", "model": "deepseek-v4.1-flash" },
{ "provider": "opencode-go", "model": "deepseek-v4.1-flash" },
{ "provider": "novita", "model": "deepseek/deepseek-v4.1-flash" },
{ "provider": "neuralwatt", "model": "deepseek-v4.1-flash" }
],
"agents": {
"cyber": { "provider": "zro", "model": "dolly1-security" },
"reviewer": {
"provider": "zro", "model": "dolly1-security",
"instructions": "Read only. Report findings; change nothing.",
"tools": ["read", "grep", "ls"]
}
},
"maxConcurrentAgents": 4,
"maxDepth": 2
}
The pre-rename pi-subagents.json is still read when the new file is absent,
so existing setups keep working without a change. A pre-array config that sets
only provider and model is accepted as a one-entry pool.
These are also the defaults if the file is absent. The provider and model
must exist in Pi's model registry. No credentials belong in this file; use
Pi's normal provider setup. Use /reload after changing the config.
Named agent types
The optional agents map defines named agent_type values, each pinning one
model. spawn_agent with agent_type: "cyber" runs that child on the pinned
zro/dolly1-security model and does not advance the rotation cursor. A pinned
model outside the models pool still fails over: when its run ends in an
error, the child rotates into the pool starting at the pool's first entry (see
Failover). Add the pinned model to models to control where its walk starts.
default and worker are reserved and cannot be redefined. Names use
lowercase letters, digits, underscores, or hyphens. An unavailable pinned model
fails the spawn with a clear error instead of silently using the pool.
A role may also carry instructions (appended to the child prompt) and a
tools allowlist (the pi analog to a Codex role sandbox). The allowlist is
opt-in: a role without it keeps the parent's full inherited tool set. Top-level
maxDepth caps nesting; it is undefined (unlimited, Codex V2 behavior) unless
set. Codex's V1 default of 1 excludes grandchildren.
Model rotation
spawn_agent picks the next entry in models with a simple round robin. The
cursor is a global file, ~/.pi/agent/pi-multi-agent-mode-cursor.json, so all
Pi sessions on the machine advance one shared rotation, and it survives
restarts. Entries whose provider/model is unavailable are skipped, so one dead
backend cannot block spawning; every spawn fails only when the whole pool
resolves to nothing. Each child keeps the model it was spawned with, including
follow-ups. The cursor is written with atomic file replacement.
Failover
When a run ends in an error, the child switches to the next pool entry and
continues the same task. The pool is walked once per run, so each entry is
tried at most once. Failover attempts do not touch the rotation cursor. The
walk covers every child, including a model pinned by agent_type and a
per-spawn model: override; a starting model outside the pool begins at the
pool's first entry.
Two guards keep the switch useful:
- A candidate whose context window cannot hold the failed attempt's input is skipped.
- A context overflow only fails over to a candidate with a strictly larger window. If none exists, the child reports the overflow error.
Aborts and interrupts never fail over. Pi's own retry runs first, so transient errors still get their in-place attempts before the pool takes over (default 3 at the provider level). The child reports the provider that actually served the final turn, and the engine stores it in the agent record, so follow-ups and restarts stay on that provider. Failed attempts stay in the transcript.
The configured pool applies to all new children, including full-history forks. It does not change the root model. A continued agent starts on the model and tool access saved in its record, then fails over into the pool if that run errors. The cap counts active children across the entire tree, including nested agents; it excludes the root.
Tool interface
? marks optional parameters. All six schemas reject extra fields.
| Tool | Parameters |
|---|---|
spawn_agent |
task_name: string, message: string, agent_type?: string, fork_turns?: string, reasoning_effort?: string, model?: string |
followup_task |
target: string, message: string |
send_message |
target: string, message: string, interrupt?: boolean |
close_agent |
target: string |
resume_agent |
target: string |
spawn_agents_on_csv |
csv_path: string, instruction: string, id_column?: string, agent_type?: string |
wait_agent |
timeout_ms?: number |
list_agents |
path_prefix?: string |
interrupt_agent |
target: string |
Spawn and fork
spawn_agentreturns{task_name: "/root/review", nickname: null}before constructing the child. Construction or model failures arrive as reports.task_nameaccepts lowercase letters, digits, and underscores. Names are unique under each parent. Reuse an agent withfollowup_task.fork_turnsaccepts"all"(default),"none", or a positive integer string, such as"3". A turn starts at a user message. Forks use the current branch with compaction applied, and omit unfinished tool calls.- A fork is framed for the child. The child prompt says the copied messages are the parent's, not the child's own history, and a boundary entry marks where the parent's context ends. Otherwise the child's last assistant message is the parent's orchestration turn, and it continues that work instead of its task.
agent_typeis an optional role label.defaultandworkeralways exist and behave the same. A name defined in the config'sagentsmap pins that type's model (see Named agent types). The child inherits the parent's active tools, including extension tools such as web and image tools, plus its skill catalog. The removedexplorervalue is rejected rather than silently widened. Pass noagent_typeunless you want a specific role or model.- Child sessions load workspace context files. They share the root's filesystem and working directory. This is not an OS sandbox.
- Reasoning effort defaults to the parent's level. Overrides require an
empty or partial fork. Pi accepts
off,minimal,low,medium,high,xhigh, andmax; Codex'snonemaps tooff, andultramaps tomax. Pi clamps the result to the child model's supported levels.
Tool and skill inheritance
Every new agent captures its parent's active tool list at spawn. Nothing is
withheld by role, so a research child has the parent's web, image, and file
tools.
This applies to empty forks too: fork_turns controls history, not tools.
Each child reloads the files that own those tools, including builtin overrides,
against its own Pi session. Calls to tools such as todo therefore use the
child's session state, not a function tied to the parent.
The child also inherits deferred tools: these need select_tools before use.
Other inactive tools stay blocked. An inherited extension cannot enable them.
A nested child takes its own snapshot
from its immediate parent. Follow-ups and reloads keep the saved tool limit;
changing the root's active list does not widen an existing child.
File-based extensions that own commands are also reloaded as companions. This keeps hooks from those extensions, such as permission checks, in the child. Hook-only extensions with no tools or commands are not discoverable through Pi's public extension API. Child sessions have no terminal UI. Each loaded extension must support headless use and its own shutdown cleanup. Hooks cannot start independent child prompts at startup or while idle. New tasks must go through collaboration tools so the engine can track their slots.
Skills keep their parent catalog and file paths, with a visible skill list in the child prompt. The child reads skill files on demand.
Inline/SDK tools without a reloadable source file cause a clear spawn error. The extension never drops these tools or copies functions tied to the parent. Missing tool files or failed child startup produce an error report before any model call. An agent saved without a tool set re-inherits the root's current tools and skills when it is resumed. This covers records from before tool inheritance and records from the removed read-only mode.
Messaging and lifecycle
- Targets accept canonical names such as
/root/review, direct child names such asreview, or internal agent IDs. Cross-branch messages use canonical names.send_messagecan address/root. followup_taskstarts an idle agent or queues a task on a running agent. It keeps the existing transcript. It cannot target the root.send_messagequeues information without starting an idle agent. Pi delivers it at a message boundary after pending tools, not mid-token. Steer from evidence: read the target's trace tail first, and read the file or artifact in question when the trace alone is not enough. Send on a condition (a gate clears, a defect appears, a lane must be handed back, a constraint could be violated), not on a schedule. If conflicts keep causing rebuilds or stale evidence, the skill calls for reducing parallel work, not more messages.- Each settled run sends one
FINAL_ANSWERto its parent. Pi custom messages carryMessage Type,Task name,Sender, andPayload. Reports wake idle parents; ordinary messages do not. Verify reports. They do not grant user permission. wait_agentblocks until mailbox activity, user steering, cancellation, or timeout. Last resort: a wait says nothing about what a running agent is doing. Read that agent's trace tail instead, then steer it withsend_message. Call this once with a short timeout and only when local work is done and nothing is left to read; do not loop it. A subagent with no agents of its own has nothing to wait for, and the engine returns at once. It returns{message, timed_out}, not the report body. Default: 30 seconds; minimum: 1 second; maximum: 1 hour. Smaller values are clamped; values above the maximum are rejected.list_agentsreturns{agents: [{agent_name, agent_status, transcript_path}]}. The path is absent until a child session opens. Settled agents remain available.path_prefixfilters a subtree, without a trailing slash. The root is listed as running while its tools are in use.interrupt_agentreturns{previous_status}. It interrupts the current turn without deleting the agent. Pi rejects self/ancestor interrupts from child tool calls to avoid deadlocks. Follow-ups can restart an interrupted agent. Interrupting an idle agent is a no-op.
Commands and the multi-agent-mode skill
/agentslists the team./agents stop <task_name>interrupts one agent./skill:multi-agent-mode <task>loads and submits the packaged skill in one step, with no review prompt. Delegation is optional; one implementer can be the right choice. The skill checks shared inputs, output paths, test state, and resource limits before a spawn or follow-up. Work orders name who owns each resource and when to release it. Builds and tests use stable inputs. The root follows the same ownership rules as its children. Steers must come from trace evidence. When conflicts cost more than the split saves, reduce the team. Verify the integrated result before reporting done.- The extension injects no system reminders and defines no delegation toggle. The six tools are available whenever the extension loads, and the policy lives in the skill plus the tool descriptions. These resource rules are policy, not runtime locks. The concurrency cap always applies; it is a ceiling, not a target or a CPU, RAM, or disk budget.
Persistence and compatibility
Transcripts live under ~/.pi/agent/subagents/agents/<agent_id>/. Team records
and unread child mailboxes live in ~/.pi/agent/subagents/teams/<root-session-id>.json.
Records use atomic file replacement. Reload or shutdown interrupts active
children, disposes their sessions, and suppresses completion wakes. After a
restart, followup_task reopens an agent's transcript; stale tasks never
start model calls on their own.
This replaces the old subagent{description,prompt,files,readOnly,resume}
interface. Legacy run files are left untouched, but are not imported into
the named-agent tree. Pass file paths in message and use followup_task to
continue an agent. Agents saved by the removed read-only mode re-inherit the
root's tools on their next run. The older
async design document describes the previous interface; this README is the
current contract.
Tool names and parameters follow Codex V2's tool schemas,
except for model. The packaged multi-agent-mode skill adapts OpenAI's
multi-agent instructions
and mode instructions
(Apache-2.0). Runtime choices follow Pi as described above, not Codex's wire protocol.
Checks
npm run check
npm test
Tests cover schemas, models, prompts, and history forks. They check mailbox races, interruption, shutdown, and persistence. Real Pi SDK tests use an offline scripted provider to check inherited tools, builtin overrides, deferred tools, guards, skill catalogs, nested agents, tool inheritance, and reloads. Tests make no live model calls.
Skill checks guard the policy text and its serial/parallel decision examples. They check skill loading and required wording. They do not prove that a model follows the policy or that the runtime locks resources.