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