pi-minimal-dispatch
Minimal detached lane dispatch with visible state for the pi coding agent — no personas, no queue, no server; briefs in, artifacts out; zero runtime dependencies
Package details
Install pi-minimal-dispatch from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-minimal-dispatch- Package
pi-minimal-dispatch- Version
0.4.1- Published
- Nov 4, 2026
- Downloads
- 1,442/mo · 479/wk
- Author
- zhtmike
- License
- MIT
- Types
- extension
- Size
- 125.6 KB
- Dependencies
- 0 dependencies · 1 peer
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-minimal-dispatch
Minimal detached lane dispatch with visible state for the pi coding agent — two tools, one manifest: no personas, no queue, no server, zero runtime dependencies.
Lanes are detached pi --mode json processes in their own process groups:
they survive the dispatcher's death, and their live state is derived from
their event streams — never guessed from empty files.
wave 'survey':
docs [glm-4.6 think:high] · running · 2 turns · tok 5703in/90out, $0.0009 · stop:toolUse · last 3.1s ago
review [glm-5.3-flash] · settled · 4 turns · tok 12021in/310out, $0.0041 · stop:stop · last 12s ago
Why
pi ships no sub-agents by design — its README: "install a package that does it your way." Most packages that answer grow an agent cast: persona rosters and per-role system-prompt markdown, concurrency pools and spawn budgets, fleet UIs, hub servers or daemons that must stay alive. pi-minimal-dispatch takes the other end of the trade:
- No personas, no role play — a lane is a brief file plus optional
model/thinkingpins; the dispatching agent writes each brief, so it keeps the judgment. - The filesystem is the whole protocol — briefs in, JSONL events, one manifest, artifacts out; no server, no in-memory state, re-adoptable by any later session.
- The caller decides scale — no queue, no pool, no cap; external limits are the caller's discipline.
- Zero runtime dependencies — Node builtins and a small host-API surface; CI typechecks the pi 1.4.0 floor and latest pi.
The full design rationale, positioning, and origin story: docs/design.md.
Requirements
- pi ≥ 1.4.0, macOS or Linux (POSIX process groups,
ps -o lstart=,timeout— Windows is not supported) - GNU
timeout: preinstalled on Linux; on macOSbrew install coreutilsor settimeoutBin: "gtimeout"(Configuration)
Install
pi install npm:pi-minimal-dispatch # official (registry)
pi install git:github.com/zhtmike/pi-minimal-dispatch # tracks main
pi -e ./src/index.ts # try it for one invocation
Quick start
Write one brief file per lane — the lane's whole prompt: goal, scope, expected deliverable. Then ask in plain language:
Dispatch two lanes: docs summarizes the request-handling module, review audits src/auth.
Wait for them to settle and collect the artifacts.
A real session:
> dispatch wave="survey" lanes=[{name:"docs", briefPath:".pi/work/survey-docs-brief.md",
model:"zai/glm-5.3-flash", mcp:false},
{name:"review", briefPath:".pi/work/survey-review-brief.md", mcp:false}]
wave 'survey' dispatched — 2/2 lanes spawned
docs: pid 99015 (deadline 900s)
review: pid 99016 (deadline 900s)
manifest: /abs/path/to/.pi/work/survey-manifest.json
[next: lanes(wave='survey', action='wait') — blocks until every lane settles or fails; waitSec caps the hold; action='status' to peek]
> lanes wave="survey" action="wait"
docs [glm-5.3-flash] · settled · 2 turns · tok 5703in/90out, $0.0009 · stop:stop · last 0.8s ago
review [glm-4.6] · settled · 4 turns · tok 12021in/310out, $0.0041 · stop:stop · last 2.1s ago
> lanes wave="survey" action="collect"
collect wave 'survey':
docs: /abs/path/to/.pi/work/survey-docs.md stop:stop
review: /abs/path/to/.pi/work/survey-review.md stop:stop
The ceremony: write briefs → dispatch → wait (or poll status) →
collect → release. dispatch never blocks; waiting is an explicit,
bounded wait call that returns the status table the moment every lane
is settled/failed — no sleep-then-poll guessing. In the TUI, a
collect result expands (ctrl+o / click) to render each lane's artifact
in place. release deletes exactly the wave's own files and refuses
unsettled, foreign-owned, and unconsumed waves — collect first, and a
human may always delete by hand (details).
Steering is kill + redispatch: refine the brief, run a new wave
(slugs are single-use). Hang detection: lastEventAgeSec is the
signal — no event growth for ~2× the lane's normal turn latency ⇒
kill and redispatch.
API
Two tools — full parameter, output, and configuration reference in docs/api.md:
dispatch— validate briefs, claim the wave slug, spawn every lane detached in its own process group; returns in under a second.lanes— statelessstatus/wait/kill/collect/releaseover the wave's manifest; any session can re-adopt a wave, including after the dispatcher exited.
Web/MCP reach: the default lane is offline — MCP servers connecting
grants nothing by themselves; a lane reaches their tools only through
codemode or tool_search named in its tools. Rules and examples:
MCP and web reach.
Configuration
The lane protocol is fixed; every variable knob — deadlines, kill grace, poll interval, working directory, binaries — is configurable via user or project JSON, merged defaults → user → project → per-call parameters. Invalid files fail loudly. Full table and precedence rules: docs/api.md.
Status bar & /lanes
While any tracked lane runs — or has failed and the wave is not yet released — the footer shows one line:
lanes: 2 running · 1 failed (deadline) · 1 settled
lanes [survey/docs]: running (turn 3 · bash · last 12s) ← one lane running
A lane gone quiet badges (1 idle 75s); a lane past its deadline badges
(1 overdue) — signals, not verdicts. /lanes inspects waves without
spending model tokens: bare /lanes lists every wave (with exactly one
readable wave it shows that wave's table directly), /lanes <wave>
prints the full table, /lanes <wave> <lane> shows one lane in detail —
its trace, its artifact, its failure. Failed lanes link their transcript
and .err where the terminal supports it. Full reference:
docs/api.md.
State model
Derived on every call from the event stream, PID liveness, and the
deadline — never stored, never stale. settled means the agent_settled
event was seen — success is collect with suspect: false.
| State | Meaning |
|---|---|
starting |
spawned, no session header yet |
running |
events flowing (overdue: true once past deadline) |
settled |
agent_settled emitted (any stop reason — success is collect with suspect: false) |
failed |
cause: spawn (nothing ran) · killed (tombstoned) · deadline (leash fired) · crashed (.err tail attached) |
How lanes are spawned, what the manifest records, and why state is never stored: Mechanics in docs/design.md.
Testing
npm test runs the unit suites (config, derive, format, lane, release).
Contributing · License
CONTRIBUTING.md · agent instructions in AGENTS.md · MIT