refactor(browser-tools): require explicit tab targets

Remove hidden active-browser targeting so automation requests carry workspace in the envelope and browserId in tab command args.
This commit is contained in:
Mohamed Boudra
2026-07-02 19:53:40 +02:00
parent d9615c1e98
commit 71ce96b60f
22 changed files with 1108 additions and 4227 deletions
@@ -73,7 +73,7 @@ function browserNewTabRequest(): BrowserAutomationExecuteRequest {
workspaceId: "wks_workspace_a",
command: {
command: "new_tab",
args: { workspaceId: "wks_workspace_a", url: "https://example.com" },
args: { url: "https://example.com" },
},
};
}
@@ -128,7 +128,6 @@ describe("mountBrowserAutomationHandler", () => {
test("creates a focused workspace browser tab for browser_new_tab", async () => {
const client = new FakeDaemonClient();
const setWorkspaceActiveBrowser = vi.fn(async () => undefined);
const setAgentActiveBrowser = vi.fn(async () => undefined);
const registerWorkspaceBrowser = vi.fn(async () => undefined);
const ensureResidentBrowserWebview = vi.fn();
const executeAutomationCommand = vi.fn(async () => currentListTabsPayload());
@@ -150,7 +149,6 @@ describe("mountBrowserAutomationHandler", () => {
executeAutomationCommand,
registerWorkspaceBrowser,
setWorkspaceActiveBrowser,
setAgentActiveBrowser,
},
}) satisfies DesktopHostBridge,
ensureResidentBrowserWebview,
@@ -180,10 +178,6 @@ describe("mountBrowserAutomationHandler", () => {
browserId,
workspaceId: "wks_workspace_a",
});
expect(setAgentActiveBrowser).toHaveBeenCalledWith({
agentId: "agent-1",
browserId,
});
expect(setWorkspaceActiveBrowser).toHaveBeenCalledWith({
browserId,
workspaceId: "wks_workspace_a",
+4 -31
View File
@@ -111,8 +111,6 @@ async function handleBrowserAutomationRequest(params: {
return;
}
await rememberAgentBrowserTarget({ request, browserHost });
if (!executeAutomationCommand) {
client.sendBrowserAutomationExecuteResponse({
type: "browser.automation.execute.response",
@@ -159,11 +157,11 @@ async function openBrowserTabForRequest(params: {
BrowserAutomationExecuteRequest["command"],
{ command: "new_tab" }
>;
const workspaceId = request.workspaceId ?? command.args.workspaceId;
const workspaceId = request.workspaceId;
if (!serverId || !workspaceId) {
return browserAutomationFailure({
requestId: request.requestId,
code: "browser_no_tab",
code: "browser_unsupported",
message: "Cannot create a browser tab without a workspace context.",
});
}
@@ -174,7 +172,7 @@ async function openBrowserTabForRequest(params: {
if (!workspaceKey) {
return browserAutomationFailure({
requestId: request.requestId,
code: "browser_no_tab",
code: "browser_unsupported",
message: "Cannot create a browser tab without a workspace context.",
});
}
@@ -185,9 +183,6 @@ async function openBrowserTabForRequest(params: {
await browserHost?.registerWorkspaceBrowser?.({ browserId, workspaceId });
await browserHost?.setWorkspaceActiveBrowser?.({ browserId, workspaceId });
if (request.agentId) {
await browserHost?.setAgentActiveBrowser?.({ agentId: request.agentId, browserId });
}
if (browserHost?.executeAutomationCommand) {
ensureResidentBrowserWebview({ browserId, url: normalizedUrl });
@@ -236,7 +231,7 @@ async function waitForBrowserRegistration(params: {
agentId: params.request.agentId,
cwd: params.request.cwd,
workspaceId: params.workspaceId,
command: { command: "list_tabs", args: { workspaceId: params.workspaceId } },
command: { command: "list_tabs", args: {} },
});
if (payload.ok && payload.result.command === "list_tabs") {
if (payload.result.tabs.some((tab) => tab.browserId === params.browserId)) {
@@ -252,28 +247,6 @@ function delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function rememberAgentBrowserTarget(input: {
request: BrowserAutomationExecuteRequest;
browserHost: DesktopHostBridge["browser"] | undefined;
}): Promise<void> {
if (!input.request.agentId) {
return;
}
const browserId = readRequestBrowserId(input.request);
if (!browserId) {
return;
}
await input.browserHost?.setAgentActiveBrowser?.({ agentId: input.request.agentId, browserId });
}
function readRequestBrowserId(request: BrowserAutomationExecuteRequest): string | null {
if (request.browserId) {
return request.browserId;
}
const args = request.command.args as { browserId?: unknown };
return typeof args.browserId === "string" && args.browserId.length > 0 ? args.browserId : null;
}
function normalizeBridgePayload(
requestId: string,
payload: BrowserAutomationResponsePayload,
-1
View File
@@ -132,7 +132,6 @@ export interface DesktopBrowserBridge {
workspaceId: string;
browserId: string | null;
}) => Promise<void>;
setAgentActiveBrowser?: (input: { agentId: string; browserId: string | null }) => Promise<void>;
openDevTools?: (browserId: string) => Promise<unknown>;
clearPartition?: (browserId: string) => Promise<void>;
executeAutomationCommand?: (
@@ -1,4 +1,5 @@
import AsyncStorage from "@react-native-async-storage/async-storage";
import { BrowserAutomationBrowserIdSchema } from "@getpaseo/protocol/browser-automation/rpc-schemas";
import { create } from "zustand";
import { createJSONStorage, persist } from "zustand/middleware";
import {
@@ -22,10 +23,14 @@ interface BrowserStoreState extends BrowserIndexState {
}
function createBrowserId(): string {
let browserId: string;
if (typeof globalThis.crypto?.randomUUID === "function") {
return globalThis.crypto.randomUUID();
browserId = globalThis.crypto.randomUUID();
} else {
const randomSuffix = Math.random().toString(16).slice(2) || "0";
browserId = `${Date.now()}-${randomSuffix}`;
}
return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
return BrowserAutomationBrowserIdSchema.parse(browserId);
}
export const useBrowserStore = create<BrowserStoreState>()(