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.
- package/agent/index.js +53 -13
- package/agent/lib/analytics.js +4 -1
- package/agent/lib/commands.js +252 -252
- package/agent/lib/sandbox.js +1 -1
- package/ai/skills/testdriver:aws-setup/SKILL.md +4 -4
- package/ai/skills/testdriver:captcha/SKILL.md +1 -1
- package/ai/skills/testdriver:ci-cd/SKILL.md +23 -23
- package/ai/skills/testdriver:cloud/SKILL.md +1 -1
- package/ai/skills/testdriver:customizing-devices/SKILL.md +5 -5
- package/ai/skills/testdriver:running-tests/SKILL.md +11 -11
- package/ai/skills/testdriver:secrets/SKILL.md +1 -1
- package/ai/skills/testdriver:testdriver/SKILL.md +5 -5
- package/ai/skills/testdriver:variables/SKILL.md +3 -3
- package/debugger/index.html +0 -36
- package/lib/vitest/hooks.mjs +3 -0
- package/package.json +1 -1
- package/sdk-log-formatter.js +8 -1
- package/sdk.d.ts +86 -2
- package/sdk.js +126 -24
package/agent/lib/commands.js
CHANGED
|
@@ -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
|
-
//
|
|
258
|
-
|
|
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
|
-
|
|
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
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
-
|
|
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
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
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
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
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
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
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
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
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
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
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
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
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
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
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
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
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
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
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
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
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
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
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
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
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
|
|
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
|
},
|