ai 6.0.288 → 6.0.290

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.
Files changed (40) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/index.d.mts +13 -0
  3. package/dist/index.d.ts +13 -0
  4. package/dist/index.js +377 -281
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +314 -213
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/internal/index.js +20 -5
  9. package/dist/internal/index.js.map +1 -1
  10. package/dist/internal/index.mjs +20 -5
  11. package/dist/internal/index.mjs.map +1 -1
  12. package/docs/03-ai-sdk-core/16-mcp-tools.mdx +57 -0
  13. package/docs/03-ai-sdk-core/40-middleware.mdx +92 -7
  14. package/docs/04-ai-sdk-ui/03-chatbot-tool-usage.mdx +7 -0
  15. package/package.json +5 -4
  16. package/src/embed/embed-many.ts +19 -3
  17. package/src/generate-speech/generated-audio-file.ts +0 -8
  18. package/src/generate-text/generated-file.ts +0 -4
  19. package/src/generate-text/parse-tool-call.ts +30 -23
  20. package/src/generate-text/run-tools-transformation.ts +37 -3
  21. package/src/generate-text/stream-text.ts +9 -28
  22. package/src/generate-text/to-response-messages.ts +7 -0
  23. package/src/generate-text/tool-call.ts +26 -0
  24. package/src/generate-text/validate-tool-approvals.ts +20 -2
  25. package/src/middleware/extract-reasoning-middleware.ts +1 -1
  26. package/src/middleware/wrap-embedding-model.ts +12 -2
  27. package/src/model/get-embedding-model-provider-options-transformer.ts +17 -0
  28. package/src/prompt/content-part.ts +2 -0
  29. package/src/prompt/convert-to-language-model-prompt.ts +7 -2
  30. package/src/prompt/data-content.ts +11 -2
  31. package/src/ui/chat.ts +108 -17
  32. package/src/ui/convert-to-model-messages.ts +8 -0
  33. package/src/ui/http-chat-transport.ts +1 -1
  34. package/src/ui/last-assistant-message-is-complete-with-approval-responses.ts +1 -1
  35. package/src/ui/last-assistant-message-is-complete-with-tool-calls.ts +2 -1
  36. package/src/ui/process-ui-message-stream.ts +6 -0
  37. package/src/ui/ui-messages.ts +10 -0
  38. package/src/ui/validate-ui-messages.ts +79 -114
  39. package/src/ui-message-stream/ui-message-chunks.ts +2 -0
  40. package/src/util/write-to-server-response.ts +0 -2
