mirror of
https://github.com/stablyai/orca.git
synced 2026-10-17 00:03:08 +00:00
fix(agents): widen echo window to cover agent cold boot
Readiness can fire on the shell's bracketed-paste handshake while the agent binary is still booting; the paste sits in the TTY buffer and renders only when the TUI drains stdin, several seconds later. Live-validated: claude cold boot rendered the buffered paste after the 3s window, yielding a false echo-timeout with the prompt sitting unsubmitted in the composer. A slow verdict is safe (the Enter stays withheld until the echo), so the window absorbs the boot instead. Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -122,6 +122,26 @@ describe('pasteDraftWhenAgentReady echo verification', () => {
|
||||
expect(onTimeout).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('verifies a late echo from an agent that was still booting when the paste landed', async () => {
|
||||
const { promise, onTimeout } = startVerifiedPaste()
|
||||
await flushMicrotasks()
|
||||
|
||||
testState.ptyObserver?.(`${DECSET_BRACKETED_PASTE}${CODEX_COMPOSER_PROMPT_RENDER}`)
|
||||
await flushMicrotasks()
|
||||
expect(testState.sendRuntimePtyInputVerified).toHaveBeenCalledTimes(1)
|
||||
|
||||
// Cold-booting agents drain the buffered paste seconds later; the echo
|
||||
// must still verify and submit once it renders.
|
||||
await vi.advanceTimersByTimeAsync(6000)
|
||||
testState.ptyObserver?.(CODEX_VERBATIM_ECHO)
|
||||
await flushMicrotasks()
|
||||
await vi.advanceTimersByTimeAsync(50)
|
||||
|
||||
await expect(promise).resolves.toBe(true)
|
||||
expect(testState.sendRuntimePtyInputVerified).toHaveBeenNthCalledWith(2, {}, 'pty-1', '\r')
|
||||
expect(onTimeout).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('withholds the Enter and reports echo-timeout when the paste never renders', async () => {
|
||||
const { promise, onTimeout } = startVerifiedPaste()
|
||||
await flushMicrotasks()
|
||||
@@ -132,7 +152,7 @@ describe('pasteDraftWhenAgentReady echo verification', () => {
|
||||
|
||||
// Trust/update screens only redraw; the pasted characters never render.
|
||||
testState.ptyObserver?.(TRUST_SCREEN_REDRAW)
|
||||
await vi.advanceTimersByTimeAsync(3000)
|
||||
await vi.advanceTimersByTimeAsync(10_000)
|
||||
|
||||
await expect(promise).resolves.toBe(false)
|
||||
expect(testState.sendRuntimePtyInputVerified).toHaveBeenCalledTimes(1)
|
||||
|
||||
@@ -44,9 +44,12 @@ export function sanitizeBracketedPasteContent(content: string): string {
|
||||
// or (2) the launch fails outright. The hard timeout caps the wait so a
|
||||
// stuck launch doesn't pin a Promise forever.
|
||||
const READINESS_TIMEOUT_MS = 8000
|
||||
// Why: a live composer repaints the pasted text within one frame; the window
|
||||
// only needs to absorb remote-PTY round-trips and slow first paints.
|
||||
const ECHO_VERIFY_TIMEOUT_MS = 3000
|
||||
// Why: readiness can fire on the shell's own bracketed-paste handshake while
|
||||
// the agent binary is still booting — the paste then sits in the TTY buffer
|
||||
// and only renders when the TUI drains stdin, several seconds later. The
|
||||
// window must absorb that cold boot (plus remote-PTY round-trips); a slow
|
||||
// verdict is safe because the submit Enter stays withheld until the echo.
|
||||
const ECHO_VERIFY_TIMEOUT_MS = 10_000
|
||||
|
||||
export type PasteDeliveryTimeoutReason = 'readiness-timeout' | 'echo-timeout'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user