A multiplayer, role-based coordination protocol for human-AI collaborative development.
Check out pvp.codes!
PVP is NOT a chatbot. It's a coordination layer where multiple humans and AI agents collaborate in real-time to:
- Shape prompts collaboratively
- Observe AI reasoning
- Gate actions before execution
- Record auditable streams of decisions
The code is a side effect; the real artifact is the recorded, auditable stream of human decisions mediated by AI execution.
- ✅ Real-time multiplayer collaboration
- ✅ Role-based access control (driver, navigator, adversary, observer, approver, admin)
- ✅ Approval gates for high-risk operations
- ✅ Live streaming of AI thinking and responses
- ✅ Context sharing and management
- ✅ Session forking and merging
- ✅ Interrupt mechanisms for human intervention
- ✅ WebSocket transport with reconnection
- ✅ Terminal UI (TUI) client
- ✅ Structured logging and monitoring
- ✅ Decision tracking - Git-based audit trail with automatic commit metadata
npm install github:agrathwohl/pvpInstall globally to use the CLI binaries from anywhere:
# From GitHub Package Registry
npm i -g @agrathwohl/pvp
# Verify installation
pvp --versionPrerequisites: The agent requires Bun runtime. Install it first:
curl -fsSL https://bun.sh/install | bashAfter global installation, three binaries are available:
| Binary | Runtime | Description |
|---|---|---|
pvp-server |
Node.js | WebSocket server for session coordination |
pvp-tui |
Node.js | Terminal UI client for human participants |
pvp-agent |
Bun | Claude AI agent with tool execution |
Start the PVP WebSocket server:
# Default (port 3000)
pvp-server
# Custom port and host
pvp-server --port 8080 --host 0.0.0.0Connect to a session with the Terminal UI:
# Create a new session
pvp-tui --url ws://localhost:3000 --name "Alice"
# Join existing session
pvp-tui --url ws://localhost:3000 --session <session-id> --name "Bob"
# Connect to remote server
pvp-tui --url wss://ws.pvp.codes --session <session-id> --name "Alice"Connect a Claude AI agent to a session:
# Basic usage (requires ANTHROPIC_API_KEY env var)
pvp-agent --url ws://localhost:3000 --session <session-id>
# With local working directory (agent executes commands in your local folder)
pvp-agent --url wss://ws.pvp.codes --session <session-id> --local
# Specify working directory path
pvp-agent --url wss://ws.pvp.codes --session <session-id> --local /path/to/project
# Full options
pvp-agent \
--url ws://localhost:3000 \
--session <session-id> \
--name "Claude Assistant" \
--model claude-sonnet-4-5-20250929 \
--api-key sk-ant-... \
--localpvp-agent options:
| Option | Description | Default |
|---|---|---|
-u, --url <url> |
WebSocket server URL | ws://localhost:3000 |
-s, --session <id> |
Session ID to join | (required) |
-n, --name <name> |
Agent display name | Claude Assistant |
-m, --model <model> |
Claude model | claude-sonnet-4-5-20250929 |
-k, --api-key <key> |
Anthropic API key | $ANTHROPIC_API_KEY |
-l, --local [path] |
Use local working directory | (disabled) |
--mcp-config <file> |
MCP servers config file | (none) |
Important: pvp-agent uses Bun runtime. If you see Cannot find package "bun", ensure Bun is installed and in your PATH.
# Clone and install dependencies
git clone https://github.com/your-username/pvp.git
cd pvp
npm install
# Build the project
npm run buildimport { startServer, startTUI } from "@agrathwohl/pvp";
// Start server
const server = await startServer({ port: 3000 });
// Start TUI client
await startTUI({
url: "ws://localhost:3000",
name: "Alice",
role: "driver",
});// Agent requires Bun runtime - import from submodule
import { startAgent } from "@agrathwohl/pvp/agent";
const agent = await startAgent({
url: "ws://localhost:3000",
session: "session-id",
apiKey: process.env.ANTHROPIC_API_KEY,
});See API Documentation for complete reference.
This project uses different runtimes per component for optimal performance and security:
| Component | Runtime | Command | Notes |
|---|---|---|---|
| Server | Node.js | npm run server |
WebSocket server, session management |
| TUI | Node.js | npm run tui |
Terminal user interface client |
| Agent | Bun | npm run agent |
Claude AI agent with shell execution |
The agent component requires Bun runtime because its shell executor (src/agent/tools/shell-executor.ts) uses Bun.spawn for secure command execution:
- Security: Array-based arguments prevent shell injection attacks
- Performance: Native subprocess streaming without external dependencies
- Safety: Built-in timeout and buffer limits
Important: Do NOT run the agent with tsx or Node.js. It will fail with module not found errors. Always use npm run agent.
npm run server
# or
npm run server -- --port 3000 --host 0.0.0.0# Create a new session
npm run tui -- --server ws://localhost:3000 --name "Alice" --role driver
# Join an existing session
npm run tui -- --server ws://localhost:3000 --session <session-id> --name "Bob" --role navigator# Join an existing session with Claude AI agent
npm run agent -- --server ws://localhost:3000 --session <session-id>
# With custom settings
npm run agent -- \
--server ws://localhost:3000 \
--session <session-id> \
--name "Claude" \
--model claude-sonnet-4-5-20250929 \
--api-key sk-ant-...
# Note: Requires ANTHROPIC_API_KEY environment variable or --api-key flag
# Get your API key at: https://console.anthropic.com/Agent Capabilities:
- Native Anthropic tool use API for shell command execution
- Command safety categorization (safe/low/medium/high/critical)
- Approval gates for risky operations
- Real-time streaming output
- Multi-turn conversation with tool results
┌─────────────────┐ ┌─────────────────┐
│ TUI Client │─────▶│ PVP Server │
│ (Ink/React) │ │ (WebSocket) │
└─────────────────┘ └─────────────────┘
│
┌────────┴────────┐
│ Protocol │
│ - 40+ msgs │
│ - Gates │
│ - Context │
│ - Forks │
└─────────────────┘
PVP includes an integrated git decision tracking system that automatically captures session context and embeds it into git commits.
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ PVP Server │────▶│ Bridge Service │────▶│ Git Hooks │
│ │ │ (port 9847) │ │ │
│ • Messages │ │ • State mgmt │ │ • Trailers │
│ • Approvals │ │ • HTTP API │ │ • Git notes │
│ • Tool exec │ │ • Webhooks │ │ • Validation │
└─────────────────┘ └─────────────────┘ └─────────────────┘
-
Bridge Service - Local daemon that maintains session state
- Starts automatically with the PVP server
- HTTP API at
http://localhost:9847 - Unix socket at
/tmp/pvp-git-bridge.sock
-
TUI Integration - Real-time decision tracking display
- Shows messages, prompts, approvals since last commit
- Displays tool execution summary
- Polls bridge service every 5 seconds
-
Git Hooks - Automatic commit metadata injection
prepare-commit-msg- Injects PVP trailerspost-commit- Stores extended metadata in git-notespre-push- Validates PVP metadata coverage
When connected, the TUI shows a decision tracking panel:
┌─────────────────────────────────────────────────────────────────┐
│ 📊 Decision Tracking | Msgs: 15 | Prompts: 3 | Approvals: 2 │
│ Tools: shell_execute, file_write | Last: a1b2c3d │
└─────────────────────────────────────────────────────────────────┘
Commits include PVP trailers:
feat(auth): implement JWT validation [pvp:msg-01]
Added JWT token validation middleware.
PVP-Session: ses_01HX7K9P4QZCVD3N8MYW6R5T2B
PVP-Messages: msg-01,msg-03,msg-05
PVP-Confidence: 0.85
Decision-By: human:alice,ai:claude
| Endpoint | Method | Description |
|---|---|---|
/commit-context |
GET | Current session metrics |
/extended-metadata |
GET | Full session metadata |
/status |
GET | Bridge service status |
/health |
GET | Health check |
- Architecture Guide - Full system design
- Git Commit Protocol - Commit format specification
- Git Hooks README - Hook installation and usage
- Human: driver, navigator, adversary, observer, approver, admin
- Agent: AI assistants that execute actions
- Session: create, join, leave, end, config_update
- Participant: announce, role_change
- Heartbeat: ping, pong
- Presence: active, idle, away, disconnected
- Context: add, update, remove (files, references, structured data)
- Secrets: share, revoke (API keys, credentials)
- Prompts: draft, submit, amend
- Thinking: start, chunk, end (AI reasoning streams)
- Response: start, chunk, end (AI output streams)
- Tools: propose, approve, reject, execute, result
- Gates: request, approve, reject, timeout (approval workflows)
- Interrupts: raise, acknowledge (human intervention)
- Forks: create, switch (parallel exploration)
- Merge: propose, execute (combine forks)
Gates require human approval before executing high-risk operations:
// Configure which operations require approval
const config: SessionConfig = {
require_approval_for: ["file_write", "shell_execute", "deploy"],
default_gate_quorum: { type: "any", count: 2 }, // Require 2 approvals
// ...
};Quorum Rules:
any: N approvals from anyoneall: All approvers must approverole: N approvals from specific rolespecific: Specific participants must approvemajority: >50% of approvers
Share context between participants:
const contextMsg = createMessage("context.add", sessionId, participantId, {
key: "requirements",
content_type: "file",
content: fileContents,
visible_to: ["agent_01"], // Optional: restrict visibility
});import { WebSocketClient } from "./src/transports/websocket.js";
import { createMessage } from "./src/protocol/messages.js";
import { ulid } from "./src/utils/ulid.js";
const client = new WebSocketClient("ws://localhost:3000", ulid());
client.on("connected", () => {
const createMsg = createMessage("session.create", ulid(), participantId, {
name: "My Session",
config: {
/* ... */
},
});
client.send(createMsg);
});
client.connect();See examples/ directory for complete examples:
basic-session.ts- Single participant sessionmulti-participant.ts- Multiple humans collaborating
| Key | Mode | Action |
|---|---|---|
p |
stream | Start composing prompt |
Ctrl+Enter |
compose | Submit prompt |
Esc |
compose | Cancel composition |
a |
gate | Approve gate |
r |
gate | Reject gate |
t |
stream | Toggle thinking panel |
Ctrl+C |
any | Exit |
Every message follows this structure:
{
v: 1, // Protocol version
id: string, // Message ID (ULID)
ts: string, // ISO timestamp
session: string, // Session ID
sender: string, // Participant ID
type: string, // Message type
ref?: string, // Reference to another message
seq?: number, // Sequence number (total ordering)
causal_refs?: string[], // Causal dependencies
fork?: string, // Fork ID
payload: object // Type-specific payload
}{
require_approval_for: ToolCategory[],
default_gate_quorum: QuorumRule,
allow_forks: boolean,
max_participants: number,
ordering_mode: "causal" | "total",
on_participant_timeout: "wait" | "skip" | "pause_session",
heartbeat_interval_seconds: number,
idle_timeout_seconds: number,
away_timeout_seconds: number
}Real-time bidirectional communication for TUI clients.
Server: src/transports/websocket.ts
Client: Automatic reconnection with exponential backoff
# Watch mode (server)
npm run dev
# Type checking
npm run build
# Run tests
npm testimport { createMessage } from "./src/protocol/messages.js";
const message = createMessage(
"prompt.submit", // type
sessionId, // session
participantId, // sender
{
// payload
content: "Build a login form",
target_agent: "claude_01",
contributors: [participantId],
context_keys: ["requirements"],
},
);import { ParticipantManager } from "./src/server/participant.js";
const pm = new ParticipantManager();
if (pm.canPrompt(participant)) {
// Submit prompt
}
if (pm.canApprove(participant)) {
// Approve gate
}import { GateManager } from "./src/server/gates.js";
const gm = new GateManager();
const gate = gm.createGate(gateRequest);
gm.addApproval(gate, approverId);
const { met, reason } = gm.evaluateQuorum(gate, participants);LOG_LEVEL=info # Logging level (debug, info, warn, error)
NODE_ENV=production # Environment# Build
npm run build
# Run server
NODE_ENV=production npm run server -- --port 3000
# Or with PM2
pm2 start dist/server/index.js --name pvp-server- Unit tests (protocol, session, gates, decision tracking)
- Integration tests (MCP server integration)
- Decision tracking system (git-based audit trail)
- MCP transport support
- Agent adapters (Claude, OpenAI, etc.)
- Persistent session recovery
- Message replay functionality
- Web UI client
- Enhanced fork/merge workflows
- Decision tree visualization
This is an implementation of the PVP specification. Contributions should:
- Follow the protocol specification precisely
- Maintain type safety (TypeScript strict mode)
- Include tests for new features
- Use structured logging (pino)
- API Reference - Complete programmatic API documentation
- Changelog - Version history and release notes
- Decision Tracking Architecture - Full system design
- Git Commit Protocol - Commit format specification
- Git Hooks Guide - Hook installation and usage
- Testing Guide - Test patterns and coverage
MIT
Built following the Pair Vibecoding Protocol specification.