@elizaos/plugin-browser 2.0.3-beta.2 → 2.0.3-beta.4

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 (48) hide show
  1. package/dist/actions/browser.d.ts.map +1 -1
  2. package/dist/actions/browser.js +264 -7
  3. package/dist/actions/browser.js.map +1 -1
  4. package/dist/actions/wait-for-url-predicate.d.ts +34 -0
  5. package/dist/actions/wait-for-url-predicate.d.ts.map +1 -0
  6. package/dist/actions/wait-for-url-predicate.js +33 -0
  7. package/dist/actions/wait-for-url-predicate.js.map +1 -0
  8. package/dist/actions/wait-for-url.d.ts +64 -0
  9. package/dist/actions/wait-for-url.d.ts.map +1 -0
  10. package/dist/actions/wait-for-url.js +89 -0
  11. package/dist/actions/wait-for-url.js.map +1 -0
  12. package/dist/index.d.ts +4 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +27 -8
  15. package/dist/index.js.map +1 -1
  16. package/dist/parity/browser-matrix.d.ts +45 -0
  17. package/dist/parity/browser-matrix.d.ts.map +1 -0
  18. package/dist/parity/browser-matrix.js +361 -0
  19. package/dist/parity/browser-matrix.js.map +1 -0
  20. package/dist/parity/index.d.ts +5 -0
  21. package/dist/parity/index.d.ts.map +1 -0
  22. package/dist/parity/index.js +13 -0
  23. package/dist/parity/index.js.map +1 -0
  24. package/dist/routes/workspace.d.ts.map +1 -1
  25. package/dist/routes/workspace.js +42 -2
  26. package/dist/routes/workspace.js.map +1 -1
  27. package/dist/workspace/browser-capture.d.ts.map +1 -1
  28. package/dist/workspace/browser-capture.js +33 -1
  29. package/dist/workspace/browser-capture.js.map +1 -1
  30. package/dist/workspace/browser-workspace-desktop.d.ts.map +1 -1
  31. package/dist/workspace/browser-workspace-desktop.js +19 -5
  32. package/dist/workspace/browser-workspace-desktop.js.map +1 -1
  33. package/dist/workspace/browser-workspace-errors.d.ts +62 -0
  34. package/dist/workspace/browser-workspace-errors.d.ts.map +1 -0
  35. package/dist/workspace/browser-workspace-errors.js +69 -0
  36. package/dist/workspace/browser-workspace-errors.js.map +1 -0
  37. package/dist/workspace/browser-workspace-helpers.d.ts.map +1 -1
  38. package/dist/workspace/browser-workspace-helpers.js +29 -8
  39. package/dist/workspace/browser-workspace-helpers.js.map +1 -1
  40. package/dist/workspace/browser-workspace-web.d.ts.map +1 -1
  41. package/dist/workspace/browser-workspace-web.js +20 -6
  42. package/dist/workspace/browser-workspace-web.js.map +1 -1
  43. package/dist/workspace/index.d.ts +1 -0
  44. package/dist/workspace/index.d.ts.map +1 -1
  45. package/dist/workspace/index.js +1 -0
  46. package/dist/workspace/index.js.map +1 -1
  47. package/package.json +5 -4
  48. package/registry-entry.json +75 -0
