@higher.archi/boe 1.0.9 → 1.0.11

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.
Files changed (82) hide show
  1. package/dist/core/memory/working-memory.d.ts +2 -2
  2. package/dist/core/memory/working-memory.d.ts.map +1 -1
  3. package/dist/core/memory/working-memory.interface.d.ts +2 -2
  4. package/dist/core/memory/working-memory.interface.d.ts.map +1 -1
  5. package/dist/core/memory/working-memory.js +2 -2
  6. package/dist/core/memory/working-memory.js.map +1 -1
  7. package/dist/core/types/fact.d.ts +4 -4
  8. package/dist/core/types/fact.d.ts.map +1 -1
  9. package/dist/engines/backward/engine.d.ts +2 -2
  10. package/dist/engines/backward/engine.d.ts.map +1 -1
  11. package/dist/engines/backward/engine.js.map +1 -1
  12. package/dist/engines/bayesian/engine.d.ts +2 -2
  13. package/dist/engines/bayesian/engine.d.ts.map +1 -1
  14. package/dist/engines/bayesian/engine.js.map +1 -1
  15. package/dist/engines/constraint/engine.d.ts +2 -2
  16. package/dist/engines/constraint/engine.d.ts.map +1 -1
  17. package/dist/engines/constraint/engine.js.map +1 -1
  18. package/dist/engines/defeasible/engine.d.ts +2 -2
  19. package/dist/engines/defeasible/engine.d.ts.map +1 -1
  20. package/dist/engines/defeasible/engine.js.map +1 -1
  21. package/dist/engines/expert/engine.d.ts +2 -2
  22. package/dist/engines/expert/engine.d.ts.map +1 -1
  23. package/dist/engines/expert/engine.js.map +1 -1
  24. package/dist/engines/forward/engine.d.ts +2 -2
  25. package/dist/engines/forward/engine.d.ts.map +1 -1
  26. package/dist/engines/forward/engine.js.map +1 -1
  27. package/dist/engines/fuzzy/engine.d.ts +2 -2
  28. package/dist/engines/fuzzy/engine.d.ts.map +1 -1
  29. package/dist/engines/fuzzy/engine.js.map +1 -1
  30. package/dist/engines/monte-carlo/engine.d.ts +2 -2
  31. package/dist/engines/monte-carlo/engine.d.ts.map +1 -1
  32. package/dist/engines/monte-carlo/engine.js.map +1 -1
  33. package/dist/engines/scoring/compiler.d.ts +2 -2
  34. package/dist/engines/scoring/compiler.d.ts.map +1 -1
  35. package/dist/engines/scoring/compiler.js +38 -0
  36. package/dist/engines/scoring/compiler.js.map +1 -1
  37. package/dist/engines/scoring/engine.d.ts +7 -4
  38. package/dist/engines/scoring/engine.d.ts.map +1 -1
  39. package/dist/engines/scoring/engine.js +3 -0
  40. package/dist/engines/scoring/engine.js.map +1 -1
  41. package/dist/engines/scoring/index.d.ts +1 -1
  42. package/dist/engines/scoring/index.d.ts.map +1 -1
  43. package/dist/engines/scoring/index.js +5 -1
  44. package/dist/engines/scoring/index.js.map +1 -1
  45. package/dist/engines/scoring/strategy.d.ts +2 -2
  46. package/dist/engines/scoring/strategy.d.ts.map +1 -1
  47. package/dist/engines/scoring/strategy.js +28 -6
  48. package/dist/engines/scoring/strategy.js.map +1 -1
  49. package/dist/engines/scoring/types.d.ts +137 -19
  50. package/dist/engines/scoring/types.d.ts.map +1 -1
  51. package/dist/engines/scoring/types.js +27 -0
  52. package/dist/engines/scoring/types.js.map +1 -1
  53. package/dist/engines/sequential/engine.d.ts +2 -2
  54. package/dist/engines/sequential/engine.d.ts.map +1 -1
  55. package/dist/engines/sequential/engine.js.map +1 -1
  56. package/dist/engines/utility/engine.d.ts +2 -2
  57. package/dist/engines/utility/engine.d.ts.map +1 -1
  58. package/dist/engines/utility/engine.js.map +1 -1
  59. package/dist/index.d.ts +2 -2
  60. package/dist/index.d.ts.map +1 -1
  61. package/dist/index.js +3 -2
  62. package/dist/index.js.map +1 -1
  63. package/package.json +1 -1
  64. package/src/core/memory/working-memory.interface.ts +2 -2
  65. package/src/core/memory/working-memory.ts +12 -12
  66. package/src/core/types/fact.ts +4 -4
  67. package/src/engines/backward/engine.ts +2 -2
  68. package/src/engines/bayesian/engine.ts +2 -2
  69. package/src/engines/constraint/engine.ts +2 -2
  70. package/src/engines/defeasible/engine.ts +2 -2
  71. package/src/engines/expert/engine.ts +2 -2
  72. package/src/engines/forward/engine.ts +2 -2
  73. package/src/engines/fuzzy/engine.ts +2 -2
  74. package/src/engines/monte-carlo/engine.ts +2 -2
  75. package/src/engines/scoring/compiler.ts +56 -3
  76. package/src/engines/scoring/engine.ts +11 -4
  77. package/src/engines/scoring/index.ts +8 -1
  78. package/src/engines/scoring/strategy.ts +43 -15
  79. package/src/engines/scoring/types.ts +161 -20
  80. package/src/engines/sequential/engine.ts +2 -2
  81. package/src/engines/utility/engine.ts +2 -2
  82. package/src/index.ts +10 -2
