ai 7.0.114 → 7.0.117

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 (34) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/index.js +951 -1250
  3. package/dist/index.js.map +1 -1
  4. package/dist/internal/index.js +159 -212
  5. package/dist/internal/index.js.map +1 -1
  6. package/docs/03-agents/02-building-agents.mdx +2 -2
  7. package/docs/03-agents/08-terminal-ui.mdx +12 -8
  8. package/docs/03-ai-sdk-core/15-tools-and-tool-calling.mdx +6 -1
  9. package/docs/03-ai-sdk-core/17-mcp-apps.mdx +1 -1
  10. package/docs/03-ai-sdk-core/26-reasoning.mdx +2 -2
  11. package/docs/03-ai-sdk-core/32-evaluation.mdx +7 -4
  12. package/docs/03-ai-sdk-core/65-lifecycle-callbacks.mdx +7 -4
  13. package/docs/03-ai-sdk-harnesses/01-overview.mdx +3 -2
  14. package/docs/03-ai-sdk-harnesses/02-harness-agent.mdx +97 -107
  15. package/docs/03-ai-sdk-harnesses/03-tools.mdx +1 -22
  16. package/docs/03-ai-sdk-harnesses/04-skills.mdx +0 -4
  17. package/docs/03-ai-sdk-harnesses/05-harness-adapters.mdx +3 -3
  18. package/docs/03-ai-sdk-harnesses/06-workflow-utilities.mdx +43 -9
  19. package/docs/03-ai-sdk-harnesses/07-ui.mdx +33 -30
  20. package/docs/03-ai-sdk-harnesses/08-terminal-ui.mdx +8 -6
  21. package/docs/04-ai-sdk-ui/01-overview.mdx +1 -1
  22. package/docs/04-ai-sdk-ui/02-chatbot.mdx +9 -7
  23. package/docs/04-ai-sdk-ui/08-object-generation.mdx +4 -1
  24. package/docs/06-advanced/04-caching.mdx +2 -1
  25. package/docs/06-advanced/11-secure-url-fetching.mdx +39 -0
  26. package/docs/07-reference/01-ai-sdk-core/01-generate-text.mdx +1 -1
  27. package/docs/07-reference/02-ai-sdk-ui/03-use-object.mdx +4 -1
  28. package/docs/07-reference/03-ai-sdk-rsc/01-stream-ui.mdx +1 -0
  29. package/docs/07-reference/03-ai-sdk-rsc/02-create-ai.mdx +1 -1
  30. package/docs/07-reference/03-ai-sdk-rsc/03-create-streamable-ui.mdx +1 -1
  31. package/docs/07-reference/06-ai-sdk-tui/01-run-agent-tui.mdx +12 -8
  32. package/package.json +12 -12
  33. package/src/prompt/file-part-data.ts +9 -3
  34. package/src/util/download/download.ts +2 -2
@@ -38,10 +38,10 @@ The AI SDK includes the following harness adapters:
38
38
  | [Claude Code](/providers/ai-sdk-harnesses/claude-code) | Sandbox bridge | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
39
39
  | [Cline](/providers/ai-sdk-harnesses/cline) | Host process | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
40
40
  | [Codex](/providers/ai-sdk-harnesses/codex) | Sandbox bridge | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> |
41
- | [Cursor](/providers/ai-sdk-harnesses/cursor) | Sandbox via ACP | <Check /> | <Check /> | <Cross /> | <Check /> | <Cross /> |
41
+ | [Cursor](/providers/ai-sdk-harnesses/cursor) | Sandbox via ACP | <Check /> | <Check /> | <Cross /> | <Cross /> | <Cross /> |
42
42
  | [Deep Agents](/providers/ai-sdk-harnesses/deepagents) | Sandbox bridge | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> via auto-rejection |
43
43
  | [fx](/providers/ai-sdk-harnesses/fx) | Sandbox via ACP | <Check /> | <Check /> | <Cross /> | <Check /> | <Cross /> |
44
- | [GitHub Copilot](/providers/ai-sdk-harnesses/github-copilot) | Sandbox via ACP | <Check /> | <Check /> | <Cross /> | <Check /> | <Cross /> |
45
- | [Grok Build](/providers/ai-sdk-harnesses/grok-build) | Sandbox via ACP | <Check /> | <Check /> | <Check /> | <Check /> | <Cross /> |
44
+ | [GitHub Copilot](/providers/ai-sdk-harnesses/github-copilot) | Sandbox via ACP | <Check /> | <Check /> | <Cross /> | <Cross /> | <Cross /> |
45
+ | [Grok Build](/providers/ai-sdk-harnesses/grok-build) | Sandbox via ACP | <Check /> | <Check /> | <Check /> | <Cross /> | <Cross /> |
46
46
  | [OpenCode](/providers/ai-sdk-harnesses/opencode) | Sandbox bridge | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> via auto-rejection |
47
47
  | [Pi](/providers/ai-sdk-harnesses/pi) | Host process | <Check /> | <Check /> | <Cross /> | <Check /> | <Check /> |
@@ -23,7 +23,7 @@ before following the examples.
23
23
  <InstallPackages packages="@ai-sdk/workflow-harness workflow" />
24
24
 
25
25
  In addition to the workflow specific packages, install the core harness package,
26
- a harness adapter, and a sandbox provider as shown in
26
+ a harness adapter, and a sandbox adapter as shown in
27
27
  [HarnessAgent](/docs/ai-sdk-harnesses/harness-agent).
28
28
 
29
29
  ## Configuring the Harness Agent
@@ -35,15 +35,10 @@ semantic agent steps, set `stopWhen` to `isStepCount(1)` so one call to
35
35
  ```ts filename='harness-workflow/agent.ts' highlight="13-17"
36
36
  import { HarnessAgent } from '@ai-sdk/harness/agent';
37
37
  import { claudeCode } from '@ai-sdk/harness-claude-code';
38
- import { createVercelSandbox } from '@ai-sdk/sandbox-vercel';
39
38
  import { isStepCount } from 'ai';
40
39
 
41
40
  export const agent = new HarnessAgent({
42
41
  harness: claudeCode,
43
- sandbox: createVercelSandbox({
44
- runtime: 'node24',
45
- ports: [4000],
46
- }),
47
42
  instructions: 'You are a helpful coding assistant.',
48
43
  /*
49
44
  * Only needed for semantic agent-step workflows.
@@ -62,7 +57,7 @@ the shared agent with the highlighted `stopWhen` option shown above, then call
62
57
  ### Defining the Agent Step
63
58
 
64
59
  Keep the Workflow step in its own module and import the agent dynamically inside
65
- the step body. This keeps the agent, sandbox provider, and other Node.js
60
+ the step body. This keeps the agent, sandbox adapter, and other Node.js
66
61
  dependencies out of the workflow bundle.
67
62
 
68
63
  ```ts filename='harness-workflow/agent-step.ts'
@@ -77,10 +72,25 @@ export async function agentStep(
77
72
  'use step';
78
73
 
79
74
  const { agent } = await import('./agent');
75
+ const {
76
+ createVercelNetworkSandboxSession,
77
+ resumeVercelNetworkSandboxSession,
78
+ } = await import('@ai-sdk/sandbox-vercel');
79
+ const sandboxId = `harness-${state.sessionId}`;
80
+ const isFirstStep = state.resumeFrom == null && state.continueFrom == null;
81
+ const sandboxSession = isFirstStep
82
+ ? await createVercelNetworkSandboxSession({
83
+ sandboxId,
84
+ runtime: 'node24',
85
+ ports: [4000],
86
+ template: await agent.getSandboxTemplate(),
87
+ })
88
+ : await resumeVercelNetworkSandboxSession({ sandboxId });
80
89
 
81
90
  return runHarnessAgentStep({
82
91
  agent,
83
92
  state,
93
+ sandboxSession,
84
94
  });
85
95
  }
86
96
  ```
@@ -165,7 +175,9 @@ export async function POST(request: Request) {
165
175
  }
166
176
  ```
167
177
 
168
- The `sessionId` gives the sandbox a stable identity across workflow runs. Keep
178
+ The workflow state `sessionId` identifies the harness session. The step derives
179
+ `sandboxId` from it before first creating the sandbox and uses that same ID to
180
+ reattach on later steps. The state does not serialize a live sandbox. Keep
169
181
  `agent.ts`, `agent-step.ts`, `workflow.ts`, and the route in separate modules so
170
182
  the workflow bundle does not include Node-heavy agent, sandbox, or framework
171
183
  dependencies.
@@ -194,10 +206,25 @@ export async function timeSliceStep(
194
206
  'use step';
195
207
 
196
208
  const { agent } = await import('./agent');
209
+ const {
210
+ createVercelNetworkSandboxSession,
211
+ resumeVercelNetworkSandboxSession,
212
+ } = await import('@ai-sdk/sandbox-vercel');
213
+ const sandboxId = `harness-${state.sessionId}`;
214
+ const isFirstStep = state.resumeFrom == null && state.continueFrom == null;
215
+ const sandboxSession = isFirstStep
216
+ ? await createVercelNetworkSandboxSession({
217
+ sandboxId,
218
+ runtime: 'node24',
219
+ ports: [4000],
220
+ template: await agent.getSandboxTemplate(),
221
+ })
222
+ : await resumeVercelNetworkSandboxSession({ sandboxId });
197
223
 
198
224
  return runHarnessAgentTimeSlice({
199
225
  agent,
200
226
  state,
227
+ sandboxSession,
201
228
  });
202
229
  }
203
230
  ```
@@ -281,11 +308,18 @@ export async function POST(request: Request) {
281
308
  }
282
309
  ```
283
310
 
284
- The `sessionId` gives the sandbox a stable identity across workflow runs. Keep
311
+ The workflow state `sessionId` identifies the harness session. The step derives
312
+ `sandboxId` from it; otherwise, you must persist `sandboxSession.id` separately
313
+ to reattach across steps. Keep
285
314
  `agent.ts`, `time-slice-step.ts`, `workflow.ts`, and the route in separate
286
315
  modules so the workflow bundle does not include Node-heavy agent, sandbox, or
287
316
  framework dependencies.
288
317
 
318
+ `destroyOnFinish` destroys the harness session, but never the supplied sandbox.
319
+ The caller can explicitly call `sandboxSession.destroy()` on the network
320
+ session when no later step needs to reattach. Never destroy it at a
321
+ time-slice boundary where a subsequent step must resume it.
322
+
289
323
  ## Resume Persistence
290
324
 
291
325
  Workflow automatically persists the `HarnessWorkflowState` returned by each
@@ -82,53 +82,53 @@ Define the `HarnessAgent` on the server:
82
82
  ```ts filename='app/api/chat/agent.ts'
83
83
  import { HarnessAgent } from '@ai-sdk/harness/agent';
84
84
  import { claudeCode } from '@ai-sdk/harness-claude-code';
85
- import { createVercelSandbox } from '@ai-sdk/sandbox-vercel';
86
85
 
87
86
  export const agent = new HarnessAgent({
88
87
  harness: claudeCode,
89
- sandbox: createVercelSandbox({
90
- runtime: 'node24',
91
- ports: [4000],
92
- }),
93
88
  instructions: 'You are a helpful coding assistant.',
94
89
  });
95
90
  ```
96
91
 
97
92
  ## Session Store
98
93
 
99
- Persist only the opaque resume state returned by `session.detach()`. If the
94
+ Persist the opaque resume state returned by `session.detach()`. If the
100
95
  turn paused for approval or was otherwise interrupted, that resume state carries
101
96
  the continuation state internally. The chat id can also be the harness
102
- `sessionId`, which gives the sandbox a stable identity across requests and
103
- processes.
97
+ `sessionId`. Here we derive a stable `sandboxId` from that known chat id. If
98
+ you do not derive one, persist the returned `sandboxSession.id` separately.
104
99
 
