mirror of
https://github.com/stablyai/orca.git
synced 2026-10-26 08:02:44 +00:00
Fix terminal read for blank TUI tails (#5050)
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -3825,6 +3825,59 @@ export class OrcaRuntimeService {
|
||||
return rendererSnapshot ? { ...rendererSnapshot, source: 'renderer' } : null
|
||||
}
|
||||
|
||||
private async withVisibleSnapshotFallback(
|
||||
ptyId: string,
|
||||
read: RuntimeTerminalRead,
|
||||
opts: { cursor?: number; limit?: number } = {}
|
||||
): Promise<RuntimeTerminalRead> {
|
||||
if (!shouldFallbackToVisibleTerminalSnapshot(read, opts)) {
|
||||
return read
|
||||
}
|
||||
const lines = await this.readRendererVisibleSnapshotLines(ptyId)
|
||||
if (lines.length === 0) {
|
||||
return read
|
||||
}
|
||||
return buildVisibleSnapshotReadFallback(read, lines, opts.limit)
|
||||
}
|
||||
|
||||
private async readRendererVisibleSnapshotLines(ptyId: string): Promise<string[]> {
|
||||
const controller = this.ptyController
|
||||
if (!controller?.serializeBuffer) {
|
||||
return []
|
||||
}
|
||||
if (controller.hasRendererSerializer && !controller.hasRendererSerializer(ptyId)) {
|
||||
return []
|
||||
}
|
||||
try {
|
||||
// Why: raw PTY tails can be whitespace-only while a full-screen TUI is
|
||||
// visibly nonblank in renderer xterm. Ask the renderer for the active
|
||||
// screen instead of reusing the headless transcript path.
|
||||
const snapshot = await controller.serializeBuffer(ptyId, {
|
||||
scrollbackRows: 0,
|
||||
altScreenForcesZeroRows: false
|
||||
})
|
||||
if (!snapshot || snapshot.data.length === 0) {
|
||||
return []
|
||||
}
|
||||
const emulator = new HeadlessEmulator({
|
||||
cols: snapshot.cols,
|
||||
rows: snapshot.rows,
|
||||
scrollback: 0
|
||||
})
|
||||
try {
|
||||
await emulator.write(snapshot.data)
|
||||
return emulator
|
||||
.getVisibleLines()
|
||||
.map((line) => line.trimEnd())
|
||||
.filter((line) => line.trim().length > 0)
|
||||
} finally {
|
||||
emulator.dispose()
|
||||
}
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
private async serializeHeadlessTerminalBuffer(
|
||||
ptyId: string,
|
||||
opts: { scrollbackRows?: number; includeEmpty?: boolean } = {}
|
||||
@@ -5930,7 +5983,8 @@ export class OrcaRuntimeService {
|
||||
): Promise<RuntimeTerminalRead> {
|
||||
const pty = this.getLivePtyForHandle(handle)
|
||||
if (pty) {
|
||||
return this.readPtyTerminal(handle, pty.pty, opts)
|
||||
const read = this.readPtyTerminal(handle, pty.pty, opts)
|
||||
return this.withVisibleSnapshotFallback(pty.pty.ptyId, read, opts)
|
||||
}
|
||||
|
||||
const { leaf } = this.getLiveLeafForHandle(handle)
|
||||
@@ -5944,7 +5998,7 @@ export class OrcaRuntimeService {
|
||||
cursor: opts.cursor,
|
||||
limit: opts.limit
|
||||
})
|
||||
return read
|
||||
return leaf.ptyId ? this.withVisibleSnapshotFallback(leaf.ptyId, read, opts) : read
|
||||
}
|
||||
|
||||
async sendTerminal(
|
||||
@@ -15388,6 +15442,41 @@ function readTerminalTail(args: {
|
||||
}
|
||||
}
|
||||
|
||||
function shouldFallbackToVisibleTerminalSnapshot(
|
||||
read: RuntimeTerminalRead,
|
||||
opts: { cursor?: number; limit?: number }
|
||||
): boolean {
|
||||
if (typeof opts.cursor === 'number') {
|
||||
return false
|
||||
}
|
||||
if (read.tail.length === 0) {
|
||||
return false
|
||||
}
|
||||
const hasSubstantialBlankTail =
|
||||
read.limited === true || read.truncated || read.tail.length >= DEFAULT_TERMINAL_READ_LIMIT
|
||||
return hasSubstantialBlankTail && read.tail.every((line) => line.trim().length === 0)
|
||||
}
|
||||
|
||||
function buildVisibleSnapshotReadFallback(
|
||||
read: RuntimeTerminalRead,
|
||||
visibleLines: string[],
|
||||
limit: number | undefined
|
||||
): RuntimeTerminalRead {
|
||||
const lineLimit = terminalReadLimit(limit, DEFAULT_TERMINAL_READ_LIMIT)
|
||||
const lineBoundedTail = visibleLines.slice(-lineLimit)
|
||||
const charBoundedTail = trimTerminalPreviewToCharacterBudget(
|
||||
lineBoundedTail,
|
||||
MAX_TERMINAL_PREVIEW_CHARS
|
||||
)
|
||||
return {
|
||||
...read,
|
||||
tail: charBoundedTail.tail,
|
||||
limited:
|
||||
read.limited || lineBoundedTail.length < visibleLines.length || charBoundedTail.limited,
|
||||
returnedLineCount: charBoundedTail.tail.length
|
||||
}
|
||||
}
|
||||
|
||||
function getTerminalState(leaf: RuntimeLeafRecord): RuntimeTerminalState {
|
||||
if (leaf.connected) {
|
||||
return 'running'
|
||||
|
||||
Reference in New Issue
Block a user