codex-as-api 0.6.3__tar.gz → 0.6.5__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 (36) hide show
  1. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/PKG-INFO +97 -26
  2. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/README.md +96 -25
  3. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/RELEASES.md +78 -0
  4. codex_as_api-0.6.5/config/codex-upstream-contract.json +86 -0
  5. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/config/model-capabilities.json +12 -12
  6. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/pyproject.toml +3 -1
  7. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/src/codex_as_api/__init__.py +1 -1
  8. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/src/codex_as_api/anthropic_adapter.py +5 -4
  9. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/src/codex_as_api/auth.py +133 -21
  10. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/src/codex_as_api/messages.py +1 -1
  11. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/src/codex_as_api/model_capabilities.py +13 -6
  12. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/src/codex_as_api/protocol.py +2 -1
  13. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/src/codex_as_api/provider.py +78 -66
  14. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/src/codex_as_api/server.py +96 -5
  15. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/tests/conftest.py +1 -0
  16. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/tests/test_anthropic_adapter.py +1 -4
  17. codex_as_api-0.6.5/tests/test_auth.py +730 -0
  18. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/tests/test_codex_config.py +4 -4
  19. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/tests/test_messages.py +0 -1
  20. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/tests/test_model_capabilities.py +34 -3
  21. codex_as_api-0.6.5/tests/test_package_versions.py +94 -0
  22. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/tests/test_protocol.py +0 -3
  23. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/tests/test_provider.py +87 -7
  24. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/tests/test_server.py +317 -6
  25. codex_as_api-0.6.3/tests/test_auth.py +0 -326
  26. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/.gitignore +0 -0
  27. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/LICENSE +0 -0
  28. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/THIRD_PARTY_NOTICES.md +0 -0
  29. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/config/o200k_base.tiktoken +0 -0
  30. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/requirements-server.txt +0 -0
  31. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/requirements.txt +0 -0
  32. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/src/codex_as_api/codex_config.py +0 -0
  33. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/src/codex_as_api/o200k_tokenizer.py +0 -0
  34. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/tests/__init__.py +0 -0
  35. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/tests/fixtures/o200k_base_encode_ordinary.json +0 -0
  36. {codex_as_api-0.6.3 → codex_as_api-0.6.5}/tests/test_o200k_tokenizer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codex-as-api
3
- Version: 0.6.3
3
+ Version: 0.6.5
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
@@ -49,7 +49,7 @@ Use ChatGPT / Codex OAuth as a local OpenAI-compatible API server.
49
49
  - **Tool calling** — function calls, tool results, and parallel tool calls
50
50
  - **Image support** — generation, inspection, multimodal Chat input, and capability-gated `original` image detail in classic Responses
51
51
  - **Reasoning** — configurable effort/context, `standard` mode compatibility, persisted reasoning, and streaming thinking content
52
- - **Codex features** — `prompt_cache_key`, process-local `response_id` continuation, subagent headers, and remote compaction
52
+ - **Codex features** — session-aware `prompt_cache_key`, process-local Chat `response_id` continuation, subagent headers, and remote compaction
53
53
  - **Codex config aware** — reads `CODEX_HOME` / `~/.codex/config.toml` for model, reasoning-effort, and context-window settings
54
54
  - **Token estimate & compaction helpers** — Anthropic-compatible `/v1/messages/count_tokens` and `/v1/messages/compact`
55
55
  - **Auto auth** — reads `~/.codex/auth.json` and auto-refreshes OAuth tokens
@@ -165,7 +165,7 @@ Environment variables (Python, Rust, and TypeScript):
165
165
  | `CODEX_AS_API_PORT` | `18080` | Listen port |
166
166
  | `CODEX_AS_API_MODEL` | `~/.codex/config.toml` `model`, else `gpt-5.5` | Model identifier passed to Codex backend |
167
167
  | `CODEX_AS_API_AUTH_PATH` | `~/.codex/auth.json` | Path to OAuth credentials file |
168
- | `CODEX_AS_API_CODEX_CLI_VERSION` | latest `@openai/codex` from npm | Override the Codex CLI version used in backend request `User-Agent` headers |
168
+ | `CODEX_AS_API_CODEX_CLI_VERSION` | `0.147.0` | Override the validated Codex compatibility baseline identified in backend request `User-Agent` headers |
169
169
  | `CODEX_AS_API_RESPONSES_LITE` | `auto` | Responses Lite mode: `auto`, `on`, or `off` |
170
170
  | `CODEX_AS_API_CODEX_METADATA` | `off` | Add Codex-style per-turn `client_metadata` and related backend headers |
171
171
  | `CODEX_HOME` | `~/.codex` | Codex home directory used for `auth.json` and `config.toml` discovery |
@@ -177,8 +177,8 @@ model = "gpt-5.6-sol"
177
177
  model_reasoning_effort = "high"
178
178
 
179
179
  # Optional overrides. Without them, known models use the bundled Codex catalog values.
180
- model_context_window = 372000
181
- model_auto_compact_token_limit = 334800
180
+ model_context_window = 272000
181
+ model_auto_compact_token_limit = 244800
182
182
  ```
183
183
 
184
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.
@@ -198,7 +198,7 @@ model_auto_compact_token_limit = 334800
198
198
  | `gpt-5.3-codex-spark` | Ultra-fast coding model |
199
199
  | `gpt-5.2` | Previous generation model |
200
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 372,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 372,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.
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.
202
202
 
203
203
  To use a different port:
204
204
 
@@ -310,13 +310,36 @@ curl -N http://localhost:18080/v1/messages \
310
310
  }'
311
311
  ```
312
312
 
313
+ ### Identifier and cache behavior
314
+
315
+ The two compatibility facades intentionally use the similarly named fields for
316
+ different jobs:
317
+
318
+ | Input or output | `/v1/chat/completions` | `/v1/messages` |
319
+ |---|---|---|
320
+ | `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
+ | `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
+ | `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 |
324
+ | `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 |
326
+
327
+ Anthropic `cache_control` is a cache-boundary annotation, not an identifier.
328
+ This proxy accepts only `{"type":"ephemeral"}` with optional `ttl: "5m"` or
329
+ `"1h"` at request, system, message, content-block, and tool locations. The
330
+ private Codex request has no Anthropic breakpoint or TTL fields, so accepted
331
+ hints do not reproduce Anthropic caching semantics. Normal `/v1/messages`
332
+ responses do not expose the Chat-only `response_id`. The custom
333
+ `/v1/messages/compact` endpoint retains its documented local
334
+ `previous_response_id` support.
335
+
313
336
  ### `POST /v1/messages/count_tokens`
314
337
 
315
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.
316
339
 
317
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`.
318
341
 
319
- Official Codex also has a [`ceil(UTF-8 bytes / 4)` truncation helper](https://github.com/openai/codex/blob/393f64565ab46f09d99ca4d9bd973537e72a114b/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/393f64565ab46f09d99ca4d9bd973537e72a114b/codex-rs/core/src/context_manager/history.rs#L160-L185). 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.
342
+ 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.
320
343
 
321
344
  ```bash
322
345
  curl http://localhost:18080/v1/messages/count_tokens \
@@ -388,7 +411,7 @@ Health check. Returns auth availability, configured model and reasoning effort,
388
411
 
389
412
  ```bash
390
413
  curl http://localhost:18080/health
391
- # {"status":"ok","auth_available":true,"model":"gpt-5.6-sol","reasoning_effort":"high","codex_config_path":"/Users/me/.codex/config.toml","context_window":372000,"auto_compact_token_limit":334800}
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}
392
415
  ```
393
416
 
394
417
  ## Codex-Specific Features
@@ -399,7 +422,7 @@ These features are extensions beyond the standard OpenAI API, designed for Codex
399
422
 
400
423
  `prompt_cache_key` keeps related prefixes in the same backend cache family. Use one stable, privacy-safe key per conversation or application prefix.
401
424
 
402
- Official Codex [defaults this field to its own per-conversation `thread_id`](https://github.com/openai/codex/blob/393f64565ab46f09d99ca4d9bd973537e72a114b/codex-rs/core/src/client.rs#L469-L473). This multi-client facade cannot infer that boundary safely and does not reuse its process-level metadata thread ID as a cache key, which could mix unrelated callers. Set `prompt_cache_key` explicitly when stable cache affinity is required.
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.
403
426
 
404
427
  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).
405
428
 
@@ -454,7 +477,7 @@ The mapping is intentionally transport-aware:
454
477
  |---|---|
455
478
  | `reasoning.mode: "standard"` | Omit mode; send the resolved effort/context |
456
479
  | `reasoning.mode: "pro"` | HTTP 400; no official Codex alias |
457
- | `prompt_cache_key` | Forward unchanged |
480
+ | `prompt_cache_key` | Forward an explicit value; otherwise use non-empty `client_metadata.session_id` |
458
481
  | Non-null `prompt_cache_options` / breakpoint | HTTP 400; `null` is omitted and there is no private field |
459
482
  | Non-null `safety_identifier` | HTTP 400; `null` is omitted and OAuth account/thread IDs are not semantic aliases |
460
483
  | Non-empty `stop` / `stop_sequences` | HTTP 400; no private field |
@@ -462,7 +485,7 @@ The mapping is intentionally transport-aware:
462
485
  | `service_tier: "fast"` | Send `service_tier: "priority"` |
463
486
  | `service_tier: "default"` | Omit the field |
464
487
 
465
- 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 official Codex [HTTP/WebSocket request structures](https://github.com/openai/codex/blob/393f64565ab46f09d99ca4d9bd973537e72a114b/codex-rs/codex-api/src/common.rs#L215-L293).
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).
466
489
 
467
490
  ### `responses_lite`
468
491
 
@@ -508,15 +531,15 @@ Set `parallel_tool_calls: true` to request parallel tool calls when the selected
508
531
 
509
532
  ### `client_metadata` and `codex_metadata`
510
533
 
511
- `client_metadata` is forwarded to the Codex backend. Set `codex_metadata: true` or `CODEX_AS_API_CODEX_METADATA=on` to overlay Codex-style turn metadata keys such as `turn_id`, `session_id`, `thread_id`, and `x-codex-turn-metadata`.
534
+ `client_metadata` is forwarded to the Codex backend. Set `codex_metadata: true` or `CODEX_AS_API_CODEX_METADATA=on` to add Codex-style turn metadata. Metadata mode requires a non-empty caller-supplied `client_metadata.session_id`; it preserves that session, defaults a missing root `thread_id` to the session, and preserves an explicit child `thread_id`.
512
535
 
