Skip to content
This repository was archived by the owner on Jun 28, 2026. It is now read-only.

Commit acc272e

Browse files
Merge pull request #370 from olasunkanmi-SE/feature/browser-automation-tool
feat: add Browser Automation Tool (#11)
2 parents fc5f45b + ea3e193 commit acc272e

11 files changed

Lines changed: 1759 additions & 2 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { z } from "zod";
2+
import { Logger, LogLevel } from "../../../infrastructure/logger/logger";
3+
import { BrowserTool } from "../../../tools/tools";
4+
import { BROWSER_ACTIONS } from "../../../tools/browser-actions";
5+
import { StructuredTool } from "@langchain/core/tools";
6+
7+
const browserSchema = z.object({
8+
action: z.enum(BROWSER_ACTIONS).describe("The browser action to perform."),
9+
url: z.string().optional().describe("URL for navigate/tab_new actions."),
10+
ref: z
11+
.string()
12+
.optional()
13+
.describe(
14+
"Element reference from a previous snapshot (for click, type, hover, select_option).",
15+
),
16+
text: z.string().optional().describe("Text to type (for type action)."),
17+
value: z
18+
.string()
19+
.optional()
20+
.describe("Value to select (for select_option action)."),
21+
expression: z
22+
.string()
23+
.optional()
24+
.describe("JavaScript expression to evaluate in the page context."),
25+
key: z
26+
.string()
27+
.optional()
28+
.describe(
29+
"Key to press (for press_key action, e.g. 'Enter', 'Tab', 'ArrowDown').",
30+
),
31+
time: z
32+
.number()
33+
.optional()
34+
.describe("Time to wait in ms (for wait action, default 2000)."),
35+
});
36+
37+
type BrowserToolInput = z.infer<typeof browserSchema>;
38+
39+
// StructuredTool<any> is intentional — StructuredTool<typeof browserSchema>
40+
// triggers TS2589 ("Type instantiation is excessively deep"). All tools
41+
// in this codebase use <any> for the same reason.
42+
export class LangChainBrowserTool extends StructuredTool<any> {
43+
private readonly logger: Logger;
44+
constructor(private readonly toolInstance: BrowserTool) {
45+
super();
46+
this.logger = Logger.initialize("LangChainBrowserTool", {
47+
minLevel: LogLevel.DEBUG,
48+
enableConsole: true,
49+
enableFile: true,
50+
enableTelemetry: true,
51+
});
52+
}
53+
54+
name = "browser";
55+
description =
56+
"Control a headless browser via Playwright. Navigate pages, click elements, type text, take screenshots, read page snapshots (accessibility tree), and execute JavaScript. " +
57+
"Workflow: 1) navigate to a URL, 2) snapshot to see the page structure and element refs, 3) interact using refs from the snapshot.";
58+
59+
schema = browserSchema;
60+
61+
async _call(input: BrowserToolInput): Promise<string> {
62+
this.logger.info(
63+
`Executing tool: ${this.name} with action: ${input.action}`,
64+
);
65+
try {
66+
const result = await this.toolInstance.execute(input);
67+
return result;
68+
} catch (error: unknown) {
69+
const message = error instanceof Error ? error.message : String(error);
70+
this.logger.error(`Error in tool ${this.name}: ${message}`, { input });
71+
return `Error: ${message}`;
72+
}
73+
}
74+
}

src/agents/langgraph/tools/provider.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
WebPreviewTool,
1717
SearchTool,
1818
DeepTerminalTool,
19+
BrowserTool,
1920
TodoTool,
2021
MemoryTool,
2122
TestRunnerTool,
@@ -40,6 +41,7 @@ import { LangChainTodoTool } from "./todo";
4041
import { LangChainMemoryTool } from "./memory";
4142
import { LangChainTestRunnerTool } from "./test-runner";
4243
import { LangChainComposerTool } from "./composer";
44+
import { LangChainBrowserTool } from "./browser";
4345
import {
4446
DebugControlTool,
4547
DebugEvaluateTool,
@@ -146,6 +148,12 @@ class WebPreviewToolFactory implements IToolFactory {
146148
}
147149
}
148150

151+
class BrowserToolFactory implements IToolFactory {
152+
createTool(): StructuredTool<any> {
153+
return new LangChainBrowserTool(new BrowserTool());
154+
}
155+
}
156+
149157
class TodoToolFactory implements IToolFactory {
150158
createTool(): StructuredTool<any> {
151159
return new LangChainTodoTool(new TodoTool());
@@ -266,6 +274,7 @@ const TOOL_ROLE_MAPPING: Record<string, string[]> = {
266274
"manage_tasks",
267275
"manage_core_memory",
268276
"compose_files",
277+
"browser",
269278
],
270279
"doc-writer": [
271280
"search",
@@ -285,6 +294,7 @@ const TOOL_ROLE_MAPPING: Record<string, string[]> = {
285294
"search_vector_db",
286295
"standup_intelligence",
287296
"team_graph",
297+
"browser",
288298
],
289299
"file-organizer": [
290300
"file",
@@ -328,6 +338,7 @@ const TOOL_ROLE_MAPPING: Record<string, string[]> = {
328338
"manage_tasks",
329339
"standup_intelligence",
330340
"team_graph",
341+
"browser",
331342
],
332343
reviewer: [
333344
"analyze",
@@ -366,6 +377,7 @@ const TOOL_ROLE_MAPPING: Record<string, string[]> = {
366377
"search_vector_db",
367378
"manage_terminal",
368379
"run_tests",
380+
"browser",
369381
],
370382
debugger: [
371383
"debug_get_state",
@@ -383,6 +395,7 @@ const TOOL_ROLE_MAPPING: Record<string, string[]> = {
383395
"edit_file",
384396
"ripgrep_search",
385397
"get_diagnostics",
398+
"browser",
386399
],
387400
"architecture-expert": [
388401
"get_architecture_knowledge",
@@ -424,6 +437,7 @@ export class ToolProvider {
424437
// new ListFilesToolFactory(), // Provided by DeepAgent backend
425438
// new EditFileToolFactory(), // Provided by DeepAgent backend
426439
new WebPreviewToolFactory(),
440+
new BrowserToolFactory(),
427441
new SearchToolFactory(this.contextRetriever),
428442
new TodoToolFactory(),
429443
new MemoryToolFactory(),

0 commit comments

Comments
 (0)