@orkestrel/workflow 0.0.7 → 0.0.8

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.
@@ -7,6 +7,8 @@ import { EmitterErrorHandler } from '@orkestrel/emitter';
7
7
  import { EmitterHooks } from '@orkestrel/emitter';
8
8
  import { EmitterInterface } from '@orkestrel/emitter';
9
9
  import { Failure } from '@orkestrel/contract';
10
+ import { JSONRecord } from '@orkestrel/contract';
11
+ import { JSONValue } from '@orkestrel/contract';
10
12
  import { LiteralShape } from '@orkestrel/contract';
11
13
  import { NumberShape } from '@orkestrel/contract';
12
14
  import { ObjectShape } from '@orkestrel/contract';
@@ -37,7 +39,7 @@ import { TokenUsage } from '@orkestrel/budget';
37
39
  *
38
40
  * @param snapshot - The snapshot to validate
39
41
  */
40
- export declare function assertSnapshot(snapshot: WorkflowSnapshot): void;
42
+ export declare function assertSnapshot(snapshot: unknown): void;
41
43
 
42
44
  /**
43
45
  * Build a {@link PhaseContext} — a phase's own identity plus a back-reference to its
@@ -90,6 +92,31 @@ export declare function buildWorkflowContext(node: WorkflowContext): WorkflowCon
90
92
  */
91
93
  export declare function canTransitionTask(from: TaskStatus, to: TaskStatus): boolean;
92
94
 
95
+ /**
96
+ * Validate and clone one complete task activity frame.
97
+ *
98
+ * @remarks
99
+ * This is the hostile boundary behind task reports and snapshot hydration. Supplying
100
+ * `updated` stamps an input frame without reading an `updated` property from it; omitting
101
+ * `updated` restores a stored frame and reads its persisted timestamp exactly once. Every
102
+ * untrusted property is captured once inside one protected boundary. The returned frame,
103
+ * collections, progress, operations, and constraints are copied and frozen.
104
+ *
105
+ * @param input - The untrusted complete activity frame
106
+ * @param updated - An optional accepted timestamp used instead of a persisted `updated`
107
+ * @returns An immutable cloned {@link TaskActivity}
108
+ * @throws {WorkflowError} With `MUTATION` when the frame cannot be read or validated
109
+ */
110
+ export declare function cloneTaskActivity(input: unknown, updated?: number): TaskActivity;
111
+
112
+ /**
113
+ * Validate and own a workflow snapshot before live construction.
114
+ *
115
+ * @param input - The hostile snapshot boundary
116
+ * @returns A deeply owned frozen snapshot
117
+ */
118
+ export declare function cloneWorkflowSnapshot(input: unknown): WorkflowSnapshot;
119
+
93
120
  /**
94
121
  * Flatten a nested list of per-phase {@link TaskResult} lists into one positional list
95
122
  * — the workflow tier of the result tree, built from each phase's `results()`.
@@ -388,8 +415,8 @@ export declare function createScheduler(): SchedulerInterface;
388
415
  *
389
416
  * `options.functions` is the {@link import('./types.js').WorkflowFunctions} registry each live
390
417
  * task's `run` name resolves against ONCE at construction into its runtime
391
- * {@link import('./types.js').TaskInterface.handler} a name omitted or absent from the
392
- * registry resolves to no handler (the no-handler rule).
418
+ * {@link import('./types.js').TaskInterface.handler}. An omitted name is the deliberate no-op;
419
+ * an unresolved present name remains inspectable but is rejected if execution is attempted.
393
420
  *
394
421
  * @param definition - The workflow definition to bring to life
395
422
  * @param options - Runtime options (initial listeners, `bail` override, per-node options)
@@ -475,7 +502,7 @@ export declare function createWorkflowManager(options?: WorkflowManagerOptions):
475
502
  *
476
503
  * @remarks
477
504
  * The runner is a PURE engine — it re-implements no concurrency / retry / abort logic, AND it
478
- * carries no `functions` / `tools` / `agents` registry of its own: each live task already
505
+ * carries no behavior or provider registry of its own: each live task already
479
506
  * resolved its own {@link import('./types.js').WorkflowFunction} into
480
507
  * {@link import('./types.js').TaskInterface.handler} ONCE at construction, from the
481
508
  * {@link WorkflowOptions.functions} registry supplied to `execute` / {@link createWorkflow}.
@@ -489,11 +516,10 @@ export declare function createWorkflowManager(options?: WorkflowManagerOptions):
489
516
  * the live entity (`start` → `complete` / `fail`), and resolves a
490
517
  * {@link import('./types.js').WorkflowResult}.
491
518
  *
492
- * Static tool / agent calling is OPT-IN: a caller wires a plain
493
- * {@link import('./types.js').WorkflowFunction} into its OWN {@link WorkflowOptions.functions}
494
- * registry, same as any other behavior the `@orkestrel/tool` package ships the
495
- * tool/agent adapter factories for that. A task with no resolved handler AUTO-COMPLETES
496
- * (the ROADMAP no-handler rule).
519
+ * External integrations remain application-owned: a caller wires an ordinary
520
+ * {@link import('./types.js').WorkflowFunction} into its own {@link WorkflowOptions.functions}
521
+ * registry. Only a task that omits `run` auto-completes; unresolved named work is rejected
522
+ * before dispatch.
497
523
  *
498
524
  * @param options - An optional pacing `scheduler` (default the shipped cross-environment one).
499
525
  * See {@link WorkflowRunnerOptions}.
@@ -728,6 +754,14 @@ export declare function derivePhaseStatus(tasks: readonly TaskStatus[]): PhaseSt
728
754
  */
729
755
  export declare function deriveWorkflowStatus(phases: readonly PhaseDerivation[]): WorkflowStatus;
730
756
 
757
+ /**
758
+ * Normalize an unknown thrown value to a non-empty persistence-safe message.
759
+ *
760
+ * @param error - The caught value
761
+ * @returns A non-empty message without stack or cause data
762
+ */
763
+ export declare function errorToMessage(error: unknown): string;
764
+
731
765
  /**
732
766
  * Box an error as a {@link Failure} — the graceful outcome half of a {@link Result}.
733
767
  *
@@ -765,6 +799,9 @@ export declare function failure<E>(error: E): Failure<E>;
765
799
  */
766
800
  export declare function findFailure(results: readonly TaskResult[]): TaskResult | undefined;
767
801
 
802
+ /** Test that every present behavior reference resolves before dispatch. */
803
+ export declare function hasWorkflowHandlers(snapshot: WorkflowSnapshot, functions: WorkflowFunctions | undefined): boolean;
804
+
768
805
  /**
769
806
  * Insert one `[key, value]` entry at a positional index into a readonly entries array —
770
807
  * the pure splice-in step behind an insertion-ordered registry's `add`.
@@ -791,6 +828,34 @@ export declare function findFailure(results: readonly TaskResult[]): TaskResult
791
828
  */
792
829
  export declare function insertEntry<T>(entries: readonly (readonly [string, T])[], index: number, key: string, value: T): readonly (readonly [string, T])[];
793
830
 
831
+ /** Test the workflow lifecycle vocabulary. */
832
+ export declare function isLifecycleStatus(value: unknown): value is LifecycleStatus;
833
+
834
+ /**
835
+ * Validate a safe owned JSON graph as a coherent workflow snapshot.
836
+ *
837
+ * @remarks
838
+ * Callers at hostile boundaries use {@link isWorkflowSnapshot}, which owns the
839
+ * graph first so this semantic pass never observes accessors or prototypes.
840
+ */
841
+ export declare function isOwnedWorkflowSnapshot(value: unknown): value is WorkflowSnapshot;
842
+
843
+ /**
844
+ * Test whether an unknown value is valid persisted task activity.
845
+ */
846
+ export declare function isTaskActivity(value: unknown): value is TaskActivity;
847
+
848
+ /**
849
+ * Test whether an unknown value is a valid whole-frame activity report.
850
+ */
851
+ export declare function isTaskActivityInput(value: unknown): value is TaskActivityInput;
852
+
853
+ /** Test a normalized persisted task failure. */
854
+ export declare function isTaskFailure(value: unknown): value is TaskFailure;
855
+
856
+ /** Test a result's lineage against its containing snapshot nodes. */
857
+ export declare function isTaskResult(value: unknown, workflow: unknown, phase: unknown, task: unknown): value is TaskResult;
858
+
794
859
  /**
795
860
  * Test whether a {@link LifecycleStatus} is TERMINAL — a node in this state will not
796
861
  * transition further.
@@ -825,24 +890,7 @@ export declare function isTerminalStatus(status: LifecycleStatus): boolean;
825
890
  */
826
891
  export declare function isWorkflowError(value: unknown): value is WorkflowError;
827
892
 
828
- /**
829
- * Narrow an `unknown` to a {@link WorkflowSnapshot} — the AGENTS §14 boundary guard for an
830
- * UNTRUSTED snapshot read (a storage row a {@link import('./stores/DatabaseWorkflowStore.js').DatabaseWorkflowStore}
831
- * reads back from its opaque JSON column, a snapshot loaded from disk).
832
- *
833
- * @remarks
834
- * A total guard (it NEVER throws — adversarial input returns `false`, AGENTS §14). It checks the
835
- * snapshot's SHAPE — `id` / `name` / `status` strings, a `boolean` `bail`, an array of `phases`,
836
- * `created` / `updated` numbers — enough to safely impose the {@link WorkflowSnapshot} type at a
837
- * storage boundary WITHOUT a cast. It is complementary to
838
- * {@link import('./factories.js').assertSnapshot}, which validates the DEEPER invariant (every
839
- * node's status / override drawn from the lifecycle vocabulary) and THROWS a `RESTORE`
840
- * {@link import('./errors.js').WorkflowError} — the deep gate a {@link import('./factories.js').restoreWorkflow}
841
- * applies. A boundary read narrows shape with this guard; a restore validates vocabulary with `assertSnapshot`.
842
- *
843
- * @param value - The value to test (an opaque storage read)
844
- * @returns `true` when `value` has the structural shape of a {@link WorkflowSnapshot}
845
- */
893
+ /** Total hostile-boundary workflow snapshot guard. */
846
894
  export declare function isWorkflowSnapshot(value: unknown): value is WorkflowSnapshot;
847
895
 
848
896
  /**
@@ -861,6 +909,14 @@ export declare function isWorkflowSnapshot(value: unknown): value is WorkflowSna
861
909
  */
862
910
  export declare type LifecycleStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped' | 'stopped';
863
911
 
