Open-source project
HazAT/pi-interactive-subagents avatar
HazAT/pi-interactive-subagents

pi-interactive-subagents, running background agents in multiplexer panes

Interactive subagents for pi — spawn, orchestrate, and manage sub-agent sessions in cmux terminals

723 stars184 forksTypeScriptMIT

At a glance

What is it?
pi-interactive-subagents is a pi extension that spawns sub-agent sessions into separate cmux, tmux or zellij panes and returns immediately, so the main session keeps working while results are steered back in later. Its state labels come from a runtime snapshot written by the child, not from watching a session file grow.
Who is it for?
Adopt pi-interactive-subagents if you already drive pi inside a multiplexer and you want side investigations to run in parallel with your own turn rather than blocking it, and expect the bundled planner, scout, worker, reviewer and visual-tester definitions to be starting points you override in .pi/agents/ rather than a fixed team.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 164 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

subagent() returns immediately and the work happens in a pane

The core promise is non-blocking. Call subagent() and it returns at once with a started acknowledgement while the sub-agent runs in its own terminal pane inside the multiplexer. A widget above the input line shows every running agent with a state label and an elapsed time, so the main session stays usable. The five labels are starting, active, waiting, stalled and running. Starting means the child has been launched but no valid snapshot has been observed yet. Active means observed runtime work: an agent turn, a provider request, streaming, or a tool execution. Waiting means the child finished a turn and is deliberately open for more input. Stalled means the parent has gone too long without a valid current snapshot and no longer trusts the run is healthy. Running is a fallback for backends that produce no snapshot at all, with Claude given as the example. When a sub-agent finishes, its result is steered back into the main session as an async notification, which triggers a new turn so the agent can act on it.

Calling it twice is all the parallelism you need

There is no queue, no scheduler and no concurrency limit to configure. Parallel execution is just multiple calls, each of which returns immediately and runs concurrently, and each of which steers its result back independently as it finishes:

typescript
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

The name is a display label for the widget, the agent field selects which definition runs, and the task is the instruction. The documented flow spells out the sequence: the agent calls subagent() and gets an immediate started, the sub-agent runs in its pane, the widget shows live status, the user keeps chatting in a fully interactive main session, the sub-agent finishes, and the result comes back as a normal completion or failure that the main agent processes with new context. Completion messages render with a colored background and expand with Ctrl+O to reveal the full summary and the session file path, which is what lets you jump back into a finished run.

Installing the extension and starting pi inside a multiplexer

Installation is a single pi package command:

bash
pi install git:github.com/HazAT/pi-interactive-subagents

Then start pi inside one of the supported multiplexers. Four are listed: cmux, tmux, zellij and WezTerm, the last of which is a terminal emulator with multiplexing built in and needs no wrapper. The launch forms given are:

bash
cmux pi
# or
tmux new -A -s pi 'pi'
# or
zellij --session pi   # then run: pi

Two environment variables tune the behaviour. PI_SUBAGENT_MUX takes cmux, tmux, zellij or wezterm if you want to force a specific backend rather than let the extension detect one. PI_SUBAGENT_SHELL_READY_DELAY_MS exists because a slow shell startup can cause sub-agent commands to be dropped before the prompt is ready, and it defaults to 500:

bash
export PI_SUBAGENT_SHELL_READY_DELAY_MS=2500

If you see commands disappearing on a slow machine, that variable, not the multiplexer, is the first thing to raise.

Focus is not the same thing as the interactive option

Two design details are called out because they cause confusion. Sub-agent panes are created without stealing keyboard focus on cmux and tmux, and launch commands target child surfaces by an explicit ID, so where focus sits and where a command is delivered are separate concerns. The interactive option controls parent status notifications rather than terminal focus, which is a distinction that is easy to get wrong when you first see a sub-agent pane open and the cursor stays where it was. The widget itself is the place where state is visible, since the pane is deliberately not the focus target. The README also notes that the state labels are no longer derived from session-file growth, which is a change in how the extension decides a run is healthy rather than a cosmetic detail.

Where the state labels come from and what stalled really means

Liveness for Pi-backed sub-agents is decided by a small activity snapshot written by a child extension, and the widget labels each agent from that snapshot. Session JSONL is still used, but only for the transcript, resume, lineage and result extraction, not for deciding whether a run is progressing. A fixed internal watchdog marks a run stalled when valid snapshots never appear, stop being readable, or stop matching the current child. The important part is what the watchdog does not do: a valid long-running active or waiting state does not become stalled just because time passes, so a slow but healthy agent is not killed off by a timer. When a run enters stalled or recovers from it, the parent agent receives a steer message so it can react. A stalled label therefore means the parent lost trust in the run, which is a different claim from the child having stopped working, and if you are triaging a stuck task that distinction is where you should start.

