@ai-sdk/workflow 1.0.0-beta.2 → 1.0.0-beta.20

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
  */
@@ -82,7 +92,7 @@ export type ProviderOptions = SharedV4ProviderOptions;
82
92
  /**
83
93
  * Telemetry settings for observability.
84
94
  */
85
- export interface TelemetrySettings {
95
+ export interface TelemetryOptions {
86
96
  /**
87
97
  * Enable or disable telemetry. Defaults to true.
88
98
  */
@@ -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,14 +335,11 @@ 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>;
342
- experimental_telemetry?: TelemetrySettings;
342
+ experimental_telemetry?: TelemetryOptions;
343
343
  experimental_context?: unknown;
344
344
  messages: ModelMessage[];
345
345
  }
@@ -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.
@@ -405,7 +407,7 @@ export interface WorkflowAgentOptions<
405
407
  /**
406
408
  * Optional telemetry configuration (experimental).
407
409
  */
408
- experimental_telemetry?: TelemetrySettings;
410
+ experimental_telemetry?: TelemetryOptions;
409
411
 
410
412
  /**
411
413
  * Default context that is passed into tool execution for every stream call on this 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,280 @@ 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;
593
-
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>>;
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;
617
684
 
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>>;
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>>;
625
708
 
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;
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>>;
632
716
 
633
- /**
634
- * The tool choice strategy. Default: 'auto'.
635
- * Overrides the toolChoice from the constructor if provided.
636
- */
637
- toolChoice?: ToolChoice<TTools>;
717
+ /**
718
+ * The tool choice strategy. Default: 'auto'.
719
+ * Overrides the toolChoice from the constructor if provided.
720
+ */
721
+ toolChoice?: ToolChoice<TTools>;
638
722
 
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>>;
723
+ /**
724
+ * Limits the tools that are available for the model to call without
725
+ * changing the tool call and result types in the result.
726
+ */
727
+ activeTools?: Array<keyof NoInfer<TTools>>;
644
728
 
645
- /**
646
- * Optional telemetry configuration (experimental).
647
- */
648
- experimental_telemetry?: TelemetrySettings;
729
+ /**
730
+ * Optional telemetry configuration (experimental).
731
+ */
732
+ experimental_telemetry?: TelemetryOptions;
649
733
 
650
- /**
651
- * Context that is passed into tool execution.
652
- * Experimental (can break in patch releases).
653
- * @default undefined
654
- */
655
- experimental_context?: unknown;
734
+ /**
735
+ * Context that is passed into tool execution.
736
+ * Experimental (can break in patch releases).
737
+ * @default undefined
738
+ */
739
+ experimental_context?: unknown;
656
740
 
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>;
741
+ /**
742
+ * Optional specification for parsing structured outputs from the LLM response.
743
+ * Use `Output.object({ schema })` for structured output or `Output.text()` for text output.
744
+ *
745
+ * @example
746
+ * ```typescript
747
+ * import { Output } from '@workflow/ai';
748
+ * import { z } from 'zod';
749
+ *
750
+ * const result = await agent.stream({
751
+ * messages: [...],
752
+ * writable: getWritable(),
753
+ * output: Output.object({
754
+ * schema: z.object({
755
+ * sentiment: z.enum(['positive', 'negative', 'neutral']),
756
+ * confidence: z.number(),
757
+ * }),
758
+ * }),
759
+ * });
760
+ *
761
+ * console.log(result.output); // { sentiment: 'positive', confidence: 0.95 }
762
+ * ```
763
+ */
764
+ output?: OutputSpecification<OUTPUT, PARTIAL_OUTPUT>;
681
765
 
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;
766
+ /**
767
+ * Whether to include raw chunks from the provider in the stream.
768
+ * When enabled, you will receive raw chunks with type 'raw' that contain the unprocessed data from the provider.
769
+ * This allows access to cutting-edge provider features not yet wrapped by the AI SDK.
770
+ * Defaults to false.
771
+ */
772
+ includeRawChunks?: boolean;
689
773
 
690
- /**
691
- * A function that attempts to repair a tool call that failed to parse.
692
- */
693
- experimental_repairToolCall?: ToolCallRepairFunction<TTools>;
774
+ /**
775
+ * A function that attempts to repair a tool call that failed to parse.
776
+ */
777
+ experimental_repairToolCall?: ToolCallRepairFunction<TTools>;
694
778
 
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>>;
779
+ /**
780
+ * Optional stream transformations.
781
+ * They are applied in the order they are provided.
782
+ * The stream transformations must maintain the stream structure for streamText to work correctly.
783
+ */
784
+ experimental_transform?:
785
+ | StreamTextTransform<TTools>
786
+ | Array<StreamTextTransform<TTools>>;
703
787
 
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;
788
+ /**
789
+ * Custom download function to use for URLs.
790
+ * By default, files are downloaded if the model does not support the URL for the given media type.
791
+ */
792
+ experimental_download?: DownloadFunction;
709
793
 
710
- /**
711
- * Callback function to be called after each step completes.
712
- */
713
- onStepFinish?: StreamTextOnStepFinishCallback<TTools, any>;
794
+ /**
795
+ * Callback function to be called after each step completes.
796
+ */
797
+ onStepFinish?: WorkflowAgentOnStepFinishCallback<TTools>;
714
798
 
715
- /**
716
- * Callback that is invoked when an error occurs during streaming.
717
- * You can use it to log errors.
718
- */
719
- onError?: StreamTextOnErrorCallback;
799
+ /**
800
+ * Callback that is invoked when an error occurs during streaming.
801
+ * You can use it to log errors.
802
+ */
803
+ onError?: WorkflowAgentOnErrorCallback;
720
804
 
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>;
805
+ /**
806
+ * Callback that is called when the LLM response and all request tool executions
807
+ * (for tools that have an `execute` function) are finished.
808
+ */
809
+ onFinish?: WorkflowAgentOnFinishCallback<TTools, OUTPUT>;
726
810
 
727
- /**
728
- * Callback that is called when the operation is aborted.
729
- */
730
- onAbort?: StreamTextOnAbortCallback<TTools>;
811
+ /**
812
+ * Callback that is called when the operation is aborted.
813
+ */
814
+ onAbort?: WorkflowAgentOnAbortCallback<TTools>;
731
815
 
732
- /**
733
- * Callback called when the agent starts streaming, before any LLM calls.
734
- */
735
- experimental_onStart?: WorkflowAgentOnStartCallback;
816
+ /**
817
+ * Callback called when the agent starts streaming, before any LLM calls.
818
+ */
819
+ experimental_onStart?: WorkflowAgentOnStartCallback;
736
820
 
737
- /**
738
- * Callback called before each step (LLM call) begins.
739
- */
740
- experimental_onStepStart?: WorkflowAgentOnStepStartCallback;
821
+ /**
822
+ * Callback called before each step (LLM call) begins.
823
+ */
824
+ experimental_onStepStart?: WorkflowAgentOnStepStartCallback;
741
825
 
742
- /**
743
- * Callback called before a tool's execute function runs.
744
- */
745
- experimental_onToolCallStart?: WorkflowAgentOnToolCallStartCallback;
826
+ /**
827
+ * Callback called before a tool's execute function runs.
828
+ */
829
+ experimental_onToolCallStart?: WorkflowAgentOnToolCallStartCallback;
746
830
 
747
- /**
748
- * Callback called after a tool execution completes.
749
- */
750
- experimental_onToolCallFinish?: WorkflowAgentOnToolCallFinishCallback;
831
+ /**
832
+ * Callback called after a tool execution completes.
833
+ */
834
+ experimental_onToolCallFinish?: WorkflowAgentOnToolCallFinishCallback;
751
835
 
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>;
836
+ /**
837
+ * Callback function called before each step in the agent loop.
838
+ * Use this to modify settings, manage context, or inject messages dynamically.
839
+ *
840
+ * @example
841
+ * ```typescript
842
+ * prepareStep: async ({ messages, stepNumber }) => {
843
+ * // Inject messages from a queue
844
+ * const queuedMessages = await getQueuedMessages();
845
+ * if (queuedMessages.length > 0) {
846
+ * return {
847
+ * messages: [...messages, ...queuedMessages],
848
+ * };
849
+ * }
850
+ * return {};
851
+ * }
852
+ * ```
853
+ */
854
+ prepareStep?: PrepareStepCallback<TTools>;
771
855
 
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;
856
+ /**
857
+ * Timeout in milliseconds for the stream operation.
858
+ * When specified, creates an AbortSignal that will abort the operation after the given time.
859
+ * If both `timeout` and `abortSignal` are provided, whichever triggers first will abort.
860
+ */
861
+ timeout?: number;
778
862
 
779
- /**
780
- * Whether to send a 'finish' chunk to the writable stream when streaming completes.
781
- * @default true
782
- */
783
- sendFinish?: boolean;
863
+ /**
864
+ * Whether to send a 'finish' chunk to the writable stream when streaming completes.
865
+ * @default true
866
+ */
867
+ sendFinish?: boolean;
784
868
 
785
- /**
786
- * Whether to prevent the writable stream from being closed after streaming completes.
787
- * @default false
788
- */
789
- preventClose?: boolean;
790
- }
869
+ /**
870
+ * Whether to prevent the writable stream from being closed after streaming completes.
871
+ * @default false
872
+ */
873
+ preventClose?: boolean;
874
+ };
791
875
 
