ai 6.0.289 → 6.0.291

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 (33) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/index.d.mts +13 -0
  3. package/dist/index.d.ts +13 -0
  4. package/dist/index.js +326 -250
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +263 -182
  7. package/dist/index.mjs.map +1 -1
  8. package/dist/internal/index.js +4 -2
  9. package/dist/internal/index.js.map +1 -1
  10. package/dist/internal/index.mjs +4 -2
  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/04-ai-sdk-ui/03-chatbot-tool-usage.mdx +7 -0
  14. package/package.json +4 -3
  15. package/src/embed/embed-many.ts +19 -3
  16. package/src/generate-speech/generated-audio-file.ts +0 -8
  17. package/src/generate-text/generated-file.ts +0 -4
  18. package/src/generate-text/parse-tool-call.ts +30 -23
  19. package/src/generate-text/stream-text.ts +9 -1
  20. package/src/generate-text/to-response-messages.ts +7 -0
  21. package/src/generate-text/tool-call.ts +26 -0
  22. package/src/generate-text/validate-tool-approvals.ts +20 -2
  23. package/src/middleware/extract-reasoning-middleware.ts +1 -1
  24. package/src/middleware/wrap-embedding-model.ts +12 -2
  25. package/src/model/get-embedding-model-provider-options-transformer.ts +17 -0
  26. package/src/prompt/content-part.ts +2 -0
  27. package/src/ui/chat.ts +108 -17
  28. package/src/ui/convert-to-model-messages.ts +8 -0
  29. package/src/ui/process-ui-message-stream.ts +6 -0
  30. package/src/ui/ui-messages.ts +10 -0
  31. package/src/ui/validate-ui-messages.ts +79 -114
  32. package/src/ui-message-stream/ui-message-chunks.ts +2 -0
  33. package/src/util/write-to-server-response.ts +0 -2
@@ -4,6 +4,7 @@ import {
4
4
  } from '@ai-sdk/provider-utils';
5
5
  import { logWarnings } from '../logger/log-warnings';
6
6
  import { getEmbeddingModelMaxInputBytesPerCall } from '../model/get-embedding-model-max-input-bytes-per-call';
7
+ import { getEmbeddingModelProviderOptionsTransformer } from '../model/get-embedding-model-provider-options-transformer';
7
8
  import { resolveEmbeddingModel } from '../model/resolve-model';
8
9
  import { assembleOperationName } from '../telemetry/assemble-operation-name';
9
10
  import { getBaseTelemetryAttributes } from '../telemetry/get-base-telemetry-attributes';
@@ -250,6 +251,8 @@ export async function embedMany({
250
251
  ? maxInputBytesPerCall
251
252
  : Infinity,
252
253
  });
254
+ const providerOptionsTransformer =
255
+ getEmbeddingModelProviderOptionsTransformer(model);
253
256
 
254
257
  // serially embed the chunks:
255
258
  const embeddings: Array<Embedding> = [];
