An open API service indexing awesome lists of open source software.

https://github.com/tryinget/pi-server

Session multiplexer for pi-coding-agent — WebSocket + stdio, the protocol IS the architecture
https://github.com/tryinget/pi-server

coding-agent multiplexer pi session-management stdio websocket

Last synced: 17 days ago
JSON representation

Session multiplexer for pi-coding-agent — WebSocket + stdio, the protocol IS the architecture

Awesome Lists containing this project

README

          

# pi-server

Session multiplexer for [pi-coding-agent](https://www.npmjs.com/package/@mariozechner/pi-coding-agent). Exposes N independent `AgentSession` instances through WebSocket and stdio transports.

[![CI](https://github.com/tryingET/pi-server/actions/workflows/ci.yml/badge.svg)](https://github.com/tryingET/pi-server/actions/workflows/ci.yml)
[![npm](https://img.shields.io/npm/v/pi-app-server.svg)](https://www.npmjs.com/package/pi-app-server)

> 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**: `AuthProvider` abstraction (allow-all, token, IP allowlist, composite)
- **Graceful shutdown**: Drain in-flight commands, notify clients
- **Protocol versioning**: `serverVersion` + `protocolVersion` for compatibility checks

## Installation

```bash
npm install pi-app-server
```

## Quick Start

### WebSocket

```bash
# Start server
npx pi-server

# Connect with wscat
wscat -c ws://localhost:3141
```

```js
// 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

```bash
echo '{"type":"create_session","sessionId":"test"}
{"type":"switch_session","sessionId":"test"}
{"id":"cmd-1","type":"prompt","sessionId":"test","message":"Hello!"}' | npx pi-server
```

## 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 detection
- **`SessionVersionStore`** — Optimistic concurrency via version counters
- **`CommandExecutionEngine`** — Deterministic lane serialization and timeout management

## Protocol

See [PROTOCOL.md](./PROTOCOL.md) for the normative wire contract.

### Command → Response

Every command receives exactly one response:

```json
{"id": "cmd-1", "type": "prompt", "sessionId": "s1", "message": "hello"}
{"id": "cmd-1", "type": "response", "command": "prompt", "success": true}
```

### Event Broadcast

Events flow session → subscribers:

```json
{"type": "event", "sessionId": "s1", "event": {"type": "agent_start", ...}}
```

### Extension UI Round-Trip

1. Extension calls `ui.select()` → server creates pending request
2. Server broadcasts `extension_ui_request` event with `requestId`
3. Client sends `extension_ui_response` command with same `requestId`
4. Server resolves pending promise → extension continues

### Idempotency & Replay

```json
// 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

```bash
# 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](https://github.com/googleapis/release-please) for automated versioning.

### Automated Flow

1. Push to `main` → release-please creates/updates a release PR
2. Merge the release PR → Creates GitHub release + git tag
3. Release published → GitHub Action publishes to npm with provenance

### Manual Release Check

```bash
npm run release:check
```

This validates:
- `package.json` has required fields
- `dist/` exists with compiled files
- Entry point has correct shebang
- `npm pack` produces expected files
- Full CI passes

## Documentation

| Document | Purpose |
|----------|---------|
| [AGENTS.md](./AGENTS.md) | Crystallized learnings, patterns, anti-patterns |
| [PROTOCOL.md](./PROTOCOL.md) | Normative wire contract |
| [ADR-0001](./docs/adr/0001-atomic-outcome-storage.md) | Atomic outcome storage (timeout semantics) |
| [ADR-0007](./docs/adr/0007-session-persistence.md) | Session persistence |
| [ADR-0009](./docs/adr/0009-connection-authentication.md) | Historical authentication proposal (superseded) |
| [ADR-0010](./docs/adr/0010-circuit-breaker.md) | Circuit breaker for LLM calls |
| [ADR-0014](./docs/adr/0014-pluggable-authentication.md) | Pluggable connection authentication (implemented) |
| [ADR-0019](./docs/adr/0019-durable-command-journal-foundation.md) | Durable command journal foundation (Level 4) |
| [ROADMAP.md](./ROADMAP.md) | Phase tracking and milestones |

## License

MIT