From 34d17d60010f23a7a2f5af45cece70b83a64875f Mon Sep 17 00:00:00 2001 From: Wooseong Kim Date: Thu, 1 Oct 2026 15:55:57 +0900 Subject: [PATCH 1/4] fix(runtime): clear a stale agent wait when the tail is a settled ready prompt A blocked stamp stayed after the dialog left, so a later send to an idle Codex composer was still refused. Clear it only when the tail is a settled ready prompt, and keep it while the tail is empty, inconclusive, or blocked. --- .../orca-runtime-tail-wait-memo.test.ts | 30 +++++++++++++++++- src/main/runtime/terminal-wait-tail-state.ts | 31 ++++++++++++++++++- .../terminal/output/pty-output-ingestion.ts | 10 ++++-- .../wait/terminal-wait-blocked-checks.ts | 31 ++++++++++++++----- 4 files changed, 89 insertions(+), 13 deletions(-) diff --git a/src/main/runtime/orca-runtime-tail-wait-memo.test.ts b/src/main/runtime/orca-runtime-tail-wait-memo.test.ts index 4ac28674dd8e..0665d21847a3 100644 --- a/src/main/runtime/orca-runtime-tail-wait-memo.test.ts +++ b/src/main/runtime/orca-runtime-tail-wait-memo.test.ts @@ -1,5 +1,9 @@ import { describe, expect, it, vi } from 'vitest' -import { computeTerminalTailWaitState } from './terminal-wait-tail-state' +import { + computeTerminalTailWaitState, + resolveWaitBlockedAt, + tailSettledReadyClearsBlockedWait +} from './terminal-wait-tail-state' describe('terminal tail wait state', () => { it('computeTerminalTailWaitState reports fromTail and blocked signals', () => { @@ -34,4 +38,28 @@ describe('terminal tail wait state', () => { lastIndexOf.mockRestore() } }) + + it('clears a blocked stamp only for a settled ready tail', () => { + const ready = { + waitText: + '╭───╮\n│ >_ openai codex (v0.157.0) │\n│ model: gpt-5 │\n│ directory: ~/repo │\n╰───╯', + signal: null, + fromTail: true + } + const blocked = { + waitText: 'Hooks need review\nPress enter to confirm', + signal: { reason: 'agent-hooks-review-prompt' as const, index: 0 }, + fromTail: true + } + const preview = { ...ready, fromTail: false } + const inconclusive = { ...ready, waitText: 'ordinary output' } + + expect(tailSettledReadyClearsBlockedWait(ready)).toBe(true) + expect(resolveWaitBlockedAt(12, false, ready, 99)).toBeNull() + expect(resolveWaitBlockedAt(12, true, ready, 99)).toBe(99) + expect(resolveWaitBlockedAt(12, false, blocked, 99)).toBe(12) + expect(resolveWaitBlockedAt(12, false, preview, 99)).toBe(12) + expect(resolveWaitBlockedAt(12, false, inconclusive, 99)).toBe(12) + expect(resolveWaitBlockedAt(null, false, ready, 99)).toBeNull() + }) }) diff --git a/src/main/runtime/terminal-wait-tail-state.ts b/src/main/runtime/terminal-wait-tail-state.ts index 28bb094b8e38..81dcb2cc375e 100644 --- a/src/main/runtime/terminal-wait-tail-state.ts +++ b/src/main/runtime/terminal-wait-tail-state.ts @@ -1,7 +1,10 @@ import type { RuntimeTerminalWaitBlockedReason } from '../../shared/runtime-types' import { buildTailLines } from './terminal-tail-state' import { tailMayContainBlockedSignal } from './terminal-tail-sentinel-index' -import { findActionableTerminalWaitBlockedSignal } from './terminal-wait-detection' +import { + findActionableTerminalWaitBlockedSignal, + isKnownReadyPromptSettled +} from './terminal-wait-detection' import { terminalWaitBlockedSentinelRe } from './agent-state-rules/blocked-text-layer' export function buildTerminalWaitText( @@ -95,3 +98,29 @@ export function tailGainedNewerBlockedReason( ) return appendCandidateSignal !== null && appendCandidateSignal.index > previous.signal.index } + +// Why: a blocked stamp is otherwise sticky, and a pane that has gone quiet never +// runs another check. Only a settled ready tail is positive evidence the dialog +// is gone. Empty fast-path text, a preview fallback, and a tail that still has +// a blocked signal are not — failing to gain a newer reason must not clear. +export function tailSettledReadyClearsBlockedWait(state: TerminalTailWaitState): boolean { + if (!state.fromTail || state.signal !== null || state.waitText.length === 0) { + return false + } + return isKnownReadyPromptSettled(state.waitText) +} + +export function resolveWaitBlockedAt( + current: number | null, + gainedNewerBlockedReason: boolean, + next: TerminalTailWaitState, + at: number +): number | null { + if (gainedNewerBlockedReason) { + return at + } + if (current !== null && tailSettledReadyClearsBlockedWait(next)) { + return null + } + return current +} diff --git a/src/main/runtime/terminal/output/pty-output-ingestion.ts b/src/main/runtime/terminal/output/pty-output-ingestion.ts index eb2a89c4fa5c..f6516c19e2f2 100644 --- a/src/main/runtime/terminal/output/pty-output-ingestion.ts +++ b/src/main/runtime/terminal/output/pty-output-ingestion.ts @@ -17,6 +17,7 @@ import { } from '../../terminal-tail-state' import { computeTerminalTailWaitState, + resolveWaitBlockedAt, tailGainedNewerBlockedReason } from '../../terminal-wait-tail-state' import { extractOscTitleScanTail } from '../../../../shared/osc-title-scan-tail' @@ -249,9 +250,12 @@ export abstract class OrcaRuntimeWithOnPtyData extends OrcaRuntimeWithPreparePty nextTail.partialLine, leaf.preview ) - if (tailGainedNewerBlockedReason(previousWaitState, nextWaitState, normalized.text)) { - leaf.waitBlockedAt = at - } + leaf.waitBlockedAt = resolveWaitBlockedAt( + leaf.waitBlockedAt, + tailGainedNewerBlockedReason(previousWaitState, nextWaitState, normalized.text), + nextWaitState, + at + ) leaf.tailWaitState = nextWaitState leaf.tailBuffer = nextTail.lines leaf.tailTranscriptBuffer = nextTranscript.lines diff --git a/src/main/runtime/terminal/wait/terminal-wait-blocked-checks.ts b/src/main/runtime/terminal/wait/terminal-wait-blocked-checks.ts index 4ec2165ea66e..51e989b3add0 100644 --- a/src/main/runtime/terminal/wait/terminal-wait-blocked-checks.ts +++ b/src/main/runtime/terminal/wait/terminal-wait-blocked-checks.ts @@ -13,6 +13,7 @@ import { } from '../../wait-blocked-check-state' import { computeTerminalTailWaitState, + resolveWaitBlockedAt, tailGainedNewerBlockedReason } from '../../terminal-wait-tail-state' import { ownRetainedString } from '../../../../shared/own-retained-string' @@ -90,16 +91,30 @@ export abstract class OrcaRuntimeWithScheduleWaitBlockedCheck extends OrcaRuntim signal: null, fromTail: false } - if ( - tailGainedNewerBlockedReason( - previousWaitState, - nextWaitState, - readWaitBlockedCarry(state.appended) - ) - ) { - pty.waitBlockedAt = at + const gainedNewerBlockedReason = tailGainedNewerBlockedReason( + previousWaitState, + nextWaitState, + readWaitBlockedCarry(state.appended) + ) + if (gainedNewerBlockedReason) { this.recordAgentPromptPermissionObservation(ptyId) } + const nextBlockedAt = resolveWaitBlockedAt( + pty.waitBlockedAt, + gainedNewerBlockedReason, + nextWaitState, + at + ) + // Why: the mirror copy in onPtyData already ran when this check was deferred, + // so a quiet pane would keep the leaf stamp after the pty one is cleared. + if (pty.waitBlockedAt !== null && nextBlockedAt === null) { + for (const leaf of this.getLeavesForPty(ptyId)) { + if (leaf.tailBuffer === pty.tailBuffer) { + leaf.waitBlockedAt = null + } + } + } + pty.waitBlockedAt = nextBlockedAt state.lastAt = at state.lastWaitState = nextWaitState resetWaitBlockedCarry(state.appended) From fde72691784df9243b26a8777d4eafb3536e78d1 Mon Sep 17 00:00:00 2001 From: Wooseong Kim Date: Thu, 1 Oct 2026 18:52:22 +0900 Subject: [PATCH 2/4] fix(runtime): skip a ready-tail scan when the wait stamp is already clear The deferred check still clears a leaf that shares the PTY tail, and that path is now tested without another output chunk. A null stamp no longer pays for the ready-prompt scan. Co-authored-by: Cursor --- .../orca-runtime-tail-wait-memo.test.ts | 14 +++++++++++++ src/main/runtime/terminal-wait-tail-state.ts | 20 +++++++++++++++++++ .../wait/terminal-wait-blocked-checks.ts | 14 ++++++------- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/main/runtime/orca-runtime-tail-wait-memo.test.ts b/src/main/runtime/orca-runtime-tail-wait-memo.test.ts index 0665d21847a3..3bc993aeb41e 100644 --- a/src/main/runtime/orca-runtime-tail-wait-memo.test.ts +++ b/src/main/runtime/orca-runtime-tail-wait-memo.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it, vi } from 'vitest' import { computeTerminalTailWaitState, + clearMirroredLeafWaitStamps, resolveWaitBlockedAt, tailSettledReadyClearsBlockedWait } from './terminal-wait-tail-state' @@ -62,4 +63,17 @@ describe('terminal tail wait state', () => { expect(resolveWaitBlockedAt(12, false, inconclusive, 99)).toBe(12) expect(resolveWaitBlockedAt(null, false, ready, 99)).toBeNull() }) + + it('clears deferred stamps only on leaves that share the PTY tail', () => { + const tail = ['ready tail'] + const matching = { tailBuffer: tail, waitBlockedAt: 4 } + const other = { tailBuffer: ['other tail'], waitBlockedAt: 4 } + + clearMirroredLeafWaitStamps(4, null, tail, [matching, other]) + + expect(matching.waitBlockedAt).toBeNull() + expect(other.waitBlockedAt).toBe(4) + clearMirroredLeafWaitStamps(null, null, tail, [other]) + expect(other.waitBlockedAt).toBe(4) + }) }) diff --git a/src/main/runtime/terminal-wait-tail-state.ts b/src/main/runtime/terminal-wait-tail-state.ts index 81dcb2cc375e..7dfaf5ea6613 100644 --- a/src/main/runtime/terminal-wait-tail-state.ts +++ b/src/main/runtime/terminal-wait-tail-state.ts @@ -119,8 +119,28 @@ export function resolveWaitBlockedAt( if (gainedNewerBlockedReason) { return at } + // A null stamp has nothing to clear. Skip the ready-prompt scan on that hot path. if (current !== null && tailSettledReadyClearsBlockedWait(next)) { return null } return current } + +/** The deferred check cleared the PTY stamp. Matching leaves share that tail and must drop its copy too. */ +export function clearMirroredLeafWaitStamps< + T extends { tailBuffer: unknown; waitBlockedAt: number | null } +>( + ptyWaitBlockedAt: number | null, + nextBlockedAt: number | null, + ptyTailBuffer: unknown, + leaves: readonly T[] +): void { + if (ptyWaitBlockedAt === null || nextBlockedAt !== null) { + return + } + for (const leaf of leaves) { + if (leaf.tailBuffer === ptyTailBuffer) { + leaf.waitBlockedAt = null + } + } +} diff --git a/src/main/runtime/terminal/wait/terminal-wait-blocked-checks.ts b/src/main/runtime/terminal/wait/terminal-wait-blocked-checks.ts index 51e989b3add0..80c8790d6a0d 100644 --- a/src/main/runtime/terminal/wait/terminal-wait-blocked-checks.ts +++ b/src/main/runtime/terminal/wait/terminal-wait-blocked-checks.ts @@ -12,6 +12,7 @@ import { type WaitBlockedCheckState } from '../../wait-blocked-check-state' import { + clearMirroredLeafWaitStamps, computeTerminalTailWaitState, resolveWaitBlockedAt, tailGainedNewerBlockedReason @@ -107,13 +108,12 @@ export abstract class OrcaRuntimeWithScheduleWaitBlockedCheck extends OrcaRuntim ) // Why: the mirror copy in onPtyData already ran when this check was deferred, // so a quiet pane would keep the leaf stamp after the pty one is cleared. - if (pty.waitBlockedAt !== null && nextBlockedAt === null) { - for (const leaf of this.getLeavesForPty(ptyId)) { - if (leaf.tailBuffer === pty.tailBuffer) { - leaf.waitBlockedAt = null - } - } - } + clearMirroredLeafWaitStamps( + pty.waitBlockedAt, + nextBlockedAt, + pty.tailBuffer, + this.getLeavesForPty(ptyId) + ) pty.waitBlockedAt = nextBlockedAt state.lastAt = at state.lastWaitState = nextWaitState From e7ca405831c7e32112319bbff54ac24a4b2c9385 Mon Sep 17 00:00:00 2001 From: Wooseong Kim Date: Sun, 4 Oct 2026 20:25:38 +0900 Subject: [PATCH 3/4] fix(runtime): check the current screen before refusing a send --- .../agent-prompt-submission-runtime.test.ts | 37 +++++++++++++++++++ .../construction/runtime-service-state.ts | 7 ++++ .../runtime-terminal-agent-status-query.ts | 9 ++++- .../terminal/wait/terminal-wait-permission.ts | 4 +- 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/main/runtime/agent-prompt-submission-runtime.test.ts b/src/main/runtime/agent-prompt-submission-runtime.test.ts index ab7515f31bc7..96e8faa5f821 100644 --- a/src/main/runtime/agent-prompt-submission-runtime.test.ts +++ b/src/main/runtime/agent-prompt-submission-runtime.test.ts @@ -159,6 +159,43 @@ describe('agent prompt submission runtime', () => { expect(writes).toEqual([]) }) + it('does not refuse a ready live screen because the retained tail has an old blocked prompt', async () => { + vi.useFakeTimers() + const { runtime, handle, writes } = await createAgentPromptSubmissionRuntime( + (runtime, data) => { + if (data.includes('\r')) { + runtime.onPtyData('pty-prompt', '\x1b]0;Codex working\x07', Date.now()) + } + }, + 'codex' + ) + runtime.onPtyData( + 'pty-prompt', + 'Hooks need review\nPress enter to confirm\n', + Date.now() + ) + const internals = runtime as unknown as { + headlessTerminals: Map }> + readLiveTerminalScreenLines: (ptyId: string) => string[] | null + } + await internals.headlessTerminals.get('pty-prompt')?.writeChain + vi.spyOn(internals, 'readLiveTerminalScreenLines').mockReturnValue([ + '╭───╮', + '│ >_ openai codex (v0.157.0) │', + '│ model: gpt-5 │', + '│ directory: ~/repo │', + '╰───╯' + ]) + + const submission = runtime.sendTerminalAgentPrompt(handle, 'review this', { + inputKind: 'driving' + }) + await vi.runAllTimersAsync() + + await expect(submission).resolves.toMatchObject({ accepted: true }) + expect(writes.some((data) => data.includes('review this'))).toBe(true) + }) + it('does not paste into a coalesced live permission title', async () => { const { runtime, handle, writes } = await createPromptRuntime(() => undefined) runtime.onPtyData( diff --git a/src/main/runtime/construction/runtime-service-state.ts b/src/main/runtime/construction/runtime-service-state.ts index 7066c212b718..4c0a11ff90e4 100644 --- a/src/main/runtime/construction/runtime-service-state.ts +++ b/src/main/runtime/construction/runtime-service-state.ts @@ -206,6 +206,13 @@ export abstract class OrcaRuntimeWithStopRequestedPtyIds extends OrcaRuntimeWith getTabTitle: (tabId) => this.tabs.get(tabId)?.title ?? null, getExplicitStatus: (handle) => this.getFreshExplicitAgentStatusForHandle(handle), getLifecycleStatus: (ptyId) => this.agentPromptLifecycleByPtyId.get(ptyId), + readLiveScreenLines: (ptyId) => { + const state = this.headlessTerminals.get(ptyId) + if (!state || state.outputSequence !== this.getPtyOutputSequence(ptyId)) { + return null + } + return this.readLiveTerminalScreenLines(ptyId) + }, isRunning: (handle) => this.isTerminalRunningAgent(handle), getTitleDisplayClear: (ptyId) => this.getPtyTitleDisplayClear(ptyId) }) diff --git a/src/main/runtime/runtime-terminal-agent-status-query.ts b/src/main/runtime/runtime-terminal-agent-status-query.ts index cc0c4cf1a62a..81d3d9179007 100644 --- a/src/main/runtime/runtime-terminal-agent-status-query.ts +++ b/src/main/runtime/runtime-terminal-agent-status-query.ts @@ -23,6 +23,8 @@ import { buildTerminalWaitText } from './terminal-wait-tail-state' export type RuntimeTerminalAgentStatusSnapshot = { waitText: string + /** Current whole emulator screen when it is caught up; null falls back to retained tail text. */ + liveScreenText: string | null waitBlockedAt: number | null title: string | null titleStatus: AgentStatus | null @@ -41,6 +43,7 @@ type Dependencies = { getLifecycleStatus( ptyId: string ): { status: AgentStatus | null; updatedAt: number } | null | undefined + readLiveScreenLines(ptyId: string): readonly string[] | null isRunning(handle: string): Promise getTitleDisplayClear(ptyId: string): TitleDisplayClear | null } @@ -75,7 +78,9 @@ export class RuntimeTerminalAgentStatusQuery { const terminal = this.getSnapshot(handle, ptyId, clear) const explicitStatus = this.deps.getExplicitStatus(handle) const lifecycle = getDisplayPromptLifecycle(this.deps.getLifecycleStatus(ptyId), clear) - const blockedByWaitText = detectTerminalWaitBlockedReason(terminal.waitText) + const blockedByWaitText = detectTerminalWaitBlockedReason( + terminal.liveScreenText ?? terminal.waitText + ) const liveTitleClearsBlockedText = terminal.titleStatusIsLive && terminal.titleStatus !== null && @@ -198,6 +203,7 @@ export class RuntimeTerminalAgentStatusQuery { ) return { waitText, + liveScreenText: this.deps.readLiveScreenLines(expectedPtyId)?.join('\n') ?? null, waitBlockedAt: pty.pty.waitBlockedAt, title: ptyTitle?.title ?? null, titleStatus: ptyTitle @@ -224,6 +230,7 @@ export class RuntimeTerminalAgentStatusQuery { ) return { waitText: buildTerminalWaitText(leaf.tailBuffer, leaf.tailPartialLine, leaf.preview), + liveScreenText: this.deps.readLiveScreenLines(expectedPtyId)?.join('\n') ?? null, waitBlockedAt: leaf.waitBlockedAt, title: title?.title ?? null, titleStatus: title ? detectAgentStatusFromTitle(title.title) : leaf.lastAgentStatus, diff --git a/src/main/runtime/terminal/wait/terminal-wait-permission.ts b/src/main/runtime/terminal/wait/terminal-wait-permission.ts index 411630180c24..18761cb48d33 100644 --- a/src/main/runtime/terminal/wait/terminal-wait-permission.ts +++ b/src/main/runtime/terminal/wait/terminal-wait-permission.ts @@ -125,7 +125,9 @@ export abstract class OrcaRuntimeWithResolveAuthoritativeTerminalWaitPermission lifecycle: { status: AgentStatus | null; updatedAt: number } | null | undefined, observedOnScreen = false ): RuntimeTerminalWaitBlockedReason | null { - const blockedByWaitText = detectTerminalWaitBlockedReason(terminal.waitText) + const blockedByWaitText = detectTerminalWaitBlockedReason( + terminal.liveScreenText ?? terminal.waitText + ) if (!blockedByWaitText) { return null } From 07d1f4206b11c8bee29c072cbbeed83fa1618596 Mon Sep 17 00:00:00 2001 From: Wooseong Kim Date: Mon, 5 Oct 2026 00:35:48 +0900 Subject: [PATCH 4/4] fix(runtime): inspect stamped tails for ready prompts --- .../orca-runtime-tail-wait-memo.test.ts | 22 +++++++++++++++++ src/main/runtime/terminal-wait-tail-state.ts | 24 +++++++++++++++---- .../terminal/output/pty-output-ingestion.ts | 3 ++- .../wait/terminal-wait-blocked-checks.ts | 3 ++- 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/main/runtime/orca-runtime-tail-wait-memo.test.ts b/src/main/runtime/orca-runtime-tail-wait-memo.test.ts index 3bc993aeb41e..aecf2ae28870 100644 --- a/src/main/runtime/orca-runtime-tail-wait-memo.test.ts +++ b/src/main/runtime/orca-runtime-tail-wait-memo.test.ts @@ -40,6 +40,28 @@ describe('terminal tail wait state', () => { } }) + it('reads a ready tail only when a blocked stamp needs settled-ready evidence', () => { + const lines = [ + '╭───╮', + '│ >_ openai codex (v0.157.0) │', + '│ model: gpt-5 │', + '│ directory: ~/repo │', + '╰───╯' + ] + + const unstamped = computeTerminalTailWaitState(lines, '', '') + expect(unstamped.fromTail).toBe(true) + expect(unstamped.waitText).toBe('') + + const stamped = computeTerminalTailWaitState(lines, '', '', true) + expect(stamped.fromTail).toBe(true) + expect(stamped.waitText).toBe('') + expect(stamped.signal).toBeNull() + expect(stamped.settledReadyPrompt).toBe(true) + expect(tailSettledReadyClearsBlockedWait(stamped)).toBe(true) + expect(resolveWaitBlockedAt(12, false, stamped, 99)).toBeNull() + }) + it('clears a blocked stamp only for a settled ready tail', () => { const ready = { waitText: diff --git a/src/main/runtime/terminal-wait-tail-state.ts b/src/main/runtime/terminal-wait-tail-state.ts index 7dfaf5ea6613..2e71ace9697d 100644 --- a/src/main/runtime/terminal-wait-tail-state.ts +++ b/src/main/runtime/terminal-wait-tail-state.ts @@ -23,6 +23,8 @@ export function buildTerminalWaitText( export type TerminalTailWaitState = { waitText: string signal: { reason: RuntimeTerminalWaitBlockedReason; index: number } | null + // Why optional: stamped checks record the settled-ready decision without retaining a rebuilt tail. + settledReadyPrompt?: boolean // Why: preview is only an empty-tail fallback, recomputed each append, so a preview-derived state can't be reused as the next previous state (gated on fromTail). fromTail: boolean } @@ -31,7 +33,8 @@ export type TerminalTailWaitState = { export function computeTerminalTailWaitState( lines: string[], partialLine: string, - preview: string + preview: string, + inspectTailForReadyPrompt = false ): TerminalTailWaitState { const tailInspection = inspectTerminalWaitTail(lines, partialLine) if (!tailInspection.fromTail) { @@ -41,7 +44,7 @@ export function computeTerminalTailWaitState( fromTail: false } } - if (!tailInspection.mayContainBlockedSignal) { + if (!tailInspection.mayContainBlockedSignal && !inspectTailForReadyPrompt) { // Why: reads waitText only when a signal exists; avoid retaining a rebuilt 256 KiB string in the common case. return { waitText: '', signal: null, fromTail: true } } @@ -51,9 +54,14 @@ export function computeTerminalTailWaitState( .join('\n') const fromTail = tailText.length > 0 const waitText = fromTail ? tailText : preview + const checkedReadyPrompt = inspectTailForReadyPrompt && !tailInspection.mayContainBlockedSignal return { - waitText, - signal: findActionableTerminalWaitBlockedSignal(waitText.toLowerCase()), + waitText: checkedReadyPrompt ? '' : waitText, + signal: tailInspection.mayContainBlockedSignal + ? findActionableTerminalWaitBlockedSignal(waitText.toLowerCase()) + : null, + settledReadyPrompt: + checkedReadyPrompt && fromTail ? isKnownReadyPromptSettled(waitText) : undefined, fromTail } } @@ -104,7 +112,13 @@ export function tailGainedNewerBlockedReason( // is gone. Empty fast-path text, a preview fallback, and a tail that still has // a blocked signal are not — failing to gain a newer reason must not clear. export function tailSettledReadyClearsBlockedWait(state: TerminalTailWaitState): boolean { - if (!state.fromTail || state.signal !== null || state.waitText.length === 0) { + if (!state.fromTail || state.signal !== null) { + return false + } + if (state.settledReadyPrompt === true) { + return true + } + if (state.waitText.length === 0) { return false } return isKnownReadyPromptSettled(state.waitText) diff --git a/src/main/runtime/terminal/output/pty-output-ingestion.ts b/src/main/runtime/terminal/output/pty-output-ingestion.ts index f6516c19e2f2..a7a70e9f847f 100644 --- a/src/main/runtime/terminal/output/pty-output-ingestion.ts +++ b/src/main/runtime/terminal/output/pty-output-ingestion.ts @@ -248,7 +248,8 @@ export abstract class OrcaRuntimeWithOnPtyData extends OrcaRuntimeWithPreparePty const nextWaitState = computeTerminalTailWaitState( nextTail.lines, nextTail.partialLine, - leaf.preview + leaf.preview, + leaf.waitBlockedAt !== null ) leaf.waitBlockedAt = resolveWaitBlockedAt( leaf.waitBlockedAt, diff --git a/src/main/runtime/terminal/wait/terminal-wait-blocked-checks.ts b/src/main/runtime/terminal/wait/terminal-wait-blocked-checks.ts index 80c8790d6a0d..23619c7e5904 100644 --- a/src/main/runtime/terminal/wait/terminal-wait-blocked-checks.ts +++ b/src/main/runtime/terminal/wait/terminal-wait-blocked-checks.ts @@ -85,7 +85,8 @@ export abstract class OrcaRuntimeWithScheduleWaitBlockedCheck extends OrcaRuntim const nextWaitState = computeTerminalTailWaitState( pty.tailBuffer, pty.tailPartialLine, - pty.preview + pty.preview, + pty.waitBlockedAt !== null ) const previousWaitState = state.lastWaitState ?? { waitText: '',