ai-sdk-provider-claude-code 3.5.2 → 3.5.3

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
@@ -616,6 +616,22 @@ All requests made through this provider report timing in `providerMetadata['clau
616
616
  - When replaying conversation history through the prompt, assistant tool calls are serialized as text lines — `[Tool call: Read({"file_path":"/x"})]` (inputs truncated at 1000 characters) — paired with `Tool Result (Read): ...` lines for tool messages
617
617
  - `canUseTool` requires streaming input at the SDK level (AsyncIterable prompt). This provider supports it via `streamingInput`: use `'auto'` (default when `canUseTool` is set) or `'always'`. See GUIDE for details.
618
618
 
619
+ ## Error Diagnostics
620
+
621
+ Mapped provider errors append a trimmed stderr tail directly to the message as `... | stderr (tail): <last lines>`, making failures visible in logs without inspecting metadata. The full captured stderr (capped at 4000 characters) remains available through `getErrorMetadata`:
622
+
623
+ ```typescript
624
+ import { generateText } from 'ai';
625
+ import { claudeCode, getErrorMetadata } from 'ai-sdk-provider-claude-code';
626
+ try {
627
+ await generateText({ model: claudeCode('haiku'), prompt: 'Hello, Claude!' });
628
+ } catch (error) {
629
+ console.error(getErrorMetadata(error)?.stderr);
630
+ }
631
+ ```
632
+
633
+ Authentication and timeout failures are also classified when the only evidence is in stderr, using high-precision matching to avoid false positives from unrelated stderr output.
634
+
619
635
  ## Tool Error Parity (Streaming)
620
636
 
621
637
  - In addition to `tool-call` and `tool-result`, this provider emits a distinct `tool-error` stream event when a tool execution fails.
package/dist/index.cjs CHANGED
@@ -376,6 +376,21 @@ ${segmentText}`;
376
376
 
377
377
  // src/errors.ts
378
378
  var import_provider = require("@ai-sdk/provider");