513
- Do not rely on user-supplied values for those reserved Codex metadata keys when metadata mode is enabled; this package regenerates them per turn. Metadata `thread_id` is not a `previous_response_id` alias and is not automatically reused as `prompt_cache_key` by this multi-client proxy.
536
+ The installation ID and process window ID remain stable, while `turn_id` and `x-codex-turn-metadata` are regenerated for each request. Metadata `thread_id` is neither a `previous_response_id` alias nor a cache key. An explicit `prompt_cache_key` wins; otherwise the non-empty session ID supplies cache affinity.
514
537
 
515
538
  ### `previous_response_id`
516
539
 
517
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`.
518
541
 
519
- That event source is intentional: the official Codex SSE parser [takes semantic items from `response.output_item.done`](https://github.com/openai/codex/blob/393f64565ab46f09d99ca4d9bd973537e72a114b/codex-rs/codex-api/src/sse/responses.rs#L326-L337), while its [`response.completed` shape contains the response ID, usage, and `end_turn`](https://github.com/openai/codex/blob/393f64565ab46f09d99ca4d9bd973537e72a114b/codex-rs/codex-api/src/sse/responses.rs#L112-L120), 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.
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.
520
543
 
521
544
  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.
522
545
 
@@ -642,7 +665,7 @@ curl -N http://localhost:18080/v1/chat/completions \
642
665
 
643
666
  ## Using with Claude Code
644
667
 
645
- The `/v1/messages` endpoint implements the Anthropic Messages gateway shape used by Claude Code. Compatibility was validated with Claude Code `2.1.209`, including streaming, real Codex OAuth chat, request-level GPT model routing, adaptive thinking, `--effort max`, and Fast Mode.
668
+ 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.
646
669
 
647
670
  Start the proxy first. `CODEX_AS_API_MODEL` is the fallback used when Claude Code sends a built-in Anthropic model name:
648
671
 
@@ -666,7 +689,14 @@ CLAUDE_CODE_ATTRIBUTION_HEADER=0 \
666
689
  claude
667
690
  ```
668
691
 
669
- Run `/model` and select **GPT-5.6 Sol**. The custom entry is appended to the built-in rows rather than replacing them. Claude Code `2.1.209` recognizes this GPT model ID without a custom capability declaration: `/effort low|medium|high|xhigh|max`, the picker effort control, and `claude --effort ...` are translated from Claude Code's `output_config.effort` to the Codex reasoning effort. Claude Code Fast Mode's `speed: "fast"` is translated to the Codex `priority` service tier.
692
+ Run `/model` and select **GPT-5.6 Sol**. The custom entry is appended to the built-in rows rather than replacing them. Claude Code `2.1.220` recognizes this GPT model ID without a custom capability declaration: `/effort low|medium|high|xhigh|max`, the picker effort control, and `claude --effort ...` are translated from Claude Code's `output_config.effort` to the Codex reasoning effort. Claude Code Fast Mode's `speed: "fast"` is translated to the Codex `priority` service tier.
693
+
694
+ Claude Code sends `x-claude-code-session-id` for gateway request grouping. This
695
+ proxy hashes the exact header value into a stable `prompt_cache_key`; it does
696
+ not turn the header into Codex `session_id` or `thread_id` metadata. A
697
+ top-level `prompt_cache_key` is supported as a proxy extension and takes
698
+ precedence. Normal Messages requests remain stateless, so callers must send
699
+ full message history.
670
700
 
671
701
  If managed settings define an `availableModels` allowlist, that list must include the exact `ANTHROPIC_CUSTOM_MODEL_OPTION` value, such as `gpt-5.6-sol`; otherwise Claude Code hides or rejects the custom row.
672
702
 
@@ -710,7 +740,7 @@ HTTP Server (FastAPI / Axum / Express)
710
740
  ```
711
741
 
712
742
  The provider handles:
713
- - Token loading and automatic refresh on 401
743
+ - Token loading, proactive refresh five minutes before expiry, and refresh on 401
714
744
  - OpenAI Responses API over SSE
715
745
  - `prompt_cache_key` and cache read/write accounting
716
746
  - GPT-5.6 reasoning effort/context plus `standard` mode compatibility
@@ -723,20 +753,32 @@ The provider handles:
723
753
 
724
754
  ## Release & package publishing
725
755
 
726
- - Bump versions in `pyproject.toml`, `src/codex_as_api/__init__.py`, `src/codex_as_api/server.py`, `ts/package.json`, `ts/package-lock.json`, `rust/Cargo.toml`, and `rust/Cargo.lock`.
727
- - Publish a GitHub Release such as `v0.6.3` from the matching commit.
728
- - The manually-dispatched `Publish npm packages` workflow builds/tests the TypeScript package, runs `npm pack --dry-run`, publishes `codex-as-api` to npmjs when `NPM_TOKEN` is configured, and publishes `@eunho-j/codex-as-api` to GitHub Packages with `GITHUB_TOKEN`.
756
+ The `Release` GitHub Actions workflow runs on a pushed `v*` tag. It rejects a
757
+ tag that does not exactly match every Python, npm, and Rust version surface,
758
+ runs the complete cross-runtime gate, and then publishes PyPI, npmjs, the
759
+ scoped GitHub npm package, platform Rust binaries, and one GitHub Release.
729
760
 
730
- Publishing to npmjs requires an authenticated npm session (`npm login` beforehand; `npm whoami` should succeed). From the repository root, the publish itself is one command:
761
+ The workflow uses registry-supported OIDC for PyPI and npmjs. No `PYPI_TOKEN`,
762
+ `NPM_TOKEN`, personal access token, or repository secret is required. One-time
763
+ registry setup is still required:
731
764
 
732
- ```bash
733
- npm publish ./ts --access public
734
- ```
765
+ - Create GitHub environments named `pypi` and `npm`; add deployment protection
766
+ rules if desired.
767
+ - On PyPI, add a trusted publisher for project `codex-as-api`, owner `Eunho-J`,
768
+ repository `codex-as-api`, workflow `release.yml`, environment `pypi`.
769
+ - On npmjs, add a GitHub Actions trusted publisher for package `codex-as-api`,
770
+ owner `Eunho-J`, repository `codex-as-api`, workflow `release.yml`,
771
+ environment `npm`, with `npm publish` allowed.
772
+ - GitHub Packages and GitHub Releases use the job-scoped automatic
773
+ `GITHUB_TOKEN`; repository policy must allow the workflow's declared
774
+ `packages: write` and `contents: write` permissions.
735
775
 
736
- Publishing to PyPI requires `UV_PUBLISH_TOKEN` and a freshly built distribution:
776
+ After bumping every version and passing the local gate, push the matching tag:
737
777
 
738
778
  ```bash
