feat(preemptive-compaction): add onBeforeSummarize callback and context injection

- 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)
This commit is contained in:
YeonGyu-Kim
2025-12-20 15:39:54 +09:00
parent 8406f3d6d7
commit a3ff28b250
4 changed files with 83 additions and 2 deletions

View File

@@ -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<void> => {
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 })
}
}
}

View File

@@ -8,7 +8,8 @@ export { createDirectoryAgentsInjectorHook } from "./directory-agents-injector";
export { createDirectoryReadmeInjectorHook } from "./directory-readme-injector"; export { createDirectoryReadmeInjectorHook } from "./directory-readme-injector";
export { createEmptyTaskResponseDetectorHook } from "./empty-task-response-detector"; export { createEmptyTaskResponseDetectorHook } from "./empty-task-response-detector";
export { createAnthropicAutoCompactHook, type AnthropicAutoCompactOptions } from "./anthropic-auto-compact"; 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 { createThinkModeHook } from "./think-mode";
export { createClaudeCodeHooksHook } from "./claude-code-hooks"; export { createClaudeCodeHooksHook } from "./claude-code-hooks";
export { createRulesInjectorHook } from "./rules-injector"; export { createRulesInjectorHook } from "./rules-injector";

View File

@@ -8,8 +8,19 @@ import {
} from "./constants" } from "./constants"
import { log } from "../../shared/logger" 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> | void
export interface PreemptiveCompactionOptions { export interface PreemptiveCompactionOptions {
experimental?: ExperimentalConfig experimental?: ExperimentalConfig
onBeforeSummarize?: BeforeSummarizeCallback
} }
interface MessageInfo { interface MessageInfo {
@@ -68,6 +79,7 @@ export function createPreemptiveCompactionHook(
options?: PreemptiveCompactionOptions options?: PreemptiveCompactionOptions
) { ) {
const experimental = options?.experimental const experimental = options?.experimental
const onBeforeSummarize = options?.onBeforeSummarize
const enabled = experimental?.preemptive_compaction !== false const enabled = experimental?.preemptive_compaction !== false
const threshold = experimental?.preemptive_compaction_threshold ?? DEFAULT_THRESHOLD const threshold = experimental?.preemptive_compaction_threshold ?? DEFAULT_THRESHOLD
@@ -132,6 +144,16 @@ export function createPreemptiveCompactionHook(
log("[preemptive-compaction] triggering compaction", { sessionID, usageRatio }) log("[preemptive-compaction] triggering compaction", { sessionID, usageRatio })
try { try {
if (onBeforeSummarize) {
await onBeforeSummarize({
sessionID,
providerID,
modelID,
usageRatio,
directory: ctx.directory,
})
}
await ctx.client.session.summarize({ await ctx.client.session.summarize({
path: { id: sessionID }, path: { id: sessionID },
body: { providerID, modelID }, body: { providerID, modelID },

View File

@@ -14,6 +14,7 @@ import {
createClaudeCodeHooksHook, createClaudeCodeHooksHook,
createAnthropicAutoCompactHook, createAnthropicAutoCompactHook,
createPreemptiveCompactionHook, createPreemptiveCompactionHook,
createCompactionContextInjector,
createRulesInjectorHook, createRulesInjectorHook,
createBackgroundNotificationHook, createBackgroundNotificationHook,
createAutoUpdateCheckerHook, createAutoUpdateCheckerHook,
@@ -256,7 +257,11 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
const anthropicAutoCompact = isHookEnabled("anthropic-auto-compact") const anthropicAutoCompact = isHookEnabled("anthropic-auto-compact")
? createAnthropicAutoCompactHook(ctx, { experimental: pluginConfig.experimental }) ? createAnthropicAutoCompactHook(ctx, { experimental: pluginConfig.experimental })
: null; : 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") const rulesInjector = isHookEnabled("rules-injector")
? createRulesInjectorHook(ctx) ? createRulesInjectorHook(ctx)
: null; : null;