From 5729479e9cd8cec705fad49f1952ff5da9b44e97 Mon Sep 17 00:00:00 2001 From: luvs01 Date: Thu, 13 Aug 2026 09:37:47 +0900 Subject: [PATCH] fix(google-antigravity): persist replay expiry --- src/adapters/google-antigravity-replay.ts | 10 +++++++++- tests/google-antigravity-replay.test.ts | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/adapters/google-antigravity-replay.ts b/src/adapters/google-antigravity-replay.ts index f7ef904f44..d3ffa480c8 100644 --- a/src/adapters/google-antigravity-replay.ts +++ b/src/adapters/google-antigravity-replay.ts @@ -522,7 +522,15 @@ function refreshReplaySessionCandidate(key: string, entry: ReplayEntry): void { } function deleteExpiredReplaySessions(now: number): void { - for (const [key, entry] of replayCache) if (entry.expiresAtMs <= now) deleteReplaySession(key); + let deleted = false; + for (const [key, entry] of replayCache) { + if (entry.expiresAtMs > now) continue; + deleteReplaySession(key); + deleted = true; + } + // Expiry is a durable mutation too: rewrite the snapshot so opaque thought + // signatures do not remain at rest after their in-memory TTL has elapsed. + if (deleted) markReplayDirty(); } /** diff --git a/tests/google-antigravity-replay.test.ts b/tests/google-antigravity-replay.test.ts index 8c8b2d1f99..1b77f422bc 100644 --- a/tests/google-antigravity-replay.test.ts +++ b/tests/google-antigravity-replay.test.ts @@ -604,6 +604,20 @@ describe("durable antigravity replay snapshot", () => { await flushAntigravityReplay(); }); + test("sweeping expired sessions removes them from the durable snapshot", async () => { + const now = Date.now(); + observeAntigravityReplay(MODEL, SESSION, [fcPart("get_x", { a: 1 }, SIG)]); + await flushAntigravityReplay(); + expect(readFileSync(snapshotPath(), "utf8")).toContain(SIG); + + expect(sweepExpiredAntigravityReplay(now + 60 * 60 * 1000 + 1)).toBe(1); + await flushAntigravityReplay(); + + const raw = JSON.parse(readFileSync(snapshotPath(), "utf8")) as { sessions?: unknown[] }; + expect(raw.sessions).toHaveLength(0); + expect(readFileSync(snapshotPath(), "utf8")).not.toContain(SIG); + }); + test("snapshot cap keeps the most recently active sessions", async () => { // A (two calls, ~899 serialized bytes) fits under the cap; A+B (~1,347) // does not, so the cap must pick exactly one session by activity.