@loamly/tracker 1.6.0 → 1.8.0

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.
@@ -26,7 +26,7 @@ var Loamly = (() => {
26
26
  });
27
27
 
28
28
  // src/config.ts
29
- var VERSION = "1.6.0";
29
+ var VERSION = "1.8.0";
30
30
  var DEFAULT_CONFIG = {
31
31
  apiHost: "https://app.loamly.ai",
32
32
  endpoints: {
@@ -191,6 +191,467 @@ var Loamly = (() => {
191
191
  }
192
192
  }
193
193
 
194
+ // src/detection/behavioral-classifier.ts
195
+ var NAIVE_BAYES_WEIGHTS = {
196
+ human: {
197
+ time_to_first_click_delayed: 0.85,
198
+ time_to_first_click_normal: 0.75,
199
+ time_to_first_click_fast: 0.5,
200
+ time_to_first_click_immediate: 0.25,
201
+ scroll_speed_variable: 0.8,
202
+ scroll_speed_erratic: 0.7,
203
+ scroll_speed_uniform: 0.35,
204
+ scroll_speed_none: 0.45,
205
+ nav_timing_click: 0.75,
206
+ nav_timing_unknown: 0.55,
207
+ nav_timing_paste: 0.35,
208
+ has_referrer: 0.7,
209
+ no_referrer: 0.45,
210
+ homepage_landing: 0.65,
211
+ deep_landing: 0.5,
212
+ mouse_movement_curved: 0.9,
213
+ mouse_movement_linear: 0.3,
214
+ mouse_movement_none: 0.4,
215
+ form_fill_normal: 0.85,
216
+ form_fill_fast: 0.6,
217
+ form_fill_instant: 0.2,
218
+ focus_blur_normal: 0.75,
219
+ focus_blur_rapid: 0.45
220
+ },
221
+ ai_influenced: {
222
+ time_to_first_click_immediate: 0.75,
223
+ time_to_first_click_fast: 0.55,
224
+ time_to_first_click_normal: 0.4,
225
+ time_to_first_click_delayed: 0.35,
226
+ scroll_speed_none: 0.55,
227
+ scroll_speed_uniform: 0.7,
228
+ scroll_speed_variable: 0.35,
229
+ scroll_speed_erratic: 0.4,
230
+ nav_timing_paste: 0.75,
231
+ nav_timing_unknown: 0.5,
232
+ nav_timing_click: 0.35,
233
+ no_referrer: 0.65,
234
+ has_referrer: 0.4,
235
+ deep_landing: 0.6,
236
+ homepage_landing: 0.45,
237
+ mouse_movement_none: 0.6,
238
+ mouse_movement_linear: 0.75,
239
+ mouse_movement_curved: 0.25,
240
+ form_fill_instant: 0.8,
241
+ form_fill_fast: 0.55,
242
+ form_fill_normal: 0.3,
243
+ focus_blur_rapid: 0.6,
244
+ focus_blur_normal: 0.4
245
+ }
246
+ };
247
+ var PRIORS = {
248
+ human: 0.85,
249
+ ai_influenced: 0.15
250
+ };
251
+ var DEFAULT_WEIGHT = 0.5;
252
+ var BehavioralClassifier = class {
253
+ /**
254
+ * Create a new classifier
255
+ * @param minSessionTimeMs Minimum session time before classification (default: 10s)
256
+ */
257
+ constructor(minSessionTimeMs = 1e4) {
258
+ this.classified = false;
259
+ this.result = null;
260
+ this.onClassify = null;
261
+ this.minSessionTime = minSessionTimeMs;
262
+ this.data = {
263
+ firstClickTime: null,
264
+ scrollEvents: [],
265
+ mouseEvents: [],
266
+ formEvents: [],
267
+ focusBlurEvents: [],
268
+ startTime: Date.now()
269
+ };
270
+ }
271
+ /**
272
+ * Set callback for when classification completes
273
+ */
274
+ setOnClassify(callback) {
275
+ this.onClassify = callback;
276
+ }
277
+ /**
278
+ * Record a click event
279
+ */
280
+ recordClick() {
281
+ if (this.data.firstClickTime === null) {
282
+ this.data.firstClickTime = Date.now();
283
+ }
284
+ this.checkAndClassify();
285
+ }
286
+ /**
287
+ * Record a scroll event
288
+ */
289
+ recordScroll(position) {
290
+ this.data.scrollEvents.push({ time: Date.now(), position });
291
+ if (this.data.scrollEvents.length > 50) {
292
+ this.data.scrollEvents = this.data.scrollEvents.slice(-50);
293
+ }
294
+ this.checkAndClassify();
295
+ }
296
+ /**
297
+ * Record mouse movement
298
+ */
299
+ recordMouse(x, y) {
300
+ this.data.mouseEvents.push({ time: Date.now(), x, y });
301
+ if (this.data.mouseEvents.length > 100) {
302
+ this.data.mouseEvents = this.data.mouseEvents.slice(-100);
303
+ }
304
+ this.checkAndClassify();
305
+ }
306
+ /**
307
+ * Record form field interaction start
308
+ */
309
+ recordFormStart(fieldId) {
310
+ const existing = this.data.formEvents.find((e) => e.fieldId === fieldId && e.endTime === 0);
311
+ if (!existing) {
312
+ this.data.formEvents.push({ fieldId, startTime: Date.now(), endTime: 0 });
313
+ }
314
+ }
315
+ /**
316
+ * Record form field interaction end
317
+ */
318
+ recordFormEnd(fieldId) {
319
+ const event = this.data.formEvents.find((e) => e.fieldId === fieldId && e.endTime === 0);
320
+ if (event) {
321
+ event.endTime = Date.now();
322
+ }
323
+ this.checkAndClassify();
324
+ }
325
+ /**
326
+ * Record focus/blur event
327
+ */
328
+ recordFocusBlur(type) {
329
+ this.data.focusBlurEvents.push({ type, time: Date.now() });
330
+ if (this.data.focusBlurEvents.length > 20) {
331
+ this.data.focusBlurEvents = this.data.focusBlurEvents.slice(-20);
332
+ }
333
+ }
334
+ /**
335
+ * Check if we have enough data and classify
336
+ */
337
+ checkAndClassify() {
338
+ if (this.classified) return;
339
+ const sessionDuration = Date.now() - this.data.startTime;
340
+ if (sessionDuration < this.minSessionTime) return;
341
+ const hasData = this.data.scrollEvents.length >= 2 || this.data.mouseEvents.length >= 5 || this.data.firstClickTime !== null;
342
+ if (!hasData) return;
343
+ this.classify();
344
+ }
345
+ /**
346
+ * Force classification (for beforeunload)
347
+ */
348
+ forceClassify() {
349
+ if (this.classified) return this.result;
350
+ return this.classify();
351
+ }
352
+ /**
353
+ * Perform classification
354
+ */
355
+ classify() {
356
+ const sessionDuration = Date.now() - this.data.startTime;
357
+ const signals = this.extractSignals();
358
+ let humanLogProb = Math.log(PRIORS.human);
359
+ let aiLogProb = Math.log(PRIORS.ai_influenced);
360
+ for (const signal of signals) {
361
+ const humanWeight = NAIVE_BAYES_WEIGHTS.human[signal] ?? DEFAULT_WEIGHT;
362
+ const aiWeight = NAIVE_BAYES_WEIGHTS.ai_influenced[signal] ?? DEFAULT_WEIGHT;
363
+ humanLogProb += Math.log(humanWeight);
364
+ aiLogProb += Math.log(aiWeight);
365
+ }
366
+ const maxLog = Math.max(humanLogProb, aiLogProb);
367
+ const humanExp = Math.exp(humanLogProb - maxLog);
368
+ const aiExp = Math.exp(aiLogProb - maxLog);
369
+ const total = humanExp + aiExp;
370
+ const humanProbability = humanExp / total;
371
+ const aiProbability = aiExp / total;
372
+ let classification;
373
+ let confidence;
374
+ if (humanProbability > 0.6) {
375
+ classification = "human";
376
+ confidence = humanProbability;
377
+ } else if (aiProbability > 0.6) {
378
+ classification = "ai_influenced";
379
+ confidence = aiProbability;
380
+ } else {
381
+ classification = "uncertain";
382
+ confidence = Math.max(humanProbability, aiProbability);
383
+ }
384
+ this.result = {
385
+ classification,
386
+ humanProbability,
387
+ aiProbability,
388
+ confidence,
389
+ signals,
390
+ timestamp: Date.now(),
391
+ sessionDurationMs: sessionDuration
392
+ };
393
+ this.classified = true;
394
+ if (this.onClassify) {
395
+ this.onClassify(this.result);
396
+ }
397
+ return this.result;
398
+ }
399
+ /**
400
+ * Extract behavioral signals from collected data
401
+ */
402
+ extractSignals() {
403
+ const signals = [];
404
+ if (this.data.firstClickTime !== null) {
405
+ const timeToClick = this.data.firstClickTime - this.data.startTime;
406
+ if (timeToClick < 500) {
407
+ signals.push("time_to_first_click_immediate");
408
+ } else if (timeToClick < 2e3) {
409
+ signals.push("time_to_first_click_fast");
410
+ } else if (timeToClick < 1e4) {
411
+ signals.push("time_to_first_click_normal");
412
+ } else {
413
+ signals.push("time_to_first_click_delayed");
414
+ }
415
+ }
416
+ if (this.data.scrollEvents.length === 0) {
417
+ signals.push("scroll_speed_none");
418
+ } else if (this.data.scrollEvents.length >= 3) {
419
+ const scrollDeltas = [];
420
+ for (let i = 1; i < this.data.scrollEvents.length; i++) {
421
+ const delta = this.data.scrollEvents[i].time - this.data.scrollEvents[i - 1].time;
422
+ scrollDeltas.push(delta);
423
+ }
424
+ const mean = scrollDeltas.reduce((a, b) => a + b, 0) / scrollDeltas.length;
425
+ const variance = scrollDeltas.reduce((sum, d) => sum + Math.pow(d - mean, 2), 0) / scrollDeltas.length;
426
+ const stdDev = Math.sqrt(variance);
427
+ const cv = mean > 0 ? stdDev / mean : 0;
428
+ if (cv < 0.2) {
429
+ signals.push("scroll_speed_uniform");
430
+ } else if (cv < 0.6) {
431
+ signals.push("scroll_speed_variable");
432
+ } else {
433
+ signals.push("scroll_speed_erratic");
434
+ }
435
+ }
436
+ if (this.data.mouseEvents.length === 0) {
437
+ signals.push("mouse_movement_none");
438
+ } else if (this.data.mouseEvents.length >= 10) {
439
+ const n = Math.min(this.data.mouseEvents.length, 20);
440
+ const recentMouse = this.data.mouseEvents.slice(-n);
441
+ let sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0;
442
+ for (const event of recentMouse) {
443
+ sumX += event.x;
444
+ sumY += event.y;
445
+ sumXY += event.x * event.y;
446
+ sumX2 += event.x * event.x;
447
+ }
448
+ const denominator = n * sumX2 - sumX * sumX;
449
+ const slope = denominator !== 0 ? (n * sumXY - sumX * sumY) / denominator : 0;
450
+ const intercept = (sumY - slope * sumX) / n;
451
+ let ssRes = 0, ssTot = 0;
452
+ const yMean = sumY / n;
453
+ for (const event of recentMouse) {
454
+ const yPred = slope * event.x + intercept;
455
+ ssRes += Math.pow(event.y - yPred, 2);
456
+ ssTot += Math.pow(event.y - yMean, 2);
457
+ }
458
+ const r2 = ssTot !== 0 ? 1 - ssRes / ssTot : 0;
459
+ if (r2 > 0.95) {
460
+ signals.push("mouse_movement_linear");
461
+ } else {
462
+ signals.push("mouse_movement_curved");
463
+ }
464
+ }
465
+ const completedForms = this.data.formEvents.filter((e) => e.endTime > 0);
466
+ if (completedForms.length > 0) {
467
+ const avgFillTime = completedForms.reduce((sum, e) => sum + (e.endTime - e.startTime), 0) / completedForms.length;
468
+ if (avgFillTime < 100) {
469
+ signals.push("form_fill_instant");
470
+ } else if (avgFillTime < 500) {
471
+ signals.push("form_fill_fast");
472
+ } else {
473
+ signals.push("form_fill_normal");
474
+ }
475
+ }
476
+ if (this.data.focusBlurEvents.length >= 4) {
477
+ const recentFB = this.data.focusBlurEvents.slice(-10);
478
+ const intervals = [];
479
+ for (let i = 1; i < recentFB.length; i++) {
480
+ intervals.push(recentFB[i].time - recentFB[i - 1].time);
481
+ }
482
+ const avgInterval = intervals.reduce((a, b) => a + b, 0) / intervals.length;
483
+ if (avgInterval < 1e3) {
484
+ signals.push("focus_blur_rapid");
485
+ } else {
486
+ signals.push("focus_blur_normal");
487
+ }
488
+ }
489
+ return signals;
490
+ }
491
+ /**
492
+ * Add context signals (set by tracker from external data)
493
+ */
494
+ addContextSignal(_signal) {
495
+ }
496
+ /**
497
+ * Get current result (null if not yet classified)
498
+ */
499
+ getResult() {
500
+ return this.result;
501
+ }
502
+ /**
503
+ * Check if classification has been performed
504
+ */
505
+ hasClassified() {
506
+ return this.classified;
507
+ }
508
+ };
509
+
510
+ // src/detection/focus-blur.ts
511
+ var FocusBlurAnalyzer = class {
512
+ constructor() {
513
+ this.sequence = [];
514
+ this.firstInteractionTime = null;
515
+ this.analyzed = false;
516
+ this.result = null;
517
+ this.pageLoadTime = performance.now();
518
+ }
519
+ /**
520
+ * Initialize event tracking
521
+ * Must be called after DOM is ready
522
+ */
523
+ initTracking() {
524
+ document.addEventListener("focus", (e) => {
525
+ this.recordEvent("focus", e.target);
526
+ }, true);
527
+ document.addEventListener("blur", (e) => {
528
+ this.recordEvent("blur", e.target);
529
+ }, true);
530
+ window.addEventListener("focus", () => {
531
+ this.recordEvent("window_focus", null);
532
+ });
533
+ window.addEventListener("blur", () => {
534
+ this.recordEvent("window_blur", null);
535
+ });
536
+ const recordFirstInteraction = () => {
537
+ if (this.firstInteractionTime === null) {
538
+ this.firstInteractionTime = performance.now();
539
+ }
540
+ };
541
+ document.addEventListener("click", recordFirstInteraction, { once: true, passive: true });
542
+ document.addEventListener("keydown", recordFirstInteraction, { once: true, passive: true });
543
+ }
544
+ /**
545
+ * Record a focus/blur event
546
+ */
547
+ recordEvent(type, target) {
548
+ const event = {
549
+ type,
550
+ target: target?.tagName || "WINDOW",
551
+ timestamp: performance.now()
552
+ };
553
+ this.sequence.push(event);
554
+ if (this.sequence.length > 20) {
555
+ this.sequence = this.sequence.slice(-20);
556
+ }
557
+ }
558
+ /**
559
+ * Analyze the focus/blur sequence for paste patterns
560
+ */
561
+ analyze() {
562
+ if (this.analyzed && this.result) {
563
+ return this.result;
564
+ }
565
+ const signals = [];
566
+ let confidence = 0;
567
+ const earlyEvents = this.sequence.filter((e) => e.timestamp < this.pageLoadTime + 500);
568
+ const hasEarlyWindowFocus = earlyEvents.some((e) => e.type === "window_focus");
569
+ if (hasEarlyWindowFocus) {
570
+ signals.push("early_window_focus");
571
+ confidence += 0.15;
572
+ }
573
+ const hasEarlyBodyFocus = earlyEvents.some(
574
+ (e) => e.type === "focus" && e.target === "BODY"
575
+ );
576
+ if (hasEarlyBodyFocus) {
577
+ signals.push("early_body_focus");
578
+ confidence += 0.15;
579
+ }
580
+ const hasLinkFocus = this.sequence.some(
581
+ (e) => e.type === "focus" && e.target === "A"
582
+ );
583
+ if (!hasLinkFocus) {
584
+ signals.push("no_link_focus");
585
+ confidence += 0.1;
586
+ }
587
+ const firstFocus = this.sequence.find((e) => e.type === "focus");
588
+ if (firstFocus && (firstFocus.target === "BODY" || firstFocus.target === "HTML")) {
589
+ signals.push("first_focus_body");
590
+ confidence += 0.1;
591
+ }
592
+ const windowEvents = this.sequence.filter(
593
+ (e) => e.type === "window_focus" || e.type === "window_blur"
594
+ );
595
+ if (windowEvents.length <= 2) {
596
+ signals.push("minimal_window_switches");
597
+ confidence += 0.05;
598
+ }
599
+ if (this.firstInteractionTime !== null) {
600
+ const timeToInteraction = this.firstInteractionTime - this.pageLoadTime;
601
+ if (timeToInteraction > 3e3) {
602
+ signals.push("delayed_first_interaction");
603
+ confidence += 0.1;
604
+ }
605
+ }
606
+ confidence = Math.min(confidence, 0.65);
607
+ let navType;
608
+ if (confidence >= 0.35) {
609
+ navType = "likely_paste";
610
+ } else if (signals.length === 0) {
611
+ navType = "unknown";
612
+ } else {
613
+ navType = "likely_click";
614
+ }
615
+ this.result = {
616
+ nav_type: navType,
617
+ confidence,
618
+ signals,
619
+ sequence: this.sequence.slice(-10),
620
+ time_to_first_interaction_ms: this.firstInteractionTime ? Math.round(this.firstInteractionTime - this.pageLoadTime) : null
621
+ };
622
+ this.analyzed = true;
623
+ return this.result;
624
+ }
625
+ /**
626
+ * Get current result (analyze if not done)
627
+ */
628
+ getResult() {
629
+ return this.analyze();
630
+ }
631
+ /**
632
+ * Check if analysis has been performed
633
+ */
634
+ hasAnalyzed() {
635
+ return this.analyzed;
636
+ }
637
+ /**
638
+ * Get the raw sequence for debugging
639
+ */
640
+ getSequence() {
641
+ return [...this.sequence];
642
+ }
643
+ /**
644
+ * Reset the analyzer
645
+ */
646
+ reset() {
647
+ this.sequence = [];
648
+ this.pageLoadTime = performance.now();
649
+ this.firstInteractionTime = null;
650
+ this.analyzed = false;
651
+ this.result = null;
652
+ }
653
+ };
654
+
194
655
  // src/utils.ts
195
656
  function generateUUID() {
196
657
  if (typeof crypto !== "undefined" && crypto.randomUUID) {
@@ -276,6 +737,10 @@ var Loamly = (() => {
276
737
  var sessionStartTime = null;
277
738
  var navigationTiming = null;
278
739
  var aiDetection = null;
740
+ var behavioralClassifier = null;
741
+ var behavioralMLResult = null;
742
+ var focusBlurAnalyzer = null;
743
+ var focusBlurResult = null;
279
744
  function log(...args) {
280
745
  if (debugMode) {
281
746
  console.log("[Loamly]", ...args);
@@ -315,6 +780,16 @@ var Loamly = (() => {
315
780
  if (!userConfig.disableBehavioral) {
316
781
  setupBehavioralTracking();
317
782
  }
783
+ behavioralClassifier = new BehavioralClassifier(1e4);
784
+ behavioralClassifier.setOnClassify(handleBehavioralClassification);
785
+ setupBehavioralMLTracking();
786
+ focusBlurAnalyzer = new FocusBlurAnalyzer();
787
+ focusBlurAnalyzer.initTracking();
788
+ setTimeout(() => {
789
+ if (focusBlurAnalyzer) {
790
+ handleFocusBlurAnalysis(focusBlurAnalyzer.analyze());
791
+ }
792
+ }, 5e3);
318
793
  log("Initialization complete");
319
794
  }
320
795
  function pageview(customUrl) {
@@ -496,6 +971,116 @@ var Loamly = (() => {
496
971
  body: JSON.stringify(payload)
497
972
  });
498
973
  }
974
+ function setupBehavioralMLTracking() {
975
+ if (!behavioralClassifier) return;
976
+ let mouseSampleCount = 0;
977
+ document.addEventListener("mousemove", (e) => {
978
+ mouseSampleCount++;
979
+ if (mouseSampleCount % 10 === 0 && behavioralClassifier) {
980
+ behavioralClassifier.recordMouse(e.clientX, e.clientY);
981
+ }
982
+ }, { passive: true });
983
+ document.addEventListener("click", () => {
984
+ if (behavioralClassifier) {
985
+ behavioralClassifier.recordClick();
986
+ }
987
+ }, { passive: true });
988
+ let lastScrollY = 0;
989
+ document.addEventListener("scroll", () => {
990
+ const currentY = window.scrollY;
991
+ if (Math.abs(currentY - lastScrollY) > 50 && behavioralClassifier) {
992
+ lastScrollY = currentY;
993
+ behavioralClassifier.recordScroll(currentY);
994
+ }
995
+ }, { passive: true });
996
+ document.addEventListener("focusin", (e) => {
997
+ if (behavioralClassifier) {
998
+ behavioralClassifier.recordFocusBlur("focus");
999
+ const target = e.target;
1000
+ if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
1001
+ behavioralClassifier.recordFormStart(target.id || target.getAttribute("name") || "unknown");
1002
+ }
1003
+ }
1004
+ }, { passive: true });
1005
+ document.addEventListener("focusout", (e) => {
1006
+ if (behavioralClassifier) {
1007
+ behavioralClassifier.recordFocusBlur("blur");
1008
+ const target = e.target;
1009
+ if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
1010
+ behavioralClassifier.recordFormEnd(target.id || target.getAttribute("name") || "unknown");
1011
+ }
1012
+ }
1013
+ }, { passive: true });
1014
+ window.addEventListener("beforeunload", () => {
1015
+ if (behavioralClassifier && !behavioralClassifier.hasClassified()) {
1016
+ const result = behavioralClassifier.forceClassify();
1017
+ if (result) {
1018
+ handleBehavioralClassification(result);
1019
+ }
1020
+ }
1021
+ });
1022
+ setTimeout(() => {
1023
+ if (behavioralClassifier && !behavioralClassifier.hasClassified()) {
1024
+ behavioralClassifier.forceClassify();
1025
+ }
1026
+ }, 3e4);
1027
+ }
1028
+ function handleBehavioralClassification(result) {
1029
+ log("Behavioral ML classification:", result);
1030
+ behavioralMLResult = {
1031
+ classification: result.classification,
1032
+ humanProbability: result.humanProbability,
1033
+ aiProbability: result.aiProbability,
1034
+ confidence: result.confidence,
1035
+ signals: result.signals,
1036
+ sessionDurationMs: result.sessionDurationMs
1037
+ };
1038
+ sendBehavioralEvent("ml_classification", {
1039
+ classification: result.classification,
1040
+ human_probability: result.humanProbability,
1041
+ ai_probability: result.aiProbability,
1042
+ confidence: result.confidence,
1043
+ signals: result.signals,
1044
+ session_duration_ms: result.sessionDurationMs,
1045
+ navigation_timing: navigationTiming,
1046
+ ai_detection: aiDetection,
1047
+ focus_blur: focusBlurResult
1048
+ });
1049
+ if (result.classification === "ai_influenced" && result.confidence >= 0.7) {
1050
+ aiDetection = {
1051
+ isAI: true,
1052
+ confidence: result.confidence,
1053
+ method: "behavioral"
1054
+ };
1055
+ log("AI detection updated from behavioral ML:", aiDetection);
1056
+ }
1057
+ }
1058
+ function handleFocusBlurAnalysis(result) {
1059
+ log("Focus/blur analysis:", result);
1060
+ focusBlurResult = {
1061
+ navType: result.nav_type,
1062
+ confidence: result.confidence,
1063
+ signals: result.signals,
1064
+ timeToFirstInteractionMs: result.time_to_first_interaction_ms
1065
+ };
1066
+ sendBehavioralEvent("focus_blur_analysis", {
1067
+ nav_type: result.nav_type,
1068
+ confidence: result.confidence,
1069
+ signals: result.signals,
1070
+ time_to_first_interaction_ms: result.time_to_first_interaction_ms,
1071
+ sequence_length: result.sequence.length
1072
+ });
1073
+ if (result.nav_type === "likely_paste" && result.confidence >= 0.4) {
1074
+ if (!aiDetection || aiDetection.confidence < result.confidence) {
1075
+ aiDetection = {
1076
+ isAI: true,
1077
+ confidence: result.confidence,
1078
+ method: "behavioral"
1079
+ };
1080
+ log("AI detection updated from focus/blur analysis:", aiDetection);
1081
+ }
1082
+ }
1083
+ }
499
1084
  function getCurrentSessionId() {
500
1085
  return sessionId;
501
1086
  }
@@ -508,6 +1093,12 @@ var Loamly = (() => {
508
1093
  function getNavigationTimingResult() {
509
1094
  return navigationTiming;
510
1095
  }
1096
+ function getBehavioralMLResult() {
1097
+ return behavioralMLResult;
1098
+ }
1099
+ function getFocusBlurResult() {
1100
+ return focusBlurResult;
1101
+ }
511
1102
  function isTrackerInitialized() {
512
1103
  return initialized;
513
1104
  }
@@ -519,6 +1110,10 @@ var Loamly = (() => {
519
1110
  sessionStartTime = null;
520
1111
  navigationTiming = null;
521
1112
  aiDetection = null;
1113
+ behavioralClassifier = null;
1114
+ behavioralMLResult = null;
1115
+ focusBlurAnalyzer = null;
1116
+ focusBlurResult = null;
522
1117
  try {
523
1118
  sessionStorage.removeItem("loamly_session");
524
1119
  sessionStorage.removeItem("loamly_start");
@@ -539,6 +1134,8 @@ var Loamly = (() => {
539
1134
  getVisitorId: getCurrentVisitorId,
540
1135
  getAIDetection: getAIDetectionResult,
541
1136
  getNavigationTiming: getNavigationTimingResult,
1137
+ getBehavioralML: getBehavioralMLResult,
1138
+ getFocusBlur: getFocusBlurResult,
542
1139
  isInitialized: isTrackerInitialized,
543
1140
  reset,
544
1141
  debug: setDebug
@@ -591,4 +1188,11 @@ var Loamly = (() => {
591
1188
  var browser_default = loamly;
592
1189
  return __toCommonJS(browser_exports);
593
1190
  })();
1191
+ /**
1192
+ * Loamly Tracker Configuration
1193
+ *
1194
+ * @module @loamly/tracker
1195
+ * @license MIT
1196
+ * @see https://github.com/loamly/loamly
1197
+ */
594
1198
  //# sourceMappingURL=loamly.iife.global.js.map