@@ -86,14 +86,104 @@ export type TierDefinition = {
86
86
  threshold: number | '-Infinity'; // Minimum score to qualify (inclusive), or '-Infinity' for catch-all
87
87
  };
88
88
 
89
+ /**
90
+ * Style slots for tier visual presentation
91
+ *
92
+ * Each field accepts a CSS color value, class name, or any string
93
+ * the consumer's UI stack requires. BOE passes these through untouched.
94
+ *
95
+ * @example Tailwind classes
96
+ * ```typescript
97
+ * const style: TierStyle = {
98
+ * base: 'emerald-500',
99
+ * text: 'text-emerald-700',
100
+ * background: 'bg-emerald-50',
101
+ * border: 'border-emerald-300',
102
+ * hover: 'hover:bg-emerald-100',
103
+ * anchor: 'text-emerald-600 hover:text-emerald-800',
104
+ * icon: 'star'
105
+ * };
106
+ * ```
107
+ *
108
+ * @example Raw CSS values
109
+ * ```typescript
110
+ * const style: TierStyle = {
111
+ * base: '#10B981',
112
+ * text: '#065F46',
113
+ * background: '#ECFDF5',
114
+ * hover: '#059669'
115
+ * };
116
+ * ```
117
+ */
118
+ export type TierStyle = {
119
+ // Core visual
120
+ base?: string; // Primary color value or class
121
+ text?: string; // Text color or class
122
+ background?: string; // Background color or class
123
+ border?: string; // Border color or class
124
+
125
+ // Interactive states
126
+ hover?: string; // Hover state color or class
127
+ active?: string; // Active/pressed state color or class
128
+ focus?: string; // Focus state color or class
129
+ disabled?: string; // Disabled state color or class
130
+
131
+ // Links
132
+ anchor?: string; // Link/anchor color or class
133
+
134
+ // Decorative
135
+ icon?: string; // Icon identifier (e.g., 'star', 'shield-check')
136
+ badge?: string; // Badge/pill styling
137
+ ring?: string; // Ring/outline (focus rings, selection indicators)
138
+ shadow?: string; // Shadow value or class
139
+ gradient?: string; // Gradient value or class
140
+ opacity?: string; // Opacity value or class
141
+
142
+ // Extensible
143
+ [key: string]: unknown;
144
+ };
145
+
146
+ /** Known keys on TierStyle that must be strings when provided */
147
+ export const TIER_STYLE_KNOWN_KEYS: readonly string[] = [
148
+ 'base', 'text', 'background', 'border',
149
+ 'hover', 'active', 'focus', 'disabled',
150
+ 'anchor',
151
+ 'icon', 'badge', 'ring', 'shadow', 'gradient', 'opacity'
152
+ ];
153
+
154
+ /**
155
+ * Styled extension of TierDefinition
156
+ *
157
+ * Adds an optional `style` field for visual presentation data.
158
+ * BOE validates the style structure at compile time but passes it
159
+ * through untouched — the consumer's UI layer interprets the values.
160
+ *
161
+ * Use with generics to get typed style data in scoring results:
162
+ * ```typescript
163
+ * const config: ScoringConfig<StyledTierDefinition> = {
164
+ * mode: 'scoring',
165
+ * strategies: ['raw'],
166
+ * tiers: [
167
+ * { id: 'elite', name: 'Elite', threshold: 900, style: { base: '#10B981', icon: 'star' } },
168
+ * { id: 'standard', name: 'Standard', threshold: 0, style: { base: '#6B7280' } }
169
+ * ]
170
+ * };
171
+ * // result.tier?.style is typed as TierStyle
172
+ * ```
173
+ */
174
+ export type StyledTierDefinition = TierDefinition & {
175
+ style?: TierStyle;
176
+ };
177
+
89
178
  /**
90
179
  * Matched tier in scoring result
180
+ *
181
+ * Preserves all properties from the original tier definition (including
182
+ * `style` when using StyledTierDefinition) with the threshold resolved
183
+ * to a numeric value.
91
184
  */
