@orkestrel/workflow 0.0.1 → 0.0.2
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.
- package/README.md +16 -5
- package/dist/src/core/index.cjs +1203 -418
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +1461 -420
- package/dist/src/core/index.d.ts +1461 -420
- package/dist/src/core/index.js +1194 -414
- package/dist/src/core/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -7,17 +7,42 @@ import { DriverInterface } from '@orkestrel/database';
|
|
|
7
7
|
import { EmitterErrorHandler } from '@orkestrel/emitter';
|
|
8
8
|
import { EmitterHooks } from '@orkestrel/emitter';
|
|
9
9
|
import { EmitterInterface } from '@orkestrel/emitter';
|
|
10
|
+
import { Failure } from '@orkestrel/contract';
|
|
10
11
|
import { LiteralShape } from '@orkestrel/contract';
|
|
11
12
|
import { NumberShape } from '@orkestrel/contract';
|
|
12
13
|
import { ObjectShape } from '@orkestrel/contract';
|
|
13
14
|
import { OptionalShape } from '@orkestrel/contract';
|
|
14
15
|
import { Result } from '@orkestrel/contract';
|
|
15
16
|
import { StringShape } from '@orkestrel/contract';
|
|
17
|
+
import { Success } from '@orkestrel/contract';
|
|
16
18
|
import { TableInterface } from '@orkestrel/database';
|
|
17
19
|
import { TokenUsage } from '@orkestrel/budget';
|
|
18
20
|
import { ToolInterface } from '@orkestrel/agent';
|
|
19
21
|
import { ToolManagerInterface } from '@orkestrel/agent';
|
|
20
|
-
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Options for {@link import('./factories.js').createAgentFunction} — the OPT-IN adapter that
|
|
25
|
+
* wraps a live `AgentInterface` (`@orkestrel/agent`) as a {@link WorkflowFunction}, folding a
|
|
26
|
+
* nested workflow-authoring depth / cycle guard into its closure.
|
|
27
|
+
*
|
|
28
|
+
* @remarks
|
|
29
|
+
* All fields are optional: omitted entirely, the adapter runs the agent with no nested
|
|
30
|
+
* workflow tool bound and no depth/cycle bound (depth `0`, empty ancestry).
|
|
31
|
+
* - `runner` — when supplied, the adapter BINDS a depth/cycle-aware
|
|
32
|
+
* {@link import('./factories.js').createWorkflowTool} onto the agent's `context.tools` (the
|
|
33
|
+
* propagation seam), so the agent can author + run a NESTED workflow through it. Omitted ⇒
|
|
34
|
+
* the agent runs with no workflow tool bound.
|
|
35
|
+
* - `depth` — this invocation's nesting depth (default `0`); the bound workflow tool runs its
|
|
36
|
+
* nested workflow at `depth + 1`, bounded by {@link import('./constants.js').MAX_WORKFLOW_DEPTH}.
|
|
37
|
+
* - `ancestry` — the workflow / agent identifiers already in this run chain (default empty); a
|
|
38
|
+
* cycle (this agent already present) is rejected with a typed `DEPTH`
|
|
39
|
+
* {@link import('./errors.js').WorkflowError}.
|
|
40
|
+
*/
|
|
41
|
+
export declare interface AgentFunctionOptions {
|
|
42
|
+
readonly runner?: WorkflowRunnerInterface;
|
|
43
|
+
readonly depth?: number;
|
|
44
|
+
readonly ancestry?: readonly string[];
|
|
45
|
+
}
|
|
21
46
|
|
|
22
47
|
/**
|
|
23
48
|
* The ancestry identifier of an agent in a run chain — `agent:<name>`.
|
|
@@ -43,10 +68,13 @@ export declare function agentTag(name: string): string;
|
|
|
43
68
|
* untrusted JSON, so a status (or an override) outside
|
|
44
69
|
* {@link import('./constants.js').WORKFLOW_STATUSES} /
|
|
45
70
|
* {@link import('./constants.js').PHASE_STATUSES} / {@link import('./constants.js').TASK_STATUSES},
|
|
46
|
-
*
|
|
71
|
+
* a non-boolean `bail` (the workflow's OR any phase's — both are REQUIRED persisted policy), a
|
|
72
|
+
* present-but-invalid phase `concurrency` (not a positive integer), or a present-but-invalid task
|
|
73
|
+
* `run` (an empty string) / `retries` / `timeout` (not a non-negative integer),
|
|
47
74
|
* is rejected loudly (naming the offending node) rather than silently producing a broken tree.
|
|
48
|
-
* The `override`
|
|
49
|
-
* fields is the contract's concern; this
|
|
75
|
+
* The `override` / `concurrency` / `run` / `retries` / `timeout` are optional, so each is only
|
|
76
|
+
* checked WHEN present. Structural shape beyond these fields is the contract's concern; this
|
|
77
|
+
* guards exactly the fields the live state machine reads back.
|
|
50
78
|
*
|
|
51
79
|
* @param snapshot - The snapshot to validate
|
|
52
80
|
*/
|
|
@@ -262,6 +290,48 @@ export declare interface ControllerInterface<TInput, TResult> {
|
|
|
262
290
|
abort(reason?: unknown): void;
|
|
263
291
|
}
|
|
264
292
|
|
|
293
|
+
/**
|
|
294
|
+
* Wrap a live `AgentInterface` (`@orkestrel/agent`) as a {@link WorkflowFunction} — the OPT-IN
|
|
295
|
+
* adapter that runs the agent to a settled result, folding a nested workflow-authoring
|
|
296
|
+
* depth / cycle guard into its own closure.
|
|
297
|
+
*
|
|
298
|
+
* @remarks
|
|
299
|
+
* Composes into a caller's {@link WorkflowOptions.functions} registry like any other behavior;
|
|
300
|
+
* the PURE {@link import('./WorkflowRunner.js').WorkflowRunner} has no knowledge of agents
|
|
301
|
+
* itself. Before running the agent, the depth/cycle guard REJECTS the call (a THROWN typed
|
|
302
|
+
* `DEPTH` {@link WorkflowError}, which the leaf `fail`s) when running it would push a nested
|
|
303
|
+
* chain past {@link MAX_WORKFLOW_DEPTH}, OR when this agent is already an ancestor (a cycle) —
|
|
304
|
+
* ported from the former engine-side guard. When {@link AgentFunctionOptions.runner} is
|
|
305
|
+
* supplied, the adapter BINDS a depth/cycle-aware {@link createWorkflowTool} onto the agent's
|
|
306
|
+
* `context.tools` (the propagation seam) — closed over `depth` and the extended ancestry (the
|
|
307
|
+
* tool itself computes `depth + 1` internally) — so the agent can author + run a NESTED
|
|
308
|
+
* workflow through it; the wrapped default is the CURRENT task's own workflow id (used only on
|
|
309
|
+
* a no-args tool call). The task's cancellation folds into the agent run: an already-aborted
|
|
310
|
+
* `controller.signal` cancels the agent up front; otherwise a one-shot listener fires
|
|
311
|
+
* `agent.abort(reason)` when the task cancels, removed in `finally`. `agent.generate()` resolves
|
|
312
|
+
* a partial `AgentResult` on a cancel (never rejects), returned as the task's completed value.
|
|
313
|
+
*
|
|
314
|
+
* A bound agent is effectively SINGLE-RUN: `context.tools.add` binds one {@link ToolInterface}
|
|
315
|
+
* under the fixed {@link import('./constants.js').WORKFLOW_TOOL_NAME}, and `agent.generate()` /
|
|
316
|
+
* `agent.abort()` are per-agent state. Two CONCURRENT tasks sharing the SAME `agent` instance
|
|
317
|
+
* race on that one tool binding (last-write-wins) and on generate/abort — give each concurrent
|
|
318
|
+
* task its OWN agent instance.
|
|
319
|
+
*
|
|
320
|
+
* @param agent - The live `AgentInterface` to run
|
|
321
|
+
* @param options - The nested-workflow binding + depth/cycle bookkeeping (see {@link AgentFunctionOptions})
|
|
322
|
+
* @returns A {@link WorkflowFunction} that runs `agent` to its settled result
|
|
323
|
+
*
|
|
324
|
+
* @example
|
|
325
|
+
* ```ts
|
|
326
|
+
* import { createAgentFunction, createWorkflowRunner } from '@src/core'
|
|
327
|
+
*
|
|
328
|
+
* const runner = createWorkflowRunner()
|
|
329
|
+
* const review = createAgentFunction(myAgent, { runner })
|
|
330
|
+
* await runner.execute(definition, { functions: { review } })
|
|
331
|
+
* ```
|
|
332
|
+
*/
|
|
333
|
+
export declare function createAgentFunction(agent: AgentInterface, options?: AgentFunctionOptions): WorkflowFunction;
|
|
334
|
+
|
|
265
335
|
/**
|
|
266
336
|
* Create a {@link DatabaseWorkflowStore} over any {@link DriverInterface} — the durable,
|
|
267
337
|
* driver-pluggable backing for the W-d persistence seam, the opt-in twin of
|
|
@@ -425,6 +495,38 @@ export declare function createRunner<TInput, TResult>(options: RunnerOptions<TIn
|
|
|
425
495
|
*/
|
|
426
496
|
export declare function createScheduler(): SchedulerInterface;
|
|
427
497
|
|
|
498
|
+
/**
|
|
499
|
+
* Wrap a registered tool as a {@link WorkflowFunction} — the OPT-IN adapter that lets a
|
|
500
|
+
* `function`-form task run a `@orkestrel/agent` tool BY NAME.
|
|
501
|
+
*
|
|
502
|
+
* @remarks
|
|
503
|
+
* Composes into a caller's {@link WorkflowOptions.functions} registry like any other behavior
|
|
504
|
+
* (`{ publish: createToolFunction(tools, 'publish') }`); the PURE
|
|
505
|
+
* {@link import('./WorkflowRunner.js').WorkflowRunner} has no knowledge of tools itself. The
|
|
506
|
+
* returned function executes `name` against `tools` with the task's `controller.input` as the
|
|
507
|
+
* call arguments, id-correlated to the task's own id. A `ToolManagerInterface.execute` NEVER
|
|
508
|
+
* throws (a handler throw is isolated into `result.error`), so a failing tool is surfaced here
|
|
509
|
+
* as a THROWN `Error` carrying the original message as `cause` — the leaf `fail`s, honouring
|
|
510
|
+
* `bail`. An UNREGISTERED tool name is a programmer error (an explicit binding to a name that
|
|
511
|
+
* doesn't exist) — unlike the engine's own silent auto-complete of an unresolved task handler,
|
|
512
|
+
* this THROWS a typed `TOOL` {@link WorkflowError}.
|
|
513
|
+
*
|
|
514
|
+
* @param tools - The {@link ToolManagerInterface} the named tool is registered on
|
|
515
|
+
* @param name - The registered tool's name
|
|
516
|
+
* @returns A {@link WorkflowFunction} that runs the named tool
|
|
517
|
+
*
|
|
518
|
+
* @example
|
|
519
|
+
* ```ts
|
|
520
|
+
* import { createToolFunction, createToolManager, createWorkflowRunner } from '@src/core'
|
|
521
|
+
*
|
|
522
|
+
* const tools = createToolManager()
|
|
523
|
+
* tools.add(myPublishTool)
|
|
524
|
+
* const runner = createWorkflowRunner()
|
|
525
|
+
* await runner.execute(definition, { functions: { publish: createToolFunction(tools, 'publish') } })
|
|
526
|
+
* ```
|
|
527
|
+
*/
|
|
528
|
+
export declare function createToolFunction(tools: ToolManagerInterface, name: string): WorkflowFunction;
|
|
529
|
+
|
|
428
530
|
/**
|
|
429
531
|
* Build the live W-b entity tree from a {@link WorkflowDefinition} — the whole
|
|
430
532
|
* {@link WorkflowInterface} → {@link import('./types.js').PhaseInterface} →
|
|
@@ -441,6 +543,11 @@ export declare function createScheduler(): SchedulerInterface;
|
|
|
441
543
|
* `options.phases[id].tasks[id]` (the AGENTS §8 nested-by-id bag). The W-b tree is the
|
|
442
544
|
* state machine ONLY — it does not execute tasks (W-c drives the transitions).
|
|
443
545
|
*
|
|
546
|
+
* `options.functions` is the {@link import('./types.js').WorkflowFunctions} registry each live
|
|
547
|
+
* task's `run` name resolves against ONCE at construction into its runtime
|
|
548
|
+
* {@link import('./types.js').TaskInterface.handler} — a name omitted or absent from the
|
|
549
|
+
* registry resolves to no handler (the no-handler rule).
|
|
550
|
+
*
|
|
444
551
|
* @param definition - The workflow definition to bring to life
|
|
445
552
|
* @param options - Runtime options (initial listeners, `bail` override, per-node options)
|
|
446
553
|
* @returns The live {@link WorkflowInterface} root
|
|
@@ -516,50 +623,45 @@ export declare function createWorkflowDraftContract(): ContractInterface<Workflo
|
|
|
516
623
|
/**
|
|
517
624
|
* Create a workflow runner — a {@link WorkflowRunnerInterface} that EXECUTES a live W-b
|
|
518
625
|
* workflow tree by COMPOSING the shipped substrate: phases sequential, tasks concurrent,
|
|
519
|
-
* each task dispatched
|
|
520
|
-
*
|
|
521
|
-
* @remarks
|
|
522
|
-
* The runner is
|
|
523
|
-
*
|
|
524
|
-
*
|
|
525
|
-
*
|
|
526
|
-
*
|
|
527
|
-
*
|
|
528
|
-
*
|
|
529
|
-
*
|
|
530
|
-
*
|
|
626
|
+
* each task dispatched through its OWN resolved handler under the workflow's `bail` policy.
|
|
627
|
+
*
|
|
628
|
+
* @remarks
|
|
629
|
+
* The runner is a PURE engine — it re-implements no concurrency / retry / abort logic, AND it
|
|
630
|
+
* carries no `functions` / `tools` / `agents` registry of its own: each live task already
|
|
631
|
+
* resolved its own {@link import('./types.js').WorkflowFunction} into
|
|
632
|
+
* {@link import('./types.js').TaskInterface.handler} ONCE at construction, from the
|
|
633
|
+
* {@link WorkflowOptions.functions} registry supplied to `execute` / {@link createWorkflow}.
|
|
634
|
+
* Per-phase bounded concurrency is one {@link createRunner} per phase; `bail` maps onto that
|
|
635
|
+
* Runner's fail-fast (`true` — the first failure aborts the in-flight siblings + skips the
|
|
636
|
+
* rest) vs settle-all (`false` — failures are recorded, the run finishes); the run-level abort
|
|
637
|
+
* / timeout / budget ({@link import('./types.js').WorkflowRunOptions}) fold through
|
|
638
|
+
* `AbortSignal.any` (the agent runtime's pattern); pacing is the shipped scheduler.
|
|
639
|
+
* `execute(definition, options?)` BUILDS the live tree from the definition itself (via
|
|
640
|
+
* {@link createWorkflow} — one source of truth, returned in `WorkflowResult.workflow`), drives
|
|
641
|
+
* the live entity (`start` → `complete` / `fail`), and resolves a
|
|
531
642
|
* {@link import('./types.js').WorkflowResult}.
|
|
532
643
|
*
|
|
533
|
-
*
|
|
534
|
-
*
|
|
535
|
-
*
|
|
536
|
-
*
|
|
537
|
-
* (the ROADMAP no-handler rule).
|
|
538
|
-
*
|
|
539
|
-
*
|
|
540
|
-
*
|
|
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}.
|
|
644
|
+
* Static tool / agent calling is OPT-IN, wired through the adapter factories
|
|
645
|
+
* {@link createToolFunction} / {@link createAgentFunction} — plain
|
|
646
|
+
* {@link import('./types.js').WorkflowFunction}s a caller composes into its OWN
|
|
647
|
+
* {@link WorkflowOptions.functions} registry, same as any other behavior. A task with no
|
|
648
|
+
* resolved handler AUTO-COMPLETES (the ROADMAP no-handler rule).
|
|
649
|
+
*
|
|
650
|
+
* @param options - An optional pacing `scheduler` (default the shipped cross-environment one).
|
|
651
|
+
* See {@link WorkflowRunnerOptions}.
|
|
548
652
|
* @returns A working {@link WorkflowRunnerInterface}
|
|
549
653
|
*
|
|
550
654
|
* @example
|
|
551
655
|
* ```ts
|
|
552
|
-
* import { createWorkflowRunner
|
|
656
|
+
* import { createWorkflowRunner } from '@src/core'
|
|
553
657
|
*
|
|
554
|
-
* const
|
|
555
|
-
* const runner = createWorkflowRunner({
|
|
556
|
-
* functions: { compile: async (controller) => `built ${controller.task.id}` },
|
|
557
|
-
* tools,
|
|
558
|
-
* })
|
|
658
|
+
* const runner = createWorkflowRunner()
|
|
559
659
|
* const definition = { id: 'w', name: 'W', phases: [{ id: 'p', name: 'P', tasks: [
|
|
560
|
-
* { id: 't', name: 'T', run:
|
|
660
|
+
* { id: 't', name: 'T', run: 'compile' },
|
|
561
661
|
* ] }] }
|
|
562
|
-
* const result = await runner.execute(definition
|
|
662
|
+
* const result = await runner.execute(definition, {
|
|
663
|
+
* functions: { compile: async (controller) => `built ${controller.task.id}` },
|
|
664
|
+
* })
|
|
563
665
|
* result.status // 'completed'
|
|
564
666
|
* result.workflow.phase('p')?.task('t')?.status // 'completed'
|
|
565
667
|
* ```
|
|
@@ -568,25 +670,26 @@ export declare function createWorkflowRunner(options?: WorkflowRunnerOptions): W
|
|
|
568
670
|
|
|
569
671
|
/**
|
|
570
672
|
* Wrap a {@link WorkflowDefinition} as an LLM-callable {@link ToolInterface} — it ADVERTISES
|
|
571
|
-
* the SIMPLE flat authoring shape (`{ name?, steps: [{ name
|
|
673
|
+
* the SIMPLE flat authoring shape (`{ name?, steps: [{ name }] }`) as its `parameters` so
|
|
572
674
|
* even a small model can author a complete tree, and its handler EXPANDS / COMPLETES the
|
|
573
675
|
* authored blob, validates it against the STRICT contract, runs it through `runner`, and
|
|
574
676
|
* returns the run SUMMARY (throwing a typed {@link WorkflowError} on failure).
|
|
575
677
|
*
|
|
576
678
|
* @remarks
|
|
577
679
|
* 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
|
|
579
|
-
* {@link
|
|
580
|
-
* ONLY the model-supplied `args` (no ambient context, no signal), the run's
|
|
581
|
-
* CLOSED OVER at bind time via {@link WorkflowToolOptions}, and the
|
|
582
|
-
*
|
|
680
|
+
* expose it for free — nothing MCP is wired here). It is ALSO the propagation carrier
|
|
681
|
+
* {@link createAgentFunction} binds onto a wrapped agent's `context.tools`: because a tool
|
|
682
|
+
* handler receives ONLY the model-supplied `args` (no ambient context, no signal), the run's
|
|
683
|
+
* depth + ancestry are CLOSED OVER at bind time via {@link WorkflowToolOptions}, and the
|
|
684
|
+
* handler enforces the SAME depth / cycle guard itself (this function owns it now — the engine
|
|
685
|
+
* carries none) before running the nested workflow at `depth + 1` with the extended ancestry.
|
|
583
686
|
*
|
|
584
687
|
* **Widened authoring surface (additive — the canonical contract + runner stay STRICT and
|
|
585
688
|
* 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,
|
|
587
|
-
*
|
|
588
|
-
*
|
|
589
|
-
* - the FLAT shape `{ name?, steps: [{ name
|
|
689
|
+
* nested {@link WorkflowDefinition} (six required `id`/`name` strings, an all-or-nothing tree).
|
|
690
|
+
* So the tool ACCEPTS three authoring forms and converges them on the SAME strict
|
|
691
|
+
* {@link createWorkflowContract} gate before running (soundness preserved):
|
|
692
|
+
* - the FLAT shape `{ name?, steps: [{ name }] }` — the ADVERTISED `parameters` (the simplest
|
|
590
693
|
* form, {@link import('./helpers.js').expandSteps}'d into one one-task phase per step);
|
|
591
694
|
* - a nested DRAFT with any `id`/`name` OMITTED — {@link createWorkflowDraftContract}-parsed then
|
|
592
695
|
* {@link import('./helpers.js').completeDraft}'d (missing ids synthesized positionally);
|
|
@@ -608,9 +711,15 @@ export declare function createWorkflowRunner(options?: WorkflowRunnerOptions): W
|
|
|
608
711
|
* gate (e.g. an explicit empty `id`, `concurrency: 0`) ⇒ THROW a `TOOL` {@link WorkflowError} (no run).
|
|
609
712
|
* - **Over-deep / cyclic** ⇒ THROW a `DEPTH` {@link WorkflowError} when the nested run would exceed
|
|
610
713
|
* {@link MAX_WORKFLOW_DEPTH}, or the target workflow id is already an ancestor (a cycle) — the
|
|
611
|
-
*
|
|
612
|
-
*
|
|
613
|
-
*
|
|
714
|
+
* SAME `code` {@link createAgentFunction}'s own guard raises. Enforced HERE, INSIDE this
|
|
715
|
+
* handler, before ever calling `runner.execute` — the engine itself performs no such check.
|
|
716
|
+
* - **Otherwise** ⇒ `runner.execute(target)`, RETURNING the plain summary of the terminal run
|
|
717
|
+
* (`{ status, count }`, via {@link workflowToolSummary}).
|
|
718
|
+
*
|
|
719
|
+
* The tool executes AUTHORED STRUCTURE, not consumer behavior: a nested tree authored through
|
|
720
|
+
* it (flat, draft, or full form) carries no {@link WorkflowFunctions} registry, so EVERY one of
|
|
721
|
+
* its tasks auto-completes under the no-handler rule. This handler validates and synthesizes
|
|
722
|
+
* shape — it never runs a caller's handlers.
|
|
614
723
|
*
|
|
615
724
|
* @param definition - The workflow the tool runs when called with no authored args
|
|
616
725
|
* @param runner - The {@link WorkflowRunnerInterface} that executes the (nested) workflow
|
|
@@ -702,17 +811,23 @@ export declare const DEFAULT_BAIL = false;
|
|
|
702
811
|
/**
|
|
703
812
|
* The default per-phase task concurrency the {@link import('./factories.js').createWorkflowRunner}
|
|
704
813
|
* runner applies when a {@link import('./types.js').PhaseDefinition} omits its `concurrency`
|
|
705
|
-
* throttle — a
|
|
814
|
+
* throttle — a cap that is effectively unbounded for any realistic phase.
|
|
706
815
|
*
|
|
707
816
|
* @remarks
|
|
708
817
|
* The determinism principle fixes that a phase's tasks run CONCURRENTLY; `concurrency` is
|
|
709
818
|
* 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
|
|
711
|
-
*
|
|
712
|
-
*
|
|
713
|
-
*
|
|
819
|
+
* all of a phase's tasks at once — modelled as this finite cap so the value flows straight
|
|
820
|
+
* into the substrate {@link import('./types.js').RunnerInterface}'s `concurrency` (which
|
|
821
|
+
* expects a positive integer) without a special unbounded branch. No realistic phase
|
|
822
|
+
* declares enough tasks to reach it, so it behaves as "run them all".
|
|
823
|
+
*
|
|
824
|
+
* WHY `1024` and not a huge sentinel like `1_000_000`: the backing `@orkestrel/queue` Runner
|
|
825
|
+
* EAGERLY spawns one parked worker loop per concurrency unit AT CONSTRUCTION, so this default
|
|
826
|
+
* must be a value whose eager allocation cost is negligible for every default-concurrency
|
|
827
|
+
* phase — a million-unit default meant ~1e6 promise/closure allocations per such phase. A
|
|
828
|
+
* phase may still DECLARE a larger explicit `concurrency` and pays that allocation knowingly.
|
|
714
829
|
*/
|
|
715
|
-
export declare const DEFAULT_PHASE_CONCURRENCY =
|
|
830
|
+
export declare const DEFAULT_PHASE_CONCURRENCY = 1024;
|
|
716
831
|
|
|
717
832
|
/**
|
|
718
833
|
* A promise paired with its externally-callable `resolve`/`reject` — the settle path
|
|
@@ -733,10 +848,12 @@ export declare interface DeferredInterface<T> {
|
|
|
733
848
|
*
|
|
734
849
|
* @remarks
|
|
735
850
|
* The structural fields (`id` / `name` / `description` + the ordered phases / tasks)
|
|
736
|
-
* carry over verbatim
|
|
737
|
-
*
|
|
738
|
-
* `
|
|
739
|
-
*
|
|
851
|
+
* carry over verbatim, as does each phase's `concurrency` (persisted on the
|
|
852
|
+
* {@link PhaseSnapshot} so a restore reinstates the same throttle) and each task's `run` /
|
|
853
|
+
* `retries` / `timeout` (persisted on the {@link TaskSnapshot}, like `bail` / `concurrency`,
|
|
854
|
+
* so a restore + a {@link import('./types.js').WorkflowOptions.functions} registry resumes
|
|
855
|
+
* real work). The `bail` policy carries over — at the
|
|
856
|
+
* workflow tier AND, per phase, the
|
|
740
857
|
* EFFECTIVE policy (`phase.bail ?? workflowBail`) on each {@link PhaseSnapshot} — so the seeded
|
|
741
858
|
* snapshot is self-contained; a fresh seed has no `override`. `created` / `updated` are stamped now.
|
|
742
859
|
* {@link import('./factories.js').createWorkflow} builds from this.
|
|
@@ -754,6 +871,34 @@ export declare interface DeferredInterface<T> {
|
|
|
754
871
|
*/
|
|
755
872
|
export declare function definitionToSnapshot(definition: WorkflowDefinition, bail?: boolean): WorkflowSnapshot;
|
|
756
873
|
|
|
874
|
+
/**
|
|
875
|
+
* Derive the PENDING SUFFIX boundary of a positional list of {@link LifecycleStatus}es —
|
|
876
|
+
* the index of the first entry in the contiguous trailing run of `pending` entries.
|
|
877
|
+
*
|
|
878
|
+
* @remarks
|
|
879
|
+
* The native, hook-free replacement for a runner-installed cursor (AGENTS §12): a
|
|
880
|
+
* {@link import('./types.js').WorkflowInterface}'s `add` / `remove` / `move` / `update`
|
|
881
|
+
* reads this over its live phases' statuses to decide which positions are safe to edit.
|
|
882
|
+
* Because entries run SEQUENTIALLY (phases sequential, AGENTS determinism), every
|
|
883
|
+
* already-started entry forms a contiguous LEADING prefix and every still-`pending`
|
|
884
|
+
* entry forms the trailing suffix — so the boundary is simply the count of leading
|
|
885
|
+
* non-`pending` entries: the index of the first `pending` entry, or the full length when
|
|
886
|
+
* none is `pending` (nothing is safely editable). A `pending` container's entries are ALL
|
|
887
|
+
* `pending`, so the boundary is `0` and every position is naturally accepted — callers
|
|
888
|
+
* need no special case for that.
|
|
889
|
+
*
|
|
890
|
+
* @param statuses - The positional list of statuses to derive the boundary from
|
|
891
|
+
* @returns The index of the first `pending` entry, or `statuses.length` when none is `pending`
|
|
892
|
+
*
|
|
893
|
+
* @example
|
|
894
|
+
* ```ts
|
|
895
|
+
* deriveBoundary(['completed', 'running', 'pending', 'pending']) // 2
|
|
896
|
+
* deriveBoundary(['pending', 'pending']) // 0
|
|
897
|
+
* deriveBoundary(['completed', 'completed']) // 2 (nothing pending)
|
|
898
|
+
* ```
|
|
899
|
+
*/
|
|
900
|
+
export declare function deriveBoundary(statuses: readonly LifecycleStatus[]): number;
|
|
901
|
+
|
|
757
902
|
/**
|
|
758
903
|
* Derive a phase's status from its tasks' statuses (tasks are concurrent, so this
|
|
759
904
|
* is an order-insensitive reduction).
|
|
@@ -814,41 +959,80 @@ export declare function deriveWorkflowStatus(phases: readonly PhaseDerivation[])
|
|
|
814
959
|
* @remarks
|
|
815
960
|
* The expansion of the tool's ADVERTISED surface (AGENTS §21 — the simplest form a small
|
|
816
961
|
* model can author). Each {@link WorkflowStep} maps to a phase holding exactly one task:
|
|
817
|
-
* the step's `name` becomes the task's `run
|
|
818
|
-
*
|
|
819
|
-
*
|
|
820
|
-
*
|
|
821
|
-
*
|
|
822
|
-
*
|
|
823
|
-
*
|
|
824
|
-
* @param flat - The flat steps blob (`{ name?, steps: [{ name
|
|
962
|
+
* the step's `name` becomes the task's `run` (the behavior-registry key). Ids/names are
|
|
963
|
+
* auto-filled positionally — it builds an ids-omitted {@link WorkflowDraft} and delegates
|
|
964
|
+
* to {@link completeDraft}, so the two lenient surfaces share ONE synthesis path (step `i`
|
|
965
|
+
* → phase `phase-<i>`, its task `phase-<i>-task-0`). The optional `name` becomes the
|
|
966
|
+
* workflow's `name`. The result is a complete definition the caller validates against the
|
|
967
|
+
* STRICT contract before running.
|
|
968
|
+
*
|
|
969
|
+
* @param flat - The flat steps blob (`{ name?, steps: [{ name }] }`)
|
|
825
970
|
* @returns A complete {@link WorkflowDefinition} (one one-task phase per step)
|
|
826
971
|
*/
|
|
827
972
|
export declare function expandSteps(flat: WorkflowSteps): WorkflowDefinition;
|
|
828
973
|
|
|
829
974
|
/**
|
|
830
|
-
*
|
|
831
|
-
*
|
|
975
|
+
* Box an error as a {@link Failure} — the graceful outcome half of a {@link Result}.
|
|
976
|
+
*
|
|
977
|
+
* @typeParam E - The boxed error's type
|
|
978
|
+
* @param error - The error to box
|
|
979
|
+
* @returns A {@link Failure} wrapping `error`
|
|
980
|
+
*
|
|
981
|
+
* @example
|
|
982
|
+
* ```ts
|
|
983
|
+
* const result = failure(new WorkflowError('MUTATION', 'refused')) // { success: false, error }
|
|
984
|
+
* ```
|
|
985
|
+
*/
|
|
986
|
+
export declare function failure<E>(error: E): Failure<E>;
|
|
987
|
+
|
|
988
|
+
/**
|
|
989
|
+
* Find the first {@link TaskResult} in a positional list whose boxed outcome is a
|
|
990
|
+
* `Failure` — the pure scan shared by a phase's and a workflow's derived-`failed`
|
|
991
|
+
* `fail`-event lookup.
|
|
992
|
+
*
|
|
993
|
+
* @remarks
|
|
994
|
+
* The shared leaf behind {@link import('./phases/Phase.js').Phase} and
|
|
995
|
+
* {@link import('./Workflow.js').Workflow}'s own `#failure` — each gathers ITS tier's
|
|
996
|
+
* results (a phase's own settled tasks, a workflow's flattened `results()`) and feeds
|
|
997
|
+
* them here; the tier-local method keeps the §12 invariant throw (a derived `failed`
|
|
998
|
+
* status guarantees a failing result exists) since throwing on `undefined` is
|
|
999
|
+
* orchestration, not a leaf concern.
|
|
832
1000
|
*
|
|
833
|
-
* @param
|
|
834
|
-
* @returns
|
|
1001
|
+
* @param results - The results to scan, in any order
|
|
1002
|
+
* @returns The first result whose `result.success` is `false`, or `undefined` if none
|
|
1003
|
+
*
|
|
1004
|
+
* @example
|
|
1005
|
+
* ```ts
|
|
1006
|
+
* findFailure([completedResult, failedResult]) // failedResult
|
|
1007
|
+
* ```
|
|
835
1008
|
*/
|
|
836
|
-
export declare function
|
|
837
|
-
readonly via: 'agent';
|
|
838
|
-
readonly name: string;
|
|
839
|
-
};
|
|
1009
|
+
export declare function findFailure(results: readonly TaskResult[]): TaskResult | undefined;
|
|
840
1010
|
|
|
841
1011
|
/**
|
|
842
|
-
*
|
|
843
|
-
*
|
|
1012
|
+
* Insert one `[key, value]` entry at a positional index into a readonly entries array —
|
|
1013
|
+
* the pure splice-in step behind an insertion-ordered registry's `add`.
|
|
844
1014
|
*
|
|
845
|
-
* @
|
|
846
|
-
*
|
|
1015
|
+
* @remarks
|
|
1016
|
+
* Shared by {@link import('./tasks/TaskManager.js').TaskManager} and
|
|
1017
|
+
* {@link import('./phases/PhaseManager.js').PhaseManager}: both convert their
|
|
1018
|
+
* insertion-ordered `Map` to `[...map.entries()]`, call this to splice the new entry
|
|
1019
|
+
* in at the target index, then rebuild the `Map` from the result (a stateful step that
|
|
1020
|
+
* stays a `#` private method — this helper does no `Map` construction). Does not
|
|
1021
|
+
* mutate `entries`; returns a new array.
|
|
1022
|
+
*
|
|
1023
|
+
* @typeParam T - The entry's value type
|
|
1024
|
+
* @param entries - The current positional entries, in order
|
|
1025
|
+
* @param index - The index to insert at (`0` prepends, `entries.length` appends)
|
|
1026
|
+
* @param key - The new entry's key
|
|
1027
|
+
* @param value - The new entry's value
|
|
1028
|
+
* @returns A new entries array with `[key, value]` inserted at `index`
|
|
1029
|
+
*
|
|
1030
|
+
* @example
|
|
1031
|
+
* ```ts
|
|
1032
|
+
* insertEntry([['a', 1], ['b', 2]], 1, 'c', 3) // [['a', 1], ['c', 3], ['b', 2]]
|
|
1033
|
+
* ```
|
|
847
1034
|
*/
|
|
848
|
-
export declare function
|
|
849
|
-
readonly via: 'function';
|
|
850
|
-
readonly name: string;
|
|
851
|
-
};
|
|
1035
|
+
export declare function insertEntry<T>(entries: readonly (readonly [string, T])[], index: number, key: string, value: T): readonly (readonly [string, T])[];
|
|
852
1036
|
|
|
853
1037
|
/**
|
|
854
1038
|
* Test whether a {@link LifecycleStatus} is TERMINAL — a node in this state will not
|
|
@@ -867,17 +1051,6 @@ export declare function isFunctionTask(form: TaskForm): form is {
|
|
|
867
1051
|
*/
|
|
868
1052
|
export declare function isTerminalStatus(status: LifecycleStatus): boolean;
|
|
869
1053
|
|
|
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
1054
|
/**
|
|
882
1055
|
* Narrow an unknown caught value to a {@link WorkflowError}.
|
|
883
1056
|
*
|
|
@@ -932,16 +1105,17 @@ export declare function isWorkflowSnapshot(value: unknown): value is WorkflowSna
|
|
|
932
1105
|
export declare type LifecycleStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped' | 'stopped';
|
|
933
1106
|
|
|
934
1107
|
/**
|
|
935
|
-
* The maximum nesting depth a workflow
|
|
936
|
-
*
|
|
1108
|
+
* The maximum nesting depth a workflow → agent → workflow chain may reach — the bound
|
|
1109
|
+
* the {@link import('./factories.js').createAgentFunction} and
|
|
1110
|
+
* {@link import('./factories.js').createWorkflowTool} adapters' depth/cycle guards enforce.
|
|
937
1111
|
*
|
|
938
1112
|
* @remarks
|
|
939
|
-
* The limit lives in ONE place.
|
|
940
|
-
*
|
|
941
|
-
*
|
|
942
|
-
* (
|
|
943
|
-
*
|
|
944
|
-
*
|
|
1113
|
+
* The limit lives in ONE place. An {@link import('./factories.js').createAgentFunction}-wrapped
|
|
1114
|
+
* agent running at this depth can no longer author + run a NESTED workflow through its bound
|
|
1115
|
+
* workflow tool (that would be depth `MAX_WORKFLOW_DEPTH + 1`), so the over-deep invocation is
|
|
1116
|
+
* REJECTED (a typed `DEPTH` {@link import('./errors.js').WorkflowError} throw). The chain
|
|
1117
|
+
* therefore nests workflows down to this depth, and the nested run at
|
|
1118
|
+
* depth `MAX_WORKFLOW_DEPTH` fails.
|
|
945
1119
|
*/
|
|
946
1120
|
export declare const MAX_WORKFLOW_DEPTH = 8;
|
|
947
1121
|
|
|
@@ -990,6 +1164,54 @@ export declare class MemoryWorkflowStore implements WorkflowStoreInterface {
|
|
|
990
1164
|
delete(id: string): Promise<void>;
|
|
991
1165
|
}
|
|
992
1166
|
|
|
1167
|
+
/**
|
|
1168
|
+
* Reposition the entry keyed `key` to a new positional index in a readonly entries
|
|
1169
|
+
* array — the pure remove-then-reinsert step behind an insertion-ordered registry's
|
|
1170
|
+
* `move`.
|
|
1171
|
+
*
|
|
1172
|
+
* @remarks
|
|
1173
|
+
* The move counterpart of {@link insertEntry}: finds the entry by `key`, splices it
|
|
1174
|
+
* out, then splices it back in at `index`. An absent `key` is a no-op (returns a copy
|
|
1175
|
+
* of `entries` unchanged) — the caller (`TaskManager.move` / `PhaseManager.move`)
|
|
1176
|
+
* already gates on the target's existence before calling this, so the no-op branch is
|
|
1177
|
+
* defensive, never reached in practice. Does not mutate `entries`; returns a new array.
|
|
1178
|
+
*
|
|
1179
|
+
* @typeParam T - The entry's value type
|
|
1180
|
+
* @param entries - The current positional entries, in order
|
|
1181
|
+
* @param key - The key of the entry to reposition
|
|
1182
|
+
* @param index - The new index for the entry
|
|
1183
|
+
* @returns A new entries array with the `key` entry repositioned to `index`
|
|
1184
|
+
*
|
|
1185
|
+
* @example
|
|
1186
|
+
* ```ts
|
|
1187
|
+
* moveEntry([['a', 1], ['b', 2], ['c', 3]], 'a', 2) // [['b', 2], ['c', 3], ['a', 1]]
|
|
1188
|
+
* ```
|
|
1189
|
+
*/
|
|
1190
|
+
export declare function moveEntry<T>(entries: readonly (readonly [string, T])[], key: string, index: number): readonly (readonly [string, T])[];
|
|
1191
|
+
|
|
1192
|
+
/**
|
|
1193
|
+
* Park until `signal` aborts — a promise-parked wait (AGENTS §21), never a timer or
|
|
1194
|
+
* busy-loop, that NEVER rejects.
|
|
1195
|
+
*
|
|
1196
|
+
* @remarks
|
|
1197
|
+
* Resolves IMMEDIATELY when `signal` is already aborted; otherwise attaches a one-shot
|
|
1198
|
+
* `abort` listener and resolves when it fires, removing the listener either way. The
|
|
1199
|
+
* shared leaf behind the duplicate abort-wiring an execution engine otherwise hand-rolls
|
|
1200
|
+
* at every fold point.
|
|
1201
|
+
*
|
|
1202
|
+
* @param signal - The signal to park on
|
|
1203
|
+
* @returns A promise that resolves once `signal` has aborted
|
|
1204
|
+
*
|
|
1205
|
+
* @example
|
|
1206
|
+
* ```ts
|
|
1207
|
+
* const controller = new AbortController()
|
|
1208
|
+
* const parked = parkSignal(controller.signal)
|
|
1209
|
+
* controller.abort()
|
|
1210
|
+
* await parked // resolves
|
|
1211
|
+
* ```
|
|
1212
|
+
*/
|
|
1213
|
+
export declare function parkSignal(signal: AbortSignal): Promise<void>;
|
|
1214
|
+
|
|
993
1215
|
/**
|
|
994
1216
|
* The live DERIVED state machine (W-b) for one phase — an observable (AGENTS §13) whose
|
|
995
1217
|
* {@link PhaseStatus} is computed from its tasks (never set directly) and recomputed
|
|
@@ -1011,10 +1233,38 @@ export declare class MemoryWorkflowStore implements WorkflowStoreInterface {
|
|
|
1011
1233
|
* `start` / `complete` / `fail` / `stop` on a derived-status CHANGE, strictly AFTER the
|
|
1012
1234
|
* recompute + escalate; the emitter isolates a listener throw and routes it to its `error`
|
|
1013
1235
|
* handler (the `error` option); `fail` carries the failing task's {@link TaskResult}.
|
|
1236
|
+
* - **Structural API (AGENTS §7).** `add` / `remove` / `move` / `update` gate BEFORE
|
|
1237
|
+
* delegating to {@link tasks} (the manager gates the target's own existence/status/id/
|
|
1238
|
+
* bounds), then emit the matching {@link PhaseEventMap} event on success only. NATIVE
|
|
1239
|
+
* gating, purely from this phase's own derived `status` (no runner-installed hook): while
|
|
1240
|
+
* `pending`, any valid `index` is accepted; while `running`, `add` accepts ONLY a pure
|
|
1241
|
+
* append (a live runner subscribed to the `add` event picks it up), and `remove` / `move` /
|
|
1242
|
+
* `update` always fail gracefully (the tasks are already handed to the execution
|
|
1243
|
+
* substrate); while terminal, everything is refused.
|
|
1244
|
+
* - **Patch (AGENTS §12).** `patch` applies a validated {@link PhaseUpdate} to SELF
|
|
1245
|
+
* (`name` / `description` / `concurrency` / `bail`) — defense-in-depth: it throws a
|
|
1246
|
+
* `MUTATION` {@link WorkflowError} unless this phase's own `status` is `pending`, mirroring
|
|
1247
|
+
* the owning {@link WorkflowInterface.update}'s gate.
|
|
1248
|
+
* - **Minting (AGENTS §7).** {@link add} MINTS a live {@link Task} from a {@link TaskDefinition}
|
|
1249
|
+
* (converts it to a {@link TaskSnapshot}, builds the task wired to THIS phase) — the same
|
|
1250
|
+
* construction path {@link #append} uses at build time, so a live mint and a restored/built
|
|
1251
|
+
* task are wired IDENTICALLY. At construction, the workflow-level
|
|
1252
|
+
* {@link import('../types.js').WorkflowFunctions} registry (threaded from
|
|
1253
|
+
* {@link import('../types.js').WorkflowOptions.functions}) resolves each task's `run` name into
|
|
1254
|
+
* its runtime {@link import('../types.js').TaskInterface.handler} ONCE; a `run` that is omitted
|
|
1255
|
+
* or unregistered resolves to no handler (the no-handler rule).
|
|
1256
|
+
* - **Runtime lifecycle (AGENTS §10).** `pause` / `resume` / `wait` mirror the workflow's own
|
|
1257
|
+
* quartet, scoped to this phase — a driving
|
|
1258
|
+
* {@link import('../types.js').WorkflowRunnerInterface.execute} gates a task's own
|
|
1259
|
+
* pre-dispatch on the workflow's gate FIRST, then this phase's gate, WITHOUT touching
|
|
1260
|
+
* {@link status} — `paused` is runtime-only, never persisted. `skip` / `stop` (this phase's
|
|
1261
|
+
* own terminal forcing) always release a parked {@link wait} waiter, mirroring
|
|
1262
|
+
* {@link import('../Workflow.js').Workflow.destroy}'s cascade — a permanently-ended phase
|
|
1263
|
+
* has nothing left to pause for.
|
|
1014
1264
|
*/
|
|
1015
1265
|
export declare class Phase implements PhaseInterface {
|
|
1016
1266
|
#private;
|
|
1017
|
-
constructor(snapshot: PhaseSnapshot, workflow: WorkflowInterface, escalate: () => void, options?: PhaseOptions, bail?: boolean);
|
|
1267
|
+
constructor(snapshot: PhaseSnapshot, workflow: WorkflowInterface, escalate: () => void, options?: PhaseOptions, bail?: boolean, functions?: WorkflowFunctions);
|
|
1018
1268
|
get emitter(): EmitterInterface<PhaseEventMap>;
|
|
1019
1269
|
get id(): string;
|
|
1020
1270
|
get name(): string;
|
|
@@ -1022,12 +1272,22 @@ export declare class Phase implements PhaseInterface {
|
|
|
1022
1272
|
get context(): PhaseContext;
|
|
1023
1273
|
get workflow(): WorkflowInterface;
|
|
1024
1274
|
get bail(): boolean;
|
|
1275
|
+
get concurrency(): number | undefined;
|
|
1276
|
+
get paused(): boolean;
|
|
1025
1277
|
get status(): PhaseStatus;
|
|
1026
1278
|
get tasks(): TaskManagerInterface;
|
|
1027
1279
|
task(id: string): TaskInterface | undefined;
|
|
1028
1280
|
results(): readonly TaskResult[];
|
|
1029
1281
|
skip(): void;
|
|
1030
1282
|
stop(): void;
|
|
1283
|
+
pause(): void;
|
|
1284
|
+
resume(): void;
|
|
1285
|
+
wait(): Promise<void>;
|
|
1286
|
+
add(definition: TaskDefinition, index?: number): Result<TaskInterface, WorkflowError>;
|
|
1287
|
+
remove(id: string): Result<TaskInterface, WorkflowError>;
|
|
1288
|
+
move(id: string, index: number): Result<TaskInterface, WorkflowError>;
|
|
1289
|
+
update(id: string, patch: TaskUpdate): Result<TaskInterface, WorkflowError>;
|
|
1290
|
+
patch(value: PhaseUpdate): void;
|
|
1031
1291
|
snapshot(): PhaseSnapshot;
|
|
1032
1292
|
}
|
|
1033
1293
|
|
|
@@ -1082,6 +1342,7 @@ export declare interface PhaseDefinition {
|
|
|
1082
1342
|
* The snapshot persists the EFFECTIVE failure policy this phase runs under: the phase's own
|
|
1083
1343
|
* `bail` when it declares one, else the `workflowBail` it inherits — so a restore reinstates
|
|
1084
1344
|
* the same per-phase policy without a silent default (`effectiveBail = phase.bail ?? workflowBail`).
|
|
1345
|
+
* `concurrency` (the resource throttle) carries over verbatim, omitted when undefined.
|
|
1085
1346
|
*
|
|
1086
1347
|
* @param phase - The phase definition to seed from
|
|
1087
1348
|
* @param workflowBail - The workflow-level `bail` default the phase inherits when it declares none
|
|
@@ -1130,16 +1391,7 @@ export declare const phaseDraftShape: ObjectShape<{
|
|
|
1130
1391
|
id: OptionalShape<StringShape>;
|
|
1131
1392
|
name: OptionalShape<StringShape>;
|
|
1132
1393
|
description: OptionalShape<StringShape>;
|
|
1133
|
-
run:
|
|
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
|
-
}>]>;
|
|
1394
|
+
run: OptionalShape<StringShape>;
|
|
1143
1395
|
retries: OptionalShape<NumberShape>;
|
|
1144
1396
|
timeout: OptionalShape<NumberShape>;
|
|
1145
1397
|
}>>;
|
|
@@ -1154,9 +1406,12 @@ export declare const phaseDraftShape: ObjectShape<{
|
|
|
1154
1406
|
* @remarks
|
|
1155
1407
|
* `start` fires when the phase begins; `complete` when all its tasks settled
|
|
1156
1408
|
* successfully; `fail` when a task failed under `bail` (carrying the
|
|
1157
|
-
* {@link TaskResult}); `stop` when the phase was ended.
|
|
1158
|
-
*
|
|
1159
|
-
*
|
|
1409
|
+
* {@link TaskResult}); `stop` when the phase was ended. `add` / `remove` / `move` /
|
|
1410
|
+
* `update` fire on a successful structural or patch edit through
|
|
1411
|
+
* {@link PhaseInterface.add} / `remove` / `move` / `update` (AGENTS §7) — never on a
|
|
1412
|
+
* refused/gated one. A throwing listener is isolated by the emitter and routed to its
|
|
1413
|
+
* `error` handler, not the domain surface (AGENTS §13). A `type` alias (AGENTS §4.5)
|
|
1414
|
+
* so it satisfies `EventMap`.
|
|
1160
1415
|
*/
|
|
1161
1416
|
export declare type PhaseEventMap = {
|
|
1162
1417
|
/** The phase began — its `id`. */
|
|
@@ -1167,6 +1422,14 @@ export declare type PhaseEventMap = {
|
|
|
1167
1422
|
readonly fail: readonly [result: TaskResult];
|
|
1168
1423
|
/** The phase was permanently stopped. */
|
|
1169
1424
|
readonly stop: readonly [];
|
|
1425
|
+
/** A task was inserted — the inserted task + its final index. */
|
|
1426
|
+
readonly add: readonly [task: TaskInterface, index: number];
|
|
1427
|
+
/** A task was removed — the removed task. */
|
|
1428
|
+
readonly remove: readonly [task: TaskInterface];
|
|
1429
|
+
/** A task was repositioned — the moved task + its new index. */
|
|
1430
|
+
readonly move: readonly [task: TaskInterface, index: number];
|
|
1431
|
+
/** A task was patched — the patched task. */
|
|
1432
|
+
readonly update: readonly [task: TaskInterface];
|
|
1170
1433
|
};
|
|
1171
1434
|
|
|
1172
1435
|
/** Initial {@link PhaseEventMap} listeners — the reserved `on` option (AGENTS §8). */
|
|
@@ -1194,6 +1457,12 @@ export declare type PhaseInput = Partial<PhaseContext>;
|
|
|
1194
1457
|
* - **Observable (AGENTS §13).** The owned {@link emitter} ({@link PhaseEventMap}) fires
|
|
1195
1458
|
* `start` / `complete` / `fail` / `stop` on a derived-status change; the emitter isolates a
|
|
1196
1459
|
* listener throw and routes it to its `error` handler (the `error` option).
|
|
1460
|
+
* - **Runtime lifecycle (AGENTS §10).** `pause` / `resume` / `wait` mirror
|
|
1461
|
+
* {@link WorkflowInterface.pause} / `resume` / `wait`, scoped to this phase — a driving
|
|
1462
|
+
* {@link WorkflowRunnerInterface.execute} gates a task's own pre-dispatch on BOTH the
|
|
1463
|
+
* workflow's and its phase's gate. `paused` is RUNTIME-ONLY, never persisted; idempotent;
|
|
1464
|
+
* released by `resume` and by this phase's own `stop` / `skip` forcing a terminal status
|
|
1465
|
+
* (a permanently-ended phase has nothing left to pause for).
|
|
1197
1466
|
*/
|
|
1198
1467
|
export declare interface PhaseInterface {
|
|
1199
1468
|
readonly emitter: EmitterInterface<PhaseEventMap>;
|
|
@@ -1205,13 +1474,167 @@ export declare interface PhaseInterface {
|
|
|
1205
1474
|
readonly status: PhaseStatus;
|
|
1206
1475
|
/** The RESOLVED effective failure policy this phase runs under (`phase.bail ?? workflow.bail`); mirrors {@link WorkflowInterface.bail}. */
|
|
1207
1476
|
readonly bail: boolean;
|
|
1477
|
+
/** Max tasks in flight at once (a resource throttle); mirrors {@link PhaseSnapshot.concurrency}. `undefined` ⇒ unbounded. */
|
|
1478
|
+
readonly concurrency: number | undefined;
|
|
1479
|
+
/**
|
|
1480
|
+
* Whether the phase is currently paused (AGENTS §10 — resumable); RUNTIME-ONLY — never a
|
|
1481
|
+
* {@link PhaseStatus}, never persisted in a {@link PhaseSnapshot} (a paused phase's
|
|
1482
|
+
* `status` still reports its ordinary derived value).
|
|
1483
|
+
*/
|
|
1484
|
+
readonly paused: boolean;
|
|
1208
1485
|
readonly tasks: TaskManagerInterface;
|
|
1209
1486
|
/** Look up one live task by its `id`. */
|
|
1210
1487
|
task(id: string): TaskInterface | undefined;
|
|
1211
1488
|
/** The settled tasks' results, in positional order — the phase tier of the result tree. */
|
|
1212
1489
|
results(): readonly TaskResult[];
|
|
1490
|
+
/**
|
|
1491
|
+
* FORCE this phase to `skipped` (AGENTS §10), overriding the derived value; idempotent.
|
|
1492
|
+
*
|
|
1493
|
+
* @remarks
|
|
1494
|
+
* A NO-OP once `status` is already terminal — a settled phase cannot be re-forced. Always
|
|
1495
|
+
* releases a parked {@link wait} waiter regardless (a terminal phase has nothing left to
|
|
1496
|
+
* pause for).
|
|
1497
|
+
*/
|
|
1213
1498
|
skip(): void;
|
|
1499
|
+
/**
|
|
1500
|
+
* FORCE this phase to `stopped` (AGENTS §10), overriding the derived value; idempotent.
|
|
1501
|
+
*
|
|
1502
|
+
* @remarks
|
|
1503
|
+
* A NO-OP once `status` is already terminal (a settled phase cannot be re-forced). Always
|
|
1504
|
+
* releases a parked {@link wait} waiter regardless (a terminal phase has nothing left to
|
|
1505
|
+
* pause for).
|
|
1506
|
+
*/
|
|
1214
1507
|
stop(): void;
|
|
1508
|
+
/**
|
|
1509
|
+
* Suspend the phase (AGENTS §10 — resumable); idempotent.
|
|
1510
|
+
*
|
|
1511
|
+
* @remarks
|
|
1512
|
+
* A no-op when already `paused` or when `status` is terminal. RUNTIME-ONLY (AGENTS §10) —
|
|
1513
|
+
* never a {@link PhaseStatus}, never persisted in a {@link PhaseSnapshot}. A driving
|
|
1514
|
+
* {@link WorkflowRunnerInterface.execute} gates a task's own pre-dispatch on this phase's
|
|
1515
|
+
* gate (after the workflow's own gate). **Pausing does NOT suspend a driving run's
|
|
1516
|
+
* timeout / budget / abort clocks** — those bounds keep ticking while paused, so a long
|
|
1517
|
+
* pause can still fire a run-level cancel and stop the workflow while parked.
|
|
1518
|
+
*
|
|
1519
|
+
* @example
|
|
1520
|
+
* ```ts
|
|
1521
|
+
* phase.pause()
|
|
1522
|
+
* phase.paused // true
|
|
1523
|
+
* ```
|
|
1524
|
+
*/
|
|
1525
|
+
pause(): void;
|
|
1526
|
+
/**
|
|
1527
|
+
* Continue a paused phase (AGENTS §10); idempotent — a no-op unless {@link paused}.
|
|
1528
|
+
*
|
|
1529
|
+
* @example
|
|
1530
|
+
* ```ts
|
|
1531
|
+
* phase.resume()
|
|
1532
|
+
* phase.paused // false
|
|
1533
|
+
* ```
|
|
1534
|
+
*/
|
|
1535
|
+
resume(): void;
|
|
1536
|
+
/**
|
|
1537
|
+
* Park until this phase is not paused — **promise-parked**, never a timer or busy-loop
|
|
1538
|
+
* (AGENTS §21; mirrors {@link WorkflowInterface.wait}).
|
|
1539
|
+
*
|
|
1540
|
+
* @remarks
|
|
1541
|
+
* Resolves IMMEDIATELY when not {@link paused}. While paused, parks until `resume` or
|
|
1542
|
+
* this phase's own `stop` / `skip` forcing a terminal status — all release a parked
|
|
1543
|
+
* waiter. NEVER rejects.
|
|
1544
|
+
*
|
|
1545
|
+
* @returns A promise that resolves once the phase is no longer paused
|
|
1546
|
+
*/
|
|
1547
|
+
wait(): Promise<void>;
|
|
1548
|
+
/**
|
|
1549
|
+
* MINT a live {@link TaskInterface} from `definition` and insert it into this phase
|
|
1550
|
+
* (AGENTS §7 the entity structural API) — gated BEFORE delegating to {@link tasks}'
|
|
1551
|
+
* manager.
|
|
1552
|
+
*
|
|
1553
|
+
* @remarks
|
|
1554
|
+
* Converts `definition` → {@link TaskSnapshot} and constructs the live task (wired to
|
|
1555
|
+
* THIS phase, its recompute cascade, and its emitter hooks), carrying its `run` /
|
|
1556
|
+
* `retries` / `timeout` from `definition` and resolving its {@link TaskInterface.handler}
|
|
1557
|
+
* against the workflow-level {@link WorkflowOptions.functions} registry — the SAME
|
|
1558
|
+
* resolution {@link import('./factories.js').createWorkflow} performs at build time.
|
|
1559
|
+
* Requires `definition.id` to be UNIQUE among this phase's existing
|
|
1560
|
+
* task ids — a duplicate is a `MUTATION` failure (mirrors
|
|
1561
|
+
* {@link TaskManagerInterface.add}'s own duplicate-id gate).
|
|
1562
|
+
*
|
|
1563
|
+
* NATIVE gating, purely from this phase's own derived `status` (AGENTS §12 — no
|
|
1564
|
+
* runner-installed hook), UNCHANGED from the entity-taking predecessor. While
|
|
1565
|
+
* `pending`: any valid `index` is accepted (delegates the minted task to
|
|
1566
|
+
* {@link TaskManagerInterface.add} then emits `add`). While `running`: accepted ONLY as
|
|
1567
|
+
* a pure append (`index` omitted or `=== tasks.count`) — a live runner subscribed to
|
|
1568
|
+
* the `add` event picks the new task up for same-run execution; the derived-status
|
|
1569
|
+
* model guarantees this phase cannot reach a terminal status while the accepted task is
|
|
1570
|
+
* still `pending` (its status feeds `status` via {@link import('./helpers.js').derivePhaseStatus}).
|
|
1571
|
+
* While terminal: always refused.
|
|
1572
|
+
*
|
|
1573
|
+
* **Abort edge.** An append ACCEPTED while `running` can still settle `skipped` rather
|
|
1574
|
+
* than run — if the driving run is cancelled (abort / timeout / budget / `workflow.destroy()`)
|
|
1575
|
+
* before the substrate actually dispatches the newly-minted task, the runner's halt sweep
|
|
1576
|
+
* `skip`s it like any other not-yet-started task. Acceptance here only guarantees the task
|
|
1577
|
+
* is WIRED into the live tree, not that it will execute.
|
|
1578
|
+
*
|
|
1579
|
+
* @param definition - The {@link TaskDefinition} to mint a live task from
|
|
1580
|
+
* @param index - The insertion position; omitted inserts at the end
|
|
1581
|
+
* @returns A {@link Result} boxing the minted, inserted task, or a `MUTATION` failure
|
|
1582
|
+
*/
|
|
1583
|
+
add(definition: TaskDefinition, index?: number): Result<TaskInterface, WorkflowError>;
|
|
1584
|
+
/**
|
|
1585
|
+
* Remove the `pending` task `id` from this phase.
|
|
1586
|
+
*
|
|
1587
|
+
* @remarks
|
|
1588
|
+
* NATIVE gating: allowed only while this phase's own `status` is `pending`. While
|
|
1589
|
+
* `running` or terminal, always a `MUTATION` failure — a running phase's tasks are
|
|
1590
|
+
* already handed to the execution substrate and only a pure {@link add} append remains
|
|
1591
|
+
* possible.
|
|
1592
|
+
*
|
|
1593
|
+
* @param id - The task id to remove
|
|
1594
|
+
* @returns A {@link Result} boxing the removed task, or a `MUTATION` failure
|
|
1595
|
+
*/
|
|
1596
|
+
remove(id: string): Result<TaskInterface, WorkflowError>;
|
|
1597
|
+
/**
|
|
1598
|
+
* Reposition the `pending` task `id` to `index` within this phase.
|
|
1599
|
+
*
|
|
1600
|
+
* @remarks
|
|
1601
|
+
* NATIVE gating: allowed only while this phase's own `status` is `pending`; `running` /
|
|
1602
|
+
* terminal always fail (see {@link remove}).
|
|
1603
|
+
*
|
|
1604
|
+
* @param id - The task id to move
|
|
1605
|
+
* @param index - The destination position
|
|
1606
|
+
* @returns A {@link Result} boxing the moved task, or a `MUTATION` failure
|
|
1607
|
+
*/
|
|
1608
|
+
move(id: string, index: number): Result<TaskInterface, WorkflowError>;
|
|
1609
|
+
/**
|
|
1610
|
+
* Apply a validated {@link TaskUpdate} patch to the `pending` task `id` in this phase.
|
|
1611
|
+
*
|
|
1612
|
+
* @remarks
|
|
1613
|
+
* NATIVE gating: allowed only while this phase's own `status` is `pending`; `running` /
|
|
1614
|
+
* terminal always fail (see {@link remove}).
|
|
1615
|
+
*
|
|
1616
|
+
* @param id - The task id to patch
|
|
1617
|
+
* @param patch - The fields to update
|
|
1618
|
+
* @returns A {@link Result} boxing the patched task, or a `MUTATION` failure
|
|
1619
|
+
*/
|
|
1620
|
+
update(id: string, patch: TaskUpdate): Result<TaskInterface, WorkflowError>;
|
|
1621
|
+
/**
|
|
1622
|
+
* Apply a validated declarative patch to SELF (`name` / `description` /
|
|
1623
|
+
* `concurrency` / `bail`).
|
|
1624
|
+
*
|
|
1625
|
+
* @remarks
|
|
1626
|
+
* Defense-in-depth (AGENTS §12): the owning {@link WorkflowInterface.update} gates
|
|
1627
|
+
* FIRST, so a direct call here THROWS a `MUTATION`
|
|
1628
|
+
* {@link import('./errors.js').WorkflowError} unless this phase's own `status` is
|
|
1629
|
+
* `pending`.
|
|
1630
|
+
*
|
|
1631
|
+
* @param value - The {@link PhaseUpdate} fields to apply
|
|
1632
|
+
* @example
|
|
1633
|
+
* ```ts
|
|
1634
|
+
* phase.patch({ concurrency: 4 })
|
|
1635
|
+
* ```
|
|
1636
|
+
*/
|
|
1637
|
+
patch(value: PhaseUpdate): void;
|
|
1215
1638
|
snapshot(): PhaseSnapshot;
|
|
1216
1639
|
}
|
|
1217
1640
|
|
|
@@ -1225,6 +1648,11 @@ export declare interface PhaseInterface {
|
|
|
1225
1648
|
* `append` adds one at the end, `phase(id)` looks one up, `phases()` lists them in
|
|
1226
1649
|
* positional order, `count` is the size. A snapshot RESTORE re-`append`s in the
|
|
1227
1650
|
* snapshot's order, reproducing it exactly.
|
|
1651
|
+
* - **Gated mutation API (AGENTS §12).** `add` / `remove` / `move` / `update` are the
|
|
1652
|
+
* graceful `Result` counterparts to `append`, gating ONLY on the target's OWN
|
|
1653
|
+
* existence/status/id/bounds — a duplicate id, an absent/non-`pending` target, an
|
|
1654
|
+
* out-of-bounds `index`, or a patch that fails {@link phaseUpdateShape} validation
|
|
1655
|
+
* all fail gracefully with a `MUTATION` {@link WorkflowError} instead of throwing.
|
|
1228
1656
|
* - **No batch matrix.** A workflow's phases are a fixed positional set, so AGENTS §9.2
|
|
1229
1657
|
* is deliberately omitted.
|
|
1230
1658
|
* - **Event-free.** A purely structural container — the live {@link PhaseInterface}s own
|
|
@@ -1242,6 +1670,10 @@ export declare class PhaseManager implements PhaseManagerInterface {
|
|
|
1242
1670
|
#private;
|
|
1243
1671
|
get count(): number;
|
|
1244
1672
|
append(phase: PhaseInterface): void;
|
|
1673
|
+
add(phase: PhaseInterface, index?: number): Result<PhaseInterface, WorkflowError>;
|
|
1674
|
+
remove(id: string): Result<PhaseInterface, WorkflowError>;
|
|
1675
|
+
move(id: string, index: number): Result<PhaseInterface, WorkflowError>;
|
|
1676
|
+
update(id: string, patch: PhaseUpdate): Result<PhaseInterface, WorkflowError>;
|
|
1245
1677
|
phase(id: string): PhaseInterface | undefined;
|
|
1246
1678
|
phases(): readonly PhaseInterface[];
|
|
1247
1679
|
}
|
|
@@ -1253,10 +1685,61 @@ export declare class PhaseManager implements PhaseManagerInterface {
|
|
|
1253
1685
|
* @remarks
|
|
1254
1686
|
* `append` adds one live {@link PhaseInterface} at the end; `phase(id)` looks one up;
|
|
1255
1687
|
* `phases()` lists them in positional order; `count` is the tally. No batch matrix.
|
|
1688
|
+
* `add` / `remove` / `move` / `update` (AGENTS §12) are the GATED mutation
|
|
1689
|
+
* counterparts a {@link WorkflowInterface.add} / `remove` / `move` / `update`
|
|
1690
|
+
* delegates to AFTER its own container-status/hook gating — the manager gates ONLY
|
|
1691
|
+
* on the target's OWN existence/status/id/bounds and stays event-free (the entity
|
|
1692
|
+
* emits on success).
|
|
1256
1693
|
*/
|
|
1257
1694
|
export declare interface PhaseManagerInterface {
|
|
1258
1695
|
readonly count: number;
|
|
1696
|
+
/**
|
|
1697
|
+
* Add `phase` at the end (the build-time wiring path).
|
|
1698
|
+
*
|
|
1699
|
+
* @remarks
|
|
1700
|
+
* THROWS a `MUTATION` {@link import('./errors.js').WorkflowError} on a duplicate
|
|
1701
|
+
* `id` (a genuine programmer error — a build-time wiring bug, AGENTS §12) instead of
|
|
1702
|
+
* silently overwriting the existing entry.
|
|
1703
|
+
*
|
|
1704
|
+
* @param phase - The live phase to append
|
|
1705
|
+
*/
|
|
1259
1706
|
append(phase: PhaseInterface): void;
|
|
1707
|
+
/**
|
|
1708
|
+
* Insert `phase` at `index` (default the end) — the GATED mutation counterpart to
|
|
1709
|
+
* {@link append}: a duplicate `id` or an out-of-bounds `index` fails gracefully
|
|
1710
|
+
* instead of throwing.
|
|
1711
|
+
*
|
|
1712
|
+
* @param phase - The live phase to insert
|
|
1713
|
+
* @param index - The insertion position (`[0, count]`); omitted inserts at the end
|
|
1714
|
+
* @returns A {@link Result} boxing the inserted phase, or a `MUTATION` failure
|
|
1715
|
+
*/
|
|
1716
|
+
add(phase: PhaseInterface, index?: number): Result<PhaseInterface, WorkflowError>;
|
|
1717
|
+
/**
|
|
1718
|
+
* Remove the `pending` phase `id`.
|
|
1719
|
+
*
|
|
1720
|
+
* @param id - The phase id to remove
|
|
1721
|
+
* @returns A {@link Result} boxing the removed phase, or a `MUTATION` failure when
|
|
1722
|
+
* `id` is absent or not `pending`
|
|
1723
|
+
*/
|
|
1724
|
+
remove(id: string): Result<PhaseInterface, WorkflowError>;
|
|
1725
|
+
/**
|
|
1726
|
+
* Reposition the `pending` phase `id` to `index`.
|
|
1727
|
+
*
|
|
1728
|
+
* @param id - The phase id to move
|
|
1729
|
+
* @param index - The destination position (`[0, count)`)
|
|
1730
|
+
* @returns A {@link Result} boxing the moved phase, or a `MUTATION` failure when
|
|
1731
|
+
* `id` is absent, not `pending`, or `index` is out of bounds
|
|
1732
|
+
*/
|
|
1733
|
+
move(id: string, index: number): Result<PhaseInterface, WorkflowError>;
|
|
1734
|
+
/**
|
|
1735
|
+
* Apply a validated {@link PhaseUpdate} patch to the `pending` phase `id`.
|
|
1736
|
+
*
|
|
1737
|
+
* @param id - The phase id to patch
|
|
1738
|
+
* @param patch - The fields to update
|
|
1739
|
+
* @returns A {@link Result} boxing the patched phase, or a `MUTATION` failure when
|
|
1740
|
+
* `id` is absent, not `pending`, or `patch` fails validation
|
|
1741
|
+
*/
|
|
1742
|
+
update(id: string, patch: PhaseUpdate): Result<PhaseInterface, WorkflowError>;
|
|
1260
1743
|
phase(id: string): PhaseInterface | undefined;
|
|
1261
1744
|
phases(): readonly PhaseInterface[];
|
|
1262
1745
|
}
|
|
@@ -1292,16 +1775,7 @@ export declare const phaseShape: ObjectShape<{
|
|
|
1292
1775
|
id: StringShape;
|
|
1293
1776
|
name: StringShape;
|
|
1294
1777
|
description: OptionalShape<StringShape>;
|
|
1295
|
-
run:
|
|
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
|
-
}>]>;
|
|
1778
|
+
run: OptionalShape<StringShape>;
|
|
1305
1779
|
retries: OptionalShape<NumberShape>;
|
|
1306
1780
|
timeout: OptionalShape<NumberShape>;
|
|
1307
1781
|
}>>;
|
|
@@ -1334,6 +1808,12 @@ export declare interface PhaseSnapshot {
|
|
|
1334
1808
|
* per-phase policy identically without a silent default.
|
|
1335
1809
|
*/
|
|
1336
1810
|
readonly bail: boolean;
|
|
1811
|
+
/**
|
|
1812
|
+
* Max tasks in flight at once (a resource throttle), persisted so a restore reinstates the
|
|
1813
|
+
* same per-phase throttle — mirrors {@link import('./types.js').PhaseDefinition.concurrency}.
|
|
1814
|
+
* Omitted ⇒ unbounded.
|
|
1815
|
+
*/
|
|
1816
|
+
readonly concurrency?: number;
|
|
1337
1817
|
readonly tasks: readonly TaskSnapshot[];
|
|
1338
1818
|
}
|
|
1339
1819
|
|
|
@@ -1349,6 +1829,45 @@ export declare interface PhaseSnapshot {
|
|
|
1349
1829
|
*/
|
|
1350
1830
|
export declare type PhaseStatus = LifecycleStatus;
|
|
1351
1831
|
|
|
1832
|
+
/**
|
|
1833
|
+
* A declarative partial update to a {@link PhaseInterface} — the fields a `pending`
|
|
1834
|
+
* phase's {@link PhaseInterface.patch} (and the owning {@link PhaseManagerInterface.update})
|
|
1835
|
+
* accept, runtime-validated via {@link import('./shapers.js').phaseUpdateShape}.
|
|
1836
|
+
*
|
|
1837
|
+
* @remarks
|
|
1838
|
+
* Mirrors the identity + throttle/policy fields of {@link PhaseDefinition} (`name` /
|
|
1839
|
+
* `description` / `concurrency` / `bail`) — never `id` / `tasks` (structural children
|
|
1840
|
+
* change through {@link PhaseInterface.add} / `remove` / `move`, not a patch). Every
|
|
1841
|
+
* field is optional; an omitted field is left unchanged.
|
|
1842
|
+
*
|
|
1843
|
+
* @example
|
|
1844
|
+
* ```ts
|
|
1845
|
+
* const result = workflow.phases.update(phase.id, { concurrency: 4, bail: true })
|
|
1846
|
+
* ```
|
|
1847
|
+
*/
|
|
1848
|
+
export declare interface PhaseUpdate {
|
|
1849
|
+
readonly name?: string;
|
|
1850
|
+
readonly description?: string;
|
|
1851
|
+
readonly concurrency?: number;
|
|
1852
|
+
readonly bail?: boolean;
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
/**
|
|
1856
|
+
* The shape of a {@link import('./types.js').PhaseUpdate} — a partial edit to a
|
|
1857
|
+
* `pending` phase's `name` / `description` / `concurrency` / `bail`, all optional.
|
|
1858
|
+
*
|
|
1859
|
+
* @remarks
|
|
1860
|
+
* Mirrors {@link phaseShape}'s corresponding field constraints exactly; never `id` /
|
|
1861
|
+
* `tasks` (structural children change through the phase's own `add` / `remove` /
|
|
1862
|
+
* `move`, not a patch, AGENTS §12).
|
|
1863
|
+
*/
|
|
1864
|
+
export declare const phaseUpdateShape: ObjectShape<{
|
|
1865
|
+
name: OptionalShape<StringShape>;
|
|
1866
|
+
description: OptionalShape<StringShape>;
|
|
1867
|
+
concurrency: OptionalShape<NumberShape>;
|
|
1868
|
+
bail: OptionalShape<LiteralShape<readonly [true, false]>>;
|
|
1869
|
+
}>;
|
|
1870
|
+
|
|
1352
1871
|
/**
|
|
1353
1872
|
* Rebuild an equivalent live W-b entity tree from a {@link WorkflowSnapshot} — the
|
|
1354
1873
|
* inverse of {@link WorkflowInterface.snapshot}, restoring structure + each node's status
|
|
@@ -1414,6 +1933,15 @@ export declare function restoreWorkflow(snapshot: WorkflowSnapshot, options?: Wo
|
|
|
1414
1933
|
* unit failure (after its retries) records the error and `abort()`s the run, so every
|
|
1415
1934
|
* sibling's signal fires; later failures are ignored and `execute` rejects with the
|
|
1416
1935
|
* first error. A user `abort(reason)` likewise rejects a running `execute`.
|
|
1936
|
+
* - **`pause` / `resume` / `stop` (§10) ride the backing Queue.** `pause` / `resume`
|
|
1937
|
+
* delegate straight to the Queue's own pause/resume (holding/releasing the NEXT
|
|
1938
|
+
* dispatch while an in-flight unit finishes); `paused` mirrors the Queue's. `stop` is a
|
|
1939
|
+
* GRACEFUL permanent end, distinct from `abort`: still-pending (never-dispatched)
|
|
1940
|
+
* units are rejected by the Queue's own stop WITHOUT their handler ever running, and
|
|
1941
|
+
* `#settle` reads that fact (`#dispatched`) to treat the rejection as a stop artifact —
|
|
1942
|
+
* not a failure, never tripping fail-fast — while an in-flight unit still runs to
|
|
1943
|
+
* completion and settles normally. `execute` RESOLVES (never rejects) once every unit
|
|
1944
|
+
* has settled, with whatever results actually completed.
|
|
1417
1945
|
* - **Observable (§13).** The owned {@link emitter} ({@link RunnerEventMap}) carries the run
|
|
1418
1946
|
* lifecycle — `start` / `unit` / `spawn` / `settle` / `fail` / `finish` / `abort` — for
|
|
1419
1947
|
* fire-and-forget observers. Every event is emitted directly, strictly AFTER the relevant
|
|
@@ -1429,8 +1957,66 @@ export declare class Runner<TInput, TResult> implements RunnerInterface<TInput,
|
|
|
1429
1957
|
get emitter(): EmitterInterface<RunnerEventMap<TResult>>;
|
|
1430
1958
|
get active(): number;
|
|
1431
1959
|
get stopped(): boolean;
|
|
1960
|
+
get paused(): boolean;
|
|
1961
|
+
/**
|
|
1962
|
+
* Inject one more unit into an IN-FLIGHT `execute` run — a LIVE counterpart to a
|
|
1963
|
+
* `Controller.spawn`, called from OUTSIDE any unit's handler.
|
|
1964
|
+
*
|
|
1965
|
+
* @remarks
|
|
1966
|
+
* Returns `undefined` synchronously (graceful, non-throwing — AGENTS §12) unless the
|
|
1967
|
+
* runner is currently mid-`execute` and not yet stopped — covering "never started",
|
|
1968
|
+
* "already drained", "aborted", and "destroyed". Otherwise the unit is routed through
|
|
1969
|
+
* the SAME backing queue as a declared/`spawn`ed unit via `#launch` — the outstanding-
|
|
1970
|
+
* unit count gate increments BEFORE this call returns, so an in-flight `execute`
|
|
1971
|
+
* keeps awaiting it (the drain race: `#running` flips to `false` as the very first
|
|
1972
|
+
* step after `execute`'s `await drained.promise` settles, so a `spawn` reaching this
|
|
1973
|
+
* method after the run has fully drained is cleanly rejected with `undefined` —
|
|
1974
|
+
* never silently dropped, never hangs `execute`). Emits {@link RunnerEventMap.spawn}
|
|
1975
|
+
* with a `parent` of `undefined` (this call has no spawning unit) once accepted.
|
|
1976
|
+
*
|
|
1977
|
+
* @param input - The unit's work payload
|
|
1978
|
+
* @returns The unit's result promise, or `undefined` when no in-flight run can accept it
|
|
1979
|
+
* @example
|
|
1980
|
+
* ```ts
|
|
1981
|
+
* const runner = createRunner({ handler: (c) => c.input })
|
|
1982
|
+
* const result = runner.execute([1, 2])
|
|
1983
|
+
* const extra = runner.spawn(3) // Promise<number> | undefined
|
|
1984
|
+
* await result
|
|
1985
|
+
* ```
|
|
1986
|
+
*/
|
|
1987
|
+
spawn(input: TInput): Promise<TResult> | undefined;
|
|
1432
1988
|
execute(inputs: readonly TInput[]): Promise<readonly TResult[]>;
|
|
1433
1989
|
abort(reason?: unknown): void;
|
|
1990
|
+
/**
|
|
1991
|
+
* Suspend dispatch (AGENTS §10 — resumable): delegates to the backing queue's own
|
|
1992
|
+
* `pause`, which holds the NEXT dispatch while any in-flight unit finishes.
|
|
1993
|
+
*
|
|
1994
|
+
* @remarks
|
|
1995
|
+
* A no-op once the runner is `stopped` — a stopped runner has no dispatch left to
|
|
1996
|
+
* suspend, mirroring the guard `stop()` itself applies. Also a no-op when already
|
|
1997
|
+
* `paused` (the queue's own `pause` is idempotent), so calling it repeatedly is safe.
|
|
1998
|
+
*/
|
|
1999
|
+
pause(): void;
|
|
2000
|
+
/**
|
|
2001
|
+
* Continue a paused runner (AGENTS §10); delegates to the backing queue's `resume`.
|
|
2002
|
+
*
|
|
2003
|
+
* @remarks
|
|
2004
|
+
* A no-op once the runner is `stopped` (nothing left to resume) and a no-op when the
|
|
2005
|
+
* runner is not currently `paused`, so calling it repeatedly or on a never-paused
|
|
2006
|
+
* runner is safe.
|
|
2007
|
+
*/
|
|
2008
|
+
resume(): void;
|
|
2009
|
+
/**
|
|
2010
|
+
* Permanently end the runner (AGENTS §10) — a GRACEFUL stop, distinct from `abort`.
|
|
2011
|
+
* Marks the runner `stopping` + `stopped`, then stops the backing queue: every
|
|
2012
|
+
* still-PENDING (never-dispatched) unit is rejected by the queue with its own
|
|
2013
|
+
* "queue is stopped" error, WITHOUT running its handler; every already-in-flight unit
|
|
2014
|
+
* keeps running to completion and settles normally. `#settle` reads `#stopping` to
|
|
2015
|
+
* classify a never-dispatched unit's rejection as a stop artifact (decrement the count
|
|
2016
|
+
* gate, no recorded failure, no fail-fast trip) rather than a genuine failure — a
|
|
2017
|
+
* dispatched unit's rejection while stopping is still a real failure. Idempotent.
|
|
2018
|
+
*/
|
|
2019
|
+
stop(): void;
|
|
1434
2020
|
destroy(): void;
|
|
1435
2021
|
}
|
|
1436
2022
|
|
|
@@ -1523,6 +2109,8 @@ export declare interface RunnerInterface<TInput, TResult> {
|
|
|
1523
2109
|
readonly emitter: EmitterInterface<RunnerEventMap<TResult>>;
|
|
1524
2110
|
readonly active: number;
|
|
1525
2111
|
readonly stopped: boolean;
|
|
2112
|
+
/** Whether the runner is currently paused (AGENTS §10 — resumable, no new dispatch); rides the backing queue's own `paused`. */
|
|
2113
|
+
readonly paused: boolean;
|
|
1526
2114
|
/**
|
|
1527
2115
|
* Run all `inputs` — and anything they `spawn` — to completion; resolve their
|
|
1528
2116
|
* results in order: the declared inputs first (in input order), then the spawned
|
|
@@ -1537,6 +2125,25 @@ export declare interface RunnerInterface<TInput, TResult> {
|
|
|
1537
2125
|
* @returns The units' results, in order (declared first, then spawns)
|
|
1538
2126
|
*/
|
|
1539
2127
|
execute(inputs: readonly TInput[]): Promise<readonly TResult[]>;
|
|
2128
|
+
/**
|
|
2129
|
+
* Inject one more unit into an IN-FLIGHT `execute` run — a LIVE counterpart to a
|
|
2130
|
+
* `Controller.spawn`, called from OUTSIDE any unit's handler (the seam a live
|
|
2131
|
+
* `running` {@link PhaseInterface}'s `add` event lets a subscribed run offer a newly
|
|
2132
|
+
* added task to the SAME execution substrate).
|
|
2133
|
+
*
|
|
2134
|
+
* @remarks
|
|
2135
|
+
* Returns `undefined` synchronously (graceful, non-throwing — AGENTS §12) when the
|
|
2136
|
+
* runner is not currently mid-`execute`, or the run has already fully drained — the
|
|
2137
|
+
* caller reads `undefined` as "not accepted". Otherwise the unit is routed through
|
|
2138
|
+
* the SAME backing queue as a declared/`spawn`ed unit (the runner's
|
|
2139
|
+
* outstanding-unit count gate keeps the in-flight `execute` awaiting it) and emits
|
|
2140
|
+
* the {@link RunnerEventMap.spawn} event; its result promise resolves once the unit
|
|
2141
|
+
* settles.
|
|
2142
|
+
*
|
|
2143
|
+
* @param input - The unit's work payload
|
|
2144
|
+
* @returns The unit's result promise, or `undefined` when no in-flight run can accept it
|
|
2145
|
+
*/
|
|
2146
|
+
spawn(input: TInput): Promise<TResult> | undefined;
|
|
1540
2147
|
/**
|
|
1541
2148
|
* Cancel every in-flight + pending unit (and the backing queue), making a running
|
|
1542
2149
|
* `execute` reject.
|
|
@@ -1544,6 +2151,44 @@ export declare interface RunnerInterface<TInput, TResult> {
|
|
|
1544
2151
|
* @param reason - An optional cancellation reason propagated to every unit's signal
|
|
1545
2152
|
*/
|
|
1546
2153
|
abort(reason?: unknown): void;
|
|
2154
|
+
/**
|
|
2155
|
+
* Suspend dispatch (AGENTS §10 — resumable): the backing queue holds the NEXT dispatch
|
|
2156
|
+
* while any in-flight unit finishes; idempotent.
|
|
2157
|
+
*
|
|
2158
|
+
* @example
|
|
2159
|
+
* ```ts
|
|
2160
|
+
* runner.pause()
|
|
2161
|
+
* runner.paused // true
|
|
2162
|
+
* ```
|
|
2163
|
+
*/
|
|
2164
|
+
pause(): void;
|
|
2165
|
+
/**
|
|
2166
|
+
* Continue a paused runner (AGENTS §10); idempotent.
|
|
2167
|
+
*
|
|
2168
|
+
* @example
|
|
2169
|
+
* ```ts
|
|
2170
|
+
* runner.resume()
|
|
2171
|
+
* runner.paused // false
|
|
2172
|
+
* ```
|
|
2173
|
+
*/
|
|
2174
|
+
resume(): void;
|
|
2175
|
+
/**
|
|
2176
|
+
* Permanently end the runner (AGENTS §10) — a GRACEFUL stop: no further unit is
|
|
2177
|
+
* dispatched, but every already-in-flight unit runs to completion and settles
|
|
2178
|
+
* normally. A never-dispatched (still-pending) unit is rejected by the backing queue
|
|
2179
|
+
* and is NOT recorded as a failure (it never trips fail-fast); a genuine in-flight
|
|
2180
|
+
* failure still is. `execute`'s promise RESOLVES (never rejects) once every unit has
|
|
2181
|
+
* settled, with whatever results actually completed. Idempotent.
|
|
2182
|
+
*
|
|
2183
|
+
* @example
|
|
2184
|
+
* ```ts
|
|
2185
|
+
* const runner = createRunner({ handler: (c) => c.input, concurrency: 1 })
|
|
2186
|
+
* const results = runner.execute([1, 2, 3])
|
|
2187
|
+
* runner.stop() // the in-flight unit finishes; the rest are gracefully dropped
|
|
2188
|
+
* await results // resolves with whatever settled — never rejects
|
|
2189
|
+
* ```
|
|
2190
|
+
*/
|
|
2191
|
+
stop(): void;
|
|
1547
2192
|
/** Tear the runner down — `abort` plus stop the backing queue; idempotent. */
|
|
1548
2193
|
destroy(): void;
|
|
1549
2194
|
}
|
|
@@ -1683,28 +2328,31 @@ export declare interface SchedulerOptions {
|
|
|
1683
2328
|
export declare type SchedulerPriority = 'user' | 'normal' | 'background';
|
|
1684
2329
|
|
|
1685
2330
|
/**
|
|
1686
|
-
* The shape of ONE flat step — `{ name
|
|
2331
|
+
* The shape of ONE flat step — `{ name }` — the building block of
|
|
1687
2332
|
* {@link workflowStepsShape}.
|
|
1688
2333
|
*
|
|
1689
2334
|
* @remarks
|
|
1690
|
-
* `name` is the REGISTERED behavior name the step runs (it becomes the task's `run
|
|
1691
|
-
* `via` is the optional execution mechanism (defaults to `'function'` when omitted). The
|
|
2335
|
+
* `name` is the REGISTERED behavior name the step runs (it becomes the task's `run`). The
|
|
1692
2336
|
* tool expands each step into a one-task phase, in order
|
|
1693
2337
|
* ({@link import('./helpers.js').expandSteps}).
|
|
1694
2338
|
*/
|
|
1695
2339
|
export declare const stepShape: ObjectShape<{
|
|
1696
2340
|
name: StringShape;
|
|
1697
|
-
via: OptionalShape<LiteralShape<readonly ["function", "tool", "agent"]>>;
|
|
1698
2341
|
}>;
|
|
1699
2342
|
|
|
1700
2343
|
/**
|
|
1701
|
-
*
|
|
1702
|
-
*
|
|
2344
|
+
* Box a value as a {@link Success} — the graceful outcome half of a {@link Result}.
|
|
2345
|
+
*
|
|
2346
|
+
* @typeParam T - The boxed value's type
|
|
2347
|
+
* @param value - The value to box
|
|
2348
|
+
* @returns A {@link Success} wrapping `value`
|
|
1703
2349
|
*
|
|
1704
|
-
* @
|
|
1705
|
-
*
|
|
2350
|
+
* @example
|
|
2351
|
+
* ```ts
|
|
2352
|
+
* const result = success(task) // { success: true, value: task }
|
|
2353
|
+
* ```
|
|
1706
2354
|
*/
|
|
1707
|
-
export declare function
|
|
2355
|
+
export declare function success<T>(value: T): Success<T>;
|
|
1708
2356
|
|
|
1709
2357
|
/**
|
|
1710
2358
|
* The live leaf state machine (W-b) for one task — an observable (AGENTS §13), guarded
|
|
@@ -1731,10 +2379,16 @@ export declare function stepToForm(step: WorkflowStep): TaskForm;
|
|
|
1731
2379
|
* matching event strictly AFTER the state change, BEFORE the cascade; the emitter isolates
|
|
1732
2380
|
* a listener throw and routes it to its `error` handler (the `error` option), so a buggy
|
|
1733
2381
|
* observer can never corrupt a transition.
|
|
2382
|
+
* - **Declarative config (AGENTS §12).** `run` / `retries` / `timeout` PERSIST in a
|
|
2383
|
+
* {@link TaskSnapshot} (like a phase's `bail` / `concurrency`), carried verbatim from the
|
|
2384
|
+
* matching {@link import('../types.js').TaskDefinition} / {@link TaskSnapshot} field. `handler`
|
|
2385
|
+
* is the RUNTIME-ONLY counterpart — `run` resolved ONCE at construction against the
|
|
2386
|
+
* workflow-level {@link import('../types.js').WorkflowOptions.functions} registry — and is
|
|
2387
|
+
* NEVER persisted; `undefined` when `run` is omitted or unregistered (the no-handler rule).
|
|
1734
2388
|
*/
|
|
1735
2389
|
export declare class Task implements TaskInterface {
|
|
1736
2390
|
#private;
|
|
1737
|
-
constructor(context: TaskContext, phase: PhaseInterface, workflow: WorkflowInterface, recompute: () => void, options?: TaskOptions, status?: TaskStatus, result?: TaskResult);
|
|
2391
|
+
constructor(context: TaskContext, phase: PhaseInterface, workflow: WorkflowInterface, recompute: () => void, options?: TaskOptions, status?: TaskStatus, result?: TaskResult, run?: string, retries?: number, timeout?: number, handler?: WorkflowFunction);
|
|
1738
2392
|
get emitter(): EmitterInterface<TaskEventMap>;
|
|
1739
2393
|
get id(): string;
|
|
1740
2394
|
get name(): string;
|
|
@@ -1744,11 +2398,31 @@ export declare class Task implements TaskInterface {
|
|
|
1744
2398
|
get workflow(): WorkflowInterface;
|
|
1745
2399
|
get status(): TaskStatus;
|
|
1746
2400
|
get result(): TaskResult | undefined;
|
|
2401
|
+
get run(): string | undefined;
|
|
2402
|
+
get handler(): WorkflowFunction | undefined;
|
|
2403
|
+
get retries(): number | undefined;
|
|
2404
|
+
get timeout(): number | undefined;
|
|
1747
2405
|
start(): void;
|
|
1748
2406
|
complete(value: unknown): void;
|
|
1749
2407
|
fail(error: unknown): void;
|
|
1750
2408
|
skip(): void;
|
|
1751
2409
|
stop(): void;
|
|
2410
|
+
/**
|
|
2411
|
+
* Apply a validated declarative patch to SELF (`name` / `description`).
|
|
2412
|
+
*
|
|
2413
|
+
* @remarks
|
|
2414
|
+
* Defense-in-depth (AGENTS §12): the owning
|
|
2415
|
+
* {@link import('../types.js').TaskManagerInterface.update} gates FIRST (target
|
|
2416
|
+
* exists + `pending`), so this is the second, redundant check — it THROWS a
|
|
2417
|
+
* `MUTATION` {@link WorkflowError} unless this task's own `status` is `pending`.
|
|
2418
|
+
*
|
|
2419
|
+
* @param value - The {@link TaskUpdate} fields to apply
|
|
2420
|
+
* @example
|
|
2421
|
+
* ```ts
|
|
2422
|
+
* task.patch({ name: 'Renamed task' })
|
|
2423
|
+
* ```
|
|
2424
|
+
*/
|
|
2425
|
+
patch(value: TaskUpdate): void;
|
|
1752
2426
|
snapshot(): TaskSnapshot;
|
|
1753
2427
|
}
|
|
1754
2428
|
|
|
@@ -1776,15 +2450,6 @@ export declare const TASK_STATUSES: readonly TaskStatus[];
|
|
|
1776
2450
|
*/
|
|
1777
2451
|
export declare const TASK_TRANSITIONS: Readonly<Record<TaskStatus, readonly TaskStatus[]>>;
|
|
1778
2452
|
|
|
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
2453
|
/**
|
|
1789
2454
|
* The ambient context of a task — its own identity plus a back-reference to the
|
|
1790
2455
|
* phase (and, transitively, the workflow) it belongs to.
|
|
@@ -1862,32 +2527,39 @@ export declare interface TaskControllerInterface {
|
|
|
1862
2527
|
}
|
|
1863
2528
|
|
|
1864
2529
|
/**
|
|
1865
|
-
* The serializable definition of one task — its identity plus
|
|
1866
|
-
*
|
|
2530
|
+
* The serializable definition of one task — its identity plus an optional reference to
|
|
2531
|
+
* the behavior it runs.
|
|
1867
2532
|
*
|
|
1868
2533
|
* @remarks
|
|
1869
2534
|
* 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
|
-
*
|
|
1872
|
-
*
|
|
2535
|
+
* (factories.ts), and it carries NO functions. `id` is the positional identity within
|
|
2536
|
+
* its phase; `name` is the human label; `description` is optional prose. `run` is a
|
|
2537
|
+
* PLAIN NAME — a key resolved ONCE at construction against a workflow-level
|
|
2538
|
+
* {@link WorkflowFunctions} registry into a runtime {@link TaskInterface.handler}
|
|
2539
|
+
* carried on the live task. A task whose `run` is omitted, or whose name is unregistered,
|
|
2540
|
+
* has no handler and AUTO-COMPLETES (the no-handler rule).
|
|
1873
2541
|
*/
|
|
1874
2542
|
export declare interface TaskDefinition {
|
|
1875
2543
|
readonly id: string;
|
|
1876
2544
|
readonly name: string;
|
|
1877
2545
|
readonly description?: string;
|
|
1878
|
-
readonly run
|
|
2546
|
+
readonly run?: string;
|
|
1879
2547
|
/**
|
|
1880
2548
|
* @remarks
|
|
1881
2549
|
* Extra attempts after the first on failure (a non-negative integer); the runner threads it
|
|
1882
2550
|
* to this task's substrate unit, OVERRIDING the phase Runner's `retries` default. Omitted ⇒
|
|
1883
|
-
* the default (no extra attempts).
|
|
2551
|
+
* the default (no extra attempts). PERSISTED in a {@link TaskSnapshot} (like `bail` and
|
|
2552
|
+
* `concurrency`), so `restoreWorkflow(snapshot, { functions })` resumes with the same
|
|
2553
|
+
* reliability config; only the resolved handler itself is runtime-only.
|
|
1884
2554
|
*/
|
|
1885
2555
|
readonly retries?: number;
|
|
1886
2556
|
/**
|
|
1887
2557
|
* @remarks
|
|
1888
2558
|
* The per-attempt deadline in milliseconds (a non-negative integer); the runner threads it to
|
|
1889
2559
|
* this task's substrate unit, OVERRIDING the phase Runner's `timeout` default. Omitted (or a
|
|
1890
|
-
* non-positive value) ⇒ no deadline.
|
|
2560
|
+
* non-positive value) ⇒ no deadline. PERSISTED in a {@link TaskSnapshot} (like `bail` and
|
|
2561
|
+
* `concurrency`), so `restoreWorkflow(snapshot, { functions })` resumes with the same
|
|
2562
|
+
* reliability config; only the resolved handler itself is runtime-only.
|
|
1891
2563
|
*/
|
|
1892
2564
|
readonly timeout?: number;
|
|
1893
2565
|
}
|
|
@@ -1897,6 +2569,12 @@ export declare interface TaskDefinition {
|
|
|
1897
2569
|
* {@link TaskSnapshot} — the per-task leaf step of {@link definitionToSnapshot} (no
|
|
1898
2570
|
* result yet, empty metadata).
|
|
1899
2571
|
*
|
|
2572
|
+
* @remarks
|
|
2573
|
+
* `run` / `retries` / `timeout` carry over verbatim (persisted declarative config, like a
|
|
2574
|
+
* phase's `bail` / `concurrency`) — a restore reinstates the same behavior reference and
|
|
2575
|
+
* reliability overrides once paired with a {@link import('./types.js').WorkflowOptions.functions}
|
|
2576
|
+
* registry.
|
|
2577
|
+
*
|
|
1900
2578
|
* @param task - The task definition to seed from
|
|
1901
2579
|
* @returns An initial {@link TaskSnapshot}
|
|
1902
2580
|
*/
|
|
@@ -1914,7 +2592,8 @@ export declare interface TaskDraft {
|
|
|
1914
2592
|
readonly id?: string;
|
|
1915
2593
|
readonly name?: string;
|
|
1916
2594
|
readonly description?: string;
|
|
1917
|
-
|
|
2595
|
+
/** The behavior reference — a registry key resolved against {@link WorkflowFunctions} at construction; omitted ⇒ no handler. */
|
|
2596
|
+
readonly run?: string;
|
|
1918
2597
|
/** Extra attempts after the first on failure (a non-negative integer); overrides the phase Runner default. Execution-only. */
|
|
1919
2598
|
readonly retries?: number;
|
|
1920
2599
|
/** The per-attempt deadline in milliseconds (a non-negative integer); overrides the phase Runner default. Execution-only. */
|
|
@@ -1928,22 +2607,13 @@ export declare interface TaskDraft {
|
|
|
1928
2607
|
* @remarks
|
|
1929
2608
|
* A PROVIDED `id` / `name` still carries `minLength: 1`, so an explicitly-empty `id: ''`
|
|
1930
2609
|
* is INVALID (rejected by the draft contract), never auto-filled — keeping "garbage"
|
|
1931
|
-
* distinct from "omitted". `run` stays
|
|
2610
|
+
* distinct from "omitted". `run` stays optional, mirroring {@link taskShape}.
|
|
1932
2611
|
*/
|
|
1933
2612
|
export declare const taskDraftShape: ObjectShape<{
|
|
1934
2613
|
id: OptionalShape<StringShape>;
|
|
1935
2614
|
name: OptionalShape<StringShape>;
|
|
1936
2615
|
description: OptionalShape<StringShape>;
|
|
1937
|
-
run:
|
|
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
|
-
}>]>;
|
|
2616
|
+
run: OptionalShape<StringShape>;
|
|
1947
2617
|
retries: OptionalShape<NumberShape>;
|
|
1948
2618
|
timeout: OptionalShape<NumberShape>;
|
|
1949
2619
|
}>;
|
|
@@ -1973,58 +2643,6 @@ export declare type TaskEventMap = {
|
|
|
1973
2643
|
readonly stop: readonly [];
|
|
1974
2644
|
};
|
|
1975
2645
|
|
|
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
2646
|
/** Initial {@link TaskEventMap} listeners — the reserved `on` option (AGENTS §8). */
|
|
2029
2647
|
export declare type TaskHooks = EmitterHooks<TaskEventMap>;
|
|
2030
2648
|
|
|
@@ -2077,11 +2695,52 @@ export declare interface TaskInterface {
|
|
|
2077
2695
|
readonly status: TaskStatus;
|
|
2078
2696
|
/** The recorded outcome once the task settled with one (`completed` / `failed`), else `undefined`. */
|
|
2079
2697
|
readonly result: TaskResult | undefined;
|
|
2698
|
+
/**
|
|
2699
|
+
* The behavior reference — a plain registry key name, PERSISTED (mirrors
|
|
2700
|
+
* {@link TaskDefinition.run} / {@link TaskSnapshot.run}), like {@link PhaseInterface.bail}.
|
|
2701
|
+
* `undefined` when this task has no behavior reference.
|
|
2702
|
+
*/
|
|
2703
|
+
readonly run: string | undefined;
|
|
2704
|
+
/**
|
|
2705
|
+
* The RESOLVED runtime handler — RUNTIME-ONLY, NEVER persisted in a {@link TaskSnapshot}.
|
|
2706
|
+
* Resolved ONCE at construction (build, restore, or a live mint) by looking `run` up in the
|
|
2707
|
+
* workflow-level {@link WorkflowOptions.functions} registry: `functions?.[run]` when `run`
|
|
2708
|
+
* is defined, else `undefined`. A task with no `handler` (an omitted `run`, or a `run` name
|
|
2709
|
+
* absent from the registry) AUTO-COMPLETES (the no-handler rule) — its phase/workflow still
|
|
2710
|
+
* reaches a terminal status, just with no dispatched behavior.
|
|
2711
|
+
*/
|
|
2712
|
+
readonly handler: WorkflowFunction | undefined;
|
|
2713
|
+
/**
|
|
2714
|
+
* Extra attempts after the first on failure — PERSISTED (mirrors {@link TaskDefinition.retries}
|
|
2715
|
+
* / {@link TaskSnapshot.retries}), like {@link PhaseInterface.concurrency}. `undefined` ⇒ none.
|
|
2716
|
+
*/
|
|
2717
|
+
readonly retries: number | undefined;
|
|
2718
|
+
/**
|
|
2719
|
+
* The per-attempt deadline in milliseconds — PERSISTED (mirrors {@link TaskDefinition.timeout}
|
|
2720
|
+
* / {@link TaskSnapshot.timeout}). `undefined` ⇒ no deadline.
|
|
2721
|
+
*/
|
|
2722
|
+
readonly timeout: number | undefined;
|
|
2080
2723
|
start(): void;
|
|
2081
2724
|
complete(value: unknown): void;
|
|
2082
2725
|
fail(error: unknown): void;
|
|
2083
2726
|
skip(): void;
|
|
2084
2727
|
stop(): void;
|
|
2728
|
+
/**
|
|
2729
|
+
* Apply a validated declarative patch to SELF (`name` / `description`).
|
|
2730
|
+
*
|
|
2731
|
+
* @remarks
|
|
2732
|
+
* Defense-in-depth (AGENTS §12): the owning {@link TaskManagerInterface.update} gates
|
|
2733
|
+
* FIRST (target exists + `pending`), so a direct call here is the second, redundant
|
|
2734
|
+
* check — it THROWS a `MUTATION` {@link import('./errors.js').WorkflowError} unless
|
|
2735
|
+
* this task's own `status` is `pending`.
|
|
2736
|
+
*
|
|
2737
|
+
* @param value - The {@link TaskUpdate} fields to apply
|
|
2738
|
+
* @example
|
|
2739
|
+
* ```ts
|
|
2740
|
+
* task.patch({ name: 'Renamed task' })
|
|
2741
|
+
* ```
|
|
2742
|
+
*/
|
|
2743
|
+
patch(value: TaskUpdate): void;
|
|
2085
2744
|
snapshot(): TaskSnapshot;
|
|
2086
2745
|
}
|
|
2087
2746
|
|
|
@@ -2096,6 +2755,11 @@ export declare interface TaskInterface {
|
|
|
2096
2755
|
* `tasks()` lists them in positional order, `count` is the size. A `skip` is a STATUS
|
|
2097
2756
|
* change on a stored task (never a removal), so order survives it; a snapshot RESTORE
|
|
2098
2757
|
* re-`append`s in the snapshot's order, reproducing it exactly.
|
|
2758
|
+
* - **Gated mutation API (AGENTS §12).** `add` / `remove` / `move` / `update` are the
|
|
2759
|
+
* graceful `Result` counterparts to `append`, gating ONLY on the target's OWN
|
|
2760
|
+
* existence/status/id/bounds — a duplicate id, an absent/non-`pending` target, an
|
|
2761
|
+
* out-of-bounds `index`, or a patch that fails {@link taskUpdateShape} validation all
|
|
2762
|
+
* fail gracefully with a `MUTATION` {@link WorkflowError} instead of throwing.
|
|
2099
2763
|
* - **No batch matrix.** A phase's tasks are a fixed positional set, so AGENTS §9.2 (the
|
|
2100
2764
|
* bulk verb overloads) is deliberately omitted — there is no `remove` family here.
|
|
2101
2765
|
* - **Event-free.** A purely structural container — the live {@link TaskInterface}s own
|
|
@@ -2113,6 +2777,10 @@ export declare class TaskManager implements TaskManagerInterface {
|
|
|
2113
2777
|
#private;
|
|
2114
2778
|
get count(): number;
|
|
2115
2779
|
append(task: TaskInterface): void;
|
|
2780
|
+
add(task: TaskInterface, index?: number): Result<TaskInterface, WorkflowError>;
|
|
2781
|
+
remove(id: string): Result<TaskInterface, WorkflowError>;
|
|
2782
|
+
move(id: string, index: number): Result<TaskInterface, WorkflowError>;
|
|
2783
|
+
update(id: string, patch: TaskUpdate): Result<TaskInterface, WorkflowError>;
|
|
2116
2784
|
task(id: string): TaskInterface | undefined;
|
|
2117
2785
|
tasks(): readonly TaskInterface[];
|
|
2118
2786
|
}
|
|
@@ -2126,11 +2794,61 @@ export declare class TaskManager implements TaskManagerInterface {
|
|
|
2126
2794
|
* `append` adds one live {@link TaskInterface} at the end (the build-time wiring path);
|
|
2127
2795
|
* `task(id)` looks one up; `tasks()` lists them in positional order; `count` is the
|
|
2128
2796
|
* 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).
|
|
2797
|
+
* fixed positional set, not a bulk-mutated collection). `add` / `remove` / `move` /
|
|
2798
|
+
* `update` (AGENTS §12) are the GATED mutation counterparts a
|
|
2799
|
+
* {@link PhaseInterface.add} / `remove` / `move` / `update` delegates to AFTER its own
|
|
2800
|
+
* container-status/hook gating — the manager gates ONLY on the target's OWN
|
|
2801
|
+
* existence/status/id/bounds and stays event-free (the entity emits on success).
|
|
2130
2802
|
*/
|
|
2131
2803
|
export declare interface TaskManagerInterface {
|
|
2132
2804
|
readonly count: number;
|
|
2805
|
+
/**
|
|
2806
|
+
* Add `task` at the end (the build-time wiring path).
|
|
2807
|
+
*
|
|
2808
|
+
* @remarks
|
|
2809
|
+
* THROWS a `MUTATION` {@link import('./errors.js').WorkflowError} on a duplicate
|
|
2810
|
+
* `id` (a genuine programmer error — a build-time wiring bug, AGENTS §12) instead of
|
|
2811
|
+
* silently overwriting the existing entry.
|
|
2812
|
+
*
|
|
2813
|
+
* @param task - The live task to append
|
|
2814
|
+
*/
|
|
2133
2815
|
append(task: TaskInterface): void;
|
|
2816
|
+
/**
|
|
2817
|
+
* Insert `task` at `index` (default the end) — the GATED mutation counterpart to
|
|
2818
|
+
* {@link append}: a duplicate `id` or an out-of-bounds `index` fails gracefully
|
|
2819
|
+
* instead of throwing.
|
|
2820
|
+
*
|
|
2821
|
+
* @param task - The live task to insert
|
|
2822
|
+
* @param index - The insertion position (`[0, count]`); omitted inserts at the end
|
|
2823
|
+
* @returns A {@link Result} boxing the inserted task, or a `MUTATION` failure
|
|
2824
|
+
*/
|
|
2825
|
+
add(task: TaskInterface, index?: number): Result<TaskInterface, WorkflowError>;
|
|
2826
|
+
/**
|
|
2827
|
+
* Remove the `pending` task `id`.
|
|
2828
|
+
*
|
|
2829
|
+
* @param id - The task id to remove
|
|
2830
|
+
* @returns A {@link Result} boxing the removed task, or a `MUTATION` failure when
|
|
2831
|
+
* `id` is absent or not `pending`
|
|
2832
|
+
*/
|
|
2833
|
+
remove(id: string): Result<TaskInterface, WorkflowError>;
|
|
2834
|
+
/**
|
|
2835
|
+
* Reposition the `pending` task `id` to `index`.
|
|
2836
|
+
*
|
|
2837
|
+
* @param id - The task id to move
|
|
2838
|
+
* @param index - The destination position (`[0, count)`)
|
|
2839
|
+
* @returns A {@link Result} boxing the moved task, or a `MUTATION` failure when `id`
|
|
2840
|
+
* is absent, not `pending`, or `index` is out of bounds
|
|
2841
|
+
*/
|
|
2842
|
+
move(id: string, index: number): Result<TaskInterface, WorkflowError>;
|
|
2843
|
+
/**
|
|
2844
|
+
* Apply a validated {@link TaskUpdate} patch to the `pending` task `id`.
|
|
2845
|
+
*
|
|
2846
|
+
* @param id - The task id to patch
|
|
2847
|
+
* @param patch - The fields to update
|
|
2848
|
+
* @returns A {@link Result} boxing the patched task, or a `MUTATION` failure when
|
|
2849
|
+
* `id` is absent, not `pending`, or `patch` fails validation
|
|
2850
|
+
*/
|
|
2851
|
+
update(id: string, patch: TaskUpdate): Result<TaskInterface, WorkflowError>;
|
|
2134
2852
|
task(id: string): TaskInterface | undefined;
|
|
2135
2853
|
tasks(): readonly TaskInterface[];
|
|
2136
2854
|
}
|
|
@@ -2141,9 +2859,11 @@ export declare interface TaskManagerInterface {
|
|
|
2141
2859
|
*
|
|
2142
2860
|
* @remarks
|
|
2143
2861
|
* The reserved `on` (AGENTS §8) wires initial {@link TaskEventMap} listeners; a
|
|
2144
|
-
* {@link
|
|
2145
|
-
*
|
|
2146
|
-
*
|
|
2862
|
+
* {@link import('./factories.js').createWorkflow}-built tree threads each level's `on`
|
|
2863
|
+
* from its parent options, the same way a {@link WorkflowInterface.add} /
|
|
2864
|
+
* {@link PhaseInterface.add} mint threads a leaf's `on` from ITS options. `metadata` is
|
|
2865
|
+
* the open consumer bag carried verbatim into a {@link TaskSnapshot} (mirrors
|
|
2866
|
+
* {@link TaskInput.metadata}), never interpreted by the workflow.
|
|
2147
2867
|
*/
|
|
2148
2868
|
export declare interface TaskOptions {
|
|
2149
2869
|
readonly on?: TaskHooks;
|
|
@@ -2180,23 +2900,15 @@ export declare interface TaskResult {
|
|
|
2180
2900
|
}
|
|
2181
2901
|
|
|
2182
2902
|
/**
|
|
2183
|
-
* The shape of a {@link import('./types.js').TaskDefinition} — identity plus
|
|
2184
|
-
* behavior reference (
|
|
2903
|
+
* The shape of a {@link import('./types.js').TaskDefinition} — identity plus an optional
|
|
2904
|
+
* `run` behavior reference (a plain registry-key string, min length 1). `description` is
|
|
2905
|
+
* optional prose.
|
|
2185
2906
|
*/
|
|
2186
2907
|
export declare const taskShape: ObjectShape<{
|
|
2187
2908
|
id: StringShape;
|
|
2188
2909
|
name: StringShape;
|
|
2189
2910
|
description: OptionalShape<StringShape>;
|
|
2190
|
-
run:
|
|
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
|
-
}>]>;
|
|
2911
|
+
run: OptionalShape<StringShape>;
|
|
2200
2912
|
retries: OptionalShape<NumberShape>;
|
|
2201
2913
|
timeout: OptionalShape<NumberShape>;
|
|
2202
2914
|
}>;
|
|
@@ -2209,6 +2921,12 @@ export declare const taskShape: ObjectShape<{
|
|
|
2209
2921
|
* Pure JSON DATA (no class instances, no functions). `result` is the task's
|
|
2210
2922
|
* {@link TaskResult} when it has settled with an outcome, else `undefined`.
|
|
2211
2923
|
* `metadata` is the open consumer bag carried from the task's {@link TaskInput}.
|
|
2924
|
+
* `run` / `retries` / `timeout` are the DECLARATIVE config the task carries — persisted
|
|
2925
|
+
* like a {@link PhaseSnapshot}'s `bail` / `concurrency`, so a restore reinstates the same
|
|
2926
|
+
* behavior reference and reliability overrides (`run` re-resolves against the
|
|
2927
|
+
* {@link WorkflowOptions.functions} registry supplied to
|
|
2928
|
+
* {@link import('./factories.js').restoreWorkflow}); each omitted ⇒ the corresponding
|
|
2929
|
+
* unset default.
|
|
2212
2930
|
*/
|
|
2213
2931
|
export declare interface TaskSnapshot {
|
|
2214
2932
|
readonly id: string;
|
|
@@ -2217,6 +2935,12 @@ export declare interface TaskSnapshot {
|
|
|
2217
2935
|
readonly status: TaskStatus;
|
|
2218
2936
|
readonly result?: TaskResult;
|
|
2219
2937
|
readonly metadata: Readonly<Record<string, unknown>>;
|
|
2938
|
+
/** The behavior reference — a registry key resolved against {@link WorkflowFunctions} on restore/build. */
|
|
2939
|
+
readonly run?: string;
|
|
2940
|
+
/** Extra attempts after the first on failure (a non-negative integer); overrides the phase Runner default. */
|
|
2941
|
+
readonly retries?: number;
|
|
2942
|
+
/** The per-attempt deadline in milliseconds (a non-negative integer); overrides the phase Runner default. */
|
|
2943
|
+
readonly timeout?: number;
|
|
2220
2944
|
}
|
|
2221
2945
|
|
|
2222
2946
|
/**
|
|
@@ -2235,8 +2959,40 @@ export declare interface TaskSnapshot {
|
|
|
2235
2959
|
*/
|
|
2236
2960
|
export declare type TaskStatus = LifecycleStatus;
|
|
2237
2961
|
|
|
2238
|
-
/**
|
|
2239
|
-
|
|
2962
|
+
/**
|
|
2963
|
+
* A declarative partial update to a {@link TaskInterface} — the fields a `pending`
|
|
2964
|
+
* task's {@link TaskInterface.patch} (and the owning {@link TaskManagerInterface.update})
|
|
2965
|
+
* accept, runtime-validated via {@link import('./shapers.js').taskUpdateShape}.
|
|
2966
|
+
*
|
|
2967
|
+
* @remarks
|
|
2968
|
+
* Mirrors the identity fields of {@link TaskDefinition} (`name` / `description`) —
|
|
2969
|
+
* never `run` / `retries` / `timeout` (a form/reliability change is a structural
|
|
2970
|
+
* replace, not a patch) and never `id` (identity is immutable once created). Every
|
|
2971
|
+
* field is optional; an omitted field is left unchanged.
|
|
2972
|
+
*
|
|
2973
|
+
* @example
|
|
2974
|
+
* ```ts
|
|
2975
|
+
* const result = task.phase.tasks.update(task.id, { name: 'Renamed task' })
|
|
2976
|
+
* ```
|
|
2977
|
+
*/
|
|
2978
|
+
export declare interface TaskUpdate {
|
|
2979
|
+
readonly name?: string;
|
|
2980
|
+
readonly description?: string;
|
|
2981
|
+
}
|
|
2982
|
+
|
|
2983
|
+
/**
|
|
2984
|
+
* The shape of a {@link import('./types.js').TaskUpdate} — a partial edit to a
|
|
2985
|
+
* `pending` task's `name` / `description`, both optional.
|
|
2986
|
+
*
|
|
2987
|
+
* @remarks
|
|
2988
|
+
* Mirrors {@link taskShape}'s `name` / `description` constraints exactly (a provided
|
|
2989
|
+
* `name` still has `minLength: 1`); never `id` / `run` / `retries` / `timeout` (those
|
|
2990
|
+
* are not patchable fields, AGENTS §12).
|
|
2991
|
+
*/
|
|
2992
|
+
export declare const taskUpdateShape: ObjectShape<{
|
|
2993
|
+
name: OptionalShape<StringShape>;
|
|
2994
|
+
description: OptionalShape<StringShape>;
|
|
2995
|
+
}>;
|
|
2240
2996
|
|
|
2241
2997
|
/**
|
|
2242
2998
|
* The {@link TaskStatus} values that are TERMINAL — a task in one of these will
|
|
@@ -2295,6 +3051,21 @@ export declare type UnitOutcome<TResult> = {
|
|
|
2295
3051
|
* `start` / `complete` / `fail` / `stop` on a derived-status CHANGE; the emitter isolates a
|
|
2296
3052
|
* listener throw and routes it to its `error` handler (the `error` option); `fail` carries
|
|
2297
3053
|
* the failing task's {@link TaskResult}.
|
|
3054
|
+
* - **Structural API (AGENTS §7).** `add` / `remove` / `move` / `update` gate BEFORE
|
|
3055
|
+
* delegating to {@link phases} (the manager gates the target's own existence/status/id/
|
|
3056
|
+
* bounds), then emit the matching {@link WorkflowEventMap} event on success only. NATIVE,
|
|
3057
|
+
* bottom-up gating (no runner-installed hook): refused outright while this workflow's own
|
|
3058
|
+
* `status` is terminal; otherwise a target position must fall within the PENDING SUFFIX —
|
|
3059
|
+
* the contiguous trailing run of `pending` phases — whose boundary is
|
|
3060
|
+
* {@link import('./helpers.js').deriveBoundary} over the live phases' statuses. A `pending`
|
|
3061
|
+
* workflow's phases are all `pending`, so the boundary is `0` and every position is
|
|
3062
|
+
* naturally accepted.
|
|
3063
|
+
* - **Runtime lifecycle (AGENTS §10).** `pause` / `resume` / `wait` gate execution at the runner's
|
|
3064
|
+
* phase/task boundaries WITHOUT touching {@link status} — `paused` is runtime-only, never
|
|
3065
|
+
* persisted. `destroy` is a terminal teardown: it aborts {@link signal}, `stop`s every
|
|
3066
|
+
* non-terminal live phase (so an engine parked on a phase's own gate unparks and the tree
|
|
3067
|
+
* lands coherent), forces the `stop` override on THIS workflow when not already terminal,
|
|
3068
|
+
* releases any parked {@link wait} waiter, and marks {@link destroyed} — all four idempotent.
|
|
2298
3069
|
*/
|
|
2299
3070
|
export declare class Workflow implements WorkflowInterface {
|
|
2300
3071
|
#private;
|
|
@@ -2305,6 +3076,9 @@ export declare class Workflow implements WorkflowInterface {
|
|
|
2305
3076
|
get description(): string | undefined;
|
|
2306
3077
|
get context(): WorkflowContext;
|
|
2307
3078
|
get bail(): boolean;
|
|
3079
|
+
get paused(): boolean;
|
|
3080
|
+
get destroyed(): boolean;
|
|
3081
|
+
get signal(): AbortSignal;
|
|
2308
3082
|
get status(): WorkflowStatus;
|
|
2309
3083
|
get phases(): PhaseManagerInterface;
|
|
2310
3084
|
phase(id: string): PhaseInterface | undefined;
|
|
@@ -2312,6 +3086,14 @@ export declare class Workflow implements WorkflowInterface {
|
|
|
2312
3086
|
skip(): void;
|
|
2313
3087
|
stop(): void;
|
|
2314
3088
|
complete(): void;
|
|
3089
|
+
pause(): void;
|
|
3090
|
+
resume(): void;
|
|
3091
|
+
destroy(): void;
|
|
3092
|
+
wait(): Promise<void>;
|
|
3093
|
+
add(definition: PhaseDefinition, index?: number): Result<PhaseInterface, WorkflowError>;
|
|
3094
|
+
remove(id: string): Result<PhaseInterface, WorkflowError>;
|
|
3095
|
+
move(id: string, index: number): Result<PhaseInterface, WorkflowError>;
|
|
3096
|
+
update(id: string, patch: PhaseUpdate): Result<PhaseInterface, WorkflowError>;
|
|
2315
3097
|
snapshot(): WorkflowSnapshot;
|
|
2316
3098
|
}
|
|
2317
3099
|
|
|
@@ -2323,24 +3105,28 @@ export declare const WORKFLOW_STATUSES: readonly WorkflowStatus[];
|
|
|
2323
3105
|
* multi-line guide that teaches a small model how to author a complete workflow tree.
|
|
2324
3106
|
*
|
|
2325
3107
|
* @remarks
|
|
2326
|
-
* Presents the SIMPLE flat shape (`{ name, steps: [{ name
|
|
2327
|
-
* one complete worked example ({@link WORKFLOW_TOOL_FLAT_EXAMPLE}), names
|
|
2328
|
-
*
|
|
3108
|
+
* Presents the SIMPLE flat shape (`{ name, steps: [{ name }] }`) as the PRIMARY way with
|
|
3109
|
+
* one complete worked example ({@link WORKFLOW_TOOL_FLAT_EXAMPLE}), names that a step's
|
|
3110
|
+
* `name` is a REGISTERED name (not a human label), and documents the full nested
|
|
2329
3111
|
* {@link WorkflowDefinition} as the ADVANCED form with a minimal example
|
|
2330
3112
|
* ({@link WORKFLOW_TOOL_NESTED_EXAMPLE}). Both examples are interpolated VERBATIM from the
|
|
2331
3113
|
* validated constants, so a parity test pins them — the description can never drift from a
|
|
2332
3114
|
* 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).
|
|
3115
|
+
* schema; the nested form is the documented escape-hatch (the tool accepts both). NOTE: a step's
|
|
3116
|
+
* "registered behavior name" is authored STRUCTURE only —
|
|
3117
|
+
* {@link import('./factories.js').createWorkflowTool} runs the authored tree with no
|
|
3118
|
+
* {@link WorkflowFunctions} registry of its own, so every one of its tasks auto-completes under
|
|
3119
|
+
* the no-handler rule; the tool validates/synthesizes shape, it does not dispatch behavior.
|
|
2334
3120
|
*/
|
|
2335
3121
|
export declare const WORKFLOW_TOOL_DESCRIPTION: string;
|
|
2336
3122
|
|
|
2337
3123
|
/**
|
|
2338
3124
|
* A complete FLAT authoring example — the PRIMARY way a small model authors a workflow
|
|
2339
|
-
* through {@link import('./factories.js').createWorkflowTool}: `{ name, steps: [{ name
|
|
3125
|
+
* through {@link import('./factories.js').createWorkflowTool}: `{ name, steps: [{ name }] }`.
|
|
2340
3126
|
*
|
|
2341
3127
|
* @remarks
|
|
2342
3128
|
* Each step becomes a one-task phase, in order; a step's `name` is a REGISTERED behavior name
|
|
2343
|
-
* (not a label)
|
|
3129
|
+
* (not a label) — the registry key its task's `run` resolves against. The tool expands this
|
|
2344
3130
|
* ({@link import('./helpers.js').expandSteps}) into a valid {@link WorkflowDefinition}. It
|
|
2345
3131
|
* is embedded VERBATIM in {@link WORKFLOW_TOOL_DESCRIPTION} and guarded by a parity test
|
|
2346
3132
|
* (it must expand to a tree the STRICT contract accepts), so the doc example can never drift.
|
|
@@ -2348,16 +3134,16 @@ export declare const WORKFLOW_TOOL_DESCRIPTION: string;
|
|
|
2348
3134
|
export declare const WORKFLOW_TOOL_FLAT_EXAMPLE: WorkflowSteps;
|
|
2349
3135
|
|
|
2350
3136
|
/**
|
|
2351
|
-
* The name under which
|
|
2352
|
-
* depth/cycle-aware workflow tool onto a
|
|
2353
|
-
*
|
|
3137
|
+
* The name under which {@link import('./factories.js').createAgentFunction} BINDS the
|
|
3138
|
+
* depth/cycle-aware workflow tool onto a wrapped agent's `context.tools` (`AgentContextInterface`,
|
|
3139
|
+
* `@orkestrel/agent`).
|
|
2354
3140
|
*
|
|
2355
3141
|
* @remarks
|
|
2356
|
-
* The propagation seam's well-known key:
|
|
3142
|
+
* The propagation seam's well-known key: when its `runner` option is supplied, the adapter adds a
|
|
2357
3143
|
* {@link import('./factories.js').createWorkflowTool}-built tool under this name to the
|
|
2358
|
-
*
|
|
2359
|
-
*
|
|
2360
|
-
*
|
|
3144
|
+
* agent's `context.tools`, so it can author + run a NESTED workflow (bounded by
|
|
3145
|
+
* {@link MAX_WORKFLOW_DEPTH}). An agent that wants to fan out into a workflow calls this tool by
|
|
3146
|
+
* this name; the bound handler runs the nested workflow at depth + 1.
|
|
2361
3147
|
*/
|
|
2362
3148
|
export declare const WORKFLOW_TOOL_NAME = "workflow";
|
|
2363
3149
|
|
|
@@ -2372,32 +3158,6 @@ export declare const WORKFLOW_TOOL_NAME = "workflow";
|
|
|
2372
3158
|
*/
|
|
2373
3159
|
export declare const WORKFLOW_TOOL_NESTED_EXAMPLE: WorkflowDefinition;
|
|
2374
3160
|
|
|
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
3161
|
/**
|
|
2402
3162
|
* The ambient context of a workflow — the identity every level inherits.
|
|
2403
3163
|
*
|
|
@@ -2440,7 +3200,8 @@ export declare interface WorkflowDefinition {
|
|
|
2440
3200
|
* @remarks
|
|
2441
3201
|
* The lenient authoring form `createWorkflowDraftContract` validates and
|
|
2442
3202
|
* {@link import('./helpers.js').completeDraft} completes into a strict
|
|
2443
|
-
* {@link WorkflowDefinition}. `run` stays
|
|
3203
|
+
* {@link WorkflowDefinition}. `run` stays optional (a plain name string); the `bail`
|
|
3204
|
+
* policy carries over.
|
|
2444
3205
|
*/
|
|
2445
3206
|
export declare interface WorkflowDraft {
|
|
2446
3207
|
readonly id?: string;
|
|
@@ -2476,16 +3237,7 @@ export declare const workflowDraftShape: ObjectShape<{
|
|
|
2476
3237
|
id: OptionalShape<StringShape>;
|
|
2477
3238
|
name: OptionalShape<StringShape>;
|
|
2478
3239
|
description: OptionalShape<StringShape>;
|
|
2479
|
-
run:
|
|
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
|
-
}>]>;
|
|
3240
|
+
run: OptionalShape<StringShape>;
|
|
2489
3241
|
retries: OptionalShape<NumberShape>;
|
|
2490
3242
|
timeout: OptionalShape<NumberShape>;
|
|
2491
3243
|
}>>;
|
|
@@ -2540,8 +3292,20 @@ export declare class WorkflowError extends Error {
|
|
|
2540
3292
|
* returning a failure result), and the `@orkestrel/agent` package's `ToolManager`
|
|
2541
3293
|
* ISOLATES the throw into the canonical tool result's top-level `error` (AGENTS §14 — the
|
|
2542
3294
|
* universal tool-handler contract); the error `context` names the wrapped workflow id.
|
|
3295
|
+
* - `MUTATION` — a GATED structural or patch edit was refused: a duplicate id on
|
|
3296
|
+
* `append`/`add`, a target that does not exist or is not `pending`, an out-of-bounds
|
|
3297
|
+
* `index`, a patch that failed shaper validation, or a live structural edit refused by
|
|
3298
|
+
* the NATIVE bottom-up gate — a terminal container, an edit targeting (or destined for)
|
|
3299
|
+
* a position BEFORE the container's own pending-suffix boundary, or (a running phase)
|
|
3300
|
+
* anything other than a pure append. The manager /
|
|
3301
|
+
* entity structural API (AGENTS §12) returns it as a graceful `Result` `failure` —
|
|
3302
|
+
* it NEVER throws for this code except {@link TaskInterface.patch} /
|
|
3303
|
+
* {@link PhaseInterface.patch}'s defense-in-depth self-check and the build-time
|
|
3304
|
+
* {@link TaskManagerInterface.append} / {@link PhaseManagerInterface.append} duplicate-id
|
|
3305
|
+
* guard (both genuine programmer-error paths, AGENTS §12). The error `context` names
|
|
3306
|
+
* the offending id / index / status.
|
|
2543
3307
|
*/
|
|
2544
|
-
export declare type WorkflowErrorCode = 'TRANSITION' | 'RESTORE' | 'DEPTH' | 'TOOL';
|
|
3308
|
+
export declare type WorkflowErrorCode = 'TRANSITION' | 'RESTORE' | 'DEPTH' | 'TOOL' | 'MUTATION';
|
|
2545
3309
|
|
|
2546
3310
|
/**
|
|
2547
3311
|
* The push observation surface (AGENTS §13) of the workflow entity (W-b) — the
|
|
@@ -2552,10 +3316,13 @@ export declare type WorkflowErrorCode = 'TRANSITION' | 'RESTORE' | 'DEPTH' | 'TO
|
|
|
2552
3316
|
* Present-tense events with arg tuples. `start` fires when the workflow begins;
|
|
2553
3317
|
* `complete` when every phase settled successfully; `fail` when a phase failed
|
|
2554
3318
|
* under `bail` (carrying the failing {@link TaskResult}); `stop` when the workflow
|
|
2555
|
-
* was permanently ended.
|
|
2556
|
-
*
|
|
2557
|
-
* AGENTS §
|
|
2558
|
-
*
|
|
3319
|
+
* was permanently ended. `add` / `remove` / `move` / `update` fire on a successful
|
|
3320
|
+
* structural or patch edit through {@link WorkflowInterface.add} / `remove` / `move` /
|
|
3321
|
+
* `update` (AGENTS §7) — never on a refused/gated one. A throwing listener never
|
|
3322
|
+
* reaches the domain surface — the emitter isolates it and routes it to its OWN
|
|
3323
|
+
* `error` handler (the `error` option, AGENTS §13). Declared as a `type` alias (not
|
|
3324
|
+
* `interface extends EventMap`, AGENTS §4.5) so the type-literal satisfies `EventMap`
|
|
3325
|
+
* structurally.
|
|
2559
3326
|
*/
|
|
2560
3327
|
export declare type WorkflowEventMap = {
|
|
2561
3328
|
/** The workflow began — its `id`. */
|
|
@@ -2566,6 +3333,14 @@ export declare type WorkflowEventMap = {
|
|
|
2566
3333
|
readonly fail: readonly [result: TaskResult];
|
|
2567
3334
|
/** The workflow was permanently stopped. */
|
|
2568
3335
|
readonly stop: readonly [];
|
|
3336
|
+
/** A phase was inserted — the inserted phase + its final index. */
|
|
3337
|
+
readonly add: readonly [phase: PhaseInterface, index: number];
|
|
3338
|
+
/** A phase was removed — the removed phase. */
|
|
3339
|
+
readonly remove: readonly [phase: PhaseInterface];
|
|
3340
|
+
/** A phase was repositioned — the moved phase + its new index. */
|
|
3341
|
+
readonly move: readonly [phase: PhaseInterface, index: number];
|
|
3342
|
+
/** A phase was patched — the patched phase. */
|
|
3343
|
+
readonly update: readonly [phase: PhaseInterface];
|
|
2569
3344
|
};
|
|
2570
3345
|
|
|
2571
3346
|
/**
|
|
@@ -2589,10 +3364,10 @@ export declare type WorkflowFunction = (controller: TaskControllerInterface) =>
|
|
|
2589
3364
|
* {@link WorkflowFunction} handlers.
|
|
2590
3365
|
*
|
|
2591
3366
|
* @remarks
|
|
2592
|
-
*
|
|
2593
|
-
*
|
|
2594
|
-
*
|
|
2595
|
-
* lookup, with no lifecycle of its own.
|
|
3367
|
+
* A live {@link TaskInterface} resolves its `run` name against this registry ONCE at
|
|
3368
|
+
* construction into its {@link TaskInterface.handler}. A name absent from the registry (or
|
|
3369
|
+
* an omitted `run`) is the no-handler case — the task AUTO-COMPLETES (the ROADMAP rule). A
|
|
3370
|
+
* plain record (not a manager) — the registry is a lookup, with no lifecycle of its own.
|
|
2596
3371
|
*/
|
|
2597
3372
|
export declare type WorkflowFunctions = Readonly<Record<string, WorkflowFunction>>;
|
|
2598
3373
|
|
|
@@ -2634,13 +3409,182 @@ export declare interface WorkflowInterface {
|
|
|
2634
3409
|
readonly bail: boolean;
|
|
2635
3410
|
readonly status: WorkflowStatus;
|
|
2636
3411
|
readonly phases: PhaseManagerInterface;
|
|
3412
|
+
/**
|
|
3413
|
+
* Whether the workflow is currently paused (AGENTS §10 — resumable); RUNTIME-ONLY —
|
|
3414
|
+
* never a {@link WorkflowStatus}, never persisted in a {@link WorkflowSnapshot} (a
|
|
3415
|
+
* paused workflow's `status` still reports its ordinary `pending` / `running` value).
|
|
3416
|
+
*/
|
|
3417
|
+
readonly paused: boolean;
|
|
3418
|
+
/** Whether {@link destroy} has torn this workflow down; RUNTIME-ONLY, never persisted. */
|
|
3419
|
+
readonly destroyed: boolean;
|
|
3420
|
+
/**
|
|
3421
|
+
* This workflow's own cancellation signal — fires on {@link destroy}. RUNTIME-ONLY
|
|
3422
|
+
* (implemented over `@orkestrel/abort`, AGENTS core precedent), never persisted.
|
|
3423
|
+
*/
|
|
3424
|
+
readonly signal: AbortSignal;
|
|
2637
3425
|
/** Look up one live phase by its `id`. */
|
|
2638
3426
|
phase(id: string): PhaseInterface | undefined;
|
|
2639
3427
|
/** Every settled task's result across all phases, in positional order — the workflow tier of the result tree. */
|
|
2640
3428
|
results(): readonly TaskResult[];
|
|
3429
|
+
/**
|
|
3430
|
+
* FORCE this workflow to `skipped` (AGENTS §10), overriding the derived value; idempotent.
|
|
3431
|
+
*
|
|
3432
|
+
* @remarks
|
|
3433
|
+
* A NO-OP once `status` is already terminal — a settled workflow cannot be re-forced.
|
|
3434
|
+
* Always releases a parked {@link wait} waiter regardless (a terminal workflow has nothing
|
|
3435
|
+
* left to pause for).
|
|
3436
|
+
*/
|
|
2641
3437
|
skip(): void;
|
|
3438
|
+
/**
|
|
3439
|
+
* FORCE this workflow to `stopped` (AGENTS §10), overriding the derived value; idempotent.
|
|
3440
|
+
*
|
|
3441
|
+
* @remarks
|
|
3442
|
+
* A NO-OP once `status` is already terminal — a settled workflow cannot be re-forced. Always
|
|
3443
|
+
* releases a parked {@link wait} waiter regardless (a terminal workflow has nothing left to
|
|
3444
|
+
* pause for).
|
|
3445
|
+
*/
|
|
2642
3446
|
stop(): void;
|
|
3447
|
+
/**
|
|
3448
|
+
* FORCE this workflow to `completed` (AGENTS §10), overriding the derived value.
|
|
3449
|
+
*
|
|
3450
|
+
* @remarks
|
|
3451
|
+
* A NO-OP unless `status` is `pending` — its ONLY legitimate use is settling a vacuously
|
|
3452
|
+
* DONE tree (no work happened), mirroring the runner's own gate. Never overrides a real
|
|
3453
|
+
* `completed` / a bail-true `failed` / a `stopped` / a derived `skipped`.
|
|
3454
|
+
*/
|
|
2643
3455
|
complete(): void;
|
|
3456
|
+
/**
|
|
3457
|
+
* Suspend the workflow (AGENTS §10 — resumable); idempotent.
|
|
3458
|
+
*
|
|
3459
|
+
* @remarks
|
|
3460
|
+
* A no-op when already `paused`, when `status` is terminal, or once {@link destroyed}.
|
|
3461
|
+
* RUNTIME-ONLY (AGENTS §10) — never a {@link WorkflowStatus}, never persisted in a
|
|
3462
|
+
* {@link WorkflowSnapshot}. A driving {@link WorkflowRunnerInterface.execute} gates at the
|
|
3463
|
+
* next phase boundary and before each task's own dispatch; an in-flight task body is
|
|
3464
|
+
* never suspended mid-flight. **Pausing does NOT suspend the run's timeout / budget /
|
|
3465
|
+
* abort clocks** — those bounds keep ticking while paused, so a run parked on
|
|
3466
|
+
* `pause()` can still be cancelled (and settle `stopped`) by its own deadline / budget /
|
|
3467
|
+
* abort while parked.
|
|
3468
|
+
*
|
|
3469
|
+
* @example
|
|
3470
|
+
* ```ts
|
|
3471
|
+
* workflow.pause()
|
|
3472
|
+
* workflow.paused // true
|
|
3473
|
+
* ```
|
|
3474
|
+
*/
|
|
3475
|
+
pause(): void;
|
|
3476
|
+
/**
|
|
3477
|
+
* Continue a paused workflow (AGENTS §10); idempotent — a no-op unless {@link paused}.
|
|
3478
|
+
*
|
|
3479
|
+
* @example
|
|
3480
|
+
* ```ts
|
|
3481
|
+
* workflow.resume()
|
|
3482
|
+
* workflow.paused // false
|
|
3483
|
+
* ```
|
|
3484
|
+
*/
|
|
3485
|
+
resume(): void;
|
|
3486
|
+
/**
|
|
3487
|
+
* Tear this workflow down (AGENTS §10) — a TERMINAL teardown: aborts {@link signal},
|
|
3488
|
+
* `stop`s every non-terminal live phase (so any engine parked on a phase's own gate
|
|
3489
|
+
* unparks and the tree lands coherent), forces the `stop` override on THIS workflow if
|
|
3490
|
+
* it is not already terminal, resolves any parked {@link wait} waiter, and marks
|
|
3491
|
+
* {@link destroyed}; idempotent.
|
|
3492
|
+
*
|
|
3493
|
+
* @remarks
|
|
3494
|
+
* After `destroy`, every structural mutator (`add` / `remove` / `move` / `update` /
|
|
3495
|
+
* `patch`) and `pause` / `resume` reject (a `Result` failure) or no-op — never throws
|
|
3496
|
+
* for calling `destroy` itself twice.
|
|
3497
|
+
*
|
|
3498
|
+
* @example
|
|
3499
|
+
* ```ts
|
|
3500
|
+
* workflow.destroy()
|
|
3501
|
+
* workflow.destroyed // true
|
|
3502
|
+
* ```
|
|
3503
|
+
*/
|
|
3504
|
+
destroy(): void;
|
|
3505
|
+
/**
|
|
3506
|
+
* Park until this workflow is not paused — **promise-parked**, never a timer or
|
|
3507
|
+
* busy-loop (AGENTS §21; mirrors {@link ControllerInterface.wait}'s doc style).
|
|
3508
|
+
*
|
|
3509
|
+
* @remarks
|
|
3510
|
+
* Resolves IMMEDIATELY when not {@link paused}. While paused, parks until `resume` /
|
|
3511
|
+
* `skip` / `stop` / `destroy` — each always releases a parked waiter (a permanently
|
|
3512
|
+
* ended workflow has nothing left to pause for). NEVER rejects.
|
|
3513
|
+
*
|
|
3514
|
+
* @returns A promise that resolves once the workflow is no longer paused
|
|
3515
|
+
*/
|
|
3516
|
+
wait(): Promise<void>;
|
|
3517
|
+
/**
|
|
3518
|
+
* MINT a live {@link PhaseInterface} (and its tasks) from `definition` and insert it
|
|
3519
|
+
* into this workflow (AGENTS §7 the entity structural API) — gated BEFORE delegating
|
|
3520
|
+
* to {@link phases}' manager.
|
|
3521
|
+
*
|
|
3522
|
+
* @remarks
|
|
3523
|
+
* Converts `definition` → {@link PhaseSnapshot} and constructs the live phase (wired to
|
|
3524
|
+
* THIS workflow, its recompute cascade, and its emitter hooks) plus each of its live
|
|
3525
|
+
* tasks — each task's `run` / `retries` / `timeout` carried from its {@link TaskDefinition}
|
|
3526
|
+
* and its {@link TaskInterface.handler} resolved against the workflow-level
|
|
3527
|
+
* {@link WorkflowOptions.functions} registry (mirrors
|
|
3528
|
+
* {@link import('./factories.js').createWorkflow}'s build-time resolution). The
|
|
3529
|
+
* phase's effective `bail` resolves exactly as the build path does
|
|
3530
|
+
* (`definition.bail ?? this.bail`). Requires `definition.id` to be UNIQUE among this
|
|
3531
|
+
* workflow's existing phase ids — a duplicate is a `MUTATION` failure (mirrors
|
|
3532
|
+
* {@link PhaseManagerInterface.add}'s own duplicate-id gate).
|
|
3533
|
+
*
|
|
3534
|
+
* NATIVE gating, purely from this workflow's own derived `status` and the phase list's
|
|
3535
|
+
* positions (AGENTS §12 — no runner-installed hook), UNCHANGED from the entity-taking
|
|
3536
|
+
* predecessor: refused outright while this workflow's own `status` is terminal or once
|
|
3537
|
+
* {@link destroyed}. Otherwise the effective target position (`index ?? phases.count`)
|
|
3538
|
+
* must fall within the PENDING SUFFIX — the contiguous trailing run of `pending` phases
|
|
3539
|
+
* (phases run sequentially, so every already-started phase forms a contiguous leading
|
|
3540
|
+
* prefix); its boundary is {@link import('./helpers.js').deriveBoundary}. A `pending`
|
|
3541
|
+
* workflow's phases are ALL `pending`, so the boundary is `0` and every index is
|
|
3542
|
+
* naturally accepted — no special case needed. Delegates the minted phase to
|
|
3543
|
+
* {@link PhaseManagerInterface.add} then emits `add` on success.
|
|
3544
|
+
*
|
|
3545
|
+
* @param definition - The {@link PhaseDefinition} to mint a live phase (and tasks) from
|
|
3546
|
+
* @param index - The insertion position; omitted inserts at the end
|
|
3547
|
+
* @returns A {@link Result} boxing the minted, inserted phase, or a `MUTATION` failure
|
|
3548
|
+
*/
|
|
3549
|
+
add(definition: PhaseDefinition, index?: number): Result<PhaseInterface, WorkflowError>;
|
|
3550
|
+
/**
|
|
3551
|
+
* Remove the `pending` phase `id` from this workflow.
|
|
3552
|
+
*
|
|
3553
|
+
* @remarks
|
|
3554
|
+
* NATIVE gating: refused while this workflow's own `status` is terminal. Otherwise the
|
|
3555
|
+
* target must exist at an index within the pending suffix (at or past
|
|
3556
|
+
* {@link import('./helpers.js').deriveBoundary}) — the manager separately gates the
|
|
3557
|
+
* target's own `pending` status (AGENTS §9).
|
|
3558
|
+
*
|
|
3559
|
+
* @param id - The phase id to remove
|
|
3560
|
+
* @returns A {@link Result} boxing the removed phase, or a `MUTATION` failure
|
|
3561
|
+
*/
|
|
3562
|
+
remove(id: string): Result<PhaseInterface, WorkflowError>;
|
|
3563
|
+
/**
|
|
3564
|
+
* Reposition the `pending` phase `id` to `index` within this workflow.
|
|
3565
|
+
*
|
|
3566
|
+
* @remarks
|
|
3567
|
+
* NATIVE gating: refused while this workflow's own `status` is terminal. Otherwise BOTH
|
|
3568
|
+
* the target's current index and the destination `index` must fall within the pending
|
|
3569
|
+
* suffix (see {@link remove}).
|
|
3570
|
+
*
|
|
3571
|
+
* @param id - The phase id to move
|
|
3572
|
+
* @param index - The destination position
|
|
3573
|
+
* @returns A {@link Result} boxing the moved phase, or a `MUTATION` failure
|
|
3574
|
+
*/
|
|
3575
|
+
move(id: string, index: number): Result<PhaseInterface, WorkflowError>;
|
|
3576
|
+
/**
|
|
3577
|
+
* Apply a validated {@link PhaseUpdate} patch to the `pending` phase `id` in this workflow.
|
|
3578
|
+
*
|
|
3579
|
+
* @remarks
|
|
3580
|
+
* NATIVE gating: refused while this workflow's own `status` is terminal. Otherwise the
|
|
3581
|
+
* target must exist at an index within the pending suffix (see {@link remove}).
|
|
3582
|
+
*
|
|
3583
|
+
* @param id - The phase id to patch
|
|
3584
|
+
* @param patch - The fields to update
|
|
3585
|
+
* @returns A {@link Result} boxing the patched phase, or a `MUTATION` failure
|
|
3586
|
+
*/
|
|
3587
|
+
update(id: string, patch: PhaseUpdate): Result<PhaseInterface, WorkflowError>;
|
|
2644
3588
|
snapshot(): WorkflowSnapshot;
|
|
2645
3589
|
}
|
|
2646
3590
|
|
|
@@ -2670,6 +3614,19 @@ export declare interface WorkflowOptions {
|
|
|
2670
3614
|
readonly error?: EmitterErrorHandler;
|
|
2671
3615
|
/** Per-phase {@link PhaseOptions}, keyed by the phase's `id`. */
|
|
2672
3616
|
readonly phases?: Readonly<Record<string, PhaseOptions>>;
|
|
3617
|
+
/**
|
|
3618
|
+
* The `function`-task behavior registry ({@link WorkflowFunctions}) each live task's
|
|
3619
|
+
* {@link TaskDefinition.run} / {@link TaskSnapshot.run} name resolves against ONCE at
|
|
3620
|
+
* construction into its runtime {@link TaskInterface.handler} — the SAME registry a
|
|
3621
|
+
* fresh build ({@link import('./factories.js').createWorkflow}) and a restore
|
|
3622
|
+
* ({@link import('./factories.js').restoreWorkflow}) both consume, and the same shape a
|
|
3623
|
+
* live {@link WorkflowInterface.add} / {@link PhaseInterface.add} mint resolves a newly
|
|
3624
|
+
* minted task against. A `run` name absent from `functions` (or omitted entirely)
|
|
3625
|
+
* resolves to no handler — that task AUTO-COMPLETES (the no-handler rule): its
|
|
3626
|
+
* phase/workflow still reaches a terminal status, just with no dispatched behavior.
|
|
3627
|
+
* Omitted ⇒ an empty registry (every task auto-completes).
|
|
3628
|
+
*/
|
|
3629
|
+
readonly functions?: WorkflowFunctions;
|
|
2673
3630
|
}
|
|
2674
3631
|
|
|
2675
3632
|
/**
|
|
@@ -2695,35 +3652,51 @@ export declare interface WorkflowResult {
|
|
|
2695
3652
|
|
|
2696
3653
|
/**
|
|
2697
3654
|
* The thin orchestrator that EXECUTES a live W-b workflow tree by COMPOSING the shipped
|
|
2698
|
-
* substrate — phases sequential, tasks concurrent — dispatching each task
|
|
2699
|
-
*
|
|
3655
|
+
* substrate — phases sequential, tasks concurrent — dispatching each task through its OWN
|
|
3656
|
+
* resolved handler under the `bail` policy.
|
|
2700
3657
|
*
|
|
2701
3658
|
* @remarks
|
|
2702
3659
|
* - **Composes, never re-implements.** Per-phase bounded concurrency is one
|
|
2703
3660
|
* {@link createRunner} per phase (the substrate {@link RunnerInterface} over the workers
|
|
2704
3661
|
* `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} +
|
|
3662
|
+
* timeout / budget / entity `signal` fold through {@link createAbort} / {@link createTimeout} +
|
|
2706
3663
|
* `AbortSignal.any` (exactly as the agent runtime folds its bounds); pacing is the shipped
|
|
2707
3664
|
* {@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
|
|
2709
|
-
*
|
|
2710
|
-
*
|
|
2711
|
-
*
|
|
2712
|
-
*
|
|
2713
|
-
*
|
|
2714
|
-
*
|
|
2715
|
-
*
|
|
2716
|
-
*
|
|
2717
|
-
*
|
|
2718
|
-
*
|
|
2719
|
-
*
|
|
2720
|
-
*
|
|
2721
|
-
*
|
|
2722
|
-
*
|
|
2723
|
-
*
|
|
2724
|
-
*
|
|
2725
|
-
*
|
|
2726
|
-
*
|
|
3665
|
+
* its own — it only sequences phases, dispatches a task's own handler, and drives the live
|
|
3666
|
+
* entity.
|
|
3667
|
+
* - **Pure engine — no registries, no tool/agent knowledge.** The runner carries no
|
|
3668
|
+
* `functions` / `tools` / `agents` registry: each live {@link TaskInterface} already
|
|
3669
|
+
* resolved its own {@link import('./types.js').WorkflowFunction} into
|
|
3670
|
+
* {@link import('./types.js').TaskInterface.handler} ONCE at construction (build, restore,
|
|
3671
|
+
* or a live mint all resolve it identically, from {@link WorkflowOptions.functions}), so
|
|
3672
|
+
* dispatch is simply "invoke the task's own handler". Static tool / agent calling is an
|
|
3673
|
+
* OPT-IN concern of `factories.ts`'s adapter factories ({@link import('./factories.js').createToolFunction},
|
|
3674
|
+
* {@link import('./factories.js').createAgentFunction}) — plain {@link import('./types.js').WorkflowFunction}s a
|
|
3675
|
+
* caller wires into {@link WorkflowOptions.functions} like any other behavior. This module
|
|
3676
|
+
* never imports `@orkestrel/agent`.
|
|
3677
|
+
* - **Two `execute` forms, one engine.** `execute(definition, options)` BUILDS the live tree
|
|
3678
|
+
* from a {@link WorkflowDefinition} (single source of truth for the `run` / `concurrency`
|
|
3679
|
+
* metadata); `execute(workflow, options)` DRIVES a caller-owned, ALREADY-BUILT
|
|
3680
|
+
* {@link WorkflowInterface} instead — the entity-native control surface (AGENTS §10:
|
|
3681
|
+
* `pause` / `resume` / `add` / `stop` / `destroy` live on the entity itself). Both forms
|
|
3682
|
+
* converge on the SAME `#execute` engine: neither reads a `WorkflowDefinition` once the tree
|
|
3683
|
+
* exists — `#runTask` reads each task's OWN {@link import('./types.js').TaskInterface.handler}
|
|
3684
|
+
* / `retries` / `timeout`, and `#runPhase` reads each phase's OWN
|
|
3685
|
+
* {@link PhaseInterface.concurrency} / `bail`, so a live `add`-minted phase or task (V5)
|
|
3686
|
+
* runs under EXACTLY the same rules as one built from the original definition.
|
|
3687
|
+
* - **Phases sequential, tasks concurrent — LIVE continuity.** `#execute` drives the phases in
|
|
3688
|
+
* order, RE-READING `workflow.phases.phases()` every iteration (a cursor over the live
|
|
3689
|
+
* manager, not a one-time snapshot) so a caller's `workflow.add(phaseDefinition)` mid-run is
|
|
3690
|
+
* picked up. Within a phase, `#runPhase` subscribes to that phase's `add` event BEFORE
|
|
3691
|
+
* capturing its task list, then `spawn`s any task added mid-phase onto the SAME substrate
|
|
3692
|
+
* Runner (so it is actually dispatched, under the same `concurrency`); a task added too late
|
|
3693
|
+
* for `spawn` to accept (the runner already drained) is swept `skip`ped afterward so the
|
|
3694
|
+
* phase always reaches a coherent terminal state.
|
|
3695
|
+
* - **Dispatch by handler.** `#runTask` invokes the live task's own
|
|
3696
|
+
* {@link import('./types.js').TaskInterface.handler} directly: `undefined` (an omitted `run`,
|
|
3697
|
+
* or a `run` name absent from the {@link WorkflowOptions.functions} registry it was resolved
|
|
3698
|
+
* against) AUTO-COMPLETES — the ROADMAP no-handler rule; otherwise the handler runs with the
|
|
3699
|
+
* task's {@link import('./types.js').TaskControllerInterface} handle.
|
|
2727
3700
|
* - **`bail` → substrate.** Under `bail: true` (halt) a genuine task failure `fail`s the leaf
|
|
2728
3701
|
* THEN re-throws, so the substrate Runner fail-fasts — it aborts the in-flight siblings
|
|
2729
3702
|
* (their `controller.signal` fires; a mid-flight sibling `skip`s) and rejects the phase run;
|
|
@@ -2731,7 +3704,20 @@ export declare interface WorkflowResult {
|
|
|
2731
3704
|
* Under `bail: false` (graceful) a failure `fail`s the leaf and RESOLVES (never throws), so
|
|
2732
3705
|
* the Runner settles every unit (allSettled) and the run finishes (the workflow derives
|
|
2733
3706
|
* `completed`, the failure recorded in the result tree).
|
|
2734
|
-
* - **
|
|
3707
|
+
* - **Pause / stop / destroy gates.** `workflow.pause()` is honoured at exactly two points —
|
|
3708
|
+
* the next phase boundary (workflow-only) and each task's own pre-dispatch (before
|
|
3709
|
+
* `task.start()`, workflow gate FIRST then this task's own `phase.pause()`) — by parking on
|
|
3710
|
+
* {@link WorkflowInterface.wait} / {@link PhaseInterface.wait}; an in-flight task body is
|
|
3711
|
+
* NEVER suspended mid-flight. A GRACEFUL `workflow.stop()` (no signal involved) is caught at
|
|
3712
|
+
* those same gates: not-yet-started work is `skip`ped, in-flight work finishes naturally. A
|
|
3713
|
+
* HARD `workflow.destroy()` aborts {@link WorkflowInterface.signal}, which `#fold` has folded
|
|
3714
|
+
* into the run's composed signal — so it cancels the active phase Runner (and every
|
|
3715
|
+
* in-flight task) exactly like an external abort / timeout / budget fire. EVERY park on a
|
|
3716
|
+
* `wait()` gate is RACED against that same run signal (`#raceWait`, S2) — so a cancel firing
|
|
3717
|
+
* WHILE parked unparks the engine promptly instead of hanging until `resume`; the existing
|
|
3718
|
+
* halt / abort re-checks after the gate then decide the outcome.
|
|
3719
|
+
* - **Abort / Timeout / Budget / entity-signal fold.** `#execute` folds the live workflow's
|
|
3720
|
+
* own {@link WorkflowInterface.signal}, the run's external `signal`, a
|
|
2735
3721
|
* {@link TimeoutInterface}, and the `@orkestrel/budget` package's `BudgetInterface`'s
|
|
2736
3722
|
* `signal` into one `runSignal` (`AbortSignal.any`); a fire aborts the active phase's Runner
|
|
2737
3723
|
* (cancelling every in-flight task) and HALTS the run — the remaining tasks / phases `skip`
|
|
@@ -2739,19 +3725,77 @@ export declare interface WorkflowResult {
|
|
|
2739
3725
|
* {@link TaskController} signal `AbortSignal.any`-combines the substrate per-unit signal with
|
|
2740
3726
|
* `runSignal`, so a handler observes either cause directly.
|
|
2741
3727
|
* - **Re-entrant-safe.** No shared per-run mutable field: the active-Runner holder is LOCAL to
|
|
2742
|
-
* each `#execute`, so a nested `execute` (
|
|
2743
|
-
* while the outer run is suspended
|
|
3728
|
+
* each `#execute`, so a nested `execute` (a bound workflow-tool handler re-entering this
|
|
3729
|
+
* instance while the outer run is suspended awaiting it) cannot clobber the outer run's state.
|
|
2744
3730
|
*/
|
|
2745
3731
|
export declare class WorkflowRunner implements WorkflowRunnerInterface {
|
|
2746
3732
|
#private;
|
|
2747
|
-
constructor(
|
|
3733
|
+
constructor(scheduler: SchedulerInterface);
|
|
3734
|
+
/**
|
|
3735
|
+
* Execute a workflow definition to completion — BUILD its live tree, run the phases
|
|
3736
|
+
* sequentially with each phase's tasks concurrent — resolving its terminal
|
|
3737
|
+
* {@link WorkflowResult} (whose `workflow` is the freshly-built live tree).
|
|
3738
|
+
*
|
|
3739
|
+
* @remarks
|
|
3740
|
+
* One-shot. The runner BUILDS the live tree from `definition` internally (one source of
|
|
3741
|
+
* truth — the per-task `run` and per-phase `concurrency` come from the same definition
|
|
3742
|
+
* the tree is constructed from, so the executed tree can never drift from the metadata).
|
|
3743
|
+
* The {@link WorkflowOptions} part of `options` (initial `on` listeners, a `bail` override,
|
|
3744
|
+
* the per-node `phases` bag, the {@link WorkflowOptions.functions} registry each task's
|
|
3745
|
+
* `run` resolves against) is forwarded to the build. Under `bail: false` (graceful) every
|
|
3746
|
+
* task settles (a failure is recorded on its {@link TaskInterface}) and the workflow
|
|
3747
|
+
* reaches `completed`; under `bail: true` (halt) the first failure aborts the in-flight
|
|
3748
|
+
* sibling tasks AND `skip`s the remaining tasks / phases, settling the workflow `failed`. A
|
|
3749
|
+
* {@link WorkflowRunOptions} abort / timeout / budget fires every in-flight task's signal
|
|
3750
|
+
* and `stop`s the run. `execute` resolves (never rejects) on a cancel — the partial outcome
|
|
3751
|
+
* is read from the returned {@link WorkflowResult} (its `workflow` / `status` / `results`).
|
|
3752
|
+
*
|
|
3753
|
+
* @param definition - The {@link WorkflowDefinition} to build the live tree from and drive
|
|
3754
|
+
* @param options - The construction options ({@link WorkflowOptions}: `on` / `bail` /
|
|
3755
|
+
* `phases` / `functions`) PLUS the per-run bounds (`signal` / `timeout` / `budget`)
|
|
3756
|
+
* @returns The run's terminal {@link WorkflowResult} (its `workflow` is the built tree)
|
|
3757
|
+
* @example
|
|
3758
|
+
* ```ts
|
|
3759
|
+
* const result = await runner.execute(definition, { timeout: 5_000 })
|
|
3760
|
+
* result.status // 'completed' | 'failed' | 'stopped'
|
|
3761
|
+
* ```
|
|
3762
|
+
*/
|
|
2748
3763
|
execute(definition: WorkflowDefinition, options?: WorkflowRunOptions): Promise<WorkflowResult>;
|
|
3764
|
+
/**
|
|
3765
|
+
* Drive an ALREADY-BUILT, CALLER-OWNED live {@link WorkflowInterface} — the entity-native
|
|
3766
|
+
* counterpart to the definition-building {@link execute} overload.
|
|
3767
|
+
*
|
|
3768
|
+
* @remarks
|
|
3769
|
+
* `createWorkflow` mints the live tree, this overload drives it, and the caller controls
|
|
3770
|
+
* the SAME entity mid-run via its own `pause` / `resume` / `add` / `stop` / `destroy`
|
|
3771
|
+
* (AGENTS §10). Requires `workflow.status === 'pending'` and `!workflow.destroyed` —
|
|
3772
|
+
* otherwise this is a programmer-timing error and it THROWS a `TRANSITION`
|
|
3773
|
+
* {@link WorkflowError} (AGENTS §12) rather than silently no-opping or building a second
|
|
3774
|
+
* tree. Once accepted, observable semantics are byte-identical to the `definition` form —
|
|
3775
|
+
* except the phase loop RE-READS the live tree every iteration, so a caller's live `add`
|
|
3776
|
+
* mid-run is picked up and actually dispatched. `options` carries only the per-run bounds
|
|
3777
|
+
* (`signal` / `timeout` / `budget`) — the construction half of {@link WorkflowRunOptions}
|
|
3778
|
+
* does not apply, since the tree already exists.
|
|
3779
|
+
*
|
|
3780
|
+
* @param workflow - The live {@link WorkflowInterface} to drive
|
|
3781
|
+
* @param options - The per-run bounds (`signal` / `timeout` / `budget`)
|
|
3782
|
+
* @returns The run's terminal {@link WorkflowResult} (its `workflow` is the SAME entity passed in)
|
|
3783
|
+
* @example
|
|
3784
|
+
* ```ts
|
|
3785
|
+
* const workflow = createWorkflow(definition)
|
|
3786
|
+
* const run = runner.execute(workflow)
|
|
3787
|
+
* workflow.pause()
|
|
3788
|
+
* workflow.resume()
|
|
3789
|
+
* await run
|
|
3790
|
+
* ```
|
|
3791
|
+
*/
|
|
3792
|
+
execute(workflow: WorkflowInterface, options?: Omit<WorkflowRunOptions, keyof WorkflowOptions>): Promise<WorkflowResult>;
|
|
2749
3793
|
}
|
|
2750
3794
|
|
|
2751
3795
|
/**
|
|
2752
3796
|
* A thin orchestrator that EXECUTES a live {@link WorkflowInterface} tree by composing the
|
|
2753
|
-
* shipped substrate — phases sequential, tasks concurrent,
|
|
2754
|
-
* `bail` policy.
|
|
3797
|
+
* shipped substrate — phases sequential, tasks concurrent, each task dispatched through its
|
|
3798
|
+
* OWN resolved handler under the `bail` policy.
|
|
2755
3799
|
*
|
|
2756
3800
|
* @remarks
|
|
2757
3801
|
* `execute(definition, options?)` BUILDS the live W-b entity tree from the definition itself
|
|
@@ -2760,18 +3804,22 @@ export declare class WorkflowRunner implements WorkflowRunnerInterface {
|
|
|
2760
3804
|
* through ONE substrate {@link RunnerInterface} (concurrency =
|
|
2761
3805
|
* the phase's {@link PhaseDefinition.concurrency}). The definition is the SINGLE source of
|
|
2762
3806
|
* truth: the runner owns both the declarative state (the live tree it constructs) and the
|
|
2763
|
-
* EXECUTION-ONLY
|
|
2764
|
-
*
|
|
2765
|
-
*
|
|
2766
|
-
*
|
|
2767
|
-
* {@link
|
|
2768
|
-
*
|
|
2769
|
-
* → `complete` / `fail`), never
|
|
2770
|
-
* substrate's fail-fast (`bail: true`
|
|
2771
|
-
* the rest) vs settle-all (`bail:
|
|
2772
|
-
*
|
|
2773
|
-
* listeners, a `bail` override,
|
|
2774
|
-
* per
|
|
3807
|
+
* EXECUTION-ONLY field the snapshot deliberately dropped — each task's `run` (resolved into
|
|
3808
|
+
* its {@link TaskInterface.handler} once at construction, against
|
|
3809
|
+
* {@link WorkflowOptions.functions}) and each phase's `concurrency` (so there is no
|
|
3810
|
+
* separately-supplied workflow to drift from the definition). The freshly-built live tree is
|
|
3811
|
+
* returned in {@link WorkflowResult.workflow}. The runner carries NO registry of its own — it
|
|
3812
|
+
* simply invokes each task's OWN {@link TaskInterface.handler}; a task with no handler
|
|
3813
|
+
* AUTO-COMPLETES. The runner DRIVES the live entity (`start` → `complete` / `fail`), never
|
|
3814
|
+
* re-implementing status. The `bail` policy maps onto the substrate's fail-fast (`bail: true`
|
|
3815
|
+
* — the first failure aborts in-flight siblings and skips the rest) vs settle-all (`bail:
|
|
3816
|
+
* false` — failures are recorded and the run finishes). The {@link WorkflowOptions} half of
|
|
3817
|
+
* the options is forwarded to `createWorkflow` (initial listeners, a `bail` override,
|
|
3818
|
+
* per-node options, the `functions` registry); the Abort / Timeout / Budget bounds fold per
|
|
3819
|
+
* run via `AbortSignal.any`, halting the run and `stop`ping the workflow. A second
|
|
3820
|
+
* `execute(workflow, options?)` overload drives a CALLER-BUILT live tree instead — the
|
|
3821
|
+
* entity-native control surface (AGENTS §10: `pause` / `resume` / `add` / `stop` /
|
|
3822
|
+
* `destroy` live on {@link WorkflowInterface} itself); see its own doc for details.
|
|
2775
3823
|
*/
|
|
2776
3824
|
export declare interface WorkflowRunnerInterface {
|
|
2777
3825
|
/**
|
|
@@ -2781,10 +3829,11 @@ export declare interface WorkflowRunnerInterface {
|
|
|
2781
3829
|
*
|
|
2782
3830
|
* @remarks
|
|
2783
3831
|
* One-shot. The runner BUILDS the live tree from `definition` internally (one source of
|
|
2784
|
-
* truth — the per-task {@link
|
|
2785
|
-
* same definition the tree is constructed from, so the executed
|
|
2786
|
-
* the
|
|
2787
|
-
* listeners, a `bail` override, the per-node `phases` bag
|
|
3832
|
+
* truth — the per-task `run` (resolved into its {@link TaskInterface.handler}) and per-phase
|
|
3833
|
+
* `concurrency` come from the same definition the tree is constructed from, so the executed
|
|
3834
|
+
* tree can never drift from the metadata). The {@link WorkflowOptions} part of `options`
|
|
3835
|
+
* (initial `on` listeners, a `bail` override, the per-node `phases` bag, the `functions`
|
|
3836
|
+
* registry) is forwarded to the build.
|
|
2788
3837
|
* Under `bail: false` (graceful) every task settles (a failure is recorded on its
|
|
2789
3838
|
* {@link TaskInterface}) and the workflow reaches `completed`; under `bail: true` (halt)
|
|
2790
3839
|
* the first failure aborts the in-flight sibling tasks AND `skip`s the remaining tasks /
|
|
@@ -2796,33 +3845,74 @@ export declare interface WorkflowRunnerInterface {
|
|
|
2796
3845
|
* the run as `stopped` — the cancel supersedes the same-tick failure, and that task's error
|
|
2797
3846
|
* is not recorded.
|
|
2798
3847
|
*
|
|
3848
|
+
* **Programmer-error exception (AGENTS §12).** A PATHOLOGICAL `definition` (e.g. a
|
|
3849
|
+
* duplicate phase or task `id`) THROWS SYNCHRONOUSLY at construction — before any phase
|
|
3850
|
+
* runs, and before the returned `Promise` is even created — rather than resolving a
|
|
3851
|
+
* failed/partial {@link WorkflowResult}. This is the one exception to the "resolves,
|
|
3852
|
+
* never rejects" contract above: a malformed definition is a programmer-timing error, not
|
|
3853
|
+
* a runtime outcome to report through the result tree.
|
|
3854
|
+
*
|
|
2799
3855
|
* @param definition - The {@link WorkflowDefinition} to build the live tree from and drive
|
|
2800
3856
|
* @param options - The construction options ({@link WorkflowOptions}: `on` / `bail` /
|
|
2801
3857
|
* `phases`) PLUS the per-run bounds (`signal` / `timeout` / `budget`)
|
|
2802
3858
|
* @returns The run's terminal {@link WorkflowResult} (its `workflow` is the built tree)
|
|
2803
3859
|
*/
|
|
2804
3860
|
execute(definition: WorkflowDefinition, options?: WorkflowRunOptions): Promise<WorkflowResult>;
|
|
3861
|
+
/**
|
|
3862
|
+
* Drive an ALREADY-BUILT, CALLER-OWNED live {@link WorkflowInterface} — the
|
|
3863
|
+
* ENTITY-NATIVE counterpart to the definition-building {@link execute} overload.
|
|
3864
|
+
*
|
|
3865
|
+
* @remarks
|
|
3866
|
+
* The entity itself is now the single control surface (no separate run handle):
|
|
3867
|
+
* `createWorkflow` mints the live tree, this overload drives it, and the caller
|
|
3868
|
+
* controls the SAME entity mid-run via its own `pause` / `resume` / `add` / `stop` /
|
|
3869
|
+
* `destroy` (AGENTS §10). Requires `workflow.status === 'pending'` and
|
|
3870
|
+
* `!workflow.destroyed` — otherwise this is a programmer-timing error and it THROWS a
|
|
3871
|
+
* `TRANSITION` {@link import('./errors.js').WorkflowError} (AGENTS §12) rather than
|
|
3872
|
+
* silently no-opping or building a second tree. Once accepted, phases run
|
|
3873
|
+
* SEQUENTIALLY and, within each phase, tasks CONCURRENTLY — byte-identical observable
|
|
3874
|
+
* semantics to the `definition`-form `execute` — except the phase loop RE-READS the
|
|
3875
|
+
* live `workflow.phases` / each phase's live `tasks` every iteration (a cursor over
|
|
3876
|
+
* the live managers, not a one-time snapshot), so a caller's live `add` mid-run is
|
|
3877
|
+
* picked up and actually dispatched. `workflow.pause()` gates the run at the next
|
|
3878
|
+
* phase boundary AND before each task's dispatch (an in-flight task body is never
|
|
3879
|
+
* suspended); `workflow.stop()` skips not-yet-started work gracefully; `workflow.destroy()`
|
|
3880
|
+
* folds `workflow.signal` into the run's cancellation, aborting in-flight work
|
|
3881
|
+
* immediately. `options` carries only the per-run BOUNDS (`signal` / `timeout` /
|
|
3882
|
+
* `budget`) — the construction half of {@link WorkflowRunOptions}
|
|
3883
|
+
* does not apply, since the tree already exists.
|
|
3884
|
+
*
|
|
3885
|
+
* **Run round-trips through the snapshot.** Driving a tree rebuilt by
|
|
3886
|
+
* {@link import('./factories.js').restoreWorkflow} behaves according to whether a
|
|
3887
|
+
* {@link WorkflowFunctions} registry was supplied at that build: WITH a registry,
|
|
3888
|
+
* each task's `run` name is re-resolved against it, so a matched task carries a real
|
|
3889
|
+
* handler and this overload actually DISPATCHES it, resuming real work. WITHOUT a
|
|
3890
|
+
* registry (or when a task's `run` name has no match in it), the task's
|
|
3891
|
+
* {@link TaskInterface.run} is `undefined` — the no-handler rule then AUTO-COMPLETES
|
|
3892
|
+
* that task (no dispatch occurs). A PARTIALLY-run restored tree (any live phase/task
|
|
3893
|
+
* not `pending`) is rejected outright by the `workflow.status === 'pending'` guard
|
|
3894
|
+
* above — only a wholly `pending` restored tree is drivable.
|
|
3895
|
+
*
|
|
3896
|
+
* @param workflow - The live {@link WorkflowInterface} to drive (its own entity surface —
|
|
3897
|
+
* `pause` / `resume` / `add` / `stop` / `destroy` — is the caller's control seam)
|
|
3898
|
+
* @param options - The per-run bounds (`signal` / `timeout` / `budget`); the construction
|
|
3899
|
+
* half of {@link WorkflowRunOptions} does not apply (the tree already exists)
|
|
3900
|
+
* @returns The run's terminal {@link WorkflowResult} (its `workflow` is the SAME entity passed in)
|
|
3901
|
+
*/
|
|
3902
|
+
execute(workflow: WorkflowInterface, options?: Omit<WorkflowRunOptions, keyof WorkflowOptions>): Promise<WorkflowResult>;
|
|
2805
3903
|
}
|
|
2806
3904
|
|
|
2807
3905
|
/**
|
|
2808
|
-
* The options for `createWorkflowRunner` — the
|
|
2809
|
-
*
|
|
2810
|
-
*
|
|
2811
|
-
* @remarks
|
|
2812
|
-
*
|
|
2813
|
-
*
|
|
2814
|
-
*
|
|
2815
|
-
* - `
|
|
2816
|
-
*
|
|
2817
|
-
*
|
|
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.
|
|
3906
|
+
* The options for `createWorkflowRunner` — the optional pacing scheduler the runner
|
|
3907
|
+
* paces phase boundaries with.
|
|
3908
|
+
*
|
|
3909
|
+
* @remarks
|
|
3910
|
+
* The runner is a PURE engine — it carries no `functions` / `tools` / `agents` registry
|
|
3911
|
+
* (each live task already resolved its own handler at construction from
|
|
3912
|
+
* {@link WorkflowOptions.functions}); wiring a `function`-form task to a tool or an agent is
|
|
3913
|
+
* an OPT-IN concern of `factories.ts`'s adapter factories
|
|
3914
|
+
* ({@link import('./factories.js').createToolFunction}, {@link import('./factories.js').createAgentFunction}),
|
|
3915
|
+
* which a caller composes into its OWN `functions` registry.
|
|
2826
3916
|
* - `scheduler` — the {@link SchedulerInterface} that paces the tree (a cooperative
|
|
2827
3917
|
* `yield` between phases). Omitted ⇒ the shipped cross-environment default
|
|
2828
3918
|
* ({@link createScheduler}).
|
|
@@ -2833,10 +3923,6 @@ export declare interface WorkflowRunnerInterface {
|
|
|
2833
3923
|
* wire. A future runner-level emitter would introduce its own `EmitterHooks` here.
|
|
2834
3924
|
*/
|
|
2835
3925
|
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
3926
|
readonly scheduler?: SchedulerInterface;
|
|
2841
3927
|
}
|
|
2842
3928
|
|
|
@@ -2869,28 +3955,15 @@ export declare interface WorkflowRunnerOptions {
|
|
|
2869
3955
|
* `signal` and `start`s it. (A `max: 0` budget is exhausted from its first `start`, so it
|
|
2870
3956
|
* cancels the run at entry — a DIFFERENT primitive from the `timeout: 0` "no deadline" case.)
|
|
2871
3957
|
*
|
|
2872
|
-
* The
|
|
2873
|
-
*
|
|
2874
|
-
*
|
|
2875
|
-
*
|
|
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.
|
|
3958
|
+
* The engine itself carries NO nesting bookkeeping — the depth / cycle guard for a nested
|
|
3959
|
+
* `agent` → workflow-tool → workflow chain lives entirely in the OPT-IN adapter factories
|
|
3960
|
+
* ({@link import('./factories.js').createAgentFunction}, {@link import('./factories.js').createWorkflowTool}),
|
|
3961
|
+
* closed over their own `depth` / `ancestry`, never threaded through `execute`'s options.
|
|
2885
3962
|
*/
|
|
2886
3963
|
export declare type WorkflowRunOptions = WorkflowOptions & {
|
|
2887
3964
|
readonly signal?: AbortSignal;
|
|
2888
3965
|
readonly timeout?: number;
|
|
2889
3966
|
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
3967
|
};
|
|
2895
3968
|
|
|
2896
3969
|
/**
|
|
@@ -2911,16 +3984,7 @@ export declare const workflowShape: ObjectShape<{
|
|
|
2911
3984
|
id: StringShape;
|
|
2912
3985
|
name: StringShape;
|
|
2913
3986
|
description: OptionalShape<StringShape>;
|
|
2914
|
-
run:
|
|
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
|
-
}>]>;
|
|
3987
|
+
run: OptionalShape<StringShape>;
|
|
2924
3988
|
retries: OptionalShape<NumberShape>;
|
|
2925
3989
|
timeout: OptionalShape<NumberShape>;
|
|
2926
3990
|
}>>;
|
|
@@ -2994,18 +4058,16 @@ export declare interface WorkflowSnapshotRow {
|
|
|
2994
4058
|
export declare type WorkflowStatus = LifecycleStatus;
|
|
2995
4059
|
|
|
2996
4060
|
/**
|
|
2997
|
-
* One flat step — `{ name
|
|
4061
|
+
* One flat step — `{ name }` — the building block of a {@link WorkflowSteps} blob.
|
|
2998
4062
|
*
|
|
2999
4063
|
* @remarks
|
|
3000
|
-
* `name` is the REGISTERED behavior name the step runs (it becomes the task's `run
|
|
3001
|
-
* NOT a human label)
|
|
3002
|
-
*
|
|
4064
|
+
* `name` is the REGISTERED behavior name the step runs (it becomes the task's `run`,
|
|
4065
|
+
* NOT a human label) — resolved against a workflow-level {@link WorkflowFunctions}
|
|
4066
|
+
* registry at construction.
|
|
3003
4067
|
*/
|
|
3004
4068
|
export declare interface WorkflowStep {
|
|
3005
|
-
/** The registered behavior name this step runs (becomes the task's `run
|
|
4069
|
+
/** The registered behavior name this step runs (becomes the task's `run`). */
|
|
3006
4070
|
readonly name: string;
|
|
3007
|
-
/** How to run it — `'function'` (default), `'tool'`, or `'agent'`. */
|
|
3008
|
-
readonly via?: TaskVia;
|
|
3009
4071
|
}
|
|
3010
4072
|
|
|
3011
4073
|
/**
|
|
@@ -3025,11 +4087,11 @@ export declare interface WorkflowSteps {
|
|
|
3025
4087
|
|
|
3026
4088
|
/**
|
|
3027
4089
|
* The FLAT authoring shape `createWorkflowTool` advertises as its `parameters` — the
|
|
3028
|
-
* simplest surface a small model can fill: `{ name?, steps: [{ name
|
|
4090
|
+
* simplest surface a small model can fill: `{ name?, steps: [{ name }] }`.
|
|
3029
4091
|
*
|
|
3030
4092
|
* @remarks
|
|
3031
4093
|
* The deliberately-reduced surface (AGENTS §21): a flat ordered list of steps, each a
|
|
3032
|
-
* `{ name
|
|
4094
|
+
* `{ name }`. The tool EXPANDS it ({@link import('./helpers.js').expandSteps}) into a
|
|
3033
4095
|
* full {@link import('./types.js').WorkflowDefinition} — one one-task phase per step, in
|
|
3034
4096
|
* order — then validates against the STRICT
|
|
3035
4097
|
* {@link import('./factories.js').createWorkflowContract} gate. The full nested form is
|
|
@@ -3040,7 +4102,6 @@ export declare const workflowStepsShape: ObjectShape<{
|
|
|
3040
4102
|
name: OptionalShape<StringShape>;
|
|
3041
4103
|
steps: ArrayShape<ObjectShape<{
|
|
3042
4104
|
name: StringShape;
|
|
3043
|
-
via: OptionalShape<LiteralShape<readonly ["function", "tool", "agent"]>>;
|
|
3044
4105
|
}>>;
|
|
3045
4106
|
}>;
|
|
3046
4107
|
|
|
@@ -3106,26 +4167,6 @@ export declare interface WorkflowStoreInterface {
|
|
|
3106
4167
|
*/
|
|
3107
4168
|
export declare function workflowTag(id: string): string;
|
|
3108
4169
|
|
|
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
4170
|
/**
|
|
3130
4171
|
* Options for {@link import('./factories.js').createWorkflowTool} — the depth + ancestry the
|
|
3131
4172
|
* wrapped {@link WorkflowDefinition} runs the NESTED workflow at when an LLM invokes the tool.
|