playwright-core 1.58.0-alpha-2025-12-11 → 1.58.0-alpha-2025-12-13

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.
@@ -613,7 +613,9 @@ import_validatorPrimitives.scheme.BrowserTypeLaunchPersistentContextParams = (0,
613
613
  model: import_validatorPrimitives.tString,
614
614
  cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
615
615
  cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
616
- secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue")))
616
+ secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue"))),
617
+ maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt),
618
+ maxTokens: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
617
619
  })),
618
620
  userDataDir: import_validatorPrimitives.tString,
619
621
  slowMo: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tFloat)
@@ -712,7 +714,9 @@ import_validatorPrimitives.scheme.BrowserNewContextParams = (0, import_validator
712
714
  model: import_validatorPrimitives.tString,
713
715
  cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
714
716
  cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
715
- secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue")))
717
+ secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue"))),
718
+ maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt),
719
+ maxTokens: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
716
720
  })),
717
721
  proxy: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tObject)({
718
722
  server: import_validatorPrimitives.tString,
@@ -790,7 +794,9 @@ import_validatorPrimitives.scheme.BrowserNewContextForReuseParams = (0, import_v
790
794
  model: import_validatorPrimitives.tString,
791
795
  cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
792
796
  cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
793
- secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue")))
797
+ secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue"))),
798
+ maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt),
799
+ maxTokens: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
794
800
  })),
795
801
  proxy: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tObject)({
796
802
  server: import_validatorPrimitives.tString,
@@ -913,7 +919,9 @@ import_validatorPrimitives.scheme.BrowserContextInitializer = (0, import_validat
913
919
  model: import_validatorPrimitives.tString,
914
920
  cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
915
921
  cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
916
- secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue")))
922
+ secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue"))),
923
+ maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt),
924
+ maxTokens: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
917
925
  }))
918
926
  })
919
927
  });
@@ -1519,16 +1527,24 @@ import_validatorPrimitives.scheme.PageUpdateSubscriptionResult = (0, import_vali
1519
1527
  import_validatorPrimitives.scheme.PagePerformParams = (0, import_validatorPrimitives.tObject)({
1520
1528
  task: import_validatorPrimitives.tString,
1521
1529
  key: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
1522
- maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
1530
+ maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt),
1531
+ maxTokens: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
1532
+ });
1533
+ import_validatorPrimitives.scheme.PagePerformResult = (0, import_validatorPrimitives.tObject)({
1534
+ turns: import_validatorPrimitives.tInt,
1535
+ inputTokens: import_validatorPrimitives.tInt,
1536
+ outputTokens: import_validatorPrimitives.tInt
1523
1537
  });
1524
- import_validatorPrimitives.scheme.PagePerformResult = (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tObject)({}));
1525
1538
  import_validatorPrimitives.scheme.PageExtractParams = (0, import_validatorPrimitives.tObject)({
1526
1539
  query: import_validatorPrimitives.tString,
1527
1540
  schema: import_validatorPrimitives.tAny,
1528
1541
  maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
1529
1542
  });
1530
1543
  import_validatorPrimitives.scheme.PageExtractResult = (0, import_validatorPrimitives.tObject)({
1531
- result: import_validatorPrimitives.tAny
1544
+ result: import_validatorPrimitives.tAny,
1545
+ turns: import_validatorPrimitives.tInt,
1546
+ inputTokens: import_validatorPrimitives.tInt,
1547
+ outputTokens: import_validatorPrimitives.tInt
1532
1548
  });
1533
1549
  import_validatorPrimitives.scheme.FrameInitializer = (0, import_validatorPrimitives.tObject)({
1534
1550
  url: import_validatorPrimitives.tString,
@@ -2823,7 +2839,9 @@ import_validatorPrimitives.scheme.AndroidDeviceLaunchBrowserParams = (0, import_
2823
2839
  model: import_validatorPrimitives.tString,
2824
2840
  cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
2825
2841
  cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
2826
- secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue")))
2842
+ secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue"))),
2843
+ maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt),
2844
+ maxTokens: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
2827
2845
  })),
2828
2846
  pkg: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
2829
2847
  args: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)(import_validatorPrimitives.tString)),
@@ -41,9 +41,10 @@ var import_context = require("./context");
41
41
  async function pagePerform(progress, page, options) {
42
42
  const context = new import_context.Context(progress, page);
43
43
  if (await cachedPerform(context, options))
44
- return;
45
- await perform(context, options.task, void 0, options);
44
+ return { turns: 0, inputTokens: 0, outputTokens: 0 };
45
+ const { usage } = await perform(context, options.task, void 0, options);
46
46
  await updateCache(context, options);
47
+ return usage;
47
48
  }
