From 54249bc0153f5917c201d20ebd3d5b22b010acf8 Mon Sep 17 00:00:00 2001 From: ciregenz Date: Tue, 2 Jun 2026 12:27:28 -0700 Subject: [PATCH] [eric] browser: retry cold screenshot capture to dodge the turn-0 viz race --- frontend/src/shared/browserCommandHandler.ts | 24 ++++++++++++++++---- frontend/src/shared/browserRegistry.ts | 1 + 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/frontend/src/shared/browserCommandHandler.ts b/frontend/src/shared/browserCommandHandler.ts index b6f31d55..f9a580d1 100644 --- a/frontend/src/shared/browserCommandHandler.ts +++ b/frontend/src/shared/browserCommandHandler.ts @@ -57,10 +57,26 @@ export function getActionLabel(action: string): string { } async function handleScreenshot(wv: BrowserWebview): Promise> { - const nativeImage = await wv.capturePage(); - const dataUrl = nativeImage.toDataURL(); - const base64 = dataUrl.replace(/^data:image\/\w+;base64,/, ''); - return { image: base64, url: wv.getURL(), title: wv.getTitle() }; + // capturePage throws UnknownVizError if the webview hasn't composited a frame + // yet (the Viz compositor races the first paint, reliably bit turn-0 captures). + // Retry a few times with a short backoff so a cold first screenshot succeeds + // instead of burning a whole agent turn on a transient error. + let lastErr: any; + for (let attempt = 0; attempt < 4; attempt++) { + try { + const nativeImage = await wv.capturePage(); + if (!nativeImage.isEmpty()) { + const dataUrl = nativeImage.toDataURL(); + const base64 = dataUrl.replace(/^data:image\/\w+;base64,/, ''); + return { image: base64, url: wv.getURL(), title: wv.getTitle() }; + } + lastErr = new Error('capturePage returned an empty image (frame not painted yet)'); + } catch (err: any) { + lastErr = err; + } + await new Promise((r) => setTimeout(r, 250 * (attempt + 1))); + } + return { error: `Screenshot failed after retries: ${lastErr?.message || String(lastErr)}` }; } async function handleGetText(wv: BrowserWebview): Promise> { diff --git a/frontend/src/shared/browserRegistry.ts b/frontend/src/shared/browserRegistry.ts index 604508e5..a60a8f70 100644 --- a/frontend/src/shared/browserRegistry.ts +++ b/frontend/src/shared/browserRegistry.ts @@ -11,6 +11,7 @@ export interface BrowserWebview extends HTMLElement { capturePage: (rect?: { x: number; y: number; width: number; height: number }) => Promise<{ toDataURL: () => string; toPNG: () => Buffer; + isEmpty: () => boolean; }>; executeJavaScript: (code: string) => Promise; sendInputEvent: (event: any) => void;