pipeai 0.1.0 → 0.2.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/dist/index.d.ts CHANGED
@@ -1,13 +1,16 @@
1
- import { Tool, FlexibleSchema, ToolExecutionOptions, streamText, generateText, Output, LanguageModel, ModelMessage, ToolChoice, ToolSet, StopCondition, OnStepFinishEvent, OnFinishEvent, GenerateTextResult as GenerateTextResult$1, StreamTextResult as StreamTextResult$1, UIMessageStreamWriter } from 'ai';
1
+ import { Tool, ToolExecutionOptions, UIMessageStreamWriter, FlexibleSchema, streamText, generateText, Output, LanguageModel, ModelMessage, ToolChoice, ToolSet, StopCondition, OnStepFinishEvent, OnFinishEvent, GenerateTextResult as GenerateTextResult$1, StreamTextResult as StreamTextResult$1 } from 'ai';
2
2
  import { ZodType } from 'zod';
3
3
 
4
4
  declare const TOOL_PROVIDER_BRAND: unique symbol;
5
+ type ToolExecuteOptions = ToolExecutionOptions & {
6
+ writer?: UIMessageStreamWriter;
7
+ };
5
8
  type ToolProviderConfig<TContext, TInput, TOutput> = {
6
9
  description?: string;
7
10
  input: FlexibleSchema<TInput>;
8
11
  output?: FlexibleSchema<unknown>;
9
12
  providerOptions?: unknown;
10
- execute: (input: TInput, ctx: Readonly<TContext>, options?: ToolExecutionOptions) => Promise<TOutput>;
13
+ execute: (input: TInput, ctx: Readonly<TContext>, options: ToolExecuteOptions) => Promise<TOutput>;
11
14
  };
12
15
  interface IToolProvider<TContext> {
13
16
  readonly [TOOL_PROVIDER_BRAND]: true;
@@ -56,16 +59,19 @@ interface AgentConfig<TContext, TInput = void, TOutput = void> extends AIPassthr
56
59
  result: OnStepFinishEvent;
57
60
  ctx: Readonly<TContext>;
58
61
  input: TInput;
62
+ writer?: UIMessageStreamWriter;
59
63
  }) => MaybePromise<void>;
60
64
  onFinish?: (params: {
61
65
  result: OnFinishEvent;
62
66
  ctx: Readonly<TContext>;
63
67
  input: TInput;
68
+ writer?: UIMessageStreamWriter;
64
69
  }) => MaybePromise<void>;
65
70
  onError?: (params: {
66
71
  error: unknown;
67
72
  ctx: Readonly<TContext>;
68
73
  input: TInput;
74
+ writer?: UIMessageStreamWriter;
69
75
  }) => MaybePromise<void>;
70
76
  }
