@ai-sdk/anthropic 3.0.53 → 3.0.54

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/anthropic",
3
- "version": "3.0.53",
3
+ "version": "3.0.54",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -238,6 +238,13 @@ export interface AnthropicCodeExecutionToolResultContent {
238
238
  return_code: number;
239
239
  content: Array<{ type: 'code_execution_output'; file_id: string }>;
240
240
  }
241
+ | {
242
+ type: 'encrypted_code_execution_result';
243
+ encrypted_stdout: string;
244
+ stderr: string;
245
+ return_code: number;
246
+ content: Array<{ type: 'code_execution_output'; file_id: string }>;
247
+ }
241
248
  | {
242
249
  type: 'code_execution_tool_result_error';
243
250
  error_code: string;
@@ -416,7 +423,7 @@ export type AnthropicTool =
416
423
  type: 'memory_20250818';
417
424
  }
418
425
  | {
419
- type: 'web_fetch_20250910';
426
+ type: 'web_fetch_20250910' | 'web_fetch_20260209';
420
427
  name: string;
421
428
  max_uses?: number;
422
429
  allowed_domains?: string[];
@@ -426,7 +433,7 @@ export type AnthropicTool =
426
433
  cache_control: AnthropicCacheControl | undefined;
427
434
  }
428
435
  | {
429
- type: 'web_search_20250305';
436
+ type: 'web_search_20250305' | 'web_search_20260209';
430
437
  name: string;
431
438
  max_uses?: number;
432
439
  allowed_domains?: string[];
@@ -622,6 +629,17 @@ export const anthropicMessagesResponseSchema = lazySchema(() =>
622
629
  id: z.string(),
623
630
  name: z.string(),
624
631
  input: z.record(z.string(), z.unknown()).nullish(),
632
+ caller: z
633
+ .union([
634
+ z.object({
635
+ type: z.literal('code_execution_20260120'),
636
+ tool_id: z.string(),
637
+ }),
638
+ z.object({
639
+ type: z.literal('direct'),
640
+ }),
641
+ ])
642
+ .optional(),
625
643
  }),
