fix(sessions): stop leaking file path as prompt content on read failure (#108546)

* fix(sessions): stop leaking file path as prompt content on read failure

When readFileSync fails for a valid file path, resolvePromptInput returns
the raw path string as prompt content instead of undefined. This injects
filesystem paths into the LLM context. The existing console.error warning
still fires; the caller already handles undefined returns correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: cover unreadable prompt paths

* test: use tracked resource loader temp dirs

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <peter@steipete.me>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
(cherry picked from commit c924819292)
This commit is contained in:
krissding
2026-07-16 13:42:03 +08:00
committed by Dallin Romney
parent f8f1e8bb0f
commit cc0e215fa4
2 changed files with 38 additions and 16 deletions
+37 -15
View File
@@ -1,16 +1,15 @@
// Resource loader tests cover compatibility wiring for SDK prompt transform
// aliases.
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { afterEach, describe, expect, it, vi } from "vitest";
import { useAutoCleanupTempDirTracker } from "../../../test/helpers/temp-dir.js";
import { DefaultResourceLoader } from "./resource-loader.js";
const tempDirs = useAutoCleanupTempDirTracker(afterEach);
describe("DefaultResourceLoader", () => {
it("keeps deprecated SDK prompt override aliases wired to prompt transforms", async () => {
// These aliases are deprecated but shipped SDK surface, so they still map
// through the same transform path as the current options.
const root = mkdtempSync(join(tmpdir(), "openclaw-resource-loader-"));
it("does not use unreadable prompt file paths as prompt content", async () => {
const root = tempDirs.make("openclaw-resource-loader-");
const consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
try {
const loader = new DefaultResourceLoader({
cwd: root,
@@ -20,18 +19,41 @@ describe("DefaultResourceLoader", () => {
noPromptTemplates: true,
noThemes: true,
noContextFiles: true,
systemPrompt: "base",
appendSystemPrompt: ["tail"],
systemPromptOverride: (base) => `${base ?? ""} legacy`,
appendSystemPromptOverride: (base) => [...base, "legacy"],
systemPrompt: root,
appendSystemPrompt: [root],
});
await loader.reload();
expect(loader.getSystemPrompt()).toBe("base legacy");
expect(loader.getAppendSystemPrompt()).toEqual(["tail", "legacy"]);
expect(loader.getSystemPrompt()).toBeUndefined();
expect(loader.getAppendSystemPrompt()).toEqual([]);
expect(consoleError).toHaveBeenCalledTimes(2);
} finally {
rmSync(root, { force: true, recursive: true });
consoleError.mockRestore();
}
});
it("keeps deprecated SDK prompt override aliases wired to prompt transforms", async () => {
// These aliases are deprecated but shipped SDK surface, so they still map
// through the same transform path as the current options.
const root = tempDirs.make("openclaw-resource-loader-");
const loader = new DefaultResourceLoader({
cwd: root,
agentDir: root,
noExtensions: true,
noSkills: true,
noPromptTemplates: true,
noThemes: true,
noContextFiles: true,
systemPrompt: "base",
appendSystemPrompt: ["tail"],
systemPromptOverride: (base) => `${base ?? ""} legacy`,
appendSystemPromptOverride: (base) => [...base, "legacy"],
});
await loader.reload();
expect(loader.getSystemPrompt()).toBe("base legacy");
expect(loader.getAppendSystemPrompt()).toEqual(["tail", "legacy"]);
});
});
+1 -1
View File
@@ -64,7 +64,7 @@ function resolvePromptInput(input: string | undefined, description: string): str
console.error(
chalk.yellow(`Warning: Could not read ${description} file ${input}: ${String(error)}`),
);
return input;
return undefined;
}
}