Agent pane: a prompt or chat too large to send is never retried unchanged

Merged
TE
6d ago
+170-24

Leo, 2026-10-31: "this is completely hardlocked bc of message size". The pane showed "message too large to send: 16907300 bytes, the limit is 15728640" with "cmux tries again on its own" and Retry. A refusal for size is the same at every ask, so the pane retried forever.

acpmux refuses a message past rpc::MAX_MESSAGE_BYTES with reason: "message.too_large". With this PR the pane never asks again unchanged for that reason:

  • A failed send (sendCause → too-large): one sentence that names the attachment taking the most of the prompt and says to take it out with Edit or send it on its own (prompt.failure.tooLargeFile; prompt.failure.tooLarge when there is no attachment to name, such as a long paste). The card offers Edit, Remove and Details, not Retry, and neither does the message's context menu; chat.retryPrompt leaves such a row out from any path. The byte count stays under Details.
  • A chat open (the pane's handshake, connectHost): refused for that reason, it says "This chat is too large to open. Update acpmux, then Retry." at once, and the pane stops retrying on its own. The line's Retry still asks. #28754 makes acpmux never refuse an open that way; this covers a daemon from before it (a peer, or an older app).

Together with #28754 (the daemon side), a chat with large messages opens, and a prompt too large to send says what to do instead of wedging.

Screenshots

Gallery: agent-pane.transcript/failed-send-too-large, agent-pane.host-error/too-large, agent-pane.transcript/prompt-delivery

The gallery advisory renders these before and after (light and dark) in this PR's status comment.

Tests

Red first (8c61092cdba, tests only), then the fix:

bun test src/agent-session/acpmux/hostRetry.test.ts src/agent-session/acpmux/conversation/PromptDelivery.test.tsx

  • base + tests: 3 pass, 2 fail (a handshake refused as message.too_large…: no hostRefusedTooLarge; a prompt too large to send names its largest attachment…: generic "Not sent" sentence and a Retry button)
  • head: 14 pass, 0 fail; at 877aeb2 (after the review's menu finding): 17 pass, 0 fail with test/message-more-button.test.tsx

Review (codex gpt-6-astra): SHIP at 877aeb236ae15b48d30bbbbbbb227b8d122b5444 (its first pass at 47f042f6db0 blocked on the context menu's Retry, fixed in 877aeb2).

Visual review (codex gpt-6-astra): SHIP at 877aeb236ae15b48d30bbbbbbb227b8d122b5444. It opened the four after captures from the gallery advisory run (failed-send-too-large and host-error/too-large, dark and light). I viewed the after images too: the failed send names the attachment and offers Edit, Remove and Details with no Retry; at 360 px the host line cuts after "Update acpmux," beside its Retry button, with the whole line in its tooltip, like the other host lines.

Reds at 877aeb2: cmux-next checks fails only on its Tier routing step, in tests/test_cmux_next_base_tier.py (test_a_feat_cmux_next_push_defers_the_base_tier, test_a_push_whose_scheme_compile_is_the_dev_landing_build_keeps_it, test_coalescing_off_or_another_branch_runs_the_base_tier_on_the_push). The same three error the same way at the base tip d20e58e9359c, and this PR changes only webviews/. The gallery advisory's comment and mark-stale steps failed on GitHub's installation rate limit (HTTP 403), after capture passed.

Observed / Not run

  • Observed: the two runs above on this VM; tsc --noEmit clean on the changed files; check-xcstrings-keys.py passes; vp fmt --check clean on the changed files.
  • Not run: a fleet build of the pane with a real 16.9 MB refusal (the proof after both PRs land is a fleet NEXT build sending a 20 MB attachment to a local and a peer chat).

Localization: 3 new keys (prompt.failure.tooLarge, prompt.failure.tooLargeFile, host.tooLarge) in all 21 agent-pane locales, non-English marked needs_review like the catalog's others.

Changelog

Fixed: a message too large to send says which attachment to take out instead of retrying forever, and a chat too large to open no longer retries on its own.

Agent session: agent session

🤖 Generated with Claude Code