@midscene/web 0.0.1 → 0.1.1

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/dist/lib/index.js CHANGED
@@ -47,26 +47,27 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
47
47
  // src/index.ts
48
48
  var src_exports = {};
49
49
  __export(src_exports, {
50
- PlaywrightAiFixture: () => PlaywrightAiFixture
50
+ PlaywrightAiFixture: () => PlaywrightAiFixture,
51
+ PuppeteerAgent: () => PageAgent
51
52
  });
52
53
  module.exports = __toCommonJS(src_exports);
53
54
 
54
55
  // src/playwright/index.ts
55
- var import_utils3 = require("@midscene/core/utils");
56
+ var import_crypto = require("crypto");
56
57
 
57
- // src/playwright/actions.ts
58
- var import_assert2 = __toESM(require("assert"));
59
- var import_core = __toESM(require("@midscene/core"));
60
- var import_utils = require("@midscene/core/utils");
61
- var import_image2 = require("@midscene/core/image");
58
+ // src/playwright/cache.ts
59
+ var import_path2 = __toESM(require("path"));
60
+ var import_fs2 = __toESM(require("fs"));
61
+ var import_utils2 = require("@midscene/core/utils");
62
62
 
63
- // src/playwright/utils.ts
63
+ // src/common/utils.ts
64
64
  var import_fs = __toESM(require("fs"));
65
65
  var import_assert = __toESM(require("assert"));
66
66
  var import_path = __toESM(require("path"));
67
67
  var import_image = require("@midscene/core/image");
68
+ var import_utils = require("@midscene/core/utils");
68
69
 
