@loamly/tracker 1.6.0 → 1.7.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 +411 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +20 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.mjs +411 -1
- package/dist/index.mjs.map +1 -1
- package/dist/loamly.iife.global.js +411 -1
- package/dist/loamly.iife.global.js.map +1 -1
- package/dist/loamly.iife.min.global.js +1 -1
- package/dist/loamly.iife.min.global.js.map +1 -1
- package/package.json +1 -1
- package/src/config.ts +1 -1
- package/src/core.ts +135 -1
- package/src/detection/behavioral-classifier.ts +489 -0
- package/src/detection/index.ts +6 -2
- package/src/types.ts +21 -0
|
@@ -26,7 +26,7 @@ var Loamly = (() => {
|
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
// src/config.ts
|
|
29
|
-
var VERSION = "1.
|
|
29
|
+
var VERSION = "1.7.0";
|
|
30
30
|
var DEFAULT_CONFIG = {
|
|
31
31
|
apiHost: "https://app.loamly.ai",
|
|
32
32
|
endpoints: {
|
|
@@ -191,6 +191,322 @@ 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
|
+
|
|
194
510
|
// src/utils.ts
|
|
195
511
|
function generateUUID() {
|
|
196
512
|
if (typeof crypto !== "undefined" && crypto.randomUUID) {
|
|
@@ -276,6 +592,8 @@ var Loamly = (() => {
|
|
|
276
592
|
var sessionStartTime = null;
|
|
277
593
|
var navigationTiming = null;
|
|
278
594
|
var aiDetection = null;
|
|
595
|
+
var behavioralClassifier = null;
|
|
596
|
+
var behavioralMLResult = null;
|
|
279
597
|
function log(...args) {
|
|
280
598
|
if (debugMode) {
|
|
281
599
|
console.log("[Loamly]", ...args);
|
|
@@ -315,6 +633,9 @@ var Loamly = (() => {
|
|
|
315
633
|
if (!userConfig.disableBehavioral) {
|
|
316
634
|
setupBehavioralTracking();
|
|
317
635
|
}
|
|
636
|
+
behavioralClassifier = new BehavioralClassifier(1e4);
|
|
637
|
+
behavioralClassifier.setOnClassify(handleBehavioralClassification);
|
|
638
|
+
setupBehavioralMLTracking();
|
|
318
639
|
log("Initialization complete");
|
|
319
640
|
}
|
|
320
641
|
function pageview(customUrl) {
|
|
@@ -496,6 +817,89 @@ var Loamly = (() => {
|
|
|
496
817
|
body: JSON.stringify(payload)
|
|
497
818
|
});
|
|
498
819
|
}
|
|
820
|
+
function setupBehavioralMLTracking() {
|
|
821
|
+
if (!behavioralClassifier) return;
|
|
822
|
+
let mouseSampleCount = 0;
|
|
823
|
+
document.addEventListener("mousemove", (e) => {
|
|
824
|
+
mouseSampleCount++;
|
|
825
|
+
if (mouseSampleCount % 10 === 0 && behavioralClassifier) {
|
|
826
|
+
behavioralClassifier.recordMouse(e.clientX, e.clientY);
|
|
827
|
+
}
|
|
828
|
+
}, { passive: true });
|
|
829
|
+
document.addEventListener("click", () => {
|
|
830
|
+
if (behavioralClassifier) {
|
|
831
|
+
behavioralClassifier.recordClick();
|
|
832
|
+
}
|
|
833
|
+
}, { passive: true });
|
|
834
|
+
let lastScrollY = 0;
|
|
835
|
+
document.addEventListener("scroll", () => {
|
|
836
|
+
const currentY = window.scrollY;
|
|
837
|
+
if (Math.abs(currentY - lastScrollY) > 50 && behavioralClassifier) {
|
|
838
|
+
lastScrollY = currentY;
|
|
839
|
+
behavioralClassifier.recordScroll(currentY);
|
|
840
|
+
}
|
|
841
|
+
}, { passive: true });
|
|
842
|
+
document.addEventListener("focusin", (e) => {
|
|
843
|
+
if (behavioralClassifier) {
|
|
844
|
+
behavioralClassifier.recordFocusBlur("focus");
|
|
845
|
+
const target = e.target;
|
|
846
|
+
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
|
|
847
|
+
behavioralClassifier.recordFormStart(target.id || target.getAttribute("name") || "unknown");
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}, { passive: true });
|
|
851
|
+
document.addEventListener("focusout", (e) => {
|
|
852
|
+
if (behavioralClassifier) {
|
|
853
|
+
behavioralClassifier.recordFocusBlur("blur");
|
|
854
|
+
const target = e.target;
|
|
855
|
+
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") {
|
|
856
|
+
behavioralClassifier.recordFormEnd(target.id || target.getAttribute("name") || "unknown");
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}, { passive: true });
|
|
860
|
+
window.addEventListener("beforeunload", () => {
|
|
861
|
+
if (behavioralClassifier && !behavioralClassifier.hasClassified()) {
|
|
862
|
+
const result = behavioralClassifier.forceClassify();
|
|
863
|
+
if (result) {
|
|
864
|
+
handleBehavioralClassification(result);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
setTimeout(() => {
|
|
869
|
+
if (behavioralClassifier && !behavioralClassifier.hasClassified()) {
|
|
870
|
+
behavioralClassifier.forceClassify();
|
|
871
|
+
}
|
|
872
|
+
}, 3e4);
|
|
873
|
+
}
|
|
874
|
+
function handleBehavioralClassification(result) {
|
|
875
|
+
log("Behavioral ML classification:", result);
|
|
876
|
+
behavioralMLResult = {
|
|
877
|
+
classification: result.classification,
|
|
878
|
+
humanProbability: result.humanProbability,
|
|
879
|
+
aiProbability: result.aiProbability,
|
|
880
|
+
confidence: result.confidence,
|
|
881
|
+
signals: result.signals,
|
|
882
|
+
sessionDurationMs: result.sessionDurationMs
|
|
883
|
+
};
|
|
884
|
+
sendBehavioralEvent("ml_classification", {
|
|
885
|
+
classification: result.classification,
|
|
886
|
+
human_probability: result.humanProbability,
|
|
887
|
+
ai_probability: result.aiProbability,
|
|
888
|
+
confidence: result.confidence,
|
|
889
|
+
signals: result.signals,
|
|
890
|
+
session_duration_ms: result.sessionDurationMs,
|
|
891
|
+
navigation_timing: navigationTiming,
|
|
892
|
+
ai_detection: aiDetection
|
|
893
|
+
});
|
|
894
|
+
if (result.classification === "ai_influenced" && result.confidence >= 0.7) {
|
|
895
|
+
aiDetection = {
|
|
896
|
+
isAI: true,
|
|
897
|
+
confidence: result.confidence,
|
|
898
|
+
method: "behavioral"
|
|
899
|
+
};
|
|
900
|
+
log("AI detection updated from behavioral ML:", aiDetection);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
499
903
|
function getCurrentSessionId() {
|
|
500
904
|
return sessionId;
|
|
501
905
|
}
|
|
@@ -508,6 +912,9 @@ var Loamly = (() => {
|
|
|
508
912
|
function getNavigationTimingResult() {
|
|
509
913
|
return navigationTiming;
|
|
510
914
|
}
|
|
915
|
+
function getBehavioralMLResult() {
|
|
916
|
+
return behavioralMLResult;
|
|
917
|
+
}
|
|
511
918
|
function isTrackerInitialized() {
|
|
512
919
|
return initialized;
|
|
513
920
|
}
|
|
@@ -519,6 +926,8 @@ var Loamly = (() => {
|
|
|
519
926
|
sessionStartTime = null;
|
|
520
927
|
navigationTiming = null;
|
|
521
928
|
aiDetection = null;
|
|
929
|
+
behavioralClassifier = null;
|
|
930
|
+
behavioralMLResult = null;
|
|
522
931
|
try {
|
|
523
932
|
sessionStorage.removeItem("loamly_session");
|
|
524
933
|
sessionStorage.removeItem("loamly_start");
|
|
@@ -539,6 +948,7 @@ var Loamly = (() => {
|
|
|
539
948
|
getVisitorId: getCurrentVisitorId,
|
|
540
949
|
getAIDetection: getAIDetectionResult,
|
|
541
950
|
getNavigationTiming: getNavigationTimingResult,
|
|
951
|
+
getBehavioralML: getBehavioralMLResult,
|
|
542
952
|
isInitialized: isTrackerInitialized,
|
|
543
953
|
reset,
|
|
544
954
|
debug: setDebug
|