Repository navigation
Play a video or recording an agent writes inline in the chat - #18402
Conversation
A reply's inline image opens a viewer over the pane: zoom about the pointer (pinch, Command-scroll, double click, + - 0), pan a zoomed image, arrows and the arrow keys through every image of the chat, copy as PNG, Escape or a click beside the image to close. The mock light-theme chat shows three screens to open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
agent-pane.image-viewer: a reply with three screenshots, and images in two replies the viewer's arrows go through. The opened viewer needs a click, so its states wait for play steps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Red: the viewer's image list keeps an image on a fence's later line, loses images after a lone backtick, with parentheses in the target or inside bold; Tab leaves the viewer; an image the list misses opens without its name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The viewer's image list now comes from Markdown.tsx's own block parser and inline pattern (inlineImages), so it holds exactly the images a reply draws: none from code, and those after a lone backtick, with parentheses in the target or inside bold. An image the list still misses opens under its own name. WebKit's pinch (gesture events) zooms about the pointer; the stage's padding is symmetric so zoom stays about the pointer; Tab wraps inside the viewer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e data URLs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Notes draw under the reply by first reference, so the viewer lists their images there too; a reply whose only image has an upper-case data URL scheme is read, as Markdown.tsx draws it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The mock chat and the gallery entry show a chart, a gradient and a diagram instead of drawn app screens: previews are never mockups of cmux. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The viewer is a ui/Dialog (Base UI): it traps focus, makes the pane behind inert, closes on Escape and returns focus to the image, replacing the hand-rolled role, tab index and Tab handling the UI rules forbid. Only its own image keys (arrows, + - 0) stay a key handler, marked ui-allow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ext-image-viewer # Conflicts: # webviews/src/agent-session/acpmux/conversation/Markdown.tsx
…e reply budget Red: since base moved data URLs over 4 KB behind short refs and capped a reply at 8 MB, chatImages lists none of a reply's long images. Focused: bun test src/agent-session/acpmux/conversation/imageViewer.test.tsx -t budget (fail: 0 listed, 6 drawn) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he reply budget inlineImages mirrors renderInline again: it takes the URLs back from capDataUrls' short refs (passed down into bold, italic and link text), and chatImages drops the reply's images past dataUrlsOverBudget, which draw as their name. Focused: bun test src/agent-session/acpmux/conversation/imageViewer.test.tsx -t budget (pass) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Red since the move to ui/Dialog: a press inside the viewer focuses the dialog popup, above the body that handled the image keys, and the wheel and gesture listeners ran before the portal mounted the stage. Focused: bun test src/agent-session/acpmux/conversation/imageViewer.test.tsx (fail: keys stay at scale(1); Control-scroll not prevented) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ui/Dialog passes onKeyDown to its popup, which takes focus after a press inside, so the viewer's arrows and + - 0 keys go there. The stage is a callback ref, so the pinch and wheel listeners attach once the portal mounts it. Focused: bun test src/agent-session/acpmux/conversation/imageViewer.test.tsx (11 pass) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ext-image-viewer # Conflicts: # webviews/src/agent-session/acpmux/App.tsx
The opened viewer as play steps on the reply with three images, and anchors that prove nothing under the viewer moves. Regenerated agent pane bundle for the base merge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ext-image-viewer # Conflicts: # webviews/src/agent-session/acpmux/Localizable.xcstrings # webviews/src/agent-session/acpmux/conversation/Markdown.tsx
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ext-image-viewer # Conflicts: # webviews/src/agent-session/acpmux/conversation/Markdown.tsx
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Red: direct.ts drops ACP image content blocks, and a tool that writes or prints an image path (a screenshot, a rendered chart) shows only text. Red (local): bun test src/agent-session/acpmux/conversation/toolImages.test.tsx fails: ../toolImages does not exist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An agent's image outputs now show inline under the tool row, without opening it: - images a call returns (ACP image content blocks), kept as data URLs up to 8 MB each; - image files it wrote or printed (a saved screenshot, a rendered chart): its edited file, its locations and absolute image paths in its output, loaded through the host like a reply's local image. At most four per call, capped at 360x220 and lazily decoded. A click opens the image viewer, as does a local or web image in a reply now (ReplyImage's shown image is OpenableImage). Gallery: transcript variant tool-images with a returned screenshot and a saved chart. Focused (local): bun test src/agent-session/acpmux/conversation/toolImages.test.tsx 4 pass (red at 5c3dbb8). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ext-image-viewer # Conflicts: # webviews/src/agent-session/acpmux/App.tsx
…r branch Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Red: the host's image.load refuses a PDF (not a raster or SVG), and a tool's PDF output path shows no preview. Red (local): bun test src/agent-session/acpmux/conversation/toolImages.test.tsx fails on the report.pdf case; AgentPaneReplyImageTests .aPDFShowsItsFirstPageAsAPNG names thumbnailSide, which does not exist yet (runs in hosted cmux-next swift test). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The host draws page 1 of a local PDF (CoreGraphics, on white, longest side 1024) and hands the pane a PNG data URL, so the chip and the tool image strip show it like any image and a click opens the in-pane viewer. Only page 1 is decoded, so PDFs up to 100 MB qualify; a file that is not a PDF is refused. Tool output and edited paths ending in .pdf now count as image outputs. Gallery: the transcript tool-images variant adds a build-report PDF thumbnail. Focused: bun test toolImages.test.tsx (red 3/1 at 788b662, green 4/0); AgentPaneReplyImageTests.aPDFShowsItsFirstPageAsAPNG on the fleet suite runner. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Base no longer commits the web bundles: the agent-pane bundle files are dropped; only GENERATED.md stays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drops the committed agent-pane bundle files, as base did. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drops the committed agent-pane bundle files, as base did. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drops the committed agent-pane bundle files, as base did; the page CSP change stays in scripts/cmux-next/build-agent-pane-web.sh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Blacksmith runners detected OOM events on the following jobs:
|
Picks up the bundle-removal CI fixes. The string catalog keeps base's text and adds only this branch's keys. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CGPDFPageGetDrawingTransform only scales a page down, so a page smaller than the thumbnail drew at its natural size in white margins. Build the transform by hand and clip to the crop box. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The merge kept this branch's generated strings and dropped base's composer.connectSSH and composer.connectCloud. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # webviews/src/agent-session/acpmux/conversation/Markdown.tsx
# Conflicts: # webviews/src/agent-session/acpmux/conversation/ToolRow.tsx # webviews/src/agent-session/acpmux/conversation/conversation.css # webviews/src/agent-session/acpmux/conversation/transcript.gallery.ts # webviews/src/agent-session/acpmux/toolImages.ts # webviews/src/gallery/fixtures/toolImages.ts
…after the base merge Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 41ccb71. Configure here.
| return () => { | ||
| live = false; | ||
| }; | ||
| }, [near, loadable, src, failed, path]); |
There was a problem hiding this comment.
Player state ignores path changes
Low Severity
src, failed, and expanded are initialized from the first path and never reset when that prop changes. The load effect also returns early while src or failed is still set, so a reused ReplyMedia keeps playing or hiding the previous file after markdown or a tool row updates the path.
Reviewed by Cursor Bugbot for commit 41ccb71. Configure here.


Summary
A video or audio file an agent writes or links now plays inline in the chat. Before this, it showed only as a path chip.
media.load {src}with the path from the reply or tool output. The host checks it like a reply image: inside the session's folders, not on the deny list, a regular file, a known type, at most 4 GB. Video types are mp4, m4v, mov and webm; audio types are mp3, m4a, aac, wav and flac.cmux-agent://pane/__media/<token>.<ext>URL. The page never names a file itself.media-src 'self';connect-srcstays'none'.) use it. If the host refuses a file, it shows as a name card.Web media (CI artifact URLs, GitHub user-attachments) is refused for now; it follows in its own PR. With the
agent.pageHosttunable on, the page runs under the page host's CSP, which has nomedia-src, so video there falls back to the name card.Targets
feat-cmux-next(#18401 and #18357 landed as 7672e43). Part of bead cx-ncc.51.Testing
bun test src/agent-session/acpmux/chips/ReplyMedia.test.tsx(local): failed at the red commit because the module was missing; 3 pass after the fix.bun test src/agent-session/acpmux src/gallery: 1197 pass. The 4 failures (composer pickers, chat menu, palette Continue in, automation chat menu) also fail on cleanfeat-cmux-next.AgentPaneReplyMediaTestscover the grant (inside, outside, wrong type, web URL, same file same URL, unknown token), byte ranges (206 slice, open-ended, whole file, 416 past the end) and the params contract. Fleet Swift suite (step 18909ba90b44,run-swift-testing-suites.shat c24deb9): all three pass. The later merges changed no Swift.webviews/:bun test src/agent-session/acpmux/chips src/agent-session/acpmux/conversation test/gallery-coverage.test.ts test/pane-english.test.ts test/ui-rules.test.ts222 pass, 0 fail; the typecheck passes on all 1047 files. Fleet Swift suite on 41ccb71 (merged head): it compiles, andAgentPaneReplyImageTestsandAgentPaneReplyMediaTestspass. A later base merge (9506c26, with ci: run webviews tests per file with a timeout, on the 8 vCPU runner #18423) brought only unrelated Swift (terminal sizing, the context-menu export fix);react-apps-checkis green on 9506c26vp checkpasses on the changed files.verify-local.py --only swift-syntax --swift-changedpasses. The agent pane bundle is no longer committed (base dropped it); builds generate it.Changelog
Added: A video or audio file an agent writes or links plays inline in the agent chat, with an Expand view
Proof
Before (feat-cmux-next): a video a tool writes shows nothing under the call, and a reply link to one is only a path chip. Base gallery, the same transcript without the video variant:
/live/?entry=agent-pane.transcript#/agent-pane.transcript/tool-imagesAfter: variant
tool-videoplays a real 7 s terminal recording of a test run inline, muted on hover, with controls and Expand. Preview:/wt/feat-cmux-next-media-video/?entry=agent-pane.transcript#/agent-pane.transcript/tool-video🤖 Generated with Claude Code
Note
Medium Risk
New host-mediated file access and custom scheme byte-range serving touch WebKit security boundaries; path checks mirror images but large-file streaming and grant lifecycle add new attack surface to validate.
Overview
Adds inline video and audio playback for local files an agent links or tools write in the agent chat, instead of showing only path chips or treating
.mp4paths as images.The native host exposes a passive
media.load {src}bridge call (same path rules as reply images: in-session roots, allowed extensions, up to 4 GB). Approved files get opaquecmux-agent://pane/__media/<token>.<ext>URLs viaAgentPaneMediaGrants; the scheme handler serves them with HTTP byte ranges (206/416), rejects symlinks swapped after grant, and addslink.media_refused. Agent pane CSP gainsmedia-src 'self'; remote/https media is refused.The acpmux UI adds
ReplyMedia: lazymedia.loadnear the viewport, inline<video>/<audio>with expand dialog for video, wired fromReplyImage, tool output strips (toolImages+ToolRow), plus styles, i18n, gallerytool-video, and Swift/TS tests.Reviewed by Cursor Bugbot for commit 9506c26. Bugbot is set up for automated code reviews on this repo. Configure here.