fix(sessions): exclude archived trajectory artifacts (#126912)

* fix(sessions): exclude archived trajectory artifacts

* fix(sessions): classify archived transcripts through canonical parser

Reject archived trajectory and compaction checkpoint artifacts while preserving retained conversation transcripts and existing Windows test cleanup.

Co-authored-by: sunlit-deng <yang.jiajun1@xydigit.com>

* test(memory): compact session archive listener expectations

Preserve every archive-listener invariant while keeping the regression suite below its enforced line budget.

Co-authored-by: sunlit-deng <yang.jiajun1@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
sunlit-deng
2026-08-26 18:16:26 +08:00
committed by GitHub
parent c69abc2327
commit 01ac8db8ce
4 changed files with 86 additions and 12 deletions
@@ -1083,6 +1083,10 @@ describe("session startup catch-up", () => {
it.each([
"thread.jsonl.bak.2026-06-23T10-00-00.000Z",
"thread.trajectory.jsonl",
"thread.trajectory.jsonl.deleted.2026-06-23T10-00-00.000Z",
"thread.trajectory.jsonl.reset.2026-06-23T10-00-00.000Z.zst",
"thread.checkpoint.11111111-1111-4111-8111-111111111111.jsonl.deleted.2026-06-23T10-00-00.000Z",
"thread.checkpoint.11111111-1111-4111-8111-111111111111.jsonl.reset.2026-06-23T10-00-00.000Z.zst",
"sessions.json",
])("ignores non-corpus session artifact updates for %s", async (fileName) => {
vi.useFakeTimers();
@@ -1096,10 +1100,8 @@ describe("session startup catch-up", () => {
await vi.advanceTimersByTimeAsync(6000);
await harness.waitForSessionSync();
expect(harness.getPendingArchiveFiles()).toEqual([]);
expect(harness.getDirtyArchiveFiles()).toEqual([]);
expect(harness.syncCalls).toEqual([]);
expect(harness.indexedPaths).toEqual([]);
expect([harness.getPendingArchiveFiles(), harness.getDirtyArchiveFiles()]).toEqual([[], []]);
expect([harness.syncCalls, harness.indexedPaths]).toEqual([[], []]);
} finally {
harness.stopTranscriptListener();
}
@@ -1119,8 +1121,7 @@ describe("session startup catch-up", () => {
await harness.waitForSessionSync();
expect(harness.getDirtyArchiveFiles()).toEqual([]);
expect(harness.syncCalls).toEqual([]);
expect(harness.indexedPaths).toEqual([]);
expect([harness.syncCalls, harness.indexedPaths]).toEqual([[], []]);
} finally {
harness.stopTranscriptListener();
}
@@ -110,7 +110,15 @@ describe("listSessionFilesForAgent", () => {
"active.jsonl.reset.2026-02-16T22-26-33.000Z",
"active.jsonl.deleted.2026-02-16T22-27-33.000Z",
];
const excluded = ["active.jsonl.bak.2026-02-16T22-28-33.000Z", "sessions.json", "notes.md"];
const excluded = [
"active.jsonl.bak.2026-02-16T22-28-33.000Z",
"active.trajectory.jsonl.deleted.2026-02-16T22-30-33.000Z",
"active.trajectory.jsonl.reset.2026-02-16T22-31-33.000Z.zst",
"active.checkpoint.11111111-1111-4111-8111-111111111111.jsonl.deleted.2026-02-16T22-32-33.000Z",
"active.checkpoint.11111111-1111-4111-8111-111111111111.jsonl.reset.2026-02-16T22-33-33.000Z.zst",
"sessions.json",
"notes.md",
];
excluded.push("active.checkpoint.11111111-1111-4111-8111-111111111111.jsonl");
for (const fileName of [...included, ...excluded]) {
+67
View File
@@ -90,6 +90,8 @@ describe("session artifact helpers", () => {
it("classifies usage-counted transcript files", () => {
expect(isUsageCountedSessionTranscriptFileName("abc.jsonl")).toBe(true);
expect(isUsageCountedSessionTranscriptFileName(".jsonl")).toBe(true);
expect(isUsageCountedSessionTranscriptFileName("keep.deleted.keep.jsonl")).toBe(true);
expect(
isUsageCountedSessionTranscriptFileName("abc.jsonl.reset.2026-01-01T00-00-00.000Z"),
).toBe(true);
@@ -101,6 +103,39 @@ describe("session artifact helpers", () => {
`abc.jsonl.deleted.2026-01-01T00-00-00.000Z.${"a".repeat(32)}`,
),
).toBe(true);
expect(
isUsageCountedSessionTranscriptFileName("abc.jsonl.reset.2026-01-01T00-00-00.000Z.zst"),
).toBe(true);
expect(
isUsageCountedSessionTranscriptFileName(
"abc.checkpoint.not-a-uuid.jsonl.deleted.2026-01-01T00-00-00.000Z",
),
).toBe(true);
expect(
isUsageCountedSessionTranscriptFileName(
"abc.trajectory.jsonl.deleted.2026-01-01T00-00-00.000Z",
),
).toBe(false);
expect(
isUsageCountedSessionTranscriptFileName(
"abc.trajectory.jsonl.reset.2026-01-01T00-00-00.000Z.zst",
),
).toBe(false);
for (const reason of ["reset", "deleted"] as const) {
expect(
isUsageCountedSessionTranscriptFileName(
`abc.checkpoint.11111111-1111-4111-8111-111111111111.jsonl.${reason}.2026-01-01T00-00-00.000Z.zst`,
),
).toBe(false);
}
expect(isUsageCountedSessionTranscriptFileName(".jsonl.reset.2026-01-01T00-00-00.000Z")).toBe(
false,
);
expect(
isUsageCountedSessionTranscriptFileName(
`abc.jsonl.deleted.2026-01-01T00-00-00.000Z.${"A".repeat(32)}`,
),
).toBe(false);
expect(isUsageCountedSessionTranscriptFileName("abc.jsonl.bak.2026-01-01T00-00-00.000Z")).toBe(
false,
);
@@ -114,6 +149,10 @@ describe("session artifact helpers", () => {
it("parses usage-counted session ids from file names", () => {
expect(parseUsageCountedSessionIdFromFileName("abc.jsonl")).toBe("abc");
expect(parseUsageCountedSessionIdFromFileName(".jsonl")).toBe("");
expect(parseUsageCountedSessionIdFromFileName("keep.deleted.keep.jsonl")).toBe(
"keep.deleted.keep",
);
expect(parseUsageCountedSessionIdFromFileName("abc.jsonl.reset.2026-01-01T00-00-00.000Z")).toBe(
"abc",
);
@@ -125,6 +164,14 @@ describe("session artifact helpers", () => {
`abc.jsonl.deleted.2026-01-01T00-00-00.000Z.${"a".repeat(32)}`,
),
).toBe("abc");
expect(
parseUsageCountedSessionIdFromFileName("abc.jsonl.reset.2026-01-01T00-00-00.000Z.zst"),
).toBe("abc");
expect(
parseUsageCountedSessionIdFromFileName(
"abc.checkpoint.not-a-uuid.jsonl.deleted.2026-01-01T00-00-00.000Z",
),
).toBe("abc.checkpoint.not-a-uuid");
expect(parseUsageCountedSessionIdFromFileName("abc.jsonl.bak.2026-01-01T00-00-00.000Z")).toBe(
null,
);
@@ -134,6 +181,26 @@ describe("session artifact helpers", () => {
),
).toBeNull();
expect(parseUsageCountedSessionIdFromFileName("abc.trajectory.jsonl")).toBeNull();
expect(
parseUsageCountedSessionIdFromFileName(
"abc.trajectory.jsonl.deleted.2026-01-01T00-00-00.000Z",
),
).toBeNull();
expect(
parseUsageCountedSessionIdFromFileName(
"abc.trajectory.jsonl.reset.2026-01-01T00-00-00.000Z.zst",
),
).toBeNull();
for (const reason of ["reset", "deleted"] as const) {
expect(
parseUsageCountedSessionIdFromFileName(
`abc.checkpoint.11111111-1111-4111-8111-111111111111.jsonl.${reason}.2026-01-01T00-00-00.000Z.zst`,
),
).toBeNull();
}
expect(
parseUsageCountedSessionIdFromFileName(".jsonl.reset.2026-01-01T00-00-00.000Z"),
).toBeNull();
});
it("formats and parses archive timestamps", () => {
+3 -5
View File
@@ -137,10 +137,7 @@ export function isPrimarySessionTranscriptFileName(fileName: string): boolean {
/** Returns true for transcript files counted in usage, including reset/deleted archives. */
export function isUsageCountedSessionTranscriptFileName(fileName: string): boolean {
if (isPrimarySessionTranscriptFileName(fileName)) {
return true;
}
return hasArchiveSuffix(fileName, "reset") || hasArchiveSuffix(fileName, "deleted");
return parseUsageCountedSessionIdFromFileName(fileName) !== null;
}
/** Extracts the session id from a usage-counted transcript filename. */
@@ -153,7 +150,8 @@ export function parseUsageCountedSessionIdFromFileName(fileName: string): string
const marker = `.jsonl.${reason}.`;
const index = normalized.lastIndexOf(marker);
if (index > 0 && hasArchiveSuffix(normalized, reason)) {
return normalized.slice(0, index);
const sessionId = normalized.slice(0, index);
return isPrimarySessionTranscriptFileName(`${sessionId}.jsonl`) ? sessionId : null;
}
}
return null;