71
77
  declare class Agent<TContext, TInput = void, TOutput = void> {
@@ -103,6 +109,17 @@ declare class WorkflowLoopError extends Error {
103
109
  readonly maxIterations: number;
104
110
  constructor(iterations: number, maxIterations: number);
105
111
  }
112
+ interface WorkflowSnapshot {
113
+ readonly version: 1;
114
+ readonly resumeFromIndex: number;
115
+ readonly output: unknown;
116
+ readonly gateId: string;
117
+ readonly gatePayload: unknown;
118
+ }
119
+ declare class WorkflowSuspended extends Error {
120
+ readonly snapshot: WorkflowSnapshot;
121
+ constructor(snapshot: WorkflowSnapshot);
122
+ }
106
123
  interface AgentStepHooks<TContext, TOutput, TNextOutput> {
107
124
  mapGenerateResult?: (params: {
108
125
  result: GenerateTextResult;
@@ -172,6 +189,9 @@ type RepeatOptions<TContext, TOutput> = {
172
189
  maxIterations?: number;
173
190
  };
174
191
  type ElementOf<T> = T extends readonly (infer E)[] ? E : never;
192
+ interface SchemaWithParse<T = unknown> {
193
+ parse(data: unknown): T;
194
+ }
175
195
  type StepNode = {
176
196
  readonly type: "step";
177
197
  readonly id: string;
@@ -189,6 +209,16 @@ type StepNode = {
189
209
  readonly type: "finally";
190
210
  readonly id: string;
191
211
  readonly execute: (state: RuntimeState) => MaybePromise<void>;
212
+ } | {
213
+ readonly type: "gate";
214
+ readonly id: string;
215
+ readonly payload: (state: RuntimeState) => MaybePromise<unknown>;
216
+ readonly schema?: SchemaWithParse;
217
+ readonly condition?: (state: RuntimeState) => MaybePromise<boolean>;
218
+ readonly merge?: (params: {
219
+ priorOutput: unknown;
220
+ response: unknown;
221
+ }) => MaybePromise<unknown>;
192
222
  };
193
223
  interface RuntimeState {
194
224
  ctx: unknown;
@@ -196,45 +226,76 @@ interface RuntimeState {
196
226
  mode: "generate" | "stream";
197
227
  writer?: UIMessageStreamWriter;
198
228
  }
199
- declare class SealedWorkflow<TContext, TInput = void, TOutput = void> {
229
+ declare class SealedWorkflow<TContext, TInput = void, TOutput = void, TGates extends Record<string, unknown> = {}> {
200
230
  readonly id?: string;
201
231
  protected readonly steps: ReadonlyArray<StepNode>;
202
232
  protected constructor(steps: ReadonlyArray<StepNode>, id?: string);
203
233
  generate(ctx: TContext, ...args: TInput extends void ? [input?: TInput] : [input: TInput]): Promise<WorkflowResult<TOutput>>;
204
234
  stream(ctx: TContext, ...args: TInput extends void ? [input?: TInput, options?: WorkflowStreamOptions] : [input: TInput, options?: WorkflowStreamOptions]): WorkflowStreamResult<TOutput>;
205
- protected execute(state: RuntimeState): Promise<void>;
206
- protected executeNestedWorkflow(state: RuntimeState, workflow: SealedWorkflow<TContext, unknown, unknown>): Promise<void>;
235
+ protected execute(state: RuntimeState, startIndex?: number): Promise<void>;
236
+ protected executeNestedWorkflow(state: RuntimeState, workflow: SealedWorkflow<TContext, unknown, unknown, any>): Promise<void>;
207
237
  protected executeAgent<TAgentInput, TNextOutput>(state: RuntimeState, agent: Agent<TContext, any, TNextOutput>, ctx: TContext, options?: AgentStepHooks<TContext, any, TNextOutput>): Promise<void>;
238
+ loadState<K extends string & keyof TGates>(gateId: K, snapshot: WorkflowSnapshot): ResumedWorkflow<TContext, TGates[K], TOutput>;
239
+ private findGateIndex;
240
+ }
241
+ declare class ResumedWorkflow<TContext, TResponse = unknown, TOutput = void> extends SealedWorkflow<TContext, TResponse, TOutput> {
242
+ private readonly startIndex;
243
+ private readonly schema?;
244
+ private readonly mergeFn?;
245
+ private readonly priorOutput;
246
+ /** @internal */
247
+ constructor(steps: ReadonlyArray<StepNode>, startIndex: number, schema?: SchemaWithParse<TResponse>, mergeFn?: (params: {
248
+ priorOutput: unknown;
249
+ response: unknown;
250
+ }) => MaybePromise<unknown>, priorOutput?: unknown);
251
+ private validateResponse;
252
+ generate(ctx: TContext, ...args: TResponse extends void ? [response?: TResponse] : [response: TResponse]): Promise<WorkflowResult<TOutput>>;
253
+ stream(ctx: TContext, ...args: TResponse extends void ? [response?: TResponse, options?: WorkflowStreamOptions] : [response: TResponse, options?: WorkflowStreamOptions]): WorkflowStreamResult<TOutput>;
208
254
  }
209
- declare class Workflow<TContext, TInput = void, TOutput = void> extends SealedWorkflow<TContext, TInput, TOutput> {
255
+ declare class Workflow<TContext, TInput = void, TOutput = void, TGates extends Record<string, unknown> = {}> extends SealedWorkflow<TContext, TInput, TOutput, TGates> {
210
256
  private constructor();
211
257
  static create<TContext, TInput = void>(options?: {
212
258
  id?: string;
213
259
  }): Workflow<TContext, TInput, TInput>;
214
260
  static from<TContext, TInput, TOutput>(agent: Agent<TContext, TInput, TOutput>, options?: StepOptions<TContext, TInput, TOutput>): Workflow<TContext, TInput, TOutput>;
215
- step<TNextOutput>(agent: Agent<TContext, TOutput, TNextOutput>, options?: StepOptions<TContext, TOutput, TNextOutput>): Workflow<TContext, TInput, TNextOutput>;
216
- step<TNextOutput>(workflow: SealedWorkflow<TContext, TOutput, TNextOutput>): Workflow<TContext, TInput, TNextOutput>;
261
+ step<TNextOutput>(agent: Agent<TContext, TOutput, TNextOutput>, options?: StepOptions<TContext, TOutput, TNextOutput>): Workflow<TContext, TInput, TNextOutput, TGates>;
262
+ step<TNextOutput>(workflow: SealedWorkflow<TContext, TOutput, TNextOutput>): Workflow<TContext, TInput, TNextOutput, TGates>;
217
263
  step<TNextOutput>(id: string, fn: (params: {
218
264
  ctx: Readonly<TContext>;
219
265
  input: TOutput;
220
- }) => MaybePromise<TNextOutput>): Workflow<TContext, TInput, TNextOutput>;
221
- branch<TNextOutput>(cases: BranchCase<TContext, TOutput, TNextOutput>[]): Workflow<TContext, TInput, TNextOutput>;
222
- branch<TKeys extends string, TNextOutput>(config: BranchSelect<TContext, TOutput, TKeys, TNextOutput>): Workflow<TContext, TInput, TNextOutput>;
266
+ }) => MaybePromise<TNextOutput>): Workflow<TContext, TInput, TNextOutput, TGates>;
267
+ gate<TResponse = TOutput, Id extends string = string>(id: Id & (Id extends keyof TGates ? never : Id), options?: {
268
+ payload?: (params: {
269
+ ctx: Readonly<TContext>;
270
+ input: TOutput;
271
+ }) => MaybePromise<unknown>;
272
+ schema?: SchemaWithParse<TResponse>;
273
+ condition?: (params: {
274
+ ctx: Readonly<TContext>;
275
+ input: TOutput;
276
+ }) => MaybePromise<boolean>;
277
+ merge?: (params: {
278
+ priorOutput: TOutput;
279
+ response: TResponse;
280
+ }) => MaybePromise<TResponse>;
281
+ }): Workflow<TContext, TInput, TResponse, TGates & Record<Id, TResponse>>;
282
+ branch<TNextOutput>(cases: BranchCase<TContext, TOutput, TNextOutput>[]): Workflow<TContext, TInput, TNextOutput, TGates>;
283
+ branch<TKeys extends string, TNextOutput>(config: BranchSelect<TContext, TOutput, TKeys, TNextOutput>): Workflow<TContext, TInput, TNextOutput, TGates>;
223
284
  private branchPredicate;
224
285
  private branchSelect;
225
286
  foreach<TNextOutput>(target: Agent<TContext, ElementOf<TOutput>, TNextOutput> | SealedWorkflow<TContext, ElementOf<TOutput>, TNextOutput>, options?: {
226
287
  concurrency?: number;
227
- }): Workflow<TContext, TInput, TNextOutput[]>;
228
- repeat(target: Agent<TContext, TOutput, TOutput> | SealedWorkflow<TContext, TOutput, TOutput>, options: RepeatOptions<TContext, TOutput>): Workflow<TContext, TInput, TOutput>;
288
+ }): Workflow<TContext, TInput, TNextOutput[], TGates>;
289
+ repeat(target: Agent<TContext, TOutput, TOutput> | SealedWorkflow<TContext, TOutput, TOutput>, options: RepeatOptions<TContext, TOutput>): Workflow<TContext, TInput, TOutput, TGates>;
229
290
  catch(id: string, fn: (params: {
230
291
  error: unknown;
231
292
  ctx: Readonly<TContext>;
232
293
  lastOutput: TOutput;
233
294
  stepId: string;
234
- }) => MaybePromise<TOutput>): Workflow<TContext, TInput, TOutput>;
295
+ }) => MaybePromise<TOutput>): Workflow<TContext, TInput, TOutput, TGates>;
235
296
  finally(id: string, fn: (params: {
236
297
  ctx: Readonly<TContext>;
237
- }) => MaybePromise<void>): SealedWorkflow<TContext, TInput, TOutput>;
298
+ }) => MaybePromise<void>): SealedWorkflow<TContext, TInput, TOutput, TGates>;
238
299
  }
239
300
 
240
- export { Agent, type AgentConfig, type AgentStepHooks, type BranchCase, type BranchSelect, type GenerateTextResult, type IToolProvider, type MaybePromise, type RepeatOptions, type Resolvable, SealedWorkflow, type StepOptions, type StreamTextResult, type ToolProviderConfig, Workflow, WorkflowBranchError, WorkflowLoopError, type WorkflowResult, type WorkflowStreamOptions, type WorkflowStreamResult, defineTool };
301
+ export { Agent, type AgentConfig, type AgentStepHooks, type BranchCase, type BranchSelect, type GenerateTextResult, type IToolProvider, type MaybePromise, type RepeatOptions, type Resolvable, ResumedWorkflow, SealedWorkflow, type StepOptions, type StreamTextResult, type ToolExecuteOptions, type ToolProviderConfig, Workflow, WorkflowBranchError, WorkflowLoopError, type WorkflowResult, type WorkflowSnapshot, type WorkflowStreamOptions, type WorkflowStreamResult, WorkflowSuspended, defineTool };
package/dist/index.js CHANGED
@@ -7,6 +7,31 @@ import {
7
7
 
8
8
  // src/tool-provider.ts
9
9
  import { tool } from "ai";
10
+
11
+ // src/utils.ts
12
+ import { AsyncLocalStorage } from "async_hooks";
13
+ var writerStorage = new AsyncLocalStorage();
14
+ function runWithWriter(writer, fn) {
15
+ return writerStorage.run(writer, fn);
16
+ }
17
+ function getActiveWriter() {
18
+ return writerStorage.getStore();
19
+ }
20
+ function resolveValue(value, ctx, input) {
21
+ if (typeof value === "function") {
22
+ return value(ctx, input);
23
+ }
24
+ return value;
25
+ }
26
+ async function extractOutput(result, hasStructuredOutput) {
27
+ if (hasStructuredOutput) {
28
+ const output = await result.output;
29
+ if (output !== void 0) return output;
30
+ }
31
+ return await result.text;
32
+ }
33
+
34
+ // src/tool-provider.ts
10
35
  var TOOL_PROVIDER_BRAND = /* @__PURE__ */ Symbol.for("agent-workflow.ToolProvider");
11
36
  var ToolProvider = class {
12
37
  [TOOL_PROVIDER_BRAND] = true;
@@ -19,7 +44,7 @@ var ToolProvider = class {
19
44
  return tool({
20
45
  ...toolDef,
21
46
  parameters: inputSchema,
22
- execute: (input, options) => execute(input, context, options)
47
+ execute: (input, options) => execute(input, context, { ...options, writer: getActiveWriter() })
23
48
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
24
49
  });
25
50
  }
@@ -31,21 +56,6 @@ function isToolProvider(obj) {
31
56
  return typeof obj === "object" && obj !== null && TOOL_PROVIDER_BRAND in obj;
32
57
  }
33
58
 
34
- // src/utils.ts
35
- function resolveValue(value, ctx, input) {
36
- if (typeof value === "function") {
37
- return value(ctx, input);
38
- }
39
- return value;
40
- }
41
- async function extractOutput(result, hasStructuredOutput) {
42
- if (hasStructuredOutput) {
43
- const output = await result.output;
44
- if (output !== void 0) return output;
45
- }
46
- return await result.text;
47
- }
48
-
49
59
  // src/agent.ts
50
60
  var Agent = class {
51
61
  id;
@@ -110,7 +120,7 @@ var Agent = class {
110
120
  return await generateText(options);
111
121
  } catch (error) {
112
122
  if (this.config.onError) {
113
- await this.config.onError({ error, ctx, input });
123
+ await this.config.onError({ error, ctx, input, writer: getActiveWriter() });
114
124
  }
115
125
  throw error;
116
126
  }
@@ -121,7 +131,7 @@ var Agent = class {
121
131
  const options = this.buildCallOptions(resolved, ctx, input);
122
132
  return streamText({
123
133
  ...options,
124
- onError: this.config.onError ? ({ error }) => this.config.onError({ error, ctx, input }) : void 0
134
+ onError: this.config.onError ? ({ error }) => this.config.onError({ error, ctx, input, writer: getActiveWriter() }) : void 0
125
135
  });
126
136
  }
127
137
  asTool(ctx, options) {
@@ -144,6 +154,13 @@ var Agent = class {
144
154
  description: this.description,
145
155
  parameters: this.config.input,
146
156
  execute: async (toolInput) => {
157
+ const writer = getActiveWriter();
158
+ if (writer) {
159
+ const result2 = await this.stream(ctx, toolInput);
160
+ writer.merge(result2.toUIMessageStream());
161
+ if (options?.mapOutput) return options.mapOutput(result2);
162
+ return extractOutput(result2, this.hasOutput);
163
+ }
147
164
  const result = await this.generate(ctx, toolInput);
148
165
  if (options?.mapOutput) return options.mapOutput(result);
149
166
  return extractOutput(result, this.hasOutput);
@@ -163,8 +180,8 @@ var Agent = class {
163
180
  ...resolved.messages ? { messages: resolved.messages } : { prompt: resolved.prompt ?? "" },
164
181
  ...resolved.system ? { system: resolved.system } : {},
165
182
  ...this.config.output ? { output: this.config.output } : {},
166
- onStepFinish: this._onStepFinish ? (event) => this._onStepFinish({ result: event, ctx, input }) : void 0,
167
- onFinish: this._onFinish ? (event) => this._onFinish({ result: event, ctx, input }) : void 0
183
+ onStepFinish: this._onStepFinish ? (event) => this._onStepFinish({ result: event, ctx, input, writer: getActiveWriter() }) : void 0,
184
+ onFinish: this._onFinish ? (event) => this._onFinish({ result: event, ctx, input, writer: getActiveWriter() }) : void 0
168
185
  };
169
186
  }
170
187
  resolveConfig(ctx, input) {
@@ -235,6 +252,14 @@ var WorkflowLoopError = class extends Error {
235
252
  this.name = "WorkflowLoopError";
236
253
  }
237
254
  };
255
+ var WorkflowSuspended = class extends Error {
256
+ snapshot;
257
+ constructor(snapshot) {
258
+ super(`Workflow suspended at gate "${snapshot.gateId}"`);
259
+ this.name = "WorkflowSuspended";
260
+ this.snapshot = snapshot;
261
+ }
262
+ };
238
263
  var SealedWorkflow = class {
239
264
  id;
240
265
  steps;
@@ -291,12 +316,13 @@ var SealedWorkflow = class {
291
316
  };
292
317
  }
293
318
  // ── Internal: execute pipeline ────────────────────────────────
294
- async execute(state) {
319
+ async execute(state, startIndex = 0) {
295
320
  if (this.steps.length === 0) {
296
321
  throw new Error("Workflow has no steps. Add at least one step before calling generate() or stream().");
297
322
  }
298
323
  let pendingError = null;
299
- for (const node of this.steps) {
324
+ for (let i = startIndex; i < this.steps.length; i++) {
325
+ const node = this.steps[i];
300
326
  if (node.type === "finally") {
301
327
  await node.execute(state);
302
328
  continue;
@@ -316,10 +342,26 @@ var SealedWorkflow = class {
316
342
  }
317
343
  continue;
318
344
  }
345
+ if (node.type === "gate") {
346
+ if (pendingError) continue;
347
+ if (node.condition) {
348
+ const shouldSuspend = await node.condition(state);
349
+ if (!shouldSuspend) continue;
350
+ }
351
+ const gatePayload = await node.payload(state);
352
+ throw new WorkflowSuspended({
353
+ version: 1,
354
+ resumeFromIndex: i,
355
+ output: state.output,
356
+ gateId: node.id,
357
+ gatePayload
358
+ });
359
+ }
319
360
  if (pendingError) continue;
320
361
  try {
321
362
  await node.execute(state);
322
363
  } catch (error) {
364
+ if (error instanceof WorkflowSuspended) throw error;
323
365
  pendingError = { error, stepId: node.id };
324
366
  }
325
367
  }
@@ -329,7 +371,16 @@ var SealedWorkflow = class {
329
371
  // Defined on SealedWorkflow (not Workflow) because TypeScript's protected
330
372
  // access rules only allow calling workflow.execute() from the same class.
331
373
  async executeNestedWorkflow(state, workflow) {
332
- await workflow.execute(state);
374
+ try {
375
+ await workflow.execute(state);
376
+ } catch (error) {
377
+ if (error instanceof WorkflowSuspended) {
378
+ throw new Error(
379
+ `Gates inside nested workflows are not yet supported. Gate "${error.snapshot.gateId}" was hit inside nested workflow "${workflow.id ?? "(anonymous)"}". Consider using a conditional gate with \`condition\` to skip when criteria are met, or restructure the workflow to use gates at the top level only.`
380
+ );
381
+ }
382
+ throw error;
383
+ }
333
384
  }
334
385
  // ── Internal: execute an agent within a step/branch ───────────
335
386
  // In stream mode, output extraction awaits the full stream before returning.
@@ -339,20 +390,23 @@ var SealedWorkflow = class {
339
390
  const input = state.output;
340
391
  const hasStructuredOutput = agent.hasOutput;
341
392
  if (state.mode === "stream" && state.writer) {
342
- const result = await agent.stream(ctx, state.output);
343
- if (options?.handleStream) {
344
- await options.handleStream({ result, writer: state.writer, ctx });
345
- } else {
346
- state.writer.merge(result.toUIMessageStream());
347
- }
348
- if (options?.onStreamResult) {
349
- await options.onStreamResult({ result, ctx, input });
350
- }
351
- if (options?.mapStreamResult) {
352
- state.output = await options.mapStreamResult({ result, ctx, input });
353
- } else {
354
- state.output = await extractOutput(result, hasStructuredOutput);
355
- }
393
+ const writer = state.writer;
394
+ await runWithWriter(writer, async () => {
395
+ const result = await agent.stream(ctx, state.output);
396
+ if (options?.handleStream) {
397
+ await options.handleStream({ result, writer, ctx });
398
+ } else {
399
+ writer.merge(result.toUIMessageStream());
400
+ }
401
+ if (options?.onStreamResult) {
402
+ await options.onStreamResult({ result, ctx, input });
403
+ }
404
+ if (options?.mapStreamResult) {
405
+ state.output = await options.mapStreamResult({ result, ctx, input });
406
+ } else {
407
+ state.output = await extractOutput(result, hasStructuredOutput);
408
+ }
409
+ });
356
410
  } else {
357
411
  const result = await agent.generate(ctx, state.output);
358
412
  if (options?.onGenerateResult) {
@@ -365,6 +419,106 @@ var SealedWorkflow = class {
365
419
  }
366
420
  }
367
421
  }
422
+ // ── Gate: load persisted state for resumption ──────────────────
423
+ loadState(gateId, snapshot) {
424
+ if (snapshot.gateId !== gateId) {
425
+ throw new Error(
426
+ `loadState: gate ID mismatch \u2014 expected "${gateId}" but snapshot has "${snapshot.gateId}".`
427
+ );
428
+ }
429
+ const gateIndex = this.findGateIndex(snapshot);
430
+ const gateNode = this.steps[gateIndex];
431
+ return new ResumedWorkflow(
432
+ this.steps,
433
+ gateIndex + 1,
434
+ gateNode.schema,
435
+ gateNode.merge,
436
+ snapshot.output
437
+ );
438
+ }
439
+ findGateIndex(snapshot) {
440
+ if (snapshot.version !== 1) {
441
+ throw new Error(`Unsupported snapshot version: ${snapshot.version}`);
442
+ }
443
+ const hint = snapshot.resumeFromIndex;
444
+ if (hint >= 0 && hint < this.steps.length) {
445
+ const node = this.steps[hint];
446
+ if (node.type === "gate" && node.id === snapshot.gateId) {
447
+ return hint;
448
+ }
449
+ }
450
+ for (let i = 0; i < this.steps.length; i++) {
451
+ const node = this.steps[i];
452
+ if (node.type === "gate" && node.id === snapshot.gateId) {
453
+ return i;
454
+ }
455
+ }
456
+ throw new Error(
457
+ `Gate "${snapshot.gateId}" not found in workflow. The workflow definition may have changed since the snapshot was created.`
458
+ );
459
+ }
460
+ };
461
+ var ResumedWorkflow = class extends SealedWorkflow {
462
+ startIndex;
463
+ schema;
464
+ mergeFn;
465
+ priorOutput;
466
+ /** @internal */
467
+ constructor(steps, startIndex, schema, mergeFn, priorOutput) {
468
+ super(steps);
469
+ this.startIndex = startIndex;
470
+ this.schema = schema;
471
+ this.mergeFn = mergeFn;
472
+ this.priorOutput = priorOutput;
473
+ }
474
+ validateResponse(response) {
475
+ if (this.schema) {
476
+ return this.schema.parse(response);
477
+ }
478
+ return response;
479
+ }
480
+ async generate(ctx, ...args) {
481
+ const response = this.validateResponse(args[0]);
482
+ const output = this.mergeFn ? await this.mergeFn({ priorOutput: this.priorOutput, response }) : response;
483
+ const state = { ctx, output, mode: "generate" };
484
+ await this.execute(state, this.startIndex);
485
+ return { output: state.output };
486
+ }
487
+ stream(ctx, ...args) {
488
+ const response = this.validateResponse(args[0]);
489
+ const options = args[1];
490
+ let resolveOutput;
491
+ let rejectOutput;
492
+ const outputPromise = new Promise((res, rej) => {
493
+ resolveOutput = res;
494
+ rejectOutput = rej;
495
+ });
496
+ outputPromise.catch(() => {
497
+ });
498
+ const mergeFn = this.mergeFn;
499
+ const priorOutput = this.priorOutput;
500
+ const stream = createUIMessageStream({
501
+ execute: async ({ writer }) => {
502
+ const output = mergeFn ? await mergeFn({ priorOutput, response }) : response;
503
+ const state = {
504
+ ctx,
505
+ output,
506
+ mode: "stream",
507
+ writer
508
+ };
509
+ try {
510
+ await this.execute(state, this.startIndex);
511
+ resolveOutput(state.output);
512
+ } catch (error) {
513
+ rejectOutput(error);
514
+ throw error;
515
+ }
516
+ },
517
+ ...options?.onError ? { onError: options.onError } : {},
518
+ ...options?.onFinish ? { onFinish: options.onFinish } : {}
519
+ });
520
+ return { stream, output: outputPromise };
521
+ }
368
522
  };
369
523
  var Workflow = class _Workflow extends SealedWorkflow {
370
524
  constructor(steps = [], id) {
@@ -418,6 +572,32 @@ var Workflow = class _Workflow extends SealedWorkflow {
418
572
  };
419
573
  return new _Workflow([...this.steps, node], this.id);
420
574
  }
575
+ // ── gate: human-in-the-loop suspension point ────────────────
576
+ gate(id, options) {
577
+ if (this.steps.some((s) => s.type === "gate" && s.id === id)) {
578
+ throw new Error(`Workflow: duplicate gate ID "${id}". Each gate must have a unique identifier.`);
579
+ }
580
+ const node = {
581
+ type: "gate",
582
+ id,
583
+ schema: options?.schema,
584
+ condition: options?.condition ? async (state) => options.condition({
585
+ ctx: state.ctx,
586
+ input: state.output
587
+ }) : void 0,
588
+ merge: options?.merge ? (params) => options.merge(params) : void 0,
589
+ payload: async (state) => {
590
+ if (options?.payload) {
591
+ return options.payload({
592
+ ctx: state.ctx,
593
+ input: state.output
594
+ });
595
+ }
596
+ return state.output;
597
+ }
598
+ };
599
+ return new _Workflow([...this.steps, node], this.id);
600
+ }
421
601
  // ── branch: implementation ────────────────────────────────────
422
602
  branch(casesOrConfig) {
423
603
  if (Array.isArray(casesOrConfig)) {
@@ -563,6 +743,7 @@ export {
563
743
  Workflow,
564
744
  WorkflowBranchError,
565
745
  WorkflowLoopError,
746
+ WorkflowSuspended,
566
747
  defineTool
567
748
  };
568
749
  //# sourceMappingURL=index.js.map