






















@@ -7,7 +7,7 @@ import {
77resolveTrajectoryPointerFilePath,
88} from "../../trajectory/paths.js";
99import { formatSessionArchiveTimestamp } from "./artifacts.js";
10-import { enforceSessionDiskBudget } from "./disk-budget.js";
10+import { enforceSessionDiskBudget, pruneUnreferencedSessionArtifacts } from "./disk-budget.js";
1111import type { SessionEntry } from "./types.js";
12121313async function expectPathExists(targetPath: string): Promise<void> {
@@ -102,6 +102,50 @@ describe("enforceSessionDiskBudget", () => {
102102});
103103});
104104105+it("reclaims stale store temps under pressure but never a fresh in-flight one (#56827)", async () => {
106+await withTempDir({ prefix: "openclaw-disk-budget-" }, async (dir) => {
107+const storePath = path.join(dir, "sessions.json");
108+const sessionId = "keep";
109+const transcriptPath = path.join(dir, `${sessionId}.jsonl`);
110+const staleTemp = path.join(
111+dir,
112+"sessions.json.111.0f9c1a2b-3c4d-4e5f-8a9b-0c1d2e3f4a5b.tmp",
113+);
114+const freshTemp = path.join(
115+dir,
116+"sessions.json.222.1a2b3c4d-5e6f-4a8b-9c0d-1e2f3a4b5c6d.tmp",
117+);
118+const store: Record<string, SessionEntry> = {
119+"agent:main:main": { sessionId, updatedAt: Date.now() },
120+};
121+await fs.writeFile(storePath, JSON.stringify(store, null, 2), "utf-8");
122+await fs.writeFile(transcriptPath, "k".repeat(80), "utf-8");
123+await fs.writeFile(staleTemp, "s".repeat(300), "utf-8");
124+await fs.writeFile(freshTemp, "f".repeat(300), "utf-8");
125+// Age the stale temp past the staleness window; the fresh one is in-flight.
126+const old = new Date(Date.now() - 30 * 60 * 1000);
127+await fs.utimes(staleTemp, old, old);
128+129+const result = await enforceSessionDiskBudget({
130+ store,
131+ storePath,
132+maintenance: {
133+maxDiskBytes: 750,
134+highWaterBytes: 600,
135+},
136+warnOnly: false,
137+});
138+139+// Stale orphan reclaimed; fresh in-flight temp (a live atomic-write source)
140+// and referenced transcript preserved even though still over the high-water mark.
141+await expectPathMissing(staleTemp);
142+await expectPathExists(freshTemp);
143+await expectPathExists(transcriptPath);
144+expectBudgetResult(result);
145+expect(result.removedFiles).toBe(1);
146+});
147+});
148+105149it("preserves runtime-provided session keys when removing entries for disk budget", async () => {
106150await withTempDir({ prefix: "openclaw-disk-budget-" }, async (dir) => {
107151const storePath = path.join(dir, "sessions.json");
@@ -287,3 +331,41 @@ describe("enforceSessionDiskBudget", () => {
287331});
288332});
289333});
334+335+describe("pruneUnreferencedSessionArtifacts", () => {
336+it("reclaims stale store temp sidecars but preserves in-flight ones (#56827)", async () => {
337+await withTempDir({ prefix: "openclaw-prune-temp-" }, async (dir) => {
338+const storePath = path.join(dir, "sessions.json");
339+const staleTemp = path.join(
340+dir,
341+"sessions.json.111.0f9c1a2b-3c4d-4e5f-8a9b-0c1d2e3f4a5b.tmp",
342+);
343+const freshTemp = path.join(
344+dir,
345+"sessions.json.222.1a2b3c4d-5e6f-4a8b-9c0d-1e2f3a4b5c6d.tmp",
346+);
347+const store: Record<string, SessionEntry> = {
348+"agent:main:main": { sessionId: "keep", updatedAt: Date.now() },
349+};
350+await fs.writeFile(storePath, JSON.stringify(store, null, 2), "utf-8");
351+await fs.writeFile(staleTemp, "s".repeat(64), "utf-8");
352+await fs.writeFile(freshTemp, "f".repeat(64), "utf-8");
353+// Age the stale temp well past the temp staleness window; keep the other in-flight.
354+const old = new Date(Date.now() - 30 * 60 * 1000);
355+await fs.utimes(staleTemp, old, old);
356+357+const result = await pruneUnreferencedSessionArtifacts({
358+ store,
359+ storePath,
360+// 30d general cutoff: a stale temp must be reclaimed by its own short window,
361+// not by the unreferenced-artifact age threshold.
362+olderThanMs: 30 * 24 * 60 * 60 * 1000,
363+});
364+365+await expectPathMissing(staleTemp);
366+await expectPathExists(freshTemp);
367+await expectPathExists(storePath);
368+expect(result.removedFiles).toBeGreaterThanOrEqual(1);
369+});
370+});
371+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。