@orkestrel/workflow 0.0.8 → 0.0.10

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.
@@ -1,83 +1,9 @@
1
- import { arrayShape, attempt, cloneJSONRecord, cloneJSONValue, compileGuard, createContract, integerShape, isArray, isBoolean, isContractError, isFiniteNumber, isInteger, isJSONValue, isNonEmptyString, isRecord, literalShape, objectShape, optionalShape, rawShape, stringShape } from "@orkestrel/contract";
1
+ import { createAbort, linkSignal } from "@orkestrel/abort";
2
+ import { arrayShape, attempt, cloneJSONRecord, cloneJSONValue, compileGuard, createContract, integerShape, isArray, isBoolean, isContractError, isFiniteNumber, isFunction, isInteger, isJSONValue, isNonEmptyString, isRecord, literalShape, objectShape, optionalShape, rawShape, stringShape } from "@orkestrel/contract";
2
3
  import { createDatabase, createMemoryDriver } from "@orkestrel/database";
3
- import { createAbort } from "@orkestrel/abort";
4
4
  import { Emitter } from "@orkestrel/emitter";
5
5
  import { createTimeout } from "@orkestrel/timeout";
6
6
  import { createQueue } from "@orkestrel/queue";
7
- //#region src/core/Scheduler.ts
8
- /**
9
- * The safe cross-environment cooperative-yield default — a {@link SchedulerInterface}
10
- * built on `setTimeout` / `clearTimeout` alone, so it runs unchanged in both the
11
- * browser and Node.
12
- *
13
- * @remarks
14
- * - **Cross-environment.** Uses ONLY `setTimeout` / `clearTimeout` — universally
15
- * available. It deliberately avoids env-specific fast paths (`setImmediate`,
16
- * `scheduler.yield`, `requestAnimationFrame`, `node:timers/promises`,
17
- * `MessageChannel`); those belong to the environment backends, built with the
18
- * agent loop that consumes them.
19
- * - **`yield` is a macrotask host-turn, not a microtask.** `yield()` waits on a
20
- * `setTimeout(0)`, NOT `queueMicrotask`. A microtask drains before the host
21
- * regains control, so it would not actually let pending I/O, timers, or
22
- * rendering run — it only defers within the current task. A zero-delay timer is
23
- * the correct cross-environment "give the host a turn".
24
- * - **Abort-aware.** A pending `yield` / `delay` rejects with `signal.reason` when
25
- * the signal aborts (the standard `AbortSignal` convention). An already-aborted
26
- * signal rejects immediately without arming a timer. Either settle path clears
27
- * the timer and removes the abort listener — no leaked timer, no leaked
28
- * listener, and no double-settle.
29
- * - **Priority is accepted but uniform.** `options.priority` is part of the
30
- * contract, but a `setTimeout`-based default cannot act on urgency, so it treats
31
- * every priority the same. Environment backends honour it.
32
- * - **Event-free.** A pure functional primitive — no Emitter, no events.
33
- *
34
- * @example
35
- * ```ts
36
- * const scheduler = new Scheduler()
37
- * while (!signal.aborted) {
38
- * doSomeWork()
39
- * await scheduler.yield({ signal }) // let the host run between work units
40
- * }
41
- * ```
42
- */
43
- var Scheduler = class {
44
- /**
45
- * Yield control back to the host so other tasks (I/O, timers, rendering) can
46
- * run, then resume — a macrotask turn via `setTimeout(0)` (NOT a microtask,
47
- * which would resume before the host regains control).
48
- */
49
- yield(options) {
50
- return this.#sleep(0, options?.signal);
51
- }
52
- /**
53
- * Resume after at least `ms` milliseconds; abort rejects with `signal.reason`.
54
- *
55
- * @remarks
56
- * `ms` should be a non-negative finite number. The primitive stays minimal and
57
- * does no validation: it passes `ms` straight to the host `setTimeout`, which
58
- * clamps a negative value or `NaN` to ~0 — so an out-of-domain `ms` resolves on
59
- * the next host turn rather than throwing.
60
- */
61
- delay(ms, options) {
62
- return this.#sleep(ms, options?.signal);
63
- }
64
- #sleep(ms, signal) {
65
- if (signal?.aborted === true) return Promise.reject(signal.reason);
66
- return new Promise((resolve, reject) => {
67
- const handle = setTimeout(() => {
68
- signal?.removeEventListener("abort", onAbort);
69
- resolve();
70
- }, ms);
71
- const onAbort = this.#abort.bind(this, handle, reject, signal);
72
- signal?.addEventListener("abort", onAbort, { once: true });
73
- });
74
- }
75
- #abort(handle, reject, signal) {
76
- clearTimeout(handle);
77
- reject(signal?.reason);
78
- }
79
- };
80
- //#endregion
81
7
  //#region src/core/constants.ts
82
8
  /** The default {@link import('./types.js').WorkflowDefinition.bail} — graceful (continue on a leaf failure). */
83
9
  var DEFAULT_BAIL = false;
@@ -183,50 +109,42 @@ var DEFAULT_PHASE_CONCURRENCY = 1024;
183
109
  */
184
110
  var MAX_TIMER_MS = 2147483647;
185
111
  //#endregion
186
- //#region src/core/errors.ts
112
+ //#region src/core/helpers.ts
187
113
  /**
188
- * An error raised by the workflow runtime.
114
+ * Capture every top-level {@link WorkflowOptions} value exactly once into an owned plain bag.
189
115
  *
190
116
  * @remarks
191
- * Carries a {@link WorkflowErrorCode} and an optional `context` bag naming the
192
- * offending node id / status. Raised for an illegal lifecycle transition
193
- * (`TRANSITION`), a structurally invalid {@link import('./types.js').WorkflowSnapshot}
194
- * boundary (`RESTORE`), or a refused structural/activity edit (`MUTATION`).
195
- */
196
- var WorkflowError = class extends Error {
197
- code;
198
- context;
199
- constructor(code, message, context) {
200
- super(message);
201
- this.name = "WorkflowError";
202
- this.code = code;
203
- if (context !== void 0) this.context = context;
204
- }
205
- };
206
- /**
207
- * Narrow an unknown caught value to a {@link WorkflowError}.
117
+ * Direct property reads preserve inherited and non-enumerable option values while preventing
118
+ * accessor-backed caller bags from shifting policy, handlers, hooks, or nested options between
119
+ * construction stages. Nested bags and the functions registry retain their original identities so
120
+ * entity constructors can snapshot keyed child options and live additions can resolve against the
121
+ * same registry.
208
122
  *
209
- * @param value - The value to test (typically a `catch` binding)
210
- * @returns `true` when `value` is a {@link WorkflowError}
123
+ * @param options - The caller-owned workflow construction options
124
+ * @returns An owned top-level options bag containing the captured values
211
125
  *
212
126
  * @example
213
127
  * ```ts
214
- * try {
215
- * task.complete('done')
216
- * } catch (error) {
217
- * if (isWorkflowError(error) && error.code === 'TRANSITION') retry()
218
- * }
128
+ * const captured = captureWorkflowOptions(options)
129
+ * const workflow = createWorkflow(definition, captured)
219
130
  * ```
220
131
  */
