@ai-sdk/workflow 2.0.19 → 2.0.21

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.
@@ -8,7 +8,6 @@ import type {
8
8
  import {
9
9
  getErrorMessage,
10
10
  isAbortError,
11
- validateTypes,
12
11
  withUserAgentSuffix,
13
12
  type Context,
14
13
  type HasRequiredKey,
@@ -49,7 +48,11 @@ import {
49
48
  verifyToolApprovalSignature,
50
49
  } from 'ai/internal';
51
50
  import { createLanguageModelToolResultOutput } from './create-language-model-tool-result-output.js';
52
- import type { ModelCallStreamPart } from './do-stream-step.js';
51
+ import type {
52
+ ModelCallStreamPart,
53
+ ModelStopCondition,
54
+ } from './do-stream-step.js';
55
+ import { resolveToolContext } from './resolve-tool-context.js';
53
56
  import { streamTextIterator } from './stream-text-iterator.js';
54
57
 
55
58
  // Re-export for consumers
@@ -80,7 +83,9 @@ export type WorkflowAgentOnStepFinishCallback<
80
83
  * Infer the type of the tools of a workflow agent.
81
84
  */
82
85
  export type InferWorkflowAgentTools<WORKFLOW_AGENT> =
83
- WORKFLOW_AGENT extends WorkflowAgent<infer TOOLS, any> ? TOOLS : never;
86
+ WORKFLOW_AGENT extends WorkflowAgent<infer TOOLS, any, any, any>
87
+ ? TOOLS
88
+ : never;
84
89
 
85
90
  /**
86
91
  * Infer the UI message type of a workflow agent.
@@ -120,6 +125,8 @@ export interface OutputSpecification<OUTPUT, PARTIAL> {
120
125
  ): Promise<OUTPUT>;
121
126
  }
122
127
 
128
+ type DefaultWorkflowAgentOutput<OUTPUT> = 0 extends 1 & OUTPUT ? never : OUTPUT;
129
+
123
130
  /**
124
131
  * Provider-specific options type. This is equivalent to SharedV4ProviderOptions from @ai-sdk/provider.
125
132
  */
@@ -401,8 +408,8 @@ export interface PrepareCallOptions<
401
408
  instructions?: Instructions;
402
409
  toolChoice?: ToolChoice<TTools>;
403
410
  stopWhen?:
404
- | StopCondition<NoInfer<ToolSet>, any>
405
- | Array<StopCondition<NoInfer<ToolSet>, any>>;
411
+ | StopCondition<NoInfer<TTools>, TRuntimeContext>
412
+ | Array<StopCondition<NoInfer<TTools>, TRuntimeContext>>;
406
413
  activeTools?: ActiveTools<NoInfer<TTools>>;
407
414
  experimental_download?: DownloadFunction;
408
415
  telemetry?: TelemetryOptions<TRuntimeContext, TTools>;
@@ -447,6 +454,8 @@ export type PrepareCallCallback<
447
454
  export type WorkflowAgentOptions<
448
455
  TTools extends ToolSet = ToolSet,
449
456
  TRuntimeContext extends Context = Context,
457
+ OUTPUT = any,
458
+ PARTIAL_OUTPUT = any,
450
459
  > = GenerationSettings &
451
460
  WorkflowAgentToolsContextParameter<TTools> & {
452
461
  /**
@@ -513,8 +522,8 @@ export type WorkflowAgentOptions<
513
522
  * Per-stream `stopWhen` values passed to `stream()` override this default.
514
523
  */
515
524
  stopWhen?:
516
- | StopCondition<NoInfer<ToolSet>, any>
517
- | Array<StopCondition<NoInfer<ToolSet>, any>>;
525
+ | StopCondition<NoInfer<TTools>, TRuntimeContext>
526
+ | Array<StopCondition<NoInfer<TTools>, TRuntimeContext>>;
518
527
 
519
528
  /**
520
529
  * Default set of active tools that limits which tools the model can call,
@@ -530,7 +539,7 @@ export type WorkflowAgentOptions<
530
539
  *
531
540
  * Per-stream `output` values passed to `stream()` override this default.
532
541
  */
533
- output?: OutputSpecification<any, any>;
542
+ output?: OutputSpecification<OUTPUT, PARTIAL_OUTPUT>;
534
543
 
535
544
  /**
536
545
  * Default function that attempts to repair a tool call that failed to parse.
@@ -937,8 +946,8 @@ export type WorkflowAgentStreamOptions<
937
946
  * When the condition is an array, any of the conditions can be met to stop the generation.
938
947
  */
939
948
  stopWhen?:
940
- | StopCondition<NoInfer<ToolSet>, any>
941
- | Array<StopCondition<NoInfer<ToolSet>, any>>;
949
+ | StopCondition<NoInfer<TTools>, TRuntimeContext>
950
+ | Array<StopCondition<NoInfer<TTools>, TRuntimeContext>>;
942
951
 
943
952
  /**
944
953
  * The tool choice strategy. Default: 'auto'.
@@ -1213,6 +1222,64 @@ type WorkflowToolExecutionResult = {
1213
1222
  isError: boolean;
1214
1223
  };
1215
1224
 
1225
+ function addToolResultsToStep(
1226
+ step: StepResult<ToolSet, any> | undefined,
1227
+ executedResults: WorkflowToolExecutionResult[],
1228
+ ) {
1229
+ if (step == null || executedResults.length === 0) {
1230
+ return;
1231
+ }
1232
+
1233
+ const toolOutputs = executedResults.map(result => {
1234
+ const toolCall = step.toolCalls.find(
1235
+ toolCall => toolCall.toolCallId === result.modelResult.toolCallId,
1236
+ );
1237
+
1238
+ const common = {
1239
+ toolCallId: result.modelResult.toolCallId,
1240
+ toolName: result.modelResult.toolName,
1241
+ input: toolCall?.input,
1242
+ ...(toolCall?.dynamic === true ? { dynamic: true as const } : {}),
1243
+ ...(toolCall?.providerExecuted === true
1244
+ ? { providerExecuted: true }
1245
+ : {}),
1246
+ };
1247
+
1248
+ return result.isError
1249
+ ? {
1250
+ type: 'tool-error' as const,
1251
+ ...common,
1252
+ error: result.rawOutput,
1253
+ }
1254
+ : {
1255
+ type: 'tool-result' as const,
1256
+ ...common,
1257
+ output: result.rawOutput,
1258
+ };
1259
+ });
1260
+
1261
+ step.content.push(...(toolOutputs as StepResult<ToolSet, any>['content']));
1262
+
1263
+ const toolResults = toolOutputs.filter(
1264
+ result => result.type === 'tool-result',
1265
+ );
1266
+ step.toolResults.push(
1267
+ ...(toolResults as StepResult<ToolSet, any>['toolResults']),
1268
+ );
1269
+ step.staticToolResults.push(
1270
+ ...(toolResults.filter(result => result.dynamic !== true) as StepResult<
1271
+ ToolSet,
1272
+ any
1273
+ >['staticToolResults']),
1274
+ );
1275
+ step.dynamicToolResults.push(
1276
+ ...(toolResults.filter(result => result.dynamic === true) as StepResult<
1277
+ ToolSet,
1278
+ any
1279
+ >['dynamicToolResults']),
1280
+ );
1281
+ }
1282
+
1216
1283
  /**
1217
1284
  * Result of the WorkflowAgent.stream method.
1218
1285
  */
@@ -1313,6 +1380,8 @@ export interface WorkflowAgentStreamResult<
1313
1380
  export class WorkflowAgent<
1314
1381
  TBaseTools extends ToolSet = ToolSet,
1315
1382
  TRuntimeContext extends Context = Context,
1383
+ OUTPUT = any,
1384
+ PARTIAL_OUTPUT = any,
1316
1385
  > {
1317
1386
  /**
1318
1387
  * The id of the agent.
@@ -1331,10 +1400,10 @@ export class WorkflowAgent<
1331
1400
  private runtimeContext?: TRuntimeContext;
1332
1401
  private toolsContext?: InferToolSetContext<TBaseTools>;
1333
1402
  private stopWhen?:
1334
- | StopCondition<ToolSet, any>
1335
- | Array<StopCondition<ToolSet, any>>;
1403
+ | StopCondition<TBaseTools, TRuntimeContext>
1404
+ | Array<StopCondition<TBaseTools, TRuntimeContext>>;
1336
1405
  private activeTools?: ActiveTools<TBaseTools>;
1337
- private output?: OutputSpecification<any, any>;
1406
+ private output?: OutputSpecification<OUTPUT, PARTIAL_OUTPUT>;
1338
1407
  private repairToolCall?: ToolCallRepairFunction<TBaseTools>;
1339
1408
  private experimentalDownload?: DownloadFunction;
1340
1409
  private experimentalSandbox?: SandboxSession;
@@ -1361,7 +1430,14 @@ export class WorkflowAgent<
1361
1430
  private constructorOnToolExecutionEnd?: WorkflowAgentOnToolExecutionEndCallback<TBaseTools>;
1362
1431
  private prepareCall?: PrepareCallCallback<TBaseTools, TRuntimeContext>;
1363
1432
 
1364
- constructor(options: WorkflowAgentOptions<TBaseTools, TRuntimeContext>) {
1433
+ constructor(
1434
+ options: WorkflowAgentOptions<
1435
+ TBaseTools,
1436
+ TRuntimeContext,
1437
+ OUTPUT,
1438
+ PARTIAL_OUTPUT
1439
+ >,
1440
+ ) {
1365
1441
  this.id = options.id;
1366
1442
  this.model = options.model;
1367
1443
  this.tools = (options.tools ?? {}) as TBaseTools;
@@ -1416,16 +1492,16 @@ export class WorkflowAgent<
1416
1492
 
1417
1493
  async stream<
1418
1494
  TTools extends TBaseTools = TBaseTools,
1419
- OUTPUT = never,
1420
- PARTIAL_OUTPUT = never,
1495
+ TOutput = DefaultWorkflowAgentOutput<OUTPUT>,
1496
+ TPartialOutput = DefaultWorkflowAgentOutput<PARTIAL_OUTPUT>,
1421
1497
  >(
1422
1498
  options: WorkflowAgentStreamOptions<
1423
1499
  TTools,
1424
1500
  TRuntimeContext,
1425
- OUTPUT,
1426
- PARTIAL_OUTPUT
1501
+ TOutput,
1502
+ TPartialOutput
1427
1503
  >,
1428
- ): Promise<WorkflowAgentStreamResult<TTools, OUTPUT>> {
1504
+ ): Promise<WorkflowAgentStreamResult<TTools, TOutput>> {
1429
1505
  const { onFinish, onEnd = onFinish } = options;
1430
1506
 
1431
1507
  // Call prepareCall to transform parameters before the agent loop
@@ -1869,7 +1945,7 @@ export class WorkflowAgent<
1869
1945
  );
1870
1946
  const mergedOnEnd = mergeCallbacks(
1871
1947
  this.constructorOnEnd as
1872
- | WorkflowAgentOnEndCallback<TTools, TRuntimeContext, OUTPUT>
1948
+ | WorkflowAgentOnEndCallback<TTools, TRuntimeContext, TOutput>
1873
1949
  | undefined,
1874
1950
  onEnd,
1875
1951
  );
@@ -2149,7 +2225,7 @@ export class WorkflowAgent<
2149
2225
  toolResults: [],
2150
2226
  finishReason: 'other',
2151
2227
  totalUsage: aggregateUsage(steps),
2152
- output: undefined as OUTPUT,
2228
+ output: undefined as TOutput,
2153
2229
  };
2154
2230
  }
2155
2231
 
@@ -2160,7 +2236,10 @@ export class WorkflowAgent<
2160
2236
  prompt: modelPrompt,
2161
2237
  initialInstructions: effectiveInstructions,
2162
2238
  initialMessages: prompt.messages,
2163
- stopConditions: effectiveStopWhenFromPrepare,
2239
+ stopConditions: effectiveStopWhenFromPrepare as
2240
+ | ModelStopCondition
2241
+ | ModelStopCondition[]
2242
+ | undefined,
2164
2243
  onStepEnd: mergedOnStepEnd as any,
2165
2244
  onStepStart: mergedOnStepStart as any,
2166
2245
  prepareStep: (options.prepareStep ??
@@ -2297,27 +2376,34 @@ export class WorkflowAgent<
2297
2376
  );
2298
2377
 
2299
2378
  // Collect provider tool results
2300
- const providerResults: WorkflowToolExecutionResult[] =
2301
- await Promise.all(
2302
- providerToolCalls.map(toolCall =>
2303
- resolveProviderToolResult(
2304
- toolCall,
2305
- providerExecutedToolResults,
2306
- effectiveTools as ToolSet,
2307
- download,
2308
- ),
2309
- ),
2310
- );
2311
- await Promise.all(
2312
- providerToolCalls.map((toolCall, index) =>
2313
- recordProviderExecutedToolTelemetry(
2379
+ const providerResultEntries = await Promise.all(
2380
+ providerToolCalls.map(async toolCall => ({
2381
+ toolCall,
2382
+ result: await resolveProviderToolResult(
2314
2383
  toolCall,
2315
- providerResults[index],
2316
- iterMessages,
2317
- currentStepNumber,
2384
+ providerExecutedToolResults,
2385
+ effectiveTools as ToolSet,
2386
+ download,
2318
2387
  ),
2388
+ })),
2389
+ );
2390
+ await Promise.all(
2391
+ providerResultEntries.flatMap(({ toolCall, result }) =>
2392
+ result == null
2393
+ ? []
2394
+ : [
2395
+ recordProviderExecutedToolTelemetry(
2396
+ toolCall,
2397
+ result,
2398
+ iterMessages,
2399
+ currentStepNumber,
2400
+ ),
2401
+ ],
2319
2402
  ),
2320
2403
  );
2404
+ const providerResults = providerResultEntries.flatMap(
2405
+ ({ result }) => (result == null ? [] : [result]),
2406
+ );
2321
2407
 
2322
2408
  const continuationInvalidResults = invalidToolCalls.map(
2323
2409
  createInvalidToolResult,
@@ -2346,6 +2432,8 @@ export class WorkflowAgent<
2346
2432
  output: r.rawOutput,
2347
2433
  }));
2348
2434
 
2435
+ addToolResultsToStep(step, executedResults);
2436
+
2349
2437
  if (resolvedResults.length > 0) {
2350
2438
  iterMessages.push({
2351
2439
  role: 'tool',
@@ -2369,7 +2457,7 @@ export class WorkflowAgent<
2369
2457
  runtimeContext,
2370
2458
  toolsContext:
2371
2459
  toolsContext as unknown as InferToolSetContext<TTools>,
2372
- output: undefined as OUTPUT,
2460
+ output: undefined as TOutput,
2373
2461
  });
2374
2462
  }
2375
2463
  if (!wasAborted && steps.length > 0) {
@@ -2453,7 +2541,7 @@ export class WorkflowAgent<
2453
2541
  toolResults: allToolResults,
2454
2542
  finishReason,
2455
2543
  totalUsage,
2456
- output: undefined as OUTPUT,
2544
+ output: undefined as TOutput,
2457
2545
  };
2458
2546
  }
2459
2547
 
@@ -2473,27 +2561,34 @@ export class WorkflowAgent<
2473
2561
  );
2474
2562
 
2475
2563
  // For provider-executed tools, use the results from the stream
2476
- const providerToolResults: WorkflowToolExecutionResult[] =
2477
- await Promise.all(
2478
- providerToolCalls.map(toolCall =>
2479
- resolveProviderToolResult(
2480
- toolCall,
2481
- providerExecutedToolResults,
2482
- effectiveTools as ToolSet,
2483
- download,
2484
- ),
2485
- ),
2486
- );
2487
- await Promise.all(
2488
- providerToolCalls.map((toolCall, index) =>
2489
- recordProviderExecutedToolTelemetry(
2564
+ const providerToolResultEntries = await Promise.all(
2565
+ providerToolCalls.map(async toolCall => ({
2566
+ toolCall,
2567
+ result: await resolveProviderToolResult(
2490
2568
  toolCall,
2491
- providerToolResults[index],
2492
- iterMessages,
2493
- currentStepNumber,
2569
+ providerExecutedToolResults,
2570
+ effectiveTools as ToolSet,
2571
+ download,
2494
2572
  ),
2573
+ })),
2574
+ );
2575
+ await Promise.all(
2576
+ providerToolResultEntries.flatMap(({ toolCall, result }) =>
2577
+ result == null
2578
+ ? []
2579
+ : [
2580
+ recordProviderExecutedToolTelemetry(
2581
+ toolCall,
2582
+ result,
2583
+ iterMessages,
2584
+ currentStepNumber,
2585
+ ),
2586
+ ],
2495
2587
  ),
2496
2588
  );
2589
+ const providerToolResults = providerToolResultEntries.flatMap(
2590
+ ({ result }) => (result == null ? [] : [result]),
2591
+ );
2497
2592
  const continuationInvalidToolResults = invalidToolCalls.map(
2498
2593
  createInvalidToolResult,
2499
2594
  );
@@ -2560,6 +2655,8 @@ export class WorkflowAgent<
2560
2655
  output: r.rawOutput,
2561
2656
  }));
2562
2657
 
2658
+ addToolResultsToStep(step, executedToolResults);
2659
+
2563
2660
  result = await iterator.next(continuationToolResults);
2564
2661
  } else {
2565
2662
  // Final step with no tool calls - reset tracking
@@ -2616,8 +2713,10 @@ export class WorkflowAgent<
2616
2713
  prompt.messages) as unknown as ModelMessage[];
2617
2714
 
2618
2715
  // Parse structured output if output is specified (stream-level overrides constructor default)
2619
- const effectiveOutput = options.output ?? this.output;
2620
- let experimentalOutput: OUTPUT = undefined as OUTPUT;
2716
+ const effectiveOutput = (options.output ?? this.output) as
2717
+ | OutputSpecification<TOutput, TPartialOutput>
2718
+ | undefined;
2719
+ let experimentalOutput: TOutput = undefined as TOutput;
2621
2720
  if (effectiveOutput && steps.length > 0) {
2622
2721
  const lastStep = steps[steps.length - 1];
2623
2722
  const text = lastStep.text;
@@ -2974,33 +3073,6 @@ async function writeApprovalToolResults(
2974
3073
  }
2975
3074
  }
2976
3075
 
2977
- /**
2978
- * Resolve the per-tool context that gets passed into a tool's `execute`
2979
- * (and `needsApproval`) function. When the tool declares a `contextSchema`,
2980
- * the entry is validated against it.
2981
- */
2982
- async function resolveToolContext({
2983
- toolName,
2984
- tool,
2985
- toolsContext,
2986
- }: {
2987
- toolName: string;
2988
- tool: ToolSet[string];
2989
- toolsContext: Record<string, Context | undefined> | undefined;
2990
- }): Promise<unknown> {
2991
- const contextSchema = (tool as { contextSchema?: unknown }).contextSchema;
2992
- const entry = toolsContext?.[toolName];
2993
- if (contextSchema == null) {
2994
- return entry;
2995
- }
2996
-
2997
- return await validateTypes({
2998
- value: entry,
2999
- schema: contextSchema as Parameters<typeof validateTypes>[0]['schema'],
3000
- context: { field: 'tool context', entityName: toolName },
3001
- });
3002
- }
3003
-
3004
3076
  function aggregateUsage(steps: StepResult<any, any>[]): LanguageModelUsage {
3005
3077
  let inputTokens = 0;
3006
3078
  let outputTokens = 0;
@@ -3023,9 +3095,18 @@ async function resolveProviderToolResult(
3023
3095
  >,
3024
3096
  tools?: ToolSet,
3025
3097
  download?: DownloadFunction,
3026
- ): Promise<WorkflowToolExecutionResult> {
3098
+ ): Promise<WorkflowToolExecutionResult | undefined> {
3027
3099
  const streamResult = providerExecutedToolResults?.get(toolCall.toolCallId);
3028
3100
  if (!streamResult) {
3101
+ const tool = tools?.[toolCall.toolName];
3102
+ if (
3103
+ tool?.type === 'provider' &&
3104
+ tool.isProviderExecuted &&
3105
+ tool.supportsDeferredResults
3106
+ ) {
3107
+ return undefined;
3108
+ }
3109
+
3029
3110
  console.warn(
3030
3111
  `[WorkflowAgent] Provider-executed tool "${toolCall.toolName}" (${toolCall.toolCallId}) ` +
3031
3112
  `did not receive a result from the stream. This may indicate a provider issue.`,