perf: skip no-op terminal side-effect drains

Skip empty terminal side-effect drains for ordinary PTY output while preserving title/status/BEL handling and stale working-title recovery.
This commit is contained in:
Neil 2026-05-30 12:35:53 -07:00 committed by GitHub
parent d349679d37
commit 9ee0d88b59
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 56 additions and 5 deletions

View File

@ -93,6 +93,26 @@ describe('createIpcPtyTransport', () => {
transport.disconnect()
})
it('does not schedule PTY side-effect drains for ordinary output with no working title', async () => {
vi.useFakeTimers()
try {
const { createPtyOutputProcessor } = await import('./pty-transport')
const onTitleChange = vi.fn()
const onBell = vi.fn()
const processor = createPtyOutputProcessor({ onTitleChange, onBell })
const callbacks = { onData: vi.fn() }
processor.processData('plain command output\r\n'.repeat(50), callbacks)
expect(callbacks.onData).toHaveBeenCalledTimes(1)
expect(vi.getTimerCount()).toBe(0)
expect(onTitleChange).not.toHaveBeenCalled()
expect(onBell).not.toHaveBeenCalled()
} finally {
vi.useRealTimers()
}
})
it('preserves stale-title detection after compacting deferred side effects', async () => {
vi.useFakeTimers()
try {

View File

@ -101,6 +101,7 @@ export function createPtyOutputProcessor({
let sideEffectDrainTimer: ReturnType<typeof setTimeout> | null = null
let pendingSideEffects: PendingPtySideEffect[] = []
let pendingSideEffectIndex = 0
let pendingWorkingTitleSideEffects = 0
const agentTracker =
onAgentBecameIdle || onAgentBecameWorking || onAgentExited
? createAgentStatusTracker(
@ -112,6 +113,20 @@ export function createPtyOutputProcessor({
)
: null
function isWorkingTitle(title: string | null): boolean {
return title !== null && detectAgentStatusFromTitle(title) === 'working'
}
function countWorkingTitles(titles: string[]): number {
let count = 0
for (const title of titles) {
if (isWorkingTitle(normalizeTerminalTitle(title))) {
count += 1
}
}
return count
}
function applyObservedTerminalTitle(title: string, suppressAgentTracker = false): void {
// Why: cursor-agent's native OSC title is the literal string "Cursor Agent"
// and it re-emits that title many times per turn (on every internal redraw)
@ -152,6 +167,7 @@ export function createPtyOutputProcessor({
}
function enqueuePtySideEffect(next: PendingPtySideEffect): void {
const workingTitleCount = countWorkingTitles(next.titles)
const prior = pendingSideEffects.at(-1)
if (
prior &&
@ -164,9 +180,11 @@ export function createPtyOutputProcessor({
!next.containsBell
) {
prior.scannedForTitles ||= next.scannedForTitles
pendingWorkingTitleSideEffects += workingTitleCount
return
}
pendingSideEffects.push(next)
pendingWorkingTitleSideEffects += workingTitleCount
}
function schedulePtySideEffects(
@ -174,14 +192,22 @@ export function createPtyOutputProcessor({
payloads: ReturnType<typeof processAgentStatusChunk>['payloads'],
suppressAttentionEvents: boolean
): void {
const scannedForTitles = Boolean(onTitleChange && data.length > 0)
const scannedForTitles = Boolean(onTitleChange && data.includes('\x1b]'))
const titles = scannedForTitles ? extractAllOscTitles(data) : []
const deliveredPayloads =
onAgentStatus && !suppressAttentionEvents && payloads.length > 0 ? payloads : []
const containsBell = Boolean(
onBell && !suppressAttentionEvents && bellDetector.chunkContainsBell(data)
)
if (!scannedForTitles && deliveredPayloads.length === 0 && !containsBell) {
const needsStaleTitleProbe = Boolean(
onTitleChange &&
data.length > 0 &&
titles.length === 0 &&
!suppressAttentionEvents &&
(isWorkingTitle(lastEmittedTitle) || pendingWorkingTitleSideEffects > 0)
)
const shouldEmitEmptyTitleScan = scannedForTitles || needsStaleTitleProbe
if (!shouldEmitEmptyTitleScan && deliveredPayloads.length === 0 && !containsBell) {
return
}
@ -192,7 +218,7 @@ export function createPtyOutputProcessor({
enqueuePtySideEffect({
payloads: [],
titles: [],
scannedForTitles,
scannedForTitles: shouldEmitEmptyTitleScan,
containsBell,
suppressAttentionEvents
})
@ -206,11 +232,11 @@ export function createPtyOutputProcessor({
suppressAttentionEvents
})
}
if (titles.length === 0 && scannedForTitles) {
if (titles.length === 0 && shouldEmitEmptyTitleScan) {
enqueuePtySideEffect({
payloads: [],
titles: [],
scannedForTitles: true,
scannedForTitles: shouldEmitEmptyTitleScan,
containsBell: false,
suppressAttentionEvents
})
@ -260,6 +286,10 @@ export function createPtyOutputProcessor({
}
function applyPtySideEffect(next: PendingPtySideEffect): void {
pendingWorkingTitleSideEffects -= countWorkingTitles(next.titles)
if (pendingWorkingTitleSideEffects < 0) {
pendingWorkingTitleSideEffects = 0
}
if (onAgentStatus) {
for (const payload of next.payloads) {
onAgentStatus(payload)
@ -367,6 +397,7 @@ export function createPtyOutputProcessor({
clearSideEffectDrainTimer()
pendingSideEffects.length = 0
pendingSideEffectIndex = 0
pendingWorkingTitleSideEffects = 0
clearStaleTitleTimer()
agentTracker?.reset()
bellDetector.reset()