illuma-agents 1.0.19 → 1.0.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,431 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+ var langgraph = require('@langchain/langgraph');
5
+ var tools = require('@langchain/core/tools');
6
+
7
+ /**
8
+ * Browser Automation Tools with LangGraph Interrupt Support
9
+ *
10
+ * These tools use LangGraph's interrupt() mechanism to pause execution
11
+ * and wait for the browser extension to execute actions and return results.
12
+ *
13
+ * Flow:
14
+ * 1. Agent calls browser tool (e.g., browser_navigate)
15
+ * 2. Tool calls interrupt() with action details
16
+ * 3. Graph pauses and returns interrupt to client
17
+ * 4. Extension executes action in browser
18
+ * 5. Extension sends resume Command with actual result
19
+ * 6. Graph continues with real browser data
20
+ *
21
+ * This enables proper chain-of-thought with browser context because
22
+ * the agent receives ACTUAL results (page elements, screenshots, etc.)
23
+ * instead of placeholder acknowledgments.
24
+ */
25
+ // ============================================
26
+ // Tool Schemas (same as BrowserTools.ts)
27
+ // ============================================
28
+ const BrowserClickSchema = zod.z.object({
29
+ index: zod.z.number().optional().describe('The index of the element to click, as shown in the page context'),
30
+ coordinates: zod.z.object({
31
+ x: zod.z.number().describe('X coordinate in viewport pixels'),
32
+ y: zod.z.number().describe('Y coordinate in viewport pixels'),
33
+ }).optional().describe('Coordinates for clicking by position'),
34
+ reason: zod.z.string().optional().describe('Why you are clicking this element'),
35
+ });
36
+ const BrowserTypeSchema = zod.z.object({
37
+ index: zod.z.number().describe('The index of the input element to type into'),
38
+ text: zod.z.string().describe('The text to type'),
39
+ clear: zod.z.boolean().optional().describe('Clear existing content first'),
40
+ pressEnter: zod.z.boolean().optional().describe('Press Enter after typing'),
41
+ });
42
+ const BrowserNavigateSchema = zod.z.object({
43
+ url: zod.z.string().describe('The URL to navigate to'),
44
+ reason: zod.z.string().optional().describe('Why navigating to this URL'),
45
+ });
46
+ const BrowserScrollSchema = zod.z.object({
47
+ direction: zod.z.enum(['up', 'down', 'left', 'right']).describe('Scroll direction'),
48
+ amount: zod.z.number().optional().describe('Pixels to scroll (default: 500)'),
49
+ });
50
+ const BrowserExtractSchema = zod.z.object({
51
+ query: zod.z.string().optional().describe('Query to filter extracted content'),
52
+ selector: zod.z.string().optional().describe('CSS selector to extract from'),
53
+ });
54
+ const BrowserHoverSchema = zod.z.object({
55
+ index: zod.z.number().describe('Element index to hover over'),
56
+ });
57
+ const BrowserWaitSchema = zod.z.object({
58
+ duration: zod.z.number().optional().describe('Milliseconds to wait (default: 1000)'),
59
+ reason: zod.z.string().optional().describe('Why waiting'),
60
+ });
61
+ const BrowserGoBackSchema = zod.z.object({
62
+ reason: zod.z.string().optional().describe('Why going back'),
63
+ });
64
+ const BrowserScreenshotSchema = zod.z.object({
65
+ fullPage: zod.z.boolean().optional().describe('Capture full page vs viewport'),
66
+ });
67
+ const BrowserGetPageStateSchema = zod.z.object({
68
+ reason: zod.z.string().optional().describe('Why getting page state'),
69
+ });
70
+ // ============================================
71
+ // Helper to generate interrupt IDs
72
+ // ============================================
73
+ let interruptCounter = 0;
74
+ function generateInterruptId() {
75
+ return `browser_${Date.now()}_${++interruptCounter}`;
76
+ }
77
+ // ============================================
78
+ // Interrupt-based Tool Implementations
79
+ // ============================================
80
+ /**
81
+ * Navigate tool - navigates to URL and returns page state
82
+ */
83
+ function createBrowserNavigateInterruptTool() {
84
+ return tools.tool(async ({ url, reason }) => {
85
+ const interruptId = generateInterruptId();
86
+ // This will pause the graph and wait for extension to provide result
87
+ const result = langgraph.interrupt({
88
+ type: 'browser_interrupt',
89
+ action: { type: 'navigate', url, reason },
90
+ interruptId,
91
+ });
92
+ // When resumed, result contains actual page state
93
+ if (!result.success) {
94
+ return `Navigation failed: ${result.error}`;
95
+ }
96
+ if (result.pageState) {
97
+ return `Successfully navigated to ${result.pageState.url} (${result.pageState.title})
98
+
99
+ ## Interactive Elements (${result.pageState.elementCount} total)
100
+ ${result.pageState.elementList}
101
+
102
+ Viewport: ${result.pageState.viewportHeight}px, Scroll: ${result.pageState.scrollPosition}/${result.pageState.scrollHeight}px`;
103
+ }
104
+ return `Successfully navigated to ${url}`;
105
+ }, {
106
+ name: 'browser_navigate',
107
+ description: `Navigate to a URL. Returns the page state with interactive elements after navigation completes.
108
+
109
+ Example: browser_navigate({ url: "https://www.amazon.com" })
110
+ Returns: Page title, URL, and list of interactive elements with their [index] numbers.`,
111
+ schema: BrowserNavigateSchema,
112
+ });
113
+ }
114
+ /**
115
+ * Click tool - clicks element and returns updated state
116
+ */
117
+ function createBrowserClickInterruptTool() {
118
+ return tools.tool(async ({ index, coordinates, reason }) => {
119
+ const interruptId = generateInterruptId();
120
+ const result = langgraph.interrupt({
121
+ type: 'browser_interrupt',
122
+ action: { type: 'click', index, coordinates, reason },
123
+ interruptId,
124
+ });
125
+ if (!result.success) {
126
+ return `Click failed: ${result.error}`;
127
+ }
128
+ // If click caused navigation, return new page state
129
+ if (result.pageState) {
130
+ return `Clicked element. Page updated:
131
+
132
+ URL: ${result.pageState.url}
133
+ Title: ${result.pageState.title}
134
+
135
+ ## Interactive Elements (${result.pageState.elementCount} total)
136
+ ${result.pageState.elementList}`;
137
+ }
138
+ return `Successfully clicked element${index !== undefined ? ` [${index}]` : ''}`;
139
+ }, {
140
+ name: 'browser_click',
141
+ description: `Click an element by index or coordinates.
142
+
143
+ Use the [index] number from the interactive elements list.
144
+ Example: browser_click({ index: 5 }) to click element [5]`,
145
+ schema: BrowserClickSchema,
146
+ });
147
+ }
148
+ /**
149
+ * Type tool - types text into input field
150
+ */
151
+ function createBrowserTypeInterruptTool() {
152
+ return tools.tool(async ({ index, text, clear, pressEnter }) => {
153
+ const interruptId = generateInterruptId();
154
+ const result = langgraph.interrupt({
155
+ type: 'browser_interrupt',
156
+ action: { type: 'type', index, text, clear, pressEnter },
157
+ interruptId,
158
+ });
159
+ if (!result.success) {
160
+ return `Type failed: ${result.error}`;
161
+ }
162
+ // If typing + enter caused navigation (e.g., search), return new state
163
+ if (result.pageState) {
164
+ return `Typed "${text}"${pressEnter ? ' and pressed Enter' : ''}. Page updated:
165
+
166
+ URL: ${result.pageState.url}
167
+ Title: ${result.pageState.title}
168
+
169
+ ## Interactive Elements (${result.pageState.elementCount} total)
170
+ ${result.pageState.elementList}`;
171
+ }
172
+ return `Successfully typed "${text}" into element [${index}]${pressEnter ? ' and pressed Enter' : ''}`;
173
+ }, {
174
+ name: 'browser_type',
175
+ description: `Type text into an input field.
176
+
177
+ Use the [index] from interactive elements list. Look for <input> elements.
178
+ Set pressEnter: true to submit after typing (for search fields).
179
+
180
+ Example: browser_type({ index: 3, text: "laptop 16gb ram", pressEnter: true })`,
181
+ schema: BrowserTypeSchema,
182
+ });
183
+ }
184
+ /**
185
+ * Get page state tool - captures current page elements
186
+ */
187
+ function createBrowserGetPageStateInterruptTool() {
188
+ return tools.tool(async ({ reason }) => {
189
+ const interruptId = generateInterruptId();
190
+ const result = langgraph.interrupt({
191
+ type: 'browser_interrupt',
192
+ action: { type: 'get_page_state', reason },
193
+ interruptId,
194
+ });
195
+ if (!result.success) {
196
+ return `Failed to get page state: ${result.error}`;
197
+ }
198
+ if (result.pageState) {
199
+ return `## Current Page
200
+ URL: ${result.pageState.url}
201
+ Title: ${result.pageState.title}
202
+
203
+ ## Interactive Elements (${result.pageState.elementCount} total)
204
+ ${result.pageState.elementList}
205
+
206
+ Viewport: ${result.pageState.viewportHeight}px
207
+ Scroll: ${result.pageState.scrollPosition}/${result.pageState.scrollHeight}px`;
208
+ }
209
+ return 'Page state captured but no elements found.';
210
+ }, {
211
+ name: 'browser_get_page_state',
212
+ description: `Get the current page state with all interactive elements.
213
+
214
+ Returns the list of clickable/typeable elements with their [index] numbers.
215
+ Use this to see what elements are available on the current page.`,
216
+ schema: BrowserGetPageStateSchema,
217
+ });
218
+ }
219
+ /**
220
+ * Scroll tool
221
+ */
222
+ function createBrowserScrollInterruptTool() {
223
+ return tools.tool(async ({ direction, amount }) => {
224
+ const interruptId = generateInterruptId();
225
+ const result = langgraph.interrupt({
226
+ type: 'browser_interrupt',
227
+ action: { type: 'scroll', direction, amount },
228
+ interruptId,
229
+ });
230
+ if (!result.success) {
231
+ return `Scroll failed: ${result.error}`;
232
+ }
233
+ if (result.pageState) {
234
+ return `Scrolled ${direction}. New elements visible:
235
+
236
+ ## Interactive Elements (${result.pageState.elementCount} total)
237
+ ${result.pageState.elementList}
238
+
239
+ Scroll: ${result.pageState.scrollPosition}/${result.pageState.scrollHeight}px`;
240
+ }
241
+ return `Successfully scrolled ${direction}${amount ? ` ${amount}px` : ''}`;
242
+ }, {
243
+ name: 'browser_scroll',
244
+ description: `Scroll the page in a direction.
245
+
246
+ Example: browser_scroll({ direction: "down", amount: 500 })`,
247
+ schema: BrowserScrollSchema,
248
+ });
249
+ }
250
+ /**
251
+ * Extract tool
252
+ */
253
+ function createBrowserExtractInterruptTool() {
254
+ return tools.tool(async ({ query, selector }) => {
255
+ const interruptId = generateInterruptId();
256
+ const result = langgraph.interrupt({
257
+ type: 'browser_interrupt',
258
+ action: { type: 'extract', query, selector },
259
+ interruptId,
260
+ });
261
+ if (!result.success) {
262
+ return `Extract failed: ${result.error}`;
263
+ }
264
+ return result.extractedContent || 'No content extracted.';
265
+ }, {
266
+ name: 'browser_extract',
267
+ description: `Extract text content from the page.
268
+
269
+ Example: browser_extract({ query: "price" })`,
270
+ schema: BrowserExtractSchema,
271
+ });
272
+ }
273
+ /**
274
+ * Hover tool
275
+ */
276
+ function createBrowserHoverInterruptTool() {
277
+ return tools.tool(async ({ index }) => {
278
+ const interruptId = generateInterruptId();
279
+ const result = langgraph.interrupt({
280
+ type: 'browser_interrupt',
281
+ action: { type: 'hover', index },
282
+ interruptId,
283
+ });
284
+ if (!result.success) {
285
+ return `Hover failed: ${result.error}`;
286
+ }
287
+ return `Successfully hovered over element [${index}]`;
288
+ }, {
289
+ name: 'browser_hover',
290
+ description: `Hover over an element to reveal tooltips or menus.`,
291
+ schema: BrowserHoverSchema,
292
+ });
293
+ }
294
+ /**
295
+ * Wait tool
296
+ */
297
+ function createBrowserWaitInterruptTool() {
298
+ return tools.tool(async ({ duration, reason }) => {
299
+ const interruptId = generateInterruptId();
300
+ const result = langgraph.interrupt({
301
+ type: 'browser_interrupt',
302
+ action: { type: 'wait', duration, reason },
303
+ interruptId,
304
+ });
305
+ if (!result.success) {
306
+ return `Wait failed: ${result.error}`;
307
+ }
308
+ return `Waited ${duration || 1000}ms`;
309
+ }, {
310
+ name: 'browser_wait',
311
+ description: `Wait for a duration before next action.`,
312
+ schema: BrowserWaitSchema,
313
+ });
314
+ }
315
+ /**
316
+ * Go back tool
317
+ */
318
+ function createBrowserGoBackInterruptTool() {
319
+ return tools.tool(async ({ reason }) => {
320
+ const interruptId = generateInterruptId();
321
+ const result = langgraph.interrupt({
322
+ type: 'browser_interrupt',
323
+ action: { type: 'back', reason },
324
+ interruptId,
325
+ });
326
+ if (!result.success) {
327
+ return `Go back failed: ${result.error}`;
328
+ }
329
+ if (result.pageState) {
330
+ return `Went back to: ${result.pageState.url}
331
+
332
+ ## Interactive Elements (${result.pageState.elementCount} total)
333
+ ${result.pageState.elementList}`;
334
+ }
335
+ return 'Successfully went back';
336
+ }, {
337
+ name: 'browser_back',
338
+ description: `Go back to the previous page in history.`,
339
+ schema: BrowserGoBackSchema,
340
+ });
341
+ }
342
+ /**
343
+ * Screenshot tool
344
+ */
345
+ function createBrowserScreenshotInterruptTool() {
346
+ return tools.tool(async ({ fullPage }) => {
347
+ const interruptId = generateInterruptId();
348
+ const result = langgraph.interrupt({
349
+ type: 'browser_interrupt',
350
+ action: { type: 'screenshot', fullPage },
351
+ interruptId,
352
+ });
353
+ if (!result.success) {
354
+ return `Screenshot failed: ${result.error}`;
355
+ }
356
+ if (result.screenshot) {
357
+ return `Screenshot captured. [Image data available]`;
358
+ }
359
+ return 'Screenshot captured';
360
+ }, {
361
+ name: 'browser_screenshot',
362
+ description: `Capture a screenshot of the current page.`,
363
+ schema: BrowserScreenshotSchema,
364
+ });
365
+ }
366
+ // ============================================
367
+ // Tool Collection
368
+ // ============================================
369
+ const EBrowserInterruptTools = {
370
+ CLICK: 'browser_click',
371
+ TYPE: 'browser_type',
372
+ NAVIGATE: 'browser_navigate',
373
+ SCROLL: 'browser_scroll',
374
+ EXTRACT: 'browser_extract',
375
+ HOVER: 'browser_hover',
376
+ WAIT: 'browser_wait',
377
+ BACK: 'browser_back',
378
+ SCREENSHOT: 'browser_screenshot',
379
+ GET_PAGE_STATE: 'browser_get_page_state',
380
+ };
381
+ const BROWSER_INTERRUPT_TOOL_NAMES = Object.values(EBrowserInterruptTools);
382
+ function isBrowserInterruptToolCall(toolName) {
383
+ return BROWSER_INTERRUPT_TOOL_NAMES.includes(toolName);
384
+ }
385
+ /**
386
+ * Create all interrupt-based browser tools
387
+ *
388
+ * Use these when the client is a browser extension that can:
389
+ * 1. Detect browser_interrupt events in the stream
390
+ * 2. Execute browser actions locally
391
+ * 3. Send Command({ resume: result }) to continue the graph
392
+ */
393
+ function createBrowserInterruptTools() {
394
+ return [
395
+ createBrowserNavigateInterruptTool(),
396
+ createBrowserClickInterruptTool(),
397
+ createBrowserTypeInterruptTool(),
398
+ createBrowserGetPageStateInterruptTool(),
399
+ createBrowserScrollInterruptTool(),
400
+ createBrowserExtractInterruptTool(),
401
+ createBrowserHoverInterruptTool(),
402
+ createBrowserWaitInterruptTool(),
403
+ createBrowserGoBackInterruptTool(),
404
+ createBrowserScreenshotInterruptTool(),
405
+ ];
406
+ }
407
+ /**
408
+ * Check if an interrupt is a browser interrupt
409
+ */
410
+ function isBrowserInterrupt(value) {
411
+ return (typeof value === 'object' &&
412
+ value !== null &&
413
+ value.type === 'browser_interrupt');
414
+ }
415
+
416
+ exports.BROWSER_INTERRUPT_TOOL_NAMES = BROWSER_INTERRUPT_TOOL_NAMES;
417
+ exports.EBrowserInterruptTools = EBrowserInterruptTools;
418
+ exports.createBrowserClickInterruptTool = createBrowserClickInterruptTool;
419
+ exports.createBrowserExtractInterruptTool = createBrowserExtractInterruptTool;
420
+ exports.createBrowserGetPageStateInterruptTool = createBrowserGetPageStateInterruptTool;
421
+ exports.createBrowserGoBackInterruptTool = createBrowserGoBackInterruptTool;
422
+ exports.createBrowserHoverInterruptTool = createBrowserHoverInterruptTool;
423
+ exports.createBrowserInterruptTools = createBrowserInterruptTools;
424
+ exports.createBrowserNavigateInterruptTool = createBrowserNavigateInterruptTool;
425
+ exports.createBrowserScreenshotInterruptTool = createBrowserScreenshotInterruptTool;
426
+ exports.createBrowserScrollInterruptTool = createBrowserScrollInterruptTool;
427
+ exports.createBrowserTypeInterruptTool = createBrowserTypeInterruptTool;
428
+ exports.createBrowserWaitInterruptTool = createBrowserWaitInterruptTool;
429
+ exports.isBrowserInterrupt = isBrowserInterrupt;
430
+ exports.isBrowserInterruptToolCall = isBrowserInterruptToolCall;
431
+ //# sourceMappingURL=BrowserInterruptTools.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BrowserInterruptTools.cjs","sources":["../../../src/tools/BrowserInterruptTools.ts"],"sourcesContent":["/**\r\n * Browser Automation Tools with LangGraph Interrupt Support\r\n * \r\n * These tools use LangGraph's interrupt() mechanism to pause execution\r\n * and wait for the browser extension to execute actions and return results.\r\n * \r\n * Flow:\r\n * 1. Agent calls browser tool (e.g., browser_navigate)\r\n * 2. Tool calls interrupt() with action details\r\n * 3. Graph pauses and returns interrupt to client\r\n * 4. Extension executes action in browser\r\n * 5. Extension sends resume Command with actual result\r\n * 6. Graph continues with real browser data\r\n * \r\n * This enables proper chain-of-thought with browser context because\r\n * the agent receives ACTUAL results (page elements, screenshots, etc.)\r\n * instead of placeholder acknowledgments.\r\n */\r\n\r\nimport { z } from 'zod';\r\nimport { interrupt } from '@langchain/langgraph';\r\nimport { tool, DynamicStructuredTool } from '@langchain/core/tools';\r\n\r\n// ============================================\r\n// Browser Interrupt Types\r\n// ============================================\r\n\r\n/**\r\n * Interrupt payload sent to the extension\r\n */\r\nexport interface BrowserInterrupt {\r\n /** Type of browser action to execute */\r\n type: 'browser_interrupt';\r\n /** The specific action to perform */\r\n action: BrowserAction;\r\n /** Unique ID for this interrupt (for matching resume) */\r\n interruptId: string;\r\n}\r\n\r\n/**\r\n * Browser action types\r\n */\r\nexport type BrowserAction = \r\n | { type: 'navigate'; url: string; reason?: string }\r\n | { type: 'click'; index?: number; coordinates?: { x: number; y: number }; reason?: string }\r\n | { type: 'type'; index: number; text: string; clear?: boolean; pressEnter?: boolean }\r\n | { type: 'scroll'; direction: 'up' | 'down' | 'left' | 'right'; amount?: number }\r\n | { type: 'extract'; query?: string; selector?: string }\r\n | { type: 'hover'; index: number }\r\n | { type: 'wait'; duration?: number; reason?: string }\r\n | { type: 'back'; reason?: string }\r\n | { type: 'screenshot'; fullPage?: boolean }\r\n | { type: 'get_page_state'; reason?: string };\r\n\r\n/**\r\n * Result returned from extension after executing action\r\n */\r\nexport interface BrowserActionResult {\r\n success: boolean;\r\n error?: string;\r\n /** Page state after action (for navigate, click, get_page_state) */\r\n pageState?: {\r\n url: string;\r\n title: string;\r\n /** Formatted element list for LLM */\r\n elementList: string;\r\n elementCount: number;\r\n scrollPosition: number;\r\n scrollHeight: number;\r\n viewportHeight: number;\r\n };\r\n /** Screenshot data URL */\r\n screenshot?: string;\r\n /** Extracted content */\r\n extractedContent?: string;\r\n /** Any additional data */\r\n data?: unknown;\r\n}\r\n\r\n// ============================================\r\n// Tool Schemas (same as BrowserTools.ts)\r\n// ============================================\r\n\r\nconst BrowserClickSchema = z.object({\r\n index: z.number().optional().describe(\r\n 'The index of the element to click, as shown in the page context'\r\n ),\r\n coordinates: z.object({\r\n x: z.number().describe('X coordinate in viewport pixels'),\r\n y: z.number().describe('Y coordinate in viewport pixels'),\r\n }).optional().describe('Coordinates for clicking by position'),\r\n reason: z.string().optional().describe('Why you are clicking this element'),\r\n});\r\n\r\nconst BrowserTypeSchema = z.object({\r\n index: z.number().describe('The index of the input element to type into'),\r\n text: z.string().describe('The text to type'),\r\n clear: z.boolean().optional().describe('Clear existing content first'),\r\n pressEnter: z.boolean().optional().describe('Press Enter after typing'),\r\n});\r\n\r\nconst BrowserNavigateSchema = z.object({\r\n url: z.string().describe('The URL to navigate to'),\r\n reason: z.string().optional().describe('Why navigating to this URL'),\r\n});\r\n\r\nconst BrowserScrollSchema = z.object({\r\n direction: z.enum(['up', 'down', 'left', 'right']).describe('Scroll direction'),\r\n amount: z.number().optional().describe('Pixels to scroll (default: 500)'),\r\n});\r\n\r\nconst BrowserExtractSchema = z.object({\r\n query: z.string().optional().describe('Query to filter extracted content'),\r\n selector: z.string().optional().describe('CSS selector to extract from'),\r\n});\r\n\r\nconst BrowserHoverSchema = z.object({\r\n index: z.number().describe('Element index to hover over'),\r\n});\r\n\r\nconst BrowserWaitSchema = z.object({\r\n duration: z.number().optional().describe('Milliseconds to wait (default: 1000)'),\r\n reason: z.string().optional().describe('Why waiting'),\r\n});\r\n\r\nconst BrowserGoBackSchema = z.object({\r\n reason: z.string().optional().describe('Why going back'),\r\n});\r\n\r\nconst BrowserScreenshotSchema = z.object({\r\n fullPage: z.boolean().optional().describe('Capture full page vs viewport'),\r\n});\r\n\r\nconst BrowserGetPageStateSchema = z.object({\r\n reason: z.string().optional().describe('Why getting page state'),\r\n});\r\n\r\n// ============================================\r\n// Helper to generate interrupt IDs\r\n// ============================================\r\n\r\nlet interruptCounter = 0;\r\nfunction generateInterruptId(): string {\r\n return `browser_${Date.now()}_${++interruptCounter}`;\r\n}\r\n\r\n// ============================================\r\n// Interrupt-based Tool Implementations\r\n// ============================================\r\n\r\n/**\r\n * Navigate tool - navigates to URL and returns page state\r\n */\r\nexport function createBrowserNavigateInterruptTool(): DynamicStructuredTool<typeof BrowserNavigateSchema> {\r\n return tool<typeof BrowserNavigateSchema>(\r\n async ({ url, reason }) => {\r\n const interruptId = generateInterruptId();\r\n \r\n // This will pause the graph and wait for extension to provide result\r\n const result = interrupt<BrowserInterrupt, BrowserActionResult>({\r\n type: 'browser_interrupt',\r\n action: { type: 'navigate', url, reason },\r\n interruptId,\r\n });\r\n \r\n // When resumed, result contains actual page state\r\n if (!result.success) {\r\n return `Navigation failed: ${result.error}`;\r\n }\r\n \r\n if (result.pageState) {\r\n return `Successfully navigated to ${result.pageState.url} (${result.pageState.title})\r\n\r\n## Interactive Elements (${result.pageState.elementCount} total)\r\n${result.pageState.elementList}\r\n\r\nViewport: ${result.pageState.viewportHeight}px, Scroll: ${result.pageState.scrollPosition}/${result.pageState.scrollHeight}px`;\r\n }\r\n \r\n return `Successfully navigated to ${url}`;\r\n },\r\n {\r\n name: 'browser_navigate',\r\n description: `Navigate to a URL. Returns the page state with interactive elements after navigation completes.\r\n\r\nExample: browser_navigate({ url: \"https://www.amazon.com\" })\r\nReturns: Page title, URL, and list of interactive elements with their [index] numbers.`,\r\n schema: BrowserNavigateSchema,\r\n }\r\n );\r\n}\r\n\r\n/**\r\n * Click tool - clicks element and returns updated state\r\n */\r\nexport function createBrowserClickInterruptTool(): DynamicStructuredTool<typeof BrowserClickSchema> {\r\n return tool<typeof BrowserClickSchema>(\r\n async ({ index, coordinates, reason }) => {\r\n const interruptId = generateInterruptId();\r\n \r\n const result = interrupt<BrowserInterrupt, BrowserActionResult>({\r\n type: 'browser_interrupt',\r\n action: { type: 'click', index, coordinates, reason },\r\n interruptId,\r\n });\r\n \r\n if (!result.success) {\r\n return `Click failed: ${result.error}`;\r\n }\r\n \r\n // If click caused navigation, return new page state\r\n if (result.pageState) {\r\n return `Clicked element. Page updated:\r\n\r\nURL: ${result.pageState.url}\r\nTitle: ${result.pageState.title}\r\n\r\n## Interactive Elements (${result.pageState.elementCount} total)\r\n${result.pageState.elementList}`;\r\n }\r\n \r\n return `Successfully clicked element${index !== undefined ? ` [${index}]` : ''}`;\r\n },\r\n {\r\n name: 'browser_click',\r\n description: `Click an element by index or coordinates.\r\n\r\nUse the [index] number from the interactive elements list.\r\nExample: browser_click({ index: 5 }) to click element [5]`,\r\n schema: BrowserClickSchema,\r\n }\r\n );\r\n}\r\n\r\n/**\r\n * Type tool - types text into input field\r\n */\r\nexport function createBrowserTypeInterruptTool(): DynamicStructuredTool<typeof BrowserTypeSchema> {\r\n return tool<typeof BrowserTypeSchema>(\r\n async ({ index, text, clear, pressEnter }) => {\r\n const interruptId = generateInterruptId();\r\n \r\n const result = interrupt<BrowserInterrupt, BrowserActionResult>({\r\n type: 'browser_interrupt',\r\n action: { type: 'type', index, text, clear, pressEnter },\r\n interruptId,\r\n });\r\n \r\n if (!result.success) {\r\n return `Type failed: ${result.error}`;\r\n }\r\n \r\n // If typing + enter caused navigation (e.g., search), return new state\r\n if (result.pageState) {\r\n return `Typed \"${text}\"${pressEnter ? ' and pressed Enter' : ''}. Page updated:\r\n\r\nURL: ${result.pageState.url}\r\nTitle: ${result.pageState.title}\r\n\r\n## Interactive Elements (${result.pageState.elementCount} total)\r\n${result.pageState.elementList}`;\r\n }\r\n \r\n return `Successfully typed \"${text}\" into element [${index}]${pressEnter ? ' and pressed Enter' : ''}`;\r\n },\r\n {\r\n name: 'browser_type',\r\n description: `Type text into an input field.\r\n\r\nUse the [index] from interactive elements list. Look for <input> elements.\r\nSet pressEnter: true to submit after typing (for search fields).\r\n\r\nExample: browser_type({ index: 3, text: \"laptop 16gb ram\", pressEnter: true })`,\r\n schema: BrowserTypeSchema,\r\n }\r\n );\r\n}\r\n\r\n/**\r\n * Get page state tool - captures current page elements\r\n */\r\nexport function createBrowserGetPageStateInterruptTool(): DynamicStructuredTool<typeof BrowserGetPageStateSchema> {\r\n return tool<typeof BrowserGetPageStateSchema>(\r\n async ({ reason }) => {\r\n const interruptId = generateInterruptId();\r\n \r\n const result = interrupt<BrowserInterrupt, BrowserActionResult>({\r\n type: 'browser_interrupt',\r\n action: { type: 'get_page_state', reason },\r\n interruptId,\r\n });\r\n \r\n if (!result.success) {\r\n return `Failed to get page state: ${result.error}`;\r\n }\r\n \r\n if (result.pageState) {\r\n return `## Current Page\r\nURL: ${result.pageState.url}\r\nTitle: ${result.pageState.title}\r\n\r\n## Interactive Elements (${result.pageState.elementCount} total)\r\n${result.pageState.elementList}\r\n\r\nViewport: ${result.pageState.viewportHeight}px\r\nScroll: ${result.pageState.scrollPosition}/${result.pageState.scrollHeight}px`;\r\n }\r\n \r\n return 'Page state captured but no elements found.';\r\n },\r\n {\r\n name: 'browser_get_page_state',\r\n description: `Get the current page state with all interactive elements.\r\n\r\nReturns the list of clickable/typeable elements with their [index] numbers.\r\nUse this to see what elements are available on the current page.`,\r\n schema: BrowserGetPageStateSchema,\r\n }\r\n );\r\n}\r\n\r\n/**\r\n * Scroll tool\r\n */\r\nexport function createBrowserScrollInterruptTool(): DynamicStructuredTool<typeof BrowserScrollSchema> {\r\n return tool<typeof BrowserScrollSchema>(\r\n async ({ direction, amount }) => {\r\n const interruptId = generateInterruptId();\r\n \r\n const result = interrupt<BrowserInterrupt, BrowserActionResult>({\r\n type: 'browser_interrupt',\r\n action: { type: 'scroll', direction, amount },\r\n interruptId,\r\n });\r\n \r\n if (!result.success) {\r\n return `Scroll failed: ${result.error}`;\r\n }\r\n \r\n if (result.pageState) {\r\n return `Scrolled ${direction}. New elements visible:\r\n\r\n## Interactive Elements (${result.pageState.elementCount} total)\r\n${result.pageState.elementList}\r\n\r\nScroll: ${result.pageState.scrollPosition}/${result.pageState.scrollHeight}px`;\r\n }\r\n \r\n return `Successfully scrolled ${direction}${amount ? ` ${amount}px` : ''}`;\r\n },\r\n {\r\n name: 'browser_scroll',\r\n description: `Scroll the page in a direction.\r\n\r\nExample: browser_scroll({ direction: \"down\", amount: 500 })`,\r\n schema: BrowserScrollSchema,\r\n }\r\n );\r\n}\r\n\r\n/**\r\n * Extract tool\r\n */\r\nexport function createBrowserExtractInterruptTool(): DynamicStructuredTool<typeof BrowserExtractSchema> {\r\n return tool<typeof BrowserExtractSchema>(\r\n async ({ query, selector }) => {\r\n const interruptId = generateInterruptId();\r\n \r\n const result = interrupt<BrowserInterrupt, BrowserActionResult>({\r\n type: 'browser_interrupt',\r\n action: { type: 'extract', query, selector },\r\n interruptId,\r\n });\r\n \r\n if (!result.success) {\r\n return `Extract failed: ${result.error}`;\r\n }\r\n \r\n return result.extractedContent || 'No content extracted.';\r\n },\r\n {\r\n name: 'browser_extract',\r\n description: `Extract text content from the page.\r\n\r\nExample: browser_extract({ query: \"price\" })`,\r\n schema: BrowserExtractSchema,\r\n }\r\n );\r\n}\r\n\r\n/**\r\n * Hover tool\r\n */\r\nexport function createBrowserHoverInterruptTool(): DynamicStructuredTool<typeof BrowserHoverSchema> {\r\n return tool<typeof BrowserHoverSchema>(\r\n async ({ index }) => {\r\n const interruptId = generateInterruptId();\r\n \r\n const result = interrupt<BrowserInterrupt, BrowserActionResult>({\r\n type: 'browser_interrupt',\r\n action: { type: 'hover', index },\r\n interruptId,\r\n });\r\n \r\n if (!result.success) {\r\n return `Hover failed: ${result.error}`;\r\n }\r\n \r\n return `Successfully hovered over element [${index}]`;\r\n },\r\n {\r\n name: 'browser_hover',\r\n description: `Hover over an element to reveal tooltips or menus.`,\r\n schema: BrowserHoverSchema,\r\n }\r\n );\r\n}\r\n\r\n/**\r\n * Wait tool\r\n */\r\nexport function createBrowserWaitInterruptTool(): DynamicStructuredTool<typeof BrowserWaitSchema> {\r\n return tool<typeof BrowserWaitSchema>(\r\n async ({ duration, reason }) => {\r\n const interruptId = generateInterruptId();\r\n \r\n const result = interrupt<BrowserInterrupt, BrowserActionResult>({\r\n type: 'browser_interrupt',\r\n action: { type: 'wait', duration, reason },\r\n interruptId,\r\n });\r\n \r\n if (!result.success) {\r\n return `Wait failed: ${result.error}`;\r\n }\r\n \r\n return `Waited ${duration || 1000}ms`;\r\n },\r\n {\r\n name: 'browser_wait',\r\n description: `Wait for a duration before next action.`,\r\n schema: BrowserWaitSchema,\r\n }\r\n );\r\n}\r\n\r\n/**\r\n * Go back tool\r\n */\r\nexport function createBrowserGoBackInterruptTool(): DynamicStructuredTool<typeof BrowserGoBackSchema> {\r\n return tool<typeof BrowserGoBackSchema>(\r\n async ({ reason }) => {\r\n const interruptId = generateInterruptId();\r\n \r\n const result = interrupt<BrowserInterrupt, BrowserActionResult>({\r\n type: 'browser_interrupt',\r\n action: { type: 'back', reason },\r\n interruptId,\r\n });\r\n \r\n if (!result.success) {\r\n return `Go back failed: ${result.error}`;\r\n }\r\n \r\n if (result.pageState) {\r\n return `Went back to: ${result.pageState.url}\r\n\r\n## Interactive Elements (${result.pageState.elementCount} total)\r\n${result.pageState.elementList}`;\r\n }\r\n \r\n return 'Successfully went back';\r\n },\r\n {\r\n name: 'browser_back',\r\n description: `Go back to the previous page in history.`,\r\n schema: BrowserGoBackSchema,\r\n }\r\n );\r\n}\r\n\r\n/**\r\n * Screenshot tool\r\n */\r\nexport function createBrowserScreenshotInterruptTool(): DynamicStructuredTool<typeof BrowserScreenshotSchema> {\r\n return tool<typeof BrowserScreenshotSchema>(\r\n async ({ fullPage }) => {\r\n const interruptId = generateInterruptId();\r\n \r\n const result = interrupt<BrowserInterrupt, BrowserActionResult>({\r\n type: 'browser_interrupt',\r\n action: { type: 'screenshot', fullPage },\r\n interruptId,\r\n });\r\n \r\n if (!result.success) {\r\n return `Screenshot failed: ${result.error}`;\r\n }\r\n \r\n if (result.screenshot) {\r\n return `Screenshot captured. [Image data available]`;\r\n }\r\n \r\n return 'Screenshot captured';\r\n },\r\n {\r\n name: 'browser_screenshot',\r\n description: `Capture a screenshot of the current page.`,\r\n schema: BrowserScreenshotSchema,\r\n }\r\n );\r\n}\r\n\r\n// ============================================\r\n// Tool Collection\r\n// ============================================\r\n\r\nexport const EBrowserInterruptTools = {\r\n CLICK: 'browser_click',\r\n TYPE: 'browser_type',\r\n NAVIGATE: 'browser_navigate',\r\n SCROLL: 'browser_scroll',\r\n EXTRACT: 'browser_extract',\r\n HOVER: 'browser_hover',\r\n WAIT: 'browser_wait',\r\n BACK: 'browser_back',\r\n SCREENSHOT: 'browser_screenshot',\r\n GET_PAGE_STATE: 'browser_get_page_state',\r\n} as const;\r\n\r\nexport const BROWSER_INTERRUPT_TOOL_NAMES = Object.values(EBrowserInterruptTools);\r\n\r\nexport type BrowserInterruptToolName = typeof BROWSER_INTERRUPT_TOOL_NAMES[number];\r\n\r\nexport function isBrowserInterruptToolCall(toolName: string): toolName is BrowserInterruptToolName {\r\n return BROWSER_INTERRUPT_TOOL_NAMES.includes(toolName as BrowserInterruptToolName);\r\n}\r\n\r\n/**\r\n * Create all interrupt-based browser tools\r\n * \r\n * Use these when the client is a browser extension that can:\r\n * 1. Detect browser_interrupt events in the stream\r\n * 2. Execute browser actions locally\r\n * 3. Send Command({ resume: result }) to continue the graph\r\n */\r\nexport function createBrowserInterruptTools(): DynamicStructuredTool[] {\r\n return [\r\n createBrowserNavigateInterruptTool(),\r\n createBrowserClickInterruptTool(),\r\n createBrowserTypeInterruptTool(),\r\n createBrowserGetPageStateInterruptTool(),\r\n createBrowserScrollInterruptTool(),\r\n createBrowserExtractInterruptTool(),\r\n createBrowserHoverInterruptTool(),\r\n createBrowserWaitInterruptTool(),\r\n createBrowserGoBackInterruptTool(),\r\n createBrowserScreenshotInterruptTool(),\r\n ];\r\n}\r\n\r\n/**\r\n * Check if an interrupt is a browser interrupt\r\n */\r\nexport function isBrowserInterrupt(value: unknown): value is BrowserInterrupt {\r\n return (\r\n typeof value === 'object' &&\r\n value !== null &&\r\n (value as BrowserInterrupt).type === 'browser_interrupt'\r\n );\r\n}\r\n"],"names":["z","tool","interrupt"],"mappings":"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"}
@@ -68,6 +68,9 @@ const BrowserScreenshotSchema = zod.z.object({
68
68
  fullPage: zod.z.boolean().optional().describe('Whether to capture the full page or just the viewport (default: viewport only)'),
69
69
  reason: zod.z.string().optional().describe('Why you need a screenshot (e.g., "to identify visual elements", "to analyze page layout")'),
70
70
  });
