← Commit history

bridge 0.1.1: screenshot best-practice hints (page=nbrowser_screenshot, window+chrome+child-popups=desktop_screenshot_window parent/child hwnds, NEVER fullscreen) + open_window returns hwnd

John Lauer ·259226ae47 ·3mo ago ·parent 1cbc719
6 files changed +36−23
bridge/BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.1.0+0.1.1
bridge/bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "native-browser",   "displayName": "Adom Native Browser (Chrome + Edge)",-  "version": "0.1.0",+  "version": "0.1.1",   "expectedExtensionVersion": "0.1.0",   "description": "Drives the user's REAL signed-in Chrome/Edge via an installed extension (native-messaging transport). Mirrors the puppeteer bridge's verb surface as nbrowser_* but runs inside the actual logged-in profile, so cookies/SSO/saved-logins/captcha human-trust all work. The bridge binds an ephemeral loopback port (PORTS.md compliant) and writes a discovery file; the native-messaging host pumps stdio <-> this bridge; the extension never touches a TCP port.",   "homepage": "https://wiki.adom.inc/adom/adom-browser-extension",
bridge/package.json+1−1
@@ -1,6 +1,6 @@ {   "name": "adom-native-browser-bridge",-  "version": "0.1.0",+  "version": "0.1.1",   "private": true,   "description": "adom-desktop bridge for the Adom native-browser extension (Chrome + Edge)",   "main": "server.js",
bridge/server.js+14−14
@@ -796,8 +796,8 @@ const VERB_META = {   nbrowser_wait: { hint: 'Sleep server-side (let a page settle) before the next verb.', related: ['nbrowser_wait_for', 'nbrowser_screenshot'], pitfalls: ['prefer nbrowser_wait_for for a DETERMINISTIC wait (selector/url/text) instead of a fixed sleep'] },   nbrowser_wait_for: { hint: 'DETERMINISTIC wait after a click/nav: block until a CSS selector exists / the URL contains a substring / the visible text contains a substring, or timeoutMs. Use this instead of nbrowser_wait + fixed sleeps + coordinate races. Returns {matched, by, url, waitedMs}.', related: ['nbrowser_click', 'nbrowser_navigate', 'nbrowser_eval'], pitfalls: ['pass at least one of selector / urlIncludes / textIncludes', 'matched:false + timedOut:true means the condition never appeared - screenshot to see the real page', 'selector matches the top document only (not inside cross-origin iframes)'] },   nbrowser_keepalive: { hint: 'Keep a session ACTIVE to avoid vendor idle sign-outs (Gusto/Intuit ~10-15 min): replays a benign mousemove/focus on an interval (NO navigation, no side-effects). Per-session; auto-stops when the window closes. Call with {stop:true} to cancel. Set at the START of a long unattended automation.', related: ['nbrowser_open_window', 'nbrowser_login_state'], pitfalls: ['client-side activity only - a vendor that expires the SERVER session regardless may still sign out; combine with periodic real actions if so', 'default interval 4min (30s..15min); pick below the vendor idle timeout'] },-  nbrowser_screenshot: { hint: 'CDP screenshot - captures a BACKGROUND/occluded window WITHOUT foregrounding it. This is how you "see" a page; never pull a window forward to grab a shot.', related: ['nbrowser_eval', 'nbrowser_click'], pitfalls: ['resized <=1568 for the model; pass toFile:true to save full-res to a laptop file (pull_file it)'] },-  nbrowser_screenshot_full_res: { hint: 'Full-resolution / full-page screenshot via CDP.', related: ['nbrowser_screenshot'], pitfalls: ['large base64 can be slow over the relay - prefer toFile then pull_file'] },+  nbrowser_screenshot: { hint: 'CDP screenshot of the PAGE - captures a BACKGROUND/occluded window WITHOUT foregrounding it. Your DEFAULT way to "see" a page. It captures page CONTENT only - NOT the browser chrome (URL/tabs) and NOT native popups/dialogs (Chrome password dropdown, print dialog, alert/confirm) which live OUTSIDE the page.', related: ['nbrowser_eval', 'nbrowser_click', 'nbrowser_handle_dialog'], pitfalls: ['resized <=1568 for the model; pass toFile:true to save full-res to a laptop file (pull_file it)', 'to see the WINDOW FRAME or a native popup/dropdown/dialog, use AD desktop_screenshot_window {hwnd | titleContains} (WGC): it captures the window PLUS its OWNED CHILD windows (returns a parent/child hwnd array, so you see popups like the password dropdown), and it is background-safe (no foregrounding)', '⛔ NEVER take a fullscreen / whole-monitor screenshot to inspect a browser window - it grabs the user\'s other work, is imprecise, and often implies foregrounding. Page → nbrowser_screenshot; window/chrome/popup → desktop_screenshot_window'] },+  nbrowser_screenshot_full_res: { hint: 'Full-resolution, full-PAGE screenshot via CDP (the entire scrollable page, not just the viewport). Still PAGE content only.', related: ['nbrowser_screenshot'], pitfalls: ['this is full-PAGE, NOT "fullscreen monitor" - it never captures the desktop or the user\'s other windows', 'large base64 can be slow over the relay - prefer toFile then pull_file', 'for the window frame or a native dialog/popup, use AD desktop_screenshot_window {hwnd | titleContains} (parent/child hwnds), never a monitor capture'] },   nbrowser_eval: { hint: 'Run JS in the page and get the result. Powerful - use read-only expressions to inspect state.', related: ['nbrowser_screenshot'], pitfalls: ['runs arbitrary JS on the user\'s LIVE session - never run destructive scripts'] },   nbrowser_evaluate: { hint: 'Alias of nbrowser_eval.', related: ['nbrowser_eval'], pitfalls: ['runs arbitrary JS on the user\'s LIVE session - never run destructive scripts'] },   nbrowser_input_dispatch: { hint: 'Low-level isTrusted CDP input (click/move/type/key).', related: ['nbrowser_click', 'nbrowser_type'], pitfalls: ['prefer nbrowser_click (smart selector) over raw xy'] },@@ -935,7 +935,7 @@ function dispatch(verb, args) {         d('nbrowser_install_chrome', 'Install NATIVE Google Chrome for the user (Windows). Downloads Google\'s official installer + runs a quiet user-scoped install (no admin on a normal PC); escalates to a notify-first UAC on a locked-down box. NON-BLOCKING - poll nbrowser_chrome_status. Step 1 of putting the AI onto the user\'s real signed-in browser.', { elevate: 'optional "auto"(default) | true (skip quiet, straight to UAC) | false (quiet only, no prompt)' }, { ok: 'bool', installing: 'bool', installed: 'bool?', alreadyInstalled: 'bool?', executablePath: 'str?', statusVerb: 'str' }, 30, { statusVerb: 'nbrowser_chrome_status', example: {} }),         d('nbrowser_chrome_status', 'READ-ONLY: is native Google Chrome installed / how is an install going? Poll after nbrowser_install_chrome.', {}, { installed: 'bool', executablePath: 'str?', phase: '"idle"|"installing"|"awaiting_uac"|"ready"|"failed"', installing: 'bool', awaitingUac: 'bool', errorCode: 'str?' }, 15, { example: {} }),         d('nbrowser_ping', 'Round-trip liveness check through to the extension.', {}, { ok: 'bool', pong: 'bool' }, 15),-        d('nbrowser_open_window', 'Open a NEW window in the BACKGROUND (unfocused) and return a sessionId to drive it. REGISTRATION REQUIRED: sessionId + thread + purpose, or refused (session_unregistered). Never steals focus; a topmost driven window is auto-reverted (focus_violation).', { sessionId: 'required str (your short task id)', thread: 'required str (which AI thread you are)', purpose: 'required str (human-readable, e.g. "finishing John\'s IRS signup")', url: 'required str', profile: P, userRequestedForeground: 'optional bool - raise to foreground ONLY when the user asked or a browser/auth feature requires it (default false)', foregroundReason: 'REQUIRED with userRequestedForeground - human-readable why; without it the window STAYS background. Warn the user via AD notify_user FIRST.' }, { ok: 'bool', sessionId: 'str', tabId: 'str', windowId: 'int', url: 'str', _warning: 'str?', keptBackground: 'bool?' }, 30, { example: { sessionId: 'irs', thread: 'thread-7', purpose: 'finishing John IRS signup', url: 'https://wiki.adom.inc' } }),+        d('nbrowser_open_window', 'Open a NEW window in the BACKGROUND (unfocused) and return a sessionId to drive it. REGISTRATION REQUIRED: sessionId + thread + purpose, or refused (session_unregistered). Never steals focus; a topmost driven window is auto-reverted (focus_violation).', { sessionId: 'required str (your short task id)', thread: 'required str (which AI thread you are)', purpose: 'required str (human-readable, e.g. "finishing John\'s IRS signup")', url: 'required str', profile: P, userRequestedForeground: 'optional bool - raise to foreground ONLY when the user asked or a browser/auth feature requires it (default false)', foregroundReason: 'REQUIRED with userRequestedForeground - human-readable why; without it the window STAYS background. Warn the user via AD notify_user FIRST.' }, { ok: 'bool', sessionId: 'str', tabId: 'str', windowId: 'int', hwnd: 'int (OS window handle - use with desktop_screenshot_window to capture the window+chrome+child popups)', url: 'str', _warning: 'str?', keptBackground: 'bool?' }, 30, { example: { sessionId: 'irs', thread: 'thread-7', purpose: 'finishing John IRS signup', url: 'https://wiki.adom.inc' } }),         d('nbrowser_close_window', 'Close a window the agent opened. includeSpawned:true also closes everything that session SPAWNED (composes/popups).', { sessionId: 'required str', includeSpawned: 'optional bool (also close this session\'s spawned windows)', profile: P }, { ok: 'bool', closed: 'bool', sessionId: 'str', alsoClosedSpawned: '[int]' }, 20, { example: { sessionId: 'work', includeSpawned: true } }),         d('nbrowser_owned', 'List everything a session owns: opened windows + adopted SPAWNED windows/tabs (compose/popup/target=_blank a driven page opened). Acknowledges spawns.', { sessionId: 'optional str (all sessions if omitted)', profile: P }, { ok: 'bool', opened: '[{sessionId,windowId,url,title,purpose,ageMs}]', spawned: '[{kind,windowId,tabId,url,ageMs,spawnedBy,opener}]' }, 20, { example: { sessionId: 'irs' } }),         d('nbrowser_cleanup', 'Close ONLY what a session SPAWNED (leaked composes/popups). Never the user\'s windows, never your primary opened window.', { sessionId: 'required str', profile: P }, { ok: 'bool', closed: '[{windowId,url}]', count: 'int', keptOpenedWindow: 'int?' }, 20, { example: { sessionId: 'irs' } }),@@ -1221,7 +1221,7 @@ function dispatch(verb, args) {   }); } -function onHostFrame(frame) {+async function onHostFrame(frame) {   if (frame && frame.id != null && pending.has(frame.id)) {     const p = pending.get(frame.id);     clearTimeout(p.timer);@@ -1270,16 +1270,16 @@ function onHostFrame(frame) {       if (p.verb === 'nbrowser_open_window') {         const sid = payload.sessionId;         // Robust hwnd capture: the diff can miss on the first pass (window not painted yet), so retry-        // briefly so a window never ends up silently UNBADGED.-        (async () => {-          for (let i = 0; i < 4; i++) {-            let neu;-            try { const after = await chromeHwnds(); neu = [...after].find((h) => !preOpenHwnds.has(h)); } catch {}-            if (neu) { lastHwnd = neu; if (sid) sessionHwnd.set(sid, neu); taskbar(neu, { overlay: { badge: 'adom', tooltip: 'Adom is driving this browser' } }); return; }-            await new Promise((r) => setTimeout(r, 250));-          }-        })();-        payload = { ...payload, _taskbarHint: flashHint() };+        // briefly - so a window is never silently UNBADGED, and we can hand the AI its OS hwnd for+        // desktop_screenshot_window (the window+chrome+child-popup capture technique).+        let neu = null;+        for (let i = 0; i < 4; i++) {+          try { const after = await chromeHwnds(); neu = [...after].find((h) => !preOpenHwnds.has(h)) || null; } catch {}+          if (neu) { lastHwnd = neu; if (sid) sessionHwnd.set(sid, neu); taskbar(neu, { overlay: { badge: 'adom', tooltip: 'Adom is driving this browser' } }); break; }+          await new Promise((r) => setTimeout(r, 250));+        }+        payload = { ...payload, ...(neu ? { hwnd: neu } : {}), _taskbarHint: flashHint(),+          _screenshotHint: 'To SEE this window without foregrounding it: nbrowser_screenshot {sessionId} = the PAGE bitmap (fast, default). To see the WINDOW FRAME or a native popup/dialog (Chrome password dropdown, print dialog, alert/confirm), use AD desktop_screenshot_window {hwnd:' + (neu || '<from desktop_list_windows by title>') + '} - it captures the window + its OWNED CHILD windows (parent/child hwnds). NEVER a fullscreen/monitor screenshot to inspect a browser window.' };       }       // Badge any adopted SPAWNED windows surfaced by owned/list_windows (they have their own hwnd, no       // sessionId, so we correlate by bounds on-demand — there is no push channel).
native-browser-bridge-manifest.json+4−4
@@ -1,8 +1,8 @@ {   "manifest_version": 1,-  "version": "0.1.0",-  "url": "https://wiki.adom.inc/download/adom/adom-browser-extension/0.1.0/native-browser-bridge-v0.1.0.bin",-  "sha256": "93d416ed71c3922a1b48fba3b955bc55a697846805043c422f5c73bd658f528d",-  "size": 52755,+  "version": "0.1.1",+  "url": "https://wiki.adom.inc/download/adom/adom-browser-extension/0.1.1/native-browser-bridge-v0.1.1.bin",+  "sha256": "5412f87a9f81ee457f4c2315dba5ad464446b33f600adeb260b1df64013b39e9",+  "size": 53513,   "released_at": "2026-07-14T00:00:00Z" }\ No newline at end of file
skills/driving-the-extension/SKILL.md+15−2
@@ -308,9 +308,22 @@ a `_modal` note. **Dismiss it:** `nbrowser_handle_dialog {sessionId, accept:true `accept:false` (Cancel/Stay), or `text:"…"` to answer a prompt - then re-screenshot and continue. If an action mysteriously stops working, suspect a dialog. -## Screenshots+## Screenshots — pick the RIGHT one (page vs window vs NEVER fullscreen)+Three ways to "see", and the choice matters:+- **The PAGE → `nbrowser_screenshot {sessionId}`** (CDP). Your DEFAULT. Fast, background-safe, captures the+  page bitmap. But it shows **page content ONLY** - no browser chrome (URL/tabs) and **no native popups/+  dialogs** (Chrome's saved-password dropdown, print dialog, `alert`/`confirm`) which render OUTSIDE the page.+- **The WINDOW + chrome + native popups → `desktop_screenshot_window {hwnd | titleContains}`** (AD, WGC).+  Use this when you need the URL bar, a `chrome://` page, or a native popup/dialog. It captures the window+  **plus its OWNED CHILD windows and returns the full parent/child hwnd array** - the same technique that+  makes KiCad/Fusion error dialogs visible. Background-safe (no foregrounding). `nbrowser_open_window`+  returns the window's **`hwnd`** for exactly this; or find it via `desktop_list_windows` by title.+- **⛔ NEVER a fullscreen / whole-monitor screenshot to inspect a browser window.** It grabs the user's+  other work, is imprecise, and usually implies foregrounding. There is always a targeted option above.+  (`nbrowser_screenshot_full_res` is full-**PAGE**, not full-monitor - it still only sees the page.) ```bash-adom-desktop -t AdomLapper nbrowser_screenshot '{"sessionId":"s1","toFile":true}'   # writes a laptop PNG, returns its path+adom-desktop -t AdomLapper nbrowser_screenshot '{"sessionId":"s1","toFile":true}'   # PAGE bitmap → laptop PNG path+adom-desktop -t AdomLapper desktop_screenshot_window '{"hwnd":<from open_window>}'   # WINDOW + chrome + child popups # then: adom-desktop -t AdomLapper pull_file '{"filePaths":["<path>"],"saveTo":"/tmp"}' ``` Screenshots are large base64 - over the relay they can time out, so **prefer `toFile:true` + `pull_file`**