Skip to content

fix(reliability): persist longer under sustained agent work - #9538

Open
Sphinx-8 wants to merge 1 commit into
jo-inc:masterfrom
Sphinx-8:fix/reliability-long-run
Open

fix(reliability): persist longer under sustained agent work#9538
Sphinx-8 wants to merge 1 commit into
jo-inc:masterfrom
Sphinx-8:fix/reliability-long-run

Conversation

@Sphinx-8

Copy link
Copy Markdown

Three failure modes observed after ~18h of continuous single-user agent use (DGX Spark, systemd user service, one persistent tab):

1. 410 "browser_restarted" window too short

tabNotFoundResponse only answered 410 browser_restarted for 5 minutes after a browser restart. Any client that pauses longer than that (agents routinely do — or waits out a slow relaunch) hits a plain 404 on its dead tab ID and treats the tab as permanently gone instead of recreating it.

Widened the window to 30 minutes and named it BROWSER_RESTARTED_WINDOW_MS.

2. Memory-pressure restart was idle-only

The 30s interval bailed out when any session was open, so a Camoufox process tree leaking past BROWSER_RSS_RESTART_THRESHOLD_MB (default 1500MB) was never restarted while a session lived — the exact "long work" scenario — and could OOM the host first.

Now:

  • browser-tree RSS check runs in both idle and active phases;
  • in the active phase, sessions are closed first, so in-flight/follow-up requests get a clean 503 session_expired (retryable: true) and the client recreates its session;
  • native-growth restart stays idle-only — its baseline is only meaningful within one launch cycle and is reset in closeBrowserFully.

3. Crashed tabs waited out the full idle timeout

A page whose content process crashed (page.crashtabState.crashed) kept holding its registered slot for the full TAB_INACTIVITY_MS (5 min). The reaper now reaps crashed tabs after a short quiet period — min(60s, TAB_INACTIVITY_MS) — long enough for the in-flight call that surfaced the crash to finish and retry on a new tab.

Tests

  • tests/unit/browserRestartWindow.test.js (new) — 410 window boundaries + crashed-reaper decision logic (pure, no browser).
  • tests/unit/browserRestart410.test.js (new) — real server + Camoufox: create tab → POST /stop → dead tab ID answers 410 browser_restarted, garbage ID answers 404.
  • tests/unit/memoryPressure.test.js — mirrored decision function updated for active-phase semantics; 3 expectation changes.
  • .gitignore — ignore .worktrees/ (local delegation worktrees were being double-discovered by jest).

Full local run: unit 1621 passed, e2e 74 passed, plugins 43 passed.

No route changes, so openapi.json is untouched (openapi coverage test passes).

Three failure modes surfaced after ~18h of continuous use:

1. 410 window too short — tabNotFoundResponse only answered 410
   browser_restarted for 5 minutes after a browser restart. An agent
   that pauses longer than that (or waits out a slow relaunch) hits a
   plain 404 on its dead tab and treats the tab as permanently gone.
   Widen the window to 30 minutes and name the constant.

2. Memory-pressure restart was idle-only — the 30s interval bailed out
   when any session was open, so a browser tree leaking past
   BROWSER_RSS_RESTART_THRESHOLD_MB was never restarted until the
   session went idle, risking OOM on long work. Now the browser-tree
   RSS check runs in both phases; in the active phase sessions are
   closed first so clients get a clean 503 session_expired and
   recreate. Native-growth restart stays idle-only (its baseline is
   only meaningful within one launch cycle).

3. Crashed tabs waited out the full idle timeout — a page whose
   content process crashed held its slot for the full
   TAB_INACTIVITY_MS. Reap crashed tabs after a short quiet period
   (min(60s, TAB_INACTIVITY_MS)), long enough for the in-flight call
   that surfaced the crash to finish.

Tests: browserRestartWindow.test.js (window + crashed-reaper logic),
browserRestart410.test.js (real server: stop browser, dead tab -> 410
browser_restarted, garbage ID -> 404), memoryPressure.test.js updated
for the active-phase semantics.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant