- Introduced multiple new markdown files detailing the Agent Client Protocol (ACP), AI agent orchestration landscape, and various tools for managing multi-agent systems. - Included in-depth analysis of protocol standards, governance structures, and emerging frameworks relevant to AI agent integration. - Documented key features, architecture, and integration potential of various desktop and CLI orchestrators, enhancing understanding of the current ecosystem. - Provided insights into best practices for integrating multi-provider agent support within the Electron framework. This documentation aims to serve as a foundational resource for developers and stakeholders involved in AI agent orchestration and integration.
29 KiB
Claude Coupling Analysis
Comprehensive analysis of how tightly the Claude Agent Teams UI codebase is coupled to Claude/Claude Code/Claude Agent Teams. The goal is to understand the effort required to abstract the AI provider layer to support other agents (OpenAI Codex, Gemini CLI, etc.).
Date: 2026-03-24
Branch: dev
Commit: 08be859
Summary Table
| Area | Coupling (1-10) | Effort | Key Blockers |
|---|---|---|---|
| 1. Process Management | 9 | High | Binary name, CLI flags, kill semantics |
| 2. Protocol / Communication | 10 | High | stream-json is Claude-proprietary |
| 3. Message Parsing (JSONL) | 9 | High | Schema is Claude Code's internal format |
| 4. Team Management | 10 | Very High | Agent Teams is a Claude Code feature |
| 5. Session Data / Paths | 9 | Medium | ~/.claude/ hardcoded everywhere |
| 6. Authentication | 8 | Medium | claude auth status, GCS binary dist |
| 7. MCP Integration | 5 | Low | MCP is a cross-vendor standard |
| 8. UI Components | 6 | Medium | Branding strings, CLAUDE.md references |
| 9. Types / Interfaces | 8 | High | Types mirror Claude Code JSONL schema |
| 10. Configuration | 7 | Medium | Path constants, env vars, config files |
| Pricing / Cost | 7 | Medium | pricing.json is Claude-model-centric |
| Model Parsing | 9 | Low | parseModelString() only handles claude-* |
Overall Coupling Score: 8.3 / 10 — Deeply coupled to Claude Code at nearly every layer.
1. Process Management
Coupling: 9/10 | Effort: High
Specific Files
src/main/services/team/ClaudeBinaryResolver.ts— resolves theclaudebinary across platformssrc/main/utils/childProcess.ts—spawnCli()/execCli()wrappers injectCLAUDE_HOOK_JUDGE_MODEenv varsrc/main/services/team/TeamProvisioningService.ts— spawnsclaudewith Claude-specific flagssrc/main/services/infrastructure/CliInstallerService.ts— downloadsclaudebinary from GCSsrc/main/services/schedule/ScheduledTaskExecutor.ts— spawnsclaude -pfor scheduled tasks
What's Claude-specific
- Binary name:
ClaudeBinaryResolversearches forclaudebinary across PATH, NVM, platform-specific dirs - CLI flags:
--input-format stream-json,--output-format stream-json,--verbose,--setting-sources,--mcp-config,--disallowedTools,--dangerously-skip-permissions,--permission-prompt-tool,--model,--effort,--worktree,--resume,--no-session-persistence,--max-turns,--permission-mode - Env var:
CLAUDE_HOOK_JUDGE_MODE: 'true'injected into every CLI process - Env var:
CLAUDE_CONFIG_DIRset inbuildEnrichedEnv() - Env var override:
CLAUDE_CLI_PATHfor custom binary location - Kill semantics:
killTeamProcess()uses SIGKILL specifically because Claude CLI cleanup on SIGTERM deletes team files - GCS distribution:
CliInstallerServicedownloads fromhttps://storage.googleapis.com/claude-code-dist-.../claude-code-releases - Version command:
claude --versionexpected to output"X.Y.Z (Claude Code)" - Install command:
claude installfor shell integration
Abstraction Approach
Create a CliProvider interface:
interface CliProvider {
name: string;
resolveBinaryPath(): Promise<string | null>;
buildSpawnArgs(options: SpawnOptions): string[];
buildEnv(binaryPath: string): NodeJS.ProcessEnv;
parseVersionOutput(stdout: string): string;
getKillSignal(): NodeJS.Signals;
install(): Promise<void>;
checkAuth(): Promise<AuthStatus>;
}
Each provider (ClaudeCliProvider, CodexCliProvider, GeminiCliProvider) implements this. ClaudeBinaryResolver becomes ClaudeCliProvider.resolveBinaryPath().
2. Protocol / Communication
Coupling: 10/10 | Effort: High
Specific Files
src/main/services/team/TeamProvisioningService.ts(lines 126-132, 2742-2980, 4849-5290) — stream-json parsersrc/renderer/utils/streamJsonParser.ts— renderer-side stream-json log parsingsrc/renderer/components/team/CliLogsRichView.tsx— renders stream-json outputsrc/shared/utils/teammateMessageParser.ts— parses<teammate-message>XML format
What's Claude-specific
- stream-json protocol: Claude Code's proprietary newline-delimited JSON over stdin/stdout
- Input:
{"type":"user","message":{"role":"user","content":[...]}}\n - Output types:
user,assistant,control_request,result,system result.success= turn complete,result.error= failurecontrol_requestfor tool approval prompts
- Input:
- Message envelope:
{"type":"user","message":{"role":"user","content":[{"type":"text","text":"..."}]}} - Teammate message format: XML tags
<teammate-message teammate_id="..." color="..." summary="...">content</teammate-message> - Preflight ping:
claude -p "Output only the single word PONG." --output-format text --model haiku --max-turns 1 --no-session-persistence - Tool approval:
control_requesttype withtool_input,tool_name, approval via stdin
Abstraction Approach
This is the hardest area. Create a CliProtocol interface:
interface CliProtocol {
formatInputMessage(text: string): string;
parseOutputLine(line: string): ParsedOutputMessage;
isResultSuccess(msg: ParsedOutputMessage): boolean;
isResultError(msg: ParsedOutputMessage): boolean;
isToolApprovalRequest(msg: ParsedOutputMessage): ToolApprovalRequest | null;
formatToolApprovalResponse(approved: boolean): string;
getProtocolFlags(): string[]; // e.g. ['--input-format', 'stream-json', ...]
}
Each agent's protocol would need a distinct implementation. OpenAI Codex uses a different protocol (REST-based sandbox execution, not stdin/stdout). This would require major architectural changes.
3. Message Parsing (JSONL)
Coupling: 9/10 | Effort: High
Specific Files
src/main/types/jsonl.ts— raw JSONL entry types (Claude Code session file format)src/main/types/messages.ts— parsed message types and type guardssrc/main/types/domain.ts— domain types referencing~/.claude/projects/structuresrc/main/types/chunks.ts— chunk building from parsed messagessrc/main/utils/jsonl.ts— JSONL file parsersrc/main/constants/messageTags.ts—<local-command-stdout>,<local-command-caveat>,<system-reminder>tags
What's Claude-specific
- JSONL schema: Entry types (
user,assistant,system,summary,file-history-snapshot,queue-operation) are Claude Code's internal format - Content blocks:
text,thinking,tool_use,tool_result,image— follows Anthropic Messages API schema thinking+signature: Extended thinking is an Anthropic-specific featureisMetaflag: Claude Code's internal convention for distinguishing real user messages from tool resultsisSidechain: Claude Code's flag for subagent messagesstop_reason:end_turn,tool_use,max_tokens,stop_sequence— Anthropic API values- XML tags in content:
<local-command-stdout>,<local-command-caveat>,<system-reminder>,<command-name>are Claude Code's internal message wrapping <synthetic>model: Claude Code's marker for system-generated placeholdersisCompactSummary: Claude Code's context compaction mechanism- Usage metadata:
cache_read_input_tokens,cache_creation_input_tokens— Anthropic cache API
Abstraction Approach
Create a SessionParser interface that converts provider-specific session data to a normalized ParsedMessage:
interface SessionDataProvider {
parseSessionFile(path: string): AsyncIterable<ParsedMessage>;
isRealUserMessage(msg: ParsedMessage): boolean;
isToolCall(block: ContentBlock): boolean;
extractToolResult(msg: ParsedMessage): ToolResult | null;
}
The existing ParsedMessage type is actually reasonably generic (it has toolCalls, toolResults, content). The provider-specific part is the parsing FROM the raw format TO ParsedMessage. New providers would implement different parsers.
4. Team Management
Coupling: 10/10 | Effort: Very High
Specific Files
src/main/services/team/TeamProvisioningService.ts(~7800 lines) — the monolithagent-teams-controller/— workspace package for file-level team operationssrc/main/services/team/*.ts(~35 files) — team data, inbox, tasks, kanban, review, cross-teamsrc/shared/types/team.ts— TeamConfig, TeamTask, SendMessageRequest, etc.src/main/ipc/teams.ts— ~65 IPC handlers for team operationssrc/shared/utils/leadDetection.ts— detects team lead byagentTypevalues
What's Claude-specific
- Agent Teams is a Claude Code feature:
TeamCreate,TaskCreate,TaskUpdate,TaskList,TaskGet,SendMessage,TeamDeleteare Claude Code CLI tools - Team file structure:
~/.claude/teams/{teamName}/config.json,inboxes/{member}.json,kanban-state.json,processes.json,members.meta.json - Task file structure:
~/.claude/tasks/{teamName}/{taskId}.json - Inbox protocol: File-based message passing — lead reads stdin, teammates read inbox files
- Lead/teammate distinction: Lead uses stream-json, teammates are independent CLI processes
- Tool blocking:
--disallowedTools TeamDelete,TodoWrite agentTypevalues:team-lead,lead,orchestrator,general-purpose— Claude Code internal valuesteammate_spawnedtool results: How team member processes are detected- Cross-team communication:
cross_team_send,cross_team_list_targets,cross_team_get_outbox - Action mode instructions: Custom protocol text injected into team prompts
agent-teams-controllerpackage: Pure JS module that reads Claude Code's team filesystem directly
Abstraction Approach
This is by far the hardest area. Agent Teams is a unique Claude Code feature with no equivalent in other CLI agents. Options:
- Option A: Keep team management as Claude-only feature, abstract only session viewing
- Option B: Build a generic team orchestration layer that wraps different agent CLIs. Would need to implement inbox/task/kanban semantics independently of Claude Code.
- Option C: Make team management pluggable — each provider declares
supportsTeams: booleanand provides aTeamOrchestratorimplementation if supported
Option A is the most realistic short-term approach.
5. Session Data / Paths
Coupling: 9/10 | Effort: Medium
Specific Files
src/main/utils/pathDecoder.ts— all path construction (~/.claude/projects/,~/.claude/todos/,~/.claude/teams/,~/.claude/tasks/)src/main/services/discovery/ProjectScanner.ts— scans~/.claude/projects/src/main/services/infrastructure/FileWatcher.ts— watches~/.claude/projects/,~/.claude/todos/,~/.claude/teams/,~/.claude/tasks/src/main/services/infrastructure/SshConnectionManager.ts— hardcodes~/.claude/projectsfor remotesrc/main/services/infrastructure/ConfigManager.ts— config at~/.claude/claude-devtools-config.jsonsrc/main/constants/worktreePatterns.ts— detects.claude/worktrees/pattern
What's Claude-specific
- Base path:
~/.claude/as root for all data - Path encoding:
/Users/name/project→-Users-name-project(Claude Code's convention) - Session files:
~/.claude/projects/{encoded-path}/{uuid}.jsonl - Subagent files:
~/.claude/projects/{path}/{session_uuid}/agent_{uuid}.jsonl - Todo files:
~/.claude/todos/{sessionId}.json - Team files:
~/.claude/teams/{teamName}/... - Task files:
~/.claude/tasks/{teamName}/{taskId}.json - Config:
~/.claude/claude-devtools-config.json(our config, stored in Claude's directory) - SSH remote: Hardcoded
/home/{user}/.claude/projects,/Users/{user}/.claude/projects,/root/.claude/projects - Worktree patterns:
.claude/worktrees/as a known source
Abstraction Approach
Path resolution is already partially abstracted via getClaudeBasePath() with override support (setClaudeBasePathOverride). Extend to:
interface DataPathProvider {
getBasePath(): string; // ~/.claude/, ~/.codex/, etc.
getProjectsPath(): string; // {base}/projects/
getSessionPath(projectId: string, sessionId: string): string;
getSubagentPath(projectId: string, sessionId: string): string;
encodeProjectPath(absolutePath: string): string;
decodeProjectPath(encoded: string): string;
}
Medium effort because path functions are centralized in pathDecoder.ts. The SSH remote paths would need provider-specific resolution.
6. Authentication
Coupling: 8/10 | Effort: Medium
Specific Files
src/main/services/infrastructure/CliInstallerService.ts—claude auth status --output-format json,claude --versionsrc/shared/types/cliInstaller.ts—CliInstallationStatus.authLoggedIn,authMethodsrc/main/utils/cliAuthDiagLog.ts— diagnostic logging for auth issuessrc/renderer/components/dashboard/CliStatusBanner.tsx— shows login status
What's Claude-specific
- Auth check:
claude auth status --output-format json— returns{loggedIn: boolean, authMethod: string} - Auth method types:
"oauth_token","api_key"— Claude-specific - Binary distribution: GCS bucket
claude-code-dist-*with platform-specific binaries - Install flow: Downloads binary → SHA256 verify →
claude installfor shell integration - Version parsing:
"2.1.59 (Claude Code)"format - Preflight auth check: Runs
claude -p "PONG"to verify auth works
Abstraction Approach
interface CliInstallerProvider {
getLatestVersion(): Promise<string>;
downloadBinary(platform: CliPlatform): Promise<string>; // returns temp path
installBinary(binaryPath: string): Promise<void>;
checkVersion(binaryPath: string): Promise<string>;
checkAuth(binaryPath: string): Promise<AuthStatus>;
}
7. MCP Integration
Coupling: 5/10 | Effort: Low
Specific Files
src/main/services/team/TeamMcpConfigBuilder.ts— builds MCP config JSON for team processessrc/main/services/extensions/install/McpInstallService.ts— installs MCP serverssrc/shared/types/extensions/mcp.ts— MCP typesmcp-server/— built-in MCP server for the app
What's Claude-specific
- Config file location:
.claude.jsonin home dir,.mcp.jsonin project - CLI flag:
--mcp-configto pass config path to CLI - Config format: Standard MCP format (
{mcpServers: {name: {command, args}}}) - Built-in server:
mcp-server/is our own — not Claude-specific
What's NOT Claude-specific
MCP (Model Context Protocol) is becoming a cross-vendor standard. The protocol itself is vendor-neutral. The config format may vary by agent but the server implementation is portable.
Abstraction Approach
MCP is already the most abstracted area. The only coupling is the config file naming (.claude.json) and the --mcp-config flag. A provider interface would specify how to pass MCP config to the CLI.
8. UI Components
Coupling: 6/10 | Effort: Medium
Specific Files
src/renderer/index.html— title "Claude Agent Teams UI"src/renderer/components/common/ErrorBoundary.tsx— CSS classesbg-claude-dark-bg,text-claude-dark-textsrc/renderer/components/team/ClaudeLogsDialog.tsx,ClaudeLogsPanel.tsx,ClaudeLogsSection.tsx,ClaudeLogsFilterPopover.tsx,useClaudeLogsController.ts— "Claude Logs" feature namingsrc/renderer/types/claudeMd.ts— CLAUDE.md tracking typessrc/renderer/utils/claudeMdTracker.ts(70 occurrences) — CLAUDE.md context trackingsrc/renderer/utils/contextTracker.ts(56 occurrences) — references CLAUDE.md sourcessrc/renderer/components/chat/SessionContextPanel/— CLAUDE.md sectionsrc/renderer/components/settings/sections/GeneralSection.tsx(69 occurrences) — "Claude Root" settingssrc/renderer/components/dashboard/CliStatusBanner.tsx— "Claude CLI" statussrc/renderer/index.css— comments mentioning "Claude Code"src/shared/constants/cli.ts—CLI_NOT_FOUND_MESSAGE = 'Claude CLI not found...'
What's Claude-specific
- Branding strings: "Claude Agent Teams UI", "Claude CLI", "Claude Logs", "Claude Root"
- CSS theme variables:
claude-dark-bg,claude-dark-text,claude-dark-border,claude-dark-surfacein ErrorBoundary - CLAUDE.md feature: The entire CLAUDE.md tracking system (types, tracker, UI) is Claude Code specific
- "Claude Logs": 5+ components for viewing CLI logs named "ClaudeLogs*"
- Settings: "Local Claude Root" setting for
~/.claudeoverride
What's NOT Claude-specific
- Chat rendering (UserChunk, AIChunk, SystemChunk) is generic
- Kanban board UI is generic
- Team member list, task management UI is generic
- Tool call visualization is generic (tool_use/tool_result pattern is shared across LLM providers)
Abstraction Approach
- Replace hardcoded strings with a config/branding module
- Rename
ClaudeLogs*→CliLogs*orAgentLogs* - Rename
claudeMdTracker→instructionFileTracker(provider specifies filename pattern) - CSS variable renaming is mechanical (
claude-dark-*→app-dark-*) - "Claude Root" → "Agent Data Directory"
9. Types / Interfaces
Coupling: 8/10 | Effort: High
Specific Files
src/main/types/jsonl.ts—ChatHistoryEntryunion follows Claude Code JSONL exactlysrc/main/types/messages.ts—ParsedMessagewith Claude-specific fields (isMeta,isSidechain,isCompactSummary)src/main/types/domain.ts—MessageType,TokenUsagewithcache_read_input_tokenssrc/shared/types/team.ts— Team types entirely Claude Agent Teams specificsrc/shared/types/api.ts— API surface exposes Claude-specific session/team typessrc/shared/utils/modelParser.ts— parsesclaude-*model strings onlysrc/shared/utils/pricing.ts— pricing data is Claude/Anthropic model centric
What's Claude-specific
- Content block types:
thinkingwithsignaturefield — Anthropic extended thinking - Token usage fields:
cache_read_input_tokens,cache_creation_input_tokens— Anthropic prompt caching - Model string format:
claude-{family}-{major}-{minor}-{date}and oldclaude-{major}-{family}-{date} - Model families:
sonnet,opus,haiku— Anthropic model names isMeta/isSidechain: Claude Code's internal conventionsstop_reasonvalues:end_turn,tool_use,max_tokens,stop_sequence- Pricing data:
resources/pricing.jsonis Anthropic-model-only (includes Bedrock/Vertex variants)
Abstraction Approach
The ParsedMessage type is actually fairly close to a generic representation. Key changes:
- Make
thinkingcontent optional/provider-specific - Generalize token usage (some fields are Anthropic-specific)
modelParser.tsneeds a provider-aware implementation- Pricing needs multi-provider support (or provider-supplied pricing)
10. Configuration
Coupling: 7/10 | Effort: Medium
Specific Files
src/main/services/infrastructure/ConfigManager.ts— stores config in~/.claude/claude-devtools-config.jsonsrc/main/utils/cliEnv.ts— setsCLAUDE_CONFIG_DIRenv varsrc/main/utils/pathDecoder.ts—getClaudeBasePath()with override supportsrc/shared/utils/cliArgsParser.ts—PROTECTED_CLI_FLAGSare Claude CLI flagssrc/main/ipc/config.ts— configuration IPC handlerssrc/main/services/team/TeamMcpConfigBuilder.ts—.claude.jsonuser MCP config
What's Claude-specific
- Config dir:
~/.claude/as base - Config filename:
claude-devtools-config.json - Env vars:
CLAUDE_CONFIG_DIR,CLAUDE_CLI_PATH,CLAUDE_HOOK_JUDGE_MODE - Protected flags:
--input-format,--output-format,--setting-sources,--mcp-config,--disallowedTools,--verbose - Settings sources:
user,project,local— Claude CLI setting hierarchy - User config files:
.claude.json(MCP),~/.claude/settings.json
Abstraction Approach
Already partially abstracted (setClaudeBasePathOverride exists). Extend:
interface ProviderConfig {
basePath: string;
configFileName: string;
envVars: Record<string, string>;
protectedFlags: Set<string>;
settingSources?: string;
}
Additional Coupling: agent-teams-controller Package
Coupling: 10/10 | Effort: High
The agent-teams-controller/ workspace package is a pure JS module that directly reads/writes Claude Code's team filesystem:
runtimeHelpers.js:getPaths()returns~/.claude/teams/{name}/,~/.claude/tasks/{name}/context.js:createControllerContext({teamName, claudeDir})tasks.js,kanban.js,review.js,messages.js, etc. — all operate on Claude's file structures
This package would need to be either:
- Made provider-aware (different file layouts per provider)
- Replaced with a generic team data layer
Estimated Overall Effort for Full Abstraction
| Phase | Scope | Estimated Effort |
|---|---|---|
| Phase 1: Session viewing only | Path abstraction + JSONL parser + model parser | 2-3 weeks |
| Phase 2: UI de-branding | Rename strings, CSS vars, component names | 1 week |
| Phase 3: CLI provider interface | Binary resolution + auth + install | 2 weeks |
| Phase 4: Protocol abstraction | stream-json → generic protocol layer | 3-4 weeks |
| Phase 5: Team management abstraction | Generic orchestration layer | 4-8 weeks |
| Total | Full multi-provider support | 12-18 weeks |
Recommended Abstraction Strategy
Priority Order (what to do first)
-
Paths first (low risk, high reward) —
pathDecoder.tsalready has override support. MakegetBasePath()provider-aware. This unblocks session viewing for other agents. -
Session parser second — Create
SessionDataProviderinterface. The existingParsedMessagetype works as the normalized target. Each provider implements a parser FROM their raw format TOParsedMessage. -
Model/pricing third — Make
parseModelString()and pricing lookup provider-aware. Use a registry pattern where each provider registers its models. -
CLI provider fourth — Abstract binary resolution, auth, install, spawning. This is where protocol differences become critical.
-
Team management last — This is the hardest and most Claude-specific feature. Consider keeping it as a Claude-only feature initially.
What's Hardest
-
stream-json protocol — This is Claude Code's proprietary stdin/stdout protocol. Other agents use completely different paradigms (OpenAI Codex uses sandboxed REST API, Gemini CLI may use different protocol). Abstracting this requires a fundamental architectural decision about how the app communicates with agents.
-
Agent Teams — No other CLI agent has an equivalent feature. The entire team management subsystem (~35 service files, ~65 IPC handlers, controller package) is built around Claude Code's Agent Teams. Supporting multi-agent orchestration for other providers would essentially mean building this from scratch.
-
JSONL session format — Claude Code's JSONL format is deeply embedded in the codebase (types, parsers, chunk builders, context trackers). While
ParsedMessageserves as a reasonable intermediary, the raw parsing layer touching 10+ files would need provider-specific implementations.
What's Easiest
- MCP — Already vendor-neutral. Only config file naming and CLI flag need adjustment.
- UI branding — Mechanical string/CSS replacement.
- Path configuration — Override mechanism already exists.
Architecture Diagram: Provider-Agnostic Layer
┌─────────────────────────────────────────────────────────────┐
│ Renderer (UI) │
│ Components (generic) │ Store (generic) │ Types (generic)│
└────────────────────────────┬────────────────────────────────┘
│ IPC
┌────────────────────────────┴────────────────────────────────┐
│ Provider Manager │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Session │ │ CLI │ │ Team │ │
│ │ Provider │ │ Provider │ │ Provider │ ← Interfaces │
│ └─────┬────┘ └─────┬────┘ └─────┬────┘ │
│ │ │ │ │
│ ┌─────┴────┐ ┌─────┴────┐ ┌─────┴────────┐ │
│ │ Claude │ │ Claude │ │ Claude Agent │ │
│ │ JSONL │ │ CLI │ │ Teams │ ← Impls │
│ │ Parser │ │ Spawner │ │ Orchestrator │ │
│ └──────────┘ └──────────┘ └──────────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ Codex │ │ Codex │ │ (not │ │
│ │ Session │ │ CLI │ │ supported) │ ← Future │
│ │ Parser │ │ Spawner │ │ │ │
│ └──────────┘ └──────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
┌────────────────────────────┴────────────────────────────────┐
│ Data Path Provider │
│ ~/.claude/ │ ~/.codex/ │ ~/.gemini/ │ etc. │
└─────────────────────────────────────────────────────────────┘
Key Interfaces
CliProvider
├── resolveBinaryPath() → string | null
├── buildSpawnArgs(opts) → string[]
├── buildEnv(binary) → ProcessEnv
├── checkAuth(binary) → AuthStatus
├── getKillSignal() → Signals
└── getProtocolFlags() → string[]
CliProtocol
├── formatInputMessage(text) → string
├── parseOutputLine(line) → ParsedOutput
├── isSuccess(msg) → boolean
├── isError(msg) → boolean
└── isToolApproval(msg) → ToolApproval | null
SessionDataProvider
├── parseSessionFile(path) → AsyncIterable<ParsedMessage>
├── getSessionPaths(basePath) → string[]
├── getSubagentPaths(sessionPath) → string[]
└── encodeProjectPath(path) → string
DataPathProvider
├── getBasePath() → string
├── getProjectsPath() → string
├── getTeamsPath() → string | null
├── getSessionFilePath(project, session) → string
└── getConfigFilePath() → string
TeamOrchestrator (optional per provider)
├── supportsTeams: boolean
├── createTeam(request) → TeamCreateResponse
├── launchTeam(request) → TeamLaunchResponse
├── sendMessage(team, request) → SendMessageResult
└── stopTeam(teamName) → void
ModelInfoProvider
├── parseModelString(model) → ModelInfo | null
├── getModelFamilies() → string[]
├── getPricing(model) → Pricing | null
└── getContextWindow(model) → number
InstructionFileProvider
├── getFilename() → string // "CLAUDE.md", ".codexrc", etc.
├── getGlobalPath() → string
├── getProjectPath(projectDir) → string
└── getSourceTypes() → string[]
Conclusion
The codebase is deeply coupled to Claude Code at approximately 8.3/10 overall. The coupling is most severe in:
- Team management (10/10) — Claude Agent Teams is a unique feature with no equivalent
- Protocol (10/10) — stream-json is proprietary
- Session data (9/10) — JSONL format, path encoding, file structure
- Process management (9/10) — Claude binary, flags, kill semantics
The most pragmatic path to multi-provider support would be a phased approach starting with session viewing (paths + JSONL parser abstraction), which delivers value with ~3 weeks effort, before tackling the much harder protocol and team management layers.
Full abstraction to support other agents with team management would require 12-18 weeks of focused effort, with the protocol and team management layers being the primary engineering challenges.