fix: time out agent-browser stale close (#3832)

This commit is contained in:
Neil 2026-05-30 11:46:02 -07:00 committed by GitHub
parent d7c42b144a
commit d23c754535
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 70 additions and 5 deletions

View File

@ -157,6 +157,40 @@ describe('AgentBrowserBridge', () => {
expect(clickCall![1]).not.toContain('--cdp')
})
it('continues when stale agent-browser session close hangs during session creation', async () => {
vi.useFakeTimers()
try {
const closeKill = vi.fn()
execFileMock.mockImplementation(
(_bin: string, args: string[], _opts: unknown, cb: Function) => {
if (args.includes('close')) {
return { kill: closeKill }
}
if (args.includes('snapshot')) {
cb(null, JSON.stringify({ success: true, data: { snapshot: 'ready' } }), '')
return { kill: vi.fn() }
}
throw new Error(`unexpected agent-browser args ${args.join(' ')}`)
}
)
const promise = bridge.snapshot()
let settled = false
void promise.finally(() => {
settled = true
})
await vi.advanceTimersByTimeAsync(3_000)
await Promise.resolve()
expect(settled).toBe(true)
await expect(promise).resolves.toEqual({ browserPageId: 'tab-1', snapshot: 'ready' })
expect(closeKill).toHaveBeenCalled()
} finally {
vi.useRealTimers()
}
})
// ── --json always appended ──
it('always appends --json to commands', async () => {

View File

@ -54,6 +54,7 @@ import type {
const EXEC_TIMEOUT_MS = 90_000
const CONSECUTIVE_TIMEOUT_LIMIT = 3
const WAIT_PROCESS_TIMEOUT_GRACE_MS = 1_000
const STALE_SESSION_CLOSE_TIMEOUT_MS = 3_000
type SessionState = {
proxy: CdpWsProxy
@ -1917,11 +1918,7 @@ export class AgentBrowserBridge {
// across Orca restarts. A stale session ignores --cdp (already initialized) and
// connects to the dead port. Must await close so the daemon forgets the session
// before we pass --cdp with the new port.
await new Promise<void>((resolve) => {
execFile(this.agentBrowserBin, ['--session', sessionName, 'close'], { timeout: 3000 }, () =>
resolve()
)
})
await this.closeStaleAgentBrowserSession(sessionName)
const proxy = new CdpWsProxy(wc)
const cdpEndpoint = await proxy.start()
@ -2097,6 +2094,40 @@ export class AgentBrowserBridge {
return new BrowserError('browser_tab_not_found', pageUnavailableMessageForSession(sessionName))
}
private closeStaleAgentBrowserSession(sessionName: string): Promise<void> {
return new Promise((resolve) => {
let child: ReturnType<typeof execFile> | null = null
let settled = false
const finish = (): void => {
if (settled) {
return
}
settled = true
clearTimeout(timeout)
resolve()
}
// Why: this is best-effort daemon cleanup before creating a fresh session;
// a wedged close command must not block the real browser action.
const timeout = setTimeout(() => {
child?.kill()
finish()
}, STALE_SESSION_CLOSE_TIMEOUT_MS)
try {
child = execFile(
this.agentBrowserBin,
['--session', sessionName, 'close'],
{ timeout: STALE_SESSION_CLOSE_TIMEOUT_MS },
finish
)
} catch {
finish()
}
})
}
private createCommandError(
sessionName: string,
message: string,