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:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user