diff --git a/src/plugin/event.ts b/src/plugin/event.ts index 3755a88c..fb88bcd6 100644 --- a/src/plugin/event.ts +++ b/src/plugin/event.ts @@ -13,6 +13,7 @@ import { lspManager } from "../tools" import type { CreatedHooks } from "../create-hooks" import type { Managers } from "../create-managers" import { normalizeSessionStatusToIdle } from "./session-status-normalizer" +import { pruneRecentSyntheticIdles } from "./recent-synthetic-idles" type FirstMessageVariantGate = { markSessionCreated: (sessionInfo: { id?: string; title?: string; parentID?: string } | undefined) => void @@ -54,6 +55,12 @@ export function createEventHandler(args: { const DEDUP_WINDOW_MS = 500 return async (input): Promise => { + pruneRecentSyntheticIdles({ + recentSyntheticIdles, + now: Date.now(), + dedupWindowMs: DEDUP_WINDOW_MS, + }) + if (input.event.type === "session.idle") { const sessionID = (input.event.properties as Record | undefined)?.sessionID as string | undefined if (sessionID) { diff --git a/src/plugin/recent-synthetic-idles.test.ts b/src/plugin/recent-synthetic-idles.test.ts new file mode 100644 index 00000000..468e6e30 --- /dev/null +++ b/src/plugin/recent-synthetic-idles.test.ts @@ -0,0 +1,24 @@ +import { describe, it, expect } from "bun:test" + +import { pruneRecentSyntheticIdles } from "./recent-synthetic-idles" + +describe("pruneRecentSyntheticIdles", () => { + it("removes entries older than dedup window", () => { + // given + const recentSyntheticIdles = new Map([ + ["ses_old", 1000], + ["ses_new", 1600], + ]) + + // when + pruneRecentSyntheticIdles({ + recentSyntheticIdles, + now: 2000, + dedupWindowMs: 500, + }) + + // then + expect(recentSyntheticIdles.has("ses_old")).toBe(false) + expect(recentSyntheticIdles.has("ses_new")).toBe(true) + }) +}) diff --git a/src/plugin/recent-synthetic-idles.ts b/src/plugin/recent-synthetic-idles.ts new file mode 100644 index 00000000..971ff5e0 --- /dev/null +++ b/src/plugin/recent-synthetic-idles.ts @@ -0,0 +1,13 @@ +export function pruneRecentSyntheticIdles(args: { + recentSyntheticIdles: Map + now: number + dedupWindowMs: number +}): void { + const { recentSyntheticIdles, now, dedupWindowMs } = args + + for (const [sessionID, emittedAt] of recentSyntheticIdles) { + if (now - emittedAt >= dedupWindowMs) { + recentSyntheticIdles.delete(sessionID) + } + } +}