← Commit history

v2.0.314: Polling audit (John): deleted the dead geometry/wiggle tail of the hwnd resolver (CDP bounds it never used, a lock never taken, the literal if(false) recapture block) and its dead state; the 30s visibility sweep now runs in-process through koffi instead of spawning a PowerShell + Add-Type every tick; the per-session CDP placement sweep (120s) is gone, subsumed by it; the duplicate 5-minute stale-session-file cleanup is gone (the 30s health loop already did it); per-tab settings enforcement (owner chip / title decor) runs on a settings change and once a minute instead of two page.evaluate per tab every 8s; removed-setting shims (agentActivity, openFlash) deleted.

John Lauer ·20ec4e23d7 ·2mo ago ·parent b5663f5
4 files changed +48−169
adom-bridge-puppeteer-manifest.json+5−5
@@ -1,10 +1,10 @@ {   "manifest_version": 1,   "name": "puppeteer",-  "version": "2.0.313",-  "url": "https://wiki.adom.inc/download/adom/pup-bridge/2.0.313/adom-bridge-puppeteer-v2.0.313.zip",-  "sha256": "ae0ef90237d1e2e33b95f3df6a54a66659a9287a29f3961c46564f9bbe870abf",-  "size": 1436748,+  "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,   "verbPrefixes": [     "browser_",     "desktop_recorder_",@@ -12,5 +12,5 @@   ],   "healthEndpoint": "/status",   "statusVerb": "browser_readiness",-  "released_at": "2026-08-23T15:18:11.864854Z"+  "released_at": "2026-08-23T15:22:33.995719Z" }
src/BRIDGE_VERSION+1−1
@@ -1 +1 @@-2.0.313\ No newline at end of file+2.0.314\ No newline at end of file
src/bridge.json+1−1
@@ -2,7 +2,7 @@   "manifest_version": 1,   "name": "puppeteer",   "displayName": "Puppeteer (Chrome for Testing + Native)",-  "version": "2.0.313",+  "version": "2.0.314",   "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+41−162
@@ -8216,7 +8216,6 @@ async function resolveSessionHwndByBounds(session, sessionId) {     } catch (e) {}     return null;   }-  let _releaseWiggle = null;   // v1.9.291: hoisted so the outer catch can always release the lock   try {     // v1.9.278: NEVER trust a cached hwnd blind - validate it still belongs to pup before reuse     // (a dead/recycled handle now owned by Edge is exactly how the Edge overlay happened).@@ -8334,140 +8333,33 @@ async function resolveSessionHwndByBounds(session, sessionId) {     } catch (e) {}     const page = (session.tabs || [])[0] && (session.tabs || [])[0].page;     if (!page) return null;-    // v1.9.290 UNIQUE-WIDTH WIGGLE: parked windows share the identical rect, so plain geometry can't-    // tell two same-profile windows apart (dup-hwnd bug). Give THIS window a UNIQUE width via CDP-    // (pup controls it directly — no page observer can fight it), match on that exact width, restore.-    let bounds = null, cdp = null, windowId = null, markerW = null, _widthChanged = false;-    try {-      cdp = await page.target().createCDPSession();-      ({ windowId } = await cdp.send('Browser.getWindowForTarget'));-      ({ bounds } = await cdp.send('Browser.getWindowBounds', { windowId }));-    } catch (e) { try { if (cdp) await cdp.detach(); } catch (x) {} return null; }-    if (!bounds || !Number.isFinite(bounds.width)) { try { if (cdp) await cdp.detach(); } catch (x) {} return null; }-    const be = browsers.get(session.profileName);-    const pid = be && be.browser && (be.browser._detachedPid || (() => { try { return be.browser.process()?.pid; } catch (e) { return null; } })());-    if (!pid) { try { if (cdp) await cdp.detach(); } catch (x) {} return null; }-    const _origW = bounds.width;-    // v1.9.291: SERIALIZE the wiggle. The enforcer fires applyAppOverlay (hence resolve) for many-    // sessions concurrently; if their width-changes + enumerations overlap, every session matches-    // whichever window is momentarily at a marker width — collapsing them onto one hwnd. Take a-    // global lock so exactly one wiggle (set width -> enumerate -> restore) runs at a time.-    // v1.9.308 (John: "why did you resize this window so weird? what the fuck?"): the width-WIGGLE is-    // DELETED. It shrank the window to a marker width to identify it, and when the restore failed it-    // left the window stuck narrow — visibly deforming John's window. NEVER acceptable. Handle-    // resolution now uses ONLY read-only signals: the birth-time capture, the persisted handle, and-    // the sole-window-of-a-profile check below — none of which touch window geometry. A window none of-    // those can resolve is left BARE (no overlay), which is fine; mangling the user's window is not.-    // markerW stays null, so the matching loop below is pure read-only geometry (no setWindowBounds).-    // v2.0.27: NATIVE enumeration (ab desktop_enum_windows) — the EnumWindows PowerShell, its-    // unique temp files, and the EBUSY class are all gone. Same read-only geometry doctrine as-    // v1.9.308 (the width-wiggle stays deleted; bare > wrong, forever).+    // v2.0.314 (audit after John: "analyze all the polling... delete old code we don't use"): the+    // geometry/wiggle tail is GONE (dev-skills/pup-no-wiggle). It opened a CDP session to read bounds+    // it never used, carried a lock never taken, and ended in a literal `if (false)` recapture block.+    // What remains is the ONE read-only, wiggle-free signal: if this profile's browser process owns+    // exactly one top-level window, that window is this session's. Otherwise leave it bare; the+    // property tag (set at birth) is how it gets resolved.     const rows = await enumChromeWidgetWindows();-    if (!rows.length) { if (_releaseWiggle) { try { _releaseWiggle(); } catch (e) {} } try { if (cdp) await cdp.detach(); } catch (e) {} return null; }-    // Every chrome.exe under this profile counts (renderers have no windows, so PID may be the parent-    // or a sibling in the tree — accept any, then disambiguate purely on geometry).-    // v1.9.302: collect ALL matching windows and demand EXACTLY ONE. The old code broke on the first-    // match, so an accidental collision silently won. If 0 or 2+ windows match, we cannot know which is-    // ours, so we LEAVE IT BARE rather than paint a wrong window (John's rule: bare > wrong, forever-    // after the Edge-overlay incident). A bare window self-heals on the next resolve; a wrong one lies.-    // GEOMETRY MATCHING DELETED (dev-skills/pup-no-wiggle, John 2026-08-22): matching windows by-    // identical bounds/width cannot disambiguate same-geometry windows on the shared profile and IS-    // the banned wiggle. Window identity is the property tag, resolved deterministically above.-    // `matches` stays empty so nothing is ever picked by geometry.-    const matches = new Set();-    try { if (_widthChanged) await cdp.send('Browser.setWindowBounds', { windowId, bounds: { width: _origW } }); } catch (e) {}-    if (_releaseWiggle) { try { _releaseWiggle(); } catch (e) {} }-    try { if (cdp) await cdp.detach(); } catch (e) {}-    // v1.9.303: the RELIABLE, wiggle-free path (the wiggle never worked on maximized windows — proven).-    // Windows owned by THIS profile's browser process ARE this profile's windows. If the profile has-    // exactly ONE such window, that IS this session's window, maximized or not — no geometry guessing.     const _profPids = pupBrowserPidSet(session.profileName);     const _profWins = new Set();     for (const r of rows) { if (r.h && _profPids.has(r.pid)) _profWins.add(r.h); }-    let resolved = null, how = '';-    if (!_geomBackoff && matches.size === 1) { resolved = [...matches][0]; how = markerW != null ? 'unique-width wiggle' : 'window bounds'; }-    else if (!_geomBackoff && _profWins.size === 1) { resolved = [..._profWins][0]; how = 'sole window of profile process (wiggle-free)'; }-    if (resolved && hwndOwnedByOtherSession(resolved, sessionId)) { console.log(`[hwnd] "${sessionId}" geometry picked ${resolved} but another session owns it — refusing`); resolved = null; }+    let resolved = (_profWins.size === 1) ? [..._profWins][0] : null;+    if (resolved && hwndOwnedByOtherSession(resolved, sessionId)) { console.log(`[hwnd] "${sessionId}" sole-window pick ${resolved} is owned by another session — refusing`); resolved = null; }     if (resolved) {       session._hwnd = resolved;       persistSessionHwnd(sessionId, resolved);-      console.log(`[hwnd] "${sessionId}" resolved OS handle ${resolved} — ${how}`);+      console.log(`[hwnd] "${sessionId}" resolved OS handle ${resolved} — sole window of profile process`);       return resolved;     }-    // v2.0.32: LAST-RESORT RE-CAPTURE for a bare BACKGROUND window (a window opened before the birth-    // fix, or re-attached by recovery/drag-out, so it has no __pupHwnd, on a crowded shared profile-    // where geometry cannot disambiguate — the raw-Chrome-icon case). Move THIS window's CDP target-    // to a UNIQUE far-off-screen slot, enumerate, grab the window now at that exact coordinate (its-    // identity by construction), then move it straight back on-screen at z-bottom. INVISIBLE because-    // the window is already background/z-bottom; NEVER done to a window the user is using. This gives-    // an on-screen window the same unambiguous handle a birth-time window gets.-    // OFF-SCREEN RE-CAPTURE NUDGE DELETED (dev-skills/pup-no-wiggle, John 2026-08-22): this MOVED the-    // window via setWindowBounds and identified it by "which window's rect changed" — the textbook-    // wiggle. It is banned. An untagged window on a multi-window profile is left BARE (no guess); it-    // becomes resolvable the instant it carries its property tag (every birth + every resolve tags it).-    if (false && !userIsUsingWindow(session) && (!session._recaptureAt || Date.now() - session._recaptureAt > 30000)) {-      session._recaptureAt = Date.now();-      try {-        const tab0 = (session.tabs || [])[0];-        const pg = tab0 && tab0.page;-        if (pg) {-          const cdp2 = await pg.target().createCDPSession();-          try {-            const { windowId } = await cdp2.send('Browser.getWindowForTarget');-            const gb = await cdp2.send('Browser.getWindowBounds', { windowId });-            const orig = gb && gb.bounds;-            // v2.0.44 (mimic-ralph: re-capture failed SILENTLY on every recovered window): the old-            // probe matched the ABSOLUTE -33000 position, but this box CLAMPS off-screen coordinates-            // (the same clamp that forced birth capture to set-diff in 2.0.38) — the moved window-            // never appears at -33000, so the probe never matched, said nothing, and left the window-            // wherever the clamp put it. Identify by bounds CHANGE instead: snapshot every candidate-            // row, move OUR window via CDP, and the one hwnd whose rect changed is ours no matter-            // where Windows actually placed it. Then restore the ORIGINAL rect exactly.-            const _before = new Map((await enumChromeWidgetWindows()).map(r => [r.h, `${r.L},${r.T},${r.W},${r.H}`]));-            const probeLeft = -33000 - (Math.floor(Date.now() / 7) % 500);   // still aim off-screen: invisible when NOT clamped-            await cdp2.send('Browser.setWindowBounds', { windowId, bounds: { left: probeLeft, top: -33000, width: 900, height: 700 } });-            let got = null;-            for (let i = 0; i < 8 && !got; i++) {-              const rr = await enumChromeWidgetWindows({ fresh: true });-              const _bpid2 = (() => { try { const be2 = browsers.get(session.profileName); return Number((be2 && be2.browser && (be2.browser._detachedPid || (be2.browser.process() && be2.browser.process().pid))) || 0) || null; } catch (e) { return null; } })();-              const moved = rr.filter(r => !hwndOwnedByOtherSession(r.h, sessionId) && _before.has(r.h) && _before.get(r.h) !== `${r.L},${r.T},${r.W},${r.H}`-                && (!_bpid2 || Number(r.pid) === _bpid2));-              if (moved.length === 1) got = moved[0].h;-              else if (moved.length > 1) { console.log(`[hwnd] "${sessionId}" re-capture saw ${moved.length} windows move at once — refusing (concurrent motion)`); break; }-              else await new Promise(r => setTimeout(r, 600));-            }-            if (!got) console.log(`[hwnd] "${sessionId}" re-capture probe: no unique moved window — leaving bare`);-            // restore the ORIGINAL rect exactly (never dump the window at 0,0 — that repositioning-            // was itself user-visible wonk when the clamp made the probe land on-screen)-            try { await cdp2.send('Browser.setWindowBounds', { windowId, bounds: orig && Number.isFinite(orig.width) ? { left: orig.left, top: orig.top, width: orig.width, height: orig.height } : { windowState: 'normal', left: 40, top: 40, width: 1200, height: 800 } }); } catch (e) {}-            if (got) {-              try { if (pg) pg.__pupHwnd = got; } catch (e) {}-              session._hwnd = got; persistSessionHwnd(sessionId, got);-              session._hwndFailStreak = 0; session._hwndBackoffUntil = 0;-              if (_releaseWiggle) { try { _releaseWiggle(); } catch (e) {} }-              try { if (cdp) await cdp.detach(); } catch (e) {}-              try { await cdp2.detach(); } catch (e) {}-              console.log(`[hwnd] "${sessionId}" resolved OS handle ${got} — off-screen RE-CAPTURE (background bare window, no birth handle)`);-              if (!userIsUsingWindow(session)) parkSessionWindowDirect(pg, sessionId).catch(() => {});-              return got;-            }-          } finally { try { await cdp2.detach(); } catch (e) {} }-        }-      } catch (e) { console.log(`[hwnd] "${sessionId}" re-capture probe failed: ${e.message}`); }-    }-    // Multiple windows on the profile and no unique wiggle match → we cannot know which is ours, so-    // leave it BARE rather than paint a guessed/shared handle (John's rule: bare > wrong). This is the-    // case the birth-time resolution (todo #5) will finally fix for multi-window profiles.     session._hwndFailStreak = (session._hwndFailStreak || 0) + 1;     if (session._hwndFailStreak >= 3) {       session._hwndBackoffUntil = Date.now() + 5 * 60000;-      console.log(`[hwnd] "${sessionId}" NOT resolved — ${_profWins.size} windows on this profile, ${matches.size} wiggle-matches; leaving bare + BACKING OFF resolution for 5 min (${session._hwndFailStreak} straight misses — stop hammering enumeration for an answer that isn't changing)`);+      console.log(`[hwnd] "${sessionId}" NOT resolved — ${_profWins.size} windows on this profile, no property tag; leaving bare + backing off for 5 min`);     } else {-      console.log(`[hwnd] "${sessionId}" NOT resolved — ${_profWins.size} windows on this profile, ${matches.size} wiggle-matches; leaving bare rather than guessing`);+      console.log(`[hwnd] "${sessionId}" NOT resolved — ${_profWins.size} windows on this profile, no property tag; leaving bare rather than guessing`);     }     return null;   } catch (e) {-    if (_releaseWiggle) { try { _releaseWiggle(); } catch (x) {} }     // v2.0.17: exceptions count toward the same backoff — the EBUSY spin was exactly this path looping.     session._hwndFailStreak = (session._hwndFailStreak || 0) + 1;     if (session._hwndFailStreak >= 3) session._hwndBackoffUntil = Date.now() + 5 * 60000;@@ -8795,7 +8687,13 @@ async function reconcileModel(reason) {     // reaches pages navigated AFTER a deploy/toggle, so existing windows had none. Keep every live     // page in sync with the setting here: add the chip where missing (on), remove it (off). Cheap     // and idempotent (one querySelector per page), pointer-events:none so it never blocks anything.-    try {+    // v2.0.314 (audit): this block ran up to TWO page.evaluate per tab every 8s, forever, waking+    // throttled renderers to re-assert settings that change only when a human flips a toggle. It+    // now runs on the first tick after a settings change and then once a minute as a safety net.+    const _enfSig = [pupSettings().ownerChip, pupSettings().titleTag, pupSettings().titlePrefix].join('|');+    const _enfDue = (_enfSig !== reconcileModel._enfSig) || (Date.now() - (reconcileModel._enfAt || 0) > 60000);+    if (_enfDue) { reconcileModel._enfSig = _enfSig; reconcileModel._enfAt = Date.now(); }+    if (_enfDue) try {       const chipOn = pupSettings().ownerChip === 'on';       const _tagOn = pupSettings().titleTag === 'on';       const _prefixOn = pupSettings().titlePrefix === 'on';@@ -9418,7 +9316,6 @@ setInterval(() => { // pup-render-test), but the fix must NOT touch OS focus: candidates to investigate next are the // browser-level launch flag --disable-backgrounding-occluded-windows (retains the compositor surface // without emulating focus) and rendering ONLY on genuine reveal. Do NOT reintroduce focus-emulation.-function renderLeaseTouch() { /* no-op: render lease reverted (foregrounded windows) */ }  async function parkSessionWindowDirect(page, sessionId) {   // ── macOS park ── the whole background contract is one primitive: (re-)hide the@@ -9780,8 +9677,6 @@ async function flashViaAD(sessionId, opts) {   catch (e) { return null; } } const _flashedHwnds = new Set(); // every hwnd pup ever flashed this boot — the clear-all sweep list-let _hwndWiggle = 0;   // v1.9.290: monotonic-ish counter for unique resolution widths-let _wiggleLock = Promise.resolve();   // v1.9.291: serialize wiggle resolves (enforcer fires them concurrently) // v1.9.243 (John: "i just hit clear flashes again and it still didn't clear"). Per-boot bookkeeping // was the hole: a bridge restart (every ship) empties the set while the taskbar keeps its orange, so // clear-all had nothing to sweep. Bookkeeping across restarts is inherently fragile — so DON'T rely@@ -13506,11 +13401,11 @@ const server = http.createServer(async (req, res) => {             return done({ ok: false, error: 'unknown setting or value' });           }           const _ch = { ...(pupSettings()._chosen || {}), [key]: true };-          savePupSettings(key === 'openFlash' ? { [key]: val, _openFlashChosen: true, _chosen: _ch } : { [key]: val, _chosen: _ch });+          savePupSettings({ [key]: val, _chosen: _ch });           console.log(`[settings] "${key}" -> ${val} (dashboard dialog)`);           // v1.9.279: a taskbar-identity setting must take effect on EXISTING windows immediately,           // not just future ones. Reconcile now re-stamps every live window to match (idempotent).-          if (['aumidIcons', 'taskbarGrouping', 'aumidIconStyle'].includes(key)) reconcileModel('setting ' + key).catch(() => {});+          if (['aumidIcons', 'taskbarGrouping', 'aumidIconStyle', 'ownerChip', 'titleTag', 'titlePrefix'].includes(key)) reconcileModel('setting ' + key).catch(() => {});           if (['jumpLists', 'jlViewTasks', 'jlCloseAll', 'jlDashboard', 'jlAnnotate'].includes(key)) refreshAllJumplists().catch(() => {});           // v2.0.197 (the 6-state proof exposed it): overlayBadges OFF only stopped FUTURE paints —           // badges already on glass stayed until some unrelated churn wiped them, so the toggle@@ -14913,14 +14808,9 @@ const server = http.createServer(async (req, res) => {             sendJSON(res, { success: false, error: `taskbarGrouping must be one of ${valid.join('|')}`, });             return;           }-          // v1.9.57: agentActivity 'on'|'off' — the taskbar progress bar that marks which window a-          // thread is currently driving. On by default; this is the user's taskbar, so give them the-          // switch rather than assuming everyone wants the motion.-          const aa = args.agentActivity;-          if (aa !== undefined && !['on', 'off'].includes(aa)) {-            sendJSON(res, { success: false, error: `agentActivity must be one of on|off` });-            return;-          }+          // agentActivity was collapsed into taskbarActivity in 2.0.292; callers still passing it get a+          // pointer instead of a silent write to a key nothing reads (audit, 2.0.314).+          if (args.agentActivity !== undefined) { sendJSON(res, { success: false, error: 'agentActivity was removed in 2.0.292; use taskbarActivity: "progress"|"flash"|"off"' }); return; }           // v1.9.95: reportIssues on|off — the bug-report invitation pup attaches as _reportIssues           // (on failures, on the first verb of a bridge run, and occasionally after). On by default:           // reports from real machines are the only way this bridge gets correct. A user who finds it@@ -14957,11 +14847,6 @@ const server = http.createServer(async (req, res) => {           if (mt) { savePupSettings({ minimalTouch: mt }); console.log(`[configure] minimalTouch -> ${mt}`); }           if (ri) { savePupSettings({ reportIssues: ri }); console.log(`[configure] reportIssues -> ${ri}`); }           if (al) { savePupSettings({ autoLogin: al }); console.log(`[configure] autoLogin -> ${al}`); }-          if (aa) {-            savePupSettings({ agentActivity: aa });-            console.log(`[configure] agentActivity -> ${aa}`);-            if (aa === 'off') { for (const [sid] of _busyState) { setSessionProgress(sid, false).catch(() => {}); } _busyState.clear(); }-          }           if (g) {             savePupSettings({ taskbarGrouping: g });             console.log(`[configure] taskbarGrouping -> ${g}`);@@ -15033,7 +14918,7 @@ const server = http.createServer(async (req, res) => {             : `NOTE: stored but INERT on ${_os} — pup's taskbar buttons, badges and progress bar are Windows-only (there is no equivalent Dock affordance), so nothing changed on screen. Persists across bridge updates (~/.adom/pup-settings.json) and takes effect if this profile is used on Windows.`;           const _gHint = g             ? `taskbarGrouping is now "${g}" — on Windows, ${g === 'grouped' ? 'all pup windows stack under ONE "Adom Pup" taskbar button and its badge shows the WINDOW count' : 'each pup window gets its OWN taskbar button wearing its tab count'}. ${_effect}`-            : `Current pup settings. Set with e.g. {"taskbarGrouping":"grouped"}.${_isWin ? '' : ' Both taskbarGrouping and agentActivity are Windows-only in effect; they are stored but inert here.'}`;+            : `Current pup settings. Set with e.g. {"taskbarGrouping":"grouped"}.${_isWin ? '' : ' taskbarGrouping and taskbarActivity are Windows-only in effect; they are stored but inert here.'}`;           const _sOut = pupSettings();           const _tmOut = _sOut.aumidIcons !== 'on' ? 'plain' : (_sOut.threadTiles === 'on' ? 'tiles' : 'aumid');           sendJSON(res, { success: true, output: JSON.stringify({ ok: true, settings: { ..._sOut, taskbarMode: _tmOut }, platform: process.platform, taskbarSupported: _isWin, _hint: _gHint }) });@@ -19474,19 +19359,9 @@ setInterval(async () => {   // whole standing population at boot and every 2 minutes; every repair is reported via the   // cleanup queue so a rescue is never silent. Healthy windows are never touched (the healer   // no-ops when bounds are on-screen).-  const sweepPlacement = async (cause) => {-    for (const [sid, sess] of [...sessions]) {-      try {-        if (!sess || !(sess.tabs || []).length) continue;-        const r = await healOffscreenWindow(sess, sid);-        if (r && r !== 'park-failed') {-          recordCleanup(`moved session "${sid}" back ON-SCREEN (found stranded off-screen during the ${cause} placement sweep — its taskbar button would have revealed nothing)`);-        }-      } catch (e) {}-    }-  };-  setTimeout(() => { sweepPlacement('startup').catch(() => {}); }, 15000);-  setInterval(() => { if (minimalTouch()) return; sweepPlacement('periodic').catch(() => {}); }, 120000);+  // v2.0.314 (audit): the per-session CDP placement sweep (120s, a CDP session + getWindowBounds per+  // window per tick) is DELETED — sweepVisibility below asserts the same off-screen invariant for+  // the whole population from the OS side, every 30s, in-process.    // v2.0.251 (John: "why can't i open this pup window? its like you did that off-screen thing again   // and now when i go to click on it as a human you fuck me over"). The placement sweep above asserts@@ -19520,6 +19395,18 @@ setInterval(async () => {     // session. Titled + WS_CAPTION only, so Chrome's own untitled helper windows are never touched.     const pids = [...pupBrowserPidSet()].filter(Boolean);     if (!pids.length) return;+    // v2.0.314: in-process (koffi) — microseconds, no spawn. The PowerShell below is the fallback.+    if (win32.available()) {+      const fixes = win32.fixHiddenOrOffscreen(pids) || [];+      for (const f of fixes) {+        const t = String(f.title || '').slice(0, 60);+        if (f.kind === 'MOVED-ONSCREEN') { console.log(`[visibility] moved an OFF-SCREEN pup window back on screen (hwnd ${f.hwnd}, "${t}") during the ${cause} sweep`); try { recordCleanup(`moved a pup window back ON-SCREEN ("${t}") — it was parked off-screen, so its taskbar button revealed nothing when clicked`); } catch (e) {} }+        else if (f.kind === 'MOVE-FAILED') console.log(`[visibility] hwnd ${f.hwnd} ("${t}") is off-screen and would NOT move back — reporting rather than pretending it is fine`);+        else if (f.kind === 'RESHOWN') { console.log(`[visibility] re-showed a HIDDEN pup window (hwnd ${f.hwnd}, "${t}") during the ${cause} sweep`); try { recordCleanup(`re-showed a pup window that had been left HIDDEN ("${t}") — its taskbar button would have revealed nothing when clicked`); } catch (e) {} }+        else console.log(`[visibility] hwnd ${f.hwnd} ("${t}") is hidden and would NOT re-show — reporting rather than pretending it is fine`);+      }+      return;+    }     const ps = `$s=@' using System; using System.Text; using System.Collections.Generic; using System.Runtime.InteropServices; public static class VS {@@ -19630,16 +19517,8 @@ public static class VS {     reapLeakedIsolatedProfiles().catch(() => {});   }, 15 * 60 * 1000); -  // Periodic cleanup: every 5 minutes, remove stale session files with dead PIDs-  setInterval(async () => {-    const files = listSessionFiles();-    for (const sf of files) {-      if (!sessions.has(sf.sessionId) && !isProcessAlive(sf.pid)) {-        console.log(`Cleanup: removing stale session file "${sf.sessionId}" (PID ${sf.pid} dead)`);-        deleteSessionFile(sf.sessionId);-      }-    }-  }, 5 * 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. };  // v2.0.16 (#557 ask 3, pup-side defense): EADDRINUSE SELF-HEAL. The #557 permanent outage was ab's