Claude API-key lanes on ACP
Child of epic 7191 "Converge every runner on one agent contract: ACP-native". Settled inputs: PRD (task 7189), architecture (task 7190). Architecture boundary C14. Highest-risk provider child (R2): never silently drop a Claude capability.
## Original requirements (PRD, verbatim)
**R10 — Claude parity floor (D3 operator comment).** "we must not lose any functionality that we have now with Claude Code. Claude will remain at least as functional as now or better." Everything Claude runners and Claude console sessions do today keeps working on the new runtime: OMC plugin layer and managed settings, subagents, skills, slash-commands-backed workflows, hooks-based guards, `--append-system-prompt` charter, `--settings` deny list, assigned session id and resume, StopFailure self-heal, apiKeyHelper (no key in env), Bedrock lane (K2/K3), subscription OAuth + spillover, quota/identity, usage/Efficiency, HUD/status line, `--add-dir` multi-repo, NodeLink shared tokens, Windows git-bash. "Not everything becomes MCP": a Claude feature with no ACP/MCP equivalent stays on its native mechanism inside the Claude shim rather than being dropped.
**R2** (Claude leg): Claude Code, DeepSeek (as a Claude-shim variant) on the new runtime.
## Approach (architecture, verbatim)
Operator decision A1: Claude **API-key, Bedrock-lane and DeepSeek** accounts run on ACP through the pinned upstream `@agentclientprotocol/claude-agent-acp` (Agent SDK). A5: Claude subscription-OAuth stays on the legacy TUI path permanently (`runtime: legacy_only`, no switch; AC-1, AC-4, AC-13, AC-15 exempt it, console shows the reason).
**C14: Claude API-key / Bedrock / DeepSeek on claude-agent-acp.** The Claude-OAuth `legacy_only` capability, fixtures, and the ADR entry "Claude route". Its scope is a **per-item route table** covering every R10 item: OMC plugin layer + managed settings, subagents, skills, slash-command workflows, guard hooks, charter, deny list, assigned id + resume, StopFailure self-heal, apiKeyHelper, Bedrock, spillover, quota/identity, usage/Efficiency, HUD/status line, `--add-dir`, NodeLink tokens, Windows git-bash. Each item is routed one of three ways: `_meta.claudeCode.options` field, fork change, or `legacy_only` typed gap with its reason. **StopFailure** is mapped to an `error{class:"api"}` event that triggers today's self-heal (break + replace), proven by a replay fixture of a recorded API-error frame and by a host-side fault flag (`JAAH_ACP_FAULT=api_error`, honoured only in test builds) for the dev check.
Provider mapping: parity floor via `_meta.claudeCode.options`: `systemPrompt.append` (charter), `settings` (hooks + deny list), `plugins` / `settingSources` (OMC layer, managed settings), `resume`, `additionalDirectories`, `apiKeyHelper` (no key in env), Bedrock env, `CLAUDE_CONFIG_DIR`. Fork on gap into `packages/acp-forks/claude-agent-acp`. Spillover (D-SW-1): OAuth→API-key spillover ends the attempt and re-dispatches on the target runtime. Implement `acp_launch` on the Claude adapter for the api_key/bedrock/deepseek auth kinds only. Fixtures at the pin: dispatch → events → question answered >10 min later → settle; usage-limit frame; API-error frame. Add the Claude section to the runtime part of `apps/jaahd/README.md`.
Credentials & reach: AC-1 Claude API key, AC-3 API-key item | `jaah/system/anthropic_api_key` | verified. AC-1 DeepSeek | `jaah/system/deepseek` | verified. AC-3 Bedrock item | Bedrock-lane account on dev | verified (A8). AC-3 `/proc/<pid>/environ` | read in the console via the host's `session_start` self-check event | verified. AC-1 static/MicroVM | dev nodes | verified. AC-1–AC-4 | `jaah/debug/main/operator-login` | verified. AC-1 merged-PR leg | dev test project via operator login | verified.
Out of scope: Do NOT move Claude subscription-OAuth off the legacy TUI path (A5), and do NOT delete or alter the Claude TUI path. PRD §5 verbatim:
- MUST NOT delete the legacy paths (v1 SDK, per-vendor transcript parsers in `apps/jaahd/agents/*.py`, Claude-shaped hook scripts, `clauded_wt` TUI driving). MUST NOT file that task (D5).
- MUST NOT publish (D2). MUST NOT change runner permission posture (D6).
- MUST NOT drop or regress any current Claude Code capability (R10); no Claude feature is forced through MCP/ACP where that loses function.
- MUST NOT remove the console terminal/TUI view (D3). MUST NOT submit an upstream ACP RFD (D8). MUST NOT integrate with T3 Code's RPC or MCP API.
- MUST NOT change the task lifecycle states or the merge lane. MUST NOT change billing beyond usage ingestion. MUST NOT add vendors. MUST NOT touch `apps/mobile`.
- MUST NOT put a token in the agent's environment or weaken process-ancestry identity (ADR-0083); MUST NOT add a second verb table.
Done means:
- AC-1 (Claude API key, DeepSeek) Coder task end-to-end. → verify: on dev, create a `coder` task on a test project targeting each, dispatch to a static node, observe in the task view (a) live event stream entries of the normalised kinds, (b) a question raised by the agent appears as a question card and the typed answer reaches the agent, (c) the task ends `done` via the extension/MCP settle with a merged PR. Repeat on a MicroVM node. Fails if it needs a tmux pane/TUI scrape or a vendor transcript parser.
- AC-3 (R10) Claude parity. → verify: run the Claude parity checklist (every item in R10) on dev with the switch ON; each item is observed working (e.g. OMC subagent spawn visible in the event stream; Bedrock-lane account dispatch completes; API-key account dispatch completes with no key in `/proc/<pid>/environ` of the engine; resume of an ended session from the console continues the same session; StopFailure-class API error triggers the existing self-heal). Any item that worked at 81076d689 and fails ON = fail. The PR body carries the per-item route table with evidence per row; any `legacy_only` row names its reason and is visible in the console.
- AC-6 (Claude) → verify: replay fixture of a usage-limit frame; classified usage_limit with a reset time; existing spillover still applies.
- AC-9 (Claude) → verify: `./dc check full` runs the Claude fixtures recorded at the pin (incl. the API-error → self-heal fixture) and passes.
- AC-19 (part) → verify: ADR entry "Claude route" added; `python3 tools/check_context.py` passes.
Required checks: ./dc check full
Outcome
Claude API-key and DeepSeek runs now use ACP via claude-agent-acp, routed per item, with Bedrock kept legacy (console shows why). Also fixed CP ingest dead-lettering usage events. Quick gate and review lanes green; PR #5982 queued, full gate pending. Live dev ACs left to 7281. Drafts: 7388, 7389.
Artifacts
Activity
- Draft
- Ready
- Runner started
- Runner ended (done)