#11754: fix(read): persist image data and inject MEDIA directive for channel delivery
agents
stale
Cluster:
Voice Call and TTS Improvements
## Summary
When the `read` tool reads an image file, the base64 image data is returned as a content block visible to the LLM but never converted to a deliverable media URL. This means images read by agents are not sent to Telegram or other channels — the user only sees the agent's text reply without the image.
## Changes
- **`src/agents/pi-tools.read.ts`**: After reading an image, persist the base64 data to a cache file under `.openclaw/media-cache/` in the workspace and inject a `MEDIA:./relative-path` directive into the text content block. The existing delivery pipeline then picks up the relative path via `splitMediaFromOutput` and sends the image to the channel.
- **`src/agents/pi-tools.ts`**: Pass `workspaceRoot` to `createOpenClawReadTool` for the non-sandboxed path.
- **`src/agents/pi-tools.read.image-delivery.test.ts`**: Tests verifying MEDIA injection for image reads, no injection for text reads, and no injection without workspaceRoot.
## How it works
1. Agent calls `read` on an image file
2. Read tool returns `{ type: 'image', data: '<base64>', mimeType: 'image/png' }` content block
3. **New**: The image data is persisted to `.openclaw/media-cache/<hash>.png`
4. **New**: A `MEDIA:./…` directive is appended to the text content block
5. The delivery pipeline (`splitMediaFromOutput`) extracts the media URL
6. Image is sent to Telegram/other channels via `sendMedia`
Fixes #11735
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR updates the `read` tool wrapper so that when an image is read (base64 `image` content block), the image payload is persisted into a workspace-local cache directory (`.openclaw/media-cache/`) and a `MEDIA:` directive is appended to the tool’s text output. The existing media parsing/delivery pipeline (`splitMediaFromOutput` → channel `sendMedia`) can then detect the directive and deliver the image to downstream channels. It also wires `workspaceRoot` through the non-sandboxed tool creation path and adds a Vitest suite covering the injection behavior.
<h3>Confidence Score: 2/5</h3>
- This PR has a few correctness issues that can break media delivery and should be fixed before merging.
- The core idea (persist + MEDIA directive) fits the existing `splitMediaFromOutput` pipeline, but the current directive/path formatting and injection behavior are inconsistent with how MEDIA tokens are parsed/consumed, and the persistence step can produce invalid/corrupted files without detection. These are likely to cause real delivery failures or duplicated MEDIA extraction in normal operation.
- src/agents/pi-tools.read.ts (MEDIA path format, injection target, image persistence validation); src/agents/pi-tools.read.image-delivery.test.ts (would currently fail once path semantics are corrected/validated)
<!-- greptile_other_comments_section -->
<sub>(4/5) You can add custom instructions or style guidelines for the agent [here](https://app.greptile.com/review/github)!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#20735: fix: skip auto-attaching tool MEDIA: paths already sent via message t…
by anillBhoi · 2026-02-19
76.8%
#8042: feat(telegram): add media index for reply-to media lookup
by batumilove · 2026-02-03
76.6%
#19399: telegram: fix MEDIA false positives and partial final drop
by HOYALIM · 2026-02-17
75.9%
#20294: fix(message): thread mediaLocalRoots through channel plugin dispatch
by odrobnik · 2026-02-18
75.7%
#23764: feat(tui): render inline images via MEDIA: protocol and pi-tui Image
by ademczuk · 2026-02-22
75.3%
#18890: fix(media): parse tool-result MEDIA directives with shared parser
by teededung · 2026-02-17
74.8%
#2958: fix(media): wire tools.media.image.maxBytes config to image processin…
by shamsulalam1114 · 2026-01-27
74.0%
#14794: fix: parse inline MEDIA: tokens in agent replies
by explainanalyze · 2026-02-12
73.9%
#8076: fix(web): handle data URLs in loadWebMedia to prevent ENAMETOOLONG
by batumilove · 2026-02-03
73.9%
#21110: fix(tts): deliver audio via structured mediaUrl instead of MEDIA: t...
by hydro13 · 2026-02-19
73.3%