@ai-sdk/anthropic 4.0.65 → 4.0.67

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.
@@ -179,7 +179,7 @@ import { streamText, tool } from 'ai';
179
179
  import { z } from 'zod';
180
180
 
181
181
  const result = streamText({
182
- model: anthropic('claude-sonnet-5'),
182
+ model: anthropic('claude-sonnet-5-5'),
183
183
  tools: {
184
184
  writeFile: tool({
185
185
  description: 'Write content to a file',
@@ -197,9 +197,10 @@ const result = streamText({
197
197
  });
198
198
  ```
199
199
 
200
- For `claude-opus-5-5` and `claude-fable-5-1`, the default `"auto"` mode uses
201
- native structured outputs through `output_config.format`. These models reject
202
- forced tool use, so the `"jsonTool"` mode cannot be used with them. If you
200
+ For `claude-sonnet-5-5`, `claude-opus-5-5`, and `claude-fable-5-1`, the
201
+ default `"auto"` mode uses native structured outputs through
202
+ `output_config.format`. These models reject forced tool use, so the
203
+ `"jsonTool"` mode cannot be used with them. If you
203
204
  request `"jsonTool"` anyway, the provider falls back to `"outputFormat"` and
204
205
  emits a warning. Likewise, a `required` or named `toolChoice` is downgraded to
205
206
  `auto` with a warning; instruct the model to use the tool in your prompt and
@@ -207,7 +208,7 @@ verify that a tool call was made.
207
208
 
208
209
  ### Effort
209
210
 
210
- Anthropic introduced an `effort` option with `claude-opus-4-5` that affects thinking, text responses, and function calls. Effort defaults to `high` and you can set it to `medium` or `low` to save tokens and to lower time-to-last-token latency (TTLT). `claude-opus-4-7`, `claude-opus-4-8`, `claude-opus-5`, `claude-opus-5-5`, `claude-fable-5`, `claude-fable-5-1`, and `claude-sonnet-5` additionally support `xhigh` for maximum reasoning effort.
211
+ Anthropic introduced an `effort` option with `claude-opus-4-5` that affects thinking, text responses, and function calls. Effort defaults to `high` and you can set it to `medium` or `low` to save tokens and to lower time-to-last-token latency (TTLT). `claude-opus-4-7`, `claude-opus-4-8`, `claude-opus-5`, `claude-opus-5-5`, `claude-fable-5`, `claude-fable-5-1`, `claude-sonnet-5`, and `claude-sonnet-5-5` additionally support `xhigh` for maximum reasoning effort.
211
212
 
212
213
  On `claude-opus-5`, thinking can only be disabled at effort levels up to and including `high`. When you combine `thinking: { type: 'disabled' }` with `effort: 'xhigh'` or `effort: 'max'`, the AI SDK lowers the effort to `high` and emits a warning instead of sending a request that the API would reject.
213
214
 
@@ -218,6 +219,16 @@ carrying over the setting you used on `claude-opus-5`. Set `maxOutputTokens`
218
219
  with room for thinking as well as the reply; thinking counts toward the limit
219
220
  even when its content is not returned.
220
221
 
222
+ On `claude-sonnet-5-5`, thinking cannot be turned off either, and the API
223
+ default effort is `high`. Its effort levels are not equivalent to those of
224
+ `claude-sonnet-5`, so test several levels rather than carrying over your
225
+ current setting. For agentic coding and other multi-step tool use, start at
226
+ `medium` for well-specified tasks and move to `high` for harder ones. The
227
+ lowest thinking setting, [`between_tools`](#between-tools-thinking), is only
228
+ accepted at `low`, `medium`, and `high` effort. When you combine it with
229
+ `effort: 'xhigh'` or `effort: 'max'`, the AI SDK lowers the effort to `high`
230
+ and emits a warning.
231
+
221
232
  ```ts highlight="8-10"
222
233
  import { anthropic, AnthropicLanguageModelOptions } from '@ai-sdk/anthropic';
223
234
  import { generateText } from 'ai';
@@ -322,6 +333,8 @@ The `inferenceGeo` option accepts `'us'` (US-only infrastructure) or `'global'`
322
333
 
323
334
  Claude Fable 5 has safeguards that limit its performance in certain areas like cybersecurity, biology, and chemistry, and automated safety checks are run on every request.
324
335
 
336
+ Claude Sonnet 5.5 also has safety classifiers enabled, including for cybersecurity, biology, and reasoning extraction. Requests that ask the model to reproduce its internal reasoning in the response text can be declined with the `reasoning_extraction` category; use `thinking: { type: 'adaptive', display: 'summarized' }` and read the reasoning parts instead.
337
+
325
338
  When one of these checks blocks a request, the API does not answer it. Instead it returns a classifier block: a `200` response with a `refusal` stop reason and an optional `stop_details` object describing the category that triggered the block. The AI SDK surfaces this as a `content-filter` finish reason, with the details available on `providerMetadata.anthropic.stopDetails`.
326
339
 
327
340
  A classifier block looks like this:
@@ -405,7 +418,7 @@ import { generateText, tool } from 'ai';
405
418
  import { z } from 'zod';
406
419
 
407
420
  const result = await generateText({
408
- model: anthropic('claude-sonnet-5'),
421
+ model: anthropic('claude-sonnet-5-5'),
409
422
  prompt: 'Use the bash tool to run: echo hello',
410
423
  tools: { bash: tool({ inputSchema: z.object({ command: z.string() }) }) },
411
424
  providerOptions: {
@@ -491,15 +504,21 @@ const { text } = await generateText({
491
504
 
492
505
  ##### Adaptive-Only Models
493
506
 
494
- `claude-opus-5-5`, `claude-fable-5`, and `claude-fable-5-1` always run adaptive
495
- thinking. The API rejects `thinking: { type: 'disabled' }` and budget-based
496
- `thinking: { type: 'enabled' }` for these models. To keep existing code working,
497
- the provider:
507
+ `claude-sonnet-5-5`, `claude-opus-5-5`, `claude-fable-5`, and
508
+ `claude-fable-5-1` always run adaptive thinking. The API rejects
509
+ `thinking: { type: 'disabled' }` and budget-based
510
+ `thinking: { type: 'enabled' }` for these models. To keep existing code
511
+ working, the provider:
498
512
 
499
513
  - removes `thinking: { type: 'disabled' }` and emits a warning,
500
514
  - converts `thinking: { type: 'enabled', budgetTokens }` to `{ type: 'adaptive' }` and emits a warning,
501
515
  - maps the top-level `reasoning: 'none'` option to `effort: 'low'` and emits a warning.
502
516
 
517
+ On `claude-sonnet-5-5`, which supports
518
+ [`between_tools` thinking](#between-tools-thinking), the provider instead
519
+ replaces `thinking: { type: 'disabled' }` with `thinking: { type: 'between_tools' }`
520
+ (with a warning) and maps `reasoning: 'none'` to `between_tools` thinking.
521
+
503
522
  Use `effort` to control how much these models think. On `claude-opus-5-5`, set
504
523
  `effort: 'low'` when time to first token matters, and move to `medium` if
505
524
  quality drops:
@@ -524,6 +543,42 @@ const { text } = await generateText({
524
543
  setting, thinking blocks are returned with empty text.
525
544
  </Note>
526
545
 
546
+ ##### Between-Tools Thinking
547
+
548
+ `claude-sonnet-5-5` supports `thinking: { type: 'between_tools' }`, its lowest
549
+ thinking setting. The model does no upfront thinking, but the short progress
550
+ notes it writes between tool calls are still returned as thinking blocks, each
551
+ with a short summary. They are available as reasoning parts and are sent back
552
+ unchanged on the next step. `between_tools` accepts no other thinking options
553
+ (such as `display` or `blockBinding`) and is only accepted at `low`, `medium`,
554
+ and `high` effort. Per-message effort changes via
555
+ [mid-conversation system messages](#mid-conversation-system-controls) are not
556
+ supported with `between_tools`.
557
+
558
+ ```ts highlight="16-17"
559
+ import { anthropic, AnthropicLanguageModelOptions } from '@ai-sdk/anthropic';
560
+ import { generateText, isStepCount, tool } from 'ai';
561
+ import { z } from 'zod';
562
+
563
+ const { text, steps } = await generateText({
564
+ model: anthropic('claude-sonnet-5-5'),
565
+ stopWhen: isStepCount(5),
566
+ tools: {
567
+ weather: tool({
568
+ inputSchema: z.object({ city: z.string() }),
569
+ execute: async ({ city }) => ({ city, temperature: 72 }),
570
+ }),
571
+ },
572
+ providerOptions: {
573
+ anthropic: {
574
+ thinking: { type: 'between_tools' },
575
+ effort: 'low',
576
+ } satisfies AnthropicLanguageModelOptions,
577
+ },
578
+ prompt: 'Compare the weather in San Francisco and New York.',
579
+ });
580
+ ```
581
+
527
582
  ##### Thinking Display (Opus 4.7+)
528
583
 
529
584
  Starting with `claude-opus-4-7`, thinking content is omitted from the response by default — thinking blocks are present in the stream but their text is empty. To receive reasoning output, set `display: 'summarized'`:
@@ -551,11 +606,11 @@ console.log(text);
551
606
 
552
607
  ##### Thinking Updates
553
608
 
554
- Use `display: 'updates'` with `claude-opus-5-5` or `claude-fable-5-1` to stream
555
- thinking summaries between tool calls. These models return progress notes
556
- between tool calls as thinking blocks rather than text, so without this setting
557
- a long tool-calling turn can look silent. The provider adds the required
558
- `thinking-display-updates-2026-08-18` beta header automatically:
609
+ Use `display: 'updates'` with `claude-sonnet-5-5`, `claude-opus-5-5`, or
610
+ `claude-fable-5-1` to stream thinking summaries between tool calls. These models
611
+ return progress notes between tool calls as thinking blocks rather than text, so
612
+ without this setting a long tool-calling turn can look silent. The provider adds
613
+ the required `thinking-display-updates-2026-08-18` beta header automatically:
559
614
 
560
615
  ```ts highlight="12-16"
561
616
  import { anthropic, AnthropicLanguageModelOptions } from '@ai-sdk/anthropic';
@@ -580,11 +635,13 @@ const result = streamText({
580
635
 
581
636
  ##### Thinking Binding Controls
582
637
 
583
- Fable 5.1 can recover from a thinking-block prefix mismatch by dropping the
584
- mismatched block. Set `blockBinding.prefixMismatchBehavior` to `drop_block`, or
585
- set it to `error` to reject the request. You can provide block binding by itself
586
- to preserve the model's default thinking mode, or combine it with adaptive
587
- thinking.
638
+ Fable 5.1, Opus 5.5, and Sonnet 5.5 can recover from a thinking-block prefix
639
+ mismatch by dropping the mismatched block. Set
640
+ `blockBinding.prefixMismatchBehavior` to `drop_block`, or set it to `error` to
641
+ reject the request. You can provide block binding by itself to preserve the
642
+ model's default thinking mode, or combine it with adaptive thinking. Block
643
+ binding is not available with `between_tools` thinking; keep the message
644
+ history append-only instead.
588
645
 
589
646
  ```ts highlight="7-11"
590
647
  const result = await generateText({
@@ -930,7 +987,7 @@ import { generateText } from 'ai';
930
987
  const errorMessage = '... long error message ...';
931
988
 
932
989
  const result = await generateText({
933
- model: anthropic('claude-sonnet-5'),
990
+ model: anthropic('claude-sonnet-5-5'),
934
991
  messages: [
935
992
  {
936
993
  role: 'user',
@@ -964,7 +1021,7 @@ You can also use cache control on system messages by providing multiple system m
964
1021
 
965
1022
  ```ts highlight="3,7-9"
966
1023
  const result = await generateText({
967
- model: anthropic('claude-sonnet-5'),
1024
+ model: anthropic('claude-sonnet-5-5'),
968
1025
  messages: [
969
1026
  {
970
1027
  role: 'system',
@@ -1206,19 +1263,20 @@ const computerTool = anthropic.tools.computer_20251124({
1206
1263
  ```
1207
1264
 
1208
1265
  <Note>
1209
- Use `computerToolset_20260801` for Claude Opus 5.5 (required), Opus 5, Sonnet
1210
- 5, Fable 5, Fable 5.1, and Opus 4.8. Use `computer_20251124` for Claude Opus
1211
- 4.5 through Opus 4.7, which support the zoom action. Use `computer_20250124`
1212
- for Claude Sonnet 4.5, Haiku 4.5, Opus 4.1, Sonnet 4, Opus 4, and Sonnet 3.7.
1266
+ Use `computerToolset_20260801` for Claude Sonnet 5.5 and Opus 5.5 (required),
1267
+ Opus 5, Sonnet 5, Fable 5, Fable 5.1, and Opus 4.8. Use `computer_20251124`
1268
+ for Claude Opus 4.5 through Opus 4.7, which support the zoom action. Use
1269
+ `computer_20250124` for Claude Sonnet 4.5, Haiku 4.5, Opus 4.1, Sonnet 4, Opus
1270
+ 4, and Sonnet 3.7.
1213
1271
  </Note>
1214
1272
 
1215
1273
  #### Computer Toolset
1216
1274
 
1217
1275
  Newer models use the computer toolset instead of a versioned computer tool.
1218
- `claude-opus-5-5` accepts computer use only through the toolset and rejects the
1219
- older `computer_*` tool types with a 400. The toolset does not require a beta
1220
- header and has no display size parameters: coordinates are always in the pixel
1221
- space of the screenshots you return. Zoom is enabled by default, and Claude can
1276
+ `claude-sonnet-5-5` and `claude-opus-5-5` accept computer use only through the
1277
+ toolset and reject the older `computer_*` tool types with a 400. The toolset
1278
+ does not require a beta header and has no display size parameters: coordinates
1279
+ are always in the pixel space of the screenshots you return. Zoom is enabled by default, and Claude can
1222
1280
  return several actions in one turn, each as its own tool call.
1223
1281
 
1224
1282
  The API returns each action as a separate `tool_use` block with
@@ -1478,7 +1536,7 @@ import { generateText, tool } from 'ai';
1478
1536
  import { z } from 'zod';
1479
1537
 
1480
1538
  const result = await generateText({
1481
- model: anthropic('claude-sonnet-5'),
1539
+ model: anthropic('claude-sonnet-5-5'),
1482
1540
  prompt: 'What is the weather in San Francisco?',
1483
1541
  tools: {
1484
1542
  toolSearch: anthropic.tools.toolSearchBm25_20251119(),
@@ -1508,7 +1566,7 @@ For more precise tool matching, you can use the regex variant:
1508
1566
 
1509
1567
  ```ts
1510
1568
  const result = await generateText({
1511
- model: anthropic('claude-sonnet-5'),
1569
+ model: anthropic('claude-sonnet-5-5'),
1512
1570
  prompt: 'Get the weather data',
1513
1571
  tools: {
1514
1572
  toolSearch: anthropic.tools.toolSearchRegex_20251119(),
@@ -1529,7 +1587,7 @@ import { generateText, tool } from 'ai';
1529
1587
  import { z } from 'zod';
1530
1588
 
1531
1589
  const result = await generateText({
1532
- model: anthropic('claude-sonnet-5'),
1590
+ model: anthropic('claude-sonnet-5-5'),
1533
1591
  prompt: 'What is the weather in San Francisco?',
1534
1592
  tools: {
1535
1593
  // Custom search tool
@@ -1572,10 +1630,10 @@ This sends `tool_reference` blocks to Anthropic, which loads the corresponding d
1572
1630
 
1573
1631
  ### Mid-Conversation System Controls
1574
1632
 
1575
- With `claude-opus-5-5` and `claude-fable-5-1`, use an empty system message to
1576
- change effort from the next user turn. A separate system message containing text
1577
- can provide a turn-scoped reminder with `clearAt: 'next_user_message'`: its text
1578
- applies until the next user message. Keep the reminder after a user turn and
1633
+ With `claude-sonnet-5-5`, `claude-opus-5-5`, and `claude-fable-5-1`, use an
1634
+ empty system message to change effort from the next user turn. A separate
1635
+ system message containing text can provide a turn-scoped reminder with
1636
+ `clearAt: 'next_user_message'`: its text applies until the next user message. Keep the reminder after a user turn and
1579
1637
  before an assistant turn, or at the end of the messages array.
1580
1638
  The provider adds the required
1581
1639
  `mid-conversation-system-clear-at-2026-08-21` and
@@ -1711,7 +1769,7 @@ import { anthropic, AnthropicLanguageModelOptions } from '@ai-sdk/anthropic';
1711
1769
  import { generateText } from 'ai';
1712
1770
 
1713
1771
  const result = await generateText({
1714
- model: anthropic('claude-sonnet-5'),
1772
+ model: anthropic('claude-sonnet-5-5'),
1715
1773
  prompt: `Call the echo tool with "hello world". what does it respond with back?`,
1716
1774
  providerOptions: {
1717
1775
  anthropic: {
@@ -1993,6 +2051,18 @@ In this flow:
1993
2051
  `code_execution_20260120` or `code_execution_20250825` tool.
1994
2052
  </Note>
1995
2053
 
2054
+ #### Pruning Programmatic Tool History
2055
+
2056
+ When pruning removes a code execution call but keeps a tool call that references
2057
+ it through `caller.toolId`, the provider omits the dangling caller metadata from
2058
+ history before a subsequent user message and emits a warning. The retained tool
2059
+ calls and results are still sent to Anthropic; the removed code execution history
2060
+ is not restored.
2061
+
2062
+ Tool-result messages do not end a turn. Caller metadata in an active tool
2063
+ continuation is preserved, so keep the source code execution call when returning
2064
+ programmatic tool results.
2065
+
1996
2066
  #### Container Persistence
1997
2067
 
1998
2068
  When using programmatic tool calling across multiple steps, you need to preserve the container ID between steps using `prepareStep`. You can use the `forwardAnthropicContainerIdFromLastStep` helper function to do this automatically. The container ID is available in `providerMetadata.anthropic.container.id` after each step completes.
@@ -2082,7 +2152,7 @@ Option 1: URL-based PDF document
2082
2152
 
2083
2153
  ```ts
2084
2154
  const result = await generateText({
2085
- model: anthropic('claude-sonnet-5'),
2155
+ model: anthropic('claude-sonnet-5-5'),
2086
2156
  messages: [
2087
2157
  {
2088
2158
  role: 'user',
@@ -2108,7 +2178,7 @@ Option 2: Base64-encoded PDF document
2108
2178
 
2109
2179
  ```ts
2110
2180
  const result = await generateText({
2111
- model: anthropic('claude-sonnet-5'),
2181
+ model: anthropic('claude-sonnet-5-5'),
2112
2182
  messages: [
2113
2183
  {
2114
2184
  role: 'user',
@@ -2139,6 +2209,7 @@ and the `mediaType` should be set to `'application/pdf'`.
2139
2209
  | ------------------- | ----------- | ----------------- | ---------- | ------------ | ---------- | ----------- | ---------- |
2140
2210
  | `claude-opus-5-5` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
2141
2211
  | `claude-opus-5` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
2212
+ | `claude-sonnet-5-5` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
2142
2213
  | `claude-sonnet-5` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
2143
2214
  | `claude-fable-5-1` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
2144
2215
  | `claude-fable-5` | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> | <Check /> |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/anthropic",
3
- "version": "4.0.65",
3
+ "version": "4.0.67",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -35,8 +35,8 @@
35
35
  }
36
36
  },
37
37
  "dependencies": {
38
- "@ai-sdk/provider": "4.0.18",
39
- "@ai-sdk/provider-utils": "5.0.49"
38
+ "@ai-sdk/provider": "4.0.19",
39
+ "@ai-sdk/provider-utils": "5.0.50"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@ai-sdk/test-server": "2.0.2",
@@ -24,6 +24,7 @@ export type AnthropicModelId =
24
24
  | 'claude-fable-5'
25
25
  | 'claude-fable-5-1'
26
26
  | 'claude-sonnet-5'
27
+ | 'claude-sonnet-5-5'
27
28
  | (string & {});
28
29
 
29
30
  /**
@@ -150,6 +151,9 @@ export const anthropicLanguageModelOptions = z.object({
150
151
  * `claude-fable-5-1`) reject `enabled` and `disabled`. For those models the
151
152
  * provider drops the unsupported setting, emits a warning, and sends an
152
153
  * adaptive thinking request. Use `effort` to control how much they think.
154
+ *
155
+ * `claude-sonnet-5-5` supports `between_tools`, its lowest thinking setting,
156
+ * and the provider uses it in place of `disabled` for that model.
153
157
  */
154
158
  thinking: z
155
159
  .union([
@@ -182,6 +186,14 @@ export const anthropicLanguageModelOptions = z.object({
182
186
  z.object({
183
187
  type: z.literal('disabled'),
184
188
  }),
189
+ z.object({
190
+ /**
191
+ * for `claude-sonnet-5-5`: no upfront thinking, but progress notes
192
+ * between tool calls are returned as summarized thinking blocks.
193
+ * Only supported at `low`, `medium`, and `high` effort.
194
+ */
195
+ type: z.literal('between_tools'),
196
+ }),
185
197
  ]),
186
198
  z.object({
187
199
  type: z.never().optional(),
@@ -407,6 +407,7 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
407
407
  rejectsThinkingDisabledAboveHighEffort,
408
408
  rejectsThinkingDisabled,
409
409
  rejectsForcedToolUse,
410
+ supportsBetweenToolsThinking,
410
411
  isKnownModel,
411
412
  } = getModelCapabilities(modelId);
412
413
 
@@ -573,6 +574,7 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
573
574
  supportsAdaptiveThinking,
574
575
  supportsXhighEffort,
575
576
  rejectsThinkingDisabled,
577
+ supportsBetweenToolsThinking,
576
578
  maxOutputTokensForModel,
577
579
  warnings,
578
580
  });
@@ -595,7 +597,16 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
595
597
  if (rejectsThinkingDisabled && anthropicOptions?.thinking != null) {
596
598
  const thinking = anthropicOptions.thinking;
597
599
 
598
- if (thinking.type === 'disabled') {
600
+ if (thinking.type === 'disabled' && supportsBetweenToolsThinking) {
601
+ warnings.push({
602
+ type: 'unsupported',
603
+ feature: 'providerOptions.anthropic.thinking',
604
+ details:
605
+ `thinking cannot be disabled for ${modelId}. ` +
606
+ `Using 'between_tools' thinking, the lowest thinking setting, instead.`,
607
+ });
608
+ anthropicOptions.thinking = { type: 'between_tools' };
609
+ } else if (thinking.type === 'disabled') {
599
610
  warnings.push({
600
611
  type: 'unsupported',
601
612
  feature: 'providerOptions.anthropic.thinking',
@@ -635,9 +646,28 @@ export class AnthropicLanguageModel implements LanguageModelV4 {
635
646
  anthropicOptions.effort = 'high';
636
647
  }
637
648
 
649
+ // `between_tools` thinking is only accepted at `low`, `medium`, and
650
+ // `high` effort. Lower the effort to `high` to keep the minimal thinking
651
+ // setting instead of sending a request that the API would reject.
652
+ if (
653
+ anthropicOptions?.thinking?.type === 'between_tools' &&
654
+ (anthropicOptions.effort === 'xhigh' || anthropicOptions.effort === 'max')
655
+ ) {
656
+ warnings.push({
657
+ type: 'unsupported',
658
+ feature: 'providerOptions.anthropic.effort',
659
+ details:
660
+ `effort '${anthropicOptions.effort}' is not supported with 'between_tools' thinking. ` +
661
+ `The effort has been lowered to 'high'.`,
662
+ });
663
+ anthropicOptions.effort = 'high';
664
+ }
665
+
638
666
  const thinkingType = anthropicOptions?.thinking?.type;
639
667
  const isThinking =
640
- thinkingType === 'enabled' || thinkingType === 'adaptive';
668
+ thinkingType === 'enabled' ||
669
+ thinkingType === 'adaptive' ||
670
+ thinkingType === 'between_tools';
641
671
  const thinkingBlockBinding =
642
672
  anthropicOptions?.thinking != null &&
643
673
  'blockBinding' in anthropicOptions.thinking
@@ -3127,9 +3157,27 @@ export function getModelCapabilities(modelId: string): {
3127
3157
  * Forced tool use (`tool_choice` `any` or a named tool) is rejected with a 400.
3128
3158
  */
3129
3159
  rejectsForcedToolUse: boolean;
3160
+ /**
3161
+ * Supports `thinking.type` `between_tools`, the lowest thinking setting on
3162
+ * models that reject disabled thinking.
3163
+ */
3164
+ supportsBetweenToolsThinking: boolean;
3130
3165
  isKnownModel: boolean;
3131
3166
  } {
3132
- if (modelId.includes('claude-opus-5-5')) {
3167
+ if (modelId.includes('claude-sonnet-5-5')) {
3168
+ return {
3169
+ maxOutputTokens: 128000,
3170
+ supportsStructuredOutput: true,
3171
+ supportsAdaptiveThinking: true,
3172
+ rejectsSamplingParameters: true,
3173
+ supportsXhighEffort: true,
3174
+ rejectsThinkingDisabledAboveHighEffort: true,
3175
+ rejectsThinkingDisabled: true,
3176
+ rejectsForcedToolUse: true,
3177
+ supportsBetweenToolsThinking: true,
3178
+ isKnownModel: true,
3179
+ };
3180
+ } else if (modelId.includes('claude-opus-5-5')) {
3133
3181
  return {
3134
3182
  maxOutputTokens: 128000,
3135
3183
  supportsStructuredOutput: true,
@@ -3139,6 +3187,7 @@ export function getModelCapabilities(modelId: string): {
3139
3187
  rejectsThinkingDisabledAboveHighEffort: true,
3140
3188
  rejectsThinkingDisabled: true,
3141
3189
  rejectsForcedToolUse: true,
3190
+ supportsBetweenToolsThinking: false,
3142
3191
  isKnownModel: true,
3143
3192
  };
3144
3193
  } else if (modelId.includes('claude-opus-5')) {
@@ -3151,6 +3200,7 @@ export function getModelCapabilities(modelId: string): {
3151
3200
  rejectsThinkingDisabledAboveHighEffort: true,
3152
3201
  rejectsThinkingDisabled: false,
3153
3202
  rejectsForcedToolUse: false,
3203
+ supportsBetweenToolsThinking: false,
3154
3204
  isKnownModel: true,
3155
3205
  };
3156
3206
  } else if (modelId.includes('claude-fable-5-1')) {
@@ -3163,6 +3213,7 @@ export function getModelCapabilities(modelId: string): {
3163
3213
  rejectsThinkingDisabledAboveHighEffort: false,
3164
3214
  rejectsThinkingDisabled: true,
3165
3215
  rejectsForcedToolUse: true,
3216
+ supportsBetweenToolsThinking: false,
3166
3217
  isKnownModel: true,
3167
3218
  };
3168
3219
  } else if (modelId.includes('claude-fable-5')) {
@@ -3175,6 +3226,7 @@ export function getModelCapabilities(modelId: string): {
3175
3226
  rejectsThinkingDisabledAboveHighEffort: false,
3176
3227
  rejectsThinkingDisabled: true,
3177
3228
  rejectsForcedToolUse: false,
3229
+ supportsBetweenToolsThinking: false,
3178
3230
  isKnownModel: true,
3179
3231
  };
3180
3232
  } else if (
@@ -3191,6 +3243,7 @@ export function getModelCapabilities(modelId: string): {
3191
3243
  rejectsThinkingDisabledAboveHighEffort: false,
3192
3244
  rejectsThinkingDisabled: false,
3193
3245
  rejectsForcedToolUse: false,
3246
+ supportsBetweenToolsThinking: false,
3194
3247
  isKnownModel: true,
3195
3248
  };
3196
3249
  } else if (
@@ -3206,6 +3259,7 @@ export function getModelCapabilities(modelId: string): {
3206
3259
  rejectsThinkingDisabledAboveHighEffort: false,
3207
3260
  rejectsThinkingDisabled: false,
3208
3261
  rejectsForcedToolUse: false,
3262
+ supportsBetweenToolsThinking: false,
3209
3263
  isKnownModel: true,
3210
3264
  };
3211
3265
  } else if (
@@ -3222,6 +3276,7 @@ export function getModelCapabilities(modelId: string): {
3222
3276
  rejectsThinkingDisabledAboveHighEffort: false,
3223
3277
  rejectsThinkingDisabled: false,
3224
3278
  rejectsForcedToolUse: false,
3279
+ supportsBetweenToolsThinking: false,
3225
3280
  isKnownModel: true,
3226
3281
  };
3227
3282
  } else if (modelId.includes('claude-opus-4-1')) {
@@ -3234,6 +3289,7 @@ export function getModelCapabilities(modelId: string): {
3234
3289
  rejectsThinkingDisabledAboveHighEffort: false,
3235
3290
  rejectsThinkingDisabled: false,
3236
3291
  rejectsForcedToolUse: false,
3292
+ supportsBetweenToolsThinking: false,
3237
3293
  isKnownModel: true,
3238
3294
  };
3239
3295
  } else if (/claude-sonnet-4(?:-|@)/.test(modelId)) {
@@ -3246,6 +3302,7 @@ export function getModelCapabilities(modelId: string): {
3246
3302
  rejectsThinkingDisabledAboveHighEffort: false,
3247
3303
  rejectsThinkingDisabled: false,
3248
3304
  rejectsForcedToolUse: false,
3305
+ supportsBetweenToolsThinking: false,
3249
3306
  isKnownModel: true,
3250
3307
  };
3251
3308
  } else if (/claude-opus-4(?:-|@)/.test(modelId)) {
@@ -3258,6 +3315,7 @@ export function getModelCapabilities(modelId: string): {
3258
3315
  rejectsThinkingDisabledAboveHighEffort: false,
3259
3316
  rejectsThinkingDisabled: false,
3260
3317
  rejectsForcedToolUse: false,
3318
+ supportsBetweenToolsThinking: false,
3261
3319
  isKnownModel: true,
3262
3320
  };
3263
3321
  } else if (modelId.includes('claude-3-haiku')) {
@@ -3270,6 +3328,7 @@ export function getModelCapabilities(modelId: string): {
3270
3328
  rejectsThinkingDisabledAboveHighEffort: false,
3271
3329
  rejectsThinkingDisabled: false,
3272
3330
  rejectsForcedToolUse: false,
3331
+ supportsBetweenToolsThinking: false,
3273
3332
  isKnownModel: true,
3274
3333
  };
3275
3334
  } else if (
@@ -3284,6 +3343,7 @@ export function getModelCapabilities(modelId: string): {
3284
3343
  rejectsThinkingDisabledAboveHighEffort: false,
3285
3344
  rejectsThinkingDisabled: false,
3286
3345
  rejectsForcedToolUse: false,
3346
+ supportsBetweenToolsThinking: false,
3287
3347
  isKnownModel: false,
3288
3348
  };
3289
3349
  } else if (modelId.includes('claude-')) {
@@ -3299,6 +3359,7 @@ export function getModelCapabilities(modelId: string): {
3299
3359
  rejectsThinkingDisabledAboveHighEffort: true,
3300
3360
  rejectsThinkingDisabled: false,
3301
3361
  rejectsForcedToolUse: false,
3362
+ supportsBetweenToolsThinking: false,
3302
3363
  isKnownModel: false,
3303
3364
  };
3304
3365
  } else {
@@ -3313,6 +3374,7 @@ export function getModelCapabilities(modelId: string): {
3313
3374
  rejectsThinkingDisabledAboveHighEffort: false,
3314
3375
  rejectsThinkingDisabled: false,
3315
3376
  rejectsForcedToolUse: false,
3377
+ supportsBetweenToolsThinking: false,
3316
3378
  isKnownModel: false,
3317
3379
  };
3318
3380
  }
@@ -3356,6 +3418,7 @@ function resolveAnthropicReasoningConfig({
3356
3418
  supportsAdaptiveThinking,
3357
3419
  supportsXhighEffort,
3358
3420
  rejectsThinkingDisabled,
3421
+ supportsBetweenToolsThinking,
3359
3422
  maxOutputTokensForModel,
3360
3423
  warnings,
3361
3424
  }: {
@@ -3364,6 +3427,7 @@ function resolveAnthropicReasoningConfig({
3364
3427
  supportsAdaptiveThinking: boolean;
3365
3428
  supportsXhighEffort: boolean;
3366
3429
  rejectsThinkingDisabled: boolean;
3430
+ supportsBetweenToolsThinking: boolean;
3367
3431
  maxOutputTokensForModel: number;
3368
3432
  warnings: SharedV4Warning[];
3369
3433
  }): Pick<AnthropicLanguageModelOptions, 'thinking' | 'effort'> | undefined {
@@ -3372,6 +3436,12 @@ function resolveAnthropicReasoningConfig({
3372
3436
  }
3373
3437
 
3374
3438
  if (reasoning === 'none') {
3439
+ // `between_tools` is the lowest thinking setting on models that support
3440
+ // it: no upfront thinking, only short progress notes between tool calls.
3441
+ if (supportsBetweenToolsThinking) {
3442
+ return { thinking: { type: 'between_tools' } };
3443
+ }
3444
+
3375
3445
  // Models that always run adaptive thinking cannot disable it. Use low
3376
3446
  // effort to keep thinking short instead of sending a request that the
3377
3447
  // API would reject.
@@ -105,6 +105,7 @@ export async function convertToAnthropicPrompt({
105
105
 
106
106
  let system: AnthropicPrompt['system'] = undefined;
107
107
  const messages: AnthropicPrompt['messages'] = [];
108
+ let lastUserMessageIndex = -1;
108
109
 
109
110
  async function shouldEnableCitations(
110
111
  providerMetadata: SharedV4ProviderMetadata | undefined,
@@ -291,6 +292,10 @@ export async function convertToAnthropicPrompt({
291
292
  const { role, content } = message;
292
293
  switch (role) {
293
294
  case 'user': {
295
+ if (content.length > 0) {
296
+ lastUserMessageIndex = messages.length;
297
+ }
298
+
294
299
  for (let j = 0; j < content.length; j++) {
295
300
  const part = content[j];
296
301
 
@@ -1427,6 +1432,55 @@ export async function convertToAnthropicPrompt({
1427
1432
  }
1428
1433
  }
1429
1434
 
1435
+ // Pruning can remove a code execution call while retaining tool calls that
1436
+ // reference it. Check the converted blocks, since unsupported source calls
1437
+ // may also have been omitted during conversion.
1438
+ const codeExecutionToolCallIds = new Set<string>();
1439
+ for (const message of messages) {
1440
+ if (message.role !== 'assistant') {
1441
+ continue;
1442
+ }
1443
+ for (const part of message.content) {
1444
+ if (part.type === 'server_tool_use' && part.name === 'code_execution') {
1445
+ codeExecutionToolCallIds.add(part.id);
1446
+ }
1447
+ }
1448
+ }
1449
+
1450
+ // Only normalize history before a subsequent user message. Tool-result
1451
+ // messages do not end a turn: their caller metadata must remain intact so
1452
+ // Anthropic can resume an active code execution.
1453
+ const warnedToolCallIds = new Set<string>();
1454
+ for (let i = 0; i < lastUserMessageIndex; i++) {
1455
+ const message = messages[i];
1456
+ if (message.role !== 'assistant') {
1457
+ continue;
1458
+ }
1459
+ for (const part of message.content) {
1460
+ if (!('caller' in part)) {
1461
+ continue;
1462
+ }
1463
+ const caller = part.caller;
1464
+ if (
1465
+ caller == null ||
1466
+ caller.type === 'direct' ||
1467
+ codeExecutionToolCallIds.has(caller.tool_id)
1468
+ ) {
1469
+ continue;
1470
+ }
1471
+
1472
+ const toolCallId = 'id' in part ? part.id : part.tool_use_id;
1473
+ delete part.caller;
1474
+ if (!warnedToolCallIds.has(toolCallId)) {
1475
+ warnedToolCallIds.add(toolCallId);
1476
+ warnings.push({
1477
+ type: 'other',
1478
+ message: `Omitted caller metadata for tool ${toolCallId} because source code execution tool ${caller.tool_id} is missing from the conversation history.`,
1479
+ });
1480
+ }
1481
+ }
1482
+ }
1483
+
1430
1484
  return {
1431
1485
  prompt: { system, messages },
1432
1486
  betas,