#19325: feat: support messages.mediaLocalRoots for custom media directories
size: XS
Cluster:
Media Handling Improvements
## Problem
Agents cannot send local files (via the message tool's `filePath`/`media` parameters) from directories outside the built-in allowlist (tmpdir, state-dir subdirs, agent workspace). This forces workarounds like direct API calls with `curl` for common use cases:
- Sending images from ComfyUI/Stable Diffusion output directories
- Attaching files from custom asset folders
- Sharing generated content from any non-workspace directory
## Solution
Add a `messages.mediaLocalRoots` config option — an array of additional local directories to allow for media attachments. These are merged with the existing built-in roots and agent workspace directory.
### Example config
```json
{
"messages": {
"mediaLocalRoots": ["/home/user/assets", "/home/user/ComfyUI/output"]
}
}
```
## Changes
- **`src/config/types.messages.ts`** — Add `mediaLocalRoots?: string[]` to `MessagesConfig`
- **`src/config/zod-schema.session.ts`** — Add `mediaLocalRoots` to the messages zod schema
- **`src/media/local-roots.ts`** — Read and merge user-configured roots in `getAgentScopedMediaLocalRoots()`
Paths are resolved to absolute and deduplicated against built-in roots. No filesystem-root entries are possible (existing validation in `assertLocalMediaAllowed` rejects them).
## Notes
- The `mediaLocalRoots` field already existed in the BlueBubbles channel schema but was never wired up globally. This PR adds it at the `messages` level so it applies across all channels.
- Minimal change: 3 files, ~24 lines added.
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Adds `messages.mediaLocalRoots` config to allow agents to send local files from custom directories beyond the built-in allowlist. Paths are validated as non-empty absolute paths at both schema load time and runtime, then merged with existing built-in roots. The implementation addresses prior review feedback by enforcing `min(1)` and `startsWith("/")` in the zod schema and adding defensive `trim()` + `isAbsolute()` checks in the runtime path resolution.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk
- The changes are minimal (3 files, ~24 lines), address a clear use case, and include proper validation at both schema and runtime levels. Previous review concerns about empty strings and relative paths have been addressed with defensive validation. Existing security validation in `assertLocalMediaAllowed` prevents filesystem root entries. The implementation follows established patterns and doesn't introduce new attack vectors.
- No files require special attention
<sub>Last reviewed commit: 082f22b</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#20294: fix(message): thread mediaLocalRoots through channel plugin dispatch
by odrobnik · 2026-02-18
80.6%
#19171: fix(feishu): pass mediaLocalRoots to sendMediaFeishu for agent-scop...
by whiskyboy · 2026-02-17
78.5%
#23627: fix(telegram,feishu): pass mediaLocalRoots through channel action a...
by rockkoca · 2026-02-22
78.0%
#20186: fix(discord): thread mediaLocalRoots through reply delivery path
by pvoo · 2026-02-18
77.1%
#22401: fix: resolve relative media paths against workspace and fix /tmp on...
by derrickburns · 2026-02-21
76.3%
#20488: fix(discord): pass mediaLocalRoots to sendMessageDiscord
by olyashok · 2026-02-19
76.1%
#23148: fix: forward mediaLocalRoots in whatsapp plugin sendMedia
by MunemHashmi · 2026-02-22
74.7%
#22178: test(web): allow fixture roots in media local file tests
by Kansodata · 2026-02-20
74.3%
#9817: fix(media): resolve relative paths before reading local files (#8759)
by lailoo · 2026-02-05
74.1%
#18170: feat(telegram): support local Bot API server via `apiRoot` config
by iemesowum · 2026-02-16
74.1%