Skip to content

Add inbound Anthropic Messages API for Claude Code clients #75

Closed

Description

@NOirBRight

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_messages today exists for catalog-owned models such as opencode-go/union-alpha.

Scope

Routing to the three upstream protocols

  • anthropic_messages upstream — 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.
  • responses upstream — Messages ↔ Responses translation, reusing the existing RoutePlan/wire-adapter seam.
  • chat_completions upstream — Messages ↔ Chat Completions translation.
  • POST /v1/messages including 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, and x-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.py currently implements do_GET/do_POST only); an absent probe answer must not break startup.

Authentication

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/messages routes 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.py

Linux: ./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

  1. NOirBRight commented on Sep 20, 2026

    @NOirBRight
    OwnerAuthor

    Read-only preparation at ed76f0a identified two implementation traps to cover within the already-approved three-protocol scope:

    1. route_plan._wire_format_adapter currently defaults unknown pairs to transparent. Messages→Responses/Chat must gain explicit adapter/body-mode selection; adding only the HTTP route risks forwarding an unconverted request.
    2. route_plan_for_request currently treats a general configured anthropic_messages upstream as unsupported except maintained-model overrides. Native inbound Messages must work for custom enabled/exported Anthropic-format models as well, not just union-alpha. Keep unrelated outbound-client qualification (Implement and qualify Anthropic Messages as a Gateway upstream protocol #246 / ADR-0012) separate.

    These observations do not waive #74. Test the public RoutePlan/HTTP seams plus existing reverse/outbound routes; no production code has been changed.

  2. NOirBRight commented on Sep 21, 2026

    @NOirBRight
    OwnerAuthor

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestneeds-triageMaintainer needs to evaluate this issuewayfinder:taskWayfinder implementation task

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions