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
v2.0.315: Loop radar + the rest of the polling audit (John): every periodic loop is wrapped in trackedLoop (ticks/min vs declared cadence, time per tick, slow-tick and spam warnings in the log ring), every in-process Win32 call is counted, /status carries loops + win32Calls, and the dashboard shows a per-minute loop panel (actual/declared, red when a loop runs hot) plus the ab enum_windows rate, so moving work out of ab's activity log does not remove the radar. Merged the standalone 30s health loop into the 15s probe loop (one browser-death detector); the corner-pencil click now PUSHES through a page binding and the per-tab harvest poll is a 32s fallback; the 4-minute steady-state UIAutomation icon audit is retired (the churn-triggered audit stays).
4 files changed
+113−37
adom-bridge-puppeteer-manifest.json+5−5@@ -1,10 +1,10 @@ { "manifest_version": 1, "name": "puppeteer",- "version": "2.0.314",- "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.314/adom-bridge-puppeteer-v2.0.314.zip",- "sha256": "60cc05c65b2991704238f02141c8b0d3a0cbe1001fbfa74b6b503e22a33ecd3f",- "size": 1435036,+ "version": "2.0.315",+ "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.315/adom-bridge-puppeteer-v2.0.315.zip",+ "sha256": "5b19e10891ebc1be5f99b42f6e1757406069e09a7bc3fde94afc996c0d43f1c8",+ "size": 1437739, "verbPrefixes": [ "browser_", "desktop_recorder_",@@ -12,5 +12,5 @@ ], "healthEndpoint": "/status", "statusVerb": "browser_readiness",- "released_at": "2026-08-23T15:22:33.995719Z"+ "released_at": "2026-08-23T15:32:24.443153Z" }
src/BRIDGE_VERSION+1−1@@ -1 +1 @@-2.0.314+2.0.315
src/bridge.json+1−1@@ -2,7 +2,7 @@ "manifest_version": 1, "name": "puppeteer", "displayName": "Puppeteer (Chrome for Testing + Native)",- "version": "2.0.314",+ "version": "2.0.315", "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. pup_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. pup_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, pup_alert_window posts a native toast whose click focuses the window, pup_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-bridge", "author": "Adom Inc.",
src/server.js+106−30@@ -8,8 +8,46 @@ const path = require('path'); const win32 = require('./win32'); // v2.0.311: an in-memory ring of the identity/foreground log lines, exposed on /status as `log`, so // a birth or caption can be debugged from the cloud instead of guessing (AD keeps no bridge stdout on disk).+// v2.0.315 LOOP TRACKER (John: "if you're going to move those out of ab's activity log... you need+// a tracking mechanism so you can look at your logs and realize 'oh shit, i sure spam my own+// loops'"). Every periodic loop is wrapped in trackedLoop(name, cadenceMs, fn): ticks, time per+// tick, and ticks-per-minute are counted; a loop that ticks faster than 1.5x its declared cadence or+// whose tick exceeds 2s logs a [loops] warning; once a minute a [loops] summary (ticks per loop + in-+// process Win32 calls per function) lands in the ring and on /status.loops for the dashboard.+const _loopStats = new Map();+function trackedLoop(name, cadenceMs, fn) {+ const st = { name, cadenceMs, ticks: 0, minuteTicks: 0, minuteAt: Date.now(), totalMs: 0, maxMs: 0, lastMs: 0, lastAt: 0, slow: 0, warned: 0 };+ _loopStats.set(name, st);+ return async (...a) => {+ const t0 = Date.now();+ if (t0 - st.minuteAt > 60000) { st.minuteAt = t0; st.minuteTicks = 0; }+ st.ticks++; st.minuteTicks++; st.lastAt = t0;+ const expectedPerMin = Math.max(1, Math.ceil(60000 / cadenceMs));+ if (st.minuteTicks > expectedPerMin * 1.5 && Date.now() - st.warned > 60000) { st.warned = Date.now(); console.log(`[loops] WARNING "${name}" ticked ${st.minuteTicks}x in the last minute (declared cadence ${cadenceMs}ms = ${expectedPerMin}/min) — a loop is spamming`); }+ try { return await fn(...a); }+ finally {+ const ms = Date.now() - t0; st.lastMs = ms; st.totalMs += ms; if (ms > st.maxMs) st.maxMs = ms;+ if (ms > 2000) { st.slow++; console.log(`[loops] "${name}" tick took ${ms}ms (cadence ${cadenceMs}ms)`); }+ }+ };+}+function loopReport() {+ const out = {};+ for (const [n, st] of _loopStats) out[n] = { cadenceMs: st.cadenceMs, ticks: st.ticks, lastMin: (Date.now() - st.minuteAt <= 60000 ? st.minuteTicks : 0), avgMs: st.ticks ? Math.round(st.totalMs / st.ticks) : 0, maxMs: st.maxMs, lastMs: st.lastMs, slowTicks: st.slow, lastAt: st.lastAt };+ return out;+}+let _win32CallsPrev = {};+setInterval(() => {+ try {+ const r = loopReport();+ const now = win32.stats ? win32.stats() : {};+ const delta = {}; for (const k of Object.keys(now)) { const d = now[k] - (_win32CallsPrev[k] || 0); if (d) delta[k] = d; } _win32CallsPrev = { ...now };+ const parts = Object.entries(r).filter(([, v]) => v.lastMin).map(([k, v]) => `${k}=${v.lastMin}x/${v.avgMs}ms`);+ console.log(`[loops] last minute: ${parts.join(' ') || '(idle)'} | win32: ${Object.entries(delta).map(([k, v]) => `${k}=${v}`).join(' ') || 'none'} | ab enum=${_enumWindow && Date.now() - _enumWindow.at < 60000 ? _enumWindow.n : 0}`);+ } catch (e) {}+}, 60000); const _logRing = [];-{ const _orig = console.log.bind(console); console.log = (...a) => { try { const t = a.map(x => typeof x === 'string' ? x : JSON.stringify(x)).join(' '); if (/^\[(hwnd|birth|focus|foreground|caption|screenshot|settings|annotate|launch)\]/.test(t)) { _logRing.push(new Date().toISOString().slice(11, 23) + ' ' + t.slice(0, 300)); if (_logRing.length > 300) _logRing.shift(); } } catch (e) {} _orig(...a); }; } // v2.0.310: in-process Win32 (SetProp/GetProp/EnumWindows), no PowerShell+{ const _orig = console.log.bind(console); console.log = (...a) => { try { const t = a.map(x => typeof x === 'string' ? x : JSON.stringify(x)).join(' '); if (/^\[(hwnd|birth|focus|foreground|caption|screenshot|settings|annotate|launch|loops|visibility|health)\]/.test(t)) { _logRing.push(new Date().toISOString().slice(11, 23) + ' ' + t.slice(0, 300)); if (_logRing.length > 300) _logRing.shift(); } } catch (e) {} _orig(...a); }; } // v2.0.310: in-process Win32 (SetProp/GetProp/EnumWindows), no PowerShell const os = require('os'); const { execSync, execFileSync, execFile } = require('child_process'); // v1.8.29: run a PowerShell script file with NO console window (windowsHide) and no cmd@@ -1014,6 +1052,17 @@ function attachTab(session, sessionId, page, { makeActive = true, opener = 'user // vault grows the adom-you identity automatically (John authorized). Self-guarded, page-local. // A synchronous Puppeteer binding stores the cred BEFORE the submit navigates away (a // navigation would wipe the page global); the sweep-harvested global is the SPA fallback.+ // v2.0.315: the corner-pencil click PUSHES (binding) instead of being polled every 4s per tab.+ page.exposeFunction('__pupFloatyPush', (tabHint) => {+ try {+ const sF = sessions.get(sessionId); if (!sF) return;+ if (sF._annotateArmed && Date.now() - sF._annotateArmed < 4000) return;+ sF._annotateArmed = Date.now();+ const tF = (sF.tabs || []).find(t => t && t.page === page);+ console.log(`[floaty] "${sessionId}" pencil clicked (push) — arming capture watcher on ${tF ? tF.tabId : '?'}`);+ startAnnotation(sF, sessionId, 'user-floaty', { tool: 'pen', tabId: tF ? tF.tabId : null }).catch(() => {});+ } catch (e) {}+ }).catch(() => {}); page.exposeFunction('__pupCredCapture', (json) => { (async () => { try {@@ -5321,7 +5370,8 @@ function scheduleIconAudit(reason, delayMs = 25000) { global.__auditTimer = setTimeout(() => { auditTaskbarIcons(reason).catch(() => {}); }, delayMs); } if (process.platform === 'win32') {- setInterval(() => { if (sessions.size) auditTaskbarIcons('steady-state').catch(() => {}); }, 4 * 60 * 1000);+ // v2.0.315 (audit): the 4-minute steady-state audit (one UIAutomation PowerShell crawl of the whole+ // taskbar, forever) is retired; the audit still runs 25s after every identity churn via scheduleIconAudit. } // v2.0.235: the overlayBadges apply loop existed TWICE (dashboard set-setting + pup_configure). // A progress-tracker edit landed in both copies but its variable was declared in only one, so every@@ -7525,7 +7575,7 @@ const FLOATY_JS = `(() => { }, 600); } function tipHide() { clearTimeout(timer); tip.style.opacity = '0'; tip.style.transform = 'translateY(4px)'; } btn.onmouseenter = tipShow; btn.onmouseleave = tipHide;- btn.onclick = function () { tipHide(); btn.style.borderColor = '#00d4cb'; if (window.__pupStartAnnotate) { window.__pupStartAnnotate('pen'); } window.__pupFloatyClick = Date.now(); setTimeout(function () { btn.style.borderColor = 'rgba(0,184,177,.45)'; }, 1500); };+ btn.onclick = function () { tipHide(); btn.style.borderColor = '#00d4cb'; if (window.__pupStartAnnotate) { window.__pupStartAnnotate('pen'); } window.__pupFloatyClick = Date.now(); if (typeof window.__pupFloatyPush === 'function') { try { window.__pupFloatyPush('btn'); window.__pupFloatyClick = null; } catch (e) {} } setTimeout(function () { btn.style.borderColor = 'rgba(0,184,177,.45)'; }, 1500); }; // drag by grip (floating-windows skill: visible grip, viewport-clamped); a drag PINS it (function () { var drag = null;@@ -7550,7 +7600,7 @@ const FLOATY_JS = `(() => { if (e.altKey && e.shiftKey && !e.ctrlKey && !e.metaKey && (e.key === 'A' || e.key === 'a')) { e.preventDefault(); e.stopPropagation(); if (window.__pupStartAnnotate) { window.__pupStartAnnotate('pen'); }- window.__pupFloatyClick = Date.now();+ window.__pupFloatyClick = Date.now(); if (typeof window.__pupFloatyPush === 'function') { try { window.__pupFloatyPush('hotkey'); window.__pupFloatyClick = null; } catch (e) {} } } }, true); })()`;@@ -8534,6 +8584,8 @@ function statusCore() { enumWindows: { total: _enumCalls, lastMin: (Date.now() - _enumWindow.at < 60000 ? _enumWindow.n : 0) }, win32: { inProcess: win32.available(), why: win32.why() }, // v2.0.310: koffi loaded? (else PowerShell fallback) log: _logRing.slice(-120),+ loops: loopReport(),+ win32Calls: win32.stats ? win32.stats() : {}, // the CURRENT identity mode, so a test harness asserts against what pup IS, not an assumption settings: { taskbarMode: st.aumidIcons !== 'on' ? 'plain' : (st.threadTiles === 'on' ? 'tiles' : 'aumid'),@@ -9289,7 +9341,7 @@ async function highFpsDowngrade(sessionId, why) { return true; } catch (e) { console.log(`[highfps] "${sessionId}" downgrade failed: ${e.message}`); return false; } }-setInterval(() => {+setInterval(trackedLoop('highfps-lease-janitor', 30000, () => { const now = Date.now(); for (const [sid, rec] of [..._highFps]) { if (!sessions.has(sid)) { _highFps.delete(sid); continue; }@@ -9306,7 +9358,7 @@ setInterval(() => { console.log(`[highfps] "${sid}" lease expires in ~${Math.ceil(left / 60000)} min — call pup_highfps_extend {sessionId,minutes} to keep it, or start a recording (a running recording holds it open indefinitely)`); } }-}, 30000);+}), 30000); // v2.0.285 RENDER LEASE / focus-emulation blank-window fix: REVERTED. Emulation.setFocusEmulationEnabled // (the lever it used to keep an occluded window's page painted) ACTIVATES the OS window — it@@ -11391,7 +11443,9 @@ async function withTimeout(promise, ms, label) { // // - Stale-file cleanup still runs (PID dead → file deleted) so we don't // accumulate junk for Chromes that genuinely crashed.-setInterval(async () => {+// v2.0.315 (audit): the standalone 30s health loop is folded into the 15s probe loop below+// (every other tick), so there is ONE browser-death detector instead of two.+async function healthLegacyTick() { for (const [profileName, be] of browsers) { if (!be.browser.isConnected()) { console.log(`[health] browser for profile "${profileName}" disconnected — attempting reconnect`);@@ -11435,7 +11489,7 @@ setInterval(async () => { deleteSessionFile(sf.sessionId); } }-}, 30_000);+} // v1.0.16: the single source of truth for per-verb HINTS — used BOTH by // `pup_describe` (so AD's Verbs tab shows hint/related/pitfalls under each@@ -12236,7 +12290,7 @@ async function dashboardSnapshot() { let depsOk = false; try { require.resolve('puppeteer'); depsOk = true; } catch (e) {} return { ts: now,- bridge: { version: BRIDGE_VERSION, platform: process.platform, uptimeMs: Math.round(process.uptime() * 1000), depsOk, minimalTouch: minimalTouch(), flashedPending: _flashedHwnds.size, settings: pupSettings() },+ bridge: { version: BRIDGE_VERSION, platform: process.platform, uptimeMs: Math.round(process.uptime() * 1000), depsOk, minimalTouch: minimalTouch(), flashedPending: _flashedHwnds.size, settings: pupSettings(), loops: loopReport(), win32Calls: (win32.stats ? win32.stats() : {}), win32InProcess: win32.available(), abEnumLastMin: (_enumWindow && Date.now() - _enumWindow.at < 60000 ? _enumWindow.n : 0) }, counts: { windows: wins.length, tabs: wins.reduce((a, w) => a + w.tabCount, 0), browsers: browsers.size, threads: threads.length }, threads, profiles, taskbarButtons: [...buttons.values()],@@ -12296,6 +12350,12 @@ select,input{background:#111b23;color:#e6edf3;border:1px solid #23303c;border-ra .btn.danger{border-color:#5c2b2b;color:#ff9d95} .btn.danger:hover{background:#2a1717;border-color:#f85149} .sub{margin-left:auto;color:#7d8f9f;font-size:12px}+.loops{margin:18px 0 6px;padding:10px 12px;border:1px solid #30363d;border-radius:8px;background:#161b22;font:11.5px ui-monospace,monospace;color:#8b949e}+.lpt{color:#7d8f9f;margin:2px 0 6px}+.lp{display:inline-block;margin:0 10px 6px 0;padding:2px 7px;border:1px solid #30363d;border-radius:6px;color:#c9d1d9}+.lp b{color:#00b8b1}+.lp.hot{border-color:#f85149;color:#f85149}+.lp.hot b{color:#f85149} /* taskbar strip: mimic of the real Windows taskbar */ #toasts{position:fixed;bottom:18px;left:50%;transform:translateX(-50%);z-index:99999;display:flex;flex-direction:column-reverse;gap:8px;align-items:center}@@ -12544,6 +12604,7 @@ footer{color:#5c6c7a;font-size:11px;padding:8px 18px 22px} </div> <div id="taskbar"></div> <main id="m"><div class="empty">connecting...</div></main>+<div id="loops" class="loops"></div> <footer id="ft"></footer> <script> const _q=s=>document.querySelector(s); let SNAP=null; const COLL=new Set();@@ -12666,7 +12727,16 @@ function render(){ ).join('')+'</div></div></div>'; }).join('')+'</div></div>'; }).join(''):'<div class="empty">no pup windows open'+(th||q?' matching this filter':'')+'</div>';- _q('#ft').textContent='bridge up '+ago(d.bridge.uptimeMs)+' \u00b7 '+d.bridge.platform+' \u00b7 deps '+(d.bridge.depsOk?'ok':'MISSING');+ _q('#ft').textContent='bridge up '+ago(d.bridge.uptimeMs)+' \u00b7 '+d.bridge.platform+' \u00b7 deps '+(d.bridge.depsOk?'ok':'MISSING')+' \u00b7 win32 '+(d.bridge.win32InProcess?'in-process':'powershell fallback');+ // v2.0.315 LOOP RADAR (John): the periodic loops and in-process Win32 calls no longer show in ab's+ // activity log, so they are shown HERE, per minute, with the declared cadence next to each so a+ // spamming loop stands out (ticks/min far above 60000/cadence).+ try{+ const lp=d.bridge.loops||{}; const w=d.bridge.win32Calls||{};+ const rows=Object.entries(lp).map(([n,v])=>{const exp=Math.max(1,Math.ceil(60000/v.cadenceMs));const hot=v.lastMin>exp*1.5;return '<span class="lp'+(hot?' hot':'')+'" title="declared cadence '+v.cadenceMs+'ms = '+exp+'/min; avg '+v.avgMs+'ms, max '+v.maxMs+'ms, '+v.ticks+' ticks total, '+v.slowTicks+' slow">'+n+' <b>'+v.lastMin+'</b>/'+exp+'</span>';}).join('');+ const wc=Object.entries(w).map(([n,v])=>n+'='+v).join(' ');+ _q('#loops').innerHTML='<div class="lpt">loops, ticks in the last minute (actual/declared)</div>'+rows+'<div class="lpt">in-process win32 calls since boot: '+(wc||'none')+' \u00b7 ab enum_windows last minute: '+(d.bridge.abEnumLastMin||0)+'</div>';+ }catch(e){} } function toast(msg,type){ const t=document.createElement('div');t.className='toast '+(type||'info');t.textContent=msg;@@ -19013,10 +19083,10 @@ const _onPupBound = async () => { // v1.9.47: reclaim on boot (catches windows orphaned by a crash/restart) and then as a slow safety // net, so an unbranded window can never persist even if its window-opened event was missed. setTimeout(() => { reclaimUnmanagedWindows().catch(() => {}); }, 12000);- setInterval(() => { if (minimalTouch()) return; reclaimUnmanagedWindows().catch(() => {}); }, 90000);+ setInterval(trackedLoop('reclaim-unmanaged', 90000, () => { if (minimalTouch()) return; return reclaimUnmanagedWindows().catch(() => {}); }), 90000); // v1.9.279: the reconciler runs UNGATED (bookkeeping + identity only, never a process kill), so // closed windows are reaped and stamp/settings drift is corrected within seconds, in every mode.- setInterval(() => { reconcileModel('periodic').catch(() => {}); }, 8000);+ setInterval(trackedLoop('reconcile-model', 8000, () => reconcileModel('periodic').catch(() => {})), 8000); // v2.0.161 (John: AUMID icons need a JANITOR, not just close-time cleanup): sweep orphaned // Adom.Pup.* registry keys + prior-generation .ico caches on a slow cadence, so leaks from // generation bumps, renamed/closed threads, and crashed windows never accumulate over a long@@ -19024,7 +19094,7 @@ const _onPupBound = async () => { // sweep runs every 5 min and reaps even when nothing is live (an empty map long after boot means // the keys are genuinely orphaned, and a re-stamp self-heals the rare reclaim race). reg.exe is // only spawned every 5 min, so it adds no ab-log noise.- setInterval(() => { try { pruneStaleAumids(); } catch (e) {} }, 300000);+ setInterval(trackedLoop('prune-stale-aumids', 300000, () => { try { pruneStaleAumids(); } catch (e) {} }), 300000); // Build the notched category tiles once at boot (John's clean-backdrop-for-the-overlay idea). // Fire-and-forget: pupIconPath serves the un-notched tile until this finishes, then it re-stamps. ensureNotchedIcons().catch(() => {});@@ -19036,7 +19106,7 @@ const _onPupBound = async () => { // reconnects, and every rescan already reconciles inline — the patrol is just a backstop, and a // hot one multiplies any probe-judgement bug across every window (today's lesson). setTimeout(() => { for (const [sid, s] of [...sessions]) reconcileSessionTabs(s, sid).catch(() => {}); }, 8000);- setInterval(() => { if (minimalTouch()) return; for (const [sid, s] of [...sessions]) reconcileSessionTabs(s, sid).catch(() => {}); }, 300000);+ setInterval(trackedLoop('reconcile-session-tabs', 300000, () => { if (minimalTouch()) return; for (const [sid, s] of [...sessions]) reconcileSessionTabs(s, sid).catch(() => {}); }), 300000); // v1.9.171 — CRASH SELF-HEAL. The shared Chrome dies two ways on heavy/hostile pages (measured // repeatedly on the Autodesk sign-in flow), and pup must recover from BOTH without a human: //@@ -19070,7 +19140,7 @@ const _onPupBound = async () => { // it does not forbid resurrecting DEAD windows to restore the user's state - that restoration is // exactly what the user wants after a sleep. (Hung-Chrome teardown, which DOES kill, stays gated.) let _lastRecoveryTick = Date.now();- setInterval(async () => {+ setInterval(trackedLoop('sleep-wake-heal', 20000, async () => { if (process.platform !== 'win32' || pupSettings().sleepRecovery !== 'on') return; const now = Date.now(); const gap = now - _lastRecoveryTick;@@ -19108,7 +19178,7 @@ const _onPupBound = async () => { _healingIds.delete(sid); } }- }, 20000);+ }), 20000); // 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@@ -19117,7 +19187,7 @@ const _onPupBound = async () => { // (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 () => {+ setInterval(trackedLoop('notmine-caption-watch', 2500, async () => { // v2.0.292: fgWatch removed (its foreground-STEAL purpose died with off-screen birth; it was // caption-only, a 20s-scoped duplicate of notMineCaption). The 20s self-foreground caption now // honors notMineCaption — the single control for "a window fronted itself, Adom did not do it".@@ -19157,14 +19227,14 @@ const _onPupBound = async () => { } catch (e) {} } } catch (e) {}- }, 2500);+ }), 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 // ALLOWED in minimal-touch (v1.9.191, gentle branding), so enforcement gets its OWN interval with no // minimal-touch gate. 10s cadence: a window is never knowingly bare for more than ~10s.-setInterval(async () => {+setInterval(trackedLoop('overlay-enforcer', 4000, async () => { if (process.platform !== 'win32') return; // v2.0.67: floaty-click harvest runs BEFORE the identity gate (the pencil must work even when // identity stamping is unsupported) and heartbeats so a dead sweep is visible in the log.@@ -19176,7 +19246,10 @@ setInterval(async () => { // owner, so the in-page code returns immediately). At ~10 tabs that was 7.5 renderer wakeups per // second, forever, on an idle machine. Now: ONE evaluate per tab returning both harvests, the // dead title sweep deleted, and tabs are skipped entirely when neither feature can fire.- try {+ // v2.0.315: both harvests now arrive by BINDING (push) the instant they happen; this poll is only+ // the fallback for a page that somehow lacks the bindings, so it runs every 8th tick (~32s).+ global.__floatyPollTick = (global.__floatyPollTick || 0) + 1;+ if (global.__floatyPollTick % 8 === 0) try { const _capOn = pupSettings().captureLogins !== 'off'; for (const [sidF, sF] of [...sessions]) { if (sF._lostBrowser || !(sF.tabs || []).length) continue;@@ -19276,9 +19349,12 @@ setInterval(async () => { }).catch(() => {}); } } catch (e) {}- }, 4000); // v1.9.329: 4s sweep (was 10s) — a bare window is now a visible notch-hole; heal it fast+ }), 4000); // v1.9.329: 4s sweep (was 10s) — a bare window is now a visible notch-hole; heal it fast - setInterval(async () => {+ let _healthTick = 0;+ setInterval(trackedLoop('health+user-activity', 15000, async () => {+ _healthTick++;+ if (_healthTick % 2 === 0) { try { await healthLegacyTick(); } catch (e) {} } // the old 30s loop's duties if (process.platform !== 'win32') return; // v1.9.185 minimal-touch: NEVER kill or relaunch the user's Chrome behind their back. John: // "you're just slapping monitoring on top of it so you can fucking restart it on me which is@@ -19339,19 +19415,19 @@ setInterval(async () => { } } } catch (e) {}- }, 15000);+ }), 15000); // v1.9.166: taskbar-progress reconciler — clear any bar that is lit but shouldn't be. // v1.9.181: only for sessions whose bar is ACTUALLY lit (_progressSince set). The old loop fired an // AD desktop_taskbar call (a PowerShell round-trip on the desktop) for EVERY session EVERY 20s // forever — with 10 windows that was 30 pointless PS invocations a minute pounding the machine pup // is supposed to stay invisible on. Steady-state is now zero calls.- setInterval(async () => {+ setInterval(trackedLoop('progress-reconciler', 20000, async () => { if (process.platform !== 'win32' || taskbarActivityMode() !== 'progress') return; // v2.0.292 for (const [sid, sess] of [...sessions]) { const st = _busyState.get(sid); if ((!st || !st.busy) && sess && sess._progressSince) { try { await setSessionProgress(sid, false); } catch (e) {} } }- }, 20000);+ }), 20000); // v1.9.145 (John found v21-review stranded at -32000 all day) — PLACEMENT SWEEP. The healer // (healOffscreenWindow) existed but only ran from a few verb paths, so a long-lived window that // predated the on-screen park kept its off-screen geometry FOREVER — made worse by the reparent:@@ -19492,7 +19568,7 @@ public static class VS { // unopenable window is not a safety net, it is the bug with a longer fuse. One PowerShell per // 30s across the whole population is cheap next to that. setTimeout(() => { sweepVisibility('startup').catch(() => {}); }, 8000);- setInterval(() => { sweepVisibility('periodic').catch(() => {}); }, 30000);+ setInterval(trackedLoop('visibility-sweep', 30000, () => sweepVisibility('periodic').catch(() => {})), 30000); // v1.9.89: reap ORPHANED pup chrome/renderers — any process under a pup-profiles dir whose profile // has no live session (a previous bridge crash left them, or renderers that outlived a close and are@@ -19503,19 +19579,19 @@ public static class VS { // v2.0.230: skip the PowerShell process enumeration entirely when there is nothing that could // have leaked (no live sessions AND no session files on disk) — it was ~20 PowerShell spawns an // hour on a totally idle machine.- setInterval(() => {+ setInterval(trackedLoop('reap-orphan-chromes', 180000, () => { if (!sessions.size) { try { if (!listSessionFiles().length) return; } catch (e) {} } reapOrphanPupChromes().catch(() => {});- }, 180000);+ }), 180000); // v2.0.129: reap LEAKED isolated profile DIRECTORIES (iso-* throwaways whose window is long gone). // Boot pass waits 60s so session recovery has fully settled first (never delete a profile an iso // session is mid-recovery on); then a slow 15-min net drains the backlog a few at a time. setTimeout(() => { reapLeakedIsolatedProfiles().catch(() => {}); }, 60000);- setInterval(() => {+ setInterval(trackedLoop('reap-leaked-profiles', 900000, () => { if (!sessions.size) { try { if (!listSessionFiles().length) return; } catch (e) {} } reapLeakedIsolatedProfiles().catch(() => {});- }, 15 * 60 * 1000);+ }), 15 * 60 * 1000); // (audit 2.0.314) the 5-minute stale-session-file cleanup was a byte-for-byte duplicate of the // tail of the 30s health loop; deleted.