Skip to content

Commit e77b22e

Browse files
authored
fix: isolate concurrent browser agent instances (#24794)
1 parent 1b3e7d6 commit e77b22e

File tree

8 files changed

+446
-173
lines changed

8 files changed

+446
-173
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"I'll launch two browser agents concurrently to check both repositories."},{"functionCall":{"name":"browser_agent","args":{"task":"Navigate to https://example.com and get the page title"}}},{"functionCall":{"name":"browser_agent","args":{"task":"Navigate to https://example.com and get the page title"}}}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":100,"candidatesTokenCount":50,"totalTokenCount":150}}]}
2+
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"functionCall":{"name":"navigate_page","args":{"url":"https://example.com"}}}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":100,"candidatesTokenCount":20,"totalTokenCount":120}}]}
3+
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"functionCall":{"name":"navigate_page","args":{"url":"https://example.com"}}}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":100,"candidatesTokenCount":20,"totalTokenCount":120}}]}
4+
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"functionCall":{"name":"take_snapshot","args":{}}}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":150,"candidatesTokenCount":15,"totalTokenCount":165}}]}
5+
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"functionCall":{"name":"take_snapshot","args":{}}}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":150,"candidatesTokenCount":15,"totalTokenCount":165}}]}
6+
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"functionCall":{"name":"complete_task","args":{"result":{"success":true,"summary":"Page title is Example Domain."}}}}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":200,"candidatesTokenCount":30,"totalTokenCount":230}}]}
7+
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"functionCall":{"name":"complete_task","args":{"result":{"success":true,"summary":"Page title is Example Domain."}}}}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":200,"candidatesTokenCount":30,"totalTokenCount":230}}]}
8+
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Both browser agents completed successfully. Agent 1 and Agent 2 both navigated to their respective pages and confirmed the page titles."}],"role":"model"},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":300,"candidatesTokenCount":40,"totalTokenCount":340}}]}

integration-tests/browser-agent.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,4 +307,48 @@ describe.skipIf(!chromeAvailable)('browser-agent', () => {
307307

308308
await run.expectText('successfully written', 15000);
309309
});
310+
311+
it('should handle concurrent browser agents with isolated session mode', async () => {
312+
rig.setup('browser-concurrent', {
313+
fakeResponsesPath: join(__dirname, 'browser-agent.concurrent.responses'),
314+
settings: {
315+
agents: {
316+
overrides: {
317+
browser_agent: {
318+
enabled: true,
319+
},
320+
},
321+
browser: {
322+
headless: true,
323+
// Isolated mode supports concurrent browser agents.
324+
// Persistent/existing modes reject concurrent calls to prevent
325+
// Chrome profile lock conflicts.
326+
sessionMode: 'isolated',
327+
},
328+
},
329+
},
330+
});
331+
332+
const result = await rig.run({
333+
args: 'Launch two browser agents concurrently to check example.com',
334+
});
335+
336+
assertModelHasOutput(result);
337+
338+
const toolLogs = rig.readToolLogs();
339+
const browserCalls = toolLogs.filter(
340+
(t) => t.toolRequest.name === 'browser_agent',
341+
);
342+
343+
// Both browser_agent invocations should have been called
344+
expect(browserCalls.length).toBe(2);
345+
346+
// Both should complete successfully (no errors)
347+
for (const call of browserCalls) {
348+
expect(
349+
call.toolRequest.success,
350+
`browser_agent call failed: ${JSON.stringify(call.toolRequest)}`,
351+
).toBe(true);
352+
}
353+
});
310354
});

packages/core/src/agents/browser/browserAgentFactory.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ const mockBrowserManager = {
3838
]),
3939
callTool: vi.fn().mockResolvedValue({ content: [] }),
4040
close: vi.fn().mockResolvedValue(undefined),
41+
acquire: vi.fn(),
42+
release: vi.fn(),
4143
};
4244

4345
// Mock dependencies

0 commit comments

Comments
 (0)