pi-herdr-agents
Asynchronous Pi subagents in Herdr, with optional isolated Git worktrees
Package details
Install pi-herdr-agents from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-herdr-agents- Package
pi-herdr-agents- Version
1.7.0- Published
- Sep 9, 2026
- Downloads
- 3,167/mo · 1,126/wk
- Author
- giuseppecrj
- License
- MIT
- Types
- extension, skill
- Size
- 703.9 KB
- Dependencies
- 0 dependencies · 4 peers
Pi manifest JSON
{
"extensions": [
"./pi-extension/subagents/index.ts"
],
"skills": [
"./skills"
],
"image": "https://raw.githubusercontent.com/giuseppecrj/pi-herdr-agents/main/docs/assets/pi-herdr-agents-gallery.png"
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
Pi Herdr Agents

Asynchronous subagents for Pi, running exclusively in Herdr.
Delegate investigation, implementation, and review without blocking the parent session. Each child runs as a real Pi process in its own Herdr surface; results return automatically when the child finishes.
Features
- Non-blocking delegation —
subagentacknowledges launch immediately while the parent keeps working. - Parallel execution — run independent scouts, workers, and reviewers at the same time.
- Live supervision — track process and turn state in Pi's subagent widget; interrupt one child turn without destroying its session.
- Managed worktrees — isolate writing agents in retained Herdr workspaces with explicit Git ownership and recovery details.
- Conversation handoff — continue the active Pi conversation in a new worktree with
/worktreewhile preserving the parent session. - Orchestrated reviews — fan out fresh public reviewers and synthesize their evidence in the parent.
- Reusable roles — use bundled agents, project or global definitions, and installable role packs.
- Persistent specialists — retain one policy-bound Pi session for sequential, turn-based tasks.
Requirements
Other terminal multiplexers are not supported. Worktrees isolate Git checkouts, not processes or permissions; child agents and installed Pi packages run with your user account's access.
Install
Install from npm:
pi install npm:pi-herdr-agents
Install project-locally or try it for one run:
pi install -l npm:pi-herdr-agents
pi -e npm:pi-herdr-agents
Then start Pi inside Herdr:
herdr
pi
Restart or /reload Pi after installation. Review package source before installing any Pi package.
Quick start
Ask Pi to delegate naturally:
Use two scouts in parallel to map the authentication flow, then summarize their findings.
Or launch a named role directly:
/subagent scout Analyze the authentication module and report relevant files and risks
For an isolated writing task:
/worktree auth-fix Implement the approved authentication fix and run the focused tests
Pi can also call the tool directly:
subagent({ name: "Auth scout", agent: "scout", model: "<provider>/<fast-tier-id>", thinking: "low", task: "Map the authentication flow" });
subagent({ name: "DB scout", agent: "scout", model: "<provider>/<fast-tier-id>", thinking: "low", task: "Map the session schema" });
// Both return immediately; each result comes back independently.
Use ordinary panes for read-only agents. A single or sequential writer can work in the parent checkout; give each parallel independent writing agent a unique managed worktree. The parent acts as coordinator: decompose work, give each child one bounded outcome with its goal, allowed files, verification, and commit instruction, and keep dependent writes sequential. Children are leaves by default; the parent owns integration and final verification. See Worktree subagents.
How it works

A subagent call selects the target checkout, reuses its Herdr workspace, and gives the child a pane in an extension-owned Agents tab. Four panes fit in each tab by default; overflow opens another tab in the same workspace. A worktree is created only when explicitly requested for checkout isolation. The call launches a child Pi session and returns started. The parent watcher combines Herdr process state with child activity details and projects the result into a live widget:
╭─ Subagents ──────────────────── 1 active · 1 open ─╮
│ 00:23 Scout: Auth (scout) active · read 7m │
│ 00:45 Reviewer (reviewer) waiting 2m │
╰────────────────────────────────────────────────────╯
When the child completes, the parent receives one bounded subagent_result message and starts a new turn with that result in context. Disposable ordinary panes close after result delivery; Herdr removes a tab when its last pane closes. Persistent specialists keep their pane between tasks, and managed worktree roots return to retained interactive shells. Callers never need to poll, tail session files, or wait in a shell loop.
Troubleshooting completion delivery
If a child finishes but the parent returns an empty or unrelated response, first verify that the result reached the parent session:
jq -c 'select(.type == "custom_message" and .customType == "subagent_result")' "$PI_SESSION_FILE" | tail -1
If the entry exists, spawning and result extraction worked; investigate parent wake-up and model-facing delivery rather than the child process. Completion wake-ups must contain the bounded result directly—do not send a separate message that merely tells the parent to look at an adjacent custom message.
Git package refs are pinned. To move an installed development copy back to the current main, install that ref explicitly and reload the active Pi session:
pi install git:github.com/giuseppecrj/pi-herdr-agents@main
# Then run /reload inside Pi.
Smoke-test delivery with an autonomous subagent instructed to return one exact marker. Success means the marker itself—not only a generic wake-up notice—automatically appears in the parent turn.
Subagent tabs, panes, and worktree workspaces are created without stealing keyboard focus. Launch commands target child panes by explicit ID, so focus and command delivery are independent. If a fresh or resumed launch fails, the extension closes the ordinary pane that it created and preserves the original launch error. It does not close a caller-supplied surface, and managed worktree workspaces remain retained on failure. Note: the interactive option controls parent status notifications, not terminal focus.
What's Included
Extensions
Subagents — 6 main-session tools + 6 commands, plus 2 child-only tools:
| Tool | Description |
|---|---|
subagent |
Spawn a sub-agent in a dedicated herdr pane (async — returns immediately) |
subagent_interrupt |
Interrupt a running Pi-backed subagent's current turn |
subagent_send |
Deliver a follow-up task to an idle persistent specialist |
subagent_stop |
Gracefully stop a persistent specialist after its active task settles |
subagents_list |
List available agent definitions |
subagent_resume |
Resume a previous Pi-backed sub-agent session in a new ordinary pane (async) |
| Pi child-only tool | Description |
|---|---|
caller_ping |
Ask the parent for help; ordinary children exit, persistent specialists stay alive |
subagent_done |
Mark an interactive child complete and exit; autonomous agents auto-exit |
| Command | Description |
|---|---|
/plan |
Start a full planning workflow |
/iterate |
Fork into a subagent for quick fixes |
/btw <question> |
Open an ephemeral side-question session in a background tab |
/btw-close |
Close the current BTW session |
/worktree <name> [task] |
Continue this session in a new managed worktree (/worktree list lists them) |
/subagent <agent> <task> |
Spawn a named agent directly (/subagent list lists available agents) |
Taxonomy and discovery
This package distinguishes directly runnable agent roles, Pi-native skills, and authenticated Pi runtimes. A multi-stage user outcome may be a command or skill that composes roles; it is not itself an agent role.
The current orchestration inventory is:
| Surface | Entry point | Behavior |
|---|---|---|
| Planning | /plan |
Scout, interactive planner, workers, and reviewer; writes plan artifacts. |
| Iteration | /iterate |
Opens one interactive full-context Pi fork. |
| Side question | /btw, /btw-close |
Opens one replaceable interactive Pi side session. |
| Worktree handoff | /worktree <name> [task], /worktree list |
Forks the active conversation into a managed worktree. |
| Orchestrated review | /skill:orchestrate |
Parent materializes evidence, fans out public reviewers, and synthesizes results. |
| Adversarial review | /skill:orchestrate, adversarial-reviewer |
Risk-based public discovery, cross-family verification, and parent synthesis. |
Bundled visible definitions
| Definition | Classification | Default runtime | Responsibility |
|---|---|---|---|
| planner | Coordinator agent role | Config, then parent | Clarifies requirements, explores approaches, and writes plans with ordered tasks. |
| scout | Leaf agent role | Config, then parent | Maps relevant code, conventions, and verification paths. |
| worker | Leaf agent role | Config, then parent | Implements bounded tasks and verifies the result. |
| reviewer | Leaf agent role | Config, then parent | Reviews changes for correctness, security, and maintainability. |
| visual-tester | Leaf agent role | Config, then parent | Performs visual QA through the chrome-cdp skill. |
| poteto | Coordinator agent role | Config, then parent | Autonomously investigates, edits minimally, delegates independent work, and verifies. |
| adversarial-reviewer | Coordinator role | Exact eligible authenticated Pi models selected by risk and project policy | Runs two routine or three high-risk discovery reviewers, candidate-dependent cross-family verification, and parent synthesis through public asynchronous children. |
All subagents execute through Pi. Claude models remain available through normal
Pi provider/model routing. Legacy role definitions that contain cli fail before
Herdr creates a pane or worktree; remove cli and cli-model, then select an
authenticated Pi provider/model-id.
Optional prerequisites fail closed and are not bundled:
visual-testerneeds an externalchrome-cdpskill that providesscripts/cdp.mjs.- Adversarial review needs a resolved standalone
reviewerrole, confirmed human-only authorship or known author model families, and enough distinct exact authenticated Pi models to satisfy project author-family exclusion and cross-family verification. Routine discovery uses two distinct IDs; concrete high-risk surfaces use three distinct lenses. The parent materializes pinned evidence before public fan-out. A project-approved reduced topology must disclose omitted coverage. /planuses the bundled scout and planner roles and records ordered tasks inplan.md; it does not require a researcher role, todo tool, orwrite-todosskill.
This package does not install optional prerequisites.
Bundled agents use model defaults from config.json when configured; otherwise
they inherit the parent model. Thinking defaults still come from agent
frontmatter or the parent level. This resolution chain remains available as a
fallback, but orchestrators should explicitly set each child's exact
authenticated provider/model-id and supported thinking level. Select the
model tier first: fast for bounded mechanical work and recon, mid for ordinary
implementation or review, and frontier for architecture, security, hard
diagnosis, or adversarial review. Then select thinking within that model's
supported range. Independent reviewers must use a different provider/family
than the model that produced the work; a stronger model in the same family is a
quality escalation, not independent review.
Discovery loads definitions in package → global → project order, so effective
priority remains project (.pi/agents/) > global
($PI_CODING_AGENT_DIR/agents/, defaulting to ~/.pi/agent/agents/) >
package. Package definitions include bundled roles and roles contributed by
installed Pi role packs. Both subagents_list and /subagent list show each
visible definition's source; contributed roles include their package identity,
for example (package:@acme/security-roles). A hidden higher-priority definition
still suppresses a visible lower-priority definition.
Custom roles and installable role packs are the package's main extension points. See Custom Agents for the complete create, package, verify, and launch workflow.
Async Subagent Flow
1. Agent calls subagent() → returns immediately ("started")
2. Sub-agent runs in herdr 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. Active watchers survive parent /reload, /new, /resume, and /fork transitions, so completion is delivered into the replacement session. Quitting Pi still stops parent-side delivery. The live widget above the input tracks every agent still in flight:
╭─ Subagents ──────────────────── 1 active · 2 open ─╮
│ 01:23 Scout: Auth (scout) active · read 7m │
│ 00:45 Reviewer (reviewer) stalled 4m │
│ 00:12 Scout: DB (scout) starting… │
╰─────────────────────────────────────────────────────────╯
Completion messages render with a colored background and are expandable with Ctrl+O. Results larger than 16,000 characters are abbreviated in the parent context while preserving their beginning, conclusion, and session path; the complete result remains in the child session. The extension includes that bounded result and a continuation instruction directly in the single custom subagent_result message that triggers or steers Pi, avoiding empty turns caused by a separate context-free wake-up. The renderer uses the unadorned bounded result from structured details. Completed rows are removed from the widget as soon as their result is delivered or suppressed.
In-progress status updates
The widget projects each sub-agent from a process + turn lifecycle:
- Herdr pane inspection is the coarse authority for whether the child process is present and whether Herdr reports it as idle, working, blocked, or done.
- Child activity snapshots enrich the label with Pi-only detail (tool name, streaming, etc.) when available.
- Session JSONL is still used for transcript, resume, lineage, and result extraction — not for liveness.
Projected labels include:
starting— launched; pane/activity confirmation is still settlingactive— processing work (agent turn, provider request, streaming, or tool execution)blocked— Herdr reports the child as blockedwaiting— turn finished; the process is intentionally open for more input or another stageinterrupted— the current turn was cancelled (Escape /subagent_interrupt); the process stays open and is not treated as active processingstalled— pane inspection is unhealthy long enough that the parent can no longer trust the runrunning— fallback when only coarse process presence is known (e.g. non-Pi backends)finalizing— completion was observed and delivery is in progress; the process elapsed timer freezes here
The widget header counts active vs open:
- active —
active,starting,running, orblocked - open — everything else still tracked (
waiting,interrupted,stalled,finalizing, …)
When activeCount === 0 (every tracked row is open), the border uses an amber accent. Process elapsed time (MM:SS on the left) freezes when the process reaches finalizing/completed/failed. Interrupt does not freeze that process clock; the interrupted state shows its own duration on the right while the process remains open.
A fixed internal watchdog marks a run as stalled when pane inspection fails or the pane disappears without a completion sidecar; 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, or any /iterate fork) 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 activity 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 extension reads config.json from the installed package root—the directory
containing this README and package.json, not pi-extension/subagents/ or
Herdr's config.toml. That file is package-local: npm or git package updates may
overwrite it. Common global package roots are:
- npm:
~/.pi/agent/npm/node_modules/pi-herdr-agents/ - git:
~/.pi/agent/git/<host>/<owner>/pi-herdr-agents/
Project-local installs use the corresponding .pi/npm/ or .pi/git/ root.
From the actual package root, copy the example when you want local overrides:
cp config.json.example config.json
{
"status": {
"enabled": true
},
"models": {
"agents": {}
},
"roles": {
"bundled": true
},
"persistent": {
"maxAgents": 3
},
"supervision": {
"forcePolling": false,
"hangWarningMinutes": 15
},
"panes": {
"mode": "grouped",
"direction": "right",
"maxPerTab": 4
}
}
If config.json is absent, status, role, pane, and persistent-specialist settings fall back to config.json.example.
Model routing does not read the example: no model overrides apply until a real
config.json exists.
The copyable example is model-neutral, so it works without requiring credentials for a specific provider. To configure models, replace the empty section with exact IDs from your authenticated model catalog:
{
"models": {
"default": "your-provider/your-default-model",
"agents": {
"scout": "your-provider/your-fast-model",
"reviewer": "your-provider/your-review-model"
}
}
}
Set persistent.maxAgents to the maximum concurrently retained persistent specialists. It defaults to 3; a persistent spawn at the cap is rejected before Herdr creates a pane or workspace, and no specialist is evicted.
Set roles.bundled to false to exclude this package's bundled role definitions from listing and exact-name launch. It defaults to true. Registered role packs remain available, and global and project definitions keep their existing precedence. A role-pack name collides with a bundled role only while that bundled layer is enabled; when it is disabled, the role pack can supply that name.
Supervision transport
On supported local filesystems, supervision uses file wake-ups plus one shared 4.8-second pane reconciliation. A wake-up only prompts fresh evidence collection; it never establishes a result by itself. If the watcher or shared pane inspection becomes unavailable, supervision quietly returns to the legacy one-second polling cadence. No caller action is required.
Set supervision.forcePolling to true in the package-local config.json to
disable wake-ups and use that legacy cadence deliberately. The setting is read
when the coordinator is created, so run /reload after changing it.
subagents_list reports the active transport mode (wake+batch,
polling(forced), or polling(fallback)) and watcher count.
supervision.hangWarningMinutes defaults to 15; set it to 0 to disable
no-progress advisories. For example, this keeps the default transport and sets
a 30-minute advisory budget:
{
"supervision": {
"forcePolling": false,
"hangWarningMinutes": 30
}
}
While a child projects active or blocked, the parent compares durable session
JSONL and activity-snapshot updates against this budget. An advisory is warning-only, fires once per no-progress episode, and
never interrupts, kills, retries, or restarts a child. It identifies blocked-tool (an outstanding tool call may still complete),
truncated-turn (an observed toolUse stop with no tool call; its cause is unknown), or
generic-no-progress when neither condition is established, then
includes the session path and manual recovery options. Ordinary children can be
interrupted or, after manual termination, resumed or newly spawned. Persistent
ordinary-pane specialists can be interrupted or stopped with subagent_stop and
replaced; they cannot be resumed. Managed-worktree children, including persistent
ones, retain their workspace and continue there only after the previous process
has exited; do not use subagent_resume or start a concurrent writer. Interactive children stay
quiet just as they do for stalled/recovered notices; their widget state still
updates. A later durable update clears the episode and sends the corresponding
recovered notice for non-interactive children.
polling(fallback) means at least one tracked child is using per-child polling;
other children can still use wake+batch.
A Linux manual benchmark on 2026-09-06 used isolated Herdr panes held pending,
20-second windows, and the extension's completion/supervision seams. At 10
children across three rotated rounds, wake+batch averaged 2.20 CLI launches/s
versus 14.20 for forced polling (84.5% fewer); mean evidence-to-resolver
latency was 3.2 ms versus 449.0 ms, and the largest reconciliation probe gap
was 4.82 s. The benchmark measures /proc CPU ticks for the supervisor and
isolated Herdr tree, not parent-model latency; raw samples are written to
/tmp/issue29-bench/ by test/bench/supervision-bench.mjs.
panes.mode defaults to "grouped" when omitted. Ordinary public subagent and subagent_resume launches, including bare forks and /iterate, fill extension-owned Agents, Agents 2, etc. tabs in the target checkout's existing workspace. panes.maxPerTab is a positive safe integer, defaults to 4, and counts all live panes in each owned tab, including user-added panes and retained shells. Overlapping launches in one parent respect this cap. It is independent of persistent.maxAgents.
Checkout matching uses Herdr's canonical worktree.checkout_path and includes descendant directories. Shell working directories do not establish workspace ownership. If no checkout matches (including non-Git directories), placement uses the caller's workspace; overflow never creates a workspace. A reviewer with cwd set to a managed checkout joins that workspace without creating another worktree. Resume placement uses the saved session's cwd.
Explicit panes.mode: "tab" preserves one new tab per ordinary child in the caller's workspace. Explicit "split" preserves splits of the stable parent pane. panes.direction is "right" (default) or "down" and applies to grouped and legacy splits. maxPerTab does not affect these legacy modes. Managed worktrees retain their separate workspaces, while /btw keeps its existing tab behavior.
Ownership is tracked by returned pane/tab/workspace IDs, never labels. Separate parent processes own separate groups; /reload preserves a parent's in-memory ownership, but a full restart does not adopt old tabs. Placement never moves existing panes or renames user tabs. Background launches preserve focus; Herdr may resize sibling panes when splitting or closing. User-added panes are never closed by automatic tab cleanup. An owned tab remains reusable while user panes remain, even after all child panes close.
Run /reload after changing role, model, or pane settings.
models.default sets the model for subagents that do not specify a model.
models.agents sets per-agent defaults, keyed by the agent name passed to
subagent({ agent: ... }). Explicit model tool arguments take precedence,
followed by agent frontmatter, per-agent config, the global default, and finally
the parent model. Model values must be exact authenticated provider/model-id
references. A value can contain an ordered comma-separated fallback list, for
example provider/preferred, provider/fallback. The extension validates every
candidate before launch, then launches later candidates only after the selected
child settles with a provider/agent error. Pi owns any automatic transient
retrying inside that child; the extension does not infer retry counts or
permanence from the error text. A completed child result, including a negative
task result, never switches models. Completion metadata reports the requested
candidate, every attempted candidate, the model actually used, and each raw
model failure in attempt order when fallbacks are tried.
A catalog-listed model and configured authentication do not prove that the
active provider account can use that model. Providers may reject an account /
model combination only when the request is made. The completion preserves each
raw provider reason with its model and suggests checking account access,
spawning a new subagent with a supported model, or choosing an appropriate
configured fallback. subagent_resume does not select a model and should be
used only after the session's stored model is usable. Persistent session sidecars fail closed: v1 does not resume or revive a stopped or crashed specialist; retain its evidence and spawn a new specialist. The completion does not
claim a permanent failure or a retry count that Pi has not exposed. Reliable
structured permanence and retry counts require an upstream Pi/ExtensionAPI
diagnostics seam for final provider errors and retry outcomes.
config.json is gitignored in the source tree so local overrides are not
committed from a checkout. On an installed package root, treat it as disposable
local state that package updates may replace. Run /reload after changing it;
status, model, role, and pane configuration are loaded when the extension starts.
Spawning Subagents
// Explicit fast-tier runtime for bounded reconnaissance
subagent({ name: "Scout", agent: "scout", model: "<provider>/<fast-tier-id>", thinking: "low", task: "Analyze the codebase..." });
// Force a full-context fork for this spawn
subagent({ name: "Iterate", fork: true, model: "<provider>/<mid-tier-id>", thinking: "medium", task: "Fix the bug where..." });
// Explicit frontier-tier runtime for architecture work
subagent({ name: "Planner", agent: "planner", model: "<provider>/<frontier-tier-id>", thinking: "high", task: "Work through the design with me" });
// Explicit mid-tier runtime with a custom working directory
subagent({ name: "Designer", agent: "game-designer", model: "<provider>/<mid-tier-id>", thinking: "medium", cwd: "agents/game-designer", task: "..." });
// Isolated ticket branch in a Herdr-managed Git worktree
subagent({
name: "Ticket 123",
agent: "worker",
model: "<provider>/<mid-tier-id>",
thinking: "medium",
worktree: { branch: "ticket/123", base: "main" },
task: "Implement ticket 123, test it, and commit the result",
});
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
name |
string | required | Short stable child label; coordinated groups use <task>-<role>[-n] (widget and pane title) |
task |
string | required | Task prompt for the sub-agent |
agent |
string | — | Load defaults from agent definition |
fork |
boolean | false |
Force the full-context fork mode for this spawn, overriding any agent session-mode frontmatter |
persistent |
boolean | false |
Keep one specialist session alive for sequential tasks; follow-ups use subagent_send only |
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 | configured or parent | Exact authenticated provider/model-id, or an ordered comma-separated Pi fallback list; fallback lists are unavailable for worktree spawns. Resolution is tool argument → agent frontmatter → per-agent config → global config → parent |
thinking |
string | parent level | Pick the model tier first, then set thinking within that model's range: minimal/low for bounded mechanical work, medium for ordinary implementation or review, high+ for architecture, security, or hard diagnosis. Omitting still inherits the parent level; this is a discouraged fallback for orchestrated children. |
systemPrompt |
string | — | Role/system-prompt text for a bare spawn; named agents keep their definition body |
skills |
string | — | Comma-separated skill names |
tools |
string | — | Comma-separated tool names |
cwd |
string | — | Working directory, or source repository when worktree is set (see Role Folders) |
worktree |
object | — | Isolated Herdr-managed Git worktree; requires branch, with optional base (committed HEAD by default) |
Naming coordinated children
Before launching a new group, choose a short task slug and label each new child
<task>-<role>[-n], such as login-api or login-test2. Roles are plan,
research, ui, api, build, test, review, browser, security,
perf, and merge. Leave existing labels unchanged. After the final launch,
print name | agent kind | role | model | worktree and use each name in
prompts, handoffs, and results.
Isolated worktree runs
Use one worktree per parallel independent writing task; a single or sequential writer can work in the parent checkout, and read-only agents use ordinary panes. cwd selects the source Git repository, branch must be unique, and base is resolved to an exact commit before creation. If base is omitted, the source checkout's committed HEAD is used. Parent-checkout changes that have not been committed are not copied.
A launch with worktree and an effective bundled scout, reviewer, or adversarial-reviewer returns a non-blocking warning. Scouts and reviewers normally need an ordinary pane; the adversarial reviewer is a coordinator that uses an ordinary pane for its child reviewers. To inspect or review an existing worker result, start an ordinary child in that retained worktree path. Project or global role overrides do not receive these bundled-role warnings. A read,bash allowlist is not an enforced read-only boundary because shell commands can mutate files; report-only roles must restrict Bash to safe inspection and avoid artifact-generating verification in the reviewed checkout.
The child starts at the returned worktree root. Tell writing agents to test and commit when you want a commit-based handoff, and tell them not to push, merge, switch branches, or remove the worktree. The parent owns review and integration.
Successful, failed, and help-requesting worktree runs retain their workspace and root shell. A reviewer's disposable pane can close without closing that root, tab, or checkout. Completion includes the worktree path, Herdr workspace, branch, base/head SHAs, commits ahead, changed and untracked files, and clean/dirty/conflicted state. Here, clean means no uncommitted files; the branch may still contain commits. If Git inspection fails, state is reported as unknown rather than guessed.
An ownership manifest is written under the parent session's artifacts/<session-id>/worktree-runs/ directory before Herdr creates resources. V1 does not automatically recover watchers after a full process restart, and subagent_resume does not reattach the managed worktree lifecycle.
The extension does not push, create a PR, merge, cherry-pick, or remove the worktree or branch automatically. For task selection, lifecycle states, review commands, failure recovery, and safe cleanup, read Worktree subagents. The research report records the rationale and deferred roadmap.
Persistent specialists
Set persistent: true on a subagent launch to create one logical specialist with one v1 session generation. Its resolved tools, denied tools, model, thinking level, and optional worktree binding are snapshotted at launch and do not change when work is sent later. subagents_list shows each live specialist's logical ID, generation ID, state, completed-task count, and effective policy.
The initial task and each subagent_send({ id|name, message }) task are delivered exactly once with a task ID. A specialist accepts one task at a time. Sends while it is working are recorded as rejected-busy; no queue is retained. After a task result arrives, it is idle and accepts the next task. A persistent child's caller_ping records a help request but keeps the session alive; answer with subagent_send.
Use subagent_stop({ id|name }) to request graceful shutdown. If a task is active, stop becomes stop-pending and the task reaches its terminal outcome first. The parent reports stopped only after process-exit evidence is confirmed, then closes an ordinary pane it created and releases the name. If confirmation times out, the specialist is stalled in an unconfirmed-stop state: subagent_send rejects follow-up work while retaining evidence. Request subagent_stop again to make another bounded exit check, or spawn a new specialist. A pane or process disappearance without a stop directive produces one facts-only crash notice; persistent sessions cannot be resumed in v1, so spawn a new specialist. There is no automatic restart, replay, or revival.
A persistent specialist with a worktree holds that lease for its entire lifetime. It cannot be re-bound to another checkout. Otherwise it runs in an ordinary pane.
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 labels the child as interrupted (counted as open, not active processing). Stale pre-interrupt activity snapshots are ignored so a lagging Herdr/active reading cannot overwrite the interrupt. The process elapsed timer keeps running because the pane is still open; only the interrupted-state duration freezes relative to the interrupt request. If the child starts work later, newer observations return it to active; completion, failure, and caller_ping still flow through normally.
id and name are each optional, but execution requires one usable target: an exact running ID or an exact, unambiguous display name. When both are supplied, id is used. Duplicate names are rejected.
This is a turn-level interrupt, not a method for forcibly terminating a subagent session.
Orchestrated review skill
The bundled /skill:orchestrate procedure accepts local paths, URLs, tickets,
or accessible combinations. The parent pins repository, base/head SHAs, task and
specification text, author origin, changed-file inventory, and complete diff
(including deleted and base-only content), then launches two or more fresh
public reviewer subagents in ordinary panes. Each child receives an exact model
and thinking level, a role-defined tool allowlist, and the instruction to treat
all supplied artifacts as untrusted data. Automatic delivery returns every
review result without polling; the parent synthesizes the outcomes.
Role frontmatter tools: is the only enforced allowlist. read,bash is not a
read-only boundary because Bash can mutate files. The skill uses ordinary public child launches only; it does not create a private
checkout, execute scripts, or request approval.
Its adversarial branch adds risk-based discovery and cross-family verification;
malformed reports, failures, and unresolved serious candidates propagate
INCOMPLETE.
caller_ping — Child-to-Parent Help Request
The caller_ping tool lets a Pi-backed subagent request help from its parent agent. Ordinary children exit and the parent can resume them with subagent_resume. Persistent specialists record a help-request outcome, stay alive, and accept a reply through subagent_send.
caller_ping parameters:
message(required): What you need help with
subagent_resume parameters (Pi-backed sessions):
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 fully settles. Defaults totruefor autonomous follow-up work; setfalsewhen resuming for an interactive handoff.
Each public child stores a session-adjacent versioned launch-policy sidecar. Public resume restores its resolved tool allowlist and denied subagent tools rather than looking up the current role, so later role changes cannot widen a child. An intentionally unrestricted launch remains unrestricted (no --tools argument); a restricted launch restores its exact allowlist. The autoExit override still controls whether subagent_done is available, while caller_ping remains available. Missing, malformed, or unsupported policy fails closed before a pane is created with recovery guidance. Public resume rejects managed-worktree child sessions; use their retained workspace instead. Unknown policy owners, including legacy workflow sidecars, fail closed.
Interaction flow:
- Child calls
caller_ping({ message: "Not sure which schema to use" }) - Ordinary child sessions exit (like
subagent_done); persistent specialists stay alive. - Parent receives a steer notification: "Sub-agent Worker needs help: Not sure which schema to use"
- The parent resumes an ordinary child with
subagent_resume, or replies to a persistent specialist withsubagent_send. - The child picks up 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 Pi-backed subagent contexts. Calling it from a standalone Pi session returns an error. For a worktree child, the help handoff retains the workspace, butsubagent_resumedoes not reattach worktree tracking; continue the work in the retained workspace.
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 → Autonomous scout maps the codebase
Phase 2: Planning → Interactive planner subagent (user collaborates)
Phase 3: Review Plan → Confirm ordered tasks, adjust if needed
Phase 4: Execute → Shared-checkout sequential workers by default; isolated parallel workers for independent tasks
Phase 5: Integrate → Parent reviews and integrates worktree branches one at a time, only when worktrees are used
Phase 6: Review → Reviewer subagent checks the integrated changes
The parent workspace and tab names stay unchanged. Subagents use the configured placement policy; grouped mode reuses available space in owned Agents tabs.
The /iterate Workflow
For quick, focused work without polluting the main session's context.
/iterate Fix the off-by-one error in the pagination logic
This always forks the current session into a subagent with full conversation context. It does not inherit an agent default session-mode. Make the fix, verify it, and exit to return. The main session gets a summary of what was done.
The /btw Workflow
Use /btw for a quick side question without adding a turn to the main session:
/btw What did we decide about session cleanup?
The extension snapshots the current active conversation branch, opens a non-focused Herdr tab, and starts an interactive Pi session with the same model and thinking level. The answer stays in that tab and is never delivered as a subagent result. A second /btw replaces the previous one; /btw-close closes it explicitly.
BTW shares the current working directory. It treats inherited work as reference context and modifies the workspace only when the side question explicitly requests it. Cleanup is best effort; if closing fails, the tab remains available for manual recovery.
The /worktree Workflow
/worktree <worktree> [task] creates a Herdr-managed worktree from the current committed branch and launches a new interactive Pi session there with the active conversation branch. The original session remains available. Use /worktree list to list worktrees for the current repository. This is a new-process handoff, not an in-place move of the existing shell or Pi process.
Custom Agents
Custom agent roles are the package's primary extension mechanism. Create one when a child needs a reusable, bounded responsibility such as scouting, implementation, or review. If the new concept instead describes a multi-stage user outcome, make it a workflow, command, or Pi skill that composes roles; do not disguise a workflow as an agent definition.
1. Choose the scope
| Scope | Location | Use when |
|---|---|---|
| Project | .pi/agents/<name>.md |
The role belongs to one repository |
| Global | $PI_CODING_AGENT_DIR/agents/<name>.md |
The role should be available everywhere; the default root is ~/.pi/agent |
| Role pack | An installed Pi package's registered roles/ directory |
The role should be independently installable and shareable |
| Bundled | This package's agents/<name>.md |
Contributing a fallback role maintained with pi-herdr-agents |
The filename stem is the launch key. name frontmatter is optional because it
defaults to the filename stem. If supplied, keep it identical so overrides remain
predictable; role packs reject mismatches.
2. Create the definition
---
description: Reviews a bounded change for concrete security vulnerabilities
thinking: high
tools: read, bash
system-prompt: append
session-mode: standalone
spawning: false
auto-exit: true
---
# Security Reviewer
Review only the requested change. Trace trust boundaries and affected callers.
Report concrete findings with file and line references, exploit conditions,
severity, and the smallest safe correction. Do not modify files.
Omit model to use models.agents.<name>, then models.default, then the
parent model. Put model in frontmatter only when the role itself needs a
specific exact authenticated provider/model-id.
tools is passed to Pi's --tools allowlist and may name any registered
built-in, extension, or custom tool. Listing a tool does not install its
extension. Use one non-empty inline comma-separated scalar, such as
tools: read, grep; do not use YAML lists, containers, quotes, or comments.
Omitting tools intentionally leaves the role unrestricted. Likewise, skills
names must already be discoverable by Pi; this package does not install role
prerequisites.
3. Verify and launch
/subagent list
/subagent security-reviewer Review the authentication changes against main
Or call the tool directly:
subagent({
name: "Security review",
agent: "security-reviewer",
task: "Review the authentication changes against main.",
});
Agent files are read when definitions are listed or launched, so creating or
editing one normally does not require /reload. Installing, removing, updating,
or changing the extension code of a role pack uses Pi's normal /reload flow.
Publish a role pack
A role pack is an ordinary Pi package with Markdown definitions and a tiny extension that registers their directory through Pi's public inter-extension event bus:
security-roles/
├── package.json
├── extension.ts
└── roles/
└── security-reviewer.md
{
"name": "@acme/security-roles",
"version": "1.0.0",
"keywords": ["pi-package"],
"type": "module",
"pi": {
"extensions": ["./extension.ts"]
},
"peerDependencies": {
"@earendil-works/pi-coding-agent": "*"
}
}
import { fileURLToPath } from "node:url";
const roles = fileURLToPath(new URL("./roles", import.meta.url));
export default (pi: any) => {
const unsubscribe = pi.events.on(
"pi-herdr-subagents:roles:discover:v1", // stable protocol identifier
(request: { apiVersion: number; register(path: string): void }) => {
if (request.apiVersion === 1) request.register(roles);
},
);
pi.on("session_shutdown", unsubscribe);
};
Install both packages through Pi; the role pack remains inert if
pi-herdr-agents is absent:
pi install npm:pi-herdr-agents
pi install npm:@acme/security-roles
Registration is synchronous and accepts one absolute Markdown file or a
directory whose direct .md children are roles. The bridge must unsubscribe on
session_shutdown as shown so removed or updated packages do not survive a
reload. A copyable package lives in examples/role-pack/.
The host reads and validates
the files, derives package name/version from the nearest package.json, and
reports invalid paths, missing descriptions, filename/name mismatches, and
package-layer collisions in the listing surfaces.
Role packs cannot replace an enabled bundled role, and duplicate role names from multiple role packs are disabled rather than resolved by extension load order. Use a global or project definition for an intentional override.
See ADR-0003 for the registration seam, collision rules, and rejected alternatives.
Authoring checklist
- The role has one bounded responsibility and a clear report or handoff contract.
- The filename stem is the role name; if
nameis present, it matches the stem. descriptionstates the role's input/output responsibility.toolsandskillscontain only installed, necessary capabilities.- Leaf roles set
spawning: false. - Autonomous roles set
auto-exit: true; interactive roles leave it off. - Generic roles omit
modelunless a particular runtime is functionally required. /subagent listshows the expected source and a smoke launch succeeds.
Capability declarations are strict: use the unquoted, unindented keys
tools:, deny-tools:, and spawning: exactly once when present. Declare
tools and deny-tools as non-empty inline comma-separated scalars, and
spawning as exactly true or false. YAML lists, containers, multiline
values, quotes, comments, empty values, duplicates, noncanonical key spelling,
and invalid booleans are rejected. A role with an invalid capability declaration
is excluded from discovery, and an exact-name launch reports the diagnostic
before creating a Herdr pane or worktree. Other unsupported or unknown
frontmatter may still be ignored.
Compare definitions against the reference below and verify them with
/subagent list plus a smoke launch.
Frontmatter Reference
| Field | Type | Description |
|---|---|---|
name |
string | Optional explicit agent name used in agent: "my-agent"; defaults to the filename stem and must match it in role packs |
description |
string | Shown in subagents_list output |
model |
string | Optional exact authenticated Pi model default or ordered comma-separated fallback list; omit to use per-agent config, global config, then the parent |
thinking |
string | Optional Pi thinking default (off through max); omit to inherit the parent |
system-prompt |
string | append passes the agent body through Pi's appended system prompt; replace replaces Pi's default system prompt. Without this field, the body is included in the task wrapper |
tools |
string | One non-empty inline comma-separated Pi --tools allowlist under the exact unquoted key tools:; may contain any registered built-in, extension, or custom tool name. Omit to leave unrestricted. YAML lists, containers, multiline values, quotes, comments, noncanonical keys, and duplicates are rejected. |
skills |
string | Comma-separated installed skill names to auto-load. Use this plural form for new definitions; legacy project/global definitions using singular skill remain compatible. |
session-mode |
string | Default child-session mode: standalone, lineage-only, or fork |
spawning |
boolean | Set exactly false to deny all subagent-spawning tools under the exact unquoted key spawning:. Only one true or false declaration is accepted. |
deny-tools |
string | One non-empty inline comma-separated pi-herdr-agents tool list to suppress under the exact unquoted key deny-tools:; this is not a universal cross-extension deny list. YAML lists, containers, multiline values, quotes, comments, noncanonical keys, and duplicates are rejected. |
auto-exit |
boolean | Auto-shutdown after Pi fully settles when the latest assistant turn does not end with stopReason: "aborted" — no subagent_done call needed. User input does not permanently disable auto-exit. Recommended for autonomous agents (scout, worker); not for interactive ones (planner). Also determines the default value of interactive (see below). |
interactive |
boolean | Override whether stall/recovery transitions wake the parent session. Defaults to the inverse of auto-exit: autonomous agents (auto-exit: true) are non-interactive and get stall pings; agents without auto-exit are interactive and stay quiet. Explicit values take precedence. |
persistent |
boolean | Keep this role's specialist session open between tasks. Follow-up work uses subagent_send; persistent specialists cannot be resumed in v1. |
cwd |
string | Default working directory. Absolute paths are unambiguous; relative agent-frontmatter paths resolve from Pi's agent config directory (PI_CODING_AGENT_DIR or ~/.pi/agent), not the project root |
disable-model-invocation |
boolean | Hide a role from discovery surfaces like subagents_list. The definition remains directly invocable by exact 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 subagent 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 callerfork— linked child session seeded with the caller's prior conversation context
lineage-only is useful when you want session discovery and fork lineage UX to show the relationship later, but you do not want the child to inherit the parent's turns.
fork: true on the tool call always forces the fork mode for that specific spawn. /iterate uses this explicit override on purpose.
---
name: planner
session-mode: lineage-only
---
auto-exit
When set to true, the agent session shuts down on Pi's agent_settled event unless the latest assistant message has stopReason: "aborted" — no explicit subagent_done call is needed.
Behavior:
- Low-level
agent_endevents do not close the session because Pi may still retry, compact and retry, or process a queued continuation. - After
agent_settled, a normal or error stop exits, while an aborted stop stays open. - User input does not permanently disable auto-exit; the latest settled assistant stop reason determines whether the session exits.
- 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, iterate) 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; named agents without auto-exit are interactive and stay quiet. Bare spawns have no agent definition and default to autonomous auto-exit behavior. /iterate is interactive because it explicitly passes interactive: true.
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
Without a restrictive tools allowlist or spawning policy, a sub-agent can spawn further sub-agents. Control this with frontmatter:
spawning: false
Denies all subagent lifecycle tools (subagent, subagent_interrupt, subagent_send, subagent_stop, subagents_list, subagent_resume):
---
name: worker
spawning: false
---
deny-tools
Fine-grained control over tools registered by pi-herdr-agents:
---
name: focused-agent
deny-tools: subagent
---
Recommended Configuration
| Agent | spawning |
Rationale |
|---|---|---|
| planner | (default) | Can spawn scouts for investigation. |
| poteto | true |
Delegates independent work. |
| adversarial-reviewer | true |
Compatibility coordinator; launches bounded discovery, conditional verification, and synthesis children. It sets auto-exit: false so automatic child-result steers can drive every wave, then calls subagent_done. |
| worker | false |
Implements bounded tasks. |
| reviewer | false |
Reviews without delegation. |
| scout | false |
Gathers context without delegation. |
| visual-tester | false |
Performs visual QA without delegation. |
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. Use an absolute path for a project directory; relative frontmatter paths are resolved from Pi's agent config directory:
---
name: game-designer
cwd: /absolute/path/to/project/agents/game-designer
spawning: false
---
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, ...
denied: subagent, subagents_list, ...
Development
Run local checks:
npm ci
npm test
npm run lint
npm pack --dry-run
Run the required end-to-end suite from inside Herdr:
npm run test:integration
The deterministic suite launches real Pi sessions, Herdr panes, and worktrees without provider credentials. The optional live-provider smoke test is not a merge gate:
PI_TEST_MODEL="openai-codex/gpt-5.6-luna" PI_TEST_TIMEOUT=180000 \
npm run test:integration:live
See RELEASING.md for versioning, trusted publication, and release verification.
Acknowledgements
This package builds on earlier open-source work by HazAT/pi-interactive-subagents and 0xRichardH/pi-herdr-subagents. 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. Copyright notice retained from the upstream lineage (HazAT).