Skip to content

fix(codex): improve OAuth websocket parity for fast/priority responses#2575

Open
Willhong wants to merge 4 commits intorouter-for-me:devfrom
Willhong:fix/codex-oauth-fast-ws-parity
Open

fix(codex): improve OAuth websocket parity for fast/priority responses#2575
Willhong wants to merge 4 commits intorouter-for-me:devfrom
Willhong:fix/codex-oauth-fast-ws-parity

Conversation

@Willhong
Copy link
Copy Markdown

@Willhong Willhong commented Apr 6, 2026

Summary

Improve Codex OAuth websocket parity so service_tier=priority behaves closer to official Codex /fast behavior.

This patch focuses on Codex OAuth websocket routing and request-shape parity, and is related to the same Codex websocket transport area touched by #2230 / #2256, but addresses a different problem:

What this changes

1) Default file-backed Codex OAuth auths to websocket-capable

Files:

  • internal/watcher/synthesizer/file.go
  • sdk/auth/filestore.go

Behavior:

  • Preserve explicit websockets metadata when present.
  • Otherwise synthesize Attributes["websockets"] = "true" for provider codex.

Why:

  • File-backed Codex OAuth auths were otherwise routed through HTTP fallback instead of the Codex websocket path.

2) Normalize Codex websocket request bodies in both execute paths

File:

  • internal/runtime/executor/codex_websockets_executor.go

Behavior:

  • For both Execute and ExecuteStream:
    • ensure model
    • ensure stream=true in streaming path
    • drop unsupported carryover fields
    • ensure non-empty instructions

Why:

  • Codex websocket upstream is stricter than generic OpenAI Responses handling and rejects some under-normalized payloads.

3) Add session-aligned websocket metadata/header parity

Files:

  • internal/runtime/executor/codex_websockets_executor.go
  • internal/runtime/executor/codex_executor.go

Behavior:

  • Populate prompt_cache_key from the execution/session id when absent.
  • Populate client_metadata.x-codex-turn-metadata.
  • Mirror the same execution/session identity into websocket headers:
    • Session_id
    • x-client-request-id
    • x-codex-turn-metadata
  • Stop forcing Conversation_id on Codex websocket requests.
  • Use an originator closer to the official app-server/debug path.

Why:

  • Official Codex websocket requests carry request/session metadata that was missing or mismatched in the proxy path.
  • This metadata appears to matter for Codex OAuth websocket behavior, especially when using service_tier=priority.

Investigation summary

During investigation, the following concrete issues were confirmed:

  1. Official Codex /fast sends service_tier: "priority" over websocket.
  2. Official default behavior omits the tier field rather than sending "default".
  3. File-backed Codex OAuth auths in CLIProxyAPI were not websocket-enabled by default.
  4. Proxy websocket requests were missing Codex-like session metadata and header parity.
  5. Sending literal service_tier: "default" can be rejected upstream on the Codex websocket path.

Validation

Official Codex baseline

Using traced official Codex websocket requests:

  • default/no tier -> request omits service_tier
  • fast -> request sends service_tier: "priority"

Proxy behavior after this patch

The proxy websocket path:

  • routes Codex OAuth auths over websocket instead of HTTP fallback
  • sends service_tier: "priority" upstream
  • includes prompt cache + turn metadata + request/session headers
  • avoids invalid default-tier semantics by using omitted tier as baseline

Latency benchmark

Short websocket prompt:
Reply with exactly: OK and nothing else.

Patched proxy, comparing omitted tier vs priority:

omit(null)

  • total median: 1.94s
  • first-token median: 1.28s

priority

  • total median: 1.81s
  • first-token median: 1.12s

This is not a dramatic improvement, but it is measurable and consistently in the expected direction after the websocket parity fixes.

Notes / scope

  • Upstream still returns response.completed.service_tier = default, so that field alone does not appear reliable for verifying Codex fast behavior on this route.
  • This PR does not claim perfect 1:1 parity with official Codex internals.
  • It does provide validated transport/session/request-shape fixes that move Codex OAuth websocket behavior materially closer to official /fast behavior.

Related

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the codex executor to handle websocket session metadata and instructions more robustly, including setting specific headers and default values. It also introduces a default "websockets" attribute for the codex provider in the synthesizer and filestore. Feedback highlights a potential session ID mismatch between headers and body metadata, as well as opportunities to refactor duplicated logic in the websocket executor and the authentication attribute handling.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f9cd37cc14

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@Willhong
Copy link
Copy Markdown
Author

Willhong commented Apr 6, 2026

Addressed the review feedback.

This follow-up update includes:

  • fixing the executionSessionID vs body/header metadata mismatch
    • the effective session id is now used consistently for body/header metadata
    • the original execution session id is still used only for session-store lookups, so we do not pollute the store with one-off generated sessions
  • refactoring duplicated websocket body preparation in Execute / ExecuteStream into prepareCodexWebsocketRequestBody(...)
  • refactoring the default Codex OAuth websocket attribute logic into the shared helper sdk/auth.CodexWebsocketsAttributeValue(...)
  • preserving previous_response_id so incremental websocket turns do not regress

I also pushed the updated branch, and the live instance I am testing against is already running this patched build.

Copy link
Copy Markdown
Collaborator

@luispater luispater left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary:
I found one blocking issue in the websocket request normalization path.

Key findings:

  • Blocking: normalizeCodexWebsocketInstructions now injects You are a helpful assistant. whenever instructions is missing or blank. That silently changes the effective prompt for requests that previously had no system instructions, so the websocket path stops being behaviorally neutral relative to the existing empty-string handling in the Codex executors. For a compatibility-layer fix, that prompt mutation is too invasive to ship.

Test plan:

  • Reviewed the websocket request preparation changes and the Codex auth routing updates.

Copy link
Copy Markdown
Collaborator

@luispater luispater left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary:
The websocket parity direction makes sense, but this is a protocol-sensitive path and I do not have enough confidence to approve it without regression coverage.

Key findings:

  • Non-blocking: please add targeted tests around websocket request-body normalization and header/session shaping so future parity work does not silently drift again.
  • Non-blocking: defaulting file-backed Codex auths to websocket-capable changes routing behavior; a focused regression test around that fallback would make this much safer.

Test plan:

  • Reviewed the Codex websocket/body/header changes and the auth synthesis updates.

This is an automated Codex review result and still requires manual verification by a human reviewer.

@luispater luispater added the codex label Apr 7, 2026
@luispater luispater changed the base branch from main to dev April 7, 2026 02:01
@Willhong
Copy link
Copy Markdown
Author

Willhong commented Apr 7, 2026

@luispater Addressed the requested change and pushed an update.

What changed:

  • restored behaviorally neutral websocket instruction normalization by delegating to the existing Codex normalization logic (missing/null -> "")
  • removed the default assistant prompt injection from the websocket path
  • added regression tests for websocket request-body normalization and session metadata shaping
  • added focused regression coverage for the file-backed Codex auth websocket fallback / explicit override

Validation:

  • /usr/local/go/bin/go test ./internal/runtime/executor -run 'Test(BuildCodexWebsocketRequestBodyPreservesPreviousResponseID|PrepareCodexWebsocketRequestBody.*|ApplyCodexWebsocketHeaders.*|ApplyCodexHeaders.*)'
  • /usr/local/go/bin/go test ./internal/watcher/synthesizer -run 'Test(FileSynthesizer_Synthesize_CodexAuth.*)'

Could you take another look when you have a moment?

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants