diff --git a/src/main/browser/agent-browser/agent-browser-bridge.ts b/src/main/browser/agent-browser/agent-browser-bridge.ts index 8da034b95b7..b746a98d472 100644 --- a/src/main/browser/agent-browser/agent-browser-bridge.ts +++ b/src/main/browser/agent-browser/agent-browser-bridge.ts @@ -278,7 +278,8 @@ export class AgentBrowserBridge extends AgentBrowserBridgeInteractionCommands { worktreeId, browserPageId, async (sessionName) => await this.execAgentBrowser(sessionName, args, { cliArgv: true }), - { elements: passthroughElementArgs(args) } + // Raw commands and batches can click or press keys too. + { elements: passthroughElementArgs(args), blockFileChooser: true } ) } } diff --git a/src/main/browser/agent-browser/command-queue/agent-browser-bridge-file-chooser.test.ts b/src/main/browser/agent-browser/command-queue/agent-browser-bridge-file-chooser.test.ts index 2abf12b414c..1315276dd31 100644 --- a/src/main/browser/agent-browser/command-queue/agent-browser-bridge-file-chooser.test.ts +++ b/src/main/browser/agent-browser/command-queue/agent-browser-bridge-file-chooser.test.ts @@ -108,6 +108,13 @@ describe('an agent click that opens a file picker', () => { expect(intercepting).toBe(false) }) + it('catches the picker a raw exec click opens', async () => { + await expect(bridge.exec('click @e3')).rejects.toMatchObject({ + code: 'browser_file_chooser_opened' + }) + expect(intercepting).toBe(false) + }) + it("stops intercepting the desktop user's pickers when an SSH host loses the page mid-click", async () => { const revoked = new AbortController() onClick = () => revoked.abort() diff --git a/src/main/browser/agent-browser/command-queue/agent-browser-bridge-queue.ts b/src/main/browser/agent-browser/command-queue/agent-browser-bridge-queue.ts index acd319bfb4a..e6fb071bd29 100644 --- a/src/main/browser/agent-browser/command-queue/agent-browser-bridge-queue.ts +++ b/src/main/browser/agent-browser/command-queue/agent-browser-bridge-queue.ts @@ -72,7 +72,8 @@ export abstract class AgentBrowserBridgeQueue extends AgentBrowserBridgeActionEf ): Promise { return this.enqueueTargetedCommand(worktreeId, browserPageId, execute, { ...options, - reportEffects: options.reportEffects !== false + reportEffects: options.reportEffects !== false, + blockFileChooser: options.blockFileChooser !== false }) } @@ -85,7 +86,7 @@ export abstract class AgentBrowserBridgeQueue extends AgentBrowserBridgeActionEf ): Promise { return this.enqueuePageAction(worktreeId, browserPageId, execute, { ...options, - skipFileChooserBlock: true + blockFileChooser: false }) } @@ -117,8 +118,8 @@ export abstract class AgentBrowserBridgeQueue extends AgentBrowserBridgeActionEf const inThisCall = captureActiveRpcCall() // Decided at enqueue: a queued command runs under the caller of the command ahead of it. const reportEffects = options.reportEffects === true && agentCommand.agent - // Page actions deliver agent or remote input; a desktop picker would activate Orca over the user's work. - const blockFileChooser = options.reportEffects === true && options.skipFileChooserBlock !== true + // A desktop picker would activate Orca over the user's work. + const blockFileChooser = options.blockFileChooser === true const expectedUrl = getRequestedPageUrl() // A dialog blocks the page until it is answered. A command still waiting never runs; one diff --git a/src/main/browser/agent-browser/command-queue/agent-browser-queued-command.ts b/src/main/browser/agent-browser/command-queue/agent-browser-queued-command.ts index 6d1ca18a4b7..8bccc646bfb 100644 --- a/src/main/browser/agent-browser/command-queue/agent-browser-queued-command.ts +++ b/src/main/browser/agent-browser/command-queue/agent-browser-queued-command.ts @@ -8,8 +8,9 @@ export type EnqueueTargetedCommandOptions = { ensureSession?: boolean /** Page actions report what they caused when an agent runs them. */ reportEffects?: boolean - // Upload catches the picker itself; actions without user activation cannot open one. - skipFileChooserBlock?: boolean + // Agent or remote input must not raise the OS file picker. Upload catches the picker itself, and + // actions without user activation cannot open one. + blockFileChooser?: boolean // Text mutations must not fall back to a global tab from another worktree. requireScopedTarget?: boolean // Element arguments that may be snapshot refs. diff --git a/src/main/browser/agent-browser/commands/agent-browser-bridge-interaction-commands.ts b/src/main/browser/agent-browser/commands/agent-browser-bridge-interaction-commands.ts index 6dd604ab103..28b2d61362e 100644 --- a/src/main/browser/agent-browser/commands/agent-browser-bridge-interaction-commands.ts +++ b/src/main/browser/agent-browser/commands/agent-browser-bridge-interaction-commands.ts @@ -115,7 +115,7 @@ export abstract class AgentBrowserBridgeInteractionCommands extends AgentBrowser await chooser.release() } }, - { elements: [element], skipFileChooserBlock: true } + { elements: [element], blockFileChooser: false } ) }