48
49
  async function pageExtract(progress, page, options) {
49
50
  const context = new import_context.Context(progress, page);
@@ -53,7 +54,8 @@ Extract the following information from the page. Do not perform any actions, jus
53
54
 
54
55
  ### Query
55
56
  ${options.query}`;
56
- return await perform(context, task, options.schema, options);
57
+ const { result, usage } = await perform(context, task, options.schema, options);
58
+ return { result, usage };
57
59
  }
58
60
  async function perform(context, userTask, resultSchema, options = {}) {
59
61
  const { progress, page } = context;
@@ -62,13 +64,17 @@ async function perform(context, userTask, resultSchema, options = {}) {
62
64
  throw new Error(`page.perform() and page.extract() require the agent to be set on the browser context`);
63
65
  const { full } = await page.snapshotForAI(progress);
64
66
  const { tools, callTool } = (0, import_backend.toolsForLoop)(context);
67
+ const limits = context.limits(options);
68
+ let turns = 0;
65
69
  const loop = new import_mcpBundle.Loop(browserContext._options.agent.provider, {
66
70
  model: browserContext._options.agent.model,
67
71
  summarize: true,
68
72
  debug: import_utilsBundle.debug,
69
73
  callTool,
70
74
  tools,
75
+ ...limits,
71
76
  beforeTurn: (params) => {
77
+ ++turns;
72
78
  const lastReply = params.conversation.messages.findLast((m) => m.role === "assistant");
73
79
  const toolCall = lastReply?.content.find((c) => c.type === "tool_call");
74
80
  if (!resultSchema && toolCall && toolCall.arguments.thatShouldBeIt)
@@ -82,15 +88,22 @@ async function perform(context, userTask, resultSchema, options = {}) {
82
88
  ### Page snapshot
83
89
  ${full}
84
90
  `;
85
- const { result } = await loop.run(task, { resultSchema });
86
- return result;
91
+ const { result, usage } = await loop.run(task, { resultSchema });
92
+ return {
93
+ result,
94
+ usage: {
95
+ turns,
96
+ inputTokens: usage.input,
97
+ outputTokens: usage.output
98
+ }
99
+ };
87
100
  }
88
101
  const allCaches = /* @__PURE__ */ new Map();