912
+ /** Compare two optional description values. */
913
+ export declare function matchesDescription(left: unknown, right: unknown): boolean;
914
+
915
+ /**
916
+ * The largest delay representable by the host timer APIs without overflow or clamping.
917
+ */
918
+ export declare const MAX_TIMER_MS = 2147483647;
919
+
864
920
  /**
865
921
  * The in-memory {@link WorkflowStoreInterface} — a process-lifetime `Map` of
866
922
  * {@link WorkflowSnapshot}s keyed by workflow id, the DEFAULT store
@@ -972,9 +1028,11 @@ export declare function parkSignal(signal: AbortSignal): Promise<void>;
972
1028
  * `results()` collects the settled tasks' {@link TaskResult}s (the phase tier of the result
973
1029
  * tree); `workflow` navigates UP to the live parent.
974
1030
  * - **Observable (AGENTS §13).** The owned {@link emitter} ({@link PhaseEventMap}) fires
975
- * `start` / `complete` / `fail` / `stop` on a derived-status CHANGE, strictly AFTER the
976
- * recompute + escalate; the emitter isolates a listener throw and routes it to its `error`
977
- * handler (the `error` option); `fail` carries the failing task's {@link TaskResult}.
1031
+ * `start` / `complete` / `fail` / `pause` / `resume` / `skip` / `stop` after the
1032
+ * corresponding status or runtime-gate change. Status events fire after the phase recomputes
1033
+ * and before it escalates to the workflow, preserving child/phase cause before parent effect.
1034
+ * The emitter isolates a listener throw and routes it to its `error` handler (the `error`
1035
+ * option); `fail` carries the failing task's {@link TaskResult}.
978
1036
  * - **Structural API (AGENTS §7).** `add` / `remove` / `move` / `update` gate BEFORE
979
1037
  * delegating to {@link tasks} (the manager gates the target's own existence/status/id/
980
1038
  * bounds), then emit the matching {@link PhaseEventMap} event on success only. NATIVE
@@ -994,7 +1052,8 @@ export declare function parkSignal(signal: AbortSignal): Promise<void>;
994
1052
  * {@link import('../types.js').WorkflowFunctions} registry (threaded from
995
1053
  * {@link import('../types.js').WorkflowOptions.functions}) resolves each task's `run` name into
996
1054
  * its runtime {@link import('../types.js').TaskInterface.handler} ONCE; a `run` that is omitted
997
- * or unregistered resolves to no handler (the no-handler rule).
1055
+ * or unregistered resolves to no handler; only an omitted `run` is a no-op, while an
1056
+ * unresolved present name makes the containing tree non-drivable.
998
1057
  * - **Runtime lifecycle (AGENTS §10).** `pause` / `resume` / `wait` mirror the workflow's own
999
1058
  * quartet, scoped to this phase — a driving
1000
1059
  * {@link import('../types.js').WorkflowRunnerInterface.execute} gates a task's own
@@ -1007,7 +1066,7 @@ export declare function parkSignal(signal: AbortSignal): Promise<void>;
1007
1066
  export declare class Phase implements PhaseInterface {
1008
1067
  #private;
1009
1068
  readonly description?: string;
1010
- constructor(snapshot: PhaseSnapshot, workflow: WorkflowInterface, escalate: () => void, options?: PhaseOptions, bail?: boolean, functions?: WorkflowFunctions);
1069
+ constructor(snapshot: PhaseSnapshot, workflow: WorkflowInterface, escalate: () => void, options?: PhaseOptions, bail?: boolean, functions?: WorkflowFunctions, silence?: number);
1011
1070
  get emitter(): EmitterInterface<PhaseEventMap>;
1012
1071
  get id(): string;
1013
1072
  get name(): string;
@@ -1116,8 +1175,10 @@ export declare interface PhaseDerivation {
1116
1175
  * @remarks
1117
1176
  * `start` fires when the phase begins; `complete` when all its tasks settled
1118
1177
  * successfully; `fail` when a task failed under `bail` (carrying the
1119
- * {@link TaskResult}); `stop` when the phase was ended. `add` / `remove` / `move` /
1120
- * `update` fire on a successful structural or patch edit through
1178
+ * {@link TaskResult}); `pause` / `resume` when its runtime gate closes / opens;
1179
+ * `skip` when the phase was intentionally skipped; `stop` when the phase was ended.
1180
+ * `add` / `remove` / `move` / `update` fire on a successful
1181
+ * structural or patch edit through
1121
1182
  * {@link PhaseInterface.add} / `remove` / `move` / `update` (AGENTS §7) — never on a
1122
1183
  * refused/gated one. A throwing listener is isolated by the emitter and routed to its
1123
1184
  * `error` handler, not the domain surface (AGENTS §13). A `type` alias (AGENTS §4.5)
@@ -1130,6 +1191,12 @@ export declare type PhaseEventMap = {
1130
1191
  readonly complete: readonly [];
1131
1192
  /** A task failed under `bail` — the failing task's result. */
1132
1193
  readonly fail: readonly [result: TaskResult];
1194
+ /** The phase's runtime gate closed. */
1195
+ readonly pause: readonly [];
1196
+ /** The phase's runtime gate opened. */
1197
+ readonly resume: readonly [];
1198
+ /** The phase was intentionally skipped. */
1199
+ readonly skip: readonly [];
1133
1200
  /** The phase was permanently stopped. */
1134
1201
  readonly stop: readonly [];
1135
1202
  /** A task was inserted — the inserted task + its final index. */
@@ -1165,8 +1232,9 @@ export declare type PhaseInput = Partial<PhaseContext>;
1165
1232
  * - **Override.** `skip` / `stop` (AGENTS §10) FORCE the phase's status, overriding the
1166
1233
  * derived value (e.g. skipping a whole phase); the override survives a snapshot.
1167
1234
  * - **Observable (AGENTS §13).** The owned {@link emitter} ({@link PhaseEventMap}) fires
1168
- * `start` / `complete` / `fail` / `stop` on a derived-status change; the emitter isolates a
1169
- * listener throw and routes it to its `error` handler (the `error` option).
1235
+ * `start` / `complete` / `fail` / `pause` / `resume` / `stop` after the corresponding
1236
+ * status or runtime-gate change; the emitter isolates a listener throw and routes it to
1237
+ * its `error` handler (the `error` option).
1170
1238
  * - **Runtime lifecycle (AGENTS §10).** `pause` / `resume` / `wait` mirror
1171
1239
  * {@link WorkflowInterface.pause} / `resume` / `wait`, scoped to this phase — a driving
1172
1240
  * {@link WorkflowRunnerInterface.execute} gates a task's own pre-dispatch on BOTH the
@@ -1532,10 +1600,10 @@ export declare interface PhaseSnapshot {
1532
1600
  * derived from its tasks' statuses.
1533
1601
  *
1534
1602
  * @remarks
1535
- * A semantic tier of the shared {@link LifecycleStatus} vocabulary. A phase is
1536
- * `failed` only when a task failed AND the workflow's `bail` policy is in force (a
1537
- * failure under graceful mode is data, not a phase failure). `stopped` propagates
1538
- * when every task was stopped. See {@link import('./helpers.js').derivePhaseStatus}.
1603
+ * A semantic tier of the shared {@link LifecycleStatus} vocabulary. A failed task makes
1604
+ * its phase `failed` regardless of policy; the phase's effective `bail` determines whether
1605
+ * that failure propagates to the workflow or is retained as graceful result data. `stopped`
1606
+ * propagates when every task was stopped. See {@link import('./helpers.js').derivePhaseStatus}.
1539
1607
  */
1540
1608
  export declare type PhaseStatus = LifecycleStatus;
1541
1609
 
@@ -1578,6 +1646,33 @@ export declare const phaseUpdateShape: ObjectShape<{
1578
1646
  bail: OptionalShape<LiteralShape<readonly [true, false]>>;
1579
1647
  }, false>;
1580
1648
 
1649
+ /**
1650
+ * Rebuild an interrupted workflow at its remaining retry budget.
1651
+ *
1652
+ * @param snapshot - The hostile persisted snapshot
1653
+ * @param options - Runtime handlers and entity options
1654
+ * @returns A recoverable live workflow
1655
+ */
1656
+ export declare function recoverWorkflow(snapshot: unknown, options?: WorkflowOptions): WorkflowInterface;
1657
+
1658
+ /**
1659
+ * Convert interrupted running work into a recoverable pending suffix or an
1660
+ * exhausted recovery failure without replenishing attempts.
1661
+ *
1662
+ * @param snapshot - A fully validated owned snapshot with no terminal overrides
1663
+ * @returns The recovery projection
1664
+ */
1665
+ export declare function recoverWorkflowSnapshot(snapshot: WorkflowSnapshot): WorkflowSnapshot;
1666
+
1667
+ /**
1668
+ * Resolve a task's runtime silence window against its workflow default.
1669
+ *
1670
+ * @param value - The task-level override; any present non-positive or non-finite value disables
1671
+ * @param fallback - The workflow-level default
1672
+ * @returns A host-safe effective window (`1..MAX_TIMER_MS`), or `undefined`
1673
+ */
1674
+ export declare function resolveTaskSilence(value: number | undefined, fallback: number | undefined): number | undefined;
1675
+
1581
1676
  /**
1582
1677
  * Rebuild an equivalent live W-b entity tree from a {@link WorkflowSnapshot} — the
1583
1678
  * inverse of {@link WorkflowInterface.snapshot}, restoring structure + each node's status
@@ -1594,6 +1689,9 @@ export declare const phaseUpdateShape: ObjectShape<{
1594
1689
  * still wins when supplied (to deliberately re-run under a different policy). A structurally
1595
1690
  * invalid snapshot (a status — or override — outside the lifecycle vocabulary, or a
1596
1691
  * non-boolean `bail`) throws a `RESTORE` {@link WorkflowError}.
1692
+ * Runtime handlers are optional: without a matching `functions` entry, a persisted `run`
1693
+ * remains visible with an undefined `handler` so the exact state is inspectable. The runner
1694
+ * rejects that unresolved tree if execution is attempted.
1597
1695
  *
1598
1696
  * @param snapshot - The snapshot to restore (carries its own `bail` + `override`)
1599
1697
  * @param options - Runtime options (initial listeners, an optional `bail` override, per-node options)
@@ -1607,7 +1705,7 @@ export declare const phaseUpdateShape: ObjectShape<{
1607
1705
  * restored.status === workflow.status // true
1608
1706
  * ```
1609
1707
  */
1610
- export declare function restoreWorkflow(snapshot: WorkflowSnapshot, options?: WorkflowOptions): WorkflowInterface;
1708
+ export declare function restoreWorkflow(snapshot: unknown, options?: WorkflowOptions): WorkflowInterface;
1611
1709
 
1612
1710
  /**
1613
1711
  * A thin generic orchestrator that drives declared units — and any they `spawn` —
@@ -2065,9 +2163,8 @@ export declare function success<T>(value: T): Success<T>;
2065
2163
  * `TRANSITION` {@link WorkflowError} on an illegal move (e.g. completing a non-`running`
2066
2164
  * task) — the legal graph is the single source of truth, so the leaf can never reach an
2067
2165
  * impossible state.
2068
- * - **Override (snapshot fidelity).** `skip` / `stop` set `#override` to the forced terminal
2069
- * status, so a RESTORE can tell a forced leaf (`skipped` / `stopped`) from a run-produced
2070
- * one and reinstate it AS an override — preserving the round-trip.
2166
+ * - **Snapshot fidelity.** A leaf needs no override: `skipped` / `stopped` are explicit terminal
2167
+ * statuses, and restore reinstates the leaf directly from {@link TaskSnapshot.status}.
2071
2168
  * - **The cascade.** Every status change records its boxed result (when any), fires the leaf's
2072
2169
  * OWN event, THEN calls the parent phase's `#recompute` (injected at construction) so the
2073
2170
  * transition propagates UP (Task → Phase → Workflow re-derive). The own-event-before-cascade
@@ -2082,12 +2179,13 @@ export declare function success<T>(value: T): Success<T>;
2082
2179
  * matching {@link import('../types.js').TaskDefinition} / {@link TaskSnapshot} field. `handler`
2083
2180
  * is the RUNTIME-ONLY counterpart — `run` resolved ONCE at construction against the
2084
2181
  * workflow-level {@link import('../types.js').WorkflowOptions.functions} registry — and is
2085
- * NEVER persisted; `undefined` when `run` is omitted or unregistered (the no-handler rule).
2182
+ * NEVER persisted; `undefined` when `run` is omitted or unregistered. Only omission is a
2183
+ * deliberate no-op; unresolved named work is rejected before dispatch.
2086
2184
  */
