refactor: stop caching cli image files

This commit is contained in:
Peter Steinberger
2026-05-10 03:54:10 +01:00
parent 799bc44bfc
commit 7ea66fcd74
3 changed files with 30 additions and 20 deletions

View File

@@ -634,6 +634,9 @@ sessionId}` and session key context.
inline runs seed those entries into the per-agent runtime scratch namespace,
and disk-backed tools overlay that SQLite scratch for attachment paths. The
old subagent-run attachment-dir registry columns and cleanup hooks are gone.
- CLI image hydration no longer maintains stable `openclaw-cli-images` cache
files. External CLI backends still receive file paths, but those paths are
per-run temp materializations with cleanup.
- Cache-trace diagnostics, Anthropic payload diagnostics, raw model stream
diagnostics, and diagnostics timeline events now write SQLite diagnostic rows
instead of `logs/*.jsonl` files.

View File

@@ -202,7 +202,7 @@ describe("buildCliArgs", () => {
});
describe("writeCliImages", () => {
it("uses stable hashed file paths so repeated image hydration reuses the same path", async () => {
it("materializes images into per-run temp paths and cleans them up", async () => {
const workspaceDir = await fs.mkdtemp(
path.join(resolvePreferredOpenClawTmpDir(), "openclaw-cli-write-images-"),
);
@@ -225,12 +225,16 @@ describe("writeCliImages", () => {
try {
expect(first.paths).toHaveLength(1);
expect(second.paths).toEqual(first.paths);
expect(first.paths[0]).toContain(`${resolvePreferredOpenClawTmpDir()}/openclaw-cli-images/`);
expect(second.paths).toHaveLength(1);
expect(second.paths).not.toEqual(first.paths);
expect(first.paths[0]).toContain(`${resolvePreferredOpenClawTmpDir()}/openclaw-cli-images-`);
expect(first.paths[0]).toMatch(/\.png$/);
await expect(fs.readFile(first.paths[0])).resolves.toEqual(Buffer.from(image.data, "base64"));
await first.cleanup();
await expect(fs.access(first.paths[0])).rejects.toMatchObject({ code: "ENOENT" });
} finally {
await fs.rm(first.paths[0], { force: true });
await first.cleanup();
await second.cleanup();
await fs.rm(workspaceDir, { recursive: true, force: true });
}
});
@@ -254,7 +258,7 @@ describe("writeCliImages", () => {
try {
expect(written.paths[0]).toMatch(/\.heic$/);
} finally {
await fs.rm(written.paths[0], { force: true });
await written.cleanup();
await fs.rm(workspaceDir, { recursive: true, force: true });
}
});

View File

@@ -8,7 +8,6 @@ import type { SourceReplyDeliveryMode } from "../../auto-reply/get-reply-options
import type { ThinkLevel } from "../../auto-reply/thinking.js";
import type { CliBackendConfig } from "../../config/types.js";
import type { OpenClawConfig } from "../../config/types.openclaw.js";
import { privateFileStore } from "../../infra/private-file-store.js";
import { tempWorkspace } from "../../infra/private-temp-workspace.js";
import { resolvePreferredOpenClawTmpDir } from "../../infra/tmp-openclaw-dir.js";
import { MAX_IMAGE_BYTES } from "../../media/constants.js";
@@ -206,7 +205,7 @@ export function resolvePromptInput(params: { backend: CliBackendConfig; prompt:
return { argsPrompt: params.prompt };
}
function resolveCliImagePath(image: ImageContent): string {
function resolveCliImageFileName(image: ImageContent): string {
const ext = extensionForMime(image.mimeType) ?? ".bin";
const digest = crypto
.createHash("sha256")
@@ -214,14 +213,19 @@ function resolveCliImagePath(image: ImageContent): string {
.update("\0")
.update(image.data)
.digest("hex");
return path.join(resolvePreferredOpenClawTmpDir(), "openclaw-cli-images", `${digest}${ext}`);
return `${digest}${ext}`;
}
function resolveCliImageRoot(params: { backend: CliBackendConfig; workspaceDir: string }): string {
async function createCliImageRoot(params: {
backend: CliBackendConfig;
workspaceDir: string;
}): Promise<string> {
if (params.backend.imagePathScope === "workspace") {
return path.join(params.workspaceDir, ".openclaw-cli-images");
const root = path.join(params.workspaceDir, ".openclaw-cli-images", crypto.randomUUID());
await fs.mkdir(root, { recursive: true, mode: 0o700 });
return root;
}
return path.join(resolvePreferredOpenClawTmpDir(), "openclaw-cli-images");
return await fs.mkdtemp(path.join(resolvePreferredOpenClawTmpDir(), "openclaw-cli-images-"));
}
function appendImagePathsToPrompt(prompt: string, paths: string[], prefix = ""): string {
@@ -275,23 +279,22 @@ export async function writeCliImages(params: {
workspaceDir: string;
images: ImageContent[];
}): Promise<{ paths: string[]; cleanup: () => Promise<void> }> {
const imageRoot = resolveCliImageRoot({
const imageRoot = await createCliImageRoot({
backend: params.backend,
workspaceDir: params.workspaceDir,
});
await fs.mkdir(imageRoot, { recursive: true, mode: 0o700 });
const store = privateFileStore(imageRoot);
const paths: string[] = [];
for (let i = 0; i < params.images.length; i += 1) {
const image = params.images[i];
const fileName = path.basename(resolveCliImagePath(image));
const fileName = resolveCliImageFileName(image);
const filePath = path.join(imageRoot, fileName);
const buffer = Buffer.from(image.data, "base64");
await store.writeText(fileName, buffer);
paths.push(store.path(fileName));
await fs.writeFile(filePath, buffer, { mode: 0o600 });
paths.push(filePath);
}
// Keep content-addressed image paths stable across Claude CLI runs so prompt
// text and argv don't churn on every turn with fresh temp-dir suffixes.
const cleanup = async () => {};
const cleanup = async () => {
await fs.rm(imageRoot, { recursive: true, force: true });
};
return { paths, cleanup };
}