@ai-sdk/anthropic 4.0.58 → 4.0.60

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.
@@ -195,6 +195,48 @@ function getAnthropicCallerMetadata(
195
195
  : { providerMetadata: { anthropic: { caller: callerInfo } } };
196
196
  }
197
197
 
198
+ /**
199
+ * Maps the custom tool names of toolset tools (e.g. the computer toolset) to
200
+ * the toolset name that the Anthropic API uses in `toolset_name`.
201
+ */
202
+ function getAnthropicToolsetNames(
203
+ tools: LanguageModelV4CallOptions['tools'],
204
+ ): Record<string, string> {
205
+ const toolsetNames: Record<string, string> = {};
206
+
207
+ for (const tool of tools ?? []) {
208
+ if (
209
+ tool.type === 'provider' &&
210
+ tool.id === 'anthropic.computer_toolset_20260801'
211
+ ) {
212
+ toolsetNames[tool.name] = 'computer';
213
+ }
214
+ }
215
+
216
+ return toolsetNames;
217
+ }
218
+
219
+ /**
220
+ * Toolset member calls (e.g. `left_click` from the computer toolset) are
221
+ * exposed as a single tool call on the toolset tool. The member name is
222
+ * injected as the `action` property, matching the input shape of the older
223
+ * computer tool versions.
224
+ */
225
+ function toToolsetMemberInput({
226
+ memberName,
227
+ input,
228
+ }: {
229
+ memberName: string;
230
+ input: unknown;
231
+ }): JSONObject {
232
+ return {
233
+ action: memberName,
234
+ ...(typeof input === 'object' && input !== null && !Array.isArray(input)
235
+ ? (input as JSONObject)
236
+ : {}),
237
+ };
238
+ }
239
+
198
240
  function getProviderOptionsName(provider: string): string {
199
241
  const dotIndex = provider.indexOf('.');
200
242
  return dotIndex === -1 ? provider : provider.substring(0, dotIndex);
@@ -362,6 +404,8 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
362
404
  rejectsSamplingParameters,
363
405
  supportsXhighEffort,
364
406
  rejectsThinkingDisabledAboveHighEffort,
407
+ rejectsThinkingDisabled,
408
+ rejectsForcedToolUse,
365
409
  isKnownModel,
366
410
  } = getModelCapabilities(modelId);
367
411
 
@@ -414,10 +458,30 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
414
458
 
415
459
  const structureOutputMode =
416
460
  anthropicOptions?.structuredOutputMode ?? 'auto';
417
- const useStructuredOutput =
461
+ let useStructuredOutput =
418
462
  structureOutputMode === 'outputFormat' ||
419
463
  (structureOutputMode === 'auto' && supportsStructuredOutput);
420
464
 
465
+ // The JSON response tool relies on forced tool use, which some models
466
+ // reject (e.g. Opus 5.5, Fable 5.1). Fall back to native structured
467
+ // outputs when the model supports them.
468
+ if (
469
+ !useStructuredOutput &&
470
+ rejectsForcedToolUse &&
471
+ supportsStructuredOutput &&
472
+ responseFormat?.type === 'json' &&
473
+ responseFormat.schema != null
474
+ ) {
475
+ warnings.push({
476
+ type: 'unsupported',
477
+ feature: 'providerOptions.anthropic.structuredOutputMode',
478
+ details:
479
+ `structuredOutputMode 'jsonTool' is not supported by ${modelId} because it rejects forced tool use. ` +
480
+ `Using 'outputFormat' instead.`,
481
+ });
482
+ useStructuredOutput = true;
483
+ }
484
+
421
485
  const jsonResponseTool: LanguageModelV4FunctionTool | undefined =
422
486
  responseFormat?.type === 'json' &&
423
487
  responseFormat.schema != null &&
@@ -456,6 +520,8 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
456
520
  'anthropic.code_execution_20260120': 'code_execution',
457
521
  'anthropic.computer_20241022': 'computer',
458
522
  'anthropic.computer_20250124': 'computer',