Four tools, three commands and five bundled agent definitions

The extension registers four main-session tools and three commands, plus one tool available only to a sub-agent. The tools are subagent for spawning, subagent_interrupt to interrupt a running Pi-backed sub-agent's current turn, subagents_list to list available agent definitions, and subagent_resume to resume a previous session asynchronously. The commands are /plan for a full planning workflow, /iterate to fork into a subagent for quick fixes, and /subagent <agent> <task> to spawn a named agent directly. Five agents are bundled. The planner runs on Opus with medium thinking and handles requirement clarification, approach exploration, plans and todos. The scout runs on Haiku for fast codebase reconnaissance. The worker runs on Sonnet and implements tasks from todos. The reviewer runs on Opus with medium thinking for bugs, security issues and correctness. The visual-tester runs on Sonnet and does visual QA through Chrome CDP with screenshots, responsive and interaction testing.

Agent discovery is project-local first, and your own file wins

Definition lookup follows a strict priority order: project-local in .pi/agents/, then global in ~/.pi/agent/agents/, then the package-bundled definitions. Any bundled agent can be overridden by placing your own version in the higher-priority location, so a team that wants a different scout or a different model for the worker does not have to patch the package. The repository layout backs this up, with a .pi/ directory and an agents/ directory at the top level, alongside a pi-extension/ directory holding the extension entry point that package.json registers as ./pi-extension/subagents/index.ts. The package declares peer dependencies on @mariozechner/pi-coding-agent, @mariozechner/pi-tui and @sinclair/typebox, all with a wildcard range, and development versions pinned at ^0.65.0 with typebox at ^0.34.49. The tests are plain node:test, with node --test test/test.ts for the unit run and a separate integration glob using --test-concurrency=1.

Where this extension is the wrong tool

The first constraint is structural. Sub-agents are hosted in multiplexer panes, so if your pi session is not running inside cmux, tmux, zellij or WezTerm, there is nowhere for them to live. WezTerm counts only because it has its own multiplexer, and the README is explicit that no wrapper command is needed for it. The second limitation is that a background sub-agent cannot ask you a question, because its result comes back as a completion, not a prompt, and the waiting state means the child is idle and open for more input rather than that it is about to interrupt your turn. The third is the model coupling. The bundled definitions name specific models per role, so if your pi setup does not have those models available the mapping has to be redone in your own agent files rather than left as is. The fourth is that the extension is a pi extension, not a standalone tool, and it targets the pi coding agent, so it does nothing for other agent harnesses.

Editorial conclusion

Adopt pi-interactive-subagents if you already drive pi inside a multiplexer and you want side investigations to run in parallel with your own turn rather than blocking it, and expect the bundled planner, scout, worker, reviewer and visual-tester definitions to be starting points you override in .pi/agents/ rather than a fixed team. Skip it if your pi session is not running under cmux, tmux, zellij or WezTerm, since panes are how the sub-agents are hosted, and if you need the whole thing in one terminal without multiplexing there is no path. Verify first that your agent names and models exist as described, and read the stalled semantics before you trust the widget, because a stalled label means the parent stopped trusting the run rather than that the child stopped working. The last push was on 2026-05-12 and the release v3.7.2 landed the same day.

Frequently asked questions

How do I install pi-interactive-subagents?

Run pi install git:github.com/HazAT/pi-interactive-subagents from inside a pi session. The extension then needs pi to be started inside cmux, tmux, zellij or WezTerm, because sub-agents run in multiplexer panes.

What do the subagent states starting, active, waiting, stalled and running mean?

Starting means no valid child snapshot has been seen yet, active means observed runtime work, waiting means the child finished a turn and is open for more input, stalled means the parent no longer trusts the run is healthy, and running is a fallback for backends without child snapshots.

How do I override one of the bundled agent definitions?

Agent discovery goes project-local in .pi/agents/ first, then global in ~/.pi/agent/agents/, then the package-bundled definitions. Placing your own version of an agent in either higher-priority location overrides the bundled one.

Which multiplexer does pi-interactive-subagents use by default?

The extension detects the multiplexer and supports cmux, tmux, zellij and WezTerm. You can force a specific backend by setting PI_SUBAGENT_MUX to cmux, tmux, zellij or wezterm.

What is PI_SUBAGENT_SHELL_READY_DELAY_MS for?

It controls how long the extension waits for a shell prompt before sending sub-agent commands, and it defaults to 500. Raising it helps when a slow shell startup causes commands to be dropped before the prompt is ready.

Official sources

  1. HazAT/pi-interactive-subagents on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/hazat-pi-interactive-subagents.svg)](https://hysenlabs.com/projects/hazat-pi-interactive-subagents)

Was this useful?