mirror of
https://github.com/stablyai/orca.git
synced 2026-10-19 00:03:20 +00:00
fix(terminal): detach when recovery snapshot is unavailable
This commit is contained in:
@@ -1716,23 +1716,23 @@ export const TERMINAL_METHODS: RpcAnyMethod[] = [
|
||||
if (closed || streams.get(stream.streamId) !== stream) {
|
||||
return
|
||||
}
|
||||
const size = runtime.getTerminalSize(stream.ptyId)
|
||||
if (!serialized) {
|
||||
throw new Error('Remote terminal recovery snapshot unavailable.')
|
||||
}
|
||||
const displayMode = runtime.getMobileDisplayMode(stream.ptyId)
|
||||
// Why: dropped ACK-pending output breaks live replay; send a fresh snapshot before resuming output.
|
||||
// Why: clients discard truncated snapshots, so mark truncated only when serialization actually failed.
|
||||
sendSnapshotFrames((opcode, payload) => sendFrame(stream.streamId, opcode, payload), {
|
||||
kind: 'scrollback',
|
||||
cols: serialized?.cols ?? size?.cols ?? 80,
|
||||
rows: serialized?.rows ?? size?.rows ?? 24,
|
||||
cols: serialized.cols,
|
||||
rows: serialized.rows,
|
||||
displayMode,
|
||||
reason: 'ack-pending-overflow',
|
||||
seq: serialized?.seq,
|
||||
source: serialized?.source,
|
||||
truncated: !serialized,
|
||||
truncatedByByteBudget: serialized?.truncatedByByteBudget,
|
||||
data: serialized?.data ?? ''
|
||||
seq: serialized.seq,
|
||||
source: serialized.source,
|
||||
truncatedByByteBudget: serialized.truncatedByByteBudget,
|
||||
data: serialized.data
|
||||
})
|
||||
if (serialized && typeof serialized.seq === 'number') {
|
||||
if (typeof serialized.seq === 'number') {
|
||||
// Why: chunks queued before the snapshot serialized are already in it; replaying them would duplicate output.
|
||||
const snapshotSeq = serialized.seq
|
||||
const retained = stream.ackPendingOutput.filter(
|
||||
|
||||
@@ -1676,14 +1676,23 @@ describe('terminal multiplex RPC', () => {
|
||||
await dispatchPromise
|
||||
})
|
||||
|
||||
it('ends a stream when ACK overflow recovery serialization fails', async () => {
|
||||
it.each([
|
||||
{
|
||||
failure: 'throws',
|
||||
recover: () => Promise.reject(new Error('snapshot unavailable'))
|
||||
},
|
||||
{
|
||||
failure: 'returns no snapshot',
|
||||
recover: () => Promise.resolve(null)
|
||||
}
|
||||
])('ends a stream when ACK overflow recovery serialization $failure', async ({ recover }) => {
|
||||
const dataListenerRef: {
|
||||
current?: (data: string, meta?: { seq?: number; rawLength?: number }) => void
|
||||
} = {}
|
||||
const serializeTerminalBuffer = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ data: 'initial snapshot', cols: 120, rows: 40 })
|
||||
.mockRejectedValue(new Error('snapshot unavailable'))
|
||||
.mockImplementation(recover)
|
||||
const harness = startDesktopMultiplexSubscribe({
|
||||
serializeTerminalBuffer,
|
||||
subscribeToTerminalData: vi.fn(
|
||||
|
||||
Reference in New Issue
Block a user