Skip to content

Sessions sharing one Chrome over --cdp hijack each other's tabs (no persisted session-to-tab binding) #1530

Description

@dandaka

Summary

When N agents share one Chrome over --cdp <port> with unique --session names, sessions hijack each other's tabs (and the user's tabs). The session's "current tab" is an in-memory index inside the daemon; nothing maps a session to a CDP target across daemon restarts or tab removals, so the pointer silently re-resolves to some other tab.

Reproduction

Verified on a live system (v0.28.0; the relevant code is unchanged in 0.31.1). Start one Chrome with --remote-debugging-port=9222, then create 10 named sessions and open a distinct URL in each:

for i in $(seq 1 10); do
  agent-browser --session "agent$i" --cdp 9222 open "https://example.com/?page=$i"
done
for i in $(seq 1 10); do
  agent-browser --session "agent$i" eval "location.href"
done

Result: eval location.href returned the SAME url for all 10 sessions, and tab list from different sessions showed the same current-tab marker. Every session ends up driving whatever tab happens to be active.

Root cause

--session isolates the daemon process, not the tab. Two code paths re-resolve the active tab silently:

  1. Daemon (re)attach. discover_and_attach_targets in cli/src/native/browser.rs attaches to all page targets and sets active_page_index = 0. Target.getTargets returns most-recently-active first, so a fresh daemon (idle-timeout shutdown, crash, or the first command of a new session) adopts whatever tab is currently active, typically another agent's tab.
  2. Tab removal fallback. active_page_index_after_removal and the clamp logic in browser.rs silently shift the pointer to a neighboring tab when the current tab is closed by anyone else.

The consequences compound: open <url> navigates the wrong tab in place, and tab close with no ref closes the wrong tab. There is no persisted session to target mapping anywhere; daemon session state is only {session}.{pid,sock,stream,version} in the socket dir. Meanwhile PageInfo already stores target_id per tab, so the raw data exists. Only the binding, its persistence, and the failure semantics are missing.

Related issues

These look like the same defect surfacing in different code paths:

Proposed fix

  1. Persist a session to target binding ({session}.target next to the existing per-session files), written when a session creates a tab or explicitly switches, and re-selected by targetId on attach instead of index 0.
  2. Fail loudly instead of falling back: when the bound tab is gone, return a structured tab_gone error (non-zero exit, machine-readable in --json) while keeping tab list / tab new / tab switch working for recovery.
  3. Gate the strict semantics behind an opt-in --pin-tab flag (sticky per session) so existing scripts keep the legacy fallback; the re-attach-by-binding part is a pure bug fix and applies without the flag.
  4. Expose targetId in the CLI surface (tab list --json, accept target ids as tab refs), which subsumes Feature request: agent-browser tab list --json – include CDP targetId and support tab operations by targetId #1265.

I have an implementation of this ready and will open a PR referencing this issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions