ai-sdk-provider-claude-code 3.0.1 → 3.1.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
@@ -269,6 +269,28 @@ console.log(result.object); // Guaranteed to match schema
269
269
  - 🔧 Tool management (MCP servers, permissions)
270
270
  - 🧩 Callbacks (hooks, canUseTool)
271
271
 
272
+ ## Agent SDK Options (Advanced)
273
+
274
+ This provider now exposes additional Agent SDK options directly (e.g. `betas`, `sandbox`,
275
+ `plugins`, `resumeSessionAt`, `enableFileCheckpointing`, `maxBudgetUsd`, `tools`,
276
+ `allowDangerouslySkipPermissions`).
277
+
278
+ For newer SDK options, use the `sdkOptions` escape hatch. It **overrides** explicit settings,
279
+ but provider-managed fields are ignored (`model`, `abortController`, `prompt`, `outputFormat`).
280
+ If you set `sdkOptions.resume`, it also drives the streaming prompt `session_id` so the SDK
281
+ and prompt target the same session.
282
+
283
+ ```ts
284
+ const model = claudeCode('sonnet', {
285
+ betas: ['context-1m-2025-08-07'],
286
+ sandbox: { enabled: true },
287
+ sdkOptions: {
288
+ maxBudgetUsd: 1,
289
+ resume: 'session-abc',
290
+ },
291
+ });
292
+ ```
293
+
272
294
  ## Image Inputs (Streaming Only)
273
295
 
274
296
  - Enable streaming input (`streamingInput: 'always'` or provide `canUseTool`) before sending images.
package/dist/index.cjs CHANGED
@@ -467,6 +467,27 @@ var claudeCodeSettingsSchema = import_zod.z.object({
467
467
  resume: import_zod.z.string().optional(),
468
468
  allowedTools: import_zod.z.array(import_zod.z.string()).optional(),
469
469
  disallowedTools: import_zod.z.array(import_zod.z.string()).optional(),
470
+ betas: import_zod.z.array(import_zod.z.string()).optional(),
471
+ allowDangerouslySkipPermissions: import_zod.z.boolean().optional(),
472
+ enableFileCheckpointing: import_zod.z.boolean().optional(),
473
+ maxBudgetUsd: import_zod.z.number().min(0).optional(),
474
+ plugins: import_zod.z.array(
475
+ import_zod.z.object({
476
+ type: import_zod.z.string(),
477
+ path: import_zod.z.string()
478
+ }).passthrough()
479
+ ).optional(),
480
+ resumeSessionAt: import_zod.z.string().optional(),
481
+ sandbox: import_zod.z.any().refine((val) => val === void 0 || typeof val === "object", {
482
+ message: "sandbox must be an object"
483
+ }).optional(),
484
+ tools: import_zod.z.union([
485
+ import_zod.z.array(import_zod.z.string()),
486
+ import_zod.z.object({
487
+ type: import_zod.z.literal("preset"),
488
+ preset: import_zod.z.literal("claude_code")
489
+ })
490
+ ]).optional(),
470
491
  settingSources: import_zod.z.array(import_zod.z.enum(["user", "project", "local"])).optional(),
471
492
  streamingInput: import_zod.z.enum(["auto", "always", "off"]).optional(),
472
493
  // Hooks and tool-permission callback (permissive validation of shapes)
@@ -532,7 +553,8 @@ var claudeCodeSettingsSchema = import_zod.z.object({
532
553
  message: "stderr must be a function"
533
554
  }).optional(),
534
555
  strictMcpConfig: import_zod.z.boolean().optional(),
535
- extraArgs: import_zod.z.record(import_zod.z.string(), import_zod.z.union([import_zod.z.string(), import_zod.z.null()])).optional()
556
+ extraArgs: import_zod.z.record(import_zod.z.string(), import_zod.z.union([import_zod.z.string(), import_zod.z.null()])).optional(),
557
+ sdkOptions: import_zod.z.record(import_zod.z.string(), import_zod.z.any()).optional()
536
558
  }).strict();
