Repository navigation
fix(desktop): new tabs the agent opens from links no longer steal composer focus - #29023
juliusmarminge wants to merge 8 commits into
Conversation
A target=_blank link clicked by the agent's CDP input reported a foreground-tab disposition, so the web opened it as a user action and moved focus off the composer. The window-open handler now also treats a link as background when the guest had agent input in the last 5 seconds, reusing the signal downloads already used (renamed agentActedRecently). Fixes #29009 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… foreground Agent input marks a guest for 5 s, so a person who clicked into that guest and opened a _blank link inside the window got a background tab. A person's click focuses the guest while the agent's leaves focus with the composer, so background the tab only when the guest does not hold keyboard focus. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ocused frame Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — Agent-opened preview tabs stay in the background when the guest lacks focus, preventing interruption of composer input. The change is confined to focus handling and a corresponding download-predicate refactor, with no obvious material risk. No code changes detected at You can add or adjust custom eligibility rules. Learn more. |
Handing issue #29009 to this PR; I stopped further testing and will not push the separate local patch. Independently reproduced the original bug on Linux/corex with the desktop build of The separate local patch at |
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
|
Screenshots from the Linux desktop dev app runs in the PR body (Xvfb, Codex agent, local
|
|
Taking over this one |
|
Taking over this one |

Problem
When the agent clicks a
target="_blank"link in a desktop-hosted Browser tab, the new tab opens in the foreground and keyboard focus leaves the composer. The desktop sent such links withbackgroundonly forbackground-tabdispositions, so a plain left click from the agent looked like a user action to the web client, which then moved focus into the right panel. It also happens when the agent works in another thread: the right panel of the thread on screen takes focus.Reproduced on main (05c0ed7) with the Linux desktop dev app: a Codex agent opened a local page and clicked its
_blanklink withpreview_act clickwhile the composer had focus.Change
The guest's window-open handler now opens a new-tab link in the background when the agent sent input to that guest in the last 5 seconds and no frame in the guest has focus. This reuses the agent-input timestamp the desktop already keeps for downloads, renamed from
humanStartedDownloadtoagentActedRecently. The download decision is unchanged.The focused-frame check keeps a person's click in the foreground, even right after agent input. A person's click focuses a frame in the guest. The agent's click does not: the host gets focus back after the agent's press, and guests of other threads are hidden. The web client already handles
background: truewithout a user action, so nothing changes there.Scope and approval
Fixes #29009
Verification
vp test run apps/desktop/src/preview/Manager.test.ts apps/desktop/src/preview/DesktopBrowserHost.test.ts: 93 passed.PreviewManager > opens a new-tab link the agent clicked in the backgroundcovers three cases: a person's click opens in the foreground, an agent click opens in the background, and a person's click right after agent input opens in the foreground. On main, the agent-click case fails (background: false, expectedtrue).vp run check:changed: format, lint, knip, typecheck and tests pass.Desktop app run (Linux,
vp run dev:desktopunder Xvfb, fresh database, Codex agent drivingpreview_openandpreview_act clickon a local page with a_blanklink). A CDP monitor logged the app's focused element. Before is main 05c0ed7; after is ae2972e._blanklink, same threadSame-thread agent click, before:
After: the press briefly focuses the webview, focus comes back to the composer, and nothing moves when the tab opens.
Cross-thread click with B's Files panel open, before. The agent clicks in A at 02:21:54.548:
After. The agent clicks in A at 03:07:36.129, and B's focus does not change:
Not checked:
webContents.isFocused()is false for every guest, which is why the check usesfocusedFrame.🤖 Generated with Claude Code (Claude Opus 5.5, issue lead; implementation and red test by Claude Opus 5.5 step agents; reviews by GPT-6.1 Sol via Codex and Claude Opus 5.5)
Takeover
Taken over from @juliusmarminge for evidence. No code changes; main merged in by merge commit (fdd40f40bd), evidence recaptured at that head and verification added below.
Evidence
Beyond the title: a person's click sent through a second web client's streamed Browser tab counts as agent input, so at this head the new tab opens in the background on the desktop host (row R1; on main it opens in the foreground).
Real Electron 44.10.0 desktop app under Linux Xvfb (
--no-sandbox --disable-gpu), 1440×1000, DPR 1, 100% font scale, dark theme, renderer clock frozen, freshT3CODE_DEV_SEED=0databases with synthetic data, drafts reset per row. Agent clicks go through the thread-scoped MCPpreview_act; person clicks and typing are real X11 input. R1's second client is Chromium at 1440×1000, DPR 2. 390-wide and light captures were skipped: the change renders nothing new. Each recording has a sidebar with timestamps and the focused element. Diff0: int32 |after − before| × 255. DiffT: channels differing by more than 16/255, in magenta. Both compare the settled final frames._blanklink, same thread, Browser visible. Before: the new tab is selected and the typing lands in its URL field. After: the source tab stays selected and the composer gets the typing.A1 final frames, before (cefb544af3) and after (fdd40f40bd):
What else changed: A1/S1 selected tab, URL-field focus and composer text; A2 Terminal versus Files; S2 chat width and the thread-details card from the panel opening; S4 tab-strip widths and the selected tab; R1 desktop host tab selection and focus (viewer final frames identical at zero threshold). H1 and T1: nine address-bar icon pixels differ, none above 16/255. B1: identical at zero threshold.
Full per-row steps, timestamps, destination-inspection clips and sheets: Evidence.md · index · matrix. Rebuild: REPRODUCE · seed-reproduction.js · capture.mjs · all scripts.
Verification log
vp run check:changedpassed (format, lint, knip, typecheck, 93 tests inManager.test.tsandDesktopBrowserHost.test.ts). Earlier rounds' tests and reviews stand.cannot-verify macos,cannot-verify windows: window activation andfocusedFrameon native macOS/Windows.🤖 Generated with Claude Code