@openprose/reactor 0.1.0-rc.1

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 (78) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +231 -0
  3. package/dist/adapters/agent-sdk-passthrough/index.d.ts +8 -0
  4. package/dist/adapters/agent-sdk-passthrough/index.d.ts.map +1 -0
  5. package/dist/adapters/agent-sdk-passthrough/index.js +25 -0
  6. package/dist/adapters/clock-system/index.d.ts +9 -0
  7. package/dist/adapters/clock-system/index.d.ts.map +1 -0
  8. package/dist/adapters/clock-system/index.js +39 -0
  9. package/dist/adapters/connector-static/index.d.ts +11 -0
  10. package/dist/adapters/connector-static/index.d.ts.map +1 -0
  11. package/dist/adapters/connector-static/index.js +35 -0
  12. package/dist/adapters/event-sink-memory/index.d.ts +10 -0
  13. package/dist/adapters/event-sink-memory/index.d.ts.map +1 -0
  14. package/dist/adapters/event-sink-memory/index.js +20 -0
  15. package/dist/adapters/index.d.ts +10 -0
  16. package/dist/adapters/index.d.ts.map +1 -0
  17. package/dist/adapters/index.js +25 -0
  18. package/dist/adapters/json.d.ts +3 -0
  19. package/dist/adapters/json.d.ts.map +1 -0
  20. package/dist/adapters/json.js +61 -0
  21. package/dist/adapters/model-gateway-record-replay/index.d.ts +24 -0
  22. package/dist/adapters/model-gateway-record-replay/index.d.ts.map +1 -0
  23. package/dist/adapters/model-gateway-record-replay/index.js +55 -0
  24. package/dist/adapters/sandbox-null/index.d.ts +3 -0
  25. package/dist/adapters/sandbox-null/index.d.ts.map +1 -0
  26. package/dist/adapters/sandbox-null/index.js +8 -0
  27. package/dist/adapters/storage-fs/index.d.ts +14 -0
  28. package/dist/adapters/storage-fs/index.d.ts.map +1 -0
  29. package/dist/adapters/storage-fs/index.js +65 -0
  30. package/dist/adapters/storage-memory/index.d.ts +11 -0
  31. package/dist/adapters/storage-memory/index.d.ts.map +1 -0
  32. package/dist/adapters/storage-memory/index.js +34 -0
  33. package/dist/adapters/types.d.ts +22 -0
  34. package/dist/adapters/types.d.ts.map +1 -0
  35. package/dist/adapters/types.js +97 -0
  36. package/dist/composition/index.d.ts +79 -0
  37. package/dist/composition/index.d.ts.map +1 -0
  38. package/dist/composition/index.js +280 -0
  39. package/dist/cost/index.d.ts +49 -0
  40. package/dist/cost/index.d.ts.map +1 -0
  41. package/dist/cost/index.js +206 -0
  42. package/dist/evidence-plan/index.d.ts +57 -0
  43. package/dist/evidence-plan/index.d.ts.map +1 -0
  44. package/dist/evidence-plan/index.js +164 -0
  45. package/dist/forecast/index.d.ts +39 -0
  46. package/dist/forecast/index.d.ts.map +1 -0
  47. package/dist/forecast/index.js +119 -0
  48. package/dist/index.d.ts +14 -0
  49. package/dist/index.d.ts.map +1 -0
  50. package/dist/index.js +29 -0
  51. package/dist/judge/index.d.ts +29 -0
  52. package/dist/judge/index.d.ts.map +1 -0
  53. package/dist/judge/index.js +138 -0
  54. package/dist/kernel/index.d.ts +170 -0
  55. package/dist/kernel/index.d.ts.map +1 -0
  56. package/dist/kernel/index.js +637 -0
  57. package/dist/memo/index.d.ts +59 -0
  58. package/dist/memo/index.d.ts.map +1 -0
  59. package/dist/memo/index.js +189 -0
  60. package/dist/policy/index.d.ts +249 -0
  61. package/dist/policy/index.d.ts.map +1 -0
  62. package/dist/policy/index.js +1463 -0
  63. package/dist/projection/index.d.ts +119 -0
  64. package/dist/projection/index.d.ts.map +1 -0
  65. package/dist/projection/index.js +506 -0
  66. package/dist/reactor/index.d.ts +54 -0
  67. package/dist/reactor/index.d.ts.map +1 -0
  68. package/dist/reactor/index.js +1861 -0
  69. package/dist/receipt/index.d.ts +190 -0
  70. package/dist/receipt/index.d.ts.map +1 -0
  71. package/dist/receipt/index.js +646 -0
  72. package/dist/sdk/exit-bundle.d.ts +144 -0
  73. package/dist/sdk/exit-bundle.d.ts.map +1 -0
  74. package/dist/sdk/exit-bundle.js +1034 -0
  75. package/dist/sdk/index.d.ts +201 -0
  76. package/dist/sdk/index.d.ts.map +1 -0
  77. package/dist/sdk/index.js +418 -0
  78. package/package.json +89 -0
