OpenClaw-OpenCode Bridge - Next Steps
Phase 0: ✅ COMPLETE (2026-02-16)
Phase 1: Ready to start
Phase 0 Summary
What We Accomplished
✅ HTTP REST API fully documented:
/project- List all workspaces/session- Create/list sessions (workspace-specific!)/session/:id/message- Send/retrieve messages/global/event- SSE event stream- End-to-end tested with real orchestrator session
✅ Critical Discovery - Workspace Isolation:
- OpenCode web server serves only the workspace where it was started
- To access different workspaces, must run multiple servers or restart in new directory
- For OpenClaw: Need multiple OpenCode instances (one per builder workspace)
✅ Architecture Decision:
- Use HTTP REST API (not ACP/JSON-RPC)
- Server-Sent Events for real-time updates
- Multiple OpenCode servers for multi-workspace support
Documentation Created
opencode-protocol-discovered.md- Complete HTTP API referencephase-0-findings.md- Research journey and key discoveriesphase-0-research.md- Original research plan (archived)
Phase 1: Core Pattern Implementation
Goal: Build TypeScript/Node.js client for OpenCode HTTP API + Gateway integration
Tasks
1. OpenCode HTTP Client Library
Location: packages/openclaw-gateway/src/opencode/ (or new package)
Core Components:
// src/opencode/client.ts
class OpenCodeClient {
constructor(baseURL: string) // e.g., "http://localhost:5400"
// Project management
async listProjects(): Promise<Project[]>
// Session management
async createSession(opts: CreateSessionOpts): Promise<Session>
async listSessions(): Promise<Session[]>
async getSession(id: string): Promise<Session>
// Messaging
async sendMessage(sessionId: string, message: Message): Promise<void>
async getMessages(sessionId: string): Promise<Message[]>
// Event streaming
streamEvents(callback: (event: OpenCodeEvent) => void): EventSource
// Health
async healthCheck(): Promise<HealthStatus>
}Types:
interface Project {
id: string
worktree: string
vcs?: 'git'
time: { created: number; updated: number }
}
interface Session {
id: string
slug: string
version: string
projectID: string
directory: string
title: string
time: { created: number; updated: number }
}
interface Message {
info: {
id: string
sessionID: string
role: 'user' | 'assistant'
time: { created: number; updated: number }
}
parts: MessagePart[]
}
interface MessagePart {
type: 'text' | 'tool_use' | 'tool_result'
text?: string
// ... other fields
}
interface OpenCodeEvent {
directory?: string
payload: {
type: string // 'message.part.updated', 'file.edited', etc.
properties: Record<string, any>
}
}Testing:
- Unit tests with mock HTTP responses
- Integration tests against real OpenCode server
- Test workspace isolation behavior
2. Multi-Workspace Server Management
Problem: Each builder needs its own OpenCode server instance.
Solution: OpenCode Server Manager
// src/opencode/server-manager.ts
class OpenCodeServerManager {
private servers: Map<string, OpenCodeServerInstance>
async startServer(workspace: string): Promise<OpenCodeClient>
async stopServer(workspace: string): Promise<void>
getClient(workspace: string): OpenCodeClient | undefined
async healthCheckAll(): Promise<Map<string, boolean>>
}
interface OpenCodeServerInstance {
workspace: string
port: number
process: ChildProcess
client: OpenCodeClient
startTime: number
}Port Allocation Strategy:
- Base port: 5400
- Per-builder offset: 5400 + builder_index
- Example:
- Builder 1 (
/home/coder/src/.builders/builder-0001): port 5400 - Builder 2 (
/home/coder/src/.builders/builder-0002): port 5401 - Builder 3 (
/home/coder/src/.builders/builder-0003): port 5402
- Builder 1 (
Startup Process:
cd /home/coder/src/.builders/builder-0001
opencode web --hostname 0.0.0.0 --port 5400 &Lifecycle Management:
- Start OpenCode when builder is created
- Stop OpenCode when builder is destroyed
- Health check every 30 seconds
- Auto-restart on failure
3. Gateway Integration
Update Gateway to use OpenCode client:
// packages/openclaw-gateway/src/handlers/discord-dm.ts
import { OpenCodeServerManager } from '../opencode/server-manager'
class DiscordDMHandler {
private serverManager: OpenCodeServerManager
async handleMessage(message: DiscordMessage) {
// 1. Determine which builder/workspace to use
const workspace = await this.resolveWorkspace(message)
// 2. Get or start OpenCode server for that workspace
const client = await this.serverManager.startServer(workspace)
// 3. Get or create session
const session = await this.getOrCreateSession(client, message.author)
// 4. Send message to OpenCode
await client.sendMessage(session.id, {
parts: [{ type: 'text', text: message.content }]
})
// 5. Stream events back to Discord (via event filter)
// ... (see next section)
}
}4. Event Filtering (Hardcoded Rules)
Goal: Filter OpenCode SSE events → send relevant updates to Discord DM
No AI processing - just hardcoded rules based on event types.
// src/opencode/event-filter.ts
class OpenCodeEventFilter {
shouldForward(event: OpenCodeEvent): boolean {
const { type } = event.payload
// Forward these events to user
const forwardTypes = [
'message.part.updated', // Claude's response chunks
'file.edited', // File changes
'command.executed', // Commands run
'session.diff', // Session diffs
]
return forwardTypes.includes(type)
}
formatForDiscord(event: OpenCodeEvent): string {
// Convert OpenCode event to Discord message format
// ...
}
}Event Flow:
OpenCode SSE Event
↓
Event Filter (hardcoded rules)
↓ (if shouldForward)
Format for Discord
↓
Send to Discord DM
5. Testing Plan
Unit Tests:
- OpenCodeClient methods
- ServerManager lifecycle
- Event filter rules
Integration Tests:
- Start OpenCode server programmatically
- Create session via API
- Send message via API
- Receive response
- Stream events via SSE
- Stop server cleanly
End-to-End Test:
- User sends Discord DM → “Create a new file hello.txt”
- Gateway receives message
- Gateway starts/uses OpenCode for appropriate workspace
- Gateway sends message to OpenCode session
- OpenCode processes with Claude
- Gateway receives SSE events (file created, command executed)
- Gateway filters events
- Gateway sends updates back to Discord DM
- User sees “Created hello.txt” in Discord
6. Integration with Orchestrator Pattern
Current Architecture:
Discord DM → Personal Assistant → Orchestrator → Builder Sessions
With OpenCode Integration:
Discord DM
↓
Personal Assistant (OpenCode session in codev)
↓
Orchestrator (OpenCode session in codev)
↓
Builder Sessions (OpenCode sessions in .builders/builder-NNNN)
Key Insight: All tiers use OpenCode, but in different workspaces!
Workspace Mapping:
- Personal Assistant:
/home/coder(global project) - Orchestrator:
/home/coder/src(main workspace) - Builder N:
/home/coder/src/.builders/builder-000N(builder workspace)
Implementation:
interface OpenClawTier {
name: 'personal-assistant' | 'orchestrator' | 'builder'
workspace: string
opencode: OpenCodeClient
session: Session
}
class OpenClawArchitecture {
private tiers: Map<string, OpenClawTier>
private serverManager: OpenCodeServerManager
async initializeTier(tier: 'personal-assistant' | 'orchestrator' | 'builder', workspace: string) {
const client = await this.serverManager.startServer(workspace)
const session = await client.createSession({
directory: workspace,
title: `OpenClaw ${tier}`,
agent: 'default'
})
this.tiers.set(tier, { name: tier, workspace, opencode: client, session })
}
async sendToTier(tier: string, message: string) {
const tierData = this.tiers.get(tier)
await tierData.opencode.sendMessage(tierData.session.id, {
parts: [{ type: 'text', text: message }]
})
}
}Phase 1 Deliverables
- ✅
OpenCodeClientclass (TypeScript) - ✅
OpenCodeServerManagerclass (TypeScript) - ✅
OpenCodeEventFilterclass (TypeScript) - ✅ Unit tests for all components
- ✅ Integration tests with real OpenCode server
- ✅ Gateway integration (Discord DM → OpenCode)
- ✅ End-to-end test with builder workspace
- ✅ Documentation updates
Phase 1 Timeline
Estimated Duration: 2-3 days
Breakdown:
- Day 1: OpenCodeClient + ServerManager implementation
- Day 2: Event filtering + Gateway integration
- Day 3: Testing + documentation
Success Criteria
Phase 1 is complete when:
- ✅ Can start OpenCode server programmatically for any workspace
- ✅ Can create sessions via HTTP API
- ✅ Can send messages and receive responses
- ✅ SSE event stream works and filters correctly
- ✅ Discord DM → OpenCode → Discord DM works end-to-end
- ✅ Multi-workspace support validated (personal assistant + orchestrator + builder)
- ✅ All tests passing
Next Phase Preview
Phase 2: Advanced Features
- Session persistence across restarts
- Builder lifecycle integration with Taskfile
- Project list integration (
.ai/projectlist.md) - Advanced event filtering (tool use detection)
- Error handling & retry logic
- Metrics & monitoring
Phase 3: Production Readiness
- Security (OPENCODE_SERVER_PASSWORD)
- Resource limits (max concurrent sessions)
- Graceful shutdown
- Logging & debugging
- Performance optimization
References
- Phase 0 Docs:
opencode-protocol-discovered.md,phase-0-findings.md - Architecture:
.ai/projects/ai-dev/opencode-session-pattern.md - OpenCode HTTP API:
http://localhost:5400(when running) - Orchestrator Session:
ses_3ae187259ffeMdIpeZbzA5R4ZU