diff --git a/src/gateway/session-archive-cleanup.test.ts b/src/gateway/session-archive-cleanup.test.ts index 86c51156d76..1b4f6282732 100644 --- a/src/gateway/session-archive-cleanup.test.ts +++ b/src/gateway/session-archive-cleanup.test.ts @@ -122,6 +122,20 @@ describe("sweepSessionArchiveFiles", () => { ]); }); + it("trims legacy numeric .bak files from rotateSessionFile", async () => { + // rotateSessionFile() creates backups as sessions.json.bak.${Date.now()}. + const baseTs = Date.now(); + for (let i = 0; i < 5; i++) { + fs.writeFileSync(path.join(sessionsDir, `sessions.json.bak.${baseTs + i * 1000}`), ""); + } + + const result = await sweepSessionArchiveFiles({ stateDir: tempDir }); + + const remaining = fs.readdirSync(sessionsDir).filter((f) => f.startsWith("sessions.json.bak.")); + expect(remaining).toHaveLength(3); + expect(result.removed).toBe(2); + }); + it("returns zeros when no agent session directories exist", async () => { mocks.resolveAgentSessionDirs.mockResolvedValue([]); const result = await sweepSessionArchiveFiles({ stateDir: tempDir }); diff --git a/src/gateway/session-archive-cleanup.ts b/src/gateway/session-archive-cleanup.ts index f5ad95005eb..e575199a1d8 100644 --- a/src/gateway/session-archive-cleanup.ts +++ b/src/gateway/session-archive-cleanup.ts @@ -70,10 +70,13 @@ export async function sweepSessionArchiveFiles(params: { // name. This mirrors the rotation logic in rotateSessionFile(). const bakByBase = new Map(); for (const entry of entries) { - // Use parseSessionArchiveTimestamp to validate the file is a genuine - // .bak.* archive — plain indexOf(".bak.") could false-match session - // IDs that contain ".bak." (e.g. "foo.bak.bar.jsonl.deleted."). - if (parseSessionArchiveTimestamp(entry, "bak") == null) { + // Match genuine .bak.* archives: either ISO-timestamped (from archive + // operations) or numeric (from rotateSessionFile's Date.now() suffix). + // Plain indexOf(".bak.") would false-match session IDs that contain + // ".bak." as a substring (e.g. "foo.bak.bar.jsonl.deleted."). + const isIsoBak = parseSessionArchiveTimestamp(entry, "bak") != null; + const isNumericBak = /\.bak\.\d+$/.test(entry); + if (!isIsoBak && !isNumericBak) { continue; } const bakIdx = entry.lastIndexOf(".bak.");