@@ -0,0 +1,637 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KERNEL_MAY_NEVER = exports.KERNEL_BACKSTOPS = exports.KERNEL_HOUR_MS = exports.KERNEL_DAY_MS = void 0;
4
+ exports.judgedActivations = judgedActivations;
5
+ exports.evaluatePredicate = evaluatePredicate;
6
+ exports.evaluateBackstops = evaluateBackstops;
7
+ exports.validateKernelPolicyArtifact = validateKernelPolicyArtifact;
8
+ exports.evaluateBackstopDivergencePredicate = evaluateBackstopDivergencePredicate;
9
+ exports.resolvePersistentIndeterminate = resolvePersistentIndeterminate;
10
+ exports.compareRollback = compareRollback;
11
+ exports.detectReceiptCycles = detectReceiptCycles;
12
+ exports.createKernelSafetyReceipt = createKernelSafetyReceipt;
13
+ const receipt_1 = require("../receipt");
14
+ exports.KERNEL_DAY_MS = 24 * 60 * 60 * 1000;
15
+ exports.KERNEL_HOUR_MS = 60 * 60 * 1000;
16
+ exports.KERNEL_BACKSTOPS = {
17
+ maxPolicyAgeMs: 30 * exports.KERNEL_DAY_MS,
18
+ maxPolicyAgeNoAnchorMs: 7 * exports.KERNEL_DAY_MS,
19
+ minRecompileIntervalMs: exports.KERNEL_HOUR_MS,
20
+ maxCalibrationDivergenceMultiplier: 2,
21
+ maxUnforcedDeepIntervalMs: 7 * exports.KERNEL_DAY_MS,
22
+ };
23
+ const VALIDATED_KERNEL_POLICY_ARTIFACT_TOKEN_BRAND = Symbol("ValidatedKernelPolicyArtifactToken");
24
+ const JUDGED_ACTIVATIONS_BRAND = Symbol("JudgedActivations");
25
+ const CONTENT_HASH_V0_PATTERN = /^sha256:[a-f0-9]{64}$/;
26
+ exports.KERNEL_MAY_NEVER = [
27
+ "author judgment, policy, cadence, thresholds, or freshness functions",
28
+ "call a model, agent SDK, or policy author to decide whether a backstop fired",
29
+ "interpret *.prose.md or add new source syntax",
30
+ "lengthen, disable, or silently reinterpret a fixed backstop",
31
+ "quiesce on missing, stale, conflicting, or malformed safety-critical inputs",
32
+ "let indeterminate persist silently past a backstop interval",
33
+ "mutate the world or perform fulfillment side effects",
34
+ "discover new evidence dependencies during shallow judging",
35
+ "patch a memo key in place after deep roaming finds a new dependency",
36
+ "treat a null signer as a cryptographic signature",
37
+ "use wall-clock duration for rollback comparisons",
38
+ "make cost.provider_norm part of a kernel decision",
39
+ "emit fail-safe, indeterminate, or blocked outcomes as droppable annotations",
40
+ "hide uncertainty by emitting confident up when calibration is degraded",
41
+ ];
42
+ function judgedActivations(value, name = "judged_activations") {
43
+ assertJudgedActivationCount(value, name);
44
+ return value;
45
+ }
46
+ function evaluatePredicate(expression, facts) {
47
+ if (!isRecord(expression)) {
48
+ return { outcome: "indeterminate", reason: "predicate is malformed" };
49
+ }
50
+ const kind = expression["kind"];
51
+ switch (kind) {
52
+ case "equals":
53
+ return evaluateFactComparison(expression, facts, (left, right) => left === right);
54
+ case "not-equals":
55
+ return evaluateFactComparison(expression, facts, (left, right) => left !== right);
56
+ case "greater-than-or-equal":
57
+ return evaluateNumericComparison(expression, facts, (left, right) => left >= right);
58
+ case "less-than":
59
+ return evaluateNumericComparison(expression, facts, (left, right) => left < right);
60
+ case "and":
61
+ return evaluateAnd(expression["predicates"], facts);
62
+ case "or":
63
+ return evaluateOr(expression["predicates"], facts);
64
+ case "not":
65
+ return invertPredicate(evaluatePredicate(expression["predicate"], facts));
66
+ default:
67
+ return { outcome: "indeterminate", reason: "predicate kind is malformed" };
68
+ }
69
+ }
70
+ function evaluateBackstops(input) {
71
+ if (!isValidatedKernelPolicyArtifactToken(input.token)) {
72
+ throw new Error("validated policy artifact token is required for backstop evaluation");
73
+ }
74
+ assertJudgedActivationCount(input.policy_warmup_judged_activations, "policy_warmup_judged_activations");
75
+ if (input.warmup_length !== undefined) {
76
+ assertJudgedActivationCount(input.warmup_length, "warmup_length");
77
+ }
78
+ const asOfMs = parseInstantMs(input.as_of, "as_of");
79
+ const outcomes = [];
80
+ const policyAgeMs = asOfMs - parseInstantMs(input.last_policy_revalidated_at, "last_policy_revalidated_at");
81
+ const noAnchor = input.token.no_anchor;
82
+ if (noAnchor) {
83
+ if (policyAgeMs >= exports.KERNEL_BACKSTOPS.maxPolicyAgeNoAnchorMs) {
84
+ outcomes.push({
85
+ backstop: "max_policy_age_no_anchor",
86
+ action: "force-policy-revalidation",
87
+ reason: "no-anchor policy age crossed the 7 day substitution ceiling",
88
+ });
89
+ }
90
+ }
91
+ else if (policyAgeMs >= exports.KERNEL_BACKSTOPS.maxPolicyAgeMs) {
92
+ outcomes.push({
93
+ backstop: "max_policy_age",
94
+ action: "force-policy-revalidation",
95
+ reason: "policy age crossed the 30 day ceiling",
96
+ });
97
+ }
98
+ const sinceRecompileMs = asOfMs - parseInstantMs(input.last_recompile_at, "last_recompile_at");
99
+ if (input.recompile_requested &&
100
+ sinceRecompileMs < exports.KERNEL_BACKSTOPS.minRecompileIntervalMs) {
101
+ outcomes.push({
102
+ backstop: "min_recompile_interval",
103
+ action: "delay-recompile-for-min-interval",
104
+ reason: "anti-thrash floor delays recompile but not safety escalation",
105
+ });
106
+ }
107
+ if (input.observed_calibration_divergence_multiplier !== undefined &&
108
+ input.observed_calibration_divergence_multiplier >=
109
+ exports.KERNEL_BACKSTOPS.maxCalibrationDivergenceMultiplier) {
110
+ outcomes.push({
111
+ backstop: "max_calibration_divergence",
112
+ action: "force-policy-recompile",
113
+ reason: "observed calibration divergence crossed the 2x ceiling",
114
+ });
115
+ }
116
+ if (!noAnchor) {
117
+ if (input.max_calibration_evidence_age_ms === undefined) {
118
+ outcomes.push({
119
+ backstop: "max_calibration_evidence_age",
120
+ action: "enter-degraded-calibration",
121
+ reason: "missing calibration evidence age seed fails closed as stale",
122
+ });
123
+ }
124
+ else if (input.calibration_evidence_as_of === undefined) {
125
+ outcomes.push({
126
+ backstop: "max_calibration_evidence_age",
127
+ action: "enter-degraded-calibration",
128
+ reason: "missing calibration evidence fails closed as stale",
129
+ });
130
+ }
131
+ else {
132
+ const evidenceAgeMs = asOfMs -
133
+ parseInstantMs(input.calibration_evidence_as_of, "calibration_evidence_as_of");
134
+ if (evidenceAgeMs >= input.max_calibration_evidence_age_ms) {
135
+ outcomes.push({
136
+ backstop: "max_calibration_evidence_age",
137
+ action: "enter-degraded-calibration",
138
+ reason: "calibration evidence is stale",
139
+ });
140
+ }
141
+ }
142
+ }
143
+ if (input.warmup_length === undefined) {
144
+ outcomes.push({
145
+ backstop: "warmup_length",
146
+ action: "no-last-known-good",
147
+ reason: "missing warmup seed means no policy attains last-known-good",
148
+ });
149
+ }
150
+ else if (input.policy_warmup_judged_activations < input.warmup_length) {
151
+ outcomes.push({
152
+ backstop: "warmup_length",
153
+ action: "no-last-known-good",
154
+ reason: "policy has not completed warmup without tripping",
155
+ });
156
+ }
157
+ if (noAnchor) {
158
+ if (input.last_unforced_deep_at === undefined) {
159
+ outcomes.push({
160
+ backstop: "max_unforced_deep_interval",
161
+ action: "force-deep-revalidation",
162
+ reason: "no-anchor B2 requires a deep revalidation baseline",
163
+ });
164
+ }
165
+ else {
166
+ const unforcedDeepAgeMs = asOfMs - parseInstantMs(input.last_unforced_deep_at, "last_unforced_deep_at");
167
+ if (unforcedDeepAgeMs >= exports.KERNEL_BACKSTOPS.maxUnforcedDeepIntervalMs) {
168
+ outcomes.push({
169
+ backstop: "max_unforced_deep_interval",
170
+ action: "force-deep-revalidation",
171
+ reason: "no-anchor B2 crossed the 7 day forced deep interval",
172
+ });
173
+ }
174
+ }
175
+ }
176
+ return { outcomes };
177
+ }
178
+ function validateKernelPolicyArtifact(input) {
179
+ const liveObservableSet = new Set(input.live_observables);
180
+ const predicateFacts = extractPredicateFacts(input.falsification_predicate);
181
+ const falsificationLiveObservableRefs = liveRefs(predicateFacts, liveObservableSet);
182
+ const divergenceFacts = input.backstop_divergence_predicate === undefined
183
+ ? []
184
+ : extractPredicateFacts(input.backstop_divergence_predicate);
185
+ const divergenceLiveObservableRefs = liveRefs(divergenceFacts, liveObservableSet);
186
+ const liveObservableRefs = liveRefs([...predicateFacts, ...divergenceFacts], liveObservableSet);
187
+ const errors = [];
188
+ if (falsificationLiveObservableRefs.length === 0) {
189
+ errors.push("falsification predicate must reference at least one live observable");
190
+ }
191
+ if (input.no_anchor && input.backstop_divergence_predicate === undefined) {
192
+ errors.push("no-anchor policy requires a B2 backstop_divergence_predicate");
193
+ }
194
+ if (input.no_anchor &&
195
+ input.backstop_divergence_predicate !== undefined &&
196
+ divergenceLiveObservableRefs.length === 0) {
197
+ errors.push("no-anchor backstop_divergence_predicate must reference at least one live observable");
198
+ }
199
+ if (errors.length > 0) {
200
+ return { ok: false, errors, live_observable_refs: liveObservableRefs };
201
+ }
202
+ const token = createValidatedKernelPolicyArtifactToken({
203
+ no_anchor: input.no_anchor,
204
+ live_observable_refs: liveObservableRefs,
205
+ ...(input.backstop_divergence_predicate === undefined
206
+ ? {}
207
+ : { backstop_divergence_predicate: input.backstop_divergence_predicate }),
208
+ });
209
+ return { ok: true, live_observable_refs: token.live_observable_refs, token };
210
+ }
211
+ function evaluateBackstopDivergencePredicate(input) {
212
+ if (!isValidatedKernelPolicyArtifactToken(input.token)) {
213
+ return {
214
+ outcome: "indeterminate",
215
+ predicate: {
216
+ outcome: "indeterminate",
217
+ reason: "validated policy artifact token is missing or malformed",
218
+ },
219
+ };
220
+ }
221
+ const predicateExpression = input.token.backstop_divergence_predicate;
222
+ if (predicateExpression === undefined) {
223
+ return {
224
+ outcome: "indeterminate",
225
+ predicate: {
226
+ outcome: "indeterminate",
227
+ reason: "validated policy artifact token has no backstop_divergence_predicate",
228
+ },
229
+ };
230
+ }
231
+ const outsideLiveRefsFact = firstFactOutsideLiveRefs(predicateExpression, input.token.live_observable_refs);
232
+ if (outsideLiveRefsFact !== undefined) {
233
+ return {
234
+ outcome: "indeterminate",
235
+ predicate: {
236
+ outcome: "indeterminate",
237
+ reason: `backstop_divergence_predicate references non-live observable ${outsideLiveRefsFact}`,
238
+ },
239
+ };
240
+ }
241
+ const predicate = evaluatePredicate(predicateExpression, input.facts);
242
+ if (predicate.outcome === "not-tripped") {
243
+ return { outcome: "not-tripped", predicate };
244
+ }
245
+ if (predicate.outcome === "indeterminate") {
246
+ return { outcome: "indeterminate", predicate };
247
+ }
248
+ return {
249
+ outcome: "force-policy-recompile",
250
+ predicate,
251
+ receipt: createKernelSafetyReceipt({
252
+ ...input.receipt_input,
253
+ reason: "backstop-divergence",
254
+ fix_target: "policy-artifact",
255
+ interrupt_cause: "needs-judgment",
256
+ event_cause: "escalation",
257
+ }),
258
+ };
259
+ }
260
+ function resolvePersistentIndeterminate(input) {
261
+ assertIntervalBasis(input.interval_basis, "interval_basis");
262
+ const elapsedMs = parseInstantMs(input.as_of, "as_of") -
263
+ parseInstantMs(input.first_indeterminate_at, "first_indeterminate_at");
264
+ if (elapsedMs < input.backstop_interval_ms) {
265
+ return {
266
+ outcome: "pending",
267
+ remaining_ms: input.backstop_interval_ms - elapsedMs,
268
+ };
269
+ }
270
+ return {
271
+ outcome: "needs-judgment",
272
+ receipt: createKernelSafetyReceipt({
273
+ ...input.receipt_input,
274
+ as_of: input.as_of,
275
+ reason: "persistent-indeterminate",
276
+ fix_target: "contract-author",
277
+ interrupt_cause: "needs-judgment",
278
+ event_cause: "escalation",
279
+ }),
280
+ };
281
+ }
282
+ function compareRollback(input) {
283
+ assertJudgedActivationCount(input.fresh_policy_judged_activations_before_trip, "fresh_policy_judged_activations_before_trip");
284
+ if (input.last_known_good_judged_activations_before_trip !== undefined) {
285
+ assertJudgedActivationCount(input.last_known_good_judged_activations_before_trip, "last_known_good_judged_activations_before_trip");
286
+ }
287
+ if (input.last_known_good_revision === undefined ||
288
+ input.last_known_good_judged_activations_before_trip === undefined) {
289
+ return {
290
+ outcome: "no-last-known-good",
291
+ reason: "rollback target absent; kernel must enter the safest available path",
292
+ };
293
+ }
294
+ if (input.fresh_policy_judged_activations_before_trip <
295
+ input.last_known_good_judged_activations_before_trip) {
296
+ return {
297
+ outcome: "rollback",
298
+ reason: "fresh policy tripped in fewer judged activations than last-known-good",
299
+ target_policy_revision: input.last_known_good_revision,
300
+ };
301
+ }
302
+ return {
303
+ outcome: "keep-current",
304
+ reason: "fresh policy did not prove worse by judged activation count",
305
+ target_policy_revision: input.fresh_policy_revision,
306
+ };
307
+ }
308
+ function detectReceiptCycles(edges) {
309
+ const graph = createCanonicalReceiptGraph(edges);
310
+ const visiting = new Set();
311
+ const visited = new Set();
312
+ const path = [];
313
+ for (const node of graph.keys()) {
314
+ const cycle = visitCycleNode(node, graph, visiting, visited, path);
315
+ if (cycle.length > 0) {
316
+ return { cycle_checked: true, has_cycle: true, cycle };
317
+ }
318
+ }
319
+ return { cycle_checked: true, has_cycle: false, cycle: [] };
320
+ }
321
+ function createKernelSafetyReceipt(input) {
322
+ if (input.evidence_input_ids.length === 0) {
323
+ throw new Error("kernel safety receipts require at least one evidence_input_id");
324
+ }
325
+ const role = input.role ?? "judge";
326
+ const eventCause = input.event_cause ?? "escalation";
327
+ return (0, receipt_1.createReceiptV0)({
328
+ core: {
329
+ responsibility_id: input.responsibility_id,
330
+ contract_revision: input.contract_revision,
331
+ event_cause: eventCause,
332
+ memo_key: input.memo_key,
333
+ evidence_input_ids: input.evidence_input_ids,
334
+ as_of: input.as_of,
335
+ role,
336
+ },
337
+ sig: (0, receipt_1.createNullSignerReceiptSignatureV0)(),
338
+ verdict: {
339
+ status: "blocked",
340
+ confidence: {
341
+ value: 0,
342
+ derivation_method: "kernel-fail-safe",
343
+ calibration_grade: "none",
344
+ label_source: "deterministic-kernel",
345
+ },
346
+ blocked: {
347
+ reason: input.reason,
348
+ fix_target: input.fix_target,
349
+ interrupt_cause: input.interrupt_cause,
350
+ },
351
+ },
352
+ freshness: {
353
+ as_of: input.as_of,
354
+ next_forecast_recheck: input.next_forecast_recheck ?? input.as_of,
355
+ },
356
+ composition: {
357
+ consumed_receipts: [],
358
+ cycle_checked: input.cycle_checked ?? false,
359
+ },
360
+ cost: {
361
+ provider: "kernel",
362
+ model: "deterministic",
363
+ role,
364
+ tags: ["kernel", "fail-safe"],
365
+ responsibility_id: input.responsibility_id,
366
+ run_id: `kernel-${input.as_of}`,
367
+ as_of: input.as_of,
368
+ tokens: { fresh: 0, reused: 0 },
369
+ surprise_cause: eventCause,
370
+ },
371
+ });
372
+ }
373
+ function createValidatedKernelPolicyArtifactToken(input) {
374
+ const token = {
375
+ [VALIDATED_KERNEL_POLICY_ARTIFACT_TOKEN_BRAND]: true,
376
+ no_anchor: input.no_anchor,
377
+ live_observable_refs: Object.freeze([...input.live_observable_refs]),
378
+ ...(input.backstop_divergence_predicate === undefined
379
+ ? {}
380
+ : {
381
+ backstop_divergence_predicate: cloneAndFreezePredicate(input.backstop_divergence_predicate),
382
+ }),
383
+ };
384
+ return Object.freeze(token);
385
+ }
386
+ function isValidatedKernelPolicyArtifactToken(value) {
387
+ if (!isRecord(value)) {
388
+ return false;
389
+ }
390
+ const brand = value[VALIDATED_KERNEL_POLICY_ARTIFACT_TOKEN_BRAND];
391
+ const liveObservableRefs = value["live_observable_refs"];
392
+ return (brand === true &&
393
+ typeof value["no_anchor"] === "boolean" &&
394
+ Array.isArray(liveObservableRefs) &&
395
+ liveObservableRefs.every((ref) => typeof ref === "string" && ref.length > 0));
396
+ }
397
+ function cloneAndFreezePredicate(expression) {
398
+ switch (expression.kind) {
399
+ case "equals":
400
+ case "not-equals":
401
+ case "greater-than-or-equal":
402
+ case "less-than":
403
+ return Object.freeze({ ...expression });
404
+ case "and":
405
+ case "or":
406
+ return Object.freeze({
407
+ kind: expression.kind,
408
+ predicates: Object.freeze(expression.predicates.map((predicate) => cloneAndFreezePredicate(predicate))),
409
+ });
410
+ case "not":
411
+ return Object.freeze({
412
+ kind: "not",
413
+ predicate: cloneAndFreezePredicate(expression.predicate),
414
+ });
415
+ }
416
+ }
417
+ function firstFactOutsideLiveRefs(expression, liveObservableRefs) {
418
+ const liveObservableSet = new Set(liveObservableRefs);
419
+ return extractPredicateFacts(expression).find((fact) => !liveObservableSet.has(fact));
420
+ }
421
+ function liveRefs(facts, liveObservableSet) {
422
+ return [...new Set(facts)]
423
+ .filter((fact) => liveObservableSet.has(fact))
424
+ .sort((left, right) => left.localeCompare(right));
425
+ }
426
+ function assertIntervalBasis(value, name) {
427
+ if (value !== "elapsed-time") {
428
+ throw new Error(`${name} must be elapsed-time for v0.1`);
429
+ }
430
+ }
431
+ function assertJudgedActivationCount(value, name) {
432
+ if (!Number.isSafeInteger(value) || value < 0) {
433
+ throw new Error(`${name} must be a non-negative safe integer`);
434
+ }
435
+ }
436
+ function evaluateFactComparison(expression, facts, compare) {
437
+ const factName = expression["fact"];
438
+ if (typeof factName !== "string" || factName.length === 0) {
439
+ return { outcome: "indeterminate", reason: "predicate fact is malformed" };
440
+ }
441
+ const expected = expression["value"];
442
+ if (!isFactValue(expected)) {
443
+ return { outcome: "indeterminate", reason: "predicate value is malformed" };
444
+ }
445
+ if (!Object.prototype.hasOwnProperty.call(facts, factName)) {
446
+ return { outcome: "indeterminate", reason: `missing fact ${factName}` };
447
+ }
448
+ return compare(facts[factName] ?? null, expected)
449
+ ? { outcome: "tripped" }
450
+ : { outcome: "not-tripped" };
451
+ }
452
+ function evaluateNumericComparison(expression, facts, compare) {
453
+ const factName = expression["fact"];
454
+ const expected = expression["value"];
455
+ if (typeof factName !== "string" || factName.length === 0) {
456
+ return { outcome: "indeterminate", reason: "predicate fact is malformed" };
457
+ }
458
+ if (typeof expected !== "number" || !Number.isFinite(expected)) {
459
+ return { outcome: "indeterminate", reason: "predicate threshold is malformed" };
460
+ }
461
+ if (!Object.prototype.hasOwnProperty.call(facts, factName)) {
462
+ return { outcome: "indeterminate", reason: `missing fact ${factName}` };
463
+ }
464
+ const actual = facts[factName];
465
+ if (typeof actual !== "number" || !Number.isFinite(actual)) {
466
+ return { outcome: "indeterminate", reason: `fact ${factName} is not numeric` };
467
+ }
468
+ return compare(actual, expected)
469
+ ? { outcome: "tripped" }
470
+ : { outcome: "not-tripped" };
471
+ }
472
+ function evaluateAnd(predicates, facts) {
473
+ if (!Array.isArray(predicates) || predicates.length === 0) {
474
+ return { outcome: "indeterminate", reason: "and predicate is malformed" };
475
+ }
476
+ let indeterminateReason;
477
+ for (const predicate of predicates) {
478
+ const result = evaluatePredicate(predicate, facts);
479
+ if (result.outcome === "not-tripped") {
480
+ return { outcome: "not-tripped" };
481
+ }
482
+ if (result.outcome === "indeterminate") {
483
+ indeterminateReason = result.reason ?? "and predicate is indeterminate";
484
+ }
485
+ }
486
+ return indeterminateReason === undefined
487
+ ? { outcome: "tripped" }
488
+ : { outcome: "indeterminate", reason: indeterminateReason };
489
+ }
490
+ function evaluateOr(predicates, facts) {
491
+ if (!Array.isArray(predicates) || predicates.length === 0) {
492
+ return { outcome: "indeterminate", reason: "or predicate is malformed" };
493
+ }
494
+ let indeterminateReason;
495
+ for (const predicate of predicates) {
496
+ const result = evaluatePredicate(predicate, facts);
497
+ if (result.outcome === "tripped") {
498
+ return { outcome: "tripped" };
499
+ }
500
+ if (result.outcome === "indeterminate") {
501
+ indeterminateReason = result.reason ?? "or predicate is indeterminate";
502
+ }
503
+ }
504
+ return indeterminateReason === undefined
505
+ ? { outcome: "not-tripped" }
506
+ : { outcome: "indeterminate", reason: indeterminateReason };
507
+ }
508
+ function invertPredicate(result) {
509
+ if (result.outcome === "indeterminate") {
510
+ return result;
511
+ }
512
+ return result.outcome === "tripped"
513
+ ? { outcome: "not-tripped" }
514
+ : { outcome: "tripped" };
515
+ }
516
+ function extractPredicateFacts(expression) {
517
+ const facts = new Set();
518
+ collectPredicateFacts(expression, facts);
519
+ return [...facts].sort((left, right) => left.localeCompare(right));
520
+ }
521
+ function collectPredicateFacts(expression, facts) {
522
+ if (!isRecord(expression)) {
523
+ return;
524
+ }
525
+ const kind = expression["kind"];
526
+ switch (kind) {
527
+ case "equals":
528
+ case "not-equals":
529
+ case "greater-than-or-equal":
530
+ case "less-than": {
531
+ const fact = expression["fact"];
532
+ if (typeof fact === "string" && fact.length > 0) {
533
+ facts.add(fact);
534
+ }
535
+ return;
536
+ }
537
+ case "and":
538
+ case "or": {
539
+ const predicates = expression["predicates"];
540
+ if (Array.isArray(predicates)) {
541
+ for (const predicate of predicates) {
542
+ collectPredicateFacts(predicate, facts);
543
+ }
544
+ }
545
+ return;
546
+ }
547
+ case "not":
548
+ collectPredicateFacts(expression["predicate"], facts);
549
+ return;
550
+ default:
551
+ return;
552
+ }
553
+ }
554
+ function visitCycleNode(node, graph, visiting, visited, path) {
555
+ if (visited.has(node)) {
556
+ return [];
557
+ }
558
+ const existingIndex = path.indexOf(node);
559
+ if (visiting.has(node) && existingIndex >= 0) {
560
+ return [...path.slice(existingIndex), node];
561
+ }
562
+ visiting.add(node);
563
+ path.push(node);
564
+ for (const next of graph.get(node) ?? []) {
565
+ const cycle = visitCycleNode(next, graph, visiting, visited, path);
566
+ if (cycle.length > 0) {
567
+ return cycle;
568
+ }
569
+ }
570
+ path.pop();
571
+ visiting.delete(node);
572
+ visited.add(node);
573
+ return [];
574
+ }
575
+ function createCanonicalReceiptGraph(edges) {
576
+ if (!Array.isArray(edges)) {
577
+ throw new Error("receipt cycle edges must be an array");
578
+ }
579
+ const adjacency = new Map();
580
+ for (const [index, edge] of edges.entries()) {
581
+ assertConsumedReceiptEdge(edge, index);
582
+ receiptGraphTargets(adjacency, edge.from).add(edge.to);
583
+ receiptGraphTargets(adjacency, edge.to);
584
+ }
585
+ const graph = new Map();
586
+ for (const node of [...adjacency.keys()].sort(compareContentHashV0)) {
587
+ const targets = adjacency.get(node) ?? new Set();
588
+ graph.set(node, [...targets].sort(compareContentHashV0));
589
+ }
590
+ return graph;
591
+ }
592
+ function receiptGraphTargets(adjacency, node) {
593
+ const existing = adjacency.get(node);
594
+ if (existing !== undefined) {
595
+ return existing;
596
+ }
597
+ const targets = new Set();
598
+ adjacency.set(node, targets);
599
+ return targets;
600
+ }
601
+ function assertConsumedReceiptEdge(edge, index) {
602
+ if (!isRecord(edge)) {
603
+ throw new Error(`receipt cycle edges[${index}] must be an object`);
604
+ }
605
+ assertContentHashV0(edge["from"], `receipt cycle edges[${index}].from`);
606
+ assertContentHashV0(edge["to"], `receipt cycle edges[${index}].to`);
607
+ }
608
+ function assertContentHashV0(value, name) {
609
+ if (typeof value !== "string" || !CONTENT_HASH_V0_PATTERN.test(value)) {
610
+ throw new Error(`${name} must use sha256:<64 lowercase hex>`);
611
+ }
612
+ }
613
+ function compareContentHashV0(left, right) {
614
+ if (left < right) {
615
+ return -1;
616
+ }
617
+ if (left > right) {
618
+ return 1;
619
+ }
620
+ return 0;
621
+ }
622
+ function isFactValue(value) {
623
+ return (value === null ||
624
+ typeof value === "string" ||
625
+ typeof value === "number" ||
626
+ typeof value === "boolean");
627
+ }
628
+ function isRecord(value) {
629
+ return typeof value === "object" && value !== null && !Array.isArray(value);
630
+ }
631
+ function parseInstantMs(value, name) {
632
+ const parsed = Date.parse(value);
633
+ if (!Number.isFinite(parsed)) {
634
+ throw new Error(`${name} must be a replayable instant`);
635
+ }
636
+ return parsed;
637
+ }
@@ -0,0 +1,59 @@
1
+ import { type ContentHashV0, type ReceiptRecheckKindV0, type ReceiptV0 } from "../receipt";
2
+ export declare const MEMO_KEY_SCHEMA: "openprose.memo-key";
3
+ export declare const MEMO_KEY_VERSION: 0;
4
+ export interface DependencyReceiptMemoRefV0 {
5
+ readonly upstream_content_hash: ContentHashV0;
6
+ readonly contract_revision: ContentHashV0;
7
+ readonly acceptable_signer_set: readonly string[];
8
+ }
9
+ export interface MemoKeyInputV0 {
10
+ readonly contract_revision: ContentHashV0;
11
+ readonly evidence_receipts: readonly ContentHashV0[];
12
+ readonly dependency_receipts: readonly DependencyReceiptMemoRefV0[];
13
+ }
14
+ export interface PolicyArtifactMemoNamespaceV0 {
15
+ readonly policy_artifact_namespace: string;
16
+ readonly policy_artifact_revision: string;
17
+ }
18
+ export interface MemoizedVerdictV0 {
19
+ readonly memo_key: ContentHashV0;
20
+ readonly verdict_receipt_hash: ContentHashV0;
21
+ readonly reusable_tokens: number;
22
+ readonly stored_as_of: string;
23
+ readonly receipt: ReceiptV0;
24
+ }
25
+ export type MemoLookupResultV0 = {
26
+ readonly outcome: "hit";
27
+ readonly entry: MemoizedVerdictV0;
28
+ } | {
29
+ readonly outcome: "miss";
30
+ readonly reason: "absent" | "namespace-empty";
31
+ };
32
+ export interface MemoHitReceiptInputV0 {
33
+ readonly source_receipt: ReceiptV0;
34
+ readonly as_of: string;
35
+ readonly next_forecast_recheck: string;
36
+ readonly event_cause?: "real-input" | "forecast-recheck" | "escalation";
37
+ readonly recheck_kind?: ReceiptRecheckKindV0;
38
+ }
39
+ export declare class InMemoryMemoStoreV0 {
40
+ private readonly namespaces;
41
+ lookup(namespace: PolicyArtifactMemoNamespaceV0, memoKey: ContentHashV0): MemoLookupResultV0;
42
+ store(namespace: PolicyArtifactMemoNamespaceV0, entry: MemoizedVerdictV0): void;
43
+ }
44
+ export declare function computeMemoKeyV0(input: MemoKeyInputV0): ContentHashV0;
45
+ export declare function normalizeMemoKeyInput(input: MemoKeyInputV0): {
46
+ readonly schema: typeof MEMO_KEY_SCHEMA;
47
+ readonly v: typeof MEMO_KEY_VERSION;
48
+ readonly contract_revision: ContentHashV0;
49
+ readonly evidence_receipts: readonly ContentHashV0[];
50
+ readonly dependency_receipts: readonly {
51
+ readonly upstream_content_hash: ContentHashV0;
52
+ readonly contract_revision: ContentHashV0;
53
+ readonly acceptable_signer_set: readonly string[];
54
+ }[];
55
+ };
56
+ export declare function createMemoHitReceiptV0(input: MemoHitReceiptInputV0): ReceiptV0;
57
+ export declare function createMemoizedVerdictEntryV0(memoKey: ContentHashV0, receipt: ReceiptV0): MemoizedVerdictV0;
58
+ export declare function namespaceKey(namespace: PolicyArtifactMemoNamespaceV0): string;
59
+ //# sourceMappingURL=index.d.ts.map