@warlock.js/ai-anthropic 5.30.0 → 5.31.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 (43) hide show
  1. package/CHANGELOG.md +44 -6
  2. package/cjs/index.cjs +815 -132
  3. package/cjs/index.cjs.map +1 -1
  4. package/esm/config.type.d.mts +107 -4
  5. package/esm/config.type.d.mts.map +1 -1
  6. package/esm/index.d.mts +8 -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 +58 -0
  11. package/esm/known-pricing.d.mts.map +1 -0
  12. package/esm/known-pricing.mjs +165 -0
  13. package/esm/known-pricing.mjs.map +1 -0
  14. package/esm/model.d.mts +89 -17
  15. package/esm/model.d.mts.map +1 -1
  16. package/esm/model.mjs +383 -86
  17. package/esm/model.mjs.map +1 -1
  18. package/esm/request.type.d.mts +25 -0
  19. package/esm/request.type.d.mts.map +1 -0
  20. package/esm/thinking-mode.d.mts +14 -1
  21. package/esm/thinking-mode.d.mts.map +1 -1
  22. package/esm/thinking-mode.mjs +102 -2
  23. package/esm/thinking-mode.mjs.map +1 -1
  24. package/esm/utils/cache-usage.mjs +62 -0
  25. package/esm/utils/cache-usage.mjs.map +1 -0
  26. package/esm/utils/index.d.mts +3 -0
  27. package/esm/utils/map-stop-reason.d.mts +1 -0
  28. package/esm/utils/provider-blocks.d.mts +39 -0
  29. package/esm/utils/provider-blocks.d.mts.map +1 -0
  30. package/esm/utils/to-anthropic-messages.d.mts +71 -0
  31. package/esm/utils/to-anthropic-messages.d.mts.map +1 -0
  32. package/esm/utils/to-anthropic-messages.mjs +12 -4
  33. package/esm/utils/to-anthropic-messages.mjs.map +1 -1
  34. package/esm/utils/to-anthropic-tools.d.mts +19 -0
  35. package/esm/utils/to-anthropic-tools.d.mts.map +1 -0
  36. package/esm/utils/wrap-anthropic-error.d.mts +1 -0
  37. package/esm/utils/wrap-anthropic-error.mjs +6 -1
  38. package/esm/utils/wrap-anthropic-error.mjs.map +1 -1
  39. package/llms-full.txt +214 -32
  40. package/llms.txt +1 -1
  41. package/package.json +3 -3
  42. package/skills/README.md +1 -1
  43. package/skills/setup-anthropic/SKILL.md +214 -32
package/cjs/index.cjs CHANGED
@@ -31,58 +31,166 @@ _anthropic_ai_sdk = __toESM(_anthropic_ai_sdk, 1);
31
31
  let _warlock_js_ai = require("@warlock.js/ai");
32
32
  let _warlock_js_logger = require("@warlock.js/logger");
33
33
 
34
- //#region ../../packages/ai-anthropic/src/known-vision-models.ts
34
+ //#region ../../packages/ai-anthropic/src/known-pricing.ts
35
35
  /**
36
- * Model-name prefixes for Claude families that accept image input
37
- * (vision) on the Messages API.
36
+ * Anthropic's prompt-cache write multipliers, relative to the base input
37
+ * rate: a 5-minute write costs 1.25x, a 1-hour write 2x. They hold for
38
+ * every Claude model on Anthropic's price list.
38
39
  *
39
- * Every Claude 3, Claude 3.5/3.7, Claude 4 and Claude 5 family model is
40
- * multimodal, so the list covers both the dotted legacy naming
41
- * (`claude-3-haiku-...`, `claude-3-5-sonnet-...`) and the current
42
- * `claude-<tier>-4-*` naming (`claude-opus-4-7`, `claude-sonnet-4-6`,
43
- * `claude-haiku-4-5`). Pre-3 families (`claude-2`, `claude-instant`)
44
- * are text-only and intentionally absent.
40
+ * Source: https://platform.claude.com/docs/en/about-claude/pricing
41
+ * (fetched 2026-10-17).
42
+ */
43
+ const ANTHROPIC_CACHE_WRITE_MULTIPLIER = 1.25;
44
+ const ANTHROPIC_CACHE_WRITE_1H_MULTIPLIER = 2;
45
+ /**
46
+ * Built-in Claude API list prices, USD per 1,000,000 tokens, keyed by
47
+ * model id. `cachedInput` is the cache-hit rate, `cacheWrite` the
48
+ * 5-minute write rate and `cacheWrite1h` the 1-hour write rate.
45
49
  *
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.
50
+ * Source: https://platform.claude.com/docs/en/about-claude/pricing,
51
+ * "Model pricing" table, fetched 2026-10-17. Standard (global) routing,
52
+ * non-batch. Not covered, so pass `pricing` yourself when you need them:
54
53
  *
55
- * Matched as a prefix so dated variants (`claude-opus-4-20250514`) are
56
- * covered without listing every release tag. Devs can always override
57
- * per-model via `anthropic.model({ name, vision: true | false })` —
58
- * explicit config wins over inference in either direction.
54
+ * - Claude Haiku 5.5 — priced by prompt length (higher rates above
55
+ * 100,000 prompt tokens), which a flat `ModelPricing` cannot express.
56
+ * - US-only inference (`inference_geo: "us"`, 1.1x), fast mode, the Batch
57
+ * API, and Claude on Bedrock / Vertex AI (partner-priced).
58
+ * - Limited-availability models (Mythos) and retired models.
59
+ *
60
+ * Dated snapshot ids (`claude-haiku-4-5-20251001`) resolve to their alias
61
+ * entry through {@link anthropicPricingFor}.
59
62
  */
60
- const VISION_CAPABLE_PREFIXES = [
61
- "claude-3",
62
- "claude-4",
63
- "claude-opus-4",
64
- "claude-sonnet-4",
65
- "claude-haiku-4",
66
- "claude-opus-5",
67
- "claude-sonnet-5",
68
- "claude-fable-5",
69
- "claude-mythos-5"
70
- ];
63
+ const ANTHROPIC_MODEL_PRICING = {
64
+ "claude-fable-5-1": {
65
+ input: 10,
66
+ output: 50,
67
+ cachedInput: .25,
68
+ cacheWrite: 12.5,
69
+ cacheWrite1h: 20
70
+ },
71
+ "claude-fable-5": {
72
+ input: 10,
73
+ output: 50,
74
+ cachedInput: 1,
75
+ cacheWrite: 12.5,
76
+ cacheWrite1h: 20
77
+ },
78
+ "claude-opus-5-5": {
79
+ input: 4,
80
+ output: 20,
81
+ cachedInput: .2,
82
+ cacheWrite: 5,
83
+ cacheWrite1h: 8
84
+ },
85
+ "claude-opus-5": {
86
+ input: 5,
87
+ output: 25,
88
+ cachedInput: .5,
89
+ cacheWrite: 6.25,
90
+ cacheWrite1h: 10
91
+ },
92
+ "claude-opus-4-8": {
93
+ input: 5,
94
+ output: 25,
95
+ cachedInput: .5,
96
+ cacheWrite: 6.25,
97
+ cacheWrite1h: 10
98
+ },
99
+ "claude-opus-4-7": {
100
+ input: 5,
101
+ output: 25,
102
+ cachedInput: .5,
103
+ cacheWrite: 6.25,
104
+ cacheWrite1h: 10
105
+ },
106
+ "claude-opus-4-6": {
107
+ input: 5,
108
+ output: 25,
109
+ cachedInput: .5,
110
+ cacheWrite: 6.25,
111
+ cacheWrite1h: 10
112
+ },
113
+ "claude-opus-4-5": {
114
+ input: 5,
115
+ output: 25,
116
+ cachedInput: .5,
117
+ cacheWrite: 6.25,
118
+ cacheWrite1h: 10
119
+ },
120
+ "claude-sonnet-5-5": {
121
+ input: 2,
122
+ output: 10,
123
+ cachedInput: .1,
124
+ cacheWrite: 2.5,
125
+ cacheWrite1h: 4
126
+ },
127
+ "claude-sonnet-5": {
128
+ input: 2,
129
+ output: 10,
130
+ cachedInput: .2,
131
+ cacheWrite: 2.5,
132
+ cacheWrite1h: 4
133
+ },
134
+ "claude-sonnet-4-6": {
135
+ input: 3,
136
+ output: 15,
137
+ cachedInput: .3,
138
+ cacheWrite: 3.75,
139
+ cacheWrite1h: 6
140
+ },
141
+ "claude-sonnet-4-5": {
142
+ input: 3,
143
+ output: 15,
144
+ cachedInput: .3,
145
+ cacheWrite: 3.75,
146
+ cacheWrite1h: 6
147
+ },
148
+ "claude-haiku-4-5": {
149
+ input: 1,
150
+ output: 5,
151
+ cachedInput: .1,
152
+ cacheWrite: 1.25,
153
+ cacheWrite1h: 2
154
+ }
155
+ };
156
+ /** A trailing `-YYYYMMDD` snapshot date on a Claude model id. */
157
+ const SNAPSHOT_SUFFIX = /-\d{8}$/;
71
158
  /**
72
- * Infer whether a given Claude model name supports vision based on the
73
- * known-prefix list. Unknown models default to `false` so that passing
74
- * an image attachment to an unsupported model surfaces a clear,
75
- * agent-side capability error instead of an opaque Anthropic 400.
159
+ * Look up the built-in list price for a Claude model id. Accepts the
160
+ * alias (`claude-sonnet-4-5`) or a dated snapshot
161
+ * (`claude-sonnet-4-5-20250929`). Returns `undefined` for any id not in
162
+ * {@link ANTHROPIC_MODEL_PRICING}, so cost stays unknown rather than wrong.
76
163
  *
77
164
  * @example
78
- * inferVisionCapability("claude-sonnet-4-6"); // → true
79
- * inferVisionCapability("claude-3-5-sonnet-latest"); // → true
80
- * inferVisionCapability("claude-2.1"); // → false
81
- * inferVisionCapability("custom-proxy-llm"); // → false
165
+ * anthropicPricingFor("claude-haiku-4-5-20251001"); // { input: 1, output: 5, … }
166
+ * anthropicPricingFor("claude-haiku-5-5"); // undefined (prompt-length tiers)
82
167
  */
83
- function inferVisionCapability(modelName) {
84
- const normalized = modelName.toLowerCase();
85
- return VISION_CAPABLE_PREFIXES.some((prefix) => normalized.startsWith(prefix));
168
+ function anthropicPricingFor(modelName) {
169
+ const exact = ANTHROPIC_MODEL_PRICING[modelName];
170
+ if (exact) return exact;
171
+ return ANTHROPIC_MODEL_PRICING[modelName.replace(SNAPSHOT_SUFFIX, "")];
172
+ }
173
+ /**
174
+ * Fill the cache-write rates Anthropic derives from the input rate
175
+ * (5-minute writes 1.25x, 1-hour writes 2x) into a pricing entry that
176
+ * omits them, so a caller who supplies only `{ input, output }` is no
177
+ * longer billed nothing for cache writes. Explicit rates always win; the
178
+ * cache-read rate is left alone because it differs per model.
179
+ *
180
+ * @example
181
+ * withAnthropicCacheWriteRates({ input: 3, output: 15 });
182
+ * // { input: 3, output: 15, cacheWrite: 3.75, cacheWrite1h: 6 }
183
+ */
184
+ function withAnthropicCacheWriteRates(pricing) {
185
+ if (!pricing) return;
186
+ const cacheWrite = pricing.cacheWrite ?? pricing.cachedOutput ?? pricing.input * 1.25;
187
+ const cacheWrite1h = pricing.cacheWrite1h ?? pricing.input * 2;
188
+ if (pricing.cacheWrite === cacheWrite && pricing.cacheWrite1h === cacheWrite1h) return pricing;
189
+ return {
190
+ ...pricing,
191
+ cacheWrite,
192
+ cacheWrite1h
193
+ };
86
194
  }
87
195
 
88
196
  //#endregion
@@ -121,6 +229,11 @@ const BUDGET_RELEASE_ROOTS = [
121
229
  * Dates (`-20250514`) and tags (`-latest`, `-v1`) are not minor versions.
122
230
  */
123
231
  const NEXT_MINOR_VERSION = /^-\d{1,2}(?!\d)/;
232
+ /**
233
+ * Whether a lower-cased model id is the release `root` itself, or a dated /
234
+ * tagged / `@` snapshot of it — but not a later minor version of the same
235
+ * line (`claude-opus-4` matches `claude-opus-4-20250514`, not `claude-opus-4-7`).
236
+ */
124
237
  function matchesReleaseRoot(name, root) {
125
238
  if (!name.startsWith(root)) return false;
126
239
  const rest = name.slice(root.length);
@@ -185,9 +298,261 @@ const BETWEEN_TOOLS_RELEASE_ROOTS = ["claude-sonnet-5-5"];
185
298
  * acceptsBetweenTools("claude-opus-5-5"); // → false
186
299
  */
187
300
  function acceptsBetweenTools(modelName) {
301
+ return matchesAnyRelease(modelName, BETWEEN_TOOLS_RELEASE_ROOTS);
302
+ }
303
+ /**
304
+ * Whether the model id — lower-cased, with any platform prefix
305
+ * (`anthropic.`, `us.anthropic.` on Bedrock) removed — is one of these
306
+ * releases, dated or tagged, but not a later minor release.
307
+ */
308
+ function matchesAnyRelease(modelName, roots) {
188
309
  const normalized = modelName.toLowerCase();
189
310
  const id = normalized.slice(normalized.lastIndexOf(".") + 1);
190
- return BETWEEN_TOOLS_RELEASE_ROOTS.some((root) => matchesReleaseRoot(id, root));
311
+ return roots.some((root) => matchesReleaseRoot(id, root));
312
+ }
313
+ /**
314
+ * Releases that accept `thinking: { type: "disabled" }` and run reliably
315
+ * with it. Source: claude-api ref, "Thinking & Effort" per-model table
316
+ * (cached 2026-10-06): Claude Sonnet 5 accepts `disabled`; Claude Haiku 5.5
317
+ * accepts it at effort `low`, `medium` and `high` (a 400 at `xhigh` /
318
+ * `max`). No effort is sent alongside it, so Haiku 5.5 runs at its default,
319
+ * `medium`, which is inside the accepted range.
320
+ */
321
+ const DISABLED_THINKING_RELEASE_ROOTS = ["claude-sonnet-5", "claude-haiku-5-5"];
322
+ /**
323
+ * Releases that accept `disabled` but are kept on adaptive thinking at the
324
+ * lowest effort instead. Claude Opus 5 accepts `disabled` at effort `high`
325
+ * or below, but the claude-api ref ("Two failure modes when thinking is
326
+ * disabled", cached 2026-10-06) documents that with thinking disabled it
327
+ * occasionally writes a tool call into its visible text instead of a
328
+ * `tool_use` block — the call never runs and no error is raised — and can
329
+ * leak `<thinking>` tags into the response. The ref's primary
330
+ * recommendation is to keep thinking on and lower `effort`, so the adapter
331
+ * does exactly that.
332
+ */
333
+ const LOWEST_EFFORT_RELEASE_ROOTS = ["claude-opus-5"];
334
+ /**
335
+ * The wire form for `reasoning: { effort: "none" }` on this model:
336
+ *
337
+ * | Model | Form |
338
+ * |----------------------------------------|-------------------|
339
+ * | Claude Sonnet 5.5 | `between_tools` |
340
+ * | Claude Sonnet 5, Claude Haiku 5.5 | `disabled` |
341
+ * | Claude Opus 5 | `lowest-effort` |
342
+ * | Opus 5.5, Fable 5/5.1, everything else | `omit` |
343
+ *
344
+ * Opus 5.5 and Fable 5/5.1 have no thinking-off form (`disabled` and
345
+ * `budget_tokens` are a 400 at every effort), so nothing is sent and the
346
+ * model runs at its default effort; older models run without thinking when
347
+ * the field is absent.
348
+ *
349
+ * @example
350
+ * noReasoningForm("claude-sonnet-5"); // → "disabled"
351
+ * noReasoningForm("claude-opus-5"); // → "lowest-effort"
352
+ * noReasoningForm("claude-opus-5-5"); // → "omit"
353
+ */
354
+ function noReasoningForm(modelName) {
355
+ if (acceptsBetweenTools(modelName)) return "between_tools";
356
+ if (matchesAnyRelease(modelName, DISABLED_THINKING_RELEASE_ROOTS)) return "disabled";
357
+ if (matchesAnyRelease(modelName, LOWEST_EFFORT_RELEASE_ROOTS)) return "lowest-effort";
358
+ return "omit";
359
+ }
360
+ const PREFIX_CHECK_RELEASE_ROOTS = [
361
+ "claude-fable-5-1",
362
+ "claude-opus-5-5",
363
+ "claude-sonnet-5-5",
364
+ "claude-haiku-5-5"
365
+ ];
366
+ /** Releases documented not to run the conversation check. */
367
+ const NO_PREFIX_CHECK_RELEASE_ROOTS = [
368
+ "claude-mythos-5-1",
369
+ "claude-mythos-5",
370
+ "claude-mythos-preview",
371
+ "claude-fable-5",
372
+ "claude-opus-5",
373
+ "claude-sonnet-5"
374
+ ];
375
+ /** Families released before Claude Fable 5.1 in their entirety. */
376
+ const NO_PREFIX_CHECK_FAMILY_PREFIXES = [
377
+ ...BUDGET_FAMILY_PREFIXES,
378
+ "claude-opus-4",
379
+ "claude-sonnet-4"
380
+ ];
381
+ /**
382
+ * Whether this model binds a replayed thinking block to the exact
383
+ * conversation before it (preserved thinking). Unknown and future ids are
384
+ * assumed to run the check — the safe direction: reasoning a kept turn
385
+ * can't use is left out instead of risking a 400. Matches the id with or
386
+ * without a platform prefix and with dated or tagged suffixes.
387
+ *
388
+ * @example
389
+ * inferPreservedThinking("claude-opus-5-5"); // → true
390
+ * inferPreservedThinking("claude-opus-5"); // → false
391
+ * inferPreservedThinking("claude-opus-4-8"); // → false
392
+ */
393
+ function inferPreservedThinking(modelName) {
394
+ const normalized = modelName.toLowerCase();
395
+ const id = normalized.slice(normalized.lastIndexOf(".") + 1);
396
+ if (PREFIX_CHECK_RELEASE_ROOTS.some((root) => matchesReleaseRoot(id, root))) return true;
397
+ if (NO_PREFIX_CHECK_RELEASE_ROOTS.some((root) => matchesReleaseRoot(id, root)) || NO_PREFIX_CHECK_FAMILY_PREFIXES.some((prefix) => id.startsWith(prefix))) return false;
398
+ return true;
399
+ }
400
+
401
+ //#endregion
402
+ //#region ../../packages/ai-anthropic/src/known-output-limits.ts
403
+ /** Family prefixes whose every release has a 128K output limit. */
404
+ const FAMILY_128K_PREFIXES = [
405
+ "claude-fable-5",
406
+ "claude-mythos-5",
407
+ "claude-opus-5",
408
+ "claude-sonnet-5",
409
+ "claude-haiku-5"
410
+ ];
411
+ /** Individual releases, matched as a release root (dated / `@` variants included). */
412
+ const RELEASE_OUTPUT_LIMITS = [
413
+ ["claude-opus-4-8", 128e3],
414
+ ["claude-opus-4-7", 128e3],
415
+ ["claude-opus-4-6", 128e3],
416
+ ["claude-sonnet-4-6", 128e3],
417
+ ["claude-opus-4-5", 64e3],
418
+ ["claude-sonnet-4-5", 64e3],
419
+ ["claude-haiku-4-5", 64e3]
420
+ ];
421
+ /**
422
+ * The documented maximum output tokens of a Claude model, or `undefined`
423
+ * when the id is not a known Claude model. Platform prefixes before
424
+ * `claude-` (`anthropic.`, `us.anthropic.`) and dated or `@` snapshot
425
+ * suffixes are accepted, so the Bedrock / Google Cloud ids of a known
426
+ * model resolve too. Override per model with
427
+ * `anthropic.model({ name, maxOutputTokens })`.
428
+ *
429
+ * @example
430
+ * inferMaxOutputTokens("claude-opus-5-5"); // → 128000
431
+ * inferMaxOutputTokens("claude-haiku-4-5-20251001"); // → 64000
432
+ * inferMaxOutputTokens("my-gateway/model"); // → undefined
433
+ */
434
+ function inferMaxOutputTokens(modelName) {
435
+ const lowered = modelName.toLowerCase();
436
+ const start = lowered.indexOf("claude-");
437
+ if (start === -1) return;
438
+ const normalized = lowered.slice(start);
439
+ if (FAMILY_128K_PREFIXES.some((prefix) => normalized.startsWith(prefix))) return 128e3;
440
+ for (const [root, limit] of RELEASE_OUTPUT_LIMITS) if (matchesReleaseRoot(normalized, root)) return limit;
441
+ }
442
+
443
+ //#endregion
444
+ //#region ../../packages/ai-anthropic/src/known-vision-models.ts
445
+ /**
446
+ * Model-name prefixes for Claude families that accept image input
447
+ * (vision) on the Messages API.
448
+ *
449
+ * Every Claude 3, Claude 3.5/3.7, Claude 4 and Claude 5 family model is
450
+ * multimodal, so the list covers both the dotted legacy naming
451
+ * (`claude-3-haiku-...`, `claude-3-5-sonnet-...`) and the current
452
+ * `claude-<tier>-4-*` naming (`claude-opus-4-7`, `claude-sonnet-4-6`,
453
+ * `claude-haiku-4-5`). Pre-3 families (`claude-2`, `claude-instant`)
454
+ * are text-only and intentionally absent.
455
+ *
456
+ * The Claude 5 series is multimodal too (claude-api ref, cached
457
+ * 2026-09-25): its vision examples use `claude-opus-5-5`; the models list
458
+ * credits Claude Fable 5.1 with vision and Claude Sonnet 5 with
459
+ * high-resolution vision; Claude Mythos 5.1 has "same capabilities" as
460
+ * Fable 5.1. The tier-5 prefixes cover every 5.x release
461
+ * (`claude-opus-5`, `claude-opus-5-5`, `claude-sonnet-5-5`,
462
+ * `claude-fable-5-1`, …). Opus/Sonnet 4.6–4.8 are already covered by the
463
+ * tier-4 prefixes.
464
+ *
465
+ * Matched as a prefix so dated variants (`claude-opus-4-20250514`) are
466
+ * covered without listing every release tag. Devs can always override
467
+ * per-model via `anthropic.model({ name, vision: true | false })` —
468
+ * explicit config wins over inference in either direction.
469
+ */
470
+ const VISION_CAPABLE_PREFIXES = [
471
+ "claude-3",
472
+ "claude-4",
473
+ "claude-opus-4",
474
+ "claude-sonnet-4",
475
+ "claude-haiku-4",
476
+ "claude-opus-5",
477
+ "claude-sonnet-5",
478
+ "claude-fable-5",
479
+ "claude-mythos-5"
480
+ ];
481
+ /**
482
+ * Infer whether a given Claude model name supports vision based on the
483
+ * known-prefix list. Unknown models default to `false` so that passing
484
+ * an image attachment to an unsupported model surfaces a clear,
485
+ * agent-side capability error instead of an opaque Anthropic 400.
486
+ *
487
+ * @example
488
+ * inferVisionCapability("claude-sonnet-4-6"); // → true
489
+ * inferVisionCapability("claude-3-5-sonnet-latest"); // → true
490
+ * inferVisionCapability("claude-2.1"); // → false
491
+ * inferVisionCapability("custom-proxy-llm"); // → false
492
+ */
493
+ function inferVisionCapability(modelName) {
494
+ const normalized = modelName.toLowerCase();
495
+ return VISION_CAPABLE_PREFIXES.some((prefix) => normalized.startsWith(prefix));
496
+ }
497
+
498
+ //#endregion
499
+ //#region ../../packages/ai-anthropic/src/utils/cache-usage.ts
500
+ /**
501
+ * Copy Anthropic's cache counters onto a neutral `Usage`.
502
+ *
503
+ * - `cache_read_input_tokens` → `cachedTokens`
504
+ * - `cache_creation_input_tokens` → `cacheWriteTokens`
505
+ * - `cache_creation.ephemeral_1h_input_tokens` → `cacheWrite1hTokens`
506
+ * (the 1-hour writes, billed at 2x input instead of 1.25x)
507
+ *
508
+ * When Anthropic omits the per-lifetime breakdown, `requestTtl` decides:
509
+ * a request whose every cache marker asked for `"1h"` wrote only 1-hour
510
+ * entries. Counters are set only when positive, so an absent channel
511
+ * stays undefined. Anthropic's `input_tokens` excludes all of these, so
512
+ * the caller marks the usage `inputIncludesCache: false`.
513
+ */
514
+ function applyAnthropicCacheUsage(usage, raw, requestTtl) {
515
+ const cacheRead = raw.cache_read_input_tokens;
516
+ if (typeof cacheRead === "number" && cacheRead > 0) usage.cachedTokens = cacheRead;
517
+ const cacheWrite = raw.cache_creation_input_tokens;
518
+ if (typeof cacheWrite !== "number" || cacheWrite <= 0) return;
519
+ usage.cacheWriteTokens = cacheWrite;
520
+ const reported1h = raw.cache_creation?.ephemeral_1h_input_tokens;
521
+ const writes1h = typeof reported1h === "number" ? reported1h : requestTtl === "1h" ? cacheWrite : 0;
522
+ if (writes1h > 0) usage.cacheWrite1hTokens = Math.min(writes1h, cacheWrite);
523
+ }
524
+ /**
525
+ * Scan a request body (top level, system, tools, messages) for
526
+ * `cache_control` markers and report the lifetime they request. Used only
527
+ * as a fallback when the response lacks Anthropic's per-lifetime
528
+ * cache-write breakdown.
529
+ *
530
+ * @example
531
+ * requestCacheTtl({ cache_control: { type: "ephemeral", ttl: "1h" } }); // "1h"
532
+ */
533
+ function requestCacheTtl(body) {
534
+ let found;
535
+ const visit = (value, depth) => {
536
+ if (depth > 8 || typeof value !== "object" || value === null) return;
537
+ if (Array.isArray(value)) {
538
+ for (const item of value) visit(item, depth + 1);
539
+ return;
540
+ }
541
+ const record = value;
542
+ const marker = record.cache_control;
543
+ if (typeof marker === "object" && marker !== null) {
544
+ const ttl = marker.ttl === "1h" ? "1h" : "5m";
545
+ found = found === void 0 || found === ttl ? ttl : "mixed";
546
+ }
547
+ for (const key of [
548
+ "system",
549
+ "tools",
550
+ "messages",
551
+ "content"
552
+ ]) if (key in record) visit(record[key], depth + 1);
553
+ };
554
+ visit(body, 0);
555
+ return found;
191
556
  }
192
557
 
193
558
  //#endregion
@@ -333,6 +698,9 @@ function isReplayable(block) {
333
698
  * provider, or blocks whose `tool_use` ids no longer match `toolCalls`,
334
699
  * are ignored and the turn is rebuilt from `content` + `toolCalls`.
335
700
  *
701
+ * With `dropRetainedThinking`, a turn marked `compaction: "retained"` is
702
+ * replayed without its thinking blocks (see {@link ToAnthropicMessagesOptions}).
703
+ *
336
704
  * Other consecutive same-role turns are left as-is — the Messages API
337
705
  * merges them server-side.
338
706
  *
@@ -344,7 +712,7 @@ function isReplayable(block) {
344
712
  * // system === "Be concise."
345
713
  * // messages === [{ role: "user", content: "Hi" }]
346
714
  */
347
- function toAnthropicMessages(messages) {
715
+ function toAnthropicMessages(messages, options = {}) {
348
716
  const systemParts = [];
349
717
  const mapped = [];
350
718
  for (const message of messages) {
@@ -366,18 +734,20 @@ function toAnthropicMessages(messages) {
366
734
  });
367
735
  continue;
368
736
  }
737
+ const dropThinking = options.dropRetainedThinking === true && message.compaction === "retained";
369
738
  if (message.role === "assistant") {
370
739
  const replayed = fromProviderBlocks(message.blocks, message.toolCalls);
371
- if (replayed) {
740
+ const content = dropThinking ? replayed?.filter((block) => !isThinkingBlock(block)) : replayed;
741
+ if (content && content.length > 0) {
372
742
  mapped.push({
373
743
  role: "assistant",
374
- content: replayed
744
+ content
375
745
  });
376
746
  continue;
377
747
  }
378
748
  }
379
749
  if (message.role === "assistant" && message.toolCalls && message.toolCalls.length > 0) {
380
- const blocks = extractThinkingBlocks(message.toolCalls);
750
+ const blocks = dropThinking ? [] : extractThinkingBlocks(message.toolCalls);
381
751
  const text = stringifyContent(message.content);
382
752
  if (text) blocks.push({
383
753
  type: "text",
@@ -412,6 +782,9 @@ function toAnthropicMessages(messages) {
412
782
  messages: mapped
413
783
  };
414
784
  }
785
+ function isThinkingBlock(block) {
786
+ return block.type === "thinking" || block.type === "redacted_thinking";
787
+ }
415
788
  /**
416
789
  * Recover signed extended-thinking blocks stored by this adapter on a tool
417
790
  * request. Anthropic requires these blocks to be replayed before the
@@ -583,6 +956,11 @@ function wrapAnthropicError(thrown) {
583
956
  context,
584
957
  retryAfter: parseRetryAfter(shape.headers)
585
958
  });
959
+ if (shape.type === "overloaded_error" || shape.status === 529) return new _warlock_js_ai.ProviderOverloadedError(message, {
960
+ cause: thrown,
961
+ context,
962
+ retryAfter: parseRetryAfter(shape.headers)
963
+ });
586
964
  if (shape.type === "invalid_request_error" || isClientStatus(shape.status)) {
587
965
  if (/prompt is too long/i.test(message)) return new _warlock_js_ai.ContextLengthExceededError(message, {
588
966
  cause: thrown,
@@ -688,14 +1066,30 @@ function parseRetryAfter(headers) {
688
1066
  //#endregion
689
1067
  //#region ../../packages/ai-anthropic/src/model.ts
690
1068
  const LOG_MODULE = "ai.anthropic";
1069
+ /** `provider` value of `ai.providerTool()` entries this adapter sends verbatim. */
1070
+ const ANTHROPIC_WIRE_FORMAT = "anthropic";
691
1071
  /**
692
1072
  * Anthropic requires `max_tokens` on every request (unlike OpenAI,
693
- * where it is optional). When neither the per-call option nor the
694
- * model config supplies one, fall back to a generous default so a
695
- * caller who never thought about token caps still gets a complete
696
- * answer instead of a 400.
1073
+ * where it is optional). When neither the per-call option nor the model
1074
+ * config supplies one, the adapter picks a default large enough for agent
1075
+ * work (see `resolveDefaultMaxTokens`):
1076
+ *
1077
+ * - streaming: the model's documented max output (`known-output-limits.ts`,
1078
+ * or `config.maxOutputTokens`), else this value;
1079
+ * - non-streaming: this value, capped by the model's max output. The
1080
+ * installed `@anthropic-ai/sdk` (0.129.0, `calculateNonstreamingTimeout`)
1081
+ * refuses a non-streaming request whose `max_tokens` implies more than
1082
+ * ten minutes (above ~21,333 tokens) unless the client sets a `timeout`,
1083
+ * so the non-streaming default stays below that.
697
1084
  */
698
- const DEFAULT_MAX_TOKENS = 4096;
1085
+ const DEFAULT_MAX_TOKENS = 16384;
1086
+ /**
1087
+ * Answer room kept on top of a thinking budget when the default
1088
+ * `max_tokens` is sized: thinking counts toward `max_tokens` (installed
1089
+ * SDK `ThinkingConfigParam` docs, `messages.d.ts:1105`), and in budget
1090
+ * mode `max_tokens` must exceed `budget_tokens`.
1091
+ */
1092
+ const MIN_ANSWER_TOKENS = 4096;
699
1093
  /**
700
1094
  * Anthropic rejects an extended-thinking budget below 1024 tokens, so
701
1095
  * any resolved budget is floored at this minimum before it reaches the
@@ -714,11 +1108,10 @@ const DEFAULT_THINKING_EFFORT = "medium";
714
1108
  *
715
1109
  * - Budget-mode models: sent as `thinking.budget_tokens` when the caller
716
1110
  * doesn't pass an explicit `reasoning.maxTokens`.
717
- * - Both modes: reserved on top of `DEFAULT_MAX_TOKENS` when the caller
718
- * sets no `maxTokens`, because thinking counts toward `max_tokens`
719
- * (installed SDK `ThinkingConfigParam` docs, `messages.d.ts:1105`).
720
- * Adaptive models therefore get the same `max_tokens` the budget path
721
- * has always produced.
1111
+ * - Both modes: when the caller sets no `maxTokens`, the default
1112
+ * `max_tokens` keeps at least this budget plus `MIN_ANSWER_TOKENS`,
1113
+ * because thinking counts toward `max_tokens` (installed SDK
1114
+ * `ThinkingConfigParam` docs, `messages.d.ts:1105`).
722
1115
  */
723
1116
  const EFFORT_THINKING_BUDGET = {
724
1117
  minimal: 1024,
@@ -794,8 +1187,10 @@ var AnthropicModel = class {
794
1187
  this.config = config;
795
1188
  this.name = config.name;
796
1189
  this.provider = provider;
797
- this.pricing = config.pricing;
1190
+ this.pricing = withAnthropicCacheWriteRates(config.pricing ?? anthropicPricingFor(config.name));
798
1191
  this.thinkingMode = config.thinkingMode ?? inferThinkingMode(config.name);
1192
+ this.maxOutputTokens = config.maxOutputTokens ?? inferMaxOutputTokens(config.name);
1193
+ this.preservedThinking = config.preservedThinking ?? (this.thinkingMode === "adaptive" && inferPreservedThinking(config.name));
799
1194
  this.capabilities = {
800
1195
  structuredOutput: config.structuredOutput ?? true,
801
1196
  vision: config.vision ?? inferVisionCapability(config.name),
@@ -811,23 +1206,50 @@ var AnthropicModel = class {
811
1206
  * instance's `ModelConfig` defaults for this call only.
812
1207
  */
813
1208
  async complete(messages, options) {
814
- this.logger.debug(LOG_MODULE, "request", "Starting call to messages.create", {
815
- model: this.name,
816
- messageCount: messages.length,
817
- streaming: false,
818
- toolCount: options?.tools?.length ?? 0
1209
+ return this.send(this.buildLoggedRequest(messages, {
1210
+ ...options,
1211
+ stream: false
1212
+ }), { signal: options?.signal });
1213
+ }
1214
+ buildRequest(messages, options) {
1215
+ const built = this.buildBody(messages, options);
1216
+ const betas = [...new Set([...resolveAnthropicOptions(options).betas ?? [], ...built.betas])];
1217
+ const body = structuredClone({
1218
+ ...built.body,
1219
+ stream: options?.stream ?? false
819
1220
  });
1221
+ return {
1222
+ endpoint: betas.length > 0 ? "beta.messages" : "messages",
1223
+ body,
1224
+ betas
1225
+ };
1226
+ }
1227
+ buildLoggedRequest(messages, options) {
1228
+ try {
1229
+ return this.buildRequest(messages, options);
1230
+ } catch (thrown) {
1231
+ throw this.logAndWrap(thrown instanceof _warlock_js_ai.AIError ? thrown : new _warlock_js_ai.InvalidRequestError(`Could not build the Anthropic request: ${thrown instanceof Error ? thrown.message : String(thrown)}`, { cause: thrown }));
1232
+ }
1233
+ }
1234
+ send(request, options) {
1235
+ const message = request.body.stream ? "Starting streaming call to messages.create" : "Starting call to messages.create";
1236
+ this.logger.debug(LOG_MODULE, "request", message, {
1237
+ model: request.body.model,
1238
+ messageCount: Array.isArray(request.body.messages) ? request.body.messages.length : 0,
1239
+ streaming: request.body.stream,
1240
+ toolCount: Array.isArray(request.body.tools) ? request.body.tools.length : 0
1241
+ });
1242
+ return request.body.stream ? this.streamRequest(request, options) : this.completeRequest(request, options);
1243
+ }
1244
+ async completeRequest(request, options) {
820
1245
  let response;
821
1246
  try {
822
- response = await this.send({
823
- ...this.buildBody(messages, options),
824
- stream: false
825
- }, options, "create");
1247
+ response = await this.dispatch(request, options, "create");
826
1248
  } catch (thrown) {
827
1249
  throw this.logAndWrap(thrown);
828
1250
  }
829
1251
  const finishReason = mapStopReason(response.stop_reason);
830
- const usage = this.extractUsage(response.usage);
1252
+ const usage = this.extractUsage(response.usage, requestCacheTtl(request.body));
831
1253
  const toolCalls = this.extractToolCalls(response.content);
832
1254
  const blocks = toProviderBlocks(response.content);
833
1255
  this.logger.debug(LOG_MODULE, "response", "call to messages.create succeeded", {
@@ -854,11 +1276,18 @@ var AnthropicModel = class {
854
1276
  * const tokens = await model.countTokens(messages, { tools });
855
1277
  */
856
1278
  async countTokens(messages, options) {
857
- const body = this.buildBody(messages, options);
1279
+ const request = this.buildLoggedRequest(messages, {
1280
+ ...options,
1281
+ stream: options?.stream === true
1282
+ });
1283
+ const body = request.body;
858
1284
  const countBody = {};
859
1285
  for (const key of COUNT_TOKENS_FIELDS) if (body[key] !== void 0) countBody[key] = body[key];
860
1286
  try {
861
- return (await this.send(countBody, options, "countTokens")).input_tokens;
1287
+ return (await this.dispatch({
1288
+ ...request,
1289
+ body: countBody
1290
+ }, options, "countTokens")).input_tokens;
862
1291
  } catch (thrown) {
863
1292
  throw this.logAndWrap(thrown);
864
1293
  }
@@ -869,29 +1298,38 @@ var AnthropicModel = class {
869
1298
  * arguments have fully accumulated, and a terminal `done` carrying the
870
1299
  * final finish reason + usage totals. Callers consume it with
871
1300
  * `for await`.
1301
+ *
1302
+ * With `options.streamChunks` it also yields `reasoning` for every
1303
+ * non-empty `thinking_delta`, `tool-args` for every `input_json_delta`
1304
+ * of a client `tool_use` block, and `usage` after `message_start` and
1305
+ * each `message_delta` (whose counts are cumulative).
872
1306
  */
873
1307
  async *stream(messages, options) {
874
- this.logger.debug(LOG_MODULE, "request", "Starting streaming call to messages.create", {
875
- model: this.name,
876
- messageCount: messages.length,
877
- streaming: true,
878
- toolCount: options?.tools?.length ?? 0
1308
+ yield* this.send(this.buildLoggedRequest(messages, {
1309
+ ...options,
1310
+ stream: true
1311
+ }), {
1312
+ signal: options?.signal,
1313
+ streamChunks: options?.streamChunks
879
1314
  });
1315
+ }
1316
+ async *streamRequest(request, options) {
880
1317
  let stream;
881
1318
  try {
882
- stream = await this.send({
883
- ...this.buildBody(messages, options),
884
- stream: true
885
- }, options, "create");
1319
+ stream = await this.dispatch(request, options, "create");
886
1320
  } catch (thrown) {
887
1321
  throw this.logAndWrap(thrown);
888
1322
  }
889
1323
  let rawStopReason = null;
1324
+ let compaction = compactionIterationUsage(void 0);
890
1325
  const usage = {
891
1326
  input: 0,
892
1327
  output: 0,
893
- total: 0
1328
+ total: 0,
1329
+ inputIncludesCache: false
894
1330
  };
1331
+ const cacheTtl = requestCacheTtl(request.body);
1332
+ const want = (0, _warlock_js_ai.resolveStreamChunks)(options);
895
1333
  const toolBlocks = /* @__PURE__ */ new Map();
896
1334
  const thinkingBlocks = /* @__PURE__ */ new Map();
897
1335
  const streamedBlocks = /* @__PURE__ */ new Map();
@@ -902,10 +1340,14 @@ var AnthropicModel = class {
902
1340
  usage.input = event.message.usage.input_tokens ?? 0;
903
1341
  const thinkingTokens = event.message.usage.output_tokens_details?.thinking_tokens;
904
1342
  if (thinkingTokens !== null && thinkingTokens !== void 0) usage.reasoningTokens = thinkingTokens;
905
- const cacheRead = event.message.usage.cache_read_input_tokens;
906
- if (cacheRead !== null && cacheRead !== void 0 && cacheRead > 0) usage.cachedTokens = cacheRead;
907
- const cacheWrite = event.message.usage.cache_creation_input_tokens;
908
- if (cacheWrite !== null && cacheWrite !== void 0 && cacheWrite > 0) usage.cacheWriteTokens = cacheWrite;
1343
+ applyAnthropicCacheUsage(usage, event.message.usage, cacheTtl);
1344
+ if (want.usage) yield {
1345
+ type: "usage",
1346
+ usage: {
1347
+ ...usage,
1348
+ total: usage.input + usage.output
1349
+ }
1350
+ };
909
1351
  continue;
910
1352
  }
911
1353
  if (event.type === "content_block_start") {
@@ -926,10 +1368,22 @@ var AnthropicModel = class {
926
1368
  };
927
1369
  else if (event.delta.type === "input_json_delta") {
928
1370
  const accumulator = toolBlocks.get(event.index);
929
- if (accumulator) accumulator.json += event.delta.partial_json;
1371
+ if (accumulator) {
1372
+ accumulator.json += event.delta.partial_json;
1373
+ if (want.toolArgs && event.delta.partial_json !== "") yield {
1374
+ type: "tool-args",
1375
+ callId: accumulator.id,
1376
+ name: accumulator.name,
1377
+ delta: event.delta.partial_json
1378
+ };
1379
+ }
930
1380
  } else if (event.delta.type === "thinking_delta") {
931
1381
  const thinking = thinkingBlocks.get(event.index);
932
1382
  if (thinking?.type === "thinking") thinking.thinking += event.delta.thinking;
1383
+ if (want.reasoning && event.delta.thinking !== "") yield {
1384
+ type: "reasoning",
1385
+ delta: event.delta.thinking
1386
+ };
933
1387
  } else if (event.delta.type === "signature_delta") {
934
1388
  const thinking = thinkingBlocks.get(event.index);
935
1389
  if (thinking?.type === "thinking") thinking.signature += event.delta.signature;
@@ -952,19 +1406,33 @@ var AnthropicModel = class {
952
1406
  }
953
1407
  if (event.type === "message_delta") {
954
1408
  rawStopReason = event.delta.stop_reason ?? rawStopReason;
1409
+ if (Array.isArray(event.usage.iterations)) compaction = compactionIterationUsage(event.usage);
955
1410
  usage.output = event.usage.output_tokens ?? usage.output;
956
1411
  const thinkingTokens = event.usage.output_tokens_details?.thinking_tokens;
957
1412
  if (thinkingTokens !== null && thinkingTokens !== void 0) usage.reasoningTokens = thinkingTokens;
958
- const cacheRead = event.usage.cache_read_input_tokens;
959
- if (cacheRead !== null && cacheRead !== void 0 && cacheRead > 0) usage.cachedTokens = cacheRead;
960
- const cacheWrite = event.usage.cache_creation_input_tokens;
961
- if (cacheWrite !== null && cacheWrite !== void 0 && cacheWrite > 0) usage.cacheWriteTokens = cacheWrite;
1413
+ applyAnthropicCacheUsage(usage, {
1414
+ cache_read_input_tokens: event.usage.cache_read_input_tokens,
1415
+ cache_creation_input_tokens: event.usage.cache_creation_input_tokens,
1416
+ cache_creation: usage.cacheWrite1hTokens !== void 0 ? { ephemeral_1h_input_tokens: usage.cacheWrite1hTokens } : void 0
1417
+ }, cacheTtl);
1418
+ if (want.usage) yield {
1419
+ type: "usage",
1420
+ usage: {
1421
+ ...usage,
1422
+ total: usage.input + usage.output
1423
+ }
1424
+ };
962
1425
  }
963
1426
  }
964
1427
  } catch (thrown) {
965
1428
  throw this.logAndWrap(thrown);
966
1429
  }
1430
+ usage.input += compaction.input;
1431
+ usage.output += compaction.output;
1432
+ addCacheCounts(usage, compaction);
967
1433
  usage.total = usage.input + usage.output;
1434
+ const cost = (0, _warlock_js_ai.computeCost)(usage, this.pricing);
1435
+ if (cost) usage.cost = cost;
968
1436
  const finishReason = mapStopReason(rawStopReason);
969
1437
  const blocks = toProviderBlocks(this.finishStreamedBlocks(streamedBlocks));
970
1438
  this.logger.debug(LOG_MODULE, "response", "Streaming call to messages.create succeeded", {
@@ -1018,38 +1486,74 @@ var AnthropicModel = class {
1018
1486
  * and the beta-only body fields); otherwise to `client.messages`. The
1019
1487
  * cancellation signal rides in the request options, as before.
1020
1488
  */
1021
- send(body, options, method) {
1022
- const betas = resolveAnthropicOptions(options).betas;
1489
+ dispatch(request, options, method) {
1023
1490
  const requestOptions = options?.signal ? { signal: options.signal } : void 0;
1024
- const api = betas && betas.length > 0 ? this.client.beta.messages : this.client.messages;
1025
- return api[method].call(api, betas && betas.length > 0 ? {
1026
- ...body,
1027
- betas
1028
- } : body, requestOptions);
1491
+ const api = request.endpoint === "beta.messages" ? this.client.beta.messages : this.client.messages;
1492
+ return api[method].call(api, request.endpoint === "beta.messages" ? {
1493
+ ...request.body,
1494
+ betas: request.betas
1495
+ } : request.body, requestOptions);
1029
1496
  }
1030
1497
  /**
1031
- * The full request body: the adapter-built params, then
1032
- * `thinking.display` when thinking is sent on an adaptive model, then
1033
- * `providerOptions.anthropic.body` merged last (caller fields win). The
1034
- * body may add fields the adapter does not build (`context_management`,
1035
- * `fallbacks`, …) but may not replace the conversation or the model —
1036
- * see {@link RESERVED_BODY_FIELDS}.
1498
+ * The full request body and the beta flags it needs: the adapter-built
1499
+ * params, then `thinking.display` when thinking is sent on an adaptive
1500
+ * model, the typed `thinkingBlockBinding`, `contextManagement` and
1501
+ * `serverCompaction` options, then `providerOptions.anthropic.body`
1502
+ * merged last (caller fields win). The body may add fields the adapter
1503
+ * does not build (`fallbacks`, …) but may not replace the conversation or
1504
+ * the model — see {@link RESERVED_BODY_FIELDS}.
1037
1505
  */
1038
1506
  buildBody(messages, options) {
1039
1507
  const anthropic = resolveAnthropicOptions(options);
1040
- const body = { ...this.buildParams(messages, options) };
1508
+ const betas = [];
1509
+ const body = { ...this.buildParams(messages, options, { dropRetainedThinking: this.preservedThinking && anthropic.thinkingBlockBinding === void 0 }) };
1041
1510
  if (anthropic.thinkingDisplay && this.thinkingMode === "adaptive" && body.thinking?.type === "adaptive") body.thinking = {
1042
1511
  ...body.thinking,
1043
1512
  display: anthropic.thinkingDisplay
1044
1513
  };
1514
+ if (anthropic.thinkingBlockBinding) this.applyBlockBinding(body, anthropic.thinkingBlockBinding, betas);
1515
+ const edits = buildContextEdits(anthropic);
1516
+ if (edits.length > 0) {
1517
+ if (anthropic.body?.context_management !== void 0) throw new _warlock_js_ai.InvalidRequestError("providerOptions.anthropic.contextManagement / serverCompaction cannot be combined with body.context_management: set the edits in one place.", { context: { fields: ["context_management"] } });
1518
+ body.context_management = { edits };
1519
+ if (edits.some((edit) => edit.type !== "compact_20260112")) betas.push(CONTEXT_MANAGEMENT_BETA);
1520
+ if (edits.some((edit) => edit.type === "compact_20260112")) betas.push(COMPACTION_BETA);
1521
+ }
1045
1522
  const reserved = Object.keys(anthropic.body ?? {}).filter((key) => RESERVED_BODY_FIELDS.has(key));
1046
1523
  if (reserved.length > 0) throw new _warlock_js_ai.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 } });
1047
1524
  return {
1048
- ...body,
1049
- ...anthropic.body
1525
+ body: {
1526
+ ...body,
1527
+ ...anthropic.body
1528
+ },
1529
+ betas
1050
1530
  };
1051
1531
  }
1052
1532
  /**
1533
+ * Set `thinking.block_binding.prefix_mismatch_behavior`. It rides on
1534
+ * adaptive or budget (`enabled`) thinking. A model that runs adaptive
1535
+ * thinking when `thinking` is omitted (every preserved-thinking model) gets
1536
+ * an explicit `{ type: "adaptive" }`, which is the same request. Any other
1537
+ * shape (`between_tools`, `disabled`) takes no extra field, so the option
1538
+ * is skipped with a debug log.
1539
+ */
1540
+ applyBlockBinding(body, behavior, betas) {
1541
+ const thinking = body.thinking;
1542
+ const type = thinking?.type ?? (this.preservedThinking ? "adaptive" : void 0);
1543
+ if (type !== "adaptive" && type !== "enabled") {
1544
+ this.logger.debug(LOG_MODULE, "thinking-block-binding.ignored", "thinkingBlockBinding needs adaptive or budget thinking on this request; not sent", {
1545
+ model: this.name,
1546
+ thinking: thinking?.type ?? "none"
1547
+ });
1548
+ return;
1549
+ }
1550
+ body.thinking = {
1551
+ ...thinking ?? { type },
1552
+ block_binding: { prefix_mismatch_behavior: behavior }
1553
+ };
1554
+ betas.push(THINKING_BINDING_BETA);
1555
+ }
1556
+ /**
1053
1557
  * Assemble the Anthropic request body shared by `complete()` and
1054
1558
  * `stream()` (each adds its own `stream` literal so the SDK's create
1055
1559
  * overload resolves to the right return type). Hoists the system
@@ -1061,13 +1565,13 @@ var AnthropicModel = class {
1061
1565
  * rejects the two together, and always for adaptive-mode models, which
1062
1566
  * reject any explicit temperature (see `acceptsTemperature`).
1063
1567
  */
1064
- buildParams(messages, options) {
1065
- const { system, messages: anthropicMessages } = toAnthropicMessages(messages);
1568
+ buildParams(messages, options, conversion) {
1569
+ const { system, messages: anthropicMessages } = toAnthropicMessages(messages, conversion);
1066
1570
  const thinking = this.buildThinking(options?.reasoning);
1067
1571
  const temperature = options?.temperature ?? this.config.temperature;
1068
1572
  const configuredMaxTokens = options?.maxTokens ?? this.config.maxTokens;
1069
1573
  const thinkingBudget = thinking.thinking?.type === "enabled" ? thinking.thinking.budget_tokens : void 0;
1070
- const maxTokens = configuredMaxTokens ?? (thinking.reservedTokens !== void 0 ? thinking.reservedTokens + DEFAULT_MAX_TOKENS : DEFAULT_MAX_TOKENS);
1574
+ const maxTokens = configuredMaxTokens ?? this.resolveDefaultMaxTokens(options?.stream === true, thinking.reservedTokens);
1071
1575
  const sendTemperature = temperature !== void 0 && !thinking.thinking && acceptsTemperature(this.thinkingMode);
1072
1576
  if (thinkingBudget !== void 0 && configuredMaxTokens !== void 0 && configuredMaxTokens <= thinkingBudget) throw new Error(`Anthropic maxTokens (${configuredMaxTokens}) must be greater than thinking budget_tokens (${thinkingBudget})`);
1073
1577
  return {
@@ -1077,12 +1581,31 @@ var AnthropicModel = class {
1077
1581
  ...this.buildAutomaticCacheControl(options?.cacheControl),
1078
1582
  ...this.buildSystem(system, options?.cacheControl),
1079
1583
  ...sendTemperature ? { temperature } : {},
1080
- ...this.buildTools(options?.tools, options?.cacheControl),
1584
+ ...this.buildTools(options, options?.cacheControl),
1081
1585
  ...this.buildOutputConfig(options?.responseSchema, thinking.effort),
1082
1586
  ...thinking.thinking ? { thinking: thinking.thinking } : {}
1083
1587
  };
1084
1588
  }
1085
1589
  /**
1590
+ * The `max_tokens` sent when neither the call nor the model config sets
1591
+ * `maxTokens`:
1592
+ *
1593
+ * - streaming: the model's documented max output, or
1594
+ * `DEFAULT_MAX_TOKENS` (16384) for an unknown id;
1595
+ * - non-streaming: `DEFAULT_MAX_TOKENS`, capped by the model's max output
1596
+ * (the SDK requires streaming for larger non-streaming requests unless
1597
+ * the client sets a `timeout`; see `DEFAULT_MAX_TOKENS`).
1598
+ *
1599
+ * With thinking on, the value is raised to at least the thinking budget
1600
+ * plus `MIN_ANSWER_TOKENS`, and never exceeds the model's max output.
1601
+ */
1602
+ resolveDefaultMaxTokens(stream, reservedTokens) {
1603
+ const limit = this.maxOutputTokens;
1604
+ const base = stream ? limit ?? DEFAULT_MAX_TOKENS : limit === void 0 ? DEFAULT_MAX_TOKENS : Math.min(limit, DEFAULT_MAX_TOKENS);
1605
+ const withThinking = reservedTokens === void 0 ? base : Math.max(base, reservedTokens + MIN_ANSWER_TOKENS);
1606
+ return limit === void 0 ? withThinking : Math.min(withThinking, limit);
1607
+ }
1608
+ /**
1086
1609
  * Spread-friendly `system` fragment. Returns an empty object when no
1087
1610
  * system prompt was hoisted out of the messages.
1088
1611
  *
@@ -1145,12 +1668,18 @@ var AnthropicModel = class {
1145
1668
  * `messages.d.ts:1084`), so the budget is floored at that minimum.
1146
1669
  *
1147
1670
  * The neutral `effort: "none"` ("run without reasoning") is mapped per
1148
- * model:
1149
- * - Claude Sonnet 5.5 (`acceptsBetweenTools`): `thinking: { type:
1150
- * "between_tools" }`, its only thinking-off form. It is sent alone —
1151
- * no `output_config.effort` (the model default, `high`, is within the
1152
- * `high`-or-below range it requires) and no `display`, both of which
1153
- * the API rejects alongside it.
1671
+ * model by `noReasoningForm` (see `thinking-mode.ts` for the sources):
1672
+ * - Claude Sonnet 5.5: `thinking: { type: "between_tools" }`, its only
1673
+ * thinking-off form. It is sent alone — no `output_config.effort` (the
1674
+ * model default, `high`, is within the `high`-or-below range it
1675
+ * requires) and no `display`, both of which the API rejects alongside it.
1676
+ * - Claude Sonnet 5 and Claude Haiku 5.5: `thinking: { type: "disabled" }`
1677
+ * with no effort and no `display`. Haiku 5.5 accepts `disabled` only at
1678
+ * effort `high` or below; its default (`medium`) applies.
1679
+ * - Claude Opus 5: adaptive thinking at `output_config.effort: "low"`.
1680
+ * Opus 5 accepts `disabled`, but with it the model can write a tool call
1681
+ * as plain text (the call never runs) and leak thinking tags; the lowest
1682
+ * effort is the documented alternative.
1154
1683
  * - Opus 5.5 and Fable 5/5.1: no `thinking` field. These models have no
1155
1684
  * thinking-off form (`disabled` and `budget_tokens` are a 400 at every
1156
1685
  * effort), so thinking still runs at the model's default effort. Pass
@@ -1159,7 +1688,7 @@ var AnthropicModel = class {
1159
1688
  */
1160
1689
  buildThinking(reasoning) {
1161
1690
  if (!this.capabilities.reasoning || !reasoning) return {};
1162
- if (reasoning.effort === "none") return acceptsBetweenTools(this.name) ? { thinking: { type: "between_tools" } } : {};
1691
+ if (reasoning.effort === "none") return this.buildNoReasoning();
1163
1692
  if (reasoning.maxTokens === void 0 && reasoning.effort === void 0) return {};
1164
1693
  const effort = reasoning.effort ?? DEFAULT_THINKING_EFFORT;
1165
1694
  const budget = Math.max(MIN_THINKING_BUDGET, reasoning.maxTokens ?? EFFORT_THINKING_BUDGET[effort]);
@@ -1177,6 +1706,22 @@ var AnthropicModel = class {
1177
1706
  };
1178
1707
  }
1179
1708
  /**
1709
+ * The thinking settings for `reasoning: { effort: "none" }`, per
1710
+ * `noReasoningForm(this.name)` (documented on `buildThinking`).
1711
+ */
1712
+ buildNoReasoning() {
1713
+ switch (noReasoningForm(this.name)) {
1714
+ case "between_tools": return { thinking: { type: "between_tools" } };
1715
+ case "disabled": return { thinking: { type: "disabled" } };
1716
+ case "lowest-effort": return {
1717
+ thinking: { type: "adaptive" },
1718
+ effort: EFFORT_TO_OUTPUT_EFFORT.low,
1719
+ reservedTokens: EFFORT_THINKING_BUDGET.low
1720
+ };
1721
+ case "omit": return {};
1722
+ }
1723
+ }
1724
+ /**
1180
1725
  * Spread-friendly tools fragment. Returns an empty object when no
1181
1726
  * tools were supplied so the caller can unconditionally spread it.
1182
1727
  *
@@ -1189,9 +1734,16 @@ var AnthropicModel = class {
1189
1734
  * breakpoint there would pay the ~1.25x write surcharge every turn
1190
1735
  * with no reads.
1191
1736
  */
1192
- buildTools(tools, cacheControl) {
1737
+ buildTools(options, cacheControl) {
1738
+ const { tools, definitions } = (0, _warlock_js_ai.selectProviderTools)(options, {
1739
+ accepts: [ANTHROPIC_WIRE_FORMAT, this.provider],
1740
+ module: LOG_MODULE,
1741
+ model: this.name,
1742
+ logger: this.logger
1743
+ });
1193
1744
  const mapped = toAnthropicTools(tools);
1194
- if (!mapped) return {};
1745
+ const providerDefined = definitions;
1746
+ if (!mapped) return providerDefined.length > 0 ? { tools: providerDefined } : {};
1195
1747
  if (this.config.promptCaching && mapped.length > 0) {
1196
1748
  const last = mapped.length - 1;
1197
1749
  const lastTool = mapped[last];
@@ -1200,7 +1752,7 @@ var AnthropicModel = class {
1200
1752
  cache_control: this.cacheControl(cacheControl?.ttl)
1201
1753
  };
1202
1754
  }
1203
- return { tools: mapped };
1755
+ return { tools: [...providerDefined, ...mapped] };
1204
1756
  }
1205
1757
  /**
1206
1758
  * Spread-friendly `output_config` fragment carrying both the native
@@ -1274,27 +1826,34 @@ var AnthropicModel = class {
1274
1826
  /**
1275
1827
  * Normalize Anthropic's `usage` block into the neutral `Usage` shape.
1276
1828
  * Anthropic reports `input_tokens` / `output_tokens` separately with
1277
- * no pre-summed total, so `total` is computed. Cache-read tokens are
1278
- * surfaced as `cachedTokens` and cache-write tokens as
1279
- * `cacheWriteTokens`, each only when non-zero. Anthropic also reports
1829
+ * no pre-summed total, so `total` is computed. `input_tokens` is the
1830
+ * uncached remainder only — cache reads (`cachedTokens`) and writes
1831
+ * (`cacheWriteTokens`, with the 1-hour share in `cacheWrite1hTokens`)
1832
+ * come on top of it — so the usage is marked
1833
+ * `inputIncludesCache: false`. Anthropic also reports
1280
1834
  * `output_tokens_details.thinking_tokens` as the reasoning subset of
1281
1835
  * its inclusive `output_tokens`; it is surfaced as `reasoningTokens`
1282
- * when present, without changing the billing total.
1836
+ * when present, without changing the billing total. Server-side
1837
+ * compaction iterations are added in (see {@link compactionIterationUsage}).
1838
+ * `cost` is attached when the model is priced.
1283
1839
  */
1284
- extractUsage(raw) {
1285
- const input = raw.input_tokens ?? 0;
1286
- const output = raw.output_tokens ?? 0;
1287
- const cacheRead = raw.cache_read_input_tokens;
1288
- const cacheWrite = raw.cache_creation_input_tokens;
1840
+ extractUsage(raw, cacheTtl) {
1841
+ const compaction = compactionIterationUsage(raw);
1842
+ const input = (raw.input_tokens ?? 0) + compaction.input;
1843
+ const output = (raw.output_tokens ?? 0) + compaction.output;
1289
1844
  const thinkingTokens = raw.output_tokens_details?.thinking_tokens;
1290
- return {
1845
+ const usage = {
1291
1846
  input,
1292
1847
  output,
1293
1848
  total: input + output,
1294
- ...cacheRead !== null && cacheRead !== void 0 && cacheRead > 0 ? { cachedTokens: cacheRead } : {},
1295
- ...cacheWrite !== null && cacheWrite !== void 0 && cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {},
1849
+ inputIncludesCache: false,
1296
1850
  ...thinkingTokens !== null && thinkingTokens !== void 0 ? { reasoningTokens: thinkingTokens } : {}
1297
1851
  };
1852
+ applyAnthropicCacheUsage(usage, raw, cacheTtl);
1853
+ addCacheCounts(usage, compaction);
1854
+ const cost = (0, _warlock_js_ai.computeCost)(usage, this.pricing);
1855
+ if (cost) usage.cost = cost;
1856
+ return usage;
1298
1857
  }
1299
1858
  /**
1300
1859
  * Wrap a thrown provider error into the typed `AIError` hierarchy and
@@ -1338,6 +1897,119 @@ const COUNT_TOKENS_FIELDS = [
1338
1897
  "context_management",
1339
1898
  "mcp_servers"
1340
1899
  ];
1900
+ /** Beta flag for typed context editing (Anthropic docs, "Context editing", read 2026-10-17). */
1901
+ const CONTEXT_MANAGEMENT_BETA = "context-management-2025-06-27";
1902
+ /** Beta flag for threshold compaction (Anthropic docs, "Compaction at a token threshold"). */
1903
+ const COMPACTION_BETA = "compact-2026-01-12";
1904
+ /** Beta flag for `thinking.block_binding` (Anthropic docs, "Preserved thinking"). */
1905
+ const THINKING_BINDING_BETA = "thinking-binding-controls-2026-08-01";
1906
+ /** The API rejects a compaction trigger below 50,000 input tokens. */
1907
+ const MIN_COMPACTION_TRIGGER = 5e4;
1908
+ /**
1909
+ * Translate the typed `contextManagement` / `serverCompaction` options into
1910
+ * `context_management.edits`. `clear_thinking_20251015` must come first
1911
+ * when strategies are combined (Anthropic docs, "Context editing").
1912
+ */
1913
+ function buildContextEdits(anthropic) {
1914
+ const edits = [];
1915
+ const { clearThinking, clearToolUses } = anthropic.contextManagement ?? {};
1916
+ if (clearThinking) edits.push(clearThinkingEdit(clearThinking === true ? {} : clearThinking));
1917
+ if (clearToolUses) edits.push(clearToolUsesEdit(clearToolUses === true ? {} : clearToolUses));
1918
+ if (anthropic.serverCompaction) {
1919
+ const compaction = anthropic.serverCompaction === true ? {} : anthropic.serverCompaction;
1920
+ if (compaction.triggerTokens !== void 0) assertCount("serverCompaction.triggerTokens", compaction.triggerTokens, MIN_COMPACTION_TRIGGER);
1921
+ edits.push({
1922
+ type: "compact_20260112",
1923
+ ...compaction.triggerTokens !== void 0 ? { trigger: {
1924
+ type: "input_tokens",
1925
+ value: compaction.triggerTokens
1926
+ } } : {},
1927
+ ...compaction.instructions ? { instructions: compaction.instructions } : {}
1928
+ });
1929
+ }
1930
+ return edits;
1931
+ }
1932
+ function clearThinkingEdit(options) {
1933
+ if (options.keep === void 0 || options.keep === "all") return {
1934
+ type: "clear_thinking_20251015",
1935
+ ...options.keep ? { keep: "all" } : {}
1936
+ };
1937
+ assertCount("contextManagement.clearThinking.keep", options.keep, 1);
1938
+ return {
1939
+ type: "clear_thinking_20251015",
1940
+ keep: {
1941
+ type: "thinking_turns",
1942
+ value: options.keep
1943
+ }
1944
+ };
1945
+ }
1946
+ function clearToolUsesEdit(options) {
1947
+ const edit = { type: "clear_tool_uses_20250919" };
1948
+ if (options.trigger) {
1949
+ const trigger = "inputTokens" in options.trigger ? {
1950
+ type: "input_tokens",
1951
+ value: options.trigger.inputTokens
1952
+ } : {
1953
+ type: "tool_uses",
1954
+ value: options.trigger.toolUses
1955
+ };
1956
+ assertCount("contextManagement.clearToolUses.trigger", trigger.value, 1);
1957
+ edit.trigger = trigger;
1958
+ }
1959
+ if (options.keep !== void 0) {
1960
+ assertCount("contextManagement.clearToolUses.keep", options.keep, 0);
1961
+ edit.keep = {
1962
+ type: "tool_uses",
1963
+ value: options.keep
1964
+ };
1965
+ }
1966
+ if (options.clearAtLeast !== void 0) {
1967
+ assertCount("contextManagement.clearToolUses.clearAtLeast", options.clearAtLeast, 1);
1968
+ edit.clear_at_least = {
1969
+ type: "input_tokens",
1970
+ value: options.clearAtLeast
1971
+ };
1972
+ }
1973
+ if (options.excludeTools !== void 0) edit.exclude_tools = [...options.excludeTools];
1974
+ if (options.clearToolInputs !== void 0) edit.clear_tool_inputs = Array.isArray(options.clearToolInputs) ? [...options.clearToolInputs] : options.clearToolInputs;
1975
+ return edit;
1976
+ }
1977
+ function assertCount(field, value, minimum) {
1978
+ if (!Number.isSafeInteger(value) || value < minimum) throw new _warlock_js_ai.InvalidRequestError(`providerOptions.anthropic.${field} must be an integer of at least ${minimum}; got ${value}.`, { context: { field } });
1979
+ }
1980
+ /**
1981
+ * Tokens of the `compaction` entries in a beta response's `usage.iterations`.
1982
+ * With server-side compaction the top-level `input_tokens` / `output_tokens`
1983
+ * cover only the non-compaction iterations; the billed total is the sum
1984
+ * over every iteration (Anthropic docs, "Compaction at a token threshold" →
1985
+ * "Understanding usage", read 2026-10-17). Zeros when there are none.
1986
+ */
1987
+ function compactionIterationUsage(raw) {
1988
+ const totals = {
1989
+ input: 0,
1990
+ output: 0,
1991
+ cacheRead: 0,
1992
+ cacheWrite: 0
1993
+ };
1994
+ const iterations = raw?.iterations;
1995
+ if (!Array.isArray(iterations)) return totals;
1996
+ for (const entry of iterations) {
1997
+ const iteration = entry;
1998
+ if (iteration?.type !== "compaction") continue;
1999
+ totals.input += numberOrZero(iteration.input_tokens);
2000
+ totals.output += numberOrZero(iteration.output_tokens);
2001
+ totals.cacheRead += numberOrZero(iteration.cache_read_input_tokens);
2002
+ totals.cacheWrite += numberOrZero(iteration.cache_creation_input_tokens);
2003
+ }
2004
+ return totals;
2005
+ }
2006
+ function numberOrZero(value) {
2007
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
2008
+ }
2009
+ function addCacheCounts(usage, extra) {
2010
+ if (extra.cacheRead > 0) usage.cachedTokens = (usage.cachedTokens ?? 0) + extra.cacheRead;
2011
+ if (extra.cacheWrite > 0) usage.cacheWriteTokens = (usage.cacheWriteTokens ?? 0) + extra.cacheWrite;
2012
+ }
1341
2013
  /** Read `providerOptions.anthropic`, tolerating its absence or a wrong shape. */
1342
2014
  function resolveAnthropicOptions(options) {
1343
2015
  const raw = options?.providerOptions?.anthropic;
@@ -1422,6 +2094,17 @@ var AnthropicSDK = class {
1422
2094
  };
1423
2095
 
1424
2096
  //#endregion
2097
+ exports.ANTHROPIC_BLOCK_FORMAT = ANTHROPIC_BLOCK_FORMAT;
2098
+ exports.ANTHROPIC_CACHE_WRITE_1H_MULTIPLIER = ANTHROPIC_CACHE_WRITE_1H_MULTIPLIER;
2099
+ exports.ANTHROPIC_CACHE_WRITE_MULTIPLIER = ANTHROPIC_CACHE_WRITE_MULTIPLIER;
2100
+ exports.ANTHROPIC_MODEL_PRICING = ANTHROPIC_MODEL_PRICING;
1425
2101
  exports.AnthropicModel = AnthropicModel;
1426
2102
  exports.AnthropicSDK = AnthropicSDK;
2103
+ exports.anthropicPricingFor = anthropicPricingFor;
2104
+ exports.fromProviderBlocks = fromProviderBlocks;
2105
+ exports.inferPreservedThinking = inferPreservedThinking;
2106
+ exports.toAnthropicMessages = toAnthropicMessages;
2107
+ exports.toAnthropicTools = toAnthropicTools;
2108
+ exports.toProviderBlocks = toProviderBlocks;
2109
+ exports.withAnthropicCacheWriteRates = withAnthropicCacheWriteRates;
1427
2110
  //# sourceMappingURL=index.cjs.map