ai 7.0.18 → 7.0.19

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 (48) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/index.d.ts +38 -3
  3. package/dist/index.js +160 -77
  4. package/dist/index.js.map +1 -1
  5. package/dist/internal/index.js +43 -31
  6. package/dist/internal/index.js.map +1 -1
  7. package/docs/02-foundations/02-providers-and-models.mdx +57 -52
  8. package/docs/02-getting-started/00-choosing-a-provider.mdx +1 -16
  9. package/docs/02-getting-started/01-navigating-the-library.mdx +12 -12
  10. package/docs/02-getting-started/02-nextjs-app-router.mdx +2 -36
  11. package/docs/02-getting-started/03-nextjs-pages-router.mdx +2 -36
  12. package/docs/02-getting-started/04-svelte.mdx +2 -34
  13. package/docs/02-getting-started/05-nuxt.mdx +2 -36
  14. package/docs/02-getting-started/06-nodejs.mdx +1 -18
  15. package/docs/02-getting-started/07-expo.mdx +3 -62
  16. package/docs/02-getting-started/08-tanstack-start.mdx +2 -36
  17. package/docs/02-getting-started/09-coding-agents.mdx +2 -32
  18. package/docs/03-ai-sdk-core/16-mcp-tools.mdx +44 -0
  19. package/docs/03-ai-sdk-core/30-embeddings.mdx +18 -18
  20. package/docs/03-ai-sdk-core/38-video-generation.mdx +22 -3
  21. package/docs/03-ai-sdk-harnesses/02-harness-agent.mdx +1 -28
  22. package/docs/03-ai-sdk-harnesses/05-harness-adapters.mdx +7 -7
  23. package/docs/03-ai-sdk-harnesses/06-workflow-utilities.mdx +1 -16
  24. package/docs/03-ai-sdk-harnesses/08-terminal-ui.mdx +1 -28
  25. package/docs/04-ai-sdk-ui/01-overview.mdx +5 -5
  26. package/docs/07-reference/01-ai-sdk-core/13-generate-video.mdx +3 -2
  27. package/docs/07-reference/02-ai-sdk-ui/index.mdx +5 -5
  28. package/package.json +4 -4
  29. package/src/generate-text/collect-tool-approvals.ts +17 -4
  30. package/src/generate-text/execute-tool-call.ts +3 -2
  31. package/src/generate-text/execute-tools-from-stream.ts +2 -1
  32. package/src/generate-text/generate-text.ts +5 -4
  33. package/src/generate-text/index.ts +1 -0
  34. package/src/generate-text/invoke-tool-callbacks-from-stream.ts +6 -4
  35. package/src/generate-text/parse-tool-call.ts +5 -4
  36. package/src/generate-text/resolve-tool-approval.ts +9 -6
  37. package/src/generate-text/stream-language-model-call.ts +2 -1
  38. package/src/generate-text/stream-text.ts +3 -2
  39. package/src/generate-text/to-response-messages.ts +4 -3
  40. package/src/generate-text/tool-approval-signature.ts +4 -40
  41. package/src/generate-text/tool-fingerprint.ts +76 -0
  42. package/src/generate-text/validate-tool-approvals.ts +6 -1
  43. package/src/generate-video/generate-video.ts +96 -21
  44. package/src/ui/convert-to-model-messages.ts +3 -2
  45. package/src/ui/process-ui-message-stream.ts +3 -0
  46. package/src/ui/validate-ui-messages.ts +2 -1
  47. package/src/util/canonical-hash.ts +44 -0
  48. package/src/util/get-own.ts +18 -0
@@ -33,22 +33,7 @@ Once you've installed the `ai` package, you already have the full AI SDK documen
33
33
 
34
34
  Install the `ai` package if you haven't already:
35
35
 
36
- <div className="my-4">
37
- <Tabs items={['pnpm', 'npm', 'yarn', 'bun']}>
38
- <Tab>
39
- <Snippet text="pnpm add ai" dark />
40
- </Tab>
41
- <Tab>
42
- <Snippet text="npm install ai" dark />
43
- </Tab>
44
- <Tab>
45
- <Snippet text="yarn add ai" dark />
46
- </Tab>
47
- <Tab>
48
- <Snippet text="bun add ai" dark />
49
- </Tab>
50
- </Tabs>
51
- </div>
36
+ <InstallPackages packages="ai" />
52
37
 
53
38
  After installation, your agent can reference the bundled source code and documentation at paths like:
54
39
 
@@ -70,22 +55,7 @@ AI SDK DevTools gives you full visibility into your AI SDK calls during developm
70
55
 
71
56
  Install the DevTools package:
72
57
 
