From 487d39e02ef647cc4144aca1163d5d3391124ccb Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sat, 12 Sep 2026 01:36:59 -0700 Subject: [PATCH] perf(browser): fill plain fields in one stdin edit (#20364) --- .../agent-browser-bridge-core-commands.ts | 27 ++------ .../agent-browser-bridge-text-input.test.ts | 62 ++++++------------- 2 files changed, 25 insertions(+), 64 deletions(-) diff --git a/src/main/browser/agent-browser-bridge-core-commands.ts b/src/main/browser/agent-browser-bridge-core-commands.ts index aac82f77487..335bacef3b9 100644 --- a/src/main/browser/agent-browser-bridge-core-commands.ts +++ b/src/main/browser/agent-browser-bridge-core-commands.ts @@ -6,14 +6,10 @@ import type { } from '../../shared/runtime-types' import { assertClipboardTextWriteWithinLimitWithYield } from '../../shared/clipboard-text' import { normalizeBrowserNavigationUrl } from '../../shared/browser-url' -import { iterateBrowserTextInsertionChunks } from './browser-text-insertion' import { BrowserError } from './cdp-bridge' import { ORCA_TAB_SESSION_PREFIX } from './agent-browser-orphan-sweep' import { focusedValueSetExpression } from './agent-browser-bridge-input' -import { - AGENT_BROWSER_TEXT_ARGUMENT_MAX_BYTES, - EMBEDDED_NAVIGATION_TIMEOUT_MS -} from './agent-browser-bridge-types' +import { EMBEDDED_NAVIGATION_TIMEOUT_MS } from './agent-browser-bridge-types' import { isAbortedNavigationError, waitForAbortedNavigationReplacement @@ -157,23 +153,10 @@ export abstract class AgentBrowserBridgeCoreCommands extends AgentBrowserBridgeQ async (sessionName) => { if (!(await this.isExplicitContentEditableTarget(sessionName, element))) { await this.execAgentBrowser(sessionName, ['focus', element]) - await this.execAgentBrowser(sessionName, [ - 'eval', - focusedValueSetExpression(JSON.stringify('')) - ]) - for (const chunk of iterateBrowserTextInsertionChunks( - value, - AGENT_BROWSER_TEXT_ARGUMENT_MAX_BYTES - )) { - await this.execAgentBrowser(sessionName, [ - 'eval', - focusedValueSetExpression(JSON.stringify(chunk), { append: true }) - ]) - } - await this.execAgentBrowser(sessionName, [ - 'eval', - focusedValueSetExpression(JSON.stringify(''), { append: true, dispatchEvents: true }) - ]) + // One stdin edit avoids argv limits and repeated copying of the growing field value. + await this.execAgentBrowser(sessionName, ['eval', '--stdin'], { + stdinText: focusedValueSetExpression(JSON.stringify(value), { dispatchEvents: true }) + }) return { filled: element } as BrowserFillResult } diff --git a/src/main/browser/agent-browser-bridge-text-input.test.ts b/src/main/browser/agent-browser-bridge-text-input.test.ts index 4216a6192be..f083d11067c 100644 --- a/src/main/browser/agent-browser-bridge-text-input.test.ts +++ b/src/main/browser/agent-browser-bridge-text-input.test.ts @@ -278,8 +278,9 @@ describe('AgentBrowserBridge', () => { ) expect(evalCall).toBeDefined() const args = evalCall![1] as string[] - const expression = args[args.indexOf('eval') + 1] - expect(() => new Function(expression)).not.toThrow() + expect(args[args.indexOf('eval') + 1]).toBe('--stdin') + expect(stdinWrites).toHaveLength(1) + expect(() => new Function(stdinWrites[0])).not.toThrow() }) it('replaces contenteditable text through the browser editing pipeline', async () => { @@ -359,12 +360,7 @@ describe('AgentBrowserBridge', () => { await bridge.fill('@spinbutton', '200') - const expressions = execFileMock.mock.calls - .filter((call: unknown[]) => (call[1] as string[]).includes('eval')) - .map((call: unknown[]) => { - const args = call[1] as string[] - return args[args.indexOf('eval') + 1] - }) + const expressions = stdinWrites const input = createFillEvalNode({ tagName: 'INPUT' }) const wrapper = createFillEvalNode({ @@ -389,12 +385,7 @@ describe('AgentBrowserBridge', () => { await bridge.fill('@spinbutton', '200') - const expressions = execFileMock.mock.calls - .filter((call: unknown[]) => (call[1] as string[]).includes('eval')) - .map((call: unknown[]) => { - const args = call[1] as string[] - return args[args.indexOf('eval') + 1] - }) + const expressions = stdinWrites const input = createFillEvalNode({ tagName: 'INPUT' }) const wrapper = createFillEvalNode({ @@ -419,12 +410,7 @@ describe('AgentBrowserBridge', () => { await bridge.fill('@spinbutton', '200') - const expressions = execFileMock.mock.calls - .filter((call: unknown[]) => (call[1] as string[]).includes('eval')) - .map((call: unknown[]) => { - const args = call[1] as string[] - return args[args.indexOf('eval') + 1] - }) + const expressions = stdinWrites const input = createFillEvalNode({ tagName: 'INPUT' }) const controlled = createFillEvalNode({ tagName: 'DIV', descendant: input.node }) @@ -452,12 +438,7 @@ describe('AgentBrowserBridge', () => { await bridge.fill('@spinbutton', '200') - const expressions = execFileMock.mock.calls - .filter((call: unknown[]) => (call[1] as string[]).includes('eval')) - .map((call: unknown[]) => { - const args = call[1] as string[] - return args[args.indexOf('eval') + 1] - }) + const expressions = stdinWrites const hiddenInput = createFillEvalNode({ tagName: 'INPUT', type: 'hidden' }) const numberInput = createFillEvalNode({ tagName: 'INPUT', type: 'number' }) @@ -485,12 +466,7 @@ describe('AgentBrowserBridge', () => { await bridge.fill('@input', '200') - const expressions = execFileMock.mock.calls - .filter((call: unknown[]) => (call[1] as string[]).includes('eval')) - .map((call: unknown[]) => { - const args = call[1] as string[] - return args[args.indexOf('eval') + 1] - }) + const expressions = stdinWrites const input = createFillEvalNode({ tagName: 'INPUT' }) @@ -503,8 +479,8 @@ describe('AgentBrowserBridge', () => { expect(input.events.map((event) => event.type)).toEqual(['input', 'change']) }) - it('chunks large agent-browser fill values before eval transport', async () => { - const text = ['x'.repeat(AGENT_BROWSER_TEXT_ARGUMENT_MAX_BYTES), 'tail'].join('') + it('fills large plain fields with one stdin edit and one event pair', async () => { + const text = `${'é\n'.repeat(512 * 1024)}tail'\\` succeedWith({ ok: true }) await bridge.fill('@textarea', text) @@ -512,15 +488,17 @@ describe('AgentBrowserBridge', () => { const evalCalls = execFileMock.mock.calls.filter((call: unknown[]) => (call[1] as string[]).includes('eval') ) - const appendExpressions = evalCalls.slice(1, -1).map((call: unknown[]) => { - const args = call[1] as string[] - return args[args.indexOf('eval') + 1] + expect(evalCalls).toHaveLength(1) + expect(evalCalls[0][1]).toContain('--stdin') + expect(stdinWrites).toHaveLength(1) + expect((evalCalls[0][1] as string[]).join('')).not.toContain(text) + const input = createFillEvalNode({ tagName: 'TEXTAREA' }) + runFillEvalExpressions(stdinWrites, { + activeElement: input.node, + getElementById: () => null }) - - expect(appendExpressions).toHaveLength(2) - expect(appendExpressions.some((expression) => expression.includes(text))).toBe(false) - expect(appendExpressions[0]).toContain('x'.repeat(AGENT_BROWSER_TEXT_ARGUMENT_MAX_BYTES)) - expect(appendExpressions[1]).toContain('tail') + expect(input.value).toBe(text) + expect(input.events.map((event) => event.type)).toEqual(['input', 'change']) }) it.each([