Skip to content

fix: own a labelled browser tab so Chrome's Gemini panel (or an operator tab) can't take it (0.3.2) - #3

Merged
mabry1985 merged 2 commits into
mainfrom
fix/gemini-panel-steals-tab
Sep 13, 2026
Merged

mabry1985 merged 2 commits into
mainfrom
fix/gemini-panel-steals-tab

Conversation

@mabry1985

@mabry1985 mabry1985 commented Sep 13, 2026

Copy link
Copy Markdown
Member

Chrome's Gemini panel steals the browser tab → ERR_BLOCKED_BY_CLIENT

Live 2026-09-13 on merchantAgent: one Kill Team item priced, then every navigation failed with net::ERR_BLOCKED_BY_CLIENT. Chrome 149's built-in Gemini side panel opened by itself as a webview target; agent-browser 0.27.1 makes any newly discovered target the ACTIVE tab and runs every command on the active tab, so the plugin navigated the panel. (Upstream fixed tab stealing in 0.34.0; the core pin is 0.27.1.)

Design (after review round 1)

  • The plugin owns one labelled tab (ebaytab). The launch step is tab ebaytab / tab new --label ebaytab with the launch flags — no navigation of any other tab. (The old open about:blank launch blanked whatever tab was active, which could be the operator's.)
  • Every navigation switches to that tab first — one CLI call; flags attached so a dead daemon relaunches with our profile.
  • Only Chrome's panels are ever closed (type == "webview" or the Gemini URL); ordinary pages never, whatever their URL.
  • ERR_BLOCKED_BY_CLIENT → close panels, reclaim the tab, retry once.
  • One read path (_read_page: navigate → wait → read → settle). A read whose host is not the requested site reclaims the tab and re-reads through the same settle loop, once. Sold/active search, Amazon, session status (never a false "signed out") and the page probe all use it.

Verification

  • Live, throwaway headless session + temp profile: tab new --label ebaytab --profile P --args … launched Chrome on P with the stealth arg; repeat reused the same Chrome; unknown label → exit 1 "No tab with label"; duplicate label refused; open landed in the labelled tab.
  • Live, before the fix: three navigations blocked while tab list showed t2 webview ACTIVE gemini.google.com/glic; closing it restored navigation.
  • 156 host-free tests (ordering assertions for switch-before-navigate and reclaim-between-retries; pages never closed; settle after reclaim; session status/probe/Amazon reclaim; host rules).

🤖 Generated with Claude Code

https://claude.ai/code/session_011UQ33xXfygwEkzbbJx3YwZ

mabry1985 and others added 2 commits September 13, 2026 01:39
Seen live 2026-09-13 on merchantAgent: one Kill Team item priced, then every navigation
failed with `net::ERR_BLOCKED_BY_CLIENT`. Chrome 149's built-in Gemini side panel
(gemini.google.com/glic) opened on its own in the headed window as a `webview` target, and
the pinned agent-browser (0.27.1) makes a newly discovered target the ACTIVE tab — so
`open` navigated the panel, which Chrome refuses. (Upstream 0.34.0 fixed "event-discovered
targets steal the active tab"; the core pin is still 0.27.1.)

- `Browser.ensure_page_tab()`: one `tab list`; when the active tab is not an ordinary page
  (a `page` target on http(s)/about:blank that is not the Gemini panel), close the non-page
  targets, reselect the first web page, or open a new tab. Runs before every navigation.
- A navigation that still fails with ERR_BLOCKED_BY_CLIENT is repaired and retried once;
  with nothing to repair it raises as before.
- A read whose `location.href` is the panel (or chrome:// / devtools://) is treated as
  decided — the settle loop no longer spins ~10 s on it — and `_fetch` repairs the tab,
  re-navigates and re-reads once instead of reporting "couldn't find the results list".
- Skill: ERR_BLOCKED_BY_CLIENT explained as a browser-tab problem, not an eBay one.
- 142 host-free tests (was 135); 0.3.2.

Not done: disabling the panel at launch (`--disable-features=Glic`) — the feature name for
Chrome 149 could not be verified, and `--args` splits on commas, which would clobber the
base `--disable-features=Translate`. The repair covers it whatever opens the panel.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011UQ33xXfygwEkzbbJx3YwZ
… any foreign read

Review round 1 on this PR (FIX FIRST): the first cut closed every tab that was not
http(s) — the operator's file://, data:, view-source: tabs too — and reselected
whichever web tab agent-browser listed first, which could be the operator's Gmail;
its re-read after a repair skipped the settle loop (the false "markup changed"
error the redirect fix exists to prevent); session status could report
signed_in:false when another tab answered.

- The plugin owns ONE labelled tab (`ebaytab`). The launch step is now
  `tab ebaytab` / `tab new --label ebaytab` WITH the launch flags — verified live on
  a throwaway session: it launches Chrome on the requested profile with the stealth
  argument, is reused on repeat, and `open` lands in the labelled tab. The old
  `open about:blank` launch blanked whatever tab was active (could be the operator's).
- Every navigation switches to that tab first (one CLI call, flags attached, so a dead
  daemon relaunches with OUR options instead of a default headless browser).
- Only Chrome's panels are ever closed: `type == "webview"` or the Gemini URL. An
  ordinary page is never closed, whatever its URL.
- ERR_BLOCKED_BY_CLIENT on a navigation → close panels, reclaim our tab, retry once.
- tools: ONE read path (`_read_page`: navigate → wait → read → settle). A read whose
  host is not the requested site (panel, about:blank, an operator tab) reclaims the
  tab, re-navigates and reads again through the SAME settle loop — once. Used by sold
  and active searches, Amazon, session status (never a false "signed out": it says it
  could not check) and the page probe (reports landed_url).
- 156 host-free tests (was 142): ordering asserted (switch immediately before each
  navigation; panel closed and tab reclaimed BETWEEN the two navigations), pages
  never closed, labelled-tab creation without navigating another tab, flags on tab
  commands, settle after reclaim, session status / probe / Amazon reclaim, host rules.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011UQ33xXfygwEkzbbJx3YwZ
@mabry1985 mabry1985 changed the title fix: repair the browser tab when Chrome's Gemini panel steals it (0.3.2) fix: own a labelled browser tab so Chrome's Gemini panel (or an operator tab) can't take it (0.3.2) Sep 13, 2026
@mabry1985

Copy link
Copy Markdown
Member Author

Gate for e011018: CI test pass; Vera's panel returned PASS (check-run: "PASS verdict — post refused" — GitHub refused the review post, so the red protoReview check is a posting failure, not a finding); in-session adversarial review round 2: MERGE, no blockers. Non-blocking follow-ups are on branch fix/own-tab-followups (0.3.3) and will be their own PR on main.

@mabry1985
mabry1985 merged commit 1ebb61c into main Sep 13, 2026
1 of 2 checks passed
@mabry1985
mabry1985 deleted the fix/gemini-panel-steals-tab branch September 13, 2026 09:09
mabry1985 added a commit that referenced this pull request Sep 13, 2026
…h (0.3.3)

Review round 2 on #3 (MERGE, no blockers) confirmed two gaps with stubs; closed here.

- Wedged own tab: if the labelled tab exists but will not take focus (hung/crashed),
  `tab new --label` is refused "already used" and every navigation failed until the
  operator closed it by hand. Now: close OUR labelled tab (the only one carrying the
  label) and create a fresh one ("recreated").
- Wrong-search reads: the host check let an operator's own eBay search (a cmd-click in
  the shared window) answer ours — `ok: true` with prices for the operator's query.
  For a search request, the landed page must be the same host and, when it states
  search terms (`_nkw` on eBay /sch/, `k` on Amazon /s), the SAME terms (normalized:
  case, +/%20, spacing, parameter order). An item/product page answering a search is a
  hijack. Sign-in and challenge hops on subdomains, and search pages that state no
  terms (eBay rewrites), are not judged here.
- The module docstring now states the residual sub-second race honestly (0.27.1's
  `open` cannot name a tab; the read-side check catches the result).
- Tests: wedged-tab recovery closes only the labelled tab; reclaim through a blocked
  navigation with an operator Gmail and an operator eBay search in the list closes only
  the panel (locks in round 1's fix via the real path); 10 same-search cases.
  168 host-free tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011UQ33xXfygwEkzbbJx3YwZ
mabry1985 added a commit that referenced this pull request Sep 13, 2026
…h (0.3.3) (#4)

* fix: recover a wedged plugin tab; a search read must be our own search (0.3.3)

Review round 2 on #3 (MERGE, no blockers) confirmed two gaps with stubs; closed here.

- Wedged own tab: if the labelled tab exists but will not take focus (hung/crashed),
  `tab new --label` is refused "already used" and every navigation failed until the
  operator closed it by hand. Now: close OUR labelled tab (the only one carrying the
  label) and create a fresh one ("recreated").
- Wrong-search reads: the host check let an operator's own eBay search (a cmd-click in
  the shared window) answer ours — `ok: true` with prices for the operator's query.
  For a search request, the landed page must be the same host and, when it states
  search terms (`_nkw` on eBay /sch/, `k` on Amazon /s), the SAME terms (normalized:
  case, +/%20, spacing, parameter order). An item/product page answering a search is a
  hijack. Sign-in and challenge hops on subdomains, and search pages that state no
  terms (eBay rewrites), are not judged here.
- The module docstring now states the residual sub-second race honestly (0.27.1's
  `open` cannot name a tab; the read-side check catches the result).
- Tests: wedged-tab recovery closes only the labelled tab; reclaim through a blocked
  navigation with an operator Gmail and an operator eBay search in the list closes only
  the panel (locks in round 1's fix via the real path); 10 same-search cases.
  168 host-free tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011UQ33xXfygwEkzbbJx3YwZ

* fix: only a different site counts as settled; same-search check survives `domain: ebay.com`

Review of #4 (MERGE, no blockers) — two confirmed follow-ups folded in:
- FP2: `_is_undecided` treated any hijack as settled, so a same-site page flagged by the
  same-search rule that was ALSO still loading skipped the settle loop and failed as
  "could not find the results list". Now only `_foreign_host` (a different site) is settled.
- FN1: the same-search check compared the raw hostname, so with `domain: ebay.com` (no www)
  eBay's redirect to www silently disabled it on every read. Compare `_site(host)`.
- Tests: www-less domain in both directions, a search landing on a sign-in subdomain, and the
  settle behaviour (the reviewer's surviving mutant M1 now fails). 172 tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011UQ33xXfygwEkzbbJx3YwZ

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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