@maplezzk/pi-interactive-subagents
Interactive async subagents for pi — spawn, orchestrate, and manage sub-agent sessions in multiplexer panes. Fork of HazAT/pi-interactive-subagents.
Package details
Install @maplezzk/pi-interactive-subagents from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@maplezzk/pi-interactive-subagents- Package
@maplezzk/pi-interactive-subagents- Version
3.13.1- Published
- Sep 7, 2026
- Downloads
- 1,286/mo · 277/wk
- Author
- maplezzk
- License
- MIT
- Types
- extension, skill
- Size
- 237.1 KB
- Dependencies
- 3 dependencies · 3 peers
Pi manifest JSON
{
"extensions": [
"./index.ts",
"../pi-extensions-i18n/index.ts"
],
"skills": [
"./SKILL.md"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-interactive-subagents
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.
Fork notice: This package is a fork of HazAT/pi-interactive-subagents (MIT license). Full credit to the original author HazAT for the design and implementation. Changes in this fork: monorepo integration and scoped npm publishing.
https://github.com/user-attachments/assets/30adb156-cfb4-4c47-84ca-dd4aa80cba9f
How It Works
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 — starting, active, waiting, stalled, or running. 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. Completion reminders are injected only after the model stops normally; user aborts and provider errors stay quiet.
╭─ Subagents ──────────────────────────── 2 running ─╮
│ 00:23 Scout: Auth (scout) active · bash 7m │
│ 00:45 Scout: DB (scout) waiting 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 independently
Install
pi install npm:@maplezzk/pi-interactive-subagents
Supported multiplexers:
- cmux
- tmux
- zellij
- WezTerm (terminal emulator with built-in multiplexing)
- herdr (terminal-native agent multiplexer)
- Otty (macOS terminal emulator with built-in multiplexing)
- Orca (agent workbench with built-in terminal multiplexing)
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 needed
# or
# start herdr (`herdr`), split a pane (prefix+v or prefix+-), then run `pi` in it
# or
# just run pi inside Otty — no wrapper needed
# or
# just run pi inside Orca — no wrapper needed
Optional: set PI_SUBAGENT_MUX=muxy|cmux|tmux|zellij|wezterm|herdr|otty|orca to force a specific backend. Herdr uses its original split layout by default; set PI_SUBAGENT_HERDR_MODE=tab for one background tab per subagent, or split to select pane splitting explicitly.
You can also configure it from inside Pi with /config:subagent. Run it without arguments for an interactive menu, or use /config:subagent auto|muxy|cmux|tmux|zellij|wezterm|herdr [split|tab]|otty|orca for a direct choice. For example, /config:subagent herdr tab persists both the Herdr backend and tab mode. The selection is persisted in Pi's user extension config directory; explicit PI_TERMINAL_MUX / PI_SUBAGENT_MUX and PI_SUBAGENT_HERDR_MODE environment variables take precedence. /subagent-config and /pi-subagent-config remain available as compatibility aliases.
Otty notes:
- Otty sets
TERM_PROGRAM=ottyautomatically when pi runs inside it; the backend detects this env var.- To drive subagent panes, Otty's IPC
send-keysmust be enabled. Addipc-allow-send-keys = trueto~/.config/otty/config.tomland reload Otty.- In Otty 1.0.4,
pane closeis unreliable. The backend'scloseSurfacekeeps the subagent state marker in sync even if the pane stays visually — your next subagent will still get a fresh split from the agent pane.
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=2500
To set a default model for all subagents, use PI_SUBAGENT_DEFAULT_MODEL. When unset, subagents inherit the parent session's model (i.e. no --model flag is passed to the child pi process):
export PI_SUBAGENT_DEFAULT_MODEL="anthropic/claude-sonnet-4-20250514"
Model resolution order (highest priority wins):
- Explicit
modelparameter in the subagent tool call - Agent definition frontmatter (
model:field in the.mdfile) PI_SUBAGENT_DEFAULT_MODELenvironment variable- Inherited from parent session (no
--modelpassed)
Subagent panes are created without stealing keyboard focus (cmux, tmux). Launch commands target child surfaces by explicit ID, so focus and command delivery are independent. Note: the interactive option controls parent status notifications, not terminal focus.
What's Included
Extensions
Subagents — 4 main-session tools + 2 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 |
/subagent <agent> <task> |
Spawn a named agent directly |
Bundled Agents
| 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.
Async Subagent Flow
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 · write 7m │
│ 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.
In-progress status updates
The widget tracks each Pi-backed sub-agent from a child-written runtime snapshot and labels it with a coarse state:
starting— launched, but no valid child snapshot has been observed yetactive— the child is doing observed runtime work: agent turn, provider request, streaming, or tool executionwaiting— the child finished a turn and is intentionally open for more input or another stagestalled— the parent has gone too long without a valid current child snapshot and can no longer trust the run is healthyrunning— fallback for backends without child snapshots (e.g. Claude)
These labels are no longer derived from session-file growth. Session JSONL is still used for transcript, resume, lineage, and result extraction, but Pi-backed liveness now comes from a small activity snapshot written by the child extension. A fixed internal watchdog marks a run as stalled when valid snapshots never appear, stop being readable, or stop matching the current child; valid long-running active or waiting states do not become stalled just because time passes. 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.
Interactive subagents stay silent. Long-running user-driven subagents (e.g. planner) do not wake the parent session on stalled/recovered transitions — the user is working directly in the subagent's pane, and a steer message there would just burn an orchestrator turn on a no-op "still waiting" ping. The widget still updates normally, and child snapshots are still recorded/classified regardless of the interactive setting. By default, agents with auto-exit: true are treated as autonomous and get stall pings; agents without it are treated as interactive and stay quiet. Override per-agent with interactive: true|false in frontmatter, or per-spawn with interactive: true|false on the tool call.
Configuration
The persisted Herdr mode, child-spawning policy, and explicit child-extension list are read from the user extension config at ~/.pi/agent/extensions/pi-interactive-subagents/config.json (respecting PI_CODING_AGENT_DIR). The status panel's status.enabled is read from the installed package's config.json, falling back to config.json.example. The package's config.json.example documents the available fields; add allowSubagentSpawning and subagentExtensions to the user config without overwriting its existing mux settings:
{
"herdrMode": "split",
"allowSubagentSpawning": false,
"subagentExtensions": [],
"status": {
"enabled": true
}
}
herdrMode accepts split (default, backward-compatible pane layout) or tab (one background tab per subagent). The /config:subagent herdr split|tab command updates this field. allowSubagentSpawning is a global switch for whether child subagents may create or manage other subagents; it defaults to false. Set it to true to enable the lifecycle tools in child sessions when the corresponding extension is selected. subagentExtensions is an optional list of extension paths to load explicitly in child sessions; automatic project/global extension discovery is disabled. Paths may be absolute, start with ~/, or be relative to PI_CODING_AGENT_DIR. subagent-done.ts is always loaded. Explicit deny-tools restrictions still apply.
config.json is gitignored so local overrides don't get committed.
Spawning Subagents
// Named agent with defaults from agent definition
subagent({ name: "Scout", agent: "scout", task: "Analyze the codebase..." });
// Child sessions start fresh; use session-mode: lineage-only when lineage metadata is useful
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: "..." });
Parameters
| 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 |
interactive |
boolean | derived | Mark this spawn as interactive (don't wake the parent on stall/recovery). Defaults to the agent's interactive frontmatter, otherwise the inverse of auto-exit. |
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) |
Interrupting a running subagent
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 immediately moves the child back to waiting, and stale pre-interrupt snapshots are ignored. If the child starts work later, newer snapshots return it 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.
caller_ping — Child-to-Parent Help Request
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.
caller_ping parameters:
message(required): What you need help with
subagent_resume parameters:
sessionPath(required): Path to the child session.jsonlfilename(optional): Display name for the resumed pane (defaults toResume)message(optional): Follow-up prompt to send after resumingautoExit(optional): Whether the resumed session should auto-exit after its next response. Defaults totruefor autonomous follow-up work; setfalsewhen resuming for an interactive handoff.
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 Workflow
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
Custom Agents
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
---
# My Agent
You are a specialized agent that does X...
Frontmatter Reference
| 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 or lineage-only |
spawning |
boolean | Legacy field retained for compatibility. Use the global allowSubagentSpawning setting to control child-session subagent lifecycle 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). Also determines the default value of interactive (see below). |
interactive |
boolean | derived |
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.
session-mode
Choose how a child 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 caller
All child tasks are delivered through an artifact-backed initial message. There is no full-context fork mode.
---
name: planner
session-mode: lineage-only
---
auto-exit
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) where the user drives the session
---
name: scout
auto-exit: true
---
interactive
Controls whether status transitions (stalled, recovered) wake the parent session with a steer message.
Default: the inverse of auto-exit. Autonomous agents (auto-exit: true) are non-interactive and ping the parent on stall/recovery; agents without auto-exit are interactive and stay quiet. Bare spawns with no agent defs are treated as interactive.
Why it exists: Interactive agents can run for minutes or hours while the user thinks, types, and reads in the subagent's pane. Child snapshots still update the widget, but stalled/recovered supervision messages rarely need to wake the parent for user-driven sessions. Skipping the steer keeps the parent quiet until the child actually finishes.
When to override:
- Set
interactive: falseon an agent that doesn't auto-exit but you still want stall pings for - Set
interactive: trueon an autonomous agent you'd rather check on yourself
---
name: planner
# interactive defaults to true because auto-exit is not set
---
Or per spawn:
subagent({ name: "Scout", agent: "scout", interactive: true, task: "..." });
Tool Access Control
Child subagent sessions cannot create or manage other subagents unless the global allowSubagentSpawning setting is true. When it is false (the default), the lifecycle tools subagent, subagent_interrupt, subagents_list, and subagent_resume are not registered in child sessions. The child-only subagent_done tool remains available; caller_ping remains available for requesting help from the parent.
spawning (legacy)
The global allowSubagentSpawning setting is authoritative for child sessions. Existing spawning: false fields remain accepted for compatibility but do not override the global setting.
deny-tools
Fine-grained control over individual extension tools:
---
name: focused-agent
deny-tools: subagent
---
Global setting
The global switch applies uniformly to planner, worker, reviewer, scout, and custom child sessions. Leave allowSubagentSpawning as false unless a child agent is explicitly expected to create or manage another subagent.
Role Folders
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
---
Tools Widget
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, ...
Requirements
- pi — the coding agent
- One supported multiplexer:
cmux pi
# or
tmux new -A -s pi 'pi'
# or
zellij --session pi # then run: pi
# or
# just run pi inside WezTerm
# or
# start herdr (`herdr`), split a pane, then run `pi` in it
# or
# just run pi inside Otty
# or
# just run pi inside Orca
Optional backend override:
export PI_SUBAGENT_MUX=cmux # or tmux, zellij, wezterm, herdr, otty, orca
Acknowledgements
The sub-agent status supervision and turn-only interruption features were inspired by RepoPrompt's sub-agent snapshot polling and run cancellation features.
License
MIT
Naming composition
Subagents do not depend on pi-naming. Terminal creation supplies an initial surface label where supported; this extension does not schedule delayed title overwrites or rename the parent's terminal when /plan runs. Agent identity and activity remain visible in the subagent widget.
Each launch (Pi or Claude) and Pi resume writes fresh PI_TERMINAL_RENAME_CONTEXT ownership data through pi-terminal-mux, replacing inherited values. This allows a cooperating naming extension to update only the child's explicitly owned terminal target, never the shared workspace. Shared or unverified window/tab targets remain unchanged.
To use automatic titles or /rename in Pi children, explicitly add the installed pi-naming entrypoint to subagentExtensions. Merely installing both packages in the parent does not enable extensions in isolated child sessions. Naming uses the same terminal-mux protocol and does not import this package.
Publication requires a terminal-mux dependency version providing the ownership API.