perf(terminal): bound the reattach payload agent-signal scan (#12681)

hasCursorAgentReattachPayloadScreenSignal built a char-by-char copy of the
entire reattach payload so it could read the last header plus 5000 chars. On a
2MB daemon snapshot that cost 17.5ms of synchronous renderer main-thread work —
~75% of what xterm then spends parsing the same bytes — and the miss case paid
it in full for a result that is always false.

Two changes, both matching existing in-tree precedent: bound the scan to a
256KB tail (as the kitty tracker already bounds its own scan), and strip via
the shared precompiled CSI_SEQUENCE_PATTERN instead of a hand-rolled loop,
which is also faster in V8 because it copies spans rather than building a rope
per character.

  2MB snapshot, header hit   17.5ms -> 0.80ms  (22x)
  2MB snapshot, miss          8.7ms -> 0.52ms  (17x)
  200KB snapshot, header hit  1.5ms -> 0.62ms  (2.4x)

config/scripts/terminal-reattach-payload-scan-benchmark.mjs reproduces this and
asserts every candidate agrees with the baseline before timing it. It also
records a negative result: porting the daemon mouse mirror's includes()
pre-filter to the kitty tracker makes reattach slower, because snapshots always
contain the introducer.

Adds guards for the two behaviours a future shortcut would silently break: a
CSI-split header must still match, and a header behind the tail bound must not.
Also byte-pins POST_REPLAY_REATTACH_RESET_KEEP_MOUSE, which shipped unpinned.

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Neil
2026-08-06 00:23:57 -07:00
committed by GitHub
co-authored by Orca
parent 39ba201ed2
commit a7ffb244e4
4 changed files with 373 additions and 22 deletions
@@ -16427,6 +16427,81 @@ describe('connectPanePty', () => {
disposable.dispose()
})
// Why pinned: the classifier strips CSI precisely so a styled header still matches. A future
// "just lastIndexOf the raw bytes" shortcut would pass every other test and silently break this.
it('still detects the Cursor Agent screen when CSI styling splits the header and the marker', async () => {
const { connectPanePty } = await import('./pty-connection')
enableActiveRuntimeEnvironment()
const transport = createMockTransport('remote:env-1@@terminal-1')
const capturedReplayCallback: {
current: ((data: string, meta?: { clearBeforeReplay?: boolean }) => void) | null
} = { current: null }
transport.connect.mockImplementation(async ({ callbacks }: { callbacks: ConnectCallbacks }) => {
capturedReplayCallback.current = callbacks.onReplayData ?? null
return { id: 'remote:env-1@@terminal-1', replay: '' }
})
transportFactoryQueue.push(transport)
setReattachPaneTitle('renamed shell')
const pane = createPane(1)
const textarea = {} as HTMLTextAreaElement
configureTerminalFocusMode(pane, textarea)
const manager = createManager(1)
const deps = createDeps()
const disposable = await withMockedDocumentActiveElement(textarea, async () => {
const connection = connectPanePty(pane as never, manager as never, deps as never)
await flushAsyncTicks(6)
capturedReplayCallback.current?.(
'\x1b[4;3HCursor \x1b[1mAgent\x1b[0m\x1b[9;3H→\x1b[0m Plan, search, build anything'
)
await flushAsyncTicks(12)
expect(pane.terminal.write).toHaveBeenCalledWith(
POST_REPLAY_LIVE_AGENT_REATTACH_RESET,
expect.any(Function)
)
return connection
})
disposable.dispose()
})
// Why pinned: the scan reads only a bounded tail, so a header buried behind megabytes of
// scrollback describes a finished run, not the current screen.
it('ignores a Cursor Agent screen buried beyond the payload scan tail limit', async () => {
const { connectPanePty } = await import('./pty-connection')
enableActiveRuntimeEnvironment()
const transport = createMockTransport('remote:env-1@@terminal-1')
const capturedReplayCallback: {
current: ((data: string, meta?: { clearBeforeReplay?: boolean }) => void) | null
} = { current: null }
transport.connect.mockImplementation(async ({ callbacks }: { callbacks: ConnectCallbacks }) => {
capturedReplayCallback.current = callbacks.onReplayData ?? null
return { id: 'remote:env-1@@terminal-1', replay: '' }
})
transportFactoryQueue.push(transport)
setReattachPaneTitle('renamed shell')
const pane = createPane(1)
const textarea = {} as HTMLTextAreaElement
configureTerminalFocusMode(pane, textarea)
const manager = createManager(1)
const deps = createDeps()
const disposable = await withMockedDocumentActiveElement(textarea, async () => {
const connection = connectPanePty(pane as never, manager as never, deps as never)
await flushAsyncTicks(6)
capturedReplayCallback.current?.(
`${ANSI_POSITIONED_CURSOR_AGENT_REATTACH_SCREEN}${'shell scrollback\r\n'.repeat(20_000)}`
)
await flushAsyncTicks(12)
expect(transport.sendInput).not.toHaveBeenCalledWith('\x1b[I')
return connection
})
disposable.dispose()
})
it('downgrades a scrollback-only Cursor Agent signal when the parsed viewport shows a shell', async () => {
const { connectPanePty } = await import('./pty-connection')
enableActiveRuntimeEnvironment()
@@ -115,6 +115,7 @@ import {
queuePanePtyResizeIfHeld,
type PanePtyResizeHoldFlushDetail
} from '@/lib/pane-manager/pane-pty-resize-hold'
import { CSI_SEQUENCE_PATTERN } from '../../../../shared/ansi-escape-sequences'
import {
buildPostReplayLiveAgentReattachReset,
POST_REPLAY_LIVE_AGENT_SNAPSHOT_RESET,
@@ -469,33 +470,22 @@ function terminalOwnsDomFocus(terminal: TerminalWithFocusMode): boolean {
return document.activeElement === terminal.textarea
}
function stripAnsiCsiSequences(data: string): string {
let normalized = ''
let index = 0
while (index < data.length) {
if (data.charCodeAt(index) === 0x1b && data[index + 1] === '[') {
index += 2
while (index < data.length) {
const code = data.charCodeAt(index)
index += 1
if (code >= 0x40 && code <= 0x7e) {
break
}
}
continue
}
normalized += data[index]
index += 1
}
return normalized
}
const CURSOR_AGENT_REATTACH_HEADER = 'Cursor Agent'
const CURSOR_AGENT_REATTACH_INPUT_MARKER = '→'
const CURSOR_AGENT_REATTACH_SCREEN_SIGNAL_MAX_CHARS = 5000
// Why bounded: reattach payloads reach multiple MB, but every replay puts the current screen last
// and only the header nearest the end matters. 256KB clears even a fully SGR-styled frame by ~2x,
// so the cut only ever drops stale scrollback — which would have been rejected anyway.
const CURSOR_AGENT_REATTACH_SCAN_TAIL_LIMIT_CHARS = 256 * 1024
function hasCursorAgentReattachPayloadScreenSignal(data: string): boolean {
const normalized = stripAnsiCsiSequences(data)
const tail =
data.length > CURSOR_AGENT_REATTACH_SCAN_TAIL_LIMIT_CHARS
? data.slice(-CURSOR_AGENT_REATTACH_SCAN_TAIL_LIMIT_CHARS)
: data
// Why CSI only: OSC-carried titles must keep counting as a header occurrence, as they did when
// this stripped CSI by hand.
const normalized = tail.replace(CSI_SEQUENCE_PATTERN, '')
// Why: anchor on the LAST header occurrence — replay buffers keep scrollback,
// and an earlier finished run must not classify the current screen.
const headerIndex = normalized.lastIndexOf(CURSOR_AGENT_REATTACH_HEADER)
@@ -6,6 +6,7 @@ import {
POST_REPLAY_LIVE_SNAPSHOT_RESET,
POST_REPLAY_MODE_RESET,
POST_REPLAY_REATTACH_RESET,
POST_REPLAY_REATTACH_RESET_KEEP_MOUSE,
RESET_MOUSE_REPORTING,
buildPostReplayLiveAgentReattachReset,
replayPayloadEndsWithCursorHidden
@@ -33,6 +34,17 @@ describe('terminal mode reset profiles', () => {
expect(POST_REPLAY_REATTACH_RESET).not.toContain('\x1b[?2004l')
})
// Why ?1004l stays: #944 — a hard-killed TUI leaves the daemon emulator on the alternate buffer,
// so this profile can reach a plain shell, where armed focus reporting rings BEL on every pane
// switch. Dropping it would also make this byte-identical to the live-agent profile.
it('pins the live alternate-screen profile, which keeps mouse reporting but not focus', () => {
expect(POST_REPLAY_REATTACH_RESET_KEEP_MOUSE).toBe(
'\x1b[0 q\x1b[<99u\x1b[=0u\x1b[?25h\x1b[?1004l'
)
expect(POST_REPLAY_REATTACH_RESET_KEEP_MOUSE).not.toContain(RESET_MOUSE_REPORTING)
expect(POST_REPLAY_REATTACH_RESET_KEEP_MOUSE).not.toBe(POST_REPLAY_LIVE_AGENT_REATTACH_RESET)
})
// Why: #12101 — a cold-restored seed re-arms mouse reporting for a dead TUI.
it('disarms mouse reporting on the cold-restore seed', () => {
expect(COLD_RESTORE_SEED_MODE_RESET).toBe(RESET_MOUSE_REPORTING)