ai-sdk-provider-claude-code 3.0.0-beta.1 → 3.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
@@ -9,7 +9,7 @@
9
9
 
10
10
  # AI SDK Provider for Claude Code SDK
11
11
 
12
- > **Latest Release**: Version 2.x uses the Claude Agent SDK with native structured outputs support (v2.2.0+). For AI SDK v4 support, use the `ai-sdk-v4` tag or version 0.2.x.
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
 
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
 
@@ -17,23 +17,24 @@
17
17
 
18
18
  | Provider Version | AI SDK Version | Underlying SDK | NPM Tag | Status | Branch |
19
19
  | ---------------- | -------------- | ------------------------------------ | -------------------- | ------ | --------------------------------------------------------------------------------------- |
20
- | 2.x.x | v5 | `@anthropic-ai/claude-agent-sdk` | `latest` | Stable | `main` |
21
- | 1.x.x | v5 | `@anthropic-ai/claude-code` (legacy) | `v1-claude-code-sdk` | Stable | [`v1`](https://github.com/ben-vargas/ai-sdk-provider-claude-code/tree/v1) |
22
- | 0.x.x | v4 | `@anthropic-ai/claude-code` | `ai-sdk-v4` | Legacy | [`ai-sdk-v4`](https://github.com/ben-vargas/ai-sdk-provider-claude-code/tree/ai-sdk-v4) |
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) |
23
24
 
24
25
  ### Installing the Right Version
25
26
 
26
- **For AI SDK v5 with Claude Agent SDK (recommended):**
27
+ **For AI SDK v6 (recommended):**
27
28
 
28
29
  ```bash
29
- npm install ai-sdk-provider-claude-code ai
30
+ npm install ai-sdk-provider-claude-code ai@^6.0.0
30
31
  # or explicitly: npm install ai-sdk-provider-claude-code@latest
31
32
  ```
32
33
 
33
- **For AI SDK v5 with Claude Code SDK (legacy):**
34
+ **For AI SDK v5:**
34
35
 
35
36
  ```bash
36
- npm install ai-sdk-provider-claude-code@v1-claude-code-sdk ai
37
+ npm install ai-sdk-provider-claude-code@ai-sdk-v5 ai@^5.0.0
37
38
  ```
38
39
 
39
40
  **For AI SDK v4 (legacy):**
@@ -45,69 +46,17 @@ npm install ai-sdk-provider-claude-code@ai-sdk-v4 ai@^4.3.16
45
46
 
46
47
  ## Zod Compatibility
47
48
 
48
- This package is **tested and compatible with both Zod 3 and Zod 4**, but there's an important peer dependency consideration:
49
-
50
- ### Current Status
51
-
52
- - ✅ **Zod 3** (fully supported, no warnings)
53
- - ⚠️ **Zod 4** (functional, but requires `--legacy-peer-deps`)
54
-
55
- While this package declares support for both versions (`peerDependencies: "zod": "^3.0.0 || ^4.0.0"`), the underlying `@anthropic-ai/claude-agent-sdk` currently only declares support for Zod 3 (`peerDependencies: "zod": "^3.24.1"`).
56
-
57
- **All 302 tests pass with both Zod 3 and Zod 4.** See `examples/zod4-compatibility-test.ts` for comprehensive compatibility verification.
58
-
59
- ### Installation Instructions
60
-
61
- **With Zod 3 (recommended for now):**
49
+ This package is **fully compatible with both Zod 3 and Zod 4**.
62
50
 
63
51
  ```bash
52
+ # With Zod 3
64
53
  npm install ai-sdk-provider-claude-code ai zod@^3.0.0
65
- ```
66
-
67
- **With Zod 4 (requires package manager-specific flags):**
68
-
69
- For **npm**:
70
-
71
- ```bash
72
- npm install ai-sdk-provider-claude-code ai zod@^4.0.0 --legacy-peer-deps
73
- ```
74
-
75
- For **pnpm**:
76
-
77
- ```bash
78
- pnpm install ai-sdk-provider-claude-code ai zod@^4.0.0 --no-strict-peer-dependencies
79
- # Or configure it project-wide:
80
- pnpm config set strict-peer-dependencies false
81
- ```
82
-
83
- For **Yarn** (Berry/v2+):
84
-
85
- ```bash
86
- yarn add ai-sdk-provider-claude-code ai zod@^4.0.0
87
- # Yarn's peer resolution typically doesn't error here
88
- ```
89
-
90
- ### For Package Developers
91
-
92
- If you're developing with this package in your repository, add a configuration file to avoid needing the flag on every install:
93
-
94
- **For npm** (`.npmrc`):
95
-
96
- ```ini
97
- # .npmrc
98
- legacy-peer-deps=true
99
- ```
100
-
101
- **For pnpm** (`.npmrc`):
102
54
 
103
- ```ini
104
- # .npmrc
105
- strict-peer-dependencies=false
55
+ # With Zod 4
56
+ npm install ai-sdk-provider-claude-code ai zod@^4.0.0
106
57
  ```
107
58
 
108
- > **Note**: The `.npmrc` file in this repository is committed for CI/development consistency but is **not included in the published package** (it's excluded via the `files` field in `package.json`). End users will still need to use the appropriate flags when installing with Zod 4.
109
-
110
- > **Temporary Workaround**: This configuration is needed until `@anthropic-ai/claude-agent-sdk` adds official Zod 4 support to their peer dependencies. Track progress in the [claude-agent-sdk repository](https://github.com/anthropics/anthropic-sdk-typescript).
59
+ Both this package and the underlying `@anthropic-ai/claude-agent-sdk` declare support for both versions (`peerDependencies: "zod": "^3.24.1 || ^4.0.0"`).
111
60
 
112
61
  ## Installation
113
62
 
@@ -121,10 +70,13 @@ claude login
121
70
  ### 2. Add the provider
122
71
 
123
72
  ```bash
124
- # For v5 (recommended)
125
- npm install ai-sdk-provider-claude-code ai
73
+ # For AI SDK v6 (recommended)
74
+ npm install ai-sdk-provider-claude-code ai@^6.0.0
75
+
76
+ # For AI SDK v5
77
+ npm install ai-sdk-provider-claude-code@ai-sdk-v5 ai@^5.0.0
126
78
 
127
- # For v4 (legacy)
79
+ # For AI SDK v4 (legacy)
128
80
  npm install ai-sdk-provider-claude-code@ai-sdk-v4 ai@^4.3.16
129
81
  ```
130
82
 
@@ -140,7 +92,7 @@ Please ensure you have appropriate permissions and comply with all applicable te
140
92
 
141
93
  ## Quick Start
142
94
 
143
- ### AI SDK v5
95
+ ### AI SDK v6
144
96
 
145
97
  ```typescript
146
98
  import { streamText } from 'ai';
@@ -155,22 +107,31 @@ const text = await result.text;
155
107
  console.log(text);
156
108
  ```
157
109
 
158
- ### AI SDK v4
110
+ ### AI SDK v5
159
111
 
160
112
  ```typescript
161
- import { generateText } from 'ai';
113
+ // npm install ai-sdk-provider-claude-code@ai-sdk-v5 ai@^5.0.0
114
+ import { streamText } from 'ai';
162
115
  import { claudeCode } from 'ai-sdk-provider-claude-code';
163
116
 
164
- const { text } = await generateText({
117
+ const result = streamText({
165
118
  model: claudeCode('haiku'),
166
119
  prompt: 'Hello, Claude!',
167
120
  });
168
121
 
122
+ const text = await result.text;
169
123
  console.log(text);
170
124
  ```
171
125
 
172
126
  ## Breaking Changes
173
127
 
128
+ ### Version 3.0.0 (AI SDK v6 Stable)
129
+
130
+ This version upgrades to AI SDK v6 stable with updated provider types:
131
+
132
+ - **`usage.raw`** now contains raw provider usage (previously in `providerMetadata['claude-code'].rawUsage`)
133
+ - Internal type changes for `LanguageModelV3Usage` and `LanguageModelV3FinishReason` (transparent to most users)
134
+
174
135
  ### Version 2.0.0 (Claude Agent SDK Migration)
175
136
 
176
137
  This version migrates to `@anthropic-ai/claude-agent-sdk` with **new defaults for better control**:
@@ -317,6 +278,37 @@ console.log(result.object); // Guaranteed to match schema
317
278
  - Use realistic image payloads—very small placeholders may result in the model asking for a different image.
318
279
  - `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`.
319
280
 
281
+ ## Skills Support
282
+
283
+ Claude Code supports **Skills** - custom tools and capabilities defined in your user or project settings. To enable skills, configure both `settingSources` and `allowedTools`:
284
+
285
+ ```typescript
286
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
287
+ import { streamText } from 'ai';
288
+
289
+ const result = await streamText({
290
+ model: claudeCode('sonnet', {
291
+ settingSources: ['user', 'project'],
292
+ allowedTools: ['Skill', 'Read', 'Write', 'Bash'],
293
+ }),
294
+ prompt: 'Use my /custom-skill to help with this task',
295
+ });
296
+ ```
297
+
298
+ **Requirements:**
299
+
300
+ - `settingSources` - Where to load skills from (`'user'`, `'project'`, `'local'`)
301
+ - `allowedTools` must include `'Skill'` to invoke skills
302
+
303
+ **Where to define Skills:**
304
+
305
+ - User: `~/.claude/skills/your-skill/SKILL.md`
306
+ - Project: `.claude/skills/your-skill/SKILL.md`
307
+
308
+ **Validation:** If you add `'Skill'` to `allowedTools` but forget to set `settingSources`, a validation warning will alert you that skills won't load.
309
+
310
+ See [examples/skills-management.ts](examples/skills-management.ts) for more examples.
311
+
320
312
  ## Limitations
321
313
 
322
314
  - Requires Node.js ≥ 18
package/dist/index.cjs CHANGED
@@ -407,13 +407,15 @@ function getErrorMetadata(error) {
407
407
  function mapClaudeCodeFinishReason(subtype) {
408
408
  switch (subtype) {
409
409
  case "success":
410
- return "stop";
410
+ return { unified: "stop", raw: subtype };
411
411
  case "error_max_turns":
412
- return "length";
412
+ return { unified: "length", raw: subtype };
413
413
  case "error_during_execution":
414
- return "error";
414
+ return { unified: "error", raw: subtype };
415
+ case void 0:
416
+ return { unified: "stop", raw: void 0 };
415
417
  default:
416
- return "stop";
418
+ return { unified: "other", raw: subtype };
417
419
  }
418
420
  }
419
421
 
@@ -585,6 +587,11 @@ function validateSettings(settings) {
585
587
  if (validSettings.disallowedTools) {
586
588
  validateToolNames(validSettings.disallowedTools, "disallowed");
587
589
  }
590
+ if (validSettings.allowedTools?.includes("Skill") && !validSettings.settingSources) {
591
+ warnings.push(
592
+ "allowedTools includes 'Skill' but settingSources is not set. Skills require settingSources (e.g., ['user', 'project']) to load skill definitions."
593
+ );
594
+ }
588
595
  return { valid: true, warnings, errors };
589
596
  } catch (error) {
590
597
  errors.push(`Validation error: ${error instanceof Error ? error.message : String(error)}`);
@@ -689,6 +696,42 @@ function isAbortError(err) {
689
696
  return false;
690
697
  }
691
698
  var STREAMING_FEATURE_WARNING = "Claude Agent SDK features (hooks/MCP/images) require streaming input. Set `streamingInput: 'always'` or provide `canUseTool` (auto streams only when canUseTool is set).";
699
+ function createEmptyUsage() {
700
+ return {
701
+ inputTokens: {
702
+ total: 0,
703
+ noCache: 0,
704
+ cacheRead: 0,
705
+ cacheWrite: 0
706
+ },
707
+ outputTokens: {
708
+ total: 0,
709
+ text: void 0,
710
+ reasoning: void 0
711
+ },
712
+ raw: void 0
713
+ };
714
+ }
715
+ function convertClaudeCodeUsage(usage) {
716
+ const inputTokens = usage.input_tokens ?? 0;
717
+ const outputTokens = usage.output_tokens ?? 0;
718
+ const cacheWrite = usage.cache_creation_input_tokens ?? 0;
719
+ const cacheRead = usage.cache_read_input_tokens ?? 0;
720
+ return {
721
+ inputTokens: {
722
+ total: inputTokens + cacheWrite + cacheRead,
723
+ noCache: inputTokens,
724
+ cacheRead,
725
+ cacheWrite
726
+ },
727
+ outputTokens: {
728
+ total: outputTokens,
729
+ text: void 0,
730
+ reasoning: void 0
731
+ },
732
+ raw: usage
733
+ };
734
+ }
692
735
  function toAsyncIterablePrompt(messagesPrompt, outputStreamEnded, sessionId, contentParts) {
693
736
  const content = contentParts && contentParts.length > 0 ? contentParts : [{ type: "text", text: messagesPrompt }];
694
737
  const msg = {
@@ -1042,16 +1085,12 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1042
1085
  const queryOptions = this.createQueryOptions(abortController, options.responseFormat);
1043
1086
  let text = "";
1044
1087
  let structuredOutput;
1045
- let usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
1046
- let finishReason = "stop";
1088
+ let usage = createEmptyUsage();
1089
+ let finishReason = { unified: "stop", raw: void 0 };
1047
1090
  let wasTruncated = false;
1048
1091
  let costUsd;
1049
1092
  let durationMs;
1050
- let rawUsage;
1051
- const warnings = this.generateAllWarnings(
1052
- options,
1053
- messagesPrompt
1054
- );
1093
+ const warnings = this.generateAllWarnings(options, messagesPrompt);
1055
1094
  if (messageWarnings) {
1056
1095
  messageWarnings.forEach((warning) => {
1057
1096
  warnings.push({
@@ -1114,18 +1153,13 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1114
1153
  `[claude-code] Request completed - Session: ${message.session_id}, Cost: $${costUsd?.toFixed(4) ?? "N/A"}, Duration: ${durationMs ?? "N/A"}ms`
1115
1154
  );
1116
1155
  if ("usage" in message) {
1117
- rawUsage = message.usage;
1118
- usage = {
1119
- inputTokens: (message.usage.cache_creation_input_tokens ?? 0) + (message.usage.cache_read_input_tokens ?? 0) + (message.usage.input_tokens ?? 0),
1120
- outputTokens: message.usage.output_tokens ?? 0,
1121
- totalTokens: (message.usage.cache_creation_input_tokens ?? 0) + (message.usage.cache_read_input_tokens ?? 0) + (message.usage.input_tokens ?? 0) + (message.usage.output_tokens ?? 0)
1122
- };
1156
+ usage = convertClaudeCodeUsage(message.usage);
1123
1157
  this.logger.debug(
1124
- `[claude-code] Token usage - Input: ${usage.inputTokens}, Output: ${usage.outputTokens}, Total: ${usage.totalTokens}`
1158
+ `[claude-code] Token usage - Input: ${usage.inputTokens.total}, Output: ${usage.outputTokens.total}`
1125
1159
  );
1126
1160
  }
1127
1161
  finishReason = mapClaudeCodeFinishReason(message.subtype);
1128
- this.logger.debug(`[claude-code] Finish reason: ${finishReason}`);
1162
+ this.logger.debug(`[claude-code] Finish reason: ${finishReason.unified}`);
1129
1163
  } else if (message.type === "system" && message.subtype === "init") {
1130
1164
  this.setSessionId(message.session_id);
1131
1165
  this.logger.info(`[claude-code] Session initialized: ${message.session_id}`);
@@ -1145,7 +1179,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1145
1179
  `[claude-code] Detected truncated response, returning ${text.length} characters of buffered text`
1146
1180
  );
1147
1181
  wasTruncated = true;
1148
- finishReason = "length";
1182
+ finishReason = { unified: "length", raw: "truncation" };
1149
1183
  warnings.push({
1150
1184
  type: "other",
1151
1185
  message: CLAUDE_CODE_TRUNCATION_WARNING
@@ -1177,7 +1211,6 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1177
1211
  ...this.sessionId !== void 0 && { sessionId: this.sessionId },
1178
1212
  ...costUsd !== void 0 && { costUsd },
1179
1213
  ...durationMs !== void 0 && { durationMs },
1180
- ...rawUsage !== void 0 && { rawUsage },
1181
1214
  ...wasTruncated && { truncated: true }
1182
1215
  }
1183
1216
  }
@@ -1207,10 +1240,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1207
1240
  if (queryOptions.includePartialMessages === void 0) {
1208
1241
  queryOptions.includePartialMessages = true;
1209
1242
  }
1210
- const warnings = this.generateAllWarnings(
1211
- options,
1212
- messagesPrompt
1213
- );
1243
+ const warnings = this.generateAllWarnings(options, messagesPrompt);
1214
1244
  if (messageWarnings) {
1215
1245
  messageWarnings.forEach((warning) => {
1216
1246
  warnings.push({
@@ -1275,7 +1305,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1275
1305
  }
1276
1306
  toolStates.clear();
1277
1307
  };
1278
- let usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
1308
+ let usage = createEmptyUsage();
1279
1309
  let accumulatedText = "";
1280
1310
  let textPartId;
1281
1311
  let streamedTextLength = 0;
@@ -1573,22 +1603,16 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1573
1603
  this.logger.info(
1574
1604
  `[claude-code] Stream completed - Session: ${message.session_id}, Cost: $${message.total_cost_usd?.toFixed(4) ?? "N/A"}, Duration: ${message.duration_ms ?? "N/A"}ms`
1575
1605
  );
1576
- let rawUsage;
1577
1606
  if ("usage" in message) {
1578
- rawUsage = message.usage;
1579
- usage = {
1580
- inputTokens: (message.usage.cache_creation_input_tokens ?? 0) + (message.usage.cache_read_input_tokens ?? 0) + (message.usage.input_tokens ?? 0),
1581
- outputTokens: message.usage.output_tokens ?? 0,
1582
- totalTokens: (message.usage.cache_creation_input_tokens ?? 0) + (message.usage.cache_read_input_tokens ?? 0) + (message.usage.input_tokens ?? 0) + (message.usage.output_tokens ?? 0)
1583
- };
1607
+ usage = convertClaudeCodeUsage(message.usage);
1584
1608
  this.logger.debug(
1585
- `[claude-code] Stream token usage - Input: ${usage.inputTokens}, Output: ${usage.outputTokens}, Total: ${usage.totalTokens}`
1609
+ `[claude-code] Stream token usage - Input: ${usage.inputTokens.total}, Output: ${usage.outputTokens.total}`
1586
1610
  );
1587
1611
  }
1588
1612
  const finishReason = mapClaudeCodeFinishReason(
1589
1613
  message.subtype
1590
1614
  );
1591
- this.logger.debug(`[claude-code] Stream finish reason: ${finishReason}`);
1615
+ this.logger.debug(`[claude-code] Stream finish reason: ${finishReason.unified}`);
1592
1616
  this.setSessionId(message.session_id);
1593
1617
  const structuredOutput = "structured_output" in message ? message.structured_output : void 0;
1594
1618
  const alreadyStreamedJson = textPartId && options.responseFormat?.type === "json" && hasReceivedStreamEvents;
@@ -1647,7 +1671,6 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1647
1671
  costUsd: message.total_cost_usd
1648
1672
  },
1649
1673
  ...message.duration_ms !== void 0 && { durationMs: message.duration_ms },
1650
- ...rawUsage !== void 0 && { rawUsage },
1651
1674
  // JSON validation warnings are collected during streaming and included
1652
1675
  // in providerMetadata since the AI SDK's finish event doesn't support
1653
1676
  // a top-level warnings field (unlike stream-start which was already emitted)
@@ -1710,7 +1733,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1710
1733
  const warningsJson = this.serializeWarningsForMetadata(streamWarnings);
1711
1734
  controller.enqueue({
1712
1735
  type: "finish",
1713
- finishReason: "length",
1736
+ finishReason: { unified: "length", raw: "truncation" },
1714
1737
  usage,
1715
1738
  providerMetadata: {
1716
1739
  "claude-code": {