app
Pup - the AI's own browser
Public Made by Adomby adom
pup gives your AI a real browser on your desktop that it drives completely, in the background, signed in as you: windows that never jump in front of your work, one shared profile that learns your logins, and every window labeled on the taskbar with its AI thread's name.
← Commit history
Show the foreground REASON on screen, and fix why a PDF window came to the front unasked. (1) John: 'every time you open a window in the foreground and you force the AI to give you a reason you should flash that on screen as an AD caption for 1 second' — every GRANTED foreground now captions the caller's justification on screen for ~1s, so the gate's audit trail is visible in real time instead of only in lastForeground. Deliberately not gated by minimalTouch: it is a user-protection signal, not cosmetic. (2) Root-caused his 5-PDF window arriving in the foreground although NO ai requested it and pup's own log said 'backgrounded': Chrome's PDF viewer activates the window on load, and BOTH of pup's safety nets were dead — my 1.9.186 minimal-touch change had dropped the park re-assert as 'redundant' (it is the net that catches exactly this), and reassertBottomAfterNav relied solely on AD's desktop_set_window_state, a verb absent on this desktop, so it silently no-opped. The re-assert is restored in minimal-touch, reassertBottomAfterNav now bottoms the window in-bridge via SetWindowPos with the AD verb only as fallback, and navigate re-asserts too. All paths still respect userIsUsingWindow so a window the user chose to have up is never yanked.
4 files changed
+48−9
adom-bridge-puppeteer-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "puppeteer",- "version": "1.9.200",- "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.200/adom-bridge-puppeteer-v1.9.200.zip",- "sha256": "98fb76e43dfd664ccce6f3bf9d46d257c6dd20b2e1d5a4e776f505ebb55cf02b",- "size": 432927,+ "version": "1.9.201",+ "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.201/adom-bridge-puppeteer-v1.9.201.zip",+ "sha256": "517353f5df2a76857da789c86b9dcd9b5e2d7fd9d4a460664a331be906f62085",+ "size": 434015, "verbPrefixes": [ "browser_", "desktop_recorder_",
src/BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.200+1.9.201
src/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "puppeteer", "displayName": "Puppeteer (Chrome for Testing + Native)",- "version": "1.9.200",+ "version": "1.9.201", "description": "Drives a browser ALREADY on the machine \u2014 installed Chrome, else Microsoft Edge (every Windows PC ships with Edge) \u2014 launched with a FRESH isolated profile, so the common case has NO ~150 MB Chrome-for-Testing download and it never touches the user's real tabs/logins. Binds its local server to LOOPBACK (127.0.0.1) only, so no Windows Firewall 'allow Node through the firewall' prompt ever pops. Chrome for Testing is fetched only as a LAST RESORT if the machine has no Chromium browser at all. SPAWN-VERIFIED fallback: pup tries each detected browser (Chrome \u2192 Edge \u2192 CfT) and actually launches it, falling through to the next if one won't start (and handling browsers like Edge that relaunch onto another PID), so a corrupt/partial install never hard-fails an open. The browser that launches is cached as the default (persisted across restarts) so it isn't re-probed each time. On spawn the bridge SELF-WARMS: detects browsers + disk, and only if the box has no Chrome/Edge and no cached CfT does it background-fetch CfT so the first open is instant. browser_use pins a specific browser (chrome | edge | cft | auto); {browser:'chrome',install:true} downloads & installs REAL Google Chrome (user-scoped, no admin) on an Edge-only box. browser_readiness (READ-ONLY) reports which browser will be used + the full candidate list + the default + free disk. Every failure returns a specific errorCode + _hint (chrome_install_no_disk, chrome_download_network, chrome_install_permission, chrome_stable_not_detected, \u2026). Launches with FULL browser permissions by default for full debug (strictPermissions:true opts out). Headful automation: open/close windows+tabs, navigate, screenshot, eval JS, record. pup is a GENERIC, non-profile browser only \u2014 every session is a fresh isolated Chrome/Edge process, never the user's real signed-in browser; profile defaults to sessionId. To drive the user's REAL Chrome/Edge (their profile, cookies, SSO), use the Adom browser EXTENSION's nbrowser_* verbs \u2014 a separate bridge, not pup's job. macOS (v1.9.74+): full parity with mac-native mechanics \u2014 background-by-default is the app-level hide (windows stay drivable/screenshottable while hidden), raise/lower map to unhide+activate/hide, browser_alert_window posts a native toast whose click focuses the window, browser_use {browser:'chrome',install:true} installs Chrome from the official dmg, and taskbar identity/badging remains Windows-only.", "homepage": "https://wiki.adom.inc/adom/adom-desktop", "author": "Adom Inc.",
src/server.js+42−3@@ -3300,6 +3300,20 @@ async function setSessionProgress(sessionId, busy) { // a window (a reveal / demo / 'show me'), fire an AD toast FIRST so the user's eyes are drawn to what // just appeared — never silently push a window onto their screen. Best-effort; debounced per session. const _fgToastAt = new Map();+// v1.9.201 (John): "every time you open a window in the foreground and you force the AI to give you+// a reason you should flash that on screen as an AD caption for 1 second". The foreground GATE already+// makes callers justify themselves and records the reason in lastForeground — but that audit trail was+// only visible if you went looking for it. Now the reason appears ON SCREEN the moment the window+// arrives, so the justification is accountable in real time. Deliberately NOT gated by minimalTouch:+// this is a user-protection signal, not cosmetic machinery.+async function captionForegroundReason(sessionId, reason, verb) {+ try {+ const why = String(reason || '').trim();+ if (!why) return;+ await pupCaption(`Adom brought "${sessionId}" to the front — reason given: "${why}"`, 1000, 'pup-fg-reason');+ } catch (e) {}+}+ async function toastForeground(sessionId, reason) { if (minimalTouch()) return; // v1.9.185 minimal-touch: no cosmetic/shell machinery pushed at Chrome try {@@ -5194,6 +5208,21 @@ async function parkSessionWindowDirect(page, sessionId) { // raise — tested). force:false so AD skips a window the user is actively holding foreground. async function reassertBottomAfterNav(sessionId) { if (process.platform !== 'win32' || !sessionId) return;+ // v1.9.201 (John's PDF window came to the front with NO ai requesting it): this used ONLY AD's+ // desktop_set_window_state — a verb that is NOT present on every desktop (the Bills thread hit+ // exactly that: "desktop_set_window_state is not in this desktop's verb set"). Where it is absent+ // this whole re-assert silently no-opped, so anything CHROME foregrounded on its own stayed on the+ // user's screen. Local PDFs are the proven trigger: Chrome's PDF viewer activates the window on+ // load. Do it IN-BRIDGE first (SetWindowPos to z-bottom, which never depends on an AD verb and+ // respects a user-foregrounded window), and keep the AD verb only as a secondary attempt.+ const sess = sessions.get(sessionId);+ if (sess && userIsUsingWindow(sess)) return; // never yank a window the user chose to have up+ let bottomed = false;+ try {+ const hwnd = await cachedSessionHwnd(sessionId);+ if (hwnd && osBottomUnlessForeground(hwnd, true) === 'bottomed') bottomed = true;+ } catch (e) {}+ if (bottomed) { console.log(`[park] "${sessionId}" (post-nav re-assert): bottomed`); return; } try { await chrome.adCommand('desktop_set_window_state', { titleContains: `(session: ${sessionId}`, state: 'bottom', force: false }, { timeoutMs: 5000 , caller: sessionCaller(sessions.get(sessionId))}); } catch (e) {}@@ -6189,9 +6218,13 @@ async function launchSession(sessionId, url, { freshProfile = false, profile, st }) .catch(e => { be.browser._bgResult = 'error'; console.log(`[park] "${sessionId}"${tag} failed: ${e.message}`); }); be.browser._bgPromise = doPark('');- // v1.9.185 minimal-touch: park ONCE. The re-assert existed to beat later Chrome/shell- // repositioning, but it is a second cross-process window mutation per open.- if (!minimalTouch()) setTimeout(() => { doPark(' (re-assert)'); }, 2500);+ // v1.9.201 — RE-ASSERT ALWAYS. My 1.9.186 minimal-touch change dropped this second park pass+ // as "a redundant window mutation". It is not redundant: it is the safety net that re-parks a+ // window CHROME ITSELF brought forward after the first park. Local PDFs are the proven case —+ // Chrome's PDF viewer activates the window on load, so John saw a 5-PDF window pop to the+ // front even though NO ai requested foreground and pup's own log said "backgrounded".+ // Keeping a window off the user's screen IS the point of minimal-touch, so this belongs in it.+ setTimeout(() => { doPark(' (re-assert)'); }, 2500); setTimeout(() => { brandPupWindow(sessionId).catch(() => {}); }, 1200); } } catch (e) {}@@ -8765,6 +8798,9 @@ const server = http.createServer(async (req, res) => { // three of John's foregrounded windows lost their overlay. Re-assert it after the raise. setTimeout(() => { updateTabCountBadge(session, args.sessionId || session._sessionId).catch(() => {}); }, 1200); try { recordAgentUpdate(session, command, { updateNote: `FOREGROUND: ${fgReason}` }, session.activeTabId); } catch (e) {}+ // v1.9.201: show the caller's justification on screen for ~1s, so a foreground is never+ // silent and John can see WHY it happened as it happens.+ captionForegroundReason(sessionId, fgReason, command).catch(() => {}); try { // v1.9.149 (John's audit: two of three raised windows came up 0,0 full-screen, not the // standard frame): the reveal must present the SAME work-area-minus-10px geometry the@@ -10114,6 +10150,9 @@ const server = http.createServer(async (req, res) => { } } tab.errors.length = 0;+ // v1.9.201: Chrome can foreground a window on its own during a navigation (local PDFs do+ // this reliably). Re-assert the park unless the user has this window up on purpose.+ if (!userIsUsingWindow(session)) setTimeout(() => { reassertBottomAfterNav(resolvedId).catch(() => {}); }, 1800); // Re-apply title suffix after navigation try { const pName = session.profileName;