Skip to content

Comments

fix(agents): sanitize tool pairing after compaction and history truncation#4852

Open
lailoo wants to merge 1 commit intoopenclaw:mainfrom
lailoo:fix/compaction-tool-pairing
Open

fix(agents): sanitize tool pairing after compaction and history truncation#4852
lailoo wants to merge 1 commit intoopenclaw:mainfrom
lailoo:fix/compaction-tool-pairing

Conversation

@lailoo
Copy link
Contributor

@lailoo lailoo commented Jan 30, 2026

Summary

When compaction prunes older messages or limitHistoryTurns truncates history, tool_result entries can become orphaned if their matching tool_use was removed. This causes unexpected tool_use_id API errors.

Changes

compaction-safeguard.ts

  • Call sanitizeToolUseResultPairing() after pruneHistoryForContextShare() to clean up orphan tool_result entries

history.ts

  • Call sanitizeToolUseResultPairing() after truncating history in limitHistoryTurns()

Tests

  • Added 2 new tests for limitHistoryTurns tool pairing scenarios:
    • removes orphan toolResult after truncation
    • preserves valid tool pairs after truncation

Root Cause

Both compaction and history truncation can remove assistant messages containing tool_use blocks while leaving their corresponding tool_result entries in the retained portion. This creates orphaned tool_result entries that reference non-existent tool_use blocks.

Testing

  • All 17 tests in compaction-safeguard.test.ts pass
  • All 11 tests in pi-embedded-runner.limithistoryturns.test.ts pass

Fixes

Greptile Overview

Greptile Summary

This PR hardens transcript integrity by running sanitizeToolUseResultPairing() after two history-shaping operations: compaction pruning (pruneHistoryForContextShare) and DM history truncation (limitHistoryTurns). This prevents “orphan” toolResult entries whose matching toolCall/tool_use messages were removed, which can trigger strict-provider API errors like unexpected tool_use_id.

Changes are localized to the compaction safeguard extension and embedded runner history logic, plus two new unit tests covering tool pairing behavior during truncation.

Confidence Score: 4/5

  • This PR is safe to merge with low risk; it adds a focused repair step after truncation/pruning and includes targeted tests.
  • Core change is a small call-site addition to an existing transcript-repair utility in two places that can create orphan tool results. Behavior is consistent with existing repair semantics (dropping free-floating tool results). Only notable concern is that one new test doesn’t strongly assert correct pairing/order, so a narrow regression could slip through.
  • src/agents/pi-embedded-runner.limithistoryturns.test.ts

…ation

When compaction prunes older messages or limitHistoryTurns truncates history,
tool_result entries can become orphaned if their matching tool_use was removed.
This causes 'unexpected tool_use_id' API errors.

Changes:
- compaction-safeguard.ts: call sanitizeToolUseResultPairing() after pruning
- history.ts: call sanitizeToolUseResultPairing() after truncation
- Added 2 new tests for limitHistoryTurns tool pairing scenarios

Fixes openclaw#4739, openclaw#4728, openclaw#4723, openclaw#3462, openclaw#3455, openclaw#4261, openclaw#4367, openclaw#4323, openclaw#4650
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 3, 2026

Additional Comments (1)

src/agents/pi-embedded-runner.limithistoryturns.test.ts
[P1] Test doesn’t actually assert toolCall/toolResult pairing

This test only checks that a toolResult remains and that the returned array length is 3, but it doesn’t verify that the retained toolResult.toolCallId matches a retained assistant toolCall (or that the toolResult is immediately after the toolCall). A regression where an orphan toolResult survives (or pairing order is wrong) could still pass.

Consider asserting the exact sequence and IDs (e.g., limited[1] is the toolCall with id call_1 and limited[2] is the matching toolResult with toolCallId === 'call_1').

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-runner.limithistoryturns.test.ts
Line: 185:205

Comment:
[P1] Test doesn’t actually assert toolCall/toolResult pairing

This test only checks that a `toolResult` remains and that the returned array length is 3, but it doesn’t verify that the retained `toolResult.toolCallId` matches a retained assistant `toolCall` (or that the `toolResult` is immediately after the `toolCall`). A regression where an orphan `toolResult` survives (or pairing order is wrong) could still pass.

Consider asserting the exact sequence and IDs (e.g., `limited[1]` is the toolCall with id `call_1` and `limited[2]` is the matching toolResult with `toolCallId === 'call_1'`).

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant