Skip to content

fix(agent): merge consecutive assistant messages before API replay (#29148, #49147) - #55603

Merged
teknium1 merged 2 commits into
mainfrom
fix/29148-49147-merge-consecutive-assistant
Jun 30, 2026
Merged

teknium1 merged 2 commits into
mainfrom
fix/29148-49147-merge-consecutive-assistant

Conversation

@teknium1

Copy link
Copy Markdown
Collaborator

Summary

Strict OpenAI-compatible providers now accept replayed histories that contain consecutive assistant messages — repair_message_sequence() collapses them into a single assistant turn before every API call.

Root cause: DeepSeek v4, Moonshot/Kimi and similar strict providers reject a history where an assistant message carrying tool_calls is immediately followed by another assistant message instead of its tool results (HTTP 400 — "An assistant message with 'tool_calls' must be followed by tool messages…"). repair_message_sequence — the defensive belt run before every API call — fixed orphan-tool and consecutive-user shapes but never merged consecutive assistant messages. That gap is the bug.

The split shape is produced by recovery/continuation paths that append an interim assistant turn (thinking-prefill, codex incomplete-continuation) and by host-fed / legacy-persisted / resumed histories. The serializer itself is 1:1 and never splits — verified by repro — so the fix belongs in the repair pass, not serialization.

This consolidates three contributor PRs (#29168 @Bartok9, #49162 @woaini30050, #34510 @weidzhou) that each fixed one half of the problem, into a single pass covering both reported shapes.

Changes

  • agent/agent_runtime_helpers.py: add Pass 0 to repair_message_sequence() — merges adjacent assistant messages (union of tool_calls, concatenated content, carried reasoning_content). Runs before Pass 1 so the merged union of tool_call ids is known to the orphan-tool filter. A tool result or user turn between two assistants blocks the merge (distinct, valid rounds).
  • tests/run_agent/test_message_sequence_repair.py: 8 regression tests.

Shapes handled

Input Result
assistant(tc=[A])assistant(tc=[B]) (parallel split, #29148) assistant(tc=[A,B])
assistant(content)assistant(tc=[A]) (content-then-tool, #49147) assistant(content, tc=[A])
3× consecutive assistant(tc) one turn, 3 tool_calls
two text-only assistants one merged text turn
assistant(tc)toolassistant(tc) not merged (distinct rounds)
assistantuserassistant not merged (normal dialog)
already-valid single assistant(tc=[A,B]) unchanged (repairs == 0)

Validation

Before After
repair_message_sequence on split shape left consecutive assistants → DeepSeek 400 merged into one valid turn
tests/run_agent/test_message_sequence_repair.py 16 pass 24 pass
Live DeepSeek replay of repaired #49147 shape n/a HTTP 200, summarized correctly
orphan-tool / consecutive-user passes pass pass (unchanged)

E2E: ran the real repair_message_sequence on the exact #29148 and #49147 shapes, asserted the output is structurally valid (every assistant(tool_calls) immediately followed by its tool results, no consecutive assistants), then replayed the repaired #49147 history to deepseek/deepseek-chat-v3.1 — 200 OK.

Closes #29148, closes #49147.

Infographic

Consecutive assistant merge

Nous Research

@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 P1 High — major feature broken, no workaround labels Jun 30, 2026
teknium1 and others added 2 commits June 30, 2026 04:05
…quence

Strict OpenAI-compatible providers (DeepSeek v4, Moonshot/Kimi) reject a
replayed history where an assistant message carrying tool_calls is
immediately followed by another assistant message instead of its tool
results — HTTP 400 'An assistant message with tool_calls must be
followed by tool messages...'.

repair_message_sequence (the defensive belt run before every API call)
fixed orphan-tool and consecutive-user shapes but never merged
consecutive assistant messages. Adds a Pass 0 that collapses adjacent
assistant turns into one — union of tool_calls, concatenated content,
carried reasoning_content — covering both reported shapes:
  - parallel tool calls split across two assistant turns (#29148)
  - content-only assistant followed by tool_calls-only assistant (#49147)

A tool result or user turn between two assistants blocks the merge
(distinct, valid rounds). Runs before Pass 1 so the merged union of
tool_call ids is known to the orphan-tool filter.

Closes #29148, #49147.
Co-authored-by: Bartok9 <danielrpike9@gmail.com>
Co-authored-by: woaini30050 <woaini30050@users.noreply.github.com>
Co-authored-by: weidzhou <weidzhou@users.noreply.github.com>
The Pass 0 consecutive-assistant merge collapsed codex_responses interim
turns, which legitimately stay separate — each carries its own encrypted
continuation state (codex_reasoning_items / codex_message_items) that
must replay verbatim. Skip the merge when either side is a codex interim
(has codex_reasoning_items / codex_message_items / finish_reason=='incomplete').

Fixes the slice-2 regression in test_run_agent_codex_responses.py
(test_duplicate_detection_distinguishes_different_codex_{reasoning,message_items}).
@alt-glitch

Copy link
Copy Markdown
Contributor

This was generated by AI during triage.

Consolidates three contributor PRs into one repair pass: #29168 (@Bartok9, merge separately-stored consecutive assistant tool_calls), #49162 (@woaini30050, split-one-record shape for #49147), and #34510 (@weidzhou, closed). Superset of both cluster mechanisms (#29148 merge-shape + #49147 split-shape) — related, not a duplicate. Maintainer to pick between this consolidation and the individual open PRs.

@teknium1
teknium1 force-pushed the fix/29148-49147-merge-consecutive-assistant branch from a8565cf to 43bf4d5 Compare June 30, 2026 11:05
@Bartok9

Bartok9 commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Thanks for the clean triage @alt-glitch. Confirming the relationship: #29168 fixes the parallel-split shape (assistant(tc=[A])assistant(tc=[B]), #29148), which this PR's Pass-0 covers as one half of its superset. No objection to consolidating — a single repair pass covering both #29148 and #49147 shapes is the cleaner long-term surface than three partial PRs. Happy to close #29168 in favor of this if you'd rather land the consolidation; just say the word and I'll defer.

@tonydwb tonydwb left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review Summary

Verdict: Approved

Excellent fix for a real compatibility issue with strict OpenAI-compatible providers (DeepSeek v4, Moonshot/Kimi). The merge logic correctly handles all edge cases: parallel tool_calls split across assistants, content+tool_calls splits, text-only consecutive assistants, and the critical guard against merging tool-call rounds separated by tool results. Comprehensive test coverage with 8 test cases.

Looks Good

  • Well-documented root cause (refs #29148, #49147)
  • Correct ordering: merge runs BEFORE Pass 1 so tool_call union is known for orphan detection
  • Handles reasoning_content preservation correctly
  • The multimodal content guard (leave list content alone) is prudent

Reviewed by Hermes Agent

@teknium1
teknium1 merged commit cbe397e into main Jun 30, 2026
31 checks passed
@teknium1
teknium1 deleted the fix/29148-49147-merge-consecutive-assistant branch June 30, 2026 11:22
dtera pushed a commit to dtera/hermes-agent that referenced this pull request Jul 1, 2026
…ousResearch#29148, NousResearch#49147) (NousResearch#55603)

* fix(agent): merge consecutive assistant messages in repair_message_sequence

Strict OpenAI-compatible providers (DeepSeek v4, Moonshot/Kimi) reject a
replayed history where an assistant message carrying tool_calls is
immediately followed by another assistant message instead of its tool
results — HTTP 400 'An assistant message with tool_calls must be
followed by tool messages...'.

repair_message_sequence (the defensive belt run before every API call)
fixed orphan-tool and consecutive-user shapes but never merged
consecutive assistant messages. Adds a Pass 0 that collapses adjacent
assistant turns into one — union of tool_calls, concatenated content,
carried reasoning_content — covering both reported shapes:
  - parallel tool calls split across two assistant turns (NousResearch#29148)
  - content-only assistant followed by tool_calls-only assistant (NousResearch#49147)

A tool result or user turn between two assistants blocks the merge
(distinct, valid rounds). Runs before Pass 1 so the merged union of
tool_call ids is known to the orphan-tool filter.

Closes NousResearch#29148, NousResearch#49147.
Co-authored-by: Bartok9 <danielrpike9@gmail.com>
Co-authored-by: woaini30050 <woaini30050@users.noreply.github.com>
Co-authored-by: weidzhou <weidzhou@users.noreply.github.com>

* fix(agent): exempt codex Responses interim turns from assistant merge

The Pass 0 consecutive-assistant merge collapsed codex_responses interim
turns, which legitimately stay separate — each carries its own encrypted
continuation state (codex_reasoning_items / codex_message_items) that
must replay verbatim. Skip the merge when either side is a codex interim
(has codex_reasoning_items / codex_message_items / finish_reason=='incomplete').

Fixes the slice-2 regression in test_run_agent_codex_responses.py
(test_duplicate_detection_distinguishes_different_codex_{reasoning,message_items}).

---------

Co-authored-by: Bartok9 <danielrpike9@gmail.com>
Co-authored-by: woaini30050 <woaini30050@users.noreply.github.com>
Co-authored-by: weidzhou <weidzhou@users.noreply.github.com>
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…ousResearch#29148, NousResearch#49147) (NousResearch#55603)

* fix(agent): merge consecutive assistant messages in repair_message_sequence

Strict OpenAI-compatible providers (DeepSeek v4, Moonshot/Kimi) reject a
replayed history where an assistant message carrying tool_calls is
immediately followed by another assistant message instead of its tool
results — HTTP 400 'An assistant message with tool_calls must be
followed by tool messages...'.

repair_message_sequence (the defensive belt run before every API call)
fixed orphan-tool and consecutive-user shapes but never merged
consecutive assistant messages. Adds a Pass 0 that collapses adjacent
assistant turns into one — union of tool_calls, concatenated content,
carried reasoning_content — covering both reported shapes:
  - parallel tool calls split across two assistant turns (NousResearch#29148)
  - content-only assistant followed by tool_calls-only assistant (NousResearch#49147)

A tool result or user turn between two assistants blocks the merge
(distinct, valid rounds). Runs before Pass 1 so the merged union of
tool_call ids is known to the orphan-tool filter.

Closes NousResearch#29148, NousResearch#49147.
Co-authored-by: Bartok9 <danielrpike9@gmail.com>
Co-authored-by: woaini30050 <woaini30050@users.noreply.github.com>
Co-authored-by: weidzhou <weidzhou@users.noreply.github.com>

* fix(agent): exempt codex Responses interim turns from assistant merge

The Pass 0 consecutive-assistant merge collapsed codex_responses interim
turns, which legitimately stay separate — each carries its own encrypted
continuation state (codex_reasoning_items / codex_message_items) that
must replay verbatim. Skip the merge when either side is a codex interim
(has codex_reasoning_items / codex_message_items / finish_reason=='incomplete').

Fixes the slice-2 regression in test_run_agent_codex_responses.py
(test_duplicate_detection_distinguishes_different_codex_{reasoning,message_items}).

---------

Co-authored-by: Bartok9 <danielrpike9@gmail.com>
Co-authored-by: woaini30050 <woaini30050@users.noreply.github.com>
Co-authored-by: weidzhou <weidzhou@users.noreply.github.com>
Jasper6439 pushed a commit to Jasper6439/hermes-agent that referenced this pull request Jul 5, 2026
…ousResearch#29148, NousResearch#49147) (NousResearch#55603)

* fix(agent): merge consecutive assistant messages in repair_message_sequence

Strict OpenAI-compatible providers (DeepSeek v4, Moonshot/Kimi) reject a
replayed history where an assistant message carrying tool_calls is
immediately followed by another assistant message instead of its tool
results — HTTP 400 'An assistant message with tool_calls must be
followed by tool messages...'.

repair_message_sequence (the defensive belt run before every API call)
fixed orphan-tool and consecutive-user shapes but never merged
consecutive assistant messages. Adds a Pass 0 that collapses adjacent
assistant turns into one — union of tool_calls, concatenated content,
carried reasoning_content — covering both reported shapes:
  - parallel tool calls split across two assistant turns (NousResearch#29148)
  - content-only assistant followed by tool_calls-only assistant (NousResearch#49147)

A tool result or user turn between two assistants blocks the merge
(distinct, valid rounds). Runs before Pass 1 so the merged union of
tool_call ids is known to the orphan-tool filter.

Closes NousResearch#29148, NousResearch#49147.
Co-authored-by: Bartok9 <danielrpike9@gmail.com>
Co-authored-by: woaini30050 <woaini30050@users.noreply.github.com>
Co-authored-by: weidzhou <weidzhou@users.noreply.github.com>

* fix(agent): exempt codex Responses interim turns from assistant merge

The Pass 0 consecutive-assistant merge collapsed codex_responses interim
turns, which legitimately stay separate — each carries its own encrypted
continuation state (codex_reasoning_items / codex_message_items) that
must replay verbatim. Skip the merge when either side is a codex interim
(has codex_reasoning_items / codex_message_items / finish_reason=='incomplete').

Fixes the slice-2 regression in test_run_agent_codex_responses.py
(test_duplicate_detection_distinguishes_different_codex_{reasoning,message_items}).

---------

Co-authored-by: Bartok9 <danielrpike9@gmail.com>
Co-authored-by: woaini30050 <woaini30050@users.noreply.github.com>
Co-authored-by: weidzhou <weidzhou@users.noreply.github.com>
habarmc1223-sudo pushed a commit to habarmc1223-sudo/hermes-agent-fluxmem that referenced this pull request Jul 8, 2026
…ousResearch#29148, NousResearch#49147) (NousResearch#55603)

* fix(agent): merge consecutive assistant messages in repair_message_sequence

Strict OpenAI-compatible providers (DeepSeek v4, Moonshot/Kimi) reject a
replayed history where an assistant message carrying tool_calls is
immediately followed by another assistant message instead of its tool
results — HTTP 400 'An assistant message with tool_calls must be
followed by tool messages...'.

repair_message_sequence (the defensive belt run before every API call)
fixed orphan-tool and consecutive-user shapes but never merged
consecutive assistant messages. Adds a Pass 0 that collapses adjacent
assistant turns into one — union of tool_calls, concatenated content,
carried reasoning_content — covering both reported shapes:
  - parallel tool calls split across two assistant turns (NousResearch#29148)
  - content-only assistant followed by tool_calls-only assistant (NousResearch#49147)

A tool result or user turn between two assistants blocks the merge
(distinct, valid rounds). Runs before Pass 1 so the merged union of
tool_call ids is known to the orphan-tool filter.

Closes NousResearch#29148, NousResearch#49147.
Co-authored-by: Bartok9 <danielrpike9@gmail.com>
Co-authored-by: woaini30050 <woaini30050@users.noreply.github.com>
Co-authored-by: weidzhou <weidzhou@users.noreply.github.com>

* fix(agent): exempt codex Responses interim turns from assistant merge

The Pass 0 consecutive-assistant merge collapsed codex_responses interim
turns, which legitimately stay separate — each carries its own encrypted
continuation state (codex_reasoning_items / codex_message_items) that
must replay verbatim. Skip the merge when either side is a codex interim
(has codex_reasoning_items / codex_message_items / finish_reason=='incomplete').

Fixes the slice-2 regression in test_run_agent_codex_responses.py
(test_duplicate_detection_distinguishes_different_codex_{reasoning,message_items}).

---------

Co-authored-by: Bartok9 <danielrpike9@gmail.com>
Co-authored-by: woaini30050 <woaini30050@users.noreply.github.com>
Co-authored-by: weidzhou <weidzhou@users.noreply.github.com>
santhreal pushed a commit to santhreal/hermes-agent that referenced this pull request Jul 13, 2026
…ousResearch#29148, NousResearch#49147) (NousResearch#55603)

* fix(agent): merge consecutive assistant messages in repair_message_sequence

Strict OpenAI-compatible providers (DeepSeek v4, Moonshot/Kimi) reject a
replayed history where an assistant message carrying tool_calls is
immediately followed by another assistant message instead of its tool
results — HTTP 400 'An assistant message with tool_calls must be
followed by tool messages...'.

repair_message_sequence (the defensive belt run before every API call)
fixed orphan-tool and consecutive-user shapes but never merged
consecutive assistant messages. Adds a Pass 0 that collapses adjacent
assistant turns into one — union of tool_calls, concatenated content,
carried reasoning_content — covering both reported shapes:
  - parallel tool calls split across two assistant turns (NousResearch#29148)
  - content-only assistant followed by tool_calls-only assistant (NousResearch#49147)

A tool result or user turn between two assistants blocks the merge
(distinct, valid rounds). Runs before Pass 1 so the merged union of
tool_call ids is known to the orphan-tool filter.

Closes NousResearch#29148, NousResearch#49147.
Co-authored-by: Bartok9 <danielrpike9@gmail.com>
Co-authored-by: woaini30050 <woaini30050@users.noreply.github.com>
Co-authored-by: weidzhou <weidzhou@users.noreply.github.com>

* fix(agent): exempt codex Responses interim turns from assistant merge

The Pass 0 consecutive-assistant merge collapsed codex_responses interim
turns, which legitimately stay separate — each carries its own encrypted
continuation state (codex_reasoning_items / codex_message_items) that
must replay verbatim. Skip the merge when either side is a codex interim
(has codex_reasoning_items / codex_message_items / finish_reason=='incomplete').

Fixes the slice-2 regression in test_run_agent_codex_responses.py
(test_duplicate_detection_distinguishes_different_codex_{reasoning,message_items}).

---------

Co-authored-by: Bartok9 <danielrpike9@gmail.com>
Co-authored-by: woaini30050 <woaini30050@users.noreply.github.com>
Co-authored-by: weidzhou <weidzhou@users.noreply.github.com>
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
…ousResearch#29148, NousResearch#49147) (NousResearch#55603)

* fix(agent): merge consecutive assistant messages in repair_message_sequence

Strict OpenAI-compatible providers (DeepSeek v4, Moonshot/Kimi) reject a
replayed history where an assistant message carrying tool_calls is
immediately followed by another assistant message instead of its tool
results — HTTP 400 'An assistant message with tool_calls must be
followed by tool messages...'.

repair_message_sequence (the defensive belt run before every API call)
fixed orphan-tool and consecutive-user shapes but never merged
consecutive assistant messages. Adds a Pass 0 that collapses adjacent
assistant turns into one — union of tool_calls, concatenated content,
carried reasoning_content — covering both reported shapes:
  - parallel tool calls split across two assistant turns (NousResearch#29148)
  - content-only assistant followed by tool_calls-only assistant (NousResearch#49147)

A tool result or user turn between two assistants blocks the merge
(distinct, valid rounds). Runs before Pass 1 so the merged union of
tool_call ids is known to the orphan-tool filter.

Closes NousResearch#29148, NousResearch#49147.
Co-authored-by: Bartok9 <danielrpike9@gmail.com>
Co-authored-by: woaini30050 <woaini30050@users.noreply.github.com>
Co-authored-by: weidzhou <weidzhou@users.noreply.github.com>

* fix(agent): exempt codex Responses interim turns from assistant merge

The Pass 0 consecutive-assistant merge collapsed codex_responses interim
turns, which legitimately stay separate — each carries its own encrypted
continuation state (codex_reasoning_items / codex_message_items) that
must replay verbatim. Skip the merge when either side is a codex interim
(has codex_reasoning_items / codex_message_items / finish_reason=='incomplete').

Fixes the slice-2 regression in test_run_agent_codex_responses.py
(test_duplicate_detection_distinguishes_different_codex_{reasoning,message_items}).

---------

Co-authored-by: Bartok9 <danielrpike9@gmail.com>
Co-authored-by: woaini30050 <woaini30050@users.noreply.github.com>
Co-authored-by: weidzhou <weidzhou@users.noreply.github.com>
leewenjie pushed a commit to leewenjie/hermes-agent that referenced this pull request Aug 7, 2026
…ousResearch#29148, NousResearch#49147) (NousResearch#55603)

* fix(agent): merge consecutive assistant messages in repair_message_sequence

Strict OpenAI-compatible providers (DeepSeek v4, Moonshot/Kimi) reject a
replayed history where an assistant message carrying tool_calls is
immediately followed by another assistant message instead of its tool
results — HTTP 400 'An assistant message with tool_calls must be
followed by tool messages...'.

repair_message_sequence (the defensive belt run before every API call)
fixed orphan-tool and consecutive-user shapes but never merged
consecutive assistant messages. Adds a Pass 0 that collapses adjacent
assistant turns into one — union of tool_calls, concatenated content,
carried reasoning_content — covering both reported shapes:
  - parallel tool calls split across two assistant turns (NousResearch#29148)
  - content-only assistant followed by tool_calls-only assistant (NousResearch#49147)

A tool result or user turn between two assistants blocks the merge
(distinct, valid rounds). Runs before Pass 1 so the merged union of
tool_call ids is known to the orphan-tool filter.

Closes NousResearch#29148, NousResearch#49147.
Co-authored-by: Bartok9 <danielrpike9@gmail.com>
Co-authored-by: woaini30050 <woaini30050@users.noreply.github.com>
Co-authored-by: weidzhou <weidzhou@users.noreply.github.com>

* fix(agent): exempt codex Responses interim turns from assistant merge

The Pass 0 consecutive-assistant merge collapsed codex_responses interim
turns, which legitimately stay separate — each carries its own encrypted
continuation state (codex_reasoning_items / codex_message_items) that
must replay verbatim. Skip the merge when either side is a codex interim
(has codex_reasoning_items / codex_message_items / finish_reason=='incomplete').

Fixes the slice-2 regression in test_run_agent_codex_responses.py
(test_duplicate_detection_distinguishes_different_codex_{reasoning,message_items}).

---------

Co-authored-by: Bartok9 <danielrpike9@gmail.com>
Co-authored-by: woaini30050 <woaini30050@users.noreply.github.com>
Co-authored-by: weidzhou <weidzhou@users.noreply.github.com>
KiruyaMomochi added a commit to KiruyaMomochi/hermes-agent that referenced this pull request Sep 2, 2026
…g consecutive assistants

_merge_consecutive_roles() only propagated _thinking_signature_invalidated
when the second message already carried it. The merge itself mutates the
surviving turn boundary, so EVERY thinking signature dies — including the
ones from the first message, which were signed against pre-merge content.

Unconditionally flag the merged turn so _manage_thinking_signatures demotes
stale signatures instead of replaying them into a 400 "Invalid signature in
thinking block".

Additionally downgrade the second message's thinking blocks to text at
merge time (rather than dropping them) to preserve reasoning content.
Anthropic requires thinking blocks to precede text/tool_use within a turn;
concatenating would strand them after the first message's content.

Latent upstream bug: reachable via prefill messages, custom context engines,
Codex→Anthropic model switch, or direct adapter calls, but not triggered by
ordinary conversation history (which is repaired earlier at
repair_message_sequence_with_cursor since cbe397e NousResearch#55603).
melon-xf added a commit to melon-xf/hermes-agent that referenced this pull request Sep 3, 2026
…ousResearch#29148, NousResearch#49147) (NousResearch#55603)

* fix(agent): merge consecutive assistant messages in repair_message_sequence

Strict OpenAI-compatible providers (DeepSeek v4, Moonshot/Kimi) reject a
replayed history where an assistant message carrying tool_calls is
immediately followed by another assistant message instead of its tool
results — HTTP 400 'An assistant message with tool_calls must be
followed by tool messages...'.

repair_message_sequence (the defensive belt run before every API call)
fixed orphan-tool and consecutive-user shapes but never merged
consecutive assistant messages. Adds a Pass 0 that collapses adjacent
assistant turns into one — union of tool_calls, concatenated content,
carried reasoning_content — covering both reported shapes:
  - parallel tool calls split across two assistant turns (NousResearch#29148)
  - content-only assistant followed by tool_calls-only assistant (NousResearch#49147)

A tool result or user turn between two assistants blocks the merge
(distinct, valid rounds). Runs before Pass 1 so the merged union of
tool_call ids is known to the orphan-tool filter.

Closes NousResearch#29148, NousResearch#49147.
Co-authored-by: Bartok9 <danielrpike9@gmail.com>
Co-authored-by: woaini30050 <woaini30050@users.noreply.github.com>
Co-authored-by: weidzhou <weidzhou@users.noreply.github.com>

* fix(agent): exempt codex Responses interim turns from assistant merge

The Pass 0 consecutive-assistant merge collapsed codex_responses interim
turns, which legitimately stay separate — each carries its own encrypted
continuation state (codex_reasoning_items / codex_message_items) that
must replay verbatim. Skip the merge when either side is a codex interim
(has codex_reasoning_items / codex_message_items / finish_reason=='incomplete').

Fixes the slice-2 regression in test_run_agent_codex_responses.py
(test_duplicate_detection_distinguishes_different_codex_{reasoning,message_items}).

---------

Co-authored-by: Bartok9 <danielrpike9@gmail.com>
Co-authored-by: woaini30050 <woaini30050@users.noreply.github.com>
Co-authored-by: weidzhou <weidzhou@users.noreply.github.com>
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 P1 High — major feature broken, no workaround type/bug Something isn't working

Projects

None yet

4 participants