@@ -269,9 +272,22 @@ export async function embedMany({
269
272
  supportsParallelCalls ? maxParallelCalls : 1,
270
273
  );
271
274
 
275
+ let nextChunkStartIndex = 0;
272
276
  for (const parallelChunk of parallelChunks) {
273
277
  const results = await Promise.all(
274
- parallelChunk.map(chunk => {
278
+ parallelChunk.map(async chunk => {
279
+ // Capture the range before awaiting transformations or retrying.
280
+ const startIndex = nextChunkStartIndex;
281
+ nextChunkStartIndex += chunk.length;
282
+ const chunkProviderOptions = providerOptionsTransformer
283
+ ? await providerOptionsTransformer({
284
+ providerOptions,
285
+ values,
286
+ startIndex,
287
+ endIndex: startIndex + chunk.length,
288
+ })
289
+ : providerOptions;
290
+
275
291
  return retry(() => {
276
292
  // nested spans to align with the embedMany telemetry data:
277
293
  return recordSpan({
@@ -296,7 +312,7 @@ export async function embedMany({
296
312
  values: chunk,
297
313
  abortSignal,
298
314
  headers: headersWithUserAgent,
299
- providerOptions,
315
+ providerOptions: chunkProviderOptions,
300
316
  });
301
317
 
302
318
  const embeddings = modelResponse.embeddings;
@@ -343,7 +359,7 @@ export async function embedMany({
343
359
  result.providerMetadata,
344
360
  )) {
345
361
  providerMetadata[providerName] = {
346
- ...(providerMetadata[providerName] ?? {}),
362
+ ...providerMetadata[providerName],
347
363
  ...metadata,
348
364
  };
349
365
  }
@@ -54,12 +54,4 @@ export class DefaultGeneratedAudioFile
54
54
 
55
55
  export class DefaultGeneratedAudioFileWithType extends DefaultGeneratedAudioFile {
56
56
  readonly type = 'audio';
57
-
58
- constructor(options: {
59
- data: string | Uint8Array;
60
- mediaType: string;
61
- format: string;
62
- }) {
63
- super(options);
64
- }
65
57
  }
@@ -63,8 +63,4 @@ export class DefaultGeneratedFile implements GeneratedFile {
63
63
 
64
64
  export class DefaultGeneratedFileWithType extends DefaultGeneratedFile {
65
65
  readonly type = 'file';
66
-
67
- constructor(options: { data: string | Uint8Array; mediaType: string }) {
68
- super(options);
69
- }
70
66
  }
@@ -9,7 +9,11 @@ import {
9
9
  import { InvalidToolInputError } from '../error/invalid-tool-input-error';
10
10
  import { NoSuchToolError } from '../error/no-such-tool-error';
11
11
  import { ToolCallRepairError } from '../error/tool-call-repair-error';
12
- import type { DynamicToolCall, TypedToolCall } from './tool-call';
12
+ import {
13
+ setToolCallInputSchemaInput,
14
+ type DynamicToolCall,
15
+ type TypedToolCall,
16
+ } from './tool-call';
13
17
  import type { ToolCallRepairFunction } from './tool-call-repair-function';
14
18
  import type { ToolSet } from './tool-set';
15
19
 
@@ -167,26 +171,29 @@ async function doParseToolCall<TOOLS extends ToolSet>({
167
171
  });
168
172
  }
169
173
 
170
- return tool.type === 'dynamic'
171
- ? {
172
- type: 'tool-call',
173
- toolCallId: toolCall.toolCallId,
174
- toolName: toolCall.toolName,
175
- input: parseResult.value,
176
- providerExecuted: toolCall.providerExecuted,
177
- providerMetadata: toolCall.providerMetadata,
178
- ...(tool.metadata != null ? { toolMetadata: tool.metadata } : {}),
179
- dynamic: true,
180
- title: tool.title,
181
- }
182
- : {
183
- type: 'tool-call',
184
- toolCallId: toolCall.toolCallId,
185
- toolName,
186
- input: parseResult.value,
187
- providerExecuted: toolCall.providerExecuted,
188
- providerMetadata: toolCall.providerMetadata,
189
- ...(tool.metadata != null ? { toolMetadata: tool.metadata } : {}),
190
- title: tool.title,
191
- };
174
+ return setToolCallInputSchemaInput(
175
+ tool.type === 'dynamic'
176
+ ? {
177
+ type: 'tool-call',
178
+ toolCallId: toolCall.toolCallId,
179
+ toolName: toolCall.toolName,
180
+ input: parseResult.value,
181
+ providerExecuted: toolCall.providerExecuted,
182
+ providerMetadata: toolCall.providerMetadata,
183
+ ...(tool.metadata != null ? { toolMetadata: tool.metadata } : {}),
184
+ dynamic: true,
185
+ title: tool.title,
186
+ }
187
+ : {
188
+ type: 'tool-call',
189
+ toolCallId: toolCall.toolCallId,
190
+ toolName,
191
+ input: parseResult.value,
192
+ providerExecuted: toolCall.providerExecuted,
193
+ providerMetadata: toolCall.providerMetadata,
194
+ ...(tool.metadata != null ? { toolMetadata: tool.metadata } : {}),
195
+ title: tool.title,
196
+ },
197
+ parseResult.rawValue,
198
+ );
192
199
  }
@@ -79,6 +79,7 @@ import { consumeStream } from '../util/consume-stream';
79
79
  import { createIdMap } from '../util/create-id-map';
80
80
  import { createStitchableStream } from '../util/create-stitchable-stream';
81
81
  import type { DownloadFunction } from '../util/download/download-function';
82
+ import { isDeepEqualData } from '../util/is-deep-equal-data';
82
83
  import { mergeAbortSignals } from '../util/merge-abort-signals';
83
84
  import { mergeObjects } from '../util/merge-objects';
84
85
  import { now as originalNow } from '../util/now';
@@ -123,7 +124,7 @@ import type {
123
124
  UIMessageStreamOptions,
124
125
  } from './stream-text-result';
125
126
  import { toResponseMessages } from './to-response-messages';
126
- import type { TypedToolCall } from './tool-call';
127
+ import { getToolCallInputSchemaInput, type TypedToolCall } from './tool-call';
127
128
  import type { ToolCallRepairFunction } from './tool-call-repair-function';
128
129
  import type { ToolOutput } from './tool-output';
129
130
  import type { StaticToolOutputDenied } from './tool-output-denied';
@@ -2984,10 +2985,17 @@ class DefaultStreamTextResult<
2984
2985
  }
2985
2986
 
2986
2987
  case 'tool-approval-request': {
2988
+ const inputSchemaInput = getToolCallInputSchemaInput(
2989
+ part.toolCall,
2990
+ );
2987
2991
  controller.enqueue({
2988
2992
  type: 'tool-approval-request',
2989
2993
  approvalId: part.approvalId,
2990
2994
  toolCallId: part.toolCall.toolCallId,
2995
+ ...(inputSchemaInput != null &&
2996
+ !isDeepEqualData(inputSchemaInput.value, part.toolCall.input)
2997
+ ? { inputSchemaInput: inputSchemaInput.value }
2998
+ : {}),
2991
2999
  ...(part.signature != null
2992
3000
  ? { signature: part.signature }
2993
3001
  : {}),
@@ -5,8 +5,10 @@ import type {
5
5
  ToolModelMessage,
6
6
  } from '../prompt';
7
7
  import { createToolModelOutput } from '../prompt/create-tool-model-output';
8
+ import { isDeepEqualData } from '../util/is-deep-equal-data';
8
9
  import type { ContentPart } from './content-part';
9
10
  import type { ToolSet } from './tool-set';
11
+ import { getToolCallInputSchemaInput } from './tool-call';
10
12
 
11
13
  /**
12
14
  * Converts the result of a `generateText` or `streamText` call to a list of response messages.
@@ -113,11 +115,16 @@ export async function toResponseMessages<TOOLS extends ToolSet>({
113
115
  break;
114
116
  }
115
117
  case 'tool-approval-request':
118
+ const inputSchemaInput = getToolCallInputSchemaInput(part.toolCall);
116
119
  content.push({
117
120
  type: 'tool-approval-request',
118
121
  approvalId: part.approvalId,
119
122
  toolCallId: part.toolCall.toolCallId,
120
123
  ...(part.signature != null ? { signature: part.signature } : {}),
124
+ ...(inputSchemaInput != null &&
125
+ !isDeepEqualData(inputSchemaInput.value, part.toolCall.input)
126
+ ? { inputSchemaInput: inputSchemaInput.value }
127
+ : {}),
121
128
  });
122
129
  break;
123
130
  }
@@ -4,6 +4,8 @@ import type { ProviderMetadata } from '../types';
4
4
  import type { ValueOf } from '../util/value-of';
5
5
  import type { ToolSet } from './tool-set';
6
6
 
7
+ const inputSchemaInputSymbol = Symbol('ai-sdk-tool-call-input-schema-input');
8
+
7
9
  type BaseToolCall = {
8
10
  type: 'tool-call';
9
11
  toolCallId: string;
@@ -47,3 +49,27 @@ export type DynamicToolCall = BaseToolCall & {
47
49
  export type TypedToolCall<TOOLS extends ToolSet> =
48
50
  | StaticToolCall<TOOLS>
49
51
  | DynamicToolCall;
52
+
53
+ export function setToolCallInputSchemaInput<TOOLS extends ToolSet>(
54
+ toolCall: TypedToolCall<TOOLS>,
55
+ inputSchemaInput: unknown,
56
+ ): TypedToolCall<TOOLS> {
57
+ Object.defineProperty(toolCall, inputSchemaInputSymbol, {
58
+ value: inputSchemaInput,
59
+ });
60
+ return toolCall;
61
+ }
62
+
63
+ export function getToolCallInputSchemaInput<TOOLS extends ToolSet>(
64
+ toolCall: TypedToolCall<TOOLS>,
65
+ ): { value: unknown } | undefined {
66
+ return inputSchemaInputSymbol in toolCall
67
+ ? {
68
+ value: (
69
+ toolCall as TypedToolCall<TOOLS> & {
70
+ [inputSchemaInputSymbol]: unknown;
71
+ }
72
+ )[inputSchemaInputSymbol],
73
+ }
74
+ : undefined;
75
+ }
@@ -5,6 +5,7 @@ import {
5
5
  } from '@ai-sdk/provider-utils';
6
6
  import { InvalidToolApprovalSignatureError } from '../error/invalid-tool-approval-signature-error';
7
7
  import { InvalidToolInputError } from '../error/invalid-tool-input-error';
8
+ import { isDeepEqualData } from '../util/is-deep-equal-data';
8
9
  import type { CollectedToolApprovals } from './collect-tool-approvals';
9
10
  import { isApprovalNeeded } from './is-approval-needed';
10
11
  import { verifyToolApprovalSignature } from './tool-approval-signature';
@@ -86,18 +87,35 @@ export async function validateApprovedToolApprovals<TOOLS extends ToolSet>({
86
87
  typeof tool.execute === 'function' &&
87
88
  tool.inputSchema != null
88
89
  ) {
90
+ const hasInputSchemaInput = Object.prototype.hasOwnProperty.call(
91
+ approvalRequest,
92
+ 'inputSchemaInput',
93
+ );
89
94
  const validation = await safeValidateTypes({
90
- value: toolCall.input,
95
+ value: hasInputSchemaInput
96
+ ? approvalRequest.inputSchemaInput
97
+ : toolCall.input,
91
98
  schema: asSchema(tool.inputSchema),
92
99
  });
93
100
 
101
+ let validationError: unknown;
94
102
  if (!validation.success) {
103
+ validationError = validation.error;
104
+ } else if (!isDeepEqualData(validation.value, toolCall.input)) {
105
+ // Revalidation must never change the operation that was approved,
106
+ // including when older or projected history omits the schema input.
107
+ validationError = new Error(
108
+ 'Approved tool input does not match the validated schema output.',
109
+ );
110
+ }
111
+
112
+ if (validationError != null) {
95
113
  invalid.push({
96
114
  ...approval,
97
115
  error: new InvalidToolInputError({
98
116
  toolName: toolCall.toolName,
99
117
  toolInput: JSON.stringify(toolCall.input),
100
- cause: validation.error,
118
+ cause: validationError,
101
119
  }),
102
120
  });
103
121
  continue;
@@ -24,7 +24,7 @@ export function extractReasoningMiddleware({
24
24
  startWithReasoning?: boolean;
25
25
  }): LanguageModelMiddleware {
26
26
  const openingTag = `<${tagName}>`;
27
- const closingTag = `<\/${tagName}>`;
27
+ const closingTag = `</${tagName}>`;
28
28
 
29
29
  return {
30
30
  specificationVersion: 'v3',
@@ -2,11 +2,18 @@ import type {
2
2
  EmbeddingModelV3,
3
3
  EmbeddingModelV3CallOptions,
4
4
  } from '@ai-sdk/provider';
5
- import { EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL } from '@ai-sdk/provider-utils';
5
+ import {
6
+ EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL,
7
+ EXPERIMENTAL_EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER,
8
+ } from '@ai-sdk/provider-utils';
6
9
  import {
7
10
  getEmbeddingModelMaxInputBytesPerCall,
8
11
  type EmbeddingModelWithMaxInputBytesPerCall,
9
12
  } from '../model/get-embedding-model-max-input-bytes-per-call';
13
+ import {
14
+ getEmbeddingModelProviderOptionsTransformer,
15
+ type EmbeddingModelWithProviderOptionsTransformer,
16
+ } from '../model/get-embedding-model-provider-options-transformer';
10
17
  import type { EmbeddingModelMiddleware } from '../types';
11
18
  import { asArray } from '../util/as-array';
12
19
 
@@ -57,7 +64,8 @@ const doWrap = ({
57
64
  middleware: EmbeddingModelMiddleware;
58
65
  modelId?: string;
59
66
  providerId?: string;
60
- }): EmbeddingModelWithMaxInputBytesPerCall => {
67
+ }): EmbeddingModelWithMaxInputBytesPerCall &
68
+ EmbeddingModelWithProviderOptionsTransformer => {
61
69
  async function doTransform({
62
70
  params,
63
71
  }: {
@@ -74,6 +82,8 @@ const doWrap = ({
74
82
  overrideMaxEmbeddingsPerCall?.({ model }) ?? model.maxEmbeddingsPerCall,
75
83
  [EXPERIMENTAL_EMBEDDING_MODEL_MAX_INPUT_BYTES_PER_CALL]:
76
84
  getEmbeddingModelMaxInputBytesPerCall(model),
85
+ [EXPERIMENTAL_EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER]:
86
+ getEmbeddingModelProviderOptionsTransformer(model),
77
87
  supportsParallelCalls:
78
88
  overrideSupportsParallelCalls?.({ model }) ?? model.supportsParallelCalls,
79
89
  async doEmbed(
@@ -0,0 +1,17 @@
1
+ import type { EmbeddingModelV3 } from '@ai-sdk/provider';
2
+ import {
3
+ EXPERIMENTAL_EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER,
4
+ type EmbeddingModelProviderOptionsTransformer,
5
+ } from '@ai-sdk/provider-utils';
6
+
7
+ export type EmbeddingModelWithProviderOptionsTransformer = EmbeddingModelV3 & {
8
+ readonly [EXPERIMENTAL_EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER]?: EmbeddingModelProviderOptionsTransformer;
9
+ };
10
+
11
+ export function getEmbeddingModelProviderOptionsTransformer(
12
+ model: EmbeddingModelV3,
13
+ ) {
14
+ return (model as EmbeddingModelWithProviderOptionsTransformer)[
15
+ EXPERIMENTAL_EMBEDDING_MODEL_PROVIDER_OPTIONS_TRANSFORMER
16
+ ];
17
+ }
@@ -201,6 +201,8 @@ export const toolApprovalRequestSchema: z.ZodType<ToolApprovalRequest> =
201
201
  type: z.literal('tool-approval-request'),
202
202
  approvalId: z.string(),
203
203
  toolCallId: z.string(),
204
+ signature: z.string().optional(),
205
+ inputSchemaInput: z.unknown().optional(),
204
206
  });
205
207
 
206
208
  /**
package/src/ui/chat.ts CHANGED
@@ -259,6 +259,7 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
259
259
  private onData?: ChatInit<UI_MESSAGE>['onData'];
260
260
  private sendAutomaticallyWhen?: ChatInit<UI_MESSAGE>['sendAutomaticallyWhen'];
261
261
 
262
+ private pendingApprovalMessageId: string | undefined;
262
263
  private activeResponse: ActiveResponse<UI_MESSAGE> | undefined = undefined;
263
264
  private activeResumeRequest: ActiveResumeRequest | undefined = undefined;
264
265
  private jobExecutor = new SerialJobExecutor();
@@ -361,9 +362,37 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
361
362
  options?: ChatRequestOptions,
362
363
  ): Promise<void> => {
363
364
  if (message == null) {
364
- await this.makeRequest({
365
- trigger: 'submit-message',
366
- messageId: this.lastMessage?.id,
365
+ let messageId = this.pendingApprovalMessageId;
366
+
367
+ if (messageId == null) {
368
+ messageId = this.lastMessage?.id;
369
+
370
+ // When hydrating a chat with an already-responded approval, continue
371
+ // the most recent matching assistant message so result chunks can
372
+ // resolve its tool invocation.
373
+ for (let i = this.state.messages.length - 1; i >= 0; i--) {
374
+ const candidate = this.state.messages[i];
375
+ if (
376
+ candidate.role === 'assistant' &&
377
+ candidate.parts.some(
378
+ part => isToolUIPart(part) && part.state === 'approval-responded',
379
+ )
380
+ ) {
381
+ messageId = candidate.id;
382
+ break;
383
+ }
384
+ }
385
+ }
386
+
387
+ const consumesPendingApproval =
388
+ messageId != null && messageId === this.pendingApprovalMessageId;
389
+ const pendingApprovalMessageIndex = consumesPendingApproval
390
+ ? this.state.messages.findIndex(message => message.id === messageId)
391
+ : -1;
392
+
393
+ await this.makeRequestForToolApproval({
394
+ messageId,
395
+ messageIndex: pendingApprovalMessageIndex,
367
396
  ...options,
368
397
  });
369
398
  return;
@@ -489,7 +518,6 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
489
518
  }) =>
490
519
  this.jobExecutor.run(async () => {
491
520
  const messages = this.state.messages;
492
- const lastMessage = messages[messages.length - 1];
493
521
 
494
522
  const updatePart = (
495
523
  part: UIMessagePart<UIDataTypes, UITools>,
@@ -504,11 +532,25 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
504
532
  }
505
533
  : part;
506
534
 
507
- // update the message to trigger an immediate UI update
508
- this.state.replaceMessage(messages.length - 1, {
509
- ...lastMessage,
510
- parts: lastMessage.parts.map(updatePart),
511
- });
535
+ const messageIndex = messages.findIndex(message =>
536
+ message.parts.some(
537
+ part =>
538
+ isToolUIPart(part) &&
539
+ part.state === 'approval-requested' &&
540
+ part.approval.id === id,
541
+ ),
542
+ );
543
+
544
+ if (messageIndex !== -1) {
545
+ const message = messages[messageIndex];
546
+
547
+ // update the message to trigger an immediate UI update
548
+ this.state.replaceMessage(messageIndex, {
549
+ ...message,
550
+ parts: message.parts.map(updatePart),
551
+ });
552
+ this.pendingApprovalMessageId = message.id;
553
+ }
512
554
 
513
555
  // update the active response if it exists
514
556
  if (this.activeResponse) {
@@ -525,9 +567,14 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
525
567
  this.shouldSendAutomatically().then(shouldSend => {
526
568
  if (shouldSend) {
527
569
  // no await to avoid deadlocking
528
- this.makeRequest({
529
- trigger: 'submit-message',
530
- messageId: this.lastMessage?.id,
570
+ const messageId =
571
+ messageIndex === -1
572
+ ? this.lastMessage?.id
573
+ : messages[messageIndex].id;
574
+
575
+ this.makeRequestForToolApproval({
576
+ messageId,
577
+ messageIndex,
531
578
  ...options,
532
579
  });
533
580
  }
@@ -610,6 +657,36 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
610
657
  return result as boolean;
611
658
  }
612
659
 
660
+ private async makeRequestForToolApproval({
661
+ messageId,
662
+ messageIndex,
663
+ ...options
664
+ }: {
665
+ messageId?: string;
666
+ messageIndex: number;
667
+ } & ChatRequestOptions) {
668
+ const consumesPendingApproval =
669
+ messageId != null && messageId === this.pendingApprovalMessageId;
670
+ if (consumesPendingApproval) {
671
+ this.pendingApprovalMessageId = undefined;
672
+ }
673
+
674
+ await this.makeRequest({
675
+ trigger: 'submit-message',
676
+ messageId,
677
+ ...options,
678
+ });
679
+
680
+ if (
681
+ consumesPendingApproval &&
682
+ this.status === 'error' &&
683
+ this.pendingApprovalMessageId == null
684
+ ) {
685
+ this.pendingApprovalMessageId =
686
+ this.state.messages[messageIndex]?.id ?? messageId;
687
+ }
688
+ }
689
+
613
690
  private async makeRequest({
614
691
  trigger,
615
692
  metadata,
@@ -697,6 +774,18 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
697
774
  this.setStatus({ status: 'submitted', error: undefined });
698
775
 
699
776
  const lastMessage = this.lastMessage;
777
+ const responseMessageIndex =
778
+ trigger === 'submit-message' && messageId != null
779
+ ? this.state.messages.findIndex(message => message.id === messageId)
780
+ : this.state.messages.length - 1;
781
+ const responseMessage =
782
+ responseMessageIndex === -1
783
+ ? lastMessage
784
+ : this.state.messages[responseMessageIndex];
785
+ const usesEarlierAssistantMessage =
786
+ responseMessageIndex !== -1 &&
787
+ responseMessageIndex < this.state.messages.length - 1 &&
788
+ responseMessage?.role === 'assistant';
700
789
 
701
790
  let isAbort = false;
702
791
  let isDisconnect = false;
@@ -709,7 +798,7 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
709
798
  lastMessage:
710
799
  trigger === 'resume-stream' || trigger === 'regenerate-message'
711
800
  ? undefined
712
- : this.state.snapshot(lastMessage),
801
+ : this.state.snapshot(responseMessage),
713
802
  messageId: this.generateId(),
714
803
  }),
715
804
  abortController,
@@ -763,10 +852,12 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
763
852
  this.setStatus({ status: 'streaming' });
764
853
  }
765
854
 
766
- const replaceLastMessage =
767
- response.state.message.id === this.lastMessage?.id;
768
-
769
- if (replaceLastMessage) {
855
+ if (usesEarlierAssistantMessage) {
856
+ this.state.replaceMessage(
857
+ responseMessageIndex,
858
+ response.state.message,
859
+ );
860
+ } else if (response.state.message.id === this.lastMessage?.id) {
770
861
  this.state.replaceMessage(
771
862
  this.state.messages.length - 1,
772
863
  response.state.message,
@@ -204,6 +204,14 @@ export async function convertToModelMessages<UI_MESSAGE extends UIMessage>(
204
204
  type: 'tool-approval-request' as const,
205
205
  approvalId: part.approval.id,
206
206
  toolCallId: part.toolCallId,
207
+ ...(Object.prototype.hasOwnProperty.call(
208
+ part.approval,
209
+ 'inputSchemaInput',
210
+ )
211
+ ? {
212
+ inputSchemaInput: part.approval.inputSchemaInput,
213
+ }
214
+ : {}),
207
215
  ...(part.approval.signature != null
208
216
  ? { signature: part.approval.signature }
209
217
  : {}),
@@ -721,6 +721,12 @@ export function processUIMessageStream<UI_MESSAGE extends UIMessage>({
721
721
  ...(chunk.approvalDescriptor != null
722
722
  ? { descriptor: chunk.approvalDescriptor }
723
723
  : {}),
724
+ ...(Object.prototype.hasOwnProperty.call(
725
+ chunk,
726
+ 'inputSchemaInput',
727
+ )
728
+ ? { inputSchemaInput: chunk.inputSchemaInput }
729
+ : {}),
724
730
  ...(chunk.signature != null
725
731
  ? { signature: chunk.signature }
726
732
  : {}),
@@ -263,6 +263,7 @@ export type UIToolInvocation<TOOL extends UITool | Tool> = {
263
263
  descriptor?: unknown;
264
264
  reason?: never;
265
265
  signature?: string;
266
+ inputSchemaInput?: unknown;
266
267
  };
267
268
  }
268
269
  | {
@@ -277,6 +278,7 @@ export type UIToolInvocation<TOOL extends UITool | Tool> = {
277
278
  descriptor?: unknown;
278
279
  reason?: string;
279
280
  signature?: string;
281
+ inputSchemaInput?: unknown;
280
282
  };
281
283
  }
282
284
  | {
@@ -293,6 +295,7 @@ export type UIToolInvocation<TOOL extends UITool | Tool> = {
293
295
  descriptor?: unknown;
294
296
  reason?: string;
295
297
  signature?: string;
298
+ inputSchemaInput?: unknown;
296
299
  };
297
300
  }
298
301
  | {
@@ -309,6 +312,7 @@ export type UIToolInvocation<TOOL extends UITool | Tool> = {
309
312
  descriptor?: unknown;
310
313
  reason?: string;
311
314
  signature?: string;
315
+ inputSchemaInput?: unknown;
312
316
  };
313
317
  }
314
318
  | {
@@ -323,6 +327,7 @@ export type UIToolInvocation<TOOL extends UITool | Tool> = {
323
327
  descriptor?: unknown;
324
328
  reason?: string;
325
329
  signature?: string;
330
+ inputSchemaInput?: unknown;
326
331
  };
327
332
  }
328
333
  );
@@ -381,6 +386,7 @@ export type DynamicToolUIPart = {
381
386
  descriptor?: unknown;
382
387
  reason?: never;
383
388
  signature?: string;
389
+ inputSchemaInput?: unknown;
384
390
  };
385
391
  }
386
392
  | {
@@ -395,6 +401,7 @@ export type DynamicToolUIPart = {
395
401
  descriptor?: unknown;
396
402
  reason?: string;
397
403
  signature?: string;
404
+ inputSchemaInput?: unknown;
398
405
  };
399
406
  }
400
407
  | {
@@ -411,6 +418,7 @@ export type DynamicToolUIPart = {
411
418
  descriptor?: unknown;
412
419
  reason?: string;
413
420
  signature?: string;
421
+ inputSchemaInput?: unknown;
414
422
  };
415
423
  }
416
424
  | {
@@ -426,6 +434,7 @@ export type DynamicToolUIPart = {
426
434
  descriptor?: unknown;
427
435
  reason?: string;
428
436
  signature?: string;
437
+ inputSchemaInput?: unknown;
429
438
  };
430
439
  }
431
440
  | {
@@ -440,6 +449,7 @@ export type DynamicToolUIPart = {
440
449
  descriptor?: unknown;
441
450
  reason?: string;
442
451
  signature?: string;
452
+ inputSchemaInput?: unknown;
443
453
  };
444
454
  }
445
455
  );