ai-sdk-provider-claude-code 3.4.3 → 3.5.0

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
@@ -7,7 +7,7 @@
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
- # AI SDK Provider for Claude Code SDK
10
+ # AI SDK Provider for Claude Agent SDK
11
11
 
12
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.
13
13
 
@@ -22,27 +22,7 @@
22
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
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) |
24
24
 
25
- ### Installing the Right Version
26
-
27
- **For AI SDK v6 (recommended):**
28
-
29
- ```bash
30
- npm install ai-sdk-provider-claude-code ai@^6.0.0
31
- # or explicitly: npm install ai-sdk-provider-claude-code@latest
32
- ```
33
-
34
- **For AI SDK v5:**
35
-
36
- ```bash
37
- npm install ai-sdk-provider-claude-code@ai-sdk-v5 ai@^5.0.0
38
- ```
39
-
40
- **For AI SDK v4 (legacy):**
41
-
42
- ```bash
43
- npm install ai-sdk-provider-claude-code@ai-sdk-v4 ai@^4.3.16
44
- # or use specific version: npm install ai-sdk-provider-claude-code@^0.2.2
45
- ```
25
+ Install commands for each line are listed under [Installation](#installation) below.
46
26
 
47
27
  ## Zod Compatibility
48
28
 
@@ -70,19 +50,21 @@ claude auth login
70
50
  ```bash
71
51
  # For AI SDK v6 (recommended)
72
52
  npm install ai-sdk-provider-claude-code ai@^6.0.0
53
+ # or explicitly: npm install ai-sdk-provider-claude-code@latest
73
54
 
74
55
  # For AI SDK v5
75
56
  npm install ai-sdk-provider-claude-code@ai-sdk-v5 ai@^5.0.0
76
57
 
77
58
  # For AI SDK v4 (legacy)
78
59
  npm install ai-sdk-provider-claude-code@ai-sdk-v4 ai@^4.3.16
60
+ # or use a specific version: npm install ai-sdk-provider-claude-code@^0.2.2
79
61
  ```
80
62
 
81
63
  ## Disclaimer
82
64
 
83
65
  **This is an unofficial community provider** and is not affiliated with or endorsed by Anthropic or Vercel. By using this provider:
84
66
 
85
- - You understand that your data will be sent to Anthropic's servers through the Claude Code SDK
67
+ - You understand that your data will be sent to Anthropic's servers through the Claude Agent SDK
86
68
  - You agree to comply with [Anthropic's Terms of Service](https://www.anthropic.com/legal/consumer-terms)
87
69
  - You acknowledge this software is provided "as is" without warranties of any kind
88
70
 
@@ -155,15 +137,18 @@ Key changes:
155
137
  - **`sonnet`** - Claude Sonnet (balanced performance)
156
138
  - **`haiku`** - Claude Haiku (fastest, most cost-effective)
157
139
 
158
- You can also use full model identifiers directly (e.g., `claude-opus-4-5`, `claude-sonnet-4-5-20250514`).
140
+ You can also use full model identifiers directly (e.g., `claude-sonnet-4-6`, `claude-opus-4-8`).
159
141
 
160
142
  ## Documentation
161
143
 
162
- - **[Usage Guide](docs/ai-sdk-v5/GUIDE.md)** - Comprehensive examples and configuration
163
- - **[Breaking Changes](docs/ai-sdk-v5/V5_BREAKING_CHANGES.md)** - v0.x to v1.x migration guide
164
- - **[Troubleshooting](docs/ai-sdk-v5/TROUBLESHOOTING.md)** - Common issues and solutions
144
+ - **[Session Management](docs/sessions.md)** - Creating, resuming, forking, inspecting, and deleting sessions
165
145
  - **[Examples](examples/)** - Sample scripts and patterns
166
- - **[Tool Streaming Support](docs/ai-sdk-v5/TOOL_STREAMING_SUPPORT.md)** - Event semantics and performance notes
146
+ - **[Usage Guide](docs/ai-sdk-v5/GUIDE.md)** - Comprehensive examples and configuration (written for provider 2.x / AI SDK v5; most patterns still apply)
147
+ - **[Troubleshooting](docs/ai-sdk-v5/TROUBLESHOOTING.md)** - Common issues and solutions (written for provider 2.x / AI SDK v5)
148
+ - **[Tool Streaming Support](docs/ai-sdk-v5/TOOL_STREAMING_SUPPORT.md)** - Event semantics and performance notes (written for provider 2.x / AI SDK v5)
149
+ - **[Breaking Changes](docs/ai-sdk-v5/V5_BREAKING_CHANGES.md)** - v0.x to v1.x migration guide (historical)
150
+
151
+ The `docs/ai-sdk-v4/` and `docs/ai-sdk-v5/` directories cover legacy provider versions (0.x and 1.x–2.x respectively) and are kept for reference.
167
152
 
168
153
  ## Migrating to Claude Agent SDK (v2.0.0)
169
154
 
@@ -257,7 +242,7 @@ console.log(result.object); // Matches the schema above
257
242
 
258
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.
259
244
  >
260
- > **Current CLI limitation:** Some JSON Schema features can cause the Claude Code CLI to silently fall back to prose (no `structured_output`). This includes `format` constraints (e.g., `email`, `uri`) and complex regex patterns (lookaheads/backreferences). Workaround: keep the generation schema simple, then validate with a stricter schema client-side. See `examples/structured-output-repro.ts` and `examples/limitations.ts`.
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`.
261
246
 
262
247
  ## Core Features
263
248
 
@@ -273,32 +258,102 @@ console.log(result.object); // Matches the schema above
273
258
 
274
259
  This provider exposes Agent SDK options directly. Key options include:
275
260
 
276
- | Option | Description |
277
- | --------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
278
- | `betas` | Enable beta features (e.g., `['context-1m-2025-08-07']`) |
279
- | `sandbox` | Configure sandbox behavior (`{ enabled: true }`) |
280
- | `plugins` | Load custom plugins from local paths |
281
- | `resumeSessionAt` | Resume session at a specific message UUID |
282
- | `enableFileCheckpointing` | Enable file rewind support |
283
- | `maxBudgetUsd` | Maximum budget in USD for the query |
284
- | `tools` | Tool configuration (array of names or preset) |
285
- | `allowDangerouslySkipPermissions` | Allow bypassing permissions |
286
- | `persistSession` | When `false`, disables session persistence to disk (v3.2.0+) |
287
- | `spawnClaudeCodeProcess` | Custom process spawner for VMs/containers (v3.2.0+) |
288
- | `permissionMode` | Permission mode: `'default'`, `'acceptEdits'`, `'bypassPermissions'`, `'plan'`, `'delegate'`, `'dontAsk'` |
289
- | `sessionId` | Use a specific session ID for deterministic tracking and correlation (v3.4.0+) |
290
- | `debug` | Enable programmatic debug logging from the SDK (v3.4.0+) |
291
- | `debugFile` | Path to a file for SDK debug log output (v3.4.0+) |
292
- | `effort` | Effort level: `'low'`, `'medium'`, `'high'`, or `'max'` |
293
- | `thinking` | Thinking config: `{ type: 'adaptive' }`, `{ type: 'enabled', budgetTokens?: number }`, or `{ type: 'disabled' }` |
294
- | `promptSuggestions` | Enable prompt suggestions (`boolean`) |
295
-
296
- **Agent definitions** (`agents`) now support additional fields (v3.2.0+):
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 |
293
+
294
+ **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
+
296
+ **Agent definitions** (`agents`) use the Agent SDK's `AgentDefinition` type directly (v3.5.0+), which adds `effort`, `permissionMode`, `background`, `memory`, `initialPrompt`, `skills`, `maxTurns`, and full model ID strings on top of the previously supported fields:
297
297
 
298
298
  - `disallowedTools` - Tools to explicitly disallow for the agent
299
299
  - `mcpServers` - MCP servers available to the agent
300
300
  - `criticalSystemReminder_EXPERIMENTAL` - Experimental critical reminder
301
301
 
302
+ **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
+
304
+ ### User dialogs (`onUserDialog` / `supportedDialogKinds`)
305
+
306
+ 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`).
307
+
308
+ ```ts
309
+ const model = claudeCode('sonnet', {
310
+ supportedDialogKinds: ['refusal_fallback_prompt'],
311
+ onUserDialog: async (request) => {
312
+ // Each dialogKind defines its own payload/result shape; answer
313
+ // unrecognized kinds with { behavior: 'cancelled' } so the CLI
314
+ // applies the dialog's default behavior.
315
+ if (request.dialogKind === 'refusal_fallback_prompt') {
316
+ // Valid results for this kind: 'retry_fallback' | 'edit_prompt' | 'cancelled'
317
+ return { behavior: 'completed', result: 'retry_fallback' };
318
+ }
319
+ return { behavior: 'cancelled' };
320
+ },
321
+ });
322
+ ```
323
+
324
+ 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
+
326
+ ### Permission decisions (`canUseTool` extras)
327
+
328
+ SDK 0.3.x enriched the `canUseTool` callback (no provider change needed — these arrive on the existing `options` argument):
329
+
330
+ - `title` — full permission prompt sentence (e.g. "Claude wants to read foo.txt"); prefer it over reconstructing from `toolName` + input
331
+ - `displayName` — short noun phrase for the tool action (e.g. "Read file"), suitable for button labels
332
+ - `description` — human-readable subtitle (e.g. "Claude will have read and write access to ...")
333
+
334
+ `PermissionResult` (both `allow` and `deny` branches) gained an optional `decisionClassification` — `'user_temporary' | 'user_permanent' | 'user_reject'` — describing how the decision was made; the `PermissionDecisionClassification` type is re-exported.
335
+
336
+ ```ts
337
+ const model = claudeCode('sonnet', {
338
+ canUseTool: async (toolName, input, options) => {
339
+ // Prefer the SDK-provided prompt text over reconstructing it yourself.
340
+ const approved = await askUser({
341
+ prompt: options.title ?? `Allow ${toolName}?`, // "Claude wants to read foo.txt"
342
+ buttonLabel: options.displayName ?? toolName, // "Read file"
343
+ subtitle: options.description, // "Claude will have read access to ..."
344
+ });
345
+ return approved
346
+ ? { behavior: 'allow', updatedInput: input, decisionClassification: 'user_temporary' }
347
+ : { behavior: 'deny', message: 'Denied by user', decisionClassification: 'user_reject' };
348
+ },
349
+ });
350
+ ```
351
+
352
+ > **Upstream CLI caveats (verified on CLI 2.1.172):**
353
+ >
354
+ > - 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`.
356
+
302
357
  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`).
