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.697 (insiders): pup says when an update restarts its browser: a notification that your pup windows will close and come back in about a minute (nothing crashed), and another when they are all back, or which ones are not back yet (John, 2026-10-04: 'i just thought you had crashed'). Each notice's delivery is logged. Desktops with Windows Do Not Disturb on get them in the notification center instead of a popup.
3 files changed
+31−13
rust/Cargo.lock+12−12@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "adom-pup-bridge"-version = "2.0.695"+version = "2.0.697" dependencies = [ "pup-browser", "pup-cdp",@@ -655,7 +655,7 @@ dependencies = [ [[package]] name = "pup-browser"-version = "2.0.695"+version = "2.0.697" dependencies = [ "pup-core", "pup-host",@@ -667,7 +667,7 @@ dependencies = [ [[package]] name = "pup-cdp"-version = "2.0.695"+version = "2.0.697" dependencies = [ "serde_json", "thiserror",@@ -677,7 +677,7 @@ dependencies = [ [[package]] name = "pup-core"-version = "2.0.695"+version = "2.0.697" dependencies = [ "serde", "serde_json",@@ -687,7 +687,7 @@ dependencies = [ [[package]] name = "pup-dashboard"-version = "2.0.695"+version = "2.0.697" dependencies = [ "pup-cdp", "pup-core",@@ -700,7 +700,7 @@ dependencies = [ [[package]] name = "pup-host"-version = "2.0.695"+version = "2.0.697" dependencies = [ "serde", "serde_json",@@ -709,14 +709,14 @@ dependencies = [ [[package]] name = "pup-host-mac"-version = "2.0.695"+version = "2.0.697" dependencies = [ "pup-host", ] [[package]] name = "pup-host-win"-version = "2.0.695"+version = "2.0.697" dependencies = [ "pup-core", "pup-host",@@ -726,7 +726,7 @@ dependencies = [ [[package]] name = "pup-identity"-version = "2.0.695"+version = "2.0.697" dependencies = [ "fontdue", "image",@@ -736,7 +736,7 @@ dependencies = [ [[package]] name = "pup-session"-version = "2.0.695"+version = "2.0.697" dependencies = [ "pup-core", "serde",@@ -746,7 +746,7 @@ dependencies = [ [[package]] name = "pup-vault"-version = "2.0.695"+version = "2.0.697" dependencies = [ "pup-core", "pup-host",@@ -757,7 +757,7 @@ dependencies = [ [[package]] name = "pup-verbs"-version = "2.0.695"+version = "2.0.697" 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.695"+version = "2.0.697" edition = "2021" license = "MIT"
rust/verbs/src/extdrive.rs+18@@ -185,6 +185,16 @@ pub fn reload_stale_worker(ctx: &Ctx) { let files: Vec<Value> = ctx.sessions.all().into_iter().filter(|s| s.transport == "ext").map(|s| s.file_json()).collect(); let ids: Vec<String> = files.iter().filter_map(|f| f["sessionId"].as_str().map(|s| s.to_string())).collect(); crate::recovery::reserve(&ids); crate::recovery::mark_lost(&ids); // 2.0.682: pup ends this browser itself+ // 2.0.696: say so. The windows vanish for about a minute and come back one by one, and with no word from pup+ // that read as a crash (John, 2026-10-04: "i just thought you had crashed").+ let to = want.replace('-', ".");+ let toast = |title: String, body: String| {+ let r = ctx.ab.call("notify_user", json!({ "title": title.clone(), "body": body, "focus": false }), &crate::identity::internal_caller(), Duration::from_secs(8));+ pup_core::log(format!("[extdrive] update notice \"{title}\": {}", match r { Ok(_) => "shown".to_string(), Err(e) => format!("NOT shown ({e})") }));+ };+ if !ids.is_empty() {+ toast(format!("pup is updating to {to}"), format!("Your {} pup window{} will close and come back in about a minute, on the same pages. Nothing has crashed.", ids.len(), if ids.len() == 1 { "" } else { "s" }));+ } for id in &ids { ctx.sessions.remove(id); } let n = crate::kill_extdrive(ctx); if n > 0 { *ASKED.lock().unwrap() = Some(want.clone()); } // 2.0.690: done only once the old browser really ended@@ -195,6 +205,14 @@ pub fn reload_stale_worker(ctx: &Ctx) { while t0.elapsed() < Duration::from_secs(8) && hwnds.iter().any(|h| ctx.host.is_alive(*h)) { std::thread::sleep(Duration::from_millis(200)); } pup_core::log(format!("[extdrive] lane worker {} is behind pup {}: re-staged the extension and cycled the lane browser ({n} process(es), windows gone after {} ms); relaunching its {} window(s) from their files on the new extension", have.replace('-', "."), want.replace('-', "."), t0.elapsed().as_millis(), ids.len())); for f in files { crate::recovery::relaunch_from_file(ctx, &f); }+ if !ids.is_empty() {+ let back: Vec<&String> = ids.iter().filter(|id| ctx.sessions.exists(id)).collect();+ let missing: Vec<&str> = ids.iter().filter(|id| !ctx.sessions.exists(id)).map(|s| s.as_str()).collect();+ let secs = t0.elapsed().as_secs();+ if missing.is_empty() { toast(format!("pup updated to {to}"), format!("All {} window{} are back ({secs} s).", back.len(), if back.len() == 1 { "" } else { "s" })); }+ else { toast(format!("pup updated to {to}"), format!("{} of {} windows are back. Not back yet: {}. pup keeps trying in the background.", back.len(), ids.len(), missing.join(", "))); }+ pup_core::log(format!("[extdrive] update cycle finished in {secs} s: {} of {} window(s) back{}", back.len(), ids.len(), if missing.is_empty() { String::new() } else { format!("; not yet: {}", missing.join(", ")) }));+ } } /// 2.0.647: a lane that ends takes its worker version with it; a fresh browser reports its own at hello.