From a3ff28b25022fda9ebf7a15c6177aaf8965be063 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sat, 20 Dec 2025 15:39:54 +0900 Subject: [PATCH] feat(preemptive-compaction): add onBeforeSummarize callback and context injection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added BeforeSummarizeCallback type to allow injecting context before session summarization - Added onBeforeSummarize option to PreemptiveCompactionOptions - Created compaction-context-injector module that injects summarization instructions with sections: - User Requests (As-Is) - Final Goal - Work Completed - Remaining Tasks - MUST NOT Do (Critical Constraints) - Wired up callback invocation in preemptive-compaction before calling summarize API - Exported new hook from src/hooks/index.ts 🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode) --- .../compaction-context-injector/index.ts | 53 +++++++++++++++++++ src/hooks/index.ts | 3 +- src/hooks/preemptive-compaction/index.ts | 22 ++++++++ src/index.ts | 7 ++- 4 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 src/hooks/compaction-context-injector/index.ts diff --git a/src/hooks/compaction-context-injector/index.ts b/src/hooks/compaction-context-injector/index.ts new file mode 100644 index 0000000..62e14f2 --- /dev/null +++ b/src/hooks/compaction-context-injector/index.ts @@ -0,0 +1,53 @@ +import type { SummarizeContext } from "../preemptive-compaction" +import { injectHookMessage } from "../../features/hook-message-injector" +import { log } from "../../shared/logger" + +const SUMMARIZE_CONTEXT_PROMPT = `[COMPACTION CONTEXT INJECTION] + +When summarizing this session, you MUST include the following sections in your summary: + +## 1. User Requests (As-Is) +- List all original user requests exactly as they were stated +- Preserve the user's exact wording and intent + +## 2. Final Goal +- What the user ultimately wanted to achieve +- The end result or deliverable expected + +## 3. Work Completed +- What has been done so far +- Files created/modified +- Features implemented +- Problems solved + +## 4. Remaining Tasks +- What still needs to be done +- Pending items from the original request +- Follow-up tasks identified during the work + +## 5. MUST NOT Do (Critical Constraints) +- Things that were explicitly forbidden +- Approaches that failed and should not be retried +- User's explicit restrictions or preferences +- Anti-patterns identified during the session + +This context is critical for maintaining continuity after compaction. +` + +export function createCompactionContextInjector() { + return async (ctx: SummarizeContext): Promise => { + log("[compaction-context-injector] injecting context", { sessionID: ctx.sessionID }) + + const success = injectHookMessage(ctx.sessionID, SUMMARIZE_CONTEXT_PROMPT, { + agent: "general", + model: { providerID: ctx.providerID, modelID: ctx.modelID }, + path: { cwd: ctx.directory }, + }) + + if (success) { + log("[compaction-context-injector] context injected", { sessionID: ctx.sessionID }) + } else { + log("[compaction-context-injector] injection failed", { sessionID: ctx.sessionID }) + } + } +} diff --git a/src/hooks/index.ts b/src/hooks/index.ts index b8a299a..9b7bf86 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -8,7 +8,8 @@ export { createDirectoryAgentsInjectorHook } from "./directory-agents-injector"; export { createDirectoryReadmeInjectorHook } from "./directory-readme-injector"; export { createEmptyTaskResponseDetectorHook } from "./empty-task-response-detector"; export { createAnthropicAutoCompactHook, type AnthropicAutoCompactOptions } from "./anthropic-auto-compact"; -export { createPreemptiveCompactionHook, type PreemptiveCompactionOptions } from "./preemptive-compaction"; +export { createPreemptiveCompactionHook, type PreemptiveCompactionOptions, type SummarizeContext, type BeforeSummarizeCallback } from "./preemptive-compaction"; +export { createCompactionContextInjector } from "./compaction-context-injector"; export { createThinkModeHook } from "./think-mode"; export { createClaudeCodeHooksHook } from "./claude-code-hooks"; export { createRulesInjectorHook } from "./rules-injector"; diff --git a/src/hooks/preemptive-compaction/index.ts b/src/hooks/preemptive-compaction/index.ts index bca3b45..d5a7560 100644 --- a/src/hooks/preemptive-compaction/index.ts +++ b/src/hooks/preemptive-compaction/index.ts @@ -8,8 +8,19 @@ import { } from "./constants" import { log } from "../../shared/logger" +export interface SummarizeContext { + sessionID: string + providerID: string + modelID: string + usageRatio: number + directory: string +} + +export type BeforeSummarizeCallback = (ctx: SummarizeContext) => Promise | void + export interface PreemptiveCompactionOptions { experimental?: ExperimentalConfig + onBeforeSummarize?: BeforeSummarizeCallback } interface MessageInfo { @@ -68,6 +79,7 @@ export function createPreemptiveCompactionHook( options?: PreemptiveCompactionOptions ) { const experimental = options?.experimental + const onBeforeSummarize = options?.onBeforeSummarize const enabled = experimental?.preemptive_compaction !== false const threshold = experimental?.preemptive_compaction_threshold ?? DEFAULT_THRESHOLD @@ -132,6 +144,16 @@ export function createPreemptiveCompactionHook( log("[preemptive-compaction] triggering compaction", { sessionID, usageRatio }) try { + if (onBeforeSummarize) { + await onBeforeSummarize({ + sessionID, + providerID, + modelID, + usageRatio, + directory: ctx.directory, + }) + } + await ctx.client.session.summarize({ path: { id: sessionID }, body: { providerID, modelID }, diff --git a/src/index.ts b/src/index.ts index d29738f..88306f0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,6 +14,7 @@ import { createClaudeCodeHooksHook, createAnthropicAutoCompactHook, createPreemptiveCompactionHook, + createCompactionContextInjector, createRulesInjectorHook, createBackgroundNotificationHook, createAutoUpdateCheckerHook, @@ -256,7 +257,11 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => { const anthropicAutoCompact = isHookEnabled("anthropic-auto-compact") ? createAnthropicAutoCompactHook(ctx, { experimental: pluginConfig.experimental }) : null; - const preemptiveCompaction = createPreemptiveCompactionHook(ctx, { experimental: pluginConfig.experimental }); + const compactionContextInjector = createCompactionContextInjector(); + const preemptiveCompaction = createPreemptiveCompactionHook(ctx, { + experimental: pluginConfig.experimental, + onBeforeSummarize: compactionContextInjector, + }); const rulesInjector = isHookEnabled("rules-injector") ? createRulesInjectorHook(ctx) : null;