← Commit history

v2.0.695 (insiders): An update never closes a window pup had not adopted yet: the browser cycle that loads a new extension now waits until every window in pup's browser has a session (about 5 s for a dragged-out tab or a page's popup), so it is saved and reopened like the rest. 2.0.694's install on John's laptop closed an AI Flow page and a YouTube video that way (2026-10-04). Tested on arav-rog: a popup opened right before the install was adopted, survived the cycle and came back with its thread's tile.

John Lauer ·5105cda8a4 ·17d ago ·parent 8cd6441
3 files changed +23−14
rust/Cargo.lock+12−12
@@ -10,7 +10,7 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"  [[package]] name = "adom-pup-bridge"-version = "2.0.694"+version = "2.0.695" dependencies = [  "pup-browser",  "pup-cdp",@@ -655,7 +655,7 @@ dependencies = [  [[package]] name = "pup-browser"-version = "2.0.694"+version = "2.0.695" dependencies = [  "pup-core",  "pup-host",@@ -667,7 +667,7 @@ dependencies = [  [[package]] name = "pup-cdp"-version = "2.0.694"+version = "2.0.695" dependencies = [  "serde_json",  "thiserror",@@ -677,7 +677,7 @@ dependencies = [  [[package]] name = "pup-core"-version = "2.0.694"+version = "2.0.695" dependencies = [  "serde",  "serde_json",@@ -687,7 +687,7 @@ dependencies = [  [[package]] name = "pup-dashboard"-version = "2.0.694"+version = "2.0.695" dependencies = [  "pup-cdp",  "pup-core",@@ -700,7 +700,7 @@ dependencies = [  [[package]] name = "pup-host"-version = "2.0.694"+version = "2.0.695" dependencies = [  "serde",  "serde_json",@@ -709,14 +709,14 @@ dependencies = [  [[package]] name = "pup-host-mac"-version = "2.0.694"+version = "2.0.695" dependencies = [  "pup-host", ]  [[package]] name = "pup-host-win"-version = "2.0.694"+version = "2.0.695" dependencies = [  "pup-core",  "pup-host",@@ -726,7 +726,7 @@ dependencies = [  [[package]] name = "pup-identity"-version = "2.0.694"+version = "2.0.695" dependencies = [  "fontdue",  "image",@@ -736,7 +736,7 @@ dependencies = [  [[package]] name = "pup-session"-version = "2.0.694"+version = "2.0.695" dependencies = [  "pup-core",  "serde",@@ -746,7 +746,7 @@ dependencies = [  [[package]] name = "pup-vault"-version = "2.0.694"+version = "2.0.695" dependencies = [  "pup-core",  "pup-host",@@ -757,7 +757,7 @@ dependencies = [  [[package]] name = "pup-verbs"-version = "2.0.694"+version = "2.0.695" 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.694"+version = "2.0.695" edition = "2021" license = "MIT" 
rust/verbs/src/extdrive.rs+10−1
@@ -169,6 +169,9 @@ pub fn reload_stale_worker(ctx: &Ctx) {     }     let pending = ctx.sessions.read_files().iter().filter(|f| f["transport"].as_str() == Some("ext")).filter_map(|f| f["sessionId"].as_str().map(|x| x.to_string())).any(|id| !ctx.sessions.exists(&id));     if pending { return; }+    // 2.0.695: and never while a window the browser holds has no session yet. adopt_stray_windows gives it one+    // (and a file) within 5 s; cycling first closed such windows for good.+    if STRAYS_WAITING.load(std::sync::atomic::Ordering::Relaxed) != 0 { return; }     let shim = crate::lane_shim(ctx);     let staged = pup_browser::stage_extensions_lane(&*ctx.host, ctx.port, "adom-you", true, Some(&lane), shim);     if staged.is_empty() { pup_core::log("[extdrive] worker reload skipped: re-staging the extension produced nothing".to_string()); return; }@@ -623,6 +626,10 @@ pub fn rebind_unbound(ctx: &Ctx) -> usize { /// dragged out of (pup still lists that tab there), else the thread whose tab opened it. rebind_unbound then /// binds its OS window and stamps the thread's tile, exactly like a window pup opened itself. A window must /// stay unowned for 5 s first, so a window pup is opening right now is never taken.+/// How many unowned windows adopt_stray_windows saw but has not adopted yet (they are inside their 5 s wait).+/// reload_stale_worker never cycles the browser while this is above zero: a window with no session has no file,+/// so the cycle would close it for good (John's laptop, 2.0.694 install: an "AI Flow" page and a YouTube video).+pub static STRAYS_WAITING: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(usize::MAX);   // MAX = not scanned yet pub fn adopt_stray_windows(ctx: &Ctx) -> usize {     static SEEN: Mutex<Option<std::collections::HashMap<i64, u64>>> = Mutex::new(None);     if !alive(ctx) { return 0; }@@ -638,11 +645,12 @@ pub fn adopt_stray_windows(ctx: &Ctx) -> usize {     let seen = seen.get_or_insert_with(Default::default);     seen.retain(|w, _| wins.contains_key(w));     let mut n = 0;+    let mut waiting = 0;     for (wid, wtabs) in wins {         // about:blank-only windows are close_orphan_blank_windows' job         if wtabs.iter().all(|t| { let u = t["url"].as_str().unwrap_or(""); u.is_empty() || u == "about:blank" }) { continue; }         let first = *seen.entry(wid).or_insert(now);-        if now.saturating_sub(first) < 5000 { continue; }+        if now.saturating_sub(first) < 5000 { waiting += 1; continue; }         let ids: Vec<String> = wtabs.iter().filter_map(|t| t["tabId"].as_i64()).map(|i| i.to_string()).collect();         let openers: Vec<String> = wtabs.iter().filter_map(|t| t["openerTabId"].as_i64()).map(|i| i.to_string()).collect();         let from_drag = sessions.iter().find(|s| s.tabs.iter().any(|t| ids.contains(&t.target_id)));@@ -675,6 +683,7 @@ pub fn adopt_stray_windows(ctx: &Ctx) -> usize {         seen.remove(&wid);         n += 1;     }+    STRAYS_WAITING.store(waiting, std::sync::atomic::Ordering::Relaxed);     n }