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 1148
Standalone per-platform binaries to download and run, no tools needed. The newest is pinned on top.
The taskbar progress bar is requested for every page load: open, navigate, new tab, reload, back and forward now run it on the window being worked on (only screenshots did before). A leftover safety setting (Minimal-touch, on by default) had switched it off for everyone, and on the extension lane it was aimed at whichever pup window Windows listed first (John, 2026-10-05). Note: Adom Bridge 2.1.168 does not render taskbar progress at all yet (adom/adom-bridge#224), so the bar appears once that is fixed. Includes 2.0.698: restored windows keep their videos paused for 30 s.
Stable link for websites and docs: /download/adom/pup-bridge/latest
All releases showing 161-180 of 1148
pup_clear_cookies {domain, names?}: drop a site's cookies in the window's jar, HttpOnly included, on either lane (chrome.cookies in the control extension; Network.getAllCookies + deleteCookies over CDP). Built to test and then fix the analog.com case John keeps asking about: it loads on a fresh jar and answers Access Denied on adom-you, which points at Akamai's verdict cookie (_abck at -1) carried over from the CDP-lane visits, not at the browser as it is now.
No duplicate tabs. A recovery relaunch that raced a caller's opens re-added the saved tab list on top of the caller's tabs (ti.com twice, 33 tabs for 20 sites, John 2026-09-13). Two fixes: pup_open_tab on the extension lane de-dups by URL the way the CDP lane has since 2.0.478 (a second open of a URL the window already holds activates that tab); and a relaunch reopens its saved tabs ONLY into the window it made itself, never onto a newer window for the same id.
An extension-lane window survives a pup restart intact. On the 2.0.535 restart, recovery declared the lane gone two seconds after boot, before the extension's first heartbeat, KILLED the live browser ('ended 5 stale browser process(es)'), rebuilt the 20-tab window from its file, and then did it all again 17 s later because the orphan pass (CDP-shaped) removed the rebuilt session and the file pass (pid 0 reads as gone) relaunched it. Three fixes: readopt_ext waits up to 8 s for the first heartbeat when the lane's pids were re-attached; ext session files never enter the CDP reconnect, at boot or on the health tick, and are relaunched only when the lane is really dead; the tabless/orphan pass skips ext sessions.
The default lane logs in and downloads; nothing on it is a silent no-op any more. ab-smoke on 2.0.534 failed login and download because both go through resolve_tab, the seam every CDP-only verb uses, which found no CDP handle on an ext session and did nothing. resolve_tab now refuses an ext session loudly (lane_needs_cdp, saying what works here and how to opt into CDP), so all 25 CDP-only call sites fail visibly instead of silently. pup_login has an ext arm: the same locate/fill/submit scripts and the same wall detector (hoisted to WALL_JS), evaluated through the transport; realKeystrokes stays CDP-only. pup_download has an ext arm through chrome.downloads in the control extension, returning savedTo/filename/bytes in the same envelope.
The identity button shows the user's avatar. Chrome lets an extension set its action icon at any time (chrome.action.setIcon with imageData), so the identity extension now has a service worker that fetches the adom-you avatar through a new pup endpoint, GET /identity/avatar (pup fetches it server-side with its own token, cached 10 min, so the extension needs no host permission for wherever the avatar lives), draws it circle-cropped onto the family's teal tile at 16/32/48/128, and sets it as the button; refreshed every 10 minutes and retried every 30 s until pup answers. John: 'you should be showing my avatar up in your button in the chrome toolbar ... or can you just do it dynamically whenever you want?' Yes, dynamically.
An extension-lane session is re-adopted across a pup restart, never relaunched, while its Chrome window is still in the lane browser. Recovery's liveness check is CDP-based, which an ext window never satisfies, so a restart declared both ext sessions' Chrome gone and relaunched them as new windows beside the originals (the 17-tab proof window orphaned, a duplicate opened next to it). The session file carries windowId; the live tabs listing says whether that window still exists and which tabs it holds, and the session is rebuilt from that, at boot and on the health tick.
Two gaps from the 20-site proof. (1) A command in flight counts as alive: the worker does not poll while it executes a command, so a slow site's tab open (nordicsemi, ~30 s) stopped the heartbeat, alive() read the lane as dead, and the next three tab opens were refused with 'browser not running' while the browser was fine. Heartbeat window is 20 s and an in-flight command holds it. (2) A tab open that outlives the CLI relay is no longer lost: the ext tab wait is 20 s so it returns before ab's ~60 s relay, and refresh_all adopts any tab Chrome has in the session's window that pup does not track, the way the CDP lane's adopt_window_tabs does. 17 of 20 tabs were in the window; pup recorded 16.
No lane verbs, gated on the edit and the build this time (2.0.529 and 2.0.530 shipped without it). pup_ext_* are gone from the verb surface and every hint: a lane window is a pup window, driven by pup_open_window / pup_navigate / pup_eval / pup_screenshot / pup_open_tab / pup_list_tabs / pup_switch_tab / pup_close_window. Lane health is the extensionLane field of pup_status.
No lane verbs (2.0.529 shipped without this by mistake: an assertion fired before the edit was written). pup_ext_* are gone from the verb surface: a lane window is a pup window, driven by pup_open_window, pup_navigate, pup_eval, pup_screenshot, pup_open_tab, pup_list_tabs, pup_switch_tab, pup_close_window. Lane health is the extensionLane field of pup_status. SKILL.md and every hint updated.
No lane verbs. pup_ext_open/navigate/eval/tabs/tab/activate/screenshot/close/status/diag are gone from the verb surface (John: 'why am i still seeing you talk about verbs that have ext in them'). A lane window is a pup window: pup_open_window, pup_navigate, pup_eval, pup_screenshot, pup_open_tab, pup_list_tabs, pup_switch_tab, pup_close_window. Lane health (connected, lastPollMsAgo, processes, windows) is a field of pup_status. SKILL.md examples updated; the profile_owned_by_ext_lane hint no longer names removed verbs.
A killed lane reads as dead at once. alive() is heartbeat-based and a browser killed 3 s after its last poll still counted as alive for 7 more seconds, so the next pup_open_window took the warm path and waited 60 s on a dead channel until ab timed out (the first cold open of the 20-site proof, 2026-09-13; the retry two minutes later came up in 7.3 s). kill_extdrive now zeroes the heartbeat, and a warm open whose channel does not answer within 20 s falls back to a cold launch instead of failing.
lane:cdp takes the adom-you profile back from the extension lane, symmetric with the ext birth taking it from CDP. The two lanes cannot share one --user-data-dir (Chrome is one process per profile), so an explicit lane:cdp open on adom-you now closes the ext windows and launches CDP instead of failing with a raw profile_owned_by_ext_lane error. Default stays ext; lane:cdp is the opt-in for CDP-only features (iframes, trusted input, annotate).
captureVisibleTab needs : added it to the pup control extension so pup_screenshot works on the extension lane (it hit 'the or activeTab permission is required'). session_row now reports transport/lane so pup_list_windows and pup_status show which lane a window is on. Follows 2.0.525 making the lane a first-class session.
The extension lane is a first-class pup session. Session gains transport (cdp|ext, round-tripped through the session file); verbs/src/transport.rs is the ONE seam a verb reaches the page through (navigate, evaluate, screenshot, open/activate/refresh/close tab, close window), with a CDP arm and an extension-channel arm. pup_open_window picks the lane before any browser launches: defaultLane is ext, so a plain open is an extension-driven window on adom-you with no debug port; lane:cdp opts into CDP-only features (iframes, trusted input, annotate, highFps, isolation). identity's tile and badge are composed through the transport, so an ext window wears the same thread tile and the same 75% badge; list_tabs, open_tab, switch_tab, close_tab and close_window branch on the transport; recovery relaunches a window on the lane it was born on. pup_ext_* remain as aliases of the session verbs. This is the structural answer to John's 'why is the non-cdp lane a whole new set of code'.
Share the progress-bar code instead of the lane keeping its own. Extracted activity::progress_on_hwnd as the single implementation (the filling, running-average-estimated bar capped at 92% until work finishes, then 100% and cleared); with_progress is now a thin session-keyed entry point and the extension lane calls the same function with its window's hwnd. The lane previously hand-rolled a flat 35% bar and discarded ab's reply. Part of ending the pattern John called out: the lane reinventing code the CDP lane already has. Overlay (badge_js) and identity (stamp_hwnd) are already shared; progress was the remaining duplicate primitive.
One overlay composer for both lanes. The extension lane's badge (2.0.520/521) was my own full-bleed 32px PNG composed in the worker, ignoring everything identity::overlay already does: overlaySize (default 75, because at 100 the badge covers the thread tile's bottom line of text, John's 2.0.262 request), the adaptive contrast card, the generic fallback. It stamped over the tile's name. John: 'go look at your overlay icon code ... you fucked up the sizing on your overlays.' The lane now runs identity's badge_js through its own eval channel (it has no CDP), with the same second-rail fetch_favicon_oob fallback, and paints with the same desktop_taskbar call. The worker's private composer is deleted.
Give the nbe-in-pup button a DISTINCT icon; prune retired staged extension folders. nbe's own icon is byte-identical to pup's identity icon (both the teal two-teardrop glyph), so on the shared adom-you toolbar the identity and nbe buttons were indistinguishable and read as a duplicated, broken button (John: 'what the fuck did you do with the adom-you browser extension button'). The pup-staged nbe copy now carries a distinct browser-window mark (same teal family, same filenames so the manifest and the key-derived extension ID are untouched; tools/sync-nbe.sh re-applies it so a re-sync from nb's repo never restores the colliding icon). Separately, stage_extensions_lane now prunes any pup-*-extension folder no longer in the build, so a retired placeholder like pup-lane-extension cannot linger and add a stray icon.
Overlay badge: compose the favicon to a 32px PNG in the extension before handing it to ab. Raw favicon bytes worked for PNG-serving sites (st.com, mouser) and failed the moment a site served an ICO/SVG: digikey's badge was refused by ab's LoadImageW, so the button kept showing Mouser's M after the navigate. The CDP lane never hits this because badge_js draws onto a canvas and ships a PNG; the worker now does the same with createImageBitmap + OffscreenCanvas. Progress bar: use exactly the shape activity.rs sends (state normal with a value, then none) and log a refusal instead of discarding ab's reply, which is why the first cut could not say whether the bar ever showed.
The extension lane keeps the taskbar contract the CDP lane keeps. John: 'why are you causing flashing of the windows taskbar icon when we've been through this 100 times ... you're supposed to default to showing progress bars in the icon. and why do i not see the overlay icon'. The lane made none of the three ab calls the CDP lane makes. Now: an indeterminate progress bar on the lane's button while open/navigate/tab run, cleared after (honouring taskbarActivity and minimalTouch exactly as activity.rs does); the active tab's favicon painted as the overlay badge, fetched by the extension itself since this lane has no CDP to fetch it with; and clear_attention after each of those, which removes the tint Windows paints when it denies Chrome the foreground. That tint was the flash: pup never called desktop_flash_window for this lane, the OS did it.
Re-assert the lane window's size and normal state on every open. fit ran only when dress_window first dressed a window, so a window that later ended up minimized was never corrected: pup reported the lane up and John had nothing on his screen, three times now. Chrome's own saved placement was already the full work area, so this is purely the window being minimized at the OS level afterwards. chrome.windows.update with state normal also restores a minimized window, and it does it without raising anything, which is why this goes through the extension rather than ab geometry.