fix(trajectory): clean up runtime files across short reads (#109206)

This commit is contained in:
Wynne668
2026-07-16 15:02:08 -07:00
committed by GitHub
parent 484ea309d1
commit 6c8ab16158
2 changed files with 27 additions and 8 deletions
+25 -7
View File
@@ -1,7 +1,8 @@
// Trajectory cleanup tests cover retention pruning of trajectory artifacts.
import fsSync from "node:fs";
import fs from "node:fs/promises";
import path from "node:path";
import { describe, expect, it } from "vitest";
import { describe, expect, it, vi } from "vitest";
import { formatSqliteSessionFileMarker } from "../config/sessions/sqlite-marker.js";
import { withTempDir } from "../test-helpers/temp-dir.js";
import {
@@ -131,14 +132,31 @@ describe("trajectory cleanup", () => {
const pointerPath = resolveTrajectoryPointerFilePath(sessionFile);
await fs.writeFile(pointerPath, pointerFile(sessionId, safeExternalRuntime), "utf8");
await removeSessionTrajectoryArtifacts({
sessionId,
sessionFile,
storePath,
restrictToStoreDir: true,
});
const realReadSync = fsSync.readSync.bind(fsSync);
let shortReadCalls = 0;
const readSpy = vi.spyOn(fsSync, "readSync").mockImplementation(((
fd: number,
buffer: NodeJS.ArrayBufferView,
offset: number,
length: number,
position: fsSync.ReadPosition | null,
) => {
shortReadCalls += 1;
return realReadSync(fd, buffer, offset, Math.min(length, 16), position);
}) as typeof fsSync.readSync);
try {
await removeSessionTrajectoryArtifacts({
sessionId,
sessionFile,
storePath,
restrictToStoreDir: true,
});
} finally {
readSpy.mockRestore();
}
await expectPathMissing(safeExternalRuntime);
expect(shortReadCalls).toBeGreaterThan(1);
await expectPathMissing(pointerPath);
await fs.writeFile(pointerPath, pointerFile(sessionId, unsafeExternalRuntime), "utf8");
+2 -1
View File
@@ -4,6 +4,7 @@ import path from "node:path";
import { isRecord } from "@openclaw/normalization-core/record-coerce";
import { resolveSessionFilePath } from "../config/sessions/paths.js";
import { parseSqliteSessionFileMarker } from "../config/sessions/sqlite-marker.js";
import { readFileWindowFullySync } from "../infra/file-read.js";
import { isPathInside } from "../infra/path-guards.js";
import {
resolveTrajectoryFilePath,
@@ -79,7 +80,7 @@ function readFirstNonEmptyLine(filePath: string): string | null {
try {
fd = fs.openSync(filePath, "r");
const buffer = Buffer.alloc(64 * 1024);
const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, 0);
const bytesRead = readFileWindowFullySync(fd, buffer, 0);
if (bytesRead <= 0) {
return null;
}