523
+ 'anthropic.computer_20251124': 'computer',
524
+ 'anthropic.computer_toolset_20260801': 'computer',
459
525
  'anthropic.text_editor_20241022': 'str_replace_editor',
460
526
  'anthropic.text_editor_20250124': 'str_replace_editor',
461
527
  'anthropic.text_editor_20250429': 'str_replace_based_edit_tool',
@@ -475,12 +541,15 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
475
541
  },
476
542
  });
477
543
 
544
+ const toolsetNames = getAnthropicToolsetNames(tools);
545
+
478
546
  const { prompt: messagesPrompt, betas } = await convertToAnthropicPrompt({
479
547
  prompt,
480
548
  sendReasoning: anthropicOptions?.sendReasoning ?? true,
481
549
  warnings,
482
550
  cacheControlValidator,
483
551
  toolNameMapping,
552
+ toolsetNames,
484
553
  });
485
554
 
486
555
  /*
@@ -490,8 +559,10 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
490
559
  if (isCustomReasoning(reasoning) && anthropicOptions?.effort == null) {
491
560
  const reasoningConfig = resolveAnthropicReasoningConfig({
492
561
  reasoning,
562
+ modelId,
493
563
  supportsAdaptiveThinking,
494
564
  supportsXhighEffort,
565
+ rejectsThinkingDisabled,
495
566
  maxOutputTokensForModel,
496
567
  warnings,
497
568
  });
@@ -508,6 +579,33 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
508
579
  }
509
580
  }
510
581
 
582
+ // Some models always run adaptive thinking and reject `disabled` and
583
+ // budget-based `enabled` thinking with a 400. Drop the unsupported
584
+ // setting and keep the request adaptive so it still succeeds.
585
+ if (rejectsThinkingDisabled && anthropicOptions?.thinking != null) {
586
+ const thinking = anthropicOptions.thinking;
587
+
588
+ if (thinking.type === 'disabled') {
589
+ warnings.push({
590
+ type: 'unsupported',
591
+ feature: 'providerOptions.anthropic.thinking',
592
+ details:
593
+ `thinking cannot be disabled for ${modelId}; it always uses adaptive thinking. ` +
594
+ `The thinking setting has been removed. Lower 'effort' to reduce thinking.`,
595
+ });
596
+ anthropicOptions.thinking = undefined;
597
+ } else if (thinking.type === 'enabled') {
598
+ warnings.push({
599
+ type: 'unsupported',
600
+ feature: 'providerOptions.anthropic.thinking',
601
+ details:
602
+ `budget-based thinking is not supported by ${modelId}; it always uses adaptive thinking. ` +
603
+ `Using adaptive thinking instead. Use 'effort' to control how much the model thinks.`,
604
+ });
605
+ anthropicOptions.thinking = { type: 'adaptive' };
606
+ }
607
+ }
608
+
511
609
  // Newer models only allow disabling thinking at effort levels up to and
512
610
  // including `high`; at `xhigh` and `max` the API returns a 400. Lower
513
611
  // the effort to `high` to preserve the explicit request to run without
@@ -671,6 +769,16 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
671
769
  system: messagesPrompt.system,
672
770
  messages: messagesPrompt.messages,
673
771
 
772
+ ...(anthropicOptions?.safeguards &&
773
+ anthropicOptions.safeguards.length > 0 && {
774
+ safeguards: anthropicOptions.safeguards.map(safeguard => ({
775
+ type: safeguard.type,
776
+ ...(safeguard.classifierContext !== undefined && {
777
+ classifier_context: safeguard.classifierContext,
778
+ }),
779
+ })),
780
+ }),
781
+
674
782
  ...(contextManagement && {
675
783
  context_management: {
676
784
  edits: contextManagement.edits
@@ -810,6 +918,13 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
810
918
  betas.add('mcp-client-2025-04-04');
811
919
  }
812
920
 
921
+ if (
922
+ anthropicOptions?.safeguards &&
923
+ anthropicOptions.safeguards.length > 0
924
+ ) {
925
+ betas.add('dangerous-tool-use-2026-09-03');
926
+ }
927
+
813
928
  if (contextManagement) {
814
929
  betas.add('context-management-2025-06-27');
815
930
 
@@ -886,6 +1001,7 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
886
1001
  supportsStructuredOutput: false,
887
1002
  supportsStrictTools,
888
1003
  defaultEagerInputStreaming,
1004
+ rejectsForcedToolUse,
889
1005
  }
890
1006
  : {
891
1007
  tools: tools ?? [],
@@ -895,6 +1011,7 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
895
1011
  supportsStructuredOutput,
896
1012
  supportsStrictTools,
897
1013
  defaultEagerInputStreaming,
1014
+ rejectsForcedToolUse,
898
1015
  },
899
1016
  );
900
1017
 
@@ -1167,6 +1284,27 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
1167
1284
  type: 'text',
1168
1285
  text: JSON.stringify(part.input),
1169
1286
  });
1287
+ } else if (part.toolset_name != null) {
1288
+ // toolset member calls (e.g. the computer toolset) are mapped to
1289
+ // the toolset tool with the member name as the `action`:
1290
+ content.push({
1291
+ type: 'tool-call',
1292
+ toolCallId: part.id,
1293
+ toolName: toolNameMapping.toCustomToolName(part.toolset_name),
1294
+ input: JSON.stringify(
1295
+ toToolsetMemberInput({
1296
+ memberName: part.name,
1297
+ input: part.input,
1298
+ }),
1299
+ ),
1300
+ providerMetadata: {
1301
+ anthropic: {
1302
+ toolsetName: part.toolset_name,
1303
+ ...getAnthropicCallerMetadata(part.caller).providerMetadata
1304
+ ?.anthropic,
1305
+ },
1306
+ },
1307
+ });
1170
1308
  } else {
1171
1309
  content.push({
1172
1310
  type: 'tool-call',
@@ -1560,6 +1698,9 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
1560
1698
  ...(response.input_transformations != null
1561
1699
  ? { inputTransformations: response.input_transformations }
1562
1700
  : {}),
1701
+ ...(response.safeguard_results != null
1702
+ ? { safeguardResults: response.safeguard_results }
1703
+ : {}),
1563
1704
 
1564
1705
  iterations: response.usage.iterations
1565
1706
  ? response.usage.iterations.map(
@@ -1676,6 +1817,12 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
1676
1817
  firstDelta: boolean;
1677
1818
  providerToolName?: string;
1678
1819
  providerToolInputType?: string;
1820
+ /**
1821
+ * Set for toolset member calls (e.g. the computer toolset). The raw
1822
+ * member input is accumulated and emitted as a single delta with the
1823
+ * member name injected as `action` when the block completes.
1824
+ */
1825
+ toolset?: { name: string; memberName: string };
1679
1826
  caller?: {
1680
1827
  type:
1681
1828
  | 'code_execution_20250825'
@@ -1697,6 +1844,7 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
1697
1844
  let stopSequence: string | null = null;
1698
1845
  let stopDetails: AnthropicMessageMetadata['stopDetails'] = undefined;
1699
1846
  let inputTransformations: AnthropicMessageMetadata['inputTransformations'];
1847
+ let safeguardResults: AnthropicMessageMetadata['safeguardResults'];
1700
1848
  let container: AnthropicMessageMetadata['container'] | null = null;
1701
1849
  let isJsonResponseFromTool = false;
1702
1850
  let isMessageOpen = false;
@@ -1841,6 +1989,34 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
1841
1989
  type: 'text-start',
1842
1990
  id: String(value.index),
1843
1991
  });
1992
+ } else if (part.toolset_name != null) {
1993
+ const callerInfo = getAnthropicCallerInfo(part.caller);
1994
+ const customToolName = toolNameMapping.toCustomToolName(
1995
+ part.toolset_name,
1996
+ );
1997
+ const hasNonEmptyInput =
1998
+ part.input && Object.keys(part.input).length > 0;
1999
+
2000
+ // toolset member input is accumulated and emitted when the
2001
+ // block completes (see content_block_stop):
2002
+ contentBlocks[value.index] = {
2003
+ type: 'tool-call',
2004
+ toolCallId: part.id,
2005
+ toolName: customToolName,
2006
+ input: hasNonEmptyInput ? JSON.stringify(part.input) : '',
2007
+ firstDelta: true,
2008
+ toolset: {
2009
+ name: part.toolset_name,
2010
+ memberName: part.name,
2011
+ },
2012
+ ...(callerInfo && { caller: callerInfo }),
2013
+ };
2014
+
2015
+ controller.enqueue({
2016
+ type: 'tool-input-start',
2017
+ id: part.id,
2018
+ toolName: customToolName,
2019
+ });
1844
2020
  } else {
1845
2021
  const callerInfo = getAnthropicCallerInfo(part.caller);
1846
2022
 
@@ -2331,6 +2507,37 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
2331
2507
  usesJsonResponseTool && contentBlock.toolName === 'json';
2332
2508
 
2333
2509
  if (!isJsonResponseTool) {
2510
+ // toolset member calls: emit the accumulated input with
2511
+ // the member name injected as `action` in one delta.
2512
+ if (contentBlock.toolset != null) {
2513
+ let memberInput: unknown = {};
2514
+ try {
2515
+ memberInput =
2516
+ contentBlock.input === ''
2517
+ ? {}
2518
+ : secureJsonParse(contentBlock.input);
2519
+ } catch {
2520
+ // ignore parse errors, fall back to the raw input
2521
+ memberInput = undefined;
2522
+ }
2523
+
2524
+ contentBlock.input =
2525
+ memberInput === undefined
2526
+ ? contentBlock.input
2527
+ : JSON.stringify(
2528
+ toToolsetMemberInput({
2529
+ memberName: contentBlock.toolset.memberName,
2530
+ input: memberInput,
2531
+ }),
2532
+ );
2533
+
2534
+ controller.enqueue({
2535
+ type: 'tool-input-delta',
2536
+ id: contentBlock.toolCallId,
2537
+ delta: contentBlock.input,
2538
+ });
2539
+ }
2540
+
2334
2541
  controller.enqueue({
2335
2542
  type: 'tool-input-end',
2336
2543
  id: contentBlock.toolCallId,
@@ -2373,10 +2580,15 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
2373
2580
  contentBlock.providerToolName === 'code_execution'
2374
2581
  ? { dynamic: true }
2375
2582
  : {}),
2376
- ...(contentBlock.caller && {
2583
+ ...((contentBlock.caller || contentBlock.toolset) && {
2377
2584
  providerMetadata: {
2378
2585
  anthropic: {
2379
- caller: contentBlock.caller,
2586
+ ...(contentBlock.toolset && {
2587
+ toolsetName: contentBlock.toolset.name,
2588
+ }),
2589
+ ...(contentBlock.caller && {
2590
+ caller: contentBlock.caller,
2591
+ }),
2380
2592
  },
2381
2593
  },
2382
2594
  }),
@@ -2478,6 +2690,13 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
2478
2690
  return;
2479
2691
  }
2480
2692
 
2693
+ // toolset member input is emitted as a single delta once
2694
+ // the block is complete (the member name is injected):
2695
+ if (contentBlock.toolset != null) {
2696
+ contentBlock.input += delta;
2697
+ return;
2698
+ }
2699
+
2481
2700
  // for the code execution 20250825, we need to add
2482
2701
  // the type to the delta and change the tool name.
2483
2702
  if (
@@ -2701,6 +2920,12 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
2701
2920
  inputTransformations = value.input_transformations;
2702
2921
  }
2703
2922
 
2923
+ // Earlier deltas may carry null while the classifier is still
2924
+ // running; the last non-null value is the final verdict.
2925
+ if (value.delta.safeguard_results != null) {
2926
+ safeguardResults = value.delta.safeguard_results;
2927
+ }
2928
+
2704
2929
  rawUsage = {
2705
2930
  ...rawUsage,
2706
2931
  ...(value.usage as JSONObject),
@@ -2720,6 +2945,7 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
2720
2945
  ...(inputTransformations != null
2721
2946
  ? { inputTransformations }
2722
2947
  : {}),
2948
+ ...(safeguardResults != null ? { safeguardResults } : {}),
2723
2949
  iterations: usage.iterations
2724
2950
  ? usage.iterations.map(
2725
2951
  iter =>
@@ -2844,9 +3070,30 @@ export function getModelCapabilities(modelId: string): {
2844
3070
  rejectsSamplingParameters: boolean;
2845
3071
  supportsXhighEffort: boolean;
2846
3072
  rejectsThinkingDisabledAboveHighEffort: boolean;
3073
+ /**
3074
+ * Thinking is always adaptive: `thinking.type` `disabled` and `enabled`
3075
+ * are rejected with a 400.
3076
+ */
3077
+ rejectsThinkingDisabled: boolean;
3078
+ /**
3079
+ * Forced tool use (`tool_choice` `any` or a named tool) is rejected with a 400.
3080
+ */
3081
+ rejectsForcedToolUse: boolean;
2847
3082
  isKnownModel: boolean;
2848
3083
  } {
2849
- if (modelId.includes('claude-opus-5')) {
3084
+ if (modelId.includes('claude-opus-5-5')) {
3085
+ return {
3086
+ maxOutputTokens: 128000,
3087
+ supportsStructuredOutput: true,
3088
+ supportsAdaptiveThinking: true,
3089
+ rejectsSamplingParameters: true,
3090
+ supportsXhighEffort: true,
3091
+ rejectsThinkingDisabledAboveHighEffort: true,
3092
+ rejectsThinkingDisabled: true,
3093
+ rejectsForcedToolUse: true,
3094
+ isKnownModel: true,
3095
+ };
3096
+ } else if (modelId.includes('claude-opus-5')) {
2850
3097
  return {
2851
3098
  maxOutputTokens: 128000,
2852
3099
  supportsStructuredOutput: true,
@@ -2854,12 +3101,37 @@ export function getModelCapabilities(modelId: string): {
2854
3101
  rejectsSamplingParameters: true,
2855
3102
  supportsXhighEffort: true,
2856
3103
  rejectsThinkingDisabledAboveHighEffort: true,
3104
+ rejectsThinkingDisabled: false,
3105
+ rejectsForcedToolUse: false,
3106
+ isKnownModel: true,
3107
+ };
3108
+ } else if (modelId.includes('claude-fable-5-1')) {
3109
+ return {
3110
+ maxOutputTokens: 128000,
3111
+ supportsStructuredOutput: true,
3112
+ supportsAdaptiveThinking: true,
3113
+ rejectsSamplingParameters: true,
3114
+ supportsXhighEffort: true,
3115
+ rejectsThinkingDisabledAboveHighEffort: false,
3116
+ rejectsThinkingDisabled: true,
3117
+ rejectsForcedToolUse: true,
3118
+ isKnownModel: true,
3119
+ };
3120
+ } else if (modelId.includes('claude-fable-5')) {
3121
+ return {
3122
+ maxOutputTokens: 128000,
3123
+ supportsStructuredOutput: true,
3124
+ supportsAdaptiveThinking: true,
3125
+ rejectsSamplingParameters: true,
3126
+ supportsXhighEffort: true,
3127
+ rejectsThinkingDisabledAboveHighEffort: false,
3128
+ rejectsThinkingDisabled: true,
3129
+ rejectsForcedToolUse: false,
2857
3130
  isKnownModel: true,
2858
3131
  };
2859
3132
  } else if (
2860
3133
  modelId.includes('claude-opus-4-8') ||
2861
3134
  modelId.includes('claude-opus-4-7') ||
2862
- modelId.includes('claude-fable-5') ||
2863
3135
  modelId.includes('claude-sonnet-5')
2864
3136
  ) {
2865
3137
  return {
@@ -2869,6 +3141,8 @@ export function getModelCapabilities(modelId: string): {
2869
3141
  rejectsSamplingParameters: true,
2870
3142
  supportsXhighEffort: true,
2871
3143
  rejectsThinkingDisabledAboveHighEffort: false,
3144
+ rejectsThinkingDisabled: false,
3145
+ rejectsForcedToolUse: false,
2872
3146
  isKnownModel: true,
2873
3147
  };
2874
3148
  } else if (
@@ -2882,6 +3156,8 @@ export function getModelCapabilities(modelId: string): {
2882
3156
  rejectsSamplingParameters: false,
2883
3157
  supportsXhighEffort: false,
2884
3158
  rejectsThinkingDisabledAboveHighEffort: false,
3159
+ rejectsThinkingDisabled: false,
3160
+ rejectsForcedToolUse: false,
2885
3161
  isKnownModel: true,
2886
3162
  };
2887
3163
  } else if (
@@ -2896,6 +3172,8 @@ export function getModelCapabilities(modelId: string): {
2896
3172
  rejectsSamplingParameters: false,
2897
3173
  supportsXhighEffort: false,
2898
3174
  rejectsThinkingDisabledAboveHighEffort: false,
3175
+ rejectsThinkingDisabled: false,
3176
+ rejectsForcedToolUse: false,
2899
3177
  isKnownModel: true,
2900
3178
  };
2901
3179
  } else if (modelId.includes('claude-opus-4-1')) {
@@ -2906,6 +3184,8 @@ export function getModelCapabilities(modelId: string): {
2906
3184
  rejectsSamplingParameters: false,
2907
3185
  supportsXhighEffort: false,
2908
3186
  rejectsThinkingDisabledAboveHighEffort: false,
3187
+ rejectsThinkingDisabled: false,
3188
+ rejectsForcedToolUse: false,
2909
3189
  isKnownModel: true,
2910
3190
  };
2911
3191
  } else if (/claude-sonnet-4(?:-|@)/.test(modelId)) {
@@ -2916,6 +3196,8 @@ export function getModelCapabilities(modelId: string): {
2916
3196
  rejectsSamplingParameters: false,
2917
3197
  supportsXhighEffort: false,
2918
3198
  rejectsThinkingDisabledAboveHighEffort: false,
3199
+ rejectsThinkingDisabled: false,
3200
+ rejectsForcedToolUse: false,
2919
3201
  isKnownModel: true,
2920
3202
  };
2921
3203
  } else if (/claude-opus-4(?:-|@)/.test(modelId)) {
@@ -2926,6 +3208,8 @@ export function getModelCapabilities(modelId: string): {
2926
3208
  rejectsSamplingParameters: false,
2927
3209
  supportsXhighEffort: false,
2928
3210
  rejectsThinkingDisabledAboveHighEffort: false,
3211
+ rejectsThinkingDisabled: false,
3212
+ rejectsForcedToolUse: false,
2929
3213
  isKnownModel: true,
2930
3214
  };
2931
3215
  } else if (modelId.includes('claude-3-haiku')) {
@@ -2936,6 +3220,8 @@ export function getModelCapabilities(modelId: string): {
2936
3220
  rejectsSamplingParameters: false,
2937
3221
  supportsXhighEffort: false,
2938
3222
  rejectsThinkingDisabledAboveHighEffort: false,
3223
+ rejectsThinkingDisabled: false,
3224
+ rejectsForcedToolUse: false,
2939
3225
  isKnownModel: true,
2940
3226
  };
2941
3227
  } else if (
@@ -2948,6 +3234,8 @@ export function getModelCapabilities(modelId: string): {
2948
3234
  rejectsSamplingParameters: false,
2949
3235
  supportsXhighEffort: false,
2950
3236
  rejectsThinkingDisabledAboveHighEffort: false,
3237
+ rejectsThinkingDisabled: false,
3238
+ rejectsForcedToolUse: false,
2951
3239
  isKnownModel: false,
2952
3240
  };
2953
3241
  } else if (modelId.includes('claude-')) {
@@ -2961,6 +3249,8 @@ export function getModelCapabilities(modelId: string): {
2961
3249
  rejectsSamplingParameters: true,
2962
3250
  supportsXhighEffort: true,
2963
3251
  rejectsThinkingDisabledAboveHighEffort: true,
3252
+ rejectsThinkingDisabled: false,
3253
+ rejectsForcedToolUse: false,
2964
3254
  isKnownModel: false,
2965
3255
  };
2966
3256
  } else {
@@ -2973,6 +3263,8 @@ export function getModelCapabilities(modelId: string): {
2973
3263
  rejectsSamplingParameters: false,
2974
3264
  supportsXhighEffort: false,
2975
3265
  rejectsThinkingDisabledAboveHighEffort: false,
3266
+ rejectsThinkingDisabled: false,
3267
+ rejectsForcedToolUse: false,
2976
3268
  isKnownModel: false,
2977
3269
  };
2978
3270
  }
@@ -3012,14 +3304,18 @@ export function hasDynamicFilteringWebToolWithoutCodeExecution(
3012
3304
 
3013
3305
  function resolveAnthropicReasoningConfig({
3014
3306
  reasoning,
3307
+ modelId,
3015
3308
  supportsAdaptiveThinking,
3016
3309
  supportsXhighEffort,
3310
+ rejectsThinkingDisabled,
3017
3311
  maxOutputTokensForModel,
3018
3312
  warnings,
3019
3313
  }: {
3020
3314
  reasoning: LanguageModelV4CallOptions['reasoning'];
3315
+ modelId: string;
3021
3316
  supportsAdaptiveThinking: boolean;
3022
3317
  supportsXhighEffort: boolean;
3318
+ rejectsThinkingDisabled: boolean;
3023
3319
  maxOutputTokensForModel: number;
3024
3320
  warnings: SharedV4Warning[];
3025
3321
  }): Pick<AnthropicLanguageModelOptions, 'thinking' | 'effort'> | undefined {
@@ -3028,6 +3324,20 @@ function resolveAnthropicReasoningConfig({
3028
3324
  }
3029
3325
 
3030
3326
  if (reasoning === 'none') {
3327
+ // Models that always run adaptive thinking cannot disable it. Use low
3328
+ // effort to keep thinking short instead of sending a request that the
3329
+ // API would reject.
3330
+ if (rejectsThinkingDisabled) {
3331
+ warnings.push({
3332
+ type: 'compatibility',
3333
+ feature: 'reasoning',
3334
+ details:
3335
+ `reasoning 'none' is not supported by ${modelId}; it always uses adaptive thinking. ` +
3336
+ `Using effort 'low' to minimize thinking instead.`,
3337
+ });
3338
+ return { effort: 'low' };
3339
+ }
3340
+
3031
3341
  return { thinking: { type: 'disabled' } };
3032
3342
  }
3033
3343
 
@@ -1,4 +1,5 @@
1
1
  import type { JSONObject } from '@ai-sdk/provider';
2
+ import type { AnthropicSafeguardResult } from './anthropic-api';
2
3
 
3
4
  /**
4
5
  * Represents a single iteration in the usage breakdown.
@@ -62,6 +63,19 @@ export interface AnthropicMessageMetadata {
62
63
  reason: string;
63
64
  }>;
64
65
 
66
+ /**
67
+ * Verdicts of the server-side safeguards requested with
68
+ * `providerOptions.anthropic.safeguards`, one entry per requested safeguard.
69
+ * For `dangerous_tool_use`, `status.type` is `'available'` when the
70
+ * classifier ran and `status.tool_uses` maps each tool call id to its
71
+ * verdict (`type: 'evaluated'` with an `outcome` of `'not_flagged'` or
72
+ * `'flagged'` plus an `explanation`, `'skipped'`, or `'unavailable'`).
73
+ *
74
+ * Kept in the API's wire shape (snake_case), since consumers such as Claude
75
+ * Code read the verdicts verbatim.
76
+ */
77
+ safeguardResults?: AnthropicSafeguardResult[];
78
+
65
79
  /**
66
80
  * Details about why the request stopped. Present only when the API returns
67
81
  * a `refusal` stop reason together with a `stop_details` object (a