From 36ca0e5c13e238632c5685e3be39425dfaa1722c Mon Sep 17 00:00:00 2001 From: OpenClaw Bot Date: Sun, 15 Feb 2026 13:09:26 +0000 Subject: [PATCH] fix: allow agent workspace directories in media local roots Modifies getDefaultLocalRoots() to dynamically scan STATE_DIR for workspace-* directories (e.g., workspace-) and include them in the allowed local roots for media file access. This fixes the issue where the message tool rejects file attachments from agent-specific workspace directories like ~/.openclaw/workspace-clawdy/. Fixes openclaw/openclaw#17133 --- src/web/media.ts | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/web/media.ts b/src/web/media.ts index c6dd11d05de..b54749f57a8 100644 --- a/src/web/media.ts +++ b/src/web/media.ts @@ -1,3 +1,4 @@ +import fsSync from "node:fs"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; @@ -34,14 +35,44 @@ type WebMediaOptions = { readFile?: (filePath: string) => Promise; }; +/** + * Scans STATE_DIR for agent workspace directories matching the pattern "workspace-*" + * and returns them as additional allowed local roots. + */ +function getAgentWorkspaceDirsSync(): string[] { + try { + // Note: Synchronous scan on each call - acceptable for now since this is only + // used when validating local media paths, which is not a hot path. + // For better performance, could add caching with invalidation. + const entries = fsSync.readdirSync(STATE_DIR, { withFileTypes: true }); + return entries + .filter( + (entry) => + entry.isDirectory() && + entry.name.startsWith("workspace-") && + // Exclude the default "workspace" directory (handled separately) + entry.name !== "workspace", + ) + .map((entry) => path.join(STATE_DIR, entry.name)); + } catch { + // If STATE_DIR doesn't exist or isn't readable, return empty array + return []; + } +} + export function getDefaultLocalRoots(): readonly string[] { - return [ + const staticRoots = [ os.tmpdir(), path.join(STATE_DIR, "media"), path.join(STATE_DIR, "agents"), path.join(STATE_DIR, "workspace"), path.join(STATE_DIR, "sandboxes"), ]; + + // Dynamically discover agent workspace directories (e.g., workspace-) + const workspaceDirs = getAgentWorkspaceDirsSync(); + + return [...staticRoots, ...workspaceDirs]; } async function assertLocalMediaAllowed(