@ai-sdk/workflow 1.0.0-beta.1 → 1.0.0-beta.10

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.
@@ -21,6 +21,7 @@ import {
21
21
  type ToolChoice,
22
22
  type ToolSet,
23
23
  type UIMessage,
24
+ LanguageModel,
24
25
  } from 'ai';
25
26
  import {
26
27
  convertToLanguageModelPrompt,
@@ -34,6 +35,15 @@ import type { CompatibleLanguageModel } from './types.js';
34
35
  // Re-export for consumers
35
36
  export type { CompatibleLanguageModel } from './types.js';
36
37
 
38
+ /**
39
+ * Callback function to be called after each step completes.
40
+ * Alias for the AI SDK's StreamTextOnStepFinishCallback, using
41
+ * WorkflowAgent-consistent naming.
42
+ */
43
+ export type WorkflowAgentOnStepFinishCallback<
44
+ TTools extends ToolSet = ToolSet,
45
+ > = StreamTextOnStepFinishCallback<TTools, any>;
46
+
37
47
  /**
38
48
  * Infer the type of the tools of a workflow agent.
39
49
  */
@@ -248,10 +258,7 @@ export interface PrepareStepInfo<TTools extends ToolSet = ToolSet> {
248
258
  * The current model configuration (string or function).
249
259
  * The function should return a LanguageModelV4 instance.
250
260
  */
251
- model:
252
- | string
253
- | CompatibleLanguageModel
254
- | (() => Promise<CompatibleLanguageModel>);
261
+ model: LanguageModel;
255
262
 
256
263
  /**
257
264
  * The current step number (0-indexed).
@@ -282,12 +289,8 @@ export interface PrepareStepInfo<TTools extends ToolSet = ToolSet> {
282
289
  export interface PrepareStepResult extends Partial<GenerationSettings> {
283
290
  /**
284
291
  * Override the model for this step.
285
- * The function should return a LanguageModelV4 instance.
286
292
  */
287
- model?:
288
- | string
289
- | CompatibleLanguageModel
290
- | (() => Promise<CompatibleLanguageModel>);
293
+ model?: LanguageModel;
291
294
 
292
295
  /**
293
296
  * Override the system message for this step.
@@ -332,10 +335,7 @@ export type PrepareStepCallback<TTools extends ToolSet = ToolSet> = (
332
335
  export interface PrepareCallOptions<
333
336
  TTools extends ToolSet = ToolSet,
334
337
  > extends Partial<GenerationSettings> {
335
- model:
336
- | string
337
- | CompatibleLanguageModel
338
- | (() => Promise<CompatibleLanguageModel>);
338
+ model: LanguageModel;
339
339
  tools: TTools;
340
340
  instructions?: string | SystemModelMessage | Array<SystemModelMessage>;
341
341
  toolChoice?: ToolChoice<TTools>;
@@ -367,16 +367,18 @@ export type PrepareCallCallback<TTools extends ToolSet = ToolSet> = (
367
367
  export interface WorkflowAgentOptions<
368
368
  TTools extends ToolSet = ToolSet,
369
369
  > extends GenerationSettings {
370
+ /**
371
+ * The id of the agent.
372
+ */
373
+ id?: string;
374
+
370
375
  /**
371
376
  * The model provider to use for the agent.
372
377
  *
373
378
  * This should be a string compatible with the Vercel AI Gateway (e.g., 'anthropic/claude-opus'),
374
- * or a step function that returns a LanguageModelV4 instance.
379
+ * or a LanguageModelV4 instance from a provider.
375
380
  */
376
- model:
377
- | string
378
- | CompatibleLanguageModel
379
- | (() => Promise<CompatibleLanguageModel>);
381
+ model: LanguageModel;
380
382
 
381
383
  /**
382
384
  * A set of tools available to the agent.
@@ -416,6 +418,46 @@ export interface WorkflowAgentOptions<
416
418
  */
417
419
  experimental_context?: unknown;
418
420
 
421
+ /**
422
+ * Default stop condition for the agent loop. When the condition is an array,
423
+ * any of the conditions can be met to stop the generation.
424
+ *
425
+ * Per-stream `stopWhen` values passed to `stream()` override this default.
426
+ */
427
+ stopWhen?:
428
+ | StopCondition<NoInfer<ToolSet>, any>
429
+ | Array<StopCondition<NoInfer<ToolSet>, any>>;
430
+
431
+ /**
432
+ * Default set of active tools that limits which tools the model can call,
433
+ * without changing the tool call and result types in the result.
434
+ *
435
+ * Per-stream `activeTools` values passed to `stream()` override this default.
436
+ */
437
+ activeTools?: Array<keyof NoInfer<TTools>>;
438
+
439
+ /**
440
+ * Default output specification for structured outputs.
441
+ * Use `Output.object({ schema })` for structured output or `Output.text()` for text output.
442
+ *
443
+ * Per-stream `output` values passed to `stream()` override this default.
444
+ */
445
+ output?: OutputSpecification<any, any>;
446
+
447
+ /**
448
+ * Default function that attempts to repair a tool call that failed to parse.
449
+ *
450
+ * Per-stream `experimental_repairToolCall` values passed to `stream()` override this default.
451
+ */
452
+ experimental_repairToolCall?: ToolCallRepairFunction<TTools>;
453
+
454
+ /**
455
+ * Default custom download function to use for URLs.
456
+ *
457
+ * Per-stream `experimental_download` values passed to `stream()` override this default.
458
+ */
459
+ experimental_download?: DownloadFunction;
460
+
419
461
  /**
420
462
  * Default callback function called before each step in the agent loop.
421
463
  * Use this to modify settings, manage context, or inject messages dynamically
@@ -428,12 +470,12 @@ export interface WorkflowAgentOptions<
428
470
  /**
429
471
  * Callback function to be called after each step completes.
430
472
  */
431
- onStepFinish?: StreamTextOnStepFinishCallback<ToolSet, any>;
473
+ onStepFinish?: WorkflowAgentOnStepFinishCallback<ToolSet>;
432
474
 
433
475
  /**
434
476
  * Callback that is called when the LLM response and all request tool executions are finished.
435
477
  */
436
- onFinish?: StreamTextOnFinishCallback<ToolSet>;
478
+ onFinish?: WorkflowAgentOnFinishCallback<ToolSet>;
437
479
 
438
480
  /**
439
481
  * Callback called when the agent starts streaming, before any LLM calls.
@@ -466,7 +508,7 @@ export interface WorkflowAgentOptions<
466
508
  /**
467
509
  * Callback that is called when the LLM response and all request tool executions are finished.
468
510
  */
469
- export type StreamTextOnFinishCallback<
511
+ export type WorkflowAgentOnFinishCallback<
470
512
  TTools extends ToolSet = ToolSet,
471
513
  OUTPUT = never,
472
514
  > = (event: {
@@ -510,14 +552,14 @@ export type StreamTextOnFinishCallback<
510
552
  /**
511
553
  * Callback that is invoked when an error occurs during streaming.
512
554
  */
513
- export type StreamTextOnErrorCallback = (event: {
555
+ export type WorkflowAgentOnErrorCallback = (event: {
514
556
  error: unknown;
515
557
  }) => PromiseLike<void> | void;
516
558
 
517
559
  /**
518
560
  * Callback that is set using the `onAbort` option.
519
561
  */
520
- export type StreamTextOnAbortCallback<TTools extends ToolSet = ToolSet> =
562
+ export type WorkflowAgentOnAbortCallback<TTools extends ToolSet = ToolSet> =
521
563
  (event: {
522
564
  /**
523
565
  * Details for all previously finished steps.
@@ -530,10 +572,7 @@ export type StreamTextOnAbortCallback<TTools extends ToolSet = ToolSet> =
530
572
  */
531
573
  export type WorkflowAgentOnStartCallback = (event: {
532
574
  /** The model being used */
533
- readonly model:
534
- | string
535
- | CompatibleLanguageModel
536
- | (() => Promise<CompatibleLanguageModel>);
575
+ readonly model: LanguageModel;
537
576
  /** The messages being sent */
538
577
  readonly messages: ModelMessage[];
539
578
  }) => PromiseLike<void> | void;
@@ -541,17 +580,17 @@ export type WorkflowAgentOnStartCallback = (event: {
541
580
  /**
542
581
  * Callback that is called before each step (LLM call) begins.
543
582
  */
544
- export type WorkflowAgentOnStepStartCallback = (event: {
545
- /** The current step number (0-based) */
546
- readonly stepNumber: number;
547
- /** The model being used for this step */
548
- readonly model:
549
- | string
550
- | CompatibleLanguageModel
551
- | (() => Promise<CompatibleLanguageModel>);
552
- /** The messages being sent for this step */
553
- readonly messages: ModelMessage[];
554
- }) => PromiseLike<void> | void;
583
+ export type WorkflowAgentOnStepStartCallback<TTools extends ToolSet = ToolSet> =
584
+ (event: {
585
+ /** The current step number (0-based) */
586
+ readonly stepNumber: number;
587
+ /** The model being used for this step */
588
+ readonly model: LanguageModel;
589
+ /** The messages being sent for this step */
590
+ readonly messages: ModelMessage[];
591
+ /** Results from all previously finished steps */
592
+ readonly steps: ReadonlyArray<StepResult<TTools, any>>;
593
+ }) => PromiseLike<void> | void;
555
594
 
556
595
  /**
557
596
  * Callback that is called before a tool's execute function runs.
@@ -559,235 +598,287 @@ export type WorkflowAgentOnStepStartCallback = (event: {
559
598
  export type WorkflowAgentOnToolCallStartCallback = (event: {
560
599
  /** The tool call being executed */
561
600
  readonly toolCall: ToolCall;
601
+ /** The current step number (0-based) */
602
+ readonly stepNumber: number;
562
603
  }) => PromiseLike<void> | void;
563
604
 
564
605
  /**
565
606
  * Callback that is called after a tool execution completes.
607
+ * Uses a discriminated union pattern: check `success` to determine
608
+ * whether `output` or `error` is available.
566
609
  */
567
- export type WorkflowAgentOnToolCallFinishCallback = (event: {
568
- /** The tool call that was executed */
569
- readonly toolCall: ToolCall;
570
- /** The tool result (undefined if execution failed) */
571
- readonly result?: unknown;
572
- /** The error if execution failed */
573
- readonly error?: unknown;
574
- }) => PromiseLike<void> | void;
610
+ export type WorkflowAgentOnToolCallFinishCallback = (
611
+ event:
612
+ | {
613
+ /** The tool call that was executed */
614
+ readonly toolCall: ToolCall;
615
+ /** The current step number (0-based) */
616
+ readonly stepNumber: number;
617
+ /** Execution time in milliseconds */
618
+ readonly durationMs: number;
619
+ /** Whether the tool call succeeded */
620
+ readonly success: true;
621
+ /** The tool result */
622
+ readonly output: unknown;
623
+ readonly error?: never;
624
+ }
625
+ | {
626
+ /** The tool call that was executed */
627
+ readonly toolCall: ToolCall;
628
+ /** The current step number (0-based) */
629
+ readonly stepNumber: number;
630
+ /** Execution time in milliseconds */
631
+ readonly durationMs: number;
632
+ /** Whether the tool call succeeded */
633
+ readonly success: false;
634
+ /** The error that occurred */
635
+ readonly error: unknown;
636
+ readonly output?: never;
637
+ },
638
+ ) => PromiseLike<void> | void;
575
639
 
576
640
  /**
577
641
  * Options for the {@link WorkflowAgent.stream} method.
578
642
  */
579
- export interface WorkflowAgentStreamOptions<
643
+ export type WorkflowAgentStreamOptions<
580
644
  TTools extends ToolSet = ToolSet,
581
645
  OUTPUT = never,
582
646
  PARTIAL_OUTPUT = never,
583
- > extends Partial<GenerationSettings> {
584
- /**
585
- * The conversation messages to process. Should follow the AI SDK's ModelMessage format.
586
- */
587
- messages: ModelMessage[];
588
-
589
- /**
590
- * Optional system prompt override. If provided, overrides the system prompt from the constructor.
591
- */
592
- system?: string;
647
+ > = Partial<GenerationSettings> &
648
+ (
649
+ | {
650
+ /**
651
+ * A prompt. It can be either a text prompt or a list of messages.
652
+ *
653
+ * You can either use `prompt` or `messages` but not both.
654
+ */
655
+ prompt: string | Array<ModelMessage>;
656
+
657
+ /**
658
+ * A list of messages.
659
+ *
660
+ * You can either use `prompt` or `messages` but not both.
661
+ */
662
+ messages?: never;
663
+ }
664
+ | {
665
+ /**
666
+ * The conversation messages to process. Should follow the AI SDK's ModelMessage format.
667
+ *
668
+ * You can either use `prompt` or `messages` but not both.
669
+ */
670
+ messages: Array<ModelMessage>;
671
+
672
+ /**
673
+ * A prompt. It can be either a text prompt or a list of messages.
674
+ *
675
+ * You can either use `prompt` or `messages` but not both.
676
+ */
677
+ prompt?: never;
678
+ }
679
+ ) & {
680
+ /**
681
+ * Optional system prompt override. If provided, overrides the system prompt from the constructor.
682
+ */
683
+ system?: string;
593
684
 
594
- /**
595
- * A WritableStream that receives raw LanguageModelV4StreamPart chunks in real-time
596
- * as the model generates them. This enables streaming to the client without
597
- * coupling WorkflowAgent to UIMessageChunk format.
598
- *
599
- * Convert to UIMessageChunks at the response boundary using
600
- * `createUIMessageChunkTransform()` from `@ai-sdk/workflow`.
601
- *
602
- * @example
603
- * ```typescript
604
- * // In the workflow:
605
- * await agent.stream({
606
- * messages,
607
- * writable: getWritable<ModelCallStreamPart>(),
608
- * });
609
- *
610
- * // In the route handler:
611
- * return createUIMessageStreamResponse({
612
- * stream: run.readable.pipeThrough(createModelCallToUIChunkTransform()),
613
- * });
614
- * ```
615
- */
616
- writable?: WritableStream<ModelCallStreamPart<ToolSet>>;
685
+ /**
686
+ * A WritableStream that receives raw LanguageModelV4StreamPart chunks in real-time
687
+ * as the model generates them. This enables streaming to the client without
688
+ * coupling WorkflowAgent to UIMessageChunk format.
689
+ *
690
+ * Convert to UIMessageChunks at the response boundary using
691
+ * `createUIMessageChunkTransform()` from `@ai-sdk/workflow`.
692
+ *
693
+ * @example
694
+ * ```typescript
695
+ * // In the workflow:
696
+ * await agent.stream({
697
+ * messages,
698
+ * writable: getWritable<ModelCallStreamPart>(),
699
+ * });
700
+ *
701
+ * // In the route handler:
702
+ * return createUIMessageStreamResponse({
703
+ * stream: run.readable.pipeThrough(createModelCallToUIChunkTransform()),
704
+ * });
705
+ * ```
706
+ */
707
+ writable?: WritableStream<ModelCallStreamPart<ToolSet>>;
617
708
 
618
- /**
619
- * Condition for stopping the generation when there are tool results in the last step.
620
- * When the condition is an array, any of the conditions can be met to stop the generation.
621
- */
622
- stopWhen?:
623
- | StopCondition<NoInfer<ToolSet>, any>
624
- | Array<StopCondition<NoInfer<ToolSet>, any>>;
709
+ /**
710
+ * Condition for stopping the generation when there are tool results in the last step.
711
+ * When the condition is an array, any of the conditions can be met to stop the generation.
712
+ */
713
+ stopWhen?:
714
+ | StopCondition<NoInfer<ToolSet>, any>
715
+ | Array<StopCondition<NoInfer<ToolSet>, any>>;
625
716
 
626
- /**
627
- * Maximum number of sequential LLM calls (steps), e.g. when you use tool calls.
628
- * A maximum number can be set to prevent infinite loops in the case of misconfigured tools.
629
- * By default, it's unlimited (the agent loops until completion).
630
- */
631
- maxSteps?: number;
717
+ /**
718
+ * Maximum number of sequential LLM calls (steps), e.g. when you use tool calls.
719
+ * A maximum number can be set to prevent infinite loops in the case of misconfigured tools.
720
+ * By default, it's unlimited (the agent loops until completion).
721
+ */
722
+ maxSteps?: number;
632
723
 
633
- /**
634
- * The tool choice strategy. Default: 'auto'.
635
- * Overrides the toolChoice from the constructor if provided.
636
- */
637
- toolChoice?: ToolChoice<TTools>;
724
+ /**
725
+ * The tool choice strategy. Default: 'auto'.
726
+ * Overrides the toolChoice from the constructor if provided.
727
+ */
728
+ toolChoice?: ToolChoice<TTools>;
638
729
 
639
- /**
640
- * Limits the tools that are available for the model to call without
641
- * changing the tool call and result types in the result.
642
- */
643
- activeTools?: Array<keyof NoInfer<TTools>>;
730
+ /**
731
+ * Limits the tools that are available for the model to call without
732
+ * changing the tool call and result types in the result.
733
+ */
734
+ activeTools?: Array<keyof NoInfer<TTools>>;
644
735
 
645
- /**
646
- * Optional telemetry configuration (experimental).
647
- */
648
- experimental_telemetry?: TelemetrySettings;
736
+ /**
737
+ * Optional telemetry configuration (experimental).
738
+ */
739
+ experimental_telemetry?: TelemetrySettings;
649
740
 
650
- /**
651
- * Context that is passed into tool execution.
652
- * Experimental (can break in patch releases).
653
- * @default undefined
654
- */
655
- experimental_context?: unknown;
741
+ /**
742
+ * Context that is passed into tool execution.
743
+ * Experimental (can break in patch releases).
744
+ * @default undefined
745
+ */
746
+ experimental_context?: unknown;
656
747
 
657
- /**
658
- * Optional specification for parsing structured outputs from the LLM response.
659
- * Use `Output.object({ schema })` for structured output or `Output.text()` for text output.
660
- *
661
- * @example
662
- * ```typescript
663
- * import { Output } from '@workflow/ai';
664
- * import { z } from 'zod';
665
- *
666
- * const result = await agent.stream({
667
- * messages: [...],
668
- * writable: getWritable(),
669
- * output: Output.object({
670
- * schema: z.object({
671
- * sentiment: z.enum(['positive', 'negative', 'neutral']),
672
- * confidence: z.number(),
673
- * }),
674
- * }),
675
- * });
676
- *
677
- * console.log(result.output); // { sentiment: 'positive', confidence: 0.95 }
678
- * ```
679
- */
680
- output?: OutputSpecification<OUTPUT, PARTIAL_OUTPUT>;
748
+ /**
749
+ * Optional specification for parsing structured outputs from the LLM response.
750
+ * Use `Output.object({ schema })` for structured output or `Output.text()` for text output.
751
+ *
752
+ * @example
753
+ * ```typescript
754
+ * import { Output } from '@workflow/ai';
755
+ * import { z } from 'zod';
756
+ *
757
+ * const result = await agent.stream({
758
+ * messages: [...],
759
+ * writable: getWritable(),
760
+ * output: Output.object({
761
+ * schema: z.object({
762
+ * sentiment: z.enum(['positive', 'negative', 'neutral']),
763
+ * confidence: z.number(),
764
+ * }),
765
+ * }),
766
+ * });
767
+ *
768
+ * console.log(result.output); // { sentiment: 'positive', confidence: 0.95 }
769
+ * ```
770
+ */
771
+ output?: OutputSpecification<OUTPUT, PARTIAL_OUTPUT>;
681
772
 
682
- /**
683
- * Whether to include raw chunks from the provider in the stream.
684
- * When enabled, you will receive raw chunks with type 'raw' that contain the unprocessed data from the provider.
685
- * This allows access to cutting-edge provider features not yet wrapped by the AI SDK.
686
- * Defaults to false.
687
- */
688
- includeRawChunks?: boolean;
773
+ /**
774
+ * Whether to include raw chunks from the provider in the stream.
775
+ * When enabled, you will receive raw chunks with type 'raw' that contain the unprocessed data from the provider.
776
+ * This allows access to cutting-edge provider features not yet wrapped by the AI SDK.
777
+ * Defaults to false.
778
+ */
779
+ includeRawChunks?: boolean;
689
780
 
690
- /**
691
- * A function that attempts to repair a tool call that failed to parse.
692
- */
693
- experimental_repairToolCall?: ToolCallRepairFunction<TTools>;
781
+ /**
782
+ * A function that attempts to repair a tool call that failed to parse.
783
+ */
784
+ experimental_repairToolCall?: ToolCallRepairFunction<TTools>;
694
785
 
695
- /**
696
- * Optional stream transformations.
697
- * They are applied in the order they are provided.
698
- * The stream transformations must maintain the stream structure for streamText to work correctly.
699
- */
700
- experimental_transform?:
701
- | StreamTextTransform<TTools>
702
- | Array<StreamTextTransform<TTools>>;
786
+ /**
787
+ * Optional stream transformations.
788
+ * They are applied in the order they are provided.
789
+ * The stream transformations must maintain the stream structure for streamText to work correctly.
790
+ */
791
+ experimental_transform?:
792
+ | StreamTextTransform<TTools>
793
+ | Array<StreamTextTransform<TTools>>;
703
794
 
704
- /**
705
- * Custom download function to use for URLs.
706
- * By default, files are downloaded if the model does not support the URL for the given media type.
707
- */
708
- experimental_download?: DownloadFunction;
795
+ /**
796
+ * Custom download function to use for URLs.
797
+ * By default, files are downloaded if the model does not support the URL for the given media type.
798
+ */
799
+ experimental_download?: DownloadFunction;
709
800
 
710
- /**
711
- * Callback function to be called after each step completes.
712
- */
713
- onStepFinish?: StreamTextOnStepFinishCallback<TTools, any>;
801
+ /**
802
+ * Callback function to be called after each step completes.
803
+ */
804
+ onStepFinish?: WorkflowAgentOnStepFinishCallback<TTools>;
714
805
 
715
- /**
716
- * Callback that is invoked when an error occurs during streaming.
717
- * You can use it to log errors.
718
- */
719
- onError?: StreamTextOnErrorCallback;
806
+ /**
807
+ * Callback that is invoked when an error occurs during streaming.
808
+ * You can use it to log errors.
809
+ */
810
+ onError?: WorkflowAgentOnErrorCallback;
720
811
 
721
- /**
722
- * Callback that is called when the LLM response and all request tool executions
723
- * (for tools that have an `execute` function) are finished.
724
- */
725
- onFinish?: StreamTextOnFinishCallback<TTools, OUTPUT>;
812
+ /**
813
+ * Callback that is called when the LLM response and all request tool executions
814
+ * (for tools that have an `execute` function) are finished.
815
+ */
816
+ onFinish?: WorkflowAgentOnFinishCallback<TTools, OUTPUT>;
726
817
 
727
- /**
728
- * Callback that is called when the operation is aborted.
729
- */
730
- onAbort?: StreamTextOnAbortCallback<TTools>;
818
+ /**
819
+ * Callback that is called when the operation is aborted.
820
+ */
821
+ onAbort?: WorkflowAgentOnAbortCallback<TTools>;
731
822
 
732
- /**
733
- * Callback called when the agent starts streaming, before any LLM calls.
734
- */
735
- experimental_onStart?: WorkflowAgentOnStartCallback;
823
+ /**
824
+ * Callback called when the agent starts streaming, before any LLM calls.
825
+ */
826
+ experimental_onStart?: WorkflowAgentOnStartCallback;
736
827
 
737
- /**
738
- * Callback called before each step (LLM call) begins.
739
- */
740
- experimental_onStepStart?: WorkflowAgentOnStepStartCallback;
828
+ /**
829
+ * Callback called before each step (LLM call) begins.
830
+ */
831
+ experimental_onStepStart?: WorkflowAgentOnStepStartCallback;
741
832
 
742
- /**
743
- * Callback called before a tool's execute function runs.
744
- */
745
- experimental_onToolCallStart?: WorkflowAgentOnToolCallStartCallback;
833
+ /**
834
+ * Callback called before a tool's execute function runs.
835
+ */
836
+ experimental_onToolCallStart?: WorkflowAgentOnToolCallStartCallback;
746
837
 
747
- /**
748
- * Callback called after a tool execution completes.
749
- */
750
- experimental_onToolCallFinish?: WorkflowAgentOnToolCallFinishCallback;
838
+ /**
839
+ * Callback called after a tool execution completes.
840
+ */
841
+ experimental_onToolCallFinish?: WorkflowAgentOnToolCallFinishCallback;
751
842
 
752
- /**
753
- * Callback function called before each step in the agent loop.
754
- * Use this to modify settings, manage context, or inject messages dynamically.
755
- *
756
- * @example
757
- * ```typescript
758
- * prepareStep: async ({ messages, stepNumber }) => {
759
- * // Inject messages from a queue
760
- * const queuedMessages = await getQueuedMessages();
761
- * if (queuedMessages.length > 0) {
762
- * return {
763
- * messages: [...messages, ...queuedMessages],
764
- * };
765
- * }
766
- * return {};
767
- * }
768
- * ```
769
- */
770
- prepareStep?: PrepareStepCallback<TTools>;
843
+ /**
844
+ * Callback function called before each step in the agent loop.
845
+ * Use this to modify settings, manage context, or inject messages dynamically.
846
+ *
847
+ * @example
848
+ * ```typescript
849
+ * prepareStep: async ({ messages, stepNumber }) => {
850
+ * // Inject messages from a queue
851
+ * const queuedMessages = await getQueuedMessages();
852
+ * if (queuedMessages.length > 0) {
853
+ * return {
854
+ * messages: [...messages, ...queuedMessages],
855
+ * };
856
+ * }
857
+ * return {};
858
+ * }
859
+ * ```
860
+ */
861
+ prepareStep?: PrepareStepCallback<TTools>;
771
862
 
772
- /**
773
- * Timeout in milliseconds for the stream operation.
774
- * When specified, creates an AbortSignal that will abort the operation after the given time.
775
- * If both `timeout` and `abortSignal` are provided, whichever triggers first will abort.
776
- */
777
- timeout?: number;
863
+ /**
864
+ * Timeout in milliseconds for the stream operation.
865
+ * When specified, creates an AbortSignal that will abort the operation after the given time.
866
+ * If both `timeout` and `abortSignal` are provided, whichever triggers first will abort.
867
+ */
868
+ timeout?: number;
778
869
 
779
- /**
780
- * Whether to send a 'finish' chunk to the writable stream when streaming completes.
781
- * @default true
782
- */
783
- sendFinish?: boolean;
870
+ /**
871
+ * Whether to send a 'finish' chunk to the writable stream when streaming completes.
872
+ * @default true
873
+ */
874
+ sendFinish?: boolean;
784
875
 
785
- /**
786
- * Whether to prevent the writable stream from being closed after streaming completes.
787
- * @default false
788
- */
789
- preventClose?: boolean;
790
- }
876
+ /**
877
+ * Whether to prevent the writable stream from being closed after streaming completes.
878
+ * @default false
879
+ */
880
+ preventClose?: boolean;
881
+ };
791
882
 
792
883
  /**
793
884
  * A tool call made by the model. Matches the AI SDK's tool call shape.
@@ -898,10 +989,12 @@ export interface WorkflowAgentStreamResult<
898
989
  * ```
899
990
  */
900
991
  export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
901
- private model:
902
- | string
903
- | CompatibleLanguageModel
904
- | (() => Promise<CompatibleLanguageModel>);
992
+ /**
993
+ * The id of the agent.
994
+ */
995
+ public readonly id: string | undefined;
996
+
997
+ private model: LanguageModel;
905
998
  /**
906
999
  * The tool set configured for this agent.
907
1000
  */
@@ -914,12 +1007,16 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
914
1007
  private toolChoice?: ToolChoice<TBaseTools>;
915
1008
  private telemetry?: TelemetrySettings;
916
1009
  private experimentalContext: unknown;
1010
+ private stopWhen?:
1011
+ | StopCondition<ToolSet, any>
1012
+ | Array<StopCondition<ToolSet, any>>;
1013
+ private activeTools?: Array<keyof TBaseTools>;
1014
+ private output?: OutputSpecification<any, any>;
1015
+ private experimentalRepairToolCall?: ToolCallRepairFunction<TBaseTools>;
1016
+ private experimentalDownload?: DownloadFunction;
917
1017
  private prepareStep?: PrepareStepCallback<TBaseTools>;
918
- private constructorOnStepFinish?: StreamTextOnStepFinishCallback<
919
- ToolSet,
920
- any
921
- >;
922
- private constructorOnFinish?: StreamTextOnFinishCallback<ToolSet>;
1018
+ private constructorOnStepFinish?: WorkflowAgentOnStepFinishCallback<ToolSet>;
1019
+ private constructorOnFinish?: WorkflowAgentOnFinishCallback<ToolSet>;
923
1020
  private constructorOnStart?: WorkflowAgentOnStartCallback;
924
1021
  private constructorOnStepStart?: WorkflowAgentOnStepStartCallback;
925
1022
  private constructorOnToolCallStart?: WorkflowAgentOnToolCallStartCallback;
@@ -927,6 +1024,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
927
1024
  private prepareCall?: PrepareCallCallback<TBaseTools>;
928
1025
 
929
1026
  constructor(options: WorkflowAgentOptions<TBaseTools>) {
1027
+ this.id = options.id;
930
1028
  this.model = options.model;
931
1029
  this.tools = (options.tools ?? {}) as TBaseTools;
932
1030
  // `instructions` takes precedence over deprecated `system`
@@ -934,6 +1032,11 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
934
1032
  this.toolChoice = options.toolChoice;
935
1033
  this.telemetry = options.experimental_telemetry;
936
1034
  this.experimentalContext = options.experimental_context;
1035
+ this.stopWhen = options.stopWhen;
1036
+ this.activeTools = options.activeTools;
1037
+ this.output = options.output;
1038
+ this.experimentalRepairToolCall = options.experimental_repairToolCall;
1039
+ this.experimentalDownload = options.experimental_download;
937
1040
  this.prepareStep = options.prepareStep;
938
1041
  this.constructorOnStepFinish = options.onStepFinish;
939
1042
  this.constructorOnFinish = options.onFinish;
@@ -972,12 +1075,11 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
972
1075
  options: WorkflowAgentStreamOptions<TTools, OUTPUT, PARTIAL_OUTPUT>,
973
1076
  ): Promise<WorkflowAgentStreamResult<TTools, OUTPUT>> {
974
1077
  // Call prepareCall to transform parameters before the agent loop
975
- let effectiveModel:
976
- | string
977
- | CompatibleLanguageModel
978
- | (() => Promise<CompatibleLanguageModel>) = this.model;
1078
+ let effectiveModel: LanguageModel = this.model;
979
1079
  let effectiveInstructions = options.system ?? this.instructions;
980
- let effectiveMessages = options.messages;
1080
+ let effectivePrompt: string | Array<ModelMessage> | undefined =
1081
+ options.prompt;
1082
+ let effectiveMessages: Array<ModelMessage> | undefined = options.messages;
981
1083
  let effectiveGenerationSettings = { ...this.generationSettings };
982
1084
  let effectiveExperimentalContext =
983
1085
  options.experimental_context ?? this.experimentalContext;
@@ -985,6 +1087,14 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
985
1087
  let effectiveTelemetryFromPrepare =
986
1088
  options.experimental_telemetry ?? this.telemetry;
987
1089
 
1090
+ // Resolve messages for prepareCall: use messages directly, or convert prompt
1091
+ const resolvedMessagesForPrepareCall: ModelMessage[] =
1092
+ effectiveMessages ??
1093
+ (typeof effectivePrompt === 'string'
1094
+ ? [{ role: 'user' as const, content: effectivePrompt }]
1095
+ : (effectivePrompt as ModelMessage[])) ??
1096
+ [];
1097
+
988
1098
  if (this.prepareCall) {
989
1099
  const prepared = await this.prepareCall({
990
1100
  model: effectiveModel,
@@ -993,16 +1103,17 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
993
1103
  toolChoice: effectiveToolChoiceFromPrepare as ToolChoice<TBaseTools>,
994
1104
  experimental_telemetry: effectiveTelemetryFromPrepare,
995
1105
  experimental_context: effectiveExperimentalContext,
996
- messages: effectiveMessages as ModelMessage[],
1106
+ messages: resolvedMessagesForPrepareCall,
997
1107
  ...effectiveGenerationSettings,
998
1108
  } as PrepareCallOptions<TBaseTools>);
999
1109
 
1000
1110
  if (prepared.model !== undefined) effectiveModel = prepared.model;
1001
1111
  if (prepared.instructions !== undefined)
1002
1112
  effectiveInstructions = prepared.instructions;
1003
- if (prepared.messages !== undefined)
1004
- effectiveMessages =
1005
- prepared.messages as WorkflowAgentStreamOptions<TTools>['messages'];
1113
+ if (prepared.messages !== undefined) {
1114
+ effectiveMessages = prepared.messages as Array<ModelMessage>;
1115
+ effectivePrompt = undefined; // messages from prepareCall take precedence
1116
+ }
1006
1117
  if (prepared.experimental_context !== undefined)
1007
1118
  effectiveExperimentalContext = prepared.experimental_context;
1008
1119
  if (prepared.toolChoice !== undefined)
@@ -1035,7 +1146,9 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1035
1146
 
1036
1147
  const prompt = await standardizePrompt({
1037
1148
  system: effectiveInstructions,
1038
- messages: effectiveMessages,
1149
+ ...(effectivePrompt != null
1150
+ ? { prompt: effectivePrompt }
1151
+ : { messages: effectiveMessages! }),
1039
1152
  });
1040
1153
 
1041
1154
  // Process tool approval responses before starting the agent loop.
@@ -1164,7 +1277,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1164
1277
  const modelPrompt = await convertToLanguageModelPrompt({
1165
1278
  prompt,
1166
1279
  supportedUrls: {},
1167
- download: options.experimental_download,
1280
+ download: options.experimental_download ?? this.experimentalDownload,
1168
1281
  });
1169
1282
 
1170
1283
  const effectiveAbortSignal = mergeAbortSignals(
@@ -1210,13 +1323,13 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1210
1323
  // Merge constructor + stream callbacks (constructor first, then stream)
1211
1324
  const mergedOnStepFinish = mergeCallbacks(
1212
1325
  this.constructorOnStepFinish as
1213
- | StreamTextOnStepFinishCallback<TTools, any>
1326
+ | WorkflowAgentOnStepFinishCallback<TTools>
1214
1327
  | undefined,
1215
1328
  options.onStepFinish,
1216
1329
  );
1217
1330
  const mergedOnFinish = mergeCallbacks(
1218
1331
  this.constructorOnFinish as
1219
- | StreamTextOnFinishCallback<TTools, OUTPUT>
1332
+ | WorkflowAgentOnFinishCallback<TTools, OUTPUT>
1220
1333
  | undefined,
1221
1334
  options.onFinish,
1222
1335
  );
@@ -1243,10 +1356,11 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1243
1356
  // Merge telemetry settings
1244
1357
  const effectiveTelemetry = effectiveTelemetryFromPrepare;
1245
1358
 
1246
- // Filter tools if activeTools is specified
1359
+ // Filter tools if activeTools is specified (stream-level overrides constructor default)
1360
+ const effectiveActiveTools = options.activeTools ?? this.activeTools;
1247
1361
  const effectiveTools =
1248
- options.activeTools && options.activeTools.length > 0
1249
- ? filterTools(this.tools, options.activeTools as string[])
1362
+ effectiveActiveTools && effectiveActiveTools.length > 0
1363
+ ? filterTools(this.tools, effectiveActiveTools as string[])
1250
1364
  : this.tools;
1251
1365
 
1252
1366
  // Initialize context
@@ -1262,7 +1376,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1262
1376
  if (mergedOnStart) {
1263
1377
  await mergedOnStart({
1264
1378
  model: effectiveModel,
1265
- messages: effectiveMessages as ModelMessage[],
1379
+ messages: prompt.messages,
1266
1380
  });
1267
1381
  }
1268
1382
 
@@ -1272,59 +1386,67 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1272
1386
  tools: ToolSet,
1273
1387
  messages: LanguageModelV4Prompt,
1274
1388
  context?: unknown,
1389
+ currentStepNumber: number = 0,
1275
1390
  ): Promise<LanguageModelV4ToolResultPart> => {
1391
+ const toolCallEvent: ToolCall = {
1392
+ type: 'tool-call',
1393
+ toolCallId: toolCall.toolCallId,
1394
+ toolName: toolCall.toolName,
1395
+ input: toolCall.input,
1396
+ };
1397
+
1276
1398
  if (mergedOnToolCallStart) {
1277
1399
  await mergedOnToolCallStart({
1278
- toolCall: {
1279
- type: 'tool-call',
1280
- toolCallId: toolCall.toolCallId,
1281
- toolName: toolCall.toolName,
1282
- input: toolCall.input,
1283
- },
1400
+ toolCall: toolCallEvent,
1401
+ stepNumber: currentStepNumber,
1284
1402
  });
1285
1403
  }
1404
+
1405
+ const startTime = Date.now();
1286
1406
  let result: LanguageModelV4ToolResultPart;
1287
1407
  try {
1288
1408
  result = await executeTool(toolCall, tools, messages, context);
1289
1409
  } catch (err) {
1410
+ const durationMs = Date.now() - startTime;
1290
1411
  if (mergedOnToolCallFinish) {
1291
1412
  await mergedOnToolCallFinish({
1292
- toolCall: {
1293
- type: 'tool-call',
1294
- toolCallId: toolCall.toolCallId,
1295
- toolName: toolCall.toolName,
1296
- input: toolCall.input,
1297
- },
1413
+ toolCall: toolCallEvent,
1414
+ stepNumber: currentStepNumber,
1415
+ durationMs,
1416
+ success: false,
1298
1417
  error: err,
1299
1418
  });
1300
1419
  }
1301
1420
  throw err;
1302
1421
  }
1422
+
1423
+ const durationMs = Date.now() - startTime;
1303
1424
  if (mergedOnToolCallFinish) {
1304
1425
  const isError =
1305
1426
  result.output &&
1306
1427
  'type' in result.output &&
1307
1428
  (result.output.type === 'error-text' ||
1308
1429
  result.output.type === 'error-json');
1309
- await mergedOnToolCallFinish({
1310
- toolCall: {
1311
- type: 'tool-call',
1312
- toolCallId: toolCall.toolCallId,
1313
- toolName: toolCall.toolName,
1314
- input: toolCall.input,
1315
- },
1316
- ...(isError
1317
- ? {
1318
- error:
1319
- 'value' in result.output ? result.output.value : undefined,
1320
- }
1321
- : {
1322
- result:
1323
- result.output && 'value' in result.output
1324
- ? result.output.value
1325
- : undefined,
1326
- }),
1327
- });
1430
+ if (isError) {
1431
+ await mergedOnToolCallFinish({
1432
+ toolCall: toolCallEvent,
1433
+ stepNumber: currentStepNumber,
1434
+ durationMs,
1435
+ success: false,
1436
+ error: 'value' in result.output ? result.output.value : undefined,
1437
+ });
1438
+ } else {
1439
+ await mergedOnToolCallFinish({
1440
+ toolCall: toolCallEvent,
1441
+ stepNumber: currentStepNumber,
1442
+ durationMs,
1443
+ success: true,
1444
+ output:
1445
+ result.output && 'value' in result.output
1446
+ ? result.output.value
1447
+ : undefined,
1448
+ });
1449
+ }
1328
1450
  }
1329
1451
  return result;
1330
1452
  };
@@ -1335,7 +1457,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1335
1457
  await options.onAbort({ steps });
1336
1458
  }
1337
1459
  return {
1338
- messages: options.messages as unknown as ModelMessage[],
1460
+ messages: prompt.messages,
1339
1461
  steps,
1340
1462
  toolCalls: [],
1341
1463
  toolResults: [],
@@ -1348,7 +1470,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1348
1470
  tools: effectiveTools as ToolSet,
1349
1471
  writable: options.writable,
1350
1472
  prompt: modelPrompt,
1351
- stopConditions: options.stopWhen,
1473
+ stopConditions: options.stopWhen ?? this.stopWhen,
1352
1474
  maxSteps: options.maxSteps,
1353
1475
  onStepFinish: mergedOnStepFinish,
1354
1476
  onStepStart: mergedOnStepStart,
@@ -1361,9 +1483,11 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1361
1483
  experimental_context: experimentalContext,
1362
1484
  experimental_telemetry: effectiveTelemetry,
1363
1485
  includeRawChunks: options.includeRawChunks ?? false,
1364
- repairToolCall:
1365
- options.experimental_repairToolCall as ToolCallRepairFunction<ToolSet>,
1366
- responseFormat: await options.output?.responseFormat,
1486
+ repairToolCall: (options.experimental_repairToolCall ??
1487
+ this.experimentalRepairToolCall) as
1488
+ | ToolCallRepairFunction<ToolSet>
1489
+ | undefined,
1490
+ responseFormat: await (options.output ?? this.output)?.responseFormat,
1367
1491
  });
1368
1492
 
1369
1493
  // Track the final conversation messages from the iterator
@@ -1390,6 +1514,8 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1390
1514
  context,
1391
1515
  providerExecutedToolResults,
1392
1516
  } = result.value;
1517
+ // Capture current step number before pushing (0-based)
1518
+ const currentStepNumber = steps.length;
1393
1519
  if (step) {
1394
1520
  steps.push(step as unknown as StepResult<TTools, any>);
1395
1521
  }
@@ -1453,6 +1579,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1453
1579
  effectiveTools as ToolSet,
1454
1580
  iterMessages,
1455
1581
  experimentalContext,
1582
+ currentStepNumber,
1456
1583
  ),
1457
1584
  ),
1458
1585
  );
@@ -1553,6 +1680,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1553
1680
  effectiveTools as ToolSet,
1554
1681
  iterMessages,
1555
1682
  experimentalContext,
1683
+ currentStepNumber,
1556
1684
  ),
1557
1685
  ),
1558
1686
  );
@@ -1641,18 +1769,19 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1641
1769
  // Don't throw yet - we want to call onFinish first
1642
1770
  }
1643
1771
 
1644
- // Use the final messages from the iterator, or fall back to original messages
1772
+ // Use the final messages from the iterator, or fall back to standardized messages
1645
1773
  const messages = (finalMessages ??
1646
- options.messages) as unknown as ModelMessage[];
1774
+ prompt.messages) as unknown as ModelMessage[];
1647
1775
 
1648
- // Parse structured output if output is specified
1776
+ // Parse structured output if output is specified (stream-level overrides constructor default)
1777
+ const effectiveOutput = options.output ?? this.output;
1649
1778
  let experimentalOutput: OUTPUT = undefined as OUTPUT;
1650
- if (options.output && steps.length > 0) {
1779
+ if (effectiveOutput && steps.length > 0) {
1651
1780
  const lastStep = steps[steps.length - 1];
1652
1781
  const text = lastStep.text;
1653
1782
  if (text) {
1654
1783
  try {
1655
- experimentalOutput = await options.output.parseCompleteOutput(
1784
+ experimentalOutput = await effectiveOutput.parseCompleteOutput(
1656
1785
  { text },
1657
1786
  {
1658
1787
  response: lastStep.response,