codex-as-api 0.6.5__tar.gz → 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. {codex_as_api-0.6.5 → codex_as_api-0.7.0}/.gitignore +1 -0
  2. {codex_as_api-0.6.5 → codex_as_api-0.7.0}/PKG-INFO +164 -80
  3. {codex_as_api-0.6.5 → codex_as_api-0.7.0}/README.md +159 -74
  4. {codex_as_api-0.6.5 → codex_as_api-0.7.0}/RELEASES.md +72 -0
  5. codex_as_api-0.7.0/config/codex-upstream-contract.json +48 -0
  6. {codex_as_api-0.6.5 → codex_as_api-0.7.0}/pyproject.toml +11 -7
  7. codex_as_api-0.7.0/requirements.txt +8 -0
  8. codex_as_api-0.7.0/rust/THIRD_PARTY_NOTICES.md +29069 -0
  9. codex_as_api-0.7.0/scripts/check_no_static_model_catalog.py +202 -0
  10. codex_as_api-0.7.0/scripts/check_package_versions.py +235 -0
  11. codex_as_api-0.7.0/scripts/check_registry_release.py +483 -0
  12. codex_as_api-0.7.0/scripts/check_release_artifacts.py +361 -0
  13. codex_as_api-0.7.0/scripts/generate_rust_third_party_notices.py +311 -0
  14. codex_as_api-0.7.0/scripts/package_rust_release.py +74 -0
  15. {codex_as_api-0.6.5 → codex_as_api-0.7.0}/src/codex_as_api/__init__.py +1 -1
  16. codex_as_api-0.7.0/src/codex_as_api/anthropic_adapter.py +1084 -0
  17. codex_as_api-0.7.0/src/codex_as_api/auth.py +595 -0
  18. {codex_as_api-0.6.5 → codex_as_api-0.7.0}/src/codex_as_api/codex_config.py +35 -14
  19. codex_as_api-0.7.0/src/codex_as_api/messages.py +172 -0
  20. codex_as_api-0.7.0/src/codex_as_api/model_capabilities.py +694 -0
  21. codex_as_api-0.7.0/src/codex_as_api/protocol.py +144 -0
  22. codex_as_api-0.7.0/src/codex_as_api/provider.py +2753 -0
  23. codex_as_api-0.7.0/src/codex_as_api/server.py +2639 -0
  24. codex_as_api-0.7.0/src/codex_as_api/strict_json.py +71 -0
  25. codex_as_api-0.7.0/tests/conftest.py +136 -0
  26. {codex_as_api-0.6.5 → codex_as_api-0.7.0}/tests/test_anthropic_adapter.py +804 -169
  27. codex_as_api-0.7.0/tests/test_auth.py +1288 -0
  28. {codex_as_api-0.6.5 → codex_as_api-0.7.0}/tests/test_codex_config.py +52 -11
  29. {codex_as_api-0.6.5 → codex_as_api-0.7.0}/tests/test_messages.py +40 -34
  30. codex_as_api-0.7.0/tests/test_model_capabilities.py +795 -0
  31. codex_as_api-0.7.0/tests/test_no_static_model_catalog.py +137 -0
  32. codex_as_api-0.7.0/tests/test_package_versions.py +155 -0
  33. {codex_as_api-0.6.5 → codex_as_api-0.7.0}/tests/test_protocol.py +92 -28
  34. codex_as_api-0.7.0/tests/test_provider.py +4206 -0
  35. codex_as_api-0.7.0/tests/test_registry_release.py +501 -0
  36. codex_as_api-0.7.0/tests/test_release_artifacts.py +322 -0
  37. codex_as_api-0.7.0/tests/test_rust_third_party_notices.py +156 -0
  38. codex_as_api-0.7.0/tests/test_server.py +5893 -0
  39. codex_as_api-0.7.0/tests/test_strict_json.py +32 -0
  40. codex_as_api-0.6.5/config/codex-upstream-contract.json +0 -86
  41. codex_as_api-0.6.5/config/model-capabilities.json +0 -128
  42. codex_as_api-0.6.5/requirements-server.txt +0 -3
  43. codex_as_api-0.6.5/requirements.txt +0 -9
  44. codex_as_api-0.6.5/src/codex_as_api/anthropic_adapter.py +0 -938
  45. codex_as_api-0.6.5/src/codex_as_api/auth.py +0 -383
  46. codex_as_api-0.6.5/src/codex_as_api/messages.py +0 -121
  47. codex_as_api-0.6.5/src/codex_as_api/model_capabilities.py +0 -251
  48. codex_as_api-0.6.5/src/codex_as_api/protocol.py +0 -99
  49. codex_as_api-0.6.5/src/codex_as_api/provider.py +0 -1632
  50. codex_as_api-0.6.5/src/codex_as_api/server.py +0 -1335
  51. codex_as_api-0.6.5/tests/conftest.py +0 -54
  52. codex_as_api-0.6.5/tests/test_auth.py +0 -730
  53. codex_as_api-0.6.5/tests/test_model_capabilities.py +0 -104
  54. codex_as_api-0.6.5/tests/test_package_versions.py +0 -94
  55. codex_as_api-0.6.5/tests/test_provider.py +0 -1748
  56. codex_as_api-0.6.5/tests/test_server.py +0 -2692
  57. {codex_as_api-0.6.5 → codex_as_api-0.7.0}/LICENSE +0 -0
  58. {codex_as_api-0.6.5 → codex_as_api-0.7.0}/THIRD_PARTY_NOTICES.md +0 -0
  59. {codex_as_api-0.6.5 → codex_as_api-0.7.0}/config/o200k_base.tiktoken +0 -0
  60. {codex_as_api-0.6.5 → codex_as_api-0.7.0}/src/codex_as_api/o200k_tokenizer.py +0 -0
  61. {codex_as_api-0.6.5 → codex_as_api-0.7.0}/tests/__init__.py +0 -0
  62. {codex_as_api-0.6.5 → codex_as_api-0.7.0}/tests/fixtures/o200k_base_encode_ordinary.json +0 -0
  63. {codex_as_api-0.6.5 → codex_as_api-0.7.0}/tests/test_o200k_tokenizer.py +0 -0
@@ -11,3 +11,4 @@ rust/target/
11
11
  uv.lock
12
12
  ts/node_modules/
13
13
  ts/dist/
14
+ .pnpm-store/
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: codex-as-api
3
- Version: 0.6.5
3
+ Version: 0.7.0
4
4
  Summary: Use ChatGPT/Codex OAuth as a local OpenAI-compatible API server.
5
5
  Project-URL: Homepage, https://github.com/Eunho-J/codex-as-api
6
6
  Project-URL: Repository, https://github.com/Eunho-J/codex-as-api
@@ -18,18 +18,17 @@ Classifier: Programming Language :: Python :: 3.12
18
18
  Classifier: Programming Language :: Python :: 3.13
19
19
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
20
20
  Requires-Python: >=3.10
21
+ Requires-Dist: fastapi>=0.100
22
+ Requires-Dist: pydantic>=2.0
21
23
  Requires-Dist: regex
22
24
  Requires-Dist: tomli>=2; python_version < '3.11'
25
+ Requires-Dist: uvicorn>=0.23
23
26
  Provides-Extra: dev
24
27
  Requires-Dist: httpx; extra == 'dev'
25
28
  Requires-Dist: mypy; extra == 'dev'
26
29
  Requires-Dist: pytest; extra == 'dev'
27
30
  Requires-Dist: ruff; extra == 'dev'