379
+ var STDERR_TAIL_MARKER = " | stderr (tail):";
380
+ var ANSI_ESCAPE_SEQUENCE = (
381
+ // eslint-disable-next-line no-control-regex
382
+ /\x1b(?:\][^\x07\x1b]*(?:\x07|\x1b\\)|\[[0-?]*[ -/]*[@-~])/g
383
+ );
384
+ function stderrTail(raw) {
385
+ const withoutAnsi = raw.replace(ANSI_ESCAPE_SEQUENCE, "");
386
+ const withoutControlCharacters = Array.from(withoutAnsi).filter((character) => {
387
+ const codePoint = character.codePointAt(0) ?? 0;
388
+ return character === "\r" || character === "\n" || codePoint === 8232 || codePoint === 8233 || codePoint >= 32 && codePoint < 127 || codePoint > 159;
389
+ }).join("");
390
+ const tail = withoutControlCharacters.split(/\r\n|\r|\n|\u2028|\u2029/).map((line) => line.trim()).filter((line) => line.length > 0).slice(-5).join("; ");
391
+ const codePoints = Array.from(tail);
392
+ return codePoints.length > 600 ? `\u2026${codePoints.slice(-599).join("")}` : tail;
393
+ }
379
394
  function createAPICallError({
380
395
  message,
381
396
  code,
@@ -390,26 +405,37 @@ function createAPICallError({
390
405
  stderr,
391
406
  promptExcerpt
392
407
  };
408
+ const tail = typeof stderr === "string" && stderr.length > 0 ? stderrTail(stderr) : "";
409
+ const enrichedMessage = tail && !message.includes(STDERR_TAIL_MARKER) ? `${message}${STDERR_TAIL_MARKER} ${tail}` : message;
393
410
  return new import_provider.APICallError({
394
- message,
411
+ message: enrichedMessage,
395
412
  isRetryable,
396
413
  url: "claude-code-cli://command",
397
414
  requestBodyValues: promptExcerpt ? { prompt: promptExcerpt } : void 0,
398
415
  data: metadata
399
416
  });
400
417
  }
401
- function createAuthenticationError({ message }) {
402
- return new import_provider.LoadAPIKeyError({
418
+ function createAuthenticationError({
419
+ message,
420
+ stderr
421
+ }) {
422
+ const error = new import_provider.LoadAPIKeyError({
403
423
  message: message || "Authentication failed. Please ensure Claude Code SDK is properly authenticated."
404
424
  });
425
+ if (stderr) {
426
+ error.data = { stderr };
427
+ }
428
+ return error;
405
429
  }
406
430
  function createTimeoutError({
407
431
  message,
432
+ stderr,
408
433
  promptExcerpt,
409
434
  timeoutMs
410
435
  }) {
411
436
  const metadata = {
412
437
  code: "TIMEOUT",
438
+ stderr,
413
439
  promptExcerpt
414
440
  };
415
441
  return new import_provider.APICallError({
@@ -435,6 +461,9 @@ function getErrorMetadata(error) {
435
461
  if (error instanceof import_provider.APICallError && error.data) {
436
462
  return error.data;
437
463
  }
464
+ if (error instanceof import_provider.LoadAPIKeyError && error.data) {
465
+ return error.data;
466
+ }
438
467
  return void 0;
439
468
  }
440
469
 
@@ -1012,10 +1041,16 @@ function createVerboseLogger(logger, verbose = false) {
1012
1041
 
1013
1042
  // src/claude-code-language-model.ts
1014
1043
  var import_claude_agent_sdk = require("@anthropic-ai/claude-agent-sdk");
1015
- var PROVIDER_VERSION = "3.5.2";
1044
+ var PROVIDER_VERSION = "3.5.3";
1016
1045
  var DEFAULT_CLIENT_APP = `ai-sdk-provider-claude-code/${PROVIDER_VERSION}`;
1017
1046
  var CLAUDE_CODE_TRUNCATION_WARNING = "Claude Code SDK output ended unexpectedly; returning truncated response from buffered text. Await upstream fix to avoid data loss.";
1018
1047
  var MIN_TRUNCATION_LENGTH = 512;
1048
+ function capStderr(raw) {
1049
+ if (raw.length <= 4e3) {
1050
+ return raw;
1051
+ }
1052
+ return Array.from(raw).slice(-4e3).join("");
1053
+ }
1019
1054
  function isClaudeCodeTruncationError(error, bufferedText) {
1020
1055
  const isSyntaxError = error instanceof SyntaxError || // eslint-disable-next-line @typescript-eslint/no-explicit-any
1021
1056
  typeof error?.name === "string" && // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -2024,12 +2059,18 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2024
2059
  return opts;
2025
2060
  }
2026
2061
  handleClaudeCodeError(error, messagesPrompt, collectedStderr) {
2027
- if (isAbortError(error)) {
2028
- throw error;
2029
- }
2030
2062
  if (error instanceof import_provider2.APICallError || error instanceof import_provider2.LoadAPIKeyError) {
2031
2063
  return error;
2032
2064
  }
2065
+ const rawSdkStderr = typeof error === "object" && error !== null && "stderr" in error && typeof error.stderr === "string" ? error.stderr : void 0;
2066
+ const cappedSdkStderr = rawSdkStderr !== void 0 ? capStderr(rawSdkStderr) : void 0;
2067
+ const selectedStderr = cappedSdkStderr !== void 0 && stderrTail(cappedSdkStderr) ? cappedSdkStderr : collectedStderr ? capStderr(collectedStderr) : void 0;
2068
+ const stderr = selectedStderr || void 0;
2069
+ const tail = stderr ? stderrTail(stderr) : "";
2070
+ const appendStderrTail = (message) => tail && !message.includes(STDERR_TAIL_MARKER) ? `${message}${STDERR_TAIL_MARKER} ${tail}` : message;
2071
+ if (isAbortError(error)) {
2072
+ throw error;
2073
+ }
2033
2074
  const isErrorWithMessage = (err) => {
2034
2075
  return typeof err === "object" && err !== null && "message" in err;
2035
2076
  };
@@ -2050,26 +2091,43 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2050
2091
  "oauth_org_not_allowed"
2051
2092
  // SDK 0.3.x assistant error kind: OAuth org not permitted
2052
2093
  ];
2094
+ const stderrAuthPatterns = [
2095
+ "not logged in",
2096
+ "not authenticated",
2097
+ "auth failed",
2098
+ "please login",
2099
+ "please run /login",
2100
+ "claude login",
2101
+ "claude auth login",
2102
+ "invalid api key",
2103
+ "oauth token revoked",
2104
+ "oauth_org_not_allowed"
2105
+ ];
2053
2106
  const errorMessage = isErrorWithMessage(error) && error.message ? error.message.toLowerCase() : "";
2107
+ const stderrMessage = stderr?.toLowerCase() ?? "";
2054
2108
  const errorKind = getStructuredErrorKind(error);
2055
2109
  const exitCode = isErrorWithCode(error) && typeof error.exitCode === "number" ? error.exitCode : void 0;
2056
- const isAuthError = errorKind === "authentication_failed" || errorKind === "oauth_org_not_allowed" || authErrorPatterns.some((pattern) => errorMessage.includes(pattern)) || exitCode === 401;
2110
+ const isAuthError = errorKind === "authentication_failed" || errorKind === "oauth_org_not_allowed" || authErrorPatterns.some((pattern) => errorMessage.includes(pattern)) || stderrAuthPatterns.some((pattern) => stderrMessage.includes(pattern)) || exitCode === 401;
2057
2111
  if (isAuthError) {
2058
2112
  return createAuthenticationError({
2059
- message: isErrorWithMessage(error) && error.message ? error.message : "Authentication failed. Please ensure Claude Code SDK is properly authenticated."
2113
+ message: appendStderrTail(
2114
+ isErrorWithMessage(error) && error.message ? error.message : "Authentication failed. Please ensure Claude Code SDK is properly authenticated."
2115
+ ),
2116
+ stderr
2060
2117
  });
2061
2118
  }
2062
2119
  const errorCode = isErrorWithCode(error) && typeof error.code === "string" ? error.code : "";
2063
- if (errorCode === "ETIMEDOUT" || errorMessage.includes("timeout")) {
2120
+ if (errorCode === "ETIMEDOUT" || errorMessage.includes("timeout") || /request timed out|etimedout/i.test(stderr ?? "")) {
2064
2121
  return createTimeoutError({
2065
- message: isErrorWithMessage(error) && error.message ? error.message : "Request timed out",
2122
+ message: appendStderrTail(
2123
+ isErrorWithMessage(error) && error.message ? error.message : "Request timed out"
2124
+ ),
2125
+ stderr,
2066
2126
  promptExcerpt: messagesPrompt.substring(0, 200)
2067
2127
  // Don't specify timeoutMs since we don't know the actual timeout value
2068
2128
  // It's controlled by the consumer via AbortSignal
2069
2129
  });
2070
2130
  }
2071
- const stderrFromError = isErrorWithCode(error) && typeof error.stderr === "string" ? error.stderr : void 0;
2072
- const stderr = stderrFromError || collectedStderr || void 0;
2073
2131
  if (errorKind === "overloaded" || errorKind === "rate_limit" || errorMessage.includes("overloaded")) {
2074
2132
  return createAPICallError({
2075
2133
  message: isErrorWithMessage(error) && error.message ? error.message : "Anthropic API is overloaded. Please retry.",
@@ -2293,6 +2351,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2293
2351
  let collectedStderr = "";
2294
2352
  const stderrCollector = (data) => {
2295
2353
  collectedStderr += data;
2354
+ collectedStderr = capStderr(collectedStderr);
2296
2355
  };
2297
2356
  const sdkOptions = this.getSanitizedSdkOptions();
2298
2357
  const effectiveResume = this.getEffectiveResume(sdkOptions);
@@ -2771,6 +2830,7 @@ var ClaudeCodeLanguageModel = class _ClaudeCodeLanguageModel {
2771
2830
  let collectedStderr = "";
2772
2831
  const stderrCollector = (data) => {
2773
2832
  collectedStderr += data;
2833
+ collectedStderr = capStderr(collectedStderr);
2774
2834
  };
2775
2835
  const sdkOptions = this.getSanitizedSdkOptions();
2776
2836
  const effectiveResume = this.getEffectiveResume(sdkOptions);