fix(compaction): remove hardcoded Claude model from compaction hooks

This commit is contained in:
YeonGyu-Kim
2026-02-06 17:49:26 +09:00
parent f1b2f6f3f7
commit 60bbeb7304
8 changed files with 119 additions and 114 deletions
@@ -1,14 +1,4 @@
import { describe, expect, it, mock, beforeEach } from "bun:test"
// Mock dependencies before importing
const mockInjectHookMessage = mock(() => true)
mock.module("../../features/hook-message-injector", () => ({
injectHookMessage: mockInjectHookMessage,
}))
mock.module("../../shared/logger", () => ({
log: () => {},
}))
import { describe, expect, it, mock } from "bun:test"
mock.module("../../shared/system-directive", () => ({
createSystemDirective: (type: string) => `[DIRECTIVE:${type}]`,
@@ -25,78 +15,45 @@ mock.module("../../shared/system-directive", () => ({
}))
import { createCompactionContextInjector } from "./index"
import type { SummarizeContext } from "./index"
describe("createCompactionContextInjector", () => {
beforeEach(() => {
mockInjectHookMessage.mockClear()
})
describe("Agent Verification State preservation", () => {
it("includes Agent Verification State section in compaction prompt", async () => {
// given
//#given
const injector = createCompactionContextInjector()
const context: SummarizeContext = {
sessionID: "test-session",
providerID: "anthropic",
modelID: "claude-sonnet-4-5",
usageRatio: 0.85,
directory: "/test/dir",
}
// when
await injector(context)
//#when
const prompt = injector()
// then
expect(mockInjectHookMessage).toHaveBeenCalledTimes(1)
const calls = mockInjectHookMessage.mock.calls as unknown as [string, string, unknown][]
const injectedPrompt = calls[0]?.[1] ?? ""
expect(injectedPrompt).toContain("Agent Verification State")
expect(injectedPrompt).toContain("Current Agent")
expect(injectedPrompt).toContain("Verification Progress")
//#then
expect(prompt).toContain("Agent Verification State")
expect(prompt).toContain("Current Agent")
expect(prompt).toContain("Verification Progress")
})
it("includes Momus-specific context for reviewer agents", async () => {
// given
it("includes reviewer-agent continuity fields", async () => {
//#given
const injector = createCompactionContextInjector()
const context: SummarizeContext = {
sessionID: "test-session",
providerID: "anthropic",
modelID: "claude-sonnet-4-5",
usageRatio: 0.9,
directory: "/test/dir",
}
// when
await injector(context)
//#when
const prompt = injector()
// then
const calls = mockInjectHookMessage.mock.calls as unknown as [string, string, unknown][]
const injectedPrompt = calls[0]?.[1] ?? ""
expect(injectedPrompt).toContain("Previous Rejections")
expect(injectedPrompt).toContain("Acceptance Status")
expect(injectedPrompt).toContain("reviewer agents")
//#then
expect(prompt).toContain("Previous Rejections")
expect(prompt).toContain("Acceptance Status")
expect(prompt).toContain("reviewer agents")
})
it("preserves file verification progress in compaction prompt", async () => {
// given
it("preserves file verification progress fields", async () => {
//#given
const injector = createCompactionContextInjector()
const context: SummarizeContext = {
sessionID: "test-session",
providerID: "anthropic",
modelID: "claude-sonnet-4-5",
usageRatio: 0.95,
directory: "/test/dir",
}
// when
await injector(context)
//#when
const prompt = injector()
// then
const calls = mockInjectHookMessage.mock.calls as unknown as [string, string, unknown][]
const injectedPrompt = calls[0]?.[1] ?? ""
expect(injectedPrompt).toContain("Pending Verifications")
expect(injectedPrompt).toContain("Files already verified")
//#then
expect(prompt).toContain("Pending Verifications")
expect(prompt).toContain("Files already verified")
})
})
})
+2 -26
View File
@@ -1,16 +1,6 @@
import { injectHookMessage } from "../../features/hook-message-injector"
import { log } from "../../shared/logger"
import { createSystemDirective, SystemDirectiveTypes } from "../../shared/system-directive"
export interface SummarizeContext {
sessionID: string
providerID: string
modelID: string
usageRatio: number
directory: string
}
const SUMMARIZE_CONTEXT_PROMPT = `${createSystemDirective(SystemDirectiveTypes.COMPACTION_CONTEXT)}
const COMPACTION_CONTEXT_PROMPT = `${createSystemDirective(SystemDirectiveTypes.COMPACTION_CONTEXT)}
When summarizing this session, you MUST include the following sections in your summary:
@@ -58,19 +48,5 @@ 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 })
}
}
return (): string => COMPACTION_CONTEXT_PROMPT
}
+1 -1
View File
@@ -34,7 +34,7 @@ export { createDelegateTaskRetryHook } from "./delegate-task-retry";
export { createQuestionLabelTruncatorHook } from "./question-label-truncator";
export { createSubagentQuestionBlockerHook } from "./subagent-question-blocker";
export { createStopContinuationGuardHook, type StopContinuationGuard } from "./stop-continuation-guard";
export { createCompactionContextInjector, type SummarizeContext } from "./compaction-context-injector";
export { createCompactionContextInjector } from "./compaction-context-injector";
export { createUnstableAgentBabysitterHook } from "./unstable-agent-babysitter";
export { createPreemptiveCompactionHook } from "./preemptive-compaction";
export { createTasksTodowriteDisablerHook } from "./tasks-todowrite-disabler";
+35
View File
@@ -60,6 +60,41 @@ describe("preemptive-compaction", () => {
expect(summarize).toHaveBeenCalled()
})
test("triggers summarize for non-anthropic providers when usage exceeds threshold", async () => {
//#given
const messages = mock(() =>
Promise.resolve({
data: [
{
info: {
role: "assistant",
providerID: "openai",
modelID: "gpt-5.2",
tokens: {
input: 180000,
output: 0,
reasoning: 0,
cache: { read: 0, write: 0 },
},
},
},
],
})
)
const summarize = mock(() => Promise.resolve())
const hook = createPreemptiveCompactionHook(createMockCtx({ messages, summarize }))
const output = { title: "", output: "", metadata: {} }
//#when
await hook["tool.execute.after"](
{ tool: "Read", sessionID, callID: "call-3" },
output
)
//#then
expect(summarize).toHaveBeenCalled()
})
test("does not summarize when usage is below threshold", async () => {
// #given
const messages = mock(() =>
+8 -3
View File
@@ -1,8 +1,10 @@
const DEFAULT_ACTUAL_LIMIT = 200_000
const ANTHROPIC_ACTUAL_LIMIT =
process.env.ANTHROPIC_1M_CONTEXT === "true" ||
process.env.VERTEX_ANTHROPIC_1M_CONTEXT === "true"
? 1_000_000
: 200_000
: DEFAULT_ACTUAL_LIMIT
const PREEMPTIVE_COMPACTION_THRESHOLD = 0.78
@@ -59,11 +61,14 @@ export function createPreemptiveCompactionHook(ctx: PluginInput) {
if (assistantMessages.length === 0) return
const lastAssistant = assistantMessages[assistantMessages.length - 1]
if (lastAssistant.providerID !== "anthropic") return
const actualLimit =
lastAssistant.providerID === "anthropic"
? ANTHROPIC_ACTUAL_LIMIT
: DEFAULT_ACTUAL_LIMIT
const lastTokens = lastAssistant.tokens
const totalInputTokens = (lastTokens?.input ?? 0) + (lastTokens?.cache?.read ?? 0)
const usageRatio = totalInputTokens / ANTHROPIC_ACTUAL_LIMIT
const usageRatio = totalInputTokens / actualLimit
if (usageRatio < PREEMPTIVE_COMPACTION_THRESHOLD) return