28
31
  Requires-Dist: types-regex; extra == 'dev'
29
- Provides-Extra: server
30
- Requires-Dist: fastapi>=0.100; extra == 'server'
31
- Requires-Dist: pydantic>=2.0; extra == 'server'
32
- Requires-Dist: uvicorn>=0.23; extra == 'server'
33
32
  Description-Content-Type: text/markdown
34
33
 
35
34
  # codex-as-api
@@ -43,16 +42,16 @@ Use ChatGPT / Codex OAuth as a local OpenAI-compatible API server.
43
42
 
44
43
  ## Features
45
44
 
46
- - **OpenAI & Anthropic compatible** — `POST /v1/chat/completions` and `POST /v1/messages` endpoints
45
+ - **OpenAI & Anthropic compatible** — Chat Completions, Messages, and `GET /v1/models`
47
46
  - **Claude Code ready** — use Codex models directly from Claude Code CLI
48
47
  - **Streaming** — full SSE streaming for both OpenAI and Anthropic protocols
49
48
  - **Tool calling** — function calls, tool results, and parallel tool calls
50
49
  - **Image support** — generation, inspection, multimodal Chat input, and capability-gated `original` image detail in classic Responses
51
- - **Reasoning** — configurable effort/context, `standard` mode compatibility, persisted reasoning, and streaming thinking content
52
- - **Codex features** — session-aware `prompt_cache_key`, process-local Chat `response_id` continuation, subagent headers, and remote compaction
50
+ - **Reasoning** — capability-checked effort/context, persisted reasoning, and explicit Codex reasoning extensions without fabricated Anthropic signatures
51
+ - **Codex features** — live authenticated model discovery, session-aware `prompt_cache_key`, account-scoped Chat continuation, subagent headers, and remote compaction
53
52
  - **Codex config aware** — reads `CODEX_HOME` / `~/.codex/config.toml` for model, reasoning-effort, and context-window settings
54
53
  - **Token estimate & compaction helpers** — Anthropic-compatible `/v1/messages/count_tokens` and `/v1/messages/compact`
55
- - **Auto auth** — reads `~/.codex/auth.json` and auto-refreshes OAuth tokens
54
+ - **Managed ChatGPT auth** — reads official `~/.codex/auth.json` token data and refreshes managed OAuth tokens
56
55
  - **3 implementations** — Python, TypeScript (npm), and Rust — identical behavior
57
56
 
58
57
  ## What it does
@@ -70,9 +69,17 @@ npm install -g @openai/codex
70
69
  codex login
71
70
  ```
72
71
 
73
- The server reads that file to obtain and refresh ChatGPT OAuth tokens automatically.
72
+ The server reads the official nested `tokens` object from that file and refreshes
73
+ managed ChatGPT OAuth tokens automatically. `auth_mode` may be omitted, `null`,
74
+ or `"chatgpt"`.
74
75
 
75
- `tokens` and latest root-level `access_token` / `refresh_token` / `id_token` auth files are supported. `personal_access_token`-only, `agent_identity`-only, and `bedrock_api_key`-only auth files are not supported for the ChatGPT OAuth backend; rerun `codex login` if you hit that diagnostic.
76
+ Unofficial root-level `access_token` / `refresh_token` / `id_token` fields and
77
+ noncanonical auth-mode aliases are rejected. The official
78
+ `"chatgptAuthTokens"` mode is also unsupported: those credentials are owned and
79
+ refreshed by an external host application, while this standalone proxy has no
80
+ host refresh callback. `personal_access_token`-, `agent_identity`-, and
81
+ Bedrock-only auth files are not ChatGPT OAuth credentials; run `codex login` to
82
+ create the supported managed-token file.
76
83
 
77
84
  ## Install & Run
78
85
 
@@ -97,7 +104,7 @@ Or from source:
97
104
  ```bash
98
105
  git clone https://github.com/Eunho-J/codex-as-api.git
99
106
  cd codex-as-api
100
- pip install -e ".[server]"
107
+ pip install -e .
101
108
  codex-as-api
102
109
  ```
103
110
 
@@ -139,7 +146,7 @@ Can also be used as a library:
139
146
  import { ChatGPTOAuthProvider, createApp } from "codex-as-api";
140
147
 
141
148
  // Use the provider directly
142
- const provider = new ChatGPTOAuthProvider({ model: "gpt-5.5" });
149
+ const provider = new ChatGPTOAuthProvider({ model: "gpt-5.6-sol" });
143
150
  const response = await provider.chat(
144
151
  [
145
152
  { role: "system", content: "You are helpful." },
@@ -163,9 +170,8 @@ Environment variables (Python, Rust, and TypeScript):
163
170
  |----------|---------|-------------|
164
171
  | `CODEX_AS_API_HOST` | `127.0.0.1` | Bind address |
165
172
  | `CODEX_AS_API_PORT` | `18080` | Listen port |
166
- | `CODEX_AS_API_MODEL` | `~/.codex/config.toml` `model`, else `gpt-5.5` | Model identifier passed to Codex backend |
173
+ | `CODEX_AS_API_MODEL` | `~/.codex/config.toml` `model`, else live default | Model identifier passed to Codex backend |
167
174
  | `CODEX_AS_API_AUTH_PATH` | `~/.codex/auth.json` | Path to OAuth credentials file |
168
- | `CODEX_AS_API_CODEX_CLI_VERSION` | `0.147.0` | Override the validated Codex compatibility baseline identified in backend request `User-Agent` headers |
169
175
  | `CODEX_AS_API_RESPONSES_LITE` | `auto` | Responses Lite mode: `auto`, `on`, or `off` |
170
176
  | `CODEX_AS_API_CODEX_METADATA` | `off` | Add Codex-style per-turn `client_metadata` and related backend headers |
171
177
  | `CODEX_HOME` | `~/.codex` | Codex home directory used for `auth.json` and `config.toml` discovery |
@@ -176,29 +182,18 @@ The server also reads root-level Codex CLI settings from `~/.codex/config.toml`:
176
182
  model = "gpt-5.6-sol"
177
183
  model_reasoning_effort = "high"
178
184
 
179
- # Optional overrides. Without them, known models use the bundled Codex catalog values.
185
+ # Optional explicit context values. A published live maximum still clamps them.
180
186
  model_context_window = 272000
181
187
  model_auto_compact_token_limit = 244800
182
188
  ```
183
189
 
184
- `CODEX_AS_API_MODEL` overrides the Codex config model. A request-level `reasoning_effort` overrides `model_reasoning_effort`; when both are omitted, known models use the Codex catalog default. The effective model, reasoning setting, and context settings are exposed from `/health`; context settings are also returned by Anthropic token-count responses.
190
+ `CODEX_AS_API_MODEL` overrides the Codex config model. Reasoning-effort precedence is request, Codex config, then the authenticated model catalog default when present. When no request or configured model exists, the proxy selects the catalog's first `visibility: "list"` model by ascending priority, matching Codex. The effective model, reasoning setting, catalog status, and context settings are exposed from `/health`; optional reasoning and context values are `null` when neither config nor the live catalog supplies them. Context settings are also returned by Anthropic token-count responses when available.
185
191
 
186
192
  ### Supported Models
187
193
 
