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 141-160 of 1148
The thread tile is rendered in the binary (John, 2026-09-14: 'it should just be in pup's rust binary. why would we use javascript?'). Until now the taskbar name tile was JavaScript evaluated inside whatever page the window was on, a leftover from the Node bridge that could not draw text; a strict Content-Security-Policy (claude.ai, the wiki) refused it and the button silently fell back to a nameless per-window icon. pup_identity::tile now lays out and rasterises the tile with fontdue from the same Familjen Grotesk Bold face (fg-700 converted to TTF), same layout rules, same plate, every ICO entry at its own pixel size; output verified pixel-for-pixel against the browser-rendered tiles on the laptop. The page-evaluation path, tile_js and the embedded woff2 are deleted. Also: the extension lane stamps the LIVE window before pup_open_window returns (birth used to bind Chrome's transient launch window), and recovery stamps a re-adopted window immediately; the 15 s health-tick rebind is now only a backstop. identity.rs was reconstructed after a bad restore this afternoon; all identity and verbs tests pass.
Extension-lane taskbar identity, second pass, from pup.log rather than theory. Birth bound the window to a TRANSIENT hwnd (the about:blank launch window Chrome replaces) and the stamp failed with 'not a valid window'; recovery then re-adopted the real window after each bridge restart but never stamped it; 2.0.557's late rebind only handled hwnd None so it skipped a window that was bound-but-unstamped. Now the health-tick rebind covers all three states (unbound, bound to a dead hwnd, bound but unstamped) and stamps. pup_audit_icons reads identity back from Windows through ab instead of trusting the in-memory app_id, which does not survive a restart and produced a false 'unstamped' finding on a correctly stamped window.
Taskbar identity regression in the extension lane, fixed (John, 2026-09-14: 'why is my windows taskbar icon just this? did you regress on the windows taskbar icons?'). An ext-lane window whose birth outran open_window_ext's 3 s hwnd wait (the 2.0.545 birth goes about:blank, identity shim, then the real URL, which can take longer on a cold browser) kept hwnd None for ever, so identity::stamp had nothing to stamp and the button wore Chrome's stock icon under its native per-profile AUMID. Three changes: (1) extdrive::rebind_unbound runs on every 15 s health tick and on pup_rescan, binding every unbound ext session to the lane window and stamping it exactly as a timely birth would; (2) pup_audit_icons now reports an unbound or unstamped session as a finding instead of zero findings; (3) pup_rescan judges ext sessions by the lane's liveness rather than a debug port they deliberately never have, so it stops calling a live ext window 'their Chrome is gone'. The birth wait is also 6 s. Carries 2.0.556's --disable-extensions-except bubble fix.
The real fix for the external-extension Action required bubble, replacing the 2.0.553 attempt. Diagnosis: on Windows, extension settings live in Secure Preferences under MAC protection, not in Preferences, so pre-seeding extensions.settings..ack_external (2.0.553) never landed and is removed as an approach. The registry-installed extensions (Adobe Acrobat efaidnbmnnnibpcajpcglclefindmkaj, Application Launcher for Drive lmjegmlicamnimmfhcmpkclmigmmcbeh) were confirmed present in adom-you Secure Preferences with location 6 (external registry) and unacknowledged, which is exactly the state that raises the bubble. Fix: every Chrome for Testing launch now passes --disable-extensions-except=<pup's own extension paths> alongside --load-extension, so only pup's toolbar extensions are enabled and Chrome's external registry providers do not install anything into pup profiles. Profile-scoped via launch flags; the machine registry is untouched.
Corrects a claim 2.0.552 through 2.0.554 shipped, and labels an unproven fix honestly. (1) WITHDRAWN: the statement that neither profile state nor cookie history is the discriminator for Akamai vendor sites. That rested on a warm-versus-fresh profile comparison, but isolated:true does not create a separate profile (it forces the CDP lane and the anonymous tile while the jar stays adom-you, filed as pup-bridge#112), so the comparison ran adom-you twice. Profile state is now documented as UNTESTED rather than excluded, across the root skill, pup-why-cft and pup-bot-walls. (2) The ack_external seeding added in 2.0.553 for the external-extension Action required bubble is retained but is UNVERIFIED: repeated readings of the same profile disagreed and extensions.settings read back empty, so there is no evidence it suppresses the dialog. Do not describe the bubble as fixed. (3) Carries forward 2.0.554's corrected 19-of-20 vendor-site score with analog.com intermittent, the measurement-hygiene section, pup_close_all_windows, and the em-dash sweep.
Skills updated with what today actually established, replacing what it only appeared to. (1) The 20-of-20 vendor-site score is corrected to 19 of 20: analog.com was denied as the 7th of 20 sequential loads having rendered fine on a single visit minutes earlier, and the clean run did not reproduce. analog.com is documented as intermittent under sequential load rather than solved. (2) New measurement-hygiene section in pup-browser-detection covering the five mistakes that produced four wrong causal stories in one day: comparing pup against a hand-rolled Chrome launch, reading window titles instead of screenshots, poisoning your own egress reputation by repeated probing, promoting a single passing run to documentation, and leaving orphaned Chrome processes holding profiles. Carries forward 2.0.553's ack_external fix for the external-extension Action required bubble, pup_close_all_windows, and the em-dash sweep.
Three fixes. (1) The Action required bubble: Chrome's ExternalInstallManager gates the 'Another program on your computer added an extension' dialog per-extension on extensions.settings..ack_external, NOT on the extensions.alerts.initialized flag pup was setting, so the old suppression never applied to it (John hit it again on 2026-09-14 for Application Launcher For Drive). prepare_profile now pre-acknowledges every machine-wide registered id, profile-scoped; pup still never edits the machine's registry. The registry read lives behind a new defaulted Host::external_extension_ids implemented only in host-win. (2) pup_close_all_windows is served instead of erroring not_in_rust_yet with no equivalent named, the same defect class as #108; it aliases onto close_window's existing all:true path, so it closes only the caller's own windows. (3) Em-dashes removed from the four pup skill files, 156 of them, per John's standing style rule.
Two corrections. (1) adom/pup-bridge#108: pup_list_sessions now resolves to the same caller-scoped implementation as pup_my_windows (registered passive, so it never launches a browser) and carries a catalog entry that survives regeneration, instead of erroring not_in_rust_yet while naming no supported equivalent. (2) WITHDRAWN across the root skill, pup-why-cft, pup-browser-detection and pup-bot-walls: the claim that matching CfT to the installed Chrome is what makes vendor sites load. That comparison was uncontrolled, every denied trial was a raw hand-rolled Chrome launch without pup's flag set while every passing trial was pup. Re-measured with screenshots on 2026-09-14, analog.com renders in pup on CfT 152 on both a warm profile and a brand-new isolated jar, so neither the build nor the cookie jar is the established discriminator. Keep the pin near the installed Chrome as hygiene, not as an explanation.
Correct the causal story for vendor walls in the skills: a stale Chrome-for-Testing did NOT break Akamai's bmak sensor. analog.com answered HTTP 403 from AkamaiGHost on CfT 148, which is a refusal at the HTTP layer before any page JavaScript runs; CfT 148 executes JavaScript fine. typeof bmak === undefined on a deny page is a symptom (the Access Denied page carries no sensor script), not the cause. The discriminator is the connection fingerprint (the TLS ClientHello differs between CfT builds); which element flips the verdict is not isolated. Corrected in the root pup skill, pup-why-cft, pup-browser-detection and pup-bot-walls.
A Chrome-for-Testing version bump now actually fetches the new build. detect_cft falls back to any cached CfT so an offline box still drives, but ensure_background short-circuited on that fallback, so 148 stayed cached and the pinned 152 was never downloaded. ensure_background now gates on the PINNED build: present = ready; missing = download it in the background while a cached fallback drives meanwhile, so a box upgrades on its own after a bump.
Match Chrome for Testing to the user's installed Chrome: CFT_BUILD_ID 148.0.7778.97 -> 152.0.7977.82 (the CfT build closest to the measured installed Chrome 152.0.7977.83). Pup drove CfT 148 while presenting 152 in the UA, a version mismatch that shows in the TLS ClientHello: the extension SET Chrome offers changes between 148 and 152 (post-quantum key share, ECH, cert compression roll in over versions), which is where the lane's JA4 diverged from real Chrome and where Akamai on analog.com keys. Matching the version makes the UA rewrite truthful and the ClientHello native-152. The box fetches 152 on the next pup_prewarm / ext open (~150 MB); the identity brand-list shim still runs because CfT omits the Google Chrome brand regardless of version.
Correction to 2.0.546's claim: the startup-promo bar was NOT suppressed by disabling the named features (read from a screenshot after the promotion, which was my error). The promo is field-trial gated; laneFieldTrials (default on) adds --disable-field-trial-config to the stock launch when off, the flag that removed the bar in 2.0.505. Whether that flag changes what analog.com's edge sees is measured before it becomes the default.
The stock lane launch keeps the UI-only suppressions. 2.0.545's stock flag set brought back the 'Chromium now launches when Windows starts' bar (John's 2.0.505 fix) and the sign-in promos, because it dropped the whole feature list. The startup-promo and sign-in features are disabled by name again; they change nothing on the wire. Field trials stay on, since --disable-field-trial-config alters feature rollouts and with them the request fingerprint that this launch exists to keep stock (analog.com denies the non-stock one).
analog.com loads on the extension lane. Measured on AdomLapper 2026-09-13: with the lane presenting the installed Chrome 152 on the wire and in JS AND launching with only the firewall guards (no QUIC, no Cast/mDNS) and everything else stock, analog answered its real page with the Akamai sensor running; the identity alone under the old flag set was still denied, so the launch flags (the TLS ClientHello they shape) were the decisive tell. laneStockFlags now defaults on. A cold launch goes to about:blank, applies the identity, and only then navigates, so the very first real request carries it (the launch page used to load before the rule existed).
JS identity shim that actually holds: navigator.userAgentData is a NEW object on every access in Chrome 148 (measured: navigator.userAgentData === navigator.userAgentData is false), so a property defined on one instance was lost and userAgentData.brands still read Not/A)Brand/99 Chromium/148 while the wire said 152. The shim now installs a Navigator.prototype.userAgentData getter that wraps the real object in a Proxy (brands, fullVersionList, getHighEntropyValues, toJSON), and userAgent/appVersion on the prototype too. Plus laneStockFlags (default off): an experiment lever that launches the lane with only the firewall guards (no QUIC, no Cast/mDNS listener) and everything else stock, so the TLS ClientHello is real Chrome's, for edges that still deny after every header matches (analog.com).
The extension lane presents the user's INSTALLED Chrome, on the wire and in JS. Measured via nbe on AdomLapper: the user's Chrome is 152 and sends Chromium;v=152, Not?A_Brand;v=24, Google Chrome;v=152, while Chrome for Testing 148 sent Not/A)Brand;v=99, Chromium;v=148 and analog.com's edge denied it as real Chrome passed on the same IP over the same protocol. The lane now rewrites User-Agent, Sec-CH-UA and Sec-CH-UA-Full-Version-List to the installed version with real Chrome's brand order and GREASE (consumerBrandsHeader pins the exact header), and a document_start MAIN-world shim (baked at staging) mirrors navigator.userAgent and userAgentData. The CDP lane's metadata uses the same order and GREASE, so both lanes match the user's Chrome.
A lane browser that outlives a pup update no longer runs the old extension unnoticed. The hello now carries the worker's versioned filename; pup_status.extensionLane reports workerVersion and stale, with a hint; and the health tick cycles a stale lane the moment no extension-lane window is open, so the next open stages the current extension (measured: worker.2-0-539 kept serving under pup 2.0.541, and the brand rewrite shipped in .540 never ran until the lane was cycled by hand). Also pup_clear_site_data {origin|origins}: cache, cookies, storage and service workers for an origin, through chrome.browsingData on the lane and Storage.clearDataForOrigin over CDP.
Apply the consumer brand rewrite at the session birth (2.0.540 hooked it into dress_window, which the session-based birth never calls, so it never ran). setBrands now reports whether Chrome accepted the declarativeNetRequest rule and how many dynamic rules are live, so a silently ignored header shows in the log instead of being guessed at.
The extension lane sends real Chrome's brand list. Measured on AdomLapper: Chrome for Testing's first request carries Sec-CH-UA without the Google Chrome brand ('Not/A)Brand;v=99, Chromium;v=148'), and analog.com's Akamai edge answers that first request with a live 403 (cache bypassed, cookies cleared, sensor never runs) while arrow, mouser and st accept the same jar, IP and UA. The control extension now installs a declarativeNetRequest rule rewriting Sec-CH-UA and Sec-CH-UA-Full-Version-List to what real Chrome sends, applied once per lane browser (consumerBrands, default on). The CDP lane already patched brands over CDP; this is the lane's own way to do it.
pup_clear_cookies {domain, names?} is wired (2.0.538 shipped its transport half only: a heredoc failure slipped past the ship gate). Drops a site's cookies in the window's jar, HttpOnly included, on either lane. Built to test and fix the analog.com case: loads on a fresh jar, Access Denied on adom-you, which points at Akamai's verdict cookie carried over from the CDP-lane visits.