fix(fallback): guard duplicate prompt injections

This commit is contained in:
YeonGyu-Kim
2026-05-14 01:03:19 +09:00
parent ea55c385bb
commit 5ffbe0e24e
7 changed files with 257 additions and 15 deletions
@@ -39,11 +39,14 @@ import { _resetForTesting as resetSessionState, updateSessionAgent } from "../..
import { runAggressiveTruncationStrategy } from "./aggressive-truncation-strategy"
type FakeClient = {
session: { promptAsync: (input: PromptAsyncCall) => Promise<unknown> }
session: {
promptAsync: (input: PromptAsyncCall) => Promise<unknown>
status?: () => Promise<unknown>
}
tui: { showToast: (input: unknown) => Promise<unknown> }
}
function createRecordingClient(): { client: FakeClient; calls: PromptAsyncCall[] } {
function createRecordingClient(status?: () => Promise<unknown>): { client: FakeClient; calls: PromptAsyncCall[] } {
const calls: PromptAsyncCall[] = []
const client: FakeClient = {
session: {
@@ -51,6 +54,7 @@ function createRecordingClient(): { client: FakeClient; calls: PromptAsyncCall[]
calls.push(input)
return undefined
},
...(status ? { status } : {}),
},
tui: {
showToast: async () => undefined,
@@ -173,4 +177,27 @@ describe("runAggressiveTruncationStrategy - pins agent/model/variant on recovere
expect(calls[0].body.variant).toBeUndefined()
expect(calls[0].body.auto).toBe(true)
})
test("does not send the delayed auto prompt when the session becomes active before recovery fires", async () => {
// given
const sessionID = "session-truncation-active"
const { client, calls } = createRecordingClient(async () => ({
[sessionID]: { type: "busy" },
}))
// when
await runAggressiveTruncationStrategy({
sessionID,
autoCompactState: createAutoCompactState(),
client: client as never,
directory: "/tmp/test-truncation",
truncateAttempt: 0,
currentTokens: 250_000,
maxTokens: 200_000,
})
await flushDeferredPrompt()
// then
expect(calls).toHaveLength(0)
})
})
@@ -17,6 +17,7 @@ import {
findNearestMessageWithFields,
findNearestMessageWithFieldsFromSDK,
} from "../../features/hook-message-injector"
import { isSessionActive } from "../shared/session-idle-settle"
export async function runAggressiveTruncationStrategy(params: {
sessionID: string
@@ -73,6 +74,13 @@ export async function runAggressiveTruncationStrategy(params: {
clearSessionState(params.autoCompactState, params.sessionID)
setTimeout(async () => {
try {
if (await isSessionActive(params.client, params.sessionID)) {
log("[auto-compact] skipped delayed auto prompt because session became active", {
sessionID: params.sessionID,
})
return
}
const sdkMessage = await findNearestMessageWithFieldsFromSDK(params.client, params.sessionID)
const previousMessage = sdkMessage ?? (() => {
const messageDir = getMessageDir(params.sessionID)
@@ -98,7 +106,12 @@ export async function runAggressiveTruncationStrategy(params: {
} as never,
query: { directory: params.directory },
})
} catch {}
} catch (error) {
log("[auto-compact] delayed auto prompt failed", {
sessionID: params.sessionID,
error: String(error),
})
}
}, 500)
return { handled: true, nextTruncateAttempt }