Repository navigation
acpmux: a prompt names its files as uploads, of any size, by reference - #25730
Merged
teamleaderleo merged 12 commits intoOct 27, 2026
Merged
Conversation
…ference Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a
… daemon that runs the chat
A block with _meta.acpmux.upload {uploadId, fileName} gets the bytes of that
finished upload of its own chat (file_put_begin/chunk/finish) before anything
reads the prompt, so screenshots no longer have to ride inline in one frame
(Leo's 8 screenshots never reached acpmux). Advertised as promptUploads; a peer
without it is refused before anything is sent; a queue edit cannot name one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a
teamleaderleo
temporarily deployed
to
ci-routing-agent
October 26, 2026 22:50 — with
GitHub Actions
Inactive
Collaborator
Author
|
Owned by an agent session · session |
Collaborator
Author
|
Bead |
teamleaderleo
had a problem deploying
to
artifacts
October 26, 2026 23:07 — with
GitHub Actions
Failure
teamleaderleo
temporarily deployed
to
sentry-dsyms
October 26, 2026 23:07 — with
GitHub Actions
Inactive
… restored or held prompt reads its uploads at dispatch Review of #25730: resolution ran before the prompt-id ledger, so a resend after the age sweep failed with upload.missing instead of the first outcome; and a queued prompt restored from the log or a held agent message released went to run_prompt without resolution. Tests for the size caps and the swept resend. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a
teamleaderleo
temporarily deployed
to
ci-routing-agent
October 26, 2026 23:12 — with
GitHub Actions
Inactive
teamleaderleo
temporarily deployed
to
ci-routing-agent
October 26, 2026 23:12 — with
GitHub Actions
Inactive
… progress, refs, no caps) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a
…es to the agent by path Leo, 2026-10-26: "I ALREADY SAID WE NEED ARBITARRILY LARGE FILES OF ANY TYPE". The resumable upload loses its 1 GiB cap (only the disk bounds it), and a prompt's references lose the 32-file / 8 MiB / 32 MiB caps: an image goes inline while it fits the model's input and the prompt's one-message room, every other file (and a larger image) as a resource_link to its path, with a note listing every path. Tests: 50 screenshots and a 2 GB file of no known type reach a busy chat, accepted in under 1 s, bytes intact. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a
…d>; the contract says what exists, and how a page's bytes reach native Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a
…y the guard first Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a
Keeps this PR near 800 changed lines; the doc lands separately at the same path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a
teamleaderleo
temporarily deployed
to
ci-routing-agent
October 26, 2026 23:50 — with
GitHub Actions
Inactive
teamleaderleo
temporarily deployed
to
ci-routing-agent
October 26, 2026 23:51 — with
GitHub Actions
Inactive
…n; uploads read only through real folders and one link; the prompt stays one message Review fixes: - A resolved link keeps its path in _meta.acpmux.uploadPath, and a prompt that waited (queued, held, restored) is refused with upload.missing at its turn when that file is gone. - file_uploads::open_finished: the chat's and the upload's folders must be real folders, and the file must have one hard link. A read failure other than not found is upload.unreadable, not upload.missing. - Inline room is counted as base64 and against the rest of the prompt; the context note lists files while the message has room, and a prompt whose links alone pass one message is upload.tooMany. - The 2 GB test moves to the binary-frames PR; a 2 GB begin plus chunk and the pastSize refusal are checked here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a
teamleaderleo
temporarily deployed
to
ci-routing-agent
October 27, 2026 00:29 — with
GitHub Actions
Inactive
teamleaderleo
temporarily deployed
to
ci-routing-agent
October 27, 2026 00:30 — with
GitHub Actions
Inactive
…rson's files, and the sweep keeps uploads in use Review fixes: - A resource_link whose uri is acpmux-upload:<id> counts as a reference even without _meta.acpmux.upload, so it is refused as upload.invalid (and gated for an older peer) instead of reaching an agent unresolved. - A prompt under Web control that carries a link this daemon resolved (uploadPath) is refused with upload.remote at its turn: a web client's queue edit keeps the person's resolved blocks. - Resolving a reference, and starting a turn with its link, marks the upload's folder as used; the age sweep deletes only uploads made and last used before its cutoff. still_there checks only paths inside the uploads folder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a
|
Found 2 test failures on Blacksmith runners: Failures
|
Contributor
|
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)
web and size checks: green |
…n; upload messages never join another turn Review fixes: - The sweep decides on and deletes each upload under file_uploads::IN_USE, and resolving a reference or starting a turn with its link marks it used and checks it under the same lock. The last use is a .used marker file, written owner-only, so it works on every platform (no folder mtime). - A message that names or links uploads never joins another agent message's turn, and none joins it: its own turn reads and checks its files. - A resolved link under Web control is refused (unit test); the integration test that could pass without reaching that guard is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a
teamleaderleo
temporarily deployed
to
ci-routing-agent
October 27, 2026 00:49 — with
GitHub Actions
Inactive
teamleaderleo
temporarily deployed
to
ci-routing-agent
October 27, 2026 00:49 — with
GitHub Actions
Inactive
… never takes a file a turn needs Review fix: file_uploads::touch returns its error, and resolving a reference or starting a turn with its link refuses the prompt (upload.unreadable) when the .used marker cannot be written. Test: a folder in the marker's place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a
teamleaderleo
temporarily deployed
to
ci-routing-agent
October 27, 2026 01:13 — with
GitHub Actions
Inactive
teamleaderleo
temporarily deployed
to
ci-routing-agent
October 27, 2026 01:14 — with
GitHub Actions
Inactive
teamleaderleo
temporarily deployed
to
sentry-dsyms
October 27, 2026 01:38 — with
GitHub Actions
Inactive
teamleaderleo
temporarily deployed
to
artifacts
October 27, 2026 01:38 — with
GitHub Actions
Inactive
teamleaderleo
marked this pull request as ready for review
October 27, 2026 01:43
teamleaderleo
had a problem deploying
to
artifacts
October 27, 2026 01:44 — with
GitHub Actions
Failure
teamleaderleo
temporarily deployed
to
artifacts
October 27, 2026 01:44 — with
GitHub Actions
Inactive
teamleaderleo
added a commit
that referenced
this pull request
Oct 27, 2026
…ence, no caps) Split out of #25730 so that PR stays near 800 lines. Describes the daemon-facing upload API, the upload-ref block shapes, what the agent receives, the binary data-frame transport still to be built, and how bytes held by a page reach native. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a
Contributor
|
Shipped in:
|
teamleaderleo
added a commit
that referenced
this pull request
Oct 27, 2026
…ence) (#25774) * acpmux docs: the prompt-uploads contract (files of any size, by reference, no caps) Split out of #25730 so that PR stays near 800 lines. Describes the daemon-facing upload API, the upload-ref block shapes, what the agent receives, the binary data-frame transport still to be built, and how bytes held by a page reach native. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a * acpmux docs: prompt uploads split resume (uploadResume) from binary frames (uploadFrames); new refusal reasons Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a * acpmux docs: prompt uploads section 0 names the PRs that carry each part Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a * acpmux docs: prompt uploads: text after frames is ordered, begin forPrompt, filePutChunks on peers, status keeps an upload Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a * acpmux docs: prompt uploads 1b: uploads.begin carries promptId and ticket, data goes as file_put_chunk until uploadFrames, and the ticket stays page-to-native Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a * acpmux docs: prompt uploads 1b: upload states (progress never terminal, one terminal state per token, status, cancel, the pending id, a late ack), carriers, and the sweep reaps unfinished uploads Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a * acpmux docs: prompt uploads 1b: a token is one upload attempt; a failed attachment is retried under a new token Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a * acpmux docs: prompt uploads: cancel deletes a finished upload, and owned references (#25840) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a * acpmux docs: prompt uploads: files have no cap; one prompt's own references still fit one message Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a * acpmux prompt-uploads doc: every upload flag and its peer gate, the two corrupt finishes, the context note's "and N more" Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-1288bbb2b5d2f5c6b66a --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This branch was previously deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
The coordinator, relaying Leo, 2026-10-26: "Leo's message with 8 screenshots (about 6.6 MB) failed with "Not sent: no answer from acpmux". ... fix it so 20 screenshots send in seconds: attachments go as blobs or files referenced from the prompt, not inlined base64 in one frame."
Why the send failed (not the daemon)
Both the 8-screenshot send and its "text-only" Retry (prompt
3baa5aa2-…, 739 characters) died in the app before reaching acpmux:com.cmuxterm.app.next/page-host.chromium) showspage host binding payload refusedat 15:03:02.968 PDT (the first send) and at 15:10:34.992 PDT, 1 s after the Retry was sent (22:10:33.931Z). The Retry re-sends the failed prompt's attachments too (direct.tsfailedPromptskeeps{input, attachments, promptId}), so it was the same ~9 MB frame.ChromiumPageHostBridge.swift:137drops a page call over 4 MiB with only that log line and never answers, so the page'stransport.sendnever settles and the row fails at the 60 sdeliveryTiming.failMs.3baa5aa2in any session log, and in the same minute it took a 1-image prompt from the same pane in 31 ms. Its log shows no slow handler, lock or store stall at that time.sleep 150, a rawsession/promptgot_acpmux/prompt_acceptedin 1.4 ms (text steer), 93 ms (8 x 820 KB images inline, 9 MB) and 207 ms (20 images inline, 22 MB).So large prompts need to stop riding in one frame. Every acpmux hop also caps one message at
rpc::MAX_MESSAGE_BYTES(15 MiB), so 20 inline screenshots could not pass a peer link either.Leo, later: "why the FUCK SHOULD THERE BE A FUCKING SIZE CAP ANYWAYS JUST FUCKING / I ALREADY SAID WE NEED ARBITARRILY LARGE FILES OF ANY TYPE"
Change
A prompt block may name a finished upload of its own chat instead of carrying the bytes. The file is uploaded first with the existing resumable
_acpmux/file_put_begin/_chunk/_finish(chunks of at most 512 KiB, so no frame grows with the file). The prompt itself stays a few hundred bytes. The two ref shapes:{"type": "image", "mimeType": "image/png", "_meta": {"acpmux": {"upload": {"uploadId": ID, "fileName": NAME}}}}(nodata, nouri){"type": "resource_link", "uri": "acpmux-upload:ID", "name": NAME, "_meta": {"acpmux": {"upload": {"uploadId": ID, "fileName": NAME}}}}The daemon that runs the chat resolves them (
src/prompt_uploads.rs) before anything reads the prompt (server/requests.rssession/prompt), off the async runtime (spawn_blocking):imageblock, so transcripts, queue and ledger see what an inline sender would have sent.resource_linkwithuri: file://<path of the finished upload>, its name, MIME type (sniffed from the first bytes) and size. A text block (_meta.acpmux.context: true) lists the paths so the agent can open them.file_uploads::MAX_BYTESis gone from begin/chunk/finish; writing past the size declared at begin isfilePut.pastSize. The one-shotfile_putkeeps its old limit (it is one frame).Rules:
file_uploads::finished_pathandopen_finished: UUIDv7 id, plain file name, real folders, opened no-follow and owner-only, one hard link). Not there isupload.missing; there but unreadable isupload.unreadable. Aresource_linkwhoseuriis notacpmux-upload:<uploadId>(or that has the uri and no reference), animagewith auri, or aresourceblock isupload.invalid.upload.tooMany.upload.remote; it cannot upload either), and a person's waiting prompt that a web client edited is refused at its turn rather than handing their files to a turn the web client controls.upload.missingat its turn if a file it links to was swept meanwhile. Using an upload (a prompt naming it, a turn starting with it) writes a.usedmarker in its folder, under the same lock the sweep holds while it decides and deletes; the sweep removes only uploads made and last used before its cutoff, and a prompt whose marker cannot be written is refused (upload.unreadable). A message with uploads never joins another agent message's turn.attach.edit).hub.knows_prompt), and a queued or held prompt restored after a restart reads its uploads at dispatch (hub/turns.rs), refusing that one prompt if they are gone.promptUploadsininitialize. A chat on a peer that does not list it is refused (upload.unsupported) before anything is sent; otherwise the prompt is forwarded as it is and the peer resolves it from its own uploads.Not in this PR: binary upload frames,
file_put_statusresume, finishsha256and the disk-full reason (next PR, flaguploadFrames); model-sized copies per harness; the app sender. The contract doc is #25774. About 950 changed lines, about 500 of them tests.Risk: contained to prompts that carry
_meta.acpmux.upload, and to chunked uploads over 1 GiB that were refused before. Every other prompt takes the same path as before (prompt_uploads::anyis false).Tests
tests/prompt_uploads.rs(8 integration tests):fifty_uploaded_screenshots_reach_a_busy_chat_by_reference: 50 x 800 KiB PNGs uploaded in chunks, then named in one prompt to a chat mid-turn. No frame reaches 1 MiB and the prompt is accepted (queued) in under 1 s. When the turn ends the agent gets 12 inline and 38 by path, every one SHA-256 intact.a_large_image_and_any_other_file_go_by_path: a 6 MiB PNG and a binary file both arrive asresource_linkwith their paths, bytes intact.a_reference_outside_the_chats_finished_uploads_is_refused: another chat's upload, an unfinished upload, a traversal name, a bad id, a missing name, a mismatcheduri, aresourceblock, a web client, a queue edit; one prompt id sent twice runs once, and its resend after the uploads are swept still answers.an_upload_of_any_size_begins_and_takes_its_bytes: a 2 GB upload, past the old 1 GiB cap, begins and takes bytes; past its declared size isfilePut.pastSize.a_waiting_prompt_whose_upload_was_swept_is_refused_at_its_turn,an_upload_whose_use_cannot_be_marked_is_refused.a_peers_chat_reads_the_upload_on_the_peer,a_peer_that_cannot_read_uploads_is_refused_before_anything_is_sent.upload.remote.The 2 GB end-to-end test (streamed, SHA-256 at the agent's path, accept time, peak RSS) is in the binary-frames PR, #25793's follow-up, since it streams the way the app will.
Results (
cmux-ci test, 32-vCPU Linux):feat-cmux-nextplus the first tests,30a880d68cfe(run 40414404415): 4 failed. The agent gets an image with no bytes (image:e3b0c442…, the SHA-256 of nothing), the bad references run, the old peer is sent the prompt.588df63166ecplus this head's tests, run 40420600748): 3 failed. 50 references are refused, the 6 MiB image is refused, aresourceblock is accepted.filePut.tooLarge("a file is at most 1024 MiB").80a5e1e00930(run 40429351425):prompt_uploads8 passed,send_attach12,file_put7,durable_agents57, libfile_uploads/prompt_uploads/queue_edit/digest13,cargo clippy -p acpmux --all-targets -D warningsclean.Agent session: agent session
🤖 Generated with Claude Code