README.md
# AI SDK Skill
Official Vercel AI SDK skill from [skills.sh/vercel/ai/ai-sdk](https://skills.sh/vercel/ai/ai-sdk).
Helps answer questions about the AI SDK and build AI-powered features using `generateText`, `streamText`, `useChat`, tool calling, structured output, and more.
## See also
- **ai-sdk-6** - Complementary skill focused on AI SDK v6 specific APIs (`ToolLoopAgent`, `Output` patterns, MCP integration, etc.)
- **ai-sdk-7** - Complementary skill focused on AI SDK v7 APIs and migration (`WorkflowAgent`, `HarnessAgent`, tool/runtime context, telemetry, realtime, video, etc.)
references/common-errors.md
---
title: Common Errors
description: Reference for common AI SDK errors and how to resolve them.
---
# Common Errors
Start by checking the installed `ai` major version. AI SDK 6 and 7 share many
symbols, but some migration fixes differ. For v7-specific migration work, prefer
the `ai-sdk-7` skill's
[migration-v6-to-v7.md](../../ai-sdk-7/references/migration-v6-to-v7.md).
## `maxTokens` → `maxOutputTokens`
```typescript
// ❌ Incorrect
const result = await generateText({
model: 'anthropic/claude-opus-5',
maxTokens: 512, // deprecated: use `maxOutputTokens` instead
prompt: 'Write a short story',
});
// ✅ Correct
const result = await generateText({
model: 'anthropic/claude-opus-5',
maxOutputTokens: 512,
prompt: 'Write a short story',
});
```
## `maxSteps` → `stopWhen`
```typescript
// ❌ Incorrect
const result = await generateText({
model: 'anthropic/claude-opus-5',
tools: { weather },
maxSteps: 5, // deprecated: use `stopWhen` instead
prompt: 'What is the weather in NYC?',
});
// ✅ Correct in AI SDK 7
import { generateText, isStepCount } from 'ai';
const result = await generateText({
model: 'anthropic/claude-opus-5',
tools: { weather },
stopWhen: isStepCount(5),
prompt: 'What is the weather in NYC?',
});
```
For AI SDK 6, the helper is `stepCountIs`. For AI SDK 7, it is `isStepCount`.
## `parameters` → `inputSchema` (in tool definition)
```typescript
// ❌ Incorrect
const weatherTool = tool({
description: 'Get weather for a location',
parameters: z.object({
// deprecated: use `inputSchema` instead
location: z.string(),
}),
execute: async ({ location }) => ({ location, temp: 72 }),
});
// ✅ Correct
const weatherTool = tool({
description: 'Get weather for a location',
inputSchema: z.object({
location: z.string(),
}),
execute: async ({ location }) => ({ location, temp: 72 }),
});
```
## `generateObject` → `generateText` with `output`
`generateObject` is deprecated. Use `generateText` with the `output` option instead.
```typescript
// ❌ Deprecated
import { generateObject } from 'ai'; // deprecated: use `generateText` with `output` instead
const result = await generateObject({
// deprecated function
model: 'anthropic/claude-opus-5',
schema: z.object({
// deprecated: use `Output.object({ schema })` instead
recipe: z.object({
name: z.string(),
ingredients: z.array(z.string()),
}),
}),
prompt: 'Generate a recipe for chocolate cake',
});
// ✅ Correct
import { generateText, Output } from 'ai';
const result = await generateText({
model: 'anthropic/claude-opus-5',
output: Output.object({
schema: z.object({
recipe: z.object({
name: z.string(),
ingredients: z.array(z.string()),
}),
}),
}),
prompt: 'Generate a recipe for chocolate cake',
});
console.log(result.output); // typed object
```
## Manual JSON parsing → `generateText` with `output`
```typescript
// ❌ Incorrect
const result = await generateText({
model: 'anthropic/claude-opus-5',
prompt: `Extract the user info as JSON: { "name": string, "age": number }
Input: John is 25 years old`,
});
const parsed = JSON.parse(result.text);
// ✅ Correct
import { generateText, Output } from 'ai';
const result = await generateText({
model: 'anthropic/claude-opus-5',
output: Output.object({
schema: z.object({
name: z.string(),
age: z.number(),
}),
}),
prompt: 'Extract the user info: John is 25 years old',
});
console.log(result.output); // { name: 'John', age: 25 }
```
## Other `output` options
```typescript
// Output.array - for generating arrays of items
const result = await generateText({
model: 'anthropic/claude-opus-5',
output: Output.array({
element: z.object({
city: z.string(),
country: z.string(),
}),
}),
prompt: 'List 5 capital cities',
});
// Output.choice - for selecting from predefined options
const result = await generateText({
model: 'anthropic/claude-opus-5',
output: Output.choice({
options: ['positive', 'negative', 'neutral'] as const,
}),
prompt: 'Classify the sentiment: I love this product!',
});
// Output.json - for untyped JSON output
const result = await generateText({
model: 'anthropic/claude-opus-5',
output: Output.json(),
prompt: 'Return some JSON data',
});
```
## `toDataStreamResponse` → `toUIMessageStreamResponse`
When using `useChat` on the frontend, use UI message streams instead of legacy
data streams. In AI SDK 6, `toUIMessageStreamResponse()` may be available on the
stream result. In AI SDK 7, prefer the stateless helpers from `ai`.
```typescript
// ❌ Incorrect (when using useChat)
const result = streamText({
// config
});
return result.toDataStreamResponse(); // deprecated for useChat: use toUIMessageStreamResponse
// ✅ AI SDK 6 pattern
const result = streamText({
// config
});
return result.toUIMessageStreamResponse();
```
```typescript
// ✅ AI SDK 7 pattern
import {
createUIMessageStreamResponse,
streamText,
toUIMessageStream,
} from 'ai';
const result = streamText({
// config
});
const uiStream = toUIMessageStream({
stream: result.stream,
originalMessages,
});
return createUIMessageStreamResponse({ stream: uiStream });
```
## AI SDK 7 core renames
If a typecheck fails after upgrading to AI SDK 7, search for these common
renames before inventing a workaround:
- `system` -> `instructions`
- `fullStream` -> `stream`
- `experimental_output` -> `output`
- `onFinish` -> `onEnd`
- `onStepFinish` -> `onStepEnd`
- `experimental_onStart` -> `onStart`
- `experimental_onStepStart` -> `onStepStart`
- `experimental_telemetry` -> `telemetry`
- `experimental_include` -> `include`
- `includeRawChunks` -> `include.rawChunks`
- `experimental_context` -> `context`
- `experimental_activeTools` -> `activeTools`
- `ToolCallOptions` -> `ToolExecutionOptions`
- `isToolOrDynamicToolUIPart` -> `isToolUIPart`
- `stepCountIs` -> `isStepCount`
Also check result-shape changes: `result.usage` now covers all steps, while
`result.finalStep.usage` is the previous final-step-only behavior.
## Removed managed input state in `useChat`
The `useChat` hook no longer manages input state internally. You must now manage input state manually.
```tsx
// ❌ Deprecated
import { useChat } from '@ai-sdk/react';
export default function Page() {
const {
input, // deprecated: manage input state manually with useState
handleInputChange, // deprecated: use custom onChange handler
handleSubmit, // deprecated: use sendMessage() instead
} = useChat({
api: '/api/chat', // deprecated: use `transport: new DefaultChatTransport({ api })` instead
});
return (
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleInputChange} />
<button type="submit">Send</button>
</form>
);
}
// ✅ Correct
import { useChat } from '@ai-sdk/react';
import { DefaultChatTransport } from 'ai';
import { useState } from 'react';
export default function Page() {
const [input, setInput] = useState('');
const { sendMessage } = useChat({
transport: new DefaultChatTransport({ api: '/api/chat' }),
});
const handleSubmit = e => {
e.preventDefault();
sendMessage({ text: input });
setInput('');
};
return (
<form onSubmit={handleSubmit}>
<input value={input} onChange={e => setInput(e.target.value)} />
<button type="submit">Send</button>
</form>
);
}
```
## `tool-invocation` → `tool-{toolName}` (typed tool parts)
When rendering messages with `useChat`, use the typed tool part names (`tool-{toolName}`) instead of the generic `tool-invocation` type. This provides better type safety and access to tool-specific input/output types.
> For end-to-end type-safety, see [Type-Safe Agents](type-safe-agents.md).
Typed tool parts also use different property names:
- `part.args` → `part.input`
- `part.result` → `part.output`
```tsx
// ❌ Incorrect - using generic tool-invocation
{
message.parts.map((part, i) => {
switch (part.type) {
case 'text':
return <div key={`${message.id}-${i}`}>{part.text}</div>;
case 'tool-invocation': // deprecated: use typed tool parts instead
return (
<pre key={`${message.id}-${i}`}>
{JSON.stringify(part.toolInvocation, null, 2)}
</pre>
);
}
});
}
// ✅ Correct - using typed tool parts (recommended)
{
message.parts.map(part => {
switch (part.type) {
case 'text':
return part.text;
case 'tool-askForConfirmation':
// handle askForConfirmation tool
break;
case 'tool-getWeatherInformation':
// handle getWeatherInformation tool
break;
}
});
}
// ✅ Alternative - using isToolUIPart as a catch-all
import { isToolUIPart } from 'ai';
{
message.parts.map(part => {
if (part.type === 'text') {
return part.text;
}
if (isToolUIPart(part)) {
// handle any tool part generically
return (
<div key={part.toolCallId}>
{part.toolName}: {part.state}
</div>
);
}
});
}
```
## `useChat` state-dependent property access
Tool part properties are only available in certain states. TypeScript will error if you access them without checking state first.
```tsx
// ❌ Incorrect - input may be undefined during streaming
// TS18048: 'part.input' is possibly 'undefined'
if (part.type === 'tool-getWeather') {
const location = part.input.location;
}
// ✅ Correct - check for input-available or output-available
if (
part.type === 'tool-getWeather' &&
(part.state === 'input-available' || part.state === 'output-available')
) {
const location = part.input.location;
}
// ❌ Incorrect - output is only available after execution
// TS18048: 'part.output' is possibly 'undefined'
if (part.type === 'tool-getWeather') {
const weather = part.output;
}
// ✅ Correct - check for output-available
if (part.type === 'tool-getWeather' && part.state === 'output-available') {
const location = part.input.location;
const weather = part.output;
}
```
## `part.toolInvocation.args` → `part.input`
```tsx
// ❌ Incorrect
if (part.type === 'tool-invocation') {
// deprecated: use `part.input` on typed tool parts instead
const location = part.toolInvocation.args.location;
}
// ✅ Correct
if (
part.type === 'tool-getWeather' &&
(part.state === 'input-available' || part.state === 'output-available')
) {
const location = part.input.location;
}
```
## `part.toolInvocation.result` → `part.output`
```tsx
// ❌ Incorrect
if (part.type === 'tool-invocation') {
// deprecated: use `part.output` on typed tool parts instead
const weather = part.toolInvocation.result;
}
// ✅ Correct
if (part.type === 'tool-getWeather' && part.state === 'output-available') {
const weather = part.output;
}
```
## `part.toolInvocation.toolCallId` → `part.toolCallId`
```tsx
// ❌ Incorrect
if (part.type === 'tool-invocation') {
// deprecated: use `part.toolCallId` on typed tool parts instead
const id = part.toolInvocation.toolCallId;
}
// ✅ Correct
if (part.type === 'tool-getWeather') {
const id = part.toolCallId;
}
```
## Tool invocation states renamed
```tsx
// ❌ Incorrect
switch (part.toolInvocation.state) {
case 'partial-call': // deprecated: use `input-streaming` instead
return <div>Loading...</div>;
case 'call': // deprecated: use `input-available` instead
return <div>Executing...</div>;
case 'result': // deprecated: use `output-available` instead
return <div>Done</div>;
}
// ✅ Correct
switch (part.state) {
case 'input-streaming':
return <div>Loading...</div>;
case 'input-available':
return <div>Executing...</div>;
case 'output-available':
return <div>Done</div>;
}
```
## `addToolResult` → `addToolOutput`
```tsx
// ❌ Incorrect
addToolResult({
// deprecated: use `addToolOutput` instead
toolCallId: part.toolInvocation.toolCallId,
result: 'Yes, confirmed.', // deprecated: use `output` instead
});
// ✅ Correct
addToolOutput({
tool: 'askForConfirmation',
toolCallId: part.toolCallId,
output: 'Yes, confirmed.',
});
```
## `messages` → `uiMessages` in `createAgentUIStreamResponse`
```typescript
// ❌ Incorrect
return createAgentUIStreamResponse({
agent: myAgent,
messages, // incorrect: use `uiMessages` instead
});
// ✅ Correct
return createAgentUIStreamResponse({
agent: myAgent,
uiMessages: messages,
});
```
references/devtools.md
---
title: AI SDK DevTools
description: Debug AI SDK calls by inspecting captured runs and steps.
---
# AI SDK DevTools
> **Development only** — do not ship `devToolsMiddleware` to production.
## Why Use DevTools
DevTools captures all AI SDK calls (`generateText`, `streamText`, `ToolLoopAgent`) to a local JSON file. This lets you inspect LLM requests, responses, tool calls, and multi-step interactions without manually logging.
## Setup
Requires AI SDK 6. Install as a dev dependency:
```bash
bun add -d @ai-sdk/devtools
```
Wrap your model with the middleware:
```ts
import { wrapLanguageModel } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { devToolsMiddleware } from '@ai-sdk/devtools';
const model = wrapLanguageModel({
model: anthropic('claude-sonnet-5'),
middleware: devToolsMiddleware(),
});
```
## Viewing Captured Data
All runs and steps are saved to:
```
.devtools/generations.json
```
Read this file directly to inspect captured data:
```bash
cat .devtools/generations.json | jq
```
Or launch the web UI:
```bash
bunx @ai-sdk/devtools
# Open http://localhost:4983
```
## Data Structure
- **Run**: A complete multi-step interaction grouped by initial prompt
- **Step**: A single LLM call within a run (includes input, output, tool calls, token usage)
references/examples.md
---
title: Canonical Examples (vercel/ai)
description: Fetch provider × feature working examples from the AI SDK repo on demand.
---
# Canonical Examples
The `vercel/ai` repo maintains runnable examples for every supported AI SDK function, provider, and feature combination. These fill the gap between conceptual docs in `node_modules/ai/docs/` and high-level API reference on ai-sdk.dev — working, copy-pasteable code that tracks the current `main` branch.
> **Do not clone the repo.** Fetch individual files on demand via WebFetch or `gh api`.
## Path Pattern
```
examples/ai-functions/src/{function}/{provider}/{feature}.ts
```
## Top-Level Categories
| Category | Directories |
| --------------- | ------------------------------------------------------------ |
| Text generation | `generate-text`, `stream-text`, `stream-text-custom-loop` |
| Agent | `agent` |
| Embedding | `embed`, `embed-many`, `rerank` |
| Media | `generate-image`, `generate-video`, `generate-speech`, `transcribe` |
| Tooling | `tools`, `middleware`, `registry`, `telemetry`, `gateway` |
| Integration | `complex`, `upload-file` |
Each function directory splits by provider: `anthropic`, `openai`, `google`, `amazon`, `azure`, `bedrock`, `cohere`, `groq`, `xai`, `deepseek`, `fireworks`, `huggingface`, and more.
## Discovery
List files under a provider subdirectory:
```bash
gh api repos/vercel/ai/contents/examples/ai-functions/src/{function}/{provider} \
--jq '[.[] | select(.type=="file") | .name]'
```
Or fetch the GitHub tree page with WebFetch:
```
https://github.com/vercel/ai/tree/main/examples/ai-functions/src/{function}/{provider}
```
## Fetching a Single File
Use the raw URL:
```
https://raw.githubusercontent.com/vercel/ai/main/examples/ai-functions/src/{function}/{provider}/{feature}.ts
```
Concrete example — Anthropic prompt caching:
```
https://raw.githubusercontent.com/vercel/ai/main/examples/ai-functions/src/generate-text/anthropic/cache-control.ts
```
## When to Reach for This
- Provider-specific features (Anthropic `adaptive-thinking`, OpenAI `computer-use`, Google grounding)
- Version-suffixed feature flags (e.g. `code-execution-20250825.ts`)
- Multi-step agent patterns not covered in docs
- Middleware compositions
- Streaming edge cases (tool-call streaming, reasoning streams)
## When Not to Reach for This
- Basic API usage — `node_modules/ai/docs/` is faster
- High-level API reference — ai-sdk.dev is faster
- Examples assume you already know the API; they are working patterns, not tutorials
references/type-safe-agents.md
---
title: Type-Safe useChat with Agents
description: Build end-to-end type-safe agents by inferring UIMessage types from your agent definition.
---
# Type-Safe useChat with Agents
Build end-to-end type-safe agents by inferring `UIMessage` types from your agent definition for type-safe UI rendering with `useChat`.
## Recommended Structure
```
lib/
agents/
my-agent.ts # Agent definition + type export
tools/
weather-tool.ts # Individual tool definitions
calculator-tool.ts
```
## Define Tools
```ts
// lib/tools/weather-tool.ts
import { tool } from 'ai';
import { z } from 'zod';
export const weatherTool = tool({
description: 'Get current weather for a location',
inputSchema: z.object({
location: z.string().describe('City name'),
}),
execute: async ({ location }) => {
return { temperature: 72, condition: 'sunny', location };
},
});
```
## Define Agent and Export Type
```ts
// lib/agents/my-agent.ts
import { ToolLoopAgent, InferAgentUIMessage } from 'ai';
import { weatherTool } from '../tools/weather-tool';
import { calculatorTool } from '../tools/calculator-tool';
export const myAgent = new ToolLoopAgent({
model: 'anthropic/claude-sonnet-5',
instructions: 'You are a helpful assistant.',
tools: {
weather: weatherTool,
calculator: calculatorTool,
},
});
// Infer the UIMessage type from the agent
export type MyAgentUIMessage = InferAgentUIMessage<typeof myAgent>;
```
### With Custom Metadata
```ts
// lib/agents/my-agent.ts
import { z } from 'zod';
const metadataSchema = z.object({
createdAt: z.number(),
model: z.string().optional(),
});
type MyMetadata = z.infer<typeof metadataSchema>;
export type MyAgentUIMessage = InferAgentUIMessage<typeof myAgent, MyMetadata>;
```
## Use with `useChat`
```tsx
// app/chat.tsx
import { useChat } from '@ai-sdk/react';
import type { MyAgentUIMessage } from '@/lib/agents/my-agent';
export function Chat() {
const { messages } = useChat<MyAgentUIMessage>();
return (
<div>
{messages.map(message => (
<Message key={message.id} message={message} />
))}
</div>
);
}
```
## Rendering Parts with Type Safety
Tool parts are typed as `tool-{toolName}` based on your agent's tools:
```tsx
function Message({ message }: { message: MyAgentUIMessage }) {
return (
<div>
{message.parts.map((part, i) => {
switch (part.type) {
case 'text':
return <p key={i}>{part.text}</p>;
case 'tool-weather':
// part.input and part.output are fully typed
if (part.state === 'output-available') {
return (
<div key={i}>
Weather in {part.input.location}: {part.output.temperature}F
</div>
);
}
return <div key={i}>Loading weather...</div>;
case 'tool-calculator':
// TypeScript knows this is the calculator tool
return <div key={i}>Calculating...</div>;
default:
return null;
}
})}
</div>
);
}
```
The `part.type` discriminant narrows the type, giving you autocomplete and type checking for `input` and `output` based on each tool's schema.
## Splitting Tool Rendering into Components
When rendering many tools, you may want to split each tool into its own component. Use `UIToolInvocation<TOOL>` to derive a typed invocation from your tool and export it alongside the tool definition:
```ts
// lib/tools/weather-tool.ts
import { tool, UIToolInvocation } from 'ai';
import { z } from 'zod';
export const weatherTool = tool({
description: 'Get current weather for a location',
inputSchema: z.object({
location: z.string().describe('City name'),
}),
execute: async ({ location }) => {
return { temperature: 72, condition: 'sunny', location };
},
});
// Export the invocation type for use in UI components
export type WeatherToolInvocation = UIToolInvocation<typeof weatherTool>;
```
Then import only the type in your component:
```tsx
// components/weather-tool.tsx
import type { WeatherToolInvocation } from '@/lib/tools/weather-tool';
export function WeatherToolComponent({
invocation,
}: {
invocation: WeatherToolInvocation;
}) {
// invocation.input and invocation.output are fully typed
if (invocation.state === 'output-available') {
return (
<div>
Weather in {invocation.input.location}: {invocation.output.temperature}F
</div>
);
}
return <div>Loading weather for {invocation.input?.location}...</div>;
}
```
Use the component in your message renderer:
```tsx
function Message({ message }: { message: MyAgentUIMessage }) {
return (
<div>
{message.parts.map((part, i) => {
switch (part.type) {
case 'text':
return <p key={i}>{part.text}</p>;
case 'tool-weather':
return <WeatherToolComponent key={i} invocation={part} />;
case 'tool-calculator':
return <CalculatorToolComponent key={i} invocation={part} />;
default:
return null;
}
})}
</div>
);
}
```
This approach keeps your tool rendering logic organized while maintaining full type safety, without needing to import the tool implementation into your UI components.
SKILL.md
---
name: ai-sdk
description: 'Answer questions about the AI SDK and help build AI-powered features. Use when developers ask about Vercel AI SDK, generateText, streamText, ToolLoopAgent, useChat, providers, tools, structured output, embeddings, streaming, or adding AI to an app. First identify the installed major version and route version-specific work: use ai-sdk-7 for AI SDK 7 features/migrations such as WorkflowAgent, HarnessAgent, reasoning, runtime/tools context, toolApproval, telemetry, realtime, or v6-to-v7 upgrades; use ai-sdk-6 for v6 code.'
argument-hint: "[question or feature]"
---
## Prerequisites
Before searching docs, check the installed major version in `package.json`,
lockfiles, or `node_modules/ai/package.json`.
- AI SDK 7 implementation or migration work -> use `ai-sdk-7`.
- AI SDK 6 implementation work -> use `ai-sdk-6`.
- Unknown or mixed versions -> continue with this skill until the version is
clear. If starting fresh or no version is pinned, assume the current line
(AI SDK 7) and use `ai-sdk-7`.
Before searching docs, check if `node_modules/ai/docs/` exists. If not, install
**only** the `ai` package using the project's package manager (e.g., `bun add ai`).
Do not install other packages at this stage. Provider packages (e.g., `@ai-sdk/openai`) and client packages (e.g., `@ai-sdk/react`) should be installed later when needed based on user requirements.
### Monorepo path note
In Bun / pnpm / Yarn workspace monorepos, dependencies are usually **not** hoisted to the repo root — they live inside each app's `node_modules/`. If `node_modules/ai/docs/` doesn't exist at the working directory, check workspace locations before assuming docs are missing:
- `apps/*/node_modules/ai/docs/` (e.g. `apps/web/node_modules/ai/docs/`)
- `packages/*/node_modules/ai/docs/`
Glob from the repo root: `apps/*/node_modules/ai/docs/` or `**/node_modules/ai/docs/`. Substitute the resolved path everywhere this skill says `node_modules/ai/docs/` or `node_modules/ai/src/`. The same applies to provider docs at `node_modules/@ai-sdk/<provider>/docs/`.
## Critical: Do Not Trust Internal Knowledge
Everything you know about the AI SDK is outdated or wrong. Your training data contains obsolete APIs, deprecated patterns, and incorrect usage.
**When working with the AI SDK:**
1. Ensure `ai` package is installed (see Prerequisites)
2. Identify the installed major version and use `ai-sdk-7` or `ai-sdk-6` for deep version-specific work
3. Search `node_modules/ai/docs/` and `node_modules/ai/src/` for current APIs
4. If not found locally, search ai-sdk.dev documentation (instructions below)
5. Never rely on memory - always verify against source code or docs
6. **`useChat` has changed significantly** - check [Common Errors](references/common-errors.md) before writing client code
7. **Always fetch current model IDs** - Never use model IDs from memory. A public catalog of current IDs across providers is available at `https://ai-gateway.vercel.sh/v1/models` — useful purely for discovery, not a recommendation to use Gateway as a runtime provider. Example: `curl -s https://ai-gateway.vercel.sh/v1/models | jq -r '[.data[] | select(.id | startswith("anthropic/")) | .id] | reverse | .[]'` (swap `anthropic/` for `openai/`, `google/`, etc.). Pick the model family/tier by capability, cost, and latency requirements; within the chosen family, use the latest version from the catalog (e.g. prefer `claude-sonnet-5` over an older `claude-sonnet-4-x`) — version number alone is not a selection criterion across families.
8. Run typecheck after changes to ensure code is correct
9. **Be minimal** - Only specify options that differ from defaults. When unsure of defaults, check docs or source rather than guessing or over-specifying.
If you cannot find documentation to support your answer, state that explicitly.
## Finding Documentation
### ai@6.0.34+ and ai@7+
Search bundled docs and source in `node_modules/ai/`:
- **Docs**: `grep "query" node_modules/ai/docs/`
- **Source**: `grep "query" node_modules/ai/src/`
Provider packages include docs at `node_modules/@ai-sdk/<provider>/docs/`.
For v7-specific features such as `WorkflowAgent`, `HarnessAgent`, tool context,
reasoning, telemetry, realtime, video, or v6-to-v7 migration, read the
`ai-sdk-7` skill after confirming the installed major version.
### Earlier versions or missing local docs
1. Search: `https://ai-sdk.dev/api/search-docs?q=your_query`
2. Fetch `.md` URLs from results (e.g., `https://ai-sdk.dev/docs/agents/building-agents.md`)
### Working examples
For runnable provider × feature examples (Anthropic cache-control, OpenAI computer-use, Google grounding, etc.), see [examples.md](references/examples.md). Fetch individual files on demand via WebFetch or `gh api` — do not clone the repo.
## When Typecheck Fails
**Before searching source code**, grep [Common Errors](references/common-errors.md) for the failing property or function name. Many type errors are caused by deprecated APIs documented there.
If not found in common-errors.md:
1. Search `node_modules/ai/src/` and `node_modules/ai/docs/`
2. Search ai-sdk.dev (for earlier versions or if not found locally)
## Building and Consuming Agents
### Creating Agents
Use the agent pattern that matches the installed major version and task:
- v6/v7 in-memory agent loops: `ToolLoopAgent`
- v7 durable workflow-backed agents: `WorkflowAgent` from `@ai-sdk/workflow`
- v7 external coding/runtime harnesses: `HarnessAgent` from `@ai-sdk/harness/agent`
Search `node_modules/ai/docs/` for current agent creation APIs before writing code.
**File conventions**: See [type-safe-agents.md](references/type-safe-agents.md) for where to save agents and tools.
**Type Safety**: When consuming agents with `useChat`, always use `InferAgentUIMessage<typeof agent>` for type-safe tool results. See [reference](references/type-safe-agents.md).
### Consuming Agents (Framework-Specific)
Before implementing agent consumption:
1. Check `package.json` to detect the project's framework/stack
2. Search documentation for the framework's quickstart guide
3. Follow the framework-specific patterns for streaming, API routes, and client integration
## References
- [Common Errors](references/common-errors.md) - Renamed parameters reference (parameters → inputSchema, etc.)
- [Type-Safe Agents with useChat](references/type-safe-agents.md) - End-to-end type safety with InferAgentUIMessage
- [DevTools](references/devtools.md) - Local debugging and observability (development only)
- [Canonical Examples](references/examples.md) - Provider × feature working code from vercel/ai/examples
## Related Skills
- `ai-sdk-7` - AI SDK 7 development, HarnessAgent, WorkflowAgent, telemetry, realtime, video, and v6-to-v7 migration
- `ai-sdk-6` - AI SDK 6 development with ToolLoopAgent, Output patterns, MCP, middleware, tools, and UI hooks