@ai-sdk/workflow 2.0.72 → 2.0.74

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": "2.0.72",
3
+ "version": "2.0.74",
4
4
  "type": "module",
5
5
  "description": "WorkflowAgent for building AI agents with AI SDK",
6
6
  "license": "Apache-2.0",
@@ -36,13 +36,13 @@
36
36
  }
37
37
  },
38
38
  "dependencies": {
39
- "@ai-sdk/provider": "4.0.27",
40
- "@ai-sdk/provider-utils": "5.0.59",
41
- "ai": "7.0.140",
39
+ "@ai-sdk/provider": "4.0.28",
40
+ "@ai-sdk/provider-utils": "5.0.61",
41
+ "ai": "7.0.142",
42
42
  "ajv": "^8.20.0"
43
43
  },
44
44
  "devDependencies": {
45
- "@ai-sdk/otel": "1.0.140",
45
+ "@ai-sdk/otel": "1.0.142",
46
46
  "@opentelemetry/api": "1.9.1",
47
47
  "@opentelemetry/sdk-trace-base": "2.7.1",
48
48
  "@types/node": "22.19.19",
@@ -42,7 +42,12 @@ export async function buildModelStepResult(
42
42
  break;
43
43
  case 'tool-call': {
44
44
  const call = toolCalls[part.toolCallIndex];
45
- providerContent.push({ ...call, input: '', type: 'tool-call' });
45
+ const { inputSchemaInput: _inputSchemaInput, ...publicCall } = call;
46
+ providerContent.push({
47
+ ...publicCall,
48
+ input: '',
49
+ type: 'tool-call',
50
+ });
46
51
  break;
47
52
  }
48
53
  case 'provider-tool-result':
@@ -162,6 +162,17 @@ async function generateModelCall(
162
162
  performance: { responseTimeMs },
163
163
  });
164
164
  abortSignal?.throwIfAborted();
165
+ const repairedToolCallIds = new Set<string>();
166
+ const repairToolCall = options.repairToolCall;
167
+ const trackedRepairToolCall = repairToolCall
168
+ ? async (repairOptions: Parameters<typeof repairToolCall>[0]) => {
169
+ const repaired = await repairToolCall(repairOptions);
170
+ if (repaired != null) {
171
+ repairedToolCallIds.add(repairOptions.toolCall.toolCallId);
172
+ }
173
+ return repaired;
174
+ }
175
+ : undefined;
165
176
  const toolCalls = await Promise.all(
166
177
  response.content
167
178
  .filter(part => part.type === 'tool-call')
@@ -169,7 +180,9 @@ async function generateModelCall(
169
180
  parseToolCall({
170
181
  toolCall,
171
182
  tools,
172
- repairToolCall: options.repairToolCall,
183
+ repairToolCall: trackedRepairToolCall as Parameters<
184
+ typeof parseToolCall
185
+ >[0]['repairToolCall'],
173
186
  instructions:
174
187
  modelPrompt
175
188
  .filter(message => message.role === 'system')
@@ -269,6 +282,7 @@ async function generateModelCall(
269
282
  },
270
283
  providerExecutedToolResults,
271
284
  toolInputLifecycleEvents,
285
+ repairedToolCallIds: [...repairedToolCallIds],
272
286
  };
273
287
  }
274
288
 
@@ -116,6 +116,17 @@ async function streamModelStep(
116
116
  // Tools are serialized before crossing the step boundary because zod schemas
117
117
  // contain functions that can't be serialized by the workflow runtime.
118
118
  const toolInputLifecycleEvents: ToolInputLifecycleEvent[] = [];
119
+ const repairedToolCallIds = new Set<string>();
120
+ const repairToolCall = options?.repairToolCall;
121
+ const trackedRepairToolCall = repairToolCall
122
+ ? async (repairOptions: Parameters<typeof repairToolCall>[0]) => {
123
+ const repaired = await repairToolCall(repairOptions);
124
+ if (repaired != null) {
125
+ repairedToolCallIds.add(repairOptions.toolCall.toolCallId);
126
+ }
127
+ return repaired;
128
+ }
129
+ : undefined;
119
130
  const tools = serializedTools
120
131
  ? resolveSerializableTools(serializedTools)
121
132
  : undefined;
@@ -196,7 +207,7 @@ async function streamModelStep(
196
207
  frequencyPenalty: options?.frequencyPenalty,
197
208
  stopSequences: options?.stopSequences,
198
209
  seed: options?.seed,
199
- repairToolCall: options?.repairToolCall,
210
+ repairToolCall: trackedRepairToolCall,
200
211
  download: options?.download,
201
212
  ...languageModelCallTelemetry,
202
213
  }),
@@ -449,7 +460,21 @@ async function streamModelStep(
449
460
  toolCallId: part.toolCall.toolCallId,
450
461
  ...(part.signature != null ? { signature: part.signature } : {}),
451
462
  });
452
- } else {
463
+ } else if (
464
+ // The original schema must validate and normalize these calls outside
465
+ // the step before they become available to UI stream consumers. The
466
+ // matching `tool-error` for an invalid call (emitted by
467
+ // streamModelCall immediately after the `tool-call`) is deferred as
468
+ // well, so `writeDeferredToolCalls` stays the sole emitter.
469
+ // Otherwise the UI stream would receive a duplicate `tool-error`
470
+ // chunk, and the one written here would arrive before its deferred
471
+ // `tool-call`.
472
+ !(
473
+ (part.type === 'tool-call' ||
474
+ (part.type === 'tool-error' && !part.providerExecuted)) &&
475
+ serializedTools?.[part.toolName]?.hasOwnValidator
476
+ )
477
+ ) {
453
478
  await writer.write(part);
454
479
  }
455
480
  }