@@ -1 +1 @@
1
- {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/actions/browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EAIP,MAAM,eAAe,CAAC;AAYvB;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC;AA2SnC,eAAO,MAAM,aAAa,EAAE,MAyS3B,CAAC"}
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/actions/browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EAMP,MAAM,eAAe,CAAC;AAkBvB;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC;AAqlBnC,eAAO,MAAM,aAAa,EAAE,MA4W3B,CAAC"}
@@ -6,6 +6,11 @@ import {
6
6
  executeBrowserWorkspaceCommand,
7
7
  getBrowserWorkspaceMode
8
8
  } from "../workspace/browser-workspace.js";
9
+ import {
10
+ WAIT_FOR_URL_DEFAULT_POLL_INTERVAL_MS,
11
+ WAIT_FOR_URL_DEFAULT_TIMEOUT_MS,
12
+ waitForUrl
13
+ } from "./wait-for-url.js";
9
14
  function getMessageText(message) {
10
15
  const content = message?.content;
11
16
  if (typeof content === "string") {
@@ -22,6 +27,9 @@ function inferBrowserSubaction(params, messageText) {
22
27
  if (normalizedAction === "autofill-login" || params?.subaction === "autofill-login") {
23
28
  return "autofill-login";
24
29
  }
30
+ if (normalizedAction === "wait-for-url" || params?.subaction === "wait-for-url") {
31
+ return "wait-for-url";
32
+ }
25
33
  const legacySubaction = normalizeLegacyBrowserAction(normalizedAction);
26
34
  if (legacySubaction) {
27
35
  return legacySubaction;
@@ -60,6 +68,9 @@ function normalizeBrowserAction(action) {
60
68
  return "cursor-hide";
61
69
  case "autofill_login":
62
70
  return "autofill-login";
71
+ case "wait_for_url":
72
+ case "wait-for-url":
73
+ return "wait-for-url";
63
74
  default:
64
75
  return action;
65
76
  }
@@ -77,6 +88,7 @@ function normalizeLegacyBrowserAction(action) {
77
88
  case "switch_tab":
78
89
  return "tab";
79
90
  case "autofill-login":
91
+ case "wait-for-url":
80
92
  return void 0;
81
93
  case void 0:
82
94
  return void 0;
@@ -129,6 +141,193 @@ ${serialized}`;
129
141
  }
130
142
  return `Browser ${command.subaction} completed in ${result.mode} mode.`;
131
143
  }
144
+ function browserProgressRationale(command, params, messageText) {
145
+ const explicit = params?.rationale?.trim();
146
+ if (explicit) return explicit;
147
+ switch (command.subaction) {
148
+ case "open":
149
+ case "navigate":
150
+ return command.url ? `open ${command.url}` : "open requested page";
151
+ case "click":
152
+ case "realistic-click":
153
+ return command.selector ? `click ${command.selector}` : "click requested target";
154
+ case "type":
155
+ case "realistic-fill":
156
+ case "realistic-type":
157
+ return command.selector ? `fill ${command.selector}` : "type requested text";
158
+ case "press":
159
+ case "realistic-press":
160
+ return command.key ? `press ${command.key}` : "press requested key";
161
+ case "tab":
162
+ return command.tabAction ? `${command.tabAction} browser tab` : "manage browser tabs";
163
+ case "wait":
164
+ return command.selector ? `wait for ${command.selector}` : "wait for browser state";
165
+ case "state":
166
+ return messageText.trim() || "read browser state";
167
+ default:
168
+ return `run browser ${command.subaction}`;
169
+ }
170
+ }
171
+ function buildBrowserStepProgressContent(command, params, messageText, success, error) {
172
+ const rationale = error ? `failed: ${error}` : browserProgressRationale(command, params, messageText);
173
+ return {
174
+ text: `Step 1: ${command.subaction} \u2014 ${rationale}`,
175
+ source: "action_progress",
176
+ merge: "replace",
177
+ metadata: {
178
+ transient: true,
179
+ compactProgress: true,
180
+ progress: {
181
+ source: "browser",
182
+ actionName: "BROWSER",
183
+ step: 1,
184
+ kind: command.subaction,
185
+ rationale,
186
+ success,
187
+ error
188
+ }
189
+ }
190
+ };
191
+ }
192
+ async function emitBrowserStepProgress(callback, command, params, messageText, success, error) {
193
+ if (params?.streamProgress !== true || !callback) return;
194
+ try {
195
+ await callback(
196
+ buildBrowserStepProgressContent(
197
+ command,
198
+ params,
199
+ messageText,
200
+ success,
201
+ error
202
+ ),
203
+ "BROWSER"
204
+ );
205
+ } catch (callbackError) {
206
+ logger.warn(
207
+ {
208
+ src: "plugin:browser",
209
+ action: command.subaction,
210
+ error: callbackError instanceof Error ? callbackError.message : String(callbackError)
211
+ },
212
+ "Failed to emit browser progress callback"
213
+ );
214
+ }
215
+ }
216
+ function currentUrlFromResult(result) {
217
+ if (result.tab?.url) {
218
+ return result.tab.url;
219
+ }
220
+ if (typeof result.value === "string" && result.value.trim()) {
221
+ return result.value.trim();
222
+ }
223
+ if (Array.isArray(result.tabs)) {
224
+ const visible = result.tabs.find((tab) => tab.visible) ?? result.tabs[0];
225
+ if (visible?.url) {
226
+ return visible.url;
227
+ }
228
+ }
229
+ return null;
230
+ }
231
+ async function readCurrentBrowserUrl(browserService, target, tabId) {
232
+ const run = async (command) => browserService ? browserService.execute(command, target) : executeBrowserWorkspaceCommand(command);
233
+ try {
234
+ const got = await run({
235
+ subaction: "get",
236
+ getMode: "url",
237
+ id: tabId
238
+ });
239
+ const url = currentUrlFromResult(got);
240
+ if (url) {
241
+ return url;
242
+ }
243
+ } catch (error) {
244
+ logger.debug(
245
+ `[BROWSER] wait_for_url get-url poll failed, falling back to state: ${error instanceof Error ? error.message : String(error)}`
246
+ );
247
+ }
248
+ try {
249
+ const state = await run({ subaction: "state", id: tabId });
250
+ return currentUrlFromResult(state);
251
+ } catch (error) {
252
+ logger.debug(
253
+ `[BROWSER] wait_for_url state poll could not read URL: ${error instanceof Error ? error.message : String(error)}`
254
+ );
255
+ return null;
256
+ }
257
+ }
258
+ async function executeBrowserWaitForUrl(runtime, params, messageText, callback) {
259
+ const pattern = (params?.pattern ?? "").trim();
260
+ if (!pattern) {
261
+ const text = "wait_for_url needs a `pattern` (substring or regex) to watch for.";
262
+ logger.warn(`[BROWSER] ${text}`);
263
+ return {
264
+ text,
265
+ success: false,
266
+ values: { success: false, error: "BROWSER_WAIT_FOR_URL_NO_PATTERN" },
267
+ data: { actionName: "BROWSER", subaction: "wait_for_url" }
268
+ };
269
+ }
270
+ const browserService = runtime.getService(BROWSER_SERVICE_TYPE) ?? null;
271
+ const target = params?.target;
272
+ const startUrl = params?.url?.trim() || extractFirstUrl(messageText) || void 0;
273
+ const timeoutMs = params?.timeoutMs ?? WAIT_FOR_URL_DEFAULT_TIMEOUT_MS;
274
+ const pollIntervalMs = params?.pollIntervalMs ?? WAIT_FOR_URL_DEFAULT_POLL_INTERVAL_MS;
275
+ let tabId = params?.id?.trim() || void 0;
276
+ if (startUrl) {
277
+ const openCommand = {
278
+ subaction: tabId ? "navigate" : "open",
279
+ url: startUrl,
280
+ id: tabId
281
+ };
282
+ try {
283
+ const opened = browserService ? await browserService.execute(openCommand, target) : await executeBrowserWorkspaceCommand(openCommand);
284
+ tabId = opened.tab?.id ?? tabId;
285
+ } catch (error) {
286
+ const reason = error instanceof Error ? error.message : String(error);
287
+ logger.warn(
288
+ `[BROWSER] wait_for_url could not open ${startUrl}: ${reason}`
289
+ );
290
+ return {
291
+ text: `Couldn't open ${startUrl} to watch for "${pattern}": ${reason}`,
292
+ success: false,
293
+ values: { success: false, error: "BROWSER_WAIT_FOR_URL_OPEN_FAILED" },
294
+ data: { actionName: "BROWSER", subaction: "wait_for_url" }
295
+ };
296
+ }
297
+ }
298
+ await callback?.({
299
+ text: startUrl ? `I opened ${startUrl} \u2014 watching for "${pattern}" and I'll resume when it's reached.` : `Watching the current tab for "${pattern}" \u2014 I'll resume when it's reached.`
300
+ });
301
+ logger.info(
302
+ `[BROWSER] wait_for_url pattern="${pattern}" timeoutMs=${timeoutMs} pollIntervalMs=${pollIntervalMs} target=${target ?? "auto"}`
303
+ );
304
+ const outcome = await waitForUrl(
305
+ { pattern, timeoutMs, pollIntervalMs },
306
+ {
307
+ getCurrentUrl: () => readCurrentBrowserUrl(browserService, target, tabId),
308
+ emitStatus: async (text) => {
309
+ await callback?.({ text });
310
+ }
311
+ }
312
+ );
313
+ return {
314
+ text: outcome.message,
315
+ success: outcome.matched,
316
+ userFacingText: outcome.message,
317
+ values: {
318
+ success: outcome.matched,
319
+ subaction: "wait_for_url",
320
+ status: outcome.status,
321
+ matched: outcome.matched,
322
+ polls: outcome.polls
323
+ },
324
+ data: {
325
+ actionName: "BROWSER",
326
+ subaction: "wait_for_url",
327
+ outcome
328
+ }
329
+ };
330
+ }
132
331
  const browserAction = {
133
332
  name: "BROWSER",
134
333
  contexts: ["browser", "web", "automation", "secrets"],
@@ -151,10 +350,10 @@ const browserAction = {
151
350
  "LOG_INTO_SITE",
152
351
  "SIGN_IN_TO_SITE"
153
352
  ],
154
- description: "BROWSER action. Control registered browser target: app workspace, bridge Chrome/Safari companion, computeruse Chromium, or Stagehand fallback. BrowserService picks target if omitted. action=autofill_login + domain vault-gated autofills open workspace tab.",
155
- descriptionCompressed: "Browser open|navigate|click|type|screenshot|state|autofill_login; bridge status elsewhere",
353
+ description: "BROWSER action. Control registered browser target: app workspace, bridge Chrome/Safari companion, computeruse Chromium, or Stagehand fallback. BrowserService picks target if omitted. action=autofill_login + domain vault-gated autofills open workspace tab. action=wait_for_url + pattern opens an optional url then watches the tab and resumes when its URL matches (OAuth callback, deploy/CI done), streaming progress.",
354
+ descriptionCompressed: "Browser open|navigate|click|type|screenshot|state|autofill_login|wait_for_url; bridge status elsewhere",
156
355
  validate: async () => true,
157
- handler: async (runtime, message, _state, options) => {
356
+ handler: async (runtime, message, _state, options, callback) => {
158
357
  const params = options?.parameters;
159
358
  const messageText = getMessageText(message);
160
359
  const subaction = inferBrowserSubaction(params, messageText);
@@ -162,6 +361,9 @@ const browserAction = {
162
361
  const { executeBrowserAutofillLogin } = await import("./browser-autofill-login.js");
163
362
  return executeBrowserAutofillLogin(runtime, message, options);
164
363
  }
364
+ if (subaction === "wait-for-url") {
365
+ return executeBrowserWaitForUrl(runtime, params, messageText, callback);
366
+ }
165
367
  const url = params?.url?.trim() || extractFirstUrl(messageText) || void 0;
166
368
  const command = {
167
369
  id: params?.id?.trim(),
@@ -187,6 +389,13 @@ const browserAction = {
187
389
  `[BROWSER] ${command.subaction} via target=${params?.target ?? "auto"} (workspace mode=${getBrowserWorkspaceMode(process.env)})`
188
390
  );
189
391
  const result = browserService ? await browserService.execute(command, params?.target) : await executeBrowserWorkspaceCommand(command);
392
+ await emitBrowserStepProgress(
393
+ callback,
394
+ command,
395
+ params,
396
+ messageText,
397
+ true
398
+ );
190
399
  return {
191
400
  text: formatBrowserSessionResult(command, result),
192
401
  success: true,
@@ -202,10 +411,18 @@ const browserAction = {
202
411
  }
203
412
  };
204
413
  } catch (error) {
205
- const messageText2 = error instanceof Error ? error.message : "Browser action failed";
206
- logger.warn(`[BROWSER] Failed: ${messageText2}`);
414
+ const errorText = error instanceof Error ? error.message : "Browser action failed";
415
+ logger.warn(`[BROWSER] Failed: ${errorText}`);
416
+ await emitBrowserStepProgress(
417
+ callback,
418
+ command,
419
+ params,
420
+ messageText,
421
+ false,
422
+ errorText
423
+ );
207
424
  return {
208
- text: `Browser action failed: ${messageText2}`,
425
+ text: `Browser action failed: ${errorText}`,
209
426
  success: false,
210
427
  values: { success: false, error: "BROWSER_FAILED" },
211
428
  data: {
@@ -222,6 +439,18 @@ const browserAction = {
222
439
  required: false,
223
440
  schema: { type: "string" }
224
441
  },
442
+ {
443
+ name: "streamProgress",
444
+ description: "When true, emit a compact Step 1 progress callback after the browser command dispatches.",
445
+ required: false,
446
+ schema: { type: "boolean", default: false }
447
+ },
448
+ {
449
+ name: "rationale",
450
+ description: "Optional rationale shown in streamProgress callback text.",
451
+ required: false,
452
+ schema: { type: "string" }
453
+ },
225
454
  {
226
455
  name: "action",
227
456
  description: "Browser action. Snake_case canonical; legacy kebab-case and subaction accepted.",
@@ -259,10 +488,23 @@ const browserAction = {
259
488
  "realistic_press",
260
489
  "cursor_move",
261
490
  "cursor_hide",
262
- "autofill_login"
491
+ "autofill_login",
492
+ "wait_for_url"
263
493
  ]
264
494
  }
265
495
  },
496
+ {
497
+ name: "pattern",
498
+ description: "For action=wait_for_url: substring or /regex/ to match the tab URL (e.g. callback?code=, or /\\/done$/).",
499
+ required: false,
500
+ schema: { type: "string" }
501
+ },
502
+ {
503
+ name: "pollIntervalMs",
504
+ description: "For action=wait_for_url: poll cadence in ms. Default 2000.",
505
+ required: false,
506
+ schema: { type: "number" }
507
+ },
266
508
  {
267
509
  name: "tabAction",
268
510
  description: "Tab operation when subaction is tab",
@@ -403,6 +645,21 @@ const browserAction = {
403
645
  text: "click completed in desktop mode."
404
646
  }
405
647
  }
648
+ ],
649
+ [
650
+ {
651
+ name: "{{name1}}",
652
+ content: {
653
+ text: "Open the GitHub OAuth page and let me know when it redirects back to our callback."
654
+ }
655
+ },
656
+ {
657
+ name: "{{agentName}}",
658
+ content: {
659
+ text: `I opened https://github.com/login/oauth/authorize \u2014 watching for "callback?code=" and I'll resume when it's reached.`,
660
+ actions: ["BROWSER"]
661
+ }
662
+ }
406
663
  ]
407
664
  ]
408
665
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/actions/browser.ts"],"sourcesContent":["import type {\n Action,\n ActionExample,\n HandlerOptions,\n Memory,\n} from \"@elizaos/core\";\nimport { logger } from \"@elizaos/core\";\nimport {\n BROWSER_SERVICE_TYPE,\n type BrowserService,\n} from \"../browser-service.js\";\nimport {\n type BrowserWorkspaceCommand,\n executeBrowserWorkspaceCommand,\n getBrowserWorkspaceMode,\n} from \"../workspace/browser-workspace.js\";\n\n/**\n * Targets are the registered browser backends. The agent uses what is\n * available; specifying a target overrides automatic routing. `workspace`\n * is the app-owned browser surface, `bridge` is the paired Chrome/Safari\n * companion, `stagehand` is the Playwright/Stagehand fallback, and other\n * plugins may register additional target ids.\n */\nexport type BrowserTarget = string;\n\ntype BrowserWorkspaceSubaction =\n | \"back\"\n | \"click\"\n | \"close\"\n | \"forward\"\n | \"get\"\n | \"hide\"\n | \"navigate\"\n | \"open\"\n | \"press\"\n | \"reload\"\n | \"screenshot\"\n | \"show\"\n | \"snapshot\"\n | \"state\"\n | \"tab\"\n | \"type\"\n | \"wait\"\n | \"realistic-click\"\n | \"realistic-fill\"\n | \"realistic-type\"\n | \"realistic-press\"\n | \"cursor-move\"\n | \"cursor-hide\";\n\ntype BrowserWorkspaceAction =\n | BrowserWorkspaceSubaction\n | \"realistic_click\"\n | \"realistic_fill\"\n | \"realistic_type\"\n | \"realistic_press\"\n | \"cursor_move\"\n | \"cursor_hide\";\n\ntype BrowserActionSubaction = BrowserWorkspaceSubaction | \"autofill-login\";\ntype BrowserActionValue =\n | BrowserWorkspaceAction\n | \"autofill_login\"\n | \"autofill-login\";\ntype NormalizedBrowserAction =\n | BrowserWorkspaceSubaction\n | \"autofill-login\"\n | \"info\"\n | \"context\"\n | \"get_context\"\n | \"list_tabs\"\n | \"open_tab\"\n | \"close_tab\"\n | \"switch_tab\";\n\ntype BrowserActionParameters = {\n /**\n * Optional target override. Default: the BrowserService active target\n * selected from registered targets. Forces a specific backend when set.\n */\n target?: BrowserTarget;\n id?: string;\n key?: string;\n pixels?: number;\n script?: string;\n selector?: string;\n /**\n * Canonical browser action. Legacy `subaction` remains accepted.\n */\n action?:\n | BrowserActionValue\n | \"info\"\n | \"context\"\n | \"get_context\"\n | \"list_tabs\"\n | \"open_tab\"\n | \"close_tab\"\n | \"switch_tab\";\n subaction?: BrowserActionSubaction;\n /** Registrable hostname for `action: \"autofill_login\"`. */\n domain?: string;\n /** Saved login username for autofill-login (optional). */\n username?: string;\n /** When true with autofill-login, submit after filling. */\n submit?: boolean;\n tabAction?: \"close\" | \"list\" | \"new\" | \"switch\";\n text?: string;\n timeoutMs?: number;\n url?: string;\n /** Cursor animation duration (ms) for realistic-* + cursor-* subactions. */\n cursorDurationMs?: number;\n /** Per-character delay for realistic-type / realistic-fill (ms). */\n perCharDelayMs?: number;\n /** Replace existing input value when filling (vs append). */\n replace?: boolean;\n /** Cursor target X (CSS pixels) for cursor-move. */\n x?: number;\n /** Cursor target Y (CSS pixels) for cursor-move. */\n y?: number;\n /** Hint that the agent is operating in a watch-mode (page-browser) scope. */\n watchMode?: boolean;\n};\n\nfunction getMessageText(message: Memory | undefined): string {\n const content = message?.content;\n if (typeof content === \"string\") {\n return content;\n }\n return typeof content?.text === \"string\" ? content.text : \"\";\n}\n\nfunction extractFirstUrl(value: string): string | null {\n const match = value.match(/https?:\\/\\/[^\\s<>\"'`]+/i);\n return match?.[0] ?? null;\n}\n\nfunction inferBrowserSubaction(\n params: BrowserActionParameters | undefined,\n messageText: string,\n): BrowserWorkspaceCommand[\"subaction\"] | \"autofill-login\" {\n const normalizedAction = normalizeBrowserAction(params?.action);\n if (\n normalizedAction === \"autofill-login\" ||\n params?.subaction === \"autofill-login\"\n ) {\n return \"autofill-login\";\n }\n\n const legacySubaction = normalizeLegacyBrowserAction(normalizedAction);\n if (legacySubaction) {\n return legacySubaction;\n }\n if (params?.subaction) {\n return params.subaction;\n }\n\n if (params?.tabAction) {\n return \"tab\";\n }\n\n // In watch mode the user is observing the agent drive the browser; prefer\n // the realistic-* subactions so the cursor moves and pointer events fire\n // faithfully. Default-mode (no watcher) keeps the leaner click()/value=\n // path for speed.\n const watchMode = params?.watchMode === true;\n\n if (params?.selector && params?.text) {\n return watchMode ? \"realistic-fill\" : \"type\";\n }\n\n if (params?.selector) {\n return watchMode ? \"realistic-click\" : \"click\";\n }\n\n if (params?.url?.trim() || extractFirstUrl(messageText)) {\n return params?.id ? \"navigate\" : \"open\";\n }\n\n return \"state\";\n}\n\nfunction normalizeBrowserAction(\n action: BrowserActionParameters[\"action\"] | undefined,\n): NormalizedBrowserAction | undefined {\n switch (action) {\n case \"realistic_click\":\n return \"realistic-click\";\n case \"realistic_fill\":\n return \"realistic-fill\";\n case \"realistic_type\":\n return \"realistic-type\";\n case \"realistic_press\":\n return \"realistic-press\";\n case \"cursor_move\":\n return \"cursor-move\";\n case \"cursor_hide\":\n return \"cursor-hide\";\n case \"autofill_login\":\n return \"autofill-login\";\n default:\n return action as NormalizedBrowserAction | undefined;\n }\n}\n\nfunction normalizeLegacyBrowserAction(\n action: BrowserActionParameters[\"action\"] | undefined,\n): BrowserWorkspaceCommand[\"subaction\"] | undefined {\n const normalizedAction = normalizeBrowserAction(action);\n switch (normalizedAction) {\n case \"info\":\n case \"context\":\n case \"get_context\":\n return \"state\";\n case \"list_tabs\":\n case \"open_tab\":\n case \"close_tab\":\n case \"switch_tab\":\n return \"tab\";\n case \"autofill-login\":\n return undefined;\n case undefined:\n return undefined;\n default:\n return isWorkspaceSubaction(normalizedAction)\n ? normalizedAction\n : undefined;\n }\n}\n\nfunction isWorkspaceSubaction(\n action: unknown,\n): action is BrowserWorkspaceCommand[\"subaction\"] {\n return (\n action === \"back\" ||\n action === \"click\" ||\n action === \"close\" ||\n action === \"forward\" ||\n action === \"get\" ||\n action === \"hide\" ||\n action === \"navigate\" ||\n action === \"open\" ||\n action === \"press\" ||\n action === \"reload\" ||\n action === \"screenshot\" ||\n action === \"show\" ||\n action === \"snapshot\" ||\n action === \"state\" ||\n action === \"tab\" ||\n action === \"type\" ||\n action === \"wait\" ||\n action === \"realistic-click\" ||\n action === \"realistic-fill\" ||\n action === \"realistic-type\" ||\n action === \"realistic-press\" ||\n action === \"cursor-move\" ||\n action === \"cursor-hide\"\n );\n}\n\nfunction normalizeLegacyTabAction(\n action: BrowserActionParameters[\"action\"] | undefined,\n): BrowserActionParameters[\"tabAction\"] | undefined {\n switch (normalizeBrowserAction(action)) {\n case \"list_tabs\":\n return \"list\";\n case \"open_tab\":\n return \"new\";\n case \"close_tab\":\n return \"close\";\n case \"switch_tab\":\n return \"switch\";\n default:\n return undefined;\n }\n}\n\nfunction formatBrowserSessionResult(\n command: BrowserWorkspaceCommand,\n result: Awaited<ReturnType<typeof executeBrowserWorkspaceCommand>>,\n): string {\n if (result.tabs) {\n const labels = result.tabs\n .map((tab) => `- ${tab.title} (${tab.url})`)\n .join(\"\\n\");\n return labels\n ? `Browser tabs (${result.mode}):\\n${labels}`\n : `No browser session tabs are open (${result.mode}).`;\n }\n\n if (result.closed) {\n return `Browser closed (${result.mode}).`;\n }\n\n if (result.tab) {\n return `${command.subaction} completed in ${result.mode} mode.\\n${result.tab.title}\\n${result.tab.url}`;\n }\n\n if (result.value !== undefined) {\n if (\n command.subaction === \"cursor-move\" &&\n result.value !== null &&\n typeof result.value === \"object\" &&\n \"x\" in result.value &&\n \"y\" in result.value\n ) {\n const cursor = result.value as { x: number; y: number };\n return `Cursor moved to (${Math.round(cursor.x)}, ${Math.round(cursor.y)}) in ${result.mode} mode.`;\n }\n const serialized =\n typeof result.value === \"string\"\n ? result.value\n : JSON.stringify(result.value, null, 2);\n return `Browser ${command.subaction} result (${result.mode}):\\n${serialized}`;\n }\n\n if (result.snapshot?.data) {\n return `Browser ${command.subaction} captured a preview in ${result.mode} mode.`;\n }\n\n return `Browser ${command.subaction} completed in ${result.mode} mode.`;\n}\n\nexport const browserAction: Action = {\n name: \"BROWSER\",\n contexts: [\"browser\", \"web\", \"automation\", \"secrets\"],\n roleGate: { minRole: \"OWNER\" },\n similes: [\n \"BROWSE_SITE\",\n \"BROWSER_SESSION\",\n \"CONTROL_BROWSER\",\n \"CONTROL_BROWSER_SESSION\",\n \"MANAGE_ELIZA_BROWSER_WORKSPACE\",\n \"NAVIGATE_SITE\",\n \"OPEN_SITE\",\n \"USE_BROWSER\",\n \"BROWSER_ACTION\",\n \"BROWSER_AUTOFILL_LOGIN\",\n \"AGENT_AUTOFILL\",\n \"AUTOFILL_BROWSER_LOGIN\",\n \"AUTOFILL_LOGIN\",\n \"FILL_BROWSER_CREDENTIALS\",\n \"LOG_INTO_SITE\",\n \"SIGN_IN_TO_SITE\",\n ],\n description:\n \"BROWSER action. Control registered browser target: app workspace, bridge Chrome/Safari companion, computeruse Chromium, or Stagehand fallback. BrowserService picks target if omitted. action=autofill_login + domain vault-gated autofills open workspace tab.\",\n descriptionCompressed:\n \"Browser open|navigate|click|type|screenshot|state|autofill_login; bridge status elsewhere\",\n validate: async () => true,\n handler: async (runtime, message, _state, options) => {\n const params = (options as HandlerOptions | undefined)?.parameters as\n | BrowserActionParameters\n | undefined;\n const messageText = getMessageText(message);\n const subaction = inferBrowserSubaction(params, messageText);\n\n if (subaction === \"autofill-login\") {\n const { executeBrowserAutofillLogin } = await import(\n \"./browser-autofill-login.js\"\n );\n return executeBrowserAutofillLogin(runtime, message, options);\n }\n\n const url =\n params?.url?.trim() || extractFirstUrl(messageText) || undefined;\n\n const command: BrowserWorkspaceCommand = {\n id: params?.id?.trim(),\n key: params?.key?.trim(),\n pixels: params?.pixels,\n script: params?.script,\n selector: params?.selector?.trim(),\n subaction,\n tabAction: params?.tabAction ?? normalizeLegacyTabAction(params?.action),\n text: params?.text,\n value: params?.text,\n timeoutMs: params?.timeoutMs,\n url,\n cursorDurationMs: params?.cursorDurationMs,\n perCharDelayMs: params?.perCharDelayMs,\n replace: params?.replace,\n x: params?.x,\n y: params?.y,\n };\n\n const browserService =\n runtime.getService<BrowserService>(BROWSER_SERVICE_TYPE);\n\n try {\n logger.info(\n `[BROWSER] ${command.subaction} via target=${params?.target ?? \"auto\"} (workspace mode=${getBrowserWorkspaceMode(process.env)})`,\n );\n const result = browserService\n ? await browserService.execute(command, params?.target)\n : await executeBrowserWorkspaceCommand(command);\n\n return {\n text: formatBrowserSessionResult(command, result),\n success: true,\n values: {\n success: true,\n mode: result.mode,\n subaction: result.subaction,\n },\n data: {\n actionName: \"BROWSER\",\n command,\n result,\n },\n };\n } catch (error) {\n const messageText =\n error instanceof Error ? error.message : \"Browser action failed\";\n logger.warn(`[BROWSER] Failed: ${messageText}`);\n return {\n text: `Browser action failed: ${messageText}`,\n success: false,\n values: { success: false, error: \"BROWSER_FAILED\" },\n data: {\n actionName: \"BROWSER\",\n command,\n },\n };\n }\n },\n parameters: [\n {\n name: \"target\",\n description:\n \"Optional browser target id. Common values: workspace, bridge, computeruse, stagehand.\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"action\",\n description:\n \"Browser action. Snake_case canonical; legacy kebab-case and subaction accepted.\",\n required: false,\n schema: {\n type: \"string\" as const,\n enum: [\n \"back\",\n \"click\",\n \"close\",\n \"context\",\n \"forward\",\n \"get\",\n \"get_context\",\n \"hide\",\n \"info\",\n \"list_tabs\",\n \"navigate\",\n \"open\",\n \"open_tab\",\n \"press\",\n \"reload\",\n \"screenshot\",\n \"show\",\n \"snapshot\",\n \"state\",\n \"tab\",\n \"type\",\n \"wait\",\n \"close_tab\",\n \"switch_tab\",\n \"realistic_click\",\n \"realistic_fill\",\n \"realistic_type\",\n \"realistic_press\",\n \"cursor_move\",\n \"cursor_hide\",\n \"autofill_login\",\n ],\n },\n },\n {\n name: \"tabAction\",\n description: \"Tab operation when subaction is tab\",\n required: false,\n schema: {\n type: \"string\" as const,\n enum: [\"close\", \"list\", \"new\", \"switch\"],\n },\n },\n {\n name: \"domain\",\n description:\n \"Required for action=autofill_login: registrable hostname, e.g. github.com.\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"username\",\n description: \"For autofill-login: saved login username; omit for latest.\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"submit\",\n description: \"For autofill-login: submit after filling. Default false.\",\n required: false,\n schema: { type: \"boolean\" as const },\n },\n {\n name: \"id\",\n description: \"Session or tab id to target\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"url\",\n description: \"URL for open or navigate\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"selector\",\n description: \"Selector for click, type, or wait\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"text\",\n description: \"Text for type\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"key\",\n description: \"Keyboard key for press\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"pixels\",\n description: \"Scroll distance in pixels\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"timeoutMs\",\n description: \"Command timeout in milliseconds\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"script\",\n description: \"Script for eval\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"watchMode\",\n description:\n \"User watching hint; prefer realistic-* click/fill, visible cursor, pointer events.\",\n required: false,\n schema: { type: \"boolean\" as const },\n },\n {\n name: \"cursorDurationMs\",\n description: \"Cursor animation duration (ms) for realistic-* subactions\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"perCharDelayMs\",\n description: \"Per-character delay for realistic-type/realistic-fill (ms)\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"replace\",\n description: \"For realistic-fill: replace existing input, not append.\",\n required: false,\n schema: { type: \"boolean\" as const },\n },\n {\n name: \"x\",\n description: \"Cursor target X (CSS pixels) for cursor-move\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"y\",\n description: \"Cursor target Y (CSS pixels) for cursor-move\",\n required: false,\n schema: { type: \"number\" as const },\n },\n ],\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Open elizaos.ai in a new browser tab.\",\n },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: \"open completed in desktop mode.\\nelizaOS\\nhttps://elizaos.ai\",\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Click the sign-in button on that page.\",\n },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: \"click completed in desktop mode.\",\n },\n },\n ],\n ] as ActionExample[][],\n};\n"],"mappings":"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","names":["messageText"]}
1
+ {"version":3,"sources":["../../src/actions/browser.ts"],"sourcesContent":["import type {\n Action,\n ActionExample,\n Content,\n HandlerCallback,\n HandlerOptions,\n Memory,\n} from \"@elizaos/core\";\nimport { logger } from \"@elizaos/core\";\nimport {\n BROWSER_SERVICE_TYPE,\n type BrowserService,\n} from \"../browser-service.js\";\nimport {\n type BrowserWorkspaceCommand,\n type BrowserWorkspaceCommandResult,\n executeBrowserWorkspaceCommand,\n getBrowserWorkspaceMode,\n} from \"../workspace/browser-workspace.js\";\nimport {\n WAIT_FOR_URL_DEFAULT_POLL_INTERVAL_MS,\n WAIT_FOR_URL_DEFAULT_TIMEOUT_MS,\n waitForUrl,\n} from \"./wait-for-url.js\";\n\n/**\n * Targets are the registered browser backends. The agent uses what is\n * available; specifying a target overrides automatic routing. `workspace`\n * is the app-owned browser surface, `bridge` is the paired Chrome/Safari\n * companion, `stagehand` is the Playwright/Stagehand fallback, and other\n * plugins may register additional target ids.\n */\nexport type BrowserTarget = string;\n\ntype BrowserWorkspaceSubaction =\n | \"back\"\n | \"click\"\n | \"close\"\n | \"forward\"\n | \"get\"\n | \"hide\"\n | \"navigate\"\n | \"open\"\n | \"press\"\n | \"reload\"\n | \"screenshot\"\n | \"show\"\n | \"snapshot\"\n | \"state\"\n | \"tab\"\n | \"type\"\n | \"wait\"\n | \"realistic-click\"\n | \"realistic-fill\"\n | \"realistic-type\"\n | \"realistic-press\"\n | \"cursor-move\"\n | \"cursor-hide\";\n\ntype BrowserWorkspaceAction =\n | BrowserWorkspaceSubaction\n | \"realistic_click\"\n | \"realistic_fill\"\n | \"realistic_type\"\n | \"realistic_press\"\n | \"cursor_move\"\n | \"cursor_hide\";\n\ntype BrowserActionSubaction =\n | BrowserWorkspaceSubaction\n | \"autofill-login\"\n | \"wait-for-url\";\ntype BrowserActionValue =\n | BrowserWorkspaceAction\n | \"autofill_login\"\n | \"autofill-login\"\n | \"wait_for_url\"\n | \"wait-for-url\";\ntype NormalizedBrowserAction =\n | BrowserWorkspaceSubaction\n | \"autofill-login\"\n | \"wait-for-url\"\n | \"info\"\n | \"context\"\n | \"get_context\"\n | \"list_tabs\"\n | \"open_tab\"\n | \"close_tab\"\n | \"switch_tab\";\n\ntype BrowserActionParameters = {\n /**\n * Optional target override. Default: the BrowserService active target\n * selected from registered targets. Forces a specific backend when set.\n */\n target?: BrowserTarget;\n id?: string;\n key?: string;\n pixels?: number;\n script?: string;\n selector?: string;\n /**\n * Canonical browser action. Legacy `subaction` remains accepted.\n */\n action?:\n | BrowserActionValue\n | \"info\"\n | \"context\"\n | \"get_context\"\n | \"list_tabs\"\n | \"open_tab\"\n | \"close_tab\"\n | \"switch_tab\";\n subaction?: BrowserActionSubaction;\n /** For action=wait_for_url: substring or regex to match the tab URL. */\n pattern?: string;\n /** For action=wait_for_url: poll cadence in ms (default ~2000). */\n pollIntervalMs?: number;\n /** Registrable hostname for `action: \"autofill_login\"`. */\n domain?: string;\n /** Saved login username for autofill-login (optional). */\n username?: string;\n /** When true with autofill-login, submit after filling. */\n submit?: boolean;\n tabAction?: \"close\" | \"list\" | \"new\" | \"switch\";\n text?: string;\n timeoutMs?: number;\n url?: string;\n /** Cursor animation duration (ms) for realistic-* + cursor-* subactions. */\n cursorDurationMs?: number;\n /** Per-character delay for realistic-type / realistic-fill (ms). */\n perCharDelayMs?: number;\n /** Replace existing input value when filling (vs append). */\n replace?: boolean;\n /** Cursor target X (CSS pixels) for cursor-move. */\n x?: number;\n /** Cursor target Y (CSS pixels) for cursor-move. */\n y?: number;\n /** Hint that the agent is operating in a watch-mode (page-browser) scope. */\n watchMode?: boolean;\n /** Emit one compact progress callback after the browser step dispatches. */\n streamProgress?: boolean;\n /** Optional rationale to show in the compact progress callback. */\n rationale?: string;\n};\n\nfunction getMessageText(message: Memory | undefined): string {\n const content = message?.content;\n if (typeof content === \"string\") {\n return content;\n }\n return typeof content?.text === \"string\" ? content.text : \"\";\n}\n\nfunction extractFirstUrl(value: string): string | null {\n const match = value.match(/https?:\\/\\/[^\\s<>\"'`]+/i);\n return match?.[0] ?? null;\n}\n\nfunction inferBrowserSubaction(\n params: BrowserActionParameters | undefined,\n messageText: string,\n): BrowserWorkspaceCommand[\"subaction\"] | \"autofill-login\" | \"wait-for-url\" {\n const normalizedAction = normalizeBrowserAction(params?.action);\n if (\n normalizedAction === \"autofill-login\" ||\n params?.subaction === \"autofill-login\"\n ) {\n return \"autofill-login\";\n }\n\n if (\n normalizedAction === \"wait-for-url\" ||\n params?.subaction === \"wait-for-url\"\n ) {\n return \"wait-for-url\";\n }\n\n const legacySubaction = normalizeLegacyBrowserAction(normalizedAction);\n if (legacySubaction) {\n return legacySubaction;\n }\n if (params?.subaction) {\n return params.subaction;\n }\n\n if (params?.tabAction) {\n return \"tab\";\n }\n\n // In watch mode the user is observing the agent drive the browser; prefer\n // the realistic-* subactions so the cursor moves and pointer events fire\n // faithfully. Default-mode (no watcher) keeps the leaner click()/value=\n // path for speed.\n const watchMode = params?.watchMode === true;\n\n if (params?.selector && params?.text) {\n return watchMode ? \"realistic-fill\" : \"type\";\n }\n\n if (params?.selector) {\n return watchMode ? \"realistic-click\" : \"click\";\n }\n\n if (params?.url?.trim() || extractFirstUrl(messageText)) {\n return params?.id ? \"navigate\" : \"open\";\n }\n\n return \"state\";\n}\n\nfunction normalizeBrowserAction(\n action: BrowserActionParameters[\"action\"] | undefined,\n): NormalizedBrowserAction | undefined {\n switch (action) {\n case \"realistic_click\":\n return \"realistic-click\";\n case \"realistic_fill\":\n return \"realistic-fill\";\n case \"realistic_type\":\n return \"realistic-type\";\n case \"realistic_press\":\n return \"realistic-press\";\n case \"cursor_move\":\n return \"cursor-move\";\n case \"cursor_hide\":\n return \"cursor-hide\";\n case \"autofill_login\":\n return \"autofill-login\";\n case \"wait_for_url\":\n case \"wait-for-url\":\n return \"wait-for-url\";\n default:\n return action as NormalizedBrowserAction | undefined;\n }\n}\n\nfunction normalizeLegacyBrowserAction(\n action: BrowserActionParameters[\"action\"] | undefined,\n): BrowserWorkspaceCommand[\"subaction\"] | undefined {\n const normalizedAction = normalizeBrowserAction(action);\n switch (normalizedAction) {\n case \"info\":\n case \"context\":\n case \"get_context\":\n return \"state\";\n case \"list_tabs\":\n case \"open_tab\":\n case \"close_tab\":\n case \"switch_tab\":\n return \"tab\";\n case \"autofill-login\":\n case \"wait-for-url\":\n return undefined;\n case undefined:\n return undefined;\n default:\n return isWorkspaceSubaction(normalizedAction)\n ? normalizedAction\n : undefined;\n }\n}\n\nfunction isWorkspaceSubaction(\n action: unknown,\n): action is BrowserWorkspaceCommand[\"subaction\"] {\n return (\n action === \"back\" ||\n action === \"click\" ||\n action === \"close\" ||\n action === \"forward\" ||\n action === \"get\" ||\n action === \"hide\" ||\n action === \"navigate\" ||\n action === \"open\" ||\n action === \"press\" ||\n action === \"reload\" ||\n action === \"screenshot\" ||\n action === \"show\" ||\n action === \"snapshot\" ||\n action === \"state\" ||\n action === \"tab\" ||\n action === \"type\" ||\n action === \"wait\" ||\n action === \"realistic-click\" ||\n action === \"realistic-fill\" ||\n action === \"realistic-type\" ||\n action === \"realistic-press\" ||\n action === \"cursor-move\" ||\n action === \"cursor-hide\"\n );\n}\n\nfunction normalizeLegacyTabAction(\n action: BrowserActionParameters[\"action\"] | undefined,\n): BrowserActionParameters[\"tabAction\"] | undefined {\n switch (normalizeBrowserAction(action)) {\n case \"list_tabs\":\n return \"list\";\n case \"open_tab\":\n return \"new\";\n case \"close_tab\":\n return \"close\";\n case \"switch_tab\":\n return \"switch\";\n default:\n return undefined;\n }\n}\n\nfunction formatBrowserSessionResult(\n command: BrowserWorkspaceCommand,\n result: Awaited<ReturnType<typeof executeBrowserWorkspaceCommand>>,\n): string {\n if (result.tabs) {\n const labels = result.tabs\n .map((tab) => `- ${tab.title} (${tab.url})`)\n .join(\"\\n\");\n return labels\n ? `Browser tabs (${result.mode}):\\n${labels}`\n : `No browser session tabs are open (${result.mode}).`;\n }\n\n if (result.closed) {\n return `Browser closed (${result.mode}).`;\n }\n\n if (result.tab) {\n return `${command.subaction} completed in ${result.mode} mode.\\n${result.tab.title}\\n${result.tab.url}`;\n }\n\n if (result.value !== undefined) {\n if (\n command.subaction === \"cursor-move\" &&\n result.value !== null &&\n typeof result.value === \"object\" &&\n \"x\" in result.value &&\n \"y\" in result.value\n ) {\n const cursor = result.value as { x: number; y: number };\n return `Cursor moved to (${Math.round(cursor.x)}, ${Math.round(cursor.y)}) in ${result.mode} mode.`;\n }\n const serialized =\n typeof result.value === \"string\"\n ? result.value\n : JSON.stringify(result.value, null, 2);\n return `Browser ${command.subaction} result (${result.mode}):\\n${serialized}`;\n }\n\n if (result.snapshot?.data) {\n return `Browser ${command.subaction} captured a preview in ${result.mode} mode.`;\n }\n\n return `Browser ${command.subaction} completed in ${result.mode} mode.`;\n}\n\nfunction browserProgressRationale(\n command: BrowserWorkspaceCommand,\n params: BrowserActionParameters | undefined,\n messageText: string,\n): string {\n const explicit = params?.rationale?.trim();\n if (explicit) return explicit;\n\n switch (command.subaction) {\n case \"open\":\n case \"navigate\":\n return command.url ? `open ${command.url}` : \"open requested page\";\n case \"click\":\n case \"realistic-click\":\n return command.selector\n ? `click ${command.selector}`\n : \"click requested target\";\n case \"type\":\n case \"realistic-fill\":\n case \"realistic-type\":\n return command.selector\n ? `fill ${command.selector}`\n : \"type requested text\";\n case \"press\":\n case \"realistic-press\":\n return command.key ? `press ${command.key}` : \"press requested key\";\n case \"tab\":\n return command.tabAction\n ? `${command.tabAction} browser tab`\n : \"manage browser tabs\";\n case \"wait\":\n return command.selector\n ? `wait for ${command.selector}`\n : \"wait for browser state\";\n case \"state\":\n return messageText.trim() || \"read browser state\";\n default:\n return `run browser ${command.subaction}`;\n }\n}\n\nfunction buildBrowserStepProgressContent(\n command: BrowserWorkspaceCommand,\n params: BrowserActionParameters | undefined,\n messageText: string,\n success: boolean,\n error?: string,\n): Content {\n const rationale = error\n ? `failed: ${error}`\n : browserProgressRationale(command, params, messageText);\n return {\n text: `Step 1: ${command.subaction} — ${rationale}`,\n source: \"action_progress\",\n merge: \"replace\",\n metadata: {\n transient: true,\n compactProgress: true,\n progress: {\n source: \"browser\",\n actionName: \"BROWSER\",\n step: 1,\n kind: command.subaction,\n rationale,\n success,\n error,\n },\n },\n };\n}\n\nasync function emitBrowserStepProgress(\n callback: HandlerCallback | undefined,\n command: BrowserWorkspaceCommand,\n params: BrowserActionParameters | undefined,\n messageText: string,\n success: boolean,\n error?: string,\n): Promise<void> {\n if (params?.streamProgress !== true || !callback) return;\n try {\n await callback(\n buildBrowserStepProgressContent(\n command,\n params,\n messageText,\n success,\n error,\n ),\n \"BROWSER\",\n );\n } catch (callbackError) {\n logger.warn(\n {\n src: \"plugin:browser\",\n action: command.subaction,\n error:\n callbackError instanceof Error\n ? callbackError.message\n : String(callbackError),\n },\n \"Failed to emit browser progress callback\",\n );\n }\n}\n\nfunction currentUrlFromResult(\n result: BrowserWorkspaceCommandResult,\n): string | null {\n if (result.tab?.url) {\n return result.tab.url;\n }\n if (typeof result.value === \"string\" && result.value.trim()) {\n return result.value.trim();\n }\n if (Array.isArray(result.tabs)) {\n const visible = result.tabs.find((tab) => tab.visible) ?? result.tabs[0];\n if (visible?.url) {\n return visible.url;\n }\n }\n return null;\n}\n\n/**\n * Reads the current tab URL via the active browser target. Prefers a targeted\n * `get url` (cheap), falling back to `state` for backends that don't implement\n * the get-url mode. Returns null when the URL is not yet readable.\n */\nasync function readCurrentBrowserUrl(\n browserService: BrowserService | null,\n target: BrowserTarget | undefined,\n tabId: string | undefined,\n): Promise<string | null> {\n const run = async (\n command: BrowserWorkspaceCommand,\n ): Promise<BrowserWorkspaceCommandResult> =>\n browserService\n ? browserService.execute(command, target)\n : executeBrowserWorkspaceCommand(command);\n\n try {\n const got = await run({\n subaction: \"get\",\n getMode: \"url\",\n id: tabId,\n });\n const url = currentUrlFromResult(got);\n if (url) {\n return url;\n }\n } catch (error) {\n logger.debug(\n `[BROWSER] wait_for_url get-url poll failed, falling back to state: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n\n try {\n const state = await run({ subaction: \"state\", id: tabId });\n return currentUrlFromResult(state);\n } catch (error) {\n logger.debug(\n `[BROWSER] wait_for_url state poll could not read URL: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n return null;\n }\n}\n\n/**\n * Handles `action: \"wait_for_url\"`. Optionally opens/navigates to a starting\n * URL, tells the user it is watching, then polls the tab URL against `pattern`,\n * streaming a status update each poll. Resolves with a typed success/timeout\n * result and never throws on timeout.\n */\nasync function executeBrowserWaitForUrl(\n runtime: Parameters<NonNullable<Action[\"handler\"]>>[0],\n params: BrowserActionParameters | undefined,\n messageText: string,\n callback: HandlerCallback | undefined,\n): Promise<ReturnType<NonNullable<Action[\"handler\"]>>> {\n const pattern = (params?.pattern ?? \"\").trim();\n if (!pattern) {\n const text =\n \"wait_for_url needs a `pattern` (substring or regex) to watch for.\";\n logger.warn(`[BROWSER] ${text}`);\n return {\n text,\n success: false,\n values: { success: false, error: \"BROWSER_WAIT_FOR_URL_NO_PATTERN\" },\n data: { actionName: \"BROWSER\", subaction: \"wait_for_url\" },\n };\n }\n\n const browserService =\n runtime.getService<BrowserService>(BROWSER_SERVICE_TYPE) ?? null;\n const target = params?.target;\n const startUrl =\n params?.url?.trim() || extractFirstUrl(messageText) || undefined;\n const timeoutMs = params?.timeoutMs ?? WAIT_FOR_URL_DEFAULT_TIMEOUT_MS;\n const pollIntervalMs =\n params?.pollIntervalMs ?? WAIT_FOR_URL_DEFAULT_POLL_INTERVAL_MS;\n\n let tabId = params?.id?.trim() || undefined;\n\n // Optionally launch the starting URL so the user can act on it.\n if (startUrl) {\n const openCommand: BrowserWorkspaceCommand = {\n subaction: tabId ? \"navigate\" : \"open\",\n url: startUrl,\n id: tabId,\n };\n try {\n const opened = browserService\n ? await browserService.execute(openCommand, target)\n : await executeBrowserWorkspaceCommand(openCommand);\n tabId = opened.tab?.id ?? tabId;\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n logger.warn(\n `[BROWSER] wait_for_url could not open ${startUrl}: ${reason}`,\n );\n return {\n text: `Couldn't open ${startUrl} to watch for \"${pattern}\": ${reason}`,\n success: false,\n values: { success: false, error: \"BROWSER_WAIT_FOR_URL_OPEN_FAILED\" },\n data: { actionName: \"BROWSER\", subaction: \"wait_for_url\" },\n };\n }\n }\n\n await callback?.({\n text: startUrl\n ? `I opened ${startUrl} — watching for \"${pattern}\" and I'll resume when it's reached.`\n : `Watching the current tab for \"${pattern}\" — I'll resume when it's reached.`,\n });\n\n logger.info(\n `[BROWSER] wait_for_url pattern=\"${pattern}\" timeoutMs=${timeoutMs} pollIntervalMs=${pollIntervalMs} target=${target ?? \"auto\"}`,\n );\n\n const outcome = await waitForUrl(\n { pattern, timeoutMs, pollIntervalMs },\n {\n getCurrentUrl: () => readCurrentBrowserUrl(browserService, target, tabId),\n emitStatus: async (text) => {\n await callback?.({ text });\n },\n },\n );\n\n return {\n text: outcome.message,\n success: outcome.matched,\n userFacingText: outcome.message,\n values: {\n success: outcome.matched,\n subaction: \"wait_for_url\",\n status: outcome.status,\n matched: outcome.matched,\n polls: outcome.polls,\n },\n data: {\n actionName: \"BROWSER\",\n subaction: \"wait_for_url\",\n outcome,\n },\n };\n}\n\nexport const browserAction: Action = {\n name: \"BROWSER\",\n contexts: [\"browser\", \"web\", \"automation\", \"secrets\"],\n roleGate: { minRole: \"OWNER\" },\n similes: [\n \"BROWSE_SITE\",\n \"BROWSER_SESSION\",\n \"CONTROL_BROWSER\",\n \"CONTROL_BROWSER_SESSION\",\n \"MANAGE_ELIZA_BROWSER_WORKSPACE\",\n \"NAVIGATE_SITE\",\n \"OPEN_SITE\",\n \"USE_BROWSER\",\n \"BROWSER_ACTION\",\n \"BROWSER_AUTOFILL_LOGIN\",\n \"AGENT_AUTOFILL\",\n \"AUTOFILL_BROWSER_LOGIN\",\n \"AUTOFILL_LOGIN\",\n \"FILL_BROWSER_CREDENTIALS\",\n \"LOG_INTO_SITE\",\n \"SIGN_IN_TO_SITE\",\n ],\n description:\n \"BROWSER action. Control registered browser target: app workspace, bridge Chrome/Safari companion, computeruse Chromium, or Stagehand fallback. BrowserService picks target if omitted. action=autofill_login + domain vault-gated autofills open workspace tab. action=wait_for_url + pattern opens an optional url then watches the tab and resumes when its URL matches (OAuth callback, deploy/CI done), streaming progress.\",\n descriptionCompressed:\n \"Browser open|navigate|click|type|screenshot|state|autofill_login|wait_for_url; bridge status elsewhere\",\n validate: async () => true,\n handler: async (\n runtime,\n message,\n _state,\n options,\n callback?: HandlerCallback,\n ) => {\n const params = (options as HandlerOptions | undefined)?.parameters as\n | BrowserActionParameters\n | undefined;\n const messageText = getMessageText(message);\n const subaction = inferBrowserSubaction(params, messageText);\n\n if (subaction === \"autofill-login\") {\n const { executeBrowserAutofillLogin } = await import(\n \"./browser-autofill-login.js\"\n );\n return executeBrowserAutofillLogin(runtime, message, options);\n }\n\n if (subaction === \"wait-for-url\") {\n return executeBrowserWaitForUrl(runtime, params, messageText, callback);\n }\n\n const url =\n params?.url?.trim() || extractFirstUrl(messageText) || undefined;\n\n const command: BrowserWorkspaceCommand = {\n id: params?.id?.trim(),\n key: params?.key?.trim(),\n pixels: params?.pixels,\n script: params?.script,\n selector: params?.selector?.trim(),\n subaction,\n tabAction: params?.tabAction ?? normalizeLegacyTabAction(params?.action),\n text: params?.text,\n value: params?.text,\n timeoutMs: params?.timeoutMs,\n url,\n cursorDurationMs: params?.cursorDurationMs,\n perCharDelayMs: params?.perCharDelayMs,\n replace: params?.replace,\n x: params?.x,\n y: params?.y,\n };\n\n const browserService =\n runtime.getService<BrowserService>(BROWSER_SERVICE_TYPE);\n\n try {\n logger.info(\n `[BROWSER] ${command.subaction} via target=${params?.target ?? \"auto\"} (workspace mode=${getBrowserWorkspaceMode(process.env)})`,\n );\n const result = browserService\n ? await browserService.execute(command, params?.target)\n : await executeBrowserWorkspaceCommand(command);\n await emitBrowserStepProgress(\n callback,\n command,\n params,\n messageText,\n true,\n );\n\n return {\n text: formatBrowserSessionResult(command, result),\n success: true,\n values: {\n success: true,\n mode: result.mode,\n subaction: result.subaction,\n },\n data: {\n actionName: \"BROWSER\",\n command,\n result,\n },\n };\n } catch (error) {\n const errorText =\n error instanceof Error ? error.message : \"Browser action failed\";\n logger.warn(`[BROWSER] Failed: ${errorText}`);\n await emitBrowserStepProgress(\n callback,\n command,\n params,\n messageText,\n false,\n errorText,\n );\n return {\n text: `Browser action failed: ${errorText}`,\n success: false,\n values: { success: false, error: \"BROWSER_FAILED\" },\n data: {\n actionName: \"BROWSER\",\n command,\n },\n };\n }\n },\n parameters: [\n {\n name: \"target\",\n description:\n \"Optional browser target id. Common values: workspace, bridge, computeruse, stagehand.\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"streamProgress\",\n description:\n \"When true, emit a compact Step 1 progress callback after the browser command dispatches.\",\n required: false,\n schema: { type: \"boolean\" as const, default: false },\n },\n {\n name: \"rationale\",\n description: \"Optional rationale shown in streamProgress callback text.\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"action\",\n description:\n \"Browser action. Snake_case canonical; legacy kebab-case and subaction accepted.\",\n required: false,\n schema: {\n type: \"string\" as const,\n enum: [\n \"back\",\n \"click\",\n \"close\",\n \"context\",\n \"forward\",\n \"get\",\n \"get_context\",\n \"hide\",\n \"info\",\n \"list_tabs\",\n \"navigate\",\n \"open\",\n \"open_tab\",\n \"press\",\n \"reload\",\n \"screenshot\",\n \"show\",\n \"snapshot\",\n \"state\",\n \"tab\",\n \"type\",\n \"wait\",\n \"close_tab\",\n \"switch_tab\",\n \"realistic_click\",\n \"realistic_fill\",\n \"realistic_type\",\n \"realistic_press\",\n \"cursor_move\",\n \"cursor_hide\",\n \"autofill_login\",\n \"wait_for_url\",\n ],\n },\n },\n {\n name: \"pattern\",\n description:\n \"For action=wait_for_url: substring or /regex/ to match the tab URL (e.g. callback?code=, or /\\\\/done$/).\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"pollIntervalMs\",\n description: \"For action=wait_for_url: poll cadence in ms. Default 2000.\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"tabAction\",\n description: \"Tab operation when subaction is tab\",\n required: false,\n schema: {\n type: \"string\" as const,\n enum: [\"close\", \"list\", \"new\", \"switch\"],\n },\n },\n {\n name: \"domain\",\n description:\n \"Required for action=autofill_login: registrable hostname, e.g. github.com.\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"username\",\n description: \"For autofill-login: saved login username; omit for latest.\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"submit\",\n description: \"For autofill-login: submit after filling. Default false.\",\n required: false,\n schema: { type: \"boolean\" as const },\n },\n {\n name: \"id\",\n description: \"Session or tab id to target\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"url\",\n description: \"URL for open or navigate\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"selector\",\n description: \"Selector for click, type, or wait\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"text\",\n description: \"Text for type\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"key\",\n description: \"Keyboard key for press\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"pixels\",\n description: \"Scroll distance in pixels\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"timeoutMs\",\n description: \"Command timeout in milliseconds\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"script\",\n description: \"Script for eval\",\n required: false,\n schema: { type: \"string\" as const },\n },\n {\n name: \"watchMode\",\n description:\n \"User watching hint; prefer realistic-* click/fill, visible cursor, pointer events.\",\n required: false,\n schema: { type: \"boolean\" as const },\n },\n {\n name: \"cursorDurationMs\",\n description: \"Cursor animation duration (ms) for realistic-* subactions\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"perCharDelayMs\",\n description: \"Per-character delay for realistic-type/realistic-fill (ms)\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"replace\",\n description: \"For realistic-fill: replace existing input, not append.\",\n required: false,\n schema: { type: \"boolean\" as const },\n },\n {\n name: \"x\",\n description: \"Cursor target X (CSS pixels) for cursor-move\",\n required: false,\n schema: { type: \"number\" as const },\n },\n {\n name: \"y\",\n description: \"Cursor target Y (CSS pixels) for cursor-move\",\n required: false,\n schema: { type: \"number\" as const },\n },\n ],\n examples: [\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Open elizaos.ai in a new browser tab.\",\n },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: \"open completed in desktop mode.\\nelizaOS\\nhttps://elizaos.ai\",\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Click the sign-in button on that page.\",\n },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: \"click completed in desktop mode.\",\n },\n },\n ],\n [\n {\n name: \"{{name1}}\",\n content: {\n text: \"Open the GitHub OAuth page and let me know when it redirects back to our callback.\",\n },\n },\n {\n name: \"{{agentName}}\",\n content: {\n text: \"I opened https://github.com/login/oauth/authorize — watching for \\\"callback?code=\\\" and I'll resume when it's reached.\",\n actions: [\"BROWSER\"],\n },\n },\n ],\n ] as ActionExample[][],\n};\n"],"mappings":"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","names":[]}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Pure URL-matching predicate for the BROWSER `wait_for_url` subaction.
3
+ *
4
+ * A `pattern` is treated as a regular expression only when it is written as a
5
+ * `/.../` literal (with optional flags); any other pattern is a
6
+ * case-insensitive substring match. This keeps ordinary URL fragments like
7
+ * `callback?code=` (which contain regex metacharacters) predictable. An invalid
8
+ * regex literal always falls back to a substring match so the agent never
9
+ * crashes on user input.
10
+ *
11
+ * Kept free of any browser/runtime imports so it stays trivially unit-testable.
12
+ */
13
+ /** How a given pattern was interpreted when building the predicate. */
14
+ export type WaitForUrlPatternKind = "regex" | "substring";
15
+ export interface WaitForUrlPredicate {
16
+ /** The original, untrimmed pattern the caller supplied. */
17
+ readonly pattern: string;
18
+ /** How the pattern was interpreted ("regex" or "substring"). */
19
+ readonly kind: WaitForUrlPatternKind;
20
+ /** Returns true when `url` satisfies the pattern. */
21
+ test(url: string): boolean;
22
+ }
23
+ /**
24
+ * Build a {@link WaitForUrlPredicate} from a caller-supplied pattern.
25
+ *
26
+ * - `"/foo\\d+/i"` → regex `/foo\d+/i`.
27
+ * - `"/\\/done$/"` → regex.
28
+ * - `"callback?code="` → substring (case-insensitive), even though it contains
29
+ * regex metacharacters.
30
+ * - An invalid `/.../ ` literal → falls back to a case-insensitive substring
31
+ * match on the original pattern text.
32
+ */
33
+ export declare function buildWaitForUrlPredicate(pattern: string): WaitForUrlPredicate;
34
+ //# sourceMappingURL=wait-for-url-predicate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wait-for-url-predicate.d.ts","sourceRoot":"","sources":["../../src/actions/wait-for-url-predicate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,uEAAuE;AACvE,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,WAAW,CAAC;AAE1D,MAAM,WAAW,mBAAmB;IAClC,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,gEAAgE;IAChE,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,qDAAqD;IACrD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5B;AAYD;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB,CAwB7E"}
@@ -0,0 +1,33 @@
1
+ const REGEX_LITERAL = /^\/(.+)\/([a-z]*)$/i;
2
+ function compileRegex(source, flags) {
3
+ try {
4
+ return new RegExp(source, flags);
5
+ } catch {
6
+ return null;
7
+ }
8
+ }
9
+ function buildWaitForUrlPredicate(pattern) {
10
+ const trimmed = pattern.trim();
11
+ const literalMatch = trimmed.match(REGEX_LITERAL);
12
+ if (literalMatch) {
13
+ const [, source, flags] = literalMatch;
14
+ const compiled = compileRegex(source, flags || "");
15
+ if (compiled) {
16
+ return {
17
+ pattern,
18
+ kind: "regex",
19
+ test: (url) => compiled.test(url)
20
+ };
21
+ }
22
+ }
23
+ const needle = trimmed.toLowerCase();
24
+ return {
25
+ pattern,
26
+ kind: "substring",
27
+ test: (url) => needle.length === 0 ? false : url.toLowerCase().includes(needle)
28
+ };
29
+ }
30
+ export {
31
+ buildWaitForUrlPredicate
32
+ };
33
+ //# sourceMappingURL=wait-for-url-predicate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/actions/wait-for-url-predicate.ts"],"sourcesContent":["/**\n * Pure URL-matching predicate for the BROWSER `wait_for_url` subaction.\n *\n * A `pattern` is treated as a regular expression only when it is written as a\n * `/.../` literal (with optional flags); any other pattern is a\n * case-insensitive substring match. This keeps ordinary URL fragments like\n * `callback?code=` (which contain regex metacharacters) predictable. An invalid\n * regex literal always falls back to a substring match so the agent never\n * crashes on user input.\n *\n * Kept free of any browser/runtime imports so it stays trivially unit-testable.\n */\n\n/** How a given pattern was interpreted when building the predicate. */\nexport type WaitForUrlPatternKind = \"regex\" | \"substring\";\n\nexport interface WaitForUrlPredicate {\n /** The original, untrimmed pattern the caller supplied. */\n readonly pattern: string;\n /** How the pattern was interpreted (\"regex\" or \"substring\"). */\n readonly kind: WaitForUrlPatternKind;\n /** Returns true when `url` satisfies the pattern. */\n test(url: string): boolean;\n}\n\nconst REGEX_LITERAL = /^\\/(.+)\\/([a-z]*)$/i;\n\nfunction compileRegex(source: string, flags: string): RegExp | null {\n try {\n return new RegExp(source, flags);\n } catch {\n return null;\n }\n}\n\n/**\n * Build a {@link WaitForUrlPredicate} from a caller-supplied pattern.\n *\n * - `\"/foo\\\\d+/i\"` → regex `/foo\\d+/i`.\n * - `\"/\\\\/done$/\"` → regex.\n * - `\"callback?code=\"` → substring (case-insensitive), even though it contains\n * regex metacharacters.\n * - An invalid `/.../ ` literal → falls back to a case-insensitive substring\n * match on the original pattern text.\n */\nexport function buildWaitForUrlPredicate(pattern: string): WaitForUrlPredicate {\n const trimmed = pattern.trim();\n\n const literalMatch = trimmed.match(REGEX_LITERAL);\n if (literalMatch) {\n const [, source, flags] = literalMatch;\n const compiled = compileRegex(source, flags || \"\");\n if (compiled) {\n return {\n pattern,\n kind: \"regex\",\n test: (url: string) => compiled.test(url),\n };\n }\n // Invalid regex literal: fall through to substring on the raw pattern.\n }\n\n const needle = trimmed.toLowerCase();\n return {\n pattern,\n kind: \"substring\",\n test: (url: string) =>\n needle.length === 0 ? false : url.toLowerCase().includes(needle),\n };\n}\n"],"mappings":"AAyBA,MAAM,gBAAgB;AAEtB,SAAS,aAAa,QAAgB,OAA8B;AAClE,MAAI;AACF,WAAO,IAAI,OAAO,QAAQ,KAAK;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAYO,SAAS,yBAAyB,SAAsC;AAC7E,QAAM,UAAU,QAAQ,KAAK;AAE7B,QAAM,eAAe,QAAQ,MAAM,aAAa;AAChD,MAAI,cAAc;AAChB,UAAM,CAAC,EAAE,QAAQ,KAAK,IAAI;AAC1B,UAAM,WAAW,aAAa,QAAQ,SAAS,EAAE;AACjD,QAAI,UAAU;AACZ,aAAO;AAAA,QACL;AAAA,QACA,MAAM;AAAA,QACN,MAAM,CAAC,QAAgB,SAAS,KAAK,GAAG;AAAA,MAC1C;AAAA,IACF;AAAA,EAEF;AAEA,QAAM,SAAS,QAAQ,YAAY;AACnC,SAAO;AAAA,IACL;AAAA,IACA,MAAM;AAAA,IACN,MAAM,CAAC,QACL,OAAO,WAAW,IAAI,QAAQ,IAAI,YAAY,EAAE,SAAS,MAAM;AAAA,EACnE;AACF;","names":[]}