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: agor-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: agor-core/src/sidecar.rs |
done |
| 3.2 |
Per-provider runner discovery |
MOD: agor-core/src/sidecar.rs |
done |
| 3.3 |
Provider-specific env var stripping |
MOD: agor-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<string, unknown> // provider-specific
}
↓ (Tauri invoke)
Rust (generic):
AgentQueryOptions {
provider: String
session_id: String
prompt: String
model: Option<String>
max_turns: Option<u32>
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)
src/lib/providers/types.ts
src/lib/providers/registry.svelte.ts
src/lib/providers/claude.ts
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)
agor-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