Skip to content

Commit 0214b37

Browse files
committed
[AI] fix(memory-core): use replaceFileAtomic for dreaming writes and narrow catch block (ClawSweeper P1)
- Replace manual tmp-${randomUUID()}+writeFile+rename with replaceFileAtomic from @openclaw/fs-safe for all three dreaming write paths: inline daily (preserveExistingMode), separate report, and deep separate report. Removed unused randomUUID import. - Narrow fs.access catch block in repairShortTermPromotionArtifacts to only suppress ENOENT — EACCES/EPERM now rethrow instead of triggering dangling-ref pruning. - Both fixes address ClawSweeper P1 findings on PR #94537.
1 parent 49ce2f6 commit 0214b37

2 files changed

Lines changed: 29 additions & 12 deletions

File tree

extensions/memory-core/src/dreaming-markdown.ts

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
// Memory Core plugin module implements dreaming markdown behavior.
2-
import { randomUUID } from "node:crypto";
32
import fs from "node:fs/promises";
43
import path from "node:path";
54
import {
@@ -12,6 +11,7 @@ import {
1211
replaceManagedMarkdownBlock,
1312
withTrailingNewline,
1413
} from "openclaw/plugin-sdk/memory-host-markdown";
14+
import { replaceFileAtomic } from "openclaw/plugin-sdk/security-runtime";
1515
import { updateDeepDreamsFile } from "./dreaming-dreams-file.js";
1616
import { resolveMemoryCoreNowMs, resolveMemoryCoreTimestamp } from "./time.js";
1717

@@ -91,9 +91,14 @@ export async function writeDailyDreamingPhaseBlock(params: {
9191
});
9292
// Write atomically via temp file so a crash mid-write cannot truncate the
9393
// daily memory file (read-modify-write on a non-atomic filesystem).
94-
const inlineTmpPath = `${inlinePath}.tmp-${randomUUID()}`;
95-
await fs.writeFile(inlineTmpPath, withTrailingNewline(updated), "utf-8");
96-
await fs.rename(inlineTmpPath, inlinePath);
94+
await replaceFileAtomic({
95+
filePath: inlinePath,
96+
content: withTrailingNewline(updated),
97+
mode: 0o600,
98+
preserveExistingMode: true,
99+
tempPrefix: "dreaming-inline",
100+
throwOnCleanupError: true,
101+
});
97102
}
98103

99104
if (shouldWriteSeparate(params.storage)) {
@@ -110,9 +115,13 @@ export async function writeDailyDreamingPhaseBlock(params: {
110115
body,
111116
"",
112117
].join("\n");
113-
const reportTmpPath = `${reportPath}.tmp-${randomUUID()}`;
114-
await fs.writeFile(reportTmpPath, report, "utf-8");
115-
await fs.rename(reportTmpPath, reportPath);
118+
await replaceFileAtomic({
119+
filePath: reportPath,
120+
content: report,
121+
mode: 0o600,
122+
tempPrefix: "dreaming-report",
123+
throwOnCleanupError: true,
124+
});
116125
}
117126

118127
await appendMemoryHostEvent(params.workspaceDir, {
@@ -148,9 +157,13 @@ export async function writeDeepDreamingReport(params: {
148157
if (shouldWriteSeparate(params.storage)) {
149158
reportPath = resolveSeparateReportPath(params.workspaceDir, "deep", nowMs, params.timezone);
150159
await fs.mkdir(path.dirname(reportPath), { recursive: true });
151-
const reportTmpPath = `${reportPath}.tmp-${randomUUID()}`;
152-
await fs.writeFile(reportTmpPath, `# Deep Sleep\n\n${body}\n`, "utf-8");
153-
await fs.rename(reportTmpPath, reportPath);
160+
await replaceFileAtomic({
161+
filePath: reportPath,
162+
content: `# Deep Sleep\n\n${body}\n`,
163+
mode: 0o600,
164+
tempPrefix: "dreaming-deep",
165+
throwOnCleanupError: true,
166+
});
154167
}
155168
await appendMemoryHostEvent(params.workspaceDir, {
156169
type: "memory.dream.completed",

extensions/memory-core/src/short-term-promotion.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2774,8 +2774,12 @@ export async function repairShortTermPromotionArtifacts(params: {
27742774
await fs.access(sourcePath);
27752775
sourceExists = true;
27762776
break;
2777-
} catch {
2778-
// ENOENT or other error — try next candidate path
2777+
} catch (err) {
2778+
// Only treat ENOENT (file genuinely doesn't exist) as missing.
2779+
// EACCES, EPERM, and other errors should not trigger pruning.
2780+
if ((err as NodeJS.ErrnoException)?.code !== "ENOENT") {
2781+
throw err;
2782+
}
27792783
}
27802784
}
27812785
if (!sourceExists) {

0 commit comments

Comments
 (0)