fix(codex): drop foreign-issuer reasoning on replay - #33156
Merged
Conversation
reasoning.encrypted_content is sealed to the Responses endpoint that minted it. When a session switches model providers mid-conversation — say the user runs /model gpt-5.5 after several turns on grok-4.3, or vice versa — the persisted codex_reasoning_items carry blobs the new endpoint cannot decrypt, and every subsequent turn fails with HTTP 400 invalid_encrypted_content. This is the cross-issuer prevention layer. Pairs with: * PR #33035 — runtime recovery when the HTTP 400 fires anyway * PR #33146 — prevention for transient rs_tmp_* items Stamps each reasoning item with the issuer kind that minted it (codex_backend / xai_responses / github_responses / other:<url>) at normalize time, then drops items at replay time when the active endpoint differs from the stamp. Unstamped (legacy) items pass through for backwards compatibility. Cherry-picked from @chaconne67's PR #31629. Conflict against current main (#33035's replay_encrypted_reasoning parameter) resolved as 'keep both' — the two guards compose: replay_encrypted_reasoning=False is the session-wide kill switch, current_issuer_kind is the per-item filter that runs only when replay is still enabled.
Contributor
🔎 Lint report:
|
1 task
19 tasks
1 task
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.
Salvage of #31629 (@chaconne67) onto current main — cross-issuer prevention layer for encrypted-content corruption.
Summary
reasoning.encrypted_contentis sealed to the Responses endpoint that minted it. When a session switches model providers mid-conversation (e.g./model gpt-5.5after several turns ongrok-4.3), persistedcodex_reasoning_itemscarry blobs the new endpoint cannot decrypt, and every subsequent turn fails with HTTP 400invalid_encrypted_content.This PR stamps each reasoning item with the issuer kind that minted it (
codex_backend/xai_responses/github_responses/other:<url>) at normalize time, then drops items at replay time when the active endpoint differs from the stamp. Unstamped (legacy) items pass through for backwards compatibility.Defense-in-depth alignment
Three complementary layers now:
rs_tmp_*reasoning itemsinvalid_encrypted_contentfires anyway, disable replay for the session and retryChanges
agent/codex_responses_adapter.py:_classify_responses_issuer()helper builds a stable identifier from transport params._chat_messages_to_responses_input()gainscurrent_issuer_kindparam + per-item filter inside the replay loop._normalize_codex_response()gainsissuer_kindparam that stamps each reasoning item before persistence.agent/transports/codex.py:ResponsesApiTransportresolves the issuer kind once per call, threads it through bothconvert_messagesandnormalize_response. Caches_last_issuer_kindfor the normalize path.tests/run_agent/test_codex_xai_oauth_recovery.py: regression coverage for the cross-issuer drop, the stamp, and the legacy-unstamped passthrough.Conflict resolution against current main
PR #33035 (recovery layer) landed after this PR was opened and added a
replay_encrypted_reasoning: bool = Trueparameter to the same function. Resolved as keep both — the two guards compose:replay_encrypted_reasoning=Falseis the session-wide kill switch (drops ALL replay)current_issuer_kindis the per-item filter that runs only when replay is still enabledWhen replay is killed at the session level, the cross-issuer check is short-circuited because the iteration over
codex_reasoning_itemsdoesn't fire in the first place.Validation
tests/run_agent/test_codex_xai_oauth_recovery.py+tests/agent/test_codex_responses_adapter.py+tests/run_agent/test_run_agent_codex_responses.py→ 112/112 passingAttribution
Cherry-picked from @chaconne67's #31629. AUTHOR_MAP updated in follow-up commit. Original PR opened May 21; conflict against current main resolved as keep-both because #33035 introduced an orthogonal guard layer.
Infographic