@orkestrel/workflow 0.0.1 → 0.0.3

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,5 +1,4 @@
1
1
  import { AbortInterface } from '@orkestrel/abort';
2
- import { AgentInterface } from '@orkestrel/agent';
3
2
  import { ArrayShape } from '@orkestrel/contract';
4
3
  import { BudgetInterface } from '@orkestrel/budget';
5
4
  import { ContractInterface } from '@orkestrel/contract';
@@ -7,31 +6,16 @@ import { DriverInterface } from '@orkestrel/database';
7
6
  import { EmitterErrorHandler } from '@orkestrel/emitter';
8
7
  import { EmitterHooks } from '@orkestrel/emitter';
9
8
  import { EmitterInterface } from '@orkestrel/emitter';
9
+ import { Failure } from '@orkestrel/contract';
10
10
  import { LiteralShape } from '@orkestrel/contract';
11
11
  import { NumberShape } from '@orkestrel/contract';
12
12
  import { ObjectShape } from '@orkestrel/contract';
13
13
  import { OptionalShape } from '@orkestrel/contract';
14
14
  import { Result } from '@orkestrel/contract';
15
15
  import { StringShape } from '@orkestrel/contract';
16
+ import { Success } from '@orkestrel/contract';
16
17
  import { TableInterface } from '@orkestrel/database';
17
18
  import { TokenUsage } from '@orkestrel/budget';
18
- import { ToolInterface } from '@orkestrel/agent';
19
- import { ToolManagerInterface } from '@orkestrel/agent';
20
- import { UnionShape } from '@orkestrel/contract';
21
-
22
- /**
23
- * The ancestry identifier of an agent in a run chain — `agent:<name>`.
24
- *
25
- * @remarks
26
- * The agent counterpart of {@link workflowTag}: the runner adds one when it dispatches an
27
- * `agent` task, and rejects the task (a typed `DEPTH` `task.fail`) when the same tag is
28
- * already in the ancestry (a re-entry cycle). The `agent:` namespace keeps it distinct
29
- * from a same-string workflow id.
30
- *
31
- * @param name - The agent's registry name (the `agent`-form's `name`)
32
- * @returns The namespaced ancestry tag (`agent:<name>`)
33
- */
34
- export declare function agentTag(name: string): string;
35
19
 
36
20
  /**
37
21
  * Assert that a {@link WorkflowSnapshot} carries a `boolean` `bail` — at the workflow tier AND
@@ -43,10 +27,13 @@ export declare function agentTag(name: string): string;
43
27
  * untrusted JSON, so a status (or an override) outside
44
28
  * {@link import('./constants.js').WORKFLOW_STATUSES} /
45
29
  * {@link import('./constants.js').PHASE_STATUSES} / {@link import('./constants.js').TASK_STATUSES},
46
- * or a non-boolean `bail` (the workflow's OR any phase's — both are REQUIRED persisted policy),
30
+ * a non-boolean `bail` (the workflow's OR any phase's — both are REQUIRED persisted policy), a
31
+ * present-but-invalid phase `concurrency` (not a positive integer), or a present-but-invalid task
32
+ * `run` (an empty string) / `retries` / `timeout` (not a non-negative integer),
47
33
  * is rejected loudly (naming the offending node) rather than silently producing a broken tree.
48
- * The `override` is optional, so it is only checked WHEN present. Structural shape beyond these
49
- * fields is the contract's concern; this guards exactly the fields the live state machine reads back.
34
+ * The `override` / `concurrency` / `run` / `retries` / `timeout` are optional, so each is only
35
+ * checked WHEN present. Structural shape beyond these fields is the contract's concern; this
36
+ * guards exactly the fields the live state machine reads back.
50
37
  *
51
38
  * @param snapshot - The snapshot to validate
52
39
  */
@@ -117,48 +104,6 @@ export declare function canTransitionTask(from: TaskStatus, to: TaskStatus): boo
117
104
  */
118
105
  export declare function collectResults(phases: readonly (readonly TaskResult[])[]): readonly TaskResult[];
119
106
 
120
- /**
121
- * Complete a {@link WorkflowDraft} into a strict {@link WorkflowDefinition} — synthesize
122
- * any MISSING `id` deterministically + positionally, and default any MISSING `name` to
123
- * its (now-resolved) `id`.
124
- *
125
- * @remarks
126
- * The positional id scheme is stable and human-legible: the workflow is `wf`, phase `i`
127
- * is `phase-<i>`, and task `j` of that phase is `<phaseId>-task-<j>` (so a provided phase
128
- * id flows into its tasks' synthesized ids). A PROVIDED `id` / `name` at any level is kept
129
- * VERBATIM — synthesis touches only the omitted ones. A missing `name` defaults to the
130
- * resolved `id` (never the other way round), so the result always has both. `run`,
131
- * `description`, the per-phase `concurrency` / `bail`, the per-task `retries` / `timeout`, and
132
- * the workflow `bail` carry over unchanged. The result is a complete
133
- * {@link WorkflowDefinition}; the caller still validates it against the STRICT contract.
134
- *
135
- * @param draft - The draft workflow (id/name optional at all three levels)
136
- * @returns A complete {@link WorkflowDefinition} with every id/name filled
137
- */
138
- export declare function completeDraft(draft: WorkflowDraft): WorkflowDefinition;
139
-
140
- /**
141
- * Complete one {@link PhaseDraft} into a strict {@link PhaseDefinition} — the per-phase
142
- * step of {@link completeDraft} (phase `index` → `phase-<index>` when its id is omitted).
143
- *
144
- * @param phase - The draft phase
145
- * @param index - The phase's positional index in the workflow
146
- * @returns A complete {@link PhaseDefinition}
147
- */
148
- export declare function completePhaseDraft(phase: PhaseDraft, index: number): PhaseDefinition;
149
-
150
- /**
151
- * Complete one {@link TaskDraft} into a strict {@link TaskDefinition} — the per-task leaf
152
- * step of {@link completeDraft} (task `index` of phase `<phaseId>` → `<phaseId>-task-<index>`
153
- * when its id is omitted).
154
- *
155
- * @param task - The draft task
156
- * @param phaseId - The (resolved) parent phase id, so the synthesized task id nests under it
157
- * @param index - The task's positional index within its phase
158
- * @returns A complete {@link TaskDefinition}
159
- */
160
- export declare function completeTaskDraft(task: TaskDraft, phaseId: string, index: number): TaskDefinition;
161
-
162
107
  /**
163
108
  * The per-unit handle a runner handler receives — wraps the unit's identity,
164
109
  * input, cancellation, and the run controls (`wait` / `spawn` / `abort`).
@@ -441,6 +386,11 @@ export declare function createScheduler(): SchedulerInterface;
441
386
  * `options.phases[id].tasks[id]` (the AGENTS §8 nested-by-id bag). The W-b tree is the
442
387
  * state machine ONLY — it does not execute tasks (W-c drives the transitions).
443
388
  *
389
+ * `options.functions` is the {@link import('./types.js').WorkflowFunctions} registry each live
390
+ * 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).
393
+ *
444
394
  * @param definition - The workflow definition to bring to life
445
395
  * @param options - Runtime options (initial listeners, `bail` override, per-node options)
446
396
  * @returns The live {@link WorkflowInterface} root
@@ -485,152 +435,54 @@ export declare function createWorkflow(definition: WorkflowDefinition, options?:
485
435
  */
486
436
  export declare function createWorkflowContract(): ContractInterface<WorkflowDefinition>;
487
437
 
488
- /**
489
- * Compile the LENIENT workflow DRAFT contract — identical to
490
- * {@link createWorkflowContract} EXCEPT `id` and `name` are OPTIONAL at all three levels
491
- * (workflow / phase / task), so a small model can omit the six identity strings.
492
- *
493
- * @remarks
494
- * The widened authoring surface {@link createWorkflowTool} parses an authored blob through
495
- * before {@link import('./helpers.js').completeDraft} fills the missing ids/names. It does
496
- * NOT relax the canonical contract — {@link createWorkflowContract} stays byte-for-byte
497
- * unchanged and STRICT, and the completed draft is re-validated against THAT strict gate
498
- * before running (soundness preserved). A PROVIDED `id` / `name` still carries `minLength: 1`,
499
- * so an explicitly-empty `id: ''` is REJECTED (parses to `undefined`), never auto-filled —
500
- * keeping "garbage" distinct from "omitted". `run` stays required.
501
- *
502
- * @returns The compiled {@link WorkflowDraft} contract
503
- *
504
- * @example
505
- * ```ts
506
- * import { createWorkflowDraftContract, completeDraft } from '@src/core'
507
- *
508
- * const draft = createWorkflowDraftContract()
509
- * const parsed = draft.parse({ phases: [{ tasks: [{ run: { via: 'function', name: 'f' } }] }] })
510
- * const definition = parsed && completeDraft(parsed) // ids/names filled positionally
511
- * draft.parse({ id: '', phases: [] }) // undefined — an explicit empty id is rejected
512
- * ```
513
- */
514
- export declare function createWorkflowDraftContract(): ContractInterface<WorkflowDraft>;
515
-
516
438
  /**
517
439
  * Create a workflow runner — a {@link WorkflowRunnerInterface} that EXECUTES a live W-b
518
440
  * workflow tree by COMPOSING the shipped substrate: phases sequential, tasks concurrent,
519
- * each task dispatched BY NAME under the workflow's `bail` policy.
520
- *
521
- * @remarks
522
- * The runner is THIN — it re-implements no concurrency / retry / abort logic. Per-phase
523
- * bounded concurrency is one {@link createRunner} per phase;
524
- * `bail` maps onto that Runner's fail-fast (`true` — the first failure aborts the in-flight
525
- * siblings + skips the rest) vs settle-all (`false` failures are recorded, the run
526
- * finishes); the run-level abort / timeout / budget ({@link import('./types.js').WorkflowRunOptions})
527
- * fold through `AbortSignal.any` (the agent runtime's pattern); pacing is the shipped
528
- * scheduler. `execute(definition, options?)` BUILDS the live tree from the definition itself
529
- * (via {@link createWorkflow}one source of truth, returned in `WorkflowResult.workflow`),
530
- * drives the live entity (`start` → `complete` / `fail`), and resolves a
441
+ * each task dispatched through its OWN resolved handler under the workflow's `bail` policy.
442
+ *
443
+ * @remarks
444
+ * The runner is a PURE engine — it re-implements no concurrency / retry / abort logic, AND it
445
+ * carries no `functions` / `tools` / `agents` registry of its own: each live task already
446
+ * resolved its own {@link import('./types.js').WorkflowFunction} into
447
+ * {@link import('./types.js').TaskInterface.handler} ONCE at construction, from the
448
+ * {@link WorkflowOptions.functions} registry supplied to `execute` / {@link createWorkflow}.
449
+ * Per-phase bounded concurrency is one {@link createRunner} per phase; `bail` maps onto that
450
+ * Runner's fail-fast (`true` the first failure aborts the in-flight siblings + skips the
451
+ * rest) vs settle-all (`false` failures are recorded, the run finishes); the run-level abort
452
+ * / timeout / budget ({@link import('./types.js').WorkflowRunOptions}) fold through
453
+ * `AbortSignal.any` (the agent runtime's pattern); pacing is the shipped scheduler.
454
+ * `execute(definition, options?)` BUILDS the live tree from the definition itself (via
455
+ * {@link createWorkflow} — one source of truth, returned in `WorkflowResult.workflow`), drives
456
+ * the live entity (`start` → `complete` / `fail`), and resolves a
531
457
  * {@link import('./types.js').WorkflowResult}.
532
458
  *
533
- * A task is dispatched on its {@link import('./types.js').TaskForm}: `function` the
534
- * `functions` registry, `tool` the `tools` {@link ToolManagerInterface}, `agent` → the
535
- * `agents` {@link import('./types.js').WorkflowAgents} resolver (W-c2), behind a depth + cycle
536
- * guard. A task whose handler is NOT found (an unregistered name for ANY form) AUTO-COMPLETES
459
+ * Static tool / agent calling is OPT-IN: a caller wires a plain
460
+ * {@link import('./types.js').WorkflowFunction} into its OWN {@link WorkflowOptions.functions}
461
+ * registry, same as any other behavior the `@orkestrel/tool` package ships the
462
+ * tool/agent adapter factories for that. A task with no resolved handler AUTO-COMPLETES
537
463
  * (the ROADMAP no-handler rule).
538
464
  *
539
- * The runner is constructed with a reference to {@link createWorkflowTool} (the workflow-tool
540
- * binder) so it can BIND a depth/cycle-aware workflow tool onto a dispatched subagent's context
541
- * — the propagation seam — WITHOUT this module's classes importing its own `factories.ts` (the
542
- * factories→classes direction; the binder is injected as a value at construction).
543
- *
544
- * @param options - The behavior registries (`functions` / `tools` / `agents`) the runner
545
- * dispatches a task by name through, plus an optional pacing `scheduler` (default the shipped
546
- * cross-environment one). Omitting `functions` / `tools` / `agents` makes those task forms
547
- * auto-complete (no handler). See {@link WorkflowRunnerOptions}.
465
+ * @param options - An optional pacing `scheduler` (default the shipped cross-environment one).
466
+ * See {@link WorkflowRunnerOptions}.
548
467
  * @returns A working {@link WorkflowRunnerInterface}
549
468
  *
550
469
  * @example
551
470
  * ```ts
552
- * import { createWorkflowRunner, createToolManager } from '@src/core'
471
+ * import { createWorkflowRunner } from '@src/core'
553
472
  *
554
- * const tools = createToolManager()
555
- * const runner = createWorkflowRunner({
556
- * functions: { compile: async (controller) => `built ${controller.task.id}` },
557
- * tools,
558
- * })
473
+ * const runner = createWorkflowRunner()
559
474
  * const definition = { id: 'w', name: 'W', phases: [{ id: 'p', name: 'P', tasks: [
560
- * { id: 't', name: 'T', run: { via: 'function', name: 'compile' } },
475
+ * { id: 't', name: 'T', run: 'compile' },
561
476
  * ] }] }
562
- * const result = await runner.execute(definition) // builds + drives the tree
477
+ * const result = await runner.execute(definition, {
478
+ * functions: { compile: async (controller) => `built ${controller.task.id}` },
479
+ * })
563
480
  * result.status // 'completed'
564
481
  * result.workflow.phase('p')?.task('t')?.status // 'completed'
565
482
  * ```
566
483
  */
567
484
  export declare function createWorkflowRunner(options?: WorkflowRunnerOptions): WorkflowRunnerInterface;
568
485
 
569
- /**
570
- * Wrap a {@link WorkflowDefinition} as an LLM-callable {@link ToolInterface} — it ADVERTISES
571
- * the SIMPLE flat authoring shape (`{ name?, steps: [{ name, via? }] }`) as its `parameters` so
572
- * even a small model can author a complete tree, and its handler EXPANDS / COMPLETES the
573
- * authored blob, validates it against the STRICT contract, runs it through `runner`, and
574
- * returns the run SUMMARY (throwing a typed {@link WorkflowError} on failure).
575
- *
576
- * @remarks
577
- * A plain {@link ToolManagerInterface}-compatible tool (so `createMCPServer` / `createMCPRoutes`
578
- * expose it for free — nothing MCP is wired here). It is ALSO the propagation carrier the
579
- * {@link WorkflowRunner} binds onto a dispatched subagent (W-c2): because a tool handler receives
580
- * ONLY the model-supplied `args` (no ambient context, no signal), the run's depth + ancestry are
581
- * CLOSED OVER at bind time via {@link WorkflowToolOptions}, and the handler runs the nested
582
- * workflow at `depth + 1` with the extended ancestry.
583
- *
584
- * **Widened authoring surface (additive — the canonical contract + runner stay STRICT and
585
- * unchanged).** A 2B model reliably CALLS the tool but cannot reliably emit the full four-level
586
- * nested {@link WorkflowDefinition} (six required `id`/`name` strings, a nested tagged union,
587
- * all-or-nothing). So the tool ACCEPTS three authoring forms and converges them on the SAME
588
- * strict {@link createWorkflowContract} gate before running (soundness preserved):
589
- * - the FLAT shape `{ name?, steps: [{ name, via? }] }` — the ADVERTISED `parameters` (the simplest
590
- * form, {@link import('./helpers.js').expandSteps}'d into one one-task phase per step);
591
- * - a nested DRAFT with any `id`/`name` OMITTED — {@link createWorkflowDraftContract}-parsed then
592
- * {@link import('./helpers.js').completeDraft}'d (missing ids synthesized positionally);
593
- * - the full nested {@link WorkflowDefinition} — the advanced escape-hatch (documented in the
594
- * description), accepted as the draft super-set.
595
- *
596
- * The handler conforms to the universal tool-handler contract (AGENTS §14): it returns the PLAIN
597
- * run-summary VALUE on success and THROWS a typed {@link WorkflowError} on every failure path. It
598
- * does NOT build a {@link ToolResult} itself — the `@orkestrel/agent` package's `ToolManager`
599
- * performs the ONE canonical wrap (`{ id, name, value }` on a return; `{ id, name, error }` on a
600
- * throw, ISOLATED so nothing escapes the run), so the outcome appears EXACTLY ONCE, identically,
601
- * over BOTH the agent loop and MCP (a throw → MCP `isError: true`):
602
- * - **No authored args** (an empty `arguments`) ⇒ runs the WRAPPED `definition`.
603
- * - **A `steps` array** ⇒ the FLAT form: parse it, {@link import('./helpers.js').expandSteps} it.
604
- * - **Otherwise** ⇒ the nested form: {@link createWorkflowDraftContract}-parse it,
605
- * {@link import('./helpers.js').completeDraft} it.
606
- * - **Strict gate** ⇒ the expanded / completed result is validated against
607
- * {@link createWorkflowContract}.`is`; a blob that can't expand, or whose result fails the strict
608
- * gate (e.g. an explicit empty `id`, `concurrency: 0`) ⇒ THROW a `TOOL` {@link WorkflowError} (no run).
609
- * - **Over-deep / cyclic** ⇒ THROW a `DEPTH` {@link WorkflowError} when the nested run would exceed
610
- * {@link MAX_WORKFLOW_DEPTH}, or the target workflow id is already an ancestor (a cycle) — the
611
- * same `code` the agent-task guard raises.
612
- * - **Otherwise** ⇒ `runner.execute(target, { depth: depth + 1, ancestry: … })`, RETURNING the
613
- * plain summary of the terminal run (`{ status, count }`, via {@link workflowToolSummary}).
614
- *
615
- * @param definition - The workflow the tool runs when called with no authored args
616
- * @param runner - The {@link WorkflowRunnerInterface} that executes the (nested) workflow
617
- * @param options - The depth + ancestry to run the nested workflow under (see
618
- * {@link WorkflowToolOptions}); omitted ⇒ depth `0` / empty ancestry (a top-level wrap)
619
- * @returns A {@link ToolInterface} (named {@link import('./constants.js').WORKFLOW_TOOL_NAME})
620
- * whose `parameters` advertise the FLAT authoring schema (the nested form stays accepted)
621
- *
622
- * @example
623
- * ```ts
624
- * import { createWorkflowRunner, createWorkflowTool, createToolManager } from '@src/core'
625
- *
626
- * const runner = createWorkflowRunner()
627
- * const tool = createWorkflowTool(definition, runner)
628
- * const tools = createToolManager()
629
- * tools.add(tool) // a model can now author + run a workflow in one call
630
- * ```
631
- */
632
- export declare function createWorkflowTool(definition: WorkflowDefinition, runner: WorkflowRunnerInterface, options?: WorkflowToolOptions): ToolInterface;
633
-
634
486
  /**
635
487
  * A {@link WorkflowStoreInterface} backed by one table of the `databases` layer — a
636
488
  * workflow's durable run-state IS a row, so persistence reduces to keyed point-access
@@ -702,17 +554,23 @@ export declare const DEFAULT_BAIL = false;
702
554
  /**
703
555
  * The default per-phase task concurrency the {@link import('./factories.js').createWorkflowRunner}
704
556
  * runner applies when a {@link import('./types.js').PhaseDefinition} omits its `concurrency`
705
- * throttle — a large cap that is effectively unbounded for any realistic phase.
557
+ * throttle — a cap that is effectively unbounded for any realistic phase.
706
558
  *
707
559
  * @remarks
708
560
  * The determinism principle fixes that a phase's tasks run CONCURRENTLY; `concurrency` is
709
561
  * only an optional resource throttle (max-in-flight). With none declared, the runner runs
710
- * all of a phase's tasks at once — modelled as this large finite cap so the value flows
711
- * straight into the substrate {@link import('./types.js').RunnerInterface}'s
712
- * `concurrency` (which expects a positive integer) without a special unbounded branch. No
713
- * realistic phase declares enough tasks to reach it, so it behaves as "run them all".
562
+ * all of a phase's tasks at once — modelled as this finite cap so the value flows straight
563
+ * into the substrate {@link import('./types.js').RunnerInterface}'s `concurrency` (which
564
+ * expects a positive integer) without a special unbounded branch. No realistic phase
565
+ * declares enough tasks to reach it, so it behaves as "run them all".
566
+ *
567
+ * WHY `1024` and not a huge sentinel like `1_000_000`: the backing `@orkestrel/queue` Runner
568
+ * EAGERLY spawns one parked worker loop per concurrency unit AT CONSTRUCTION, so this default
569
+ * must be a value whose eager allocation cost is negligible for every default-concurrency
570
+ * phase — a million-unit default meant ~1e6 promise/closure allocations per such phase. A
571
+ * phase may still DECLARE a larger explicit `concurrency` and pays that allocation knowingly.
714
572
  */
715
- export declare const DEFAULT_PHASE_CONCURRENCY = 1000000;
573
+ export declare const DEFAULT_PHASE_CONCURRENCY = 1024;
716
574
 
717
575
  /**
718
576
  * A promise paired with its externally-callable `resolve`/`reject` — the settle path
@@ -733,10 +591,12 @@ export declare interface DeferredInterface<T> {
733
591
  *
734
592
  * @remarks
735
593
  * The structural fields (`id` / `name` / `description` + the ordered phases / tasks)
736
- * carry over verbatim; the W-b live tree is the DECLARATIVE state machine, so the
737
- * execution-only definition fields (per-phase `run` / `concurrency`, per-task `retries` /
738
- * `timeout`) are intentionally dropped (W-c reads them from the definition when it drives
739
- * transitions). The `bail` policy carries over at the workflow tier AND, per phase, the
594
+ * carry over verbatim, as does each phase's `concurrency` (persisted on the
595
+ * {@link PhaseSnapshot} so a restore reinstates the same throttle) and each task's `run` /
596
+ * `retries` / `timeout` (persisted on the {@link TaskSnapshot}, like `bail` / `concurrency`,
597
+ * so a restore + a {@link import('./types.js').WorkflowOptions.functions} registry resumes
598
+ * real work). The `bail` policy carries over — at the
599
+ * workflow tier AND, per phase, the
740
600
  * EFFECTIVE policy (`phase.bail ?? workflowBail`) on each {@link PhaseSnapshot} — so the seeded
741
601
  * snapshot is self-contained; a fresh seed has no `override`. `created` / `updated` are stamped now.
742
602
  * {@link import('./factories.js').createWorkflow} builds from this.
@@ -754,6 +614,34 @@ export declare interface DeferredInterface<T> {
754
614
  */
755
615
  export declare function definitionToSnapshot(definition: WorkflowDefinition, bail?: boolean): WorkflowSnapshot;
756
616
 
617
+ /**
618
+ * Derive the PENDING SUFFIX boundary of a positional list of {@link LifecycleStatus}es —
619
+ * the index of the first entry in the contiguous trailing run of `pending` entries.
620
+ *
621
+ * @remarks
622
+ * The native, hook-free replacement for a runner-installed cursor (AGENTS §12): a
623
+ * {@link import('./types.js').WorkflowInterface}'s `add` / `remove` / `move` / `update`
624
+ * reads this over its live phases' statuses to decide which positions are safe to edit.
625
+ * Because entries run SEQUENTIALLY (phases sequential, AGENTS determinism), every
626
+ * already-started entry forms a contiguous LEADING prefix and every still-`pending`
627
+ * entry forms the trailing suffix — so the boundary is simply the count of leading
628
+ * non-`pending` entries: the index of the first `pending` entry, or the full length when
629
+ * none is `pending` (nothing is safely editable). A `pending` container's entries are ALL
630
+ * `pending`, so the boundary is `0` and every position is naturally accepted — callers
631
+ * need no special case for that.
632
+ *
633
+ * @param statuses - The positional list of statuses to derive the boundary from
634
+ * @returns The index of the first `pending` entry, or `statuses.length` when none is `pending`
635
+ *
636
+ * @example
637
+ * ```ts
638
+ * deriveBoundary(['completed', 'running', 'pending', 'pending']) // 2
639
+ * deriveBoundary(['pending', 'pending']) // 0
640
+ * deriveBoundary(['completed', 'completed']) // 2 (nothing pending)
641
+ * ```
642
+ */
643
+ export declare function deriveBoundary(statuses: readonly LifecycleStatus[]): number;
644
+
757
645
  /**
758
646
  * Derive a phase's status from its tasks' statuses (tasks are concurrent, so this
759
647
  * is an order-insensitive reduction).
@@ -808,47 +696,67 @@ export declare function derivePhaseStatus(tasks: readonly TaskStatus[]): PhaseSt
808
696
  export declare function deriveWorkflowStatus(phases: readonly PhaseDerivation[]): WorkflowStatus;
809
697
 
810
698
  /**
811
- * Expand a flat {@link WorkflowSteps} blob into a strict {@link WorkflowDefinition} — each
812
- * step becomes a one-task phase, IN ORDER.
699
+ * Box an error as a {@link Failure} the graceful outcome half of a {@link Result}.
813
700
  *
814
- * @remarks
815
- * The expansion of the tool's ADVERTISED surface (AGENTS §21 — the simplest form a small
816
- * model can author). Each {@link WorkflowStep} maps to a phase holding exactly one task:
817
- * the step's `name` becomes the task's `run.name`, and its `via` becomes the task's `run.via`
818
- * (defaulting to `'function'` when omitted). Ids/names are auto-filled positionally — it
819
- * builds an ids-omitted {@link WorkflowDraft} and delegates to {@link completeDraft}, so the
820
- * two lenient surfaces share ONE synthesis path (step `i` → phase `phase-<i>`, its task
821
- * `phase-<i>-task-0`). The optional `name` becomes the workflow's `name`. The result is a
822
- * complete definition the caller validates against the STRICT contract before running.
701
+ * @typeParam E - The boxed error's type
702
+ * @param error - The error to box
703
+ * @returns A {@link Failure} wrapping `error`
823
704
  *
824
- * @param flat - The flat steps blob (`{ name?, steps: [{ name, via? }] }`)
825
- * @returns A complete {@link WorkflowDefinition} (one one-task phase per step)
705
+ * @example
706
+ * ```ts
707
+ * const result = failure(new WorkflowError('MUTATION', 'refused')) // { success: false, error }
708
+ * ```
826
709
  */
827
- export declare function expandSteps(flat: WorkflowSteps): WorkflowDefinition;
710
+ export declare function failure<E>(error: E): Failure<E>;
828
711
 
829
712
  /**
830
- * Narrow a {@link TaskForm} to the `agent` form a task that runs a registered
831
- * agent (a subagent).
713
+ * Find the first {@link TaskResult} in a positional list whose boxed outcome is a
714
+ * `Failure` — the pure scan shared by a phase's and a workflow's derived-`failed`
715
+ * `fail`-event lookup.
716
+ *
717
+ * @remarks
718
+ * The shared leaf behind {@link import('./phases/Phase.js').Phase} and
719
+ * {@link import('./Workflow.js').Workflow}'s own `#failure` — each gathers ITS tier's
720
+ * results (a phase's own settled tasks, a workflow's flattened `results()`) and feeds
721
+ * them here; the tier-local method keeps the §12 invariant throw (a derived `failed`
722
+ * status guarantees a failing result exists) since throwing on `undefined` is
723
+ * orchestration, not a leaf concern.
832
724
  *
833
- * @param form - The task form to test
834
- * @returns `true` when `form.via` is `'agent'`
725
+ * @param results - The results to scan, in any order
726
+ * @returns The first result whose `result.success` is `false`, or `undefined` if none
727
+ *
728
+ * @example
729
+ * ```ts
730
+ * findFailure([completedResult, failedResult]) // failedResult
731
+ * ```
835
732
  */
836
- export declare function isAgentTask(form: TaskForm): form is {
837
- readonly via: 'agent';
838
- readonly name: string;
839
- };
733
+ export declare function findFailure(results: readonly TaskResult[]): TaskResult | undefined;
840
734
 
841
735
  /**
842
- * Narrow a {@link TaskForm} to the `function` form a task that runs a registered
843
- * function.
736
+ * Insert one `[key, value]` entry at a positional index into a readonly entries array
737
+ * the pure splice-in step behind an insertion-ordered registry's `add`.
738
+ *
739
+ * @remarks
740
+ * Shared by {@link import('./tasks/TaskManager.js').TaskManager} and
741
+ * {@link import('./phases/PhaseManager.js').PhaseManager}: both convert their
742
+ * insertion-ordered `Map` to `[...map.entries()]`, call this to splice the new entry
743
+ * in at the target index, then rebuild the `Map` from the result (a stateful step that
744
+ * stays a `#` private method — this helper does no `Map` construction). Does not
745
+ * mutate `entries`; returns a new array.
844
746
  *
845
- * @param form - The task form to test
846
- * @returns `true` when `form.via` is `'function'`
747
+ * @typeParam T - The entry's value type
748
+ * @param entries - The current positional entries, in order
749
+ * @param index - The index to insert at (`0` prepends, `entries.length` appends)
750
+ * @param key - The new entry's key
751
+ * @param value - The new entry's value
752
+ * @returns A new entries array with `[key, value]` inserted at `index`
753
+ *
754
+ * @example
755
+ * ```ts
756
+ * insertEntry([['a', 1], ['b', 2]], 1, 'c', 3) // [['a', 1], ['c', 3], ['b', 2]]
757
+ * ```
847
758
  */
848
- export declare function isFunctionTask(form: TaskForm): form is {
849
- readonly via: 'function';
850
- readonly name: string;
851
- };
759
+ export declare function insertEntry<T>(entries: readonly (readonly [string, T])[], index: number, key: string, value: T): readonly (readonly [string, T])[];
852
760
 
853
761
  /**
854
762
  * Test whether a {@link LifecycleStatus} is TERMINAL — a node in this state will not
@@ -867,17 +775,6 @@ export declare function isFunctionTask(form: TaskForm): form is {
867
775
  */
868
776
  export declare function isTerminalStatus(status: LifecycleStatus): boolean;
869
777
 
870
- /**
871
- * Narrow a {@link TaskForm} to the `tool` form — a task that runs a registered tool.
872
- *
873
- * @param form - The task form to test
874
- * @returns `true` when `form.via` is `'tool'`
875
- */
876
- export declare function isToolTask(form: TaskForm): form is {
877
- readonly via: 'tool';
878
- readonly name: string;
879
- };
880
-
881
778
  /**
882
779
  * Narrow an unknown caught value to a {@link WorkflowError}.
883
780
  *
@@ -931,20 +828,6 @@ export declare function isWorkflowSnapshot(value: unknown): value is WorkflowSna
931
828
  */
932
829
  export declare type LifecycleStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped' | 'stopped';
933
830
 
934
- /**
935
- * The maximum nesting depth a workflow's `agent` task may spawn into (W-c) — the
936
- * bound the runner's depth/cycle guard enforces.
937
- *
938
- * @remarks
939
- * The limit lives in ONE place. The `agent` {@link import('./types.js').TaskForm} is
940
- * bounded by it when the {@link import('./WorkflowRunner.js').WorkflowRunner} resolves a
941
- * subagent: an agent running at this depth can no longer author + run a nested workflow
942
- * (that would be depth `MAX_WORKFLOW_DEPTH + 1`), so the over-deep `agent` task is
943
- * rejected (a typed `DEPTH` `task.fail`). The chain therefore nests workflows down to
944
- * this depth, and the `agent` task in the depth-`MAX_WORKFLOW_DEPTH` workflow fails.
945
- */
946
- export declare const MAX_WORKFLOW_DEPTH = 8;
947
-
948
831
  /**
949
832
  * The in-memory {@link WorkflowStoreInterface} — a process-lifetime `Map` of
950
833
  * {@link WorkflowSnapshot}s keyed by workflow id, the DEFAULT store
@@ -990,6 +873,54 @@ export declare class MemoryWorkflowStore implements WorkflowStoreInterface {
990
873
  delete(id: string): Promise<void>;
991
874
  }
992
875
 
876
+ /**
877
+ * Reposition the entry keyed `key` to a new positional index in a readonly entries
878
+ * array — the pure remove-then-reinsert step behind an insertion-ordered registry's
879
+ * `move`.
880
+ *
881
+ * @remarks
882
+ * The move counterpart of {@link insertEntry}: finds the entry by `key`, splices it
883
+ * out, then splices it back in at `index`. An absent `key` is a no-op (returns a copy
884
+ * of `entries` unchanged) — the caller (`TaskManager.move` / `PhaseManager.move`)
885
+ * already gates on the target's existence before calling this, so the no-op branch is
886
+ * defensive, never reached in practice. Does not mutate `entries`; returns a new array.
887
+ *
888
+ * @typeParam T - The entry's value type
889
+ * @param entries - The current positional entries, in order
890
+ * @param key - The key of the entry to reposition
891
+ * @param index - The new index for the entry
892
+ * @returns A new entries array with the `key` entry repositioned to `index`
893
+ *
894
+ * @example
895
+ * ```ts
896
+ * moveEntry([['a', 1], ['b', 2], ['c', 3]], 'a', 2) // [['b', 2], ['c', 3], ['a', 1]]
897
+ * ```
898
+ */
899
+ export declare function moveEntry<T>(entries: readonly (readonly [string, T])[], key: string, index: number): readonly (readonly [string, T])[];
900
+
901
+ /**
902
+ * Park until `signal` aborts — a promise-parked wait (AGENTS §21), never a timer or
903
+ * busy-loop, that NEVER rejects.
904
+ *
905
+ * @remarks
906
+ * Resolves IMMEDIATELY when `signal` is already aborted; otherwise attaches a one-shot
907
+ * `abort` listener and resolves when it fires, removing the listener either way. The
908
+ * shared leaf behind the duplicate abort-wiring an execution engine otherwise hand-rolls
909
+ * at every fold point.
910
+ *
911
+ * @param signal - The signal to park on
912
+ * @returns A promise that resolves once `signal` has aborted
913
+ *
914
+ * @example
915
+ * ```ts
916
+ * const controller = new AbortController()
917
+ * const parked = parkSignal(controller.signal)
918
+ * controller.abort()
919
+ * await parked // resolves
920
+ * ```
921
+ */
922
+ export declare function parkSignal(signal: AbortSignal): Promise<void>;
923
+
993
924
  /**
994
925
  * The live DERIVED state machine (W-b) for one phase — an observable (AGENTS §13) whose
995
926
  * {@link PhaseStatus} is computed from its tasks (never set directly) and recomputed
@@ -1011,10 +942,38 @@ export declare class MemoryWorkflowStore implements WorkflowStoreInterface {
1011
942
  * `start` / `complete` / `fail` / `stop` on a derived-status CHANGE, strictly AFTER the
1012
943
  * recompute + escalate; the emitter isolates a listener throw and routes it to its `error`
1013
944
  * handler (the `error` option); `fail` carries the failing task's {@link TaskResult}.
945
+ * - **Structural API (AGENTS §7).** `add` / `remove` / `move` / `update` gate BEFORE
946
+ * delegating to {@link tasks} (the manager gates the target's own existence/status/id/
947
+ * bounds), then emit the matching {@link PhaseEventMap} event on success only. NATIVE
948
+ * gating, purely from this phase's own derived `status` (no runner-installed hook): while
949
+ * `pending`, any valid `index` is accepted; while `running`, `add` accepts ONLY a pure
950
+ * append (a live runner subscribed to the `add` event picks it up), and `remove` / `move` /
951
+ * `update` always fail gracefully (the tasks are already handed to the execution
952
+ * substrate); while terminal, everything is refused.
953
+ * - **Patch (AGENTS §12).** `patch` applies a validated {@link PhaseUpdate} to SELF
954
+ * (`name` / `description` / `concurrency` / `bail`) — defense-in-depth: it throws a
955
+ * `MUTATION` {@link WorkflowError} unless this phase's own `status` is `pending`, mirroring
956
+ * the owning {@link WorkflowInterface.update}'s gate.
957
+ * - **Minting (AGENTS §7).** {@link add} MINTS a live {@link Task} from a {@link TaskDefinition}
958
+ * (converts it to a {@link TaskSnapshot}, builds the task wired to THIS phase) — the same
959
+ * construction path {@link #append} uses at build time, so a live mint and a restored/built
960
+ * task are wired IDENTICALLY. At construction, the workflow-level
961
+ * {@link import('../types.js').WorkflowFunctions} registry (threaded from
962
+ * {@link import('../types.js').WorkflowOptions.functions}) resolves each task's `run` name into
963
+ * its runtime {@link import('../types.js').TaskInterface.handler} ONCE; a `run` that is omitted
964
+ * or unregistered resolves to no handler (the no-handler rule).
965
+ * - **Runtime lifecycle (AGENTS §10).** `pause` / `resume` / `wait` mirror the workflow's own
966
+ * quartet, scoped to this phase — a driving
967
+ * {@link import('../types.js').WorkflowRunnerInterface.execute} gates a task's own
968
+ * pre-dispatch on the workflow's gate FIRST, then this phase's gate, WITHOUT touching
969
+ * {@link status} — `paused` is runtime-only, never persisted. `skip` / `stop` (this phase's
970
+ * own terminal forcing) always release a parked {@link wait} waiter, mirroring
971
+ * {@link import('../Workflow.js').Workflow.destroy}'s cascade — a permanently-ended phase
972
+ * has nothing left to pause for.
1014
973
  */
1015
974
  export declare class Phase implements PhaseInterface {
1016
975
  #private;
1017
- constructor(snapshot: PhaseSnapshot, workflow: WorkflowInterface, escalate: () => void, options?: PhaseOptions, bail?: boolean);
976
+ constructor(snapshot: PhaseSnapshot, workflow: WorkflowInterface, escalate: () => void, options?: PhaseOptions, bail?: boolean, functions?: WorkflowFunctions);
1018
977
  get emitter(): EmitterInterface<PhaseEventMap>;
1019
978
  get id(): string;
1020
979
  get name(): string;
@@ -1022,12 +981,22 @@ export declare class Phase implements PhaseInterface {
1022
981
  get context(): PhaseContext;
1023
982
  get workflow(): WorkflowInterface;
1024
983
  get bail(): boolean;
984
+ get concurrency(): number | undefined;
985
+ get paused(): boolean;
1025
986
  get status(): PhaseStatus;
1026
987
  get tasks(): TaskManagerInterface;
1027
988
  task(id: string): TaskInterface | undefined;
1028
989
  results(): readonly TaskResult[];
1029
990
  skip(): void;
1030
991
  stop(): void;
992
+ pause(): void;
993
+ resume(): void;
994
+ wait(): Promise<void>;
995
+ add(definition: TaskDefinition, index?: number): Result<TaskInterface, WorkflowError>;
996
+ remove(id: string): Result<TaskInterface, WorkflowError>;
997
+ move(id: string, index: number): Result<TaskInterface, WorkflowError>;
998
+ update(id: string, patch: TaskUpdate): Result<TaskInterface, WorkflowError>;
999
+ patch(value: PhaseUpdate): void;
1031
1000
  snapshot(): PhaseSnapshot;
1032
1001
  }
1033
1002
 
@@ -1082,6 +1051,7 @@ export declare interface PhaseDefinition {
1082
1051
  * The snapshot persists the EFFECTIVE failure policy this phase runs under: the phase's own
1083
1052
  * `bail` when it declares one, else the `workflowBail` it inherits — so a restore reinstates
1084
1053
  * the same per-phase policy without a silent default (`effectiveBail = phase.bail ?? workflowBail`).
1054
+ * `concurrency` (the resource throttle) carries over verbatim, omitted when undefined.
1085
1055
  *
1086
1056
  * @param phase - The phase definition to seed from
1087
1057
  * @param workflowBail - The workflow-level `bail` default the phase inherits when it declares none
@@ -1106,47 +1076,6 @@ export declare interface PhaseDerivation {
1106
1076
  readonly bail: boolean;
1107
1077
  }
1108
1078
 
1109
- /** A draft phase — a {@link PhaseDefinition} with OPTIONAL `id` / `name` and {@link TaskDraft} tasks. */
1110
- export declare interface PhaseDraft {
1111
- readonly id?: string;
1112
- readonly name?: string;
1113
- readonly description?: string;
1114
- readonly tasks: readonly TaskDraft[];
1115
- /** Max tasks in flight at once (a resource throttle); omitted ⇒ unbounded. */
1116
- readonly concurrency?: number;
1117
- /** The per-phase failure-policy OVERRIDE; omitted ⇒ inherits the workflow `bail` (`effectiveBail = phase.bail ?? workflow.bail`). */
1118
- readonly bail?: boolean;
1119
- }
1120
-
1121
- /**
1122
- * The shape of a PHASE in a draft workflow — identical to {@link phaseShape} EXCEPT
1123
- * `id` and `name` are OPTIONAL, and its tasks are {@link taskDraftShape}s.
1124
- */
1125
- export declare const phaseDraftShape: ObjectShape<{
1126
- id: OptionalShape<StringShape>;
1127
- name: OptionalShape<StringShape>;
1128
- description: OptionalShape<StringShape>;
1129
- tasks: ArrayShape<ObjectShape<{
1130
- id: OptionalShape<StringShape>;
1131
- name: OptionalShape<StringShape>;
1132
- description: OptionalShape<StringShape>;
1133
- run: UnionShape<[ ObjectShape<{
1134
- via: LiteralShape<readonly ["function"]>;
1135
- name: StringShape;
1136
- }>, ObjectShape<{
1137
- via: LiteralShape<readonly ["tool"]>;
1138
- name: StringShape;
1139
- }>, ObjectShape<{
1140
- via: LiteralShape<readonly ["agent"]>;
1141
- name: StringShape;
1142
- }>]>;
1143
- retries: OptionalShape<NumberShape>;
1144
- timeout: OptionalShape<NumberShape>;
1145
- }>>;
1146
- concurrency: OptionalShape<NumberShape>;
1147
- bail: OptionalShape<LiteralShape<readonly [true, false]>>;
1148
- }>;
1149
-
1150
1079
  /**
1151
1080
  * The push observation surface (AGENTS §13) of the phase entity (W-b) — analogous
1152
1081
  * to {@link WorkflowEventMap}, scoped to one phase.
@@ -1154,9 +1083,12 @@ export declare const phaseDraftShape: ObjectShape<{
1154
1083
  * @remarks
1155
1084
  * `start` fires when the phase begins; `complete` when all its tasks settled
1156
1085
  * successfully; `fail` when a task failed under `bail` (carrying the
1157
- * {@link TaskResult}); `stop` when the phase was ended. A throwing listener is
1158
- * isolated by the emitter and routed to its `error` handler, not the domain surface
1159
- * (AGENTS §13). A `type` alias (AGENTS §4.5) so it satisfies `EventMap`.
1086
+ * {@link TaskResult}); `stop` when the phase was ended. `add` / `remove` / `move` /
1087
+ * `update` fire on a successful structural or patch edit through
1088
+ * {@link PhaseInterface.add} / `remove` / `move` / `update` (AGENTS §7) never on a
1089
+ * refused/gated one. A throwing listener is isolated by the emitter and routed to its
1090
+ * `error` handler, not the domain surface (AGENTS §13). A `type` alias (AGENTS §4.5)
1091
+ * so it satisfies `EventMap`.
1160
1092
  */
1161
1093
  export declare type PhaseEventMap = {
1162
1094
  /** The phase began — its `id`. */
@@ -1167,6 +1099,14 @@ export declare type PhaseEventMap = {
1167
1099
  readonly fail: readonly [result: TaskResult];
1168
1100
  /** The phase was permanently stopped. */
1169
1101
  readonly stop: readonly [];
1102
+ /** A task was inserted — the inserted task + its final index. */
1103
+ readonly add: readonly [task: TaskInterface, index: number];
1104
+ /** A task was removed — the removed task. */
1105
+ readonly remove: readonly [task: TaskInterface];
1106
+ /** A task was repositioned — the moved task + its new index. */
1107
+ readonly move: readonly [task: TaskInterface, index: number];
1108
+ /** A task was patched — the patched task. */
1109
+ readonly update: readonly [task: TaskInterface];
1170
1110
  };
1171
1111
 
1172
1112
  /** Initial {@link PhaseEventMap} listeners — the reserved `on` option (AGENTS §8). */
@@ -1194,6 +1134,12 @@ export declare type PhaseInput = Partial<PhaseContext>;
1194
1134
  * - **Observable (AGENTS §13).** The owned {@link emitter} ({@link PhaseEventMap}) fires
1195
1135
  * `start` / `complete` / `fail` / `stop` on a derived-status change; the emitter isolates a
1196
1136
  * listener throw and routes it to its `error` handler (the `error` option).
1137
+ * - **Runtime lifecycle (AGENTS §10).** `pause` / `resume` / `wait` mirror
1138
+ * {@link WorkflowInterface.pause} / `resume` / `wait`, scoped to this phase — a driving
1139
+ * {@link WorkflowRunnerInterface.execute} gates a task's own pre-dispatch on BOTH the
1140
+ * workflow's and its phase's gate. `paused` is RUNTIME-ONLY, never persisted; idempotent;
1141
+ * released by `resume` and by this phase's own `stop` / `skip` forcing a terminal status
1142
+ * (a permanently-ended phase has nothing left to pause for).
1197
1143
  */
1198
1144
  export declare interface PhaseInterface {
1199
1145
  readonly emitter: EmitterInterface<PhaseEventMap>;
@@ -1205,13 +1151,167 @@ export declare interface PhaseInterface {
1205
1151
  readonly status: PhaseStatus;
1206
1152
  /** The RESOLVED effective failure policy this phase runs under (`phase.bail ?? workflow.bail`); mirrors {@link WorkflowInterface.bail}. */
1207
1153
  readonly bail: boolean;
1154
+ /** Max tasks in flight at once (a resource throttle); mirrors {@link PhaseSnapshot.concurrency}. `undefined` ⇒ unbounded. */
1155
+ readonly concurrency: number | undefined;
1156
+ /**
1157
+ * Whether the phase is currently paused (AGENTS §10 — resumable); RUNTIME-ONLY — never a
1158
+ * {@link PhaseStatus}, never persisted in a {@link PhaseSnapshot} (a paused phase's
1159
+ * `status` still reports its ordinary derived value).
1160
+ */
1161
+ readonly paused: boolean;
1208
1162
  readonly tasks: TaskManagerInterface;
1209
1163
  /** Look up one live task by its `id`. */
1210
1164
  task(id: string): TaskInterface | undefined;
1211
1165
  /** The settled tasks' results, in positional order — the phase tier of the result tree. */
1212
1166
  results(): readonly TaskResult[];
1167
+ /**
1168
+ * FORCE this phase to `skipped` (AGENTS §10), overriding the derived value; idempotent.
1169
+ *
1170
+ * @remarks
1171
+ * A NO-OP once `status` is already terminal — a settled phase cannot be re-forced. Always
1172
+ * releases a parked {@link wait} waiter regardless (a terminal phase has nothing left to
1173
+ * pause for).
1174
+ */
1213
1175
  skip(): void;
1176
+ /**
1177
+ * FORCE this phase to `stopped` (AGENTS §10), overriding the derived value; idempotent.
1178
+ *
1179
+ * @remarks
1180
+ * A NO-OP once `status` is already terminal (a settled phase cannot be re-forced). Always
1181
+ * releases a parked {@link wait} waiter regardless (a terminal phase has nothing left to
1182
+ * pause for).
1183
+ */
1214
1184
  stop(): void;
1185
+ /**
1186
+ * Suspend the phase (AGENTS §10 — resumable); idempotent.
1187
+ *
1188
+ * @remarks
1189
+ * A no-op when already `paused` or when `status` is terminal. RUNTIME-ONLY (AGENTS §10) —
1190
+ * never a {@link PhaseStatus}, never persisted in a {@link PhaseSnapshot}. A driving
1191
+ * {@link WorkflowRunnerInterface.execute} gates a task's own pre-dispatch on this phase's
1192
+ * gate (after the workflow's own gate). **Pausing does NOT suspend a driving run's
1193
+ * timeout / budget / abort clocks** — those bounds keep ticking while paused, so a long
1194
+ * pause can still fire a run-level cancel and stop the workflow while parked.
1195
+ *
1196
+ * @example
1197
+ * ```ts
1198
+ * phase.pause()
1199
+ * phase.paused // true
1200
+ * ```
1201
+ */
1202
+ pause(): void;
1203
+ /**
1204
+ * Continue a paused phase (AGENTS §10); idempotent — a no-op unless {@link paused}.
1205
+ *
1206
+ * @example
1207
+ * ```ts
1208
+ * phase.resume()
1209
+ * phase.paused // false
1210
+ * ```
1211
+ */
1212
+ resume(): void;
1213
+ /**
1214
+ * Park until this phase is not paused — **promise-parked**, never a timer or busy-loop
1215
+ * (AGENTS §21; mirrors {@link WorkflowInterface.wait}).
1216
+ *
1217
+ * @remarks
1218
+ * Resolves IMMEDIATELY when not {@link paused}. While paused, parks until `resume` or
1219
+ * this phase's own `stop` / `skip` forcing a terminal status — all release a parked
1220
+ * waiter. NEVER rejects.
1221
+ *
1222
+ * @returns A promise that resolves once the phase is no longer paused
1223
+ */
1224
+ wait(): Promise<void>;
1225
+ /**
1226
+ * MINT a live {@link TaskInterface} from `definition` and insert it into this phase
1227
+ * (AGENTS §7 the entity structural API) — gated BEFORE delegating to {@link tasks}'
1228
+ * manager.
1229
+ *
1230
+ * @remarks
1231
+ * Converts `definition` → {@link TaskSnapshot} and constructs the live task (wired to
1232
+ * THIS phase, its recompute cascade, and its emitter hooks), carrying its `run` /
1233
+ * `retries` / `timeout` from `definition` and resolving its {@link TaskInterface.handler}
1234
+ * against the workflow-level {@link WorkflowOptions.functions} registry — the SAME
1235
+ * resolution {@link import('./factories.js').createWorkflow} performs at build time.
1236
+ * Requires `definition.id` to be UNIQUE among this phase's existing
1237
+ * task ids — a duplicate is a `MUTATION` failure (mirrors
1238
+ * {@link TaskManagerInterface.add}'s own duplicate-id gate).
1239
+ *
1240
+ * NATIVE gating, purely from this phase's own derived `status` (AGENTS §12 — no
1241
+ * runner-installed hook), UNCHANGED from the entity-taking predecessor. While
1242
+ * `pending`: any valid `index` is accepted (delegates the minted task to
1243
+ * {@link TaskManagerInterface.add} then emits `add`). While `running`: accepted ONLY as
1244
+ * a pure append (`index` omitted or `=== tasks.count`) — a live runner subscribed to
1245
+ * the `add` event picks the new task up for same-run execution; the derived-status
1246
+ * model guarantees this phase cannot reach a terminal status while the accepted task is
1247
+ * still `pending` (its status feeds `status` via {@link import('./helpers.js').derivePhaseStatus}).
1248
+ * While terminal: always refused.
1249
+ *
1250
+ * **Abort edge.** An append ACCEPTED while `running` can still settle `skipped` rather
1251
+ * than run — if the driving run is cancelled (abort / timeout / budget / `workflow.destroy()`)
1252
+ * before the substrate actually dispatches the newly-minted task, the runner's halt sweep
1253
+ * `skip`s it like any other not-yet-started task. Acceptance here only guarantees the task
1254
+ * is WIRED into the live tree, not that it will execute.
1255
+ *
1256
+ * @param definition - The {@link TaskDefinition} to mint a live task from
1257
+ * @param index - The insertion position; omitted inserts at the end
1258
+ * @returns A {@link Result} boxing the minted, inserted task, or a `MUTATION` failure
1259
+ */
1260
+ add(definition: TaskDefinition, index?: number): Result<TaskInterface, WorkflowError>;
1261
+ /**
1262
+ * Remove the `pending` task `id` from this phase.
1263
+ *
1264
+ * @remarks
1265
+ * NATIVE gating: allowed only while this phase's own `status` is `pending`. While
1266
+ * `running` or terminal, always a `MUTATION` failure — a running phase's tasks are
1267
+ * already handed to the execution substrate and only a pure {@link add} append remains
1268
+ * possible.
1269
+ *
1270
+ * @param id - The task id to remove
1271
+ * @returns A {@link Result} boxing the removed task, or a `MUTATION` failure
1272
+ */
1273
+ remove(id: string): Result<TaskInterface, WorkflowError>;
1274
+ /**
1275
+ * Reposition the `pending` task `id` to `index` within this phase.
1276
+ *
1277
+ * @remarks
1278
+ * NATIVE gating: allowed only while this phase's own `status` is `pending`; `running` /
1279
+ * terminal always fail (see {@link remove}).
1280
+ *
1281
+ * @param id - The task id to move
1282
+ * @param index - The destination position
1283
+ * @returns A {@link Result} boxing the moved task, or a `MUTATION` failure
1284
+ */
1285
+ move(id: string, index: number): Result<TaskInterface, WorkflowError>;
1286
+ /**
1287
+ * Apply a validated {@link TaskUpdate} patch to the `pending` task `id` in this phase.
1288
+ *
1289
+ * @remarks
1290
+ * NATIVE gating: allowed only while this phase's own `status` is `pending`; `running` /
1291
+ * terminal always fail (see {@link remove}).
1292
+ *
1293
+ * @param id - The task id to patch
1294
+ * @param patch - The fields to update
1295
+ * @returns A {@link Result} boxing the patched task, or a `MUTATION` failure
1296
+ */
1297
+ update(id: string, patch: TaskUpdate): Result<TaskInterface, WorkflowError>;
1298
+ /**
1299
+ * Apply a validated declarative patch to SELF (`name` / `description` /
1300
+ * `concurrency` / `bail`).
1301
+ *
1302
+ * @remarks
1303
+ * Defense-in-depth (AGENTS §12): the owning {@link WorkflowInterface.update} gates
1304
+ * FIRST, so a direct call here THROWS a `MUTATION`
1305
+ * {@link import('./errors.js').WorkflowError} unless this phase's own `status` is
1306
+ * `pending`.
1307
+ *
1308
+ * @param value - The {@link PhaseUpdate} fields to apply
1309
+ * @example
1310
+ * ```ts
1311
+ * phase.patch({ concurrency: 4 })
1312
+ * ```
1313
+ */
1314
+ patch(value: PhaseUpdate): void;
1215
1315
  snapshot(): PhaseSnapshot;
1216
1316
  }
1217
1317
 
@@ -1225,6 +1325,11 @@ export declare interface PhaseInterface {
1225
1325
  * `append` adds one at the end, `phase(id)` looks one up, `phases()` lists them in
1226
1326
  * positional order, `count` is the size. A snapshot RESTORE re-`append`s in the
1227
1327
  * snapshot's order, reproducing it exactly.
1328
+ * - **Gated mutation API (AGENTS §12).** `add` / `remove` / `move` / `update` are the
1329
+ * graceful `Result` counterparts to `append`, gating ONLY on the target's OWN
1330
+ * existence/status/id/bounds — a duplicate id, an absent/non-`pending` target, an
1331
+ * out-of-bounds `index`, or a patch that fails {@link phaseUpdateShape} validation
1332
+ * all fail gracefully with a `MUTATION` {@link WorkflowError} instead of throwing.
1228
1333
  * - **No batch matrix.** A workflow's phases are a fixed positional set, so AGENTS §9.2
1229
1334
  * is deliberately omitted.
1230
1335
  * - **Event-free.** A purely structural container — the live {@link PhaseInterface}s own
@@ -1242,6 +1347,10 @@ export declare class PhaseManager implements PhaseManagerInterface {
1242
1347
  #private;
1243
1348
  get count(): number;
1244
1349
  append(phase: PhaseInterface): void;
1350
+ add(phase: PhaseInterface, index?: number): Result<PhaseInterface, WorkflowError>;
1351
+ remove(id: string): Result<PhaseInterface, WorkflowError>;
1352
+ move(id: string, index: number): Result<PhaseInterface, WorkflowError>;
1353
+ update(id: string, patch: PhaseUpdate): Result<PhaseInterface, WorkflowError>;
1245
1354
  phase(id: string): PhaseInterface | undefined;
1246
1355
  phases(): readonly PhaseInterface[];
1247
1356
  }
@@ -1253,10 +1362,61 @@ export declare class PhaseManager implements PhaseManagerInterface {
1253
1362
  * @remarks
1254
1363
  * `append` adds one live {@link PhaseInterface} at the end; `phase(id)` looks one up;
1255
1364
  * `phases()` lists them in positional order; `count` is the tally. No batch matrix.
1365
+ * `add` / `remove` / `move` / `update` (AGENTS §12) are the GATED mutation
1366
+ * counterparts a {@link WorkflowInterface.add} / `remove` / `move` / `update`
1367
+ * delegates to AFTER its own container-status/hook gating — the manager gates ONLY
1368
+ * on the target's OWN existence/status/id/bounds and stays event-free (the entity
1369
+ * emits on success).
1256
1370
  */
1257
1371
  export declare interface PhaseManagerInterface {
1258
1372
  readonly count: number;
1373
+ /**
1374
+ * Add `phase` at the end (the build-time wiring path).
1375
+ *
1376
+ * @remarks
1377
+ * THROWS a `MUTATION` {@link import('./errors.js').WorkflowError} on a duplicate
1378
+ * `id` (a genuine programmer error — a build-time wiring bug, AGENTS §12) instead of
1379
+ * silently overwriting the existing entry.
1380
+ *
1381
+ * @param phase - The live phase to append
1382
+ */
1259
1383
  append(phase: PhaseInterface): void;
1384
+ /**
1385
+ * Insert `phase` at `index` (default the end) — the GATED mutation counterpart to
1386
+ * {@link append}: a duplicate `id` or an out-of-bounds `index` fails gracefully
1387
+ * instead of throwing.
1388
+ *
1389
+ * @param phase - The live phase to insert
1390
+ * @param index - The insertion position (`[0, count]`); omitted inserts at the end
1391
+ * @returns A {@link Result} boxing the inserted phase, or a `MUTATION` failure
1392
+ */
1393
+ add(phase: PhaseInterface, index?: number): Result<PhaseInterface, WorkflowError>;
1394
+ /**
1395
+ * Remove the `pending` phase `id`.
1396
+ *
1397
+ * @param id - The phase id to remove
1398
+ * @returns A {@link Result} boxing the removed phase, or a `MUTATION` failure when
1399
+ * `id` is absent or not `pending`
1400
+ */
1401
+ remove(id: string): Result<PhaseInterface, WorkflowError>;
1402
+ /**
1403
+ * Reposition the `pending` phase `id` to `index`.
1404
+ *
1405
+ * @param id - The phase id to move
1406
+ * @param index - The destination position (`[0, count)`)
1407
+ * @returns A {@link Result} boxing the moved phase, or a `MUTATION` failure when
1408
+ * `id` is absent, not `pending`, or `index` is out of bounds
1409
+ */
1410
+ move(id: string, index: number): Result<PhaseInterface, WorkflowError>;
1411
+ /**
1412
+ * Apply a validated {@link PhaseUpdate} patch to the `pending` phase `id`.
1413
+ *
1414
+ * @param id - The phase id to patch
1415
+ * @param patch - The fields to update
1416
+ * @returns A {@link Result} boxing the patched phase, or a `MUTATION` failure when
1417
+ * `id` is absent, not `pending`, or `patch` fails validation
1418
+ */
1419
+ update(id: string, patch: PhaseUpdate): Result<PhaseInterface, WorkflowError>;
1260
1420
  phase(id: string): PhaseInterface | undefined;
1261
1421
  phases(): readonly PhaseInterface[];
1262
1422
  }
@@ -1292,16 +1452,7 @@ export declare const phaseShape: ObjectShape<{
1292
1452
  id: StringShape;
1293
1453
  name: StringShape;
1294
1454
  description: OptionalShape<StringShape>;
1295
- run: UnionShape<[ ObjectShape<{
1296
- via: LiteralShape<readonly ["function"]>;
1297
- name: StringShape;
1298
- }>, ObjectShape<{
1299
- via: LiteralShape<readonly ["tool"]>;
1300
- name: StringShape;
1301
- }>, ObjectShape<{
1302
- via: LiteralShape<readonly ["agent"]>;
1303
- name: StringShape;
1304
- }>]>;
1455
+ run: OptionalShape<StringShape>;
1305
1456
  retries: OptionalShape<NumberShape>;
1306
1457
  timeout: OptionalShape<NumberShape>;
1307
1458
  }>>;
@@ -1334,6 +1485,12 @@ export declare interface PhaseSnapshot {
1334
1485
  * per-phase policy identically without a silent default.
1335
1486
  */
1336
1487
  readonly bail: boolean;
1488
+ /**
1489
+ * Max tasks in flight at once (a resource throttle), persisted so a restore reinstates the
1490
+ * same per-phase throttle — mirrors {@link import('./types.js').PhaseDefinition.concurrency}.
1491
+ * Omitted ⇒ unbounded.
1492
+ */
1493
+ readonly concurrency?: number;
1337
1494
  readonly tasks: readonly TaskSnapshot[];
1338
1495
  }
1339
1496
 
@@ -1349,6 +1506,45 @@ export declare interface PhaseSnapshot {
1349
1506
  */
1350
1507
  export declare type PhaseStatus = LifecycleStatus;
1351
1508
 
1509
+ /**
1510
+ * A declarative partial update to a {@link PhaseInterface} — the fields a `pending`
1511
+ * phase's {@link PhaseInterface.patch} (and the owning {@link PhaseManagerInterface.update})
1512
+ * accept, runtime-validated via {@link import('./shapers.js').phaseUpdateShape}.
1513
+ *
1514
+ * @remarks
1515
+ * Mirrors the identity + throttle/policy fields of {@link PhaseDefinition} (`name` /
1516
+ * `description` / `concurrency` / `bail`) — never `id` / `tasks` (structural children
1517
+ * change through {@link PhaseInterface.add} / `remove` / `move`, not a patch). Every
1518
+ * field is optional; an omitted field is left unchanged.
1519
+ *
1520
+ * @example
1521
+ * ```ts
1522
+ * const result = workflow.phases.update(phase.id, { concurrency: 4, bail: true })
1523
+ * ```
1524
+ */
1525
+ export declare interface PhaseUpdate {
1526
+ readonly name?: string;
1527
+ readonly description?: string;
1528
+ readonly concurrency?: number;
1529
+ readonly bail?: boolean;
1530
+ }
1531
+
1532
+ /**
1533
+ * The shape of a {@link import('./types.js').PhaseUpdate} — a partial edit to a
1534
+ * `pending` phase's `name` / `description` / `concurrency` / `bail`, all optional.
1535
+ *
1536
+ * @remarks
1537
+ * Mirrors {@link phaseShape}'s corresponding field constraints exactly; never `id` /
1538
+ * `tasks` (structural children change through the phase's own `add` / `remove` /
1539
+ * `move`, not a patch, AGENTS §12).
1540
+ */
1541
+ export declare const phaseUpdateShape: ObjectShape<{
1542
+ name: OptionalShape<StringShape>;
1543
+ description: OptionalShape<StringShape>;
1544
+ concurrency: OptionalShape<NumberShape>;
1545
+ bail: OptionalShape<LiteralShape<readonly [true, false]>>;
1546
+ }>;
1547
+
1352
1548
  /**
1353
1549
  * Rebuild an equivalent live W-b entity tree from a {@link WorkflowSnapshot} — the
1354
1550
  * inverse of {@link WorkflowInterface.snapshot}, restoring structure + each node's status
@@ -1414,6 +1610,15 @@ export declare function restoreWorkflow(snapshot: WorkflowSnapshot, options?: Wo
1414
1610
  * unit failure (after its retries) records the error and `abort()`s the run, so every
1415
1611
  * sibling's signal fires; later failures are ignored and `execute` rejects with the
1416
1612
  * first error. A user `abort(reason)` likewise rejects a running `execute`.
1613
+ * - **`pause` / `resume` / `stop` (§10) ride the backing Queue.** `pause` / `resume`
1614
+ * delegate straight to the Queue's own pause/resume (holding/releasing the NEXT
1615
+ * dispatch while an in-flight unit finishes); `paused` mirrors the Queue's. `stop` is a
1616
+ * GRACEFUL permanent end, distinct from `abort`: still-pending (never-dispatched)
1617
+ * units are rejected by the Queue's own stop WITHOUT their handler ever running, and
1618
+ * `#settle` reads that fact (`#dispatched`) to treat the rejection as a stop artifact —
1619
+ * not a failure, never tripping fail-fast — while an in-flight unit still runs to
1620
+ * completion and settles normally. `execute` RESOLVES (never rejects) once every unit
1621
+ * has settled, with whatever results actually completed.
1417
1622
  * - **Observable (§13).** The owned {@link emitter} ({@link RunnerEventMap}) carries the run
1418
1623
  * lifecycle — `start` / `unit` / `spawn` / `settle` / `fail` / `finish` / `abort` — for
1419
1624
  * fire-and-forget observers. Every event is emitted directly, strictly AFTER the relevant
@@ -1429,8 +1634,66 @@ export declare class Runner<TInput, TResult> implements RunnerInterface<TInput,
1429
1634
  get emitter(): EmitterInterface<RunnerEventMap<TResult>>;
1430
1635
  get active(): number;
1431
1636
  get stopped(): boolean;
1637
+ get paused(): boolean;
1638
+ /**
1639
+ * Inject one more unit into an IN-FLIGHT `execute` run — a LIVE counterpart to a
1640
+ * `Controller.spawn`, called from OUTSIDE any unit's handler.
1641
+ *
1642
+ * @remarks
1643
+ * Returns `undefined` synchronously (graceful, non-throwing — AGENTS §12) unless the
1644
+ * runner is currently mid-`execute` and not yet stopped — covering "never started",
1645
+ * "already drained", "aborted", and "destroyed". Otherwise the unit is routed through
1646
+ * the SAME backing queue as a declared/`spawn`ed unit via `#launch` — the outstanding-
1647
+ * unit count gate increments BEFORE this call returns, so an in-flight `execute`
1648
+ * keeps awaiting it (the drain race: `#running` flips to `false` as the very first
1649
+ * step after `execute`'s `await drained.promise` settles, so a `spawn` reaching this
1650
+ * method after the run has fully drained is cleanly rejected with `undefined` —
1651
+ * never silently dropped, never hangs `execute`). Emits {@link RunnerEventMap.spawn}
1652
+ * with a `parent` of `undefined` (this call has no spawning unit) once accepted.
1653
+ *
1654
+ * @param input - The unit's work payload
1655
+ * @returns The unit's result promise, or `undefined` when no in-flight run can accept it
1656
+ * @example
1657
+ * ```ts
1658
+ * const runner = createRunner({ handler: (c) => c.input })
1659
+ * const result = runner.execute([1, 2])
1660
+ * const extra = runner.spawn(3) // Promise<number> | undefined
1661
+ * await result
1662
+ * ```
1663
+ */
1664
+ spawn(input: TInput): Promise<TResult> | undefined;
1432
1665
  execute(inputs: readonly TInput[]): Promise<readonly TResult[]>;
1433
1666
  abort(reason?: unknown): void;
1667
+ /**
1668
+ * Suspend dispatch (AGENTS §10 — resumable): delegates to the backing queue's own
1669
+ * `pause`, which holds the NEXT dispatch while any in-flight unit finishes.
1670
+ *
1671
+ * @remarks
1672
+ * A no-op once the runner is `stopped` — a stopped runner has no dispatch left to
1673
+ * suspend, mirroring the guard `stop()` itself applies. Also a no-op when already
1674
+ * `paused` (the queue's own `pause` is idempotent), so calling it repeatedly is safe.
1675
+ */
1676
+ pause(): void;
1677
+ /**
1678
+ * Continue a paused runner (AGENTS §10); delegates to the backing queue's `resume`.
1679
+ *
1680
+ * @remarks
1681
+ * A no-op once the runner is `stopped` (nothing left to resume) and a no-op when the
1682
+ * runner is not currently `paused`, so calling it repeatedly or on a never-paused
1683
+ * runner is safe.
1684
+ */
1685
+ resume(): void;
1686
+ /**
1687
+ * Permanently end the runner (AGENTS §10) — a GRACEFUL stop, distinct from `abort`.
1688
+ * Marks the runner `stopping` + `stopped`, then stops the backing queue: every
1689
+ * still-PENDING (never-dispatched) unit is rejected by the queue with its own
1690
+ * "queue is stopped" error, WITHOUT running its handler; every already-in-flight unit
1691
+ * keeps running to completion and settles normally. `#settle` reads `#stopping` to
1692
+ * classify a never-dispatched unit's rejection as a stop artifact (decrement the count
1693
+ * gate, no recorded failure, no fail-fast trip) rather than a genuine failure — a
1694
+ * dispatched unit's rejection while stopping is still a real failure. Idempotent.
1695
+ */
1696
+ stop(): void;
1434
1697
  destroy(): void;
1435
1698
  }
1436
1699
 
@@ -1523,6 +1786,8 @@ export declare interface RunnerInterface<TInput, TResult> {
1523
1786
  readonly emitter: EmitterInterface<RunnerEventMap<TResult>>;
1524
1787
  readonly active: number;
1525
1788
  readonly stopped: boolean;
1789
+ /** Whether the runner is currently paused (AGENTS §10 — resumable, no new dispatch); rides the backing queue's own `paused`. */
1790
+ readonly paused: boolean;
1526
1791
  /**
1527
1792
  * Run all `inputs` — and anything they `spawn` — to completion; resolve their
1528
1793
  * results in order: the declared inputs first (in input order), then the spawned
@@ -1537,6 +1802,25 @@ export declare interface RunnerInterface<TInput, TResult> {
1537
1802
  * @returns The units' results, in order (declared first, then spawns)
1538
1803
  */
1539
1804
  execute(inputs: readonly TInput[]): Promise<readonly TResult[]>;
1805
+ /**
1806
+ * Inject one more unit into an IN-FLIGHT `execute` run — a LIVE counterpart to a
1807
+ * `Controller.spawn`, called from OUTSIDE any unit's handler (the seam a live
1808
+ * `running` {@link PhaseInterface}'s `add` event lets a subscribed run offer a newly
1809
+ * added task to the SAME execution substrate).
1810
+ *
1811
+ * @remarks
1812
+ * Returns `undefined` synchronously (graceful, non-throwing — AGENTS §12) when the
1813
+ * runner is not currently mid-`execute`, or the run has already fully drained — the
1814
+ * caller reads `undefined` as "not accepted". Otherwise the unit is routed through
1815
+ * the SAME backing queue as a declared/`spawn`ed unit (the runner's
1816
+ * outstanding-unit count gate keeps the in-flight `execute` awaiting it) and emits
1817
+ * the {@link RunnerEventMap.spawn} event; its result promise resolves once the unit
1818
+ * settles.
1819
+ *
1820
+ * @param input - The unit's work payload
1821
+ * @returns The unit's result promise, or `undefined` when no in-flight run can accept it
1822
+ */
1823
+ spawn(input: TInput): Promise<TResult> | undefined;
1540
1824
  /**
1541
1825
  * Cancel every in-flight + pending unit (and the backing queue), making a running
1542
1826
  * `execute` reject.
@@ -1544,10 +1828,48 @@ export declare interface RunnerInterface<TInput, TResult> {
1544
1828
  * @param reason - An optional cancellation reason propagated to every unit's signal
1545
1829
  */
1546
1830
  abort(reason?: unknown): void;
1547
- /** Tear the runner down — `abort` plus stop the backing queue; idempotent. */
1548
- destroy(): void;
1549
- }
1550
-
1831
+ /**
1832
+ * Suspend dispatch (AGENTS §10 — resumable): the backing queue holds the NEXT dispatch
1833
+ * while any in-flight unit finishes; idempotent.
1834
+ *
1835
+ * @example
1836
+ * ```ts
1837
+ * runner.pause()
1838
+ * runner.paused // true
1839
+ * ```
1840
+ */
1841
+ pause(): void;
1842
+ /**
1843
+ * Continue a paused runner (AGENTS §10); idempotent.
1844
+ *
1845
+ * @example
1846
+ * ```ts
1847
+ * runner.resume()
1848
+ * runner.paused // false
1849
+ * ```
1850
+ */
1851
+ resume(): void;
1852
+ /**
1853
+ * Permanently end the runner (AGENTS §10) — a GRACEFUL stop: no further unit is
1854
+ * dispatched, but every already-in-flight unit runs to completion and settles
1855
+ * normally. A never-dispatched (still-pending) unit is rejected by the backing queue
1856
+ * and is NOT recorded as a failure (it never trips fail-fast); a genuine in-flight
1857
+ * failure still is. `execute`'s promise RESOLVES (never rejects) once every unit has
1858
+ * settled, with whatever results actually completed. Idempotent.
1859
+ *
1860
+ * @example
1861
+ * ```ts
1862
+ * const runner = createRunner({ handler: (c) => c.input, concurrency: 1 })
1863
+ * const results = runner.execute([1, 2, 3])
1864
+ * runner.stop() // the in-flight unit finishes; the rest are gracefully dropped
1865
+ * await results // resolves with whatever settled — never rejects
1866
+ * ```
1867
+ */
1868
+ stop(): void;
1869
+ /** Tear the runner down — `abort` plus stop the backing queue; idempotent. */
1870
+ destroy(): void;
1871
+ }
1872
+
1551
1873
  /**
1552
1874
  * Options for `createRunner`.
1553
1875
  *
@@ -1683,28 +2005,18 @@ export declare interface SchedulerOptions {
1683
2005
  export declare type SchedulerPriority = 'user' | 'normal' | 'background';
1684
2006
 
1685
2007
  /**
1686
- * The shape of ONE flat step — `{ name, via? }` — the building block of
1687
- * {@link workflowStepsShape}.
2008
+ * Box a value as a {@link Success} — the graceful outcome half of a {@link Result}.
1688
2009
  *
1689
- * @remarks
1690
- * `name` is the REGISTERED behavior name the step runs (it becomes the task's `run.name`);
1691
- * `via` is the optional execution mechanism (defaults to `'function'` when omitted). The
1692
- * tool expands each step into a one-task phase, in order
1693
- * ({@link import('./helpers.js').expandSteps}).
1694
- */
1695
- export declare const stepShape: ObjectShape<{
1696
- name: StringShape;
1697
- via: OptionalShape<LiteralShape<readonly ["function", "tool", "agent"]>>;
1698
- }>;
1699
-
1700
- /**
1701
- * Convert one flat {@link WorkflowStep} into a {@link TaskForm} — `name` → the form's `name`,
1702
- * `via` → the form's discriminant (defaulting to `'function'`).
2010
+ * @typeParam T - The boxed value's type
2011
+ * @param value - The value to box
2012
+ * @returns A {@link Success} wrapping `value`
1703
2013
  *
1704
- * @param step - The flat step
1705
- * @returns The {@link TaskForm} the step's task runs
2014
+ * @example
2015
+ * ```ts
2016
+ * const result = success(task) // { success: true, value: task }
2017
+ * ```
1706
2018
  */
1707
- export declare function stepToForm(step: WorkflowStep): TaskForm;
2019
+ export declare function success<T>(value: T): Success<T>;
1708
2020
 
1709
2021
  /**
1710
2022
  * The live leaf state machine (W-b) for one task — an observable (AGENTS §13), guarded
@@ -1731,10 +2043,16 @@ export declare function stepToForm(step: WorkflowStep): TaskForm;
1731
2043
  * matching event strictly AFTER the state change, BEFORE the cascade; the emitter isolates
1732
2044
  * a listener throw and routes it to its `error` handler (the `error` option), so a buggy
1733
2045
  * observer can never corrupt a transition.
2046
+ * - **Declarative config (AGENTS §12).** `run` / `retries` / `timeout` PERSIST in a
2047
+ * {@link TaskSnapshot} (like a phase's `bail` / `concurrency`), carried verbatim from the
2048
+ * matching {@link import('../types.js').TaskDefinition} / {@link TaskSnapshot} field. `handler`
2049
+ * is the RUNTIME-ONLY counterpart — `run` resolved ONCE at construction against the
2050
+ * workflow-level {@link import('../types.js').WorkflowOptions.functions} registry — and is
2051
+ * NEVER persisted; `undefined` when `run` is omitted or unregistered (the no-handler rule).
1734
2052
  */
1735
2053
  export declare class Task implements TaskInterface {
1736
2054
  #private;
1737
- constructor(context: TaskContext, phase: PhaseInterface, workflow: WorkflowInterface, recompute: () => void, options?: TaskOptions, status?: TaskStatus, result?: TaskResult);
2055
+ constructor(context: TaskContext, phase: PhaseInterface, workflow: WorkflowInterface, recompute: () => void, options?: TaskOptions, status?: TaskStatus, result?: TaskResult, run?: string, retries?: number, timeout?: number, handler?: WorkflowFunction);
1738
2056
  get emitter(): EmitterInterface<TaskEventMap>;
1739
2057
  get id(): string;
1740
2058
  get name(): string;
@@ -1744,11 +2062,31 @@ export declare class Task implements TaskInterface {
1744
2062
  get workflow(): WorkflowInterface;
1745
2063
  get status(): TaskStatus;
1746
2064
  get result(): TaskResult | undefined;
2065
+ get run(): string | undefined;
2066
+ get handler(): WorkflowFunction | undefined;
2067
+ get retries(): number | undefined;
2068
+ get timeout(): number | undefined;
1747
2069
  start(): void;
1748
2070
  complete(value: unknown): void;
1749
2071
  fail(error: unknown): void;
1750
2072
  skip(): void;
1751
2073
  stop(): void;
2074
+ /**
2075
+ * Apply a validated declarative patch to SELF (`name` / `description`).
2076
+ *
2077
+ * @remarks
2078
+ * Defense-in-depth (AGENTS §12): the owning
2079
+ * {@link import('../types.js').TaskManagerInterface.update} gates FIRST (target
2080
+ * exists + `pending`), so this is the second, redundant check — it THROWS a
2081
+ * `MUTATION` {@link WorkflowError} unless this task's own `status` is `pending`.
2082
+ *
2083
+ * @param value - The {@link TaskUpdate} fields to apply
2084
+ * @example
2085
+ * ```ts
2086
+ * task.patch({ name: 'Renamed task' })
2087
+ * ```
2088
+ */
2089
+ patch(value: TaskUpdate): void;
1752
2090
  snapshot(): TaskSnapshot;
1753
2091
  }
1754
2092
 
@@ -1776,15 +2114,6 @@ export declare const TASK_STATUSES: readonly TaskStatus[];
1776
2114
  */
1777
2115
  export declare const TASK_TRANSITIONS: Readonly<Record<TaskStatus, readonly TaskStatus[]>>;
1778
2116
 
1779
- /**
1780
- * The three task-form mechanisms (the {@link TaskVia} discriminant), frozen.
1781
- *
1782
- * @remarks
1783
- * The runtime source of truth for the `via` axis — drive the contract's literal
1784
- * shape and any guard from this array rather than repeating the literals.
1785
- */
1786
- export declare const TASK_VIAS: readonly TaskVia[];
1787
-
1788
2117
  /**
1789
2118
  * The ambient context of a task — its own identity plus a back-reference to the
1790
2119
  * phase (and, transitively, the workflow) it belongs to.
@@ -1862,32 +2191,39 @@ export declare interface TaskControllerInterface {
1862
2191
  }
1863
2192
 
1864
2193
  /**
1865
- * The serializable definition of one task — its identity plus the behavior it runs
1866
- * (a {@link TaskForm}, referenced by name).
2194
+ * The serializable definition of one task — its identity plus an optional reference to
2195
+ * the behavior it runs.
1867
2196
  *
1868
2197
  * @remarks
1869
2198
  * Pure JSON DATA: a UI or an LLM authors it, it round-trips through the contract
1870
- * (factories.ts), and it carries NO functions. `id` is the positional identity
1871
- * within its phase; `name` is the human label; `description` is optional prose;
1872
- * `run` is the {@link TaskForm} naming the registered behavior.
2199
+ * (factories.ts), and it carries NO functions. `id` is the positional identity within
2200
+ * its phase; `name` is the human label; `description` is optional prose. `run` is a
2201
+ * PLAIN NAME a key resolved ONCE at construction against a workflow-level
2202
+ * {@link WorkflowFunctions} registry into a runtime {@link TaskInterface.handler}
2203
+ * carried on the live task. A task whose `run` is omitted, or whose name is unregistered,
2204
+ * has no handler and AUTO-COMPLETES (the no-handler rule).
1873
2205
  */
1874
2206
  export declare interface TaskDefinition {
1875
2207
  readonly id: string;
1876
2208
  readonly name: string;
1877
2209
  readonly description?: string;
1878
- readonly run: TaskForm;
2210
+ readonly run?: string;
1879
2211
  /**
1880
2212
  * @remarks
1881
2213
  * Extra attempts after the first on failure (a non-negative integer); the runner threads it
1882
2214
  * to this task's substrate unit, OVERRIDING the phase Runner's `retries` default. Omitted ⇒
1883
- * the default (no extra attempts). Execution-only: it is NOT persisted in a snapshot.
2215
+ * the default (no extra attempts). PERSISTED in a {@link TaskSnapshot} (like `bail` and
2216
+ * `concurrency`), so `restoreWorkflow(snapshot, { functions })` resumes with the same
2217
+ * reliability config; only the resolved handler itself is runtime-only.
1884
2218
  */
1885
2219
  readonly retries?: number;
1886
2220
  /**
1887
2221
  * @remarks
1888
2222
  * The per-attempt deadline in milliseconds (a non-negative integer); the runner threads it to
1889
2223
  * this task's substrate unit, OVERRIDING the phase Runner's `timeout` default. Omitted (or a
1890
- * non-positive value) ⇒ no deadline. Execution-only: it is NOT persisted in a snapshot.
2224
+ * non-positive value) ⇒ no deadline. PERSISTED in a {@link TaskSnapshot} (like `bail` and
2225
+ * `concurrency`), so `restoreWorkflow(snapshot, { functions })` resumes with the same
2226
+ * reliability config; only the resolved handler itself is runtime-only.
1891
2227
  */
1892
2228
  readonly timeout?: number;
1893
2229
  }
@@ -1897,57 +2233,17 @@ export declare interface TaskDefinition {
1897
2233
  * {@link TaskSnapshot} — the per-task leaf step of {@link definitionToSnapshot} (no
1898
2234
  * result yet, empty metadata).
1899
2235
  *
2236
+ * @remarks
2237
+ * `run` / `retries` / `timeout` carry over verbatim (persisted declarative config, like a
2238
+ * phase's `bail` / `concurrency`) — a restore reinstates the same behavior reference and
2239
+ * reliability overrides once paired with a {@link import('./types.js').WorkflowOptions.functions}
2240
+ * registry.
2241
+ *
1900
2242
  * @param task - The task definition to seed from
1901
2243
  * @returns An initial {@link TaskSnapshot}
1902
2244
  */
1903
2245
  export declare function taskDefinitionToSnapshot(task: WorkflowDefinition['phases'][number]['tasks'][number]): TaskSnapshot;
1904
2246
 
1905
- /**
1906
- * A draft task — a {@link TaskDefinition} with OPTIONAL `id` / `name`.
1907
- *
1908
- * @remarks
1909
- * The tool synthesizes a missing `id` positionally and defaults a missing `name` to
1910
- * its `id` ({@link import('./helpers.js').completeDraft}). A PROVIDED `id` / `name` is
1911
- * preserved verbatim (and must be non-empty — the draft contract's `minLength: 1`).
1912
- */
1913
- export declare interface TaskDraft {
1914
- readonly id?: string;
1915
- readonly name?: string;
1916
- readonly description?: string;
1917
- readonly run: TaskForm;
1918
- /** Extra attempts after the first on failure (a non-negative integer); overrides the phase Runner default. Execution-only. */
1919
- readonly retries?: number;
1920
- /** The per-attempt deadline in milliseconds (a non-negative integer); overrides the phase Runner default. Execution-only. */
1921
- readonly timeout?: number;
1922
- }
1923
-
1924
- /**
1925
- * The shape of a TASK in a draft workflow — identical to {@link taskShape} EXCEPT `id`
1926
- * and `name` are OPTIONAL (the tool synthesizes any missing one positionally).
1927
- *
1928
- * @remarks
1929
- * A PROVIDED `id` / `name` still carries `minLength: 1`, so an explicitly-empty `id: ''`
1930
- * is INVALID (rejected by the draft contract), never auto-filled — keeping "garbage"
1931
- * distinct from "omitted". `run` stays required.
1932
- */
1933
- export declare const taskDraftShape: ObjectShape<{
1934
- id: OptionalShape<StringShape>;
1935
- name: OptionalShape<StringShape>;
1936
- description: OptionalShape<StringShape>;
1937
- run: UnionShape<[ ObjectShape<{
1938
- via: LiteralShape<readonly ["function"]>;
1939
- name: StringShape;
1940
- }>, ObjectShape<{
1941
- via: LiteralShape<readonly ["tool"]>;
1942
- name: StringShape;
1943
- }>, ObjectShape<{
1944
- via: LiteralShape<readonly ["agent"]>;
1945
- name: StringShape;
1946
- }>]>;
1947
- retries: OptionalShape<NumberShape>;
1948
- timeout: OptionalShape<NumberShape>;
1949
- }>;
1950
-
1951
2247
  /**
1952
2248
  * The push observation surface (AGENTS §13) of the task entity (W-b) — the
1953
2249
  * lifecycle moments of one task.
@@ -1973,58 +2269,6 @@ export declare type TaskEventMap = {
1973
2269
  readonly stop: readonly [];
1974
2270
  };
1975
2271
 
1976
- /**
1977
- * A {@link TaskDefinition}'s behavior reference — a descriptive tagged union over
1978
- * the three execution forms a task can take, discriminated by `via` (the axis is
1979
- * the execution MECHANISM, never a bare `kind`; AGENTS §4.4). Each form references
1980
- * its behavior BY NAME through a registry (the runner resolves the name at W-b);
1981
- * a definition NEVER inlines a function, so the whole tree stays JSON-serializable.
1982
- *
1983
- * @remarks
1984
- * - `function` — a registered function the runner invokes directly.
1985
- * - `tool` — a registered {@link ToolInterface} the
1986
- * runner executes as a tool call.
1987
- * - `agent` — a registered agent (a subagent). It will later carry a depth/cycle
1988
- * guard (W-c, bounded by {@link import('./constants.js').MAX_WORKFLOW_DEPTH}); in
1989
- * W-a it is purely the typed reference.
1990
- *
1991
- * `name` is the registry key (it identifies the behavior the form runs). A
1992
- * descriptive `via` literal (not `kind`) names the axis that varies, and the
1993
- * `is*` guards narrow on `run.via` cleanly.
1994
- */
1995
- export declare type TaskForm = {
1996
- readonly via: 'function';
1997
- readonly name: string;
1998
- } | {
1999
- readonly via: 'tool';
2000
- readonly name: string;
2001
- } | {
2002
- readonly via: 'agent';
2003
- readonly name: string;
2004
- };
2005
-
2006
- /**
2007
- * The shape of a {@link import('./types.js').TaskForm} — a descriptive tagged union
2008
- * over the three execution mechanisms, discriminated by the `via` literal (never a
2009
- * bare `kind`; AGENTS §4.4). Each variant pairs the `via` discriminant with a `name`
2010
- * (the registry key for the behavior).
2011
- *
2012
- * @remarks
2013
- * The union and each `via` literal + `name` carry a `description` so the emitted JSON
2014
- * Schema spells out what the discriminant means and that `name` is a REGISTERED key
2015
- * (not a human label) — the field-level guidance a small model needs to fill `run`.
2016
- */
2017
- export declare const taskFormShape: UnionShape<[ ObjectShape<{
2018
- via: LiteralShape<readonly ["function"]>;
2019
- name: StringShape;
2020
- }>, ObjectShape<{
2021
- via: LiteralShape<readonly ["tool"]>;
2022
- name: StringShape;
2023
- }>, ObjectShape<{
2024
- via: LiteralShape<readonly ["agent"]>;
2025
- name: StringShape;
2026
- }>]>;
2027
-
2028
2272
  /** Initial {@link TaskEventMap} listeners — the reserved `on` option (AGENTS §8). */
2029
2273
  export declare type TaskHooks = EmitterHooks<TaskEventMap>;
2030
2274
 
@@ -2077,11 +2321,52 @@ export declare interface TaskInterface {
2077
2321
  readonly status: TaskStatus;
2078
2322
  /** The recorded outcome once the task settled with one (`completed` / `failed`), else `undefined`. */
2079
2323
  readonly result: TaskResult | undefined;
2324
+ /**
2325
+ * The behavior reference — a plain registry key name, PERSISTED (mirrors
2326
+ * {@link TaskDefinition.run} / {@link TaskSnapshot.run}), like {@link PhaseInterface.bail}.
2327
+ * `undefined` when this task has no behavior reference.
2328
+ */
2329
+ readonly run: string | undefined;
2330
+ /**
2331
+ * The RESOLVED runtime handler — RUNTIME-ONLY, NEVER persisted in a {@link TaskSnapshot}.
2332
+ * Resolved ONCE at construction (build, restore, or a live mint) by looking `run` up in the
2333
+ * workflow-level {@link WorkflowOptions.functions} registry: `functions?.[run]` when `run`
2334
+ * is defined, else `undefined`. A task with no `handler` (an omitted `run`, or a `run` name
2335
+ * absent from the registry) AUTO-COMPLETES (the no-handler rule) — its phase/workflow still
2336
+ * reaches a terminal status, just with no dispatched behavior.
2337
+ */
2338
+ readonly handler: WorkflowFunction | undefined;
2339
+ /**
2340
+ * Extra attempts after the first on failure — PERSISTED (mirrors {@link TaskDefinition.retries}
2341
+ * / {@link TaskSnapshot.retries}), like {@link PhaseInterface.concurrency}. `undefined` ⇒ none.
2342
+ */
2343
+ readonly retries: number | undefined;
2344
+ /**
2345
+ * The per-attempt deadline in milliseconds — PERSISTED (mirrors {@link TaskDefinition.timeout}
2346
+ * / {@link TaskSnapshot.timeout}). `undefined` ⇒ no deadline.
2347
+ */
2348
+ readonly timeout: number | undefined;
2080
2349
  start(): void;
2081
2350
  complete(value: unknown): void;
2082
2351
  fail(error: unknown): void;
2083
2352
  skip(): void;
2084
2353
  stop(): void;
2354
+ /**
2355
+ * Apply a validated declarative patch to SELF (`name` / `description`).
2356
+ *
2357
+ * @remarks
2358
+ * Defense-in-depth (AGENTS §12): the owning {@link TaskManagerInterface.update} gates
2359
+ * FIRST (target exists + `pending`), so a direct call here is the second, redundant
2360
+ * check — it THROWS a `MUTATION` {@link import('./errors.js').WorkflowError} unless
2361
+ * this task's own `status` is `pending`.
2362
+ *
2363
+ * @param value - The {@link TaskUpdate} fields to apply
2364
+ * @example
2365
+ * ```ts
2366
+ * task.patch({ name: 'Renamed task' })
2367
+ * ```
2368
+ */
2369
+ patch(value: TaskUpdate): void;
2085
2370
  snapshot(): TaskSnapshot;
2086
2371
  }
2087
2372
 
@@ -2096,6 +2381,11 @@ export declare interface TaskInterface {
2096
2381
  * `tasks()` lists them in positional order, `count` is the size. A `skip` is a STATUS
2097
2382
  * change on a stored task (never a removal), so order survives it; a snapshot RESTORE
2098
2383
  * re-`append`s in the snapshot's order, reproducing it exactly.
2384
+ * - **Gated mutation API (AGENTS §12).** `add` / `remove` / `move` / `update` are the
2385
+ * graceful `Result` counterparts to `append`, gating ONLY on the target's OWN
2386
+ * existence/status/id/bounds — a duplicate id, an absent/non-`pending` target, an
2387
+ * out-of-bounds `index`, or a patch that fails {@link taskUpdateShape} validation all
2388
+ * fail gracefully with a `MUTATION` {@link WorkflowError} instead of throwing.
2099
2389
  * - **No batch matrix.** A phase's tasks are a fixed positional set, so AGENTS §9.2 (the
2100
2390
  * bulk verb overloads) is deliberately omitted — there is no `remove` family here.
2101
2391
  * - **Event-free.** A purely structural container — the live {@link TaskInterface}s own
@@ -2113,6 +2403,10 @@ export declare class TaskManager implements TaskManagerInterface {
2113
2403
  #private;
2114
2404
  get count(): number;
2115
2405
  append(task: TaskInterface): void;
2406
+ add(task: TaskInterface, index?: number): Result<TaskInterface, WorkflowError>;
2407
+ remove(id: string): Result<TaskInterface, WorkflowError>;
2408
+ move(id: string, index: number): Result<TaskInterface, WorkflowError>;
2409
+ update(id: string, patch: TaskUpdate): Result<TaskInterface, WorkflowError>;
2116
2410
  task(id: string): TaskInterface | undefined;
2117
2411
  tasks(): readonly TaskInterface[];
2118
2412
  }
@@ -2126,11 +2420,61 @@ export declare class TaskManager implements TaskManagerInterface {
2126
2420
  * `append` adds one live {@link TaskInterface} at the end (the build-time wiring path);
2127
2421
  * `task(id)` looks one up; `tasks()` lists them in positional order; `count` is the
2128
2422
  * tally. No batch matrix (AGENTS §9.2 is deliberately omitted — a phase's tasks are a
2129
- * fixed positional set, not a bulk-mutated collection).
2423
+ * fixed positional set, not a bulk-mutated collection). `add` / `remove` / `move` /
2424
+ * `update` (AGENTS §12) are the GATED mutation counterparts a
2425
+ * {@link PhaseInterface.add} / `remove` / `move` / `update` delegates to AFTER its own
2426
+ * container-status/hook gating — the manager gates ONLY on the target's OWN
2427
+ * existence/status/id/bounds and stays event-free (the entity emits on success).
2130
2428
  */
2131
2429
  export declare interface TaskManagerInterface {
2132
2430
  readonly count: number;
2431
+ /**
2432
+ * Add `task` at the end (the build-time wiring path).
2433
+ *
2434
+ * @remarks
2435
+ * THROWS a `MUTATION` {@link import('./errors.js').WorkflowError} on a duplicate
2436
+ * `id` (a genuine programmer error — a build-time wiring bug, AGENTS §12) instead of
2437
+ * silently overwriting the existing entry.
2438
+ *
2439
+ * @param task - The live task to append
2440
+ */
2133
2441
  append(task: TaskInterface): void;
2442
+ /**
2443
+ * Insert `task` at `index` (default the end) — the GATED mutation counterpart to
2444
+ * {@link append}: a duplicate `id` or an out-of-bounds `index` fails gracefully
2445
+ * instead of throwing.
2446
+ *
2447
+ * @param task - The live task to insert
2448
+ * @param index - The insertion position (`[0, count]`); omitted inserts at the end
2449
+ * @returns A {@link Result} boxing the inserted task, or a `MUTATION` failure
2450
+ */
2451
+ add(task: TaskInterface, index?: number): Result<TaskInterface, WorkflowError>;
2452
+ /**
2453
+ * Remove the `pending` task `id`.
2454
+ *
2455
+ * @param id - The task id to remove
2456
+ * @returns A {@link Result} boxing the removed task, or a `MUTATION` failure when
2457
+ * `id` is absent or not `pending`
2458
+ */
2459
+ remove(id: string): Result<TaskInterface, WorkflowError>;
2460
+ /**
2461
+ * Reposition the `pending` task `id` to `index`.
2462
+ *
2463
+ * @param id - The task id to move
2464
+ * @param index - The destination position (`[0, count)`)
2465
+ * @returns A {@link Result} boxing the moved task, or a `MUTATION` failure when `id`
2466
+ * is absent, not `pending`, or `index` is out of bounds
2467
+ */
2468
+ move(id: string, index: number): Result<TaskInterface, WorkflowError>;
2469
+ /**
2470
+ * Apply a validated {@link TaskUpdate} patch to the `pending` task `id`.
2471
+ *
2472
+ * @param id - The task id to patch
2473
+ * @param patch - The fields to update
2474
+ * @returns A {@link Result} boxing the patched task, or a `MUTATION` failure when
2475
+ * `id` is absent, not `pending`, or `patch` fails validation
2476
+ */
2477
+ update(id: string, patch: TaskUpdate): Result<TaskInterface, WorkflowError>;
2134
2478
  task(id: string): TaskInterface | undefined;
2135
2479
  tasks(): readonly TaskInterface[];
2136
2480
  }
@@ -2141,9 +2485,11 @@ export declare interface TaskManagerInterface {
2141
2485
  *
2142
2486
  * @remarks
2143
2487
  * The reserved `on` (AGENTS §8) wires initial {@link TaskEventMap} listeners; a
2144
- * {@link createTask}-built tree threads each level's `on` from its parent options.
2145
- * `metadata` is the open consumer bag carried verbatim into a {@link TaskSnapshot}
2146
- * (mirrors {@link TaskInput.metadata}), never interpreted by the workflow.
2488
+ * {@link import('./factories.js').createWorkflow}-built tree threads each level's `on`
2489
+ * from its parent options, the same way a {@link WorkflowInterface.add} /
2490
+ * {@link PhaseInterface.add} mint threads a leaf's `on` from ITS options. `metadata` is
2491
+ * the open consumer bag carried verbatim into a {@link TaskSnapshot} (mirrors
2492
+ * {@link TaskInput.metadata}), never interpreted by the workflow.
2147
2493
  */
2148
2494
  export declare interface TaskOptions {
2149
2495
  readonly on?: TaskHooks;
@@ -2180,23 +2526,15 @@ export declare interface TaskResult {
2180
2526
  }
2181
2527
 
2182
2528
  /**
2183
- * The shape of a {@link import('./types.js').TaskDefinition} — identity plus the
2184
- * behavior reference ({@link taskFormShape}). `description` is optional prose.
2529
+ * The shape of a {@link import('./types.js').TaskDefinition} — identity plus an optional
2530
+ * `run` behavior reference (a plain registry-key string, min length 1). `description` is
2531
+ * optional prose.
2185
2532
  */
2186
2533
  export declare const taskShape: ObjectShape<{
2187
2534
  id: StringShape;
2188
2535
  name: StringShape;
2189
2536
  description: OptionalShape<StringShape>;
2190
- run: UnionShape<[ ObjectShape<{
2191
- via: LiteralShape<readonly ["function"]>;
2192
- name: StringShape;
2193
- }>, ObjectShape<{
2194
- via: LiteralShape<readonly ["tool"]>;
2195
- name: StringShape;
2196
- }>, ObjectShape<{
2197
- via: LiteralShape<readonly ["agent"]>;
2198
- name: StringShape;
2199
- }>]>;
2537
+ run: OptionalShape<StringShape>;
2200
2538
  retries: OptionalShape<NumberShape>;
2201
2539
  timeout: OptionalShape<NumberShape>;
2202
2540
  }>;
@@ -2209,6 +2547,12 @@ export declare const taskShape: ObjectShape<{
2209
2547
  * Pure JSON DATA (no class instances, no functions). `result` is the task's
2210
2548
  * {@link TaskResult} when it has settled with an outcome, else `undefined`.
2211
2549
  * `metadata` is the open consumer bag carried from the task's {@link TaskInput}.
2550
+ * `run` / `retries` / `timeout` are the DECLARATIVE config the task carries — persisted
2551
+ * like a {@link PhaseSnapshot}'s `bail` / `concurrency`, so a restore reinstates the same
2552
+ * behavior reference and reliability overrides (`run` re-resolves against the
2553
+ * {@link WorkflowOptions.functions} registry supplied to
2554
+ * {@link import('./factories.js').restoreWorkflow}); each omitted ⇒ the corresponding
2555
+ * unset default.
2212
2556
  */
2213
2557
  export declare interface TaskSnapshot {
2214
2558
  readonly id: string;
@@ -2217,6 +2561,12 @@ export declare interface TaskSnapshot {
2217
2561
  readonly status: TaskStatus;
2218
2562
  readonly result?: TaskResult;
2219
2563
  readonly metadata: Readonly<Record<string, unknown>>;
2564
+ /** The behavior reference — a registry key resolved against {@link WorkflowFunctions} on restore/build. */
2565
+ readonly run?: string;
2566
+ /** Extra attempts after the first on failure (a non-negative integer); overrides the phase Runner default. */
2567
+ readonly retries?: number;
2568
+ /** The per-attempt deadline in milliseconds (a non-negative integer); overrides the phase Runner default. */
2569
+ readonly timeout?: number;
2220
2570
  }
2221
2571
 
2222
2572
  /**
@@ -2235,8 +2585,40 @@ export declare interface TaskSnapshot {
2235
2585
  */
2236
2586
  export declare type TaskStatus = LifecycleStatus;
2237
2587
 
2238
- /** The discriminant literal of a {@link TaskForm} — the execution mechanism axis. */
2239
- export declare type TaskVia = TaskForm['via'];
2588
+ /**
2589
+ * A declarative partial update to a {@link TaskInterface} — the fields a `pending`
2590
+ * task's {@link TaskInterface.patch} (and the owning {@link TaskManagerInterface.update})
2591
+ * accept, runtime-validated via {@link import('./shapers.js').taskUpdateShape}.
2592
+ *
2593
+ * @remarks
2594
+ * Mirrors the identity fields of {@link TaskDefinition} (`name` / `description`) —
2595
+ * never `run` / `retries` / `timeout` (a form/reliability change is a structural
2596
+ * replace, not a patch) and never `id` (identity is immutable once created). Every
2597
+ * field is optional; an omitted field is left unchanged.
2598
+ *
2599
+ * @example
2600
+ * ```ts
2601
+ * const result = task.phase.tasks.update(task.id, { name: 'Renamed task' })
2602
+ * ```
2603
+ */
2604
+ export declare interface TaskUpdate {
2605
+ readonly name?: string;
2606
+ readonly description?: string;
2607
+ }
2608
+
2609
+ /**
2610
+ * The shape of a {@link import('./types.js').TaskUpdate} — a partial edit to a
2611
+ * `pending` task's `name` / `description`, both optional.
2612
+ *
2613
+ * @remarks
2614
+ * Mirrors {@link taskShape}'s `name` / `description` constraints exactly (a provided
2615
+ * `name` still has `minLength: 1`); never `id` / `run` / `retries` / `timeout` (those
2616
+ * are not patchable fields, AGENTS §12).
2617
+ */
2618
+ export declare const taskUpdateShape: ObjectShape<{
2619
+ name: OptionalShape<StringShape>;
2620
+ description: OptionalShape<StringShape>;
2621
+ }>;
2240
2622
 
2241
2623
  /**
2242
2624
  * The {@link TaskStatus} values that are TERMINAL — a task in one of these will
@@ -2295,6 +2677,21 @@ export declare type UnitOutcome<TResult> = {
2295
2677
  * `start` / `complete` / `fail` / `stop` on a derived-status CHANGE; the emitter isolates a
2296
2678
  * listener throw and routes it to its `error` handler (the `error` option); `fail` carries
2297
2679
  * the failing task's {@link TaskResult}.
2680
+ * - **Structural API (AGENTS §7).** `add` / `remove` / `move` / `update` gate BEFORE
2681
+ * delegating to {@link phases} (the manager gates the target's own existence/status/id/
2682
+ * bounds), then emit the matching {@link WorkflowEventMap} event on success only. NATIVE,
2683
+ * bottom-up gating (no runner-installed hook): refused outright while this workflow's own
2684
+ * `status` is terminal; otherwise a target position must fall within the PENDING SUFFIX —
2685
+ * the contiguous trailing run of `pending` phases — whose boundary is
2686
+ * {@link import('./helpers.js').deriveBoundary} over the live phases' statuses. A `pending`
2687
+ * workflow's phases are all `pending`, so the boundary is `0` and every position is
2688
+ * naturally accepted.
2689
+ * - **Runtime lifecycle (AGENTS §10).** `pause` / `resume` / `wait` gate execution at the runner's
2690
+ * phase/task boundaries WITHOUT touching {@link status} — `paused` is runtime-only, never
2691
+ * persisted. `destroy` is a terminal teardown: it aborts {@link signal}, `stop`s every
2692
+ * non-terminal live phase (so an engine parked on a phase's own gate unparks and the tree
2693
+ * lands coherent), forces the `stop` override on THIS workflow when not already terminal,
2694
+ * releases any parked {@link wait} waiter, and marks {@link destroyed} — all four idempotent.
2298
2695
  */
2299
2696
  export declare class Workflow implements WorkflowInterface {
2300
2697
  #private;
@@ -2305,6 +2702,9 @@ export declare class Workflow implements WorkflowInterface {
2305
2702
  get description(): string | undefined;
2306
2703
  get context(): WorkflowContext;
2307
2704
  get bail(): boolean;
2705
+ get paused(): boolean;
2706
+ get destroyed(): boolean;
2707
+ get signal(): AbortSignal;
2308
2708
  get status(): WorkflowStatus;
2309
2709
  get phases(): PhaseManagerInterface;
2310
2710
  phase(id: string): PhaseInterface | undefined;
@@ -2312,92 +2712,20 @@ export declare class Workflow implements WorkflowInterface {
2312
2712
  skip(): void;
2313
2713
  stop(): void;
2314
2714
  complete(): void;
2715
+ pause(): void;
2716
+ resume(): void;
2717
+ destroy(): void;
2718
+ wait(): Promise<void>;
2719
+ add(definition: PhaseDefinition, index?: number): Result<PhaseInterface, WorkflowError>;
2720
+ remove(id: string): Result<PhaseInterface, WorkflowError>;
2721
+ move(id: string, index: number): Result<PhaseInterface, WorkflowError>;
2722
+ update(id: string, patch: PhaseUpdate): Result<PhaseInterface, WorkflowError>;
2315
2723
  snapshot(): WorkflowSnapshot;
2316
2724
  }
2317
2725
 
2318
2726
  /** Every {@link WorkflowStatus} value, frozen — the lifecycle vocabulary of a workflow. */
2319
2727
  export declare const WORKFLOW_STATUSES: readonly WorkflowStatus[];
2320
2728
 
2321
- /**
2322
- * The DESCRIPTION {@link import('./factories.js').createWorkflowTool} advertises — a
2323
- * multi-line guide that teaches a small model how to author a complete workflow tree.
2324
- *
2325
- * @remarks
2326
- * Presents the SIMPLE flat shape (`{ name, steps: [{ name, via }] }`) as the PRIMARY way with
2327
- * one complete worked example ({@link WORKFLOW_TOOL_FLAT_EXAMPLE}), names the three `via`
2328
- * values + that a step's `name` is a REGISTERED name (not a human label), and documents the full nested
2329
- * {@link WorkflowDefinition} as the ADVANCED form with a minimal example
2330
- * ({@link WORKFLOW_TOOL_NESTED_EXAMPLE}). Both examples are interpolated VERBATIM from the
2331
- * validated constants, so a parity test pins them — the description can never drift from a
2332
- * real, contract-valid example. The `parameters` the tool advertises are the FLAT shape's
2333
- * schema; the nested form is the documented escape-hatch (the tool accepts both).
2334
- */
2335
- export declare const WORKFLOW_TOOL_DESCRIPTION: string;
2336
-
2337
- /**
2338
- * A complete FLAT authoring example — the PRIMARY way a small model authors a workflow
2339
- * through {@link import('./factories.js').createWorkflowTool}: `{ name, steps: [{ name, via }] }`.
2340
- *
2341
- * @remarks
2342
- * Each step becomes a one-task phase, in order; a step's `name` is a REGISTERED behavior name
2343
- * (not a label) and `via` is the execution mechanism. The tool expands this
2344
- * ({@link import('./helpers.js').expandSteps}) into a valid {@link WorkflowDefinition}. It
2345
- * is embedded VERBATIM in {@link WORKFLOW_TOOL_DESCRIPTION} and guarded by a parity test
2346
- * (it must expand to a tree the STRICT contract accepts), so the doc example can never drift.
2347
- */
2348
- export declare const WORKFLOW_TOOL_FLAT_EXAMPLE: WorkflowSteps;
2349
-
2350
- /**
2351
- * The name under which the {@link import('./WorkflowRunner.js').WorkflowRunner} BINDS the
2352
- * depth/cycle-aware workflow tool onto a dispatched `agent` task's
2353
- * `AgentContextInterface` (the future `@orkestrel/agent` package, W-c2).
2354
- *
2355
- * @remarks
2356
- * The propagation seam's well-known key: before running an `agent` task, the runner adds a
2357
- * {@link import('./factories.js').createWorkflowTool}-built tool under this name to the
2358
- * resolved agent's `context.tools`, so the subagent can author + run a NESTED workflow
2359
- * (bounded by {@link MAX_WORKFLOW_DEPTH}). A subagent that wants to fan out into a workflow
2360
- * calls this tool by this name; the bound handler runs the nested workflow at depth + 1.
2361
- */
2362
- export declare const WORKFLOW_TOOL_NAME = "workflow";
2363
-
2364
- /**
2365
- * A minimal NESTED authoring example — the ADVANCED escape-hatch form a model may use
2366
- * instead of the flat shape: a full {@link WorkflowDefinition}.
2367
- *
2368
- * @remarks
2369
- * The full four-level form, documented in {@link WORKFLOW_TOOL_DESCRIPTION} as the advanced
2370
- * alternative. It is embedded VERBATIM and guarded by a parity test (`createWorkflowContract().is`
2371
- * must accept it), so the doc example can never drift from a valid definition.
2372
- */
2373
- export declare const WORKFLOW_TOOL_NESTED_EXAMPLE: WorkflowDefinition;
2374
-
2375
- /**
2376
- * The `agent`-task behavior resolver (W-c2) — resolves a registered agent BY NAME to a
2377
- * live {@link AgentInterface} the runner runs as a subagent.
2378
- *
2379
- * @remarks
2380
- * The `agent` analogue of the {@link WorkflowFunctions} registry / the
2381
- * {@link ToolManagerInterface} the other two forms dispatch through: the runner resolves
2382
- * an `agent`-form {@link TaskForm}'s `name` here. A name absent from the resolver is the
2383
- * no-handler case — the task AUTO-COMPLETES (the ROADMAP rule), exactly like an
2384
- * unregistered `function` / `tool`.
2385
- *
2386
- * **Resolve a FRESH agent per call.** A phase's tasks run CONCURRENTLY, so two `agent`
2387
- * tasks naming the same agent are resolved concurrently — and the runner BINDS a
2388
- * depth/cycle-aware workflow tool onto each resolved agent's `context.tools` (the
2389
- * propagation seam — see {@link WorkflowToolOptions}). An implementation that returns the
2390
- * SAME instance for both would have them race on that mutation, so it must mint a fresh
2391
- * agent each call (exactly as `AgentRegistry.build` does). A function resolver
2392
- * (`(name) => …`) is the minimal shape; the agents module's `AgentRegistry` is not a
2393
- * direct fit (its `build` rehydrates from a serializable `AgentJobInput`, not a bare
2394
- * name), so a small adapter `(name) => registry.build({ provider, ... })` bridges it.
2395
- *
2396
- * @param name - The agent's registry key (the `agent`-form's `name`)
2397
- * @returns The resolved live agent, or `undefined` when the name is unregistered
2398
- */
2399
- export declare type WorkflowAgents = (name: string) => AgentInterface | undefined;
2400
-
2401
2729
  /**
2402
2730
  * The ambient context of a workflow — the identity every level inherits.
2403
2731
  *
@@ -2433,68 +2761,6 @@ export declare interface WorkflowDefinition {
2433
2761
  readonly bail?: boolean;
2434
2762
  }
2435
2763
 
2436
- /**
2437
- * A draft workflow — a {@link WorkflowDefinition} with OPTIONAL `id` / `name` at all
2438
- * three levels (workflow / phase / task).
2439
- *
2440
- * @remarks
2441
- * The lenient authoring form `createWorkflowDraftContract` validates and
2442
- * {@link import('./helpers.js').completeDraft} completes into a strict
2443
- * {@link WorkflowDefinition}. `run` stays required; the `bail` policy carries over.
2444
- */
2445
- export declare interface WorkflowDraft {
2446
- readonly id?: string;
2447
- readonly name?: string;
2448
- readonly description?: string;
2449
- readonly phases: readonly PhaseDraft[];
2450
- /** Failure policy: `false` (default) continues gracefully, `true` halts on the first failure. */
2451
- readonly bail?: boolean;
2452
- }
2453
-
2454
- /**
2455
- * The shape of a DRAFT workflow — identical to {@link workflowShape} EXCEPT `id` and
2456
- * `name` are OPTIONAL at all three levels (workflow / phase / task), so a small model
2457
- * can omit the six identity strings and let the tool synthesize them positionally.
2458
- *
2459
- * @remarks
2460
- * The lenient counterpart {@link import('./factories.js').createWorkflowDraftContract}
2461
- * compiles. `run` stays required; a provided `id` / `name` still has `minLength: 1` (so an
2462
- * explicitly-empty `id: ''` is REJECTED, not auto-filled). After
2463
- * {@link import('./helpers.js').completeDraft} fills the missing ids/names, the result is
2464
- * validated against the STRICT {@link import('./factories.js').createWorkflowContract} gate
2465
- * before running.
2466
- */
2467
- export declare const workflowDraftShape: ObjectShape<{
2468
- id: OptionalShape<StringShape>;
2469
- name: OptionalShape<StringShape>;
2470
- description: OptionalShape<StringShape>;
2471
- phases: ArrayShape<ObjectShape<{
2472
- id: OptionalShape<StringShape>;
2473
- name: OptionalShape<StringShape>;
2474
- description: OptionalShape<StringShape>;
2475
- tasks: ArrayShape<ObjectShape<{
2476
- id: OptionalShape<StringShape>;
2477
- name: OptionalShape<StringShape>;
2478
- description: OptionalShape<StringShape>;
2479
- run: UnionShape<[ ObjectShape<{
2480
- via: LiteralShape<readonly ["function"]>;
2481
- name: StringShape;
2482
- }>, ObjectShape<{
2483
- via: LiteralShape<readonly ["tool"]>;
2484
- name: StringShape;
2485
- }>, ObjectShape<{
2486
- via: LiteralShape<readonly ["agent"]>;
2487
- name: StringShape;
2488
- }>]>;
2489
- retries: OptionalShape<NumberShape>;
2490
- timeout: OptionalShape<NumberShape>;
2491
- }>>;
2492
- concurrency: OptionalShape<NumberShape>;
2493
- bail: OptionalShape<LiteralShape<readonly [true, false]>>;
2494
- }>>;
2495
- bail: OptionalShape<LiteralShape<readonly [true, false]>>;
2496
- }>;
2497
-
2498
2764
  /**
2499
2765
  * An error thrown by the workflow entity + W-c2 recursion layer.
2500
2766
  *
@@ -2503,11 +2769,11 @@ export declare const workflowDraftShape: ObjectShape<{
2503
2769
  * offending node id / status. Thrown for an illegal lifecycle transition
2504
2770
  * (`TRANSITION`), a structurally invalid {@link import('./types.js').WorkflowSnapshot}
2505
2771
  * passed to {@link import('./factories.js').restoreWorkflow} (`RESTORE`), an over-deep /
2506
- * cyclic nested-workflow dispatch (`DEPTH`), and a malformed
2507
- * {@link import('./factories.js').createWorkflowTool} args blob (`TOOL`). On the
2508
- * workflow-tool seam the `DEPTH` / `TOOL` throw is ISOLATED by the
2509
- * `@orkestrel/agent` package's `ToolManager` into the tool result's
2510
- * top-level `error` (AGENTS §14 — the universal tool-handler contract).
2772
+ * cyclic nested-workflow dispatch (`DEPTH`), and a malformed workflow-authoring-tool args
2773
+ * blob (`TOOL`). `DEPTH` and `TOOL` are public type surface constructed by the
2774
+ * `@orkestrel/tool` package's workflow-tool / agent-function adapters; on that seam the
2775
+ * throw is ISOLATED by its `ToolManager` into the tool result's top-level `error`
2776
+ * (AGENTS §14 — the universal tool-handler contract).
2511
2777
  */
2512
2778
  export declare class WorkflowError extends Error {
2513
2779
  readonly code: WorkflowErrorCode;
@@ -2525,23 +2791,35 @@ export declare class WorkflowError extends Error {
2525
2791
  * the offending current status + requested transition in the error `context`.
2526
2792
  * - `RESTORE` — a {@link import('./factories.js').restoreWorkflow} given a structurally
2527
2793
  * invalid {@link WorkflowSnapshot} (a status outside the lifecycle vocabulary).
2528
- * - `DEPTH` — a nested-workflow dispatch (W-c2) that the runner's depth / cycle guard
2529
- * rejected: running it would push the nested-workflow chain past
2530
- * {@link import('./constants.js').MAX_WORKFLOW_DEPTH}, OR its target agent (or a
2531
- * workflow it would author) is already an ancestor of the current run (a re-entry
2532
- * cycle). Raised on BOTH seams of the W-c2 recursion an `agent`-task dispatch
2533
- * (`fail`ed with this code; it never runs) AND the
2534
- * {@link import('./factories.js').createWorkflowTool} handler (thrown, then ISOLATED
2535
- * by the `@orkestrel/agent` package's `ToolManager` into the tool
2536
- * result's `error`). The error `context` names the offending agent / workflow id + the depth.
2537
- * - `TOOL` the {@link import('./factories.js').createWorkflowTool} handler was handed
2538
- * a MALFORMED / over-constraint authored args blob (e.g. an empty `id`, `concurrency: 0`)
2539
- * that the contract rejected, so no workflow ran. The handler THROWS it (rather than
2540
- * returning a failure result), and the `@orkestrel/agent` package's `ToolManager`
2541
- * ISOLATES the throw into the canonical tool result's top-level `error` (AGENTS §14 — the
2542
- * universal tool-handler contract); the error `context` names the wrapped workflow id.
2543
- */
2544
- export declare type WorkflowErrorCode = 'TRANSITION' | 'RESTORE' | 'DEPTH' | 'TOOL';
2794
+ * - `DEPTH` — a nested-workflow dispatch (W-c2) that a depth / cycle guard rejected:
2795
+ * running it would push the nested-workflow chain past a bounded max depth, OR its
2796
+ * target agent (or a workflow it would author) is already an ancestor of the current
2797
+ * run (a re-entry cycle). Public type surface consumed by the `@orkestrel/tool`
2798
+ * package's workflow-tool / agent-function adapters, which construct
2799
+ * {@link import('./errors.js').WorkflowError}s with this code (thrown, then ISOLATED
2800
+ * by that package's `ToolManager` into the tool result's `error`). The error `context`
2801
+ * names the offending agent / workflow id + the depth.
2802
+ * - `TOOL` a workflow-authoring tool handler (in `@orkestrel/tool`) was handed a
2803
+ * MALFORMED / over-constraint authored args blob (e.g. an empty `id`, `concurrency: 0`)
2804
+ * that {@link import('./factories.js').createWorkflowContract} rejected, so no workflow
2805
+ * ran. Public type surface: `@orkestrel/tool` constructs this code and THROWS it
2806
+ * (rather than returning a failure result); its `ToolManager` ISOLATES the throw into
2807
+ * the canonical tool result's top-level `error` (AGENTS §14 — the universal
2808
+ * tool-handler contract); the error `context` names the wrapped workflow id.
2809
+ * - `MUTATION` — a GATED structural or patch edit was refused: a duplicate id on
2810
+ * `append`/`add`, a target that does not exist or is not `pending`, an out-of-bounds
2811
+ * `index`, a patch that failed shaper validation, or a live structural edit refused by
2812
+ * the NATIVE bottom-up gate — a terminal container, an edit targeting (or destined for)
2813
+ * a position BEFORE the container's own pending-suffix boundary, or (a running phase)
2814
+ * anything other than a pure append. The manager /
2815
+ * entity structural API (AGENTS §12) returns it as a graceful `Result` `failure` —
2816
+ * it NEVER throws for this code except {@link TaskInterface.patch} /
2817
+ * {@link PhaseInterface.patch}'s defense-in-depth self-check and the build-time
2818
+ * {@link TaskManagerInterface.append} / {@link PhaseManagerInterface.append} duplicate-id
2819
+ * guard (both genuine programmer-error paths, AGENTS §12). The error `context` names
2820
+ * the offending id / index / status.
2821
+ */
2822
+ export declare type WorkflowErrorCode = 'TRANSITION' | 'RESTORE' | 'DEPTH' | 'TOOL' | 'MUTATION';
2545
2823
 
2546
2824
  /**
2547
2825
  * The push observation surface (AGENTS §13) of the workflow entity (W-b) — the
@@ -2552,10 +2830,13 @@ export declare type WorkflowErrorCode = 'TRANSITION' | 'RESTORE' | 'DEPTH' | 'TO
2552
2830
  * Present-tense events with arg tuples. `start` fires when the workflow begins;
2553
2831
  * `complete` when every phase settled successfully; `fail` when a phase failed
2554
2832
  * under `bail` (carrying the failing {@link TaskResult}); `stop` when the workflow
2555
- * was permanently ended. A throwing listener never reaches the domain surface the
2556
- * emitter isolates it and routes it to its OWN `error` handler (the `error` option,
2557
- * AGENTS §13). Declared as a `type` alias (not `interface extends EventMap`, AGENTS
2558
- * §4.5) so the type-literal satisfies `EventMap` structurally.
2833
+ * was permanently ended. `add` / `remove` / `move` / `update` fire on a successful
2834
+ * structural or patch edit through {@link WorkflowInterface.add} / `remove` / `move` /
2835
+ * `update` (AGENTS §7) never on a refused/gated one. A throwing listener never
2836
+ * reaches the domain surface — the emitter isolates it and routes it to its OWN
2837
+ * `error` handler (the `error` option, AGENTS §13). Declared as a `type` alias (not
2838
+ * `interface extends EventMap`, AGENTS §4.5) so the type-literal satisfies `EventMap`
2839
+ * structurally.
2559
2840
  */
2560
2841
  export declare type WorkflowEventMap = {
2561
2842
  /** The workflow began — its `id`. */
@@ -2566,6 +2847,14 @@ export declare type WorkflowEventMap = {
2566
2847
  readonly fail: readonly [result: TaskResult];
2567
2848
  /** The workflow was permanently stopped. */
2568
2849
  readonly stop: readonly [];
2850
+ /** A phase was inserted — the inserted phase + its final index. */
2851
+ readonly add: readonly [phase: PhaseInterface, index: number];
2852
+ /** A phase was removed — the removed phase. */
2853
+ readonly remove: readonly [phase: PhaseInterface];
2854
+ /** A phase was repositioned — the moved phase + its new index. */
2855
+ readonly move: readonly [phase: PhaseInterface, index: number];
2856
+ /** A phase was patched — the patched phase. */
2857
+ readonly update: readonly [phase: PhaseInterface];
2569
2858
  };
2570
2859
 
2571
2860
  /**
@@ -2589,10 +2878,10 @@ export declare type WorkflowFunction = (controller: TaskControllerInterface) =>
2589
2878
  * {@link WorkflowFunction} handlers.
2590
2879
  *
2591
2880
  * @remarks
2592
- * The runner resolves a `function`-form {@link TaskForm}'s `name` here. A name absent
2593
- * from the registry is the no-handler case the task AUTO-COMPLETES (the ROADMAP rule),
2594
- * exactly like an unsupported form. A plain record (not a manager) the registry is a
2595
- * lookup, with no lifecycle of its own.
2881
+ * A live {@link TaskInterface} resolves its `run` name against this registry ONCE at
2882
+ * construction into its {@link TaskInterface.handler}. A name absent from the registry (or
2883
+ * an omitted `run`) is the no-handler case the task AUTO-COMPLETES (the ROADMAP rule). A
2884
+ * plain record (not a manager) — the registry is a lookup, with no lifecycle of its own.
2596
2885
  */
2597
2886
  export declare type WorkflowFunctions = Readonly<Record<string, WorkflowFunction>>;
2598
2887
 
@@ -2634,13 +2923,182 @@ export declare interface WorkflowInterface {
2634
2923
  readonly bail: boolean;
2635
2924
  readonly status: WorkflowStatus;
2636
2925
  readonly phases: PhaseManagerInterface;
2926
+ /**
2927
+ * Whether the workflow is currently paused (AGENTS §10 — resumable); RUNTIME-ONLY —
2928
+ * never a {@link WorkflowStatus}, never persisted in a {@link WorkflowSnapshot} (a
2929
+ * paused workflow's `status` still reports its ordinary `pending` / `running` value).
2930
+ */
2931
+ readonly paused: boolean;
2932
+ /** Whether {@link destroy} has torn this workflow down; RUNTIME-ONLY, never persisted. */
2933
+ readonly destroyed: boolean;
2934
+ /**
2935
+ * This workflow's own cancellation signal — fires on {@link destroy}. RUNTIME-ONLY
2936
+ * (implemented over `@orkestrel/abort`, AGENTS core precedent), never persisted.
2937
+ */
2938
+ readonly signal: AbortSignal;
2637
2939
  /** Look up one live phase by its `id`. */
2638
2940
  phase(id: string): PhaseInterface | undefined;
2639
2941
  /** Every settled task's result across all phases, in positional order — the workflow tier of the result tree. */
2640
2942
  results(): readonly TaskResult[];
2943
+ /**
2944
+ * FORCE this workflow to `skipped` (AGENTS §10), overriding the derived value; idempotent.
2945
+ *
2946
+ * @remarks
2947
+ * A NO-OP once `status` is already terminal — a settled workflow cannot be re-forced.
2948
+ * Always releases a parked {@link wait} waiter regardless (a terminal workflow has nothing
2949
+ * left to pause for).
2950
+ */
2641
2951
  skip(): void;
2952
+ /**
2953
+ * FORCE this workflow to `stopped` (AGENTS §10), overriding the derived value; idempotent.
2954
+ *
2955
+ * @remarks
2956
+ * A NO-OP once `status` is already terminal — a settled workflow cannot be re-forced. Always
2957
+ * releases a parked {@link wait} waiter regardless (a terminal workflow has nothing left to
2958
+ * pause for).
2959
+ */
2642
2960
  stop(): void;
2961
+ /**
2962
+ * FORCE this workflow to `completed` (AGENTS §10), overriding the derived value.
2963
+ *
2964
+ * @remarks
2965
+ * A NO-OP unless `status` is `pending` — its ONLY legitimate use is settling a vacuously
2966
+ * DONE tree (no work happened), mirroring the runner's own gate. Never overrides a real
2967
+ * `completed` / a bail-true `failed` / a `stopped` / a derived `skipped`.
2968
+ */
2643
2969
  complete(): void;
2970
+ /**
2971
+ * Suspend the workflow (AGENTS §10 — resumable); idempotent.
2972
+ *
2973
+ * @remarks
2974
+ * A no-op when already `paused`, when `status` is terminal, or once {@link destroyed}.
2975
+ * RUNTIME-ONLY (AGENTS §10) — never a {@link WorkflowStatus}, never persisted in a
2976
+ * {@link WorkflowSnapshot}. A driving {@link WorkflowRunnerInterface.execute} gates at the
2977
+ * next phase boundary and before each task's own dispatch; an in-flight task body is
2978
+ * never suspended mid-flight. **Pausing does NOT suspend the run's timeout / budget /
2979
+ * abort clocks** — those bounds keep ticking while paused, so a run parked on
2980
+ * `pause()` can still be cancelled (and settle `stopped`) by its own deadline / budget /
2981
+ * abort while parked.
2982
+ *
2983
+ * @example
2984
+ * ```ts
2985
+ * workflow.pause()
2986
+ * workflow.paused // true
2987
+ * ```
2988
+ */
2989
+ pause(): void;
2990
+ /**
2991
+ * Continue a paused workflow (AGENTS §10); idempotent — a no-op unless {@link paused}.
2992
+ *
2993
+ * @example
2994
+ * ```ts
2995
+ * workflow.resume()
2996
+ * workflow.paused // false
2997
+ * ```
2998
+ */
2999
+ resume(): void;
3000
+ /**
3001
+ * Tear this workflow down (AGENTS §10) — a TERMINAL teardown: aborts {@link signal},
3002
+ * `stop`s every non-terminal live phase (so any engine parked on a phase's own gate
3003
+ * unparks and the tree lands coherent), forces the `stop` override on THIS workflow if
3004
+ * it is not already terminal, resolves any parked {@link wait} waiter, and marks
3005
+ * {@link destroyed}; idempotent.
3006
+ *
3007
+ * @remarks
3008
+ * After `destroy`, every structural mutator (`add` / `remove` / `move` / `update` /
3009
+ * `patch`) and `pause` / `resume` reject (a `Result` failure) or no-op — never throws
3010
+ * for calling `destroy` itself twice.
3011
+ *
3012
+ * @example
3013
+ * ```ts
3014
+ * workflow.destroy()
3015
+ * workflow.destroyed // true
3016
+ * ```
3017
+ */
3018
+ destroy(): void;
3019
+ /**
3020
+ * Park until this workflow is not paused — **promise-parked**, never a timer or
3021
+ * busy-loop (AGENTS §21; mirrors {@link ControllerInterface.wait}'s doc style).
3022
+ *
3023
+ * @remarks
3024
+ * Resolves IMMEDIATELY when not {@link paused}. While paused, parks until `resume` /
3025
+ * `skip` / `stop` / `destroy` — each always releases a parked waiter (a permanently
3026
+ * ended workflow has nothing left to pause for). NEVER rejects.
3027
+ *
3028
+ * @returns A promise that resolves once the workflow is no longer paused
3029
+ */
3030
+ wait(): Promise<void>;
3031
+ /**
3032
+ * MINT a live {@link PhaseInterface} (and its tasks) from `definition` and insert it
3033
+ * into this workflow (AGENTS §7 the entity structural API) — gated BEFORE delegating
3034
+ * to {@link phases}' manager.
3035
+ *
3036
+ * @remarks
3037
+ * Converts `definition` → {@link PhaseSnapshot} and constructs the live phase (wired to
3038
+ * THIS workflow, its recompute cascade, and its emitter hooks) plus each of its live
3039
+ * tasks — each task's `run` / `retries` / `timeout` carried from its {@link TaskDefinition}
3040
+ * and its {@link TaskInterface.handler} resolved against the workflow-level
3041
+ * {@link WorkflowOptions.functions} registry (mirrors
3042
+ * {@link import('./factories.js').createWorkflow}'s build-time resolution). The
3043
+ * phase's effective `bail` resolves exactly as the build path does
3044
+ * (`definition.bail ?? this.bail`). Requires `definition.id` to be UNIQUE among this
3045
+ * workflow's existing phase ids — a duplicate is a `MUTATION` failure (mirrors
3046
+ * {@link PhaseManagerInterface.add}'s own duplicate-id gate).
3047
+ *
3048
+ * NATIVE gating, purely from this workflow's own derived `status` and the phase list's
3049
+ * positions (AGENTS §12 — no runner-installed hook), UNCHANGED from the entity-taking
3050
+ * predecessor: refused outright while this workflow's own `status` is terminal or once
3051
+ * {@link destroyed}. Otherwise the effective target position (`index ?? phases.count`)
3052
+ * must fall within the PENDING SUFFIX — the contiguous trailing run of `pending` phases
3053
+ * (phases run sequentially, so every already-started phase forms a contiguous leading
3054
+ * prefix); its boundary is {@link import('./helpers.js').deriveBoundary}. A `pending`
3055
+ * workflow's phases are ALL `pending`, so the boundary is `0` and every index is
3056
+ * naturally accepted — no special case needed. Delegates the minted phase to
3057
+ * {@link PhaseManagerInterface.add} then emits `add` on success.
3058
+ *
3059
+ * @param definition - The {@link PhaseDefinition} to mint a live phase (and tasks) from
3060
+ * @param index - The insertion position; omitted inserts at the end
3061
+ * @returns A {@link Result} boxing the minted, inserted phase, or a `MUTATION` failure
3062
+ */
3063
+ add(definition: PhaseDefinition, index?: number): Result<PhaseInterface, WorkflowError>;
3064
+ /**
3065
+ * Remove the `pending` phase `id` from this workflow.
3066
+ *
3067
+ * @remarks
3068
+ * NATIVE gating: refused while this workflow's own `status` is terminal. Otherwise the
3069
+ * target must exist at an index within the pending suffix (at or past
3070
+ * {@link import('./helpers.js').deriveBoundary}) — the manager separately gates the
3071
+ * target's own `pending` status (AGENTS §9).
3072
+ *
3073
+ * @param id - The phase id to remove
3074
+ * @returns A {@link Result} boxing the removed phase, or a `MUTATION` failure
3075
+ */
3076
+ remove(id: string): Result<PhaseInterface, WorkflowError>;
3077
+ /**
3078
+ * Reposition the `pending` phase `id` to `index` within this workflow.
3079
+ *
3080
+ * @remarks
3081
+ * NATIVE gating: refused while this workflow's own `status` is terminal. Otherwise BOTH
3082
+ * the target's current index and the destination `index` must fall within the pending
3083
+ * suffix (see {@link remove}).
3084
+ *
3085
+ * @param id - The phase id to move
3086
+ * @param index - The destination position
3087
+ * @returns A {@link Result} boxing the moved phase, or a `MUTATION` failure
3088
+ */
3089
+ move(id: string, index: number): Result<PhaseInterface, WorkflowError>;
3090
+ /**
3091
+ * Apply a validated {@link PhaseUpdate} patch to the `pending` phase `id` in this workflow.
3092
+ *
3093
+ * @remarks
3094
+ * NATIVE gating: refused while this workflow's own `status` is terminal. Otherwise the
3095
+ * target must exist at an index within the pending suffix (see {@link remove}).
3096
+ *
3097
+ * @param id - The phase id to patch
3098
+ * @param patch - The fields to update
3099
+ * @returns A {@link Result} boxing the patched phase, or a `MUTATION` failure
3100
+ */
3101
+ update(id: string, patch: PhaseUpdate): Result<PhaseInterface, WorkflowError>;
2644
3102
  snapshot(): WorkflowSnapshot;
2645
3103
  }
2646
3104
 
@@ -2670,6 +3128,19 @@ export declare interface WorkflowOptions {
2670
3128
  readonly error?: EmitterErrorHandler;
2671
3129
  /** Per-phase {@link PhaseOptions}, keyed by the phase's `id`. */
2672
3130
  readonly phases?: Readonly<Record<string, PhaseOptions>>;
3131
+ /**
3132
+ * The `function`-task behavior registry ({@link WorkflowFunctions}) each live task's
3133
+ * {@link TaskDefinition.run} / {@link TaskSnapshot.run} name resolves against ONCE at
3134
+ * construction into its runtime {@link TaskInterface.handler} — the SAME registry a
3135
+ * fresh build ({@link import('./factories.js').createWorkflow}) and a restore
3136
+ * ({@link import('./factories.js').restoreWorkflow}) both consume, and the same shape a
3137
+ * live {@link WorkflowInterface.add} / {@link PhaseInterface.add} mint resolves a newly
3138
+ * minted task against. A `run` name absent from `functions` (or omitted entirely)
3139
+ * resolves to no handler — that task AUTO-COMPLETES (the no-handler rule): its
3140
+ * phase/workflow still reaches a terminal status, just with no dispatched behavior.
3141
+ * Omitted ⇒ an empty registry (every task auto-completes).
3142
+ */
3143
+ readonly functions?: WorkflowFunctions;
2673
3144
  }
2674
3145
 
2675
3146
  /**
@@ -2695,35 +3166,51 @@ export declare interface WorkflowResult {
2695
3166
 
2696
3167
  /**
2697
3168
  * The thin orchestrator that EXECUTES a live W-b workflow tree by COMPOSING the shipped
2698
- * substrate — phases sequential, tasks concurrent — dispatching each task BY NAME under the
2699
- * `bail` policy, including the W-c2 `agent` form behind a depth + cycle guard.
3169
+ * substrate — phases sequential, tasks concurrent — dispatching each task through its OWN
3170
+ * resolved handler under the `bail` policy.
2700
3171
  *
2701
3172
  * @remarks
2702
3173
  * - **Composes, never re-implements.** Per-phase bounded concurrency is one
2703
3174
  * {@link createRunner} per phase (the substrate {@link RunnerInterface} over the workers
2704
3175
  * `Queue`); `bail` maps onto that Runner's fail-fast vs settle-all; the run-level abort /
2705
- * timeout / budget fold through {@link createAbort} / {@link createTimeout} +
3176
+ * timeout / budget / entity `signal` fold through {@link createAbort} / {@link createTimeout} +
2706
3177
  * `AbortSignal.any` (exactly as the agent runtime folds its bounds); pacing is the shipped
2707
3178
  * {@link SchedulerInterface}. The runner writes ZERO concurrency / retry / abort logic of
2708
- * its own — it only sequences phases, dispatches a task, and drives the live entity.
2709
- * - **Phases sequential, tasks concurrent.** `#execute` awaits the phases in order (phase
2710
- * N+1 starts only once phase N has fully settled). Within a phase, ALL its tasks are the
2711
- * one Runner's `inputs`, run at `concurrency` = the phase's
2712
- * {@link PhaseDefinition.concurrency} (default {@link DEFAULT_PHASE_CONCURRENCY}).
2713
- * - **Dispatch by name.** `#dispatch` branches on the task's
2714
- * {@link import('./types.js').TaskForm} (read from the `definition`, correlated by `id`):
2715
- * `function` the {@link WorkflowFunctions} registry, `tool` the
2716
- * {@link ToolManagerInterface}, `agent` the {@link WorkflowAgents} resolver (W-c2). A
2717
- * handler that is NOT found (an unregistered name for ANY form) AUTO-COMPLETES — the
2718
- * ROADMAP no-handler rule.
2719
- * - **`agent` form + depth/cycle guard (W-c2).** An `agent` task resolves its subagent via
2720
- * `agents`, BINDS a depth/cycle-aware workflow tool onto the subagent's `context.tools`
2721
- * (the propagation seam), folds the task's cancellation into the agent run (a workflow
2722
- * cancel `abort`s the subagent), and drives it: success → `complete(result)`, throw →
2723
- * `fail(error)`. Before running, the guard REJECTS the task into a typed `DEPTH`
2724
- * {@link WorkflowError} (`fail`) when running it would push the nested chain past
2725
- * {@link MAX_WORKFLOW_DEPTH}, OR when its target agent is already an ancestor (a cycle).
2726
- * The rejected task never runs the agent.
3179
+ * its own — it only sequences phases, dispatches a task's own handler, and drives the live
3180
+ * entity.
3181
+ * - **Pure engine no registries, no tool/agent knowledge.** The runner carries no
3182
+ * `functions` / `tools` / `agents` registry: each live {@link TaskInterface} already
3183
+ * resolved its own {@link import('./types.js').WorkflowFunction} into
3184
+ * {@link import('./types.js').TaskInterface.handler} ONCE at construction (build, restore,
3185
+ * or a live mint all resolve it identically, from {@link WorkflowOptions.functions}), so
3186
+ * dispatch is simply "invoke the task's own handler". Static tool / agent calling is an
3187
+ * OPT-IN concern of the `@orkestrel/tool` package's adapter factories — plain
3188
+ * {@link import('./types.js').WorkflowFunction}s a caller wires into
3189
+ * {@link WorkflowOptions.functions} like any other behavior. This module never imports
3190
+ * any tool/agent package.
3191
+ * - **Two `execute` forms, one engine.** `execute(definition, options)` BUILDS the live tree
3192
+ * from a {@link WorkflowDefinition} (single source of truth for the `run` / `concurrency`
3193
+ * metadata); `execute(workflow, options)` DRIVES a caller-owned, ALREADY-BUILT
3194
+ * {@link WorkflowInterface} instead the entity-native control surface (AGENTS §10:
3195
+ * `pause` / `resume` / `add` / `stop` / `destroy` live on the entity itself). Both forms
3196
+ * converge on the SAME `#execute` engine: neither reads a `WorkflowDefinition` once the tree
3197
+ * exists `#runTask` reads each task's OWN {@link import('./types.js').TaskInterface.handler}
3198
+ * / `retries` / `timeout`, and `#runPhase` reads each phase's OWN
3199
+ * {@link PhaseInterface.concurrency} / `bail`, so a live `add`-minted phase or task (V5)
3200
+ * runs under EXACTLY the same rules as one built from the original definition.
3201
+ * - **Phases sequential, tasks concurrent — LIVE continuity.** `#execute` drives the phases in
3202
+ * order, RE-READING `workflow.phases.phases()` every iteration (a cursor over the live
3203
+ * manager, not a one-time snapshot) so a caller's `workflow.add(phaseDefinition)` mid-run is
3204
+ * picked up. Within a phase, `#runPhase` subscribes to that phase's `add` event BEFORE
3205
+ * capturing its task list, then `spawn`s any task added mid-phase onto the SAME substrate
3206
+ * Runner (so it is actually dispatched, under the same `concurrency`); a task added too late
3207
+ * for `spawn` to accept (the runner already drained) is swept `skip`ped afterward so the
3208
+ * phase always reaches a coherent terminal state.
3209
+ * - **Dispatch by handler.** `#runTask` invokes the live task's own
3210
+ * {@link import('./types.js').TaskInterface.handler} directly: `undefined` (an omitted `run`,
3211
+ * or a `run` name absent from the {@link WorkflowOptions.functions} registry it was resolved
3212
+ * against) AUTO-COMPLETES — the ROADMAP no-handler rule; otherwise the handler runs with the
3213
+ * task's {@link import('./types.js').TaskControllerInterface} handle.
2727
3214
  * - **`bail` → substrate.** Under `bail: true` (halt) a genuine task failure `fail`s the leaf
2728
3215
  * THEN re-throws, so the substrate Runner fail-fasts — it aborts the in-flight siblings
2729
3216
  * (their `controller.signal` fires; a mid-flight sibling `skip`s) and rejects the phase run;
@@ -2731,7 +3218,20 @@ export declare interface WorkflowResult {
2731
3218
  * Under `bail: false` (graceful) a failure `fail`s the leaf and RESOLVES (never throws), so
2732
3219
  * the Runner settles every unit (allSettled) and the run finishes (the workflow derives
2733
3220
  * `completed`, the failure recorded in the result tree).
2734
- * - **Abort / Timeout / Budget fold.** `#execute` folds the run's external `signal`, a
3221
+ * - **Pause / stop / destroy gates.** `workflow.pause()` is honoured at exactly two points —
3222
+ * the next phase boundary (workflow-only) and each task's own pre-dispatch (before
3223
+ * `task.start()`, workflow gate FIRST then this task's own `phase.pause()`) — by parking on
3224
+ * {@link WorkflowInterface.wait} / {@link PhaseInterface.wait}; an in-flight task body is
3225
+ * NEVER suspended mid-flight. A GRACEFUL `workflow.stop()` (no signal involved) is caught at
3226
+ * those same gates: not-yet-started work is `skip`ped, in-flight work finishes naturally. A
3227
+ * HARD `workflow.destroy()` aborts {@link WorkflowInterface.signal}, which `#fold` has folded
3228
+ * into the run's composed signal — so it cancels the active phase Runner (and every
3229
+ * in-flight task) exactly like an external abort / timeout / budget fire. EVERY park on a
3230
+ * `wait()` gate is RACED against that same run signal (`#raceWait`, S2) — so a cancel firing
3231
+ * WHILE parked unparks the engine promptly instead of hanging until `resume`; the existing
3232
+ * halt / abort re-checks after the gate then decide the outcome.
3233
+ * - **Abort / Timeout / Budget / entity-signal fold.** `#execute` folds the live workflow's
3234
+ * own {@link WorkflowInterface.signal}, the run's external `signal`, a
2735
3235
  * {@link TimeoutInterface}, and the `@orkestrel/budget` package's `BudgetInterface`'s
2736
3236
  * `signal` into one `runSignal` (`AbortSignal.any`); a fire aborts the active phase's Runner
2737
3237
  * (cancelling every in-flight task) and HALTS the run — the remaining tasks / phases `skip`
@@ -2739,19 +3239,77 @@ export declare interface WorkflowResult {
2739
3239
  * {@link TaskController} signal `AbortSignal.any`-combines the substrate per-unit signal with
2740
3240
  * `runSignal`, so a handler observes either cause directly.
2741
3241
  * - **Re-entrant-safe.** No shared per-run mutable field: the active-Runner holder is LOCAL to
2742
- * each `#execute`, so a nested `execute` (the bound workflow tool re-entering this instance
2743
- * while the outer run is suspended on an `agent` task) cannot clobber the outer run's state.
3242
+ * each `#execute`, so a nested `execute` (a bound workflow-tool handler re-entering this
3243
+ * instance while the outer run is suspended awaiting it) cannot clobber the outer run's state.
2744
3244
  */
2745
3245
  export declare class WorkflowRunner implements WorkflowRunnerInterface {
2746
3246
  #private;
2747
- constructor(functions: WorkflowFunctions, tools: ToolManagerInterface | undefined, agents: WorkflowAgents | undefined, scheduler: SchedulerInterface, workflowTool: WorkflowToolBinder | undefined);
3247
+ constructor(scheduler: SchedulerInterface);
3248
+ /**
3249
+ * Execute a workflow definition to completion — BUILD its live tree, run the phases
3250
+ * sequentially with each phase's tasks concurrent — resolving its terminal
3251
+ * {@link WorkflowResult} (whose `workflow` is the freshly-built live tree).
3252
+ *
3253
+ * @remarks
3254
+ * One-shot. The runner BUILDS the live tree from `definition` internally (one source of
3255
+ * truth — the per-task `run` and per-phase `concurrency` come from the same definition
3256
+ * the tree is constructed from, so the executed tree can never drift from the metadata).
3257
+ * The {@link WorkflowOptions} part of `options` (initial `on` listeners, a `bail` override,
3258
+ * the per-node `phases` bag, the {@link WorkflowOptions.functions} registry each task's
3259
+ * `run` resolves against) is forwarded to the build. Under `bail: false` (graceful) every
3260
+ * task settles (a failure is recorded on its {@link TaskInterface}) and the workflow
3261
+ * reaches `completed`; under `bail: true` (halt) the first failure aborts the in-flight
3262
+ * sibling tasks AND `skip`s the remaining tasks / phases, settling the workflow `failed`. A
3263
+ * {@link WorkflowRunOptions} abort / timeout / budget fires every in-flight task's signal
3264
+ * and `stop`s the run. `execute` resolves (never rejects) on a cancel — the partial outcome
3265
+ * is read from the returned {@link WorkflowResult} (its `workflow` / `status` / `results`).
3266
+ *
3267
+ * @param definition - The {@link WorkflowDefinition} to build the live tree from and drive
3268
+ * @param options - The construction options ({@link WorkflowOptions}: `on` / `bail` /
3269
+ * `phases` / `functions`) PLUS the per-run bounds (`signal` / `timeout` / `budget`)
3270
+ * @returns The run's terminal {@link WorkflowResult} (its `workflow` is the built tree)
3271
+ * @example
3272
+ * ```ts
3273
+ * const result = await runner.execute(definition, { timeout: 5_000 })
3274
+ * result.status // 'completed' | 'failed' | 'stopped'
3275
+ * ```
3276
+ */
2748
3277
  execute(definition: WorkflowDefinition, options?: WorkflowRunOptions): Promise<WorkflowResult>;
3278
+ /**
3279
+ * Drive an ALREADY-BUILT, CALLER-OWNED live {@link WorkflowInterface} — the entity-native
3280
+ * counterpart to the definition-building {@link execute} overload.
3281
+ *
3282
+ * @remarks
3283
+ * `createWorkflow` mints the live tree, this overload drives it, and the caller controls
3284
+ * the SAME entity mid-run via its own `pause` / `resume` / `add` / `stop` / `destroy`
3285
+ * (AGENTS §10). Requires `workflow.status === 'pending'` and `!workflow.destroyed` —
3286
+ * otherwise this is a programmer-timing error and it THROWS a `TRANSITION`
3287
+ * {@link WorkflowError} (AGENTS §12) rather than silently no-opping or building a second
3288
+ * tree. Once accepted, observable semantics are byte-identical to the `definition` form —
3289
+ * except the phase loop RE-READS the live tree every iteration, so a caller's live `add`
3290
+ * mid-run is picked up and actually dispatched. `options` carries only the per-run bounds
3291
+ * (`signal` / `timeout` / `budget`) — the construction half of {@link WorkflowRunOptions}
3292
+ * does not apply, since the tree already exists.
3293
+ *
3294
+ * @param workflow - The live {@link WorkflowInterface} to drive
3295
+ * @param options - The per-run bounds (`signal` / `timeout` / `budget`)
3296
+ * @returns The run's terminal {@link WorkflowResult} (its `workflow` is the SAME entity passed in)
3297
+ * @example
3298
+ * ```ts
3299
+ * const workflow = createWorkflow(definition)
3300
+ * const run = runner.execute(workflow)
3301
+ * workflow.pause()
3302
+ * workflow.resume()
3303
+ * await run
3304
+ * ```
3305
+ */
3306
+ execute(workflow: WorkflowInterface, options?: Omit<WorkflowRunOptions, keyof WorkflowOptions>): Promise<WorkflowResult>;
2749
3307
  }
2750
3308
 
2751
3309
  /**
2752
3310
  * A thin orchestrator that EXECUTES a live {@link WorkflowInterface} tree by composing the
2753
- * shipped substrate — phases sequential, tasks concurrent, dispatched by name under the
2754
- * `bail` policy.
3311
+ * shipped substrate — phases sequential, tasks concurrent, each task dispatched through its
3312
+ * OWN resolved handler under the `bail` policy.
2755
3313
  *
2756
3314
  * @remarks
2757
3315
  * `execute(definition, options?)` BUILDS the live W-b entity tree from the definition itself
@@ -2760,18 +3318,22 @@ export declare class WorkflowRunner implements WorkflowRunnerInterface {
2760
3318
  * through ONE substrate {@link RunnerInterface} (concurrency =
2761
3319
  * the phase's {@link PhaseDefinition.concurrency}). The definition is the SINGLE source of
2762
3320
  * truth: the runner owns both the declarative state (the live tree it constructs) and the
2763
- * EXECUTION-ONLY fields the snapshot deliberately dropped — each task's {@link TaskForm}
2764
- * (`run`) and each phase's `concurrency` (so there is no separately-supplied workflow to drift
2765
- * from the definition). The freshly-built live tree is returned in {@link WorkflowResult.workflow}.
2766
- * Each task is dispatched by its {@link TaskForm} a `function` through the
2767
- * {@link WorkflowFunctions} registry, a `tool` through the {@link ToolManagerInterface}; a
2768
- * task whose handler is not found AUTO-COMPLETES. The runner DRIVES the live entity (`start`
2769
- * → `complete` / `fail`), never re-implementing status. The `bail` policy maps onto the
2770
- * substrate's fail-fast (`bail: true` — the first failure aborts in-flight siblings and skips
2771
- * the rest) vs settle-all (`bail: false` — failures are recorded and the run finishes). The
2772
- * {@link WorkflowOptions} half of the options is forwarded to `createWorkflow` (initial
2773
- * listeners, a `bail` override, per-node options); the Abort / Timeout / Budget bounds fold
2774
- * per run via `AbortSignal.any`, halting the run and `stop`ping the workflow.
3321
+ * EXECUTION-ONLY field the snapshot deliberately dropped — each task's `run` (resolved into
3322
+ * its {@link TaskInterface.handler} once at construction, against
3323
+ * {@link WorkflowOptions.functions}) and each phase's `concurrency` (so there is no
3324
+ * separately-supplied workflow to drift from the definition). The freshly-built live tree is
3325
+ * returned in {@link WorkflowResult.workflow}. The runner carries NO registry of its own — it
3326
+ * simply invokes each task's OWN {@link TaskInterface.handler}; a task with no handler
3327
+ * AUTO-COMPLETES. The runner DRIVES the live entity (`start` → `complete` / `fail`), never
3328
+ * re-implementing status. The `bail` policy maps onto the substrate's fail-fast (`bail: true`
3329
+ * the first failure aborts in-flight siblings and skips the rest) vs settle-all (`bail:
3330
+ * false` failures are recorded and the run finishes). The {@link WorkflowOptions} half of
3331
+ * the options is forwarded to `createWorkflow` (initial listeners, a `bail` override,
3332
+ * per-node options, the `functions` registry); the Abort / Timeout / Budget bounds fold per
3333
+ * run via `AbortSignal.any`, halting the run and `stop`ping the workflow. A second
3334
+ * `execute(workflow, options?)` overload drives a CALLER-BUILT live tree instead — the
3335
+ * entity-native control surface (AGENTS §10: `pause` / `resume` / `add` / `stop` /
3336
+ * `destroy` live on {@link WorkflowInterface} itself); see its own doc for details.
2775
3337
  */
2776
3338
  export declare interface WorkflowRunnerInterface {
2777
3339
  /**
@@ -2781,10 +3343,11 @@ export declare interface WorkflowRunnerInterface {
2781
3343
  *
2782
3344
  * @remarks
2783
3345
  * One-shot. The runner BUILDS the live tree from `definition` internally (one source of
2784
- * truth — the per-task {@link TaskForm} (`run`) and per-phase `concurrency` come from the
2785
- * same definition the tree is constructed from, so the executed tree can never drift from
2786
- * the form/throttle metadata). The {@link WorkflowOptions} part of `options` (initial `on`
2787
- * listeners, a `bail` override, the per-node `phases` bag) is forwarded to the build.
3346
+ * truth — the per-task `run` (resolved into its {@link TaskInterface.handler}) and per-phase
3347
+ * `concurrency` come from the same definition the tree is constructed from, so the executed
3348
+ * tree can never drift from the metadata). The {@link WorkflowOptions} part of `options`
3349
+ * (initial `on` listeners, a `bail` override, the per-node `phases` bag, the `functions`
3350
+ * registry) is forwarded to the build.
2788
3351
  * Under `bail: false` (graceful) every task settles (a failure is recorded on its
2789
3352
  * {@link TaskInterface}) and the workflow reaches `completed`; under `bail: true` (halt)
2790
3353
  * the first failure aborts the in-flight sibling tasks AND `skip`s the remaining tasks /
@@ -2796,33 +3359,73 @@ export declare interface WorkflowRunnerInterface {
2796
3359
  * the run as `stopped` — the cancel supersedes the same-tick failure, and that task's error
2797
3360
  * is not recorded.
2798
3361
  *
3362
+ * **Programmer-error exception (AGENTS §12).** A PATHOLOGICAL `definition` (e.g. a
3363
+ * duplicate phase or task `id`) THROWS SYNCHRONOUSLY at construction — before any phase
3364
+ * runs, and before the returned `Promise` is even created — rather than resolving a
3365
+ * failed/partial {@link WorkflowResult}. This is the one exception to the "resolves,
3366
+ * never rejects" contract above: a malformed definition is a programmer-timing error, not
3367
+ * a runtime outcome to report through the result tree.
3368
+ *
2799
3369
  * @param definition - The {@link WorkflowDefinition} to build the live tree from and drive
2800
3370
  * @param options - The construction options ({@link WorkflowOptions}: `on` / `bail` /
2801
3371
  * `phases`) PLUS the per-run bounds (`signal` / `timeout` / `budget`)
2802
3372
  * @returns The run's terminal {@link WorkflowResult} (its `workflow` is the built tree)
2803
3373
  */
2804
3374
  execute(definition: WorkflowDefinition, options?: WorkflowRunOptions): Promise<WorkflowResult>;
3375
+ /**
3376
+ * Drive an ALREADY-BUILT, CALLER-OWNED live {@link WorkflowInterface} — the
3377
+ * ENTITY-NATIVE counterpart to the definition-building {@link execute} overload.
3378
+ *
3379
+ * @remarks
3380
+ * The entity itself is now the single control surface (no separate run handle):
3381
+ * `createWorkflow` mints the live tree, this overload drives it, and the caller
3382
+ * controls the SAME entity mid-run via its own `pause` / `resume` / `add` / `stop` /
3383
+ * `destroy` (AGENTS §10). Requires `workflow.status === 'pending'` and
3384
+ * `!workflow.destroyed` — otherwise this is a programmer-timing error and it THROWS a
3385
+ * `TRANSITION` {@link import('./errors.js').WorkflowError} (AGENTS §12) rather than
3386
+ * silently no-opping or building a second tree. Once accepted, phases run
3387
+ * SEQUENTIALLY and, within each phase, tasks CONCURRENTLY — byte-identical observable
3388
+ * semantics to the `definition`-form `execute` — except the phase loop RE-READS the
3389
+ * live `workflow.phases` / each phase's live `tasks` every iteration (a cursor over
3390
+ * the live managers, not a one-time snapshot), so a caller's live `add` mid-run is
3391
+ * picked up and actually dispatched. `workflow.pause()` gates the run at the next
3392
+ * phase boundary AND before each task's dispatch (an in-flight task body is never
3393
+ * suspended); `workflow.stop()` skips not-yet-started work gracefully; `workflow.destroy()`
3394
+ * folds `workflow.signal` into the run's cancellation, aborting in-flight work
3395
+ * immediately. `options` carries only the per-run BOUNDS (`signal` / `timeout` /
3396
+ * `budget`) — the construction half of {@link WorkflowRunOptions}
3397
+ * does not apply, since the tree already exists.
3398
+ *
3399
+ * **Run round-trips through the snapshot.** Driving a tree rebuilt by
3400
+ * {@link import('./factories.js').restoreWorkflow} behaves according to whether a
3401
+ * {@link WorkflowFunctions} registry was supplied at that build: WITH a registry,
3402
+ * each task's `run` name is re-resolved against it, so a matched task carries a real
3403
+ * handler and this overload actually DISPATCHES it, resuming real work. WITHOUT a
3404
+ * registry (or when a task's `run` name has no match in it), the task's
3405
+ * {@link TaskInterface.run} is `undefined` — the no-handler rule then AUTO-COMPLETES
3406
+ * that task (no dispatch occurs). A PARTIALLY-run restored tree (any live phase/task
3407
+ * not `pending`) is rejected outright by the `workflow.status === 'pending'` guard
3408
+ * above — only a wholly `pending` restored tree is drivable.
3409
+ *
3410
+ * @param workflow - The live {@link WorkflowInterface} to drive (its own entity surface —
3411
+ * `pause` / `resume` / `add` / `stop` / `destroy` — is the caller's control seam)
3412
+ * @param options - The per-run bounds (`signal` / `timeout` / `budget`); the construction
3413
+ * half of {@link WorkflowRunOptions} does not apply (the tree already exists)
3414
+ * @returns The run's terminal {@link WorkflowResult} (its `workflow` is the SAME entity passed in)
3415
+ */
3416
+ execute(workflow: WorkflowInterface, options?: Omit<WorkflowRunOptions, keyof WorkflowOptions>): Promise<WorkflowResult>;
2805
3417
  }
2806
3418
 
2807
3419
  /**
2808
- * The options for `createWorkflowRunner` — the behavior registries the runner dispatches
2809
- * a task BY NAME through, plus the optional pacing scheduler.
2810
- *
2811
- * @remarks
2812
- * - `functions`the {@link WorkflowFunctions} registry for `function`-form tasks. A name
2813
- * absent here is the no-handler case (the task auto-completes). Omitted an empty
2814
- * registry (every `function` task auto-completes).
2815
- * - `tools` the {@link ToolManagerInterface} (the shipped tool registry) a `tool`-form
2816
- * task is dispatched through: the runner resolves `run.name` via `tools.tool(name)` and
2817
- * invokes it with the task's input. An unregistered tool name is the no-handler case
2818
- * (auto-completes). Omitted ⇒ every `tool` task auto-completes.
2819
- * - `agents` — the {@link WorkflowAgents} resolver (W-c2) an `agent`-form task is dispatched
2820
- * through: the runner resolves `run.name` to a live
2821
- * {@link AgentInterface}, BINDS a depth/cycle-aware workflow
2822
- * tool onto its `context.tools` (the propagation seam), folds the task's cancellation into
2823
- * the agent run, and drives it (success → `complete`, throw → `fail`), all behind the
2824
- * depth + cycle guard. An unregistered agent name is the no-handler case (auto-completes).
2825
- * Omitted ⇒ every `agent` task auto-completes.
3420
+ * The options for `createWorkflowRunner` — the optional pacing scheduler the runner
3421
+ * paces phase boundaries with.
3422
+ *
3423
+ * @remarks
3424
+ * The runner is a PURE engine it carries no `functions` / `tools` / `agents` registry
3425
+ * (each live task already resolved its own handler at construction from
3426
+ * {@link WorkflowOptions.functions}); wiring a `function`-form task to a tool or an agent is
3427
+ * an OPT-IN concern of the `@orkestrel/tool` package's adapter factories, which a caller
3428
+ * composes into its OWN `functions` registry.
2826
3429
  * - `scheduler` — the {@link SchedulerInterface} that paces the tree (a cooperative
2827
3430
  * `yield` between phases). Omitted ⇒ the shipped cross-environment default
2828
3431
  * ({@link createScheduler}).
@@ -2833,10 +3436,6 @@ export declare interface WorkflowRunnerInterface {
2833
3436
  * wire. A future runner-level emitter would introduce its own `EmitterHooks` here.
2834
3437
  */
2835
3438
  export declare interface WorkflowRunnerOptions {
2836
- readonly functions?: WorkflowFunctions;
2837
- readonly tools?: ToolManagerInterface;
2838
- /** The {@link WorkflowAgents} resolver for `agent`-form tasks (W-c2); omitted ⇒ every `agent` task auto-completes. */
2839
- readonly agents?: WorkflowAgents;
2840
3439
  readonly scheduler?: SchedulerInterface;
2841
3440
  }
2842
3441
 
@@ -2869,28 +3468,15 @@ export declare interface WorkflowRunnerOptions {
2869
3468
  * `signal` and `start`s it. (A `max: 0` budget is exhausted from its first `start`, so it
2870
3469
  * cancels the run at entry — a DIFFERENT primitive from the `timeout: 0` "no deadline" case.)
2871
3470
  *
2872
- * The last two are the W-c2 DEPTH / CYCLE bookkeeping — the run's position in a nested
2873
- * workflow→agent→workflow chain, threaded across the
2874
- * `execute agent-task createWorkflowTool nested execute` boundary so the guard can
2875
- * bound recursion. A TOP-LEVEL `execute` omits both (depth `0`, empty ancestry); only the
2876
- * runner-bound workflow tool ({@link import('./factories.js').createWorkflowTool}) supplies
2877
- * them, incrementing the depth and extending the ancestry for each nested run.
2878
- * - `depth` — the run's nesting depth (default `0`). An `agent` task is REJECTED (a typed
2879
- * `DEPTH` `task.fail`) when running it would push the chain past
2880
- * {@link import('./constants.js').MAX_WORKFLOW_DEPTH}.
2881
- * - `ancestry` — the identifiers of the workflows + agents already in this run chain
2882
- * (e.g. `workflow:<id>` / `agent:<name>`). An `agent` task whose target agent — or the
2883
- * workflow it would author — is already present is a CYCLE and is likewise rejected.
2884
- * Default empty.
3471
+ * The engine itself carries NO nesting bookkeeping — the depth / cycle guard for a nested
3472
+ * `agent` workflow-tool → workflow chain lives entirely in the OPT-IN adapter factories
3473
+ * shipped by `@orkestrel/tool`, closed over their own `depth` / `ancestry`, never threaded
3474
+ * through `execute`'s options.
2885
3475
  */
2886
3476
  export declare type WorkflowRunOptions = WorkflowOptions & {
2887
3477
  readonly signal?: AbortSignal;
2888
3478
  readonly timeout?: number;
2889
3479
  readonly budget?: BudgetInterface<TokenUsage>;
2890
- /** The run's nesting depth (W-c2); default `0`. Threaded by the bound workflow tool, never by a top-level caller. */
2891
- readonly depth?: number;
2892
- /** The workflow / agent identifiers already in this run chain (W-c2 cycle guard); default empty. */
2893
- readonly ancestry?: readonly string[];
2894
3480
  };
2895
3481
 
2896
3482
  /**
@@ -2911,16 +3497,7 @@ export declare const workflowShape: ObjectShape<{
2911
3497
  id: StringShape;
2912
3498
  name: StringShape;
2913
3499
  description: OptionalShape<StringShape>;
2914
- run: UnionShape<[ ObjectShape<{
2915
- via: LiteralShape<readonly ["function"]>;
2916
- name: StringShape;
2917
- }>, ObjectShape<{
2918
- via: LiteralShape<readonly ["tool"]>;
2919
- name: StringShape;
2920
- }>, ObjectShape<{
2921
- via: LiteralShape<readonly ["agent"]>;
2922
- name: StringShape;
2923
- }>]>;
3500
+ run: OptionalShape<StringShape>;
2924
3501
  retries: OptionalShape<NumberShape>;
2925
3502
  timeout: OptionalShape<NumberShape>;
2926
3503
  }>>;
@@ -2993,57 +3570,6 @@ export declare interface WorkflowSnapshotRow {
2993
3570
  */
2994
3571
  export declare type WorkflowStatus = LifecycleStatus;
2995
3572
 
2996
- /**
2997
- * One flat step — `{ name, via? }` — the building block of a {@link WorkflowSteps} blob.
2998
- *
2999
- * @remarks
3000
- * `name` is the REGISTERED behavior name the step runs (it becomes the task's `run.name`,
3001
- * NOT a human label). `via` is the optional execution mechanism — `'function'` (the
3002
- * default when omitted), `'tool'`, or `'agent'`.
3003
- */
3004
- export declare interface WorkflowStep {
3005
- /** The registered behavior name this step runs (becomes the task's `run.name`). */
3006
- readonly name: string;
3007
- /** How to run it — `'function'` (default), `'tool'`, or `'agent'`. */
3008
- readonly via?: TaskVia;
3009
- }
3010
-
3011
- /**
3012
- * The FLAT authoring blob `createWorkflowTool` advertises — `{ name?, steps }` — the
3013
- * simplest surface a small model can fill.
3014
- *
3015
- * @remarks
3016
- * Each {@link WorkflowStep} becomes a one-task phase, in order
3017
- * ({@link import('./helpers.js').expandSteps}); `name` is the optional workflow name
3018
- * (defaulted when omitted). The expanded tree is validated against the STRICT
3019
- * {@link import('./factories.js').createWorkflowContract} gate before running.
3020
- */
3021
- export declare interface WorkflowSteps {
3022
- readonly name?: string;
3023
- readonly steps: readonly WorkflowStep[];
3024
- }
3025
-
3026
- /**
3027
- * The FLAT authoring shape `createWorkflowTool` advertises as its `parameters` — the
3028
- * simplest surface a small model can fill: `{ name?, steps: [{ name, via? }] }`.
3029
- *
3030
- * @remarks
3031
- * The deliberately-reduced surface (AGENTS §21): a flat ordered list of steps, each a
3032
- * `{ name, via? }`. The tool EXPANDS it ({@link import('./helpers.js').expandSteps}) into a
3033
- * full {@link import('./types.js').WorkflowDefinition} — one one-task phase per step, in
3034
- * order — then validates against the STRICT
3035
- * {@link import('./factories.js').createWorkflowContract} gate. The full nested form is
3036
- * STILL accepted by the tool (it branches on the args' shape) and is documented as the
3037
- * advanced escape-hatch in the tool's description — but THIS is what `parameters` advertises.
3038
- */
3039
- export declare const workflowStepsShape: ObjectShape<{
3040
- name: OptionalShape<StringShape>;
3041
- steps: ArrayShape<ObjectShape<{
3042
- name: StringShape;
3043
- via: OptionalShape<LiteralShape<readonly ["function", "tool", "agent"]>>;
3044
- }>>;
3045
- }>;
3046
-
3047
3573
  /**
3048
3574
  * The durable persistence seam for a {@link WorkflowSnapshot} — three async primitives
3049
3575
  * (`get` / `set` / `delete`) keyed by a workflow id, the snapshot analogue of
@@ -3091,89 +3617,4 @@ export declare interface WorkflowStoreInterface {
3091
3617
  delete(id: string): Promise<void>;
3092
3618
  }
3093
3619
 
3094
- /**
3095
- * The ancestry identifier of a workflow run — `workflow:<id>`.
3096
- *
3097
- * @remarks
3098
- * The {@link import('./WorkflowRunner.js').WorkflowRunner}'s cycle guard records one of
3099
- * these per workflow in the current nested run chain (carried on
3100
- * {@link import('./types.js').WorkflowRunOptions.ancestry}). Tagging the bare id keeps a
3101
- * workflow id and an {@link agentTag} agent name in ONE namespaced set without collision,
3102
- * so re-entering a workflow OR an agent already in the chain is a single `includes` check.
3103
- *
3104
- * @param id - The workflow definition's `id`
3105
- * @returns The namespaced ancestry tag (`workflow:<id>`)
3106
- */
3107
- export declare function workflowTag(id: string): string;
3108
-
3109
- /**
3110
- * The workflow-tool binder — the signature of {@link import('./factories.js').createWorkflowTool},
3111
- * threaded into the {@link WorkflowRunnerInterface} at construction (W-c2) so the runner can BIND a
3112
- * depth/cycle-aware workflow tool onto a dispatched subagent.
3113
- *
3114
- * @remarks
3115
- * The runner receives a REFERENCE to `createWorkflowTool` rather than importing it, because
3116
- * `createWorkflowTool` lives in `factories.ts` which imports the runner CLASS (the
3117
- * factories→classes direction); importing the factory back into the class would be a cycle. The
3118
- * factory (which legitimately owns `createWorkflowContract`) is passed as a value at construction,
3119
- * and the runner invokes it with `this` as the `runner` argument — keeping the cycle-free seam an
3120
- * explicit, typed contract rather than a hidden dependency.
3121
- *
3122
- * @param definition - The workflow the bound tool runs when called with no authored args
3123
- * @param runner - The runner that executes the (nested) workflow (the runner passes `this`)
3124
- * @param options - The depth + ancestry the nested workflow runs under (see {@link WorkflowToolOptions})
3125
- * @returns The bound {@link ToolInterface}
3126
- */
3127
- export declare type WorkflowToolBinder = (definition: WorkflowDefinition, runner: WorkflowRunnerInterface, options?: WorkflowToolOptions) => ToolInterface;
3128
-
3129
- /**
3130
- * Options for {@link import('./factories.js').createWorkflowTool} — the depth + ancestry the
3131
- * wrapped {@link WorkflowDefinition} runs the NESTED workflow at when an LLM invokes the tool.
3132
- *
3133
- * @remarks
3134
- * This is the PROPAGATION carrier across the agent/tool boundary. A `Tool`'s handler receives
3135
- * ONLY the model-supplied `args` (no ambient context, no signal — see
3136
- * the `@orkestrel/agent` package's `ToolOptions`), so the run's position in the
3137
- * workflow→agent→workflow chain CANNOT be threaded through a tool call at runtime. Instead the
3138
- * runner CLOSES it over the tool at BIND time: when it dispatches an `agent` task at depth `D`
3139
- * with ancestry `A`, it builds the agent's workflow tool with `{ depth: D, ancestry: A }`, so
3140
- * the handler's closure carries them. On invocation the handler runs the nested workflow at
3141
- * `depth: D + 1` with `ancestry: A ∪ { workflow:<id> }` — bounded by
3142
- * {@link import('./constants.js').MAX_WORKFLOW_DEPTH} (an over-deep / cyclic nested run THROWS a
3143
- * typed `DEPTH` {@link import('./errors.js').WorkflowError}, which the `ToolManager` isolates into
3144
- * the tool result's top-level `error`, AGENTS §14).
3145
- *
3146
- * Both fields are OPTIONAL: a workflow tool built for a TOP-LEVEL caller (not by the runner's
3147
- * agent-task binding) omits them — its nested run starts the chain at depth `1` with the bare
3148
- * `workflow:<id>` ancestry.
3149
- */
3150
- export declare interface WorkflowToolOptions {
3151
- /** The depth the INVOKING agent runs at; the nested workflow runs at `depth + 1`. Default `0`. */
3152
- readonly depth?: number;
3153
- /** The ancestry of the invoking run; the nested run extends it with its own `workflow:<id>`. Default empty. */
3154
- readonly ancestry?: readonly string[];
3155
- }
3156
-
3157
- /**
3158
- * Summarize a terminal {@link WorkflowResult} into the PLAIN value a
3159
- * {@link import('./factories.js').createWorkflowTool} handler returns on success.
3160
- *
3161
- * @remarks
3162
- * This is the run summary the handler returns DIRECTLY — NOT a `ToolResult` (the future `@orkestrel/agent` package).
3163
- * The handler conforms to the universal tool-handler contract (AGENTS §14): it returns the plain value
3164
- * (and throws on failure), so the `@orkestrel/agent` package's `ToolManager` performs
3165
- * the ONE canonical wrap (`{ id, name, value }`) and the model reads exactly this summary — once,
3166
- * identically — over BOTH the agent loop and MCP. The summary is LEAN: the workflow's terminal `status`
3167
- * and the COUNT of settled task results — enough for a caller / model to react without serializing the
3168
- * whole live tree. (It carries no synthetic `id` / `name`: a tool handler has no call id; the manager
3169
- * supplies the canonical envelope's identity.)
3170
- *
3171
- * @param result - The terminal {@link WorkflowResult} the run produced
3172
- * @returns The plain success summary — `{ status, count }`
3173
- */
3174
- export declare function workflowToolSummary(result: WorkflowResult): Readonly<{
3175
- status: WorkflowStatus;
3176
- count: number;
3177
- }>;
3178
-
3179
3620
  export { }