browser-use 0.3.0 → 0.5.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.
package/README.md CHANGED
@@ -95,13 +95,13 @@ npx browser-use --mcp
95
95
 
96
96
  ```
97
97
  ┌─────────────────────────────────────────────────────┐
98
- │ Browser-Use
98
+ │ Browser-Use
99
99
  ├─────────────────────────────────────────────────────┤
100
- │ Agent ← MessageManager ← LLM Providers
101
- │ ↓
100
+ │ Agent ← MessageManager ← LLM Providers
101
+ │ ↓
102
102
  │ Controller → Action Registry → BrowserSession │
103
- │ ↓
104
- │ DomService
103
+ │ ↓
104
+ │ DomService
105
105
  └─────────────────────────────────────────────────────┘
106
106
  ```
107
107
 
@@ -396,23 +396,23 @@ const agent = new Agent({
396
396
 
397
397
  ```bash
398
398
  # Install dependencies
399
- npm install
399
+ pnpm install
400
400
 
401
401
  # Build
402
- npm run build
402
+ pnpm build
403
403
 
404
404
  # Run tests
405
- npm test
405
+ pnpm test
406
406
 
407
407
  # Lint & format
408
- npm run lint
409
- npm run prettier
408
+ pnpm lint
409
+ pnpm prettier
410
410
 
411
411
  # Type checking
412
- npm run typecheck
412
+ pnpm typecheck
413
413
 
414
414
  # Run an example
415
- npx tsx examples/simple-search.ts
415
+ pnpm exec tsx examples/simple-search.ts
416
416
  ```
417
417
 
418
418
  ## Requirements
@@ -7,7 +7,7 @@ export declare class Page {
7
7
  private _mouse;
8
8
  constructor(browser_session: BrowserSession);
9
9
  get mouse(): Mouse;
10
- _currentPage(): Promise<import("playwright-core").Page>;
10
+ _currentPage(): Promise<import("playwright").Page>;
11
11
  get_url(): Promise<string>;
12
12
  get_title(): Promise<string>;
13
13
  goto(url: string, options?: {
@@ -332,6 +332,10 @@ export declare class Agent<Context = ControllerContext, AgentStructuredOutput =
332
332
  private _replace_shortened_urls_in_value;
333
333
  private _parseCompletionPayload;
334
334
  private _isModelActionMissing;
335
+ private _getOutputActionNames;
336
+ private _toStrictActionParamSchema;
337
+ private _buildActionOutputSchema;
338
+ private _buildLlmOutputFormat;
335
339
  private _get_model_output_with_retry;
336
340
  private _try_switch_to_fallback_llm;
337
341
  private _log_fallback_switch;
@@ -3633,13 +3633,83 @@ export class Agent {
3633
3633
  return Object.keys(candidate).length === 0;
3634
3634
  });
3635
3635
  }
3636
+ _getOutputActionNames(doneOnly) {
3637
+ const registryActions = this.controller.registry.get_all_actions();
3638
+ const modelForStep = doneOnly
3639
+ ? this.DoneActionModel
3640
+ : this.ActionModel;
3641
+ const modelAvailableNames = modelForStep?.available_actions;
3642
+ if (Array.isArray(modelAvailableNames) && modelAvailableNames.length > 0) {
3643
+ const deduped = Array.from(new Set(modelAvailableNames.filter((name) => typeof name === 'string' &&
3644
+ name.trim().length > 0 &&
3645
+ registryActions.has(name))));
3646
+ if (deduped.length > 0) {
3647
+ return deduped;
3648
+ }
3649
+ }
3650
+ if (doneOnly && registryActions.has('done')) {
3651
+ return ['done'];
3652
+ }
3653
+ return Array.from(registryActions.keys());
3654
+ }
3655
+ _toStrictActionParamSchema(schema) {
3656
+ if (schema instanceof z.ZodObject) {
3657
+ return schema.strict();
3658
+ }
3659
+ return schema;
3660
+ }
3661
+ _buildActionOutputSchema(doneOnly) {
3662
+ const registryActions = this.controller.registry.get_all_actions();
3663
+ const actionSchemas = this._getOutputActionNames(doneOnly)
3664
+ .map((actionName) => {
3665
+ const actionInfo = registryActions.get(actionName);
3666
+ if (!actionInfo) {
3667
+ return null;
3668
+ }
3669
+ const paramSchema = this._toStrictActionParamSchema(actionInfo.paramSchema);
3670
+ return z.object({ [actionName]: paramSchema }).strict();
3671
+ })
3672
+ .filter((schema) => schema != null);
3673
+ if (actionSchemas.length === 0) {
3674
+ const doneAction = registryActions.get('done');
3675
+ if (doneAction) {
3676
+ const doneParams = this._toStrictActionParamSchema(doneAction.paramSchema);
3677
+ return z.object({ done: doneParams }).strict();
3678
+ }
3679
+ return z.object({ done: z.object({}).strict() }).strict();
3680
+ }
3681
+ if (actionSchemas.length === 1) {
3682
+ return actionSchemas[0];
3683
+ }
3684
+ const [firstActionSchema, secondActionSchema, ...remainingActionSchemas] = actionSchemas;
3685
+ return z.union([
3686
+ firstActionSchema,
3687
+ secondActionSchema,
3688
+ ...remainingActionSchemas,
3689
+ ]);
3690
+ }
3691
+ _buildLlmOutputFormat(doneOnly) {
3692
+ const schema = z.object({
3693
+ thinking: z.string().optional().nullable(),
3694
+ evaluation_previous_goal: z.string().optional().nullable(),
3695
+ memory: z.string().optional().nullable(),
3696
+ next_goal: z.string().optional().nullable(),
3697
+ current_plan_item: z.number().int().optional().nullable(),
3698
+ plan_update: z.array(z.string()).optional().nullable(),
3699
+ action: z
3700
+ .array(this._buildActionOutputSchema(doneOnly))
3701
+ .optional()
3702
+ .nullable(),
3703
+ });
3704
+ const outputFormat = schema;
3705
+ outputFormat.schema = schema;
3706
+ return outputFormat;
3707
+ }
3636
3708
  async _get_model_output_with_retry(messages, signal = null) {
3637
3709
  const urlReplacements = this._process_messages_and_replace_long_urls_shorter_ones(messages);
3638
3710
  const invokeAndParse = async (inputMessages) => {
3639
3711
  this._throwIfAborted(signal);
3640
- const outputFormat = this._enforceDoneOnlyForCurrentStep
3641
- ? DoneOnlyLLMOutputFormat
3642
- : AgentLLMOutputFormat;
3712
+ const outputFormat = this._buildLlmOutputFormat(this._enforceDoneOnlyForCurrentStep);
3643
3713
  const completion = await this.llm.ainvoke(inputMessages, outputFormat, {
3644
3714
  signal: signal ?? undefined,
3645
3715
  session_id: this.session_id,
@@ -173,9 +173,9 @@ export declare class BrowserProfile {
173
173
  get traces_dir(): Nullable<string>;
174
174
  get user_data_dir(): Nullable<string>;
175
175
  get viewport_expansion(): number;
176
- get viewport(): Nullable<import("playwright-core").ViewportSize>;
176
+ get viewport(): Nullable<import("playwright").ViewportSize>;
177
177
  get wait_for_network_idle_page_load_time(): number;
178
- get window_size(): Nullable<import("playwright-core").ViewportSize>;
178
+ get window_size(): Nullable<import("playwright").ViewportSize>;
179
179
  private applyLegacyWindowSize;
180
180
  private warnStorageStateUserDataDirConflict;
181
181
  private warnUserDataDirNonDefault;
@@ -360,10 +360,17 @@ const DEFAULT_BROWSER_PROFILE_OPTIONS = {
360
360
  profile_directory: 'Default',
361
361
  cookies_file: null,
362
362
  };
363
+ const splitArgOnce = (arg) => {
364
+ const separatorIndex = arg.indexOf('=');
365
+ if (separatorIndex === -1) {
366
+ return [arg, ''];
367
+ }
368
+ return [arg.slice(0, separatorIndex), arg.slice(separatorIndex + 1)];
369
+ };
363
370
  const argsAsDict = (args) => {
364
371
  const result = {};
365
372
  for (const arg of args) {
366
- const [keyPart, valuePart = ''] = arg.split('=', 1);
373
+ const [keyPart, valuePart = ''] = splitArgOnce(arg);
367
374
  const key = keyPart.trim().replace(/^-+/, '');
368
375
  result[key] = valuePart.trim();
369
376
  }
@@ -376,9 +383,22 @@ const cloneDefaultOptions = () => JSON.parse(JSON.stringify(DEFAULT_BROWSER_PROF
376
383
  const normalizeDomainEntry = (entry) => String(entry ?? '')
377
384
  .trim()
378
385
  .toLowerCase();
386
+ const isExactHostDomainEntry = (entry) => {
387
+ if (!entry) {
388
+ return false;
389
+ }
390
+ if (entry.includes('*') || entry.includes('://') || entry.includes('/')) {
391
+ return false;
392
+ }
393
+ // Keep set optimization for plain hostnames only. Entries with ports/pattern-like
394
+ // delimiters must stay as arrays to preserve wildcard/scheme matching semantics.
395
+ return !entry.includes(':');
396
+ };
379
397
  const optimizeDomainList = (value) => {
380
398
  const cleaned = value.map(normalizeDomainEntry).filter(Boolean);
381
- if (cleaned.length >= DOMAIN_OPTIMIZATION_THRESHOLD) {
399
+ const canOptimizeToSet = cleaned.length >= DOMAIN_OPTIMIZATION_THRESHOLD &&
400
+ cleaned.every(isExactHostDomainEntry);
401
+ if (canOptimizeToSet) {
382
402
  logger.warning(`Optimizing domain list with ${cleaned.length} entries to a Set for O(1) matching`);
383
403
  return new Set(cleaned);
384
404
  }
@@ -407,16 +427,12 @@ export class BrowserProfile {
407
427
  allowed_domains: Array.isArray(init.allowed_domains)
408
428
  ? optimizeDomainList(init.allowed_domains)
409
429
  : init.allowed_domains instanceof Set
410
- ? new Set(Array.from(init.allowed_domains)
411
- .map(normalizeDomainEntry)
412
- .filter(Boolean))
430
+ ? optimizeDomainList(Array.from(init.allowed_domains))
413
431
  : defaults.allowed_domains,
414
432
  prohibited_domains: Array.isArray(init.prohibited_domains)
415
433
  ? optimizeDomainList(init.prohibited_domains)
416
434
  : init.prohibited_domains instanceof Set
417
- ? new Set(Array.from(init.prohibited_domains)
418
- .map(normalizeDomainEntry)
419
- .filter(Boolean))
435
+ ? optimizeDomainList(Array.from(init.prohibited_domains))
420
436
  : defaults.prohibited_domains,
421
437
  window_position: init.window_position ?? defaults.window_position,
422
438
  };
@@ -96,6 +96,8 @@ export declare class BrowserSession {
96
96
  get_or_create_cdp_session(page?: Page | null): Promise<any>;
97
97
  private _waitForStableNetwork;
98
98
  private _setActivePage;
99
+ private _syncCurrentTabFromPage;
100
+ private _syncTabsWithBrowserPages;
99
101
  private _captureClosedPopupMessage;
100
102
  private _getClosedPopupMessagesSnapshot;
101
103
  private _recordRecentEvent;
@@ -145,13 +147,13 @@ export declare class BrowserSession {
145
147
  private _shutdown_browser_session;
146
148
  close(): Promise<void>;
147
149
  get_browser_state_with_recovery(options?: BrowserStateOptions): Promise<BrowserStateSummary>;
148
- get_current_page(): Promise<import("playwright-core").Page | null>;
150
+ get_current_page(): Promise<import("playwright").Page | null>;
149
151
  update_current_page(page: Page | null, title?: string | null, url?: string | null): void;
150
152
  private _buildTabs;
151
- navigate_to(url: string, options?: BrowserNavigationOptions): Promise<import("playwright-core").Page | null>;
152
- create_new_tab(url: string, options?: BrowserNavigationOptions): Promise<import("playwright-core").Page | null>;
153
+ navigate_to(url: string, options?: BrowserNavigationOptions): Promise<import("playwright").Page | null>;
154
+ create_new_tab(url: string, options?: BrowserNavigationOptions): Promise<import("playwright").Page | null>;
153
155
  private _resolveTabIndex;
154
- switch_to_tab(identifier: number | string, options?: BrowserActionOptions): Promise<import("playwright-core").Page | null>;
156
+ switch_to_tab(identifier: number | string, options?: BrowserActionOptions): Promise<import("playwright").Page | null>;
155
157
  close_tab(identifier: number | string): Promise<void>;
156
158
  wait(seconds: number, options?: BrowserActionOptions): Promise<void>;
157
159
  send_keys(keys: string, options?: BrowserActionOptions): Promise<void>;
@@ -327,6 +329,7 @@ export declare class BrowserSession {
327
329
  private _is_new_tab_page;
328
330
  private _is_ip_address_host;
329
331
  private _get_domain_variants;
332
+ private _setEntryMatchesUrl;
330
333
  /**
331
334
  * Check if page is displaying a PDF
332
335
  */
@@ -530,6 +533,10 @@ export declare class BrowserSession {
530
533
  * Updates human_current_page to reflect which tab the user is viewing
531
534
  */
532
535
  private _onTabVisibilityChange;
536
+ /**
537
+ * Normalize pid values before issuing process operations.
538
+ */
539
+ private _normalizePid;
533
540
  /**
534
541
  * Kill all child processes spawned by this browser session
535
542
  */