188
- | Model | Description |
189
- |-------|-------------|
190
- | `gpt-5.6` | Public alias; resolved to `gpt-5.6-sol` before the Codex OAuth request |
191
- | `gpt-5.6-sol` | Latest frontier agentic coding model; defaults to `low` effort in Codex |
192
- | `gpt-5.6-terra` | Balanced agentic coding model for everyday work; defaults to `medium` effort |
193
- | `gpt-5.6-luna` | Fast and affordable agentic coding model; defaults to `medium` effort |
194
- | `gpt-5.5` | Frontier model for complex coding, research, and real-world work |
195
- | `gpt-5.4` | Strong model for everyday coding |
196
- | `gpt-5.4-mini` | Small, fast, and cost-efficient model for simpler coding tasks |
197
- | `gpt-5.3-codex` | Coding-optimized model |
198
- | `gpt-5.3-codex-spark` | Ultra-fast coding model |
199
- | `gpt-5.2` | Previous generation model |
200
-
201
- Model capability behavior is driven by `config/model-capabilities.json` across Python, TypeScript, and Rust. The public `gpt-5.6` alias uses Sol's private Codex capability entry. The GPT-5.6 entries use the official Codex Responses Lite contract and a 272,000-token **Codex OAuth** context-window maximum; do not substitute the larger public API context figures for this backend. Larger config overrides are clamped to 272,000. Unknown models use conservative behavior: classic Responses payloads, no assumed parallel tool support, no automatic verbosity, reasoning-effort, or service-tier fields, and, without config overrides, the legacy 200,000-token context / 160,000-token compact thresholds.
194
+ Models and capabilities come only from the authenticated Codex `/models` response. The proxy keeps each account/base-URL/client-version snapshot in memory for 300 seconds, invalidates it when a Responses `X-Models-Etag` changes, and never serves a static, stale, or cross-account fallback. An unavailable or malformed refresh returns `503 catalog_unavailable` before streaming headers are sent; an upstream HTTP rejection after the one allowed OAuth refresh preserves its status as `upstream_error`.
195
+
196
+ Use `GET /v1/models` instead of relying on a documentation list. Every row in the fresh authenticated snapshot is returned, including its exact slug, visibility, `supported_in_api`, reasoning-summary controls, and opaque `comp_hash` compatibility identifier when supplied. A valid empty catalog returns `200` with `data: []`; health and model-default resolution then return `503 catalog_unavailable` rather than selecting a fallback. Requests must use an exact live slug; unknown explicit IDs return `404 model_not_found`.
202
197
 
203
198
  To use a different port:
204
199
 
@@ -214,6 +209,14 @@ CODEX_AS_API_HOST=0.0.0.0 codex-as-api
214
209
 
215
210
  ## API Endpoints
216
211
 
212
+ ### `GET /v1/models`
213
+
214
+ Returns the current account's authenticated Codex model catalog using an OpenAI-compatible list envelope. The response includes safe routing and capability metadata but omits backend instructions and model messages.
215
+
216
+ ```bash
217
+ curl http://localhost:18080/v1/models
218
+ ```
219
+
217
220
  ### `POST /v1/chat/completions`
218
221
 
219
222
  Standard OpenAI chat completions. Supports streaming (`stream: true`) and non-streaming.
@@ -273,15 +276,25 @@ curl http://localhost:18080/v1/chat/completions \
273
276
  }'
274
277
  ```
275
278
 
279
+ `reasoning_content` is a response-only proxy extension. Chat request messages
280
+ cannot replay that field losslessly and reject it instead of silently dropping
281
+ it; continuation uses the complete provider Response items saved behind
282
+ `previous_response_id`.
283
+
276
284
  ### `POST /v1/messages`
277
285
 
278
- Anthropic Messages API compatible endpoint. Supports streaming (`stream: true`) and non-streaming. A client model that matches the bundled Codex catalog, such as `gpt-5.6-sol`, is used for the backend call. Anthropic names and other unknown model IDs use the configured `CODEX_AS_API_MODEL` fallback. The response always preserves the client-supplied model name.
286
+ Claude Code compatibility endpoint using the Anthropic Messages shape. It supports streaming (`stream: true`) and non-streaming only for requests carrying one valid `x-claude-code-session-id` header. A live Codex model ID routes directly. A recognized `claude-*` facade ID requires an explicit `CODEX_AS_API_MODEL` or Codex config model that is present in the same fresh catalog; it is not silently routed to a default. Other unknown IDs fail. The response preserves the client-supplied model name.
287
+
288
+ This compatibility endpoint is scoped to Claude Code requests. The proxy accepts
289
+ and strips the non-forwarded `max_tokens` and `cache_control` compatibility
290
+ fields only when one valid `x-claude-code-session-id` header is present.
279
291
 
280
292
  ```bash
281
293
  curl http://localhost:18080/v1/messages \
282
294
  -H "Content-Type: application/json" \
283
295
  -H "x-api-key: unused" \
284
296
  -H "anthropic-version: 2023-06-01" \
297
+ -H "x-claude-code-session-id: example-session" \
285
298
  -d '{
286
299
  "model": "claude-sonnet-4-6",
287
300
  "max_tokens": 200,
@@ -299,6 +312,7 @@ curl -N http://localhost:18080/v1/messages \
299
312
  -H "Content-Type: application/json" \
300
313
  -H "x-api-key: unused" \
301
314
  -H "anthropic-version: 2023-06-01" \
315
+ -H "x-claude-code-session-id: example-session" \
302
316
  -d '{
303
317
  "model": "claude-sonnet-4-6",
304
318
  "max_tokens": 200,
@@ -310,6 +324,35 @@ curl -N http://localhost:18080/v1/messages \
310
324
  }'
