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
Foreground captions on EVERY raise path + the NO-FAULT WATCH (two John additions to the iron law). (1) 'if a user asks to fg, you show a caption for 2 seconds explaining the reason. do you do that? cuz i asked you 100 times' - the browser_raise verb captioned, but the FIVE other raise paths routed through osRaiseSessionWindow (wiki view toggle, sign-in raise, wiki auto-login, heal-retry, assisted-click) captioned NOTHING. Now enforced at the choke point: osRaiseSessionWindow takes a reason and captions it for 2s; all five call sites carry genuine reasons; assisted_click passes null since it narrates its own captions. A new raise path without a caption is now definitionally a bug. Also honest context: until 1.9.244 this morning, even the paths that DID caption were showing for ~30s not 2s because the expiry never reached AD (the duration parameter rename) - all 100 asks were being betrayed by one wrong parameter name. (2) The NO-FAULT WATCH ('if the window does get foreground by chrome itself or a focus or some other reason, you watch it for 20 seconds and put up a caption saying it was not my fault'): for 20s after each background open/nav, pup polls the OS foreground only while armed; if the window surfaces without a pup raise it captions 'This window was NOT foregrounded by Adom: JavaScript, a focus change, Chrome itself, or your own click raised it. Not me!' for 2s, marks it foreground (iron law: whatever raised it, it stays up), and backs off. CAPTION ONLY, never a knockback - the knockback variant was 1.9.252, killed same day. Both recorded as clauses 3 and 4 of the IRON LAW in dev-skills/pup-bridge-dev.
4 files changed
+49−12
adom-bridge-puppeteer-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "puppeteer",- "version": "1.9.254",- "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.254/adom-bridge-puppeteer-v1.9.254.zip",- "sha256": "e1fd62378a0cbe4ad3f6312c6218f119a31d0b0f8ad02c90f37d7beca190fd8c",- "size": 470998,+ "version": "1.9.255",+ "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.255/adom-bridge-puppeteer-v1.9.255.zip",+ "sha256": "c45a4ecc84b800ac3857a3faf9f154c83aacfb3e117135dd8d2d6a78bcf65616",+ "size": 472066, "verbPrefixes": [ "browser_", "desktop_recorder_",
src/BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.254+1.9.255
src/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "puppeteer", "displayName": "Puppeteer (Chrome for Testing + Native)",- "version": "1.9.254",+ "version": "1.9.255", "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+43−6@@ -4426,7 +4426,13 @@ async function wikiCaption(text, expiresInMs) { // at, which is why John "never saw the switch work." CDP place+maximize → blur → bringToFront → // clear WS_EX_NOACTIVATE + SetForegroundWindow (via the detached/browser PID, with a WMI child // fallback for launcher→browser PID relaunches). Best-effort throughout; never throws.-async function osRaiseSessionWindow(session, sessionId) {+async function osRaiseSessionWindow(session, sessionId, reason) {+ // v1.9.255 (John: "if a user asks to fg, you show a caption for 2 seconds explaining the reason.+ // do you do that? cuz i asked you 100 times"): the browser_raise verb captioned, but the five+ // OTHER raise paths routed here and captioned NOTHING. Enforce at the choke point: every raise+ // through this function captions its reason for 2s. Callers that already narrate (assisted_click)+ // pass reason=null to avoid a double caption.+ try { if (reason) captionForegroundReason(sessionId, reason, 'osRaiseSessionWindow').catch(() => {}); } catch (e) {} try { try { const cdp = await session.page.target().createCDPSession();@@ -5786,6 +5792,8 @@ async function parkSessionWindowDirect(page, sessionId) { // batch sat on top with honest 'backgrounded' verdicts; a later same-window navigate does NOT // raise — tested). force:false so AD skips a window the user is actively holding foreground. async function reassertBottomAfterNav(sessionId) {+ // v1.9.255 (John): arm the NO-FAULT WATCH - see the fgWatch interval. Caption-only, never a knockback.+ try { const _gs = sessions.get(sessionId); if (_gs && !_gs._foreground) _gs._fgWatchUntil = Date.now() + 20000; } catch (e) {} 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@@ -9691,7 +9699,7 @@ const server = http.createServer(async (req, res) => { updateThumbnailTooltip(session, sessionId).catch(() => {}); session._foreground = true; try { await session.page.bringToFront(); } catch (e) {}- try { await osRaiseSessionWindow(session, sessionId); } catch (e) {}+ try { await osRaiseSessionWindow(session, sessionId, 'you asked for the signed-in wiki view; you were signed in automatically'); } catch (e) {} wikiCaption(`Adom wiki: signed you in automatically${_autoWho && _autoWho !== 'authed' ? ` as ${_autoWho}` : ''}, no login needed`, 6000).catch(() => {}); console.log(`[wikiview] "${sessionId}" AUTO-SIGNED from AD desktop token (${_autoWho}) — no manual login`); sendJSON(res, { success: true, output: JSON.stringify({@@ -9707,7 +9715,7 @@ const server = http.createServer(async (req, res) => { session._wikiView = "authed"; session._foreground = true; try { await session.page.bringToFront(); } catch (e) {}- try { await osRaiseSessionWindow(session, sessionId); } catch (e) {} // user clicked the task — SHOW them the sign-in page+ try { await osRaiseSessionWindow(session, sessionId, 'you clicked Sign in: finish the one-time wiki sign-in here'); } catch (e) {} // user clicked the task — SHOW them the sign-in page try { await setWikiModeGlyph(session.page, "○ Public"); } catch (e) {} wikiCaption("\u{1F513} Adom wiki: finish the one-time sign-in HERE — you'll come back to your page signed in", 9000).catch(() => {}); // The hydrogen SSO often AUTO-COMPLETES: if the user's SSO session is already alive@@ -9834,7 +9842,7 @@ const server = http.createServer(async (req, res) => { // (full OS SetForegroundWindow raise, not the old CDP-only maximize that left it behind // the user's active window — the reason John "never saw the switch work"). Plus a taskbar // flash as a belt-and-suspenders notice in case Windows' foreground lock denies the raise.- await osRaiseSessionWindow(s2, sessionId);+ await osRaiseSessionWindow(s2, sessionId, 'you clicked the taskbar menu to switch this wiki view'); // v1.9.29 BUG FIX: VERIFY the window actually came back. John clicked the toggle, the old // window closed, captions fired, and NO window returned: the relaunch had happened but the // window stayed unfindable/off-screen (the identity stamp logged "No visible window"),@@ -9850,7 +9858,7 @@ const server = http.createServer(async (req, res) => { } catch (e) {} if (!_visible) { try { await healOffscreenWindow(s2, sessionId); } catch (e) {}- try { await osRaiseSessionWindow(s2, sessionId); } catch (e) {}+ try { await osRaiseSessionWindow(s2, sessionId, 'retrying: bringing the wiki window you clicked back on-screen'); } catch (e) {} await new Promise(r => setTimeout(r, 700 * (i + 1))); } }@@ -10833,7 +10841,7 @@ const server = http.createServer(async (req, res) => { await pupCaption(`Adom: ${_reason} I will bring this window up and WAIT until you are ready before clicking ${_label}.`, 9000); try { await chrome.adCommand('notify_user', { title: 'Adom needs one click', body: `${_label} — ${_reason} I will wait until you are on the window.`, focus: false }, { timeoutMs: 4000 }); } catch (e) {} session._foreground = true; session._lastForeground = { ts: Date.now(), verb: command, reason: _reason };- try { await osRaiseSessionWindow(session, resolvedId); } catch (e) {}+ try { await osRaiseSessionWindow(session, resolvedId, null); } catch (e) {} // assisted_click narrates its own captions const hwnd = await sessionHwnd(resolvedId); const ready = await waitForUserReady(resolvedId, hwnd, { maxMs: Number(args.maxWaitMs) || 120000 }); if (!ready) {@@ -12436,6 +12444,35 @@ server.listen(PORT, BIND_HOST, async () => { const _healingIds = new Set(); // sessionIds with a relaunch in flight (guards the 15s re-tick) const MAX_HEAL_ATTEMPTS = 2; const HEAL_COOLDOWN_MS = 5 * 60 * 1000;+ // v1.9.255 NO-FAULT WATCH (John: "if the window does get foreground by chrome itself or a focus+ // or some other reason, you do watch it for 20 seconds and put up a caption saying something like+ // 'it wasn't my fault, the javascript, or focus, or chrome caused the foreground. not me!'").+ // For 20s after each background open/nav, poll the OS foreground ONLY while a watch is armed+ // (zero steady-state cost). If the window surfaces without a granted foregroundReason: show the+ // disclaimer caption (2s), mark it foreground (the IRON LAW: whatever raised it, it stays up),+ // disarm. NEVER a knockback - that variant shipped as 1.9.252 and was rightly killed same day.+ setInterval(async () => {+ if (process.platform !== 'win32') return;+ try {+ const now = Date.now();+ const watched = [...sessions].filter(([, s2]) => s2._fgWatchUntil && s2._fgWatchUntil > now && !s2._foreground && !s2._lostBrowser);+ if (!watched.length) return;+ const fg = osGetForegroundWindow();+ if (!fg) return;+ for (const [sid2, s2] of watched) {+ try {+ if (!s2._hwnd) { try { await resolveSessionHwndByBounds(s2, sid2); } catch (e) {} }+ if (!s2._hwnd || String(fg) !== String(s2._hwnd)) continue;+ s2._fgWatchUntil = 0;+ s2._foreground = true; // iron law: it is up, it stays up+ console.log(`[fg-watch] "${sid2}" surfaced to the foreground with no pup raise — captioning the no-fault notice (no knockback)`);+ pupCaption(`This window was NOT foregrounded by Adom: JavaScript, a focus change, Chrome itself, or your own click raised "${sid2}". Not me!`, 2000, 'pup-nofault').catch(() => {});+ break;+ } catch (e) {}+ }+ } catch (e) {}+ }, 2500);+ // v1.9.243 (John: "you have red led's galore in your dashboard. why?"). The overlay ENFORCER was // dead on arrival: it sat inside the health interval ABOVE, whose first line is `if (minimalTouch()) // return;` — and minimal-touch is the normal mode, so it never ran once. Overlays are explicitly