Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions skills/autobrowse/scripts/evaluate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,27 @@ function executeCommand(command) {
}
}

// evaluate.mjs leaves the browser session running by default so a caller can
// attach (e.g. browser-trace bisect in the browse.sh pipeline). But when WE
// created the session — no caller-managed session attached via BROWSE_SESSION
// or --session — nothing else will clean it up, and the inner agent only
// *might* run `browse stop` (LLMs routinely skip the trailing cleanup step).
// So tear down the daemon ourselves: killing it drops the CDP connection, and
// our own `browse open` runs without --keep-alive, so the remote session ends.
function ownsSession() {
return !process.env.BROWSE_SESSION && !getArg("session");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e473b74. Configure here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e473b74. Configure here.


function teardownOwnedSession() {
if (!ownsSession()) return;
try {
execFileSync("browse", ["stop"], { timeout: 15_000, stdio: "ignore" });
console.error("Stopped browser session (evaluate.mjs owned it).");
} catch {
// best-effort — never fail the run over cleanup
}
}

function buildSystemPrompt(strategy, traceDir, browseEnv) {
const openFlag = browseEnv === "remote" ? "--remote" : "--local";
const envDesc = browseEnv === "remote"
Expand Down Expand Up @@ -610,10 +631,12 @@ async function main() {
tokens_out: totalOutputTokens,
trace_dir: traceDir,
};
teardownOwnedSession();
console.log(JSON.stringify(result));
}

main().catch((err) => {
console.error("Fatal error:", err);
teardownOwnedSession();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e473b74. Configure here.

process.exit(1);
});