221
- function isWorkflowError(value) {
222
- try {
223
- return value instanceof WorkflowError;
224
- } catch {
225
- return false;
226
- }
132
+ function captureWorkflowOptions(options) {
133
+ const on = options?.on;
134
+ const bail = options?.bail;
135
+ const error = options?.error;
136
+ const phases = options?.phases;
137
+ const functions = options?.functions;
138
+ const silence = options?.silence;
139
+ return Object.freeze({
140
+ ...on === void 0 ? {} : { on },
141
+ ...bail === void 0 ? {} : { bail },
142
+ ...error === void 0 ? {} : { error },
143
+ ...phases === void 0 ? {} : { phases },
144
+ ...functions === void 0 ? {} : { functions },
145
+ ...silence === void 0 ? {} : { silence }
146
+ });
227
147
  }
228
- //#endregion
229
- //#region src/core/helpers.ts
230
148
  /**
231
149
  * Test whether a {@link LifecycleStatus} is TERMINAL — a node in this state will not
232
150
  * transition further.
@@ -750,6 +668,61 @@ function createDeferred() {
750
668
  return Promise.withResolvers();
751
669
  }
752
670
  /**
671
+ * Schedule one cancellable host operation behind an owned settlement signal.
672
+ *
673
+ * @remarks
674
+ * The completion and failure paths each own an {@link AbortController}; their native composite is
675
+ * linked to the optional caller signal before `start` can arm host work. Scheduler backends attach
676
+ * only to that safe composite, so caller mutation of `addEventListener` or `removeEventListener`
677
+ * cannot strand the operation. The first completion resolves, the first host failure rejects with
678
+ * its exact value, and caller abort rejects with its exact linked reason. Caller abort and host
679
+ * failure cancel an armed handle; synchronous settlement also cancels the handle immediately after
680
+ * `start` returns it. Cancellation is secondary cleanup: if its closure throws, the already-winning
681
+ * completion, exact host failure, or exact caller reason still settles without escape or replacement.
682
+ *
683
+ * @param start - Arm host work and return its cancellation closure
684
+ * @param signal - Optional caller cancellation signal
685
+ * @returns A promise settled exactly once by completion, host failure, or caller abort
686
+ */
687
+ function scheduleHost(start, signal) {
688
+ const completion = new AbortController();
689
+ const failed = new AbortController();
690
+ let settled;
691
+ try {
692
+ settled = linkSignal(AbortSignal.any([completion.signal, failed.signal]), signal);
693
+ } catch (error) {
694
+ return Promise.reject(error);
695
+ }
696
+ if (settled.aborted) return Promise.reject(settled.reason);
697
+ return new Promise((resolve, reject) => {
698
+ let cancel;
699
+ let hostFailure;
700
+ settled.addEventListener("abort", () => {
701
+ if (completion.signal.aborted) {
702
+ resolve();
703
+ return;
704
+ }
705
+ const reason = failed.signal.aborted ? hostFailure : settled.reason;
706
+ try {
707
+ cancel?.();
708
+ } catch {}
709
+ reject(reason);
710
+ }, { once: true });
711
+ try {
712
+ cancel = start(() => completion.abort(), (error) => {
713
+ hostFailure = error;
714
+ failed.abort();
715
+ });
716
+ } catch (error) {
717
+ hostFailure = error;
718
+ failed.abort();
719
+ }
720
+ if (settled.aborted) try {
721
+ cancel?.();
722
+ } catch {}
723
+ });
724
+ }
725
+ /**
753
726
  * Park until `signal` aborts — a promise-parked wait (AGENTS §21), never a timer or
754
727
  * busy-loop, that NEVER rejects.
755
728
  *
@@ -777,6 +750,113 @@ function parkSignal(signal) {
777
750
  });
778
751
  }
779
752
  //#endregion
753
+ //#region src/core/Scheduler.ts
754
+ /**
755
+ * The safe cross-environment cooperative-yield default — a {@link SchedulerInterface}
756
+ * built on `setTimeout` / `clearTimeout` alone, so it runs unchanged in both the
757
+ * browser and Node.
758
+ *
759
+ * @remarks
760
+ * - **Cross-environment.** Uses ONLY `setTimeout` / `clearTimeout` — universally
761
+ * available. It deliberately avoids env-specific fast paths (`setImmediate`,
762
+ * `scheduler.yield`, `requestAnimationFrame`, `node:timers/promises`,
763
+ * `MessageChannel`); those belong to the environment backends, built with the
764
+ * agent loop that consumes them.
765
+ * - **`yield` is a macrotask host-turn, not a microtask.** `yield()` waits on a
766
+ * `setTimeout(0)`, NOT `queueMicrotask`. A microtask drains before the host
767
+ * regains control, so it would not actually let pending I/O, timers, or
768
+ * rendering run — it only defers within the current task. A zero-delay timer is
769
+ * the correct cross-environment "give the host a turn".
770
+ * - **Abort-aware.** A pending `yield` / `delay` rejects with `signal.reason` exactly.
771
+ * {@link scheduleHost} links an owned settlement composite to the caller before arming
772
+ * the timer, so pre-abort schedules nothing, caller signal method mutation is harmless,
773
+ * cancellation clears the handle, and native first-settlement wins exactly once.
774
+ * - **Priority is accepted but uniform.** `options.priority` is part of the
775
+ * contract, but a `setTimeout`-based default cannot act on urgency, so it treats
776
+ * every priority the same. Environment backends honour it.
777
+ * - **Event-free.** A pure functional primitive — no Emitter, no events.
778
+ *
779
+ * @example
780
+ * ```ts
781
+ * const scheduler = new Scheduler()
782
+ * while (!signal.aborted) {
783
+ * doSomeWork()
784
+ * await scheduler.yield({ signal }) // let the host run between work units
785
+ * }
786
+ * ```
787
+ */
788
+ var Scheduler = class {
789
+ /**
790
+ * Yield control back to the host so other tasks (I/O, timers, rendering) can
791
+ * run, then resume — a macrotask turn via `setTimeout(0)` (NOT a microtask,
792
+ * which would resume before the host regains control).
793
+ */
794
+ yield(options) {
795
+ return this.#sleep(0, options?.signal);
796
+ }
797
+ /**
798
+ * Resume after at least `ms` milliseconds; abort rejects with `signal.reason`.
799
+ *
800
+ * @remarks
801
+ * `ms` should be a non-negative finite number. The primitive stays minimal and
802
+ * does no validation: it passes `ms` straight to the host `setTimeout`, which
803
+ * clamps a negative value or `NaN` to ~0 — so an out-of-domain `ms` resolves on
804
+ * the next host turn rather than throwing.
805
+ */
806
+ delay(ms, options) {
807
+ return this.#sleep(ms, options?.signal);
808
+ }
809
+ #sleep(ms, signal) {
810
+ return scheduleHost((complete) => {
811
+ const handle = setTimeout(complete, ms);
812
+ return () => clearTimeout(handle);
813
+ }, signal);
814
+ }
815
+ };
816
+ //#endregion
817
+ //#region src/core/errors.ts
818
+ /**
819
+ * An error raised by the workflow runtime.
820
+ *
821
+ * @remarks
822
+ * Carries a {@link WorkflowErrorCode} and an optional `context` bag naming the
823
+ * offending node id / status. Raised for an illegal lifecycle transition
824
+ * (`TRANSITION`), a structurally invalid {@link import('./types.js').WorkflowSnapshot}
825
+ * boundary (`RESTORE`), or a refused structural/activity edit (`MUTATION`).
826
+ */
827
+ var WorkflowError = class extends Error {
828
+ code;
829
+ context;
830
+ constructor(code, message, context) {
831
+ super(message);
832
+ this.name = "WorkflowError";
833
+ this.code = code;
834
+ if (context !== void 0) this.context = context;
835
+ }
836
+ };
837
+ /**
838
+ * Narrow an unknown caught value to a {@link WorkflowError}.
839
+ *
840
+ * @param value - The value to test (typically a `catch` binding)
841
+ * @returns `true` when `value` is a {@link WorkflowError}
842
+ *
843
+ * @example
844
+ * ```ts
845
+ * try {
846
+ * task.complete('done')
847
+ * } catch (error) {
848
+ * if (isWorkflowError(error) && error.code === 'TRANSITION') retry()
849
+ * }
850
+ * ```
851
+ */
852
+ function isWorkflowError(value) {
853
+ try {
854
+ return value instanceof WorkflowError;
855
+ } catch {
856
+ return false;
857
+ }
858
+ }
859
+ //#endregion
780
860
  //#region src/core/validators.ts
781
861
  /** Test the workflow lifecycle vocabulary. */
