#21042: fix(ui): render images in tool result messages
app: web-ui
size: S
## Summary
Fixes #21025 — Webchat UI doesn't render images in tool results.
When a tool (e.g. `read`) returns image content blocks in its result, the webchat control UI silently drops the image data during content normalization. The tool result section shows only the text portion (e.g. "Read image file [image/png]") with no image.
## Root Cause
Two issues working together:
1. **Content mapper drops image fields** — `normalizeMessage()` in `message-normalizer.ts` maps content items but only preserves `type`, `text`, `name`, and `args`. The `data`, `mimeType`, and `source` fields needed for image rendering are silently discarded.
2. **Tool result early-return skips images** — In `grouped-render.ts`, when a message is a tool result with no text content, the renderer short-circuits to show only tool cards. `extractImages()` is called but its results are never rendered in that path.
## What Changed
### `ui/src/ui/chat/message-normalizer.ts`
- Preserve `data`, `mimeType`, and `source` fields in the content mapper so image blocks survive normalization.
### `ui/src/ui/types/chat-types.ts`
- Extended `MessageContentItem` type to include `data`, `mimeType`, `source` fields and `"image"` / `"image_url"` type variants.
### `ui/src/ui/chat/grouped-render.ts`
- `extractImages()`: Added handling for inline `data`/`mimeType` fields (the format preserved by the updated normalizer), in addition to the existing `source` object format.
- Tool-result-only rendering path: When images are present, wrap both images and tool cards in a bubble instead of rendering cards alone.
### `ui/src/ui/chat/message-normalizer.test.ts`
- Added tests for image field preservation (`data`, `mimeType`, `source`).
- Existing tests updated to use `toMatchObject` for forward-compatible assertions.
## Options Evaluated
**Option 1 (implemented):** Preserve fields through normalizer + render in tool result path
- Minimal change, fixes the bug at both its source (normalization) and symptom (rendering)
- No new components or behavioral changes
**Option 2 (not implemented):** Bypass normalizer for tool results entirely
- Would require a parallel code path for raw tool messages
- Higher complexity, more surface area for bugs
## Validation
- Tests added for all three new fields (`data`, `mimeType`, `source`)
- Existing `normalizeMessage` tests pass (updated to `toMatchObject` for compatibility)
- No changes to non-image rendering paths
🤖 AI-Assisted: Yes (SureThing via Claude). Fully tested conceptually, confirmed existing test structure followed. I understand what the code does.
---
*First contribution — happy to iterate on feedback!*
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Fixed webchat UI to render images in tool results by preserving image metadata (`data`, `mimeType`, `source`) through message normalization.
- Modified `normalizeMessage()` to preserve `data`, `mimeType`, and `source` fields when mapping content blocks
- Extended `MessageContentItem` type to include image-related fields and `"image"` / `"image_url"` type variants
- Updated `extractImages()` to handle inline `data`/`mimeType` format (preserved by normalizer) in addition to existing `source` object format
- Fixed tool-result rendering to wrap both images and tool cards in a bubble when images are present
- Added comprehensive test coverage for image field preservation in three scenarios: inline format, Anthropic source format, and undefined values
<h3>Confidence Score: 4/5</h3>
- Safe to merge with minor review - well-tested fix for a clear bug
- The changes correctly address the stated issue with appropriate test coverage. The fix is minimal and follows existing patterns. Score is 4 rather than 5 due to some non-essential formatting changes mixed in with the core fix, though these don't introduce risk.
- No files require special attention - all changes are straightforward and well-tested
<sub>Last reviewed commit: 3c64127</sub>
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#4269: fix: support tool result image format in Control UI
by bobcyw · 2026-01-30
83.0%
#9248: Fix: Webchat UI goes grey/unresponsive after Slack message tool calls
by vishaltandale00 · 2026-02-05
80.5%
#22333: Fix webchat inline image rendering and size handling
by AIflow-Labs · 2026-02-21
80.1%
#16733: fix(ui): avoid injected newlines when tool output is hidden
by jp117 · 2026-02-15
80.1%
#17448: ui: make tool cards collapsible with inline expansion
by karimStekelenburg · 2026-02-15
79.5%
#23764: feat(tui): render inline images via MEDIA: protocol and pi-tui Image
by ademczuk · 2026-02-22
79.1%
#8284: Fix: Webchat images now persist after sending
by vishaltandale00 · 2026-02-03
78.5%
#8353: fix(ui): display tool calls during webchat streaming
by MarvinDontPanic · 2026-02-03
78.1%
#6819: fix(tui): handle unstructured tool results and errors in tool execu...
by TreyDong · 2026-02-02
77.5%
#8880: feat(ui): make URLs clickable in tool output
by jnvw · 2026-02-04
77.1%