71
+ const BrowserGetPageStateSchema = zod.z.object({
72
+ reason: zod.z.string().optional().describe('Why you need fresh page state (e.g., "after navigation", "to see updated elements")'),
73
+ });
71
74
  // ============================================
72
75
  // Tool Implementations
73
76
  // ============================================
@@ -180,8 +183,18 @@ Use this tool when you need to:
180
183
  - Navigate to a different page
181
184
  - Open a new URL
182
185
 
186
+ **IMPORTANT**: After calling browser_navigate, you MUST call browser_get_page_state
187
+ before using browser_click or browser_type. This is because navigation changes the page,
188
+ and you need to see the new page's elements before you can interact with them.
189
+
183
190
  Provide the full URL including the protocol (https://).
184
191
 
192
+ **Correct workflow**:
193
+ 1. browser_navigate({ url: "https://www.amazon.com" })
194
+ 2. browser_get_page_state({ reason: "see elements on Amazon" })
195
+ 3. Now find the search input's [index] in the returned state
196
+ 4. browser_type({ index: <search_input_index>, text: "query", pressEnter: true })
197
+
185
198
  Example: browser_navigate({ url: "https://www.google.com" })`,
186
199
  schema: BrowserNavigateSchema,
187
200
  });
@@ -357,6 +370,48 @@ Example: browser_screenshot({ fullPage: false })`,
357
370
  schema: BrowserScreenshotSchema,
358
371
  });
