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
The fg-steal guard is REMOVED, same day it shipped (John: 'if the user clicks that windows taskbar icon to bring it to the fg, you MUST do it immediately - forcing it to the background for 20 seconds is RUDE AS FUCK'). He is right, and the SOLVED PLAYBOOK in dev-skills/pup-bridge-dev already prohibited exactly this - 'do NOT add any loop that re-parks after the initial park; every such loop, no matter how clever (input-timing, short windows, stability watches), eventually yanks a real user's click; John proved each variant failed' - I built 1.9.252 without re-reading it. Removed: the guard interval, the guard arming in reassertBottomAfterNav, the 3-strike logic, and the osRestoreForeground AttachThreadInput helper (the playbook says new code never needs it: with --no-startup-window the browser is a background process and the OS itself denies it the foreground - and the live measurement agreed). KEPT, because they fix the disruption at the SOURCE per the playbook: the hwnd-targeted background call (BestWindow picked the largest window of the process instead of the session's own - the wrong-window 'backgrounded' lie), and a diagnostic-only focus measurement at window creation that logs ONLY if the playbook assumption ever breaks. The IRON LAW (bg always; a user's taskbar click wins immediately and unconditionally) is now recorded verbatim at the top of the playbook with the incident log.
4 files changed
+11−94
adom-bridge-puppeteer-manifest.json+4−4@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "puppeteer",- "version": "1.9.253",- "url": "https://wiki.adom.inc/download/adom/adom-desktop-puppeteer-bridge/1.9.253/adom-bridge-puppeteer-v1.9.253.zip",- "sha256": "55d98b473a5acba1b863d63530ca75d869ddb9442f73c7f2f4029ca8cfd6ae01",- "size": 472458,+ "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, "verbPrefixes": [ "browser_", "desktop_recorder_",
src/BRIDGE_VERSION+1−1@@ -1 +1 @@-1.9.253+1.9.254
src/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "puppeteer", "displayName": "Puppeteer (Chrome for Testing + Native)",- "version": "1.9.253",+ "version": "1.9.254", "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+5−88@@ -5786,19 +5786,6 @@ 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.252 (John: "why did that pup window that just opened to the adom-theme disrupt me in the- // foreground?"): arm the FG-STEAL GUARD. Chrome can self-raise a parked background window on late- // paints or an in-page focus() AFTER these scripted re-asserts finish, and under minimal-touch the- // old watcher that would notice was disabled - so the steal stuck and the user got disrupted. For- // 20s after any background open/navigation, the guard interval below knocks back any un-granted- // foreground takeover the moment it is seen.- try {- const _gs = sessions.get(sessionId);- if (_gs && !_gs._foreground) {- if (!_gs._fgGuardUntil || _gs._fgGuardUntil < Date.now()) _gs._fgStealCount = 0; // fresh arm resets the strike count- _gs._fgGuardUntil = 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@@ -6681,40 +6668,6 @@ async function getOrLaunchBrowser(profileName, { freshProfile = false, strictPer // always makes a tab; only CDP Target.createTarget accepts newWindow. Page identity is resolved by // diffing browser.pages() before/after rather than matching internal target ids, so this stays // correct across puppeteer versions. Returns null if the window never materialises.-function osRestoreForeground(hwnd) {- // Give the USER's window its focus back (SetForegroundWindow needs the AttachThreadInput trick- // when called from a background process). Only ever called with the user's prior-foreground hwnd.- if (process.platform !== 'win32' || !hwnd) return false;- try {- const scriptPath = path.join(require('os').tmpdir(), `_adom_restorefg_${process.pid}.ps1`);- const ps = [- 'Add-Type @"',- 'using System; using System.Runtime.InteropServices;',- 'public class WinRf {',- ' [DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();',- ' [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr h, out uint pid);',- ' [DllImport("user32.dll")] public static extern bool AttachThreadInput(uint a, uint b, bool f);',- ' [DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr h);',- ' [DllImport("user32.dll")] public static extern bool BringWindowToTop(IntPtr h);',- ' [DllImport("kernel32.dll")] public static extern uint GetCurrentThreadId();',- '}',- '"@',- `$t = [IntPtr]${Number(hwnd)}`,- '$fg = [WinRf]::GetForegroundWindow()',- 'if ($fg -eq $t) { Write-Output "already"; exit }',- '$p1 = 0; $tid1 = [WinRf]::GetWindowThreadProcessId($fg, [ref]$p1)',- '$me = [WinRf]::GetCurrentThreadId()',- '[void][WinRf]::AttachThreadInput($me, $tid1, $true)',- '[void][WinRf]::SetForegroundWindow($t)',- '[void][WinRf]::BringWindowToTop($t)',- '[void][WinRf]::AttachThreadInput($me, $tid1, $false)',- 'Write-Output "restored"',- ].join(String.fromCharCode(13, 10));- fs.writeFileSync(scriptPath, ps);- const out = runPsHidden(scriptPath, { timeout: 5000 }).trim();- return out === 'restored' || out === 'already';- } catch (e) { return false; }-} async function createPageInNewWindow(browser) { let cdp = null; try {@@ -6741,12 +6694,11 @@ async function createPageInNewWindow(browser) { } try { const nowFg = osGetForegroundWindow();- if (prevFg && nowFg && String(nowFg) !== String(prevFg)) {- const restored = osRestoreForeground(prevFg);- console.log(`[focus] MEASURED: createTarget(newWindow) STOLE the OS foreground (fg ${prevFg} -> ${nowFg}); restored to the user: ${restored}`);- } else {- console.log(`[focus] MEASURED: createTarget(newWindow) did NOT move the OS foreground (fg=${prevFg || '?'})`);- }+ // Diagnostic only. The playbook (dev-skills/pup-bridge-dev, SOLVED PLAYBOOK) says this can+ // never show a steal: with --no-startup-window the browser is a background process and the OS+ // denies it the foreground. Measured TRUE on 2026-07-31. If this line EVER logs a steal, the+ // playbook assumption broke - investigate, do not add re-park loops (they are banned there).+ if (prevFg && nowFg && String(nowFg) !== String(prevFg)) console.log(`[focus] MEASURED: createTarget(newWindow) moved the OS foreground (${prevFg} -> ${nowFg}) — PLAYBOOK ASSUMPTION BROKEN, investigate`); } catch (e) {} return fresh; } catch (e) {@@ -12484,41 +12436,6 @@ 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.252 FG-STEAL GUARD (ungated: user protection, not cosmetic machinery - the same doctrine- // as the foreground caption). A background pup window must NEVER sit in the OS foreground without- // a granted foregroundReason. Chrome self-raises parked windows (late first paint, in-page- // focus()), and the fixed post-nav re-asserts can end before the last raise. While any session's- // guard is armed (20s after each background open/nav), one cheap foreground read per 2.5s tick- // catches the steal and bottoms the window immediately, giving the user their screen back.- // Steady-state (no guards armed) costs nothing. A user's own deliberate raise outside the guard- // window, or any granted raise (_foreground), is never touched.- setInterval(async () => {- if (process.platform !== 'win32') return;- try {- const now = Date.now();- const guarded = [...sessions].filter(([, s2]) => s2._fgGuardUntil && s2._fgGuardUntil > now && !s2._foreground && !s2._lostBrowser);- if (!guarded.length) return;- const fg = osGetForegroundWindow();- if (!fg) return;- for (const [sid2, s2] of guarded) {- try {- if (!s2._hwnd) { try { await resolveSessionHwndByBounds(s2, sid2); } catch (e) {} }- if (!s2._hwnd || String(fg) !== String(s2._hwnd)) continue;- if ((s2._fgStealCount || 0) >= 3) {- // three knockbacks in one guard window = a human is insisting; the human wins.- s2._fgGuardUntil = 0; s2._foreground = true;- console.log(`[fg-guard] "${sid2}" raised 3x inside one guard window — assuming the USER wants it up; backing off and marking foreground`);- continue;- }- console.log(`[fg-guard] "${sid2}" STOLE the OS foreground with no granted reason (Chrome self-raise) — bottoming it and returning the screen to the user`);- s2._fgStealCount = (s2._fgStealCount || 0) + 1;- await reassertBottomAfterNav(sid2);- 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