






















@@ -315,6 +315,88 @@ describe("Integration: saveSessionStore with pruning", () => {
315315await expectPathExists(freshOrphanTranscript);
316316});
317317318+it("sessions cleanup fix-missing prunes malformed stored session rows", async () => {
319+applyEnforcedMaintenanceConfig(mockLoadConfig);
320+321+const now = Date.now();
322+const validTranscript = path.join(testDir, "valid-present.jsonl");
323+const legacyPresentTranscript = path.join(testDir, "legacy-present.jsonl");
324+await fs.writeFile(
325+storePath,
326+JSON.stringify(
327+{
328+"invalid-no-file": { sessionId: "agent:main:main", updatedAt: now },
329+"invalid-bad-file": {
330+sessionId: "agent:main:main",
331+sessionFile: "../outside.jsonl",
332+updatedAt: now,
333+},
334+"invalid-missing-relative-file": {
335+sessionId: "agent:main:main",
336+sessionFile: "missing.jsonl",
337+updatedAt: now,
338+},
339+"agent:main:metadata": {
340+sessionId: "agent:main:metadata",
341+updatedAt: now,
342+groupActivation: "always",
343+},
344+"legacy-present-invalid-id": {
345+sessionId: "agent:main:main",
346+sessionFile: "legacy-present.jsonl",
347+updatedAt: now,
348+},
349+"valid-present": { sessionId: "valid-present", updatedAt: now },
350+} satisfies Record<string, SessionEntry>,
351+null,
352+2,
353+),
354+"utf-8",
355+);
356+await fs.writeFile(validTranscript, "valid", "utf-8");
357+await fs.writeFile(legacyPresentTranscript, "legacy", "utf-8");
358+359+const dryRun = await runSessionsCleanup({
360+cfg: {},
361+opts: { store: storePath, dryRun: true, enforce: true, fixMissing: true },
362+targets: [{ agentId: "main", storePath }],
363+});
364+const preview = dryRun.previewResults[0];
365+expect(preview?.summary.missing).toBe(3);
366+expect(preview?.summary.beforeCount).toBe(6);
367+expect(preview?.summary.afterCount).toBe(3);
368+expect(preview?.missingKeys.has("invalid-no-file")).toBe(true);
369+expect(preview?.missingKeys.has("invalid-bad-file")).toBe(true);
370+expect(preview?.missingKeys.has("invalid-missing-relative-file")).toBe(true);
371+expect(preview?.missingKeys.has("agent:main:metadata")).toBe(false);
372+expect(preview?.missingKeys.has("legacy-present-invalid-id")).toBe(false);
373+const rawAfterDryRun = JSON.parse(await fs.readFile(storePath, "utf-8")) as Record<
374+string,
375+unknown
376+>;
377+expect(rawAfterDryRun).toHaveProperty("invalid-no-file");
378+379+const applied = await runSessionsCleanup({
380+cfg: {},
381+opts: { store: storePath, enforce: true, fixMissing: true },
382+targets: [{ agentId: "main", storePath }],
383+});
384+385+expect(applied.appliedSummaries[0]?.missing).toBe(3);
386+expect(applied.appliedSummaries[0]?.afterCount).toBe(3);
387+const persisted = loadSessionStore(storePath, { skipCache: true });
388+expect(Object.keys(persisted)).toEqual([
389+"agent:main:metadata",
390+"legacy-present-invalid-id",
391+"valid-present",
392+]);
393+expect(persisted["agent:main:metadata"]).toMatchObject({ groupActivation: "always" });
394+expect(persisted["agent:main:metadata"]?.sessionId).toBeUndefined();
395+expect(persisted["legacy-present-invalid-id"]?.sessionId).toBe("agent:main:main");
396+await expectPathExists(validTranscript);
397+await expectPathExists(legacyPresentTranscript);
398+});
399+318400it("sessions cleanup previews stale direct DM rows after dmScope returns to main", async () => {
319401applyEnforcedMaintenanceConfig(mockLoadConfig);
320402此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。