@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.

Packages

Package details

extensionskill

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=otty automatically when pi runs inside it; the backend detects this env var.
  • To drive subagent panes, Otty's IPC send-keys must be enabled. Add ipc-allow-send-keys = true to ~/.config/otty/config.toml and reload Otty.
  • In Otty 1.0.4, pane close is unreliable. The backend's closeSurface keeps 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):

  1. Explicit model parameter in the subagent tool call
  2. Agent definition frontmatter (model: field in the .md file)
  3. PI_SUBAGENT_DEFAULT_MODEL environment variable
  4. Inherited from parent session (no --model passed)

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 yet
  • active — the child is doing observed runtime work: agent turn, provider request, streaming, or tool execution
  • waiting — the child finished a turn and is intentionally open for more input or another stage
  • stalled — the parent has gone too long without a valid current child snapshot and can no longer trust the run is healthy
  • running — 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 .jsonl file
  • name (optional): Display name for the resumed pane (defaults to Resume)
  • message (optional): Follow-up prompt to send after resuming
  • autoExit (optional): Whether the resumed session should auto-exit after its next response. Defaults to true for autonomous follow-up work; set false when resuming for an interactive handoff.

Interaction flow:

  1. Child calls caller_ping({ message: "Not sure which schema to use" })
  2. Child session exits (like subagent_done)
  3. Parent receives a steer notification: "Sub-agent Worker needs help: Not sure which schema to use"
  4. Parent resumes the child session via subagent_resume with the response
  5. 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_ping is 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 caller
  • lineage-only — fresh blank child session with parentSession linkage, 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_end event)
  • 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: false on an agent that doesn't auto-exit but you still want stall pings for
  • Set interactive: true on 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
    • tmux
    • zellij
    • WezTerm
    • herdr (terminal-native agent multiplexer)
    • Otty (macOS terminal emulator; needs ipc-allow-send-keys = true)
    • Orca (agent workbench; sets TERM_PROGRAM=Orca automatically)
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.