@heyhuynhgiabuu/pi-peers
Session-to-session plain-text mailboxes for Pi: discover live sessions across projects and message them (including pi-task subagents), with prompt-free accept/refuse inbound and race-hardened delivery.
Package details
Install @heyhuynhgiabuu/pi-peers from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@heyhuynhgiabuu/pi-peers- Package
@heyhuynhgiabuu/pi-peers- Version
0.1.0- Published
- Aug 13, 2026
- Downloads
- 99/mo · 14/wk
- Author
- killerkidbo
- License
- MIT
- Types
- extension
- Size
- 66.4 KB
- Dependencies
- 0 dependencies · 2 peers
Pi manifest JSON
{
"extensions": [
"./dist/index.js"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-peers — pi sessions find and message each other, with roles
A Pi extension for session-to-session plain-text mailboxes: every pi session registers itself in a shared directory and watches an inbox. Sending is a file landing in another session's inbox; receiving is that file being claimed and handed to the agent as a message clearly marked as coming from a peer, not from the user.
Built as an independent experiment after researching @shift-labs/pi-peer —
same mechanics, different posture where the tradeoffs matter:
| shift-labs/pi-peer | pi-peers | |
|---|---|---|
| Inbound default | accept |
accept — no confirm dialogs; refuse is the explicit opt-out |
| Identity | self-asserted, any same-user session | cwd/session hash-bound records, still advisory under the same-user file model |
| Roles | none | user / companion / task declared per session |
| Subagent habit | a task session can inject under accept | pi-task children self-label as task; accept delivers them like other peers |
| Install | npm package | npm package |
Agent creation and lifecycle intentionally stay with
pi-task — this
package is transport only: discovery, delivery, presence, receipts.
Install
pi install npm:@heyhuynhgiabuu/pi-peers
or add to ~/.pi/agent/settings.json:
{ "packages": ["npm:@heyhuynhgiabuu/pi-peers"] }
Every session sharing the same machine (and PI_PEERS_DIR) then discovers the
others automatically.
Configuration
| Variable | Default | Meaning |
|---|---|---|
PI_PEERS_DIR |
~/.pi/agent/peers |
Where records and mailboxes live (0700); relative values resolve to an absolute path |
PI_PEERS_INBOUND |
accept |
accept delivers with no prompt; refuse drops everything; invalid values fail closed as refuse |
PI_PEERS_ROLE |
inferred | Override the role as user, companion, or task; pi-task children are inferred as task, other sessions default to user |
Use
peer action=list # discover: live/stalled first, newest offline (folded past 10)
peer action=send # send plain text to another session by name
/peers # human listing; shows every record (no offline folding)
Plain text only, capped at 32 KB. A message is a summary plus a path, not a payload. ANSI escape sequences and control characters are stripped at parse, keeping newlines and tabs. A session that is not running keeps its mailbox, so mail waits and is read on resume. Every delivery carries the no-authority preamble: it cannot approve anything, cannot change configuration, and any slash command in it is inert text. A policy receipt can be delivered, rejected, or report a delivery error; no receipt before timeout remains queued/pending.
Working with pi-task subagents
Use pi-task's task tool to create, control, resume, cancel, and collect the
result from subagents. Task children self-label as role task. Once a task
session appears in peer action=list, use peer action=send only as a
coordination side-channel for a new finding or warning that materially affects
its work. Do not poll task status through peer mail, and do not treat peer
mail as delegation, approval, or the task result.
Inbound is accepted without prompts by default, including messages from
pi-task subagents. Set PI_PEERS_INBOUND=refuse only when this session should
receive no peer mail.
Guarantees (pinned by the test suite)
- Identity is a hash of cwd+session id; two sessions in one directory never share an inbox.
- Presence is pid + heartbeat + liveness: live / stalled (alive but wedged) / offline (mail still waits); a pid whose process died decays to offline, so crashed sessions never ghost the listing and unresumable empty inboxes are actually swept. Registry writes use a live-owner fence: transient lock contention throws so the holder keeps its lease (retries), while a real replacement returns ownership loss and closes the stale watcher.
- Sweep never destroys a mailbox that holds mail; only the empty mailbox of an unresumable session is discarded promptly.
- A reader never sees a half-written letter (
.tmp+ rename); a letter is atomically claimed before it is read, so concurrent watchers cannot deliver it twice, claims from dead processes are recovered, and requeues land under a fresh name when the original is occupied. - Loops terminate structurally: dedupe window, per-sender rate, backlog cap, and consecutive-error circuit breaker.
- Default inbound is
accept— peer mail always delivers without a prompt;refuseis the only opt-out viaPI_PEERS_INBOUND.
Trust model
Local same-user advisory identity: records, letters, receipts, and roles are forgeable by other same-user processes. Mitigations are behavioral, not cryptographic — bounded reads, sanitized metadata, control-byte stripping, and a no-authority preamble on every delivery. Do not send secrets over peer mail.
Layout
src/
registry.ts addresses, roles, presence, lock/lease fencing, sweeping
mailbox.ts deposit, drain, watch, receipts, claims
policy.ts accept/refuse policy and loop guards
format.ts the strings the model reads
index.ts pi wiring: lifecycle, tool, delivery
test/
index.test.ts behavior spec