fix(remote): accept the history marker glued to a row on tmux before 3.2

tmux before 3.2 prints no newline after `capture-pane -p` output, and
`-J` leaves the history's last row unterminated when the screen starts
inside a wrapped line. The marker row printed next is then glued onto
that row (`...first half<marker>\n31\n...`). The parser took the marker
only as a whole row, so it returned null and every load fell back to
the local capture while the screen stayed that way. Debian 11, Ubuntu
20.04 and RHEL 8 ship tmux 3.1c, 3.0a and 2.7.

The parser now takes the first `<marker>\n` wherever it ends a row (the
marker is random per read, so no row the pane holds ends with it) and
re-terminates a history that lacks its newline. A tail -c cut still
drops only the history's partial first row, now decided on the whole
output, so a cut inside the one row the marker is glued to leaves no
history instead of losing the marker. dropPartialFirstRow, no longer
called, is removed.

Docs and the findFrameInScreen JSDoc also say the splice does not place
the frame of an agent TUI mid-turn (a live region pinned to the bottom,
new rows inserted above it), which falls back to the history-over-frame
join.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
timkjrandClaude Opus 5.5 committed 2026-10-11 18:54:20 -05:00
1 parent 8e2594a274
commit 8d92e577f8
4 files changed
+88 -38

No files matched your search

+6
View File
@@ -732,6 +732,12 @@ function capturedRowText(row: string): string {
* above it matches. A row that changed between the two reads (a spinner, a
* clock) fails the true placement; the minimum keeps a lower one from being
* taken on a row or two that happen to be equal.
*
* An agent TUI mid-turn (Claude Code, codex, opencode) is not placed: it keeps
* a live region (spinner, composer, footer) pinned to the bottom and inserts new
* rows above it, so the frame is not a shifted copy of the screen and the
* comparison down to the screen's last content row fails. The caller then falls
* back to the plain join. A plain shell places.
*/
export function findFrameInScreen(screenRows: string[], frameRows: string[]): number {
const screen = screenRows.map(capturedRowText);