739
- uv build --clear && uv publish dist/*
779
+ python scripts/check_package_versions.py --tag v0.6.5
780
+ git tag v0.6.5
781
+ git push origin v0.6.5
740
782
  ```
741
783
 
742
784
  ## Tests
@@ -767,6 +809,35 @@ npm test
767
809
 
768
810
  ## Release Notes
769
811
 
812
+ ### v0.6.5
813
+
814
+ - Align GPT-5.6 alias, Sol, Terra, and Luna context limits with Codex `0.147.0`
815
+ at 272,000 tokens and derive the 244,800-token automatic compact threshold.
816
+ - Pin the verified upstream request, Responses Lite, header, and model contract
817
+ to `openai/codex` commit
818
+ `be6e8eac029b183056b7e4402879f15d2c85f61b`; track its new fractional SSE
819
+ rollout-budget field without claiming an unapproved facade mapping.
820
+ - Refresh OAuth credentials five minutes before expiry, coalesce concurrent
821
+ refreshes, reload changed credentials after 401, and preserve upstream HTTP
822
+ status codes across all three servers.
823
+ - Replace startup npm version discovery with deterministic compatibility and
824
+ package-version identity in the Codex-style `User-Agent`.
825
+ - Add full cross-runtime CI plus tag-gated OIDC publishing to PyPI and npmjs,
826
+ `GITHUB_TOKEN` publishing to GitHub Packages, and GitHub Release assets.
827
+
828
+ ### v0.6.4
829
+
830
+ - Remove process-global generated Codex session/thread identities and require
831
+ explicit session identity when Codex metadata mode is enabled.
832
+ - Resolve Chat cache affinity as explicit `prompt_cache_key`, then
833
+ `client_metadata.session_id`, then omission.
834
+ - Derive Claude Code cache affinity from `x-claude-code-session-id` without
835
+ fabricating Codex metadata; keep normal Anthropic Messages stateless.
836
+ - Validate and strip Anthropic `cache_control` compatibility hints without
837
+ claiming unavailable breakpoint or TTL semantics.
838
+ - Update current compatibility evidence to Codex `0.145.0`, Codex `main` at
839
+ `bd2de422aa287b97b06ca6425a10935bcf1b3731`, and Claude Code `2.1.220`.
840
+
770
841
  ### v0.6.3
771
842
 
772
843
  - Accept Claude Code auxiliary requests that combine ambient `output_config.effort` with call-level `thinking.disabled`.
@@ -15,7 +15,7 @@ Use ChatGPT / Codex OAuth as a local OpenAI-compatible API server.
15
15
  - **Tool calling** — function calls, tool results, and parallel tool calls
16
16
  - **Image support** — generation, inspection, multimodal Chat input, and capability-gated `original` image detail in classic Responses
17
17
  - **Reasoning** — configurable effort/context, `standard` mode compatibility, persisted reasoning, and streaming thinking content
18
- - **Codex features** — `prompt_cache_key`, process-local `response_id` continuation, subagent headers, and remote compaction
18
+ - **Codex features** — session-aware `prompt_cache_key`, process-local Chat `response_id` continuation, subagent headers, and remote compaction
19
19
  - **Codex config aware** — reads `CODEX_HOME` / `~/.codex/config.toml` for model, reasoning-effort, and context-window settings
20
20
  - **Token estimate & compaction helpers** — Anthropic-compatible `/v1/messages/count_tokens` and `/v1/messages/compact`
21
21
  - **Auto auth** — reads `~/.codex/auth.json` and auto-refreshes OAuth tokens
@@ -131,7 +131,7 @@ Environment variables (Python, Rust, and TypeScript):
131
131
  | `CODEX_AS_API_PORT` | `18080` | Listen port |
132
132
  | `CODEX_AS_API_MODEL` | `~/.codex/config.toml` `model`, else `gpt-5.5` | Model identifier passed to Codex backend |
133
133
  | `CODEX_AS_API_AUTH_PATH` | `~/.codex/auth.json` | Path to OAuth credentials file |
134
- | `CODEX_AS_API_CODEX_CLI_VERSION` | latest `@openai/codex` from npm | Override the Codex CLI version used in backend request `User-Agent` headers |
134
+ | `CODEX_AS_API_CODEX_CLI_VERSION` | `0.147.0` | Override the validated Codex compatibility baseline identified in backend request `User-Agent` headers |
135
135
  | `CODEX_AS_API_RESPONSES_LITE` | `auto` | Responses Lite mode: `auto`, `on`, or `off` |
136
136
  | `CODEX_AS_API_CODEX_METADATA` | `off` | Add Codex-style per-turn `client_metadata` and related backend headers |
137
137
  | `CODEX_HOME` | `~/.codex` | Codex home directory used for `auth.json` and `config.toml` discovery |
@@ -143,8 +143,8 @@ model = "gpt-5.6-sol"
143
143
  model_reasoning_effort = "high"
144
144
 
145
145
  # Optional overrides. Without them, known models use the bundled Codex catalog values.
146
- model_context_window = 372000
147
- model_auto_compact_token_limit = 334800
146
+ model_context_window = 272000
147
+ model_auto_compact_token_limit = 244800
148
148
  ```
149
149
 
150
150
  `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.
@@ -164,7 +164,7 @@ model_auto_compact_token_limit = 334800
164
164
  | `gpt-5.3-codex-spark` | Ultra-fast coding model |
165
165
  | `gpt-5.2` | Previous generation model |
166
166
 
167
- 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 372,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 372,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.
167
+ 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.
168
168
 
169
169
  To use a different port:
170
170
 
@@ -276,13 +276,36 @@ curl -N http://localhost:18080/v1/messages \
276
276
  }'
277
277
  ```
278
278
 
279
+ ### Identifier and cache behavior
280
+
281
+ The two compatibility facades intentionally use the similarly named fields for
282
+ different jobs:
283
+
284
+ | Input or output | `/v1/chat/completions` | `/v1/messages` |
285
+ |---|---|---|
286
+ | `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 |
287
+ | `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 |
288
+ | `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 |
289
+ | `x-claude-code-session-id` | Not used | SHA-256 of `codex-as-api:claude-code-session:<exact header value>` supplies cache affinity only |
290
+ | `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 |
291
+ | `cache_control` | Not an OpenAI Chat control | Validated as an Anthropic compatibility hint, then stripped before Codex transport |
292
+
293
+ Anthropic `cache_control` is a cache-boundary annotation, not an identifier.
294
+ This proxy accepts only `{"type":"ephemeral"}` with optional `ttl: "5m"` or
295
+ `"1h"` at request, system, message, content-block, and tool locations. The
296
+ private Codex request has no Anthropic breakpoint or TTL fields, so accepted
297
+ hints do not reproduce Anthropic caching semantics. Normal `/v1/messages`
298
+ responses do not expose the Chat-only `response_id`. The custom
299
+ `/v1/messages/compact` endpoint retains its documented local
300
+ `previous_response_id` support.
301
+
279
302
  ### `POST /v1/messages/count_tokens`
280
303
 
281
304
  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.
282
305
 
283
306
  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`.
284
307
 
285
- Official Codex also has a [`ceil(UTF-8 bytes / 4)` truncation helper](https://github.com/openai/codex/blob/393f64565ab46f09d99ca4d9bd973537e72a114b/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/393f64565ab46f09d99ca4d9bd973537e72a114b/codex-rs/core/src/context_manager/history.rs#L160-L185). 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.
308
+ 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.
286
309
 
287
310
  ```bash
288
311
  curl http://localhost:18080/v1/messages/count_tokens \
@@ -354,7 +377,7 @@ Health check. Returns auth availability, configured model and reasoning effort,
354
377
 
355
378
  ```bash
356
379
  curl http://localhost:18080/health
357
- # {"status":"ok","auth_available":true,"model":"gpt-5.6-sol","reasoning_effort":"high","codex_config_path":"/Users/me/.codex/config.toml","context_window":372000,"auto_compact_token_limit":334800}
380
+ # {"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}
358
381
  ```
359
382
 
360
383
  ## Codex-Specific Features
@@ -365,7 +388,7 @@ These features are extensions beyond the standard OpenAI API, designed for Codex
365
388
 
366
389
  `prompt_cache_key` keeps related prefixes in the same backend cache family. Use one stable, privacy-safe key per conversation or application prefix.
367
390
 
368
- Official Codex [defaults this field to its own per-conversation `thread_id`](https://github.com/openai/codex/blob/393f64565ab46f09d99ca4d9bd973537e72a114b/codex-rs/core/src/client.rs#L469-L473). This multi-client facade cannot infer that boundary safely and does not reuse its process-level metadata thread ID as a cache key, which could mix unrelated callers. Set `prompt_cache_key` explicitly when stable cache affinity is required.
391
+ 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.
369
392
 
370
393
  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).
371
394
 
@@ -420,7 +443,7 @@ The mapping is intentionally transport-aware:
420
443
  |---|---|
421
444
  | `reasoning.mode: "standard"` | Omit mode; send the resolved effort/context |
422
445
  | `reasoning.mode: "pro"` | HTTP 400; no official Codex alias |
423
- | `prompt_cache_key` | Forward unchanged |
446
+ | `prompt_cache_key` | Forward an explicit value; otherwise use non-empty `client_metadata.session_id` |
424
447
  | Non-null `prompt_cache_options` / breakpoint | HTTP 400; `null` is omitted and there is no private field |
425
448
  | Non-null `safety_identifier` | HTTP 400; `null` is omitted and OAuth account/thread IDs are not semantic aliases |
426
449
  | Non-empty `stop` / `stop_sequences` | HTTP 400; no private field |
@@ -428,7 +451,7 @@ The mapping is intentionally transport-aware:
428
451
  | `service_tier: "fast"` | Send `service_tier: "priority"` |
429
452
  | `service_tier: "default"` | Omit the field |
430
453
 
431
- 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 official Codex [HTTP/WebSocket request structures](https://github.com/openai/codex/blob/393f64565ab46f09d99ca4d9bd973537e72a114b/codex-rs/codex-api/src/common.rs#L215-L293).
454
+ 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).
432
455
 
433
456
  ### `responses_lite`
434
457
 
@@ -474,15 +497,15 @@ Set `parallel_tool_calls: true` to request parallel tool calls when the selected
474
497
 
475
498
  ### `client_metadata` and `codex_metadata`
476
499
 
477
- `client_metadata` is forwarded to the Codex backend. Set `codex_metadata: true` or `CODEX_AS_API_CODEX_METADATA=on` to overlay Codex-style turn metadata keys such as `turn_id`, `session_id`, `thread_id`, and `x-codex-turn-metadata`.
500
+ `client_metadata` is forwarded to the Codex backend. Set `codex_metadata: true` or `CODEX_AS_API_CODEX_METADATA=on` to add Codex-style turn metadata. Metadata mode requires a non-empty caller-supplied `client_metadata.session_id`; it preserves that session, defaults a missing root `thread_id` to the session, and preserves an explicit child `thread_id`.
478
501
 
479
- Do not rely on user-supplied values for those reserved Codex metadata keys when metadata mode is enabled; this package regenerates them per turn. Metadata `thread_id` is not a `previous_response_id` alias and is not automatically reused as `prompt_cache_key` by this multi-client proxy.
502
+ The installation ID and process window ID remain stable, while `turn_id` and `x-codex-turn-metadata` are regenerated for each request. Metadata `thread_id` is neither a `previous_response_id` alias nor a cache key. An explicit `prompt_cache_key` wins; otherwise the non-empty session ID supplies cache affinity.
480
503
 
481
504
  ### `previous_response_id`
482
505
 
483
506
  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`.
484
507
 
485
- That event source is intentional: the official Codex SSE parser [takes semantic items from `response.output_item.done`](https://github.com/openai/codex/blob/393f64565ab46f09d99ca4d9bd973537e72a114b/codex-rs/codex-api/src/sse/responses.rs#L326-L337), while its [`response.completed` shape contains the response ID, usage, and `end_turn`](https://github.com/openai/codex/blob/393f64565ab46f09d99ca4d9bd973537e72a114b/codex-rs/codex-api/src/sse/responses.rs#L112-L120), 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.
508
+ 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.
486
509
 
487
510
  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.
488
511
 
@@ -608,7 +631,7 @@ curl -N http://localhost:18080/v1/chat/completions \
608
631
 
609
632
  ## Using with Claude Code
610
633
 
611
- The `/v1/messages` endpoint implements the Anthropic Messages gateway shape used by Claude Code. Compatibility was validated with Claude Code `2.1.209`, including streaming, real Codex OAuth chat, request-level GPT model routing, adaptive thinking, `--effort max`, and Fast Mode.
634
+ 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.
612
635
 
613
636
  Start the proxy first. `CODEX_AS_API_MODEL` is the fallback used when Claude Code sends a built-in Anthropic model name:
614
637
 
@@ -632,7 +655,14 @@ CLAUDE_CODE_ATTRIBUTION_HEADER=0 \
632
655
  claude
633
656
  ```
634
657
 
635
- Run `/model` and select **GPT-5.6 Sol**. The custom entry is appended to the built-in rows rather than replacing them. Claude Code `2.1.209` recognizes this GPT model ID without a custom capability declaration: `/effort low|medium|high|xhigh|max`, the picker effort control, and `claude --effort ...` are translated from Claude Code's `output_config.effort` to the Codex reasoning effort. Claude Code Fast Mode's `speed: "fast"` is translated to the Codex `priority` service tier.
658
+ Run `/model` and select **GPT-5.6 Sol**. The custom entry is appended to the built-in rows rather than replacing them. Claude Code `2.1.220` recognizes this GPT model ID without a custom capability declaration: `/effort low|medium|high|xhigh|max`, the picker effort control, and `claude --effort ...` are translated from Claude Code's `output_config.effort` to the Codex reasoning effort. Claude Code Fast Mode's `speed: "fast"` is translated to the Codex `priority` service tier.
659
+
660
+ Claude Code sends `x-claude-code-session-id` for gateway request grouping. This
661
+ proxy hashes the exact header value into a stable `prompt_cache_key`; it does
662
+ not turn the header into Codex `session_id` or `thread_id` metadata. A
663
+ top-level `prompt_cache_key` is supported as a proxy extension and takes
664
+ precedence. Normal Messages requests remain stateless, so callers must send
665
+ full message history.
636
666
 
637
667
  If managed settings define an `availableModels` allowlist, that list must include the exact `ANTHROPIC_CUSTOM_MODEL_OPTION` value, such as `gpt-5.6-sol`; otherwise Claude Code hides or rejects the custom row.
638
668
 
@@ -676,7 +706,7 @@ HTTP Server (FastAPI / Axum / Express)
676
706
  ```
677
707
 
678
708
  The provider handles:
679
- - Token loading and automatic refresh on 401
709
+ - Token loading, proactive refresh five minutes before expiry, and refresh on 401
680
710
  - OpenAI Responses API over SSE
681
711
  - `prompt_cache_key` and cache read/write accounting
682
712
  - GPT-5.6 reasoning effort/context plus `standard` mode compatibility
@@ -689,20 +719,32 @@ The provider handles:
689
719
 
690
720
  ## Release & package publishing
691
721
 
692
- - Bump versions in `pyproject.toml`, `src/codex_as_api/__init__.py`, `src/codex_as_api/server.py`, `ts/package.json`, `ts/package-lock.json`, `rust/Cargo.toml`, and `rust/Cargo.lock`.
693
- - Publish a GitHub Release such as `v0.6.3` from the matching commit.
694
- - The manually-dispatched `Publish npm packages` workflow builds/tests the TypeScript package, runs `npm pack --dry-run`, publishes `codex-as-api` to npmjs when `NPM_TOKEN` is configured, and publishes `@eunho-j/codex-as-api` to GitHub Packages with `GITHUB_TOKEN`.
722
+ The `Release` GitHub Actions workflow runs on a pushed `v*` tag. It rejects a
723
+ tag that does not exactly match every Python, npm, and Rust version surface,
724
+ runs the complete cross-runtime gate, and then publishes PyPI, npmjs, the
725
+ scoped GitHub npm package, platform Rust binaries, and one GitHub Release.
695
726
 
696
- Publishing to npmjs requires an authenticated npm session (`npm login` beforehand; `npm whoami` should succeed). From the repository root, the publish itself is one command:
727
+ The workflow uses registry-supported OIDC for PyPI and npmjs. No `PYPI_TOKEN`,
728
+ `NPM_TOKEN`, personal access token, or repository secret is required. One-time
729
+ registry setup is still required:
697
730
 
698
- ```bash
699
- npm publish ./ts --access public
700
- ```
731
+ - Create GitHub environments named `pypi` and `npm`; add deployment protection
732
+ rules if desired.
733
+ - On PyPI, add a trusted publisher for project `codex-as-api`, owner `Eunho-J`,
734
+ repository `codex-as-api`, workflow `release.yml`, environment `pypi`.
735
+ - On npmjs, add a GitHub Actions trusted publisher for package `codex-as-api`,
736
+ owner `Eunho-J`, repository `codex-as-api`, workflow `release.yml`,
737
+ environment `npm`, with `npm publish` allowed.
738
+ - GitHub Packages and GitHub Releases use the job-scoped automatic
739
+ `GITHUB_TOKEN`; repository policy must allow the workflow's declared
740
+ `packages: write` and `contents: write` permissions.
701
741
 
702
- Publishing to PyPI requires `UV_PUBLISH_TOKEN` and a freshly built distribution:
742
+ After bumping every version and passing the local gate, push the matching tag:
703
743
 
704
744
  ```bash
705
- uv build --clear && uv publish dist/*
745
+ python scripts/check_package_versions.py --tag v0.6.5
746
+ git tag v0.6.5
747
+ git push origin v0.6.5
706
748
  ```
707
749
 
708
750
  ## Tests
@@ -733,6 +775,35 @@ npm test
733
775
 
734
776
  ## Release Notes
735
777
 
778
+ ### v0.6.5
779
+
780
+ - Align GPT-5.6 alias, Sol, Terra, and Luna context limits with Codex `0.147.0`
781
+ at 272,000 tokens and derive the 244,800-token automatic compact threshold.
782
+ - Pin the verified upstream request, Responses Lite, header, and model contract
783
+ to `openai/codex` commit
784
+ `be6e8eac029b183056b7e4402879f15d2c85f61b`; track its new fractional SSE
785
+ rollout-budget field without claiming an unapproved facade mapping.
786
+ - Refresh OAuth credentials five minutes before expiry, coalesce concurrent
787
+ refreshes, reload changed credentials after 401, and preserve upstream HTTP
788
+ status codes across all three servers.
789
+ - Replace startup npm version discovery with deterministic compatibility and
790
+ package-version identity in the Codex-style `User-Agent`.
791
+ - Add full cross-runtime CI plus tag-gated OIDC publishing to PyPI and npmjs,
792
+ `GITHUB_TOKEN` publishing to GitHub Packages, and GitHub Release assets.
793
+
794
+ ### v0.6.4
795
+
796
+ - Remove process-global generated Codex session/thread identities and require
797
+ explicit session identity when Codex metadata mode is enabled.
798
+ - Resolve Chat cache affinity as explicit `prompt_cache_key`, then
799
+ `client_metadata.session_id`, then omission.
800
+ - Derive Claude Code cache affinity from `x-claude-code-session-id` without
801
+ fabricating Codex metadata; keep normal Anthropic Messages stateless.
802
+ - Validate and strip Anthropic `cache_control` compatibility hints without
803
+ claiming unavailable breakpoint or TTL semantics.
804
+ - Update current compatibility evidence to Codex `0.145.0`, Codex `main` at
805
+ `bd2de422aa287b97b06ca6425a10935bcf1b3731`, and Claude Code `2.1.220`.
806
+
736
807
  ### v0.6.3
737
808
 
738
809
  - Accept Claude Code auxiliary requests that combine ambient `output_config.effort` with call-level `thinking.disabled`.
@@ -1,5 +1,83 @@
1
1
  # Release Notes
2
2
 
3
+ ## v0.6.5
4
+
5
+ ### Codex 0.147 compatibility
6
+
7
+ - Align GPT-5.6 alias, Sol, Terra, and Luna context limits with the stable
8
+ Codex 0.147 catalog at 272,000 tokens. Automatic compact defaults now derive
9
+ to 244,800 tokens.
10
+ - Pin a structured compatibility fixture to `openai/codex` 0.147.0 commit
11
+ `be6e8eac029b183056b7e4402879f15d2c85f61b`, covering the model fields,
12
+ Responses request path, Lite behavior, and originator header. The fractional
13
+ `codex_rollout_budget_units` usage field is tracked as upstream-only because
14
+ this facade has no approved public mapping for it.
15
+ - Replace the moving startup `npm view @openai/codex version` lookup with a
16
+ deterministic Codex compatibility baseline and `codex-as-api` package
17
+ version in the request `User-Agent`.
18
+
19
+ ### Authentication and errors
20
+
21
+ - Refresh OAuth access tokens five minutes before expiry, coalesce concurrent
22
+ refresh work, and reload credentials changed by another request before
23
+ rotating a refresh token after HTTP 401.
24
+ - Preserve the existing root or nested auth-file layout, reject account
25
+ switches and conflicting token claims, and compare the credential snapshot
26
+ again before persisting a refresh response.
27
+ - Preserve upstream HTTP statuses, including rate-limit and overload responses,
28
+ in Python and TypeScript to match Rust.
29
+
30
+ ### CI and publishing
31
+
32
+ - Add pull-request and push CI for Python tests, Ruff, mypy, package builds,
33
+ TypeScript tests/build/package checks, and Rust format, Clippy, tests, and
34
+ release builds.
35
+ - Add a tag-triggered release workflow that requires exact tag/package version
36
+ agreement and the full cross-runtime gate before publishing.
37
+ - Publish PyPI and npmjs through trusted-publisher OIDC, GitHub Packages and
38
+ GitHub Releases through the automatic repository `GITHUB_TOKEN`, and attach
39
+ Python/npm distributions plus Linux, macOS, and Windows Rust binaries.
40
+ - Remove the manual npm workflow that could report success after silently
41
+ skipping npmjs publication when `NPM_TOKEN` was absent.
42
+
43
+ ## v0.6.4
44
+
45
+ ### Codex identity and cache affinity
46
+
47
+ - Remove process-global generated `session_id` and `thread_id` values from
48
+ Python, TypeScript, and Rust. Codex metadata mode now requires an explicit
49
+ non-empty `client_metadata.session_id`, defaults a root `thread_id` to that
50
+ session, preserves an explicit child thread, and refreshes only turn-scoped
51
+ identity on each request.
52
+ - Resolve Chat `prompt_cache_key` as explicit value, then non-empty
53
+ `client_metadata.session_id`, then omission. This matches Codex 0.145.0 and
54
+ current `main`, where the official default is Responses metadata
55
+ `session_id`, not `thread_id`.
56
+ - Retain bounded process-local Chat `previous_response_id` history replay over
57
+ private HTTP. The ID remains local and is never forwarded upstream.
58
+
59
+ ### Claude Code session and cache compatibility
60
+
61
+ - Read `x-claude-code-session-id` on `/v1/messages` and derive a stable,
62
+ privacy-safe SHA-256 `prompt_cache_key`; an explicit proxy extension
63
+ `prompt_cache_key` takes precedence.
64
+ - Keep normal Anthropic Messages stateless: reject non-null
65
+ `previous_response_id`, disable ambient Codex metadata, avoid fabricated
66
+ Claude thread IDs, and remove Python's nonstandard Anthropic `response_id`.
67
+ - Validate Anthropic `cache_control` hints at request, system, message, content,
68
+ and tool locations. Accept only `type: "ephemeral"` with optional TTL `5m` or
69
+ `1h`, then strip the hint because the private Codex transport cannot preserve
70
+ Anthropic cache breakpoints or TTL behavior.
71
+
72
+ ### Validation
73
+
74
+ - Python: 515 tests, mypy, and changed-file Ruff checks.
75
+ - TypeScript: 261 tests and production build.
76
+ - Rust: 253 tests and rustfmt.
77
+ - Re-audited `@openai/codex` 0.145.0 plus `main` at
78
+ `bd2de422aa287b97b06ca6425a10935bcf1b3731`, and reproduced the Claude Code
79
+ 2.1.220 request shape.
80
+
3
81
  ## v0.6.3
4
82
 
5
83
  ### Claude Code WebSearch/WebFetch auxiliary calls