fix(plugin): dedupe synthetic idle events

This commit is contained in:
YeonGyu-Kim
2026-05-31 05:31:30 +09:00
parent 1432a1141c
commit 411895db4e
2 changed files with 4 additions and 12 deletions
+4 -6
View File
@@ -391,7 +391,7 @@ describe("createEventHandler - idle deduplication", () => {
expect(spawnTmuxPane).toHaveBeenCalledTimes(1) expect(spawnTmuxPane).toHaveBeenCalledTimes(1)
}) })
it("does NOT dedup real-idle-after-synthetic-idle within 500ms", async () => { it("#given session.status already emitted a synthetic idle #when real session.idle follows immediately #then hooks run once", async () => {
//#given //#given
const dispatchCalls: EventInput[] = [] const dispatchCalls: EventInput[] = []
const eventHandler = createIdleTrackingEventHandler(dispatchCalls) const eventHandler = createIdleTrackingEventHandler(dispatchCalls)
@@ -415,11 +415,9 @@ describe("createEventHandler - idle deduplication", () => {
})) }))
//#then //#then
expect(dispatchCalls).toHaveLength(2) expect(dispatchCalls).toHaveLength(1)
expect(dispatchCalls[0]?.event.type).toBe("session.idle") expect(dispatchCalls[0]?.event.type).toBe("session.idle")
expect((dispatchCalls[0]?.event.properties as { sessionID?: string } | undefined)?.sessionID).toBe(sessionId) expect((dispatchCalls[0]?.event.properties as { sessionID?: string } | undefined)?.sessionID).toBe(sessionId)
expect(dispatchCalls[1]?.event.type).toBe("session.idle")
expect((dispatchCalls[1]?.event.properties as { sessionID?: string } | undefined)?.sessionID).toBe(sessionId)
}) })
it("#given idle recovery handles an interrupted tool turn #when session.idle arrives #then later idle hooks are skipped for that event", async () => { it("#given idle recovery handles an interrupted tool turn #when session.idle arrives #then later idle hooks are skipped for that event", async () => {
@@ -619,7 +617,7 @@ describe("createEventHandler - idle deduplication", () => {
} }
}) })
it("keeps other session dedup state untouched when bypassing synthetic-idle for current session", async () => { it("keeps other session dedup state untouched when suppressing real-idle-after-synthetic-idle", async () => {
//#given //#given
const originalDateNow = Date.now const originalDateNow = Date.now
let currentNow = 30_000 let currentNow = 30_000
@@ -679,7 +677,7 @@ describe("createEventHandler - idle deduplication", () => {
})) }))
//#then //#then
expect(dispatchedSessionIds).toEqual(["ses_a", "ses_b", "ses_a"]) expect(dispatchedSessionIds).toEqual(["ses_a", "ses_b"])
} finally { } finally {
Date.now = originalDateNow Date.now = originalDateNow
} }
-6
View File
@@ -593,12 +593,6 @@ export function createEventHandler(args: {
const emittedAt = recentSyntheticIdles.get(sessionID); const emittedAt = recentSyntheticIdles.get(sessionID);
if (emittedAt !== undefined && now - emittedAt < DEDUP_WINDOW_MS) { if (emittedAt !== undefined && now - emittedAt < DEDUP_WINDOW_MS) {
recentSyntheticIdles.delete(sessionID); recentSyntheticIdles.delete(sessionID);
// Let real idle events through even when a synthetic idle fired moments earlier.
// OpenCode diagnostics expect a concrete session.idle event signal.
const lastAnyIdleAt = recentAnyIdles.get(sessionID);
if (lastAnyIdleAt === emittedAt) {
recentAnyIdles.delete(sessionID);
}
} }
} }
const recovered = await recoverInterruptedToolResultsOnIdleEvent(input); const recovered = await recoverInterruptedToolResultsOnIdleEvent(input);