311
325
  ```
312
326
 
327
+ Codex reasoning summaries are not Anthropic signed-thinking blocks. A non-streaming response exposes real upstream reasoning only through the top-level `codex_reasoning` proxy extension. A stream emits `event: codex_reasoning_delta` with `type: "codex_reasoning_delta"`. The proxy never fabricates `thinking`, `thinking_delta`, or `signature_delta` data.
328
+
329
+ Hosted Anthropic `web_search` declarations are rejected with HTTP 400 on
330
+ Messages, token-counting, and compact requests. OpenAI Responses cannot supply
331
+ the Anthropic server-tool provenance and encrypted lifecycle fields required to
332
+ translate a hosted result losslessly, so the gateway does not approximate that
333
+ protocol. Provider output containing an unexpected hosted web-search call is an
334
+ upstream protocol error instead of a fabricated Anthropic result block.
335
+
336
+ Completed responses require authoritative final token usage and a real
337
+ `response.completed` event. Emitted function calls map to `tool_calls` or
338
+ `tool_use`; otherwise a missing, `null`, or `true` `end_turn` maps to `stop` or
339
+ `end_turn`, matching pinned Codex, where only explicit `false` requests another
340
+ sampling turn. An explicit `false` completion without a representable client
341
+ tool call is an upstream protocol error because this proxy does not implement
342
+ Codex's internal follow-up loop. Non-streaming requests return HTTP 502 when
343
+ the completion is nonterminal or final usage is absent; a stream that has
344
+ already started emits an Anthropic `error` event and never emits a successful
345
+ `message_stop`. The final response includes every required usage field, using
346
+ `null` only where the Anthropic type permits it, plus required container,
347
+ citation, context-management, and direct-caller fields. To preserve Claude
348
+ Code's realtime behavior, `message_start` is sent immediately without guessed
349
+ input-token counts; this narrow stream-start shape is Claude Code compatibility
350
+ behavior, not generic Anthropic API parity.
351
+
352
+ Generated `tool_use` blocks with `caller: {"type":"direct"}` and empty
353
+ assistant content arrays can be replayed through the request adapter. Malformed,
354
+ null, or server-tool caller variants still fail before transport.
355
+
313
356
  ### Identifier and cache behavior
314
357
 
315
358
  The two compatibility facades intentionally use the similarly named fields for
@@ -320,24 +363,31 @@ different jobs:
320
363
  | `client_metadata.session_id` | Forwarded to Codex, used as the default `prompt_cache_key`, and required when Codex metadata mode is enabled | Not used or forwarded |
321
364
  | `client_metadata.thread_id` | Codex metadata thread identity; defaults to `session_id` for a root request and preserves an explicit child thread | Not used or synthesized |
322
365
  | `prompt_cache_key` | Explicit value wins; otherwise a non-empty `client_metadata.session_id` is used; otherwise omitted | Optional proxy extension; explicit value wins, otherwise the Claude Code session header is hashed |
323
- | `x-claude-code-session-id` | Not used | SHA-256 of `codex-as-api:claude-code-session:<exact header value>` supplies cache affinity only |
366
+ | `x-claude-code-session-id` | Not used | Required for the non-forwarded `max_tokens` and `cache_control` compatibility fields; its exact value also supplies hashed cache affinity |
324
367
  | `previous_response_id` | Resolves a known entry from the 256-chain process-local history and replays full input/output over HTTP; never forwarded | Non-null values return Anthropic-style HTTP 400 because normal Messages is stateless |
325
- | `cache_control` | Not an OpenAI Chat control | Validated as an Anthropic compatibility hint, then stripped before Codex transport |
368
+ | `cache_control` | Not an OpenAI Chat control | Accepted only with the Claude Code session header, validated, then stripped before Codex transport |
326
369
 
327
370
  Anthropic `cache_control` is a cache-boundary annotation, not an identifier.
328
371
  This proxy accepts only `{"type":"ephemeral"}` with optional `ttl: "5m"` or
329
- `"1h"` at request, system, message, content-block, and tool locations. The
372
+ `"1h"` at the top level and on supported system/content blocks and tools. The
330
373
  private Codex request has no Anthropic breakpoint or TTL fields, so accepted
331
374
  hints do not reproduce Anthropic caching semantics. Normal `/v1/messages`
332
375
  responses do not expose the Chat-only `response_id`. The custom
333
376
  `/v1/messages/compact` endpoint retains its documented local
334
377
  `previous_response_id` support.
335
378
 
379
+ The Anthropic type permits `cache_control: null`; the proxy treats that exact
380
+ value as omission. A non-null cache-control object is validated in full and is
381
+ accepted only on a Claude Code request.
382
+
336
383
  ### `POST /v1/messages/count_tokens`
337
384
 
338
- Anthropic-compatible token counting helper. Codex OAuth does not expose a count-only endpoint equivalent to Anthropic's native API, so this route counts text locally and returns context-window metadata for the effective backend model. Normalized model-visible messages, tool calls, tool-result metadata, reasoning, and tool schemas are counted once. Request-envelope and generation-control fields are excluded, and image inputs use a separate fixed estimate so inline base64 data is not counted as text.
385
+ Anthropic-compatible token counting helper. Codex OAuth does not expose a count-only endpoint equivalent to Anthropic's native API, so this route counts text locally and returns context-window metadata for the effective backend model. Normalized model-visible messages, tool calls, tool-result metadata, reasoning, and tool schemas are counted once. Request-envelope fields and the approved Claude Code `max_tokens` compatibility hint are excluded; other unsupported generation controls fail instead of becoming no-ops. Image inputs use a separate fixed estimate so inline base64 data is not counted as text.
339
386
 
340
- GPT-5-family text uses a bundled port of official `tiktoken` `o200k_base` `encode_ordinary`: the same Unicode pre-tokenization regex, byte-pair merge algorithm, and merge-rank data are implemented in Python, TypeScript, and Rust. Official `tiktoken` maps `gpt-5` and its `gpt-5-`-prefixed variants to [`o200k_base`](https://github.com/openai/tiktoken/blob/08a5f3b2c987ada4fc5aa1f16c643c203fa8acaa/tiktoken/model.py#L7-L20); this project applies that GPT-5-family encoding to the bundled `gpt-5.*` Codex model IDs. The project does not depend on a `tiktoken` package or download encoding data at runtime. The last upstream synchronization check was **2026-07-14**, against [`tiktoken` 0.13.0 at `08a5f3b`](https://github.com/openai/tiktoken/tree/08a5f3b2c987ada4fc5aa1f16c643c203fa8acaa); the bundled rank file SHA-256 is `446a9538cb6c348e3516120d7c08b09f57c36495e2acfffe59a5bf8b0cfb1a2d`.
387
+ `max_tokens` and `cache_control` remain Claude Code-only compatibility hints on
388
+ this route and require `x-claude-code-session-id` when present.
389
+
390
+ Text estimation uses a bundled port of official `tiktoken` `o200k_base` `encode_ordinary`: the same Unicode pre-tokenization regex, byte-pair merge algorithm, and merge-rank data are implemented in Python, TypeScript, and Rust. It is a local estimator, not model metadata and not a substitute for a provider count API. The project does not depend on a `tiktoken` package or download encoding data at runtime. The last upstream synchronization check was **2026-07-14**, against [`tiktoken` 0.13.0 at `08a5f3b`](https://github.com/openai/tiktoken/tree/08a5f3b2c987ada4fc5aa1f16c643c203fa8acaa); the bundled rank file SHA-256 is `446a9538cb6c348e3516120d7c08b09f57c36495e2acfffe59a5bf8b0cfb1a2d`.
341
391
 
342
392
  Official Codex also has a [`ceil(UTF-8 bytes / 4)` truncation helper](https://github.com/openai/codex/blob/bd2de422aa287b97b06ca6425a10935bcf1b3731/codex-rs/utils/string/src/truncate.rs#L4-L84), but Codex documents the history estimate using that helper as [a coarse lower bound rather than a tokenizer-accurate count](https://github.com/openai/codex/blob/bd2de422aa287b97b06ca6425a10935bcf1b3731/codex-rs/core/src/context_manager/history.rs#L162-L186). This endpoint therefore uses exact `o200k_base` ordinary text tokenization instead. The complete request count remains an estimate because protocol-wrapper overhead and image cost are local constants, but the former byte-as-token and raw-payload double count that could overstate ordinary Claude Code requests by about 8x is removed.
343
393
 
@@ -354,7 +404,7 @@ curl http://localhost:18080/v1/messages/count_tokens \
354
404
 
355
405
  ### `POST /v1/messages/compact`
356
406
 
357
- Anthropic-compatible alias for remote conversation compaction. Accepts Anthropic Messages-shaped bodies and returns compacted checkpoint content. Bundled GPT model IDs select the matching backend model, and Claude Code effort and Fast Mode controls use the same mappings as `/v1/messages`.
407
+ Anthropic-compatible alias for remote conversation compaction. Accepts Anthropic Messages-shaped bodies and returns compacted checkpoint content. Live GPT model IDs select the matching backend model, and Claude Code effort and Fast Mode controls use the same mappings as `/v1/messages`. Its required non-forwarded `max_tokens` and any `cache_control` hints require one valid `x-claude-code-session-id` header.
358
408
 
359
409
  ### `POST /v1/images/generations`
360
410
 
@@ -366,7 +416,7 @@ curl http://localhost:18080/v1/images/generations \
366
416
  -d '{
367
417
  "model": "gpt-5.5",
368
418
  "prompt": "a futuristic city at sunset",
369
- "size": "1024x1024"
419
+ "size": "auto"
370
420
  }'
371
421
  ```
