# Agent Provider Adapter — Task Plan ## Goal Multi-provider agent support (Claude Code, Codex CLI, Ollama) via adapter pattern. Claude Code remains primary and fully functional. Zero regression. ## Architecture Decisions | # | Date | Decision | Rationale | |---|------|----------|-----------| | PA-1 | 2026-03-11 | Per-provider sidecar binaries (not single multi-SDK bundle) | Independent testing, no bloat, clean separation. SidecarCommand already abstracts binary path. | | PA-2 | 2026-03-11 | Generic provider_config blob in AgentQueryOptions (not discriminated union) | Rust passes through without parsing. TypeScript uses discriminated unions for compile-time safety. Minimal Rust changes. | | PA-3 | 2026-03-11 | Per-provider message adapter files → common AgentMessage type | sdk-messages.ts becomes claude-messages.ts. Registry selects parser by provider. Store/UI unchanged. | | PA-4 | 2026-03-11 | Provider selection per-project with global default | ProjectConfig.provider field (default: 'claude'). Matches real workflow. | | PA-5 | 2026-03-11 | Capability flags drive UI rendering (not provider ID checks) | ProviderCapabilities interface. AgentPane checks hasProfiles/hasSkills/etc. No hardcoded if(provider==='claude'). | | PA-6 | 2026-03-11 | Providers section in SettingsTab scroll (not inner tabs) | Current sections aren't long enough for tabs. Collapsible per-provider config panels. | ## Phases ### Phase 1: Core Abstraction Layer (no functional change) **Goal:** Insert abstraction boundary. Claude remains the only registered provider. Zero user-visible change. | # | Task | Files | Status | |---|------|-------|--------| | 1.1 | Create provider types | NEW: `src/lib/providers/types.ts` | done | | 1.2 | Create provider registry | NEW: `src/lib/providers/registry.svelte.ts` | done | | 1.3 | Create Claude provider meta | NEW: `src/lib/providers/claude.ts` | done | | 1.4 | Rename sdk-messages.ts → claude-messages.ts | RENAME + update imports | done | | 1.5 | Create message adapter registry | NEW: `src/lib/adapters/message-adapters.ts` | done | | 1.6 | Update Rust AgentQueryOptions | MOD: `bterminal-core/src/sidecar.rs` | done | | 1.7 | Update agent-bridge.ts options shape | MOD: `src/lib/adapters/agent-bridge.ts` | done | | 1.8 | Rename agent-runner.ts → claude-runner.ts | RENAME + update build script | done | | 1.9 | Add provider field to ProjectConfig | MOD: `src/lib/types/groups.ts` | done | | 1.10 | Rename ClaudeSession.svelte → AgentSession.svelte | RENAME + update imports | done | | 1.11 | Update agent-dispatcher provider routing | MOD: `src/lib/agent-dispatcher.ts` | done | | 1.12 | Update AgentPane for capability-driven rendering | MOD: `src/lib/components/Agent/AgentPane.svelte` | done | | 1.13 | Rename claude-bridge.ts → provider-bridge.ts | RENAME + genericize | done | | 1.14 | Update Rust lib.rs commands | MOD: `src-tauri/src/lib.rs` | done | | 1.15 | Update all tests | MOD: test files | done | | 1.16 | Verify: 202 vitest + 42 cargo tests pass | — | done | ### Phase 2: Settings UI | # | Task | Files | Status | |---|------|-------|--------| | 2.1 | Add Providers section to SettingsTab | MOD: `SettingsTab.svelte` | done | | 2.2 | Per-provider collapsible config panels | MOD: `SettingsTab.svelte` | done | | 2.3 | Per-project provider dropdown | MOD: `SettingsTab.svelte` | done | | 2.4 | Persist provider settings | MOD: `settings-bridge.ts` | done | | 2.5 | Provider-aware AgentPane | MOD: `AgentPane.svelte` | done | ### Phase 3: Sidecar Routing | # | Task | Files | Status | |---|------|-------|--------| | 3.1 | SidecarManager provider-based runner selection | MOD: `bterminal-core/src/sidecar.rs` | done | | 3.2 | Per-provider runner discovery | MOD: `bterminal-core/src/sidecar.rs` | done | | 3.3 | Provider-specific env var stripping | MOD: `bterminal-core/src/sidecar.rs` | done | ## Type System ### ProviderQueryOptions (TypeScript → Rust → Sidecar) ``` Frontend (typed): AgentQueryOptions { provider: ProviderId // 'claude' | 'codex' | 'ollama' session_id: string prompt: string model?: string max_turns?: number provider_config: Record // provider-specific } ↓ (Tauri invoke) Rust (generic): AgentQueryOptions { provider: String session_id: String prompt: String model: Option max_turns: Option provider_config: serde_json::Value } ↓ (stdin NDJSON) Sidecar (provider-specific): claude-runner.ts parses provider_config as ClaudeProviderConfig codex-runner.ts parses provider_config as CodexProviderConfig ollama-runner.ts parses provider_config as OllamaProviderConfig ``` ### Message Flow (Sidecar → Frontend) ``` Sidecar stdout (NDJSON, provider-specific format) ↓ Rust SidecarManager (pass-through, adds sessionId) ↓ agent-dispatcher.ts → message-adapters.ts registry → claude-messages.ts (if provider=claude) → codex-messages.ts (if provider=codex, future) → ollama-messages.ts (if provider=ollama, future) → AgentMessage (common type) ↓ agents.svelte.ts store (unchanged) ↓ AgentPane.svelte (renders AgentMessage, capability-driven) ``` ## File Inventory ### New Files (Phase 1) - `v2/src/lib/providers/types.ts` - `v2/src/lib/providers/registry.svelte.ts` - `v2/src/lib/providers/claude.ts` - `v2/src/lib/adapters/message-adapters.ts` ### Renamed Files (Phase 1) - `sdk-messages.ts` → `claude-messages.ts` - `agent-runner.ts` → `claude-runner.ts` - `ClaudeSession.svelte` → `AgentSession.svelte` - `claude-bridge.ts` → `provider-bridge.ts` (genericized) ### Modified Files (Phase 1) - `bterminal-core/src/sidecar.rs` — AgentQueryOptions struct - `src-tauri/src/lib.rs` — command handlers - `src/lib/adapters/agent-bridge.ts` — options interface - `src/lib/agent-dispatcher.ts` — provider routing - `src/lib/components/Agent/AgentPane.svelte` — capability checks - `src/lib/components/Workspace/ProjectBox.svelte` — import rename - `src/lib/types/groups.ts` — ProjectConfig.provider field - `package.json` — build:sidecar script path - Test files — import path updates