69
- // src/playwright/element.ts
70
+ // src/web-element.ts
70
71
  var WebElementInfo = class {
71
72
  constructor({
72
73
  content,
@@ -84,24 +85,13 @@ var WebElementInfo = class {
84
85
  this.id = id;
85
86
  this.attributes = attributes;
86
87
  }
87
- async tap() {
88
- await this.page.mouse.click(this.center[0], this.center[1]);
89
- }
90
- async hover() {
91
- await this.page.mouse.move(this.center[0], this.center[1]);
92
- }
93
- async type(text) {
94
- await this.page.keyboard.type(text);
95
- }
96
- async press(key) {
97
- await this.page.keyboard.press(key);
98
- }
99
88
  };
100
89
 
101
- // src/playwright/utils.ts
102
- async function parseContextFromPlaywrightPage(page, _opt) {
90
+ // src/common/utils.ts
91
+ async function parseContextFromWebPage(page, _opt) {
103
92
  (0, import_assert.default)(page, "page is required");
104
- const file = "/Users/bytedance/workspace/midscene/packages/midscene/tests/fixtures/heytea.jpeg";
93
+ const url = page.url();
94
+ const file = (0, import_utils.getTmpFile)("jpeg");
105
95
  await page.screenshot({ path: file, type: "jpeg", quality: 75 });
106
96
  const screenshotBuffer = (0, import_fs.readFileSync)(file);
107
97
  const screenshotBase64 = (0, import_image.base64Encoded)(file);
@@ -111,7 +101,8 @@ async function parseContextFromPlaywrightPage(page, _opt) {
111
101
  return {
112
102
  content: elementsInfo,
113
103
  size,
114
- screenshotBase64
104
+ screenshotBase64,
105
+ url
115
106
  };
116
107
  }
117
108
  async function getElementInfosFromPage(page) {
@@ -153,18 +144,136 @@ function findNearestPackageJson(dir) {
153
144
  return findNearestPackageJson(parentDir);
154
145
  }
155
146
 
156
- // src/playwright/actions.ts
157
- var PlayWrightActionAgent = class {
158
- constructor(page, opt) {
147
+ // src/playwright/cache.ts
148
+ function writeTestCache(taskFile, taskTitle, taskCacheJson) {
149
+ const packageJson = getPkgInfo();
150
+ (0, import_utils2.writeDumpFile)({
151
+ fileName: `${taskFile}(${taskTitle})`,
152
+ fileExt: "json",
153
+ fileContent: JSON.stringify(
154
+ __spreadValues({
155
+ pkgName: packageJson.name,
156
+ pkgVersion: packageJson.version,
157
+ taskFile,
158
+ taskTitle
159
+ }, taskCacheJson),
160
+ null,
161
+ 2
162
+ ),
163
+ type: "cache"
164
+ });
165
+ }
166
+ function readTestCache(taskFile, taskTitle) {
167
+ const cacheFile = (0, import_path2.join)((0, import_utils2.getDumpDirPath)("cache"), `${taskFile}(${taskTitle}).json`);
168
+ const pkgInfo = getPkgInfo();
169
+ if (process.env.MIDSCENE_CACHE === "true" && import_fs2.default.existsSync(cacheFile)) {
170
+ try {
171
+ const data = import_fs2.default.readFileSync(cacheFile, "utf8");
172
+ const jsonData = JSON.parse(data);
173
+ if (jsonData.pkgName !== pkgInfo.name || jsonData.pkgVersion !== pkgInfo.version) {
174
+ return void 0;
175
+ }
176
+ return jsonData;
177
+ } catch (err) {
178
+ return void 0;
179
+ }
180
+ }
181
+ return void 0;
182
+ }
183
+ function getPkgInfo() {
184
+ const packageJsonDir = findNearestPackageJson(__dirname);
185
+ if (!packageJsonDir) {
186
+ console.error("Cannot find package.json directory: ", __dirname);
187
+ return {
188
+ name: "@midscene/web",
189
+ version: "0.0.0"
190
+ };
191
+ }
192
+ const packageJsonPath = import_path2.default.join(packageJsonDir, "package.json");
193
+ const data = import_fs2.default.readFileSync(packageJsonPath, "utf8");
194
+ const packageJson = JSON.parse(data);
195
+ return {
196
+ name: packageJson.name,
197
+ version: packageJson.version
198
+ };
199
+ }
200
+
201
+ // src/common/agent.ts
202
+ var import_utils6 = require("@midscene/core/utils");
203
+
204
+ // src/common/tasks.ts
205
+ var import_assert2 = __toESM(require("assert"));
206
+ var import_core = __toESM(require("@midscene/core"));
207
+ var import_utils4 = require("@midscene/core/utils");
208
+ var import_image2 = require("@midscene/core/image");
209
+
210
+ // src/common/task-cache.ts
211
+ var TaskCache = class {
212
+ constructor(opts) {
213
+ this.cache = opts == null ? void 0 : opts.cache;
214
+ this.newCache = {
215
+ aiTasks: []
216
+ };
217
+ }
218
+ readCache(pageContext, type, userPrompt) {
219
+ var _a;
220
+ if (this.cache) {
221
+ const { aiTasks } = this.cache;
222
+ const index = aiTasks.findIndex((item) => item.prompt === userPrompt);
223
+ if (index === -1) {
224
+ return false;
225
+ }
226
+ const taskRes = aiTasks.splice(index, 1)[0];
227
+ if ((taskRes == null ? void 0 : taskRes.type) === "locate" && !((_a = taskRes.response) == null ? void 0 : _a.elements.every((element) => {
228
+ const findIndex = pageContext.content.findIndex(
229
+ (contentElement) => contentElement.id === element.id
230
+ );
231
+ if (findIndex === -1) {
232
+ return false;
233
+ }
234
+ return true;
235
+ }))) {
236
+ return false;
237
+ }
238
+ if (taskRes && taskRes.type === type && taskRes.prompt === userPrompt && this.pageContextEqual(taskRes.pageContext, pageContext)) {
239
+ return taskRes.response;
240
+ }
241
+ }
242
+ return false;
243
+ }
244
+ saveCache(cache) {
245
+ var _a;
246
+ if (cache) {
247
+ (_a = this.newCache) == null ? void 0 : _a.aiTasks.push(cache);
248
+ }
249
+ }
250
+ pageContextEqual(taskPageContext, pageContext) {
251
+ return taskPageContext.size.width === pageContext.size.width && taskPageContext.size.height === pageContext.size.height;
252
+ }
253
+ /**
254
+ * Generate task cache data.
255
+ * This method is mainly used to create or obtain some cached data for tasks, and it returns a new cache object.
256
+ * In the cache object, it may contain task-related information, states, or other necessary data.
257
+ * It is assumed that the `newCache` property already exists in the current class or object and is a data structure used to store task cache.
258
+ * @returns {Object} Returns a new cache object, which may include task cache data.
259
+ */
260
+ generateTaskCache() {
261
+ return this.newCache;
262
+ }
263
+ };
264
+
265
+ // src/common/tasks.ts
266
+ var PageTaskExecutor = class {
267
+ constructor(page, opts) {
159
268
  this.page = page;
160
269
  this.insight = new import_core.default(async () => {
161
- return await parseContextFromPlaywrightPage(page);
270
+ return await parseContextFromWebPage(page);
162
271
  });
163
- this.executor = new import_core.Executor((opt == null ? void 0 : opt.taskName) || "MidScene - PlayWrightAI");
272
+ this.taskCache = new TaskCache(opts);
164
273
  }
165
274
  async recordScreenshot(timing) {
166
- const file = (0, import_utils.getTmpFile)("jpeg");
167
- await this.page.screenshot(__spreadProps(__spreadValues({}, import_utils.commonScreenshotParam), {
275
+ const file = (0, import_utils4.getTmpFile)("jpeg");
276
+ await this.page.screenshot(__spreadProps(__spreadValues({}, import_utils4.commonScreenshotParam), {
168
277
  path: file
169
278
  }));
170
279
  const item = {
@@ -185,7 +294,7 @@ var PlayWrightActionAgent = class {
185
294
  recorder.push(shot);
186
295
  const result = await taskApply.executor(param, context, ...args);
187
296
  if (taskApply.type === "Action") {
188
- await (0, import_utils.sleep)(1e3);
297
+ await (0, import_utils4.sleep)(1e3);
189
298
  const shot2 = await this.recordScreenshot("after Action");
190
299
  recorder.push(shot2);
191
300
  }
@@ -200,23 +309,48 @@ var PlayWrightActionAgent = class {
200
309
  const taskFind = {
201
310
  type: "Insight",
202
311
  subType: "Locate",
203
- param: {
204
- prompt: plan2.thought
205
- },
312
+ param: plan2.param,
206
313
  executor: async (param) => {
207
314
  let insightDump;
208
315
  const dumpCollector = (dump) => {
209
316
  insightDump = dump;
210
317
  };
211
318
  this.insight.onceDumpUpdatedFn = dumpCollector;
212
- const element = await this.insight.locate(param.prompt);
319
+ const pageContext = await this.insight.contextRetrieverFn();
320
+ const locateCache = this.taskCache.readCache(pageContext, "locate", param.prompt);
321
+ let locateResult;
322
+ const callAI = this.insight.aiVendorFn;
323
+ const element = await this.insight.locate(param.prompt, {
324
+ callAI: async (message) => {
325
+ if (locateCache) {
326
+ locateResult = locateCache;
327
+ return Promise.resolve(locateCache);
328
+ }
329
+ locateResult = await callAI(message);
330
+ return locateResult;
331
+ }
332
+ });
213
333
  (0, import_assert2.default)(element, `Element not found: ${param.prompt}`);
334
+ if (locateResult) {
335
+ this.taskCache.saveCache({
336
+ type: "locate",
337
+ pageContext: {
338
+ url: pageContext.url,
339
+ size: pageContext.size
340
+ },
341
+ prompt: param.prompt,
342
+ response: locateResult
343
+ });
344
+ }
214
345
  return {
215
346
  output: {
216
347
  element
217
348
  },
218
349
  log: {
219
350
  dump: insightDump
351
+ },
352
+ cache: {
353
+ hit: Boolean(locateResult)
220
354
  }
221
355
  };
222
356
  }
@@ -227,7 +361,10 @@ var PlayWrightActionAgent = class {
227
361
  type: "Action",
228
362
  subType: "Input",
229
363
  param: plan2.param,
230
- executor: async (taskParam) => {
364
+ executor: async (taskParam, { element }) => {
365
+ if (element) {
366
+ await this.page.mouse.click(element.center[0], element.center[1]);
367
+ }
231
368
  (0, import_assert2.default)(taskParam.value, "No value to input");
232
369
  await this.page.keyboard.type(taskParam.value);
233
370
  }
@@ -302,43 +439,66 @@ var PlayWrightActionAgent = class {
302
439
  return tasks;
303
440
  }
304
441
  async action(userPrompt) {
305
- this.executor.description = userPrompt;
306
- const pageContext = await this.insight.contextRetrieverFn();
442
+ const taskExecutor = new import_core.Executor(userPrompt);
443
+ taskExecutor.description = userPrompt;
307
444
  let plans = [];
308
445
  const planningTask = {
309
446
  type: "Planning",
310
447
  param: {
311
448
  userPrompt
312
449
  },
313
- async executor(param) {
314
- const planResult = await (0, import_core.plan)(pageContext, param.userPrompt);
450
+ executor: async (param) => {
451
+ const pageContext = await this.insight.contextRetrieverFn();
452
+ let planResult;
453
+ const planCache = this.taskCache.readCache(pageContext, "plan", userPrompt);
454
+ if (planCache) {
455
+ planResult = planCache;
456
+ } else {
457
+ planResult = await (0, import_core.plan)(param.userPrompt, {
458
+ context: pageContext
459
+ });
460
+ }
315
461
  (0, import_assert2.default)(planResult.plans.length > 0, "No plans found");
316
462
  plans = planResult.plans;
463
+ this.taskCache.saveCache({
464
+ type: "plan",
465
+ pageContext: {
466
+ url: pageContext.url,
467
+ size: pageContext.size
468
+ },
469
+ prompt: userPrompt,
470
+ response: planResult
471
+ });
317
472
  return {
318
- output: planResult
473
+ output: planResult,
474
+ cache: {
475
+ hint: Boolean(planCache)
476
+ }
319
477
  };
320
478
  }
321
479
  };
322
480
  try {
323
- await this.executor.append(this.wrapExecutorWithScreenshot(planningTask));
324
- await this.executor.flush();
325
- this.actionDump = this.executor.dump();
481
+ await taskExecutor.append(this.wrapExecutorWithScreenshot(planningTask));
482
+ await taskExecutor.flush();
483
+ this.executionDump = taskExecutor.dump();
326
484
  const executables = await this.convertPlanToExecutable(plans);
327
- await this.executor.append(executables);
328
- await this.executor.flush();
329
- this.actionDump = this.executor.dump();
485
+ await taskExecutor.append(executables);
486
+ await taskExecutor.flush();
487
+ this.executionDump = taskExecutor.dump();
330
488
  (0, import_assert2.default)(
331
- this.executor.status !== "error",
332
- `failed to execute tasks: ${this.executor.status}, msg: ${this.executor.errorMsg || ""}`
489
+ taskExecutor.status !== "error",
490
+ `failed to execute tasks: ${taskExecutor.status}, msg: ${taskExecutor.errorMsg || ""}`
333
491
  );
334
492
  } catch (e) {
335
- this.actionDump = this.executor.dump();
493
+ this.executionDump = taskExecutor.dump();
336
494
  const err = new Error(e.message, { cause: e });
337
495
  throw err;
338
496
  }
339
497
  }
340
498
  async query(demand) {
341
- this.executor.description = JSON.stringify(demand);
499
+ const description = JSON.stringify(demand);
500
+ const taskExecutor = new import_core.Executor(description);
501
+ taskExecutor.description = description;
342
502
  let data;
343
503
  const queryTask = {
344
504
  type: "Insight",
@@ -360,11 +520,11 @@ var PlayWrightActionAgent = class {
360
520
  }
361
521
  };
362
522
  try {
363
- await this.executor.append(this.wrapExecutorWithScreenshot(queryTask));
364
- await this.executor.flush();
365
- this.actionDump = this.executor.dump();
523
+ await taskExecutor.append(this.wrapExecutorWithScreenshot(queryTask));
524
+ await taskExecutor.flush();
525
+ this.executionDump = taskExecutor.dump();
366
526
  } catch (e) {
367
- this.actionDump = this.executor.dump();
527
+ this.executionDump = taskExecutor.dump();
368
528
  const err = new Error(e.message, { cause: e });
369
529
  throw err;
370
530
  }
@@ -372,102 +532,171 @@ var PlayWrightActionAgent = class {
372
532
  }
373
533
  };
374
534
 
375
- // src/playwright/index.ts
376
- var PlaywrightAiFixture = () => {
377
- const dumps = [];
378
- const appendDump = (groupName, execution) => {
379
- let currentDump = dumps.find((dump) => dump.groupName === groupName);
380
- if (!currentDump) {
381
- currentDump = {
382
- groupName,
535
+ // src/common/agent.ts
536
+ var PageAgent = class {
537
+ constructor(page, opts) {
538
+ this.page = page;
539
+ this.dumps = [
540
+ {
541
+ groupName: (opts == null ? void 0 : opts.taskFile) || "unnamed",
383
542
  executions: []
384
- };
385
- dumps.push(currentDump);
386
- }
543
+ }
544
+ ];
545
+ this.testId = (opts == null ? void 0 : opts.testId) || String(process.pid);
546
+ this.actionAgent = new PageTaskExecutor(this.page, {
547
+ cache: (opts == null ? void 0 : opts.cache) || { aiTasks: [] }
548
+ });
549
+ }
550
+ appendDump(execution) {
551
+ const currentDump = this.dumps[0];
387
552
  currentDump.executions.push(execution);
388
- };
389
- const writeOutActionDumps = () => {
390
- (0, import_utils3.writeDumpFile)(`playwright-${process.pid}`, import_utils3.groupedActionDumpFileExt, JSON.stringify(dumps));
391
- };
392
- const groupAndCaseForTest = (testInfo) => {
393
- let groupName;
394
- let caseName;
395
- const titlePath = [...testInfo.titlePath];
396
- if (titlePath.length > 1) {
397
- caseName = titlePath.pop();
398
- groupName = titlePath.join(" > ");
399
- } else if (titlePath.length === 1) {
400
- caseName = titlePath[0];
401
- groupName = caseName;
402
- } else {
403
- caseName = "unnamed";
404
- groupName = "unnamed";
405
- }
406
- return { groupName, caseName };
407
- };
408
- const aiAction = async (page, testInfo, taskPrompt) => {
409
- const { groupName, caseName } = groupAndCaseForTest(testInfo);
410
- const actionAgent = new PlayWrightActionAgent(page, { taskName: caseName });
553
+ }
554
+ writeOutActionDumps() {
555
+ this.dumpFile = (0, import_utils6.writeDumpFile)({
556
+ fileName: `playwright-${this.testId}`,
557
+ fileExt: import_utils6.groupedActionDumpFileExt,
558
+ fileContent: JSON.stringify(this.dumps)
559
+ });
560
+ }
561
+ async aiAction(taskPrompt) {
411
562
  let error;
412
563
  try {
413
- await actionAgent.action(taskPrompt);
564
+ await this.actionAgent.action(taskPrompt);
414
565
  } catch (e) {
415
566
  error = e;
416
567
  }
417
- if (actionAgent.actionDump) {
418
- appendDump(groupName, actionAgent.actionDump);
419
- writeOutActionDumps();
568
+ if (this.actionAgent.executionDump) {
569
+ this.appendDump(this.actionAgent.executionDump);
570
+ this.writeOutActionDumps();
420
571
  }
421
572
  if (error) {
422
573
  console.error(error);
423
574
  throw new Error(error.message, { cause: error });
424
575
  }
425
- };
426
- const aiQuery = async (page, testInfo, demand) => {
427
- const { groupName, caseName } = groupAndCaseForTest(testInfo);
428
- const actionAgent = new PlayWrightActionAgent(page, { taskName: caseName });
576
+ }
577
+ async aiQuery(demand) {
429
578
  let error;
430
579
  let result;
431
580
  try {
432
- result = await actionAgent.query(demand);
581
+ result = await this.actionAgent.query(demand);
433
582
  } catch (e) {
434
583
  error = e;
435
584
  }
436
- if (actionAgent.actionDump) {
437
- appendDump(groupName, actionAgent.actionDump);
438
- writeOutActionDumps();
585
+ if (this.actionAgent.executionDump) {
586
+ this.appendDump(this.actionAgent.executionDump);
587
+ this.writeOutActionDumps();
439
588
  }
440
589
  if (error) {
441
590
  console.error(error);
442
591
  throw new Error(error.message, { cause: error });
443
592
  }
444
593
  return result;
594
+ }
595
+ async ai(taskPrompt, type = "action") {
596
+ if (type === "action") {
597
+ return this.aiAction(taskPrompt);
598
+ } else if (type === "query") {
599
+ return this.aiQuery(taskPrompt);
600
+ }
601
+ throw new Error(`Unknown or Unsupported task type: ${type}, only support 'action' or 'query'`);
602
+ }
603
+ };
604
+
605
+ // src/playwright/index.ts
606
+ var groupAndCaseForTest = (testInfo) => {
607
+ let taskFile;
608
+ let taskTitle;
609
+ const titlePath = [...testInfo.titlePath];
610
+ if (titlePath.length > 1) {
611
+ taskTitle = titlePath.pop();
612
+ taskFile = `${titlePath.join(" > ")}:${testInfo.line}`;
613
+ } else if (titlePath.length === 1) {
614
+ taskTitle = titlePath[0];
615
+ taskFile = `${taskTitle}:${testInfo.line}`;
616
+ } else {
617
+ taskTitle = "unnamed";
618
+ taskFile = "unnamed";
619
+ }
620
+ return { taskFile, taskTitle };
621
+ };
622
+ var midSceneAgentKeyId = "_midSceneAgentId";
623
+ var PlaywrightAiFixture = () => {
624
+ const pageAgentMap = {};
625
+ const agentForPage = (page, opts) => {
626
+ let idForPage = page[midSceneAgentKeyId];
627
+ if (!idForPage) {
628
+ idForPage = (0, import_crypto.randomUUID)();
629
+ page[midSceneAgentKeyId] = idForPage;
630
+ const testCase = readTestCache(opts.taskFile, opts.taskTitle) || { aiTasks: [] };
631
+ pageAgentMap[idForPage] = new PageAgent(page, {
632
+ testId: `${opts.testId}-${idForPage}`,
633
+ taskFile: opts.taskFile,
634
+ cache: testCase
635
+ });
636
+ }
637
+ return pageAgentMap[idForPage];
445
638
  };
446
639
  return {
447
- // shortcut
448
640
  ai: async ({ page }, use, testInfo) => {
449
- await use(async (taskPrompt, type = "action") => {
450
- if (type === "action") {
451
- return aiAction(page, testInfo, taskPrompt);
452
- } else if (type === "query") {
453
- return aiQuery(page, testInfo, taskPrompt);
454
- }
455
- throw new Error(`Unknown or Unsupported task type: ${type}, only support 'action' or 'query'`);
641
+ const { taskFile, taskTitle } = groupAndCaseForTest(testInfo);
642
+ const agent = agentForPage(page, { testId: testInfo.testId, taskFile, taskTitle });
643
+ await use(async (taskPrompt, opts) => {
644
+ await page.waitForLoadState("networkidle");
645
+ const actionType = (opts == null ? void 0 : opts.type) || "action";
646
+ const result = await agent.ai(taskPrompt, actionType);
647
+ return result;
456
648
  });
649
+ const taskCacheJson = agent.actionAgent.taskCache.generateTaskCache();
650
+ writeTestCache(taskFile, taskTitle, taskCacheJson);
651
+ if (agent.dumpFile) {
652
+ testInfo.annotations.push({
653
+ type: "MIDSCENE_AI_ACTION",
654
+ description: JSON.stringify({
655
+ testId: testInfo.testId,
656
+ dumpPath: agent.dumpFile
657
+ })
658
+ });
659
+ }
457
660
  },
458
661
  aiAction: async ({ page }, use, testInfo) => {
662
+ const { taskFile, taskTitle } = groupAndCaseForTest(testInfo);
663
+ const agent = agentForPage(page, { testId: testInfo.testId, taskFile, taskTitle });
459
664
  await use(async (taskPrompt) => {
460
- await aiAction(page, testInfo, taskPrompt);
665
+ await page.waitForLoadState("networkidle");
666
+ await agent.aiAction(taskPrompt);
461
667
  });
668
+ if (agent.dumpFile) {
669
+ testInfo.annotations.push({
670
+ type: "MIDSCENE_AI_ACTION",
671
+ description: JSON.stringify({
672
+ testId: testInfo.testId,
673
+ dumpPath: agent.dumpFile
674
+ })
675
+ });
676
+ }
462
677
  },
463
678
  aiQuery: async ({ page }, use, testInfo) => {
679
+ const { taskFile, taskTitle } = groupAndCaseForTest(testInfo);
680
+ const agent = agentForPage(page, { testId: testInfo.testId, taskFile, taskTitle });
464
681
  await use(async function(demand) {
465
- return aiQuery(page, testInfo, demand);
682
+ await page.waitForLoadState("networkidle");
683
+ const result = await agent.aiQuery(demand);
684
+ return result;
466
685
  });
686
+ if (agent.dumpFile) {
687
+ testInfo.annotations.push({
688
+ type: "MIDSCENE_AI_ACTION",
689
+ description: JSON.stringify({
690
+ testId: testInfo.testId,
691
+ dumpPath: agent.dumpFile
692
+ })
693
+ });
694
+ }
467
695
  }
468
696
  };
469
697
  };
470
698
  // Annotate the CommonJS export names for ESM import in node:
471
699
  0 && (module.exports = {
472
- PlaywrightAiFixture
700
+ PlaywrightAiFixture,
701
+ PuppeteerAgent
473
702
  });