372
422
 
@@ -391,7 +441,7 @@ curl http://localhost:18080/v1/inspect \
391
441
 
392
442
  Compact a conversation into a checkpoint for continuation (custom endpoint). `/v1/messages/compact` provides the Anthropic-compatible alias.
393
443
 
394
- Compact accepts the existing private Codex `reasoning_effort` (or matching `reasoning.effort`) plus `prompt_cache_key`, supported `service_tier`, `text`, and top-level `verbosity`. A known process-local `previous_response_id` is resolved to its saved Response items and replayed as full compact input; the field is never forwarded to private Codex. Non-null caller-supplied `reasoning.mode` / `reasoning.context`, `prompt_cache_options`, cache breakpoints, `safety_identifier`, encrypted-reasoning `include`, and deprecated `prompt_cache_retention` are rejected instead of being silently dropped; explicit `null` is treated as omitted. When Responses Lite is active, the proxy follows the official private Codex builder and adds `reasoning.context: "all_turns"` on the compact wire.
444
+ Compact accepts the existing private Codex `reasoning_effort` (or matching `reasoning.effort`) plus `prompt_cache_key`, supported `service_tier`, `text`, and top-level `verbosity`. A known process-local `previous_response_id` is resolved to its saved Response items and replayed as full compact input; the field is never forwarded to private Codex. Non-null caller-supplied `reasoning.mode` / `reasoning.context`, `prompt_cache_options`, cache breakpoints, encrypted-reasoning `include`, and deprecated `prompt_cache_retention` are rejected instead of being silently dropped; explicit `null` for those nullable compatibility controls is treated as omitted. Any supplied `safety_identifier`, including `null`, returns HTTP 400. When Responses Lite is active, the proxy follows the official private Codex builder and adds `reasoning.context: "all_turns"` on the compact wire.
395
445
 
396
446
  ```bash
397
447
  curl http://localhost:18080/v1/compact \
@@ -407,11 +457,10 @@ curl http://localhost:18080/v1/compact \
407
457
 
408
458
  ### `GET /health`
409
459
 
410
- Health check. Returns auth availability, configured model and reasoning effort, Codex config path, and context-window settings.
460
+ Readiness check. It returns `200` only when OAuth is available, a fresh catalog can be loaded, and the effective model resolves. Optional catalog metadata is not a readiness requirement: `reasoning_effort`, `context_window`, and `auto_compact_token_limit` are `null` when the selected live row and explicit config do not provide them. Otherwise the route returns `503` with a typed, sanitized error instead of reporting a false healthy state. The payload exposes catalog timing and effective model settings, but never access tokens, account IDs, auth paths, upstream ETags, or upstream response bodies.
411
461
 
412
462
  ```bash
413
- curl http://localhost:18080/health
414
- # {"status":"ok","auth_available":true,"model":"gpt-5.6-sol","reasoning_effort":"high","codex_config_path":"/Users/me/.codex/config.toml","context_window":272000,"auto_compact_token_limit":244800}
463
+ curl -i http://localhost:18080/health
415
464
  ```
416
465
 
417
466
  ## Codex-Specific Features
@@ -422,7 +471,7 @@ These features are extensions beyond the standard OpenAI API, designed for Codex
422
471
 
423
472
  `prompt_cache_key` keeps related prefixes in the same backend cache family. Use one stable, privacy-safe key per conversation or application prefix.
424
473
 
425
- Official Codex 0.147.0 [defaults this field to its Responses metadata session ID](https://github.com/openai/codex/blob/be6e8eac029b183056b7e4402879f15d2c85f61b/codex-rs/core/src/client.rs#L475-L487), and [projects the same value into `client_metadata.session_id`](https://github.com/openai/codex/blob/be6e8eac029b183056b7e4402879f15d2c85f61b/codex-rs/core/src/responses_metadata.rs#L219-L228), unless an explicit override is present. This proxy follows the same precedence on Chat requests: explicit `prompt_cache_key`, then a non-empty `client_metadata.session_id`, then omission. It does not generate a process-wide session or reuse `thread_id` as the cache key.
474
+ Official Codex 0.153.3 [derives this field from Responses metadata](https://github.com/openai/codex/blob/b1a547b1f73ce86205d9222ac19cff334b3b7a2e/codex-rs/core/src/client.rs#L540-L550), unless an explicit override is present. This proxy follows the same precedence on Chat requests: explicit `prompt_cache_key`, then a non-empty `client_metadata.session_id`, then omission. It does not generate a process-wide session or reuse `thread_id` as the cache key.
426
475
 
427
476
  The private Codex OAuth HTTP and WebSocket request structures do not contain public GPT-5.6 `prompt_cache_options` or content-block `prompt_cache_breakpoint` fields. This proxy treats explicit `null` as omitted and rejects non-null controls with HTTP 400 instead of forwarding a request that the private route rejects or silently pretending the requested cache policy was applied. Use a public Responses API client when explicit cache policy or breakpoints are required. See OpenAI's [public prompt caching guide](https://developers.openai.com/api/docs/guides/prompt-caching#prompt-cache-breakpoints).
428
477
 
@@ -441,9 +490,9 @@ GPT-5.6 cache accounting is returned, when the backend supplies it, as `usage.pr
441
490
 
442
491
  ### `reasoning_effort` and `reasoning`
443
492
 
444
- The legacy top-level `reasoning_effort` remains supported. Known values are the case-sensitive lowercase strings `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`, and Codex's virtual `ultra` setting. Other non-empty model-defined values are preserved. `ultra` is sent as backend effort `max`; Codex's local proactive multi-agent behavior for `ultra` is outside this proxy.
493
+ The legacy top-level `reasoning_effort` remains supported. Every effort is checked against the selected live model. Codex's virtual `ultra` setting resolves to the model's advertised multi-agent effort when valid, otherwise `max` when supported, otherwise the model's highest advertised non-`ultra` effort. If no valid wire effort exists, the request fails instead of inventing one.
445
494
 
446
- GPT-5.6 requests can instead use the public Responses-shaped object:
495
+ Requests can instead use the Responses-shaped effort/context object:
447
496
 
448
497
  ```json
449
498
  {
@@ -453,49 +502,56 @@ GPT-5.6 requests can instead use the public Responses-shaped object:
453
502
  {"role": "user", "content": "Find migration failure modes."}
454
503
  ],
455
504
  "reasoning": {
456
- "mode": "standard",
457
505
  "effort": "high",
458
506
  "context": "all_turns"
459
507
  }
460
508
  }
461
509
  ```
462
510
 
463
- - `mode: "standard"` is accepted as the public default and omitted from the private Codex request. If mode is explicit and neither the request nor Codex config selects an effort, the proxy sends the public GPT-5.6 default effort, `medium`.
464
- - `mode: "pro"` returns HTTP 400. Official Codex has no private request field or alternate model/effort mapping for Pro. Codex `ultra` and `service_tier: "priority"` are different features.
511
+ - Non-null `mode`, including `"standard"` and `"pro"`, returns HTTP 400 because the private Codex request has no mode field or alternate model mapping. Codex `ultra` and `service_tier: "priority"` are different features.
465
512
  - `context` is `auto`, `current_turn`, or `all_turns`. Encrypted reasoning content is included automatically on generation requests so full-history continuation can replay it.
