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
Capture the final window playbook: EnumWindows real-window resolution, deactivate-then-park order, short input-aware re-park, and the minimize-breaks-screenshots dead-end
1 file changed
+31−10
dev-skills/pup-bridge-dev/SKILL.md+31−10@@ -240,11 +240,18 @@ R1 never visually pop / cover the user's work · R2 the window is reachable (tas `--window-position=-32000,-32000` (NOT `--start-maximized`; strip it when `!foreground`). The window is created far past any monitor, so nothing ever appears on screen during startup. CDP renders regardless of position, so screenshots/eval/record all still work.-2. **Park ON-SCREEN at the bottom of the z-order** → satisfies R2 + R3. After CDP connects, move it back- on-screen sized to the monitor at the very back:- `SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, GetSystemMetrics(0), GetSystemMetrics(1), SWP_NOACTIVATE)`.- Fully behind the user's windows (invisible) but it has a real taskbar button to click. (Leaving it- off-screen breaks R2 — the taskbar button "restores" it off-screen where you can't see it.)+2. **Park ON-SCREEN at the bottom of the z-order** → satisfies R2 + R3. On-screen-occluded (behind the+ user's windows, taskbar-clickable) is the ONLY resting state that keeps R3 — see the minimize dead-end.+ TWO details that took blood to get right:+ - **Resolve the REAL window by ENUMERATION, not `MainWindowHandle`.** `Get-Process(pid).MainWindowHandle`+ returns a HIDDEN Chrome helper window on some boxes → `SetWindowPos` moved a phantom, the real window+ stayed off-screen, and the "backgrounded" self-check passed against the phantom (a FALSE positive).+ Enumerate top-level windows and take the largest VISIBLE `Chrome_WidgetWin_1` owned by the pid/children+ (`WinBgP::BestWindow`). This was THE "I click the taskbar and nothing shows" bug (1.8.46).+ - **Deactivate FIRST, THEN move on-screen** (order matters). If you move it on-screen while Chrome still+ has it foreground, it flashes on TOP for a moment (an active window stays topmost until deactivated) =+ the "I saw it open" pop. So: hand focus back (below) while it is still OFF-SCREEN, and only then+ `SetWindowPos(pup, HWND_BOTTOM, 0, 0, scrW, scrH, SWP_NOACTIVATE)` — now it drops behind, invisible. 3. **Hand keyboard focus back via `AttachThreadInput`** → satisfies R4, and is the crux. Plain `SetForegroundWindow(usersWindow)` is BLOCKED by Windows' foreground lock. The workaround: - snapshot the user's window with `GetForegroundWindow()` **before** spawning the browser;@@ -252,9 +259,13 @@ R1 never visually pop / cover the user's work · R2 the window is reachable (tas window's thread via `GetWindowThreadProcessId`), which shares the foreground privilege → then `SetForegroundWindow(usersWindow)` + `BringWindowToTop(usersWindow)` succeeds → detach with `AttachThreadInput(..., false)`.- - Re-run the whole background+handback a few times over ~2.5s (600/1400/2500ms) because the browser- re-grabs during startup. The initial `priorForeground` is stashed on `browser._priorForeground` so- the after-sizing re-background can restore too.+ - **Keep the re-park window SHORT (~1.2s: 300/700/1200ms), not 2.5s+.** The re-passes only exist to+ counter Chrome re-grabbing foreground during startup. A LONG/aggressive loop FIGHTS the user: they+ click the window to view it and the next pass yanks it back to the background. After ~1.2s pup must+ NEVER auto-re-park. WITHIN the window, use `GetLastInputInfo`: pup-foreground + a physical input in the+ last ~1.5s = the USER (Chrome self-raise is programmatic, no input) → bring the window on-screen+ (centered ~80%) + latch `browser._userForeground` + cancel remaining passes; else re-park. See the+ "user↔AI balance" section below — this is the behavior John cares most about. 4. **Run all this PowerShell HIDDEN** → satisfies R5. Use `execFileSync('powershell.exe', [...args], {windowsHide:true})` + `-WindowStyle Hidden` (see `runPsHidden`/`runPsHiddenAsync`), NEVER `execSync('powershell …')` — the latter goes through cmd.exe and flashes a console window. (A cmd blip@@ -274,8 +285,18 @@ must clear it first. before you lower it = the visible pop. This is the naive version; off-screen launch replaces it. - **`SetForegroundWindow(usersWindow)` alone** → silently no-ops (foreground lock). You MUST use the `AttachThreadInput` attach/detach dance.-- **Minimize-and-leave (`SW_MINIMIZE`, don't re-show)** → drops focus cleanly BUT a minimized window stops- compositing, so `Page.captureScreenshot` **times out** (breaks R3). Rejected.+- **Minimize as the background state** → tried TWICE now (SW_MINIMIZE 1.8.26-era, and full+ `SetWindowPlacement`-restore-rect minimize in 1.8.47). It's SO tempting because minimizing hands focus+ back for free and a taskbar click restores-and-stays (no re-park loop). But a minimized window reports+ `document.visibilityState === 'hidden'` and Chrome SUSPENDS its renderer, so `Page.captureScreenshot`+ **hangs ~27s then errors** — EVEN WITH `--disable-features=CalculateNativeWinOcclusion` ++ `--disable-backgrounding-occluded-windows` (those cover OCCLUDED windows, not MINIMIZED). Breaks R3.+ **REJECTED — do not try minimize again.** On-screen-occluded is the only state that both hides the window+ AND keeps it rendering. (`osMinimizeToBackground` may still be in the tree, unused — do not wire it back.)+- **`MainWindowHandle` to resolve the window** → returns a hidden Chrome helper on some boxes; you park a+ phantom and the real window never moves. Enumerate `Chrome_WidgetWin_1` instead (technique #2).+- **A long/aggressive re-park retry loop** → fights the user's click (yanks the window back). Keep it short+ (~1.2s) and input-aware (technique #3). - **`WS_EX_NOACTIVATE` on the window** → Chrome self-activates anyway (so it doesn't fix R4), AND a non-activatable window can't be brought up by a taskbar click (breaks R2). Rejected. - **Minimize→re-show loop as the focus drop** → on an on-screen window the minimize/restore is a visible