@ai-sdk/anthropic 4.0.30 → 4.0.32

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.
@@ -1059,13 +1059,15 @@ import { generateText } from 'ai';
1059
1059
 
1060
1060
  const result = await generateText({
1061
1061
  model: anthropic('claude-sonnet-4-6'),
1062
- system: 'You have access to an `advisor` tool backed by a stronger reviewer model.'
1062
+ system:
1063
+ 'You have access to an `advisor` tool backed by a stronger reviewer model.',
1063
1064
  prompt:
1064
1065
  'Build a concurrent worker pool in Go with graceful shutdown. Outline the design first.',
1065
1066
  tools: {
1066
1067
  advisor: anthropic.tools.advisor_20260301({
1067
1068
  model: 'claude-opus-4-8',
1068
1069
  maxUses: 3,
1070
+ maxTokens: 2048,
1069
1071
  }),
1070
1072
  },
1071
1073
  });
@@ -1088,6 +1090,10 @@ The advisor tool supports the following configuration options:
1088
1090
 
1089
1091
  Optional. Maximum number of advisor calls allowed in a single request. Once the executor reaches this cap, further advisor calls return an `advisor_tool_result_error` with `errorCode: 'max_uses_exceeded'`, and the executor continues without further advice.
1090
1092
 
1093
+ - **maxTokens** _number_
1094
+
1095
+ Optional. Maximum number of tokens the advisor can generate per call, including thinking and text. The minimum is `1024`, and Anthropic recommends starting with `2048`. This limit applies independently to each advisor call and is separate from the request-level `maxOutputTokens`, which controls executor output. Values above the selected advisor model's output limit result in an Anthropic `400` error. When configured, successful advisor results include `stopReason`; check for `stopReason === 'max_tokens'` to detect truncated advice. The field is available on both plaintext and redacted advisor result variants and is preserved across follow-up turns.
1096
+
1091
1097
  - **caching** _object_
1092
1098
 
1093
1099
  Optional. Enables prompt caching for the advisor's own transcript across calls within a conversation. Use `{ type: 'ephemeral', ttl: '5m' | '1h' }`. This is most useful for longer agent loops where you expect at least three advisor calls.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/anthropic",
