@theokit/sdk 2.26.0 → 2.28.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 +38 -0
- package/dist/filesystem/index.cjs +304 -0
- package/dist/filesystem/index.cjs.map +1 -0
- package/dist/filesystem/index.d.cts +12 -0
- package/dist/filesystem/index.d.ts +12 -0
- package/dist/filesystem/index.js +295 -0
- package/dist/filesystem/index.js.map +1 -0
- package/dist/filesystem/local-filesystem.d.cts +36 -0
- package/dist/filesystem/local-filesystem.d.ts +36 -0
- package/dist/filesystem/types.d.cts +118 -0
- package/dist/filesystem/types.d.ts +118 -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 +13 -3
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filesystem backend protocol — a pluggable file *storage* provider for agent
|
|
3
|
+
* tools, the storage-side twin of {@link SandboxBackend} (execution-side).
|
|
4
|
+
*
|
|
5
|
+
* SE31 (Mastra Workspaces comparison). Mirrors `SandboxBackend`'s shape: a small
|
|
6
|
+
* set of abstract methods (`readFile` / `writeFile` / `stat` / `list`) with
|
|
7
|
+
* `exists()` derived on the base class, a boundary `basePath`, a `readOnly`
|
|
8
|
+
* flag, and typed errors. Unlike `SandboxBackend` (whose file ops shell out via
|
|
9
|
+
* `execute`, requiring command execution and giving NO structured `stat`), a
|
|
10
|
+
* `FilesystemBackend` serves a *filesystem-only* workspace with no sandbox and
|
|
11
|
+
* exposes a real `stat().mtimeMs` — the primitive SE32's read-before-write
|
|
12
|
+
* safety compares against. See ADR 0011.
|
|
13
|
+
*
|
|
14
|
+
* New backends (S3, GCS, in-memory) implement the four abstract methods; higher
|
|
15
|
+
* level helpers derive on the base. This is the backend *seam* — it does NOT
|
|
16
|
+
* ship agent-facing tools nor a bundled workspace (bring-your-own-tools stands).
|
|
17
|
+
*
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
/** Structured file metadata. `mtimeMs` is the read-before-write oracle (SE32). */
|
|
21
|
+
export interface FileStat {
|
|
22
|
+
readonly size: number;
|
|
23
|
+
readonly mtimeMs: number;
|
|
24
|
+
readonly isFile: boolean;
|
|
25
|
+
readonly isDirectory: boolean;
|
|
26
|
+
}
|
|
27
|
+
/** Options a write may carry. SE32 adds `expectedMtime` (stale-write guard). */
|
|
28
|
+
export interface WriteFileOptions {
|
|
29
|
+
/**
|
|
30
|
+
* SE32 — when set, the write fails with {@link StaleFileError} if the file's
|
|
31
|
+
* current `mtimeMs` differs (someone changed it since it was last read).
|
|
32
|
+
*/
|
|
33
|
+
readonly expectedMtime?: number;
|
|
34
|
+
}
|
|
35
|
+
export interface FilesystemConfig {
|
|
36
|
+
/** Boundary root. Every path is resolved within it; escapes are rejected. */
|
|
37
|
+
readonly basePath?: string;
|
|
38
|
+
/** When true, every write throws {@link FilesystemReadOnlyError}. */
|
|
39
|
+
readonly readOnly?: boolean;
|
|
40
|
+
}
|
|
41
|
+
/** A path escaped the backend's `basePath` (traversal or symlink). */
|
|
42
|
+
export declare class FilesystemSecurityError extends Error {
|
|
43
|
+
readonly code: "filesystem_security";
|
|
44
|
+
constructor(message: string);
|
|
45
|
+
}
|
|
46
|
+
/** A write was attempted on a read-only backend. */
|
|
47
|
+
export declare class FilesystemReadOnlyError extends Error {
|
|
48
|
+
readonly code: "filesystem_readonly";
|
|
49
|
+
constructor(message: string);
|
|
50
|
+
}
|
|
51
|
+
/** A read/stat targeted a path that does not exist. */
|
|
52
|
+
export declare class FileNotFoundError extends Error {
|
|
53
|
+
readonly path: string;
|
|
54
|
+
readonly code: "filesystem_not_found";
|
|
55
|
+
constructor(path: string);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* A filesystem I/O operation failed for a reason other than not-found /
|
|
59
|
+
* read-only / stale / security (e.g. `ENOTDIR` — a path component is a file, or
|
|
60
|
+
* `EACCES` / `ENOSPC`). Carries the original error as `cause` so no raw,
|
|
61
|
+
* untyped Node `SystemError` ever escapes the backend (Unbreakable Rule 8).
|
|
62
|
+
*/
|
|
63
|
+
export declare class FilesystemError extends Error {
|
|
64
|
+
readonly path: string;
|
|
65
|
+
readonly code: "filesystem_io";
|
|
66
|
+
constructor(path: string, cause: unknown);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* SE32 — a write's `expectedMtime` did not match the file's current mtime: the
|
|
70
|
+
* file changed since it was last read, so the write would silently clobber.
|
|
71
|
+
*/
|
|
72
|
+
export declare class StaleFileError extends Error {
|
|
73
|
+
readonly path: string;
|
|
74
|
+
readonly expectedMtime: number;
|
|
75
|
+
readonly actualMtime: number;
|
|
76
|
+
readonly code: "filesystem_stale";
|
|
77
|
+
constructor(path: string, expectedMtime: number, actualMtime: number);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Pluggable filesystem backend. Implement the four abstract methods; `exists()`
|
|
81
|
+
* and the `readOnly`/`basePath` accessors derive on the base class.
|
|
82
|
+
*
|
|
83
|
+
* @public
|
|
84
|
+
*/
|
|
85
|
+
export declare abstract class FilesystemBackend {
|
|
86
|
+
protected readonly _basePath: string;
|
|
87
|
+
protected readonly _readOnly: boolean;
|
|
88
|
+
constructor(config?: FilesystemConfig);
|
|
89
|
+
/** Read a boundary-relative file as UTF-8. Throws {@link FileNotFoundError}. */
|
|
90
|
+
abstract readFile(path: string): Promise<string>;
|
|
91
|
+
/**
|
|
92
|
+
* Write UTF-8 content to a boundary-relative path (creating parents). Returns
|
|
93
|
+
* the new {@link FileStat}. Throws {@link FilesystemReadOnlyError} on a
|
|
94
|
+
* read-only backend and {@link StaleFileError} when `opts.expectedMtime`
|
|
95
|
+
* mismatches (SE32).
|
|
96
|
+
*/
|
|
97
|
+
abstract writeFile(path: string, content: string, opts?: WriteFileOptions): Promise<FileStat>;
|
|
98
|
+
/** Structured metadata for a path. Throws {@link FileNotFoundError}. */
|
|
99
|
+
abstract stat(path: string): Promise<FileStat>;
|
|
100
|
+
/** Directory entry names (not recursive). Throws {@link FileNotFoundError}. */
|
|
101
|
+
abstract list(path: string): Promise<string[]>;
|
|
102
|
+
/** Derived — true iff `stat(path)` resolves (absent ⇒ false). */
|
|
103
|
+
exists(path: string): Promise<boolean>;
|
|
104
|
+
/** Throw {@link FilesystemReadOnlyError} when the backend is read-only. */
|
|
105
|
+
protected assertWritable(): void;
|
|
106
|
+
get readOnly(): boolean;
|
|
107
|
+
get basePath(): string;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* A backend OR a per-request resolver of one. A resolver runs at tool-execution
|
|
111
|
+
* time (the request scope), so multi-tenant / multi-role agents get a distinct
|
|
112
|
+
* root or permission set per request without a shared mutable backend.
|
|
113
|
+
*
|
|
114
|
+
* @public
|
|
115
|
+
*/
|
|
116
|
+
export type FilesystemProvider<Ctx = unknown> = FilesystemBackend | ((ctx: Ctx) => FilesystemBackend | Promise<FilesystemBackend>);
|
|
117
|
+
/** Resolve a {@link FilesystemProvider} to a concrete backend for `ctx`. */
|
|
118
|
+
export declare function resolveFilesystem<Ctx>(provider: FilesystemProvider<Ctx>, ctx: Ctx): Promise<FilesystemBackend>;
|
package/dist/index.cjs
CHANGED
|
@@ -4336,7 +4336,7 @@ var init_batch = __esm({
|
|
|
4336
4336
|
});
|
|
4337
4337
|
|
|
4338
4338
|
// src/types/workflow.ts
|
|
4339
|
-
var WorkflowDuplicateStepIdError, WorkflowAlreadyRunningError, WorkflowSnapshotNotFoundError, WorkflowMaxIterationsExceededError, WorkflowNotSerializableError, WorkflowResumeStepNotFoundError, WorkflowParallelError, WorkflowCompensateNotImplementedError;
|
|
4339
|
+
var WorkflowDuplicateStepIdError, WorkflowInputError, WorkflowOutputError, WorkflowStateError, WorkflowAlreadyRunningError, WorkflowSnapshotNotFoundError, WorkflowMaxIterationsExceededError, WorkflowNotSerializableError, WorkflowResumeStepNotFoundError, WorkflowParallelError, WorkflowCompensateNotImplementedError;
|
|
4340
4340
|
var init_workflow = __esm({
|
|
4341
4341
|
"src/types/workflow.ts"() {
|
|
4342
4342
|
WorkflowDuplicateStepIdError = class extends Error {
|
|
@@ -4347,6 +4347,36 @@ var init_workflow = __esm({
|
|
|
4347
4347
|
stepId;
|
|
4348
4348
|
name = "WorkflowDuplicateStepIdError";
|
|
4349
4349
|
};
|
|
4350
|
+
WorkflowInputError = class extends Error {
|
|
4351
|
+
constructor(workflowName, detail) {
|
|
4352
|
+
super(`Workflow "${workflowName}" input failed schema validation: ${detail}`);
|
|
4353
|
+
this.workflowName = workflowName;
|
|
4354
|
+
this.detail = detail;
|
|
4355
|
+
}
|
|
4356
|
+
workflowName;
|
|
4357
|
+
detail;
|
|
4358
|
+
name = "WorkflowInputError";
|
|
4359
|
+
};
|
|
4360
|
+
WorkflowOutputError = class extends Error {
|
|
4361
|
+
constructor(workflowName, detail) {
|
|
4362
|
+
super(`Workflow "${workflowName}" output failed schema validation: ${detail}`);
|
|
4363
|
+
this.workflowName = workflowName;
|
|
4364
|
+
this.detail = detail;
|
|
4365
|
+
}
|
|
4366
|
+
workflowName;
|
|
4367
|
+
detail;
|
|
4368
|
+
name = "WorkflowOutputError";
|
|
4369
|
+
};
|
|
4370
|
+
WorkflowStateError = class extends Error {
|
|
4371
|
+
constructor(workflowName, detail) {
|
|
4372
|
+
super(`Workflow "${workflowName}" state failed schema validation: ${detail}`);
|
|
4373
|
+
this.workflowName = workflowName;
|
|
4374
|
+
this.detail = detail;
|
|
4375
|
+
}
|
|
4376
|
+
workflowName;
|
|
4377
|
+
detail;
|
|
4378
|
+
name = "WorkflowStateError";
|
|
4379
|
+
};
|
|
4350
4380
|
WorkflowAlreadyRunningError = class extends Error {
|
|
4351
4381
|
constructor(workflowName, runId) {
|
|
4352
4382
|
super(`Workflow "${workflowName}" run "${runId}" already in-flight.`);
|
|
@@ -4539,7 +4569,7 @@ var init_snapshot_store = __esm({
|
|
|
4539
4569
|
});
|
|
4540
4570
|
|
|
4541
4571
|
// src/internal/workflow/ctx.ts
|
|
4542
|
-
function makeStepContext(runId, signal) {
|
|
4572
|
+
function makeStepContext(runId, signal, state4) {
|
|
4543
4573
|
return {
|
|
4544
4574
|
runId,
|
|
4545
4575
|
signal,
|
|
@@ -4550,7 +4580,13 @@ function makeStepContext(runId, signal) {
|
|
|
4550
4580
|
},
|
|
4551
4581
|
suspend: async (payload) => {
|
|
4552
4582
|
throw new WorkflowSuspendedSentinel(payload);
|
|
4553
|
-
}
|
|
4583
|
+
},
|
|
4584
|
+
// SE29 — read reflects the current shared state; write goes through the
|
|
4585
|
+
// controller (which validates against `stateSchema`).
|
|
4586
|
+
get state() {
|
|
4587
|
+
return state4.getState();
|
|
4588
|
+
},
|
|
4589
|
+
setState: (next) => state4.setState(next)
|
|
4554
4590
|
};
|
|
4555
4591
|
}
|
|
4556
4592
|
function emit(level, runId, msg, attrs) {
|
|
@@ -4603,6 +4639,57 @@ var init_error_shape = __esm({
|
|
|
4603
4639
|
}
|
|
4604
4640
|
});
|
|
4605
4641
|
|
|
4642
|
+
// src/internal/workflow/executor-helpers.ts
|
|
4643
|
+
function assembleRun(params) {
|
|
4644
|
+
return {
|
|
4645
|
+
id: params.runId,
|
|
4646
|
+
name: params.name,
|
|
4647
|
+
status: params.status,
|
|
4648
|
+
startedAt: params.startedAt,
|
|
4649
|
+
endedAt: Date.now(),
|
|
4650
|
+
stepResults: params.stepResults,
|
|
4651
|
+
...params.output !== void 0 ? { output: params.output } : {},
|
|
4652
|
+
...params.error !== void 0 ? { error: params.error } : {}
|
|
4653
|
+
};
|
|
4654
|
+
}
|
|
4655
|
+
function abortRun(name, runId, startedAt, stepResults, signal) {
|
|
4656
|
+
return assembleRun({
|
|
4657
|
+
runId,
|
|
4658
|
+
name,
|
|
4659
|
+
status: "cancelled",
|
|
4660
|
+
stepResults,
|
|
4661
|
+
startedAt,
|
|
4662
|
+
error: { name: "AbortError", message: String(signal.reason ?? "Aborted") }
|
|
4663
|
+
});
|
|
4664
|
+
}
|
|
4665
|
+
function validateWorkflowSchema(schema, value) {
|
|
4666
|
+
if (schema === void 0) return void 0;
|
|
4667
|
+
let parsed;
|
|
4668
|
+
try {
|
|
4669
|
+
parsed = schema.safeParse(value);
|
|
4670
|
+
} catch {
|
|
4671
|
+
return "schema uses async refinements, which whole-workflow validation does not support (use a synchronous Zod schema)";
|
|
4672
|
+
}
|
|
4673
|
+
if (parsed.success) return void 0;
|
|
4674
|
+
return parsed.error.issues.map((issue) => `${issue.path.join(".") || "(root)"}: ${issue.message}`).join("; ");
|
|
4675
|
+
}
|
|
4676
|
+
function makeStateController(options, initial) {
|
|
4677
|
+
let current = initial;
|
|
4678
|
+
return {
|
|
4679
|
+
getState: () => current,
|
|
4680
|
+
setState: (next) => {
|
|
4681
|
+
const issues = validateWorkflowSchema(options.stateSchema, next);
|
|
4682
|
+
if (issues !== void 0) throw new WorkflowStateError(options.name, issues);
|
|
4683
|
+
current = next;
|
|
4684
|
+
}
|
|
4685
|
+
};
|
|
4686
|
+
}
|
|
4687
|
+
var init_executor_helpers = __esm({
|
|
4688
|
+
"src/internal/workflow/executor-helpers.ts"() {
|
|
4689
|
+
init_workflow();
|
|
4690
|
+
}
|
|
4691
|
+
});
|
|
4692
|
+
|
|
4606
4693
|
// src/internal/workflow/run-id.ts
|
|
4607
4694
|
function mintRunId() {
|
|
4608
4695
|
return `wfr-${globalThis.crypto.randomUUID().replace(/-/g, "").slice(0, 8)}`;
|
|
@@ -5269,6 +5356,8 @@ async function handleSuspend(err, ctx) {
|
|
|
5269
5356
|
suspendedPayload: err.payload,
|
|
5270
5357
|
stepResults: ctx.stepResults,
|
|
5271
5358
|
accumulatedInput: ctx.acc,
|
|
5359
|
+
state: ctx.stepCtx.state,
|
|
5360
|
+
// SE29 — capture the shared state at suspend
|
|
5272
5361
|
options: ctx.options
|
|
5273
5362
|
});
|
|
5274
5363
|
} catch (snapErr) {
|
|
@@ -5320,7 +5409,7 @@ async function runOneStep(args) {
|
|
|
5320
5409
|
result = await dispatchStep(args.step, args.acc, args.ctx, args.options, args.stepResults);
|
|
5321
5410
|
} catch (err) {
|
|
5322
5411
|
if (err instanceof WorkflowSuspendedSentinel) {
|
|
5323
|
-
const outcome = await handleSuspend(err, { ...args, stepSpan });
|
|
5412
|
+
const outcome = await handleSuspend(err, { ...args, stepSpan, stepCtx: args.ctx });
|
|
5324
5413
|
return { kind: "terminal", run: outcome.run };
|
|
5325
5414
|
}
|
|
5326
5415
|
result = {
|
|
@@ -5337,22 +5426,42 @@ async function runOneStep(args) {
|
|
|
5337
5426
|
stepSpan.end();
|
|
5338
5427
|
return { kind: "ok", result };
|
|
5339
5428
|
}
|
|
5340
|
-
function
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5429
|
+
function handleStepOutcome(outcome, step, loop, onStepEvent) {
|
|
5430
|
+
if (outcome.kind === "terminal") {
|
|
5431
|
+
if (outcome.run.status === "suspended") {
|
|
5432
|
+
onStepEvent?.({ type: "workflow_suspended", stepId: step.id });
|
|
5433
|
+
}
|
|
5434
|
+
return { terminal: outcome.run };
|
|
5435
|
+
}
|
|
5436
|
+
loop.stepResults.push(outcome.result);
|
|
5437
|
+
if (outcome.result.status === "failed") {
|
|
5438
|
+
onStepEvent?.({
|
|
5439
|
+
type: "step_failed",
|
|
5440
|
+
stepId: step.id,
|
|
5441
|
+
error: outcome.result.error ?? { name: "WorkflowStepError", message: "step failed" }
|
|
5442
|
+
});
|
|
5443
|
+
return {
|
|
5444
|
+
terminal: assembleRun({
|
|
5445
|
+
runId: loop.runId,
|
|
5446
|
+
name: loop.name,
|
|
5447
|
+
status: "failed",
|
|
5448
|
+
stepResults: loop.stepResults,
|
|
5449
|
+
startedAt: loop.startedAt,
|
|
5450
|
+
error: outcome.result.error
|
|
5451
|
+
})
|
|
5452
|
+
};
|
|
5453
|
+
}
|
|
5454
|
+
onStepEvent?.({ type: "step_completed", stepId: step.id, output: outcome.result.output });
|
|
5455
|
+
return { acc: outcome.result.output };
|
|
5349
5456
|
}
|
|
5350
5457
|
async function runStepsLoop(params) {
|
|
5351
|
-
const { options, steps, ctx, runId, startedAt, signal } = params;
|
|
5458
|
+
const { options, steps, ctx, runId, startedAt, signal, onStepEvent } = params;
|
|
5352
5459
|
const stepResults = [...params.initialStepResults ?? []];
|
|
5460
|
+
const loop = { stepResults, runId, name: options.name, startedAt };
|
|
5353
5461
|
let acc = params.input;
|
|
5354
5462
|
for (const step of steps) {
|
|
5355
5463
|
if (signal.aborted) return abortRun(options.name, runId, startedAt, stepResults, signal);
|
|
5464
|
+
onStepEvent?.({ type: "step_started", stepId: step.id });
|
|
5356
5465
|
const outcome = await runOneStep({
|
|
5357
5466
|
step,
|
|
5358
5467
|
acc,
|
|
@@ -5363,20 +5472,22 @@ async function runStepsLoop(params) {
|
|
|
5363
5472
|
name: options.name,
|
|
5364
5473
|
startedAt
|
|
5365
5474
|
});
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5475
|
+
const handled = handleStepOutcome(outcome, step, loop, onStepEvent);
|
|
5476
|
+
if ("terminal" in handled) return handled.terminal;
|
|
5477
|
+
acc = handled.acc;
|
|
5478
|
+
}
|
|
5479
|
+
const outputIssues = validateWorkflowSchema(options.outputSchema, acc);
|
|
5480
|
+
if (outputIssues !== void 0) {
|
|
5481
|
+
return assembleRun({
|
|
5482
|
+
runId,
|
|
5483
|
+
name: options.name,
|
|
5484
|
+
status: "failed",
|
|
5485
|
+
stepResults,
|
|
5486
|
+
startedAt,
|
|
5487
|
+
error: errToShape(new WorkflowOutputError(options.name, outputIssues))
|
|
5488
|
+
});
|
|
5379
5489
|
}
|
|
5490
|
+
onStepEvent?.({ type: "workflow_completed" });
|
|
5380
5491
|
return assembleRun({
|
|
5381
5492
|
runId,
|
|
5382
5493
|
name: options.name,
|
|
@@ -5399,8 +5510,33 @@ async function executeWorkflow(options, steps, input, runOpts) {
|
|
|
5399
5510
|
runSpan.end();
|
|
5400
5511
|
return abortRun(options.name, runId, startedAt, [], signal);
|
|
5401
5512
|
}
|
|
5402
|
-
const
|
|
5513
|
+
const internal = runOpts;
|
|
5514
|
+
const seededState = internal?.restoredState !== void 0 ? internal.restoredState : options.initialState;
|
|
5515
|
+
const stateController = makeStateController(options, seededState);
|
|
5516
|
+
const ctx = makeStepContext(runId, signal, stateController);
|
|
5403
5517
|
try {
|
|
5518
|
+
const inputIssues = validateWorkflowSchema(options.inputSchema, input);
|
|
5519
|
+
if (inputIssues !== void 0) {
|
|
5520
|
+
return assembleRun({
|
|
5521
|
+
runId,
|
|
5522
|
+
name: options.name,
|
|
5523
|
+
status: "failed",
|
|
5524
|
+
stepResults: [],
|
|
5525
|
+
startedAt,
|
|
5526
|
+
error: errToShape(new WorkflowInputError(options.name, inputIssues))
|
|
5527
|
+
});
|
|
5528
|
+
}
|
|
5529
|
+
const stateIssues = seededState !== void 0 ? validateWorkflowSchema(options.stateSchema, seededState) : void 0;
|
|
5530
|
+
if (stateIssues !== void 0) {
|
|
5531
|
+
return assembleRun({
|
|
5532
|
+
runId,
|
|
5533
|
+
name: options.name,
|
|
5534
|
+
status: "failed",
|
|
5535
|
+
stepResults: [],
|
|
5536
|
+
startedAt,
|
|
5537
|
+
error: errToShape(new WorkflowStateError(options.name, stateIssues))
|
|
5538
|
+
});
|
|
5539
|
+
}
|
|
5404
5540
|
return await runStepsLoop({
|
|
5405
5541
|
options,
|
|
5406
5542
|
steps,
|
|
@@ -5409,7 +5545,8 @@ async function executeWorkflow(options, steps, input, runOpts) {
|
|
|
5409
5545
|
runId,
|
|
5410
5546
|
startedAt,
|
|
5411
5547
|
signal,
|
|
5412
|
-
...
|
|
5548
|
+
...internal?.initialStepResults !== void 0 ? { initialStepResults: internal.initialStepResults } : {},
|
|
5549
|
+
...internal?.onStepEvent !== void 0 ? { onStepEvent: internal.onStepEvent } : {}
|
|
5413
5550
|
});
|
|
5414
5551
|
} finally {
|
|
5415
5552
|
runSpan.end();
|
|
@@ -5440,28 +5577,17 @@ async function dispatchStep(step, input, ctx, options, prevStepResults) {
|
|
|
5440
5577
|
}
|
|
5441
5578
|
}
|
|
5442
5579
|
}
|
|
5443
|
-
function assembleRun(params) {
|
|
5444
|
-
const endedAt = Date.now();
|
|
5445
|
-
return {
|
|
5446
|
-
id: params.runId,
|
|
5447
|
-
name: params.name,
|
|
5448
|
-
status: params.status,
|
|
5449
|
-
startedAt: params.startedAt,
|
|
5450
|
-
endedAt,
|
|
5451
|
-
stepResults: params.stepResults,
|
|
5452
|
-
...params.output !== void 0 ? { output: params.output } : {},
|
|
5453
|
-
...params.error !== void 0 ? { error: params.error } : {}
|
|
5454
|
-
};
|
|
5455
|
-
}
|
|
5456
5580
|
async function saveSnapshot(p) {
|
|
5457
5581
|
const snapshot = {
|
|
5458
|
-
_schemaVersion:
|
|
5582
|
+
_schemaVersion: 2,
|
|
5583
|
+
// SE29 — carries `state`
|
|
5459
5584
|
runId: p.runId,
|
|
5460
5585
|
workflowName: p.workflowName,
|
|
5461
5586
|
currentStepId: p.currentStepId,
|
|
5462
5587
|
suspendedPayload: p.suspendedPayload,
|
|
5463
5588
|
stepResults: p.stepResults,
|
|
5464
5589
|
accumulatedInput: p.accumulatedInput,
|
|
5590
|
+
...p.state !== void 0 ? { state: p.state } : {},
|
|
5465
5591
|
suspendedAt: Date.now()
|
|
5466
5592
|
};
|
|
5467
5593
|
const store = getSnapshotStoreFor(p.options);
|
|
@@ -5494,7 +5620,10 @@ async function resumeWorkflow(opts) {
|
|
|
5494
5620
|
signal: opts.signal,
|
|
5495
5621
|
runId: opts.runId,
|
|
5496
5622
|
// M3 #62 — restore prior step outputs so the resumed run is not lossy (internal seam).
|
|
5497
|
-
initialStepResults: snapshot.stepResults
|
|
5623
|
+
initialStepResults: snapshot.stepResults,
|
|
5624
|
+
// SE29 — restore shared state (v2 snapshot). A v1 snapshot has no `state` →
|
|
5625
|
+
// executeWorkflow falls back to `options.initialState`.
|
|
5626
|
+
...snapshot.state !== void 0 ? { restoredState: snapshot.state } : {}
|
|
5498
5627
|
});
|
|
5499
5628
|
}
|
|
5500
5629
|
var init_executor = __esm({
|
|
@@ -5502,6 +5631,7 @@ var init_executor = __esm({
|
|
|
5502
5631
|
init_workflow();
|
|
5503
5632
|
init_ctx();
|
|
5504
5633
|
init_error_shape();
|
|
5634
|
+
init_executor_helpers();
|
|
5505
5635
|
init_run_id();
|
|
5506
5636
|
init_single_flight();
|
|
5507
5637
|
init_snapshot_store();
|
|
@@ -21031,6 +21161,45 @@ var PersistenceSchema = zod.z.object({
|
|
|
21031
21161
|
|
|
21032
21162
|
// src/workflow.ts
|
|
21033
21163
|
init_path_guard();
|
|
21164
|
+
|
|
21165
|
+
// src/internal/workflow/event-stream.ts
|
|
21166
|
+
function createEventStream() {
|
|
21167
|
+
const buffer = [];
|
|
21168
|
+
const waiters = [];
|
|
21169
|
+
let ended = false;
|
|
21170
|
+
const stream = {
|
|
21171
|
+
push(event) {
|
|
21172
|
+
if (ended) return;
|
|
21173
|
+
const waiter = waiters.shift();
|
|
21174
|
+
if (waiter !== void 0) waiter({ value: event, done: false });
|
|
21175
|
+
else buffer.push(event);
|
|
21176
|
+
},
|
|
21177
|
+
end() {
|
|
21178
|
+
if (ended) return;
|
|
21179
|
+
ended = true;
|
|
21180
|
+
for (const waiter of waiters.splice(0)) waiter({ value: void 0, done: true });
|
|
21181
|
+
},
|
|
21182
|
+
next() {
|
|
21183
|
+
const buffered = buffer.shift();
|
|
21184
|
+
if (buffered !== void 0) return Promise.resolve({ value: buffered, done: false });
|
|
21185
|
+
if (ended) return Promise.resolve({ value: void 0, done: true });
|
|
21186
|
+
return new Promise((resolve3) => waiters.push(resolve3));
|
|
21187
|
+
},
|
|
21188
|
+
// `for await` calls return() on break/throw — close early so events stop
|
|
21189
|
+
// buffering in memory for a consumer that stopped iterating.
|
|
21190
|
+
return() {
|
|
21191
|
+
stream.end();
|
|
21192
|
+
buffer.length = 0;
|
|
21193
|
+
return Promise.resolve({ value: void 0, done: true });
|
|
21194
|
+
},
|
|
21195
|
+
[Symbol.asyncIterator]() {
|
|
21196
|
+
return stream;
|
|
21197
|
+
}
|
|
21198
|
+
};
|
|
21199
|
+
return stream;
|
|
21200
|
+
}
|
|
21201
|
+
|
|
21202
|
+
// src/workflow.ts
|
|
21034
21203
|
init_workflow();
|
|
21035
21204
|
var RetryPolicySchema = zod.z.object({
|
|
21036
21205
|
// EC-3 absorbed: maxAttempts MUST be a finite int in [1, 20].
|
|
@@ -21042,7 +21211,12 @@ var RetryPolicySchema = zod.z.object({
|
|
|
21042
21211
|
});
|
|
21043
21212
|
var WorkflowOptionsSchema = zod.z.object({
|
|
21044
21213
|
name: zod.z.string().min(1).max(128),
|
|
21045
|
-
persistence: PersistenceSchema
|
|
21214
|
+
persistence: PersistenceSchema,
|
|
21215
|
+
// SE27 — declared so a future `new WorkflowBuilder(parsed)` refactor cannot
|
|
21216
|
+
// silently drop them (create() passes the ORIGINAL options today, but the
|
|
21217
|
+
// schema is also the documentation of the shape).
|
|
21218
|
+
inputSchema: zod.z.custom().optional(),
|
|
21219
|
+
outputSchema: zod.z.custom().optional()
|
|
21046
21220
|
});
|
|
21047
21221
|
var WorkflowBuilder = class {
|
|
21048
21222
|
/** @internal */
|
|
@@ -21219,6 +21393,36 @@ var Workflow = class {
|
|
|
21219
21393
|
}
|
|
21220
21394
|
return result;
|
|
21221
21395
|
}
|
|
21396
|
+
/**
|
|
21397
|
+
* SE28 — run the workflow and STREAM step-level events as they happen. Returns
|
|
21398
|
+
* an async iterator of {@link WorkflowEvent}s (`step_started` / `step_completed`
|
|
21399
|
+
* / `step_failed` / `workflow_suspended` / `workflow_completed`, top-level
|
|
21400
|
+
* steps) plus a `result` promise resolving to the same terminal
|
|
21401
|
+
* {@link WorkflowRun} `run()` returns. Iterate for progress; await `result` for
|
|
21402
|
+
* the outcome. The stream ends when the run terminates.
|
|
21403
|
+
*
|
|
21404
|
+
* `result` is the AUTHORITATIVE terminal status. Not every terminal state has a
|
|
21405
|
+
* closing event: a step failure emits `step_failed`, but an `outputSchema`
|
|
21406
|
+
* rejection (SE27) or an abort ends the stream WITHOUT `workflow_completed` —
|
|
21407
|
+
* always `await result` to read the final `status`. Consuming order is free:
|
|
21408
|
+
* awaiting `result` without draining, or draining without awaiting `result`,
|
|
21409
|
+
* both work (breaking out of `for await` stops the buffering early).
|
|
21410
|
+
*/
|
|
21411
|
+
stream(input, opts) {
|
|
21412
|
+
const queue = createEventStream();
|
|
21413
|
+
const result = (async () => {
|
|
21414
|
+
const { executeWorkflow: executeWorkflow2 } = await Promise.resolve().then(() => (init_executor(), executor_exports));
|
|
21415
|
+
try {
|
|
21416
|
+
return await executeWorkflow2(this._options, this._steps, input, {
|
|
21417
|
+
...opts,
|
|
21418
|
+
onStepEvent: (event) => queue.push(event)
|
|
21419
|
+
});
|
|
21420
|
+
} finally {
|
|
21421
|
+
queue.end();
|
|
21422
|
+
}
|
|
21423
|
+
})();
|
|
21424
|
+
return Object.assign(queue, { result });
|
|
21425
|
+
}
|
|
21222
21426
|
/**
|
|
21223
21427
|
* Resume a suspended workflow from its snapshot. Throws
|
|
21224
21428
|
* `WorkflowSnapshotNotFoundError` if `runId` is unknown.
|