fix(fallback): guard duplicate prompt injections
This commit is contained in:
+29
-2
@@ -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)
|
||||
})
|
||||
})
|
||||
|
||||
+14
-1
@@ -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 }
|
||||
|
||||
Reference in New Issue
Block a user