From 8d3a43e05d1bc9f99ff48802934c8e9c6d4de4c8 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Fri, 3 Jul 2026 13:19:24 +0200 Subject: [PATCH] Stop new browser tabs from an agent stealing your focus (#1875) * fix(browser): stop new MCP browser tabs stealing focus browser_new_tab used openTabFocused, switching the workspace UI to the new tab and interrupting whatever the user was looking at. Automation commands don't need visual focus to work: webviews stay resident and paintable regardless, so open the tab in the background via openTabInBackground instead. * fix(browser): stop background MCP tabs hijacking Reload openBrowserTabForRequest still called setWorkspaceActiveBrowser for tabs opened in the background, so the desktop Reload/Force Reload menu (which targets getMostRecentWorkspaceActivePaseoBrowserWebContents) could reload an agent's hidden tab instead of whatever the user was looking at. Automation commands never needed this: every tab-scoped MCP tool takes an explicit browserId, and the existing useSyncWorkspaceActiveBrowser hook already keeps "active browser" in sync with real UI focus. Drop the redundant call. --- packages/app/src/browser-automation/handler.test.ts | 8 +++----- packages/app/src/browser-automation/handler.ts | 3 +-- packages/server/src/server/browser-tools/tools.ts | 2 +- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/app/src/browser-automation/handler.test.ts b/packages/app/src/browser-automation/handler.test.ts index 5b49b5f45..45ca127c8 100644 --- a/packages/app/src/browser-automation/handler.test.ts +++ b/packages/app/src/browser-automation/handler.test.ts @@ -250,7 +250,7 @@ describe("mountBrowserAutomationHandler", () => { useWorkspaceLayoutStore.setState({ layoutByWorkspace: {} }); }); - test("browser_new_tab creates and focuses a workspace browser tab", async () => { + test("browser_new_tab creates a workspace browser tab without stealing focus", async () => { const browser = new BrowserAutomationHandlerHarness(); const workspaceKey = buildWorkspaceTabPersistenceKey({ serverId: "server-1", @@ -278,16 +278,14 @@ describe("mountBrowserAutomationHandler", () => { expect(layout?.root).toEqual( expect.objectContaining({ kind: "pane", - pane: expect.objectContaining({ focusedTabId: openedTabs[0]?.tabId }), + pane: expect.objectContaining({ focusedTabId: previousFocusedTabId }), }), ); expect(openedTabs[0]?.tabId).not.toBe(previousFocusedTabId); expect(browser.browser.registeredWorkspaceBrowsers).toEqual([ { browserId: result.browserId, workspaceId: "wks_workspace_a" }, ]); - expect(browser.browser.activeWorkspaceBrowsers).toEqual([ - { browserId: result.browserId, workspaceId: "wks_workspace_a" }, - ]); + expect(browser.browser.activeWorkspaceBrowsers).toEqual([]); expect(browser.resident.ensuredWebviews).toEqual([ { browserId: result.browserId, url: "https://example.com" }, ]); diff --git a/packages/app/src/browser-automation/handler.ts b/packages/app/src/browser-automation/handler.ts index 35b3f5599..5647148a7 100644 --- a/packages/app/src/browser-automation/handler.ts +++ b/packages/app/src/browser-automation/handler.ts @@ -179,13 +179,12 @@ async function openBrowserTabForRequest(params: { message: "Cannot create a browser tab without a workspace context.", }); } - useWorkspaceLayoutStore.getState().openTabFocused(workspaceKey, { + useWorkspaceLayoutStore.getState().openTabInBackground(workspaceKey, { kind: "browser", browserId, }); await browserHost?.registerWorkspaceBrowser?.({ browserId, workspaceId }); - await browserHost?.setWorkspaceActiveBrowser?.({ browserId, workspaceId }); if (browserHost?.executeAutomationCommand) { ensureResidentBrowserWebview({ browserId, url: normalizedUrl }); diff --git a/packages/server/src/server/browser-tools/tools.ts b/packages/server/src/server/browser-tools/tools.ts index 981bbdc46..d7b0e0f36 100644 --- a/packages/server/src/server/browser-tools/tools.ts +++ b/packages/server/src/server/browser-tools/tools.ts @@ -115,7 +115,7 @@ export function registerBrowserTools(options: RegisterBrowserToolsOptions): void { title: "Create browser tab", description: - "Create and focus a new Paseo desktop browser tab in this agent's workspace. Pass an http(s) URL or a scheme-less host URL, which is treated as http; the returned browserId is used by tab-scoped tools.", + "Create a new Paseo desktop browser tab in this agent's workspace, opened in the background without switching the user's view. Pass an http(s) URL or a scheme-less host URL, which is treated as http; the returned browserId is used by tab-scoped tools.", inputSchema: { url: BrowserHttpUrlInputSchema.optional(), },