303
358
 
304
359
  For options not explicitly exposed, use the `sdkOptions` escape hatch. It **overrides** explicit settings,
@@ -318,6 +373,51 @@ const model = claudeCode('sonnet', {
318
373
  });
319
374
  ```
320
375
 
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.
379
+
380
+ **`Options` fields without a `ClaudeCodeSettings` equivalent** (both still reachable via `sdkOptions`):
381
+
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`.
386
+
387
+ **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
+
389
+ ## Claude Agent SDK 0.3.x Notes
390
+
391
+ This provider depends on `@anthropic-ai/claude-agent-sdk@^0.3.170`. The 0.3.x line introduces a few changes worth knowing about:
392
+
393
+ ### New peer dependencies
394
+
395
+ The Agent SDK now declares two additional peer dependencies alongside `zod`:
396
+
397
+ - `@anthropic-ai/sdk` (`>=0.93.0`)
398
+ - `@modelcontextprotocol/sdk` (`^1.29.0`)
399
+
400
+ npm 7+ installs these automatically; if your package manager does not auto-install peers (or you pin versions), add them to your project explicitly.
401
+
402
+ ### Per-platform native binaries
403
+
404
+ The Agent SDK now distributes the Claude Code runtime as per-platform native binaries via `optionalDependencies` (e.g., `@anthropic-ai/claude-agent-sdk-darwin-arm64`, `-linux-x64`, `-win32-x64`) instead of a single bundled `cli.js`. The right binary for your platform is selected at install time. If you use `pathToClaudeCodeExecutable`, `executable`, or `executableArgs`, re-validate them against your deployment — they primarily apply when pointing at a custom CLI rather than the bundled native binary. Docker/CI images that prune `optionalDependencies` will need to keep them enabled.
405
+
406
+ ### Settings isolation (`settingSources`)
407
+
408
+ SDK 0.3.x changed the SDK-level default: omitting `settingSources` now loads ALL filesystem settings (user, project, and local — matching CLI behavior). This provider preserves its documented isolation default by explicitly passing `settingSources: []` when you don't set it. Opt in to filesystem settings via `settingSources: ['user', 'project', 'local']` (or override through `sdkOptions.settingSources`).
409
+
410
+ ### Subprocess environment allowlist
411
+
412
+ SDK 0.3.x treats `Options.env` as a full **replacement** for the subprocess environment (it is no longer merged with `process.env`). The provider always constructs the subprocess environment from a sanitizing allowlist of `process.env`, then applies your `env` setting and `sdkOptions.env` on top (your values win; set a key to `undefined` to remove it). The allowlist is:
413
+
414
+ - **Platform basics** — POSIX: `HOME`, `LOGNAME`, `PATH`, `SHELL`, `TERM`, `USER`, `LANG`, `LC_ALL`, `TMPDIR`; Windows: `APPDATA`, `COMSPEC`, `HOMEDRIVE`, `HOMEPATH`, `LOCALAPPDATA`, `PATH`, `PATHEXT`, `SYSTEMDRIVE`, `SYSTEMROOT`, `TEMP`, `TMP`, `USERNAME`, `USERPROFILE`, `WINDIR`
415
+ - **Prefix-matched** — any variable starting with `ANTHROPIC_`, `CLAUDE_`, `AWS_`, or `GOOGLE_` (covers `ANTHROPIC_API_KEY`, `CLAUDE_CODE_OAUTH_TOKEN`, `CLAUDE_CONFIG_DIR`, Bedrock and Vertex credentials, etc.)
416
+ - **Proxy/TLS** — `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY` (upper- and lowercase), `NODE_EXTRA_CA_CERTS`, `SSL_CERT_FILE`, `SSL_CERT_DIR`
417
+ - **Cloud extras** — `GCLOUD_PROJECT`, `CLOUD_ML_REGION`
418
+
419
+ Variables outside this list are not inherited by the subprocess; pass them explicitly via the `env` setting if needed. The provider also sets `CLAUDE_AGENT_SDK_CLIENT_APP` to `ai-sdk-provider-claude-code/<version>` (used in the SDK's User-Agent) unless you already set it via the process environment, the `env` setting, or `sdkOptions.env`.
420
+
321
421
  ## Mid-Session Message Injection
322
422
 
323
423
  This provider supports **mid-session message injection** for supervisor patterns, allowing you to interrupt, redirect, or provide feedback to an agent during execution.
@@ -391,12 +491,26 @@ See [examples/message-injection.ts](examples/message-injection.ts) for complete
391
491
 
392
492
  ## Skills Support
393
493
 
394
- Claude Code supports **Skills** - custom tools and capabilities defined in your user or project settings. To enable skills, configure both `settingSources` and `allowedTools`:
494
+ Claude Code supports **Skills** - custom tools and capabilities defined in your user or project settings. The simplest way to enable them (v3.5.0+) is the `skills` option, which removes the need to add `'Skill'` to `allowedTools` yourself:
395
495
 
396
496
  ```typescript
397
497
  import { claudeCode } from 'ai-sdk-provider-claude-code';
398
498
  import { streamText } from 'ai';
399
499
 
500
+ const result = await streamText({
501
+ model: claudeCode('sonnet', {
502
+ settingSources: ['user', 'project'], // still required for filesystem skill discovery
503
+ skills: 'all', // or ['pdf', 'docx'] to enable only specific skills
504
+ }),
505
+ prompt: 'Use my /custom-skill to help with this task',
506
+ });
507
+ ```
508
+
509
+ Note that `skills` is a context filter, not a sandbox: unlisted skills are hidden from the model but their files remain readable on disk.
510
+
511
+ Alternatively, configure both `settingSources` and `allowedTools` explicitly:
512
+
513
+ ```typescript
400
514
  const result = await streamText({
401
515
  model: claudeCode('sonnet', {
402
516
  settingSources: ['user', 'project'],
@@ -409,7 +523,7 @@ const result = await streamText({
409
523
  **Requirements:**
410
524
 
411
525
  - `settingSources` - Where to load skills from (`'user'`, `'project'`, `'local'`)
412
- - `allowedTools` must include `'Skill'` to invoke skills
526
+ - `allowedTools` must include `'Skill'` to invoke skills (not needed when using the `skills` option)
413
527
 
414
528
  **Where to define Skills:**
415
529
 
@@ -420,11 +534,86 @@ const result = await streamText({
420
534
 
421
535
  See [examples/skills-management.ts](examples/skills-management.ts) for more examples.
422
536
 
537
+ ## Using AI SDK Tools
538
+
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.
540
+
541
+ 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
+
543
+ ```typescript
544
+ import { generateText, tool } from 'ai';
545
+ import { z } from 'zod';
546
+ import { claudeCode, createAiSdkMcpServer } from 'ai-sdk-provider-claude-code';
547
+
548
+ const tools = {
549
+ add: tool({
550
+ description: 'Add two numbers',
551
+ inputSchema: z.object({ a: z.number(), b: z.number() }),
552
+ execute: async ({ a, b }) => ({ sum: a + b }),
553
+ }),
554
+ };
555
+
556
+ const { text } = await generateText({
557
+ model: claudeCode('sonnet', {
558
+ mcpServers: { myTools: createAiSdkMcpServer('myTools', tools) },
559
+ // Tools are exposed to the CLI as mcp__<serverName>__<toolName>
560
+ allowedTools: ['mcp__myTools__add'],
561
+ }),
562
+ prompt: 'What is 2 + 3? Use the add tool.',
563
+ });
564
+ ```
565
+
566
+ Notes:
567
+
568
+ - 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
+ - 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.
570
+ - 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
+ - **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
+ - Tools without an `execute` function (client-executed tools) are rejected at creation time.
573
+ - The minimal options object passed to `execute` contains `toolCallId` and `abortSignal` when available; the AI SDK's full `ToolCallOptions` (e.g. `messages`) is not available since the tool runs outside the AI SDK call loop. Note that `toolCallId` here is the MCP JSON-RPC request id (often a small integer like `'42'`), not the model's `toolu_...` tool_use id, so it will not match the `toolCallId` on the AI SDK's `tool-call`/`tool-result` stream parts.
574
+
575
+ See [examples/ai-sdk-tools.ts](examples/ai-sdk-tools.ts) for a runnable example (`npm run example:ai-sdk-tools`).
576
+
577
+ ## Session Management
578
+
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.
580
+
581
+ 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
+
583
+ ## Reducing time-to-first-token (warm start)
584
+
585
+ The Agent SDK's `startup()` (re-exported by this package) pre-spawns the CLI subprocess and completes its initialize handshake ahead of time, returning a `WarmQuery` handle. Calling `warmQuery.query(prompt)` then writes the prompt directly to the already-running process, eliminating subprocess startup latency from time-to-first-token.
586
+
587
+ **Limitation — this does not compose with `generateText`/`streamText`.** A `WarmQuery` is a standalone query path: its `query()` method returns the SDK's `Query` directly (usable once per handle), and the SDK exposes no option for handing a pre-warmed process to a regular `query()` call — which is what this provider invokes internally. The provider therefore cannot consume a warm handle, and `startup()` only helps when you are willing to drive the SDK `Query` yourself for that one latency-critical request:
588
+
589
+ ```ts
590
+ import { startup, type WarmQuery } from 'ai-sdk-provider-claude-code';
591
+
592
+ // Pre-warm during idle time (e.g. at server boot, or while the user types).
593
+ // You can pass the same Options shape the SDK's query() accepts.
594
+ const warm: WarmQuery = await startup({ options: { model: 'sonnet' } });
595
+
596
+ // Later — the prompt goes straight to the ready process (one query per handle):
597
+ for await (const message of warm.query('Summarize the latest deploy log.')) {
598
+ if (message.type === 'assistant') {
599
+ // handle SDK messages directly (this is the SDK stream, not an AI SDK stream)
600
+ }
601
+ }
602
+
603
+ // Or discard an unused warm handle:
604
+ // warm.close(); // explicit
605
+ // await using warm = ... // WarmQuery is AsyncDisposable
606
+ ```
607
+
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.
609
+
423
610
  ## Limitations
424
611
 
425
612
  - Requires Node.js ≥ 18
426
613
  - Image inputs require streaming mode with base64/data URLs (remote fetch is not supported)
427
- - Some AI SDK parameters unsupported (temperature, maxTokens, etc.)
614
+ - 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
616
+ - 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
428
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.
429
618
 
430
619
  ## Tool Error Parity (Streaming)
@@ -458,6 +647,78 @@ providerMetadata['claude-code'].parentToolCallId: string | null;
458
647
 
459
648
  This enables UIs to build hierarchical views of nested agent execution.
460
649
 
650
+ ## Provider Metadata
651
+
652
+ Each response exposes Claude Code metadata under `providerMetadata['claude-code']` (on the `doGenerate` result, and on the `finish` stream event for `doStream`):
653
+
654
+ | Field | Type | Description |
655
+ | ------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
656
+ | `sessionId` | `string` | Session ID for multi-turn conversations |
657
+ | `costUsd` | `number` | Cost of the request in USD |
658
+ | `durationMs` | `number` | Total request duration in milliseconds |
659
+ | `modelUsage` | `object` | Per-model token usage breakdown |
660
+ | `ttftMs` | `number` | Time to first token in milliseconds (when reported by the SDK) |
661
+ | `ttftStreamMs` | `number` | Time to first streamed token in milliseconds (when reported) |
662
+ | `timeToRequestMs` | `number` | Time until the API request was issued in milliseconds (when reported) |
663
+ | `warmSpareClaimed` | `boolean` | Whether the query was served from a pre-warmed spare CLI process (when reported); see **Reducing time-to-first-token (warm start)** |
664
+ | `terminalReason` | `string` | Why the turn loop terminated (SDK `TerminalReason`, e.g. `'completed'`, `'max_turns'`; re-exported type) |
665
+ | `apiRetries` | `number` | Number of API retry attempts observed during the request (only present when > 0) |
666
+ | `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 |
667
+ | `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 |
668
+ | `estimatedThinkingTokens` | `number` | Accumulated live thinking-token estimate from the redacted-thinking phase (only present when > 0); approximate, not the authoritative billed output tokens |
669
+ | `truncated` | `true` | Present when the response was recovered from a truncated SDK stream |
670
+ | `thinkingTraces` | `array` | Thinking blocks extracted in non-streaming mode (`doGenerate` only) |
671
+
672
+ ```ts
673
+ const { providerMetadata } = await generateText({ model, prompt: 'Hello' });
674
+ const meta = providerMetadata?.['claude-code'];
675
+ console.log(meta?.costUsd, meta?.ttftMs, meta?.terminalReason);
676
+ ```
677
+
678
+ ### Prompt suggestions (`onPromptSuggestion`)
679
+
680
+ 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.)
681
+
682
+ ```ts
683
+ const model = claudeCode('sonnet', {
684
+ promptSuggestions: true,
685
+ onPromptSuggestion: (suggestion) => {
686
+ console.log('Suggested next prompt:', suggestion);
687
+ },
688
+ });
689
+ ```
690
+
691
+ ### Context usage (`query.getContextUsage()`)
692
+
693
+ 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):
694
+
695
+ ```ts
696
+ import type { Query } from 'ai-sdk-provider-claude-code';
697
+
698
+ let activeQuery: Query | undefined;
699
+ let contextUsage: unknown;
700
+ const model = claudeCode('sonnet', {
701
+ onQueryCreated: (query) => {
702
+ activeQuery = query;
703
+ },
704
+ hooks: {
705
+ Stop: [
706
+ {
707
+ hooks: [
708
+ async () => {
709
+ contextUsage = await activeQuery?.getContextUsage();
710
+ return { continue: true };
711
+ },
712
+ ],
713
+ },
714
+ ],
715
+ },
716
+ });
717
+
718
+ const result = await generateText({ model, prompt: 'Hello' });
719
+ console.log(contextUsage); // tokens used / remaining in the session context window
720
+ ```
721
+
461
722
  ## Contributing
462
723
 
463
724
  We welcome contributions, especially: