Gate liveness reaping on heartbeat capability

Only reap browsers that have sent at least one `system.heartbeat`.
A pre-heartbeat extension never opts in, so it is never dropped on
silence and keeps relying on socket-close detection exactly as before.
This removes a backward-compat regression where a new daemon paired
with an old extension would wrongly reap a live-but-idle connection
and then report `no_browser_connected` on the next command.

- Add a `heartbeat_seen` flag to `BrowserClient`, set when a heartbeat
  event arrives, and exclude never-heartbeated clients from
  `stale_browsers`.
- Cover both the opt-in and the legacy-survivor cases in unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
drakezhang
2026-07-22 13:06:17 +08:00
co-authored by Cursor
parent ed92e54dff
commit 31d2a1b3dc
4 changed files with 55 additions and 9 deletions
@@ -52,6 +52,7 @@ fn fake_client(id: &str, label: &str, connected_at_ms: i64) -> Arc<BrowserClient
connected_at_ms,
version_skew: false,
last_seen: Mutex::new(std::time::Instant::now()),
heartbeat_seen: std::sync::atomic::AtomicBool::new(false),
})
}
+1
View File
@@ -243,6 +243,7 @@ async fn status_surfaces_version_skew_for_skewed_browser() {
connected_at_ms: 0,
version_skew: true,
last_seen: Mutex::new(std::time::Instant::now()),
heartbeat_seen: std::sync::atomic::AtomicBool::new(false),
});
state.browsers.insert(client);