fix(desktop): send text to running terminals as a bracketed paste, not raw keystrokes

Merged
KI
2mo ago
+81-56

Summary

Sending a New page request to an existing agent left a mangled, unsubmitted prompt in the agent's input while the app toasted "Sent to agent".

useSendToTerminalAgent wrote the prompt through terminal.writeInput, which is a raw pty.write. The New page prompt is about 1.6 KB. Claude Code read the first ~1 KB pty chunk as a heuristic paste ([Pasted text #1 +12 lines]), typed the rest literally starting mid-word at s published after..., and took the trailing newline as a line break, so nothing was submitted. Newlines are not the trigger: a raw 1.6 KB single line fails the same way at the 1,024-byte pty read boundary. Shell line editors have no paste heuristic, which is why raw writes only break agent TUIs.

Every renderer path that puts text into a running terminal now goes through terminal.send, which frames the text as a bracketed paste when the program has that mode on, presses Enter in a separate delayed write, and serializes sends per session. It has shipped in host-service since desktop 1.18.0 and is what mobile, the CLI, MCP, agents.run continuation, and page comment delivery already use.

  • useSendToTerminalAgent (New page, diff comments, design mode) and PullRequestCodeTab, which bypassed the hook with its own raw call.
  • Preset "run in active terminal" (useV2PresetExecution).
  • Terminal rich input, which pasted through xterm and pressed Enter in the same burst, outside the host's send serialization. It now uses the shared hook, so a failed send toasts and restores the draft.
  • Removed the registry's raw writeInput and the WS transport's sendInput, which have no callers left.

Left alone on purpose: the Run button's stop writes Ctrl+C, a real keystroke. Launching a new session uses the agent's argv/stdin transport and was never affected.

Regression guard

Host-side framing was already covered in terminal.send-snapshot.node-test.ts. The gap was that nothing stopped a renderer caller from picking the raw channel. useSendToTerminalAgent.test.ts scans the renderer and fails if terminal.writeInput is called outside the one keystroke writer (useV2WorkspaceRun). It passes with this change and fails against the old hook, flagging useSendToTerminalAgent.ts:73.

Test plan

  • CDP, dev app, Claude Code v2.1.278 session in a local workspace, Pages menu > New page > send to the running session
    • Before: [Pasted text #1 +12 lines]s published after the command succeeds... sitting unsubmitted
    • After: full prompt from "Help me create..." to "...feedback loop is ready." submitted as one message, agent starts working
  • CDP, terminal rich input: 1.3 KB, 13-line prompt arrives as one submitted message, editor clears
  • CDP, sequential preset with a 1.5 KB command into the focused zsh tab: ran once, output file is byte-exact
  • pty level (tmux): the send shape runs single-line and multi-line commands correctly in zsh -i and bash; framed paste plus Enter submits on idle and mid-turn Claude Code
  • Guard test passes with the fix and fails with the hook reverted to main
  • bun run typecheck (desktop), Biome on changed files, renderer/lib/terminal and pageAgentPrompt tests
  • CDP, diff comment: 1.4 KB, 14-line comment arrives as In <file>:L2: ... in one submitted message
  • CDP, design mode on example.com: full one-line prompt arrives, including the trailing verify clause
  • CDP, PR code tab "Ask the AI" on this PR: 1.3 KB, 14-line comment arrives as In <file>:L9: ...; nothing posted to GitHub
  • CDP, sequential preset with a 1.5 KB line while an agent tab is focused: arrives whole and submitted (the raw path lost the first ~1 KB)
  • CDP, rich input into zsh: two-line command runs both lines; with an attachment, the file is uploaded and the prompt references its path
  • CDP, rich input on an ended session: toast "Couldn't send to agent", draft kept (the raw path dropped the text silently)
  • Hosts without terminal.send (older than 1.18.0) cannot open a workspace at all: MIN_HOST_SERVICE_VERSION is 1.21.0
  • Agents other than Claude Code v2.1.278 not tested (Codex was not signed in on the test profile); macOS only

Summary by CodeRabbit

  • Bug Fixes

    • Improved reliability when sending multiline prompts to terminal agents, preventing line breaks from being misinterpreted or dropped.
    • Pull request comment prompts now submit more consistently in existing terminals.
    • Terminal rich-input submissions and preset commands now use more reliable text submission.
  • Documentation

    • Updated prompt and diff-comment guidance to reflect current terminal submission behavior.
    • Clarified that design-mode prompts remain concise while additional context is provided through referenced files.