ai-sdk-provider-claude-code 2.2.3 → 2.3.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 2.x uses the Claude Agent SDK with native structured outputs support (v2.2.0+). For AI SDK v6 beta support, use the `beta` tag. For AI SDK v4 support, use the `ai-sdk-v4` 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,13 +17,22 @@
17
17
 
18
18
  | Provider Version | AI SDK Version | Underlying SDK | NPM Tag | Status | Branch |
19
19
  | ---------------- | -------------- | ------------------------------------ | -------------------- | ------ | --------------------------------------------------------------------------------------- |
20
+ | 3.x.x-beta | v6 (beta) | `@anthropic-ai/claude-agent-sdk` | `beta` | Beta | [`ai-sdk-v6`](https://github.com/ben-vargas/ai-sdk-provider-claude-code/tree/ai-sdk-v6) |
20
21
  | 2.x.x | v5 | `@anthropic-ai/claude-agent-sdk` | `latest` | Stable | `main` |
21
22
  | 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
23
  | 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) |
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 beta (early adopters):**
28
+
29
+ ```bash
30
+ npm install ai-sdk-provider-claude-code@beta ai@beta
31
+ ```
32
+
33
+ > ⚠️ **Note:** AI SDK v6 is in beta. The `ai-sdk-v6` branch and `beta` npm tag track upstream changes. When v6 goes stable, this will be merged to `main` and released as 3.0.0.
34
+
35
+ **For AI SDK v5 with Claude Agent SDK (recommended for production):**
27
36
 
