browser-use 0.2.0 → 0.3.0

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.
Files changed (259) hide show
  1. package/README.md +295 -686
  2. package/dist/actor/element.d.ts +19 -0
  3. package/dist/actor/element.js +46 -0
  4. package/dist/actor/index.d.ts +4 -0
  5. package/dist/actor/index.js +4 -0
  6. package/dist/actor/mouse.d.ts +19 -0
  7. package/dist/actor/mouse.js +39 -0
  8. package/dist/actor/page.d.ts +29 -0
  9. package/dist/actor/page.js +88 -0
  10. package/dist/actor/utils.d.ts +4 -0
  11. package/dist/actor/utils.js +35 -0
  12. package/dist/agent/cloud-events.d.ts +18 -0
  13. package/dist/agent/cloud-events.js +65 -2
  14. package/dist/agent/gif.d.ts +1 -0
  15. package/dist/agent/gif.js +24 -2
  16. package/dist/agent/judge.d.ts +17 -0
  17. package/dist/agent/judge.js +197 -0
  18. package/dist/agent/message-manager/service.d.ts +12 -4
  19. package/dist/agent/message-manager/service.js +205 -39
  20. package/dist/agent/message-manager/utils.js +0 -1
  21. package/dist/agent/message-manager/views.d.ts +4 -0
  22. package/dist/agent/message-manager/views.js +11 -7
  23. package/dist/agent/prompts.d.ts +24 -3
  24. package/dist/agent/prompts.js +274 -59
  25. package/dist/agent/service.d.ts +99 -41
  26. package/dist/agent/service.js +2266 -472
  27. package/dist/agent/variable-detector.d.ts +12 -0
  28. package/dist/agent/variable-detector.js +211 -0
  29. package/dist/agent/views.d.ts +237 -18
  30. package/dist/agent/views.js +446 -33
  31. package/dist/browser/cloud/cloud.d.ts +20 -0
  32. package/dist/browser/cloud/cloud.js +129 -0
  33. package/dist/browser/cloud/index.d.ts +2 -0
  34. package/dist/browser/cloud/index.js +2 -0
  35. package/dist/browser/cloud/views.d.ts +41 -0
  36. package/dist/browser/cloud/views.js +35 -0
  37. package/dist/browser/events.d.ts +345 -0
  38. package/dist/browser/events.js +566 -0
  39. package/dist/browser/extensions.js +17 -17
  40. package/dist/browser/index.d.ts +4 -0
  41. package/dist/browser/index.js +4 -0
  42. package/dist/browser/profile.d.ts +8 -2
  43. package/dist/browser/profile.js +79 -12
  44. package/dist/browser/session-manager.d.ts +85 -0
  45. package/dist/browser/session-manager.js +208 -0
  46. package/dist/browser/session.d.ts +100 -8
  47. package/dist/browser/session.js +1097 -58
  48. package/dist/browser/types.d.ts +0 -2
  49. package/dist/browser/views.d.ts +39 -0
  50. package/dist/browser/views.js +32 -0
  51. package/dist/browser/watchdogs/aboutblank-watchdog.d.ts +12 -0
  52. package/dist/browser/watchdogs/aboutblank-watchdog.js +131 -0
  53. package/dist/browser/watchdogs/base.d.ts +21 -0
  54. package/dist/browser/watchdogs/base.js +81 -0
  55. package/dist/browser/watchdogs/cdp-session-watchdog.d.ts +14 -0
  56. package/dist/browser/watchdogs/cdp-session-watchdog.js +177 -0
  57. package/dist/browser/watchdogs/crash-watchdog.d.ts +38 -0
  58. package/dist/browser/watchdogs/crash-watchdog.js +296 -0
  59. package/dist/browser/watchdogs/default-action-watchdog.d.ts +49 -0
  60. package/dist/browser/watchdogs/default-action-watchdog.js +212 -0
  61. package/dist/browser/watchdogs/dom-watchdog.d.ts +8 -0
  62. package/dist/browser/watchdogs/dom-watchdog.js +31 -0
  63. package/dist/browser/watchdogs/downloads-watchdog.d.ts +77 -0
  64. package/dist/browser/watchdogs/downloads-watchdog.js +409 -0
  65. package/dist/browser/watchdogs/har-recording-watchdog.d.ts +19 -0
  66. package/dist/browser/watchdogs/har-recording-watchdog.js +317 -0
  67. package/dist/browser/watchdogs/index.d.ts +15 -0
  68. package/dist/browser/watchdogs/index.js +15 -0
  69. package/dist/browser/watchdogs/local-browser-watchdog.d.ts +10 -0
  70. package/dist/browser/watchdogs/local-browser-watchdog.js +32 -0
  71. package/dist/browser/watchdogs/permissions-watchdog.d.ts +8 -0
  72. package/dist/browser/watchdogs/permissions-watchdog.js +73 -0
  73. package/dist/browser/watchdogs/popups-watchdog.d.ts +13 -0
  74. package/dist/browser/watchdogs/popups-watchdog.js +77 -0
  75. package/dist/browser/watchdogs/recording-watchdog.d.ts +27 -0
  76. package/dist/browser/watchdogs/recording-watchdog.js +249 -0
  77. package/dist/browser/watchdogs/screenshot-watchdog.d.ts +6 -0
  78. package/dist/browser/watchdogs/screenshot-watchdog.js +13 -0
  79. package/dist/browser/watchdogs/security-watchdog.d.ts +10 -0
  80. package/dist/browser/watchdogs/security-watchdog.js +84 -0
  81. package/dist/browser/watchdogs/storage-state-watchdog.d.ts +24 -0
  82. package/dist/browser/watchdogs/storage-state-watchdog.js +288 -0
  83. package/dist/cli.d.ts +7 -2
  84. package/dist/cli.js +182 -25
  85. package/dist/code-use/formatting.d.ts +3 -0
  86. package/dist/code-use/formatting.js +18 -0
  87. package/dist/code-use/index.d.ts +6 -0
  88. package/dist/code-use/index.js +6 -0
  89. package/dist/code-use/namespace.d.ts +5 -0
  90. package/dist/code-use/namespace.js +81 -0
  91. package/dist/code-use/notebook-export.d.ts +3 -0
  92. package/dist/code-use/notebook-export.js +56 -0
  93. package/dist/code-use/service.d.ts +24 -0
  94. package/dist/code-use/service.js +104 -0
  95. package/dist/code-use/utils.d.ts +4 -0
  96. package/dist/code-use/utils.js +98 -0
  97. package/dist/code-use/views.d.ts +108 -0
  98. package/dist/code-use/views.js +165 -0
  99. package/dist/config.d.ts +13 -0
  100. package/dist/config.js +69 -3
  101. package/dist/controller/registry/service.d.ts +10 -1
  102. package/dist/controller/registry/service.js +266 -10
  103. package/dist/controller/registry/views.d.ts +4 -1
  104. package/dist/controller/registry/views.js +25 -2
  105. package/dist/controller/service.d.ts +10 -1
  106. package/dist/controller/service.js +1807 -268
  107. package/dist/controller/views.d.ts +78 -155
  108. package/dist/controller/views.js +61 -12
  109. package/dist/dom/history-tree-processor/service.d.ts +5 -0
  110. package/dist/dom/history-tree-processor/service.js +169 -14
  111. package/dist/dom/history-tree-processor/view.d.ts +7 -1
  112. package/dist/dom/history-tree-processor/view.js +10 -1
  113. package/dist/dom/markdown-extractor.d.ts +37 -0
  114. package/dist/dom/markdown-extractor.js +345 -0
  115. package/dist/dom/service.d.ts +3 -1
  116. package/dist/dom/service.js +76 -0
  117. package/dist/dom/views.d.ts +1 -0
  118. package/dist/dom/views.js +45 -0
  119. package/dist/event-bus.d.ts +107 -7
  120. package/dist/event-bus.js +313 -10
  121. package/dist/exceptions.d.ts +0 -3
  122. package/dist/exceptions.js +0 -7
  123. package/dist/filesystem/file-system.d.ts +18 -0
  124. package/dist/filesystem/file-system.js +503 -42
  125. package/dist/index.d.ts +7 -0
  126. package/dist/index.js +6 -0
  127. package/dist/integrations/gmail/actions.d.ts +3 -3
  128. package/dist/integrations/gmail/actions.js +4 -4
  129. package/dist/llm/anthropic/chat.d.ts +18 -1
  130. package/dist/llm/anthropic/chat.js +123 -55
  131. package/dist/llm/anthropic/serializer.d.ts +2 -0
  132. package/dist/llm/anthropic/serializer.js +81 -9
  133. package/dist/llm/aws/chat-anthropic.d.ts +17 -0
  134. package/dist/llm/aws/chat-anthropic.js +126 -26
  135. package/dist/llm/aws/chat-bedrock.d.ts +28 -1
  136. package/dist/llm/aws/chat-bedrock.js +161 -34
  137. package/dist/llm/aws/serializer.d.ts +13 -1
  138. package/dist/llm/aws/serializer.js +56 -17
  139. package/dist/llm/azure/chat.d.ts +53 -2
  140. package/dist/llm/azure/chat.js +366 -54
  141. package/dist/llm/base.d.ts +2 -0
  142. package/dist/llm/browser-use/chat.d.ts +40 -0
  143. package/dist/llm/browser-use/chat.js +305 -0
  144. package/dist/llm/browser-use/index.d.ts +1 -0
  145. package/dist/llm/browser-use/index.js +1 -0
  146. package/dist/llm/cerebras/chat.d.ts +39 -0
  147. package/dist/llm/cerebras/chat.js +178 -0
  148. package/dist/llm/cerebras/index.d.ts +2 -0
  149. package/dist/llm/cerebras/index.js +2 -0
  150. package/dist/llm/cerebras/serializer.d.ts +7 -0
  151. package/dist/llm/cerebras/serializer.js +82 -0
  152. package/dist/llm/deepseek/chat.d.ts +19 -2
  153. package/dist/llm/deepseek/chat.js +138 -25
  154. package/dist/llm/google/chat.d.ts +46 -2
  155. package/dist/llm/google/chat.js +267 -64
  156. package/dist/llm/google/serializer.d.ts +9 -1
  157. package/dist/llm/google/serializer.js +141 -34
  158. package/dist/llm/groq/chat.d.ts +21 -2
  159. package/dist/llm/groq/chat.js +125 -26
  160. package/dist/llm/groq/parser.js +3 -1
  161. package/dist/llm/mistral/chat.d.ts +43 -0
  162. package/dist/llm/mistral/chat.js +154 -0
  163. package/dist/llm/mistral/index.d.ts +2 -0
  164. package/dist/llm/mistral/index.js +2 -0
  165. package/dist/llm/mistral/schema.d.ts +8 -0
  166. package/dist/llm/mistral/schema.js +27 -0
  167. package/dist/llm/models.d.ts +2 -0
  168. package/dist/llm/models.js +317 -0
  169. package/dist/llm/ollama/chat.d.ts +13 -1
  170. package/dist/llm/ollama/chat.js +110 -19
  171. package/dist/llm/ollama/serializer.d.ts +1 -0
  172. package/dist/llm/ollama/serializer.js +34 -12
  173. package/dist/llm/openai/chat.d.ts +16 -0
  174. package/dist/llm/openai/chat.js +94 -44
  175. package/dist/llm/openai/like.d.ts +5 -3
  176. package/dist/llm/openai/like.js +7 -3
  177. package/dist/llm/openai/responses-serializer.d.ts +18 -0
  178. package/dist/llm/openai/responses-serializer.js +72 -0
  179. package/dist/llm/openrouter/chat.d.ts +28 -2
  180. package/dist/llm/openrouter/chat.js +115 -29
  181. package/dist/llm/schema.d.ts +11 -1
  182. package/dist/llm/schema.js +81 -1
  183. package/dist/llm/vercel/chat.d.ts +50 -0
  184. package/dist/llm/vercel/chat.js +276 -0
  185. package/dist/llm/vercel/index.d.ts +1 -0
  186. package/dist/llm/vercel/index.js +1 -0
  187. package/dist/llm/vercel/serializer.d.ts +5 -0
  188. package/dist/llm/vercel/serializer.js +7 -0
  189. package/dist/llm/views.d.ts +2 -1
  190. package/dist/llm/views.js +3 -1
  191. package/dist/logging-config.d.ts +2 -0
  192. package/dist/logging-config.js +82 -29
  193. package/dist/mcp/client.d.ts +10 -5
  194. package/dist/mcp/client.js +14 -9
  195. package/dist/mcp/controller.d.ts +42 -3
  196. package/dist/mcp/controller.js +56 -31
  197. package/dist/mcp/server.d.ts +14 -0
  198. package/dist/mcp/server.js +255 -52
  199. package/dist/observability.js +10 -4
  200. package/dist/sandbox/index.d.ts +2 -0
  201. package/dist/sandbox/index.js +2 -0
  202. package/dist/sandbox/sandbox.d.ts +19 -0
  203. package/dist/sandbox/sandbox.js +140 -0
  204. package/dist/sandbox/views.d.ts +67 -0
  205. package/dist/sandbox/views.js +121 -0
  206. package/dist/skill-cli/index.d.ts +3 -0
  207. package/dist/skill-cli/index.js +3 -0
  208. package/dist/skill-cli/protocol.d.ts +30 -0
  209. package/dist/skill-cli/protocol.js +48 -0
  210. package/dist/skill-cli/server.d.ts +11 -0
  211. package/dist/skill-cli/server.js +85 -0
  212. package/dist/skill-cli/sessions.d.ts +24 -0
  213. package/dist/skill-cli/sessions.js +47 -0
  214. package/dist/skills/index.d.ts +3 -0
  215. package/dist/skills/index.js +3 -0
  216. package/dist/skills/service.d.ts +27 -0
  217. package/dist/skills/service.js +266 -0
  218. package/dist/skills/utils.d.ts +6 -0
  219. package/dist/skills/utils.js +53 -0
  220. package/dist/skills/views.d.ts +40 -0
  221. package/dist/skills/views.js +10 -0
  222. package/dist/sync/auth.js +8 -3
  223. package/dist/sync/service.d.ts +6 -6
  224. package/dist/sync/service.js +54 -89
  225. package/dist/telemetry/views.d.ts +20 -6
  226. package/dist/telemetry/views.js +23 -5
  227. package/dist/tokens/custom-pricing.d.ts +2 -0
  228. package/dist/tokens/custom-pricing.js +22 -0
  229. package/dist/tokens/index.d.ts +2 -0
  230. package/dist/tokens/index.js +2 -0
  231. package/dist/tokens/mappings.d.ts +1 -0
  232. package/dist/tokens/mappings.js +3 -0
  233. package/dist/tokens/service.js +27 -8
  234. package/dist/tools/extraction/index.d.ts +2 -0
  235. package/dist/tools/extraction/index.js +2 -0
  236. package/dist/tools/extraction/schema-utils.d.ts +6 -0
  237. package/dist/tools/extraction/schema-utils.js +237 -0
  238. package/dist/tools/extraction/views.d.ts +7 -0
  239. package/dist/tools/index.d.ts +5 -0
  240. package/dist/tools/index.js +5 -0
  241. package/dist/tools/registry/index.d.ts +2 -0
  242. package/dist/tools/registry/index.js +2 -0
  243. package/dist/tools/registry/service.d.ts +1 -0
  244. package/dist/tools/registry/service.js +1 -0
  245. package/dist/tools/registry/views.d.ts +1 -0
  246. package/dist/tools/registry/views.js +1 -0
  247. package/dist/tools/service.d.ts +2 -0
  248. package/dist/tools/service.js +1 -0
  249. package/dist/tools/utils.d.ts +2 -0
  250. package/dist/tools/utils.js +57 -0
  251. package/dist/tools/views.d.ts +1 -0
  252. package/dist/tools/views.js +1 -0
  253. package/dist/utils.d.ts +10 -1
  254. package/dist/utils.js +70 -3
  255. package/package.json +87 -26
  256. package/dist/dom/playground/process-dom.js +0 -5
  257. package/dist/dom/playground/test-accessibility.d.ts +0 -44
  258. package/dist/dom/playground/test-accessibility.js +0 -111
  259. /package/dist/{dom/playground/process-dom.d.ts → tools/extraction/views.js} +0 -0
