@takk/bayesoutputgate 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/LICENSE +190 -0
  3. package/NOTICE +45 -0
  4. package/README.md +403 -0
  5. package/SECURITY.md +98 -0
  6. package/SPEC.md +467 -0
  7. package/dist/adapter/index.cjs +411 -0
  8. package/dist/adapter/index.d.cts +29 -0
  9. package/dist/adapter/index.d.ts +29 -0
  10. package/dist/adapter/index.js +404 -0
  11. package/dist/audit/index.cjs +82 -0
  12. package/dist/audit/index.d.cts +40 -0
  13. package/dist/audit/index.d.ts +40 -0
  14. package/dist/audit/index.js +77 -0
  15. package/dist/bayesfactor/index.cjs +152 -0
  16. package/dist/bayesfactor/index.d.cts +15 -0
  17. package/dist/bayesfactor/index.d.ts +15 -0
  18. package/dist/bayesfactor/index.js +149 -0
  19. package/dist/beta/index.cjs +180 -0
  20. package/dist/beta/index.d.cts +45 -0
  21. package/dist/beta/index.d.ts +45 -0
  22. package/dist/beta/index.js +178 -0
  23. package/dist/calibration/index.cjs +339 -0
  24. package/dist/calibration/index.d.cts +53 -0
  25. package/dist/calibration/index.d.ts +53 -0
  26. package/dist/calibration/index.js +333 -0
  27. package/dist/cli/index.cjs +968 -0
  28. package/dist/cli/index.d.cts +1 -0
  29. package/dist/cli/index.d.ts +1 -0
  30. package/dist/cli/index.js +966 -0
  31. package/dist/dimensions/index.cjs +106 -0
  32. package/dist/dimensions/index.d.cts +33 -0
  33. package/dist/dimensions/index.d.ts +33 -0
  34. package/dist/dimensions/index.js +104 -0
  35. package/dist/edge/index.cjs +1141 -0
  36. package/dist/edge/index.d.cts +12 -0
  37. package/dist/edge/index.d.ts +12 -0
  38. package/dist/edge/index.js +1109 -0
  39. package/dist/gate/index.cjs +803 -0
  40. package/dist/gate/index.d.cts +77 -0
  41. package/dist/gate/index.d.ts +77 -0
  42. package/dist/gate/index.js +799 -0
  43. package/dist/hypothesis/index.cjs +268 -0
  44. package/dist/hypothesis/index.d.cts +38 -0
  45. package/dist/hypothesis/index.d.ts +38 -0
  46. package/dist/hypothesis/index.js +266 -0
  47. package/dist/index.cjs +1141 -0
  48. package/dist/index.d.cts +29 -0
  49. package/dist/index.d.ts +29 -0
  50. package/dist/index.js +1109 -0
  51. package/dist/likelihood/index.cjs +137 -0
  52. package/dist/likelihood/index.d.cts +23 -0
  53. package/dist/likelihood/index.d.ts +23 -0
  54. package/dist/likelihood/index.js +132 -0
  55. package/dist/node/index.cjs +1282 -0
  56. package/dist/node/index.d.cts +24 -0
  57. package/dist/node/index.d.ts +24 -0
  58. package/dist/node/index.js +1246 -0
  59. package/dist/policy/index.cjs +88 -0
  60. package/dist/policy/index.d.cts +11 -0
  61. package/dist/policy/index.d.ts +11 -0
  62. package/dist/policy/index.js +85 -0
  63. package/dist/types-bMjn1j4e.d.cts +159 -0
  64. package/dist/types-bMjn1j4e.d.ts +159 -0
  65. package/package.json +142 -0
