@loamly/tracker 1.7.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.
- package/dist/index.cjs +418 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +82 -2
- package/dist/index.d.ts +82 -2
- package/dist/index.mjs +416 -2
- package/dist/index.mjs.map +1 -1
- package/dist/loamly.iife.global.js +196 -2
- package/dist/loamly.iife.global.js.map +1 -1
- package/dist/loamly.iife.min.global.js +8 -1
- package/dist/loamly.iife.min.global.js.map +1 -1
- package/package.json +1 -1
- package/src/config.ts +4 -1
- package/src/core.ts +67 -1
- package/src/detection/agentic-browser.ts +328 -0
- package/src/detection/focus-blur.ts +251 -0
- package/src/detection/index.ts +15 -0
- package/src/index.ts +7 -0
- package/src/types.ts +17 -0
package/dist/index.cjs
CHANGED
|
@@ -22,7 +22,9 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
AI_BOT_PATTERNS: () => AI_BOT_PATTERNS,
|
|
24
24
|
AI_PLATFORMS: () => AI_PLATFORMS,
|
|
25
|
+
AgenticBrowserAnalyzer: () => AgenticBrowserAnalyzer,
|
|
25
26
|
VERSION: () => VERSION,
|
|
27
|
+
createAgenticAnalyzer: () => createAgenticAnalyzer,
|
|
26
28
|
default: () => loamly,
|
|
27
29
|
detectAIFromReferrer: () => detectAIFromReferrer,
|
|
28
30
|
detectAIFromUTM: () => detectAIFromUTM,
|
|
@@ -32,7 +34,7 @@ __export(index_exports, {
|
|
|
32
34
|
module.exports = __toCommonJS(index_exports);
|
|
33
35
|
|
|
34
36
|
// src/config.ts
|
|
35
|
-
var VERSION = "1.
|
|
37
|
+
var VERSION = "1.8.0";
|
|
36
38
|
var DEFAULT_CONFIG = {
|
|
37
39
|
apiHost: "https://app.loamly.ai",
|
|
38
40
|
endpoints: {
|
|
@@ -525,6 +527,151 @@ var BehavioralClassifier = class {
|
|
|
525
527
|
}
|
|
526
528
|
};
|
|
527
529
|
|
|
530
|
+
// src/detection/focus-blur.ts
|
|
531
|
+
var FocusBlurAnalyzer = class {
|
|
532
|
+
constructor() {
|
|
533
|
+
this.sequence = [];
|
|
534
|
+
this.firstInteractionTime = null;
|
|
535
|
+
this.analyzed = false;
|
|
536
|
+
this.result = null;
|
|
537
|
+
this.pageLoadTime = performance.now();
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Initialize event tracking
|
|
541
|
+
* Must be called after DOM is ready
|
|
542
|
+
*/
|
|
543
|
+
initTracking() {
|
|
544
|
+
document.addEventListener("focus", (e) => {
|
|
545
|
+
this.recordEvent("focus", e.target);
|
|
546
|
+
}, true);
|
|
547
|
+
document.addEventListener("blur", (e) => {
|
|
548
|
+
this.recordEvent("blur", e.target);
|
|
549
|
+
}, true);
|
|
550
|
+
window.addEventListener("focus", () => {
|
|
551
|
+
this.recordEvent("window_focus", null);
|
|
552
|
+
});
|
|
553
|
+
window.addEventListener("blur", () => {
|
|
554
|
+
this.recordEvent("window_blur", null);
|
|
555
|
+
});
|
|
556
|
+
const recordFirstInteraction = () => {
|
|
557
|
+
if (this.firstInteractionTime === null) {
|
|
558
|
+
this.firstInteractionTime = performance.now();
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
document.addEventListener("click", recordFirstInteraction, { once: true, passive: true });
|
|
562
|
+
document.addEventListener("keydown", recordFirstInteraction, { once: true, passive: true });
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Record a focus/blur event
|
|
566
|
+
*/
|
|
567
|
+
recordEvent(type, target) {
|
|
568
|
+
const event = {
|
|
569
|
+
type,
|
|
570
|
+
target: target?.tagName || "WINDOW",
|
|
571
|
+
timestamp: performance.now()
|
|
572
|
+
};
|
|
573
|
+
this.sequence.push(event);
|
|
574
|
+
if (this.sequence.length > 20) {
|
|
575
|
+
this.sequence = this.sequence.slice(-20);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Analyze the focus/blur sequence for paste patterns
|
|
580
|
+
*/
|
|
581
|
+
analyze() {
|
|
582
|
+
if (this.analyzed && this.result) {
|
|
583
|
+
return this.result;
|
|
584
|
+
}
|
|
585
|
+
const signals = [];
|
|
586
|
+
let confidence = 0;
|
|
587
|
+
const earlyEvents = this.sequence.filter((e) => e.timestamp < this.pageLoadTime + 500);
|
|
588
|
+
const hasEarlyWindowFocus = earlyEvents.some((e) => e.type === "window_focus");
|
|
589
|
+
if (hasEarlyWindowFocus) {
|
|
590
|
+
signals.push("early_window_focus");
|
|
591
|
+
confidence += 0.15;
|
|
592
|
+
}
|
|
593
|
+
const hasEarlyBodyFocus = earlyEvents.some(
|
|
594
|
+
(e) => e.type === "focus" && e.target === "BODY"
|
|
595
|
+
);
|
|
596
|
+
if (hasEarlyBodyFocus) {
|
|
597
|
+
signals.push("early_body_focus");
|
|
598
|
+
confidence += 0.15;
|
|
599
|
+
}
|
|
600
|
+
const hasLinkFocus = this.sequence.some(
|
|
601
|
+
(e) => e.type === "focus" && e.target === "A"
|
|
602
|
+
);
|
|
603
|
+
if (!hasLinkFocus) {
|
|
604
|
+
signals.push("no_link_focus");
|
|
605
|
+
confidence += 0.1;
|
|
606
|
+
}
|
|
607
|
+
const firstFocus = this.sequence.find((e) => e.type === "focus");
|
|
608
|
+
if (firstFocus && (firstFocus.target === "BODY" || firstFocus.target === "HTML")) {
|
|
609
|
+
signals.push("first_focus_body");
|
|
610
|
+
confidence += 0.1;
|
|
611
|
+
}
|
|
612
|
+
const windowEvents = this.sequence.filter(
|
|
613
|
+
(e) => e.type === "window_focus" || e.type === "window_blur"
|
|
614
|
+
);
|
|
615
|
+
if (windowEvents.length <= 2) {
|
|
616
|
+
signals.push("minimal_window_switches");
|
|
617
|
+
confidence += 0.05;
|
|
618
|
+
}
|
|
619
|
+
if (this.firstInteractionTime !== null) {
|
|
620
|
+
const timeToInteraction = this.firstInteractionTime - this.pageLoadTime;
|
|
621
|
+
if (timeToInteraction > 3e3) {
|
|
622
|
+
signals.push("delayed_first_interaction");
|
|
623
|
+
confidence += 0.1;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
confidence = Math.min(confidence, 0.65);
|
|
627
|
+
let navType;
|
|
628
|
+
if (confidence >= 0.35) {
|
|
629
|
+
navType = "likely_paste";
|
|
630
|
+
} else if (signals.length === 0) {
|
|
631
|
+
navType = "unknown";
|
|
632
|
+
} else {
|
|
633
|
+
navType = "likely_click";
|
|
634
|
+
}
|
|
635
|
+
this.result = {
|
|
636
|
+
nav_type: navType,
|
|
637
|
+
confidence,
|
|
638
|
+
signals,
|
|
639
|
+
sequence: this.sequence.slice(-10),
|
|
640
|
+
time_to_first_interaction_ms: this.firstInteractionTime ? Math.round(this.firstInteractionTime - this.pageLoadTime) : null
|
|
641
|
+
};
|
|
642
|
+
this.analyzed = true;
|
|
643
|
+
return this.result;
|
|
644
|
+
}
|
|
645
|
+
/**
|
|
646
|
+
* Get current result (analyze if not done)
|
|
647
|
+
*/
|
|
648
|
+
getResult() {
|
|
649
|
+
return this.analyze();
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Check if analysis has been performed
|
|
653
|
+
*/
|
|
654
|
+
hasAnalyzed() {
|
|
655
|
+
return this.analyzed;
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Get the raw sequence for debugging
|
|
659
|
+
*/
|
|
660
|
+
getSequence() {
|
|
661
|
+
return [...this.sequence];
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Reset the analyzer
|
|
665
|
+
*/
|
|
666
|
+
reset() {
|
|
667
|
+
this.sequence = [];
|
|
668
|
+
this.pageLoadTime = performance.now();
|
|
669
|
+
this.firstInteractionTime = null;
|
|
670
|
+
this.analyzed = false;
|
|
671
|
+
this.result = null;
|
|
672
|
+
}
|
|
673
|
+
};
|
|
674
|
+
|
|
528
675
|
// src/utils.ts
|
|
529
676
|
function generateUUID() {
|
|
530
677
|
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
@@ -612,6 +759,8 @@ var navigationTiming = null;
|
|
|
612
759
|
var aiDetection = null;
|
|
613
760
|
var behavioralClassifier = null;
|
|
614
761
|
var behavioralMLResult = null;
|
|
762
|
+
var focusBlurAnalyzer = null;
|
|
763
|
+
var focusBlurResult = null;
|
|
615
764
|
function log(...args) {
|
|
616
765
|
if (debugMode) {
|
|
617
766
|
console.log("[Loamly]", ...args);
|
|
@@ -654,6 +803,13 @@ function init(userConfig = {}) {
|
|
|
654
803
|
behavioralClassifier = new BehavioralClassifier(1e4);
|
|
655
804
|
behavioralClassifier.setOnClassify(handleBehavioralClassification);
|
|
656
805
|
setupBehavioralMLTracking();
|
|
806
|
+
focusBlurAnalyzer = new FocusBlurAnalyzer();
|
|
807
|
+
focusBlurAnalyzer.initTracking();
|
|
808
|
+
setTimeout(() => {
|
|
809
|
+
if (focusBlurAnalyzer) {
|
|
810
|
+
handleFocusBlurAnalysis(focusBlurAnalyzer.analyze());
|
|
811
|
+
}
|
|
812
|
+
}, 5e3);
|
|
657
813
|
log("Initialization complete");
|
|
658
814
|
}
|
|
659
815
|
function pageview(customUrl) {
|
|
@@ -907,7 +1063,8 @@ function handleBehavioralClassification(result) {
|
|
|
907
1063
|
signals: result.signals,
|
|
908
1064
|
session_duration_ms: result.sessionDurationMs,
|
|
909
1065
|
navigation_timing: navigationTiming,
|
|
910
|
-
ai_detection: aiDetection
|
|
1066
|
+
ai_detection: aiDetection,
|
|
1067
|
+
focus_blur: focusBlurResult
|
|
911
1068
|
});
|
|
912
1069
|
if (result.classification === "ai_influenced" && result.confidence >= 0.7) {
|
|
913
1070
|
aiDetection = {
|
|
@@ -918,6 +1075,32 @@ function handleBehavioralClassification(result) {
|
|
|
918
1075
|
log("AI detection updated from behavioral ML:", aiDetection);
|
|
919
1076
|
}
|
|
920
1077
|
}
|
|
1078
|
+
function handleFocusBlurAnalysis(result) {
|
|
1079
|
+
log("Focus/blur analysis:", result);
|
|
1080
|
+
focusBlurResult = {
|
|
1081
|
+
navType: result.nav_type,
|
|
1082
|
+
confidence: result.confidence,
|
|
1083
|
+
signals: result.signals,
|
|
1084
|
+
timeToFirstInteractionMs: result.time_to_first_interaction_ms
|
|
1085
|
+
};
|
|
1086
|
+
sendBehavioralEvent("focus_blur_analysis", {
|
|
1087
|
+
nav_type: result.nav_type,
|
|
1088
|
+
confidence: result.confidence,
|
|
1089
|
+
signals: result.signals,
|
|
1090
|
+
time_to_first_interaction_ms: result.time_to_first_interaction_ms,
|
|
1091
|
+
sequence_length: result.sequence.length
|
|
1092
|
+
});
|
|
1093
|
+
if (result.nav_type === "likely_paste" && result.confidence >= 0.4) {
|
|
1094
|
+
if (!aiDetection || aiDetection.confidence < result.confidence) {
|
|
1095
|
+
aiDetection = {
|
|
1096
|
+
isAI: true,
|
|
1097
|
+
confidence: result.confidence,
|
|
1098
|
+
method: "behavioral"
|
|
1099
|
+
};
|
|
1100
|
+
log("AI detection updated from focus/blur analysis:", aiDetection);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
921
1104
|
function getCurrentSessionId() {
|
|
922
1105
|
return sessionId;
|
|
923
1106
|
}
|
|
@@ -933,6 +1116,9 @@ function getNavigationTimingResult() {
|
|
|
933
1116
|
function getBehavioralMLResult() {
|
|
934
1117
|
return behavioralMLResult;
|
|
935
1118
|
}
|
|
1119
|
+
function getFocusBlurResult() {
|
|
1120
|
+
return focusBlurResult;
|
|
1121
|
+
}
|
|
936
1122
|
function isTrackerInitialized() {
|
|
937
1123
|
return initialized;
|
|
938
1124
|
}
|
|
@@ -946,6 +1132,8 @@ function reset() {
|
|
|
946
1132
|
aiDetection = null;
|
|
947
1133
|
behavioralClassifier = null;
|
|
948
1134
|
behavioralMLResult = null;
|
|
1135
|
+
focusBlurAnalyzer = null;
|
|
1136
|
+
focusBlurResult = null;
|
|
949
1137
|
try {
|
|
950
1138
|
sessionStorage.removeItem("loamly_session");
|
|
951
1139
|
sessionStorage.removeItem("loamly_start");
|
|
@@ -967,20 +1155,246 @@ var loamly = {
|
|
|
967
1155
|
getAIDetection: getAIDetectionResult,
|
|
968
1156
|
getNavigationTiming: getNavigationTimingResult,
|
|
969
1157
|
getBehavioralML: getBehavioralMLResult,
|
|
1158
|
+
getFocusBlur: getFocusBlurResult,
|
|
970
1159
|
isInitialized: isTrackerInitialized,
|
|
971
1160
|
reset,
|
|
972
1161
|
debug: setDebug
|
|
973
1162
|
};
|
|
1163
|
+
|
|
1164
|
+
// src/detection/agentic-browser.ts
|
|
1165
|
+
var CometDetector = class {
|
|
1166
|
+
constructor() {
|
|
1167
|
+
this.detected = false;
|
|
1168
|
+
this.checkComplete = false;
|
|
1169
|
+
this.observer = null;
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Initialize detection
|
|
1173
|
+
* @param timeout - Max time to observe for Comet DOM (default: 5s)
|
|
1174
|
+
*/
|
|
1175
|
+
init(timeout = 5e3) {
|
|
1176
|
+
if (typeof document === "undefined") return;
|
|
1177
|
+
this.check();
|
|
1178
|
+
if (!this.detected && document.body) {
|
|
1179
|
+
this.observer = new MutationObserver(() => this.check());
|
|
1180
|
+
this.observer.observe(document.body, { childList: true, subtree: true });
|
|
1181
|
+
setTimeout(() => {
|
|
1182
|
+
if (this.observer && !this.detected) {
|
|
1183
|
+
this.observer.disconnect();
|
|
1184
|
+
this.observer = null;
|
|
1185
|
+
this.checkComplete = true;
|
|
1186
|
+
}
|
|
1187
|
+
}, timeout);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
check() {
|
|
1191
|
+
if (document.querySelector(".pplx-agent-overlay-stop-button")) {
|
|
1192
|
+
this.detected = true;
|
|
1193
|
+
this.checkComplete = true;
|
|
1194
|
+
if (this.observer) {
|
|
1195
|
+
this.observer.disconnect();
|
|
1196
|
+
this.observer = null;
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
isDetected() {
|
|
1201
|
+
return this.detected;
|
|
1202
|
+
}
|
|
1203
|
+
isCheckComplete() {
|
|
1204
|
+
return this.checkComplete;
|
|
1205
|
+
}
|
|
1206
|
+
destroy() {
|
|
1207
|
+
if (this.observer) {
|
|
1208
|
+
this.observer.disconnect();
|
|
1209
|
+
this.observer = null;
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
};
|
|
1213
|
+
var MouseAnalyzer = class {
|
|
1214
|
+
/**
|
|
1215
|
+
* @param teleportThreshold - Distance in pixels to consider a teleport (default: 500)
|
|
1216
|
+
*/
|
|
1217
|
+
constructor(teleportThreshold = 500) {
|
|
1218
|
+
this.lastX = -1;
|
|
1219
|
+
this.lastY = -1;
|
|
1220
|
+
this.teleportingClicks = 0;
|
|
1221
|
+
this.totalMovements = 0;
|
|
1222
|
+
this.handleMove = (e) => {
|
|
1223
|
+
this.totalMovements++;
|
|
1224
|
+
this.lastX = e.clientX;
|
|
1225
|
+
this.lastY = e.clientY;
|
|
1226
|
+
};
|
|
1227
|
+
this.handleClick = (e) => {
|
|
1228
|
+
if (this.lastX !== -1 && this.lastY !== -1) {
|
|
1229
|
+
const dx = Math.abs(e.clientX - this.lastX);
|
|
1230
|
+
const dy = Math.abs(e.clientY - this.lastY);
|
|
1231
|
+
if (dx > this.teleportThreshold || dy > this.teleportThreshold) {
|
|
1232
|
+
this.teleportingClicks++;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
this.lastX = e.clientX;
|
|
1236
|
+
this.lastY = e.clientY;
|
|
1237
|
+
};
|
|
1238
|
+
this.teleportThreshold = teleportThreshold;
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Initialize mouse tracking
|
|
1242
|
+
*/
|
|
1243
|
+
init() {
|
|
1244
|
+
if (typeof document === "undefined") return;
|
|
1245
|
+
document.addEventListener("mousemove", this.handleMove, { passive: true });
|
|
1246
|
+
document.addEventListener("mousedown", this.handleClick, { passive: true });
|
|
1247
|
+
}
|
|
1248
|
+
getPatterns() {
|
|
1249
|
+
return {
|
|
1250
|
+
teleportingClicks: this.teleportingClicks,
|
|
1251
|
+
totalMovements: this.totalMovements
|
|
1252
|
+
};
|
|
1253
|
+
}
|
|
1254
|
+
destroy() {
|
|
1255
|
+
if (typeof document === "undefined") return;
|
|
1256
|
+
document.removeEventListener("mousemove", this.handleMove);
|
|
1257
|
+
document.removeEventListener("mousedown", this.handleClick);
|
|
1258
|
+
}
|
|
1259
|
+
};
|
|
1260
|
+
var CDPDetector = class {
|
|
1261
|
+
constructor() {
|
|
1262
|
+
this.detected = false;
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
* Run detection checks
|
|
1266
|
+
*/
|
|
1267
|
+
detect() {
|
|
1268
|
+
if (typeof navigator === "undefined") return false;
|
|
1269
|
+
if (navigator.webdriver) {
|
|
1270
|
+
this.detected = true;
|
|
1271
|
+
return true;
|
|
1272
|
+
}
|
|
1273
|
+
if (typeof window !== "undefined") {
|
|
1274
|
+
const win = window;
|
|
1275
|
+
const automationProps = [
|
|
1276
|
+
"__webdriver_evaluate",
|
|
1277
|
+
"__selenium_evaluate",
|
|
1278
|
+
"__webdriver_script_function",
|
|
1279
|
+
"__webdriver_script_func",
|
|
1280
|
+
"__webdriver_script_fn",
|
|
1281
|
+
"__fxdriver_evaluate",
|
|
1282
|
+
"__driver_unwrapped",
|
|
1283
|
+
"__webdriver_unwrapped",
|
|
1284
|
+
"__driver_evaluate",
|
|
1285
|
+
"__selenium_unwrapped",
|
|
1286
|
+
"__fxdriver_unwrapped"
|
|
1287
|
+
];
|
|
1288
|
+
for (const prop of automationProps) {
|
|
1289
|
+
if (prop in win) {
|
|
1290
|
+
this.detected = true;
|
|
1291
|
+
return true;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
return false;
|
|
1296
|
+
}
|
|
1297
|
+
isDetected() {
|
|
1298
|
+
return this.detected;
|
|
1299
|
+
}
|
|
1300
|
+
};
|
|
1301
|
+
var AgenticBrowserAnalyzer = class {
|
|
1302
|
+
constructor() {
|
|
1303
|
+
this.initialized = false;
|
|
1304
|
+
this.cometDetector = new CometDetector();
|
|
1305
|
+
this.mouseAnalyzer = new MouseAnalyzer();
|
|
1306
|
+
this.cdpDetector = new CDPDetector();
|
|
1307
|
+
}
|
|
1308
|
+
/**
|
|
1309
|
+
* Initialize all detectors
|
|
1310
|
+
*/
|
|
1311
|
+
init() {
|
|
1312
|
+
if (this.initialized) return;
|
|
1313
|
+
this.initialized = true;
|
|
1314
|
+
this.cometDetector.init();
|
|
1315
|
+
this.mouseAnalyzer.init();
|
|
1316
|
+
this.cdpDetector.detect();
|
|
1317
|
+
}
|
|
1318
|
+
/**
|
|
1319
|
+
* Get current detection result
|
|
1320
|
+
*/
|
|
1321
|
+
getResult() {
|
|
1322
|
+
const signals = [];
|
|
1323
|
+
let probability = 0;
|
|
1324
|
+
if (this.cometDetector.isDetected()) {
|
|
1325
|
+
signals.push("comet_dom_detected");
|
|
1326
|
+
probability = Math.max(probability, 0.85);
|
|
1327
|
+
}
|
|
1328
|
+
if (this.cdpDetector.isDetected()) {
|
|
1329
|
+
signals.push("cdp_detected");
|
|
1330
|
+
probability = Math.max(probability, 0.92);
|
|
1331
|
+
}
|
|
1332
|
+
const mousePatterns = this.mouseAnalyzer.getPatterns();
|
|
1333
|
+
if (mousePatterns.teleportingClicks > 0) {
|
|
1334
|
+
signals.push(`teleporting_clicks:${mousePatterns.teleportingClicks}`);
|
|
1335
|
+
probability = Math.max(probability, 0.78);
|
|
1336
|
+
}
|
|
1337
|
+
return {
|
|
1338
|
+
cometDOMDetected: this.cometDetector.isDetected(),
|
|
1339
|
+
cdpDetected: this.cdpDetector.isDetected(),
|
|
1340
|
+
mousePatterns,
|
|
1341
|
+
agenticProbability: probability,
|
|
1342
|
+
signals
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
/**
|
|
1346
|
+
* Cleanup resources
|
|
1347
|
+
*/
|
|
1348
|
+
destroy() {
|
|
1349
|
+
this.cometDetector.destroy();
|
|
1350
|
+
this.mouseAnalyzer.destroy();
|
|
1351
|
+
}
|
|
1352
|
+
};
|
|
1353
|
+
function createAgenticAnalyzer() {
|
|
1354
|
+
const analyzer = new AgenticBrowserAnalyzer();
|
|
1355
|
+
if (typeof document !== "undefined") {
|
|
1356
|
+
if (document.readyState === "loading") {
|
|
1357
|
+
document.addEventListener("DOMContentLoaded", () => analyzer.init());
|
|
1358
|
+
} else {
|
|
1359
|
+
analyzer.init();
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
return analyzer;
|
|
1363
|
+
}
|
|
974
1364
|
// Annotate the CommonJS export names for ESM import in node:
|
|
975
1365
|
0 && (module.exports = {
|
|
976
1366
|
AI_BOT_PATTERNS,
|
|
977
1367
|
AI_PLATFORMS,
|
|
1368
|
+
AgenticBrowserAnalyzer,
|
|
978
1369
|
VERSION,
|
|
1370
|
+
createAgenticAnalyzer,
|
|
979
1371
|
detectAIFromReferrer,
|
|
980
1372
|
detectAIFromUTM,
|
|
981
1373
|
detectNavigationType,
|
|
982
1374
|
loamly
|
|
983
1375
|
});
|
|
1376
|
+
/**
|
|
1377
|
+
* Loamly Tracker Configuration
|
|
1378
|
+
*
|
|
1379
|
+
* @module @loamly/tracker
|
|
1380
|
+
* @license MIT
|
|
1381
|
+
* @see https://github.com/loamly/loamly
|
|
1382
|
+
*/
|
|
1383
|
+
/**
|
|
1384
|
+
* Agentic Browser Detection
|
|
1385
|
+
*
|
|
1386
|
+
* LOA-187: Detects AI agentic browsers like Perplexity Comet, ChatGPT Atlas,
|
|
1387
|
+
* and other automated browsing agents.
|
|
1388
|
+
*
|
|
1389
|
+
* Detection methods:
|
|
1390
|
+
* - DOM fingerprinting (Perplexity Comet overlay)
|
|
1391
|
+
* - Mouse movement patterns (teleporting clicks)
|
|
1392
|
+
* - CDP (Chrome DevTools Protocol) automation fingerprint
|
|
1393
|
+
* - navigator.webdriver detection
|
|
1394
|
+
*
|
|
1395
|
+
* @module @loamly/tracker/detection/agentic-browser
|
|
1396
|
+
* @license MIT
|
|
1397
|
+
*/
|
|
984
1398
|
/**
|
|
985
1399
|
* Loamly Tracker
|
|
986
1400
|
*
|
|
@@ -988,7 +1402,9 @@ var loamly = {
|
|
|
988
1402
|
* See what AI tells your customers — and track when they click.
|
|
989
1403
|
*
|
|
990
1404
|
* @module @loamly/tracker
|
|
1405
|
+
* @version 1.8.0
|
|
991
1406
|
* @license MIT
|
|
1407
|
+
* @see https://github.com/loamly/loamly
|
|
992
1408
|
* @see https://loamly.ai
|
|
993
1409
|
*/
|
|
994
1410
|
//# sourceMappingURL=index.cjs.map
|