466
513
  - `reasoning_effort` and `reasoning.effort` may both be present only when equal. Conflicting values return HTTP 400.
467
514
  - Responses Lite uses `all_turns` as the Codex wire default. An explicitly different context is rejected instead of silently overwritten; use `responses_lite: false` when the backend route supports classic Responses and another context is required.
468
515
  - Remote compact keeps its existing private Codex `reasoning_effort` field but does not accept public `reasoning.mode` or `reasoning.context`.
469
516
 
470
- Anthropic `thinking` values map as `enabled → high`, `adaptive → medium`, and `disabled → none`. Claude Code's `output_config.effort` takes precedence over adaptive or enabled thinking and supports `low`, `medium`, `high`, `xhigh`, and `max`. Call-level `thinking.disabled` takes precedence over ambient `output_config.effort`, so Claude Code WebSearch/WebFetch auxiliary calls use `none` instead of failing the compatibility check with HTTP 400. The supported reasoning context and verbosity extensions are also available on `/v1/messages`, image generation, and inspection requests; Pro and non-null public cache policy/breakpoints or `safety_identifier` fail explicitly on the private Codex provider.
517
+ Anthropic `thinking` values map as `enabled → high`, `adaptive → medium`, and `disabled → none`. Claude Code's `output_config.effort` takes precedence over adaptive or enabled thinking and supports `low`, `medium`, `high`, `xhigh`, and `max`. Call-level `thinking.disabled` takes precedence over ambient `output_config.effort` for representable client-side auxiliary calls such as WebFetch; hosted Anthropic WebSearch is rejected before transport because its lifecycle cannot be translated losslessly. The supported reasoning context and verbosity extensions are also available on `/v1/messages`, image generation, and inspection requests; Pro, non-null public cache policy/breakpoints, and any supplied `safety_identifier` fail explicitly on the private Codex provider.
518
+
519
+ Anthropic's `JSONOutputFormat` carries a schema but no name. When Claude Code
520
+ sends that official shape, the proxy follows pinned Codex 0.153.3 and supplies
521
+ the transport-only name `codex_output_schema`; this label has no model-selection
522
+ or fallback role. An explicit proxy-extension name is preserved only when it is
523
+ already a valid Responses format name, and is never sanitized or truncated.
471
524
 
472
- The pinned official Codex HTTP request has no `stop` field. Omitted, `null`, and empty stop values are omitted from the private request; any non-empty OpenAI `stop` or Anthropic `stop_sequences` value returns HTTP 400 before the private transport starts.
525
+ The pinned official Codex HTTP request has no `stop` field. Omitted OpenAI
526
+ `stop`, or explicit OpenAI `stop: null`, is omitted from the private request;
527
+ any other OpenAI `stop` value returns HTTP 400. Anthropic `stop_sequences`
528
+ follows its typed API contract: omission is allowed, while explicit `null` or
529
+ an array value returns HTTP 400 because the private transport cannot apply it.
473
530
 
474
531
  The mapping is intentionally transport-aware:
475
532
 
476
533
  | Public/facade input | Private Codex behavior |
477
534
  |---|---|
478
- | `reasoning.mode: "standard"` | Omit mode; send the resolved effort/context |
479
- | `reasoning.mode: "pro"` | HTTP 400; no official Codex alias |
535
+ | Non-null `reasoning.mode` | HTTP 400; no private Codex field or model alias |
480
536
  | `prompt_cache_key` | Forward an explicit value; otherwise use non-empty `client_metadata.session_id` |
481
537
  | Non-null `prompt_cache_options` / breakpoint | HTTP 400; `null` is omitted and there is no private field |
482
- | Non-null `safety_identifier` | HTTP 400; `null` is omitted and OAuth account/thread IDs are not semantic aliases |
483
- | Non-empty `stop` / `stop_sequences` | HTTP 400; no private field |
538
+ | Any supplied `safety_identifier` | HTTP 400; the optional field is non-nullable and OAuth account/thread IDs are not semantic aliases |
539
+ | Non-null `stop` / `stop_sequences` | HTTP 400; no private field |
484
540
  | `previous_response_id` | Resolve locally and replay complete Response history over HTTP |
485
541
  | `service_tier: "fast"` | Send `service_tier: "priority"` |
486
542
  | `service_tier: "default"` | Omit the field |
487
543
 
488
- Authenticated tests on July 10, 2026 verified both official continuation paths with `gpt-5.6-sol`: a direct private Responses WebSocket completed a second delta request using the exact prior `response.id`, and the private HTTP endpoint completed the same continuation when the full prior input/output history was replayed. The proxy implements the HTTP replay strategy. Direct HTTP forwarding of `previous_response_id`, Pro, public cache controls, and `safety_identifier` was rejected and is not used. See OpenAI's [reasoning mode documentation](https://developers.openai.com/api/docs/guides/reasoning#reasoning-mode) and the Codex 0.147.0 [HTTP/WebSocket request structures](https://github.com/openai/codex/blob/be6e8eac029b183056b7e4402879f15d2c85f61b/codex-rs/codex-api/src/common.rs#L251-L307).
544
+ Authenticated tests on July 10, 2026 verified both official continuation paths with `gpt-5.6-sol`: a direct private Responses WebSocket completed a second delta request using the exact prior `response.id`, and the private HTTP endpoint completed the same continuation when the full prior input/output history was replayed. The proxy implements the HTTP replay strategy. Direct HTTP forwarding of `previous_response_id`, Pro, public cache controls, and `safety_identifier` was rejected and is not used. See OpenAI's [reasoning mode documentation](https://developers.openai.com/api/docs/guides/reasoning#reasoning-mode) and the Codex 0.153.3 [HTTP/WebSocket request structures](https://github.com/openai/codex/blob/b1a547b1f73ce86205d9222ac19cff334b3b7a2e/codex-rs/codex-api/src/common.rs#L275-L350).
489
545
 
490
546
  ### `responses_lite`
491
547
 
492
548
  Controls the Codex Responses Lite request shape. Accepted values are `true`, `false`, and `"auto"`. Request value takes precedence over `CODEX_AS_API_RESPONSES_LITE`; default is `"auto"`.
493
549
 
494
- In `"auto"` mode, this package only uses Lite when the shared model capability table says the selected model should use it. The `gpt-5.6` alias plus Sol, Terra, and Luna use Lite automatically. Setting `responses_lite: true` forces Lite and moves tools/instructions into Lite-compatible developer input items.
550
+ In `"auto"` mode, this package uses Lite only when the selected live catalog entry advertises it. Setting `responses_lite: true` forces the Lite request shape and moves tools/instructions into Lite-compatible developer input items.
495
551
 
496
552
  Codex implements web search and image generation for Lite models through client-side standalone tools. This proxy has no standalone tool executor, so Lite requests containing hosted `web_search` or `image_generation` tools fail explicitly instead of silently dropping the tools. When a request does not override the mode, `CODEX_AS_API_RESPONSES_LITE=off` selects the existing classic request contract if the backend route supports it.
497
553
 
498
- The official Codex Lite request builder removes `input_image.detail`. Accordingly, classic Responses preserve `auto`, `low`, and `high`; `original` is also preserved when the model capability advertises it. The bundled catalog enables `original` for the GPT-5.6 alias, Sol, Terra, Luna, GPT-5.5, GPT-5.4, and GPT-5.4 Mini, and rejects it for GPT-5.2 and the conservative legacy entries. Lite requests keep the image and remove only `detail` after capability validation.
554
+ The official Codex Lite request builder has no lossless `input_image.detail` mapping. Classic Responses preserve `auto`, `low`, and `high`, plus `original` only when the live catalog advertises it. A caller-supplied image detail on Lite returns HTTP 400 instead of silently removing the requested behavior.
499
555
 
