Add heartbeat event and daemon browser-liveness reaper

Introduce a `system.heartbeat` protocol event and use it as a
liveness signal on the daemon side.

- Track `last_seen` per connected browser, bumped on every inbound
  WS frame (tool response, event, ping/pong, or heartbeat).
- Add a reaper task that drops any browser silent past a 60s window
  and purges its sessions, mirroring the WS disconnect cleanup. This
  releases half-open connections left behind when the OS resumes from
  sleep and kills the service worker without delivering a close frame,
  so the daemon no longer pins a phantom "connected" browser and can
  idle-exit normally.
- Wire the reaper into both the foreground daemon and the test/embed
  entry point, aborting it on shutdown.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
drakezhang
2026-07-22 12:16:56 +08:00
co-authored by Cursor
parent ccf3c32140
commit 5e91178e03
8 changed files with 161 additions and 2 deletions
+2
View File
@@ -45,6 +45,7 @@ pub async fn run(
None => None,
};
let session_idle_task = start::spawn_session_idle_reaper(Arc::clone(&state));
let browser_liveness_task = start::spawn_browser_liveness_reaper(Arc::clone(&state));
// Ensure WS/IPC accept loops have polled `shutdown.notified()` before any
// caller can invoke `DaemonHandle::shutdown()` — `Notify::notify_waiters()`
// drops wakeups when nothing is registered yet, which otherwise hangs
@@ -55,5 +56,6 @@ pub async fn run(
ws_handle,
ipc_handle,
session_idle_task,
browser_liveness_task,
))
}