testdriverai 7.3.44 → 7.4.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.
@@ -33,8 +33,8 @@ const element = await testdriver.find(description, options)
33
33
  Similarity threshold (0-1) for cache matching. Lower values require more similarity. Set to -1 to disable cache.
34
34
  </ParamField>
35
35
 
36
- <ParamField path="timeout" type="number">
37
- Maximum time in milliseconds to poll for the element. Retries every 5 seconds until found or timeout expires.
36
+ <ParamField path="timeout" type="number" default={10000}>
37
+ Maximum time in milliseconds to poll for the element. Retries every 5 seconds until found or timeout expires. Defaults to `10000` (10 seconds). Set to `0` to disable polling and make a single attempt.
38
38
  </ParamField>
39
39
 
40
40
  <ParamField path="confidence" type="number">
@@ -309,19 +309,28 @@ const el = await testdriver.find('the blue submit button', { type: 'any' });
309
309
  </Tip>
310
310
  ## Polling for Dynamic Elements
311
311
 
312
- For elements that may not be immediately visible, use the `timeout` option to automatically poll:
312
+ By default, `find()` polls for up to 10 seconds (retrying every 5 seconds) until the element is found. You can customize this with the `timeout` option:
313
313
 
314
314
  ```javascript
315
- // Poll for element (retries every 5 seconds until found or timeout)
315
+ // Uses default 10s timeout - polls every 5 seconds
316
+ const element = await testdriver.find('login button');
317
+ await element.click();
318
+
319
+ // Custom timeout - wait up to 30 seconds
316
320
  const element = await testdriver.find('login button', { timeout: 30000 });
317
321
  await element.click();
322
+
323
+ // Disable polling - single attempt only
324
+ const element = await testdriver.find('login button', { timeout: 0 });
318
325
  ```
319
326
 
320
327
  The `timeout` option:
328
+ - Defaults to `10000` (10 seconds)
321
329
  - Retries finding the element every 5 seconds
322
330
  - Stops when the element is found or the timeout expires
323
331
  - Logs progress during polling
324
332
  - Returns the element (check `element.found()` if not throwing on failure)
333
+ - Set to `0` to disable polling and make a single attempt
325
334
 
326
335
  ## Zoom Mode for Crowded UIs
327
336
 
@@ -6,10 +6,16 @@ description: Handle async operations and prevent flaky tests
6
6
 
7
7
  ## Waiting for Elements
8
8
 
9
- Use the `timeout` option with `find()` to wait for elements that appear after async operations:
9
+ By default, `find()` automatically polls for up to 10 seconds, retrying every 5 seconds until the element is found. This means most elements that appear after short async operations will be found without any extra configuration.
10
+
11
+ For longer operations, increase the `timeout`:
10
12
 
11
13
  ```javascript
12
- // Wait up to 30 seconds for element to appear (polls every 5 seconds)
14
+ // Default behavior - polls for up to 10 seconds automatically
15
+ const element = await testdriver.find('Loading complete indicator');
16
+ await element.click();
17
+
18
+ // Wait up to 30 seconds for slower operations
13
19
  const element = await testdriver.find('Loading complete indicator', { timeout: 30000 });
14
20
  await element.click();
15
21
 
@@ -17,8 +23,8 @@ await element.click();
17
23
  await testdriver.find('submit button').click();
18
24
  await testdriver.find('success message', { timeout: 15000 });
19
25
 
20
- // Short timeout for quick checks
21
- const toast = await testdriver.find('notification toast', { timeout: 5000 });
26
+ // Disable polling for instant checks
27
+ const toast = await testdriver.find('notification toast', { timeout: 0 });
22
28
  ```
23
29
 
24
30
  ## Flake Prevention
@@ -137,6 +137,54 @@ const createCommands = (
137
137
 
138
138
  const delay = (t) => new Promise((resolve) => setTimeout(resolve, t));
139
139
 
140
+ /**
141
+ * Track an interaction via HTTP API (fire-and-forget)
142
+ * @param {Object} data - Interaction data
143
+ * @param {string} data.interactionType - Type of interaction (click, type, assert, etc.)
144
+ * @param {string} [data.prompt] - Description/prompt for the interaction
145
+ * @param {Object} [data.input] - Input data (varies by interaction type)
146
+ * @param {Object} [data.coordinates] - Coordinates {x, y}
147
+ * @param {number} data.timestamp - Absolute epoch timestamp
148
+ * @param {number} [data.duration] - Duration in ms
149
+ * @param {boolean} data.success - Whether the interaction succeeded
150
+ * @param {string} [data.error] - Error message if failed
151
+ * @param {boolean} [data.cacheHit] - Whether cache was used
152
+ * @param {string} [data.selector] - Selector ID
153
+ * @param {boolean} [data.selectorUsed] - Whether selector was used
154
+ * @param {number} [data.confidence] - AI confidence score
155
+ * @param {string} [data.reasoning] - AI reasoning
156
+ * @param {number} [data.similarity] - Cache similarity score
157
+ * @param {string} [data.screenshotUrl] - S3 key for screenshot
158
+ * @param {boolean} [data.isSecret] - Whether interaction contains sensitive data
159
+ */
160
+ const trackInteraction = (data) => {
161
+ const sessionId = sessionInstance?.get();
162
+ if (!sessionId) return;
163
+
164
+ sdk.req("/api/v7.0.0/testdriver/interaction/track", {
165
+ session: sessionId,
166
+ type: data.interactionType,
167
+ coordinates: data.coordinates,
168
+ input: data.input,
169
+ prompt: data.prompt,
170
+ selectorUsed: data.selectorUsed,
171
+ selector: data.selector,
172
+ cacheHit: data.cacheHit,
173
+ status: "completed",
174
+ success: data.success,
175
+ error: data.error,
176
+ duration: data.duration,
177
+ timestamp: data.timestamp,
178
+ isSecret: data.isSecret,
179
+ confidence: data.confidence,
180
+ reasoning: data.reasoning,
181
+ similarity: data.similarity,
182
+ screenshotUrl: data.screenshotUrl,
183
+ }).catch((err) => {
184
+ console.warn(`Failed to track ${data.interactionType} interaction:`, err.message);
185
+ });
186
+ };
187
+
140
188
  const findImageOnScreen = async (
141
189
  relativePath,
142
190
  haystack,
@@ -296,26 +344,19 @@ const createCommands = (
296
344
  emitter.emit(events.log.narration, formatter.formatAssertResult(passed, responseText, assertDuration, cacheHit), true);
297
345
 
298
346
  // Track interaction with success/failure (fire-and-forget)
299
- const sessionId = sessionInstance?.get();
300
- if (sessionId) {
301
- sandbox.send({
302
- type: "trackInteraction",
303
- interactionType: "assert",
304
- session: sessionId,
305
- prompt: assertion,
306
- timestamp: assertTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
307
- duration: assertDuration,
308
- success: passed,
309
- error: passed ? undefined : responseText,
310
- cacheHit: cacheHit,
311
- confidence: confidence,
312
- reasoning: reasoning,
313
- similarity: similarity,
314
- screenshotUrl: response?.screenshotKey ?? null,
315
- }).catch((err) => {
316
- console.warn("Failed to track assert interaction:", err.message);
317
- });
318
- }
347
+ trackInteraction({
348
+ interactionType: "assert",
349
+ prompt: assertion,
350
+ timestamp: assertTimestamp,
351
+ duration: assertDuration,
352
+ success: passed,
353
+ error: passed ? undefined : responseText,
354
+ cacheHit: cacheHit,
355
+ confidence: confidence,
356
+ reasoning: reasoning,
357
+ similarity: similarity,
358
+ screenshotUrl: response?.screenshotKey ?? null,
359
+ });
319
360
 
320
361
  if (passed) {
321
362
  return true;
@@ -430,40 +471,26 @@ const createCommands = (
430
471
  );
431
472
 
432
473
  // Track interaction success (fire-and-forget)
433
- const sessionId = sessionInstance?.get();
434
- if (sessionId) {
435
- const scrollDuration = Date.now() - scrollStartTime;
436
- sandbox.send({
437
- type: "trackInteraction",
438
- interactionType: "scroll",
439
- session: sessionId,
440
- input: { direction, amount },
441
- timestamp: scrollTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
442
- duration: scrollDuration,
443
- success: scrollSuccess,
444
- error: scrollError,
445
- }).catch((err) => {
446
- console.warn("Failed to track scroll interaction:", err.message);
447
- });
448
- }
474
+ const scrollDuration = Date.now() - scrollStartTime;
475
+ trackInteraction({
476
+ interactionType: "scroll",
477
+ input: { direction, amount },
478
+ timestamp: scrollTimestamp,
479
+ duration: scrollDuration,
480
+ success: scrollSuccess,
481
+ error: scrollError,
482
+ });
449
483
  } catch (error) {
450
484
  // Track interaction failure (fire-and-forget)
451
- const sessionId = sessionInstance?.get();
452
- if (sessionId) {
453
- const scrollDuration = Date.now() - scrollStartTime;
454
- sandbox.send({
455
- type: "trackInteraction",
456
- interactionType: "scroll",
457
- session: sessionId,
458
- input: { direction, amount },
459
- timestamp: scrollTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
460
- duration: scrollDuration,
461
- success: false,
462
- error: error.message,
463
- }).catch((err) => {
464
- console.warn("Failed to track scroll interaction:", err.message);
465
- });
466
- }
485
+ const scrollDuration = Date.now() - scrollStartTime;
486
+ trackInteraction({
487
+ interactionType: "scroll",
488
+ input: { direction, amount },
489
+ timestamp: scrollTimestamp,
490
+ duration: scrollDuration,
491
+ success: false,
492
+ error: error.message,
493
+ });
467
494
  throw error;
468
495
  }
469
496
  };
@@ -575,15 +602,12 @@ const createCommands = (
575
602
  const actionDuration = actionEndTime - clickStartTime;
576
603
 
577
604
  // Track interaction (fire-and-forget)
578
- const sessionId = sessionInstance?.get();
579
- if (sessionId && elementData.prompt) {
580
- sandbox.send({
581
- type: "trackInteraction",
605
+ if (elementData.prompt) {
606
+ trackInteraction({
582
607
  interactionType: "click",
583
- session: sessionId,
584
608
  prompt: elementData.prompt,
585
609
  input: { x, y, action },
586
- timestamp: clickTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
610
+ timestamp: clickTimestamp,
587
611
  duration: actionDuration,
588
612
  success: true,
589
613
  cacheHit: elementData.cacheHit,
@@ -593,8 +617,6 @@ const createCommands = (
593
617
  reasoning: elementData.reasoning ?? null,
594
618
  similarity: elementData.similarity ?? null,
595
619
  screenshotUrl: elementData.screenshotUrl ?? null,
596
- }).catch((err) => {
597
- console.warn("Failed to track click interaction:", err.message);
598
620
  });
599
621
  }
600
622
 
@@ -627,16 +649,13 @@ const createCommands = (
627
649
  return;
628
650
  } catch (error) {
629
651
  // Track interaction failure (fire-and-forget)
630
- const sessionId = sessionInstance?.get();
631
- if (sessionId && elementData.prompt) {
652
+ if (elementData.prompt) {
632
653
  const clickDuration = Date.now() - clickStartTime;
633
- sandbox.send({
634
- type: "trackInteraction",
654
+ trackInteraction({
635
655
  interactionType: "click",
636
- session: sessionId,
637
656
  prompt: elementData.prompt,
638
657
  input: { x, y, action },
639
- timestamp: clickTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
658
+ timestamp: clickTimestamp,
640
659
  duration: clickDuration,
641
660
  success: false,
642
661
  error: error.message,
@@ -646,8 +665,6 @@ const createCommands = (
646
665
  confidence: elementData.confidence ?? null,
647
666
  reasoning: elementData.reasoning ?? null,
648
667
  similarity: elementData.similarity ?? null,
649
- }).catch((err) => {
650
- console.warn("Failed to track click interaction:", err.message);
651
668
  });
652
669
  }
653
670
  throw error;
@@ -698,18 +715,15 @@ const createCommands = (
698
715
  await sandbox.send({ type: "moveMouse", x, y, ...elementData });
699
716
 
700
717
  // Track interaction (fire-and-forget)
701
- const sessionId = sessionInstance?.get();
702
718
  const actionEndTime = Date.now();
703
719
  const actionDuration = actionEndTime - hoverStartTime;
704
720
 
705
- if (sessionId && elementData.prompt) {
706
- sandbox.send({
707
- type: "trackInteraction",
721
+ if (elementData.prompt) {
722
+ trackInteraction({
708
723
  interactionType: "hover",
709
- session: sessionId,
710
724
  prompt: elementData.prompt,
711
725
  input: { x, y },
712
- timestamp: hoverTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
726
+ timestamp: hoverTimestamp,
713
727
  duration: actionDuration,
714
728
  success: true,
715
729
  cacheHit: elementData.cacheHit,
@@ -719,8 +733,6 @@ const createCommands = (
719
733
  reasoning: elementData.reasoning ?? null,
720
734
  similarity: elementData.similarity ?? null,
721
735
  screenshotUrl: elementData.screenshotUrl ?? null,
722
- }).catch((err) => {
723
- console.warn("Failed to track hover interaction:", err.message);
724
736
  });
725
737
  }
726
738
 
@@ -741,16 +753,13 @@ const createCommands = (
741
753
  return;
742
754
  } catch (error) {
743
755
  // Track interaction failure (fire-and-forget)
744
- const sessionId = sessionInstance?.get();
745
- if (sessionId && elementData.prompt) {
756
+ if (elementData.prompt) {
746
757
  const hoverDuration = Date.now() - hoverStartTime;
747
- sandbox.send({
748
- type: "trackInteraction",
758
+ trackInteraction({
749
759
  interactionType: "hover",
750
- session: sessionId,
751
760
  prompt: elementData.prompt,
752
761
  input: { x, y },
753
- timestamp: hoverTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
762
+ timestamp: hoverTimestamp,
754
763
  duration: hoverDuration,
755
764
  success: false,
756
765
  error: error.message,
@@ -761,8 +770,6 @@ const createCommands = (
761
770
  reasoning: elementData.reasoning ?? null,
762
771
  similarity: elementData.similarity ?? null,
763
772
  screenshotUrl: elementData.screenshotUrl ?? null,
764
- }).catch((err) => {
765
- console.warn("Failed to track hover interaction:", err.message);
766
773
  });
767
774
  }
768
775
  throw error;
@@ -963,23 +970,16 @@ const createCommands = (
963
970
  emitter.emit(events.log.narration, formatter.formatTypeResult(text, secret, typeActionEndTime - typeStartTime), true);
964
971
 
965
972
  // Track interaction (fire-and-forget)
966
- const sessionId = sessionInstance?.get();
967
- if (sessionId) {
968
- const typeDuration = Date.now() - typeStartTime;
969
- sandbox.send({
970
- type: "trackInteraction",
971
- interactionType: "type",
972
- session: sessionId,
973
- // Store masked text if secret, otherwise store actual text
974
- input: { text: secret ? "****" : text, delay },
975
- timestamp: typeTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
976
- duration: typeDuration,
977
- success: true,
978
- isSecret: secret, // Flag this interaction if it contains a secret
979
- }).catch((err) => {
980
- console.warn("Failed to track type interaction:", err.message);
981
- });
982
- }
973
+ const typeDuration = Date.now() - typeStartTime;
974
+ trackInteraction({
975
+ interactionType: "type",
976
+ // Store masked text if secret, otherwise store actual text
977
+ input: { text: secret ? "****" : text, delay },
978
+ timestamp: typeTimestamp,
979
+ duration: typeDuration,
980
+ success: true,
981
+ isSecret: secret, // Flag this interaction if it contains a secret
982
+ });
983
983
 
984
984
  const redrawStartTime = Date.now();
985
985
  await redraw.wait(5000, redrawOptions);
@@ -1036,21 +1036,14 @@ const createCommands = (
1036
1036
  );
1037
1037
 
1038
1038
  // Track interaction (fire-and-forget)
1039
- const sessionId = sessionInstance?.get();
1040
- if (sessionId) {
1041
- const pressKeysDuration = Date.now() - pressKeysStartTime;
1042
- sandbox.send({
1043
- type: "trackInteraction",
1044
- interactionType: "pressKeys",
1045
- session: sessionId,
1046
- input: { keys },
1047
- timestamp: pressKeysTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
1048
- duration: pressKeysDuration,
1049
- success: true,
1050
- }).catch((err) => {
1051
- console.warn("Failed to track pressKeys interaction:", err.message);
1052
- });
1053
- }
1039
+ const pressKeysDuration = Date.now() - pressKeysStartTime;
1040
+ trackInteraction({
1041
+ interactionType: "pressKeys",
1042
+ input: { keys },
1043
+ timestamp: pressKeysTimestamp,
1044
+ duration: pressKeysDuration,
1045
+ success: true,
1046
+ });
1054
1047
 
1055
1048
  const redrawStartTime = Date.now();
1056
1049
  await redraw.wait(5000, redrawOptions);
@@ -1079,21 +1072,14 @@ const createCommands = (
1079
1072
  const result = await delay(timeout);
1080
1073
 
1081
1074
  // Track interaction (fire-and-forget)
1082
- const sessionId = sessionInstance?.get();
1083
- if (sessionId) {
1084
- const waitDuration = Date.now() - waitStartTime;
1085
- sandbox.send({
1086
- type: "trackInteraction",
1087
- interactionType: "wait",
1088
- session: sessionId,
1089
- input: { timeout },
1090
- timestamp: waitTimestamp, // Use dashcam elapsed time instead of absolute time
1091
- duration: waitDuration,
1092
- success: true,
1093
- }).catch((err) => {
1094
- console.warn("Failed to track wait interaction:", err.message);
1095
- });
1096
- }
1075
+ const waitDuration = Date.now() - waitStartTime;
1076
+ trackInteraction({
1077
+ interactionType: "wait",
1078
+ input: { timeout },
1079
+ timestamp: waitTimestamp,
1080
+ duration: waitDuration,
1081
+ success: true,
1082
+ });
1097
1083
 
1098
1084
  return result;
1099
1085
  },
@@ -1158,44 +1144,30 @@ const createCommands = (
1158
1144
  );
1159
1145
 
1160
1146
  // Track interaction success (fire-and-forget)
1161
- const sessionId = sessionInstance?.get();
1162
- if (sessionId) {
1163
- const waitForImageDuration = Date.now() - startTime;
1164
- sandbox.send({
1165
- type: "trackInteraction",
1166
- interactionType: "waitForImage",
1167
- session: sessionId,
1168
- prompt: description,
1169
- input: { timeout },
1170
- timestamp: waitForImageTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
1171
- duration: waitForImageDuration,
1172
- success: true,
1173
- }).catch((err) => {
1174
- console.warn("Failed to track waitForImage interaction:", err.message);
1175
- });
1176
- }
1147
+ const waitForImageDuration = Date.now() - startTime;
1148
+ trackInteraction({
1149
+ interactionType: "waitForImage",
1150
+ prompt: description,
1151
+ input: { timeout },
1152
+ timestamp: waitForImageTimestamp,
1153
+ duration: waitForImageDuration,
1154
+ success: true,
1155
+ });
1177
1156
 
1178
1157
  return;
1179
1158
  } else {
1180
1159
  // Track interaction failure (fire-and-forget)
1181
- const sessionId = sessionInstance?.get();
1182
1160
  const errorMsg = `Timed out (${niceSeconds(timeout)} seconds) while searching for an image matching the description \"${description}\"`;
1183
- if (sessionId) {
1184
- const waitForImageDuration = Date.now() - startTime;
1185
- sandbox.send({
1186
- type: "trackInteraction",
1187
- interactionType: "waitForImage",
1188
- session: sessionId,
1189
- prompt: description,
1190
- input: { timeout },
1191
- timestamp: waitForImageTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
1192
- duration: waitForImageDuration,
1193
- success: false,
1194
- error: errorMsg,
1195
- }).catch((err) => {
1196
- console.warn("Failed to track waitForImage interaction:", err.message);
1197
- });
1198
- }
1161
+ const waitForImageDuration = Date.now() - startTime;
1162
+ trackInteraction({
1163
+ interactionType: "waitForImage",
1164
+ prompt: description,
1165
+ input: { timeout },
1166
+ timestamp: waitForImageTimestamp,
1167
+ duration: waitForImageDuration,
1168
+ success: false,
1169
+ error: errorMsg,
1170
+ });
1199
1171
 
1200
1172
  throw new MatchError(errorMsg);
1201
1173
  }
@@ -1267,44 +1239,30 @@ const createCommands = (
1267
1239
  emitter.emit(events.log.narration, theme.dim(`"${text}" found!`), true);
1268
1240
 
1269
1241
  // Track interaction success (fire-and-forget)
1270
- const sessionId = sessionInstance?.get();
1271
- if (sessionId) {
1272
- const waitForTextDuration = Date.now() - startTime;
1273
- sandbox.send({
1274
- type: "trackInteraction",
1275
- interactionType: "waitForText",
1276
- session: sessionId,
1277
- prompt: text,
1278
- input: { timeout },
1279
- timestamp: waitForTextTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
1280
- duration: waitForTextDuration,
1281
- success: true,
1282
- }).catch((err) => {
1283
- console.warn("Failed to track waitForText interaction:", err.message);
1284
- });
1285
- }
1242
+ const waitForTextDuration = Date.now() - startTime;
1243
+ trackInteraction({
1244
+ interactionType: "waitForText",
1245
+ prompt: text,
1246
+ input: { timeout },
1247
+ timestamp: waitForTextTimestamp,
1248
+ duration: waitForTextDuration,
1249
+ success: true,
1250
+ });
1286
1251
 
1287
1252
  return;
1288
1253
  } else {
1289
1254
  // Track interaction failure (fire-and-forget)
1290
- const sessionId = sessionInstance?.get();
1291
1255
  const errorMsg = `Timed out (${niceSeconds(timeout)} seconds) while searching for "${text}"`;
1292
- if (sessionId) {
1293
- const waitForTextDuration = Date.now() - startTime;
1294
- sandbox.send({
1295
- type: "trackInteraction",
1296
- interactionType: "waitForText",
1297
- session: sessionId,
1298
- prompt: text,
1299
- input: { timeout },
1300
- timestamp: waitForTextTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
1301
- duration: waitForTextDuration,
1302
- success: false,
1303
- error: errorMsg,
1304
- }).catch((err) => {
1305
- console.warn("Failed to track waitForText interaction:", err.message);
1306
- });
1307
- }
1256
+ const waitForTextDuration = Date.now() - startTime;
1257
+ trackInteraction({
1258
+ interactionType: "waitForText",
1259
+ prompt: text,
1260
+ input: { timeout },
1261
+ timestamp: waitForTextTimestamp,
1262
+ duration: waitForTextDuration,
1263
+ success: false,
1264
+ error: errorMsg,
1265
+ });
1308
1266
 
1309
1267
  throw new MatchError(errorMsg);
1310
1268
  }
@@ -1514,45 +1472,27 @@ const createCommands = (
1514
1472
  });
1515
1473
 
1516
1474
  // Track interaction success
1517
- const sessionId = sessionInstance?.get();
1518
- if (sessionId) {
1519
- try {
1520
- const rememberDuration = Date.now() - rememberStartTime;
1521
- await sandbox.send({
1522
- type: "trackInteraction",
1523
- interactionType: "extract",
1524
- session: sessionId,
1525
- prompt: description,
1526
- timestamp: rememberTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
1527
- duration: rememberDuration,
1528
- success: true,
1529
- });
1530
- } catch (err) {
1531
- console.warn("Failed to track extract interaction:", err.message);
1532
- }
1533
- }
1475
+ const rememberDuration = Date.now() - rememberStartTime;
1476
+ trackInteraction({
1477
+ interactionType: "extract",
1478
+ prompt: description,
1479
+ timestamp: rememberTimestamp,
1480
+ duration: rememberDuration,
1481
+ success: true,
1482
+ });
1534
1483
 
1535
1484
  return result.data;
1536
1485
  } catch (error) {
1537
1486
  // Track interaction failure
1538
- const sessionId = sessionInstance?.get();
1539
- if (sessionId) {
1540
- try {
1541
- const rememberDuration = Date.now() - rememberStartTime;
1542
- await sandbox.send({
1543
- type: "trackInteraction",
1544
- interactionType: "extract",
1545
- session: sessionId,
1546
- prompt: description,
1547
- timestamp: rememberTimestamp, // Absolute epoch timestamp - frontend calculates relative using clientStartDate
1548
- duration: rememberDuration,
1549
- success: false,
1550
- error: error.message,
1551
- });
1552
- } catch (err) {
1553
- console.warn("Failed to track extract interaction:", err.message);
1554
- }
1555
- }
1487
+ const rememberDuration = Date.now() - rememberStartTime;
1488
+ trackInteraction({
1489
+ interactionType: "extract",
1490
+ prompt: description,
1491
+ timestamp: rememberTimestamp,
1492
+ duration: rememberDuration,
1493
+ success: false,
1494
+ error: error.message,
1495
+ });
1556
1496
  throw error;
1557
1497
  }
1558
1498
  },