fix(terminal): resume output after recovery resync

This commit is contained in:
Jinjing
2026-07-22 20:09:25 -07:00
parent ffe54a59a3
commit 47edba34dd
2 changed files with 13 additions and 6 deletions
@@ -209,6 +209,9 @@ describe('remote terminal frame-drop resync', () => {
expect(server.droppedFrames).toBe(1)
// Instead, a fresh authoritative snapshot recovers the terminal.
expect(snapshots).toEqual(['INITIAL', '\x1b[2J\x1b[3J\x1b[HRECOVERED'])
server.output('ddd')
expect(data).toEqual(['aaa', 'ddd'])
})
it('passes contiguous output straight through without resyncing', async () => {
@@ -352,5 +355,8 @@ describe('remote terminal frame-drop resync', () => {
await expect(manualSnapshot).resolves.toMatchObject({ data: 'MANUAL' })
expect(server.snapshotRequests).toHaveLength(2)
expect(snapshots).toEqual(['INITIAL', '\x1b[2J\x1b[3J\x1b[HRECOVERED'])
server.output('ddd')
expect(data).toEqual(['aaa', 'ddd'])
})
})
@@ -605,15 +605,16 @@ class RemoteRuntimeTerminalMultiplexer {
clearPendingSnapshotRequest(stream)
}
clearSnapshot(stream)
// Why: the snapshot is the new authoritative output high-water; align the
// gap detector to it and re-open the live path (used by both the initial
// snapshot and a frame-drop resync, which reuses the 'initial' target).
if (target === 'initial') {
// Why: initial and recovery snapshots are authoritative output
// high-waters; align the gap detector and re-open the live path.
if (target === 'initial' || target === 'recovery') {
stream.expectedSeq = typeof info?.seq === 'number' ? info.seq : undefined
stream.resyncInFlight = false
stream.resyncPendingSend = false
stream.initialSnapshotReceived = true
stream.callbacks.onSubscribed?.()
if (target === 'initial') {
stream.initialSnapshotReceived = true
stream.callbacks.onSubscribed?.()
}
} else {
this.sendDeferredResyncSnapshot(stream)
}