@ai-sdk/workflow 2.0.17 → 2.0.19

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/workflow",
3
- "version": "2.0.17",
3
+ "version": "2.0.19",
4
4
  "type": "module",
5
5
  "description": "WorkflowAgent for building AI agents with AI SDK",
6
6
  "license": "Apache-2.0",
@@ -31,9 +31,9 @@
31
31
  }
32
32
  },
33
33
  "dependencies": {
34
- "@ai-sdk/provider": "4.0.9",
35
- "@ai-sdk/provider-utils": "5.0.34",
36
- "ai": "7.0.87",
34
+ "@ai-sdk/provider": "4.0.10",
35
+ "@ai-sdk/provider-utils": "5.0.35",
36
+ "ai": "7.0.89",
37
37
  "ajv": "^8.20.0"
38
38
  },
39
39
  "devDependencies": {
@@ -7,6 +7,7 @@ import type {
7
7
  import type { Context } from '@ai-sdk/provider-utils';
8
8
  import {
9
9
  experimental_filterActiveTools as filterActiveTools,
10
+ type ActiveTools,
10
11
  type Experimental_SandboxSession as SandboxSession,
11
12
  type Instructions,
12
13
  type LanguageModel,
@@ -164,7 +165,7 @@ export async function* streamTextIterator({
164
165
  let currentRuntimeContext: Context = runtimeContext ?? {};
165
166
  let currentToolsContext: Record<string, Context | undefined> =
166
167
  toolsContext ?? {};
167
- let currentActiveTools: string[] | undefined;
168
+ let currentActiveTools: ActiveTools<ToolSet>;
168
169
 
169
170
  const steps: StepResult<any, any>[] = [];
170
171
  let done = false;
@@ -297,7 +298,7 @@ export async function* streamTextIterator({
297
298
  try {
298
299
  // Filter tools if activeTools is specified
299
300
  const effectiveTools =
300
- currentActiveTools && currentActiveTools.length > 0
301
+ currentActiveTools !== undefined
301
302
  ? (filterActiveTools({
302
303
  tools,
303
304
  activeTools: currentActiveTools,
@@ -28,6 +28,7 @@ import {
28
28
  type ToolCallRepairFunction,
29
29
  type ToolChoice,
30
30
  type ToolSet,
31
+ type InferUITools,
31
32
  type UIMessage,
32
33
  type LanguageModel,
33
34
  type Prompt,
@@ -40,6 +41,7 @@ import {
40
41
  createRestrictedTelemetryDispatcher,
41
42
  collectToolApprovals,
42
43
  convertToLanguageModelPrompt,
44
+ mergeAbortSignals,
43
45
  mergeCallbacks,
44
46
  signToolApproval,
45
47
  standardizePrompt,
@@ -84,9 +86,13 @@ export type InferWorkflowAgentTools<WORKFLOW_AGENT> =
84
86
  * Infer the UI message type of a workflow agent.
85
87
  */
86
88
  export type InferWorkflowAgentUIMessage<
87
- _WORKFLOW_AGENT,
89
+ WORKFLOW_AGENT,
88
90
  MESSAGE_METADATA = unknown,
89
- > = UIMessage<MESSAGE_METADATA>;
91
+ > = UIMessage<
92
+ MESSAGE_METADATA,
93
+ never,
94
+ InferUITools<InferWorkflowAgentTools<WORKFLOW_AGENT>>
95
+ >;
90
96
 
91
97
  /**
92
98
  * Re-export the Output helper for structured output specifications.
@@ -349,7 +355,7 @@ export interface PrepareStepResult<
349
355
  * Override the active tools for this step.
350
356
  * Limits the tools that are available for the model to call.
351
357
  */
352
- activeTools?: string[];
358
+ activeTools?: ActiveTools<NoInfer<TTools>>;
353
359
 
354
360
  /**
355
361
  * Updated runtime context for the current and subsequent steps.
@@ -605,6 +611,13 @@ export type WorkflowAgentOptions<
605
611
  /**
606
612
  * Callback called when the agent starts streaming, before any LLM calls.
607
613
  */
614
+ onStart?: WorkflowAgentOnStartCallback<TTools, TRuntimeContext>;
615
+
616
+ /**
617
+ * Callback called when the agent starts streaming, before any LLM calls.
618
+ *
619
+ * @deprecated Use `onStart` instead.
620
+ */
608
621
  experimental_onStart?: WorkflowAgentOnStartCallback<
609
622
  TTools,
610
623
  TRuntimeContext
@@ -613,6 +626,13 @@ export type WorkflowAgentOptions<
613
626
  /**
614
627
  * Callback called before each step (LLM call) begins.
615
628
  */
629
+ onStepStart?: WorkflowAgentOnStepStartCallback<TTools, TRuntimeContext>;
630
+
631
+ /**
632
+ * Callback called before each step (LLM call) begins.
633
+ *
634
+ * @deprecated Use `onStepStart` instead.
635
+ */
616
636
  experimental_onStepStart?: WorkflowAgentOnStepStartCallback<
617
637
  TTools,
618
638
  TRuntimeContext
@@ -1080,6 +1100,13 @@ export type WorkflowAgentStreamOptions<
1080
1100
  /**
1081
1101
  * Callback called when the agent starts streaming, before any LLM calls.
1082
1102
  */
1103
+ onStart?: WorkflowAgentOnStartCallback<TTools, TRuntimeContext>;
1104
+
1105
+ /**
1106
+ * Callback called when the agent starts streaming, before any LLM calls.
1107
+ *
1108
+ * @deprecated Use `onStart` instead.
1109
+ */
1083
1110
  experimental_onStart?: WorkflowAgentOnStartCallback<
1084
1111
  TTools,
1085
1112
  TRuntimeContext
@@ -1088,6 +1115,13 @@ export type WorkflowAgentStreamOptions<
1088
1115
  /**
1089
1116
  * Callback called before each step (LLM call) begins.
1090
1117
  */
1118
+ onStepStart?: WorkflowAgentOnStepStartCallback<TTools, TRuntimeContext>;
1119
+
1120
+ /**
1121
+ * Callback called before each step (LLM call) begins.
1122
+ *
1123
+ * @deprecated Use `onStepStart` instead.
1124
+ */
1091
1125
  experimental_onStepStart?: WorkflowAgentOnStepStartCallback<
1092
1126
  TTools,
1093
1127
  TRuntimeContext
@@ -1350,8 +1384,9 @@ export class WorkflowAgent<
1350
1384
  this.constructorOnStepEnd = options.onStepEnd ?? options.onStepFinish;
1351
1385
  const { onFinish, onEnd = onFinish } = options;
1352
1386
  this.constructorOnEnd = onEnd;
1353
- this.constructorOnStart = options.experimental_onStart;
1354
- this.constructorOnStepStart = options.experimental_onStepStart;
1387
+ this.constructorOnStart = options.onStart ?? options.experimental_onStart;
1388
+ this.constructorOnStepStart =
1389
+ options.onStepStart ?? options.experimental_onStepStart;
1355
1390
  this.constructorOnToolExecutionStart = options.onToolExecutionStart;
1356
1391
  this.constructorOnToolExecutionEnd = options.onToolExecutionEnd;
1357
1392
  this.prepareCall = options.prepareCall;
@@ -1518,6 +1553,20 @@ export class WorkflowAgent<
1518
1553
  } as Prompt);
1519
1554
  const download = effectiveDownloadFromPrepare;
1520
1555
  const sandbox = options.experimental_sandbox ?? this.experimentalSandbox;
1556
+ const effectiveAbortSignal = mergeAbortSignals(
1557
+ options.abortSignal ?? effectiveGenerationSettings.abortSignal,
1558
+ options.timeout,
1559
+ );
1560
+ const timeoutAt =
1561
+ options.timeout == null ? undefined : Date.now() + options.timeout;
1562
+ const mergedOnToolExecutionStart = mergeCallbacks(
1563
+ this.constructorOnToolExecutionStart,
1564
+ options.onToolExecutionStart,
1565
+ );
1566
+ const mergedOnToolExecutionEnd = mergeCallbacks(
1567
+ this.constructorOnToolExecutionEnd,
1568
+ options.onToolExecutionEnd,
1569
+ );
1521
1570
 
1522
1571
  // Process tool approval responses before starting the agent loop.
1523
1572
  // This mirrors how stream-text.ts handles tool-approval-response parts:
@@ -1663,110 +1712,25 @@ export class WorkflowAgent<
1663
1712
  continue;
1664
1713
  }
1665
1714
 
1666
- try {
1667
- const { execute } = tool;
1668
- const resolvedContext = await resolveToolContext({
1669
- toolName: approval.toolName,
1670
- tool,
1671
- toolsContext: effectiveToolsContext,
1672
- });
1673
- const toolCallEvent: ToolCall = {
1674
- type: 'tool-call',
1675
- toolCallId: approval.toolCallId,
1676
- toolName: approval.toolName,
1677
- input: approval.input,
1678
- };
1679
- const messages = prompt.messages as unknown as ModelMessage[];
1680
- await telemetryDispatcher.onToolExecutionStart?.({
1681
- toolCall: toolCallEvent,
1682
- stepNumber: 0,
1683
- messages,
1684
- toolContext: resolvedContext,
1685
- });
1686
- const startTime = Date.now();
1687
- const executeApprovedTool = () =>
1688
- execute(approval.input, {
1689
- toolCallId: approval.toolCallId,
1690
- messages: [],
1691
- context: resolvedContext,
1692
- experimental_sandbox: sandbox,
1693
- });
1694
- const toolResult =
1695
- telemetryDispatcher.executeTool != null
1696
- ? await telemetryDispatcher.executeTool({
1697
- callId: 'workflow-agent',
1698
- toolCallId: approval.toolCallId,
1699
- execute: executeApprovedTool,
1700
- })
1701
- : await executeApprovedTool();
1702
- await telemetryDispatcher.onToolExecutionEnd?.({
1703
- toolCall: toolCallEvent,
1704
- stepNumber: 0,
1705
- durationMs: Date.now() - startTime,
1706
- messages,
1707
- toolContext: resolvedContext,
1708
- success: true,
1709
- output: toolResult,
1710
- });
1711
- toolResultContent.push({
1712
- type: 'tool-result' as const,
1713
- toolCallId: approval.toolCallId,
1714
- toolName: approval.toolName,
1715
- output: await createLanguageModelToolResultOutput({
1716
- toolCallId: approval.toolCallId,
1717
- toolName: approval.toolName,
1718
- input: approval.input,
1719
- output: toolResult,
1720
- tool,
1721
- errorMode: 'none',
1722
- supportedUrls: {},
1723
- download,
1724
- }),
1725
- });
1726
- approvedRawResults.push({
1727
- toolCallId: approval.toolCallId,
1728
- toolName: approval.toolName,
1729
- input: approval.input,
1730
- output: toolResult,
1731
- });
1732
- } catch (error) {
1733
- const errorMessage = getErrorMessage(error);
1734
- await telemetryDispatcher.onToolExecutionEnd?.({
1735
- toolCall: {
1736
- type: 'tool-call',
1737
- toolCallId: approval.toolCallId,
1738
- toolName: approval.toolName,
1739
- input: approval.input,
1740
- },
1741
- stepNumber: 0,
1742
- durationMs: 0,
1743
- messages: prompt.messages as unknown as ModelMessage[],
1744
- toolContext: undefined,
1745
- success: false,
1746
- error,
1747
- });
1748
- toolResultContent.push({
1749
- type: 'tool-result' as const,
1750
- toolCallId: approval.toolCallId,
1751
- toolName: approval.toolName,
1752
- output: await createLanguageModelToolResultOutput({
1753
- toolCallId: approval.toolCallId,
1754
- toolName: approval.toolName,
1755
- input: approval.input,
1756
- output: errorMessage,
1757
- tool,
1758
- errorMode: 'text',
1759
- supportedUrls: {},
1760
- download,
1761
- }),
1762
- });
1763
- approvedRawResults.push({
1715
+ const result = await executeToolWithCallbacks(
1716
+ {
1764
1717
  toolCallId: approval.toolCallId,
1765
1718
  toolName: approval.toolName,
1766
1719
  input: approval.input,
1767
- output: errorMessage,
1768
- });
1769
- }
1720
+ },
1721
+ this.tools as ToolSet,
1722
+ prompt.messages as unknown as LanguageModelV4Prompt,
1723
+ effectiveToolsContext,
1724
+ 0,
1725
+ sandbox,
1726
+ );
1727
+ toolResultContent.push(result.modelResult);
1728
+ approvedRawResults.push({
1729
+ toolCallId: approval.toolCallId,
1730
+ toolName: approval.toolName,
1731
+ input: approval.input,
1732
+ output: result.rawOutput,
1733
+ });
1770
1734
  }
1771
1735
  }
1772
1736
 
@@ -1854,11 +1818,6 @@ export class WorkflowAgent<
1854
1818
  download,
1855
1819
  });
1856
1820
 
1857
- const effectiveAbortSignal =
1858
- options.abortSignal ?? effectiveGenerationSettings.abortSignal;
1859
- const timeoutAt =
1860
- options.timeout == null ? undefined : Date.now() + options.timeout;
1861
-
1862
1821
  // Merge generation settings: constructor defaults < prepareCall < stream options
1863
1822
  const mergedGenerationSettings: GenerationSettings = {
1864
1823
  ...effectiveGenerationSettings,
@@ -1918,30 +1877,21 @@ export class WorkflowAgent<
1918
1877
  this.constructorOnStart as
1919
1878
  | WorkflowAgentOnStartCallback<TTools, TRuntimeContext>
1920
1879
  | undefined,
1921
- options.experimental_onStart,
1880
+ options.onStart ?? options.experimental_onStart,
1922
1881
  );
1923
1882
  const mergedOnStepStart = mergeCallbacks(
1924
1883
  this.constructorOnStepStart as
1925
1884
  | WorkflowAgentOnStepStartCallback<TTools, TRuntimeContext>
1926
1885
  | undefined,
1927
- options.experimental_onStepStart,
1886
+ options.onStepStart ?? options.experimental_onStepStart,
1928
1887
  );
1929
- const mergedOnToolExecutionStart = mergeCallbacks(
1930
- this.constructorOnToolExecutionStart,
1931
- options.onToolExecutionStart,
1932
- );
1933
- const mergedOnToolExecutionEnd = mergeCallbacks(
1934
- this.constructorOnToolExecutionEnd,
1935
- options.onToolExecutionEnd,
1936
- );
1937
-
1938
1888
  // Determine effective tool choice
1939
1889
  const effectiveToolChoice = effectiveToolChoiceFromPrepare;
1940
1890
 
1941
1891
  // Filter tools if activeTools is specified (stream-level overrides constructor default)
1942
1892
  const effectiveActiveTools = effectiveActiveToolsFromPrepare;
1943
1893
  const effectiveTools =
1944
- effectiveActiveTools && effectiveActiveTools.length > 0
1894
+ effectiveActiveTools !== undefined
1945
1895
  ? (filterActiveTools({
1946
1896
  tools: this.tools,
1947
1897
  activeTools: effectiveActiveTools,
@@ -1998,14 +1948,14 @@ export class WorkflowAgent<
1998
1948
  });
1999
1949
 
2000
1950
  // Helper to wrap executeTool with onToolExecutionStart/onToolExecutionEnd callbacks
2001
- const executeToolWithCallbacks = async (
1951
+ async function executeToolWithCallbacks(
2002
1952
  toolCall: { toolCallId: string; toolName: string; input: unknown },
2003
1953
  tools: ToolSet,
2004
1954
  messages: LanguageModelV4Prompt,
2005
1955
  perToolContexts: Record<string, Context | undefined>,
2006
1956
  currentStepNumber: number = 0,
2007
1957
  stepSandbox?: SandboxSession,
2008
- ): Promise<WorkflowToolExecutionResult> => {
1958
+ ): Promise<WorkflowToolExecutionResult> {
2009
1959
  const toolCallEvent: ToolCall = {
2010
1960
  type: 'tool-call',
2011
1961
  toolCallId: toolCall.toolCallId,
@@ -2051,6 +2001,7 @@ export class WorkflowAgent<
2051
2001
  tools,
2052
2002
  messages,
2053
2003
  resolvedContext,
2004
+ effectiveAbortSignal,
2054
2005
  download,
2055
2006
  stepSandbox,
2056
2007
  );
@@ -2145,7 +2096,7 @@ export class WorkflowAgent<
2145
2096
  });
2146
2097
  }
2147
2098
  return result;
2148
- };
2099
+ }
2149
2100
 
2150
2101
  const recordProviderExecutedToolTelemetry = async (
2151
2102
  toolCall: { toolCallId: string; toolName: string; input: unknown },
@@ -2437,7 +2388,18 @@ export class WorkflowAgent<
2437
2388
  // so useChat can show the approval UI
2438
2389
  if (options.writable) {
2439
2390
  if (allToolResults.length > 0) {
2440
- await writeToolResults(options.writable, allToolResults);
2391
+ await writeToolResults(
2392
+ options.writable,
2393
+ executedResults.map(r => ({
2394
+ toolCallId: r.modelResult.toolCallId,
2395
+ toolName: r.modelResult.toolName,
2396
+ input: toolCalls.find(
2397
+ tc => tc.toolCallId === r.modelResult.toolCallId,
2398
+ )?.input,
2399
+ output: r.rawOutput,
2400
+ isError: r.isError,
2401
+ })),
2402
+ );
2441
2403
  }
2442
2404
 
2443
2405
  const approvalToolCalls = pausedToolCalls.filter((_, i) => {
@@ -2562,9 +2524,9 @@ export class WorkflowAgent<
2562
2524
  return [];
2563
2525
  });
2564
2526
 
2565
- // Write tool results and step boundaries to the stream so the
2566
- // UI can transition tool parts to output-available state and
2567
- // properly separate multi-step model calls in the message history.
2527
+ // Write tool results and step boundaries to the stream so the UI can
2528
+ // transition tool parts to the appropriate output state and properly
2529
+ // separate multi-step model calls in the message history.
2568
2530
  if (options.writable) {
2569
2531
  await writeToolResults(
2570
2532
  options.writable,
@@ -2575,6 +2537,7 @@ export class WorkflowAgent<
2575
2537
  tc => tc.toolCallId === r.modelResult.toolCallId,
2576
2538
  )?.input,
2577
2539
  output: r.rawOutput,
2540
+ isError: r.isError,
2578
2541
  })),
2579
2542
  true,
2580
2543
  );
@@ -2937,6 +2900,7 @@ async function writeToolResults(
2937
2900
  toolName: string;
2938
2901
  input: unknown;
2939
2902
  output: unknown;
2903
+ isError: boolean;
2940
2904
  }>,
2941
2905
  writeStepBoundary = false,
2942
2906
  ) {
@@ -2944,13 +2908,23 @@ async function writeToolResults(
2944
2908
  const writer = writable.getWriter();
2945
2909
  try {
2946
2910
  for (const r of results) {
2947
- await writer.write({
2948
- type: 'tool-result',
2949
- toolCallId: r.toolCallId,
2950
- toolName: r.toolName,
2951
- input: r.input,
2952
- output: r.output,
2953
- });
2911
+ await writer.write(
2912
+ r.isError
2913
+ ? {
2914
+ type: 'tool-error',
2915
+ toolCallId: r.toolCallId,
2916
+ toolName: r.toolName,
2917
+ input: r.input,
2918
+ error: r.output,
2919
+ }
2920
+ : {
2921
+ type: 'tool-result',
2922
+ toolCallId: r.toolCallId,
2923
+ toolName: r.toolName,
2924
+ input: r.input,
2925
+ output: r.output,
2926
+ },
2927
+ );
2954
2928
  }
2955
2929
  if (writeStepBoundary) {
2956
2930
  // Emit step boundaries so the UI message history properly separates
@@ -3128,6 +3102,7 @@ async function executeTool(
3128
3102
  tools: ToolSet,
3129
3103
  messages: LanguageModelV4Prompt,
3130
3104
  context?: unknown,
3105
+ abortSignal?: AbortSignal,
3131
3106
  download?: DownloadFunction,
3132
3107
  sandbox?: SandboxSession,
3133
3108
  ): Promise<WorkflowToolExecutionResult> {
@@ -3154,6 +3129,8 @@ async function executeTool(
3154
3129
  toolCallId: toolCall.toolCallId,
3155
3130
  // Pass the conversation messages to the tool so it has context about the conversation
3156
3131
  messages,
3132
+ // Pass the effective agent signal so in-flight tool work can cooperatively cancel
3133
+ abortSignal,
3157
3134
  // Pass per-tool context to the tool (resolved from `toolsContext`)
3158
3135
  context,
3159
3136
  experimental_sandbox: sandbox,