fix(agent): preserve live merged tool-call carriers

Identify a completed merged assistant handoff from the carrier's own stop state instead of an unrelated adjacent history row. Keep carriers with pending tool calls actionable so compaction cannot abort a live tool chain.
This commit is contained in:
abundantbeing
2026-08-19 23:08:41 +07:00
committed by kshitij
parent fdf0111410
commit 6b7aee2f80
2 changed files with 28 additions and 20 deletions
+5 -6
View File
@@ -7978,7 +7978,6 @@ def reference_handoff_would_drive_next_model_call(
for index, message in enumerate(messages):
if not is_compaction_summary_message(message):
continue
previous = messages[index - 1] if index > 0 else None
merged_completed_assistant = (
isinstance(message, dict)
and message.get("role") == "assistant"
@@ -7986,17 +7985,17 @@ def reference_handoff_would_drive_next_model_call(
message.get("content")
)
== "merged"
and isinstance(previous, dict)
and previous.get("role") == "assistant"
and previous.get("finish_reason") == "stop"
and message.get("finish_reason") == "stop"
and not message.get("tool_calls")
)
if (
_handoff_carries_live_user_content(message)
and not merged_completed_assistant
):
# Embedded live ask — this row is not a sole-handoff driver. A
# merged assistant carrier after a completed stop preserves the
# assistant's own prose/tool_calls, not a fresh user request.
# completed merged assistant carrier preserves the assistant's own
# prose, not a fresh user request. A carrier with pending tool_calls
# remains live regardless of an earlier completed assistant turn.
continue
last_driving_handoff = index
@@ -42,9 +42,11 @@ def _standalone_handoff(task: str = "finish the already-done refactor") -> dict:
}
def _merged_assistant_carrier() -> dict:
"""Production merge-into-tail shape: assistant role/tool_calls survive."""
return {
def _merged_assistant_carrier(
*, finish_reason: str = "stop", tool_calls: list[dict] | None = None
) -> dict:
"""Production merge-into-tail shape with the carrier's completion state."""
carrier = {
"role": "assistant",
"content": (
f"{_MERGED_PRIOR_CONTEXT_HEADER}\n"
@@ -54,10 +56,13 @@ def _merged_assistant_carrier() -> dict:
"User asked: 'finish the already-done refactor'\n\n"
f"{_SUMMARY_END_MARKER}"
),
"tool_calls": [{"id": "stale-c1", "function": {"name": "terminal"}}],
"finish_reason": finish_reason,
COMPRESSED_SUMMARY_METADATA_KEY: True,
COMPRESSED_SUMMARY_HAS_USER_TURN_KEY: True,
}
if tool_calls is not None:
carrier["tool_calls"] = tool_calls
return carrier
class TestReferenceHandoffWouldDriveNextModelCall:
@@ -97,23 +102,27 @@ class TestReferenceHandoffWouldDriveNextModelCall:
]
assert reference_handoff_would_drive_next_model_call(messages) is False
def test_merged_assistant_carrier_after_completed_stop_drives(self):
"""Completed assistant prose/tool_calls on the carrier are not a user ask."""
def test_completed_merged_assistant_carrier_drives_without_adjacent_stop(self):
"""The carrier's own stop marks preserved assistant prose as completed."""
messages = [
{"role": "system", "content": "system"},
{"role": "user", "content": "please finish the refactor"},
{
"role": "assistant",
"content": "Refactor complete.",
"finish_reason": "stop",
},
_merged_assistant_carrier(),
]
assert reference_handoff_would_drive_next_model_call(messages) is True
def test_merged_assistant_carrier_without_completed_stop_stays_in_flight(self):
def test_live_tool_call_carrier_after_completed_stop_stays_in_flight(self):
"""Pending tool_calls on the carrier are live even after an earlier stop."""
messages = [
{"role": "user", "content": "please finish the refactor"},
_merged_assistant_carrier(),
{
"role": "assistant",
"content": "Earlier turn complete.",
"finish_reason": "stop",
},
_merged_assistant_carrier(
finish_reason="tool_calls",
tool_calls=[{"id": "live-c1", "function": {"name": "terminal"}}],
),
]
assert reference_handoff_would_drive_next_model_call(messages) is False