Repository navigation
Add inbound Anthropic Messages API for Claude Code clients #75
Description
Part of #73.
Blocked by: #74 (native dependency). Implement the verified contract #74 records; do not start from the historical PARTIAL evidence.
Objective
Add the production inbound Anthropic Messages surface for Claude Code clients, routing to all three upstream protocols, with declared best-effort adaptation per ADR-0014 and redacted adaptation diagnostics.
Authority
- ADR-0014 amends ADR-0001: equivalent conversion first, declared best-effort adaptation otherwise, no separate opt-in, actionable error when no safe adaptation can carry essential content.
- ADR-0001 still requires the representation seam, ordered content, opaque-field preservation, credential separation, and declared-adaptation outcomes.
- ADR-0012 covers the outbound Anthropic converter and is a different direction; do not fold it in. Provider-level
anthropic_messagestoday exists for catalog-owned models such asopencode-go/union-alpha.
Scope
Routing to the three upstream protocols
anthropic_messagesupstream — native route: preserve request, semantic headers, JSON response, and incremental SSE rather than round-tripping through an OpenAI representation. The Gateway still owns model resolution, credential substitution, transport safety, limits, and redaction.responsesupstream — Messages ↔ Responses translation, reusing the existingRoutePlan/wire-adapter seam.chat_completionsupstream — Messages ↔ Chat Completions translation.POST /v1/messagesincluding query variants such as?beta=true: match the path, not the query string. Add an explicit provider-scoped form only if Spike Claude Code Messages compatibility through CodexHub #74 proves a stable contract.- Route selection must respect the enabled/exported catalog and must never silently substitute a different model for the requested one.
Streaming and shape
- Non-streaming and streaming output become Anthropic message JSON / SSE with real incremental delivery:
message_start,content_block_start/delta/stop,message_delta,message_stop, without buffering the full upstream response. - Preserve tool call/result IDs and multi-turn history exactly across the round trip.
- Map usage truthfully. Unrepresentable usage detail is disclosed as unavailable, never folded into invented base counts.
Adaptation policy and diagnostics
- Equivalence first; otherwise best-effort adaptation with a named policy per field.
- Never silently remove user text, images, or tool results; never break call identity; never fabricate success or usage; never silently substitute a model.
- If no safe adaptation can carry essential content, return an actionable Anthropic error envelope.
- Best effort does not authorize blind retry after partial output or possible side effects: no field-stripping retry loop on an ambiguous upstream error (duplicate execution and double billing).
- Record a structured, redacted per-request adaptation record: which field was adapted/omitted and why. Surface it through the existing Gateway diagnostics surface (
GatewayDiagnostic; Gateway page / diagnostics overlay) — the Claude Code card's concise compatibility state is Add a managed Claude Code client config adapter and settings UI #76's job. Never inject diagnostics into model answers; never record credentials or full prompts.
Boundary behavior
- Anthropic error envelopes, body limits, timeouts, downstream disconnect, and cancellation.
anthropic-version,anthropic-beta, andx-claude-code-*headers are open sets: forward what the verified contract forwards, consume what the Gateway owns, redact secrets, and pseudonymize session/agent identifiers in diagnostics. A non-Anthropic adapter must map a known semantic or return a precise unsupported outcome — never silently strip a field./v1/messages/count_tokens: implement only if Spike Claude Code Messages compatibility through CodexHub #74 evidence supports it; otherwise return an explicit documented unsupported/fallback response.- Answer any preflight/connectivity probe only as the verified contract requires (
codex_proxy.pycurrently implementsdo_GET/do_POSTonly); an absent probe answer must not break startup.
Authentication
- Accept the Gateway client key per Spike Claude Code Messages compatibility through CodexHub #74's recorded carrier (expected:
ANTHROPIC_AUTH_TOKEN→Authorization: Bearer). Ifx-api-keyacceptance is required for an approved configuration, add it as the same Gateway key with no weakened authorization and no new bypass, keeping Harden local Gateway auth so codex-app context is not spoofable #24 intact. - Gateway credentials stay distinct from upstream credentials.
Out of scope
Client configuration, launcher, card, settings dialog, model aliases/discovery (#77), and compatibility matrix (#78). No client UI ships ahead of this route working.
Acceptance criteria
-
/v1/messagesroutes correctly to each of the three upstream formats, with per-format tests. - The native Anthropic path preserves semantic headers, ordered content blocks, opaque fields, and SSE event order, and limits rewrites to Gateway-owned model resolution, credentials, transport safety, and redaction.
- Text, standard tools, tool results, multi-turn history, JSON responses, and incremental Anthropic SSE pass the matrix approved in Spike Claude Code Messages compatibility through CodexHub #74.
- Tool call/result identity round-trips unchanged across every upstream format.
- Every non-equivalent adaptation is declared and appears in the redacted request diagnostic; nothing critical is dropped silently.
- No safe adaptation ⇒ actionable error envelope, and a deterministic test proves it.
- No blind strip-and-retry path exists; ambiguous failures fail closed.
- Malformed/oversized input, timeout, upstream error, and client disconnect have deterministic tests.
- Usage mapping is truthful; unavailable detail is disclosed rather than invented.
- Gateway Bearer auth accepts the Claude Code carrier without weakening Harden local Gateway auth so codex-app context is not spoofable #24.
- Existing Responses and Chat routes do not regress.
- Telemetry, errors, and diagnostics redact every credential and pseudonymize
x-claude-code-*identifiers.
Required verification
Risk class: strict — protocol, routing, transport, auth, security. Use the repository launcher; never a bare python/pytest.
Add focused Messages JSON/SSE, malformed-input, adaptation-policy, and route-selection suites at the public HTTP surface (no private CodexProxyHandler internals — see the test seam discipline in docs/agents/verification-policy.md). Then:
.\scripts\codexhub-python.cmd -m pytest tests/test_route_plan_seam.py tests/test_chat_completions_gateway.py -q
.\scripts\codexhub-python.cmd -m pytest -q --ignore=tests/test_real_client_e2e.py
.\scripts\codexhub-python.cmd scripts/report_quality_gates.pyLinux: ./scripts/codexhub-python.sh .... Real-CLI acceptance for this route belongs to #78; this issue proves the protocol contract deterministically plus any live evidence #74's contract requires.
References
docs/adr/0012-outbound-anthropic-messages.md(different direction — read before touching the outbound seam)src-python/codex_proxy.py,src-python/route_plan.py,src-python/route_primitives.py,src-python/protocol_translation.py,src-python/gateway_request.py(local_request_authorized)docs/agents/verification-policy.md
Document status
ADR-0014 is committed in draft PR #556 at ed76f0a6e22d3188879504638b2943f5bb855d41, not yet merged to main. The pinned link is readable now; the document is accepted direction, not live protocol evidence.
Activity
NOirBRight commentedon Sep 20, 2026
Read-only preparation at ed76f0a identified two implementation traps to cover within the already-approved three-protocol scope:
These observations do not waive #74. Test the public RoutePlan/HTTP seams plus existing reverse/outbound routes; no production code has been changed.
NOirBRight commentedon Sep 21, 2026
PARTIAL inbound Messages is on the campaign draft at b002bea. RoutePlan adapters, POST /v1/messages (query ignored), count_tokens 400, Chat request conversion, JSON message replies, and Chat SSE→Anthropic events are in. Native Anthropic SSE now fail-closes without message_stop/error. Remaining Standards holds: production still imports the #74 prototype; named Adaptation diagnostics are not yet on the Gateway diagnostic surface. No live GO.