diff --git a/packages/coding-agent/src/tools/browser/tab-supervisor.ts b/packages/coding-agent/src/tools/browser/tab-supervisor.ts index 6da6b7be0..767b48c64 100644 --- a/packages/coding-agent/src/tools/browser/tab-supervisor.ts +++ b/packages/coding-agent/src/tools/browser/tab-supervisor.ts @@ -134,6 +134,7 @@ const tabs = new Map(); // awaits) cannot interleave and leak a worker + browser refCount. const acquireChains = new Map>(); const GRACE_MS = 750; +const WORKER_INIT_TIMEOUT_MS = 15_000; // Names of tabs the supervisor force-killed (timeout past grace, failed recycle), // mapped to the kill reason. Lets the next `run` on that name explain WHY the tab // vanished instead of a bare "not alive". Cleared when the name is opened again. @@ -272,7 +273,11 @@ async function acquireTabImpl( } let info: ReadyInfo; try { - info = await initializeTabWorker(worker, initPayload, opts.timeoutMs + GRACE_MS); + info = await initializeTabWorker( + worker, + initPayload, + Math.max(WORKER_INIT_TIMEOUT_MS, opts.timeoutMs + GRACE_MS), + ); } catch (error) { // `BuildMessage`-class failures arrive asynchronously via the worker's `error` event, // after `spawnTabWorker`'s synchronous try/catch has already returned. Fall back to @@ -287,7 +292,11 @@ async function acquireTabImpl( }); worker = await spawnInlineWorker(); try { - info = await initializeTabWorker(worker, initPayload, opts.timeoutMs + GRACE_MS); + info = await initializeTabWorker( + worker, + initPayload, + Math.max(WORKER_INIT_TIMEOUT_MS, opts.timeoutMs + GRACE_MS), + ); } catch (inlineError) { await worker.terminate().catch(() => undefined); if (tempHold || browser.refCount === 0) await releaseBrowser(browser, { kill: false });