v0.2.0 — local session parity
opencode-plugin-peers 0.2.0 — local session parity
Brings the plugin to local-session parity with Claude Code's cross-session messaging. Protocol v2 makes every OpenCode session (including child sessions) individually addressable, delivery is immediate even while the target is busy, and message state is durable across restarts.
Features
- Per-session endpoints (protocol v2): one registry record per OpenCode session instead of one per process; exact endpoint-ID targeting with unique-name fallback; v1 peers keep working through a compatibility record + loopback listener
- Immediate busy-session delivery: one
promptAsyncper message, deterministicmsg_*IDs, structuredmetadata.peerMessageprovenance — no more waiting for idle - Durable per-endpoint spool:
queued/held/inflight/donestate machine, atomic writes, idempotency by sender+messageId, restart recovery, workspace-spool migration - Durable outbox + final ACKs: delivered / refused / expired / dropped / duplicate outcomes are retried into the sender's outbox; new
peer_message_statustool and/peers-outboxcommand - UDS transport on macOS/Linux (loopback TCP on Windows)
inboundPolicy: "auto": same-directory sessions accept, cross-directory hold; held messages expire after 5 minutes (configurable) with final ACKs to the sender- Permission guardrails: in
allowmode, requests touching permission config /AGENTS.md/ credentials / shell startup files stay under native OpenCode policy (best-effort — see README security section) - TUI controls: palette actions for peers / inbox / outbox / rename use explicit host dialogs with confirmations; single-Enter slash commands unchanged
- Slash commands self-register:
/peers,/list-agents,/peers-name,/peers-inbox,/peers-outboxare injected via the pluginconfighook — fresh installs get them without any manual config (opencode does not scan plugin packages forcommands/*.md)
Fixes (review round on top of the parity branch)
- Fire-and-forget ACK dispatch no longer rejects unhandled — a slow spool lock cannot crash the host opencode process
- Prompt injection is bounded by
injectTimeoutMs(default 30s) — a hungpromptAsyncno longer wedges delivery anddispose()forever - Startup routing window closed: inbound messages arriving before deferred session discovery wait (bounded) instead of getting a terminal 404
- Transient "message not visible yet" permission verdicts are no longer cached as local turns
- Protected-permission denylist extended (shell rc,
.gitconfig,.netrc,.kube,.docker, LaunchAgents, crontab); README documents it as best-effort, not a boundary
Tests
npm test: 168/168 pass — including real OpenCode 1.18.15 process coverage: two OS processes, same-name sessions in one process, exact routing, concurrent busy injection, restart recovery, v1↔v2 interop, hold accept/drop/expiry ACKs- Node 18.20.8 focused protocol coverage: pass
npm run typecheck,npm pack --dry-run: pass- Real TUI E2E:
/peersand/peers-outboxexecute on a single Enter from the home route
Docs
- README rewritten: v2 architecture, migration behavior, security model (incl. honest limits of the
allowguardrail), Claude Code comparison, new options (heldExpiryMs,maxMessageAgeMs,inboundPolicy: "auto",injectTimeoutMs)
Full Changelog: v0.1.7...v0.2.0