package/dist/index.js ADDED
@@ -0,0 +1,1109 @@
1
+ // src/errors.ts
2
+ var BayesOutputGateError = class _BayesOutputGateError extends Error {
3
+ code;
4
+ constructor(code, message) {
5
+ super(message);
6
+ this.name = "BayesOutputGateError";
7
+ this.code = code;
8
+ Object.setPrototypeOf(this, _BayesOutputGateError.prototype);
9
+ }
10
+ };
11
+ function invariant(condition, code, message) {
12
+ if (!condition) {
13
+ throw new BayesOutputGateError(code, message);
14
+ }
15
+ }
16
+
17
+ // src/canonical.ts
18
+ function canonicalize(value) {
19
+ return serialize(value);
20
+ }
21
+ function serialize(value) {
22
+ if (value === null) {
23
+ return "null";
24
+ }
25
+ const kind = typeof value;
26
+ if (kind === "number") {
27
+ return Number.isFinite(value) ? String(value) : "null";
28
+ }
29
+ if (kind === "boolean") {
30
+ return value ? "true" : "false";
31
+ }
32
+ if (kind === "string") {
33
+ return JSON.stringify(value);
34
+ }
35
+ if (Array.isArray(value)) {
36
+ return `[${value.map((item) => serialize(item)).join(",")}]`;
37
+ }
38
+ if (kind === "object") {
39
+ const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
40
+ const body = entries.map(([key, v]) => `${JSON.stringify(key)}:${serialize(v)}`).join(",");
41
+ return `{${body}}`;
42
+ }
43
+ return "null";
44
+ }
45
+
46
+ // src/audit/index.ts
47
+ var GENESIS_HASH = "0".repeat(64);
48
+ async function sha256Hex(input) {
49
+ const bytes = new TextEncoder().encode(input);
50
+ const digest = await crypto.subtle.digest("SHA-256", bytes);
51
+ return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
52
+ }
53
+ function recordOf(index, payload, previousHash, timestamp) {
54
+ return timestamp !== void 0 ? { index, timestamp, payload, previousHash } : { index, payload, previousHash };
55
+ }
56
+ var AuditChain = class {
57
+ entries = [];
58
+ /** Append a payload, sealing it against the previous entry's hash. */
59
+ async append(payload, meta = {}) {
60
+ const index = this.entries.length;
61
+ const previousHash = index === 0 ? GENESIS_HASH : this.entries[index - 1].hash;
62
+ const record = recordOf(index, payload, previousHash, meta.timestamp);
63
+ const hash = await sha256Hex(canonicalize(record));
64
+ const entry = meta.timestamp !== void 0 ? { index, timestamp: meta.timestamp, payload, previousHash, hash } : { index, payload, previousHash, hash };
65
+ this.entries.push(entry);
66
+ return entry;
67
+ }
68
+ /** A copy of the chain's entries in order. */
69
+ toArray() {
70
+ return [...this.entries];
71
+ }
72
+ /** Number of entries in the chain. */
73
+ get length() {
74
+ return this.entries.length;
75
+ }
76
+ };
77
+ async function verifyChain(entries) {
78
+ for (let i = 0; i < entries.length; i++) {
79
+ const entry = entries[i];
80
+ const expectedPrevious = i === 0 ? GENESIS_HASH : entries[i - 1].hash;
81
+ if (entry.index !== i || entry.previousHash !== expectedPrevious) {
82
+ return { valid: false, brokenAt: i };
83
+ }
84
+ const record = recordOf(entry.index, entry.payload, entry.previousHash, entry.timestamp);
85
+ const recomputed = await sha256Hex(canonicalize(record));
86
+ if (recomputed !== entry.hash) {
87
+ return { valid: false, brokenAt: i };
88
+ }
89
+ }
90
+ return { valid: true };
91
+ }
92
+
93
+ // src/mathspecial.ts
94
+ var LN_SQRT_2PI = 0.9189385332046728;
95
+ var LANCZOS_G = 7;
96
+ var LANCZOS_COEFFICIENTS = [
97
+ 0.9999999999998099,
98
+ 676.5203681218851,
99
+ -1259.1392167224028,
100
+ 771.3234287776531,
101
+ -176.6150291621406,
102
+ 12.507343278686905,
103
+ -0.13857109526572012,
104
+ 9984369578019572e-21,
105
+ 15056327351493116e-23
106
+ ];
107
+ function lgamma(x) {
108
+ if (!Number.isFinite(x) || x <= 0) {
109
+ throw new BayesOutputGateError(
110
+ "NUMERIC",
111
+ `lgamma requires a positive finite argument, got ${x}`
112
+ );
113
+ }
114
+ const z = x - 1;
115
+ let acc = LANCZOS_COEFFICIENTS[0];
116
+ for (let i = 1; i < LANCZOS_COEFFICIENTS.length; i++) {
117
+ acc += LANCZOS_COEFFICIENTS[i] / (z + i);
118
+ }
119
+ const t = z + LANCZOS_G + 0.5;
120
+ return LN_SQRT_2PI + (z + 0.5) * Math.log(t) - t + Math.log(acc);
121
+ }
122
+ function lbeta(a, b) {
123
+ return lgamma(a) + lgamma(b) - lgamma(a + b);
124
+ }
125
+ function betaLogDensity(x, a, b) {
126
+ if (a <= 0 || b <= 0) {
127
+ throw new BayesOutputGateError(
128
+ "NUMERIC",
129
+ `Beta shape parameters must be positive, got a=${a}, b=${b}`
130
+ );
131
+ }
132
+ if (x < 0 || x > 1 || !Number.isFinite(x)) {
133
+ return Number.NEGATIVE_INFINITY;
134
+ }
135
+ if (x === 0) {
136
+ if (a < 1) return Number.POSITIVE_INFINITY;
137
+ if (a > 1) return Number.NEGATIVE_INFINITY;
138
+ return -lbeta(a, b) + (b - 1) * Math.log(1);
139
+ }
140
+ if (x === 1) {
141
+ if (b < 1) return Number.POSITIVE_INFINITY;
142
+ if (b > 1) return Number.NEGATIVE_INFINITY;
143
+ return -lbeta(a, b);
144
+ }
145
+ return (a - 1) * Math.log(x) + (b - 1) * Math.log1p(-x) - lbeta(a, b);
146
+ }
147
+ function logMarginalBernoulli(successes, trials, a, b) {
148
+ if (successes < 0 || trials < 0 || successes > trials) {
149
+ throw new BayesOutputGateError(
150
+ "NUMERIC",
151
+ `invalid Bernoulli counts: ${successes} successes in ${trials} trials`
152
+ );
153
+ }
154
+ if (a <= 0 || b <= 0) {
155
+ throw new BayesOutputGateError(
156
+ "NUMERIC",
157
+ `Beta prior parameters must be positive, got a=${a}, b=${b}`
158
+ );
159
+ }
160
+ return lbeta(successes + a, trials - successes + b) - lbeta(a, b);
161
+ }
162
+ function clamp(x, lo, hi) {
163
+ if (x < lo) return lo;
164
+ if (x > hi) return hi;
165
+ return x;
166
+ }
167
+ function regularizedIncompleteBeta(x, a, b) {
168
+ if (a <= 0 || b <= 0) {
169
+ throw new BayesOutputGateError(
170
+ "NUMERIC",
171
+ `incomplete beta requires positive shapes, got a=${a}, b=${b}`
172
+ );
173
+ }
174
+ if (x <= 0) return 0;
175
+ if (x >= 1) return 1;
176
+ const logFront = lgamma(a + b) - lgamma(a) - lgamma(b) + a * Math.log(x) + b * Math.log1p(-x);
177
+ const front = Math.exp(logFront);
178
+ if (x < (a + 1) / (a + b + 2)) {
179
+ return front * betaContinuedFraction(x, a, b) / a;
180
+ }
181
+ return 1 - front * betaContinuedFraction(1 - x, b, a) / b;
182
+ }
183
+ function betaCdf(x, a, b) {
184
+ return regularizedIncompleteBeta(x, a, b);
185
+ }
186
+ function betaContinuedFraction(x, a, b) {
187
+ const maxIterations = 200;
188
+ const epsilon = 3e-12;
189
+ const tiny = 1e-300;
190
+ const qab = a + b;
191
+ const qap = a + 1;
192
+ const qam = a - 1;
193
+ let c = 1;
194
+ let d = 1 - qab * x / qap;
195
+ if (Math.abs(d) < tiny) d = tiny;
196
+ d = 1 / d;
197
+ let h = d;
198
+ for (let m = 1; m <= maxIterations; m++) {
199
+ const m2 = 2 * m;
200
+ let aa = m * (b - m) * x / ((qam + m2) * (a + m2));
201
+ d = 1 + aa * d;
202
+ if (Math.abs(d) < tiny) d = tiny;
203
+ c = 1 + aa / c;
204
+ if (Math.abs(c) < tiny) c = tiny;
205
+ d = 1 / d;
206
+ h *= d * c;
207
+ aa = -(a + m) * (qab + m) * x / ((a + m2) * (qap + m2));
208
+ d = 1 + aa * d;
209
+ if (Math.abs(d) < tiny) d = tiny;
210
+ c = 1 + aa / c;
211
+ if (Math.abs(c) < tiny) c = tiny;
212
+ d = 1 / d;
213
+ const delta = d * c;
214
+ h *= delta;
215
+ if (Math.abs(delta - 1) < epsilon) {
216
+ break;
217
+ }
218
+ }
219
+ return h;
220
+ }
221
+
222
+ // src/bayesfactor/index.ts
223
+ var LN_3 = Math.log(3);
224
+ var LN_10 = Math.log(10);
225
+ var LN_100 = Math.log(100);
226
+ var DENSITY_EPSILON = 1e-6;
227
+ function jeffreysStrength(logBayesFactor) {
228
+ const x = logBayesFactor;
229
+ if (x >= LN_100) return "decisive-high";
230
+ if (x >= LN_10) return "strong-high";
231
+ if (x >= LN_3) return "substantial-high";
232
+ if (x > -LN_3) return "inconclusive";
233
+ if (x > -LN_10) return "substantial-low";
234
+ if (x > -LN_100) return "strong-low";
235
+ return "decisive-low";
236
+ }
237
+ function bayesFactor(scores, models) {
238
+ invariant(models.length > 0, "INVALID_CONFIG", "at least one dimension model is required");
239
+ const map = /* @__PURE__ */ new Map();
240
+ for (const s of scores) {
241
+ invariant(
242
+ Number.isFinite(s.value) && s.value >= 0 && s.value <= 1,
243
+ "INVALID_SCORE",
244
+ `score for "${s.dimension}" must be a finite number in [0, 1], got ${s.value}`
245
+ );
246
+ invariant(
247
+ !map.has(s.dimension),
248
+ "INVALID_SCORE",
249
+ `duplicate score for dimension "${s.dimension}"`
250
+ );
251
+ map.set(s.dimension, s.value);
252
+ }
253
+ const contributions = [];
254
+ let logBayesFactor = 0;
255
+ for (const model of models) {
256
+ const value = map.get(model.dimension);
257
+ if (value === void 0) {
258
+ continue;
259
+ }
260
+ invariant(
261
+ Number.isFinite(model.weight) && model.weight >= 0,
262
+ "INVALID_CONFIG",
263
+ `weight for "${model.dimension}" must be a non-negative finite number, got ${model.weight}`
264
+ );
265
+ const x = clamp(value, DENSITY_EPSILON, 1 - DENSITY_EPSILON);
266
+ const logHigh = betaLogDensity(x, model.high.a, model.high.b);
267
+ const logLow = betaLogDensity(x, model.low.a, model.low.b);
268
+ const weighted = model.weight * (logHigh - logLow);
269
+ logBayesFactor += weighted;
270
+ contributions.push({
271
+ dimension: model.dimension,
272
+ score: value,
273
+ logBayesFactor: weighted,
274
+ weight: model.weight
275
+ });
276
+ }
277
+ invariant(
278
+ contributions.length > 0,
279
+ "INVALID_SCORE",
280
+ "no scored dimension matched a configured dimension model"
281
+ );
282
+ invariant(Number.isFinite(logBayesFactor), "NUMERIC", "combined log-Bayes-Factor is not finite");
283
+ const strength = jeffreysStrength(logBayesFactor);
284
+ const favored = strength.endsWith("high") ? "high" : strength.endsWith("low") ? "low" : "inconclusive";
285
+ return {
286
+ logBayesFactor,
287
+ bayesFactor: Math.exp(logBayesFactor),
288
+ favored,
289
+ strength,
290
+ contributions
291
+ };
292
+ }
293
+
294
+ // src/dimensions/index.ts
295
+ function dependenceDiagnostic(history, options = {}) {
296
+ const threshold = options.threshold ?? 0.5;
297
+ invariant(
298
+ Number.isFinite(threshold) && threshold >= 0 && threshold <= 1,
299
+ "INVALID_CONFIG",
300
+ `threshold must be in [0, 1], got ${threshold}`
301
+ );
302
+ invariant(
303
+ history.length >= 2,
304
+ "INVALID_OBSERVATION",
305
+ "dependence diagnostic needs at least two score vectors"
306
+ );
307
+ const dimensionSet = /* @__PURE__ */ new Set();
308
+ for (const vector of history) {
309
+ for (const score of vector) {
310
+ dimensionSet.add(score.dimension);
311
+ }
312
+ }
313
+ const dimensions = [...dimensionSet].sort();
314
+ const rows = history.map((vector) => {
315
+ const map = /* @__PURE__ */ new Map();
316
+ for (const score of vector) {
317
+ map.set(score.dimension, score.value);
318
+ }
319
+ return map;
320
+ });
321
+ const pairs = [];
322
+ for (let i = 0; i < dimensions.length; i++) {
323
+ for (let j = i + 1; j < dimensions.length; j++) {
324
+ const a = dimensions[i];
325
+ const b = dimensions[j];
326
+ const xs = [];
327
+ const ys = [];
328
+ for (const row of rows) {
329
+ const x = row.get(a);
330
+ const y = row.get(b);
331
+ if (x !== void 0 && y !== void 0) {
332
+ xs.push(x);
333
+ ys.push(y);
334
+ }
335
+ }
336
+ if (xs.length >= 2) {
337
+ const correlation = pearson(xs, ys);
338
+ if (Number.isFinite(correlation)) {
339
+ pairs.push({ a, b, correlation, samples: xs.length });
340
+ }
341
+ }
342
+ }
343
+ }
344
+ pairs.sort((left, right) => Math.abs(right.correlation) - Math.abs(left.correlation));
345
+ const maxAbsCorrelation = pairs.length > 0 ? Math.abs(pairs[0].correlation) : 0;
346
+ const flagged = pairs.filter((pair) => Math.abs(pair.correlation) >= threshold);
347
+ return {
348
+ dimensions,
349
+ pairs,
350
+ flagged,
351
+ maxAbsCorrelation,
352
+ independenceAssumptionSafe: flagged.length === 0
353
+ };
354
+ }
355
+ function pearson(xs, ys) {
356
+ const n = xs.length;
357
+ let sumX = 0;
358
+ let sumY = 0;
359
+ for (let i = 0; i < n; i++) {
360
+ sumX += xs[i];
361
+ sumY += ys[i];
362
+ }
363
+ const meanX = sumX / n;
364
+ const meanY = sumY / n;
365
+ let covariance = 0;
366
+ let varianceX = 0;
367
+ let varianceY = 0;
368
+ for (let i = 0; i < n; i++) {
369
+ const dx = xs[i] - meanX;
370
+ const dy = ys[i] - meanY;
371
+ covariance += dx * dy;
372
+ varianceX += dx * dx;
373
+ varianceY += dy * dy;
374
+ }
375
+ if (varianceX === 0 || varianceY === 0) {
376
+ return 0;
377
+ }
378
+ return covariance / Math.sqrt(varianceX * varianceY);
379
+ }
380
+
381
+ // src/calibration/index.ts
382
+ function goodnessOfFit(samples, params, alpha = 0.05) {
383
+ invariant(
384
+ samples.length >= 5,
385
+ "INVALID_OBSERVATION",
386
+ "goodnessOfFit needs at least five samples"
387
+ );
388
+ invariant(params.a > 0 && params.b > 0, "INVALID_CONFIG", "Beta parameters must be positive");
389
+ const sorted = [...samples].sort((left, right) => left - right);
390
+ const n = sorted.length;
391
+ let ks = 0;
392
+ for (let i = 0; i < n; i++) {
393
+ const value = sorted[i];
394
+ invariant(
395
+ Number.isFinite(value) && value >= 0 && value <= 1,
396
+ "INVALID_SCORE",
397
+ `samples must be finite and in [0, 1], got ${value}`
398
+ );
399
+ const cdf = betaCdf(value, params.a, params.b);
400
+ const upper = (i + 1) / n - cdf;
401
+ const lower = cdf - i / n;
402
+ ks = Math.max(ks, upper, lower);
403
+ }
404
+ const coefficient = alpha <= 0.01 ? 1.628 : alpha <= 0.05 ? 1.358 : 1.224;
405
+ const criticalValue = coefficient / Math.sqrt(n);
406
+ return { ksStatistic: ks, criticalValue, adequate: ks <= criticalValue, samples: n };
407
+ }
408
+ function brierScore(predictions) {
409
+ invariant(
410
+ predictions.length > 0,
411
+ "INVALID_OBSERVATION",
412
+ "brierScore needs at least one prediction"
413
+ );
414
+ let sum = 0;
415
+ for (const prediction of predictions) {
416
+ validatePrediction(prediction);
417
+ sum += (prediction.probability - prediction.outcome) ** 2;
418
+ }
419
+ return sum / predictions.length;
420
+ }
421
+ function reliability(predictions, bins = 10) {
422
+ invariant(
423
+ Number.isInteger(bins) && bins >= 1,
424
+ "INVALID_CONFIG",
425
+ `bins must be a positive integer, got ${bins}`
426
+ );
427
+ invariant(
428
+ predictions.length > 0,
429
+ "INVALID_OBSERVATION",
430
+ "reliability needs at least one prediction"
431
+ );
432
+ const sums = new Array(bins).fill(0);
433
+ const hits = new Array(bins).fill(0);
434
+ const counts = new Array(bins).fill(0);
435
+ for (const prediction of predictions) {
436
+ validatePrediction(prediction);
437
+ const index = Math.min(bins - 1, Math.floor(prediction.probability * bins));
438
+ sums[index] = sums[index] + prediction.probability;
439
+ hits[index] = hits[index] + prediction.outcome;
440
+ counts[index] = counts[index] + 1;
441
+ }
442
+ const out = [];
443
+ for (let b = 0; b < bins; b++) {
444
+ const count = counts[b];
445
+ out.push({
446
+ lower: b / bins,
447
+ upper: (b + 1) / bins,
448
+ count,
449
+ meanPredicted: count > 0 ? sums[b] / count : 0,
450
+ empiricalRate: count > 0 ? hits[b] / count : 0
451
+ });
452
+ }
453
+ return out;
454
+ }
455
+ function expectedCalibrationError(predictions, bins = 10) {
456
+ const diagram = reliability(predictions, bins);
457
+ const total = predictions.length;
458
+ let ece = 0;
459
+ for (const bin of diagram) {
460
+ if (bin.count > 0) {
461
+ ece += bin.count / total * Math.abs(bin.empiricalRate - bin.meanPredicted);
462
+ }
463
+ }
464
+ return ece;
465
+ }
466
+ function scoresForDimension(history, dimension, label) {
467
+ const out = [];
468
+ for (const observation of history) {
469
+ if (observation.label !== label) {
470
+ continue;
471
+ }
472
+ for (const score of observation.scores) {
473
+ if (score.dimension === dimension) {
474
+ out.push(score.value);
475
+ }
476
+ }
477
+ }
478
+ return out;
479
+ }
480
+ function assessAssumptions(history, models, options = {}) {
481
+ const alpha = options.alpha ?? 0.05;
482
+ const minSamples = options.minSamples ?? 5;
483
+ const inadequateDimensions = [];
484
+ for (const model of models) {
485
+ const high = scoresForDimension(history, model.dimension, "high");
486
+ const low = scoresForDimension(history, model.dimension, "low");
487
+ const highBad = high.length >= minSamples && !goodnessOfFit(high, model.high, alpha).adequate;
488
+ const lowBad = low.length >= minSamples && !goodnessOfFit(low, model.low, alpha).adequate;
489
+ if (highBad || lowBad) {
490
+ inadequateDimensions.push(model.dimension);
491
+ }
492
+ }
493
+ const vectors = history.map((observation) => observation.scores);
494
+ let independenceAssumptionSafe = true;
495
+ const dependentPairs = [];
496
+ if (vectors.length >= 2) {
497
+ const diagnostic = dependenceDiagnostic(
498
+ vectors,
499
+ options.dependenceThreshold !== void 0 ? { threshold: options.dependenceThreshold } : {}
500
+ );
501
+ independenceAssumptionSafe = diagnostic.independenceAssumptionSafe;
502
+ for (const pair of diagnostic.flagged) {
503
+ dependentPairs.push(`${pair.a}~${pair.b}`);
504
+ }
505
+ }
506
+ return {
507
+ goodnessOfFitAdequate: inadequateDimensions.length === 0,
508
+ independenceAssumptionSafe,
509
+ inadequateDimensions,
510
+ dependentPairs
511
+ };
512
+ }
513
+ function validatePrediction(prediction) {
514
+ invariant(
515
+ Number.isFinite(prediction.probability) && prediction.probability >= 0 && prediction.probability <= 1,
516
+ "INVALID_SCORE",
517
+ `prediction probability must be in [0, 1], got ${prediction.probability}`
518
+ );
519
+ invariant(
520
+ prediction.outcome === 0 || prediction.outcome === 1,
521
+ "INVALID_OBSERVATION",
522
+ `prediction outcome must be 0 or 1, got ${prediction.outcome}`
523
+ );
524
+ }
525
+
526
+ // src/beta/index.ts
527
+ var VARIANCE_FLOOR = 1e-9;
528
+ var CONCENTRATION_FLOOR = 1e-3;
529
+ var BetaModel = class _BetaModel {
530
+ priorA;
531
+ priorB;
532
+ count = 0;
533
+ sum = 0;
534
+ sumSquares = 0;
535
+ constructor(options = {}) {
536
+ const prior = options.prior ?? { a: 1, b: 1 };
537
+ invariant(
538
+ prior.a > 0 && prior.b > 0 && Number.isFinite(prior.a) && Number.isFinite(prior.b),
539
+ "INVALID_CONFIG",
540
+ `prior Beta parameters must be positive and finite, got a=${prior.a}, b=${prior.b}`
541
+ );
542
+ this.priorA = prior.a;
543
+ this.priorB = prior.b;
544
+ }
545
+ /** Build a model from labeled scores in one pass. */
546
+ static fromSamples(samples, options = {}) {
547
+ const model = new _BetaModel(options);
548
+ for (const s of samples) {
549
+ model.observe(s);
550
+ }
551
+ return model;
552
+ }
553
+ /** Restore a model from a snapshot. */
554
+ static fromSnapshot(snapshot) {
555
+ const model = new _BetaModel({ prior: snapshot.prior });
556
+ invariant(
557
+ snapshot.count >= 0 && Number.isFinite(snapshot.sum) && Number.isFinite(snapshot.sumSquares),
558
+ "INVALID_SNAPSHOT",
559
+ "snapshot fields must be finite and non-negative"
560
+ );
561
+ model.count = snapshot.count;
562
+ model.sum = snapshot.sum;
563
+ model.sumSquares = snapshot.sumSquares;
564
+ return model;
565
+ }
566
+ /** Number of real observations folded into this model so far. */
567
+ get observations() {
568
+ return this.count;
569
+ }
570
+ /** Fold a single score in [0, 1] into the model, updating its calibration online. */
571
+ observe(score) {
572
+ invariant(
573
+ Number.isFinite(score) && score >= 0 && score <= 1,
574
+ "INVALID_SCORE",
575
+ `score must be a finite number in [0, 1], got ${score}`
576
+ );
577
+ this.count += 1;
578
+ this.sum += score;
579
+ this.sumSquares += score * score;
580
+ return this;
581
+ }
582
+ /**
583
+ * The fitted Beta parameters. The prior is mixed in as `priorA + priorB` pseudo-observations with
584
+ * the prior's own mean and variance, then a and b come from method of moments on the blend, so a
585
+ * cold model returns its prior and a warm model is data-driven.
586
+ */
587
+ params() {
588
+ const priorStrength = this.priorA + this.priorB;
589
+ const priorMean = this.priorA / priorStrength;
590
+ const priorVariance = priorMean * (1 - priorMean) / (priorStrength + 1);
591
+ const totalCount = this.count + priorStrength;
592
+ const totalSum = this.sum + priorStrength * priorMean;
593
+ const totalSumSquares = this.sumSquares + priorStrength * (priorVariance + priorMean * priorMean);
594
+ const mean = clamp(totalSum / totalCount, VARIANCE_FLOOR, 1 - VARIANCE_FLOOR);
595
+ const rawVariance = totalSumSquares / totalCount - mean * mean;
596
+ const maxVariance = mean * (1 - mean);
597
+ const variance = clamp(rawVariance, VARIANCE_FLOOR, maxVariance * (1 - VARIANCE_FLOOR));
598
+ const concentration = Math.max(mean * (1 - mean) / variance - 1, CONCENTRATION_FLOOR);
599
+ const a = Math.max(mean * concentration, CONCENTRATION_FLOOR);
600
+ const b = Math.max((1 - mean) * concentration, CONCENTRATION_FLOOR);
601
+ if (!Number.isFinite(a) || !Number.isFinite(b)) {
602
+ throw new BayesOutputGateError("NUMERIC", "Beta fit produced non-finite parameters");
603
+ }
604
+ return { a, b };
605
+ }
606
+ /** Mean of the fitted Beta. */
607
+ mean() {
608
+ const { a, b } = this.params();
609
+ return a / (a + b);
610
+ }
611
+ /** Log-density of an observed score under the fitted Beta. */
612
+ logDensity(score) {
613
+ const { a, b } = this.params();
614
+ return betaLogDensity(score, a, b);
615
+ }
616
+ /** A serializable snapshot of the current state. */
617
+ snapshot() {
618
+ return {
619
+ count: this.count,
620
+ sum: this.sum,
621
+ sumSquares: this.sumSquares,
622
+ prior: { a: this.priorA, b: this.priorB }
623
+ };
624
+ }
625
+ };
626
+
627
+ // src/hypothesis/index.ts
628
+ var HypothesisManager = class {
629
+ entries = /* @__PURE__ */ new Map();
630
+ defaultWeight;
631
+ defaultPriorHigh;
632
+ defaultPriorLow;
633
+ configured = /* @__PURE__ */ new Map();
634
+ constructor(options = {}) {
635
+ this.defaultWeight = options.defaultWeight ?? 1;
636
+ this.defaultPriorHigh = options.defaultPriorHigh ?? { a: 2, b: 1 };
637
+ this.defaultPriorLow = options.defaultPriorLow ?? { a: 1, b: 2 };
638
+ invariant(
639
+ Number.isFinite(this.defaultWeight) && this.defaultWeight >= 0,
640
+ "INVALID_CONFIG",
641
+ `defaultWeight must be a non-negative finite number, got ${this.defaultWeight}`
642
+ );
643
+ for (const config of options.dimensions ?? []) {
644
+ this.configured.set(config.dimension, config);
645
+ this.ensure(config.dimension);
646
+ }
647
+ }
648
+ ensure(dimension) {
649
+ const existing = this.entries.get(dimension);
650
+ if (existing) {
651
+ return existing;
652
+ }
653
+ const config = this.configured.get(dimension);
654
+ const entry = {
655
+ high: new BetaModel({ prior: config?.priorHigh ?? this.defaultPriorHigh }),
656
+ low: new BetaModel({ prior: config?.priorLow ?? this.defaultPriorLow }),
657
+ weight: config?.weight ?? this.defaultWeight
658
+ };
659
+ invariant(
660
+ Number.isFinite(entry.weight) && entry.weight >= 0,
661
+ "INVALID_CONFIG",
662
+ `weight for "${dimension}" must be a non-negative finite number, got ${entry.weight}`
663
+ );
664
+ this.entries.set(dimension, entry);
665
+ return entry;
666
+ }
667
+ /** Fold one labeled output into the high or low model of each scored dimension. */
668
+ observe(observation) {
669
+ invariant(
670
+ observation.label === "high" || observation.label === "low",
671
+ "INVALID_OBSERVATION",
672
+ `observation label must be "high" or "low", got ${String(observation.label)}`
673
+ );
674
+ for (const score of observation.scores) {
675
+ const entry = this.ensure(score.dimension);
676
+ const model = observation.label === "high" ? entry.high : entry.low;
677
+ model.observe(score.value);
678
+ }
679
+ return this;
680
+ }
681
+ /** Fold a batch of labeled observations. */
682
+ fit(observations) {
683
+ for (const observation of observations) {
684
+ this.observe(observation);
685
+ }
686
+ return this;
687
+ }
688
+ /** The dimension names this manager currently tracks. */
689
+ get dimensions() {
690
+ return [...this.entries.keys()];
691
+ }
692
+ /** Snapshot the current calibrated models as the dimension models the Bayes Factor consumes. */
693
+ models() {
694
+ const out = [];
695
+ for (const [dimension, entry] of this.entries) {
696
+ out.push({
697
+ dimension,
698
+ high: entry.high.params(),
699
+ low: entry.low.params(),
700
+ weight: entry.weight
701
+ });
702
+ }
703
+ return out;
704
+ }
705
+ /** Number of labeled observations folded into a dimension under one hypothesis. */
706
+ observationCount(dimension, kind) {
707
+ const entry = this.entries.get(dimension);
708
+ if (!entry) {
709
+ return 0;
710
+ }
711
+ return (kind === "high" ? entry.high : entry.low).observations;
712
+ }
713
+ };
714
+
715
+ // src/policy/index.ts
716
+ function posteriorHighQuality(logBayesFactor, priorHighQuality) {
717
+ invariant(
718
+ priorHighQuality > 0 && priorHighQuality < 1,
719
+ "INVALID_CONFIG",
720
+ `priorHighQuality must be in (0, 1), got ${priorHighQuality}`
721
+ );
722
+ invariant(
723
+ Number.isFinite(logBayesFactor),
724
+ "NUMERIC",
725
+ `logBayesFactor must be finite, got ${logBayesFactor}`
726
+ );
727
+ const logPosteriorOdds = logBayesFactor + Math.log(priorHighQuality / (1 - priorHighQuality));
728
+ if (logPosteriorOdds >= 0) {
729
+ return 1 / (1 + Math.exp(-logPosteriorOdds));
730
+ }
731
+ const odds = Math.exp(logPosteriorOdds);
732
+ return odds / (1 + odds);
733
+ }
734
+ function decide(logBayesFactor, policy) {
735
+ if (policy.kind === "bayes-factor") {
736
+ invariant(
737
+ policy.passAbove >= 1,
738
+ "INVALID_CONFIG",
739
+ `passAbove must be >= 1, got ${policy.passAbove}`
740
+ );
741
+ invariant(
742
+ policy.failBelow > 0 && policy.failBelow <= 1,
743
+ "INVALID_CONFIG",
744
+ `failBelow must be in (0, 1], got ${policy.failBelow}`
745
+ );
746
+ invariant(
747
+ policy.passAbove > policy.failBelow,
748
+ "INVALID_CONFIG",
749
+ "passAbove must be greater than failBelow"
750
+ );
751
+ const bayesFactor2 = Math.exp(logBayesFactor);
752
+ const action2 = bayesFactor2 >= policy.passAbove ? "pass" : bayesFactor2 <= policy.failBelow ? "fail" : "escalate";
753
+ return { action: action2, rationale: "bayes-factor" };
754
+ }
755
+ invariant(
756
+ policy.lossFalsePass >= 0 && policy.lossFalseFail >= 0 && policy.escalationCost >= 0,
757
+ "INVALID_CONFIG",
758
+ "decision-theoretic losses must be non-negative"
759
+ );
760
+ invariant(
761
+ Number.isFinite(policy.lossFalsePass) && Number.isFinite(policy.lossFalseFail) && Number.isFinite(policy.escalationCost),
762
+ "INVALID_CONFIG",
763
+ "decision-theoretic losses must be finite"
764
+ );
765
+ const posterior = posteriorHighQuality(logBayesFactor, policy.priorHighQuality);
766
+ const expectedLoss = {
767
+ pass: (1 - posterior) * policy.lossFalsePass,
768
+ fail: posterior * policy.lossFalseFail,
769
+ escalate: policy.escalationCost
770
+ };
771
+ let action = "pass";
772
+ let best = expectedLoss.pass;
773
+ if (expectedLoss.fail < best) {
774
+ action = "fail";
775
+ best = expectedLoss.fail;
776
+ }
777
+ if (expectedLoss.escalate < best) {
778
+ action = "escalate";
779
+ }
780
+ return { action, rationale: "expected-loss", posteriorHighQuality: posterior, expectedLoss };
781
+ }
782
+
783
+ // src/gate/index.ts
784
+ function toDecision(factor, policy, guards) {
785
+ const base = {
786
+ action: policy.action,
787
+ bayesFactor: factor.bayesFactor,
788
+ logBayesFactor: factor.logBayesFactor,
789
+ strength: factor.strength,
790
+ rationale: policy.rationale,
791
+ contributions: factor.contributions,
792
+ ...policy.posteriorHighQuality !== void 0 ? { posteriorHighQuality: policy.posteriorHighQuality } : {},
793
+ ...policy.expectedLoss !== void 0 ? { expectedLoss: policy.expectedLoss } : {}
794
+ };
795
+ const assumptions = guards?.assumptions;
796
+ if (assumptions === void 0) {
797
+ return base;
798
+ }
799
+ const goodnessOfFitViolated = guards?.requireGoodnessOfFit === true && !assumptions.goodnessOfFitAdequate;
800
+ const independenceViolated = guards?.requireIndependence === true && !assumptions.independenceAssumptionSafe;
801
+ if (goodnessOfFitViolated || independenceViolated) {
802
+ return { ...base, action: "escalate", rationale: "assumption-violated", assumptions };
803
+ }
804
+ return { ...base, assumptions };
805
+ }
806
+ function evaluate(scores, models, policy, guards) {
807
+ const factor = bayesFactor(scores, models);
808
+ const decision = decide(factor.logBayesFactor, policy);
809
+ return toDecision(factor, decision, guards);
810
+ }
811
+ var OutputGate = class {
812
+ models;
813
+ policy;
814
+ guards;
815
+ constructor(options) {
816
+ invariant(
817
+ options.models.length > 0,
818
+ "INVALID_CONFIG",
819
+ "a gate needs at least one dimension model"
820
+ );
821
+ this.models = [...options.models];
822
+ this.policy = options.policy;
823
+ this.guards = options.guards;
824
+ }
825
+ /** Evaluate one output's scores. */
826
+ evaluate(scores) {
827
+ return evaluate(scores, this.models, this.policy, this.guards);
828
+ }
829
+ /** A copy of the gate's dimension models. */
830
+ dimensionModels() {
831
+ return [...this.models];
832
+ }
833
+ };
834
+ var OutputGateMonitor = class {
835
+ manager;
836
+ policy;
837
+ chain = new AuditChain();
838
+ guards;
839
+ assumptions;
840
+ constructor(options) {
841
+ this.manager = options.manager ?? new HypothesisManager();
842
+ this.policy = options.policy;
843
+ this.guards = options.guards;
844
+ }
845
+ /** Fold one labeled observation into the calibration. Does not recompute the assumption report. */
846
+ observe(observation) {
847
+ this.manager.observe(observation);
848
+ return this;
849
+ }
850
+ /** Fold a batch of labeled observations, and reassess the modeling assumptions when guarded. */
851
+ fit(observations) {
852
+ const list = [...observations];
853
+ for (const observation of list) {
854
+ this.manager.observe(observation);
855
+ }
856
+ if (this.guards !== void 0 && (this.guards.requireGoodnessOfFit === true || this.guards.requireIndependence === true)) {
857
+ this.assumptions = assessAssumptions(list, this.manager.models(), this.guards);
858
+ }
859
+ return this;
860
+ }
861
+ /** Evaluate without recording. */
862
+ evaluate(scores) {
863
+ return evaluate(scores, this.manager.models(), this.policy, this.guardsForEvaluate());
864
+ }
865
+ guardsForEvaluate() {
866
+ if (this.guards === void 0) {
867
+ return void 0;
868
+ }
869
+ return {
870
+ ...this.assumptions !== void 0 ? { assumptions: this.assumptions } : {},
871
+ ...this.guards.requireGoodnessOfFit !== void 0 ? { requireGoodnessOfFit: this.guards.requireGoodnessOfFit } : {},
872
+ ...this.guards.requireIndependence !== void 0 ? { requireIndependence: this.guards.requireIndependence } : {}
873
+ };
874
+ }
875
+ /** Evaluate and seal the decision into the audit chain. */
876
+ async record(scores, meta = {}) {
877
+ const decision = this.evaluate(scores);
878
+ const entry = await this.chain.append(decision, meta);
879
+ return { decision, entry };
880
+ }
881
+ /** A copy of the audit chain. */
882
+ auditTrail() {
883
+ return this.chain.toArray();
884
+ }
885
+ /** Number of sealed decisions. */
886
+ get auditLength() {
887
+ return this.chain.length;
888
+ }
889
+ /** The most recent assumption report, present after a guarded {@link OutputGateMonitor.fit}. */
890
+ get assumptionReport() {
891
+ return this.assumptions;
892
+ }
893
+ /** The current calibrated dimension models. */
894
+ models() {
895
+ return this.manager.models();
896
+ }
897
+ };
898
+
899
+ // src/adapter/index.ts
900
+ var TOOL_NAME = "bayes_output_gate";
901
+ var DEFAULT_POLICY = { kind: "bayes-factor", passAbove: 10, failBelow: 0.1 };
902
+ function toJsonSafe(value) {
903
+ if (typeof value === "number") {
904
+ return Number.isFinite(value) ? value : null;
905
+ }
906
+ if (Array.isArray(value)) {
907
+ return value.map((item) => toJsonSafe(item));
908
+ }
909
+ if (value !== null && typeof value === "object") {
910
+ const out = {};
911
+ for (const [key, item] of Object.entries(value)) {
912
+ out[key] = toJsonSafe(item);
913
+ }
914
+ return out;
915
+ }
916
+ return value;
917
+ }
918
+ function asObject(value, message) {
919
+ invariant(
920
+ typeof value === "object" && value !== null && !Array.isArray(value),
921
+ "INVALID_CONFIG",
922
+ message
923
+ );
924
+ return value;
925
+ }
926
+ function parseBeta(value, where) {
927
+ const obj = asObject(value, `${where} must be an object with positive a and b`);
928
+ const a = obj["a"];
929
+ const b = obj["b"];
930
+ invariant(
931
+ typeof a === "number" && typeof b === "number" && a > 0 && b > 0,
932
+ "INVALID_CONFIG",
933
+ `${where} must have positive numeric a and b`
934
+ );
935
+ return { a, b };
936
+ }
937
+ function parseScore(value) {
938
+ const obj = asObject(value, "each score must be an object with dimension and value");
939
+ const dimension = obj["dimension"];
940
+ const score = obj["value"];
941
+ invariant(
942
+ typeof dimension === "string" && dimension.length > 0,
943
+ "INVALID_SCORE",
944
+ "score dimension must be a non-empty string"
945
+ );
946
+ invariant(
947
+ typeof score === "number" && Number.isFinite(score) && score >= 0 && score <= 1,
948
+ "INVALID_SCORE",
949
+ `score value for "${dimension}" must be a finite number in [0, 1]`
950
+ );
951
+ return { dimension, value: score };
952
+ }
953
+ function parseModel(value) {
954
+ const obj = asObject(value, "each model must be an object");
955
+ const dimension = obj["dimension"];
956
+ invariant(
957
+ typeof dimension === "string" && dimension.length > 0,
958
+ "INVALID_CONFIG",
959
+ "model dimension must be a non-empty string"
960
+ );
961
+ const rawWeight = obj["weight"];
962
+ const weight = rawWeight === void 0 ? 1 : rawWeight;
963
+ invariant(
964
+ typeof weight === "number" && Number.isFinite(weight) && weight >= 0,
965
+ "INVALID_CONFIG",
966
+ `model weight for "${dimension}" must be a non-negative number`
967
+ );
968
+ return {
969
+ dimension,
970
+ high: parseBeta(obj["high"], `model "${dimension}" high`),
971
+ low: parseBeta(obj["low"], `model "${dimension}" low`),
972
+ weight
973
+ };
974
+ }
975
+ function parsePolicy(value) {
976
+ if (value === void 0) {
977
+ return DEFAULT_POLICY;
978
+ }
979
+ const obj = asObject(value, "policy must be an object");
980
+ const kind = obj["kind"];
981
+ if (kind === "decision-theoretic") {
982
+ const prior = obj["priorHighQuality"];
983
+ const lossFalsePass = obj["lossFalsePass"];
984
+ const lossFalseFail = obj["lossFalseFail"];
985
+ const escalationCost = obj["escalationCost"];
986
+ invariant(
987
+ typeof prior === "number" && typeof lossFalsePass === "number" && typeof lossFalseFail === "number" && typeof escalationCost === "number",
988
+ "INVALID_CONFIG",
989
+ "decision-theoretic policy requires numeric priorHighQuality, lossFalsePass, lossFalseFail, escalationCost"
990
+ );
991
+ return {
992
+ kind: "decision-theoretic",
993
+ priorHighQuality: prior,
994
+ lossFalsePass,
995
+ lossFalseFail,
996
+ escalationCost
997
+ };
998
+ }
999
+ invariant(kind === "bayes-factor", "INVALID_CONFIG", `unknown policy kind ${String(kind)}`);
1000
+ const passAbove = obj["passAbove"];
1001
+ const failBelow = obj["failBelow"];
1002
+ invariant(
1003
+ typeof passAbove === "number" && typeof failBelow === "number",
1004
+ "INVALID_CONFIG",
1005
+ "bayes-factor policy requires numeric passAbove and failBelow"
1006
+ );
1007
+ return { kind: "bayes-factor", passAbove, failBelow };
1008
+ }
1009
+ function parseGateInput(input) {
1010
+ const obj = asObject(input, "tool input must be an object");
1011
+ invariant(Array.isArray(obj["scores"]), "INVALID_SCORE", "scores must be an array");
1012
+ invariant(Array.isArray(obj["models"]), "INVALID_CONFIG", "models must be an array");
1013
+ const scores = obj["scores"].map(parseScore);
1014
+ const models = obj["models"].map(parseModel);
1015
+ invariant(models.length > 0, "INVALID_CONFIG", "at least one model is required");
1016
+ return { scores, models, policy: parsePolicy(obj["policy"]) };
1017
+ }
1018
+ function runTool(input) {
1019
+ const { scores, models, policy } = parseGateInput(input);
1020
+ return evaluate(scores, models, policy);
1021
+ }
1022
+ function describeTool() {
1023
+ return {
1024
+ name: TOOL_NAME,
1025
+ description: "Weigh an output's per-dimension quality scores against a high-quality and a low-quality hypothesis with a calibrated Bayes Factor, and return a pass, fail, or escalate decision on the Jeffreys scale.",
1026
+ inputSchema: {
1027
+ type: "object",
1028
+ required: ["scores", "models"],
1029
+ properties: {
1030
+ scores: {
1031
+ type: "array",
1032
+ items: {
1033
+ type: "object",
1034
+ required: ["dimension", "value"],
1035
+ properties: {
1036
+ dimension: { type: "string" },
1037
+ value: { type: "number", minimum: 0, maximum: 1 }
1038
+ }
1039
+ }
1040
+ },
1041
+ models: {
1042
+ type: "array",
1043
+ items: {
1044
+ type: "object",
1045
+ required: ["dimension", "high", "low"],
1046
+ properties: {
1047
+ dimension: { type: "string" },
1048
+ high: { type: "object", required: ["a", "b"] },
1049
+ low: { type: "object", required: ["a", "b"] },
1050
+ weight: { type: "number", minimum: 0 }
1051
+ }
1052
+ }
1053
+ },
1054
+ policy: { type: "object" }
1055
+ }
1056
+ }
1057
+ };
1058
+ }
1059
+ var bayesOutputGateTool = {
1060
+ ...describeTool(),
1061
+ handler: (input) => toJsonSafe(runTool(input))
1062
+ };
1063
+
1064
+ // src/likelihood/index.ts
1065
+ function logScoreLikelihood(score, params) {
1066
+ return betaLogDensity(score, params.a, params.b);
1067
+ }
1068
+ function logBinaryMarginalLikelihood(successes, trials, prior) {
1069
+ return logMarginalBernoulli(successes, trials, prior.a, prior.b);
1070
+ }
1071
+ function toScoreMap(scores) {
1072
+ const map = /* @__PURE__ */ new Map();
1073
+ for (const s of scores) {
1074
+ invariant(
1075
+ Number.isFinite(s.value) && s.value >= 0 && s.value <= 1,
1076
+ "INVALID_SCORE",
1077
+ `score for "${s.dimension}" must be a finite number in [0, 1], got ${s.value}`
1078
+ );
1079
+ invariant(
1080
+ !map.has(s.dimension),
1081
+ "INVALID_SCORE",
1082
+ `duplicate score for dimension "${s.dimension}"`
1083
+ );
1084
+ map.set(s.dimension, s.value);
1085
+ }
1086
+ return map;
1087
+ }
1088
+ function logVectorLikelihood(scores, models, kind) {
1089
+ const map = toScoreMap(scores);
1090
+ let logLikelihood = 0;
1091
+ let matched = 0;
1092
+ for (const model of models) {
1093
+ const value = map.get(model.dimension);
1094
+ if (value === void 0) {
1095
+ continue;
1096
+ }
1097
+ invariant(
1098
+ Number.isFinite(model.weight) && model.weight >= 0,
1099
+ "INVALID_CONFIG",
1100
+ `weight for "${model.dimension}" must be a non-negative finite number, got ${model.weight}`
1101
+ );
1102
+ const params = kind === "high" ? model.high : model.low;
1103
+ logLikelihood += model.weight * betaLogDensity(value, params.a, params.b);
1104
+ matched += 1;
1105
+ }
1106
+ return { logLikelihood, matched };
1107
+ }
1108
+
1109
+ export { AuditChain, BayesOutputGateError, BetaModel, GENESIS_HASH, HypothesisManager, OutputGate, OutputGateMonitor, TOOL_NAME, assessAssumptions, bayesFactor, bayesOutputGateTool, brierScore, decide, dependenceDiagnostic, describeTool, evaluate, expectedCalibrationError, goodnessOfFit, invariant, jeffreysStrength, logBinaryMarginalLikelihood, logScoreLikelihood, logVectorLikelihood, parseGateInput, posteriorHighQuality, reliability, runTool, sha256Hex, toJsonSafe, toScoreMap, verifyChain };