ai 6.0.220 → 6.0.221

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.
@@ -152,7 +152,7 @@ function detectMediaType({
152
152
  var import_provider_utils2 = require("@ai-sdk/provider-utils");
153
153
 
154
154
  // src/version.ts
155
- var VERSION = true ? "6.0.220" : "0.0.0-test";
155
+ var VERSION = true ? "6.0.221" : "0.0.0-test";
156
156
 
157
157
  // src/util/download/download.ts
158
158
  var download = async ({
@@ -132,7 +132,7 @@ import {
132
132
  } from "@ai-sdk/provider-utils";
133
133
 
134
134
  // src/version.ts
135
- var VERSION = true ? "6.0.220" : "0.0.0-test";
135
+ var VERSION = true ? "6.0.221" : "0.0.0-test";
136
136
 
137
137
  // src/util/download/download.ts
138
138
  var download = async ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "6.0.220",
3
+ "version": "6.0.221",
4
4
  "description": "AI SDK by Vercel - build apps like ChatGPT, Claude, Gemini, and more with a single interface for any model using the Vercel AI Gateway or go direct to OpenAI, Anthropic, Google, or any other model provider.",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -45,9 +45,9 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@opentelemetry/api": "^1.9.0",
48
- "@ai-sdk/gateway": "3.0.144",
48
+ "@ai-sdk/gateway": "3.0.145",
49
49
  "@ai-sdk/provider": "3.0.13",
50
- "@ai-sdk/provider-utils": "4.0.36"
50
+ "@ai-sdk/provider-utils": "4.0.37"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@edge-runtime/vm": "^5.0.0",
@@ -52,6 +52,7 @@ import { asArray } from '../util/as-array';
52
52
  import type { DownloadFunction } from '../util/download/download-function';
53
53
  import { mergeObjects } from '../util/merge-objects';
54
54
  import { prepareRetries } from '../util/prepare-retries';
55
+ import { setAbortTimeout } from '../util/set-abort-timeout';
55
56
  import { VERSION } from '../version';
56
57
  import type {
57
58
  OnFinishEvent,
@@ -684,10 +685,11 @@ export async function generateText<
684
685
 
685
686
  do {
686
687
  // Set up step timeout if configured
687
- const stepTimeoutId =
688
- stepTimeoutMs != null
689
- ? setTimeout(() => stepAbortController!.abort(), stepTimeoutMs)
690
- : undefined;
688
+ const stepTimeoutId = setAbortTimeout({
689
+ abortController: stepAbortController,
690
+ label: 'Step',
691
+ timeoutMs: stepTimeoutMs,
692
+ });
691
693
 
692
694
  try {
693
695
  const stepInputMessages = [...initialMessages, ...responseMessages];
@@ -278,6 +278,7 @@ export const array = <ELEMENT>({
278
278
  });
279
279
  }
280
280
 
281
+ const validatedElements: Array<ELEMENT> = [];
281
282
  for (const element of outerValue.elements) {
282
283
  const validationResult = await safeValidateTypes({
283
284
  value: element,
@@ -294,9 +295,11 @@ export const array = <ELEMENT>({
294
295
  finishReason: context.finishReason,
295
296
  });
296
297
  }
298
+
299
+ validatedElements.push(validationResult.value);
297
300
  }
298
301
 
299
- return outerValue.elements as Array<ELEMENT>;
302
+ return validatedElements;
300
303
  },
301
304
 
302
305
  async parsePartialOutput({ text }: { text: string }) {
@@ -171,10 +171,8 @@ export function runToolsTransformation<TOOLS extends ToolSet>({
171
171
  // keep track of outstanding tool results for stream closing:
172
172
  const outstandingToolResults = new Set<string>();
173
173
 
174
- // keep track of tool inputs for provider-side tool results
175
- const toolInputs = new Map<string, unknown>();
176
-
177
174
  // keep track of parsed tool calls so provider-emitted approval requests can reference them
175
+ // keep track of tool inputs for provider-side tool results
178
176
  const toolCallsByToolCallId = new Map<string, TypedToolCall<TOOLS>>();
179
177
 
180
178
  let canClose = false;
@@ -349,8 +347,6 @@ export function runToolsTransformation<TOOLS extends ToolSet>({
349
347
  break;
350
348
  }
351
349
 
352
- toolInputs.set(toolCall.toolCallId, toolCall.input);
353
-
354
350
  // Only execute tools that are not provider-executed:
355
351
  if (tool.execute != null && toolCall.providerExecuted !== true) {
356
352
  const toolExecutionId = generateId(); // use our own id to guarantee uniqueness
@@ -405,7 +401,7 @@ export function runToolsTransformation<TOOLS extends ToolSet>({
405
401
  type: 'tool-error',
406
402
  toolCallId: chunk.toolCallId,
407
403
  toolName,
408
- input: toolInputs.get(chunk.toolCallId),
404
+ input: toolCall?.input,
409
405
  providerExecuted: true,
410
406
  error: chunk.result,
411
407
  dynamic: chunk.dynamic,
@@ -421,7 +417,7 @@ export function runToolsTransformation<TOOLS extends ToolSet>({
421
417
  type: 'tool-result',
422
418
  toolCallId: chunk.toolCallId,
423
419
  toolName,
424
- input: toolInputs.get(chunk.toolCallId),
420
+ input: toolCall?.input,
425
421
  output: chunk.result,
426
422
  providerExecuted: true,
427
423
  dynamic: chunk.dynamic,
@@ -82,6 +82,7 @@ import { mergeAbortSignals } from '../util/merge-abort-signals';
82
82
  import { mergeObjects } from '../util/merge-objects';
83
83
  import { now as originalNow } from '../util/now';
84
84
  import { prepareRetries } from '../util/prepare-retries';
85
+ import { setAbortTimeout } from '../util/set-abort-timeout';
85
86
  import { collectToolApprovals } from './collect-tool-approvals';
86
87
  import { validateApprovedToolApprovals } from './validate-tool-approvals';
87
88
  import type {
@@ -1567,25 +1568,25 @@ class DefaultStreamTextResult<
1567
1568
  const includeRawChunks = self.includeRawChunks;
1568
1569
 
1569
1570
  // Set up step timeout if configured
1570
- const stepTimeoutId =
1571
- stepTimeoutMs != null
1572
- ? setTimeout(() => stepAbortController!.abort(), stepTimeoutMs)
1573
- : undefined;
1571
+ const stepTimeoutId = setAbortTimeout({
1572
+ abortController: stepAbortController,
1573
+ label: 'Step',
1574
+ timeoutMs: stepTimeoutMs,
1575
+ });
1574
1576
 
1575
1577
  // Set up chunk timeout tracking (will be reset on each chunk)
1576
1578
  let chunkTimeoutId: ReturnType<typeof setTimeout> | undefined =
1577
1579
  undefined;
1578
1580
 
1579
1581
  function resetChunkTimeout() {
1580
- if (chunkTimeoutMs != null) {
1581
- if (chunkTimeoutId != null) {
1582
- clearTimeout(chunkTimeoutId);
1583
- }
1584
- chunkTimeoutId = setTimeout(
1585
- () => chunkAbortController!.abort(),
1586
- chunkTimeoutMs,
1587
- );
1582
+ if (chunkTimeoutId != null) {
1583
+ clearTimeout(chunkTimeoutId);
1588
1584
  }
1585
+ chunkTimeoutId = setAbortTimeout({
1586
+ abortController: chunkAbortController,
1587
+ label: 'Chunk',
1588
+ timeoutMs: chunkTimeoutMs,
1589
+ });
1589
1590
  }
1590
1591
 
1591
1592
  function clearChunkTimeout() {
@@ -1601,6 +1602,13 @@ class DefaultStreamTextResult<
1601
1602
  }
1602
1603
  }
1603
1604
 
1605
+ // The step's stream is registered lazily and consumed long after this
1606
+ // function returns, so the step timer must stay armed past setup. When
1607
+ // the merged abort signal fires (any step/chunk/total timeout or caller
1608
+ // abort), drop both step-scoped timers so neither outlives the step.
1609
+ abortSignal?.addEventListener('abort', clearStepTimeout);
1610
+ abortSignal?.addEventListener('abort', clearChunkTimeout);
1611
+
1604
1612
  try {
1605
1613
  stepFinish = new DelayedPromise<void>();
1606
1614
 
@@ -2244,9 +2252,12 @@ class DefaultStreamTextResult<
2244
2252
  }),
2245
2253
  ),
2246
2254
  );
2247
- } finally {
2255
+ } catch (error) {
2256
+ // Setup failed before the stream was registered, so neither the
2257
+ // stream's flush nor an abort will clear the timers — clear them here.
2248
2258
  clearStepTimeout();
2249
2259
  clearChunkTimeout();
2260
+ throw error;
2250
2261
  }
2251
2262
  }
2252
2263
 
package/src/ui/chat.ts CHANGED
@@ -494,7 +494,7 @@ export abstract class AbstractChat<UI_MESSAGE extends UIMessage> {
494
494
  ? {
495
495
  ...part,
496
496
  state: 'approval-responded',
497
- approval: { id, approved, reason },
497
+ approval: { ...part.approval, id, approved, reason },
498
498
  }
499
499
  : part;
500
500
 
@@ -246,10 +246,12 @@ export async function convertToModelMessages<UI_MESSAGE extends UIMessage>(
246
246
  }
247
247
  }
248
248
 
249
- modelMessages.push({
250
- role: 'assistant',
251
- content,
252
- });
249
+ if (content.length > 0) {
250
+ modelMessages.push({
251
+ role: 'assistant',
252
+ content,
253
+ });
254
+ }
253
255
 
254
256
  // check if there are tool invocations with results in the block
255
257
  // Include non-provider-executed tools, OR provider-executed tools with approval responses
@@ -297,7 +299,7 @@ export async function convertToModelMessages<UI_MESSAGE extends UIMessage>(
297
299
  type: 'error-text' as const,
298
300
  value:
299
301
  toolPart.approval?.reason ??
300
- 'Tool execution denied.',
302
+ 'Tool call execution denied.',
301
303
  },
302
304
  ...(toolPart.callProviderMetadata != null
303
305
  ? { providerOptions: toolPart.callProviderMetadata }
package/src/ui/index.ts CHANGED
@@ -37,6 +37,7 @@ export {
37
37
  getToolName,
38
38
  getToolOrDynamicToolName,
39
39
  isDataUIPart,
40
+ isDynamicToolUIPart,
40
41
  isFileUIPart,
41
42
  isReasoningUIPart,
42
43
  isStaticToolUIPart,
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Schedules a timeout that aborts the given controller with a `TimeoutError`
3
+ * `DOMException`, matching the reason produced by `AbortSignal.timeout(ms)`.
4
+ *
5
+ * @param abortController - The controller to abort when the timeout elapses.
6
+ * If undefined, no timeout is scheduled.
7
+ * @param label - Human-readable label included in the error message
8
+ * (e.g. "Step", "Chunk").
9
+ * @param timeoutMs - Duration in milliseconds before the controller is aborted.
10
+ * If undefined, no timeout is scheduled.
11
+ * @returns The timeout id (suitable for passing to `clearTimeout`), or
12
+ * `undefined` if no timeout was scheduled.
13
+ */
14
+ export function setAbortTimeout({
15
+ abortController,
16
+ label,
17
+ timeoutMs,
18
+ }: {
19
+ abortController: AbortController | undefined;
20
+ label: string;
21
+ timeoutMs: number | undefined;
22
+ }): ReturnType<typeof setTimeout> | undefined {
23
+ if (abortController == null || timeoutMs == null) {
24
+ return undefined;
25
+ }
26
+
27
+ return setTimeout(
28
+ () =>
29
+ abortController.abort(
30
+ new DOMException(
31
+ `${label} timeout of ${timeoutMs}ms exceeded`,
32
+ 'TimeoutError',
33
+ ),
34
+ ),
35
+ timeoutMs,
36
+ );
37
+ }