Skip to content

fix(runtime): clean up sessions on disconnect#17

Open
NianJiuZst wants to merge 4 commits into
Tencent:mainfrom
NianJiuZst:codex/cleanup-sessions-on-disconnect
Open

fix(runtime): clean up sessions on disconnect#17
NianJiuZst wants to merge 4 commits into
Tencent:mainfrom
NianJiuZst:codex/cleanup-sessions-on-disconnect

Conversation

@NianJiuZst

@NianJiuZst NianJiuZst commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

What problem this solves

BrowserSkill keeps a session registry in two processes: the daemon tracks the CLI-facing session id and dispatch queue, while the extension owns the Agent Window, ref store, CDP attachments, and borrowed-tab return metadata.

Before this change, losing the extension WebSocket caused the daemon to purge its side immediately, but the extension only cleared its handshake state. The local SessionManager stayed alive. Disabling the connection from the popup had the same behavior. After reconnecting, the CLI no longer knew those session ids, so it could not stop their Agent Windows or return borrowed tabs. Closing an orphan Agent Window could then close a user tab that was still inside it.

There was a second race in the daemon's reconnect generation guard. If a new socket registered before the old socket's cleanup completed, the guard correctly preserved the new browser registration, but it also preserved sessions that the extension no longer had. That produced the inverse split: daemon rows with no matching Agent Window.

How this fixes it

This change adopts an explicit safety policy: transport loss terminates local browser sessions instead of attempting to preserve unverifiable in-memory state across the disconnect.

The extension now uses the same teardown path as a normal session stop:

  1. return every borrowed tab to its original or fallback user window;
  2. keep the Agent Window open if any return fails, avoiding user-tab loss;
  3. clear the session ref store;
  4. detach session-owned CDP targets;
  5. close the Agent Window and remove the local SessionContext.

Cleanup is invoked in two lifecycle paths:

  • before an intentional popup disconnect, so teardown completes before the socket closes;
  • after an unexpected transport loss, before reconnecting.

The disconnect cleanup is coalesced, so an explicit disconnect event and the transport state callback cannot run two overlapping teardown passes. For unexpected loss, the controller first cancels WSTransport's pending automatic reconnect, performs local cleanup, and only then reconnects.

On the daemon side, a fresh handshake for an existing browser instance now purges any stale sessions and their queues/interrupt markers before replacing the browser registration. The generation guard still protects the new browser connection when the old socket task exits, but stale sessions are no longer carried across generations.

User impact

After disabling BrowserSkill or losing the loopback connection, users no longer have hidden sessions that the CLI cannot manage. Borrowed tabs are returned before Agent Windows close. A reconnect starts from a clean state and the next automation task creates a fresh session id.

This intentionally trades transparent session continuation for a deterministic safety boundary. BrowserSkill session state is currently in-memory and cannot be proven consistent after a disconnect; preserving it was the source of the orphan behavior.

If Chrome refuses to return a borrowed tab, cleanup reports the failure and leaves that Agent Window open instead of risking data loss. The failure is logged for the user to resolve manually.

Validation

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets --locked -- -D warnings
  • cargo test --workspace --locked
    • Rust unit, integration, protocol, handshake, reconnect, and session tests passed
  • corepack pnpm --filter @browser-skill/extension test
    • 36 test files passed
    • 377 tests passed
  • corepack pnpm --filter @browser-skill/extension compile
  • node --test scripts/*.test.mjs
  • corepack pnpm exec stylelint "**/*.css"
  • git diff --check

New regression tests verify safe cleanup ordering, cleanup of multiple sessions, coalescing of overlapping disconnect notifications, intentional-disconnect ordering, unexpected-disconnect cleanup, and daemon-side stale-session removal during a same-instance reconnect.

CI Baseline Compatibility

This branch also carries the one-line Biome 2.4 formatter output for apps/extension/vitest.config.ts. The same formatting mismatch currently fails the Frontend job on upstream/main; this minimal compatibility commit is intentionally separate from the functional fix and lets this PR run the repository CI suite to completion.

CI Reliability Follow-up

GitHub Actions exposed a pre-existing test-only port allocation race: integration helpers probed an ephemeral port, released it, and then asked the daemon to bind the same number, allowing another process to claim it in between. The resulting Address already in use failure was unrelated to the functional changes. The PR now lets each daemon bind port 0 directly and reads the assigned address from its handle, removing the TOCTOU window across all affected integration helpers.

@NianJiuZst NianJiuZst marked this pull request as ready for review July 10, 2026 14:12
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