autobrowse: tear down self-owned browser session on exit#123
Conversation
evaluate.mjs left the browser session running on completion, relying on the inner agent to voluntarily `browse stop` — which LLMs routinely skip after emitting their final answer, leaking a remote Browserbase session until idle timeout. It now runs `browse stop` on both the success and fatal-error paths, but ONLY when it owns the session (no caller-managed session attached via BROWSE_SESSION or --session). This preserves the browse.sh pipeline, which keeps the session alive after evaluate.mjs so browser-trace can bisect, then releases its own keep-alive session explicitly. Verified: browse stop flips browserConnected true→false; our `browse open` runs without --keep-alive so the remote session ends on disconnect. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e473b74. Configure here.
| // our own `browse open` runs without --keep-alive, so the remote session ends. | ||
| function ownsSession() { | ||
| return !process.env.BROWSE_SESSION && !getArg("session"); | ||
| } |
There was a problem hiding this comment.
Session flag skips teardown only
Medium Severity
ownsSession() treats a --session CLI value like caller-managed attachment (same as BROWSE_SESSION), but the harness never copies that flag into process.env.BROWSE_SESSION. The inner agent’s browse subprocesses still use the default session, while exit skips teardownOwnedSession(), so a remote Browserbase session opened during the run can keep running and billing after evaluate.mjs exits.
Reviewed by Cursor Bugbot for commit e473b74. Configure here.
|
|
||
| main().catch((err) => { | ||
| console.error("Fatal error:", err); | ||
| teardownOwnedSession(); |
There was a problem hiding this comment.
Interrupt exits skip session teardown
Medium Severity
This change adds teardownOwnedSession() for normal completion and fatal promise rejections, but nothing registers it for SIGINT or SIGTERM. Interrupting a long run after the agent opened a remote browser exits without calling browse stop, so an owned Browserbase session can keep running when ownsSession() would have been true.
Reviewed by Cursor Bugbot for commit e473b74. Configure here.
| // our own `browse open` runs without --keep-alive, so the remote session ends. | ||
| function ownsSession() { | ||
| return !process.env.BROWSE_SESSION && !getArg("session"); | ||
| } |
There was a problem hiding this comment.
Equals-form session flag ignored
Medium Severity
ownsSession() treats a passed --session value as caller-managed, but it uses getArg("session"), which only recognizes --session as a separate argv token. Invocations like --session=foo leave ownsSession() true, so teardownOwnedSession() still runs browse stop and can tear down a session the caller meant to keep for follow-up work.
Reviewed by Cursor Bugbot for commit e473b74. Configure here.
|
Bugbot already flagged the interrupt path (no Here's one I think Bugbot missed, and it's the bigger one because it breaks the skill's normal usage: Auto-teardown kills the shared local browser out from under parallel runs.
The problem: the skill's primary multi-task mode (SKILL.md Step 3) spawns several sub-agents at once, and in local mode they all share one The PR's verification only exercised → Suggested fix: only auto-teardown in remote mode, where ownership is real: function ownsSession() {
return browseEnv === "remote" && !process.env.BROWSE_SESSION && !getArg("session");
}Or capture this run's specific session id at startup and only stop that id, rather than inferring ownership from missing flags. Either also resolves Bugbot's (One minor extra: |


Summary
evaluate.mjsleft the browser session running when the inner-agent loop ended — relying on the agent to voluntarilybrowse stop, which LLMs routinely skip after emitting their final answer. On--env remotethat leaks a Browserbase session until idle-timeout (cost + concurrency). Found while running a real signup task end-to-end.It now runs
browse stopon both the success and fatal-error paths, only when evaluate.mjs owns the session — i.e. no caller-managed session is attached viaBROWSE_SESSIONor--session.Why the guard matters
The browse.sh skill-factory pipeline intentionally keeps the session alive after evaluate.mjs so
browser-tracecan attach and bisect ("don't release before bisecting"), then releases its own--keep-alivesession explicitly. That path setsBROWSE_SESSION, soownsSession()returns false and we don't interfere. Only standalone/local runs (which create an ad-hoc non-keep-alive session) get auto-torn-down.Verification
ownsSession()logic: true when nothing attached; false whenBROWSE_SESSIONset or--sessionpassed.browse open --remote→browse statusshowsbrowserConnected: true→browse stop→browserConnected: false. Ourbrowse openruns without--keep-alive, so the remote session ends on disconnect.Independent of the inbox feature (#119) — branched off
main. Surfaced during that testing but reviewable on its own.🤖 Generated with Claude Code
Note
Low Risk
Best-effort cleanup with an explicit guard for caller-managed sessions; failures during stop are ignored and do not affect run outcome.
Overview
evaluate.mjsnow runsbrowse stopon both the normal completion path and the fatal-error handler when the harness owns the browser session (noBROWSE_SESSIONenv and no--sessionarg). That closes the gap where the inner agent often never runs cleanup, which especially leaked remote (Browserbase) sessions until idle timeout.When a caller attaches a managed session (e.g. skill-factory / browser-trace bisect), teardown is skipped so the session can stay alive for follow-up steps.
Reviewed by Cursor Bugbot for commit e473b74. Bugbot is set up for automated code reviews on this repo. Configure here.