ai-sdk-provider-claude-code 0.2.1 → 1.0.0-beta.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
@@ -1,8 +1,36 @@
1
- # AI SDK Provider for Claude Code (Alpha)
1
+ <p align="center">
2
+ <img src="https://img.shields.io/badge/status-beta-FF6700" alt="beta status">
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/beta?color=00A79E" alt="npm beta version" /></a>
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
+ <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>
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
+ </p>
2
9
 
3
- > **Warning**: This package is experimental and subject to change.
10
+ # AI SDK Provider for Claude Code SDK
4
11
 
5
- **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-code` CLI.
12
+ > **Beta Release**: This is the v5-beta compatible version. For AI SDK v4 support, use version 0.2.x.
13
+
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-code` SDK/CLI.
15
+
16
+ ## Version Compatibility
17
+
18
+ | Provider Version | AI SDK Version | Status | Branch |
19
+ |-----------------|----------------|---------|---------|
20
+ | 0.x | v4 | Stable | [`ai-sdk-v4`](https://github.com/ben-vargas/ai-sdk-provider-claude-code/tree/ai-sdk-v4) |
21
+ | 1.x-beta | v5-beta | Beta | `main` |
22
+
23
+ ### Installing the Right Version
24
+
25
+ **For AI SDK v4 (stable):**
26
+ ```bash
27
+ npm install ai-sdk-provider-claude-code@^0.2.2 ai@^4.3.16
28
+ ```
29
+
30
+ **For AI SDK v5-beta:**
31
+ ```bash
32
+ npm install ai-sdk-provider-claude-code@beta ai@beta
33
+ ```
6
34
 
7
35
  ## Installation
8
36
 
@@ -14,14 +42,18 @@ claude login
14
42
 
15
43
  ### 2. Add the provider
16
44
  ```bash
17
- npm install ai-sdk-provider-claude-code ai
45
+ # For v5-beta
46
+ npm install ai-sdk-provider-claude-code@beta ai@beta
47
+
48
+ # For v4 (stable)
49
+ npm install ai-sdk-provider-claude-code@^0.2.2 ai@^4.3.16
18
50
  ```
19
51
 
20
52
  ## Disclaimer
21
53
 
22
54
  **This is an unofficial community provider** and is not affiliated with or endorsed by Anthropic or Vercel. By using this provider:
23
55
 
24
- - You understand that your data will be sent to Anthropic's servers through the Claude Code CLI
56
+ - You understand that your data will be sent to Anthropic's servers through the Claude Code SDK
25
57
  - You agree to comply with [Anthropic's Terms of Service](https://www.anthropic.com/legal/consumer-terms)
26
58
  - You acknowledge this software is provided "as is" without warranties of any kind
27
59
 
@@ -29,6 +61,21 @@ Please ensure you have appropriate permissions and comply with all applicable te
29
61
 
30
62
  ## Quick Start
31
63
 
64
+ ### AI SDK v5-beta
65
+ ```typescript
66
+ import { streamText } from 'ai';
67
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
68
+
69
+ const result = streamText({
70
+ model: claudeCode('sonnet'),
71
+ prompt: 'Hello, Claude!'
72
+ });
73
+
74
+ const text = await result.text;
75
+ console.log(text);
76
+ ```
77
+
78
+ ### AI SDK v4
32
79
  ```typescript
33
80
  import { generateText } from 'ai';
34
81
  import { claudeCode } from 'ai-sdk-provider-claude-code';
@@ -41,6 +88,16 @@ const { text } = await generateText({
41
88
  console.log(text);
42
89
  ```
43
90
 
91
+ ## Breaking Changes in v1.x-beta
92
+
93
+ See [Breaking Changes Guide](docs/ai-sdk-v5/V5_BREAKING_CHANGES.md) for details on migrating from v0.x to v1.x-beta.
94
+
95
+ Key changes:
96
+ - Requires AI SDK v5-beta
97
+ - New streaming API pattern
98
+ - Updated token usage properties
99
+ - Changed message types
100
+
44
101
  ## Models
45
102
 
46
103
  - **`opus`** - Claude 4 Opus (most capable)
@@ -48,13 +105,14 @@ console.log(text);
48
105
 
49
106
  ## Documentation
50
107
 
51
- - **[Usage Guide](docs/GUIDE.md)** - Comprehensive examples and configuration
52
- - **[Troubleshooting](docs/TROUBLESHOOTING.md)** - Common issues and solutions
108
+ - **[Usage Guide](docs/ai-sdk-v5/GUIDE.md)** - Comprehensive examples and configuration
109
+ - **[Breaking Changes](docs/ai-sdk-v5/V5_BREAKING_CHANGES.md)** - v0.x to v1.x migration guide
110
+ - **[Troubleshooting](docs/ai-sdk-v5/TROUBLESHOOTING.md)** - Common issues and solutions
53
111
  - **[Examples](examples/)** - Sample scripts and patterns
54
112
 
55
113
  ## Core Features
56
114
 
57
- - 🚀 Full Vercel AI SDK compatibility
115
+ - 🚀 Vercel AI SDK compatibility
58
116
  - 🔄 Streaming support
59
117
  - 💬 Multi-turn conversations
60
118
  - 🎯 Object generation with JSON schemas
@@ -75,9 +133,9 @@ We welcome contributions, especially:
75
133
  - Better error handling
76
134
  - Additional examples
77
135
 
78
- See [Contributing Guidelines](docs/GUIDE.md#contributing) for details.
136
+ See [Contributing Guidelines](docs/ai-sdk-v5/GUIDE.md#contributing) for details.
79
137
 
80
- For development status and technical details, see [Development Status](docs/DEVELOPMENT-STATUS.md).
138
+ For development status and technical details, see [Development Status](docs/ai-sdk-v5/DEVELOPMENT-STATUS.md).
81
139
 
82
140
  ## License
83
141
 
package/dist/index.cjs CHANGED
@@ -40,7 +40,7 @@ var import_provider2 = require("@ai-sdk/provider");
40
40
  var import_provider_utils = require("@ai-sdk/provider-utils");
41
41
 
42
42
  // src/convert-to-claude-code-messages.ts
43
- function convertToClaudeCodeMessages(prompt, mode) {
43
+ function convertToClaudeCodeMessages(prompt, mode = { type: "regular" }, jsonSchema) {
44
44
  const messages = [];
45
45
  const warnings = [];
46
46
  let systemPrompt;
@@ -59,26 +59,33 @@ function convertToClaudeCodeMessages(prompt, mode) {
59
59
  }
60
60
  const imageParts = message.content.filter((part) => part.type === "image");
61
61
  if (imageParts.length > 0) {
62
- warnings.push("Claude Code CLI does not support image inputs. Images will be ignored.");
62
+ warnings.push("Claude Code SDK does not support image inputs. Images will be ignored.");
63
63
  }
64
64
  }
65
65
  break;
66
- case "assistant":
66
+ case "assistant": {
67
+ let assistantContent = "";
67
68
  if (typeof message.content === "string") {
68
- messages.push(`Assistant: ${message.content}`);
69
+ assistantContent = message.content;
69
70
  } else {
70
71
  const textParts = message.content.filter((part) => part.type === "text").map((part) => part.text).join("\n");
71
72
  if (textParts) {
72
- messages.push(`Assistant: ${textParts}`);
73
+ assistantContent = textParts;
73
74
  }
74
75
  const toolCalls = message.content.filter((part) => part.type === "tool-call");
75
76
  if (toolCalls.length > 0) {
76
- messages.push(`Assistant: [Tool calls made]`);
77
+ assistantContent += `
78
+ [Tool calls made]`;
77
79
  }
78
80
  }
81
+ messages.push(`Assistant: ${assistantContent}`);
79
82
  break;
83
+ }
80
84
  case "tool":
81
- messages.push(`Tool Result (${message.content[0].toolName}): ${JSON.stringify(message.content[0].result)}`);
85
+ for (const tool of message.content) {
86
+ const resultText = tool.output.type === "text" ? tool.output.value : JSON.stringify(tool.output.value);
87
+ messages.push(`Tool Result (${tool.toolName}): ${resultText}`);
88
+ }
82
89
  break;
83
90
  }
84
91
  }
@@ -103,19 +110,20 @@ function convertToClaudeCodeMessages(prompt, mode) {
103
110
  } else {
104
111
  finalPrompt = formattedMessages.join("\n\n");
105
112
  }
106
- if (mode?.type === "object-json") {
107
- finalPrompt = `${finalPrompt}
113
+ if (mode?.type === "object-json" && jsonSchema) {
114
+ const schemaStr = JSON.stringify(jsonSchema, null, 2);
115
+ finalPrompt = `CRITICAL: You MUST respond with ONLY a JSON object. NO other text, NO explanations, NO questions.
116
+
117
+ Your response MUST start with { and end with }
118
+
119
+ The JSON MUST match this EXACT schema:
120
+ ${schemaStr}
121
+
122
+ Now, based on the following conversation, generate ONLY the JSON object with the exact fields specified above:
108
123
 
109
- CRITICAL INSTRUCTION: You MUST respond with ONLY valid JSON. Follow these rules EXACTLY:
110
- 1. Start your response with an opening brace {
111
- 2. End your response with a closing brace }
112
- 3. Do NOT include any text before the opening brace
113
- 4. Do NOT include any text after the closing brace
114
- 5. Do NOT use markdown code blocks or backticks
115
- 6. Do NOT include explanations or commentary
116
- 7. The ENTIRE response must be valid JSON that can be parsed with JSON.parse()
124
+ ${finalPrompt}
117
125
 
118
- Begin your response with { and end with }`;
126
+ Remember: Your ENTIRE response must be ONLY the JSON object, starting with { and ending with }`;
119
127
  }
120
128
  return {
121
129
  messagesPrompt: finalPrompt,
@@ -239,7 +247,7 @@ function createAuthenticationError({
239
247
  message
240
248
  }) {
241
249
  return new import_provider.LoadAPIKeyError({
242
- message: message || "Authentication failed. Please ensure Claude Code CLI is properly authenticated."
250
+ message: message || "Authentication failed. Please ensure Claude Code SDK is properly authenticated."
243
251
  });
244
252
  }
245
253
  function createTimeoutError({
@@ -432,9 +440,10 @@ var modelMap = {
432
440
  "sonnet": "sonnet"
433
441
  };
434
442
  var ClaudeCodeLanguageModel = class {
435
- specificationVersion = "v1";
443
+ specificationVersion = "v2";
436
444
  defaultObjectGenerationMode = "json";
437
445
  supportsImageUrls = false;
446
+ supportedUrls = {};
438
447
  supportsStructuredOutputs = false;
439
448
  modelId;
440
449
  settings;
@@ -469,7 +478,6 @@ var ClaudeCodeLanguageModel = class {
469
478
  const warnings = [];
470
479
  const unsupportedParams = [];
471
480
  if (options.temperature !== void 0) unsupportedParams.push("temperature");
472
- if (options.maxTokens !== void 0) unsupportedParams.push("maxTokens");
473
481
  if (options.topP !== void 0) unsupportedParams.push("topP");
474
482
  if (options.topK !== void 0) unsupportedParams.push("topK");
475
483
  if (options.presencePenalty !== void 0) unsupportedParams.push("presencePenalty");
@@ -481,7 +489,7 @@ var ClaudeCodeLanguageModel = class {
481
489
  warnings.push({
482
490
  type: "unsupported-setting",
483
491
  setting: param,
484
- details: `Claude Code CLI does not support the ${param} parameter. It will be ignored.`
492
+ details: `Claude Code SDK does not support the ${param} parameter. It will be ignored.`
485
493
  });
486
494
  }
487
495
  }
@@ -550,7 +558,7 @@ var ClaudeCodeLanguageModel = class {
550
558
  const isAuthError = authErrorPatterns.some((pattern) => errorMessage.includes(pattern)) || exitCode === 401;
551
559
  if (isAuthError) {
552
560
  return createAuthenticationError({
553
- message: isErrorWithMessage(error) && error.message ? error.message : "Authentication failed. Please ensure Claude Code CLI is properly authenticated."
561
+ message: isErrorWithMessage(error) && error.message ? error.message : "Authentication failed. Please ensure Claude Code SDK is properly authenticated."
554
562
  });
555
563
  }
556
564
  const errorCode = isErrorWithCode(error) && typeof error.code === "string" ? error.code : "";
@@ -564,7 +572,7 @@ var ClaudeCodeLanguageModel = class {
564
572
  }
565
573
  const isRetryable = errorCode === "ENOENT" || errorCode === "ECONNREFUSED" || errorCode === "ETIMEDOUT" || errorCode === "ECONNRESET";
566
574
  return createAPICallError({
567
- message: isErrorWithMessage(error) && error.message ? error.message : "Claude Code CLI error",
575
+ message: isErrorWithMessage(error) && error.message ? error.message : "Claude Code SDK error",
568
576
  code: errorCode || void 0,
569
577
  exitCode,
570
578
  stderr: isErrorWithCode(error) && typeof error.stderr === "string" ? error.stderr : void 0,
@@ -603,7 +611,12 @@ var ClaudeCodeLanguageModel = class {
603
611
  }
604
612
  }
605
613
  async doGenerate(options) {
606
- const { messagesPrompt, warnings: messageWarnings } = convertToClaudeCodeMessages(options.prompt, options.mode);
614
+ const mode = options.responseFormat?.type === "json" ? { type: "object-json" } : { type: "regular" };
615
+ const { messagesPrompt, warnings: messageWarnings } = convertToClaudeCodeMessages(
616
+ options.prompt,
617
+ mode,
618
+ options.responseFormat?.type === "json" ? options.responseFormat.schema : void 0
619
+ );
607
620
  const abortController = new AbortController();
608
621
  let abortListener;
609
622
  if (options.abortSignal) {
@@ -612,7 +625,7 @@ var ClaudeCodeLanguageModel = class {
612
625
  }
613
626
  const queryOptions = this.createQueryOptions(abortController);
614
627
  let text = "";
615
- let usage = { promptTokens: 0, completionTokens: 0 };
628
+ let usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
616
629
  let finishReason = "stop";
617
630
  let costUsd;
618
631
  let durationMs;
@@ -643,8 +656,9 @@ var ClaudeCodeLanguageModel = class {
643
656
  if ("usage" in message) {
644
657
  rawUsage = message.usage;
645
658
  usage = {
646
- promptTokens: (message.usage.cache_creation_input_tokens ?? 0) + (message.usage.cache_read_input_tokens ?? 0) + (message.usage.input_tokens ?? 0),
647
- completionTokens: message.usage.output_tokens ?? 0
659
+ inputTokens: (message.usage.cache_creation_input_tokens ?? 0) + (message.usage.cache_read_input_tokens ?? 0) + (message.usage.input_tokens ?? 0),
660
+ outputTokens: message.usage.output_tokens ?? 0,
661
+ 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)
648
662
  };
649
663
  }
650
664
  finishReason = mapClaudeCodeFinishReason(message.subtype);
@@ -662,7 +676,7 @@ var ClaudeCodeLanguageModel = class {
662
676
  options.abortSignal.removeEventListener("abort", abortListener);
663
677
  }
664
678
  }
665
- if (options.mode?.type === "object-json" && text) {
679
+ if (options.responseFormat?.type === "json" && text) {
666
680
  const extracted = extractJson(text);
667
681
  const validation = this.validateJsonExtraction(text, extracted);
668
682
  if (!validation.valid && validation.warning) {
@@ -671,14 +685,10 @@ var ClaudeCodeLanguageModel = class {
671
685
  text = extracted;
672
686
  }
673
687
  return {
674
- text: text || void 0,
688
+ content: [{ type: "text", text }],
675
689
  usage,
676
690
  finishReason,
677
- rawCall: {
678
- rawPrompt: messagesPrompt,
679
- rawSettings: queryOptions
680
- },
681
- warnings: warnings.length > 0 ? warnings : void 0,
691
+ warnings,
682
692
  response: {
683
693
  id: (0, import_provider_utils.generateId)(),
684
694
  timestamp: /* @__PURE__ */ new Date(),
@@ -698,7 +708,12 @@ var ClaudeCodeLanguageModel = class {
698
708
  };
699
709
  }
700
710
  async doStream(options) {
701
- const { messagesPrompt, warnings: messageWarnings } = convertToClaudeCodeMessages(options.prompt, options.mode);
711
+ const mode = options.responseFormat?.type === "json" ? { type: "object-json" } : { type: "regular" };
712
+ const { messagesPrompt, warnings: messageWarnings } = convertToClaudeCodeMessages(
713
+ options.prompt,
714
+ mode,
715
+ options.responseFormat?.type === "json" ? options.responseFormat.schema : void 0
716
+ );
702
717
  const abortController = new AbortController();
703
718
  let abortListener;
704
719
  if (options.abortSignal) {
@@ -718,21 +733,31 @@ var ClaudeCodeLanguageModel = class {
718
733
  const stream = new ReadableStream({
719
734
  start: async (controller) => {
720
735
  try {
736
+ controller.enqueue({ type: "stream-start", warnings });
721
737
  const response = (0, import_claude_code.query)({
722
738
  prompt: messagesPrompt,
723
739
  options: queryOptions
724
740
  });
725
- let usage = { promptTokens: 0, completionTokens: 0 };
741
+ let usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
726
742
  let accumulatedText = "";
743
+ let textPartId;
727
744
  for await (const message of response) {
728
745
  if (message.type === "assistant") {
729
746
  const text = message.message.content.map((c) => c.type === "text" ? c.text : "").join("");
730
747
  if (text) {
731
748
  accumulatedText += text;
732
- if (options.mode?.type !== "object-json") {
749
+ if (options.responseFormat?.type !== "json") {
750
+ if (!textPartId) {
751
+ textPartId = (0, import_provider_utils.generateId)();
752
+ controller.enqueue({
753
+ type: "text-start",
754
+ id: textPartId
755
+ });
756
+ }
733
757
  controller.enqueue({
734
758
  type: "text-delta",
735
- textDelta: text
759
+ id: textPartId,
760
+ delta: text
736
761
  });
737
762
  }
738
763
  }
@@ -741,18 +766,34 @@ var ClaudeCodeLanguageModel = class {
741
766
  if ("usage" in message) {
742
767
  rawUsage = message.usage;
743
768
  usage = {
744
- promptTokens: (message.usage.cache_creation_input_tokens ?? 0) + (message.usage.cache_read_input_tokens ?? 0) + (message.usage.input_tokens ?? 0),
745
- completionTokens: message.usage.output_tokens ?? 0
769
+ inputTokens: (message.usage.cache_creation_input_tokens ?? 0) + (message.usage.cache_read_input_tokens ?? 0) + (message.usage.input_tokens ?? 0),
770
+ outputTokens: message.usage.output_tokens ?? 0,
771
+ 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)
746
772
  };
747
773
  }
748
774
  const finishReason = mapClaudeCodeFinishReason(message.subtype);
749
775
  this.setSessionId(message.session_id);
750
- if (options.mode?.type === "object-json" && accumulatedText) {
776
+ if (options.responseFormat?.type === "json" && accumulatedText) {
751
777
  const extractedJson = extractJson(accumulatedText);
752
778
  this.validateJsonExtraction(accumulatedText, extractedJson);
779
+ const jsonTextId = (0, import_provider_utils.generateId)();
780
+ controller.enqueue({
781
+ type: "text-start",
782
+ id: jsonTextId
783
+ });
753
784
  controller.enqueue({
754
785
  type: "text-delta",
755
- textDelta: extractedJson
786
+ id: jsonTextId,
787
+ delta: extractedJson
788
+ });
789
+ controller.enqueue({
790
+ type: "text-end",
791
+ id: jsonTextId
792
+ });
793
+ } else if (textPartId) {
794
+ controller.enqueue({
795
+ type: "text-end",
796
+ id: textPartId
756
797
  });
757
798
  }
758
799
  controller.enqueue({
@@ -805,11 +846,6 @@ var ClaudeCodeLanguageModel = class {
805
846
  });
806
847
  return {
807
848
  stream,
808
- rawCall: {
809
- rawPrompt: messagesPrompt,
810
- rawSettings: queryOptions
811
- },
812
- warnings: warnings.length > 0 ? warnings : void 0,
813
849
  request: {
814
850
  body: messagesPrompt
815
851
  }
@@ -860,6 +896,12 @@ function createClaudeCode(options = {}) {
860
896
  modelType: "textEmbeddingModel"
861
897
  });
862
898
  };
899
+ provider.imageModel = (modelId) => {
900
+ throw new import_provider3.NoSuchModelError({
901
+ modelId,
902
+ modelType: "imageModel"
903
+ });
904
+ };
863
905
  return provider;
864
906
  }
865
907
  var claudeCode = createClaudeCode();