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 181-200 of 1148
Complete the adom-you handover, and stop the portless launch from dumping tabs into another thread's window. 2.0.517 closed the CDP browser only if it was in ctx.browsers, which is EMPTY right after a pup restart while a Chrome from before the restart is still holding the directory (pup reconnects to those lazily). So the handover no-opped, the portless launch forwarded to that browser, and Chrome opened the lane's URL as a tab inside the kicad-bridge thread's window: John saw a tab appear in someone else's pup window, which is the exact discourtesy this project has a rule about. The profile's own DevToolsActivePort is the source of truth, so the lane now closes whatever is on that port and waits for SingletonLock to clear before launching. Also fixed pup_ext_status still reporting the retired adom-you-ext profile name.
The extension lane runs on adom-you. One profile, the user's real jar, which is what John has said all along should never be anything else: the separate adom-you-ext jar carried none of his cookies, logins or history (measured, 0.2 MB of History against adom-you's 6.3 MB), which is both a pointless divergence and the empty-profile shape bot walls score against. It was only ever separate because Chrome allows ONE process per --user-data-dir and this lane must run with NO debug port, so it cannot coexist with a CDP browser on the same directory. The lane now TAKES the profile: any CDP browser holding adom-you is closed first (otherwise the portless launch forwards to it and exits, silently handing back a window that still carries a debug port), and a CDP launch on adom-you while the lane owns it is refused with a hint to drive pup_ext_* instead of failing 30 seconds later on a missing DevTools port. Nothing is refused once the lane is closed.
Stop labelling the extension lane anonymous. The identity extension was picked with profile == 'adom-you' exactly, and the lane runs on adom-you-ext, so every lane window wore the anonymous tile: 'not signed in as you, isolated throwaway jar, logins here do not enrich adom-you'. That is false, the lane profile is pup-owned and durable, and John flagged the second-order cost too: a window that advertises itself as an empty throwaway is the profile shape bot walls score against. Any adom-you* jar now wears the signed-in identity. This corrects the LABEL only; the lane still keeps its own cookie jar, which is the substantive issue.
nbe always rides the extension lane; remove the nbeInLane flag. 2.0.509 hid nbe behind that flag because the lane looked dead with it loaded, which was wrong: the lane was equally dead without it and the real cause was pup wiping its own lane token in launch_extdrive (fixed in 2.0.513). The flag was never registered as a settings key either, so pup_configure silently ignored it and reported nbeInLane=None. John needs nbe's toolbar icon as the at-a-glance signal for which lane a window is on: three teal buttons means the CDP lane, four means the extension lane.
Adopt a running extension browser pup has lost track of. When nothing has been served since this pup launched and a browser on pup's own profile says hello with a lane token, pup takes that token instead of fighting it. That case is unwinnable otherwise: the browser is unkillable because pup no longer knows its pids (a restart, or a close whose kill missed the main process and deleted lane.json on the way out), and each new launch simply forwards to it and exits, which is the '1 process(es)' line that has haunted this lane all day. Adoption cannot hijack a working lane, because a working lane has served at least one poll.
Publish the lane token AFTER the stale-browser kill, not before. 2.0.510 taught kill_extdrive to clear EXTDRIVE_LANE, which is correct on its own, but launch_extdrive set the token first and called the kill afterwards, so every launch wiped the token for the launch it was preparing: the extension polled with the correct lane and pup compared it against an empty string and refused, forever. That is why the lane looked dead since 2.0.510 and why I wrongly blamed nbe, then the port.json memo. The /exthello line added in 2.0.512 printed the mismatch in one line: 'carrying lane ext-... (pup wants "")'.
Make the extension lane observable. It deliberately runs with NO CDP debug port, which is the whole point of the lane and also means that when its extension goes quiet there is no way to look inside: today the window launched, loaded all three toolbar extensions, sized and stamped correctly, and the control channel stayed silent with nothing anywhere saying why, so I was reduced to guessing. The extension now POSTs /exthello on every poll attempt BEFORE the lane gate, carrying the lane token it actually holds plus whether its port.json read succeeded, and pup logs it throttled to one line per distinct lane per 30s, saying plainly whether that browser may take work. A token mismatch is now a log line instead of silence.
Fix the poisoned port.json memo that made the extension lane permanently silent. packagedPort() memoized on 'if (_pkgPort !== null)' while its catch set _pkgPort = 0, so ONE failed first read (it races service-worker startup, and sweepBadges can trigger it before the drive loop) locked the memo forever: 0 is !== null so it never retried, and _pkgLane stayed empty, which pupExtPoll treats as 'not the drive browser' and returns. Measured on AdomLapper: the lane window launched, loaded all three toolbar extensions, was stamped and sized correctly, and lastPollMsAgo was never set. Only a SUCCESSFUL read is memoized now; a failure leaves the memo unset so the next tick retries. Also correcting the record from 2.0.510's note: pup_ext_diag attaches over CDP to the adom-you browser by design and never talks to the lane, so the 2.0.506 worker it reported was not cross-talk.
Re-attach to an extension browser that outlived the pup process. EXTDRIVE_LANE and EXTDRIVE_PIDS were in-memory only, so every bridge_install (which restarts pup) orphaned a running ext browser permanently: take_command_for rejects every poll while the token is empty, so the live extension could never re-attach, alive() stayed false, the next pup_ext_open launched a SECOND Chrome that forwarded to the running instance and exited at once (the '1 process(es)' line in the log), and pup_ext_close killed nothing because it knew no pids. Measured on AdomLapper: pup reported browserRunning:false while two of its own Chrome for Testing windows were on John's screen. This also invalidated every nbe measurement taken today, including the one that made me blame nbe for the lane being down. The lane token and pids are now parked in extlane/lane.json, restored at boot against live pids only, and cleared when the lane is closed.
Put the nbe side-load behind pup_configure {nbeInLane}, default off, so the extension lane works again while the cause is found. Measured on AdomLapper: with nbe in the --load-extension list Chrome for Testing spawns and exits within 6 seconds, no load-error dialog, where 2.0.506 came up every time. nbe itself stages correctly now (src/sw.js and the untouched 0.2.13 manifest verified on disk, and the staging sanity gate raises no objection), so the failure is in launching Chrome with it, not in the copy. Default off is the honest state until that is understood.
Fix the staging bug that made Chrome refuse nbe, and never hand Chrome an unverified folder again. tools/gen-assets.py listed only TOP-LEVEL files, so nbe's src/ and icons/ folders were dropped and the staged copy was a manifest pointing at a src/sw.js that was never written: Chrome answered with a modal 'Error Loading Extension ... Could not load background script' in John's face and loaded nothing. The generator now walks recursively, and the Rust staging creates parent directories before writing (it had ignored the failed write). Added a sanity gate: before a folder is passed to --load-extension pup checks that every file the manifest references (service worker, popup, icons) is actually on disk, and drops the folder with a log line instead of letting Chrome throw a modal.
Load the REAL nbe into the extension lane instead of my placeholder. John, correctly: 'i thought that was the whole point of me saying to you, just use the nbe inside pup'. On 2026-08-22 I agreed to add nbe as the 4th --load-extension and then wrote a 259-line control channel of my own instead; measured against nbe it has no click, type, dialog, upload, console or full-page capture at all, so it was a weak duplicate of code we already own. pup now stages nb's extension folder verbatim (src/extension-nbe, synced by tools/sync-nbe.sh, nb's repo stays the source of truth) and adds exactly one file: toolbar.json {toolbarIcon:on}, the packaged default nb built for pup on 2026-09-12 so the icon shows in a pup window while the user's own Chrome keeps Chrome's unpinned default. pup pins it, since an extension cannot pin itself and pup owns the profile. nbe's manifest is deliberately NOT version-stamped the way pup's own extensions are: nb's readiness compares per-profile extension versions and a rewrite would report the user's browser stale. Open question this ships to answer: whether Chrome for Testing finds the inc.adom.native_browser native-messaging host at all.
A fourth teal toolbar button on the extension-driven lane (John: 'i want a 4th icon showing in this bar that's teal and looks similar to your 3 browser extensions you load already so that i can differentiate between CDP lane pup windows and nbe lane pup windows'). New src/extension-lane: an MV3 tile in the existing toolbar family (the same teal rounded square and ink glyph as the dots, pen and identity marks), drawn as a browser window, with a popup saying what the lane is. It stages ONLY when a lane token is set, so three buttons means the CDP lane and four means the extension lane. Note for the record: pup loads no nb extension, so nothing of nbe's was involved; the buttons on a pup window are pup's own four extensions.
One shared DISABLE_FEATURES list across both browser lanes. The ext lane carried a hand-trimmed copy that had lost the startup-promo feature ids, so John saw Chromium's 'now launches when Windows starts so you can begin browsing instantly' bar on an extension window; it also never passed --disable-field-trial-config, which is what gates promos like that one. Both lanes now build their --disable-features from the same constant, so the two cannot drift again.
extdrive window sizing + taskbar identity + the CfT infobar. Three flags/paths the Rust ext lane never inherited from the CDP lane: --start-maximized (window opened at the profile's remembered ~60% width), --disable-infobars (CfT's 'only for automated testing' bar, which docs/WHY-CFT.md has always said pup suppresses this way), and dress_window running on every verb path instead of only the cold-launch path that returns early (pup.log had zero stamp lines). Identity now goes through the shared identity::stamp_hwnd so an ext window wears the same thread tile a CDP window does. Geometry goes through the extension's new fit action (chrome.windows.update, plain bounds, logical px both sides) rather than ab's desktop_set_window_bounds or a maximize, per the geometry rule: both of those raise the window.
extension windows get pup taskbar identity and placement
ext control channel scoped to its own browser via a per-launch lane token
extension lane gains tabs, screenshots and activation
extension service worker re-registered every release via a versioned script URL