@ai-sdk/anthropic 3.0.125 → 3.0.127

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.
@@ -399,6 +399,57 @@ const servedByFallback = iterations?.some(
399
399
  console.log('Served by fallback:', servedByFallback);
400
400
  ```
401
401
 
402
+ ### Dangerous Tool Use Safeguard
403
+
404
+ The `safeguards` option asks the API to run additional server-side checks as part of the request. The `dangerous_tool_use` safeguard classifies every `tool_use` block in the response for dangerous actions, such as destructive shell commands or data exfiltration, and returns a verdict per tool call. This is the check that Claude Code's auto mode relies on. The `dangerous-tool-use-2026-09-03` beta header is added for you.
405
+
406
+ ```ts highlight="9-16"
407
+ import { anthropic, AnthropicLanguageModelOptions } from '@ai-sdk/anthropic';
408
+ import { generateText, tool } from 'ai';
409
+ import { z } from 'zod';
410
+
411
+ const result = await generateText({
412
+ model: anthropic('claude-sonnet-4-5'),
413
+ prompt: 'Use the bash tool to run: echo hello',
414
+ tools: { bash: tool({ inputSchema: z.object({ command: z.string() }) }) },
415
+ providerOptions: {
416
+ anthropic: {
417
+ safeguards: [
418
+ {
419
+ type: 'dangerous_tool_use',
420
+ classifierContext: { v: 1, permission_mode: 'auto' },
421
+ },
422
+ ],
423
+ } satisfies AnthropicLanguageModelOptions,
424
+ },
425
+ });
426
+
427
+ console.log(result.providerMetadata?.anthropic?.safeguardResults);
428
+ ```
429
+
430
+ The verdicts are available on `providerMetadata.anthropic.safeguardResults`, one entry per requested safeguard, in the API's wire shape:
431
+
432
+ ```json
433
+ [
434
+ {
435
+ "type": "dangerous_tool_use",
436
+ "status": {
437
+ "type": "available",
438
+ "tool_uses": {
439
+ "toolu_01Ti4QpUfhLV6QqCTFVY8C7w": {
440
+ "type": "evaluated",
441
+ "outcome": "not_flagged"
442
+ }
443
+ }
444
+ }
445
+ }
446
+ ]
447
+ ```
448
+
449
+ `status.type` is `available` when the classifier ran; `unsupported` means the API key is not enabled for the beta. Inside `status.tool_uses`, each tool call id maps to `evaluated` (with an `outcome` of `not_flagged` or `flagged`, the latter with an `explanation` such as `[Data Exfiltration]`), `skipped`, or `unavailable` (a transient classifier failure). When streaming, the verdicts arrive on the final `message_delta` event and are exposed on the `finish` part's provider metadata.
450
+
451
+ Anthropic's platform reference does not document the beta yet; the feature is described from the client side in the Claude Code [auto mode classifier](https://code.claude.com/docs/en/auto-mode-classifier-billing) and [gateway compatibility](https://code.claude.com/docs/en/llm-gateway-protocol#feature-pass-through) guides.
452
+
402
453
  ### Reasoning
403
454
 
404
455
  Anthropic models support extended thinking, where Claude shows its reasoning process before providing a final answer.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/anthropic",
3
- "version": "3.0.125",
3
+ "version": "3.0.127",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@ai-sdk/provider": "3.0.18",
40
- "@ai-sdk/provider-utils": "4.0.56"
40
+ "@ai-sdk/provider-utils": "4.0.57"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "20.17.24",
@@ -1,4 +1,5 @@
1
1
  import type { JSONObject } from '@ai-sdk/provider';
2
+ import type { AnthropicSafeguardResult } from './anthropic-messages-api';
2
3
 
3
4
  /**
4
5
  * Represents a single iteration in the usage breakdown.
@@ -65,6 +66,19 @@ export interface AnthropicMessageMetadata {
65
66
  reason: string;
66
67
  }>;
67
68
 
69
+ /**
70
+ * Verdicts of the server-side safeguards requested with
71
+ * `providerOptions.anthropic.safeguards`, one entry per requested safeguard.
72
+ * For `dangerous_tool_use`, `status.type` is `'available'` when the
73
+ * classifier ran and `status.tool_uses` maps each tool call id to its
74
+ * verdict (`type: 'evaluated'` with an `outcome` of `'not_flagged'` or
75
+ * `'flagged'` plus an `explanation`, `'skipped'`, or `'unavailable'`).
76
+ *
77
+ * Kept in the API's wire shape (snake_case), since consumers such as Claude
78
+ * Code read the verdicts verbatim.
79
+ */
80
+ safeguardResults?: AnthropicSafeguardResult[];
81
+
68
82
  /**
69
83
  * Details about why the request stopped. Present only when the API returns
70
84
  * a `refusal` stop reason together with a `stop_details` object (a
@@ -649,6 +649,27 @@ const anthropicStopDetailsSchema = z.object({
649
649
 
650
650
  export type AnthropicStopDetails = z.infer<typeof anthropicStopDetailsSchema>;
651
651
 
652
+ const anthropicSafeguardResultSchema = z.object({
653
+ type: z.string(),
654
+ status: z.object({
655
+ type: z.string(),
656
+ tool_uses: z
657
+ .record(
658
+ z.string(),
659
+ z.object({
660
+ type: z.string(),
661
+ outcome: z.string().nullish(),
662
+ explanation: z.string().nullish(),
663
+ }),
664
+ )
665
+ .nullish(),
666
+ }),
667
+ });
668
+
669
+ export type AnthropicSafeguardResult = z.infer<
670
+ typeof anthropicSafeguardResultSchema
671
+ >;
672
+
652
673
  const anthropicToolCallCallerSchema = z.union([
653
674
  z.object({
654
675
  type: z.literal('code_execution_20250825'),
@@ -963,6 +984,7 @@ export const anthropicMessagesResponseSchema = lazySchema(() =>
963
984
  input_transformations: z
964
985
  .array(anthropicInputTransformationSchema)
965
986
  .nullish(),
987
+ safeguard_results: z.array(anthropicSafeguardResultSchema).nullish(),
966
988
  usage: z.looseObject({
967
989
  input_tokens: z.number(),
968
990
  output_tokens: z.number(),
@@ -1397,6 +1419,7 @@ export const anthropicMessagesChunkSchema = lazySchema(() =>
1397
1419
  stop_reason: z.string().nullish(),
1398
1420
  stop_sequence: z.string().nullish(),
1399
1421
  stop_details: anthropicStopDetailsSchema.nullish(),
1422
+ safeguard_results: z.array(anthropicSafeguardResultSchema).nullish(),
1400
1423
  container: z
1401
1424
  .object({
1402
1425
  expires_at: z.string(),
@@ -702,6 +702,16 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
702
702
  system: messagesPrompt.system,
703
703
  messages: messagesPrompt.messages,
704
704
 
705
+ ...(anthropicOptions?.safeguards &&
706
+ anthropicOptions.safeguards.length > 0 && {
707
+ safeguards: anthropicOptions.safeguards.map(safeguard => ({
708
+ type: safeguard.type,
709
+ ...(safeguard.classifierContext !== undefined && {
710
+ classifier_context: safeguard.classifierContext,
711
+ }),
712
+ })),
713
+ }),
714
+
705
715
  ...(contextManagement && {
706
716
  context_management: {
707
717
  edits: contextManagement.edits
@@ -841,6 +851,13 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
841
851
  betas.add('mcp-client-2025-04-04');
842
852
  }
843
853
 
854
+ if (
855
+ anthropicOptions?.safeguards &&
856
+ anthropicOptions.safeguards.length > 0
857
+ ) {
858
+ betas.add('dangerous-tool-use-2026-09-03');
859
+ }
860
+
844
861
  if (contextManagement) {
845
862
  betas.add('context-management-2025-06-27');
846
863
 
@@ -1588,6 +1605,9 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
1588
1605
  ...(response.input_transformations != null
1589
1606
  ? { inputTransformations: response.input_transformations }
1590
1607
  : {}),
1608
+ ...(response.safeguard_results != null
1609
+ ? { safeguardResults: response.safeguard_results }
1610
+ : {}),
1591
1611
 
1592
1612
  iterations: response.usage.iterations
1593
1613
  ? response.usage.iterations.map(
@@ -1732,6 +1752,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
1732
1752
  let stopSequence: string | null = null;
1733
1753
  let stopDetails: AnthropicMessageMetadata['stopDetails'] = undefined;
1734
1754
  let inputTransformations: AnthropicMessageMetadata['inputTransformations'];
1755
+ let safeguardResults: AnthropicMessageMetadata['safeguardResults'];
1735
1756
  let container: AnthropicMessageMetadata['container'] | null = null;
1736
1757
  let isJsonResponseFromTool = false;
1737
1758
  let isMessageOpen = false;
@@ -2802,6 +2823,12 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
2802
2823
  inputTransformations = value.input_transformations;
2803
2824
  }
2804
2825
 
2826
+ // Earlier deltas may carry null while the classifier is still
2827
+ // running; the last non-null value is the final verdict.
2828
+ if (value.delta.safeguard_results != null) {
2829
+ safeguardResults = value.delta.safeguard_results;
2830
+ }
2831
+
2805
2832
  rawUsage = {
2806
2833
  ...rawUsage,
2807
2834
  ...(value.usage as JSONObject),
@@ -2822,6 +2849,7 @@ export class AnthropicMessagesLanguageModel implements LanguageModelV3 {
2822
2849
  ...(inputTransformations != null
2823
2850
  ? { inputTransformations }
2824
2851
  : {}),
2852
+ ...(safeguardResults != null ? { safeguardResults } : {}),
2825
2853
  iterations: usage.iterations
2826
2854
  ? usage.iterations.map(
2827
2855
  iter =>
@@ -364,6 +364,25 @@ export const anthropicLanguageModelOptions = z.object({
364
364
  */
365
365
  anthropicBeta: z.array(z.string()).optional(),
366
366
 
367
+ /**
368
+ * Server-side safeguards to run as part of the request.
369
+ *
370
+ * `dangerous_tool_use` asks the API to classify every `tool_use` block in
371
+ * the response for dangerous actions (the check Claude Code's auto mode
372
+ * relies on). The per-call verdicts are returned in
373
+ * `providerMetadata.anthropic.safeguardResults`, keyed by tool call id.
374
+ * `classifierContext` is passed through to the API as `classifier_context`.
375
+ * The `dangerous-tool-use-2026-09-03` beta is added automatically.
376
+ */
377
+ safeguards: z
378
+ .array(
379
+ z.object({
380
+ type: z.literal('dangerous_tool_use'),
381
+ classifierContext: z.record(z.string(), z.unknown()).optional(),
382
+ }),
383
+ )
384
+ .optional(),
385
+
367
386
  contextManagement: z
368
387
  .object({
369
388
  edits: z.array(