@warlock.js/ai-anthropic 5.25.0 → 5.26.0

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/CHANGELOG.md CHANGED
@@ -4,6 +4,22 @@ All notable changes to `@warlock.js/ai-anthropic` are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
6
6
 
7
+ ## 5.26.0 - 2026-09-30
8
+
9
+ ### Fixed
10
+
11
+ - Adaptive Claude reasoning now maps `minimal` to `output_config.effort: "low"` and supports `xhigh` and `max`; budget-mode models map them to 1,024, 24,000, and 32,000 thinking tokens respectively.
12
+ - Claude reasoning-token usage is now included in responses when the API reports it, including streamed responses.
13
+ - Responses stopped because the model context window was exceeded now report a length finish reason.
14
+ - **Behaviour change:** Reasoning now works on current Claude models. Claude Opus 4.7, 4.8, 5 and 5.5, Sonnet 5 and 5.5, Fable 5 and 5.1, and any model id the adapter does not recognise now get `thinking: { type: "adaptive" }` with `output_config.effort` (`low`, `medium` or `high`, taken from `reasoning.effort`; `medium` when only `reasoning.maxTokens` is given). Before, every model got `budget_tokens`, which these models reject with a 400. Claude Haiku 4.5, Opus 4.6, Sonnet 4.6 and older models keep the `budget_tokens` shape. The default `max_tokens` is unchanged in both modes.
15
+ - **Behaviour change:** `temperature` is no longer sent to models released after Claude Opus 4.6 (the same adaptive-thinking models listed above), because they reject any value other than the default with a 400. Haiku 4.5, the 4.6 models and older models still receive it.
16
+ - **Behaviour change:** Claude 5-series models (`claude-opus-5*`, `claude-sonnet-5*`, `claude-fable-5*`, `claude-mythos-5*`) are now recognised as vision-capable, so image attachments are no longer rejected for them.
17
+
18
+ ### Added
19
+
20
+ - `thinkingMode: "adaptive" | "budget"` on `anthropic.model({ ... })` overrides the thinking shape inferred from the model name. Use it for gateway model ids that don't follow Anthropic's naming, or to move Opus/Sonnet 4.6 onto adaptive thinking. It also decides whether `temperature` is sent. The `AnthropicThinkingMode` type is exported.
21
+ - `promptCaching: true` now enables Anthropic's request-level automatic cache placement when a call has no explicit cache breakpoint, so the longest reusable prefix is cached. `promptCacheTtl?: "5m" | "1h"` controls the TTL for automatic and explicit cache markers; omission keeps Anthropic's five-minute default.
22
+
7
23
  ## 5.25.0 - 2026-09-28
8
24
 
9
25
  ### Changed
@@ -55,6 +71,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
55
71
  ### Changed
56
72
 
57
73
  - Lockstep release maintenance and dependency refresh.
74
+
58
75
  ## 5.20.0 - 2026-09-24
59
76
 
60
77
  ### Changed
package/cjs/index.cjs CHANGED
@@ -36,13 +36,22 @@ let _warlock_js_logger = require("@warlock.js/logger");
36
36
  * Model-name prefixes for Claude families that accept image input
37
37
  * (vision) on the Messages API.
38
38
  *
39
- * Every Claude 3, Claude 3.5/3.7, and Claude 4 family model is
39
+ * Every Claude 3, Claude 3.5/3.7, Claude 4 and Claude 5 family model is
40
40
  * multimodal, so the list covers both the dotted legacy naming
41
41
  * (`claude-3-haiku-...`, `claude-3-5-sonnet-...`) and the current
42
42
  * `claude-<tier>-4-*` naming (`claude-opus-4-7`, `claude-sonnet-4-6`,
43
43
  * `claude-haiku-4-5`). Pre-3 families (`claude-2`, `claude-instant`)
44
44
  * are text-only and intentionally absent.
45
45
  *
46
+ * The Claude 5 series is multimodal too (claude-api ref, cached
47
+ * 2026-09-25): its vision examples use `claude-opus-5-5`; the models list
48
+ * credits Claude Fable 5.1 with vision and Claude Sonnet 5 with
49
+ * high-resolution vision; Claude Mythos 5.1 has "same capabilities" as
50
+ * Fable 5.1. The tier-5 prefixes cover every 5.x release
51
+ * (`claude-opus-5`, `claude-opus-5-5`, `claude-sonnet-5-5`,
52
+ * `claude-fable-5-1`, …). Opus/Sonnet 4.6–4.8 are already covered by the
53
+ * tier-4 prefixes.
54
+ *
46
55
  * Matched as a prefix so dated variants (`claude-opus-4-20250514`) are
47
56
  * covered without listing every release tag. Devs can always override
48
57
  * per-model via `anthropic.model({ name, vision: true | false })` —
@@ -53,7 +62,11 @@ const VISION_CAPABLE_PREFIXES = [
53
62
  "claude-4",
54
63
  "claude-opus-4",
55
64
  "claude-sonnet-4",
56
- "claude-haiku-4"
65
+ "claude-haiku-4",
66
+ "claude-opus-5",
67
+ "claude-sonnet-5",
68
+ "claude-fable-5",
69
+ "claude-mythos-5"
57
70
  ];
58
71
  /**
59
72
  * Infer whether a given Claude model name supports vision based on the
@@ -72,19 +85,100 @@ function inferVisionCapability(modelName) {
72
85
  return VISION_CAPABLE_PREFIXES.some((prefix) => normalized.startsWith(prefix));
73
86
  }
74
87
 
88
+ //#endregion
89
+ //#region ../ai-anthropic/src/thinking-mode.ts
90
+ /**
91
+ * Family prefixes that are budget-mode in their entirety — plain
92
+ * `startsWith` match (dated and `-latest` variants included).
93
+ */
94
+ const BUDGET_FAMILY_PREFIXES = [
95
+ "claude-3",
96
+ "claude-2",
97
+ "claude-instant",
98
+ "claude-haiku-4"
99
+ ];
100
+ /**
101
+ * Individual pre-4.7 releases in the `claude-<tier>-4-*` naming. Matched
102
+ * as a release root: the id must equal the root or continue with a
103
+ * separator that is not a new minor version number, so `claude-opus-4`
104
+ * matches `claude-opus-4-20250514` but NOT `claude-opus-4-7`. Ids from the
105
+ * installed SDK `Model` union (`messages.d.ts:795`) plus the 4.6 pair.
106
+ */
107
+ const BUDGET_RELEASE_ROOTS = [
108
+ "claude-opus-4",
109
+ "claude-opus-4-0",
110
+ "claude-opus-4-1",
111
+ "claude-opus-4-5",
112
+ "claude-opus-4-6",
113
+ "claude-sonnet-4",
114
+ "claude-sonnet-4-0",
115
+ "claude-sonnet-4-5",
116
+ "claude-sonnet-4-6"
117
+ ];
118
+ /**
119
+ * A remainder that starts a new minor version (`-7`, `-10`) — used to
120
+ * stop a release root from swallowing a later release of the same line.
121
+ * Dates (`-20250514`) and tags (`-latest`, `-v1`) are not minor versions.
122
+ */
123
+ const NEXT_MINOR_VERSION = /^-\d{1,2}(?!\d)/;
124
+ function matchesReleaseRoot(name, root) {
125
+ if (!name.startsWith(root)) return false;
126
+ const rest = name.slice(root.length);
127
+ if (rest === "") return true;
128
+ return (rest.startsWith("-") || rest.startsWith("@")) && !NEXT_MINOR_VERSION.test(rest);
129
+ }
130
+ /**
131
+ * Infer the thinking mode for a Claude model id. Budget for Haiku 4.5, the
132
+ * 4.6 pair, and every older family; adaptive for everything else,
133
+ * including unknown and future ids. Explicit per-model config
134
+ * (`AnthropicModelConfig.thinkingMode`) always wins over this inference.
135
+ *
136
+ * @example
137
+ * inferThinkingMode("claude-opus-5-5"); // → "adaptive"
138
+ * inferThinkingMode("claude-haiku-4-5"); // → "budget"
139
+ * inferThinkingMode("claude-opus-9"); // → "adaptive" (unknown → current API direction)
140
+ */
141
+ function inferThinkingMode(modelName) {
142
+ const normalized = modelName.toLowerCase();
143
+ if (BUDGET_FAMILY_PREFIXES.some((prefix) => normalized.startsWith(prefix))) return "budget";
144
+ if (BUDGET_RELEASE_ROOTS.some((root) => matchesReleaseRoot(normalized, root))) return "budget";
145
+ return "adaptive";
146
+ }
147
+ /**
148
+ * Whether a model of this thinking mode accepts an explicit `temperature`.
149
+ *
150
+ * Installed SDK, `MessageCreateParamsBase.temperature`
151
+ * (`messages.d.ts:2036-2040`): "Models released after Claude Opus 4.6 do
152
+ * not support setting temperature. A value of 1.0 will be accepted for
153
+ * backwards compatibility, all other values will be rejected with a 400
154
+ * error." The claude-api ref's per-model table agrees: sampling is
155
+ * "Removed - 400" on Fable 5/5.1, Opus 5.5/5/4.8/4.7 and Sonnet 5, and
156
+ * "Non-default values - 400" on Sonnet 5.5; "Allowed" on Opus 4.6,
157
+ * Sonnet 4.6, Haiku 4.5 and older.
158
+ *
159
+ * That split is exactly the adaptive/budget split in the table above
160
+ * (adaptive = released after Opus 4.6; budget = Opus 4.6 and earlier), so
161
+ * the thinking mode doubles as the sampling-parameter classification.
162
+ */
163
+ function acceptsTemperature(mode) {
164
+ return mode === "budget";
165
+ }
166
+
75
167
  //#endregion
76
168
  //#region ../ai-anthropic/src/utils/map-stop-reason.ts
77
169
  const stopReasonMap = {
78
170
  end_turn: "stop",
79
171
  stop_sequence: "stop",
80
172
  max_tokens: "length",
173
+ model_context_window_exceeded: "length",
81
174
  tool_use: "tool_calls"
82
175
  };
83
176
  /**
84
177
  * Map Anthropic's `stop_reason` to the normalized `FinishReason` union.
85
178
  *
86
- * `end_turn` / `stop_sequence` are both natural stops. `max_tokens`
87
- * maps to `length`. `tool_use` maps to `tool_calls`. Everything else —
179
+ * `end_turn` / `stop_sequence` are both natural stops. `max_tokens` and
180
+ * `model_context_window_exceeded` map to `length`. `tool_use` maps to
181
+ * `tool_calls`. Everything else —
88
182
  * `refusal` (policy intervention), `pause_turn` (incomplete
89
183
  * long-running turn), `null`, or any value a future API version adds —
90
184
  * falls through to `"error"` so the agent treats the trip as a
@@ -468,16 +562,45 @@ const DEFAULT_MAX_TOKENS = 4096;
468
562
  */
469
563
  const MIN_THINKING_BUDGET = 1024;
470
564
  /**
471
- * Map the neutral `ReasoningEffort` level to an Anthropic
472
- * `thinking.budget_tokens` value. Anthropic budgets reasoning by token
473
- * count (unlike OpenAI's opaque `reasoning_effort` enum), so the three
474
- * neutral levels translate to representative token budgets when the
475
- * caller doesn't pass an explicit `reasoning.maxTokens`.
565
+ * Effort used when the caller asks for reasoning with only
566
+ * `reasoning.maxTokens` and no `effort`. `"medium"` is what the adapter
567
+ * has always assumed (the budget lookup fell back to
568
+ * `EFFORT_THINKING_BUDGET.medium`), so adaptive models get the same tier.
569
+ */
570
+ const DEFAULT_THINKING_EFFORT = "medium";
571
+ /**
572
+ * Map the neutral `ReasoningEffort` level to a thinking token count.
573
+ *
574
+ * - Budget-mode models: sent as `thinking.budget_tokens` when the caller
575
+ * doesn't pass an explicit `reasoning.maxTokens`.
576
+ * - Both modes: reserved on top of `DEFAULT_MAX_TOKENS` when the caller
577
+ * sets no `maxTokens`, because thinking counts toward `max_tokens`
578
+ * (installed SDK `ThinkingConfigParam` docs, `messages.d.ts:1105`).
579
+ * Adaptive models therefore get the same `max_tokens` the budget path
580
+ * has always produced.
476
581
  */
477
582
  const EFFORT_THINKING_BUDGET = {
583
+ minimal: 1024,
478
584
  low: 1024,
479
585
  medium: 4096,
480
- high: 12e3
586
+ high: 12e3,
587
+ xhigh: 24e3,
588
+ max: 32e3
589
+ };
590
+ /**
591
+ * Map the neutral `ReasoningEffort` level to Anthropic's
592
+ * `output_config.effort` for adaptive-mode models. The installed SDK
593
+ * accepts `low | medium | high | xhigh | max` (`OutputConfig.effort`,
594
+ * `messages.ts:2767`); map every supported value 1:1 and clamp `minimal` to
595
+ * the nearest accepted value, `low`.
596
+ */
597
+ const EFFORT_TO_OUTPUT_EFFORT = {
598
+ minimal: "low",
599
+ low: "low",
600
+ medium: "medium",
601
+ high: "high",
602
+ xhigh: "xhigh",
603
+ max: "max"
481
604
  };
482
605
  /**
483
606
  * Anthropic-backed implementation of `ModelContract`.
@@ -525,6 +648,7 @@ var AnthropicModel = class {
525
648
  this.name = config.name;
526
649
  this.provider = provider;
527
650
  this.pricing = config.pricing;
651
+ this.thinkingMode = config.thinkingMode ?? inferThinkingMode(config.name);
528
652
  this.capabilities = {
529
653
  structuredOutput: config.structuredOutput ?? true,
530
654
  vision: config.vision ?? inferVisionCapability(config.name),
@@ -604,6 +728,8 @@ var AnthropicModel = class {
604
728
  for await (const event of stream) {
605
729
  if (event.type === "message_start") {
606
730
  usage.input = event.message.usage.input_tokens ?? 0;
731
+ const thinkingTokens = event.message.usage.output_tokens_details?.thinking_tokens;
732
+ if (thinkingTokens !== null && thinkingTokens !== void 0) usage.reasoningTokens = thinkingTokens;
607
733
  const cacheRead = event.message.usage.cache_read_input_tokens;
608
734
  if (cacheRead !== null && cacheRead !== void 0 && cacheRead > 0) usage.cachedTokens = cacheRead;
609
735
  const cacheWrite = event.message.usage.cache_creation_input_tokens;
@@ -655,6 +781,8 @@ var AnthropicModel = class {
655
781
  if (event.type === "message_delta") {
656
782
  rawStopReason = event.delta.stop_reason ?? rawStopReason;
657
783
  usage.output = event.usage.output_tokens ?? usage.output;
784
+ const thinkingTokens = event.usage.output_tokens_details?.thinking_tokens;
785
+ if (thinkingTokens !== null && thinkingTokens !== void 0) usage.reasoningTokens = thinkingTokens;
658
786
  const cacheRead = event.usage.cache_read_input_tokens;
659
787
  if (cacheRead !== null && cacheRead !== void 0 && cacheRead > 0) usage.cachedTokens = cacheRead;
660
788
  const cacheWrite = event.usage.cache_creation_input_tokens;
@@ -683,9 +811,10 @@ var AnthropicModel = class {
683
811
  * prompt out of `messages`, resolves `max_tokens` (required by
684
812
  * Anthropic) with the documented default, and conditionally attaches
685
813
  * temperature, tools, native structured output, extended thinking
686
- * (`reasoning`), and a system-prompt cache breakpoint (`cacheControl`).
814
+ * (`reasoning`), and automatic or explicit prompt-cache control.
687
815
  * Temperature is dropped when thinking is enabled, since Anthropic
688
- * rejects the two together.
816
+ * rejects the two together, and always for adaptive-mode models, which
817
+ * reject any explicit temperature (see `acceptsTemperature`).
689
818
  */
690
819
  buildParams(messages, options) {
691
820
  const { system, messages: anthropicMessages } = toAnthropicMessages(messages);
@@ -693,17 +822,19 @@ var AnthropicModel = class {
693
822
  const temperature = options?.temperature ?? this.config.temperature;
694
823
  const configuredMaxTokens = options?.maxTokens ?? this.config.maxTokens;
695
824
  const thinkingBudget = thinking.thinking?.type === "enabled" ? thinking.thinking.budget_tokens : void 0;
696
- const maxTokens = configuredMaxTokens ?? (thinkingBudget !== void 0 ? thinkingBudget + DEFAULT_MAX_TOKENS : DEFAULT_MAX_TOKENS);
825
+ const maxTokens = configuredMaxTokens ?? (thinking.reservedTokens !== void 0 ? thinking.reservedTokens + DEFAULT_MAX_TOKENS : DEFAULT_MAX_TOKENS);
826
+ const sendTemperature = temperature !== void 0 && !thinking.thinking && acceptsTemperature(this.thinkingMode);
697
827
  if (thinkingBudget !== void 0 && configuredMaxTokens !== void 0 && configuredMaxTokens <= thinkingBudget) throw new Error(`Anthropic maxTokens (${configuredMaxTokens}) must be greater than thinking budget_tokens (${thinkingBudget})`);
698
828
  return {
699
829
  model: this.name,
700
830
  max_tokens: maxTokens,
701
831
  messages: anthropicMessages,
832
+ ...this.buildAutomaticCacheControl(options?.cacheControl),
702
833
  ...this.buildSystem(system, options?.cacheControl),
703
- ...temperature !== void 0 && !thinking.thinking ? { temperature } : {},
834
+ ...sendTemperature ? { temperature } : {},
704
835
  ...this.buildTools(options?.tools),
705
- ...this.buildStructuredOutput(options?.responseSchema),
706
- ...thinking
836
+ ...this.buildOutputConfig(options?.responseSchema, thinking.effort),
837
+ ...thinking.thinking ? { thinking: thinking.thinking } : {}
707
838
  };
708
839
  }
709
840
  /**
@@ -724,31 +855,66 @@ var AnthropicModel = class {
724
855
  if (this.capabilities.promptCaching && breakpoints > 0) return { system: [{
725
856
  type: "text",
726
857
  text: system,
727
- cache_control: { type: "ephemeral" }
858
+ cache_control: this.cacheControl()
728
859
  }] };
729
860
  return { system };
730
861
  }
731
862
  /**
732
- * Translate the neutral `reasoning` option into Anthropic's
733
- * `thinking` request field. Emitted only when the model declares the
734
- * `reasoning` capability AND a reasoning option is supplied; otherwise
735
- * returns an empty object so the caller can unconditionally spread it.
863
+ * Let Anthropic select the last cacheable block when caching is enabled
864
+ * without a caller-selected breakpoint. An explicit breakpoint remains
865
+ * authoritative, so the request-level form is deliberately omitted then.
866
+ */
867
+ buildAutomaticCacheControl(cacheControl) {
868
+ const breakpoints = cacheControl?.breakpoints ?? 0;
869
+ return this.config.promptCaching && breakpoints <= 0 ? { cache_control: this.cacheControl() } : {};
870
+ }
871
+ /** Build a typed cache-control marker, omitting TTL for Anthropic's 5-minute default. */
872
+ cacheControl() {
873
+ return {
874
+ type: "ephemeral",
875
+ ...this.config.promptCacheTtl ? { ttl: this.config.promptCacheTtl } : {}
876
+ };
877
+ }
878
+ /**
879
+ * Translate the neutral `reasoning` option into Anthropic's thinking
880
+ * settings, in the shape this model accepts (`thinkingMode`). Emitted
881
+ * only when the model declares the `reasoning` capability AND a
882
+ * reasoning option is supplied; otherwise returns an empty object.
883
+ *
884
+ * - Adaptive mode (Opus 4.7+, Sonnet 5+, Fable 5+, unknown ids):
885
+ * `thinking: { type: "adaptive" }` (installed SDK
886
+ * `ThinkingConfigAdaptive`, `messages.d.ts:1065`) plus
887
+ * `output_config.effort` from the neutral effort, defaulting to
888
+ * `"medium"`. `budget_tokens` is never sent — these models 400 on it
889
+ * (claude-api ref). `reasoning.maxTokens` only sizes the default
890
+ * `max_tokens` headroom.
891
+ * - Budget mode (Haiku 4.5, Opus/Sonnet 4.6, older):
892
+ * `thinking: { type: "enabled", budget_tokens }`. An explicit
893
+ * `reasoning.maxTokens` wins; otherwise the effort maps to a tiered
894
+ * budget. `budget_tokens` must be ≥ 1024 (`ThinkingConfigEnabled`,
895
+ * `messages.d.ts:1084`), so the budget is floored at that minimum.
736
896
  *
737
- * Budget resolution: an explicit `reasoning.maxTokens` wins; otherwise
738
- * the neutral `effort` level maps to a tiered token budget. Anthropic
739
- * requires `budget_tokens` ≥ 1024, so the budget is floored at that
740
- * minimum. The neutral `effort: "none"` ("run without reasoning") maps
741
- * to no `thinking` block at all — extended thinking is opt-in here.
897
+ * The neutral `effort: "none"` ("run without reasoning") maps to no
898
+ * `thinking` field at all in both modes.
742
899
  */
743
900
  buildThinking(reasoning) {
744
901
  if (!this.capabilities.reasoning || !reasoning) return {};
745
902
  if (reasoning.effort === "none") return {};
746
903
  if (reasoning.maxTokens === void 0 && reasoning.effort === void 0) return {};
747
- const budget = reasoning.maxTokens ?? EFFORT_THINKING_BUDGET[reasoning.effort ?? "medium"];
748
- return { thinking: {
749
- type: "enabled",
750
- budget_tokens: Math.max(MIN_THINKING_BUDGET, budget)
751
- } };
904
+ const effort = reasoning.effort ?? DEFAULT_THINKING_EFFORT;
905
+ const budget = Math.max(MIN_THINKING_BUDGET, reasoning.maxTokens ?? EFFORT_THINKING_BUDGET[effort]);
906
+ if (this.thinkingMode === "adaptive") return {
907
+ thinking: { type: "adaptive" },
908
+ effort: EFFORT_TO_OUTPUT_EFFORT[effort],
909
+ reservedTokens: budget
910
+ };
911
+ return {
912
+ thinking: {
913
+ type: "enabled",
914
+ budget_tokens: budget
915
+ },
916
+ reservedTokens: budget
917
+ };
752
918
  }
753
919
  /**
754
920
  * Spread-friendly tools fragment. Returns an empty object when no
@@ -768,32 +934,49 @@ var AnthropicModel = class {
768
934
  if (!mapped) return {};
769
935
  if (this.config.promptCaching && mapped.length > 0) {
770
936
  const last = mapped.length - 1;
771
- mapped[last] = {
772
- ...mapped[last],
773
- cache_control: { type: "ephemeral" }
937
+ const lastTool = mapped[last];
938
+ if (lastTool) mapped[last] = {
939
+ ...lastTool,
940
+ cache_control: this.cacheControl()
774
941
  };
775
942
  }
776
943
  return { tools: mapped };
777
944
  }
778
945
  /**
946
+ * Spread-friendly `output_config` fragment carrying both the native
947
+ * structured-output `format` and the adaptive-thinking `effort`, merged
948
+ * into one object (both live under `OutputConfig`, installed SDK
949
+ * `messages.d.ts:796-806`). Returns an empty object when neither applies,
950
+ * so budget-mode requests carry `output_config` only when a schema asks
951
+ * for it.
952
+ */
953
+ buildOutputConfig(responseSchema, effort) {
954
+ const format = this.buildStructuredOutputFormat(responseSchema);
955
+ if (!format && !effort) return {};
956
+ return { output_config: {
957
+ ...format ? { format } : {},
958
+ ...effort ? { effort } : {}
959
+ } };
960
+ }
961
+ /**
779
962
  * Translate the neutral `responseSchema` option into Anthropic's
780
963
  * native `output_config.format` (JSON-schema structured outputs).
781
964
  *
782
- * Only emitted when the model declares the `structuredOutput`
965
+ * Only returned when the model declares the `structuredOutput`
783
966
  * capability AND the schema is a proper root-object JSON Schema —
784
967
  * Anthropic rejects non-object roots. When the capability is off
785
- * (config override) or the schema is non-object, returns an empty
786
- * object: the agent has already injected a soft schema hint into the
787
- * system prompt as the fallback, and client-side `validate()` still
788
- * enforces shape.
968
+ * (config override) or the schema is non-object, returns `undefined`:
969
+ * the agent has already injected a soft schema hint into the system
970
+ * prompt as the fallback, and client-side `validate()` still enforces
971
+ * shape.
789
972
  */
790
- buildStructuredOutput(responseSchema) {
791
- if (!responseSchema || !this.capabilities.structuredOutput) return {};
792
- if (responseSchema.type !== "object" || typeof responseSchema.properties !== "object") return {};
793
- return { output_config: { format: {
973
+ buildStructuredOutputFormat(responseSchema) {
974
+ if (!responseSchema || !this.capabilities.structuredOutput) return;
975
+ if (responseSchema.type !== "object" || typeof responseSchema.properties !== "object") return;
976
+ return {
794
977
  type: "json_schema",
795
978
  schema: responseSchema
796
- } } };
979
+ };
797
980
  }
798
981
  /**
799
982
  * Concatenate every `text` content block into the single neutral
@@ -833,24 +1016,24 @@ var AnthropicModel = class {
833
1016
  * Anthropic reports `input_tokens` / `output_tokens` separately with
834
1017
  * no pre-summed total, so `total` is computed. Cache-read tokens are
835
1018
  * surfaced as `cachedTokens` and cache-write tokens as
836
- * `cacheWriteTokens`, each only when non-zero.
837
- *
838
- * Note: Anthropic does not report a separate reasoning-token count —
839
- * extended-thinking tokens are billed inside `output_tokens` — so
840
- * `Usage.reasoningTokens` is intentionally left unset here. Populating
841
- * it would double-count against `output`.
1019
+ * `cacheWriteTokens`, each only when non-zero. Anthropic also reports
1020
+ * `output_tokens_details.thinking_tokens` as the reasoning subset of
1021
+ * its inclusive `output_tokens`; it is surfaced as `reasoningTokens`
1022
+ * when present, without changing the billing total.
842
1023
  */
843
1024
  extractUsage(raw) {
844
1025
  const input = raw.input_tokens ?? 0;
845
1026
  const output = raw.output_tokens ?? 0;
846
1027
  const cacheRead = raw.cache_read_input_tokens;
847
1028
  const cacheWrite = raw.cache_creation_input_tokens;
1029
+ const thinkingTokens = raw.output_tokens_details?.thinking_tokens;
848
1030
  return {
849
1031
  input,
850
1032
  output,
851
1033
  total: input + output,
852
1034
  ...cacheRead !== null && cacheRead !== void 0 && cacheRead > 0 ? { cachedTokens: cacheRead } : {},
853
- ...cacheWrite !== null && cacheWrite !== void 0 && cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {}
1035
+ ...cacheWrite !== null && cacheWrite !== void 0 && cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {},
1036
+ ...thinkingTokens !== null && thinkingTokens !== void 0 ? { reasoningTokens: thinkingTokens } : {}
854
1037
  };
855
1038
  }
856
1039
  /**