app
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.
← Commit history
v2.0.739 (insiders): Never a bare Chrome taskbar button during recovery (pup-bridge#143): an OS-level health-tick safety net stamps a pup identity on any pup Chrome-for-Testing window that has no session yet (the gap after a restart or worker-reload cycle while the extension channel re-handshakes), so a pup window never falls back to the stock Chrome button; it reads+stamps only and never touches the lane/kill paths, and the real per-thread tile upgrades it once adopt_stray re-adopts the window.
4 files changed
+64−13
rust/Cargo.lock+12−12@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "adom-pup-bridge"-version = "2.0.738"+version = "2.0.739" dependencies = [ "pup-browser", "pup-cdp",@@ -781,7 +781,7 @@ dependencies = [ [[package]] name = "pup-browser"-version = "2.0.738"+version = "2.0.739" dependencies = [ "pup-core", "pup-host",@@ -793,7 +793,7 @@ dependencies = [ [[package]] name = "pup-cdp"-version = "2.0.738"+version = "2.0.739" dependencies = [ "serde_json", "thiserror",@@ -803,7 +803,7 @@ dependencies = [ [[package]] name = "pup-core"-version = "2.0.738"+version = "2.0.739" dependencies = [ "serde", "serde_json",@@ -813,7 +813,7 @@ dependencies = [ [[package]] name = "pup-dashboard"-version = "2.0.738"+version = "2.0.739" dependencies = [ "pup-cdp", "pup-core",@@ -826,7 +826,7 @@ dependencies = [ [[package]] name = "pup-host"-version = "2.0.738"+version = "2.0.739" dependencies = [ "serde", "serde_json",@@ -835,7 +835,7 @@ dependencies = [ [[package]] name = "pup-host-mac"-version = "2.0.738"+version = "2.0.739" dependencies = [ "core-foundation", "core-graphics",@@ -851,7 +851,7 @@ dependencies = [ [[package]] name = "pup-host-win"-version = "2.0.738"+version = "2.0.739" dependencies = [ "pup-core", "pup-host",@@ -861,7 +861,7 @@ dependencies = [ [[package]] name = "pup-identity"-version = "2.0.738"+version = "2.0.739" dependencies = [ "fontdue", "image",@@ -871,7 +871,7 @@ dependencies = [ [[package]] name = "pup-session"-version = "2.0.738"+version = "2.0.739" dependencies = [ "pup-core", "serde",@@ -881,7 +881,7 @@ dependencies = [ [[package]] name = "pup-vault"-version = "2.0.738"+version = "2.0.739" dependencies = [ "pup-core", "pup-host",@@ -892,7 +892,7 @@ dependencies = [ [[package]] name = "pup-verbs"-version = "2.0.738"+version = "2.0.739" dependencies = [ "base64", "png 0.17.16",
rust/Cargo.toml+1−1@@ -4,7 +4,7 @@ members = ["core", "host", "host-win", "host-mac", "cdp", "browser", "session", # One version for the whole bridge, in lockstep with src/BRIDGE_VERSION (tools/ship.sh bumps both). [workspace.package]-version = "2.0.738"+version = "2.0.739" edition = "2021" license = "MIT"
rust/verbs/src/identity.rs+50@@ -397,3 +397,53 @@ pub fn audit(ctx: &Ctx) -> Value { pup_core::ok(json!({ "mode": mode(ctx), "windows": rows, "ok": true, "findings": findings, "_hint": if findings.is_empty() { "What pup believes each window is stamped with, read back from Windows." } else { "One or more windows cannot carry pup identity; see findings." } })) } ++/// 2.0.739 (pup-bridge#143): the "never a bare Chrome button" safety net.+///+/// After a bridge restart or a worker-reload cycle there is a gap where a pup-profile Chrome-for-Testing+/// window exists but has no session yet: the extension control channel is still re-handshaking, so+/// `adopt_stray_windows` (which drives the extension) cannot run, and the window sits on the taskbar wearing+/// Chrome's stock button with no thread tile. This recurs on John's laptop on every insiders ship.+///+/// This runs on every health tick and works at the OS level, independent of the extension channel, so it+/// closes the gap even while the channel is down. It enumerates pup CfT windows by PID (never the user's own+/// Chrome, never a CDP browser pup drives), and on any that a session does not already own and that is not+/// already carrying a pup AUMID, it stamps a grouped `Adom Pup` identity. That identity upgrades to the real+/// per-thread tile the moment `adopt_stray_windows` re-adopts the window and its own stamp runs. Read + stamp+/// only; it never touches the lane or kill paths that caused the 2.0.723 / 2.0.646 regressions.+pub fn stamp_stray_cft_windows(ctx: &Ctx) {+ if !cfg!(windows) { return; }+ if mode(ctx) == "plain" { return; } // the user asked for stock icons; respect it+ let pup_pids: std::collections::HashSet<u32> = crate::pup_browser_pids_by_profile(ctx).into_values().flatten().collect();+ if pup_pids.is_empty() { return; }+ let cdp: std::collections::HashSet<u32> = ctx.browsers.lock().unwrap().values().map(|b| b.launched.pid).filter(|p| *p > 0).collect();+ let owned: std::collections::HashSet<i64> = ctx.sessions.all().iter().filter_map(|s| s.hwnd).collect();+ let caller = internal_caller();+ static DONE: Mutex<Option<std::collections::HashSet<i64>>> = Mutex::new(None);+ let mut done = DONE.lock().unwrap();+ let done = done.get_or_insert_with(Default::default);+ let wins: Vec<pup_host::HostWindow> = ctx.host.frame_windows_all(Some("Chrome_WidgetWin_1")).into_iter()+ .filter(|w| pup_pids.contains(&w.pid) && !cdp.contains(&w.pid) && w.title.ends_with("Google Chrome for Testing"))+ .collect();+ let live: std::collections::HashSet<i64> = wins.iter().map(|w| w.hwnd).collect();+ done.retain(|h| live.contains(h)); // forget closed windows+ // the generic grouped pup identity (one button for every not-yet-adopted pup window)+ let app_id = id::category_aumid("default");+ let display = "Adom Pup".to_string();+ let icon = icons_dir(ctx).join("adom-pup.ico");+ for w in wins {+ if owned.contains(&w.hwnd) { continue; } // a session owns it: its own stamp carries the real tile+ if done.contains(&w.hwnd) { continue; } // stamped once already; the session upgrade re-stamps later+ if w.title.starts_with("PUPBIRTH-") { continue; } // a window being born: its birth stamps it+ // already a pup button? read the live AUMID back from Windows before stamping+ let cur = ctx.ab.call("desktop_get_window_identity", json!({ "hwnd": w.hwnd }), &caller, AB_T).ok()+ .and_then(|r| { let o = r.get("output").and_then(|o| o.as_str()).and_then(|t| serde_json::from_str::<Value>(t).ok()).unwrap_or(r); o["data"]["windows"][0]["appId"].as_str().map(|s| s.to_string()) });+ if cur.as_deref().map(|a| a.starts_with(id::APP_ID)).unwrap_or(false) { done.insert(w.hwnd); continue; }+ if !preregister(ctx, &caller, &app_id, &display, &icon) { continue; }+ let launch = exe_launch();+ match ctx.ab.call("desktop_set_window_identity", json!({ "hwnd": w.hwnd, "appId": app_id, "displayName": display, "iconPath": icon.to_string_lossy(), "relaunchCommand": id::relaunch_command(&launch, &format!("stray-{}", w.hwnd)), "relaunchDisplayName": display, "relaunchIconResource": icon.to_string_lossy() }), &caller, AB_T) {+ Ok(_) => { done.insert(w.hwnd); pup_core::log(format!("[identity] safety net: stamped a pup identity on an unadopted CfT window (hwnd {}, \"{}\") so it never shows a bare Chrome button while recovery catches up", w.hwnd, w.title)); }+ Err(e) => pup_core::log(format!("[identity] safety net: could not stamp hwnd {}: {e}", w.hwnd)),+ }+ }+}
rust/verbs/src/recovery.rs+1@@ -766,6 +766,7 @@ pub fn health_tick(ctx: &Ctx) { crate::extdrive::rescue_birth_tabs(ctx); // 2.0.724: a window stuck on its birth page (PUPBIRTH-... on the taskbar) crate::extdrive::adopt_stray_windows(ctx); // 2.0.694: windows Chrome made outside pup (a dragged-out tab, a page's popup) crate::extdrive::rebind_unbound(ctx); // ext sessions whose birth outran the hwnd wait: bind + stamp now+ crate::identity::stamp_stray_cft_windows(ctx); // 2.0.739 (#143): no pup window wears a bare Chrome button while recovery catches up crate::extdrive::reload_stale_worker(ctx); // a bridge_install must not wait for every thread's window to close crate::extdrive::close_orphan_blank_windows(ctx, None); // and any about:blank window no session owns // 2.0.542: a lane browser that outlived a pup update keeps running the OLD extension until it cycles