@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/llms-full.txt CHANGED
@@ -8,7 +8,7 @@
8
8
 
9
9
  ---
10
10
  name: setup-anthropic
11
- description: 'Wire @warlock.js/ai-anthropic — new AnthropicSDK({apiKey, baseURL?, provider?}) for Claude, .model({name, vision?, structuredOutput?, reasoning?, promptCaching?, maxTokens?}). System-prompt hoisting, max_tokens required (default 4096), extended thinking via options.reasoning → thinking budget_tokens, prompt caching via promptCaching + options.cacheControl, cost-truth usage (cachedTokens/cacheWriteTokens), no first-party embeddings. Triggers: `AnthropicSDK`, `anthropic.model`, `anthropic.count`, `maxTokens`, `reasoning`, `thinking`, `budget_tokens`, `cacheControl`, `promptCaching`, `cachedTokens`, `cacheWriteTokens`, `claude-sonnet-4-6`, `claude-haiku-4-5`, `claude-opus-4-7`; "wire claude into warlock agent", "configure anthropic provider", "use claude sonnet", "anthropic gateway baseURL", "claude extended thinking", "anthropic prompt caching cost"; typical import `import { AnthropicSDK } from "@warlock.js/ai-anthropic"`. Skip: embeddings — `@warlock.js/ai-openai/setup-openai/SKILL.md`; sibling adapters `@warlock.js/ai-openai`, `@warlock.js/ai-bedrock`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`; raw `@anthropic-ai/sdk`; Vercel `@ai-sdk/anthropic`.'
11
+ description: 'Wire @warlock.js/ai-anthropic — new AnthropicSDK({apiKey, baseURL?, provider?}) for Claude, .model({name, vision?, structuredOutput?, reasoning?, thinkingMode?, promptCaching?, maxTokens?}). System-prompt hoisting, max_tokens required (default 4096), extended thinking via options.reasoning → adaptive thinking + output_config.effort (current/unknown models) or budget_tokens (Haiku 4.5, 4.6 and older; override with thinkingMode), prompt caching via promptCaching + options.cacheControl, cost-truth usage (cachedTokens/cacheWriteTokens), no first-party embeddings. Triggers: `AnthropicSDK`, `anthropic.model`, `anthropic.count`, `maxTokens`, `reasoning`, `thinking`, `budget_tokens`, `thinkingMode`, `adaptive`, `cacheControl`, `promptCaching`, `cachedTokens`, `cacheWriteTokens`, `claude-sonnet-4-6`, `claude-haiku-4-5`, `claude-opus-4-7`; "wire claude into warlock agent", "configure anthropic provider", "use claude sonnet", "anthropic gateway baseURL", "claude extended thinking", "anthropic prompt caching cost"; typical import `import { AnthropicSDK } from "@warlock.js/ai-anthropic"`. Skip: embeddings — `@warlock.js/ai-openai/setup-openai/SKILL.md`; sibling adapters `@warlock.js/ai-openai`, `@warlock.js/ai-bedrock`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`; raw `@anthropic-ai/sdk`; Vercel `@ai-sdk/anthropic`.'
12
12
  ---
13
13
 
14
14
  # `@warlock.js/ai-anthropic`
