app
Pup - Puppeteer Bridge
Public Made by Adomby adom
pup is the AI's own browser: a real, full Chrome on the user's desktop that the AI fully controls (a sandbox, not the user's signed-in browser). Rides Bridge; pup_* verbs open windows and tabs, navigate, screenshot, and eval JS.
name: pup-window-targeting description: THE LAW for how pup finds a session's OS window for any Bridge window verb - ALWAYS by its resolved HANDLE (hwnd), NEVER by title. Title lookup is unreliable (titleTag is off by default and pages rewrite their own ) and it has independently broken the overlay paint, the park, the taskbar flash, the AUMID stamp, AND the jump list - the same bug, over and over. READ THIS before adding or touching ANY chrome.adCommand('desktop_*') call that acts on a window, or before adding a desktop_find_window resolver.</h2> <h1>pup window targeting: hwnd, never title (the recurring bug, killed)</h1> <p>John, across ONE session, caught this same class of bug break FOUR separate features, each of which I "fixed" one at a time before he snapped: "how many times did you say you couldn't look up a window by title and it messed up your algorithm... you keep duct-taping it instead of a permanent solution." He was right. This file is the permanent solution so it never regresses.</p> <h2>The disease</h2> <p>Every one of these subsystems, independently, tried to find a session's OS window by matching <code>(ai-thread: <sid>)</code> in its title (via <code>desktop_find_window</code>/<code>titleContains</code>), and every one FAILED the moment the title wasn't there:</p> <ul> <li><strong>Overlay paint</strong> - windows came back with no badge / <code>hwnd=None</code>.</li> <li><strong>Park (z-bottom + place)</strong> - windows stranded off-screen or never bottomed.</li> <li><strong>Taskbar flash</strong> - "did not match the window (title changed?)".</li> <li><strong>AUMID stamp</strong> - <code>"No visible window with title containing (ai-thread: pup-dashboard..."</code> → the dashboard window never stamped, so only SOME windows went teal (the mismatch John kept seeing).</li> <li><strong>Jump list</strong> - <code>updateWikiJumplist</code> resolved hwnd by title, got null, so <code>payload.hwnd</code> was never set and the list never committed ("i turned on jump lists and none of them work").</li> </ul> <p>Why title is fundamentally unreliable, and will NEVER be the mechanism:</p> <ol> <li><strong><code>titleTag</code> is OFF by default</strong> - John turned it off on purpose; he does not want <code>(ai-thread: X)</code> decorating his window titles. So most windows carry NO tag to match.</li> <li><strong>Pages rewrite <code>document.title</code></strong> as they load (SPAs constantly). Even a transient tag gets wiped before a find runs. The old re-stamping MutationObserver that fought this caused its own freezes.</li> </ol> <h2>THE LAW</h2> <p><strong>Every AD window verb targets the window by its resolved HWND, via <code>windowTarget()</code>. Never by title.</strong> <code>titleContains</code> survives ONLY as the cold fallback inside <code>windowTarget()</code> itself.</p> <pre><code class="language-js">// returns { hwnd } (validated, read-only) or, only if we truly have no handle, { titleContains } const tgt = await windowTarget(session, sessionId); await chrome.adCommand('desktop_taskbar', { ...tgt, overlay: {...} }); await chrome.adCommand('desktop_set_window_state', { ...tgt, state: 'bottom', force: true }); await chrome.adCommand('desktop_set_window_identity',{ ...tgt, appId, ... }); // STAMP await chrome.adCommand('desktop_set_window_jumplist',{ hwnd: session._hwnd, appId, tasks }); </code></pre> <p><code>windowTarget()</code> resolves + validates <code>session._hwnd</code> (via <code>hwndBelongsToPup</code>), read-only, and NEVER touches window geometry. Spread it into the verb args. That is the whole rule.</p> <h3>When you add a new window verb</h3> <p>Spread <code>...(await windowTarget(session, sessionId))</code>. Do NOT write a fresh <code>desktop_find_window({ titleContains })</code> to "get the hwnd" - that is the exact anti-pattern that keeps coming back. If you catch yourself typing <code>titleContains: \</code>(${TITLE_LABEL}<code>in anything other than the</code>windowTarget` fallback, STOP.</p> <h2>Where the handle comes from (so the fallback stays cold)</h2> <p>Resolution is READ-ONLY. It never resizes or moves a window (see the wiggle grave below). Priority:</p> <ol> <li><strong>Cached <code>session._hwnd</code></strong>, validated by <code>hwndBelongsToPup</code> (rejects a stale/recycled/foreign handle - e.g. one now owned by Edge or another profile's process).</li> <li><strong>Birth-time capture</strong> - <code>createPageInNewWindow</code> births each window at a UNIQUE off-screen position and reads its handle THERE, while it is off-screen and small, before park maximizes it. That is the only moment a same-profile window is unambiguous. Stashed on <code>page.__pupHwnd</code>, trusted first.</li> <li><strong>Persisted handle</strong> - <code>_hwnd</code> is written to the session file and restored on EVERY adoption path (<code>recoverSessions</code> AND <code>rescanProfile</code>). A Chrome window keeps the SAME hwnd across a bridge respawn, so a recovered window restores its handle instead of re-resolving.</li> <li><strong>Sole-window-of-a-profile</strong> - if a profile's process owns exactly one top-level Chrome frame, that IS the window (works even maximized).</li> <li>If none of the above: <strong>leave it BARE</strong> (no overlay). Bare > wrong, forever (John's Edge-overlay rule). NEVER guess.</li> </ol> <h2>⚰️ The width-wiggle is DEAD. Do not resurrect it.</h2> <p>For months, same-profile windows (which share the identical parked rect) were disambiguated by temporarily SETTING A UNIQUE WIDTH via CDP, enumerating, and restoring. It is GONE (v1.9.308) because:</p> <ul> <li>It never worked on <strong>maximized</strong> windows (CDP can't resize a maximized window) - it was matching by luck/collision the whole time.</li> <li>Its DPI-scale guessing was promiscuous and <strong>collapsed multiple windows onto one handle</strong>.</li> <li>When a restore failed it left a window <strong>stuck narrow</strong> - John: "why did you resize this window so weird? what the fuck?" Deforming the user's window is never acceptable.</li> </ul> <p>There is no invisible way to geometry-probe an on-screen window. Birth-time capture + persistence is the answer. If you think you need the wiggle, you need birth-time capture instead.</p> <h2>One-line summary</h2> <p>Target windows by handle through <code>windowTarget()</code>, resolve the handle read-only (birth-time + persist, never resize), leave bare when unsure, and never, ever look a window up by its title again.</p>
---
name: pup-window-targeting
description: THE LAW for how pup finds a session's OS window for any Bridge window verb - ALWAYS by its resolved HANDLE (hwnd), NEVER by title. Title lookup is unreliable (titleTag is off by default and pages rewrite their own <title>) and it has independently broken the overlay paint, the park, the taskbar flash, the AUMID stamp, AND the jump list - the same bug, over and over. READ THIS before adding or touching ANY chrome.adCommand('desktop_*') call that acts on a window, or before adding a desktop_find_window resolver.
---
# pup window targeting: hwnd, never title (the recurring bug, killed)
John, across ONE session, caught this same class of bug break FOUR separate features, each of which I
"fixed" one at a time before he snapped: "how many times did you say you couldn't look up a window by
title and it messed up your algorithm... you keep duct-taping it instead of a permanent solution." He
was right. This file is the permanent solution so it never regresses.
## The disease
Every one of these subsystems, independently, tried to find a session's OS window by matching
`(ai-thread: <sid>)` in its title (via `desktop_find_window`/`titleContains`), and every one FAILED
the moment the title wasn't there:
- **Overlay paint** - windows came back with no badge / `hwnd=None`.
- **Park (z-bottom + place)** - windows stranded off-screen or never bottomed.
- **Taskbar flash** - "did not match the window (title changed?)".
- **AUMID stamp** - `"No visible window with title containing (ai-thread: pup-dashboard..."` → the
dashboard window never stamped, so only SOME windows went teal (the mismatch John kept seeing).
- **Jump list** - `updateWikiJumplist` resolved hwnd by title, got null, so `payload.hwnd` was never
set and the list never committed ("i turned on jump lists and none of them work").
Why title is fundamentally unreliable, and will NEVER be the mechanism:
1. **`titleTag` is OFF by default** - John turned it off on purpose; he does not want `(ai-thread: X)`
decorating his window titles. So most windows carry NO tag to match.
2. **Pages rewrite `document.title`** as they load (SPAs constantly). Even a transient tag gets wiped
before a find runs. The old re-stamping MutationObserver that fought this caused its own freezes.
## THE LAW
**Every AD window verb targets the window by its resolved HWND, via `windowTarget()`. Never by
title.** `titleContains` survives ONLY as the cold fallback inside `windowTarget()` itself.
```js
// returns { hwnd } (validated, read-only) or, only if we truly have no handle, { titleContains }
const tgt = await windowTarget(session, sessionId);
await chrome.adCommand('desktop_taskbar', { ...tgt, overlay: {...} });
await chrome.adCommand('desktop_set_window_state', { ...tgt, state: 'bottom', force: true });
await chrome.adCommand('desktop_set_window_identity',{ ...tgt, appId, ... }); // STAMP
await chrome.adCommand('desktop_set_window_jumplist',{ hwnd: session._hwnd, appId, tasks });
```
`windowTarget()` resolves + validates `session._hwnd` (via `hwndBelongsToPup`), read-only, and NEVER
touches window geometry. Spread it into the verb args. That is the whole rule.
### When you add a new window verb
Spread `...(await windowTarget(session, sessionId))`. Do NOT write a fresh
`desktop_find_window({ titleContains })` to "get the hwnd" - that is the exact anti-pattern that keeps
coming back. If you catch yourself typing `titleContains: \`(${TITLE_LABEL}` in anything other than the
`windowTarget` fallback, STOP.
## Where the handle comes from (so the fallback stays cold)
Resolution is READ-ONLY. It never resizes or moves a window (see the wiggle grave below). Priority:
1. **Cached `session._hwnd`**, validated by `hwndBelongsToPup` (rejects a stale/recycled/foreign
handle - e.g. one now owned by Edge or another profile's process).
2. **Birth-time capture** - `createPageInNewWindow` births each window at a UNIQUE off-screen position
and reads its handle THERE, while it is off-screen and small, before park maximizes it. That is the
only moment a same-profile window is unambiguous. Stashed on `page.__pupHwnd`, trusted first.
3. **Persisted handle** - `_hwnd` is written to the session file and restored on EVERY adoption path
(`recoverSessions` AND `rescanProfile`). A Chrome window keeps the SAME hwnd across a bridge
respawn, so a recovered window restores its handle instead of re-resolving.
4. **Sole-window-of-a-profile** - if a profile's process owns exactly one top-level Chrome frame, that
IS the window (works even maximized).
5. If none of the above: **leave it BARE** (no overlay). Bare > wrong, forever (John's Edge-overlay
rule). NEVER guess.
## ⚰️ The width-wiggle is DEAD. Do not resurrect it.
For months, same-profile windows (which share the identical parked rect) were disambiguated by
temporarily SETTING A UNIQUE WIDTH via CDP, enumerating, and restoring. It is GONE (v1.9.308) because:
- It never worked on **maximized** windows (CDP can't resize a maximized window) - it was matching by
luck/collision the whole time.
- Its DPI-scale guessing was promiscuous and **collapsed multiple windows onto one handle**.
- When a restore failed it left a window **stuck narrow** - John: "why did you resize this window so
weird? what the fuck?" Deforming the user's window is never acceptable.
There is no invisible way to geometry-probe an on-screen window. Birth-time capture + persistence is
the answer. If you think you need the wiggle, you need birth-time capture instead.
## One-line summary
Target windows by handle through `windowTarget()`, resolve the handle read-only (birth-time + persist,
never resize), leave bare when unsure, and never, ever look a window up by its title again.