Skip to content

fix(codex): drop foreign-issuer reasoning on replay - #33156

Merged
teknium1 merged 2 commits into
mainfrom
hermes/hermes-5bf34d29
May 27, 2026
Merged

fix(codex): drop foreign-issuer reasoning on replay#33156
teknium1 merged 2 commits into
mainfrom
hermes/hermes-5bf34d29

Conversation

@teknium1

@teknium1 teknium1 commented May 27, 2026

Copy link
Copy Markdown
Contributor

Salvage of #31629 (@chaconne67) onto current main — cross-issuer prevention layer for encrypted-content corruption.

Summary

reasoning.encrypted_content is sealed to the Responses endpoint that minted it. When a session switches model providers mid-conversation (e.g. /model gpt-5.5 after several turns on grok-4.3), persisted codex_reasoning_items carry blobs the new endpoint cannot decrypt, and every subsequent turn fails with HTTP 400 invalid_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:

  1. Prevention — transient items (PR fix(codex): drop transient rs_tmp reasoning replay state #33146, @kpadilha) — never persist rs_tmp_* reasoning items
  2. Prevention — foreign issuers (THIS PR, @chaconne67) — drop items at replay when issuer differs from active endpoint
  3. Recovery — runtime (PR fix(codex-responses): gracefully recover from invalid_encrypted_content (salvage #10144) #33035, @victorGPT) — when HTTP 400 invalid_encrypted_content fires anyway, disable replay for the session and retry

Changes

  • agent/codex_responses_adapter.py: _classify_responses_issuer() helper builds a stable identifier from transport params. _chat_messages_to_responses_input() gains current_issuer_kind param + per-item filter inside the replay loop. _normalize_codex_response() gains issuer_kind param that stamps each reasoning item before persistence.
  • agent/transports/codex.py: ResponsesApiTransport resolves the issuer kind once per call, threads it through both convert_messages and normalize_response. Caches _last_issuer_kind for 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 = True parameter to the same function. Resolved as keep both — the two guards compose:

  • replay_encrypted_reasoning=False is the session-wide kill switch (drops ALL replay)
  • current_issuer_kind is the per-item filter that runs only when replay is still enabled

When replay is killed at the session level, the cross-issuer check is short-circuited because the iteration over codex_reasoning_items doesn'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 passing

Attribution

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

codex-cross-issuer-guard

chaconne67 and others added 2 commits May 27, 2026 02:30
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.
@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: hermes/hermes-5bf34d29 vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 9493 on HEAD, 9493 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 5013 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@teknium1
teknium1 merged commit 581b021 into main May 27, 2026
26 checks passed
@teknium1
teknium1 deleted the hermes/hermes-5bf34d29 branch May 27, 2026 09:40
@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint provider/copilot GitHub Copilot (ACP + Chat) P3 Low — cosmetic, nice to have labels May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P3 Low — cosmetic, nice to have provider/copilot GitHub Copilot (ACP + Chat) type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants