fix: ignore backup artifacts in usage transcript scan

This commit is contained in:
robo7 2026-03-13 22:45:37 +08:00
parent 524b638150
commit 8636e745e6
2 changed files with 30 additions and 5 deletions

View File

@ -241,6 +241,8 @@ describe("session cost usage", () => {
"sess-active.jsonl",
"sess-reset.jsonl.reset.2026-03-13T10-05-00.000Z",
"sess-deleted.jsonl.deleted.2026-03-13T10-10-00.000Z",
"sess-backup.jsonl.bak.2026-03-13T10-20-00.000Z",
"sessions.json.bak.1737420882",
];
for (const [i, name] of files.entries()) {
@ -286,6 +288,7 @@ describe("session cost usage", () => {
["sess-active.jsonl", "active hello"],
["sess-reset.jsonl.reset.2026-03-13T10-05-00.000Z", "reset hello"],
["sess-deleted.jsonl.deleted.2026-03-13T10-10-00.000Z", "deleted hello"],
["foo.jsonl.bar.jsonl.reset.2026-03-13T10-15-00.000Z", "compound hello"],
] as const;
for (const [name, text] of files) {
@ -309,11 +312,13 @@ describe("session cost usage", () => {
startMs: new Date("2026-03-13T00:00:00.000Z").getTime(),
});
const ids = sessions.map((session) => session.sessionId).sort();
expect(ids).toEqual(["sess-active", "sess-deleted", "sess-reset"]);
expect(ids).toEqual(["foo.jsonl.bar", "sess-active", "sess-deleted", "sess-reset"]);
const reset = sessions.find((session) => session.sessionId === "sess-reset");
const deleted = sessions.find((session) => session.sessionId === "sess-deleted");
const compound = sessions.find((session) => session.sessionId === "foo.jsonl.bar");
expect(reset?.firstUserMessage).toContain("reset hello");
expect(deleted?.firstUserMessage).toContain("deleted hello");
expect(compound?.firstUserMessage).toContain("compound hello");
});
});

View File

@ -9,6 +9,10 @@ import {
resolveSessionFilePath,
resolveSessionTranscriptsDirForAgent,
} from "../config/sessions/paths.js";
import {
isPrimarySessionTranscriptFileName,
parseSessionArchiveTimestamp,
} from "../config/sessions/artifacts.js";
import type { SessionEntry } from "../config/sessions/types.js";
import { stripEnvelope, stripMessageIdHints } from "../shared/chat-envelope.js";
import { countToolResults, extractToolCallNames } from "../utils/transcript-tools.js";
@ -53,14 +57,30 @@ export type {
SessionUsageTimeSeries,
} from "./session-cost-usage.types.js";
const SESSION_TRANSCRIPT_FILE_RE = /\.jsonl(?:\.(?:reset|deleted)\..+)?$/;
const isArchivedUsageTranscriptFilename = (name: string): boolean =>
parseSessionArchiveTimestamp(name, "reset") !== null ||
parseSessionArchiveTimestamp(name, "deleted") !== null;
const isSessionTranscriptFilename = (name: string): boolean =>
SESSION_TRANSCRIPT_FILE_RE.test(name);
isPrimarySessionTranscriptFileName(name) || isArchivedUsageTranscriptFilename(name);
const deriveSessionIdFromFilename = (name: string): string => {
const match = name.match(/^(.*?\.jsonl)(?:\.(?:reset|deleted)\..+)?$/);
return match ? match[1].slice(0, -6) : name.replace(/\.jsonl$/, "");
for (const reason of ["reset", "deleted"] as const) {
const archivedAt = parseSessionArchiveTimestamp(name, reason);
if (archivedAt === null) {
continue;
}
const marker = `.${reason}.`;
const index = name.lastIndexOf(marker);
if (index >= 0) {
const base = name.slice(0, index);
if (base.endsWith(".jsonl")) {
return base.slice(0, -6);
}
return base;
}
}
return name.endsWith(".jsonl") ? name.slice(0, -6) : name;
};
const emptyTotals = (): CostUsageTotals => ({