500
556
  ### Image detail
501
557
 
@@ -517,17 +573,17 @@ Chat multimodal blocks are preserved instead of being flattened to text:
517
573
  }
518
574
  ```
519
575
 
520
- `/v1/inspect` accepts the equivalent flat image object: `{"image_url":"data:image/...","detail":"original"}`. Recognized detail values are `auto`, `low`, `high`, and `original`; `original` additionally requires a model capability listed above. See OpenAI's [image detail guide](https://developers.openai.com/api/docs/guides/images-vision#choose-an-image-detail-level).
576
+ `/v1/inspect` accepts the equivalent flat image object: `{"image_url":"data:image/...","detail":"original"}`. Recognized detail values are `auto`, `low`, `high`, and `original`; `original` additionally requires support in the live model catalog. See OpenAI's [image detail guide](https://developers.openai.com/api/docs/guides/images-vision#choose-an-image-detail-level).
521
577
 
522
578
  ### `safety_identifier` and verbosity
523
579
 
524
- The public `safety_identifier` has no equivalent private Codex request field. `ChatGPT-Account-ID`, `thread-id`, and `session-id` identify different things and are not substituted. Explicit `null` is treated as omitted; a non-null `safety_identifier` returns HTTP 400.
580
+ The public `safety_identifier` has no equivalent private Codex request field. `ChatGPT-Account-ID`, `thread-id`, and `session-id` identify different things and are not substituted. Because this optional OpenAI field is non-nullable, any supplied value, including explicit `null`, returns HTTP 400.
525
581
 
526
582
  Standard Chat `verbosity: "low" | "medium" | "high"` maps to Responses `text.verbosity`. The existing `text` extension remains supported. Supplying both is allowed only when the values agree.
527
583
 
528
584
  ### `parallel_tool_calls`
529
585
 
530
- Set `parallel_tool_calls: true` to request parallel tool calls when the selected model capability allows it. The shared capability table gates this field, and Responses Lite always keeps `parallel_tool_calls` disabled.
586
+ Classic Responses preserves an explicit `parallel_tool_calls` value without relying on a model-catalog capability that Codex does not define. Lite requests fail rather than silently disabling an explicit `true` value.
531
587
 
532
588
  ### `client_metadata` and `codex_metadata`
533
589
 
@@ -537,9 +593,9 @@ The installation ID and process window ID remain stable, while `turn_id` and `x-
537
593
 
538
594
  ### `previous_response_id`
539
595
 
540
- Non-streaming responses and the final streaming finish chunk expose the real upstream Responses ID as `response_id`. The provider keeps up to 256 completed chains in a process-local LRU store. Passing a known ID as `previous_response_id` prepends the saved semantic input and exact prior `response.output_item.done` items—including encrypted reasoning and tool items—to the new input, then sends one full private HTTP request. The ID itself is never forwarded and is never converted to `thread_id`.
596
+ Non-streaming responses and the final streaming finish chunk expose the real upstream Responses ID as `response_id`. The provider keeps up to 256 completed chains in a process-local LRU store scoped to the authenticated account. Passing a known ID as `previous_response_id` under the same account prepends the saved semantic input and exact prior `response.output_item.done` items, including encrypted reasoning and tool items, then sends one full private HTTP request. When both the saved source model and current live model publish `comp_hash`, unequal values fail before sampling because automatic pre-turn compaction is not implemented; equal or unavailable hashes follow Codex's compatibility rule. The ID itself is never forwarded and is never converted to `thread_id`; another account cannot replay it.
541
597
 
