diff --git a/src/agents/pi-embedded-runner/compact.ts b/src/agents/pi-embedded-runner/compact.ts index 2dc4c5325..b3e1afd27 100644 --- a/src/agents/pi-embedded-runner/compact.ts +++ b/src/agents/pi-embedded-runner/compact.ts @@ -58,6 +58,7 @@ import { sanitizeToolsForGoogle, } from "./google.js"; import { getDmHistoryLimitFromSessionKey, limitHistoryTurns } from "./history.js"; +import { sanitizeToolUseResultPairing } from "../session-transcript-repair.js"; import { resolveGlobalLane, resolveSessionLane } from "./lanes.js"; import { log } from "./logger.js"; import { buildModelAliasLines, resolveModel } from "./model.js"; @@ -417,10 +418,14 @@ export async function compactEmbeddedPiSessionDirect( const validated = transcriptPolicy.validateAnthropicTurns ? validateAnthropicTurns(validatedGemini) : validatedGemini; - const limited = limitHistoryTurns( + const truncated = limitHistoryTurns( validated, getDmHistoryLimitFromSessionKey(params.sessionKey, params.config), ); + // Re-repair tool_use/tool_result pairing after truncation, since + // limitHistoryTurns may slice in the middle of a paired exchange. + const limited = + truncated.length < validated.length ? 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 e83c3ae4a..2c2533438 100644 --- a/src/agents/pi-embedded-runner/run/attempt.ts +++ b/src/agents/pi-embedded-runner/run/attempt.ts @@ -62,6 +62,7 @@ import { sanitizeSessionHistory, sanitizeToolsForGoogle, } from "../google.js"; +import { sanitizeToolUseResultPairing } from "../../session-transcript-repair.js"; import { getDmHistoryLimitFromSessionKey, limitHistoryTurns } from "../history.js"; import { log } from "../logger.js"; import { buildModelAliasLines } from "../model.js"; @@ -531,10 +532,14 @@ export async function runEmbeddedAttempt( const validated = transcriptPolicy.validateAnthropicTurns ? validateAnthropicTurns(validatedGemini) : validatedGemini; - const limited = limitHistoryTurns( + const truncated = limitHistoryTurns( validated, getDmHistoryLimitFromSessionKey(params.sessionKey, params.config), ); + // Re-repair tool_use/tool_result pairing after truncation, since + // limitHistoryTurns may slice in the middle of a paired exchange. + const limited = + truncated.length < validated.length ? sanitizeToolUseResultPairing(truncated) : truncated; cacheTrace?.recordStage("session:limited", { messages: limited }); if (limited.length > 0) { activeSession.agent.replaceMessages(limited);