@sentientui/policy 0.9.0 → 0.10.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/README.md CHANGED
@@ -30,9 +30,11 @@ replayable decisions.
30
30
  - `validateSlotDecl(decl)` — structural validation of a `SlotDecl`, returning `{ ok: true }` or `{ ok: false, reason }`.
31
31
 
32
32
  **Layout selection** (`layout-heuristics.ts`, `choose-layout.ts`, `hash.ts`)
33
- - `candidateLayouts(sections, sectionTypes, persona)` — the candidate section orderings for a persona.
34
- - `applyClusterHeuristic(sections, sectionTypes, persona)` — the persona's heuristic ordering (`CLUSTER_PRIORITY`), used as the fallback.
35
- - `chooseLayout(sections, sectionTypes, persona, learned, rand?)` — Thompson-samples the learned layout posteriors over the candidates, falling back to the heuristic.
33
+ - `candidateLayouts(sections, sectionTypes, sectionRoles?)` — the candidate orderings for a PAGE: the authored order plus each archetype's. Takes no persona — the persona selects posteriors, not which layouts are reachable.
34
+ - `LAYOUT_ARCHETYPES` / `orderByArchetype(sections, sectionTypes, archetype, sectionRoles?)` — a catalogue of orderings (`conversion_led`, `evidence_led`, `price_led`, `discovery_led`). Not a persona taxonomy.
35
+ - `previewOrderForPersona(sections, sectionTypes, persona, sectionRoles?)` — **keyless local mode only.** Maps any persona key onto an archetype so `?sentient_persona=<anything>` visibly rearranges a page with no server. Never use it for serving.
36
+ - `chooseLayout(sections, sectionTypes, persona, learned, rand?)` — Thompson-samples the learned layout posteriors over the candidates. The authored order is always among them, so "leave the page alone" can win.
37
+ - *Deprecated:* `CLUSTER_PRIORITY` (use `LAYOUT_ARCHETYPES`), `applyClusterHeuristic` (use `orderByArchetype` on the server, `previewOrderForPersona` locally).
36
38
  - `hashLayout(order)` — stable hash of a section order (the `layoutHash` key).
37
39
 
38
40
  **Personas** (`personas.ts`)
package/dist/index.d.cts CHANGED
@@ -1,5 +1,19 @@
1
1
  import { SectionRole } from './taxonomy.cjs';
2
2
 
3
+ /**
4
+ * The four labels this product used to ship as a default vocabulary.
5
+ *
6
+ * RETAINED FOR HISTORY ONLY, and deliberately no longer a default: see
7
+ * `DEFAULT_PERSONA_VOCABULARY`, which is now empty. `slot_weights`,
8
+ * `variant_weights` and `slot_decisions` rows written before 2026-09-13 are
9
+ * keyed on these strings, and `LEGACY_PERSONA_MAP` still has to canonicalize
10
+ * the pre-069 plural spellings, so deleting them would orphan real posteriors
11
+ * and break the read path for anything already stored.
12
+ *
13
+ * A project may still END UP with one of these keys — by declaring it, or by
14
+ * discovery promoting a segment someone chose to name `buyer`. What can no
15
+ * longer happen is the product asserting them on a customer's behalf.
16
+ */
3
17
  declare const PERSONAS: readonly ["buyer", "researcher", "deal_seeker", "browser"];
4
18
  type Persona = (typeof PERSONAS)[number];
5
19
  declare const UNKNOWN_PERSONA: "unknown";
@@ -19,32 +33,101 @@ declare const LEGACY_PERSONA_MAP: Record<string, Persona>;
19
33
  */
20
34
  declare function canonicalPersona(label: string | null | undefined): PersonaKey;
21
35
 
36
+ /**
37
+ * A catalogue of plausible page orderings — NOT a persona taxonomy.
38
+ *
39
+ * These four orderings were keyed by the seeded personas (`buyer`,
40
+ * `researcher`, `deal_seeker`, `browser`) until 2026-09-13. Those personas were
41
+ * removed, but the deeper problem was that keying orderings by persona NAME was
42
+ * wrong even while they existed, in two ways that were invisible until a
43
+ * customer declared a persona of their own:
44
+ *
45
+ * 1. THE ARM SPACE DEPENDED ON WHAT THE CUSTOMER NAMED THEIR PERSONA. A
46
+ * project that declared `admin` got five candidate orders including the
47
+ * page exactly as authored; a project that declared `buyer` got four, and
48
+ * the authored order was NOT among them — so its own page order could never
49
+ * be served to that persona, and there was no control arm to lose to.
50
+ * Renaming a persona silently changed which layouts were reachable.
51
+ *
52
+ * 2. THE FEASIBILITY GATE SERVED THE COLLIDING ARCHETYPE DETERMINISTICALLY.
53
+ * `routes/decide.ts` serves a heuristic order (never explored, never
54
+ * learned) when layout feasibility is `infeasible` — the state every new
55
+ * project is in. A customer who declared `buyer` had pricing hoisted above
56
+ * everything on every visit, permanently, because of a table written in
57
+ * May; a customer who declared `admin` correctly kept their own page.
58
+ *
59
+ * So the orderings are now named for what they DO. The names are internal and
60
+ * carry no claim about any visitor: they are four opinions about what a page
61
+ * should lead with, and the bandit decides between them and the authored order
62
+ * using evidence. The arrays are unchanged, so every `layout_weights` row in
63
+ * production still joins — `hashLayout` hashes the resulting ORDER, never the
64
+ * key that produced it.
65
+ */
66
+ declare const LAYOUT_ARCHETYPES: Record<string, readonly string[]>;
67
+ /** The archetype names, in a pinned order — iteration order decides candidate
68
+ * insertion order, so it must not depend on object-key enumeration luck. */
69
+ declare const LAYOUT_ARCHETYPE_NAMES: readonly ["conversion_led", "evidence_led", "price_led", "discovery_led"];
70
+ type LayoutArchetype = (typeof LAYOUT_ARCHETYPE_NAMES)[number];
71
+ /**
72
+ * @deprecated Use `LAYOUT_ARCHETYPES`. Retained so the published surface does
73
+ * not break; the keys are the four retired personas and mean nothing now.
74
+ */
22
75
  declare const CLUSTER_PRIORITY: Record<Persona, string[]>;
23
76
  /**
24
- * Reorders section IDs based on the persona's semantic priority.
77
+ * Reorders section IDs by one archetype's semantic priority.
25
78
  * Sections with no graph entry are treated as 'generic'.
26
- * Returns the input unchanged for 'unknown' — and for any custom vocabulary
27
- * persona (declared/discovered): those have no semantic prior, so they serve
28
- * the natural order until the layout bandit has learned rows, the same
29
- * cold-start posture 'unknown' gets.
30
79
  *
31
80
  * With `sectionRoles` (spec 2026-09-04 §1, phase 2d) the ordering projection is
32
81
  * `(role, parent)`: structural sections are PINNED at their original index and
33
82
  * only converters/persuaders re-rank around them. The pin is not cosmetic —
34
- * 'navigation' ranks near last in every persona priority, so an unpinned navbar
35
- * or footer would sort to the bottom of the page, exactly the visible damage a
83
+ * 'navigation' ranks near last in every archetype, so an unpinned navbar or
84
+ * footer would sort to the bottom of the page, exactly the visible damage a
36
85
  * reorder must never do. Callers without role data (the client-local fallback)
37
86
  * omit the map and get the pre-2d behaviour unchanged.
38
87
  */
39
- declare function applyClusterHeuristic(sections: string[], sectionTypes: Map<string, string>, persona: string, sectionRoles?: Map<string, SectionRole>): string[];
88
+ declare function orderByArchetype(sections: string[], sectionTypes: Map<string, string>, archetype: LayoutArchetype, sectionRoles?: Map<string, SectionRole>): string[];
89
+ /**
90
+ * PREVIEW ONLY — the keyless local engine (`@sentientui/core` index-local).
91
+ *
92
+ * Maps an arbitrary persona string to one archetype so that
93
+ * `?sentient_persona=<anything>` visibly rearranges a page with no API key and
94
+ * no server. It used to look the key up in the four-persona table, so only
95
+ * those four literal strings did anything and every other key silently no-oped;
96
+ * now any key previews an arrangement, which is what the docs promise.
97
+ *
98
+ * DO NOT USE THIS ON THE SERVER. The mapping is a hash, not a belief: it
99
+ * carries no claim that this persona wants this ordering. Server serving picks
100
+ * between the archetypes and the authored order with `chooseLayout` /
101
+ * `chooseLayoutFactored`, on evidence.
102
+ *
103
+ * 'unknown' and the empty string return the sections untouched — the natural
104
+ * order is what an unidentified visitor gets, here as everywhere.
105
+ */
106
+ declare function previewOrderForPersona(sections: string[], sectionTypes: Map<string, string>, persona: string, sectionRoles?: Map<string, SectionRole>): string[];
40
107
  /**
41
- * The candidate layout orderings for a page the distinct section orders
42
- * produced by every persona's semantic priority (plus the requesting
43
- * persona's own, which for 'unknown' is the identity order). These are the
44
- * "arms" the layout bandit explores. Returned as hash → order so it joins
45
- * directly against layout_weights rows keyed by the same hashLayout.
108
+ * @deprecated Renamed. Use `orderByArchetype` on the server (by archetype) or
109
+ * `previewOrderForPersona` in the keyless local engine (by arbitrary key).
110
+ * Kept as an alias of the preview mapping so the published signature survives.
46
111
  */
47
- declare function candidateLayouts(sections: string[], sectionTypes: Map<string, string>, persona: string, sectionRoles?: Map<string, SectionRole>): Map<string, string[]>;
112
+ declare const applyClusterHeuristic: typeof previewOrderForPersona;
113
+ /**
114
+ * The candidate layout orderings for a page — the arms the layout bandit
115
+ * explores. Returned as hash → order so it joins directly against
116
+ * `layout_weights` rows keyed by the same `hashLayout`.
117
+ *
118
+ * THE AUTHORED ORDER IS ALWAYS AN ARM. It is the control: the customer built
119
+ * this page in this order, and a bandit whose arm space excludes the baseline
120
+ * can never conclude "leave it alone" — it is structurally obliged to reorder
121
+ * something, and there is nothing for a holdout comparison to mean. Before
122
+ * 2026-09-13 the authored order was included only by accident, when the
123
+ * requesting persona's name happened to miss the archetype table; a persona
124
+ * named `buyer` had it excluded entirely.
125
+ *
126
+ * It no longer takes a persona. The set of orderings a page COULD be shown in
127
+ * is a property of the page, not of who is looking at it — what the persona
128
+ * changes is which arm wins, and that is the posterior's job.
129
+ */
130
+ declare function candidateLayouts(sections: string[], sectionTypes: Map<string, string>, sectionRoles?: Map<string, SectionRole>): Map<string, string[]>;
48
131
 
49
132
  /**
50
133
  * Stable 16-char SHA-256 prefix for a section order array.
@@ -69,15 +152,25 @@ type LearnedLayout = {
69
152
  * Thompson-samples the layout order to serve a persona over the candidate
70
153
  * orderings, using learned posteriors from layout_weights. Candidates with no
71
154
  * learned row use the uniform 1/1 prior — identical to variant cold start.
72
- * Falls back to the persona's heuristic only if sampling yields no candidate.
155
+ *
156
+ * The candidate set always contains the AUTHORED order, so "leave this page
157
+ * alone" is a real arm that can win, and at cold start it is exactly as likely
158
+ * as any reorder. This used to fall back to the persona's heuristic when
159
+ * sampling yielded nothing — which could not happen (candidateLayouts is never
160
+ * empty) and would have been the wrong answer anyway: with nothing to choose
161
+ * on, the page the customer built is the only defensible thing to serve.
162
+ *
163
+ * `persona` no longer selects candidates — the orderings a page COULD be shown
164
+ * in are a property of the page. It stays in the signature because it is what
165
+ * the CALLER keyed `learned` by, which is where the persona belongs.
73
166
  *
74
167
  * @param rand Uniform [0,1) source. Defaults to `Math.random`, which is
75
168
  * NON-DETERMINISTIC. Pass a seeded PRNG when you need a reproducible layout
76
169
  * (tests, replayable decisions) — otherwise the sampled order varies per call.
77
170
  * @param sectionRoles Optional role map (phase 2d): structural sections are
78
- * pinned in place across every candidate; see `applyClusterHeuristic`.
171
+ * pinned in place across every candidate; see `orderByArchetype`.
79
172
  */
80
- declare function chooseLayout(sections: string[], sectionTypes: Map<string, string>, persona: string, learned: Map<string, LearnedLayout>, rand?: () => number, sectionRoles?: Map<string, SectionRole>): string[];
173
+ declare function chooseLayout(sections: string[], sectionTypes: Map<string, string>, _persona: string, learned: Map<string, LearnedLayout>, rand?: () => number, sectionRoles?: Map<string, SectionRole>): string[];
81
174
 
82
175
  /**
83
176
  * Factored layout value model (spec 2026-09-04 §3a).
@@ -133,10 +226,130 @@ declare function factorCellsForOrder(order: string[], sectionTypes: Map<string,
133
226
  * start degrades gracefully: empty cells draw from Beta(1,1), which still
134
227
  * randomises across candidates, so exploration survives the switch.
135
228
  *
136
- * Falls back to the persona's heuristic prior when there are no candidates.
229
+ * The candidate set always contains the AUTHORED order, so leaving the page as
230
+ * built is a real arm rather than something only reachable by accident.
137
231
  */
138
232
  declare function chooseLayoutFactored(sections: string[], sectionTypes: Map<string, string>, persona: string, cells: LayoutFactorCell[], rand?: () => number, sectionRoles?: Map<string, SectionRole>): string[];
139
233
 
