ai-sdk-provider-claude-code 2.2.4 → 3.0.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
@@ -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
54
 
96
- ```ini
97
- # .npmrc
98
- legacy-peer-deps=true
55
+ # With Zod 4
56
+ npm install ai-sdk-provider-claude-code ai zod@^4.0.0
99
57
  ```
100
58
 
101
- **For pnpm** (`.npmrc`):
102
-
103
- ```ini
104
- # .npmrc
105
- strict-peer-dependencies=false
106
- ```
107
-
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
126
75
 
127
- # For v4 (legacy)
76
+ # For AI SDK v5
77
+ npm install ai-sdk-provider-claude-code@ai-sdk-v5 ai@^5.0.0
78
+
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**:
package/dist/index.cjs CHANGED
@@ -238,7 +238,22 @@ function convertToClaudeCodeMessages(prompt) {
238
238
  }
239
239
  case "tool":
240
240
  for (const tool3 of message.content) {
241
- const resultText = tool3.output.type === "text" ? tool3.output.value : JSON.stringify(tool3.output.value);
241
+ if (tool3.type === "tool-approval-response") {
242
+ continue;
243
+ }
244
+ let resultText;
245
+ const output = tool3.output;
246
+ if (output.type === "text" || output.type === "error-text") {
247
+ resultText = output.value;
248
+ } else if (output.type === "json" || output.type === "error-json") {
249
+ resultText = JSON.stringify(output.value);
250
+ } else if (output.type === "execution-denied") {
251
+ resultText = `[Execution denied${output.reason ? `: ${output.reason}` : ""}]`;
252
+ } else if (output.type === "content") {
253
+ resultText = output.value.filter((part) => part.type === "text").map((part) => part.text).join("\n");
254
+ } else {
255
+ resultText = "[Unknown output type]";
256
+ }
242
257
  const formattedToolResult = `Tool Result (${tool3.toolName}): ${resultText}`;
243
258
  messages.push(formattedToolResult);
244
259
  addSegment(formattedToolResult);
@@ -392,13 +407,15 @@ function getErrorMetadata(error) {
392
407
  function mapClaudeCodeFinishReason(subtype) {
393
408
  switch (subtype) {
394
409
  case "success":
395
- return "stop";
410
+ return { unified: "stop", raw: subtype };
396
411
  case "error_max_turns":
397
- return "length";
412
+ return { unified: "length", raw: subtype };
398
413
  case "error_during_execution":
399
- return "error";
414
+ return { unified: "error", raw: subtype };
415
+ case void 0:
416
+ return { unified: "stop", raw: void 0 };
400
417
  default:
401
- return "stop";
418
+ return { unified: "other", raw: subtype };
402
419
  }
403
420
  }
404
421
 
@@ -674,6 +691,42 @@ function isAbortError(err) {
674
691
  return false;
675
692
  }
676
693
  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).";
694
+ function createEmptyUsage() {
695
+ return {
696
+ inputTokens: {
697
+ total: 0,
698
+ noCache: 0,
699
+ cacheRead: 0,
700
+ cacheWrite: 0
701
+ },
702
+ outputTokens: {
703
+ total: 0,
704
+ text: void 0,
705
+ reasoning: void 0
706
+ },
707
+ raw: void 0
708
+ };
709
+ }
710
+ function convertClaudeCodeUsage(usage) {
711
+ const inputTokens = usage.input_tokens ?? 0;
712
+ const outputTokens = usage.output_tokens ?? 0;
713
+ const cacheWrite = usage.cache_creation_input_tokens ?? 0;
714
+ const cacheRead = usage.cache_read_input_tokens ?? 0;
715
+ return {
716
+ inputTokens: {
717
+ total: inputTokens + cacheWrite + cacheRead,
718
+ noCache: inputTokens,
719
+ cacheRead,
720
+ cacheWrite
721
+ },
722
+ outputTokens: {
723
+ total: outputTokens,
724
+ text: void 0,
725
+ reasoning: void 0
726
+ },
727
+ raw: usage
728
+ };
729
+ }
677
730
  function toAsyncIterablePrompt(messagesPrompt, outputStreamEnded, sessionId, contentParts) {
678
731
  const content = contentParts && contentParts.length > 0 ? contentParts : [{ type: "text", text: messagesPrompt }];
679
732
  const msg = {
@@ -698,7 +751,7 @@ var modelMap = {
698
751
  haiku: "haiku"
699
752
  };
700
753
  var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
701
- specificationVersion = "v2";
754
+ specificationVersion = "v3";
702
755
  defaultObjectGenerationMode = "json";
703
756
  supportsImageUrls = false;
704
757
  supportedUrls = {};
@@ -841,8 +894,8 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
841
894
  if (unsupportedParams.length > 0) {
842
895
  for (const param of unsupportedParams) {
843
896
  warnings.push({
844
- type: "unsupported-setting",
845
- setting: param,
897
+ type: "unsupported",
898
+ feature: param,
846
899
  details: `Claude Code SDK does not support the ${param} parameter. It will be ignored.`
847
900
  });
848
901
  }
@@ -861,8 +914,8 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
861
914
  });
862
915
  if (options.responseFormat?.type === "json" && !options.responseFormat.schema) {
863
916
  warnings.push({
864
- type: "unsupported-setting",
865
- setting: "responseFormat",
917
+ type: "unsupported",
918
+ feature: "responseFormat",
866
919
  details: "JSON response format requires a schema for the Claude Code provider. The JSON responseFormat is ignored and the call is treated as plain text."
867
920
  });
868
921
  }
@@ -1027,16 +1080,12 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1027
1080
  const queryOptions = this.createQueryOptions(abortController, options.responseFormat);
1028
1081
  let text = "";
1029
1082
  let structuredOutput;
1030
- let usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
1031
- let finishReason = "stop";
1083
+ let usage = createEmptyUsage();
1084
+ let finishReason = { unified: "stop", raw: void 0 };
1032
1085
  let wasTruncated = false;
1033
1086
  let costUsd;
1034
1087
  let durationMs;
1035
- let rawUsage;
1036
- const warnings = this.generateAllWarnings(
1037
- options,
1038
- messagesPrompt
1039
- );
1088
+ const warnings = this.generateAllWarnings(options, messagesPrompt);
1040
1089
  if (messageWarnings) {
1041
1090
  messageWarnings.forEach((warning) => {
1042
1091
  warnings.push({
@@ -1099,18 +1148,13 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1099
1148
  `[claude-code] Request completed - Session: ${message.session_id}, Cost: $${costUsd?.toFixed(4) ?? "N/A"}, Duration: ${durationMs ?? "N/A"}ms`
1100
1149
  );
1101
1150
  if ("usage" in message) {
1102
- rawUsage = message.usage;
1103
- usage = {
1104
- inputTokens: (message.usage.cache_creation_input_tokens ?? 0) + (message.usage.cache_read_input_tokens ?? 0) + (message.usage.input_tokens ?? 0),
1105
- outputTokens: message.usage.output_tokens ?? 0,
1106
- 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)
1107
- };
1151
+ usage = convertClaudeCodeUsage(message.usage);
1108
1152
  this.logger.debug(
1109
- `[claude-code] Token usage - Input: ${usage.inputTokens}, Output: ${usage.outputTokens}, Total: ${usage.totalTokens}`
1153
+ `[claude-code] Token usage - Input: ${usage.inputTokens.total}, Output: ${usage.outputTokens.total}`
1110
1154
  );
1111
1155
  }
1112
1156
  finishReason = mapClaudeCodeFinishReason(message.subtype);
1113
- this.logger.debug(`[claude-code] Finish reason: ${finishReason}`);
1157
+ this.logger.debug(`[claude-code] Finish reason: ${finishReason.unified}`);
1114
1158
  } else if (message.type === "system" && message.subtype === "init") {
1115
1159
  this.setSessionId(message.session_id);
1116
1160
  this.logger.info(`[claude-code] Session initialized: ${message.session_id}`);
@@ -1130,7 +1174,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1130
1174
  `[claude-code] Detected truncated response, returning ${text.length} characters of buffered text`
1131
1175
  );
1132
1176
  wasTruncated = true;
1133
- finishReason = "length";
1177
+ finishReason = { unified: "length", raw: "truncation" };
1134
1178
  warnings.push({
1135
1179
  type: "other",
1136
1180
  message: CLAUDE_CODE_TRUNCATION_WARNING
@@ -1162,7 +1206,6 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1162
1206
  ...this.sessionId !== void 0 && { sessionId: this.sessionId },
1163
1207
  ...costUsd !== void 0 && { costUsd },
1164
1208
  ...durationMs !== void 0 && { durationMs },
1165
- ...rawUsage !== void 0 && { rawUsage },
1166
1209
  ...wasTruncated && { truncated: true }
1167
1210
  }
1168
1211
  }
@@ -1192,10 +1235,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1192
1235
  if (queryOptions.includePartialMessages === void 0) {
1193
1236
  queryOptions.includePartialMessages = true;
1194
1237
  }
1195
- const warnings = this.generateAllWarnings(
1196
- options,
1197
- messagesPrompt
1198
- );
1238
+ const warnings = this.generateAllWarnings(options, messagesPrompt);
1199
1239
  if (messageWarnings) {
1200
1240
  messageWarnings.forEach((warning) => {
1201
1241
  warnings.push({
@@ -1260,7 +1300,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1260
1300
  }
1261
1301
  toolStates.clear();
1262
1302
  };
1263
- let usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
1303
+ let usage = createEmptyUsage();
1264
1304
  let accumulatedText = "";
1265
1305
  let textPartId;
1266
1306
  let streamedTextLength = 0;
@@ -1558,22 +1598,16 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1558
1598
  this.logger.info(
1559
1599
  `[claude-code] Stream completed - Session: ${message.session_id}, Cost: $${message.total_cost_usd?.toFixed(4) ?? "N/A"}, Duration: ${message.duration_ms ?? "N/A"}ms`
1560
1600
  );
1561
- let rawUsage;
1562
1601
  if ("usage" in message) {
1563
- rawUsage = message.usage;
1564
- usage = {
1565
- inputTokens: (message.usage.cache_creation_input_tokens ?? 0) + (message.usage.cache_read_input_tokens ?? 0) + (message.usage.input_tokens ?? 0),
1566
- outputTokens: message.usage.output_tokens ?? 0,
1567
- 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)
1568
- };
1602
+ usage = convertClaudeCodeUsage(message.usage);
1569
1603
  this.logger.debug(
1570
- `[claude-code] Stream token usage - Input: ${usage.inputTokens}, Output: ${usage.outputTokens}, Total: ${usage.totalTokens}`
1604
+ `[claude-code] Stream token usage - Input: ${usage.inputTokens.total}, Output: ${usage.outputTokens.total}`
1571
1605
  );
1572
1606
  }
1573
1607
  const finishReason = mapClaudeCodeFinishReason(
1574
1608
  message.subtype
1575
1609
  );
1576
- this.logger.debug(`[claude-code] Stream finish reason: ${finishReason}`);
1610
+ this.logger.debug(`[claude-code] Stream finish reason: ${finishReason.unified}`);
1577
1611
  this.setSessionId(message.session_id);
1578
1612
  const structuredOutput = "structured_output" in message ? message.structured_output : void 0;
1579
1613
  const alreadyStreamedJson = textPartId && options.responseFormat?.type === "json" && hasReceivedStreamEvents;
@@ -1632,7 +1666,6 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1632
1666
  costUsd: message.total_cost_usd
1633
1667
  },
1634
1668
  ...message.duration_ms !== void 0 && { durationMs: message.duration_ms },
1635
- ...rawUsage !== void 0 && { rawUsage },
1636
1669
  // JSON validation warnings are collected during streaming and included
1637
1670
  // in providerMetadata since the AI SDK's finish event doesn't support
1638
1671
  // a top-level warnings field (unlike stream-start which was already emitted)
@@ -1695,7 +1728,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1695
1728
  const warningsJson = this.serializeWarningsForMetadata(streamWarnings);
1696
1729
  controller.enqueue({
1697
1730
  type: "finish",
1698
- finishReason: "length",
1731
+ finishReason: { unified: "length", raw: "truncation" },
1699
1732
  usage,
1700
1733
  providerMetadata: {
1701
1734
  "claude-code": {
@@ -1748,9 +1781,9 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1748
1781
  const m = w.message;
1749
1782
  if (m !== void 0) base.message = String(m);
1750
1783
  }
1751
- if (w.type === "unsupported-setting") {
1752
- const setting = w.setting;
1753
- if (setting !== void 0) base.setting = String(setting);
1784
+ if (w.type === "unsupported" || w.type === "compatibility") {
1785
+ const feature = w.feature;
1786
+ if (feature !== void 0) base.feature = String(feature);
1754
1787
  if ("details" in w) {
1755
1788
  const d = w.details;
1756
1789
  if (d !== void 0) base.details = String(d);
@@ -1797,10 +1830,11 @@ function createClaudeCode(options = {}) {
1797
1830
  };
1798
1831
  provider.languageModel = createModel;
1799
1832
  provider.chat = createModel;
1800
- provider.textEmbeddingModel = (modelId) => {
1833
+ provider.specificationVersion = "v3";
1834
+ provider.embeddingModel = (modelId) => {
1801
1835
  throw new import_provider3.NoSuchModelError({
1802
1836
  modelId,
1803
- modelType: "textEmbeddingModel"
1837
+ modelType: "embeddingModel"
1804
1838
  });
1805
1839
  };
1806
1840
  provider.imageModel = (modelId) => {