@theokit/sdk 2.26.0 → 2.27.0
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/CHANGELOG.md +28 -0
- package/dist/index.cjs +249 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +249 -45
- package/dist/index.js.map +1 -1
- package/dist/internal/workflow/ctx.d.ts +6 -1
- package/dist/internal/workflow/event-stream.d.ts +13 -0
- package/dist/internal/workflow/executor-helpers.d.ts +30 -0
- package/dist/types/workflow.d.ts +134 -1
- package/dist/workflow.cjs +289 -45
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +42 -2
- package/dist/workflow.d.ts +42 -2
- package/dist/workflow.js +288 -46
- package/dist/workflow.js.map +1 -1
- package/package.json +1 -1
package/dist/workflow.d.cts
CHANGED
|
@@ -26,7 +26,7 @@ import type { ZodType } from "zod";
|
|
|
26
26
|
import { z } from "zod";
|
|
27
27
|
import type { CustomTool, SDKAgent } from "./types/agent.js";
|
|
28
28
|
import type { MessageOrigin } from "./types/run.js";
|
|
29
|
-
import type { AgentStep, BranchStep, DowhileStep, FnStep, RetryPolicy, Step, StepContext, WorkflowOptions, WorkflowResumeOptions, WorkflowRun, WorkflowRunOptions } from "./types/workflow.js";
|
|
29
|
+
import type { AgentStep, BranchStep, DowhileStep, FnStep, RetryPolicy, Step, StepContext, WorkflowOptions, WorkflowResumeOptions, WorkflowRun, WorkflowRunOptions, WorkflowStream } from "./types/workflow.js";
|
|
30
30
|
export declare class WorkflowBuilder<TInput = unknown, TOutput = unknown> {
|
|
31
31
|
private readonly options;
|
|
32
32
|
private readonly _steps;
|
|
@@ -73,6 +73,22 @@ export declare class Workflow<TInput = unknown, TOutput = unknown> {
|
|
|
73
73
|
* `run.status === "failed"`.
|
|
74
74
|
*/
|
|
75
75
|
run(input: TInput, opts?: WorkflowRunOptions): Promise<WorkflowRun<TOutput>>;
|
|
76
|
+
/**
|
|
77
|
+
* SE28 — run the workflow and STREAM step-level events as they happen. Returns
|
|
78
|
+
* an async iterator of {@link WorkflowEvent}s (`step_started` / `step_completed`
|
|
79
|
+
* / `step_failed` / `workflow_suspended` / `workflow_completed`, top-level
|
|
80
|
+
* steps) plus a `result` promise resolving to the same terminal
|
|
81
|
+
* {@link WorkflowRun} `run()` returns. Iterate for progress; await `result` for
|
|
82
|
+
* the outcome. The stream ends when the run terminates.
|
|
83
|
+
*
|
|
84
|
+
* `result` is the AUTHORITATIVE terminal status. Not every terminal state has a
|
|
85
|
+
* closing event: a step failure emits `step_failed`, but an `outputSchema`
|
|
86
|
+
* rejection (SE27) or an abort ends the stream WITHOUT `workflow_completed` —
|
|
87
|
+
* always `await result` to read the final `status`. Consuming order is free:
|
|
88
|
+
* awaiting `result` without draining, or draining without awaiting `result`,
|
|
89
|
+
* both work (breaking out of `for await` stops the buffering early).
|
|
90
|
+
*/
|
|
91
|
+
stream(input: TInput, opts?: WorkflowRunOptions): WorkflowStream<TOutput>;
|
|
76
92
|
/**
|
|
77
93
|
* Resume a suspended workflow from its snapshot. Throws
|
|
78
94
|
* `WorkflowSnapshotNotFoundError` if `runId` is unknown.
|
|
@@ -95,9 +111,33 @@ export declare function agentStep(id: string, agent: SDKAgent, promptTemplate: s
|
|
|
95
111
|
retry?: RetryPolicy;
|
|
96
112
|
origin?: MessageOrigin;
|
|
97
113
|
}): AgentStep;
|
|
114
|
+
/**
|
|
115
|
+
* SE30 — use a committed {@link Workflow} as a step inside another workflow.
|
|
116
|
+
* Wraps the child as an `FnStep` (opaque — the child runs in its OWN executor
|
|
117
|
+
* with its own id-space, so nested step-ids never collide with the parent's).
|
|
118
|
+
* The child's output becomes the step output. A non-`completed` child fails the
|
|
119
|
+
* parent step with a typed {@link WorkflowNestedError}: nested `suspended` is NOT
|
|
120
|
+
* resumable in v1 (resume continues AFTER the step — the child would be skipped;
|
|
121
|
+
* use a top-level suspend). See ADR 0010.
|
|
122
|
+
*
|
|
123
|
+
* @public
|
|
124
|
+
*/
|
|
125
|
+
export declare function workflowStep<TI = unknown, TO = unknown>(child: Workflow<TI, TO>, opts?: {
|
|
126
|
+
id?: string;
|
|
127
|
+
}): FnStep;
|
|
128
|
+
/**
|
|
129
|
+
* SE30 — clone a committed workflow under a new id/name. The clone runs
|
|
130
|
+
* independently (its own single-flight lock + observability identity) with the
|
|
131
|
+
* same committed steps. Mirrors Mastra's `cloneWorkflow`.
|
|
132
|
+
*
|
|
133
|
+
* @public
|
|
134
|
+
*/
|
|
135
|
+
export declare function cloneWorkflow<TI = unknown, TO = unknown>(wf: Workflow<TI, TO>, opts: {
|
|
136
|
+
id: string;
|
|
137
|
+
}): Workflow<TI, TO>;
|
|
98
138
|
export { __resetSnapshotStoresForTests } from "./internal/workflow/snapshot-store.js";
|
|
99
139
|
export type * from "./types/workflow.js";
|
|
100
|
-
export { WorkflowAlreadyRunningError, WorkflowCompensateNotImplementedError, WorkflowDuplicateStepIdError, WorkflowMaxIterationsExceededError, WorkflowNotSerializableError, WorkflowParallelError, WorkflowResumeStepNotFoundError, WorkflowSnapshotNotFoundError, } from "./types/workflow.js";
|
|
140
|
+
export { WorkflowAlreadyRunningError, WorkflowCompensateNotImplementedError, WorkflowDuplicateStepIdError, WorkflowInputError, WorkflowMaxIterationsExceededError, WorkflowNestedError, WorkflowNotSerializableError, WorkflowOutputError, WorkflowParallelError, WorkflowResumeStepNotFoundError, WorkflowSnapshotNotFoundError, WorkflowStateError, } from "./types/workflow.js";
|
|
101
141
|
/**
|
|
102
142
|
* Raised by a {@link workflowAsTool} tool when the wrapped workflow run does not
|
|
103
143
|
* reach `status: "completed"` (a step failed, the run was cancelled/suspended).
|
package/dist/workflow.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ import type { ZodType } from "zod";
|
|
|
26
26
|
import { z } from "zod";
|
|
27
27
|
import type { CustomTool, SDKAgent } from "./types/agent.js";
|
|
28
28
|
import type { MessageOrigin } from "./types/run.js";
|
|
29
|
-
import type { AgentStep, BranchStep, DowhileStep, FnStep, RetryPolicy, Step, StepContext, WorkflowOptions, WorkflowResumeOptions, WorkflowRun, WorkflowRunOptions } from "./types/workflow.js";
|
|
29
|
+
import type { AgentStep, BranchStep, DowhileStep, FnStep, RetryPolicy, Step, StepContext, WorkflowOptions, WorkflowResumeOptions, WorkflowRun, WorkflowRunOptions, WorkflowStream } from "./types/workflow.js";
|
|
30
30
|
export declare class WorkflowBuilder<TInput = unknown, TOutput = unknown> {
|
|
31
31
|
private readonly options;
|
|
32
32
|
private readonly _steps;
|
|
@@ -73,6 +73,22 @@ export declare class Workflow<TInput = unknown, TOutput = unknown> {
|
|
|
73
73
|
* `run.status === "failed"`.
|
|
74
74
|
*/
|
|
75
75
|
run(input: TInput, opts?: WorkflowRunOptions): Promise<WorkflowRun<TOutput>>;
|
|
76
|
+
/**
|
|
77
|
+
* SE28 — run the workflow and STREAM step-level events as they happen. Returns
|
|
78
|
+
* an async iterator of {@link WorkflowEvent}s (`step_started` / `step_completed`
|
|
79
|
+
* / `step_failed` / `workflow_suspended` / `workflow_completed`, top-level
|
|
80
|
+
* steps) plus a `result` promise resolving to the same terminal
|
|
81
|
+
* {@link WorkflowRun} `run()` returns. Iterate for progress; await `result` for
|
|
82
|
+
* the outcome. The stream ends when the run terminates.
|
|
83
|
+
*
|
|
84
|
+
* `result` is the AUTHORITATIVE terminal status. Not every terminal state has a
|
|
85
|
+
* closing event: a step failure emits `step_failed`, but an `outputSchema`
|
|
86
|
+
* rejection (SE27) or an abort ends the stream WITHOUT `workflow_completed` —
|
|
87
|
+
* always `await result` to read the final `status`. Consuming order is free:
|
|
88
|
+
* awaiting `result` without draining, or draining without awaiting `result`,
|
|
89
|
+
* both work (breaking out of `for await` stops the buffering early).
|
|
90
|
+
*/
|
|
91
|
+
stream(input: TInput, opts?: WorkflowRunOptions): WorkflowStream<TOutput>;
|
|
76
92
|
/**
|
|
77
93
|
* Resume a suspended workflow from its snapshot. Throws
|
|
78
94
|
* `WorkflowSnapshotNotFoundError` if `runId` is unknown.
|
|
@@ -95,9 +111,33 @@ export declare function agentStep(id: string, agent: SDKAgent, promptTemplate: s
|
|
|
95
111
|
retry?: RetryPolicy;
|
|
96
112
|
origin?: MessageOrigin;
|
|
97
113
|
}): AgentStep;
|
|
114
|
+
/**
|
|
115
|
+
* SE30 — use a committed {@link Workflow} as a step inside another workflow.
|
|
116
|
+
* Wraps the child as an `FnStep` (opaque — the child runs in its OWN executor
|
|
117
|
+
* with its own id-space, so nested step-ids never collide with the parent's).
|
|
118
|
+
* The child's output becomes the step output. A non-`completed` child fails the
|
|
119
|
+
* parent step with a typed {@link WorkflowNestedError}: nested `suspended` is NOT
|
|
120
|
+
* resumable in v1 (resume continues AFTER the step — the child would be skipped;
|
|
121
|
+
* use a top-level suspend). See ADR 0010.
|
|
122
|
+
*
|
|
123
|
+
* @public
|
|
124
|
+
*/
|
|
125
|
+
export declare function workflowStep<TI = unknown, TO = unknown>(child: Workflow<TI, TO>, opts?: {
|
|
126
|
+
id?: string;
|
|
127
|
+
}): FnStep;
|
|
128
|
+
/**
|
|
129
|
+
* SE30 — clone a committed workflow under a new id/name. The clone runs
|
|
130
|
+
* independently (its own single-flight lock + observability identity) with the
|
|
131
|
+
* same committed steps. Mirrors Mastra's `cloneWorkflow`.
|
|
132
|
+
*
|
|
133
|
+
* @public
|
|
134
|
+
*/
|
|
135
|
+
export declare function cloneWorkflow<TI = unknown, TO = unknown>(wf: Workflow<TI, TO>, opts: {
|
|
136
|
+
id: string;
|
|
137
|
+
}): Workflow<TI, TO>;
|
|
98
138
|
export { __resetSnapshotStoresForTests } from "./internal/workflow/snapshot-store.js";
|
|
99
139
|
export type * from "./types/workflow.js";
|
|
100
|
-
export { WorkflowAlreadyRunningError, WorkflowCompensateNotImplementedError, WorkflowDuplicateStepIdError, WorkflowMaxIterationsExceededError, WorkflowNotSerializableError, WorkflowParallelError, WorkflowResumeStepNotFoundError, WorkflowSnapshotNotFoundError, } from "./types/workflow.js";
|
|
140
|
+
export { WorkflowAlreadyRunningError, WorkflowCompensateNotImplementedError, WorkflowDuplicateStepIdError, WorkflowInputError, WorkflowMaxIterationsExceededError, WorkflowNestedError, WorkflowNotSerializableError, WorkflowOutputError, WorkflowParallelError, WorkflowResumeStepNotFoundError, WorkflowSnapshotNotFoundError, WorkflowStateError, } from "./types/workflow.js";
|
|
101
141
|
/**
|
|
102
142
|
* Raised by a {@link workflowAsTool} tool when the wrapped workflow run does not
|
|
103
143
|
* reach `status: "completed"` (a step failed, the run was cancelled/suspended).
|
package/dist/workflow.js
CHANGED
|
@@ -210,7 +210,7 @@ var init_errors = __esm({
|
|
|
210
210
|
});
|
|
211
211
|
|
|
212
212
|
// src/types/workflow.ts
|
|
213
|
-
var WorkflowDuplicateStepIdError, WorkflowAlreadyRunningError, WorkflowSnapshotNotFoundError, WorkflowMaxIterationsExceededError, WorkflowNotSerializableError, WorkflowResumeStepNotFoundError, WorkflowParallelError, WorkflowCompensateNotImplementedError;
|
|
213
|
+
var WorkflowDuplicateStepIdError, WorkflowInputError, WorkflowOutputError, WorkflowStateError, WorkflowNestedError, WorkflowAlreadyRunningError, WorkflowSnapshotNotFoundError, WorkflowMaxIterationsExceededError, WorkflowNotSerializableError, WorkflowResumeStepNotFoundError, WorkflowParallelError, WorkflowCompensateNotImplementedError;
|
|
214
214
|
var init_workflow = __esm({
|
|
215
215
|
"src/types/workflow.ts"() {
|
|
216
216
|
WorkflowDuplicateStepIdError = class extends Error {
|
|
@@ -221,6 +221,57 @@ var init_workflow = __esm({
|
|
|
221
221
|
stepId;
|
|
222
222
|
name = "WorkflowDuplicateStepIdError";
|
|
223
223
|
};
|
|
224
|
+
WorkflowInputError = class extends Error {
|
|
225
|
+
constructor(workflowName, detail) {
|
|
226
|
+
super(`Workflow "${workflowName}" input failed schema validation: ${detail}`);
|
|
227
|
+
this.workflowName = workflowName;
|
|
228
|
+
this.detail = detail;
|
|
229
|
+
}
|
|
230
|
+
workflowName;
|
|
231
|
+
detail;
|
|
232
|
+
name = "WorkflowInputError";
|
|
233
|
+
};
|
|
234
|
+
WorkflowOutputError = class extends Error {
|
|
235
|
+
constructor(workflowName, detail) {
|
|
236
|
+
super(`Workflow "${workflowName}" output failed schema validation: ${detail}`);
|
|
237
|
+
this.workflowName = workflowName;
|
|
238
|
+
this.detail = detail;
|
|
239
|
+
}
|
|
240
|
+
workflowName;
|
|
241
|
+
detail;
|
|
242
|
+
name = "WorkflowOutputError";
|
|
243
|
+
};
|
|
244
|
+
WorkflowStateError = class extends Error {
|
|
245
|
+
constructor(workflowName, detail) {
|
|
246
|
+
super(`Workflow "${workflowName}" state failed schema validation: ${detail}`);
|
|
247
|
+
this.workflowName = workflowName;
|
|
248
|
+
this.detail = detail;
|
|
249
|
+
}
|
|
250
|
+
workflowName;
|
|
251
|
+
detail;
|
|
252
|
+
name = "WorkflowStateError";
|
|
253
|
+
};
|
|
254
|
+
WorkflowNestedError = class extends Error {
|
|
255
|
+
constructor(stepId, childName, childStatus, childError) {
|
|
256
|
+
super(
|
|
257
|
+
`Nested workflow "${childName}" (step "${stepId}") ended with status "${childStatus}"${childStatus === "suspended" ? " \u2014 nested suspend/resume is not supported in v1 (use a top-level suspend)" : ""}${childError ? `: ${childError.name}: ${childError.message}` : ""}`,
|
|
258
|
+
// Reconstruct a synthetic Error from the serialized child-error shape so
|
|
259
|
+
// debuggers surface the nested cause chain — the original Error instance is
|
|
260
|
+
// lost at the WorkflowRun serialization boundary, so this is the best
|
|
261
|
+
// achievable without changing the run protocol.
|
|
262
|
+
childError ? { cause: Object.assign(new Error(childError.message), { name: childError.name }) } : void 0
|
|
263
|
+
);
|
|
264
|
+
this.stepId = stepId;
|
|
265
|
+
this.childName = childName;
|
|
266
|
+
this.childStatus = childStatus;
|
|
267
|
+
this.childError = childError;
|
|
268
|
+
}
|
|
269
|
+
stepId;
|
|
270
|
+
childName;
|
|
271
|
+
childStatus;
|
|
272
|
+
childError;
|
|
273
|
+
name = "WorkflowNestedError";
|
|
274
|
+
};
|
|
224
275
|
WorkflowAlreadyRunningError = class extends Error {
|
|
225
276
|
constructor(workflowName, runId) {
|
|
226
277
|
super(`Workflow "${workflowName}" run "${runId}" already in-flight.`);
|
|
@@ -469,7 +520,7 @@ var init_snapshot_store = __esm({
|
|
|
469
520
|
});
|
|
470
521
|
|
|
471
522
|
// src/internal/workflow/ctx.ts
|
|
472
|
-
function makeStepContext(runId, signal) {
|
|
523
|
+
function makeStepContext(runId, signal, state2) {
|
|
473
524
|
return {
|
|
474
525
|
runId,
|
|
475
526
|
signal,
|
|
@@ -480,7 +531,13 @@ function makeStepContext(runId, signal) {
|
|
|
480
531
|
},
|
|
481
532
|
suspend: async (payload) => {
|
|
482
533
|
throw new WorkflowSuspendedSentinel(payload);
|
|
483
|
-
}
|
|
534
|
+
},
|
|
535
|
+
// SE29 — read reflects the current shared state; write goes through the
|
|
536
|
+
// controller (which validates against `stateSchema`).
|
|
537
|
+
get state() {
|
|
538
|
+
return state2.getState();
|
|
539
|
+
},
|
|
540
|
+
setState: (next) => state2.setState(next)
|
|
484
541
|
};
|
|
485
542
|
}
|
|
486
543
|
function emit(level, runId, msg, attrs) {
|
|
@@ -533,6 +590,57 @@ var init_error_shape = __esm({
|
|
|
533
590
|
}
|
|
534
591
|
});
|
|
535
592
|
|
|
593
|
+
// src/internal/workflow/executor-helpers.ts
|
|
594
|
+
function assembleRun(params) {
|
|
595
|
+
return {
|
|
596
|
+
id: params.runId,
|
|
597
|
+
name: params.name,
|
|
598
|
+
status: params.status,
|
|
599
|
+
startedAt: params.startedAt,
|
|
600
|
+
endedAt: Date.now(),
|
|
601
|
+
stepResults: params.stepResults,
|
|
602
|
+
...params.output !== void 0 ? { output: params.output } : {},
|
|
603
|
+
...params.error !== void 0 ? { error: params.error } : {}
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
function abortRun(name, runId, startedAt, stepResults, signal) {
|
|
607
|
+
return assembleRun({
|
|
608
|
+
runId,
|
|
609
|
+
name,
|
|
610
|
+
status: "cancelled",
|
|
611
|
+
stepResults,
|
|
612
|
+
startedAt,
|
|
613
|
+
error: { name: "AbortError", message: String(signal.reason ?? "Aborted") }
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
function validateWorkflowSchema(schema, value) {
|
|
617
|
+
if (schema === void 0) return void 0;
|
|
618
|
+
let parsed;
|
|
619
|
+
try {
|
|
620
|
+
parsed = schema.safeParse(value);
|
|
621
|
+
} catch {
|
|
622
|
+
return "schema uses async refinements, which whole-workflow validation does not support (use a synchronous Zod schema)";
|
|
623
|
+
}
|
|
624
|
+
if (parsed.success) return void 0;
|
|
625
|
+
return parsed.error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`).join("; ");
|
|
626
|
+
}
|
|
627
|
+
function makeStateController(options, initial) {
|
|
628
|
+
let current = initial;
|
|
629
|
+
return {
|
|
630
|
+
getState: () => current,
|
|
631
|
+
setState: (next) => {
|
|
632
|
+
const issues = validateWorkflowSchema(options.stateSchema, next);
|
|
633
|
+
if (issues !== void 0) throw new WorkflowStateError(options.name, issues);
|
|
634
|
+
current = next;
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
var init_executor_helpers = __esm({
|
|
639
|
+
"src/internal/workflow/executor-helpers.ts"() {
|
|
640
|
+
init_workflow();
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
|
|
536
644
|
// src/internal/workflow/run-id.ts
|
|
537
645
|
function mintRunId() {
|
|
538
646
|
return `wfr-${globalThis.crypto.randomUUID().replace(/-/g, "").slice(0, 8)}`;
|
|
@@ -1270,6 +1378,8 @@ async function handleSuspend(err, ctx) {
|
|
|
1270
1378
|
suspendedPayload: err.payload,
|
|
1271
1379
|
stepResults: ctx.stepResults,
|
|
1272
1380
|
accumulatedInput: ctx.acc,
|
|
1381
|
+
state: ctx.stepCtx.state,
|
|
1382
|
+
// SE29 — capture the shared state at suspend
|
|
1273
1383
|
options: ctx.options
|
|
1274
1384
|
});
|
|
1275
1385
|
} catch (snapErr) {
|
|
@@ -1321,7 +1431,7 @@ async function runOneStep(args) {
|
|
|
1321
1431
|
result = await dispatchStep(args.step, args.acc, args.ctx, args.options, args.stepResults);
|
|
1322
1432
|
} catch (err) {
|
|
1323
1433
|
if (err instanceof WorkflowSuspendedSentinel) {
|
|
1324
|
-
const outcome = await handleSuspend(err, { ...args, stepSpan });
|
|
1434
|
+
const outcome = await handleSuspend(err, { ...args, stepSpan, stepCtx: args.ctx });
|
|
1325
1435
|
return { kind: "terminal", run: outcome.run };
|
|
1326
1436
|
}
|
|
1327
1437
|
result = {
|
|
@@ -1338,22 +1448,42 @@ async function runOneStep(args) {
|
|
|
1338
1448
|
stepSpan.end();
|
|
1339
1449
|
return { kind: "ok", result };
|
|
1340
1450
|
}
|
|
1341
|
-
function
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1451
|
+
function handleStepOutcome(outcome, step, loop, onStepEvent) {
|
|
1452
|
+
if (outcome.kind === "terminal") {
|
|
1453
|
+
if (outcome.run.status === "suspended") {
|
|
1454
|
+
onStepEvent?.({ type: "workflow_suspended", stepId: step.id });
|
|
1455
|
+
}
|
|
1456
|
+
return { terminal: outcome.run };
|
|
1457
|
+
}
|
|
1458
|
+
loop.stepResults.push(outcome.result);
|
|
1459
|
+
if (outcome.result.status === "failed") {
|
|
1460
|
+
onStepEvent?.({
|
|
1461
|
+
type: "step_failed",
|
|
1462
|
+
stepId: step.id,
|
|
1463
|
+
error: outcome.result.error ?? { name: "WorkflowStepError", message: "step failed" }
|
|
1464
|
+
});
|
|
1465
|
+
return {
|
|
1466
|
+
terminal: assembleRun({
|
|
1467
|
+
runId: loop.runId,
|
|
1468
|
+
name: loop.name,
|
|
1469
|
+
status: "failed",
|
|
1470
|
+
stepResults: loop.stepResults,
|
|
1471
|
+
startedAt: loop.startedAt,
|
|
1472
|
+
error: outcome.result.error
|
|
1473
|
+
})
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
onStepEvent?.({ type: "step_completed", stepId: step.id, output: outcome.result.output });
|
|
1477
|
+
return { acc: outcome.result.output };
|
|
1350
1478
|
}
|
|
1351
1479
|
async function runStepsLoop(params) {
|
|
1352
|
-
const { options, steps, ctx, runId, startedAt, signal } = params;
|
|
1480
|
+
const { options, steps, ctx, runId, startedAt, signal, onStepEvent } = params;
|
|
1353
1481
|
const stepResults = [...params.initialStepResults ?? []];
|
|
1482
|
+
const loop = { stepResults, runId, name: options.name, startedAt };
|
|
1354
1483
|
let acc = params.input;
|
|
1355
1484
|
for (const step of steps) {
|
|
1356
1485
|
if (signal.aborted) return abortRun(options.name, runId, startedAt, stepResults, signal);
|
|
1486
|
+
onStepEvent?.({ type: "step_started", stepId: step.id });
|
|
1357
1487
|
const outcome = await runOneStep({
|
|
1358
1488
|
step,
|
|
1359
1489
|
acc,
|
|
@@ -1364,20 +1494,22 @@ async function runStepsLoop(params) {
|
|
|
1364
1494
|
name: options.name,
|
|
1365
1495
|
startedAt
|
|
1366
1496
|
});
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
return assembleRun({
|
|
1371
|
-
runId,
|
|
1372
|
-
name: options.name,
|
|
1373
|
-
status: "failed",
|
|
1374
|
-
stepResults,
|
|
1375
|
-
startedAt,
|
|
1376
|
-
error: outcome.result.error
|
|
1377
|
-
});
|
|
1378
|
-
}
|
|
1379
|
-
acc = outcome.result.output;
|
|
1497
|
+
const handled = handleStepOutcome(outcome, step, loop, onStepEvent);
|
|
1498
|
+
if ("terminal" in handled) return handled.terminal;
|
|
1499
|
+
acc = handled.acc;
|
|
1380
1500
|
}
|
|
1501
|
+
const outputIssues = validateWorkflowSchema(options.outputSchema, acc);
|
|
1502
|
+
if (outputIssues !== void 0) {
|
|
1503
|
+
return assembleRun({
|
|
1504
|
+
runId,
|
|
1505
|
+
name: options.name,
|
|
1506
|
+
status: "failed",
|
|
1507
|
+
stepResults,
|
|
1508
|
+
startedAt,
|
|
1509
|
+
error: errToShape(new WorkflowOutputError(options.name, outputIssues))
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1512
|
+
onStepEvent?.({ type: "workflow_completed" });
|
|
1381
1513
|
return assembleRun({
|
|
1382
1514
|
runId,
|
|
1383
1515
|
name: options.name,
|
|
@@ -1400,8 +1532,33 @@ async function executeWorkflow(options, steps, input, runOpts) {
|
|
|
1400
1532
|
runSpan.end();
|
|
1401
1533
|
return abortRun(options.name, runId, startedAt, [], signal);
|
|
1402
1534
|
}
|
|
1403
|
-
const
|
|
1535
|
+
const internal = runOpts;
|
|
1536
|
+
const seededState = internal?.restoredState !== void 0 ? internal.restoredState : options.initialState;
|
|
1537
|
+
const stateController = makeStateController(options, seededState);
|
|
1538
|
+
const ctx = makeStepContext(runId, signal, stateController);
|
|
1404
1539
|
try {
|
|
1540
|
+
const inputIssues = validateWorkflowSchema(options.inputSchema, input);
|
|
1541
|
+
if (inputIssues !== void 0) {
|
|
1542
|
+
return assembleRun({
|
|
1543
|
+
runId,
|
|
1544
|
+
name: options.name,
|
|
1545
|
+
status: "failed",
|
|
1546
|
+
stepResults: [],
|
|
1547
|
+
startedAt,
|
|
1548
|
+
error: errToShape(new WorkflowInputError(options.name, inputIssues))
|
|
1549
|
+
});
|
|
1550
|
+
}
|
|
1551
|
+
const stateIssues = seededState !== void 0 ? validateWorkflowSchema(options.stateSchema, seededState) : void 0;
|
|
1552
|
+
if (stateIssues !== void 0) {
|
|
1553
|
+
return assembleRun({
|
|
1554
|
+
runId,
|
|
1555
|
+
name: options.name,
|
|
1556
|
+
status: "failed",
|
|
1557
|
+
stepResults: [],
|
|
1558
|
+
startedAt,
|
|
1559
|
+
error: errToShape(new WorkflowStateError(options.name, stateIssues))
|
|
1560
|
+
});
|
|
1561
|
+
}
|
|
1405
1562
|
return await runStepsLoop({
|
|
1406
1563
|
options,
|
|
1407
1564
|
steps,
|
|
@@ -1410,7 +1567,8 @@ async function executeWorkflow(options, steps, input, runOpts) {
|
|
|
1410
1567
|
runId,
|
|
1411
1568
|
startedAt,
|
|
1412
1569
|
signal,
|
|
1413
|
-
...
|
|
1570
|
+
...internal?.initialStepResults !== void 0 ? { initialStepResults: internal.initialStepResults } : {},
|
|
1571
|
+
...internal?.onStepEvent !== void 0 ? { onStepEvent: internal.onStepEvent } : {}
|
|
1414
1572
|
});
|
|
1415
1573
|
} finally {
|
|
1416
1574
|
runSpan.end();
|
|
@@ -1441,28 +1599,17 @@ async function dispatchStep(step, input, ctx, options, prevStepResults) {
|
|
|
1441
1599
|
}
|
|
1442
1600
|
}
|
|
1443
1601
|
}
|
|
1444
|
-
function assembleRun(params) {
|
|
1445
|
-
const endedAt = Date.now();
|
|
1446
|
-
return {
|
|
1447
|
-
id: params.runId,
|
|
1448
|
-
name: params.name,
|
|
1449
|
-
status: params.status,
|
|
1450
|
-
startedAt: params.startedAt,
|
|
1451
|
-
endedAt,
|
|
1452
|
-
stepResults: params.stepResults,
|
|
1453
|
-
...params.output !== void 0 ? { output: params.output } : {},
|
|
1454
|
-
...params.error !== void 0 ? { error: params.error } : {}
|
|
1455
|
-
};
|
|
1456
|
-
}
|
|
1457
1602
|
async function saveSnapshot(p) {
|
|
1458
1603
|
const snapshot = {
|
|
1459
|
-
_schemaVersion:
|
|
1604
|
+
_schemaVersion: 2,
|
|
1605
|
+
// SE29 — carries `state`
|
|
1460
1606
|
runId: p.runId,
|
|
1461
1607
|
workflowName: p.workflowName,
|
|
1462
1608
|
currentStepId: p.currentStepId,
|
|
1463
1609
|
suspendedPayload: p.suspendedPayload,
|
|
1464
1610
|
stepResults: p.stepResults,
|
|
1465
1611
|
accumulatedInput: p.accumulatedInput,
|
|
1612
|
+
...p.state !== void 0 ? { state: p.state } : {},
|
|
1466
1613
|
suspendedAt: Date.now()
|
|
1467
1614
|
};
|
|
1468
1615
|
const store = getSnapshotStoreFor(p.options);
|
|
@@ -1495,7 +1642,10 @@ async function resumeWorkflow(opts) {
|
|
|
1495
1642
|
signal: opts.signal,
|
|
1496
1643
|
runId: opts.runId,
|
|
1497
1644
|
// M3 #62 — restore prior step outputs so the resumed run is not lossy (internal seam).
|
|
1498
|
-
initialStepResults: snapshot.stepResults
|
|
1645
|
+
initialStepResults: snapshot.stepResults,
|
|
1646
|
+
// SE29 — restore shared state (v2 snapshot). A v1 snapshot has no `state` →
|
|
1647
|
+
// executeWorkflow falls back to `options.initialState`.
|
|
1648
|
+
...snapshot.state !== void 0 ? { restoredState: snapshot.state } : {}
|
|
1499
1649
|
});
|
|
1500
1650
|
}
|
|
1501
1651
|
var init_executor = __esm({
|
|
@@ -1503,6 +1653,7 @@ var init_executor = __esm({
|
|
|
1503
1653
|
init_workflow();
|
|
1504
1654
|
init_ctx();
|
|
1505
1655
|
init_error_shape();
|
|
1656
|
+
init_executor_helpers();
|
|
1506
1657
|
init_run_id();
|
|
1507
1658
|
init_single_flight();
|
|
1508
1659
|
init_snapshot_store();
|
|
@@ -2241,6 +2392,43 @@ function sanitizeIdentifier(input, options) {
|
|
|
2241
2392
|
}
|
|
2242
2393
|
return input.toLowerCase();
|
|
2243
2394
|
}
|
|
2395
|
+
|
|
2396
|
+
// src/internal/workflow/event-stream.ts
|
|
2397
|
+
function createEventStream() {
|
|
2398
|
+
const buffer = [];
|
|
2399
|
+
const waiters = [];
|
|
2400
|
+
let ended = false;
|
|
2401
|
+
const stream = {
|
|
2402
|
+
push(event) {
|
|
2403
|
+
if (ended) return;
|
|
2404
|
+
const waiter = waiters.shift();
|
|
2405
|
+
if (waiter !== void 0) waiter({ value: event, done: false });
|
|
2406
|
+
else buffer.push(event);
|
|
2407
|
+
},
|
|
2408
|
+
end() {
|
|
2409
|
+
if (ended) return;
|
|
2410
|
+
ended = true;
|
|
2411
|
+
for (const waiter of waiters.splice(0)) waiter({ value: void 0, done: true });
|
|
2412
|
+
},
|
|
2413
|
+
next() {
|
|
2414
|
+
const buffered = buffer.shift();
|
|
2415
|
+
if (buffered !== void 0) return Promise.resolve({ value: buffered, done: false });
|
|
2416
|
+
if (ended) return Promise.resolve({ value: void 0, done: true });
|
|
2417
|
+
return new Promise((resolve2) => waiters.push(resolve2));
|
|
2418
|
+
},
|
|
2419
|
+
// `for await` calls return() on break/throw — close early so events stop
|
|
2420
|
+
// buffering in memory for a consumer that stopped iterating.
|
|
2421
|
+
return() {
|
|
2422
|
+
stream.end();
|
|
2423
|
+
buffer.length = 0;
|
|
2424
|
+
return Promise.resolve({ value: void 0, done: true });
|
|
2425
|
+
},
|
|
2426
|
+
[Symbol.asyncIterator]() {
|
|
2427
|
+
return stream;
|
|
2428
|
+
}
|
|
2429
|
+
};
|
|
2430
|
+
return stream;
|
|
2431
|
+
}
|
|
2244
2432
|
function toJsonSchema(schema, options = { unrepresentable: "any" }) {
|
|
2245
2433
|
return toJSONSchema(schema, options);
|
|
2246
2434
|
}
|
|
@@ -2259,7 +2447,12 @@ var RetryPolicySchema = z.object({
|
|
|
2259
2447
|
});
|
|
2260
2448
|
var WorkflowOptionsSchema = z.object({
|
|
2261
2449
|
name: z.string().min(1).max(128),
|
|
2262
|
-
persistence: PersistenceSchema
|
|
2450
|
+
persistence: PersistenceSchema,
|
|
2451
|
+
// SE27 — declared so a future `new WorkflowBuilder(parsed)` refactor cannot
|
|
2452
|
+
// silently drop them (create() passes the ORIGINAL options today, but the
|
|
2453
|
+
// schema is also the documentation of the shape).
|
|
2454
|
+
inputSchema: z.custom().optional(),
|
|
2455
|
+
outputSchema: z.custom().optional()
|
|
2263
2456
|
});
|
|
2264
2457
|
var WorkflowBuilder = class {
|
|
2265
2458
|
/** @internal */
|
|
@@ -2436,6 +2629,36 @@ var Workflow = class {
|
|
|
2436
2629
|
}
|
|
2437
2630
|
return result;
|
|
2438
2631
|
}
|
|
2632
|
+
/**
|
|
2633
|
+
* SE28 — run the workflow and STREAM step-level events as they happen. Returns
|
|
2634
|
+
* an async iterator of {@link WorkflowEvent}s (`step_started` / `step_completed`
|
|
2635
|
+
* / `step_failed` / `workflow_suspended` / `workflow_completed`, top-level
|
|
2636
|
+
* steps) plus a `result` promise resolving to the same terminal
|
|
2637
|
+
* {@link WorkflowRun} `run()` returns. Iterate for progress; await `result` for
|
|
2638
|
+
* the outcome. The stream ends when the run terminates.
|
|
2639
|
+
*
|
|
2640
|
+
* `result` is the AUTHORITATIVE terminal status. Not every terminal state has a
|
|
2641
|
+
* closing event: a step failure emits `step_failed`, but an `outputSchema`
|
|
2642
|
+
* rejection (SE27) or an abort ends the stream WITHOUT `workflow_completed` —
|
|
2643
|
+
* always `await result` to read the final `status`. Consuming order is free:
|
|
2644
|
+
* awaiting `result` without draining, or draining without awaiting `result`,
|
|
2645
|
+
* both work (breaking out of `for await` stops the buffering early).
|
|
2646
|
+
*/
|
|
2647
|
+
stream(input, opts) {
|
|
2648
|
+
const queue = createEventStream();
|
|
2649
|
+
const result = (async () => {
|
|
2650
|
+
const { executeWorkflow: executeWorkflow2 } = await Promise.resolve().then(() => (init_executor(), executor_exports));
|
|
2651
|
+
try {
|
|
2652
|
+
return await executeWorkflow2(this._options, this._steps, input, {
|
|
2653
|
+
...opts,
|
|
2654
|
+
onStepEvent: (event) => queue.push(event)
|
|
2655
|
+
});
|
|
2656
|
+
} finally {
|
|
2657
|
+
queue.end();
|
|
2658
|
+
}
|
|
2659
|
+
})();
|
|
2660
|
+
return Object.assign(queue, { result });
|
|
2661
|
+
}
|
|
2439
2662
|
/**
|
|
2440
2663
|
* Resume a suspended workflow from its snapshot. Throws
|
|
2441
2664
|
* `WorkflowSnapshotNotFoundError` if `runId` is unknown.
|
|
@@ -2470,6 +2693,25 @@ function agentStep(id, agent, promptTemplate, opts) {
|
|
|
2470
2693
|
...opts?.origin !== void 0 ? { origin: opts.origin } : {}
|
|
2471
2694
|
};
|
|
2472
2695
|
}
|
|
2696
|
+
function workflowStep(child, opts) {
|
|
2697
|
+
const id = opts?.id ?? `workflow_${child.__options.name}`;
|
|
2698
|
+
validateStepId(id);
|
|
2699
|
+
return {
|
|
2700
|
+
kind: "fn",
|
|
2701
|
+
id,
|
|
2702
|
+
fn: async (input, ctx) => {
|
|
2703
|
+
const run = await child.run(input, { signal: ctx.signal });
|
|
2704
|
+
if (run.status === "completed") return run.output;
|
|
2705
|
+
throw new WorkflowNestedError(id, child.__options.name, run.status, run.error);
|
|
2706
|
+
}
|
|
2707
|
+
};
|
|
2708
|
+
}
|
|
2709
|
+
function cloneWorkflow(wf, opts) {
|
|
2710
|
+
return new Workflow(
|
|
2711
|
+
{ ...wf.__options, name: opts.id, workflowId: `wf-${mintShortId()}` },
|
|
2712
|
+
[...wf.__steps]
|
|
2713
|
+
);
|
|
2714
|
+
}
|
|
2473
2715
|
var WorkflowToolError = class extends Error {
|
|
2474
2716
|
constructor(toolName, workflowStatus, workflowError) {
|
|
2475
2717
|
super(
|
|
@@ -2527,6 +2769,6 @@ function walkStepsValidating(steps, seen) {
|
|
|
2527
2769
|
}
|
|
2528
2770
|
}
|
|
2529
2771
|
|
|
2530
|
-
export { Workflow, WorkflowAlreadyRunningError, WorkflowBuilder, WorkflowCompensateNotImplementedError, WorkflowDuplicateStepIdError, WorkflowMaxIterationsExceededError, WorkflowNotSerializableError, WorkflowParallelError, WorkflowResumeStepNotFoundError, WorkflowSnapshotNotFoundError, WorkflowToolError, __resetSnapshotStoresForTests, agentStep, fn, workflowAsTool };
|
|
2772
|
+
export { Workflow, WorkflowAlreadyRunningError, WorkflowBuilder, WorkflowCompensateNotImplementedError, WorkflowDuplicateStepIdError, WorkflowInputError, WorkflowMaxIterationsExceededError, WorkflowNestedError, WorkflowNotSerializableError, WorkflowOutputError, WorkflowParallelError, WorkflowResumeStepNotFoundError, WorkflowSnapshotNotFoundError, WorkflowStateError, WorkflowToolError, __resetSnapshotStoresForTests, agentStep, cloneWorkflow, fn, workflowAsTool, workflowStep };
|
|
2531
2773
|
//# sourceMappingURL=workflow.js.map
|
|
2532
2774
|
//# sourceMappingURL=workflow.js.map
|