@warlock.js/ai-anthropic 5.25.0 → 5.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm/model.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import { inferVisionCapability } from "./known-vision-models.mjs";
2
+ import { acceptsTemperature, inferThinkingMode } from "./thinking-mode.mjs";
2
3
  import { mapStopReason } from "./utils/map-stop-reason.mjs";
3
4
  import { toAnthropicMessages } from "./utils/to-anthropic-messages.mjs";
4
5
  import { toAnthropicTools } from "./utils/to-anthropic-tools.mjs";
@@ -24,16 +25,45 @@ const DEFAULT_MAX_TOKENS = 4096;
24
25
  */
25
26
  const MIN_THINKING_BUDGET = 1024;
26
27
  /**
27
- * Map the neutral `ReasoningEffort` level to an Anthropic
28
- * `thinking.budget_tokens` value. Anthropic budgets reasoning by token
29
- * count (unlike OpenAI's opaque `reasoning_effort` enum), so the three
30
- * neutral levels translate to representative token budgets when the
31
- * caller doesn't pass an explicit `reasoning.maxTokens`.
28
+ * Effort used when the caller asks for reasoning with only
29
+ * `reasoning.maxTokens` and no `effort`. `"medium"` is what the adapter
30
+ * has always assumed (the budget lookup fell back to
31
+ * `EFFORT_THINKING_BUDGET.medium`), so adaptive models get the same tier.
32
+ */
33
+ const DEFAULT_THINKING_EFFORT = "medium";
34
+ /**
35
+ * Map the neutral `ReasoningEffort` level to a thinking token count.
36
+ *
37
+ * - Budget-mode models: sent as `thinking.budget_tokens` when the caller
38
+ * doesn't pass an explicit `reasoning.maxTokens`.
39
+ * - Both modes: reserved on top of `DEFAULT_MAX_TOKENS` when the caller
40
+ * sets no `maxTokens`, because thinking counts toward `max_tokens`
41
+ * (installed SDK `ThinkingConfigParam` docs, `messages.d.ts:1105`).
42
+ * Adaptive models therefore get the same `max_tokens` the budget path
43
+ * has always produced.
32
44
  */
33
45
  const EFFORT_THINKING_BUDGET = {
46
+ minimal: 1024,
34
47
  low: 1024,
35
48
  medium: 4096,
36
- high: 12e3
49
+ high: 12e3,
50
+ xhigh: 24e3,
51
+ max: 32e3
52
+ };
53
+ /**
54
+ * Map the neutral `ReasoningEffort` level to Anthropic's
55
+ * `output_config.effort` for adaptive-mode models. The installed SDK
56
+ * accepts `low | medium | high | xhigh | max` (`OutputConfig.effort`,
57
+ * `messages.ts:2767`); map every supported value 1:1 and clamp `minimal` to
58
+ * the nearest accepted value, `low`.
59
+ */
60
+ const EFFORT_TO_OUTPUT_EFFORT = {
61
+ minimal: "low",
62
+ low: "low",
63
+ medium: "medium",
64
+ high: "high",
65
+ xhigh: "xhigh",
66
+ max: "max"
37
67
  };
38
68
  /**
39
69
  * Anthropic-backed implementation of `ModelContract`.
@@ -81,6 +111,7 @@ var AnthropicModel = class {
81
111
  this.name = config.name;
82
112
  this.provider = provider;
83
113
  this.pricing = config.pricing;
114
+ this.thinkingMode = config.thinkingMode ?? inferThinkingMode(config.name);
84
115
  this.capabilities = {
85
116
  structuredOutput: config.structuredOutput ?? true,
86
117
  vision: config.vision ?? inferVisionCapability(config.name),
@@ -160,6 +191,8 @@ var AnthropicModel = class {
160
191
  for await (const event of stream) {
161
192
  if (event.type === "message_start") {
162
193
  usage.input = event.message.usage.input_tokens ?? 0;
194
+ const thinkingTokens = event.message.usage.output_tokens_details?.thinking_tokens;
195
+ if (thinkingTokens !== null && thinkingTokens !== void 0) usage.reasoningTokens = thinkingTokens;
163
196
  const cacheRead = event.message.usage.cache_read_input_tokens;
164
197
  if (cacheRead !== null && cacheRead !== void 0 && cacheRead > 0) usage.cachedTokens = cacheRead;
165
198
  const cacheWrite = event.message.usage.cache_creation_input_tokens;
@@ -211,6 +244,8 @@ var AnthropicModel = class {
211
244
  if (event.type === "message_delta") {
212
245
  rawStopReason = event.delta.stop_reason ?? rawStopReason;
213
246
  usage.output = event.usage.output_tokens ?? usage.output;
247
+ const thinkingTokens = event.usage.output_tokens_details?.thinking_tokens;
248
+ if (thinkingTokens !== null && thinkingTokens !== void 0) usage.reasoningTokens = thinkingTokens;
214
249
  const cacheRead = event.usage.cache_read_input_tokens;
215
250
  if (cacheRead !== null && cacheRead !== void 0 && cacheRead > 0) usage.cachedTokens = cacheRead;
216
251
  const cacheWrite = event.usage.cache_creation_input_tokens;
@@ -239,9 +274,10 @@ var AnthropicModel = class {
239
274
  * prompt out of `messages`, resolves `max_tokens` (required by
240
275
  * Anthropic) with the documented default, and conditionally attaches
241
276
  * temperature, tools, native structured output, extended thinking
242
- * (`reasoning`), and a system-prompt cache breakpoint (`cacheControl`).
277
+ * (`reasoning`), and automatic or explicit prompt-cache control.
243
278
  * Temperature is dropped when thinking is enabled, since Anthropic
244
- * rejects the two together.
279
+ * rejects the two together, and always for adaptive-mode models, which
280
+ * reject any explicit temperature (see `acceptsTemperature`).
245
281
  */
