pi-subagents is a highly curated multi-agent framework for Pi agent harness.
It began as a fork of HazAT/pi-interactive-subagents, then grew into a monumental refactor: named agents, interactive panes, background workers, async parallelism, blocking agents, child-to-parent communication, forked context, a beautiful TUI widget, orchestrator mode, and much more!
Use it when one agent should hand work to another agent instead of trying to do everything in one transcript. Interactive children open in Herdr, cmux, tmux, zellij, or WezTerm; background children run headlessly.
2026-04-16_16-46-05.mp4
Note
Building with AI doesn’t have to be a solo grind.
Join our Discord community to meet other people exploring the latest models, tools, workflows, and ideas: https://discord.gg/whhrDtCrSS
We talk about what’s new, what’s useful, and what’s actually worth paying attention to in AI.
And if you want more than conversation, members also get access to heavily discounted AI products and services — including deals on tools like ChatGPT Plus and more for just a few dollars.
pi install git:github.com/edxeth/pi-subagentsA subagent is a named agent file plus a launch policy.
The agent file says who the child is and how it should run. The parent still owns the decision to launch it. The child owns the task it receives.
Two axes matter:
interactiveorbackground: where the child runs- async or sync: whether the parent waits
interactive means foreground. Pi opens a visible surface through Herdr, cmux, tmux, zellij, or WezTerm. Normal launches use a backend-specific surface, such as a tab, window, split, or stacked pane.
background means headless. Pi starts a pi -p child process without opening a pane.
Async means the parent gets a “started” result and the child answer comes back later. Sync means the parent waits for the child answer before it continues.
Interactive children open in your current terminal backend. pi-subagents supports Herdr, cmux, tmux, zellij, and WezTerm.
Start pi inside the backend you want to use. Leave PI_SUBAGENT_MUX unset to let Pi detect it, or set it to herdr, cmux, tmux, zellij, or wezterm to force one.
The backend command must exist, and Pi must be able to see the current pane or session context. If no supported backend is active, interactive launches fail with a setup hint.
Normal launches use a backend-specific surface. Herdr creates a new tab in the parent workspace and labels it with the child session title prefixed by the tab's positional index (matching ctrl+b 1..9), such as 2: [reviewer] Auth implementation review. Other backends may use windows, splits, or stacked panes.
You can turn the parent session into an orchestrator — an agent that can only delegate. It spawns sub-agents, waits for results, and synthesizes answers. It cannot read files, run commands, edit code, or search the codebase itself.
PI_ORCHESTRATOR_MODE=1 piExport it in your shell rc to enable permanently:
export PI_ORCHESTRATOR_MODE=1Enable that and two things change:
- Tool restriction. Removes read, bash, edit, write, grep, find, and every other tool except subagent, subagent_kill, subagent_resume. The LLMs cannot call what they cannot see.
- System prompt replacement. Pi's "expert coding assistant" prompt gets
replaced with one that defines the orchestrator role: decompose, delegate,
synthesize. The replacement preserves Pi's
APPEND_SYSTEM.mdcontent.
Children do not inherit any of this. Each child is a separate pi process with
its own system prompt chain — Pi default plus the child's agent body.
system-prompt: append appends to Pi's default, not the orchestrator prompt.
system-prompt: replace replaces Pi's default with the child's body. Neither
sees the orchestrator identity.
Models default to doing work themselves. Given the chance, they read the file, write the fix, run the test. That works for single-agent tasks. For multi-agent workflows it defeats the purpose — you pay for two agents to race each other, and the parent floods its context with execution details instead of staying focused on coordination.
Every production multi-agent framework hits this same limit. Anthropic's
Claude Code has COORDINATOR_MODE with the same mechanism: restricted tool
set, replacement system prompt, worker isolation. OpenAI Codex users file
issues asking for a mode where the main agent "cannot execute, only
delegate." The ADCS delegation chain spec encodes it as a scope-intersection
invariant: each hop narrows permissions, never widens.
The research calls it brain/hands separation. The orchestrator holds the plan. Workers hold the execution context. You keep them apart because mixing them makes both worse — the orchestrator loses sight of the plan when it starts reading files, and workers get confused about their role when they see orchestrator-level strategy in their context.
Orchestrator mode shines on tasks that decompose into parallel work: independent research questions, multiple implementation targets, verify-after- write cycles. The orchestrator defines the structure, dispatches each piece to the right agent, reads results, and writes the next round of instructions.
Simple requests do not benefit. A single sub-agent handles those faster.
Agents live here:
.pi/agents/in the project~/.pi/agent/agents/globally, or$PI_CODING_AGENT_DIR/agents/when that env var is set
Project agents override global agents with the same name.
A minimal agent:
---
name: scout
description: Inspect the codebase and report the relevant files.
mode: background
auto-exit: true
tools: read,grep,find,ls
---
You are a codebase scout. Find the relevant files, read enough to be useful, and return a concise map of what matters.The description matters. Pi uses it for ambient awareness, explained next.
For a fuller example of the intended style, see the scout agent gist by edxeth. It shows the shape this package works best with: a sharp role, an explicit contract, and little room for interpretation.
| Field | Default | What it controls |
|---|---|---|
name |
filename | Stable agent name used by agent: "..." |
description |
unset | One-line routing hint for ambient awareness |
enabled |
true |
Set false to hide and block the agent |
model |
Pi default | Child default model, including optional thinking suffix. When unset, the child inherits the parent's model. |
thinking |
model default | Child thinking level. When unset, the child inherits the parent's thinking level. |
allow-model-override |
true |
Whether the parent Pi session may launch or resume this agent with a different model or thinking level. Leave it alone if you want to choose models per task from the parent chat. Set false when this agent should always use the model written in its file. |
allowed-models |
unset | Extra exact model refs the parent may choose when allow-model-override is enabled. The agent model is implicitly allowed and does not need to be repeated. provider/model allows any thinking level for that model; provider/model:thinking allows only that thinking level. |
cwd |
parent cwd | Working directory for the child |
extensions |
all |
Which extension code loads in the child: all, none, or a comma-separated allowlist |
tools |
all |
Child tool availability: all, none, or a comma-separated allowlist of Pi tool names. Lists may include built-in, extension/custom, and protocol tools. none disables built-in tools while preserving extension/custom tools unless denied. |
deny-tools |
unset | Final comma-separated tool names to remove from the child after built-in tools, extensions, and protocol tools are selected |
skills |
all |
Child skill availability: all, none, or a comma-separated allowlist resolved by skill name |
inject-skills |
unset | Comma-separated skills to load into the child prompt before the task |
no-context-files |
false |
Skip trusted project context-file discovery in the child. With the default trust-project: false, Pi already ignores project-local context files. |
no-session |
false |
Use an ephemeral child session file and delete it after completion |
trust-project |
false |
Whether interactive child launches pass Pi's --approve flag and trust project-local files/settings. Background children always generate --no-approve for safety; use flags only as an explicit advanced override. |
auto-exit |
false |
Close the child after a normal completion |
system-prompt |
task body | append uses --append-system-prompt; replace uses --system-prompt |
session-mode |
lineage-only |
standalone, lineage-only, or fork |
flags |
unset | Extra CLI flags passed to the child pi process (e.g. --verbose or --some-custom-flag). Appended after all generated args — last-wins semantics against conflicting generated args, including --approve / --no-approve. Use only as an advanced escape hatch for extension-registered flags or pi built-in flags not covered by other frontmatter fields. |
env |
unset | Line-based KEY=VALUE pairs passed as environment variables to the child process. Use YAML block syntax for values with commas or =. PI_CODING_AGENT_DIR is special: when set here, it is resolved before launch and becomes the child's Pi config/session root. ~/ is expanded. Internal PI vars such as PI_SUBAGENT_* still take precedence if names conflict. |
task-expansion |
unset | Set shell when the task may include shell placeholders that Pi resolves before launch. Pi runs each placeholder from the child's target cwd, gives it 30 seconds, replaces it with captured output, and gives that prepared task to the child. The command receives PI_WORKSPACE; long output is cut with [output truncated]. Leave unset unless you trust the task text to execute shell commands. |
spawning |
false |
Allow the child to launch subagents |
async |
true |
false makes the launch sync |
mode |
interactive |
interactive pane or background process |
parent-close-policy |
terminate |
What happens to the child when the parent session exits: terminate (kill) or continue (leave running) |
Use YAML block syntax for more than one env var:
env: |
PI_CODING_AGENT_DIR=~/.pi-scout/agent
PI_SAFETYNET_DEFAULT_MODE=explore
SOME_VALUE=value,with,commasPi splits env by line. It does not split values by comma. When you set PI_CODING_AGENT_DIR, the child uses that directory for its Pi config and sessions.
trust-project controls Pi's project-local trust boundary. The default false passes --no-approve, so child sessions ignore project-local settings and project-local context files such as AGENTS.md/CLAUDE.md even when the parent project was previously approved. Set trust-project: true only for interactive children that should inherit those project-local resources. Background children still generate --no-approve; flags is the explicit advanced escape hatch if you need to override that safety default.
task-expansion: shell prepares task context before launch for small models that should not have to plan tool calls. It is opt-in because the parent task text becomes shell input and runs in the parent Pi process before the child starts. Commands execute in source order from the child's effective cwd; each placeholder gets 30 seconds before Pi inserts a timeout diagnostic. Use $PI_WORKSPACE or ${PI_WORKSPACE} inside the shell command to read the workspace path from the environment. This explicit opt-in works in every parent mode, including orchestrator mode. Use explicit shell placeholders:
Summarize the files changed.
Inline status: !`git status --short`
Changed files:
```!
git diff --name-only
```
Follow these conventions:
!`cat daily-git-brief.md`Pi expands those placeholders into command output before writing the child task artifact. Ordinary Markdown code fences are treated as literal examples, so inline placeholders inside language-tagged code fences such as sh or text do not execute. Plain standalone lines like !git status are not expanded; use inline !`git status` or a fenced shell command block. Because project-local agent files can opt into this behavior, only use task-expansion: shell in agents whose launch tasks you trust to become shell input.
Named-agent frontmatter wins over duplicate launch-time fields such as tools, cwd, and mode. model and thinking are different: while you are in a parent Pi session, you can ask Pi to run a subagent with a specific model or thinking level for that one launch or resume. That works by default. If an agent file sets allow-model-override: false, Pi ignores those per-launch model choices and uses the model from the agent file, or the inherited Pi model if the file does not name one. Use that opt-out for agents whose quality, cost, or safety depends on a specific model.
Use allowed-models when an agent should have a small exact model menu:
---
name: reviewer
model: zai/glm-5.1:high
allow-model-override: true
allowed-models: openai/gpt-5.5:low, nahcrof/glm-5.1:off, anthropic/claude-opus-4-8
---model is the default and is always allowed. allowed-models lists the other models Pi may use for this agent, so you do not need to repeat model. The :thinking suffix is optional: provider/model:low allows only that thinking level, while provider/model allows the model with whatever thinking level Pi resolves. If allow-model-override: false, Pi ignores launch-time and resume-time model choices as usual.
Ambient awareness is the quiet note Pi gives the parent model about available agents.
Pi stores that note as a hidden custom message. The user does not see it as chat. The LLM sees it as context before it decides whether to call subagent.
The note contains agents with description fields and labels each one by what the child would see:
isolated contextmeans the child starts clean. The parent must write a self-contained task.forked contextmeans the child sees the parent transcript. The parent can rely on context already in the chat.
Pi sends ambient awareness once when a top-level session first needs it, then sends a fresh copy after reload if the agent list changed.
Normal child sessions do not receive ambient awareness, even with spawning: true. They sit under a parent that made the first routing decision. A standalone child can receive its own ambient awareness because Pi treats it as a root session.
Agents without descriptions remain launchable, but they do not appear in ambient awareness.
Async launch:
- parent calls
subagent - child starts
- parent receives a “started” result
- child result comes back later by steer
Sync launch:
- the agent has
async: falsein frontmatter - parent waits
- tool result contains the child result
When one tool-call batch contains at least one sync/blocking subagent, the whole batch becomes a barrier. Pi still launches every child with its own frontmatter, so an async: true child keeps async launch metadata for resume, but the parent waits until every child in that batch completes and receives all results as tool results.
subagent(sync), subagent(async), subagent(async) → all launch
→ parent waits until all three finish
→ returns all three completed results
Pure async batches stay detached: the parent receives started results and child results arrive later by steer.
After a pure async launch, the parent should get out of the way unless it has separate work. If the parent keeps solving the delegated task, you paid for two agents to race each other.
By default, a successful async launch ends the parent turn after the current tool batch. The children keep running. Their results come back later.
Leave PI_SUBAGENT_DISABLE_COORDINATOR_ONLY_TURN unset, or set it to 0, to keep that guard. Set it to 1 when you want the parent model to keep going after async launches.
PI_SUBAGENT_DISABLE_COORDINATOR_ONLY_TURN=1 piThat only removes the runtime stop. The parent still owns only work it did not delegate.
A child session has two questions:
- should Pi attach it to the parent session tree?
- should the child model see the parent transcript?
lineage-only attaches the child to the tree and starts the model clean. This is the default. You keep lineage, resume paths, and artifact attribution without copying the whole parent chat.
standalone starts clean and skips the parent link. Use it for unrelated work.
fork attaches the child to the tree and copies parent context. Use it when the child needs decisions, files, or prior results already in the parent transcript.
The isolated context and forked context labels from ambient awareness describe model memory. They do not describe where Pi stores the child in the session tree. A lineage-only child is still a child of the parent session even though its model starts clean.
For nested launches, parent means the session that spawned the child:
top-level session
└── child session
└── grandchild session
A fork copies the entire parent session into a new child run. The child inherits all user messages, assistant responses, tool calls, and tool results from the parent transcript.
When the parent model has a larger context window than the child model, the inherited history may exceed what the child can fit. Pi handles this automatically — the child's native compaction trims inherited messages at LLM call time using the child model's actual context window and tokenizer. No manual budget configuration is needed.
A fork also gets a handoff marker. Pi appends a short system-prompt note, then writes a hidden custom message with a <subagent-boundary> tag at the end of the copied transcript. The tag says: the old messages are background, and the next user message is the child task.
That marker prevents a common failure. A child can read the parent's old role, old tools, or old task and start acting like the parent. The marker tells it where the fork begins.
The marker also steers the model. If you want a raw fork with no marker and no boundary instruction, set:
PI_SUBAGENT_DISABLE_CHILD_CONTEXT_BOUNDARY=1no-session: true gives the child a temporary session file and deletes it after completion.
For fork, Pi seeds that temporary file with the parent session content. For lineage-only, Pi also gives the child inherited context because there is no persistent child file where it can store lineage metadata.
Use no-session: true for disposable children. Do not use it when you need resume, caller_ping, or durable child history.
A child can finish in three ways.
Use auto-exit: true for autonomous agents. The child exits after a normal assistant completion.
Manual-lifecycle children get a subagent_done tool. The child writes its final assistant message, calls subagent_done, and Pi returns that final message to the parent.
Pi hides subagent_done for auto-exit: true agents. If you want an interactive child that only the operator can close, add subagent_done to deny-tools.
Use caller_ping when the child needs the parent. The child sends a message up, exits, and leaves a session file that the parent can resume.
subagent_resume starts an existing child session again. You can pass a follow-up task.
Resume tries to preserve the original launch shape: mode, model, prompt style, cwd, tools, extensions, and lifecycle settings. A resumed child should continue as the same child, even if the agent file changed after the first launch.
The child's final assistant message is its output.
For large output, let the child use Pi's write tool and mention the path in its final message.
extensions, tools, and deny-tools shape the Pi capabilities available to the child model. They are not a sandbox for untrusted code. Loaded extensions still execute with the child process permissions, and freeform flags can override generated CLI arguments. Use OS or container isolation for hard security boundaries.
Children load all extensions by default. Omit extensions or set extensions: all for the default Pi extension set.
Set extensions: none to launch with no normal extensions. pi-subagents still injects its mandatory internal helper so child lifecycle and result delivery continue to work.
Set a comma-separated allowlist when you want a smaller child environment.
---
name: reviewer
extensions: .pi/extensions/safe-tools.ts, npm:@foo/bar
---When extensions is none or an allowlist, Pi launches the child with --no-extensions, injects the subagent protocol helper, then loads only the allowlisted extensions.
Local paths stay paths. Package and remote sources keep their normal prefixes:
---
name: reviewer
extensions: ./extensions/local.ts, npm:@foo/bar, git:github.com/user/repo
---skills controls which skills the child Pi process can use.
---
name: reviewer
skills: all
---skills: all is the default. Pi keeps its normal skill discovery: project skills, global skills, settings, packages, and extension-provided skills.
---
name: reviewer
skills: none
---skills: none launches the child with --no-skills. The child has no discovered skills, and inject-skills is not allowed.
---
name: reviewer
skills: pua,torpathy
---A comma-separated list is an allowlist. pi-subagents resolves each name through Pi's resource loader, then launches the child with --no-skills --skill <resolved-path> .... Only those named skills are available.
skills resolves names from the same places Pi sees skills, including:
.pi/skills/.agents/skills/- global skill directories
- settings and package resources
- skills bundled by extension packages listed in
extensions
Package skill example:
{
"name": "my-pi-package",
"pi": {
"extensions": ["./extensions"],
"skills": ["./skills"]
}
}An agent can allowlist a skill from that package by loading the package and naming the skill:
---
name: reviewer
extensions: ./path/to/my-pi-package
skills: packaged-reviewer
---inject-skills controls which available skills start inside the child task context.
---
name: reviewer
skills: pua,torpathy
inject-skills: torpathy
---inject-skills reads the selected SKILL.md files, strips frontmatter, and prepends <skill> blocks to the child task artifact. Multiple injected skills appear in order before the task. The child gets one startup task, so it cannot answer between injected skills and the task.
Injected skills must be available under skills. These fail before launch:
skills: none
inject-skills: puaskills: pua
inject-skills: torpathyBy default, injected skills use Pi's native skill shape:
<skill name="pua">
References are relative to /path/to/pua.
...skill body...
</skill>If pi-better-skills is loaded for the child, injected skills use its path-context shape:
<skill name="pua">
<skill_context>
<skill_dir>/path/to/pua</skill_dir>
<workspace_dir>/path/to/workspace</workspace_dir>
<path_policy>
Relative file references in this SKILL.md normally resolve from skill_dir when they exist there.
Plain workspace commands like git status and bun test usually run in the workspace unless instructed otherwise.
Use $PI_SKILL_DIR/path for explicit bundled skill files.
Use $PI_WORKSPACE/path for explicit workspace/project files.
</path_policy>
</skill_context>
...skill body...
</skill>Load pi-better-skills like any other child extension:
---
name: researcher
extensions: git:github.com/edxeth/pi-better-skills
skills: deep-research
inject-skills: deep-research
---The tools field narrows the child to a Pi tool allowlist. Use built-in names such as read and bash, extension/custom names such as mcp, and protocol names such as caller_ping when they should be part of the final allowlist. Pi-subagents keeps required child protocol tools available in narrowed allowlists. The optional set_tab_title protocol tool is added only when PI_SUBAGENT_ENABLE_SET_TAB_TITLE=1. When tools is omitted or set to all, Pi keeps its default active tool set. When tools is none, Pi disables built-in tools while preserving extension/custom tools unless you deny them.
Pi silently ignores --tools names that are not registered by a built-in or a loaded extension. That means a typo (for example tools: read,edti) leaves the child silently without edit. pi-subagents surfaces a non-blocking warning in the subagent result when a name is within one edit of a built-in (edti→edit, raed→read); the launch still proceeds, because a near-miss name can be a legitimate custom tool (for example hash is one edit from bash). pi-subagents cannot validate arbitrary extension/custom tool names before the child loads its extensions, so ensure every custom/extension name in tools: is registered by an extension listed in extensions:.
deny-tools is a final named tool denylist. It can remove built-in Pi tools, extension/custom tools, or pi-subagents protocol tools after they have otherwise been selected.
---
name: reviewer
tools: read,grep,mcp
extensions: npm:pi-mcp-adapter
deny-tools: bash,edit,write,ask_user
---spawning defaults to false. That removes subagent and subagent_resume from children. Set spawning: true only for coordinator agents.
Set parent-close-policy in the agent frontmatter:
---
name: scout
parent-close-policy: continue
---| Value | Effect |
|---|---|
terminate |
Stop the child when the parent session exits |
continue |
Leave the child running and stop delivering its result to the closed parent |
The default is terminate.
The parent session gets a live widget above the editor. It shows running children, elapsed time, activity, and context usage.
Every subagent call requires both a strict name and a short title.
nameis the machine handle used in launch/result text and kill/wait targeting. Use lower-kebab<scope>-<role>, 2-4 words, max 32 characters:auth-scout,diff-reviewer,session-tester.titleis the human label shown in the widget/session UI. Use sentence-case prose, 3-8 words:Auth implementation map,Local diff bug review.
Child sessions can also get session titles like:
[scout] Auth flow reconnaissance
Disable child session titles with PI_SUBAGENT_DISABLE_SESSION_TITLES=1.
By default, the extension launches children with the same Pi entrypoint it can infer from the parent. If your real Pi command goes through a wrapper, set it:
PI_SUBAGENT_PI_COMMAND="my-wrapper pi" my-wrapper piThe wrapper applies to new children and resumed children. Quoted paths work:
PI_SUBAGENT_PI_COMMAND="'/path with spaces/my-wrapper' pi" piUser-facing knobs:
| Variable | Use |
|---|---|
PI_ORCHESTRATOR_MODE |
Set 1 to turn the parent into an orchestrator (delegation-only tools, replacement system prompt) |
PI_SUBAGENT_PI_COMMAND |
Launch children through a wrapper command |
PI_SUBAGENT_MUX |
Force herdr, cmux, tmux, zellij, or wezterm |
PI_CODING_AGENT_DIR |
Use a different Pi agent config root |
PI_SUBAGENT_DISABLE_COORDINATOR_ONLY_TURN |
Set 1 to let the parent keep running after async launches |
PI_SUBAGENT_DISABLE_CHILD_CONTEXT_BOUNDARY |
Set 1 for raw forks with no boundary marker |
PI_SUBAGENT_DISABLE_SESSION_TITLES |
Disable automatic child session names |
PI_ARTIFACT_PROJECT_ROOT |
Override internal artifact storage root |
PI_SUBAGENT_SHELL_READY_DELAY_MS |
Change the pane startup delay before Pi sends the child command |
PI_SUBAGENT_ENABLE_SET_TAB_TITLE |
Register the optional set_tab_title tool |
PI_SUBAGENT_RENAME_TMUX_WINDOW |
Let set_tab_title rename the tmux window |
PI_SUBAGENT_RENAME_TMUX_SESSION |
Let set_tab_title rename the tmux session |
Runtime internals you may see while debugging:
PI_DENY_TOOLSPI_SUBAGENT_EXTENSIONSPI_SUBAGENT_NAMEPI_SUBAGENT_AGENTPI_SUBAGENT_PARENT_SESSIONPI_SUBAGENT_SESSIONPI_SUBAGENT_SURFACEPI_SUBAGENT_AUTO_EXIT
Live test knobs:
PI_SUBAGENT_ALLOW_LIVE_WINDOWSPI_SUBAGENT_LIVE_MODELPI_SUBAGENT_KEEP_E2E_TMPPI_SUBAGENT_LIVE_LOCK_PATH
Unit tests:
bunx tsc --noEmit
npm testHerdr-focused fake tests:
node --test test/mux/herdr.test.ts
node --test test/launch/herdr-interactive-launch.test.tsThe mux test covers Herdr detection, forced preferences, adapter error reporting, non-shrinking numbered-tab creation in the parent workspace, split limitations, command send, screen reads, title and workspace labels, and cleanup. The launch test covers Herdr parity for cwd, env, flags, trust-project approval, session settings, model and thinking resolution, tool narrowing, skills, lifecycle policy, and explicit PI_SUBAGENT_MUX=herdr selection.
Live tests:
PI_SUBAGENT_ALLOW_LIVE_WINDOWS=1 npm run test:e2e-live-blocking
PI_SUBAGENT_ALLOW_LIVE_WINDOWS=1 npm run test:e2e-live-mix-blocking
npm run test:e2e-live-deny-tools
npm run test:e2e-live-tools
npm run test:e2e-live-extensions
npm run test:e2e-live-stop-after-turnThe live window tests require an explicit opt-in because they open real terminal windows.
Herdr live smoke tests are guarded and bounded:
npm run test:live-herdr-mux
npm run test:live-herdr-piWithout opt-in variables, each Herdr smoke prints a SKIP line and exits before creating Herdr surfaces. Use the skip output as guard evidence only. It is not a real live smoke run.
Run the real Herdr mux smoke only when you are ready to create temporary Herdr surfaces:
PI_SUBAGENT_ALLOW_LIVE_WINDOWS=1 npm run test:live-herdr-muxRun the real Pi Herdr smoke with both live opt-ins and a model. This script starts an interactive parent Pi session inside a Herdr-managed pane. It does not use pi -p as proof of interactive child behavior.
PI_SUBAGENT_ALLOW_LIVE_WINDOWS=1 \
PI_SUBAGENT_LIVE_MODEL=provider/model[:thinking] \
npm run test:live-herdr-piBoth Herdr smoke scripts check the herdr command, server running status, and protocol compatibility before mutating panes. They label created tabs and panes with a unique marker, then close marked surfaces during cleanup.
Herdr validation record for this release:
node --test test/mux/herdr.test.tspasses the fake Herdr mux contract suite.node --test test/launch/herdr-interactive-launch.test.tspasses the fake Herdr launch parity suite.bunx tsc --noEmitpasses type checking.npm testruns the registered Herdr mux and launch suites through the repository test entrypoint.npm run test:live-herdr-muxandnpm run test:live-herdr-pipass their skip guards whenPI_SUBAGENT_ALLOW_LIVE_WINDOWSandPI_SUBAGENT_LIVE_MODELare unset. A real live run requires the opt-in variables above.
- upstream foundation: HazAT/pi-interactive-subagents
- this fork: edxeth/pi-subagents
MIT