From bd100bdde6d4cf304167aad5e3be0420a6da9e45 Mon Sep 17 00:00:00 2001 From: Taron Sung Date: Thu, 29 Jan 2026 13:00:23 +0900 Subject: [PATCH] fix(plugins): invoke before_compaction and after_compaction hooks during compaction Fixes #3728 The before_compaction and after_compaction plugin hooks were defined in plugins/hooks.ts but never invoked. This commit adds the missing calls in the compaction pipeline (compact.ts) so plugins can: - Flush external context stores before compaction - Inject recovery instructions after compaction - Coordinate with external memory systems AI-assisted: Yes (Claude) Testing: Lightly tested - linter passes, existing tests pass --- src/agents/pi-embedded-runner/compact.ts | 57 ++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/agents/pi-embedded-runner/compact.ts b/src/agents/pi-embedded-runner/compact.ts index dc68561c2..cad53b7e7 100644 --- a/src/agents/pi-embedded-runner/compact.ts +++ b/src/agents/pi-embedded-runner/compact.ts @@ -69,6 +69,7 @@ import type { EmbeddedPiCompactResult } from "./types.js"; import { formatUserTime, resolveUserTimeFormat, resolveUserTimezone } from "../date-time.js"; import { describeUnknownError, mapThinkingLevel, resolveExecToolDefaults } from "./utils.js"; import { buildTtsSystemPromptHint } from "../../tts/tts.js"; +import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js"; export type CompactEmbeddedPiSessionParams = { sessionId: string; @@ -424,7 +425,63 @@ export async function compactEmbeddedPiSessionDirect( if (limited.length > 0) { session.agent.replaceMessages(limited); } + + // Run before_compaction plugin hooks + const hookRunner = getGlobalHookRunner(); + const messageCountBefore = session.messages.length; + let tokenCountBefore: number | undefined; + try { + tokenCountBefore = 0; + for (const message of session.messages) { + tokenCountBefore += estimateTokens(message); + } + } catch { + tokenCountBefore = undefined; + } + if (hookRunner?.hasHooks("before_compaction")) { + try { + await hookRunner.runBeforeCompaction( + { + messageCount: messageCountBefore, + tokenCount: tokenCountBefore, + }, + { + agentId: sessionAgentId, + sessionKey: params.sessionKey, + workspaceDir: effectiveWorkspace, + messageProvider: runtimeChannel ?? undefined, + }, + ); + } catch (hookErr) { + log.warn(`before_compaction hook failed: ${String(hookErr)}`); + } + } + const result = await session.compact(params.customInstructions); + + // Run after_compaction plugin hooks + const messageCountAfter = session.messages.length; + const compactedCount = messageCountBefore - messageCountAfter; + if (hookRunner?.hasHooks("after_compaction")) { + try { + await hookRunner.runAfterCompaction( + { + messageCount: messageCountAfter, + tokenCount: result.tokensBefore, // Tokens before this specific compaction operation + compactedCount, + }, + { + agentId: sessionAgentId, + sessionKey: params.sessionKey, + workspaceDir: effectiveWorkspace, + messageProvider: runtimeChannel ?? undefined, + }, + ); + } catch (hookErr) { + log.warn(`after_compaction hook failed: ${String(hookErr)}`); + } + } + // Estimate tokens after compaction by summing token estimates for remaining messages let tokensAfter: number | undefined; try {