playwright-core 1.58.0-alpha-2025-12-15 → 1.58.0-alpha-2025-12-17

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.
@@ -612,7 +612,7 @@ import_validatorPrimitives.scheme.BrowserTypeLaunchPersistentContextParams = (0,
612
612
  provider: import_validatorPrimitives.tString,
613
613
  model: import_validatorPrimitives.tString,
614
614
  cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
615
- cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
615
+ cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "update", "auto"])),
616
616
  secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue"))),
617
617
  maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt),
618
618
  maxTokens: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
@@ -713,7 +713,7 @@ import_validatorPrimitives.scheme.BrowserNewContextParams = (0, import_validator
713
713
  provider: import_validatorPrimitives.tString,
714
714
  model: import_validatorPrimitives.tString,
715
715
  cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
716
- cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
716
+ cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "update", "auto"])),
717
717
  secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue"))),
718
718
  maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt),
719
719
  maxTokens: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
@@ -793,7 +793,7 @@ import_validatorPrimitives.scheme.BrowserNewContextForReuseParams = (0, import_v
793
793
  provider: import_validatorPrimitives.tString,
794
794
  model: import_validatorPrimitives.tString,
795
795
  cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
796
- cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
796
+ cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "update", "auto"])),
797
797
  secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue"))),
798
798
  maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt),
799
799
  maxTokens: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
@@ -918,7 +918,7 @@ import_validatorPrimitives.scheme.BrowserContextInitializer = (0, import_validat
918
918
  provider: import_validatorPrimitives.tString,
919
919
  model: import_validatorPrimitives.tString,
920
920
  cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
921
- cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
921
+ cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "update", "auto"])),
922
922
  secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue"))),
923
923
  maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt),
924
924
  maxTokens: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
@@ -1186,6 +1186,14 @@ import_validatorPrimitives.scheme.PageInitializer = (0, import_validatorPrimitiv
1186
1186
  isClosed: import_validatorPrimitives.tBoolean,
1187
1187
  opener: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tChannel)(["Page"]))
1188
1188
  });
1189
+ import_validatorPrimitives.scheme.PageAgentTurnEvent = (0, import_validatorPrimitives.tObject)({
1190
+ role: import_validatorPrimitives.tString,
1191
+ message: import_validatorPrimitives.tString,
1192
+ usage: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tObject)({
1193
+ inputTokens: import_validatorPrimitives.tInt,
1194
+ outputTokens: import_validatorPrimitives.tInt
1195
+ }))
1196
+ });
1189
1197
  import_validatorPrimitives.scheme.PageBindingCallEvent = (0, import_validatorPrimitives.tObject)({
1190
1198
  binding: (0, import_validatorPrimitives.tChannel)(["BindingCall"])
1191
1199
  });
@@ -2838,7 +2846,7 @@ import_validatorPrimitives.scheme.AndroidDeviceLaunchBrowserParams = (0, import_
2838
2846
  provider: import_validatorPrimitives.tString,
2839
2847
  model: import_validatorPrimitives.tString,
2840
2848
  cacheFile: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString),
2841
- cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "auto"])),
2849
+ cacheMode: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tEnum)(["ignore", "force", "update", "auto"])),
2842
2850
  secrets: (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tArray)((0, import_validatorPrimitives.tType)("NameValue"))),
2843
2851
  maxTurns: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt),
2844
2852
  maxTokens: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tInt)
@@ -18,13 +18,25 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var actionRunner_exports = {};
20
20
  __export(actionRunner_exports, {
21
- runAction: () => runAction,
22
- serializeArgument: () => serializeArgument
21
+ generateActionTimeout: () => generateActionTimeout,
22
+ performActionTimeout: () => performActionTimeout,
23
+ runAction: () => runAction
23
24
  });
24
25
  module.exports = __toCommonJS(actionRunner_exports);
25
26
  var import_expectUtils = require("../utils/expectUtils");
