Repository navigation
Keep recordings dropped from the macOS capture thumbnail - #24473
Conversation
…cording-preview-retention
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Not verified: macOS jobs did not run: compile and app tests were skipped on CI passes on Written by web and size checks: green |
|
Found 1 test failure on Blacksmith runners: Failure
|
|
Bead |
|
|
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
…cording-preview-retention
…cording-preview-retention
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
|
|
|
|
|
|
Orphaned: no activity for 68 h, and no lane holds a lease on it. Adopted by the orphan sweep: merged |
|
Review (codex gpt-6-astra xhigh, next-review): BLOCK at 5835c1c
|
…g-preview-retention
…ng drops Base removed the per-ask file count cap (#24652); the 100-name cap on a drop still bounds how many recordings one drop pins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-5b0be570f0b96d2d4c7b
|
Owned by an agent session · session |
…h scope Base's replyPathScope() (#24360's link.preview rework) builds its roots from roots() + addedRoots, no longer from fileOpenRoots(), which is where this PR adds the retained recording copies. After a chat adopted its first session, a drag of a retained recording was refused (firstSessionAdoptionPreservesPendingAndRetainedRecordings failed at c88db7f). The scope now carries the retained exact files too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-5b0be570f0b96d2d4c7b
|
Review (codex-review-box/gpt-6-astra high, lane-place): SHIP at cd1ce12 SHIP
|
|
The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/40978245548 at e346d50 failed: cmux-next checks (god files, concurrency, crash safety, l10n). |
|
Shipped in:
|
|
Shipped in:
|
…size (#28695) * NEXT: tests for Cloud and peer chat uploads of any size (red) Leo, 2026-10-26: "WHY THE FUCK SHOULD THERE BE A FUCKING SIZE CAP ANYWAYS ... I ALREADY SAID WE NEED ARBITARRILY LARGE FILES OF ANY TYPE". These fail on base, which caps Cloud uploads at 32 MiB a file and 64 MiB a batch and peer uploads at 1 GiB: - CloudUploadsTests: a staged Cloud begin past 32 MiB, and a batch past the 64 MiB total, are taken; no language of cloud.upload.failed or cloud.upload.refused carries a placeholder or a number. - AgentPeerUploadsTests: an inline peer upload past 1 GiB is sent; a staged peer begin past 1 GiB, alone or as a batch's first file, and a batch past the 1 GiB total, are taken. - AgentPaneFilePutTests: a chunked put past 1 GiB sends its begin. Sizes are only declared (Data(count:) or a declared size); no bytes are written. To test the Cloud begin without a live Cloud machine, its admission check moves, unchanged, into CloudUploads.beginRefusal(name:size:manifest:). The old test that the refusal names the limits is dropped. Split out of #27016 (Cloud and peer stager caps); the peer test drop and the "refusal names no limit" idea come from #27016. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3f783e95b72165fe52b9 * NEXT: Cloud and peer chat uploads take files of any size Leo, 2026-10-26: "WHY THE FUCK SHOULD THERE BE A FUCKING SIZE CAP ANYWAYS ... I ALREADY SAID WE NEED ARBITARRILY LARGE FILES OF ANY TYPE". Cloud (CloudUploads): drop maximumFileBytes (32 MiB) and maximumTotalBytes (64 MiB), the guard in upload(_:to:services:), and in a staged begin the per-file size check, the manifest's per-file and total checks and the per-session staged total. A begin still needs a non-negative size and a manifest that starts with the file begun. Peer (acpmux file_put): drop AgentPaneUpload.maximumFileBytes, maximumTotalBytes and maximumPeerFileBytes (1 GiB), the four guards in AgentPaneFilePut, the begin and manifest checks in AgentPeerUploads' stager and the guard in AgentPeerUploads.upload, and the base64 and total limits in AgentPaneUpload.files. A peer that refuses a file (filePut.tooLarge) still says so. The chunk bound in both stagers is written size - offset, so a huge declared size cannot overflow it. Localization: cloud.upload.refused named the limits ("up to %1$@ each, %2$@ in all") in every locale. There are no limits to name, so the key and CloudUploadStrings.refused are gone; a refusal from the machine shows cloud.upload.failed ("Couldn't send the file to the Cloud machine."), already translated in every locale, and keeps its upload_refused code for the page. Split out of #27016 (Cloud and peer stager caps). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3f783e95b72165fe52b9 * NEXT: tests for page-decoded uploads of any size reaching Cloud and peer (red) Review of #27073: the request decoder (AgentPaneRequest+Decoding) still refuses a staged begin, or a manifest entry, over 1 GiB, and a manifest of more than 64 files, before CloudUploads or AgentPeerUploads see it. The stager tests called the stagers directly and missed it. These fail on this head: - AgentPeerUploadsTests.aDecodedBeginOfAnySizeReachesThePeerStager and CloudUploadsTests.aDecodedBeginOfAnySizeReachesTheCloudStager (2 GiB and 8 GiB): a chat.upload.begin decoded through AgentPaneRequest(body:), the bridge's own decoding, alone, as a batch's first file, and as a small first file whose manifest names a file over 1 GiB, must decode into the staged begin and be taken by the peer stager and the Cloud stager's begin check (DecodedStagedBegins builds the requests). - AgentPaneUploadTests.stagedUploadAcceptsAnyNonnegativeFileSize and stagedUploadAccepts65LargeFiles, with stagedUploadRejectsNegativeFileAndBatchSizes, taken verbatim from #26409 so the two PRs' test hunks are identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3f783e95b72165fe52b9 * NEXT: the request decoder takes a staged upload of any size Leo, 2026-10-26: "WHY THE FUCK SHOULD THERE BE A FUCKING SIZE CAP ANYWAYS ... I ALREADY SAID WE NEED ARBITARRILY LARGE FILES OF ANY TYPE". AgentPaneRequest+Decoding: drop maximumUploadStageFile (1 GiB) from a chat.upload.begin's size and each manifest entry's size, and the 64-entry manifest limit. A begin still needs a file name and a non-negative size; a manifest still needs one or more well-formed entries. A chunk stays at most 512 KiB. This is #26409's change to the same file, line for line (its 3813876 and 951315e), so whichever of #26409 and #27073 lands second merges it cleanly. Cloud and peer begins do not pass through AgentChatUploads (CloudHandlers+Move tries Cloud, then peer, then the local stager), so the local stager's caps stay #26409's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3f783e95b72165fe52b9 * cmux link access: test a sparse upload over 1 GiB in bounded parts (red) `cmux link access upload` (the Cloud chat's finish step) still refuses a file over 1 GiB, so a file the stager took whole fails at the end with upload_refused. The test pastes a sparse 1 GiB + 1 byte file and uploads it to a fake fs-v1 machine: it must be accepted and arrive whole, in parts of at most 8 MiB (overwrite, then appends). Leo: "Attachments take arbitrarily large files of any type; there is no size cap". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3f783e95b72165fe52b9 * cmux link access: an upload takes a file of any size `cmux link access upload` refused any file over 1 GiB (MAX_FILE_BYTES), so a Cloud chat upload the stager took whole still failed at finish with upload_refused. The cap is gone. The file was already streamed: copy_file hands an open file to Remote::write, which reads and sends it in 8 MiB fs.write parts (overwrite, then appends to a temporary name, then a rename), so nothing reads the whole file into memory. The uploads.rs change is taken byte-identical from #27016 (launch-polish), so the two merge cleanly; credit to that PR. Leo: "Attachments take arbitrarily large files of any type; there is no size cap". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-3f783e95b72165fe52b9 * Keep staged uploads cancellable through transfer Agent-Session: https://cmux.com/deeplink/agent?id=aid-330ba9006c9e47d455e5 * Keep peer cancellation test on main actor Agent-Session: https://cmux.com/deeplink/agent?id=aid-330ba9006c9e47d455e5 * Allow staged cancellation test to overlap finish Agent-Session: https://cmux.com/deeplink/agent?id=aid-330ba9006c9e47d455e5 * Keep peer transfer task with its stage Agent-Session: https://cmux.com/deeplink/agent?id=aid-330ba9006c9e47d455e5 * Cancel active staged uploads through terminal cleanup Agent-Session: https://cmux.com/deeplink/agent?id=aid-330ba9006c9e47d455e5 * Retain batch authorization through upload cleanup Agent-Session: https://cmux.com/deeplink/agent?id=aid-330ba9006c9e47d455e5 * Stop stage expiry while terminal cleanup owns it Agent-Session: https://cmux.com/deeplink/agent?id=aid-330ba9006c9e47d455e5 * Keep active uploads bounded by idle progress Agent-Session: https://cmux.com/deeplink/agent?id=aid-330ba9006c9e47d455e5 * Bound upload reply and diagnostic capture Agent-Session: https://cmux.com/deeplink/agent?id=aid-330ba9006c9e47d455e5 * Fix upload regression test isolation and macro mutation Agent-Session: https://cmux.com/deeplink/agent?id=aid-b14e80c6998b6f900360 * Attachment cap guard: an exemption names only a cap its file still declares (red) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-68ba07270dde07b96ff4 * Attachment cap guard: drop the exemptions of caps that are gone Cloud and peer uploads no longer declare their file and batch caps, nor the CLI its MAX_FILE_BYTES, so their exemptions go; AgentPaneDraftAttachments' maximumBytes was already gone on base. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-68ba07270dde07b96ff4 * NEXT: a pinned recording keeps its own in-memory budget; stages name every field Base's #24473 read AgentPaneUpload.maximumFileBytes and maximumTotalBytes for the macOS capture-thumbnail recording snapshot, which this branch removes, so the merged tree did not compile. The snapshot reads the recording into memory before saving it, so it keeps the same 1 GiB as its own named budget (AgentPaneRecordingSnapshot.maximumPinnedBytes / maximumPinnedTotalBytes). The Cloud and peer Stage initializers name expiry and cleanup explicitly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-68ba07270dde07b96ff4 * NEXT: Cloud and peer staged uploads share one store in CmuxNextAgentPane The two App stagers each kept their own stages, batches, expiry deadlines and cleanup tasks (+131 CmuxNextApp lines, which the arch ratchet refuses). AgentPaneStagedUploads<Target> now owns that once: a stage of any size, a batch authorized while its stages live, one DemandTimer for abandoned stages and batches (the peer stager no longer sleeps a task per stage), a transfer that owns its file until it returns, and a cancel that stops the transfer before the staged file is removed. Each App stager keeps only its own target checks, file layout and transfer. AgentPaneStagedUploadsTests covers begin of any size, batch order, chunk ownership, and cancel during the transfer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-68ba07270dde07b96ff4 * NEXT: Cloud uploads go one file per call; a late cancel takes a delivered file back Review of the carry (Astra, BLOCK at 6ff0fa8): - The older one-frame upload sent every file in one CLI call, under no upload id: more than 64 files were refused, and a failed or cancelled call left its partial file and the files already sent on the machine. CloudAccessCLI.upload now sends each file in its own call under an id of the app's, and a failure or a cancel removes every file of the upload (removeUploads) before it answers. - A cancel that came after the transfer delivered the file (the chip's x while the answer was on its way) found no stage and left the file on the machine. AgentPaneStagedUploads keeps a finished stage's undo for its lifetime; the Cloud stager's undo removes that upload id. Peers have no remote delete yet. - CloudBringBackTests expects the upload id on the CLI call (the original already sent it for one file). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-68ba07270dde07b96ff4 * NEXT: a Cloud upload's cleanup tries every file; the undo test compares before and after Review of the carry (Astra, BLOCK at f6edd0e): removeUploads stopped at the first failed removal, leaving later files on the machine; it now tries every id and throws after. The late-cancel test expected no undo at all on its second file, though the first had rightly been undone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-68ba07270dde07b96ff4 * NEXT: removeUploads' doc says once that every id is tried Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-68ba07270dde07b96ff4 * NEXT: intake's batch upload call keeps one formatter-stable line Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-68ba07270dde07b96ff4 * NEXT: the staged-upload undo test logs on a main-actor class; package-only imports The fleet test compile refused the test's undo closure that appended to a captured var (converting non-Sendable function value). AgentPaneStagedUploads is package, so its imports need not be public. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-68ba07270dde07b96ff4 * fix(attachments): drop the stale MAX_FILE_BYTES cap exemption Base removed MAX_FILE_BYTES from access_cli/uploads.rs, so the guard's exemption named a cap that no longer exists and tests/test_attachment_caps_guard.py failed (1 of 19). python3 -I tests/test_attachment_caps_guard.py: 19 OK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-63b9964940c20354b639 * test(upload): a part in flight reports progress before its answer (red) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-63b9964940c20354b639 * fix(upload): report progress while a part is in flight; idle outlasts the socket deadline Codex BLOCK on #28695: the 30 s upload idle only heard a heartbeat after a whole 8 MiB fs.write returned, so a healthy slow link was killed. The Rust Remote now writes a request line in 256 KiB slices and reports progress after each slice and on each answer; the app's upload idle is 90 s, longer than the CLI's 60 s per-read/write socket deadline (IO_DEADLINE). No size cap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-63b9964940c20354b639 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Summary
Dragging a recording from macOS's floating capture thumbnail could leave chat with a link to a file that macOS immediately deleted. Pin the dropped recording until the existing chat upload owner saves it, then return that saved path. The pane grants access to that exact saved file so its existing open, save, and drag actions continue to work.
Pending saves and retained grants belong to that chat. A real chat switch invalidates in-flight work; a new chat receiving its first session ID keeps its attachments.
Testing
86c0f7358bb1b5c47fbfb1b5passed all 26 tests inAgentPaneRecordingDropTests,AgentPaneAttachDroppedTests,AgentPaneMediaDragTests, andAgentChatUploadsTestson exact heade6541632699d852a1a2402eb44590dc069fbfb30.ad697b8d66da0fb1b797c1d65ba0d56e2a8228ffcompiled and reproduced the deleted-path failures in managed step7feb0909fcde93461dd553ae.7a590a7929dc6767d2d84a85581cbaee1d8ca45ecompiled and reproduced both the late-save cross-chat leak and first-session attachment loss in step2c010db370b582de11e02c57.Observed / Not run
Observed deletion of the original recording before the request, retained bytes after deletion, subsequent media drag, exact-file access without sibling access, expiry, mismatched names, oversized/missing previews, refused saves, and session adoption during an awaited save in native tests. Independent and primary source reviews passed; required CI is still pending. No live floating-thumbnail drag was performed in an installed app. Visual recording previews are a separate change.
Changelog
Fixed: Recordings dragged from macOS's floating capture thumbnail remain available after the thumbnail closes.
Proof
No capture: this change preserves dropped file bytes and exact-file access without changing the attachment's appearance. Native lifecycle and file-boundary regression tests cover the behavior.
Agent session: cosmic-gray