626
644
  z.object({
627
645
  type: z.literal('mcp_tool_use'),
@@ -712,6 +730,21 @@ export const anthropicMessagesResponseSchema = lazySchema(() =>
712
730
  .optional()
713
731
  .default([]),
714
732
  }),
733
+ z.object({
734
+ type: z.literal('encrypted_code_execution_result'),
735
+ encrypted_stdout: z.string(),
736
+ stderr: z.string(),
737
+ return_code: z.number(),
738
+ content: z
739
+ .array(
740
+ z.object({
741
+ type: z.literal('code_execution_output'),
742
+ file_id: z.string(),
743
+ }),
744
+ )
745
+ .optional()
746
+ .default([]),
747
+ }),
715
748
  z.object({
716
749
  type: z.literal('code_execution_tool_result_error'),
717
750
  error_code: z.string(),
@@ -954,6 +987,17 @@ export const anthropicMessagesChunkSchema = lazySchema(() =>
954
987
  id: z.string(),
955
988
  name: z.string(),
956
989
  input: z.record(z.string(), z.unknown()).nullish(),
990
+ caller: z
991
+ .union([
992
+ z.object({
993
+ type: z.literal('code_execution_20260120'),
994
+ tool_id: z.string(),
995
+ }),
996
+ z.object({
997
+ type: z.literal('direct'),
998
+ }),
999
+ ])
1000
+ .optional(),
957
1001
  }),
958
1002
  z.object({
959
1003
  type: z.literal('mcp_tool_use'),
@@ -1044,6 +1088,21 @@ export const anthropicMessagesChunkSchema = lazySchema(() =>
1044
1088
  .optional()
1045
1089
  .default([]),
1046
1090
  }),
1091
+ z.object({
1092
+ type: z.literal('encrypted_code_execution_result'),
1093
+ encrypted_stdout: z.string(),
1094
+ stderr: z.string(),
1095
+ return_code: z.number(),
1096
+ content: z
1097
+ .array(
1098
+ z.object({
1099
+ type: z.literal('code_execution_output'),
1100
+ file_id: z.string(),
1101
+ }),
1102
+ )
1103
+ .optional()
1104
+ .default([]),
1105
+ }),
1047
1106
  z.object({
1048
1107
  type: z.literal('code_execution_tool_result_error'),
1049
1108
  error_code: z.string(),
@@ -37,6 +37,7 @@ import {
37
37
  anthropicMessagesResponseSchema,
38
38
  AnthropicReasoningMetadata,
39
39
  AnthropicResponseContextManagement,
40
+ AnthropicTool,
40
41
  Citation,
41
42
  } from './anthropic-messages-api';
42
43
  import {
@@ -305,7 +306,9 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
305
306
  'anthropic.bash_20250124': 'bash',
306
307
  'anthropic.memory_20250818': 'memory',
307
308
  'anthropic.web_search_20250305': 'web_search',
309
+ 'anthropic.web_search_20260209': 'web_search',
308
310
  'anthropic.web_fetch_20250910': 'web_fetch',
311
+ 'anthropic.web_fetch_20260209': 'web_fetch',
309
312
  'anthropic.tool_search_regex_20251119': 'tool_search_tool_regex',
310
313
  'anthropic.tool_search_bm25_20251119': 'tool_search_tool_bm25',
311
314
  },
@@ -745,6 +748,10 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
745
748
  ...this.extractCitationDocuments(options.prompt),
746
749
  ];
747
750
 
751
+ const markCodeExecutionDynamic = hasWebTool20260209WithoutCodeExecution(
752
+ args.tools,
753
+ );
754
+
748
755
  const {
749
756
  responseHeaders,
750
757
  value: response,
@@ -898,6 +905,11 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
898
905
  toolName: toolNameMapping.toCustomToolName(part.name),
899
906
  input: JSON.stringify(inputToSerialize),
900
907
  providerExecuted: true,
908
+ // We want this 'code_execution' tool call to be allowed even if the tool is not explicitly provided.
909
+ // Since the validation generally bypasses dynamic tools, we mark this specific tool as dynamic.
910
+ ...(markCodeExecutionDynamic && part.name === 'code_execution'
911
+ ? { dynamic: true }
912
+ : {}),
901
913
  });
902
914
  } else if (
903
915
  part.name === 'tool_search_tool_regex' ||
@@ -1204,6 +1216,10 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
1204
1216
  ...this.extractCitationDocuments(options.prompt),
1205
1217
  ];
1206
1218
 
1219
+ const markCodeExecutionDynamic = hasWebTool20260209WithoutCodeExecution(
1220
+ body.tools,
1221
+ );
1222
+
1207
1223
  const url = this.buildRequestUrl(true);
1208
1224
  const { responseHeaders, value: response } = await postJsonToApi({
1209
1225
  url,
@@ -1438,12 +1454,26 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
1438
1454
  const customToolName =
1439
1455
  toolNameMapping.toCustomToolName(providerToolName);
1440
1456
 
1457
+ // Tools like 'web_fetch_20260209' provide input data here.
1458
+ // Other tools like 'code_execution_20260120' provide input data via deltas.
1459
+ // So we only use this if it's non-empty to avoid conflicts.
1460
+ const finalInput =
1461
+ part.input != null &&
1462
+ typeof part.input === 'object' &&
1463
+ Object.keys(part.input).length > 0
1464
+ ? JSON.stringify(part.input)
1465
+ : '';
1466
+
1441
1467
  contentBlocks[value.index] = {
1442
1468
  type: 'tool-call',
1443
1469
  toolCallId: part.id,
1444
1470
  toolName: customToolName,
1445
- input: '',
1471
+ input: finalInput,
1446
1472
  providerExecuted: true,
1473
+ ...(markCodeExecutionDynamic &&
1474
+ providerToolName === 'code_execution'
1475
+ ? { dynamic: true }
1476
+ : {}),
1447
1477
  firstDelta: true,
1448
1478
  providerToolName: part.name,
1449
1479
  };
@@ -1453,6 +1483,10 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
1453
1483
  id: part.id,
1454
1484
  toolName: customToolName,
1455
1485
  providerExecuted: true,
1486
+ ...(markCodeExecutionDynamic &&
1487
+ providerToolName === 'code_execution'
1488
+ ? { dynamic: true }
1489
+ : {}),
1456
1490
  });
1457
1491
  } else if (
1458
1492
  part.name === 'tool_search_tool_regex' ||
@@ -1773,6 +1807,10 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
1773
1807
  toolName: contentBlock.toolName,
1774
1808
  input: finalInput,
1775
1809
  providerExecuted: contentBlock.providerExecuted,
1810
+ ...(markCodeExecutionDynamic &&
1811
+ contentBlock.providerToolName === 'code_execution'
1812
+ ? { dynamic: true }
1813
+ : {}),
1776
1814
  ...(contentBlock.caller && {
1777
1815
  providerMetadata: {
1778
1816
  anthropic: {
@@ -2238,6 +2276,31 @@ function getModelCapabilities(modelId: string): {
2238
2276
  }
2239
2277
  }
2240
2278
 
2279
+ function hasWebTool20260209WithoutCodeExecution(
2280
+ tools: AnthropicTool[] | undefined,
2281
+ ): boolean {
2282
+ if (!tools) {
2283
+ return false;
2284
+ }
2285
+ let hasWebTool20260209 = false;
2286
+ let hasCodeExecutionTool = false;
2287
+ for (const tool of tools) {
2288
+ if (
2289
+ 'type' in tool &&
2290
+ (tool.type === 'web_fetch_20260209' ||
2291
+ tool.type === 'web_search_20260209')
2292
+ ) {
2293
+ hasWebTool20260209 = true;
2294
+ continue;
2295
+ }
2296
+ if (tool.name === 'code_execution') {
2297
+ hasCodeExecutionTool = true;
2298
+ break;
2299
+ }
2300
+ }
2301
+ return hasWebTool20260209 && !hasCodeExecutionTool;
2302
+ }
2303
+
2241
2304
  function mapAnthropicResponseContextManagement(
2242
2305
  contextManagement: AnthropicResponseContextManagement | null | undefined,
2243
2306
  ): AnthropicMessageMetadata['contextManagement'] | null {
@@ -6,7 +6,9 @@ import {
6
6
  import { AnthropicTool, AnthropicToolChoice } from './anthropic-messages-api';
7
7
  import { CacheControlValidator } from './get-cache-control';
8
8
  import { textEditor_20250728ArgsSchema } from './tool/text-editor_20250728';
9
+ import { webSearch_20260209ArgsSchema } from './tool/web-search_20260209';
9
10
  import { webSearch_20250305ArgsSchema } from './tool/web-search_20250305';
11
+ import { webFetch_20260209ArgsSchema } from './tool/web-fetch-20260209';
10
12
  import { webFetch_20250910ArgsSchema } from './tool/web-fetch-20250910';
11
13
  import { validateTypes } from '@ai-sdk/provider-utils';
12
14
 
@@ -249,6 +251,24 @@ export async function prepareTools({
249
251
  });
250
252
  break;
251
253
  }
254
+ case 'anthropic.web_fetch_20260209': {
255
+ betas.add('code-execution-web-tools-2026-02-09');
256
+ const args = await validateTypes({
257
+ value: tool.args,
258
+ schema: webFetch_20260209ArgsSchema,
259
+ });
260
+ anthropicTools.push({
261
+ type: 'web_fetch_20260209',
262
+ name: 'web_fetch',
263
+ max_uses: args.maxUses,
264
+ allowed_domains: args.allowedDomains,
265
+ blocked_domains: args.blockedDomains,
266
+ citations: args.citations,
267
+ max_content_tokens: args.maxContentTokens,
268
+ cache_control: undefined,
269
+ });
270
+ break;
271
+ }
252
272
  case 'anthropic.web_search_20250305': {
253
273
  const args = await validateTypes({
254
274
  value: tool.args,
@@ -265,6 +285,23 @@ export async function prepareTools({
265
285
  });
266
286
  break;
267
287
  }
288
+ case 'anthropic.web_search_20260209': {
289
+ betas.add('code-execution-web-tools-2026-02-09');
290
+ const args = await validateTypes({
291
+ value: tool.args,
292
+ schema: webSearch_20260209ArgsSchema,
293
+ });
294
+ anthropicTools.push({
295
+ type: 'web_search_20260209',
296
+ name: 'web_search',
297
+ max_uses: args.maxUses,
298
+ allowed_domains: args.allowedDomains,
299
+ blocked_domains: args.blockedDomains,
300
+ user_location: args.userLocation,
301
+ cache_control: undefined,
302
+ });
303
+ break;
304
+ }
268
305
 
269
306
  case 'anthropic.tool_search_regex_20251119': {
270
307
  betas.add('advanced-tool-use-2025-11-20');
@@ -13,7 +13,9 @@ import { textEditor_20250429 } from './tool/text-editor_20250429';
13
13
  import { textEditor_20250728 } from './tool/text-editor_20250728';
14
14
  import { toolSearchBm25_20251119 } from './tool/tool-search-bm25_20251119';
15
15
  import { toolSearchRegex_20251119 } from './tool/tool-search-regex_20251119';
16
+ import { webFetch_20260209 } from './tool/web-fetch-20260209';
16
17
  import { webFetch_20250910 } from './tool/web-fetch-20250910';
18
+ import { webSearch_20260209 } from './tool/web-search_20260209';
17
19
  import { webSearch_20250305 } from './tool/web-search_20250305';
18
20
 
19
21
  export const anthropicTools = {
@@ -173,6 +175,17 @@ export const anthropicTools = {
173
175
  */
174
176
  webFetch_20250910,
175
177
 
178
+ /**
179
+ * Creates a web fetch tool that gives Claude direct access to real-time web content.
180
+ *
181
+ * @param maxUses - The max_uses parameter limits the number of web fetches performed
182
+ * @param allowedDomains - Only fetch from these domains
183
+ * @param blockedDomains - Never fetch from these domains
184
+ * @param citations - Unlike web search where citations are always enabled, citations are optional for web fetch. Set "citations": {"enabled": true} to enable Claude to cite specific passages from fetched documents.
185
+ * @param maxContentTokens - The max_content_tokens parameter limits the amount of content that will be included in the context.
186
+ */
187
+ webFetch_20260209,
188
+
176
189
  /**
177
190
  * Creates a web search tool that gives Claude direct access to real-time web content.
178
191
  *
@@ -183,6 +196,16 @@ export const anthropicTools = {
183
196
  */
184
197
  webSearch_20250305,
185
198
 
199
+ /**
200
+ * Creates a web search tool that gives Claude direct access to real-time web content.
201
+ *
202
+ * @param maxUses - Maximum number of web searches Claude can perform during the conversation.
203
+ * @param allowedDomains - Optional list of domains that Claude is allowed to search.
204
+ * @param blockedDomains - Optional list of domains that Claude should avoid when searching.
205
+ * @param userLocation - Optional user location information to provide geographically relevant search results.
206
+ */
207
+ webSearch_20260209,
208
+
186
209
  /**
187
210
  * Creates a tool search tool that uses regex patterns to find tools.
188
211
  *
@@ -882,6 +882,9 @@ export async function convertToAnthropicMessagesPrompt({
882
882
  break;
883
883
  }
884
884
 
885
+ // ideally we'd switch schema based on the tool version (e.g.
886
+ // web_fetch_20260209 vs web_fetch_20250910), but since both
887
+ // versions share an identical output schema, we use one here.
885
888
  const webFetchOutput = await validateTypes({
886
889
  value: output.value,
887
890
  schema: webFetch_20250910OutputSchema,
@@ -926,6 +929,9 @@ export async function convertToAnthropicMessagesPrompt({
926
929
  break;
927
930
  }
928
931
 
932
+ // ideally we'd switch schema based on the tool version (e.g.
933
+ // web_search_20260209 vs web_search_20250305), but since both
934
+ // versions share an identical output schema, we use one here.
929
935
  const webSearchOutput = await validateTypes({
930
936
  value: output.value,
931
937
  schema: webSearch_20250305OutputSchema,
@@ -0,0 +1,145 @@
1
+ import {
2
+ createProviderToolFactoryWithOutputSchema,
3
+ lazySchema,
4
+ zodSchema,
5
+ } from '@ai-sdk/provider-utils';
6
+ import { z } from 'zod/v4';
7
+
8
+ export const webFetch_20260209ArgsSchema = lazySchema(() =>
9
+ zodSchema(
10
+ z.object({
11
+ maxUses: z.number().optional(),
12
+ allowedDomains: z.array(z.string()).optional(),
13
+ blockedDomains: z.array(z.string()).optional(),
14
+ citations: z.object({ enabled: z.boolean() }).optional(),
15
+ maxContentTokens: z.number().optional(),
16
+ }),
17
+ ),
18
+ );
19
+
20
+ export const webFetch_20260209OutputSchema = lazySchema(() =>
21
+ zodSchema(
22
+ z.object({
23
+ type: z.literal('web_fetch_result'),
24
+ url: z.string(),
25
+ content: z.object({
26
+ type: z.literal('document'),
27
+ title: z.string().nullable(),
28
+ citations: z.object({ enabled: z.boolean() }).optional(),
29
+ source: z.union([
30
+ z.object({
31
+ type: z.literal('base64'),
32
+ mediaType: z.literal('application/pdf'),
33
+ data: z.string(),
34
+ }),
35
+ z.object({
36
+ type: z.literal('text'),
37
+ mediaType: z.literal('text/plain'),
38
+ data: z.string(),
39
+ }),
40
+ ]),
41
+ }),
42
+ retrievedAt: z.string().nullable(),
43
+ }),
44
+ ),
45
+ );
46
+
47
+ const webFetch_20260209InputSchema = lazySchema(() =>
48
+ zodSchema(
49
+ z.object({
50
+ url: z.string(),
51
+ }),
52
+ ),
53
+ );
54
+
55
+ const factory = createProviderToolFactoryWithOutputSchema<
56
+ {
57
+ /**
58
+ * The URL to fetch.
59
+ */
60
+ url: string;
61
+ },
62
+ {
63
+ type: 'web_fetch_result';
64
+
65
+ /**
66
+ * Fetched content URL
67
+ */
68
+ url: string;
69
+
70
+ /**
71
+ * Fetched content.
72
+ */
73
+ content: {
74
+ type: 'document';
75
+
76
+ /**
77
+ * Title of the document
78
+ */
79
+ title: string | null;
80
+
81
+ /**
82
+ * Citation configuration for the document
83
+ */
84
+ citations?: { enabled: boolean };
85
+
86
+ source:
87
+ | {
88
+ type: 'base64';
89
+ mediaType: 'application/pdf';
90
+ data: string;
91
+ }
92
+ | {
93
+ type: 'text';
94
+ mediaType: 'text/plain';
95
+ data: string;
96
+ };
97
+ };
98
+
99
+ /**
100
+ * ISO 8601 timestamp when the content was retrieved
101
+ */
102
+ retrievedAt: string | null;
103
+ },
104
+ {
105
+ /**
106
+ * The maxUses parameter limits the number of web fetches performed
107
+ */
108
+ maxUses?: number;
109
+
110
+ /**
111
+ * Only fetch from these domains
112
+ */
113
+ allowedDomains?: string[];
114
+
115
+ /**
116
+ * Never fetch from these domains
117
+ */
118
+ blockedDomains?: string[];
119
+
120
+ /**
121
+ * Unlike web search where citations are always enabled, citations are optional for
122
+ * web fetch. Set "citations": {"enabled": true} to enable Claude to cite specific passages
123
+ * from fetched documents.
124
+ */
125
+ citations?: {
126
+ enabled: boolean;
127
+ };
128
+
129
+ /**
130
+ * The maxContentTokens parameter limits the amount of content that will be included in the context.
131
+ */
132
+ maxContentTokens?: number;
133
+ }
134
+ >({
135
+ id: 'anthropic.web_fetch_20260209',
136
+ inputSchema: webFetch_20260209InputSchema,
137
+ outputSchema: webFetch_20260209OutputSchema,
138
+ supportsDeferredResults: true,
139
+ });
140
+
141
+ export const webFetch_20260209 = (
142
+ args: Parameters<typeof factory>[0] = {}, // default
143
+ ) => {
144
+ return factory(args);
145
+ };
@@ -0,0 +1,136 @@
1
+ import {
2
+ createProviderToolFactoryWithOutputSchema,
3
+ lazySchema,
4
+ zodSchema,
5
+ } from '@ai-sdk/provider-utils';
6
+ import { z } from 'zod/v4';
7
+
8
+ export const webSearch_20260209ArgsSchema = lazySchema(() =>
9
+ zodSchema(
10
+ z.object({
11
+ maxUses: z.number().optional(),
12
+ allowedDomains: z.array(z.string()).optional(),
13
+ blockedDomains: z.array(z.string()).optional(),
14
+ userLocation: z
15
+ .object({
16
+ type: z.literal('approximate'),
17
+ city: z.string().optional(),
18
+ region: z.string().optional(),
19
+ country: z.string().optional(),
20
+ timezone: z.string().optional(),
21
+ })
22
+ .optional(),
23
+ }),
24
+ ),
25
+ );
26
+
27
+ export const webSearch_20260209OutputSchema = lazySchema(() =>
28
+ zodSchema(
29
+ z.array(
30
+ z.object({
31
+ url: z.string(),
32
+ title: z.string().nullable(),
33
+ pageAge: z.string().nullable(),
34
+ encryptedContent: z.string(),
35
+ type: z.literal('web_search_result'),
36
+ }),
37
+ ),
38
+ ),
39
+ );
40
+
41
+ const webSearch_20260209InputSchema = lazySchema(() =>
42
+ zodSchema(
43
+ z.object({
44
+ query: z.string(),
45
+ }),
46
+ ),
47
+ );
48
+
49
+ const factory = createProviderToolFactoryWithOutputSchema<
50
+ {
51
+ /**
52
+ * The search query to execute.
53
+ */
54
+ query: string;
55
+ },
56
+ Array<{
57
+ type: 'web_search_result';
58
+
59
+ /**
60
+ * The URL of the source page.
61
+ */
62
+ url: string;
63
+
64
+ /**
65
+ * The title of the source page.
66
+ */
67
+ title: string | null;
68
+
69
+ /**
70
+ * When the site was last updated
71
+ */
72
+ pageAge: string | null;
73
+
74
+ /**
75
+ * Encrypted content that must be passed back in multi-turn conversations for citations
76
+ */
77
+ encryptedContent: string;
78
+ }>,
79
+ {
80
+ /**
81
+ * Maximum number of web searches Claude can perform during the conversation.
82
+ */
83
+ maxUses?: number;
84
+
85
+ /**
86
+ * Optional list of domains that Claude is allowed to search.
87
+ */
88
+ allowedDomains?: string[];
89
+
90
+ /**
91
+ * Optional list of domains that Claude should avoid when searching.
92
+ */
93
+ blockedDomains?: string[];
94
+
95
+ /**
96
+ * Optional user location information to provide geographically relevant search results.
97
+ */
98
+ userLocation?: {
99
+ /**
100
+ * The type of location (must be approximate)
101
+ */
102
+ type: 'approximate';
103
+
104
+ /**
105
+ * The city name
106
+ */
107
+ city?: string;
108
+
109
+ /**
110
+ * The region or state
111
+ */
112
+ region?: string;
113
+
114
+ /**
115
+ * The country
116
+ */
117
+ country?: string;
118
+
119
+ /**
120
+ * The IANA timezone ID.
121
+ */
122
+ timezone?: string;
123
+ };
124
+ }
125
+ >({
126
+ id: 'anthropic.web_search_20260209',
127
+ inputSchema: webSearch_20260209InputSchema,
128
+ outputSchema: webSearch_20260209OutputSchema,
129
+ supportsDeferredResults: true,
130
+ });
131
+
132
+ export const webSearch_20260209 = (
133
+ args: Parameters<typeof factory>[0] = {}, // default
134
+ ) => {
135
+ return factory(args);
136
+ };