原始内容
pi-server
Session multiplexer for pi-coding-agent. Exposes N independent AgentSession instances through WebSocket and stdio transports.
Note: This is a standalone pi server package, not an extension/skills/themes bundle.
Features
- Dual transport: WebSocket (port 3141) + stdio (JSON lines)
- Session lifecycle: Create, delete, list, switch sessions
- Command execution: Deterministic lane serialization per session
- Idempotent replay: Atomic outcome storage with free replay lookups
- Optimistic concurrency: Session versioning for conflict detection
- Extension UI: Full round-trip support for
select,confirm,input,editor,interview - Resource governance: Rate limiting, session limits, message size limits
- Pluggable authentication:
AuthProviderabstraction (allow-all, token, IP allowlist, composite) - Graceful shutdown: Drain in-flight commands, notify clients
- Protocol versioning:
serverVersion+protocolVersionfor compatibility checks
Installation
npm install pi-app-server
Quick Start
WebSocket
# Start server
npx pi-server
# Connect with wscat
wscat -c ws://localhost:3141
// Create and use a session
ws> {"type":"create_session","sessionId":"my-session"}
ws> {"type":"switch_session","sessionId":"my-session"}
ws> {"id":"cmd-1","type":"prompt","sessionId":"my-session","message":"Hello!"}
stdio
echo '{"type":"create_session","sessionId":"test"}
{"type":"switch_session","sessionId":"test"}
{"id":"cmd-1","type":"prompt","sessionId":"test","message":"Hello!"}' | npx pi-server
Built-in diagnostics use stderr so stdout stays reserved for newline-delimited protocol JSON.
Architecture
src/
├── server.ts # transports, connection lifecycle, routing glue
├── session-manager.ts # orchestration: coordinates stores, engines, sessions
├── command-router.ts # session command handlers, routing
├── command-classification.ts # pure command classification (timeout, mutation)
├── command-replay-store.ts # idempotency, duplicate detection, outcome history
├── session-version-store.ts # monotonic version counters per session
├── command-execution-engine.ts # lane serialization, dependency waits, timeouts
├── resource-governor.ts # limits, rate controls, health/metrics
├── extension-ui.ts # pending UI request tracking
├── server-ui-context.ts # ExtensionUIContext for remote clients
├── validation.ts # command validation
└── types.ts # wire protocol types + SessionResolver interface
Core invariants
- For each admitted command, there is exactly one terminal response.
- For each session ID, there is at most one live
AgentSession. - Subscriber session sets are always a subset of active sessions.
- Session version is monotonic and mutation-sensitive.
- Fingerprint excludes retry identity (
id,idempotencyKey) for semantic equivalence.
Key abstractions
SessionResolver— Interface for session access (enables test doubles, future clustering)CommandReplayStore— Idempotency and duplicate detectionSessionVersionStore— Optimistic concurrency via version countersCommandExecutionEngine— Deterministic lane serialization and timeout management
Protocol
See PROTOCOL.md for the normative wire contract.
Command → Response
Every command receives exactly one response:
{"id": "cmd-1", "type": "prompt", "sessionId": "s1", "message": "hello"}
{"id": "cmd-1", "type": "response", "command": "prompt", "success": true}
Event Broadcast
Events flow session → subscribers:
{"type": "event", "sessionId": "s1", "event": {"type": "agent_start", ...}}
Extension UI Round-Trip
- Extension calls
ui.select()→ server creates pending request - Server broadcasts
extension_ui_requestevent withrequestId - Client sends
extension_ui_responsecommand with samerequestId - Server resolves pending promise → extension continues
Idempotency & Replay
// First request with idempotency key
{"id": "cmd-1", "type": "list_sessions", "idempotencyKey": "key-1"}
{"id": "cmd-1", "type": "response", "command": "list_sessions", "success": true, ...}
// Retry with same key → replayed (free, no rate limit charge)
{"id": "cmd-2", "type": "list_sessions", "idempotencyKey": "key-1"}
{"id": "cmd-2", "type": "response", "command": "list_sessions", "success": true, "replayed": true, ...}
Timeout semantics (ADR-0001)
- Timeout is a terminal stored outcome (
timedOut: true), not an indeterminate placeholder. - Replay of the same command identity returns the same timeout response.
- Late underlying completion does not overwrite the stored timeout outcome.
Development
# Install dependencies
npm install
# Build
npm run build
# Run tests
npm test # Main test suite
npm run test:integration # Integration tests
npm run test:fuzz # Fuzz tests
# Module tests
node --experimental-vm-modules dist/test-command-classification.js
node --experimental-vm-modules dist/test-session-version-store.js
node --experimental-vm-modules dist/test-command-replay-store.js
node --experimental-vm-modules dist/test-command-execution-engine.js
# Type check + lint
npm run check
# Full CI
npm run ci
Release Process
This project uses release-please for automated versioning.
Automated Flow
- Push to
main→ release-please creates/updates a release PR - Merge the release PR → Creates GitHub release + git tag
- Verify npm publication after the release until publish automation is fully trusted
Changelog + Commit Policy
Release notes are generated from Conventional Commit messages. CHANGELOG.md is treated as a generated release artifact for released versions rather than a hand-maintained diary.
See docs/release-policy.md for:
- commit-message rules for releasable work
- breaking-change marking (
!/BREAKING CHANGE:) - changelog policy (including no manual
[Unreleased]section) - release review expectations
Manual Release Check
npm run release:check
This validates:
package.jsonhas required fieldsdist/exists with compiled files- Entry point has correct shebang
npm packproduces expected files- Full CI passes
Documentation
| Document | Purpose |
|---|---|
| AGENTS.md | Crystallized learnings, patterns, anti-patterns |
| PROTOCOL.md | Normative wire contract |
| docs/release-policy.md | Commit, changelog, and release-note policy |
| ADR-0001 | Atomic outcome storage (timeout semantics) |
| ADR-0007 | Session persistence |
| ADR-0009 | Historical authentication proposal (superseded) |
| ADR-0010 | Circuit breaker for LLM calls |
| ADR-0014 | Pluggable connection authentication (implemented) |
| ADR-0019 | Durable command journal foundation (Level 4) |
| ADR-0021 | Command contract registry, protocol-pure stdio, and opaque replay identity |
| ROADMAP.md | Phase tracking and milestones |
License
MIT