testdriverai 7.2.92 → 7.3.2

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.
@@ -215,28 +215,7 @@ const createCommands = (
215
215
  return result;
216
216
  };
217
217
 
218
- const assert = async (assertion, shouldThrow = false) => {
219
- let assertStartTimeForHandler;
220
- const handleAssertResponse = (response) => {
221
- const { formatter } = require("../../sdk-log-formatter.js");
222
- const passed = response.indexOf("The task passed") > -1;
223
- const duration = assertStartTimeForHandler ? Date.now() - assertStartTimeForHandler : undefined;
224
-
225
- emitter.emit(events.log.narration, formatter.formatAssertResult(passed, response, duration), true);
226
-
227
- if (passed) {
228
- return true;
229
- } else {
230
- if (shouldThrow) {
231
- // Is fatal, otherwise it just changes the assertion to be true
232
- const errorMessage = `AI Assertion failed: ${assertion}\n${response}`;
233
- throw new MatchError(errorMessage, true);
234
- } else {
235
- return false;
236
- }
237
- }
238
- };
239
-
218
+ const assert = async (assertion, shouldThrow = false, options = {}) => {
240
219
  // Log asserting action
241
220
  const { formatter } = require("../../sdk-log-formatter.js");
242
221
  const assertingMessage = formatter.formatAsserting(assertion);
@@ -246,37 +225,80 @@ const createCommands = (
246
225
  // Frontend will calculate relative time using: timestamp - replay.clientStartDate
247
226
  const assertTimestamp = Date.now();
248
227
  const assertStartTime = assertTimestamp;
249
- assertStartTimeForHandler = assertStartTime;
250
228
 
229
+ // Extract cache options
230
+ const { threshold = -1, cacheKey, os, resolution } = options;
231
+
232
+ // Debug log cache settings
233
+ emitter.emit(
234
+ events.log.debug,
235
+ `🔍 assert() threshold: ${threshold} (cache ${threshold < 0 ? "DISABLED" : "ENABLED"}${cacheKey ? `, cacheKey: ${cacheKey.substring(0, 8)}...` : ""})`,
236
+ );
237
+
238
+ // Use v7 endpoint for assert with caching support
251
239
  let response = await sdk.req("assert", {
252
240
  expect: assertion,
253
241
  image: await system.captureScreenBase64(),
242
+ threshold,
243
+ cacheKey,
244
+ os,
245
+ resolution,
254
246
  });
247
+
255
248
  const assertDuration = Date.now() - assertStartTime;
256
249
 
257
- // Determine if assertion passed or failed
258
- const assertionPassed = response.data.indexOf("The task passed") > -1;
250
+ // Handle both old (string) and new (object) response formats
251
+ // New v7 API returns: { data: { passed, reasoning, content, cacheHit... }, cacheHit, similarity }
252
+ // Old API returns: { data: "The task passed/failed..." }
253
+ let passed;
254
+ let responseText;
255
+ let cacheHit = false;
256
+ let similarity = null;
259
257
 
260
- // Track interaction with success/failure
258
+ if (typeof response.data === 'object' && response.data !== null) {
259
+ // New structured response
260
+ passed = response.data.passed;
261
+ responseText = response.data.content || response.data.reasoning || '';
262
+ cacheHit = response.cacheHit || response.data.cacheHit || false;
263
+ similarity = response.similarity || response.data.cacheSimilarity;
264
+ } else {
265
+ // Old string response (backward compatibility)
266
+ responseText = response.data || '';
267
+ passed = responseText.indexOf("The task passed") > -1;
268
+ }
269
+
270
+ // Log the result with cache info
271
+ emitter.emit(events.log.narration, formatter.formatAssertResult(passed, responseText, assertDuration, cacheHit), true);
272
+
273
+ // Track interaction with success/failure (fire-and-forget)
261
274
  const sessionId = sessionInstance?.get();
262
275
  if (sessionId) {
263
- try {
264
- await sandbox.send({
265
- type: "trackInteraction",
266
- interactionType: "assert",
267
- session: sessionId,
268
- prompt: assertion,
269
- timestamp: assertTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
270
- duration: assertDuration,
271
- success: assertionPassed,
272
- error: assertionPassed ? undefined : response.data,
273
- });
274
- } catch (err) {
276
+ sandbox.send({
277
+ type: "trackInteraction",
278
+ interactionType: "assert",
279
+ session: sessionId,
280
+ prompt: assertion,
281
+ timestamp: assertTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
282
+ duration: assertDuration,
283
+ success: passed,
284
+ error: passed ? undefined : responseText,
285
+ cacheHit: cacheHit,
286
+ }).catch((err) => {
275
287
  console.warn("Failed to track assert interaction:", err.message);
276
- }
288
+ });
277
289
  }
278
290
 
279
- return handleAssertResponse(response.data);
291
+ if (passed) {
292
+ return true;
293
+ } else {
294
+ if (shouldThrow) {
295
+ // Is fatal, otherwise it just changes the assertion to be true
296
+ const errorMessage = `AI Assertion failed: ${assertion}\n${responseText}`;
297
+ throw new MatchError(errorMessage, true);
298
+ } else {
299
+ return false;
300
+ }
301
+ }
280
302
  };
281
303
 
282
304
  /**
@@ -378,44 +400,40 @@ const createCommands = (
378
400
  true,
379
401
  );
380
402
 
381
- // Track interaction success
403
+ // Track interaction success (fire-and-forget)
382
404
  const sessionId = sessionInstance?.get();
383
405
  if (sessionId) {
384
- try {
385
- const scrollDuration = Date.now() - scrollStartTime;
386
- await sandbox.send({
387
- type: "trackInteraction",
388
- interactionType: "scroll",
389
- session: sessionId,
390
- input: { direction, amount },
391
- timestamp: scrollTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
392
- duration: scrollDuration,
393
- success: scrollSuccess,
394
- error: scrollError,
395
- });
396
- } catch (err) {
406
+ const scrollDuration = Date.now() - scrollStartTime;
407
+ sandbox.send({
408
+ type: "trackInteraction",
409
+ interactionType: "scroll",
410
+ session: sessionId,
411
+ input: { direction, amount },
412
+ timestamp: scrollTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
413
+ duration: scrollDuration,
414
+ success: scrollSuccess,
415
+ error: scrollError,
416
+ }).catch((err) => {
397
417
  console.warn("Failed to track scroll interaction:", err.message);
398
- }
418
+ });
399
419
  }
400
420
  } catch (error) {
401
- // Track interaction failure
421
+ // Track interaction failure (fire-and-forget)
402
422
  const sessionId = sessionInstance?.get();
403
423
  if (sessionId) {
404
- try {
405
- const scrollDuration = Date.now() - scrollStartTime;
406
- await sandbox.send({
407
- type: "trackInteraction",
408
- interactionType: "scroll",
409
- session: sessionId,
410
- input: { direction, amount },
411
- timestamp: scrollTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
412
- duration: scrollDuration,
413
- success: false,
414
- error: error.message,
415
- });
416
- } catch (err) {
424
+ const scrollDuration = Date.now() - scrollStartTime;
425
+ sandbox.send({
426
+ type: "trackInteraction",
427
+ interactionType: "scroll",
428
+ session: sessionId,
429
+ input: { direction, amount },
430
+ timestamp: scrollTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
431
+ duration: scrollDuration,
432
+ success: false,
433
+ error: error.message,
434
+ }).catch((err) => {
417
435
  console.warn("Failed to track scroll interaction:", err.message);
418
- }
436
+ });
419
437
  }
420
438
  throw error;
421
439
  }
@@ -527,26 +545,24 @@ const createCommands = (
527
545
  const actionEndTime = Date.now();
528
546
  const actionDuration = actionEndTime - clickStartTime;
529
547
 
530
- // Track interaction
548
+ // Track interaction (fire-and-forget)
531
549
  const sessionId = sessionInstance?.get();
532
550
  if (sessionId && elementData.prompt) {
533
- try {
534
- await sandbox.send({
535
- type: "trackInteraction",
536
- interactionType: "click",
537
- session: sessionId,
538
- prompt: elementData.prompt,
539
- input: { x, y, action },
540
- timestamp: clickTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
541
- duration: actionDuration,
542
- success: true,
543
- cacheHit: elementData.cacheHit,
544
- selector: elementData.selector,
545
- selectorUsed: elementData.selectorUsed,
546
- });
547
- } catch (err) {
551
+ sandbox.send({
552
+ type: "trackInteraction",
553
+ interactionType: "click",
554
+ session: sessionId,
555
+ prompt: elementData.prompt,
556
+ input: { x, y, action },
557
+ timestamp: clickTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
558
+ duration: actionDuration,
559
+ success: true,
560
+ cacheHit: elementData.cacheHit,
561
+ selector: elementData.selector,
562
+ selectorUsed: elementData.selectorUsed,
563
+ }).catch((err) => {
548
564
  console.warn("Failed to track click interaction:", err.message);
549
- }
565
+ });
550
566
  }
551
567
 
552
568
  // Wait for redraw and track duration
@@ -577,28 +593,26 @@ const createCommands = (
577
593
 
578
594
  return;
579
595
  } catch (error) {
580
- // Track interaction failure
596
+ // Track interaction failure (fire-and-forget)
581
597
  const sessionId = sessionInstance?.get();
582
598
  if (sessionId && elementData.prompt) {
583
- try {
584
- const clickDuration = Date.now() - clickStartTime;
585
- await sandbox.send({
586
- type: "trackInteraction",
587
- interactionType: "click",
588
- session: sessionId,
589
- prompt: elementData.prompt,
590
- input: { x, y, action },
591
- timestamp: clickTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
592
- duration: clickDuration,
593
- success: false,
594
- error: error.message,
595
- cacheHit: elementData.cacheHit,
596
- selector: elementData.selector,
597
- selectorUsed: elementData.selectorUsed,
598
- });
599
- } catch (err) {
599
+ const clickDuration = Date.now() - clickStartTime;
600
+ sandbox.send({
601
+ type: "trackInteraction",
602
+ interactionType: "click",
603
+ session: sessionId,
604
+ prompt: elementData.prompt,
605
+ input: { x, y, action },
606
+ timestamp: clickTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
607
+ duration: clickDuration,
608
+ success: false,
609
+ error: error.message,
610
+ cacheHit: elementData.cacheHit,
611
+ selector: elementData.selector,
612
+ selectorUsed: elementData.selectorUsed,
613
+ }).catch((err) => {
600
614
  console.warn("Failed to track click interaction:", err.message);
601
- }
615
+ });
602
616
  }
603
617
  throw error;
604
618
  }
@@ -647,29 +661,27 @@ const createCommands = (
647
661
 
648
662
  await sandbox.send({ type: "moveMouse", x, y, ...elementData });
649
663
 
650
- // Track interaction
664
+ // Track interaction (fire-and-forget)
651
665
  const sessionId = sessionInstance?.get();
652
666
  const actionEndTime = Date.now();
653
667
  const actionDuration = actionEndTime - hoverStartTime;
654
668
 
655
669
  if (sessionId && elementData.prompt) {
656
- try {
657
- await sandbox.send({
658
- type: "trackInteraction",
659
- interactionType: "hover",
660
- session: sessionId,
661
- prompt: elementData.prompt,
662
- input: { x, y },
663
- timestamp: hoverTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
664
- duration: actionDuration,
665
- success: true,
666
- cacheHit: elementData.cacheHit,
667
- selector: elementData.selector,
668
- selectorUsed: elementData.selectorUsed,
669
- });
670
- } catch (err) {
670
+ sandbox.send({
671
+ type: "trackInteraction",
672
+ interactionType: "hover",
673
+ session: sessionId,
674
+ prompt: elementData.prompt,
675
+ input: { x, y },
676
+ timestamp: hoverTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
677
+ duration: actionDuration,
678
+ success: true,
679
+ cacheHit: elementData.cacheHit,
680
+ selector: elementData.selector,
681
+ selectorUsed: elementData.selectorUsed,
682
+ }).catch((err) => {
671
683
  console.warn("Failed to track hover interaction:", err.message);
672
- }
684
+ });
673
685
  }
674
686
 
675
687
  // Wait for redraw and track duration
@@ -688,28 +700,26 @@ const createCommands = (
688
700
 
689
701
  return;
690
702
  } catch (error) {
691
- // Track interaction failure
703
+ // Track interaction failure (fire-and-forget)
692
704
  const sessionId = sessionInstance?.get();
693
705
  if (sessionId && elementData.prompt) {
694
- try {
695
- const hoverDuration = Date.now() - hoverStartTime;
696
- await sandbox.send({
697
- type: "trackInteraction",
698
- interactionType: "hover",
699
- session: sessionId,
700
- prompt: elementData.prompt,
701
- input: { x, y },
702
- timestamp: hoverTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
703
- duration: hoverDuration,
704
- success: false,
705
- error: error.message,
706
- cacheHit: elementData.cacheHit,
707
- selector: elementData.selector,
708
- selectorUsed: elementData.selectorUsed,
709
- });
710
- } catch (err) {
706
+ const hoverDuration = Date.now() - hoverStartTime;
707
+ sandbox.send({
708
+ type: "trackInteraction",
709
+ interactionType: "hover",
710
+ session: sessionId,
711
+ prompt: elementData.prompt,
712
+ input: { x, y },
713
+ timestamp: hoverTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
714
+ duration: hoverDuration,
715
+ success: false,
716
+ error: error.message,
717
+ cacheHit: elementData.cacheHit,
718
+ selector: elementData.selector,
719
+ selectorUsed: elementData.selectorUsed,
720
+ }).catch((err) => {
711
721
  console.warn("Failed to track hover interaction:", err.message);
712
- }
722
+ });
713
723
  }
714
724
  throw error;
715
725
  }
@@ -903,25 +913,23 @@ const createCommands = (
903
913
  const typeActionEndTime = Date.now();
904
914
  emitter.emit(events.log.narration, formatter.formatTypeResult(text, secret, typeActionEndTime - typeStartTime), true);
905
915
 
906
- // Track interaction
916
+ // Track interaction (fire-and-forget)
907
917
  const sessionId = sessionInstance?.get();
908
918
  if (sessionId) {
909
- try {
910
- const typeDuration = Date.now() - typeStartTime;
911
- await sandbox.send({
912
- type: "trackInteraction",
913
- interactionType: "type",
914
- session: sessionId,
915
- // Store masked text if secret, otherwise store actual text
916
- input: { text: secret ? "****" : text, delay },
917
- timestamp: typeTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
918
- duration: typeDuration,
919
- success: true,
920
- isSecret: secret, // Flag this interaction if it contains a secret
921
- });
922
- } catch (err) {
919
+ const typeDuration = Date.now() - typeStartTime;
920
+ sandbox.send({
921
+ type: "trackInteraction",
922
+ interactionType: "type",
923
+ session: sessionId,
924
+ // Store masked text if secret, otherwise store actual text
925
+ input: { text: secret ? "****" : text, delay },
926
+ timestamp: typeTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
927
+ duration: typeDuration,
928
+ success: true,
929
+ isSecret: secret, // Flag this interaction if it contains a secret
930
+ }).catch((err) => {
923
931
  console.warn("Failed to track type interaction:", err.message);
924
- }
932
+ });
925
933
  }
926
934
 
927
935
  const redrawStartTime = Date.now();
@@ -978,23 +986,21 @@ const createCommands = (
978
986
  true,
979
987
  );
980
988
 
981
- // Track interaction
989
+ // Track interaction (fire-and-forget)
982
990
  const sessionId = sessionInstance?.get();
983
991
  if (sessionId) {
984
- try {
985
- const pressKeysDuration = Date.now() - pressKeysStartTime;
986
- await sandbox.send({
987
- type: "trackInteraction",
988
- interactionType: "pressKeys",
989
- session: sessionId,
990
- input: { keys },
991
- timestamp: pressKeysTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
992
- duration: pressKeysDuration,
993
- success: true,
994
- });
995
- } catch (err) {
992
+ const pressKeysDuration = Date.now() - pressKeysStartTime;
993
+ sandbox.send({
994
+ type: "trackInteraction",
995
+ interactionType: "pressKeys",
996
+ session: sessionId,
997
+ input: { keys },
998
+ timestamp: pressKeysTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
999
+ duration: pressKeysDuration,
1000
+ success: true,
1001
+ }).catch((err) => {
996
1002
  console.warn("Failed to track pressKeys interaction:", err.message);
997
- }
1003
+ });
998
1004
  }
999
1005
 
1000
1006
  const redrawStartTime = Date.now();
@@ -1023,23 +1029,21 @@ const createCommands = (
1023
1029
  emitter.emit(events.log.narration, theme.dim(`waiting ${timeout}ms...`));
1024
1030
  const result = await delay(timeout);
1025
1031
 
1026
- // Track interaction
1032
+ // Track interaction (fire-and-forget)
1027
1033
  const sessionId = sessionInstance?.get();
1028
1034
  if (sessionId) {
1029
- try {
1030
- const waitDuration = Date.now() - waitStartTime;
1031
- await sandbox.send({
1032
- type: "trackInteraction",
1033
- interactionType: "wait",
1034
- session: sessionId,
1035
- input: { timeout },
1036
- timestamp: waitTimestamp, // Use dashcam elapsed time instead of absolute time
1037
- duration: waitDuration,
1038
- success: true,
1039
- });
1040
- } catch (err) {
1035
+ const waitDuration = Date.now() - waitStartTime;
1036
+ sandbox.send({
1037
+ type: "trackInteraction",
1038
+ interactionType: "wait",
1039
+ session: sessionId,
1040
+ input: { timeout },
1041
+ timestamp: waitTimestamp, // Use dashcam elapsed time instead of absolute time
1042
+ duration: waitDuration,
1043
+ success: true,
1044
+ }).catch((err) => {
1041
1045
  console.warn("Failed to track wait interaction:", err.message);
1042
- }
1046
+ });
1043
1047
  }
1044
1048
 
1045
1049
  return result;
@@ -1099,53 +1103,49 @@ const createCommands = (
1099
1103
  emitter.emit(
1100
1104
  events.log.narration,
1101
1105
  theme.dim(
1102
- `An image matching the description "${description}" found!`,
1106
+ `An image matching the description \"${description}\" found!`,
1103
1107
  ),
1104
1108
  true,
1105
1109
  );
1106
1110
 
1107
- // Track interaction success
1111
+ // Track interaction success (fire-and-forget)
1108
1112
  const sessionId = sessionInstance?.get();
1109
1113
  if (sessionId) {
1110
- try {
1111
- const waitForImageDuration = Date.now() - startTime;
1112
- await sandbox.send({
1113
- type: "trackInteraction",
1114
- interactionType: "waitForImage",
1115
- session: sessionId,
1116
- prompt: description,
1117
- input: { timeout },
1118
- timestamp: waitForImageTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
1119
- duration: waitForImageDuration,
1120
- success: true,
1121
- });
1122
- } catch (err) {
1114
+ const waitForImageDuration = Date.now() - startTime;
1115
+ sandbox.send({
1116
+ type: "trackInteraction",
1117
+ interactionType: "waitForImage",
1118
+ session: sessionId,
1119
+ prompt: description,
1120
+ input: { timeout },
1121
+ timestamp: waitForImageTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
1122
+ duration: waitForImageDuration,
1123
+ success: true,
1124
+ }).catch((err) => {
1123
1125
  console.warn("Failed to track waitForImage interaction:", err.message);
1124
- }
1126
+ });
1125
1127
  }
1126
1128
 
1127
1129
  return;
1128
1130
  } else {
1129
- // Track interaction failure
1131
+ // Track interaction failure (fire-and-forget)
1130
1132
  const sessionId = sessionInstance?.get();
1131
- const errorMsg = `Timed out (${niceSeconds(timeout)} seconds) while searching for an image matching the description "${description}"`;
1133
+ const errorMsg = `Timed out (${niceSeconds(timeout)} seconds) while searching for an image matching the description \"${description}\"`;
1132
1134
  if (sessionId) {
1133
- try {
1134
- const waitForImageDuration = Date.now() - startTime;
1135
- await sandbox.send({
1136
- type: "trackInteraction",
1137
- interactionType: "waitForImage",
1138
- session: sessionId,
1139
- prompt: description,
1140
- input: { timeout },
1141
- timestamp: waitForImageTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
1142
- duration: waitForImageDuration,
1143
- success: false,
1144
- error: errorMsg,
1145
- });
1146
- } catch (err) {
1135
+ const waitForImageDuration = Date.now() - startTime;
1136
+ sandbox.send({
1137
+ type: "trackInteraction",
1138
+ interactionType: "waitForImage",
1139
+ session: sessionId,
1140
+ prompt: description,
1141
+ input: { timeout },
1142
+ timestamp: waitForImageTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
1143
+ duration: waitForImageDuration,
1144
+ success: false,
1145
+ error: errorMsg,
1146
+ }).catch((err) => {
1147
1147
  console.warn("Failed to track waitForImage interaction:", err.message);
1148
- }
1148
+ });
1149
1149
  }
1150
1150
 
1151
1151
  throw new MatchError(errorMsg);
@@ -1217,48 +1217,44 @@ const createCommands = (
1217
1217
  if (passed) {
1218
1218
  emitter.emit(events.log.narration, theme.dim(`"${text}" found!`), true);
1219
1219
 
1220
- // Track interaction success
1220
+ // Track interaction success (fire-and-forget)
1221
1221
  const sessionId = sessionInstance?.get();
1222
1222
  if (sessionId) {
1223
- try {
1224
- const waitForTextDuration = Date.now() - startTime;
1225
- await sandbox.send({
1226
- type: "trackInteraction",
1227
- interactionType: "waitForText",
1228
- session: sessionId,
1229
- prompt: text,
1230
- input: { timeout },
1231
- timestamp: waitForTextTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
1232
- duration: waitForTextDuration,
1233
- success: true,
1234
- });
1235
- } catch (err) {
1223
+ const waitForTextDuration = Date.now() - startTime;
1224
+ sandbox.send({
1225
+ type: "trackInteraction",
1226
+ interactionType: "waitForText",
1227
+ session: sessionId,
1228
+ prompt: text,
1229
+ input: { timeout },
1230
+ timestamp: waitForTextTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
1231
+ duration: waitForTextDuration,
1232
+ success: true,
1233
+ }).catch((err) => {
1236
1234
  console.warn("Failed to track waitForText interaction:", err.message);
1237
- }
1235
+ });
1238
1236
  }
1239
1237
 
1240
1238
  return;
1241
1239
  } else {
1242
- // Track interaction failure
1240
+ // Track interaction failure (fire-and-forget)
1243
1241
  const sessionId = sessionInstance?.get();
1244
1242
  const errorMsg = `Timed out (${niceSeconds(timeout)} seconds) while searching for "${text}"`;
1245
1243
  if (sessionId) {
1246
- try {
1247
- const waitForTextDuration = Date.now() - startTime;
1248
- await sandbox.send({
1249
- type: "trackInteraction",
1250
- interactionType: "waitForText",
1251
- session: sessionId,
1252
- prompt: text,
1253
- input: { timeout },
1254
- timestamp: waitForTextTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
1255
- duration: waitForTextDuration,
1256
- success: false,
1257
- error: errorMsg,
1258
- });
1259
- } catch (err) {
1244
+ const waitForTextDuration = Date.now() - startTime;
1245
+ sandbox.send({
1246
+ type: "trackInteraction",
1247
+ interactionType: "waitForText",
1248
+ session: sessionId,
1249
+ prompt: text,
1250
+ input: { timeout },
1251
+ timestamp: waitForTextTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
1252
+ duration: waitForTextDuration,
1253
+ success: false,
1254
+ error: errorMsg,
1255
+ }).catch((err) => {
1260
1256
  console.warn("Failed to track waitForText interaction:", err.message);
1261
- }
1257
+ });
1262
1258
  }
1263
1259
 
1264
1260
  throw new MatchError(errorMsg);
@@ -1514,12 +1510,16 @@ const createCommands = (
1514
1510
  /**
1515
1511
  * Make an AI-powered assertion
1516
1512
  * @param {string} assertion - Assertion to check
1517
- * @param {Object} [options] - Additional options (reserved for future use)
1513
+ * @param {Object} [options] - Additional options
1514
+ * @param {number} [options.threshold] - Cache threshold (0-1). Lower values require closer matches. Set to -1 to disable cache.
1515
+ * @param {string} [options.cacheKey] - Cache key for grouping cached assertions (enables caching when provided)
1516
+ * @param {string} [options.os] - Operating system identifier for cache partitioning
1517
+ * @param {string} [options.resolution] - Screen resolution for cache partitioning
1518
1518
  */
1519
1519
  "assert": async (assertion, options = {}) => {
1520
1520
  // In soft assert mode (during act()), don't throw on failure
1521
1521
  const shouldThrow = !getSoftAssertMode();
1522
- let response = await assert(assertion, shouldThrow);
1522
+ let response = await assert(assertion, shouldThrow, options);
1523
1523
 
1524
1524
  return response;
1525
1525
  },