acpmux web: accept large image attachments
Images larger than 8 MiB can be attached in the acpmux dashboard without embedding the original in a single peer message. Originals upload in 512 KiB chunks and prompts carry upload references, so an image above the former roughly 11 MiB peer-message boundary retains its original bytes. Upload or prompt refusal leaves the text and original attachment available for retry; the composer clears only after acceptance.
Leo’s requirement: “Attachments take arbitrarily large files of any type; there is no size cap.”
Previews read a bounded header, request decoding at dimensions no larger than 1024 pixels per side, and show a bounded placeholder if dimensions or decoding fail. Preview bytes are independent of the original attachment.
Web upload capabilities are scoped to uploads created by authenticated Web connections. Existing local uploads cannot be accessed with their ids or tokens, and resolved links use separate read-only proofs. Remote asking-mode restrictions still apply. Pre-resolution failures cancel staged files; after resolution, stale cancellation cannot delete accepted originals. Files retained after a later refusal use the existing age sweep instead of risking deletion during a retry.
Validation:
- Behavioral JavaScript regressions failed before the fix in run 40952418617.
- Real peer/capability regressions failed before the transport fix in run 40954480333.
- At
3a9cbcb5e0969b36282bba35bc5bee1685ba14c4, run 40958581856 passed the behavioral JavaScript suite, 7 file-put tests, all 12 prompt-upload tests including original-byte verification across a real peer connection, and 13 remote-guard tests. An existing ambiguous-session error assertion failed; comparison with the same test on base is pending. - Run 40959957826 passed the remaining 11 upload resume/frame/peer/wrapper tests. Its subsequent base comparison could not start because the shallow snapshot lacked the base commit; that comparison is being rerun with an explicit fetch.
- Static pre-push checks pass. The broader CI guard job reports SwiftUI and sidebar-history failures, despite this PR changing no Swift or sidebar files; those require comparison before landing. Independent review remains pending.
- Browser before/after at
7dbbf69f, the real dashboard served byacpmux daemon runin headless Chromium, with a 10.4 MB PNG dropped on the composer (run 41004926273). Before (merge base): no attachment, toast "Image is larger than 8 MB". After: the image is attached with a bounded preview.
| Before | After |
|---|---|
![]() | ![]() |
- A dropped
prompt_acceptednotice no longer reopens a finished turn:node --test cmux-tui/crates/acpmux/tests/web_image_attachments.mjsfailed at5a0482e1(11/12,'running' !== 'ready') and passes at7dbbf69f(12/12).
The client changes, scoped transport, and peer regressions are together because the old inline transport cannot carry the originals accepted by the UI.
Observed: at 7dbbf69f, run 41013885526 passed the behavioral JavaScript suite (12/12), 7 file-put, 12 prompt-upload (real peer connection), 13 remote-guard, 2 remote-guard-peer, 5 upload-frame, 3 upload-resume and the web-image-attachments wrapper. Its one failure, remote_guard_modes::a_web_source_is_resolved_like_its_handler_and_refused_when_it_cannot_be ("dup" matches 2 sessions; use the id), fails with the same error at this PR's merge base 13531e30 (run 41014483079) and passes on current base 6c8bc645 (run 41014168865), where test-only commit 1a312f94 updated its expectation. It is a base red that the base merge clears.
Agent session: agent session

