@passiveintent/core 1.0.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/LICENSE +661 -0
- package/README.md +711 -0
- package/dist/adapters.d.ts +183 -0
- package/dist/adapters.d.ts.map +1 -0
- package/dist/calibration.cjs +2 -0
- package/dist/calibration.cjs.map +1 -0
- package/dist/calibration.d.ts +49 -0
- package/dist/calibration.d.ts.map +1 -0
- package/dist/calibration.js +2 -0
- package/dist/calibration.js.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/intent-sdk-performance.d.ts +71 -0
- package/dist/intent-sdk-performance.d.ts.map +1 -0
- package/dist/intent-sdk.d.ts +44 -0
- package/dist/intent-sdk.d.ts.map +1 -0
- package/dist/performance-instrumentation.d.ts +55 -0
- package/dist/performance-instrumentation.d.ts.map +1 -0
- package/dist/reporting-utils.d.ts +17 -0
- package/dist/reporting-utils.d.ts.map +1 -0
- package/package.json +99 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Purushottam <purushottam@passiveintent.dev>
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the AGPL-3.0-only license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Isomorphic adapters for storage and timers.
|
|
9
|
+
* --------------------------------------------------------
|
|
10
|
+
* Allows the SDK to run safely in SSR environments (Next.js,
|
|
11
|
+
* Nuxt, Remix, etc.) where `window` / `localStorage` are
|
|
12
|
+
* not available at import time or at runtime.
|
|
13
|
+
*
|
|
14
|
+
* Browser implementations gracefully degrade to no-ops when
|
|
15
|
+
* the DOM globals are absent.
|
|
16
|
+
*/
|
|
17
|
+
export interface StorageAdapter {
|
|
18
|
+
getItem(key: string): string | null;
|
|
19
|
+
setItem(key: string, value: string): void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Async storage adapter for environments where storage I/O is inherently
|
|
23
|
+
* asynchronous (React Native AsyncStorage, Capacitor Preferences, IndexedDB
|
|
24
|
+
* wrappers, etc.).
|
|
25
|
+
*
|
|
26
|
+
* Use `IntentManager.createAsync(config)` to initialize the engine with an
|
|
27
|
+
* async backend — the factory awaits the initial `getItem` call before
|
|
28
|
+
* constructing the engine, preserving the synchronous hot-path for `track()`.
|
|
29
|
+
*
|
|
30
|
+
* The synchronous `StorageAdapter` interface remains the default for
|
|
31
|
+
* browser `localStorage`-backed use cases.
|
|
32
|
+
*/
|
|
33
|
+
export interface AsyncStorageAdapter {
|
|
34
|
+
getItem(key: string): Promise<string | null>;
|
|
35
|
+
setItem(key: string, value: string): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* localStorage-backed adapter.
|
|
39
|
+
* Falls back to no-ops when `window` or `localStorage` is unavailable
|
|
40
|
+
* (e.g. SSR, Web Workers, or restrictive iframes).
|
|
41
|
+
*/
|
|
42
|
+
export declare class BrowserStorageAdapter implements StorageAdapter {
|
|
43
|
+
getItem(key: string): string | null;
|
|
44
|
+
setItem(key: string, value: string): void;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Opaque handle returned by the timer adapter.
|
|
48
|
+
* Bridges the gap between browser (number) and Node.js (Timeout) return types.
|
|
49
|
+
*/
|
|
50
|
+
export type TimerHandle = any;
|
|
51
|
+
export interface TimerAdapter {
|
|
52
|
+
setTimeout(fn: () => void, delay: number): TimerHandle;
|
|
53
|
+
clearTimeout(id: TimerHandle): void;
|
|
54
|
+
now(): number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Timer adapter backed by the global `setTimeout` / `clearTimeout`.
|
|
58
|
+
* Uses `globalThis` so it works in browsers, Node.js, Deno, Bun, and
|
|
59
|
+
* Cloudflare Workers alike.
|
|
60
|
+
*/
|
|
61
|
+
export declare class BrowserTimerAdapter implements TimerAdapter {
|
|
62
|
+
setTimeout(fn: () => void, delay: number): TimerHandle;
|
|
63
|
+
clearTimeout(id: TimerHandle): void;
|
|
64
|
+
now(): number;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Simple in-memory storage adapter.
|
|
68
|
+
* Handy for unit tests and server-side rendering where persistence is
|
|
69
|
+
* neither needed nor available.
|
|
70
|
+
*/
|
|
71
|
+
export declare class MemoryStorageAdapter implements StorageAdapter {
|
|
72
|
+
private readonly store;
|
|
73
|
+
getItem(key: string): string | null;
|
|
74
|
+
setItem(key: string, value: string): void;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Abstracts browser DOM lifecycle events (page visibility) out of the core
|
|
78
|
+
* engine so that the SDK can be used safely in React Native, Electron, and
|
|
79
|
+
* server-side / SSR environments where `document` is absent.
|
|
80
|
+
*
|
|
81
|
+
* Implementations should call registered callbacks when the host environment
|
|
82
|
+
* transitions between an active ("resumed") and an inactive ("paused") state.
|
|
83
|
+
*/
|
|
84
|
+
export interface LifecycleAdapter {
|
|
85
|
+
/**
|
|
86
|
+
* Register a callback to be invoked when the environment becomes inactive.
|
|
87
|
+
* Returns an unsubscribe function that removes only this callback, leaving
|
|
88
|
+
* any other registered callbacks untouched.
|
|
89
|
+
*/
|
|
90
|
+
onPause(callback: () => void): () => void;
|
|
91
|
+
/**
|
|
92
|
+
* Register a callback to be invoked when the environment becomes active.
|
|
93
|
+
* Returns an unsubscribe function that removes only this callback, leaving
|
|
94
|
+
* any other registered callbacks untouched.
|
|
95
|
+
*/
|
|
96
|
+
onResume(callback: () => void): () => void;
|
|
97
|
+
/**
|
|
98
|
+
* Optional: register a callback to be invoked on any user interaction
|
|
99
|
+
* (mouse, keyboard, scroll, touch). Used by the idle-state detector.
|
|
100
|
+
*
|
|
101
|
+
* Implementations should throttle the callback internally (e.g. max once
|
|
102
|
+
* per 1 000 ms) to avoid flooding the engine with high-frequency events.
|
|
103
|
+
*
|
|
104
|
+
* Returns an unsubscribe function that removes only this callback, or
|
|
105
|
+
* `null` when the environment cannot deliver interaction events (e.g.
|
|
106
|
+
* SSR, Node.js tests with a stubbed `window`).
|
|
107
|
+
*
|
|
108
|
+
* Backward-compatible — adapters that do not implement this method are
|
|
109
|
+
* silently skipped and idle detection is disabled.
|
|
110
|
+
*/
|
|
111
|
+
onInteraction?(callback: () => void): (() => void) | null;
|
|
112
|
+
/**
|
|
113
|
+
* Optional: register a callback to be invoked when the user signals exit
|
|
114
|
+
* intent by moving the pointer above the viewport top edge (toward the
|
|
115
|
+
* browser chrome / address bar).
|
|
116
|
+
*
|
|
117
|
+
* Implementations should only fire the callback when `MouseEvent.clientY <= 0`
|
|
118
|
+
* so that normal in-page mouse movement is ignored.
|
|
119
|
+
*
|
|
120
|
+
* Returns an unsubscribe function that removes only this callback.
|
|
121
|
+
*
|
|
122
|
+
* Backward-compatible — adapters that do not implement this method are
|
|
123
|
+
* silently skipped and exit-intent detection is disabled.
|
|
124
|
+
*/
|
|
125
|
+
onExitIntent?(callback: () => void): () => void;
|
|
126
|
+
/** Remove all event listeners and release resources held by this adapter. */
|
|
127
|
+
destroy(): void;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Lifecycle adapter backed by the Page Visibility API
|
|
131
|
+
* (`document.visibilitychange`).
|
|
132
|
+
*
|
|
133
|
+
* Guards every `document` access with a `typeof document !== 'undefined'`
|
|
134
|
+
* check so the class can be imported in SSR / Node.js / React Native
|
|
135
|
+
* environments without throwing.
|
|
136
|
+
*
|
|
137
|
+
* Usage:
|
|
138
|
+
* ```ts
|
|
139
|
+
* const lifecycle = new BrowserLifecycleAdapter();
|
|
140
|
+
* lifecycle.onPause(() => {
|
|
141
|
+
* // e.g. flush pending work or persist state
|
|
142
|
+
* });
|
|
143
|
+
* lifecycle.onResume(() => {
|
|
144
|
+
* // e.g. restart timers or resume work
|
|
145
|
+
* });
|
|
146
|
+
* // later…
|
|
147
|
+
* lifecycle.destroy();
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export declare class BrowserLifecycleAdapter implements LifecycleAdapter {
|
|
151
|
+
private readonly pauseCallbacks;
|
|
152
|
+
private readonly resumeCallbacks;
|
|
153
|
+
private readonly interactionCallbacks;
|
|
154
|
+
private readonly exitIntentCallbacks;
|
|
155
|
+
private readonly handler;
|
|
156
|
+
/** Tracks the DOM listeners registered for interaction throttling. */
|
|
157
|
+
private interactionHandler;
|
|
158
|
+
private interactionLastFired;
|
|
159
|
+
private static readonly INTERACTION_THROTTLE_MS;
|
|
160
|
+
private static readonly INTERACTION_EVENTS;
|
|
161
|
+
/** Handler for exit-intent mouseleave detection on document.documentElement. */
|
|
162
|
+
private exitIntentHandler;
|
|
163
|
+
constructor();
|
|
164
|
+
onPause(callback: () => void): () => void;
|
|
165
|
+
onResume(callback: () => void): () => void;
|
|
166
|
+
onInteraction(callback: () => void): (() => void) | null;
|
|
167
|
+
/** Remove interaction DOM listeners if they are currently attached. */
|
|
168
|
+
private teardownInteractionListeners;
|
|
169
|
+
/**
|
|
170
|
+
* Register a callback to be invoked when the user moves the pointer above
|
|
171
|
+
* the top edge of the viewport (clientY <= 0), which typically indicates
|
|
172
|
+
* they are heading toward the browser chrome / address bar — a reliable
|
|
173
|
+
* proxy for exit intent on desktop.
|
|
174
|
+
*
|
|
175
|
+
* The listener is attached lazily on the first subscription so that the
|
|
176
|
+
* adapter stays tree-shakeable in SSR / Node.js environments.
|
|
177
|
+
*/
|
|
178
|
+
onExitIntent(callback: () => void): () => void;
|
|
179
|
+
/** Remove the exit-intent DOM listener if currently attached. */
|
|
180
|
+
private teardownExitIntentListener;
|
|
181
|
+
destroy(): void;
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=adapters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;GASG;AAMH,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACpC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3C;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7C,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpD;AAED;;;;GAIG;AACH,qBAAa,qBAAsB,YAAW,cAAc;IAC1D,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAUnC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;CAO1C;AAMD;;;GAGG;AAEH,MAAM,MAAM,WAAW,GAAG,GAAG,CAAC;AAE9B,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW,CAAC;IACvD,YAAY,CAAC,EAAE,EAAE,WAAW,GAAG,IAAI,CAAC;IACpC,GAAG,IAAI,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,qBAAa,mBAAoB,YAAW,YAAY;IACtD,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,WAAW;IAQtD,YAAY,CAAC,EAAE,EAAE,WAAW,GAAG,IAAI;IAKnC,GAAG,IAAI,MAAM;CASd;AAMD;;;;GAIG;AACH,qBAAa,oBAAqB,YAAW,cAAc;IACzD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAA6B;IAEnD,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAInC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;CAG1C;AAMD;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IAC3C;;;;;;;;;;;;;OAaG;IACH,aAAa,CAAC,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAC;IAC1D;;;;;;;;;;;;OAYG;IACH,YAAY,CAAC,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IAChD,6EAA6E;IAC7E,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,uBAAwB,YAAW,gBAAgB;IAC9D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyB;IACxD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAyB;IACzD,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAyB;IAC9D,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAyB;IAC7D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAa;IAErC,sEAAsE;IACtE,OAAO,CAAC,kBAAkB,CAA6B;IACvD,OAAO,CAAC,oBAAoB,CAAK;IACjC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAS;IACxD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAKxC;IAEF,gFAAgF;IAChF,OAAO,CAAC,iBAAiB,CAA0C;;IAiBnE,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAQzC,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAQ1C,aAAa,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI;IA8CxD,uEAAuE;IACvE,OAAO,CAAC,4BAA4B;IAapC;;;;;;;;OAQG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IA2B9C,iEAAiE;IACjE,OAAO,CAAC,0BAA0B;IAUlC,OAAO,IAAI,IAAI;CAWhB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var c=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var h=Object.getOwnPropertyNames;var d=Object.prototype.hasOwnProperty;var L=(e,n)=>{for(var a in n)c(e,a,{get:n[a],enumerable:!0})},M=(e,n,a,i)=>{if(n&&typeof n=="object"||typeof n=="function")for(let t of h(n))!d.call(e,t)&&t!==a&&c(e,t,{get:()=>n[t],enumerable:!(i=p(n,t))||i.enumerable});return e};var S=e=>M(c({},"__esModule",{value:!0}),e);var v={};L(v,{runCalibration:()=>C});module.exports=S(v);function C(e){if(e.length===0)throw new RangeError("runCalibration: sessionLogs must contain at least one value.");let n=e.length,a=0;for(let o of e)a+=o;let i=a/n,t=0;for(let o of e){let r=o-i;t+=r*r}let m=Math.sqrt(t/n),u=e.slice().sort((o,r)=>o-r);function b(o){let r=o/100*(n-1),l=Math.floor(r),s=Math.ceil(r);if(l===s)return u[l];let f=r-l;return u[l]*(1-f)+u[s]*f}return{baselineMeanLL:i,baselineStdLL:m,sampleSize:n,p5:b(5),p95:b(95)}}0&&(module.exports={runCalibration});
|
|
2
|
+
//# sourceMappingURL=calibration.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/calibration.ts"],"sourcesContent":["/**\n * Copyright (c) 2026 Purushottam <purushottam@passiveintent.dev>\n *\n * This source code is licensed under the AGPL-3.0-only license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/**\n * Result returned by {@link runCalibration}.\n */\nexport interface CalibrationResult {\n /** Mean log-likelihood across all sampled sessions. Pass as `baselineMeanLL`. */\n baselineMeanLL: number;\n /** Standard deviation of log-likelihood across all sampled sessions. Pass as `baselineStdLL`. */\n baselineStdLL: number;\n /** Number of sessions included in the calibration run. */\n sampleSize: number;\n /** 5th-percentile log-likelihood — useful as a hard anomaly floor. */\n p5: number;\n /** 95th-percentile log-likelihood — confirms the upper bound of normal sessions. */\n p95: number;\n}\n\n/**\n * Compute calibration parameters from a representative sample of per-session\n * log-likelihood scores.\n *\n * Feed the raw log-likelihood values collected from at least 500 production\n * sessions into this function and pass the output directly to `IntentManager`\n * (or `usePassiveIntent`) as `baselineMeanLL` and `baselineStdLL`.\n *\n * @example\n * ```ts\n * import { runCalibration } from '@passiveintent/core/calibration';\n *\n * const result = runCalibration(sessionLogs);\n * // { baselineMeanLL: -3.47, baselineStdLL: 0.91, sampleSize: 1243, p5: -5.12, p95: -1.83 }\n *\n * const intent = new IntentManager({\n * storageKey: 'my-app',\n * baselineMeanLL: result.baselineMeanLL,\n * baselineStdLL: result.baselineStdLL,\n * });\n * ```\n *\n * @param sessionLogs - Array of per-session log-likelihood scores (negative numbers).\n * Must contain at least 1 value.\n * @throws {RangeError} When `sessionLogs` is empty.\n */\nexport function runCalibration(sessionLogs: number[]): CalibrationResult {\n if (sessionLogs.length === 0) {\n throw new RangeError('runCalibration: sessionLogs must contain at least one value.');\n }\n\n const sampleSize = sessionLogs.length;\n\n // Mean (single pass)\n let sum = 0;\n for (const v of sessionLogs) sum += v;\n const baselineMeanLL = sum / sampleSize;\n\n // Population standard deviation (second pass for numerical stability)\n let sumSq = 0;\n for (const v of sessionLogs) {\n const diff = v - baselineMeanLL;\n sumSq += diff * diff;\n }\n const baselineStdLL = Math.sqrt(sumSq / sampleSize);\n\n // Percentiles via sorted copy\n const sorted = sessionLogs.slice().sort((a, b) => a - b);\n\n function percentile(p: number): number {\n const idx = (p / 100) * (sampleSize - 1);\n const lo = Math.floor(idx);\n const hi = Math.ceil(idx);\n if (lo === hi) return sorted[lo];\n const frac = idx - lo;\n return sorted[lo] * (1 - frac) + sorted[hi] * frac;\n }\n\n return {\n baselineMeanLL,\n baselineStdLL,\n sampleSize,\n p5: percentile(5),\n p95: percentile(95),\n };\n}\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,oBAAAE,IAAA,eAAAC,EAAAH,GAiDO,SAASE,EAAeE,EAA0C,CACvE,GAAIA,EAAY,SAAW,EACzB,MAAM,IAAI,WAAW,8DAA8D,EAGrF,IAAMC,EAAaD,EAAY,OAG3BE,EAAM,EACV,QAAWC,KAAKH,EAAaE,GAAOC,EACpC,IAAMC,EAAiBF,EAAMD,EAGzBI,EAAQ,EACZ,QAAWF,KAAKH,EAAa,CAC3B,IAAMM,EAAOH,EAAIC,EACjBC,GAASC,EAAOA,CAClB,CACA,IAAMC,EAAgB,KAAK,KAAKF,EAAQJ,CAAU,EAG5CO,EAASR,EAAY,MAAM,EAAE,KAAK,CAACS,EAAGC,IAAMD,EAAIC,CAAC,EAEvD,SAASC,EAAWC,EAAmB,CACrC,IAAMC,EAAOD,EAAI,KAAQX,EAAa,GAChCa,EAAK,KAAK,MAAMD,CAAG,EACnBE,EAAK,KAAK,KAAKF,CAAG,EACxB,GAAIC,IAAOC,EAAI,OAAOP,EAAOM,CAAE,EAC/B,IAAME,EAAOH,EAAMC,EACnB,OAAON,EAAOM,CAAE,GAAK,EAAIE,GAAQR,EAAOO,CAAE,EAAIC,CAChD,CAEA,MAAO,CACL,eAAAZ,EACA,cAAAG,EACA,WAAAN,EACA,GAAIU,EAAW,CAAC,EAChB,IAAKA,EAAW,EAAE,CACpB,CACF","names":["calibration_exports","__export","runCalibration","__toCommonJS","sessionLogs","sampleSize","sum","v","baselineMeanLL","sumSq","diff","baselineStdLL","sorted","a","b","percentile","p","idx","lo","hi","frac"]}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026 Purushottam <purushottam@passiveintent.dev>
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the AGPL-3.0-only license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Result returned by {@link runCalibration}.
|
|
9
|
+
*/
|
|
10
|
+
export interface CalibrationResult {
|
|
11
|
+
/** Mean log-likelihood across all sampled sessions. Pass as `baselineMeanLL`. */
|
|
12
|
+
baselineMeanLL: number;
|
|
13
|
+
/** Standard deviation of log-likelihood across all sampled sessions. Pass as `baselineStdLL`. */
|
|
14
|
+
baselineStdLL: number;
|
|
15
|
+
/** Number of sessions included in the calibration run. */
|
|
16
|
+
sampleSize: number;
|
|
17
|
+
/** 5th-percentile log-likelihood — useful as a hard anomaly floor. */
|
|
18
|
+
p5: number;
|
|
19
|
+
/** 95th-percentile log-likelihood — confirms the upper bound of normal sessions. */
|
|
20
|
+
p95: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Compute calibration parameters from a representative sample of per-session
|
|
24
|
+
* log-likelihood scores.
|
|
25
|
+
*
|
|
26
|
+
* Feed the raw log-likelihood values collected from at least 500 production
|
|
27
|
+
* sessions into this function and pass the output directly to `IntentManager`
|
|
28
|
+
* (or `usePassiveIntent`) as `baselineMeanLL` and `baselineStdLL`.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```ts
|
|
32
|
+
* import { runCalibration } from '@passiveintent/core/calibration';
|
|
33
|
+
*
|
|
34
|
+
* const result = runCalibration(sessionLogs);
|
|
35
|
+
* // { baselineMeanLL: -3.47, baselineStdLL: 0.91, sampleSize: 1243, p5: -5.12, p95: -1.83 }
|
|
36
|
+
*
|
|
37
|
+
* const intent = new IntentManager({
|
|
38
|
+
* storageKey: 'my-app',
|
|
39
|
+
* baselineMeanLL: result.baselineMeanLL,
|
|
40
|
+
* baselineStdLL: result.baselineStdLL,
|
|
41
|
+
* });
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @param sessionLogs - Array of per-session log-likelihood scores (negative numbers).
|
|
45
|
+
* Must contain at least 1 value.
|
|
46
|
+
* @throws {RangeError} When `sessionLogs` is empty.
|
|
47
|
+
*/
|
|
48
|
+
export declare function runCalibration(sessionLogs: number[]): CalibrationResult;
|
|
49
|
+
//# sourceMappingURL=calibration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calibration.d.ts","sourceRoot":"","sources":["../src/calibration.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,iFAAiF;IACjF,cAAc,EAAE,MAAM,CAAC;IACvB,iGAAiG;IACjG,aAAa,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,UAAU,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,EAAE,EAAE,MAAM,CAAC;IACX,oFAAoF;IACpF,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,iBAAiB,CAuCvE"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function m(t){if(t.length===0)throw new RangeError("runCalibration: sessionLogs must contain at least one value.");let r=t.length,i=0;for(let e of t)i+=e;let l=i/r,u=0;for(let e of t){let n=e-l;u+=n*n}let f=Math.sqrt(u/r),o=t.slice().sort((e,n)=>e-n);function c(e){let n=e/100*(r-1),a=Math.floor(n),b=Math.ceil(n);if(a===b)return o[a];let s=n-a;return o[a]*(1-s)+o[b]*s}return{baselineMeanLL:l,baselineStdLL:f,sampleSize:r,p5:c(5),p95:c(95)}}export{m as runCalibration};
|
|
2
|
+
//# sourceMappingURL=calibration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/calibration.ts"],"sourcesContent":["/**\n * Copyright (c) 2026 Purushottam <purushottam@passiveintent.dev>\n *\n * This source code is licensed under the AGPL-3.0-only license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\n/**\n * Result returned by {@link runCalibration}.\n */\nexport interface CalibrationResult {\n /** Mean log-likelihood across all sampled sessions. Pass as `baselineMeanLL`. */\n baselineMeanLL: number;\n /** Standard deviation of log-likelihood across all sampled sessions. Pass as `baselineStdLL`. */\n baselineStdLL: number;\n /** Number of sessions included in the calibration run. */\n sampleSize: number;\n /** 5th-percentile log-likelihood — useful as a hard anomaly floor. */\n p5: number;\n /** 95th-percentile log-likelihood — confirms the upper bound of normal sessions. */\n p95: number;\n}\n\n/**\n * Compute calibration parameters from a representative sample of per-session\n * log-likelihood scores.\n *\n * Feed the raw log-likelihood values collected from at least 500 production\n * sessions into this function and pass the output directly to `IntentManager`\n * (or `usePassiveIntent`) as `baselineMeanLL` and `baselineStdLL`.\n *\n * @example\n * ```ts\n * import { runCalibration } from '@passiveintent/core/calibration';\n *\n * const result = runCalibration(sessionLogs);\n * // { baselineMeanLL: -3.47, baselineStdLL: 0.91, sampleSize: 1243, p5: -5.12, p95: -1.83 }\n *\n * const intent = new IntentManager({\n * storageKey: 'my-app',\n * baselineMeanLL: result.baselineMeanLL,\n * baselineStdLL: result.baselineStdLL,\n * });\n * ```\n *\n * @param sessionLogs - Array of per-session log-likelihood scores (negative numbers).\n * Must contain at least 1 value.\n * @throws {RangeError} When `sessionLogs` is empty.\n */\nexport function runCalibration(sessionLogs: number[]): CalibrationResult {\n if (sessionLogs.length === 0) {\n throw new RangeError('runCalibration: sessionLogs must contain at least one value.');\n }\n\n const sampleSize = sessionLogs.length;\n\n // Mean (single pass)\n let sum = 0;\n for (const v of sessionLogs) sum += v;\n const baselineMeanLL = sum / sampleSize;\n\n // Population standard deviation (second pass for numerical stability)\n let sumSq = 0;\n for (const v of sessionLogs) {\n const diff = v - baselineMeanLL;\n sumSq += diff * diff;\n }\n const baselineStdLL = Math.sqrt(sumSq / sampleSize);\n\n // Percentiles via sorted copy\n const sorted = sessionLogs.slice().sort((a, b) => a - b);\n\n function percentile(p: number): number {\n const idx = (p / 100) * (sampleSize - 1);\n const lo = Math.floor(idx);\n const hi = Math.ceil(idx);\n if (lo === hi) return sorted[lo];\n const frac = idx - lo;\n return sorted[lo] * (1 - frac) + sorted[hi] * frac;\n }\n\n return {\n baselineMeanLL,\n baselineStdLL,\n sampleSize,\n p5: percentile(5),\n p95: percentile(95),\n };\n}\n"],"mappings":"AAiDO,SAASA,EAAeC,EAA0C,CACvE,GAAIA,EAAY,SAAW,EACzB,MAAM,IAAI,WAAW,8DAA8D,EAGrF,IAAMC,EAAaD,EAAY,OAG3BE,EAAM,EACV,QAAWC,KAAKH,EAAaE,GAAOC,EACpC,IAAMC,EAAiBF,EAAMD,EAGzBI,EAAQ,EACZ,QAAWF,KAAKH,EAAa,CAC3B,IAAMM,EAAOH,EAAIC,EACjBC,GAASC,EAAOA,CAClB,CACA,IAAMC,EAAgB,KAAK,KAAKF,EAAQJ,CAAU,EAG5CO,EAASR,EAAY,MAAM,EAAE,KAAK,CAACS,EAAGC,IAAMD,EAAIC,CAAC,EAEvD,SAASC,EAAWC,EAAmB,CACrC,IAAMC,EAAOD,EAAI,KAAQX,EAAa,GAChCa,EAAK,KAAK,MAAMD,CAAG,EACnBE,EAAK,KAAK,KAAKF,CAAG,EACxB,GAAIC,IAAOC,EAAI,OAAOP,EAAOM,CAAE,EAC/B,IAAME,EAAOH,EAAMC,EACnB,OAAON,EAAOM,CAAE,GAAK,EAAIE,GAAQR,EAAOO,CAAE,EAAIC,CAChD,CAEA,MAAO,CACL,eAAAZ,EACA,cAAAG,EACA,WAAAN,EACA,GAAIU,EAAW,CAAC,EAChB,IAAKA,EAAW,EAAE,CACpB,CACF","names":["runCalibration","sessionLogs","sampleSize","sum","v","baselineMeanLL","sumSq","diff","baselineStdLL","sorted","a","b","percentile","p","idx","lo","hi","frac"]}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var it=Object.defineProperty;var bt=Object.getOwnPropertyDescriptor;var vt=Object.getOwnPropertyNames;var Tt=Object.prototype.hasOwnProperty;var St=(i,t)=>{for(var e in t)it(i,e,{get:t[e],enumerable:!0})},Et=(i,t,e,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of vt(t))!Tt.call(i,s)&&s!==e&&it(i,s,{get:()=>t[s],enumerable:!(r=bt(t,s))||r.enumerable});return i};var wt=i=>Et(it({},"__esModule",{value:!0}),i);var Lt={};St(Lt,{ATTENTION_RETURN_THRESHOLD_MS:()=>U,AnomalyDispatcher:()=>S,BenchmarkRecorder:()=>f,BloomFilter:()=>y,BroadcastSync:()=>A,BrowserLifecycleAdapter:()=>D,BrowserStorageAdapter:()=>k,BrowserTimerAdapter:()=>P,DriftProtectionPolicy:()=>w,EventEmitter:()=>T,IDLE_CHECK_INTERVAL_MS:()=>F,IntentManager:()=>N,MAX_PLAUSIBLE_DWELL_MS:()=>v,MAX_STATE_LENGTH:()=>st,MarkovGraph:()=>u,MemoryStorageAdapter:()=>j,SignalEngine:()=>E,USER_IDLE_THRESHOLD_MS:()=>B,computeBloomConfig:()=>ot,normalizeRouteState:()=>L});module.exports=wt(Lt);function R(i){let e=[];for(let r=0;r<i.length;r+=32768){let s=i.subarray(r,Math.min(r+32768,i.length));e.push(String.fromCharCode.apply(null,s))}return btoa(e.join(""))}function H(i){let t=atob(i),e=new Uint8Array(t.length);for(let r=0;r<t.length;r+=1)e[r]=t.charCodeAt(r);return e}function lt(i,t=2166136261){let e=t>>>0;for(let r=0;r<i.length;r+=1)e^=i.charCodeAt(r),e+=(e<<1)+(e<<4)+(e<<7)+(e<<8)+(e<<24);return e>>>0}var G=(()=>{let i=globalThis.__PFIE_WM__;return(typeof i=="number"?i:2343432205)>>>0})(),C=new Uint32Array(2),y=class i{constructor(t={},e){this.bitSize=t.bitSize??2048,this.hashCount=t.hashCount??4;let r=Math.ceil(this.bitSize/8);this.bits=e&&e.length===r?e:new Uint8Array(r)}add(t){this.computeHashes(t);let e=C[0],r=C[1];for(let s=0;s<this.hashCount;s+=1){let n=(e+s*r>>>0)%this.bitSize;this.setBit(n)}}check(t){this.computeHashes(t);let e=C[0],r=C[1];for(let s=0;s<this.hashCount;s+=1){let n=(e+s*r>>>0)%this.bitSize;if(!this.getBit(n))return!1}return!0}static computeOptimal(t,e){if(t<=0)return{bitSize:8,hashCount:1};e<=0&&(e=1e-10),e>=1&&(e=.99);let r=Math.LN2,s=r*r,n=Math.ceil(-(t*Math.log(e))/s),o=Math.max(1,Math.round(n/t*r));return{bitSize:n,hashCount:o}}estimateCurrentFPR(t){if(t<=0)return 0;let e=-(this.hashCount*t)/this.bitSize,r=Math.exp(e);return Math.pow(1-r,this.hashCount)}getBitsetByteSize(){return this.bits.byteLength}toBase64(){return R(this.bits)}static fromBase64(t,e={}){return new i(e,H(t))}setBit(t){let e=t>>3,r=1<<(t&7);this.bits[e]|=r}getBit(t){let e=t>>3,r=1<<(t&7);return(this.bits[e]&r)!==0}computeHashes(t){C[0]=(lt(t,2166136261)^G^G)>>>0,C[1]=(lt(t,16777619)^G^G)>>>0}};function ot(i,t){i<=0&&(i=1),t<=0&&(t=1e-10),t>=1&&(t=.99);let e=Math.ceil(-(i*Math.log(t))/(Math.LN2*Math.LN2)),r=Math.max(1,Math.round(e/i*Math.log(2))),s=Math.exp(-(r*i)/e),n=Math.pow(1-s,r);return{bitSize:e,hashCount:r,estimatedFpRate:n}}var u=class i{constructor(t={}){this.rows=new Map;this.stateToIndex=new Map;this.indexToState=[];this.freedIndices=[];this.highEntropyThreshold=t.highEntropyThreshold??.75,this.divergenceThreshold=Math.abs(t.divergenceThreshold??3.5),this.smoothingEpsilon=t.smoothingEpsilon??.01,this.baselineMeanLL=t.baselineMeanLL,this.baselineStdLL=t.baselineStdLL,this.maxStates=t.maxStates??500;let e=t.smoothingAlpha??.1;this.smoothingAlpha=Number.isFinite(e)&&e>=0?e:0}ensureState(t){if(t==="")throw new Error("MarkovGraph: state label must not be empty string");let e=this.stateToIndex.get(t);if(e!==void 0)return e;let r;return this.freedIndices.length>0?(r=this.freedIndices.pop(),this.indexToState[r]=t):(r=this.indexToState.length,this.indexToState.push(t)),this.stateToIndex.set(t,r),r}incrementTransition(t,e){this.stateToIndex.size>=this.maxStates*1.5&&this.prune();let r=this.ensureState(t),s=this.ensureState(e),n=this.rows.get(r)??{total:0,toCounts:new Map},o=(n.toCounts.get(s)??0)+1;n.toCounts.set(s,o),n.total+=1,this.rows.set(r,n)}getProbability(t,e){let r=this.stateToIndex.get(t),s=this.stateToIndex.get(e);if(r===void 0||s===void 0)return 0;let n=this.rows.get(r);if(!n||n.total===0)return 0;let o=n.toCounts.get(s)??0;return this.smoothingAlpha===0?o/n.total:(o+this.smoothingAlpha)/(n.total+this.smoothingAlpha*this.stateToIndex.size)}entropyForState(t){let e=this.stateToIndex.get(t);if(e===void 0)return 0;let r=this.rows.get(e);if(!r||r.total===0)return 0;let s=0;if(this.smoothingAlpha===0)r.toCounts.forEach(n=>{let o=n/r.total;o>0&&(s-=o*Math.log(o))});else{let n=this.stateToIndex.size,o=r.total+this.smoothingAlpha*n;r.toCounts.forEach(l=>{let h=(l+this.smoothingAlpha)/o;s-=h*Math.log(h)});let a=n-r.toCounts.size;if(a>0){let l=this.smoothingAlpha/o;l>0&&(s-=a*l*Math.log(l))}}return s}normalizedEntropyForState(t){let e=this.stateToIndex.get(t);if(e===void 0)return 0;let r=this.rows.get(e);if(!r||r.total===0)return 0;let s=Math.max(2,r.toCounts.size),n=Math.log(s);if(n<=0)return 0;let o=0;r.toCounts.forEach(l=>{let h=l/r.total;h>0&&(o-=h*Math.log(h))});let a=o/n;return Math.min(1,Math.max(0,a))}static logLikelihoodTrajectory(t,e,r=.01){if(e.length<2)return 0;let s=0;for(let n=0;n<e.length-1;n+=1){let o=t.getProbability(e[n],e[n+1]);s+=Math.log(o>0?o:r)}return s}getLikelyNextStates(t,e){let r=this.stateToIndex.get(t);if(r===void 0)return[];let s=this.rows.get(r);if(!s||s.total===0)return[];let n=[],o=this.smoothingAlpha===0?s.total:s.total+this.smoothingAlpha*this.stateToIndex.size;return s.toCounts.forEach((a,l)=>{let h=this.smoothingAlpha===0?a/o:(a+this.smoothingAlpha)/o;if(h>=e){let c=this.indexToState[l];c&&c!==""&&n.push({state:c,probability:h})}}),n.sort((a,l)=>l.probability-a.probability),n}rowTotal(t){let e=this.stateToIndex.get(t);return e===void 0?0:this.rows.get(e)?.total??0}stateCount(){return this.indexToState.length}totalTransitions(){let t=0;return this.rows.forEach(e=>{t+=e.total}),t}prune(){let t=this.stateToIndex.size;if(t<=this.maxStates)return;let e=[];this.stateToIndex.forEach(n=>{let o=this.rows.get(n);e.push({index:n,total:o?.total??0})}),e.sort((n,o)=>n.total-o.total);let r=Math.max(1,Math.min(Math.ceil(t*.2),t-this.maxStates)),s=new Set;for(let n=0;n<r&&n<e.length;n+=1)s.add(e[n].index);s.forEach(n=>{this.rows.delete(n)}),this.rows.forEach(n=>{let o=0;s.forEach(a=>{let l=n.toCounts.get(a);l!==void 0&&(o+=l,n.toCounts.delete(a))}),n.total-=o}),s.forEach(n=>{let o=this.indexToState[n];o!==void 0&&o!==""&&this.stateToIndex.delete(o),this.freedIndices.push(n),this.indexToState[n]=""})}toJSON(){let t=[];return this.rows.forEach((e,r)=>{let s=[];e.toCounts.forEach((n,o)=>{s.push([o,n])}),t.push([r,e.total,s])}),{states:[...this.indexToState],rows:t,freedIndices:[...this.freedIndices]}}static fromJSON(t,e={}){let r=new i(e),s=new Set(t.freedIndices);for(let n=0;n<t.states.length;n+=1){let o=t.states[n];if(r.indexToState.push(o),s.has(n)){if(o!=="")throw new Error(`MarkovGraph.fromJSON: slot ${n} is listed in freedIndices but has non-empty label "${o}"`);r.freedIndices.push(n)}else{if(o==="")throw new Error(`MarkovGraph.fromJSON: slot ${n} has an empty-string label but is not listed in freedIndices. Empty string is reserved as the tombstone marker.`);r.stateToIndex.set(o,n)}}for(let n=0;n<t.rows.length;n+=1){let[o,a,l]=t.rows[n],h={total:a,toCounts:new Map};for(let c=0;c<l.length;c+=1){let[g,p]=l[c];h.toCounts.set(g,p)}r.rows.set(o,h)}return r}toBinary(){let t=new TextEncoder,e=3,r=new Array(this.indexToState.length);for(let a=0;a<this.indexToState.length;a+=1)r[a]=t.encode(this.indexToState[a]),e+=2+r[a].byteLength;e+=2+this.freedIndices.length*2,e+=2,this.rows.forEach(a=>{e+=8,e+=a.toCounts.size*6});let s=new Uint8Array(e),n=new DataView(s.buffer),o=0;n.setUint8(o,2),o+=1,n.setUint16(o,this.indexToState.length,!0),o+=2;for(let a=0;a<this.indexToState.length;a+=1){let l=r[a];n.setUint16(o,l.byteLength,!0),o+=2,s.set(l,o),o+=l.byteLength}n.setUint16(o,this.freedIndices.length,!0),o+=2;for(let a=0;a<this.freedIndices.length;a+=1)n.setUint16(o,this.freedIndices[a],!0),o+=2;return n.setUint16(o,this.rows.size,!0),o+=2,this.rows.forEach((a,l)=>{n.setUint16(o,l,!0),o+=2,n.setUint32(o,a.total,!0),o+=4,n.setUint16(o,a.toCounts.size,!0),o+=2,a.toCounts.forEach((h,c)=>{n.setUint16(o,c,!0),o+=2,n.setUint32(o,h,!0),o+=4})}),s}static fromBinary(t,e={}){let r=new i(e),s=new TextDecoder,n=new DataView(t.buffer,t.byteOffset,t.byteLength),o=0,a=n.getUint8(o);if(o+=1,a!==2)throw new Error(`Unsupported MarkovGraph binary version: 0x${a.toString(16).padStart(2,"0")}. Only version 0x02 is supported.`);let l=n.getUint16(o,!0);o+=2;let h=[];for(let m=0;m<l;m+=1){let d=n.getUint16(o,!0);o+=2,h.push(s.decode(t.subarray(o,o+d))),o+=d}let c=n.getUint16(o,!0);o+=2;let g=new Set;for(let m=0;m<c;m+=1)g.add(n.getUint16(o,!0)),o+=2;for(let m=0;m<h.length;m+=1){let d=h[m];if(r.indexToState.push(d),g.has(m)){if(d!=="")throw new Error(`MarkovGraph.fromBinary: slot ${m} is listed as freed but has non-empty label "${d}"`);r.freedIndices.push(m)}else{if(d==="")throw new Error(`MarkovGraph.fromBinary: slot ${m} has an empty-string label but is not listed in the freed-index section. Empty string is reserved as the tombstone marker.`);r.stateToIndex.set(d,m)}}let p=n.getUint16(o,!0);o+=2;for(let m=0;m<p;m+=1){let d=n.getUint16(o,!0);o+=2;let Q=n.getUint32(o,!0);o+=4;let _=n.getUint16(o,!0);o+=2;let O=new Map;for(let M=0;M<_;M+=1){let Y=n.getUint16(o,!0);o+=2;let tt=n.getUint32(o,!0);o+=4,O.set(Y,tt)}r.rows.set(d,{total:Q,toCounts:O})}return r}};function I(){return{count:0,totalMs:0,maxMs:0,samples:[]}}function At(i,t,e){i.count+=1,i.totalMs+=t,t>i.maxMs&&(i.maxMs=t),i.samples.length<e?i.samples.push(t):e>0&&(i.samples[i.count%e]=t)}function ht(i,t){if(i.length===0)return 0;let e=Math.max(0,Math.min(i.length-1,Math.ceil(t*i.length)-1));return i[e]}function x(i){if(i.count===0)return{count:0,avgMs:0,p95Ms:0,p99Ms:0,maxMs:0};let t=[...i.samples].sort((e,r)=>e-r);return{count:i.count,avgMs:i.totalMs/i.count,p95Ms:ht(t,.95),p99Ms:ht(t,.99),maxMs:i.maxMs}}var mt=typeof TextEncoder<"u"?new TextEncoder:null,f=class{constructor(t={}){this.enabled=t.enabled??!1,this.maxSamples=t.maxSamples??4096,this.stats={track:I(),bloomAdd:I(),bloomCheck:I(),incrementTransition:I(),entropyComputation:I(),divergenceComputation:I()}}now(){return this.enabled?performance.now():0}record(t,e){this.enabled&&At(this.stats[t],performance.now()-e,this.maxSamples)}report(t){return{benchmarkEnabled:this.enabled,track:x(this.stats.track),bloomAdd:x(this.stats.bloomAdd),bloomCheck:x(this.stats.bloomCheck),incrementTransition:x(this.stats.incrementTransition),entropyComputation:x(this.stats.entropyComputation),divergenceComputation:x(this.stats.divergenceComputation),memoryFootprint:t}}serializedSizeBytes(t){let e=JSON.stringify(t);return mt?mt.encode(e).byteLength:e.length}};var k=class{getItem(t){if(typeof window>"u"||!window.localStorage)return null;try{return window.localStorage.getItem(t)}catch{return null}}setItem(t,e){typeof window>"u"||!window.localStorage||window.localStorage.setItem(t,e)}},P=class{setTimeout(t,e){return typeof globalThis.setTimeout!="function"?0:globalThis.setTimeout(t,e)}clearTimeout(t){typeof globalThis.clearTimeout=="function"&&globalThis.clearTimeout(t)}now(){return typeof globalThis.performance<"u"&&typeof globalThis.performance.now=="function"?globalThis.performance.now():Date.now()}},j=class{constructor(){this.store=new Map}getItem(t){return this.store.get(t)??null}setItem(t,e){this.store.set(t,e)}},b=class b{constructor(){this.pauseCallbacks=[];this.resumeCallbacks=[];this.interactionCallbacks=[];this.exitIntentCallbacks=[];this.interactionHandler=null;this.interactionLastFired=0;this.exitIntentHandler=null;this.handler=()=>{if(!(typeof document>"u"))if(document.hidden)for(let t of this.pauseCallbacks)t();else for(let t of this.resumeCallbacks)t()},typeof document<"u"&&document.addEventListener("visibilitychange",this.handler)}onPause(t){return this.pauseCallbacks.push(t),()=>{let e=this.pauseCallbacks.indexOf(t);e!==-1&&this.pauseCallbacks.splice(e,1)}}onResume(t){return this.resumeCallbacks.push(t),()=>{let e=this.resumeCallbacks.indexOf(t);e!==-1&&this.resumeCallbacks.splice(e,1)}}onInteraction(t){if(typeof window>"u"||typeof window.addEventListener!="function")return null;if(this.interactionCallbacks.push(t),this.interactionHandler===null&&typeof window<"u"&&typeof window.addEventListener=="function"){this.interactionHandler=()=>{let r=typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now();if(!(r-this.interactionLastFired<b.INTERACTION_THROTTLE_MS)){this.interactionLastFired=r;for(let s of this.interactionCallbacks)s()}};let e={passive:!0};for(let r of b.INTERACTION_EVENTS)window.addEventListener(r,this.interactionHandler,e)}return()=>{let e=this.interactionCallbacks.indexOf(t);e!==-1&&this.interactionCallbacks.splice(e,1),this.interactionCallbacks.length===0&&this.teardownInteractionListeners()}}teardownInteractionListeners(){if(this.interactionHandler!==null&&typeof window<"u"&&typeof window.removeEventListener=="function")for(let t of b.INTERACTION_EVENTS)window.removeEventListener(t,this.interactionHandler);this.interactionHandler=null}onExitIntent(t){if(this.exitIntentCallbacks.push(t),this.exitIntentHandler===null){let e=typeof document<"u"?document?.documentElement:null;e!=null&&(this.exitIntentHandler=r=>{if(r.clientY<=0)for(let s of this.exitIntentCallbacks)s()},e.addEventListener("mouseleave",this.exitIntentHandler))}return()=>{let e=this.exitIntentCallbacks.indexOf(t);e!==-1&&this.exitIntentCallbacks.splice(e,1),this.exitIntentCallbacks.length===0&&this.teardownExitIntentListener()}}teardownExitIntentListener(){if(this.exitIntentHandler!==null){let t=typeof document<"u"?document?.documentElement:null;t?.removeEventListener("mouseleave",this.exitIntentHandler)}this.exitIntentHandler=null}destroy(){typeof document<"u"&&document.removeEventListener("visibilitychange",this.handler),this.teardownInteractionListeners(),this.teardownExitIntentListener(),this.pauseCallbacks.length=0,this.resumeCallbacks.length=0,this.interactionCallbacks.length=0,this.exitIntentCallbacks.length=0}};b.INTERACTION_THROTTLE_MS=1e3,b.INTERACTION_EVENTS=["mousemove","scroll","touchstart","keydown"];var D=b;var Mt=/[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/gi,Ct=/\b[0-9a-f]{24}\b/gi,It=/\/\d{4,}(?=\/|$)/g;function L(i){let t=i.indexOf("?"),e=t!==-1?i.slice(0,t):i,r=e.indexOf("#");return r!==-1&&(e=e.slice(0,r)),e=e.replace(Mt,":id").replace(Ct,":id").replace(It,"/:id"),e.length>1&&e.endsWith("/")&&(e=e.slice(0,-1)),e}var v=18e5,U=15e3,B=12e4,F=5e3;function ct(i={}){let t=i.botProtection??!0,e=i.dwellTime?.enabled??!1,r=i.crossTabSync===!0,s=i.holdoutConfig?.percentage,n=Number.isFinite(s)?Math.min(100,Math.max(0,s)):s??0,o={...i.graph,baselineMeanLL:i.baselineMeanLL??i.graph?.baselineMeanLL,baselineStdLL:i.baselineStdLL??i.graph?.baselineStdLL,smoothingAlpha:i.smoothingAlpha??i.graph?.smoothingAlpha},a=o.smoothingEpsilon,l=typeof a=="number"&&Number.isFinite(a)&&a>0?a:.01,h=i.storageKey??"passive-intent",c=i.persistDebounceMs,g=Number.isFinite(c)&&c>=0?Math.floor(c):2e3,p=i.persistThrottleMs,m=Number.isFinite(p)&&p>=0?Math.floor(p):0,d=i.eventCooldownMs,Q=Number.isFinite(d)&&d>=0?Math.floor(d):0,_=i.dwellTime?.minSamples,O=Number.isFinite(_)&&_>=1?Math.floor(_):10,M=i.dwellTime?.zScoreThreshold,Y=Number.isFinite(M)&&M>0?M:2.5,tt=i.enableBigrams??!1,et=i.bigramFrequencyThreshold,pt=Number.isFinite(et)&&et>=1?Math.floor(et):5,at=i.driftProtection?.maxAnomalyRate,yt=Number.isFinite(at)?Math.min(1,Math.max(0,at)):.4,rt=i.driftProtection?.evaluationWindowMs,gt=Number.isFinite(rt)&&rt>0?Math.floor(rt):3e5,nt=i.hesitationCorrelationWindowMs,ft=Number.isFinite(nt)&&nt>=0?Math.floor(nt):3e4;return{botProtection:t,dwellTimeEnabled:e,crossTabSync:r,holdoutPercent:n,graphConfig:o,trajectorySmoothingEpsilon:l,storageKey:h,persistDebounceMs:g,persistThrottleMs:m,eventCooldownMs:Q,dwellTimeMinSamples:O,dwellTimeZScoreThreshold:Y,enableBigrams:tt,bigramFrequencyThreshold:pt,driftMaxAnomalyRate:yt,driftEvaluationWindowMs:gt,hesitationCorrelationWindowMs:ft}}var T=class{constructor(){this.listeners=new Map}on(t,e){let r=this.listeners.get(t)??new Set;return r.add(e),this.listeners.set(t,r),()=>{r.delete(e),r.size===0&&this.listeners.delete(t)}}emit(t,e){let r=this.listeners.get(t);r&&r.forEach(s=>s(e))}removeAll(){this.listeners.clear()}};var z=class{constructor(){this.isSuspectedBot=!1;this.trackTimestamps=new Array(10).fill(0);this.trackTimestampIndex=0;this.trackTimestampCount=0}record(t){this.trackTimestamps[this.trackTimestampIndex]=t,this.trackTimestampIndex=(this.trackTimestampIndex+1)%10,this.trackTimestampCount<10&&(this.trackTimestampCount+=1);let e=this.isSuspectedBot;return this.evaluate(),{suspected:this.isSuspectedBot,transitionedToBot:this.isSuspectedBot&&!e}}get suspected(){return this.isSuspectedBot}evaluate(){let t=this.trackTimestampCount;if(t<3)return;let e=0,r=[],s=t<10?0:this.trackTimestampIndex;for(let n=0;n<t-1;n+=1){let o=(s+n)%10,a=(s+n+1)%10,l=this.trackTimestamps[a]-this.trackTimestamps[o];r.push(l),l>=0&&l<50&&(e+=1)}if(r.length>=2){let n=0;for(let a=0;a<r.length;a+=1)n+=r[a];n/=r.length;let o=0;for(let a=0;a<r.length;a+=1){let l=r[a]-n;o+=l*l}o/=r.length,o<100&&(e+=1)}this.isSuspectedBot=e>=5}};function dt(i,t){let e=i?.count??0,r=i?.meanMs??0,s=i?.m2??0,n=e+1,o=t-r,a=r+o/n,l=t-a,h=s+o*l;return{count:n,meanMs:a,m2:h}}function ut(i){return i.count<2?0:Math.sqrt(i.m2/i.count)}function kt(i){throw new Error(`Unhandled AnomalyDecision kind: ${i.kind}`)}var S=class{constructor(t){this.lastEmittedAt={high_entropy:-1/0,trajectory_anomaly:-1/0,dwell_time_anomaly:-1/0};this.lastTrajectoryAnomalyAt=-1/0;this.lastTrajectoryAnomalyZScore=0;this.lastDwellAnomalyAt=-1/0;this.lastDwellAnomalyZScore=0;this.lastDwellAnomalyState="";this.anomaliesFiredInternal=0;this.emitter=t.emitter,this.timer=t.timer,this.assignmentGroup=t.assignmentGroup,this.eventCooldownMs=t.eventCooldownMs,this.hesitationCorrelationWindowMs=t.hesitationCorrelationWindowMs,this.driftPolicy=t.driftPolicy}get anomaliesFired(){return this.anomaliesFiredInternal}dispatch(t){if(t===null)return;t.kind==="trajectory_anomaly"&&this.driftPolicy.recordAnomaly();let e=this.timer.now();if(!(this.eventCooldownMs>0&&e-this.lastEmittedAt[t.kind]<this.eventCooldownMs)){if(this.lastEmittedAt[t.kind]=e,this.anomaliesFiredInternal+=1,this.assignmentGroup!=="control")switch(t.kind){case"high_entropy":this.emitter.emit("high_entropy",t.payload);break;case"trajectory_anomaly":this.emitter.emit("trajectory_anomaly",t.payload);break;case"dwell_time_anomaly":this.emitter.emit("dwell_time_anomaly",t.payload);break;default:kt(t)}t.kind==="trajectory_anomaly"?(this.lastTrajectoryAnomalyAt=e,this.lastTrajectoryAnomalyZScore=t.payload.zScore,this.maybeEmitHesitation(e)):t.kind==="dwell_time_anomaly"&&t.isPositiveZScore&&(this.lastDwellAnomalyAt=e,this.lastDwellAnomalyZScore=t.payload.zScore,this.lastDwellAnomalyState=t.payload.state,this.maybeEmitHesitation(e))}}maybeEmitHesitation(t){t-this.lastTrajectoryAnomalyAt<this.hesitationCorrelationWindowMs&&t-this.lastDwellAnomalyAt<this.hesitationCorrelationWindowMs&&(this.lastTrajectoryAnomalyAt=-1/0,this.lastDwellAnomalyAt=-1/0,this.assignmentGroup!=="control"&&this.emitter.emit("hesitation_detected",{state:this.lastDwellAnomalyState,trajectoryZScore:this.lastTrajectoryAnomalyZScore,dwellZScore:this.lastDwellAnomalyZScore}))}};var E=class{constructor(t){this.entropyGuard=new z;this.dwellStats=new Map;this.transitionsEvaluatedInternal=0;this.graph=t.graph,this.baseline=t.baseline,this.benchmark=t.benchmark,this.dwellTimeMinSamples=t.dwellTimeMinSamples,this.dwellTimeZScoreThreshold=t.dwellTimeZScoreThreshold,this.trajectorySmoothingEpsilon=t.trajectorySmoothingEpsilon,this.driftPolicy=t.driftPolicy,this.dispatcher=new S({emitter:t.emitter,timer:t.timer,assignmentGroup:t.assignmentGroup,eventCooldownMs:t.eventCooldownMs,hesitationCorrelationWindowMs:t.hesitationCorrelationWindowMs,driftPolicy:t.driftPolicy})}get suspected(){return this.entropyGuard.suspected}get transitionsEvaluated(){return this.transitionsEvaluatedInternal}get anomaliesFired(){return this.dispatcher.anomaliesFired}get isBaselineDrifted(){return this.driftPolicy.isDrifted}get baselineStatus(){return this.driftPolicy.baselineStatus}recordBotCheck(t){return this.entropyGuard.record(t)}recordTransition(t,e,r){this.transitionsEvaluatedInternal+=1}dispatch(t){this.dispatcher.dispatch(t)}evaluateEntropy(t){let e=this.benchmark.now();if(this.entropyGuard.suspected)return this.benchmark.record("entropyComputation",e),null;if(this.graph.rowTotal(t)<10)return this.benchmark.record("entropyComputation",e),null;let r=this.graph.entropyForState(t),s=this.graph.normalizedEntropyForState(t);return this.benchmark.record("entropyComputation",e),s>=this.graph.highEntropyThreshold?{kind:"high_entropy",payload:{state:t,entropy:r,normalizedEntropy:s}}:null}evaluateTrajectory(t,e,r){let s=this.benchmark.now();if(this.driftPolicy.isDrifted)return this.benchmark.record("divergenceComputation",s),null;if(this.entropyGuard.suspected)return this.benchmark.record("divergenceComputation",s),null;if(r.length<16)return this.benchmark.record("divergenceComputation",s),null;if(!this.baseline)return this.benchmark.record("divergenceComputation",s),null;let n=u.logLikelihoodTrajectory(this.graph,r,this.trajectorySmoothingEpsilon),o=u.logLikelihoodTrajectory(this.baseline,r,this.trajectorySmoothingEpsilon),a=Math.max(1,r.length-1),l=o/a,h=-Math.abs(this.graph.divergenceThreshold),c=typeof this.graph.baselineMeanLL=="number"&&typeof this.graph.baselineStdLL=="number"&&Number.isFinite(this.graph.baselineMeanLL)&&Number.isFinite(this.graph.baselineStdLL)&&this.graph.baselineStdLL>0,g=c?this.graph.baselineStdLL*Math.sqrt(32/a):0,p=c?(l-this.graph.baselineMeanLL)/g:l,m=c?p<=h:l<=h;return this.benchmark.record("divergenceComputation",s),m?{kind:"trajectory_anomaly",payload:{stateFrom:t,stateTo:e,realLogLikelihood:n,expectedBaselineLogLikelihood:o,zScore:p}}:null}evaluateDwellTime(t,e){if(e<=0)return null;let r=dt(this.dwellStats.get(t),e);if(this.dwellStats.set(t,r),r.count<this.dwellTimeMinSamples)return null;let s=ut(r);if(s<=0)return null;let n=(e-r.meanMs)/s;return Math.abs(n)>=this.dwellTimeZScoreThreshold?{kind:"dwell_time_anomaly",payload:{state:t,dwellMs:e,meanMs:r.meanMs,stdMs:s,zScore:n},isPositiveZScore:n>0}:null}};var W=class{constructor(t){this.ctx=t;this.lastPersistedAt=-1/0;this.throttleTimer=null;this.isClosedFlag=!1}serialize(){let t=this.ctx.getGraphAndBloom();if(!t)return null;let{graph:e,bloom:r}=t;this.ctx.setEngineHealth("pruning_active");try{e.prune()}finally{this.ctx.setEngineHealth("healthy")}let s;try{let n=e.toBinary();s=R(n)}catch(n){return this.ctx.reportError("SERIALIZE",n instanceof Error?n.message:String(n),n),null}return JSON.stringify({bloomBase64:r.toBase64(),graphBinary:s})}flushNow(){this.throttleTimer!==null&&(this.ctx.getTimer().clearTimeout(this.throttleTimer),this.throttleTimer=null),this.lastPersistedAt=-1/0,this.persist()}close(){this.isClosedFlag=!0,this.throttleTimer!==null&&(this.ctx.getTimer().clearTimeout(this.throttleTimer),this.throttleTimer=null)}checkThrottle(){let t=this.ctx.getThrottleMs();if(t>0&&!this.isClosedFlag){let r=this.ctx.getTimer().now()-this.lastPersistedAt;if(r<t){if(this.throttleTimer===null){let s=t-r;this.throttleTimer=this.ctx.getTimer().setTimeout(()=>{this.throttleTimer=null,this.persist()},s)}return!0}}return!1}},K=class extends W{persist(){if(!this.ctx.isDirty()||!this.ctx.getGraphAndBloom()||this.checkThrottle())return;let t=this.serialize();if(t)try{this.ctx.getStorage().setItem(this.ctx.getStorageKey(),t),this.ctx.clearDirty(),this.lastPersistedAt=this.ctx.getTimer().now(),this.ctx.setEngineHealth("healthy")}catch(e){let r=e instanceof Error&&(e.name==="QuotaExceededError"||e.message.toLowerCase().includes("quota"));r&&this.ctx.setEngineHealth("quota_exceeded"),this.ctx.reportError(r?"QUOTA_EXCEEDED":"STORAGE_WRITE",e instanceof Error?e.message:String(e),e)}}},Z=class extends W{constructor(){super(...arguments);this.isAsyncWriting=!1;this.hasPendingAsyncPersist=!1;this.asyncWriteFailCount=0;this.retryTimer=null}persist(){if(!this.ctx.isDirty()||!this.ctx.getGraphAndBloom())return;if(this.isAsyncWriting){this.hasPendingAsyncPersist=!0;return}if(this.checkThrottle())return;let e=this.serialize();if(!e)return;this.isAsyncWriting=!0,this.hasPendingAsyncPersist=!1,this.ctx.clearDirty();let r=this.ctx.getAsyncStorage(),s;try{s=r.setItem(this.ctx.getStorageKey(),e)}catch(n){this.handleAsyncWriteError(n);return}s.then(()=>{this.isAsyncWriting=!1,this.asyncWriteFailCount=0,this.lastPersistedAt=this.ctx.getTimer().now(),this.ctx.setEngineHealth("healthy"),(this.hasPendingAsyncPersist||this.ctx.isDirty())&&(this.hasPendingAsyncPersist=!1,this.persist())}).catch(n=>{this.handleAsyncWriteError(n)})}handleAsyncWriteError(e){this.isAsyncWriting=!1,this.ctx.markDirty(),this.asyncWriteFailCount+=1;let r=e instanceof Error&&(e.name==="QuotaExceededError"||e.message.toLowerCase().includes("quota"));r&&this.ctx.setEngineHealth("quota_exceeded"),this.ctx.reportError(r?"QUOTA_EXCEEDED":"STORAGE_WRITE",e instanceof Error?e.message:String(e),e),this.hasPendingAsyncPersist=!1,!this.isClosedFlag&&this.asyncWriteFailCount===1&&this.schedulePersist()}schedulePersist(){this.isClosedFlag||(this.retryTimer!==null&&this.ctx.getTimer().clearTimeout(this.retryTimer),this.retryTimer=this.ctx.getTimer().setTimeout(()=>{this.retryTimer=null,this.persist()},this.ctx.getDebounceMs()))}flushNow(){this.retryTimer!==null&&(this.ctx.getTimer().clearTimeout(this.retryTimer),this.retryTimer=null),super.flushNow()}close(){super.close(),this.retryTimer!==null&&(this.ctx.getTimer().clearTimeout(this.retryTimer),this.retryTimer=null)}};var X=class{constructor(t){this.graphInstance=null;this.bloomInstance=null;this.isClosedFlag=!1;this.isDirtyFlag=!1;this.engineHealthInternal="healthy";this.storageKey=t.storageKey,this.persistDebounceMs=t.persistDebounceMs,this.persistThrottleMs=t.persistThrottleMs,this.storage=t.storage,this.asyncStorage=t.asyncStorage,this.timer=t.timer,this.onErrorCb=t.onError,this.asyncStorage?this.strategy=new Z(this):this.strategy=new K(this)}markDirty(){this.isDirtyFlag=!0}getStorageKey(){return this.storageKey}getStorage(){return this.storage}getAsyncStorage(){return this.asyncStorage}getTimer(){return this.timer}getThrottleMs(){return this.persistThrottleMs}getDebounceMs(){return this.persistDebounceMs}getGraphAndBloom(){return!this.graphInstance||!this.bloomInstance?null:{graph:this.graphInstance,bloom:this.bloomInstance}}isClosed(){return this.isClosedFlag}isDirty(){return this.isDirtyFlag}clearDirty(){this.isDirtyFlag=!1}setEngineHealth(t){this.engineHealthInternal=t}reportError(t,e,r){this.onErrorCb&&this.onErrorCb({code:t,message:e,originalError:r})}get engineHealth(){return this.engineHealthInternal}attach(t,e){this.graphInstance=t,this.bloomInstance=e}restore(t){let e;try{e=this.storage.getItem(this.storageKey)}catch(r){return this.onErrorCb&&this.onErrorCb({code:"STORAGE_READ",message:r instanceof Error?r.message:String(r),originalError:r}),null}if(!e)return null;try{let r=JSON.parse(e),s;if(r.graphBinary){let o=H(r.graphBinary);s=u.fromBinary(o,t)}else if(r.graph)s=u.fromJSON(r.graph,t);else return null;return{bloom:r.bloomBase64?y.fromBase64(r.bloomBase64):new y,graph:s}}catch(r){if(this.onErrorCb){let s=typeof TextEncoder<"u"?new TextEncoder().encode(e).length:e.length;this.onErrorCb({code:"RESTORE_PARSE",message:r instanceof Error?r.message:String(r),originalError:{cause:r,payloadLength:s}})}return null}}persist(){this.strategy.persist()}flushNow(){this.strategy.flushNow()}close(){this.isClosedFlag=!0,this.strategy.close()}};var V=class{constructor(t){this.pauseUnsub=null;this.resumeUnsub=null;this.exitIntentUnsub=null;this.tabHiddenAt=null;this.idleStartedAt=0;this.isIdle=!1;this.idleCheckTimer=null;this.interactionUnsub=null;this.timer=t.timer,this.dwellTimeEnabled=t.dwellTimeEnabled,this.emitter=t.emitter,this.onAdjustBaseline=t.onAdjustBaseline,this.onResetBaseline=t.onResetBaseline,this.hasPreviousState=t.hasPreviousState,this.getPreviousState=t.getPreviousState,this.lastInteractionAt=this.timer.now(),this.onExitIntentCallback=t.onExitIntent,t.lifecycleAdapter!==void 0?(this.lifecycleAdapter=t.lifecycleAdapter,this.ownsLifecycleAdapter=!1):(this.lifecycleAdapter=typeof window<"u"?new D:null,this.ownsLifecycleAdapter=!0),this.bindAdapter(this.lifecycleAdapter)}bindAdapter(t){this.pauseUnsub?.(),this.pauseUnsub=null,this.resumeUnsub?.(),this.resumeUnsub=null,this.exitIntentUnsub?.(),this.exitIntentUnsub=null,this.stopIdleTracking(),t&&(this.pauseUnsub=t.onPause(()=>{this.tabHiddenAt=this.timer.now()}),this.resumeUnsub=t.onResume(()=>{if(this.tabHiddenAt===null)return;let e=this.timer.now()-this.tabHiddenAt;if(this.tabHiddenAt=null,e>=15e3){let r=this.getPreviousState();r!==null&&this.emitter.emit("attention_return",{state:r,hiddenDuration:e})}if(this.dwellTimeEnabled){if(e>18e5){this.hasPreviousState()&&(this.onResetBaseline(),this.emitter.emit("session_stale",{reason:"hidden_duration_exceeded",measuredMs:e,thresholdMs:18e5}));return}this.hasPreviousState()&&this.onAdjustBaseline(e)}}),this.startIdleTracking(t),this.onExitIntentCallback!==void 0&&typeof t.onExitIntent=="function"&&(this.exitIntentUnsub=t.onExitIntent(()=>{this.onExitIntentCallback()})))}setAdapterForTest(t,e){this.ownsLifecycleAdapter&&this.lifecycleAdapter?.destroy(),this.lifecycleAdapter=t,this.ownsLifecycleAdapter=e,this.tabHiddenAt=null,this.isIdle=!1,this.lastInteractionAt=this.timer.now(),this.exitIntentUnsub?.(),this.exitIntentUnsub=null,this.bindAdapter(t)}destroy(){this.stopIdleTracking(),this.pauseUnsub?.(),this.pauseUnsub=null,this.resumeUnsub?.(),this.resumeUnsub=null,this.exitIntentUnsub?.(),this.exitIntentUnsub=null,this.ownsLifecycleAdapter&&this.lifecycleAdapter?.destroy()}startIdleTracking(t){if(!t||typeof t.onInteraction!="function")return;let e=t.onInteraction(()=>{if(this.lastInteractionAt=this.timer.now(),this.isIdle){let r=this.timer.now()-this.idleStartedAt;this.isIdle=!1,this.dwellTimeEnabled&&this.hasPreviousState()&&this.onAdjustBaseline(r);let s=this.getPreviousState();s!==null&&this.emitter.emit("user_resumed",{state:s,idleMs:r})}});e&&(this.interactionUnsub=e,this.scheduleIdleCheck())}stopIdleTracking(){this.interactionUnsub?.(),this.interactionUnsub=null,this.idleCheckTimer!==null&&(this.timer.clearTimeout(this.idleCheckTimer),this.idleCheckTimer=null)}scheduleIdleCheck(){this.idleCheckTimer=this.timer.setTimeout(()=>{this.idleCheckTick()},5e3)}idleCheckTick(){this.idleCheckTimer=null;let t=this.timer.now();if(!this.isIdle&&this.hasPreviousState()&&t-this.lastInteractionAt>=12e4){this.isIdle=!0,this.idleStartedAt=this.lastInteractionAt+12e4;let e=this.getPreviousState();e!==null&&this.emitter.emit("user_idle",{state:e,idleMs:t-this.idleStartedAt})}this.interactionUnsub!==null&&this.scheduleIdleCheck()}};var $=class{constructor(t){this.isSuspected=t.isSuspected,this.evaluateDwellTime=t.evaluateDwellTime,this.getPreviousStateEnteredAt=t.getPreviousStateEnteredAt,this.emitter=t.emitter}onTrackContext(t){if(!t.from||this.isSuspected())return;let e=t.now-this.getPreviousStateEnteredAt();e>18e5?this.emitter.emit("session_stale",{reason:"dwell_exceeded",measuredMs:e,thresholdMs:18e5}):this.evaluateDwellTime(t.from,e)}};var q=class{constructor(t,e){this.graph=t,this.bigramFrequencyThreshold=e}onTransition(t,e,r){if(r.length<3)return;let n=`${r[r.length-3]}\u2192${t}`,o=`${t}\u2192${e}`;this.graph.rowTotal(t)>=this.bigramFrequencyThreshold&&this.graph.incrementTransition(n,o)}};var w=class{constructor(t,e){this.drifted=!1;this.windowStart=0;this.windowTrackCount=0;this.windowAnomalyCount=0;this.maxAnomalyRate=t,this.evaluationWindowMs=e}onTrackStart(t){t-this.windowStart>=this.evaluationWindowMs&&(this.windowStart=t,this.windowTrackCount=0,this.windowAnomalyCount=0),this.windowTrackCount+=1}get isDrifted(){return this.drifted}get baselineStatus(){return this.drifted?"drifted":"active"}recordAnomaly(){this.windowAnomalyCount+=1,this.windowTrackCount>0&&this.windowAnomalyCount/this.windowTrackCount>this.maxAnomalyRate&&(this.drifted=!0)}};var st=256;function Dt(i){return!(i.type!=="transition"||typeof i.from!="string"||typeof i.to!="string"||i.from.length===0||i.to.length===0||i.from.length>256||i.to.length>256)}function _t(i){return!(i.type!=="counter"||typeof i.key!="string"||i.key.length===0||i.key.length>256||typeof i.by!="number"||!Number.isFinite(i.by))}var A=class{constructor(t,e,r,s=new Map){if(this.graph=e,this.bloom=r,this.counters=s,typeof BroadcastChannel>"u"){this.channel=null,this.isActive=!1;return}this.channel=new BroadcastChannel(t),this.isActive=!0,this.channel.onmessage=n=>{this.handleMessage(n.data)}}broadcast(t,e){if(!this.channel)return;let r={type:"transition",from:t,to:e};this.channel.postMessage(r)}broadcastCounter(t,e){if(!this.channel)return;let r={type:"counter",key:t,by:e};this.channel.postMessage(r)}applyRemote(t,e){this.bloom.add(t),this.bloom.add(e),this.graph.incrementTransition(t,e)}applyRemoteCounter(t,e){if(!this.counters.has(t)&&this.counters.size>=50)return;let r=this.counters.get(t)??0;this.counters.set(t,r+e)}close(){this.channel&&(this.channel.onmessage=null,this.channel.close())}handleMessage(t){if(typeof t!="object"||t===null)return;let e=t;if(e.type==="transition"){if(!Dt(e))return;this.applyRemote(e.from,e.to)}else if(e.type==="counter"){if(!_t(e))return;this.applyRemoteCounter(e.key,e.by)}}};var J=class{constructor(t){this.broadcastSync=new A(t.channelName,t.graph,t.bloom,t.counters),this.isSuspected=t.isSuspected}onAfterEvaluation(t,e){this.isSuspected()||this.broadcastSync.broadcast(t,e)}onCounterIncrement(t,e){this.isSuspected()||this.broadcastSync.broadcastCounter(t,e)}destroy(){this.broadcastSync.close()}};var N=class i{constructor(t={}){this.emitter=new T;this.previousState=null;this.previousStateEnteredAt=0;this.recentTrajectory=[];this.counters=new Map;this.runBotProtectionStage=t=>{if(!this.botProtection)return;this.signalEngine.recordBotCheck(t.now).transitionedToBot&&this.emitter.emit("bot_detected",{state:t.state})};this.runBloomStage=t=>{t.isNewToBloom=!this.bloom.check(t.state);let e=this.benchmark.now();this.bloom.add(t.state),this.benchmark.record("bloomAdd",e)};this.runTransitionContextStage=t=>{t.from=this.previousState,this.previousState=t.state;for(let e=0;e<this.policies.length;e+=1)this.policies[e].onTrackContext?.(t);this.previousStateEnteredAt=t.now,this.recentTrajectory.push(t.state),this.recentTrajectory.length>32&&this.recentTrajectory.shift()};this.runGraphAndSignalStage=t=>{if(this.botProtection&&this.signalEngine.suspected){t.isNewToBloom&&this.persistenceCoordinator.markDirty();return}if(t.from){let e=this.benchmark.now();this.graph.incrementTransition(t.from,t.state),this.benchmark.record("incrementTransition",e),this.signalEngine.recordTransition(t.from,t.state,this.recentTrajectory);for(let r=0;r<this.policies.length;r+=1)this.policies[r].onTransition?.(t.from,t.state,this.recentTrajectory);this.persistenceCoordinator.markDirty(),this.signalEngine.dispatch(this.signalEngine.evaluateEntropy(t.state)),this.signalEngine.dispatch(this.signalEngine.evaluateTrajectory(t.from,t.state,this.recentTrajectory));for(let r=0;r<this.policies.length;r+=1)this.policies[r].onAfterEvaluation?.(t.from,t.state);return}t.isNewToBloom&&this.persistenceCoordinator.markDirty()};this.runEmitAndPersistStage=t=>{this.emitter.emit("state_change",{from:t.from,to:t.state}),this.persistenceCoordinator.persist()};let e=ct(t);this.benchmark=new f(t.benchmark),this.timer=t.timer??new P,this.onError=t.onError,this.botProtection=e.botProtection,this.stateNormalizer=t.stateNormalizer,this.sessionId=typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():Math.random().toString(36).slice(2)+Math.random().toString(36).slice(2),this.assignmentGroup=Math.random()*100<e.holdoutPercent?"control":"treatment";let r=new X({storageKey:e.storageKey,persistDebounceMs:e.persistDebounceMs,persistThrottleMs:e.persistThrottleMs,storage:t.storage??new k,asyncStorage:t.asyncStorage??null,timer:this.timer,onError:t.onError});this.persistenceCoordinator=r;let s=r.restore(e.graphConfig);this.bloom=s?.bloom??new y(t.bloom),this.graph=s?.graph??new u(e.graphConfig),this.baseline=t.baseline?u.fromJSON(t.baseline,e.graphConfig):null,r.attach(this.graph,this.bloom);let n=new w(e.driftMaxAnomalyRate,e.driftEvaluationWindowMs),o=[n];this.signalEngine=new E({graph:this.graph,baseline:this.baseline,timer:this.timer,benchmark:this.benchmark,emitter:this.emitter,assignmentGroup:this.assignmentGroup,eventCooldownMs:e.eventCooldownMs,dwellTimeMinSamples:e.dwellTimeMinSamples,dwellTimeZScoreThreshold:e.dwellTimeZScoreThreshold,hesitationCorrelationWindowMs:e.hesitationCorrelationWindowMs,trajectorySmoothingEpsilon:e.trajectorySmoothingEpsilon,driftPolicy:n}),e.dwellTimeEnabled&&o.push(new $({isSuspected:()=>this.signalEngine.suspected,evaluateDwellTime:(a,l)=>{this.signalEngine.dispatch(this.signalEngine.evaluateDwellTime(a,l))},getPreviousStateEnteredAt:()=>this.previousStateEnteredAt,emitter:this.emitter})),e.enableBigrams&&o.push(new q(this.graph,e.bigramFrequencyThreshold)),e.crossTabSync&&o.push(new J({channelName:`passiveintent-sync:${e.storageKey}`,graph:this.graph,bloom:this.bloom,counters:this.counters,isSuspected:()=>this.signalEngine.suspected})),this.policies=o,this.lifecycleCoordinator=new V({lifecycleAdapter:t.lifecycleAdapter,timer:this.timer,dwellTimeEnabled:e.dwellTimeEnabled,emitter:this.emitter,onAdjustBaseline:a=>{this.previousStateEnteredAt+=a},onResetBaseline:()=>{this.previousStateEnteredAt=this.timer.now()},hasPreviousState:()=>this.previousState!==null,getPreviousState:()=>this.previousState,onExitIntent:()=>{if(this.previousState===null)return;let a=this.graph.getLikelyNextStates(this.previousState,.4);a.length!==0&&this.emitter.emit("exit_intent",{state:this.previousState,likelyNext:a[0].state})}}),this.trackStages=[this.runBotProtectionStage,this.runBloomStage,this.runTransitionContextStage,this.runGraphAndSignalStage,this.runEmitAndPersistStage]}on(t,e){return this.emitter.on(t,e)}static async createAsync(t){if(!t.asyncStorage)throw new Error("IntentManager.createAsync() requires config.asyncStorage");let e=t.storageKey??"passive-intent",r=await t.asyncStorage.getItem(e),s={getItem:()=>r,setItem:()=>{}},{storage:n,...o}=t;return new i({...o,storage:s})}track(t){if(t=L(t),this.stateNormalizer)try{let n=this.stateNormalizer(t),o=String(n);if(o==="")return;t=o}catch(n){this.onError&&this.onError({code:"VALIDATION",message:`IntentManager.track(): stateNormalizer threw: ${n instanceof Error?n.message:String(n)}`});return}if(t===""){this.onError&&this.onError({code:"VALIDATION",message:"IntentManager.track(): state label must not be an empty string"});return}let e=this.timer.now(),r=this.benchmark.now();for(let n=0;n<this.policies.length;n+=1)this.policies[n].onTrackStart?.(e);let s={state:t,now:e,trackStart:r,from:null,isNewToBloom:!1};for(let n=0;n<this.trackStages.length;n+=1)this.trackStages[n](s);this.benchmark.record("track",r)}get _previousStateEnteredAt(){return this.previousStateEnteredAt}hasSeen(t){let e=this.benchmark.now(),r=this.bloom.check(t);return this.benchmark.record("bloomCheck",e),r}resetSession(){this.recentTrajectory=[],this.previousState=null,this.previousStateEnteredAt=0}exportGraph(){return this.graph.toJSON()}predictNextStates(t=.3,e){if(this.previousState===null)return[];let r=this.graph.getLikelyNextStates(this.previousState,t);return e?r.filter(({state:s})=>e(s)):r}flushNow(){this.persistenceCoordinator.flushNow()}destroy(){this.persistenceCoordinator.flushNow(),this.persistenceCoordinator.close(),this.emitter.removeAll(),this.lifecycleCoordinator.destroy();for(let t=0;t<this.policies.length;t+=1)this.policies[t].destroy?.()}getTelemetry(){return{sessionId:this.sessionId,transitionsEvaluated:this.signalEngine.transitionsEvaluated,botStatus:this.signalEngine.suspected?"suspected_bot":"human",anomaliesFired:this.signalEngine.anomaliesFired,engineHealth:this.persistenceCoordinator.engineHealth,baselineStatus:this.signalEngine.baselineStatus,assignmentGroup:this.assignmentGroup}}trackConversion(t){this.emitter.emit("conversion",t)}incrementCounter(t,e=1){if(t==="")return this.onError&&this.onError({code:"VALIDATION",message:"IntentManager.incrementCounter(): key must not be an empty string"}),0;if(!Number.isFinite(e))return this.onError&&this.onError({code:"VALIDATION",message:`IntentManager.incrementCounter(): 'by' must be a finite number, got ${e}`}),this.counters.get(t)??0;if(!this.counters.has(t)&&this.counters.size>=50)return this.onError&&this.onError({code:"LIMIT_EXCEEDED",message:"IntentManager.incrementCounter(): max unique counter keys (50) reached"}),0;let r=(this.counters.get(t)??0)+e;this.counters.set(t,r);for(let s=0;s<this.policies.length;s+=1)this.policies[s].onCounterIncrement?.(t,e);return r}getCounter(t){return this.counters.get(t)??0}resetCounter(t){this.counters.delete(t)}getPerformanceReport(){let t=this.graph.toJSON();return this.benchmark.report({stateCount:this.graph.stateCount(),totalTransitions:this.graph.totalTransitions(),bloomBitsetBytes:this.bloom.getBitsetByteSize(),serializedGraphBytes:this.benchmark.serializedSizeBytes(t)})}};0&&(module.exports={ATTENTION_RETURN_THRESHOLD_MS,AnomalyDispatcher,BenchmarkRecorder,BloomFilter,BroadcastSync,BrowserLifecycleAdapter,BrowserStorageAdapter,BrowserTimerAdapter,DriftProtectionPolicy,EventEmitter,IDLE_CHECK_INTERVAL_MS,IntentManager,MAX_PLAUSIBLE_DWELL_MS,MAX_STATE_LENGTH,MarkovGraph,MemoryStorageAdapter,SignalEngine,USER_IDLE_THRESHOLD_MS,computeBloomConfig,normalizeRouteState});
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|