← Commit history

v0.16.14 (insiders): On an Edge-only box nb no longer steers the AI toward installing Chrome (adom/nb-bridge#46): chrome_status and readiness report edgeInstalled / nativeBrowserPresent / chromeNeeded and say Chrome is optional when Edge is there; the install offer appears only when no supported browser exists or the user asks for Chrome by name. notifications now prefers ab 2.1.73's mayBeSuppressed flag and hint.

John Lauer ·c8b954f882 ·2mo ago ·parent 1e6b7fa
7 files changed +47−13
bin/nb-dashboard+1−1
@@ -20,7 +20,7 @@ const { execFile, spawn } = require('child_process'); const fs = require('fs'); const crypto = require('crypto'); -const VERSION = '0.16.13';           // keep in lockstep with package.json (pkg version)+const VERSION = '0.16.14';           // keep in lockstep with package.json (pkg version) // SEND TO AI IS UN-PARKED (John: "yeah, we are re-enabling the send to ai. it seems to be good now"). // The ah build thread certified the crash family dead on the gate-fixed build and the delivery leg is // proven end to end. The park const is DELETED rather than flipped, so no dead branch can resurrect it.
bridge/BRIDGE_VERSION+1−1
@@ -1 +1 @@-0.16.13\ No newline at end of file+0.16.14\ No newline at end of file
bridge/bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "native-browser",   "displayName": "Adom Native Browser",-  "version": "0.16.13",+  "version": "0.16.14",   "expectedExtensionVersion": "0.2.10",   "description": "Drives the user's REAL signed-in Chrome/Edge, in two layers. Layer 1 (nb) needs NOTHING installed and works on any machine: nbrowser_open_os_window (open a URL in the user's real profile, background+flashing, the OAuth/consent path), nbrowser_os_windows, nbrowser_profiles, nbrowser_wake_profile, nbrowser_force_close. Layer 2 (nbe, the Adom Native Browser Extension) unlocks the rest: CDP driving (navigate/click/read/screenshot) inside their logged-in profile. The bridge binds an ephemeral loopback port and the native-messaging host pumps stdio <-> bridge.",   "homepage": "https://wiki.adom.inc/adom/nb-bridge",
bridge/package.json+1−1
@@ -1,6 +1,6 @@ {   "name": "adom-native-browser-bridge",-  "version": "0.16.13",+  "version": "0.16.14",   "private": true,   "description": "Native-browser bridge for Bridge (ab): layer 1 (nb) plus layer 2 via nbe, the Adom Native Browser Extension (Chrome + Edge).",   "main": "server.js",
bridge/server.js+34−8
@@ -1190,6 +1190,20 @@ const CHROME_MSI_URL = 'https://dl.google.com/chrome/install/googlechromestandal const chromeState = { phase: 'idle', error: null, errorCode: null, startedAt: null, finishedAt: null, awaitingUacSince: null, notifyChannel: null, msiPath: null, uacRetryRequested: false }; let _chromeInFlight = null; +// nb drives WHATEVER native browser is on the box (John, adom/nb-bridge#46). Edge ships with Windows, so on+// a fresh machine it is the browser; Chrome is optional and is only worth installing when the user asks for+// Chrome by name or when NO supported browser exists at all.+function detectEdge() {+  for (const p of ['C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe', 'C:/Program Files/Microsoft/Edge/Application/msedge.exe']) { try { if (fs.existsSync(p)) return p; } catch {} }+  return null;+}+function nativeBrowserSummary() {+  const chrome = detectChrome(), edge = detectEdge();+  const profiles = knownProfiles();+  const edgeKeys = profiles.filter((p) => p.browser === 'edge').map((p) => p.key);+  const chromeKeys = profiles.filter((p) => p.browser === 'chrome').map((p) => p.key);+  return { chromeInstalled: !!chrome, edgeInstalled: !!edge, nativeBrowserPresent: !!(chrome || edge), chromeNeeded: !(chrome || edge), edgeProfiles: edgeKeys, chromeProfiles: chromeKeys };+} function detectChrome() {   if (process.platform !== 'win32') return null;   const home = process.env.USERPROFILE || os.homedir();@@ -1303,10 +1317,12 @@ async function notificationSwitches() {   if (!ab) { try { const r = await abCommand('desktop', 'status', {}); let out = r && r.body; if (out && typeof out.output === 'string') { try { out = JSON.parse(out.output); } catch {} } ab = pick(out); } catch {} }   const bannersEnabled = ab ? ab.bannersEnabled !== false : true;   const urgentAllowed = ab ? ab.urgentAllowed === true : null;-  const toastMayBeMuted = !(toastsEnabled && adomBridgeEnabled && bannersEnabled);+  // ab 2.1.73+ folds both conditions into ONE flag to branch on; prefer it when present.+  const abSuppressed = ab && typeof ab.mayBeSuppressed === 'boolean' ? ab.mayBeSuppressed : null;+  const toastMayBeMuted = !(toastsEnabled && adomBridgeEnabled && bannersEnabled) || abSuppressed === true;   const mayBeHiddenByDnd = !toastMayBeMuted && urgentAllowed === false;-  const v = { checked: true, toastsEnabled, adomBridgeEnabled, bannersEnabled, urgentAllowed, toastMayBeMuted, mayBeHiddenByDnd, abRead: !!ab, _abDiag: ab ? undefined : diag,-    _hint: !toastsEnabled ? 'All Windows notifications are OFF on this PC: toasts fold into the notification center unseen. Ask the user to turn notifications back on.'+  const v = { checked: true, toastsEnabled, adomBridgeEnabled, bannersEnabled, urgentAllowed, mayBeSuppressed: abSuppressed, toastMayBeMuted, mayBeHiddenByDnd, abRead: !!ab, _abDiag: ab ? undefined : diag,+    _hint: abSuppressed === true && ab._hint ? String(ab._hint) : !toastsEnabled ? 'All Windows notifications are OFF on this PC: toasts fold into the notification center unseen. Ask the user to turn notifications back on.'          : (!adomBridgeEnabled || !bannersEnabled) ? 'Notifications for Adom Bridge are switched OFF in Windows Settings > Notifications: its toasts never show. Ask the user to turn them on (ab notification_settings_open opens that page).'          : mayBeHiddenByDnd ? 'This PC has never granted Adom Bridge the one-time "Allow important notifications" permission, so while Do Not Disturb is on (a fresh Windows 11 keeps it on) every ab toast, including the Approve-now one, is hidden. A human clicks Allow on Windows\' prompt or enables the priority option under Settings > System > Notifications > Adom Bridge (ab notification_settings_open); until then do not rely on the toast.'          : 'toasts can show' };@@ -1528,9 +1544,9 @@ const NBE_PITCH = 'WORTH SAYING TO THE USER: you can drive their browser with th const NO_FG = 'Open in the BACKGROUND — users automating the browser almost always have OTHER things in the foreground, and a raised window STEALS their keyboard focus (it has typed a user\'s own keystrokes into a username field and dismissed Chrome\'s password dropdown mid-task). Background is where automation SUCCEEDS. NEVER fall back to OS-level launches (Start-Process/launch/msedge/chrome --new-window) or bring_to_front/maximize to surface a window. To genuinely foreground (some auth pages / browser features need it) you must do real work: (1) FIRST warn the user with a Bridge notify (adom-bridge notify_user {message:"Adom is about to bring a browser window forward for <reason>"}), then (2) pass userRequestedForeground:true AND foregroundReason:"<why>". A bare userRequestedForeground with no foregroundReason STAYS background; any driven window that lands topmost is auto-reverted + logged (focus_violation / foreground_raised).'; const VERB_META = {   nbrowser_status: { hint: 'Connectivity check: connected profiles + active browser. Call this first if any verb errors "not connected".', related: ['nbrowser_profiles', 'nbrowser_ping', 'nbrowser_open_window'], pitfalls: ['a profile\'s extension SLEEPS when that profile has no open windows - it then shows disconnected; open a window to wake it'] },-  nbrowser_readiness: { hint: 'READ-ONLY cold-start probe: is the native-browser extension connected + ready to drive? NEVER spawns work or installs anything. state:"ready" = drive now; state:"needs-extension" = offer to install it (Level-2, see installing-the-extension) or it is asleep (open a window in that profile). Also reports chromeInstalled: if false, the user has no native Chrome and you can offer nbrowser_install_chrome.', related: ['nbrowser_status', 'nbrowser_open_window', 'nbrowser_install_chrome'], pitfalls: ['"needs-extension" can mean not-installed OR installed-but-asleep - nbrowser_status\'s tooltip distinguishes', 'this verb never installs; to install, run the installing-the-extension flow', 'for a machine-wide "what do I have" answer prefer ab\'s bridge_readiness (aggregates all bridges, spawns none)'] },+  nbrowser_readiness: { hint: 'READ-ONLY cold-start probe: is the native-browser extension connected + ready to drive? NEVER spawns work or installs anything. state:"ready" = drive now; state:"needs-extension" = offer to install it (Level-2, see installing-the-extension) or it is asleep (open a window in that profile). Also reports chromeInstalled: false does NOT mean install Chrome: nb drives whatever native browser is there (Edge ships with Windows); read nativeBrowser.chromeNeeded, which is true only when NO supported browser exists, and offer nbrowser_install_chrome only then or when the user asks for Chrome by name.', related: ['nbrowser_status', 'nbrowser_open_window', 'nbrowser_install_chrome'], pitfalls: ['"needs-extension" can mean not-installed OR installed-but-asleep - nbrowser_status\'s tooltip distinguishes', 'this verb never installs; to install, run the installing-the-extension flow', 'for a machine-wide "what do I have" answer prefer ab\'s bridge_readiness (aggregates all bridges, spawns none)'] },   nbrowser_install_chrome: { hint: '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). NON-BLOCKING - returns immediately; poll nbrowser_chrome_status. On a locked-down box it escalates to a notify-first UAC (fires a sticky "Approve now" toast, waits for the tap, then raises the prompt). This is step 1 of getting the AI onto the user\'s REAL browser - after Chrome is in, help them sign into Google + load the extension.', related: ['nbrowser_chrome_status', 'nbrowser_readiness', 'installing-the-extension'], pitfalls: ['on a locked-down box the quiet install parks on a UAC; a fresh Windows 11 keeps Do Not Disturb on until its one-time Allow-notifications prompt is answered, so the Approve-now toast is never seen: nbrowser_chrome_status.notifications says so, and {retryUac:true} shows the prompt again for someone looking at the screen without re-downloading', 'non-blocking: do NOT expect Chrome to exist on return - POLL nbrowser_chrome_status until phase:"ready"', 'awaiting_uac means a prompt is on the user\'s desktop - relay it; do not re-trigger install', 'Windows only for now', 'this only INSTALLS Chrome; the user still signs into Google themselves (their credential) - guide them via setting-up-native-chrome'] },-  nbrowser_chrome_status: { hint: 'READ-ONLY: is native Google Chrome installed, and how is an in-flight install going? Returns {installed, executablePath, phase, installing, awaitingUac}. phase: idle|installing|awaiting_uac|ready|failed. Poll this after nbrowser_install_chrome. Never installs or spawns.', related: ['nbrowser_install_chrome', 'nbrowser_readiness'], pitfalls: ['awaiting_uac + notifyChannel:true = a toast is on the desktop, keep polling; notifyChannel:false = relay the UAC to the user yourself', 'installed:true means Chrome.exe is present - next help the user sign into Google + load the extension'] },+  nbrowser_chrome_status: { hint: 'READ-ONLY: is native Google Chrome installed, and how is an in-flight install going? Returns {installed, executablePath, phase, installing, awaitingUac, edgeInstalled, nativeBrowserPresent, chromeNeeded, notifications}. chromeNeeded is true ONLY when no supported browser exists at all: on an Edge-only box Chrome is optional and the hint says so, so do not install it unless the user asks for Chrome by name. phase: idle|installing|awaiting_uac|ready|failed. Poll this after nbrowser_install_chrome. Never installs or spawns.', related: ['nbrowser_install_chrome', 'nbrowser_readiness'], pitfalls: ['awaiting_uac + notifyChannel:true = a toast is on the desktop, keep polling; notifyChannel:false = relay the UAC to the user yourself', 'installed:true means Chrome.exe is present - next help the user sign into Google + load the extension'] },   nbrowser_ping: { hint: 'Round-trip liveness through to the extension.', related: ['nbrowser_status'], pitfalls: ['a pong proves the extension is reachable, NOT that any profile has a drivable window — use nbrowser_status/readiness for that'] },   nbrowser_progress: { hint: 'FAST poll for MEASURED progress of every in-flight verb: active[] carries one block per running verb (phase, stepLabel, percent, elapsedSec, estimatedSec, etaSec, confidence measured|estimated, startKind, sessionId). Estimates come from this machine\'s own rolling per-verb history (also returned as verbDurations). Poll once a second while something slow runs; empty active[] = idle.', related: ['nbrowser_activity', 'nbrowser_status'], pitfalls: ['percent is capped at 95 until the verb actually completes - the reply of the slow verb itself carries the 100% terminal block', 'confidence is estimated (draw it dimmed) until three real samples exist for that verb on this machine', 'open-ended verbs (wait, wait_for, download_wait, keepalive, record_start) report no percent: render an indeterminate sweep, never a fake 0%'] },   nbrowser_activity: { hint: 'Bounded "who drove what, when" activity trail. Query recent verbs + a per-session summary (activeSessions, each with lastAgoMs) to debug things like "why is this profile badged" or "what is running right now". Filter with {sessionId, thread, profile, verb, errorsOnly, sinceMs, limit}.', related: ['nbrowser_events', 'nbrowser_owned', 'nbrowser_status'], pitfalls: ['the verb serves the in-memory ring (max 600 entries / ~1h, resets on restart); a persistent self-janitored JSONL survives restarts at ~/.adom/bridge-logs/native-browser-activity.jsonl for deeper forensics', 'a session with a large lastAgoMs is idle - its "Adom is driving" badge idle-expires after ~3 min'] },@@ -1758,6 +1774,9 @@ function dispatch(verb, args) {         unidentifiedProfiles,                   // connected but pre-hello (version unknown yet); resolves on next hello         chromeInstalled: !!chExe,               // is native Google Chrome present on the box?         chromeInstallPhase: chExe ? 'ready' : chromeState.phase,  // idle|installing|awaiting_uac|ready|failed+        // nb drives whatever native browser is present (adom/nb-bridge#46): on an Edge-only box Chrome is+        // optional, and an AI reading chromeInstalled:false must not conclude it should install Chrome.+        nativeBrowser: nativeBrowserSummary(),  // {chromeInstalled, edgeInstalled, nativeBrowserPresent, chromeNeeded, edgeProfiles, chromeProfiles}         _hint: extensionStale           ? `STALE EXTENSION: ${staleDetail}; this bridge expects ${EXPECTED_EXT}. New verbs (wait_for/keepalive/download_wait/set_date/totp/robust-click) will return "verb not implemented yet" on the stale profile(s). SELF-HEAL: (1) refresh the unpacked extension dir to the current build (installing-the-extension -> Updating, or re-sync the extension/ folder), then (2) nbrowser_dev_reload {"profile":"<staleProfile>"} - it re-reads disk in 2 passes and reconnects in ~10s. If the on-disk build is ALREADY current, step (2) alone heals it.` + skewHint           : ready@@ -1795,15 +1814,21 @@ function dispatch(verb, args) {         return;       }       if (exe) return resolve({ success: true, output: JSON.stringify({ ok: true, installed: true, alreadyInstalled: true, executablePath: exe, _hint: 'Native Google Chrome is already installed. Next in the onboarding: help the user sign into their Google account (so passwords/bookmarks/autofill sync), then load the Adom extension (installing-the-extension). See setting-up-native-chrome.' }) });+      const nbSum = nativeBrowserSummary();       installNativeChrome({ elevate: (args && args.elevate !== undefined) ? args.elevate : 'auto' }); // fire-and-forget; poll status-      return resolve({ success: true, output: JSON.stringify({ ok: true, installing: true, phase: 'installing', _hint: 'Installing Google Chrome in the BACKGROUND (downloading Google\'s official installer, then a quiet user-scoped install). On a normal PC this is silent, NO prompt. On a locked-down box (VM/managed) it needs a Windows approval - the bridge fires a sticky "Approve now" toast and waits for the user to tap it, then raises the UAC. Poll nbrowser_chrome_status: phase goes installing -> awaiting_uac -> ready. Then help the user sign into Google + load the extension (setting-up-native-chrome).', statusVerb: 'nbrowser_chrome_status' }) });+      return resolve({ success: true, output: JSON.stringify({ ok: true, installing: true, phase: 'installing', edgeInstalled: nbSum.edgeInstalled, chromeNeeded: nbSum.chromeNeeded, _note: nbSum.edgeInstalled ? 'Edge is already on this box and nb drives it; Chrome is optional. Continuing because you asked.' : undefined, _hint: 'Installing Google Chrome in the BACKGROUND (downloading Google\'s official installer, then a quiet user-scoped install). On a normal PC this is silent, NO prompt. On a locked-down box (VM/managed) it needs a Windows approval - the bridge fires a sticky "Approve now" toast and waits for the user to tap it, then raises the UAC. Poll nbrowser_chrome_status: phase goes installing -> awaiting_uac -> ready. Then help the user sign into Google + load the extension (setting-up-native-chrome).', statusVerb: 'nbrowser_chrome_status' }) });     }     // nbrowser_chrome_status - READ-ONLY: is native Chrome installed / how's an in-flight install going?     if (verb === 'nbrowser_chrome_status') {       const st = chromeStatus();+      const nb = nativeBrowserSummary();+      Object.assign(st, { edgeInstalled: nb.edgeInstalled, nativeBrowserPresent: nb.nativeBrowserPresent, chromeNeeded: nb.chromeNeeded, edgeProfiles: nb.edgeProfiles });       notificationSwitches().then((sw) => {       st.notifications = sw;-      const hint = st.installed+      const edgeEnough = !st.installed && nb.edgeInstalled && !st.installing && st.phase !== 'awaiting_uac';+      const hint = edgeEnough && st.phase !== 'failed'+        ? `Chrome is not installed and is NOT needed: Microsoft Edge is installed${nb.edgeProfiles.length ? ` and drivable (${nb.edgeProfiles.join(', ')})` : ''}, and nb drives whatever native browser is on the box (nbrowser_profiles, then nbrowser_open_window or nbrowser_open_os_window in an edge: profile; the extension loads into an Edge profile the same way). Install Chrome only if the user asks for Chrome by name.`+        : st.installed         ? 'Native Google Chrome is installed. Next: help the user sign into their Google account (open chrome://settings or accounts.google.com and turn ON sync so passwords/bookmarks/autofill carry over), then load the Adom extension (installing-the-extension). Full flow: setting-up-native-chrome.'         : st.awaitingUac           ? ((sw.toastMayBeMuted || sw.mayBeHiddenByDnd) ? `Waiting on a Windows security prompt that the user has probably NOT been told about: ${sw._hint} Meanwhile, anyone looking at this PC's screen can be shown the prompt again with nbrowser_install_chrome {retryUac:true} (no re-download).`@@ -1811,7 +1836,8 @@ function dispatch(verb, args) {           : st.installing ? 'Chrome install in progress (downloading / silent install). Poll again in a few seconds.'           : st.phase === 'failed' && st.errorCode === 'chrome_needs_approval' && st.canRetryUac ? `The Windows security prompt was never accepted (${(sw.toastMayBeMuted || sw.mayBeHiddenByDnd) ? sw._hint : 'the toast went unanswered'}). The installer is still on this PC: nbrowser_install_chrome {retryUac:true} shows the prompt again for someone looking at the screen.`           : st.phase === 'failed' ? `Chrome install failed (${st.errorCode}): ${st.error}. Fixes depend on the code - e.g. free disk, check network, or ask the user to install Chrome manually.`-          : 'No native Chrome yet and no install running. Call nbrowser_install_chrome to install it.';+          : nb.edgeInstalled ? 'Chrome is not installed and no install is running. Edge is on this box and nb drives it; Chrome is optional (install only if the user asks for Chrome by name).'+          : 'NO native browser found on this box (no Chrome, no Edge). nbrowser_install_chrome installs Google Chrome (about 150 MB download; may need a Windows security prompt on a locked-down box).';       return resolve({ success: true, output: JSON.stringify({ ...st, _hint: hint }) });       });       return;
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "nb-bridge",-  "version": "0.16.13",+  "version": "0.16.14",   "type": "app",   "title": "NB - Native Browser Bridge",   "description": "This package installs the SKILLS into your container so your AI knows how to drive your real signed-in Chrome and Edge; Bridge loads the bridge runtime itself from the release zip.",
skills/adom-native-browser/SKILL.md+8
@@ -12,6 +12,14 @@ human-trust signals all apply. It sails through **login walls and captchas** tha Chrome-for-Testing. When a task needs the user's *real* browser or their *real* logins, this is the tool - not pup. +## Use the browser that is there (nb-bridge#46)++nb drives whatever native browser the box has. Edge ships with Windows, so on a fresh machine Edge IS the+browser: load the extension into `edge:Default` and drive it. `nbrowser_chrome_status` and+`nbrowser_readiness.nativeBrowser` say `chromeNeeded:true` only when NO supported browser exists; on an+Edge-only box they say Chrome is optional. Install Chrome only when the user asks for Chrome by name (or+there is no browser at all): it is a 150 MB download and, on a locked-down box, a Windows security prompt.+ ## A fresh box with no signed-in browser (Layer 1 still works, from 0.16.6)  `nbrowser_profiles` lists profiles with NO signed-in account as `anonymous:true`, keyed by directory