II.
Page JSON
Structured · livepage:docs-agent-mux-archive-design-18-multi-adapter-architecture
Multi-Adapter Architecture Design json
Inspect the normalized record payload exactly as the atlas UI reads it.
{
"id": "page:docs-agent-mux-archive-design-18-multi-adapter-architecture",
"_kind": "Page",
"_file": "wiki/docs/agent-mux/archive/design/18-multi-adapter-architecture.md",
"_cluster": "wiki",
"attributes": {
"nodeKind": "Page",
"sourcePath": "docs/agent-mux/archive/design/18-multi-adapter-architecture.md",
"sourceKind": "repo-docs",
"title": "Multi-Adapter Architecture Design",
"displayName": "Multi-Adapter Architecture Design",
"slug": "docs/agent-mux/archive/design/18-multi-adapter-architecture",
"articlePath": "wiki/docs/agent-mux/archive/design/18-multi-adapter-architecture.md",
"article": "\n# Multi-Adapter Architecture Design\n\n> Archived design document. Preserved for historical context; not part of the current normative `reference/` contract.\n\n## Overview\n\nExtend agent-mux to support multiple adapter types beyond subprocess-based execution. This enables integration with sophisticated tools that provide HTTP APIs, WebSocket interfaces, or direct SDK access.\n\n## Current Architecture (Subprocess-Only)\n\n```typescript\ninterface AgentAdapter {\n buildSpawnArgs(options: RunOptions): SpawnArgs;\n parseEvent(line: string, context: ParseContext): AgentEvent | AgentEvent[] | null;\n // ... session, auth, config methods\n}\n```\n\n**Limitations:**\n- Forces all tools into subprocess model\n- Cannot leverage HTTP APIs, WebSocket streaming, or native SDKs\n- Suboptimal for TUI-first tools (OpenCode), server-based tools (Codex app-server)\n\n## Proposed Architecture\n\n### 1. Adapter Type Hierarchy\n\n```typescript\n// Base interface (shared across all adapter types)\ninterface BaseAgentAdapter {\n // Identity\n readonly agent: AgentName;\n readonly displayName: string;\n readonly adapterType: 'subprocess' | 'remote' | 'programmatic';\n \n // Capabilities (unchanged)\n readonly capabilities: AgentCapabilities;\n readonly models: ModelCapabilities[];\n \n // Common functionality\n detectAuth(): Promise<AuthState>;\n getAuthGuidance(): AuthSetupGuidance;\n sessionDir(cwd?: string): string;\n parseSessionFile(filePath: string): Promise<Session>;\n // ... other common methods\n}\n\n// Subprocess adapters (current model)\ninterface SubprocessAdapter extends BaseAgentAdapter {\n readonly adapterType: 'subprocess';\n readonly cliCommand: string;\n buildSpawnArgs(options: RunOptions): SpawnArgs;\n parseEvent(line: string, context: ParseContext): AgentEvent | AgentEvent[] | null;\n}\n\n// Remote adapters (HTTP, WebSocket, Unix sockets)\ninterface RemoteAdapter extends BaseAgentAdapter {\n readonly adapterType: 'remote';\n readonly connectionType: 'http' | 'websocket' | 'unix';\n \n connect(options: RunOptions): Promise<RemoteConnection>;\n disconnect(connection: RemoteConnection): Promise<void>;\n \n // Optional: start/stop server if adapter manages it\n startServer?(): Promise<ServerInfo>;\n stopServer?(serverInfo: ServerInfo): Promise<void>;\n}\n\n// Programmatic adapters (direct SDK integration)\ninterface ProgrammaticAdapter extends BaseAgentAdapter {\n readonly adapterType: 'programmatic';\n \n execute(options: RunOptions): AsyncIterableIterator<AgentEvent>;\n}\n```\n\n### 2. Connection Abstractions\n\n```typescript\n// Remote connection interface\ninterface RemoteConnection {\n readonly connectionId: string;\n readonly connectionType: 'http' | 'websocket' | 'unix';\n \n send(data: unknown): Promise<void>;\n receive(): AsyncIterableIterator<AgentEvent>;\n close(): Promise<void>;\n}\n\n// HTTP-specific connection\ninterface HttpConnection extends RemoteConnection {\n readonly connectionType: 'http';\n readonly baseUrl: string;\n \n get(path: string, params?: Record<string, unknown>): Promise<unknown>;\n post(path: string, data?: unknown): Promise<unknown>;\n stream(path: string, data?: unknown): AsyncIterableIterator<AgentEvent>;\n}\n\n// WebSocket-specific connection \ninterface WebSocketConnection extends RemoteConnection {\n readonly connectionType: 'websocket';\n readonly websocketUrl: string;\n \n subscribe(channel: string): AsyncIterableIterator<AgentEvent>;\n unsubscribe(channel: string): Promise<void>;\n}\n```\n\n### 3. Server Management\n\n```typescript\ninterface ServerInfo {\n readonly serverId: string;\n readonly serverType: string;\n readonly endpoint: string;\n readonly pid?: number;\n readonly health?: 'starting' | 'healthy' | 'unhealthy';\n}\n\ninterface ServerManager {\n start(adapter: RemoteAdapter, options?: ServerOptions): Promise<ServerInfo>;\n stop(serverId: string): Promise<void>;\n health(serverId: string): Promise<ServerInfo>;\n list(): Promise<ServerInfo[]>;\n}\n```\n\n## Implementation Plan\n\n### Phase 1: Core Architecture\n\n1. **Create new interface hierarchy** in `packages/core/src/adapter-types.ts`\n2. **Extend AgentAdapter** to be union type: `SubprocessAdapter | RemoteAdapter | ProgrammaticAdapter`\n3. **Update BaseAgentAdapter** in adapters package to implement `SubprocessAdapter`\n4. **Maintain backward compatibility** - all existing adapters continue working\n\n### Phase 2: Execution Engine Updates\n\n1. **Update RunHandleImpl** to route based on `adapter.adapterType`\n2. **Create RemoteRunner** for HTTP/WebSocket execution\n3. **Create ProgrammaticRunner** for direct SDK execution \n4. **Add ServerManager** for lifecycle management\n5. **Update event streaming** to handle different execution models\n\n### Phase 3: New Adapter Implementations\n\n1. **opencode-http**: HTTP server + REST API + SSE streaming\n2. **codex-sdk**: Direct SDK integration\n3. **codex-websocket**: WebSocket app-server integration\n4. **claude-agent-sdk**: Programmatic Claude interface\n5. **pi-sdk**: Enhanced programmatic Pi interface\n\n### Phase 4: Mock Infrastructure\n\n1. **MockHttpServer**: Simulate HTTP endpoints + SSE\n2. **MockWebSocketServer**: Simulate real-time connections\n3. **MockSDK**: Simulate direct SDK calls\n4. **Enhanced scenarios**: Support all adapter types\n\n## Adapter Naming Convention\n\n**Pattern**: `{tool}-{type}` where type indicates the integration method:\n\n- `opencode` (subprocess, default)\n- `opencode-http` (HTTP server)\n- `codex` (subprocess, current)\n- `codex-sdk` (programmatic SDK)\n- `codex-websocket` (WebSocket app-server)\n- `claude-agent-sdk` (programmatic)\n\n## Example Adapter Implementations\n\n### HTTP Adapter (OpenCode)\n\n```typescript\nclass OpenCodeHttpAdapter implements RemoteAdapter {\n readonly adapterType = 'remote';\n readonly connectionType = 'http';\n readonly agent = 'opencode-http';\n \n async connect(options: RunOptions): Promise<HttpConnection> {\n // Start 'opencode serve' if needed\n const serverInfo = await this.ensureServer();\n \n // Create HTTP connection\n return new OpenCodeHttpConnection({\n baseUrl: serverInfo.endpoint,\n sessionId: options.sessionId,\n model: options.model,\n });\n }\n \n private async ensureServer(): Promise<ServerInfo> {\n // Check if server already running\n // If not, start via 'opencode serve --port 0'\n // Return connection details\n }\n}\n\nclass OpenCodeHttpConnection implements HttpConnection {\n async *stream(path: string, data: unknown): AsyncIterableIterator<AgentEvent> {\n // POST to /api/chat/stream with SSE\n const response = await fetch(`${this.baseUrl}${path}`, {\n method: 'POST',\n body: JSON.stringify(data),\n headers: { 'Accept': 'text/event-stream' }\n });\n \n for await (const chunk of response.body) {\n yield this.parseServerSentEvent(chunk);\n }\n }\n}\n```\n\n### SDK Adapter (Codex)\n\n```typescript\nclass CodexSdkAdapter implements ProgrammaticAdapter {\n readonly adapterType = 'programmatic';\n readonly agent = 'codex-sdk';\n \n async *execute(options: RunOptions): AsyncIterableIterator<AgentEvent> {\n const sdk = new CodexSDK({\n apiKey: process.env.OPENAI_API_KEY,\n model: options.model || this.defaultModelId,\n });\n \n const stream = await sdk.chat.completions.create({\n messages: [{ role: 'user', content: options.prompt }],\n stream: true,\n });\n \n for await (const chunk of stream) {\n yield this.parseCodexChunk(chunk);\n }\n }\n}\n```\n\n## Migration Strategy\n\n1. **Backward Compatible**: All existing adapters continue working unchanged\n2. **Gradual Adoption**: Add new adapter types without breaking existing functionality \n3. **Clear Documentation**: Document when to use each adapter type\n4. **Mock Support**: Ensure all adapter types have full mock coverage\n\n## Benefits\n\n1. **Native Performance**: Direct SDK integration eliminates subprocess overhead\n2. **Real-time Streaming**: WebSocket connections enable bidirectional communication\n3. **Full Capabilities**: HTTP APIs provide access to complete tool feature sets\n4. **Flexible Integration**: Choose the best integration method per tool\n5. **Future-Proof**: Support emerging tools with non-CLI interfaces\n\n## Claude-Specific Transport Note\n\nClaude now spans multiple distinct integration surfaces that should not be collapsed into a single \"server mode\" mental model:\n\n- `claude` CLI can provide a **persistent structured subprocess transport** through `--print --input-format stream-json --output-format stream-json`, with stdin carrying later user turns and stdout carrying structured events.\n- `claude-agent-sdk` is a **programmatic persistent transport** with direct callback integration.\n- `claude-remote-control` is the **server-managed Claude surface for Claude.ai / Claude app clients**. Agent-mux can launch and observe the bridge honestly, but it does not advertise local stdin-driven chat semantics for it.\n- Claude channels are **MCP-mediated push/reply integrations into a running Claude host session**, not a standalone replacement for the CLI or SDK transports.\n\nAgent-mux should model these surfaces honestly and only advertise the transport semantics each one actually supports.\n\n## Trade-offs\n\n1. **Complexity**: Multiple execution paths increase code complexity\n2. **Resource Management**: HTTP servers and connections need lifecycle management\n3. **Testing**: More sophisticated mocking required\n4. **Dependencies**: Programmatic adapters may require additional npm dependencies\n\n## Files to Create/Modify\n\n### New Files\n- `packages/core/src/adapter-types.ts` - New interface hierarchy\n- `packages/core/src/remote-runner.ts` - HTTP/WebSocket execution\n- `packages/core/src/programmatic-runner.ts` - SDK execution \n- `packages/core/src/server-manager.ts` - Server lifecycle management\n- `packages/adapters/src/remote-adapter-base.ts` - Base class for remote adapters\n- `packages/adapters/src/programmatic-adapter-base.ts` - Base class for SDK adapters\n\n### Modified Files\n- `packages/core/src/adapter.ts` - Update AgentAdapter union type\n- `packages/core/src/run-handle-impl.ts` - Route by adapter type\n- `packages/adapters/src/base-adapter.ts` - Implement SubprocessAdapter\n- `packages/harness-mock/src/index.ts` - Add mock infrastructure\n\nThis architecture enables agent-mux to evolve beyond subprocess-only integration while maintaining full backward compatibility and providing a clear path for supporting sophisticated AI tools with diverse integration requirements.\n",
"documents": []
},
"outgoingEdges": [],
"incomingEdges": [
{
"from": "page:docs-agent-mux-archive-design",
"to": "page:docs-agent-mux-archive-design-18-multi-adapter-architecture",
"kind": "contains_page"
}
]
}