fix(agents): detect incomplete tool-use turns with pre-tool text (#76477)#76544
fix(agents): detect incomplete tool-use turns with pre-tool text (#76477)#76544
Conversation
) When the last assistant message ended with stopReason=toolUse, pre-tool text alone (payloadCount > 0) was suppressing the incomplete-turn guard. The model expected to continue after tool results but the post-tool response was never produced, silently dropping the final answer. Fix isIncompleteTerminalAssistantTurn to always flag toolUse stop reason as incomplete regardless of pre-tool text, and update the early-return condition in resolveIncompleteTurnPayloadText to not skip the check when the last assistant ended with a tool call.
|
Codex review: needs maintainer review before merge. Summary Reproducibility: yes. Source inspection of current main gives a high-confidence reproduction path: an attempt with Next step before merge Security Review detailsBest possible solution: Land this focused runner/lifecycle fix with its changelog entry after maintainer review and targeted agent test validation. Do we have a high-confidence way to reproduce the issue? Yes. Source inspection of current main gives a high-confidence reproduction path: an attempt with Is this the best way to solve the issue? Yes. The proposed fix is the narrow maintainable boundary: treat terminal What I checked:
Likely related people:
Remaining risk / open question:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 9772ce6ce975. |
…n lifecycle (#76477) The lifecycle handler's derivedWorkingTerminalState was emitting 'working' for interrupted tool-use turns with pre-tool text because it required !hasAssistantVisibleText for the 'abandoned' state. Update the derivation to also mark as 'abandoned' when incompleteTerminalAssistant is true, so lifecycle consumers see a consistent state with the runner's terminal result.
|
🦞🦞 Supported commands: I only act for maintainers, or for trusted ClawSweeper feedback on a ClawSweeper PR or PR opted into |
|
/clawsweeper re-review |
|
🦞🦞 I asked ClawSweeper to review this item again. |
…nclaw#76477) (openclaw#76544) * fix(agents): detect incomplete tool-use turns with pre-tool text (openclaw#76477) When the last assistant message ended with stopReason=toolUse, pre-tool text alone (payloadCount > 0) was suppressing the incomplete-turn guard. The model expected to continue after tool results but the post-tool response was never produced, silently dropping the final answer. Fix isIncompleteTerminalAssistantTurn to always flag toolUse stop reason as incomplete regardless of pre-tool text, and update the early-return condition in resolveIncompleteTurnPayloadText to not skip the check when the last assistant ended with a tool call. * fix(agents): mark tool-use terminal with pre-tool text as abandoned in lifecycle (openclaw#76477) The lifecycle handler's derivedWorkingTerminalState was emitting 'working' for interrupted tool-use turns with pre-tool text because it required !hasAssistantVisibleText for the 'abandoned' state. Update the derivation to also mark as 'abandoned' when incompleteTerminalAssistant is true, so lifecycle consumers see a consistent state with the runner's terminal result.
…nclaw#76477) (openclaw#76544) * fix(agents): detect incomplete tool-use turns with pre-tool text (openclaw#76477) When the last assistant message ended with stopReason=toolUse, pre-tool text alone (payloadCount > 0) was suppressing the incomplete-turn guard. The model expected to continue after tool results but the post-tool response was never produced, silently dropping the final answer. Fix isIncompleteTerminalAssistantTurn to always flag toolUse stop reason as incomplete regardless of pre-tool text, and update the early-return condition in resolveIncompleteTurnPayloadText to not skip the check when the last assistant ended with a tool call. * fix(agents): mark tool-use terminal with pre-tool text as abandoned in lifecycle (openclaw#76477) The lifecycle handler's derivedWorkingTerminalState was emitting 'working' for interrupted tool-use turns with pre-tool text because it required !hasAssistantVisibleText for the 'abandoned' state. Update the derivation to also mark as 'abandoned' when incompleteTerminalAssistant is true, so lifecycle consumers see a consistent state with the runner's terminal result.
Summary
Fixes #76477 — Agent tool-chain final text segments silently dropped.
When an agent turn contains tool calls (text → toolCall → toolResult → expected final text), the final text segment after the last tool result was silently dropped from both UI and next-turn context.
Root Cause
Two related bugs in
src/agents/pi-embedded-runner/run/incomplete-turn.ts:resolveIncompleteTurnPayloadTextimmediately returnednull("turn is complete") whenpayloadCount !== 0. But pre-tool text (before the tool call) created payloads, sopayloadCount > 0suppressed the incomplete-turn check — even when the model expected to continue after tool results and the post-tool response was never produced.isIncompleteTerminalAssistantTurnonly flagged incomplete turns when!hasAssistantVisibleText && stopReason === "toolUse". With pre-tool text existing,hasAssistantVisibleTextwas true, so even if reached, this check returned false.The failing scenario
Fix
isIncompleteTerminalAssistantTurn: Now returnstruewhenstopReason === "toolUse"regardless of whether pre-tool text exists — a tool-use stop reason always means the model expected to continue after tool results.resolveIncompleteTurnPayloadText: Added atoolUseTerminalguard sopayloadCount !== 0no longer suppresses the incomplete-turn check when the last assistant message ended with a tool call.Tests
5 new tests added to
run.incomplete-turn.test.ts:isIncompleteTerminalAssistantTurnflags tool-use stop reason even with pre-tool textresolveIncompleteTurnPayloadTextdetects tool-use terminal with pre-tool text as incompleteend_turnstopReason is correctly NOT flaggedrunEmbeddedPiAgentsurfaces error and logs warning for the scenarioAll 90 tests in the file pass. All 4 related test files mentioned in the review pass.