pi-mesh-agents
v0.5.3
Published
A Pi extension that turns the **current pi session into an orchestrator** for a persistent team of subagents. The orchestrator LLM spawns a *mesh* — a long-lived team of **real pi processes** working in the shared codebase — manages it over time (join, re
Readme
🕸️ pi-mesh-agents
A Pi extension that turns the current pi session into an orchestrator for a persistent team of subagents. The orchestrator LLM spawns a mesh — a long-lived team of real pi processes working in the shared codebase — manages it over time (join, redirect, kill, close), and is woken by one-line digest pushes when agents finish tasks or need decisions. Children keep the full bridge (inbox/outbox JSONL files, bus provenance, per-agent session files, digests, accounting) and gain process isolation and durable post-mortem records; every bridge crossing is appended to a tamper-evident, hash-chained JSONL log. On top of that, four visibility modes per mesh control what gets looked at — not what runs.
Deliberately lean: no ports, no network listeners, no MCP, no compile step — one codebase, eight tools, one log per mesh.
⚙️ How it works
- The orchestrator is your session. The extension registers eight tools. Its LLM decides team composition at any moment — spawn a mesh, add agents later, reuse standing ones, kill stuck ones, close when done, re-join after a process death.
mesh_spawnis non-blocking: it returns the roster, not the results. - Children are real pi processes. Every child is its own headless
pi --mode rpccoprocess session, hosted by the plugin (one real pi process per agent — the in-processcreateAgentSessionpath is gone, hosting is built in, not optional). Thetuidisplay mode adds a per-agent viewer window that renders the child's stream with pi's own transcript components (P04). Children are isolated processes — a child crash is oneerrorenvelope, not a dead orchestrator. - The inbox/outbox bridge. The orchestrator talks to children over
inbox/<agentId>.jsonl(TUI mode — headless delivery rides the coprocess's stdin) and hears them throughoutbox/<agentId>.jsonl(all modes; the plugin's per-file outbox tailer is the only intake). The orchestrator is the bus's single writer: child-originated envelopes (msg/response/status/claim/done) land on the chain at outbox-tail time — ms-scale lag vs. tool-call time, same envelope shapes. - Done means standing by. A child's
doneis not terminal: the agent keeps its full context and accepts new instructions viamesh_instruct(reuse), until it is killed or the mesh closes. An optional per-agent task budget (maxTasks, 1–32) auto-releases context-heavy agents at the budget so a fresh replacement takes over. - Digests wake the orchestrator. On task-done, blocked/waiting, kill, task-limit release, error, and close, a one-line digest is pushed into the orchestrator's session and triggers a turn if it is idle. The orchestrator is event-driven, not polling. Peer chatter never enters the orchestrator's context — only lifecycle digests do.
- Provenance. Every event crossing a bridge (orchestrator→child, child→child, done, kill, close) is one line in
.mesh/agents/<meshId>/bus.jsonl, linked by a last-event hash chain (canonical sorted-key sha256, genesis-bound).result.mdis written at close. - Durable post-mortem, always. Each child has its own pi session file (
sessions/<agentId>.jsonl,pi --resume-openable) in all modes — the canonical post-mortem record; headless modes additionally leave a replayable token-levelstream/<agentId>.jsonl. See Visibility modes. - Persistence across restarts. Every mesh close writes
state.json(roster, usage, last summaries, bus head, mode, session paths). A mesh killed by process death (outcomeshutdown) can be re-joined in a later session withmesh_rejoin— a fresh mesh whose brief carries the previous brief + every agent's last summary + pointers to the old evidence (bus, result, session files). - Resource accounting. Every agent's lifetime tokens/turns/context-percent flow from the child's outbox
statuslines (cumulative, perturn_end) and are shown inmesh_status, done digests, the close result, andresult.md— objective replace/kill triggers ("agent at 70% of window, replace soon", "6 min without bus activity, kill it"). - Opt-in claims. Setting
claims: trueenables the file-claim protocol (claimchild tool +claimbus envelopes); conflict adjudication is asynchronous — conflicts arrive at the claiming child asCONFLICTmessages (record-and-advise, no OS locks). - Children are isolated by design. They do not inherit the orchestrator's conversation; the shared
brief.mdis their only shared mesh context. Children load the operator's global skills and the workspaceAGENTS.mdlike any pi session (resource loading is not extension loading —pi -nestrips extensions only) and have no MCP tools (-nestripsbuiltin:mcp, matching the v1 posture). The bus log is the only durable evidentiary mesh artifact —stream/andoutbox/are operational, not evidentiary. - Clean shutdown. Exiting pi mid-mesh runs the
session_shutdownhook, which finalizes every open mesh (leave+closeenvelopes, outcomeshutdown) so every chain terminates cleanly. Close policy:mesh_close/ shutdown does NOT touch the dash panes — the mesh's panes persist in the shared dash (live tail while thetuichildren keep running, frozen tail once a child dies — the post-mortem room; pane removal is operator-driven only, and the operator-owned dash server is never killed by the plugin); headless children are released (SIGTERM → cleansession_shutdown→leaveline, suppressed by the gone-set invariant). With the P04 per-agent window fallback, the viewer windows are not closed — they keep rendering the final stream.
👁️ Display modes
Two modes, one per mesh, fixed at mesh_spawn by the mode setting (default tui). Every child is the same thing in both modes: a headless pi --mode rpc coprocess (one real pi session per agent, byte-faithful stream/<agentId>.jsonl tee, sessions/<agentId>.jsonl record). The mode controls what is displayed — never what runs:
| mode | child | live view | post-mortem |
|---|---|---|---|
| blind | headless --mode rpc coprocess | none (lowest CPU) | sessions/ + stream/ |
| tui | the same headless coprocess | a pane in the machine-global agent dash — the child's stream rendered by pi's own transcript components (thinking, tool executions + partial output, response text, footer) — or a per-agent viewer window as fallback (below) | sessions/ + stream/; dash panes are removed at mesh_close, the stream files remain replayable |
The viewer (scripts/meshview-agent.mjs) is display-only: it imports pi's TUI runtime + the exported transcript components from the same pi install that hosts the agents (no second agent loop, no stdin, no session machinery) and tails the child's stream/<agentId>.jsonl. A blind run is replayable afterwards even though it was never visible live:
node scripts/meshview-agent.mjs --mesh <meshId> --agent <agentId> --onceThe agent dash (default tui display)
One dash per machine, shared by every mesh on the machine: a tmux server (socket mesh-dash, session dash, one grid window) hosted in a brand-new iTerm2 window created by osascript before tmux runs (the window attaches to the server; the server outlives the window — close it and the next join reopens/reattaches). Each tui agent joins the grid at join time: one pane per agent, row-major filling with gridMax columns (default 4 — a new row starts when a row fills, and the first cell of a new row takes the full row width). The layout is applied as an exact tmux custom-layout string (deterministic + idempotent; ±1 px verified); a pane's title carries MESH-<meshId>-<agentId>.
- Pop out / back in (
mesh_dashtool, actionspop/pop-in, target<mesh>/<agent>): the agent's pane leaves the grid into its own full window (pop-<mesh>-<agent>) of the dash session and re-joins the grid tail on pop-in (slots are never reused). The grid reflows around the gap; the viewer and the child are untouched. - Grid shape (
mesh_dashactiongrid, 1–16 columns): re-lays the current panes; persisted in the dash state. mesh_dashactionstatus: the live slot table (slot, mesh/agent, mode, pane, pane pid);repair: reconcile state against the live panes (normally runs itself — a server-globalpane-exitedhook fires the CLIrepairwhenever a viewer pane dies).- CLI (outside pi):
node scripts/mesh-dash.mjs [<socket>] <status|pop|pop-in|grid|repair|attach>— socket-first positional form. - Inside the dash it is plain tmux:
C-b qpane indices,C-b zzoom a pane to the full window,C-b {/}move a pane,C-b xkill a pane (⚠ kills exactly that agent's viewer — the child keeps working, and the hook prunes + re-lays the grid within seconds). - Dash state:
~/.mesh-dash/state.json(socket → per-agentslot/mode/paneIdmap, gridMax) +dash.log(op log); lock-protected, stale-steal after 5 s. - Environment (all optional):
MESH_DASH_SOCKET(defaultmesh-dash),MESH_DASH_STATE_DIR(default~/.mesh-dash/~/.mesh-dash-<socket>),MESH_DASH_GRID_MAX(default 4),MESH_DASH_DRY_RUN=1(skip the osascript window ops — logged as "would attach"),MESH_DASH_DISABLED=1(no dash at all — everytuimesh falls back to the P04 per-agent viewer windows).
The P03 jsonl/raw watcher-pane modes are retired (P04): the rendered viewer covers both granularities (per-token deltas AND completed blocks — pi's own renderer).
👀 Viewing subagents
- The agent dash (default on desktop): every
tuiagent across all meshes renders in the shared dash grid — one window no matter how many agents or meshes are running. Opened automatically at the first join (interactive desktop sessions only; headless/CI/test runs never pop windows unless the test harness forces them);blindmeshes display nothing. - Per-agent viewer windows — the fallback: when the dash is disabled (
MESH_DASH_DISABLED=1), a dash join fails, or the osascript attach fails, the agent gets a dedicated terminal window instead, labeledMESH-<meshId>-<agentId>and running the viewer for that agent (windowssetting, defaulttrue). meshview <meshId>(dotfiles repo — companion plan F03) — on-demand grouped view, including post-mortem.- Replay:
node scripts/meshview-agent.mjs --mesh <meshId> --agent <agentId> --once(orpi --resume .mesh/agents/<meshId>/sessions/<agentId>.jsonlfor the full interactive session).
Window/pane death ≠ child death — and the plugin NEVER removes a pane (the post-mortem is the first class citizen). Closing a viewer window or killing a dash pane kills exactly the viewer process; the child coprocess — a separate process with its own lifecycle — keeps working, and the post-mortem (sessions/ + stream/) is untouched. Re-open a viewer for a live agent any time with the replay command. The plugin never removes a dash pane — not on release (task limit, mesh_kill), not on crash, not on mesh_close, not on session shutdown. A mesh's panes persist in the shared dash after close: live tail while the tui children keep running (close policy), frozen tail once a child dies — the post-mortem room, with the grid keeping the slots. Pane removal is an operator-driven event only: you close the pane (the pane-exited hook prunes + re-lays the grid within seconds) or you explicitly tell the agent to remove it — the agent then runs the mesh_dash tool's leave <mesh>/<agent> action (or the CLI's leave command), which removes exactly that pane (kill pane, re-layout, clear the state entry; the child coprocess is NOT touched and keeps working). The agent never calls leave on its own — not as cleanup, not because a pane looks stale or post-mortem. With the P04 fallback, the surviving viewer windows keep rendering and teardown is explicit: close the windows. The dash tmux server is operator-owned — the plugin never kills it. The attach window opens only when no client is attached to the dash session (a second window would just mirror the first — concurrent joins within the in-flight grace never stack a second window; and the osascript itself scans for an existing MESH-DASH tab before creating, so a second window can never be created; a manual attach likewise never stacks on a live client).
🔧 Tools
| Tool | Purpose |
|---|---|
| mesh_spawn | Create a persistent mesh (brief + optional initial roster, optional per-mesh cap). Non-blocking — results arrive as digest notifications. The mesh's mode (blind/tui) comes from the mode setting. |
| mesh_task | Join a new agent (fresh context) into a running mesh. Fails at the cap — kill first or reuse a standing one. |
| mesh_instruct | Give a live agent new instructions. Delivered after its current turn if working (steer), immediately if standing by or waiting (followUp). |
| mesh_kill | Immediately abort and remove an agent (frees its slot, discards its context — kill-pane / SIGTERM, clean leave). |
| mesh_status | Current formation: roster (name, status, task counts vs budget, latest summary), the mode-appropriate view:/postmortem: line, messages left for you since last check, provenance head hash. Omit meshId to list active, closed, and past meshes. |
| mesh_close | End the mesh: per-mode close policy, write result.md with the final per-agent table, return the result. Idempotent. Surviving server is explicit-teardown-only. |
| mesh_events | Read recent bus events (the mesh's memory) — post-mortem and re-orientation after compaction; works on closed meshes. |
| mesh_rejoin | Re-join an interrupted mesh (outcome shutdown) as a fresh continuation mesh with a continuation brief (previous brief + last summaries + old evidence pointers — bus, result, session files, streams). |
| mesh_dash | The shared agent dash: status (live slot table), pop/pop-in (an agent's pane out of / back into the grid, target <mesh>/<agent>), grid <n> (set gridMax columns), repair (reconcile state vs live panes). The dash is the default tui display (one machine-global grid for all meshes; P04 per-agent windows are the fallback). |
Every agent gets an automatic stable display name (SubAgent-<NN>, per pi process) that rides the join envelope, every digest, the roster, result.md, the child's own send/done/claim receipts, and the styled pane title (📁 <WORKSPACE> 🖥 MESH-SUBAGENT-<NN> 🆔 <PID> — the agent's own pid).
🧠 Skill
The package ships a mesh-agents skill (skills/mesh-agents/SKILL.md) — a distilled orchestrator playbook: the digest protocol (the six wake lines and how to respond to each), the seed policy (kill/replace, reuse vs replace, verdict-gated loops, cap discipline, human escalation), CLAIM CONFLICT adjudication, the rejoin/post-mortem paths, and the tested-topology map (T01 fan-out → T13 headless modes) so the orchestrating model knows which graph shapes it can rely on. Pi advertises the skill to the model and loads it when mesh work is relevant; force it with /skill:mesh-agents. The tool schemas carry the contracts; the skill carries the how.
🚀 Quickstart
No install step: the extension is loaded from TypeScript via pi's jiti loader (erasable TS only, no build). From the repo root:
pi -e ./src/index.tsor add the extension path to the extensions array in ~/.pi/agent/settings.json (the pi.extensions field in package.json declares it the same way). Then ask your agent to use the mesh — e.g. "Use mesh_spawn with a brief and two agents to review and fix the parser." A mesh-agents widget line appears while meshes are active; digest notifications arrive as Mesh notification (automatic event — not a user request): … messages.
Requirements: tmux on PATH for tui meshes — the agent dash is a tmux server (desktop display; blind meshes need no tmux; a missing/broken tmux degrades a tui join to the P04 per-agent window fallback). unichar/tmux-window (dotfiles) are not required at runtime — the styled-glyph mapping is inlined in the plugin and the auto-window degrades to no-window; meshview is the operator-facing attach command once the dotfiles companion lands.
Artifacts land under the session's working directory (.mesh/agents/ is the mesh runtime home — post-mortems and provenance verification against a running or previously run mesh start there):
.mesh/agents/<meshId>/
brief.md # shared team context (verbatim from mesh_spawn.brief)
bus.jsonl # the hash-chained provenance log
result.md # final per-agent table + messages to orchestrator (written at close)
state.json # roster + usage + summaries + bus head + mode/session paths (written at close; mesh_rejoin's input)
sessions/ # per-agent pi session files — the post-mortem record, ALL modes (supersedes transcripts/)
inbox/ # orchestrator → child (TUI mode; headless delivery rides stdin)
outbox/ # child → orchestrator (all modes; the tailer's intake)
stream/ # teed token stream (ALL modes — the viewer/dash pane's source, replayable)
tmux.json # runtime agent map (legacy filename — the P04/P05 schema has no panes; the dash's pane map lives in ~/.mesh-dash/state.json)The legacy transcripts/ dir is gone (the always-on sessions/ supersedes it); old settings files with "transcripts": true remain valid — the key is accepted and ignored. Mesh dirs are never deleted by the plugin (operator rule); mesh ids are m-<6 chars>. Meshes from before the .mesh/ move live under tmp/mesh-agents/<meshId>/ and remain readable: mesh_status (listing), mesh_events, and mesh_rejoin all fall back to the legacy location.
🎛️ Settings
Built-in defaults ← global ~/.pi/agent/mesh-agents.json ← project <cwd>/.mesh-agents.json (later wins; missing file = absent level; unknown keys ignored; malformed files fail mesh_spawn/mesh_task with invalid mesh settings <path>: <detail> and create nothing):
{
"maxAgents": 4,
"defaultModel": "provider/id",
"pushEvents": true,
"claims": false,
"windows": true,
"mode": "tui"
}maxAgents— max alive agents per mesh (working + standing), integer 1–16; default 4.mesh_spawnaccepts a per-meshmaxAgentsoverride (1–16), fixed for that mesh's lifetime.defaultModel— children without a model spec use this; re-read fresh on every join (spawn roster and eachmesh_task), so later joiners pick up changes.pushEvents—falsedisables digest pushes entirely (the mesh still works; the orchestrator checksmesh_status). Read atmesh_spawn, fixed per mesh.mode— hosting + visibility mode per mesh:blind|tui(defaulttui). Read atmesh_spawn, fixed for that mesh's lifetime — one mode per mesh, fixed at spawn (no mixed modes). See Display modes. (The P03jsonl/rawmodes are retired — the rendered viewer covers both granularities.)windows— the fallback display: when the dash is disabled (MESH_DASH_DISABLED=1) or a dash join/attach fails, auto-open a per-agent terminal window at the agent's join (desktop + interactive only;blindmeshes: no-op — nothing to display). With the dash enabled (the default),tuiagents join the shared agent dash instead and no per-agent windows open. Defaulttrue; read atmesh_spawn, fixed per mesh.meshview/replay work regardless.claims—trueenables the file-claim protocol: children call theclaimtool before editing paths another agent claimed; conflicts arrive at the claiming child asCONFLICTmessages (asynchronous adjudication) + digests the orchestrator can act on (record-and-advise — no OS locks). Defaultfalse; read atmesh_spawn, fixed per mesh.transcripts— deprecated, ignored (the always-onsessions/supersedes it; the key is accepted for compatibility with old settings files).- Resource accounting (tokens/turns/context-percent/elapsed per agent, mesh totals) is always on — no setting; with local models the currency is tokens/turns/elapsed, and dollar totals fall out of the same data for remote models.
Per-agent overrides: the agent spec's model ("provider/id", resolved via resolveCliModel) beats the settings default, which beats the orchestrator's active model. The effective spec string is recorded on every join envelope and in result.md.
🔐 Provenance
Chain:
hash(envelope) = sha256(canonical(envelope minus its own hash)),prevHash= the stored hash of the previous envelope, genesis =sha256("pi-mesh-agents|genesis|" + meshId). Appends are synchronous; the log order is the true event order.Single writer: the orchestrator appends every envelope — join/instruct at tool-call time; child-originated
msg/response/status/claim/doneat outbox-tail time (ms-scale lag vs. the child's tool call; the envelope content shapes and the event set are unchanged).stream/andoutbox/files are operational, not evidentiary.Event types:
spawn,join,instruct,msg,response,status,claim,done,error,leave,close.Claim adjudication is async (P03): the child's
claimreceipt is "submitted"; conflicts arrive asCONFLICTmessages (the child never sees adjudication synchronously).Verify any bus (active or closed):
node scripts/verify-bus.mjs .mesh/agents/<meshId>/bus.jsonl # → OK <n> events, head <hash>
The verifier recomputes every hash and link, the genesis, seq monotonicity, first-event-is-spawn, last-event-is-close, and mesh-id consistency.
🛠️ Development
Runtime is npm-install-free; the dev tooling (typescript, eslint, typebox, the pi package for types) is in devDependencies:
npx tsc --noEmit # typecheck (src/ + scripts/)
npx eslint . # lint (src, scripts, tests, configs)
node scripts/bus-smoke.ts # bus unit smoke — synthetic chain with every event type + tamper detection
node scripts/settings-smoke.ts # settings unit smoke — precedence + validation (mode/windows/deprecated transcripts)
node scripts/bridge-smoke.ts # bridge unit smoke — line types, renderSendText byte-identity, unichar vectors, line IO
node scripts/rpc-smoke.ts # headless pi --mode rpc child boot (real spawn; get_state/prompt/stream/leave)
node scripts/tmux-smoke.ts # tmux hosting + TUI child boot (real servers; pane round-trip, ready line, watcher gone-lines)The last two smokes spawn real pi processes (pinned local model, thinking low — P03_SMOKE_MODEL overrides) and real tmux servers (isolated -L sockets); they leave no server or orphan process behind.
Conventions: erasable TypeScript only (interfaces/annotations — no enums, parameter properties, or namespaces), explicit .ts extensions on relative imports (works under both pi's jiti loader and node's type stripping), import type for type-only imports. The child extension (src/child.ts) is self-contained on purpose — it runs in the child's own process and imports no mesh-agents src/ module.
🧪 Tests
tests/ is a permanent, re-runnable modality suite that drives a real live mesh with a real orchestrator LLM per topology and asserts the plan's criteria against the durable evidence:
node tests/runner.mjs # all tests, T01 → T13, ~60+ min
node tests/runner.mjs T02 T06 # selected tests
node tests/runner.mjs --list| Test | Topology / modality |
|---|---|
| T01 | Fan-out (hub-and-spoke, no peer traffic) |
| T02 | Pipeline (sequential stages, artifact flow, verdict gate) |
| T03 | Review loop (verdict-gated, fresh critic per round, loop cap) |
| T04 | Pool (reuse via mesh_instruct, budget-forced handoff, steer/followUp delivery evidence) |
| T05 | Blocked handoff (waiting → instruct answer → same-context resume) |
| T06 | Peer-to-peer direct + broadcast + reply thread + orchestrator isolation |
| T07 | Concurrent meshes (two live at once) |
| T08 | Model heterogeneity (spec > settings > orchestrator, per-join resolution) |
| T09 | Tool allowlist (per-agent built-in allowlist, read-only agent) |
| T10 | Mesh rejoin (shutdown → continuation brief → fresh mesh) |
| T11 | Usage / claims / session files (always-on sessions/ supersedes the old opt-in transcripts) |
| T12 | Tmux hosting — tui mode (real tmux server: pane titles, session files, instruct evidence, kill/pane-loss, post-mortem survival) |
| T13 | Headless modes — blind/jsonl/raw (no/with tmux, exact session paths, stream files, watcher panes, liveness via exit event, release at close) |
Regression discipline: after a T11–T13 change, run node tests/runner.mjs T11 T12 T13 then node tests/runner.mjs T01 T02 (failure triage per T00 §5).
Design notes (details in tests/README.md): each test uses a fresh headless pi --mode rpc harness session with the extension loaded; the verbatim orchestrator prompt is extracted from the current plan file (.prompts/T<NN>-R<NN>_*.md, highest revision) at run time, so plan revisions are picked up automatically; agentic runs are pinned to the local 256k model with thinking low (--no-pin overrides); results append to tmp/mesh-tests/REPORT.md (T00 §4 format) with raw event logs under tmp/mesh-tests/raw/; a failed test is auto re-run once; soft criteria are recorded but do not fail a test.
📁 Layout
mesh-agents/
src/
index.ts # extension entry: eight tools, digest push, widget, session_shutdown
mesh.ts # Mesh runtime (join/instruct/kill/status/close, outbox tailer, per-mode dispatch)
childproc.ts # child process hosts (TUI panes + headless RPC coprocesses, stream tee, tmux.json)
child.ts # self-contained child extension (send/done/claim tools, inbox watch, outbox reporting)
children.ts # child spec + task prompt (byte-identical default)
bridge.ts # inbox/outbox line types, renderSendText, unichar glyph mapper, line IO helpers
bus.ts # hash-chained JSONL bus (pure Node)
settings.ts # settings load/merge/validate (pure Node; mode/windows)
errors.ts # MeshError (machine-readable codes)
skills/
mesh-agents/
SKILL.md # packaged orchestrator skill (shipped in the npm tarball; manifest pi.skills)
scripts/
verify-bus.mjs # chain verifier
mesh-tmux-watch.mjs # 1 s pane-liveness watcher process (tui meshes only)
meshwatch.mjs # plain line-printing watcher (jsonl/raw panes; also the stream replay tool)
bus-smoke.ts # bus unit smoke
settings-smoke.ts
bridge-smoke.ts
tmux-smoke.ts # tmux hosting + TUI child boot (real servers)
rpc-smoke.ts # headless RPC child boot (real spawn)
tests/
runner.mjs # the test runner
lib/ # harness + assertion helpers
T0<NN>-<NAME>/ # one dir per test (T01–T13)
.prompts/ # plan files (P01 build, P02 upgrades, P03 hosting, T00–T13 tests) — gitignored
.mesh/ # mesh runtime artifacts: .mesh/agents/<meshId>/ (brief, bus, result, state, sessions, inbox, outbox, stream, tmux.json) — gitignored
tmp/ # test-runner state (tmp/mesh-tests/) + task-output scenario dirs — gitignored📌 Scope (v1)
In: persistent orchestrator-managed meshes, process-hosted children (real pi TUI panes / headless --mode rpc coprocesses — built in, no in-process fallback), four visibility modes (blind/jsonl/raw/tui, per mesh at spawn), the inbox/outbox bridge with always-on per-agent session files, dynamic membership, task budgets, structured done results (status/verdict/waiting_on), blocked handoffs, per-agent model + tool-allowlist modalities, concurrent meshes, hash-chained provenance (single orchestrator writer), settings layer (mode, windows), mesh persistence across pi restarts (state.json + mesh_rejoin with session-file evidence pointers), per-agent resource accounting, per-agent file-claim protocol (async conflict messages), and the tmux session lifecycle (survives window close; explicit teardown only).
Not in v1 (by design): network listeners/ports/MCP, git worktrees (already handled at the session level by the AGENTS.md rule + git-worktree skill — the plugin is cwd-agnostic) and OS-level file locks (the opt-in claim protocol is the escalation step; flock only if same-file parallel editing becomes a real pattern), mesh-to-mesh communication, TTS/human notifications from the plugin (the digest → orchestrator → agent-notify chain is the design — a plugin-direct path would only help when the orchestrator isn't listening, and the extension API offers no human channel anyway), live-session resume (re-join starts fresh children; the session files are the continuity), dollar cost as a primary currency (tokens/turns/elapsed are the currency with local models; dollar totals fall out of the same data for remote models). Children load the operator's skills/AGENTS.md like any pi session and carry no MCP tools.
Plans and specs: .prompts/P01-R09_MESH-AGENTS.md (build spec), .prompts/P02-R01_… (persistence/accounting/transcripts/claims), .prompts/P03-R01_TMUX-CHILD-HOSTING.md (process hosting + visibility modes — the binding revision), and .prompts/T00-… (test series index). Companion (separate repo): dotfiles F03 — the meshview attach/kill/list command.