246
282
  buildParams(messages, options) {
247
283
  const { system, messages: anthropicMessages } = toAnthropicMessages(messages);
@@ -249,17 +285,19 @@ var AnthropicModel = class {
249
285
  const temperature = options?.temperature ?? this.config.temperature;
250
286
  const configuredMaxTokens = options?.maxTokens ?? this.config.maxTokens;
251
287
  const thinkingBudget = thinking.thinking?.type === "enabled" ? thinking.thinking.budget_tokens : void 0;
252
- const maxTokens = configuredMaxTokens ?? (thinkingBudget !== void 0 ? thinkingBudget + DEFAULT_MAX_TOKENS : DEFAULT_MAX_TOKENS);
288
+ const maxTokens = configuredMaxTokens ?? (thinking.reservedTokens !== void 0 ? thinking.reservedTokens + DEFAULT_MAX_TOKENS : DEFAULT_MAX_TOKENS);
289
+ const sendTemperature = temperature !== void 0 && !thinking.thinking && acceptsTemperature(this.thinkingMode);
253
290
  if (thinkingBudget !== void 0 && configuredMaxTokens !== void 0 && configuredMaxTokens <= thinkingBudget) throw new Error(`Anthropic maxTokens (${configuredMaxTokens}) must be greater than thinking budget_tokens (${thinkingBudget})`);
254
291
  return {
255
292
  model: this.name,
256
293
  max_tokens: maxTokens,
257
294
  messages: anthropicMessages,
295
+ ...this.buildAutomaticCacheControl(options?.cacheControl),
258
296
  ...this.buildSystem(system, options?.cacheControl),
259
- ...temperature !== void 0 && !thinking.thinking ? { temperature } : {},
297
+ ...sendTemperature ? { temperature } : {},
260
298
  ...this.buildTools(options?.tools),
261
- ...this.buildStructuredOutput(options?.responseSchema),
262
- ...thinking
299
+ ...this.buildOutputConfig(options?.responseSchema, thinking.effort),
300
+ ...thinking.thinking ? { thinking: thinking.thinking } : {}
263
301
  };
264
302
  }
265
303
  /**
@@ -280,31 +318,66 @@ var AnthropicModel = class {
280
318
  if (this.capabilities.promptCaching && breakpoints > 0) return { system: [{
281
319
  type: "text",
282
320
  text: system,
283
- cache_control: { type: "ephemeral" }
321
+ cache_control: this.cacheControl()
284
322
  }] };
285
323
  return { system };
286
324
  }
287
325
  /**
288
- * Translate the neutral `reasoning` option into Anthropic's
289
- * `thinking` request field. Emitted only when the model declares the
290
- * `reasoning` capability AND a reasoning option is supplied; otherwise
291
- * returns an empty object so the caller can unconditionally spread it.
326
+ * Let Anthropic select the last cacheable block when caching is enabled
327
+ * without a caller-selected breakpoint. An explicit breakpoint remains
328
+ * authoritative, so the request-level form is deliberately omitted then.
329
+ */
330
+ buildAutomaticCacheControl(cacheControl) {
331
+ const breakpoints = cacheControl?.breakpoints ?? 0;
332
+ return this.config.promptCaching && breakpoints <= 0 ? { cache_control: this.cacheControl() } : {};
333
+ }
334
+ /** Build a typed cache-control marker, omitting TTL for Anthropic's 5-minute default. */
335
+ cacheControl() {
336
+ return {
337
+ type: "ephemeral",
338
+ ...this.config.promptCacheTtl ? { ttl: this.config.promptCacheTtl } : {}
339
+ };
340
+ }
341
+ /**
342
+ * Translate the neutral `reasoning` option into Anthropic's thinking
343
+ * settings, in the shape this model accepts (`thinkingMode`). Emitted
344
+ * only when the model declares the `reasoning` capability AND a
345
+ * reasoning option is supplied; otherwise returns an empty object.
292
346
  *
293
- * Budget resolution: an explicit `reasoning.maxTokens` wins; otherwise
294
- * the neutral `effort` level maps to a tiered token budget. Anthropic
295
- * requires `budget_tokens` ≥ 1024, so the budget is floored at that
296
- * minimum. The neutral `effort: "none"` ("run without reasoning") maps
297
- * to no `thinking` block at all — extended thinking is opt-in here.
347
+ * - Adaptive mode (Opus 4.7+, Sonnet 5+, Fable 5+, unknown ids):
348
+ * `thinking: { type: "adaptive" }` (installed SDK
349
+ * `ThinkingConfigAdaptive`, `messages.d.ts:1065`) plus
350
+ * `output_config.effort` from the neutral effort, defaulting to
351
+ * `"medium"`. `budget_tokens` is never sent — these models 400 on it
352
+ * (claude-api ref). `reasoning.maxTokens` only sizes the default
353
+ * `max_tokens` headroom.
354
+ * - Budget mode (Haiku 4.5, Opus/Sonnet 4.6, older):
355
+ * `thinking: { type: "enabled", budget_tokens }`. An explicit
356
+ * `reasoning.maxTokens` wins; otherwise the effort maps to a tiered
357
+ * budget. `budget_tokens` must be ≥ 1024 (`ThinkingConfigEnabled`,
358
+ * `messages.d.ts:1084`), so the budget is floored at that minimum.
359
+ *
360
+ * The neutral `effort: "none"` ("run without reasoning") maps to no
361
+ * `thinking` field at all in both modes.
298
362
  */
299
363
  buildThinking(reasoning) {
300
364
  if (!this.capabilities.reasoning || !reasoning) return {};
301
365
  if (reasoning.effort === "none") return {};
302
366
  if (reasoning.maxTokens === void 0 && reasoning.effort === void 0) return {};
303
- const budget = reasoning.maxTokens ?? EFFORT_THINKING_BUDGET[reasoning.effort ?? "medium"];
304
- return { thinking: {
305
- type: "enabled",
306
- budget_tokens: Math.max(MIN_THINKING_BUDGET, budget)
307
- } };
367
+ const effort = reasoning.effort ?? DEFAULT_THINKING_EFFORT;
368
+ const budget = Math.max(MIN_THINKING_BUDGET, reasoning.maxTokens ?? EFFORT_THINKING_BUDGET[effort]);
369
+ if (this.thinkingMode === "adaptive") return {
370
+ thinking: { type: "adaptive" },
371
+ effort: EFFORT_TO_OUTPUT_EFFORT[effort],
372
+ reservedTokens: budget
373
+ };
374
+ return {
375
+ thinking: {
376
+ type: "enabled",
377
+ budget_tokens: budget
378
+ },
379
+ reservedTokens: budget
380
+ };
308
381
  }
309
382
  /**
310
383
  * Spread-friendly tools fragment. Returns an empty object when no
@@ -324,32 +397,49 @@ var AnthropicModel = class {
324
397
  if (!mapped) return {};
325
398
  if (this.config.promptCaching && mapped.length > 0) {
326
399
  const last = mapped.length - 1;
327
- mapped[last] = {
328
- ...mapped[last],
329
- cache_control: { type: "ephemeral" }
400
+ const lastTool = mapped[last];
401
+ if (lastTool) mapped[last] = {
402
+ ...lastTool,
403
+ cache_control: this.cacheControl()
330
404
  };
331
405
  }
332
406
  return { tools: mapped };
333
407
  }
334
408
  /**
409
+ * Spread-friendly `output_config` fragment carrying both the native
410
+ * structured-output `format` and the adaptive-thinking `effort`, merged
411
+ * into one object (both live under `OutputConfig`, installed SDK
412
+ * `messages.d.ts:796-806`). Returns an empty object when neither applies,
413
+ * so budget-mode requests carry `output_config` only when a schema asks
414
+ * for it.
415
+ */
416
+ buildOutputConfig(responseSchema, effort) {
417
+ const format = this.buildStructuredOutputFormat(responseSchema);
418
+ if (!format && !effort) return {};
419
+ return { output_config: {
420
+ ...format ? { format } : {},
421
+ ...effort ? { effort } : {}
422
+ } };
423
+ }
424
+ /**
335
425
  * Translate the neutral `responseSchema` option into Anthropic's
336
426
  * native `output_config.format` (JSON-schema structured outputs).
337
427
  *
338
- * Only emitted when the model declares the `structuredOutput`
428
+ * Only returned when the model declares the `structuredOutput`
339
429
  * capability AND the schema is a proper root-object JSON Schema —
340
430
  * Anthropic rejects non-object roots. When the capability is off
341
- * (config override) or the schema is non-object, returns an empty
342
- * object: the agent has already injected a soft schema hint into the
343
- * system prompt as the fallback, and client-side `validate()` still
344
- * enforces shape.
431
+ * (config override) or the schema is non-object, returns `undefined`:
432
+ * the agent has already injected a soft schema hint into the system
433
+ * prompt as the fallback, and client-side `validate()` still enforces
434
+ * shape.
345
435
  */
346
- buildStructuredOutput(responseSchema) {
347
- if (!responseSchema || !this.capabilities.structuredOutput) return {};
348
- if (responseSchema.type !== "object" || typeof responseSchema.properties !== "object") return {};
349
- return { output_config: { format: {
436
+ buildStructuredOutputFormat(responseSchema) {
437
+ if (!responseSchema || !this.capabilities.structuredOutput) return;
438
+ if (responseSchema.type !== "object" || typeof responseSchema.properties !== "object") return;
439
+ return {
350
440
  type: "json_schema",
351
441
  schema: responseSchema
352
- } } };
442
+ };
353
443
  }
354
444
  /**
355
445
  * Concatenate every `text` content block into the single neutral
@@ -389,24 +479,24 @@ var AnthropicModel = class {
389
479
  * Anthropic reports `input_tokens` / `output_tokens` separately with
390
480
  * no pre-summed total, so `total` is computed. Cache-read tokens are
391
481
  * surfaced as `cachedTokens` and cache-write tokens as
392
- * `cacheWriteTokens`, each only when non-zero.
393
- *
394
- * Note: Anthropic does not report a separate reasoning-token count —
395
- * extended-thinking tokens are billed inside `output_tokens` — so
396
- * `Usage.reasoningTokens` is intentionally left unset here. Populating
397
- * it would double-count against `output`.
482
+ * `cacheWriteTokens`, each only when non-zero. Anthropic also reports
483
+ * `output_tokens_details.thinking_tokens` as the reasoning subset of
484
+ * its inclusive `output_tokens`; it is surfaced as `reasoningTokens`
485
+ * when present, without changing the billing total.
398
486
  */
399
487
  extractUsage(raw) {
400
488
  const input = raw.input_tokens ?? 0;
401
489
  const output = raw.output_tokens ?? 0;
402
490
  const cacheRead = raw.cache_read_input_tokens;
403
491
  const cacheWrite = raw.cache_creation_input_tokens;
492
+ const thinkingTokens = raw.output_tokens_details?.thinking_tokens;
404
493
  return {
405
494
  input,
406
495
  output,
407
496
  total: input + output,
408
497
  ...cacheRead !== null && cacheRead !== void 0 && cacheRead > 0 ? { cachedTokens: cacheRead } : {},
409
- ...cacheWrite !== null && cacheWrite !== void 0 && cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {}
498
+ ...cacheWrite !== null && cacheWrite !== void 0 && cacheWrite > 0 ? { cacheWriteTokens: cacheWrite } : {},
499
+ ...thinkingTokens !== null && thinkingTokens !== void 0 ? { reasoningTokens: thinkingTokens } : {}
410
500
  };
411
501
  }
412
502
  /**
package/esm/model.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"model.mjs","names":[],"sources":["../../../../../../ai-anthropic/src/model.ts"],"sourcesContent":["import {\n safeJsonParse,\n type Message,\n type ModelCallOptions,\n type ModelCapabilities,\n type ModelContract,\n type ModelPricing,\n type ModelResponse,\n type ModelStreamChunk,\n type ModelToolCallRequest,\n type ReasoningEffort,\n type Usage,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport type { AnthropicModelConfig } from \"./config.type\";\nimport { inferVisionCapability } from \"./known-vision-models\";\nimport { mapStopReason, toAnthropicMessages, toAnthropicTools, wrapAnthropicError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.anthropic\";\n\n/**\n * Anthropic requires `max_tokens` on every request (unlike OpenAI,\n * where it is optional). When neither the per-call option nor the\n * model config supplies one, fall back to a generous default so a\n * caller who never thought about token caps still gets a complete\n * answer instead of a 400.\n */\nconst DEFAULT_MAX_TOKENS = 4096;\n\n/**\n * Anthropic rejects an extended-thinking budget below 1024 tokens, so\n * any resolved budget is floored at this minimum before it reaches the\n * wire.\n */\nconst MIN_THINKING_BUDGET = 1024;\n\n/**\n * Map the neutral `ReasoningEffort` level to an Anthropic\n * `thinking.budget_tokens` value. Anthropic budgets reasoning by token\n * count (unlike OpenAI's opaque `reasoning_effort` enum), so the three\n * neutral levels translate to representative token budgets when the\n * caller doesn't pass an explicit `reasoning.maxTokens`.\n */\nconst EFFORT_THINKING_BUDGET: Record<Exclude<ReasoningEffort, \"none\">, number> = {\n low: 1024,\n medium: 4096,\n high: 12000,\n};\n\ntype AnthropicThinkingBlock = Anthropic.ThinkingBlock | Anthropic.RedactedThinkingBlock;\n\n/**\n * Anthropic-backed implementation of `ModelContract`.\n *\n * **Role.** The provider-facing bridge between the vendor-neutral\n * `@warlock.js/ai` agent runtime and the official `@anthropic-ai/sdk`\n * Messages API. Agents, workflows, and supervisors never talk to\n * Anthropic directly — they hold a `ModelContract`, and this class is\n * what makes that contract concrete for Claude models.\n *\n * **Responsibility.**\n * - Owns: a long-lived `Anthropic` client + frozen `ModelConfig`\n * (name, temperature, maxTokens) used as defaults for every call.\n * - Owns: translating vendor-neutral `Message[]` / `ToolConfig[]` into\n * Anthropic wire shapes (system hoisting, `tool_use` / `tool_result`\n * blocks) on the way out, and translating Anthropic's content-block\n * response (text, tool calls, stop reason, usage) back into the\n * neutral shapes on the way in.\n * - Does NOT own: dispatching tools, deciding whether to loop, tracking\n * conversation history, or retrying on failure — those are agent\n * concerns. The model is a stateless (per-call) protocol adapter.\n *\n * Because it holds a live client and shared defaults, it is modeled as\n * a class (see §4.2 of code-style.md — \"long-lived state across\n * calls\").\n *\n * @example\n * import Anthropic from \"@anthropic-ai/sdk\";\n * const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });\n * const model = new AnthropicModel(client, { name: \"claude-sonnet-4-6\" });\n *\n * const myAgent = agent({\n * model,\n * systemPrompt: \"You are a helpful assistant.\",\n * tools: [searchTool],\n * });\n *\n * const result = await myAgent.execute(\"Summarize today's news.\");\n */\nexport class AnthropicModel implements ModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly capabilities: ModelCapabilities;\n public readonly pricing?: ModelPricing;\n\n private readonly client: Anthropic;\n private readonly config: AnthropicModelConfig;\n private readonly logger: Logger = log;\n\n public constructor(\n client: Anthropic,\n config: AnthropicModelConfig,\n provider: string = \"anthropic\",\n ) {\n this.client = client;\n this.config = config;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n this.capabilities = {\n structuredOutput: config.structuredOutput ?? true,\n vision: config.vision ?? inferVisionCapability(config.name),\n // Every modern Claude model accepts Anthropic extended thinking\n // (`thinking: { type: \"enabled\", budget_tokens }`). Advertise the\n // reasoning channel so the agent forwards `reasoning` options;\n // explicit config override wins for proxied/legacy targets.\n reasoning: config.reasoning ?? true,\n // Anthropic prompt caching is caller-driven via `cache_control`\n // breakpoints. The adapter both places those breakpoints (tools\n // when `config.promptCaching`, system when\n // `options.cacheControl.breakpoints`) and reports the read/write\n // accounting (`cachedTokens` / `cacheWriteTokens`), so advertise\n // the capability unconditionally.\n promptCaching: true,\n // The Messages API accepts PDF/document content blocks\n // (`DocumentBlockParam`) on vision-capable Claude models.\n pdf: true,\n // Anthropic has no audio input content block, so `audio` stays\n // absent (treated as false) and the agent rejects audio\n // attachments upfront rather than dropping them at the wire layer.\n };\n }\n\n /**\n * Single-shot completion. Sends the full message list to the Messages\n * endpoint, waits for the terminal response, and reshapes it into a\n * vendor-neutral `ModelResponse`. Per-call `options` override the\n * instance's `ModelConfig` defaults for this call only.\n */\n public async complete(messages: Message[], options?: ModelCallOptions): Promise<ModelResponse> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting call to messages.create\", {\n model: this.name,\n messageCount: messages.length,\n streaming: false,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let response: Anthropic.Message;\n\n try {\n response = await this.client.messages.create(\n { ...this.buildParams(messages, options), stream: false },\n options?.signal ? { signal: options.signal } : undefined,\n );\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n const finishReason = mapStopReason(response.stop_reason);\n const usage = this.extractUsage(response.usage);\n const toolCalls = this.extractToolCalls(response.content);\n\n this.logger.debug(LOG_MODULE, \"response\", \"call to messages.create succeeded\", {\n finishReason,\n usage,\n });\n\n return {\n content: this.extractText(response.content),\n finishReason,\n usage,\n toolCalls,\n };\n }\n\n /**\n * Incremental streaming completion. Yields neutral `ModelStreamChunk`s\n * — `delta` for text tokens, `tool-call` once a `tool_use` block's\n * arguments have fully accumulated, and a terminal `done` carrying the\n * final finish reason + usage totals. Callers consume it with\n * `for await`.\n */\n public async *stream(\n messages: Message[],\n options?: ModelCallOptions,\n ): AsyncIterable<ModelStreamChunk> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting streaming call to messages.create\", {\n model: this.name,\n messageCount: messages.length,\n streaming: true,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let stream: Awaited<ReturnType<Anthropic[\"messages\"][\"create\"]>>;\n\n try {\n stream = await this.client.messages.create(\n { ...this.buildParams(messages, options), stream: true },\n options?.signal ? { signal: options.signal } : undefined,\n );\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n let rawStopReason: string | null = null;\n const usage: Usage = { input: 0, output: 0, total: 0 };\n const toolBlocks = new Map<number, { id: string; name: string; json: string }>();\n const thinkingBlocks = new Map<number, AnthropicThinkingBlock>();\n\n try {\n for await (const event of stream as AsyncIterable<Anthropic.RawMessageStreamEvent>) {\n if (event.type === \"message_start\") {\n usage.input = event.message.usage.input_tokens ?? 0;\n\n const cacheRead = event.message.usage.cache_read_input_tokens;\n\n if (cacheRead !== null && cacheRead !== undefined && cacheRead > 0) {\n usage.cachedTokens = cacheRead;\n }\n\n const cacheWrite = event.message.usage.cache_creation_input_tokens;\n\n if (cacheWrite !== null && cacheWrite !== undefined && cacheWrite > 0) {\n usage.cacheWriteTokens = cacheWrite;\n }\n\n continue;\n }\n\n if (event.type === \"content_block_start\") {\n const block = event.content_block;\n\n if (block.type === \"tool_use\") {\n toolBlocks.set(event.index, { id: block.id, name: block.name, json: \"\" });\n } else if (block.type === \"thinking\") {\n thinkingBlocks.set(event.index, { ...block });\n } else if (block.type === \"redacted_thinking\") {\n thinkingBlocks.set(event.index, { ...block });\n }\n\n continue;\n }\n\n if (event.type === \"content_block_delta\") {\n if (event.delta.type === \"text_delta\") {\n yield { type: \"delta\", content: event.delta.text };\n } else if (event.delta.type === \"input_json_delta\") {\n const accumulator = toolBlocks.get(event.index);\n\n if (accumulator) {\n accumulator.json += event.delta.partial_json;\n }\n } else if (event.delta.type === \"thinking_delta\") {\n const thinking = thinkingBlocks.get(event.index);\n\n if (thinking?.type === \"thinking\") {\n thinking.thinking += event.delta.thinking;\n }\n } else if (event.delta.type === \"signature_delta\") {\n const thinking = thinkingBlocks.get(event.index);\n\n if (thinking?.type === \"thinking\") {\n thinking.signature += event.delta.signature;\n }\n }\n\n continue;\n }\n\n if (event.type === \"content_block_stop\") {\n const accumulator = toolBlocks.get(event.index);\n\n if (accumulator) {\n yield {\n type: \"tool-call\",\n id: accumulator.id,\n name: accumulator.name,\n input: safeJsonParse<Record<string, unknown>>(accumulator.json, {}),\n ...this.buildThinkingMetadata(\n Array.from(thinkingBlocks.entries())\n .sort(([left], [right]) => left - right)\n .map(([, block]) => block),\n ),\n };\n\n toolBlocks.delete(event.index);\n }\n\n continue;\n }\n\n if (event.type === \"message_delta\") {\n rawStopReason = event.delta.stop_reason ?? rawStopReason;\n usage.output = event.usage.output_tokens ?? usage.output;\n\n // `message_delta.usage` carries the cumulative cache counts —\n // prefer them over the `message_start` snapshot when present\n // and non-zero so the terminal `done` reflects the final tally.\n const cacheRead = event.usage.cache_read_input_tokens;\n\n if (cacheRead !== null && cacheRead !== undefined && cacheRead > 0) {\n usage.cachedTokens = cacheRead;\n }\n\n const cacheWrite = event.usage.cache_creation_input_tokens;\n\n if (cacheWrite !== null && cacheWrite !== undefined && cacheWrite > 0) {\n usage.cacheWriteTokens = cacheWrite;\n }\n }\n }\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n usage.total = usage.input + usage.output;\n\n const finishReason = mapStopReason(rawStopReason);\n\n this.logger.debug(LOG_MODULE, \"response\", \"Streaming call to messages.create succeeded\", {\n finishReason,\n usage,\n });\n\n yield { type: \"done\", finishReason, usage };\n }\n\n /**\n * Assemble the Anthropic request body shared by `complete()` and\n * `stream()` (each adds its own `stream` literal so the SDK's create\n * overload resolves to the right return type). Hoists the system\n * prompt out of `messages`, resolves `max_tokens` (required by\n * Anthropic) with the documented default, and conditionally attaches\n * temperature, tools, native structured output, extended thinking\n * (`reasoning`), and a system-prompt cache breakpoint (`cacheControl`).\n * Temperature is dropped when thinking is enabled, since Anthropic\n * rejects the two together.\n */\n private buildParams(\n messages: Message[],\n options: ModelCallOptions | undefined,\n ): Omit<Anthropic.MessageCreateParamsNonStreaming, \"stream\"> {\n const { system, messages: anthropicMessages } = toAnthropicMessages(messages);\n const thinking = this.buildThinking(options?.reasoning);\n const temperature = options?.temperature ?? this.config.temperature;\n const configuredMaxTokens = options?.maxTokens ?? this.config.maxTokens;\n const thinkingBudget =\n thinking.thinking?.type === \"enabled\" ? thinking.thinking.budget_tokens : undefined;\n const maxTokens =\n configuredMaxTokens ??\n (thinkingBudget !== undefined ? thinkingBudget + DEFAULT_MAX_TOKENS : DEFAULT_MAX_TOKENS);\n\n if (thinkingBudget !== undefined && configuredMaxTokens !== undefined && configuredMaxTokens <= thinkingBudget) {\n throw new Error(\n `Anthropic maxTokens (${configuredMaxTokens}) must be greater than thinking budget_tokens (${thinkingBudget})`,\n );\n }\n\n return {\n model: this.name,\n max_tokens: maxTokens,\n messages: anthropicMessages,\n ...this.buildSystem(system, options?.cacheControl),\n // Extended thinking pins sampling to the default temperature —\n // Anthropic 400s when `thinking` is enabled alongside any explicit\n // `temperature`. Drop temperature in that case rather than letting\n // the request fail.\n ...(temperature !== undefined && !thinking.thinking ? { temperature } : {}),\n ...this.buildTools(options?.tools),\n ...this.buildStructuredOutput(options?.responseSchema),\n ...thinking,\n };\n }\n\n /**\n * Spread-friendly `system` fragment. Returns an empty object when no\n * system prompt was hoisted out of the messages.\n *\n * When a per-call `cacheControl.breakpoints` hint is present (≥ 1),\n * the system prompt is emitted as a single `TextBlockParam` carrying\n * `cache_control: ephemeral` — the system prompt is the longest stable\n * prefix on a turn, so one breakpoint there lets multi-turn agents\n * read it back at the ~0.1x cache-read rate. Without the hint the\n * system prompt stays a plain string (left uncached) so a one-shot\n * call never pays the ~1.25x cache-write surcharge.\n */\n private buildSystem(\n system: string | undefined,\n cacheControl: ModelCallOptions[\"cacheControl\"],\n ): { system?: string | Anthropic.TextBlockParam[] } {\n if (!system) {\n return {};\n }\n\n const breakpoints = cacheControl?.breakpoints ?? 0;\n\n if (this.capabilities.promptCaching && breakpoints > 0) {\n return {\n system: [{ type: \"text\", text: system, cache_control: { type: \"ephemeral\" } }],\n };\n }\n\n return { system };\n }\n\n /**\n * Translate the neutral `reasoning` option into Anthropic's\n * `thinking` request field. Emitted only when the model declares the\n * `reasoning` capability AND a reasoning option is supplied; otherwise\n * returns an empty object so the caller can unconditionally spread it.\n *\n * Budget resolution: an explicit `reasoning.maxTokens` wins; otherwise\n * the neutral `effort` level maps to a tiered token budget. Anthropic\n * requires `budget_tokens` ≥ 1024, so the budget is floored at that\n * minimum. The neutral `effort: \"none\"` (\"run without reasoning\") maps\n * to no `thinking` block at all — extended thinking is opt-in here.\n */\n private buildThinking(\n reasoning: ModelCallOptions[\"reasoning\"],\n ): { thinking?: Anthropic.ThinkingConfigParam } {\n if (!this.capabilities.reasoning || !reasoning) {\n return {};\n }\n\n // `effort: \"none\"` is an explicit \"run without reasoning\". Anthropic\n // has no reasoning-off effort knob — extended thinking is opt-in — so\n // we simply omit the `thinking` block. (This also short-circuits the\n // `EFFORT_THINKING_BUDGET[\"none\"]` → `undefined` → `NaN` budget path.)\n if (reasoning.effort === \"none\") {\n return {};\n }\n\n if (reasoning.maxTokens === undefined && reasoning.effort === undefined) {\n return {};\n }\n\n const budget = reasoning.maxTokens ?? EFFORT_THINKING_BUDGET[reasoning.effort ?? \"medium\"];\n\n return {\n thinking: {\n type: \"enabled\",\n budget_tokens: Math.max(MIN_THINKING_BUDGET, budget),\n },\n };\n }\n\n /**\n * Spread-friendly tools fragment. Returns an empty object when no\n * tools were supplied so the caller can unconditionally spread it.\n *\n * When `config.promptCaching` is on, marks the LAST tool with\n * `cache_control: ephemeral` — Anthropic caches the whole prefix up\n * to a breakpoint, and tools sit before `system` in that prefix, so\n * one breakpoint on the final tool caches every tool definition.\n * Reads bill at ~0.1x after the first write. The system prompt is\n * deliberately left uncached: it carries per-turn placeholders, so a\n * breakpoint there would pay the ~1.25x write surcharge every turn\n * with no reads.\n */\n private buildTools(tools: ModelCallOptions[\"tools\"]): { tools?: Anthropic.Tool[] } {\n const mapped = toAnthropicTools(tools);\n\n if (!mapped) {\n return {};\n }\n\n if (this.config.promptCaching && mapped.length > 0) {\n const last = mapped.length - 1;\n mapped[last] = { ...mapped[last], cache_control: { type: \"ephemeral\" } };\n }\n\n return { tools: mapped };\n }\n\n /**\n * Translate the neutral `responseSchema` option into Anthropic's\n * native `output_config.format` (JSON-schema structured outputs).\n *\n * Only emitted when the model declares the `structuredOutput`\n * capability AND the schema is a proper root-object JSON Schema —\n * Anthropic rejects non-object roots. When the capability is off\n * (config override) or the schema is non-object, returns an empty\n * object: the agent has already injected a soft schema hint into the\n * system prompt as the fallback, and client-side `validate()` still\n * enforces shape.\n */\n private buildStructuredOutput(responseSchema: Record<string, unknown> | undefined): {\n output_config?: Anthropic.OutputConfig;\n } {\n if (!responseSchema || !this.capabilities.structuredOutput) {\n return {};\n }\n\n if (responseSchema.type !== \"object\" || typeof responseSchema.properties !== \"object\") {\n return {};\n }\n\n return {\n output_config: {\n format: { type: \"json_schema\", schema: responseSchema },\n },\n };\n }\n\n /**\n * Concatenate every `text` content block into the single neutral\n * `content` string. `tool_use` and other block types are ignored\n * here — tool calls are surfaced separately via `extractToolCalls`.\n */\n private extractText(content: Anthropic.ContentBlock[]): string {\n return content\n .filter((block): block is Anthropic.TextBlock => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\");\n }\n\n /**\n * Reshape Anthropic's `tool_use` content blocks into the neutral\n * `ModelToolCallRequest[]`. Returns `undefined` when the model\n * requested no tools so callers can branch on presence.\n */\n private extractToolCalls(\n content: Anthropic.ContentBlock[],\n ): ModelToolCallRequest[] | undefined {\n const toolUses = content.filter(\n (block): block is Anthropic.ToolUseBlock => block.type === \"tool_use\",\n );\n\n if (toolUses.length === 0) {\n return undefined;\n }\n\n const thinkingMetadata = this.buildThinkingMetadata(content);\n\n return toolUses.map((block) => ({\n id: block.id,\n name: block.name,\n input: (block.input ?? {}) as Record<string, unknown>,\n ...thinkingMetadata,\n }));\n }\n\n /**\n * Preserve Anthropic's opaque extended-thinking blocks beside a tool\n * request so the next assistant turn can replay their signatures exactly.\n */\n private buildThinkingMetadata(\n blocks: Iterable<Anthropic.ContentBlock>,\n ): { providerMetadata?: Record<string, unknown> } {\n const thinkingBlocks = Array.from(blocks).filter(\n (block): block is AnthropicThinkingBlock =>\n block.type === \"thinking\" || block.type === \"redacted_thinking\",\n );\n\n if (thinkingBlocks.length === 0) {\n return {};\n }\n\n return { providerMetadata: { anthropic: { thinkingBlocks } } };\n }\n\n /**\n * Normalize Anthropic's `usage` block into the neutral `Usage` shape.\n * Anthropic reports `input_tokens` / `output_tokens` separately with\n * no pre-summed total, so `total` is computed. Cache-read tokens are\n * surfaced as `cachedTokens` and cache-write tokens as\n * `cacheWriteTokens`, each only when non-zero.\n *\n * Note: Anthropic does not report a separate reasoning-token count —\n * extended-thinking tokens are billed inside `output_tokens` — so\n * `Usage.reasoningTokens` is intentionally left unset here. Populating\n * it would double-count against `output`.\n */\n private extractUsage(raw: Anthropic.Usage): Usage {\n const input = raw.input_tokens ?? 0;\n const output = raw.output_tokens ?? 0;\n const cacheRead = raw.cache_read_input_tokens;\n const cacheWrite = raw.cache_creation_input_tokens;\n\n return {\n input,\n output,\n total: input + output,\n ...(cacheRead !== null && cacheRead !== undefined && cacheRead > 0\n ? { cachedTokens: cacheRead }\n : {}),\n ...(cacheWrite !== null && cacheWrite !== undefined && cacheWrite > 0\n ? { cacheWriteTokens: cacheWrite }\n : {}),\n };\n }\n\n /**\n * Wrap a thrown provider error into the typed `AIError` hierarchy and\n * emit the standard error log line before it propagates. Shared by\n * every catch site so the log shape stays identical.\n */\n private logAndWrap(thrown: unknown) {\n const wrapped = wrapAnthropicError(thrown);\n\n this.logger.error(LOG_MODULE, \"error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n return wrapped;\n }\n}\n"],"mappings":";;;;;;;;;;AAmBA,MAAM,aAAa;;;;;;;;AASnB,MAAM,qBAAqB;;;;;;AAO3B,MAAM,sBAAsB;;;;;;;;AAS5B,MAAM,yBAA2E;CAC/E,KAAK;CACL,QAAQ;CACR,MAAM;AACR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,IAAa,iBAAb,MAAqD;CAUnD,AAAO,YACL,QACA,QACA,WAAmB,aACnB;gBANgC;EAOhC,KAAK,SAAS;EACd,KAAK,SAAS;EACd,KAAK,OAAO,OAAO;EACnB,KAAK,WAAW;EAChB,KAAK,UAAU,OAAO;EACtB,KAAK,eAAe;GAClB,kBAAkB,OAAO,oBAAoB;GAC7C,QAAQ,OAAO,UAAU,sBAAsB,OAAO,IAAI;GAK1D,WAAW,OAAO,aAAa;GAO/B,eAAe;GAGf,KAAK;EAIP;CACF;;;;;;;CAQA,MAAa,SAAS,UAAqB,SAAoD;EAC7F,KAAK,OAAO,MAAM,YAAY,WAAW,oCAAoC;GAC3E,OAAO,KAAK;GACZ,cAAc,SAAS;GACvB,WAAW;GACX,WAAW,SAAS,OAAO,UAAU;EACvC,CAAC;EAED,IAAI;EAEJ,IAAI;GACF,WAAW,MAAM,KAAK,OAAO,SAAS,OACpC;IAAE,GAAG,KAAK,YAAY,UAAU,OAAO;IAAG,QAAQ;GAAM,GACxD,SAAS,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,MACjD;EACF,SAAS,QAAQ;GACf,MAAM,KAAK,WAAW,MAAM;EAC9B;EAEA,MAAM,eAAe,cAAc,SAAS,WAAW;EACvD,MAAM,QAAQ,KAAK,aAAa,SAAS,KAAK;EAC9C,MAAM,YAAY,KAAK,iBAAiB,SAAS,OAAO;EAExD,KAAK,OAAO,MAAM,YAAY,YAAY,qCAAqC;GAC7E;GACA;EACF,CAAC;EAED,OAAO;GACL,SAAS,KAAK,YAAY,SAAS,OAAO;GAC1C;GACA;GACA;EACF;CACF;;;;;;;;CASA,OAAc,OACZ,UACA,SACiC;EACjC,KAAK,OAAO,MAAM,YAAY,WAAW,8CAA8C;GACrF,OAAO,KAAK;GACZ,cAAc,SAAS;GACvB,WAAW;GACX,WAAW,SAAS,OAAO,UAAU;EACvC,CAAC;EAED,IAAI;EAEJ,IAAI;GACF,SAAS,MAAM,KAAK,OAAO,SAAS,OAClC;IAAE,GAAG,KAAK,YAAY,UAAU,OAAO;IAAG,QAAQ;GAAK,GACvD,SAAS,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,MACjD;EACF,SAAS,QAAQ;GACf,MAAM,KAAK,WAAW,MAAM;EAC9B;EAEA,IAAI,gBAA+B;EACnC,MAAM,QAAe;GAAE,OAAO;GAAG,QAAQ;GAAG,OAAO;EAAE;EACrD,MAAM,6BAAa,IAAI,IAAwD;EAC/E,MAAM,iCAAiB,IAAI,IAAoC;EAE/D,IAAI;GACF,WAAW,MAAM,SAAS,QAA0D;IAClF,IAAI,MAAM,SAAS,iBAAiB;KAClC,MAAM,QAAQ,MAAM,QAAQ,MAAM,gBAAgB;KAElD,MAAM,YAAY,MAAM,QAAQ,MAAM;KAEtC,IAAI,cAAc,QAAQ,cAAc,UAAa,YAAY,GAC/D,MAAM,eAAe;KAGvB,MAAM,aAAa,MAAM,QAAQ,MAAM;KAEvC,IAAI,eAAe,QAAQ,eAAe,UAAa,aAAa,GAClE,MAAM,mBAAmB;KAG3B;IACF;IAEA,IAAI,MAAM,SAAS,uBAAuB;KACxC,MAAM,QAAQ,MAAM;KAEpB,IAAI,MAAM,SAAS,YACjB,WAAW,IAAI,MAAM,OAAO;MAAE,IAAI,MAAM;MAAI,MAAM,MAAM;MAAM,MAAM;KAAG,CAAC;UACnE,IAAI,MAAM,SAAS,YACxB,eAAe,IAAI,MAAM,OAAO,EAAE,GAAG,MAAM,CAAC;UACvC,IAAI,MAAM,SAAS,qBACxB,eAAe,IAAI,MAAM,OAAO,EAAE,GAAG,MAAM,CAAC;KAG9C;IACF;IAEA,IAAI,MAAM,SAAS,uBAAuB;KACxC,IAAI,MAAM,MAAM,SAAS,cACvB,MAAM;MAAE,MAAM;MAAS,SAAS,MAAM,MAAM;KAAK;UAC5C,IAAI,MAAM,MAAM,SAAS,oBAAoB;MAClD,MAAM,cAAc,WAAW,IAAI,MAAM,KAAK;MAE9C,IAAI,aACF,YAAY,QAAQ,MAAM,MAAM;KAEpC,OAAO,IAAI,MAAM,MAAM,SAAS,kBAAkB;MAChD,MAAM,WAAW,eAAe,IAAI,MAAM,KAAK;MAE/C,IAAI,UAAU,SAAS,YACrB,SAAS,YAAY,MAAM,MAAM;KAErC,OAAO,IAAI,MAAM,MAAM,SAAS,mBAAmB;MACjD,MAAM,WAAW,eAAe,IAAI,MAAM,KAAK;MAE/C,IAAI,UAAU,SAAS,YACrB,SAAS,aAAa,MAAM,MAAM;KAEtC;KAEA;IACF;IAEA,IAAI,MAAM,SAAS,sBAAsB;KACvC,MAAM,cAAc,WAAW,IAAI,MAAM,KAAK;KAE9C,IAAI,aAAa;MACf,MAAM;OACJ,MAAM;OACN,IAAI,YAAY;OAChB,MAAM,YAAY;OAClB,OAAO,cAAuC,YAAY,MAAM,CAAC,CAAC;OAClE,GAAG,KAAK,sBACN,MAAM,KAAK,eAAe,QAAQ,CAAC,CAAC,CACjC,MAAM,CAAC,OAAO,CAAC,WAAW,OAAO,KAAK,CAAC,CACvC,KAAK,GAAG,WAAW,KAAK,CAC7B;MACF;MAEA,WAAW,OAAO,MAAM,KAAK;KAC/B;KAEA;IACF;IAEA,IAAI,MAAM,SAAS,iBAAiB;KAClC,gBAAgB,MAAM,MAAM,eAAe;KAC3C,MAAM,SAAS,MAAM,MAAM,iBAAiB,MAAM;KAKlD,MAAM,YAAY,MAAM,MAAM;KAE9B,IAAI,cAAc,QAAQ,cAAc,UAAa,YAAY,GAC/D,MAAM,eAAe;KAGvB,MAAM,aAAa,MAAM,MAAM;KAE/B,IAAI,eAAe,QAAQ,eAAe,UAAa,aAAa,GAClE,MAAM,mBAAmB;IAE7B;GACF;EACF,SAAS,QAAQ;GACf,MAAM,KAAK,WAAW,MAAM;EAC9B;EAEA,MAAM,QAAQ,MAAM,QAAQ,MAAM;EAElC,MAAM,eAAe,cAAc,aAAa;EAEhD,KAAK,OAAO,MAAM,YAAY,YAAY,+CAA+C;GACvF;GACA;EACF,CAAC;EAED,MAAM;GAAE,MAAM;GAAQ;GAAc;EAAM;CAC5C;;;;;;;;;;;;CAaA,AAAQ,YACN,UACA,SAC2D;EAC3D,MAAM,EAAE,QAAQ,UAAU,sBAAsB,oBAAoB,QAAQ;EAC5E,MAAM,WAAW,KAAK,cAAc,SAAS,SAAS;EACtD,MAAM,cAAc,SAAS,eAAe,KAAK,OAAO;EACxD,MAAM,sBAAsB,SAAS,aAAa,KAAK,OAAO;EAC9D,MAAM,iBACJ,SAAS,UAAU,SAAS,YAAY,SAAS,SAAS,gBAAgB;EAC5E,MAAM,YACJ,wBACC,mBAAmB,SAAY,iBAAiB,qBAAqB;EAExE,IAAI,mBAAmB,UAAa,wBAAwB,UAAa,uBAAuB,gBAC9F,MAAM,IAAI,MACR,wBAAwB,oBAAoB,iDAAiD,eAAe,EAC9G;EAGF,OAAO;GACL,OAAO,KAAK;GACZ,YAAY;GACZ,UAAU;GACV,GAAG,KAAK,YAAY,QAAQ,SAAS,YAAY;GAKjD,GAAI,gBAAgB,UAAa,CAAC,SAAS,WAAW,EAAE,YAAY,IAAI,CAAC;GACzE,GAAG,KAAK,WAAW,SAAS,KAAK;GACjC,GAAG,KAAK,sBAAsB,SAAS,cAAc;GACrD,GAAG;EACL;CACF;;;;;;;;;;;;;CAcA,AAAQ,YACN,QACA,cACkD;EAClD,IAAI,CAAC,QACH,OAAO,CAAC;EAGV,MAAM,cAAc,cAAc,eAAe;EAEjD,IAAI,KAAK,aAAa,iBAAiB,cAAc,GACnD,OAAO,EACL,QAAQ,CAAC;GAAE,MAAM;GAAQ,MAAM;GAAQ,eAAe,EAAE,MAAM,YAAY;EAAE,CAAC,EAC/E;EAGF,OAAO,EAAE,OAAO;CAClB;;;;;;;;;;;;;CAcA,AAAQ,cACN,WAC8C;EAC9C,IAAI,CAAC,KAAK,aAAa,aAAa,CAAC,WACnC,OAAO,CAAC;EAOV,IAAI,UAAU,WAAW,QACvB,OAAO,CAAC;EAGV,IAAI,UAAU,cAAc,UAAa,UAAU,WAAW,QAC5D,OAAO,CAAC;EAGV,MAAM,SAAS,UAAU,aAAa,uBAAuB,UAAU,UAAU;EAEjF,OAAO,EACL,UAAU;GACR,MAAM;GACN,eAAe,KAAK,IAAI,qBAAqB,MAAM;EACrD,EACF;CACF;;;;;;;;;;;;;;CAeA,AAAQ,WAAW,OAAgE;EACjF,MAAM,SAAS,iBAAiB,KAAK;EAErC,IAAI,CAAC,QACH,OAAO,CAAC;EAGV,IAAI,KAAK,OAAO,iBAAiB,OAAO,SAAS,GAAG;GAClD,MAAM,OAAO,OAAO,SAAS;GAC7B,OAAO,QAAQ;IAAE,GAAG,OAAO;IAAO,eAAe,EAAE,MAAM,YAAY;GAAE;EACzE;EAEA,OAAO,EAAE,OAAO,OAAO;CACzB;;;;;;;;;;;;;CAcA,AAAQ,sBAAsB,gBAE5B;EACA,IAAI,CAAC,kBAAkB,CAAC,KAAK,aAAa,kBACxC,OAAO,CAAC;EAGV,IAAI,eAAe,SAAS,YAAY,OAAO,eAAe,eAAe,UAC3E,OAAO,CAAC;EAGV,OAAO,EACL,eAAe,EACb,QAAQ;GAAE,MAAM;GAAe,QAAQ;EAAe,EACxD,EACF;CACF;;;;;;CAOA,AAAQ,YAAY,SAA2C;EAC7D,OAAO,QACJ,QAAQ,UAAwC,MAAM,SAAS,MAAM,CAAC,CACtE,KAAK,UAAU,MAAM,IAAI,CAAC,CAC1B,KAAK,EAAE;CACZ;;;;;;CAOA,AAAQ,iBACN,SACoC;EACpC,MAAM,WAAW,QAAQ,QACtB,UAA2C,MAAM,SAAS,UAC7D;EAEA,IAAI,SAAS,WAAW,GACtB;EAGF,MAAM,mBAAmB,KAAK,sBAAsB,OAAO;EAE3D,OAAO,SAAS,KAAK,WAAW;GAC9B,IAAI,MAAM;GACV,MAAM,MAAM;GACZ,OAAQ,MAAM,SAAS,CAAC;GACxB,GAAG;EACL,EAAE;CACJ;;;;;CAMA,AAAQ,sBACN,QACgD;EAChD,MAAM,iBAAiB,MAAM,KAAK,MAAM,CAAC,CAAC,QACvC,UACC,MAAM,SAAS,cAAc,MAAM,SAAS,mBAChD;EAEA,IAAI,eAAe,WAAW,GAC5B,OAAO,CAAC;EAGV,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,eAAe,EAAE,EAAE;CAC/D;;;;;;;;;;;;;CAcA,AAAQ,aAAa,KAA6B;EAChD,MAAM,QAAQ,IAAI,gBAAgB;EAClC,MAAM,SAAS,IAAI,iBAAiB;EACpC,MAAM,YAAY,IAAI;EACtB,MAAM,aAAa,IAAI;EAEvB,OAAO;GACL;GACA;GACA,OAAO,QAAQ;GACf,GAAI,cAAc,QAAQ,cAAc,UAAa,YAAY,IAC7D,EAAE,cAAc,UAAU,IAC1B,CAAC;GACL,GAAI,eAAe,QAAQ,eAAe,UAAa,aAAa,IAChE,EAAE,kBAAkB,WAAW,IAC/B,CAAC;EACP;CACF;;;;;;CAOA,AAAQ,WAAW,QAAiB;EAClC,MAAM,UAAU,mBAAmB,MAAM;EAEzC,KAAK,OAAO,MAAM,YAAY,SAAS,QAAQ,SAAS;GACtD,MAAM,QAAQ;GACd,SAAS,QAAQ;EACnB,CAAC;EAED,OAAO;CACT;AACF"}
1
+ {"version":3,"file":"model.mjs","names":[],"sources":["../../../../../../ai-anthropic/src/model.ts"],"sourcesContent":["import {\n safeJsonParse,\n type Message,\n type ModelCallOptions,\n type ModelCapabilities,\n type ModelContract,\n type ModelPricing,\n type ModelResponse,\n type ModelStreamChunk,\n type ModelToolCallRequest,\n type ReasoningEffort,\n type Usage,\n} from \"@warlock.js/ai\";\nimport { log, type Logger } from \"@warlock.js/logger\";\nimport type Anthropic from \"@anthropic-ai/sdk\";\nimport type { AnthropicModelConfig } from \"./config.type\";\nimport { inferVisionCapability } from \"./known-vision-models\";\nimport { acceptsTemperature, inferThinkingMode, type AnthropicThinkingMode } from \"./thinking-mode\";\nimport { mapStopReason, toAnthropicMessages, toAnthropicTools, wrapAnthropicError } from \"./utils\";\n\nconst LOG_MODULE = \"ai.anthropic\";\n\n/**\n * Anthropic requires `max_tokens` on every request (unlike OpenAI,\n * where it is optional). When neither the per-call option nor the\n * model config supplies one, fall back to a generous default so a\n * caller who never thought about token caps still gets a complete\n * answer instead of a 400.\n */\nconst DEFAULT_MAX_TOKENS = 4096;\n\n/**\n * Anthropic rejects an extended-thinking budget below 1024 tokens, so\n * any resolved budget is floored at this minimum before it reaches the\n * wire.\n */\nconst MIN_THINKING_BUDGET = 1024;\n\ntype ThinkingEffort = Exclude<ReasoningEffort, \"none\">;\n\n/**\n * Effort used when the caller asks for reasoning with only\n * `reasoning.maxTokens` and no `effort`. `\"medium\"` is what the adapter\n * has always assumed (the budget lookup fell back to\n * `EFFORT_THINKING_BUDGET.medium`), so adaptive models get the same tier.\n */\nconst DEFAULT_THINKING_EFFORT: ThinkingEffort = \"medium\";\n\n/**\n * Map the neutral `ReasoningEffort` level to a thinking token count.\n *\n * - Budget-mode models: sent as `thinking.budget_tokens` when the caller\n * doesn't pass an explicit `reasoning.maxTokens`.\n * - Both modes: reserved on top of `DEFAULT_MAX_TOKENS` when the caller\n * sets no `maxTokens`, because thinking counts toward `max_tokens`\n * (installed SDK `ThinkingConfigParam` docs, `messages.d.ts:1105`).\n * Adaptive models therefore get the same `max_tokens` the budget path\n * has always produced.\n */\nconst EFFORT_THINKING_BUDGET: Record<ThinkingEffort, number> = {\n // Anthropic's adaptive output config has no `minimal`; keep the minimum\n // budget for it in legacy budget mode too.\n minimal: 1024,\n low: 1024,\n medium: 4096,\n high: 12000,\n // Keep the tiers monotonic for older budget-mode models. Explicit\n // reasoning.maxTokens still wins and the existing max_tokens validation\n // remains authoritative.\n xhigh: 24000,\n max: 32000,\n};\n\n/**\n * Map the neutral `ReasoningEffort` level to Anthropic's\n * `output_config.effort` for adaptive-mode models. The installed SDK\n * accepts `low | medium | high | xhigh | max` (`OutputConfig.effort`,\n * `messages.ts:2767`); map every supported value 1:1 and clamp `minimal` to\n * the nearest accepted value, `low`.\n */\nconst EFFORT_TO_OUTPUT_EFFORT: Record<\n ThinkingEffort,\n NonNullable<Anthropic.OutputConfig[\"effort\"]>\n> = {\n minimal: \"low\",\n low: \"low\",\n medium: \"medium\",\n high: \"high\",\n xhigh: \"xhigh\",\n max: \"max\",\n};\n\ntype AnthropicThinkingBlock = Anthropic.ThinkingBlock | Anthropic.RedactedThinkingBlock;\n\n/**\n * Resolved thinking settings for one request.\n *\n * - `thinking`: the wire `thinking` field (absent = send nothing).\n * - `effort`: `output_config.effort`, set for adaptive-mode models only.\n * - `reservedTokens`: thinking tokens to reserve on top of\n * `DEFAULT_MAX_TOKENS` when the caller configured no `maxTokens`.\n */\ntype ThinkingRequest = {\n thinking?: Anthropic.ThinkingConfigParam;\n effort?: Anthropic.OutputConfig[\"effort\"];\n reservedTokens?: number;\n};\n\n/**\n * Anthropic-backed implementation of `ModelContract`.\n *\n * **Role.** The provider-facing bridge between the vendor-neutral\n * `@warlock.js/ai` agent runtime and the official `@anthropic-ai/sdk`\n * Messages API. Agents, workflows, and supervisors never talk to\n * Anthropic directly — they hold a `ModelContract`, and this class is\n * what makes that contract concrete for Claude models.\n *\n * **Responsibility.**\n * - Owns: a long-lived `Anthropic` client + frozen `ModelConfig`\n * (name, temperature, maxTokens) used as defaults for every call.\n * - Owns: translating vendor-neutral `Message[]` / `ToolConfig[]` into\n * Anthropic wire shapes (system hoisting, `tool_use` / `tool_result`\n * blocks) on the way out, and translating Anthropic's content-block\n * response (text, tool calls, stop reason, usage) back into the\n * neutral shapes on the way in.\n * - Does NOT own: dispatching tools, deciding whether to loop, tracking\n * conversation history, or retrying on failure — those are agent\n * concerns. The model is a stateless (per-call) protocol adapter.\n *\n * Because it holds a live client and shared defaults, it is modeled as\n * a class (see §4.2 of code-style.md — \"long-lived state across\n * calls\").\n *\n * @example\n * import Anthropic from \"@anthropic-ai/sdk\";\n * const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });\n * const model = new AnthropicModel(client, { name: \"claude-sonnet-4-6\" });\n *\n * const myAgent = agent({\n * model,\n * systemPrompt: \"You are a helpful assistant.\",\n * tools: [searchTool],\n * });\n *\n * const result = await myAgent.execute(\"Summarize today's news.\");\n */\nexport class AnthropicModel implements ModelContract {\n public readonly name: string;\n public readonly provider: string;\n public readonly capabilities: ModelCapabilities;\n public readonly pricing?: ModelPricing;\n\n private readonly client: Anthropic;\n private readonly config: AnthropicModelConfig;\n private readonly thinkingMode: AnthropicThinkingMode;\n private readonly logger: Logger = log;\n\n public constructor(\n client: Anthropic,\n config: AnthropicModelConfig,\n provider: string = \"anthropic\",\n ) {\n this.client = client;\n this.config = config;\n this.name = config.name;\n this.provider = provider;\n this.pricing = config.pricing;\n // Adaptive vs budget thinking — also decides whether `temperature` is\n // accepted. Explicit config wins over name inference, like `vision`.\n this.thinkingMode = config.thinkingMode ?? inferThinkingMode(config.name);\n this.capabilities = {\n structuredOutput: config.structuredOutput ?? true,\n vision: config.vision ?? inferVisionCapability(config.name),\n // Every modern Claude model accepts Anthropic extended thinking\n // (adaptive or `budget_tokens`, per `thinkingMode`). Advertise the\n // reasoning channel so the agent forwards `reasoning` options;\n // explicit config override wins for proxied/legacy targets.\n reasoning: config.reasoning ?? true,\n // Anthropic prompt caching is caller-driven via `cache_control`\n // breakpoints. The adapter both places those breakpoints (tools\n // when `config.promptCaching`, system when\n // `options.cacheControl.breakpoints`) and reports the read/write\n // accounting (`cachedTokens` / `cacheWriteTokens`), so advertise\n // the capability unconditionally.\n promptCaching: true,\n // The Messages API accepts PDF/document content blocks\n // (`DocumentBlockParam`) on vision-capable Claude models.\n pdf: true,\n // Anthropic has no audio input content block, so `audio` stays\n // absent (treated as false) and the agent rejects audio\n // attachments upfront rather than dropping them at the wire layer.\n };\n }\n\n /**\n * Single-shot completion. Sends the full message list to the Messages\n * endpoint, waits for the terminal response, and reshapes it into a\n * vendor-neutral `ModelResponse`. Per-call `options` override the\n * instance's `ModelConfig` defaults for this call only.\n */\n public async complete(messages: Message[], options?: ModelCallOptions): Promise<ModelResponse> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting call to messages.create\", {\n model: this.name,\n messageCount: messages.length,\n streaming: false,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let response: Anthropic.Message;\n\n try {\n response = await this.client.messages.create(\n { ...this.buildParams(messages, options), stream: false },\n options?.signal ? { signal: options.signal } : undefined,\n );\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n const finishReason = mapStopReason(response.stop_reason);\n const usage = this.extractUsage(response.usage);\n const toolCalls = this.extractToolCalls(response.content);\n\n this.logger.debug(LOG_MODULE, \"response\", \"call to messages.create succeeded\", {\n finishReason,\n usage,\n });\n\n return {\n content: this.extractText(response.content),\n finishReason,\n usage,\n toolCalls,\n };\n }\n\n /**\n * Incremental streaming completion. Yields neutral `ModelStreamChunk`s\n * — `delta` for text tokens, `tool-call` once a `tool_use` block's\n * arguments have fully accumulated, and a terminal `done` carrying the\n * final finish reason + usage totals. Callers consume it with\n * `for await`.\n */\n public async *stream(\n messages: Message[],\n options?: ModelCallOptions,\n ): AsyncIterable<ModelStreamChunk> {\n this.logger.debug(LOG_MODULE, \"request\", \"Starting streaming call to messages.create\", {\n model: this.name,\n messageCount: messages.length,\n streaming: true,\n toolCount: options?.tools?.length ?? 0,\n });\n\n let stream: Awaited<ReturnType<Anthropic[\"messages\"][\"create\"]>>;\n\n try {\n stream = await this.client.messages.create(\n { ...this.buildParams(messages, options), stream: true },\n options?.signal ? { signal: options.signal } : undefined,\n );\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n let rawStopReason: string | null = null;\n const usage: Usage = { input: 0, output: 0, total: 0 };\n const toolBlocks = new Map<number, { id: string; name: string; json: string }>();\n const thinkingBlocks = new Map<number, AnthropicThinkingBlock>();\n\n try {\n for await (const event of stream as AsyncIterable<Anthropic.RawMessageStreamEvent>) {\n if (event.type === \"message_start\") {\n usage.input = event.message.usage.input_tokens ?? 0;\n\n const thinkingTokens = event.message.usage.output_tokens_details?.thinking_tokens;\n\n if (thinkingTokens !== null && thinkingTokens !== undefined) {\n usage.reasoningTokens = thinkingTokens;\n }\n\n const cacheRead = event.message.usage.cache_read_input_tokens;\n\n if (cacheRead !== null && cacheRead !== undefined && cacheRead > 0) {\n usage.cachedTokens = cacheRead;\n }\n\n const cacheWrite = event.message.usage.cache_creation_input_tokens;\n\n if (cacheWrite !== null && cacheWrite !== undefined && cacheWrite > 0) {\n usage.cacheWriteTokens = cacheWrite;\n }\n\n continue;\n }\n\n if (event.type === \"content_block_start\") {\n const block = event.content_block;\n\n if (block.type === \"tool_use\") {\n toolBlocks.set(event.index, { id: block.id, name: block.name, json: \"\" });\n } else if (block.type === \"thinking\") {\n thinkingBlocks.set(event.index, { ...block });\n } else if (block.type === \"redacted_thinking\") {\n thinkingBlocks.set(event.index, { ...block });\n }\n\n continue;\n }\n\n if (event.type === \"content_block_delta\") {\n if (event.delta.type === \"text_delta\") {\n yield { type: \"delta\", content: event.delta.text };\n } else if (event.delta.type === \"input_json_delta\") {\n const accumulator = toolBlocks.get(event.index);\n\n if (accumulator) {\n accumulator.json += event.delta.partial_json;\n }\n } else if (event.delta.type === \"thinking_delta\") {\n const thinking = thinkingBlocks.get(event.index);\n\n if (thinking?.type === \"thinking\") {\n thinking.thinking += event.delta.thinking;\n }\n } else if (event.delta.type === \"signature_delta\") {\n const thinking = thinkingBlocks.get(event.index);\n\n if (thinking?.type === \"thinking\") {\n thinking.signature += event.delta.signature;\n }\n }\n\n continue;\n }\n\n if (event.type === \"content_block_stop\") {\n const accumulator = toolBlocks.get(event.index);\n\n if (accumulator) {\n yield {\n type: \"tool-call\",\n id: accumulator.id,\n name: accumulator.name,\n input: safeJsonParse<Record<string, unknown>>(accumulator.json, {}),\n ...this.buildThinkingMetadata(\n Array.from(thinkingBlocks.entries())\n .sort(([left], [right]) => left - right)\n .map(([, block]) => block),\n ),\n };\n\n toolBlocks.delete(event.index);\n }\n\n continue;\n }\n\n if (event.type === \"message_delta\") {\n rawStopReason = event.delta.stop_reason ?? rawStopReason;\n usage.output = event.usage.output_tokens ?? usage.output;\n\n const thinkingTokens = event.usage.output_tokens_details?.thinking_tokens;\n\n if (thinkingTokens !== null && thinkingTokens !== undefined) {\n usage.reasoningTokens = thinkingTokens;\n }\n\n // `message_delta.usage` carries the cumulative cache counts —\n // prefer them over the `message_start` snapshot when present\n // and non-zero so the terminal `done` reflects the final tally.\n const cacheRead = event.usage.cache_read_input_tokens;\n\n if (cacheRead !== null && cacheRead !== undefined && cacheRead > 0) {\n usage.cachedTokens = cacheRead;\n }\n\n const cacheWrite = event.usage.cache_creation_input_tokens;\n\n if (cacheWrite !== null && cacheWrite !== undefined && cacheWrite > 0) {\n usage.cacheWriteTokens = cacheWrite;\n }\n }\n }\n } catch (thrown) {\n throw this.logAndWrap(thrown);\n }\n\n usage.total = usage.input + usage.output;\n\n const finishReason = mapStopReason(rawStopReason);\n\n this.logger.debug(LOG_MODULE, \"response\", \"Streaming call to messages.create succeeded\", {\n finishReason,\n usage,\n });\n\n yield { type: \"done\", finishReason, usage };\n }\n\n /**\n * Assemble the Anthropic request body shared by `complete()` and\n * `stream()` (each adds its own `stream` literal so the SDK's create\n * overload resolves to the right return type). Hoists the system\n * prompt out of `messages`, resolves `max_tokens` (required by\n * Anthropic) with the documented default, and conditionally attaches\n * temperature, tools, native structured output, extended thinking\n * (`reasoning`), and automatic or explicit prompt-cache control.\n * Temperature is dropped when thinking is enabled, since Anthropic\n * rejects the two together, and always for adaptive-mode models, which\n * reject any explicit temperature (see `acceptsTemperature`).\n */\n private buildParams(\n messages: Message[],\n options: ModelCallOptions | undefined,\n ): Omit<Anthropic.MessageCreateParamsNonStreaming, \"stream\"> {\n const { system, messages: anthropicMessages } = toAnthropicMessages(messages);\n const thinking = this.buildThinking(options?.reasoning);\n const temperature = options?.temperature ?? this.config.temperature;\n const configuredMaxTokens = options?.maxTokens ?? this.config.maxTokens;\n const thinkingBudget =\n thinking.thinking?.type === \"enabled\" ? thinking.thinking.budget_tokens : undefined;\n const maxTokens =\n configuredMaxTokens ??\n (thinking.reservedTokens !== undefined\n ? thinking.reservedTokens + DEFAULT_MAX_TOKENS\n : DEFAULT_MAX_TOKENS);\n // Extended thinking pins sampling to the default temperature —\n // Anthropic 400s when `thinking` is enabled alongside any explicit\n // `temperature`. Models released after Opus 4.6 (adaptive mode) reject\n // an explicit temperature outright (installed SDK\n // `MessageCreateParamsBase.temperature`, `messages.d.ts:2036-2040`).\n // Drop it in both cases rather than letting the request fail.\n const sendTemperature =\n temperature !== undefined && !thinking.thinking && acceptsTemperature(this.thinkingMode);\n\n if (\n thinkingBudget !== undefined &&\n configuredMaxTokens !== undefined &&\n configuredMaxTokens <= thinkingBudget\n ) {\n throw new Error(\n `Anthropic maxTokens (${configuredMaxTokens}) must be greater than thinking budget_tokens (${thinkingBudget})`,\n );\n }\n\n return {\n model: this.name,\n max_tokens: maxTokens,\n messages: anthropicMessages,\n ...this.buildAutomaticCacheControl(options?.cacheControl),\n ...this.buildSystem(system, options?.cacheControl),\n ...(sendTemperature ? { temperature } : {}),\n ...this.buildTools(options?.tools),\n ...this.buildOutputConfig(options?.responseSchema, thinking.effort),\n ...(thinking.thinking ? { thinking: thinking.thinking } : {}),\n };\n }\n\n /**\n * Spread-friendly `system` fragment. Returns an empty object when no\n * system prompt was hoisted out of the messages.\n *\n * When a per-call `cacheControl.breakpoints` hint is present (≥ 1),\n * the system prompt is emitted as a single `TextBlockParam` carrying\n * `cache_control: ephemeral` — the system prompt is the longest stable\n * prefix on a turn, so one breakpoint there lets multi-turn agents\n * read it back at the ~0.1x cache-read rate. Without the hint the\n * system prompt stays a plain string (left uncached) so a one-shot\n * call never pays the ~1.25x cache-write surcharge.\n */\n private buildSystem(\n system: string | undefined,\n cacheControl: ModelCallOptions[\"cacheControl\"],\n ): { system?: string | Anthropic.TextBlockParam[] } {\n if (!system) {\n return {};\n }\n\n const breakpoints = cacheControl?.breakpoints ?? 0;\n\n if (this.capabilities.promptCaching && breakpoints > 0) {\n return {\n system: [{ type: \"text\", text: system, cache_control: this.cacheControl() }],\n };\n }\n\n return { system };\n }\n\n /**\n * Let Anthropic select the last cacheable block when caching is enabled\n * without a caller-selected breakpoint. An explicit breakpoint remains\n * authoritative, so the request-level form is deliberately omitted then.\n */\n private buildAutomaticCacheControl(cacheControl: ModelCallOptions[\"cacheControl\"]): {\n cache_control?: Anthropic.CacheControlEphemeral;\n } {\n const breakpoints = cacheControl?.breakpoints ?? 0;\n\n return this.config.promptCaching && breakpoints <= 0\n ? { cache_control: this.cacheControl() }\n : {};\n }\n\n /** Build a typed cache-control marker, omitting TTL for Anthropic's 5-minute default. */\n private cacheControl(): Anthropic.CacheControlEphemeral {\n return {\n type: \"ephemeral\",\n ...(this.config.promptCacheTtl ? { ttl: this.config.promptCacheTtl } : {}),\n };\n }\n\n /**\n * Translate the neutral `reasoning` option into Anthropic's thinking\n * settings, in the shape this model accepts (`thinkingMode`). Emitted\n * only when the model declares the `reasoning` capability AND a\n * reasoning option is supplied; otherwise returns an empty object.\n *\n * - Adaptive mode (Opus 4.7+, Sonnet 5+, Fable 5+, unknown ids):\n * `thinking: { type: \"adaptive\" }` (installed SDK\n * `ThinkingConfigAdaptive`, `messages.d.ts:1065`) plus\n * `output_config.effort` from the neutral effort, defaulting to\n * `\"medium\"`. `budget_tokens` is never sent — these models 400 on it\n * (claude-api ref). `reasoning.maxTokens` only sizes the default\n * `max_tokens` headroom.\n * - Budget mode (Haiku 4.5, Opus/Sonnet 4.6, older):\n * `thinking: { type: \"enabled\", budget_tokens }`. An explicit\n * `reasoning.maxTokens` wins; otherwise the effort maps to a tiered\n * budget. `budget_tokens` must be ≥ 1024 (`ThinkingConfigEnabled`,\n * `messages.d.ts:1084`), so the budget is floored at that minimum.\n *\n * The neutral `effort: \"none\"` (\"run without reasoning\") maps to no\n * `thinking` field at all in both modes.\n */\n private buildThinking(reasoning: ModelCallOptions[\"reasoning\"]): ThinkingRequest {\n if (!this.capabilities.reasoning || !reasoning) {\n return {};\n }\n\n // `effort: \"none\"` is an explicit \"run without reasoning\". Anthropic\n // has no reasoning-off effort knob — extended thinking is opt-in — so\n // we simply omit the `thinking` block. (This also short-circuits the\n // `EFFORT_THINKING_BUDGET[\"none\"]` → `undefined` → `NaN` budget path.)\n if (reasoning.effort === \"none\") {\n return {};\n }\n\n if (reasoning.maxTokens === undefined && reasoning.effort === undefined) {\n return {};\n }\n\n const effort = reasoning.effort ?? DEFAULT_THINKING_EFFORT;\n const budget = Math.max(\n MIN_THINKING_BUDGET,\n reasoning.maxTokens ?? EFFORT_THINKING_BUDGET[effort],\n );\n\n if (this.thinkingMode === \"adaptive\") {\n return {\n thinking: { type: \"adaptive\" },\n effort: EFFORT_TO_OUTPUT_EFFORT[effort],\n reservedTokens: budget,\n };\n }\n\n return {\n thinking: { type: \"enabled\", budget_tokens: budget },\n reservedTokens: budget,\n };\n }\n\n /**\n * Spread-friendly tools fragment. Returns an empty object when no\n * tools were supplied so the caller can unconditionally spread it.\n *\n * When `config.promptCaching` is on, marks the LAST tool with\n * `cache_control: ephemeral` — Anthropic caches the whole prefix up\n * to a breakpoint, and tools sit before `system` in that prefix, so\n * one breakpoint on the final tool caches every tool definition.\n * Reads bill at ~0.1x after the first write. The system prompt is\n * deliberately left uncached: it carries per-turn placeholders, so a\n * breakpoint there would pay the ~1.25x write surcharge every turn\n * with no reads.\n */\n private buildTools(tools: ModelCallOptions[\"tools\"]): { tools?: Anthropic.Tool[] } {\n const mapped = toAnthropicTools(tools);\n\n if (!mapped) {\n return {};\n }\n\n if (this.config.promptCaching && mapped.length > 0) {\n const last = mapped.length - 1;\n const lastTool = mapped[last];\n\n if (lastTool) {\n mapped[last] = { ...lastTool, cache_control: this.cacheControl() };\n }\n }\n\n return { tools: mapped };\n }\n\n /**\n * Spread-friendly `output_config` fragment carrying both the native\n * structured-output `format` and the adaptive-thinking `effort`, merged\n * into one object (both live under `OutputConfig`, installed SDK\n * `messages.d.ts:796-806`). Returns an empty object when neither applies,\n * so budget-mode requests carry `output_config` only when a schema asks\n * for it.\n */\n private buildOutputConfig(\n responseSchema: Record<string, unknown> | undefined,\n effort: Anthropic.OutputConfig[\"effort\"],\n ): { output_config?: Anthropic.OutputConfig } {\n const format = this.buildStructuredOutputFormat(responseSchema);\n\n if (!format && !effort) {\n return {};\n }\n\n return {\n output_config: {\n ...(format ? { format } : {}),\n ...(effort ? { effort } : {}),\n },\n };\n }\n\n /**\n * Translate the neutral `responseSchema` option into Anthropic's\n * native `output_config.format` (JSON-schema structured outputs).\n *\n * Only returned when the model declares the `structuredOutput`\n * capability AND the schema is a proper root-object JSON Schema —\n * Anthropic rejects non-object roots. When the capability is off\n * (config override) or the schema is non-object, returns `undefined`:\n * the agent has already injected a soft schema hint into the system\n * prompt as the fallback, and client-side `validate()` still enforces\n * shape.\n */\n private buildStructuredOutputFormat(\n responseSchema: Record<string, unknown> | undefined,\n ): Anthropic.JSONOutputFormat | undefined {\n if (!responseSchema || !this.capabilities.structuredOutput) {\n return undefined;\n }\n\n if (responseSchema.type !== \"object\" || typeof responseSchema.properties !== \"object\") {\n return undefined;\n }\n\n return { type: \"json_schema\", schema: responseSchema };\n }\n\n /**\n * Concatenate every `text` content block into the single neutral\n * `content` string. `tool_use` and other block types are ignored\n * here — tool calls are surfaced separately via `extractToolCalls`.\n */\n private extractText(content: Anthropic.ContentBlock[]): string {\n return content\n .filter((block): block is Anthropic.TextBlock => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\");\n }\n\n /**\n * Reshape Anthropic's `tool_use` content blocks into the neutral\n * `ModelToolCallRequest[]`. Returns `undefined` when the model\n * requested no tools so callers can branch on presence.\n */\n private extractToolCalls(content: Anthropic.ContentBlock[]): ModelToolCallRequest[] | undefined {\n const toolUses = content.filter(\n (block): block is Anthropic.ToolUseBlock => block.type === \"tool_use\",\n );\n\n if (toolUses.length === 0) {\n return undefined;\n }\n\n const thinkingMetadata = this.buildThinkingMetadata(content);\n\n return toolUses.map((block) => ({\n id: block.id,\n name: block.name,\n input: (block.input ?? {}) as Record<string, unknown>,\n ...thinkingMetadata,\n }));\n }\n\n /**\n * Preserve Anthropic's opaque extended-thinking blocks beside a tool\n * request so the next assistant turn can replay their signatures exactly.\n */\n private buildThinkingMetadata(blocks: Iterable<Anthropic.ContentBlock>): {\n providerMetadata?: Record<string, unknown>;\n } {\n const thinkingBlocks = Array.from(blocks).filter(\n (block): block is AnthropicThinkingBlock =>\n block.type === \"thinking\" || block.type === \"redacted_thinking\",\n );\n\n if (thinkingBlocks.length === 0) {\n return {};\n }\n\n return { providerMetadata: { anthropic: { thinkingBlocks } } };\n }\n\n /**\n * Normalize Anthropic's `usage` block into the neutral `Usage` shape.\n * Anthropic reports `input_tokens` / `output_tokens` separately with\n * no pre-summed total, so `total` is computed. Cache-read tokens are\n * surfaced as `cachedTokens` and cache-write tokens as\n * `cacheWriteTokens`, each only when non-zero. Anthropic also reports\n * `output_tokens_details.thinking_tokens` as the reasoning subset of\n * its inclusive `output_tokens`; it is surfaced as `reasoningTokens`\n * when present, without changing the billing total.\n */\n private extractUsage(raw: Anthropic.Usage): Usage {\n const input = raw.input_tokens ?? 0;\n const output = raw.output_tokens ?? 0;\n const cacheRead = raw.cache_read_input_tokens;\n const cacheWrite = raw.cache_creation_input_tokens;\n const thinkingTokens = raw.output_tokens_details?.thinking_tokens;\n\n return {\n input,\n output,\n total: input + output,\n ...(cacheRead !== null && cacheRead !== undefined && cacheRead > 0\n ? { cachedTokens: cacheRead }\n : {}),\n ...(cacheWrite !== null && cacheWrite !== undefined && cacheWrite > 0\n ? { cacheWriteTokens: cacheWrite }\n : {}),\n ...(thinkingTokens !== null && thinkingTokens !== undefined\n ? { reasoningTokens: thinkingTokens }\n : {}),\n };\n }\n\n /**\n * Wrap a thrown provider error into the typed `AIError` hierarchy and\n * emit the standard error log line before it propagates. Shared by\n * every catch site so the log shape stays identical.\n */\n private logAndWrap(thrown: unknown) {\n const wrapped = wrapAnthropicError(thrown);\n\n this.logger.error(LOG_MODULE, \"error\", wrapped.message, {\n code: wrapped.code,\n context: wrapped.context,\n });\n\n return wrapped;\n }\n}\n"],"mappings":";;;;;;;;;;;AAoBA,MAAM,aAAa;;;;;;;;AASnB,MAAM,qBAAqB;;;;;;AAO3B,MAAM,sBAAsB;;;;;;;AAU5B,MAAM,0BAA0C;;;;;;;;;;;;AAahD,MAAM,yBAAyD;CAG7D,SAAS;CACT,KAAK;CACL,QAAQ;CACR,MAAM;CAIN,OAAO;CACP,KAAK;AACP;;;;;;;;AASA,MAAM,0BAGF;CACF,SAAS;CACT,KAAK;CACL,QAAQ;CACR,MAAM;CACN,OAAO;CACP,KAAK;AACP;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwDA,IAAa,iBAAb,MAAqD;CAWnD,AAAO,YACL,QACA,QACA,WAAmB,aACnB;gBANgC;EAOhC,KAAK,SAAS;EACd,KAAK,SAAS;EACd,KAAK,OAAO,OAAO;EACnB,KAAK,WAAW;EAChB,KAAK,UAAU,OAAO;EAGtB,KAAK,eAAe,OAAO,gBAAgB,kBAAkB,OAAO,IAAI;EACxE,KAAK,eAAe;GAClB,kBAAkB,OAAO,oBAAoB;GAC7C,QAAQ,OAAO,UAAU,sBAAsB,OAAO,IAAI;GAK1D,WAAW,OAAO,aAAa;GAO/B,eAAe;GAGf,KAAK;EAIP;CACF;;;;;;;CAQA,MAAa,SAAS,UAAqB,SAAoD;EAC7F,KAAK,OAAO,MAAM,YAAY,WAAW,oCAAoC;GAC3E,OAAO,KAAK;GACZ,cAAc,SAAS;GACvB,WAAW;GACX,WAAW,SAAS,OAAO,UAAU;EACvC,CAAC;EAED,IAAI;EAEJ,IAAI;GACF,WAAW,MAAM,KAAK,OAAO,SAAS,OACpC;IAAE,GAAG,KAAK,YAAY,UAAU,OAAO;IAAG,QAAQ;GAAM,GACxD,SAAS,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,MACjD;EACF,SAAS,QAAQ;GACf,MAAM,KAAK,WAAW,MAAM;EAC9B;EAEA,MAAM,eAAe,cAAc,SAAS,WAAW;EACvD,MAAM,QAAQ,KAAK,aAAa,SAAS,KAAK;EAC9C,MAAM,YAAY,KAAK,iBAAiB,SAAS,OAAO;EAExD,KAAK,OAAO,MAAM,YAAY,YAAY,qCAAqC;GAC7E;GACA;EACF,CAAC;EAED,OAAO;GACL,SAAS,KAAK,YAAY,SAAS,OAAO;GAC1C;GACA;GACA;EACF;CACF;;;;;;;;CASA,OAAc,OACZ,UACA,SACiC;EACjC,KAAK,OAAO,MAAM,YAAY,WAAW,8CAA8C;GACrF,OAAO,KAAK;GACZ,cAAc,SAAS;GACvB,WAAW;GACX,WAAW,SAAS,OAAO,UAAU;EACvC,CAAC;EAED,IAAI;EAEJ,IAAI;GACF,SAAS,MAAM,KAAK,OAAO,SAAS,OAClC;IAAE,GAAG,KAAK,YAAY,UAAU,OAAO;IAAG,QAAQ;GAAK,GACvD,SAAS,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,MACjD;EACF,SAAS,QAAQ;GACf,MAAM,KAAK,WAAW,MAAM;EAC9B;EAEA,IAAI,gBAA+B;EACnC,MAAM,QAAe;GAAE,OAAO;GAAG,QAAQ;GAAG,OAAO;EAAE;EACrD,MAAM,6BAAa,IAAI,IAAwD;EAC/E,MAAM,iCAAiB,IAAI,IAAoC;EAE/D,IAAI;GACF,WAAW,MAAM,SAAS,QAA0D;IAClF,IAAI,MAAM,SAAS,iBAAiB;KAClC,MAAM,QAAQ,MAAM,QAAQ,MAAM,gBAAgB;KAElD,MAAM,iBAAiB,MAAM,QAAQ,MAAM,uBAAuB;KAElE,IAAI,mBAAmB,QAAQ,mBAAmB,QAChD,MAAM,kBAAkB;KAG1B,MAAM,YAAY,MAAM,QAAQ,MAAM;KAEtC,IAAI,cAAc,QAAQ,cAAc,UAAa,YAAY,GAC/D,MAAM,eAAe;KAGvB,MAAM,aAAa,MAAM,QAAQ,MAAM;KAEvC,IAAI,eAAe,QAAQ,eAAe,UAAa,aAAa,GAClE,MAAM,mBAAmB;KAG3B;IACF;IAEA,IAAI,MAAM,SAAS,uBAAuB;KACxC,MAAM,QAAQ,MAAM;KAEpB,IAAI,MAAM,SAAS,YACjB,WAAW,IAAI,MAAM,OAAO;MAAE,IAAI,MAAM;MAAI,MAAM,MAAM;MAAM,MAAM;KAAG,CAAC;UACnE,IAAI,MAAM,SAAS,YACxB,eAAe,IAAI,MAAM,OAAO,EAAE,GAAG,MAAM,CAAC;UACvC,IAAI,MAAM,SAAS,qBACxB,eAAe,IAAI,MAAM,OAAO,EAAE,GAAG,MAAM,CAAC;KAG9C;IACF;IAEA,IAAI,MAAM,SAAS,uBAAuB;KACxC,IAAI,MAAM,MAAM,SAAS,cACvB,MAAM;MAAE,MAAM;MAAS,SAAS,MAAM,MAAM;KAAK;UAC5C,IAAI,MAAM,MAAM,SAAS,oBAAoB;MAClD,MAAM,cAAc,WAAW,IAAI,MAAM,KAAK;MAE9C,IAAI,aACF,YAAY,QAAQ,MAAM,MAAM;KAEpC,OAAO,IAAI,MAAM,MAAM,SAAS,kBAAkB;MAChD,MAAM,WAAW,eAAe,IAAI,MAAM,KAAK;MAE/C,IAAI,UAAU,SAAS,YACrB,SAAS,YAAY,MAAM,MAAM;KAErC,OAAO,IAAI,MAAM,MAAM,SAAS,mBAAmB;MACjD,MAAM,WAAW,eAAe,IAAI,MAAM,KAAK;MAE/C,IAAI,UAAU,SAAS,YACrB,SAAS,aAAa,MAAM,MAAM;KAEtC;KAEA;IACF;IAEA,IAAI,MAAM,SAAS,sBAAsB;KACvC,MAAM,cAAc,WAAW,IAAI,MAAM,KAAK;KAE9C,IAAI,aAAa;MACf,MAAM;OACJ,MAAM;OACN,IAAI,YAAY;OAChB,MAAM,YAAY;OAClB,OAAO,cAAuC,YAAY,MAAM,CAAC,CAAC;OAClE,GAAG,KAAK,sBACN,MAAM,KAAK,eAAe,QAAQ,CAAC,CAAC,CACjC,MAAM,CAAC,OAAO,CAAC,WAAW,OAAO,KAAK,CAAC,CACvC,KAAK,GAAG,WAAW,KAAK,CAC7B;MACF;MAEA,WAAW,OAAO,MAAM,KAAK;KAC/B;KAEA;IACF;IAEA,IAAI,MAAM,SAAS,iBAAiB;KAClC,gBAAgB,MAAM,MAAM,eAAe;KAC3C,MAAM,SAAS,MAAM,MAAM,iBAAiB,MAAM;KAElD,MAAM,iBAAiB,MAAM,MAAM,uBAAuB;KAE1D,IAAI,mBAAmB,QAAQ,mBAAmB,QAChD,MAAM,kBAAkB;KAM1B,MAAM,YAAY,MAAM,MAAM;KAE9B,IAAI,cAAc,QAAQ,cAAc,UAAa,YAAY,GAC/D,MAAM,eAAe;KAGvB,MAAM,aAAa,MAAM,MAAM;KAE/B,IAAI,eAAe,QAAQ,eAAe,UAAa,aAAa,GAClE,MAAM,mBAAmB;IAE7B;GACF;EACF,SAAS,QAAQ;GACf,MAAM,KAAK,WAAW,MAAM;EAC9B;EAEA,MAAM,QAAQ,MAAM,QAAQ,MAAM;EAElC,MAAM,eAAe,cAAc,aAAa;EAEhD,KAAK,OAAO,MAAM,YAAY,YAAY,+CAA+C;GACvF;GACA;EACF,CAAC;EAED,MAAM;GAAE,MAAM;GAAQ;GAAc;EAAM;CAC5C;;;;;;;;;;;;;CAcA,AAAQ,YACN,UACA,SAC2D;EAC3D,MAAM,EAAE,QAAQ,UAAU,sBAAsB,oBAAoB,QAAQ;EAC5E,MAAM,WAAW,KAAK,cAAc,SAAS,SAAS;EACtD,MAAM,cAAc,SAAS,eAAe,KAAK,OAAO;EACxD,MAAM,sBAAsB,SAAS,aAAa,KAAK,OAAO;EAC9D,MAAM,iBACJ,SAAS,UAAU,SAAS,YAAY,SAAS,SAAS,gBAAgB;EAC5E,MAAM,YACJ,wBACC,SAAS,mBAAmB,SACzB,SAAS,iBAAiB,qBAC1B;EAON,MAAM,kBACJ,gBAAgB,UAAa,CAAC,SAAS,YAAY,mBAAmB,KAAK,YAAY;EAEzF,IACE,mBAAmB,UACnB,wBAAwB,UACxB,uBAAuB,gBAEvB,MAAM,IAAI,MACR,wBAAwB,oBAAoB,iDAAiD,eAAe,EAC9G;EAGF,OAAO;GACL,OAAO,KAAK;GACZ,YAAY;GACZ,UAAU;GACV,GAAG,KAAK,2BAA2B,SAAS,YAAY;GACxD,GAAG,KAAK,YAAY,QAAQ,SAAS,YAAY;GACjD,GAAI,kBAAkB,EAAE,YAAY,IAAI,CAAC;GACzC,GAAG,KAAK,WAAW,SAAS,KAAK;GACjC,GAAG,KAAK,kBAAkB,SAAS,gBAAgB,SAAS,MAAM;GAClE,GAAI,SAAS,WAAW,EAAE,UAAU,SAAS,SAAS,IAAI,CAAC;EAC7D;CACF;;;;;;;;;;;;;CAcA,AAAQ,YACN,QACA,cACkD;EAClD,IAAI,CAAC,QACH,OAAO,CAAC;EAGV,MAAM,cAAc,cAAc,eAAe;EAEjD,IAAI,KAAK,aAAa,iBAAiB,cAAc,GACnD,OAAO,EACL,QAAQ,CAAC;GAAE,MAAM;GAAQ,MAAM;GAAQ,eAAe,KAAK,aAAa;EAAE,CAAC,EAC7E;EAGF,OAAO,EAAE,OAAO;CAClB;;;;;;CAOA,AAAQ,2BAA2B,cAEjC;EACA,MAAM,cAAc,cAAc,eAAe;EAEjD,OAAO,KAAK,OAAO,iBAAiB,eAAe,IAC/C,EAAE,eAAe,KAAK,aAAa,EAAE,IACrC,CAAC;CACP;;CAGA,AAAQ,eAAgD;EACtD,OAAO;GACL,MAAM;GACN,GAAI,KAAK,OAAO,iBAAiB,EAAE,KAAK,KAAK,OAAO,eAAe,IAAI,CAAC;EAC1E;CACF;;;;;;;;;;;;;;;;;;;;;;;CAwBA,AAAQ,cAAc,WAA2D;EAC/E,IAAI,CAAC,KAAK,aAAa,aAAa,CAAC,WACnC,OAAO,CAAC;EAOV,IAAI,UAAU,WAAW,QACvB,OAAO,CAAC;EAGV,IAAI,UAAU,cAAc,UAAa,UAAU,WAAW,QAC5D,OAAO,CAAC;EAGV,MAAM,SAAS,UAAU,UAAU;EACnC,MAAM,SAAS,KAAK,IAClB,qBACA,UAAU,aAAa,uBAAuB,OAChD;EAEA,IAAI,KAAK,iBAAiB,YACxB,OAAO;GACL,UAAU,EAAE,MAAM,WAAW;GAC7B,QAAQ,wBAAwB;GAChC,gBAAgB;EAClB;EAGF,OAAO;GACL,UAAU;IAAE,MAAM;IAAW,eAAe;GAAO;GACnD,gBAAgB;EAClB;CACF;;;;;;;;;;;;;;CAeA,AAAQ,WAAW,OAAgE;EACjF,MAAM,SAAS,iBAAiB,KAAK;EAErC,IAAI,CAAC,QACH,OAAO,CAAC;EAGV,IAAI,KAAK,OAAO,iBAAiB,OAAO,SAAS,GAAG;GAClD,MAAM,OAAO,OAAO,SAAS;GAC7B,MAAM,WAAW,OAAO;GAExB,IAAI,UACF,OAAO,QAAQ;IAAE,GAAG;IAAU,eAAe,KAAK,aAAa;GAAE;EAErE;EAEA,OAAO,EAAE,OAAO,OAAO;CACzB;;;;;;;;;CAUA,AAAQ,kBACN,gBACA,QAC4C;EAC5C,MAAM,SAAS,KAAK,4BAA4B,cAAc;EAE9D,IAAI,CAAC,UAAU,CAAC,QACd,OAAO,CAAC;EAGV,OAAO,EACL,eAAe;GACb,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;GAC3B,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;EAC7B,EACF;CACF;;;;;;;;;;;;;CAcA,AAAQ,4BACN,gBACwC;EACxC,IAAI,CAAC,kBAAkB,CAAC,KAAK,aAAa,kBACxC;EAGF,IAAI,eAAe,SAAS,YAAY,OAAO,eAAe,eAAe,UAC3E;EAGF,OAAO;GAAE,MAAM;GAAe,QAAQ;EAAe;CACvD;;;;;;CAOA,AAAQ,YAAY,SAA2C;EAC7D,OAAO,QACJ,QAAQ,UAAwC,MAAM,SAAS,MAAM,CAAC,CACtE,KAAK,UAAU,MAAM,IAAI,CAAC,CAC1B,KAAK,EAAE;CACZ;;;;;;CAOA,AAAQ,iBAAiB,SAAuE;EAC9F,MAAM,WAAW,QAAQ,QACtB,UAA2C,MAAM,SAAS,UAC7D;EAEA,IAAI,SAAS,WAAW,GACtB;EAGF,MAAM,mBAAmB,KAAK,sBAAsB,OAAO;EAE3D,OAAO,SAAS,KAAK,WAAW;GAC9B,IAAI,MAAM;GACV,MAAM,MAAM;GACZ,OAAQ,MAAM,SAAS,CAAC;GACxB,GAAG;EACL,EAAE;CACJ;;;;;CAMA,AAAQ,sBAAsB,QAE5B;EACA,MAAM,iBAAiB,MAAM,KAAK,MAAM,CAAC,CAAC,QACvC,UACC,MAAM,SAAS,cAAc,MAAM,SAAS,mBAChD;EAEA,IAAI,eAAe,WAAW,GAC5B,OAAO,CAAC;EAGV,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,eAAe,EAAE,EAAE;CAC/D;;;;;;;;;;;CAYA,AAAQ,aAAa,KAA6B;EAChD,MAAM,QAAQ,IAAI,gBAAgB;EAClC,MAAM,SAAS,IAAI,iBAAiB;EACpC,MAAM,YAAY,IAAI;EACtB,MAAM,aAAa,IAAI;EACvB,MAAM,iBAAiB,IAAI,uBAAuB;EAElD,OAAO;GACL;GACA;GACA,OAAO,QAAQ;GACf,GAAI,cAAc,QAAQ,cAAc,UAAa,YAAY,IAC7D,EAAE,cAAc,UAAU,IAC1B,CAAC;GACL,GAAI,eAAe,QAAQ,eAAe,UAAa,aAAa,IAChE,EAAE,kBAAkB,WAAW,IAC/B,CAAC;GACL,GAAI,mBAAmB,QAAQ,mBAAmB,SAC9C,EAAE,iBAAiB,eAAe,IAClC,CAAC;EACP;CACF;;;;;;CAOA,AAAQ,WAAW,QAAiB;EAClC,MAAM,UAAU,mBAAmB,MAAM;EAEzC,KAAK,OAAO,MAAM,YAAY,SAAS,QAAQ,SAAS;GACtD,MAAM,QAAQ;GACd,SAAS,QAAQ;EACnB,CAAC;EAED,OAAO;CACT;AACF"}
@@ -0,0 +1,17 @@
1
+ //#region ../ai-anthropic/src/thinking-mode.d.ts
2
+ /**
3
+ * How a Claude model accepts extended thinking on the Messages API.
4
+ *
5
+ * - `"adaptive"` — `thinking: { type: "adaptive" }`, with depth steered by
6
+ * `output_config.effort`. Sending `budget_tokens` to these models is a
7
+ * 400. Installed SDK: `ThinkingConfigAdaptive`
8
+ * (`@anthropic-ai/sdk/resources/messages/messages.d.ts:1065`) and
9
+ * `OutputConfig.effort` (`messages.d.ts:796-800`).
10
+ * - `"budget"` — the legacy `thinking: { type: "enabled", budget_tokens }`
11
+ * (`ThinkingConfigEnabled`, `messages.d.ts:1078-1090`). Required on
12
+ * Haiku 4.5 and older models.
13
+ */
14
+ type AnthropicThinkingMode = "adaptive" | "budget";
15
+ //#endregion
16
+ export { AnthropicThinkingMode };
17
+ //# sourceMappingURL=thinking-mode.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thinking-mode.d.mts","names":[],"sources":["../../../../../../ai-anthropic/src/thinking-mode.ts"],"mappings":";;AAYA;;;;AAAiC;;;;;;;KAArB,qBAAA"}
@@ -0,0 +1,81 @@
1
+ //#region ../ai-anthropic/src/thinking-mode.ts
2
+ /**
3
+ * Family prefixes that are budget-mode in their entirety — plain
4
+ * `startsWith` match (dated and `-latest` variants included).
5
+ */
6
+ const BUDGET_FAMILY_PREFIXES = [
7
+ "claude-3",
8
+ "claude-2",
9
+ "claude-instant",
10
+ "claude-haiku-4"
11
+ ];
12
+ /**
13
+ * Individual pre-4.7 releases in the `claude-<tier>-4-*` naming. Matched
14
+ * as a release root: the id must equal the root or continue with a
15
+ * separator that is not a new minor version number, so `claude-opus-4`
16
+ * matches `claude-opus-4-20250514` but NOT `claude-opus-4-7`. Ids from the
17
+ * installed SDK `Model` union (`messages.d.ts:795`) plus the 4.6 pair.
18
+ */
19
+ const BUDGET_RELEASE_ROOTS = [
20
+ "claude-opus-4",
21
+ "claude-opus-4-0",
22
+ "claude-opus-4-1",
23
+ "claude-opus-4-5",
24
+ "claude-opus-4-6",
25
+ "claude-sonnet-4",
26
+ "claude-sonnet-4-0",
27
+ "claude-sonnet-4-5",
28
+ "claude-sonnet-4-6"
29
+ ];
30
+ /**
31
+ * A remainder that starts a new minor version (`-7`, `-10`) — used to
32
+ * stop a release root from swallowing a later release of the same line.
33
+ * Dates (`-20250514`) and tags (`-latest`, `-v1`) are not minor versions.
34
+ */
35
+ const NEXT_MINOR_VERSION = /^-\d{1,2}(?!\d)/;
36
+ function matchesReleaseRoot(name, root) {
37
+ if (!name.startsWith(root)) return false;
38
+ const rest = name.slice(root.length);
39
+ if (rest === "") return true;
40
+ return (rest.startsWith("-") || rest.startsWith("@")) && !NEXT_MINOR_VERSION.test(rest);
41
+ }
42
+ /**
43
+ * Infer the thinking mode for a Claude model id. Budget for Haiku 4.5, the
44
+ * 4.6 pair, and every older family; adaptive for everything else,
45
+ * including unknown and future ids. Explicit per-model config
46
+ * (`AnthropicModelConfig.thinkingMode`) always wins over this inference.
47
+ *
48
+ * @example
49
+ * inferThinkingMode("claude-opus-5-5"); // → "adaptive"
50
+ * inferThinkingMode("claude-haiku-4-5"); // → "budget"
51
+ * inferThinkingMode("claude-opus-9"); // → "adaptive" (unknown → current API direction)
52
+ */
53
+ function inferThinkingMode(modelName) {
54
+ const normalized = modelName.toLowerCase();
55
+ if (BUDGET_FAMILY_PREFIXES.some((prefix) => normalized.startsWith(prefix))) return "budget";
56
+ if (BUDGET_RELEASE_ROOTS.some((root) => matchesReleaseRoot(normalized, root))) return "budget";
57
+ return "adaptive";
58
+ }
59
+ /**
60
+ * Whether a model of this thinking mode accepts an explicit `temperature`.
61
+ *
62
+ * Installed SDK, `MessageCreateParamsBase.temperature`
63
+ * (`messages.d.ts:2036-2040`): "Models released after Claude Opus 4.6 do
64
+ * not support setting temperature. A value of 1.0 will be accepted for
65
+ * backwards compatibility, all other values will be rejected with a 400
66
+ * error." The claude-api ref's per-model table agrees: sampling is
67
+ * "Removed - 400" on Fable 5/5.1, Opus 5.5/5/4.8/4.7 and Sonnet 5, and
68
+ * "Non-default values - 400" on Sonnet 5.5; "Allowed" on Opus 4.6,
69
+ * Sonnet 4.6, Haiku 4.5 and older.
70
+ *
71
+ * That split is exactly the adaptive/budget split in the table above
72
+ * (adaptive = released after Opus 4.6; budget = Opus 4.6 and earlier), so
73
+ * the thinking mode doubles as the sampling-parameter classification.
74
+ */
75
+ function acceptsTemperature(mode) {
76
+ return mode === "budget";
77
+ }
78
+
79
+ //#endregion
80
+ export { acceptsTemperature, inferThinkingMode };
81
+ //# sourceMappingURL=thinking-mode.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thinking-mode.mjs","names":[],"sources":["../../../../../../ai-anthropic/src/thinking-mode.ts"],"sourcesContent":["/**\n * How a Claude model accepts extended thinking on the Messages API.\n *\n * - `\"adaptive\"` — `thinking: { type: \"adaptive\" }`, with depth steered by\n * `output_config.effort`. Sending `budget_tokens` to these models is a\n * 400. Installed SDK: `ThinkingConfigAdaptive`\n * (`@anthropic-ai/sdk/resources/messages/messages.d.ts:1065`) and\n * `OutputConfig.effort` (`messages.d.ts:796-800`).\n * - `\"budget\"` — the legacy `thinking: { type: \"enabled\", budget_tokens }`\n * (`ThinkingConfigEnabled`, `messages.d.ts:1078-1090`). Required on\n * Haiku 4.5 and older models.\n */\nexport type AnthropicThinkingMode = \"adaptive\" | \"budget\";\n\n/*\n * Model-family table (source: claude-api ref, \"Thinking & Effort\" per-model\n * table, cached 2026-09-25; summarised in `releases/5.26-ai-sdk-review.md` §A):\n *\n * | Model ids | Mode | `budget_tokens` | `temperature` |\n * |----------------------------------------------|------------|-------------------------|--------------------|\n * | claude-fable-5, claude-fable-5-1 (+ mythos) | adaptive | removed (400) | removed (400) |\n * | claude-opus-5-5, claude-opus-5 | adaptive | removed (400) | removed (400) |\n * | claude-opus-4-8, claude-opus-4-7 | adaptive | removed (400) | removed (400) |\n * | claude-sonnet-5-5, claude-sonnet-5 | adaptive | removed (400) | 400 (5.5: non-1.0) |\n * | claude-opus-4-6, claude-sonnet-4-6 | budget (*) | deprecated, still works | allowed |\n * | claude-haiku-4-5 and every older model | budget | required for thinking | allowed |\n * | anything not listed (future / unknown ids) | adaptive | — | — |\n *\n * (*) The 4.6 pair accepts both shapes; the claude-api ref recommends\n * adaptive there but keeps `budget_tokens` functional as a transitional\n * path. It stays on `budget` so 4.6 callers see no behaviour change (their\n * `reasoning.maxTokens` is still honoured as a hard budget) and so that\n * the `budget` set is exactly the set that still accepts `temperature`\n * (see `acceptsTemperature`). Opt 4.6 into adaptive with\n * `anthropic.model({ name, thinkingMode: \"adaptive\" })`.\n *\n * Unknown ids default to adaptive because that is the current API\n * direction: every model released after Opus 4.6 rejects `budget_tokens`.\n */\n\n/**\n * Family prefixes that are budget-mode in their entirety — plain\n * `startsWith` match (dated and `-latest` variants included).\n */\nconst BUDGET_FAMILY_PREFIXES = [\n // Claude 3 / 3.5 / 3.7 legacy naming (claude-3-haiku-…, claude-3-5-sonnet-…).\n \"claude-3\",\n // Pre-3 text models: no thinking support at all, never adaptive.\n \"claude-2\",\n \"claude-instant\",\n // Haiku 4.x: Haiku 4.5 still requires budget_tokens (claude-api ref).\n \"claude-haiku-4\",\n];\n\n/**\n * Individual pre-4.7 releases in the `claude-<tier>-4-*` naming. Matched\n * as a release root: the id must equal the root or continue with a\n * separator that is not a new minor version number, so `claude-opus-4`\n * matches `claude-opus-4-20250514` but NOT `claude-opus-4-7`. Ids from the\n * installed SDK `Model` union (`messages.d.ts:795`) plus the 4.6 pair.\n */\nconst BUDGET_RELEASE_ROOTS = [\n \"claude-opus-4\",\n \"claude-opus-4-0\",\n \"claude-opus-4-1\",\n \"claude-opus-4-5\",\n \"claude-opus-4-6\",\n \"claude-sonnet-4\",\n \"claude-sonnet-4-0\",\n \"claude-sonnet-4-5\",\n \"claude-sonnet-4-6\",\n];\n\n/**\n * A remainder that starts a new minor version (`-7`, `-10`) — used to\n * stop a release root from swallowing a later release of the same line.\n * Dates (`-20250514`) and tags (`-latest`, `-v1`) are not minor versions.\n */\nconst NEXT_MINOR_VERSION = /^-\\d{1,2}(?!\\d)/;\n\nfunction matchesReleaseRoot(name: string, root: string): boolean {\n if (!name.startsWith(root)) {\n return false;\n }\n\n const rest = name.slice(root.length);\n\n if (rest === \"\") {\n return true;\n }\n\n return (rest.startsWith(\"-\") || rest.startsWith(\"@\")) && !NEXT_MINOR_VERSION.test(rest);\n}\n\n/**\n * Infer the thinking mode for a Claude model id. Budget for Haiku 4.5, the\n * 4.6 pair, and every older family; adaptive for everything else,\n * including unknown and future ids. Explicit per-model config\n * (`AnthropicModelConfig.thinkingMode`) always wins over this inference.\n *\n * @example\n * inferThinkingMode(\"claude-opus-5-5\"); // → \"adaptive\"\n * inferThinkingMode(\"claude-haiku-4-5\"); // → \"budget\"\n * inferThinkingMode(\"claude-opus-9\"); // → \"adaptive\" (unknown → current API direction)\n */\nexport function inferThinkingMode(modelName: string): AnthropicThinkingMode {\n const normalized = modelName.toLowerCase();\n\n if (BUDGET_FAMILY_PREFIXES.some((prefix) => normalized.startsWith(prefix))) {\n return \"budget\";\n }\n\n if (BUDGET_RELEASE_ROOTS.some((root) => matchesReleaseRoot(normalized, root))) {\n return \"budget\";\n }\n\n return \"adaptive\";\n}\n\n/**\n * Whether a model of this thinking mode accepts an explicit `temperature`.\n *\n * Installed SDK, `MessageCreateParamsBase.temperature`\n * (`messages.d.ts:2036-2040`): \"Models released after Claude Opus 4.6 do\n * not support setting temperature. A value of 1.0 will be accepted for\n * backwards compatibility, all other values will be rejected with a 400\n * error.\" The claude-api ref's per-model table agrees: sampling is\n * \"Removed - 400\" on Fable 5/5.1, Opus 5.5/5/4.8/4.7 and Sonnet 5, and\n * \"Non-default values - 400\" on Sonnet 5.5; \"Allowed\" on Opus 4.6,\n * Sonnet 4.6, Haiku 4.5 and older.\n *\n * That split is exactly the adaptive/budget split in the table above\n * (adaptive = released after Opus 4.6; budget = Opus 4.6 and earlier), so\n * the thinking mode doubles as the sampling-parameter classification.\n */\nexport function acceptsTemperature(mode: AnthropicThinkingMode): boolean {\n return mode === \"budget\";\n}\n"],"mappings":";;;;;AA4CA,MAAM,yBAAyB;CAE7B;CAEA;CACA;CAEA;AACF;;;;;;;;AASA,MAAM,uBAAuB;CAC3B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;;;AAOA,MAAM,qBAAqB;AAE3B,SAAS,mBAAmB,MAAc,MAAuB;CAC/D,IAAI,CAAC,KAAK,WAAW,IAAI,GACvB,OAAO;CAGT,MAAM,OAAO,KAAK,MAAM,KAAK,MAAM;CAEnC,IAAI,SAAS,IACX,OAAO;CAGT,QAAQ,KAAK,WAAW,GAAG,KAAK,KAAK,WAAW,GAAG,MAAM,CAAC,mBAAmB,KAAK,IAAI;AACxF;;;;;;;;;;;;AAaA,SAAgB,kBAAkB,WAA0C;CAC1E,MAAM,aAAa,UAAU,YAAY;CAEzC,IAAI,uBAAuB,MAAM,WAAW,WAAW,WAAW,MAAM,CAAC,GACvE,OAAO;CAGT,IAAI,qBAAqB,MAAM,SAAS,mBAAmB,YAAY,IAAI,CAAC,GAC1E,OAAO;CAGT,OAAO;AACT;;;;;;;;;;;;;;;;;AAkBA,SAAgB,mBAAmB,MAAsC;CACvE,OAAO,SAAS;AAClB"}
@@ -3,13 +3,15 @@ const stopReasonMap = {
3
3
  end_turn: "stop",
4
4
  stop_sequence: "stop",
5
5
  max_tokens: "length",
6
+ model_context_window_exceeded: "length",
6
7
  tool_use: "tool_calls"
7
8
  };
