fix(agent-core): ignore truncated assistant tool calls#97092
Conversation
|
Codex review: needs real behavior proof before merge. Reviewed June 26, 2026, 3:48 PM ET / 19:48 UTC. Summary PR surface: Source +8, Tests +114. Total +122 across 2 files. Reproducibility: yes. Source inspection gives a high-confidence reproduction path: return a final assistant message with stopReason "length" and a toolCall block, and current main routes it into executeToolCalls. Review metrics: 1 noteworthy metric.
Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the focused agent-core guard after maintainers accept the stopReason contract and the contributor adds redacted after-fix real-runtime proof. Do we have a high-confidence way to reproduce the issue? Yes. Source inspection gives a high-confidence reproduction path: return a final assistant message with stopReason "length" and a toolCall block, and current main routes it into executeToolCalls. Is this the best way to solve the issue? Yes. Agent-core owns tool execution and replay context, so gating execution there is the right layer; provider-specific scrubbing would be narrower and would miss plugin/provider adapters that can produce the same invalid terminal shape. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 1089253ca97c. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +8, Tests +114. Total +122 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
Sanitized direct log proof for the truncated sixth Redactions applied: exact parent/child session IDs, session keys, run IDs, local paths/workspace dirs, agent id, task labels, and private PR details. I left timestamps, provider/model/API, terminal status, and aggregate tool counts intact because those are the behavior evidence. Direct accidental-child JSONL / trajectory proof, sanitized: {
"firstSessionRecord": {
"type": "session",
"version": 3,
"id": "[redacted child session id]",
"timestamp": "2026-06-26T02:17:46.897Z",
"cwd": "[redacted local workspace]"
},
"trajectoryStarted": {
"traceSchema": "openclaw-trajectory",
"schemaVersion": 1,
"source": "runtime",
"type": "session.started",
"ts": "2026-06-26T02:17:46.900Z",
"seq": 1,
"sourceSeq": 1,
"traceId": "[redacted trace id]",
"sessionId": "[redacted child session id]",
"sessionKey": "[redacted child session key]",
"runId": "[redacted run id]",
"workspaceDir": "[redacted local workspace]",
"provider": "github-copilot",
"modelId": "claude-opus-4.7",
"modelApi": "anthropic-messages",
"data": {
"trigger": "user",
"sessionFile": "[redacted local session file]",
"workspaceDir": "[redacted local workspace]",
"agentId": "[redacted agent id]",
"messageProvider": "telegram",
"messageChannel": "telegram",
"localModelLean": false,
"toolCount": 10,
"clientToolCount": 0
}
},
"trajectoryEnded": {
"traceSchema": "openclaw-trajectory",
"schemaVersion": 1,
"source": "runtime",
"type": "session.ended",
"ts": "2026-06-26T02:25:40.637Z",
"seq": 7,
"sourceSeq": 7,
"traceId": "[redacted trace id]",
"sessionId": "[redacted child session id]",
"sessionKey": "[redacted child session key]",
"runId": "[redacted run id]",
"workspaceDir": "[redacted local workspace]",
"provider": "github-copilot",
"modelId": "claude-opus-4.7",
"modelApi": "anthropic-messages",
"data": {
"status": "error",
"aborted": false,
"externalAbort": false,
"timedOut": false,
"idleTimedOut": false,
"timedOutDuringCompaction": false,
"timedOutDuringToolExecution": false,
"terminalError": "non_deliverable_terminal_turn"
}
},
"lastMessage": {
"type": "message",
"timestamp": "2026-06-26T02:25:40.519Z",
"role": "assistant"
},
"counts": {
"assistantMsgs": 88,
"toolCalls": 87,
"toolResults": 87,
"toolNames": {
"exec": 81,
"read": 2,
"process": 4
}
}
}This is the direct behavior the patch blocks: a non- |
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
Landed via #97140 as Thank you @galiniliev for the reproduction and agent-loop fix. The maintainer replacement preserves your coauthor credit and adds the provider-normalization, missing-terminal, and error-event hardening found during review. I could not safely update this fork branch: GitHub reported |
What Problem This Solves
Fixes an issue where users running streamed agent tool calls could execute an unintended partial tool call when an assistant turn stopped at the output limit while producing a tool-call batch.
Closes #97091
Why This Change Was Made
The agent loop now treats only finalized
toolUseassistant turns as executable. Non-toolUsefinal assistant messages have tool-call blocks removed before they are committed to context or emitted asmessage_end, so truncatedlengthturns cannot dispatch side-effectful tools or leave dangling tool-call replay state.User Impact
Users get safer streamed tool-call behavior: a max-token-truncated assistant turn can preserve visible assistant text, but it will not launch accidental child sessions or poison the next model request with unmatched tool calls.
Evidence
Before evidence:
Focused regression proof after fix:
Patch hygiene:
Autoreview:
Real behavior proof
Behavior addressed: A max-token-truncated assistant turn with a partial
sessions_spawntool call no longer executes the tool call and no longer leaves a dangling tool call in the committed assistant message, emittedmessage_end, or next-turn replay context.Real environment tested: Local OpenClaw source worktree using the agent-core runtime seam and the real
createAssistantMessageEventStreamevent protocol.Exact steps or command run after this patch:
node scripts/run-vitest.mjs packages/agent-core/src/agent-loop.test.ts;git diff --check;.agents/skills/autoreview/scripts/autoreview --mode local.Evidence after fix:
Observed result after fix: The new regression verifies that a
stopReason: "length"assistant message containing asessions_spawntool-call block does not emittool_execution_start, does not call the tool implementation, removes the tool-call block from the returned assistant message, removes it from themessage_endevent, and removes it from the follow-up turn's replay context.What was not tested: I did not rerun the original live Telegram/GitHub Copilot parent session because it depends on private channel credentials and reproducing a provider max-token truncation at the same point in a live tool-call batch.