open-multi-agent-kit 1.0.0 → 1.2.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 (244) hide show
  1. package/CHANGELOG.md +6 -1
  2. package/README.md +2 -2
  3. package/dist/cli/help.d.ts.map +1 -1
  4. package/dist/cli/help.js +1 -0
  5. package/dist/cli/help.js.map +1 -1
  6. package/dist/coordination/awareness.d.ts +32 -0
  7. package/dist/coordination/awareness.d.ts.map +1 -0
  8. package/dist/coordination/awareness.js +55 -0
  9. package/dist/coordination/awareness.js.map +1 -0
  10. package/dist/coordination/broker.d.ts +79 -0
  11. package/dist/coordination/broker.d.ts.map +1 -0
  12. package/dist/coordination/broker.js +195 -0
  13. package/dist/coordination/broker.js.map +1 -0
  14. package/dist/coordination/index.d.ts +16 -0
  15. package/dist/coordination/index.d.ts.map +1 -0
  16. package/dist/coordination/index.js +16 -0
  17. package/dist/coordination/index.js.map +1 -0
  18. package/dist/coordination/integration.d.ts +59 -0
  19. package/dist/coordination/integration.d.ts.map +1 -0
  20. package/dist/coordination/integration.js +126 -0
  21. package/dist/coordination/integration.js.map +1 -0
  22. package/dist/coordination/operation.d.ts +106 -0
  23. package/dist/coordination/operation.d.ts.map +1 -0
  24. package/dist/coordination/operation.js +179 -0
  25. package/dist/coordination/operation.js.map +1 -0
  26. package/dist/coordination/resource.d.ts +33 -0
  27. package/dist/coordination/resource.d.ts.map +1 -0
  28. package/dist/coordination/resource.js +89 -0
  29. package/dist/coordination/resource.js.map +1 -0
  30. package/dist/coordination/session.d.ts +74 -0
  31. package/dist/coordination/session.d.ts.map +1 -0
  32. package/dist/coordination/session.js +143 -0
  33. package/dist/coordination/session.js.map +1 -0
  34. package/dist/coordination/types.d.ts +68 -0
  35. package/dist/coordination/types.d.ts.map +1 -0
  36. package/dist/coordination/types.js +26 -0
  37. package/dist/coordination/types.js.map +1 -0
  38. package/dist/core/agent-session.d.ts.map +1 -1
  39. package/dist/core/agent-session.js +28 -7
  40. package/dist/core/agent-session.js.map +1 -1
  41. package/dist/core/compaction/control-state.d.ts +41 -0
  42. package/dist/core/compaction/control-state.d.ts.map +1 -0
  43. package/dist/core/compaction/control-state.js +85 -0
  44. package/dist/core/compaction/control-state.js.map +1 -0
  45. package/dist/core/compaction/fallback.d.ts +60 -0
  46. package/dist/core/compaction/fallback.d.ts.map +1 -0
  47. package/dist/core/compaction/fallback.js +117 -0
  48. package/dist/core/compaction/fallback.js.map +1 -0
  49. package/dist/core/compaction/index.d.ts +2 -0
  50. package/dist/core/compaction/index.d.ts.map +1 -1
  51. package/dist/core/compaction/index.js +2 -0
  52. package/dist/core/compaction/index.js.map +1 -1
  53. package/dist/core/context-budget-headroom-candidates.d.ts +16 -1
  54. package/dist/core/context-budget-headroom-candidates.d.ts.map +1 -1
  55. package/dist/core/context-budget-headroom-candidates.js +31 -13
  56. package/dist/core/context-budget-headroom-candidates.js.map +1 -1
  57. package/dist/core/context-budget-headroom-types.d.ts +7 -1
  58. package/dist/core/context-budget-headroom-types.d.ts.map +1 -1
  59. package/dist/core/context-budget-headroom-types.js +0 -1
  60. package/dist/core/context-budget-headroom-types.js.map +1 -1
  61. package/dist/core/context-budget-headroom.d.ts +10 -0
  62. package/dist/core/context-budget-headroom.d.ts.map +1 -1
  63. package/dist/core/context-budget-headroom.js +11 -2
  64. package/dist/core/context-budget-headroom.js.map +1 -1
  65. package/dist/core/context-budget-v2-global-pass.d.ts +21 -0
  66. package/dist/core/context-budget-v2-global-pass.d.ts.map +1 -0
  67. package/dist/core/context-budget-v2-global-pass.js +203 -0
  68. package/dist/core/context-budget-v2-global-pass.js.map +1 -0
  69. package/dist/core/context-budget-v2-planned-items.d.ts +12 -0
  70. package/dist/core/context-budget-v2-planned-items.d.ts.map +1 -1
  71. package/dist/core/context-budget-v2-planned-items.js +21 -2
  72. package/dist/core/context-budget-v2-planned-items.js.map +1 -1
  73. package/dist/core/context-budget-v2-planner.d.ts.map +1 -1
  74. package/dist/core/context-budget-v2-planner.js +18 -19
  75. package/dist/core/context-budget-v2-planner.js.map +1 -1
  76. package/dist/core/context-budget-v2-scoring.d.ts +7 -1
  77. package/dist/core/context-budget-v2-scoring.d.ts.map +1 -1
  78. package/dist/core/context-budget-v2-scoring.js.map +1 -1
  79. package/dist/core/context-budget-v2-selection.d.ts +3 -0
  80. package/dist/core/context-budget-v2-selection.d.ts.map +1 -1
  81. package/dist/core/context-budget-v2-selection.js +6 -2
  82. package/dist/core/context-budget-v2-selection.js.map +1 -1
  83. package/dist/core/context-budget-v2-types.d.ts +6 -1
  84. package/dist/core/context-budget-v2-types.d.ts.map +1 -1
  85. package/dist/core/context-budget-v2-types.js +6 -1
  86. package/dist/core/context-budget-v2-types.js.map +1 -1
  87. package/dist/core/provider-default-models.d.ts +1 -0
  88. package/dist/core/provider-default-models.d.ts.map +1 -1
  89. package/dist/core/provider-default-models.js +1 -0
  90. package/dist/core/provider-default-models.js.map +1 -1
  91. package/dist/core/provider-display-names.d.ts.map +1 -1
  92. package/dist/core/provider-display-names.js +1 -0
  93. package/dist/core/provider-display-names.js.map +1 -1
  94. package/dist/core/reasoning-router-resolver.d.ts +7 -0
  95. package/dist/core/reasoning-router-resolver.d.ts.map +1 -1
  96. package/dist/core/reasoning-router-resolver.js +21 -3
  97. package/dist/core/reasoning-router-resolver.js.map +1 -1
  98. package/dist/core/reasoning-router-v4.d.ts +8 -5
  99. package/dist/core/reasoning-router-v4.d.ts.map +1 -1
  100. package/dist/core/reasoning-router-v4.js +8 -5
  101. package/dist/core/reasoning-router-v4.js.map +1 -1
  102. package/dist/core/session-compaction-service.d.ts +15 -0
  103. package/dist/core/session-compaction-service.d.ts.map +1 -1
  104. package/dist/core/session-compaction-service.js +17 -4
  105. package/dist/core/session-compaction-service.js.map +1 -1
  106. package/dist/core/todo-runtime-state.d.ts +12 -0
  107. package/dist/core/todo-runtime-state.d.ts.map +1 -1
  108. package/dist/core/todo-runtime-state.js +23 -0
  109. package/dist/core/todo-runtime-state.js.map +1 -1
  110. package/dist/index.d.ts +2 -0
  111. package/dist/index.d.ts.map +1 -1
  112. package/dist/index.js +2 -0
  113. package/dist/index.js.map +1 -1
  114. package/dist/metacognition/calibration-selective.d.ts +78 -0
  115. package/dist/metacognition/calibration-selective.d.ts.map +1 -0
  116. package/dist/metacognition/calibration-selective.js +161 -0
  117. package/dist/metacognition/calibration-selective.js.map +1 -0
  118. package/dist/metacognition/calibration.d.ts +62 -0
  119. package/dist/metacognition/calibration.d.ts.map +1 -0
  120. package/dist/metacognition/calibration.js +122 -0
  121. package/dist/metacognition/calibration.js.map +1 -0
  122. package/dist/metacognition/checkpoint.d.ts +60 -0
  123. package/dist/metacognition/checkpoint.d.ts.map +1 -0
  124. package/dist/metacognition/checkpoint.js +57 -0
  125. package/dist/metacognition/checkpoint.js.map +1 -0
  126. package/dist/metacognition/context7.d.ts +48 -0
  127. package/dist/metacognition/context7.d.ts.map +1 -0
  128. package/dist/metacognition/context7.js +154 -0
  129. package/dist/metacognition/context7.js.map +1 -0
  130. package/dist/metacognition/decision.d.ts +44 -0
  131. package/dist/metacognition/decision.d.ts.map +1 -0
  132. package/dist/metacognition/decision.js +111 -0
  133. package/dist/metacognition/decision.js.map +1 -0
  134. package/dist/metacognition/evaluation.d.ts +73 -0
  135. package/dist/metacognition/evaluation.d.ts.map +1 -0
  136. package/dist/metacognition/evaluation.js +97 -0
  137. package/dist/metacognition/evaluation.js.map +1 -0
  138. package/dist/metacognition/index.d.ts +31 -0
  139. package/dist/metacognition/index.d.ts.map +1 -0
  140. package/dist/metacognition/index.js +31 -0
  141. package/dist/metacognition/index.js.map +1 -0
  142. package/dist/metacognition/knowledge-action.d.ts +43 -0
  143. package/dist/metacognition/knowledge-action.d.ts.map +1 -0
  144. package/dist/metacognition/knowledge-action.js +60 -0
  145. package/dist/metacognition/knowledge-action.js.map +1 -0
  146. package/dist/metacognition/knowledge.d.ts +84 -0
  147. package/dist/metacognition/knowledge.d.ts.map +1 -0
  148. package/dist/metacognition/knowledge.js +165 -0
  149. package/dist/metacognition/knowledge.js.map +1 -0
  150. package/dist/metacognition/obligations.d.ts +82 -0
  151. package/dist/metacognition/obligations.d.ts.map +1 -0
  152. package/dist/metacognition/obligations.js +139 -0
  153. package/dist/metacognition/obligations.js.map +1 -0
  154. package/dist/metacognition/observation-validity.d.ts +83 -0
  155. package/dist/metacognition/observation-validity.d.ts.map +1 -0
  156. package/dist/metacognition/observation-validity.js +118 -0
  157. package/dist/metacognition/observation-validity.js.map +1 -0
  158. package/dist/metacognition/observe.d.ts +45 -0
  159. package/dist/metacognition/observe.d.ts.map +1 -0
  160. package/dist/metacognition/observe.js +59 -0
  161. package/dist/metacognition/observe.js.map +1 -0
  162. package/dist/metacognition/policy.d.ts +53 -0
  163. package/dist/metacognition/policy.d.ts.map +1 -0
  164. package/dist/metacognition/policy.js +155 -0
  165. package/dist/metacognition/policy.js.map +1 -0
  166. package/dist/metacognition/predictions.d.ts +78 -0
  167. package/dist/metacognition/predictions.d.ts.map +1 -0
  168. package/dist/metacognition/predictions.js +181 -0
  169. package/dist/metacognition/predictions.js.map +1 -0
  170. package/dist/metacognition/retrieval.d.ts +53 -0
  171. package/dist/metacognition/retrieval.d.ts.map +1 -0
  172. package/dist/metacognition/retrieval.js +170 -0
  173. package/dist/metacognition/retrieval.js.map +1 -0
  174. package/dist/metacognition/risk.d.ts +48 -0
  175. package/dist/metacognition/risk.d.ts.map +1 -0
  176. package/dist/metacognition/risk.js +165 -0
  177. package/dist/metacognition/risk.js.map +1 -0
  178. package/dist/metacognition/route-economics.d.ts +95 -0
  179. package/dist/metacognition/route-economics.d.ts.map +1 -0
  180. package/dist/metacognition/route-economics.js +110 -0
  181. package/dist/metacognition/route-economics.js.map +1 -0
  182. package/dist/metacognition/runtime-bridge.d.ts +54 -0
  183. package/dist/metacognition/runtime-bridge.d.ts.map +1 -0
  184. package/dist/metacognition/runtime-bridge.js +140 -0
  185. package/dist/metacognition/runtime-bridge.js.map +1 -0
  186. package/dist/metacognition/skills.d.ts +42 -0
  187. package/dist/metacognition/skills.d.ts.map +1 -0
  188. package/dist/metacognition/skills.js +220 -0
  189. package/dist/metacognition/skills.js.map +1 -0
  190. package/dist/metacognition/state.d.ts +110 -0
  191. package/dist/metacognition/state.d.ts.map +1 -0
  192. package/dist/metacognition/state.js +52 -0
  193. package/dist/metacognition/state.js.map +1 -0
  194. package/dist/metacognition/validation.d.ts +19 -0
  195. package/dist/metacognition/validation.d.ts.map +1 -0
  196. package/dist/metacognition/validation.js +58 -0
  197. package/dist/metacognition/validation.js.map +1 -0
  198. package/dist/metacognition/verification.d.ts +93 -0
  199. package/dist/metacognition/verification.d.ts.map +1 -0
  200. package/dist/metacognition/verification.js +132 -0
  201. package/dist/metacognition/verification.js.map +1 -0
  202. package/dist/metacognition/verifier.d.ts +49 -0
  203. package/dist/metacognition/verifier.d.ts.map +1 -0
  204. package/dist/metacognition/verifier.js +88 -0
  205. package/dist/metacognition/verifier.js.map +1 -0
  206. package/dist/observation/identity.d.ts +14 -0
  207. package/dist/observation/identity.d.ts.map +1 -0
  208. package/dist/observation/identity.js +29 -0
  209. package/dist/observation/identity.js.map +1 -0
  210. package/dist/observation/index.d.ts +13 -0
  211. package/dist/observation/index.d.ts.map +1 -0
  212. package/dist/observation/index.js +13 -0
  213. package/dist/observation/index.js.map +1 -0
  214. package/dist/observation/observe-mode.d.ts +46 -0
  215. package/dist/observation/observe-mode.d.ts.map +1 -0
  216. package/dist/observation/observe-mode.js +83 -0
  217. package/dist/observation/observe-mode.js.map +1 -0
  218. package/dist/observation/store.d.ts +53 -0
  219. package/dist/observation/store.d.ts.map +1 -0
  220. package/dist/observation/store.js +126 -0
  221. package/dist/observation/store.js.map +1 -0
  222. package/dist/observation/types.d.ts +72 -0
  223. package/dist/observation/types.d.ts.map +1 -0
  224. package/dist/observation/types.js +9 -0
  225. package/dist/observation/types.js.map +1 -0
  226. package/dist/observation/view.d.ts +39 -0
  227. package/dist/observation/view.d.ts.map +1 -0
  228. package/dist/observation/view.js +173 -0
  229. package/dist/observation/view.js.map +1 -0
  230. package/docs/metacognition.md +51 -0
  231. package/docs/model-catalog-refresh.md +59 -0
  232. package/docs/providers.md +53 -0
  233. package/docs/runtime-algorithms.md +107 -3
  234. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  235. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  236. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  237. package/examples/extensions/gondolin/package-lock.json +2 -2
  238. package/examples/extensions/gondolin/package.json +1 -1
  239. package/examples/extensions/sandbox/package-lock.json +2 -2
  240. package/examples/extensions/sandbox/package.json +1 -1
  241. package/examples/extensions/with-deps/package-lock.json +2 -2
  242. package/examples/extensions/with-deps/package.json +1 -1
  243. package/npm-shrinkwrap.json +18 -18
  244. package/package.json +6 -6
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Algorithm B — pre-execution prediction ledger and mismatch scoring.
3
+ * Spec: OMK_metacognitive_control_algorithms_2026-09-19.md §5.
4
+ *
5
+ * Predictions are registered by the host BEFORE outcomes exist and bound to
6
+ * candidate/environment/check/policy fingerprints. Later edits append a new
7
+ * prediction — existing records are never overwritten. Unobserved, cancelled,
8
+ * or environment-failed outcomes stay unobserved; they are never silently
9
+ * scored as code success or failure.
10
+ */
11
+ import { brier, surprise } from "./decision.js";
12
+ import { canonical, ensure, integer, lexical, member, text, unique } from "./validation.js";
13
+ const LEDGER_BOUND = 4096;
14
+ function validatePrediction(p) {
15
+ text(p.predictionId, "predictionId", 128);
16
+ text(p.operationId, "operationId", 128);
17
+ text(p.taskId, "taskId", 128);
18
+ text(p.stageId, "stageId", 128);
19
+ member(p.kind, ["check-outcome", "scope", "side-effect", "resource", "hypothesis-outcome"], "kind");
20
+ text(p.candidateHash, "candidateHash", 128);
21
+ text(p.environmentHash, "environmentHash", 128);
22
+ text(p.checkDefinitionHash, "checkDefinitionHash", 128);
23
+ text(p.policyVersion, "policyVersion", 64);
24
+ text(p.modelRevision, "modelRevision", 128);
25
+ unique(p.skillHashes, "skillHashes", 64);
26
+ text(p.expectedOutcome, "expectedOutcome", 2048);
27
+ text(p.counterevidenceCondition, "counterevidenceCondition", 2048);
28
+ member(p.estimator, ["uncalibrated-model-estimate", "validated-estimator", "none"], "estimator");
29
+ integer(p.hostSequence, "hostSequence");
30
+ integer(p.registeredAtMs, "registeredAtMs");
31
+ if (p.probability !== undefined) {
32
+ ensure(p.probability >= 0 && p.probability <= 1, "probability out of range");
33
+ ensure(p.estimator !== "none", "probability requires a declared estimator");
34
+ }
35
+ if (p.expectedBinary !== undefined)
36
+ ensure(typeof p.expectedBinary === "boolean", "expectedBinary");
37
+ }
38
+ /**
39
+ * Register a prediction. The host calls this BEFORE the operation runs.
40
+ * hostSequence is a host-owned monotone counter; predictions are immutable
41
+ * once registered — corrections append a new record.
42
+ */
43
+ export function registerPrediction(ledger, prediction) {
44
+ ensure(ledger.length < LEDGER_BOUND, "prediction ledger bound exceeded");
45
+ validatePrediction({ ...prediction, status: "registered" });
46
+ ensure(!ledger.some((p) => p.predictionId === prediction.predictionId), "duplicate predictionId");
47
+ return [...ledger, { ...prediction, status: "registered" }];
48
+ }
49
+ /**
50
+ * Resolve a registered prediction against a host observation bound to the
51
+ * same candidate/check/environment fingerprints. Mismatched bindings reject —
52
+ * a prediction for one candidate cannot be scored against another.
53
+ */
54
+ export function resolvePrediction(ledger, predictionId, observation) {
55
+ text(predictionId, "predictionId", 128);
56
+ const index = ledger.findIndex((p) => p.predictionId === predictionId);
57
+ ensure(index >= 0, "unknown predictionId");
58
+ const p = ledger[index];
59
+ ensure(p.status === "registered", `prediction ${predictionId} is ${p.status}`);
60
+ ensure(observation.candidateHash === p.candidateHash, "candidate binding mismatch");
61
+ ensure(observation.environmentHash === p.environmentHash, "environment binding mismatch");
62
+ ensure(observation.checkDefinitionHash === p.checkDefinitionHash, "check binding mismatch");
63
+ integer(observation.observedAtMs, "observedAtMs");
64
+ ensure(observation.observedAtMs >= p.registeredAtMs, "observation predates registration");
65
+ let mismatch = "none";
66
+ let score;
67
+ let surpriseScore;
68
+ if (observation.binaryOutcome !== undefined && p.expectedBinary !== undefined) {
69
+ const outcome = observation.binaryOutcome ? 1 : 0;
70
+ const expected = p.expectedBinary ? 1 : 0;
71
+ if (outcome !== expected)
72
+ mismatch = p.kind === "check-outcome" ? "unexpected-fail" : "unexpected-side-effect";
73
+ if (p.probability !== undefined) {
74
+ score = brier(p.probability, outcome);
75
+ surpriseScore = surprise(p.probability, outcome);
76
+ }
77
+ }
78
+ else if (observation.outcome !== p.expectedOutcome) {
79
+ mismatch = p.kind === "scope" ? "scope-mismatch" : "unexpected-fail";
80
+ }
81
+ if (observation.sideEffect === true && p.kind !== "side-effect" && mismatch === "none") {
82
+ mismatch = "unexpected-side-effect";
83
+ }
84
+ const resolved = {
85
+ ...p,
86
+ status: "resolved",
87
+ resolvedAtMs: observation.observedAtMs,
88
+ observedOutcome: observation.outcome,
89
+ observedBinary: observation.binaryOutcome,
90
+ mismatch,
91
+ };
92
+ const next = ledger.slice();
93
+ next[index] = resolved;
94
+ return { ledger: next, scored: { predictionId, mismatch, brier: score, surprise: surpriseScore } };
95
+ }
96
+ /**
97
+ * Mark a prediction superseded (policy/budget/target changed before outcome)
98
+ * or unobserved (cancelled, environment failure, late result). Neither is
99
+ * ever scored as a code result.
100
+ */
101
+ export function retirePrediction(ledger, predictionId, outcome) {
102
+ const index = ledger.findIndex((p) => p.predictionId === predictionId);
103
+ ensure(index >= 0, "unknown predictionId");
104
+ const p = ledger[index];
105
+ ensure(p.status === "registered", `prediction ${predictionId} is ${p.status}`);
106
+ const next = ledger.slice();
107
+ next[index] = { ...p, status: outcome };
108
+ return next;
109
+ }
110
+ /** Categorical mismatch summary plus scored aggregates. */
111
+ export function mismatchSummary(ledger) {
112
+ const byKind = {
113
+ "unexpected-pass": 0,
114
+ "unexpected-fail": 0,
115
+ "unexpected-side-effect": 0,
116
+ "missing-observation": 0,
117
+ "scope-mismatch": 0,
118
+ none: 0,
119
+ };
120
+ const surprising = [];
121
+ let brierSum = 0, surpriseSum = 0, scored = 0;
122
+ for (const p of ledger) {
123
+ if (p.status !== "resolved")
124
+ continue;
125
+ const kind = p.mismatch ?? "none";
126
+ byKind[kind] += 1;
127
+ if (p.observedBinary !== undefined && p.probability !== undefined) {
128
+ const outcome = p.observedBinary ? 1 : 0;
129
+ brierSum += brier(p.probability, outcome);
130
+ surpriseSum += surprise(p.probability, outcome);
131
+ scored += 1;
132
+ if (kind !== "none") {
133
+ surprising.push({
134
+ predictionId: p.predictionId,
135
+ mismatch: kind,
136
+ brier: brier(p.probability, outcome),
137
+ surprise: surprise(p.probability, outcome),
138
+ });
139
+ }
140
+ }
141
+ }
142
+ surprising.sort((a, b) => (b.surprise ?? 0) - (a.surprise ?? 0) || lexical(a.predictionId, b.predictionId));
143
+ return {
144
+ total: ledger.length,
145
+ resolved: scored,
146
+ byKind,
147
+ meanBrier: scored > 0 ? brierSum / scored : undefined,
148
+ meanSurprise: scored > 0 ? surpriseSum / scored : undefined,
149
+ surprising,
150
+ };
151
+ }
152
+ /** §5.5: map a mismatch kind to the short control branch it triggers. */
153
+ export function mismatchBranch(kind) {
154
+ switch (kind) {
155
+ case "scope-mismatch":
156
+ return "impact-analysis-and-obligation-refresh";
157
+ case "unexpected-fail":
158
+ return "cause-hypothesis-discrimination";
159
+ case "unexpected-pass":
160
+ return "verifier-evaluation";
161
+ case "unexpected-side-effect":
162
+ return "boundary-and-permission-review";
163
+ case "missing-observation":
164
+ return "coverage-gap-registration";
165
+ case "none":
166
+ return "continue";
167
+ }
168
+ }
169
+ export function predictionFingerprint(p) {
170
+ return canonical([
171
+ p.operationId,
172
+ p.taskId,
173
+ p.stageId,
174
+ p.candidateHash,
175
+ p.environmentHash,
176
+ p.checkDefinitionHash,
177
+ p.policyVersion,
178
+ p.modelRevision,
179
+ ]);
180
+ }
181
+ //# sourceMappingURL=predictions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"predictions.js","sourceRoot":"","sources":["../../src/metacognition/predictions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Algorithm B — pre-execution prediction ledger and mismatch scoring.\n * Spec: OMK_metacognitive_control_algorithms_2026-09-19.md §5.\n *\n * Predictions are registered by the host BEFORE outcomes exist and bound to\n * candidate/environment/check/policy fingerprints. Later edits append a new\n * prediction — existing records are never overwritten. Unobserved, cancelled,\n * or environment-failed outcomes stay unobserved; they are never silently\n * scored as code success or failure.\n */\nimport { brier, surprise } from \"./decision.ts\";\nimport { canonical, ensure, integer, lexical, member, text, unique } from \"./validation.ts\";\n\nexport type PredictionKind = \"check-outcome\" | \"scope\" | \"side-effect\" | \"resource\" | \"hypothesis-outcome\";\nexport type EstimatorKind = \"uncalibrated-model-estimate\" | \"validated-estimator\" | \"none\";\nexport type PredictionStatus = \"registered\" | \"resolved\" | \"superseded\" | \"unobserved\";\nexport type MismatchKind =\n\t| \"unexpected-pass\"\n\t| \"unexpected-fail\"\n\t| \"unexpected-side-effect\"\n\t| \"missing-observation\"\n\t| \"scope-mismatch\"\n\t| \"none\";\n\nexport interface Prediction {\n\treadonly predictionId: string;\n\treadonly operationId: string;\n\treadonly taskId: string;\n\treadonly stageId: string;\n\treadonly kind: PredictionKind;\n\treadonly candidateHash: string;\n\treadonly environmentHash: string;\n\treadonly checkDefinitionHash: string;\n\treadonly policyVersion: string;\n\treadonly modelRevision: string;\n\treadonly skillHashes: readonly string[];\n\treadonly expectedOutcome: string;\n\t/** Binary expectation for scored predictions; undefined for categorical. */\n\treadonly expectedBinary?: boolean;\n\treadonly probability?: number;\n\treadonly estimator: EstimatorKind;\n\treadonly counterevidenceCondition: string;\n\treadonly hostSequence: number;\n\treadonly registeredAtMs: number;\n\tstatus: PredictionStatus;\n\treadonly resolvedAtMs?: number;\n\treadonly observedOutcome?: string;\n\treadonly observedBinary?: boolean;\n\treadonly mismatch?: MismatchKind;\n}\nexport interface ScoredPrediction {\n\treadonly predictionId: string;\n\treadonly mismatch: MismatchKind;\n\treadonly brier?: number;\n\treadonly surprise?: number;\n}\n\nconst LEDGER_BOUND = 4096;\n\nfunction validatePrediction(p: Prediction): void {\n\ttext(p.predictionId, \"predictionId\", 128);\n\ttext(p.operationId, \"operationId\", 128);\n\ttext(p.taskId, \"taskId\", 128);\n\ttext(p.stageId, \"stageId\", 128);\n\tmember(p.kind, [\"check-outcome\", \"scope\", \"side-effect\", \"resource\", \"hypothesis-outcome\"], \"kind\");\n\ttext(p.candidateHash, \"candidateHash\", 128);\n\ttext(p.environmentHash, \"environmentHash\", 128);\n\ttext(p.checkDefinitionHash, \"checkDefinitionHash\", 128);\n\ttext(p.policyVersion, \"policyVersion\", 64);\n\ttext(p.modelRevision, \"modelRevision\", 128);\n\tunique(p.skillHashes, \"skillHashes\", 64);\n\ttext(p.expectedOutcome, \"expectedOutcome\", 2048);\n\ttext(p.counterevidenceCondition, \"counterevidenceCondition\", 2048);\n\tmember(p.estimator, [\"uncalibrated-model-estimate\", \"validated-estimator\", \"none\"], \"estimator\");\n\tinteger(p.hostSequence, \"hostSequence\");\n\tinteger(p.registeredAtMs, \"registeredAtMs\");\n\tif (p.probability !== undefined) {\n\t\tensure(p.probability >= 0 && p.probability <= 1, \"probability out of range\");\n\t\tensure(p.estimator !== \"none\", \"probability requires a declared estimator\");\n\t}\n\tif (p.expectedBinary !== undefined) ensure(typeof p.expectedBinary === \"boolean\", \"expectedBinary\");\n}\n\n/**\n * Register a prediction. The host calls this BEFORE the operation runs.\n * hostSequence is a host-owned monotone counter; predictions are immutable\n * once registered — corrections append a new record.\n */\nexport function registerPrediction(\n\tledger: readonly Prediction[],\n\tprediction: Omit<Prediction, \"status\">,\n): readonly Prediction[] {\n\tensure(ledger.length < LEDGER_BOUND, \"prediction ledger bound exceeded\");\n\tvalidatePrediction({ ...prediction, status: \"registered\" });\n\tensure(!ledger.some((p) => p.predictionId === prediction.predictionId), \"duplicate predictionId\");\n\treturn [...ledger, { ...prediction, status: \"registered\" }];\n}\n\n/**\n * Resolve a registered prediction against a host observation bound to the\n * same candidate/check/environment fingerprints. Mismatched bindings reject —\n * a prediction for one candidate cannot be scored against another.\n */\nexport function resolvePrediction(\n\tledger: readonly Prediction[],\n\tpredictionId: string,\n\tobservation: {\n\t\treadonly candidateHash: string;\n\t\treadonly environmentHash: string;\n\t\treadonly checkDefinitionHash: string;\n\t\treadonly outcome: string;\n\t\treadonly binaryOutcome?: boolean;\n\t\treadonly sideEffect?: boolean;\n\t\treadonly observedAtMs: number;\n\t},\n): { ledger: readonly Prediction[]; scored: ScoredPrediction } {\n\ttext(predictionId, \"predictionId\", 128);\n\tconst index = ledger.findIndex((p) => p.predictionId === predictionId);\n\tensure(index >= 0, \"unknown predictionId\");\n\tconst p = ledger[index]!;\n\tensure(p.status === \"registered\", `prediction ${predictionId} is ${p.status}`);\n\tensure(observation.candidateHash === p.candidateHash, \"candidate binding mismatch\");\n\tensure(observation.environmentHash === p.environmentHash, \"environment binding mismatch\");\n\tensure(observation.checkDefinitionHash === p.checkDefinitionHash, \"check binding mismatch\");\n\tinteger(observation.observedAtMs, \"observedAtMs\");\n\tensure(observation.observedAtMs >= p.registeredAtMs, \"observation predates registration\");\n\tlet mismatch: MismatchKind = \"none\";\n\tlet score: number | undefined;\n\tlet surpriseScore: number | undefined;\n\tif (observation.binaryOutcome !== undefined && p.expectedBinary !== undefined) {\n\t\tconst outcome: 0 | 1 = observation.binaryOutcome ? 1 : 0;\n\t\tconst expected: 0 | 1 = p.expectedBinary ? 1 : 0;\n\t\tif (outcome !== expected) mismatch = p.kind === \"check-outcome\" ? \"unexpected-fail\" : \"unexpected-side-effect\";\n\t\tif (p.probability !== undefined) {\n\t\t\tscore = brier(p.probability, outcome);\n\t\t\tsurpriseScore = surprise(p.probability, outcome);\n\t\t}\n\t} else if (observation.outcome !== p.expectedOutcome) {\n\t\tmismatch = p.kind === \"scope\" ? \"scope-mismatch\" : \"unexpected-fail\";\n\t}\n\tif (observation.sideEffect === true && p.kind !== \"side-effect\" && mismatch === \"none\") {\n\t\tmismatch = \"unexpected-side-effect\";\n\t}\n\tconst resolved: Prediction = {\n\t\t...p,\n\t\tstatus: \"resolved\",\n\t\tresolvedAtMs: observation.observedAtMs,\n\t\tobservedOutcome: observation.outcome,\n\t\tobservedBinary: observation.binaryOutcome,\n\t\tmismatch,\n\t};\n\tconst next = ledger.slice();\n\tnext[index] = resolved;\n\treturn { ledger: next, scored: { predictionId, mismatch, brier: score, surprise: surpriseScore } };\n}\n\n/**\n * Mark a prediction superseded (policy/budget/target changed before outcome)\n * or unobserved (cancelled, environment failure, late result). Neither is\n * ever scored as a code result.\n */\nexport function retirePrediction(\n\tledger: readonly Prediction[],\n\tpredictionId: string,\n\toutcome: \"superseded\" | \"unobserved\",\n): readonly Prediction[] {\n\tconst index = ledger.findIndex((p) => p.predictionId === predictionId);\n\tensure(index >= 0, \"unknown predictionId\");\n\tconst p = ledger[index]!;\n\tensure(p.status === \"registered\", `prediction ${predictionId} is ${p.status}`);\n\tconst next = ledger.slice();\n\tnext[index] = { ...p, status: outcome };\n\treturn next;\n}\n\n/** Categorical mismatch summary plus scored aggregates. */\nexport function mismatchSummary(ledger: readonly Prediction[]): {\n\treadonly total: number;\n\treadonly resolved: number;\n\treadonly byKind: Readonly<Record<MismatchKind, number>>;\n\treadonly meanBrier?: number;\n\treadonly meanSurprise?: number;\n\treadonly surprising: readonly ScoredPrediction[];\n} {\n\tconst byKind: Record<MismatchKind, number> = {\n\t\t\"unexpected-pass\": 0,\n\t\t\"unexpected-fail\": 0,\n\t\t\"unexpected-side-effect\": 0,\n\t\t\"missing-observation\": 0,\n\t\t\"scope-mismatch\": 0,\n\t\tnone: 0,\n\t};\n\tconst surprising: ScoredPrediction[] = [];\n\tlet brierSum = 0,\n\t\tsurpriseSum = 0,\n\t\tscored = 0;\n\tfor (const p of ledger) {\n\t\tif (p.status !== \"resolved\") continue;\n\t\tconst kind = p.mismatch ?? \"none\";\n\t\tbyKind[kind] += 1;\n\t\tif (p.observedBinary !== undefined && p.probability !== undefined) {\n\t\t\tconst outcome: 0 | 1 = p.observedBinary ? 1 : 0;\n\t\t\tbrierSum += brier(p.probability, outcome);\n\t\t\tsurpriseSum += surprise(p.probability, outcome);\n\t\t\tscored += 1;\n\t\t\tif (kind !== \"none\") {\n\t\t\t\tsurprising.push({\n\t\t\t\t\tpredictionId: p.predictionId,\n\t\t\t\t\tmismatch: kind,\n\t\t\t\t\tbrier: brier(p.probability, outcome),\n\t\t\t\t\tsurprise: surprise(p.probability, outcome),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\tsurprising.sort((a, b) => (b.surprise ?? 0) - (a.surprise ?? 0) || lexical(a.predictionId, b.predictionId));\n\treturn {\n\t\ttotal: ledger.length,\n\t\tresolved: scored,\n\t\tbyKind,\n\t\tmeanBrier: scored > 0 ? brierSum / scored : undefined,\n\t\tmeanSurprise: scored > 0 ? surpriseSum / scored : undefined,\n\t\tsurprising,\n\t};\n}\n\n/** §5.5: map a mismatch kind to the short control branch it triggers. */\nexport function mismatchBranch(kind: MismatchKind): string {\n\tswitch (kind) {\n\t\tcase \"scope-mismatch\":\n\t\t\treturn \"impact-analysis-and-obligation-refresh\";\n\t\tcase \"unexpected-fail\":\n\t\t\treturn \"cause-hypothesis-discrimination\";\n\t\tcase \"unexpected-pass\":\n\t\t\treturn \"verifier-evaluation\";\n\t\tcase \"unexpected-side-effect\":\n\t\t\treturn \"boundary-and-permission-review\";\n\t\tcase \"missing-observation\":\n\t\t\treturn \"coverage-gap-registration\";\n\t\tcase \"none\":\n\t\t\treturn \"continue\";\n\t}\n}\n\nexport function predictionFingerprint(p: Prediction): string {\n\treturn canonical([\n\t\tp.operationId,\n\t\tp.taskId,\n\t\tp.stageId,\n\t\tp.candidateHash,\n\t\tp.environmentHash,\n\t\tp.checkDefinitionHash,\n\t\tp.policyVersion,\n\t\tp.modelRevision,\n\t]);\n}\n"]}
@@ -0,0 +1,53 @@
1
+ export interface CorpusDocument {
2
+ readonly id: string;
3
+ readonly uri: string;
4
+ readonly body: string;
5
+ }
6
+ export interface SearchHit extends CorpusDocument {
7
+ readonly score: number;
8
+ readonly trust: "candidate-only";
9
+ }
10
+ /** Retrieval tokens, not a semantic-knowledge detector. Short language names survive. */
11
+ export declare function retrievalTokens(value: string): string[];
12
+ /** BM25 with positive IDF; bounded local corpus, no I/O or model call. */
13
+ export declare function searchCorpus(query: string, corpus: readonly CorpusDocument[], limit?: number): SearchHit[];
14
+ export type AcquisitionResult<T> = {
15
+ readonly status: "ok";
16
+ readonly value: T;
17
+ } | {
18
+ readonly status: "budget-exhausted" | "busy" | "timeout" | "aborted" | "stale" | "error";
19
+ };
20
+ export interface AcquisitionOptions {
21
+ readonly maxRequests: number;
22
+ readonly maxInFlight: number;
23
+ readonly durationMs: number;
24
+ readonly perRequestMs: number;
25
+ readonly monotonicNow?: () => number;
26
+ }
27
+ /**
28
+ * Owns every started promise until actual settlement, including noncooperative requests.
29
+ * The scope is per host-approved run. It is not a billing or process-isolation boundary.
30
+ */
31
+ export declare class AcquisitionPool {
32
+ private readonly now;
33
+ private readonly deadline;
34
+ private readonly maxRequests;
35
+ private readonly maxInFlight;
36
+ private readonly perRequestMs;
37
+ private nextId;
38
+ private epoch;
39
+ private requests;
40
+ private readonly active;
41
+ constructor(options: AcquisitionOptions);
42
+ snapshot(): {
43
+ requests: number;
44
+ active: number;
45
+ quarantined: number;
46
+ remainingMs: number;
47
+ epoch: number;
48
+ };
49
+ /** Invalidation requests cancellation but never resets outstanding ownership or budget. */
50
+ invalidate(): void;
51
+ run<T>(operation: (signal: AbortSignal) => Promise<T>, parent?: AbortSignal): Promise<AcquisitionResult<T>>;
52
+ }
53
+ //# sourceMappingURL=retrieval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retrieval.d.ts","sourceRoot":"","sources":["../../src/metacognition/retrieval.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AACD,MAAM,WAAW,SAAU,SAAQ,cAAc;IAChD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,gBAAgB,CAAC;CACjC;AACD,yFAAyF;AACzF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAQvD;AACD,0EAA0E;AAC1E,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,cAAc,EAAE,EAAE,KAAK,SAAI,GAAG,SAAS,EAAE,CAyCrG;AAED,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAC5B;IAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAC5C;IAAE,QAAQ,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAA;CAAE,CAAC;AAChG,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,MAAM,CAAC;CACrC;AACD;;;GAGG;AACH,qBAAa,eAAe;IAC3B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4E;IACnG,YAAY,OAAO,EAAE,kBAAkB,EAYtC;IACD,QAAQ,IAAI;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAQxG;IACD,2FAA2F;IAC3F,UAAU,IAAI,IAAI,CAMjB;IACK,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAgEhH;CACD","sourcesContent":["/**\n * Bounded local BM25 retrieval and the acquisition pool that owns every\n * started promise until actual settlement.\n *\n * Ported from docs/OMK_skill_knowledge_control_2026-09-19.zip\n * (src/retrieval.ts). Search hits are `candidate-only`, never auto-promoted\n * to observations.\n */\nimport { ensure, integer, lexical, text, unique } from \"./validation.ts\";\n\nexport interface CorpusDocument {\n\treadonly id: string;\n\treadonly uri: string;\n\treadonly body: string;\n}\nexport interface SearchHit extends CorpusDocument {\n\treadonly score: number;\n\treadonly trust: \"candidate-only\";\n}\n/** Retrieval tokens, not a semantic-knowledge detector. Short language names survive. */\nexport function retrievalTokens(value: string): string[] {\n\treturn (\n\t\tvalue\n\t\t\t.normalize(\"NFKC\")\n\t\t\t.replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n\t\t\t.toLowerCase()\n\t\t\t.match(/[\\p{L}\\p{N}]+(?:\\+\\+|#)?/gu) ?? []\n\t);\n}\n/** BM25 with positive IDF; bounded local corpus, no I/O or model call. */\nexport function searchCorpus(query: string, corpus: readonly CorpusDocument[], limit = 8): SearchHit[] {\n\ttext(query, \"query\", 4096);\n\tinteger(limit, \"limit\", 64);\n\tensure(corpus.length <= 5000, \"corpus too large\");\n\tunique(\n\t\tcorpus.map((d) => d.id),\n\t\t\"document ids\",\n\t\t5000,\n\t);\n\tlet totalChars = 0;\n\tconst documents = corpus.map((d) => {\n\t\ttext(d.uri, \"uri\");\n\t\tensure(typeof d.body === \"string\" && d.body.length <= 100_000, \"document too large\");\n\t\ttotalChars += d.body.length;\n\t\tensure(totalChars <= 10_000_000, \"corpus byte/character policy exceeded\");\n\t\tconst tokens = retrievalTokens(d.body);\n\t\tconst frequencies = new Map<string, number>();\n\t\tfor (const token of tokens) frequencies.set(token, (frequencies.get(token) ?? 0) + 1);\n\t\treturn { d, length: tokens.length, frequencies };\n\t});\n\tconst terms = [...new Set(retrievalTokens(query))];\n\tif (!terms.length || !documents.length || limit === 0) return [];\n\tconst avgLength = Math.max(1, documents.reduce((s, d) => s + d.length, 0) / documents.length);\n\tconst idf = new Map(\n\t\tterms.map((term) => {\n\t\t\tconst frequency = documents.filter((d) => d.frequencies.has(term)).length;\n\t\t\treturn [term, Math.log(1 + (documents.length - frequency + 0.5) / (frequency + 0.5))] as const;\n\t\t}),\n\t);\n\treturn documents\n\t\t.map(({ d, length, frequencies }): SearchHit => {\n\t\t\tlet score = 0;\n\t\t\tfor (const term of terms) {\n\t\t\t\tconst tf = frequencies.get(term) ?? 0;\n\t\t\t\tscore += (idf.get(term)! * tf * 2.2) / (tf + 1.2 * (0.25 + (0.75 * length) / avgLength));\n\t\t\t}\n\t\t\treturn { ...d, score, trust: \"candidate-only\" };\n\t\t})\n\t\t.filter((d) => d.score > 0)\n\t\t.sort((a, b) => b.score - a.score || lexical(a.id, b.id))\n\t\t.slice(0, limit);\n}\n\nexport type AcquisitionResult<T> =\n\t| { readonly status: \"ok\"; readonly value: T }\n\t| { readonly status: \"budget-exhausted\" | \"busy\" | \"timeout\" | \"aborted\" | \"stale\" | \"error\" };\nexport interface AcquisitionOptions {\n\treadonly maxRequests: number;\n\treadonly maxInFlight: number;\n\treadonly durationMs: number;\n\treadonly perRequestMs: number;\n\treadonly monotonicNow?: () => number;\n}\n/**\n * Owns every started promise until actual settlement, including noncooperative requests.\n * The scope is per host-approved run. It is not a billing or process-isolation boundary.\n */\nexport class AcquisitionPool {\n\tprivate readonly now: () => number;\n\tprivate readonly deadline: number;\n\tprivate readonly maxRequests: number;\n\tprivate readonly maxInFlight: number;\n\tprivate readonly perRequestMs: number;\n\tprivate nextId = 0;\n\tprivate epoch = 0;\n\tprivate requests = 0;\n\tprivate readonly active = new Map<number, { controller: AbortController; quarantined: boolean }>();\n\tconstructor(options: AcquisitionOptions) {\n\t\tinteger(options.maxRequests, \"maxRequests\", 100_000);\n\t\tinteger(options.maxInFlight, \"maxInFlight\", 64);\n\t\tinteger(options.durationMs, \"durationMs\", 86_400_000);\n\t\tinteger(options.perRequestMs, \"perRequestMs\", 86_400_000);\n\t\tthis.now = options.monotonicNow ?? (() => performance.now());\n\t\tconst start = this.now();\n\t\tensure(Number.isFinite(start) && start >= 0, \"invalid monotonic clock\");\n\t\tthis.deadline = start + options.durationMs;\n\t\tthis.maxRequests = options.maxRequests;\n\t\tthis.maxInFlight = options.maxInFlight;\n\t\tthis.perRequestMs = options.perRequestMs;\n\t}\n\tsnapshot(): { requests: number; active: number; quarantined: number; remainingMs: number; epoch: number } {\n\t\treturn {\n\t\t\trequests: this.requests,\n\t\t\tactive: this.active.size,\n\t\t\tquarantined: [...this.active.values()].filter((e) => e.quarantined).length,\n\t\t\tremainingMs: Math.max(0, Math.floor(this.deadline - this.now())),\n\t\t\tepoch: this.epoch,\n\t\t};\n\t}\n\t/** Invalidation requests cancellation but never resets outstanding ownership or budget. */\n\tinvalidate(): void {\n\t\tthis.epoch++;\n\t\tfor (const entry of this.active.values()) {\n\t\t\tentry.quarantined = true;\n\t\t\tentry.controller.abort();\n\t\t}\n\t}\n\tasync run<T>(operation: (signal: AbortSignal) => Promise<T>, parent?: AbortSignal): Promise<AcquisitionResult<T>> {\n\t\tif (parent?.aborted) return { status: \"aborted\" };\n\t\tconst remaining = this.deadline - this.now();\n\t\tif (this.requests >= this.maxRequests || remaining <= 0 || this.perRequestMs === 0) {\n\t\t\treturn { status: \"budget-exhausted\" };\n\t\t}\n\t\tif (this.active.size >= this.maxInFlight) return { status: \"busy\" };\n\t\tconst id = ++this.nextId,\n\t\t\tepoch = this.epoch,\n\t\t\tcontroller = new AbortController();\n\t\tconst entry = { controller, quarantined: false };\n\t\tthis.active.set(id, entry);\n\t\tthis.requests++;\n\t\tconst expires = this.now() + Math.min(remaining, this.perRequestMs);\n\t\tlet resolveStop!: (result: AcquisitionResult<T>) => void;\n\t\tconst stopped = new Promise<AcquisitionResult<T>>((resolve) => {\n\t\t\tresolveStop = resolve;\n\t\t});\n\t\tconst stop = (status: \"aborted\" | \"timeout\" | \"stale\"): void => {\n\t\t\tif (this.active.has(id)) entry.quarantined = true;\n\t\t\tresolveStop({ status });\n\t\t\tcontroller.abort();\n\t\t};\n\t\tconst onParent = (): void => stop(\"aborted\");\n\t\tconst onInternal = (): void => {\n\t\t\tif (epoch !== this.epoch) resolveStop({ status: \"stale\" });\n\t\t};\n\t\tparent?.addEventListener(\"abort\", onParent, { once: true });\n\t\tcontroller.signal.addEventListener(\"abort\", onInternal, { once: true });\n\t\tconst timer = setTimeout(() => stop(\"timeout\"), Math.max(1, Math.ceil(expires - this.now())));\n\t\t// Both fulfillment and rejection are owned. Late results are never promoted.\n\t\tconst task = Promise.resolve()\n\t\t\t.then(() => {\n\t\t\t\tif (controller.signal.aborted || parent?.aborted) throw new Error(\"cancelled-before-dispatch\");\n\t\t\t\treturn operation(controller.signal);\n\t\t\t})\n\t\t\t.then(\n\t\t\t\t(value): AcquisitionResult<T> => {\n\t\t\t\t\tif (epoch !== this.epoch) return { status: \"stale\" };\n\t\t\t\t\tif (parent?.aborted) return { status: \"aborted\" };\n\t\t\t\t\tif (controller.signal.aborted || this.now() >= expires) return { status: \"timeout\" };\n\t\t\t\t\treturn { status: \"ok\", value };\n\t\t\t\t},\n\t\t\t\t(): AcquisitionResult<T> => ({\n\t\t\t\t\tstatus:\n\t\t\t\t\t\tepoch !== this.epoch\n\t\t\t\t\t\t\t? \"stale\"\n\t\t\t\t\t\t\t: parent?.aborted\n\t\t\t\t\t\t\t\t? \"aborted\"\n\t\t\t\t\t\t\t\t: controller.signal.aborted\n\t\t\t\t\t\t\t\t\t? \"timeout\"\n\t\t\t\t\t\t\t\t\t: \"error\",\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.finally(() => {\n\t\t\t\tthis.active.delete(id);\n\t\t\t});\n\t\ttry {\n\t\t\treturn await Promise.race([task, stopped]);\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\t\t\tparent?.removeEventListener(\"abort\", onParent);\n\t\t\tcontroller.signal.removeEventListener(\"abort\", onInternal);\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Bounded local BM25 retrieval and the acquisition pool that owns every
3
+ * started promise until actual settlement.
4
+ *
5
+ * Ported from docs/OMK_skill_knowledge_control_2026-09-19.zip
6
+ * (src/retrieval.ts). Search hits are `candidate-only`, never auto-promoted
7
+ * to observations.
8
+ */
9
+ import { ensure, integer, lexical, text, unique } from "./validation.js";
10
+ /** Retrieval tokens, not a semantic-knowledge detector. Short language names survive. */
11
+ export function retrievalTokens(value) {
12
+ return (value
13
+ .normalize("NFKC")
14
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
15
+ .toLowerCase()
16
+ .match(/[\p{L}\p{N}]+(?:\+\+|#)?/gu) ?? []);
17
+ }
18
+ /** BM25 with positive IDF; bounded local corpus, no I/O or model call. */
19
+ export function searchCorpus(query, corpus, limit = 8) {
20
+ text(query, "query", 4096);
21
+ integer(limit, "limit", 64);
22
+ ensure(corpus.length <= 5000, "corpus too large");
23
+ unique(corpus.map((d) => d.id), "document ids", 5000);
24
+ let totalChars = 0;
25
+ const documents = corpus.map((d) => {
26
+ text(d.uri, "uri");
27
+ ensure(typeof d.body === "string" && d.body.length <= 100_000, "document too large");
28
+ totalChars += d.body.length;
29
+ ensure(totalChars <= 10_000_000, "corpus byte/character policy exceeded");
30
+ const tokens = retrievalTokens(d.body);
31
+ const frequencies = new Map();
32
+ for (const token of tokens)
33
+ frequencies.set(token, (frequencies.get(token) ?? 0) + 1);
34
+ return { d, length: tokens.length, frequencies };
35
+ });
36
+ const terms = [...new Set(retrievalTokens(query))];
37
+ if (!terms.length || !documents.length || limit === 0)
38
+ return [];
39
+ const avgLength = Math.max(1, documents.reduce((s, d) => s + d.length, 0) / documents.length);
40
+ const idf = new Map(terms.map((term) => {
41
+ const frequency = documents.filter((d) => d.frequencies.has(term)).length;
42
+ return [term, Math.log(1 + (documents.length - frequency + 0.5) / (frequency + 0.5))];
43
+ }));
44
+ return documents
45
+ .map(({ d, length, frequencies }) => {
46
+ let score = 0;
47
+ for (const term of terms) {
48
+ const tf = frequencies.get(term) ?? 0;
49
+ score += (idf.get(term) * tf * 2.2) / (tf + 1.2 * (0.25 + (0.75 * length) / avgLength));
50
+ }
51
+ return { ...d, score, trust: "candidate-only" };
52
+ })
53
+ .filter((d) => d.score > 0)
54
+ .sort((a, b) => b.score - a.score || lexical(a.id, b.id))
55
+ .slice(0, limit);
56
+ }
57
+ /**
58
+ * Owns every started promise until actual settlement, including noncooperative requests.
59
+ * The scope is per host-approved run. It is not a billing or process-isolation boundary.
60
+ */
61
+ export class AcquisitionPool {
62
+ now;
63
+ deadline;
64
+ maxRequests;
65
+ maxInFlight;
66
+ perRequestMs;
67
+ nextId = 0;
68
+ epoch = 0;
69
+ requests = 0;
70
+ active = new Map();
71
+ constructor(options) {
72
+ integer(options.maxRequests, "maxRequests", 100_000);
73
+ integer(options.maxInFlight, "maxInFlight", 64);
74
+ integer(options.durationMs, "durationMs", 86_400_000);
75
+ integer(options.perRequestMs, "perRequestMs", 86_400_000);
76
+ this.now = options.monotonicNow ?? (() => performance.now());
77
+ const start = this.now();
78
+ ensure(Number.isFinite(start) && start >= 0, "invalid monotonic clock");
79
+ this.deadline = start + options.durationMs;
80
+ this.maxRequests = options.maxRequests;
81
+ this.maxInFlight = options.maxInFlight;
82
+ this.perRequestMs = options.perRequestMs;
83
+ }
84
+ snapshot() {
85
+ return {
86
+ requests: this.requests,
87
+ active: this.active.size,
88
+ quarantined: [...this.active.values()].filter((e) => e.quarantined).length,
89
+ remainingMs: Math.max(0, Math.floor(this.deadline - this.now())),
90
+ epoch: this.epoch,
91
+ };
92
+ }
93
+ /** Invalidation requests cancellation but never resets outstanding ownership or budget. */
94
+ invalidate() {
95
+ this.epoch++;
96
+ for (const entry of this.active.values()) {
97
+ entry.quarantined = true;
98
+ entry.controller.abort();
99
+ }
100
+ }
101
+ async run(operation, parent) {
102
+ if (parent?.aborted)
103
+ return { status: "aborted" };
104
+ const remaining = this.deadline - this.now();
105
+ if (this.requests >= this.maxRequests || remaining <= 0 || this.perRequestMs === 0) {
106
+ return { status: "budget-exhausted" };
107
+ }
108
+ if (this.active.size >= this.maxInFlight)
109
+ return { status: "busy" };
110
+ const id = ++this.nextId, epoch = this.epoch, controller = new AbortController();
111
+ const entry = { controller, quarantined: false };
112
+ this.active.set(id, entry);
113
+ this.requests++;
114
+ const expires = this.now() + Math.min(remaining, this.perRequestMs);
115
+ let resolveStop;
116
+ const stopped = new Promise((resolve) => {
117
+ resolveStop = resolve;
118
+ });
119
+ const stop = (status) => {
120
+ if (this.active.has(id))
121
+ entry.quarantined = true;
122
+ resolveStop({ status });
123
+ controller.abort();
124
+ };
125
+ const onParent = () => stop("aborted");
126
+ const onInternal = () => {
127
+ if (epoch !== this.epoch)
128
+ resolveStop({ status: "stale" });
129
+ };
130
+ parent?.addEventListener("abort", onParent, { once: true });
131
+ controller.signal.addEventListener("abort", onInternal, { once: true });
132
+ const timer = setTimeout(() => stop("timeout"), Math.max(1, Math.ceil(expires - this.now())));
133
+ // Both fulfillment and rejection are owned. Late results are never promoted.
134
+ const task = Promise.resolve()
135
+ .then(() => {
136
+ if (controller.signal.aborted || parent?.aborted)
137
+ throw new Error("cancelled-before-dispatch");
138
+ return operation(controller.signal);
139
+ })
140
+ .then((value) => {
141
+ if (epoch !== this.epoch)
142
+ return { status: "stale" };
143
+ if (parent?.aborted)
144
+ return { status: "aborted" };
145
+ if (controller.signal.aborted || this.now() >= expires)
146
+ return { status: "timeout" };
147
+ return { status: "ok", value };
148
+ }, () => ({
149
+ status: epoch !== this.epoch
150
+ ? "stale"
151
+ : parent?.aborted
152
+ ? "aborted"
153
+ : controller.signal.aborted
154
+ ? "timeout"
155
+ : "error",
156
+ }))
157
+ .finally(() => {
158
+ this.active.delete(id);
159
+ });
160
+ try {
161
+ return await Promise.race([task, stopped]);
162
+ }
163
+ finally {
164
+ clearTimeout(timer);
165
+ parent?.removeEventListener("abort", onParent);
166
+ controller.signal.removeEventListener("abort", onInternal);
167
+ }
168
+ }
169
+ }
170
+ //# sourceMappingURL=retrieval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retrieval.js","sourceRoot":"","sources":["../../src/metacognition/retrieval.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bounded local BM25 retrieval and the acquisition pool that owns every\n * started promise until actual settlement.\n *\n * Ported from docs/OMK_skill_knowledge_control_2026-09-19.zip\n * (src/retrieval.ts). Search hits are `candidate-only`, never auto-promoted\n * to observations.\n */\nimport { ensure, integer, lexical, text, unique } from \"./validation.ts\";\n\nexport interface CorpusDocument {\n\treadonly id: string;\n\treadonly uri: string;\n\treadonly body: string;\n}\nexport interface SearchHit extends CorpusDocument {\n\treadonly score: number;\n\treadonly trust: \"candidate-only\";\n}\n/** Retrieval tokens, not a semantic-knowledge detector. Short language names survive. */\nexport function retrievalTokens(value: string): string[] {\n\treturn (\n\t\tvalue\n\t\t\t.normalize(\"NFKC\")\n\t\t\t.replace(/([a-z0-9])([A-Z])/g, \"$1 $2\")\n\t\t\t.toLowerCase()\n\t\t\t.match(/[\\p{L}\\p{N}]+(?:\\+\\+|#)?/gu) ?? []\n\t);\n}\n/** BM25 with positive IDF; bounded local corpus, no I/O or model call. */\nexport function searchCorpus(query: string, corpus: readonly CorpusDocument[], limit = 8): SearchHit[] {\n\ttext(query, \"query\", 4096);\n\tinteger(limit, \"limit\", 64);\n\tensure(corpus.length <= 5000, \"corpus too large\");\n\tunique(\n\t\tcorpus.map((d) => d.id),\n\t\t\"document ids\",\n\t\t5000,\n\t);\n\tlet totalChars = 0;\n\tconst documents = corpus.map((d) => {\n\t\ttext(d.uri, \"uri\");\n\t\tensure(typeof d.body === \"string\" && d.body.length <= 100_000, \"document too large\");\n\t\ttotalChars += d.body.length;\n\t\tensure(totalChars <= 10_000_000, \"corpus byte/character policy exceeded\");\n\t\tconst tokens = retrievalTokens(d.body);\n\t\tconst frequencies = new Map<string, number>();\n\t\tfor (const token of tokens) frequencies.set(token, (frequencies.get(token) ?? 0) + 1);\n\t\treturn { d, length: tokens.length, frequencies };\n\t});\n\tconst terms = [...new Set(retrievalTokens(query))];\n\tif (!terms.length || !documents.length || limit === 0) return [];\n\tconst avgLength = Math.max(1, documents.reduce((s, d) => s + d.length, 0) / documents.length);\n\tconst idf = new Map(\n\t\tterms.map((term) => {\n\t\t\tconst frequency = documents.filter((d) => d.frequencies.has(term)).length;\n\t\t\treturn [term, Math.log(1 + (documents.length - frequency + 0.5) / (frequency + 0.5))] as const;\n\t\t}),\n\t);\n\treturn documents\n\t\t.map(({ d, length, frequencies }): SearchHit => {\n\t\t\tlet score = 0;\n\t\t\tfor (const term of terms) {\n\t\t\t\tconst tf = frequencies.get(term) ?? 0;\n\t\t\t\tscore += (idf.get(term)! * tf * 2.2) / (tf + 1.2 * (0.25 + (0.75 * length) / avgLength));\n\t\t\t}\n\t\t\treturn { ...d, score, trust: \"candidate-only\" };\n\t\t})\n\t\t.filter((d) => d.score > 0)\n\t\t.sort((a, b) => b.score - a.score || lexical(a.id, b.id))\n\t\t.slice(0, limit);\n}\n\nexport type AcquisitionResult<T> =\n\t| { readonly status: \"ok\"; readonly value: T }\n\t| { readonly status: \"budget-exhausted\" | \"busy\" | \"timeout\" | \"aborted\" | \"stale\" | \"error\" };\nexport interface AcquisitionOptions {\n\treadonly maxRequests: number;\n\treadonly maxInFlight: number;\n\treadonly durationMs: number;\n\treadonly perRequestMs: number;\n\treadonly monotonicNow?: () => number;\n}\n/**\n * Owns every started promise until actual settlement, including noncooperative requests.\n * The scope is per host-approved run. It is not a billing or process-isolation boundary.\n */\nexport class AcquisitionPool {\n\tprivate readonly now: () => number;\n\tprivate readonly deadline: number;\n\tprivate readonly maxRequests: number;\n\tprivate readonly maxInFlight: number;\n\tprivate readonly perRequestMs: number;\n\tprivate nextId = 0;\n\tprivate epoch = 0;\n\tprivate requests = 0;\n\tprivate readonly active = new Map<number, { controller: AbortController; quarantined: boolean }>();\n\tconstructor(options: AcquisitionOptions) {\n\t\tinteger(options.maxRequests, \"maxRequests\", 100_000);\n\t\tinteger(options.maxInFlight, \"maxInFlight\", 64);\n\t\tinteger(options.durationMs, \"durationMs\", 86_400_000);\n\t\tinteger(options.perRequestMs, \"perRequestMs\", 86_400_000);\n\t\tthis.now = options.monotonicNow ?? (() => performance.now());\n\t\tconst start = this.now();\n\t\tensure(Number.isFinite(start) && start >= 0, \"invalid monotonic clock\");\n\t\tthis.deadline = start + options.durationMs;\n\t\tthis.maxRequests = options.maxRequests;\n\t\tthis.maxInFlight = options.maxInFlight;\n\t\tthis.perRequestMs = options.perRequestMs;\n\t}\n\tsnapshot(): { requests: number; active: number; quarantined: number; remainingMs: number; epoch: number } {\n\t\treturn {\n\t\t\trequests: this.requests,\n\t\t\tactive: this.active.size,\n\t\t\tquarantined: [...this.active.values()].filter((e) => e.quarantined).length,\n\t\t\tremainingMs: Math.max(0, Math.floor(this.deadline - this.now())),\n\t\t\tepoch: this.epoch,\n\t\t};\n\t}\n\t/** Invalidation requests cancellation but never resets outstanding ownership or budget. */\n\tinvalidate(): void {\n\t\tthis.epoch++;\n\t\tfor (const entry of this.active.values()) {\n\t\t\tentry.quarantined = true;\n\t\t\tentry.controller.abort();\n\t\t}\n\t}\n\tasync run<T>(operation: (signal: AbortSignal) => Promise<T>, parent?: AbortSignal): Promise<AcquisitionResult<T>> {\n\t\tif (parent?.aborted) return { status: \"aborted\" };\n\t\tconst remaining = this.deadline - this.now();\n\t\tif (this.requests >= this.maxRequests || remaining <= 0 || this.perRequestMs === 0) {\n\t\t\treturn { status: \"budget-exhausted\" };\n\t\t}\n\t\tif (this.active.size >= this.maxInFlight) return { status: \"busy\" };\n\t\tconst id = ++this.nextId,\n\t\t\tepoch = this.epoch,\n\t\t\tcontroller = new AbortController();\n\t\tconst entry = { controller, quarantined: false };\n\t\tthis.active.set(id, entry);\n\t\tthis.requests++;\n\t\tconst expires = this.now() + Math.min(remaining, this.perRequestMs);\n\t\tlet resolveStop!: (result: AcquisitionResult<T>) => void;\n\t\tconst stopped = new Promise<AcquisitionResult<T>>((resolve) => {\n\t\t\tresolveStop = resolve;\n\t\t});\n\t\tconst stop = (status: \"aborted\" | \"timeout\" | \"stale\"): void => {\n\t\t\tif (this.active.has(id)) entry.quarantined = true;\n\t\t\tresolveStop({ status });\n\t\t\tcontroller.abort();\n\t\t};\n\t\tconst onParent = (): void => stop(\"aborted\");\n\t\tconst onInternal = (): void => {\n\t\t\tif (epoch !== this.epoch) resolveStop({ status: \"stale\" });\n\t\t};\n\t\tparent?.addEventListener(\"abort\", onParent, { once: true });\n\t\tcontroller.signal.addEventListener(\"abort\", onInternal, { once: true });\n\t\tconst timer = setTimeout(() => stop(\"timeout\"), Math.max(1, Math.ceil(expires - this.now())));\n\t\t// Both fulfillment and rejection are owned. Late results are never promoted.\n\t\tconst task = Promise.resolve()\n\t\t\t.then(() => {\n\t\t\t\tif (controller.signal.aborted || parent?.aborted) throw new Error(\"cancelled-before-dispatch\");\n\t\t\t\treturn operation(controller.signal);\n\t\t\t})\n\t\t\t.then(\n\t\t\t\t(value): AcquisitionResult<T> => {\n\t\t\t\t\tif (epoch !== this.epoch) return { status: \"stale\" };\n\t\t\t\t\tif (parent?.aborted) return { status: \"aborted\" };\n\t\t\t\t\tif (controller.signal.aborted || this.now() >= expires) return { status: \"timeout\" };\n\t\t\t\t\treturn { status: \"ok\", value };\n\t\t\t\t},\n\t\t\t\t(): AcquisitionResult<T> => ({\n\t\t\t\t\tstatus:\n\t\t\t\t\t\tepoch !== this.epoch\n\t\t\t\t\t\t\t? \"stale\"\n\t\t\t\t\t\t\t: parent?.aborted\n\t\t\t\t\t\t\t\t? \"aborted\"\n\t\t\t\t\t\t\t\t: controller.signal.aborted\n\t\t\t\t\t\t\t\t\t? \"timeout\"\n\t\t\t\t\t\t\t\t\t: \"error\",\n\t\t\t\t}),\n\t\t\t)\n\t\t\t.finally(() => {\n\t\t\t\tthis.active.delete(id);\n\t\t\t});\n\t\ttry {\n\t\t\treturn await Promise.race([task, stopped]);\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\t\t\tparent?.removeEventListener(\"abort\", onParent);\n\t\t\tcontroller.signal.removeEventListener(\"abort\", onInternal);\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Finite-sample risk bounds for automation gating (Jev audit algorithm A4).
3
+ *
4
+ * A point estimate must never widen automation scope. Two failures in a
5
+ * hundred is not "a 2% error rate" — its one-sided 95% Clopper–Pearson upper
6
+ * bound is about 6.2%, and that bound is what a gate reads.
7
+ *
8
+ * Scope: this bounds the failure probability of samples drawn under the stated
9
+ * conditions. It is not a safety guarantee for the next action, and it assumes
10
+ * a fixed policy, a consistent failure event, and adequately independent
11
+ * samples. Three hundred repeated clicks inside one task are not three hundred
12
+ * independent trials; cluster by task or environment instance before using it.
13
+ */
14
+ /** Regularized incomplete beta I_x(a,b). */
15
+ export declare function regularizedIncompleteBeta(a: number, b: number, x: number): number;
16
+ /**
17
+ * One-sided Clopper–Pearson upper bound on the failure probability.
18
+ *
19
+ * `U(k,n) = Beta⁻¹(1−α; k+1, n−k)` for `0 ≤ k < n`, and `1` when every trial
20
+ * failed. Zero trials is not zero risk: it is absent evidence, so it throws.
21
+ */
22
+ export declare function clopperPearsonUpperBound(failures: number, trials: number, alpha: number): number;
23
+ /** Closed form of {@link clopperPearsonUpperBound} when no trial failed. */
24
+ export declare function zeroFailureUpperBound(trials: number, alpha: number): number;
25
+ /**
26
+ * Smallest failure-free sample size whose upper bound meets `targetEpsilon`.
27
+ *
28
+ * `n ≥ ⌈log α / log(1−ε)⌉`, then nudged against the bound itself so a
29
+ * floating-point boundary cannot return a sample size that misses the target.
30
+ */
31
+ export declare function minimumZeroFailureSamples(targetEpsilon: number, alpha: number): number;
32
+ /**
33
+ * Bonferroni split for `thresholds × groups` comparisons on one validation set.
34
+ *
35
+ * Searching many thresholds and reporting the best keeps none of a single
36
+ * interval's nominal guarantee; this is the conservative correction, and it
37
+ * raises the required sample size on purpose.
38
+ */
39
+ export declare function bonferroniAlpha(alpha: number, thresholds: number, groups: number): number;
40
+ /**
41
+ * Union bound over per-step failure risks.
42
+ *
43
+ * Holds without any independence assumption, which is the point: step risks in
44
+ * one task are usually correlated, so multiplying them would understate the
45
+ * whole-task risk.
46
+ */
47
+ export declare function unionBoundRisk(stepRisks: readonly number[]): number;
48
+ //# sourceMappingURL=risk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"risk.d.ts","sourceRoot":"","sources":["../../src/metacognition/risk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAyDH,4CAA4C;AAC5C,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAOjF;AAqBD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAShG;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAI3E;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAUtF;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAKzF;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAWnE","sourcesContent":["/**\n * Finite-sample risk bounds for automation gating (Jev audit algorithm A4).\n *\n * A point estimate must never widen automation scope. Two failures in a\n * hundred is not \"a 2% error rate\" — its one-sided 95% Clopper–Pearson upper\n * bound is about 6.2%, and that bound is what a gate reads.\n *\n * Scope: this bounds the failure probability of samples drawn under the stated\n * conditions. It is not a safety guarantee for the next action, and it assumes\n * a fixed policy, a consistent failure event, and adequately independent\n * samples. Three hundred repeated clicks inside one task are not three hundred\n * independent trials; cluster by task or environment instance before using it.\n */\n\nimport { ensure } from \"./validation.ts\";\n\nconst LANCZOS_G = 7;\nconst LANCZOS_COEFFICIENTS = [\n\t0.999_999_999_999_809_93, 676.520_368_121_885_1, -1259.139_216_722_402_8, 771.323_428_777_653_13,\n\t-176.615_029_162_140_59, 12.507_343_278_686_905, -0.138_571_095_265_720_12, 9.984_369_578_019_571_6e-6,\n\t1.505_632_735_149_311_6e-7,\n] as const;\n\nfunction logGamma(x: number): number {\n\tif (x < 0.5) return Math.log(Math.PI / Math.sin(Math.PI * x)) - logGamma(1 - x);\n\tconst z = x - 1;\n\tlet a = LANCZOS_COEFFICIENTS[0];\n\tconst t = z + LANCZOS_G + 0.5;\n\tfor (const [index, coefficient] of LANCZOS_COEFFICIENTS.entries()) {\n\t\tif (index > 0) a += coefficient / (z + index);\n\t}\n\treturn 0.5 * Math.log(2 * Math.PI) + (z + 0.5) * Math.log(t) - t + Math.log(a);\n}\n\n/** Continued-fraction expansion for the incomplete beta (Lentz's method). */\nfunction betaContinuedFraction(a: number, b: number, x: number): number {\n\tconst maxIterations = 300;\n\tconst epsilon = 3e-16;\n\tconst tiny = 1e-300;\n\tconst qab = a + b;\n\tconst qap = a + 1;\n\tconst qam = a - 1;\n\tlet c = 1;\n\tlet d = 1 - (qab * x) / qap;\n\tif (Math.abs(d) < tiny) d = tiny;\n\td = 1 / d;\n\tlet h = d;\n\tfor (let m = 1; m <= maxIterations; m += 1) {\n\t\tconst m2 = 2 * m;\n\t\tlet aa = (m * (b - m) * x) / ((qam + m2) * (a + m2));\n\t\td = 1 + aa * d;\n\t\tif (Math.abs(d) < tiny) d = tiny;\n\t\tc = 1 + aa / c;\n\t\tif (Math.abs(c) < tiny) c = tiny;\n\t\td = 1 / d;\n\t\th *= d * c;\n\t\taa = (-(a + m) * (qab + m) * x) / ((a + m2) * (qap + m2));\n\t\td = 1 + aa * d;\n\t\tif (Math.abs(d) < tiny) d = tiny;\n\t\tc = 1 + aa / c;\n\t\tif (Math.abs(c) < tiny) c = tiny;\n\t\td = 1 / d;\n\t\tconst delta = d * c;\n\t\th *= delta;\n\t\tif (Math.abs(delta - 1) < epsilon) break;\n\t}\n\treturn h;\n}\n\n/** Regularized incomplete beta I_x(a,b). */\nexport function regularizedIncompleteBeta(a: number, b: number, x: number): number {\n\tif (x <= 0) return 0;\n\tif (x >= 1) return 1;\n\tconst front = Math.exp(logGamma(a + b) - logGamma(a) - logGamma(b) + a * Math.log(x) + b * Math.log1p(-x));\n\treturn x < (a + 1) / (a + b + 2)\n\t\t? (front * betaContinuedFraction(a, b, x)) / a\n\t\t: 1 - (front * betaContinuedFraction(b, a, 1 - x)) / b;\n}\n\n/** Inverse of {@link regularizedIncompleteBeta} by bisection; I_x is monotone in x. */\nfunction betaQuantile(p: number, a: number, b: number): number {\n\tlet low = 0;\n\tlet high = 1;\n\tfor (let i = 0; i < 200 && high - low > 1e-15; i += 1) {\n\t\tconst mid = (low + high) / 2;\n\t\tif (regularizedIncompleteBeta(a, b, mid) < p) low = mid;\n\t\telse high = mid;\n\t}\n\treturn (low + high) / 2;\n}\n\nfunction assertAlpha(alpha: number): void {\n\tensure(\n\t\ttypeof alpha === \"number\" && Number.isFinite(alpha) && alpha > 0 && alpha < 1,\n\t\t\"alpha must be a probability strictly between 0 and 1\",\n\t);\n}\n\n/**\n * One-sided Clopper–Pearson upper bound on the failure probability.\n *\n * `U(k,n) = Beta⁻¹(1−α; k+1, n−k)` for `0 ≤ k < n`, and `1` when every trial\n * failed. Zero trials is not zero risk: it is absent evidence, so it throws.\n */\nexport function clopperPearsonUpperBound(failures: number, trials: number, alpha: number): number {\n\tensure(Number.isSafeInteger(trials) && trials > 0, \"insufficient evidence: trials must be a positive integer\");\n\tensure(\n\t\tNumber.isSafeInteger(failures) && failures >= 0 && failures <= trials,\n\t\t\"failures must be an integer in [0, trials]\",\n\t);\n\tassertAlpha(alpha);\n\tif (failures === trials) return 1;\n\treturn betaQuantile(1 - alpha, failures + 1, trials - failures);\n}\n\n/** Closed form of {@link clopperPearsonUpperBound} when no trial failed. */\nexport function zeroFailureUpperBound(trials: number, alpha: number): number {\n\tensure(Number.isSafeInteger(trials) && trials > 0, \"insufficient evidence: trials must be a positive integer\");\n\tassertAlpha(alpha);\n\treturn -Math.expm1(Math.log(alpha) / trials);\n}\n\n/**\n * Smallest failure-free sample size whose upper bound meets `targetEpsilon`.\n *\n * `n ≥ ⌈log α / log(1−ε)⌉`, then nudged against the bound itself so a\n * floating-point boundary cannot return a sample size that misses the target.\n */\nexport function minimumZeroFailureSamples(targetEpsilon: number, alpha: number): number {\n\tensure(\n\t\ttypeof targetEpsilon === \"number\" && Number.isFinite(targetEpsilon) && targetEpsilon > 0 && targetEpsilon < 1,\n\t\t\"targetEpsilon must be strictly between 0 and 1\",\n\t);\n\tassertAlpha(alpha);\n\tlet n = Math.max(1, Math.ceil(Math.log(alpha) / Math.log1p(-targetEpsilon)));\n\twhile (zeroFailureUpperBound(n, alpha) > targetEpsilon) n += 1;\n\twhile (n > 1 && zeroFailureUpperBound(n - 1, alpha) <= targetEpsilon) n -= 1;\n\treturn n;\n}\n\n/**\n * Bonferroni split for `thresholds × groups` comparisons on one validation set.\n *\n * Searching many thresholds and reporting the best keeps none of a single\n * interval's nominal guarantee; this is the conservative correction, and it\n * raises the required sample size on purpose.\n */\nexport function bonferroniAlpha(alpha: number, thresholds: number, groups: number): number {\n\tassertAlpha(alpha);\n\tensure(Number.isSafeInteger(thresholds) && thresholds > 0, \"thresholds must be a positive integer\");\n\tensure(Number.isSafeInteger(groups) && groups > 0, \"groups must be a positive integer\");\n\treturn alpha / (thresholds * groups);\n}\n\n/**\n * Union bound over per-step failure risks.\n *\n * Holds without any independence assumption, which is the point: step risks in\n * one task are usually correlated, so multiplying them would understate the\n * whole-task risk.\n */\nexport function unionBoundRisk(stepRisks: readonly number[]): number {\n\tensure(Array.isArray(stepRisks) && stepRisks.length > 0, \"stepRisks must be a non-empty array\");\n\tlet total = 0;\n\tfor (const risk of stepRisks) {\n\t\tensure(\n\t\t\ttypeof risk === \"number\" && Number.isFinite(risk) && risk >= 0 && risk <= 1,\n\t\t\t\"each step risk must be a probability in [0, 1]\",\n\t\t);\n\t\ttotal += risk;\n\t}\n\treturn Math.min(1, total);\n}\n"]}