@ai-sdk/workflow 1.0.0-beta.8 → 1.0.0-beta.94

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
- LanguageModel,
9
- ModelMessage,
10
- StepResult,
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,
@@ -23,8 +26,9 @@ import type {
23
26
  GenerationSettings,
24
27
  PrepareStepCallback,
25
28
  WorkflowAgentOnErrorCallback,
29
+ WorkflowAgentOnStepEndCallback,
26
30
  WorkflowAgentOnStepFinishCallback,
27
- TelemetrySettings,
31
+ TelemetryOptions,
28
32
  WorkflowAgentOnStepStartCallback,
29
33
  } from './workflow-agent.js';
30
34
 
@@ -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,
@@ -73,15 +80,17 @@ export async function* streamTextIterator({
73
80
  writable?: WritableStream<ModelCallStreamPart<ToolSet>>;
74
81
  model: LanguageModel;
75
82
  stopConditions?: ModelStopCondition[] | ModelStopCondition;
76
- maxSteps?: number;
83
+ onStepEnd?: WorkflowAgentOnStepEndCallback<any>;
84
+ /** @deprecated Use `onStepEnd` instead. */
77
85
  onStepFinish?: WorkflowAgentOnStepFinishCallback<any>;
78
86
  onStepStart?: WorkflowAgentOnStepStartCallback;
79
87
  onError?: WorkflowAgentOnErrorCallback;
80
88
  prepareStep?: PrepareStepCallback<any>;
81
89
  generationSettings?: GenerationSettings;
82
90
  toolChoice?: ToolChoice<ToolSet>;
83
- experimental_context?: unknown;
84
- experimental_telemetry?: TelemetrySettings;
91
+ runtimeContext?: Context;
92
+ toolsContext?: Record<string, Context | undefined>;
93
+ telemetry?: TelemetryOptions<Context, ToolSet>;
85
94
  includeRawChunks?: boolean;
86
95
  repairToolCall?: ToolCallRepairFunction<ToolSet>;
87
96
  responseFormat?: LanguageModelV4CallOptions['responseFormat'];
@@ -94,7 +103,9 @@ export async function* streamTextIterator({
94
103
  let currentModel: LanguageModel = model;
95
104
  let currentGenerationSettings = generationSettings ?? {};
96
105
  let currentToolChoice = toolChoice;
97
- let currentContext = experimental_context;
106
+ let currentRuntimeContext: Context = runtimeContext ?? {};
107
+ let currentToolsContext: Record<string, Context | undefined> =
108
+ toolsContext ?? {};
98
109
  let currentActiveTools: string[] | undefined;
99
110
 
100
111
  const steps: StepResult<any, any>[] = [];
@@ -104,16 +115,21 @@ export async function* streamTextIterator({
104
115
  let lastStep: StepResult<any, any> | undefined;
105
116
  let lastStepWasToolCalls = false;
106
117
 
107
- // Default maxSteps to Infinity to preserve backwards compatibility
108
- // (agent loops until completion unless explicitly limited)
109
- 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;
110
131
 
111
132
  while (!done) {
112
- // Check if we've exceeded the maximum number of steps
113
- if (stepNumber >= effectiveMaxSteps) {
114
- break;
115
- }
116
-
117
133
  // Check for abort signal
118
134
  if (currentGenerationSettings.abortSignal?.aborted) {
119
135
  break;
@@ -126,19 +142,20 @@ export async function* streamTextIterator({
126
142
  stepNumber,
127
143
  steps,
128
144
  messages: conversationPrompt,
129
- experimental_context: currentContext,
145
+ runtimeContext: currentRuntimeContext,
146
+ toolsContext: currentToolsContext as never,
130
147
  });
131
148
 
132
149
  // Apply any overrides from prepareStep
133
- if (prepareResult.model !== undefined) {
150
+ if (prepareResult?.model !== undefined) {
134
151
  currentModel = prepareResult.model;
135
152
  }
136
153
  // Apply messages override BEFORE system so the system message
137
154
  // isn't lost when messages replaces the prompt.
138
- if (prepareResult.messages !== undefined) {
155
+ if (prepareResult?.messages !== undefined) {
139
156
  conversationPrompt = [...prepareResult.messages];
140
157
  }
141
- if (prepareResult.system !== undefined) {
158
+ if (prepareResult?.system !== undefined) {
142
159
  // Update or prepend system message in the conversation prompt.
143
160
  // Applied AFTER messages override so the system message isn't
144
161
  // lost when messages replaces the prompt.
@@ -159,80 +176,86 @@ export async function* streamTextIterator({
159
176
  });
160
177
  }
161
178
  }
162
- if (prepareResult.experimental_context !== undefined) {
163
- currentContext = prepareResult.experimental_context;
179
+ if (prepareResult?.runtimeContext !== undefined) {
180
+ currentRuntimeContext = prepareResult.runtimeContext;
164
181
  }
165
- 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) {
166
189
  currentActiveTools = prepareResult.activeTools;
167
190
  }
168
191
  // Apply generation settings overrides
169
- if (prepareResult.maxOutputTokens !== undefined) {
192
+ if (prepareResult?.maxOutputTokens !== undefined) {
170
193
  currentGenerationSettings = {
171
194
  ...currentGenerationSettings,
172
195
  maxOutputTokens: prepareResult.maxOutputTokens,
173
196
  };
174
197
  }
175
- if (prepareResult.temperature !== undefined) {
198
+ if (prepareResult?.temperature !== undefined) {
176
199
  currentGenerationSettings = {
177
200
  ...currentGenerationSettings,
178
201
  temperature: prepareResult.temperature,
179
202
  };
180
203
  }
181
- if (prepareResult.topP !== undefined) {
204
+ if (prepareResult?.topP !== undefined) {
182
205
  currentGenerationSettings = {
183
206
  ...currentGenerationSettings,
184
207
  topP: prepareResult.topP,
185
208
  };
186
209
  }
187
- if (prepareResult.topK !== undefined) {
210
+ if (prepareResult?.topK !== undefined) {
188
211
  currentGenerationSettings = {
189
212
  ...currentGenerationSettings,
190
213
  topK: prepareResult.topK,
191
214
  };
192
215
  }
193
- if (prepareResult.presencePenalty !== undefined) {
216
+ if (prepareResult?.presencePenalty !== undefined) {
194
217
  currentGenerationSettings = {
195
218
  ...currentGenerationSettings,
196
219
  presencePenalty: prepareResult.presencePenalty,
197
220
  };
198
221
  }
199
- if (prepareResult.frequencyPenalty !== undefined) {
222
+ if (prepareResult?.frequencyPenalty !== undefined) {
200
223
  currentGenerationSettings = {
201
224
  ...currentGenerationSettings,
202
225
  frequencyPenalty: prepareResult.frequencyPenalty,
203
226
  };
204
227
  }
205
- if (prepareResult.stopSequences !== undefined) {
228
+ if (prepareResult?.stopSequences !== undefined) {
206
229
  currentGenerationSettings = {
207
230
  ...currentGenerationSettings,
208
231
  stopSequences: prepareResult.stopSequences,
209
232
  };
210
233
  }
211
- if (prepareResult.seed !== undefined) {
234
+ if (prepareResult?.seed !== undefined) {
212
235
  currentGenerationSettings = {
213
236
  ...currentGenerationSettings,
214
237
  seed: prepareResult.seed,
215
238
  };
216
239
  }
217
- if (prepareResult.maxRetries !== undefined) {
240
+ if (prepareResult?.maxRetries !== undefined) {
218
241
  currentGenerationSettings = {
219
242
  ...currentGenerationSettings,
220
243
  maxRetries: prepareResult.maxRetries,
221
244
  };
222
245
  }
223
- if (prepareResult.headers !== undefined) {
246
+ if (prepareResult?.headers !== undefined) {
224
247
  currentGenerationSettings = {
225
248
  ...currentGenerationSettings,
226
249
  headers: prepareResult.headers,
227
250
  };
228
251
  }
229
- if (prepareResult.providerOptions !== undefined) {
252
+ if (prepareResult?.providerOptions !== undefined) {
230
253
  currentGenerationSettings = {
231
254
  ...currentGenerationSettings,
232
255
  providerOptions: prepareResult.providerOptions,
233
256
  };
234
257
  }
235
- if (prepareResult.toolChoice !== undefined) {
258
+ if (prepareResult?.toolChoice !== undefined) {
236
259
  currentToolChoice = prepareResult.toolChoice;
237
260
  }
238
261
  }
@@ -243,20 +266,66 @@ export async function* streamTextIterator({
243
266
  model: currentModel,
244
267
  messages: conversationPrompt as unknown as ModelMessage[],
245
268
  steps: [...steps],
269
+ runtimeContext: currentRuntimeContext,
270
+ toolsContext: currentToolsContext as never,
246
271
  });
247
272
  }
248
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
+
249
292
  try {
250
293
  // Filter tools if activeTools is specified
251
294
  const effectiveTools =
252
295
  currentActiveTools && currentActiveTools.length > 0
253
- ? filterToolSet(tools, currentActiveTools)
296
+ ? (filterActiveTools({
297
+ tools,
298
+ activeTools: currentActiveTools,
299
+ }) ?? tools)
254
300
  : tools;
255
301
 
256
302
  // Serialize tools before crossing the step boundary — zod schemas
257
303
  // contain functions that can't be serialized by the workflow runtime.
258
304
  // Tools are reconstructed with Ajv validation inside doStreamStep.
259
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);
260
329
 
261
330
  const { toolCalls, finish, step, providerExecutedToolResults } =
262
331
  await doStreamStep(
@@ -268,12 +337,24 @@ export async function* streamTextIterator({
268
337
  ...currentGenerationSettings,
269
338
  toolChoice: currentToolChoice,
270
339
  includeRawChunks,
271
- experimental_telemetry,
272
340
  repairToolCall,
273
341
  responseFormat,
342
+ runtimeContext: currentRuntimeContext,
343
+ toolsContext: currentToolsContext,
344
+ stepNumber,
274
345
  },
275
346
  );
276
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
+
277
358
  _isFirstIteration = false;
278
359
  stepNumber++;
279
360
  steps.push(step);
@@ -315,7 +396,8 @@ export async function* streamTextIterator({
315
396
  toolCalls,
316
397
  messages: conversationPrompt,
317
398
  step,
318
- context: currentContext,
399
+ runtimeContext: currentRuntimeContext,
400
+ toolsContext: currentToolsContext,
319
401
  providerExecutedToolResults,
320
402
  };
321
403
 
@@ -370,9 +452,11 @@ export async function* streamTextIterator({
370
452
  );
371
453
  }
372
454
 
373
- if (onStepFinish) {
374
- await onStepFinish(step);
455
+ const resolvedOnStepEnd = onStepEnd ?? onStepFinish;
456
+ if (resolvedOnStepEnd) {
457
+ await resolvedOnStepEnd(step);
375
458
  }
459
+ await telemetryDispatcher.onStepEnd?.(normalizeStepForTelemetry(step));
376
460
  } catch (error) {
377
461
  if (onError) {
378
462
  await onError({ error });
@@ -387,24 +471,28 @@ export async function* streamTextIterator({
387
471
  toolCalls: [],
388
472
  messages: conversationPrompt,
389
473
  step: lastStep,
390
- context: currentContext,
474
+ runtimeContext: currentRuntimeContext,
475
+ toolsContext: currentToolsContext,
391
476
  };
392
477
  }
393
478
 
394
479
  return conversationPrompt;
395
480
  }
396
481
 
397
- /**
398
- * Filter a tool set to only include the specified active tools.
399
- */
400
- function filterToolSet(tools: ToolSet, activeTools: string[]): ToolSet {
401
- const filtered: ToolSet = {};
402
- for (const toolName of activeTools) {
403
- if (toolName in tools) {
404
- filtered[toolName] = tools[toolName];
405
- }
406
- }
407
- 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
+ };
408
496
  }
409
497
 
410
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
  }