Architecture: OpenClaw-OpenCode ACP Integration
Status: Draft
Date: 2026-02-12
System Overview
┌─────────────────────────────────────────────────────────────────┐
│ User Layer │
│ WhatsApp, Telegram, Discord, Signal, Web Chat │
└────────────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ OpenClaw Gateway │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Agent Runtime (Claude, GPT-4, etc.) │ │
│ │ ├─ Tools: browser, message, nodes, exec... │ │
│ │ └─ NEW: opencode (ACP client tool) │ │
│ └──────────────────────────────────────────────────────────┘ │
└────────────────────────────┬────────────────────────────────────┘
│ HTTP / WebSocket
▼
┌─────────────────────────────────────────────────────────────────┐
│ OpenCode ACP Server(s) │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ Instance 1 │ │ Instance 2 │ │ Instance N │ │
│ │ (local dev) │ │ (VPS build) │ │ (team shared) │ │
│ │ Port: 3333 │ │ Port: 3334 │ │ Port: 3335 │ │
│ └────────────────┘ └────────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ OpenCode Agent Sessions │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Session 1 │ │ Session 2 │ │ Session 3 │ │
│ │ api-server │ │ dashboard │ │ PR review │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ (Each session = isolated agent context) │
└─────────────────────────────────────────────────────────────────┘
Component Design
1. OpenCode ACP Server
What it is:
- Headless daemon that manages OpenCode agent sessions
- Exposes HTTP + WebSocket endpoints for programmatic control
- Runs independently of OpenClaw (local or remote)
Startup:
# Basic
opencode acp --port 3333
# Remote-accessible
opencode acp --hostname 0.0.0.0 --port 3333
# With default working directory
opencode acp --port 3333 --cwd ~/projects
# Alternative: web mode (if ACP is included)
opencode web --hostname 0.0.0.0 --port 3333Responsibilities:
- Accept session create/attach requests
- Route messages to appropriate agent sessions
- Stream agent output back to clients
- Persist session state (TBD: confirm OpenCode behavior)
State Management:
- Sessions stored in-memory + disk (TBD: confirm persistence)
- Session IDs are UUIDs or human-readable names
- Clients reconnect via session ID
2. OpenClaw opencode Tool
What it is:
- Native OpenClaw tool (like
browser,message,exec) - Speaks ACP protocol (HTTP for commands, WebSocket for streaming)
- Manages connection lifecycle to one or more ACP servers
Tool Interface:
interface OpenCodeTool {
// Connection management
connect(url: string, name?: string): Promise<ConnectionStatus>
disconnect(name?: string): Promise<void>
// Session operations
createSession(config: SessionConfig): Promise<SessionInfo>
listSessions(instanceName?: string): Promise<SessionInfo[]>
attachSession(sessionId: string): Promise<SessionInfo>
terminateSession(sessionId: string): Promise<void>
// Messaging
sendMessage(sessionId: string, message: string): Promise<MessageResponse>
getMessages(sessionId: string, limit?: number): Promise<Message[]>
// Status & monitoring
getStatus(sessionId: string): Promise<SessionStatus>
getStats(sessionId: string): Promise<TokenStats>
exportSession(sessionId: string): Promise<SessionExport>
// Stream handling (internal)
subscribeToSession(sessionId: string, callback: (event: AgentEvent) => void): void
unsubscribeFromSession(sessionId: string): void
}Connection State:
interface ConnectionState {
instances: Map<string, {
url: string,
connected: boolean,
lastPing: Date,
activeSessions: Set<string>
}>
}Tool Parameters (OpenClaw function call format):
{
"tool": "opencode",
"action": "create_session",
"instance": "local",
<<<<<<< HEAD
"project": "/workspace/projects/api-server",
=======
"project": "repos/api-server",
>>>>>>> f146a548fbab991798ce91719b88cbf1f064588a
"message": "Add rate limiting to /login endpoint",
"model": "anthropic/claude-sonnet-4",
"agent": "default"
}3. ACP Protocol Client
Implementation:
- TypeScript library (or Node.js module)
- Handles HTTP requests + WebSocket streams
- Manages reconnection, retries, backoff
Core Classes:
class ACPClient {
constructor(baseUrl: string, options?: ClientOptions)
// HTTP operations
async createSession(config: SessionConfig): Promise<SessionInfo>
async listSessions(): Promise<SessionInfo[]>
async sendMessage(sessionId: string, message: string): Promise<void>
async getStatus(sessionId: string): Promise<SessionStatus>
async terminateSession(sessionId: string): Promise<void>
// WebSocket streaming
connectStream(sessionId: string): WebSocket
onMessage(callback: (event: AgentEvent) => void): void
onError(callback: (error: Error) => void): void
disconnect(): void
}
class ACPConnectionManager {
private clients: Map<string, ACPClient>
addInstance(name: string, url: string): void
removeInstance(name: string): void
getClient(name: string): ACPClient | undefined
healthCheck(): Promise<Map<string, boolean>>
}Reconnection Strategy:
- Exponential backoff: 1s, 2s, 4s, 8s, 16s, 30s (max)
- Keep session ID across reconnects
- Resubscribe to WebSocket streams after reconnect
4. Enhanced coding-agent Skill
Current: CLI-only (codex exec, opencode run, etc.)
Enhanced: Smart mode detection
## Mode Selection
1. Check if ACP server configured and reachable
2. If yes: Use ACP mode (persistent sessions, better UX)
3. If no: Fall back to CLI mode (spawn process, one-shot)
## Usage Examples
### ACP Mode (preferred)
```bash
# User: "Start OpenCode on api-server and add rate limiting"
# OpenClaw detects ACP server at localhost:3333
opencode action:create_session project:~/projects/api-server message:"Add rate limiting"
# Returns: session_id = "abc123"
opencode action:get_status session:abc123
# Polls status until complete
# Later:
opencode action:attach session:abc123 message:"Now write tests"CLI Mode (fallback)
# No ACP server found, use legacy mode
bash pty:true workdir:~/projects/api-server command:"opencode run 'Add rate limiting'"
# Blocks until complete
**Migration Path:**
- Skill detects ACP via config or auto-discovery (try localhost:3333)
- Logs which mode is used (for user transparency)
- Graceful fallback if ACP connection fails
---
## Data Flow
### Create Session + Send Message
User: “Start OpenCode on api-server, add rate limiting” │ ▼ OpenClaw Agent parses intent │ ▼ <<<<<<< HEAD Calls: opencode(action=“create_session”, project=“/workspace/projects/api-server”,
Calls: opencode(action=“create_session”, project=“repos/api-server”,
f146a548fbab991798ce91719b88cbf1f064588a message=“Add rate limiting to /login endpoint”) │ ▼ opencode tool → HTTP POST to ACP server POST /sessions Body: { <<<<<<< HEAD “project”: “/workspace/projects/api-server”, ======= “project”: “repos/api-server”, f146a548fbab991798ce91719b88cbf1f064588a “model”: “anthropic/claude-sonnet-4”, “agent”: “default” } │ ▼ ACP Server creates session Response: { “sessionId”: “abc123”, “status”: “created” } │ ▼ opencode tool → WebSocket connect to /sessions/abc123/stream │ ▼ opencode tool → HTTP POST message POST /sessions/abc123/messages Body: { “content”: “Add rate limiting to /login endpoint” } │ ▼ ACP Server routes to agent │ ▼ Agent works (reads files, makes changes, runs commands) │ ▼ Agent events streamed via WebSocket WS: { “type”: “file_opened”, “path”: “src/auth.ts” } WS: { “type”: “file_changed”, “path”: “src/auth.ts”, “additions”: 12 } WS: { “type”: “command_run”, “command”: “npm test” } WS: { “type”: “message”, “content”: “Tests passed!” } WS: { “type”: “complete”, “summary”: “Added express-rate-limit” } │ ▼ opencode tool buffers events, forwards to OpenClaw │ ▼ OpenClaw Agent formats for user │ ▼ User receives updates via WhatsApp: ”📂 Opened src/auth.ts” “✏️ Modified src/auth.ts (+12 lines)” ”🧪 Running tests…” ”✅ Tests passed!” “Done! Added express-rate-limit to /login. Ready to commit?”
---
## State Persistence
### OpenClaw Side
**Problem:** Gateway restarts shouldn't lose session references
**Solution:** Persist connection config + active sessions to disk
```typescript
// ~/.openclaw/config/opencode-state.json
{
"instances": [
{
"name": "local",
"url": "http://localhost:3333",
"lastConnected": "2026-02-12T08:00:00Z"
},
{
"name": "vps-heavy",
"url": "http://vps.example.com:3333",
"lastConnected": "2026-02-11T15:30:00Z"
}
],
"activeSessions": [
{
"sessionId": "abc123",
"instance": "local",
<<<<<<< HEAD
"project": "/workspace/projects/api-server",
=======
"project": "repos/api-server",
>>>>>>> f146a548fbab991798ce91719b88cbf1f064588a
"created": "2026-02-12T07:45:00Z",
"lastActive": "2026-02-12T08:00:00Z"
}
]
}
On Gateway restart:
- Load state file
- Reconnect to instances (with health check)
- Reattach to active sessions (if still alive)
OpenCode Side
Question: Does opencode acp persist sessions across restarts?
If yes: Gateway just needs to track session IDs If no: Need to handle “session lost” gracefully (notify user)
Error Handling
Connection Failures
try {
await opencode.connect('http://localhost:3333')
} catch (err) {
if (err.code === 'ECONNREFUSED') {
return "OpenCode ACP server not running. Start with: opencode acp --port 3333"
} else if (err.code === 'TIMEOUT') {
return "OpenCode server not responding. Check if it's healthy."
} else {
throw err
}
}Session Lost
// WebSocket disconnected, can't reconnect to session
if (sessionGone) {
notify user: "⚠️ Session abc123 was lost (server restarted?). You can:
1. Create new session
2. List other active sessions
3. Check if work was saved"
}Permission Timeout
// User didn't respond to permission request in 5 minutes
if (permissionTimedOut) {
send to agent: "Permission denied (timeout)"
notify user: "⏰ Permission request expired. Agent paused at: npm install express-rate-limit"
}Security Considerations
Authentication
Question: Does ACP require auth tokens?
If yes:
- Store tokens in OpenClaw config (encrypted?)
- Include in HTTP headers:
Authorization: Bearer <token>
If no:
- Rely on network isolation (localhost-only or VPN)
- Consider adding basic auth for remote instances
Command Execution
Risk: Agent runs arbitrary commands on OpenCode host
Mitigation:
- Use OpenCode’s built-in sandboxing
- Rely on permission approval flow (commands > danger threshold require approval)
- OpenClaw forwards approvals from user, doesn’t auto-approve
Data Exfiltration
Risk: Agent could read sensitive files, send to LLM provider
Mitigation:
- OpenCode’s
.gitignore+.opencodeignorepatterns - User controls which directories are accessible (via
--cwd) - Session scope limited to project directory
Deployment Topologies
Topology 1: Local Dev (Simplest)
┌──────────────────────────────────┐
│ Developer's Laptop │
│ ┌─────────────────────────────┐ │
│ │ OpenClaw Gateway │ │
│ └──────────┬──────────────────┘ │
│ │ localhost:3333 │
│ ┌──────────▼──────────────────┐ │
│ │ opencode acp │ │
│ └─────────────────────────────┘ │
└──────────────────────────────────┘
Use case: Personal dev, quick tasks
Topology 2: Remote Build Server
┌──────────────────┐ ┌────────────────────────┐
│ OpenClaw │ │ VPS (beefy) │
│ (Gateway) │◄───────►│ ┌──────────────────┐ │
│ Laptop/K8s │ HTTP │ │ opencode acp │ │
│ │ │ │ --hostname 0.0.0.0│ │
└──────────────────┘ │ └──────────────────┘ │
│ Port: 3333 │
└────────────────────────┘
Use case: Heavy builds, keep laptop cool
Topology 3: Team Collaboration
┌────────────────┐ ┌──────────────────────────┐
│ Alice │ │ Shared OpenCode Server │
│ (OpenClaw) │◄─────►│ (K8s / VM) │
└────────────────┘ │ ┌────────────────────┐ │
│ │ opencode acp │ │
┌────────────────┐ │ │ Multi-session │ │
│ Bob │◄─────►│ └────────────────────┘ │
│ (OpenClaw) │ │ Port: 3333 │
└────────────────┘ │ Auth: Required │
└──────────────────────────┘
Use case: Pair programming, shared review sessions
Topology 4: Multi-Instance (Advanced)
┌─────────────────────────────────────────────────┐
│ OpenClaw Gateway │
│ ┌──────────────────────────────────────────┐ │
│ │ opencode tool │ │
│ │ ┌──────────┬──────────┬──────────────┐ │ │
│ │ │Instance 1│Instance 2│ Instance 3 │ │ │
│ │ │ (local) │ (VPS) │ (team) │ │ │
│ │ │ :3333 │ :3334 │ :3335 │ │ │
│ │ └──────────┴──────────┴──────────────┘ │ │
│ └──────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
Use case: Different projects, different hardware, different teams
Config:
opencode:
instances:
- name: local
url: http://localhost:3333
default: true
- name: vps-heavy
url: http://build-server.example.com:3333
auth_token: "xxxxx"
- name: team-shared
url: http://team-opencode.internal:3333
auth_token: "yyyyy"Implementation Phases
Phase 0: Discovery (1-2 days)
- Run
opencode acpand document endpoints - Test with curl / wscat
- Understand WebSocket event format
- Check if
opencode webincludes ACP - Document authentication requirements
Phase 1: Minimal Client (3-5 days)
- Build TypeScript ACP client library
- Implement: create session, send message, get status
- Test against
opencode acpinstance - Handle basic errors (connection refused, timeout)
Phase 2: OpenClaw Tool (5-7 days)
- Create
opencodetool for OpenClaw - Wire into tool registry
- Implement tool actions: create, list, attach, status
- Add connection state management
- Test via OpenClaw agent (WhatsApp → OpenClaw → ACP)
Phase 3: Streaming & UX (3-5 days)
- WebSocket subscription to agent events
- Format events for messaging surfaces
- Progress notifications (file changes, commands, etc.)
- Completion notifications with summary
Phase 4: Persistence & Resilience (3-5 days)
- State file for instances + sessions
- Auto-reconnect on connection loss
- Session recovery after Gateway restart
- Graceful degradation (ACP → CLI fallback)
Phase 5: Multi-Instance & Permissions (5-7 days)
- Support multiple ACP servers
- Instance selection (default, by name, by project)
- Permission approval flow (bidirectional)
- Advanced features: export, stats, etc.
Total estimate: 3-4 weeks for production-ready integration
Testing Strategy
Unit Tests
- ACP client library (HTTP calls, WebSocket handling)
- Connection manager (instance registry, health checks)
- Error handling (retries, backoff, fallback)
Integration Tests
- Spin up mock ACP server
- OpenClaw tool calls → mock server responses
- Verify state persistence (restart Gateway, sessions survive)
End-to-End Tests
- Real
opencode acpinstance - OpenClaw agent receives WhatsApp message
- Session created, message sent, status polled
- Verify user receives formatted updates
Load Tests
- 10 concurrent sessions
- Measure: latency, memory usage, error rate
- Confirm: no session crosstalk, clean teardown
Open Technical Questions
- ACP Endpoints: What are the exact HTTP routes? (Needs documentation or code inspection)
- WebSocket Protocol: What events does OpenCode stream? Format?
- Session Lifecycle: How does OpenCode handle pause/resume? Persist across restarts?
- Multi-Client: Can multiple clients attach to same session? (For team features)
- Model Selection: Can you specify LLM model per-session via ACP?
- Rate Limiting: Does ACP enforce rate limits? How does it handle overload?
- opencode web: Does it expose ACP endpoints, or is it a separate web-only mode?
Next step: Run discovery phase to answer these questions.
References
- OpenCode CLI help:
opencode --help,opencode acp --help<<<<<<< HEAD - Existing bridge:
/workspace/projects/ai-dev/plugins/opencode-bridge/======= - Existing bridge:
projects/ai-dev/plugins/opencode-bridge/(if it exists)
f146a548fbab991798ce91719b88cbf1f064588a
- OpenClaw tool patterns:
browsertool (persistent connections),exectool (process management)