@hol-org/cigar 0.9.4

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 +201 -0
  2. package/NOTICE +5 -0
  3. package/README.md +68 -0
  4. package/dist/client.d.ts +16 -0
  5. package/dist/client.d.ts.map +1 -0
  6. package/dist/client.js +839 -0
  7. package/dist/client.js.map +1 -0
  8. package/dist/digest.d.ts +21 -0
  9. package/dist/digest.d.ts.map +1 -0
  10. package/dist/digest.js +406 -0
  11. package/dist/digest.js.map +1 -0
  12. package/dist/errors.d.ts +35 -0
  13. package/dist/errors.d.ts.map +1 -0
  14. package/dist/errors.js +54 -0
  15. package/dist/errors.js.map +1 -0
  16. package/dist/examples/quickstart.d.ts +2 -0
  17. package/dist/examples/quickstart.d.ts.map +1 -0
  18. package/dist/examples/quickstart.js +32 -0
  19. package/dist/examples/quickstart.js.map +1 -0
  20. package/dist/examples/two-agent-observer.d.ts +9 -0
  21. package/dist/examples/two-agent-observer.d.ts.map +1 -0
  22. package/dist/examples/two-agent-observer.js +59 -0
  23. package/dist/examples/two-agent-observer.js.map +1 -0
  24. package/dist/examples/verify-shared-bundle.d.ts +2 -0
  25. package/dist/examples/verify-shared-bundle.d.ts.map +1 -0
  26. package/dist/examples/verify-shared-bundle.js +13 -0
  27. package/dist/examples/verify-shared-bundle.js.map +1 -0
  28. package/dist/generated/cigar_service_pb.d.ts +642 -0
  29. package/dist/generated/cigar_service_pb.d.ts.map +1 -0
  30. package/dist/generated/cigar_service_pb.js +55 -0
  31. package/dist/generated/cigar_service_pb.js.map +1 -0
  32. package/dist/generated/context_abi_pb.d.ts +3429 -0
  33. package/dist/generated/context_abi_pb.d.ts.map +1 -0
  34. package/dist/generated/context_abi_pb.js +1386 -0
  35. package/dist/generated/context_abi_pb.js.map +1 -0
  36. package/dist/generated/errors.d.ts +242 -0
  37. package/dist/generated/errors.d.ts.map +1 -0
  38. package/dist/generated/errors.js +38 -0
  39. package/dist/generated/errors.js.map +1 -0
  40. package/dist/generated/generated/error_codes_pb.d.ts +188 -0
  41. package/dist/generated/generated/error_codes_pb.d.ts.map +1 -0
  42. package/dist/generated/generated/error_codes_pb.js +191 -0
  43. package/dist/generated/generated/error_codes_pb.js.map +1 -0
  44. package/dist/generated/models.d.ts +472 -0
  45. package/dist/generated/models.d.ts.map +1 -0
  46. package/dist/generated/models.js +314 -0
  47. package/dist/generated/models.js.map +1 -0
  48. package/dist/generated/operations.d.ts +912 -0
  49. package/dist/generated/operations.d.ts.map +1 -0
  50. package/dist/generated/operations.js +50 -0
  51. package/dist/generated/operations.js.map +1 -0
  52. package/dist/idempotency.d.ts +3 -0
  53. package/dist/idempotency.d.ts.map +1 -0
  54. package/dist/idempotency.js +16 -0
  55. package/dist/idempotency.js.map +1 -0
  56. package/dist/index.d.ts +13 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +13 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/release.json +6 -0
  61. package/dist/types.d.ts +115 -0
  62. package/dist/types.d.ts.map +1 -0
  63. package/dist/types.js +3 -0
  64. package/dist/types.js.map +1 -0
  65. package/dist/verify-replay.d.ts +3 -0
  66. package/dist/verify-replay.d.ts.map +1 -0
  67. package/dist/verify-replay.js +391 -0
  68. package/dist/verify-replay.js.map +1 -0
  69. package/dist/verify-vectors.d.ts +3 -0
  70. package/dist/verify-vectors.d.ts.map +1 -0
  71. package/dist/verify-vectors.js +383 -0
  72. package/dist/verify-vectors.js.map +1 -0
  73. package/dist/workflow-session.d.ts +93 -0
  74. package/dist/workflow-session.d.ts.map +1 -0
  75. package/dist/workflow-session.js +550 -0
  76. package/dist/workflow-session.js.map +1 -0
  77. package/fixtures/semantic-bundle-v1.json +13 -0
  78. package/package.json +60 -0