105
100
  ```ts filename='app/api/chat/session-store.ts'
106
- import type {
107
- HarnessAgentResumeSessionState,
108
- HarnessAgentSession,
109
- } from '@ai-sdk/harness/agent';
101
+ import type { HarnessAgentResumeSessionState } from '@ai-sdk/harness/agent';
102
+ import {
103
+ createVercelNetworkSandboxSession,
104
+ resumeVercelNetworkSandboxSession,
105
+ } from '@ai-sdk/sandbox-vercel';
110
106
 
111
107
  const states: Record<string, HarnessAgentResumeSessionState | undefined> = {};
112
108
 
113
- type SessionFactory = {
114
- createSession(options?: {
115
- sessionId?: string;
116
- resumeFrom?: HarnessAgentResumeSessionState;
117
- }): Promise<HarnessAgentSession>;
118
- };
119
-
120
109
  export async function resumeOrCreateSession({
121
110
  agent,
122
111
  chatId,
123
112
  }: {
124
- agent: SessionFactory;
113
+ agent: typeof import('./agent').agent;
125
114
  chatId: string;
126
115
  }) {
127
116
  const resumeFrom = states[chatId];
128
-
129
- return agent.createSession(
130
- resumeFrom ? { sessionId: chatId, resumeFrom } : { sessionId: chatId },
131
- );
117
+ const sandboxId = `harness-${chatId}`;
118
+ const sandboxSession = resumeFrom
119
+ ? await resumeVercelNetworkSandboxSession({ sandboxId })
120
+ : await createVercelNetworkSandboxSession({
121
+ sandboxId,
122
+ runtime: 'node24',
123
+ ports: [4000],
124
+ template: await agent.getSandboxTemplate(),
125
+ });
126
+ const session = await agent.createSession({
127
+ sessionId: chatId,
128
+ resumeFrom,
129
+ sandboxSession,
130
+ });
131
+ return { session, sandboxSession };
132
132
  }
133
133
 
134
134
  export async function detachAndPersist({
@@ -136,13 +136,15 @@ export async function detachAndPersist({
136
136
  session,
137
137
  }: {
138
138
  chatId: string;
139
- session: HarnessAgentSession;
139
+ session: Awaited<ReturnType<typeof resumeOrCreateSession>>['session'];
140
140
  }) {
141
141
  states[chatId] = await session.detach();
142
142
  }
143
143
  ```
144
144
 
145
- Use durable storage instead of an in-memory map in production.
145
+ Use durable storage instead of an in-memory map in production. The creator
146
+ always creates a new sandbox and fails on a conflicting `sandboxId`; only the
147
+ resume function reattaches an existing one.
146
148
 
147
149
  ## Route
148
150
 
@@ -177,7 +179,7 @@ export async function POST(request: Request) {
177
179
  return createUIMessageStreamResponse({
178
180
  stream: createUIMessageStream({
179
181
  execute: async ({ writer }) => {
180
- const session = await resumeOrCreateSession({ agent, chatId });
182
+ const { session } = await resumeOrCreateSession({ agent, chatId });
181
183
  const result = await agent.stream({ session, messages });
182
184
 
183
185
  writer.merge(
@@ -212,9 +214,10 @@ sandbox warm for the next request. Bridge-backed adapters can usually reattach
212
214
  or replay efficiently. If the turn is unfinished, `detach()` includes the turn
213
215
  continuation state in the returned resume state.
214
216
 
215
- Use `session.stop()` when you want to save resume state and stop the runtime and
216
- sandbox after each turn. The next request resumes from persisted state and
217
- continues any unfinished turn before accepting a new prompt.
217
+ `session.stop()` saves resume state and stops the harness runtime, but does not
218
+ stop a supplied sandbox. The caller owns that network session and should call
219
+ its `destroy()` method when the chat ends, not while a later request needs to
220
+ reattach. Preserve the chosen `sandboxId` across processes.
218
221
 
219
222
  ## Rendering Harness Parts
220
223
 
@@ -19,15 +19,11 @@ session for the lifetime of the terminal UI.
19
19
  ```ts filename='tui.ts'
20
20
  import { HarnessAgent, type HarnessAgentSession } from '@ai-sdk/harness/agent';
21
21
  import { codex } from '@ai-sdk/harness-codex';
22
- import { createVercelSandbox } from '@ai-sdk/sandbox-vercel';
22
+ import { createVercelNetworkSandboxSession } from '@ai-sdk/sandbox-vercel';
23
23
  import { runAgentTUI, type AgentTUIAgent } from '@ai-sdk/tui';
24
24
 
25
25
  const agent = new HarnessAgent({
26
26
  harness: codex,
27
- sandbox: createVercelSandbox({
28
- runtime: 'node24',
29
- ports: [4000],
30
- }),
31
27
  });
32
28
 
33
29
  function createTUIAgent({
@@ -56,7 +52,12 @@ function createTUIAgent({
56
52
  } as AgentTUIAgent;
57
53
  }
58
54
 
59
- const session = await agent.createSession();
55
+ const sandboxSession = await createVercelNetworkSandboxSession({
56
+ runtime: 'node24',
57
+ ports: [4000],
58
+ template: await agent.getSandboxTemplate(),
59
+ });
60
+ const session = await agent.createSession({ sandboxSession });
60
61
 
61
62
  try {
62
63
  await runAgentTUI({
@@ -67,6 +68,7 @@ try {
67
68
  });
68
69
  } finally {
69
70
  await session.destroy();
71
+ await sandboxSession.destroy();
70
72
  }
71
73
  ```
72
74
 
@@ -9,7 +9,7 @@ AI SDK UI is designed to help you build interactive chat, completion, and assist
9
9
 
10
10
  AI SDK UI provides robust abstractions that simplify the complex tasks of managing chat streams and UI updates on the frontend, enabling you to develop dynamic AI-driven interfaces more efficiently. With three main hooks — **`useChat`**, **`useCompletion`**, and **`useObject`** — you can incorporate real-time chat capabilities, text completions, streamed JSON, and interactive assistant features into your app.
11
11
 
12
- - **[`useChat`](/docs/ai-sdk-ui/chatbot)** offers real-time streaming of chat messages, abstracting state management for inputs, messages, loading, and errors, allowing for seamless integration into any UI design.
12
+ - **[`useChat`](/docs/ai-sdk-ui/chatbot)** offers real-time streaming of chat messages and manages message, loading, and error state. Manage input state in your component for seamless integration into any UI design.
13
13
  - **[`useCompletion`](/docs/ai-sdk-ui/completion)** enables you to handle text completions in your applications, managing the prompt input and automatically updating the UI as new completions are streamed.
14
14
  - **[`useObject`](/docs/ai-sdk-ui/object-generation)** is a hook that allows you to consume streamed JSON objects, providing a simple way to handle and display structured data in your application.
15
15
 
@@ -1009,10 +1009,12 @@ These are available as `reasoning-file` parts (`ReasoningFileUIPart`) with
1009
1009
  Some providers such as [Perplexity](/providers/ai-sdk-providers/perplexity#sources) and
1010
1010
  [Google](/providers/ai-sdk-providers/google#sources) include sources in the response.
1011
1011
 
1012
- Currently sources are limited to web pages that ground the response.
1013
- You can forward them to the client with the `sendSources` option:
1012
+ You can forward sources to the client with the `sendSources` option. This example uses the Perplexity Agent API's `fast` preset to search the web. Install `@ai-sdk/perplexity` and set `PERPLEXITY_API_KEY` in your environment:
1014
1013
 
1015
- ```ts filename="app/api/chat/route.ts" highlight="20"
1014
+ <InstallPackages packages="@ai-sdk/perplexity" />
1015
+
1016
+ ```ts filename="app/api/chat/route.ts" highlight="21"
1017
+ import { perplexity } from '@ai-sdk/perplexity';
1016
1018
  import {
1017
1019
  convertToModelMessages,
1018
1020
  createUIMessageStreamResponse,
@@ -1025,7 +1027,7 @@ export async function POST(req: Request) {
1025
1027
  const { messages }: { messages: UIMessage[] } = await req.json();
1026
1028
 
1027
1029
  const result = streamText({
1028
- model: 'perplexity/sonar-pro',
1030
+ model: perplexity('fast'),
1029
1031
  messages: await convertToModelMessages(messages),
1030
1032
  });
1031
1033
 
@@ -1051,7 +1053,7 @@ messages.map(message => (
1051
1053
  {message.parts
1052
1054
  .filter(part => part.type === 'source-url')
1053
1055
  .map(part => (
1054
- <span key={`source-${part.id}`}>
1056
+ <span key={`source-${part.sourceId}`}>
1055
1057
  [
1056
1058
  <a href={part.url} target="_blank">
1057
1059
  {part.title ?? new URL(part.url).hostname}
@@ -1064,8 +1066,8 @@ messages.map(message => (
1064
1066
  {message.parts
1065
1067
  .filter(part => part.type === 'source-document')
1066
1068
  .map(part => (
1067
- <span key={`source-${part.id}`}>
1068
- [<span>{part.title ?? `Document ${part.id}`}</span>]
1069
+ <span key={`source-${part.sourceId}`}>
1070
+ [<span>{part.title ?? `Document ${part.sourceId}`}</span>]
1069
1071
  </span>
1070
1072
  ))}
1071
1073
  </div>
@@ -5,7 +5,10 @@ description: Learn how to use the useObject hook.
5
5
 
6
6
  # Object Generation
7
7
 
8
- <Note>`useObject` is only available in React, Svelte, and Vue.</Note>
8
+ <Note>
9
+ `useObject` is available in React, Svelte, Vue, and the SolidJS community
10
+ integration. Angular provides the equivalent `StructuredObject` service.
11
+ </Note>
9
12
 
10
13
  The [`useObject`](/docs/reference/ai-sdk-ui/use-object) hook allows you to create interfaces that represent a structured JSON object that is being streamed.
11
14
 
@@ -30,6 +30,7 @@ const redis = new Redis({
30
30
  });
31
31
 
32
32
  export const cacheMiddleware: LanguageModelV4Middleware = {
33
+ specificationVersion: 'v4',
33
34
  wrapGenerate: async ({ doGenerate, params }) => {
34
35
  const cacheKey = JSON.stringify(params);
35
36
 
@@ -117,7 +118,7 @@ export const cacheMiddleware: LanguageModelV4Middleware = {
117
118
  replayed from the cache on later requests.
118
119
  </Note>
119
120
 
120
- `LanguageModelV4Middleware` has two methods: `wrapGenerate` and `wrapStream`. `wrapGenerate` is called when using [`generateText`](/docs/reference/ai-sdk-core/generate-text), while `wrapStream` is called when using [`streamText`](/docs/reference/ai-sdk-core/stream-text).
121
+ `LanguageModelV4Middleware` has the `wrapGenerate` and `wrapStream` wrapping methods, plus the optional `transformParams` method. `wrapGenerate` is called when using [`generateText`](/docs/reference/ai-sdk-core/generate-text), while `wrapStream` is called when using [`streamText`](/docs/reference/ai-sdk-core/stream-text).
121
122
 
122
123
  For `wrapGenerate`, you can cache the response directly. Instead, for `wrapStream`, you cache an array of the stream parts, which can then be used with [`simulateReadableStream`](/docs/ai-sdk-core/testing#simulate-ui-message-stream-responses) function to create a simulated `ReadableStream` that returns the cached response. In this way, the cached response is returned chunk-by-chunk as if it were being generated by the model. You can control the initial delay and delay between chunks by adjusting the `initialDelayInMs` and `chunkDelayInMs` parameters of `simulateReadableStream`.
123
124
 
@@ -45,6 +45,45 @@ When the SDK fetches a URL taken from a provider response, it:
45
45
 
46
46
  A blocked URL surfaces as a `DownloadError`.
47
47
 
48
+ ## Fetching untrusted URLs directly
49
+
50
+ For custom integrations using `@ai-sdk/provider-utils`, use `fetchUntrustedUrl`
51
+ when the URL comes from a response or other untrusted input. It validates URLs
52
+ and redirects and withholds credentials and unknown custom headers on the first
53
+ request unless the URL matches a configured origin:
54
+
55
+ ```ts
56
+ import { fetchUntrustedUrl } from '@ai-sdk/provider-utils';
57
+
58
+ const response = await fetchUntrustedUrl({
59
+ url: result.downloadUrl,
60
+ headers: {
61
+ authorization: `Bearer ${apiKey}`,
62
+ accept: 'image/*',
63
+ },
64
+ credentialedOrigin: providerBaseURL,
65
+ });
66
+ ```
67
+
68
+ Here, `Authorization` is sent only when the initial URL has the same origin as
69
+ `providerBaseURL`. Otherwise, only allowlisted metadata such as `Accept`,
70
+ `Range`, and `User-Agent` is sent. A cross-origin redirect drops all caller
71
+ headers except `User-Agent`, and credentials are never reattached on later hops.
72
+
73
+ `credentialedOrigin` must come from your configuration, never from the response
74
+ URL. It does not disable URL validation. For a configured private endpoint, pass
75
+ `trustedOrigin` as well; if `credentialedOrigin` is omitted, `fetchUntrustedUrl`
76
+ uses `trustedOrigin` as the origin allowed to receive credentials. Extra protocol
77
+ metadata can be allowed with `untrustedFirstHopHeaders: ['x-protocol-version']`.
78
+ Only list headers that are safe to disclose to any destination; use a configured
79
+ origin for credentials.
80
+
81
+ The existing `fetchWithValidatedRedirects` helper preserves its first-request
82
+ behavior: it sanitizes risky headers but still forwards authentication and
83
+ custom headers. Switch to `fetchUntrustedUrl` to opt into first-request
84
+ credential isolation. Existing calls to `getFromApi` also retain their behavior;
85
+ pass `credentialedOrigin` when fetching response-supplied URLs with credentials.
86
+
48
87
  ## Self-hosted and local endpoints
49
88
 
50
89
  URLs that are same-origin with the provider endpoint **you configured** (e.g. a
@@ -1071,7 +1071,7 @@ To see `generateText` in action, check out [these examples](#examples).
1071
1071
  name: 'Output.json()',
1072
1072
  type: 'Output',
1073
1073
  description:
1074
- 'Output specification for unstructured JSON generation. When the model generates a text response, it will return a JSON object.',
1074
+ 'Output specification for unstructured JSON generation. When the model generates a text response, it will return any valid JSON value.',
1075
1075
  properties: [
1076
1076
  {
1077
1077
  type: 'Options',
@@ -5,7 +5,10 @@ description: API reference for the useObject hook.
5
5
 
6
6
  # `useObject()`
7
7
 
8
- <Note>`useObject` is only available in React, Svelte, and Vue.</Note>
8
+ <Note>
9
+ `useObject` is available in React, Svelte, Vue, and the SolidJS community
10
+ integration. Angular provides the equivalent `StructuredObject` service.
11
+ </Note>
9
12
 
10
13
  Allows you to consume text streams that represent a JSON object and parse them into a complete object based on a schema.
11
14
  You can use it together with [`streamText`](/docs/reference/ai-sdk-core/stream-text) and [`Output.object()`](/docs/reference/ai-sdk-core/output#output-object) in the backend.
@@ -36,6 +36,7 @@ To see `streamUI` in action, check out [these examples](#examples).
36
36
  },
37
37
  {
38
38
  name: 'instructions',
39
+ isOptional: true,
39
40
  type: 'Instructions',
40
41
  description:
41
42
  'Instructions to use that specify the behavior of the model.'
@@ -40,7 +40,7 @@ Creates a client-server context provider that can be used to wrap parts of your
40
40
  },
41
41
  {
42
42
  name: 'onGetUIState',
43
- type: '() => UIState',
43
+ type: '() => Promise<UIState | undefined>',
44
44
  description: 'is called during SSR to compare and update UI state.',
45
45
  },
46
46
  {
@@ -72,7 +72,7 @@ Create a stream that sends UI from the server to the client. On the client side,
72
72
  },
73
73
  {
74
74
  name: 'error',
75
- type: '(Error) => void',
75
+ type: '(any) => StreamableUIWrapper',
76
76
  description:
77
77
  'Signals that there is an error in the UI stream. It will be thrown on the client side and caught by the nearest error boundary component.',
78
78
  },
@@ -191,17 +191,21 @@ await runAgentTUI({
191
191
  ## Example with Sandbox
192
192
 
193
193
  ```ts
194
- import { createJustBashSandbox } from '@ai-sdk/sandbox-just-bash';
194
+ import { createJustBashNetworkSandboxSession } from '@ai-sdk/sandbox-just-bash';
195
195
 
196
- const sandboxSession = await createJustBashSandbox({
196
+ const sandboxSession = await createJustBashNetworkSandboxSession({
197
197
  cwd: '/home/user',
198
- }).createSession();
199
-
200
- await runAgentTUI({
201
- title: 'Sandbox Assistant',
202
- agent,
203
- sandbox: sandboxSession.restricted(),
204
198
  });
199
+
200
+ try {
201
+ await runAgentTUI({
202
+ title: 'Sandbox Assistant',
203
+ agent,
204
+ sandbox: sandboxSession.restricted(),
205
+ });
206
+ } finally {
207
+ await sandboxSession.destroy();
208
+ }
205
209
  ```
206
210
 
207
211
  The sandbox is forwarded to every `agent.stream()` call as
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "7.0.114",
3
+ "version": "7.0.117",
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.92",
45
+ "@ai-sdk/gateway": "4.0.95",
46
46
  "@ai-sdk/provider": "4.0.18",
47
- "@ai-sdk/provider-utils": "5.0.47"
47
+ "@ai-sdk/provider-utils": "5.0.49"
48
48
  },
49
49
  "devDependencies": {
50
- "@ai-sdk/amazon-bedrock": "5.0.94",
51
- "@ai-sdk/deepseek": "3.0.52",
52
- "@ai-sdk/google": "4.0.80",
53
- "@ai-sdk/groq": "4.0.48",
54
- "@ai-sdk/huggingface": "2.0.55",
55
- "@ai-sdk/moonshotai": "3.0.56",
56
- "@ai-sdk/openai": "4.0.75",
57
- "@ai-sdk/test-server": "2.0.1",
58
- "@ai-sdk/xai": "5.0.8",
50
+ "@ai-sdk/amazon-bedrock": "5.0.97",
51
+ "@ai-sdk/deepseek": "3.0.54",
52
+ "@ai-sdk/google": "4.0.82",
53
+ "@ai-sdk/groq": "4.0.50",
54
+ "@ai-sdk/huggingface": "2.0.57",
55
+ "@ai-sdk/moonshotai": "3.0.58",
56
+ "@ai-sdk/openai": "4.0.78",
57
+ "@ai-sdk/test-server": "2.0.2",
58
+ "@ai-sdk/xai": "5.0.10",
59
59
  "@edge-runtime/vm": "^5.0.0",
60
60
  "@smithy/eventstream-codec": "^4.3.3",
61
61
  "@smithy/util-utf8": "^4.3.3",
@@ -24,7 +24,10 @@ type ConvertResult = {
24
24
  mediaType: string | undefined;
25
25
  };
26
26
 
27
- function convertUrlToFilePartData(url: URL): ConvertResult {
27
+ function convertUrlToFilePartData(
28
+ url: URL,
29
+ originalUrl?: string,
30
+ ): ConvertResult {
28
31
  if (url.protocol === 'data:') {
29
32
  const { mediaType, base64Content } = splitDataUrl(url.toString());
30
33
 
@@ -38,7 +41,10 @@ function convertUrlToFilePartData(url: URL): ConvertResult {
38
41
  return { data: { type: 'data', data: base64Content }, mediaType };
39
42
  }
40
43
 
41
- return { data: { type: 'url', url }, mediaType: undefined };
44
+ return {
45
+ data: { type: 'url', url, ...(originalUrl != null ? { originalUrl } : {}) },
46
+ mediaType: undefined,
47
+ };
42
48
  }
43
49
 
44
50
  function convertUrlStringToFilePartData(content: string): ConvertResult {
@@ -98,7 +104,7 @@ export function convertToLanguageModelV4FilePart(
98
104
  }
99
105
  return convertInlineDataToFilePartData(content.data);
100
106
  case 'url':
101
- return convertUrlToFilePartData(content.url);
107
+ return convertUrlToFilePartData(content.url, content.originalUrl);
102
108
  case 'reference':
103
109
  return {
104
110
  data: { type: 'reference', reference: content.reference },
@@ -3,7 +3,7 @@ import {
3
3
  DownloadError,
4
4
  readResponseWithSizeLimit,
5
5
  DEFAULT_MAX_DOWNLOAD_SIZE,
6
- fetchWithValidatedRedirects,
6
+ fetchUntrustedUrl,
7
7
  withUserAgentSuffix,
8
8
  getRuntimeEnvironmentUserAgent,
9
9
  } from '@ai-sdk/provider-utils';
@@ -36,7 +36,7 @@ export const download = async ({
36
36
  getRuntimeEnvironmentUserAgent(),
37
37
  );
38
38
 
39
- const response = await fetchWithValidatedRedirects({
39
+ const response = await fetchUntrustedUrl({
40
40
  url: urlText,
41
41
  headers,
42
42
  abortSignal,