# Model Gateway

> Set up, update, or diagnose the local ChatGPT/Codex gateway for Claude Code's /model picker. Use for gateway setup, login, model visibility, routing, or failures.

- Skill: `eigenwise/model-gateway` (Agent Skill)
- Install (CLI): `npx skillmds@latest add eigenwise/model-gateway`
- Raw SKILL.md: https://api.skillmd.com/api/skills/eigenwise/model-gateway/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: eigenwise (https://skillmd.com/u/eigenwise)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/eigenwise/model-gateway

---


# model-gateway

Two local processes give Claude Code native access to the user's ChatGPT subscription models:
`claude-code-proxy` (does OpenAI OAuth and translates Anthropic Messages API to the Codex
backend) and a shim router this plugin owns. `ANTHROPIC_BASE_URL` points at the shim: requests
for `claude-gpt-*` models are un-prefixed and go to the proxy, everything else passes through
to api.anthropic.com with the user's normal claude.ai login. The shim's `/v1/models` advertises
Codex models with a `claude-` prefix because Claude Code's model discovery drops ids that don't
start with `claude`/`anthropic`. That prefix is shared with the real Anthropic ids, so the route
is decided by the backend family segment (`claude-gpt-*`, `claude-grok-*`), never by the prefix.

All commands: `node ~/.claude/model-gateway/model-gateway.js <command>`. SessionStart recreates this stable launcher from Claude Code's installed-plugin registry, so it follows upgrades and falls back to an installed downgrade; it reports a missing install directly.

## First-time setup

Project-local wiring is the standard setup. `env --write-project` writes the current project's `.claude/settings.local.json`, so the gateway stays configured for this project's sessions and executor worktrees without putting a machine-local endpoint in a committed file. `setup` uses the same project-local target by default.

`env --write-user` remains an opt-in shared fallback for people who deliberately want one gateway URL in `~/.claude/settings.json` across every project. Claude Code gives a current project's `settings.local.json` higher precedence, so `doctor` marks the winner `[effective]`, names both files, and says that project-local wiring wins when their gateway modes disagree.

The first-run order matters:

1. Install Model Gateway at the recommended project scope.
2. Reload plugins so the new skill is available.
3. Invoke this skill and run `setup`.
4. If setup says sign-in is needed, have the user complete `login`, then run `setup` again. The second setup finishes the download, wiring, and project confirmation.
5. After the project wiring is confirmed, tell the user to fully restart the Claude Code process for this same project before selecting a model.

A plugin reload alone does not reload settings or the picker cache. Do not tell the user to select a new row until that full restart is complete.

The SessionStart hook injects a one-line nudge while the gateway is in any half-configured
state; act on it. The user sees that same line in the transcript, because a state only they can fix used to
reach the model alone. Anything routine stays out of it, and the hook always exits 0 so the line survives:
run `ensure` yourself when you need an exit code. SessionStart waits at most 12 seconds for a newly started
gateway, then leaves its supervisor to finish in the background so it stays inside Claude Code's hook budget. `setup` is one-shot and idempotent: it downloads the claude-code-proxy binary
(sha256-verified) and starts everything. Re-running it later is also the upgrade path. Worker hot-replacement accepts the current CLI or a non-older version-shaped sibling under the same lexical and resolved parent directory. This checks filesystem layout, not marketplace registration; a development layout with the same structure can also satisfy it.

Setup fails before extraction when the required checksum asset is absent, its download fails, its contents are invalid or name a different archive, or the archive digest does not match. Report the verification failure; never skip the checksum check or run the unverified download.

Use the bundled lifecycle commands for restarts and drains. They authenticate with the per-user `~/.claude/model-gateway/control-token` file. Never print or copy this secret into a conversation, request body, or settings file. Browser-origin lifecycle requests are refused, and a replacement worker must be this CLI or a non-older sibling within the same resolved plugin cache. Development checkouts cannot hot-switch to unrelated installations. After updating, restart the calling Claude Code process to load the current control protocol. These controls do not authenticate inference requests to the separate proxy or protect against a process with the same OS-user access.

```bash
node ~/.claude/model-gateway/model-gateway.js setup
# only if setup says sign-in is needed:
node ~/.claude/model-gateway/model-gateway.js login    # browser OAuth; --device for headless
node ~/.claude/model-gateway/model-gateway.js setup    # finishes the wiring
```

`login` opens the user's browser; they complete it themselves (suggest `! node ... login` if it
needs a real TTY). `env --write-project` writes this project's `.claude/settings.local.json`.
Use `env --write-user` only when the user wants a shared fallback across projects. If both are
present, project-local wiring wins: `doctor` marks it `[effective]`, names the shadowed user file,
and fails when their gateway modes differ. All wiring changes apply to new Claude Code sessions,
so restart after the write. The Codex rows appear in `/model` labeled "From gateway".

The winning `ANTHROPIC_BASE_URL` source follows Claude Code's precedence: process environment,
current-project `.claude/settings.local.json`, project `.claude/settings.json`, then user
`~/.claude/settings.json`. A process export always wins, so settings writes cannot replace it.

When `doctor` or SessionStart says that process env shadows a wired settings file, Model Gateway is
bypassed. If the user controls the Claude Code CLI launch, they can correct or unset
`ANTHROPIC_BASE_URL`, then restart. If the host replaces that value, use the supported Claude Code CLI
on the wired project instead. Model Gateway does not support Desktop routing under forced overrides on
Windows or macOS, and settings, parent, or User-scope edits cannot be promised to win.

`env --write-user --reconcile` is confirmation-gated. Plain `env --write-user` writes the shared
user fallback, then lists recorded projects whose local URL differs without changing their files.
The confirmed command removes only Model Gateway-owned keys from those other projects'
`.claude/settings.local.json` files: its base URL, the three Claude alias pins, and static gateway
flags whose values equal plugin defaults. It leaves unrelated settings alone, skips projects already
agreeing, cannot change `process.env`, and needs a restart to affect a new session.
Discovery needs Claude Code v2.1.129+; `models` shows exactly what the shim advertises. Claude Code
only refetches gateway discovery when it has an API-key credential. OAuth subscriptions do not give it
one, so Model Gateway writes Claude Code's discovery cache whenever its advertised list changes.

Restart remains necessary to surface new rows in `/model`: Claude Code reads the picker cache once at
session start. `/reload-plugins` does not reload it. Restoring or refreshing auth on an already-wired
install needs no restart of the current Claude Code process: the proxy is a separate process, so once `login` + `setup` re-authenticate it,
the next request routes through cleanly. Settings, discovery-cache, plugin, or model-row changes do need a full restart of the affected project process. Keep these two recovery paths separate. The shim supervisor also probes the proxy's `/v1/models` endpoint
while it runs, confirming a failed probe through a fresh connection before restarting an unavailable proxy with single-flight bounded backoff. It leaves a healthy proxy
alone. Recovery output remains in `~/.claude/model-gateway/logs/guardian.log`; bounded lifecycle records in
`~/.claude/model-gateway/logs/lifecycle.jsonl` identify supervisor, worker, and proxy PIDs, orderly
stop/restart requests, observed exits, and recovery outcomes. Use `doctor` to print the evidence path and
the last observed exit. An OS termination or force-killed supervisor may leave no final record, so treat an
absent exit record as absence of evidence, not a clean shutdown. Cleanup accepts a PID record whose start time matches even when the live command line is unavailable, and deletes a record only when a readable command line contradicts it; it never stops a reused PID. On Windows, an unavailable command line can mean the process is elevated, so `doctor` names the condition and `stop` or `setup` must run from a session with the same privileges. Proxy recovery stops a listener using the shared proxy binary only
when the live process tree proves it descends from the recovering supervisor. A matching shared binary alone
never proves ownership. A failed `/v1/models` check gets one fresh-connection confirmation before recovery can stop an owned listener; a healthy confirmation resets recovery without stopping or starting the proxy. This matters when an agent is mid-orchestration
(e.g. dispatching Codex subagents through the gateway): do not tell the user to restart Claude Code just to
bring auth back, or you kill the session that was about to use it.

## Selecting models

- `/model` picker: rows like "GPT-5.6-sol (Codex)" and "Grok 4.5".
- Typed: `/model claude-gpt-5.6-sol[1m]` or `/model claude-grok-4.5[1m]`. The picker and Sidequest catalog emit those exact ids. The suffix is stripped before routing to Codex or Grok.
- `lib/runtime.js`'s exported `MODEL_WINDOW_POLICY` is the sole authority for gateway backend windows, picker aliases, advertised windows, and sentry mode. GPT-5.6 Sol, Terra, Luna, and GPT-6 Astra are measured rows. GPT ids absent from the table are deliberately advertised through its explicitly unmeasured 920k default, rather than silently inheriting a window. Grok 4.5 is a measured 500k row and now has the `[1m]` picker alias.
- Codex GPT-5.6 through the ChatGPT Codex product (the subscription login this gateway routes to, not the pay-per-token API) accepted 920,012 input tokens and refused 935,012 on 2026-09-05 through claude-code-proxy 0.1.35 (upstream 55bf0b58). The shim advertises `920000` by default. Its synthetic 413 trigger is the smaller of `CODEX_GATEWAY_COMPACT_TRIGGER` when set and the policy row's backend window minus 40k tokens. `CODEX_GATEWAY_COMPACT_TRIGGER` is a ceiling, never an override of that headroom. With the optional client `autoCompactWindow` cap at `325000`, Claude Code compacts around `292000`, so the sentry is a backstop that normally does not fire. `CODEX_GATEWAY_CONTEXT_WINDOW` overrides every advertised Codex window. Claude Code 2.1.261 ignores a settings-file `CLAUDE_CODE_MAX_CONTEXT_TOKENS` value for its own unrecognized-model resolver, so rows above 200k use their policy's recognized `[1m]` alias. That alias gives Claude Code a 1M client window, the closest available setting to the verified 920k backend window; it does not promise a 1M backend input limit. A lower explicit `autoCompactWindow` still wins. Use `/context` to inspect the selected model and effective cap.
- Claude models (opus/sonnet/fable, with or without `[1m]`) keep their OWN separate native windows
  and compaction limits: the shim forwards their requests byte-identically to Anthropic and never
  applies Codex window advertisement or error rewriting to them. The env block pins the current
  real 1M aliases (Opus, Sonnet, Fable) to `[1m]` ids so a gateway session on one gets its full 1M
  window instead of the 200k gateway default; Haiku stays unpinned (it's 200k). An `env --write-*`
  command resolves those aliases through the installed Claude CLI's credential-free headless probe;
  SessionStart refreshes its cache after the CLI changes or the cache ages out. A failed probe keeps
  the last good pin, then a shipped safe default. Set a persistent per-alias override with
  `pin --opus claude-opus-4-8[1m]` (same for `--sonnet` and `--fable`), or use `pin --opus default`
  to return to auto-detection. Overrides always win. `pin` with no arguments shows each effective
  pin and whether it is overridden. Overrides live in `~/.claude/model-gateway/pins.json`, outside
  the plugin cache. After a pin change or Claude CLI upgrade, run `env --write-project` (or
  `env --write-user` for a shared fallback) and start a new Claude Code session; changing a saved value alone cannot alter
  an open session.
- Do NOT set a
  global `CLAUDE_CODE_AUTO_COMPACT_WINDOW`: it applies to both providers and can make Codex
  `/compact` fail after history already exceeds the Codex limit.
- Caution: loading a huge reference skill (e.g. `claude-api`, ~800k chars) in a single turn can
  spike Codex context past the point proactive compaction can recover from. Prefer pulling large
  references incrementally on Codex models.
- The advertised catalog is a built-in list (proxy v0.1.10 serves no /v1/models). A `models.json` file cannot add a backend that the claude-code-proxy allowlist does not support; update the proxy through `setup` instead.
- **Claude Desktop cannot use Codex/Grok models in this version**: Desktop has its own native Gateway
  configuration, separate from Claude Code CLI settings, and can point at this shim's endpoint. But
  installed Desktop 1.49585.0 validates every Gateway model ID client-side and rejects any
  `gpt`/`codex`/non-Anthropic family marker before it reaches the picker or a session, whether the ID
  came from an explicit config entry or from discovery; a terminal `[1m]` is stripped first and does
  not change the outcome. Only a genuinely Anthropic-backed route (a real Claude alias) is usable
  there. This is a Desktop-side restriction, not a Model Gateway bug, and there is no supported
  workaround: do not suggest an Anthropic-named alias to disguise a Codex/Grok route, patch the
  Desktop binary, substitute credentials or auth, intercept TLS, or use a global env or hosts trick.
  Tell the user Desktop end-to-end Codex/Grok support is not available on this version; the Claude
  Code CLI is the verified path. VS Code success has been reported by users but is not independently
  verified here. This is version-specific and can be revisited if a future Desktop release removes
  the model-family filter.
- **RC-compat and missing Codex rows**: On inspected Claude Code 2.1.267, RC-compatibility points `ANTHROPIC_BASE_URL` at `api.anthropic.com`, which disables gateway model discovery. The rows disappear and cache refresh cannot restore them. Claude Code can accept and persist `/model claude-gpt-5.6-terra[1m]`, but that client-side action does not prove a later request reaches the gateway. The current cleaner preserves canonical `[1m]` ids; do not present it as a fix for a reported request error. RC-compatibility makes Claude Code treat the gateway as first-party and can enable experimental message threading. The shim refuses Codex/Grok thread continuations locally with HTTP 400 because those backends have no conversation state. Nothing is forwarded or rerouted. A recognizing client drops the threading beta and resends the full message history. This is version- and experiment-limited, so do not promise one refusal per session. Normal gateway mode is the verified inference path. The reported 2.1.259 client and inspected 2.1.267 client have no verified end-to-end RC result: inspected session creation uses HTTPS while compatibility transport is HTTP. A detected hosts entry or bound listener proves local HTTP transport only. Sidequest dispatch is unaffected because it resolves its explicit route marker and never uses picker discovery.
- Claude models keep working normally at the same time (passthrough path). That does not mean a
  subagent invocation can mix in a gateway id freely: the `Agent` tool's own `model` parameter is a
  fixed host enum (`sonnet`/`opus`/`haiku`/`fable`), independent of gateway or plugin state, and a
  gateway id passed there is refused. To route one subagent to a specific gateway model, give it a
  definition file instead, with a full gateway id in `model:` frontmatter, and omit `model` from the
  invocation so that frontmatter applies:

  ```markdown
  ---
  name: luna-reviewer
  description: Reviews code changes using the GPT-5.6 Luna gateway model.
  model: claude-gpt-5.6-luna[1m]
  tools: Read, Grep, Glob
  ---

  Review the diff for correctness and report findings.
  ```

  Save that as `.claude/agents/luna-reviewer.md` and start a new session before invoking it —
  definitions are read at session start, so a running session won't see one just added. Invoke with
  `subagent_type: "luna-reviewer"` and no `model` argument. This needs only Model Gateway, not
  Sidequest: a concrete gateway id needs no route marker, only Sidequest's own `claude-codex-auto` id
  does. Never edit Claude Code's built-in model aliases, the `Agent` tool schema, or the host binary —
  none of that is supported or necessary. This frontmatter contract and Model Gateway's routing for a
  concrete id are confirmed; an end-to-end custom-agent spawn through this path has not been verified
  here.
- **Codex schema compatibility**: Codex rejects some Unicode property escapes such as `\p{Cc}` and `\P{Cf}`. After deferred hydration, the shim changes only a Codex-bound provider hint, never Claude Code's host schema or an Anthropic request. It admits a missing dialect or Draft 2020-12 and only `pattern` instances reached through `properties`, compatible `patternProperties` values, `additionalProperties`, `items`, `prefixItems`, `allOf`, `anyOf`, `dependentSchemas`, `propertyNames`, or `unevaluatedProperties`/`unevaluatedItems`. Each real property atom must stand alone in a negated character class without ranges, set syntax, captures, backreferences, or a negative regex context. The shim removes only that atom and preserves every other regex byte. `not`, conditionals, `oneOf`, `contains`, references, definitions, content schemas, affected pattern-property keys, unknown containers, and unsafe regexes refuse locally with HTTP 400 naming the tool, JSON Pointer, and reason code. Tell the user it was not forwarded or rerouted. Do not claim arbitrary schemas are preserved or try to bypass that diagnostic.

## Local gateway records

Request-route logging is enabled by default. It writes metadata-only JSONL records to
`~/.claude/model-gateway/logs/request-routes.jsonl`: timestamp, backend, model, request path, route and
effort when present, safe session and agent correlation ids, and dispatch-marker length when present. It
never writes request bodies, prompts, messages, tools, authentication, or arbitrary headers. Honor a user
request to disable it by setting `CODEX_GATEWAY_REQUEST_LOG=0` before the shim starts, then restart the shim
through `setup` or `ensure`. The value is read when the shim process starts, so `/reload-plugins` does not
change an already-running shim. `CODEX_GATEWAY_REQUEST_LOG_PATH` changes the file location.

Usage observability also writes one high-water JSON file per valid session under
`~/.claude/model-gateway/request-body/`. The filename is derived from the session id. Its contents are the
largest forwarded request-body byte count observed for that session and an observation timestamp. It does
not contain the request body. No retention period is promised for either local record.

When the user asks about a failed Codex compaction and consent-gated route telemetry is available, inspect fixed compaction trace metadata only: selected/effective model, backend, upstream status, elapsed time, outcome, terminal/error code, and observed usage counters. `completed` and `empty_summary` both mean the generation finished: either a `message_stop` frame arrived, or a `message_delta` declared an `end_turn`/`stop_sequence` stop reason with no content after it and no content block left open. `empty_summary` is that finish with no visible non-whitespace text. `incomplete` means the HTTP-200 SSE stream never showed a finished generation: no stop reason, a stop reason that truncates (`max_tokens`, `refusal`, `pause_turn`, `tool_use`, anything unrecognized), content still arriving after the stop, an open block, or an upstream error frame. `compaction_terminal_code` is `message_stop` only when that frame really arrived, so a completed outcome with no terminal code means the frame was lost and the shim closed the turn itself instead of re-running the compaction. Unknown provider errors appear only as `unknown_error`. Never infer summary content or repeat prompts, response text, raw errors, headers, credentials, or environment values from diagnostics.

Claude Code's `/remote-control` only lights up when `ANTHROPIC_BASE_URL` is exactly the real
Anthropic host, which conflicts with gateway model discovery. RC-compatibility is a reversible,
opt-in local HTTP transport configuration, not a verified end-to-end Remote Control solution. At
inspected Claude Code 2.1.267 its session creation uses HTTPS while the compatibility listener is
HTTP; the reported 2.1.259 client and inspected client have no verified end-to-end RC result. A
bound port or detected hosts entry proves only that transport state. Before enabling, tell the user
that the picker rows disappear and an explicit `[1m]` id may be accepted client-side without proving
its later inference request reaches the gateway. Keep normal gateway mode as the verified inference
path. Do not suggest a cache refresh as a fix for a reported request error; the current cleaner
preserves canonical `[1m]` ids.

For the confirmation-gated procedure, use the `remote-control-compatibility` skill. It manages the
plugin-marked hosts block, creates a backup before an elevated write, reconciles gateway mode, and
checks the final transport state. Do not edit the hosts file outside that procedure. If effective
process env `ANTHROPIC_BASE_URL` is HTTPS `api.anthropic.com` (including port 443), enabling is
refused before any backup, hosts write, startup, or reconciliation because the loopback mapping
cannot serve TLS. A user-controlled Claude Code CLI launch can correct or unset that value, then
restart. If a host replaces it, use the supported Claude Code CLI on the wired project instead.
Desktop routing is unsupported under forced overrides on Windows and macOS, and settings, parent,
or User-scope edits cannot be promised to win. Disabling stays available.

- The user (never this plugin, never automatically) adds one hosts entry mapping
  `api.anthropic.com` to loopback — `127.0.0.1 api.anthropic.com` on Windows
  (`C:\Windows\System32\drivers\etc\hosts`, needs Administrator), macOS, and Linux (`/etc/hosts`,
  needs `sudo`). If asked to help with this, tell the user the exact line and file, and that they
  need elevated privileges to save it; do not attempt to edit the hosts file yourself.
- `ensure`/`setup`/`doctor` detect the entry (read-only) and, only after confirming the shim can
  actually bind loopback port 80, switch `ANTHROPIC_BASE_URL` to `http://api.anthropic.com` and
  start a second listener on port 80 next to the usual `127.0.0.1:18764`. Exactly one line tells
  the user to restart Claude Code when the mode changes either direction.
- Removing the hosts entry, or port 80 becoming unavailable (no permission, or something else is
  using it), reverts to default mode automatically, again with one restart line.
- `doctor` reports the hosts entry (if any), whether port 80 actually bound (and why not if it
  didn't), and which mode each settings scope (user/project) is wired to.
- Test/advanced overrides: `CODEX_GATEWAY_HOSTS_FILE` (custom hosts path), `CODEX_GATEWAY_COMPAT_PORT`
  (port other than 80). Neither is needed for normal use.

## Day-2 operations

```bash
... status      # what's running
... doctor      # binary, auth, ports, model count, settings wiring
... ensure      # start whatever is down (SessionStart hook runs this with --quiet)
... stop
... env --remove   # unwire Claude Code (do this BEFORE uninstalling the plugin)
```

`doctor` prints the full model-window table: backend and picker ids, backend and advertised windows,
Claude Code's resolved client window and compaction point, sentry mode and trigger, and the measurement
date. It includes Codex, Grok, and native Claude pin rows. Its model-id check is useful for stale shim ids,
but a `PASS` does not prove every supported model is present. A proxy from 0.1.14 through 0.1.35 can
omit GPT-6 Astra while this check passes. If Astra is missing, check the installed and serving proxy
version, rerun `setup` to fetch the latest release, and fully restart Claude Code. Astra requires
claude-code-proxy 0.1.36 or newer. A `models.json` edit cannot add a backend that the proxy allowlist
does not support. A `FAIL` naming missing and extra ids means the shim is stale even when its version
matches: restart it through the normal `ensure` or `setup` path, then restart Claude Code sessions so the
picker re-discovers the rows.

Logs live in `~/.claude/model-gateway/logs/`. `guardian.log` has recovery output; `lifecycle.jsonl`
has bounded process evidence that `doctor` summarizes. Ports: shim 18764, proxy 18765 (override with
`CODEX_GATEWAY_PORT` / `CODEX_GATEWAY_PROXY_PORT`, but the env block and running processes must
agree).

## Failure modes worth knowing

- **Every request fails after wiring**: a SessionStart hook can time out while it starts the shim. Claude Code cancels that hook, and a directly spawned supervisor can die with its process tree before it records an exit. Model Gateway launches the supervisor outside that tree and stops waiting before the hook budget, but if this is an older install or it still repeats, run
  `doctor`, check logs. Worst case `env --remove` restores stock behavior instantly.
- **Codex sessions drop while this plugin's suite runs**: this version scopes fixture cleanup to
  the test gateway home, so the suite never touches the installed gateway. If it happens after
  updating, run `doctor` and include its supervisor conflict line and lifecycle evidence.
- **Codex models error, Claude models fine**: proxy or OpenAI side. Check `login` state
  (`doctor` shows auth), then proxy log. OpenAI gates non-Codex clients by request fingerprint;
  when they tighten it, requests die mid-stream until claude-code-proxy ships a fix, so
  suggest re-running `setup` (it fetches the latest release).
- **`doctor` says `upstream-unavailable`**: a final Codex inference failed in the last 60 seconds.
  It records completed request outcomes, not `/v1/models` or a health check, and clears only after
  a completed successful Codex response. The 60-second expiry means there is no recent failure
  evidence, not that Codex is live. An `upstream-blocked` OpenAI/auth rejection stays separate
  and does not expire; `setup` deliberately clears either record. Sidequest consumes a cached
  catalog and can lag this state by up to five minutes.
- **Startup, recovery, restart, or drain refuses to touch a listener**: each ownership probe shares one
  `CODEX_GATEWAY_PROBE_TIMEOUT_MS` budget (2 seconds by default, 8 seconds on Windows, where the Win32_Process
  lookup itself typically takes 1.8-2.4 seconds). When that budget expires, the refusal says so, names the elapsed
  budget, and points to `CODEX_GATEWAY_PROBE_TIMEOUT_MS` as the override. A malformed process result or
  unrecognized command remains an ownership-unknown refusal without the timeout guidance. Startup records
  `owner-unknown` and leaves that listener untouched; recovery leaves it for the next tick. A confirmed foreign
  owner is also left untouched. Probe children are stopped with the supervisor, so they cannot keep a test fixture home open.
- **`doctor` shows `Not authenticated` right after an upgrade**: bumping the proxy binary (e.g.
  0.1.10 → 0.1.17 via `setup`) can invalidate the credential the old version accepted — the new
  binary reads it as not authenticated and `setup` stops before wiring. Fix: re-run `login`, then
  `setup` again to finish. Until then every Codex model is down, so any run that routes to
  Codex (a whole sidequest board of Codex-tier tickets, for one) stalls entirely.
- **GPT-6 Astra is missing from `/model`**: do not diagnose account access first. Check the installed and serving claude-code-proxy version. Astra requires 0.1.36 or newer; 0.1.35 does not include its backend allowlist, while the current doctor floor can still pass. Re-run `setup` to fetch the latest GitHub release, then fully restart Claude Code. Do not propose `models.json`: it cannot add a backend the proxy does not allow.
- **No "From gateway" rows in /model**: discovery is off (`CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY`
  missing), Claude Code < v2.1.129, `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` is set (it
  disables discovery), or RC-compatibility is active. Claude Code only refetches discovery with an
  API-key credential, so OAuth users rely on Model Gateway's cache write. Run `doctor` to check the
  cache, then restart Claude Code after it updates; `/reload-plugins` does not reload picker rows.
- **Thinking/reasoning**: the Codex backend doesn't return thinking blocks into Claude Code's
  UI; that's an upstream limitation, not a bug here.
- **Permission mode flips to "accept edits on" during Codex sessions**: caused by GPT models
  calling the plan-mode tools; an approved ExitPlanMode downgrades the mode instead of
  restoring it (anthropics/claude-code#39973). The shim strips EnterPlanMode/ExitPlanMode from
  Codex-bound requests since 0.2.1, so this shouldn't recur; if it does, make sure the shim was
  restarted (`stop` + `start`). Shift+Tab restores the mode in an affected session. Escape
  hatch to re-enable plan tools: `CODEX_GATEWAY_KEEP_PLAN_TOOLS=1`.

