← Commit history
Update 5 file(s)
5 files changed
+24−12
bridge/BRIDGE_VERSION+1−1@@ -1 +1 @@-0.1.46+0.1.47
bridge/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "native-browser", "displayName": "Adom Browser Extension",- "version": "0.1.46",+ "version": "0.1.47", "expectedExtensionVersion": "0.1.3", "description": "Drives the user's REAL signed-in Chrome/Edge. MOST verbs require the Adom extension installed in that browser profile (CDP driving: navigate/click/read/screenshot). TWO verbs need NO extension at all and work on any machine: nbrowser_open_os_window (open a URL in the user's real profile, background+flashing - the OAuth/consent path) and nbrowser_os_windows (enumerate browser windows). Also extension-free: nbrowser_profiles, nbrowser_wake_profile, nbrowser_force_close. The bridge binds an ephemeral loopback port and the native-messaging host pumps stdio <-> bridge.", "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.46",+ "version": "0.1.47", "private": true, "description": "adom-desktop bridge for the Adom native-browser extension (Chrome + Edge)", "main": "server.js",
bridge/server.js+17−5@@ -729,12 +729,19 @@ function applyBadge(sid, hwnd, profileKey, style) { // REQUIRES a written reason — but that reason only ever reached the AI. When a foreground grant is // actually exercised, SHOW THE HUMAN why, full-screen, for 3s. A takeover you can't explain is exactly // what erodes trust in an agent that can touch your desktop.-function announceForeground(reason, sid) {- const why = String(reason || '').trim().slice(0, 160) || 'no reason given';- adCommand(null, 'desktop_caption', { text: `Adom is bringing a window to the front - ${why}`, id: 'adom-foreground', position: 'top', duration: 3000 })- .then((r) => { const ok = !!(r && r.status >= 200 && r.status < 300); logActivity({ verb: '_foreground_announce', sessionId: sid || null, ok, ops: why.slice(0, 80), errorCode: ok ? null : `http_${(r && r.status) || 0}`, internal: true }); })+// ANY open the user will actually SEE gets announced (John, 2026-07-22): "if you know it's going to+// foreground on its own cuz that's just the way the browser acts, that's fine, allow it, but put up a+// 2 second caption with the reason." We do NOT fight the browser's natural behavior - we just never let+// a page appear in front of the user without saying what it is and why. 2s, auto-dismiss. `why` falls+// back to the session PURPOSE, which registration already makes mandatory, so there is always something+// honest to show.+function announceVisibleOpen(what, why, sid) {+ const reason = String(why || (sid && sessionPurpose.get(sid)) || '').trim().slice(0, 160) || 'no reason given';+ adCommand(null, 'desktop_caption', { text: `Adom ${what} - ${reason}`, id: 'adom-foreground', position: 'top', duration: 2000 })+ .then((r) => { const ok = !!(r && r.status >= 200 && r.status < 300); logActivity({ verb: '_open_announce', sessionId: sid || null, ok, ops: `${what} | ${reason}`.slice(0, 110), errorCode: ok ? null : `http_${(r && r.status) || 0}`, internal: true }); }) .catch(() => {}); }+function announceForeground(reason, sid) { announceVisibleOpen('is bringing a window to the front', reason, sid); } // ── SESSION PERSISTENCE — sessionHwnd/sessionProfile are the badge's memory, and they used to be // in-memory only: every bridge redeploy WIPED them, so a thread that kept driving after a restart never // got its badge back ("no badge until it opens a fresh window"). Persist {sid -> hwnd+profile} to disk on@@ -1694,7 +1701,7 @@ function dispatch(verb, args) { // grouped the page into an existing one), so there is nothing to minimize and the page lands // in front of the user. Tell them what appeared and why - John, 2026-07-22: a sign-in page // showing up unannounced is the failure, whether or not the AI asked for foreground.- announceForeground(`opened ${(() => { try { return new URL(url).hostname; } catch { return 'a page'; } })()} in your browser${a.purpose ? ' - ' + String(a.purpose).slice(0, 90) : ''}`, a.sessionId);+ announceVisibleOpen(`opened ${(() => { try { return new URL(url).hostname; } catch { return 'a page'; } })()} in your browser`, a.purpose, a.sessionId); } // LAYER-1 BADGE (hollow): mark the window Adom OPENED even though it cannot drive it - provenance // matters most on an OAuth consent page. Uses applyBadge so the normal 3-min idle expiry restores@@ -2113,6 +2120,11 @@ async function onHostFrame(frame) { // session; profile-keyed verbs (no sessionId - a thread driving by profile: alone) touch every // session pinned to that profile, lazily re-correlating via the extension if the hwnd is unknown // (e.g. after a bridge restart). Idle-expires when commands stop.+ // open_tab with active:true lands IN FRONT of the user inside an existing window - normal browser+ // behavior, allowed, but announced.+ if (p.verb === 'nbrowser_open_tab' && p.args && p.args.active === true && payload && payload.ok !== false) {+ announceVisibleOpen('opened a tab in your browser', p.args.reason, (payload && payload.sessionId) || (p.args && p.args.sessionId));+ } if (payload && ACTIVITY.has(p.verb) && !p._internal) { if (payload.sessionId) touchBadge(payload.sessionId, p.profileKey); else touchProfileBadge(p.profileKey);
native-browser-bridge-manifest.json+4−4@@ -1,8 +1,8 @@ { "manifest_version": 1,- "version": "0.1.46",- "url": "https://wiki.adom.inc/download/adom/adom-browser-extension/0.1.46/native-browser-bridge-v0.1.46.zip",- "sha256": "bd12662cad4b58d66d70e61fa78d1448203b6230ba8179043df05edce4b0d730",- "size": 95531,+ "version": "0.1.47",+ "url": "https://wiki.adom.inc/download/adom/adom-browser-extension/0.1.47/native-browser-bridge-v0.1.47.zip",+ "sha256": "0bd172374958728330cc9e9bce6f01fe223a0962d69b7601cd4749bb4775b826",+ "size": 95930, "released_at": "2026-07-22T00:00:00Z" }