@@ -0,0 +1,550 @@
1
+ /** Identity-only state tracking for deterministic workflow context cycles. */
2
+ import { CigarError } from "./errors.js";
3
+ export const MAX_WORKFLOW_DELTA_CHAIN_LENGTH = 8;
4
+ export const MAX_WORKFLOW_REPLAY_CYCLES = 64;
5
+ export const WORKFLOW_CONTEXT_PHASES = [
6
+ "new", "plan_created", "target_bundle_loaded", "delta_compiled", "bundle_ready", "materialized",
7
+ "model_invocation_pending", "model_result_recorded", "effect_prepared", "observation_recorded",
8
+ "effect_authorization_revalidated", "effect_authorized", "effect_revalidated", "effect_dispatching",
9
+ "effect_ambiguous", "effect_settled", "checkpointed", "finished", "replay_verified", "quarantined",
10
+ ];
11
+ export const WORKFLOW_RESUME_ACTIONS = [
12
+ "create_context_plan", "compile_context_bundle", "compile_context_delta", "apply_context_delta",
13
+ "materialize_context_bundle", "begin_model_invocation", "resume_model_invocation",
14
+ "prepare_effect_or_ingest_observation", "ingest_observation", "authorize_effect_or_checkpoint",
15
+ "revalidate_context_bundle", "dispatch_effect", "observe_effect", "reconcile_effect", "checkpoint",
16
+ "materialize_or_finish", "replay", "complete",
17
+ ];
18
+ export const WORKFLOW_SESSION_ERROR_CODES = [
19
+ "invalid_transition", "invalid_event", "identity_mismatch", "invalidated", "limit_exceeded",
20
+ ];
21
+ export const WORKFLOW_SESSION_EVENT_NAMES = [
22
+ "plan_created", "bundle_compiled", "delta_compiled", "delta_applied", "materialized",
23
+ "model_invocation_started", "model_result_recorded", "effect_prepared", "observation_recorded",
24
+ "effect_authorized", "effect_revalidated", "effect_dispatched", "effect_observed", "cycle_checkpointed",
25
+ "finished", "replay_verified", "context_quarantined",
26
+ ];
27
+ export const WORKFLOW_QUARANTINE_REASONS = ["cancelled", "revoked", "invalidated"];
28
+ const ACTION_OPERATIONS = {
29
+ create_context_plan: "createContextPlan",
30
+ compile_context_bundle: "compileContextBundle",
31
+ compile_context_delta: "compileContextDelta",
32
+ apply_context_delta: null,
33
+ materialize_context_bundle: "materializeContextBundle",
34
+ begin_model_invocation: null,
35
+ resume_model_invocation: null,
36
+ prepare_effect_or_ingest_observation: null,
37
+ ingest_observation: "ingestCatalog",
38
+ authorize_effect_or_checkpoint: "authorizeEffect",
39
+ revalidate_context_bundle: "revalidateContextBundle",
40
+ dispatch_effect: "dispatchEffect",
41
+ observe_effect: "getEffectStatus",
42
+ reconcile_effect: "reconcileEffect",
43
+ checkpoint: null,
44
+ materialize_or_finish: null,
45
+ replay: "createReplay",
46
+ complete: null,
47
+ };
48
+ export function workflowOperationId(action) {
49
+ return ACTION_OPERATIONS[action];
50
+ }
51
+ export class WorkflowSessionError extends CigarError {
52
+ code;
53
+ constructor(code) {
54
+ super(`workflow context transition failed: ${code}`);
55
+ this.code = code;
56
+ }
57
+ }
58
+ const DIGEST = /^1220[0-9a-f]{64}$/;
59
+ const UUID_V7 = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
60
+ const TERMINAL_EFFECT_STATES = new Set([
61
+ "succeeded", "failed", "manual_resolution", "rejected", "expired", "cancelled", "compensated",
62
+ "compensation_failed",
63
+ ]);
64
+ /** Mutable, content-safe helper for one context lifecycle. Successful boundaries advance it atomically. */
65
+ export class WorkflowContextSession {
66
+ #phase = "new";
67
+ #completedTurns = 0;
68
+ #deltaChainLength = 0;
69
+ #activeContext;
70
+ #pendingContext;
71
+ #pendingDelta;
72
+ #selectedDelta;
73
+ #materialization;
74
+ #invocation;
75
+ #modelResultDigest;
76
+ #observationDigest;
77
+ #observationRevision;
78
+ #effect;
79
+ #completedCycles = [];
80
+ #replayVerified = false;
81
+ #quarantineReason;
82
+ get phase() { return this.#phase; }
83
+ get completedTurns() { return this.#completedTurns; }
84
+ get deltaChainLength() { return this.#deltaChainLength; }
85
+ get activeBundleId() { return this.#activeContext?.bundleId; }
86
+ replayIdentity() {
87
+ this.#requirePhase("finished", "replay_verified");
88
+ if (this.#completedCycles.length === 0)
89
+ fail("invalid_transition");
90
+ return cloneReplayIdentity({ cycles: this.#completedCycles });
91
+ }
92
+ compareReplay(candidate) {
93
+ const baseline = this.replayIdentity();
94
+ validateReplayIdentity(candidate);
95
+ return compareWorkflowReplay(baseline, candidate);
96
+ }
97
+ get resumeAction() {
98
+ switch (this.#phase) {
99
+ case "new":
100
+ case "observation_recorded": return "create_context_plan";
101
+ case "plan_created": return "compile_context_bundle";
102
+ case "target_bundle_loaded": return "compile_context_delta";
103
+ case "delta_compiled": return "apply_context_delta";
104
+ case "bundle_ready":
105
+ if (this.#modelResultDigest === undefined)
106
+ return "materialize_context_bundle";
107
+ return this.#effect === undefined ? "checkpoint" : "revalidate_context_bundle";
108
+ case "materialized": return "begin_model_invocation";
109
+ case "model_invocation_pending": return "resume_model_invocation";
110
+ case "model_result_recorded": return "prepare_effect_or_ingest_observation";
111
+ case "effect_prepared": return "ingest_observation";
112
+ case "effect_authorization_revalidated": return "authorize_effect_or_checkpoint";
113
+ case "effect_authorized": return "revalidate_context_bundle";
114
+ case "effect_revalidated": return "dispatch_effect";
115
+ case "effect_dispatching": return "observe_effect";
116
+ case "effect_ambiguous": return "reconcile_effect";
117
+ case "effect_settled": return "checkpoint";
118
+ case "checkpointed": return "materialize_or_finish";
119
+ case "finished": return "replay";
120
+ case "replay_verified":
121
+ case "quarantined": return "complete";
122
+ }
123
+ }
124
+ toString() {
125
+ return `WorkflowContextSession(phase=${this.#phase}, completedTurns=${this.#completedTurns}, `
126
+ + `deltaChainLength=${this.#deltaChainLength}, hasActiveContext=${this.#activeContext !== undefined}, `
127
+ + `hasPendingContext=${this.#pendingContext !== undefined}, hasPendingDelta=${this.#pendingDelta !== undefined}, `
128
+ + `hasSelectedDelta=${this.#selectedDelta !== undefined}, hasInvocation=${this.#invocation !== undefined}, `
129
+ + `hasProviderIdempotencyKey=${this.#invocation !== undefined}, `
130
+ + `hasModelResult=${this.#modelResultDigest !== undefined}, `
131
+ + `hasObservation=${this.#observationDigest !== undefined}, effectState=${this.#effect?.state ?? "none"}, `
132
+ + `completedCycleCount=${this.#completedCycles.length}, `
133
+ + `quarantineReason=${this.#quarantineReason ?? "none"})`;
134
+ }
135
+ recordPlanCreated(planId, bundleId, contractDigest) {
136
+ this.#requirePhase("new", "observation_recorded");
137
+ record(planId);
138
+ digest(bundleId);
139
+ digest(contractDigest);
140
+ this.#pendingContext = { planId, bundleId, contractDigest };
141
+ this.#pendingDelta = undefined;
142
+ this.#phase = "plan_created";
143
+ }
144
+ recordBundleCompiled(bundleId, contractDigest) {
145
+ this.#requirePhase("plan_created");
146
+ digest(bundleId);
147
+ digest(contractDigest);
148
+ const pending = this.#pendingContext;
149
+ if (pending === undefined)
150
+ fail("invalid_transition");
151
+ if (pending.bundleId !== bundleId || pending.contractDigest !== contractDigest)
152
+ fail("identity_mismatch");
153
+ if (this.#activeContext === undefined || this.#activeContext.bundleId === bundleId) {
154
+ this.#activeContext = pending;
155
+ this.#pendingContext = undefined;
156
+ this.#selectedDelta = undefined;
157
+ if (this.#deltaChainLength >= MAX_WORKFLOW_DELTA_CHAIN_LENGTH)
158
+ this.#deltaChainLength = 0;
159
+ this.#phase = "bundle_ready";
160
+ }
161
+ else if (this.#deltaChainLength >= MAX_WORKFLOW_DELTA_CHAIN_LENGTH) {
162
+ this.#activeContext = pending;
163
+ this.#pendingContext = undefined;
164
+ this.#selectedDelta = undefined;
165
+ this.#deltaChainLength = 0;
166
+ this.#phase = "bundle_ready";
167
+ }
168
+ else {
169
+ this.#phase = "target_bundle_loaded";
170
+ }
171
+ }
172
+ recordDeltaCompiled(baseBundleId, targetBundleId, deltaDigest) {
173
+ this.#requirePhase("target_bundle_loaded");
174
+ if (this.#deltaChainLength >= MAX_WORKFLOW_DELTA_CHAIN_LENGTH)
175
+ fail("limit_exceeded");
176
+ digest(baseBundleId);
177
+ digest(targetBundleId);
178
+ digest(deltaDigest);
179
+ if (this.#activeContext === undefined || this.#pendingContext === undefined)
180
+ fail("invalid_transition");
181
+ if (this.#activeContext.bundleId !== baseBundleId || this.#pendingContext.bundleId !== targetBundleId) {
182
+ fail("identity_mismatch");
183
+ }
184
+ this.#pendingDelta = { baseBundleId, targetBundleId, deltaDigest };
185
+ this.#phase = "delta_compiled";
186
+ }
187
+ recordDeltaApplied(baseBundleId, targetBundleId, deltaDigest) {
188
+ this.#requirePhase("delta_compiled");
189
+ digest(baseBundleId);
190
+ digest(targetBundleId);
191
+ digest(deltaDigest);
192
+ const pending = this.#pendingDelta;
193
+ if (pending === undefined)
194
+ fail("invalid_transition");
195
+ if (pending.baseBundleId !== baseBundleId || pending.targetBundleId !== targetBundleId
196
+ || pending.deltaDigest !== deltaDigest)
197
+ fail("identity_mismatch");
198
+ if (this.#deltaChainLength >= MAX_WORKFLOW_DELTA_CHAIN_LENGTH)
199
+ fail("limit_exceeded");
200
+ this.#deltaChainLength += 1;
201
+ this.#selectedDelta = pending;
202
+ this.#activeContext = this.#pendingContext;
203
+ this.#pendingContext = undefined;
204
+ this.#pendingDelta = undefined;
205
+ this.#phase = "bundle_ready";
206
+ }
207
+ recordMaterialized(bundleId, tokenizerFingerprint, materializerFingerprint, physicalInputTokens) {
208
+ this.#requirePhase("bundle_ready", "checkpointed");
209
+ digest(bundleId);
210
+ digest(tokenizerFingerprint);
211
+ digest(materializerFingerprint);
212
+ if (this.#modelResultDigest !== undefined || !positiveInteger(physicalInputTokens, 0xffff_ffff)) {
213
+ fail("invalid_event");
214
+ }
215
+ if (this.activeBundleId !== bundleId)
216
+ fail("identity_mismatch");
217
+ this.#materialization = {
218
+ bundleId, tokenizerFingerprint, materializerFingerprint, physicalInputTokens,
219
+ };
220
+ this.#phase = "materialized";
221
+ }
222
+ beginModelInvocation(invocationId, requestDigest, idempotencyKeyDigest) {
223
+ this.#requirePhase("materialized");
224
+ record(invocationId);
225
+ digest(requestDigest);
226
+ digest(idempotencyKeyDigest);
227
+ this.#invocation = { invocationId, requestDigest, idempotencyKeyDigest };
228
+ this.#phase = "model_invocation_pending";
229
+ }
230
+ recordModelResult(invocationId, resultDigest) {
231
+ this.#requirePhase("model_invocation_pending");
232
+ record(invocationId);
233
+ digest(resultDigest);
234
+ if (this.#invocation?.invocationId !== invocationId)
235
+ fail("identity_mismatch");
236
+ this.#modelResultDigest = resultDigest;
237
+ this.#phase = "model_result_recorded";
238
+ }
239
+ recordEffectPrepared(effectId, intentDigest, effectVersion, state = "prepared", attemptCount = 0, reconciliationCount = 0) {
240
+ this.#requirePhase("model_result_recorded");
241
+ record(effectId);
242
+ digest(intentDigest);
243
+ positiveBigint(effectVersion);
244
+ if (this.#effect !== undefined || state !== "prepared" || !count(attemptCount)
245
+ || !count(reconciliationCount) || attemptCount !== 0 || reconciliationCount !== 0)
246
+ fail("invalid_event");
247
+ this.#effect = { effectId, intentDigest, effectVersion, state, attemptCount, reconciliationCount };
248
+ this.#phase = "effect_prepared";
249
+ }
250
+ recordObservation(publicationDigest, revision) {
251
+ this.#requirePhase("model_result_recorded", "effect_prepared");
252
+ digest(publicationDigest);
253
+ positiveBigint(revision);
254
+ this.#observationDigest = publicationDigest;
255
+ this.#observationRevision = revision;
256
+ this.#phase = "observation_recorded";
257
+ }
258
+ recordEffectAuthorized(effectId, intentDigest, effectVersion, state = "authorized", attemptCount = 0, reconciliationCount = 0) {
259
+ this.#requirePhase("effect_authorization_revalidated");
260
+ if (this.#modelResultDigest === undefined || state !== "authorized")
261
+ fail("invalid_event");
262
+ if (this.#effect === undefined || attemptCount !== this.#effect.attemptCount
263
+ || reconciliationCount !== this.#effect.reconciliationCount)
264
+ fail("invalid_event");
265
+ this.#updateEffect(effectId, intentDigest, effectVersion, state, attemptCount, reconciliationCount, true);
266
+ this.#phase = "effect_authorized";
267
+ }
268
+ recordEffectRevalidated(bundleId, valid) {
269
+ const beforeAuthorization = this.#phase === "bundle_ready"
270
+ && this.#modelResultDigest !== undefined && this.#effect?.state === "prepared";
271
+ if (!beforeAuthorization && this.#phase !== "effect_authorized")
272
+ fail("invalid_transition");
273
+ digest(bundleId);
274
+ if (typeof valid !== "boolean")
275
+ fail("invalid_event");
276
+ if (this.activeBundleId !== bundleId)
277
+ fail("identity_mismatch");
278
+ if (!valid) {
279
+ this.#enterQuarantine("invalidated");
280
+ }
281
+ else {
282
+ this.#phase = beforeAuthorization ? "effect_authorization_revalidated" : "effect_revalidated";
283
+ }
284
+ }
285
+ quarantineContext(bundleId, reason) {
286
+ digest(bundleId);
287
+ if (!WORKFLOW_QUARANTINE_REASONS.includes(reason))
288
+ fail("invalid_event");
289
+ if (["new", "finished", "replay_verified", "quarantined"].includes(this.#phase)) {
290
+ fail("invalid_transition");
291
+ }
292
+ if (this.activeBundleId !== bundleId)
293
+ fail("identity_mismatch");
294
+ this.#enterQuarantine(reason);
295
+ }
296
+ recordEffectDispatched(effectId, intentDigest, effectVersion, state, attemptCount, reconciliationCount) {
297
+ this.#requirePhase("effect_revalidated");
298
+ if (state !== "dispatching" && state !== "unknown" && !TERMINAL_EFFECT_STATES.has(state)) {
299
+ fail("invalid_event");
300
+ }
301
+ if (this.#effect === undefined || attemptCount !== this.#effect.attemptCount + 1
302
+ || reconciliationCount !== this.#effect.reconciliationCount)
303
+ fail("invalid_event");
304
+ this.#updateEffect(effectId, intentDigest, effectVersion, state, attemptCount, reconciliationCount, true);
305
+ this.#phase = effectPhase(state);
306
+ }
307
+ recordEffectObserved(effectId, intentDigest, effectVersion, state, attemptCount, reconciliationCount) {
308
+ const allowed = this.#phase === "effect_dispatching"
309
+ ? state === "dispatching" || state === "unknown" || TERMINAL_EFFECT_STATES.has(state)
310
+ : this.#phase === "effect_ambiguous"
311
+ && (state === "unknown" || state === "authorized_for_retry" || TERMINAL_EFFECT_STATES.has(state));
312
+ if (!allowed)
313
+ fail("invalid_transition");
314
+ if (this.#effect === undefined || attemptCount < this.#effect.attemptCount
315
+ || reconciliationCount < this.#effect.reconciliationCount
316
+ || state === "authorized_for_retry" && (attemptCount !== this.#effect.attemptCount
317
+ || reconciliationCount <= this.#effect.reconciliationCount))
318
+ fail("invalid_event");
319
+ this.#updateEffect(effectId, intentDigest, effectVersion, state, attemptCount, reconciliationCount, false);
320
+ this.#phase = effectPhase(state);
321
+ }
322
+ checkpointCycle() {
323
+ const effectComplete = this.#effect === undefined
324
+ ? this.#phase === "bundle_ready"
325
+ : this.#phase === "effect_settled" && TERMINAL_EFFECT_STATES.has(this.#effect.state);
326
+ if (!effectComplete || this.#modelResultDigest === undefined || this.#observationDigest === undefined) {
327
+ fail("invalid_transition");
328
+ }
329
+ if (this.#completedCycles.length >= MAX_WORKFLOW_REPLAY_CYCLES || this.#completedTurns >= 0xffff_ffff) {
330
+ fail("limit_exceeded");
331
+ }
332
+ const selected = this.#activeContext;
333
+ const materialization = this.#materialization;
334
+ const invocation = this.#invocation;
335
+ const outcomeRevision = this.#observationRevision;
336
+ if (selected === undefined || materialization === undefined || invocation === undefined
337
+ || outcomeRevision === undefined)
338
+ fail("invalid_transition");
339
+ this.#completedCycles.push({
340
+ planId: selected.planId,
341
+ bundleId: selected.bundleId,
342
+ contractDigest: selected.contractDigest,
343
+ ...(this.#selectedDelta === undefined ? {} : { selectedDelta: { ...this.#selectedDelta } }),
344
+ materializedBundleId: materialization.bundleId,
345
+ tokenizerFingerprint: materialization.tokenizerFingerprint,
346
+ materializerFingerprint: materialization.materializerFingerprint,
347
+ physicalInputTokens: materialization.physicalInputTokens,
348
+ invocationId: invocation.invocationId,
349
+ requestDigest: invocation.requestDigest,
350
+ idempotencyKeyDigest: invocation.idempotencyKeyDigest,
351
+ modelResultDigest: this.#modelResultDigest,
352
+ ...(this.#effect === undefined ? {} : { effect: { ...this.#effect } }),
353
+ outcomeDigest: this.#observationDigest,
354
+ outcomeRevision,
355
+ });
356
+ this.#completedTurns += 1;
357
+ this.#pendingContext = undefined;
358
+ this.#pendingDelta = undefined;
359
+ this.#selectedDelta = undefined;
360
+ this.#materialization = undefined;
361
+ this.#invocation = undefined;
362
+ this.#modelResultDigest = undefined;
363
+ this.#observationDigest = undefined;
364
+ this.#observationRevision = undefined;
365
+ this.#effect = undefined;
366
+ this.#phase = "checkpointed";
367
+ }
368
+ finish() {
369
+ this.#requirePhase("checkpointed");
370
+ if (this.#completedTurns === 0)
371
+ fail("invalid_transition");
372
+ this.#phase = "finished";
373
+ }
374
+ recordReplayVerified(decisionId, executionId, candidate) {
375
+ this.#requirePhase("finished");
376
+ digest(decisionId);
377
+ record(executionId);
378
+ const comparison = this.compareReplay(candidate);
379
+ if (!comparison.exactMatch)
380
+ fail("identity_mismatch");
381
+ this.#replayVerified = true;
382
+ this.#phase = "replay_verified";
383
+ return comparison;
384
+ }
385
+ #requirePhase(...allowed) {
386
+ if (!allowed.includes(this.#phase))
387
+ fail("invalid_transition");
388
+ }
389
+ #updateEffect(effectId, intentDigest, effectVersion, state, attemptCount, reconciliationCount, requireNewVersion) {
390
+ record(effectId);
391
+ digest(intentDigest);
392
+ positiveBigint(effectVersion);
393
+ if (!count(attemptCount) || !count(reconciliationCount)
394
+ || !effectCountsValid(state, attemptCount, reconciliationCount))
395
+ fail("invalid_event");
396
+ const current = this.#effect;
397
+ if (current === undefined)
398
+ fail("invalid_transition");
399
+ const versionValid = effectVersion > current.effectVersion
400
+ || (!requireNewVersion && effectVersion === current.effectVersion && state === current.state);
401
+ if (effectId !== current.effectId || intentDigest !== current.intentDigest || !versionValid) {
402
+ fail("identity_mismatch");
403
+ }
404
+ this.#effect = { effectId, intentDigest, effectVersion, state, attemptCount, reconciliationCount };
405
+ }
406
+ #enterQuarantine(reason) {
407
+ this.#pendingContext = undefined;
408
+ this.#pendingDelta = undefined;
409
+ this.#selectedDelta = undefined;
410
+ this.#materialization = undefined;
411
+ this.#invocation = undefined;
412
+ this.#modelResultDigest = undefined;
413
+ this.#observationDigest = undefined;
414
+ this.#observationRevision = undefined;
415
+ this.#effect = undefined;
416
+ this.#replayVerified = false;
417
+ this.#quarantineReason = reason;
418
+ this.#phase = "quarantined";
419
+ }
420
+ }
421
+ function cloneReplayIdentity(identity) {
422
+ return {
423
+ cycles: identity.cycles.map((cycle) => ({
424
+ ...cycle,
425
+ ...(cycle.selectedDelta === undefined ? {} : { selectedDelta: { ...cycle.selectedDelta } }),
426
+ ...(cycle.effect === undefined ? {} : { effect: { ...cycle.effect } }),
427
+ })),
428
+ };
429
+ }
430
+ function validateReplayIdentity(identity) {
431
+ if (typeof identity !== "object" || identity === null || !Array.isArray(identity.cycles)
432
+ || identity.cycles.length === 0 || identity.cycles.length > MAX_WORKFLOW_REPLAY_CYCLES) {
433
+ fail("invalid_event");
434
+ }
435
+ for (const cycle of identity.cycles) {
436
+ record(cycle.planId);
437
+ digest(cycle.bundleId);
438
+ digest(cycle.contractDigest);
439
+ digest(cycle.materializedBundleId);
440
+ digest(cycle.tokenizerFingerprint);
441
+ digest(cycle.materializerFingerprint);
442
+ record(cycle.invocationId);
443
+ digest(cycle.requestDigest);
444
+ digest(cycle.idempotencyKeyDigest);
445
+ digest(cycle.modelResultDigest);
446
+ digest(cycle.outcomeDigest);
447
+ if (!positiveInteger(cycle.physicalInputTokens, 0xffff_ffff))
448
+ fail("invalid_event");
449
+ positiveBigint(cycle.outcomeRevision);
450
+ if (cycle.selectedDelta !== undefined) {
451
+ digest(cycle.selectedDelta.baseBundleId);
452
+ digest(cycle.selectedDelta.targetBundleId);
453
+ digest(cycle.selectedDelta.deltaDigest);
454
+ if (cycle.selectedDelta.targetBundleId !== cycle.bundleId
455
+ || cycle.selectedDelta.baseBundleId !== cycle.materializedBundleId
456
+ || cycle.selectedDelta.baseBundleId === cycle.selectedDelta.targetBundleId) {
457
+ fail("identity_mismatch");
458
+ }
459
+ }
460
+ if (cycle.effect !== undefined) {
461
+ record(cycle.effect.effectId);
462
+ digest(cycle.effect.intentDigest);
463
+ positiveBigint(cycle.effect.effectVersion);
464
+ if (!TERMINAL_EFFECT_STATES.has(cycle.effect.state) || !count(cycle.effect.attemptCount)
465
+ || !count(cycle.effect.reconciliationCount)
466
+ || !effectCountsValid(cycle.effect.state, cycle.effect.attemptCount, cycle.effect.reconciliationCount))
467
+ fail("invalid_event");
468
+ }
469
+ }
470
+ }
471
+ function compareWorkflowReplay(baseline, candidate) {
472
+ const sameLength = baseline.cycles.length === candidate.cycles.length;
473
+ const pairs = baseline.cycles.map((cycle, index) => [cycle, candidate.cycles[index]]);
474
+ const bundleDeltaSelection = comparisonStatus(sameLength && pairs.every(([left, right]) => left.planId === right.planId && left.bundleId === right.bundleId
475
+ && left.contractDigest === right.contractDigest && equalDelta(left.selectedDelta, right.selectedDelta)));
476
+ const materialization = comparisonStatus(sameLength && pairs.every(([left, right]) => left.materializedBundleId === right.materializedBundleId
477
+ && left.tokenizerFingerprint === right.tokenizerFingerprint
478
+ && left.materializerFingerprint === right.materializerFingerprint
479
+ && left.physicalInputTokens === right.physicalInputTokens));
480
+ const modelResultIdentity = comparisonStatus(sameLength && pairs.every(([left, right]) => left.invocationId === right.invocationId && left.requestDigest === right.requestDigest
481
+ && left.idempotencyKeyDigest === right.idempotencyKeyDigest
482
+ && left.modelResultDigest === right.modelResultDigest));
483
+ const toolEffectDecisions = comparisonStatus(sameLength && pairs.every(([left, right]) => equalEffect(left.effect, right.effect)));
484
+ const outcome = comparisonStatus(sameLength && pairs.every(([left, right]) => left.outcomeDigest === right.outcomeDigest && left.outcomeRevision === right.outcomeRevision));
485
+ return {
486
+ bundleDeltaSelection,
487
+ materialization,
488
+ modelResultIdentity,
489
+ toolEffectDecisions,
490
+ outcome,
491
+ exactMatch: [bundleDeltaSelection, materialization, modelResultIdentity, toolEffectDecisions, outcome]
492
+ .every((status) => status === "equal"),
493
+ };
494
+ }
495
+ function comparisonStatus(equal) {
496
+ return equal ? "equal" : "different";
497
+ }
498
+ function equalDelta(left, right) {
499
+ return left === undefined || right === undefined
500
+ ? left === right
501
+ : left.baseBundleId === right.baseBundleId && left.targetBundleId === right.targetBundleId
502
+ && left.deltaDigest === right.deltaDigest;
503
+ }
504
+ function equalEffect(left, right) {
505
+ return left === undefined || right === undefined
506
+ ? left === right
507
+ : left.effectId === right.effectId && left.intentDigest === right.intentDigest
508
+ && left.effectVersion === right.effectVersion && left.state === right.state
509
+ && left.attemptCount === right.attemptCount && left.reconciliationCount === right.reconciliationCount;
510
+ }
511
+ function fail(code) { throw new WorkflowSessionError(code); }
512
+ function digest(value) { if (!DIGEST.test(value))
513
+ fail("invalid_event"); }
514
+ function record(value) { if (!UUID_V7.test(value))
515
+ fail("invalid_event"); }
516
+ function positiveBigint(value) {
517
+ if (typeof value !== "bigint" || value <= 0n || value > 0xffffffffffffffffn)
518
+ fail("invalid_event");
519
+ }
520
+ function positiveInteger(value, maximum) {
521
+ return Number.isSafeInteger(value) && value > 0 && value <= maximum;
522
+ }
523
+ function count(value) { return Number.isInteger(value) && value >= 0 && value <= 0xffff_ffff; }
524
+ function effectCountsValid(state, attempts, reconciliations) {
525
+ if (reconciliations !== 0 && attempts === 0)
526
+ return false;
527
+ if (["prepared", "authorized", "rejected"].includes(state)) {
528
+ return attempts === 0 && reconciliations === 0;
529
+ }
530
+ if ([
531
+ "dispatching", "succeeded", "failed", "unknown", "compensated", "compensation_failed",
532
+ ].includes(state))
533
+ return attempts !== 0;
534
+ if (state === "authorized_for_retry" || state === "manual_resolution") {
535
+ return attempts !== 0 && reconciliations !== 0;
536
+ }
537
+ return state === "expired" || state === "cancelled";
538
+ }
539
+ function effectPhase(state) {
540
+ if (state === "dispatching")
541
+ return "effect_dispatching";
542
+ if (state === "unknown")
543
+ return "effect_ambiguous";
544
+ if (state === "authorized_for_retry")
545
+ return "effect_authorized";
546
+ if (TERMINAL_EFFECT_STATES.has(state))
547
+ return "effect_settled";
548
+ return fail("invalid_event");
549
+ }
550
+ //# sourceMappingURL=workflow-session.js.map