89
102
  async function cachedPerform(context, options) {
90
103
  if (!context.options?.cacheFile || context.options.cacheMode === "ignore")
91
104
  return false;
92
105
  const cache = await cachedActions(context.options.cacheFile);
93
- const cacheKey = options.key ?? options.task;
106
+ const cacheKey = (options.key ?? options.task).trim();
94
107
  const entry = cache[cacheKey];
95
108
  if (!entry) {
96
109
  if (context.options.cacheMode === "force")
@@ -106,7 +119,7 @@ async function updateCache(context, options) {
106
119
  if (!cacheFile)
107
120
  return;
108
121
  const cache = await cachedActions(cacheFile);
109
- const cacheKey = options.key ?? options.task;
122
+ const cacheKey = (options.key ?? options.task).trim();
110
123
  cache[cacheKey] = {
111
124
  timestamp: Date.now(),
112
125
  actions: context.actions
@@ -32,7 +32,6 @@ __export(backend_exports, {
32
32
  });
33
33
  module.exports = __toCommonJS(backend_exports);
34
34
  var import_tools = __toESM(require("./tools"));
35
- var import_progress = require("../progress");
36
35
  var import_mcpBundle = require("../../mcpBundle");
37
36
  function toolsForLoop(context) {
38
37
  const tools = import_tools.default.map((tool) => {
@@ -44,6 +43,7 @@ function toolsForLoop(context) {
44
43
  return result;
45
44
  });
46
45
  const callTool = async (params) => {
46
+ const intent = params.arguments._meta?.["dev.lowire/intent"];
47
47
  const tool = import_tools.default.find((t) => t.schema.name === params.name);
48
48
  if (!tool) {
49
49
  return {
@@ -54,11 +54,8 @@ function toolsForLoop(context) {
54
54
  isError: true
55
55
  };
56
56
  }
57
- const progressController = new import_progress.ProgressController();
58
57
  try {
59
- return await progressController.run(async (progress) => {
60
- return await tool.handle(context, params.arguments);
61
- });
58
+ return await context.callTool(tool, params.arguments, { intent });
62
59
  } catch (error) {
63
60
  return {
64
61
  content: [{ type: "text", text: error.message }],
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var codegen_exports = {};
20
+ __export(codegen_exports, {
21
+ generateCode: () => generateCode
22
+ });
23
+ module.exports = __toCommonJS(codegen_exports);
24
+ var import_locatorGenerators = require("../../utils/isomorphic/locatorGenerators");
25
+ var import_stringUtils = require("../../utils/isomorphic/stringUtils");
26
+ async function generateCode(sdkLanguage, action) {
27
+ switch (action.method) {
28
+ case "click": {
29
+ const locator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.selector);
30
+ return `await page.${locator}.click(${(0, import_stringUtils.formatObjectOrVoid)(action.options)});`;
31
+ }
32
+ case "drag": {
33
+ const sourceLocator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.sourceSelector);
34
+ const targetLocator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.targetSelector);
35
+ return `await page.${sourceLocator}.dragAndDrop(${targetLocator});`;
36
+ }
37
+ case "hover": {
38
+ const locator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.selector);
39
+ return `await page.${locator}.hover(${(0, import_stringUtils.formatObjectOrVoid)(action.options)});`;
40
+ }
41
+ case "pressKey": {
42
+ return `await page.keyboard.press(${(0, import_stringUtils.escapeWithQuotes)(action.key, "'")});`;
43
+ }
44
+ case "selectOption": {
45
+ const locator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.selector);
46
+ return `await page.${locator}.selectOption(${action.labels.length === 1 ? (0, import_stringUtils.escapeWithQuotes)(action.labels[0]) : "[" + action.labels.map((label) => (0, import_stringUtils.escapeWithQuotes)(label)).join(", ") + "]"});`;
47
+ }
48
+ case "pressSequentially": {
49
+ const locator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.selector);
50
+ const code = [`await page.${locator}.pressSequentially(${(0, import_stringUtils.escapeWithQuotes)(action.text)});`];
51
+ if (action.submit)
52
+ code.push(`await page.keyboard.press('Enter');`);
53
+ return code.join("\n");
54
+ }
55
+ case "fill": {
56
+ const locator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.selector);
57
+ const code = [`await page.${locator}.fill(${(0, import_stringUtils.escapeWithQuotes)(action.text)});`];
58
+ if (action.submit)
59
+ code.push(`await page.keyboard.press('Enter');`);
60
+ return code.join("\n");
61
+ }
62
+ case "setChecked": {
63
+ const locator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.selector);
64
+ if (action.checked)
65
+ return `await page.${locator}.check();`;
66
+ else
67
+ return `await page.${locator}.uncheck();`;
68
+ }
69
+ case "expectVisible": {
70
+ const locator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.selector);
71
+ return `await expect(page.${locator}).toBeVisible();`;
72
+ }
73
+ case "expectValue": {
74
+ const locator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.selector);
75
+ return `await expect(page.${locator}).toHaveValue(${(0, import_stringUtils.escapeWithQuotes)(action.value)});`;
76
+ }
77
+ }
78
+ throw new Error("Unknown action " + action.method);
79
+ }
80
+ // Annotate the CommonJS export names for ESM import in node:
81
+ 0 && (module.exports = {
82
+ generateCode
83
+ });
@@ -23,12 +23,22 @@ __export(context_exports, {
23
23
  module.exports = __toCommonJS(context_exports);
24
24
  var import_browserContext = require("../browserContext");
25
25
  var import_actionRunner = require("./actionRunner");
26
+ var import_codegen = require("./codegen");
26
27
  class Context {
27
28
  constructor(progress, page) {
28
29
  this.actions = [];
29
30
  this.progress = progress;
30
31
  this.page = page;
31
32
  this.options = page.browserContext._options.agent;
33
+ this.sdkLanguage = page.browserContext._browser.sdkLanguage();
34
+ }
35
+ async callTool(tool, params, options) {
36
+ this._callIntent = options.intent;
37
+ try {
38
+ return await tool.handle(this, params);
39
+ } finally {
40
+ this._callIntent = void 0;
41
+ }
32
42
  }
33
43
  async runActionAndWait(action) {
34
44
  return await this.runActionsAndWait([action]);
@@ -38,7 +48,8 @@ class Context {
38
48
  await this.waitForCompletion(async () => {
39
49
  for (const a of action) {
40
50
  await (0, import_actionRunner.runAction)(this.progress, this.page, a, this.options?.secrets ?? []);
41
- this.actions.push(a);
51
+ const code = await (0, import_codegen.generateCode)(this.sdkLanguage, a);
52
+ this.actions.push({ ...a, code, intent: this._callIntent });
42
53
  }
43
54
  });
44
55
  return await this.snapshotResult();
@@ -112,6 +123,12 @@ ${full}`);
112
123
  }
113
124
  }));
114
125
  }
126
+ limits(options = {}) {
127
+ return {
128
+ maxTurns: options.maxTurns ?? this.options?.maxTurns ?? 10,
129
+ maxTokens: options.maxTokens ?? this.options?.maxTokens ?? void 0
130
+ };
131
+ }
115
132
  _redactText(text) {
116
133
  const secrets = this.options?.secrets;
117
134
  if (!secrets)
@@ -90,7 +90,7 @@ class JavaScriptLanguageGenerator {
90
90
  case "fill":
91
91
  return `await ${subject}.${this._asLocator(action.selector)}.fill(${quote(action.text)});`;
92
92
  case "setInputFiles":
93
- return `await ${subject}.${this._asLocator(action.selector)}.setInputFiles(${formatObject(action.files.length === 1 ? action.files[0] : action.files)});`;
93
+ return `await ${subject}.${this._asLocator(action.selector)}.setInputFiles(${(0, import_utils.formatObject)(action.files.length === 1 ? action.files[0] : action.files)});`;
94
94
  case "press": {
95
95
  const modifiers = (0, import_language.toKeyboardModifiers)(action.modifiers);
96
96
  const shortcut = [...modifiers, action.key].join("+");
@@ -99,7 +99,7 @@ class JavaScriptLanguageGenerator {
99
99
  case "navigate":
100
100
  return `await ${subject}.goto(${quote(action.url)});`;
101
101
  case "select":
102
- return `await ${subject}.${this._asLocator(action.selector)}.selectOption(${formatObject(action.options.length === 1 ? action.options[0] : action.options)});`;
102
+ return `await ${subject}.${this._asLocator(action.selector)}.selectOption(${(0, import_utils.formatObject)(action.options.length === 1 ? action.options[0] : action.options)});`;
103
103
  case "assertText":
104
104
  return `${this._isTest ? "" : "// "}await expect(${subject}.${this._asLocator(action.selector)}).${action.substring ? "toContainText" : "toHaveText"}(${quote(action.text)});`;
105
105
  case "assertChecked":
@@ -151,7 +151,7 @@ ${useText ? "\ntest.use(" + useText + ");\n" : ""}
151
151
  const { ${options.browserName}${options.deviceName ? ", devices" : ""} } = require('playwright');
152
152
 
153
153
  (async () => {
154
- const browser = await ${options.browserName}.launch(${formatObjectOrVoid(options.launchOptions)});
154
+ const browser = await ${options.browserName}.launch(${(0, import_utils.formatObjectOrVoid)(options.launchOptions)});
155
155
  const context = await browser.newContext(${formatContextOptions(options.contextOptions, options.deviceName, false)});`);
156
156
  if (options.contextOptions.recordHar)
157
157
  formatter.add(` await context.routeFromHAR(${quote(options.contextOptions.recordHar.path)});`);
@@ -171,37 +171,14 @@ function formatOptions(value, hasArguments) {
171
171
  const keys = Object.keys(value).filter((key) => value[key] !== void 0);
172
172
  if (!keys.length)
173
173
  return "";
174
- return (hasArguments ? ", " : "") + formatObject(value);
175
- }
176
- function formatObject(value, indent = " ") {
177
- if (typeof value === "string")
178
- return quote(value);
179
- if (Array.isArray(value))
180
- return `[${value.map((o) => formatObject(o)).join(", ")}]`;
181
- if (typeof value === "object") {
182
- const keys = Object.keys(value).filter((key) => value[key] !== void 0).sort();
183
- if (!keys.length)
184
- return "{}";
185
- const tokens = [];
186
- for (const key of keys)
187
- tokens.push(`${key}: ${formatObject(value[key])}`);
188
- return `{
189
- ${indent}${tokens.join(`,
190
- ${indent}`)}
191
- }`;
192
- }
193
- return String(value);
194
- }
195
- function formatObjectOrVoid(value, indent = " ") {
196
- const result = formatObject(value, indent);
197
- return result === "{}" ? "" : result;
174
+ return (hasArguments ? ", " : "") + (0, import_utils.formatObject)(value);
198
175
  }
199
176
  function formatContextOptions(options, deviceName, isTest) {
200
177
  const device = deviceName && import_deviceDescriptors.deviceDescriptors[deviceName];
201
178
  options = { ...options, recordHar: void 0 };
202
179
  if (!device)
203
- return formatObjectOrVoid(options);
204
- let serializedObject = formatObjectOrVoid((0, import_language.sanitizeDeviceOptions)(device, options));
180
+ return (0, import_utils.formatObjectOrVoid)(options);
181
+ let serializedObject = (0, import_utils.formatObjectOrVoid)((0, import_language.sanitizeDeviceOptions)(device, options));
205
182
  if (!serializedObject)
206
183
  serializedObject = "{\n}";
207
184
  const lines = serializedObject.split("\n");