359
372
  }
373
+ /**
374
+ * Browser get page state tool - gets fresh page context after navigation or actions
375
+ * CRITICAL: Use this after browser_navigate or any action that changes the page
376
+ */
377
+ function createBrowserGetPageStateTool() {
378
+ return tools.tool(async ({ reason }) => {
379
+ return JSON.stringify({
380
+ type: 'browser_action',
381
+ action: {
382
+ type: 'get_page_state',
383
+ reason,
384
+ },
385
+ requiresBrowserExecution: true,
386
+ // Special flag: extension should inject fresh context into the conversation
387
+ requiresContextRefresh: true,
388
+ // IMPORTANT: Tell the agent to wait
389
+ message: 'Page state is being captured by the browser extension. The element list will be provided in the next message. DO NOT proceed with click or type actions until you receive the actual element list.',
390
+ });
391
+ }, {
392
+ name: EBrowserTools.GET_PAGE_STATE,
393
+ description: `Get fresh page state showing current interactive elements.
394
+
395
+ **CRITICAL WORKFLOW**: After calling this tool, you MUST STOP and WAIT. The browser extension will capture the page state and return the element list. DO NOT plan any browser_click or browser_type actions in the same response - you don't have the element indices yet!
396
+
397
+ **When to use**:
398
+ - After browser_navigate (to see elements on the new page)
399
+ - After browser_click (if it caused navigation or page changes)
400
+ - Any time you need to see what elements are currently on the page
401
+
402
+ **IMPORTANT**: This tool captures the page state asynchronously. The actual element list will be provided AFTER this tool completes. You should:
403
+ 1. Call this tool
404
+ 2. STOP and wait for the response with the element list
405
+ 3. In your NEXT response, use the element indices for click/type actions
406
+
407
+ Example workflow:
408
+ - Turn 1: browser_navigate to amazon.com, then browser_get_page_state
409
+ - Turn 2: (After receiving element list) browser_type with the correct search input index
410
+
411
+ Example: browser_get_page_state({ reason: "to see elements after navigation" })`,
412
+ schema: BrowserGetPageStateSchema,
413
+ });
414
+ }
360
415
  /**
361
416
  * Create all browser automation tools
362
417
  *
@@ -381,7 +436,7 @@ Example: browser_screenshot({ fullPage: false })`,
381
436
  function createBrowserTools(config = {}) {
382
437
  const tools = [];
383
438
  // Enable all by default
384
- const { enableClick = true, enableType = true, enableNavigate = true, enableScroll = true, enableExtract = true, enableHover = true, enableWait = true, enableBack = true, enableScreenshot = true, } = config;
439
+ const { enableClick = true, enableType = true, enableNavigate = true, enableScroll = true, enableExtract = true, enableHover = true, enableWait = true, enableBack = true, enableScreenshot = true, enableGetPageState = true, } = config;
385
440
  if (enableClick)
386
441
  tools.push(createBrowserClickTool());
387
442
  if (enableType)
@@ -400,6 +455,8 @@ function createBrowserTools(config = {}) {
400
455
  tools.push(createBrowserGoBackTool());
401
456
  if (enableScreenshot)
402
457
  tools.push(createBrowserScreenshotTool());
458
+ if (enableGetPageState)
459
+ tools.push(createBrowserGetPageStateTool());
403
460
  return tools;
404
461
  }
405
462
  /**
@@ -416,6 +473,7 @@ const EBrowserTools = {
416
473
  WAIT: 'browser_wait',
417
474
  BACK: 'browser_back',
418
475
  SCREENSHOT: 'browser_screenshot',
476
+ GET_PAGE_STATE: 'browser_get_page_state',
419
477
  };
420
478
  /**
421
479
  * Get browser tool names for filtering/identification
@@ -430,6 +488,7 @@ const BROWSER_TOOL_NAMES = [
430
488
  EBrowserTools.WAIT,
431
489
  EBrowserTools.BACK,
432
490
  EBrowserTools.SCREENSHOT,
491
+ EBrowserTools.GET_PAGE_STATE,
433
492
  ];
434
493
  /**
435
494
  * Check if a tool call is a browser action
@@ -460,6 +519,7 @@ exports.BROWSER_TOOL_NAMES = BROWSER_TOOL_NAMES;
460
519
  exports.EBrowserTools = EBrowserTools;
461
520
  exports.createBrowserClickTool = createBrowserClickTool;
462
521
  exports.createBrowserExtractTool = createBrowserExtractTool;
522
+ exports.createBrowserGetPageStateTool = createBrowserGetPageStateTool;
463
523
  exports.createBrowserGoBackTool = createBrowserGoBackTool;
464
524
  exports.createBrowserHoverTool = createBrowserHoverTool;
465
525
  exports.createBrowserNavigateTool = createBrowserNavigateTool;