537
559
  function validateModelId(modelId) {
538
560
  const knownModels = ["opus", "sonnet", "haiku"];
@@ -614,7 +636,9 @@ function validateSessionId(sessionId) {
614
636
 
615
637
  // src/logger.ts
616
638
  var defaultLogger = {
639
+ // eslint-disable-next-line no-console
617
640
  debug: (message) => console.debug(`[DEBUG] ${message}`),
641
+ // eslint-disable-next-line no-console
618
642
  info: (message) => console.info(`[INFO] ${message}`),
619
643
  warn: (message) => console.warn(`[WARN] ${message}`),
620
644
  error: (message) => console.error(`[ERROR] ${message}`)
@@ -696,6 +720,7 @@ function isAbortError(err) {
696
720
  return false;
697
721
  }
698
722
  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).";
723
+ var SDK_OPTIONS_BLOCKLIST = /* @__PURE__ */ new Set(["model", "abortController", "prompt", "outputFormat"]);
699
724
  function createEmptyUsage() {
700
725
  return {
701
726
  inputTokens: {
@@ -800,6 +825,25 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
800
825
  const mapped = modelMap[this.modelId];
801
826
  return mapped ?? this.modelId;
802
827
  }
828
+ getSanitizedSdkOptions() {
829
+ if (!this.settings.sdkOptions || typeof this.settings.sdkOptions !== "object") {
830
+ return void 0;
831
+ }
832
+ const sanitized = { ...this.settings.sdkOptions };
833
+ const blockedKeys = Array.from(SDK_OPTIONS_BLOCKLIST).filter((key) => key in sanitized);
834
+ if (blockedKeys.length > 0) {
835
+ this.logger.warn(
836
+ `[claude-code] sdkOptions includes provider-managed fields (${blockedKeys.join(
837
+ ", "
838
+ )}); these will be ignored.`
839
+ );
840
+ blockedKeys.forEach((key) => delete sanitized[key]);
841
+ }
842
+ return sanitized;
843
+ }
844
+ getEffectiveResume(sdkOptions) {
845
+ return sdkOptions?.resume ?? this.settings.resume ?? this.sessionId;
846
+ }
803
847
  extractToolUses(content) {
804
848
  if (!Array.isArray(content)) {
805
849
  return [];
@@ -933,11 +977,11 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
933
977
  }
934
978
  return warnings;
935
979
  }
936
- createQueryOptions(abortController, responseFormat) {
980
+ createQueryOptions(abortController, responseFormat, stderrCollector, sdkOptions, effectiveResume) {
937
981
  const opts = {
938
982
  model: this.getModel(),
939
983
  abortController,
940
- resume: this.settings.resume ?? this.sessionId,
984
+ resume: effectiveResume ?? this.settings.resume ?? this.sessionId,
941
985
  pathToClaudeCodeExecutable: this.settings.pathToClaudeCodeExecutable,
942
986
  maxTurns: this.settings.maxTurns,
943
987
  maxThinkingTokens: this.settings.maxThinkingTokens,
@@ -949,6 +993,14 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
949
993
  continue: this.settings.continue,
950
994
  allowedTools: this.settings.allowedTools,
951
995
  disallowedTools: this.settings.disallowedTools,
996
+ betas: this.settings.betas,
997
+ allowDangerouslySkipPermissions: this.settings.allowDangerouslySkipPermissions,
998
+ enableFileCheckpointing: this.settings.enableFileCheckpointing,
999
+ maxBudgetUsd: this.settings.maxBudgetUsd,
1000
+ plugins: this.settings.plugins,
1001
+ resumeSessionAt: this.settings.resumeSessionAt,
1002
+ sandbox: this.settings.sandbox,
1003
+ tools: this.settings.tools,
952
1004
  mcpServers: this.settings.mcpServers,
953
1005
  canUseTool: this.settings.canUseTool
954
1006
  };
@@ -987,9 +1039,6 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
987
1039
  if (this.settings.forkSession !== void 0) {
988
1040
  opts.forkSession = this.settings.forkSession;
989
1041
  }
990
- if (this.settings.stderr !== void 0) {
991
- opts.stderr = this.settings.stderr;
992
- }
993
1042
  if (this.settings.strictMcpConfig !== void 0) {
994
1043
  opts.strictMcpConfig = this.settings.strictMcpConfig;
995
1044
  }
@@ -999,8 +1048,24 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
999
1048
  if (this.settings.hooks) {
1000
1049
  opts.hooks = this.settings.hooks;
1001
1050
  }
1002
- if (this.settings.env !== void 0) {
1003
- opts.env = { ...process.env, ...this.settings.env };
1051
+ const sdkOverrides = sdkOptions ? sdkOptions : void 0;
1052
+ const sdkEnv = sdkOverrides && typeof sdkOverrides.env === "object" && sdkOverrides.env !== null ? sdkOverrides.env : void 0;
1053
+ const sdkStderr = sdkOverrides && typeof sdkOverrides.stderr === "function" ? sdkOverrides.stderr : void 0;
1054
+ if (sdkOverrides) {
1055
+ const rest = { ...sdkOverrides };
1056
+ delete rest.env;
1057
+ delete rest.stderr;
1058
+ Object.assign(opts, rest);
1059
+ }
1060
+ const userStderrCallback = sdkStderr ?? this.settings.stderr;
1061
+ if (stderrCollector || userStderrCallback) {
1062
+ opts.stderr = (data) => {
1063
+ if (stderrCollector) stderrCollector(data);
1064
+ if (userStderrCallback) userStderrCallback(data);
1065
+ };
1066
+ }
1067
+ if (this.settings.env !== void 0 || sdkEnv !== void 0) {
1068
+ opts.env = { ...process.env, ...this.settings.env, ...sdkEnv };
1004
1069
  }
1005
1070
  if (responseFormat?.type === "json" && responseFormat.schema) {
1006
1071
  opts.outputFormat = {
@@ -1010,7 +1075,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1010
1075
  }
1011
1076
  return opts;
1012
1077
  }
1013
- handleClaudeCodeError(error, messagesPrompt) {
1078
+ handleClaudeCodeError(error, messagesPrompt, collectedStderr) {
1014
1079
  if (isAbortError(error)) {
1015
1080
  throw error;
1016
1081
  }
@@ -1026,7 +1091,10 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1026
1091
  "unauthorized",
1027
1092
  "auth failed",
1028
1093
  "please login",
1029
- "claude login"
1094
+ "claude login",
1095
+ "/login",
1096
+ // CLI returns "Please run /login"
1097
+ "invalid api key"
1030
1098
  ];
1031
1099
  const errorMessage = isErrorWithMessage(error) && error.message ? error.message.toLowerCase() : "";
1032
1100
  const exitCode = isErrorWithCode(error) && typeof error.exitCode === "number" ? error.exitCode : void 0;
@@ -1046,11 +1114,13 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1046
1114
  });
1047
1115
  }
1048
1116
  const isRetryable = errorCode === "ENOENT" || errorCode === "ECONNREFUSED" || errorCode === "ETIMEDOUT" || errorCode === "ECONNRESET";
1117
+ const stderrFromError = isErrorWithCode(error) && typeof error.stderr === "string" ? error.stderr : void 0;
1118
+ const stderr = stderrFromError || collectedStderr || void 0;
1049
1119
  return createAPICallError({
1050
1120
  message: isErrorWithMessage(error) && error.message ? error.message : "Claude Code SDK error",
1051
1121
  code: errorCode || void 0,
1052
1122
  exitCode,
1053
- stderr: isErrorWithCode(error) && typeof error.stderr === "string" ? error.stderr : void 0,
1123
+ stderr,
1054
1124
  promptExcerpt: messagesPrompt.substring(0, 200),
1055
1125
  isRetryable
1056
1126
  });
@@ -1082,7 +1152,19 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1082
1152
  abortListener = () => abortController.abort(options.abortSignal?.reason);
1083
1153
  options.abortSignal.addEventListener("abort", abortListener, { once: true });
1084
1154
  }
1085
- const queryOptions = this.createQueryOptions(abortController, options.responseFormat);
1155
+ let collectedStderr = "";
1156
+ const stderrCollector = (data) => {
1157
+ collectedStderr += data;
1158
+ };
1159
+ const sdkOptions = this.getSanitizedSdkOptions();
1160
+ const effectiveResume = this.getEffectiveResume(sdkOptions);
1161
+ const queryOptions = this.createQueryOptions(
1162
+ abortController,
1163
+ options.responseFormat,
1164
+ stderrCollector,
1165
+ sdkOptions,
1166
+ effectiveResume
1167
+ );
1086
1168
  let text = "";
1087
1169
  let structuredOutput;
1088
1170
  let usage = createEmptyUsage();
@@ -1100,7 +1182,9 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1100
1182
  });
1101
1183
  }
1102
1184
  const modeSetting = this.settings.streamingInput ?? "auto";
1103
- const wantsStreamInput = modeSetting === "always" || modeSetting === "auto" && !!this.settings.canUseTool;
1185
+ const effectiveCanUseTool = sdkOptions?.canUseTool ?? this.settings.canUseTool;
1186
+ const effectivePermissionPromptToolName = sdkOptions?.permissionPromptToolName ?? this.settings.permissionPromptToolName;
1187
+ const wantsStreamInput = modeSetting === "always" || modeSetting === "auto" && !!effectiveCanUseTool;
1104
1188
  if (!wantsStreamInput && hasImageParts) {
1105
1189
  warnings.push({
1106
1190
  type: "other",
@@ -1113,7 +1197,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1113
1197
  done = () => resolve(void 0);
1114
1198
  });
1115
1199
  try {
1116
- if (this.settings.canUseTool && this.settings.permissionPromptToolName) {
1200
+ if (effectiveCanUseTool && effectivePermissionPromptToolName) {
1117
1201
  throw new Error(
1118
1202
  "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."
1119
1203
  );
@@ -1121,11 +1205,11 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1121
1205
  const sdkPrompt = wantsStreamInput ? toAsyncIterablePrompt(
1122
1206
  messagesPrompt,
1123
1207
  outputStreamEnded,
1124
- this.settings.resume ?? this.sessionId,
1208
+ effectiveResume,
1125
1209
  streamingContentParts
1126
1210
  ) : messagesPrompt;
1127
1211
  this.logger.debug(
1128
- `[claude-code] Executing query with streamingInput: ${wantsStreamInput}, session: ${this.settings.resume ?? this.sessionId ?? "new"}`
1212
+ `[claude-code] Executing query with streamingInput: ${wantsStreamInput}, session: ${effectiveResume ?? "new"}`
1129
1213
  );
1130
1214
  const response = (0, import_claude_agent_sdk.query)({
1131
1215
  prompt: sdkPrompt,
@@ -1140,6 +1224,10 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1140
1224
  this.setSessionId(message.session_id);
1141
1225
  costUsd = message.total_cost_usd;
1142
1226
  durationMs = message.duration_ms;
1227
+ if ("is_error" in message && message.is_error === true) {
1228
+ const errorMessage = "result" in message && typeof message.result === "string" ? message.result : "Claude Code CLI returned an error";
1229
+ throw Object.assign(new Error(errorMessage), { exitCode: 1 });
1230
+ }
1143
1231
  if (message.subtype === "error_max_structured_output_retries") {
1144
1232
  throw new Error(
1145
1233
  "Failed to generate valid structured output after maximum retries. The model could not produce a response matching the required schema."
@@ -1185,7 +1273,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1185
1273
  message: CLAUDE_CODE_TRUNCATION_WARNING
1186
1274
  });
1187
1275
  } else {
1188
- throw this.handleClaudeCodeError(error, messagesPrompt);
1276
+ throw this.handleClaudeCodeError(error, messagesPrompt, collectedStderr);
1189
1277
  }
1190
1278
  } finally {
1191
1279
  if (options.abortSignal && abortListener) {
@@ -1236,7 +1324,19 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1236
1324
  abortListener = () => abortController.abort(options.abortSignal?.reason);
1237
1325
  options.abortSignal.addEventListener("abort", abortListener, { once: true });
1238
1326
  }
1239
- const queryOptions = this.createQueryOptions(abortController, options.responseFormat);
1327
+ let collectedStderr = "";
1328
+ const stderrCollector = (data) => {
1329
+ collectedStderr += data;
1330
+ };
1331
+ const sdkOptions = this.getSanitizedSdkOptions();
1332
+ const effectiveResume = this.getEffectiveResume(sdkOptions);
1333
+ const queryOptions = this.createQueryOptions(
1334
+ abortController,
1335
+ options.responseFormat,
1336
+ stderrCollector,
1337
+ sdkOptions,
1338
+ effectiveResume
1339
+ );
1240
1340
  if (queryOptions.includePartialMessages === void 0) {
1241
1341
  queryOptions.includePartialMessages = true;
1242
1342
  }
@@ -1250,7 +1350,9 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1250
1350
  });
1251
1351
  }
1252
1352
  const modeSetting = this.settings.streamingInput ?? "auto";
1253
- const wantsStreamInput = modeSetting === "always" || modeSetting === "auto" && !!this.settings.canUseTool;
1353
+ const effectiveCanUseTool = sdkOptions?.canUseTool ?? this.settings.canUseTool;
1354
+ const effectivePermissionPromptToolName = sdkOptions?.permissionPromptToolName ?? this.settings.permissionPromptToolName;
1355
+ const wantsStreamInput = modeSetting === "always" || modeSetting === "auto" && !!effectiveCanUseTool;
1254
1356
  if (!wantsStreamInput && hasImageParts) {
1255
1357
  warnings.push({
1256
1358
  type: "other",
@@ -1312,7 +1414,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1312
1414
  let hasReceivedStreamEvents = false;
1313
1415
  try {
1314
1416
  controller.enqueue({ type: "stream-start", warnings });
1315
- if (this.settings.canUseTool && this.settings.permissionPromptToolName) {
1417
+ if (effectiveCanUseTool && effectivePermissionPromptToolName) {
1316
1418
  throw new Error(
1317
1419
  "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."
1318
1420
  );
@@ -1320,11 +1422,11 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1320
1422
  const sdkPrompt = wantsStreamInput ? toAsyncIterablePrompt(
1321
1423
  messagesPrompt,
1322
1424
  outputStreamEnded,
1323
- this.settings.resume ?? this.sessionId,
1425
+ effectiveResume,
1324
1426
  streamingContentParts
1325
1427
  ) : messagesPrompt;
1326
1428
  this.logger.debug(
1327
- `[claude-code] Starting stream query with streamingInput: ${wantsStreamInput}, session: ${this.settings.resume ?? this.sessionId ?? "new"}`
1429
+ `[claude-code] Starting stream query with streamingInput: ${wantsStreamInput}, session: ${effectiveResume ?? "new"}`
1328
1430
  );
1329
1431
  const response = (0, import_claude_agent_sdk.query)({
1330
1432
  prompt: sdkPrompt,
@@ -1595,6 +1697,10 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1595
1697
  }
1596
1698
  } else if (message.type === "result") {
1597
1699
  done();
1700
+ if ("is_error" in message && message.is_error === true) {
1701
+ const errorMessage = "result" in message && typeof message.result === "string" ? message.result : "Claude Code CLI returned an error";
1702
+ throw Object.assign(new Error(errorMessage), { exitCode: 1 });
1703
+ }
1598
1704
  if (message.subtype === "error_max_structured_output_retries") {
1599
1705
  throw new Error(
1600
1706
  "Failed to generate valid structured output after maximum retries. The model could not produce a response matching the required schema."
@@ -1753,7 +1859,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
1753
1859
  if (isAbortError(error)) {
1754
1860
  errorToEmit = options.abortSignal?.aborted ? options.abortSignal.reason : error;
1755
1861
  } else {
1756
- errorToEmit = this.handleClaudeCodeError(error, messagesPrompt);
1862
+ errorToEmit = this.handleClaudeCodeError(error, messagesPrompt, collectedStderr);
1757
1863
  }
1758
1864
  controller.enqueue({
1759
1865
  type: "error",