ai-sdk-provider-claude-code 3.5.3 → 4.0.1

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.
package/README.md CHANGED
@@ -3,33 +3,34 @@
3
3
  <a href="https://www.npmjs.com/package/ai-sdk-provider-claude-code"><img src="https://img.shields.io/npm/v/ai-sdk-provider-claude-code?color=00A79E" alt="npm version" /></a>
4
4
  <a href="https://www.npmjs.com/package/ai-sdk-provider-claude-code"><img src="https://img.shields.io/npm/unpacked-size/ai-sdk-provider-claude-code?color=00A79E" alt="install size" /></a>
5
5
  <a href="https://www.npmjs.com/package/ai-sdk-provider-claude-code"><img src="https://img.shields.io/npm/dy/ai-sdk-provider-claude-code.svg?color=00A79E" alt="npm downloads" /></a>
6
- <a href="https://nodejs.org/en/about/releases/"><img src="https://img.shields.io/badge/node-%3E%3D18-00A79E" alt="Node.js ≥ 18" /></a>
6
+ <a href="https://nodejs.org/en/about/releases/"><img src="https://img.shields.io/badge/node-%3E%3D22-00A79E" alt="Node.js ≥ 22" /></a>
7
7
  <a href="https://www.npmjs.com/package/ai-sdk-provider-claude-code"><img src="https://img.shields.io/npm/l/ai-sdk-provider-claude-code?color=00A79E" alt="License: MIT" /></a>
8
8
  </p>
9
9
 
10
10
  # AI SDK Provider for Claude Agent SDK
11
11
 
12
- > **Latest Release**: Version 3.x supports AI SDK v6 stable with the Claude Agent SDK. For AI SDK v5 support, use the `ai-sdk-v5` tag.
12
+ > **Latest Release**: Version 4.x supports AI SDK v7 stable with the Claude Agent SDK. Version 3.x moves to maintenance for AI SDK v6 under the `ai-sdk-v6` tag.
13
13
 
