Date: 2026-07-06.
A new pi extension that provides interactive subagent orchestration designed natively around
herdr (https://github.com/ogulcancelik/herdr — "agent multiplexer that lives in your terminal").
It works similarly to pi-interactive-subagents (HazAT) from the orchestrating agent's point of
view — spawn/resume/interrupt subagents in visible terminal surfaces, get woken by steer messages
on completion — but supports only herdr, no tmux/cmux/zellij/wezterm.
pi-interactive-subagents is built on the least-common-denominator of four terminal muxes:
split a pane → wait a fixed delay → type keystrokes into an interactive shell → scrape the
screen for a __SUBAGENT_DONE_N__ sentinel. Every serious reliability problem we've debugged is a
direct consequence of that LCD:
- The launch race (documented in internal race-investigation notes): typed launch commands are flushed by direnv/devenv
shell init (reproduced 100% in a large direnv+devenv work checkout, swallow threshold ~0.5–1s even warm). We
fixed this with guarded launch scripts + a verify/retry loop
(a local pi-interactive-subagents branch
fix/launch-verify-retry) — a closed feedback loop bolted onto an inherently open-loop mechanism. - Dishonest lifecycle state: a never-started child used to register "running" and emit "stalled 1m" steer messages forever (fixed on that branch with a launch-failed state).
- Screen-scrape completion detection:
pollForExitreads pane text looking for a sentinel; plus an.exitsidecar file fast-path. - Upstream PR #57 (HazAT/pi-interactive-subagents) adds herdr as a fifth LCD backend — it types
commands via
herdr pane runand reads screens, ignoring herdr's actual primitives. Wrong level.
herdr's socket API provides exactly the primitives the LCD lacks (verified hands-on 2026-07-06 with herdr 0.7.1 driven headless):
herdr agent start <name> --cwd .. --env K=V --no-focus -- <argv...>— launches the agent as a direct argv process. No shell, no typing, no launch race by construction. Returns pane id + agent record in ~12ms. Verified: launched pi in a devenv-poisoned cwd successfully.- Semantic agent state — herdr detects agents (there is a
pi.tomldetection manifest in-tree:workingwhen screen showsWorking..., idle fallback) and rolls up idle/working/blocked/done per pane/tab/workspace in its sidebar. herdr wait agent-status <pane> --status done|idle|blockedandherdr wait output --match— one-shot blocking waits. Verified:--status workingresolved 0.2s after submitting a prompt;--status idleunblocked when the pi turn finished (~9s).events.subscribe(raw socket, newline-delimited JSON) — push events:pane.exited,pane.closed,pane.agent_status_changed,pane.created, worktree/workspace/tab lifecycle.pane.report_agent/pane.report_metadata— a child process can self-report semantic state and display metadata (deep-integration path: pi extension inside the child reports working/blocked/done instead of relying on screen detection).notification.show, git worktree management, named sessions (--session <name>, per-session socket), remote via ssh (herdr --remote).
Design consequence: the child-exit sidecar (.exit file) plus herdr events can fully replace
sentinel screen-scraping; argv launch fully replaces the delay/type/verify/retry machinery; and
pane.exited gives truthful, immediate launch/crash failure (e.g. a bad --model typo in an
agent def previously produced a silent zombie — with herdr the pane exit is an observable event).
- herdr-only. If pi is not running inside a herdr pane (
HERDR_ENV=1/HERDR_PANE_IDenv) or the socket is unreachable, tools report a clear setup hint. No other mux code paths. - Agent-definition compatibility. MUST read the same
~/.pi/agent/agents/*.mdfiles with the same frontmatter semantics as pi-interactive-subagents (name, description, tools, deny-tools, model, thinking, spawning, auto-exit, interactive, cli, session-mode, disable-model-invocation, systemPromptMode — audit the reference impl for the full set). Both extensions will run side-by-side during a transition period and the defs must work with both. Read-only consumption: do not write/migrate these files. - Orchestrator UX parity for the core loop:
subagenttool: spawn named subagent with task, optional agent def, cwd, model, tools, systemPrompt, fork/session-mode, interactive flag. Fire-and-forget; completion/failure delivered as steer messages that wake the orchestrator.subagent_resume,subagent_interrupt,subagents_listequivalents.- Artifact-backed task handoff (task/context files under session artifacts dir) — reuse the
convention
<sessionDir>/artifacts/<session-id>/...so debugging habits transfer. subagent_donemechanism in the child (the-e subagent-done.tsextension +.exitsidecar handshake) — keep; it is transport-independent and battle-tested.
- Truthful lifecycle. Launch failure (process exits before session starts), crash
(
pane.exitedwith nonzero), completion, and interactive-user-exit must each produce a distinct, honest result steer. Never an eternal "stalled" state for a dead child. - Env correctness for direnv/devenv repos. argv launch skips interactive shell init. The
pion the dev machine is a wrapper that needs varlock inside certain work checkouts — varlock only exists inside the devenv env. Verified fix: launch argv asdirenv exec <cwd> pi ...when the target cwd has an.envrc(detect; make behavior configurable). Without this, spawns in such checkouts die instantly (verified). - TDD, red/green. Write failing tests first, then implement. Unit tests (mock the herdr CLI/socket boundary) + integration tests against a real herdr named session. Follow the node:test style of the reference repo if convenient.
- Isolation during development — CRITICAL. Running pi orchestration agents on this machine
load the live pi-interactive-subagents checkout via
~/.pi/agent/settings.jsonpackages. You MUST NOT:- modify
~/.pi/agent/settings.jsonor anything under~/.pi/(except reading agent defs), - modify the live pi-interactive-subagents checkout (the live extension source),
- touch tmux sessions/panes you did not create, or any herdr session you did not create.
Test recipe (verified): create your own tmux session; run
herdr --session herdr-testin a pane there (client needs a TTY; this starts an isolated server+socket); drive it headless viaHERDR_SESSION=herdr-test herdr <cmd>. Load the extension under test explicitly withpi -e <this-repo>/<entry>.ts(never via installed packages). Note:~/.local/binmay not be on tool-shell PATH; use the absolute herdr path.
- modify
- What to reproduce vs discard from pi-interactive-subagents. Study
its
pi-extension/subagents/(index.ts ~2100 lines, cmux.ts, status.ts, activity.ts, session.ts, subagent-done.ts, plugin/). Candidates to discard: all multi-mux surface code (cmux.ts), shell-ready delay, launch verify/retry loop (obsolete under argv launch), sentinel screen polling, possibly the Claude Code CLI path (decide; herdr detects claude natively so keeping it may be cheap), the local status-widget machinery if herdr's sidebar + steer messages suffice (or keep a slim widget — decide with rationale). Candidates to keep: agent-def parsing, artifact/task-file handoff, session seeding/fork modes, deny-tools env,.exitsidecar + subagent-done child extension, steer message formats. - Depend on
pi-herdr(ogulcancelik/pi-extensions/packages/pi-herdr) or standalone. pi-herdr is a user-facing generic pane tool (~1000-line single file, reviewed in full); it is not a library. Likely answer: standalone with a small internal herdr client module (CLI-exec based like pi-herdr, or raw socket ndJSON — evaluate; events.subscribe needs a persistent socket connection which the CLI does not provide, so a small socket client may be warranted for the event watcher). License is MIT; copying patterns with attribution is fine. - Event-driven vs poll-driven completion watcher. Prefer events.subscribe
(pane.exited/agent_status_changed) +
.exitsidecar; a low-frequency poll fallback is acceptable for robustness. Must be abortable on /reload (see da8ab6b pattern in reference). - Child state self-reporting (
pane.report_agentfrom a pi extension in the child) — nice to have, likely a later phase; the shippedpi.tomlscreen detection is thin (literalWorking...) but functional today. - Naming of tools (
subagentvsherdr_*): keep familiar names BUT both extensions may be loaded side-by-side during the transition period — tool name collisions must be handled (e.g. registration guard, distinct names, or documented mutual exclusion; decide).
- pi-interactive-subagents — reference implementation. Branch
fix/launch-verify-retry(v3.7.1 + our race fix, 3 commits). READ-ONLY. Also read its README and tests (test/test.ts,test/integration/). - herdr source clone. Docs:
docs/next/website/src/content/docs/*.mdx(esp.socket-api.mdx,cli-reference.mdx,agents.mdx,persistence-remote.mdx),SKILL.md(agent-facing usage),website/agent-detection/pi.toml. - ogulcancelik/pi-extensions
packages/pi-herdr/index.ts— same-author pi extension, good herdr-CLI patterns (error envelope parsing, alias bookkeeping, wait loops, renderers). - internal race-investigation notes — the race investigation that motivates this design.
- Pi extension API docs:
@earendil-works/pi-coding-agent/docs/(extensions.md,skills.md,packages.md) andexamples/extensions/. - herdr v0.7.1. PR #57:
gh pr view 57 --repo HazAT/pi-interactive-subagents.
- From a pi orchestrator running inside herdr:
subagentspawn → herdr pane appears running the child pi directly (argv), task delivered via artifact file, orchestrator gets a completion steer with the child's summary. Works in a devenv checkout cwd with zero launch-race mitigation code. - A child that crashes at startup (e.g. invalid
--model) produces a failure steer within seconds naming the exit code, pane, and launch script/argv — no zombie. - 3 concurrent spawns work without cross-talk.
subagent_resumeandsubagent_interruptwork; interactive (non-auto-exit) subagents wait for the user and still deliversubagent_doneresults.- Agent defs in
~/.pi/agent/agents/*.mddrive model/tools/system-prompt identically to pi-interactive-subagents (spot-check: worker, planner, reviewer). npm testgreen; integration suite runs against an isolated named herdr session and cleans up after itself. Nothing in the developer's global pi config or live sessions is touched.- README documents setup (start pi inside herdr), configuration, and differences vs pi-interactive-subagents.
Weekend-project scale, but done properly (TDD, serial implementer todos). Phase later-phase items (child self-reporting, Claude CLI path if deferred, notifications polish) as explicit follow-up todos rather than cramming.