@ai-sdk/workflow 1.0.0-beta.1 → 1.0.0-beta.100

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.
@@ -3,15 +3,18 @@ import type {
3
3
  LanguageModelV4Prompt,
4
4
  LanguageModelV4ToolResultPart,
5
5
  } from '@ai-sdk/provider';
6
- import type {
7
- Experimental_LanguageModelStreamPart as ModelCallStreamPart,
8
- ModelMessage,
9
- StepResult,
10
- StreamTextOnStepFinishCallback,
11
- ToolCallRepairFunction,
12
- ToolChoice,
13
- ToolSet,
6
+ import type { Context } from '@ai-sdk/provider-utils';
7
+ import {
8
+ experimental_filterActiveTools as filterActiveTools,
9
+ type Experimental_LanguageModelStreamPart as ModelCallStreamPart,
10
+ type LanguageModel,
11
+ type ModelMessage,
12
+ type StepResult,
13
+ type ToolCallRepairFunction,
14
+ type ToolChoice,
15
+ type ToolSet,
14
16
  } from 'ai';
17
+ import { createRestrictedTelemetryDispatcher } from 'ai/internal';
15
18
  import {
16
19
  doStreamStep,
17
20
  type ModelStopCondition,
@@ -22,11 +25,12 @@ import { serializeToolSet } from './serializable-schema.js';
22
25
  import type {
23
26
  GenerationSettings,
24
27
  PrepareStepCallback,
25
- StreamTextOnErrorCallback,
26
- TelemetrySettings,
28
+ WorkflowAgentOnErrorCallback,
29
+ WorkflowAgentOnStepEndCallback,
30
+ WorkflowAgentOnStepFinishCallback,
31
+ TelemetryOptions,
27
32
  WorkflowAgentOnStepStartCallback,
28
33
  } from './workflow-agent.js';
29
- import type { CompatibleLanguageModel } from './types.js';
30
34
 
31
35
  // Re-export for consumers
32
36
  export type { ProviderExecutedToolResult } from './do-stream-step.js';
@@ -42,8 +46,10 @@ export interface StreamTextIteratorYieldValue {
42
46
  messages: LanguageModelV4Prompt;
43
47
  /** The step result from the current step */
44
48
  step?: StepResult<ToolSet, any>;
45
- /** The current experimental context */
46
- context?: unknown;
49
+ /** The current runtime context shared across the agent loop */
50
+ runtimeContext?: Context;
51
+ /** The current per-tool context, keyed by tool name */
52
+ toolsContext?: Record<string, Context | undefined>;
47
53
  /** Provider-executed tool results (keyed by tool call ID) */
48
54
  providerExecutedToolResults?: Map<string, ProviderExecutedToolResult>;
49
55
  }
@@ -55,15 +61,16 @@ export async function* streamTextIterator({
55
61
  writable,
56
62
  model,
57
63
  stopConditions,
58
- maxSteps,
64
+ onStepEnd,
59
65
  onStepFinish,
60
66
  onStepStart,
61
67
  onError,
62
68
  prepareStep,
63
69
  generationSettings,
64
70
  toolChoice,
65
- experimental_context,
66
- experimental_telemetry,
71
+ runtimeContext,
72
+ toolsContext,
73
+ telemetry,
67
74
  includeRawChunks = false,
68
75
  repairToolCall,
69
76
  responseFormat,
@@ -71,20 +78,19 @@ export async function* streamTextIterator({
71
78
  prompt: LanguageModelV4Prompt;
72
79
  tools: ToolSet;
73
80
  writable?: WritableStream<ModelCallStreamPart<ToolSet>>;
74
- model:
75
- | string
76
- | CompatibleLanguageModel
77
- | (() => Promise<CompatibleLanguageModel>);
81
+ model: LanguageModel;
78
82
  stopConditions?: ModelStopCondition[] | ModelStopCondition;
79
- maxSteps?: number;
80
- onStepFinish?: StreamTextOnStepFinishCallback<any, any>;
83
+ onStepEnd?: WorkflowAgentOnStepEndCallback<any>;
84
+ /** @deprecated Use `onStepEnd` instead. */
85
+ onStepFinish?: WorkflowAgentOnStepFinishCallback<any>;
81
86
  onStepStart?: WorkflowAgentOnStepStartCallback;
82
- onError?: StreamTextOnErrorCallback;
87
+ onError?: WorkflowAgentOnErrorCallback;
83
88
  prepareStep?: PrepareStepCallback<any>;
84
89
  generationSettings?: GenerationSettings;
85
90
  toolChoice?: ToolChoice<ToolSet>;
86
- experimental_context?: unknown;
87
- experimental_telemetry?: TelemetrySettings;
91
+ runtimeContext?: Context;
92
+ toolsContext?: Record<string, Context | undefined>;
93
+ telemetry?: TelemetryOptions<Context, ToolSet>;
88
94
  includeRawChunks?: boolean;
89
95
  repairToolCall?: ToolCallRepairFunction<ToolSet>;
90
96
  responseFormat?: LanguageModelV4CallOptions['responseFormat'];
@@ -94,13 +100,12 @@ export async function* streamTextIterator({
94
100
  LanguageModelV4ToolResultPart[]
95
101
  > {
96
102
  let conversationPrompt = [...prompt]; // Create a mutable copy
97
- let currentModel:
98
- | string
99
- | CompatibleLanguageModel
100
- | (() => Promise<CompatibleLanguageModel>) = model;
103
+ let currentModel: LanguageModel = model;
101
104
  let currentGenerationSettings = generationSettings ?? {};
102
105
  let currentToolChoice = toolChoice;
103
- let currentContext = experimental_context;
106
+ let currentRuntimeContext: Context = runtimeContext ?? {};
107
+ let currentToolsContext: Record<string, Context | undefined> =
108
+ toolsContext ?? {};
104
109
  let currentActiveTools: string[] | undefined;
105
110
 
106
111
  const steps: StepResult<any, any>[] = [];
@@ -110,16 +115,21 @@ export async function* streamTextIterator({
110
115
  let lastStep: StepResult<any, any> | undefined;
111
116
  let lastStepWasToolCalls = false;
112
117
 
113
- // Default maxSteps to Infinity to preserve backwards compatibility
114
- // (agent loops until completion unless explicitly limited)
115
- const effectiveMaxSteps = maxSteps ?? Infinity;
118
+ // TODO(#12164): replace this AI-core telemetry bridge with a
119
+ // WorkflowAgent-specific typed dispatcher. `streamTextIterator` widens
120
+ // tools/runtime context and emits Workflow-shaped events that are only
121
+ // approximately compatible with generateText telemetry event types.
122
+ const telemetryDispatcher = createRestrictedTelemetryDispatcher<
123
+ any,
124
+ any,
125
+ any
126
+ >({
127
+ telemetry: telemetry as any,
128
+ includeRuntimeContext: telemetry?.includeRuntimeContext,
129
+ includeToolsContext: telemetry?.includeToolsContext,
130
+ }) as any;
116
131
 
117
132
  while (!done) {
118
- // Check if we've exceeded the maximum number of steps
119
- if (stepNumber >= effectiveMaxSteps) {
120
- break;
121
- }
122
-
123
133
  // Check for abort signal
124
134
  if (currentGenerationSettings.abortSignal?.aborted) {
125
135
  break;
@@ -132,19 +142,20 @@ export async function* streamTextIterator({
132
142
  stepNumber,
133
143
  steps,
134
144
  messages: conversationPrompt,
135
- experimental_context: currentContext,
145
+ runtimeContext: currentRuntimeContext,
146
+ toolsContext: currentToolsContext as never,
136
147
  });
137
148
 
138
149
  // Apply any overrides from prepareStep
139
- if (prepareResult.model !== undefined) {
150
+ if (prepareResult?.model !== undefined) {
140
151
  currentModel = prepareResult.model;
141
152
  }
142
153
  // Apply messages override BEFORE system so the system message
143
154
  // isn't lost when messages replaces the prompt.
144
- if (prepareResult.messages !== undefined) {
155
+ if (prepareResult?.messages !== undefined) {
145
156
  conversationPrompt = [...prepareResult.messages];
146
157
  }
147
- if (prepareResult.system !== undefined) {
158
+ if (prepareResult?.system !== undefined) {
148
159
  // Update or prepend system message in the conversation prompt.
149
160
  // Applied AFTER messages override so the system message isn't
150
161
  // lost when messages replaces the prompt.
@@ -165,80 +176,86 @@ export async function* streamTextIterator({
165
176
  });
166
177
  }
167
178
  }
168
- if (prepareResult.experimental_context !== undefined) {
169
- currentContext = prepareResult.experimental_context;
179
+ if (prepareResult?.runtimeContext !== undefined) {
180
+ currentRuntimeContext = prepareResult.runtimeContext;
170
181
  }
171
- if (prepareResult.activeTools !== undefined) {
182
+ if (prepareResult?.toolsContext !== undefined) {
183
+ currentToolsContext = prepareResult.toolsContext as Record<
184
+ string,
185
+ Context | undefined
186
+ >;
187
+ }
188
+ if (prepareResult?.activeTools !== undefined) {
172
189
  currentActiveTools = prepareResult.activeTools;
173
190
  }
174
191
  // Apply generation settings overrides
175
- if (prepareResult.maxOutputTokens !== undefined) {
192
+ if (prepareResult?.maxOutputTokens !== undefined) {
176
193
  currentGenerationSettings = {
177
194
  ...currentGenerationSettings,
178
195
  maxOutputTokens: prepareResult.maxOutputTokens,
179
196
  };
180
197
  }
181
- if (prepareResult.temperature !== undefined) {
198
+ if (prepareResult?.temperature !== undefined) {
182
199
  currentGenerationSettings = {
183
200
  ...currentGenerationSettings,
184
201
  temperature: prepareResult.temperature,
185
202
  };
186
203
  }
187
- if (prepareResult.topP !== undefined) {
204
+ if (prepareResult?.topP !== undefined) {
188
205
  currentGenerationSettings = {
189
206
  ...currentGenerationSettings,
190
207
  topP: prepareResult.topP,
191
208
  };
192
209
  }
193
- if (prepareResult.topK !== undefined) {
210
+ if (prepareResult?.topK !== undefined) {
194
211
  currentGenerationSettings = {
195
212
  ...currentGenerationSettings,
196
213
  topK: prepareResult.topK,
197
214
  };
198
215
  }
199
- if (prepareResult.presencePenalty !== undefined) {
216
+ if (prepareResult?.presencePenalty !== undefined) {
200
217
  currentGenerationSettings = {
201
218
  ...currentGenerationSettings,
202
219
  presencePenalty: prepareResult.presencePenalty,
203
220
  };
204
221
  }
205
- if (prepareResult.frequencyPenalty !== undefined) {
222
+ if (prepareResult?.frequencyPenalty !== undefined) {
206
223
  currentGenerationSettings = {
207
224
  ...currentGenerationSettings,
208
225
  frequencyPenalty: prepareResult.frequencyPenalty,
209
226
  };
210
227
  }
211
- if (prepareResult.stopSequences !== undefined) {
228
+ if (prepareResult?.stopSequences !== undefined) {
212
229
  currentGenerationSettings = {
213
230
  ...currentGenerationSettings,
214
231
  stopSequences: prepareResult.stopSequences,
215
232
  };
216
233
  }
217
- if (prepareResult.seed !== undefined) {
234
+ if (prepareResult?.seed !== undefined) {
218
235
  currentGenerationSettings = {
219
236
  ...currentGenerationSettings,
220
237
  seed: prepareResult.seed,
221
238
  };
222
239
  }
223
- if (prepareResult.maxRetries !== undefined) {
240
+ if (prepareResult?.maxRetries !== undefined) {
224
241
  currentGenerationSettings = {
225
242
  ...currentGenerationSettings,
226
243
  maxRetries: prepareResult.maxRetries,
227
244
  };
228
245
  }
229
- if (prepareResult.headers !== undefined) {
246
+ if (prepareResult?.headers !== undefined) {
230
247
  currentGenerationSettings = {
231
248
  ...currentGenerationSettings,
232
249
  headers: prepareResult.headers,
233
250
  };
234
251
  }
235
- if (prepareResult.providerOptions !== undefined) {
252
+ if (prepareResult?.providerOptions !== undefined) {
236
253
  currentGenerationSettings = {
237
254
  ...currentGenerationSettings,
238
255
  providerOptions: prepareResult.providerOptions,
239
256
  };
240
257
  }
241
- if (prepareResult.toolChoice !== undefined) {
258
+ if (prepareResult?.toolChoice !== undefined) {
242
259
  currentToolChoice = prepareResult.toolChoice;
243
260
  }
244
261
  }
@@ -248,20 +265,67 @@ export async function* streamTextIterator({
248
265
  stepNumber,
249
266
  model: currentModel,
250
267
  messages: conversationPrompt as unknown as ModelMessage[],
268
+ steps: [...steps],
269
+ runtimeContext: currentRuntimeContext,
270
+ toolsContext: currentToolsContext as never,
251
271
  });
252
272
  }
253
273
 
274
+ const stepStartModelInfo = getModelInfo(currentModel);
275
+ await telemetryDispatcher.onStepStart?.({
276
+ callId: 'workflow-agent',
277
+ provider: stepStartModelInfo.provider,
278
+ modelId: stepStartModelInfo.modelId,
279
+ stepNumber,
280
+ system: undefined,
281
+ messages: conversationPrompt as unknown as ModelMessage[],
282
+ tools,
283
+ toolChoice: currentToolChoice,
284
+ activeTools: currentActiveTools as never,
285
+ steps: steps.map(normalizeStepForTelemetry),
286
+ providerOptions: currentGenerationSettings.providerOptions,
287
+ output: undefined,
288
+ runtimeContext: currentRuntimeContext,
289
+ toolsContext: currentToolsContext as never,
290
+ });
291
+
254
292
  try {
255
293
  // Filter tools if activeTools is specified
256
294
  const effectiveTools =
257
295
  currentActiveTools && currentActiveTools.length > 0
258
- ? filterToolSet(tools, currentActiveTools)
296
+ ? (filterActiveTools({
297
+ tools,
298
+ activeTools: currentActiveTools,
299
+ }) ?? tools)
259
300
  : tools;
260
301
 
261
302
  // Serialize tools before crossing the step boundary — zod schemas
262
303
  // contain functions that can't be serialized by the workflow runtime.
263
304
  // Tools are reconstructed with Ajv validation inside doStreamStep.
264
305
  const serializedTools = serializeToolSet(effectiveTools);
306
+ const modelCallInfo = getModelInfo(currentModel);
307
+
308
+ await telemetryDispatcher.onLanguageModelCallStart?.({
309
+ callId: 'workflow-agent',
310
+ provider: modelCallInfo.provider,
311
+ modelId: modelCallInfo.modelId,
312
+ system: undefined,
313
+ messages: conversationPrompt as unknown as ModelMessage[],
314
+ tools:
315
+ serializedTools == null
316
+ ? undefined
317
+ : Object.values(serializedTools).map(tool => ({ ...tool })),
318
+ maxOutputTokens: currentGenerationSettings.maxOutputTokens,
319
+ temperature: currentGenerationSettings.temperature,
320
+ topP: currentGenerationSettings.topP,
321
+ topK: currentGenerationSettings.topK,
322
+ presencePenalty: currentGenerationSettings.presencePenalty,
323
+ frequencyPenalty: currentGenerationSettings.frequencyPenalty,
324
+ stopSequences: currentGenerationSettings.stopSequences,
325
+ seed: currentGenerationSettings.seed,
326
+ providerOptions: currentGenerationSettings.providerOptions,
327
+ headers: currentGenerationSettings.headers,
328
+ } as never);
265
329
 
266
330
  const { toolCalls, finish, step, providerExecutedToolResults } =
267
331
  await doStreamStep(
@@ -273,12 +337,24 @@ export async function* streamTextIterator({
273
337
  ...currentGenerationSettings,
274
338
  toolChoice: currentToolChoice,
275
339
  includeRawChunks,
276
- experimental_telemetry,
277
340
  repairToolCall,
278
341
  responseFormat,
342
+ runtimeContext: currentRuntimeContext,
343
+ toolsContext: currentToolsContext,
344
+ stepNumber,
279
345
  },
280
346
  );
281
347
 
348
+ await telemetryDispatcher.onLanguageModelCallEnd?.({
349
+ callId: step.callId,
350
+ provider: step.model?.provider ?? 'unknown',
351
+ modelId: step.model?.modelId ?? 'unknown',
352
+ finishReason: step.finishReason,
353
+ usage: step.usage,
354
+ content: step.content,
355
+ responseId: step.response.id,
356
+ });
357
+
282
358
  _isFirstIteration = false;
283
359
  stepNumber++;
284
360
  steps.push(step);
@@ -320,7 +396,8 @@ export async function* streamTextIterator({
320
396
  toolCalls,
321
397
  messages: conversationPrompt,
322
398
  step,
323
- context: currentContext,
399
+ runtimeContext: currentRuntimeContext,
400
+ toolsContext: currentToolsContext,
324
401
  providerExecutedToolResults,
325
402
  };
326
403
 
@@ -375,9 +452,11 @@ export async function* streamTextIterator({
375
452
  );
376
453
  }
377
454
 
378
- if (onStepFinish) {
379
- await onStepFinish(step);
455
+ const resolvedOnStepEnd = onStepEnd ?? onStepFinish;
456
+ if (resolvedOnStepEnd) {
457
+ await resolvedOnStepEnd(step);
380
458
  }
459
+ await telemetryDispatcher.onStepEnd?.(normalizeStepForTelemetry(step));
381
460
  } catch (error) {
382
461
  if (onError) {
383
462
  await onError({ error });
@@ -392,24 +471,28 @@ export async function* streamTextIterator({
392
471
  toolCalls: [],
393
472
  messages: conversationPrompt,
394
473
  step: lastStep,
395
- context: currentContext,
474
+ runtimeContext: currentRuntimeContext,
475
+ toolsContext: currentToolsContext,
396
476
  };
397
477
  }
398
478
 
399
479
  return conversationPrompt;
400
480
  }
401
481
 
402
- /**
403
- * Filter a tool set to only include the specified active tools.
404
- */
405
- function filterToolSet(tools: ToolSet, activeTools: string[]): ToolSet {
406
- const filtered: ToolSet = {};
407
- for (const toolName of activeTools) {
408
- if (toolName in tools) {
409
- filtered[toolName] = tools[toolName];
410
- }
411
- }
412
- return filtered;
482
+ function getModelInfo(model: LanguageModel): {
483
+ provider: string;
484
+ modelId: string;
485
+ } {
486
+ return typeof model === 'string'
487
+ ? { provider: model.split('/')[0] ?? 'gateway', modelId: model }
488
+ : { provider: model.provider, modelId: model.modelId };
489
+ }
490
+
491
+ function normalizeStepForTelemetry(step: StepResult<any, any>) {
492
+ return {
493
+ ...step,
494
+ model: step.model ?? { provider: 'unknown', modelId: 'unknown' },
495
+ };
413
496
  }
414
497
 
415
498
  /**
@@ -5,7 +5,7 @@ import { tool } from 'ai';
5
5
  import { WorkflowAgent } from '../workflow-agent.js';
6
6
  import { mockTextModel, mockSequenceModel } from '../providers/mock.js';
7
7
  import { FatalError, getWritable } from 'workflow';
8
- import z from 'zod';
8
+ import { z } from 'zod';
9
9
 
10
10
  // ============================================================================
11
11
  // Tool step functions
@@ -334,10 +334,10 @@ export async function agentOnStepStartE2e() {
334
334
  }
335
335
 
336
336
  // ============================================================================
337
- // GAP tests — experimental_onToolCallStart
337
+ // GAP tests — onToolExecutionStart
338
338
  // ============================================================================
339
339
 
340
- export async function agentOnToolCallStartE2e() {
340
+ export async function agentonToolExecutionStartE2e() {
341
341
  'use workflow';
342
342
  const calls: string[] = [];
343
343
  const agent = new WorkflowAgent({
@@ -356,14 +356,14 @@ export async function agentOnToolCallStartE2e() {
356
356
  execute: echoStep,
357
357
  },
358
358
  },
359
- experimental_onToolCallStart: async () => {
359
+ onToolExecutionStart: async () => {
360
360
  calls.push('constructor');
361
361
  },
362
362
  } as any);
363
363
  await agent.stream({
364
364
  messages: [{ role: 'user', content: 'test' }],
365
365
  writable: getWritable(),
366
- experimental_onToolCallStart: async () => {
366
+ onToolExecutionStart: async () => {
367
367
  calls.push('method');
368
368
  },
369
369
  } as any);
@@ -371,10 +371,10 @@ export async function agentOnToolCallStartE2e() {
371
371
  }
372
372
 
373
373
  // ============================================================================
374
- // GAP tests — experimental_onToolCallFinish
374
+ // GAP tests — onToolExecutionEnd
375
375
  // ============================================================================
376
376
 
377
- export async function agentOnToolCallFinishE2e() {
377
+ export async function agentonToolExecutionEndE2e() {
378
378
  'use workflow';
379
379
  const calls: string[] = [];
380
380
  let capturedEvent: any = null;
@@ -394,14 +394,14 @@ export async function agentOnToolCallFinishE2e() {
394
394
  execute: addNumbers,
395
395
  },
396
396
  },
397
- experimental_onToolCallFinish: async () => {
397
+ onToolExecutionEnd: async () => {
398
398
  calls.push('constructor');
399
399
  },
400
400
  } as any);
401
401
  await agent.stream({
402
402
  messages: [{ role: 'user', content: 'test' }],
403
403
  writable: getWritable(),
404
- experimental_onToolCallFinish: async (event: any) => {
404
+ onToolExecutionEnd: async (event: any) => {
405
405
  calls.push('method');
406
406
  capturedEvent = {
407
407
  toolName: event?.toolCall?.toolName,
@@ -505,3 +505,83 @@ export async function agentToolInputSchemaE2e(a: number, b: number) {
505
505
  lastStepText: result.steps[result.steps.length - 1]?.text,
506
506
  };
507
507
  }
508
+
509
+ // ============================================================================
510
+ // runtimeContext + toolsContext (end-to-end)
511
+ // ============================================================================
512
+
513
+ /**
514
+ * Demonstrates the full context flow:
515
+ *
516
+ * - `runtimeContext` holds shared agent state (`tenantId`, `requestId`).
517
+ * `prepareStep` reads it and tags it with the current step number;
518
+ * `onFinish` receives the final value.
519
+ * - `toolsContext` holds per-tool, schema-validated context. The
520
+ * `lookupCustomer` tool declares `contextSchema`, so its entry is
521
+ * validated and the tool's `execute` only sees its own context.
522
+ */
523
+ export async function agentRuntimeAndToolsContextE2e() {
524
+ 'use workflow';
525
+
526
+ let onFinishRuntimeContext: Record<string, unknown> | undefined;
527
+ let onFinishToolsContext: Record<string, unknown> | undefined;
528
+ let toolReceivedContext: unknown;
529
+
530
+ const agent = new WorkflowAgent({
531
+ model: mockSequenceModel([
532
+ {
533
+ type: 'tool-call',
534
+ toolName: 'lookupCustomer',
535
+ input: JSON.stringify({ customerId: 'cust_123' }),
536
+ },
537
+ { type: 'text', text: 'Customer cust_123 is eligible.' },
538
+ ]),
539
+ tools: {
540
+ lookupCustomer: tool({
541
+ description: 'Look up customer account details.',
542
+ inputSchema: z.object({ customerId: z.string() }),
543
+ contextSchema: z.object({
544
+ apiKey: z.string(),
545
+ region: z.enum(['us', 'eu']),
546
+ }),
547
+ execute: async (input, { context }) => {
548
+ toolReceivedContext = context;
549
+ return { customerId: input.customerId, eligible: true };
550
+ },
551
+ }),
552
+ },
553
+ instructions: 'You look up customers.',
554
+ runtimeContext: {
555
+ tenantId: 'tenant_123',
556
+ requestId: 'req_abc',
557
+ },
558
+ toolsContext: {
559
+ lookupCustomer: {
560
+ apiKey: 'sk-test-key',
561
+ region: 'us',
562
+ },
563
+ },
564
+ prepareStep: ({ stepNumber, runtimeContext }) => ({
565
+ runtimeContext: { ...runtimeContext, lastStep: stepNumber },
566
+ }),
567
+ onFinish: ({ runtimeContext, toolsContext }) => {
568
+ onFinishRuntimeContext = runtimeContext;
569
+ onFinishToolsContext = toolsContext;
570
+ },
571
+ });
572
+
573
+ const result = await agent.stream({
574
+ messages: [
575
+ { role: 'user', content: 'Is customer cust_123 eligible for support?' },
576
+ ],
577
+ writable: getWritable(),
578
+ });
579
+
580
+ return {
581
+ stepCount: result.steps.length,
582
+ lastStepText: result.steps[result.steps.length - 1]?.text,
583
+ toolReceivedContext,
584
+ onFinishRuntimeContext,
585
+ onFinishToolsContext,
586
+ };
587
+ }
@@ -1,5 +1,8 @@
1
- import { type ToolSet, type UIMessageChunk } from 'ai';
2
- import type { Experimental_LanguageModelStreamPart as ModelCallStreamPart } from 'ai';
1
+ import type {
2
+ Experimental_LanguageModelStreamPart as ModelCallStreamPart,
3
+ ToolSet,
4
+ UIMessageChunk,
5
+ } from 'ai';
3
6
 
4
7
  /**
5
8
  * Convert a single ModelCallStreamPart to a UIMessageChunk.
@@ -188,20 +191,20 @@ export function toUIMessageChunk(
188
191
  // standard ModelCallStreamPart types but are written by the
189
192
  // WorkflowAgent between tool execution and the next model step
190
193
  // to ensure proper message splitting in convertToModelMessages.
191
- const p = part as any;
192
- if (p.type === 'tool-approval-request') {
194
+ const passthroughPart = part as any;
195
+ if (passthroughPart.type === 'tool-approval-request') {
193
196
  return {
194
197
  type: 'tool-approval-request',
195
- approvalId: p.approvalId,
196
- toolCallId: p.toolCallId,
198
+ approvalId: passthroughPart.approvalId,
199
+ toolCallId: passthroughPart.toolCallId,
197
200
  } as UIMessageChunk;
198
201
  }
199
202
  if (
200
- p.type === 'finish-step' ||
201
- p.type === 'start-step' ||
202
- p.type === 'tool-output-denied'
203
+ passthroughPart.type === 'finish-step' ||
204
+ passthroughPart.type === 'start-step' ||
205
+ passthroughPart.type === 'tool-output-denied'
203
206
  ) {
204
- return p as UIMessageChunk;
207
+ return passthroughPart as UIMessageChunk;
205
208
  }
206
209
  return undefined;
207
210
  }