@@ -35,23 +35,23 @@ const proxied = new AnthropicSDK({
35
35
  ## Producing a model
36
36
 
37
37
  ```ts
38
- anthropic.model({ name: "claude-sonnet-4-6" }) // common case
39
- anthropic.model({ name: "claude-haiku-4-5", temperature: 0.2 }) // sampling controls
40
- anthropic.model({ name: "claude-opus-4-7", maxTokens: 8192 }) // raise the cap
38
+ anthropic.model({ name: "claude-sonnet-4-6" }); // common case
39
+ anthropic.model({ name: "claude-haiku-4-5", temperature: 0.2 }); // sampling controls
40
+ anthropic.model({ name: "claude-opus-4-7", maxTokens: 8192 }); // raise the cap
41
41
  ```
42
42
 
43
43
  ## Capabilities — what's auto-set
44
44
 
45
- | Flag | Default |
46
- | --- | --- |
47
- | `structuredOutput` | `true` (via Anthropic's native `output_config.format`) |
48
- | `vision` | Inferred from model name. `true` for Claude 3 / 3.5 / 3.7 / 4 family; `false` for pre-3 and unknown. |
49
- | `reasoning` | `true` — every modern Claude model accepts extended thinking. Override with `reasoning: false` for legacy/proxied targets that reject the `thinking` param. |
50
- | `promptCaching` | `true` — the adapter places `cache_control` breakpoints and reports both cache reads (`Usage.cachedTokens`) and writes (`Usage.cacheWriteTokens`). |
51
- | `pdf` | `true` — the Messages API accepts PDF/document content blocks on vision-capable models. |
52
- | `audio` | absent (`false`) — Anthropic has no audio-input block, so the agent rejects audio attachments upfront. |
45
+ | Flag | Default |
46
+ | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
47
+ | `structuredOutput` | `true` (via Anthropic's native `output_config.format`) |
48
+ | `vision` | Inferred from model name. `true` for Claude 3 / 3.5 / 3.7 / 4 and Claude 5-series (`claude-opus-5*`, `claude-sonnet-5*`, `claude-fable-5*`, `claude-mythos-5*`); `false` for pre-3 and unknown ids. |
49
+ | `reasoning` | `true` — forwarded in the shape the model accepts (see [Extended thinking](#extended-thinking-reasoning)). Override with `reasoning: false` for legacy/proxied targets that reject the `thinking` param. |
50
+ | `promptCaching` | `true` — the adapter places `cache_control` breakpoints and reports both cache reads (`Usage.cachedTokens`) and writes (`Usage.cacheWriteTokens`). |
51
+ | `pdf` | `true` — the Messages API accepts PDF/document content blocks on vision-capable models. |
52
+ | `audio` | absent (`false`) — Anthropic has no audio-input block, so the agent rejects audio attachments upfront. |
53
53
 
54
- Explicit config always wins (`structuredOutput`, `vision`, `reasoning`).
54
+ Explicit config always wins (`structuredOutput`, `vision`, `reasoning`, `thinkingMode`).
55
55
 
56
56
  ## Pricing & cost
57
57
 
@@ -62,7 +62,7 @@ Pricing is opt-in. Supply USD-per-million-token rates and every report this SDK'
62
62
  const anthropic = new AnthropicSDK({
63
63
  apiKey,
64
64
  pricing: {
65
- "claude-haiku-4-5": { input: 1, output: 5, cachedInput: 0.1 },
65
+ "claude-haiku-4-5": { input: 1, output: 5, cachedInput: 0.1 },
66
66
  "claude-sonnet-4-6": { input: 3, output: 15, cachedInput: 0.3 },
67
67
  },
68
68
  });
@@ -77,11 +77,11 @@ Resolution at `model()` time: per-model `pricing` > SDK registry entry for that
77
77
 
78
78
  Every `ModelResponse.usage` (and the streaming terminal `done`) is normalized to the neutral `Usage` shape. Anthropic reports `input_tokens` / `output_tokens` separately with no pre-summed total, so `total` is computed (`input + output`). The two cache channels are surfaced only when non-zero:
79
79
 
80
- | Neutral field | Anthropic source | Meaning / billing |
81
- | --- | --- | --- |
82
- | `usage.cachedTokens` | `cache_read_input_tokens` | Subset of input served from the prompt cache. Bills at `cachedInput` (falls back to `input` when unset). |
83
- | `usage.cacheWriteTokens` | `cache_creation_input_tokens` | Input tokens **written** to the cache on this call (the ~1.25x write surcharge). Bills at `cachedOutput` when set. |
84
- | `usage.reasoningTokens` | — *(not reported separately)* | Anthropic bills extended-thinking tokens **inside** `output_tokens`, so this stays unset — populating it would double-count against `output`. |
80
+ | Neutral field | Anthropic source | Meaning / billing |
81
+ | ------------------------ | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
82
+ | `usage.cachedTokens` | `cache_read_input_tokens` | Subset of input served from the prompt cache. Bills at `cachedInput` (falls back to `input` when unset). |
83
+ | `usage.cacheWriteTokens` | `cache_creation_input_tokens` | Input tokens **written** to the cache on this call (the ~1.25x write surcharge). Bills at `cachedOutput` when set. |
84
+ | `usage.reasoningTokens` | — _(not reported separately)_ | Anthropic bills extended-thinking tokens **inside** `output_tokens`, so this stays unset — populating it would double-count against `output`. |
85
85
 
86
86
  In streaming, `cachedTokens` / `cacheWriteTokens` are seeded from `message_start` and then **overwritten by the cumulative counts on `message_delta`** when those are present and non-zero, so the terminal `done` carries the final tally.
87
87
 
@@ -89,7 +89,9 @@ In streaming, `cachedTokens` / `cacheWriteTokens` are seeded from `message_start
89
89
 
90
90
  Unlike OpenAI, Anthropic **requires** `max_tokens` on every request. Resolution: per-call `options.maxTokens` > `config.maxTokens` > **default `4096`**. A caller who never sets a cap still gets a complete answer instead of a 400.
91
91
 
92
- With extended thinking enabled and no explicit cap, the adapter reserves answer room: `max_tokens = thinking budget + 4096`. An explicit `maxTokens` must be greater than the thinking budget or the call throws before it reaches Anthropic.
92
+ With extended thinking enabled and no explicit cap, the adapter reserves answer room in both modes: `max_tokens = thinking budget + 4096`. In budget mode an explicit `maxTokens` must be greater than `budget_tokens` or the call throws before it reaches Anthropic.
93
+
94
+ **Tiny caps + adaptive thinking:** thinking tokens count inside `max_tokens`, so a very small cap can be spent entirely on thinking. Live check: Sonnet 5.5 with `maxTokens: 60` returned `finishReason: "length"` and empty content. For tiny outputs give thinking room (raise `maxTokens`) or turn reasoning off (`reasoning: false` on the model, or no `options.reasoning`).
93
95
 
94
96
  ## System prompt
95
97
 
@@ -108,31 +110,70 @@ When the agent passes `responseSchema` and the model is `structuredOutput`-capab
108
110
 
109
111
  ## Extended thinking (reasoning)
110
112
 
111
- When the model is `reasoning`-capable (default `true`) and the agent passes `options.reasoning`, the adapter forwards Anthropic extended thinking as `thinking: { type: "enabled", budget_tokens }`:
113
+ When the model is `reasoning`-capable (default `true`) and the agent passes `options.reasoning`, the adapter forwards Anthropic thinking in one of two shapes, chosen per model (`thinkingMode`):
114
+
115
+ | Mode | Models (inferred from the name) | Wire shape |
116
+ | ------------ | ------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
117
+ | `"adaptive"` | Current Claude models — Opus 4.7 / 4.8 / 5 / 5.5, Sonnet 5 / 5.5, Fable 5 / 5.1 — **and any id the adapter does not recognise** | `thinking: { type: "adaptive" }` + `output_config.effort` |
118
+ | `"budget"` | Haiku 4.5, Opus 4.6, Sonnet 4.6, and every older model (Claude 3.x, 4.0 / 4.1 / 4.5, 2, instant) | `thinking: { type: "enabled", budget_tokens }` |
119
+
120
+ Adaptive models reject `budget_tokens` with a 400, so the shape matters.
121
+
122
+ - **Adaptive:** `reasoning.effort` (`"low" | "medium" | "high"`) → `output_config.effort` of the same name. With only `reasoning.maxTokens` set, effort is `"medium"`; `maxTokens` then only sizes the reserved `max_tokens` room, it is not sent as a budget.
123
+ - **Budget:** `reasoning.maxTokens` → `budget_tokens` verbatim; `reasoning.effort` → a tiered budget (`1024` / `4096` / `12000`) when no `maxTokens` is given. Any resolved budget is floored at Anthropic's `1024` minimum.
124
+ - `reasoning` with neither `effort` nor `maxTokens`, or `effort: "none"`, emits nothing.
112
125
 
113
- - `reasoning.maxTokens` → `budget_tokens` verbatim.
114
- - `reasoning.effort` (`"low" | "medium" | "high"`) → a tiered budget (`1024` / `4096` / `12000`) when no explicit `maxTokens` is given.
115
- - Any resolved budget is floored at Anthropic's `1024`-token minimum.
116
- - `reasoning` with neither `effort` nor `maxTokens` emits nothing.
126
+ ### `thinkingMode` override
117
127
 
118
- Because Anthropic 400s when `thinking` is combined with an explicit `temperature`, the adapter **drops `temperature`** for that request (thinking pins sampling to the default). Set `reasoning: false` on the model config to suppress the param entirely for proxied/legacy targets.
128
+ `anthropic.model({ name, thinkingMode: "adaptive" | "budget" })` beats the name-based detection. Use it when the id does not look like an Anthropic id: region-prefixed / Bedrock-style ids (`us.anthropic.claude-…`) and custom or gateway ids are _unrecognised_, so they default to adaptive. If they front an older model (Haiku 4.5, 4.6 or earlier) set `thinkingMode: "budget"`. It can also move Opus/Sonnet 4.6 onto adaptive. The `AnthropicThinkingMode` type is exported.
119
129
 
120
130
  ```ts
121
- await agent.execute("Prove it.", { reasoning: { effort: "high" } }); // budget_tokens: 12000
122
- await model.complete(messages, { reasoning: { maxTokens: 8000 } }); // budget_tokens: 8000
131
+ import { AnthropicSDK, type AnthropicThinkingMode } from "@warlock.js/ai-anthropic";
132
+
133
+ const anthropic = new AnthropicSDK({ apiKey: process.env.ANTHROPIC_API_KEY! });
134
+
135
+ const mode: AnthropicThinkingMode = "budget";
136
+
137
+ anthropic.model({ name: "claude-opus-5-5" }); // adaptive (detected)
138
+ anthropic.model({ name: "claude-haiku-4-5" }); // budget (detected)
139
+ anthropic.model({ name: "us.anthropic.claude-haiku-4-5", thinkingMode: mode }); // region-prefixed id of an older model
140
+ anthropic.model({ name: "claude-sonnet-4-6", thinkingMode: "adaptive" }); // opt 4.6 into adaptive
141
+ ```
142
+
143
+ ### Temperature
144
+
145
+ `temperature` is **not sent** to models after Opus 4.6 (adaptive mode) — they reject any non-default value with a 400. It is also dropped on any request that enables thinking, since Anthropic 400s on thinking plus an explicit temperature. So it is forwarded only in budget mode on requests without thinking; `thinkingMode` decides this too. Set `reasoning: false` on the model config to suppress the thinking param entirely for proxied/legacy targets.
146
+
147
+ ```ts
148
+ await agent.execute("Prove it.", { reasoning: { effort: "high" } });
149
+ // adaptive model: thinking { type: "adaptive" }, output_config.effort "high"
150
+ // budget model: thinking { type: "enabled", budget_tokens: 12000 }
151
+ await model.complete(messages, { reasoning: { maxTokens: 8000 } });
152
+ // adaptive model: effort "medium"; budget model: budget_tokens 8000
123
153
  ```
124
154
 
125
155
  When a thinking-enabled response also makes tool calls, its signed `thinking` and `redacted_thinking` blocks are retained in each call's `providerMetadata.anthropic.thinkingBlocks`. The adapter replays those opaque blocks automatically on the next assistant turn, preserving Anthropic's required signatures; application code should leave that metadata intact when persisting message history.
126
156
 
127
157
  ## Prompt caching (`cache_control`)
128
158
 
159
+ Caching is opt-in: `model({ promptCaching: true })` sends request-level
160
+ `cache_control: { type: "ephemeral" }` when a call has no explicit
161
+ `options.cacheControl.breakpoints`. Anthropic then marks the last cacheable
162
+ block itself, caching the longest reusable request prefix (including growing
163
+ message history). A caller-supplied breakpoint remains authoritative, so its
164
+ request has no top-level automatic marker.
165
+
166
+ Set `promptCacheTtl: "1h"` on the model to apply a one-hour TTL to automatic
167
+ caching and emitted explicit markers. Omit it (or use `"5m"`) for Anthropic's
168
+ default five-minute TTL; the one-hour cache has a higher write cost.
169
+
129
170
  Two independent breakpoint sites, both reported back via `usage.cachedTokens` / `usage.cacheWriteTokens`:
130
171
 
131
- - **Tools** — set `model({ promptCaching: true })` to mark the *last* tool definition with `cache_control: ephemeral`. One breakpoint caches the whole tool prefix; off by default since a write costs ~1.25x and only pays off across multiple trips.
132
- - **System prompt** — a per-call `options.cacheControl.breakpoints >= 1` emits the system prompt as a `TextBlockParam` carrying `cache_control: ephemeral` (the longest stable prefix on a turn). Without the hint the system prompt stays a plain string (uncached), so a one-shot call never pays the write surcharge. No system prompt → no block.
172
+ - **Tools** — set `model({ promptCaching: true })` to mark the _last_ tool definition with `cache_control: ephemeral`. One breakpoint caches the whole tool prefix; off by default since a write costs ~1.25x and only pays off across multiple trips.
173
+ - **System prompt** — a per-call `options.cacheControl.breakpoints >= 1` emits the system prompt as a `TextBlockParam` carrying `cache_control: ephemeral` (the longest stable prefix on a turn). Without the hint the system prompt stays a plain string with no marker of its own, but with `promptCaching: true` the automatic request-level marker still caches the longest reusable prefix, system prompt included (a live check read back 10,090 cached tokens of a large system prompt on the second call). With `promptCaching` off and no hint, nothing is cached, so a one-shot call never pays the write surcharge. A system prompt with per-turn placeholders changes the prefix each turn: write surcharge, no reads. No system prompt → no block.
133
174
 
134
175
  ```ts
135
- await agent.execute("…", { cacheControl: { breakpoints: 1 } }); // cache the system prefix
176
+ await agent.execute("…", { cacheControl: { breakpoints: 1 } }); // cache the system prefix
136
177
  ```
137
178
 
138
179
  ## Multipart messages (vision)
@@ -169,7 +210,7 @@ Raw SDK errors are wrapped into the typed `@warlock.js/ai` `AIError` hierarchy.
169
210
  ## Token counting
170
211
 
171
212
  ```ts
172
- await anthropic.count("some text") // approximate heuristic, offline
213
+ await anthropic.count("some text"); // approximate heuristic, offline
173
214
  ```
174
215
 
175
216
  ## No embeddings
package/llms.txt CHANGED
@@ -6,4 +6,4 @@
6
6
 
7
7
  ## Skills
8
8
 
9
- - [setup-anthropic](@warlock.js/ai-anthropic/setup-anthropic/SKILL.md): Wire @warlock.js/ai-anthropic — new AnthropicSDK({apiKey, baseURL?, provider?}) for Claude, .model({name, vision?, structuredOutput?, reasoning?, promptCaching?, maxTokens?}). System-prompt hoisting, max_tokens required (default 4096), extended thinking via options.reasoning → thinking budget_tokens, prompt caching via promptCaching + options.cacheControl, cost-truth usage (cachedTokens/cacheWriteTokens), no first-party embeddings. Triggers: `AnthropicSDK`, `anthropic.model`, `anthropic.count`, `maxTokens`, `reasoning`, `thinking`, `budget_tokens`, `cacheControl`, `promptCaching`, `cachedTokens`, `cacheWriteTokens`, `claude-sonnet-4-6`, `claude-haiku-4-5`, `claude-opus-4-7`; "wire claude into warlock agent", "configure anthropic provider", "use claude sonnet", "anthropic gateway baseURL", "claude extended thinking", "anthropic prompt caching cost"; typical import `import { AnthropicSDK } from "@warlock.js/ai-anthropic"`. Skip: embeddings — `@warlock.js/ai-openai/setup-openai/SKILL.md`; sibling adapters `@warlock.js/ai-openai`, `@warlock.js/ai-bedrock`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`; raw `@anthropic-ai/sdk`; Vercel `@ai-sdk/anthropic`.
9
+ - [setup-anthropic](@warlock.js/ai-anthropic/setup-anthropic/SKILL.md): Wire @warlock.js/ai-anthropic — new AnthropicSDK({apiKey, baseURL?, provider?}) for Claude, .model({name, vision?, structuredOutput?, reasoning?, thinkingMode?, promptCaching?, maxTokens?}). System-prompt hoisting, max_tokens required (default 4096), extended thinking via options.reasoning → adaptive thinking + output_config.effort (current/unknown models) or budget_tokens (Haiku 4.5, 4.6 and older; override with thinkingMode), prompt caching via promptCaching + options.cacheControl, cost-truth usage (cachedTokens/cacheWriteTokens), no first-party embeddings. Triggers: `AnthropicSDK`, `anthropic.model`, `anthropic.count`, `maxTokens`, `reasoning`, `thinking`, `budget_tokens`, `thinkingMode`, `adaptive`, `cacheControl`, `promptCaching`, `cachedTokens`, `cacheWriteTokens`, `claude-sonnet-4-6`, `claude-haiku-4-5`, `claude-opus-4-7`; "wire claude into warlock agent", "configure anthropic provider", "use claude sonnet", "anthropic gateway baseURL", "claude extended thinking", "anthropic prompt caching cost"; typical import `import { AnthropicSDK } from "@warlock.js/ai-anthropic"`. Skip: embeddings — `@warlock.js/ai-openai/setup-openai/SKILL.md`; sibling adapters `@warlock.js/ai-openai`, `@warlock.js/ai-bedrock`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`; raw `@anthropic-ai/sdk`; Vercel `@ai-sdk/anthropic`.
package/package.json CHANGED
@@ -14,13 +14,13 @@
14
14
  "url": "https://github.com/warlockjs/ai-anthropic"
15
15
  },
16
16
  "peerDependencies": {
17
- "@warlock.js/ai": "5.25.0"
17
+ "@warlock.js/ai": "5.26.0"
18
18
  },
19
19
  "dependencies": {
20
- "@anthropic-ai/sdk": "^0.96.0",
21
- "@warlock.js/logger": "5.25.0"
20
+ "@anthropic-ai/sdk": "^0.129.0",
21
+ "@warlock.js/logger": "5.26.0"
22
22
  },
23
- "version": "5.25.0",
23
+ "version": "5.26.0",
24
24
  "main": "./cjs/index.cjs",
25
25
  "module": "./esm/index.mjs",
26
26
  "types": "./esm/index.d.mts",
package/skills/README.md CHANGED
@@ -6,4 +6,4 @@ Per-task skills. All cross-references use the form `@warlock.js/<pkg>/<skill>/SK
6
6
 
7
7
  ### [`setup-anthropic/`](./setup-anthropic/SKILL.md)
8
8
 
9
- Wire @warlock.js/ai-anthropic — new AnthropicSDK({apiKey, baseURL?, provider?}) for Claude, .model({name, vision?, structuredOutput?, reasoning?, promptCaching?, maxTokens?}). System-prompt hoisting, max_tokens required (default 4096), extended thinking via options.reasoning → thinking budget_tokens, prompt caching via promptCaching + options.cacheControl with cost-truth usage (cachedTokens/cacheWriteTokens), no first-party embeddings. Load when wiring a Claude-backed model into a @warlock.js agent.
9
+ Wire @warlock.js/ai-anthropic — new AnthropicSDK({apiKey, baseURL?, provider?}) for Claude, .model({name, vision?, structuredOutput?, reasoning?, thinkingMode?, promptCaching?, maxTokens?}). System-prompt hoisting, max_tokens required (default 4096), extended thinking via options.reasoning → adaptive thinking + output_config.effort (current/unknown models) or budget_tokens (Haiku 4.5, 4.6 and older; override with thinkingMode), prompt caching via promptCaching + options.cacheControl with cost-truth usage (cachedTokens/cacheWriteTokens), no first-party embeddings. Load when wiring a Claude-backed model into a @warlock.js agent.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: setup-anthropic
3
- description: 'Wire @warlock.js/ai-anthropic — new AnthropicSDK({apiKey, baseURL?, provider?}) for Claude, .model({name, vision?, structuredOutput?, reasoning?, promptCaching?, maxTokens?}). System-prompt hoisting, max_tokens required (default 4096), extended thinking via options.reasoning → thinking budget_tokens, prompt caching via promptCaching + options.cacheControl, cost-truth usage (cachedTokens/cacheWriteTokens), no first-party embeddings. Triggers: `AnthropicSDK`, `anthropic.model`, `anthropic.count`, `maxTokens`, `reasoning`, `thinking`, `budget_tokens`, `cacheControl`, `promptCaching`, `cachedTokens`, `cacheWriteTokens`, `claude-sonnet-4-6`, `claude-haiku-4-5`, `claude-opus-4-7`; "wire claude into warlock agent", "configure anthropic provider", "use claude sonnet", "anthropic gateway baseURL", "claude extended thinking", "anthropic prompt caching cost"; typical import `import { AnthropicSDK } from "@warlock.js/ai-anthropic"`. Skip: embeddings — `@warlock.js/ai-openai/setup-openai/SKILL.md`; sibling adapters `@warlock.js/ai-openai`, `@warlock.js/ai-bedrock`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`; raw `@anthropic-ai/sdk`; Vercel `@ai-sdk/anthropic`.'
3
+ description: 'Wire @warlock.js/ai-anthropic — new AnthropicSDK({apiKey, baseURL?, provider?}) for Claude, .model({name, vision?, structuredOutput?, reasoning?, thinkingMode?, promptCaching?, maxTokens?}). System-prompt hoisting, max_tokens required (default 4096), extended thinking via options.reasoning → adaptive thinking + output_config.effort (current/unknown models) or budget_tokens (Haiku 4.5, 4.6 and older; override with thinkingMode), prompt caching via promptCaching + options.cacheControl, cost-truth usage (cachedTokens/cacheWriteTokens), no first-party embeddings. Triggers: `AnthropicSDK`, `anthropic.model`, `anthropic.count`, `maxTokens`, `reasoning`, `thinking`, `budget_tokens`, `thinkingMode`, `adaptive`, `cacheControl`, `promptCaching`, `cachedTokens`, `cacheWriteTokens`, `claude-sonnet-4-6`, `claude-haiku-4-5`, `claude-opus-4-7`; "wire claude into warlock agent", "configure anthropic provider", "use claude sonnet", "anthropic gateway baseURL", "claude extended thinking", "anthropic prompt caching cost"; typical import `import { AnthropicSDK } from "@warlock.js/ai-anthropic"`. Skip: embeddings — `@warlock.js/ai-openai/setup-openai/SKILL.md`; sibling adapters `@warlock.js/ai-openai`, `@warlock.js/ai-bedrock`, `@warlock.js/ai-google`, `@warlock.js/ai-ollama`; raw `@anthropic-ai/sdk`; Vercel `@ai-sdk/anthropic`.'
4
4
  ---
5
5
 
6
6
  # `@warlock.js/ai-anthropic`
@@ -27,23 +27,23 @@ const proxied = new AnthropicSDK({
27
27
  ## Producing a model
28
28
 
29
29
  ```ts
30
- anthropic.model({ name: "claude-sonnet-4-6" }) // common case
31
- anthropic.model({ name: "claude-haiku-4-5", temperature: 0.2 }) // sampling controls
32
- anthropic.model({ name: "claude-opus-4-7", maxTokens: 8192 }) // raise the cap
30
+ anthropic.model({ name: "claude-sonnet-4-6" }); // common case
31
+ anthropic.model({ name: "claude-haiku-4-5", temperature: 0.2 }); // sampling controls
32
+ anthropic.model({ name: "claude-opus-4-7", maxTokens: 8192 }); // raise the cap
33
33
  ```
34
34
 
35
35
  ## Capabilities — what's auto-set
36
36
 
37
- | Flag | Default |
38
- | --- | --- |
39
- | `structuredOutput` | `true` (via Anthropic's native `output_config.format`) |
40
- | `vision` | Inferred from model name. `true` for Claude 3 / 3.5 / 3.7 / 4 family; `false` for pre-3 and unknown. |
41
- | `reasoning` | `true` — every modern Claude model accepts extended thinking. Override with `reasoning: false` for legacy/proxied targets that reject the `thinking` param. |
42
- | `promptCaching` | `true` — the adapter places `cache_control` breakpoints and reports both cache reads (`Usage.cachedTokens`) and writes (`Usage.cacheWriteTokens`). |
43
- | `pdf` | `true` — the Messages API accepts PDF/document content blocks on vision-capable models. |
44
- | `audio` | absent (`false`) — Anthropic has no audio-input block, so the agent rejects audio attachments upfront. |
37
+ | Flag | Default |
38
+ | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
39
+ | `structuredOutput` | `true` (via Anthropic's native `output_config.format`) |
40
+ | `vision` | Inferred from model name. `true` for Claude 3 / 3.5 / 3.7 / 4 and Claude 5-series (`claude-opus-5*`, `claude-sonnet-5*`, `claude-fable-5*`, `claude-mythos-5*`); `false` for pre-3 and unknown ids. |
41
+ | `reasoning` | `true` — forwarded in the shape the model accepts (see [Extended thinking](#extended-thinking-reasoning)). Override with `reasoning: false` for legacy/proxied targets that reject the `thinking` param. |
42
+ | `promptCaching` | `true` — the adapter places `cache_control` breakpoints and reports both cache reads (`Usage.cachedTokens`) and writes (`Usage.cacheWriteTokens`). |
43
+ | `pdf` | `true` — the Messages API accepts PDF/document content blocks on vision-capable models. |
44
+ | `audio` | absent (`false`) — Anthropic has no audio-input block, so the agent rejects audio attachments upfront. |
45
45
 
46
- Explicit config always wins (`structuredOutput`, `vision`, `reasoning`).
46
+ Explicit config always wins (`structuredOutput`, `vision`, `reasoning`, `thinkingMode`).
47
47
 
48
48
  ## Pricing & cost
49
49
 
@@ -54,7 +54,7 @@ Pricing is opt-in. Supply USD-per-million-token rates and every report this SDK'
54
54
  const anthropic = new AnthropicSDK({
55
55
  apiKey,
56
56
  pricing: {
57
- "claude-haiku-4-5": { input: 1, output: 5, cachedInput: 0.1 },
57
+ "claude-haiku-4-5": { input: 1, output: 5, cachedInput: 0.1 },
58
58
  "claude-sonnet-4-6": { input: 3, output: 15, cachedInput: 0.3 },
59
59
  },
60
60
  });
@@ -69,11 +69,11 @@ Resolution at `model()` time: per-model `pricing` > SDK registry entry for that
69
69
 
70
70
  Every `ModelResponse.usage` (and the streaming terminal `done`) is normalized to the neutral `Usage` shape. Anthropic reports `input_tokens` / `output_tokens` separately with no pre-summed total, so `total` is computed (`input + output`). The two cache channels are surfaced only when non-zero:
71
71
 
72
- | Neutral field | Anthropic source | Meaning / billing |
73
- | --- | --- | --- |
74
- | `usage.cachedTokens` | `cache_read_input_tokens` | Subset of input served from the prompt cache. Bills at `cachedInput` (falls back to `input` when unset). |
75
- | `usage.cacheWriteTokens` | `cache_creation_input_tokens` | Input tokens **written** to the cache on this call (the ~1.25x write surcharge). Bills at `cachedOutput` when set. |
76
- | `usage.reasoningTokens` | — *(not reported separately)* | Anthropic bills extended-thinking tokens **inside** `output_tokens`, so this stays unset — populating it would double-count against `output`. |
72
+ | Neutral field | Anthropic source | Meaning / billing |
73
+ | ------------------------ | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
74
+ | `usage.cachedTokens` | `cache_read_input_tokens` | Subset of input served from the prompt cache. Bills at `cachedInput` (falls back to `input` when unset). |
75
+ | `usage.cacheWriteTokens` | `cache_creation_input_tokens` | Input tokens **written** to the cache on this call (the ~1.25x write surcharge). Bills at `cachedOutput` when set. |
76
+ | `usage.reasoningTokens` | — _(not reported separately)_ | Anthropic bills extended-thinking tokens **inside** `output_tokens`, so this stays unset — populating it would double-count against `output`. |
77
77
 
78
78
  In streaming, `cachedTokens` / `cacheWriteTokens` are seeded from `message_start` and then **overwritten by the cumulative counts on `message_delta`** when those are present and non-zero, so the terminal `done` carries the final tally.
79
79
 
@@ -81,7 +81,9 @@ In streaming, `cachedTokens` / `cacheWriteTokens` are seeded from `message_start
81
81
 
82
82
  Unlike OpenAI, Anthropic **requires** `max_tokens` on every request. Resolution: per-call `options.maxTokens` > `config.maxTokens` > **default `4096`**. A caller who never sets a cap still gets a complete answer instead of a 400.
83
83
 
84
- With extended thinking enabled and no explicit cap, the adapter reserves answer room: `max_tokens = thinking budget + 4096`. An explicit `maxTokens` must be greater than the thinking budget or the call throws before it reaches Anthropic.
84
+ With extended thinking enabled and no explicit cap, the adapter reserves answer room in both modes: `max_tokens = thinking budget + 4096`. In budget mode an explicit `maxTokens` must be greater than `budget_tokens` or the call throws before it reaches Anthropic.
85
+
86
+ **Tiny caps + adaptive thinking:** thinking tokens count inside `max_tokens`, so a very small cap can be spent entirely on thinking. Live check: Sonnet 5.5 with `maxTokens: 60` returned `finishReason: "length"` and empty content. For tiny outputs give thinking room (raise `maxTokens`) or turn reasoning off (`reasoning: false` on the model, or no `options.reasoning`).
85
87
 
86
88
  ## System prompt
87
89
 
@@ -100,31 +102,70 @@ When the agent passes `responseSchema` and the model is `structuredOutput`-capab
100
102
 
101
103
  ## Extended thinking (reasoning)
102
104
 
103
- When the model is `reasoning`-capable (default `true`) and the agent passes `options.reasoning`, the adapter forwards Anthropic extended thinking as `thinking: { type: "enabled", budget_tokens }`:
105
+ When the model is `reasoning`-capable (default `true`) and the agent passes `options.reasoning`, the adapter forwards Anthropic thinking in one of two shapes, chosen per model (`thinkingMode`):
106
+
107
+ | Mode | Models (inferred from the name) | Wire shape |
108
+ | ------------ | ------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
109
+ | `"adaptive"` | Current Claude models — Opus 4.7 / 4.8 / 5 / 5.5, Sonnet 5 / 5.5, Fable 5 / 5.1 — **and any id the adapter does not recognise** | `thinking: { type: "adaptive" }` + `output_config.effort` |
110
+ | `"budget"` | Haiku 4.5, Opus 4.6, Sonnet 4.6, and every older model (Claude 3.x, 4.0 / 4.1 / 4.5, 2, instant) | `thinking: { type: "enabled", budget_tokens }` |
111
+
112
+ Adaptive models reject `budget_tokens` with a 400, so the shape matters.
113
+
114
+ - **Adaptive:** `reasoning.effort` (`"low" | "medium" | "high"`) → `output_config.effort` of the same name. With only `reasoning.maxTokens` set, effort is `"medium"`; `maxTokens` then only sizes the reserved `max_tokens` room, it is not sent as a budget.
115
+ - **Budget:** `reasoning.maxTokens` → `budget_tokens` verbatim; `reasoning.effort` → a tiered budget (`1024` / `4096` / `12000`) when no `maxTokens` is given. Any resolved budget is floored at Anthropic's `1024` minimum.
116
+ - `reasoning` with neither `effort` nor `maxTokens`, or `effort: "none"`, emits nothing.
104
117
 
105
- - `reasoning.maxTokens` → `budget_tokens` verbatim.
106
- - `reasoning.effort` (`"low" | "medium" | "high"`) → a tiered budget (`1024` / `4096` / `12000`) when no explicit `maxTokens` is given.
107
- - Any resolved budget is floored at Anthropic's `1024`-token minimum.
108
- - `reasoning` with neither `effort` nor `maxTokens` emits nothing.
118
+ ### `thinkingMode` override
109
119
 
110
- Because Anthropic 400s when `thinking` is combined with an explicit `temperature`, the adapter **drops `temperature`** for that request (thinking pins sampling to the default). Set `reasoning: false` on the model config to suppress the param entirely for proxied/legacy targets.
120
+ `anthropic.model({ name, thinkingMode: "adaptive" | "budget" })` beats the name-based detection. Use it when the id does not look like an Anthropic id: region-prefixed / Bedrock-style ids (`us.anthropic.claude-…`) and custom or gateway ids are _unrecognised_, so they default to adaptive. If they front an older model (Haiku 4.5, 4.6 or earlier) set `thinkingMode: "budget"`. It can also move Opus/Sonnet 4.6 onto adaptive. The `AnthropicThinkingMode` type is exported.
111
121
 
112
122
  ```ts
113
- await agent.execute("Prove it.", { reasoning: { effort: "high" } }); // budget_tokens: 12000
114
- await model.complete(messages, { reasoning: { maxTokens: 8000 } }); // budget_tokens: 8000
123
+ import { AnthropicSDK, type AnthropicThinkingMode } from "@warlock.js/ai-anthropic";
124
+
125
+ const anthropic = new AnthropicSDK({ apiKey: process.env.ANTHROPIC_API_KEY! });
126
+
127
+ const mode: AnthropicThinkingMode = "budget";
128
+
129
+ anthropic.model({ name: "claude-opus-5-5" }); // adaptive (detected)
130
+ anthropic.model({ name: "claude-haiku-4-5" }); // budget (detected)
131
+ anthropic.model({ name: "us.anthropic.claude-haiku-4-5", thinkingMode: mode }); // region-prefixed id of an older model
132
+ anthropic.model({ name: "claude-sonnet-4-6", thinkingMode: "adaptive" }); // opt 4.6 into adaptive
133
+ ```
134
+
135
+ ### Temperature
136
+
137
+ `temperature` is **not sent** to models after Opus 4.6 (adaptive mode) — they reject any non-default value with a 400. It is also dropped on any request that enables thinking, since Anthropic 400s on thinking plus an explicit temperature. So it is forwarded only in budget mode on requests without thinking; `thinkingMode` decides this too. Set `reasoning: false` on the model config to suppress the thinking param entirely for proxied/legacy targets.
138
+
139
+ ```ts
140
+ await agent.execute("Prove it.", { reasoning: { effort: "high" } });
141
+ // adaptive model: thinking { type: "adaptive" }, output_config.effort "high"
142
+ // budget model: thinking { type: "enabled", budget_tokens: 12000 }
143
+ await model.complete(messages, { reasoning: { maxTokens: 8000 } });
144
+ // adaptive model: effort "medium"; budget model: budget_tokens 8000
115
145
  ```
116
146
 
117
147
  When a thinking-enabled response also makes tool calls, its signed `thinking` and `redacted_thinking` blocks are retained in each call's `providerMetadata.anthropic.thinkingBlocks`. The adapter replays those opaque blocks automatically on the next assistant turn, preserving Anthropic's required signatures; application code should leave that metadata intact when persisting message history.
118
148
 
119
149
  ## Prompt caching (`cache_control`)
120
150
 
151
+ Caching is opt-in: `model({ promptCaching: true })` sends request-level
152
+ `cache_control: { type: "ephemeral" }` when a call has no explicit
153
+ `options.cacheControl.breakpoints`. Anthropic then marks the last cacheable
154
+ block itself, caching the longest reusable request prefix (including growing
155
+ message history). A caller-supplied breakpoint remains authoritative, so its
156
+ request has no top-level automatic marker.
157
+
158
+ Set `promptCacheTtl: "1h"` on the model to apply a one-hour TTL to automatic
159
+ caching and emitted explicit markers. Omit it (or use `"5m"`) for Anthropic's
160
+ default five-minute TTL; the one-hour cache has a higher write cost.
161
+
121
162
  Two independent breakpoint sites, both reported back via `usage.cachedTokens` / `usage.cacheWriteTokens`:
122
163
 
123
- - **Tools** — set `model({ promptCaching: true })` to mark the *last* tool definition with `cache_control: ephemeral`. One breakpoint caches the whole tool prefix; off by default since a write costs ~1.25x and only pays off across multiple trips.
124
- - **System prompt** — a per-call `options.cacheControl.breakpoints >= 1` emits the system prompt as a `TextBlockParam` carrying `cache_control: ephemeral` (the longest stable prefix on a turn). Without the hint the system prompt stays a plain string (uncached), so a one-shot call never pays the write surcharge. No system prompt → no block.
164
+ - **Tools** — set `model({ promptCaching: true })` to mark the _last_ tool definition with `cache_control: ephemeral`. One breakpoint caches the whole tool prefix; off by default since a write costs ~1.25x and only pays off across multiple trips.
165
+ - **System prompt** — a per-call `options.cacheControl.breakpoints >= 1` emits the system prompt as a `TextBlockParam` carrying `cache_control: ephemeral` (the longest stable prefix on a turn). Without the hint the system prompt stays a plain string with no marker of its own, but with `promptCaching: true` the automatic request-level marker still caches the longest reusable prefix, system prompt included (a live check read back 10,090 cached tokens of a large system prompt on the second call). With `promptCaching` off and no hint, nothing is cached, so a one-shot call never pays the write surcharge. A system prompt with per-turn placeholders changes the prefix each turn: write surcharge, no reads. No system prompt → no block.
125
166
 
126
167
  ```ts
127
- await agent.execute("…", { cacheControl: { breakpoints: 1 } }); // cache the system prefix
168
+ await agent.execute("…", { cacheControl: { breakpoints: 1 } }); // cache the system prefix
128
169
  ```
129
170
 
130
171
  ## Multipart messages (vision)
@@ -161,7 +202,7 @@ Raw SDK errors are wrapped into the typed `@warlock.js/ai` `AIError` hierarchy.
161
202
  ## Token counting
162
203
 
163
204
  ```ts
164
- await anthropic.count("some text") // approximate heuristic, offline
205
+ await anthropic.count("some text"); // approximate heuristic, offline
165
206
  ```
166
207
 
167
208
  ## No embeddings