Implement BackendAdapter interface with four CLI backends: - ClaudeCodeBackend (extracted from AgentRuntime) - CodexBackend (OpenAI Codex CLI) - GeminiBackend (Google Gemini CLI) - OpenCodeBackend (OpenCode CLI) Add BackendRegistry for resolution/creation via AGENT_BACKEND env var. Refactor AgentRuntime to delegate to BackendAdapter instead of hardcoding Claude CLI. Update GatewayConfig with new env vars (AGENT_BACKEND, BACKEND_CLI_PATH, BACKEND_MODEL, BACKEND_MAX_TURNS). Includes 10 property-based test files and unit tests for edge cases.
6.0 KiB
6.0 KiB
Tasks
Task 1: Create BackendAdapter interface and shared types
- 1.1 Create
src/backends/types.tswithBackendAdapterinterface,BackendAdapterConfig,BackendEventResult,StreamCallback, andBackendNametype - 1.2 Export all types from
src/backends/index.tsbarrel file
Task 2: Implement ClaudeCodeBackend
- 2.1 Create
src/backends/claude-backend.tsimplementingBackendAdapter - 2.2 Extract CLI spawning logic from
AgentRuntime.runClaude()intoexecute()method with arg building for-p,--output-format json,--dangerously-skip-permissions,--append-system-prompt-file,--allowedTools,--max-turns, and--resume - 2.3 Implement
validate()to check CLI binary is executable - 2.4 Implement JSON array output parser extracting
session_idfromsystem/initandresultfromresultobjects - 2.5 Write property test: Claude backend required flags (Property 1)
- 🧪 PBT: For any prompt, system prompt, and tools list, generated args contain all required flags
Task 3: Implement CodexBackend
- 3.1 Create
src/backends/codex-backend.tsimplementingBackendAdapter - 3.2 Implement
execute()withcodex execsubcommand,--json,--dangerously-bypass-approvals-and-sandbox,--cd, andcodex exec resume <id>for sessions - 3.3 Implement newline-delimited JSON parser extracting final assistant message
- 3.4 Write property test: Codex backend required flags (Property 2)
- 🧪 PBT: For any prompt and working directory, generated args contain exec, --json, --dangerously-bypass-approvals-and-sandbox, and --cd
Task 4: Implement GeminiBackend
- 4.1 Create
src/backends/gemini-backend.tsimplementingBackendAdapter - 4.2 Implement
execute()with prompt as positional arg,--output-format json,--approval-mode yolo, and--resumefor sessions - 4.3 Implement JSON output parser extracting response text
- 4.4 Write property test: Gemini backend required flags (Property 3)
- 🧪 PBT: For any prompt, generated args contain the prompt positionally, --output-format json, and --approval-mode yolo
Task 5: Implement OpenCodeBackend
- 5.1 Create
src/backends/opencode-backend.tsimplementingBackendAdapter - 5.2 Implement
execute()withopencode runsubcommand,--format json,--model, and--session <id> --continuefor sessions - 5.3 Implement JSON event parser extracting final response text
- 5.4 Write property test: OpenCode backend required flags (Property 4)
- 🧪 PBT: For any prompt and optional model, generated args contain run, --format json, and --model when configured
Task 6: Implement BackendRegistry
- 6.1 Create
src/backends/registry.tswithresolveBackendName()andcreateBackend()functions - 6.2
resolveBackendNameaccepts "claude", "codex", "gemini", "opencode", defaults to "claude" for undefined, throws for invalid values - 6.3
createBackendinstantiates the correct backend implementation from aBackendName - 6.4 Write property test: Backend name resolution (Property 7)
- 🧪 PBT: For any string, resolveBackendName returns correct BackendName for valid values, "claude" for undefined, and throws for invalid
Task 7: Cross-backend property tests
- 7.1 Write property test: Session resume args across backends (Property 5)
- 🧪 PBT: For any backend and session ID, session flags appear when ID is provided and are absent when not
- 7.2 Write property test: Output parsing extracts correct fields (Property 6)
- 🧪 PBT: For any valid backend-specific JSON output, parser produces BackendEventResult with correct responseText and sessionId
- 7.3 Write property test: Non-zero exit code produces error result (Property 8)
- 🧪 PBT: For any backend, non-zero exit code, and stderr string, result has isError=true and responseText contains stderr
Task 8: Update GatewayConfig
- 8.1 Add
agentBackend,backendCliPath,backendModel,backendMaxTurnsfields toGatewayConfiginterface insrc/config.ts - 8.2 Update
loadConfig()to readAGENT_BACKEND,BACKEND_CLI_PATH,BACKEND_MODEL,BACKEND_MAX_TURNSenv vars with defaults - 8.3 Deprecate
claudeCliPathfield (keep for backward compat, map tobackendCliPathwhenAGENT_BACKEND=claude)
Task 9: Refactor AgentRuntime
- 9.1 Add
BackendAdapterparameter toAgentRuntimeconstructor - 9.2 Replace
executeClaude()andrunClaude()with calls tothis.backend.execute() - 9.3 Implement
BackendEventResult→ gatewayEventResultmapping in a helper method - 9.4 Remove
ClaudeJsonResponseinterface and Claude-specific parsing fromAgentRuntime - 9.5 Write property test: EventResult mapping preserves semantics (Property 9)
- 🧪 PBT: For any BackendEventResult and channel ID, mapping sets error or responseText correctly based on isError
- 9.6 Write property test: Session ID storage after backend execution (Property 10)
- 🧪 PBT: For any channel ID and BackendEventResult with sessionId, SessionManager contains that sessionId after processing
Task 10: Startup validation and wiring
- 10.1 Update main entry point to call
resolveBackendName()andcreateBackend()from config - 10.2 Call
backend.validate()at startup; log error with backend name and path, exit(1) on failure - 10.3 Inject the
BackendAdapterinstance intoAgentRuntimeconstructor - 10.4 Write unit tests for startup validation flow (valid backend, invalid backend name, missing CLI binary)
Task 11: Unit tests for edge cases
- 11.1 Write unit tests for each backend's
validate()method (binary exists vs missing) - 11.2 Write unit tests for timeout behavior (process killed after queryTimeoutMs)
- 11.3 Write unit tests for session corruption detection and cleanup
- 11.4 Write unit tests for default config values when env vars are unset
- 11.5 Write unit tests for unsupported option warning (e.g., ALLOWED_TOOLS on backends without tool filtering)