28
37
  ```bash
29
38
  npm install ai-sdk-provider-claude-code ai
@@ -308,6 +317,28 @@ console.log(result.object); // Guaranteed to match schema
308
317
  - 🔧 Tool management (MCP servers, permissions)
309
318
  - 🧩 Callbacks (hooks, canUseTool)
310
319
 
320
+ ## Agent SDK Options (Advanced)
321
+
322
+ This provider now exposes additional Agent SDK options directly (e.g. `betas`, `sandbox`,
323
+ `plugins`, `resumeSessionAt`, `enableFileCheckpointing`, `maxBudgetUsd`, `tools`,
324
+ `allowDangerouslySkipPermissions`).
325
+
326
+ For newer SDK options, use the `sdkOptions` escape hatch. It **overrides** explicit settings,
327
+ but provider-managed fields are ignored (`model`, `abortController`, `prompt`, `outputFormat`).
328
+ If you set `sdkOptions.resume`, it also drives the streaming prompt `session_id` so the SDK
329
+ and prompt target the same session.
330
+
331
+ ```ts
332
+ const model = claudeCode('sonnet', {
333
+ betas: ['context-1m-2025-08-07'],
334
+ sandbox: { enabled: true },
335
+ sdkOptions: {
336
+ maxBudgetUsd: 1,
337
+ resume: 'session-abc',
338
+ },
339
+ });
340
+ ```
341
+
311
342
  ## Image Inputs (Streaming Only)
312
343
 
313
344
  - Enable streaming input (`streamingInput: 'always'` or provide `canUseTool`) before sending images.
package/dist/index.cjs CHANGED
@@ -450,6 +450,27 @@ var claudeCodeSettingsSchema = import_zod.z.object({
450
450
  resume: import_zod.z.string().optional(),
451
451
  allowedTools: import_zod.z.array(import_zod.z.string()).optional(),
452
452
  disallowedTools: import_zod.z.array(import_zod.z.string()).optional(),
453
+ betas: import_zod.z.array(import_zod.z.string()).optional(),
454
+ allowDangerouslySkipPermissions: import_zod.z.boolean().optional(),
455
+ enableFileCheckpointing: import_zod.z.boolean().optional(),
456
+ maxBudgetUsd: import_zod.z.number().min(0).optional(),
457
+ plugins: import_zod.z.array(
458
+ import_zod.z.object({
459
+ type: import_zod.z.string(),
460
+ path: import_zod.z.string()
461
+ }).passthrough()
462
+ ).optional(),
463
+ resumeSessionAt: import_zod.z.string().optional(),
464
+ sandbox: import_zod.z.any().refine((val) => val === void 0 || typeof val === "object", {
465
+ message: "sandbox must be an object"
466
+ }).optional(),
467
+ tools: import_zod.z.union([
468
+ import_zod.z.array(import_zod.z.string()),
469
+ import_zod.z.object({
470
+ type: import_zod.z.literal("preset"),
471
+ preset: import_zod.z.literal("claude_code")
472
+ })
473
+ ]).optional(),
453
474
  settingSources: import_zod.z.array(import_zod.z.enum(["user", "project", "local"])).optional(),
454
475
  streamingInput: import_zod.z.enum(["auto", "always", "off"]).optional(),
455
476
  // Hooks and tool-permission callback (permissive validation of shapes)
@@ -515,7 +536,8 @@ var claudeCodeSettingsSchema = import_zod.z.object({
515
536
  message: "stderr must be a function"
516
537
  }).optional(),
517
538
  strictMcpConfig: import_zod.z.boolean().optional(),
518
- extraArgs: import_zod.z.record(import_zod.z.string(), import_zod.z.union([import_zod.z.string(), import_zod.z.null()])).optional()
539
+ extraArgs: import_zod.z.record(import_zod.z.string(), import_zod.z.union([import_zod.z.string(), import_zod.z.null()])).optional(),
540
+ sdkOptions: import_zod.z.record(import_zod.z.string(), import_zod.z.any()).optional()
519
541
  }).strict();
520
542
  function validateModelId(modelId) {
521
543
  const knownModels = ["opus", "sonnet", "haiku"];
@@ -592,7 +614,9 @@ function validateSessionId(sessionId) {
592
614
 
593
615
  // src/logger.ts
594
616
  var defaultLogger = {
617
+ // eslint-disable-next-line no-console
595
618
  debug: (message) => console.debug(`[DEBUG] ${message}`),
619
+ // eslint-disable-next-line no-console
596
620
  info: (message) => console.info(`[INFO] ${message}`),
597
621
  warn: (message) => console.warn(`[WARN] ${message}`),
598
622
  error: (message) => console.error(`[ERROR] ${message}`)
@@ -674,6 +698,7 @@ function isAbortError(err) {
674
698
  return false;
675
699
  }
676
700
  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).";
701
+ var SDK_OPTIONS_BLOCKLIST = /* @__PURE__ */ new Set(["model", "abortController", "prompt", "outputFormat"]);
677
702
  function toAsyncIterablePrompt(messagesPrompt, outputStreamEnded, sessionId, contentParts) {
678
703
  const content = contentParts && contentParts.length > 0 ? contentParts : [{ type: "text", text: messagesPrompt }];
679
704
  const msg = {
@@ -742,6 +767,25 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
742
767
  const mapped = modelMap[this.modelId];
743
768
  return mapped ?? this.modelId;
744
769
  }
770
+ getSanitizedSdkOptions() {
771
+ if (!this.settings.sdkOptions || typeof this.settings.sdkOptions !== "object") {
772
+ return void 0;
773
+ }
774
+ const sanitized = { ...this.settings.sdkOptions };
775
+ const blockedKeys = Array.from(SDK_OPTIONS_BLOCKLIST).filter((key) => key in sanitized);
776
+ if (blockedKeys.length > 0) {
777
+ this.logger.warn(
778
+ `[claude-code] sdkOptions includes provider-managed fields (${blockedKeys.join(
779
+ ", "
780
+ )}); these will be ignored.`
781
+ );
782
+ blockedKeys.forEach((key) => delete sanitized[key]);
783
+ }
784
+ return sanitized;
785
+ }
786
+ getEffectiveResume(sdkOptions) {
787
+ return sdkOptions?.resume ?? this.settings.resume ?? this.sessionId;
788
+ }
745
789
  extractToolUses(content) {
746
790
  if (!Array.isArray(content)) {
747
791
  return [];
@@ -875,11 +919,11 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
875
919
  }
876
920
  return warnings;
877
921
  }
878
- createQueryOptions(abortController, responseFormat) {
922
+ createQueryOptions(abortController, responseFormat, stderrCollector, sdkOptions, effectiveResume) {
879
923
  const opts = {
880
924
  model: this.getModel(),
881
925
  abortController,
882
- resume: this.settings.resume ?? this.sessionId,
926
+ resume: effectiveResume ?? this.settings.resume ?? this.sessionId,
883
927
  pathToClaudeCodeExecutable: this.settings.pathToClaudeCodeExecutable,
884
928
  maxTurns: this.settings.maxTurns,
885
929
  maxThinkingTokens: this.settings.maxThinkingTokens,
@@ -891,6 +935,14 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
891
935
  continue: this.settings.continue,
892
936
  allowedTools: this.settings.allowedTools,
893
937
  disallowedTools: this.settings.disallowedTools,
938
+ betas: this.settings.betas,
939
+ allowDangerouslySkipPermissions: this.settings.allowDangerouslySkipPermissions,
940
+ enableFileCheckpointing: this.settings.enableFileCheckpointing,
941
+ maxBudgetUsd: this.settings.maxBudgetUsd,
942
+ plugins: this.settings.plugins,
943
+ resumeSessionAt: this.settings.resumeSessionAt,
944
+ sandbox: this.settings.sandbox,
945
+ tools: this.settings.tools,
894
946
  mcpServers: this.settings.mcpServers,
895
947
  canUseTool: this.settings.canUseTool
896
948
  };
@@ -929,9 +981,6 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
929
981
  if (this.settings.forkSession !== void 0) {
930
982
  opts.forkSession = this.settings.forkSession;
931
983
  }
932
- if (this.settings.stderr !== void 0) {
933
- opts.stderr = this.settings.stderr;
934
- }
935
984
  if (this.settings.strictMcpConfig !== void 0) {
936
985
  opts.strictMcpConfig = this.settings.strictMcpConfig;
937
986
  }
@@ -941,8 +990,24 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
941
990
  if (this.settings.hooks) {
942
991
  opts.hooks = this.settings.hooks;
943
992
  }
944
- if (this.settings.env !== void 0) {
945
- opts.env = { ...process.env, ...this.settings.env };
993
+ const sdkOverrides = sdkOptions ? sdkOptions : void 0;
994
+ const sdkEnv = sdkOverrides && typeof sdkOverrides.env === "object" && sdkOverrides.env !== null ? sdkOverrides.env : void 0;
995
+ const sdkStderr = sdkOverrides && typeof sdkOverrides.stderr === "function" ? sdkOverrides.stderr : void 0;
996
+ if (sdkOverrides) {
997
+ const rest = { ...sdkOverrides };
998
+ delete rest.env;
999
+ delete rest.stderr;
1000
+ Object.assign(opts, rest);
1001
+ }
1002
+ const userStderrCallback = sdkStderr ?? this.settings.stderr;
1003
+ if (stderrCollector || userStderrCallback) {
1004
+ opts.stderr = (data) => {
1005
+ if (stderrCollector) stderrCollector(data);
1006
+ if (userStderrCallback) userStderrCallback(data);
1007
+ };
1008
+ }
1009
+ if (this.settings.env !== void 0 || sdkEnv !== void 0) {
1010
+ opts.env = { ...process.env, ...this.settings.env, ...sdkEnv };
946
1011
  }
947
1012
  if (responseFormat?.type === "json" && responseFormat.schema) {
948
1013
  opts.outputFormat = {
@@ -952,7 +1017,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
952
1017
  }
953
1018
  return opts;
954
1019
  }
955
- handleClaudeCodeError(error, messagesPrompt) {
1020
+ handleClaudeCodeError(error, messagesPrompt, collectedStderr) {
956
1021
  if (isAbortError(error)) {
957
1022
  throw error;
958
1023
  }
@@ -988,11 +1053,13 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
988
1053
  });
989
1054
  }
990
1055
  const isRetryable = errorCode === "ENOENT" || errorCode === "ECONNREFUSED" || errorCode === "ETIMEDOUT" || errorCode === "ECONNRESET";
1056
+ const stderrFromError = isErrorWithCode(error) && typeof error.stderr === "string" ? error.stderr : void 0;
1057
+ const stderr = stderrFromError || collectedStderr || void 0;
991
1058
  return createAPICallError({
992
1059
  message: isErrorWithMessage(error) && error.message ? error.message : "Claude Code SDK error",
993
1060
  code: errorCode || void 0,
994
1061
  exitCode,
995
- stderr: isErrorWithCode(error) && typeof error.stderr === "string" ? error.stderr : void 0,
1062
+ stderr,
996
1063
  promptExcerpt: messagesPrompt.substring(0, 200),
997
1064
  isRetryable
998
1065
  });
@@ -1024,7 +1091,19 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1024
1091
  abortListener = () => abortController.abort(options.abortSignal?.reason);
1025
1092
  options.abortSignal.addEventListener("abort", abortListener, { once: true });
1026
1093
  }
1027
- const queryOptions = this.createQueryOptions(abortController, options.responseFormat);
1094
+ let collectedStderr = "";
1095
+ const stderrCollector = (data) => {
1096
+ collectedStderr += data;
1097
+ };
1098
+ const sdkOptions = this.getSanitizedSdkOptions();
1099
+ const effectiveResume = this.getEffectiveResume(sdkOptions);
1100
+ const queryOptions = this.createQueryOptions(
1101
+ abortController,
1102
+ options.responseFormat,
1103
+ stderrCollector,
1104
+ sdkOptions,
1105
+ effectiveResume
1106
+ );
1028
1107
  let text = "";
1029
1108
  let structuredOutput;
1030
1109
  let usage = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
@@ -1046,7 +1125,9 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1046
1125
  });
1047
1126
  }
1048
1127
  const modeSetting = this.settings.streamingInput ?? "auto";
1049
- const wantsStreamInput = modeSetting === "always" || modeSetting === "auto" && !!this.settings.canUseTool;
1128
+ const effectiveCanUseTool = sdkOptions?.canUseTool ?? this.settings.canUseTool;
1129
+ const effectivePermissionPromptToolName = sdkOptions?.permissionPromptToolName ?? this.settings.permissionPromptToolName;
1130
+ const wantsStreamInput = modeSetting === "always" || modeSetting === "auto" && !!effectiveCanUseTool;
1050
1131
  if (!wantsStreamInput && hasImageParts) {
1051
1132
  warnings.push({
1052
1133
  type: "other",
@@ -1059,7 +1140,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1059
1140
  done = () => resolve(void 0);
1060
1141
  });
1061
1142
  try {
1062
- if (this.settings.canUseTool && this.settings.permissionPromptToolName) {
1143
+ if (effectiveCanUseTool && effectivePermissionPromptToolName) {
1063
1144
  throw new Error(
1064
1145
  "canUseTool requires streamingInput mode ('auto' or 'always') and cannot be used with permissionPromptToolName (SDK constraint). Set streamingInput: 'auto' (or 'always') and remove permissionPromptToolName, or remove canUseTool."
1065
1146
  );
@@ -1067,11 +1148,11 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1067
1148
  const sdkPrompt = wantsStreamInput ? toAsyncIterablePrompt(
1068
1149
  messagesPrompt,
1069
1150
  outputStreamEnded,
1070
- this.settings.resume ?? this.sessionId,
1151
+ effectiveResume,
1071
1152
  streamingContentParts
1072
1153
  ) : messagesPrompt;
1073
1154
  this.logger.debug(
1074
- `[claude-code] Executing query with streamingInput: ${wantsStreamInput}, session: ${this.settings.resume ?? this.sessionId ?? "new"}`
1155
+ `[claude-code] Executing query with streamingInput: ${wantsStreamInput}, session: ${effectiveResume ?? "new"}`
1075
1156
  );
1076
1157
  const response = (0, import_claude_agent_sdk.query)({
1077
1158
  prompt: sdkPrompt,
@@ -1136,7 +1217,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1136
1217
  message: CLAUDE_CODE_TRUNCATION_WARNING
1137
1218
  });
1138
1219
  } else {
1139
- throw this.handleClaudeCodeError(error, messagesPrompt);
1220
+ throw this.handleClaudeCodeError(error, messagesPrompt, collectedStderr);
1140
1221
  }
1141
1222
  } finally {
1142
1223
  if (options.abortSignal && abortListener) {
@@ -1188,7 +1269,19 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1188
1269
  abortListener = () => abortController.abort(options.abortSignal?.reason);
1189
1270
  options.abortSignal.addEventListener("abort", abortListener, { once: true });
1190
1271
  }
1191
- const queryOptions = this.createQueryOptions(abortController, options.responseFormat);
1272
+ let collectedStderr = "";
1273
+ const stderrCollector = (data) => {
1274
+ collectedStderr += data;
1275
+ };
1276
+ const sdkOptions = this.getSanitizedSdkOptions();
1277
+ const effectiveResume = this.getEffectiveResume(sdkOptions);
1278
+ const queryOptions = this.createQueryOptions(
1279
+ abortController,
1280
+ options.responseFormat,
1281
+ stderrCollector,
1282
+ sdkOptions,
1283
+ effectiveResume
1284
+ );
1192
1285
  if (queryOptions.includePartialMessages === void 0) {
1193
1286
  queryOptions.includePartialMessages = true;
1194
1287
  }
@@ -1205,7 +1298,9 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1205
1298
  });
1206
1299
  }
1207
1300
  const modeSetting = this.settings.streamingInput ?? "auto";
1208
- const wantsStreamInput = modeSetting === "always" || modeSetting === "auto" && !!this.settings.canUseTool;
1301
+ const effectiveCanUseTool = sdkOptions?.canUseTool ?? this.settings.canUseTool;
1302
+ const effectivePermissionPromptToolName = sdkOptions?.permissionPromptToolName ?? this.settings.permissionPromptToolName;
1303
+ const wantsStreamInput = modeSetting === "always" || modeSetting === "auto" && !!effectiveCanUseTool;
1209
1304
  if (!wantsStreamInput && hasImageParts) {
1210
1305
  warnings.push({
1211
1306
  type: "other",
@@ -1267,7 +1362,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1267
1362
  let hasReceivedStreamEvents = false;
1268
1363
  try {
1269
1364
  controller.enqueue({ type: "stream-start", warnings });
1270
- if (this.settings.canUseTool && this.settings.permissionPromptToolName) {
1365
+ if (effectiveCanUseTool && effectivePermissionPromptToolName) {
1271
1366
  throw new Error(
1272
1367
  "canUseTool requires streamingInput mode ('auto' or 'always') and cannot be used with permissionPromptToolName (SDK constraint). Set streamingInput: 'auto' (or 'always') and remove permissionPromptToolName, or remove canUseTool."
1273
1368
  );
@@ -1275,11 +1370,11 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1275
1370
  const sdkPrompt = wantsStreamInput ? toAsyncIterablePrompt(
1276
1371
  messagesPrompt,
1277
1372
  outputStreamEnded,
1278
- this.settings.resume ?? this.sessionId,
1373
+ effectiveResume,
1279
1374
  streamingContentParts
1280
1375
  ) : messagesPrompt;
1281
1376
  this.logger.debug(
1282
- `[claude-code] Starting stream query with streamingInput: ${wantsStreamInput}, session: ${this.settings.resume ?? this.sessionId ?? "new"}`
1377
+ `[claude-code] Starting stream query with streamingInput: ${wantsStreamInput}, session: ${effectiveResume ?? "new"}`
1283
1378
  );
1284
1379
  const response = (0, import_claude_agent_sdk.query)({
1285
1380
  prompt: sdkPrompt,
@@ -1715,7 +1810,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1715
1810
  if (isAbortError(error)) {
1716
1811
  errorToEmit = options.abortSignal?.aborted ? options.abortSignal.reason : error;
1717
1812
  } else {
1718
- errorToEmit = this.handleClaudeCodeError(error, messagesPrompt);
1813
+ errorToEmit = this.handleClaudeCodeError(error, messagesPrompt, collectedStderr);
1719
1814
  }
1720
1815
  controller.enqueue({
1721
1816
  type: "error",