73
- <div className="my-4">
74
- <Tabs items={['pnpm', 'npm', 'yarn', 'bun']}>
75
- <Tab>
76
- <Snippet text="pnpm add @ai-sdk/devtools" dark />
77
- </Tab>
78
- <Tab>
79
- <Snippet text="npm install @ai-sdk/devtools" dark />
80
- </Tab>
81
- <Tab>
82
- <Snippet text="yarn add @ai-sdk/devtools" dark />
83
- </Tab>
84
- <Tab>
85
- <Snippet text="bun add @ai-sdk/devtools" dark />
86
- </Tab>
87
- </Tabs>
88
- </div>
58
+ <InstallPackages packages="@ai-sdk/devtools" />
89
59
 
90
60
  ### Register the integration
91
61
 
@@ -518,6 +518,50 @@ Your handler must return an object with an `action` field that can be one of:
518
518
  - `'decline'`: User chose not to provide the information.
519
519
  - `'cancel'`: User cancelled the operation entirely.
520
520
 
521
+ ## Detecting tool-definition drift ("rug pull")
522
+
523
+ An MCP server sends tool definitions (name, description, input schema) when your
524
+ app first connects, and you typically review and approve them at that point.
525
+ Nothing in the protocol prevents the server from later serving a _different_
526
+ definition for the same tool name — for example a description carrying injected
527
+ instructions, or an input schema widened with an extra field. Because the SDK
528
+ uses whatever tools you pass on each call, a mutated definition returned by a
529
+ later `mcpClient.tools()` fetch would be used without any comparison to what was
530
+ approved. This is the MCP ["rug pull"](https://invariantlabs.ai/blog/mcp-security-notification-tool-poisoning-attacks)
531
+ class of attack.
532
+
533
+ The AI SDK provides two functions to pin the approved definitions and detect
534
+ changes. `fingerprintTools` digests the server-controlled, security-relevant
535
+ fields of each tool (string `description`, resolved input schema, and `title`)
536
+ into a stable map of tool name to digest. `detectToolDrift` diffs two such maps.
537
+ Your app owns baseline storage and the response to drift (block, force
538
+ re-approval, or alert):
539
+
540
+ ```typescript
541
+ import { fingerprintTools, detectToolDrift } from 'ai';
542
+
543
+ // Trust time (first connect, human-reviewed): capture and persist the baseline.
544
+ const baseline = await fingerprintTools(await mcpClient.tools());
545
+
546
+ // Every later fetch, before handing tools to generateText:
547
+ const tools = await mcpClient.tools();
548
+ const drift = detectToolDrift(await fingerprintTools(tools), baseline);
549
+
550
+ if (drift.changed.length || drift.added.length) {
551
+ // A pinned definition changed, or a new tool appeared. Block, re-approve,
552
+ // or alert per your policy — do not silently pass `tools` to the model.
553
+ }
554
+ ```
555
+
556
+ <Note>
557
+ This detects mutation of a tool's description, input schema, or title — the
558
+ prompt-injection and schema-widening vectors. It cannot detect a
559
+ behavior/endpoint swap where the name, description, and schema are all
560
+ unchanged, because the tool runs remotely on the MCP server and that change is
561
+ invisible to the client. Core stays unopinionated: it does not persist
562
+ baselines or block calls — those are your app's responsibility.
563
+ </Note>
564
+
521
565
  ## Examples
522
566
 
523
567
  You can see MCP in action in the following examples:
@@ -219,21 +219,21 @@ const embeddingModelWithDefaults = wrapEmbeddingModel({
219
219
 
220
220
  Several providers offer embedding models:
221
221
 
222
- | Provider | Model | Embedding Dimensions | Multimodal |
223
- | ----------------------------------------------------------------------------- | ------------------------------- | -------------------- | ------------------- |
224
- | [OpenAI](/providers/ai-sdk-providers/openai#embedding-models) | `text-embedding-3-large` | 3072 | <Cross size={18} /> |
225
- | [OpenAI](/providers/ai-sdk-providers/openai#embedding-models) | `text-embedding-3-small` | 1536 | <Cross size={18} /> |
226
- | [OpenAI](/providers/ai-sdk-providers/openai#embedding-models) | `text-embedding-ada-002` | 1536 | <Cross size={18} /> |
227
- | [Google](/providers/ai-sdk-providers/google#embedding-models) | `gemini-embedding-001` | 3072 | <Cross size={18} /> |
228
- | [Google](/providers/ai-sdk-providers/google#embedding-models) | `gemini-embedding-2` | 3072 | <Check size={18} /> |
229
- | [Google](/providers/ai-sdk-providers/google#embedding-models) | `gemini-embedding-2-preview` | 3072 | <Check size={18} /> |
230
- | [Mistral](/providers/ai-sdk-providers/mistral#embedding-models) | `mistral-embed` | 1024 | <Cross size={18} /> |
231
- | [Cohere](/providers/ai-sdk-providers/cohere#embedding-models) | `embed-english-v3.0` | 1024 | <Cross size={18} /> |
232
- | [Cohere](/providers/ai-sdk-providers/cohere#embedding-models) | `embed-multilingual-v3.0` | 1024 | <Cross size={18} /> |
233
- | [Cohere](/providers/ai-sdk-providers/cohere#embedding-models) | `embed-english-light-v3.0` | 384 | <Cross size={18} /> |
234
- | [Cohere](/providers/ai-sdk-providers/cohere#embedding-models) | `embed-multilingual-light-v3.0` | 384 | <Cross size={18} /> |
235
- | [Cohere](/providers/ai-sdk-providers/cohere#embedding-models) | `embed-english-v2.0` | 4096 | <Cross size={18} /> |
236
- | [Cohere](/providers/ai-sdk-providers/cohere#embedding-models) | `embed-english-light-v2.0` | 1024 | <Cross size={18} /> |
237
- | [Cohere](/providers/ai-sdk-providers/cohere#embedding-models) | `embed-multilingual-v2.0` | 768 | <Cross size={18} /> |
238
- | [Amazon Bedrock](/providers/ai-sdk-providers/amazon-bedrock#embedding-models) | `amazon.titan-embed-text-v1` | 1536 | <Cross size={18} /> |
239
- | [Amazon Bedrock](/providers/ai-sdk-providers/amazon-bedrock#embedding-models) | `amazon.titan-embed-text-v2:0` | 1024 | <Cross size={18} /> |
222
+ | Provider | Model | Embedding Dimensions | Multimodal |
223
+ | ----------------------------------------------------------------------------- | ------------------------------- | -------------------- | ---------- |
224
+ | [OpenAI](/providers/ai-sdk-providers/openai#embedding-models) | `text-embedding-3-large` | 3072 | <Cross /> |
225
+ | [OpenAI](/providers/ai-sdk-providers/openai#embedding-models) | `text-embedding-3-small` | 1536 | <Cross /> |
226
+ | [OpenAI](/providers/ai-sdk-providers/openai#embedding-models) | `text-embedding-ada-002` | 1536 | <Cross /> |
227
+ | [Google](/providers/ai-sdk-providers/google#embedding-models) | `gemini-embedding-001` | 3072 | <Cross /> |
228
+ | [Google](/providers/ai-sdk-providers/google#embedding-models) | `gemini-embedding-2` | 3072 | <Check /> |
229
+ | [Google](/providers/ai-sdk-providers/google#embedding-models) | `gemini-embedding-2-preview` | 3072 | <Check /> |
230
+ | [Mistral](/providers/ai-sdk-providers/mistral#embedding-models) | `mistral-embed` | 1024 | <Cross /> |
231
+ | [Cohere](/providers/ai-sdk-providers/cohere#embedding-models) | `embed-english-v3.0` | 1024 | <Cross /> |
232
+ | [Cohere](/providers/ai-sdk-providers/cohere#embedding-models) | `embed-multilingual-v3.0` | 1024 | <Cross /> |
233
+ | [Cohere](/providers/ai-sdk-providers/cohere#embedding-models) | `embed-english-light-v3.0` | 384 | <Cross /> |
234
+ | [Cohere](/providers/ai-sdk-providers/cohere#embedding-models) | `embed-multilingual-light-v3.0` | 384 | <Cross /> |
235
+ | [Cohere](/providers/ai-sdk-providers/cohere#embedding-models) | `embed-english-v2.0` | 4096 | <Cross /> |
236
+ | [Cohere](/providers/ai-sdk-providers/cohere#embedding-models) | `embed-english-light-v2.0` | 1024 | <Cross /> |
237
+ | [Cohere](/providers/ai-sdk-providers/cohere#embedding-models) | `embed-multilingual-v2.0` | 768 | <Cross /> |
238
+ | [Amazon Bedrock](/providers/ai-sdk-providers/amazon-bedrock#embedding-models) | `amazon.titan-embed-text-v1` | 1536 | <Cross /> |
239
+ | [Amazon Bedrock](/providers/ai-sdk-providers/amazon-bedrock#embedding-models) | `amazon.titan-embed-text-v2:0` | 1024 | <Cross /> |
@@ -194,11 +194,11 @@ const { video } = await generateVideo({
194
194
  });
195
195
  ```
196
196
 
197
- ### Reference Images
197
+ ### Reference Inputs
198
198
 
199
199
  Some video models support reference-to-video generation, where you provide one or
200
- more reference images that the model incorporates into the generated video. Use the `inputReferences` option to pass
201
- these images in a provider-agnostic way:
200
+ more reference images or videos that the model incorporates into the generated video. Use the `inputReferences` option to pass
201
+ these inputs in a provider-agnostic way:
202
202
 
203
203
  ```tsx highlight={"5-8"}
204
204
  const { video } = await generateVideo({
@@ -211,6 +211,25 @@ const { video } = await generateVideo({
211
211
  });
212
212
  ```
213
213
 
214
+ For URL-based video references, use the object form with an explicit `mediaType`:
215
+
216
+ ```tsx highlight={"5-10"}
217
+ const { video } = await generateVideo({
218
+ model: __VIDEO_MODEL__,
219
+ prompt: 'Match the motion in the reference clip',
220
+ inputReferences: [
221
+ {
222
+ data: 'https://example.com/reference.mp4',
223
+ mediaType: 'video/mp4',
224
+ },
225
+ ],
226
+ });
227
+ ```
228
+
229
+ Providers route each reference by its media type (image vs. video) and emit a
230
+ warning when a reference kind is unsupported (for example, providers that accept
231
+ only image references warn and ignore a video reference).
232
+
214
233
  ### Providing a Seed
215
234
 
216
235
  You can provide a seed to the `experimental_generateVideo` function to control the output of the video generation process.
@@ -13,34 +13,7 @@ results while a preconfigured harness powers these results.
13
13
 
14
14
  Install the core harness package, a harness adapter, and a sandbox provider:
15
15
 
16
- <div className="my-4">
17
- <Tabs items={['pnpm', 'npm', 'yarn', 'bun']}>
18
- <Tab>
19
- <Snippet
20
- text="pnpm add @ai-sdk/harness @ai-sdk/harness-claude-code @ai-sdk/sandbox-vercel"
21
- dark
22
- />
23
- </Tab>
24
- <Tab>
25
- <Snippet
26
- text="npm install @ai-sdk/harness @ai-sdk/harness-claude-code @ai-sdk/sandbox-vercel"
27
- dark
28
- />
29
- </Tab>
30
- <Tab>
31
- <Snippet
32
- text="yarn add @ai-sdk/harness @ai-sdk/harness-claude-code @ai-sdk/sandbox-vercel"
33
- dark
34
- />
35
- </Tab>
36
- <Tab>
37
- <Snippet
38
- text="bun add @ai-sdk/harness @ai-sdk/harness-claude-code @ai-sdk/sandbox-vercel"
39
- dark
40
- />
41
- </Tab>
42
- </Tabs>
43
- </div>
16
+ <InstallPackages packages="@ai-sdk/harness @ai-sdk/harness-claude-code @ai-sdk/sandbox-vercel" />
44
17
 
45
18
  Bridge-backed harnesses such as Claude Code and Codex require using real network sandbox
46
19
  like `@ai-sdk/sandbox-vercel`. Host-runtime harnesses such as Pi can also run with
@@ -28,10 +28,10 @@ The AI SDK includes the following harness adapters:
28
28
 
29
29
  ## Adapter Capabilities
30
30
 
31
- | Adapter | Runtime location | Custom tools | Custom skills | Built-in tool approval | Built-in tool filtering |
32
- | ------------------------------------------------------ | ---------------- | ------------------- | ------------------- | ---------------------- | -------------------------------------- |
33
- | [Claude Code](/providers/ai-sdk-harnesses/claude-code) | Sandbox bridge | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
34
- | [Codex](/providers/ai-sdk-harnesses/codex) | Sandbox bridge | <Check size={18} /> | <Check size={18} /> | <Cross size={18} /> | <Cross size={18} /> |
35
- | [Deep Agents](/providers/ai-sdk-harnesses/deepagents) | Sandbox bridge | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> via auto-rejection |
36
- | [OpenCode](/providers/ai-sdk-harnesses/opencode) | Sandbox bridge | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> via auto-rejection |
37
- | [Pi](/providers/ai-sdk-harnesses/pi) | Host process | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
31
+ | Adapter | Runtime location | Custom tools | Custom skills | Built-in tool approval | Built-in tool filtering |
32
+ | ------------------------------------------------------ | ---------------- | ------------ | ------------- | ---------------------- | ---------------------------- |
33
+ | [Claude Code](/providers/ai-sdk-harnesses/claude-code) | Sandbox bridge | <Check /> | <Check /> | <Check /> | <Check /> |
34
+ | [Codex](/providers/ai-sdk-harnesses/codex) | Sandbox bridge | <Check /> | <Check /> | <Cross /> | <Cross /> |
35
+ | [Deep Agents](/providers/ai-sdk-harnesses/deepagents) | Sandbox bridge | <Check /> | <Check /> | <Check /> | <Check /> via auto-rejection |
36
+ | [OpenCode](/providers/ai-sdk-harnesses/opencode) | Sandbox bridge | <Check /> | <Check /> | <Check /> | <Check /> via auto-rejection |
37
+ | [Pi](/providers/ai-sdk-harnesses/pi) | Host process | <Check /> | <Check /> | <Check /> | <Check /> |
@@ -18,22 +18,7 @@ integration.
18
18
 
19
19
  ## Installation
20
20
 
21
- <div className="my-4">
22
- <Tabs items={['pnpm', 'npm', 'yarn', 'bun']}>
23
- <Tab>
24
- <Snippet text="pnpm add @ai-sdk/workflow-harness workflow" dark />
25
- </Tab>
26
- <Tab>
27
- <Snippet text="npm install @ai-sdk/workflow-harness workflow" dark />
28
- </Tab>
29
- <Tab>
30
- <Snippet text="yarn add @ai-sdk/workflow-harness workflow" dark />
31
- </Tab>
32
- <Tab>
33
- <Snippet text="bun add @ai-sdk/workflow-harness workflow" dark />
34
- </Tab>
35
- </Tabs>
36
- </div>
21
+ <InstallPackages packages="@ai-sdk/workflow-harness workflow" />
37
22
 
38
23
  Install the core harness package, a harness adapter, and a sandbox provider as
39
24
  shown in [HarnessAgent](/docs/ai-sdk-harnesses/harness-agent).
@@ -12,34 +12,7 @@ session for the lifetime of the terminal UI.
12
12
 
13
13
  ## Installation
14
14
 
15
- <div className="my-4">
16
- <Tabs items={['pnpm', 'npm', 'yarn', 'bun']}>
17
- <Tab>
18
- <Snippet
19
- text="pnpm add @ai-sdk/tui @ai-sdk/harness @ai-sdk/harness-codex @ai-sdk/sandbox-vercel"
20
- dark
21
- />
22
- </Tab>
23
- <Tab>
24
- <Snippet
25
- text="npm install @ai-sdk/tui @ai-sdk/harness @ai-sdk/harness-codex @ai-sdk/sandbox-vercel"
26
- dark
27
- />
28
- </Tab>
29
- <Tab>
30
- <Snippet
31
- text="yarn add @ai-sdk/tui @ai-sdk/harness @ai-sdk/harness-codex @ai-sdk/sandbox-vercel"
32
- dark
33
- />
34
- </Tab>
35
- <Tab>
36
- <Snippet
37
- text="bun add @ai-sdk/tui @ai-sdk/harness @ai-sdk/harness-codex @ai-sdk/sandbox-vercel"
38
- dark
39
- />
40
- </Tab>
41
- </Tabs>
42
- </div>
15
+ <InstallPackages packages="@ai-sdk/tui @ai-sdk/harness @ai-sdk/harness-codex @ai-sdk/sandbox-vercel" />
43
16
 
44
17
  ## Example
45
18
 
@@ -24,11 +24,11 @@ Here is a comparison of the supported functions across these frameworks:
24
24
 
25
25
  | | [useChat](/docs/reference/ai-sdk-ui/use-chat) | [useCompletion](/docs/reference/ai-sdk-ui/use-completion) | [useObject](/docs/reference/ai-sdk-ui/use-object) | [MCP Apps](/docs/ai-sdk-core/mcp-apps) |
26
26
  | --------------------------------------------------------------- | --------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------- | -------------------------------------- |
27
- | React `@ai-sdk/react` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
28
- | Vue.js `@ai-sdk/vue` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Cross size={18} /> |
29
- | Svelte `@ai-sdk/svelte` | <Check size={18} /> Chat | <Check size={18} /> Completion | <Check size={18} /> StructuredObject | <Cross size={18} /> |
30
- | Angular `@ai-sdk/angular` | <Check size={18} /> Chat | <Check size={18} /> Completion | <Check size={18} /> StructuredObject | <Cross size={18} /> |
31
- | [SolidJS](https://github.com/kodehort/ai-sdk-solid) (community) | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Cross size={18} /> |
27
+ | React `@ai-sdk/react` | <Check /> | <Check /> | <Check /> | <Check /> |
28
+ | Vue.js `@ai-sdk/vue` | <Check /> | <Check /> | <Check /> | <Cross /> |
29
+ | Svelte `@ai-sdk/svelte` | <Check /> Chat | <Check /> Completion | <Check /> StructuredObject | <Cross /> |
30
+ | Angular `@ai-sdk/angular` | <Check /> Chat | <Check /> Completion | <Check /> StructuredObject | <Cross /> |
31
+ | [SolidJS](https://github.com/kodehort/ai-sdk-solid) (community) | <Check /> | <Check /> | <Check /> | <Cross /> |
32
32
 
33
33
  ## Framework Examples
34
34
 
@@ -118,9 +118,10 @@ console.log(videos);
118
118
  },
119
119
  {
120
120
  name: 'inputReferences',
121
- type: 'Array<DataContent>',
121
+ type: 'Array<DataContent | { data: DataContent; mediaType?: string }>',
122
122
  isOptional: true,
123
- description: 'Reference image inputs for reference-to-video generation.',
123
+ description:
124
+ 'Reference image or video inputs for reference-to-video generation. Use the object form with an explicit mediaType for URL-based video references. Providers route each reference by its media type and warn when a reference kind is unsupported.',
124
125
  },
125
126
  {
126
127
  name: 'generateAudio',
@@ -87,11 +87,11 @@ Here is a comparison of the supported functions across these frameworks:
87
87
 
88
88
  | | [useChat](/docs/reference/ai-sdk-ui/use-chat) | [useCompletion](/docs/reference/ai-sdk-ui/use-completion) | [useObject](/docs/reference/ai-sdk-ui/use-object) | [MCP Apps](/docs/ai-sdk-core/mcp-apps) |
89
89
  | --------------------------------------------------------------- | --------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------- | -------------------------------------- |
90
- | React `@ai-sdk/react` | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> |
91
- | Vue.js `@ai-sdk/vue` | <Check size={18} /> Chat | <Check size={18} /> | <Check size={18} /> | <Cross size={18} /> |
92
- | Svelte `@ai-sdk/svelte` | <Check size={18} /> Chat | <Check size={18} /> Completion | <Check size={18} /> StructuredObject | <Cross size={18} /> |
93
- | Angular `@ai-sdk/angular` | <Check size={18} /> Chat | <Check size={18} /> Completion | <Check size={18} /> StructuredObject | <Cross size={18} /> |
94
- | [SolidJS](https://github.com/kodehort/ai-sdk-solid) (community) | <Check size={18} /> | <Check size={18} /> | <Check size={18} /> | <Cross size={18} /> |
90
+ | React `@ai-sdk/react` | <Check /> | <Check /> | <Check /> | <Check /> |
91
+ | Vue.js `@ai-sdk/vue` | <Check /> Chat | <Check /> | <Check /> | <Cross /> |
92
+ | Svelte `@ai-sdk/svelte` | <Check /> Chat | <Check /> Completion | <Check /> StructuredObject | <Cross /> |
93
+ | Angular `@ai-sdk/angular` | <Check /> Chat | <Check /> Completion | <Check /> StructuredObject | <Cross /> |
94
+ | [SolidJS](https://github.com/kodehort/ai-sdk-solid) (community) | <Check /> | <Check /> | <Check /> | <Cross /> |
95
95
 
96
96
  <Note>
97
97
  [Contributions](https://github.com/vercel/ai/blob/main/CONTRIBUTING.md) are
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai",
3
- "version": "7.0.18",
3
+ "version": "7.0.19",
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,9 +42,9 @@
42
42
  }
43
43
  },
44
44
  "dependencies": {
45
- "@ai-sdk/gateway": "4.0.14",
46
- "@ai-sdk/provider": "4.0.2",
47
- "@ai-sdk/provider-utils": "5.0.6"
45
+ "@ai-sdk/gateway": "4.0.15",
46
+ "@ai-sdk/provider": "4.0.3",
47
+ "@ai-sdk/provider-utils": "5.0.7"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@edge-runtime/vm": "^5.0.0",
@@ -36,8 +36,19 @@ export function collectToolApprovals<TOOLS extends ToolSet>({
36
36
  };
37
37
  }
38
38
 
39
- // gather tool calls and prepare lookup
40
- const toolCallsByToolCallId: Record<string, TypedToolCall<TOOLS>> = {};
39
+ // gather tool calls and prepare lookup.
40
+ //
41
+ // These maps are keyed by client-supplied ids (`toolCallId`, `approvalId`)
42
+ // from the message history. Using `Object.create(null)` gives them no
43
+ // prototype, so an id that matches an inherited object property (e.g.
44
+ // `toString`, `constructor`, `__proto__`) is treated as absent instead of
45
+ // resolving to a prototype value and slipping past the `== null` guards
46
+ // below (which would otherwise skip the InvalidToolApproval /
47
+ // ToolCallNotFound checks).
48
+ const toolCallsByToolCallId: Record<
49
+ string,
50
+ TypedToolCall<TOOLS>
51
+ > = Object.create(null);
41
52
  for (const message of messages) {
42
53
  if (message.role === 'assistant' && typeof message.content !== 'string') {
43
54
  const content = message.content;
@@ -51,7 +62,7 @@ export function collectToolApprovals<TOOLS extends ToolSet>({
51
62
 
52
63
  // gather approval responses and prepare lookup
53
64
  const toolApprovalRequestsByApprovalId: Record<string, ToolApprovalRequest> =
54
- {};
65
+ Object.create(null);
55
66
  for (const message of messages) {
56
67
  if (message.role === 'assistant' && typeof message.content !== 'string') {
57
68
  const content = message.content;
@@ -64,7 +75,9 @@ export function collectToolApprovals<TOOLS extends ToolSet>({
64
75
  }
65
76
 
66
77
  // gather tool results from the last tool message
67
- const toolResults: Record<string, TypedToolResult<TOOLS>> = {};
78
+ const toolResults: Record<string, TypedToolResult<TOOLS>> = Object.create(
79
+ null,
80
+ );
68
81
  for (const part of lastMessage.content) {
69
82
  if (part.type === 'tool-result') {
70
83
  toolResults[part.toolCallId] = part as TypedToolResult<TOOLS>;
@@ -13,6 +13,7 @@ import {
13
13
  type TimeoutConfiguration,
14
14
  } from '../prompt/request-options';
15
15
  import type { TelemetryDispatcher } from '../telemetry/telemetry';
16
+ import { getOwn } from '../util/get-own';
16
17
  import { mergeAbortSignals } from '../util/merge-abort-signals';
17
18
  import { notify } from '../util/notify';
18
19
  import { now } from '../util/now';
@@ -83,7 +84,7 @@ export async function executeToolCall<TOOLS extends ToolSet>({
83
84
  | undefined
84
85
  > {
85
86
  const { toolName, toolCallId, input } = toolCall;
86
- const tool = tools?.[toolName];
87
+ const tool = getOwn(tools, toolName);
87
88
 
88
89
  if (!isExecutableTool(tool)) {
89
90
  return undefined;
@@ -91,7 +92,7 @@ export async function executeToolCall<TOOLS extends ToolSet>({
91
92
 
92
93
  const context = await validateToolContext({
93
94
  toolName,
94
- context: toolsContext?.[toolName as keyof typeof toolsContext],
95
+ context: getOwn(toolsContext, toolName),
95
96
  contextSchema: tool.contextSchema,
96
97
  });
97
98
 
@@ -9,6 +9,7 @@ import type {
9
9
  } from '@ai-sdk/provider-utils';
10
10
  import type { TimeoutConfiguration } from '../prompt/request-options';
11
11
  import type { Telemetry, TelemetryDispatcher } from '../telemetry/telemetry';
12
+ import { getOwn } from '../util/get-own';
12
13
  import { executeToolCall } from './execute-tool-call';
13
14
  import { resolveToolApproval } from './resolve-tool-approval';
14
15
  import type { LanguageModelStreamPart } from './stream-language-model-call';
@@ -95,7 +96,7 @@ export function executeToolsFromStream<
95
96
  return;
96
97
  }
97
98
 
98
- const tool = tools?.[chunk.toolName];
99
+ const tool = getOwn(tools, chunk.toolName);
99
100
 
100
101
  if (tool == null) {
101
102
  // ignore tool calls for tools that are not available,
@@ -50,6 +50,7 @@ import {
50
50
  type LanguageModelUsage,
51
51
  } from '../types/usage';
52
52
  import type { DownloadFunction } from '../util/download/download-function';
53
+ import { getOwn } from '../util/get-own';
53
54
  import { mergeAbortSignals } from '../util/merge-abort-signals';
54
55
  import { mergeObjects } from '../util/merge-objects';
55
56
  import { now as originalNow } from '../util/now';
@@ -717,7 +718,7 @@ export async function generateText<
717
718
  const modelOutput = await createToolModelOutput({
718
719
  toolCallId: output.toolCallId,
719
720
  input: output.input,
720
- tool: tools?.[output.toolName],
721
+ tool: getOwn(tools, output.toolName),
721
722
  output:
722
723
  output.type === 'tool-result' ? output.output : output.error,
723
724
  errorMode: output.type === 'tool-error' ? 'text' : 'none',
@@ -1040,7 +1041,7 @@ export async function generateText<
1040
1041
  continue; // ignore invalid tool calls
1041
1042
  }
1042
1043
 
1043
- const tool = tools?.[toolCall.toolName];
1044
+ const tool = getOwn(tools, toolCall.toolName);
1044
1045
 
1045
1046
  if (tool == null) {
1046
1047
  // ignore tool calls for tools that are not available,
@@ -1234,7 +1235,7 @@ export async function generateText<
1234
1235
  // the client tool's result is sent back.
1235
1236
  for (const toolCall of stepToolCalls) {
1236
1237
  if (!toolCall.providerExecuted) continue;
1237
- const tool = tools?.[toolCall.toolName];
1238
+ const tool = getOwn(tools, toolCall.toolName);
1238
1239
  if (tool?.type === 'provider' && tool.supportsDeferredResults) {
1239
1240
  // Check if this tool call already has a result in the current response
1240
1241
  const hasResultInResponse = currentModelResponse.content.some(
@@ -1699,7 +1700,7 @@ function asContent<TOOLS extends ToolSet>({
1699
1700
  // result may be deferred to a later turn. In this case, there's no matching tool-call
1700
1701
  // in the current response.
1701
1702
  if (toolCall == null) {
1702
- const tool = tools?.[part.toolName];
1703
+ const tool = getOwn(tools, part.toolName);
1703
1704
  const supportsDeferredResults =
1704
1705
  tool?.type === 'provider' && tool.supportsDeferredResults;
1705
1706
 
@@ -80,6 +80,7 @@ export type {
80
80
  ToolApprovalConfiguration,
81
81
  ToolApprovalStatus,
82
82
  } from './tool-approval-configuration';
83
+ export { detectToolDrift, fingerprintTools } from './tool-fingerprint';
83
84
  export type { ToolApprovalRequestOutput } from './tool-approval-request-output';
84
85
  export type { ToolApprovalResponseOutput } from './tool-approval-response-output';
85
86
  export type {
@@ -1,4 +1,6 @@
1
1
  import type { Context, ModelMessage, ToolSet } from '@ai-sdk/provider-utils';
2
+ import { createIdMap } from '../util/create-id-map';
3
+ import { getOwn } from '../util/get-own';
2
4
  import type { LanguageModelStreamPart } from './stream-language-model-call';
3
5
 
4
6
  export function invokeToolCallbacksFromStream<
@@ -19,7 +21,7 @@ export function invokeToolCallbacksFromStream<
19
21
  }): ReadableStream<LanguageModelStreamPart<TOOLS>> {
20
22
  if (tools == null) return stream;
21
23
 
22
- const ongoingToolCallToolNames: Record<string, string> = {};
24
+ const ongoingToolCallToolNames: Record<string, string> = createIdMap();
23
25
 
24
26
  return stream.pipeThrough(
25
27
  new TransformStream({
@@ -30,7 +32,7 @@ export function invokeToolCallbacksFromStream<
30
32
  case 'tool-input-start': {
31
33
  ongoingToolCallToolNames[chunk.id] = chunk.toolName;
32
34
 
33
- const tool = tools?.[chunk.toolName];
35
+ const tool = getOwn(tools, chunk.toolName);
34
36
  if (tool?.onInputStart != null) {
35
37
  await tool.onInputStart({
36
38
  toolCallId: chunk.id,
@@ -45,7 +47,7 @@ export function invokeToolCallbacksFromStream<
45
47
 
46
48
  case 'tool-input-delta': {
47
49
  const toolName = ongoingToolCallToolNames[chunk.id];
48
- const tool = tools?.[toolName];
50
+ const tool = getOwn(tools, toolName);
49
51
 
50
52
  if (tool?.onInputDelta != null) {
51
53
  await tool.onInputDelta({
@@ -62,7 +64,7 @@ export function invokeToolCallbacksFromStream<
62
64
 
63
65
  case 'tool-call': {
64
66
  const toolName = ongoingToolCallToolNames[chunk.toolCallId];
65
- const tool = tools?.[toolName];
67
+ const tool = getOwn(tools, toolName);
66
68
 
67
69
  delete ongoingToolCallToolNames[chunk.toolCallId];
68
70
 
@@ -11,6 +11,7 @@ import { InvalidToolInputError } from '../error/invalid-tool-input-error';
11
11
  import { NoSuchToolError } from '../error/no-such-tool-error';
12
12
  import { ToolCallRepairError } from '../error/tool-call-repair-error';
13
13
  import type { Instructions } from '../prompt';
14
+ import { getOwn } from '../util/get-own';
14
15
  import type { DynamicToolCall, TypedToolCall } from './tool-call';
15
16
  import type { ToolCallRepairFunction } from './tool-call-repair-function';
16
17
  import type { ToolInputRefinement } from './tool-input-refinement';
@@ -66,7 +67,7 @@ export async function parseToolCall<TOOLS extends ToolSet>({
66
67
  toolCall,
67
68
  tools,
68
69
  inputSchema: async ({ toolName }) => {
69
- const { inputSchema } = tools[toolName];
70
+ const inputSchema = getOwn(tools, toolName)?.inputSchema;
70
71
  return await asSchema(inputSchema).jsonSchema;
71
72
  },
72
73
  instructions,
@@ -95,7 +96,7 @@ export async function parseToolCall<TOOLS extends ToolSet>({
95
96
  // use parsed input when possible
96
97
  const parsedInput = await safeParseJSON({ text: toolCall.input });
97
98
  const input = parsedInput.success ? parsedInput.value : toolCall.input;
98
- const tool = tools?.[toolCall.toolName];
99
+ const tool = getOwn(tools, toolCall.toolName);
99
100
 
100
101
  // TODO AI SDK 6: special invalid tool call parts
101
102
  return {
@@ -121,7 +122,7 @@ async function refineParsedToolCallInput<TOOLS extends ToolSet>({
121
122
  toolCall: TypedToolCall<TOOLS>;
122
123
  refineToolInput: ToolInputRefinement<TOOLS> | undefined;
123
124
  }): Promise<TypedToolCall<TOOLS>> {
124
- const refine = refineToolInput?.[toolCall.toolName];
125
+ const refine = getOwn(refineToolInput, toolCall.toolName);
125
126
 
126
127
  if (refine == null) {
127
128
  return toolCall;
@@ -169,7 +170,7 @@ async function doParseToolCall<TOOLS extends ToolSet>({
169
170
  }): Promise<TypedToolCall<TOOLS>> {
170
171
  const toolName = toolCall.toolName as keyof TOOLS & string;
171
172
 
172
- const tool = tools[toolName];
173
+ const tool = getOwn(tools, toolName);
173
174
 
174
175
  if (tool == null) {
175
176
  // provider-executed dynamic tools are not part of our list of tools: