Skip to content

feat: add native Anthropic provider#11

Open
torbitmatsudakh wants to merge 98 commits into
raine:mainfrom
torbitmatsudakh:anthropic-provider
Open

feat: add native Anthropic provider#11
torbitmatsudakh wants to merge 98 commits into
raine:mainfrom
torbitmatsudakh:anthropic-provider

Conversation

@torbitmatsudakh

Copy link
Copy Markdown

Summary

Adds an anthropic provider that forwards claude-* requests to api.anthropic.com using the OAuth token Claude Code already stores in the macOS keychain. The codex and kimi providers are unaffected.

Use case: route hard tasks to a real Claude model and lighter tasks to ChatGPT or Kimi inside the same Claude Code session via /model — without quitting and re-launching. Today, switching between proxy-routed and Anthropic-direct requires exiting Claude Code and starting a fresh non-proxied session.

What's included

  • src/providers/anthropic/index.ts — provider definition with the full Claude 4 model family (Opus 4.7, Sonnet 4.5/4.6, Haiku 4.4/4.5)
  • src/providers/anthropic/client.ts — minimal HTTP client (passthrough — request/response shape already matches the Anthropic API, no format translation needed)
  • src/providers/anthropic/auth/token-store.ts — read-only keychain reader; refresh is delegated to Claude Code itself
  • 1-line addition in src/providers/registry.ts to register the provider

Streaming and count_tokens work via plain passthrough.

Notes for review

  • Auth strategy: The provider re-uses Claude Code's existing keychain entry (Claude Code-credentials) read-only and never stores or refreshes tokens itself. If Claude Code isn't installed/signed in, the provider returns 401 with a clear message.
  • Legacy model names: SUPPORTED_MODELS includes both current and older names (e.g. claude-sonnet-4-5) since Claude Code 2.1.x still emits older ids via /model and background tasks.
  • No tests yet: happy to add unit tests for keychain reading + registry resolution if the direction looks right.

Scope question

This expands the project beyond "use cheaper alternatives to Anthropic", so totally understand if it doesn't fit the project's vision. I'll keep this on my fork either way — wanted to share since the wiring turned out simpler than expected (~190 lines, mostly passthrough).

raine added 30 commits April 19, 2026 11:06
Lead the README with the main user benefit: using Claude Code with an existing
ChatGPT Plus or Pro subscription instead of being pushed toward Codex or
Opencode. This removes low-level protocol details from the opening so the first
screen explains why someone would use the project before it explains how it
works.
actions/checkout, actions/upload-artifact, and actions/download-artifact
v4 run on Node.js 20, which GitHub deprecated. Bump to v5 to silence the
deprecation warnings in release runs.
Apple Silicon kills unsigned binaries with SIGKILL, so the verification
step at the end of the install script failed with "Killed: 9" even after
removing the com.apple.quarantine xattr. Bun's compiled binary ships
unsigned, and the ubuntu-based release runner can't codesign darwin
output. Run `codesign --remove-signature` then `codesign --sign -` on
the installed binary so it gets an ad-hoc signature macOS accepts.
This lets proxy users keep portable Claude-oriented model names in settings and
skills while still targeting the small set of OpenAI models allowed by the
Codex OAuth backend. It fixes cases like cheap subagents requesting haiku or
skills pinning sonnet and failing before the request reaches upstream.

The proxy now resolves a small explicit alias map before allowlist validation
and upstream dispatch, while continuing to report the originally requested
model back to Claude Code. The README documents the supported aliases and
clarifies that they are convenience shorthands rather than semantic model
matches.
GPT-5.4 sometimes emits Read tool calls with an empty string for the optional
pages field, and Claude Code rejects that payload before the tool can run. This
repairs that compatibility gap in the proxy instead of relying on the model to
always satisfy the schema.

The reducer now buffers Read tool argument deltas until the tool block is
complete, removes pages when it is the empty string, and then emits the
sanitized JSON once. Other tools keep their existing streaming behavior, so the
fix stays narrow and preserves the normal incremental path outside Read.
The README opened with text only, which made it harder to immediately show what
this proxy enables in practice. Add a screenshot near the top so visitors can
see Claude Code running through the proxy before they get into setup details.

The image is stored as a lossless WebP under meta/ to keep the repo size down
without degrading the terminal UI capture. The README references that asset
near the intro so it appears prominently on the project page.
Stop sending the Codex CLI originator string on upstream requests and identify
this project as claude-codex-proxy instead. This keeps the proxy accurate about
what it is rather than pretending to be codex-tui or opencode.

Update the README sequence diagram to reflect the actual originator header value
so the documented request shape matches the implementation.
Claude Code uses /v1/messages/count_tokens to decide when to compact, so
counting the raw Anthropic request could drift from the prompt shape the proxy
actually sends upstream. That made the estimator include data such as the
rotating billing header even though the request translator strips it before the
Codex call.

Reuse the existing request-normalization helpers for system text, message
content, and tool-result flattening inside the local token counter. This keeps
counting aligned with the translation path without changing the proxy's
upstream behavior, and should reduce premature compaction caused by systematic
mismatch between the two code paths.
We still need to understand why Claude Code compacts earlier than expected
through the proxy, and the shared-normalization change alone does not explain
whether the remaining drift comes from local counting, translated prompt shape,
or upstream usage. Without side-by-side numbers the logs are not enough to tell
which layer is responsible.

Add opt-in telemetry behind CCP_LOG_COMPACTION so the proxy can log the local
/count_tokens result, the translated prompt estimate, and the final upstream
usage for the same turn. This keeps normal runs quiet while making it much
simpler to compare Claude Code's compaction inputs against what the proxy
actually sends and what Codex reports back.
AbortError logs did not identify which in-flight tool request was being canceled, which made the Read failures hard to correlate in proxy logs.

Thread reqId and sessionId into the streaming translator and track active tool ids and names while SSE events are in flight. Include that context in upstream stream error and stream translation error logs so canceled Read calls can be matched back to a specific request.

This does not change translated responses or tool behavior. It only adds diagnostic context to proxy logging.
The new compaction telemetry does not need its own environment variable, and a
separate flag makes the logging behavior harder to discover while we are still
actively debugging the proxy. The existing verbose mode is already the place
where we opt into noisier request and stream diagnostics.

Fold the compaction telemetry into CCP_LOG_VERBOSE so one switch enables the
full debugging view. This keeps the runtime configuration simpler and matches
how the rest of the proxy's detailed logging is controlled.
Capture per-session request sequencing in verbose compaction logs so we can see
which /v1/messages/count_tokens result Claude Code saw before each translated
request and upstream completion. This makes it possible to reconstruct the
compaction decision boundary from logs instead of inferring it indirectly from
isolated samples.

The implementation keeps lightweight in-memory snapshots per session ID,
recording the latest count_tokens and messages request metadata and linking them
through sessionSeq and previous request IDs. This does not change request
handling or upstream payloads; it only enriches existing verbose telemetry.
Claude Code decides when to compact from the last response usage plus new-message estimates. The proxy was passing Codex input_tokens through unchanged while also exposing cached_tokens as cache_read_input_tokens, which made Claude Code count cached prompt tokens twice once prompt caching started to hit.

Normalize the usage mapping to Anthropic semantics by subtracting cached prompt tokens from input_tokens and reporting them only through cache_read_input_tokens. This keeps downstream context-window calculations aligned with the real prompt size without changing request translation or stream behavior.

There are no intended breaking changes beyond lower reported context usage on cached turns. The local /v1/messages/count_tokens path is still only an estimate, but the main auto-compact path should no longer inflate after cache hits.
Claude Code calls /v1/messages/count_tokens for some sizing and warning paths, but the proxy was estimating tokens from the raw Anthropic-shaped request instead of the translated Codex payload. That let the local estimate drift from what the proxy actually sends upstream, especially for structured output requests.

Count the translated request shape instead so the estimate includes the same instructions, input items, tools, tool choice, and JSON schema format that the upstream API sees. Also stop treating inline images as raw base64 text, which could wildly overestimate prompt size and trigger premature warnings when image content was present.

This is still a local estimate rather than a provider-native count endpoint, so small drift is still possible. The intended behavior change is better alignment between proxy-side token warnings and the real upstream prompt shape.
Claude Code sends max_tokens and then uses the returned assistant usage to decide when to auto-compact. The proxy was dropping that output budget entirely, which let the upstream choose its own longer completion limit and could inflate output_tokens enough to trigger proactive compaction earlier than the direct Anthropic path.

Pass the Anthropic max_tokens field through as the upstream max_output_tokens setting, and extend verbose telemetry so one run can show whether remaining drift comes from hidden reasoning tokens or lost context-management behavior. The new logs include requested vs translated output limits, whether context_management was present, raw upstream reasoning token counts, and the mapped Anthropic usage totals Claude Code actually sees.

This change does not alter normal response translation beyond preserving Claude Code's requested output budget. The extra telemetry is only emitted in verbose mode and is intended to confirm or rule out the remaining compaction hypotheses without changing user-facing behavior.
We already tracked whether context_management was present, but that was not enough to verify which edits Claude Code was actually sending. Add the exact incoming context_management payload to verbose compaction telemetry so proxy logs can be grepped directly for the request-side context pruning instructions.

This keeps normal request handling unchanged and only enriches verbose diagnostics. The goal is to confirm whether Anthropic-side context management is active in the sessions that compact early, which helps separate request-shape drift from missing API-native context pruning behavior.
The ChatGPT Codex responses endpoint rejects the max_output_tokens field, so the recent output-budget passthrough caused normal requests to fail with a 400 unsupported-parameter error. That broke ordinary Claude Code turns immediately instead of just changing compaction behavior.

Remove the unsupported field from the translated upstream request and drop the matching telemetry field that depended on it. We still log the incoming Anthropic max_tokens value for debugging, but the proxy no longer tries to enforce it upstream until we know the correct supported parameter for this backend.

This restores request compatibility with the current upstream endpoint. The compaction investigation remains open, but the output-budget hypothesis needs a backend-compatible implementation before it can be tested safely.
The proxy was always requesting reasoning.encrypted_content from the upstream responses API, even when the translated request did not enable reasoning. That diverged from Codex client behavior and added unnecessary reasoning-specific request shape to ordinary turns.

Only attach the include field when output_config.effort produces a translated reasoning config, and add a focused translator test that locks in both paths. This keeps non-reasoning requests cleaner while preserving encrypted reasoning content when reasoning is actually enabled.
Add verbose compaction telemetry for the upstream OpenAI-Model and
X-Reasoning-Included response headers so proxy logs capture the requested
model, resolved model, and actual serving model together.

This helps investigate compaction drift and usage mismatches by showing when
upstream routing differs from the proxy's resolved model and whether the
server reports that reasoning is already included in its accounting.
Document the upstream request type as a constrained Codex ResponsesApiRequest
shape and add a regression test that locks the translated top-level fields to
that allowlist.

This reduces request-shape drift after the max_output_tokens regression and
makes future upstream field additions more deliberate by requiring source
support or an explicit verified change.
Treat X-Reasoning-Included as a presence flag in verbose telemetry so the
proxy matches Codex's header handling instead of requiring the literal string
value true.

This keeps compaction investigation logs accurate when upstream includes the
header with any non-empty value and avoids false negatives in the server-side
reasoning signal.
Users running Claude Code through the proxy can see premature auto-compaction when Claude Code assumes a smaller local context window than the upstream Codex model actually supports. That behavior is confusing because the backend may still have room while the client decides to compact early.

Document the practical workaround of setting DISABLE_AUTO_COMPACT=1 so users can keep full history longer and compact manually with /compact when needed. The note also calls out the tradeoff: disabling proactive compaction can lead to prompt-too-long failures if the session is allowed to grow too far.

This is a documentation-only change with no runtime behavior impact.
Rename the project from claude-codex-proxy to claude-code-proxy to
reflect that the Codex ChatGPT backend will be one of several supported
providers (Kimi is next, per history/2026-04-20-kimi-proxy-research-notes.md).

Restructure the codebase around a Provider abstraction so each backend
owns its own translation, upstream client, auth, and CLI surface:

- Move src/codex/, src/auth/, src/translate/ into src/providers/codex/.
- Promote src/translate/sse.ts to src/sse.ts (generic SSE plumbing is
  shared; translation is not).
- Introduce src/providers/types.ts (Provider interface) and
  src/providers/registry.ts (provider lookup via CCP_PROVIDER env or
  --provider flag, default codex).
- Move handleMessages + handleCountTokens bodies and their telemetry
  (including session timeline) into src/providers/codex/index.ts. The
  session timeline tracks codex-specific usage fields, so it belongs in
  the provider.
- server.ts becomes provider-agnostic: it parses requests, tracks a
  session sequence number, and dispatches to the selected provider.
- CLI commands are provider-namespaced: `codex auth login|device|status
  |logout`. Storage path moves to `~/.config/claude-code-proxy/codex/`
  and the macOS keychain service to `claude-code-proxy.codex` so each
  provider keeps its own credentials.

Breaking changes:
- Previously-stored credentials at the old path are ignored; users must
  run `claude-code-proxy codex auth login` again.
- The log directory moves to `$XDG_STATE_HOME/claude-code-proxy/`.
- Homebrew tap and install script URLs change; the GitHub repo itself
  needs to be renamed (and the homebrew-claude-codex-proxy tap repo)
  outside this commit.
Adds a new "kimi" provider under src/providers/kimi/ that currently
implements only the auth surface. Chat handlers return 501 until the
translate layer lands.

Auth details (reverse-engineered from kimi-cli 1.37.0):
- Device-code grant against https://auth.kimi.com (no PKCE, no scope).
- Persistent device_id stored under ~/.config/claude-code-proxy/kimi/;
  bound into the issued JWT, must be stable across sessions.
- Masquerades as kimi-cli via User-Agent and X-Msh-* fingerprint headers
  — the server appears to use these on both auth and chat endpoints.
- Tokens stored in macOS Keychain (or file elsewhere), mirroring the
  codex provider's token-store shape.
- Refresh manager: single-flight in-memory cache, refresh 5 min before
  expiry (default TTL is 15 min), handles rotating refresh tokens,
  retries 429/5xx with exponential backoff, clears local state on 401/403.

CLI:
  claude-code-proxy kimi auth login
  claude-code-proxy kimi auth status
  claude-code-proxy kimi auth logout
Fills out the translate layer so the kimi provider can serve real
/v1/messages traffic from Claude Code. Confirmed working end-to-end:
device login, token refresh on 401, streaming with thinking blocks
forwarded, tool calls, and per-turn reasoning_effort honoring Claude
Code's /effort setting.

Translate layer (Anthropic Messages <-> Kimi OpenAI-style):
- request.ts: system blocks -> role="system"; splits user messages on
  tool_result boundaries into separate role="tool" entries; assistant
  text + tool_use blocks collapse to a single assistant message; images
  pass through as image_url parts in both user and tool content (Kimi
  accepts this — verified against kimi-cli's ReadMediaFile capture).
  Sends reasoning_effort (from output_config.effort, default "high")
  and thinking:{type:"enabled"} unless the client disables thinking.
- reducer.ts: OpenAI chat.completion.chunk stream -> typed ReducerEvents.
  Opens a thinking block on first reasoning_content delta, closes it
  when content/tool_calls arrive, streams tool-call arguments as
  input_json_delta fragments keyed by upstream tool_calls[].index.
- stream.ts: emits Anthropic SSE events (message_start, thinking_delta,
  text_delta, input_json_delta, message_delta with mapped usage).
- accumulate.ts: non-streaming variant producing a full Anthropic Message.
- model-allowlist.ts: every alias (haiku/sonnet/opus/claude-*) collapses
  to kimi-for-coding — the only id the /coding/v1/models endpoint
  exposes (display_name: Kimi-k2.6).

Client + wiring:
- client.ts: POSTs to /coding/v1/chat/completions with Bearer + X-Msh-*
  fingerprint headers, triggers forceRefresh on 401 and retries once,
  surfaces 429 with retry-after.
- count-tokens.ts: gpt-tokenizer approximation over the translated
  request; good enough for Claude Code's compaction heuristic.
- index.ts: full handleMessages / handleCountTokens, logs
  reasoning_effort / thinking / max_tokens for debugging.
- cli.ts: prints ANTHROPIC_MODEL="kimi-for-coding" hint when the
  provider is kimi.
…ault

Each provider now declares a supportedModels set listing the concrete
wire ids it claims (codex: gpt-5.2 / gpt-5.3-codex / gpt-5.4 /
gpt-5.4-mini; kimi: kimi-for-coding / kimi-k2.6 / k2.6). On each
/v1/messages and /v1/messages/count_tokens request the server looks
up body.model in the registry and dispatches to the matching provider.
Unknown model ids return HTTP 400 with the full list of supported ids
per provider — there is no implicit default, so CCP_PROVIDER is gone.

This lets one `serve` process handle both providers at once; users
switch upstream purely by changing ANTHROPIC_MODEL. The removed
cross-provider aliases (haiku / sonnet / opus / claude-*) were
ambiguous and silently collapsed to whichever provider was the
current default; they're dropped to force an explicit choice.

Startup banner now enumerates the model → provider mapping and
prints a hint for ANTHROPIC_SMALL_FAST_MODEL too, since Claude Code
sends its hardcoded haiku id for background / title-gen calls and
those would also 400 without pointing at a concrete model.
raine and others added 29 commits April 25, 2026 10:17
mkdir was called without a mode, leaving directory permissions subject to the process umask. With a default umask of 0022 the result is 0755, making auth.json world-traversable even though the file itself is 0600. On shared Unix hosts this is a meaningful exposure.
maybeRotate closed the stream before attempting rename. If rename threw, the stream was left as undefined and the file still exceeded MAX_LOG_BYTES. Every subsequent log write would re-open the file, re-trigger maybeRotate, close the stream again, and fail the rename again. Fix: rename first, then close the stream; the surrounding catch block suppresses any rename error and leaves the stream intact.
readPtr returned a plain number, but Bun FFI types pointer arguments as
the nominal Pointer type ({ __pointer__: null }). At runtime a pointer is
just a number, so cast through unknown to satisfy the type checker without
changing the runtime behavior.
The project was renamed from consult-llm-mcp to consult-llm.
Both reducers previously silenced JSON.parse errors and continued
through the stream. If every upstream event was corrupt the translator
would still emit a normal finish event, producing an apparent empty
success instead of surfacing the upstream malfunction.

Threading a logger through reduceUpstream lets the reducers warn (with
a truncated preview) when a chunk fails to parse. This is purely
diagnostic; the skip behavior is unchanged.
Both providers cast resp.json() directly to TokenResponse and stored
the result. A malformed response with a missing access_token, missing
refresh_token, or non-numeric expires_in would be saved without
complaint and only manifest later as Bearer undefined upstream calls
or as a token whose expiry computes to NaN (refreshed every request).

Validate the shape on refresh and on initial login so a bad response
fails loudly with a clear message instead of poisoning the token
store.
keychainDelete previously discarded the status from
SecKeychainItemDelete, so a failed deletion would still report success
to callers. A failed logout could leave credentials in the keychain
while telling the user everything was cleared.

Surface non-success statuses as a thrown KeychainError so callers can
see the failure.
Reading the originator header for codex requests and the user-agent
header for both codex and kimi from env vars allows users to avoid
fingerprinting by upstream providers. The originator header
(claude-code-proxy by default) is a unique marker that can be used to
identify proxy traffic.

- CCP_ORIGINATOR: overrides the originator header sent to codex
  (default: claude-code-proxy)
- CCP_USER_AGENT: overrides the user-agent header for both codex
  and kimi (default for codex: unset; default for kimi: KimiCLI/<version>)
provider-specific env vars take precedence over the general ones:
- CCP_CODEX_ORIGINATOR > CCP_ORIGINATOR > claude-code-proxy
- CCP_CODEX_USER_AGENT > CCP_USER_AGENT > unset
- CCP_KIMI_USER_AGENT > CCP_USER_AGENT > KimiCLI/<version>
uses BUILD_VERSION compile-time define with "dev" fallback, same
pattern as cli.ts.
the git tag is v0.0.7 but the version string should be 0.0.7. the
homebrew formula already strips it, but the BUILD_VERSION define
used by --version and the default user-agent was including the v.
When the Codex or Kimi upstream returns 429, retry up to 3 times with
exponential backoff (2s, 4s, 8s, capped at 30s) before propagating the
429 to the client. Honor the upstream Retry-After header (numeric
seconds or HTTP-date), but if it exceeds the 30s budget, give up
immediately rather than capping silently.

Shared logic lives in src/providers/retry.ts so both providers stay in
sync. Sleep is cancellable via the request signal so a client disconnect
doesn't keep us waiting. Mid-stream rate-limit errors (UpstreamStreamError
in the SSE accumulator) are intentionally left untouched — by then the
HTTP response has already started.
Without jitter, concurrent requests hitting the same upstream rate limit
would all retry at exactly 2s/4s/8s and recreate the spike. Apply equal
jitter (random in [cap/2, cap]) to the exponential fallback only —
explicit Retry-After delays are still honored as-is.
Settings can now come from a JSON file at:

  macOS: ~/.config/claude-code-proxy/config.json (deliberately not
         ~/Library — matches where auth tokens live on macOS)
  other: ${XDG_CONFIG_HOME:-$HOME/.config}/claude-code-proxy/config.json

Precedence per setting is env var > config file > built-in default. All
existing env-var-only setups keep working unchanged, including the
fallback chains (CCP_CODEX_USER_AGENT > CCP_USER_AGENT > ...).

Implementation notes:

- src/paths.ts centralizes XDG-aware directory resolution. macOS opts
  out of XDG_CONFIG_HOME (because tokens have always been at
  ~/.config/claude-code-proxy on macOS) but still honors XDG_STATE_HOME
  for the log dir, preserving the prior log.ts behavior.
- src/config.ts loads config.json once (lazy, cached). A malformed file
  warns on stderr and falls back to defaults; per-key type mismatches
  warn and skip the bad value without affecting the rest.
- Token stores and the kimi device_id file now resolve via configDir().
  To avoid logging existing users out when XDG_CONFIG_HOME is set, both
  loadAuth() and getDeviceId() also try the legacy
  ~/.config/claude-code-proxy/<provider>/... path as a read-only
  fallback.
- Empty-string env semantics are preserved exactly: legacy `??` callers
  treat "" as a real value; CCP_CODEX_MODEL and CCP_CODEX_EFFORT
  continue to treat "" as unset (the long-standing escape hatch).
- KIMI_OAUTH_HOST and KIMI_BASE_URL move from import-time constants to
  thin getter functions so config.json values apply at runtime.

README's Configuration section now documents the new file alongside the
env-var table and lists the JSON key for each setting.
shows config file path when present and lists any non-default
settings with their source (env or config).
Document the Claude Code environment variable that disables streaming-to-non-streaming fallback when routing through the proxy. The proxy always uses streaming upstream requests, so the fallback path does not avoid upstream streaming and can retry after a partial tool-use stream in a way that duplicates tool calls.
Consistency: all other project-specific env vars use the CCP_ prefix.
These were the only two that didn't.

No backward compat needed — these are debugging-only overrides unlikely
to be set by anyone.
Document the user-facing changes for the pending v0.0.9 release so
readers can see the startup config visibility improvements, Kimi env
var rename, and Claude Code fallback guidance in one place.
This lets users request Codex service tiers from config or environment without
hand-editing requests. The fast tier follows Codex's own behavior by accepting
fast as the config value while sending priority to the upstream API.

The request translator validates supported service tier values and keeps the
wire request constrained to the tiers currently supported by Codex source.

References raine#9
This lets users select Codex fast mode per request by using model names such as
gpt-5.4-fast, avoiding a proxy restart for service tier changes.

Fast aliases are registered for routing, normalized back to the base upstream
model, and translated to the same priority service tier used by first-party
Codex. Explicit service tier config still takes precedence.
Allow Codex traffic to target a configured Responses endpoint instead of
always posting to the built-in chatgpt.com URL. This lets local debugging
proxies, load balancers, and managed egress paths receive the same request
body and headers without requiring a fork or binary patch.

The new codex.baseUrl setting follows the existing config precedence and is
reported in the startup override summary when active.
Document the user-facing Codex changes accumulated since v0.0.9 so the
release notes cover service tiers, per-request fast model aliases, and the
new upstream endpoint override.
…el coverage

Adds an "anthropic" provider that forwards claude-* requests directly to
api.anthropic.com using the Claude Pro OAuth token Claude Code stores in
the macOS keychain. Registered in providers/registry.ts so the model
router resolves claude-* names to this provider instead of returning
"unknown model" 400s.

Supported models cover the full Claude 4 family including legacy names
that Claude Code 2.1.x still emits via /model and background tasks:
  - claude-opus-4-7, claude-opus-4-6
  - claude-sonnet-4-6, claude-sonnet-4-5
  - claude-haiku-4-5, claude-haiku-4-5-20251001, claude-haiku-4-4

Authentication is read-only against the Claude Code keychain entry — the
proxy never stores or refreshes tokens itself; refresh is delegated to
Claude Code. Streaming and count_tokens endpoints are passed through
without translation since the request/response shape already matches
Anthropic's API.

This unblocks running gpt-5.5 (codex) and claude-opus-4-7 (anthropic) in
the same Claude Code session — pick the right model per task via /model.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants