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.
Releases 1153
Standalone per-platform binaries to download and run, no tools needed. The newest is pinned on top.
Annotate's quick colors are red, blue and green: the muddy yellow/orange third color is gone from the trio and from Multi's automatic cycle (red, blue, green, purple), so every mark has a color anyone can name to the AI. Yellow stays in the More flyout, and every swatch shows its color name on hover (John, 2026-10-07).
Stable link for websites and docs: /download/adom/pup-bridge/latest
All releases showing 421-440 of 1153
- v2.0.278: Deterministic window identity, wiggle DELETED (dev-skills/pup-no-wiggle): every window is tagged with a Win32 SetProp('AdomPupSid=') property at first resolve; lost handles are re-found by GetProp property lookup (no geometry, no nudge). Geometry bounds-matching and the off-screen re-capture nudge are removed from resolveSessionHwndByBounds. (John Lauer)
- NEW dev-skill pup-no-wiggle: HARD BAN on geometry/wiggle window identification (banned 3x, kept getting reintroduced). Mandates deterministic identity: birth mutex + unique off-screen coord + set-diff hwnd capture + <50ms on-screen move + Win32 SetProp('AdomPupSid=') window-property tag (VERIFIED cross-process persistent 2026-08-22) recovered via EnumProps after restart. Title is NOT the tag (pages overwrite it). (John Lauer)
- pup-taskbar-states: record John's decision to LEAVE the crash-recovery bare-icon edge as-is (rare, self-limiting) (John Lauer)
- skills: pup-vendor-login gains the BACKGROUND Google-SSO click recipe (classic full-page accountchooser is fully background-drivable via elementFromPoint+full-event-sequence; only the FedCM One-Tap bubble needs a real cursor - proven signing John into Autodesk 2026-08-22); pup-taskbar-states records the owner-loss->category downgrade CLOSED (v2.0.277 tileOwnerName) and the remaining crash-on-shared-profile bare-icon root (John Lauer)
- v2.0.277: Tiles mode: every window wears a NAME TILE, never a category glyph - an owner-less/recovered window (issue #70) now derives a stable name tile via tileOwnerName() instead of downgrading to the pup category icon (fixes John's inconsistent taskbar icons). (John Lauer)
- doctrine: reconcile placement with v2.0.276 off-screen-birth + <50ms park (measured 27ms, 0/95 foreground steal); delete focus-handback; off-screen banned only as resting/staging/fallback, sanctioned as a guaranteed sub-50ms birth transient (John Lauer)
- v2.0.276: Off-screen birth restored, moved on-screen within 50ms, handback deleted (John). Root cause of tonight's regression: 2.0.259 moved birth on-screen, which lets Chrome activate the window and steal the foreground on every open; 2.0.260's handback then called desktop_bring_to_front(prevFg), and on a machine full of pup windows prevFg is usually ANOTHER pup window, so it foregrounded pup windows with no caption (the parade John saw). Chrome/CDP expose no no-activate create option and WS_EX_NOACTIVATE is a documented dead-end, so the only lever is WHERE the window is born: Chrome does not activate a window that is off-screen. So birth is off-screen again (zero focus steal), then a single local CDP getWindowForTarget+setWindowBounds repositions it on-screen WITHOUT changing z-order in ~20ms (budget 50ms), so it is clickable from one frame in and can never be stranded. The handback is gone; the remaining focus check is a diagnostic that fires only if off-screen birth ever fails to suppress the activation. (John Lauer)
- Publish 2.0.276 (John Lauer)
- NEW user skill pup-why-cft (John: 'we made a sub-readme so folks like adrian understand it, but now i realize we have to explain it to the ai as well'). docs/WHY-CFT.md was written for humans and no AI thread could ever discover it: it is not a skill, has no trigger words, and until this morning the pup skills contradicted it. This skill is the doc's AI-discoverable form: the five dated reasons (banner, --load-extension removal as the forcing one, determinism, no consumer chrome, binary isolation), the honest trade-offs incl. no Widevine and the CfT window title, what did NOT change, and the note that stops anyone fixing launchCandidates() backwards. Wired into install.sh, files[], and the /pup skill map in the same pass, because tonight proved a capability that ships without its references rots. (John Lauer)
- Skill sweep, browser-policy pass: the /pup mental model and the entire /pup-browsers-and-chrome sub-skill still taught the mid-2026 native-first policy (drives the browser already on the machine, CfT is the last resort), which docs/WHY-CFT.md reversed for good on 2026-08-10 (automation banner; branded Chrome 137+ removed --load-extension, which the toolbar needs). Both now teach CfT-first with installed Chrome/Edge as the spawn-verified fallback ladder, which is also what pup_readiness reports on a live box (browserKind: chrome-for-testing). The stale native-first editorial in chrome.js is flagged in place with a warning note, with the base order deliberately unchanged because it IS the fallback ladder. Also: the no-semicolons pup_eval reminder in the screenshots skill updated for the 2.0.274 statement support. (John Lauer)
- install.sh: purge the pup-bridge mis-slug alongside the old adom-desktop-puppeteer-bridge one. A pkg-layer install had deposited the start-here skill under the PAGE slug as a second user-invocable copy, so the slash menu showed /pup AND /pup-bridge, and the duplicate served week-old text because this installer never refreshed it. John caught it from the slash list itself. (John Lauer)
- pup-adom-wiki: the Historical note was written in present tense (do not claim you can see logged-in features, you cannot) directly under the section saying logged-in is the DEFAULT since 1.9.162 - rewritten as actual history, keeping the one surviving rule (never drive the SSO form with the user's password). Quick-start examples gained --ai-thread, without which ab refuses the calls outright. pup start-here: the No-semicolons-in-pup_eval rule is obsolete since 2.0.274 (statement lists run via Runtime.evaluate; bad code now fails honestly). (John Lauer)
- pup start-here skill: truth pass against the shipped code. Fixed four stale claims: windows described as opening MAXIMIZED (they are born at the 10px-inset work area in normal state since the 2026-08-20 placement rules; OS-maximize raises and is never used), split described as the taskbar default (tiles has been the default since 2.0.152), the audit section expecting exactly 1 AUMID key (tiles registers one content-token key per live thread since 2.0.271, so the old expectation reads every healthy machine as broken), and the badge described as favicon plus Adom mark (mark removed 2.0.192). Also replaced the audit's title-based window ground-truth step with hwnd resolution, since titleTag is off by default and the titleContains probe matches nothing. (John Lauer)
- install.sh: ACTUALLY deploy pup-taskbar-states and pup-progress (both shipped in files[] but were never installed by this script, so installed copies drifted stale between pkg publishes). NOTE: the two previous commits' messages claimed fixes that had NOT landed — my edit scripts asserted on stale text and aborted BEFORE writing, but the push in the same shell block ran anyway, shipping unchanged files under fix-claiming messages. The skill fixes landed in the commit before this one; this commit is the installer. Lesson recorded: never chain edit-and-push in one step. (John Lauer)
- pup-taskbar-states, for real this time (the previous commit message claimed these fixes but the script aborted before writing): cite #53/#54 as CLOSED with #70 as the open gap, fix the internal wipe-vs-converge contradiction (line 51 still said wiped while line 38 said converges), add overlaySize (John Lauer)
- Skill truth pass, triggered by John running the slash commands himself: pup-taskbar-states cited two CLOSED issues (#53, #54) as open gaps, described plain mode as a wipe when it has been native-AUMID convergence since the two-CfT-buttons fix, omitted overlaySize, and predated the bounded nudge retry + minimize preservation. pup-settings still claimed an Adom mark on the badge (removed 2.0.192; only the generic fallback glyph keeps it). Both now match the shipped code, and #70 is named as the one genuinely open gap. (John Lauer)
- pup-settings: document overlaySize (the badge-size percent added in 2.0.262, default 75) and the two lower-level keys pup_status surfaces (aumidIcons, threadTiles). The user-facing settings reference was missing a setting that had already shipped, which is how a skill quietly stops being the source of truth. (John Lauer)
- dockbar.json: add launch.console=show for parity with the two verified reference bridges, so the install/serve step output is visible while the card starts instead of looking hung (John Lauer)
- v2.0.275: Close the AUMID registration race introduced with 2.0.271. Making the thread AUMID content-derived was right, but it created a new failure: if the shell builds a button for a brand-new AUMID BEFORE the IconResource write is visible, Windows caches 'no icon' against that AUMID and it stays blank forever, because the AUMID never changes again on its own. Measured on John's 'oven' button, which showed as a bare overlay badge on empty space while its .ico, its registry entry and its AUMID were all provably correct; only deleting the tile to force a fresh token brought the art back. The button rebuild now waits (up to 2.5s, then proceeds) until the AUMID's IconResource is present AND points at a real file, so the shell always has art to read at first paint. (John Lauer)
- v2.0.274: Fix the second half of adom/pup-bridge#69: pup_evaluate ran nothing and reported ok. The eval is new Function('return (' + expr + ')'), which is EXPRESSION-only, so the reporter's location.assign('...'); 'navigating' was a SyntaxError: nothing ran, the page never navigated, and the handler still answered ok:true with the failure buried in result.__error. Reproduced verbatim. Two fixes. On a syntax error the source is retried through CDP Runtime.evaluate, which has devtools-console semantics (accepts a statement list, returns the completion value), so the same code now works; a context destroyed mid-eval is treated as SUCCESS because it means the page navigated. And a failed eval is now a FAILED verb with errorCode eval_failed instead of ok:true wrapping __error, which was the exact lie this issue is about. Also confirms the switch_tab half: after 2.0.272 all four switches including repeats land on the requested tab with activated:true; the earlier partial result was measured against a session that had been reaped, not a real failure. (John Lauer)
- Publish 2.0.273 (John Lauer)
- adom/pup-bridge#66: ship the Dock Bar card. dockbar.json (manifest 1.2.0) at the repo root, a real favicon at the declared path, and pup-dashboard — the container-side status page serving /api/status in the SDK payload shape (per-LED label/state/detail/_hint plus our own top-level rollup, cached with an internal budget under the declared 2000ms). Honors the three SDK rules that broke both other bridges: serve self-backgrounds by spawning a detached child and waiting for the port the CHILD actually bound before printing it, every child process gets ~/.local/bin prepended, and a second serve detects the live instance and prints ITS url. (John Lauer)
- v2.0.272: Fix adom/pup-bridge#69: pup_switch_tab reported ok while never switching the tab on any background window. page.bringToFront() is the only thing that actually changes Chrome's visible tab and it was gated on session._foreground; pup windows are background by default, so the normal case updated pup's own activeTabId bookkeeping, never told Chrome, and still returned success:true, while pup_list_tabs (which reads Chrome's real state) kept reporting the old tab. Reproduced on AdomLapper against a 3-tab window: asking for tab-1, tab-2 or tab-3 all left Chrome on tab-2. The v1.8.26 rationale was 'only raise the OS window for a session the user is watching', which conflated activating a TAB inside a window with raising that window. The activation now always runs, and because Target.activate can pull a window forward, the user's focus is handed straight back with the same one-shot handback used at window birth in 2.0.259 (never a timer, never a loop, the window itself is never moved). The verb also VERIFIES the switch by reading visibilityState back from Chrome and returns activated plus an honest hint when it did not take, because a verb that says ok while nothing happened is the exact class this issue is about. (John Lauer)
- tools: wiki-issue-monitor.sh — the wiki-issue-loop live Monitor (John: 'do a monitor every 5 mins today'). Seeds on the current unread set so it cannot fire a 387-notification firehose, treats a failed poll as neither an event nor fatal, and marks items on repos this thread maintains. (John Lauer)
- v2.0.271: THE AUMID IS THE ICON CACHE KEY. John's Bills window showed category art while every link in pup's chain was provably correct. Measured in order on that one window: AUMID registered with IconResource pointing at the right .ico; the .ico itself rendering the Bills name tile at 16/32/48/256; threadTiles off/on full re-register -> unchanged; registration-epoch bump for a fresh ab cacheKey -> unchanged; WM_SETICON with the tile on the window itself -> unchanged; stamping a NOVEL AUMID string -> correct instantly. Windows caches a taskbar button's icon per AppUserModelID, so repointing a seen AUMID at new art is ignored and only a virgin AUMID can move it. THREAD_TILE_GEN already encoded that but is hand-edited, so it cannot react when a tile is re-rendered, which is exactly when the art changes. The thread AUMID now folds in a hash of the tile's CONTENT: identical bytes give an identical AUMID (stable across restarts, no button churn), a re-rendered tile gives a virgin AUMID and the shell is structurally forced to re-read the art. Wrong art becomes unreachable rather than something the audit has to catch after the fact. (John Lauer)
- v2.0.270: Wrong taskbar ART that pup swore was correct (John's Bills window). Established by measurement: the AUMID was registered, its IconResource pointed at the right .ico, the .ico itself rendered the Bills name tile correctly at all four sizes, the button was correctly NAMED Bills, and a full threadTiles off/on re-register did not shift it — yet the shell drew the category tile. Cause: ab caches icon HANDLES by cacheKey for its process lifetime, and the thread-tile cacheKey versioned only on tile GENERATION, which changes only when we bump it. A handle cached from an earlier stamp (category art, before the tile finished rendering) therefore served forever. The cacheKey now keys on tile CONTENT (mtime+size), so any re-render is automatically a new handle and a stale one is structurally impossible. Also closes the audit's blind spot honestly: it infers art from _buttonBuiltAt vs tile mtime and cannot read the glass, so it reported CLEAN while John looked at a wrong icon. pup_audit_icons gains {force:true} to re-assert every icon with a fresh epoch regardless of findings, and the clean-verdict hint now says outright that a clean model is not proof of correct pixels. (John Lauer)
- v2.0.269: Move Favicon overlay badges and Badge size directly under Taskbar identity (John: 'this should be near the Taskbar Identity section since its highly related'). They are part of the same decision — the identity picker's own six examples are literally 'tiles + overlay' vs 'tiles' — so having the overlay toggle sit fifteen rows below the picker that illustrates it was a discoverability bug, not just an ordering preference. (John Lauer)
- v2.0.268: Settings dialog: the sticky header was being painted over by the scrolling content. position:sticky with z-index:auto creates no stacking context, so later siblings that make their own (every .mic is position:relative) drew straight over the title and the close button. One line: z-index on .dlghead. (John Lauer)
- v2.0.267: Finish the settings overflow: the plain-mode preview strip is six 44px flex:none cards in a non-wrapping row, a hard ~298px floor that no shrinking elsewhere could beat. It was the last 49px of horizontal overflow and the remaining seam past texstrip's background. Now wraps and centers. (John Lauer)
- v2.0.266: Settings dialog layout pass (John: 'why does your settings panel look so awful?'). Three real defects, all measured live rather than guessed. (1) The 'default' pill floating over the dialog's X: .mdef was position:absolute inside .stlbl, which is not positioned, so it escaped to #dlg and parked in the corner; it is now a plain inline chip in the label with no positioning to get wrong. (2) The hard vertical seam behind the taskbar-identity choices: those rows wanted 516px inside a 439px strip, so the sample icons spilled past .texstrip's dark background; the row now wraps, the label shrinks from a rigid 175px to 150px, .sticons is shrinkable with min-width 0, and the plain-mode explainer's hard max-width:330px (the single biggest contributor to the 516) becomes a flexible basis. (3) The -7px badge overhang could clip against the strip edge, so .sticons carries matching padding. Audited every other absolutely-positioned class for the same escaped-ancestor trap; .mbcard is the only other one and its .mic parent is correctly relative. (John Lauer)
- v2.0.265: Tighten the Badge size row copy. Seen in the live pixels: the long description wrapped into a narrow six-line column beside the dropdown, which read as cramped next to every other sub-row. The setting is unchanged; only the label text is shorter. (John Lauer)
- v2.0.264: Add 75% as an overlay badge size and make it the default (John, after comparing 100/66/50 on real tiles). Because 2.0.263 turned the setting into a straight percent with a clamped parser, this is one value per surface rather than a new branch: default, dashboard dropdown, set-setting whitelist, pup_configure validation and the fallback in overlayScalePct. (John Lauer)
- v2.0.263: Overlay badge size becomes a real PERCENT and gains 66, now the default (John tried 50 and asked to try 66). overlayScalePct() parses and clamps the stored value to 25-100 so a bad value can never composite a zero-size, invisible badge, and adding another stop is now a one-word change rather than a new branch. Options 50 | 66 | 100 across the dashboard dropdown, the set-setting whitelist and pup_configure. (John Lauer)
- v2.0.266: Settings dialog layout pass (John: 'why does your settings panel look so awful?'). Three real defects, all measured live rather than guessed. (1) The 'default' pill floating over the dialog's X: .mdef was position:absolute inside .stlbl, which is not positioned, so it escaped to #dlg and parked in the corner; it is now a plain inline chip in the label with no positioning to get wrong. (2) The hard vertical seam behind the taskbar-identity choices: those rows wanted 516px inside a 439px strip, so the sample icons spilled past .texstrip's dark background; the row now wraps, the label shrinks from a rigid 175px to 150px, .sticons is shrinkable with min-width 0, and the plain-mode explainer's hard max-width:330px (the single biggest contributor to the 516) becomes a flexible basis. (3) The -7px badge overhang could clip against the strip edge, so .sticons carries matching padding. Audited every other absolutely-positioned class for the same escaped-ancestor trap; .mbcard is the only other one and its .mic parent is correctly relative. (John Lauer)
- v2.0.265: Tighten the Badge size row copy. Seen in the live pixels: the long description wrapped into a narrow six-line column beside the dropdown, which read as cramped next to every other sub-row. The setting is unchanged; only the label text is shorter. (John Lauer)
- v2.0.264: Add 75% as an overlay badge size and make it the default (John, after comparing 100/66/50 on real tiles). Because 2.0.263 turned the setting into a straight percent with a clamped parser, this is one value per surface rather than a new branch: default, dashboard dropdown, set-setting whitelist, pup_configure validation and the fallback in overlayScalePct. (John Lauer)
- v2.0.263: Overlay badge size becomes a real PERCENT and gains 66, now the default (John tried 50 and asked to try 66). overlayScalePct() parses and clamps the stored value to 25-100 so a bad value can never composite a zero-size, invisible badge, and adding another stop is now a one-word change rather than a new branch. Options 50 | 66 | 100 across the dashboard dropdown, the set-setting whitelist and pup_configure. (John Lauer)
- v2.0.262: New setting: overlay badge size, defaulting to HALF (John). At full size the favicon badge covers the thread tile's BOTTOM LINE of text, which is the whole point of thread tiles since that text is how you tell one thread's windows from another's. Windows offers no way to ask the shell for a smaller overlay slot, so the badge CONTENT is composited at half scale inside the same 48x48 canvas, anchored northeast (the tile draws its name from the bottom-left, so the far corner does the least damage) and the shell scales it as before. Wired end to end: default '50', dashboard Settings row (a labelled dropdown, the sel row type gained selLabels so a raw value like '50' reads as 'Half size' instead of a bare number), set-setting whitelist, live repaint on change so flipping it moves the taskbar immediately, pup_configure {overlaySize}, pup_describe, and pup_status.settings. The overlay cache key includes the size, without which a flip would serve the previously-composited badge and nothing would change on glass. (John Lauer)
- pup-bridge-dev: close the open gap on the focus handback. Measured: 0/82 foreground samples moved on AdomLapper (no steal to hand back), the handback verifiably fires and restores on ConfRoomROG in both process cases, and the difference is user-input recency (15ms vs 440s) i.e. Windows foreground lock protecting an active user. Records the disproven same-process theory too. (John Lauer)
- NEW dev sub-skill pup-bot-walls: identify the wall (DataDome vs Cloudflare vs Akamai vs Imperva), what the consumer-UA override already covers, the API-first sourcing ladder, the human handoff, and the TODO roadmap for looking like the human-directed session we actually are (John Lauer)
- pup-bridge-dev: document R6 (a newborn window's self-activation is Chrome, never the user), the implementation map for R1-R6, and the VERIFIED evidence table from the 2.0.259-261 run including John's three-vendor separate-tile test (John Lauer)
- v2.0.261: A newborn window's self-activation is Chrome, never the user. John opened ti.com, it landed on top of his work, and pup then REFUSED to move it: the log read [park] ti-proof (post-nav re-assert): user-foreground - hands off, on a window he had never touched. Cause: every user-intent check attributes an OS-foreground pup window to the user via _userActiveAt or simply 'there was input in the last 5 seconds', and he was typing while the window opened. Both are wrong for a window that did not exist a second ago, because there was no taskbar button to click; the old off-screen birth hid this entirely, since Chrome does not activate a window that is nowhere. Adds session._bornAt plus an 8s BIRTH_GRACE_MS honored by osBottomUnlessForeground, userIsUsingWindow and the foreground observer, so a newborn is bottomed rather than credited to the user. An explicitly granted foreground open or raise sets _raiseGranted and is exempt, because that window is supposed to be in front. (John Lauer)
- pup-bridge-dev: --no-startup-window was never the whole focus fix; the off-screen birth was doing half the work. Measured, and the v1.9.253 handback restored. (John Lauer)
- v2.0.260: Hand the keyboard focus back when an on-screen birth takes the foreground. Measured live the moment R1 landed: --no-startup-window was not the only thing suppressing Chrome's foreground grab, the off-screen birth was doing half the work, because Chrome does not bother activating a window that is nowhere. Born at a real rect it activates, and every open logged a steal. Per R2 the WINDOW is left exactly where it is and nothing re-parks it, but the user's keyboard focus is not the window's z-order: letting their typing land in a browser that just appeared is not a flaw worth accepting when handing focus back costs one call. This is the v1.9.253 handback restored (it was deleted only because the off-screen birth made it unreachable), one shot at birth, never on a timer, and it reports honestly when it does not take. (John Lauer)
- v2.0.259: Implement John's new placement rules (2026-08-20). R1: a window is BORN at its final on-screen rect via Target.createTarget left/top/width/height, the primary monitor work area inset 10px on all four sides, so it is clickable from its first millisecond. No staging position, no migration. R2: background is a preference, not a precondition; if the window comes up foreground anyway, accept it and stop, because fighting it is what produced every re-park loop. R3: a taskbar click always works, with no help from pup. R4: the hwnd is captured in the same locked moment the window is created, by set-diff over this profile's Chrome PIDs, which needs no unique birth coordinate; when two windows appear under the lock it now REFUSES to guess rather than risk a wrong handle. R5: AUMID and tile work stays async and after. Park is no longer a placement gate: bottoming is 3 bounded attempts instead of 8 slow ones and can never move, hide or off-screen the window when it fails. zConfirmedBelow is deleted; it gated placement on a proof that returned false whenever our hwnd was absent from ab's enumeration, which an off-screen window often is, making the failure self-sustaining. (John Lauer)
- NEW PLACEMENT RULES (John, 2026-08-20): windows are born on-screen at their final rect, background is a preference not a precondition, a taskbar click always works, identity is captured at birth, taskbar branding is async and after. Single source of truth in pup-bridge-dev R1-R5; the other skills now defer to it instead of contradicting it. (John Lauer)
- pup-user-foreground: OFF-SCREEN is the third way to break a taskbar click. When 'prove it is safe' fails, the fallback must still be a state the user can reach. (John Lauer)
- v2.0.258: Never rest a pup window off-screen. John could not open a freshly opened 'ab ralph' window: it sat at -21845,-21845 with a live taskbar button. Four separate paths in the launch park treated OFF-SCREEN as the safe fallback whenever z-bottom could not be PROVEN, logging 'self-heal will retry'. That trades a possible transient harm (a window briefly above the user's work) for a certain permanent one (the user can never open their own window), and the confirmation it waits for feeds the failure: zConfirmedBelow looks the hwnd up in ab's window enumeration and returns false when absent, and an off-screen window may not be enumerated at all, so once there it can never confirm its way out. All four paths now place the window ON-SCREEN at best-effort bottom, the moveOffScreen helper is deleted so it cannot be reintroduced, and the population sweep now repairs OFF-SCREEN windows as well as hidden ones (judging position from the restore rect so a minimized window is never disturbed) and runs every 30s instead of 120s. (John Lauer)