ai 7.0.89 → 7.0.91

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.
@@ -145,6 +145,7 @@ Here are the capabilities of popular models:
145
145
  | [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-5-codex` | <Check /> | <Check /> | <Check /> | <Check /> |
146
146
  | [OpenAI](/providers/ai-sdk-providers/openai) | `gpt-5-chat-latest` | <Check /> | <Check /> | <Check /> | <Check /> |
147
147
  | [Anthropic](/providers/ai-sdk-providers/anthropic) | `claude-sonnet-5` | <Check /> | <Check /> | <Check /> | <Check /> |
148
+ | [Anthropic](/providers/ai-sdk-providers/anthropic) | `claude-fable-5-1` | <Check /> | <Check /> | <Check /> | <Check /> |
148
149
  | [Anthropic](/providers/ai-sdk-providers/anthropic) | `claude-fable-5` | <Check /> | <Check /> | <Check /> | <Check /> |
149
150
  | [Anthropic](/providers/ai-sdk-providers/anthropic) | `claude-opus-4-8` | <Check /> | <Check /> | <Check /> | <Check /> |
150
151
  | [Anthropic](/providers/ai-sdk-providers/anthropic) | `claude-opus-4-7` | <Check /> | <Check /> | <Check /> | <Check /> |
@@ -154,6 +155,11 @@ Here are the capabilities of popular models:
154
155
  | [Anthropic](/providers/ai-sdk-providers/anthropic) | `claude-opus-4-1` | <Check /> | <Check /> | <Check /> | <Check /> |
155
156
  | [Anthropic](/providers/ai-sdk-providers/anthropic) | `claude-opus-4-0` | <Check /> | <Check /> | <Check /> | <Check /> |
156
157
  | [Anthropic](/providers/ai-sdk-providers/anthropic) | `claude-sonnet-4-0` | <Check /> | <Check /> | <Check /> | <Check /> |
158
+ | [Google](/providers/ai-sdk-providers/google) | `gemini-3.8-flash` | <Check /> | <Check /> | <Check /> | <Check /> |
159
+ | [Google](/providers/ai-sdk-providers/google) | `gemini-3.1-pro-preview` | <Check /> | <Check /> | <Check /> | <Check /> |
160
+ | [Google](/providers/ai-sdk-providers/google) | `gemini-3-pro-preview` | <Check /> | <Check /> | <Check /> | <Check /> |
161
+ | [Google](/providers/ai-sdk-providers/google) | `gemini-2.5-pro` | <Check /> | <Check /> | <Check /> | <Check /> |
162
+ | [Google](/providers/ai-sdk-providers/google) | `gemini-2.5-flash` | <Check /> | <Check /> | <Check /> | <Check /> |
157
163
  | [Mistral](/providers/ai-sdk-providers/mistral) | `pixtral-large-latest` | <Check /> | <Check /> | <Check /> | <Check /> |
158
164
  | [Mistral](/providers/ai-sdk-providers/mistral) | `mistral-large-latest` | <Cross /> | <Check /> | <Check /> | <Check /> |
159
165
  | [Mistral](/providers/ai-sdk-providers/mistral) | `mistral-medium-latest` | <Cross /> | <Check /> | <Check /> | <Check /> |
@@ -156,9 +156,10 @@ Run with `opa test policy.rego policy_test.rego`.
156
156
  ### Errors fail closed
157
157
 
158
158
  - If the backend errors (server unreachable, WASM fault, misbuilt bundle), `opaPolicy` returns `denied` with the error message as the reason.
159
+ - If the backend returns a present but unrecognized decision, such as an unknown `decision` value or a non-boolean legacy `allow` value, `opaPolicy` returns `denied`.
159
160
  - The error never rejects out of the callback and never aborts the run.
160
161
  - A backend outage blocks the affected call rather than silently allowing it.
161
- - This is distinct from a rule that returns no match, which normalizes to `not-applicable` (allow). Use a `default ... deny` rule if you want unmatched calls denied too.
162
+ - This is distinct from a rule that returns no match: `null` or `undefined` normalizes to `not-applicable` (allow). Use a `default ... deny` rule if you want unmatched calls denied too.
162
163
 
163
164
  ## Loading the policy
164
165
 
@@ -284,14 +284,17 @@ If your tools perform sensitive operations (modifying data, spending money, call
284
284
 
285
285
  ### Signing approvals with `experimental_toolApprovalSecret`
286
286
 
287
- When you provide a secret, the server HMAC-signs each approval request at issuance and verifies the signature when the approval is replayed. A forged or tampered approval is rejected before the tool executes.
287
+ When you provide a secret, the server HMAC-signs each approval request at issuance and verifies the signature when the approval is replayed. A forged or tampered approval is rejected before the tool executes. Configure the secret on `ToolLoopAgent` (or pass it directly to `generateText` or `streamText`).
288
288
 
289
- ```ts highlight="5"
290
- const result = await streamText({
289
+ ```ts highlight="6"
290
+ const agent = new ToolLoopAgent({
291
291
  model: __MODEL__,
292
292
  tools: { deleteFile, runQuery },
293
293
  toolApproval: { deleteFile: 'user-approval', runQuery: 'user-approval' },
294
294
  experimental_toolApprovalSecret: process.env.TOOL_APPROVAL_SECRET,
295
+ });
296
+
297
+ const result = await agent.generate({
295
298
  messages,
296
299
  });
297
300
  ```
@@ -308,7 +311,7 @@ The signature binds the approval to the exact tool name, tool call ID, and input
308
311
  ```
309
312
  TOOL_APPROVAL_SECRET=your-generated-secret-here
310
313
  ```
311
- 3. Pass it to `generateText` or `streamText` via `experimental_toolApprovalSecret`.
314
+ 3. Pass it to `ToolLoopAgent`, `generateText`, or `streamText` via `experimental_toolApprovalSecret`.
312
315
 
313
316
  Every serverless instance that might handle a request needs the same secret, since one instance signs the approval and a different instance may verify it on the next turn.
314
317
 
@@ -613,6 +613,21 @@ const agent = new WorkflowAgent({
613
613
  });
614
614
  ```
615
615
 
616
+ Tool input callbacks (`onInputStart`, `onInputDelta`, and
617
+ `onInputAvailable`) are also preserved by `WorkflowAgent`. The model call runs
618
+ inside a durable step, while callback functions remain in the workflow
619
+ context because arbitrary functions cannot cross the step boundary. As a
620
+ result, `WorkflowAgent` records the callback events during the model step and
621
+ replays them in order immediately after that step completes, before tool
622
+ execution and step lifecycle callbacks. They do not run concurrently with
623
+ model generation and cannot provide in-flight cancellation or backpressure.
624
+ Each callback receives its tool's `toolsContext` entry after
625
+ `contextSchema` validation.
626
+
627
+ For highly fragmented tool inputs, `onInputDelta` replay data is part of the
628
+ durable model-step result. Only configure `onInputDelta` when each generated
629
+ delta is needed; omit it to avoid retaining delta replay data.
630
+
616
631
  The deprecated `experimental_onStart` and `experimental_onStepStart` names
617
632
  remain available for backwards compatibility. When both the stable and
618
633
  experimental name are provided in the same constructor or `stream()` call, the
@@ -101,12 +101,89 @@ try {
101
101
  }
102
102
  ```
103
103
 
104
- Well-formed provider error events that arrive after streaming starts are
105
- normalized into [`StreamProviderError`](/docs/reference/ai-sdk-errors/ai-stream-provider-error)
106
- instances. The same instance is supplied to callbacks such as `onError` and to
107
- the `error` part in the full stream. The SDK does not automatically restart a
108
- partially consumed stream; use `isRetryable` to implement application-managed
109
- retries and decide how to handle any partial output.
104
+ ## Retrying provider errors after streaming starts
105
+
106
+ `maxRetries` retries failures that happen while starting a model call. To retry
107
+ well-formed provider error events received after response streaming has begun,
108
+ set `streamRetries`:
109
+
110
+ ```ts highlight="7"
111
+ import { streamText } from 'ai';
112
+ __PROVIDER_IMPORT__;
113
+
114
+ const { textStream } = streamText({
115
+ model: __MODEL__,
116
+ prompt: 'Write a vegetarian lasagna recipe for 4 people.',
117
+ streamRetries: 2,
118
+ });
119
+
120
+ for await (const textPart of textStream) {
121
+ process.stdout.write(textPart);
122
+ }
123
+ ```
124
+
125
+ Stream retries rerun only the failed model step with the same accumulated
126
+ conversation and generation context. Earlier completed steps, including their
127
+ tool calls and tool results, are not replayed. Tool input, tool calls, approval
128
+ requests, tool callbacks, and client-side tool execution from a failed attempt
129
+ are discarded. They are only exposed or executed after an attempt reaches a
130
+ successful model-call finish.
131
+
132
+ Well-formed provider error events are normalized into
133
+ [`StreamProviderError`](/docs/reference/ai-sdk-errors/ai-stream-provider-error)
134
+ instances. The same instance is supplied to `onError` and, if recovery is not
135
+ requested or retries are exhausted, to the `error` part in the full stream. Use
136
+ its `isRetryable` metadata when deciding whether to request recovery.
137
+
138
+ A retry remains part of the same logical step. `onStepStart` runs once for that
139
+ step. `onLanguageModelCallStart` runs for each provider call attempt, while
140
+ `onLanguageModelCallEnd` runs only for attempts that reach a model-call finish.
141
+
142
+ You can also decide dynamically in `onError`. Set `streamRetries` explicitly to
143
+ enable stream recovery; use `0` when a single retry should only be
144
+ callback-directed:
145
+
146
+ ```ts highlight="7-12"
147
+ const result = streamText({
148
+ model: __MODEL__,
149
+ prompt: 'Write a vegetarian lasagna recipe for 4 people.',
150
+ streamRetries: 0,
151
+ onError: ({ error }) => {
152
+ if (isTransientProviderError(error)) {
153
+ return { retry: true };
154
+ }
155
+ },
156
+ });
157
+ ```
158
+
159
+ Callback-directed recovery is limited to one retry per logical step. When
160
+ automatic retries are configured, `onError` can request one additional retry
161
+ after the automatic retry budget is exhausted. This bounds the total number of
162
+ recovery calls for a step to `streamRetries + 1`.
163
+
164
+ When `streamRetries` is omitted, all stream retry behavior is disabled and an
165
+ existing logging-only `onError` callback retains incremental tool streaming.
166
+ An `onError` return value other than `{ retry: true }` keeps its previous
167
+ behavior and does not request recovery. Provider error events that are
168
+ recovered are not emitted as final `error` parts.
169
+
170
+ <Note type="warning">
171
+ Non-tool output emitted before a provider error cannot be retracted. A retried
172
+ model step may therefore append repeated or divergent partial text, reasoning,
173
+ files, or sources to consumer streams. Open text and reasoning parts are ended
174
+ before recovered output begins so UI consumers do not retain them in a
175
+ streaming state. Failed-attempt output is excluded from the recovered step
176
+ result, structured output parsing, response messages, and subsequent model
177
+ steps. Final request and response metadata come from the recovered attempt.
178
+ Retries also add latency and may incur additional provider usage and cost.
179
+ </Note>
180
+
181
+ <Note type="warning">
182
+ Failed-attempt isolation prevents AI SDK client-side tools from executing, but
183
+ it cannot undo work already performed by provider-executed tools. Retrying a
184
+ step after provider-side work may repeat that work or its cost. Use
185
+ idempotency controls for provider-executed side effects.
186
+ </Note>
110
187
 
111
188
  ## Handling stream aborts
112
189
 
@@ -17,6 +17,11 @@ To prevent that, the SDK validates every response-supplied URL before fetching
17
17
  it. This happens automatically inside the provider packages — you don't need to
18
18
  configure anything.
19
19
 
20
+ For authenticated task-status polling, providers can construct the first URL
21
+ from the configured API endpoint. The SDK trusts that configured origin for the
22
+ initial request, but manually follows and validates every redirect away from it.
23
+ MiniMax, Kling AI, and ByteDance video polling use this protected path.
24
+
20
25
  ## What the SDK protects against
21
26
 
22
27
  When the SDK fetches a URL taken from a provider response, it:
@@ -45,7 +50,8 @@ A blocked URL surfaces as a `DownloadError`.
45
50
  URLs that are same-origin with the provider endpoint **you configured** (e.g. a
46
51
  custom `baseURL` pointing at a self-hosted or `localhost` deployment) are
47
52
  exempt from these checks — they target exactly the host you told the SDK to
48
- talk to. Any redirect off that origin is still validated.
53
+ talk to. This also applies to task-status polling. Any redirect off that origin
54
+ is still validated before the redirected request is sent.
49
55
 
50
56
  ## DNS validation across runtimes
51
57
 
@@ -468,6 +468,13 @@ To see `streamText` in action, check out [these examples](#examples).
468
468
  description:
469
469
  'Maximum number of retries. Set to 0 to disable retries. Default: 2.',
470
470
  },
471
+ {
472
+ name: 'streamRetries',
473
+ type: 'number',
474
+ isOptional: true,
475
+ description:
476
+ 'Maximum number of automatic retries for provider error events received after response streaming begins. Retries rerun only the current model step and preserve completed earlier steps and their tool results. Tool-related output and client-side tool work from failed attempts are discarded; other output already emitted cannot be retracted, but is excluded from the recovered step result, structured output parsing, response messages, and subsequent model steps. Final request and response metadata come from the recovered attempt. Open text and reasoning parts are ended before recovered output begins. onStepStart runs once for the logical step, onLanguageModelCallStart runs for each attempt, and onLanguageModelCallEnd runs for attempts that reach a model-call finish. Provider-executed tool work cannot be undone and may repeat. Set to 0 to disable automatic retries while allowing onError to request one callback-directed retry. With automatic retries configured, onError can request at most one additional retry after they are exhausted, bounding total recovery calls to streamRetries + 1. Omit to disable all stream retry behavior and preserve incremental tool streaming for existing onError observers. Default: 0.',
477
+ },
471
478
  {
472
479
  name: 'abortSignal',
473
480
  type: 'AbortSignal',
@@ -1184,10 +1191,10 @@ To see `streamText` in action, check out [these examples](#examples).
1184
1191
  },
1185
1192
  {
1186
1193
  name: 'onError',
1187
- type: '(event: OnErrorResult) => Promise<void> |void',
1194
+ type: 'StreamTextOnErrorCallback | StreamTextOnErrorRetryCallback',
1188
1195
  isOptional: true,
1189
1196
  description:
1190
- 'Callback that is called when an error occurs during streaming. Well-formed mid-stream provider error events are exposed as StreamProviderError instances.',
1197
+ 'Callback that is called when an error occurs during streaming. StreamTextOnErrorCallback preserves the existing PromiseLike<void> | void observer contract. StreamTextOnErrorRetryCallback additionally types `{ retry: true }` results for retry-capable handlers. Well-formed mid-stream provider error events are exposed as StreamProviderError instances. When streamRetries is explicitly configured, return `{ retry: true }` to request one callback-directed retry for the current model step after automatic retries are exhausted. Other return values do not request a retry.',
1191
1198
  properties: [
1192
1199
  {
1193
1200
  type: 'OnErrorResult',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "7.0.89",
3
+ "version": "7.0.91",
4
4
  "type": "module",
5
5
  "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.",
6
6
  "license": "Apache-2.0",
@@ -42,20 +42,20 @@
42
42
  }
43
43
  },
44
44
  "dependencies": {
45
- "@ai-sdk/gateway": "4.0.71",
45
+ "@ai-sdk/gateway": "4.0.73",
46
46
  "@ai-sdk/provider": "4.0.10",
47
- "@ai-sdk/provider-utils": "5.0.35"
47
+ "@ai-sdk/provider-utils": "5.0.36"
48
48
  },
49
49
  "devDependencies": {
50
- "@ai-sdk/amazon-bedrock": "5.0.71",
51
- "@ai-sdk/deepseek": "3.0.38",
52
- "@ai-sdk/google": "4.0.61",
53
- "@ai-sdk/groq": "4.0.36",
54
- "@ai-sdk/huggingface": "2.0.42",
55
- "@ai-sdk/moonshotai": "3.0.44",
56
- "@ai-sdk/openai": "4.0.55",
50
+ "@ai-sdk/amazon-bedrock": "5.0.73",
51
+ "@ai-sdk/deepseek": "3.0.39",
52
+ "@ai-sdk/google": "4.0.63",
53
+ "@ai-sdk/groq": "4.0.37",
54
+ "@ai-sdk/huggingface": "2.0.43",
55
+ "@ai-sdk/moonshotai": "3.0.45",
56
+ "@ai-sdk/openai": "4.0.57",
57
57
  "@ai-sdk/test-server": "2.0.1",
58
- "@ai-sdk/xai": "4.0.52",
58
+ "@ai-sdk/xai": "4.0.54",
59
59
  "@edge-runtime/vm": "^5.0.0",
60
60
  "@smithy/eventstream-codec": "^4.3.3",
61
61
  "@smithy/util-utf8": "^4.3.3",
@@ -14,6 +14,10 @@ import { executeToolCall } from './execute-tool-call';
14
14
  import { isToolExecutionAllowedFinishReason } from './is-tool-execution-allowed-finish-reason';
15
15
  import { resolveToolApproval } from './resolve-tool-approval';
16
16
  import type { LanguageModelStreamPart } from './stream-language-model-call';
17
+ import {
18
+ isStreamRetryAttemptBoundaryPart,
19
+ type StreamRetryAttemptBoundaryPart,
20
+ } from './stream-retry-attempt-boundary';
17
21
  import { maybeSignApproval } from './tool-approval-signature';
18
22
  import type { ToolApprovalConfiguration } from './tool-approval-configuration';
19
23
  import type { TypedToolCall } from './tool-call';
@@ -30,7 +34,12 @@ export type ToolExecutionEndStreamPart = {
30
34
 
31
35
  export type ExecuteToolsStreamPart<TOOLS extends ToolSet = ToolSet> =
32
36
  | LanguageModelStreamPart<TOOLS>
33
- | ToolExecutionEndStreamPart;
37
+ | ToolExecutionEndStreamPart
38
+ | StreamRetryAttemptBoundaryPart;
39
+
40
+ type ExecuteToolsInputStreamPart<TOOLS extends ToolSet> =
41
+ | LanguageModelStreamPart<TOOLS>
42
+ | StreamRetryAttemptBoundaryPart;
34
43
 
35
44
  export function executeToolsFromStream<
36
45
  TOOLS extends ToolSet,
@@ -53,7 +62,7 @@ export function executeToolsFromStream<
53
62
  executeToolInTelemetryContext,
54
63
  runInTracingChannelSpan,
55
64
  }: {
56
- stream: ReadableStream<LanguageModelStreamPart<TOOLS>>;
65
+ stream: ReadableStream<ExecuteToolsInputStreamPart<TOOLS>>;
57
66
  tools: TOOLS | undefined;
58
67
  callId: string;
59
68
  messages: ModelMessage[];
@@ -77,11 +86,11 @@ export function executeToolsFromStream<
77
86
  // forward stream
78
87
  return stream.pipeThrough(
79
88
  new TransformStream<
80
- LanguageModelStreamPart<TOOLS>,
89
+ ExecuteToolsInputStreamPart<TOOLS>,
81
90
  ExecuteToolsStreamPart<TOOLS>
82
91
  >({
83
92
  async transform(
84
- chunk: LanguageModelStreamPart<TOOLS>,
93
+ chunk: ExecuteToolsInputStreamPart<TOOLS>,
85
94
  controller: TransformStreamDefaultController<
86
95
  ExecuteToolsStreamPart<TOOLS>
87
96
  >,
@@ -89,6 +98,11 @@ export function executeToolsFromStream<
89
98
  // immediately forward all chunks
90
99
  controller.enqueue(chunk);
91
100
 
101
+ if (isStreamRetryAttemptBoundaryPart(chunk)) {
102
+ toolCallsToExecute.length = 0;
103
+ return;
104
+ }
105
+
92
106
  const chunkType = chunk.type;
93
107
 
94
108
  switch (chunkType) {
@@ -70,6 +70,8 @@ export {
70
70
  type StreamTextOnChunkCallback,
71
71
  type StreamTextOnEndCallback,
72
72
  type StreamTextOnErrorCallback,
73
+ type StreamTextOnErrorRetryCallback,
74
+ type StreamTextOnErrorResult,
73
75
  type StreamTextTransform,
74
76
  } from './stream-text';
75
77
  export type {
@@ -2,6 +2,14 @@ import type { Context, ModelMessage, ToolSet } from '@ai-sdk/provider-utils';
2
2
  import { createIdMap } from '../util/create-id-map';
3
3
  import { getOwn } from '../util/get-own';
4
4
  import type { LanguageModelStreamPart } from './stream-language-model-call';
5
+ import {
6
+ isStreamRetryAttemptBoundaryPart,
7
+ type StreamRetryAttemptBoundaryPart,
8
+ } from './stream-retry-attempt-boundary';
9
+
10
+ type ToolCallbackStreamPart<TOOLS extends ToolSet> =
11
+ | LanguageModelStreamPart<TOOLS>
12
+ | StreamRetryAttemptBoundaryPart;
5
13
 
6
14
  export function invokeToolCallbacksFromStream<
7
15
  TOOLS extends ToolSet,
@@ -13,12 +21,12 @@ export function invokeToolCallbacksFromStream<
13
21
  abortSignal,
14
22
  runtimeContext,
15
23
  }: {
16
- stream: ReadableStream<LanguageModelStreamPart<TOOLS>>;
24
+ stream: ReadableStream<ToolCallbackStreamPart<TOOLS>>;
17
25
  tools: TOOLS | undefined;
18
26
  stepInputMessages: Array<ModelMessage>;
19
27
  abortSignal: AbortSignal | undefined;
20
28
  runtimeContext: RUNTIME_CONTEXT;
21
- }): ReadableStream<LanguageModelStreamPart<TOOLS>> {
29
+ }): ReadableStream<ToolCallbackStreamPart<TOOLS>> {
22
30
  if (tools == null) return stream;
23
31
 
24
32
  const ongoingToolCallToolNames: Record<string, string> = createIdMap();
@@ -28,6 +36,10 @@ export function invokeToolCallbacksFromStream<
28
36
  async transform(chunk, controller) {
29
37
  controller.enqueue(chunk);
30
38
 
39
+ if (isStreamRetryAttemptBoundaryPart(chunk)) {
40
+ return;
41
+ }
42
+
31
43
  switch (chunk.type) {
32
44
  case 'tool-input-start': {
33
45
  ongoingToolCallToolNames[chunk.id] = chunk.toolName;
@@ -0,0 +1,29 @@
1
+ import type { SharedV4Warning } from '@ai-sdk/provider';
2
+
3
+ const streamRetryAttemptBoundarySymbol = Symbol('streamRetryAttemptBoundary');
4
+
5
+ export type StreamRetryAttemptBoundaryPart = {
6
+ [streamRetryAttemptBoundarySymbol]: true;
7
+ warnings: Array<SharedV4Warning>;
8
+ };
9
+
10
+ export function createStreamRetryAttemptBoundaryPart({
11
+ warnings,
12
+ }: {
13
+ warnings: Array<SharedV4Warning>;
14
+ }): StreamRetryAttemptBoundaryPart {
15
+ return {
16
+ [streamRetryAttemptBoundarySymbol]: true,
17
+ warnings,
18
+ };
19
+ }
20
+
21
+ export function isStreamRetryAttemptBoundaryPart(
22
+ part: unknown,
23
+ ): part is StreamRetryAttemptBoundaryPart {
24
+ return (
25
+ typeof part === 'object' &&
26
+ part != null &&
27
+ streamRetryAttemptBoundarySymbol in part
28
+ );
29
+ }