fix(background-agent): stabilize parent wakes

This commit is contained in:
YeonGyu-Kim
2026-05-15 17:54:32 +09:00
parent c0544a703a
commit 7a94cc72be
3 changed files with 225 additions and 8 deletions
@@ -293,10 +293,28 @@ async function flushBackgroundNotifications(): Promise<void> {
}
}
async function waitUntil(predicate: () => boolean, timeoutMs: number): Promise<void> {
const startedAt = Date.now()
while (!predicate()) {
if (Date.now() - startedAt >= timeoutMs) {
return
}
await new Promise((resolve) => setTimeout(resolve, 10))
}
}
function waitForCoalescedFlush(): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, 400))
}
function waitForParentWakeRequeue(manager: BackgroundManager, sessionID: string): Promise<void> {
return waitUntil(() => getPendingParentWakes(manager).has(sessionID), 600)
}
function waitForParentWakeErrorSettle(): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, 260))
}
function createToastRemoveTaskTracker(): { removeTaskCalls: string[]; resetToastManager: () => void } {
_resetTaskToastManagerForTesting()
const toastManager = initTaskToastManager(cast<PluginInput["client"]>({
@@ -5184,6 +5202,7 @@ describe("BackgroundManager.handleEvent - session.error", () => {
},
})
await flushBackgroundNotifications()
await waitForParentWakeRequeue(manager, "parent-session-wake")
//#then
expect(promptCalls).toHaveLength(1)
@@ -5195,6 +5214,74 @@ describe("BackgroundManager.handleEvent - session.error", () => {
manager.shutdown()
})
test("does not requeue dispatched parent wake when session.error arrives before accepted history is visible", async () => {
//#given
const promptCalls: Array<{ path: { id: string }; body: Record<string, unknown> }> = []
const notification = "<system-reminder>done</system-reminder>"
let historyAccepted = false
const client = {
session: {
status: async () => ({ data: { "parent-session-wake": { type: "idle" } } }),
messages: async () =>
historyAccepted
? [
{
info: {
role: "user",
time: { created: Date.now() },
},
parts: [{ type: "text", text: notification }],
},
]
: [],
promptAsync: async (args: { path: { id: string }; body: Record<string, unknown> }) => {
promptCalls.push(args)
return {}
},
abort: async () => ({}),
},
}
const manager = new BackgroundManager({ pluginContext: createPluginInput(client) })
const managerInternals = cast<{
queuePendingParentWake: (
sessionID: string,
notification: string,
promptContext: Record<string, unknown>,
shouldReply: boolean,
delayMs?: number,
) => void
flushPendingParentWake: (sessionID: string) => Promise<void>
}>(manager)
managerInternals.queuePendingParentWake(
"parent-session-wake",
notification,
{ agent: "sisyphus" },
true,
0,
)
await managerInternals.flushPendingParentWake("parent-session-wake")
//#when
setTimeout(() => {
historyAccepted = true
}, 20)
manager.handleEvent({
type: "session.error",
properties: {
sessionID: "parent-session-wake",
error: { name: "UnknownError", message: "late provider failure" },
},
})
await waitForParentWakeErrorSettle()
//#then
expect(promptCalls).toHaveLength(1)
expect(getDispatchedParentWakes(manager).has("parent-session-wake")).toBe(false)
expect(getPendingParentWakes(manager).has("parent-session-wake")).toBe(false)
manager.shutdown()
})
test("does not requeue dispatched parent wake when session history already contains assistant output after the wake", async () => {
//#given
const promptCalls: Array<{ path: { id: string }; body: Record<string, unknown> }> = []
@@ -5251,6 +5338,7 @@ describe("BackgroundManager.handleEvent - session.error", () => {
},
})
await flushBackgroundNotifications()
await waitForParentWakeErrorSettle()
//#then
expect(promptCalls).toHaveLength(1)