Repository navigation
fix(claude): confirm inlined pastes on the runtime delivery path (remote send bug) - #498
Merged
Merged
Conversation
…ote send)
The never-ending "remote/mobile send doesn't work" bug. Claude's TUI reflects a
committed bracketed paste two ways depending on size: it COLLAPSES big pastes
into a `[Pasted text #N]` placeholder (single-line >~800 chars, multiline >=~4
lines) but INLINES everything smaller with no placeholder at all. The runtime
delivery path (deliverClaudePrompt) waited for the placeholder ONLY
(awaitPastePlaceholder), so every short/inlined paste timed out unconfirmed
("did not confirm pasted prompt before submit") and the message stuck in the
composer, unsent. That hits every short remote prompt — and EVERY dictated
prompt, since the <stt>…</stt> wrapper adds newlines and forces the paste
route.
Root cause is drift: the desktop composer already confirmed on placeholder OR
inline-tail (claudePaste.ts waitForPasteAbsorbed), but that robust detector was
never ported to the runtime path. Fix consolidates the detection into a shared
module (@shared/claude/pasteConfirm) used by BOTH paths, so it can't drift
again:
- new placeholder count increase OR the paste's normalized 24-char tail
newly appearing, against a baseline captured BEFORE the paste (the baseline
also makes a SECOND paste immune to the first's stale [Pasted text #1]).
- runtime reads the live screen via a new AgentSession.snapshotScreen (Claude
already implemented it as snapshotScreen()); app-only, no submodule change.
- claudePaste.ts now imports the shared detector (byte-identical logic; the
battle-scarred timing constants are untouched); the duplicated
CLAUDE_PASTE_THRESHOLD / isPasteLike collapse to one source.
Characterized and verified against a real `claude` PTY (harness in
tmp/paste-repro): short single-line, short multiline, and dictated <stt>
prompts all now confirm via the inline signal in ~11-24ms (were 2000ms
timeouts); big pastes still confirm via placeholder. Unit test updated to the
snapshotScreen mechanism and pins the inline-tail path + the stale-placeholder
sequence guard + Enter-withheld-on-timeout.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Juliusolsson05
added a commit
that referenced
this pull request
Jul 9, 2026
…ack + #498 paste fix) One conflict, SessionView.tsx imports: union of this branch's chrome imports (PaneHeader/ComposerInput/useComposerAutoGrow) with main's post-#493 paths — useLedgerFeedItems now lives in features/feed/ledger, the runtime view types against RuntimeRenderInput from session-runtime/state (the SessionRuntime cast died in #502), and NO_GHOSTS is ReadonlyMap<string, GhostEntry>. The file body auto-merged main's honestly-typed runtimeView (semantic: transcript.semantic) under this branch's chrome unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Fixes the never-ending "remote/mobile send doesn't work" bug (
Claude session … did not confirm pasted prompt before submit (timeout)), root-caused with a real-claudePTY harness rather than guessed.Root cause (verified, not assumed)
Claude's TUI reflects a committed bracketed paste two ways, by size:
[Pasted text #N]placeholder — only big pastes (single-line >800 chars, multiline ≥4 lines).The runtime delivery path (
deliverClaudePrompt) waited for the placeholder only (awaitPastePlaceholder). So every short/inlined paste timed out at 2000ms, Enter was withheld, and the prompt stuck in the composer. This hits every short remote prompt — and every dictated prompt, since the<stt>…</stt>wrapper adds newlines and forces the paste route.It's a drift bug: the desktop composer already confirmed on placeholder OR inline-tail (
claudePaste.ts), but that robust detector was never ported to the runtime path.Measured against real
claude(harness intmp/paste-repro/):<stt>shortThe fix
@shared/claude/pasteConfirm— placeholder-count-increase or the paste's normalized 24-char tail newly appearing, against a baseline captured before the paste (the baseline also makes a second paste immune to the first's stale[Pasted text #1]).deliverClaudePromptreads the live screen via a newAgentSession.snapshotScreen(Claude already implements it) — app-only, no submodule change.claudePaste.tsnow imports the shared detector (byte-identical logic; the battle-scarred timing constants untouched). DuplicatedCLAUDE_PASTE_THRESHOLD/isPasteLikecollapse to one source, so the two paths can't drift again — which is exactly why this bug existed.Verification
tsc -b tsconfig.node.jsonclean,tsc --noEmit -p tsconfig.web.jsonclean.promptDelivery.test.tsupdated to thesnapshotScreenmechanism: 6/6 pass, pinning the inline-tail path, the stale-placeholder sequence guard, and Enter-withheld-on-timeout.claude: the actual sharedpollPasteAbsorbedresolvesabsorbedfor every dead-zone case (short single-line, short multiline, dictated<stt>) — the exact prompts that used to hang.Independent of PR #497 (mobile UI); branched off clean
main.🤖 Generated with Claude Code