@orkestrel/workflow 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,4 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- let _orkestrel_agent = require("@orkestrel/agent");
3
2
  let _orkestrel_contract = require("@orkestrel/contract");
4
3
  let _orkestrel_database = require("@orkestrel/database");
5
4
  let _orkestrel_abort = require("@orkestrel/abort");
@@ -179,102 +178,6 @@ var TASK_TRANSITIONS = Object.freeze({
179
178
  * phase may still DECLARE a larger explicit `concurrency` and pays that allocation knowingly.
180
179
  */
181
180
  var DEFAULT_PHASE_CONCURRENCY = 1024;
182
- /**
183
- * The maximum nesting depth a workflow → agent → workflow chain may reach — the bound
184
- * the {@link import('./factories.js').createAgentFunction} and
185
- * {@link import('./factories.js').createWorkflowTool} adapters' depth/cycle guards enforce.
186
- *
187
- * @remarks
188
- * The limit lives in ONE place. An {@link import('./factories.js').createAgentFunction}-wrapped
189
- * agent running at this depth can no longer author + run a NESTED workflow through its bound
190
- * workflow tool (that would be depth `MAX_WORKFLOW_DEPTH + 1`), so the over-deep invocation is
191
- * REJECTED (a typed `DEPTH` {@link import('./errors.js').WorkflowError} throw). The chain
192
- * therefore nests workflows down to this depth, and the nested run at
193
- * depth `MAX_WORKFLOW_DEPTH` fails.
194
- */
195
- var MAX_WORKFLOW_DEPTH = 8;
196
- /**
197
- * The name under which {@link import('./factories.js').createAgentFunction} BINDS the
198
- * depth/cycle-aware workflow tool onto a wrapped agent's `context.tools` (`AgentContextInterface`,
199
- * `@orkestrel/agent`).
200
- *
201
- * @remarks
202
- * The propagation seam's well-known key: when its `runner` option is supplied, the adapter adds a
203
- * {@link import('./factories.js').createWorkflowTool}-built tool under this name to the
204
- * agent's `context.tools`, so it can author + run a NESTED workflow (bounded by
205
- * {@link MAX_WORKFLOW_DEPTH}). An agent that wants to fan out into a workflow calls this tool by
206
- * this name; the bound handler runs the nested workflow at depth + 1.
207
- */
208
- var WORKFLOW_TOOL_NAME = "workflow";
209
- /**
210
- * A complete FLAT authoring example — the PRIMARY way a small model authors a workflow
211
- * through {@link import('./factories.js').createWorkflowTool}: `{ name, steps: [{ name }] }`.
212
- *
213
- * @remarks
214
- * Each step becomes a one-task phase, in order; a step's `name` is a REGISTERED behavior name
215
- * (not a label) — the registry key its task's `run` resolves against. The tool expands this
216
- * ({@link import('./helpers.js').expandSteps}) into a valid {@link WorkflowDefinition}. It
217
- * is embedded VERBATIM in {@link WORKFLOW_TOOL_DESCRIPTION} and guarded by a parity test
218
- * (it must expand to a tree the STRICT contract accepts), so the doc example can never drift.
219
- */
220
- var WORKFLOW_TOOL_FLAT_EXAMPLE = Object.freeze({
221
- name: "release",
222
- steps: Object.freeze([Object.freeze({ name: "compile" }), Object.freeze({ name: "publish" })])
223
- });
224
- /**
225
- * A minimal NESTED authoring example — the ADVANCED escape-hatch form a model may use
226
- * instead of the flat shape: a full {@link WorkflowDefinition}.
227
- *
228
- * @remarks
229
- * The full four-level form, documented in {@link WORKFLOW_TOOL_DESCRIPTION} as the advanced
230
- * alternative. It is embedded VERBATIM and guarded by a parity test (`createWorkflowContract().is`
231
- * must accept it), so the doc example can never drift from a valid definition.
232
- */
233
- var WORKFLOW_TOOL_NESTED_EXAMPLE = Object.freeze({
234
- id: "release",
235
- name: "Release",
236
- phases: Object.freeze([Object.freeze({
237
- id: "build",
238
- name: "Build",
239
- tasks: Object.freeze([Object.freeze({
240
- id: "compile",
241
- name: "Compile",
242
- run: "compile"
243
- })])
244
- })])
245
- });
246
- /**
247
- * The DESCRIPTION {@link import('./factories.js').createWorkflowTool} advertises — a
248
- * multi-line guide that teaches a small model how to author a complete workflow tree.
249
- *
250
- * @remarks
251
- * Presents the SIMPLE flat shape (`{ name, steps: [{ name }] }`) as the PRIMARY way with
252
- * one complete worked example ({@link WORKFLOW_TOOL_FLAT_EXAMPLE}), names that a step's
253
- * `name` is a REGISTERED name (not a human label), and documents the full nested
254
- * {@link WorkflowDefinition} as the ADVANCED form with a minimal example
255
- * ({@link WORKFLOW_TOOL_NESTED_EXAMPLE}). Both examples are interpolated VERBATIM from the
256
- * validated constants, so a parity test pins them — the description can never drift from a
257
- * real, contract-valid example. The `parameters` the tool advertises are the FLAT shape's
258
- * schema; the nested form is the documented escape-hatch (the tool accepts both). NOTE: a step's
259
- * "registered behavior name" is authored STRUCTURE only —
260
- * {@link import('./factories.js').createWorkflowTool} runs the authored tree with no
261
- * {@link WorkflowFunctions} registry of its own, so every one of its tasks auto-completes under
262
- * the no-handler rule; the tool validates/synthesizes shape, it does not dispatch behavior.
263
- */
264
- var WORKFLOW_TOOL_DESCRIPTION = [
265
- "Author and run a workflow (phases run sequentially, the tasks within a phase run concurrently) in one call.",
266
- "",
267
- "SIMPLEST way — a flat list of steps. Each step runs one registered behavior; steps run one after another:",
268
- " { \"name\": \"<workflow name>\", \"steps\": [ { \"name\": \"<registered name>\" }, ... ] }",
269
- "- a step's \"name\" is a REGISTERED behavior name (a registry key), NOT a human label.",
270
- "- the top-level \"name\" (the workflow name) is optional. Ids are filled in for you.",
271
- "Example:",
272
- JSON.stringify(WORKFLOW_TOOL_FLAT_EXAMPLE),
273
- "",
274
- "ADVANCED — the full nested form, for multi-task phases or explicit ids. A workflow has phases; a phase has tasks; a task has a \"run\" (a registered behavior name):",
275
- JSON.stringify(WORKFLOW_TOOL_NESTED_EXAMPLE),
276
- "In the nested form you may omit any \"id\"/\"name\" and they are filled in positionally; a provided one is kept."
277
- ].join("\n");
278
181
  //#endregion
279
182
  //#region src/core/errors.ts
280
183
  /**
@@ -285,11 +188,11 @@ var WORKFLOW_TOOL_DESCRIPTION = [
285
188
  * offending node id / status. Thrown for an illegal lifecycle transition
286
189
  * (`TRANSITION`), a structurally invalid {@link import('./types.js').WorkflowSnapshot}
287
190
  * passed to {@link import('./factories.js').restoreWorkflow} (`RESTORE`), an over-deep /
288
- * cyclic nested-workflow dispatch (`DEPTH`), and a malformed
289
- * {@link import('./factories.js').createWorkflowTool} args blob (`TOOL`). On the
290
- * workflow-tool seam the `DEPTH` / `TOOL` throw is ISOLATED by the
291
- * `@orkestrel/agent` package's `ToolManager` into the tool result's
292
- * top-level `error` (AGENTS §14 — the universal tool-handler contract).
191
+ * cyclic nested-workflow dispatch (`DEPTH`), and a malformed workflow-authoring-tool args
192
+ * blob (`TOOL`). `DEPTH` and `TOOL` are public type surface constructed by the
193
+ * `@orkestrel/tool` package's workflow-tool / agent-function adapters; on that seam the
194
+ * throw is ISOLATED by its `ToolManager` into the tool result's top-level `error`
195
+ * (AGENTS §14 — the universal tool-handler contract).
293
196
  */
294
197
  var WorkflowError = class extends Error {
295
198
  code;
@@ -322,37 +225,6 @@ function isWorkflowError(value) {
322
225
  //#endregion
323
226
  //#region src/core/helpers.ts
324
227
  /**
325
- * The ancestry identifier of a workflow run — `workflow:<id>`.
326
- *
327
- * @remarks
328
- * The {@link import('./WorkflowRunner.js').WorkflowRunner}'s cycle guard records one of
329
- * these per workflow in the current nested run chain (carried on
330
- * {@link import('./types.js').WorkflowRunOptions.ancestry}). Tagging the bare id keeps a
331
- * workflow id and an {@link agentTag} agent name in ONE namespaced set without collision,
332
- * so re-entering a workflow OR an agent already in the chain is a single `includes` check.
333
- *
334
- * @param id - The workflow definition's `id`
335
- * @returns The namespaced ancestry tag (`workflow:<id>`)
336
- */
337
- function workflowTag(id) {
338
- return `workflow:${id}`;
339
- }
340
- /**
341
- * The ancestry identifier of an agent in a run chain — `agent:<name>`.
342
- *
343
- * @remarks
344
- * The agent counterpart of {@link workflowTag}: the runner adds one when it dispatches an
345
- * `agent` task, and rejects the task (a typed `DEPTH` `task.fail`) when the same tag is
346
- * already in the ancestry (a re-entry cycle). The `agent:` namespace keeps it distinct
347
- * from a same-string workflow id.
348
- *
349
- * @param name - The agent's registry name (the `agent`-form's `name`)
350
- * @returns The namespaced ancestry tag (`agent:<name>`)
351
- */
352
- function agentTag(name) {
353
- return `agent:${name}`;
354
- }
355
- /**
356
228
  * Test whether a {@link LifecycleStatus} is TERMINAL — a node in this state will not
357
229
  * transition further.
358
230
  *
@@ -722,120 +594,6 @@ function collectResults(phases) {
722
594
  return phases.flat();
723
595
  }
724
596
  /**
725
- * Summarize a terminal {@link WorkflowResult} into the PLAIN value a
726
- * {@link import('./factories.js').createWorkflowTool} handler returns on success.
727
- *
728
- * @remarks
729
- * This is the run summary the handler returns DIRECTLY — NOT a `ToolResult` (the future `@orkestrel/agent` package).
730
- * The handler conforms to the universal tool-handler contract (AGENTS §14): it returns the plain value
731
- * (and throws on failure), so the `@orkestrel/agent` package's `ToolManager` performs
732
- * the ONE canonical wrap (`{ id, name, value }`) and the model reads exactly this summary — once,
733
- * identically — over BOTH the agent loop and MCP. The summary is LEAN: the workflow's terminal `status`
734
- * and the COUNT of settled task results — enough for a caller / model to react without serializing the
735
- * whole live tree. (It carries no synthetic `id` / `name`: a tool handler has no call id; the manager
736
- * supplies the canonical envelope's identity.)
737
- *
738
- * @param result - The terminal {@link WorkflowResult} the run produced
739
- * @returns The plain success summary — `{ status, count }`
740
- */
741
- function workflowToolSummary(result) {
742
- return {
743
- status: result.status,
744
- count: result.results.length
745
- };
746
- }
747
- /**
748
- * Complete a {@link WorkflowDraft} into a strict {@link WorkflowDefinition} — synthesize
749
- * any MISSING `id` deterministically + positionally, and default any MISSING `name` to
750
- * its (now-resolved) `id`.
751
- *
752
- * @remarks
753
- * The positional id scheme is stable and human-legible: the workflow is `wf`, phase `i`
754
- * is `phase-<i>`, and task `j` of that phase is `<phaseId>-task-<j>` (so a provided phase
755
- * id flows into its tasks' synthesized ids). A PROVIDED `id` / `name` at any level is kept
756
- * VERBATIM — synthesis touches only the omitted ones. A missing `name` defaults to the
757
- * resolved `id` (never the other way round), so the result always has both. `run`,
758
- * `description`, the per-phase `concurrency` / `bail`, the per-task `retries` / `timeout`, and
759
- * the workflow `bail` carry over unchanged. The result is a complete
760
- * {@link WorkflowDefinition}; the caller still validates it against the STRICT contract.
761
- *
762
- * @param draft - The draft workflow (id/name optional at all three levels)
763
- * @returns A complete {@link WorkflowDefinition} with every id/name filled
764
- */
765
- function completeDraft(draft) {
766
- const id = draft.id ?? "wf";
767
- return {
768
- id,
769
- name: draft.name ?? id,
770
- ...draft.description === void 0 ? {} : { description: draft.description },
771
- phases: draft.phases.map((phase, index) => completePhaseDraft(phase, index)),
772
- ...draft.bail === void 0 ? {} : { bail: draft.bail }
773
- };
774
- }
775
- /**
776
- * Complete one {@link PhaseDraft} into a strict {@link PhaseDefinition} — the per-phase
777
- * step of {@link completeDraft} (phase `index` → `phase-<index>` when its id is omitted).
778
- *
779
- * @param phase - The draft phase
780
- * @param index - The phase's positional index in the workflow
781
- * @returns A complete {@link PhaseDefinition}
782
- */
783
- function completePhaseDraft(phase, index) {
784
- const id = phase.id ?? `phase-${index}`;
785
- return {
786
- id,
787
- name: phase.name ?? id,
788
- ...phase.description === void 0 ? {} : { description: phase.description },
789
- tasks: phase.tasks.map((task, taskIndex) => completeTaskDraft(task, id, taskIndex)),
790
- ...phase.concurrency === void 0 ? {} : { concurrency: phase.concurrency },
791
- ...phase.bail === void 0 ? {} : { bail: phase.bail }
792
- };
793
- }
794
- /**
795
- * Complete one {@link TaskDraft} into a strict {@link TaskDefinition} — the per-task leaf
796
- * step of {@link completeDraft} (task `index` of phase `<phaseId>` → `<phaseId>-task-<index>`
797
- * when its id is omitted).
798
- *
799
- * @param task - The draft task
800
- * @param phaseId - The (resolved) parent phase id, so the synthesized task id nests under it
801
- * @param index - The task's positional index within its phase
802
- * @returns A complete {@link TaskDefinition}
803
- */
804
- function completeTaskDraft(task, phaseId, index) {
805
- const id = task.id ?? `${phaseId}-task-${index}`;
806
- return {
807
- id,
808
- name: task.name ?? id,
809
- ...task.description === void 0 ? {} : { description: task.description },
810
- ...task.run === void 0 ? {} : { run: task.run },
811
- ...task.retries === void 0 ? {} : { retries: task.retries },
812
- ...task.timeout === void 0 ? {} : { timeout: task.timeout }
813
- };
814
- }
815
- /**
816
- * Expand a flat {@link WorkflowSteps} blob into a strict {@link WorkflowDefinition} — each
817
- * step becomes a one-task phase, IN ORDER.
818
- *
819
- * @remarks
820
- * The expansion of the tool's ADVERTISED surface (AGENTS §21 — the simplest form a small
821
- * model can author). Each {@link WorkflowStep} maps to a phase holding exactly one task:
822
- * the step's `name` becomes the task's `run` (the behavior-registry key). Ids/names are
823
- * auto-filled positionally — it builds an ids-omitted {@link WorkflowDraft} and delegates
824
- * to {@link completeDraft}, so the two lenient surfaces share ONE synthesis path (step `i`
825
- * → phase `phase-<i>`, its task `phase-<i>-task-0`). The optional `name` becomes the
826
- * workflow's `name`. The result is a complete definition the caller validates against the
827
- * STRICT contract before running.
828
- *
829
- * @param flat - The flat steps blob (`{ name?, steps: [{ name }] }`)
830
- * @returns A complete {@link WorkflowDefinition} (one one-task phase per step)
831
- */
832
- function expandSteps(flat) {
833
- return completeDraft({
834
- ...flat.name === void 0 ? {} : { name: flat.name },
835
- phases: flat.steps.map((step) => ({ tasks: [{ run: step.name }] }))
836
- });
837
- }
838
- /**
839
597
  * Insert one `[key, value]` entry at a positional index into a readonly entries array —
840
598
  * the pure splice-in step behind an insertion-ordered registry's `add`.
841
599
  *
@@ -1013,118 +771,6 @@ var workflowShape = (0, _orkestrel_contract.objectShape)({
1013
771
  bail: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.literalShape)([true, false], { description: "Failure policy: false (default) continues gracefully, true halts on the first failure." }))
1014
772
  });
1015
773
  /**
1016
- * The shape of a TASK in a draft workflow — identical to {@link taskShape} EXCEPT `id`
1017
- * and `name` are OPTIONAL (the tool synthesizes any missing one positionally).
1018
- *
1019
- * @remarks
1020
- * A PROVIDED `id` / `name` still carries `minLength: 1`, so an explicitly-empty `id: ''`
1021
- * is INVALID (rejected by the draft contract), never auto-filled — keeping "garbage"
1022
- * distinct from "omitted". `run` stays optional, mirroring {@link taskShape}.
1023
- */
1024
- var taskDraftShape = (0, _orkestrel_contract.objectShape)({
1025
- id: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)({
1026
- min: 1,
1027
- description: "Task id; auto-filled when omitted."
1028
- })),
1029
- name: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)({
1030
- min: 1,
1031
- description: "Task name; defaults to the id when omitted."
1032
- })),
1033
- description: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)({ description: "Optional task description." })),
1034
- run: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)({
1035
- min: 1,
1036
- description: "The registered behavior name to invoke (a registry key, not a label); omitted has no handler."
1037
- })),
1038
- retries: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.integerShape)({
1039
- min: 0,
1040
- description: "Extra attempts after the first on failure; overrides the phase default. Omitted means none."
1041
- })),
1042
- timeout: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.integerShape)({
1043
- min: 0,
1044
- description: "Per-attempt deadline in milliseconds; overrides the phase default. Omitted means no deadline."
1045
- }))
1046
- });
1047
- /**
1048
- * The shape of a PHASE in a draft workflow — identical to {@link phaseShape} EXCEPT
1049
- * `id` and `name` are OPTIONAL, and its tasks are {@link taskDraftShape}s.
1050
- */
1051
- var phaseDraftShape = (0, _orkestrel_contract.objectShape)({
1052
- id: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)({
1053
- min: 1,
1054
- description: "Phase id; auto-filled when omitted."
1055
- })),
1056
- name: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)({
1057
- min: 1,
1058
- description: "Phase name; defaults to the id when omitted."
1059
- })),
1060
- description: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)({ description: "Optional phase description." })),
1061
- tasks: (0, _orkestrel_contract.arrayShape)(taskDraftShape, { description: "The phase tasks; they run CONCURRENTLY." }),
1062
- concurrency: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.integerShape)({
1063
- min: 1,
1064
- description: "Max tasks in flight at once (a resource throttle); omitted means unbounded."
1065
- })),
1066
- bail: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.literalShape)([true, false], { description: "Per-phase failure-policy override; omitted inherits the workflow bail." }))
1067
- });
1068
- /**
1069
- * The shape of a DRAFT workflow — identical to {@link workflowShape} EXCEPT `id` and
1070
- * `name` are OPTIONAL at all three levels (workflow / phase / task), so a small model
1071
- * can omit the six identity strings and let the tool synthesize them positionally.
1072
- *
1073
- * @remarks
1074
- * The lenient counterpart {@link import('./factories.js').createWorkflowDraftContract}
1075
- * compiles. `run` stays required; a provided `id` / `name` still has `minLength: 1` (so an
1076
- * explicitly-empty `id: ''` is REJECTED, not auto-filled). After
1077
- * {@link import('./helpers.js').completeDraft} fills the missing ids/names, the result is
1078
- * validated against the STRICT {@link import('./factories.js').createWorkflowContract} gate
1079
- * before running.
1080
- */
1081
- var workflowDraftShape = (0, _orkestrel_contract.objectShape)({
1082
- id: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)({
1083
- min: 1,
1084
- description: "Workflow id; auto-filled when omitted."
1085
- })),
1086
- name: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)({
1087
- min: 1,
1088
- description: "Workflow name; defaults to the id when omitted."
1089
- })),
1090
- description: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)({ description: "Optional workflow description." })),
1091
- phases: (0, _orkestrel_contract.arrayShape)(phaseDraftShape, { description: "The workflow phases; they run SEQUENTIALLY, in order." }),
1092
- bail: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.literalShape)([true, false], { description: "Failure policy: false (default) continues gracefully, true halts on the first failure." }))
1093
- });
1094
- /**
1095
- * The shape of ONE flat step — `{ name }` — the building block of
1096
- * {@link workflowStepsShape}.
1097
- *
1098
- * @remarks
1099
- * `name` is the REGISTERED behavior name the step runs (it becomes the task's `run`). The
1100
- * tool expands each step into a one-task phase, in order
1101
- * ({@link import('./helpers.js').expandSteps}).
1102
- */
1103
- var stepShape = (0, _orkestrel_contract.objectShape)({ name: (0, _orkestrel_contract.stringShape)({
1104
- min: 1,
1105
- description: "The registered behavior name this step runs (becomes the task run)."
1106
- }) });
1107
- /**
1108
- * The FLAT authoring shape `createWorkflowTool` advertises as its `parameters` — the
1109
- * simplest surface a small model can fill: `{ name?, steps: [{ name }] }`.
1110
- *
1111
- * @remarks
1112
- * The deliberately-reduced surface (AGENTS §21): a flat ordered list of steps, each a
1113
- * `{ name }`. The tool EXPANDS it ({@link import('./helpers.js').expandSteps}) into a
1114
- * full {@link import('./types.js').WorkflowDefinition} — one one-task phase per step, in
1115
- * order — then validates against the STRICT
1116
- * {@link import('./factories.js').createWorkflowContract} gate. The full nested form is
1117
- * STILL accepted by the tool (it branches on the args' shape) and is documented as the
1118
- * advanced escape-hatch in the tool's description — but THIS is what `parameters` advertises.
1119
- */
1120
- var workflowStepsShape = (0, _orkestrel_contract.objectShape)({
1121
- name: (0, _orkestrel_contract.optionalShape)((0, _orkestrel_contract.stringShape)({
1122
- min: 1,
1123
- description: "Optional workflow name."
1124
- })),
1125
- steps: (0, _orkestrel_contract.arrayShape)(stepShape, { description: "The ordered steps to run, one after another (each becomes a one-task phase)." })
1126
- });
1127
- /**
1128
774
  * The shape of a {@link import('./types.js').TaskUpdate} — a partial edit to a
1129
775
  * `pending` task's `name` / `description`, both optional.
1130
776
  *
@@ -2581,10 +2227,10 @@ var TaskController = class {
2581
2227
  * {@link import('./types.js').TaskInterface.handler} ONCE at construction (build, restore,
2582
2228
  * or a live mint all resolve it identically, from {@link WorkflowOptions.functions}), so
2583
2229
  * dispatch is simply "invoke the task's own handler". Static tool / agent calling is an
2584
- * OPT-IN concern of `factories.ts`'s adapter factories ({@link import('./factories.js').createToolFunction},
2585
- * {@link import('./factories.js').createAgentFunction}) — plain {@link import('./types.js').WorkflowFunction}s a
2586
- * caller wires into {@link WorkflowOptions.functions} like any other behavior. This module
2587
- * never imports `@orkestrel/agent`.
2230
+ * OPT-IN concern of the `@orkestrel/tool` package's adapter factories plain
2231
+ * {@link import('./types.js').WorkflowFunction}s a caller wires into
2232
+ * {@link WorkflowOptions.functions} like any other behavior. This module never imports
2233
+ * any tool/agent package.
2588
2234
  * - **Two `execute` forms, one engine.** `execute(definition, options)` BUILDS the live tree
2589
2235
  * from a {@link WorkflowDefinition} (single source of truth for the `run` / `concurrency`
2590
2236
  * metadata); `execute(workflow, options)` DRIVES a caller-owned, ALREADY-BUILT
@@ -2897,41 +2543,6 @@ function createWorkflowContract() {
2897
2543
  };
2898
2544
  }
2899
2545
  /**
2900
- * Compile the LENIENT workflow DRAFT contract — identical to
2901
- * {@link createWorkflowContract} EXCEPT `id` and `name` are OPTIONAL at all three levels
2902
- * (workflow / phase / task), so a small model can omit the six identity strings.
2903
- *
2904
- * @remarks
2905
- * The widened authoring surface {@link createWorkflowTool} parses an authored blob through
2906
- * before {@link import('./helpers.js').completeDraft} fills the missing ids/names. It does
2907
- * NOT relax the canonical contract — {@link createWorkflowContract} stays byte-for-byte
2908
- * unchanged and STRICT, and the completed draft is re-validated against THAT strict gate
2909
- * before running (soundness preserved). A PROVIDED `id` / `name` still carries `minLength: 1`,
2910
- * so an explicitly-empty `id: ''` is REJECTED (parses to `undefined`), never auto-filled —
2911
- * keeping "garbage" distinct from "omitted". `run` stays required.
2912
- *
2913
- * @returns The compiled {@link WorkflowDraft} contract
2914
- *
2915
- * @example
2916
- * ```ts
2917
- * import { createWorkflowDraftContract, completeDraft } from '@src/core'
2918
- *
2919
- * const draft = createWorkflowDraftContract()
2920
- * const parsed = draft.parse({ phases: [{ tasks: [{ run: { via: 'function', name: 'f' } }] }] })
2921
- * const definition = parsed && completeDraft(parsed) // ids/names filled positionally
2922
- * draft.parse({ id: '', phases: [] }) // undefined — an explicit empty id is rejected
2923
- * ```
2924
- */
2925
- function createWorkflowDraftContract() {
2926
- const contract = (0, _orkestrel_contract.createContract)(workflowDraftShape);
2927
- return {
2928
- schema: contract.schema,
2929
- is: contract.is,
2930
- generate: (random) => contract.generate(random),
2931
- parse: (value) => contract.parse(value)
2932
- };
2933
- }
2934
- /**
2935
2546
  * Build the live W-b entity tree from a {@link WorkflowDefinition} — the whole
2936
2547
  * {@link WorkflowInterface} → {@link import('./types.js').PhaseInterface} →
2937
2548
  * {@link import('./types.js').TaskInterface} tree, each level wired with its lineage
@@ -3165,11 +2776,11 @@ function createDatabaseWorkflowStore(driver = (0, _orkestrel_database.createMemo
3165
2776
  * the live entity (`start` → `complete` / `fail`), and resolves a
3166
2777
  * {@link import('./types.js').WorkflowResult}.
3167
2778
  *
3168
- * Static tool / agent calling is OPT-IN, wired through the adapter factories
3169
- * {@link createToolFunction} / {@link createAgentFunction} — plain
3170
- * {@link import('./types.js').WorkflowFunction}s a caller composes into its OWN
3171
- * {@link WorkflowOptions.functions} registry, same as any other behavior. A task with no
3172
- * resolved handler AUTO-COMPLETES (the ROADMAP no-handler rule).
2779
+ * Static tool / agent calling is OPT-IN: a caller wires a plain
2780
+ * {@link import('./types.js').WorkflowFunction} into its OWN {@link WorkflowOptions.functions}
2781
+ * registry, same as any other behavior the `@orkestrel/tool` package ships the
2782
+ * tool/agent adapter factories for that. A task with no resolved handler AUTO-COMPLETES
2783
+ * (the ROADMAP no-handler rule).
3173
2784
  *
3174
2785
  * @param options - An optional pacing `scheduler` (default the shipped cross-environment one).
3175
2786
  * See {@link WorkflowRunnerOptions}.
@@ -3194,231 +2805,6 @@ function createWorkflowRunner(options) {
3194
2805
  return new WorkflowRunner(options?.scheduler ?? createScheduler());
3195
2806
  }
3196
2807
  /**
3197
- * Wrap a registered tool as a {@link WorkflowFunction} — the OPT-IN adapter that lets a
3198
- * `function`-form task run a `@orkestrel/agent` tool BY NAME.
3199
- *
3200
- * @remarks
3201
- * Composes into a caller's {@link WorkflowOptions.functions} registry like any other behavior
3202
- * (`{ publish: createToolFunction(tools, 'publish') }`); the PURE
3203
- * {@link import('./WorkflowRunner.js').WorkflowRunner} has no knowledge of tools itself. The
3204
- * returned function executes `name` against `tools` with the task's `controller.input` as the
3205
- * call arguments, id-correlated to the task's own id. A `ToolManagerInterface.execute` NEVER
3206
- * throws (a handler throw is isolated into `result.error`), so a failing tool is surfaced here
3207
- * as a THROWN `Error` carrying the original message as `cause` — the leaf `fail`s, honouring
3208
- * `bail`. An UNREGISTERED tool name is a programmer error (an explicit binding to a name that
3209
- * doesn't exist) — unlike the engine's own silent auto-complete of an unresolved task handler,
3210
- * this THROWS a typed `TOOL` {@link WorkflowError}.
3211
- *
3212
- * @param tools - The {@link ToolManagerInterface} the named tool is registered on
3213
- * @param name - The registered tool's name
3214
- * @returns A {@link WorkflowFunction} that runs the named tool
3215
- *
3216
- * @example
3217
- * ```ts
3218
- * import { createToolFunction, createToolManager, createWorkflowRunner } from '@src/core'
3219
- *
3220
- * const tools = createToolManager()
3221
- * tools.add(myPublishTool)
3222
- * const runner = createWorkflowRunner()
3223
- * await runner.execute(definition, { functions: { publish: createToolFunction(tools, 'publish') } })
3224
- * ```
3225
- */
3226
- function createToolFunction(tools, name) {
3227
- return async (controller) => {
3228
- if (tools.tool(name) === void 0) throw new WorkflowError("TOOL", `tool '${name}' is not registered`, { tool: name });
3229
- const result = await tools.execute({
3230
- id: controller.task.id,
3231
- name,
3232
- arguments: controller.input
3233
- });
3234
- if (result.error !== void 0) throw new Error(result.error, { cause: result.error });
3235
- return result.value;
3236
- };
3237
- }
3238
- /**
3239
- * Wrap a live `AgentInterface` (`@orkestrel/agent`) as a {@link WorkflowFunction} — the OPT-IN
3240
- * adapter that runs the agent to a settled result, folding a nested workflow-authoring
3241
- * depth / cycle guard into its own closure.
3242
- *
3243
- * @remarks
3244
- * Composes into a caller's {@link WorkflowOptions.functions} registry like any other behavior;
3245
- * the PURE {@link import('./WorkflowRunner.js').WorkflowRunner} has no knowledge of agents
3246
- * itself. Before running the agent, the depth/cycle guard REJECTS the call (a THROWN typed
3247
- * `DEPTH` {@link WorkflowError}, which the leaf `fail`s) when running it would push a nested
3248
- * chain past {@link MAX_WORKFLOW_DEPTH}, OR when this agent is already an ancestor (a cycle) —
3249
- * ported from the former engine-side guard. When {@link AgentFunctionOptions.runner} is
3250
- * supplied, the adapter BINDS a depth/cycle-aware {@link createWorkflowTool} onto the agent's
3251
- * `context.tools` (the propagation seam) — closed over `depth` and the extended ancestry (the
3252
- * tool itself computes `depth + 1` internally) — so the agent can author + run a NESTED
3253
- * workflow through it; the wrapped default is the CURRENT task's own workflow id (used only on
3254
- * a no-args tool call). The task's cancellation folds into the agent run: an already-aborted
3255
- * `controller.signal` cancels the agent up front; otherwise a one-shot listener fires
3256
- * `agent.abort(reason)` when the task cancels, removed in `finally`. `agent.generate()` resolves
3257
- * a partial `AgentResult` on a cancel (never rejects), returned as the task's completed value.
3258
- *
3259
- * A bound agent is effectively SINGLE-RUN: `context.tools.add` binds one {@link ToolInterface}
3260
- * under the fixed {@link import('./constants.js').WORKFLOW_TOOL_NAME}, and `agent.generate()` /
3261
- * `agent.abort()` are per-agent state. Two CONCURRENT tasks sharing the SAME `agent` instance
3262
- * race on that one tool binding (last-write-wins) and on generate/abort — give each concurrent
3263
- * task its OWN agent instance.
3264
- *
3265
- * @param agent - The live `AgentInterface` to run
3266
- * @param options - The nested-workflow binding + depth/cycle bookkeeping (see {@link AgentFunctionOptions})
3267
- * @returns A {@link WorkflowFunction} that runs `agent` to its settled result
3268
- *
3269
- * @example
3270
- * ```ts
3271
- * import { createAgentFunction, createWorkflowRunner } from '@src/core'
3272
- *
3273
- * const runner = createWorkflowRunner()
3274
- * const review = createAgentFunction(myAgent, { runner })
3275
- * await runner.execute(definition, { functions: { review } })
3276
- * ```
3277
- */
3278
- function createAgentFunction(agent, options) {
3279
- return async (controller) => {
3280
- const depth = options?.depth ?? 0;
3281
- const ancestry = options?.ancestry ?? [];
3282
- if (depth + 1 > 8) throw new WorkflowError("DEPTH", `agent '${agent.id}' exceeds max workflow depth`, {
3283
- agent: agent.id,
3284
- depth,
3285
- max: 8
3286
- });
3287
- const tag = agentTag(agent.id);
3288
- if (ancestry.includes(tag)) throw new WorkflowError("DEPTH", `agent '${agent.id}' is already an ancestor (cycle)`, {
3289
- agent: agent.id,
3290
- ancestry: [...ancestry]
3291
- });
3292
- const runner = options?.runner;
3293
- if (runner !== void 0) {
3294
- const workflowId = controller.task.phase.workflow.id;
3295
- const wrapped = {
3296
- id: workflowId,
3297
- name: workflowId,
3298
- phases: []
3299
- };
3300
- agent.context.tools.add(createWorkflowTool(wrapped, runner, {
3301
- depth,
3302
- ancestry: [...ancestry, tag]
3303
- }));
3304
- }
3305
- const signal = controller.signal;
3306
- const onAbort = () => agent.abort(signal.reason);
3307
- if (signal.aborted) agent.abort(signal.reason);
3308
- else signal.addEventListener("abort", onAbort, { once: true });
3309
- try {
3310
- return await agent.generate();
3311
- } finally {
3312
- signal.removeEventListener("abort", onAbort);
3313
- }
3314
- };
3315
- }
3316
- /**
3317
- * Wrap a {@link WorkflowDefinition} as an LLM-callable {@link ToolInterface} — it ADVERTISES
3318
- * the SIMPLE flat authoring shape (`{ name?, steps: [{ name }] }`) as its `parameters` so
3319
- * even a small model can author a complete tree, and its handler EXPANDS / COMPLETES the
3320
- * authored blob, validates it against the STRICT contract, runs it through `runner`, and
3321
- * returns the run SUMMARY (throwing a typed {@link WorkflowError} on failure).
3322
- *
3323
- * @remarks
3324
- * A plain {@link ToolManagerInterface}-compatible tool (so `createMCPServer` / `createMCPRoutes`
3325
- * expose it for free — nothing MCP is wired here). It is ALSO the propagation carrier
3326
- * {@link createAgentFunction} binds onto a wrapped agent's `context.tools`: because a tool
3327
- * handler receives ONLY the model-supplied `args` (no ambient context, no signal), the run's
3328
- * depth + ancestry are CLOSED OVER at bind time via {@link WorkflowToolOptions}, and the
3329
- * handler enforces the SAME depth / cycle guard itself (this function owns it now — the engine
3330
- * carries none) before running the nested workflow at `depth + 1` with the extended ancestry.
3331
- *
3332
- * **Widened authoring surface (additive — the canonical contract + runner stay STRICT and
3333
- * unchanged).** A 2B model reliably CALLS the tool but cannot reliably emit the full four-level
3334
- * nested {@link WorkflowDefinition} (six required `id`/`name` strings, an all-or-nothing tree).
3335
- * So the tool ACCEPTS three authoring forms and converges them on the SAME strict
3336
- * {@link createWorkflowContract} gate before running (soundness preserved):
3337
- * - the FLAT shape `{ name?, steps: [{ name }] }` — the ADVERTISED `parameters` (the simplest
3338
- * form, {@link import('./helpers.js').expandSteps}'d into one one-task phase per step);
3339
- * - a nested DRAFT with any `id`/`name` OMITTED — {@link createWorkflowDraftContract}-parsed then
3340
- * {@link import('./helpers.js').completeDraft}'d (missing ids synthesized positionally);
3341
- * - the full nested {@link WorkflowDefinition} — the advanced escape-hatch (documented in the
3342
- * description), accepted as the draft super-set.
3343
- *
3344
- * The handler conforms to the universal tool-handler contract (AGENTS §14): it returns the PLAIN
3345
- * run-summary VALUE on success and THROWS a typed {@link WorkflowError} on every failure path. It
3346
- * does NOT build a {@link ToolResult} itself — the `@orkestrel/agent` package's `ToolManager`
3347
- * performs the ONE canonical wrap (`{ id, name, value }` on a return; `{ id, name, error }` on a
3348
- * throw, ISOLATED so nothing escapes the run), so the outcome appears EXACTLY ONCE, identically,
3349
- * over BOTH the agent loop and MCP (a throw → MCP `isError: true`):
3350
- * - **No authored args** (an empty `arguments`) ⇒ runs the WRAPPED `definition`.
3351
- * - **A `steps` array** ⇒ the FLAT form: parse it, {@link import('./helpers.js').expandSteps} it.
3352
- * - **Otherwise** ⇒ the nested form: {@link createWorkflowDraftContract}-parse it,
3353
- * {@link import('./helpers.js').completeDraft} it.
3354
- * - **Strict gate** ⇒ the expanded / completed result is validated against
3355
- * {@link createWorkflowContract}.`is`; a blob that can't expand, or whose result fails the strict
3356
- * gate (e.g. an explicit empty `id`, `concurrency: 0`) ⇒ THROW a `TOOL` {@link WorkflowError} (no run).
3357
- * - **Over-deep / cyclic** ⇒ THROW a `DEPTH` {@link WorkflowError} when the nested run would exceed
3358
- * {@link MAX_WORKFLOW_DEPTH}, or the target workflow id is already an ancestor (a cycle) — the
3359
- * SAME `code` {@link createAgentFunction}'s own guard raises. Enforced HERE, INSIDE this
3360
- * handler, before ever calling `runner.execute` — the engine itself performs no such check.
3361
- * - **Otherwise** ⇒ `runner.execute(target)`, RETURNING the plain summary of the terminal run
3362
- * (`{ status, count }`, via {@link workflowToolSummary}).
3363
- *
3364
- * The tool executes AUTHORED STRUCTURE, not consumer behavior: a nested tree authored through
3365
- * it (flat, draft, or full form) carries no {@link WorkflowFunctions} registry, so EVERY one of
3366
- * its tasks auto-completes under the no-handler rule. This handler validates and synthesizes
3367
- * shape — it never runs a caller's handlers.
3368
- *
3369
- * @param definition - The workflow the tool runs when called with no authored args
3370
- * @param runner - The {@link WorkflowRunnerInterface} that executes the (nested) workflow
3371
- * @param options - The depth + ancestry to run the nested workflow under (see
3372
- * {@link WorkflowToolOptions}); omitted ⇒ depth `0` / empty ancestry (a top-level wrap)
3373
- * @returns A {@link ToolInterface} (named {@link import('./constants.js').WORKFLOW_TOOL_NAME})
3374
- * whose `parameters` advertise the FLAT authoring schema (the nested form stays accepted)
3375
- *
3376
- * @example
3377
- * ```ts
3378
- * import { createWorkflowRunner, createWorkflowTool, createToolManager } from '@src/core'
3379
- *
3380
- * const runner = createWorkflowRunner()
3381
- * const tool = createWorkflowTool(definition, runner)
3382
- * const tools = createToolManager()
3383
- * tools.add(tool) // a model can now author + run a workflow in one call
3384
- * ```
3385
- */
3386
- function createWorkflowTool(definition, runner, options) {
3387
- const strict = createWorkflowContract();
3388
- const draft = createWorkflowDraftContract();
3389
- const steps = (0, _orkestrel_contract.createContract)(workflowStepsShape);
3390
- const depth = options?.depth ?? 0;
3391
- const ancestry = options?.ancestry ?? [];
3392
- return (0, _orkestrel_agent.createTool)({
3393
- name: WORKFLOW_TOOL_NAME,
3394
- description: WORKFLOW_TOOL_DESCRIPTION,
3395
- parameters: (0, _orkestrel_contract.schemaToParameters)(steps.schema),
3396
- execute: async (args) => {
3397
- let target;
3398
- if (Object.keys(args).length === 0) target = definition;
3399
- else if (Array.isArray(args.steps)) {
3400
- const flat = steps.parse(args);
3401
- target = flat === void 0 ? void 0 : expandSteps(flat);
3402
- } else {
3403
- const parsed = draft.parse(args);
3404
- target = parsed === void 0 ? void 0 : completeDraft(parsed);
3405
- }
3406
- if (target === void 0 || !strict.is(target)) throw new WorkflowError("TOOL", "malformed workflow definition", { workflow: definition.id });
3407
- if (depth + 1 > 8) throw new WorkflowError("DEPTH", `nested workflow exceeds max depth 8`, {
3408
- workflow: target.id,
3409
- depth,
3410
- max: 8
3411
- });
3412
- const tag = workflowTag(target.id);
3413
- if (ancestry.includes(tag)) throw new WorkflowError("DEPTH", `workflow '${target.id}' is already an ancestor (cycle)`, {
3414
- workflow: target.id,
3415
- ancestry: [...ancestry]
3416
- });
3417
- return workflowToolSummary(await runner.execute(target));
3418
- }
3419
- });
3420
- }
3421
- /**
3422
2808
  * Create the safe cross-environment cooperative-yield default — a
3423
2809
  * {@link SchedulerInterface} built on `setTimeout` / `clearTimeout` alone, so it
3424
2810
  * runs unchanged in both the browser and Node.
@@ -3514,7 +2900,6 @@ exports.Controller = Controller;
3514
2900
  exports.DEFAULT_BAIL = DEFAULT_BAIL;
3515
2901
  exports.DEFAULT_PHASE_CONCURRENCY = DEFAULT_PHASE_CONCURRENCY;
3516
2902
  exports.DatabaseWorkflowStore = DatabaseWorkflowStore;
3517
- exports.MAX_WORKFLOW_DEPTH = MAX_WORKFLOW_DEPTH;
3518
2903
  exports.MemoryWorkflowStore = MemoryWorkflowStore;
3519
2904
  exports.PHASE_STATUSES = PHASE_STATUSES;
3520
2905
  exports.Phase = Phase;
@@ -3528,40 +2913,27 @@ exports.Task = Task;
3528
2913
  exports.TaskController = TaskController;
3529
2914
  exports.TaskManager = TaskManager;
3530
2915
  exports.WORKFLOW_STATUSES = WORKFLOW_STATUSES;
3531
- exports.WORKFLOW_TOOL_DESCRIPTION = WORKFLOW_TOOL_DESCRIPTION;
3532
- exports.WORKFLOW_TOOL_FLAT_EXAMPLE = WORKFLOW_TOOL_FLAT_EXAMPLE;
3533
- exports.WORKFLOW_TOOL_NAME = WORKFLOW_TOOL_NAME;
3534
- exports.WORKFLOW_TOOL_NESTED_EXAMPLE = WORKFLOW_TOOL_NESTED_EXAMPLE;
3535
2916
  exports.Workflow = Workflow;
3536
2917
  exports.WorkflowError = WorkflowError;
3537
2918
  exports.WorkflowRunner = WorkflowRunner;
3538
- exports.agentTag = agentTag;
3539
2919
  exports.assertSnapshot = assertSnapshot;
3540
2920
  exports.buildPhaseContext = buildPhaseContext;
3541
2921
  exports.buildTaskContext = buildTaskContext;
3542
2922
  exports.buildWorkflowContext = buildWorkflowContext;
3543
2923
  exports.canTransitionTask = canTransitionTask;
3544
2924
  exports.collectResults = collectResults;
3545
- exports.completeDraft = completeDraft;
3546
- exports.completePhaseDraft = completePhaseDraft;
3547
- exports.completeTaskDraft = completeTaskDraft;
3548
- exports.createAgentFunction = createAgentFunction;
3549
2925
  exports.createDatabaseWorkflowStore = createDatabaseWorkflowStore;
3550
2926
  exports.createDeferred = createDeferred;
3551
2927
  exports.createMemoryWorkflowStore = createMemoryWorkflowStore;
3552
2928
  exports.createRunner = createRunner;
3553
2929
  exports.createScheduler = createScheduler;
3554
- exports.createToolFunction = createToolFunction;
3555
2930
  exports.createWorkflow = createWorkflow;
3556
2931
  exports.createWorkflowContract = createWorkflowContract;
3557
- exports.createWorkflowDraftContract = createWorkflowDraftContract;
3558
2932
  exports.createWorkflowRunner = createWorkflowRunner;
3559
- exports.createWorkflowTool = createWorkflowTool;
3560
2933
  exports.definitionToSnapshot = definitionToSnapshot;
3561
2934
  exports.deriveBoundary = deriveBoundary;
3562
2935
  exports.derivePhaseStatus = derivePhaseStatus;
3563
2936
  exports.deriveWorkflowStatus = deriveWorkflowStatus;
3564
- exports.expandSteps = expandSteps;
3565
2937
  exports.failure = failure;
3566
2938
  exports.findFailure = findFailure;
3567
2939
  exports.insertEntry = insertEntry;
@@ -3571,20 +2943,13 @@ exports.isWorkflowSnapshot = isWorkflowSnapshot;
3571
2943
  exports.moveEntry = moveEntry;
3572
2944
  exports.parkSignal = parkSignal;
3573
2945
  exports.phaseDefinitionToSnapshot = phaseDefinitionToSnapshot;
3574
- exports.phaseDraftShape = phaseDraftShape;
3575
2946
  exports.phaseShape = phaseShape;
3576
2947
  exports.phaseUpdateShape = phaseUpdateShape;
3577
2948
  exports.restoreWorkflow = restoreWorkflow;
3578
- exports.stepShape = stepShape;
3579
2949
  exports.success = success;
3580
2950
  exports.taskDefinitionToSnapshot = taskDefinitionToSnapshot;
3581
- exports.taskDraftShape = taskDraftShape;
3582
2951
  exports.taskShape = taskShape;
3583
2952
  exports.taskUpdateShape = taskUpdateShape;
3584
- exports.workflowDraftShape = workflowDraftShape;
3585
2953
  exports.workflowShape = workflowShape;
3586
- exports.workflowStepsShape = workflowStepsShape;
3587
- exports.workflowTag = workflowTag;
3588
- exports.workflowToolSummary = workflowToolSummary;
3589
2954
 
3590
2955
  //# sourceMappingURL=index.cjs.map