8
9
  /**
9
10
  * Map Anthropic's `stop_reason` to the normalized `FinishReason` union.
10
11
  *
11
- * `end_turn` / `stop_sequence` are both natural stops. `max_tokens`
12
- * maps to `length`. `tool_use` maps to `tool_calls`. Everything else —
12
+ * `end_turn` / `stop_sequence` are both natural stops. `max_tokens` and
13
+ * `model_context_window_exceeded` map to `length`. `tool_use` maps to
14
+ * `tool_calls`. Everything else —
13
15
  * `refusal` (policy intervention), `pause_turn` (incomplete
14
16
  * long-running turn), `null`, or any value a future API version adds —
15
17
  * falls through to `"error"` so the agent treats the trip as a
@@ -1 +1 @@
1
- {"version":3,"file":"map-stop-reason.mjs","names":[],"sources":["../../../../../../../ai-anthropic/src/utils/map-stop-reason.ts"],"sourcesContent":["import type { FinishReason } from \"@warlock.js/ai\";\n\nconst stopReasonMap: Record<string, FinishReason> = {\n end_turn: \"stop\",\n stop_sequence: \"stop\",\n max_tokens: \"length\",\n tool_use: \"tool_calls\",\n};\n\n/**\n * Map Anthropic's `stop_reason` to the normalized `FinishReason` union.\n *\n * `end_turn` / `stop_sequence` are both natural stops. `max_tokens`\n * maps to `length`. `tool_use` maps to `tool_calls`. Everything else —\n * `refusal` (policy intervention), `pause_turn` (incomplete\n * long-running turn), `null`, or any value a future API version adds —\n * falls through to `\"error\"` so the agent treats the trip as a\n * non-clean terminal rather than silently accepting a partial result.\n *\n * @example\n * mapStopReason(\"end_turn\"); // \"stop\"\n * mapStopReason(\"tool_use\"); // \"tool_calls\"\n * mapStopReason(\"refusal\"); // \"error\"\n * mapStopReason(null); // \"error\"\n */\nexport function mapStopReason(raw: string | null | undefined): FinishReason {\n return stopReasonMap[raw ?? \"\"] ?? \"error\";\n}\n"],"mappings":";AAEA,MAAM,gBAA8C;CAClD,UAAU;CACV,eAAe;CACf,YAAY;CACZ,UAAU;AACZ;;;;;;;;;;;;;;;;;AAkBA,SAAgB,cAAc,KAA8C;CAC1E,OAAO,cAAc,OAAO,OAAO;AACrC"}
1
+ {"version":3,"file":"map-stop-reason.mjs","names":[],"sources":["../../../../../../../ai-anthropic/src/utils/map-stop-reason.ts"],"sourcesContent":["import type { FinishReason } from \"@warlock.js/ai\";\n\nconst stopReasonMap: Record<string, FinishReason> = {\n end_turn: \"stop\",\n stop_sequence: \"stop\",\n max_tokens: \"length\",\n model_context_window_exceeded: \"length\",\n tool_use: \"tool_calls\",\n};\n\n/**\n * Map Anthropic's `stop_reason` to the normalized `FinishReason` union.\n *\n * `end_turn` / `stop_sequence` are both natural stops. `max_tokens` and\n * `model_context_window_exceeded` map to `length`. `tool_use` maps to\n * `tool_calls`. Everything else —\n * `refusal` (policy intervention), `pause_turn` (incomplete\n * long-running turn), `null`, or any value a future API version adds —\n * falls through to `\"error\"` so the agent treats the trip as a\n * non-clean terminal rather than silently accepting a partial result.\n *\n * @example\n * mapStopReason(\"end_turn\"); // \"stop\"\n * mapStopReason(\"tool_use\"); // \"tool_calls\"\n * mapStopReason(\"refusal\"); // \"error\"\n * mapStopReason(null); // \"error\"\n */\nexport function mapStopReason(raw: string | null | undefined): FinishReason {\n return stopReasonMap[raw ?? \"\"] ?? \"error\";\n}\n"],"mappings":";AAEA,MAAM,gBAA8C;CAClD,UAAU;CACV,eAAe;CACf,YAAY;CACZ,+BAA+B;CAC/B,UAAU;AACZ;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,cAAc,KAA8C;CAC1E,OAAO,cAAc,OAAO,OAAO;AACrC"}