@theokit/sdk 2.25.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.
Files changed (47) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/dist/a2a/index.cjs +208 -4
  3. package/dist/a2a/index.cjs.map +1 -1
  4. package/dist/a2a/index.js +208 -4
  5. package/dist/a2a/index.js.map +1 -1
  6. package/dist/{cron-B44D-678.d.ts → cron-BR1NCSk1.d.cts} +11 -1
  7. package/dist/{cron-qI-dbG7c.d.cts → cron-DgEQCJ2i.d.ts} +11 -1
  8. package/dist/cron.cjs +187 -4
  9. package/dist/cron.cjs.map +1 -1
  10. package/dist/cron.d.cts +2 -2
  11. package/dist/cron.d.ts +2 -2
  12. package/dist/cron.js +187 -4
  13. package/dist/cron.js.map +1 -1
  14. package/dist/{errors-DRS-kqOK.d.ts → errors-CbY3pxY7.d.ts} +1 -1
  15. package/dist/{errors-DIKBXffg.d.cts → errors-DLMNb4Ka.d.cts} +1 -1
  16. package/dist/errors.d.cts +2 -2
  17. package/dist/eval.cjs +187 -4
  18. package/dist/eval.cjs.map +1 -1
  19. package/dist/eval.js +187 -4
  20. package/dist/eval.js.map +1 -1
  21. package/dist/index.cjs +483 -51
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.cts +69 -7
  24. package/dist/index.d.ts +69 -7
  25. package/dist/index.js +481 -52
  26. package/dist/index.js.map +1 -1
  27. package/dist/internal/runtime/processors/run-processors.d.ts +10 -0
  28. package/dist/internal/runtime/processors/tripwire-run.d.ts +16 -0
  29. package/dist/internal/runtime/processors/wrap-output-run.d.ts +18 -0
  30. package/dist/internal/workflow/ctx.d.ts +6 -1
  31. package/dist/internal/workflow/event-stream.d.ts +13 -0
  32. package/dist/internal/workflow/executor-helpers.d.ts +30 -0
  33. package/dist/{run-Cr0C6cOM.d.cts → run-CdWiihyU.d.cts} +109 -2
  34. package/dist/{run-Cr0C6cOM.d.ts → run-CdWiihyU.d.ts} +109 -2
  35. package/dist/types/agent.d.ts +10 -0
  36. package/dist/types/index.d.ts +1 -0
  37. package/dist/types/processors.d.ts +84 -0
  38. package/dist/types/run-events.d.ts +11 -1
  39. package/dist/types/run.d.ts +12 -0
  40. package/dist/types/workflow.d.ts +134 -1
  41. package/dist/workflow.cjs +289 -45
  42. package/dist/workflow.cjs.map +1 -1
  43. package/dist/workflow.d.cts +42 -2
  44. package/dist/workflow.d.ts +42 -2
  45. package/dist/workflow.js +288 -46
  46. package/dist/workflow.js.map +1 -1
  47. package/package.json +1 -1
@@ -101,6 +101,19 @@ export interface StepContext {
101
101
  };
102
102
  /** Pause the workflow; resume via `Workflow.resume({...})`. */
103
103
  readonly suspend: (payload?: unknown) => Promise<never>;
104
+ /**
105
+ * SE29 — the workflow's shared state (from `WorkflowOptions.initialState`,
106
+ * mutated by {@link setState}), visible to every subsequent step in the run.
107
+ * `undefined` when no `initialState`/`setState` has run. Persisted across
108
+ * suspend/resume.
109
+ */
110
+ readonly state: unknown;
111
+ /**
112
+ * SE29 — update the shared state for subsequent steps. Validated against
113
+ * `WorkflowOptions.stateSchema` when set (a mismatch throws
114
+ * {@link WorkflowStateError}, which fails the step/run — Rule 8).
115
+ */
116
+ readonly setState: (next: unknown) => void;
104
117
  }
