browser-use 0.6.0 → 0.7.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 (85) hide show
  1. package/README.md +29 -18
  2. package/dist/actor/element.js +24 -3
  3. package/dist/actor/mouse.js +21 -3
  4. package/dist/actor/page.js +33 -11
  5. package/dist/agent/gif.js +28 -3
  6. package/dist/agent/message-manager/service.js +2 -22
  7. package/dist/agent/message-manager/utils.js +15 -2
  8. package/dist/agent/message-manager/views.d.ts +7 -7
  9. package/dist/agent/message-manager/views.js +1 -0
  10. package/dist/agent/prompts.d.ts +3 -0
  11. package/dist/agent/prompts.js +22 -12
  12. package/dist/agent/service.d.ts +9 -1
  13. package/dist/agent/service.js +215 -81
  14. package/dist/agent/system_prompt.md +12 -11
  15. package/dist/agent/system_prompt_anthropic_flash.md +6 -5
  16. package/dist/agent/system_prompt_no_thinking.md +12 -11
  17. package/dist/agent/views.d.ts +2 -0
  18. package/dist/agent/views.js +48 -36
  19. package/dist/browser/extensions.js +20 -10
  20. package/dist/browser/profile.d.ts +4 -0
  21. package/dist/browser/profile.js +107 -4
  22. package/dist/browser/session.d.ts +28 -1
  23. package/dist/browser/session.js +1436 -528
  24. package/dist/browser/watchdogs/default-action-watchdog.js +32 -3
  25. package/dist/browser/watchdogs/downloads-watchdog.d.ts +4 -0
  26. package/dist/browser/watchdogs/downloads-watchdog.js +105 -9
  27. package/dist/browser/watchdogs/har-recording-watchdog.d.ts +1 -0
  28. package/dist/browser/watchdogs/har-recording-watchdog.js +54 -2
  29. package/dist/browser/watchdogs/permissions-watchdog.d.ts +5 -0
  30. package/dist/browser/watchdogs/permissions-watchdog.js +106 -3
  31. package/dist/browser/watchdogs/recording-watchdog.d.ts +2 -0
  32. package/dist/browser/watchdogs/recording-watchdog.js +54 -2
  33. package/dist/browser/watchdogs/security-watchdog.d.ts +1 -0
  34. package/dist/browser/watchdogs/security-watchdog.js +47 -7
  35. package/dist/browser/watchdogs/storage-state-watchdog.d.ts +6 -0
  36. package/dist/browser/watchdogs/storage-state-watchdog.js +206 -14
  37. package/dist/cli.d.ts +13 -2
  38. package/dist/cli.js +188 -8
  39. package/dist/code-use/namespace.js +52 -7
  40. package/dist/code-use/notebook-export.js +18 -2
  41. package/dist/code-use/service.js +1 -0
  42. package/dist/config.js +27 -5
  43. package/dist/controller/action-timeout.d.ts +9 -0
  44. package/dist/controller/action-timeout.js +95 -0
  45. package/dist/controller/registry/service.d.ts +1 -0
  46. package/dist/controller/registry/service.js +28 -1
  47. package/dist/controller/registry/views.d.ts +2 -0
  48. package/dist/controller/registry/views.js +44 -17
  49. package/dist/controller/service.d.ts +2 -1
  50. package/dist/controller/service.js +494 -329
  51. package/dist/filesystem/file-system.js +38 -8
  52. package/dist/integrations/gmail/service.js +30 -6
  53. package/dist/llm/browser-use/chat.js +2 -2
  54. package/dist/llm/codex/auth.d.ts +118 -0
  55. package/dist/llm/codex/auth.js +599 -0
  56. package/dist/llm/codex/chat.d.ts +70 -0
  57. package/dist/llm/codex/chat.js +392 -0
  58. package/dist/llm/codex/index.d.ts +2 -0
  59. package/dist/llm/codex/index.js +2 -0
  60. package/dist/llm/google/chat.js +18 -1
  61. package/dist/logging-config.js +22 -11
  62. package/dist/mcp/client.d.ts +1 -0
  63. package/dist/mcp/client.js +12 -10
  64. package/dist/mcp/redaction.d.ts +3 -0
  65. package/dist/mcp/redaction.js +132 -0
  66. package/dist/mcp/server.d.ts +2 -0
  67. package/dist/mcp/server.js +64 -22
  68. package/dist/observability.js +1 -1
  69. package/dist/screenshots/service.js +25 -2
  70. package/dist/skill-cli/direct.d.ts +4 -1
  71. package/dist/skill-cli/direct.js +260 -64
  72. package/dist/skill-cli/server.d.ts +1 -0
  73. package/dist/skill-cli/server.js +115 -25
  74. package/dist/skill-cli/tunnel.d.ts +1 -0
  75. package/dist/skill-cli/tunnel.js +16 -4
  76. package/dist/sync/auth.js +22 -9
  77. package/dist/telemetry/service.js +21 -2
  78. package/dist/telemetry/views.js +31 -8
  79. package/dist/tokens/custom-pricing.js +2 -2
  80. package/dist/tokens/openrouter-pricing.d.ts +11 -0
  81. package/dist/tokens/openrouter-pricing.js +102 -0
  82. package/dist/tokens/service.js +20 -16
  83. package/dist/utils.d.ts +3 -1
  84. package/dist/utils.js +4 -2
  85. package/package.json +75 -33
@@ -10,10 +10,11 @@ import { CONFIG } from '../config.js';
10
10
  import { EventBus } from '../event-bus.js';
11
11
  import { uuid7str, SignalHandler, get_browser_use_version, check_latest_browser_use_version, sanitize_surrogates, } from '../utils.js';
12
12
  import { Controller as DefaultController } from '../controller/service.js';
13
+ import { isActionTimeoutError, runActionWithTimeout, } from '../controller/action-timeout.js';
13
14
  import { FileSystem as AgentFileSystem, DEFAULT_FILE_SYSTEM_PATH, } from '../filesystem/file-system.js';
14
15
  import { SystemPrompt, get_ai_step_system_prompt, get_ai_step_user_prompt, get_rerun_summary_message, get_rerun_summary_prompt, } from './prompts.js';
15
16
  import { MessageManager } from './message-manager/service.js';
16
- import { BrowserStateHistory } from '../browser/views.js';
17
+ import { BrowserError, BrowserStateHistory, } from '../browser/views.js';
17
18
  import { BrowserSession } from '../browser/session.js';
18
19
  import { BrowserProfile, DEFAULT_BROWSER_PROFILE } from '../browser/profile.js';
19
20
  import { HistoryTreeProcessor } from '../dom/history-tree-processor/service.js';
@@ -24,7 +25,7 @@ import { ChatBrowserUse } from '../llm/browser-use/chat.js';
24
25
  import { ModelProviderError, ModelRateLimitError } from '../llm/exceptions.js';
25
26
  import { AssistantMessage, ContentPartTextParam, SystemMessage, UserMessage, } from '../llm/messages.js';
26
27
  import { getLlmByName } from '../llm/models.js';
27
- import { ActionResult, AgentHistory, AgentHistoryList, AgentOutput, AgentState, AgentStepInfo, AgentError, StepMetadata, ActionModel, PlanItem, defaultMessageCompactionSettings, normalizeMessageCompactionSettings, } from './views.js';
28
+ import { ActionResult, AgentHistory, AgentHistoryList, AgentOutput, AgentState, AgentStepInfo, AgentError, StepMetadata, ActionModel, PlanItem, defaultMessageCompactionSettings, normalizeMessageCompactionSettings, redactSensitiveDataFromString, } from './views.js';
28
29
  import { detect_variables_in_history, substitute_in_dict, } from './variable-detector.js';
29
30
  import { CreateAgentOutputFileEvent, CreateAgentSessionEvent, CreateAgentTaskEvent, CreateAgentStepEvent, UpdateAgentTaskEvent, } from './cloud-events.js';
30
31
  import { create_history_gif } from './gif.js';
@@ -34,31 +35,33 @@ import { AgentTelemetryEvent } from '../telemetry/views.js';
34
35
  import { TokenCost } from '../tokens/service.js';
35
36
  import { construct_judge_messages, construct_simple_judge_messages, } from './judge.js';
36
37
  import { CloudSkillService, MissingCookieException, build_skill_parameters_schema, get_skill_slug, } from '../skills/index.js';
37
- loadEnv();
38
+ loadEnv({ quiet: true });
38
39
  const logger = createLogger('browser_use.agent');