@@ -10,7 +10,12 @@ export interface SensitiveDataMap {
10
10
  export interface ExecuteActionContext<Context> {
11
11
  context?: Context;
12
12
  browser_session?: BrowserSession | null;
13
+ browser?: BrowserSession | null;
14
+ browser_context?: BrowserSession | null;
15
+ page_url?: string | null;
16
+ cdp_client?: unknown;
13
17
  page_extraction_llm?: BaseChatModel | null;
18
+ extraction_schema?: Record<string, unknown> | null;
14
19
  file_system?: FileSystem | null;
15
20
  available_file_paths?: string[] | null;
16
21
  sensitive_data?: SensitiveDataMap | null;
@@ -22,16 +27,20 @@ export type RegistryActionHandler<Params = any, Context = unknown> = (params: Pa
22
27
  }) => Promise<unknown> | unknown;
23
28
  export interface ActionOptions {
24
29
  param_model?: ZodTypeAny;
30
+ action_name?: string;
25
31
  domains?: string[] | null;
26
32
  allowed_domains?: string[] | null;
27
33
  page_filter?: ((page: Page) => boolean) | null;
34
+ terminates_sequence?: boolean;
28
35
  }
29
36
  export declare class Registry<Context = unknown> {
30
37
  private registry;
31
38
  private excludeActions;
32
39
  constructor(exclude_actions?: string[] | null);
33
- action(description: string, options?: ActionOptions): <Params = any>(handler: RegistryActionHandler<Params, Context>) => RegistryActionHandler<Params, Context>;
40
+ action(description: string, options?: ActionOptions): <Params = any>(handler: RegistryActionHandler<Params, Context>) => any;
34
41
  get_action(action_name: string): RegisteredAction | null;
42
+ exclude_action(action_name: string): void;
43
+ remove_action(action_name: string): void;
35
44
  get_all_actions(): Map<string, RegisteredAction>;
36
45
  execute_action: (...args: any[]) => any;
37
46
  private replace_sensitive_data;
@@ -1,10 +1,145 @@
1
1
  import { z } from 'zod';
2
+ import { createHmac } from 'node:crypto';
2
3
  import { createLogger } from '../../logging-config.js';
3
4
  import { observe_debug } from '../../observability.js';
4
5
  import { time_execution_async } from '../../utils.js';
5
6
  import { is_new_tab_page, match_url_with_domain_pattern } from '../../utils.js';
7
+ import { BrowserError } from '../../browser/views.js';
6
8
  import { ActionModel, ActionRegistry, RegisteredAction } from './views.js';
7
9
  const logger = createLogger('browser_use.controller.registry');
10
+ const SPECIAL_PARAM_NAMES = new Set([
11
+ 'context',
12
+ 'browser_session',
13
+ 'browser',
14
+ 'browser_context',
15
+ 'page',
16
+ 'page_url',
17
+ 'cdp_client',
18
+ 'page_extraction_llm',
19
+ 'available_file_paths',
20
+ 'has_sensitive_data',
21
+ 'file_system',
22
+ 'extraction_schema',
23
+ 'sensitive_data',
24
+ 'signal',
25
+ ]);
26
+ const splitTopLevelParameters = (paramsSource) => {
27
+ const segments = [];
28
+ let current = '';
29
+ let depthParen = 0;
30
+ let depthBrace = 0;
31
+ let depthBracket = 0;
32
+ let quote = null;
33
+ let escaped = false;
34
+ const flush = () => {
35
+ const trimmed = current.trim();
36
+ if (trimmed) {
37
+ segments.push(trimmed);
38
+ }
39
+ current = '';
40
+ };
41
+ for (const char of paramsSource) {
42
+ if (escaped) {
43
+ current += char;
44
+ escaped = false;
45
+ continue;
46
+ }
47
+ if (char === '\\') {
48
+ current += char;
49
+ escaped = true;
50
+ continue;
51
+ }
52
+ if (quote) {
53
+ current += char;
54
+ if (char === quote) {
55
+ quote = null;
56
+ }
57
+ continue;
58
+ }
59
+ if (char === "'" || char === '"' || char === '`') {
60
+ current += char;
61
+ quote = char;
62
+ continue;
63
+ }
64
+ if (char === '(')
65
+ depthParen += 1;
66
+ else if (char === ')')
67
+ depthParen -= 1;
68
+ else if (char === '{')
69
+ depthBrace += 1;
70
+ else if (char === '}')
71
+ depthBrace -= 1;
72
+ else if (char === '[')
73
+ depthBracket += 1;
74
+ else if (char === ']')
75
+ depthBracket -= 1;
76
+ if (char === ',' &&
77
+ depthParen === 0 &&
78
+ depthBrace === 0 &&
79
+ depthBracket === 0) {
80
+ flush();
81
+ continue;
82
+ }
83
+ current += char;
84
+ }
85
+ flush();
86
+ return segments;
87
+ };
88
+ const extractFunctionParameters = (fn) => {
89
+ const source = fn.toString().trim();
90
+ if (!source) {
91
+ return null;
92
+ }
93
+ let paramsSource;
94
+ const arrowIndex = source.indexOf('=>');
95
+ if (arrowIndex !== -1) {
96
+ let lhs = source.slice(0, arrowIndex).trim();
97
+ if (lhs.startsWith('async ')) {
98
+ lhs = lhs.slice('async '.length).trim();
99
+ }
100
+ if (lhs.startsWith('(') && lhs.endsWith(')')) {
101
+ paramsSource = lhs.slice(1, -1);
102
+ }
103
+ else {
104
+ paramsSource = lhs;
105
+ }
106
+ }
107
+ else {
108
+ const openIndex = source.indexOf('(');
109
+ const closeIndex = source.indexOf(')', openIndex + 1);
110
+ if (openIndex === -1 || closeIndex === -1) {
111
+ return null;
112
+ }
113
+ paramsSource = source.slice(openIndex + 1, closeIndex);
114
+ }
115
+ const tokens = splitTopLevelParameters(paramsSource);
116
+ if (!tokens.length) {
117
+ return [];
118
+ }
119
+ const parsed = [];
120
+ for (const token of tokens) {
121
+ if (token.startsWith('...')) {
122
+ const fnName = fn.name || '<anonymous>';
123
+ throw new Error(`Action '${fnName}' has ${token} which is not allowed. Actions must have explicit positional parameters only.`);
124
+ }
125
+ if (token.includes('{') ||
126
+ token.includes('}') ||
127
+ token.includes('[') ||
128
+ token.includes(']')) {
129
+ return null;
130
+ }
131
+ const eqIndex = token.indexOf('=');
132
+ const name = (eqIndex === -1 ? token : token.slice(0, eqIndex)).trim();
133
+ if (!name) {
134
+ return null;
135
+ }
136
+ parsed.push({
137
+ name,
138
+ hasDefault: eqIndex !== -1,
139
+ });
140
+ }
141
+ return parsed;
142
+ };
8
143
  const isAbortError = (error) => error instanceof Error && error.name === 'AbortError';
9
144
  const createAbortError = (reason) => {
10
145
  if (isAbortError(reason)) {
@@ -19,6 +154,9 @@ const createAbortError = (reason) => {
19
154
  return error;
20
155
  };
21
156
  const wrapActionExecutionError = (actionName, error) => {
157
+ if (error instanceof BrowserError) {
158
+ return error;
159
+ }
22
160
  const message = error instanceof Error ? error.message : String(error);
23
161
  const wrapped = new Error(`Error executing action ${actionName}: ${message}`);
24
162
  if (error !== undefined) {
@@ -26,6 +164,61 @@ const wrapActionExecutionError = (actionName, error) => {
26
164
  }
27
165
  return wrapped;
28
166
  };
167
+ const isSpecialContextMissingError = (error) => {
168
+ if (!(error instanceof Error)) {
169
+ return false;
170
+ }
171
+ return (error.message.includes('requires browser_session but none provided') ||
172
+ error.message.includes('requires page_extraction_llm but none provided'));
173
+ };
174
+ const safeJsonStringify = (value) => {
175
+ try {
176
+ return JSON.stringify(value);
177
+ }
178
+ catch {
179
+ return String(value);
180
+ }
181
+ };
182
+ const isTimeoutError = (error) => error instanceof Error && error.name === 'TimeoutError';
183
+ const BASE32_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
184
+ const decodeBase32Secret = (secret) => {
185
+ const sanitized = secret.toUpperCase().replace(/[^A-Z2-7]/g, '');
186
+ if (!sanitized.length) {
187
+ throw new Error('Invalid TOTP secret: empty base32 payload');
188
+ }
189
+ let bits = 0;
190
+ let bitBuffer = 0;
191
+ const bytes = [];
192
+ for (const char of sanitized) {
193
+ const value = BASE32_ALPHABET.indexOf(char);
194
+ if (value < 0) {
195
+ throw new Error(`Invalid base32 character in TOTP secret: ${char}`);
196
+ }
197
+ bitBuffer = (bitBuffer << 5) | value;
198
+ bits += 5;
199
+ while (bits >= 8) {
200
+ bytes.push((bitBuffer >>> (bits - 8)) & 0xff);
201
+ bits -= 8;
202
+ }
203
+ }
204
+ if (!bytes.length) {
205
+ throw new Error('Invalid TOTP secret: failed to decode base32 payload');
206
+ }
207
+ return Buffer.from(bytes);
208
+ };
209
+ const generateTotpCode = (secret) => {
210
+ const key = decodeBase32Secret(secret);
211
+ const counter = Math.floor(Date.now() / 1000 / 30);
212
+ const counterBuffer = Buffer.alloc(8);
213
+ counterBuffer.writeBigUInt64BE(BigInt(counter));
214
+ const hmac = createHmac('sha1', key).update(counterBuffer).digest();
215
+ const offset = hmac[hmac.length - 1] & 0x0f;
216
+ const binaryCode = ((hmac[offset] & 0x7f) << 24) |
217
+ ((hmac[offset + 1] & 0xff) << 16) |
218
+ ((hmac[offset + 2] & 0xff) << 8) |
219
+ (hmac[offset + 3] & 0xff);
220
+ return String(binaryCode % 1_000_000).padStart(6, '0');
221
+ };
29
222
  export class Registry {
30
223
  registry = new ActionRegistry();
31
224
  excludeActions;
@@ -33,21 +226,68 @@ export class Registry {
33
226
  this.excludeActions = new Set(exclude_actions ?? []);
34
227
  }
35
228
  action(description, options = {}) {
36
- const schema = options.param_model ?? z.object({}).strict();
229
+ if (options.allowed_domains && options.domains) {
230
+ throw new Error("Cannot specify both 'domains' and 'allowed_domains' - they are aliases for the same parameter");
231
+ }
232
+ let schema = options.param_model ?? z.object({}).strict();
233
+ const actionNameOverride = options.action_name ?? null;
37
234
  const domains = options.allowed_domains ?? options.domains ?? null;
38
235
  const pageFilter = options.page_filter ?? null;
236
+ const terminatesSequence = options.terminates_sequence ?? false;
39
237
  return (handler) => {
40
- if (this.excludeActions.has(handler.name)) {
238
+ const actionName = actionNameOverride ?? handler.name;
239
+ if (this.excludeActions.has(actionName)) {
41
240
  return handler;
42
241
  }
43
- const action = new RegisteredAction(handler.name, description, handler, schema, domains, pageFilter);
242
+ const parsedHandlerParams = extractFunctionParameters(handler);
243
+ let normalizedHandler = handler;
244
+ if (!options.param_model) {
245
+ const supportsCompatSignature = Boolean(parsedHandlerParams &&
246
+ parsedHandlerParams.length > 0 &&
247
+ !(parsedHandlerParams.length <= 2 &&
248
+ parsedHandlerParams[0]?.name === 'params'));
249
+ if (supportsCompatSignature && parsedHandlerParams) {
250
+ const actionParams = parsedHandlerParams.filter((entry) => !SPECIAL_PARAM_NAMES.has(entry.name));
251
+ const shape = Object.fromEntries(actionParams.map((entry) => [
252
+ entry.name,
253
+ entry.hasDefault ? z.any().optional() : z.any(),
254
+ ]));
255
+ schema = z.object(shape).strict();
256
+ normalizedHandler = ((params, ctx) => {
257
+ const args = parsedHandlerParams.map((entry) => {
258
+ if (SPECIAL_PARAM_NAMES.has(entry.name)) {
259
+ const value = ctx[entry.name];
260
+ if ((value === null || value === undefined) &&
261
+ !entry.hasDefault) {
262
+ throw new Error(`Action ${actionName} requires ${entry.name} but none provided.`);
263
+ }
264
+ return value;
265
+ }
266
+ const value = params[entry.name];
267
+ if (value === undefined && !entry.hasDefault) {
268
+ throw new Error(`${actionName}() missing required parameter '${entry.name}'`);
269
+ }
270
+ return value;
271
+ });
272
+ return handler(...args);
273
+ });
274
+ }
275
+ }
276
+ const action = new RegisteredAction(actionName, description, normalizedHandler, schema, domains, pageFilter, terminatesSequence);
44
277
  this.registry.register(action);
45
- return handler;
278
+ return normalizedHandler;
46
279
  };
47
280
  }
48
281
  get_action(action_name) {
49
282
  return this.registry.get(action_name);
50
283
  }
284
+ exclude_action(action_name) {
285
+ this.excludeActions.add(action_name);
286
+ this.registry.remove(action_name);
287
+ }
288
+ remove_action(action_name) {
289
+ this.registry.remove(action_name);
290
+ }
51
291
  get_all_actions() {
52
292
  return this.registry.actionsMap;
53
293
  }
@@ -55,14 +295,14 @@ export class Registry {
55
295
  name: 'execute_action',
56
296
  ignore_input: true,
57
297
  ignore_output: true,
58
- })(time_execution_async('--execute_action')(async (action_name, params, { browser_session = null, page_extraction_llm = null, file_system = null, sensitive_data = null, available_file_paths = null, signal = null, context = null, } = {}) => {
298
+ })(time_execution_async('--execute_action')(async (action_name, params, { browser_session = null, page_extraction_llm = null, extraction_schema = null, file_system = null, sensitive_data = null, available_file_paths = null, signal = null, context = null, } = {}) => {
59
299
  const action = this.registry.get(action_name);
60
300
  if (!action) {
61
301
  throw new Error(`Action ${action_name} not found`);
62
302
  }
63
303
  const parsed = action.paramSchema.safeParse(params);
64
304
  if (!parsed.success) {
65
- throw new Error(`Invalid parameters for action ${action_name}: ${parsed.error.message}`);
305
+ throw new Error(`Invalid parameters ${safeJsonStringify(params)} for action ${action_name}: ${parsed.error.message}`);
66
306
  }
67
307
  let validatedParams = parsed.data;
68
308
  let currentUrl = null;
@@ -86,11 +326,16 @@ export class Registry {
86
326
  browser: browser_session,
87
327
  browser_context: browser_session,
88
328
  page,
329
+ page_url: currentUrl,
330
+ cdp_client: browser_session?.cdp_client ?? null,
89
331
  page_extraction_llm,
332
+ extraction_schema,
90
333
  file_system,
91
334
  available_file_paths,
335
+ sensitive_data,
92
336
  signal,
93
- has_sensitive_data: action_name === 'input_text' && Boolean(sensitive_data),
337
+ has_sensitive_data: (action_name === 'input_text' || action_name === 'input') &&
338
+ Boolean(sensitive_data),
94
339
  };
95
340
  if (signal?.aborted) {
96
341
  throw createAbortError(signal.reason);
@@ -102,6 +347,12 @@ export class Registry {
102
347
  if (signal?.aborted || isAbortError(error)) {
103
348
  throw createAbortError(signal?.reason ?? error);
104
349
  }
350
+ if (isTimeoutError(error)) {
351
+ throw new Error(`Error executing action ${action_name} due to timeout.`, { cause: error });
352
+ }
353
+ if (isSpecialContextMissingError(error)) {
354
+ throw error;
355
+ }
105
356
  throw wrapActionExecutionError(action_name, error);
106
357
  }
107
358
  }));
@@ -134,10 +385,15 @@ export class Registry {
134
385
  const missing = new Set();
135
386
  const traverse = (value) => {
136
387
  if (typeof value === 'string') {
137
- return value.replace(secretPattern, (_, placeholder) => {
388
+ return value.replace(secretPattern, (_, placeholderValue) => {
389
+ const placeholder = String(placeholderValue);
138
390
  if (placeholder in applicableSecrets) {
139
391
  replaced.add(placeholder);
140
- return applicableSecrets[placeholder];
392
+ const replacement = applicableSecrets[placeholder];
393
+ if (placeholder.endsWith('bu_2fa_code')) {
394
+ return generateTotpCode(replacement);
395
+ }
396
+ return replacement;
141
397
  }
142
398
  missing.add(placeholder);
143
399
  return `<secret>${placeholder}</secret>`;
@@ -165,7 +421,7 @@ export class Registry {
165
421
  return;
166
422
  }
167
423
  const urlInfo = currentUrl && !is_new_tab_page(currentUrl) ? ` on ${currentUrl}` : '';
168
- logger.info(`🔒 Using sensitive data placeholders: ${Array.from(placeholders).join(', ')}${urlInfo}`);
424
+ logger.info(`🔒 Using sensitive data placeholders: ${Array.from(placeholders).sort().join(', ')}${urlInfo}`);
169
425
  }
170
426
  create_action_model(options = {}) {
171
427
  const { include_actions = null, page = null } = options;
@@ -11,7 +11,8 @@ export declare class RegisteredAction {
11
11
  readonly paramSchema: ZodTypeAny;
12
12
  readonly domains: string[] | null;
13
13
  readonly pageFilter: ((page: Page) => boolean) | null;
14
- constructor(name: string, description: string, handler: ActionHandler, paramSchema: ZodTypeAny, domains?: string[] | null, pageFilter?: ((page: Page) => boolean) | null);
14
+ readonly terminates_sequence: boolean;
15
+ constructor(name: string, description: string, handler: ActionHandler, paramSchema: ZodTypeAny, domains?: string[] | null, pageFilter?: ((page: Page) => boolean) | null, terminates_sequence?: boolean);
15
16
  promptDescription(): string;
16
17
  }
17
18
  export declare class ActionModel {
@@ -30,6 +31,7 @@ export declare class ActionModel {
30
31
  export declare class ActionRegistry {
31
32
  private actions;
32
33
  register(action: RegisteredAction): void;
34
+ remove(name: string): void;
33
35
  get(name: string): RegisteredAction | null;
34
36
  getAll(): RegisteredAction[];
35
37
  get actionsMap(): Map<string, RegisteredAction>;
@@ -46,6 +48,7 @@ export declare class SpecialActionParameters {
46
48
  browser_context: BrowserSession | null;
47
49
  page: Page | null;
48
50
  page_extraction_llm: BaseChatModel | null;
51
+ extraction_schema: Record<string, unknown> | null;
49
52
  file_system: FileSystem | null;
50
53
  available_file_paths: string[] | null;
51
54
  signal: AbortSignal | null;
@@ -22,13 +22,15 @@ export class RegisteredAction {
22
22
  paramSchema;
23
23
  domains;
24
24
  pageFilter;
25
- constructor(name, description, handler, paramSchema, domains = null, pageFilter = null) {
25
+ terminates_sequence;
26
+ constructor(name, description, handler, paramSchema, domains = null, pageFilter = null, terminates_sequence = false) {
26
27
  this.name = name;
27
28
  this.description = description;
28
29
  this.handler = handler;
29
30
  this.paramSchema = paramSchema;
30
31
  this.domains = domains;
31
32
  this.pageFilter = pageFilter;
33
+ this.terminates_sequence = terminates_sequence;
32
34
  }
33
35
  promptDescription() {
34
36
  const skipKeys = new Set(['title']);
@@ -36,8 +38,25 @@ export class RegisteredAction {
36
38
  description += `{${this.name}: `;
37
39
  const schemaShape = (this.paramSchema instanceof z.ZodObject && this.paramSchema.shape) ||
38
40
  ('shape' in this.paramSchema ? this.paramSchema.shape : null);
41
+ const hideStructuredDoneSuccess = Boolean(this.name === 'done' &&
42
+ schemaShape &&
43
+ typeof schemaShape === 'object' &&
44
+ Object.prototype.hasOwnProperty.call(schemaShape, 'data') &&
45
+ Object.prototype.hasOwnProperty.call(schemaShape, 'success'));
46
+ if (hideStructuredDoneSuccess) {
47
+ skipKeys.add('success');
48
+ }
49
+ const hideExtractOutputSchema = Boolean(this.name === 'extract_structured_data' &&
50
+ schemaShape &&
51
+ typeof schemaShape === 'object' &&
52
+ Object.prototype.hasOwnProperty.call(schemaShape, 'output_schema'));
53
+ if (hideExtractOutputSchema) {
54
+ skipKeys.add('output_schema');
55
+ }
39
56
  if (schemaShape) {
40
- const props = Object.fromEntries(Object.entries(schemaShape).map(([key, value]) => {
57
+ const props = Object.fromEntries(Object.entries(schemaShape)
58
+ .filter(([key]) => !skipKeys.has(key))
59
+ .map(([key, value]) => {
41
60
  const entries = value instanceof z.ZodType ? value._def : value;
42
61
  const cleanEntries = Object.fromEntries(Object.entries(entries).filter(([propKey]) => !skipKeys.has(propKey)));
43
62
  return [key, cleanEntries];
@@ -97,6 +116,9 @@ export class ActionRegistry {
97
116
  register(action) {
98
117
  this.actions.set(action.name, action);
99
118
  }
119
+ remove(name) {
120
+ this.actions.delete(name);
121
+ }
100
122
  get(name) {
101
123
  return this.actions.get(name) ?? null;
102
124
  }
@@ -164,6 +186,7 @@ export class SpecialActionParameters {
164
186
  browser_context = null;
165
187
  page = null;
166
188
  page_extraction_llm = null;
189
+ extraction_schema = null;
167
190
  file_system = null;
168
191
  available_file_paths = null;
169
192
  signal = null;
@@ -28,22 +28,31 @@ export interface ActParams<Context = unknown> {
28
28
  export declare class Controller<Context = unknown> {
29
29
  registry: Registry<Context>;
30
30
  private displayFilesInDoneText;
31
+ private outputModel;
32
+ private coordinateClickingEnabled;
33
+ private clickActionHandler;
31
34
  private logger;
32
35
  constructor(options?: ControllerOptions<Context>);
33
36
  private registerDefaultActions;
34
37
  private registerNavigationActions;
35
38
  private registerElementActions;
39
+ private registerClickActions;
36
40
  private registerTabActions;
37
41
  private registerContentActions;
42
+ private registerExplorationActions;
38
43
  private registerScrollActions;
39
44
  private registerFileSystemActions;
45
+ private registerUtilityActions;
40
46
  private registerKeyboardActions;
41
47
  private registerDropdownActions;
42
48
  private registerSheetsActions;
43
49
  private gotoSheetsRange;
44
50
  private registerDoneAction;
45
51
  use_structured_output_action(outputModel: z.ZodTypeAny): void;
46
- action(description: string, options?: {}): <Params = any>(handler: import("./index.js").RegistryActionHandler<Params, Context>) => import("./index.js").RegistryActionHandler<Params, Context>;
52
+ get_output_model(): z.ZodTypeAny | null;
53
+ exclude_action(actionName: string): void;
54
+ set_coordinate_clicking(enabled: boolean): void;
55
+ action(description: string, options?: {}): <Params = any>(handler: import("./index.js").RegistryActionHandler<Params, Context>) => any;
47
56
  act(action: Record<string, unknown>, { browser_session, page_extraction_llm, sensitive_data, available_file_paths, file_system, context, signal, }: ActParams<Context>): Promise<ActionResult>;
48
57
  }
49
58
  export {};