542
- That event source is intentional: the Codex 0.147.0 SSE parser [takes semantic items from `response.output_item.done`](https://github.com/openai/codex/blob/be6e8eac029b183056b7e4402879f15d2c85f61b/codex-rs/codex-api/src/sse/responses.rs#L330-L341), while its [`response.completed` shape contains the response ID, usage, and `end_turn`](https://github.com/openai/codex/blob/be6e8eac029b183056b7e4402879f15d2c85f61b/codex-rs/codex-api/src/sse/responses.rs#L112-L160), not replay history. The authenticated private HTTP rollout likewise returned an empty `response.completed.output`; the proxy therefore commits the completed output-item events instead of treating that extra field as conversation state.
598
+ That event source is intentional: the Codex 0.153.3 SSE parser [takes semantic items from `response.output_item.done`](https://github.com/openai/codex/blob/b1a547b1f73ce86205d9222ac19cff334b3b7a2e/codex-rs/codex-api/src/sse/responses.rs#L357-L372), while its [`response.completed` shape contains response metadata and usage](https://github.com/openai/codex/blob/b1a547b1f73ce86205d9222ac19cff334b3b7a2e/codex-rs/codex-api/src/sse/responses.rs#L118-L166), not replay history. The authenticated private HTTP rollout likewise returned an empty `response.completed.output`; the proxy therefore commits the completed output-item events instead of treating that extra field as conversation state.
543
599
 
544
600
  Only a real `response.completed` event commits a chain. Branches from an older retained ID are supported. Restarting the server or evicting an old entry removes that local state; an unknown ID returns HTTP 400 before any upstream request.
545
601
 
@@ -667,15 +723,19 @@ curl -N http://localhost:18080/v1/chat/completions \
667
723
 
668
724
  The `/v1/messages` endpoint implements the Anthropic Messages gateway shape used by Claude Code. The current request shape was reproduced with Claude Code `2.1.220`; streaming, real Codex OAuth chat, request-level GPT model routing, adaptive thinking, `--effort max`, and Fast Mode remain covered.
669
725
 
670
- Start the proxy first. `CODEX_AS_API_MODEL` is the fallback used when Claude Code sends a built-in Anthropic model name:
726
+ Start the proxy first. `CODEX_AS_API_MODEL` is required when Claude Code sends a built-in `claude-*` facade model name; the configured backend must exist in the fresh authenticated catalog:
671
727
 
672
728
  ```bash
673
729
  CODEX_AS_API_MODEL=gpt-5.6-terra \
674
- CODEX_AS_API_RESPONSES_LITE=off \
675
730
  codex-as-api
676
731
  ```
677
732
 
678
- `CODEX_AS_API_RESPONSES_LITE=off` is required for Claude Code hosted WebSearch on GPT-5.6. Official Codex Responses Lite uses a client-side standalone `web.run` tool, while this gateway receives Anthropic's hosted `web_search` declaration and has no standalone executor; classic Responses preserves that hosted tool. WebFetch does not use a hosted Responses tool, but still needs the disabled-thinking precedence fix when process-level effort is enabled.
733
+ Claude Code hosted WebSearch is not supported through this gateway, regardless
734
+ of Responses Lite mode. Classic Responses can execute an OpenAI hosted search,
735
+ but its output lacks the Anthropic provenance and encrypted lifecycle data
736
+ needed for a lossless Messages response. WebFetch remains a client-side
737
+ auxiliary request and retains the disabled-thinking precedence behavior when
738
+ process-level effort is enabled.
679
739
 
680
740
  To keep the built-in Fable, Opus, Sonnet, and Haiku rows and append one GPT row, launch the GPT-routed Claude Code process with these variables:
681
741
 
@@ -702,7 +762,7 @@ If managed settings define an `availableModels` allowlist, that list must includ
702
762
 
703
763
  For a persistent gateway configuration, put the same variables in the `env` object in `~/.claude/settings.json`. Do that only when every Claude Code process using that config should route through codex-as-api.
704
764
 
705
- `ANTHROPIC_CUSTOM_MODEL_OPTION` adds one picker row. To make Terra or Luna the visible GPT row, change that value and the display text. Any bundled GPT model can also be selected directly without changing the visible row:
765
+ `ANTHROPIC_CUSTOM_MODEL_OPTION` adds one picker row. To make another live model the visible GPT row, change that value and the display text. Any model returned by `/v1/models` can also be selected directly without changing the visible row:
706
766
 
707
767
  ```bash
708
768
  ANTHROPIC_BASE_URL=http://127.0.0.1:18080 ANTHROPIC_AUTH_TOKEN=unused \
@@ -711,9 +771,9 @@ ANTHROPIC_BASE_URL=http://127.0.0.1:18080 ANTHROPIC_AUTH_TOKEN=unused \
711
771
  claude --model gpt-5.6-luna --effort medium
712
772
  ```
713
773
 
714
- Known bundled GPT IDs are sent to the matching Codex backend model. Built-in Claude/Fable/Opus/Sonnet/Haiku IDs are not Codex models, so this proxy maps those requests to `CODEX_AS_API_MODEL` while preserving the selected name in the Anthropic response.
774
+ Live Codex IDs are sent to the matching backend model. Built-in Claude/Fable/Opus/Sonnet/Haiku IDs are not Codex models, so recognized `claude-*` IDs require the explicitly configured `CODEX_AS_API_MODEL`; the proxy preserves the selected client name in the Anthropic response.
715
775
 
716
- `ANTHROPIC_BASE_URL` applies to the whole Claude Code process. Therefore the built-in rows remain available in the picker, but they also pass through codex-as-api and use the configured Codex fallback; the rows do not call Anthropic models in that process. To use an actual Anthropic model and GPT in parallel, keep the gateway variables out of global settings, start the GPT process with the inline command above, and start a second process normally:
776
+ `ANTHROPIC_BASE_URL` applies to the whole Claude Code process. Therefore the built-in rows remain available in the picker, but they also pass through codex-as-api and use the explicitly configured Codex backend; the rows do not call Anthropic models in that process. To use an actual Anthropic model and GPT in parallel, keep the gateway variables out of global settings, start the GPT process with the inline command above, and start a second process normally:
717
777
 
718
778
  ```bash
719
779
  claude --model sonnet
@@ -735,19 +795,21 @@ HTTP Server (FastAPI / Axum / Express)
735
795
  |
736
796
  +---> ChatGPTOAuthProvider
737
797
  |
738
- +---> ~/.codex/auth.json (OAuth tokens, auto-refresh)
798
+ +---> ~/.codex/auth.json (managed ChatGPT OAuth tokens, auto-refresh)
799
+ +---> https://chatgpt.com/backend-api/codex/models
739
800
  +---> https://chatgpt.com/backend-api/codex/responses
740
801
  ```
741
802
 
742
803
  The provider handles:
743
- - Token loading, proactive refresh five minutes before expiry, and refresh on 401
804
+ - Managed ChatGPT token loading, proactive refresh five minutes before expiry, and refresh on 401
805
+ - Fresh-only authenticated model discovery, validation, and ETag invalidation
744
806
  - OpenAI Responses API over SSE
745
807
  - `prompt_cache_key` and cache read/write accounting
746
- - GPT-5.6 reasoning effort/context plus `standard` mode compatibility
747
- - Reasoning content streaming (`reasoning_content`, `reasoning`)
808
+ - Live catalog-gated reasoning effort and context handling
809
+ - Real Codex reasoning extensions without fabricated Anthropic signatures
748
810
  - Tool call streaming
749
811
  - Codex-specific headers (`x-openai-subagent`, `x-openai-memgen-request`)
750
- - Bounded process-local `previous_response_id` history replay over private HTTP
812
+ - Bounded, account-scoped `previous_response_id` history replay over private HTTP
751
813
  - Multimodal Chat input, image generation/inspection, and capability-gated classic `original` detail
752
814
  - Remote conversation compaction
753
815
 
@@ -756,7 +818,10 @@ The provider handles:
756
818
  The `Release` GitHub Actions workflow runs on a pushed `v*` tag. It rejects a
757
819
  tag that does not exactly match every Python, npm, and Rust version surface,
758
820
  runs the complete cross-runtime gate, and then publishes PyPI, npmjs, the
759
- scoped GitHub npm package, platform Rust binaries, and one GitHub Release.
821
+ scoped GitHub npm package, deterministic platform Rust ZIP archives, and one
822
+ GitHub Release. Each Rust archive contains the platform binary, `LICENSE`, and
823
+ the generated `THIRD_PARTY_NOTICES.md` covering both bundled tokenizer data and
824
+ the locked normal dependency graph across supported release targets.
760
825
 
761
826
  The workflow uses registry-supported OIDC for PyPI and npmjs. No `PYPI_TOKEN`,
762
827
  `NPM_TOKEN`, personal access token, or repository secret is required. One-time
@@ -776,9 +841,9 @@ registry setup is still required:
776
841
  After bumping every version and passing the local gate, push the matching tag:
777
842
 
778
843
  ```bash
779
- python scripts/check_package_versions.py --tag v0.6.5
780
- git tag v0.6.5
781
- git push origin v0.6.5
844
+ python scripts/check_package_versions.py --tag v0.7.0
845
+ git tag v0.7.0
846
+ git push origin v0.7.0
782
847
  ```
783
848
 
784
849
  ## Tests
@@ -786,7 +851,7 @@ git push origin v0.6.5
786
851
  ### Python
787
852
 
788
853
  ```bash
789
- pip install -e ".[dev,server]"
854
+ pip install -e ".[dev]"
790
855
  pip install httpx
791
856
  pytest tests/ -v
792
857
  ```
@@ -809,6 +874,25 @@ npm test
809
874
 
810
875
  ## Release Notes
811
876
 
877
+ ### v0.7.0
878
+
879
+ - Replace bundled model capability tables with a fresh authenticated Codex
880
+ catalog and add `GET /v1/models` across all three runtimes.
881
+ - Remove stale/unknown-model fallbacks, fabricated Anthropic signatures, silent
882
+ request downgrades, and malformed upstream recovery; return typed failures.
883
+ - Reject hosted Anthropic WebSearch instead of translating incomplete OpenAI
884
+ search lifecycle data, and require authoritative completion and final usage.
885
+ - Keep immediate Claude Code `message_start` delivery without inventing token
886
+ counts; final Anthropic fields are emitted only from authoritative provider data.
887
+ - Scope continuation history and catalog state to the authenticated account,
888
+ validate before streaming, and make `/health` report actual readiness.
889
+ - Require the official nested `tokens` auth layout, remove root-level and
890
+ noncanonical auth aliases, and reject external-host `chatgptAuthTokens`.
891
+ - Pin the validated private contract to Codex `0.153.3` and publish the exact
892
+ tested artifacts to PyPI, npmjs, GitHub Packages, and GitHub Releases.
893
+ - Install Python server dependencies with the base package so the default
894
+ `pip install codex-as-api` command produces a runnable server.
895
+
812
896
  ### v0.6.5
813
897
 
814
898
  - Align GPT-5.6 alias, Sol, Terra, and Luna context limits with Codex `0.147.0`