792
876
  /**
793
877
  * A tool call made by the model. Matches the AI SDK's tool call shape.
@@ -898,10 +982,12 @@ export interface WorkflowAgentStreamResult<
898
982
  * ```
899
983
  */
900
984
  export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
901
- private model:
902
- | string
903
- | CompatibleLanguageModel
904
- | (() => Promise<CompatibleLanguageModel>);
985
+ /**
986
+ * The id of the agent.
987
+ */
988
+ public readonly id: string | undefined;
989
+
990
+ private model: LanguageModel;
905
991
  /**
906
992
  * The tool set configured for this agent.
907
993
  */
@@ -912,14 +998,18 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
912
998
  | Array<SystemModelMessage>;
913
999
  private generationSettings: GenerationSettings;
914
1000
  private toolChoice?: ToolChoice<TBaseTools>;
915
- private telemetry?: TelemetrySettings;
1001
+ private telemetry?: TelemetryOptions;
916
1002
  private experimentalContext: unknown;
1003
+ private stopWhen?:
1004
+ | StopCondition<ToolSet, any>
1005
+ | Array<StopCondition<ToolSet, any>>;
1006
+ private activeTools?: Array<keyof TBaseTools>;
1007
+ private output?: OutputSpecification<any, any>;
1008
+ private experimentalRepairToolCall?: ToolCallRepairFunction<TBaseTools>;
1009
+ private experimentalDownload?: DownloadFunction;
917
1010
  private prepareStep?: PrepareStepCallback<TBaseTools>;