@@ -15,6 +15,7 @@ import {
15
15
  import { z } from 'zod/v4';
16
16
  import { InvalidArgumentError } from '../error';
17
17
  import { jsonValueSchema } from '../types/json-value';
18
+ import { isDeepEqualData } from '../util/is-deep-equal-data';
18
19
  import { providerMetadataSchema } from '../types/provider-metadata';
19
20
  import type {
20
21
  DataUIPart,
@@ -49,8 +50,36 @@ function asDynamicToolPart(toolPart: ToolUIPart): DynamicToolUIPart {
49
50
  } as DynamicToolUIPart;
50
51
  }
51
52
 
52
- const uiMessagesSchema = lazySchema(() =>
53
- zodSchema(
53
+ function getToolPartInputSchemaInput(
54
+ toolPart: ToolUIPart,
55
+ ): { value: unknown } | undefined {
56
+ return toolPart.approval != null &&
57
+ Object.prototype.hasOwnProperty.call(toolPart.approval, 'inputSchemaInput')
58
+ ? { value: toolPart.approval.inputSchemaInput }
59
+ : undefined;
60
+ }
61
+
62
+ const uiMessagesSchema = lazySchema(() => {
63
+ const approvalRequestedSchema = z.object({
64
+ id: z.string(),
65
+ approved: z.never().optional(),
66
+ descriptor: z.unknown().optional(),
67
+ reason: z.never().optional(),
68
+ signature: z.string().optional(),
69
+ inputSchemaInput: z.unknown().optional(),
70
+ });
71
+ const approvalRespondedSchema = approvalRequestedSchema.extend({
72
+ approved: z.boolean(),
73
+ reason: z.string().optional(),
74
+ });
75
+ const approvalGrantedSchema = approvalRespondedSchema.extend({
76
+ approved: z.literal(true),
77
+ });
78
+ const approvalDeniedSchema = approvalRespondedSchema.extend({
79
+ approved: z.literal(false),
80
+ });
81
+
82
+ return zodSchema(
54
83
  z
55
84
  .array(
56
85
  z
@@ -140,13 +169,7 @@ const uiMessagesSchema = lazySchema(() =>
140
169
  output: z.never().optional(),
141
170
  errorText: z.never().optional(),
142
171
  callProviderMetadata: providerMetadataSchema.optional(),
143
- approval: z.object({
144
- id: z.string(),
145
- approved: z.never().optional(),
146
- descriptor: z.unknown().optional(),
147
- reason: z.never().optional(),
148
- signature: z.string().optional(),
149
- }),
172
+ approval: approvalRequestedSchema,
150
173
  }),
151
174
  z.object({
152
175
  type: z.literal('dynamic-tool'),
@@ -159,13 +182,7 @@ const uiMessagesSchema = lazySchema(() =>
159
182
  output: z.never().optional(),
160
183
  errorText: z.never().optional(),
161
184
  callProviderMetadata: providerMetadataSchema.optional(),
162
- approval: z.object({
163
- id: z.string(),
164
- approved: z.boolean(),
165
- descriptor: z.unknown().optional(),
166
- reason: z.string().optional(),
167
- signature: z.string().optional(),
168
- }),
185
+ approval: approvalRespondedSchema,
169
186
  }),
170
187
  z.object({
171
188
  type: z.literal('dynamic-tool'),
@@ -180,15 +197,7 @@ const uiMessagesSchema = lazySchema(() =>
180
197
  callProviderMetadata: providerMetadataSchema.optional(),
181
198
  resultProviderMetadata: providerMetadataSchema.optional(),
182
199
  preliminary: z.boolean().optional(),
183
- approval: z
184
- .object({
185
- id: z.string(),
186
- approved: z.literal(true),
187
- descriptor: z.unknown().optional(),
188
- reason: z.string().optional(),
189
- signature: z.string().optional(),
190
- })
191
- .optional(),
200
+ approval: approvalGrantedSchema.optional(),
192
201
  }),
193
202
  z.object({
194
203
  type: z.literal('dynamic-tool'),
@@ -203,15 +212,7 @@ const uiMessagesSchema = lazySchema(() =>
203
212
  errorText: z.string(),
204
213
  callProviderMetadata: providerMetadataSchema.optional(),
205
214
  resultProviderMetadata: providerMetadataSchema.optional(),
206
- approval: z
207
- .object({
208
- id: z.string(),
209
- approved: z.literal(true),
210
- descriptor: z.unknown().optional(),
211
- reason: z.string().optional(),
212
- signature: z.string().optional(),
213
- })
214
- .optional(),
215
+ approval: approvalGrantedSchema.optional(),
215
216
  }),
216
217
  z.object({
217
218
  type: z.literal('dynamic-tool'),
@@ -224,13 +225,7 @@ const uiMessagesSchema = lazySchema(() =>
224
225
  output: z.never().optional(),
225
226
  errorText: z.never().optional(),
226
227
  callProviderMetadata: providerMetadataSchema.optional(),
227
- approval: z.object({
228
- id: z.string(),
229
- approved: z.literal(false),
230
- descriptor: z.unknown().optional(),
231
- reason: z.string().optional(),
232
- signature: z.string().optional(),
233
- }),
228
+ approval: approvalDeniedSchema,
234
229
  }),
235
230
  z.object({
236
231
  type: z.string().startsWith('tool-'),
@@ -266,13 +261,7 @@ const uiMessagesSchema = lazySchema(() =>
266
261
  output: z.never().optional(),
267
262
  errorText: z.never().optional(),
268
263
  callProviderMetadata: providerMetadataSchema.optional(),
269
- approval: z.object({
270
- id: z.string(),
271
- approved: z.never().optional(),
272
- descriptor: z.unknown().optional(),
273
- reason: z.never().optional(),
274
- signature: z.string().optional(),
275
- }),
264
+ approval: approvalRequestedSchema,
276
265
  }),
277
266
  z.object({
278
267
  type: z.string().startsWith('tool-'),
@@ -284,13 +273,7 @@ const uiMessagesSchema = lazySchema(() =>
284
273
  output: z.never().optional(),
285
274
  errorText: z.never().optional(),
286
275
  callProviderMetadata: providerMetadataSchema.optional(),
287
- approval: z.object({
288
- id: z.string(),
289
- approved: z.boolean(),
290
- descriptor: z.unknown().optional(),
291
- reason: z.string().optional(),
292
- signature: z.string().optional(),
293
- }),
276
+ approval: approvalRespondedSchema,
294
277
  }),
295
278
  z.object({
296
279
  type: z.string().startsWith('tool-'),
@@ -304,15 +287,7 @@ const uiMessagesSchema = lazySchema(() =>
304
287
  callProviderMetadata: providerMetadataSchema.optional(),
305
288
  resultProviderMetadata: providerMetadataSchema.optional(),
306
289
  preliminary: z.boolean().optional(),
307
- approval: z
308
- .object({
309
- id: z.string(),
310
- approved: z.literal(true),
311
- descriptor: z.unknown().optional(),
312
- reason: z.string().optional(),
313
- signature: z.string().optional(),
314
- })
315
- .optional(),
290
+ approval: approvalGrantedSchema.optional(),
316
291
  }),
317
292
  z.object({
318
293
  type: z.string().startsWith('tool-'),
@@ -326,15 +301,7 @@ const uiMessagesSchema = lazySchema(() =>
326
301
  errorText: z.string(),
327
302
  callProviderMetadata: providerMetadataSchema.optional(),
328
303
  resultProviderMetadata: providerMetadataSchema.optional(),
329
- approval: z
330
- .object({
331
- id: z.string(),
332
- approved: z.literal(true),
333
- descriptor: z.unknown().optional(),
334
- reason: z.string().optional(),
335
- signature: z.string().optional(),
336
- })
337
- .optional(),
304
+ approval: approvalGrantedSchema.optional(),
338
305
  }),
339
306
  z.object({
340
307
  type: z.string().startsWith('tool-'),
@@ -346,13 +313,7 @@ const uiMessagesSchema = lazySchema(() =>
346
313
  output: z.never().optional(),
347
314
  errorText: z.never().optional(),
348
315
  callProviderMetadata: providerMetadataSchema.optional(),
349
- approval: z.object({
350
- id: z.string(),
351
- approved: z.literal(false),
352
- descriptor: z.unknown().optional(),
353
- reason: z.string().optional(),
354
- signature: z.string().optional(),
355
- }),
316
+ approval: approvalDeniedSchema,
356
317
  }),
357
318
  ]),
358
319
  ),
@@ -372,8 +333,8 @@ const uiMessagesSchema = lazySchema(() =>
372
333
  }),
373
334
  )
374
335
  .nonempty('Messages array must not be empty'),
375
- ),
376
- );
336
+ );
337
+ });
377
338
 
378
339
  export type SafeValidateUIMessagesResult<UI_MESSAGE extends UIMessage> =
379
340
  | {
@@ -433,7 +394,7 @@ async function safeValidateUIMessagesInternal<UI_MESSAGE extends UIMessage>(
433
394
 
434
395
  if (metadataSchema) {
435
396
  for (const [msgIdx, message] of validatedMessages.entries()) {
436
- await validateTypes({
397
+ message.metadata = await validateTypes({
437
398
  value: message.metadata,
438
399
  schema: metadataSchema,
439
400
  context: {
@@ -471,7 +432,7 @@ async function safeValidateUIMessagesInternal<UI_MESSAGE extends UIMessage>(
471
432
  };
472
433
  }
473
434
 
474
- await validateTypes({
435
+ dataPart.data = await validateTypes({
475
436
  value: dataPart.data,
476
437
  schema: dataSchema,
477
438
  context: {
@@ -527,49 +488,53 @@ async function safeValidateUIMessagesInternal<UI_MESSAGE extends UIMessage>(
527
488
  entityName: toolName,
528
489
  entityId: toolPart.toolCallId,
529
490
  };
491
+ const inputSchemaInput = getToolPartInputSchemaInput(toolPart);
492
+ const inputToValidate =
493
+ inputSchemaInput == null
494
+ ? toolPart.input
495
+ : inputSchemaInput.value;
530
496
  let convertToDynamic = false;
531
497
 
532
498
  // Tool input validation
533
- if (toolPart.state === 'output-error') {
534
- // Failed calls can retain invalid input. Keep them loadable, but
535
- // expose incompatible input as unknown instead of the current
536
- // static tool input type.
537
- if (toolPart.input !== undefined) {
538
- const result = await safeValidateTypes({
539
- value: toolPart.input,
540
- schema: tool.inputSchema,
541
- context: inputValidationContext,
542
- });
543
- convertToDynamic = !result.success;
544
- }
545
- } else if (toolPart.state === 'output-available') {
499
+ if (
500
+ toolPart.state !== 'input-streaming' &&
501
+ (toolPart.state !== 'output-error' ||
502
+ inputSchemaInput != null ||
503
+ toolPart.input !== undefined)
504
+ ) {
546
505
  const result = await safeValidateTypes({
547
- value: toolPart.input,
506
+ value: inputToValidate,
548
507
  schema: tool.inputSchema,
549
508
  context: inputValidationContext,
550
509
  });
551
510
 
511
+ let inputError: TypeValidationError | undefined;
552
512
  if (!result.success) {
553
- // Empty terminal input can represent aborted or incomplete
554
- // history whose input was never streamed. Preserve it without
555
- // claiming that it matches the current static input type.
556
- if (isEmptyObject(toolPart.input)) {
513
+ inputError = result.error;
514
+ } else if (inputSchemaInput != null) {
515
+ if (!isDeepEqualData(result.value, toolPart.input)) {
516
+ inputError = new TypeValidationError({
517
+ value: toolPart.input,
518
+ cause:
519
+ 'Tool input does not match the output reconstructed from inputSchemaInput.',
520
+ context: inputValidationContext,
521
+ });
522
+ }
523
+ }
524
+
525
+ if (inputError != null) {
526
+ // Failed calls and empty terminal inputs must remain loadable,
527
+ // but must not claim to match the current static input type.
528
+ if (
529
+ toolPart.state === 'output-error' ||
530
+ (toolPart.state === 'output-available' &&
531
+ isEmptyObject(toolPart.input))
532
+ ) {
557
533
  convertToDynamic = true;
558
534
  } else {
559
- throw result.error;
535
+ throw inputError;
560
536
  }
561
537
  }
562
- } else if (
563
- toolPart.state === 'input-available' ||
564
- toolPart.state === 'approval-requested' ||
565
- toolPart.state === 'approval-responded' ||
566
- toolPart.state === 'output-denied'
567
- ) {
568
- await validateTypes({
569
- value: toolPart.input,
570
- schema: tool.inputSchema,
571
- context: inputValidationContext,
572
- });
573
538
  }
574
539
 
575
540
  // Tool output validation
@@ -86,6 +86,7 @@ export const uiMessageChunkSchema = lazySchema(() =>
86
86
  approvalId: z.string(),
87
87
  toolCallId: z.string(),
88
88
  approvalDescriptor: z.unknown().optional(),
89
+ inputSchemaInput: z.unknown().optional(),
89
90
  signature: z.string().optional(),
90
91
  }),
91
92
  z.looseObject({
@@ -272,6 +273,7 @@ export type UIMessageChunk<
272
273
  approvalId: string;
273
274
  toolCallId: string;
274
275
  approvalDescriptor?: unknown;
276
+ inputSchemaInput?: unknown;
275
277
  signature?: string;
276
278
  }
277
279
  | {
@@ -38,8 +38,6 @@ export function writeToServerResponse({
38
38
  });
39
39
  }
40
40
  }
41
- } catch (error) {
42
- throw error;
43
41
  } finally {
44
42
  response.end();
45
43
  }