@ai-sdk/workflow 1.0.0-beta.24 → 1.0.0-beta.27

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": "1.0.0-beta.24",
3
+ "version": "1.0.0-beta.27",
4
4
  "description": "WorkflowAgent for building AI agents with AI SDK",
5
5
  "license": "Apache-2.0",
6
6
  "main": "./dist/index.js",
@@ -27,9 +27,9 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "ajv": "^8.18.0",
30
+ "@ai-sdk/provider-utils": "5.0.0-beta.27",
30
31
  "@ai-sdk/provider": "4.0.0-beta.12",
31
- "@ai-sdk/provider-utils": "5.0.0-beta.25",
32
- "ai": "7.0.0-beta.109"
32
+ "ai": "7.0.0-beta.112"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/node": "20.17.24",
@@ -47,7 +47,8 @@
47
47
  "node": ">=18"
48
48
  },
49
49
  "publishConfig": {
50
- "access": "public"
50
+ "access": "public",
51
+ "provenance": true
51
52
  },
52
53
  "homepage": "https://ai-sdk.dev/docs",
53
54
  "repository": {
@@ -54,7 +54,7 @@ export interface DoStreamStepOptions {
54
54
  providerOptions?: ProviderOptions;
55
55
  toolChoice?: ToolChoice<ToolSet>;
56
56
  includeRawChunks?: boolean;
57
- experimental_telemetry?: TelemetryOptions;
57
+ telemetry?: TelemetryOptions;
58
58
  repairToolCall?: ToolCallRepairFunction<ToolSet>;
59
59
  responseFormat?: LanguageModelV4CallOptions['responseFormat'];
60
60
  }
@@ -115,6 +115,7 @@ export async function doStreamStep(
115
115
  // pre-converted LanguageModelV4Prompt. standardizePrompt inside
116
116
  // streamModelCall handles both formats.
117
117
  messages: conversationPrompt as unknown as ModelMessage[],
118
+ allowSystemInMessages: true,
118
119
  tools,
119
120
  toolChoice: options?.toolChoice,
120
121
  includeRawChunks: options?.includeRawChunks,
@@ -23,6 +23,8 @@ export type SerializableToolDef = {
23
23
  inputSchema: JSONSchema7;
24
24
  /** Present on provider tools (e.g. anthropic.tools.webSearch). */
25
25
  type?: 'provider';
26
+ /** Provider tool is executed by the provider. */
27
+ isProviderExecuted?: boolean;
26
28
  /** Provider tool ID, e.g. 'anthropic.web_search_20250305'. */
27
29
  id?: `${string}.${string}`;
28
30
  /** Provider tool configuration args (maxUses, allowedDomains, etc.). */
@@ -49,6 +51,7 @@ export function serializeToolSet(
49
51
  // them as provider-executed tools (e.g. anthropic webSearch).
50
52
  if ((t as any).type === 'provider') {
51
53
  def.type = 'provider';
54
+ def.isProviderExecuted = (t as any).isProviderExecuted ?? false;
52
55
  def.id = (t as any).id;
53
56
  def.args = (t as any).args;
54
57
  }
@@ -81,6 +84,7 @@ export function resolveSerializableTools(
81
84
  type: 'provider' as const,
82
85
  id: t.id!,
83
86
  args: t.args ?? {},
87
+ isProviderExecuted: t.isProviderExecuted ?? false,
84
88
  inputSchema: jsonSchema(t.inputSchema),
85
89
  }),
86
90
  ];
@@ -63,7 +63,7 @@ export async function* streamTextIterator({
63
63
  generationSettings,
64
64
  toolChoice,
65
65
  experimental_context,
66
- experimental_telemetry,
66
+ telemetry,
67
67
  includeRawChunks = false,
68
68
  repairToolCall,
69
69
  responseFormat,
@@ -80,7 +80,7 @@ export async function* streamTextIterator({
80
80
  generationSettings?: GenerationSettings;
81
81
  toolChoice?: ToolChoice<ToolSet>;
82
82
  experimental_context?: unknown;
83
- experimental_telemetry?: TelemetryOptions;
83
+ telemetry?: TelemetryOptions;
84
84
  includeRawChunks?: boolean;
85
85
  repairToolCall?: ToolCallRepairFunction<ToolSet>;
86
86
  responseFormat?: LanguageModelV4CallOptions['responseFormat'];
@@ -261,7 +261,7 @@ export async function* streamTextIterator({
261
261
  ...currentGenerationSettings,
262
262
  toolChoice: currentToolChoice,
263
263
  includeRawChunks,
264
- experimental_telemetry,
264
+ telemetry,
265
265
  repairToolCall,
266
266
  responseFormat,
267
267
  },
@@ -14,10 +14,12 @@ import {
14
14
  type Experimental_LanguageModelStreamPart as ModelCallStreamPart,
15
15
  type ModelMessage,
16
16
  Output,
17
+ Prompt,
17
18
  type StepResult,
18
19
  type StopCondition,
19
- type StreamTextOnStepFinishCallback,
20
+ type GenerateTextOnStepFinishCallback,
20
21
  type SystemModelMessage,
22
+ type ActiveTools,
21
23
  type ToolCallRepairFunction,
22
24
  type ToolChoice,
23
25
  type ToolSet,
@@ -37,12 +39,12 @@ export type { CompatibleLanguageModel } from './types.js';
37
39
 
38
40
  /**
39
41
  * Callback function to be called after each step completes.
40
- * Alias for the AI SDK's StreamTextOnStepFinishCallback, using
42
+ * Alias for the AI SDK's GenerateTextOnStepFinishCallback, using
41
43
  * WorkflowAgent-consistent naming.
42
44
  */
43
45
  export type WorkflowAgentOnStepFinishCallback<
44
46
  TTools extends ToolSet = ToolSet,
45
- > = StreamTextOnStepFinishCallback<TTools, any>;
47
+ > = GenerateTextOnStepFinishCallback<TTools, any>;
46
48
 
47
49
  /**
48
50
  * Infer the type of the tools of a workflow agent.
@@ -339,6 +341,10 @@ export interface PrepareCallOptions<
339
341
  tools: TTools;
340
342
  instructions?: string | SystemModelMessage | Array<SystemModelMessage>;
341
343
  toolChoice?: ToolChoice<TTools>;
344
+ telemetry?: TelemetryOptions;
345
+ /**
346
+ * @deprecated Use `telemetry` instead. This alias will be removed in a future major release.
347
+ */
342
348
  experimental_telemetry?: TelemetryOptions;
343
349
  experimental_context?: unknown;
344
350
  messages: ModelMessage[];
@@ -405,7 +411,14 @@ export interface WorkflowAgentOptions<
405
411
  toolChoice?: ToolChoice<TTools>;
406
412
 
407
413
  /**
408
- * Optional telemetry configuration (experimental).
414
+ * Optional telemetry configuration.
415
+ */
416
+ telemetry?: TelemetryOptions;
417
+
418
+ /**
419
+ * Optional telemetry configuration.
420
+ *
421
+ * @deprecated Use `telemetry` instead. This alias will be removed in a future major release.
409
422
  */
410
423
  experimental_telemetry?: TelemetryOptions;
411
424
 
@@ -434,7 +447,7 @@ export interface WorkflowAgentOptions<
434
447
  *
435
448
  * Per-stream `activeTools` values passed to `stream()` override this default.
436
449
  */
437
- activeTools?: Array<keyof NoInfer<TTools>>;
450
+ activeTools?: ActiveTools<NoInfer<TTools>>;
438
451
 
439
452
  /**
440
453
  * Default output specification for structured outputs.
@@ -724,10 +737,17 @@ export type WorkflowAgentStreamOptions<
724
737
  * Limits the tools that are available for the model to call without
725
738
  * changing the tool call and result types in the result.
726
739
  */
727
- activeTools?: Array<keyof NoInfer<TTools>>;
740
+ activeTools?: ActiveTools<NoInfer<TTools>>;
728
741
 
729
742
  /**
730
- * Optional telemetry configuration (experimental).
743
+ * Optional telemetry configuration.
744
+ */
745
+ telemetry?: TelemetryOptions;
746
+
747
+ /**
748
+ * Optional telemetry configuration.
749
+ *
750
+ * @deprecated Use `telemetry` instead. This alias will be removed in a future major release.
731
751
  */
732
752
  experimental_telemetry?: TelemetryOptions;
733
753
 
@@ -1003,7 +1023,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1003
1023
  private stopWhen?:
1004
1024
  | StopCondition<ToolSet, any>
1005
1025
  | Array<StopCondition<ToolSet, any>>;
1006
- private activeTools?: Array<keyof TBaseTools>;
1026
+ private activeTools?: ActiveTools<TBaseTools>;
1007
1027
  private output?: OutputSpecification<any, any>;
1008
1028
  private experimentalRepairToolCall?: ToolCallRepairFunction<TBaseTools>;
1009
1029
  private experimentalDownload?: DownloadFunction;
@@ -1023,7 +1043,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1023
1043
  // `instructions` takes precedence over deprecated `system`
1024
1044
  this.instructions = options.instructions ?? options.system;
1025
1045
  this.toolChoice = options.toolChoice;
1026
- this.telemetry = options.experimental_telemetry;
1046
+ this.telemetry = options.telemetry ?? options.experimental_telemetry;
1027
1047
  this.experimentalContext = options.experimental_context;
1028
1048
  this.stopWhen = options.stopWhen;
1029
1049
  this.activeTools = options.activeTools;
@@ -1080,7 +1100,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1080
1100
  options.experimental_context ?? this.experimentalContext;
1081
1101
  let effectiveToolChoiceFromPrepare = options.toolChoice ?? this.toolChoice;
1082
1102
  let effectiveTelemetryFromPrepare =
1083
- options.experimental_telemetry ?? this.telemetry;
1103
+ options.telemetry ?? options.experimental_telemetry ?? this.telemetry;
1084
1104
 
1085
1105
  // Resolve messages for prepareCall: use messages directly, or convert prompt
1086
1106
  const resolvedMessagesForPrepareCall: ModelMessage[] =
@@ -1096,6 +1116,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1096
1116
  tools: this.tools,
1097
1117
  instructions: effectiveInstructions,
1098
1118
  toolChoice: effectiveToolChoiceFromPrepare as ToolChoice<TBaseTools>,
1119
+ telemetry: effectiveTelemetryFromPrepare,
1099
1120
  experimental_telemetry: effectiveTelemetryFromPrepare,
1100
1121
  experimental_context: effectiveExperimentalContext,
1101
1122
  messages: resolvedMessagesForPrepareCall,
@@ -1114,7 +1135,9 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1114
1135
  if (prepared.toolChoice !== undefined)
1115
1136
  effectiveToolChoiceFromPrepare =
1116
1137
  prepared.toolChoice as ToolChoice<TBaseTools>;
1117
- if (prepared.experimental_telemetry !== undefined)
1138
+ if (prepared.telemetry !== undefined)
1139
+ effectiveTelemetryFromPrepare = prepared.telemetry;
1140
+ else if (prepared.experimental_telemetry !== undefined)
1118
1141
  effectiveTelemetryFromPrepare = prepared.experimental_telemetry;
1119
1142
  if (prepared.maxOutputTokens !== undefined)
1120
1143
  effectiveGenerationSettings.maxOutputTokens = prepared.maxOutputTokens;
@@ -1141,10 +1164,11 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1141
1164
 
1142
1165
  const prompt = await standardizePrompt({
1143
1166
  system: effectiveInstructions,
1167
+ allowSystemInMessages: true, // TODO: consider exposing this as a parameter
1144
1168
  ...(effectivePrompt != null
1145
1169
  ? { prompt: effectivePrompt }
1146
1170
  : { messages: effectiveMessages! }),
1147
- });
1171
+ } as Prompt);
1148
1172
 
1149
1173
  // Process tool approval responses before starting the agent loop.
1150
1174
  // This mirrors how stream-text.ts handles tool-approval-response parts:
@@ -1277,9 +1301,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1277
1301
 
1278
1302
  const effectiveAbortSignal = mergeAbortSignals(
1279
1303
  options.abortSignal ?? effectiveGenerationSettings.abortSignal,
1280
- options.timeout != null
1281
- ? AbortSignal.timeout(options.timeout)
1282
- : undefined,
1304
+ options.timeout,
1283
1305
  );
1284
1306
 
1285
1307
  // Merge generation settings: constructor defaults < prepareCall < stream options
@@ -1357,7 +1379,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1357
1379
  effectiveActiveTools && effectiveActiveTools.length > 0
1358
1380
  ? (filterActiveTools({
1359
1381
  tools: this.tools,
1360
- activeTools: effectiveActiveTools as string[],
1382
+ activeTools: effectiveActiveTools,
1361
1383
  }) ?? this.tools)
1362
1384
  : this.tools;
1363
1385
 
@@ -1479,7 +1501,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1479
1501
  generationSettings: mergedGenerationSettings,
1480
1502
  toolChoice: effectiveToolChoice as ToolChoice<ToolSet>,
1481
1503
  experimental_context: experimentalContext,
1482
- experimental_telemetry: effectiveTelemetry,
1504
+ telemetry: effectiveTelemetry,
1483
1505
  includeRawChunks: options.includeRawChunks ?? false,
1484
1506
  repairToolCall: (options.experimental_repairToolCall ??
1485
1507
  this.experimentalRepairToolCall) as
@@ -1523,11 +1545,16 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1523
1545
 
1524
1546
  // Only execute tools if there are tool calls
1525
1547
  if (toolCalls.length > 0) {
1548
+ const invalidToolCalls = toolCalls.filter(tc => tc.invalid === true);
1549
+ const validToolCalls = toolCalls.filter(tc => tc.invalid !== true);
1550
+
1526
1551
  // Separate provider-executed tool calls from client-executed ones
1527
- const nonProviderToolCalls = toolCalls.filter(
1552
+ const nonProviderToolCalls = validToolCalls.filter(
1528
1553
  tc => !tc.providerExecuted,
1529
1554
  );
1530
- const providerToolCalls = toolCalls.filter(tc => tc.providerExecuted);
1555
+ const providerToolCalls = validToolCalls.filter(
1556
+ tc => tc.providerExecuted,
1557
+ );
1531
1558
 
1532
1559
  // Check which tools need approval (can be async)
1533
1560
  const approvalNeeded = await Promise.all(
@@ -1591,7 +1618,15 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1591
1618
  ),
1592
1619
  );
1593
1620
 
1594
- const resolvedResults = [...executableResults, ...providerResults];
1621
+ const continuationInvalidResults = invalidToolCalls.map(
1622
+ createInvalidToolResult,
1623
+ );
1624
+ const resolvedResults = [
1625
+ ...executableResults,
1626
+ ...providerResults,
1627
+ ...continuationInvalidResults,
1628
+ ];
1629
+ const executedResults = [...executableResults, ...providerResults];
1595
1630
 
1596
1631
  const allToolCalls: ToolCall[] = toolCalls.map(tc => ({
1597
1632
  type: 'tool-call' as const,
@@ -1600,7 +1635,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1600
1635
  input: tc.input,
1601
1636
  }));
1602
1637
 
1603
- const allToolResults: ToolResult[] = resolvedResults.map(r => ({
1638
+ const allToolResults: ToolResult[] = executedResults.map(r => ({
1604
1639
  type: 'tool-result' as const,
1605
1640
  toolCallId: r.toolCallId,
1606
1641
  toolName: r.toolName,
@@ -1688,25 +1723,32 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1688
1723
  providerToolCalls.map(toolCall =>
1689
1724
  resolveProviderToolResult(toolCall, providerExecutedToolResults),
1690
1725
  );
1726
+ const continuationInvalidToolResults = invalidToolCalls.map(
1727
+ createInvalidToolResult,
1728
+ );
1691
1729
 
1692
- // Combine results in the original order
1693
- const toolResults = toolCalls.map(tc => {
1730
+ // Combine executable/provider results in the original order,
1731
+ // while preserving invalid tool calls as error results for the
1732
+ // next model step without emitting them as synthetic UI success.
1733
+ const continuationToolResults = toolCalls.flatMap(tc => {
1734
+ const invalidResult = continuationInvalidToolResults.find(
1735
+ r => r.toolCallId === tc.toolCallId,
1736
+ );
1737
+ if (invalidResult) return [invalidResult];
1694
1738
  const clientResult = clientToolResults.find(
1695
1739
  r => r.toolCallId === tc.toolCallId,
1696
1740
  );
1697
- if (clientResult) return clientResult;
1741
+ if (clientResult) return [clientResult];
1698
1742
  const providerResult = providerToolResults.find(
1699
1743
  r => r.toolCallId === tc.toolCallId,
1700
1744
  );
1701
- if (providerResult) return providerResult;
1702
- // This should never happen, but return empty result as fallback
1703
- return {
1704
- type: 'tool-result' as const,
1705
- toolCallId: tc.toolCallId,
1706
- toolName: tc.toolName,
1707
- output: { type: 'text' as const, value: '' },
1708
- };
1745
+ if (providerResult) return [providerResult];
1746
+ return [];
1709
1747
  });
1748
+ const executedToolResults = continuationToolResults.filter(
1749
+ result =>
1750
+ !invalidToolCalls.some(tc => tc.toolCallId === result.toolCallId),
1751
+ );
1710
1752
 
1711
1753
  // Write tool results and step boundaries to the stream so the
1712
1754
  // UI can transition tool parts to output-available state and
@@ -1714,7 +1756,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1714
1756
  if (options.writable) {
1715
1757
  await writeToolResultsWithStepBoundary(
1716
1758
  options.writable,
1717
- toolResults.map(r => ({
1759
+ executedToolResults.map(r => ({
1718
1760
  toolCallId: r.toolCallId,
1719
1761
  toolName: r.toolName,
1720
1762
  input: toolCalls.find(tc => tc.toolCallId === r.toolCallId)
@@ -1731,7 +1773,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1731
1773
  toolName: tc.toolName,
1732
1774
  input: tc.input,
1733
1775
  }));
1734
- lastStepToolResults = toolResults.map(r => ({
1776
+ lastStepToolResults = executedToolResults.map(r => ({
1735
1777
  type: 'tool-result' as const,
1736
1778
  toolCallId: r.toolCallId,
1737
1779
  toolName: r.toolName,
@@ -1739,7 +1781,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1739
1781
  output: 'value' in r.output ? r.output.value : undefined,
1740
1782
  }));
1741
1783
 
1742
- result = await iterator.next(toolResults);
1784
+ result = await iterator.next(continuationToolResults);
1743
1785
  } else {
1744
1786
  // Final step with no tool calls - reset tracking
1745
1787
  lastStepToolCalls = [];
@@ -2041,6 +2083,22 @@ function resolveProviderToolResult(
2041
2083
  };
2042
2084
  }
2043
2085
 
2086
+ function createInvalidToolResult(toolCall: {
2087
+ toolCallId: string;
2088
+ toolName: string;
2089
+ error?: unknown;
2090
+ }): LanguageModelV4ToolResultPart {
2091
+ return {
2092
+ type: 'tool-result' as const,
2093
+ toolCallId: toolCall.toolCallId,
2094
+ toolName: toolCall.toolName,
2095
+ output: {
2096
+ type: 'error-text' as const,
2097
+ value: getErrorMessage(toolCall.error),
2098
+ },
2099
+ };
2100
+ }
2101
+
2044
2102
  async function executeTool(
2045
2103
  toolCall: { toolCallId: string; toolName: string; input: unknown },
2046
2104
  tools: ToolSet,