918
- private constructorOnStepFinish?: StreamTextOnStepFinishCallback<
919
- ToolSet,
920
- any
921
- >;
922
- private constructorOnFinish?: StreamTextOnFinishCallback<ToolSet>;
1011
+ private constructorOnStepFinish?: WorkflowAgentOnStepFinishCallback<ToolSet>;
1012
+ private constructorOnFinish?: WorkflowAgentOnFinishCallback<ToolSet>;
923
1013
  private constructorOnStart?: WorkflowAgentOnStartCallback;
924
1014
  private constructorOnStepStart?: WorkflowAgentOnStepStartCallback;
925
1015
  private constructorOnToolCallStart?: WorkflowAgentOnToolCallStartCallback;
@@ -927,6 +1017,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
927
1017
  private prepareCall?: PrepareCallCallback<TBaseTools>;
928
1018
 
929
1019
  constructor(options: WorkflowAgentOptions<TBaseTools>) {
1020
+ this.id = options.id;
930
1021
  this.model = options.model;
931
1022
  this.tools = (options.tools ?? {}) as TBaseTools;
932
1023
  // `instructions` takes precedence over deprecated `system`
@@ -934,6 +1025,11 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
934
1025
  this.toolChoice = options.toolChoice;
935
1026
  this.telemetry = options.experimental_telemetry;
936
1027
  this.experimentalContext = options.experimental_context;
1028
+ this.stopWhen = options.stopWhen;
1029
+ this.activeTools = options.activeTools;
1030
+ this.output = options.output;
1031
+ this.experimentalRepairToolCall = options.experimental_repairToolCall;
1032
+ this.experimentalDownload = options.experimental_download;
937
1033
  this.prepareStep = options.prepareStep;
938
1034
  this.constructorOnStepFinish = options.onStepFinish;
939
1035
  this.constructorOnFinish = options.onFinish;
@@ -972,12 +1068,11 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
972
1068
  options: WorkflowAgentStreamOptions<TTools, OUTPUT, PARTIAL_OUTPUT>,
973
1069
  ): Promise<WorkflowAgentStreamResult<TTools, OUTPUT>> {
974
1070
  // Call prepareCall to transform parameters before the agent loop
975
- let effectiveModel:
976
- | string
977
- | CompatibleLanguageModel
978
- | (() => Promise<CompatibleLanguageModel>) = this.model;
1071
+ let effectiveModel: LanguageModel = this.model;
979
1072
  let effectiveInstructions = options.system ?? this.instructions;
980
- let effectiveMessages = options.messages;
1073
+ let effectivePrompt: string | Array<ModelMessage> | undefined =
1074
+ options.prompt;
1075
+ let effectiveMessages: Array<ModelMessage> | undefined = options.messages;
981
1076
  let effectiveGenerationSettings = { ...this.generationSettings };
982
1077
  let effectiveExperimentalContext =
983
1078
  options.experimental_context ?? this.experimentalContext;
@@ -985,6 +1080,14 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
985
1080
  let effectiveTelemetryFromPrepare =
986
1081
  options.experimental_telemetry ?? this.telemetry;
987
1082
 
1083
+ // Resolve messages for prepareCall: use messages directly, or convert prompt
1084
+ const resolvedMessagesForPrepareCall: ModelMessage[] =
1085
+ effectiveMessages ??
1086
+ (typeof effectivePrompt === 'string'
1087
+ ? [{ role: 'user' as const, content: effectivePrompt }]
1088
+ : (effectivePrompt as ModelMessage[])) ??
1089
+ [];
1090
+
988
1091
  if (this.prepareCall) {
989
1092
  const prepared = await this.prepareCall({
990
1093
  model: effectiveModel,
@@ -993,16 +1096,17 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
993
1096
  toolChoice: effectiveToolChoiceFromPrepare as ToolChoice<TBaseTools>,
994
1097
  experimental_telemetry: effectiveTelemetryFromPrepare,
995
1098
  experimental_context: effectiveExperimentalContext,
996
- messages: effectiveMessages as ModelMessage[],
1099
+ messages: resolvedMessagesForPrepareCall,
997
1100
  ...effectiveGenerationSettings,
998
1101
  } as PrepareCallOptions<TBaseTools>);
999
1102
 
1000
1103
  if (prepared.model !== undefined) effectiveModel = prepared.model;
1001
1104
  if (prepared.instructions !== undefined)
1002
1105
  effectiveInstructions = prepared.instructions;
1003
- if (prepared.messages !== undefined)
1004
- effectiveMessages =
1005
- prepared.messages as WorkflowAgentStreamOptions<TTools>['messages'];
1106
+ if (prepared.messages !== undefined) {
1107
+ effectiveMessages = prepared.messages as Array<ModelMessage>;
1108
+ effectivePrompt = undefined; // messages from prepareCall take precedence
1109
+ }
1006
1110
  if (prepared.experimental_context !== undefined)
1007
1111
  effectiveExperimentalContext = prepared.experimental_context;
1008
1112
  if (prepared.toolChoice !== undefined)
@@ -1035,7 +1139,9 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1035
1139
 
1036
1140
  const prompt = await standardizePrompt({
1037
1141
  system: effectiveInstructions,
1038
- messages: effectiveMessages,
1142
+ ...(effectivePrompt != null
1143
+ ? { prompt: effectivePrompt }
1144
+ : { messages: effectiveMessages! }),
1039
1145
  });
1040
1146
 
1041
1147
  // Process tool approval responses before starting the agent loop.
@@ -1164,7 +1270,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1164
1270
  const modelPrompt = await convertToLanguageModelPrompt({
1165
1271
  prompt,
1166
1272
  supportedUrls: {},
1167
- download: options.experimental_download,
1273
+ download: options.experimental_download ?? this.experimentalDownload,
1168
1274
  });
1169
1275
 
1170
1276
  const effectiveAbortSignal = mergeAbortSignals(
@@ -1210,13 +1316,13 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1210
1316
  // Merge constructor + stream callbacks (constructor first, then stream)
1211
1317
  const mergedOnStepFinish = mergeCallbacks(
1212
1318
  this.constructorOnStepFinish as
1213
- | StreamTextOnStepFinishCallback<TTools, any>
1319
+ | WorkflowAgentOnStepFinishCallback<TTools>
1214
1320
  | undefined,
1215
1321
  options.onStepFinish,
1216
1322
  );
1217
1323
  const mergedOnFinish = mergeCallbacks(
1218
1324
  this.constructorOnFinish as
1219
- | StreamTextOnFinishCallback<TTools, OUTPUT>
1325
+ | WorkflowAgentOnFinishCallback<TTools, OUTPUT>
1220
1326
  | undefined,
1221
1327
  options.onFinish,
1222
1328
  );
@@ -1243,10 +1349,11 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1243
1349
  // Merge telemetry settings
1244
1350
  const effectiveTelemetry = effectiveTelemetryFromPrepare;
1245
1351
 
1246
- // Filter tools if activeTools is specified
1352
+ // Filter tools if activeTools is specified (stream-level overrides constructor default)
1353
+ const effectiveActiveTools = options.activeTools ?? this.activeTools;
1247
1354
  const effectiveTools =
1248
- options.activeTools && options.activeTools.length > 0
1249
- ? filterTools(this.tools, options.activeTools as string[])
1355
+ effectiveActiveTools && effectiveActiveTools.length > 0
1356
+ ? filterTools(this.tools, effectiveActiveTools as string[])
1250
1357
  : this.tools;
1251
1358
 
1252
1359
  // Initialize context
@@ -1262,7 +1369,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1262
1369
  if (mergedOnStart) {
1263
1370
  await mergedOnStart({
1264
1371
  model: effectiveModel,
1265
- messages: effectiveMessages as ModelMessage[],
1372
+ messages: prompt.messages,
1266
1373
  });
1267
1374
  }
1268
1375
 
@@ -1272,59 +1379,67 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1272
1379
  tools: ToolSet,
1273
1380
  messages: LanguageModelV4Prompt,
1274
1381
  context?: unknown,
1382
+ currentStepNumber: number = 0,
1275
1383
  ): Promise<LanguageModelV4ToolResultPart> => {
1384
+ const toolCallEvent: ToolCall = {
1385
+ type: 'tool-call',
1386
+ toolCallId: toolCall.toolCallId,
1387
+ toolName: toolCall.toolName,
1388
+ input: toolCall.input,
1389
+ };
1390
+
1276
1391
  if (mergedOnToolCallStart) {
1277
1392
  await mergedOnToolCallStart({
1278
- toolCall: {
1279
- type: 'tool-call',
1280
- toolCallId: toolCall.toolCallId,
1281
- toolName: toolCall.toolName,
1282
- input: toolCall.input,
1283
- },
1393
+ toolCall: toolCallEvent,
1394
+ stepNumber: currentStepNumber,
1284
1395
  });
1285
1396
  }
1397
+
1398
+ const startTime = Date.now();
1286
1399
  let result: LanguageModelV4ToolResultPart;
1287
1400
  try {
1288
1401
  result = await executeTool(toolCall, tools, messages, context);
1289
1402
  } catch (err) {
1403
+ const durationMs = Date.now() - startTime;
1290
1404
  if (mergedOnToolCallFinish) {
1291
1405
  await mergedOnToolCallFinish({
1292
- toolCall: {
1293
- type: 'tool-call',
1294
- toolCallId: toolCall.toolCallId,
1295
- toolName: toolCall.toolName,
1296
- input: toolCall.input,
1297
- },
1406
+ toolCall: toolCallEvent,
1407
+ stepNumber: currentStepNumber,
1408
+ durationMs,
1409
+ success: false,
1298
1410
  error: err,
1299
1411
  });
1300
1412
  }
1301
1413
  throw err;
1302
1414
  }
1415
+
1416
+ const durationMs = Date.now() - startTime;
1303
1417
  if (mergedOnToolCallFinish) {
1304
1418
  const isError =
1305
1419
  result.output &&
1306
1420
  'type' in result.output &&
1307
1421
  (result.output.type === 'error-text' ||
1308
1422
  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
- });
1423
+ if (isError) {
1424
+ await mergedOnToolCallFinish({
1425
+ toolCall: toolCallEvent,
1426
+ stepNumber: currentStepNumber,
1427
+ durationMs,
1428
+ success: false,
1429
+ error: 'value' in result.output ? result.output.value : undefined,
1430
+ });
1431
+ } else {
1432
+ await mergedOnToolCallFinish({
1433
+ toolCall: toolCallEvent,
1434
+ stepNumber: currentStepNumber,
1435
+ durationMs,
1436
+ success: true,
1437
+ output:
1438
+ result.output && 'value' in result.output
1439
+ ? result.output.value
1440
+ : undefined,
1441
+ });
1442
+ }
1328
1443
  }
1329
1444
  return result;
1330
1445
  };
@@ -1335,7 +1450,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1335
1450
  await options.onAbort({ steps });
1336
1451
  }
1337
1452
  return {
1338
- messages: options.messages as unknown as ModelMessage[],
1453
+ messages: prompt.messages,
1339
1454
  steps,
1340
1455
  toolCalls: [],
1341
1456
  toolResults: [],
@@ -1348,8 +1463,8 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1348
1463
  tools: effectiveTools as ToolSet,
1349
1464
  writable: options.writable,
1350
1465
  prompt: modelPrompt,
1351
- stopConditions: options.stopWhen,
1352
- maxSteps: options.maxSteps,
1466
+ stopConditions: options.stopWhen ?? this.stopWhen,
1467
+
1353
1468
  onStepFinish: mergedOnStepFinish,
1354
1469
  onStepStart: mergedOnStepStart,
1355
1470
  onError: options.onError,
@@ -1361,9 +1476,11 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1361
1476
  experimental_context: experimentalContext,
1362
1477
  experimental_telemetry: effectiveTelemetry,
1363
1478
  includeRawChunks: options.includeRawChunks ?? false,
1364
- repairToolCall:
1365
- options.experimental_repairToolCall as ToolCallRepairFunction<ToolSet>,
1366
- responseFormat: await options.output?.responseFormat,
1479
+ repairToolCall: (options.experimental_repairToolCall ??
1480
+ this.experimentalRepairToolCall) as
1481
+ | ToolCallRepairFunction<ToolSet>
1482
+ | undefined,
1483
+ responseFormat: await (options.output ?? this.output)?.responseFormat,
1367
1484
  });
1368
1485
 
1369
1486
  // Track the final conversation messages from the iterator
@@ -1390,6 +1507,8 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1390
1507
  context,
1391
1508
  providerExecutedToolResults,
1392
1509
  } = result.value;
1510
+ // Capture current step number before pushing (0-based)
1511
+ const currentStepNumber = steps.length;
1393
1512
  if (step) {
1394
1513
  steps.push(step as unknown as StepResult<TTools, any>);
1395
1514
  }
@@ -1453,6 +1572,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1453
1572
  effectiveTools as ToolSet,
1454
1573
  iterMessages,
1455
1574
  experimentalContext,
1575
+ currentStepNumber,
1456
1576
  ),
1457
1577
  ),
1458
1578
  );
@@ -1553,6 +1673,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1553
1673
  effectiveTools as ToolSet,
1554
1674
  iterMessages,
1555
1675
  experimentalContext,
1676
+ currentStepNumber,
1556
1677
  ),
1557
1678
  ),
1558
1679
  );
@@ -1641,18 +1762,19 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1641
1762
  // Don't throw yet - we want to call onFinish first
1642
1763
  }
1643
1764
 
1644
- // Use the final messages from the iterator, or fall back to original messages
1765
+ // Use the final messages from the iterator, or fall back to standardized messages
1645
1766
  const messages = (finalMessages ??
1646
- options.messages) as unknown as ModelMessage[];
1767
+ prompt.messages) as unknown as ModelMessage[];
1647
1768
 
1648
- // Parse structured output if output is specified
1769
+ // Parse structured output if output is specified (stream-level overrides constructor default)
1770
+ const effectiveOutput = options.output ?? this.output;
1649
1771
  let experimentalOutput: OUTPUT = undefined as OUTPUT;
1650
- if (options.output && steps.length > 0) {
1772
+ if (effectiveOutput && steps.length > 0) {
1651
1773
  const lastStep = steps[steps.length - 1];
1652
1774
  const text = lastStep.text;
1653
1775
  if (text) {
1654
1776
  try {
1655
- experimentalOutput = await options.output.parseCompleteOutput(
1777
+ experimentalOutput = await effectiveOutput.parseCompleteOutput(
1656
1778
  { text },
1657
1779
  {
1658
1780
  response: lastStep.response,