3
- "version": "4.0.30",
3
+ "version": "4.0.32",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@ai-sdk/provider": "4.0.5",
39
- "@ai-sdk/provider-utils": "5.0.21"
39
+ "@ai-sdk/provider-utils": "5.0.22"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "22.19.19",
@@ -358,10 +358,12 @@ export interface AnthropicAdvisorToolResultContent {
358
358
  | {
359
359
  type: 'advisor_result';
360
360
  text: string;
361
+ stop_reason?: string;
361
362
  }
362
363
  | {
363
364
  type: 'advisor_redacted_result';
364
365
  encrypted_content: string;
366
+ stop_reason?: string;
365
367
  }
366
368
  | {
367
369
  type: 'advisor_tool_result_error';
@@ -530,6 +532,7 @@ export type AnthropicTool =
530
532
  name: 'advisor';
531
533
  model: string;
532
534
  max_uses?: number;
535
+ max_tokens?: number;
533
536
  caching?: {
534
537
  type: 'ephemeral';
535
538
  ttl: '5m' | '1h';
@@ -924,10 +927,12 @@ export const anthropicResponseSchema = lazySchema(() =>
924
927
  z.object({
925
928
  type: z.literal('advisor_result'),
926
929
  text: z.string(),
930
+ stop_reason: z.string().nullish(),
927
931
  }),
928
932
  z.object({
929
933
  type: z.literal('advisor_redacted_result'),
930
934
  encrypted_content: z.string(),
935
+ stop_reason: z.string().nullish(),
931
936
  }),
932
937
  z.object({
933
938
  type: z.literal('advisor_tool_result_error'),
@@ -1321,10 +1326,12 @@ export const anthropicChunkSchema = lazySchema(() =>
1321
1326
  z.object({
1322
1327
  type: z.literal('advisor_result'),
1323
1328
  text: z.string(),
1329
+ stop_reason: z.string().nullish(),
1324
1330
  }),
1325
1331
  z.object({
1326
1332
  type: z.literal('advisor_redacted_result'),
1327
1333
  encrypted_content: z.string(),
1334
+ stop_reason: z.string().nullish(),
1328
1335
  }),
1329
1336
  z.object({
1330
1337
  type: z.literal('advisor_tool_result_error'),
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  APICallError,
3
+ InvalidResponseDataError,
3
4
  type JSONObject,
4
5
  type LanguageModelV4,
5
6
  type LanguageModelV4CallOptions,
@@ -1388,6 +1389,9 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
1388
1389
  result: {
1389
1390
  type: 'advisor_result',
1390
1391
  text: part.content.text,
1392
+ ...(part.content.stop_reason != null && {
1393
+ stopReason: part.content.stop_reason,
1394
+ }),
1391
1395
  },
1392
1396
  });
1393
1397
  } else if (part.content.type === 'advisor_redacted_result') {
@@ -1398,6 +1402,9 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
1398
1402
  result: {
1399
1403
  type: 'advisor_redacted_result',
1400
1404
  encryptedContent: part.content.encrypted_content,
1405
+ ...(part.content.stop_reason != null && {
1406
+ stopReason: part.content.stop_reason,
1407
+ }),
1401
1408
  },
1402
1409
  });
1403
1410
  } else {
@@ -1588,6 +1595,9 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
1588
1595
  let stopDetails: AnthropicMessageMetadata['stopDetails'] = undefined;
1589
1596
  let container: AnthropicMessageMetadata['container'] | null = null;
1590
1597
  let isJsonResponseFromTool = false;
1598
+ let isMessageOpen = false;
1599
+ let activeMessageId: string | null | undefined;
1600
+ let hasInvalidMessageSequence = false;
1591
1601
 
1592
1602
  let blockType:
1593
1603
  | 'text'
@@ -1619,6 +1629,10 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
1619
1629
  },
1620
1630
 
1621
1631
  transform(chunk, controller) {
1632
+ if (hasInvalidMessageSequence) {
1633
+ return;
1634
+ }
1635
+
1622
1636
  if (options.includeRawChunks) {
1623
1637
  controller.enqueue({ type: 'raw', rawValue: chunk.rawValue });
1624
1638
  }
@@ -2097,6 +2111,9 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
2097
2111
  result: {
2098
2112
  type: 'advisor_result',
2099
2113
  text: part.content.text,
2114
+ ...(part.content.stop_reason != null && {
2115
+ stopReason: part.content.stop_reason,
2116
+ }),
2100
2117
  },
2101
2118
  });
2102
2119
  } else if (part.content.type === 'advisor_redacted_result') {
@@ -2107,6 +2124,9 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
2107
2124
  result: {
2108
2125
  type: 'advisor_redacted_result',
2109
2126
  encryptedContent: part.content.encrypted_content,
2127
+ ...(part.content.stop_reason != null && {
2128
+ stopReason: part.content.stop_reason,
2129
+ }),
2110
2130
  },
2111
2131
  });
2112
2132
  } else {
@@ -2405,6 +2425,27 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
2405
2425
  }
2406
2426
 
2407
2427
  case 'message_start': {
2428
+ if (isMessageOpen) {
2429
+ if (activeMessageId === value.message.id) {
2430
+ return;
2431
+ }
2432
+
2433
+ hasInvalidMessageSequence = true;
2434
+ controller.enqueue({
2435
+ type: 'error',
2436
+ error: new InvalidResponseDataError({
2437
+ data: value,
2438
+ message:
2439
+ `Received message_start for message ${JSON.stringify(value.message.id)} ` +
2440
+ `while message ${JSON.stringify(activeMessageId)} is still open.`,
2441
+ }),
2442
+ });
2443
+ return;
2444
+ }
2445
+
2446
+ isMessageOpen = true;
2447
+ activeMessageId = value.message.id;
2448
+
2408
2449
  usage.input_tokens = value.message.usage.input_tokens;
2409
2450
  usage.cache_read_input_tokens =
2410
2451
  value.message.usage.cache_read_input_tokens ?? 0;
@@ -2559,6 +2600,9 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
2559
2600
  }
2560
2601
 
2561
2602
  case 'message_stop': {
2603
+ isMessageOpen = false;
2604
+ activeMessageId = undefined;
2605
+
2562
2606
  const anthropicMetadata = {
2563
2607
  usage: (rawUsage as JSONObject) ?? null,
2564
2608
  stopSequence,
@@ -358,6 +358,9 @@ export async function prepareTools({
358
358
  name: 'advisor',
359
359
  model: args.model,
360
360
  ...(args.maxUses !== undefined && { max_uses: args.maxUses }),
361
+ ...(args.maxTokens !== undefined && {
362
+ max_tokens: args.maxTokens,
363
+ }),
361
364
  ...(args.caching !== undefined && { caching: args.caching }),
362
365
  });
363
366
  break;
@@ -44,6 +44,8 @@ export const anthropicTools = {
44
44
  *
45
45
  * @param model - The advisor model ID (required), e.g. `"claude-opus-4-8"`.
46
46
  * @param maxUses - Maximum advisor calls per request (per-request cap).
47
+ * @param maxTokens - Maximum advisor output tokens per call, including
48
+ * thinking and text. Minimum 1024; Anthropic recommends starting with 2048.
47
49
  * @param caching - Enables prompt caching for the advisor's transcript
48
50
  * across calls within a conversation. Worthwhile from ~3 advisor calls
49
51
  * per conversation.
@@ -1243,6 +1243,9 @@ export async function convertToAnthropicPrompt({
1243
1243
  content: {
1244
1244
  type: 'advisor_result',
1245
1245
  text: advisorOutput.text,
1246
+ ...(advisorOutput.stopReason !== undefined && {
1247
+ stop_reason: advisorOutput.stopReason,
1248
+ }),
1246
1249
  },
1247
1250
  cache_control: cacheControl,
1248
1251
  });
@@ -1253,6 +1256,9 @@ export async function convertToAnthropicPrompt({
1253
1256
  content: {
1254
1257
  type: 'advisor_redacted_result',
1255
1258
  encrypted_content: advisorOutput.encryptedContent,
1259
+ ...(advisorOutput.stopReason !== undefined && {
1260
+ stop_reason: advisorOutput.stopReason,
1261
+ }),
1256
1262
  },
1257
1263
  cache_control: cacheControl,
1258
1264
  });
@@ -10,6 +10,7 @@ export const advisor_20260301ArgsSchema = lazySchema(() =>
10
10
  z.object({
11
11
  model: z.string(),
12
12
  maxUses: z.number().optional(),
13
+ maxTokens: z.number().int().min(1024).optional(),
13
14
  caching: z
14
15
  .object({
15
16
  type: z.literal('ephemeral'),
@@ -26,10 +27,12 @@ export const advisor_20260301OutputSchema = lazySchema(() =>
26
27
  z.object({
27
28
  type: z.literal('advisor_result'),
28
29
  text: z.string(),
30
+ stopReason: z.string().optional(),
29
31
  }),
30
32
  z.object({
31
33
  type: z.literal('advisor_redacted_result'),
32
34
  encryptedContent: z.string(),
35
+ stopReason: z.string().optional(),
33
36
  }),
34
37
  z.object({
35
38
  type: z.literal('advisor_tool_result_error'),
@@ -54,6 +57,12 @@ const factory = createProviderExecutedToolFactory<
54
57
  * Plaintext advice from the advisor model.
55
58
  */
56
59
  text: string;
60
+
61
+ /**
62
+ * The advisor sub-inference stop reason when `maxTokens` is configured.
63
+ * A value of `"max_tokens"` indicates that the advice was truncated.
64
+ */
65
+ stopReason?: string;
57
66
  }
58
67
  | {
59
68
  type: 'advisor_redacted_result';
@@ -64,6 +73,12 @@ const factory = createProviderExecutedToolFactory<
64
73
  * advice into the executor's prompt.
65
74
  */
66
75
  encryptedContent: string;
76
+
77
+ /**
78
+ * The advisor sub-inference stop reason when `maxTokens` is configured.
79
+ * A value of `"max_tokens"` indicates that the advice was truncated.
80
+ */
81
+ stopReason?: string;
67
82
  }
68
83
  | {
69
84
  type: 'advisor_tool_result_error';
@@ -99,6 +114,17 @@ const factory = createProviderExecutedToolFactory<
99
114
  */
100
115
  maxUses?: number;
101
116
 
117
+ /**
118
+ * Maximum number of tokens the advisor can generate per call, including
119
+ * thinking and text. This is independent of the executor's request-level
120
+ * `maxOutputTokens`.
121
+ *
122
+ * The minimum value is 1024. Anthropic recommends starting with 2048.
123
+ * Values above the selected advisor model's output limit return a
124
+ * `400 invalid_request_error` from the API.
125
+ */
126
+ maxTokens?: number;
127
+
102
128
  /**
103
129
  * Enables prompt caching for the advisor's own transcript across calls
104
130
  * within a conversation. Unlike `cache_control` on content blocks, this