diff --git a/src/agents/pi-embedded-runner/run/attempt.ts b/src/agents/pi-embedded-runner/run/attempt.ts index 893bcbc6717..c010d5811d0 100644 --- a/src/agents/pi-embedded-runner/run/attempt.ts +++ b/src/agents/pi-embedded-runner/run/attempt.ts @@ -810,17 +810,6 @@ export async function runEmbeddedAttempt( note: `images: prompt=${imageResult.images.length} history=${imageResult.historyImagesByIndex.size}`, }); - const shouldTrackCacheTtl = - params.config?.agents?.defaults?.contextPruning?.mode === "cache-ttl" && - isCacheTtlEligibleProvider(params.provider, params.modelId); - if (shouldTrackCacheTtl) { - appendCacheTtlTimestamp(sessionManager, { - timestamp: Date.now(), - provider: params.provider, - modelId: params.modelId, - }); - } - // Only pass images option if there are actually images to pass // This avoids potential issues with models that don't expect the images parameter if (imageResult.images.length > 0) { @@ -848,6 +837,22 @@ export async function runEmbeddedAttempt( } } + // Append cache-TTL timestamp AFTER prompt + compaction retry completes. + // Previously this was before the prompt, which caused a custom entry to be + // inserted between compaction and the next prompt — breaking the + // prepareCompaction() guard that checks the last entry type, leading to + // double-compaction. See: https://github.com/openclaw/openclaw/issues/9282 + const shouldTrackCacheTtl = + params.config?.agents?.defaults?.contextPruning?.mode === "cache-ttl" && + isCacheTtlEligibleProvider(params.provider, params.modelId); + if (shouldTrackCacheTtl) { + appendCacheTtlTimestamp(sessionManager, { + timestamp: Date.now(), + provider: params.provider, + modelId: params.modelId, + }); + } + messagesSnapshot = activeSession.messages.slice(); sessionIdUsed = activeSession.sessionId; cacheTrace?.recordStage("session:after", {