mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
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:
@@ -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" },
|
||||
]);
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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(),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user