@@ -500,6 +525,7 @@ async function streamModelStep(
500
525
  },
501
526
  providerExecutedToolResults,
502
527
  toolInputLifecycleEvents,
528
+ repairedToolCallIds: [...repairedToolCallIds],
503
529
  ...(hasTerminalError ? { terminalError } : {}),
504
530
  };
505
531
  }
@@ -1,12 +1,20 @@
1
1
  import type {
2
2
  LanguageModelV4CallOptions,
3
3
  LanguageModelV4Prompt,
4
+ LanguageModelV4ToolCall,
4
5
  LanguageModelV4ToolResultPart,
5
6
  SharedV4ProviderOptions,
6
7
  } from '@ai-sdk/provider';
7
- import type { Context } from '@ai-sdk/provider-utils';
8
+ import {
9
+ asSchema,
10
+ getErrorMessage,
11
+ safeParseJSON,
12
+ type Context,
13
+ } from '@ai-sdk/provider-utils';
8
14
  import {
9
15
  experimental_filterActiveTools as filterActiveTools,
16
+ InvalidToolInputError,
17
+ isDeepEqualData,
10
18
  type ActiveTools,
11
19
  type Experimental_SandboxSession as SandboxSession,
12
20
  type Instructions,
@@ -20,6 +28,8 @@ import {
20
28
  import {
21
29
  createRestrictedTelemetryDispatcher,
22
30
  createToolSearchState,
31
+ filterIncludedContext,
32
+ parseToolCall,
23
33
  } from 'ai/internal';
24
34
  import { buildModelStepResult } from './build-model-step-result.js';
25
35
  import { doGenerateStep } from './do-generate-step.js';
@@ -39,7 +49,11 @@ import {
39
49
  } from './add-tool-results-to-conversation.js';
40
50
  import { toModelResponseMessages } from './to-model-response-messages.js';
41
51
  import { resolveToolContext } from './resolve-tool-context.js';
42
- import { serializeToolSet } from './serializable-schema.js';
52
+ import {
53
+ resolveSerializableTools,
54
+ serializeToolSet,
55
+ type SerializableToolDef,
56
+ } from './serializable-schema.js';
43
57
  import type {
44
58
  GenerationSettings,
45
59
  PrepareStepCallback,
@@ -392,6 +406,12 @@ export async function* modelCallIterator({
392
406
  recordInputs: telemetry?.recordInputs,
393
407
  recordOutputs: telemetry?.recordOutputs,
394
408
  stepNumber,
409
+ includeRuntimeContext: telemetry?.includeRuntimeContext,
410
+ // Exclude unselected fields before serializing the step input.
411
+ runtimeContext: filterIncludedContext({
412
+ context: currentRuntimeContext,
413
+ includeContext: telemetry?.includeRuntimeContext,
414
+ }),
395
415
  }
396
416
  : undefined,
397
417
  };
@@ -423,12 +443,41 @@ export async function* modelCallIterator({
423
443
  }
424
444
 
425
445
  const {
426
- toolCalls,
446
+ toolCalls: serializedToolCalls,
427
447
  finish,
428
448
  raw,
429
449
  providerExecutedToolResults,
430
450
  toolInputLifecycleEvents,
451
+ repairedToolCallIds,
431
452
  } = modelCallResult;
453
+ const toolCalls = await validateToolCallInputs({
454
+ toolCalls: serializedToolCalls,
455
+ tools: effectiveTools,
456
+ serializedTools,
457
+ repairToolCall,
458
+ repairedToolCallIds: new Set(repairedToolCallIds),
459
+ instructions:
460
+ conversationPrompt
461
+ .filter(message => message.role === 'system')
462
+ .map(message => message.content)
463
+ .join('\n') || initialInstructions,
464
+ messages: stepInputMessages.filter(
465
+ message => message.role !== 'system',
466
+ ),
467
+ abortSignal: currentGenerationSettings.abortSignal,
468
+ });
469
+ if (mode === 'stream' && writable != null) {
470
+ // Replace the tool-call chunk deferred inside the model step with the
471
+ // original schema's validated, normalized, or repaired result.
472
+ await writeDeferredToolCalls(
473
+ writable,
474
+ toolCalls.filter(
475
+ (_, index) =>
476
+ serializedTools[serializedToolCalls[index].toolName]
477
+ ?.hasOwnValidator,
478
+ ),
479
+ );
480
+ }
432
481
  await invokeToolInputLifecycleCallbacks({
433
482
  events: toolInputLifecycleEvents ?? [],
434
483
  toolCalls,
@@ -664,6 +713,172 @@ export async function* modelCallIterator({
664
713
  return conversationPrompt;
665
714
  }
666
715
 
716
+ async function validateToolCallInputs({
717
+ toolCalls,
718
+ tools,
719
+ serializedTools,
720
+ repairToolCall,
721
+ repairedToolCallIds,
722
+ instructions,
723
+ messages,
724
+ abortSignal,
725
+ }: {
726
+ toolCalls: ParsedToolCall[];
727
+ tools: ToolSet;
728
+ serializedTools: Record<string, SerializableToolDef>;
729
+ repairToolCall?: ToolCallRepairFunction<ToolSet>;
730
+ repairedToolCallIds: Set<string>;
731
+ instructions?: Instructions;
732
+ messages: ModelMessage[];
733
+ abortSignal?: AbortSignal;
734
+ }): Promise<ParsedToolCall[]> {
735
+ return Promise.all(
736
+ toolCalls.map(async toolCall => {
737
+ if (toolCall.invalid || toolCall.providerExecuted) {
738
+ return toolCall;
739
+ }
740
+
741
+ const tool = tools[toolCall.toolName];
742
+ if (tool == null) {
743
+ return toolCall;
744
+ }
745
+
746
+ const inputSchema = asSchema(tool.inputSchema);
747
+ if (inputSchema.validate == null) {
748
+ return toolCall;
749
+ }
750
+
751
+ let repairedInput: { value: unknown } | undefined;
752
+ const parsedToolCall = await parseToolCall({
753
+ toolCall: {
754
+ type: 'tool-call',
755
+ toolCallId: toolCall.toolCallId,
756
+ toolName: toolCall.toolName,
757
+ input:
758
+ JSON.stringify(toolCall.input) ??
759
+ (toolCall.input == null ? '' : String(toolCall.input)),
760
+ providerExecuted: toolCall.providerExecuted,
761
+ providerMetadata: toolCall.providerMetadata,
762
+ } satisfies LanguageModelV4ToolCall,
763
+ tools,
764
+ // A call that was already repaired inside the model step is not
765
+ // repaired again, matching parseToolCall's single repair attempt.
766
+ repairToolCall:
767
+ repairToolCall == null || repairedToolCallIds.has(toolCall.toolCallId)
768
+ ? undefined
769
+ : async options => {
770
+ const repairedToolCall = await repairToolCallInStep({
771
+ repairToolCall,
772
+ serializedTools,
773
+ toolCall: options.toolCall,
774
+ error: {
775
+ toolName: options.toolCall.toolName,
776
+ toolInput: options.toolCall.input,
777
+ message: getErrorMessage(options.error),
778
+ },
779
+ instructions,
780
+ messages: options.messages,
781
+ });
782
+ if (repairedToolCall != null) {
783
+ const parsedRepairedInput =
784
+ repairedToolCall.input.trim() === ''
785
+ ? { success: true as const, value: {} }
786
+ : await safeParseJSON({
787
+ text: repairedToolCall.input,
788
+ });
789
+ if (parsedRepairedInput.success) {
790
+ repairedInput = { value: parsedRepairedInput.value };
791
+ }
792
+ }
793
+ return repairedToolCall;
794
+ },
795
+ instructions,
796
+ messages,
797
+ abortSignal,
798
+ });
799
+
800
+ const inputSchemaInput = repairedInput ?? { value: toolCall.input };
801
+
802
+ return {
803
+ ...toolCall,
804
+ ...parsedToolCall,
805
+ ...(!parsedToolCall.invalid &&
806
+ !isDeepEqualData(inputSchemaInput.value, parsedToolCall.input)
807
+ ? { inputSchemaInput: inputSchemaInput.value }
808
+ : {}),
809
+ };
810
+ }),
811
+ );
812
+ }
813
+
814
+ /**
815
+ * Runs `repairToolCall` inside a step. The callback may be a `'use step'`
816
+ * function, so its arguments must be serializable: the tools and the
817
+ * `inputSchema` accessor are rebuilt here instead of crossing the boundary
818
+ * from the workflow body.
819
+ */
820
+ async function repairToolCallInStep({
821
+ repairToolCall,
822
+ serializedTools,
823
+ toolCall,
824
+ error,
825
+ instructions,
826
+ messages,
827
+ }: {
828
+ repairToolCall: ToolCallRepairFunction<ToolSet>;
829
+ serializedTools: Record<string, SerializableToolDef>;
830
+ toolCall: LanguageModelV4ToolCall;
831
+ error: { toolName: string; toolInput: string; message: string };
832
+ instructions?: Instructions;
833
+ messages: ModelMessage[];
834
+ }): Promise<LanguageModelV4ToolCall | null> {
835
+ 'use step';
836
+ const tools = resolveSerializableTools(serializedTools);
837
+ return repairToolCall({
838
+ toolCall,
839
+ tools,
840
+ inputSchema: async ({ toolName }) =>
841
+ asSchema(tools[toolName]?.inputSchema).jsonSchema,
842
+ instructions,
843
+ system: instructions,
844
+ messages,
845
+ // Errors are passed as plain data and rebuilt so that
846
+ // `InvalidToolInputError.isInstance` keeps working in the callback.
847
+ error: new InvalidToolInputError({
848
+ toolName: error.toolName,
849
+ toolInput: error.toolInput,
850
+ cause: new Error(error.message),
851
+ message: error.message,
852
+ }),
853
+ });
854
+ }
855
+
856
+ async function writeDeferredToolCalls(
857
+ writable: WritableStream<ModelCallStreamPart<ToolSet>>,
858
+ toolCalls: ParsedToolCall[],
859
+ ) {
860
+ 'use step';
861
+ const writer = writable.getWriter();
862
+ try {
863
+ for (const toolCall of toolCalls) {
864
+ await writer.write(toolCall as ModelCallStreamPart<ToolSet>);
865
+ // Match streamModelCall, which follows an invalid call with a tool-error.
866
+ if (toolCall.invalid && !toolCall.providerExecuted) {
867
+ await writer.write({
868
+ type: 'tool-error',
869
+ toolCallId: toolCall.toolCallId,
870
+ toolName: toolCall.toolName,
871
+ input: toolCall.input,
872
+ error: getErrorMessage(toolCall.error),
873
+ dynamic: true,
874
+ } as ModelCallStreamPart<ToolSet>);
875
+ }
876
+ }
877
+ } finally {
878
+ writer.releaseLock();
879
+ }
880
+ }
881
+
667
882
  async function invokeToolInputLifecycleCallbacks({
668
883
  events,
669
884
  toolCalls,
@@ -734,7 +949,7 @@ async function invokeToolInputLifecycleCallbacks({
734
949
  break;
735
950
  case 'available': {
736
951
  const toolCall = toolCallsById.get(toolCallId);
737
- if (toolCall == null) {
952
+ if (toolCall == null || toolCall.invalid) {
738
953
  break;
739
954
  }
740
955
  await tool.onInputAvailable?.({
@@ -104,7 +104,7 @@ export async function runModelCallWithTelemetry({
104
104
  reasoning: options?.reasoning,
105
105
  providerOptions: options?.providerOptions,
106
106
  output: undefined,
107
- runtimeContext: {},
107
+ runtimeContext: telemetry.runtimeContext ?? {},
108
108
  toolsContext: {},
109
109
  } as never);
110
110
  await dispatcher.onStepStart?.({
@@ -120,7 +120,7 @@ export async function runModelCallWithTelemetry({
120
120
  steps: [],
121
121
  providerOptions: options?.providerOptions,
122
122
  output: undefined,
123
- runtimeContext: {},
123
+ runtimeContext: telemetry.runtimeContext ?? {},
124
124
  toolsContext: {},
125
125
  } as never);
126
126
 
@@ -157,7 +157,7 @@ export async function runModelCallWithTelemetry({
157
157
  callId,
158
158
  tools,
159
159
  stepNumber: telemetry.stepNumber,
160
- runtimeContext: {},
160
+ runtimeContext: telemetry.runtimeContext ?? {},
161
161
  toolsContext: {},
162
162
  },
163
163
  );
package/src/model-call.ts CHANGED
@@ -5,12 +5,14 @@ import type {
5
5
  LanguageModelV4Source,
6
6
  SharedV4ProviderMetadata,
7
7
  } from '@ai-sdk/provider';
8
+ import type { Context } from '@ai-sdk/provider-utils';
8
9
  import type {
9
10
  Experimental_LanguageModelStreamPart,
10
11
  FinishReason,
11
12
  LanguageModelUsage,
12
13
  StepResultPerformance,
13
14
  StopCondition,
15
+ TelemetryOptions,
14
16
  ToolCallRepairFunction,
15
17
  ToolChoice,
16
18
  ToolSet,
@@ -28,6 +30,8 @@ export type ModelCallStreamPart<TTools extends ToolSet = ToolSet> =
28
30
  approvalId: string;
29
31
  toolCallId: string;
30
32
  signature?: string;
33
+ /** Raw input before the original tool schema transformed it. */
34
+ inputSchemaInput?: unknown;
31
35
  }
32
36
  | { type: 'reset-step' };
33
37
 
@@ -91,6 +95,9 @@ export interface ModelCallTelemetry {
91
95
  recordInputs?: boolean;
92
96
  recordOutputs?: boolean;
93
97
  stepNumber: number;
98
+ includeRuntimeContext?: TelemetryOptions['includeRuntimeContext'];
99
+ /** Runtime context filtered before crossing the durable step boundary. */
100
+ runtimeContext?: Context;
94
101
  }
95
102
 
96
103
  /**
@@ -108,6 +115,8 @@ export interface ParsedToolCall {
108
115
  dynamic?: boolean;
109
116
  invalid?: boolean;
110
117
  error?: unknown;
118
+ /** Raw input before the original tool schema validated and transformed it. */
119
+ inputSchemaInput?: unknown;
111
120
  }
112
121
 
113
122
  /**
@@ -219,6 +228,11 @@ export type ModelCallResult =
219
228
  * tool input lifecycle callback replay was added.
220
229
  */
221
230
  toolInputLifecycleEvents?: ToolInputLifecycleEvent[];
231
+ /**
232
+ * Tool call ids that `repairToolCall` already repaired inside the step,
233
+ * so deferred validation does not repair them a second time.
234
+ */
235
+ repairedToolCallIds?: string[];
222
236
  /** Present when the model stream emitted an error part or generation failed. */
223
237
  terminalError?: unknown;
224
238
  };
@@ -36,6 +36,8 @@ export type SerializableToolDef = {
36
36
  inputExamples?: Array<{ input: unknown }>;
37
37
  /** Provider-specific options attached to the tool definition. */
38
38
  providerOptions?: Tool['providerOptions'];
39
+ /** Whether the original input schema has its own runtime validator. */
40
+ hasOwnValidator?: boolean;
39
41
  /** Input lifecycle callbacks that must be invoked outside the step. */
40
42
  hasOnInputStart?: boolean;
41
43
  hasOnInputDelta?: boolean;
@@ -69,6 +71,7 @@ export function serializeToolSet<TOOLS extends ToolSet>(
69
71
  ): Record<string, SerializableToolDef> {
70
72
  return Object.fromEntries(
71
73
  Object.entries(tools).map(([name, t]) => {
74
+ const inputSchema = asSchema(t.inputSchema);
72
75
  const def: SerializableToolDef = {
73
76
  title: t.title,
74
77
  metadata: t.metadata,
@@ -78,12 +81,16 @@ export function serializeToolSet<TOOLS extends ToolSet>(
78
81
  toolsContext,
79
82
  experimental_sandbox: sandbox,
80
83
  }),
81
- inputSchema: asSchema(t.inputSchema).jsonSchema as JSONSchema7,
84
+ inputSchema: inputSchema.jsonSchema as JSONSchema7,
82
85
  strict: t.strict,
83
86
  inputExamples: t.inputExamples,
84
87
  providerOptions: t.providerOptions,
85
88
  };
86
89
 
90
+ if (inputSchema.validate != null) {
91
+ def.hasOwnValidator = true;
92
+ }
93
+
87
94
  if (t.type === 'dynamic') {
88
95
  def.type = 'dynamic';
89
96
  }
@@ -159,9 +166,9 @@ function formatAjvError(error: ErrorObject): string {
159
166
  /**
160
167
  * Reconstructs tool objects from serializable tool definitions inside a step.
161
168
  *
162
- * Wraps each tool's JSON Schema with `jsonSchema()` and validates tool call
163
- * arguments against the schema using Ajv. This provides runtime type safety
164
- * equivalent to using zod schemas directly with the AI SDK.
169
+ * Wraps each tool's JSON Schema with `jsonSchema()`. Tools without their own
170
+ * validator are validated with Ajv. Tools with their own validator are
171
+ * validated after the workflow step, where the original schema is available.
165
172
  */
166
173
  export function resolveSerializableTools(
167
174
  tools: Record<string, SerializableToolDef>,
@@ -198,6 +205,11 @@ export function resolveSerializableTools(
198
205
  });
199
206
  };
200
207
 
208
+ const createInputSchema = (tool: SerializableToolDef) =>
209
+ tool.hasOwnValidator
210
+ ? jsonSchema(tool.inputSchema)
211
+ : createValidatedInputSchema(tool.inputSchema);
212
+
201
213
  return Object.fromEntries(
202
214
  Object.entries(tools).map(([name, t]) => {
203
215
  // Provider tools are executed server-side — pass them through
@@ -235,7 +247,7 @@ export function resolveSerializableTools(
235
247
  description: t.description,
236
248
  inputExamples: t.inputExamples,
237
249
  providerOptions: t.providerOptions,
238
- inputSchema: createValidatedInputSchema(t.inputSchema),
250
+ inputSchema: createInputSchema(t),
239
251
  }),
240
252
  ];
241
253
  }
@@ -247,7 +259,7 @@ export function resolveSerializableTools(
247
259
  strict: t.strict,
248
260
  inputExamples: t.inputExamples,
249
261
  providerOptions: t.providerOptions,
250
- inputSchema: createValidatedInputSchema(t.inputSchema),
262
+ inputSchema: createInputSchema(t),
251
263
  };
252
264
 
253
265
  return [
@@ -10,11 +10,24 @@ async function getGlobalTelemetryIntegrationCount(): Promise<number | null> {
10
10
  return globalThis.AI_SDK_TELEMETRY_INTEGRATIONS?.length ?? null;
11
11
  }
12
12
 
13
- export async function agentStreamWithGlobalTelemetry(prompt: string) {
13
+ export async function agentStreamWithGlobalTelemetry(
14
+ prompt: string,
15
+ includeTraceIdentity?: boolean,
16
+ ) {
14
17
  'use workflow';
15
18
  const agent = new WorkflowAgent({
16
19
  model: mockTextModel(`Echo: ${prompt}`),
17
- telemetry: { functionId: 'telemetry-test' },
20
+ telemetry: {
21
+ functionId: 'telemetry-test',
22
+ includeRuntimeContext:
23
+ includeTraceIdentity == null
24
+ ? undefined
25
+ : { traceIdentity: includeTraceIdentity },
26
+ },
27
+ runtimeContext: {
28
+ traceIdentity: { sessionId: 'chat_1', userId: 'user_1' },
29
+ secret: 'must not cross the model-step boundary',
30
+ },
18
31
  });
19
32
  const result = await agent.stream({
20
33
  messages: [{ role: 'user', content: prompt }],
@@ -29,11 +42,24 @@ export async function agentStreamWithGlobalTelemetry(prompt: string) {
29
42
  };
30
43
  }
31
44
 
32
- export async function agentGenerateWithGlobalTelemetry(prompt: string) {
45
+ export async function agentGenerateWithGlobalTelemetry(
46
+ prompt: string,
47
+ includeTraceIdentity?: boolean,
48
+ ) {
33
49
  'use workflow';
34
50
  const agent = new WorkflowAgent({
35
51
  model: mockTextModel(`Echo: ${prompt}`),
36
- telemetry: { functionId: 'telemetry-test' },
52
+ telemetry: {
53
+ functionId: 'telemetry-test',
54
+ includeRuntimeContext:
55
+ includeTraceIdentity == null
56
+ ? undefined
57
+ : { traceIdentity: includeTraceIdentity },
58
+ },
59
+ runtimeContext: {
60
+ traceIdentity: { sessionId: 'chat_1', userId: 'user_1' },
61
+ secret: 'must not cross the model-step boundary',
62
+ },
37
63
  });
38
64
  const result = await agent.generate({
39
65
  messages: [{ role: 'user', content: prompt }],
@@ -191,6 +191,9 @@ export function toUIMessageChunk(
191
191
  approvalId: part.approvalId,
192
192
  toolCallId:
193
193
  'toolCallId' in part ? part.toolCallId : part.toolCall.toolCallId,
194
+ ...('inputSchemaInput' in part && part.inputSchemaInput !== undefined
195
+ ? { inputSchemaInput: part.inputSchemaInput }
196
+ : {}),
194
197
  ...(part.signature != null ? { signature: part.signature } : {}),
195
198
  };
196
199