diff --git a/src/agents/pi-embedded-runner/compact.ts b/src/agents/pi-embedded-runner/compact.ts index 280e7d0aba..84a0c61661 100644 --- a/src/agents/pi-embedded-runner/compact.ts +++ b/src/agents/pi-embedded-runner/compact.ts @@ -44,6 +44,7 @@ import { createOpenClawCodingTools } from "../pi-tools.js"; import { resolveSandboxContext } from "../sandbox.js"; import { repairSessionFileIfNeeded } from "../session-file-repair.js"; import { guardSessionManager } from "../session-tool-result-guard-wrapper.js"; +import { sanitizeToolUseResultPairing } from "../session-transcript-repair.js"; import { acquireSessionWriteLock } from "../session-write-lock.js"; import { detectRuntimeShell } from "../shell-utils.js"; import { @@ -429,10 +430,16 @@ export async function compactEmbeddedPiSessionDirect( const validated = transcriptPolicy.validateAnthropicTurns ? validateAnthropicTurns(validatedGemini) : validatedGemini; - const limited = limitHistoryTurns( + const truncated = limitHistoryTurns( validated, getDmHistoryLimitFromSessionKey(params.sessionKey, params.config), ); + // Re-run tool_use/tool_result pairing repair after truncation, since + // limitHistoryTurns can orphan tool_result blocks by removing the + // assistant message that contained the matching tool_use. + const limited = transcriptPolicy.repairToolUseResultPairing + ? sanitizeToolUseResultPairing(truncated) + : truncated; if (limited.length > 0) { session.agent.replaceMessages(limited); } diff --git a/src/agents/pi-embedded-runner/run/attempt.ts b/src/agents/pi-embedded-runner/run/attempt.ts index 086b11fae1..893bcbc671 100644 --- a/src/agents/pi-embedded-runner/run/attempt.ts +++ b/src/agents/pi-embedded-runner/run/attempt.ts @@ -48,6 +48,7 @@ import { resolveSandboxContext } from "../../sandbox.js"; import { resolveSandboxRuntimeStatus } from "../../sandbox/runtime-status.js"; import { repairSessionFileIfNeeded } from "../../session-file-repair.js"; import { guardSessionManager } from "../../session-tool-result-guard-wrapper.js"; +import { sanitizeToolUseResultPairing } from "../../session-transcript-repair.js"; import { acquireSessionWriteLock } from "../../session-write-lock.js"; import { detectRuntimeShell } from "../../shell-utils.js"; import { @@ -556,10 +557,16 @@ export async function runEmbeddedAttempt( const validated = transcriptPolicy.validateAnthropicTurns ? validateAnthropicTurns(validatedGemini) : validatedGemini; - const limited = limitHistoryTurns( + const truncated = limitHistoryTurns( validated, getDmHistoryLimitFromSessionKey(params.sessionKey, params.config), ); + // Re-run tool_use/tool_result pairing repair after truncation, since + // limitHistoryTurns can orphan tool_result blocks by removing the + // assistant message that contained the matching tool_use. + const limited = transcriptPolicy.repairToolUseResultPairing + ? sanitizeToolUseResultPairing(truncated) + : truncated; cacheTrace?.recordStage("session:limited", { messages: limited }); if (limited.length > 0) { activeSession.agent.replaceMessages(limited);