cmux NEXT: pasted screenshots in a chat's composer come back after a relaunch

Merged
TE
16d ago
+214-20

Verified: an independent review passed. On the fleet, AgentPaneRelaunchWakeTests passed 6/6 at this head plus #23018's one-line base fix (step 0fef2bd075ab20ecb5a59685). Red here: defaults-snapshot, and swift test, which fails building AgentPaneProjectRepoTests.swift. Both are base reds; #23018 fixed the second.

Leo pasted 4 screenshots into a chat's composer and quit. After the relaunch the draft's text came back, but the screenshots were gone.

Cause. #22790 saves what a chat's composer holds at quit in agent-pane-wake-states.json. Each chip's contents went into that JSON as base64, and the file has a 4 MB cap per tab. Four Retina screenshots (8 MB or more of base64) go over it, and then the tab's whole composer was dropped without any error. The draft's text survived because acpmux stores it separately.

Fix (AgentPaneRelaunchWake):

  • At quit, each attachment's bytes are written as a file of their own (.png, .jpg, .pdf, …) to agent-pane-wake-attachments/ in the tag's state directory, next to the JSON. The JSON keeps only metadata and file names, so it stays a few KB.
  • At launch, the files are read back into the chips, which come back with the handshake's wake as before (thumbnails included). Then the JSON and the folder are both removed. A chip that is sent or removed later in that run is never brought back.
  • Each save first clears the folder. A tab that doesn't fit leaves no files behind, and a quit with nothing held leaves neither the JSON nor the folder.
  • The cap is now 128 MB for attachment files across all tabs. A full composer of images is at most 50 MB. Tabs are kept smallest first, as before.
  • A link chip now keeps its path. Before, it came back without one.
  • A JSON file written by the last build (contents inline) still restores.

Tests (AgentPaneRelaunchWakeTests):

  • pastedScreenshotsComeBackAfterARelaunch: four 1.5 MB screenshots plus a link chip are saved through the quit path. The test checks that the JSON is under 16 KB, that four .png copies with the same bytes are on disk, and that the next launch's first-page handshake hands back all four with their bytes and the link's path. It then checks that the files are gone after the launch, and that a later save with nothing held leaves nothing on disk.
  • attachmentsPastTheCapLeaveNoFilesAndOldSavesStillRestore: a tab that doesn't fit the cap leaves none of its files, and a save in the old format still restores.

Not covered: a crash or force-kill still loses chips. Only the quit path (Cmd-Q, Restart to Update, SIGTERM) saves them, as before.

Verification: prepush gate passes. Swift tests run on the fleet and in CI, not locally (no builds on this Mac).

🤖 Generated with Claude Code

Agent session: cosmic-gray


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes pasted screenshots in a chat's composer surviving a relaunch. The wake-state JSON used to hold attachment bytes as base64, and four Retina screenshots exceeded the 4 MB per-tab cap, so the tab's whole composer was dropped and only the draft's text came back. Attachments now save as their own files beside the JSON, which keeps only metadata and file names. A crash or force-kill still loses chips; only the quit path saves them.

Bug Fixes

  • The attachment files share a 128 MB cap across all tabs; a tab over the cap leaves no files behind.
  • Launch reads the files back into the chips and removes both the JSON and the folder, so a chip sent or removed later in the run is never restored.
  • Link chips now keep their path; saves from the previous build (contents inline) still restore.