Merge pull request #3942 from code-yeongyu/fix/3819-compaction-agent-token-cache
fix(compaction): ignore compaction agent updates
This commit is contained in:
@@ -235,6 +235,45 @@ describe("context-window-monitor", () => {
|
||||
expect(output.output).toContain("context remaining")
|
||||
})
|
||||
|
||||
// #given only a compaction agent summary message update is seen
|
||||
// #when tool.execute.after checks context usage
|
||||
// #then stale pre-compaction tokens should not create a context reminder
|
||||
it("should ignore compaction-agent message updates when caching context usage", async () => {
|
||||
const hook = createContextWindowMonitorHook(ctx as never)
|
||||
const sessionID = "ses_compaction_agent_context"
|
||||
|
||||
await hook.event({
|
||||
event: {
|
||||
type: "message.updated",
|
||||
properties: {
|
||||
info: {
|
||||
agent: "compaction",
|
||||
role: "assistant",
|
||||
sessionID,
|
||||
providerID: "anthropic",
|
||||
modelID: "claude-sonnet-4-6",
|
||||
finish: true,
|
||||
tokens: {
|
||||
input: 150000,
|
||||
output: 1000,
|
||||
reasoning: 0,
|
||||
cache: { read: 10000, write: 0 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const output = { title: "", output: "original", metadata: null }
|
||||
await hook["tool.execute.after"](
|
||||
{ tool: "bash", sessionID, callID: "call_1" },
|
||||
output
|
||||
)
|
||||
|
||||
expect(output.output).toBe("original")
|
||||
expect(ctx.client.session.messages).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
// #given session is deleted
|
||||
// #when session.deleted event fires
|
||||
// #then cached data should be cleaned up
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
resolveActualContextLimit,
|
||||
type ContextLimitModelCacheState,
|
||||
} from "../shared/context-limit-resolver"
|
||||
import { isCompactionAgent } from "../shared/compaction-marker"
|
||||
import { createSystemDirective, SystemDirectiveTypes } from "../shared/system-directive"
|
||||
|
||||
const CONTEXT_WARNING_THRESHOLD = 0.70
|
||||
@@ -94,6 +95,7 @@ export function createContextWindowMonitorHook(
|
||||
|
||||
if (event.type === "message.updated") {
|
||||
const info = props?.info as {
|
||||
agent?: unknown
|
||||
role?: string
|
||||
sessionID?: string
|
||||
providerID?: string
|
||||
@@ -103,6 +105,7 @@ export function createContextWindowMonitorHook(
|
||||
} | undefined
|
||||
|
||||
if (!info || info.role !== "assistant" || !info.finish) return
|
||||
if (isCompactionAgent(info.agent)) return
|
||||
if (!info.sessionID || !info.providerID || !info.tokens) return
|
||||
|
||||
tokenCache.set(info.sessionID, {
|
||||
|
||||
@@ -55,7 +55,9 @@ function setupImmediateTimeouts(): () => void {
|
||||
|
||||
globalThis.setTimeout = ((callback: (...args: unknown[]) => void, _delay?: number, ...args: unknown[]) => {
|
||||
callback(...args)
|
||||
return 1 as unknown as ReturnType<typeof setTimeout>
|
||||
const timeoutID = originalSetTimeout(() => undefined, 0)
|
||||
originalClearTimeout(timeoutID)
|
||||
return timeoutID
|
||||
}) as typeof setTimeout
|
||||
|
||||
globalThis.clearTimeout = (() => {}) as typeof clearTimeout
|
||||
@@ -637,6 +639,78 @@ describe("preemptive-compaction", () => {
|
||||
Date.now = originalNow
|
||||
})
|
||||
|
||||
// #given compaction already succeeded for a session
|
||||
// #when the compaction agent emits its summary message update
|
||||
// #then it should not clear the compaction guard or trigger a duplicate summary
|
||||
it("should ignore compaction-agent message updates after successful compaction", async () => {
|
||||
const hook = createPreemptiveCompactionHook(ctx as never, {} as never)
|
||||
const sessionID = "ses_compaction_agent_update"
|
||||
|
||||
await hook.event({
|
||||
event: {
|
||||
type: "message.updated",
|
||||
properties: {
|
||||
info: {
|
||||
role: "assistant",
|
||||
sessionID,
|
||||
providerID: "anthropic",
|
||||
modelID: "claude-sonnet-4-6",
|
||||
finish: true,
|
||||
tokens: {
|
||||
input: 170000,
|
||||
output: 0,
|
||||
reasoning: 0,
|
||||
cache: { read: 10000, write: 0 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await hook["tool.execute.after"](
|
||||
{ tool: "bash", sessionID, callID: "call_1" },
|
||||
{ title: "", output: "test", metadata: null }
|
||||
)
|
||||
|
||||
expect(ctx.client.session.summarize).toHaveBeenCalledTimes(1)
|
||||
|
||||
const originalNow = Date.now
|
||||
try {
|
||||
Date.now = () => originalNow() + 61_000
|
||||
|
||||
await hook.event({
|
||||
event: {
|
||||
type: "message.updated",
|
||||
properties: {
|
||||
info: {
|
||||
agent: "compaction",
|
||||
role: "assistant",
|
||||
sessionID,
|
||||
providerID: "anthropic",
|
||||
modelID: "claude-sonnet-4-6",
|
||||
finish: true,
|
||||
tokens: {
|
||||
input: 170000,
|
||||
output: 0,
|
||||
reasoning: 0,
|
||||
cache: { read: 10000, write: 0 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await hook["tool.execute.after"](
|
||||
{ tool: "bash", sessionID, callID: "call_2" },
|
||||
{ title: "", output: "test", metadata: null }
|
||||
)
|
||||
|
||||
expect(ctx.client.session.summarize).toHaveBeenCalledTimes(1)
|
||||
} finally {
|
||||
Date.now = originalNow
|
||||
}
|
||||
})
|
||||
|
||||
// #given modelContextLimitsCache has model-specific limit (256k)
|
||||
// #when tokens are above default 78% of 200k but below 78% of 256k
|
||||
// #then should NOT trigger compaction
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { OhMyOpenCodeConfig } from "../config"
|
||||
import { isCompactionAgent } from "../shared/compaction-marker"
|
||||
import type { ContextLimitModelCacheState } from "../shared/context-limit-resolver"
|
||||
|
||||
import { createPostCompactionDegradationMonitor } from "./preemptive-compaction-degradation-monitor"
|
||||
@@ -70,6 +71,7 @@ export function createPreemptiveCompactionHook(
|
||||
if (event.type === "message.updated") {
|
||||
const info = props?.info as {
|
||||
id?: string
|
||||
agent?: unknown
|
||||
role?: string
|
||||
sessionID?: string
|
||||
providerID?: string
|
||||
@@ -80,6 +82,7 @@ export function createPreemptiveCompactionHook(
|
||||
} | undefined
|
||||
|
||||
if (!info || info.role !== "assistant" || !info.finish || !info.sessionID) return
|
||||
if (isCompactionAgent(info.agent)) return
|
||||
|
||||
if (info.providerID && info.tokens) {
|
||||
tokenCache.set(info.sessionID, {
|
||||
|
||||
Reference in New Issue
Block a user