Ready: fix(tabs): stop event-discovered tabs from stealing the active tab#1426
Open
soichisumi wants to merge 1 commit into
Open
Ready: fix(tabs): stop event-discovered tabs from stealing the active tab#1426soichisumi wants to merge 1 commit into
soichisumi wants to merge 1 commit into
Conversation
When the daemon is attached to a shared Chrome (e.g. over `--cdp`), every command first drains pending CDP target events. A `Target.targetCreated` for a tab the human (or another client) opens is registered via `add_page()`, which unconditionally sets it active — so an externally opened tab silently steals the agent's active tab and the next command targets the wrong page. Split the activation decision out of `add_page()`: - `add_page()` keeps activating (explicit user commands, recording setup). - `add_page_without_activation()` appends without moving the active pointer. - a pure `active_page_index_after_add()` helper makes the rule unit-testable (mirrors the existing `active_page_index_after_removal` helper). The event-drain path now uses `add_page_without_activation()`, so discovered tabs still appear in `tab list` but never steal active. Explicit new-tab paths (`tab new`, `window new`, `click --new-tab`) are unaffected — they activate via their own code paths. The first/only page still activates, so a non-empty manager always has a valid active index. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
|
@soichisumi is attempting to deploy a commit to the Vercel Labs Team on Vercel. A member of the Team first needs to authorize it. |
Author
This was referenced Jun 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Target.targetCreated) tabs — e.g. a tab the human opens in a shared Chrome — from silently stealing the daemon's active tab.Why
When the daemon is attached to a shared Chrome (e.g. over
--cdp, or whenever a human / another client also drives the browser), every command first drains pending CDP target events. ATarget.targetCreatedfor a tab someone else opens is registered viaadd_page(), which doesself.active_page_index = indexunconditionally — so a tab the human opens silently becomes the daemon's active tab, and the agent's next command (snapshot / click / fill) targets the wrong page. In a shared-Chrome / human-in-the-loop setup this is the dominant "my command went to the wrong tab" failure.What
add_page():add_page()keeps activating (explicit user commands, recording setup).add_page_without_activation()appends without moving the active pointer.active_page_index_after_add()helper encodes the rule.apply_drained_events, onTarget.targetCreated) now usesadd_page_without_activation().How
active_page_index_after_add(active, new_index, was_empty, activate)returns the new index only when the caller explicitly activates or the manager was empty (first/only page) — mirroring the existingactive_page_index_after_removalhelper so the rule is unit-testable without a liveBrowserManager.Discovered tabs still appear in
tab list, but never steal active. Explicit new-tab paths are unaffected —tab new,window new, andclick --new-tabactivate via their own code paths (they do not go through the event drain).Behavior change to note: a popup opened by page JS (
window.open/target="_blank"without--new-tab) also arrives via the discovery path, so it is now append-only too — interact with it viatab switch. A future refinement could special-case popups whoseopenerIdis the active target (CDPTarget.targetCreatedcarriesopenerId), but that is not modeled today, and re-introducing "a new target always steals active" is exactly the bug this fixes.Test plan
cargo test --bin agent-browser—active_page_index_after_addtests pass: first-page activates; discovered tab is append-only; explicit activation moves active.apply_drained_events->add_page_without_activation) and confirmed explicit new-tab paths still activate via their own code paths.