234
+ /**
235
+ * Factored slot value model — the slot analogue of `layout-factored.ts`.
236
+ *
237
+ * `slot_weights` keys one independent Beta posterior per
238
+ * (arm, segment, persona), where `segment` is the joined `device:source`. That
239
+ * is why the decision context stopped at two fields: every field ADDED
240
+ * multiplies the cell count. Three devices x six sources x five personas is
241
+ * already 90 cells per arm; adding new-vs-returning makes it 180, adding
242
+ * country multiplies again — and each cell estimates from its own slice of the
243
+ * same thin traffic, so a richer context converges WORSE, not better.
244
+ *
245
+ * This model replaces one-parameter-per-context-combination with one per
246
+ * context FACTOR:
247
+ *
248
+ * V(arm | context) = Σ_f draw[arm, factor f, level of f in this context]
249
+ *
250
+ * Four factors at 3 + 6 + 5 + 2 levels is 16 cells per arm instead of 180, and
251
+ * every trial teaches every context that shares ANY factor level: a conversion
252
+ * on mobile/paid/returning updates "this arm on mobile", which transfers to
253
+ * mobile/organic/new. Additive over factors is the same shape the layout model
254
+ * uses additively over positions, and it is fair across arms for the same
255
+ * reason — every arm in one request is scored under the identical factor set,
256
+ * so the sum has the same number of terms for each.
257
+ *
258
+ * It improves sample efficiency; it does not manufacture signal. With a handful
259
+ * of conversions no model learns a ranking, which is what the feasibility gate
260
+ * exists to say out loud.
261
+ */
262
+ /** Persona key of the pooled global cells. Reserved — never a real persona. */
263
+ declare const GLOBAL_FACTOR_LEVEL = "__global__";
264
+ /**
265
+ * Context factors the model conditions on, in a pinned order.
266
+ *
267
+ * Widening this list is the whole point of the design — it costs O(levels), not
268
+ * O(product) — but it is NOT free: each factor adds a term to the sum, and a
269
+ * factor with no signal adds variance to every score. Add one when there is a
270
+ * reason to believe it changes which arm wins, not because the column exists.
271
+ */
272
+ declare const SLOT_FACTORS: readonly ["device", "source", "persona", "visit"];
273
+ type SlotFactor = (typeof SLOT_FACTORS)[number];
274
+ type SlotFactorContext = {
275
+ device: string | null;
276
+ source: string | null;
277
+ persona: string;
278
+ /** 'new' | 'returning' | null when the visit count is unknown. */
279
+ visit: string | null;
280
+ };
281
+ type SlotFactorCell = {
282
+ arm: string;
283
+ factor: string;
284
+ /** Factor level, or GLOBAL_FACTOR_LEVEL for the arm's context-free row. */
285
+ level: string;
286
+ exposures: number;
287
+ conversions: number;
288
+ };
289
+ /**
290
+ * The (factor, level) pairs one served decision contributes to — the write-side
291
+ * projection close-out uses, and the read-side lookup serving uses. Both call
292
+ * this so the two can never disagree about what a context decomposes into.
293
+ *
294
+ * An UNKNOWN persona contributes no persona term. That is the Pareto safety
295
+ * invariant of CONTRACTS §4 restated for this model: unknown-persona traffic
296
+ * must run on exactly the persona-agnostic policy, so the persona factor is not
297
+ * merely empty for them, it is absent — an `unknown` LEVEL would otherwise
298
+ * become a real segment that accumulates its own rate and steers serving.
299
+ *
300
+ * A null device/source/visit is likewise absent rather than levelled as
301
+ * 'unknown', for the same reason: "not measured" is not a level.
302
+ */
303
+ declare function factorLevelsFor(ctx: SlotFactorContext): Array<{
304
+ factor: SlotFactor;
305
+ level: string;
306
+ }>;
307
+ type FactoredSlotChoice = {
308
+ arm: string;
309
+ factorsUsed: number;
310
+ };
311
+ /**
312
+ * Thompson-style selection over the factored model.
313
+ *
314
+ * ONE draw per (arm, factor, level) cell, cached for the call — an arm's score
315
+ * is the sum of its factor draws, and every arm is scored under the same factor
316
+ * set, so the comparison is made in a single sampled world. A fresh draw per
317
+ * comparison would add pure noise to the ranking rather than exploration.
318
+ *
319
+ * Shrinkage follows CONTRACTS §4 exactly: a factor-level cell shrinks toward
320
+ * the arm's GLOBAL cell, and the global cell shrinks toward the slot's pooled
321
+ * rate across all arms. The MEAN crosses each boundary, never the sample size —
322
+ * so a thin factor level keeps a posterior as wide as its own evidence warrants
323
+ * and Thompson sampling still explores it.
324
+ *
325
+ * Cold start degrades to the unfactored behaviour: with no cells at all every
326
+ * arm draws from the same shrunken pool, which still randomises, so exploration
327
+ * survives enabling this model on a project with history.
328
+ */
329
+ declare function chooseSlotArmFactored(arms: readonly string[], ctx: SlotFactorContext, cells: readonly SlotFactorCell[], rand?: () => number): FactoredSlotChoice | null;
330
+ /**
331
+ * The cells one closed trial writes, given its context — the write-side
332
+ * projection. Always includes the arm's GLOBAL row, which is what every factor
333
+ * level shrinks toward and what the slot-wide pool is summed from.
334
+ *
335
+ * `personaWeight` is the SOFT assignment the layout model established: the
336
+ * persona term trains at the portrait's `reliability_score` (declared personas
337
+ * at 1.0) while every other factor trains at 1. A mismeasured persona otherwise
338
+ * induces attenuation bias — it drags a cell toward the population mean in
339
+ * proportion to how often it is wrong — and soft weighting lets the large
340
+ * unknown mass inform the global term instead of forming a dead bucket.
341
+ */
342
+ declare function factorCellsForTrial(arm: string, ctx: SlotFactorContext, personaWeight: number): Array<{
343
+ arm: string;
344
+ factor: string;
345
+ level: string;
346
+ weight: number;
347
+ }>;
348
+ /** Visit-count bucket. Two levels on purpose: new-vs-returning is one of the
349
+ * largest conversion differences on any site, and finer buckets would spend
350
+ * parameters on a tail that prod does not currently have. */
351
+ declare function visitLevel(visitCount: number | null | undefined): string | null;
352
+
140
353
  /** Learned Beta(alpha, beta) posterior for one arm. */
141
354
  type ArmPosterior = {
142
355
  arm: string;
@@ -463,6 +676,36 @@ type PersonaVocabularyMember = {
463
676
  * contains, and the fallback when a project has no active set (a missed
464
677
  * app-code insert degrades to today's behaviour, never an error).
465
678
  */
679
+ /**
680
+ * The vocabulary a project has when it has declared nothing: EMPTY.
681
+ *
682
+ * This shipped as a hardcoded four — buyer / researcher / deal_seeker / browser
683
+ * — which the product then presented as if it knew the customer's audience.
684
+ * Earned rows (2026-09-12) demoted them to a `starter` state; this removes them.
685
+ *
686
+ * The measurement that settled it, across all of production history:
687
+ * `unknown` served 10,882 decisions, the four seeded personas served **18
688
+ * between them**. They were not a taxonomy, they were decoration on an axis
689
+ * that was 99.8% empty — and every one of them was an assertion about visitors
690
+ * nobody had met.
691
+ *
692
+ * A persona now has exactly two honest origins:
693
+ *
694
+ * - **declared** — the customer's own code tells us (a role, a plan tier).
695
+ * Ground truth; no evidence gate, because eligibility is their decision.
696
+ * - **discovered** — `persona-discovery.ts` finds it in real behaviour and it
697
+ * clears the interaction gate (the RANKING of arms must differ inside vs
698
+ * outside the segment, not merely the conversion rate).
699
+ *
700
+ * Everything else resolves to `unknown`, which is where day-0 value accrues and
701
+ * where the pooled bandit has always done the actual work.
702
+ *
703
+ * THIS IS ONLY SAFE BECAUSE SERVING NO LONGER NEEDS A PERSONA. The factored
704
+ * model (migration 149) conditions on device, source and visit count as
705
+ * first-class factors — measured, not guessed — so a project with no personas
706
+ * still adapts per visitor. Before that landed, emptying this would have meant
707
+ * no personalization at all.
708
+ */
466
709
  declare const DEFAULT_PERSONA_VOCABULARY: readonly PersonaVocabularyMember[];
467
710
  type PersonaResolution = {
468
711
  /** Vocabulary key, or 'unknown'. This is what decisions/weights key on. */
@@ -509,4 +752,4 @@ declare function resolvePersona(input: {
509
752
  */
510
753
  declare function decisionPersona(label: string | null | undefined): string;
511
754
 
512
- export { type ArmPosterior, CLUSTER_PRIORITY, DEFAULT_PERSONA_VOCABULARY, EV_SHRINK_K, type EvArm, GLOBAL_FACTOR_PERSONA, LAYOUT_FACTOR_BUCKETS, LEGACY_PERSONA_MAP, type LayoutFactorCell, type LearnedLayout, PERSONAS, PERSONA_DISPLAY, PERSONA_KEY_RE, POOL_ALL, type Persona, type PersonaKey, type PersonaResolution, type PersonaVocabularyMember, type PoolCells, type PoolCounts, RESERVED_PERSONA_KEYS, SHRINKAGE_M, type SlotDecl, type SlotResult, UNKNOWN_PERSONA, type ValueCell, type ValueCellRow, WEIGHTS_FALLBACK_PRIOR_PULLS, type WeightsFallbackArm, applyClusterHeuristic, broadestValueCell, candidateLayouts, canonicalArm, canonicalPersona, chooseLayout, chooseLayoutFactored, confidenceBand, decisionPersona, factorCellsForOrder, fnv1a, hashLayout, layoutBucketOf, marginalArmKey, normalizeDeclaredPersona, parseArm, pickDeterministicArm, pickFromWeights, pooledPosterior, posteriorOfCounts, resolvePersona, sampleArm, sampleArmEv, sampleBeta, shrunkAvgValue, shrunkPosterior, slotBaselineArm, slotResultFor, validateSlotDecl, weightCellsFor };
755
+ export { type ArmPosterior, CLUSTER_PRIORITY, DEFAULT_PERSONA_VOCABULARY, EV_SHRINK_K, type EvArm, type FactoredSlotChoice, GLOBAL_FACTOR_LEVEL, GLOBAL_FACTOR_PERSONA, LAYOUT_ARCHETYPES, LAYOUT_ARCHETYPE_NAMES, LAYOUT_FACTOR_BUCKETS, LEGACY_PERSONA_MAP, type LayoutArchetype, type LayoutFactorCell, type LearnedLayout, PERSONAS, PERSONA_DISPLAY, PERSONA_KEY_RE, POOL_ALL, type Persona, type PersonaKey, type PersonaResolution, type PersonaVocabularyMember, type PoolCells, type PoolCounts, RESERVED_PERSONA_KEYS, SHRINKAGE_M, SLOT_FACTORS, type SlotDecl, type SlotFactor, type SlotFactorCell, type SlotFactorContext, type SlotResult, UNKNOWN_PERSONA, type ValueCell, type ValueCellRow, WEIGHTS_FALLBACK_PRIOR_PULLS, type WeightsFallbackArm, applyClusterHeuristic, broadestValueCell, candidateLayouts, canonicalArm, canonicalPersona, chooseLayout, chooseLayoutFactored, chooseSlotArmFactored, confidenceBand, decisionPersona, factorCellsForOrder, factorCellsForTrial, factorLevelsFor, fnv1a, hashLayout, layoutBucketOf, marginalArmKey, normalizeDeclaredPersona, orderByArchetype, parseArm, pickDeterministicArm, pickFromWeights, pooledPosterior, posteriorOfCounts, previewOrderForPersona, resolvePersona, sampleArm, sampleArmEv, sampleBeta, shrunkAvgValue, shrunkPosterior, slotBaselineArm, slotResultFor, validateSlotDecl, visitLevel, weightCellsFor };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,19 @@
1
1
  import { SectionRole } from './taxonomy.js';
2
2
 
3
+ /**
4
+ * The four labels this product used to ship as a default vocabulary.
5
+ *
6
+ * RETAINED FOR HISTORY ONLY, and deliberately no longer a default: see
7
+ * `DEFAULT_PERSONA_VOCABULARY`, which is now empty. `slot_weights`,
8
+ * `variant_weights` and `slot_decisions` rows written before 2026-09-13 are
9
+ * keyed on these strings, and `LEGACY_PERSONA_MAP` still has to canonicalize
10
+ * the pre-069 plural spellings, so deleting them would orphan real posteriors
11
+ * and break the read path for anything already stored.
12
+ *
13
+ * A project may still END UP with one of these keys — by declaring it, or by
14
+ * discovery promoting a segment someone chose to name `buyer`. What can no
15
+ * longer happen is the product asserting them on a customer's behalf.
16
+ */
3
17
  declare const PERSONAS: readonly ["buyer", "researcher", "deal_seeker", "browser"];
4
18
  type Persona = (typeof PERSONAS)[number];
5
19
  declare const UNKNOWN_PERSONA: "unknown";
@@ -19,32 +33,101 @@ declare const LEGACY_PERSONA_MAP: Record<string, Persona>;
19
33
  */
20
34
  declare function canonicalPersona(label: string | null | undefined): PersonaKey;
21
35
 
36
+ /**
37
+ * A catalogue of plausible page orderings — NOT a persona taxonomy.
38
+ *
39
+ * These four orderings were keyed by the seeded personas (`buyer`,
40
+ * `researcher`, `deal_seeker`, `browser`) until 2026-09-13. Those personas were
41
+ * removed, but the deeper problem was that keying orderings by persona NAME was
42
+ * wrong even while they existed, in two ways that were invisible until a
43
+ * customer declared a persona of their own:
44
+ *
45
+ * 1. THE ARM SPACE DEPENDED ON WHAT THE CUSTOMER NAMED THEIR PERSONA. A
46
+ * project that declared `admin` got five candidate orders including the
47
+ * page exactly as authored; a project that declared `buyer` got four, and
48
+ * the authored order was NOT among them — so its own page order could never
49
+ * be served to that persona, and there was no control arm to lose to.
50
+ * Renaming a persona silently changed which layouts were reachable.
51
+ *
52
+ * 2. THE FEASIBILITY GATE SERVED THE COLLIDING ARCHETYPE DETERMINISTICALLY.
53
+ * `routes/decide.ts` serves a heuristic order (never explored, never
54
+ * learned) when layout feasibility is `infeasible` — the state every new
55
+ * project is in. A customer who declared `buyer` had pricing hoisted above
56
+ * everything on every visit, permanently, because of a table written in
57
+ * May; a customer who declared `admin` correctly kept their own page.
58
+ *
59
+ * So the orderings are now named for what they DO. The names are internal and
60
+ * carry no claim about any visitor: they are four opinions about what a page
61
+ * should lead with, and the bandit decides between them and the authored order
62
+ * using evidence. The arrays are unchanged, so every `layout_weights` row in
63
+ * production still joins — `hashLayout` hashes the resulting ORDER, never the
64
+ * key that produced it.
65
+ */
66
+ declare const LAYOUT_ARCHETYPES: Record<string, readonly string[]>;
67
+ /** The archetype names, in a pinned order — iteration order decides candidate
68
+ * insertion order, so it must not depend on object-key enumeration luck. */
69
+ declare const LAYOUT_ARCHETYPE_NAMES: readonly ["conversion_led", "evidence_led", "price_led", "discovery_led"];
70
+ type LayoutArchetype = (typeof LAYOUT_ARCHETYPE_NAMES)[number];
71
+ /**
72
+ * @deprecated Use `LAYOUT_ARCHETYPES`. Retained so the published surface does
73
+ * not break; the keys are the four retired personas and mean nothing now.
74
+ */
22
75
  declare const CLUSTER_PRIORITY: Record<Persona, string[]>;
23
76
  /**
24
- * Reorders section IDs based on the persona's semantic priority.
77
+ * Reorders section IDs by one archetype's semantic priority.
25
78
  * Sections with no graph entry are treated as 'generic'.
26
- * Returns the input unchanged for 'unknown' — and for any custom vocabulary
27
- * persona (declared/discovered): those have no semantic prior, so they serve
28
- * the natural order until the layout bandit has learned rows, the same
29
- * cold-start posture 'unknown' gets.
30
79
  *
31
80
  * With `sectionRoles` (spec 2026-09-04 §1, phase 2d) the ordering projection is
32
81
  * `(role, parent)`: structural sections are PINNED at their original index and
33
82
  * only converters/persuaders re-rank around them. The pin is not cosmetic —
34
- * 'navigation' ranks near last in every persona priority, so an unpinned navbar
35
- * or footer would sort to the bottom of the page, exactly the visible damage a
83
+ * 'navigation' ranks near last in every archetype, so an unpinned navbar or
84
+ * footer would sort to the bottom of the page, exactly the visible damage a
36
85
  * reorder must never do. Callers without role data (the client-local fallback)
37
86
  * omit the map and get the pre-2d behaviour unchanged.
38
87
  */
39
- declare function applyClusterHeuristic(sections: string[], sectionTypes: Map<string, string>, persona: string, sectionRoles?: Map<string, SectionRole>): string[];
88
+ declare function orderByArchetype(sections: string[], sectionTypes: Map<string, string>, archetype: LayoutArchetype, sectionRoles?: Map<string, SectionRole>): string[];
89
+ /**
90
+ * PREVIEW ONLY — the keyless local engine (`@sentientui/core` index-local).
91
+ *
92
+ * Maps an arbitrary persona string to one archetype so that
93
+ * `?sentient_persona=<anything>` visibly rearranges a page with no API key and
94
+ * no server. It used to look the key up in the four-persona table, so only
95
+ * those four literal strings did anything and every other key silently no-oped;
96
+ * now any key previews an arrangement, which is what the docs promise.
97
+ *
98
+ * DO NOT USE THIS ON THE SERVER. The mapping is a hash, not a belief: it
99
+ * carries no claim that this persona wants this ordering. Server serving picks
100
+ * between the archetypes and the authored order with `chooseLayout` /
101
+ * `chooseLayoutFactored`, on evidence.
102
+ *
103
+ * 'unknown' and the empty string return the sections untouched — the natural
104
+ * order is what an unidentified visitor gets, here as everywhere.
105
+ */
106
+ declare function previewOrderForPersona(sections: string[], sectionTypes: Map<string, string>, persona: string, sectionRoles?: Map<string, SectionRole>): string[];
40
107
  /**
41
- * The candidate layout orderings for a page the distinct section orders
42
- * produced by every persona's semantic priority (plus the requesting
43
- * persona's own, which for 'unknown' is the identity order). These are the
44
- * "arms" the layout bandit explores. Returned as hash → order so it joins
45
- * directly against layout_weights rows keyed by the same hashLayout.
108
+ * @deprecated Renamed. Use `orderByArchetype` on the server (by archetype) or
109
+ * `previewOrderForPersona` in the keyless local engine (by arbitrary key).
110
+ * Kept as an alias of the preview mapping so the published signature survives.
46
111
  */
47
- declare function candidateLayouts(sections: string[], sectionTypes: Map<string, string>, persona: string, sectionRoles?: Map<string, SectionRole>): Map<string, string[]>;
112
+ declare const applyClusterHeuristic: typeof previewOrderForPersona;
113
+ /**
114
+ * The candidate layout orderings for a page — the arms the layout bandit
115
+ * explores. Returned as hash → order so it joins directly against
116
+ * `layout_weights` rows keyed by the same `hashLayout`.
117
+ *
118
+ * THE AUTHORED ORDER IS ALWAYS AN ARM. It is the control: the customer built
119
+ * this page in this order, and a bandit whose arm space excludes the baseline
120
+ * can never conclude "leave it alone" — it is structurally obliged to reorder
121
+ * something, and there is nothing for a holdout comparison to mean. Before
122
+ * 2026-09-13 the authored order was included only by accident, when the
123
+ * requesting persona's name happened to miss the archetype table; a persona
124
+ * named `buyer` had it excluded entirely.
125
+ *
126
+ * It no longer takes a persona. The set of orderings a page COULD be shown in
127
+ * is a property of the page, not of who is looking at it — what the persona
128
+ * changes is which arm wins, and that is the posterior's job.
129
+ */
130
+ declare function candidateLayouts(sections: string[], sectionTypes: Map<string, string>, sectionRoles?: Map<string, SectionRole>): Map<string, string[]>;
48
131
 
49
132
  /**
50
133
  * Stable 16-char SHA-256 prefix for a section order array.
@@ -69,15 +152,25 @@ type LearnedLayout = {
69
152
  * Thompson-samples the layout order to serve a persona over the candidate
70
153
  * orderings, using learned posteriors from layout_weights. Candidates with no
71
154
  * learned row use the uniform 1/1 prior — identical to variant cold start.
72
- * Falls back to the persona's heuristic only if sampling yields no candidate.
155
+ *
156
+ * The candidate set always contains the AUTHORED order, so "leave this page
157
+ * alone" is a real arm that can win, and at cold start it is exactly as likely
158
+ * as any reorder. This used to fall back to the persona's heuristic when
159
+ * sampling yielded nothing — which could not happen (candidateLayouts is never
160
+ * empty) and would have been the wrong answer anyway: with nothing to choose
161
+ * on, the page the customer built is the only defensible thing to serve.
162
+ *
163
+ * `persona` no longer selects candidates — the orderings a page COULD be shown
164
+ * in are a property of the page. It stays in the signature because it is what
165
+ * the CALLER keyed `learned` by, which is where the persona belongs.
73
166
  *
74
167
  * @param rand Uniform [0,1) source. Defaults to `Math.random`, which is
75
168
  * NON-DETERMINISTIC. Pass a seeded PRNG when you need a reproducible layout
76
169
  * (tests, replayable decisions) — otherwise the sampled order varies per call.
77
170
  * @param sectionRoles Optional role map (phase 2d): structural sections are
78
- * pinned in place across every candidate; see `applyClusterHeuristic`.
171
+ * pinned in place across every candidate; see `orderByArchetype`.
79
172
  */
80
- declare function chooseLayout(sections: string[], sectionTypes: Map<string, string>, persona: string, learned: Map<string, LearnedLayout>, rand?: () => number, sectionRoles?: Map<string, SectionRole>): string[];
173
+ declare function chooseLayout(sections: string[], sectionTypes: Map<string, string>, _persona: string, learned: Map<string, LearnedLayout>, rand?: () => number, sectionRoles?: Map<string, SectionRole>): string[];
81
174
 
82
175
  /**
83
176
  * Factored layout value model (spec 2026-09-04 §3a).
@@ -133,10 +226,130 @@ declare function factorCellsForOrder(order: string[], sectionTypes: Map<string,
133
226
  * start degrades gracefully: empty cells draw from Beta(1,1), which still
134
227
  * randomises across candidates, so exploration survives the switch.
135
228
  *
136
- * Falls back to the persona's heuristic prior when there are no candidates.
229
+ * The candidate set always contains the AUTHORED order, so leaving the page as
230
+ * built is a real arm rather than something only reachable by accident.
137
231
  */
138
232
  declare function chooseLayoutFactored(sections: string[], sectionTypes: Map<string, string>, persona: string, cells: LayoutFactorCell[], rand?: () => number, sectionRoles?: Map<string, SectionRole>): string[];
139
233
 
234
+ /**
235
+ * Factored slot value model — the slot analogue of `layout-factored.ts`.
236
+ *
237
+ * `slot_weights` keys one independent Beta posterior per
238
+ * (arm, segment, persona), where `segment` is the joined `device:source`. That
239
+ * is why the decision context stopped at two fields: every field ADDED
240
+ * multiplies the cell count. Three devices x six sources x five personas is
241
+ * already 90 cells per arm; adding new-vs-returning makes it 180, adding
242
+ * country multiplies again — and each cell estimates from its own slice of the
243
+ * same thin traffic, so a richer context converges WORSE, not better.
244
+ *
245
+ * This model replaces one-parameter-per-context-combination with one per
246
+ * context FACTOR:
247
+ *
248
+ * V(arm | context) = Σ_f draw[arm, factor f, level of f in this context]
249
+ *
250
+ * Four factors at 3 + 6 + 5 + 2 levels is 16 cells per arm instead of 180, and
251
+ * every trial teaches every context that shares ANY factor level: a conversion
252
+ * on mobile/paid/returning updates "this arm on mobile", which transfers to
253
+ * mobile/organic/new. Additive over factors is the same shape the layout model
254
+ * uses additively over positions, and it is fair across arms for the same
255
+ * reason — every arm in one request is scored under the identical factor set,
256
+ * so the sum has the same number of terms for each.
257
+ *
258
+ * It improves sample efficiency; it does not manufacture signal. With a handful
259
+ * of conversions no model learns a ranking, which is what the feasibility gate
260
+ * exists to say out loud.
261
+ */
262
+ /** Persona key of the pooled global cells. Reserved — never a real persona. */
263
+ declare const GLOBAL_FACTOR_LEVEL = "__global__";
264
+ /**
265
+ * Context factors the model conditions on, in a pinned order.
266
+ *
267
+ * Widening this list is the whole point of the design — it costs O(levels), not
268
+ * O(product) — but it is NOT free: each factor adds a term to the sum, and a
269
+ * factor with no signal adds variance to every score. Add one when there is a
270
+ * reason to believe it changes which arm wins, not because the column exists.
271
+ */
272
+ declare const SLOT_FACTORS: readonly ["device", "source", "persona", "visit"];
273
+ type SlotFactor = (typeof SLOT_FACTORS)[number];
274
+ type SlotFactorContext = {
275
+ device: string | null;
276
+ source: string | null;
277
+ persona: string;
278
+ /** 'new' | 'returning' | null when the visit count is unknown. */
279
+ visit: string | null;
280
+ };
281
+ type SlotFactorCell = {
282
+ arm: string;
283
+ factor: string;
284
+ /** Factor level, or GLOBAL_FACTOR_LEVEL for the arm's context-free row. */
285
+ level: string;
286
+ exposures: number;
287
+ conversions: number;
288
+ };
289
+ /**
290
+ * The (factor, level) pairs one served decision contributes to — the write-side
291
+ * projection close-out uses, and the read-side lookup serving uses. Both call
292
+ * this so the two can never disagree about what a context decomposes into.
293
+ *
294
+ * An UNKNOWN persona contributes no persona term. That is the Pareto safety
295
+ * invariant of CONTRACTS §4 restated for this model: unknown-persona traffic
296
+ * must run on exactly the persona-agnostic policy, so the persona factor is not
297
+ * merely empty for them, it is absent — an `unknown` LEVEL would otherwise
298
+ * become a real segment that accumulates its own rate and steers serving.
299
+ *
300
+ * A null device/source/visit is likewise absent rather than levelled as
301
+ * 'unknown', for the same reason: "not measured" is not a level.
302
+ */
303
+ declare function factorLevelsFor(ctx: SlotFactorContext): Array<{
304
+ factor: SlotFactor;
305
+ level: string;
306
+ }>;
307
+ type FactoredSlotChoice = {
308
+ arm: string;
309
+ factorsUsed: number;
310
+ };
311
+ /**
312
+ * Thompson-style selection over the factored model.
313
+ *
314
+ * ONE draw per (arm, factor, level) cell, cached for the call — an arm's score
315
+ * is the sum of its factor draws, and every arm is scored under the same factor
316
+ * set, so the comparison is made in a single sampled world. A fresh draw per
317
+ * comparison would add pure noise to the ranking rather than exploration.
318
+ *
319
+ * Shrinkage follows CONTRACTS §4 exactly: a factor-level cell shrinks toward
320
+ * the arm's GLOBAL cell, and the global cell shrinks toward the slot's pooled
321
+ * rate across all arms. The MEAN crosses each boundary, never the sample size —
322
+ * so a thin factor level keeps a posterior as wide as its own evidence warrants
323
+ * and Thompson sampling still explores it.
324
+ *
325
+ * Cold start degrades to the unfactored behaviour: with no cells at all every
326
+ * arm draws from the same shrunken pool, which still randomises, so exploration
327
+ * survives enabling this model on a project with history.
328
+ */
329
+ declare function chooseSlotArmFactored(arms: readonly string[], ctx: SlotFactorContext, cells: readonly SlotFactorCell[], rand?: () => number): FactoredSlotChoice | null;
330
+ /**
331
+ * The cells one closed trial writes, given its context — the write-side
332
+ * projection. Always includes the arm's GLOBAL row, which is what every factor
333
+ * level shrinks toward and what the slot-wide pool is summed from.
334
+ *
335
+ * `personaWeight` is the SOFT assignment the layout model established: the
336
+ * persona term trains at the portrait's `reliability_score` (declared personas
337
+ * at 1.0) while every other factor trains at 1. A mismeasured persona otherwise
338
+ * induces attenuation bias — it drags a cell toward the population mean in
339
+ * proportion to how often it is wrong — and soft weighting lets the large
340
+ * unknown mass inform the global term instead of forming a dead bucket.
341
+ */
342
+ declare function factorCellsForTrial(arm: string, ctx: SlotFactorContext, personaWeight: number): Array<{
343
+ arm: string;
344
+ factor: string;
345
+ level: string;
346
+ weight: number;
347
+ }>;
348
+ /** Visit-count bucket. Two levels on purpose: new-vs-returning is one of the
349
+ * largest conversion differences on any site, and finer buckets would spend
350
+ * parameters on a tail that prod does not currently have. */
351
+ declare function visitLevel(visitCount: number | null | undefined): string | null;
352
+
140
353
  /** Learned Beta(alpha, beta) posterior for one arm. */
141
354
  type ArmPosterior = {
142
355
  arm: string;
@@ -463,6 +676,36 @@ type PersonaVocabularyMember = {
463
676
  * contains, and the fallback when a project has no active set (a missed
464
677
  * app-code insert degrades to today's behaviour, never an error).
465
678
  */
679
+ /**
680
+ * The vocabulary a project has when it has declared nothing: EMPTY.
681
+ *
682
+ * This shipped as a hardcoded four — buyer / researcher / deal_seeker / browser
683
+ * — which the product then presented as if it knew the customer's audience.
684
+ * Earned rows (2026-09-12) demoted them to a `starter` state; this removes them.
685
+ *
686
+ * The measurement that settled it, across all of production history:
687
+ * `unknown` served 10,882 decisions, the four seeded personas served **18
688
+ * between them**. They were not a taxonomy, they were decoration on an axis
689
+ * that was 99.8% empty — and every one of them was an assertion about visitors
690
+ * nobody had met.
691
+ *
692
+ * A persona now has exactly two honest origins:
693
+ *
694
+ * - **declared** — the customer's own code tells us (a role, a plan tier).
695
+ * Ground truth; no evidence gate, because eligibility is their decision.
696
+ * - **discovered** — `persona-discovery.ts` finds it in real behaviour and it
697
+ * clears the interaction gate (the RANKING of arms must differ inside vs
698
+ * outside the segment, not merely the conversion rate).
699
+ *
700
+ * Everything else resolves to `unknown`, which is where day-0 value accrues and
701
+ * where the pooled bandit has always done the actual work.
702
+ *
703
+ * THIS IS ONLY SAFE BECAUSE SERVING NO LONGER NEEDS A PERSONA. The factored
704
+ * model (migration 149) conditions on device, source and visit count as
705
+ * first-class factors — measured, not guessed — so a project with no personas
706
+ * still adapts per visitor. Before that landed, emptying this would have meant
707
+ * no personalization at all.
708
+ */
466
709
  declare const DEFAULT_PERSONA_VOCABULARY: readonly PersonaVocabularyMember[];
467
710
  type PersonaResolution = {
468
711
  /** Vocabulary key, or 'unknown'. This is what decisions/weights key on. */
@@ -509,4 +752,4 @@ declare function resolvePersona(input: {
509
752
  */
510
753
  declare function decisionPersona(label: string | null | undefined): string;
511
754
 
512
- export { type ArmPosterior, CLUSTER_PRIORITY, DEFAULT_PERSONA_VOCABULARY, EV_SHRINK_K, type EvArm, GLOBAL_FACTOR_PERSONA, LAYOUT_FACTOR_BUCKETS, LEGACY_PERSONA_MAP, type LayoutFactorCell, type LearnedLayout, PERSONAS, PERSONA_DISPLAY, PERSONA_KEY_RE, POOL_ALL, type Persona, type PersonaKey, type PersonaResolution, type PersonaVocabularyMember, type PoolCells, type PoolCounts, RESERVED_PERSONA_KEYS, SHRINKAGE_M, type SlotDecl, type SlotResult, UNKNOWN_PERSONA, type ValueCell, type ValueCellRow, WEIGHTS_FALLBACK_PRIOR_PULLS, type WeightsFallbackArm, applyClusterHeuristic, broadestValueCell, candidateLayouts, canonicalArm, canonicalPersona, chooseLayout, chooseLayoutFactored, confidenceBand, decisionPersona, factorCellsForOrder, fnv1a, hashLayout, layoutBucketOf, marginalArmKey, normalizeDeclaredPersona, parseArm, pickDeterministicArm, pickFromWeights, pooledPosterior, posteriorOfCounts, resolvePersona, sampleArm, sampleArmEv, sampleBeta, shrunkAvgValue, shrunkPosterior, slotBaselineArm, slotResultFor, validateSlotDecl, weightCellsFor };
755
+ export { type ArmPosterior, CLUSTER_PRIORITY, DEFAULT_PERSONA_VOCABULARY, EV_SHRINK_K, type EvArm, type FactoredSlotChoice, GLOBAL_FACTOR_LEVEL, GLOBAL_FACTOR_PERSONA, LAYOUT_ARCHETYPES, LAYOUT_ARCHETYPE_NAMES, LAYOUT_FACTOR_BUCKETS, LEGACY_PERSONA_MAP, type LayoutArchetype, type LayoutFactorCell, type LearnedLayout, PERSONAS, PERSONA_DISPLAY, PERSONA_KEY_RE, POOL_ALL, type Persona, type PersonaKey, type PersonaResolution, type PersonaVocabularyMember, type PoolCells, type PoolCounts, RESERVED_PERSONA_KEYS, SHRINKAGE_M, SLOT_FACTORS, type SlotDecl, type SlotFactor, type SlotFactorCell, type SlotFactorContext, type SlotResult, UNKNOWN_PERSONA, type ValueCell, type ValueCellRow, WEIGHTS_FALLBACK_PRIOR_PULLS, type WeightsFallbackArm, applyClusterHeuristic, broadestValueCell, candidateLayouts, canonicalArm, canonicalPersona, chooseLayout, chooseLayoutFactored, chooseSlotArmFactored, confidenceBand, decisionPersona, factorCellsForOrder, factorCellsForTrial, factorLevelsFor, fnv1a, hashLayout, layoutBucketOf, marginalArmKey, normalizeDeclaredPersona, orderByArchetype, parseArm, pickDeterministicArm, pickFromWeights, pooledPosterior, posteriorOfCounts, previewOrderForPersona, resolvePersona, sampleArm, sampleArmEv, sampleBeta, shrunkAvgValue, shrunkPosterior, slotBaselineArm, slotResultFor, validateSlotDecl, visitLevel, weightCellsFor };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var F=Object.defineProperty;var de=Object.getOwnPropertyDescriptor;var xe=Object.getOwnPropertyNames,J=Object.getOwnPropertySymbols;var ee=Object.prototype.hasOwnProperty,he=Object.prototype.propertyIsEnumerable;var Q=(e,r,t)=>r in e?F(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t,B=(e,r)=>{for(var t in r||(r={}))ee.call(r,t)&&Q(e,t,r[t]);if(J)for(var t of J(r))he.call(r,t)&&Q(e,t,r[t]);return e};var ye=(e,r)=>{for(var t in r)F(e,t,{get:r[t],enumerable:!0})},ve=(e,r,t,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of xe(r))!ee.call(e,o)&&o!==t&&F(e,o,{get:()=>r[o],enumerable:!(n=de(r,o))||n.enumerable});return e};var Pe=e=>ve(F({},"__esModule",{value:!0}),e);var qe={};ye(qe,{CLUSTER_PRIORITY:()=>te,DEFAULT_PERSONA_VOCABULARY:()=>me,EV_SHRINK_K:()=>ue,GLOBAL_FACTOR_PERSONA:()=>se,LAYOUT_FACTOR_BUCKETS:()=>Y,LEGACY_PERSONA_MAP:()=>U,PERSONAS:()=>K,PERSONA_DISPLAY:()=>q,PERSONA_KEY_RE:()=>ce,POOL_ALL:()=>P,RESERVED_PERSONA_KEYS:()=>fe,SHRINKAGE_M:()=>ne,UNKNOWN_PERSONA:()=>R,WEIGHTS_FALLBACK_PRIOR_PULLS:()=>oe,applyClusterHeuristic:()=>L,broadestValueCell:()=>_e,candidateLayouts:()=>D,canonicalArm:()=>T,canonicalPersona:()=>V,chooseLayout:()=>Se,chooseLayoutFactored:()=>Me,confidenceBand:()=>Ke,decisionPersona:()=>Be,factorCellsForOrder:()=>we,fnv1a:()=>ie,hashLayout:()=>H,layoutBucketOf:()=>W,marginalArmKey:()=>Ee,normalizeDeclaredPersona:()=>Fe,parseArm:()=>Z,pickDeterministicArm:()=>Ie,pickFromWeights:()=>Re,pooledPosterior:()=>Ce,posteriorOfCounts:()=>A,resolvePersona:()=>$e,sampleArm:()=>z,sampleArmEv:()=>De,sampleBeta:()=>w,shrunkAvgValue:()=>le,shrunkPosterior:()=>C,slotBaselineArm:()=>ae,slotResultFor:()=>Ne,validateSlotDecl:()=>Oe,weightCellsFor:()=>Le});module.exports=Pe(qe);var K=["buyer","researcher","deal_seeker","browser"],R="unknown",q={buyer:"Buyer",researcher:"Researcher",deal_seeker:"Deal seeker",browser:"Browser",unknown:"Unknown"},U={buyers:"buyer",researchers:"researcher","deal-seekers":"deal_seeker",browsers:"browser",buyer:"buyer",researcher:"researcher",deal_seeker:"deal_seeker",browser:"browser"};function V(e){var t;if(e==null)return R;let r=e.trim().toLowerCase();return(t=U[r])!=null?t:R}var ke=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];function k(e,r){return e>>>r|e<<32-r}function re(e){return(e>>>0).toString(16).padStart(8,"0")}function Ae(e){let r=new TextEncoder().encode(e),t=r.length,n=t*8,o=(t+8>>6)+1<<6,s=new Uint8Array(o);s.set(r),s[t]=128;let a=new DataView(s.buffer);a.setUint32(o-8,Math.floor(n/4294967296),!1),a.setUint32(o-4,n>>>0,!1);let i=1779033703,u=3144134277,l=1013904242,f=2773480762,h=1359893119,g=2600822924,p=528734635,y=1541459225,b=new Uint32Array(64);for(let M=0;M<o;M+=64){for(let m=0;m<16;m++)b[m]=a.getUint32(M+m*4,!1);for(let m=16;m<64;m++){let N=k(b[m-15],7)^k(b[m-15],18)^b[m-15]>>>3,I=k(b[m-2],17)^k(b[m-2],19)^b[m-2]>>>10;b[m]=b[m-16]+N+b[m-7]+I>>>0}let c=i,v=u,d=l,E=f,x=h,_=g,S=p,O=y;for(let m=0;m<64;m++){let N=k(x,6)^k(x,11)^k(x,25),I=x&_^~x&S,X=O+N+I+ke[m]+b[m]>>>0,pe=k(c,2)^k(c,13)^k(c,22),ge=c&v^c&d^v&d,be=pe+ge>>>0;O=S,S=_,_=x,x=E+X>>>0,E=d,d=v,v=c,c=X+be>>>0}i=i+c>>>0,u=u+v>>>0,l=l+d>>>0,f=f+E>>>0,h=h+x>>>0,g=g+_>>>0,p=p+S>>>0,y=y+O>>>0}return re(i)+re(u)}function H(e){return Ae(e.join("|"))}var te={buyer:["pricing","cta","hero","comparison","social_proof","trust","features","faq","navigation","generic"],researcher:["features","comparison","faq","hero","trust","social_proof","pricing","cta","navigation","generic"],deal_seeker:["pricing","comparison","social_proof","trust","cta","hero","features","faq","navigation","generic"],browser:["hero","features","social_proof","pricing","cta","trust","faq","comparison","navigation","generic"]};function L(e,r,t,n){let o=te[t];if(!o)return e;let s=o.indexOf("generic"),a=l=>{let f=o.indexOf(l);return f===-1?s:f},i=n?e.filter(l=>n.get(l)!=="structural"):[...e];if(i.sort((l,f)=>{var p,y;let h=(p=r.get(l))!=null?p:"generic",g=(y=r.get(f))!=null?y:"generic";return a(h)-a(g)}),!n)return i;let u=0;return e.map(l=>n.get(l)==="structural"?l:i[u++])}function D(e,r,t,n){let o=new Map;for(let s of[...K,t]){let a=L(e,r,s,n);o.set(H(a),a)}return o}function j(e,r){if(e<1)return j(1+e,r)*Math.pow(Math.max(1e-15,r()),1/e);let t=e-1/3,n=1/Math.sqrt(9*t);for(;;){let o,s;do{let i=Math.max(1e-15,r()),u=r();o=Math.sqrt(-2*Math.log(i))*Math.cos(2*Math.PI*u),s=1+n*o}while(s<=0);s=s*s*s;let a=r();if(a<1-.0331*o*o*o*o||Math.log(a)<.5*o*o+t*(1-s+Math.log(s)))return t*s}}function w(e,r,t=Math.random){let n=j(e,t),o=j(r,t),s=n+o;return s<=0?e/(e+r):n/s}function z(e,r=Math.random){if(e.length===0)return null;if(e.length===1)return e[0].arm;let t=e[0],n=w(t.alpha,t.beta,r);for(let o=1;o<e.length;o++){let s=e[o],a=w(s.alpha,s.beta,r);a>n&&(t=s,n=a)}return t.arm}function Se(e,r,t,n,o=Math.random,s){var f,h;let a=D(e,r,t,s),i=[];for(let g of a.keys()){let p=n.get(g);i.push({arm:g,alpha:(f=p==null?void 0:p.alpha)!=null?f:1,beta:(h=p==null?void 0:p.beta)!=null?h:1})}let u=z(i,o),l=u?a.get(u):void 0;return l!=null?l:L(e,r,t,s)}var ne=20;function C(e,r,t=20){let n=r.alpha+r.beta;if(n<=0||t<=0)return{alpha:e.alpha,beta:e.beta};let o=r.alpha/n,s=t*n/(n+t);return{alpha:e.alpha+s*o,beta:e.beta+s*(1-o)}}var oe=5;function Re(e,r){var n,o;let t=null;for(let s of e){if(!r.includes(s.variantId))continue;let a=(n=s.pulls)!=null?n:0,i=a>0?a*s.avgReward/(a+oe):0;(!t||i>t.score)&&(t={variantId:s.variantId,score:i})}return(o=t==null?void 0:t.variantId)!=null?o:null}var P="__all__",$={exposures:0,conversions:0};function A(e){return{alpha:e.conversions+1,beta:Math.max(0,e.exposures-e.conversions)+1}}function Ce(e,r,t=20){var g,p,y,b;let n=(g=e.segment)!=null?g:$,o=(p=e.global)!=null?p:$,s=A(o),a=C(A(n),s,t);if(!r)return a;let i=(y=e.persona)!=null?y:$,u=(b=e.child)!=null?b:$,l=C(A(i),s,t),f=(n.exposures+1)/(n.exposures+i.exposures+2),h={alpha:f*a.alpha+(1-f)*l.alpha,beta:f*a.beta+(1-f)*l.beta};return C(A(u),h,t)}function _e(e){var n,o;let r=null,t=-1;for(let s of e){let a=s.segment===P,i=s.persona===P,u=a&&i?3:a||i?2:1;u>t&&(t=u,r=s)}return{valueSum:(n=r==null?void 0:r.valueSum)!=null?n:0,valueCount:(o=r==null?void 0:r.valueCount)!=null?o:0}}function Le(e,r){return r==="unknown"||r===P||r===""?[{segment:e,persona:P},{segment:P,persona:P}]:[{segment:e,persona:r},{segment:e,persona:P},{segment:P,persona:r},{segment:P,persona:P}]}var Y=4,se="__global__";function W(e,r){return r<=0?0:Math.min(Y-1,Math.floor(e*Y/r))}function we(e,r){return e.map((t,n)=>{var o;return{parent:(o=r.get(t))!=null?o:"generic",bucket:W(n,e.length)}})}var G=(e,r)=>`${e}#${r}`;function Me(e,r,t,n,o=Math.random,s){var M;let a=D(e,r,t,s);if(a.size===0)return L(e,r,t,s);let i=new Map,u=new Map,l=0,f=0;for(let c of n)c.persona===se?(i.set(G(c.parent,c.bucket),c),l+=c.exposures,f+=c.conversions):c.persona===t&&u.set(G(c.parent,c.bucket),c);let h=A({exposures:l,conversions:f}),g=new Map,p=(c,v)=>{var N,I;let d=G(c,v),E=g.get(d);if(E!==void 0)return E;let x=i.get(d),_=C(A({exposures:(N=x==null?void 0:x.exposures)!=null?N:0,conversions:(I=x==null?void 0:x.conversions)!=null?I:0}),h),S=u.get(d),O=S?C(A({exposures:S.exposures,conversions:S.conversions}),_):_,m=w(O.alpha,O.beta,o);return g.set(d,m),m},y=null,b=-1/0;for(let c of a.values()){let v=0;for(let d=0;d<c.length;d++)v+=p((M=r.get(c[d]))!=null?M:"generic",W(d,c.length));v>b&&(b=v,y=c)}return y!=null?y:L(e,r,t,s)}function T(e){return Object.keys(e).sort().map(r=>`${r}=${e[r]}`).join("|")}function Z(e){if(e.length===0)return null;let r={};for(let t of e.split("|")){let n=t.indexOf("=");if(n<=0||n!==t.lastIndexOf("=")||n===t.length-1)return null;let o=t.slice(0,n);if(o in r)return null;r[o]=t.slice(n+1)}return r}function Ee(e,r){return`${e}=${r}`}function ae(e){var t,n,o;if(e.arms)return typeof e.baseline=="string"?e.baseline:(t=e.arms[0])!=null?t:"";if(e.baseline!==void 0&&typeof e.baseline=="object")return T(e.baseline);if(typeof e.baseline=="string")return e.baseline;let r={};for(let[s,a]of Object.entries((n=e.dims)!=null?n:{}))r[s]=(o=a[0])!=null?o:"";return T(r)}function Oe(e){let r=Array.isArray(e.arms),t=e.dims!=null;if(r&&t)return{ok:!1,reason:"declare exactly one of arms or dims (got both)"};if(!r&&!t)return{ok:!1,reason:"declare exactly one of arms or dims (got neither)"};if(r){let s=e.arms;if(s.length<2)return{ok:!1,reason:"arms requires at least 2 entries"};if(s.length>12)return{ok:!1,reason:"arms allows at most 12 entries"};if(new Set(s).size!==s.length)return{ok:!1,reason:"arms must be unique"};if(s.some(a=>a.includes("=")))return{ok:!1,reason:"enumerated arm ids may not contain '=' (reserved for dims encoding)"};if(e.baseline!==void 0){if(typeof e.baseline!="string")return{ok:!1,reason:"baseline for an arms slot must be a string"};if(!s.includes(e.baseline))return{ok:!1,reason:"baseline must be one of the declared arms"}}return{ok:!0}}let n=Object.entries(e.dims);if(n.length<1)return{ok:!1,reason:"dims requires at least 1 dimension"};if(n.length>4)return{ok:!1,reason:"dims allows at most 4 dimensions"};let o=1;for(let[s,a]of n){if(a.length<2)return{ok:!1,reason:`dim "${s}" requires at least 2 values`};if(a.length>6)return{ok:!1,reason:`dim "${s}" allows at most 6 values`};if(new Set(a).size!==a.length)return{ok:!1,reason:`dim "${s}" has duplicate values`};o*=a.length}if(o>64)return{ok:!1,reason:`declared space of ${o} combinations exceeds the 64 maximum`};if(e.baseline!==void 0){if(typeof e.baseline=="string")return{ok:!1,reason:"baseline for a dims slot must be a per-dim record"};let s=e.baseline,a=n.map(([u])=>u).sort(),i=Object.keys(s).sort();if(a.join(" ")!==i.join(" "))return{ok:!1,reason:"baseline must set every declared dim exactly once"};for(let[u,l]of n)if(!l.includes(s[u]))return{ok:!1,reason:`baseline value for dim "${u}" is not declared`}}return{ok:!0}}function Ne(e,r){var t,n;return e.dims!=null?(n=(t=Z(r))!=null?t:Z(ae(e)))!=null?n:{}:r}function ie(e){let r=2166136261;for(let t=0;t<e.length;t++)r^=e.charCodeAt(t),r=r+((r<<1)+(r<<4)+(r<<7)+(r<<8)+(r<<24))>>>0;return r}function Ie(e,r,t){if(t.length===0)throw new Error("pickDeterministicArm requires at least one arm");let n=[...t].sort();return n[ie(`${e}:${r}`)%n.length]}function Ke(e){return e>=.3?e<.7?"medium":"high":"low"}var ue=20;function le(e,r,t=ue){let n=e.valueCount>0?e.valueSum/e.valueCount:0;if(r<=0)return n;if(e.valueCount<=0)return r;let o=e.valueCount/(e.valueCount+t);return o*n+(1-o)*r}function De(e,r,t=Math.random){if(e.length===0)return null;if(e.length===1)return e[0].arm;let n=null,o=-1/0;for(let s of e){let a=w(s.alpha,s.beta,t)*le(s,r);a>o&&(n=s,o=a)}return n.arm}var ce=/^[a-z0-9][a-z0-9_-]{0,31}$/;function Fe(e){if(typeof e!="string")return null;let r=e.trim().toLowerCase();return r&&ce.test(r)?r:null}var fe=["unknown","__all__","buyers","researchers","deal-seekers","browsers"],me=K.map(e=>({key:e,displayName:q[e]})),Ue=64;function Ve(e){var t;let r=new Map;for(let n of e)if(n.status!=="retired"){r.set(n.key,n.key);for(let o of(t=n.aliases)!=null?t:[])fe.includes(o)||r.set(o,n.key)}return r}function $e(e,r=me){var i,u,l;let t=Ve(r),n=(i=e.inferredConfidence)!=null?i:0,o,s=(l=(u=e.declared)==null?void 0:u.trim().toLowerCase())!=null?l:"";if(s!==""){let f=t.get(s),h=f===void 0?t.get(V(s)):void 0,g=f!=null?f:h;if(g!==void 0)return{persona:g,source:"declared",confidence:1};o=s.slice(0,Ue)}let a=V(e.clusterLabel);return a!==R&&t.has(a)?B({persona:t.get(a),source:"inferred",confidence:n},o!==void 0&&{unrecognizedDeclared:o}):B({persona:R,source:"none",confidence:n},o!==void 0&&{unrecognizedDeclared:o})}function Be(e){var t;if(e==null)return R;let r=e.trim().toLowerCase();return r===""?R:(t=U[r])!=null?t:r}0&&(module.exports={CLUSTER_PRIORITY,DEFAULT_PERSONA_VOCABULARY,EV_SHRINK_K,GLOBAL_FACTOR_PERSONA,LAYOUT_FACTOR_BUCKETS,LEGACY_PERSONA_MAP,PERSONAS,PERSONA_DISPLAY,PERSONA_KEY_RE,POOL_ALL,RESERVED_PERSONA_KEYS,SHRINKAGE_M,UNKNOWN_PERSONA,WEIGHTS_FALLBACK_PRIOR_PULLS,applyClusterHeuristic,broadestValueCell,candidateLayouts,canonicalArm,canonicalPersona,chooseLayout,chooseLayoutFactored,confidenceBand,decisionPersona,factorCellsForOrder,fnv1a,hashLayout,layoutBucketOf,marginalArmKey,normalizeDeclaredPersona,parseArm,pickDeterministicArm,pickFromWeights,pooledPosterior,posteriorOfCounts,resolvePersona,sampleArm,sampleArmEv,sampleBeta,shrunkAvgValue,shrunkPosterior,slotBaselineArm,slotResultFor,validateSlotDecl,weightCellsFor});
1
+ "use strict";var I=Object.defineProperty;var ye=Object.getOwnPropertyDescriptor;var ve=Object.getOwnPropertyNames,re=Object.getOwnPropertySymbols;var ne=Object.prototype.hasOwnProperty,Ae=Object.prototype.propertyIsEnumerable;var te=(e,r,t)=>r in e?I(e,r,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[r]=t,Y=(e,r)=>{for(var t in r||(r={}))ne.call(r,t)&&te(e,t,r[t]);if(re)for(var t of re(r))Ae.call(r,t)&&te(e,t,r[t]);return e};var _e=(e,r)=>{for(var t in r)I(e,t,{get:r[t],enumerable:!0})},Se=(e,r,t,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of ve(r))!ne.call(e,o)&&o!==t&&I(e,o,{get:()=>r[o],enumerable:!(n=ye(r,o))||n.enumerable});return e};var Pe=e=>Se(I({},"__esModule",{value:!0}),e);var er={};_e(er,{CLUSTER_PRIORITY:()=>we,DEFAULT_PERSONA_VOCABULARY:()=>de,EV_SHRINK_K:()=>fe,GLOBAL_FACTOR_LEVEL:()=>U,GLOBAL_FACTOR_PERSONA:()=>le,LAYOUT_ARCHETYPES:()=>N,LAYOUT_ARCHETYPE_NAMES:()=>$,LAYOUT_FACTOR_BUCKETS:()=>z,LEGACY_PERSONA_MAP:()=>T,PERSONAS:()=>ke,PERSONA_DISPLAY:()=>Ce,PERSONA_KEY_RE:()=>me,POOL_ALL:()=>P,RESERVED_PERSONA_KEYS:()=>ge,SHRINKAGE_M:()=>ae,SLOT_FACTORS:()=>De,UNKNOWN_PERSONA:()=>R,WEIGHTS_FALLBACK_PRIOR_PULLS:()=>ie,applyClusterHeuristic:()=>Ee,broadestValueCell:()=>Ke,candidateLayouts:()=>K,canonicalArm:()=>J,canonicalPersona:()=>D,chooseLayout:()=>Oe,chooseLayoutFactored:()=>Te,chooseSlotArmFactored:()=>Ve,confidenceBand:()=>Ge,decisionPersona:()=>Qe,factorCellsForOrder:()=>Ie,factorCellsForTrial:()=>$e,factorLevelsFor:()=>X,fnv1a:()=>ce,hashLayout:()=>V,layoutBucketOf:()=>W,marginalArmKey:()=>Ye,normalizeDeclaredPersona:()=>We,orderByArchetype:()=>H,parseArm:()=>Q,pickDeterministicArm:()=>je,pickFromWeights:()=>Fe,pooledPosterior:()=>Ne,posteriorOfCounts:()=>_,previewOrderForPersona:()=>se,resolvePersona:()=>Je,sampleArm:()=>j,sampleArmEv:()=>ze,sampleBeta:()=>w,shrunkAvgValue:()=>pe,shrunkPosterior:()=>S,slotBaselineArm:()=>ue,slotResultFor:()=>qe,validateSlotDecl:()=>He,visitLevel:()=>Be,weightCellsFor:()=>Ue});module.exports=Pe(er);var ke=["buyer","researcher","deal_seeker","browser"],R="unknown",Ce={buyer:"Buyer",researcher:"Researcher",deal_seeker:"Deal seeker",browser:"Browser",unknown:"Unknown"},T={buyers:"buyer",researchers:"researcher","deal-seekers":"deal_seeker",browsers:"browser",buyer:"buyer",researcher:"researcher",deal_seeker:"deal_seeker",browser:"browser"};function D(e){var t;if(e==null)return R;let r=e.trim().toLowerCase();return(t=T[r])!=null?t:R}var Re=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];function L(e,r){return e>>>r|e<<32-r}function oe(e){return(e>>>0).toString(16).padStart(8,"0")}function Le(e){let r=new TextEncoder().encode(e),t=r.length,n=t*8,o=(t+8>>6)+1<<6,s=new Uint8Array(o);s.set(r),s[t]=128;let a=new DataView(s.buffer);a.setUint32(o-8,Math.floor(n/4294967296),!1),a.setUint32(o-4,n>>>0,!1);let l=1779033703,u=3144134277,f=1013904242,m=2773480762,y=1359893119,d=2600822924,b=528734635,c=1541459225,p=new Uint32Array(64);for(let A=0;A<o;A+=64){for(let g=0;g<16;g++)p[g]=a.getUint32(A+g*4,!1);for(let g=16;g<64;g++){let O=L(p[g-15],7)^L(p[g-15],18)^p[g-15]>>>3,F=L(p[g-2],17)^L(p[g-2],19)^p[g-2]>>>10;p[g]=p[g-16]+O+p[g-7]+F>>>0}let i=l,v=u,x=f,k=m,h=y,C=d,M=b,E=c;for(let g=0;g<64;g++){let O=L(h,6)^L(h,11)^L(h,25),F=h&C^~h&M,ee=E+O+F+Re[g]+p[g]>>>0,be=L(i,2)^L(i,13)^L(i,22),xe=i&v^i&x^v&x,he=be+xe>>>0;E=M,M=C,C=h,h=k+ee>>>0,k=x,x=v,v=i,i=ee+he>>>0}l=l+i>>>0,u=u+v>>>0,f=f+x>>>0,m=m+k>>>0,y=y+h>>>0,d=d+C>>>0,b=b+M>>>0,c=c+E>>>0}return oe(l)+oe(u)}function V(e){return Le(e.join("|"))}var N={conversion_led:["pricing","cta","hero","comparison","social_proof","trust","features","faq","navigation","generic"],evidence_led:["features","comparison","faq","hero","trust","social_proof","pricing","cta","navigation","generic"],price_led:["pricing","comparison","social_proof","trust","cta","hero","features","faq","navigation","generic"],discovery_led:["hero","features","social_proof","pricing","cta","trust","faq","comparison","navigation","generic"]},$=["conversion_led","evidence_led","price_led","discovery_led"],we={buyer:[...N.conversion_led],researcher:[...N.evidence_led],deal_seeker:[...N.price_led],browser:[...N.discovery_led]};function H(e,r,t,n){let o=N[t];if(!o)return e;let s=o.indexOf("generic"),a=f=>{let m=o.indexOf(f);return m===-1?s:m},l=n?e.filter(f=>n.get(f)!=="structural"):[...e];if(l.sort((f,m)=>{var b,c;let y=(b=r.get(f))!=null?b:"generic",d=(c=r.get(m))!=null?c:"generic";return a(y)-a(d)}),!n)return l;let u=0;return e.map(f=>n.get(f)==="structural"?f:l[u++])}function Me(e){let r=2166136261;for(let t=0;t<e.length;t++)r^=e.charCodeAt(t),r=Math.imul(r,16777619)>>>0;return r>>>0}function se(e,r,t,n){if(!t||t==="unknown")return e;let o=$[Me(t)%$.length];return H(e,r,o,n)}var Ee=se;function K(e,r,t){let n=new Map;n.set(V(e),[...e]);for(let o of $){let s=H(e,r,o,t),a=V(s);n.has(a)||n.set(a,s)}return n}function q(e,r){if(e<1)return q(1+e,r)*Math.pow(Math.max(1e-15,r()),1/e);let t=e-1/3,n=1/Math.sqrt(9*t);for(;;){let o,s;do{let l=Math.max(1e-15,r()),u=r();o=Math.sqrt(-2*Math.log(l))*Math.cos(2*Math.PI*u),s=1+n*o}while(s<=0);s=s*s*s;let a=r();if(a<1-.0331*o*o*o*o||Math.log(a)<.5*o*o+t*(1-s+Math.log(s)))return t*s}}function w(e,r,t=Math.random){let n=q(e,t),o=q(r,t),s=n+o;return s<=0?e/(e+r):n/s}function j(e,r=Math.random){if(e.length===0)return null;if(e.length===1)return e[0].arm;let t=e[0],n=w(t.alpha,t.beta,r);for(let o=1;o<e.length;o++){let s=e[o],a=w(s.alpha,s.beta,r);a>n&&(t=s,n=a)}return t.arm}function Oe(e,r,t,n,o=Math.random,s){var m,y;let a=K(e,r,s),l=[];for(let d of a.keys()){let b=n.get(d);l.push({arm:d,alpha:(m=b==null?void 0:b.alpha)!=null?m:1,beta:(y=b==null?void 0:b.beta)!=null?y:1})}let u=j(l,o),f=u?a.get(u):void 0;return f!=null?f:e}var ae=20;function S(e,r,t=20){let n=r.alpha+r.beta;if(n<=0||t<=0)return{alpha:e.alpha,beta:e.beta};let o=r.alpha/n,s=t*n/(n+t);return{alpha:e.alpha+s*o,beta:e.beta+s*(1-o)}}var ie=5;function Fe(e,r){var n,o;let t=null;for(let s of e){if(!r.includes(s.variantId))continue;let a=(n=s.pulls)!=null?n:0,l=a>0?a*s.avgReward/(a+ie):0;(!t||l>t.score)&&(t={variantId:s.variantId,score:l})}return(o=t==null?void 0:t.variantId)!=null?o:null}var P="__all__",B={exposures:0,conversions:0};function _(e){return{alpha:e.conversions+1,beta:Math.max(0,e.exposures-e.conversions)+1}}function Ne(e,r,t=20){var d,b,c,p;let n=(d=e.segment)!=null?d:B,o=(b=e.global)!=null?b:B,s=_(o),a=S(_(n),s,t);if(!r)return a;let l=(c=e.persona)!=null?c:B,u=(p=e.child)!=null?p:B,f=S(_(l),s,t),m=(n.exposures+1)/(n.exposures+l.exposures+2),y={alpha:m*a.alpha+(1-m)*f.alpha,beta:m*a.beta+(1-m)*f.beta};return S(_(u),y,t)}function Ke(e){var n,o;let r=null,t=-1;for(let s of e){let a=s.segment===P,l=s.persona===P,u=a&&l?3:a||l?2:1;u>t&&(t=u,r=s)}return{valueSum:(n=r==null?void 0:r.valueSum)!=null?n:0,valueCount:(o=r==null?void 0:r.valueCount)!=null?o:0}}function Ue(e,r){return r==="unknown"||r===P||r===""?[{segment:e,persona:P},{segment:P,persona:P}]:[{segment:e,persona:r},{segment:e,persona:P},{segment:P,persona:r},{segment:P,persona:P}]}var z=4,le="__global__";function W(e,r){return r<=0?0:Math.min(z-1,Math.floor(e*z/r))}function Ie(e,r){return e.map((t,n)=>{var o;return{parent:(o=r.get(t))!=null?o:"generic",bucket:W(n,e.length)}})}var G=(e,r)=>`${e}#${r}`;function Te(e,r,t,n,o=Math.random,s){var A;let a=K(e,r,s),l=new Map,u=new Map,f=0,m=0;for(let i of n)i.persona===le?(l.set(G(i.parent,i.bucket),i),f+=i.exposures,m+=i.conversions):i.persona===t&&u.set(G(i.parent,i.bucket),i);let y=_({exposures:f,conversions:m}),d=new Map,b=(i,v)=>{var O,F;let x=G(i,v),k=d.get(x);if(k!==void 0)return k;let h=l.get(x),C=S(_({exposures:(O=h==null?void 0:h.exposures)!=null?O:0,conversions:(F=h==null?void 0:h.conversions)!=null?F:0}),y),M=u.get(x),E=M?S(_({exposures:M.exposures,conversions:M.conversions}),C):C,g=w(E.alpha,E.beta,o);return d.set(x,g),g},c=null,p=-1/0;for(let i of a.values()){let v=0;for(let x=0;x<i.length;x++)v+=b((A=r.get(i[x]))!=null?A:"generic",W(x,i.length));v>p&&(p=v,c=i)}return c!=null?c:e}var U="__global__",De=["device","source","persona","visit"];function X(e){let r=[];return e.device&&r.push({factor:"device",level:e.device}),e.source&&r.push({factor:"source",level:e.source}),e.persona&&e.persona!==R&&r.push({factor:"persona",level:e.persona}),e.visit&&r.push({factor:"visit",level:e.visit}),r}var Z=(e,r,t)=>`${e}\0${r}\0${t}`;function Ve(e,r,t,n=Math.random){if(e.length===0)return null;if(e.length===1)return{arm:e[0],factorsUsed:0};let o=new Map,s=0,a=0;for(let c of t)o.set(Z(c.arm,c.factor,c.level),c),c.level===U&&(s+=c.exposures,a+=c.conversions);let l=_({exposures:s,conversions:a}),u=X(r),f=new Map,m=c=>{var A,i;let p=o.get(Z(c,"global",U));return S(_({exposures:(A=p==null?void 0:p.exposures)!=null?A:0,conversions:(i=p==null?void 0:p.conversions)!=null?i:0}),l)},y=(c,p,A)=>{let i=Z(c,p,A),v=f.get(i);if(v!==void 0)return v;let x=m(c),k=o.get(i),h=k?S(_({exposures:k.exposures,conversions:k.conversions}),x):x,C=w(h.alpha,h.beta,n);return f.set(i,C),C},d=null,b=-1/0;for(let c of e){let p=u.length===0?y(c,"global",U):0;for(let{factor:A,level:i}of u)p+=y(c,A,i);p>b&&(b=p,d=c)}return d===null?null:{arm:d,factorsUsed:u.length}}function $e(e,r,t){let n=Number.isFinite(t)?Math.max(0,Math.min(1,t)):0,o=[{arm:e,factor:"global",level:U,weight:1}];for(let{factor:s,level:a}of X(r))o.push({arm:e,factor:s,level:a,weight:s==="persona"?n:1});return o}function Be(e){return e==null||!Number.isFinite(e)||e<1?null:e>1?"returning":"new"}function J(e){return Object.keys(e).sort().map(r=>`${r}=${e[r]}`).join("|")}function Q(e){if(e.length===0)return null;let r={};for(let t of e.split("|")){let n=t.indexOf("=");if(n<=0||n!==t.lastIndexOf("=")||n===t.length-1)return null;let o=t.slice(0,n);if(o in r)return null;r[o]=t.slice(n+1)}return r}function Ye(e,r){return`${e}=${r}`}function ue(e){var t,n,o;if(e.arms)return typeof e.baseline=="string"?e.baseline:(t=e.arms[0])!=null?t:"";if(e.baseline!==void 0&&typeof e.baseline=="object")return J(e.baseline);if(typeof e.baseline=="string")return e.baseline;let r={};for(let[s,a]of Object.entries((n=e.dims)!=null?n:{}))r[s]=(o=a[0])!=null?o:"";return J(r)}function He(e){let r=Array.isArray(e.arms),t=e.dims!=null;if(r&&t)return{ok:!1,reason:"declare exactly one of arms or dims (got both)"};if(!r&&!t)return{ok:!1,reason:"declare exactly one of arms or dims (got neither)"};if(r){let s=e.arms;if(s.length<2)return{ok:!1,reason:"arms requires at least 2 entries"};if(s.length>12)return{ok:!1,reason:"arms allows at most 12 entries"};if(new Set(s).size!==s.length)return{ok:!1,reason:"arms must be unique"};if(s.some(a=>a.includes("=")))return{ok:!1,reason:"enumerated arm ids may not contain '=' (reserved for dims encoding)"};if(e.baseline!==void 0){if(typeof e.baseline!="string")return{ok:!1,reason:"baseline for an arms slot must be a string"};if(!s.includes(e.baseline))return{ok:!1,reason:"baseline must be one of the declared arms"}}return{ok:!0}}let n=Object.entries(e.dims);if(n.length<1)return{ok:!1,reason:"dims requires at least 1 dimension"};if(n.length>4)return{ok:!1,reason:"dims allows at most 4 dimensions"};let o=1;for(let[s,a]of n){if(a.length<2)return{ok:!1,reason:`dim "${s}" requires at least 2 values`};if(a.length>6)return{ok:!1,reason:`dim "${s}" allows at most 6 values`};if(new Set(a).size!==a.length)return{ok:!1,reason:`dim "${s}" has duplicate values`};o*=a.length}if(o>64)return{ok:!1,reason:`declared space of ${o} combinations exceeds the 64 maximum`};if(e.baseline!==void 0){if(typeof e.baseline=="string")return{ok:!1,reason:"baseline for a dims slot must be a per-dim record"};let s=e.baseline,a=n.map(([u])=>u).sort(),l=Object.keys(s).sort();if(a.join(" ")!==l.join(" "))return{ok:!1,reason:"baseline must set every declared dim exactly once"};for(let[u,f]of n)if(!f.includes(s[u]))return{ok:!1,reason:`baseline value for dim "${u}" is not declared`}}return{ok:!0}}function qe(e,r){var t,n;return e.dims!=null?(n=(t=Q(r))!=null?t:Q(ue(e)))!=null?n:{}:r}function ce(e){let r=2166136261;for(let t=0;t<e.length;t++)r^=e.charCodeAt(t),r=r+((r<<1)+(r<<4)+(r<<7)+(r<<8)+(r<<24))>>>0;return r}function je(e,r,t){if(t.length===0)throw new Error("pickDeterministicArm requires at least one arm");let n=[...t].sort();return n[ce(`${e}:${r}`)%n.length]}function Ge(e){return e>=.3?e<.7?"medium":"high":"low"}var fe=20;function pe(e,r,t=fe){let n=e.valueCount>0?e.valueSum/e.valueCount:0;if(r<=0)return n;if(e.valueCount<=0)return r;let o=e.valueCount/(e.valueCount+t);return o*n+(1-o)*r}function ze(e,r,t=Math.random){if(e.length===0)return null;if(e.length===1)return e[0].arm;let n=null,o=-1/0;for(let s of e){let a=w(s.alpha,s.beta,t)*pe(s,r);a>o&&(n=s,o=a)}return n.arm}var me=/^[a-z0-9][a-z0-9_-]{0,31}$/;function We(e){if(typeof e!="string")return null;let r=e.trim().toLowerCase();return r&&me.test(r)?r:null}var ge=["unknown","__all__","buyers","researchers","deal-seekers","browsers"],de=[],Ze=64;function Xe(e){var t;let r=new Map;for(let n of e)if(n.status!=="retired"){r.set(n.key,n.key);for(let o of(t=n.aliases)!=null?t:[])ge.includes(o)||r.set(o,n.key)}return r}function Je(e,r=de){var l,u,f;let t=Xe(r),n=(l=e.inferredConfidence)!=null?l:0,o,s=(f=(u=e.declared)==null?void 0:u.trim().toLowerCase())!=null?f:"";if(s!==""){let m=t.get(s),y=m===void 0?t.get(D(s)):void 0,d=m!=null?m:y;if(d!==void 0)return{persona:d,source:"declared",confidence:1};o=s.slice(0,Ze)}let a=D(e.clusterLabel);return a!==R&&t.has(a)?Y({persona:t.get(a),source:"inferred",confidence:n},o!==void 0&&{unrecognizedDeclared:o}):Y({persona:R,source:"none",confidence:n},o!==void 0&&{unrecognizedDeclared:o})}function Qe(e){var t;if(e==null)return R;let r=e.trim().toLowerCase();return r===""?R:(t=T[r])!=null?t:r}0&&(module.exports={CLUSTER_PRIORITY,DEFAULT_PERSONA_VOCABULARY,EV_SHRINK_K,GLOBAL_FACTOR_LEVEL,GLOBAL_FACTOR_PERSONA,LAYOUT_ARCHETYPES,LAYOUT_ARCHETYPE_NAMES,LAYOUT_FACTOR_BUCKETS,LEGACY_PERSONA_MAP,PERSONAS,PERSONA_DISPLAY,PERSONA_KEY_RE,POOL_ALL,RESERVED_PERSONA_KEYS,SHRINKAGE_M,SLOT_FACTORS,UNKNOWN_PERSONA,WEIGHTS_FALLBACK_PRIOR_PULLS,applyClusterHeuristic,broadestValueCell,candidateLayouts,canonicalArm,canonicalPersona,chooseLayout,chooseLayoutFactored,chooseSlotArmFactored,confidenceBand,decisionPersona,factorCellsForOrder,factorCellsForTrial,factorLevelsFor,fnv1a,hashLayout,layoutBucketOf,marginalArmKey,normalizeDeclaredPersona,orderByArchetype,parseArm,pickDeterministicArm,pickFromWeights,pooledPosterior,posteriorOfCounts,previewOrderForPersona,resolvePersona,sampleArm,sampleArmEv,sampleBeta,shrunkAvgValue,shrunkPosterior,slotBaselineArm,slotResultFor,validateSlotDecl,visitLevel,weightCellsFor});
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{a as U}from"./chunk-HBG7RQ56.mjs";var K=["buyer","researcher","deal_seeker","browser"],C="unknown",j={buyer:"Buyer",researcher:"Researcher",deal_seeker:"Deal seeker",browser:"Browser",unknown:"Unknown"},V={buyers:"buyer",researchers:"researcher","deal-seekers":"deal_seeker",browsers:"browser",buyer:"buyer",researcher:"researcher",deal_seeker:"deal_seeker",browser:"browser"};function $(e){var t;if(e==null)return C;let r=e.trim().toLowerCase();return(t=V[r])!=null?t:C}var te=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];function P(e,r){return e>>>r|e<<32-r}function z(e){return(e>>>0).toString(16).padStart(8,"0")}function ne(e){let r=new TextEncoder().encode(e),t=r.length,n=t*8,s=(t+8>>6)+1<<6,o=new Uint8Array(s);o.set(r),o[t]=128;let a=new DataView(o.buffer);a.setUint32(s-8,Math.floor(n/4294967296),!1),a.setUint32(s-4,n>>>0,!1);let i=1779033703,u=3144134277,l=1013904242,f=2773480762,h=1359893119,g=2600822924,p=528734635,y=1541459225,b=new Uint32Array(64);for(let L=0;L<s;L+=64){for(let m=0;m<16;m++)b[m]=a.getUint32(L+m*4,!1);for(let m=16;m<64;m++){let E=P(b[m-15],7)^P(b[m-15],18)^b[m-15]>>>3,O=P(b[m-2],17)^P(b[m-2],19)^b[m-2]>>>10;b[m]=b[m-16]+E+b[m-7]+O>>>0}let c=i,v=u,d=l,w=f,x=h,R=g,A=p,M=y;for(let m=0;m<64;m++){let E=P(x,6)^P(x,11)^P(x,25),O=x&R^~x&A,H=M+E+O+te[m]+b[m]>>>0,Q=P(c,2)^P(c,13)^P(c,22),ee=c&v^c&d^v&d,re=Q+ee>>>0;M=A,A=R,R=x,x=w+H>>>0,w=d,d=v,v=c,c=H+re>>>0}i=i+c>>>0,u=u+v>>>0,l=l+d>>>0,f=f+w>>>0,h=h+x>>>0,g=g+R>>>0,p=p+A>>>0,y=y+M>>>0}return z(i)+z(u)}function G(e){return ne(e.join("|"))}var oe={buyer:["pricing","cta","hero","comparison","social_proof","trust","features","faq","navigation","generic"],researcher:["features","comparison","faq","hero","trust","social_proof","pricing","cta","navigation","generic"],deal_seeker:["pricing","comparison","social_proof","trust","cta","hero","features","faq","navigation","generic"],browser:["hero","features","social_proof","pricing","cta","trust","faq","comparison","navigation","generic"]};function N(e,r,t,n){let s=oe[t];if(!s)return e;let o=s.indexOf("generic"),a=l=>{let f=s.indexOf(l);return f===-1?o:f},i=n?e.filter(l=>n.get(l)!=="structural"):[...e];if(i.sort((l,f)=>{var p,y;let h=(p=r.get(l))!=null?p:"generic",g=(y=r.get(f))!=null?y:"generic";return a(h)-a(g)}),!n)return i;let u=0;return e.map(l=>n.get(l)==="structural"?l:i[u++])}function D(e,r,t,n){let s=new Map;for(let o of[...K,t]){let a=N(e,r,o,n);s.set(G(a),a)}return s}function B(e,r){if(e<1)return B(1+e,r)*Math.pow(Math.max(1e-15,r()),1/e);let t=e-1/3,n=1/Math.sqrt(9*t);for(;;){let s,o;do{let i=Math.max(1e-15,r()),u=r();s=Math.sqrt(-2*Math.log(i))*Math.cos(2*Math.PI*u),o=1+n*s}while(o<=0);o=o*o*o;let a=r();if(a<1-.0331*s*s*s*s||Math.log(a)<.5*s*s+t*(1-o+Math.log(o)))return t*o}}function I(e,r,t=Math.random){let n=B(e,t),s=B(r,t),o=n+s;return o<=0?e/(e+r):n/o}function Y(e,r=Math.random){if(e.length===0)return null;if(e.length===1)return e[0].arm;let t=e[0],n=I(t.alpha,t.beta,r);for(let s=1;s<e.length;s++){let o=e[s],a=I(o.alpha,o.beta,r);a>n&&(t=o,n=a)}return t.arm}function Se(e,r,t,n,s=Math.random,o){var f,h;let a=D(e,r,t,o),i=[];for(let g of a.keys()){let p=n.get(g);i.push({arm:g,alpha:(f=p==null?void 0:p.alpha)!=null?f:1,beta:(h=p==null?void 0:p.beta)!=null?h:1})}let u=Y(i,s),l=u?a.get(u):void 0;return l!=null?l:N(e,r,t,o)}var W=20;function _(e,r,t=20){let n=r.alpha+r.beta;if(n<=0||t<=0)return{alpha:e.alpha,beta:e.beta};let s=r.alpha/n,o=t*n/(n+t);return{alpha:e.alpha+o*s,beta:e.beta+o*(1-s)}}var se=5;function Ce(e,r){var n,s;let t=null;for(let o of e){if(!r.includes(o.variantId))continue;let a=(n=o.pulls)!=null?n:0,i=a>0?a*o.avgReward/(a+se):0;(!t||i>t.score)&&(t={variantId:o.variantId,score:i})}return(s=t==null?void 0:t.variantId)!=null?s:null}var k="__all__",F={exposures:0,conversions:0};function S(e){return{alpha:e.conversions+1,beta:Math.max(0,e.exposures-e.conversions)+1}}function we(e,r,t=20){var g,p,y,b;let n=(g=e.segment)!=null?g:F,s=(p=e.global)!=null?p:F,o=S(s),a=_(S(n),o,t);if(!r)return a;let i=(y=e.persona)!=null?y:F,u=(b=e.child)!=null?b:F,l=_(S(i),o,t),f=(n.exposures+1)/(n.exposures+i.exposures+2),h={alpha:f*a.alpha+(1-f)*l.alpha,beta:f*a.beta+(1-f)*l.beta};return _(S(u),h,t)}function Me(e){var n,s;let r=null,t=-1;for(let o of e){let a=o.segment===k,i=o.persona===k,u=a&&i?3:a||i?2:1;u>t&&(t=u,r=o)}return{valueSum:(n=r==null?void 0:r.valueSum)!=null?n:0,valueCount:(s=r==null?void 0:r.valueCount)!=null?s:0}}function Ee(e,r){return r==="unknown"||r===k||r===""?[{segment:e,persona:k},{segment:k,persona:k}]:[{segment:e,persona:r},{segment:e,persona:k},{segment:k,persona:r},{segment:k,persona:k}]}var T=4,ae="__global__";function Z(e,r){return r<=0?0:Math.min(T-1,Math.floor(e*T/r))}function Fe(e,r){return e.map((t,n)=>{var s;return{parent:(s=r.get(t))!=null?s:"generic",bucket:Z(n,e.length)}})}var q=(e,r)=>`${e}#${r}`;function Ue(e,r,t,n,s=Math.random,o){var L;let a=D(e,r,t,o);if(a.size===0)return N(e,r,t,o);let i=new Map,u=new Map,l=0,f=0;for(let c of n)c.persona===ae?(i.set(q(c.parent,c.bucket),c),l+=c.exposures,f+=c.conversions):c.persona===t&&u.set(q(c.parent,c.bucket),c);let h=S({exposures:l,conversions:f}),g=new Map,p=(c,v)=>{var E,O;let d=q(c,v),w=g.get(d);if(w!==void 0)return w;let x=i.get(d),R=_(S({exposures:(E=x==null?void 0:x.exposures)!=null?E:0,conversions:(O=x==null?void 0:x.conversions)!=null?O:0}),h),A=u.get(d),M=A?_(S({exposures:A.exposures,conversions:A.conversions}),R):R,m=I(M.alpha,M.beta,s);return g.set(d,m),m},y=null,b=-1/0;for(let c of a.values()){let v=0;for(let d=0;d<c.length;d++)v+=p((L=r.get(c[d]))!=null?L:"generic",Z(d,c.length));v>b&&(b=v,y=c)}return y!=null?y:N(e,r,t,o)}function X(e){return Object.keys(e).sort().map(r=>`${r}=${e[r]}`).join("|")}function J(e){if(e.length===0)return null;let r={};for(let t of e.split("|")){let n=t.indexOf("=");if(n<=0||n!==t.lastIndexOf("=")||n===t.length-1)return null;let s=t.slice(0,n);if(s in r)return null;r[s]=t.slice(n+1)}return r}function $e(e,r){return`${e}=${r}`}function ie(e){var t,n,s;if(e.arms)return typeof e.baseline=="string"?e.baseline:(t=e.arms[0])!=null?t:"";if(e.baseline!==void 0&&typeof e.baseline=="object")return X(e.baseline);if(typeof e.baseline=="string")return e.baseline;let r={};for(let[o,a]of Object.entries((n=e.dims)!=null?n:{}))r[o]=(s=a[0])!=null?s:"";return X(r)}function Be(e){let r=Array.isArray(e.arms),t=e.dims!=null;if(r&&t)return{ok:!1,reason:"declare exactly one of arms or dims (got both)"};if(!r&&!t)return{ok:!1,reason:"declare exactly one of arms or dims (got neither)"};if(r){let o=e.arms;if(o.length<2)return{ok:!1,reason:"arms requires at least 2 entries"};if(o.length>12)return{ok:!1,reason:"arms allows at most 12 entries"};if(new Set(o).size!==o.length)return{ok:!1,reason:"arms must be unique"};if(o.some(a=>a.includes("=")))return{ok:!1,reason:"enumerated arm ids may not contain '=' (reserved for dims encoding)"};if(e.baseline!==void 0){if(typeof e.baseline!="string")return{ok:!1,reason:"baseline for an arms slot must be a string"};if(!o.includes(e.baseline))return{ok:!1,reason:"baseline must be one of the declared arms"}}return{ok:!0}}let n=Object.entries(e.dims);if(n.length<1)return{ok:!1,reason:"dims requires at least 1 dimension"};if(n.length>4)return{ok:!1,reason:"dims allows at most 4 dimensions"};let s=1;for(let[o,a]of n){if(a.length<2)return{ok:!1,reason:`dim "${o}" requires at least 2 values`};if(a.length>6)return{ok:!1,reason:`dim "${o}" allows at most 6 values`};if(new Set(a).size!==a.length)return{ok:!1,reason:`dim "${o}" has duplicate values`};s*=a.length}if(s>64)return{ok:!1,reason:`declared space of ${s} combinations exceeds the 64 maximum`};if(e.baseline!==void 0){if(typeof e.baseline=="string")return{ok:!1,reason:"baseline for a dims slot must be a per-dim record"};let o=e.baseline,a=n.map(([u])=>u).sort(),i=Object.keys(o).sort();if(a.join(" ")!==i.join(" "))return{ok:!1,reason:"baseline must set every declared dim exactly once"};for(let[u,l]of n)if(!l.includes(o[u]))return{ok:!1,reason:`baseline value for dim "${u}" is not declared`}}return{ok:!0}}function qe(e,r){var t,n;return e.dims!=null?(n=(t=J(r))!=null?t:J(ie(e)))!=null?n:{}:r}function ue(e){let r=2166136261;for(let t=0;t<e.length;t++)r^=e.charCodeAt(t),r=r+((r<<1)+(r<<4)+(r<<7)+(r<<8)+(r<<24))>>>0;return r}function je(e,r,t){if(t.length===0)throw new Error("pickDeterministicArm requires at least one arm");let n=[...t].sort();return n[ue(`${e}:${r}`)%n.length]}function ze(e){return e>=.3?e<.7?"medium":"high":"low"}var le=20;function ce(e,r,t=le){let n=e.valueCount>0?e.valueSum/e.valueCount:0;if(r<=0)return n;if(e.valueCount<=0)return r;let s=e.valueCount/(e.valueCount+t);return s*n+(1-s)*r}function We(e,r,t=Math.random){if(e.length===0)return null;if(e.length===1)return e[0].arm;let n=null,s=-1/0;for(let o of e){let a=I(o.alpha,o.beta,t)*ce(o,r);a>s&&(n=o,s=a)}return n.arm}var fe=/^[a-z0-9][a-z0-9_-]{0,31}$/;function Xe(e){if(typeof e!="string")return null;let r=e.trim().toLowerCase();return r&&fe.test(r)?r:null}var me=["unknown","__all__","buyers","researchers","deal-seekers","browsers"],pe=K.map(e=>({key:e,displayName:j[e]})),ge=64;function be(e){var t;let r=new Map;for(let n of e)if(n.status!=="retired"){r.set(n.key,n.key);for(let s of(t=n.aliases)!=null?t:[])me.includes(s)||r.set(s,n.key)}return r}function Je(e,r=pe){var i,u,l;let t=be(r),n=(i=e.inferredConfidence)!=null?i:0,s,o=(l=(u=e.declared)==null?void 0:u.trim().toLowerCase())!=null?l:"";if(o!==""){let f=t.get(o),h=f===void 0?t.get($(o)):void 0,g=f!=null?f:h;if(g!==void 0)return{persona:g,source:"declared",confidence:1};s=o.slice(0,ge)}let a=$(e.clusterLabel);return a!==C&&t.has(a)?U({persona:t.get(a),source:"inferred",confidence:n},s!==void 0&&{unrecognizedDeclared:s}):U({persona:C,source:"none",confidence:n},s!==void 0&&{unrecognizedDeclared:s})}function Qe(e){var t;if(e==null)return C;let r=e.trim().toLowerCase();return r===""?C:(t=V[r])!=null?t:r}export{oe as CLUSTER_PRIORITY,pe as DEFAULT_PERSONA_VOCABULARY,le as EV_SHRINK_K,ae as GLOBAL_FACTOR_PERSONA,T as LAYOUT_FACTOR_BUCKETS,V as LEGACY_PERSONA_MAP,K as PERSONAS,j as PERSONA_DISPLAY,fe as PERSONA_KEY_RE,k as POOL_ALL,me as RESERVED_PERSONA_KEYS,W as SHRINKAGE_M,C as UNKNOWN_PERSONA,se as WEIGHTS_FALLBACK_PRIOR_PULLS,N as applyClusterHeuristic,Me as broadestValueCell,D as candidateLayouts,X as canonicalArm,$ as canonicalPersona,Se as chooseLayout,Ue as chooseLayoutFactored,ze as confidenceBand,Qe as decisionPersona,Fe as factorCellsForOrder,ue as fnv1a,G as hashLayout,Z as layoutBucketOf,$e as marginalArmKey,Xe as normalizeDeclaredPersona,J as parseArm,je as pickDeterministicArm,Ce as pickFromWeights,we as pooledPosterior,S as posteriorOfCounts,Je as resolvePersona,Y as sampleArm,We as sampleArmEv,I as sampleBeta,ce as shrunkAvgValue,_ as shrunkPosterior,ie as slotBaselineArm,qe as slotResultFor,Be as validateSlotDecl,Ee as weightCellsFor};
1
+ import{a as T}from"./chunk-HBG7RQ56.mjs";var ve=["buyer","researcher","deal_seeker","browser"],L="unknown",Ae={buyer:"Buyer",researcher:"Researcher",deal_seeker:"Deal seeker",browser:"Browser",unknown:"Unknown"},D={buyers:"buyer",researchers:"researcher","deal-seekers":"deal_seeker",browsers:"browser",buyer:"buyer",researcher:"researcher",deal_seeker:"deal_seeker",browser:"browser"};function V(e){var t;if(e==null)return L;let r=e.trim().toLowerCase();return(t=D[r])!=null?t:L}var se=[1116352408,1899447441,3049323471,3921009573,961987163,1508970993,2453635748,2870763221,3624381080,310598401,607225278,1426881987,1925078388,2162078206,2614888103,3248222580,3835390401,4022224774,264347078,604807628,770255983,1249150122,1555081692,1996064986,2554220882,2821834349,2952996808,3210313671,3336571891,3584528711,113926993,338241895,666307205,773529912,1294757372,1396182291,1695183700,1986661051,2177026350,2456956037,2730485921,2820302411,3259730800,3345764771,3516065817,3600352804,4094571909,275423344,430227734,506948616,659060556,883997877,958139571,1322822218,1537002063,1747873779,1955562222,2024104815,2227730452,2361852424,2428436474,2756734187,3204031479,3329325298];function k(e,r){return e>>>r|e<<32-r}function G(e){return(e>>>0).toString(16).padStart(8,"0")}function ae(e){let r=new TextEncoder().encode(e),t=r.length,n=t*8,o=(t+8>>6)+1<<6,s=new Uint8Array(o);s.set(r),s[t]=128;let a=new DataView(s.buffer);a.setUint32(o-8,Math.floor(n/4294967296),!1),a.setUint32(o-4,n>>>0,!1);let l=1779033703,u=3144134277,f=1013904242,m=2773480762,y=1359893119,d=2600822924,b=528734635,c=1541459225,p=new Uint32Array(64);for(let A=0;A<o;A+=64){for(let g=0;g<16;g++)p[g]=a.getUint32(A+g*4,!1);for(let g=16;g<64;g++){let O=k(p[g-15],7)^k(p[g-15],18)^p[g-15]>>>3,F=k(p[g-2],17)^k(p[g-2],19)^p[g-2]>>>10;p[g]=p[g-16]+O+p[g-7]+F>>>0}let i=l,v=u,x=f,S=m,h=y,P=d,w=b,E=c;for(let g=0;g<64;g++){let O=k(h,6)^k(h,11)^k(h,25),F=h&P^~h&w,j=E+O+F+se[g]+p[g]>>>0,te=k(i,2)^k(i,13)^k(i,22),ne=i&v^i&x^v&x,oe=te+ne>>>0;E=w,w=P,P=h,h=S+j>>>0,S=x,x=v,v=i,i=j+oe>>>0}l=l+i>>>0,u=u+v>>>0,f=f+x>>>0,m=m+S>>>0,y=y+h>>>0,d=d+P>>>0,b=b+w>>>0,c=c+E>>>0}return G(l)+G(u)}function $(e){return ae(e.join("|"))}var N={conversion_led:["pricing","cta","hero","comparison","social_proof","trust","features","faq","navigation","generic"],evidence_led:["features","comparison","faq","hero","trust","social_proof","pricing","cta","navigation","generic"],price_led:["pricing","comparison","social_proof","trust","cta","hero","features","faq","navigation","generic"],discovery_led:["hero","features","social_proof","pricing","cta","trust","faq","comparison","navigation","generic"]},B=["conversion_led","evidence_led","price_led","discovery_led"],ke={buyer:[...N.conversion_led],researcher:[...N.evidence_led],deal_seeker:[...N.price_led],browser:[...N.discovery_led]};function z(e,r,t,n){let o=N[t];if(!o)return e;let s=o.indexOf("generic"),a=f=>{let m=o.indexOf(f);return m===-1?s:m},l=n?e.filter(f=>n.get(f)!=="structural"):[...e];if(l.sort((f,m)=>{var b,c;let y=(b=r.get(f))!=null?b:"generic",d=(c=r.get(m))!=null?c:"generic";return a(y)-a(d)}),!n)return l;let u=0;return e.map(f=>n.get(f)==="structural"?f:l[u++])}function ie(e){let r=2166136261;for(let t=0;t<e.length;t++)r^=e.charCodeAt(t),r=Math.imul(r,16777619)>>>0;return r>>>0}function le(e,r,t,n){if(!t||t==="unknown")return e;let o=B[ie(t)%B.length];return z(e,r,o,n)}var Ce=le;function K(e,r,t){let n=new Map;n.set($(e),[...e]);for(let o of B){let s=z(e,r,o,t),a=$(s);n.has(a)||n.set(a,s)}return n}function Y(e,r){if(e<1)return Y(1+e,r)*Math.pow(Math.max(1e-15,r()),1/e);let t=e-1/3,n=1/Math.sqrt(9*t);for(;;){let o,s;do{let l=Math.max(1e-15,r()),u=r();o=Math.sqrt(-2*Math.log(l))*Math.cos(2*Math.PI*u),s=1+n*o}while(s<=0);s=s*s*s;let a=r();if(a<1-.0331*o*o*o*o||Math.log(a)<.5*o*o+t*(1-s+Math.log(s)))return t*s}}function M(e,r,t=Math.random){let n=Y(e,t),o=Y(r,t),s=n+o;return s<=0?e/(e+r):n/s}function W(e,r=Math.random){if(e.length===0)return null;if(e.length===1)return e[0].arm;let t=e[0],n=M(t.alpha,t.beta,r);for(let o=1;o<e.length;o++){let s=e[o],a=M(s.alpha,s.beta,r);a>n&&(t=s,n=a)}return t.arm}function Ee(e,r,t,n,o=Math.random,s){var m,y;let a=K(e,r,s),l=[];for(let d of a.keys()){let b=n.get(d);l.push({arm:d,alpha:(m=b==null?void 0:b.alpha)!=null?m:1,beta:(y=b==null?void 0:b.beta)!=null?y:1})}let u=W(l,o),f=u?a.get(u):void 0;return f!=null?f:e}var Z=20;function C(e,r,t=20){let n=r.alpha+r.beta;if(n<=0||t<=0)return{alpha:e.alpha,beta:e.beta};let o=r.alpha/n,s=t*n/(n+t);return{alpha:e.alpha+s*o,beta:e.beta+s*(1-o)}}var ue=5;function Fe(e,r){var n,o;let t=null;for(let s of e){if(!r.includes(s.variantId))continue;let a=(n=s.pulls)!=null?n:0,l=a>0?a*s.avgReward/(a+ue):0;(!t||l>t.score)&&(t={variantId:s.variantId,score:l})}return(o=t==null?void 0:t.variantId)!=null?o:null}var R="__all__",U={exposures:0,conversions:0};function _(e){return{alpha:e.conversions+1,beta:Math.max(0,e.exposures-e.conversions)+1}}function Ue(e,r,t=20){var d,b,c,p;let n=(d=e.segment)!=null?d:U,o=(b=e.global)!=null?b:U,s=_(o),a=C(_(n),s,t);if(!r)return a;let l=(c=e.persona)!=null?c:U,u=(p=e.child)!=null?p:U,f=C(_(l),s,t),m=(n.exposures+1)/(n.exposures+l.exposures+2),y={alpha:m*a.alpha+(1-m)*f.alpha,beta:m*a.beta+(1-m)*f.beta};return C(_(u),y,t)}function Ie(e){var n,o;let r=null,t=-1;for(let s of e){let a=s.segment===R,l=s.persona===R,u=a&&l?3:a||l?2:1;u>t&&(t=u,r=s)}return{valueSum:(n=r==null?void 0:r.valueSum)!=null?n:0,valueCount:(o=r==null?void 0:r.valueCount)!=null?o:0}}function Te(e,r){return r==="unknown"||r===R||r===""?[{segment:e,persona:R},{segment:R,persona:R}]:[{segment:e,persona:r},{segment:e,persona:R},{segment:R,persona:r},{segment:R,persona:R}]}var X=4,ce="__global__";function J(e,r){return r<=0?0:Math.min(X-1,Math.floor(e*X/r))}function He(e,r){return e.map((t,n)=>{var o;return{parent:(o=r.get(t))!=null?o:"generic",bucket:J(n,e.length)}})}var H=(e,r)=>`${e}#${r}`;function qe(e,r,t,n,o=Math.random,s){var A;let a=K(e,r,s),l=new Map,u=new Map,f=0,m=0;for(let i of n)i.persona===ce?(l.set(H(i.parent,i.bucket),i),f+=i.exposures,m+=i.conversions):i.persona===t&&u.set(H(i.parent,i.bucket),i);let y=_({exposures:f,conversions:m}),d=new Map,b=(i,v)=>{var O,F;let x=H(i,v),S=d.get(x);if(S!==void 0)return S;let h=l.get(x),P=C(_({exposures:(O=h==null?void 0:h.exposures)!=null?O:0,conversions:(F=h==null?void 0:h.conversions)!=null?F:0}),y),w=u.get(x),E=w?C(_({exposures:w.exposures,conversions:w.conversions}),P):P,g=M(E.alpha,E.beta,o);return d.set(x,g),g},c=null,p=-1/0;for(let i of a.values()){let v=0;for(let x=0;x<i.length;x++)v+=b((A=r.get(i[x]))!=null?A:"generic",J(x,i.length));v>p&&(p=v,c=i)}return c!=null?c:e}var I="__global__",Xe=["device","source","persona","visit"];function Q(e){let r=[];return e.device&&r.push({factor:"device",level:e.device}),e.source&&r.push({factor:"source",level:e.source}),e.persona&&e.persona!==L&&r.push({factor:"persona",level:e.persona}),e.visit&&r.push({factor:"visit",level:e.visit}),r}var q=(e,r,t)=>`${e}\0${r}\0${t}`;function Je(e,r,t,n=Math.random){if(e.length===0)return null;if(e.length===1)return{arm:e[0],factorsUsed:0};let o=new Map,s=0,a=0;for(let c of t)o.set(q(c.arm,c.factor,c.level),c),c.level===I&&(s+=c.exposures,a+=c.conversions);let l=_({exposures:s,conversions:a}),u=Q(r),f=new Map,m=c=>{var A,i;let p=o.get(q(c,"global",I));return C(_({exposures:(A=p==null?void 0:p.exposures)!=null?A:0,conversions:(i=p==null?void 0:p.conversions)!=null?i:0}),l)},y=(c,p,A)=>{let i=q(c,p,A),v=f.get(i);if(v!==void 0)return v;let x=m(c),S=o.get(i),h=S?C(_({exposures:S.exposures,conversions:S.conversions}),x):x,P=M(h.alpha,h.beta,n);return f.set(i,P),P},d=null,b=-1/0;for(let c of e){let p=u.length===0?y(c,"global",I):0;for(let{factor:A,level:i}of u)p+=y(c,A,i);p>b&&(b=p,d=c)}return d===null?null:{arm:d,factorsUsed:u.length}}function Qe(e,r,t){let n=Number.isFinite(t)?Math.max(0,Math.min(1,t)):0,o=[{arm:e,factor:"global",level:I,weight:1}];for(let{factor:s,level:a}of Q(r))o.push({arm:e,factor:s,level:a,weight:s==="persona"?n:1});return o}function er(e){return e==null||!Number.isFinite(e)||e<1?null:e>1?"returning":"new"}function ee(e){return Object.keys(e).sort().map(r=>`${r}=${e[r]}`).join("|")}function re(e){if(e.length===0)return null;let r={};for(let t of e.split("|")){let n=t.indexOf("=");if(n<=0||n!==t.lastIndexOf("=")||n===t.length-1)return null;let o=t.slice(0,n);if(o in r)return null;r[o]=t.slice(n+1)}return r}function tr(e,r){return`${e}=${r}`}function fe(e){var t,n,o;if(e.arms)return typeof e.baseline=="string"?e.baseline:(t=e.arms[0])!=null?t:"";if(e.baseline!==void 0&&typeof e.baseline=="object")return ee(e.baseline);if(typeof e.baseline=="string")return e.baseline;let r={};for(let[s,a]of Object.entries((n=e.dims)!=null?n:{}))r[s]=(o=a[0])!=null?o:"";return ee(r)}function nr(e){let r=Array.isArray(e.arms),t=e.dims!=null;if(r&&t)return{ok:!1,reason:"declare exactly one of arms or dims (got both)"};if(!r&&!t)return{ok:!1,reason:"declare exactly one of arms or dims (got neither)"};if(r){let s=e.arms;if(s.length<2)return{ok:!1,reason:"arms requires at least 2 entries"};if(s.length>12)return{ok:!1,reason:"arms allows at most 12 entries"};if(new Set(s).size!==s.length)return{ok:!1,reason:"arms must be unique"};if(s.some(a=>a.includes("=")))return{ok:!1,reason:"enumerated arm ids may not contain '=' (reserved for dims encoding)"};if(e.baseline!==void 0){if(typeof e.baseline!="string")return{ok:!1,reason:"baseline for an arms slot must be a string"};if(!s.includes(e.baseline))return{ok:!1,reason:"baseline must be one of the declared arms"}}return{ok:!0}}let n=Object.entries(e.dims);if(n.length<1)return{ok:!1,reason:"dims requires at least 1 dimension"};if(n.length>4)return{ok:!1,reason:"dims allows at most 4 dimensions"};let o=1;for(let[s,a]of n){if(a.length<2)return{ok:!1,reason:`dim "${s}" requires at least 2 values`};if(a.length>6)return{ok:!1,reason:`dim "${s}" allows at most 6 values`};if(new Set(a).size!==a.length)return{ok:!1,reason:`dim "${s}" has duplicate values`};o*=a.length}if(o>64)return{ok:!1,reason:`declared space of ${o} combinations exceeds the 64 maximum`};if(e.baseline!==void 0){if(typeof e.baseline=="string")return{ok:!1,reason:"baseline for a dims slot must be a per-dim record"};let s=e.baseline,a=n.map(([u])=>u).sort(),l=Object.keys(s).sort();if(a.join(" ")!==l.join(" "))return{ok:!1,reason:"baseline must set every declared dim exactly once"};for(let[u,f]of n)if(!f.includes(s[u]))return{ok:!1,reason:`baseline value for dim "${u}" is not declared`}}return{ok:!0}}function or(e,r){var t,n;return e.dims!=null?(n=(t=re(r))!=null?t:re(fe(e)))!=null?n:{}:r}function pe(e){let r=2166136261;for(let t=0;t<e.length;t++)r^=e.charCodeAt(t),r=r+((r<<1)+(r<<4)+(r<<7)+(r<<8)+(r<<24))>>>0;return r}function ar(e,r,t){if(t.length===0)throw new Error("pickDeterministicArm requires at least one arm");let n=[...t].sort();return n[pe(`${e}:${r}`)%n.length]}function ir(e){return e>=.3?e<.7?"medium":"high":"low"}var me=20;function ge(e,r,t=me){let n=e.valueCount>0?e.valueSum/e.valueCount:0;if(r<=0)return n;if(e.valueCount<=0)return r;let o=e.valueCount/(e.valueCount+t);return o*n+(1-o)*r}function cr(e,r,t=Math.random){if(e.length===0)return null;if(e.length===1)return e[0].arm;let n=null,o=-1/0;for(let s of e){let a=M(s.alpha,s.beta,t)*ge(s,r);a>o&&(n=s,o=a)}return n.arm}var de=/^[a-z0-9][a-z0-9_-]{0,31}$/;function mr(e){if(typeof e!="string")return null;let r=e.trim().toLowerCase();return r&&de.test(r)?r:null}var be=["unknown","__all__","buyers","researchers","deal-seekers","browsers"],xe=[],he=64;function ye(e){var t;let r=new Map;for(let n of e)if(n.status!=="retired"){r.set(n.key,n.key);for(let o of(t=n.aliases)!=null?t:[])be.includes(o)||r.set(o,n.key)}return r}function gr(e,r=xe){var l,u,f;let t=ye(r),n=(l=e.inferredConfidence)!=null?l:0,o,s=(f=(u=e.declared)==null?void 0:u.trim().toLowerCase())!=null?f:"";if(s!==""){let m=t.get(s),y=m===void 0?t.get(V(s)):void 0,d=m!=null?m:y;if(d!==void 0)return{persona:d,source:"declared",confidence:1};o=s.slice(0,he)}let a=V(e.clusterLabel);return a!==L&&t.has(a)?T({persona:t.get(a),source:"inferred",confidence:n},o!==void 0&&{unrecognizedDeclared:o}):T({persona:L,source:"none",confidence:n},o!==void 0&&{unrecognizedDeclared:o})}function dr(e){var t;if(e==null)return L;let r=e.trim().toLowerCase();return r===""?L:(t=D[r])!=null?t:r}export{ke as CLUSTER_PRIORITY,xe as DEFAULT_PERSONA_VOCABULARY,me as EV_SHRINK_K,I as GLOBAL_FACTOR_LEVEL,ce as GLOBAL_FACTOR_PERSONA,N as LAYOUT_ARCHETYPES,B as LAYOUT_ARCHETYPE_NAMES,X as LAYOUT_FACTOR_BUCKETS,D as LEGACY_PERSONA_MAP,ve as PERSONAS,Ae as PERSONA_DISPLAY,de as PERSONA_KEY_RE,R as POOL_ALL,be as RESERVED_PERSONA_KEYS,Z as SHRINKAGE_M,Xe as SLOT_FACTORS,L as UNKNOWN_PERSONA,ue as WEIGHTS_FALLBACK_PRIOR_PULLS,Ce as applyClusterHeuristic,Ie as broadestValueCell,K as candidateLayouts,ee as canonicalArm,V as canonicalPersona,Ee as chooseLayout,qe as chooseLayoutFactored,Je as chooseSlotArmFactored,ir as confidenceBand,dr as decisionPersona,He as factorCellsForOrder,Qe as factorCellsForTrial,Q as factorLevelsFor,pe as fnv1a,$ as hashLayout,J as layoutBucketOf,tr as marginalArmKey,mr as normalizeDeclaredPersona,z as orderByArchetype,re as parseArm,ar as pickDeterministicArm,Fe as pickFromWeights,Ue as pooledPosterior,_ as posteriorOfCounts,le as previewOrderForPersona,gr as resolvePersona,W as sampleArm,cr as sampleArmEv,M as sampleBeta,ge as shrunkAvgValue,C as shrunkPosterior,fe as slotBaselineArm,or as slotResultFor,nr as validateSlotDecl,er as visitLevel,Te as weightCellsFor};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentientui/policy",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "private": false,
5
5
  "description": "Pure decision-policy functions shared by the SentientUI API and the keyless local engine",
6
6
  "license": "MIT",