26
- var import_serializers = require("../../protocol/serializers");
27
- async function runAction(progress, page, action, secrets) {
27
+ var import_time = require("../../utils/isomorphic/time");
28
+ var import_progress = require("../progress");
29
+ async function runAction(parentProgress, mode, page, action, secrets) {
30
+ const timeout = mode === "generate" ? generateActionTimeout(action) : performActionTimeout(action);
31
+ const mt = (0, import_time.monotonicTime)();
32
+ const deadline = mt + timeout;
33
+ const minDeadline = parentProgress.deadline ? Math.min(parentProgress.deadline, deadline) : deadline;
34
+ const pc = new import_progress.ProgressController();
35
+ return await pc.run(async (progress) => {
36
+ return await innerRunAction(progress, page, action, secrets);
37
+ }, minDeadline - mt);
38
+ }
39
+ async function innerRunAction(progress, page, action, secrets) {
28
40
  const frame = page.mainFrame();
29
41
  switch (action.method) {
30
42
  case "click":
@@ -63,7 +75,7 @@ async function runAction(progress, page, action, secrets) {
63
75
  await frame.uncheck(progress, action.selector, { ...strictTrue });
64
76
  break;
65
77
  case "expectVisible": {
66
- const result = await frame.expect(progress, action.selector, { expression: "to.be.visible", isNot: false }, 5e3);
78
+ const result = await frame.expect(progress, action.selector, { expression: "to.be.visible", isNot: false });
67
79
  if (result.errorMessage)
68
80
  throw new Error(result.errorMessage);
69
81
  break;
@@ -72,28 +84,56 @@ async function runAction(progress, page, action, secrets) {
72
84
  let result;
73
85
  if (action.type === "textbox" || action.type === "combobox" || action.type === "slider") {
74
86
  const expectedText = (0, import_expectUtils.serializeExpectedTextValues)([action.value]);
75
- result = await frame.expect(progress, action.selector, { expression: "to.have.value", expectedText, isNot: false }, 5e3);
87
+ result = await frame.expect(progress, action.selector, { expression: "to.have.value", expectedText, isNot: false });
76
88
  } else if (action.type === "checkbox" || action.type === "radio") {
77
- const expectedValue = serializeArgument({ checked: true });
78
- result = await frame.expect(progress, action.selector, { expression: "to.be.checked", expectedValue, isNot: false }, 5e3);
89
+ const expectedValue = { checked: action.value === "true" };
90
+ result = await frame.expect(progress, action.selector, { selector: action.selector, expression: "to.be.checked", expectedValue, isNot: false });
79
91
  } else {
80
92
  throw new Error(`Unsupported element type: ${action.type}`);
81
93
  }
82
- if (result.errorMessage)
94
+ if (!result.matches)
83
95
  throw new Error(result.errorMessage);
84
96
  break;
85
97
  }
86
98
  }
87
99
  }
88
- function serializeArgument(arg) {
89
- return {
90
- value: (0, import_serializers.serializePlainValue)(arg),
91
- handles: []
92
- };
100
+ function generateActionTimeout(action) {
101
+ switch (action.method) {
102
+ case "click":
103
+ case "drag":
104
+ case "hover":
105
+ case "selectOption":
106
+ case "pressKey":
107
+ case "pressSequentially":
108
+ case "fill":
109
+ case "setChecked":
110
+ return 5e3;
111
+ case "expectVisible":
112
+ case "expectValue":
113
+ return 1;
114
+ }
115
+ }
116
+ function performActionTimeout(action) {
117
+ switch (action.method) {
118
+ case "click":
119
+ case "drag":
120
+ case "hover":
121
+ case "selectOption":
122
+ case "pressKey":
123
+ case "pressSequentially":
124
+ case "fill":
125
+ case "setChecked":
126
+ return 0;
127
+ // no timeout
128
+ case "expectVisible":
129
+ case "expectValue":
130
+ return 5e3;
131
+ }
93
132
  }
94
133
  const strictTrue = { strict: true };
95
134
  // Annotate the CommonJS export names for ESM import in node:
96
135
  0 && (module.exports = {
97
- runAction,
98
- serializeArgument
136
+ generateActionTimeout,
137
+ performActionTimeout,
138
+ runAction
99
139
  });
@@ -38,11 +38,12 @@ var import_utilsBundle = require("../../utilsBundle");
38
38
  var import_mcpBundle = require("../../mcpBundle");
39
39
  var import_actionRunner = require("./actionRunner");
40
40
  var import_context = require("./context");
41
+ var import_page = require("../page");
41
42
  async function pagePerform(progress, page, options) {
42
43
  const context = new import_context.Context(progress, page);
43
- if (await cachedPerform(context, options))
44
+ if (await cachedPerform(progress, context, options))
44
45
  return { turns: 0, inputTokens: 0, outputTokens: 0 };
45
- const { usage } = await perform(context, options.task, void 0, options);
46
+ const { usage } = await perform(progress, context, options.task, void 0, options);
46
47
  await updateCache(context, options);
47
48
  return usage;
48
49
  }
@@ -54,16 +55,17 @@ Extract the following information from the page. Do not perform any actions, jus
54
55
 
55
56
  ### Query
56
57
  ${options.query}`;
57
- const { result, usage } = await perform(context, task, options.schema, options);
58
+ const { result, usage } = await perform(progress, context, task, options.schema, options);
58
59
  return { result, usage };
59
60
  }
60
- async function perform(context, userTask, resultSchema, options = {}) {
61
- const { progress, page } = context;
61
+ async function perform(progress, context, userTask, resultSchema, options = {}) {
62
+ const { page } = context;
62
63
  const browserContext = page.browserContext;
63
64
  if (!browserContext._options.agent)
64
65
  throw new Error(`page.perform() and page.extract() require the agent to be set on the browser context`);
65
66
  const { full } = await page.snapshotForAI(progress);
66
67
  const { tools, callTool } = (0, import_backend.toolsForLoop)(context);
68
+ page.emit(import_page.Page.Events.AgentTurn, { role: "user", message: userTask });
67
69
  const limits = context.limits(options);
68
70
  let turns = 0;
69
71
  const loop = new import_mcpBundle.Loop(browserContext._options.agent.provider, {
@@ -73,14 +75,35 @@ async function perform(context, userTask, resultSchema, options = {}) {
73
75
  callTool,
74
76
  tools,
75
77
  ...limits,
76
- beforeTurn: (params) => {
78
+ onBeforeTurn: ({ conversation }) => {
79
+ const userMessage = conversation.messages.find((m) => m.role === "user");
80
+ page.emit(import_page.Page.Events.AgentTurn, { role: "user", message: userMessage?.content ?? "" });
81
+ return "continue";
82
+ },
83
+ onAfterTurn: ({ assistantMessage, totalUsage }) => {
77
84
  ++turns;
78
- const lastReply = params.conversation.messages.findLast((m) => m.role === "assistant");
79
- const toolCall = lastReply?.content.find((c) => c.type === "tool_call");
80
- if (!resultSchema && toolCall && toolCall.arguments.thatShouldBeIt)
85
+ const usage2 = { inputTokens: totalUsage.input, outputTokens: totalUsage.output };
86
+ const intent = assistantMessage.content.filter((c) => c.type === "text").map((c) => c.text).join("\n");
87
+ page.emit(import_page.Page.Events.AgentTurn, { role: "assistant", message: intent, usage: usage2 });
88
+ if (!assistantMessage.content.filter((c) => c.type === "tool_call").length)
89
+ page.emit(import_page.Page.Events.AgentTurn, { role: "assistant", message: `no tool calls`, usage: usage2 });
90
+ return "continue";
91
+ },
92
+ onBeforeToolCall: ({ toolCall }) => {
93
+ page.emit(import_page.Page.Events.AgentTurn, { role: "assistant", message: `call tool "${toolCall.name}"` });
94
+ return "continue";
95
+ },
96
+ onAfterToolCall: ({ toolCall }) => {
97
+ const suffix = toolCall.result?.isError ? "failed" : "succeeded";
98
+ page.emit(import_page.Page.Events.AgentTurn, { role: "user", message: `tool "${toolCall.name}" ${suffix}` });
99
+ if (toolCall.arguments.thatShouldBeIt)
81
100
  return "break";
82
101
  return "continue";
83
102
  },
103
+ onToolCallError: ({ toolCall, error }) => {
104
+ page.emit(import_page.Page.Events.AgentTurn, { role: "user", message: `tool "${toolCall.name}" failed: ${error.message}` });
105
+ return "continue";
106
+ },
84
107
  ...options
85
108
  });
86
109
  const task = `${userTask}
@@ -99,8 +122,8 @@ ${full}
99
122
  };
100
123
  }
101
124
  const allCaches = /* @__PURE__ */ new Map();
102
- async function cachedPerform(context, options) {
103
- if (!context.options?.cacheFile || context.options.cacheMode === "ignore")
125
+ async function cachedPerform(progress, context, options) {
126
+ if (!context.options?.cacheFile || context.options.cacheMode === "ignore" || context.options.cacheMode === "update")
104
127
  return false;
105
128
  const cache = await cachedActions(context.options.cacheFile);
106
129
  const cacheKey = (options.key ?? options.task).trim();
@@ -111,7 +134,7 @@ async function cachedPerform(context, options) {
111
134
  return false;
112
135
  }
113
136
  for (const action of entry.actions)
114
- await (0, import_actionRunner.runAction)(context.progress, context.page, action, context.options.secrets ?? []);
137
+ await (0, import_actionRunner.runAction)(progress, "run", context.page, action, context.options.secrets ?? []);
115
138
  return true;
116
139
  }
117
140
  async function updateCache(context, options) {
@@ -124,7 +147,9 @@ async function updateCache(context, options) {
124
147
  timestamp: Date.now(),
125
148
  actions: context.actions
126
149
  };
127
- await import_fs.default.promises.writeFile(cacheFile, JSON.stringify(cache, void 0, 2));
150
+ const entries = Object.entries(cache);
151
+ entries.sort((e1, e2) => e1[0].localeCompare(e2[0]));
152
+ await import_fs.default.promises.writeFile(cacheFile, JSON.stringify(Object.fromEntries(entries), void 0, 2));
128
153
  }
129
154
  async function cachedActions(cacheFile) {
130
155
  let cache = allCaches.get(cacheFile);
@@ -72,6 +72,8 @@ async function generateCode(sdkLanguage, action) {
72
72
  }
73
73
  case "expectValue": {
74
74
  const locator = (0, import_locatorGenerators.asLocator)(sdkLanguage, action.selector);
75
+ if (action.type === "checkbox" || action.type === "radio")
76
+ return `await expect(page.${locator}).toBeChecked({ checked: ${action.value === "true"} });`;
75
77
  return `await expect(page.${locator}).toHaveValue(${(0, import_stringUtils.escapeWithQuotes)(action.value)});`;
76
78
  }
77
79
  }
@@ -25,9 +25,9 @@ var import_browserContext = require("../browserContext");
25
25
  var import_actionRunner = require("./actionRunner");
26
26
  var import_codegen = require("./codegen");
27
27
  class Context {
28
- constructor(progress, page) {
28
+ constructor(apiCallProgress, page) {
29
29
  this.actions = [];
30
- this.progress = progress;
30
+ this._progress = apiCallProgress;
31
31
  this.page = page;
32
32
  this.options = page.browserContext._options.agent;
33
33
  this.sdkLanguage = page.browserContext._browser.sdkLanguage();
@@ -44,18 +44,15 @@ class Context {
44
44
  return await this.runActionsAndWait([action]);
45
45
  }
46
46
  async runActionsAndWait(action) {
47
- try {
48
- await this.waitForCompletion(async () => {
49
- for (const a of action) {
50
- await (0, import_actionRunner.runAction)(this.progress, this.page, a, this.options?.secrets ?? []);
51
- const code = await (0, import_codegen.generateCode)(this.sdkLanguage, a);
52
- this.actions.push({ ...a, code, intent: this._callIntent });
53
- }
54
- });
55
- return await this.snapshotResult();
56
- } catch (e) {
57
- return await this.snapshotResult(e);
58
- }
47
+ const error = await this.waitForCompletion(async () => {
48
+ for (const a of action) {
49
+ await (0, import_actionRunner.runAction)(this._progress, "generate", this.page, a, this.options?.secrets ?? []);
50
+ const code = await (0, import_codegen.generateCode)(this.sdkLanguage, a);
51
+ this.actions.push({ ...a, code, intent: this._callIntent });
52
+ }
53
+ return void 0;
54
+ }).catch((error2) => error2);
55
+ return await this.snapshotResult(error);
59
56
  }
60
57
  async waitForCompletion(callback) {
61
58
  const requests = [];
@@ -67,13 +64,13 @@ class Context {
67
64
  let result;
68
65
  try {
69
66
  result = await callback();
70
- await this.progress.wait(500);
67
+ await this._progress.wait(500);
71
68
  } finally {
72
69
  disposeListeners();
73
70
  }
74
71
  const requestedNavigation = requests.some((request) => request.isNavigationRequest());
75
72
  if (requestedNavigation) {
76
- await this.page.mainFrame().waitForLoadState(this.progress, "load");
73
+ await this.page.mainFrame().waitForLoadState(this._progress, "load");
77
74
  return result;
78
75
  }
79
76
  const promises = [];
@@ -83,13 +80,13 @@ class Context {
83
80
  else
84
81
  promises.push(request.response());
85
82
  }
86
- await this.progress.race(promises, { timeout: 5e3 });
83
+ await this._progress.race(promises, { timeout: 5e3 });
87
84
  if (requests.length)
88
- await this.progress.wait(500);
85
+ await this._progress.wait(500);
89
86
  return result;
90
87
  }
91
88
  async snapshotResult(error) {
92
- let { full } = await this.page.snapshotForAI(this.progress);
89
+ let { full } = await this.page.snapshotForAI(this._progress);
93
90
  full = this._redactText(full);
94
91
  const text = [];
95
92
  if (error)
@@ -116,7 +113,7 @@ ${full}`);
116
113
  async refSelectors(params) {
117
114
  return Promise.all(params.map(async (param) => {
118
115
  try {
119
- const { resolvedSelector } = await this.page.mainFrame().resolveSelector(this.progress, `aria-ref=${param.ref}`);
116
+ const { resolvedSelector } = await this.page.mainFrame().resolveSelector(this._progress, `aria-ref=${param.ref}`);
120
117
  return resolvedSelector;
121
118
  } catch (e) {
122
119
  throw new Error(`Ref ${param.ref} not found in the current page snapshot. Try capturing new snapshot.`);
@@ -184,7 +184,7 @@ const fillForm = defineTool({
184
184
  schema: {
185
185
  name: "browser_fill_form",
186
186
  title: "Fill form",
187
- description: "Fill multiple form fields",
187
+ description: "Fill multiple form fields. Always use this tool when you can fill more than one field at a time.",
188
188
  inputSchema: baseSchema.extend({
189
189
  fields: import_mcpBundle.z.array(import_mcpBundle.z.object({
190
190
  name: import_mcpBundle.z.string().describe("Human-readable field name"),
@@ -123,7 +123,10 @@ class BidiExecutionContext {
123
123
  }
124
124
  return names2;
125
125
  });
126
- const values = await Promise.all(names.map((name) => handle.evaluateHandle((object, name2) => object[name2], name)));
126
+ const values = await Promise.all(names.map(async (name) => {
127
+ const value = await this._rawCallFunction("(object, name) => object[name]", [{ handle: handle._objectId }, { type: "string", value: name }], true, false);
128
+ return createHandle(handle._context, value);
129
+ }));
127
130
  const map = /* @__PURE__ */ new Map();
128
131
  for (let i = 0; i < names.length; i++)
129
132
  map.set(names[i], values[i]);
@@ -152,7 +155,7 @@ class BidiExecutionContext {
152
155
  return createHandle(context, result);
153
156
  }
154
157
  async contentFrameIdForFrame(handle) {
155
- const contentWindow = await this._rawCallFunction("e => e.contentWindow", { handle: handle._objectId });
158
+ const contentWindow = await this._rawCallFunction("e => e.contentWindow", [{ handle: handle._objectId }]);
156
159
  if (contentWindow?.type === "window")
157
160
  return contentWindow.value.context;
158
161
  return null;
@@ -166,17 +169,17 @@ class BidiExecutionContext {
166
169
  return null;
167
170
  }
168
171
  async _remoteValueForReference(reference, createHandle2) {
169
- return await this._rawCallFunction("e => e", reference, createHandle2);
172
+ return await this._rawCallFunction("e => e", [reference], createHandle2);
170
173
  }
171
- async _rawCallFunction(functionDeclaration, arg, createHandle2) {
174
+ async _rawCallFunction(functionDeclaration, args, createHandle2, awaitPromise = true) {
172
175
  const response = await this._session.send("script.callFunction", {
173
176
  functionDeclaration,
174
177
  target: this._target,
175
- arguments: [arg],
178
+ arguments: args,
176
179
  // "Root" is necessary for the handle to be returned.
177
180
  resultOwnership: createHandle2 ? bidi.Script.ResultOwnership.Root : bidi.Script.ResultOwnership.None,
178
181
  serializationOptions: { maxObjectDepth: 0, maxDomDepth: 0 },
179
- awaitPromise: true,
182
+ awaitPromise,
180
183
  userActivation: true
181
184
  });
182
185
  if (response.type === "exception")
@@ -186,14 +189,65 @@ class BidiExecutionContext {
186
189
  throw new js.JavaScriptErrorInEvaluate("Unexpected response type: " + JSON.stringify(response));
187
190
  }
188
191
  }
189
- function renderPreview(remoteObject) {
190
- if (remoteObject.type === "undefined")
191
- return "undefined";
192
- if (remoteObject.type === "null")
193
- return "null";
194
- if ("value" in remoteObject)
195
- return String(remoteObject.value);
196
- return `<${remoteObject.type}>`;
192
+ function renderPreview(remoteObject, nested = false) {
193
+ switch (remoteObject.type) {
194
+ case "undefined":
195
+ case "null":
196
+ return remoteObject.type;
197
+ case "number":
198
+ case "boolean":
199
+ case "string":
200
+ return String(remoteObject.value);
201
+ case "bigint":
202
+ return `${remoteObject.value}n`;
203
+ case "date":
204
+ return String(new Date(remoteObject.value));
205
+ case "regexp":
206
+ return String(new RegExp(remoteObject.value.pattern, remoteObject.value.flags));
207
+ case "node":
208
+ return remoteObject.value?.localName || "Node";
209
+ case "object":
210
+ if (nested)
211
+ return "Object";
212
+ const tokens = [];
213
+ for (const [name, value] of remoteObject.value || []) {
214
+ if (typeof name === "string")
215
+ tokens.push(`${name}: ${renderPreview(value, true)}`);
216
+ }
217
+ return `{${tokens.join(", ")}}`;
218
+ case "array":
219
+ case "htmlcollection":
220
+ case "nodelist":
221
+ if (nested || !remoteObject.value)
222
+ return remoteObject.value ? `Array(${remoteObject.value.length})` : "Array";
223
+ return `[${remoteObject.value.map((v) => renderPreview(v, true)).join(", ")}]`;
224
+ case "map":
225
+ return remoteObject.value ? `Map(${remoteObject.value.length})` : "Map";
226
+ case "set":
227
+ return remoteObject.value ? `Set(${remoteObject.value.length})` : "Set";
228
+ case "arraybuffer":
229
+ return "ArrayBuffer";
230
+ case "error":
231
+ return "Error";
232
+ case "function":
233
+ return "Function";
234
+ case "generator":
235
+ return "Generator";
236
+ case "promise":
237
+ return "Promise";
238
+ case "proxy":
239
+ return "Proxy";
240
+ case "symbol":
241
+ return "Symbol()";
242
+ case "typedarray":
243
+ return "TypedArray";
244
+ case "weakmap":
245
+ return "WeakMap";
246
+ case "weakset":
247
+ return "WeakSet";
248
+ case "window":
249
+ return "Window";
250
+ }
197
251
  }
198
252
  function remoteObjectValue(remoteObject) {
199
253
  if (remoteObject.type === "undefined")
@@ -212,7 +266,10 @@ function createHandle(context, remoteObject) {
212
266
  return new dom.ElementHandle(context, remoteObject.handle);
213
267
  }
214
268
  const objectId = "handle" in remoteObject ? remoteObject.handle : void 0;
215
- return new js.JSHandle(context, remoteObject.type, renderPreview(remoteObject), objectId, remoteObjectValue(remoteObject));
269
+ const preview = renderPreview(remoteObject);
270
+ const handle = new js.JSHandle(context, remoteObject.type, preview, objectId, remoteObjectValue(remoteObject));
271
+ handle._setPreview(preview);
272
+ return handle;
216
273
  }
217
274
  // Annotate the CommonJS export names for ESM import in node:
218
275
  0 && (module.exports = {
@@ -263,7 +263,7 @@ ${params.stackTrace?.callFrames.map((f) => {
263
263
  return;
264
264
  const callFrame = params.stackTrace?.callFrames[0];
265
265
  const location = callFrame ?? { url: "", lineNumber: 1, columnNumber: 1 };
266
- this._page.addConsoleMessage(null, entry.method, entry.args.map((arg) => (0, import_bidiExecutionContext.createHandle)(context, arg)), location, params.text || void 0);
266
+ this._page.addConsoleMessage(null, entry.method, entry.args.map((arg) => (0, import_bidiExecutionContext.createHandle)(context, arg)), location);
267
267
  }
268
268
  async _onFileDialogOpened(params) {
269
269
  if (!params.element)
@@ -67,6 +67,7 @@ class PageDispatcher extends import_dispatcher.Dispatcher {
67
67
  }
68
68
  this._dispatchEvent("route", { route: new import_networkDispatchers3.RouteDispatcher(import_networkDispatchers.RequestDispatcher.from(this.parentScope(), request), route) });
69
69
  };
70
+ this.addObjectListener(import_page.Page.Events.AgentTurn, (params) => this._dispatchEvent("agentTurn", params));
70
71
  this.addObjectListener(import_page.Page.Events.Close, () => {
71
72
  this._dispatchEvent("close");
72
73
  this._dispose();
@@ -56,6 +56,7 @@ var import_callLog = require("./callLog");
56
56
  var rawBindingsControllerSource = __toESM(require("../generated/bindingsControllerSource"));
57
57
  var import_screencast = require("./screencast");
58
58
  const PageEvent = {
59
+ AgentTurn: "agentturn",
59
60
  Close: "close",
60
61
  Crash: "crash",
61
62
  Download: "download",
@@ -44,9 +44,11 @@ class ProgressController {
44
44
  await this._donePromise;
45
45
  }
46
46
  async run(task, timeout) {
47
+ const deadline = timeout ? (0, import_utils.monotonicTime)() + timeout : 0;
47
48
  (0, import_utils.assert)(this._state === "before");
48
49
  this._state = "running";
49
50
  const progress = {
51
+ deadline,
50
52
  log: (message) => {
51
53
  if (this._state === "running")
52
54
  this.metadata.log.push(message);
@@ -55,7 +57,9 @@ class ProgressController {
55
57
  metadata: this.metadata,
56
58
  race: (promise, options) => {
57
59
  const promises = Array.isArray(promise) ? promise : [promise];
58
- const timerPromise = options?.timeout ? new Promise((f) => setTimeout(f, options.timeout)) : null;
60
+ const mt = (0, import_utils.monotonicTime)();
61
+ const dl = options?.timeout ? mt + options.timeout : 0;
62
+ const timerPromise = dl && (!deadline || dl < deadline) ? new Promise((f) => setTimeout(f, dl - mt)) : null;
59
63
  return Promise.race([...promises, ...timerPromise ? [timerPromise] : [], this._forceAbortPromise]);
60
64
  },
61
65
  wait: async (timeout2) => {
@@ -65,7 +69,7 @@ class ProgressController {
65
69
  }
66
70
  };
67
71
  let timer;
68
- if (timeout) {
72
+ if (deadline) {
69
73
  const timeoutError = new import_errors.TimeoutError(`Timeout ${timeout}ms exceeded.`);
70
74
  timer = setTimeout(() => {
71
75
  if (this.metadata.pauseStartTime && !this.metadata.pauseEndTime)
@@ -75,7 +79,7 @@ class ProgressController {
75
79
  this._state = { error: timeoutError };
76
80
  this._forceAbortPromise.reject(timeoutError);
77
81
  }
78
- }, timeout);
82
+ }, deadline - (0, import_utils.monotonicTime)());
79
83
  }
80
84
  try {
81
85
  const result = await task(progress);
@@ -47,20 +47,21 @@ var import_launchApp2 = require("../../launchApp");
47
47
  var import_playwright = require("../../playwright");
48
48
  var import_progress = require("../../progress");
49
49
  const tracesDirMarker = "traces.dir";
50
- function validateTraceUrl(traceUrl) {
51
- if (!traceUrl)
52
- return traceUrl;
53
- if (traceUrl.startsWith("http://") || traceUrl.startsWith("https://"))
54
- return traceUrl;
55
- if (traceUrl.endsWith(".json"))
56
- return traceUrl;
50
+ function validateTraceUrl(traceFileOrUrl) {
51
+ if (!traceFileOrUrl)
52
+ return traceFileOrUrl;
53
+ if (traceFileOrUrl.startsWith("http://") || traceFileOrUrl.startsWith("https://"))
54
+ return traceFileOrUrl;
55
+ let traceFile = traceFileOrUrl;
56
+ if (traceFile.endsWith(".json"))
57
+ return toFilePathUrl(traceFile);
57
58
  try {
58
- const stat = import_fs.default.statSync(traceUrl);
59
+ const stat = import_fs.default.statSync(traceFile);
59
60
  if (stat.isDirectory())
60
- return import_path.default.join(traceUrl, tracesDirMarker);
61
- return traceUrl;
61
+ traceFile = import_path.default.join(traceFile, tracesDirMarker);
62
+ return toFilePathUrl(traceFile);
62
63
  } catch {
63
- throw new Error(`Trace file ${traceUrl} does not exist!`);
64
+ throw new Error(`Trace file ${traceFileOrUrl} does not exist!`);
64
65
  }
65
66
  }
66
67
  async function startTraceViewerServer(options) {
@@ -221,15 +222,18 @@ function traceDescriptor(traceDir, tracePrefix) {
221
222
  };
222
223
  for (const name of import_fs.default.readdirSync(traceDir)) {
223
224
  if (!tracePrefix || name.startsWith(tracePrefix))
224
- result.entries.push({ name, path: import_path.default.join(traceDir, name) });
225
+ result.entries.push({ name, path: toFilePathUrl(import_path.default.join(traceDir, name)) });
225
226
  }
226
227
  const resourcesDir = import_path.default.join(traceDir, "resources");
227
228
  if (import_fs.default.existsSync(resourcesDir)) {
228
229
  for (const name of import_fs.default.readdirSync(resourcesDir))
229
- result.entries.push({ name: "resources/" + name, path: import_path.default.join(resourcesDir, name) });
230
+ result.entries.push({ name: "resources/" + name, path: toFilePathUrl(import_path.default.join(resourcesDir, name)) });
230
231
  }
231
232
  return result;
232
233
  }
234
+ function toFilePathUrl(filePath) {
235
+ return `file?path=${encodeURIComponent(filePath)}`;
236
+ }
233
237
  // Annotate the CommonJS export names for ESM import in node:
234
238
  0 && (module.exports = {
235
239
  installRootRedirect,