92
- export type ScoringTierMatch = {
93
- id: string;
94
- name?: string;
95
- description?: string;
96
- threshold: number;
185
+ export type ScoringTierMatch<T extends TierDefinition = TierDefinition> = Omit<T, 'threshold'> & {
186
+ threshold: number; // Resolved numeric value (never '-Infinity' string)
97
187
  };
98
188
 
99
189
  // ========================================
@@ -237,11 +327,11 @@ export type OverrideDefinition =
237
327
  /**
238
328
  * Override result — attached to ScoringResult when an override fires
239
329
  */
240
- export type OverrideResult = {
330
+ export type OverrideResult<T extends TierDefinition = TierDefinition> = {
241
331
  id: string;
242
332
  effect: OverrideEffect;
243
333
  originalScore: number;
244
- originalTier?: ScoringTierMatch;
334
+ originalTier?: ScoringTierMatch<T>;
245
335
  applied: true;
246
336
  };
247
337
 
@@ -287,25 +377,49 @@ export type ScoringRule = BaseRule & {
287
377
  /**
288
378
  * Scoring ruleset
289
379
  */
290
- export type ScoringRuleSet = BaseRuleSet & {
380
+ export type ScoringRuleSet<T extends TierDefinition = TierDefinition> = BaseRuleSet & {
291
381
  mode: 'scoring';
292
382
  rules: ScoringRule[];
293
- config?: Partial<ScoringConfig>;
383
+ config?: Partial<ScoringConfig<T>>;
384
+ };
385
+
386
+ /**
387
+ * Composite confidence configuration
388
+ *
389
+ * Controls how confidence is computed from multiple components.
390
+ * Without this config, confidence = signal coverage (existing behavior).
391
+ */
392
+ export type ConfidenceConfig = {
393
+ signalCoverageWeight?: number; // default: 1.0
394
+ dataFreshnessWeight?: number; // default: 0 (existing behavior)
395
+ };
396
+
397
+ /**
398
+ * Breakdown of composite confidence components
399
+ */
400
+ export type ConfidenceBreakdown = {
401
+ signalCoverage: number;
402
+ dataFreshness: number;
403
+ weights: { signalCoverage: number; dataFreshness: number };
294
404
  };
295
405
 
296
406
  /**
297
407
  * Scoring configuration
408
+ *
409
+ * Generic over tier type — defaults to TierDefinition for backward compatibility.
410
+ * Use ScoringConfig<StyledTierDefinition> to get typed style data flowing through results.
298
411
  */
299
- export type ScoringConfig = {
412
+ export type ScoringConfig<T extends TierDefinition = TierDefinition> = {
300
413
  mode: 'scoring';
301
414
  strategies: ScoringMethod[]; // Default: ['raw'] - methods applied in sequence
302
415
  caps?: ScoringCaps; // Optional global caps (used with 'capped' method)
303
416
  outputBounds?: OutputBounds; // Optional output scaling
304
417
  baseScore?: number; // Starting score before rule contributions (default: 0)
305
- tiers?: TierDefinition[]; // Optional tier definitions for score classification
418
+ tiers?: T[]; // Optional tier definitions for score classification
306
419
  categories?: ScoringCategory[]; // Optional category grouping for two-level weight hierarchy
307
420
  overrides?: OverrideDefinition[]; // Optional post-scoring overrides evaluated in order (first match wins)
308
421
  nullHandling?: NullHandling; // Default null handling for all rules (default: 'exclude')
422
+ confidenceConfig?: ConfidenceConfig; // Optional composite confidence configuration
309
423
  decay?: {
310
424
  timestamp?: string | Expression; // default timestamp path for all rules
311
425
  config: DecayConfig; // engine-level decay config
@@ -356,10 +470,10 @@ export type CompiledOverrideDefinition =
356
470
  /**
357
471
  * Compiled scoring ruleset
358
472
  */
359
- export type CompiledScoringRuleSet = CompiledBaseRuleSet & {
473
+ export type CompiledScoringRuleSet<T extends TierDefinition = TierDefinition> = CompiledBaseRuleSet & {
360
474
  mode: 'scoring';
361
475
  rules: CompiledScoringRule[];
362
- config: ScoringConfig;
476
+ config: ScoringConfig<T>;
363
477
  overrides?: CompiledOverrideDefinition[];
364
478
  };
365
479
 
@@ -370,34 +484,61 @@ export type CompiledScoringRuleSet = CompiledBaseRuleSet & {
370
484
  /**
371
485
  * Score function for dynamic scoring
372
486
  */
373
- export type ScoreFunction = (context: BindingContext) => number;
487
+ export type ScoreFunction<TContext extends BindingContext = BindingContext> =
488
+ (context: TContext) => number;
374
489
 
375
490
  /**
376
491
  * Registry of score functions
377
492
  */
378
- export type ScoreFunctionRegistry = Record<string, ScoreFunction>;
493
+ export type ScoreFunctionRegistry<TContext extends BindingContext = BindingContext> =
494
+ Record<string, ScoreFunction<TContext>>;
495
+
496
+ /**
497
+ * Create a typed score function with autocomplete on context keys.
498
+ *
499
+ * Due to function contravariance, a narrowly-typed `(NarrowCtx) => number` isn't
500
+ * directly assignable to `(BindingContext) => number`. This helper handles the cast
501
+ * safely — rule inputs guarantee the correct facts at runtime.
502
+ *
503
+ * @example
504
+ * ```typescript
505
+ * type MyContext = { applicant: Fact<{ income: number; creditScore: number }> };
506
+ * const myFn = createScoreFunction<MyContext>((ctx) => {
507
+ * return ctx.applicant.data.creditScore / 850 * 100; // fully typed!
508
+ * });
509
+ * ```
510
+ */
511
+ export function createScoreFunction<TContext extends BindingContext = BindingContext>(
512
+ fn: (context: TContext) => number
513
+ ): ScoreFunction {
514
+ return fn as ScoreFunction;
515
+ }
379
516
 
380
517
  /**
381
518
  * Runtime options for scoring execution
382
519
  */
383
- export type ScoringOptions = {
384
- scoreFunctions?: ScoreFunctionRegistry; // Optional dynamic score functions
520
+ export type ScoringOptions<TContext extends BindingContext = BindingContext> = {
521
+ scoreFunctions?: ScoreFunctionRegistry<TContext>; // Optional dynamic score functions
385
522
  onFire?: (ruleId: string, context: BindingContext, score: number) => void;
386
523
  };
387
524
 
388
525
  /**
389
526
  * Execution result
527
+ *
528
+ * Generic over tier type — when using StyledTierDefinition, the matched
529
+ * tier in the result preserves the full style data.
390
530
  */
391
- export type ScoringResult = {
531
+ export type ScoringResult<T extends TierDefinition = TierDefinition> = {
392
532
  totalScore: number;
393
533
  confidence: number; // 0-1, signal coverage ratio (weighted if weights present)
394
534
  contributions: Record<string, number>; // Which rules contributed what scores
395
535
  contributionPercentages: Record<string, number>; // Normalized percentages (0-100) per rule
396
536
  fired: string[]; // Rules that matched and contributed
397
537
  iterations: 1; // Scoring is always one-pass
398
- tier?: ScoringTierMatch; // Matched tier (if tiers configured)
538
+ tier?: ScoringTierMatch<T>; // Matched tier (if tiers configured)
399
539
  categoryBreakdown?: CategoryResult[]; // Per-category breakdown (if categories configured)
400
- override?: OverrideResult; // Override that fired (if any)
540
+ override?: OverrideResult<T>; // Override that fired (if any)
541
+ confidenceBreakdown?: ConfidenceBreakdown; // composite confidence components (if confidenceConfig set)
401
542
  totalScoreBeforeDecay?: number; // undecayed total for comparison
402
543
  contributionsBeforeDecay?: Record<string, number>; // undecayed per-rule contributions
403
544
  decayInfo?: Record<string, DecayInfo>; // per-rule decay audit
@@ -52,7 +52,7 @@ export class SequentialEngine implements IRuleEngine<CompiledSequentialRuleSet,
52
52
  // IWorkingMemory Implementation
53
53
  // ========================================
54
54
 
55
- add(input: FactInput): Fact {
55
+ add<T = Record<string, any>>(input: FactInput<T>): Fact<T> {
56
56
  return this.wm.add(input);
57
57
  }
58
58
 
@@ -60,7 +60,7 @@ export class SequentialEngine implements IRuleEngine<CompiledSequentialRuleSet,
60
60
  return this.wm.remove(factId);
61
61
  }
62
62
 
63
- update(input: FactInput): Fact {
63
+ update<T = Record<string, any>>(input: FactInput<T>): Fact<T> {
64
64
  return this.wm.update(input);
65
65
  }
66
66
 
@@ -81,7 +81,7 @@ export class UtilityEngine implements IRuleEngine<CompiledUtilityRuleSet, Utilit
81
81
  // IWorkingMemory Implementation
82
82
  // ========================================
83
83
 
84
- add(input: FactInput): Fact {
84
+ add<T = Record<string, any>>(input: FactInput<T>): Fact<T> {
85
85
  return this.wm.add(input);
86
86
  }
87
87
 
@@ -89,7 +89,7 @@ export class UtilityEngine implements IRuleEngine<CompiledUtilityRuleSet, Utilit
89
89
  return this.wm.remove(factId);
90
90
  }
91
91
 
92
- update(input: FactInput): Fact {
92
+ update<T = Record<string, any>>(input: FactInput<T>): Fact<T> {
93
93
  return this.wm.update(input);
94
94
  }
95
95
 
package/src/index.ts CHANGED
@@ -81,7 +81,8 @@ export type {
81
81
  export {
82
82
  ScoringStrategy,
83
83
  compileScoringRuleSet,
84
- scoringStrategy
84
+ scoringStrategy,
85
+ createScoreFunction
85
86
  } from './engines/scoring';
86
87
  export type {
87
88
  ScoringRule,
@@ -90,11 +91,14 @@ export type {
90
91
  CompiledScoringRuleSet,
91
92
  ScoringOptions,
92
93
  ScoringResult,
94
+ ScoreFunction,
93
95
  ScoreFunctionRegistry,
94
96
  ScoringMethod,
95
97
  OutputBounds,
96
98
  ScoringCategory,
97
99
  CategoryResult,
100
+ ConfidenceConfig,
101
+ ConfidenceBreakdown,
98
102
  OverrideEffect,
99
103
  OverrideDefinition,
100
104
  ForceTierOverride,
@@ -106,7 +110,11 @@ export type {
106
110
  ForceScoreOverride,
107
111
  OverrideResult,
108
112
  CompiledOverrideDefinition,
109
- NullHandling
113
+ NullHandling,
114
+ TierDefinition,
115
+ TierStyle,
116
+ StyledTierDefinition,
117
+ ScoringTierMatch
110
118
  } from './engines/scoring';
111
119
 
112
120
  // Sequential