39
40
  const URL_PATTERN = /https?:\/\/[^\s<>"']+|www\.[^\s<>"']+|[^\s<>"']+\.[a-z]{2,}(?:\/[^\s<>"']*)?/gi;
40
- export const log_response = (response, registry, logInstance = logger) => {
41
+ export const log_response = (response, registry, logInstance = logger, sensitive_data = null) => {
42
+ const redact = (value) => redactSensitiveDataFromString(value, sensitive_data);
41
43
  if (response.current_state.thinking) {
42
- logInstance.debug(`💡 Thinking:\n${response.current_state.thinking}`);
44
+ logInstance.debug(`💡 Thinking:\n${redact(response.current_state.thinking)}`);
43
45
  }
44
46
  const evalGoal = response.current_state.evaluation_previous_goal;
45
47
  if (evalGoal) {
48
+ const redactedEvalGoal = redact(evalGoal);
46
49
  if (evalGoal.toLowerCase().includes('success')) {
47
- logInstance.info(` \x1b[32m👍 Eval: ${evalGoal}\x1b[0m`);
50
+ logInstance.info(` \x1b[32m👍 Eval: ${redactedEvalGoal}\x1b[0m`);
48
51
  }
49
52
  else if (evalGoal.toLowerCase().includes('failure')) {
50
- logInstance.info(` \x1b[31m⚠️ Eval: ${evalGoal}\x1b[0m`);
53
+ logInstance.info(` \x1b[31m⚠️ Eval: ${redactedEvalGoal}\x1b[0m`);
51
54
  }
52
55
  else {
53
- logInstance.info(` ❔ Eval: ${evalGoal}`);
56
+ logInstance.info(` ❔ Eval: ${redactedEvalGoal}`);
54
57
  }
55
58
  }
56
59
  if (response.current_state.memory) {
57
- logInstance.info(` 🧠 Memory: ${response.current_state.memory}`);
60
+ logInstance.info(` 🧠 Memory: ${redact(response.current_state.memory)}`);
58
61
  }
59
62
  const nextGoal = response.current_state.next_goal;
60
63
  if (nextGoal) {
61
- logInstance.info(` \x1b[34m🎯 Next goal: ${nextGoal}\x1b[0m`);
64
+ logInstance.info(` \x1b[34m🎯 Next goal: ${redact(nextGoal)}\x1b[0m`);
62
65
  }
63
66
  };
64
67
  class AsyncMutex {
@@ -103,8 +106,10 @@ class ExecutionTimeoutError extends Error {
103
106
  }
104
107
  }
105
108
  const ensureDir = (target) => {
106
- if (!fs.existsSync(target)) {
107
- fs.mkdirSync(target, { recursive: true });
109
+ const existed = fs.existsSync(target);
110
+ fs.mkdirSync(target, { recursive: true, mode: 0o700 });
111
+ if (!existed && process.platform !== 'win32') {
112
+ fs.chmodSync(target, 0o700);
108
113
  }
109
114
  };
110
115
  const resolve_agent_llm = (llm) => {
@@ -183,6 +188,11 @@ const defaultAgentOptions = () => ({
183
188
  loop_detection_enabled: true,
184
189
  _url_shortening_limit: 25,
185
190
  });
191
+ const chmodPrivateFile = async (filePath) => {
192
+ if (process.platform !== 'win32') {
193
+ await fs.promises.chmod(filePath, 0o600);
194
+ }
195
+ };
186
196
  const AgentLLMOutputSchema = z.object({
187
197
  thinking: z.string().optional().nullable(),
188
198
  evaluation_previous_goal: z.string().optional().nullable(),
@@ -290,6 +300,9 @@ export class Agent {
290
300
  _url_shortening_limit = 25;
291
301
  skill_service = null;
292
302
  _skills_registered = false;
303
+ _redactSensitiveText(value) {
304
+ return redactSensitiveDataFromString(value, this.sensitive_data ?? null);
305
+ }
293
306
  constructor(params) {
294
307
  const { task, llm, page = null, browser = null, browser_context = null, browser_profile = null, browser_session = null, tools = null, controller = null, sensitive_data = null, initial_actions = null, directly_open_url = true, register_new_step_callback = null, register_done_callback = null, register_should_stop_callback = null, register_external_agent_status_raise_error_callback = null, output_model_schema = null, extraction_schema = null, use_vision = true, include_recent_events = false, sample_images = null, llm_screenshot_size = null, save_conversation_path = null, save_conversation_path_encoding = 'utf-8', max_failures = 3, override_system_message = null, extend_system_message = null, generate_gif = false, available_file_paths = [], include_attributes, max_actions_per_step = 5, use_thinking = true, flash_mode = false, use_judge = true, ground_truth = null, max_history_items = null, page_extraction_llm = null, fallback_llm = null, judge_llm = null, skill_ids = null, skills = null, skill_service = null, enable_planning = true, planning_replan_on_stall = 3, planning_exploration_limit = 5, context = null, source = null, file_system_path = null, task_id = null, cloud_sync = null, calculate_cost = false, display_files_in_done_text = true, include_tool_call_examples = false, vision_detail_level = 'auto', session_attachment_mode = 'copy', llm_timeout = null, step_timeout = 180, final_response_after_failure = true, message_compaction = true, loop_detection_window = 20, loop_detection_enabled = true, _url_shortening_limit = 25, } = { ...defaultAgentOptions(), ...params };
295
308
  const resolvedLlm = resolve_agent_llm(llm);
@@ -408,7 +421,7 @@ export class Agent {
408
421
  const extractedUrl = this._extract_start_url(task);
409
422
  if (extractedUrl) {
410
423
  this.initial_url = extractedUrl;
411
- this.logger.info(`🔗 Found URL in task: ${extractedUrl}, adding as initial action...`);
424
+ this.logger.info(`🔗 Found URL in task: ${this._redactSensitiveText(extractedUrl)}, adding as initial action...`);
412
425
  resolvedInitialActions = [
413
426
  { go_to_url: { url: extractedUrl, new_tab: false } },
414
427
  ];
@@ -1189,7 +1202,7 @@ export class Agent {
1189
1202
  }
1190
1203
  }
1191
1204
  if (shouldExclude) {
1192
- this.logger.debug(`Excluding URL with file extension from auto-navigation: ${url}`);
1205
+ this.logger.debug(`Excluding URL with file extension from auto-navigation: ${this._redactSensitiveText(url)}`);
1193
1206
  continue;
1194
1207
  }
1195
1208
  const contextStart = Math.max(0, startIndex - 20);
@@ -1197,7 +1210,7 @@ export class Agent {
1197
1210
  .slice(contextStart, startIndex)
1198
1211
  .toLowerCase();
1199
1212
  if (excludedWords.some((word) => contextText.includes(word))) {
1200
- this.logger.debug(`Excluding URL with word in excluded words from auto-navigation: ${url} (context: "${contextText.trim()}")`);
1213
+ this.logger.debug(`Excluding URL with word in excluded words from auto-navigation: ${this._redactSensitiveText(url)} (context: "${this._redactSensitiveText(contextText.trim())}")`);
1201
1214
  continue;
1202
1215
  }
1203
1216
  if (!url.startsWith('http://') && !url.startsWith('https://')) {
@@ -1288,9 +1301,9 @@ export class Agent {
1288
1301
  }
1289
1302
  }
1290
1303
  const paramStr = paramSummary.length > 0 ? `(${paramSummary.join(', ')})` : '';
1291
- actionDetails.push(`${actionName}${paramStr}`);
1304
+ actionDetails.push(this._redactSensitiveText(`${actionName}${paramStr}`));
1292
1305
  lastActionName = actionName;
1293
- lastParamStr = paramStr;
1306
+ lastParamStr = this._redactSensitiveText(paramStr);
1294
1307
  }
1295
1308
  // Create summary based on single vs multi-action
1296
1309
  if (actionCount === 1) {
@@ -1355,6 +1368,21 @@ export class Agent {
1355
1368
  this.DoneAgentOutput = AgentOutput.type_with_custom_actions_no_thinking(this.DoneActionModel);
1356
1369
  }
1357
1370
  }
1371
+ _filter_cookies_for_domain_policy(browser_session, cookies) {
1372
+ const checker = browser_session._get_cookie_access_denial_reason;
1373
+ if (typeof checker !== 'function') {
1374
+ return cookies;
1375
+ }
1376
+ return cookies.filter((cookie) => {
1377
+ try {
1378
+ return checker.call(browser_session, cookie) == null;
1379
+ }
1380
+ catch (error) {
1381
+ this.logger.debug(`Skipping skill cookie because domain policy check failed: ${error.message}`);
1382
+ return false;
1383
+ }
1384
+ });
1385
+ }
1358
1386
  async _register_skills_as_actions() {
1359
1387
  if (!this.skill_service || this._skills_registered) {
1360
1388
  return;
@@ -1386,8 +1414,9 @@ export class Agent {
1386
1414
  }
1387
1415
  try {
1388
1416
  const cookiesRaw = await browser_session.get_cookies();
1389
- const cookies = Array.isArray(cookiesRaw)
1390
- ? cookiesRaw
1417
+ const allowedCookies = this._filter_cookies_for_domain_policy(browser_session, Array.isArray(cookiesRaw) ? cookiesRaw : []);
1418
+ const cookies = allowedCookies.length > 0
1419
+ ? allowedCookies
1391
1420
  .map((cookie) => {
1392
1421
  const record = cookie && typeof cookie === 'object'
1393
1422
  ? cookie
@@ -1455,18 +1484,17 @@ export class Agent {
1455
1484
  return '';
1456
1485
  }
1457
1486
  const currentCookies = await this.browser_session.get_cookies();
1487
+ const allowedCookies = this._filter_cookies_for_domain_policy(this.browser_session, Array.isArray(currentCookies) ? currentCookies : []);
1458
1488
  const cookieNames = new Set();
1459
- if (Array.isArray(currentCookies)) {
1460
- for (const cookie of currentCookies) {
1461
- if (!cookie || typeof cookie !== 'object') {
1462
- continue;
1463
- }
1464
- const name = typeof cookie.name === 'string'
1465
- ? String(cookie.name)
1466
- : '';
1467
- if (name) {
1468
- cookieNames.add(name);
1469
- }
1489
+ for (const cookie of allowedCookies) {
1490
+ if (!cookie || typeof cookie !== 'object') {
1491
+ continue;
1492
+ }
1493
+ const name = typeof cookie.name === 'string'
1494
+ ? String(cookie.name)
1495
+ : '';
1496
+ if (name) {
1497
+ cookieNames.add(name);
1470
1498
  }
1471
1499
  }
1472
1500
  const unavailableSkills = [];
@@ -1516,9 +1544,10 @@ export class Agent {
1516
1544
  return lines.join('\n');
1517
1545
  }
1518
1546
  catch (error) {
1519
- this.logger.error(`Error getting unavailable skills info: ${error instanceof Error
1547
+ const message = error instanceof Error
1520
1548
  ? `${error.name}: ${error.message}`
1521
- : String(error)}`);
1549
+ : String(error);
1550
+ this.logger.error(`Error getting unavailable skills info: ${this._redactSensitiveText(message)}`);
1522
1551
  return '';
1523
1552
  }
1524
1553
  }
@@ -1669,7 +1698,7 @@ export class Agent {
1669
1698
  const isTimeout = error instanceof ExecutionTimeoutError;
1670
1699
  if (isTimeout) {
1671
1700
  const timeoutMessage = `Step ${currentStep + 1} timed out after ${this.settings.step_timeout} seconds`;
1672
- this.logger.error(`⏰ ${timeoutMessage}`);
1701
+ this.logger.error(`⏰ ${this._redactSensitiveText(timeoutMessage)}`);
1673
1702
  this.state.consecutive_failures += 1;
1674
1703
  this.state.last_result = [
1675
1704
  new ActionResult({ error: timeoutMessage }),
@@ -1680,7 +1709,7 @@ export class Agent {
1680
1709
  agent_run_error = timeoutMessage;
1681
1710
  break;
1682
1711
  }
1683
- this.logger.error(`❌ Unhandled step error at step ${currentStep + 1}: ${message}`);
1712
+ this.logger.error(`❌ Unhandled step error at step ${currentStep + 1}: ${this._redactSensitiveText(message)}`);
1684
1713
  this.state.consecutive_failures += 1;
1685
1714
  this.state.last_result = [
1686
1715
  new ActionResult({
@@ -1718,7 +1747,7 @@ export class Agent {
1718
1747
  include_in_memory: true,
1719
1748
  }),
1720
1749
  ], new BrowserStateHistory('', '', [], [], null), null));
1721
- this.logger.info(`❌ ${agent_run_error}`);
1750
+ this.logger.info(`❌ ${this._redactSensitiveText(agent_run_error)}`);
1722
1751
  }
1723
1752
  this.logger.debug('📊 Collecting usage summary...');
1724
1753
  this.history.usage =
@@ -1731,7 +1760,7 @@ export class Agent {
1731
1760
  }
1732
1761
  catch (error) {
1733
1762
  agent_run_error = error instanceof Error ? error.message : String(error);
1734
- this.logger.error(`Agent run failed with exception: ${agent_run_error}`);
1763
+ this.logger.error(`Agent run failed with exception: ${this._redactSensitiveText(agent_run_error)}`);
1735
1764
  throw error;
1736
1765
  }
1737
1766
  finally {
@@ -1813,7 +1842,7 @@ export class Agent {
1813
1842
  catch (error) {
1814
1843
  if (signal?.aborted) {
1815
1844
  const message = error instanceof Error ? error.message : String(error);
1816
- this.logger.debug(`Step aborted before completion: ${message}`);
1845
+ this.logger.debug(`Step aborted before completion: ${this._redactSensitiveText(message)}`);
1817
1846
  }
1818
1847
  else {
1819
1848
  await this._handle_step_error(error);
@@ -1890,7 +1919,7 @@ export class Agent {
1890
1919
  }
1891
1920
  catch (error) {
1892
1921
  const message = error instanceof Error ? error.message : String(error);
1893
- this.logger.error(`📸 Failed to store screenshot for step ${this.state.n_steps}: ${message}`);
1922
+ this.logger.error(`📸 Failed to store screenshot for step ${this.state.n_steps}: ${this._redactSensitiveText(message)}`);
1894
1923
  this._current_screenshot_path = null;
1895
1924
  }
1896
1925
  }
@@ -1961,10 +1990,10 @@ export class Agent {
1961
1990
  ? finalResult.extracted_content
1962
1991
  : String(finalResult.extracted_content ?? '');
1963
1992
  if (success) {
1964
- this.logger.info(`\n📄 \x1b[32m Final Result:\x1b[0m \n${renderedContent}\n\n`);
1993
+ this.logger.info(`\n📄 \x1b[32m Final Result:\x1b[0m \n${this._redactSensitiveText(renderedContent)}\n\n`);
1965
1994
  }
1966
1995
  else {
1967
- this.logger.info(`\n📄 \x1b[31m Final Result:\x1b[0m \n${renderedContent}\n\n`);
1996
+ this.logger.info(`\n📄 \x1b[31m Final Result:\x1b[0m \n${this._redactSensitiveText(renderedContent)}\n\n`);
1968
1997
  }
1969
1998
  const attachments = Array.isArray(finalResult.attachments)
1970
1999
  ? finalResult.attachments
@@ -1977,7 +2006,7 @@ export class Agent {
1977
2006
  }
1978
2007
  }
1979
2008
  async multi_act(actions, options = {}) {
1980
- const { signal = null } = options;
2009
+ const { signal = null, action_timeout = null } = options;
1981
2010
  const results = [];
1982
2011
  if (!this.browser_session) {
1983
2012
  throw new Error('BrowserSession is not set up');
@@ -2014,7 +2043,7 @@ export class Agent {
2014
2043
  const preActionFocusTargetId = this.browser_session.agent_focus_target_id ??
2015
2044
  this.browser_session.active_tab?.page_id ??
2016
2045
  null;
2017
- const actResult = await this.controller.registry.execute_action(actionName, actionParams, {
2046
+ const actResult = await runActionWithTimeout(actionName, action_timeout, signal, (actionSignal) => this.controller.registry.execute_action(actionName, actionParams, {
2018
2047
  browser_session: this.browser_session,
2019
2048
  page_extraction_llm: this.settings.page_extraction_llm,
2020
2049
  extraction_schema: this.extraction_schema,
@@ -2022,11 +2051,11 @@ export class Agent {
2022
2051
  available_file_paths: this.available_file_paths,
2023
2052
  file_system: this.file_system,
2024
2053
  context: this.context ?? undefined,
2025
- signal,
2026
- });
2054
+ signal: actionSignal,
2055
+ }));
2027
2056
  results.push(actResult);
2028
2057
  // Log action execution
2029
- this.logger.info(`☑️ Executed action ${i + 1}/${actions.length}: ${actionName}(${JSON.stringify(actionParams)})`);
2058
+ this.logger.info(`☑️ Executed action ${i + 1}/${actions.length}: ${actionName}(${this._redactSensitiveText(JSON.stringify(actionParams))})`);
2030
2059
  // Break early if done, error, or last action
2031
2060
  if (results[results.length - 1]?.is_done ||
2032
2061
  results[results.length - 1]?.error ||
@@ -2055,14 +2084,87 @@ export class Agent {
2055
2084
  this._capture_shared_pinned_tab();
2056
2085
  }
2057
2086
  catch (error) {
2058
- const message = error instanceof Error ? error.message : String(error);
2059
- this.logger.error(`❌ Action ${i + 1} failed: ${message}`);
2060
2087
  this._capture_shared_pinned_tab();
2061
- throw error;
2088
+ if (this._shouldPropagateActionError(error)) {
2089
+ throw error;
2090
+ }
2091
+ if (isActionTimeoutError(error)) {
2092
+ this.logger.error(`❌ Action ${i + 1} failed: ${this._redactSensitiveText(error.message)}`);
2093
+ results.push(new ActionResult({ error: error.message }));
2094
+ return results;
2095
+ }
2096
+ if (error instanceof BrowserError) {
2097
+ const browserErrorResult = this._actionResultFromBrowserError(error);
2098
+ if (browserErrorResult) {
2099
+ this.logger.error(`❌ Action ${i + 1} failed with BrowserError: ${this._redactSensitiveText(error.toString())}`);
2100
+ results.push(browserErrorResult);
2101
+ return results;
2102
+ }
2103
+ throw error;
2104
+ }
2105
+ const registryTimeoutResult = this._actionResultFromRegistryTimeout(actionName, error);
2106
+ if (registryTimeoutResult) {
2107
+ this.logger.error(`❌ Action ${i + 1} failed: ${this._redactSensitiveText(registryTimeoutResult.error ?? '')}`);
2108
+ results.push(registryTimeoutResult);
2109
+ return results;
2110
+ }
2111
+ const message = this._formatActionExecutionError(error);
2112
+ this.logger.error(`❌ Action ${i + 1} failed: ${this._redactSensitiveText(message)}`);
2113
+ results.push(new ActionResult({ error: message }));
2114
+ return results;
2062
2115
  }
2063
2116
  }
2064
2117
  return results;
2065
2118
  }
2119
+ _shouldPropagateActionError(error) {
2120
+ if (error instanceof Error) {
2121
+ if (error.name === 'InterruptedError' || error.name === 'AbortError') {
2122
+ return true;
2123
+ }
2124
+ }
2125
+ return this._isConnectionLikeError(error);
2126
+ }
2127
+ _isConnectionLikeError(error) {
2128
+ const name = error instanceof Error ? error.name : '';
2129
+ const message = error instanceof Error ? error.message : String(error);
2130
+ const text = `${name} ${message}`.toLowerCase();
2131
+ return (name === 'ConnectionError' ||
2132
+ text.includes('websocket connection closed') ||
2133
+ text.includes('connection closed') ||
2134
+ text.includes('browser has been closed') ||
2135
+ text.includes('browser closed') ||
2136
+ text.includes('no browser'));
2137
+ }
2138
+ _formatActionExecutionError(error) {
2139
+ const typeName = error instanceof Error
2140
+ ? error.name || error.constructor.name || 'Error'
2141
+ : 'Error';
2142
+ const message = error instanceof Error ? error.message : String(error);
2143
+ return `${typeName}: ${message}`;
2144
+ }
2145
+ _actionResultFromBrowserError(error) {
2146
+ if (error.long_term_memory == null) {
2147
+ this.logger.warning('⚠️ A BrowserError was raised without long_term_memory - always set long_term_memory when raising BrowserError to propagate right messages to LLM.');
2148
+ return null;
2149
+ }
2150
+ if (error.short_term_memory != null) {
2151
+ return new ActionResult({
2152
+ extracted_content: error.short_term_memory,
2153
+ error: error.long_term_memory,
2154
+ include_extracted_content_only_once: true,
2155
+ });
2156
+ }
2157
+ return new ActionResult({ error: error.long_term_memory });
2158
+ }
2159
+ _actionResultFromRegistryTimeout(actionName, error) {
2160
+ const message = error instanceof Error ? error.message : String(error);
2161
+ if (message !== `Error executing action ${actionName} due to timeout.`) {
2162
+ return null;
2163
+ }
2164
+ return new ActionResult({
2165
+ error: `${actionName} was not executed due to timeout.`,
2166
+ });
2167
+ }
2066
2168
  async _generate_rerun_summary(originalTask, results, summaryLlm = null, signal = null) {
2067
2169
  if (!this.browser_session) {
2068
2170
  return new ActionResult({
@@ -2077,7 +2179,8 @@ export class Agent {
2077
2179
  screenshotB64 = await this.browser_session.take_screenshot(false);
2078
2180
  }
2079
2181
  catch (error) {
2080
- this.logger.warning(`Failed to capture screenshot for rerun summary: ${error instanceof Error ? error.message : String(error)}`);
2182
+ const message = error instanceof Error ? error.message : String(error);
2183
+ this.logger.warning(`Failed to capture screenshot for rerun summary: ${this._redactSensitiveText(message)}`);
2081
2184
  }
2082
2185
  const errorCount = results.filter((result) => Boolean(result.error)).length;
2083
2186
  const successCount = results.length - errorCount;
@@ -2105,7 +2208,7 @@ export class Agent {
2105
2208
  !['complete', 'partial', 'failed'].includes(String(summary.completion_status))) {
2106
2209
  throw new Error('Structured rerun summary response did not match expected schema');
2107
2210
  }
2108
- this.logger.info(`Rerun Summary: ${summary.summary}`);
2211
+ this.logger.info(`Rerun Summary: ${this._redactSensitiveText(summary.summary)}`);
2109
2212
  this.logger.info(`Rerun Status: ${summary.completion_status} (success=${summary.success})`);
2110
2213
  return new ActionResult({
2111
2214
  is_done: true,
@@ -2115,9 +2218,10 @@ export class Agent {
2115
2218
  });
2116
2219
  }
2117
2220
  catch (structuredError) {
2118
- this.logger.debug(`Structured rerun summary failed: ${structuredError instanceof Error
2221
+ const message = structuredError instanceof Error
2119
2222
  ? structuredError.message
2120
- : String(structuredError)}, falling back to text response`);
2223
+ : String(structuredError);
2224
+ this.logger.debug(`Structured rerun summary failed: ${this._redactSensitiveText(message)}, falling back to text response`);
2121
2225
  }
2122
2226
  try {
2123
2227
  const response = await llm.ainvoke([message], undefined, {
@@ -2135,7 +2239,8 @@ export class Agent {
2135
2239
  });
2136
2240
  }
2137
2241
  catch (error) {
2138
- this.logger.warning(`Failed to generate rerun summary: ${error instanceof Error ? error.message : String(error)}`);
2242
+ const message = error instanceof Error ? error.message : String(error);
2243
+ this.logger.warning(`Failed to generate rerun summary: ${this._redactSensitiveText(message)}`);
2139
2244
  return new ActionResult({
2140
2245
  is_done: true,
2141
2246
  success: errorCount === 0,
@@ -2159,10 +2264,17 @@ export class Agent {
2159
2264
  if (!page || typeof page.content !== 'function') {
2160
2265
  throw new Error('No page available for markdown extraction');
2161
2266
  }
2267
+ await this.browser_session.validate_page_after_action(page, signal);
2162
2268
  if (typeof page.url === 'function') {
2163
2269
  currentUrl = page.url();
2164
2270
  }
2165
- const html = (await page.content()) || '';
2271
+ let html = '';
2272
+ try {
2273
+ html = (await page.content()) || '';
2274
+ }
2275
+ finally {
2276
+ await this.browser_session.validate_page_after_action(page, signal);
2277
+ }
2166
2278
  const extracted = extractCleanMarkdownFromHtml(html, {
2167
2279
  extract_links: extractLinks,
2168
2280
  });
@@ -2191,7 +2303,8 @@ export class Agent {
2191
2303
  (await this.browser_session.take_screenshot?.(false)) ?? null;
2192
2304
  }
2193
2305
  catch (error) {
2194
- this.logger.warning(`Failed to capture screenshot for ai_step: ${error instanceof Error ? error.message : String(error)}`);
2306
+ const message = error instanceof Error ? error.message : String(error);
2307
+ this.logger.warning(`Failed to capture screenshot for ai_step: ${this._redactSensitiveText(message)}`);
2195
2308
  }
2196
2309
  }
2197
2310
  const userMessage = screenshotB64
@@ -2226,9 +2339,10 @@ export class Agent {
2226
2339
  });
2227
2340
  }
2228
2341
  catch (error) {
2229
- this.logger.warning(`Failed to execute AI step: ${error instanceof Error ? error.message : String(error)}`);
2342
+ const message = error instanceof Error ? error.message : String(error);
2343
+ this.logger.warning(`Failed to execute AI step: ${this._redactSensitiveText(message)}`);
2230
2344
  return new ActionResult({
2231
- error: `AI step failed: ${error instanceof Error ? error.message : String(error)}`,
2345
+ error: `AI step failed: ${message}`,
2232
2346
  });
2233
2347
  }
2234
2348
  }
@@ -2261,7 +2375,7 @@ export class Agent {
2261
2375
  delaySource = `using saved step_interval=${this._formatDelaySeconds(stepDelay)}`;
2262
2376
  }
2263
2377
  }
2264
- this.logger.info(`Replaying ${stepName} (${index + 1}/${history.history.length}) [${delaySource}]: ${goal}`);
2378
+ this.logger.info(`Replaying ${stepName} (${index + 1}/${history.history.length}) [${delaySource}]: ${this._redactSensitiveText(goal)}`);
2265
2379
  const actions = historyItem.model_output?.action ?? [];
2266
2380
  const hasValidAction = actions.length && !actions.every((action) => action == null);
2267
2381
  if (!historyItem.model_output || !hasValidAction) {
@@ -2279,7 +2393,7 @@ export class Agent {
2279
2393
  const preview = firstError.length > 100
2280
2394
  ? `${firstError.slice(0, 100)}...`
2281
2395
  : firstError;
2282
- this.logger.warning(`${stepName}: Original step had error(s), skipping (skip_failures=true): ${preview}`);
2396
+ this.logger.warning(`${stepName}: Original step had error(s), skipping (skip_failures=true): ${this._redactSensitiveText(preview)}`);
2283
2397
  results.push(new ActionResult({
2284
2398
  error: `Skipped - original step had error: ${preview}`,
2285
2399
  }));
@@ -2332,7 +2446,7 @@ export class Agent {
2332
2446
  }
2333
2447
  if (attempt === max_retries) {
2334
2448
  const message = `${stepName} failed after ${max_retries} attempts: ${errorMessage}`;
2335
- this.logger.error(message);
2449
+ this.logger.error(this._redactSensitiveText(message));
2336
2450
  const failure = new ActionResult({ error: message });
2337
2451
  results.push(failure);
2338
2452
  if (!skip_failures) {
@@ -2409,7 +2523,7 @@ export class Agent {
2409
2523
  const params = actionPayload[actionName] ?? {};
2410
2524
  const query = typeof params.query === 'string' ? params.query : '';
2411
2525
  const extractLinks = Boolean(params.extract_links);
2412
- this.logger.info(`Using AI step for extract action: ${query.slice(0, 50)}...`);
2526
+ this.logger.info(`Using AI step for extract action: ${this._redactSensitiveText(query.slice(0, 50))}...`);
2413
2527
  const aiResult = await this._execute_ai_step(query, false, extractLinks, ai_step_llm, signal);
2414
2528
  results.push(aiResult);
2415
2529
  continue;
@@ -2674,7 +2788,8 @@ export class Agent {
2674
2788
  return true;
2675
2789
  }
2676
2790
  catch (error) {
2677
- this.logger.warning(`Failed to re-open dropdown: ${error instanceof Error ? error.message : String(error)}`);
2791
+ const message = error instanceof Error ? error.message : String(error);
2792
+ this.logger.warning(`Failed to re-open dropdown: ${this._redactSensitiveText(message)}`);
2678
2793
  return false;
2679
2794
  }
2680
2795
  }
@@ -2739,7 +2854,7 @@ export class Agent {
2739
2854
  if (node?.xpath === historicalElement.xpath) {
2740
2855
  currentNode = node;
2741
2856
  matchLevel = 'XPATH';
2742
- this.logger.info(`Element matched at XPATH fallback: ${historicalElement.xpath}`);
2857
+ this.logger.info(`Element matched at XPATH fallback: ${this._redactSensitiveText(historicalElement.xpath)}`);
2743
2858
  break;
2744
2859
  }
2745
2860
  }
@@ -2754,7 +2869,7 @@ export class Agent {
2754
2869
  nodeAxName === targetAxName) {
2755
2870
  currentNode = node;
2756
2871
  matchLevel = 'AX_NAME';
2757
- this.logger.info(`Element matched at AX_NAME fallback: ${targetAxName}`);
2872
+ this.logger.info(`Element matched at AX_NAME fallback: ${this._redactSensitiveText(targetAxName)}`);
2758
2873
  break;
2759
2874
  }
2760
2875
  }
@@ -2771,7 +2886,7 @@ export class Agent {
2771
2886
  node?.attributes?.[attrKey] === attrValue) {
2772
2887
  currentNode = node;
2773
2888
  matchLevel = 'ATTRIBUTE';
2774
- this.logger.info(`Element matched via ${attrKey} attribute fallback: ${attrValue}`);
2889
+ this.logger.info(`Element matched via ${attrKey} attribute fallback: ${this._redactSensitiveText(attrValue)}`);
2775
2890
  break;
2776
2891
  }
2777
2892
  }
@@ -2994,7 +3109,8 @@ export class Agent {
2994
3109
  }
2995
3110
  }
2996
3111
  catch (error) {
2997
- this.logger.error(`Error during agent cleanup: ${error instanceof Error ? error.message : String(error)}`);
3112
+ const message = error instanceof Error ? error.message : String(error);
3113
+ this.logger.error(`Error during agent cleanup: ${this._redactSensitiveText(message)}`);
2998
3114
  }
2999
3115
  if (this.skill_service &&
3000
3116
  typeof this.skill_service.close === 'function') {
@@ -3002,7 +3118,8 @@ export class Agent {
3002
3118
  await this.skill_service.close();
3003
3119
  }
3004
3120
  catch (error) {
3005
- this.logger.error(`Error during skill service cleanup: ${error instanceof Error ? error.message : String(error)}`);
3121
+ const message = error instanceof Error ? error.message : String(error);
3122
+ this.logger.error(`Error during skill service cleanup: ${this._redactSensitiveText(message)}`);
3006
3123
  }
3007
3124
  }
3008
3125
  })();
@@ -3089,7 +3206,7 @@ export class Agent {
3089
3206
  return { trace, trace_details };
3090
3207
  }
3091
3208
  async _log_agent_run() {
3092
- this.logger.info(`\x1b[34m🎯 Task: ${this.task}\x1b[0m`);
3209
+ this.logger.info(`\x1b[34m🎯 Task: ${this._redactSensitiveText(this.task)}\x1b[0m`);
3093
3210
  this.logger.debug(`🤖 Browser-Use Library Version ${this.version} (${this.source})`);
3094
3211
  if (CONFIG.BROWSER_USE_VERSION_CHECK &&
3095
3212
  process.env.NODE_ENV !== 'test' &&
@@ -3131,15 +3248,20 @@ export class Agent {
3131
3248
  if (this.register_new_step_callback && this.state.last_model_output) {
3132
3249
  await this.register_new_step_callback(browser_state_summary, this.state.last_model_output, this.state.n_steps);
3133
3250
  }
3134
- log_response(this.state.last_model_output, this.controller, this.logger);
3251
+ log_response(this.state.last_model_output, this.controller, this.logger, this.sensitive_data);
3135
3252
  if (this.settings.save_conversation_path) {
3136
3253
  const dir = this.settings.save_conversation_path;
3137
3254
  const filepath = path.join(dir, `step_${this.state.n_steps}.json`);
3138
- await fs.promises.mkdir(path.dirname(filepath), { recursive: true });
3255
+ ensureDir(path.dirname(filepath));
3139
3256
  await fs.promises.writeFile(filepath, JSON.stringify({
3140
3257
  messages: input_messages,
3141
3258
  response: this.state.last_model_output?.model_dump(),
3142
- }, null, 2), this.settings.save_conversation_path_encoding);
3259
+ }, null, 2), {
3260
+ encoding: this.settings
3261
+ .save_conversation_path_encoding,
3262
+ mode: 0o600,
3263
+ });
3264
+ await chmodPrivateFile(filepath);
3143
3265
  }
3144
3266
  }
3145
3267
  /** Handle all types of errors that can occur during a step (python c011 parity). */
@@ -3148,7 +3270,7 @@ export class Agent {
3148
3270
  const message = error.message
3149
3271
  ? `The agent was interrupted mid-step - ${error.message}`
3150
3272
  : 'The agent was interrupted mid-step';
3151
- this.logger.warning(message);
3273
+ this.logger.warning(this._redactSensitiveText(message));
3152
3274
  return;
3153
3275
  }
3154
3276
  const include_trace = this.logger.level === 'debug';
@@ -3168,11 +3290,12 @@ export class Agent {
3168
3290
  this.logger.warning(parseLog);
3169
3291
  }
3170
3292
  }
3293
+ const redactedErrorMsg = this._redactSensitiveText(error_msg);
3171
3294
  if (isFinalFailure) {
3172
- this.logger.error(`${prefix}${error_msg}`);
3295
+ this.logger.error(`${prefix}${redactedErrorMsg}`);
3173
3296
  }
3174
3297
  else {
3175
- this.logger.warning(`${prefix}${error_msg}`);
3298
+ this.logger.warning(`${prefix}${redactedErrorMsg}`);
3176
3299
  }
3177
3300
  this.state.last_result = [new ActionResult({ error: error_msg })];
3178
3301
  }
@@ -3415,7 +3538,7 @@ export class Agent {
3415
3538
  ? parsed.reason.trim()
3416
3539
  : 'Task requirements not fully met';
3417
3540
  if (!isCorrect) {
3418
- this.logger.info(`⚠️ Simple judge overriding success to failure: ${reason}`);
3541
+ this.logger.info(`⚠️ Simple judge overriding success to failure: ${this._redactSensitiveText(reason)}`);
3419
3542
  lastResult.success = false;
3420
3543
  const note = `[Simple judge: ${reason}]`;
3421
3544
  if (lastResult.extracted_content) {
@@ -3427,7 +3550,8 @@ export class Agent {
3427
3550
  }
3428
3551
  }
3429
3552
  catch (error) {
3430
- this.logger.warning(`Simple judge failed with error: ${error instanceof Error ? error.message : String(error)}`);
3553
+ const message = error instanceof Error ? error.message : String(error);
3554
+ this.logger.warning(`Simple judge failed with error: ${this._redactSensitiveText(message)}`);
3431
3555
  }
3432
3556
  }
3433
3557
  async _judge_trace() {
@@ -3456,7 +3580,8 @@ export class Agent {
3456
3580
  return validation.data;
3457
3581
  }
3458
3582
  catch (error) {
3459
- this.logger.warning(`Judge trace failed: ${error instanceof Error ? error.message : String(error)}`);
3583
+ const message = error instanceof Error ? error.message : String(error);
3584
+ this.logger.warning(`Judge trace failed: ${this._redactSensitiveText(message)}`);
3460
3585
  return null;
3461
3586
  }
3462
3587
  }
@@ -3493,7 +3618,7 @@ export class Agent {
3493
3618
  if (judgement.reasoning) {
3494
3619
  judgeLog += ` ${judgement.reasoning}\n`;
3495
3620
  }
3496
- this.logger.info(judgeLog);
3621
+ this.logger.info(this._redactSensitiveText(judgeLog));
3497
3622
  }
3498
3623
  _replace_urls_in_text(text) {
3499
3624
  const replacedUrls = {};
@@ -3788,7 +3913,7 @@ export class Agent {
3788
3913
  }
3789
3914
  _try_switch_to_fallback_llm(error) {
3790
3915
  if (this._using_fallback_llm) {
3791
- this.logger.warning(`⚠️ Fallback LLM also failed (${error.name}: ${error.message}), no more fallbacks available`);
3916
+ this.logger.warning(`⚠️ Fallback LLM also failed (${error.name}: ${this._redactSensitiveText(error.message)}), no more fallbacks available`);
3792
3917
  return false;
3793
3918
  }
3794
3919
  const retryableStatusCodes = new Set([401, 402, 429, 500, 502, 503, 504]);
@@ -3799,7 +3924,7 @@ export class Agent {
3799
3924
  return false;
3800
3925
  }
3801
3926
  if (!this._fallback_llm) {
3802
- this.logger.warning(`⚠️ LLM error (${error.name}: ${error.message}) but no fallback_llm configured`);
3927
+ this.logger.warning(`⚠️ LLM error (${error.name}: ${this._redactSensitiveText(error.message)}) but no fallback_llm configured`);
3803
3928
  return false;
3804
3929
  }
3805
3930
  this._log_fallback_switch(error, this._fallback_llm);
@@ -3886,7 +4011,16 @@ export class Agent {
3886
4011
  {});
3887
4012
  const paramsResult = actionInfo.paramSchema.safeParse(rawParams);
3888
4013
  if (!paramsResult.success) {
3889
- throw new Error(`Invalid parameters for action '${requestedActionName}': ${paramsResult.error.message}`);
4014
+ // Surface a human-readable issue list (zod v4 `prettifyError`) plus
4015
+ // a corrective hint, rather than the default JSON dump of `.issues`.
4016
+ // This Error propagates → `_handle_step_error` writes it into
4017
+ // `state.last_result` → `create_state_messages` injects it into the
4018
+ // next LLM turn, so the model knows exactly what shape it got wrong.
4019
+ const pretty = z.prettifyError(paramsResult.error);
4020
+ const sentParams = JSON.stringify(rawParams);
4021
+ throw new Error(`Schema validation failed for action '${requestedActionName}'. ` +
4022
+ `You sent: ${sentParams}. Issues:\n${pretty}\n` +
4023
+ `Please retry with parameters matching the action's schema exactly.`);
3890
4024
  }
3891
4025
  normalizedActions.push(new modelForStep({
3892
4026
  [actionName]: paramsResult.data,