14
14
  **ai-sdk-provider-claude-code** lets you use Claude via the [Vercel AI SDK](https://sdk.vercel.ai/docs) through the official `@anthropic-ai/claude-agent-sdk` and the Claude Code CLI.
15
15
 
16
16
  ## Version Compatibility
17
17
 
18
- | Provider Version | AI SDK Version | Underlying SDK | NPM Tag | Status | Branch |
19
- | ---------------- | -------------- | ------------------------------------ | -------------------- | ------ | --------------------------------------------------------------------------------------- |
20
- | 3.x.x | v6 | `@anthropic-ai/claude-agent-sdk` | `latest` | Stable | `main` |
21
- | 2.x.x | v5 | `@anthropic-ai/claude-agent-sdk` | `ai-sdk-v5` | Stable | [`ai-sdk-v5`](https://github.com/ben-vargas/ai-sdk-provider-claude-code/tree/ai-sdk-v5) |
22
- | 1.x.x | v5 | `@anthropic-ai/claude-code` (legacy) | `v1-claude-code-sdk` | Legacy | [`v1`](https://github.com/ben-vargas/ai-sdk-provider-claude-code/tree/v1) |
23
- | 0.x.x | v4 | `@anthropic-ai/claude-code` (legacy) | `ai-sdk-v4` | Legacy | [`ai-sdk-v4`](https://github.com/ben-vargas/ai-sdk-provider-claude-code/tree/ai-sdk-v4) |
18
+ | Provider Version | AI SDK Version | Underlying SDK | NPM Tag | Status | Branch |
19
+ | ---------------- | -------------- | -------------------------------- | -------------------- | ----------- | ----------- |
20
+ | 4.x.x | v7 | `@anthropic-ai/claude-agent-sdk` | `latest` | Stable | `main` |
21
+ | 3.x.x | v6 | `@anthropic-ai/claude-agent-sdk` | `ai-sdk-v6` | Maintenance | `ai-sdk-v6` |
22
+ | 2.x.x | v5 | `@anthropic-ai/claude-agent-sdk` | `ai-sdk-v5` | Legacy | `ai-sdk-v5` |
23
+ | 1.x.x | v5 | `@anthropic-ai/claude-code` | `v1-claude-code-sdk` | Legacy | `v1` |
24
+ | 0.x.x | v4 | `@anthropic-ai/claude-code` | `ai-sdk-v4` | Legacy | `ai-sdk-v4` |
24
25
 
25
26
  Install commands for each line are listed under [Installation](#installation) below.
26
27
 
27
28
  ## Zod Compatibility
28
29
 
29
- **Starting from v3.2.0, this package requires Zod 4.**
30
+ **The 4.x line requires Zod `^4.1.8`.** Version 3.x remains available for AI SDK v6 under the `ai-sdk-v6` tag.
30
31
 
31
32
  ```bash
32
- npm install ai-sdk-provider-claude-code ai zod@^4.0.0
33
+ npm install ai-sdk-provider-claude-code ai zod@^4.1.8
33
34
  ```
34
35
 
35
36
  > **Note:** Zod 3 support was dropped in v3.2.0 due to the underlying `@anthropic-ai/claude-agent-sdk@0.2.x` requiring Zod 4. If you need Zod 3 support, use `ai-sdk-provider-claude-code@3.1.x`.
@@ -48,9 +49,11 @@ claude auth login
48
49
  ### 2. Add the provider
49
50
 
50
51
  ```bash
51
- # For AI SDK v6 (recommended)
52
- npm install ai-sdk-provider-claude-code ai@^6.0.0
53
- # or explicitly: npm install ai-sdk-provider-claude-code@latest
52
+ # For AI SDK v7 (4.x; current latest tag)
53
+ npm install ai-sdk-provider-claude-code ai
54
+
55
+ # For AI SDK v6 maintenance (3.x)
56
+ npm install ai-sdk-provider-claude-code@ai-sdk-v6 ai@^6.0.0
54
57
 
55
58
  # For AI SDK v5
56
59
  npm install ai-sdk-provider-claude-code@ai-sdk-v5 ai@^5.0.0
@@ -72,9 +75,26 @@ Please ensure you have appropriate permissions and comply with all applicable te
72
75
 
73
76
  ## Quick Start
74
77
 
75
- ### AI SDK v6
78
+ ### AI SDK v7 (`latest`)
79
+
80
+ ```typescript
81
+ // npm install ai-sdk-provider-claude-code ai
82
+ import { streamText } from 'ai';
83
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
84
+
85
+ const result = streamText({
86
+ model: claudeCode('haiku'),
87
+ prompt: 'Hello, Claude!',
88
+ });
89
+
90
+ const text = await result.text;
91
+ console.log(text);
92
+ ```
93
+
94
+ ### AI SDK v6 (maintenance)
76
95
 
77
96
  ```typescript
97
+ // npm install ai-sdk-provider-claude-code@ai-sdk-v6 ai@^6.0.0
78
98
  import { streamText } from 'ai';
79
99
  import { claudeCode } from 'ai-sdk-provider-claude-code';
80
100
 
@@ -105,6 +125,24 @@ console.log(text);
105
125
 
106
126
  ## Breaking Changes
107
127
 
128
+ ### Version 4.0.0 (AI SDK v7)
129
+
130
+ This release ports the provider to AI SDK v7 / `LanguageModelV4`, adds first-class Claude Agent SDK callback, query-controller, MCP, and image support, and keeps the v7 support boundaries explicit:
131
+
132
+ - Requires Node.js ≥ 22 and Zod `^4.1.8`
133
+ - ESM-only package output; CommonJS `require()` is no longer available
134
+ - Tool failures now use spec `tool-result` parts/events with `isError: true` instead of the provider-specific `tool-error` stream extension
135
+
136
+ ### Optional AI SDK v7 surfaces not implemented
137
+
138
+ Version 4.0.0 intentionally keeps optional provider surfaces absent unless the Claude Agent SDK has a durable provider-reference mapping:
139
+
140
+ - `ProviderV4.files()` is not implemented yet. The AI SDK interface uploads `{ type: 'data' }` or `{ type: 'text' }` bytes and returns a reusable provider reference, but Claude Agent SDK `0.3.205` exposes no direct upload/reuse API for that contract. This provider forwards inline **image** file parts in prompts; non-image inline files (for example PDFs) emit an unsupported-file call warning and are not forwarded. It does not upload files into durable provider references.
141
+ - Canonical V4 tool-result file parts are replayed into conversation history as text markers like `[File <name>: <mediaType>]`; raw file bytes are not re-sent on replay. Richer tool-result file replay, such as re-sending actual image/file bytes for tool-result file parts, is deferred.
142
+ - `ProviderV4.skills()` is not implemented yet. Claude Code skills are loaded from configured user/project/local skill directories with the existing `skills` setting below; there is no Agent SDK API that uploads a skill bundle and returns an AI SDK provider reference.
143
+ - Workflow serialization is deferred. `@ai-sdk/provider-utils@5.0.5` exposes `WORKFLOW_SERIALIZE`, `WORKFLOW_DESERIALIZE`, and `serializeModelOptions()` for provider model classes in the AI SDK v7 stack, but this provider has not added a serialization contract for provider instances or settings. Callback/function settings such as `canUseTool`, hooks, `logger`, `spawnClaudeCodeProcess`, and `SessionStore` methods are not JSON-serializable and must be recreated by the application.
144
+ - V4 `custom` and `reasoning-file` parts are not emitted as provider output yet. Claude Agent SDK `0.3.205` has no durable reasoning-file artifact output that maps to AI SDK `reasoning-file`; assistant-history `custom` and `reasoning-file` parts have no Claude Code replay representation and are skipped (unknown unsupported content variants still warn).
145
+
108
146
  ### Version 3.0.0 (AI SDK v6 Stable)
109
147
 
110
148
  This version upgrades to AI SDK v6 stable with updated provider types:
@@ -133,11 +171,12 @@ Key changes:
133
171
 
134
172
  ## Models
135
173
 
136
- - **`opus`** - Claude Opus (most capable)
174
+ - **`fable`** - Claude Fable (most capable)
175
+ - **`opus`** - Claude Opus (highly capable)
137
176
  - **`sonnet`** - Claude Sonnet (balanced performance)
138
177
  - **`haiku`** - Claude Haiku (fastest, most cost-effective)
139
178
 
140
- You can also use full model identifiers directly (e.g., `claude-sonnet-4-6`, `claude-opus-4-8`).
179
+ You can also use full model identifiers directly (e.g., `claude-fable-5`, `claude-sonnet-4-6`, `claude-opus-4-8`).
141
180
 
142
181
  ## Documentation
143
182
 
@@ -193,7 +232,7 @@ claude auth login
193
232
 
194
233
  If you're upgrading from version 1.x:
195
234
 
196
- 1. **Update the package**: `npm install ai-sdk-provider-claude-code@latest`
235
+ 1. **Update the package**: `npm install ai-sdk-provider-claude-code@ai-sdk-v5`
197
236
  2. **If you relied on default system prompt or CLAUDE.md**, add explicit configuration:
198
237
  ```ts
199
238
  const model = claudeCode('sonnet', {
@@ -212,37 +251,41 @@ If you're upgrading from version 1.x:
212
251
 
213
252
  ## Structured Outputs
214
253
 
215
- This provider supports **native structured outputs** via the Claude Agent SDK (v0.1.45+). When using `generateObject()` or `streamObject()`, the SDK returns schema-compliant JSON for **supported** JSON Schema features via constrained decoding.
254
+ This provider supports **native structured outputs** via Claude Agent SDK constrained decoding. On the 4.x line (AI SDK v7), use `generateText()` with an `output` specification such as `Output.object({ schema })`, then destructure `output` from the result. For streaming structured output, use `streamText()` with the same `output` setting and read `partialOutputStream` as partial objects arrive.
216
255
 
217
256
  ```typescript
218
- import { generateObject } from 'ai';
257
+ import { generateText, Output } from 'ai';
219
258
  import { claudeCode } from 'ai-sdk-provider-claude-code';
220
259
  import { z } from 'zod';
221
260
 
222
- const result = await generateObject({
261
+ const UserProfileSchema = z.object({
262
+ name: z.string(),
263
+ age: z.number(),
264
+ email: z.string().describe('Email address (validate client-side)'),
265
+ });
266
+
267
+ const { output } = await generateText({
223
268
  model: claudeCode('sonnet'),
224
- schema: z.object({
225
- name: z.string(),
226
- age: z.number(),
227
- email: z.string().describe('Email address (validate client-side)'),
228
- }),
269
+ output: Output.object({ schema: UserProfileSchema }),
229
270
  prompt: 'Generate a user profile for a software developer',
230
271
  });
231
272
 
232
- console.log(result.object); // Matches the schema above
273
+ console.log(output); // Matches the schema above
233
274
  // { name: "Alex Chen", age: 28, email: "alex@example.com" }
234
275
  ```
235
276
 
236
277
  **Benefits:**
237
278
 
238
279
  - ✅ **Schema compliance (supported features)** - Constrained decoding ensures valid output
239
- - ✅ **No JSON parsing errors** - SDK handles all validation
280
+ - ✅ **No JSON parsing errors** - AI SDK handles validation against your schema
240
281
  - ✅ **No prompt engineering** - Schema enforcement is native to the SDK
241
282
  - ✅ **Better performance** - No retry/extraction logic needed
242
283
 
243
- > **Note:** A schema is required for JSON output. Using `responseFormat: { type: 'json' }` without a schema is not supported by Claude Code (matching Anthropic's official provider behavior). An `unsupported-setting` warning will be emitted and the call will be treated as plain text.
284
+ > **Note:** Schema-less JSON output (AI SDK v7 `Output.json()`) is not supported by Claude Code; use `Output.object()` / `Output.array()` / `Output.choice()` with a schema or choices. The provider emits a V4 `unsupported` warning with `feature: 'responseFormat'` and treats the call as plain text.
285
+ >
286
+ > **Current CLI limitation:** Some JSON Schema features can cause the Claude Code CLI to silently fall back to prose (no `structured_output`). The provider mitigates the most common case: `format` keywords (`date-time`, `email`, `uri`, `uuid`, ... — produced by Zod's `.datetime()`, `.email()`, `.url()`, `.uuid()`) are stripped client-side before the schema is sent, with the hint folded into the field's `description` (e.g., `(expected format: email)`). Server-side enforcement of `format` still does not exist in the CLI, but the AI SDK validates `output` against your original Zod schema client-side, so nothing is lost. Complex regex `pattern`s (lookaheads/backreferences) remain unmitigated — `pattern` is passed through untouched because the CLI genuinely rejects some patterns. Keep generation schemas simple and enforce stricter invariants after generation.
244
287
  >
245
- > **Current CLI limitation:** Some JSON Schema features can cause the Claude Code CLI to silently fall back to prose (no `structured_output`). The provider mitigates the most common case: `format` keywords (`date-time`, `email`, `uri`, `uuid`, ... — produced by Zod's `.datetime()`, `.email()`, `.url()`, `.uuid()`) are stripped client-side before the schema is sent, with the hint folded into the field's `description` (e.g. `(expected format: email)`). Server-side enforcement of `format` still does not exist in the CLI, but `generateObject`/`streamObject` validate against your original Zod schema client-side, so nothing is lost. Complex regex `pattern`s (lookaheads/backreferences) remain unmitigated — `pattern` is passed through untouched because the CLI genuinely enforces simple patterns. If the CLI still falls back to prose, the provider first tries to parse the prose as JSON (graceful recovery, with a warning) and otherwise fails fast with a descriptive error instead of letting an opaque `AI_NoObjectGeneratedError` surface downstream. See `examples/structured-output-repro.ts` and `examples/limitations.ts`.
288
+ > If you are staying on the 3.x (AI SDK v6) line, its legacy structured-output examples may still use `generateObject()` / `streamObject()`; new 4.x code should use `generateText()` / `streamText()` with `Output`.
246
289
 
247
290
  ## Core Features
248
291
 
@@ -252,44 +295,56 @@ console.log(result.object); // Matches the schema above
252
295
  - 🎯 Native structured outputs with schema compliance for supported features
253
296
  - 🛑 AbortSignal support
254
297
  - 🔧 Tool management (MCP servers, permissions)
255
- - 🧩 Callbacks (hooks, canUseTool)
298
+ - 🧩 Callbacks (`onSdkMessage`, task/hook/MCP status events, `canUseTool`, `onElicitation`)
299
+ - 🎛️ Query controller access for safe live-session controls
300
+
301
+ ## AI SDK v7 app-level features
302
+
303
+ DevTools and OpenTelemetry/OTel telemetry registration are app-level `ai` package features. This provider exposes standard AI SDK v7 metadata and stream parts for them, but adds no runtime dependencies for DevTools or OTel.
256
304
 
257
305
  ## Agent SDK Options (Advanced)
258
306
 
259
307
  This provider exposes Agent SDK options directly. Key options include:
260
308
 
261
- | Option | Description |
262
- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
263
- | `betas` | Enable beta features (e.g., `['context-1m-2025-08-07']`) |
264
- | `sandbox` | Configure sandbox behavior (`{ enabled: true }`). Cannot be combined with a `settings` file path (inline `settings` objects are fine) |
265
- | `plugins` | Load custom plugins from local paths |
266
- | `resumeSessionAt` | Resume session at a specific message UUID |
267
- | `enableFileCheckpointing` | Enable file rewind support |
268
- | `maxBudgetUsd` | Maximum budget in USD for the query |
269
- | `tools` | Tool configuration (array of names or preset) |
270
- | `allowDangerouslySkipPermissions` | Allow bypassing permissions |
271
- | `persistSession` | When `false`, disables session persistence to disk (v3.2.0+) |
272
- | `spawnClaudeCodeProcess` | Custom process spawner for VMs/containers (v3.2.0+) |
273
- | `permissionMode` | Permission mode: `'default'`, `'acceptEdits'`, `'bypassPermissions'`, `'plan'`, `'dontAsk'`, `'auto'` (`'auto'` and `'dontAsk'` added in SDK 0.3.x; `'delegate'` was removed in SDK 0.3.x and the CLI rejects it, so the provider rejects it at validation time) |
274
- | `sessionId` | Use a specific session ID for deterministic tracking and correlation (v3.4.0+). Must be a valid UUID; cannot be combined with `continue`/`resume` unless `forkSession` is also set |
275
- | `debug` | Enable programmatic debug logging from the SDK (v3.4.0+) |
276
- | `debugFile` | Path to a file for SDK debug log output (v3.4.0+) |
277
- | `effort` | Effort level: `'low'`, `'medium'`, `'high'`, `'xhigh'`, or `'max'` |
278
- | `thinking` | Thinking config: `{ type: 'adaptive' }`, `{ type: 'enabled', budgetTokens?: number }`, or `{ type: 'disabled' }` |
279
- | `promptSuggestions` | Enable prompt suggestions (`boolean`) |
280
- | `skills` | Enable skills for the session: `'all'` or an array of skill names (v3.5.0+) |
281
- | `settings` | Inline `Settings` object or path to a settings JSON file (v3.5.0+) |
282
- | `managedSettings` | Restrictive policy-tier settings enforced on the subprocess (v3.5.0+) |
283
- | `toolAliases` | Map built-in tool names to replacement tools, e.g. `{ Bash: 'mcp__workspace__bash' }` (v3.5.0+) |
284
- | `toolConfig` | Per-tool configuration for built-in tools, e.g. `{ askUserQuestion: { previewFormat: 'html' } }` (v3.5.0+) |
285
- | `planModeInstructions` | Custom workflow instructions for plan mode (v3.5.0+) |
286
- | `title` | Custom title for a new session (v3.5.0+) |
287
- | `forwardSubagentText` | Forward subagent text/thinking blocks for nested transcripts (v3.5.0+) |
288
- | `agentProgressSummaries` | Periodic AI-generated progress summaries for running subagents (v3.5.0+) |
289
- | `includeHookEvents` | Include hook lifecycle events in the output stream (v3.5.0+) |
290
- | `fallbackModel` | Fallback model(s) if the primary is overloaded — accepts a comma-separated list to try in order. Must differ from the main model |
291
- | `onUserDialog` | Callback rendering blocking CLI dialogs (`request_user_dialog`); see **User dialogs** below |
292
- | `supportedDialogKinds` | Dialog kinds your `onUserDialog` can render; required for dialogs to be emitted at all |
309
+ | Option | Description |
310
+ | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
311
+ | `betas` | Enable beta features (e.g., `['context-1m-2025-08-07']`) |
312
+ | `sandbox` | Configure sandbox behavior (`{ enabled: true }`). Cannot be combined with a `settings` file path (inline `settings` objects are fine) |
313
+ | `plugins` | Load custom plugins from local paths |
314
+ | `resumeSessionAt` | Resume session at a specific message UUID |
315
+ | `enableFileCheckpointing` | Enable file rewind support |
316
+ | `maxBudgetUsd` | Maximum budget in USD for the query |
317
+ | `tools` | Tool configuration (array of names or preset) |
318
+ | `allowDangerouslySkipPermissions` | Allow bypassing permissions |
319
+ | `persistSession` | When `false`, disables session persistence to disk (v3.2.0+) |
320
+ | `spawnClaudeCodeProcess` | Custom process spawner for VMs/containers (v3.2.0+) |
321
+ | `permissionMode` | Permission mode: `'default'`, `'acceptEdits'`, `'bypassPermissions'`, `'plan'`, `'dontAsk'`, `'auto'` (`'auto'` and `'dontAsk'` added in SDK 0.3.x; `'delegate'` was removed in SDK 0.3.x and the CLI rejects it, so the provider rejects it at validation time) |
322
+ | `sessionId` | Use a specific session ID for deterministic tracking and correlation (v3.4.0+). Must be a valid UUID; cannot be combined with `continue`/`resume` unless `forkSession` is also set |
323
+ | `debug` | Enable programmatic debug logging from the SDK (v3.4.0+) |
324
+ | `debugFile` | Path to a file for SDK debug log output (v3.4.0+) |
325
+ | `effort` | Effort level: `'low'`, `'medium'`, `'high'`, `'xhigh'`, or `'max'` |
326
+ | `thinking` | Thinking config: `{ type: 'adaptive' }`, `{ type: 'enabled', budgetTokens?: number }`, or `{ type: 'disabled' }` |
327
+ | `promptSuggestions` | Enable prompt suggestions (`boolean`) |
328
+ | `skills` | Enable skills for the session: `'all'` or an array of skill names (v3.5.0+) |
329
+ | `settings` | Inline `Settings` object or path to a settings JSON file (v3.5.0+) |
330
+ | `managedSettings` | Restrictive policy-tier settings enforced on the subprocess (v3.5.0+) |
331
+ | `toolAliases` | Map built-in tool names to replacement tools, e.g. `{ Bash: 'mcp__workspace__bash' }` (v3.5.0+) |
332
+ | `toolConfig` | Per-tool configuration for built-in tools, e.g. `{ askUserQuestion: { previewFormat: 'html' } }` (v3.5.0+) |
333
+ | `planModeInstructions` | Custom workflow instructions for plan mode (v3.5.0+) |
334
+ | `title` | Custom title for a new session (v3.5.0+) |
335
+ | `forwardSubagentText` | Forward subagent text/thinking blocks for nested transcripts (v3.5.0+) |
336
+ | `agentProgressSummaries` | Periodic AI-generated progress summaries for running subagents (v3.5.0+) |
337
+ | `includeHookEvents` | Include hook lifecycle events in the output stream (v3.5.0+) |
338
+ | `fallbackModel` | Fallback model(s) if the primary is overloaded — accepts a comma-separated list to try in order. Must differ from the main model |
339
+ | `onUserDialog` | Callback rendering blocking CLI dialogs (`request_user_dialog`); see **User dialogs** below |
340
+ | `supportedDialogKinds` | Dialog kinds your `onUserDialog` can render; required for dialogs to be emitted at all |
341
+ | `onSdkMessage` | Raw callback for every observed Agent SDK `SDKMessage`; useful for future SDK message types and custom telemetry |
342
+ | `onTaskEvent` | Callback for task/subagent lifecycle events (`ClaudeCodeTaskEvent`); also accumulated in `providerMetadata['claude-code'].taskEvents` when present |
343
+ | `onHookEvent` | Callback for hook lifecycle events (`ClaudeCodeHookEvent`); set `includeHookEvents: true` when you want the SDK to emit hook started/progress/response messages |
344
+ | `onMcpStatusChange` | Callback for the initial MCP status snapshot observed from the SDK init message (`ClaudeCodeMcpStatusEvent`); the same request-time snapshot is surfaced in `providerMetadata['claude-code'].mcpServers` when available. For live status after runtime MCP changes, use `controller.mcpServerStatus()` while the query is live and SDK streaming input/output is active |
345
+ | `onElicitation` | First-class Agent SDK `OnElicitation` callback for MCP elicitation requests (form fields, URL auth, or other server-requested input) |
346
+ | `agent` | Select a named Claude Code agent persona for the main thread. Use intentionally: a configured agent can carry its own prompt/tool/model policy, separate from the AI SDK call's prompt/model surface |
347
+ | `onQueryControllerCreated` | Callback receiving a `ClaudeCodeQueryController` for safe live-query controls. Control-protocol methods require a live SDK `Query`, and most require SDK streaming input/output; `controller.rawQuery` remains available when you need the raw SDK `Query` |
293
348
 
294
349
  **System prompt** (`systemPrompt`) accepts a string, a string array, or the Claude Code preset object (v3.5.0+ for the array form). In the array form, include the re-exported `SYSTEM_PROMPT_DYNAMIC_BOUNDARY` marker as a standalone element to split the static (cross-session cacheable) prefix from the dynamic suffix. The preset object additionally accepts `excludeDynamicSections: true` to strip per-user dynamic sections (working directory, git status) so the prompt caches across users.
295
350
 
@@ -301,6 +356,8 @@ This provider exposes Agent SDK options directly. Key options include:
301
356
 
302
357
  **Alpha options** (v3.5.0+, marked `@alpha` upstream and subject to change): `taskBudget` (`{ total: number }` API-side token budget), `sessionStore` (mirror session transcripts to a custom storage adapter; the provider rejects combining it with `persistSession: false` or `enableFileCheckpointing: true`, and `continue: true` without a `resume` ID requires the store to implement `listSessions()`), `sessionStoreFlush` (`'batched'` or `'eager'`), and `loadTimeoutMs` (resume-load timeout). The SDK's `InMemorySessionStore` reference implementation and the `SessionStore`/`SessionStoreFlush` types are re-exported.
303
358
 
359
+ The package also re-exports SDK helper functions/types used by advanced hosts: `resolveSettings`, `filterEscalatingDefaultMode`, `SandboxCredentialsConfig`, and `SDKFilesPersistedEvent` in addition to the session, hook, MCP, permission, and warm-start exports documented below.
360
+
304
361
  ### User dialogs (`onUserDialog` / `supportedDialogKinds`)
305
362
 
306
363
  Some CLI flows ask the host to render a blocking dialog (a `request_user_dialog` control request) — for example `'refusal_fallback_prompt'`, which asks whether to retry a refused request differently. The SDK **fails closed** here: a dialog kind not declared in `supportedDialogKinds` is never emitted, and the flow behind it degrades to its no-dialog behavior (for `'refusal_fallback_prompt'`, the classic refusal error ends the turn). Providing `onUserDialog` alone does NOT opt you in — both options are required, and passing a non-empty `supportedDialogKinds` without the callback throws at SDK option intake, so the provider **rejects** that combination at validation time (`createClaudeCode`/model construction throws `Invalid settings`).
@@ -323,9 +380,11 @@ const model = claudeCode('sonnet', {
323
380
 
324
381
  The `OnUserDialog`, `UserDialogRequest`, and `UserDialogResult` types are re-exported. Note that `UserDialogResult.result` is typed `unknown` — the CLI validates it against the dialog kind's own result schema at runtime, and a result that doesn't match (e.g. the wrong shape or an unknown string) is **silently** replaced by the dialog's default (for `'refusal_fallback_prompt'`, `'cancelled'`), so double-check the result values for each kind you handle.
325
382
 
326
- ### Permission decisions (`canUseTool` extras)
383
+ ### Permission decisions (`canUseTool`, `permissionMode`, and AI SDK `toolApproval`)
327
384
 
328
- SDK 0.3.x enriched the `canUseTool` callback (no provider change needed — these arrive on the existing `options` argument):
385
+ SDK 0.3.x enriched the Claude Agent SDK `canUseTool` callback (no provider change needed — these arrive on the existing `options` argument):
386
+
387
+ AI SDK v7 `toolApproval` is **not** bridged to Claude Code's internal tool permission system. Use Claude Agent SDK `canUseTool` / `permissionMode` for Claude Code built-in and MCP tools; call-level `toolApproval` on `generateText`/`streamText` applies to app-level AI SDK tools handled by the `ai` package.
329
388
 
330
389
  - `title` — full permission prompt sentence (e.g. "Claude wants to read foo.txt"); prefer it over reconstructing from `toolName` + input
331
390
  - `displayName` — short noun phrase for the tool action (e.g. "Read file"), suitable for button labels
@@ -352,7 +411,39 @@ const model = claudeCode('sonnet', {
352
411
  > **Upstream CLI caveats (verified on CLI 2.1.172):**
353
412
  >
354
413
  > - A `PreToolUse` hook returning `permissionDecision: 'defer'` combined with a `canUseTool` callback fails the tool call **before** `canUseTool` is ever consulted. When `canUseTool` should handle the call, have the hook return no decision (or `'allow'`) instead of `'defer'`.
355
- > - The `PermissionDenied` hook only fires for CLI-internal auto-mode classifier denials (e.g. `permissionMode: 'auto'`). Denials issued by `canUseTool` do **not** trigger it — they surface via the result message's `permission_denials`, which the provider merges into `providerMetadata['claude-code'].permissionDenials`.
414
+ > - The `PermissionDenied` hook only fires for CLI-internal auto-mode classifier denials (e.g. `permissionMode: 'auto'`). Denials issued by `canUseTool` do **not** trigger it — they surface via the result message's `permission_denials`, which the provider merges into `finalStep.providerMetadata['claude-code'].permissionDenials`.
415
+
416
+ ### Agent SDK event callbacks (`onSdkMessage`, task/hook/MCP status, elicitation)
417
+
418
+ Use these callbacks when you need Agent SDK observability without parsing AI SDK stream parts yourself:
419
+
420
+ ```ts
421
+ const model = claudeCode('sonnet', {
422
+ onSdkMessage: (message) => {
423
+ console.debug('raw SDK message:', message.type);
424
+ },
425
+ onTaskEvent: (event) => {
426
+ console.debug('task event:', event.subtype);
427
+ },
428
+ includeHookEvents: true,
429
+ onHookEvent: (event) => {
430
+ console.debug('hook event:', event.subtype);
431
+ },
432
+ onMcpStatusChange: (status) => {
433
+ console.debug('MCP init status snapshot:', status);
434
+ },
435
+ onElicitation: async (request) => {
436
+ return renderMcpElicitation(request);
437
+ },
438
+ });
439
+ ```
440
+
441
+ - `onSdkMessage` receives the raw `SDKMessage` objects the provider observes. Prefer this when you want to preserve new or rare SDK messages without waiting for provider-specific metadata.
442
+ - `onTaskEvent` receives normalized task/subagent events and the same objects are collected into `providerMetadata['claude-code'].taskEvents` when any fire during the request.
443
+ - `onHookEvent` receives normalized hook lifecycle events; because the underlying SDK only emits hook lifecycle messages when requested, pair it with `includeHookEvents: true`.
444
+ - `onMcpStatusChange` receives the initial MCP server status snapshot observed during the request; final metadata includes that same request-time `mcpServers` snapshot when available. For live status after `reconnectMcpServer()`, `toggleMcpServer()`, or `setMcpServers()`, call `controller.mcpServerStatus()` while the query is live and SDK streaming input/output is active.
445
+ - `onElicitation` is the first-class Agent SDK `OnElicitation` hook. It is for MCP-server-requested input; if you do not provide it, unhandled elicitation requests follow the SDK's default decline/cancel behavior.
446
+ - `agent` selects a named Claude Code agent for the main thread. This is different from `agents` (which defines subagents). Use it only when you want the named agent's configured prompt/tools/model behavior to participate in the main request.
356
447
 
357
448
  See [`ClaudeCodeSettings`](https://github.com/ben-vargas/ai-sdk-provider-claude-code/blob/main/src/types.ts) for the full list of supported options (e.g., `allowedTools`, `disallowedTools`, `hooks`, `canUseTool`, `env`, `settingSources`).
358
449
 
@@ -373,22 +464,17 @@ const model = claudeCode('sonnet', {
373
464
  });
374
465
  ```
375
466
 
376
- ### Not exposed (and why)
377
-
378
- A few Agent SDK surfaces are deliberately not wrapped by this provider. A compile-time drift guard (`src/options-coverage.test.ts`) keeps this list exhaustive: every SDK `Options` field is either mapped, provider-managed, or consciously listed below.
467
+ ### SDK boundaries
379
468
 
380
- **`Options` fields without a `ClaudeCodeSettings` equivalent** (both still reachable via `sdkOptions`):
469
+ **Provider-managed fields** are set internally and ignored if passed via `sdkOptions`: `model`, `abortController`, `prompt`, and `outputFormat`.
381
470
 
382
- - `agent` selects a named agent persona for the _main_ thread, overriding the conversation's system prompt, tools, and model. That conflicts with the AI SDK contract, where the model id and system prompt come from the AI SDK call itself. (Defining subagents via `agents` is fully supported.)
383
- - `onElicitation` — an interactive host-UI callback for MCP elicitation requests (form fields, URL auth). Headless AI SDK usage has no dialog surface, and the SDK safely auto-declines unhandled requests.
384
-
385
- **Provider-managed fields** that are set internally and ignored if passed via `sdkOptions`: `model`, `abortController`, `prompt`, and `outputFormat`.
471
+ `agent` and `onElicitation` are first-class `ClaudeCodeSettings` fields on the 4.x line. Older 3.x docs described them as `sdkOptions` escape hatches; use the direct settings names in new code.
386
472
 
387
473
  **Alternate SDK entry points** — the Agent SDK also ships `/browser` (WebSocket browser transport), `/bridge` (remote-control session transport), and `/assistant` (worker/daemon harness) entry points. These are alpha surfaces with their own versioning cadence and are aimed at embedding hosts rather than AI SDK consumers, so this provider does not re-export them. Import them directly from `@anthropic-ai/claude-agent-sdk/<entry>` if you need them, with the usual alpha-stability caveats.
388
474
 
389
475
  ## Claude Agent SDK 0.3.x Notes
390
476
 
391
- This provider depends on `@anthropic-ai/claude-agent-sdk@^0.3.205`. The 0.3.x line introduces a few changes worth knowing about:
477
+ This provider depends on `@anthropic-ai/claude-agent-sdk@0.3.205` (exact pin). The pin is exact rather than a caret because upstream releases have shipped broken `sdk.d.ts` declarations before (0.3.198–0.3.202 collapsed `SDKMessage` to `any`, fixed in 0.3.203); the weekly canary gates each pin move. The 0.3.x line introduces a few changes worth knowing about:
392
478
 
393
479
  ### New peer dependencies
394
480
 
@@ -450,7 +536,7 @@ for await (const chunk of result.textStream) {
450
536
 
451
537
  **Requirements:**
452
538
 
453
- - `streamingInput: 'always'` or `'auto'` with `canUseTool` set
539
+ - `streamingInput: 'always'` or `'auto'` with `canUseTool` set; image parts also auto-enable SDK streaming input under `'auto'`
454
540
  - Messages injected via `inject(content)` are delivered to the agent mid-turn
455
541
 
456
542
  **Important:** Injection works between tool calls, not during continuous text generation. Use tasks that involve tool usage (file operations, bash commands, etc.) for effective mid-turn interruption.
@@ -480,14 +566,14 @@ injector.inject('STOP!', (delivered) => {
480
566
 
481
567
  See [examples/message-injection.ts](examples/message-injection.ts) for complete examples including conditional injection and supervisor approval patterns.
482
568
 
483
- ## Image Inputs (Streaming Only)
569
+ ## Image Inputs
484
570
 
485
- - Enable streaming input (`streamingInput: 'always'` or provide `canUseTool`) before sending images.
486
- - Supported payloads: data URLs (`data:image/png;base64,...`), strings prefixed with `base64:<mediaType>,<data>`, or objects `{ data: '<base64>', mimeType: 'image/png' }`.
571
+ - With `streamingInput: 'auto'` (the default), supported image prompts automatically enable the Agent SDK streaming-input path for that request; you no longer need to set `streamingInput: 'always'` just to send images.
572
+ - Supported payloads include data URLs (`data:image/png;base64,...`), strings prefixed with `base64:<mediaType>,<data>`, or AI SDK v7 file parts such as `{ type: 'file', data: { type: 'data', data: '<base64>' }, mediaType: 'image/png' }` (use `mediaType`, not `mimeType`).
487
573
  - Remote HTTP(S) image URLs are ignored with the warning "Image URLs are not supported by this provider; supply base64/data URLs." (`supportsImageUrls` remains `false`).
488
- - When streaming input is disabled, image parts trigger the streaming prerequisite warning and are omitted from the request.
574
+ - `streamingInput: 'off'` remains an explicit opt-out: image prompts skip the streaming-input path, image parts are omitted, and the provider emits a generic `type: 'other'` image streaming-input warning.
489
575
  - Use realistic image payloads—very small placeholders may result in the model asking for a different image.
490
- - `examples/images.ts` accepts a local image path and converts it to a data URL on the fly: `npx tsx examples/images.ts /absolute/path/to/image.png`.
576
+ - `examples/images.ts` accepts a local image path, reads its bytes, and builds an AI SDK v7 file part: `npx tsx examples/images.ts /absolute/path/to/image.png`.
491
577
 
492
578
  ## Skills Support
493
579
 
@@ -536,7 +622,7 @@ See [examples/skills-management.ts](examples/skills-management.ts) for more exam
536
622
 
537
623
  ## Using AI SDK Tools
538
624
 
539
- The Claude Code CLI executes its own tools, so AI SDK tools passed to `generateText`/`streamText` via the `tools` option are ignored (with an `unsupported` warning). Automatic bridging is impossible by design: at the `LanguageModelV3` layer the provider only receives tool _declarations_ (name, description, JSON schema) — the `execute` functions live in the `ai` package layer and never reach any provider.
625
+ The Claude Code CLI executes its own tools, so AI SDK tools passed to `generateText`/`streamText` via the `tools` option are ignored (with an `unsupported` warning). Automatic bridging is impossible by design: at the `LanguageModelV4` layer the provider only receives tool _declarations_ (name, description, JSON schema) — the `execute` functions live in the `ai` package layer and never reach any provider.
540
626
 
541
627
  Instead, bridge your tools explicitly with the `createAiSdkMcpServer` helper, which turns a map of AI SDK tools into an in-process MCP server that the CLI can call:
542
628
 
@@ -567,6 +653,7 @@ Notes:
567
653
 
568
654
  - Each tool's `execute` runs in your process; string results pass through as MCP text content, everything else is `JSON.stringify`'d, and thrown errors become `isError` tool results instead of crashing the CLI session. Results that cannot be serialized to JSON (e.g. circular objects) also become `isError` results with a serialization message.
569
655
  - Tool calls/results surface to the AI SDK as **provider-executed dynamic tool parts** (`tool-call`/`tool-result` with `mcp__<serverName>__<toolName>` names), not as executions of your local `tools` option.
656
+ - AI SDK v7 `toolApproval` is call-level approval for app-level AI SDK tools. Once a tool is exposed to Claude Code as MCP, approve or deny it with Claude Agent SDK controls (`canUseTool`, `permissionMode`, `allowedTools`, `disallowedTools`).
570
657
  - Only **Zod object schemas** are supported (`z.object({...})`, the same schema you pass to the AI SDK `tool()` helper). Tools defined with the AI SDK's `jsonSchema()` helper are rejected at creation time because the Agent SDK's `tool()` requires a Zod shape.
571
658
  - **Validation scope:** the Agent SDK's `tool()` takes only the schema _shape_ and validates incoming args field-by-field (running field-level validation and transforms, and stripping unknown keys) before `execute` runs. Object-level constructs — `.refine()`/`.superRefine()` (cross-field invariants) and `.strict()`/`.passthrough()`/`.catchall()` (unknown-key modes) — are **not** enforced by the bridge: re-parsing on top of the SDK's output would re-run transforms and reject valid transform schemas (e.g. `z.string().transform(v => v.length)`). Perform cross-field and unknown-key checks inside `execute`.
572
659
  - Tools without an `execute` function (client-executed tools) are rejected at creation time.
@@ -576,7 +663,7 @@ See [examples/ai-sdk-tools.ts](examples/ai-sdk-tools.ts) for a runnable example
576
663
 
577
664
  ## Session Management
578
665
 
579
- Every request runs as a Claude Code session, persisted under `~/.claude/projects/` by default and identified by `providerMetadata['claude-code'].sessionId`. Sessions can be resumed (`resume`), forked (`forkSession`), pinned to a deterministic ID (`sessionId`), titled (`title`), or kept ephemeral (`persistSession: false`). The provider also re-exports the SDK's session lifecycle helpers — `listSessions()`, `getSessionMessages()`, `forkSession()`, `getSessionInfo()`, `renameSession()`, `tagSession()`, `deleteSession()`, `listSubagents()`, `getSubagentMessages()`, `importSessionToStore()`, and `foldSessionSummary()` — for managing stored sessions outside of a query.
666
+ Every request runs as a Claude Code session, persisted under `~/.claude/projects/` by default. In AI SDK v7, read the session ID from `finalStep.providerMetadata['claude-code'].sessionId` (`result.finalStep` for `generateText`, or `await stream.finalStep` for `streamText`). Sessions can be resumed (`resume`), forked (`forkSession`), pinned to a deterministic ID (`sessionId`), titled (`title`), or kept ephemeral (`persistSession: false`). The provider also re-exports the SDK's session lifecycle helpers — `listSessions()`, `getSessionMessages()`, `forkSession()`, `getSessionInfo()`, `renameSession()`, `tagSession()`, `deleteSession()`, `listSubagents()`, `getSubagentMessages()`, `importSessionToStore()`, and `foldSessionSummary()` — for managing stored sessions outside of a query.
580
667
 
581
668
  See [docs/sessions.md](docs/sessions.md) for the full guide (settings vs helpers, disk storage vs custom `SessionStore`, `title` vs `renameSession()`), and [examples/session-management.ts](examples/session-management.ts) for a runnable walkthrough (`npm run example:sessions`).
582
669
 
@@ -605,38 +692,42 @@ for await (const message of warm.query('Summarize the latest deploy log.')) {
605
692
  // await using warm = ... // WarmQuery is AsyncDisposable
606
693
  ```
607
694
 
608
- All requests made through this provider report timing in `providerMetadata['claude-code']` (`ttftMs`, `ttftStreamMs`, `timeToRequestMs`), plus `warmSpareClaimed` when the SDK reports whether the query was served from a pre-warmed spare process (surfaced as `true` or `false` whenever reported) — use these to measure whether warm-start plumbing is worth it for your workload.
695
+ All requests made through this provider report timing in `finalStep.providerMetadata['claude-code']` (`ttftMs`, `ttftStreamMs`, `timeToRequestMs`), plus `warmSpareClaimed` when the SDK reports whether the query was served from a pre-warmed spare process (surfaced as `true` or `false` whenever reported; use `await stream.finalStep` after `streamText`) — use these to measure whether warm-start plumbing is worth it for your workload.
609
696
 
610
697
  ## Limitations
611
698
 
612
- - Requires Node.js ≥ 18
613
- - Image inputs require streaming mode with base64/data URLs (remote fetch is not supported)
699
+ - Requires Node.js ≥ 22
700
+ - With `streamingInput: 'auto'`, image inputs auto-enable streaming input for supported base64/data/file-part payloads, but remote image URLs are still not fetched by this provider
701
+ - `ProviderV4.files()` / FilesV4 upload is not implemented: inline file/image data is supported where Claude Code can represent it, but the provider does not create durable AI SDK provider-reference uploads
702
+ - `ProviderV4.skills()` / SkillsV4 upload is not implemented: Claude Code skills still come from configured user/project/local skill directories via the `skills` / `settingSources` options
703
+ - Workflow serialization remains deferred; callback/function settings such as `canUseTool`, hooks, `onSdkMessage`, `onElicitation`, `logger`, `spawnClaudeCodeProcess`, and `SessionStore` methods must be reconstructed by the application
614
704
  - Some AI SDK parameters are unsupported and ignored with an `unsupported` warning: `temperature`, `topP`, `topK`, `presencePenalty`, `frequencyPenalty`, `stopSequences`, `seed`, and `maxOutputTokens` (the CLI does not accept an output token cap)
615
- - AI SDK `tools` and `toolChoice` (other than `'auto'`) are not supported: the Claude Code CLI executes its own tools, so AI SDK tool definitions cannot be auto-bridged at the provider layer (both emit an `unsupported` warning). To expose custom tools to the CLI, bridge them with the `createAiSdkMcpServer` helper and pass the result via the `mcpServers` setting (plus `allowedTools`) see **Using AI SDK Tools** above
705
+ - AI SDK `tools`, `toolChoice` (other than `'auto'`), and call-level `toolApproval` do not approve Claude Code internal built-in/MCP tools. To expose custom tools to the CLI, bridge them with the `createAiSdkMcpServer` helper and pass the result via the `mcpServers` setting (plus `allowedTools`); approve or deny Claude Code tools with `canUseTool`, `permissionMode`, `allowedTools`, and `disallowedTools`
616
706
  - When replaying conversation history through the prompt, assistant tool calls are serialized as text lines — `[Tool call: Read({"file_path":"/x"})]` (inputs truncated at 1000 characters) — paired with `Tool Result (Read): ...` lines for tool messages
617
- - `canUseTool` requires streaming input at the SDK level (AsyncIterable prompt). This provider supports it via `streamingInput`: use `'auto'` (default when `canUseTool` is set) or `'always'`. See GUIDE for details.
707
+ - `canUseTool` requires streaming input at the SDK level (AsyncIterable prompt), and image prompts use the same path. This provider supports it via `streamingInput`: use `'auto'` (streams when `canUseTool` is set or image parts are present), `'always'`, or `'off'` (`'off'` disables streaming image input, emits a generic `type: 'other'` image streaming-input warning, and omits image parts). See GUIDE for details.
618
708
 
619
709
  ## Error Diagnostics
620
710
 
621
- Mapped provider errors append a trimmed stderr tail directly to the message as `... | stderr (tail): <last lines>`, making failures visible in logs without inspecting metadata. The full captured stderr (capped at 4000 characters) remains available through `getErrorMetadata`:
711
+ Mapped errors from this provider append a trimmed stderr tail to the error message as `... | stderr (tail): <last lines>`, making CLI failures visible in logs without inspecting metadata.
622
712
 
623
- ```typescript
713
+ ```ts
624
714
  import { generateText } from 'ai';
625
715
  import { claudeCode, getErrorMetadata } from 'ai-sdk-provider-claude-code';
716
+
626
717
  try {
627
- await generateText({ model: claudeCode('haiku'), prompt: 'Hello, Claude!' });
718
+ await generateText({ model: claudeCode('sonnet'), prompt: 'Hello!' });
628
719
  } catch (error) {
629
720
  console.error(getErrorMetadata(error)?.stderr);
630
721
  }
631
722
  ```
632
723
 
633
- Authentication and timeout failures are also classified when the only evidence is in stderr, using high-precision matching to avoid false positives from unrelated stderr output.
724
+ The metadata accessor returns the full captured stderr, capped at 4000 characters. Authentication and timeout failures are also classified when the only evidence is in stderr, using high-precision matching to avoid false positives from unrelated stderr output.
634
725
 
635
726
  ## Tool Error Parity (Streaming)
636
727
 
637
- - In addition to `tool-call` and `tool-result`, this provider emits a distinct `tool-error` stream event when a tool execution fails.
638
- - For parity with other tool events, `tool-error` includes `providerExecuted: true` and `providerMetadata['claude-code']` (e.g., `rawError`). These fields are documented extensions; downstream consumers may safely ignore them if unused.
639
- - See Tool Streaming Support for full event list, ordering guarantees, and performance considerations.
728
+ - AI SDK v7 represents failed tool executions with the standard `tool-result` stream event/part and `isError: true`.
729
+ - The former provider-specific `tool-error` stream extension is not emitted on the 4.x line; inspect `providerMetadata['claude-code']` (e.g., `rawError`) on the `tool-result` when you need Claude Code details.
730
+ - See **Content Block Streaming** below for the current streaming event overview; the Tool Streaming Support doc is historical v5-era reference material.
640
731
 
641
732
  ## Content Block Streaming
642
733
 
@@ -653,7 +744,7 @@ For subagent parent/child tracking, see **Subagent Hierarchy Tracking** in this
653
744
  When Claude Code spawns subagents via the `Task` tool, this provider exposes parent-child relationships through `providerMetadata`:
654
745
 
655
746
  ```ts
656
- // Available on tool-input-start, tool-call, tool-result, and tool-error events
747
+ // Available on tool-input-start, tool-call, and tool-result events
657
748
  providerMetadata['claude-code'].parentToolCallId: string | null;
658
749
  ```
659
750
 
@@ -665,35 +756,38 @@ This enables UIs to build hierarchical views of nested agent execution.
665
756
 
666
757
  ## Provider Metadata
667
758
 
668
- Each response exposes Claude Code metadata under `providerMetadata['claude-code']` (on the `doGenerate` result, and on the `finish` stream event for `doStream`):
669
-
670
- | Field | Type | Description |
671
- | ------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
672
- | `sessionId` | `string` | Session ID for multi-turn conversations |
673
- | `costUsd` | `number` | Cost of the request in USD |
674
- | `durationMs` | `number` | Total request duration in milliseconds |
675
- | `modelUsage` | `object` | Per-model token usage breakdown |
676
- | `ttftMs` | `number` | Time to first token in milliseconds (when reported by the SDK) |
677
- | `ttftStreamMs` | `number` | Time to first streamed token in milliseconds (when reported) |
678
- | `timeToRequestMs` | `number` | Time until the API request was issued in milliseconds (when reported) |
679
- | `warmSpareClaimed` | `boolean` | Whether the query was served from a pre-warmed spare CLI process (when reported); see **Reducing time-to-first-token (warm start)** |
680
- | `terminalReason` | `string` | Why the turn loop terminated (SDK `TerminalReason`, e.g. `'completed'`, `'max_turns'`; re-exported type) |
681
- | `apiRetries` | `number` | Number of API retry attempts observed during the request (only present when > 0) |
682
- | `permissionDenials` | `array` | Denied tool calls: `{ toolName, toolUseId?, reason? }` (only present when non-empty). Stream-time auto-denials are warn-logged; PreToolUse-hook denials are merged from the result message |
683
- | `mirrorErrors` | `array` | SessionStore transcript-mirror append failures: `{ error, sessionId }` (only present when non-empty). Each is a transcript batch the SDK DROPPED after retries — also warn-logged — so `sessionStore` consumers can detect a silently-incomplete mirror |
684
- | `estimatedThinkingTokens` | `number` | Accumulated live thinking-token estimate from the redacted-thinking phase (only present when > 0); approximate, not the authoritative billed output tokens |
685
- | `truncated` | `true` | Present when the response was recovered from a truncated SDK stream |
686
- | `thinkingTraces` | `array` | Thinking blocks extracted in non-streaming mode (`doGenerate` only) |
759
+ Each response exposes Claude Code metadata under the final step's `providerMetadata['claude-code']` (AI SDK v7: `result.finalStep.providerMetadata`, or `await stream.finalStep` for `streamText`; internally this comes from the provider `doGenerate` result or `finish` stream event):
760
+
761
+ | Field | Type | Description |
762
+ | ------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
763
+ | `sessionId` | `string` | Session ID for multi-turn conversations |
764
+ | `costUsd` | `number` | Cost of the request in USD |
765
+ | `durationMs` | `number` | Total request duration in milliseconds |
766
+ | `modelUsage` | `object` | Per-model token usage breakdown |
767
+ | `ttftMs` | `number` | Time to first token in milliseconds (when reported by the SDK) |
768
+ | `ttftStreamMs` | `number` | Time to first streamed token in milliseconds (when reported) |
769
+ | `timeToRequestMs` | `number` | Time until the API request was issued in milliseconds (when reported) |
770
+ | `warmSpareClaimed` | `boolean` | Whether the query was served from a pre-warmed spare CLI process (when reported); see **Reducing time-to-first-token (warm start)** |
771
+ | `terminalReason` | `string` | Why the turn loop terminated (SDK `TerminalReason`, e.g. `'completed'`, `'max_turns'`; re-exported type) |
772
+ | `apiRetries` | `number` | Number of API retry attempts observed during the request (only present when > 0) |
773
+ | `permissionDenials` | `array` | Denied tool calls: `{ toolName, toolUseId?, reason?, agentId?, decisionReasonType?, raw? }` when available. Stream-time auto-denials are warn-logged; PreToolUse-hook denials are merged from the result message |
774
+ | `taskEvents` | `array` | `ClaudeCodeTaskEvent[]` task/subagent lifecycle events observed during the request (only present when non-empty) |
775
+ | `hookEvents` | `array` | `ClaudeCodeHookEvent[]` hook lifecycle events observed during the request (only present when non-empty; requires SDK hook event emission, e.g. `includeHookEvents: true`) |
776
+ | `mcpServers` | `array` | Initial MCP server status snapshot observed from the SDK init message during the request, matching the data delivered to `onMcpStatusChange` when available. Use `controller.mcpServerStatus()` for live status after runtime MCP changes only while the query is live and SDK streaming input/output is active |
777
+ | `mirrorErrors` | `array` | SessionStore transcript-mirror append failures: `{ error, sessionId }` (only present when non-empty). Each is a transcript batch the SDK DROPPED after retries — also warn-logged — so `sessionStore` consumers can detect a silently-incomplete mirror |
778
+ | `estimatedThinkingTokens` | `number` | Accumulated live thinking-token estimate from the redacted-thinking phase (only present when > 0); approximate, not the authoritative billed output tokens |
779
+ | `truncated` | `true` | Present when the response was recovered from a truncated SDK stream |
780
+ | `thinkingTraces` | `array` | Thinking blocks extracted in non-streaming mode (`doGenerate` only) |
687
781
 
688
782
  ```ts
689
- const { providerMetadata } = await generateText({ model, prompt: 'Hello' });
690
- const meta = providerMetadata?.['claude-code'];
783
+ const { finalStep } = await generateText({ model, prompt: 'Hello' });
784
+ const meta = finalStep.providerMetadata?.['claude-code'];
691
785
  console.log(meta?.costUsd, meta?.ttftMs, meta?.terminalReason);
692
786
  ```
693
787
 
694
788
  ### Prompt suggestions (`onPromptSuggestion`)
695
789
 
696
- When `promptSuggestions` is `true` or left unset (the SDK enables suggestions when the option is absent or true, disabling them only when explicitly `false`), the agent predicts the next user prompt after each turn. Delivery is subject to CLI heuristics (suppressed on the first turn, after API errors, in plan mode, or via `CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION=false`), so it may not fire on every turn. The SDK delivers it AFTER the `result` message — i.e. after the AI SDK response has already finished — so it cannot be part of `providerMetadata`. Register a callback instead. (In streaming mode the provider briefly drains post-result messages to deliver the suggestion; the drain stops after the first suggestion and is capped at 10 seconds so a lingering CLI process is never held open indefinitely.)
790
+ Set `promptSuggestions: true` to receive predicted next prompts. When the option is unset or `false`, the CLI does not emit `prompt_suggestion` messages and `onPromptSuggestion` never fires. Delivery is still subject to CLI heuristics (suppressed on the first turn, after API errors, in plan mode, or via `CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION=false`), so it may not fire on every enabled turn. The SDK delivers the suggestion AFTER the `result` message — i.e. after the AI SDK response has already finished — so it cannot be part of `providerMetadata`. Register a callback instead. (In streaming mode the provider briefly drains post-result messages to deliver the suggestion; the drain stops after the first suggestion and is capped at 10 seconds so a lingering CLI process is never held open indefinitely.)
697
791
 
698
792
  ```ts
699
793
  const model = claudeCode('sonnet', {
@@ -704,25 +798,31 @@ const model = claudeCode('sonnet', {
704
798
  });
705
799
  ```
706
800
 
707
- ### Context usage (`query.getContextUsage()`)
801
+ ### Query controller (`onQueryControllerCreated` / `createClaudeCodeQueryController`)
802
+
803
+ `onQueryCreated` still exposes the raw Agent SDK `Query` for advanced consumers. The provider also offers a safer controller surface for the wrapped live-query operations listed below and exports the same wrapper as `createClaudeCodeQueryController(query)`. The controller's `rawQuery` property remains available when you intentionally need the underlying SDK object.
804
+
805
+ `onQueryControllerCreated` only applies to the live SDK `Query` created for that request. Controller methods that send Agent SDK control requests require that query to still be running, and most control requests are supported by the SDK only when streaming input/output is active. In this provider, set `streamingInput: 'always'` for controller-control recipes (or use `'auto'` only when another feature already triggers streaming input, such as `canUseTool` or image parts).
708
806
 
709
- The provider does not auto-fetch context usage (it would add a round-trip per request). `getContextUsage()` is a control-protocol round-trip to the CLI subprocess, so it **must be called while the query is still live** — by the time `generateText`/`streamText` resolves, the subprocess has exited and the call rejects with `ProcessTransport is not ready for writing`. Capture the `Query` object via the existing `onQueryCreated` callback and ask for it from a hook that fires during the turn (a `Stop` hook runs at the end of the turn while the process is still alive):
807
+ Use `onQueryControllerCreated` when you want to call wrapped controls—`interrupt()`, `setPermissionMode()`, `setMcpPermissionModeOverride()`, `setModel()`, `setMaxThinkingTokens()`, `applyFlagSettings()`, `mcpServerStatus()`, `reconnectMcpServer()`, `toggleMcpServer()`, `setMcpServers()`, `getContextUsage()`, `rewindFiles()`, `stopTask()`, `backgroundTasks()`, and optional `streamInput()`—without passing the entire raw query through your UI layer:
710
808
 
711
809
  ```ts
712
- import type { Query } from 'ai-sdk-provider-claude-code';
810
+ import type { ClaudeCodeQueryController } from 'ai-sdk-provider-claude-code';
713
811
 
714
- let activeQuery: Query | undefined;
812
+ let activeController: ClaudeCodeQueryController | undefined;
715
813
  let contextUsage: unknown;
814
+
716
815
  const model = claudeCode('sonnet', {
717
- onQueryCreated: (query) => {
718
- activeQuery = query;
816
+ streamingInput: 'always', // Required for Agent SDK control requests.
817
+ onQueryControllerCreated: (controller) => {
818
+ activeController = controller;
719
819
  },
720
820
  hooks: {
721
821
  Stop: [
722
822
  {
723
823
  hooks: [
724
824
  async () => {
725
- contextUsage = await activeQuery?.getContextUsage();
825
+ contextUsage = await activeController?.getContextUsage();
726
826
  return { continue: true };
727
827
  },
728
828
  ],
@@ -735,6 +835,10 @@ const result = await generateText({ model, prompt: 'Hello' });
735
835
  console.log(contextUsage); // tokens used / remaining in the session context window
736
836
  ```
737
837
 
838
+ The controller does not extend the SDK query lifetime. Controller calls that talk to the CLI subprocess must happen before `generateText`/`streamText` resolves; after the subprocess exits, the underlying SDK call rejects with `ProcessTransport is not ready for writing`.
839
+
840
+ For MCP, `onMcpStatusChange` and `providerMetadata['claude-code'].mcpServers` record the request's initial SDK init snapshot; after `reconnectMcpServer()`, `toggleMcpServer()`, or `setMcpServers()`, call `controller.mcpServerStatus()` only while the query is live and SDK streaming input/output is active.
841
+
738
842
  ## Contributing
739
843
 
740
844
  We welcome contributions, especially: