@warlock.js/ai-anthropic 5.30.0 → 5.32.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.
Files changed (50) hide show
  1. package/CHANGELOG.md +58 -6
  2. package/cjs/index.cjs +847 -138
  3. package/cjs/index.cjs.map +1 -1
  4. package/esm/config.type.d.mts +117 -8
  5. package/esm/config.type.d.mts.map +1 -1
  6. package/esm/index.d.mts +9 -3
  7. package/esm/index.mjs +7 -1
  8. package/esm/known-output-limits.mjs +46 -0
  9. package/esm/known-output-limits.mjs.map +1 -0
  10. package/esm/known-pricing.d.mts +63 -0
  11. package/esm/known-pricing.d.mts.map +1 -0
  12. package/esm/known-pricing.mjs +172 -0
  13. package/esm/known-pricing.mjs.map +1 -0
  14. package/esm/model.d.mts +90 -20
  15. package/esm/model.d.mts.map +1 -1
  16. package/esm/model.mjs +389 -88
  17. package/esm/model.mjs.map +1 -1
  18. package/esm/request.type.d.mts +23 -0
  19. package/esm/request.type.d.mts.map +1 -0
  20. package/esm/sdk.d.mts +1 -3
  21. package/esm/sdk.d.mts.map +1 -1
  22. package/esm/thinking-mode.d.mts +14 -2
  23. package/esm/thinking-mode.d.mts.map +1 -1
  24. package/esm/thinking-mode.mjs +102 -2
  25. package/esm/thinking-mode.mjs.map +1 -1
  26. package/esm/utils/cache-usage.mjs +62 -0
  27. package/esm/utils/cache-usage.mjs.map +1 -0
  28. package/esm/utils/index.d.mts +6 -0
  29. package/esm/utils/index.mjs +1 -1
  30. package/esm/utils/map-stop-reason.d.mts +1 -0
  31. package/esm/utils/provider-blocks.d.mts +37 -0
  32. package/esm/utils/provider-blocks.d.mts.map +1 -0
  33. package/esm/utils/provider-blocks.mjs +3 -3
  34. package/esm/utils/provider-blocks.mjs.map +1 -1
  35. package/esm/utils/server-tool-usage.mjs +17 -0
  36. package/esm/utils/server-tool-usage.mjs.map +1 -0
  37. package/esm/utils/to-anthropic-messages.d.mts +69 -0
  38. package/esm/utils/to-anthropic-messages.d.mts.map +1 -0
  39. package/esm/utils/to-anthropic-messages.mjs +12 -4
  40. package/esm/utils/to-anthropic-messages.mjs.map +1 -1
  41. package/esm/utils/to-anthropic-tools.d.mts +17 -0
  42. package/esm/utils/to-anthropic-tools.d.mts.map +1 -0
  43. package/esm/utils/wrap-anthropic-error.d.mts +1 -0
  44. package/esm/utils/wrap-anthropic-error.mjs +6 -1
  45. package/esm/utils/wrap-anthropic-error.mjs.map +1 -1
  46. package/llms-full.txt +248 -32
  47. package/llms.txt +1 -1
  48. package/package.json +3 -3
  49. package/skills/README.md +1 -1
  50. package/skills/setup-anthropic/SKILL.md +248 -32
package/esm/model.mjs CHANGED
@@ -1,24 +1,44 @@
1
+ import { anthropicPricingFor, withAnthropicCacheWriteRates } from "./known-pricing.mjs";
2
+ import { acceptsTemperature, inferPreservedThinking, inferThinkingMode, noReasoningForm } from "./thinking-mode.mjs";
3
+ import { inferMaxOutputTokens } from "./known-output-limits.mjs";
1
4
  import { inferVisionCapability } from "./known-vision-models.mjs";
2
- import { acceptsBetweenTools, acceptsTemperature, inferThinkingMode } from "./thinking-mode.mjs";
5
+ import { applyServerToolUsage } from "./utils/server-tool-usage.mjs";
6
+ import { applyAnthropicCacheUsage, requestCacheTtl } from "./utils/cache-usage.mjs";
3
7
  import { mapStopReason } from "./utils/map-stop-reason.mjs";
4
8
  import { toProviderBlocks } from "./utils/provider-blocks.mjs";
5
9
  import { toAnthropicMessages } from "./utils/to-anthropic-messages.mjs";
6
10
  import { toAnthropicTools } from "./utils/to-anthropic-tools.mjs";
7
11
  import { wrapAnthropicError } from "./utils/wrap-anthropic-error.mjs";
8
12
  import "./utils/index.mjs";
9
- import { InvalidRequestError, safeJsonParse } from "@warlock.js/ai";
13
+ import { AIError, InvalidRequestError, computeCost, resolveStreamChunks, safeJsonParse, selectProviderTools } from "@warlock.js/ai";
10
14
  import { log } from "@warlock.js/logger";
11
15
 
12
16
  //#region ../../packages/ai-anthropic/src/model.ts
13
17
  const LOG_MODULE = "ai.anthropic";
18
+ /** `provider` value of `ai.providerTool()` entries this adapter sends verbatim. */
19
+ const ANTHROPIC_WIRE_FORMAT = "anthropic";
14
20
  /**
15
21
  * Anthropic requires `max_tokens` on every request (unlike OpenAI,
16
- * where it is optional). When neither the per-call option nor the
17
- * model config supplies one, fall back to a generous default so a
18
- * caller who never thought about token caps still gets a complete
19
- * answer instead of a 400.
22
+ * where it is optional). When neither the per-call option nor the model
23
+ * config supplies one, the adapter picks a default large enough for agent
24
+ * work (see `resolveDefaultMaxTokens`):
25
+ *
26
+ * - streaming: the model's documented max output (`known-output-limits.ts`,
27
+ * or `config.maxOutputTokens`), else this value;
28
+ * - non-streaming: this value, capped by the model's max output. The
29
+ * installed `@anthropic-ai/sdk` (0.129.0, `calculateNonstreamingTimeout`)
30
+ * refuses a non-streaming request whose `max_tokens` implies more than
31
+ * ten minutes (above ~21,333 tokens) unless the client sets a `timeout`,
32
+ * so the non-streaming default stays below that.
33
+ */
34
+ const DEFAULT_MAX_TOKENS = 16384;
35
+ /**
36
+ * Answer room kept on top of a thinking budget when the default
37
+ * `max_tokens` is sized: thinking counts toward `max_tokens` (installed
38
+ * SDK `ThinkingConfigParam` docs, `messages.d.ts:1105`), and in budget
39
+ * mode `max_tokens` must exceed `budget_tokens`.
20
40
  */
21
- const DEFAULT_MAX_TOKENS = 4096;
41
+ const MIN_ANSWER_TOKENS = 4096;
22
42
  /**
23
43
  * Anthropic rejects an extended-thinking budget below 1024 tokens, so
24
44
  * any resolved budget is floored at this minimum before it reaches the
@@ -37,11 +57,10 @@ const DEFAULT_THINKING_EFFORT = "medium";
37
57
  *
38
58
  * - Budget-mode models: sent as `thinking.budget_tokens` when the caller
39
59
  * doesn't pass an explicit `reasoning.maxTokens`.
40
- * - Both modes: reserved on top of `DEFAULT_MAX_TOKENS` when the caller
41
- * sets no `maxTokens`, because thinking counts toward `max_tokens`
42
- * (installed SDK `ThinkingConfigParam` docs, `messages.d.ts:1105`).
43
- * Adaptive models therefore get the same `max_tokens` the budget path
44
- * has always produced.
60
+ * - Both modes: when the caller sets no `maxTokens`, the default
61
+ * `max_tokens` keeps at least this budget plus `MIN_ANSWER_TOKENS`,
62
+ * because thinking counts toward `max_tokens` (installed SDK
63
+ * `ThinkingConfigParam` docs, `messages.d.ts:1105`).
45
64
  */
46
65
  const EFFORT_THINKING_BUDGET = {
47
66
  minimal: 1024,
@@ -67,7 +86,7 @@ const EFFORT_TO_OUTPUT_EFFORT = {
67
86
  max: "max"
68
87
  };
69
88
  /** Content-block types whose input arrives as `input_json_delta` while streaming. */
70
- const STREAMED_INPUT_BLOCK_TYPES = new Set([
89
+ const STREAMED_INPUT_BLOCK_TYPES = /* @__PURE__ */ new Set([
71
90
  "tool_use",
72
91
  "server_tool_use",
73
92
  "mcp_tool_use"
@@ -117,8 +136,10 @@ var AnthropicModel = class {
117
136
  this.config = config;
118
137
  this.name = config.name;
119
138
  this.provider = provider;
120
- this.pricing = config.pricing;
139
+ this.pricing = withAnthropicCacheWriteRates(config.pricing ?? anthropicPricingFor(config.name));
121
140
  this.thinkingMode = config.thinkingMode ?? inferThinkingMode(config.name);
141
+ this.maxOutputTokens = config.maxOutputTokens ?? inferMaxOutputTokens(config.name);
142
+ this.preservedThinking = config.preservedThinking ?? (this.thinkingMode === "adaptive" && inferPreservedThinking(config.name));
122
143
  this.capabilities = {
123
144
  structuredOutput: config.structuredOutput ?? true,
124
145
  vision: config.vision ?? inferVisionCapability(config.name),
@@ -134,23 +155,50 @@ var AnthropicModel = class {
134
155
  * instance's `ModelConfig` defaults for this call only.
135
156
  */
136
157
  async complete(messages, options) {
137
- this.logger.debug(LOG_MODULE, "request", "Starting call to messages.create", {
138
- model: this.name,
139
- messageCount: messages.length,
140
- streaming: false,
141
- toolCount: options?.tools?.length ?? 0
158
+ return this.send(this.buildLoggedRequest(messages, {
159
+ ...options,
160
+ stream: false
161
+ }), { signal: options?.signal });
162
+ }
163
+ buildRequest(messages, options) {
164
+ const built = this.buildBody(messages, options);
165
+ const betas = [.../* @__PURE__ */ new Set([...resolveAnthropicOptions(options).betas ?? [], ...built.betas])];
166
+ const body = structuredClone({
167
+ ...built.body,
168
+ stream: options?.stream ?? false
142
169
  });
170
+ return {
171
+ endpoint: betas.length > 0 ? "beta.messages" : "messages",
172
+ body,
173
+ betas
174
+ };
175
+ }
176
+ buildLoggedRequest(messages, options) {
177
+ try {
178
+ return this.buildRequest(messages, options);
179
+ } catch (thrown) {
180
+ throw this.logAndWrap(thrown instanceof AIError ? thrown : new InvalidRequestError(`Could not build the Anthropic request: ${thrown instanceof Error ? thrown.message : String(thrown)}`, { cause: thrown }));
181
+ }
182
+ }
183
+ send(request, options) {
184
+ const message = request.body.stream ? "Starting streaming call to messages.create" : "Starting call to messages.create";
185
+ this.logger.debug(LOG_MODULE, "request", message, {
186
+ model: request.body.model,
187
+ messageCount: Array.isArray(request.body.messages) ? request.body.messages.length : 0,
188
+ streaming: request.body.stream,
189
+ toolCount: Array.isArray(request.body.tools) ? request.body.tools.length : 0
190
+ });
191
+ return request.body.stream ? this.streamRequest(request, options) : this.completeRequest(request, options);
192
+ }
193
+ async completeRequest(request, options) {
143
194
  let response;
144
195
  try {
145
- response = await this.send({
146
- ...this.buildBody(messages, options),
147
- stream: false
148
- }, options, "create");
196
+ response = await this.dispatch(request, options, "create");
149
197
  } catch (thrown) {
150
198
  throw this.logAndWrap(thrown);
151
199
  }
152
200
  const finishReason = mapStopReason(response.stop_reason);
153
- const usage = this.extractUsage(response.usage);
201
+ const usage = this.extractUsage(response.usage, requestCacheTtl(request.body));
154
202
  const toolCalls = this.extractToolCalls(response.content);
155
203
  const blocks = toProviderBlocks(response.content);
156
204
  this.logger.debug(LOG_MODULE, "response", "call to messages.create succeeded", {
@@ -177,11 +225,18 @@ var AnthropicModel = class {
177
225
  * const tokens = await model.countTokens(messages, { tools });
178
226
  */
179
227
  async countTokens(messages, options) {
180
- const body = this.buildBody(messages, options);
228
+ const request = this.buildLoggedRequest(messages, {
229
+ ...options,
230
+ stream: options?.stream === true
231
+ });
232
+ const body = request.body;
181
233
  const countBody = {};
182
234
  for (const key of COUNT_TOKENS_FIELDS) if (body[key] !== void 0) countBody[key] = body[key];
183
235
  try {
184
- return (await this.send(countBody, options, "countTokens")).input_tokens;
236
+ return (await this.dispatch({
237
+ ...request,
238
+ body: countBody
239
+ }, options, "countTokens")).input_tokens;
185
240
  } catch (thrown) {
186
241
  throw this.logAndWrap(thrown);
187
242
  }
@@ -192,29 +247,38 @@ var AnthropicModel = class {
192
247
  * arguments have fully accumulated, and a terminal `done` carrying the
193
248
  * final finish reason + usage totals. Callers consume it with
194
249
  * `for await`.
250
+ *
251
+ * With `options.streamChunks` it also yields `reasoning` for every
252
+ * non-empty `thinking_delta`, `tool-args` for every `input_json_delta`
253
+ * of a client `tool_use` block, and `usage` after `message_start` and
254
+ * each `message_delta` (whose counts are cumulative).
195
255
  */
196
256
  async *stream(messages, options) {
197
- this.logger.debug(LOG_MODULE, "request", "Starting streaming call to messages.create", {
198
- model: this.name,
199
- messageCount: messages.length,
200
- streaming: true,
201
- toolCount: options?.tools?.length ?? 0
257
+ yield* this.send(this.buildLoggedRequest(messages, {
258
+ ...options,
259
+ stream: true
260
+ }), {
261
+ signal: options?.signal,
262
+ streamChunks: options?.streamChunks
202
263
  });
264
+ }
265
+ async *streamRequest(request, options) {
203
266
  let stream;
204
267
  try {
205
- stream = await this.send({
206
- ...this.buildBody(messages, options),
207
- stream: true
208
- }, options, "create");
268
+ stream = await this.dispatch(request, options, "create");
209
269
  } catch (thrown) {
210
270
  throw this.logAndWrap(thrown);
211
271
  }
212
272
  let rawStopReason = null;
273
+ let compaction = compactionIterationUsage(void 0);
213
274
  const usage = {
214
275
  input: 0,
215
276
  output: 0,
216
- total: 0
277
+ total: 0,
278
+ inputIncludesCache: false
217
279
  };
280
+ const cacheTtl = requestCacheTtl(request.body);
281
+ const want = resolveStreamChunks(options);
218
282
  const toolBlocks = /* @__PURE__ */ new Map();
219
283
  const thinkingBlocks = /* @__PURE__ */ new Map();
220
284
  const streamedBlocks = /* @__PURE__ */ new Map();
@@ -225,10 +289,15 @@ var AnthropicModel = class {
225
289
  usage.input = event.message.usage.input_tokens ?? 0;
226
290
  const thinkingTokens = event.message.usage.output_tokens_details?.thinking_tokens;
227
291
  if (thinkingTokens !== null && thinkingTokens !== void 0) usage.reasoningTokens = thinkingTokens;
228
- const cacheRead = event.message.usage.cache_read_input_tokens;
229
- if (cacheRead !== null && cacheRead !== void 0 && cacheRead > 0) usage.cachedTokens = cacheRead;
230
- const cacheWrite = event.message.usage.cache_creation_input_tokens;
231
- if (cacheWrite !== null && cacheWrite !== void 0 && cacheWrite > 0) usage.cacheWriteTokens = cacheWrite;
292
+ applyServerToolUsage(usage, event.message.usage.server_tool_use);
293
+ applyAnthropicCacheUsage(usage, event.message.usage, cacheTtl);
294
+ if (want.usage) yield {
295
+ type: "usage",
296
+ usage: {
297
+ ...usage,
298
+ total: usage.input + usage.output
299
+ }
300
+ };
232
301
  continue;
233
302
  }
234
303
  if (event.type === "content_block_start") {
@@ -249,10 +318,22 @@ var AnthropicModel = class {
249
318
  };
250
319
  else if (event.delta.type === "input_json_delta") {
251
320
  const accumulator = toolBlocks.get(event.index);
252
- if (accumulator) accumulator.json += event.delta.partial_json;
321
+ if (accumulator) {
322
+ accumulator.json += event.delta.partial_json;
323
+ if (want.toolArgs && event.delta.partial_json !== "") yield {
324
+ type: "tool-args",
325
+ callId: accumulator.id,
326
+ name: accumulator.name,
327
+ delta: event.delta.partial_json
328
+ };
329
+ }
253
330
  } else if (event.delta.type === "thinking_delta") {
254
331
  const thinking = thinkingBlocks.get(event.index);
255
332
  if (thinking?.type === "thinking") thinking.thinking += event.delta.thinking;
333
+ if (want.reasoning && event.delta.thinking !== "") yield {
334
+ type: "reasoning",
335
+ delta: event.delta.thinking
336
+ };
256
337
  } else if (event.delta.type === "signature_delta") {
257
338
  const thinking = thinkingBlocks.get(event.index);
258
339
  if (thinking?.type === "thinking") thinking.signature += event.delta.signature;
@@ -275,19 +356,34 @@ var AnthropicModel = class {
275
356
  }
276
357
  if (event.type === "message_delta") {
277
358
  rawStopReason = event.delta.stop_reason ?? rawStopReason;
359
+ if (Array.isArray(event.usage.iterations)) compaction = compactionIterationUsage(event.usage);
360
+ applyServerToolUsage(usage, event.usage.server_tool_use);
278
361
  usage.output = event.usage.output_tokens ?? usage.output;
279
362
  const thinkingTokens = event.usage.output_tokens_details?.thinking_tokens;
280
363
  if (thinkingTokens !== null && thinkingTokens !== void 0) usage.reasoningTokens = thinkingTokens;
281
- const cacheRead = event.usage.cache_read_input_tokens;
282
- if (cacheRead !== null && cacheRead !== void 0 && cacheRead > 0) usage.cachedTokens = cacheRead;
283
- const cacheWrite = event.usage.cache_creation_input_tokens;
284
- if (cacheWrite !== null && cacheWrite !== void 0 && cacheWrite > 0) usage.cacheWriteTokens = cacheWrite;
364
+ applyAnthropicCacheUsage(usage, {
365
+ cache_read_input_tokens: event.usage.cache_read_input_tokens,
366
+ cache_creation_input_tokens: event.usage.cache_creation_input_tokens,
367
+ cache_creation: usage.cacheWrite1hTokens !== void 0 ? { ephemeral_1h_input_tokens: usage.cacheWrite1hTokens } : void 0
368
+ }, cacheTtl);
369
+ if (want.usage) yield {
370
+ type: "usage",
371
+ usage: {
372
+ ...usage,
373
+ total: usage.input + usage.output
374
+ }
375
+ };
285
376
  }
286
377
  }
287
378
  } catch (thrown) {
288
379
  throw this.logAndWrap(thrown);
289
380
  }
381
+ usage.input += compaction.input;
382
+ usage.output += compaction.output;
383
+ addCacheCounts(usage, compaction);
290
384
  usage.total = usage.input + usage.output;
385
+ const cost = computeCost(usage, this.pricing);
386
+ if (cost) usage.cost = cost;
291
387
  const finishReason = mapStopReason(rawStopReason);
292
388
  const blocks = toProviderBlocks(this.finishStreamedBlocks(streamedBlocks));
293
389
  this.logger.debug(LOG_MODULE, "response", "Streaming call to messages.create succeeded", {
@@ -341,38 +437,74 @@ var AnthropicModel = class {
341
437
  * and the beta-only body fields); otherwise to `client.messages`. The
342
438
  * cancellation signal rides in the request options, as before.
343
439
  */
344
- send(body, options, method) {
345
- const betas = resolveAnthropicOptions(options).betas;
440
+ dispatch(request, options, method) {
346
441
  const requestOptions = options?.signal ? { signal: options.signal } : void 0;
347
- const api = betas && betas.length > 0 ? this.client.beta.messages : this.client.messages;
348
- return api[method].call(api, betas && betas.length > 0 ? {
349
- ...body,
350
- betas
351
- } : body, requestOptions);
442
+ const api = request.endpoint === "beta.messages" ? this.client.beta.messages : this.client.messages;
443
+ return api[method].call(api, request.endpoint === "beta.messages" ? {
444
+ ...request.body,
445
+ betas: request.betas
446
+ } : request.body, requestOptions);
352
447
  }
353
448
  /**
354
- * The full request body: the adapter-built params, then
355
- * `thinking.display` when thinking is sent on an adaptive model, then
356
- * `providerOptions.anthropic.body` merged last (caller fields win). The
357
- * body may add fields the adapter does not build (`context_management`,
358
- * `fallbacks`, …) but may not replace the conversation or the model —
359
- * see {@link RESERVED_BODY_FIELDS}.
449
+ * The full request body and the beta flags it needs: the adapter-built
450
+ * params, then `thinking.display` when thinking is sent on an adaptive
451
+ * model, the typed `thinkingBlockBinding`, `contextManagement` and
452
+ * `serverCompaction` options, then `providerOptions.anthropic.body`
453
+ * merged last (caller fields win). The body may add fields the adapter
454
+ * does not build (`fallbacks`, …) but may not replace the conversation or
455
+ * the model — see {@link RESERVED_BODY_FIELDS}.
360
456
  */
361
457
  buildBody(messages, options) {
362
458
  const anthropic = resolveAnthropicOptions(options);
363
- const body = { ...this.buildParams(messages, options) };
459
+ const betas = [];
460
+ const body = { ...this.buildParams(messages, options, { dropRetainedThinking: this.preservedThinking && anthropic.thinkingBlockBinding === void 0 }) };
364
461
  if (anthropic.thinkingDisplay && this.thinkingMode === "adaptive" && body.thinking?.type === "adaptive") body.thinking = {
365
462
  ...body.thinking,
366
463
  display: anthropic.thinkingDisplay
367
464
  };
465
+ if (anthropic.thinkingBlockBinding) this.applyBlockBinding(body, anthropic.thinkingBlockBinding, betas);
466
+ const edits = buildContextEdits(anthropic);
467
+ if (edits.length > 0) {
468
+ if (anthropic.body?.context_management !== void 0) throw new InvalidRequestError("providerOptions.anthropic.contextManagement / serverCompaction cannot be combined with body.context_management: set the edits in one place.", { context: { fields: ["context_management"] } });
469
+ body.context_management = { edits };
470
+ if (edits.some((edit) => edit.type !== "compact_20260112")) betas.push(CONTEXT_MANAGEMENT_BETA);
471
+ if (edits.some((edit) => edit.type === "compact_20260112")) betas.push(COMPACTION_BETA);
472
+ }
368
473
  const reserved = Object.keys(anthropic.body ?? {}).filter((key) => RESERVED_BODY_FIELDS.has(key));
369
474
  if (reserved.length > 0) throw new InvalidRequestError(`providerOptions.anthropic.body cannot set ${reserved.map((key) => `"${key}"`).join(", ")}: the adapter builds these from the call's messages, tools and model. Use the agent/model options instead.`, { context: { fields: reserved } });
370
475
  return {
371
- ...body,
372
- ...anthropic.body
476
+ body: {
477
+ ...body,
478
+ ...anthropic.body
479
+ },
480
+ betas
373
481
  };
374
482
  }
375
483
  /**
484
+ * Set `thinking.block_binding.prefix_mismatch_behavior`. It rides on
485
+ * adaptive or budget (`enabled`) thinking. A model that runs adaptive
486
+ * thinking when `thinking` is omitted (every preserved-thinking model) gets
487
+ * an explicit `{ type: "adaptive" }`, which is the same request. Any other
488
+ * shape (`between_tools`, `disabled`) takes no extra field, so the option
489
+ * is skipped with a debug log.
490
+ */
491
+ applyBlockBinding(body, behavior, betas) {
492
+ const thinking = body.thinking;
493
+ const type = thinking?.type ?? (this.preservedThinking ? "adaptive" : void 0);
494
+ if (type !== "adaptive" && type !== "enabled") {
495
+ this.logger.debug(LOG_MODULE, "thinking-block-binding.ignored", "thinkingBlockBinding needs adaptive or budget thinking on this request; not sent", {
496
+ model: this.name,
497
+ thinking: thinking?.type ?? "none"
498
+ });
499
+ return;
500
+ }
501
+ body.thinking = {
502
+ ...thinking ?? { type },
503
+ block_binding: { prefix_mismatch_behavior: behavior }
504
+ };
505
+ betas.push(THINKING_BINDING_BETA);
506
+ }
507
+ /**
376
508
  * Assemble the Anthropic request body shared by `complete()` and
377
509
  * `stream()` (each adds its own `stream` literal so the SDK's create
378
510
  * overload resolves to the right return type). Hoists the system
@@ -384,13 +516,13 @@ var AnthropicModel = class {
384
516
  * rejects the two together, and always for adaptive-mode models, which
385
517
  * reject any explicit temperature (see `acceptsTemperature`).
386
518
  */
387
- buildParams(messages, options) {
388
- const { system, messages: anthropicMessages } = toAnthropicMessages(messages);
519
+ buildParams(messages, options, conversion) {
520
+ const { system, messages: anthropicMessages } = toAnthropicMessages(messages, conversion);
389
521
  const thinking = this.buildThinking(options?.reasoning);
390
522
  const temperature = options?.temperature ?? this.config.temperature;
391
523
  const configuredMaxTokens = options?.maxTokens ?? this.config.maxTokens;
392
524
  const thinkingBudget = thinking.thinking?.type === "enabled" ? thinking.thinking.budget_tokens : void 0;
393
- const maxTokens = configuredMaxTokens ?? (thinking.reservedTokens !== void 0 ? thinking.reservedTokens + DEFAULT_MAX_TOKENS : DEFAULT_MAX_TOKENS);
525
+ const maxTokens = configuredMaxTokens ?? this.resolveDefaultMaxTokens(options?.stream === true, thinking.reservedTokens);
394
526
  const sendTemperature = temperature !== void 0 && !thinking.thinking && acceptsTemperature(this.thinkingMode);
395
527
  if (thinkingBudget !== void 0 && configuredMaxTokens !== void 0 && configuredMaxTokens <= thinkingBudget) throw new Error(`Anthropic maxTokens (${configuredMaxTokens}) must be greater than thinking budget_tokens (${thinkingBudget})`);
396
528
  return {
@@ -400,12 +532,31 @@ var AnthropicModel = class {
400
532
  ...this.buildAutomaticCacheControl(options?.cacheControl),
401
533
  ...this.buildSystem(system, options?.cacheControl),
402
534
  ...sendTemperature ? { temperature } : {},
403
- ...this.buildTools(options?.tools, options?.cacheControl),
535
+ ...this.buildTools(options, options?.cacheControl),
404
536
  ...this.buildOutputConfig(options?.responseSchema, thinking.effort),
405
537
  ...thinking.thinking ? { thinking: thinking.thinking } : {}
406
538
  };
407
539
  }
408
540
  /**
541
+ * The `max_tokens` sent when neither the call nor the model config sets
542
+ * `maxTokens`:
543
+ *
544
+ * - streaming: the model's documented max output, or
545
+ * `DEFAULT_MAX_TOKENS` (16384) for an unknown id;
546
+ * - non-streaming: `DEFAULT_MAX_TOKENS`, capped by the model's max output
547
+ * (the SDK requires streaming for larger non-streaming requests unless
548
+ * the client sets a `timeout`; see `DEFAULT_MAX_TOKENS`).
549
+ *
550
+ * With thinking on, the value is raised to at least the thinking budget
551
+ * plus `MIN_ANSWER_TOKENS`, and never exceeds the model's max output.
552
+ */
553
+ resolveDefaultMaxTokens(stream, reservedTokens) {
554
+ const limit = this.maxOutputTokens;
555
+ const base = stream ? limit ?? DEFAULT_MAX_TOKENS : limit === void 0 ? DEFAULT_MAX_TOKENS : Math.min(limit, DEFAULT_MAX_TOKENS);
556
+ const withThinking = reservedTokens === void 0 ? base : Math.max(base, reservedTokens + MIN_ANSWER_TOKENS);
557
+ return limit === void 0 ? withThinking : Math.min(withThinking, limit);
558
+ }
559
+ /**
409
560
  * Spread-friendly `system` fragment. Returns an empty object when no
410
561
  * system prompt was hoisted out of the messages.
411
562
  *
@@ -468,12 +619,18 @@ var AnthropicModel = class {
468
619
  * `messages.d.ts:1084`), so the budget is floored at that minimum.
469
620
  *
470
621
  * The neutral `effort: "none"` ("run without reasoning") is mapped per
471
- * model:
472
- * - Claude Sonnet 5.5 (`acceptsBetweenTools`): `thinking: { type:
473
- * "between_tools" }`, its only thinking-off form. It is sent alone —
474
- * no `output_config.effort` (the model default, `high`, is within the
475
- * `high`-or-below range it requires) and no `display`, both of which
476
- * the API rejects alongside it.
622
+ * model by `noReasoningForm` (see `thinking-mode.ts` for the sources):
623
+ * - Claude Sonnet 5.5: `thinking: { type: "between_tools" }`, its only
624
+ * thinking-off form. It is sent alone — no `output_config.effort` (the
625
+ * model default, `high`, is within the `high`-or-below range it
626
+ * requires) and no `display`, both of which the API rejects alongside it.
627
+ * - Claude Sonnet 5 and Claude Haiku 5.5: `thinking: { type: "disabled" }`
628
+ * with no effort and no `display`. Haiku 5.5 accepts `disabled` only at
629
+ * effort `high` or below; its default (`medium`) applies.
630
+ * - Claude Opus 5: adaptive thinking at `output_config.effort: "low"`.
631
+ * Opus 5 accepts `disabled`, but with it the model can write a tool call
632
+ * as plain text (the call never runs) and leak thinking tags; the lowest
633
+ * effort is the documented alternative.
477
634
  * - Opus 5.5 and Fable 5/5.1: no `thinking` field. These models have no
478
635
  * thinking-off form (`disabled` and `budget_tokens` are a 400 at every
479
636
  * effort), so thinking still runs at the model's default effort. Pass
@@ -482,7 +639,7 @@ var AnthropicModel = class {
482
639
  */
483
640
  buildThinking(reasoning) {
484
641
  if (!this.capabilities.reasoning || !reasoning) return {};
485
- if (reasoning.effort === "none") return acceptsBetweenTools(this.name) ? { thinking: { type: "between_tools" } } : {};
642
+ if (reasoning.effort === "none") return this.buildNoReasoning();
486
643
  if (reasoning.maxTokens === void 0 && reasoning.effort === void 0) return {};
487
644
  const effort = reasoning.effort ?? DEFAULT_THINKING_EFFORT;
488
645
  const budget = Math.max(MIN_THINKING_BUDGET, reasoning.maxTokens ?? EFFORT_THINKING_BUDGET[effort]);
@@ -500,6 +657,22 @@ var AnthropicModel = class {
500
657
  };
501
658
  }
502
659
  /**
660
+ * The thinking settings for `reasoning: { effort: "none" }`, per
661
+ * `noReasoningForm(this.name)` (documented on `buildThinking`).
662
+ */
663
+ buildNoReasoning() {
664
+ switch (noReasoningForm(this.name)) {
665
+ case "between_tools": return { thinking: { type: "between_tools" } };
666
+ case "disabled": return { thinking: { type: "disabled" } };
667
+ case "lowest-effort": return {
668
+ thinking: { type: "adaptive" },
669
+ effort: EFFORT_TO_OUTPUT_EFFORT.low,
670
+ reservedTokens: EFFORT_THINKING_BUDGET.low
671
+ };
672
+ case "omit": return {};
673
+ }
674
+ }
675
+ /**
503
676
  * Spread-friendly tools fragment. Returns an empty object when no
504
677
  * tools were supplied so the caller can unconditionally spread it.
505
678
  *
@@ -512,9 +685,16 @@ var AnthropicModel = class {
512
685
  * breakpoint there would pay the ~1.25x write surcharge every turn
513
686
  * with no reads.
514
687
  */
515
- buildTools(tools, cacheControl) {
688
+ buildTools(options, cacheControl) {
689
+ const { tools, definitions } = selectProviderTools(options, {
690
+ accepts: [ANTHROPIC_WIRE_FORMAT, this.provider],
691
+ module: LOG_MODULE,
692
+ model: this.name,
693
+ logger: this.logger
694
+ });
516
695
  const mapped = toAnthropicTools(tools);
517
- if (!mapped) return {};
696
+ const providerDefined = definitions;
697
+ if (!mapped) return providerDefined.length > 0 ? { tools: providerDefined } : {};
518
698
  if (this.config.promptCaching && mapped.length > 0) {
519
699
  const last = mapped.length - 1;
520
700
  const lastTool = mapped[last];
@@ -523,7 +703,7 @@ var AnthropicModel = class {
523
703
  cache_control: this.cacheControl(cacheControl?.ttl)
524
704
  };
525
705
  }
526
- return { tools: mapped };
706
+ return { tools: [...providerDefined, ...mapped] };
527
707
  }
528
708
  /**
529
709
  * Spread-friendly `output_config` fragment carrying both the native
@@ -597,27 +777,35 @@ var AnthropicModel = class {
597
777
  /**
598
778
  * Normalize Anthropic's `usage` block into the neutral `Usage` shape.
599
779
  * Anthropic reports `input_tokens` / `output_tokens` separately with
600
- * no pre-summed total, so `total` is computed. Cache-read tokens are
601
- * surfaced as `cachedTokens` and cache-write tokens as
602
- * `cacheWriteTokens`, each only when non-zero. Anthropic also reports
780
+ * no pre-summed total, so `total` is computed. `input_tokens` is the
781
+ * uncached remainder only — cache reads (`cachedTokens`) and writes
782
+ * (`cacheWriteTokens`, with the 1-hour share in `cacheWrite1hTokens`)
783
+ * come on top of it — so the usage is marked
784
+ * `inputIncludesCache: false`. Anthropic also reports
603
785
  * `output_tokens_details.thinking_tokens` as the reasoning subset of
604
786
  * its inclusive `output_tokens`; it is surfaced as `reasoningTokens`
605
- * when present, without changing the billing total.
787
+ * when present, without changing the billing total. Server-side
788
+ * compaction iterations are added in (see {@link compactionIterationUsage}).
789
+ * `cost` is attached when the model is priced.
606
790
  */
607
- extractUsage(raw) {
608
- const input = raw.input_tokens ?? 0;
609
- const output = raw.output_tokens ?? 0;
610
- const cacheRead = raw.cache_read_input_tokens;
611
- const cacheWrite = raw.cache_creation_input_tokens;
791
+ extractUsage(raw, cacheTtl) {
792
+ const compaction = compactionIterationUsage(raw);
793
+ const input = (raw.input_tokens ?? 0) + compaction.input;
794
+ const output = (raw.output_tokens ?? 0) + compaction.output;
612
795
  const thinkingTokens = raw.output_tokens_details?.thinking_tokens;
613
- return {
796
+ const usage = {
614
797
  input,
615
798
  output,
616
799
  total: input + output,
617
- ...cacheRead !== null && cacheRead !== void 0 && cacheRead > 0 ? { cachedTokens: cacheRead } : {},
618
- ...cacheWrite !== null && cacheWrite !== void 0 && cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {},
800
+ inputIncludesCache: false,
619
801
  ...thinkingTokens !== null && thinkingTokens !== void 0 ? { reasoningTokens: thinkingTokens } : {}
620
802
  };
803
+ applyServerToolUsage(usage, raw.server_tool_use);
804
+ applyAnthropicCacheUsage(usage, raw, cacheTtl);
805
+ addCacheCounts(usage, compaction);
806
+ const cost = computeCost(usage, this.pricing);
807
+ if (cost) usage.cost = cost;
808
+ return usage;
621
809
  }
622
810
  /**
623
811
  * Wrap a thrown provider error into the typed `AIError` hierarchy and
@@ -640,7 +828,7 @@ var AnthropicModel = class {
640
828
  * model call tools the agent never registered). `stream` is chosen per
641
829
  * call by `complete()` / `stream()`.
642
830
  */
643
- const RESERVED_BODY_FIELDS = new Set([
831
+ const RESERVED_BODY_FIELDS = /* @__PURE__ */ new Set([
644
832
  "model",
645
833
  "messages",
646
834
  "system",
@@ -661,6 +849,119 @@ const COUNT_TOKENS_FIELDS = [
661
849
  "context_management",
662
850
  "mcp_servers"
663
851
  ];
852
+ /** Beta flag for typed context editing (Anthropic docs, "Context editing", read 2026-10-17). */
853
+ const CONTEXT_MANAGEMENT_BETA = "context-management-2025-06-27";
854
+ /** Beta flag for threshold compaction (Anthropic docs, "Compaction at a token threshold"). */
855
+ const COMPACTION_BETA = "compact-2026-01-12";
856
+ /** Beta flag for `thinking.block_binding` (Anthropic docs, "Preserved thinking"). */
857
+ const THINKING_BINDING_BETA = "thinking-binding-controls-2026-08-01";
858
+ /** The API rejects a compaction trigger below 50,000 input tokens. */
859
+ const MIN_COMPACTION_TRIGGER = 5e4;
860
+ /**
861
+ * Translate the typed `contextManagement` / `serverCompaction` options into
862
+ * `context_management.edits`. `clear_thinking_20251015` must come first
863
+ * when strategies are combined (Anthropic docs, "Context editing").
864
+ */
865
+ function buildContextEdits(anthropic) {
866
+ const edits = [];
867
+ const { clearThinking, clearToolUses } = anthropic.contextManagement ?? {};
868
+ if (clearThinking) edits.push(clearThinkingEdit(clearThinking === true ? {} : clearThinking));
869
+ if (clearToolUses) edits.push(clearToolUsesEdit(clearToolUses === true ? {} : clearToolUses));
870
+ if (anthropic.serverCompaction) {
871
+ const compaction = anthropic.serverCompaction === true ? {} : anthropic.serverCompaction;
872
+ if (compaction.triggerTokens !== void 0) assertCount("serverCompaction.triggerTokens", compaction.triggerTokens, MIN_COMPACTION_TRIGGER);
873
+ edits.push({
874
+ type: "compact_20260112",
875
+ ...compaction.triggerTokens !== void 0 ? { trigger: {
876
+ type: "input_tokens",
877
+ value: compaction.triggerTokens
878
+ } } : {},
879
+ ...compaction.instructions ? { instructions: compaction.instructions } : {}
880
+ });
881
+ }
882
+ return edits;
883
+ }
884
+ function clearThinkingEdit(options) {
885
+ if (options.keep === void 0 || options.keep === "all") return {
886
+ type: "clear_thinking_20251015",
887
+ ...options.keep ? { keep: "all" } : {}
888
+ };
889
+ assertCount("contextManagement.clearThinking.keep", options.keep, 1);
890
+ return {
891
+ type: "clear_thinking_20251015",
892
+ keep: {
893
+ type: "thinking_turns",
894
+ value: options.keep
895
+ }
896
+ };
897
+ }
898
+ function clearToolUsesEdit(options) {
899
+ const edit = { type: "clear_tool_uses_20250919" };
900
+ if (options.trigger) {
901
+ const trigger = "inputTokens" in options.trigger ? {
902
+ type: "input_tokens",
903
+ value: options.trigger.inputTokens
904
+ } : {
905
+ type: "tool_uses",
906
+ value: options.trigger.toolUses
907
+ };
908
+ assertCount("contextManagement.clearToolUses.trigger", trigger.value, 1);
909
+ edit.trigger = trigger;
910
+ }
911
+ if (options.keep !== void 0) {
912
+ assertCount("contextManagement.clearToolUses.keep", options.keep, 0);
913
+ edit.keep = {
914
+ type: "tool_uses",
915
+ value: options.keep
916
+ };
917
+ }
918
+ if (options.clearAtLeast !== void 0) {
919
+ assertCount("contextManagement.clearToolUses.clearAtLeast", options.clearAtLeast, 1);
920
+ edit.clear_at_least = {
921
+ type: "input_tokens",
922
+ value: options.clearAtLeast
923
+ };
924
+ }
925
+ if (options.excludeTools !== void 0) edit.exclude_tools = [...options.excludeTools];
926
+ if (options.clearToolInputs !== void 0) edit.clear_tool_inputs = Array.isArray(options.clearToolInputs) ? [...options.clearToolInputs] : options.clearToolInputs;
927
+ return edit;
928
+ }
929
+ function assertCount(field, value, minimum) {
930
+ if (!Number.isSafeInteger(value) || value < minimum) throw new InvalidRequestError(`providerOptions.anthropic.${field} must be an integer of at least ${minimum}; got ${value}.`, { context: { field } });
931
+ }
932
+ /**
933
+ * Tokens of the `compaction` entries in a beta response's `usage.iterations`.
934
+ * With server-side compaction the top-level `input_tokens` / `output_tokens`
935
+ * cover only the non-compaction iterations; the billed total is the sum
936
+ * over every iteration (Anthropic docs, "Compaction at a token threshold" →
937
+ * "Understanding usage", read 2026-10-17). Zeros when there are none.
938
+ */
939
+ function compactionIterationUsage(raw) {
940
+ const totals = {
941
+ input: 0,
942
+ output: 0,
943
+ cacheRead: 0,
944
+ cacheWrite: 0
945
+ };
946
+ const iterations = raw?.iterations;
947
+ if (!Array.isArray(iterations)) return totals;
948
+ for (const entry of iterations) {
949
+ const iteration = entry;
950
+ if (iteration?.type !== "compaction") continue;
951
+ totals.input += numberOrZero(iteration.input_tokens);
952
+ totals.output += numberOrZero(iteration.output_tokens);
953
+ totals.cacheRead += numberOrZero(iteration.cache_read_input_tokens);
954
+ totals.cacheWrite += numberOrZero(iteration.cache_creation_input_tokens);
955
+ }
956
+ return totals;
957
+ }
958
+ function numberOrZero(value) {
959
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
960
+ }
961
+ function addCacheCounts(usage, extra) {
962
+ if (extra.cacheRead > 0) usage.cachedTokens = (usage.cachedTokens ?? 0) + extra.cacheRead;
963
+ if (extra.cacheWrite > 0) usage.cacheWriteTokens = (usage.cacheWriteTokens ?? 0) + extra.cacheWrite;
964
+ }
664
965
  /** Read `providerOptions.anthropic`, tolerating its absence or a wrong shape. */
665
966
  function resolveAnthropicOptions(options) {
666
967
  const raw = options?.providerOptions?.anthropic;