782
862
  function isLifecycleStatus(value) {
@@ -868,9 +948,17 @@ function isWorkflowSnapshot(value) {
868
948
  const cloned = attempt(() => cloneJSONValue(value));
869
949
  return cloned.success && isOwnedWorkflowSnapshot(cloned.value);
870
950
  }
871
- /** Test that every present behavior reference resolves before dispatch. */
872
- function hasWorkflowHandlers(snapshot, functions) {
873
- for (const phase of snapshot.phases) for (const task of phase.tasks) if (task.run !== void 0 && functions?.[task.run] === void 0) return false;
951
+ function hasWorkflowHandlers(workflow, functions) {
952
+ if ("destroyed" in workflow) {
953
+ for (const phase of workflow.phases.phases()) for (const task of phase.tasks.tasks()) if (task.run !== void 0 && !isFunction(task.handler)) return false;
954
+ return true;
955
+ }
956
+ const runs = /* @__PURE__ */ new Set();
957
+ for (const phase of workflow.phases) for (const task of phase.tasks) {
958
+ if (task.run === void 0 || runs.has(task.run)) continue;
959
+ runs.add(task.run);
960
+ if (!isFunction(functions?.[task.run])) return false;
961
+ }
874
962
  return true;
875
963
  }
876
964
  /** Locate the nearest identifiable node for an inconsistent owned snapshot. */
@@ -974,18 +1062,25 @@ function isTaskActivity(value) {
974
1062
  * Validate and own a workflow snapshot before live construction.
975
1063
  *
976
1064
  * @param input - The hostile snapshot boundary
1065
+ * @param id - The optional storage key the owned snapshot must match
977
1066
  * @returns A deeply owned frozen snapshot
1067
+ * @throws {WorkflowError} With `RESTORE` when the snapshot is invalid or does not match `id`
978
1068
  */
979
- function cloneWorkflowSnapshot(input) {
1069
+ function cloneWorkflowSnapshot(input, id) {
1070
+ let cloned;
980
1071
  try {
981
- const cloned = cloneJSONValue(input);
982
- if (!isOwnedWorkflowSnapshot(cloned)) throw new WorkflowError("RESTORE", "workflow snapshot is inconsistent", workflowSnapshotContext(cloned));
983
- return cloned;
1072
+ cloned = cloneJSONValue(input);
984
1073
  } catch (error) {
985
1074
  if (isWorkflowError(error)) throw error;
986
1075
  if (isContractError(error)) throw new WorkflowError("RESTORE", `workflow snapshot could not be read safely: ${error.message}`);
987
1076
  throw new WorkflowError("RESTORE", "workflow snapshot could not be read safely");
988
1077
  }
1078
+ if (!isOwnedWorkflowSnapshot(cloned)) throw new WorkflowError("RESTORE", "workflow snapshot is inconsistent", workflowSnapshotContext(cloned));
1079
+ if (id !== void 0 && cloned.id !== id) throw new WorkflowError("RESTORE", `workflow snapshot '${cloned.id}' does not match storage key '${id}'`, {
1080
+ requested: id,
1081
+ payload: cloned.id
1082
+ });
1083
+ return cloned;
989
1084
  }
990
1085
  /**
991
1086
  * Validate and clone one complete task activity frame.
@@ -1210,7 +1305,8 @@ var phaseUpdateShape = objectShape({
1210
1305
  * the row `{ id: snapshot.id, snapshot }`.
1211
1306
  * - **`get(id)` resolves the stored snapshot for an id**, narrowing the opaque JSON column back to
1212
1307
  * a {@link WorkflowSnapshot} ({@link import('../helpers.js').isWorkflowSnapshot} — the AGENTS §14
1213
- * boundary narrow for an untrusted storage read), or `undefined` if none is stored.
1308
+ * boundary narrow for an untrusted storage read), or `undefined` if none is stored. A present
1309
+ * snapshot whose own id differs from the requested key rejects with normalized `RESTORE` evidence.
1214
1310
  * - **`delete(id)` drops a snapshot by id**; an absent id is a no-op (no throw).
1215
1311
  *
1216
1312
  * UNLIKE the server package's `SessionStoreInterface` there is NO
@@ -1221,7 +1317,8 @@ var phaseUpdateShape = objectShape({
1221
1317
  *
1222
1318
  * @example
1223
1319
  * ```ts
1224
- * import { createDatabaseWorkflowStore, createMemoryDriver, createWorkflow, restoreWorkflow } from '@src/core'
1320
+ * import { createMemoryDriver } from '@orkestrel/database'
1321
+ * import { createDatabaseWorkflowStore, createWorkflow, restoreWorkflow } from '@orkestrel/workflow'
1225
1322
  *
1226
1323
  * const store = createDatabaseWorkflowStore(createMemoryDriver()) // a durable driver swaps in here
1227
1324
  * const workflow = createWorkflow(definition)
@@ -1242,11 +1339,11 @@ var DatabaseWorkflowStore = class {
1242
1339
  constructor(table) {
1243
1340
  this.#table = table;
1244
1341
  }
1245
- /** Resolve the persisted snapshot for `id`, narrowing the opaque JSON column back to a `WorkflowSnapshot`. */
1342
+ /** Resolve and key-check the snapshot for `id`, narrowing the opaque column to `WorkflowSnapshot`. */
1246
1343
  async get(id) {
1247
1344
  const row = await this.#table.get(id);
1248
1345
  if (row === void 0) return void 0;
1249
- return cloneWorkflowSnapshot(row.snapshot);
1346
+ return cloneWorkflowSnapshot(row.snapshot, id);
1250
1347
  }
1251
1348
  /** Insert or replace under the snapshot's OWN `id` (no separate id param) — the row is `{ id, snapshot }`. */
1252
1349
  async set(snapshot) {
@@ -1291,7 +1388,7 @@ var DatabaseWorkflowStore = class {
1291
1388
  *
1292
1389
  * @example
1293
1390
  * ```ts
1294
- * import { createMemoryWorkflowStore, createWorkflow, restoreWorkflow } from '@src/core'
1391
+ * import { createMemoryWorkflowStore, createWorkflow, restoreWorkflow } from '@orkestrel/workflow'
1295
1392
  *
1296
1393
  * const store = createMemoryWorkflowStore()
1297
1394
  * const workflow = createWorkflow(definition)
@@ -1380,14 +1477,18 @@ var Task = class {
1380
1477
  this.#workflow = workflow;
1381
1478
  this.#recompute = recompute;
1382
1479
  try {
1383
- this.#metadata = cloneJSONRecord(options?.metadata ?? metadata);
1480
+ const metadataOption = options?.metadata;
1481
+ this.#metadata = cloneJSONRecord(metadataOption ?? metadata);
1384
1482
  } catch (error) {
1385
1483
  if (isContractError(error)) throw new WorkflowError("RESTORE", `task '${context.id}' metadata could not be read safely: ${error.message}`, { task: context.id });
1386
1484
  throw new WorkflowError("RESTORE", `task '${context.id}' metadata could not be read safely`, { task: context.id });
1387
1485
  }
1486
+ const on = options?.on;
1487
+ const listenerError = options?.error;
1488
+ const silenceOption = options?.silence;
1388
1489
  this.#emitter = new Emitter({
1389
- ...options?.on === void 0 ? {} : { on: options.on },
1390
- ...options?.error === void 0 ? {} : { error: options.error }
1490
+ ...on === void 0 ? {} : { on },
1491
+ ...listenerError === void 0 ? {} : { error: listenerError }
1391
1492
  });
1392
1493
  this.#status = status;
1393
1494
  this.#result = result;
@@ -1402,7 +1503,7 @@ var Task = class {
1402
1503
  this.#attempts = attempts;
1403
1504
  this.#handler = handler;
1404
1505
  this.#abort = createAbort();
1405
- this.#silence = resolveTaskSilence(options?.silence, silence);
1506
+ this.#silence = resolveTaskSilence(silenceOption, silence);
1406
1507
  this.#onSilence = this.#expire.bind(this);
1407
1508
  this.#liveness = this.#silence === void 0 ? void 0 : createTimeout({
1408
1509
  ms: this.#silence,
@@ -1760,9 +1861,9 @@ var TaskManager = class {
1760
1861
  *
1761
1862
  * @remarks
1762
1863
  * - **Derived status.** `status` is `#override` when one is in force, else
1763
- * {@link derivePhaseStatus} over the live tasks' statuses. {@link #recompute} (passed to
1864
+ * {@link derivePhaseStatus} over the live tasks' statuses. `#recompute` (passed to
1764
1865
  * each child {@link Task}) re-derives on every child transition; a CHANGE emits the matching
1765
- * event AND escalates to the workflow ({@link #escalate}, the upward step of the cascade).
1866
+ * event AND escalates to the workflow (`#escalate`, the upward step of the cascade).
1766
1867
  * - **Override (AGENTS §10).** `skip` / `stop` FORCE the phase's status (e.g. skipping a whole
1767
1868
  * phase), overriding the derived value; the override is PERSISTED in the snapshot's own
1768
1869
  * `override` field and restored DIRECTLY (no divergence guess), so a forced phase round-trips.
@@ -1793,10 +1894,12 @@ var TaskManager = class {
1793
1894
  * construction path {@link #append} uses at build time, so a live mint and a restored/built
1794
1895
  * task are wired IDENTICALLY. At construction, the workflow-level
1795
1896
  * {@link import('../types.js').WorkflowFunctions} registry (threaded from
1796
- * {@link import('../types.js').WorkflowOptions.functions}) resolves each task's `run` name into
1797
- * its runtime {@link import('../types.js').TaskInterface.handler} ONCE; a `run` that is omitted
1798
- * or unregistered resolves to no handler; only an omitted `run` is a no-op, while an
1799
- * unresolved present name makes the containing tree non-drivable.
1897
+ * {@link import('../types.js').WorkflowOptions.functions}) resolves every unique initial `run`
1898
+ * name ONCE before any task is built; siblings sharing a name receive the exact same captured
1899
+ * runtime {@link import('../types.js').TaskInterface.handler}. A later live {@link add} reads
1900
+ * that name once from the retained registry at its own mint moment. An omitted or unregistered
1901
+ * `run` resolves to no handler; only omission is a no-op, while an unresolved present name makes
1902
+ * the containing tree non-drivable.
1800
1903
  * - **Runtime lifecycle (AGENTS §10).** `pause` / `resume` / `wait` mirror the workflow's own
1801
1904
  * quartet, scoped to this phase — a driving
1802
1905
  * {@link import('../types.js').WorkflowRunnerInterface.execute} gates a task's own
@@ -1822,6 +1925,9 @@ var Phase = class {
1822
1925
  #paused;
1823
1926
  #gate;
1824
1927
  constructor(snapshot, workflow, escalate, options, bail, functions, silence) {
1928
+ const on = options?.on;
1929
+ const error = options?.error;
1930
+ const tasks = options?.tasks;
1825
1931
  this.#id = snapshot.id;
1826
1932
  this.#name = snapshot.name;
1827
1933
  if (snapshot.description !== void 0) Object.defineProperty(this, "description", {
@@ -1832,13 +1938,19 @@ var Phase = class {
1832
1938
  this.#escalateUp = escalate;
1833
1939
  this.#functions = functions;
1834
1940
  this.#silence = silence;
1941
+ const handlers = /* @__PURE__ */ new Map();
1942
+ for (const task of snapshot.tasks) if (task.run !== void 0 && !handlers.has(task.run)) handlers.set(task.run, functions?.[task.run]);
1835
1943
  this.#bail = bail ?? snapshot.bail;
1836
1944
  this.#concurrency = snapshot.concurrency;
1837
1945
  this.#emitter = new Emitter({
1838
- ...options?.on === void 0 ? {} : { on: options.on },
1839
- ...options?.error === void 0 ? {} : { error: options.error }
1946
+ ...on === void 0 ? {} : { on },
1947
+ ...error === void 0 ? {} : { error }
1840
1948
  });
1841
- for (const task of snapshot.tasks) this.#append(task, options);
1949
+ for (const task of snapshot.tasks) {
1950
+ const taskOptions = tasks?.[task.id];
1951
+ const handler = task.run === void 0 ? void 0 : handlers.get(task.run);
1952
+ this.#append(task, taskOptions, handler);
1953
+ }
1842
1954
  this.#override = snapshot.override;
1843
1955
  this.#status = this.status;
1844
1956
  this.#paused = false;
@@ -2020,17 +2132,17 @@ var Phase = class {
2020
2132
  if (result.success) this.#emitter.emit("add", result.value, at);
2021
2133
  return result;
2022
2134
  }
2023
- #append(task, options) {
2024
- const created = this.#create(task, options?.tasks?.[task.id]);
2135
+ #append(task, options, handler) {
2136
+ const created = this.#create(task, options, handler);
2025
2137
  this.#tasks.append(created);
2026
2138
  }
2027
- #create(snapshot, options) {
2028
- const context = buildTaskContext(this.context, snapshot);
2029
- const handler = snapshot.run === void 0 ? void 0 : this.#functions?.[snapshot.run];
2030
- return new Task(context, this, this.#workflow, () => this.#recompute(), options, snapshot.status, snapshot.result, snapshot.run, snapshot.retries, snapshot.timeout, snapshot.metadata, snapshot.attempts, snapshot.activity, handler, this.#silence);
2139
+ #create(snapshot, options, handler) {
2140
+ return new Task(buildTaskContext(this.context, snapshot), this, this.#workflow, () => this.#recompute(), options, snapshot.status, snapshot.result, snapshot.run, snapshot.retries, snapshot.timeout, snapshot.metadata, snapshot.attempts, snapshot.activity, handler, this.#silence);
2031
2141
  }
2032
2142
  #mint(definition) {
2033
- return this.#create(taskDefinitionToSnapshot(definition), void 0);
2143
+ const snapshot = taskDefinitionToSnapshot(definition);
2144
+ const handler = snapshot.run === void 0 ? void 0 : this.#functions?.[snapshot.run];
2145
+ return this.#create(snapshot, void 0, handler);
2034
2146
  }
2035
2147
  #statuses() {
2036
2148
  return this.#tasks.tasks().map((task) => task.status);
@@ -2125,11 +2237,11 @@ var PhaseManager = class {
2125
2237
  * - **Construction.** Built from a {@link WorkflowSnapshot} (the unified input —
2126
2238
  * {@link import('./factories.js').createWorkflow} seeds an initial snapshot from a
2127
2239
  * {@link import('./types.js').WorkflowDefinition}, {@link import('./factories.js').restoreWorkflow}
2128
- * passes a persisted one). Each child {@link Phase} is wired to escalate to {@link #recompute}.
2240
+ * passes a persisted one). Each child {@link Phase} is wired to escalate to `#recompute`.
2129
2241
  * - **Derived status.** `status` is `#override` when forced, else
2130
2242
  * {@link deriveWorkflowStatus} over the live phases' statuses feeding `bail`. `failed` is
2131
2243
  * reachable ONLY under `bail: true` (a single failed task halts the workflow); under
2132
- * `bail: false` a failed phase folds into `completed`. {@link #recompute} diffs on each phase
2244
+ * `bail: false` a failed phase folds into `completed`. `#recompute` diffs on each phase
2133
2245
  * change; a CHANGE emits.
2134
2246
  * - **Override (AGENTS §10).** `skip` / `stop` FORCE the status; an executed task-free pending tree
2135
2247
  * may also be force-completed vacuously. The override is PERSISTED in the snapshot's own
@@ -2178,15 +2290,22 @@ var Workflow = class {
2178
2290
  #gate;
2179
2291
  #destroyed;
2180
2292
  constructor(snapshot, options) {
2293
+ const captured = captureWorkflowOptions(options);
2294
+ const on = captured.on;
2295
+ const bail = captured.bail;
2296
+ const error = captured.error;
2297
+ const phases = captured.phases;
2298
+ const functions = captured.functions;
2299
+ const silence = captured.silence;
2181
2300
  this.#context = buildWorkflowContext(snapshot);
2182
2301
  if (snapshot.description !== void 0) Object.defineProperty(this, "description", { value: snapshot.description });
2183
- this.#bail = options?.bail ?? snapshot.bail;
2184
- this.#bailOverride = options?.bail;
2185
- this.#functions = options?.functions;
2186
- this.#silence = options?.silence;
2302
+ this.#bail = bail ?? snapshot.bail;
2303
+ this.#bailOverride = bail;
2304
+ this.#functions = functions;
2305
+ this.#silence = silence;
2187
2306
  this.#emitter = new Emitter({
2188
- ...options?.on === void 0 ? {} : { on: options.on },
2189
- ...options?.error === void 0 ? {} : { error: options.error }
2307
+ ...on === void 0 ? {} : { on },
2308
+ ...error === void 0 ? {} : { error }
2190
2309
  });
2191
2310
  this.#created = snapshot.created;
2192
2311
  this.#updated = snapshot.updated;
@@ -2194,7 +2313,10 @@ var Workflow = class {
2194
2313
  this.#paused = false;
2195
2314
  this.#gate = void 0;
2196
2315
  this.#destroyed = false;
2197
- for (const phase of snapshot.phases) this.#append(phase, options);
2316
+ for (const phase of snapshot.phases) {
2317
+ const phaseOptions = phases?.[phase.id];
2318
+ this.#append(phase, phaseOptions);
2319
+ }
2198
2320
  this.#override = snapshot.override;
2199
2321
  this.#status = this.status;
2200
2322
  }
@@ -2386,7 +2508,7 @@ var Workflow = class {
2386
2508
  return found;
2387
2509
  }
2388
2510
  #append(phase, options) {
2389
- const created = new Phase(phase, this, () => this.#recompute(), options?.phases?.[phase.id], this.#bailOverride, this.#functions, this.#silence);
2511
+ const created = new Phase(phase, this, () => this.#recompute(), options, this.#bailOverride, this.#functions, this.#silence);
2390
2512
  this.#phases.append(created);
2391
2513
  }
2392
2514
  #mint(definition) {
@@ -2418,12 +2540,11 @@ var Workflow = class {
2418
2540
  * `functions` registry in) and stores it under `definition.id` — an already-present id
2419
2541
  * OVERWRITES (last write wins). `count` is the map size, `workflow(id)` looks one up,
2420
2542
  * `workflows()` lists them in insertion order.
2421
- * - **Durable open / save.** `open(id)` returns an already-registered workflow directly; on a
2422
- * registry MISS with a `store` set it rehydrates through {@link restoreWorkflow} (flowing the
2423
- * manager's `functions` registry in so the rehydrated tree is RUNNABLE), registers it, and
2424
- * returns it lenient (`undefined`) with no store or a store miss. `save(id)` persists a
2425
- * registered workflow's `snapshot()` to the `store` — lenient (`false`) with no store or an
2426
- * unknown id.
2543
+ * - **Durable open / save.** `open(id)` returns an already-registered workflow directly; same-id
2544
+ * misses share one hydration. A concurrent `add` wins, while `remove` / `clear` invalidate
2545
+ * earlier reads; wrong-key payloads reject with `RESTORE`. `save(id)` captures a registered
2546
+ * workflow's snapshot at invocation and serializes same-id writes without coupling other ids.
2547
+ * Both remain lenient without a store or registered id.
2427
2548
  * - **Removal.** `remove` drops one by id, or a batch (§9.2, array overload FIRST) — `true` when
2428
2549
  * any was removed. `clear` empties the registry.
2429
2550
  * - **No active pointer.** Unlike its `ConversationManager` / `WorkspaceManager` twins, there is
@@ -2441,6 +2562,12 @@ var Workflow = class {
2441
2562
  */
2442
2563
  var WorkflowManager = class {
2443
2564
  #workflows = /* @__PURE__ */ new Map();
2565
+ #opens = /* @__PURE__ */ new Map();
2566
+ #saves = /* @__PURE__ */ new Map();
2567
+ #mutations = /* @__PURE__ */ new Map();
2568
+ #additions = /* @__PURE__ */ new Map();
2569
+ #hydrations = /* @__PURE__ */ new Map();
2570
+ #generation = Symbol();
2444
2571
  #functions;
2445
2572
  #store;
2446
2573
  constructor(options) {
@@ -2458,36 +2585,140 @@ var WorkflowManager = class {
2458
2585
  }
2459
2586
  add(definition) {
2460
2587
  const workflow = createWorkflow(definition, { ...this.#functions === void 0 ? {} : { functions: this.#functions } });
2588
+ const mutation = this.#invalidate(workflow.id);
2589
+ if (mutation === void 0) this.#additions.delete(workflow.id);
2590
+ else this.#additions.set(workflow.id, mutation);
2461
2591
  this.#workflows.set(workflow.id, workflow);
2462
2592
  return workflow;
2463
2593
  }
2464
- async open(id) {
2594
+ open(id) {
2465
2595
  const existing = this.#workflows.get(id);
2466
- if (existing !== void 0) return existing;
2467
- if (this.#store === void 0) return void 0;
2468
- const snapshot = await this.#store.get(id);
2469
- if (snapshot === void 0) return void 0;
2470
- const workflow = restoreWorkflow(snapshot, { ...this.#functions === void 0 ? {} : { functions: this.#functions } });
2471
- this.#workflows.set(workflow.id, workflow);
2472
- return workflow;
2473
- }
2474
- async save(id) {
2596
+ if (existing !== void 0) return Promise.resolve(existing);
2597
+ if (this.#store === void 0) return Promise.resolve(void 0);
2598
+ const pending = this.#opens.get(id);
2599
+ if (pending !== void 0) return pending;
2600
+ const mutation = this.#mutations.get(id);
2601
+ const generation = this.#generation;
2602
+ const lease = this.#retain(id);
2603
+ const reservation = Promise.withResolvers();
2604
+ const opening = reservation.promise;
2605
+ this.#opens.set(id, opening);
2606
+ this.#hydrate(id, mutation, generation, lease, this.#store).then(reservation.resolve, reservation.reject);
2607
+ opening.then(() => this.#releaseOpen(id, opening), () => this.#releaseOpen(id, opening));
2608
+ return opening;
2609
+ }
2610
+ save(id) {
2475
2611
  const workflow = this.#workflows.get(id);
2476
- if (this.#store === void 0 || workflow === void 0) return false;
2477
- await this.#store.set(workflow.snapshot());
2478
- return true;
2612
+ if (this.#store === void 0 || workflow === void 0) return Promise.resolve(false);
2613
+ const snapshot = workflow.snapshot();
2614
+ const previous = this.#saves.get(id);
2615
+ const reservation = Promise.withResolvers();
2616
+ const saving = reservation.promise;
2617
+ this.#saves.set(id, saving);
2618
+ this.#persist(this.#store, previous, snapshot).then(reservation.resolve, reservation.reject);
2619
+ saving.then(() => this.#settle(id, saving), () => this.#settle(id, saving));
2620
+ return saving.then(() => true);
2479
2621
  }
2480
2622
  remove(ids) {
2481
2623
  if (isArray(ids)) {
2482
2624
  let removed = false;
2483
- for (const id of ids) if (this.#workflows.delete(id)) removed = true;
2625
+ for (const id of ids) {
2626
+ this.#invalidate(id);
2627
+ this.#additions.delete(id);
2628
+ if (this.#workflows.delete(id)) removed = true;
2629
+ }
2484
2630
  return removed;
2485
2631
  }
2632
+ this.#invalidate(ids);
2633
+ this.#additions.delete(ids);
2486
2634
  return this.#workflows.delete(ids);
2487
2635
  }
2488
2636
  clear() {
2637
+ this.#generation = Symbol();
2638
+ this.#mutations.clear();
2639
+ this.#additions.clear();
2640
+ this.#opens.clear();
2489
2641
  this.#workflows.clear();
2490
2642
  }
2643
+ async #hydrate(id, mutation, generation, lease, store) {
2644
+ try {
2645
+ const snapshot = await store.get(id);
2646
+ if (!this.#owns(id, mutation, generation)) return this.#resolve(id, generation);
2647
+ if (snapshot === void 0) return void 0;
2648
+ let owned;
2649
+ try {
2650
+ owned = cloneWorkflowSnapshot(snapshot, id);
2651
+ } catch (error) {
2652
+ if (!this.#owns(id, mutation, generation)) return this.#resolve(id, generation);
2653
+ throw error;
2654
+ }
2655
+ if (!this.#owns(id, mutation, generation)) return this.#resolve(id, generation);
2656
+ let workflow;
2657
+ try {
2658
+ workflow = restoreWorkflow(owned, { ...this.#functions === void 0 ? {} : { functions: this.#functions } });
2659
+ } catch (error) {
2660
+ if (!this.#owns(id, mutation, generation)) return this.#resolve(id, generation);
2661
+ throw error;
2662
+ }
2663
+ if (!this.#owns(id, mutation, generation)) return this.#resolve(id, generation);
2664
+ return this.#register(id, workflow, mutation, generation);
2665
+ } finally {
2666
+ this.#releaseHydration(id, lease);
2667
+ }
2668
+ }
2669
+ #owns(id, mutation, generation) {
2670
+ return this.#generation === generation && this.#mutations.get(id) === mutation;
2671
+ }
2672
+ #resolve(id, generation) {
2673
+ if (this.#generation !== generation) return void 0;
2674
+ const mutation = this.#mutations.get(id);
2675
+ const workflow = this.#workflows.get(id);
2676
+ return workflow !== void 0 && this.#additions.get(id) === mutation ? workflow : void 0;
2677
+ }
2678
+ #register(id, workflow, mutation, generation) {
2679
+ if (!this.#owns(id, mutation, generation)) return this.#resolve(id, generation);
2680
+ this.#workflows.set(id, workflow);
2681
+ return workflow;
2682
+ }
2683
+ async #persist(store, previous, snapshot) {
2684
+ if (previous !== void 0) try {
2685
+ await previous;
2686
+ } catch {}
2687
+ await store.set(snapshot);
2688
+ }
2689
+ #invalidate(id) {
2690
+ this.#opens.delete(id);
2691
+ if (!this.#hydrations.has(id)) {
2692
+ this.#mutations.delete(id);
2693
+ this.#additions.delete(id);
2694
+ return;
2695
+ }
2696
+ const mutation = Symbol();
2697
+ this.#mutations.set(id, mutation);
2698
+ return mutation;
2699
+ }
2700
+ #retain(id) {
2701
+ const lease = Symbol();
2702
+ const hydrations = this.#hydrations.get(id);
2703
+ if (hydrations === void 0) this.#hydrations.set(id, /* @__PURE__ */ new Set([lease]));
2704
+ else hydrations.add(lease);
2705
+ return lease;
2706
+ }
2707
+ #releaseOpen(id, opening) {
2708
+ if (this.#opens.get(id) === opening) this.#opens.delete(id);
2709
+ }
2710
+ #releaseHydration(id, lease) {
2711
+ const hydrations = this.#hydrations.get(id);
2712
+ if (hydrations === void 0) return;
2713
+ hydrations.delete(lease);
2714
+ if (hydrations.size !== 0) return;
2715
+ this.#hydrations.delete(id);
2716
+ this.#mutations.delete(id);
2717
+ this.#additions.delete(id);
2718
+ }
2719
+ #settle(id, saving) {
2720
+ if (this.#saves.get(id) === saving) this.#saves.delete(id);
2721
+ }
2491
2722
  };
2492
2723
  //#endregion
2493
2724
  //#region src/core/Controller.ts
@@ -2606,6 +2837,7 @@ var Runner = class {
2606
2837
  #order = [];
2607
2838
  #values = /* @__PURE__ */ new Map();
2608
2839
  #dispatched = /* @__PURE__ */ new Set();
2840
+ #queued = /* @__PURE__ */ new Set();
2609
2841
  #count = 0;
2610
2842
  #drained;
2611
2843
  #started = false;
@@ -2613,18 +2845,28 @@ var Runner = class {
2613
2845
  #stopped = false;
2614
2846
  #stopping = false;
2615
2847
  #failure;
2848
+ #stopPromise;
2849
+ #abortPromise;
2850
+ #destroyPromise;
2616
2851
  constructor(options) {
2617
- this.#handler = options.handler;
2618
- this.#entries = options.entries;
2852
+ const handler = options.handler;
2853
+ const entries = options.entries;
2854
+ const on = options.on;
2855
+ const error = options.error;
2856
+ const concurrency = options.concurrency;
2857
+ const retries = options.retries;
2858
+ const timeout = options.timeout;
2859
+ this.#handler = handler;
2860
+ this.#entries = entries;
2619
2861
  this.#emitter = new Emitter({
2620
- ...options.on === void 0 ? {} : { on: options.on },
2621
- ...options.error === void 0 ? {} : { error: options.error }
2862
+ ...on === void 0 ? {} : { on },
2863
+ ...error === void 0 ? {} : { error }
2622
2864
  });
2623
2865
  this.#queue = createQueue({
2624
2866
  handler: this.#dispatch.bind(this),
2625
- ...options.concurrency === void 0 ? {} : { concurrency: options.concurrency },
2626
- ...options.retries === void 0 ? {} : { retries: options.retries },
2627
- ...options.timeout === void 0 ? {} : { timeout: options.timeout }
2867
+ ...concurrency === void 0 ? {} : { concurrency },
2868
+ ...retries === void 0 ? {} : { retries },
2869
+ ...timeout === void 0 ? {} : { timeout }
2628
2870
  });
2629
2871
  }
2630
2872
  get emitter() {
@@ -2666,7 +2908,7 @@ var Runner = class {
2666
2908
  * ```
2667
2909
  */
2668
2910
  spawn(input) {
2669
- if (this.#stopped || !this.#running) return void 0;
2911
+ if (!this.#accepts()) return void 0;
2670
2912
  return this.#launch(input, void 0, true);
2671
2913
  }
2672
2914
  async execute(inputs) {
@@ -2674,29 +2916,37 @@ var Runner = class {
2674
2916
  if (this.#stopped) throw new Error("runner is stopped");
2675
2917
  this.#started = true;
2676
2918
  this.#running = true;
2677
- this.#emitter.emit("start");
2678
- if (inputs.length === 0) {
2679
- this.#running = false;
2680
- this.#emitter.emit("finish", []);
2681
- return [];
2682
- }
2683
2919
  const drained = createDeferred();
2684
2920
  this.#drained = drained;
2685
- for (const input of inputs) this.#launch(input);
2921
+ for (const input of inputs) {
2922
+ if (!this.#accepts()) break;
2923
+ this.#launch(input);
2924
+ }
2925
+ this.#emitter.emit("start");
2926
+ if (this.#count === 0) drained.resolve();
2686
2927
  await drained.promise;
2687
2928
  this.#running = false;
2929
+ const cleanup = await this.#cleanup();
2688
2930
  if (this.#failure !== void 0) throw this.#failure.error;
2931
+ if (cleanup !== void 0) throw cleanup.error;
2689
2932
  const results = this.#collect();
2690
2933
  this.#emitter.emit("finish", results);
2934
+ const finishing = await this.#cleanup();
2935
+ if (finishing !== void 0) throw finishing.error;
2691
2936
  return results;
2692
2937
  }
2693
2938
  abort(reason) {
2694
- if (this.#stopped) return;
2939
+ if (this.#abortPromise !== void 0) return this.#abortPromise;
2940
+ const barrier = createDeferred();
2941
+ this.#abortPromise = barrier.promise;
2942
+ barrier.promise.catch(() => {});
2695
2943
  if (this.#running && this.#failure === void 0) this.#failure = { error: reason === void 0 ? /* @__PURE__ */ new Error("runner aborted") : reason };
2696
2944
  this.#cancel(reason);
2697
- this.#queue.abort(reason);
2698
2945
  this.#stopped = true;
2946
+ const cleanup = this.#queue.abort(reason);
2947
+ this.#settleLifecycle(barrier, cleanup);
2699
2948
  this.#emitter.emit("abort", reason);
2949
+ return barrier.promise;
2700
2950
  }
2701
2951
  /**
2702
2952
  * Suspend dispatch (AGENTS §10 — resumable): delegates to the backing queue's own
@@ -2734,18 +2984,28 @@ var Runner = class {
2734
2984
  * dispatched unit's rejection while stopping is still a real failure. Idempotent.
2735
2985
  */
2736
2986
  stop() {
2737
- if (this.#stopped) return;
2987
+ if (this.#destroyPromise !== void 0) return this.#destroyPromise;
2988
+ if (this.#abortPromise !== void 0) return this.#abortPromise;
2989
+ if (this.#stopPromise !== void 0) return this.#stopPromise;
2990
+ const barrier = createDeferred();
2991
+ this.#stopPromise = barrier.promise;
2992
+ barrier.promise.catch(() => {});
2738
2993
  this.#stopping = true;
2739
2994
  this.#stopped = true;
2740
- this.#queue.stop();
2995
+ const cleanup = this.#queue.stop();
2996
+ this.#settleLifecycle(barrier, cleanup);
2997
+ return barrier.promise;
2741
2998
  }
2742
2999
  destroy() {
2743
- if (this.#stopped) {
2744
- this.#queue.destroy();
2745
- return;
2746
- }
3000
+ if (this.#destroyPromise !== void 0) return this.#destroyPromise;
3001
+ const barrier = createDeferred();
3002
+ this.#destroyPromise = barrier.promise;
3003
+ barrier.promise.catch(() => {});
3004
+ this.#stopped = true;
2747
3005
  this.abort();
2748
- this.#queue.destroy();
3006
+ const cleanup = this.#queue.destroy();
3007
+ this.#settleDestroy(barrier, cleanup);
3008
+ return barrier.promise;
2749
3009
  }
2750
3010
  #launch(input, parent, announce = parent !== void 0) {
2751
3011
  const id = crypto.randomUUID();
@@ -2754,14 +3014,24 @@ var Runner = class {
2754
3014
  this.#order.push(id);
2755
3015
  this.#count += 1;
2756
3016
  if (announce) this.#emitter.emit("spawn", id, parent);
2757
- const promise = this.#queue.enqueue({
2758
- id,
2759
- input
2760
- }, {
2761
- id,
2762
- signal: abort.signal,
2763
- ...this.#entries?.(input)
2764
- });
3017
+ let promise;
3018
+ try {
3019
+ const entry = this.#entries?.(input);
3020
+ const retries = entry?.retries;
3021
+ const timeout = entry?.timeout;
3022
+ this.#queued.add(id);
3023
+ promise = this.#queue.enqueue({
3024
+ id,
3025
+ input
3026
+ }, {
3027
+ id,
3028
+ signal: abort.signal,
3029
+ ...retries === void 0 ? {} : { retries },
3030
+ ...timeout === void 0 ? {} : { timeout }
3031
+ });
3032
+ } catch (error) {
3033
+ promise = Promise.reject(error);
3034
+ }
2765
3035
  promise.then((value) => this.#settle(id, {
2766
3036
  ok: true,
2767
3037
  value
@@ -2780,14 +3050,14 @@ var Runner = class {
2780
3050
  return this.#handler(controller);
2781
3051
  }
2782
3052
  #spawn(input, parent) {
2783
- if (!this.#running) throw new Error("spawn is unavailable outside an active run");
3053
+ if (!this.#accepts()) throw new Error("spawn is unavailable outside an active run");
2784
3054
  return this.#launch(input, parent);
2785
3055
  }
2786
3056
  #settle(id, outcome) {
2787
3057
  if (outcome.ok) {
2788
3058
  this.#values.set(id, { value: outcome.value });
2789
3059
  this.#emitter.emit("settle", id);
2790
- } else if (this.#stopping && !this.#dispatched.has(id)) {} else if (this.#failure === void 0) {
3060
+ } else if (this.#stopping && this.#queued.has(id) && !this.#dispatched.has(id)) {} else if (this.#failure === void 0) {
2791
3061
  this.#failure = { error: outcome.error };
2792
3062
  this.#emitter.emit("fail", id, outcome.error);
2793
3063
  this.abort(outcome.error);
@@ -2803,6 +3073,46 @@ var Runner = class {
2803
3073
  }
2804
3074
  return results;
2805
3075
  }
3076
+ #accepts() {
3077
+ return this.#running && !this.#stopped;
3078
+ }
3079
+ async #cleanup() {
3080
+ const cleanup = this.#destroyPromise ?? this.#abortPromise ?? this.#stopPromise;
3081
+ if (cleanup === void 0) return void 0;
3082
+ try {
3083
+ await cleanup;
3084
+ return;
3085
+ } catch (error) {
3086
+ return { error };
3087
+ }
3088
+ }
3089
+ async #settleLifecycle(barrier, cleanup) {
3090
+ let failure;
3091
+ try {
3092
+ await cleanup;
3093
+ } catch (error) {
3094
+ failure = { error };
3095
+ }
3096
+ await this.#waitDrain();
3097
+ if (failure === void 0) barrier.resolve();
3098
+ else barrier.reject(failure.error);
3099
+ }
3100
+ async #settleDestroy(barrier, cleanup) {
3101
+ let failure;
3102
+ try {
3103
+ await cleanup;
3104
+ } catch (error) {
3105
+ failure = { error };
3106
+ }
3107
+ await this.#waitDrain();
3108
+ this.#emitter.destroy();
3109
+ if (failure === void 0) barrier.resolve();
3110
+ else barrier.reject(failure.error);
3111
+ }
3112
+ async #waitDrain() {
3113
+ if (this.#count === 0) return;
3114
+ await this.#drained?.promise;
3115
+ }
2806
3116
  #cancel(reason) {
2807
3117
  for (const abort of this.#aborts.values()) abort.abort(reason);
2808
3118
  }
@@ -3078,8 +3388,10 @@ var WorkflowPersistence = class {
3078
3388
  await this.#writing;
3079
3389
  return;
3080
3390
  }
3081
- const writing = this.#drain();
3391
+ const reservation = Promise.withResolvers();
3392
+ const writing = reservation.promise;
3082
3393
  this.#writing = writing;
3394
+ this.#drain().then(reservation.resolve, reservation.reject);
3083
3395
  try {
3084
3396
  await writing;
3085
3397
  } finally {
@@ -3115,8 +3427,9 @@ var WorkflowPersistence = class {
3115
3427
  * - **Composes, never re-implements.** Per-phase bounded concurrency is one
3116
3428
  * {@link createRunner} per phase (the substrate {@link RunnerInterface} over the workers
3117
3429
  * `Queue`); `bail` maps onto that Runner's fail-fast vs settle-all; the run-level abort /
3118
- * timeout / budget / entity `signal` fold through {@link createAbort} / {@link createTimeout} +
3119
- * `AbortSignal.any` (exactly as the agent runtime folds its bounds); pacing is the shipped
3430
+ * timeout / budget / entity `signal` fold through the `@orkestrel/abort` signal contract,
3431
+ * {@link createTimeout}, and `AbortSignal.any` (exactly as the agent runtime folds its bounds);
3432
+ * pacing is the shipped
3120
3433
  * {@link SchedulerInterface}. The runner writes ZERO concurrency / retry / abort logic of
3121
3434
  * its own — it only sequences phases, dispatches a task's own handler, and drives the live
3122
3435
  * entity. The workflow layer owns per-task deadlines because timeout settlement must
@@ -3191,34 +3504,46 @@ var WorkflowRunner = class WorkflowRunner {
3191
3504
  }
3192
3505
  execute(target, options) {
3193
3506
  if (this.#isWorkflow(target)) {
3507
+ const signal = options?.signal;
3508
+ const timeout = options?.timeout;
3509
+ const budget = options?.budget;
3510
+ const store = options?.store;
3194
3511
  this.#acquire(target);
3195
- return this.#execute(target, options);
3512
+ return this.#execute(target, signal, timeout, budget, store);
3196
3513
  }
3197
- const workflow = new Workflow(definitionToSnapshot(target, options?.bail ?? target.bail ?? false), options);
3514
+ const captured = captureWorkflowOptions(options);
3515
+ const signal = options?.signal;
3516
+ const timeout = options?.timeout;
3517
+ const budget = options?.budget;
3518
+ const store = options?.store;
3519
+ const workflow = new Workflow(definitionToSnapshot(target, captured.bail ?? target.bail ?? false), captured);
3198
3520
  this.#acquire(workflow);
3199
- return this.#execute(workflow, options);
3521
+ return this.#execute(workflow, signal, timeout, budget, store);
3200
3522
  }
3201
3523
  #acquire(workflow) {
3202
3524
  const tasks = workflow.phases.phases().flatMap((phase) => phase.tasks.tasks());
3203
- if (!((workflow.status === "pending" || workflow.status === "running") && tasks.every((task) => task.status !== "running") && (tasks.length === 0 || tasks.some((task) => task.status === "pending")) && tasks.every((task) => task.run === void 0 || task.handler !== void 0)) || workflow.destroyed || WorkflowRunner.#executions.has(workflow)) throw new WorkflowError("TRANSITION", `workflow '${workflow.id}' is not drivable`, {
3525
+ if (!((workflow.status === "pending" || workflow.status === "running") && tasks.every((task) => task.status !== "running") && (tasks.length === 0 || tasks.some((task) => task.status === "pending")) && hasWorkflowHandlers(workflow)) || workflow.destroyed || WorkflowRunner.#executions.has(workflow)) throw new WorkflowError("TRANSITION", `workflow '${workflow.id}' is not drivable`, {
3204
3526
  id: workflow.id,
3205
3527
  status: workflow.status,
3206
3528
  destroyed: workflow.destroyed
3207
3529
  });
3208
3530
  WorkflowRunner.#executions.add(workflow);
3209
3531
  }
3210
- async #execute(workflow, options) {
3211
- const ms = options?.timeout;
3212
- const timeout = ms !== void 0 && Number.isFinite(ms) && ms > 0 && ms <= 2147483647 ? createTimeout({ ms }) : void 0;
3213
- timeout?.start();
3214
- options?.budget?.start();
3215
- const runSignal = this.#fold(workflow, options, timeout);
3216
- const persistence = options?.store === void 0 ? void 0 : new WorkflowPersistence(workflow, options.store);
3532
+ async #execute(workflow, signal, ms, budget, store) {
3217
3533
  const holder = { runner: void 0 };
3218
- const onCancel = this.#abortActive.bind(this, holder, runSignal);
3219
- if (runSignal.aborted) onCancel();
3220
- else runSignal.addEventListener("abort", onCancel, { once: true });
3534
+ let timeout;
3535
+ let persistence;
3536
+ let runSignal;
3537
+ let onCancel;
3221
3538
  try {
3539
+ timeout = ms !== void 0 && Number.isFinite(ms) && ms > 0 && ms <= 2147483647 ? createTimeout({ ms }) : void 0;
3540
+ timeout?.start();
3541
+ budget?.start();
3542
+ runSignal = this.#fold(workflow, signal, budget, timeout);
3543
+ persistence = store === void 0 ? void 0 : new WorkflowPersistence(workflow, store);
3544
+ onCancel = this.#abortActive.bind(this, holder, runSignal);
3545
+ if (runSignal.aborted) onCancel();
3546
+ else runSignal.addEventListener("abort", onCancel, { once: true });
3222
3547
  if (persistence !== void 0 && !await persistence.checkpoint("initial")) {
3223
3548
  if (this.#stoppable(workflow)) workflow.stop();
3224
3549
  this.#skipFrom(workflow.phases.phases(), 0);
@@ -3252,11 +3577,7 @@ var WorkflowRunner = class WorkflowRunner {
3252
3577
  }
3253
3578
  index += 1;
3254
3579
  const remaining = workflow.phases.phases();
3255
- if (index < remaining.length && !this.#cancelled(runSignal)) try {
3256
- await this.#scheduler.yield({ signal: runSignal });
3257
- } catch (error) {
3258
- if (!runSignal.aborted) throw error;
3259
- }
3580
+ if (index < remaining.length && !this.#cancelled(runSignal)) await this.#pace(runSignal);
3260
3581
  }
3261
3582
  if (this.#cancelled(runSignal)) this.#haltFrom(workflow.phases.phases(), 0, workflow, runSignal);
3262
3583
  else if (this.#completable(workflow)) workflow.complete();
@@ -3276,7 +3597,14 @@ var WorkflowRunner = class WorkflowRunner {
3276
3597
  } finally {
3277
3598
  persistence?.detach();
3278
3599
  timeout?.clear();
3279
- runSignal.removeEventListener("abort", onCancel);
3600
+ if (runSignal !== void 0 && onCancel !== void 0) runSignal.removeEventListener("abort", onCancel);
3601
+ }
3602
+ }
3603
+ async #pace(signal) {
3604
+ try {
3605
+ await this.#scheduler.yield({ signal });
3606
+ } catch (error) {
3607
+ if (!signal.aborted) throw error;
3280
3608
  }
3281
3609
  }
3282
3610
  async #runPhase(workflow, phase, runSignal, holder, persistence) {
@@ -3304,8 +3632,11 @@ var WorkflowRunner = class WorkflowRunner {
3304
3632
  } catch {
3305
3633
  return !this.#cancelled(runSignal);
3306
3634
  } finally {
3307
- created.destroy();
3308
- holder.runner = void 0;
3635
+ try {
3636
+ await created.destroy();
3637
+ } finally {
3638
+ holder.runner = void 0;
3639
+ }
3309
3640
  }
3310
3641
  } finally {
3311
3642
  phase.emitter.off("add", onAdd);
@@ -3516,11 +3847,11 @@ var WorkflowRunner = class WorkflowRunner {
3516
3847
  if (timeout !== void 0) signals.push(timeout.signal);
3517
3848
  return AbortSignal.any(signals);
3518
3849
  }
3519
- #fold(workflow, options, timeout) {
3850
+ #fold(workflow, signal, budget, timeout) {
3520
3851
  const signals = [workflow.signal];
3521
- if (options?.signal !== void 0) signals.push(options.signal);
3852
+ if (signal !== void 0) signals.push(signal);
3522
3853
  if (timeout !== void 0) signals.push(timeout.signal);
3523
- if (options?.budget !== void 0) signals.push(options.budget.signal);
3854
+ if (budget !== void 0) signals.push(budget.signal);
3524
3855
  return signals.length === 1 ? workflow.signal : AbortSignal.any(signals);
3525
3856
  }
3526
3857
  #haltFrom(phases, index, workflow, runSignal) {
@@ -3583,7 +3914,7 @@ var WorkflowRunner = class WorkflowRunner {
3583
3914
  *
3584
3915
  * @example
3585
3916
  * ```ts
3586
- * import { createWorkflowContract } from '@src/core'
3917
+ * import { createWorkflowContract } from '@orkestrel/workflow'
3587
3918
  *
3588
3919
  * const contract = createWorkflowContract()
3589
3920
  * const definition = contract.generate() // a valid WorkflowDefinition
@@ -3621,7 +3952,7 @@ function createWorkflowContract() {
3621
3952
  *
3622
3953
  * @example
3623
3954
  * ```ts
3624
- * import { createWorkflow } from '@src/core'
3955
+ * import { createWorkflow } from '@orkestrel/workflow'
3625
3956
  *
3626
3957
  * const workflow = createWorkflow(definition, { on: { complete: () => done() } })
3627
3958
  * const phase = workflow.phase('phase-build')
@@ -3629,7 +3960,8 @@ function createWorkflowContract() {
3629
3960
  * ```
3630
3961
  */
3631
3962
  function createWorkflow(definition, options) {
3632
- return new Workflow(definitionToSnapshot(definition, options?.bail ?? definition.bail ?? false), options);
3963
+ const captured = captureWorkflowOptions(options);
3964
+ return new Workflow(definitionToSnapshot(definition, captured.bail ?? definition.bail ?? false), captured);
3633
3965
  }
3634
3966
  /**
3635
3967
  * Rebuild an equivalent live W-b entity tree from a {@link WorkflowSnapshot} — the
@@ -3657,27 +3989,35 @@ function createWorkflow(definition, options) {
3657
3989
  *
3658
3990
  * @example
3659
3991
  * ```ts
3660
- * import { restoreWorkflow } from '@src/core'
3992
+ * import { restoreWorkflow } from '@orkestrel/workflow'
3661
3993
  *
3662
3994
  * const restored = restoreWorkflow(workflow.snapshot()) // bail comes from the snapshot
3663
3995
  * restored.status === workflow.status // true
3664
3996
  * ```
3665
3997
  */
3666
3998
  function restoreWorkflow(snapshot, options) {
3667
- return new Workflow(cloneWorkflowSnapshot(snapshot), options);
3999
+ const captured = captureWorkflowOptions(options);
4000
+ return new Workflow(cloneWorkflowSnapshot(snapshot), captured);
3668
4001
  }
3669
4002
  /**
3670
4003
  * Rebuild an interrupted workflow at its remaining retry budget.
3671
4004
  *
4005
+ * @remarks
4006
+ * Each phase captures every unique initial `run` binding once before constructing tasks. Recovery
4007
+ * validates those live tasks' captured callable handlers without rereading the registry, while the
4008
+ * retained registry identity remains available to resolve future live additions at their mint time.
4009
+ *
3672
4010
  * @param snapshot - The hostile persisted snapshot
3673
4011
  * @param options - Runtime handlers and entity options
3674
4012
  * @returns A recoverable live workflow
3675
4013
  */
3676
4014
  function recoverWorkflow(snapshot, options) {
4015
+ const captured = captureWorkflowOptions(options);
3677
4016
  const owned = cloneWorkflowSnapshot(snapshot);
3678
4017
  if (owned.override !== void 0 || owned.phases.some((phase) => phase.override !== void 0)) throw new WorkflowError("RESTORE", `workflow '${owned.id}' has a terminal override`, { workflow: owned.id });
3679
- if (!hasWorkflowHandlers(owned, options?.functions)) throw new WorkflowError("RESTORE", `workflow '${owned.id}' has an unresolved run`, { workflow: owned.id });
3680
- return new Workflow(cloneWorkflowSnapshot(recoverWorkflowSnapshot(owned)), options);
4018
+ const workflow = new Workflow(cloneWorkflowSnapshot(recoverWorkflowSnapshot(owned)), captured);
4019
+ if (!hasWorkflowHandlers(workflow)) throw new WorkflowError("RESTORE", `workflow '${owned.id}' has an unresolved run`, { workflow: owned.id });
4020
+ return workflow;
3681
4021
  }
3682
4022
  /**
3683
4023
  * Assert that a {@link WorkflowSnapshot} carries a `boolean` `bail` — at the workflow tier AND
@@ -3709,7 +4049,7 @@ function assertSnapshot(snapshot) {
3709
4049
  *
3710
4050
  * @remarks
3711
4051
  * The snapshot analogue of the server package's `createMemorySessionStore`
3712
- * (and the {@link createMemoryQueueStore} family), but LEANER — there is no idle-TTL, so no
4052
+ * (and the `createMemoryQueueStore` family), but LEANER — there is no idle-TTL, so no
3713
4053
  * options bag (AGENTS §21 minimal): a persisted run-state lives until an explicit `delete`. This is
3714
4054
  * the zero-plumbing DEFAULT (a plain `Map`); its driver-pluggable twin is
3715
4055
  * {@link createDatabaseWorkflowStore} (the snapshot as one opaque JSON column over a `databases`
@@ -3721,7 +4061,7 @@ function assertSnapshot(snapshot) {
3721
4061
  *
3722
4062
  * @example
3723
4063
  * ```ts
3724
- * import { createMemoryWorkflowStore, createWorkflow, restoreWorkflow } from '@src/core'
4064
+ * import { createMemoryWorkflowStore, createWorkflow, restoreWorkflow } from '@orkestrel/workflow'
3725
4065
  *
3726
4066
  * const store = createMemoryWorkflowStore()
3727
4067
  * const workflow = createWorkflow(definition)
@@ -3741,7 +4081,7 @@ function createMemoryWorkflowStore() {
3741
4081
  * @remarks
3742
4082
  * Builds a one-table database (`snapshots`, keyed by `id`) over the supplied driver, the snapshot
3743
4083
  * held as ONE OPAQUE JSON COLUMN — the column map is `{ id; snapshot }` where `snapshot` is a
3744
- * `rawShape` (a JSON blob), exactly as {@link createDatabaseQueueStore} stores its `input`. The
4084
+ * `rawShape` (a JSON blob), exactly as `createDatabaseQueueStore` stores its `input`. The
3745
4085
  * snapshot is already a COMPLETE, self-contained, pure-JSON payload, so storing it whole is lossless
3746
4086
  * AND keeps the row type FLAT — a structured multi-column snapshot table would force the contract to
3747
4087
  * `Infer` the deeply-nested snapshot shape (workflow → phases → tasks → results) and trip TS2589;
@@ -3757,7 +4097,8 @@ function createMemoryWorkflowStore() {
3757
4097
  *
3758
4098
  * @example
3759
4099
  * ```ts
3760
- * import { createDatabaseWorkflowStore, createMemoryDriver, createWorkflow, restoreWorkflow } from '@src/core'
4100
+ * import { createMemoryDriver } from '@orkestrel/database'
4101
+ * import { createDatabaseWorkflowStore, createWorkflow, restoreWorkflow } from '@orkestrel/workflow'
3761
4102
  *
3762
4103
  * const store = createDatabaseWorkflowStore(createMemoryDriver()) // a durable driver swaps in here
3763
4104
  * const workflow = createWorkflow(definition)
@@ -3808,7 +4149,7 @@ function createDatabaseWorkflowStore(driver = createMemoryDriver()) {
3808
4149
  *
3809
4150
  * @example
3810
4151
  * ```ts
3811
- * import { createWorkflowRunner } from '@src/core'
4152
+ * import { createWorkflowRunner } from '@orkestrel/workflow'
3812
4153
  *
3813
4154
  * const runner = createWorkflowRunner()
3814
4155
  * const definition = { id: 'w', name: 'W', phases: [{ id: 'p', name: 'P', tasks: [
@@ -3844,7 +4185,7 @@ function createWorkflowRunner(options) {
3844
4185
  *
3845
4186
  * @example
3846
4187
  * ```ts
3847
- * import { createMemoryWorkflowStore, createWorkflowManager } from '@src/core'
4188
+ * import { createMemoryWorkflowStore, createWorkflowManager } from '@orkestrel/workflow'
3848
4189
  *
3849
4190
  * const manager = createWorkflowManager({
3850
4191
  * store: createMemoryWorkflowStore(),
@@ -3867,7 +4208,8 @@ function createWorkflowManager(options) {
3867
4208
  * `yield()` gives the host a turn via a zero-delay macrotask (so pending I/O,
3868
4209
  * timers, and rendering actually run — a microtask would not); `delay(ms)` resumes
3869
4210
  * after at least `ms`. Pass `options.signal` to make a pending yield/delay reject
3870
- * with the signal's `reason` on abort (with full timer/listener cleanup).
4211
+ * with the signal's exact `reason`; the shared owned-signal lifecycle clears the timer
4212
+ * without invoking caller-owned listener methods.
3871
4213
  * `options.priority` is accepted for contract compliance but treated uniformly by
3872
4214
  * this default — environment backends honour it.
3873
4215
  *
@@ -3875,7 +4217,8 @@ function createWorkflowManager(options) {
3875
4217
  *
3876
4218
  * @example
3877
4219
  * ```ts
3878
- * import { createAbort, createScheduler } from '@src/core'
4220
+ * import { createAbort } from '@orkestrel/abort'
4221
+ * import { createScheduler } from '@orkestrel/workflow'
3879
4222
  *
3880
4223
  * const abort = createAbort()
3881
4224
  * const scheduler = createScheduler()
@@ -3889,7 +4232,7 @@ function createWorkflowManager(options) {
3889
4232
  *
3890
4233
  * @example
3891
4234
  * ```ts
3892
- * import { createScheduler } from '@src/core'
4235
+ * import { createScheduler } from '@orkestrel/workflow'
3893
4236
  *
3894
4237
  * // A backoff: wait a growing interval between retries.
3895
4238
  * const scheduler = createScheduler()
@@ -3931,7 +4274,7 @@ function createScheduler() {
3931
4274
  *
3932
4275
  * @example
3933
4276
  * ```ts
3934
- * import { createRunner } from '@src/core'
4277
+ * import { createRunner } from '@orkestrel/workflow'
3935
4278
  *
3936
4279
  * // A handler that fans out one sibling per declared unit, then returns its own value.
3937
4280
  * const runner = createRunner<number, number>({
@@ -3950,6 +4293,6 @@ function createRunner(options) {
3950
4293
  return new Runner(options);
3951
4294
  }
3952
4295
  //#endregion
3953
- export { Controller, DEFAULT_BAIL, DEFAULT_PHASE_CONCURRENCY, DatabaseWorkflowStore, MAX_TIMER_MS, MemoryWorkflowStore, PHASE_STATUSES, Phase, PhaseManager, Runner, Scheduler, TASK_STATUSES, TASK_TRANSITIONS, TERMINAL_TASK_STATUSES, Task, TaskController, TaskManager, WORKFLOW_STATUSES, Workflow, WorkflowError, WorkflowManager, WorkflowPersistence, WorkflowRunner, assertSnapshot, buildPhaseContext, buildTaskContext, buildWorkflowContext, canTransitionTask, cloneTaskActivity, cloneWorkflowSnapshot, collectResults, createDatabaseWorkflowStore, createDeferred, createMemoryWorkflowStore, createRunner, createScheduler, createWorkflow, createWorkflowContract, createWorkflowManager, createWorkflowRunner, definitionToSnapshot, deriveBoundary, derivePhaseStatus, deriveWorkflowStatus, errorToMessage, failure, findFailure, hasWorkflowHandlers, insertEntry, isLifecycleStatus, isOwnedWorkflowSnapshot, isTaskActivity, isTaskActivityInput, isTaskFailure, isTaskResult, isTerminalStatus, isWorkflowError, isWorkflowSnapshot, matchesDescription, moveEntry, parkSignal, phaseDefinitionToSnapshot, phaseShape, phaseUpdateShape, recoverWorkflow, recoverWorkflowSnapshot, resolveTaskSilence, restoreWorkflow, success, taskDefinitionToSnapshot, taskShape, taskUpdateShape, workflowShape, workflowSnapshotContext };
4296
+ export { Controller, DEFAULT_BAIL, DEFAULT_PHASE_CONCURRENCY, DatabaseWorkflowStore, MAX_TIMER_MS, MemoryWorkflowStore, PHASE_STATUSES, Phase, PhaseManager, Runner, Scheduler, TASK_STATUSES, TASK_TRANSITIONS, TERMINAL_TASK_STATUSES, Task, TaskController, TaskManager, WORKFLOW_STATUSES, Workflow, WorkflowError, WorkflowManager, WorkflowPersistence, WorkflowRunner, assertSnapshot, buildPhaseContext, buildTaskContext, buildWorkflowContext, canTransitionTask, captureWorkflowOptions, cloneTaskActivity, cloneWorkflowSnapshot, collectResults, createDatabaseWorkflowStore, createDeferred, createMemoryWorkflowStore, createRunner, createScheduler, createWorkflow, createWorkflowContract, createWorkflowManager, createWorkflowRunner, definitionToSnapshot, deriveBoundary, derivePhaseStatus, deriveWorkflowStatus, errorToMessage, failure, findFailure, hasWorkflowHandlers, insertEntry, isLifecycleStatus, isOwnedWorkflowSnapshot, isTaskActivity, isTaskActivityInput, isTaskFailure, isTaskResult, isTerminalStatus, isWorkflowError, isWorkflowSnapshot, matchesDescription, moveEntry, parkSignal, phaseDefinitionToSnapshot, phaseShape, phaseUpdateShape, recoverWorkflow, recoverWorkflowSnapshot, resolveTaskSilence, restoreWorkflow, scheduleHost, success, taskDefinitionToSnapshot, taskShape, taskUpdateShape, workflowShape, workflowSnapshotContext };
3954
4297
 
3955
4298
  //# sourceMappingURL=index.js.map