Attribute Codex requests with canonical session, thread, and turn identity - #1303
Merged
Merged
Conversation
…ntity The ChatGPT Codex backend attributes usage to turns from the identity metadata the official client sends, not from individual requests. Our transport sent none of it, so every tool continuation and retry was an unlabelled request that relied on undocumented fallback attribution (compare earendil-works/pi#9488). Every HTTP request and WebSocket frame now carries the same attribution set as codex-rs: the originator, session-id, thread-id, x-client-request-id, and x-codex-window-id headers plus an ASCII-escaped x-codex-turn-metadata record, mirrored in the request's client_metadata. The WebSocket handshake carries the static originator. Agent.OpenAI.TurnState widens the existing per-connection turn state: alongside the first-write-wins x-codex-turn-state token it now holds the turn identifier (a UUID version 7 minted on the first request of a turn and shared by tool continuations, retries, reconnects, HTTP fallback, and inline compaction), the context-window generation, and a fallback thread identity for unpersisted sessions. The prompt cache key, which is the persisted session identifier, is the session and thread identity exactly as in the official client. Compaction requests are labelled request_kind "compaction" without leaving the current turn, and a committed manual or automatic compaction advances the context window for subsequent requests. Agent.Uuid provides the RFC 9562 version 7 generator. Co-authored-by: Haskell Agent (claude-fable-5-1, xhigh) <agent@digitallyinduced.com> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to the check against earendil-works/pi#9488: our Codex transport sent none of the identity metadata the official client uses for turn attribution, so tool continuations and retries reached the backend as unlabelled requests. This PR sends the same attribution set as
codex-rs.Wire changes (HTTP
/responsesand WebSocketresponse.create)originator: haskell-agent,session-id,thread-id,x-client-request-id(= thread id),x-codex-window-id(<thread>:<n>),x-codex-turn-metadata(ASCII-escaped JSON withsession_id,thread_id,turn_id,window_id,window_number,context_window_id,request_kind,turn_started_at_unix_ms).client_metadata:session_id,thread_id,turn_id,x-codex-window-id,x-codex-turn-metadata, merged with the existingx-codex-turn-stateand Responses Lite fields.originatorheader only; per-turn attribution travels in each frame because a socket outlives turns.Identity lifecycle
Agent.OpenAI.TurnStatewidens the existing per-connectionCodexTurnState: next to the first-write-winsx-codex-turn-statetoken it holds a UUIDv7 turn id minted on the first request of a logical turn and reused by tool continuations, transport retries, reconnects (copyCodexTurnState), HTTP fallback, and inline compaction.resetCodexTurnState(new prompt, turn completion, backend reset) rotates it.prompt_cache_key, i.e. the persisted session id — the same rulecodex-rsuses for itssession-idheader. Unpersisted sessions get a stable generated fallback per connection.remoteCompactionV2RequestOptions,sendWsRequestWithEventsPreservingTurnState) are labelledrequest_kind: compactionwhile staying in the current turn. A committed manual or automatic compaction advances the context-window generation for subsequent requests (Agent.Runtime.Providers.OpenAI).createCodexMessageWithProviderAtand friends) are attributed as standalone turns.Agent.Uuid(agent-core) provides the RFC 9562 UUID version 7 generator.Known limits
client_metadatapasses through unchanged.Test plan
cabal repl agent-core:test:agent-core-test→:main --match Agent.Uuid: 5 examples, 0 failurescabal repl agent-openai:test:agent-openai-test→:main: 447 examples, 0 failures, 1 pending (pre-existing)agent-core,agent-openai,agent-runtimelibraries load in the GHCi multi-repl without new warnings🤖 Generated with Claude Code
Gateway counterpart: digitallyinduced/haskell-agent-gateway#181 forwards these headers to
chatgpt.com.