pi-peer
v1.2.1
Published
Standalone peer-to-peer Pi communication over the HerdR workspace - talk_to, talk_sessions, and talk_latest tools with a resilient local mailbox.
Readme
pi-peer
Peer-to-peer communication between Pi coding-agent sessions running in the same HerdR workspace. Two independently running sessions can find each other, read each other's recent history, and send each other blocking requests.
It is not a subagent framework: no delegation, no agent roles, no loop workflows, no advisor surface. Three tools, nothing else.
Features
talk_to— send a request to another live session and get its answer back.talk_sessions— list live peers, their status, and how many requests are queued for them.talk_latest— read a peer's most recent completed conversation events.- No daemon. Peers coordinate through an atomic file mailbox in the agent directory.
- No blind waits. A dead peer fails a call immediately; a slow peer returns a
pendingresult and wakes you later. - Private by default. Thinking is never published, and no session ever reads another session's transcript.
How it works
HerdR workspace
┌────────────────┐ ┌────────────────┐
│ Pi session A │ │ Pi session B │
│ peer-a1b │ │ peer-c3d │
└───────┬────────┘ └───────▲────────┘
│ │
│ 1. talk_to(target="peer-c3d") │ 2. arrives as
▼ │ a user message
┌───────────────────────────────────────────────────────┐
│ <agent-dir>/pi-peer/talk/<workspace-id>/ │
│ sessions/ inbox/ replies/ waiters/ latest/ │
└───────────────────────────────────────────────────────┘
▲ │
│ 4. reply, or a <peer_pong> wake │ 3. B answers
│ when the deadline passed ▼
└─────────────────────────────────────┘Each session registers itself, heartbeats every 10 s, and polls its own mailbox. There is no central process to run.
Requirements
- Pi coding agent running inside a HerdR pane.
HERDR_ENV=1,HERDR_PANE_ID, andHERDR_SOCKET_PATHset for each session — these provide session identity and the workspace socket.- Node 18+ for development.
Install
pi install npm:pi-peerGit installs remain available when you need an unreleased commit:
pi install git:github.com/MinhDuyDEV/pi-peerTools register automatically when a Pi session starts inside a HerdR pane, and only when that session is an eligible root seat. Set PI_PEER_DISABLED=1 for sessions that must not appear as peers or receive requests, and PI_CODING_AGENT_DIR to override the agent directory (default ~/.pi/agent).
Eligibility
A session gets the three talk tools only if all of these hold:
PI_PEER_DISABLEDis not1(generic opt-out), andPI_TASK_TOOL_DISABLEDis not1(child/writer sessions spawned by the task tooling never become peers), and- the harness seat is root:
PI_HARNESS_SEAT_ROLEis unset, empty, orroot. Any explicit non-root value (implementer,peer, or anything else) is a worker seat and stays ineligible.
PI_TASK_TOOL_DISABLED=1 is the primary child boundary used by pi-subagents; the seat-role check is defense in depth for interactive HerdR co-workers. This keeps ordinary child/writer sessions from sending or receiving peer requests or reading peer history. Root eligibility is not authentication between roots: the channel assumes the same-user/HerdR environment, and every peer payload remains untrusted advice.
Migration from 1.1.0 (this fork)
- Peer sessions that were previously registered from child/writer processes are no longer registered: reload those sessions (or leave them as-is — they simply stop appearing in
talk_sessions). messagebodies are now capped at 8 192 characters at the tool-parameter and envelope layers, and published history events are capped at 2 048 characters each. Requests or artifacts beyond these bounds are rejected/truncated rather than amplified.- Delivered requests and pending-reply wakes now carry an explicit UNTRUSTED advisory label and escape delimiter-injection text. A peer request can no longer masquerade as an authoritative instruction, and a peer's
talk_latesthistory no longer includes the<peer_message>/<peer_pong>delivery wrappers. - Development contracts moved to
typebox1.3.7 and@earendil-works/pi-coding-agent0.84.x (matching the harness host); the package now installs fromgithub.com/MinhDuyDEV/pi-peer.
Migrating from the upstream pi-roo extension, which used to bundle these tools: set features.talk=false in your pi-roo config, install pi-peer, then reload every Pi session. The storage namespace changed (pi-roo/talk → pi-peer/talk), so the cutover is a clean break with no dual-read migration.
Usage
Find out who is around, then ask one of them for a second opinion:
tool: talk_sessions
peer-a1b alpha idle (current)
peer-c3d beta working (2 queued)
peer-e5f gamma idletool: talk_to target="peer-c3d" message="Review my auth refactor: does the session fixation fix hold?"The request is queued immediately and delivered when peer-c3d goes idle, as a real user message in its session. Its answer comes back as the tool result.
Tools
talk_sessions
Lists live peers, one per line: <public-id> <name> <status>. The current session is marked (current), and a peer with pending inbound requests shows (N queued). Status is one of idle | working | blocked | done | unknown. Stale panes are excluded. No parameters.
A public id is peer- plus the last three characters of the session id; the full session id stays internal.
talk_to
Sends a blocking request to another live session and returns its final response.
| Name | Type | Required | Notes |
| --- | --- | --- | --- |
| target | string | yes | Public peer id (peer-xxx) or unique display name. |
| message | string | yes | Non-empty request message (max 8 192 characters). |
| timeoutMs | number | no | Wait deadline, clamped to 1 000–3 600 000 ms. Default 600 000 ms (10 min). |
When timeoutMs passes while the target is still working, the call returns a non-error pending result telling you not to resend. The reply arrives later as a <peer_pong> user message that wakes your session.
talk_latest
Fetches the N most recent completed conversation events published by a peer, oldest first.
| Name | Type | Required | Notes |
| --- | --- | --- | --- |
| target | string | yes | Public peer id (peer-xxx) or unique display name. |
| count | integer | no | 1–10, default 1. |
tool: talk_latest target="peer-c3d" count=3Each peer publishes its own bounded history (max 10 events: user, assistant text, tool calls, tool results; each event message capped at 2 048 characters). talk_latest reads only that published artifact — it never touches another session's transcript, and thinking is never published. In-progress turns are excluded and flagged in the output, and peer delivery wrappers (<peer_message>/<peer_pong>) are never published.
Guarantees
- Liveness decides. Registrations, refreshed every 10 s, are the authoritative signal. A peer that shut down cleanly fails your call immediately; a crashed one fails it once its registration goes stale (about a minute) plus two confirming checks. Dead peers cannot be listed or targeted.
- Requests never vanish silently. Invalid or malformed requests get an
ok=falsereply so the caller stops waiting; aborting withdraws a queued request; a request already being processed is not interrupted. - Fair, serial delivery. One request at a time per receiver, oldest first, delivered only while the receiver is idle. Request cycles (A → B → A) are rejected before delivery.
- Exactly one answer. An in-deadline reply consumes the pending waiter, so a late
<peer_pong>wake never duplicates it. - Ambiguity fails closed. Two peers sharing a public id resolve to an error, never to a guess.
- Bound untrusted input and output.
talk_tomessages are capped at 8 192 characters (schema + envelope), published history events at 2 048 characters each, andtalk_latestoutput at the artifact ceiling. Delivered wrappers escape delimiter closers so a peer cannot terminate a wrapper early or inject markup. - Non-authoritative by construction. Every delivered request and pending-reply wake is labeled UNTRUSTED advisory input with no lifecycle/claim/evidence/decision/ship/write authority, so no peer message can be mistaken for an instruction.
- No control-traffic leak.
<peer_message>/<peer_pong>delivery wrappers are excluded from published history;talk_latestnever shows another session's peer-talk traffic as if it were that peer's own words.
For the full flow, invariants, and cleanup rules, see docs/ARCHITECTURE.md.
Storage
Artifacts live under <agent-dir>/pi-peer/talk/<workspace-id>/:
| Path | Contents |
| --- | --- |
| sessions/ | Peer registrations, refreshed by heartbeat. |
| inbox/ | Queued requests per target session. |
| replies/<caller-session-id>/ | Answers written by the responding peer. |
| waiters/<caller-session-id>/ | Pending-wake trackers owned by the caller. |
| latest/ | Each peer's published history. |
Writes are atomic (temp file plus rename) and the mailbox directory is created with 0700 permissions. Orphaned artifacts are swept on idle; artifacts of dead sessions are collected after a 24 h TTL.
Development
npm install
npm test # 64 tests (3 suites)
npm run test:focused # 57 unit tests
npm run test:integration # 7 mocked two-peer lifecycle tests
npm run typecheck # tsc --noEmitpi-extension/pi-peer/— shipped runtime:index.ts(entrypoint),service.ts(tool registration),schemas.ts,herdr.ts(workspace identity),history.ts,protocol.ts(request/reply envelopes),storage.ts(atomic persistence).test/peer/— unit tests.test/integration/— mocked two-peer lifecycle.docs/— architecture and decisions; seedocs/decisions/0011-standalone-pi-peer-extension.mdfor the packaging decision.
Distribution is available through npm for releases and GitHub for unreleased commits.
Related Work
- Pi coding agent — the host whose extension API this builds on.
- HerdR — the pane/workspace environment that provides session identity and peer discovery.
License
MIT
