@ai-sdk/workflow 1.0.0-beta.26 → 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.26",
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.26",
32
- "ai": "7.0.0-beta.111"
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": {
@@ -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
  ];
@@ -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.
@@ -445,7 +447,7 @@ export interface WorkflowAgentOptions<
445
447
  *
446
448
  * Per-stream `activeTools` values passed to `stream()` override this default.
447
449
  */
448
- activeTools?: Array<keyof NoInfer<TTools>>;
450
+ activeTools?: ActiveTools<NoInfer<TTools>>;
449
451
 
450
452
  /**
451
453
  * Default output specification for structured outputs.
@@ -735,7 +737,7 @@ export type WorkflowAgentStreamOptions<
735
737
  * Limits the tools that are available for the model to call without
736
738
  * changing the tool call and result types in the result.
737
739
  */
738
- activeTools?: Array<keyof NoInfer<TTools>>;
740
+ activeTools?: ActiveTools<NoInfer<TTools>>;
739
741
 
740
742
  /**
741
743
  * Optional telemetry configuration.
@@ -1021,7 +1023,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1021
1023
  private stopWhen?:
1022
1024
  | StopCondition<ToolSet, any>
1023
1025
  | Array<StopCondition<ToolSet, any>>;
1024
- private activeTools?: Array<keyof TBaseTools>;
1026
+ private activeTools?: ActiveTools<TBaseTools>;
1025
1027
  private output?: OutputSpecification<any, any>;
1026
1028
  private experimentalRepairToolCall?: ToolCallRepairFunction<TBaseTools>;
1027
1029
  private experimentalDownload?: DownloadFunction;
@@ -1162,10 +1164,11 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1162
1164
 
1163
1165
  const prompt = await standardizePrompt({
1164
1166
  system: effectiveInstructions,
1167
+ allowSystemInMessages: true, // TODO: consider exposing this as a parameter
1165
1168
  ...(effectivePrompt != null
1166
1169
  ? { prompt: effectivePrompt }
1167
1170
  : { messages: effectiveMessages! }),
1168
- });
1171
+ } as Prompt);
1169
1172
 
1170
1173
  // Process tool approval responses before starting the agent loop.
1171
1174
  // This mirrors how stream-text.ts handles tool-approval-response parts:
@@ -1376,7 +1379,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1376
1379
  effectiveActiveTools && effectiveActiveTools.length > 0
1377
1380
  ? (filterActiveTools({
1378
1381
  tools: this.tools,
1379
- activeTools: effectiveActiveTools as string[],
1382
+ activeTools: effectiveActiveTools,
1380
1383
  }) ?? this.tools)
1381
1384
  : this.tools;
1382
1385
 
@@ -1542,11 +1545,16 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1542
1545
 
1543
1546
  // Only execute tools if there are tool calls
1544
1547
  if (toolCalls.length > 0) {
1548
+ const invalidToolCalls = toolCalls.filter(tc => tc.invalid === true);
1549
+ const validToolCalls = toolCalls.filter(tc => tc.invalid !== true);
1550
+
1545
1551
  // Separate provider-executed tool calls from client-executed ones
1546
- const nonProviderToolCalls = toolCalls.filter(
1552
+ const nonProviderToolCalls = validToolCalls.filter(
1547
1553
  tc => !tc.providerExecuted,
1548
1554
  );
1549
- const providerToolCalls = toolCalls.filter(tc => tc.providerExecuted);
1555
+ const providerToolCalls = validToolCalls.filter(
1556
+ tc => tc.providerExecuted,
1557
+ );
1550
1558
 
1551
1559
  // Check which tools need approval (can be async)
1552
1560
  const approvalNeeded = await Promise.all(
@@ -1610,7 +1618,15 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1610
1618
  ),
1611
1619
  );
1612
1620
 
1613
- 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];
1614
1630
 
1615
1631
  const allToolCalls: ToolCall[] = toolCalls.map(tc => ({
1616
1632
  type: 'tool-call' as const,
@@ -1619,7 +1635,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1619
1635
  input: tc.input,
1620
1636
  }));
1621
1637
 
1622
- const allToolResults: ToolResult[] = resolvedResults.map(r => ({
1638
+ const allToolResults: ToolResult[] = executedResults.map(r => ({
1623
1639
  type: 'tool-result' as const,
1624
1640
  toolCallId: r.toolCallId,
1625
1641
  toolName: r.toolName,
@@ -1707,25 +1723,32 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1707
1723
  providerToolCalls.map(toolCall =>
1708
1724
  resolveProviderToolResult(toolCall, providerExecutedToolResults),
1709
1725
  );
1726
+ const continuationInvalidToolResults = invalidToolCalls.map(
1727
+ createInvalidToolResult,
1728
+ );
1710
1729
 
1711
- // Combine results in the original order
1712
- 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];
1713
1738
  const clientResult = clientToolResults.find(
1714
1739
  r => r.toolCallId === tc.toolCallId,
1715
1740
  );
1716
- if (clientResult) return clientResult;
1741
+ if (clientResult) return [clientResult];
1717
1742
  const providerResult = providerToolResults.find(
1718
1743
  r => r.toolCallId === tc.toolCallId,
1719
1744
  );
1720
- if (providerResult) return providerResult;
1721
- // This should never happen, but return empty result as fallback
1722
- return {
1723
- type: 'tool-result' as const,
1724
- toolCallId: tc.toolCallId,
1725
- toolName: tc.toolName,
1726
- output: { type: 'text' as const, value: '' },
1727
- };
1745
+ if (providerResult) return [providerResult];
1746
+ return [];
1728
1747
  });
1748
+ const executedToolResults = continuationToolResults.filter(
1749
+ result =>
1750
+ !invalidToolCalls.some(tc => tc.toolCallId === result.toolCallId),
1751
+ );
1729
1752
 
1730
1753
  // Write tool results and step boundaries to the stream so the
1731
1754
  // UI can transition tool parts to output-available state and
@@ -1733,7 +1756,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1733
1756
  if (options.writable) {
1734
1757
  await writeToolResultsWithStepBoundary(
1735
1758
  options.writable,
1736
- toolResults.map(r => ({
1759
+ executedToolResults.map(r => ({
1737
1760
  toolCallId: r.toolCallId,
1738
1761
  toolName: r.toolName,
1739
1762
  input: toolCalls.find(tc => tc.toolCallId === r.toolCallId)
@@ -1750,7 +1773,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1750
1773
  toolName: tc.toolName,
1751
1774
  input: tc.input,
1752
1775
  }));
1753
- lastStepToolResults = toolResults.map(r => ({
1776
+ lastStepToolResults = executedToolResults.map(r => ({
1754
1777
  type: 'tool-result' as const,
1755
1778
  toolCallId: r.toolCallId,
1756
1779
  toolName: r.toolName,
@@ -1758,7 +1781,7 @@ export class WorkflowAgent<TBaseTools extends ToolSet = ToolSet> {
1758
1781
  output: 'value' in r.output ? r.output.value : undefined,
1759
1782
  }));
1760
1783
 
1761
- result = await iterator.next(toolResults);
1784
+ result = await iterator.next(continuationToolResults);
1762
1785
  } else {
1763
1786
  // Final step with no tool calls - reset tracking
1764
1787
  lastStepToolCalls = [];
@@ -2060,6 +2083,22 @@ function resolveProviderToolResult(
2060
2083
  };
2061
2084
  }
2062
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
+
2063
2102
  async function executeTool(
2064
2103
  toolCall: { toolCallId: string; toolName: string; input: unknown },
2065
2104
  tools: ToolSet,