fix(host): keep replayed terminal queries out of the shell's input #60
- IA
Fixes #48.
When a terminal is reattached, the host replays its scrollback, and xterm answers queries in that old output as if they were live. A replayed cursor-position query (`CSI 6n`) made xterm send `CSI row;col R` to the current shell, and that ended up in front of whatever was pasted next. This is where the `1R` prefix came from.
- **Host:** sends the scrollback as one frame marked `replay: true` (`TerminalFrame` in `apps/host/src/protocol.ts`), so the client knows where the replay ends. - **App:** while xterm is parsing a replay frame, the terminal panel drops what xterm's `onData` emits. xterm calls the write callback right after it parses that chunk and before any live output queued behind it. So the window closes exactly where the replay ends, and live queries are still answered. A counter tracks pending replays, so a stale replay still in xterm's write buffer during a quick reattach can't end the window early.
### Smoke test
I ran a real `bun ace serve` with an isolated `ACE_HOME` and a real zsh, connected through `GatewayClient`, and used `@xterm/headless@6.0.0` (the same core as the app's `@xterm/xterm@6.0.0`) with the panel's input logic copied in:
1. Client A opens a terminal and runs `printf '\033[6n'; read -rs -t 2 -d R x; echo LIVE:${#x}`. The live query is answered (`LIVE:5`). 2. Client A disconnects. Client B reattaches and the scrollback replays. Then it types `echo PASTED-OK`. - **Without the fix:** B sends `\e[5;1R` during the replay, and zsh prints `zsh: command not found: 1Recho`. This reproduces the issue. - **With the fix:** B sends nothing during the replay, and `PASTED-OK` prints. 3. After the replay, B runs the live query again. It is still answered (`LIVE-B:6`).
`bun types` and `bun run ci` pass. I didn't run this check again in the desktop app's webview.
### Built in Ace
Built in an Ace channel (agent with shell in a lane). The smoke test used `@xterm/headless`, installed temporarily in `/tmp` and not committed, because the agent can't type into the desktop app's terminal. It also used a throwaway `ace serve` on port 4199 instead of the running canary host.
iamnbutlermerged 2e4ce3444d6elast month on GitHub
iamnbutlerreferenced this in Land desktop inspection/actions and tab API from feat/github-cache #89last month on GitHub