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.
This commit is contained in:
Mohamed Boudra
2026-07-03 13:19:24 +02:00
committed by GitHub
parent 7cb0ccba60
commit 8d3a43e05d
3 changed files with 5 additions and 8 deletions

View File

@@ -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" },
]);

View File

@@ -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 });

View File

@@ -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(),
},