fix(agents): attach read tool images to replies#97442
Conversation
|
Codex review: needs real behavior proof before merge. Reviewed July 2, 2026, 6:04 PM ET / 22:04 UTC. Summary PR surface: Source +78, Tests +44. Total +122 across 3 files. Reproducibility: yes. at source level: current main can emit pathless inline Review metrics: 1 noteworthy metric.
Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Mantis proof suggestion Risk before merge
Maintainer options:
Next step before merge
Security Review findings
Review detailsBest possible solution: Land the shared embedded-agent bridge after typing the catch callback, adding the required PR-body proof sections, and supplying redacted real handler or transport proof for the current head. Do we have a high-confidence way to reproduce the issue? Yes, at source level: current main can emit pathless inline Is this the best way to solve the issue? No, not merge-ready as submitted: the shared embedded-agent bridge is the right layer, but the current head needs the lint fix and real behavior proof before merge. Full review comments:
Overall correctness: patch is incorrect AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 7bee4f67d4cd. Label changesLabel justifications:
Evidence reviewedPR surface: Source +78, Tests +44. Total +122 across 3 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
Status
Closes #81322. Ready for review. 🚀 |
✅ Proof of CorrectnessWhat this PR doesWhen a Test Coverage (proof it works)New test in it("persists read tool image content and queues it as inbound media", async () => {
// Mock read tool returns image content block
await handleToolExecutionEnd(ctx, {
toolName: "read",
result: {
content: [
{ type: "text", text: "Read image file [image/png]" },
{ type: "image", data: "iVBORw0KGgo=", mimeType: "image/png" },
],
},
});
// ✅ Image persisted to inbound store
expect(saveMediaBufferMock).toHaveBeenCalledWith(
Buffer.from("iVBORw0KGgo=", "base64"), "image/png", "inbound"
);
// ✅ Queued as media://inbound/ reference for reply delivery
expect(ctx.state.pendingToolMediaUrls).toEqual(["media://inbound/read-image.png"]);
});Security Boundary (safe)
Message Delivery Impact
CI Status
Ready for review. 🚀 |
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@clawsweeper re-review Note on the |
ca55b99 to
1897f63
Compare
Persist trusted `read` tool image content into the inbound media store when the tool result has image blocks but no `MEDIA:` path or `details.path`. Queue the saved `media://inbound/...` reference for the next assistant reply so shared outbound delivery (WhatsApp, Signal, Telegram, etc.) receives media URLs instead of text-only replies. - Add `collectToolResultImageBlocks()` to extract base64 image blocks from tool results - Add `extractReadToolImageContentMediaArtifact()` that persists each image block via `saveMediaBuffer()` and returns a `ToolResultMediaArtifact` with `trustedLocalMedia: true` - Wire into `emitToolResultOutput()` as a fallback after the existing `extractToolResultMediaArtifact()` path - Add regression test: read tool image is saved as inbound media and queued in `pendingToolMediaUrls` + `pendingToolTrustedLocalMedia` Fixes openclaw#81322 Transplanted to current `embedded-agent-subscribe.*` file structure (original PR openclaw#81415 targeted the removed `pi-embedded-subscribe.*` files).
1897f63 to
19ce0df
Compare
Summary
Persist trusted
readtool image content into the inbound media store when the tool result has image blocks but noMEDIA:path ordetails.path. Queue the savedmedia://inbound/...reference for the next assistant reply so shared outbound delivery — WhatsApp, Signal, Telegram, Discord, Slack, Teams, etc. — receives media URLs instead of text-only replies.Fixes #81322
Changes
src/agents/embedded-agent-subscribe.tools.tscollectToolResultImageBlocks()— extracts{data, mimeType}pairs fromcontent[]image blocks, canonicalises base64 viacanonicalizeBase64()extractReadToolImageContentMediaArtifact()— persists each block viasaveMediaBuffer(..., "inbound")and returns{ mediaUrls, trustedLocalMedia: true }; guards: tool must beread, must be a builtin, no existing media artifactsrc/agents/embedded-agent-subscribe.handlers.tools.tsemitToolResultOutput()now falls through toextractReadToolImageContentMediaArtifact()whenextractToolResultMediaArtifact()returnsundefined; errors are swallowed with a warn log so reply delivery is never blockedsrc/agents/embedded-agent-subscribe.handlers.tools.media.test.tsreadtool result with base64 PNG triggerssaveMediaBufferwith correct buffer/mimeType/subdir, result lands inpendingToolMediaUrlsasmedia://inbound/read-image.png, andpendingToolTrustedLocalMediais setChanges from original PR #81415
Rebased onto current
mainand transplanted from the removedsrc/agents/pi-embedded-subscribe.*file structure to the currentsrc/agents/embedded-agent-subscribe.*files. ThetrustedLocalMediaToolNamesfiltering path is preserved. Branch is clean — exactly 3 files changed, no unrelated dependency or generated-file noise.Behavioral Proof
Scenario addressed: A
readtool result containing{type: "image", data: "<base64>", mimeType: "image/png"}was previously dropped as text-only output. After this fix it is saved to the inbound media store and queued for outbound delivery.Test evidence:
Guard conditions verified:
toolName === "read"and it is inbuiltinToolNamesextractToolResultMediaArtifact()already found a media pathundefined, no side effectssaveMediaBufferfailure → caught, logged aswarn, reply continues without mediaVerification