105
118
  export interface StepResult {
106
119
  readonly stepId: string;
@@ -128,15 +141,57 @@ export interface WorkflowRun<TOutput = unknown> {
128
141
  readonly stepResults: ReadonlyArray<StepResult>;
129
142
  }
130
143
  export interface WorkflowSnapshot {
131
- readonly _schemaVersion: 1;
144
+ /** v1 = pre-SE29 (no `state`); v2 = SE29 (carries `state`). Resume reads both. */
145
+ readonly _schemaVersion: 1 | 2;
132
146
  readonly runId: string;
133
147
  readonly workflowName: string;
134
148
  readonly currentStepId: string;
135
149
  readonly suspendedPayload?: unknown;
136
150
  readonly stepResults: ReadonlyArray<StepResult>;
137
151
  readonly accumulatedInput: unknown;
152
+ /** SE29 — shared state captured at suspend (v2). Absent on a v1 snapshot. */
153
+ readonly state?: unknown;
138
154
  readonly suspendedAt: number;
139
155
  }
156
+ /**
157
+ * SE28 — a step-level workflow event emitted by `Workflow.stream()` as top-level
158
+ * steps run. Coarse-grained (one event per top-level step; nested
159
+ * parallel/branch/foreach emit as their single wrapping step), distinct from the
160
+ * token-delta agent stream. Discriminate on `type`.
161
+ *
162
+ * @public
163
+ */
164
+ export type WorkflowEvent = {
165
+ readonly type: "step_started";
166
+ readonly stepId: string;
167
+ } | {
168
+ readonly type: "step_completed";
169
+ readonly stepId: string;
170
+ readonly output: unknown;
171
+ } | {
172
+ readonly type: "step_failed";
173
+ readonly stepId: string;
174
+ readonly error: {
175
+ readonly name: string;
176
+ readonly message: string;
177
+ };
178
+ } | {
179
+ readonly type: "workflow_suspended";
180
+ readonly stepId: string;
181
+ } | {
182
+ readonly type: "workflow_completed";
183
+ };
184
+ /**
185
+ * SE28 — the async iterator returned by `Workflow.stream()`. Yields
186
+ * {@link WorkflowEvent}s in execution order; `result` resolves to the same
187
+ * terminal {@link WorkflowRun} the `run()` path returns (the authoritative
188
+ * outcome — the stream ends when the run terminates).
189
+ *
190
+ * @public
191
+ */
192
+ export type WorkflowStream<TOutput = unknown> = AsyncIterableIterator<WorkflowEvent> & {
193
+ readonly result: Promise<WorkflowRun<TOutput>>;
194
+ };
140
195
  export interface WorkflowPersistenceOptions {
141
196
  readonly backend: "memory" | "json";
142
197
  /** Required for `backend: "json"`. */
@@ -145,6 +200,34 @@ export interface WorkflowPersistenceOptions {
145
200
  export interface WorkflowOptions {
146
201
  readonly name: string;
147
202
  readonly persistence?: WorkflowPersistenceOptions;
203
+ /**
204
+ * SE27 — Zod schema for the WHOLE workflow's input. When set, `run(input)`
205
+ * validates `input` BEFORE step 1; a mismatch yields `status: "failed"` with a
206
+ * typed {@link WorkflowInputError} in `error` (fail-fast, no step runs, no
207
+ * silent coerce). Absent ⇒ no whole-workflow input validation (unchanged).
208
+ */
209
+ readonly inputSchema?: ZodType;
210
+ /**
211
+ * SE27 — Zod schema for the workflow's final output. When set, the terminal
212
+ * `completed` output is validated before `WorkflowRun.output` is populated; a
213
+ * mismatch yields `status: "failed"` with a typed {@link WorkflowOutputError}.
214
+ * Only validated on the `completed` path (suspended/failed runs skip it).
215
+ */
216
+ readonly outputSchema?: ZodType;
217
+ /**
218
+ * SE29 — Zod schema for the workflow's shared state (see `StepContext.state` /
219
+ * `setState`). When set, `initialState` and every `setState(next)` are
220
+ * validated against it (a mismatch throws {@link WorkflowStateError}). When
221
+ * `initialState` is absent, `state` starts as `undefined` and validation fires
222
+ * on the first `setState` call.
223
+ */
224
+ readonly stateSchema?: ZodType;
225
+ /**
226
+ * SE29 — the initial shared state, seeded onto `StepContext.state` before
227
+ * step 1. Validated against `stateSchema` when both are set. Persisted across
228
+ * suspend/resume.
229
+ */
230
+ readonly initialState?: unknown;
148
231
  /** Internal — minted at `.commit()`. Not user-facing. */
149
232
  readonly workflowId?: string;
150
233
  }
@@ -179,6 +262,56 @@ export declare class WorkflowDuplicateStepIdError extends Error {
179
262
  readonly name = "WorkflowDuplicateStepIdError";
180
263
  constructor(stepId: string);
181
264
  }
265
+ /**
266
+ * SE27 — the whole-workflow `inputSchema` rejected `run(input)` (before step 1).
267
+ * `detail` is a pre-formatted issues summary (a string, NOT Zod's `ZodIssue[]`).
268
+ */
269
+ export declare class WorkflowInputError extends Error {
270
+ readonly workflowName: string;
271
+ readonly detail: string;
272
+ readonly name = "WorkflowInputError";
273
+ constructor(workflowName: string, detail: string);
274
+ }
275
+ /**
276
+ * SE27 — the whole-workflow `outputSchema` rejected the final output (on `completed`).
277
+ * `detail` is a pre-formatted issues summary (a string, NOT Zod's `ZodIssue[]`).
278
+ */
279
+ export declare class WorkflowOutputError extends Error {
280
+ readonly workflowName: string;
281
+ readonly detail: string;
282
+ readonly name = "WorkflowOutputError";
283
+ constructor(workflowName: string, detail: string);
284
+ }
285
+ /**
286
+ * SE29 — `WorkflowOptions.stateSchema` rejected an `initialState` or a
287
+ * `setState(next)` call. `detail` is a pre-formatted issues summary.
288
+ */
289
+ export declare class WorkflowStateError extends Error {
290
+ readonly workflowName: string;
291
+ readonly detail: string;
292
+ readonly name = "WorkflowStateError";
293
+ constructor(workflowName: string, detail: string);
294
+ }
295
+ /**
296
+ * SE30 — a nested workflow (via `workflowStep`) did not `complete`. A nested
297
+ * `suspended` is NOT resumable in v1 (resume continues AFTER the step, so the
298
+ * child would be skipped) — restructure with a top-level suspend. A nested
299
+ * `failed`/`cancelled` fails the parent step with the child's error attached.
300
+ */
301
+ export declare class WorkflowNestedError extends Error {
302
+ readonly stepId: string;
303
+ readonly childName: string;
304
+ readonly childStatus: Exclude<WorkflowRun["status"], "completed">;
305
+ readonly childError?: {
306
+ name: string;
307
+ message: string;
308
+ } | undefined;
309
+ readonly name = "WorkflowNestedError";
310
+ constructor(stepId: string, childName: string, childStatus: Exclude<WorkflowRun["status"], "completed">, childError?: {
311
+ name: string;
312
+ message: string;
313
+ } | undefined);
314
+ }
182
315
  export declare class WorkflowAlreadyRunningError extends Error {
183
316
  readonly workflowName: string;
184
317
  readonly runId: string;
package/dist/workflow.cjs CHANGED
@@ -213,7 +213,7 @@ var init_errors = __esm({
213
213
  });
214
214
 
215
215
  // src/types/workflow.ts
216
- exports.WorkflowDuplicateStepIdError = void 0; exports.WorkflowAlreadyRunningError = void 0; exports.WorkflowSnapshotNotFoundError = void 0; exports.WorkflowMaxIterationsExceededError = void 0; exports.WorkflowNotSerializableError = void 0; exports.WorkflowResumeStepNotFoundError = void 0; exports.WorkflowParallelError = void 0; exports.WorkflowCompensateNotImplementedError = void 0;
216
+ exports.WorkflowDuplicateStepIdError = void 0; exports.WorkflowInputError = void 0; exports.WorkflowOutputError = void 0; exports.WorkflowStateError = void 0; exports.WorkflowNestedError = void 0; exports.WorkflowAlreadyRunningError = void 0; exports.WorkflowSnapshotNotFoundError = void 0; exports.WorkflowMaxIterationsExceededError = void 0; exports.WorkflowNotSerializableError = void 0; exports.WorkflowResumeStepNotFoundError = void 0; exports.WorkflowParallelError = void 0; exports.WorkflowCompensateNotImplementedError = void 0;
217
217
  var init_workflow = __esm({
218
218
  "src/types/workflow.ts"() {
219
219
  exports.WorkflowDuplicateStepIdError = class extends Error {
@@ -224,6 +224,57 @@ var init_workflow = __esm({
224
224
  stepId;
225
225
  name = "WorkflowDuplicateStepIdError";
226
226
  };
227
+ exports.WorkflowInputError = class extends Error {
228
+ constructor(workflowName, detail) {
229
+ super(`Workflow "${workflowName}" input failed schema validation: ${detail}`);
230
+ this.workflowName = workflowName;
231
+ this.detail = detail;
232
+ }
233
+ workflowName;
234
+ detail;
235
+ name = "WorkflowInputError";
236
+ };
237
+ exports.WorkflowOutputError = class extends Error {
238
+ constructor(workflowName, detail) {
239
+ super(`Workflow "${workflowName}" output failed schema validation: ${detail}`);
240
+ this.workflowName = workflowName;
241
+ this.detail = detail;
242
+ }
243
+ workflowName;
244
+ detail;
245
+ name = "WorkflowOutputError";
246
+ };
247
+ exports.WorkflowStateError = class extends Error {
248
+ constructor(workflowName, detail) {
249
+ super(`Workflow "${workflowName}" state failed schema validation: ${detail}`);
250
+ this.workflowName = workflowName;
251
+ this.detail = detail;
252
+ }
253
+ workflowName;
254
+ detail;
255
+ name = "WorkflowStateError";
256
+ };
257
+ exports.WorkflowNestedError = class extends Error {
258
+ constructor(stepId, childName, childStatus, childError) {
259
+ super(
260
+ `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}` : ""}`,
261
+ // Reconstruct a synthetic Error from the serialized child-error shape so
262
+ // debuggers surface the nested cause chain — the original Error instance is
263
+ // lost at the WorkflowRun serialization boundary, so this is the best
264
+ // achievable without changing the run protocol.
265
+ childError ? { cause: Object.assign(new Error(childError.message), { name: childError.name }) } : void 0
266
+ );
267
+ this.stepId = stepId;
268
+ this.childName = childName;
269
+ this.childStatus = childStatus;
270
+ this.childError = childError;
271
+ }
272
+ stepId;
273
+ childName;
274
+ childStatus;
275
+ childError;
276
+ name = "WorkflowNestedError";
277
+ };
227
278
  exports.WorkflowAlreadyRunningError = class extends Error {
228
279
  constructor(workflowName, runId) {
229
280
  super(`Workflow "${workflowName}" run "${runId}" already in-flight.`);
@@ -472,7 +523,7 @@ var init_snapshot_store = __esm({
472
523
  });
473
524
 
474
525
  // src/internal/workflow/ctx.ts
475
- function makeStepContext(runId, signal) {
526
+ function makeStepContext(runId, signal, state2) {
476
527
  return {
477
528
  runId,
478
529
  signal,
@@ -483,7 +534,13 @@ function makeStepContext(runId, signal) {
483
534
  },
484
535
  suspend: async (payload) => {
485
536
  throw new WorkflowSuspendedSentinel(payload);
486
- }
537
+ },
538
+ // SE29 — read reflects the current shared state; write goes through the
539
+ // controller (which validates against `stateSchema`).
540
+ get state() {
541
+ return state2.getState();
542
+ },
543
+ setState: (next) => state2.setState(next)
487
544
  };
488
545
  }
489
546
  function emit(level, runId, msg, attrs) {
@@ -536,6 +593,57 @@ var init_error_shape = __esm({
536
593
  }
537
594
  });
538
595
 
596
+ // src/internal/workflow/executor-helpers.ts
597
+ function assembleRun(params) {
598
+ return {
599
+ id: params.runId,
600
+ name: params.name,
601
+ status: params.status,
602
+ startedAt: params.startedAt,
603
+ endedAt: Date.now(),
604
+ stepResults: params.stepResults,
605
+ ...params.output !== void 0 ? { output: params.output } : {},
606
+ ...params.error !== void 0 ? { error: params.error } : {}
607
+ };
608
+ }
609
+ function abortRun(name, runId, startedAt, stepResults, signal) {
610
+ return assembleRun({
611
+ runId,
612
+ name,
613
+ status: "cancelled",
614
+ stepResults,
615
+ startedAt,
616
+ error: { name: "AbortError", message: String(signal.reason ?? "Aborted") }
617
+ });
618
+ }
619
+ function validateWorkflowSchema(schema, value) {
620
+ if (schema === void 0) return void 0;
621
+ let parsed;
622
+ try {
623
+ parsed = schema.safeParse(value);
624
+ } catch {
625
+ return "schema uses async refinements, which whole-workflow validation does not support (use a synchronous Zod schema)";
626
+ }
627
+ if (parsed.success) return void 0;
628
+ return parsed.error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`).join("; ");
629
+ }
630
+ function makeStateController(options, initial) {
631
+ let current = initial;
632
+ return {
633
+ getState: () => current,
634
+ setState: (next) => {
635
+ const issues = validateWorkflowSchema(options.stateSchema, next);
636
+ if (issues !== void 0) throw new exports.WorkflowStateError(options.name, issues);
637
+ current = next;
638
+ }
639
+ };
640
+ }
641
+ var init_executor_helpers = __esm({
642
+ "src/internal/workflow/executor-helpers.ts"() {
643
+ init_workflow();
644
+ }
645
+ });
646
+
539
647
  // src/internal/workflow/run-id.ts
540
648
  function mintRunId() {
541
649
  return `wfr-${globalThis.crypto.randomUUID().replace(/-/g, "").slice(0, 8)}`;
@@ -1273,6 +1381,8 @@ async function handleSuspend(err, ctx) {
1273
1381
  suspendedPayload: err.payload,
1274
1382
  stepResults: ctx.stepResults,
1275
1383
  accumulatedInput: ctx.acc,
1384
+ state: ctx.stepCtx.state,
1385
+ // SE29 — capture the shared state at suspend
1276
1386
  options: ctx.options
1277
1387
  });
1278
1388
  } catch (snapErr) {
@@ -1324,7 +1434,7 @@ async function runOneStep(args) {
1324
1434
  result = await dispatchStep(args.step, args.acc, args.ctx, args.options, args.stepResults);
1325
1435
  } catch (err) {
1326
1436
  if (err instanceof WorkflowSuspendedSentinel) {
1327
- const outcome = await handleSuspend(err, { ...args, stepSpan });
1437
+ const outcome = await handleSuspend(err, { ...args, stepSpan, stepCtx: args.ctx });
1328
1438
  return { kind: "terminal", run: outcome.run };
1329
1439
  }
1330
1440
  result = {
@@ -1341,22 +1451,42 @@ async function runOneStep(args) {
1341
1451
  stepSpan.end();
1342
1452
  return { kind: "ok", result };
1343
1453
  }
1344
- function abortRun(name, runId, startedAt, stepResults, signal) {
1345
- return assembleRun({
1346
- runId,
1347
- name,
1348
- status: "cancelled",
1349
- stepResults,
1350
- startedAt,
1351
- error: { name: "AbortError", message: String(signal.reason ?? "Aborted") }
1352
- });
1454
+ function handleStepOutcome(outcome, step, loop, onStepEvent) {
1455
+ if (outcome.kind === "terminal") {
1456
+ if (outcome.run.status === "suspended") {
1457
+ onStepEvent?.({ type: "workflow_suspended", stepId: step.id });
1458
+ }
1459
+ return { terminal: outcome.run };
1460
+ }
1461
+ loop.stepResults.push(outcome.result);
1462
+ if (outcome.result.status === "failed") {
1463
+ onStepEvent?.({
1464
+ type: "step_failed",
1465
+ stepId: step.id,
1466
+ error: outcome.result.error ?? { name: "WorkflowStepError", message: "step failed" }
1467
+ });
1468
+ return {
1469
+ terminal: assembleRun({
1470
+ runId: loop.runId,
1471
+ name: loop.name,
1472
+ status: "failed",
1473
+ stepResults: loop.stepResults,
1474
+ startedAt: loop.startedAt,
1475
+ error: outcome.result.error
1476
+ })
1477
+ };
1478
+ }
1479
+ onStepEvent?.({ type: "step_completed", stepId: step.id, output: outcome.result.output });
1480
+ return { acc: outcome.result.output };
1353
1481
  }
1354
1482
  async function runStepsLoop(params) {
1355
- const { options, steps, ctx, runId, startedAt, signal } = params;
1483
+ const { options, steps, ctx, runId, startedAt, signal, onStepEvent } = params;
1356
1484
  const stepResults = [...params.initialStepResults ?? []];
1485
+ const loop = { stepResults, runId, name: options.name, startedAt };
1357
1486
  let acc = params.input;
1358
1487
  for (const step of steps) {
1359
1488
  if (signal.aborted) return abortRun(options.name, runId, startedAt, stepResults, signal);
1489
+ onStepEvent?.({ type: "step_started", stepId: step.id });
1360
1490
  const outcome = await runOneStep({
1361
1491
  step,
1362
1492
  acc,
@@ -1367,20 +1497,22 @@ async function runStepsLoop(params) {
1367
1497
  name: options.name,
1368
1498
  startedAt
1369
1499
  });
1370
- if (outcome.kind === "terminal") return outcome.run;
1371
- stepResults.push(outcome.result);
1372
- if (outcome.result.status === "failed") {
1373
- return assembleRun({
1374
- runId,
1375
- name: options.name,
1376
- status: "failed",
1377
- stepResults,
1378
- startedAt,
1379
- error: outcome.result.error
1380
- });
1381
- }
1382
- acc = outcome.result.output;
1500
+ const handled = handleStepOutcome(outcome, step, loop, onStepEvent);
1501
+ if ("terminal" in handled) return handled.terminal;
1502
+ acc = handled.acc;
1383
1503
  }
1504
+ const outputIssues = validateWorkflowSchema(options.outputSchema, acc);
1505
+ if (outputIssues !== void 0) {
1506
+ return assembleRun({
1507
+ runId,
1508
+ name: options.name,
1509
+ status: "failed",
1510
+ stepResults,
1511
+ startedAt,
1512
+ error: errToShape(new exports.WorkflowOutputError(options.name, outputIssues))
1513
+ });
1514
+ }
1515
+ onStepEvent?.({ type: "workflow_completed" });
1384
1516
  return assembleRun({
1385
1517
  runId,
1386
1518
  name: options.name,
@@ -1403,8 +1535,33 @@ async function executeWorkflow(options, steps, input, runOpts) {
1403
1535
  runSpan.end();
1404
1536
  return abortRun(options.name, runId, startedAt, [], signal);
1405
1537
  }
1406
- const ctx = makeStepContext(runId, signal);
1538
+ const internal = runOpts;
1539
+ const seededState = internal?.restoredState !== void 0 ? internal.restoredState : options.initialState;
1540
+ const stateController = makeStateController(options, seededState);
1541
+ const ctx = makeStepContext(runId, signal, stateController);
1407
1542
  try {
1543
+ const inputIssues = validateWorkflowSchema(options.inputSchema, input);
1544
+ if (inputIssues !== void 0) {
1545
+ return assembleRun({
1546
+ runId,
1547
+ name: options.name,
1548
+ status: "failed",
1549
+ stepResults: [],
1550
+ startedAt,
1551
+ error: errToShape(new exports.WorkflowInputError(options.name, inputIssues))
1552
+ });
1553
+ }
1554
+ const stateIssues = seededState !== void 0 ? validateWorkflowSchema(options.stateSchema, seededState) : void 0;
1555
+ if (stateIssues !== void 0) {
1556
+ return assembleRun({
1557
+ runId,
1558
+ name: options.name,
1559
+ status: "failed",
1560
+ stepResults: [],
1561
+ startedAt,
1562
+ error: errToShape(new exports.WorkflowStateError(options.name, stateIssues))
1563
+ });
1564
+ }
1408
1565
  return await runStepsLoop({
1409
1566
  options,
1410
1567
  steps,
@@ -1413,7 +1570,8 @@ async function executeWorkflow(options, steps, input, runOpts) {
1413
1570
  runId,
1414
1571
  startedAt,
1415
1572
  signal,
1416
- ...runOpts?.initialStepResults !== void 0 ? { initialStepResults: runOpts.initialStepResults } : {}
1573
+ ...internal?.initialStepResults !== void 0 ? { initialStepResults: internal.initialStepResults } : {},
1574
+ ...internal?.onStepEvent !== void 0 ? { onStepEvent: internal.onStepEvent } : {}
1417
1575
  });
1418
1576
  } finally {
1419
1577
  runSpan.end();
@@ -1444,28 +1602,17 @@ async function dispatchStep(step, input, ctx, options, prevStepResults) {
1444
1602
  }
1445
1603
  }
1446
1604
  }
1447
- function assembleRun(params) {
1448
- const endedAt = Date.now();
1449
- return {
1450
- id: params.runId,
1451
- name: params.name,
1452
- status: params.status,
1453
- startedAt: params.startedAt,
1454
- endedAt,
1455
- stepResults: params.stepResults,
1456
- ...params.output !== void 0 ? { output: params.output } : {},
1457
- ...params.error !== void 0 ? { error: params.error } : {}
1458
- };
1459
- }
1460
1605
  async function saveSnapshot(p) {
1461
1606
  const snapshot = {
1462
- _schemaVersion: 1,
1607
+ _schemaVersion: 2,
1608
+ // SE29 — carries `state`
1463
1609
  runId: p.runId,
1464
1610
  workflowName: p.workflowName,
1465
1611
  currentStepId: p.currentStepId,
1466
1612
  suspendedPayload: p.suspendedPayload,
1467
1613
  stepResults: p.stepResults,
1468
1614
  accumulatedInput: p.accumulatedInput,
1615
+ ...p.state !== void 0 ? { state: p.state } : {},
1469
1616
  suspendedAt: Date.now()
1470
1617
  };
1471
1618
  const store = getSnapshotStoreFor(p.options);
@@ -1498,7 +1645,10 @@ async function resumeWorkflow(opts) {
1498
1645
  signal: opts.signal,
1499
1646
  runId: opts.runId,
1500
1647
  // M3 #62 — restore prior step outputs so the resumed run is not lossy (internal seam).
1501
- initialStepResults: snapshot.stepResults
1648
+ initialStepResults: snapshot.stepResults,
1649
+ // SE29 — restore shared state (v2 snapshot). A v1 snapshot has no `state` →
1650
+ // executeWorkflow falls back to `options.initialState`.
1651
+ ...snapshot.state !== void 0 ? { restoredState: snapshot.state } : {}
1502
1652
  });
1503
1653
  }
1504
1654
  var init_executor = __esm({
@@ -1506,6 +1656,7 @@ var init_executor = __esm({
1506
1656
  init_workflow();
1507
1657
  init_ctx();
1508
1658
  init_error_shape();
1659
+ init_executor_helpers();
1509
1660
  init_run_id();
1510
1661
  init_single_flight();
1511
1662
  init_snapshot_store();
@@ -2244,6 +2395,43 @@ function sanitizeIdentifier(input, options) {
2244
2395
  }
2245
2396
  return input.toLowerCase();
2246
2397
  }
2398
+
2399
+ // src/internal/workflow/event-stream.ts
2400
+ function createEventStream() {
2401
+ const buffer = [];
2402
+ const waiters = [];
2403
+ let ended = false;
2404
+ const stream = {
2405
+ push(event) {
2406
+ if (ended) return;
2407
+ const waiter = waiters.shift();
2408
+ if (waiter !== void 0) waiter({ value: event, done: false });
2409
+ else buffer.push(event);
2410
+ },
2411
+ end() {
2412
+ if (ended) return;
2413
+ ended = true;
2414
+ for (const waiter of waiters.splice(0)) waiter({ value: void 0, done: true });
2415
+ },
2416
+ next() {
2417
+ const buffered = buffer.shift();
2418
+ if (buffered !== void 0) return Promise.resolve({ value: buffered, done: false });
2419
+ if (ended) return Promise.resolve({ value: void 0, done: true });
2420
+ return new Promise((resolve2) => waiters.push(resolve2));
2421
+ },
2422
+ // `for await` calls return() on break/throw — close early so events stop
2423
+ // buffering in memory for a consumer that stopped iterating.
2424
+ return() {
2425
+ stream.end();
2426
+ buffer.length = 0;
2427
+ return Promise.resolve({ value: void 0, done: true });
2428
+ },
2429
+ [Symbol.asyncIterator]() {
2430
+ return stream;
2431
+ }
2432
+ };
2433
+ return stream;
2434
+ }
2247
2435
  function toJsonSchema(schema, options = { unrepresentable: "any" }) {
2248
2436
  return zod.toJSONSchema(schema, options);
2249
2437
  }
@@ -2262,7 +2450,12 @@ var RetryPolicySchema = zod.z.object({
2262
2450
  });
2263
2451
  var WorkflowOptionsSchema = zod.z.object({
2264
2452
  name: zod.z.string().min(1).max(128),
2265
- persistence: PersistenceSchema
2453
+ persistence: PersistenceSchema,
2454
+ // SE27 — declared so a future `new WorkflowBuilder(parsed)` refactor cannot
2455
+ // silently drop them (create() passes the ORIGINAL options today, but the
2456
+ // schema is also the documentation of the shape).
2457
+ inputSchema: zod.z.custom().optional(),
2458
+ outputSchema: zod.z.custom().optional()
2266
2459
  });
2267
2460
  var WorkflowBuilder = class {
2268
2461
  /** @internal */
@@ -2439,6 +2632,36 @@ var Workflow = class {
2439
2632
  }
2440
2633
  return result;
2441
2634
  }
2635
+ /**
2636
+ * SE28 — run the workflow and STREAM step-level events as they happen. Returns
2637
+ * an async iterator of {@link WorkflowEvent}s (`step_started` / `step_completed`
2638
+ * / `step_failed` / `workflow_suspended` / `workflow_completed`, top-level
2639
+ * steps) plus a `result` promise resolving to the same terminal
2640
+ * {@link WorkflowRun} `run()` returns. Iterate for progress; await `result` for
2641
+ * the outcome. The stream ends when the run terminates.
2642
+ *
2643
+ * `result` is the AUTHORITATIVE terminal status. Not every terminal state has a
2644
+ * closing event: a step failure emits `step_failed`, but an `outputSchema`
2645
+ * rejection (SE27) or an abort ends the stream WITHOUT `workflow_completed` —
2646
+ * always `await result` to read the final `status`. Consuming order is free:
2647
+ * awaiting `result` without draining, or draining without awaiting `result`,
2648
+ * both work (breaking out of `for await` stops the buffering early).
2649
+ */
2650
+ stream(input, opts) {
2651
+ const queue = createEventStream();
2652
+ const result = (async () => {
2653
+ const { executeWorkflow: executeWorkflow2 } = await Promise.resolve().then(() => (init_executor(), executor_exports));
2654
+ try {
2655
+ return await executeWorkflow2(this._options, this._steps, input, {
2656
+ ...opts,
2657
+ onStepEvent: (event) => queue.push(event)
2658
+ });
2659
+ } finally {
2660
+ queue.end();
2661
+ }
2662
+ })();
2663
+ return Object.assign(queue, { result });
2664
+ }
2442
2665
  /**
2443
2666
  * Resume a suspended workflow from its snapshot. Throws
2444
2667
  * `WorkflowSnapshotNotFoundError` if `runId` is unknown.
@@ -2473,6 +2696,25 @@ function agentStep(id, agent, promptTemplate, opts) {
2473
2696
  ...opts?.origin !== void 0 ? { origin: opts.origin } : {}
2474
2697
  };
2475
2698
  }
2699
+ function workflowStep(child, opts) {
2700
+ const id = opts?.id ?? `workflow_${child.__options.name}`;
2701
+ validateStepId(id);
2702
+ return {
2703
+ kind: "fn",
2704
+ id,
2705
+ fn: async (input, ctx) => {
2706
+ const run = await child.run(input, { signal: ctx.signal });
2707
+ if (run.status === "completed") return run.output;
2708
+ throw new exports.WorkflowNestedError(id, child.__options.name, run.status, run.error);
2709
+ }
2710
+ };
2711
+ }
2712
+ function cloneWorkflow(wf, opts) {
2713
+ return new Workflow(
2714
+ { ...wf.__options, name: opts.id, workflowId: `wf-${mintShortId()}` },
2715
+ [...wf.__steps]
2716
+ );
2717
+ }
2476
2718
  var WorkflowToolError = class extends Error {
2477
2719
  constructor(toolName, workflowStatus, workflowError) {
2478
2720
  super(
@@ -2535,7 +2777,9 @@ exports.WorkflowBuilder = WorkflowBuilder;
2535
2777
  exports.WorkflowToolError = WorkflowToolError;
2536
2778
  exports.__resetSnapshotStoresForTests = __resetSnapshotStoresForTests;
2537
2779
  exports.agentStep = agentStep;
2780
+ exports.cloneWorkflow = cloneWorkflow;
2538
2781
  exports.fn = fn;
2539
2782
  exports.workflowAsTool = workflowAsTool;
2783
+ exports.workflowStep = workflowStep;
2540
2784
  //# sourceMappingURL=workflow.cjs.map
2541
2785
  //# sourceMappingURL=workflow.cjs.map