2087
2185
  export declare class Task implements TaskInterface {
2088
2186
  #private;
2089
2187
  readonly description?: string;
2090
- constructor(context: TaskContext, phase: PhaseInterface, workflow: WorkflowInterface, recompute: () => void, options?: TaskOptions, status?: TaskStatus, result?: TaskResult, run?: string, retries?: number, timeout?: number, handler?: WorkflowFunction);
2188
+ constructor(context: TaskContext, phase: PhaseInterface, workflow: WorkflowInterface, recompute: () => void, options?: TaskOptions, status?: TaskStatus, result?: TaskResult, run?: string, retries?: number, timeout?: number, metadata?: JSONRecord, attempts?: number, activity?: TaskActivity, handler?: WorkflowFunction, silence?: number);
2091
2189
  get emitter(): EmitterInterface<TaskEventMap>;
2092
2190
  get id(): string;
2093
2191
  get name(): string;
@@ -2096,15 +2194,26 @@ export declare class Task implements TaskInterface {
2096
2194
  get workflow(): WorkflowInterface;
2097
2195
  get status(): TaskStatus;
2098
2196
  get result(): TaskResult | undefined;
2197
+ get attempts(): number;
2099
2198
  get run(): string | undefined;
2100
2199
  get handler(): WorkflowFunction | undefined;
2101
2200
  get retries(): number | undefined;
2102
2201
  get timeout(): number | undefined;
2202
+ get activity(): TaskActivity | undefined;
2203
+ get silence(): number | undefined;
2204
+ get silent(): boolean;
2205
+ get paused(): boolean;
2206
+ get signal(): AbortSignal;
2103
2207
  start(): void;
2104
- complete(value: unknown): void;
2105
- fail(error: unknown): void;
2208
+ complete(value: JSONValue): void;
2209
+ fail(error: TaskFailure): void;
2106
2210
  skip(): void;
2107
2211
  stop(): void;
2212
+ report(input: TaskActivityInput): Result<TaskActivity, WorkflowError>;
2213
+ pulse(): boolean;
2214
+ pause(): void;
2215
+ resume(): void;
2216
+ wait(): Promise<void>;
2108
2217
  /**
2109
2218
  * Apply a validated declarative patch to SELF (`name` / `description`).
2110
2219
  *
@@ -2148,6 +2257,44 @@ export declare const TASK_STATUSES: readonly TaskStatus[];
2148
2257
  */
2149
2258
  export declare const TASK_TRANSITIONS: Readonly<Record<TaskStatus, readonly TaskStatus[]>>;
2150
2259
 
2260
+ /**
2261
+ * The bounded, JSON-serializable activity most recently accepted from a task reporter.
2262
+ */
2263
+ export declare interface TaskActivity {
2264
+ readonly note?: string;
2265
+ readonly progress?: TaskProgress;
2266
+ readonly operations: readonly TaskOperation[];
2267
+ readonly constraints: readonly TaskConstraint[];
2268
+ readonly updated: number;
2269
+ }
2270
+
2271
+ /**
2272
+ * One complete replacement of a running task's observable activity.
2273
+ *
2274
+ * @remarks
2275
+ * Omitted `operations` or `constraints` mean an empty list. Omitted `progress` clears the
2276
+ * previous aggregate progress. Use {@link TaskInterface.report} to commit the replacement.
2277
+ */
2278
+ export declare interface TaskActivityInput {
2279
+ readonly note?: string;
2280
+ readonly progress?: TaskProgress;
2281
+ readonly operations?: readonly TaskOperation[];
2282
+ readonly constraints?: readonly TaskConstraint[];
2283
+ }
2284
+
2285
+ /**
2286
+ * One constraint claimed active when a running task's complete frame was accepted.
2287
+ *
2288
+ * @remarks
2289
+ * Constraints describe active limits or requirements without embedding provider policy in
2290
+ * core. `id` is unique within one complete report and `started` is finite and non-negative.
2291
+ */
2292
+ export declare interface TaskConstraint {
2293
+ readonly id: string;
2294
+ readonly name: string;
2295
+ readonly started: number;
2296
+ }
2297
+
2151
2298
  /**
2152
2299
  * The ambient context of a task — its own identity plus a back-reference to the
2153
2300
  * phase (and, transitively, the workflow) it belongs to.
@@ -2162,18 +2309,17 @@ export declare interface TaskContext extends WorkflowContext {
2162
2309
  }
2163
2310
 
2164
2311
  /**
2165
- * The lean per-task handle a {@link import('./types.js').WorkflowFunction} receives — the
2166
- * running task's folded cancellation, its input, its lineage, and read-UP access to the
2167
- * result tree.
2312
+ * The attempt-scoped handle a {@link import('./types.js').WorkflowFunction} receives.
2168
2313
  *
2169
2314
  * @remarks
2170
2315
  * - **A leaf handle, NOT the runner `Controller`.** A workflow task is a leaf of the
2171
- * declarative W-b tree, not a fan-out unit, so this carries none of the runner
2172
- * `Controller`'s `spawn` / `wait` only what a leaf needs.
2173
- * - **Folded signal.** `signal` is the cancellation the runner folds for THIS run: it fires
2174
- * on a workflow-level abort / timeout / budget ceiling, or under `bail: true` — when a
2175
- * sibling task fails (the runner aborts the in-flight siblings via the substrate's
2176
- * fail-fast). A handler races its work against it; `aborted` reads it.
2316
+ * declarative W-b tree, not a fan-out unit, so it has no `spawn`; its `wait` instead
2317
+ * checkpoints the workflow, phase, and task cooperative gates.
2318
+ * - **Folded signal.** `signal` is the cancellation folded for THIS attempt: its per-attempt
2319
+ * deadline, task stop/skip, workflow abort/timeout/budget/destroy, or a sibling fail-fast.
2320
+ * A handler races its work against it; `aborted` reads it.
2321
+ * - **Attempt ownership.** `report` / `pulse` are closures supplied by the runner and refuse
2322
+ * after this signal aborts or a retry token supersedes this handle.
2177
2323
  * - **Input + lineage.** `input` is the task's open `metadata` bag (its
2178
2324
  * {@link import('./types.js').TaskInput} payload, `{}` when none); `task` is the full
2179
2325
  * {@link TaskContext}, so `task.phase` / `task.phase.workflow` navigate UP the lineage.
@@ -2188,10 +2334,15 @@ export declare interface TaskContext extends WorkflowContext {
2188
2334
  declare class TaskController_2 implements TaskControllerInterface {
2189
2335
  #private;
2190
2336
  readonly signal: AbortSignal;
2191
- readonly input: Readonly<Record<string, unknown>>;
2337
+ readonly input: JSONRecord;
2192
2338
  readonly task: TaskContext;
2193
- constructor(signal: AbortSignal, input: Readonly<Record<string, unknown>>, task: TaskContext, results: () => readonly TaskResult[]);
2339
+ readonly attempt: number;
2340
+ constructor(signal: AbortSignal, input: JSONRecord, task: TaskInterface, attempt: number, results: () => readonly TaskResult[], report: (input: TaskActivityInput) => Result<TaskActivity, WorkflowError>, pulse: () => boolean);
2194
2341
  get aborted(): boolean;
2342
+ get paused(): boolean;
2343
+ report(input: TaskActivityInput): Result<TaskActivity, WorkflowError>;
2344
+ pulse(): boolean;
2345
+ wait(): Promise<void>;
2195
2346
  results(): readonly TaskResult[];
2196
2347
  }
2197
2348
  export { TaskController_2 as TaskController }
@@ -2201,26 +2352,49 @@ export { TaskController_2 as TaskController }
2201
2352
  * cancellation, its input, its lineage, and read-UP access to the result tree.
2202
2353
  *
2203
2354
  * @remarks
2204
- * A NEW, lean handle (NOT the runner `Controller` — it carries no `spawn` / `wait`; a
2205
- * workflow task is a leaf of the declarative tree, not a fan-out unit). It exposes:
2206
- * - `signal` — the task's folded cancellation: fires on a workflow-level `abort` /
2207
- * `timeout` / `budget` ceiling, or — under `bail: true` — when a sibling task fails (the
2208
- * runner aborts the in-flight siblings). A handler races its work against it.
2355
+ * A NEW, lean handle (NOT the runner `Controller` — it carries no `spawn`; a workflow task
2356
+ * is a leaf of the declarative tree, not a fan-out unit). It exposes:
2357
+ * - `signal` — this attempt's folded cancellation: its per-attempt deadline, task
2358
+ * stop/skip, workflow abort/timeout/budget/destroy, or a sibling fail-fast.
2209
2359
  * - `aborted` — whether `signal` has fired.
2210
2360
  * - `input` — the task's `metadata` bag (the open consumer payload from its
2211
2361
  * {@link TaskInput}); `{}` when none.
2212
2362
  * - `task` — the task's full {@link TaskContext} (so `task.phase` / `task.phase.workflow`
2213
2363
  * navigate UP the lineage).
2364
+ * - `wait()` — a cooperative checkpoint for the workflow, phase, and task pause gates.
2214
2365
  * - `results()` — every settled task's {@link TaskResult} across already-finished phases,
2215
2366
  * so a `function` task can read an earlier phase's output (the W-b result tree, read-only).
2216
2367
  */
2217
2368
  export declare interface TaskControllerInterface {
2218
- /** Fires on a workflow-level abort / timeout / budget, or a sibling failure under `bail: true`. */
2369
+ /** Fires on this attempt's deadline, task stop/skip, run cancellation, or sibling fail-fast. */
2219
2370
  readonly signal: AbortSignal;
2220
2371
  readonly aborted: boolean;
2221
2372
  /** The task's open `metadata` bag (its {@link TaskInput} payload); `{}` when none. */
2222
- readonly input: Readonly<Record<string, unknown>>;
2373
+ readonly input: JSONRecord;
2223
2374
  readonly task: TaskContext;
2375
+ /** The one-based persisted launch represented by this handle. */
2376
+ readonly attempt: number;
2377
+ /** Whether the workflow, phase, or task cooperative gate is currently paused. */
2378
+ readonly paused: boolean;
2379
+ /**
2380
+ * Replace this running task's complete observable activity.
2381
+ *
2382
+ * @param input - The complete operations, progress, and constraints replacement
2383
+ * @returns The accepted frame, or a `TRANSITION` failure after ownership is lost or this attempt aborts
2384
+ */
2385
+ report(input: TaskActivityInput): Result<TaskActivity, WorkflowError>;
2386
+ /**
2387
+ * Confirm liveness without replacing current activity.
2388
+ *
2389
+ * @returns `true` when committed, or `false` after ownership is lost or this attempt aborts
2390
+ */
2391
+ pulse(): boolean;
2392
+ /**
2393
+ * Cooperatively park while any workflow, phase, or task gate is paused, or until cancelled.
2394
+ *
2395
+ * @returns A promise that resolves when every applicable gate is open or the signal aborts
2396
+ */
2397
+ wait(): Promise<void>;
2224
2398
  /** Every settled task's result across already-finished phases — the result tree, read-only. */
2225
2399
  results(): readonly TaskResult[];
2226
2400
  }
@@ -2235,8 +2409,8 @@ export declare interface TaskControllerInterface {
2235
2409
  * its phase; `name` is the human label; `description` is optional prose. `run` is a
2236
2410
  * PLAIN NAME — a key resolved ONCE at construction against a workflow-level
2237
2411
  * {@link WorkflowFunctions} registry into a runtime {@link TaskInterface.handler}
2238
- * carried on the live task. A task whose `run` is omitted, or whose name is unregistered,
2239
- * has no handler and AUTO-COMPLETES (the no-handler rule).
2412
+ * carried on the live task. An omitted `run` is the deliberate no-op form and completes
2413
+ * with JSON `null`; an unresolved present name remains inspectable but is not executable.
2240
2414
  */
2241
2415
  export declare interface TaskDefinition {
2242
2416
  readonly id: string;
@@ -2254,11 +2428,10 @@ export declare interface TaskDefinition {
2254
2428
  readonly retries?: number;
2255
2429
  /**
2256
2430
  * @remarks
2257
- * The per-attempt deadline in milliseconds (a non-negative integer); the runner threads it to
2258
- * this task's substrate unit, OVERRIDING the phase Runner's `timeout` default. Omitted (or a
2259
- * non-positive value) no deadline. PERSISTED in a {@link TaskSnapshot} (like `bail` and
2260
- * `concurrency`), so `restoreWorkflow(snapshot, { functions })` resumes with the same
2261
- * reliability config; only the resolved handler itself is runtime-only.
2431
+ * The workflow-owned per-attempt deadline in milliseconds, an integer from `0` through
2432
+ * `MAX_TIMER_MS`. Zero or omission means no deadline. PERSISTED in a {@link TaskSnapshot},
2433
+ * so `restoreWorkflow(snapshot, { functions })` resumes with the same reliability config;
2434
+ * only the resolved handler itself is runtime-only.
2262
2435
  */
2263
2436
  readonly timeout?: number;
2264
2437
  }
@@ -2286,7 +2459,8 @@ export declare function taskDefinitionToSnapshot(task: WorkflowDefinition['phase
2286
2459
  * @remarks
2287
2460
  * `start` fires when the task begins; `complete` when it finishes successfully
2288
2461
  * (carrying its {@link TaskResult}); `fail` when it errors (carrying the result);
2289
- * `skip` when it is intentionally not executed; `stop` when it is ended early. A
2462
+ * `pause` / `resume` when its runtime gate closes / opens; `skip` when it is
2463
+ * intentionally not executed; `stop` when it is ended early. A
2290
2464
  * throwing listener is isolated by the emitter and routed to its `error` handler,
2291
2465
  * not the domain surface (AGENTS §13). A `type` alias (AGENTS §4.5) so it satisfies
2292
2466
  * `EventMap`.
@@ -2298,12 +2472,46 @@ export declare type TaskEventMap = {
2298
2472
  readonly complete: readonly [result: TaskResult];
2299
2473
  /** The task failed — its result. */
2300
2474
  readonly fail: readonly [result: TaskResult];
2475
+ /** The task's runtime gate closed. */
2476
+ readonly pause: readonly [];
2477
+ /** The task's runtime gate opened. */
2478
+ readonly resume: readonly [];
2301
2479
  /** The task was intentionally skipped. */
2302
2480
  readonly skip: readonly [];
2303
2481
  /** The task was permanently stopped. */
2304
2482
  readonly stop: readonly [];
2483
+ /** A complete activity replacement was committed. */
2484
+ readonly report: readonly [activity: TaskActivity];
2485
+ /** The task confirmed liveness without replacing its current activity. */
2486
+ readonly pulse: readonly [activity: TaskActivity];
2487
+ /** No report or pulse was accepted during the effective silence window. */
2488
+ readonly silence: readonly [];
2305
2489
  };
2306
2490
 
2491
+ /** A normalized JSON-safe task failure persisted without a stack or cause. */
2492
+ export declare interface TaskFailure {
2493
+ readonly origin: TaskFailureOrigin;
2494
+ readonly message: string;
2495
+ }
2496
+
2497
+ /**
2498
+ * The structured outcome of a task execution — its full lineage, its terminal
2499
+ * status, the moment it settled, and its boxed produced outcome.
2500
+ *
2501
+ * @remarks
2502
+ * Carries the complete lineage (`task` / `phase` / `workflow` contexts) so a result
2503
+ * is self-describing wherever it travels. `status` is the terminal state this
2504
+ * result records. `result` BOXES the produced outcome in a {@link Result}: it is
2505
+ * PRESENT exactly when `status` is `completed` (a {@link import('@orkestrel/contract').Success})
2506
+ * or `failed` (a {@link import('@orkestrel/contract').Failure}), and ABSENT when `status` is
2507
+ * `skipped` or `stopped` (terminal, but produced no outcome) — a pending/running
2508
+ * task has no result at all (a non-terminal status, per
2509
+ * {@link import('./helpers.js').isTerminalStatus}). This boxed `result` REPLACES separate
2510
+ * `value?` / `error?` fields: a success's payload is `result.value`, a failure's reason is `result.error`.
2511
+ * `timestamp` is when the result was created (ms since epoch).
2512
+ */
2513
+ export declare type TaskFailureOrigin = 'handler' | 'timeout' | 'recovery';
2514
+
2307
2515
  /** Initial {@link TaskEventMap} listeners — the reserved `on` option (AGENTS §8). */
2308
2516
  export declare type TaskHooks = EmitterHooks<TaskEventMap>;
2309
2517
 
@@ -2318,7 +2526,7 @@ export declare type TaskHooks = EmitterHooks<TaskEventMap>;
2318
2526
  */
2319
2527
  export declare interface TaskInput extends Partial<TaskContext> {
2320
2528
  /** An open consumer bag — stored and snapshotted, never interpreted by the workflow. */
2321
- readonly metadata?: Readonly<Record<string, unknown>>;
2529
+ readonly metadata?: JSONRecord;
2322
2530
  }
2323
2531
 
2324
2532
  /**
@@ -2337,13 +2545,15 @@ export declare interface TaskInput extends Partial<TaskContext> {
2337
2545
  * {@link import('@orkestrel/contract').Failure}), `skip` (AGENTS §10 — intentionally not run),
2338
2546
  * and `stop` (AGENTS §10 — ended early). Each is GUARDED: an illegal transition (e.g.
2339
2547
  * completing a non-`running` task) throws a {@link import('./errors.js').WorkflowError}.
2340
- * `skip` / `stop` set the override so the leaf's terminal state survives a snapshot.
2548
+ * A leaf needs no override: `skipped` / `stopped` are explicit terminal statuses and
2549
+ * restore directly from {@link TaskSnapshot.status}.
2341
2550
  * - **Result.** `result` is the recorded {@link TaskResult} once the task settled with an
2342
2551
  * outcome (`completed` / `failed`), else `undefined` — the lineage-navigable leaf of the
2343
2552
  * result tree.
2344
2553
  * - **Observable (AGENTS §13).** The owned {@link emitter} ({@link TaskEventMap}) fires
2345
- * `start` / `complete` / `fail` / `skip` / `stop` strictly AFTER each transition; the
2346
- * emitter isolates a listener throw and routes it to its `error` handler (the `error` option).
2554
+ * `start` / `complete` / `fail` / `pause` / `resume` / `skip` / `stop` strictly AFTER
2555
+ * each state change; the emitter isolates a listener throw and routes it to its `error`
2556
+ * handler (the `error` option).
2347
2557
  */
2348
2558
  export declare interface TaskInterface {
2349
2559
  readonly emitter: EmitterInterface<TaskEventMap>;
@@ -2354,6 +2564,8 @@ export declare interface TaskInterface {
2354
2564
  readonly phase: PhaseInterface;
2355
2565
  readonly workflow: WorkflowInterface;
2356
2566
  readonly status: TaskStatus;
2567
+ /** Total launches already consumed; zero while fresh and one-based after launch. */
2568
+ readonly attempts: number;
2357
2569
  /** The recorded outcome once the task settled with one (`completed` / `failed`), else `undefined`. */
2358
2570
  readonly result: TaskResult | undefined;
2359
2571
  /**
@@ -2366,9 +2578,9 @@ export declare interface TaskInterface {
2366
2578
  * The RESOLVED runtime handler — RUNTIME-ONLY, NEVER persisted in a {@link TaskSnapshot}.
2367
2579
  * Resolved ONCE at construction (build, restore, or a live mint) by looking `run` up in the
2368
2580
  * workflow-level {@link WorkflowOptions.functions} registry: `functions?.[run]` when `run`
2369
- * is defined, else `undefined`. A task with no `handler` (an omitted `run`, or a `run` name
2370
- * absent from the registry) AUTO-COMPLETES (the no-handler rule) its phase/workflow still
2371
- * reaches a terminal status, just with no dispatched behavior.
2581
+ * is defined, else `undefined`. An omitted `run` is the deliberate no-op form. A present,
2582
+ * unresolved `run` remains visible on exact restore, but the runner rejects it before
2583
+ * dispatch instead of falsely completing named work.
2372
2584
  */
2373
2585
  readonly handler: WorkflowFunction | undefined;
2374
2586
  /**
@@ -2377,15 +2589,49 @@ export declare interface TaskInterface {
2377
2589
  */
2378
2590
  readonly retries: number | undefined;
2379
2591
  /**
2380
- * The per-attempt deadline in milliseconds — PERSISTED (mirrors {@link TaskDefinition.timeout}
2381
- * / {@link TaskSnapshot.timeout}). `undefined` ⇒ no deadline.
2592
+ * The workflow-owned per-attempt deadline in milliseconds (`0..MAX_TIMER_MS`) — PERSISTED
2593
+ * (mirrors {@link TaskDefinition.timeout} / {@link TaskSnapshot.timeout}). Zero or
2594
+ * `undefined` means no deadline.
2382
2595
  */
2383
2596
  readonly timeout: number | undefined;
2597
+ /** The last accepted reporter claim, absent while pending. */
2598
+ readonly activity: TaskActivity | undefined;
2599
+ /** The effective host-safe silence window (`1..MAX_TIMER_MS`), or `undefined` when disabled. */
2600
+ readonly silence: number | undefined;
2601
+ /** Whether no report or pulse was accepted during the current silence window. */
2602
+ readonly silent: boolean;
2603
+ /** Whether this task's cooperative execution gate is paused. */
2604
+ readonly paused: boolean;
2605
+ /** This task's own cancellation signal; running/pending {@link stop} or {@link skip} fires it. */
2606
+ readonly signal: AbortSignal;
2384
2607
  start(): void;
2385
- complete(value: unknown): void;
2386
- fail(error: unknown): void;
2608
+ complete(value: JSONValue): void;
2609
+ fail(error: TaskFailure): void;
2387
2610
  skip(): void;
2388
2611
  stop(): void;
2612
+ /**
2613
+ * Replace the complete observable activity of this running task.
2614
+ *
2615
+ * @param input - The complete operations, progress, and constraints replacement
2616
+ * @returns The accepted immutable frame; `MUTATION` for invalid input or `TRANSITION` when not running
2617
+ */
2618
+ report(input: TaskActivityInput): Result<TaskActivity, WorkflowError>;
2619
+ /**
2620
+ * Confirm liveness without replacing the current operations, progress, or constraints.
2621
+ *
2622
+ * @returns `true` when committed, or `false` when the task is not running
2623
+ */
2624
+ pulse(): boolean;
2625
+ /** Suspend this task's cooperative gate while pending or running; idempotent. */
2626
+ pause(): void;
2627
+ /** Continue this task's cooperative gate; idempotent. */
2628
+ resume(): void;
2629
+ /**
2630
+ * Park until this task is not paused.
2631
+ *
2632
+ * @returns A promise that resolves once the task gate is released
2633
+ */
2634
+ wait(): Promise<void>;
2389
2635
  /**
2390
2636
  * Apply a validated declarative patch to SELF (`name` / `description`).
2391
2637
  *
@@ -2514,6 +2760,19 @@ export declare interface TaskManagerInterface {
2514
2760
  tasks(): readonly TaskInterface[];
2515
2761
  }
2516
2762
 
2763
+ /**
2764
+ * One operation claimed active when a running task's complete frame was accepted.
2765
+ *
2766
+ * @remarks
2767
+ * `id` is stable within one complete activity report, `name` is the human-readable label,
2768
+ * and `started` is a finite non-negative reporter timestamp.
2769
+ */
2770
+ export declare interface TaskOperation {
2771
+ readonly id: string;
2772
+ readonly name: string;
2773
+ readonly started: number;
2774
+ }
2775
+
2517
2776
  /**
2518
2777
  * The runtime options for a {@link TaskInterface} — the construction bag the live
2519
2778
  * leaf state machine (W-b) carries that the W-a {@link TaskDefinition} did not.
@@ -2531,32 +2790,31 @@ export declare interface TaskOptions {
2531
2790
  /** The emitter's listener-error handler (AGENTS §13) — a listener throw routes here, not to a domain event. */
2532
2791
  readonly error?: EmitterErrorHandler;
2533
2792
  /** An open consumer bag — stored and snapshotted, never interpreted by the workflow. */
2534
- readonly metadata?: Readonly<Record<string, unknown>>;
2793
+ readonly metadata?: JSONRecord;
2794
+ /** Runtime-only silence window; non-positive, non-finite, or over-`MAX_TIMER_MS` disables inheritance. */
2795
+ readonly silence?: number;
2535
2796
  }
2536
2797
 
2537
2798
  /**
2538
- * The structured outcome of a task execution its full lineage, its terminal
2539
- * status, the moment it settled, and its boxed produced outcome.
2799
+ * The aggregate progress most recently reported by a running task.
2540
2800
  *
2541
2801
  * @remarks
2542
- * Carries the complete lineage (`task` / `phase` / `workflow` contexts) so a result
2543
- * is self-describing wherever it travels. `status` is the terminal state this
2544
- * result records. `result` BOXES the produced outcome in a {@link Result}: it is
2545
- * PRESENT exactly when `status` is `completed` (a {@link import('@orkestrel/contract').Success})
2546
- * or `failed` (a {@link import('@orkestrel/contract').Failure}), and ABSENT when `status` is
2547
- * `skipped` or `stopped` (terminal, but produced no outcome) — a pending/running
2548
- * task has no result at all (a non-terminal status, per
2549
- * {@link import('./helpers.js').isTerminalStatus}). This boxed `result` REPLACES separate
2550
- * `value?` / `error?` fields: a success's payload is `result.value`, a failure's reason is `result.error`.
2551
- * `timestamp` is when the result was created (ms since epoch).
2802
+ * `current` and an optional `total` are finite non-negative numbers; when `total` is present
2803
+ * it is at least `current`. `unit` is optional observer-facing text.
2552
2804
  */
2805
+ export declare interface TaskProgress {
2806
+ readonly current: number;
2807
+ readonly total?: number;
2808
+ readonly unit?: string;
2809
+ }
2810
+
2553
2811
  export declare interface TaskResult {
2554
2812
  readonly task: TaskContext;
2555
2813
  readonly phase: PhaseContext;
2556
2814
  readonly workflow: WorkflowContext;
2557
2815
  readonly status: TaskStatus;
2558
2816
  /** The boxed outcome — present for `completed` (Success) / `failed` (Failure), absent otherwise. */
2559
- readonly result?: Result<unknown>;
2817
+ readonly result?: Result<JSONValue, TaskFailure>;
2560
2818
  readonly timestamp: number;
2561
2819
  }
2562
2820
 
@@ -2595,13 +2853,17 @@ export declare interface TaskSnapshot {
2595
2853
  readonly description?: string;
2596
2854
  readonly status: TaskStatus;
2597
2855
  readonly result?: TaskResult;
2598
- readonly metadata: Readonly<Record<string, unknown>>;
2856
+ readonly metadata: JSONRecord;
2857
+ /** Total launches already consumed; zero while fresh and never reset by recovery. */
2858
+ readonly attempts: number;
2599
2859
  /** The behavior reference — a registry key resolved against {@link WorkflowFunctions} on restore/build. */
2600
2860
  readonly run?: string;
2601
2861
  /** Extra attempts after the first on failure (a non-negative integer); overrides the phase Runner default. */
2602
2862
  readonly retries?: number;
2603
- /** The per-attempt deadline in milliseconds (a non-negative integer); overrides the phase Runner default. */
2863
+ /** Workflow-owned per-attempt deadline (`0..MAX_TIMER_MS`); zero or omission means disabled. */
2604
2864
  readonly timeout?: number;
2865
+ /** Pending omits activity; running/completed/failed require it; skipped/stopped may retain it. */
2866
+ readonly activity?: TaskActivity;
2605
2867
  }
2606
2868
 
2607
2869
  /**
@@ -2700,18 +2962,20 @@ export declare type UnitOutcome<TResult> = {
2700
2962
  * reachable ONLY under `bail: true` (a single failed task halts the workflow); under
2701
2963
  * `bail: false` a failed phase folds into `completed`. {@link #recompute} diffs on each phase
2702
2964
  * change; a CHANGE emits.
2703
- * - **Override (AGENTS §10).** `skip` / `stop` FORCE the status; the override is PERSISTED in the
2704
- * snapshot's own `override` field and restored DIRECTLY (no divergence guess). The snapshot also
2705
- * persists `bail`, so a restore re-derives status identically without a silent policy default.
2965
+ * - **Override (AGENTS §10).** `skip` / `stop` FORCE the status; an executed task-free pending tree
2966
+ * may also be force-completed vacuously. The override is PERSISTED in the snapshot's own
2967
+ * `override` field and restored DIRECTLY (no divergence guess). The snapshot also persists
2968
+ * `bail`, so a restore re-derives status identically without a silent policy default.
2706
2969
  * - **Result tree.** `results()` flattens every phase's `results()` ({@link collectResults}) — the
2707
2970
  * workflow tier; `phase(id)` + each `phase.task(id)` navigate DOWN, a task's `phase` / `workflow`
2708
2971
  * navigate UP.
2709
2972
  * - **Snapshot.** `snapshot()` serializes the whole live tree to a {@link WorkflowSnapshot} (pure
2710
2973
  * JSON); {@link import('./factories.js').restoreWorkflow} rebuilds an equivalent live tree.
2711
2974
  * - **Observable (AGENTS §13).** The owned {@link emitter} ({@link WorkflowEventMap}) fires
2712
- * `start` / `complete` / `fail` / `stop` on a derived-status CHANGE; the emitter isolates a
2713
- * listener throw and routes it to its `error` handler (the `error` option); `fail` carries
2714
- * the failing task's {@link TaskResult}.
2975
+ * `start` / `complete` / `fail` / `pause` / `resume` / `skip` / `stop` after the
2976
+ * corresponding status or runtime-gate change; the emitter isolates a listener throw and
2977
+ * routes it to its `error` handler (the `error` option); `fail` carries the failing task's
2978
+ * {@link TaskResult}.
2715
2979
  * - **Structural API (AGENTS §7).** `add` / `remove` / `move` / `update` gate BEFORE
2716
2980
  * delegating to {@link phases} (the manager gates the target's own existence/status/id/
2717
2981
  * bounds), then emit the matching {@link WorkflowEventMap} event on success only. NATIVE,
@@ -2723,10 +2987,10 @@ export declare type UnitOutcome<TResult> = {
2723
2987
  * naturally accepted.
2724
2988
  * - **Runtime lifecycle (AGENTS §10).** `pause` / `resume` / `wait` gate execution at the runner's
2725
2989
  * phase/task boundaries WITHOUT touching {@link status} — `paused` is runtime-only, never
2726
- * persisted. `destroy` is a terminal teardown: it aborts {@link signal}, `stop`s every
2727
- * non-terminal live phase (so an engine parked on a phase's own gate unparks and the tree
2728
- * lands coherent), forces the `stop` override on THIS workflow when not already terminal,
2729
- * releases any parked {@link wait} waiter, and marks {@link destroyed} — all four idempotent.
2990
+ * persisted. `destroy` is a terminal teardown: it `stop`s every non-terminal task and
2991
+ * phase (releasing their gates and liveness resources), aborts {@link signal}, forces the
2992
+ * workflow `stop` override when needed, releases its parked waiter, and marks
2993
+ * {@link destroyed} — all idempotent.
2730
2994
  */
2731
2995
  export declare class Workflow implements WorkflowInterface {
2732
2996
  #private;
@@ -2761,6 +3025,9 @@ export declare class Workflow implements WorkflowInterface {
2761
3025
  /** Every {@link WorkflowStatus} value, frozen — the lifecycle vocabulary of a workflow. */
2762
3026
  export declare const WORKFLOW_STATUSES: readonly WorkflowStatus[];
2763
3027
 
3028
+ /** A runner-owned durability boundary. */
3029
+ export declare type WorkflowCheckpoint = 'initial' | 'attempt' | 'settlement' | 'final';
3030
+
2764
3031
  /**
2765
3032
  * The ambient context of a workflow — the identity every level inherits.
2766
3033
  *
@@ -2797,18 +3064,13 @@ export declare interface WorkflowDefinition {
2797
3064
  }
2798
3065
 
2799
3066
  /**
2800
- * An error thrown by the workflow entity + W-c2 recursion layer.
3067
+ * An error raised by the workflow runtime.
2801
3068
  *
2802
3069
  * @remarks
2803
3070
  * Carries a {@link WorkflowErrorCode} and an optional `context` bag naming the
2804
- * offending node id / status. Thrown for an illegal lifecycle transition
3071
+ * offending node id / status. Raised for an illegal lifecycle transition
2805
3072
  * (`TRANSITION`), a structurally invalid {@link import('./types.js').WorkflowSnapshot}
2806
- * passed to {@link import('./factories.js').restoreWorkflow} (`RESTORE`), an over-deep /
2807
- * cyclic nested-workflow dispatch (`DEPTH`), and a malformed workflow-authoring-tool args
2808
- * blob (`TOOL`). `DEPTH` and `TOOL` are public type surface constructed by the
2809
- * `@orkestrel/tool` package's workflow-tool / agent-function adapters; on that seam the
2810
- * throw is ISOLATED by its `ToolManager` into the tool result's top-level `error`
2811
- * (AGENTS §14 — the universal tool-handler contract).
3073
+ * boundary (`RESTORE`), or a refused structural/activity edit (`MUTATION`).
2812
3074
  */
2813
3075
  export declare class WorkflowError extends Error {
2814
3076
  readonly code: WorkflowErrorCode;
@@ -2826,21 +3088,6 @@ export declare class WorkflowError extends Error {
2826
3088
  * the offending current status + requested transition in the error `context`.
2827
3089
  * - `RESTORE` — a {@link import('./factories.js').restoreWorkflow} given a structurally
2828
3090
  * invalid {@link WorkflowSnapshot} (a status outside the lifecycle vocabulary).
2829
- * - `DEPTH` — a nested-workflow dispatch (W-c2) that a depth / cycle guard rejected:
2830
- * running it would push the nested-workflow chain past a bounded max depth, OR its
2831
- * target agent (or a workflow it would author) is already an ancestor of the current
2832
- * run (a re-entry cycle). Public type surface consumed by the `@orkestrel/tool`
2833
- * package's workflow-tool / agent-function adapters, which construct
2834
- * {@link import('./errors.js').WorkflowError}s with this code (thrown, then ISOLATED
2835
- * by that package's `ToolManager` into the tool result's `error`). The error `context`
2836
- * names the offending agent / workflow id + the depth.
2837
- * - `TOOL` — a workflow-authoring tool handler (in `@orkestrel/tool`) was handed a
2838
- * MALFORMED / over-constraint authored args blob (e.g. an empty `id`, `concurrency: 0`)
2839
- * that {@link import('./factories.js').createWorkflowContract} rejected, so no workflow
2840
- * ran. Public type surface: `@orkestrel/tool` constructs this code and THROWS it
2841
- * (rather than returning a failure result); its `ToolManager` ISOLATES the throw into
2842
- * the canonical tool result's top-level `error` (AGENTS §14 — the universal
2843
- * tool-handler contract); the error `context` names the wrapped workflow id.
2844
3091
  * - `MUTATION` — a GATED structural or patch edit was refused: a duplicate id on
2845
3092
  * `append`/`add`, a target that does not exist or is not `pending`, an out-of-bounds
2846
3093
  * `index`, a patch that failed shaper validation, or a live structural edit refused by
@@ -2854,7 +3101,7 @@ export declare class WorkflowError extends Error {
2854
3101
  * guard (both genuine programmer-error paths, AGENTS §12). The error `context` names
2855
3102
  * the offending id / index / status.
2856
3103
  */
2857
- export declare type WorkflowErrorCode = 'TRANSITION' | 'RESTORE' | 'DEPTH' | 'TOOL' | 'MUTATION';
3104
+ export declare type WorkflowErrorCode = 'TRANSITION' | 'RESTORE' | 'MUTATION';
2858
3105
 
2859
3106
  /**
2860
3107
  * The push observation surface (AGENTS §13) of the workflow entity (W-b) — the
@@ -2864,8 +3111,10 @@ export declare type WorkflowErrorCode = 'TRANSITION' | 'RESTORE' | 'DEPTH' | 'TO
2864
3111
  * @remarks
2865
3112
  * Present-tense events with arg tuples. `start` fires when the workflow begins;
2866
3113
  * `complete` when every phase settled successfully; `fail` when a phase failed
2867
- * under `bail` (carrying the failing {@link TaskResult}); `stop` when the workflow
2868
- * was permanently ended. `add` / `remove` / `move` / `update` fire on a successful
3114
+ * under `bail` (carrying the failing {@link TaskResult}); `pause` / `resume` when its
3115
+ * runtime gate closes / opens; `skip` when the workflow was intentionally skipped;
3116
+ * `stop` when it was permanently ended. `add` / `remove`
3117
+ * / `move` / `update` fire on a successful
2869
3118
  * structural or patch edit through {@link WorkflowInterface.add} / `remove` / `move` /
2870
3119
  * `update` (AGENTS §7) — never on a refused/gated one. A throwing listener never
2871
3120
  * reaches the domain surface — the emitter isolates it and routes it to its OWN
@@ -2880,6 +3129,12 @@ export declare type WorkflowEventMap = {
2880
3129
  readonly complete: readonly [];
2881
3130
  /** A phase failed under `bail` — the failing task's result. */
2882
3131
  readonly fail: readonly [result: TaskResult];
3132
+ /** The workflow's runtime gate closed. */
3133
+ readonly pause: readonly [];
3134
+ /** The workflow's runtime gate opened. */
3135
+ readonly resume: readonly [];
3136
+ /** The workflow was intentionally skipped. */
3137
+ readonly skip: readonly [];
2883
3138
  /** The workflow was permanently stopped. */
2884
3139
  readonly stop: readonly [];
2885
3140
  /** A phase was inserted — the inserted phase + its final index. */
@@ -2892,6 +3147,15 @@ export declare type WorkflowEventMap = {
2892
3147
  readonly update: readonly [phase: PhaseInterface];
2893
3148
  };
2894
3149
 
3150
+ /** A normalized persistence failure surfaced as workflow result data. */
3151
+ export declare interface WorkflowFault {
3152
+ readonly origin: 'persistence';
3153
+ readonly checkpoint: WorkflowCheckpoint;
3154
+ readonly message: string;
3155
+ readonly task?: string;
3156
+ readonly attempt?: number;
3157
+ }
3158
+
2895
3159
  /**
2896
3160
  * A registered workflow function — the behavior a `function`-form
2897
3161
  * {@link TaskDefinition} runs, resolved BY NAME through the {@link WorkflowFunctions}
@@ -2906,7 +3170,7 @@ export declare type WorkflowEventMap = {
2906
3170
  * should honour `controller.signal` (a workflow-level abort / timeout / budget, or — under
2907
3171
  * `bail: true` — a sibling's failure, fires it) so a cancel stops it promptly.
2908
3172
  */
2909
- export declare type WorkflowFunction = (controller: TaskControllerInterface) => Promise<unknown> | unknown;
3173
+ export declare type WorkflowFunction = (controller: TaskControllerInterface) => Promise<JSONValue> | JSONValue;
2910
3174
 
2911
3175
  /**
2912
3176
  * The `function`-task behavior registry — workflow function names mapped to their
@@ -2914,9 +3178,10 @@ export declare type WorkflowFunction = (controller: TaskControllerInterface) =>
2914
3178
  *
2915
3179
  * @remarks
2916
3180
  * A live {@link TaskInterface} resolves its `run` name against this registry ONCE at
2917
- * construction into its {@link TaskInterface.handler}. A name absent from the registry (or
2918
- * an omitted `run`) is the no-handler case the task AUTO-COMPLETES (the ROADMAP rule). A
2919
- * plain record (not a manager) — the registry is a lookup, with no lifecycle of its own.
3181
+ * construction into its {@link TaskInterface.handler}. An omitted `run` is the deliberate
3182
+ * no-op case. A present name absent from the registry remains inspectable but makes the tree
3183
+ * non-drivable until restored with a matching handler. A plain record (not a manager) — the
3184
+ * registry is a lookup, with no lifecycle of its own.
2920
3185
  */
2921
3186
  export declare type WorkflowFunctions = Readonly<Record<string, WorkflowFunction>>;
2922
3187
 
@@ -2940,14 +3205,15 @@ export declare type WorkflowInput = Partial<WorkflowContext>;
2940
3205
  * - **Children.** `phases` is the lean {@link PhaseManagerInterface} (AGENTS §9);
2941
3206
  * `phase(id)` / `phases().phases()` read in positional order. `results` collects ALL
2942
3207
  * tasks' results across every phase (the workflow tier of the result tree).
2943
- * - **Override.** `skip` / `stop` (AGENTS §10) FORCE the workflow's status; the override
2944
- * survives a snapshot.
3208
+ * - **Override.** `skip` / `stop` (AGENTS §10) FORCE the workflow's status; `complete`
3209
+ * may force only a task-free, otherwise-pending tree. The override survives a snapshot.
2945
3210
  * - **Snapshot.** `snapshot()` serializes the whole live tree to a {@link WorkflowSnapshot}
2946
3211
  * (pure JSON — structure + each node's status + recorded results + positional order);
2947
3212
  * {@link restoreWorkflow} rebuilds an equivalent live tree.
2948
3213
  * - **Observable (AGENTS §13).** The owned {@link emitter} ({@link WorkflowEventMap}) fires
2949
- * `start` / `complete` / `fail` / `stop` on a derived-status change; the emitter isolates a
2950
- * listener throw and routes it to its `error` handler (the `error` option).
3214
+ * `start` / `complete` / `fail` / `pause` / `resume` / `stop` after the corresponding
3215
+ * status or runtime-gate change; the emitter isolates a listener throw and routes it to
3216
+ * its `error` handler (the `error` option).
2951
3217
  */
2952
3218
  export declare interface WorkflowInterface {
2953
3219
  readonly emitter: EmitterInterface<WorkflowEventMap>;
@@ -2997,9 +3263,9 @@ export declare interface WorkflowInterface {
2997
3263
  * FORCE this workflow to `completed` (AGENTS §10), overriding the derived value.
2998
3264
  *
2999
3265
  * @remarks
3000
- * A NO-OP unless `status` is `pending` its ONLY legitimate use is settling a vacuously
3001
- * DONE tree (no work happened), mirroring the runner's own gate. Never overrides a real
3002
- * `completed` / a bail-true `failed` / a `stopped` / a derived `skipped`.
3266
+ * A NO-OP unless `status` is `pending` and the tree is genuinely vacuous: zero phases or
3267
+ * every phase contains zero tasks. Its ONLY legitimate use is settling an executed no-op
3268
+ * tree. It never overrides pending work or any started/terminal state.
3003
3269
  */
3004
3270
  complete(): void;
3005
3271
  /**
@@ -3033,16 +3299,15 @@ export declare interface WorkflowInterface {
3033
3299
  */
3034
3300
  resume(): void;
3035
3301
  /**
3036
- * Tear this workflow down (AGENTS §10) — a TERMINAL teardown: aborts {@link signal},
3037
- * `stop`s every non-terminal live phase (so any engine parked on a phase's own gate
3038
- * unparks and the tree lands coherent), forces the `stop` override on THIS workflow if
3039
- * it is not already terminal, resolves any parked {@link wait} waiter, and marks
3040
- * {@link destroyed}; idempotent.
3302
+ * Tear this workflow down (AGENTS §10) — an atomic TERMINAL teardown: mark
3303
+ * {@link destroyed}, pin non-terminal workflow/phase overrides to `stopped`, stop every
3304
+ * non-terminal task, release gates and liveness resources, abort {@link signal}, then
3305
+ * destroy task, phase, and workflow emitters in ownership order; idempotent.
3041
3306
  *
3042
3307
  * @remarks
3043
- * After `destroy`, every structural mutator (`add` / `remove` / `move` / `update` /
3044
- * `patch`) and `pause` / `resume` reject (a `Result` failure) or no-op never throws
3045
- * for calling `destroy` itself twice.
3308
+ * {@link destroyed} is set before final events, so reentrant mutation is refused and a
3309
+ * recursive `destroy` is a no-op. Already-terminal genuine completed/failed state is
3310
+ * preserved, and state/snapshots remain inspectable after emitter resources are destroyed.
3046
3311
  *
3047
3312
  * @example
3048
3313
  * ```ts
@@ -3298,9 +3563,8 @@ export declare interface WorkflowManagerInterface {
3298
3563
  * every {@link import('./factories.js').createWorkflow} ({@link WorkflowManagerInterface.add})
3299
3564
  * and every {@link import('./factories.js').restoreWorkflow}
3300
3565
  * ({@link WorkflowManagerInterface.open}'s hydration path) the manager performs — so a
3301
- * hydrated workflow carries real resolved `handler`s and is RUNNABLE, not merely a restored
3302
- * state mirror. Omitted every minted/hydrated task resolves no `handler` (the no-handler
3303
- * rule — it auto-completes if driven).
3566
+ * hydrated workflow carries real resolved `handler`s and is RUNNABLE. Omitted named work
3567
+ * remains inspectable but cannot be driven; omitted-`run` tasks remain deliberate no-ops.
3304
3568
  */
3305
3569
  export declare interface WorkflowManagerOptions {
3306
3570
  /**
@@ -3316,7 +3580,7 @@ export declare interface WorkflowManagerOptions {
3316
3580
  * (`add`, via {@link import('./factories.js').createWorkflow}) or hydrates (`open`'s
3317
3581
  * registry-miss path, via {@link import('./factories.js').restoreWorkflow}) — so a
3318
3582
  * hydrated workflow is RUNNABLE, its tasks carrying real resolved `handler`s. Omitted ⇒
3319
- * every task resolves no `handler` (the no-handler rule).
3583
+ * named tasks remain inspectable but execution rejects them.
3320
3584
  */
3321
3585
  readonly functions?: WorkflowFunctions;
3322
3586
  }
@@ -3354,12 +3618,66 @@ export declare interface WorkflowOptions {
3354
3618
  * fresh build ({@link import('./factories.js').createWorkflow}) and a restore
3355
3619
  * ({@link import('./factories.js').restoreWorkflow}) both consume, and the same shape a
3356
3620
  * live {@link WorkflowInterface.add} / {@link PhaseInterface.add} mint resolves a newly
3357
- * minted task against. A `run` name absent from `functions` (or omitted entirely)
3358
- * resolves to no handler that task AUTO-COMPLETES (the no-handler rule): its
3359
- * phase/workflow still reaches a terminal status, just with no dispatched behavior.
3360
- * Omitted ⇒ an empty registry (every task auto-completes).
3621
+ * minted task against. An omitted `run` resolves to no handler and is the deliberate
3622
+ * no-op form. A present name absent from `functions` also has no handler so exact restore
3623
+ * remains inspectable, but {@link WorkflowRunnerInterface.execute} rejects that tree.
3624
+ * Omitted ⇒ an empty registry; only tasks that also omit `run` are executable no-ops.
3361
3625
  */
3362
3626
  readonly functions?: WorkflowFunctions;
3627
+ /** Runtime-only default silence; non-positive, non-finite, or over-`MAX_TIMER_MS` disables it. */
3628
+ readonly silence?: number;
3629
+ }
3630
+
3631
+ /**
3632
+ * Advanced run-local snapshot persistence with one writer and one coalesced latest obligation.
3633
+ *
3634
+ * @remarks
3635
+ * Normally composed by `WorkflowRunner.execute({ store })`; exported for hosts that need to
3636
+ * coordinate the same required boundaries around their own runner integration.
3637
+ */
3638
+ export declare class WorkflowPersistence implements WorkflowPersistenceInterface {
3639
+ #private;
3640
+ constructor(workflow: WorkflowInterface, store: WorkflowStoreInterface);
3641
+ get fault(): WorkflowFault | undefined;
3642
+ /**
3643
+ * Persist every change through this required boundary.
3644
+ *
3645
+ * @param checkpoint - The boundary being made durable
3646
+ * @param task - The task owning an attempt or settlement
3647
+ * @param attempt - The persisted attempt number
3648
+ * @returns Whether the latest state reached the store
3649
+ */
3650
+ checkpoint(checkpoint: WorkflowCheckpoint, task?: TaskInterface, attempt?: number): Promise<boolean>;
3651
+ /**
3652
+ * Stop observing the live tree and persist its final state.
3653
+ *
3654
+ * @returns Whether the final snapshot reached the store
3655
+ */
3656
+ finalize(): Promise<boolean>;
3657
+ /** Stop observing the live tree. */
3658
+ detach(): void;
3659
+ }
3660
+
3661
+ /**
3662
+ * The advanced run-local durability coordinator normally composed by
3663
+ * {@link WorkflowRunnerInterface.execute} when `store` is supplied.
3664
+ */
3665
+ export declare interface WorkflowPersistenceInterface {
3666
+ /** The first required checkpoint failure, if one occurred. */
3667
+ readonly fault: WorkflowFault | undefined;
3668
+ /**
3669
+ * Make the latest state durable at one required boundary.
3670
+ *
3671
+ * @param checkpoint - The required durability boundary
3672
+ * @param task - The task owning an attempt or settlement
3673
+ * @param attempt - The persisted one-based attempt number
3674
+ * @returns Whether the latest live state reached the store
3675
+ */
3676
+ checkpoint(checkpoint: WorkflowCheckpoint, task?: TaskInterface, attempt?: number): Promise<boolean>;
3677
+ /** Detach observers and make the final live state durable. */
3678
+ finalize(): Promise<boolean>;
3679
+ /** Stop observing the live workflow tree; idempotent. */
3680
+ detach(): void;
3363
3681
  }
3364
3682
 
3365
3683
  /**
@@ -3376,11 +3694,17 @@ export declare interface WorkflowOptions {
3376
3694
  * settled task across all phases, in positional order — the same array `workflow.results()`
3377
3695
  * yields). Returning the live `workflow` (not just a snapshot) keeps the entity tree the
3378
3696
  * source of truth — the runner adds only the convenience `status` / `results` projections.
3697
+ * A scheduler or other engine-infrastructure failure rejects after the runner coherently
3698
+ * stops remaining work and attempts final persistence.
3379
3699
  */
3380
3700
  export declare interface WorkflowResult {
3381
3701
  readonly workflow: WorkflowInterface;
3382
3702
  readonly status: WorkflowStatus;
3383
3703
  readonly results: readonly TaskResult[];
3704
+ /** Whether the returned final state is stored; omitted when no store was supplied. */
3705
+ readonly durable?: boolean;
3706
+ /** The first required persistence failure; omitted when none occurred. */
3707
+ readonly fault?: WorkflowFault;
3384
3708
  }
3385
3709
 
3386
3710
  /**
@@ -3396,17 +3720,16 @@ export declare interface WorkflowResult {
3396
3720
  * `AbortSignal.any` (exactly as the agent runtime folds its bounds); pacing is the shipped
3397
3721
  * {@link SchedulerInterface}. The runner writes ZERO concurrency / retry / abort logic of
3398
3722
  * its own — it only sequences phases, dispatches a task's own handler, and drives the live
3399
- * entity.
3400
- * - **Pure engine no registries, no tool/agent knowledge.** The runner carries no
3401
- * `functions` / `tools` / `agents` registry: each live {@link TaskInterface} already
3723
+ * entity. The workflow layer owns per-task deadlines because timeout settlement must
3724
+ * update the live leaf under the phase's `bail` policy before the substrate unit settles.
3725
+ * - **Pure engine no integration registry.** The runner carries no behavior or provider
3726
+ * registry: each live {@link TaskInterface} already
3402
3727
  * resolved its own {@link import('./types.js').WorkflowFunction} into
3403
3728
  * {@link import('./types.js').TaskInterface.handler} ONCE at construction (build, restore,
3404
3729
  * or a live mint all resolve it identically, from {@link WorkflowOptions.functions}), so
3405
- * dispatch is simply "invoke the task's own handler". Static tool / agent calling is an
3406
- * OPT-IN concern of the `@orkestrel/tool` package's adapter factories — plain
3407
- * {@link import('./types.js').WorkflowFunction}s a caller wires into
3408
- * {@link WorkflowOptions.functions} like any other behavior. This module never imports
3409
- * any tool/agent package.
3730
+ * dispatch is simply "invoke the task's own handler". Provider, protocol, and tool
3731
+ * integrations remain application-owned {@link import('./types.js').WorkflowFunction}s
3732
+ * composed into {@link WorkflowOptions.functions}. This module imports none of them.
3410
3733
  * - **Two `execute` forms, one engine.** `execute(definition, options)` BUILDS the live tree
3411
3734
  * from a {@link WorkflowDefinition} (single source of truth for the `run` / `concurrency`
3412
3735
  * metadata); `execute(workflow, options)` DRIVES a caller-owned, ALREADY-BUILT
@@ -3426,10 +3749,9 @@ export declare interface WorkflowResult {
3426
3749
  * for `spawn` to accept (the runner already drained) is swept `skip`ped afterward so the
3427
3750
  * phase always reaches a coherent terminal state.
3428
3751
  * - **Dispatch by handler.** `#runTask` invokes the live task's own
3429
- * {@link import('./types.js').TaskInterface.handler} directly: `undefined` (an omitted `run`,
3430
- * or a `run` name absent from the {@link WorkflowOptions.functions} registry it was resolved
3431
- * against) AUTO-COMPLETES the ROADMAP no-handler rule; otherwise the handler runs with the
3432
- * task's {@link import('./types.js').TaskControllerInterface} handle.
3752
+ * {@link import('./types.js').TaskInterface.handler} directly. An omitted `run` deliberately
3753
+ * auto-completes with JSON `null`; a present unresolved name is rejected by the synchronous
3754
+ * execution claim and never false-completes.
3433
3755
  * - **`bail` → substrate.** Under `bail: true` (halt) a genuine task failure `fail`s the leaf
3434
3756
  * THEN re-throws, so the substrate Runner fail-fasts — it aborts the in-flight siblings
3435
3757
  * (their `controller.signal` fires; a mid-flight sibling `skip`s) and rejects the phase run;
@@ -3437,11 +3759,12 @@ export declare interface WorkflowResult {
3437
3759
  * Under `bail: false` (graceful) a failure `fail`s the leaf and RESOLVES (never throws), so
3438
3760
  * the Runner settles every unit (allSettled) and the run finishes (the workflow derives
3439
3761
  * `completed`, the failure recorded in the result tree).
3440
- * - **Pause / stop / destroy gates.** `workflow.pause()` is honoured at exactly two points
3441
- * the next phase boundary (workflow-only) and each task's own pre-dispatch (before
3442
- * `task.start()`, workflow gate FIRST then this task's own `phase.pause()`) by parking on
3443
- * {@link WorkflowInterface.wait} / {@link PhaseInterface.wait}; an in-flight task body is
3444
- * NEVER suspended mid-flight. A GRACEFUL `workflow.stop()` (no signal involved) is caught at
3762
+ * - **Pause / stop / destroy gates.** Workflow, phase, and task gates are checked before
3763
+ * dispatch, and a running handler can checkpoint their folded state through
3764
+ * {@link import('./types.js').TaskControllerInterface.wait}. Because the substrate acquires
3765
+ * concurrency before this handler gate, a paused task occupies one phase slot until resume;
3766
+ * already-running siblings continue and its per-attempt timeout keeps counting. A GRACEFUL
3767
+ * `workflow.stop()` (no signal involved) is caught at
3445
3768
  * those same gates: not-yet-started work is `skip`ped, in-flight work finishes naturally. A
3446
3769
  * HARD `workflow.destroy()` aborts {@link WorkflowInterface.signal}, which `#fold` has folded
3447
3770
  * into the run's composed signal — so it cancels the active phase Runner (and every
@@ -3458,8 +3781,8 @@ export declare interface WorkflowResult {
3458
3781
  * {@link TaskController} signal `AbortSignal.any`-combines the substrate per-unit signal with
3459
3782
  * `runSignal`, so a handler observes either cause directly.
3460
3783
  * - **Re-entrant-safe.** No shared per-run mutable field: the active-Runner holder is LOCAL to
3461
- * each `#execute`, so a nested `execute` (a bound workflow-tool handler re-entering this
3462
- * instance while the outer run is suspended awaiting it) cannot clobber the outer run's state.
3784
+ * each `#execute`, so a nested application-level `execute` cannot clobber the outer run's
3785
+ * state.
3463
3786
  */
3464
3787
  export declare class WorkflowRunner implements WorkflowRunnerInterface {
3465
3788
  #private;
@@ -3482,6 +3805,8 @@ export declare class WorkflowRunner implements WorkflowRunnerInterface {
3482
3805
  * {@link WorkflowRunOptions} abort / timeout / budget fires every in-flight task's signal
3483
3806
  * and `stop`s the run. `execute` resolves (never rejects) on a cancel — the partial outcome
3484
3807
  * is read from the returned {@link WorkflowResult} (its `workflow` / `status` / `results`).
3808
+ * An unexpected scheduler or engine-infrastructure failure rejects after remaining work is
3809
+ * stopped, swept, and final persistence is attempted.
3485
3810
  *
3486
3811
  * @param definition - The {@link WorkflowDefinition} to build the live tree from and drive
3487
3812
  * @param options - The construction options ({@link WorkflowOptions}: `on` / `bail` /
@@ -3501,10 +3826,11 @@ export declare class WorkflowRunner implements WorkflowRunnerInterface {
3501
3826
  * @remarks
3502
3827
  * `createWorkflow` mints the live tree, this overload drives it, and the caller controls
3503
3828
  * the SAME entity mid-run via its own `pause` / `resume` / `add` / `stop` / `destroy`
3504
- * (AGENTS §10). Requires `workflow.status === 'pending'` and `!workflow.destroyed`
3505
- * otherwise this is a programmer-timing error and it THROWS a `TRANSITION`
3506
- * {@link WorkflowError} (AGENTS §12) rather than silently no-opping or building a second
3507
- * tree. Once accepted, observable semantics are byte-identical to the `definition` form —
3829
+ * (AGENTS §10). Requires `workflow.status === 'pending'`, `!workflow.destroyed`, and no
3830
+ * prior execution claim. A process-local object-identity claim shared by all runner instances
3831
+ * is acquired synchronously and never released, so a same-object second call throws a `TRANSITION`
3832
+ * {@link WorkflowError} before any asynchronous status change. Once accepted, observable
3833
+ * semantics are byte-identical to the `definition` form —
3508
3834
  * except the phase loop RE-READS the live tree every iteration, so a caller's live `add`
3509
3835
  * mid-run is picked up and actually dispatched. `options` carries only the per-run bounds
3510
3836
  * (`signal` / `timeout` / `budget`) — the construction half of {@link WorkflowRunOptions}
@@ -3542,8 +3868,8 @@ export declare class WorkflowRunner implements WorkflowRunnerInterface {
3542
3868
  * {@link WorkflowOptions.functions}) and each phase's `concurrency` (so there is no
3543
3869
  * separately-supplied workflow to drift from the definition). The freshly-built live tree is
3544
3870
  * returned in {@link WorkflowResult.workflow}. The runner carries NO registry of its own — it
3545
- * simply invokes each task's OWN {@link TaskInterface.handler}; a task with no handler
3546
- * AUTO-COMPLETES. The runner DRIVES the live entity (`start` → `complete` / `fail`), never
3871
+ * simply invokes each task's OWN {@link TaskInterface.handler}; an omitted `run` is the only
3872
+ * auto-completing no-op. The runner DRIVES the live entity (`start` → `complete` / `fail`), never
3547
3873
  * re-implementing status. The `bail` policy maps onto the substrate's fail-fast (`bail: true`
3548
3874
  * — the first failure aborts in-flight siblings and skips the rest) vs settle-all (`bail:
3549
3875
  * false` — failures are recorded and the run finishes). The {@link WorkflowOptions} half of
@@ -3581,9 +3907,9 @@ export declare interface WorkflowRunnerInterface {
3581
3907
  * **Programmer-error exception (AGENTS §12).** A PATHOLOGICAL `definition` (e.g. a
3582
3908
  * duplicate phase or task `id`) THROWS SYNCHRONOUSLY at construction — before any phase
3583
3909
  * runs, and before the returned `Promise` is even created — rather than resolving a
3584
- * failed/partial {@link WorkflowResult}. This is the one exception to the "resolves,
3585
- * never rejects" contract above: a malformed definition is a programmer-timing error, not
3586
- * a runtime outcome to report through the result tree.
3910
+ * failed/partial {@link WorkflowResult}. Unexpected scheduler or engine-infrastructure
3911
+ * failures may reject asynchronously after remaining work is stopped, swept, and final
3912
+ * persistence is attempted. Neither case is a domain task outcome to disguise as a result.
3587
3913
  *
3588
3914
  * @param definition - The {@link WorkflowDefinition} to build the live tree from and drive
3589
3915
  * @param options - The construction options ({@link WorkflowOptions}: `on` / `bail` /
@@ -3599,10 +3925,11 @@ export declare interface WorkflowRunnerInterface {
3599
3925
  * The entity itself is now the single control surface (no separate run handle):
3600
3926
  * `createWorkflow` mints the live tree, this overload drives it, and the caller
3601
3927
  * controls the SAME entity mid-run via its own `pause` / `resume` / `add` / `stop` /
3602
- * `destroy` (AGENTS §10). Requires `workflow.status === 'pending'` and
3603
- * `!workflow.destroyed` otherwise this is a programmer-timing error and it THROWS a
3604
- * `TRANSITION` {@link import('./errors.js').WorkflowError} (AGENTS §12) rather than
3605
- * silently no-opping or building a second tree. Once accepted, phases run
3928
+ * `destroy` (AGENTS §10). Requires `workflow.status === 'pending'`,
3929
+ * `!workflow.destroyed`, and no prior execution claim. A process-local object-identity claim
3930
+ * shared by every runner instance is acquired synchronously and never released, so a same-object
3931
+ * call throws a `TRANSITION` {@link import('./errors.js').WorkflowError} even before an
3932
+ * asynchronous status change. Once accepted, phases run
3606
3933
  * SEQUENTIALLY and, within each phase, tasks CONCURRENTLY — byte-identical observable
3607
3934
  * semantics to the `definition`-form `execute` — except the phase loop RE-READS the
3608
3935
  * live `workflow.phases` / each phase's live `tasks` every iteration (a cursor over
@@ -3619,12 +3946,10 @@ export declare interface WorkflowRunnerInterface {
3619
3946
  * {@link import('./factories.js').restoreWorkflow} behaves according to whether a
3620
3947
  * {@link WorkflowFunctions} registry was supplied at that build: WITH a registry,
3621
3948
  * each task's `run` name is re-resolved against it, so a matched task carries a real
3622
- * handler and this overload actually DISPATCHES it, resuming real work. WITHOUT a
3623
- * registry (or when a task's `run` name has no match in it), the task's
3624
- * {@link TaskInterface.run} is `undefined` the no-handler rule then AUTO-COMPLETES
3625
- * that task (no dispatch occurs). A PARTIALLY-run restored tree (any live phase/task
3626
- * not `pending`) is rejected outright by the `workflow.status === 'pending'` guard
3627
- * above — only a wholly `pending` restored tree is drivable.
3949
+ * handler and this overload actually DISPATCHES it, resuming real work. Without a registry,
3950
+ * the persisted {@link TaskInterface.run} remains visible for inspection while `handler` is
3951
+ * `undefined`, and this overload rejects the tree before dispatch. A quiescent recovered tree may contain
3952
+ * terminal work plus pending work; a tree with any `running` leaf is not drivable.
3628
3953
  *
3629
3954
  * @param workflow - The live {@link WorkflowInterface} to drive (its own entity surface —
3630
3955
  * `pause` / `resume` / `add` / `stop` / `destroy` — is the caller's control seam)
@@ -3642,9 +3967,7 @@ export declare interface WorkflowRunnerInterface {
3642
3967
  * @remarks
3643
3968
  * The runner is a PURE engine — it carries no `functions` / `tools` / `agents` registry
3644
3969
  * (each live task already resolved its own handler at construction from
3645
- * {@link WorkflowOptions.functions}); wiring a `function`-form task to a tool or an agent is
3646
- * an OPT-IN concern of the `@orkestrel/tool` package's adapter factories, which a caller
3647
- * composes into its OWN `functions` registry.
3970
+ * {@link WorkflowOptions.functions}).
3648
3971
  * - `scheduler` — the {@link SchedulerInterface} that paces the tree (a cooperative
3649
3972
  * `yield` between phases). Omitted ⇒ the shipped cross-environment default
3650
3973
  * ({@link createScheduler}).
@@ -3679,23 +4002,19 @@ export declare interface WorkflowRunnerOptions {
3679
4002
  * {@link TaskControllerInterface.signal} fires) and HALTS the run — the remaining tasks
3680
4003
  * and phases are `skip`ped and the workflow settles `stopped`.
3681
4004
  * - `signal` — an external cancellation (a caller `AbortController`).
3682
- * - `timeout` — a whole-run deadline in milliseconds. A non-positive value (`0` or negative)
3683
- * NO deadline (the runner arms an `@orkestrel/timeout` `TimeoutInterface`
3684
- * only when `timeout > 0`).
4005
+ * - `timeout` — a whole-run deadline in milliseconds. A non-positive, non-finite, or
4006
+ * over-`MAX_TIMER_MS` value means no deadline.
3685
4007
  * - `budget` — a whole-run cost ceiling (a {@link BudgetInterface} over {@link TokenUsage}
3686
4008
  * — its `signal` fires when a task-reported usage crosses `max`); the runner folds its
3687
4009
  * `signal` and `start`s it. (A `max: 0` budget is exhausted from its first `start`, so it
3688
4010
  * cancels the run at entry — a DIFFERENT primitive from the `timeout: 0` "no deadline" case.)
3689
4011
  *
3690
- * The engine itself carries NO nesting bookkeeping — the depth / cycle guard for a nested
3691
- * `agent` → workflow-tool → workflow chain lives entirely in the OPT-IN adapter factories
3692
- * shipped by `@orkestrel/tool`, closed over their own `depth` / `ancestry`, never threaded
3693
- * through `execute`'s options.
3694
4012
  */
3695
4013
  export declare type WorkflowRunOptions = WorkflowOptions & {
3696
4014
  readonly signal?: AbortSignal;
3697
4015
  readonly timeout?: number;
3698
4016
  readonly budget?: BudgetInterface<TokenUsage>;
4017
+ readonly store?: WorkflowStoreInterface;
3699
4018
  };
3700
4019
 
3701
4020
  /**
@@ -3738,17 +4057,18 @@ export declare const workflowShape: ObjectShape<{
3738
4057
  * it is self-contained, it carries the policy it ran under: `bail` (AGENTS §4.4) is the
3739
4058
  * failure policy, so {@link import('./factories.js').restoreWorkflow} re-derives status
3740
4059
  * IDENTICALLY without a silent default. `status` is the EFFECTIVE status (override-or-derived)
3741
- * at snapshot time; `override` is the forced status of a whole-workflow `skip` / `stop`,
3742
- * PRESENT only when one is in force (so a restore reinstates it DIRECTLY rather than guessing
3743
- * from a status divergence). `phases` are the workflow's {@link PhaseSnapshot}s in order;
3744
- * `created` / `updated` are ms since epoch.
4060
+ * at snapshot time; `override` is the forced status of a whole-workflow `skip` / `stop` or
4061
+ * vacuous `completed`. The completed override is valid only for an otherwise-derived pending
4062
+ * tree containing no tasks. An override is PRESENT only when one is in force (so a restore
4063
+ * reinstates it DIRECTLY rather than guessing from a status divergence). `phases` are the
4064
+ * workflow's {@link PhaseSnapshot}s in order; `created` / `updated` are ms since epoch.
3745
4065
  */
3746
4066
  export declare interface WorkflowSnapshot {
3747
4067
  readonly id: string;
3748
4068
  readonly name: string;
3749
4069
  readonly description?: string;
3750
4070
  readonly status: WorkflowStatus;
3751
- /** The forced status of a whole-workflow `skip` / `stop`; present only when an override is in force. */
4071
+ /** Whole-workflow `skip` / `stop` or valid task-free vacuous `completed`; omitted when derived. */
3752
4072
  readonly override?: WorkflowStatus;
3753
4073
  /** The failure policy the workflow ran under (AGENTS §4.4) — persisted so a restore re-derives identically. */
3754
4074
  readonly bail: boolean;
@@ -3757,6 +4077,9 @@ export declare interface WorkflowSnapshot {
3757
4077
  readonly updated: number;
3758
4078
  }
3759
4079
 
4080
+ /** Locate the nearest identifiable node for an inconsistent owned snapshot. */
4081
+ export declare function workflowSnapshotContext(value: unknown): Readonly<Record<string, unknown>> | undefined;
4082
+
3760
4083
  /**
3761
4084
  * One row of the table a {@link import('./stores/DatabaseWorkflowStore.js').DatabaseWorkflowStore}
3762
4085
  * persists — a workflow `id` plus its {@link WorkflowSnapshot} held as ONE OPAQUE JSON column.