@warlock.js/ai-anthropic 5.23.1 → 5.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,22 @@ All notable changes to `@warlock.js/ai-anthropic` are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
6
6
 
7
+ ## 5.24.0 - 2026-09-27
8
+
9
+ ### Fixed
10
+
11
+ - Preserve signed and redacted extended-thinking blocks across Anthropic tool-use follow-ups, and ensure `max_tokens` exceeds the thinking budget.
12
+
13
+ ### Changed
14
+
15
+ - Lockstep release; package APIs are unchanged.
16
+
17
+ ## 5.23.2 - 2026-09-26
18
+
19
+ ### Changed
20
+
21
+ - Lockstep patch release; package APIs are unchanged.
22
+
7
23
  ## 5.23.1 - 2026-09-26
8
24
 
9
25
  ### Changed
package/cjs/index.cjs CHANGED
@@ -150,7 +150,7 @@ function toAnthropicMessages(messages) {
150
150
  continue;
151
151
  }
152
152
  if (message.role === "assistant" && message.toolCalls && message.toolCalls.length > 0) {
153
- const blocks = [];
153
+ const blocks = extractThinkingBlocks(message.toolCalls);
154
154
  const text = stringifyContent(message.content);
155
155
  if (text) blocks.push({
156
156
  type: "text",
@@ -186,6 +186,35 @@ function toAnthropicMessages(messages) {
186
186
  };
187
187
  }
188
188
  /**
189
+ * Recover signed extended-thinking blocks stored by this adapter on a tool
190
+ * request. Anthropic requires these blocks to be replayed before the
191
+ * assistant's text and tool uses on the following request.
192
+ */
193
+ function extractThinkingBlocks(toolCalls) {
194
+ for (const toolCall of toolCalls) {
195
+ const anthropic = toolCall.providerMetadata?.anthropic;
196
+ if (!isRecord(anthropic) || !Array.isArray(anthropic.thinkingBlocks)) continue;
197
+ return anthropic.thinkingBlocks.flatMap(toAnthropicThinkingBlock);
198
+ }
199
+ return [];
200
+ }
201
+ function toAnthropicThinkingBlock(block) {
202
+ if (!isRecord(block) || typeof block.type !== "string") return [];
203
+ if (block.type === "thinking" && typeof block.thinking === "string" && typeof block.signature === "string") return [{
204
+ type: "thinking",
205
+ thinking: block.thinking,
206
+ signature: block.signature
207
+ }];
208
+ if (block.type === "redacted_thinking" && typeof block.data === "string") return [{
209
+ type: "redacted_thinking",
210
+ data: block.data
211
+ }];
212
+ return [];
213
+ }
214
+ function isRecord(value) {
215
+ return typeof value === "object" && value !== null;
216
+ }
217
+ /**
189
218
  * Multipart content is only meaningful on user messages — for any other
190
219
  * role collapse a `ContentPart[]` to its concatenated text so the wire
191
220
  * format stays valid. Plain strings pass through unchanged.
@@ -570,6 +599,7 @@ var AnthropicModel = class {
570
599
  total: 0
571
600
  };
572
601
  const toolBlocks = /* @__PURE__ */ new Map();
602
+ const thinkingBlocks = /* @__PURE__ */ new Map();
573
603
  try {
574
604
  for await (const event of stream) {
575
605
  if (event.type === "message_start") {
@@ -587,6 +617,8 @@ var AnthropicModel = class {
587
617
  name: block.name,
588
618
  json: ""
589
619
  });
620
+ else if (block.type === "thinking") thinkingBlocks.set(event.index, { ...block });
621
+ else if (block.type === "redacted_thinking") thinkingBlocks.set(event.index, { ...block });
590
622
  continue;
591
623
  }
592
624
  if (event.type === "content_block_delta") {
@@ -597,6 +629,12 @@ var AnthropicModel = class {
597
629
  else if (event.delta.type === "input_json_delta") {
598
630
  const accumulator = toolBlocks.get(event.index);
599
631
  if (accumulator) accumulator.json += event.delta.partial_json;
632
+ } else if (event.delta.type === "thinking_delta") {
633
+ const thinking = thinkingBlocks.get(event.index);
634
+ if (thinking?.type === "thinking") thinking.thinking += event.delta.thinking;
635
+ } else if (event.delta.type === "signature_delta") {
636
+ const thinking = thinkingBlocks.get(event.index);
637
+ if (thinking?.type === "thinking") thinking.signature += event.delta.signature;
600
638
  }
601
639
  continue;
602
640
  }
@@ -607,7 +645,8 @@ var AnthropicModel = class {
607
645
  type: "tool-call",
608
646
  id: accumulator.id,
609
647
  name: accumulator.name,
610
- input: (0, _warlock_js_ai.safeJsonParse)(accumulator.json, {})
648
+ input: (0, _warlock_js_ai.safeJsonParse)(accumulator.json, {}),
649
+ ...this.buildThinkingMetadata(Array.from(thinkingBlocks.entries()).sort(([left], [right]) => left - right).map(([, block]) => block))
611
650
  };
612
651
  toolBlocks.delete(event.index);
613
652
  }
@@ -652,9 +691,13 @@ var AnthropicModel = class {
652
691
  const { system, messages: anthropicMessages } = toAnthropicMessages(messages);
653
692
  const thinking = this.buildThinking(options?.reasoning);
654
693
  const temperature = options?.temperature ?? this.config.temperature;
694
+ const configuredMaxTokens = options?.maxTokens ?? this.config.maxTokens;
695
+ const thinkingBudget = thinking.thinking?.type === "enabled" ? thinking.thinking.budget_tokens : void 0;
696
+ const maxTokens = configuredMaxTokens ?? (thinkingBudget !== void 0 ? thinkingBudget + DEFAULT_MAX_TOKENS : DEFAULT_MAX_TOKENS);
697
+ if (thinkingBudget !== void 0 && configuredMaxTokens !== void 0 && configuredMaxTokens <= thinkingBudget) throw new Error(`Anthropic maxTokens (${configuredMaxTokens}) must be greater than thinking budget_tokens (${thinkingBudget})`);
655
698
  return {
656
699
  model: this.name,
657
- max_tokens: options?.maxTokens ?? this.config.maxTokens ?? DEFAULT_MAX_TOKENS,
700
+ max_tokens: maxTokens,
658
701
  messages: anthropicMessages,
659
702
  ...this.buildSystem(system, options?.cacheControl),
660
703
  ...temperature !== void 0 && !thinking.thinking ? { temperature } : {},
@@ -768,13 +811,24 @@ var AnthropicModel = class {
768
811
  extractToolCalls(content) {
769
812
  const toolUses = content.filter((block) => block.type === "tool_use");
770
813
  if (toolUses.length === 0) return;
814
+ const thinkingMetadata = this.buildThinkingMetadata(content);
771
815
  return toolUses.map((block) => ({
772
816
  id: block.id,
773
817
  name: block.name,
774
- input: block.input ?? {}
818
+ input: block.input ?? {},
819
+ ...thinkingMetadata
775
820
  }));
776
821
  }
777
822
  /**
823
+ * Preserve Anthropic's opaque extended-thinking blocks beside a tool
824
+ * request so the next assistant turn can replay their signatures exactly.
825
+ */
826
+ buildThinkingMetadata(blocks) {
827
+ const thinkingBlocks = Array.from(blocks).filter((block) => block.type === "thinking" || block.type === "redacted_thinking");
828
+ if (thinkingBlocks.length === 0) return {};
829
+ return { providerMetadata: { anthropic: { thinkingBlocks } } };
830
+ }
831
+ /**
778
832
  * Normalize Anthropic's `usage` block into the neutral `Usage` shape.
779
833
  * Anthropic reports `input_tokens` / `output_tokens` separately with
780
834
  * no pre-summed total, so `total` is computed. Cache-read tokens are
package/cjs/index.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["AIError","ProviderTimeoutError","ProviderAuthError","QuotaExceededError","ProviderRateLimitError","ContextLengthExceededError","InvalidRequestError","ProviderError","APIError","APIConnectionTimeoutError","log","Anthropic"],"sources":["../../../../../../ai-anthropic/src/known-vision-models.ts","../../../../../../ai-anthropic/src/utils/map-stop-reason.ts","../../../../../../ai-anthropic/src/utils/to-anthropic-messages.ts","../../../../../../ai-anthropic/src/utils/to-anthropic-tools.ts","../../../../../../ai-anthropic/src/utils/wrap-anthropic-error.ts","../../../../../../ai-anthropic/src/model.ts","../../../../../../ai-anthropic/src/sdk.ts"],"sourcesContent":["/**\n * Model-name prefixes for Claude families that accept image input\n * (vision) on the Messages API.\n *\n * Every Claude 3, Claude 3.5/3.7, and Claude 4 family model is\n * multimodal, so the list covers both the dotted legacy naming\n * (`claude-3-haiku-...`, `claude-3-5-sonnet-...`) and the current\n * `claude-<tier>-4-*` naming (`claude-opus-4-7`, `claude-sonnet-4-6`,\n * `claude-haiku-4-5`). Pre-3 families (`claude-2`, `claude-instant`)\n * are text-only and intentionally absent.\n *\n * Matched as a prefix so dated variants (`claude-opus-4-20250514`) are\n * covered without listing every release tag. Devs can always override\n * per-model via `anthropic.model({ name, vision: true | false })` —\n * explicit config wins over inference in either direction.\n */\nconst VISION_CAPABLE_PREFIXES = [\n \"claude-3\",\n \"claude-4\",\n \"claude-opus-4\",\n \"claude-sonnet-4\",\n \"claude-haiku-4\",\n];\n\n/**\n * Infer whether a given Claude model name supports vision based on the\n * known-prefix list. Unknown models default to `false` so that passing\n * an image attachment to an unsupported model surfaces a clear,\n * agent-side capability error instead of an opaque Anthropic 400.\n *\n * @example\n * inferVisionCapability(\"claude-sonnet-4-6\"); // → true\n * inferVisionCapability(\"claude-3-5-sonnet-latest\"); // → true\n * inferVisionCapability(\"claude-2.1\"); // → false\n * inferVisionCapability(\"custom-proxy-llm\"); // → false\n */\nexport function inferVisionCapability(modelName: string): boolean {\n const normalized = modelName.toLowerCase();\n\n return VISION_CAPABLE_PREFIXES.some((prefix) => normalized.startsWith(prefix));\n}\n","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","import type { ContentPart, Message } from \"@warlock.js/ai\";\nimport type Anthropic from \"@anthropic-ai/sdk\";\n\n/**\n * Result of splitting a vendor-neutral `Message[]` for the Anthropic\n * Messages API: the system prompt is hoisted to a top-level `system`\n * string (Anthropic has no `\"system\"` role inside `messages`), and the\n * remaining turns are mapped to `MessageParam[]`.\n */\nexport type AnthropicMessages = {\n system: string | undefined;\n messages: Anthropic.MessageParam[];\n};\n\nconst ANTHROPIC_IMAGE_MEDIA_TYPES = [\"image/jpeg\", \"image/png\", \"image/gif\", \"image/webp\"] as const;\n\ntype AnthropicImageMediaType = (typeof ANTHROPIC_IMAGE_MEDIA_TYPES)[number];\n\n/**\n * Convert vendor-neutral `Message[]` into Anthropic's request shape.\n *\n * Anthropic differs from the OpenAI Chat protocol in three ways this\n * function absorbs:\n *\n * 1. **No `system` role.** System messages are concatenated (newline-\n * separated) and returned separately as the top-level `system`\n * parameter.\n * 2. **Tool results are `user` turns.** A neutral `tool` message becomes\n * a `user` message whose content is a single `tool_result` block\n * keyed by `tool_use_id`.\n * 3. **Tool calls are `tool_use` content blocks.** An assistant message\n * carrying `toolCalls` becomes an `assistant` message whose content\n * is an optional leading `text` block followed by one `tool_use`\n * block per call.\n *\n * Consecutive same-role turns are left as-is — the Messages API merges\n * them server-side.\n *\n * @example\n * const { system, messages } = toAnthropicMessages([\n * { role: \"system\", content: \"Be concise.\" },\n * { role: \"user\", content: \"Hi\" },\n * ]);\n * // system === \"Be concise.\"\n * // messages === [{ role: \"user\", content: \"Hi\" }]\n */\nexport function toAnthropicMessages(messages: Message[]): AnthropicMessages {\n const systemParts: string[] = [];\n const mapped: Anthropic.MessageParam[] = [];\n\n for (const message of messages) {\n if (message.role === \"system\") {\n systemParts.push(stringifyContent(message.content));\n\n continue;\n }\n\n if (message.role === \"tool\") {\n mapped.push({\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n tool_use_id: message.toolCallId ?? \"\",\n content: stringifyContent(message.content),\n },\n ],\n });\n\n continue;\n }\n\n if (message.role === \"assistant\" && message.toolCalls && message.toolCalls.length > 0) {\n const blocks: Anthropic.ContentBlockParam[] = [];\n const text = stringifyContent(message.content);\n\n if (text) {\n blocks.push({ type: \"text\", text });\n }\n\n for (const toolCall of message.toolCalls) {\n blocks.push({\n type: \"tool_use\",\n id: toolCall.id,\n name: toolCall.name,\n input: toolCall.input ?? {},\n });\n }\n\n mapped.push({ role: \"assistant\", content: blocks });\n\n continue;\n }\n\n if (message.role === \"user\" && Array.isArray(message.content)) {\n mapped.push({\n role: \"user\",\n content: message.content.map(toAnthropicContentBlock),\n });\n\n continue;\n }\n\n mapped.push({\n role: message.role === \"assistant\" ? \"assistant\" : \"user\",\n content: stringifyContent(message.content),\n });\n }\n\n return {\n system: systemParts.length > 0 ? systemParts.join(\"\\n\\n\") : undefined,\n messages: mapped,\n };\n}\n\n/**\n * Multipart content is only meaningful on user messages — for any other\n * role collapse a `ContentPart[]` to its concatenated text so the wire\n * format stays valid. Plain strings pass through unchanged.\n */\nfunction stringifyContent(content: string | ContentPart[]): string {\n if (typeof content === \"string\") {\n return content;\n }\n\n return content\n .filter((part): part is { type: \"text\"; text: string } => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\");\n}\n\n/**\n * Map a single resolved `ContentPart` to an Anthropic content block.\n * Text passes straight through; images become an `image` block with a\n * `url` source (remote) or a `base64` source (inlined bytes). The\n * agent has already resolved every attachment before it reaches here,\n * so this never reads files or fetches URLs.\n */\nfunction toAnthropicContentBlock(part: ContentPart): Anthropic.ContentBlockParam {\n if (part.type === \"text\") {\n return { type: \"text\", text: part.text };\n }\n\n // PDF → Anthropic `document` block (A2). The Messages API accepts a\n // url or base64 `application/pdf` source on PDF-capable Claude models.\n if (part.type === \"pdf\") {\n const source =\n \"url\" in part.source\n ? { type: \"url\", url: part.source.url }\n : { type: \"base64\", media_type: \"application/pdf\", data: part.source.base64 };\n return { type: \"document\", source } as unknown as Anthropic.ContentBlockParam;\n }\n\n // Anthropic has no audio content block; the agent's capability gate\n // normally prevents this, but stay loud rather than mis-map it.\n if (part.type === \"audio\") {\n throw new Error(\"Anthropic adapter: audio attachments are not supported\");\n }\n\n if (\"url\" in part.source) {\n return { type: \"image\", source: { type: \"url\", url: part.source.url } };\n }\n\n return {\n type: \"image\",\n source: {\n type: \"base64\",\n media_type: part.source.mediaType as AnthropicImageMediaType,\n data: part.source.base64,\n },\n };\n}\n","import { extractJsonSchema, type ToolConfig } from \"@warlock.js/ai\";\nimport type Anthropic from \"@anthropic-ai/sdk\";\n\n/**\n * Convert vendor-neutral `ToolConfig[]` into Anthropic's `tools` array.\n * Uses the shared `extractJsonSchema` helper; Anthropic requires the\n * input schema to be a JSON-Schema object, so a non-object extraction\n * is coerced into an empty-object schema rather than rejected — the\n * tool still registers and the model simply sees no parameters.\n *\n * @example\n * const tools = toAnthropicTools([weatherTool, calculatorTool]);\n * await client.messages.create({ model, max_tokens, messages, tools });\n */\nexport function toAnthropicTools(\n tools: ToolConfig<unknown, unknown>[] | undefined,\n): Anthropic.Tool[] | undefined {\n if (!tools || tools.length === 0) {\n return undefined;\n }\n\n return tools.map((tool) => ({\n name: tool.name,\n description: tool.description,\n input_schema: toInputSchema(tool.input),\n }));\n}\n\n/**\n * Coerce the extracted JSON Schema into Anthropic's `Tool.InputSchema`\n * shape (root must be `{ type: \"object\" }`). Anything that isn't an\n * object schema degrades to a parameterless object so registration\n * never fails on a malformed extractor result.\n */\nfunction toInputSchema(input: ToolConfig<unknown, unknown>[\"input\"]): Anthropic.Tool.InputSchema {\n const schema = extractJsonSchema(input);\n\n if (schema && schema.type === \"object\") {\n return schema as Anthropic.Tool.InputSchema;\n }\n\n return { type: \"object\" };\n}\n","import {\n AIError,\n ContextLengthExceededError,\n InvalidRequestError,\n ProviderAuthError,\n ProviderError,\n ProviderRateLimitError,\n ProviderTimeoutError,\n QuotaExceededError,\n} from \"@warlock.js/ai\";\nimport { APIConnectionTimeoutError, APIError } from \"@anthropic-ai/sdk\";\n\n/**\n * Raw-error fields the wrapper reads off an Anthropic SDK error.\n *\n * `APIError` exposes `status`, `type` (the `error.type` from the\n * response body, e.g. `\"rate_limit_error\"`), `message`, `headers`, and\n * `requestID`. We duck-type because wrapped retries, proxied errors,\n * and custom subclasses sometimes lose the `instanceof` relationship.\n */\ntype AnthropicErrorShape = {\n status?: number;\n type?: string | null;\n message?: string;\n headers?: HeaderBag | undefined;\n name?: string;\n requestId?: string;\n};\n\n/** Either a fetch `Headers` instance or a plain record (duck-typed tests). */\ntype HeaderBag = Headers | Record<string, string>;\n\n/**\n * Wrap any thrown value caught inside the Anthropic adapter into the\n * appropriate `@warlock.js/ai` `AIError` subclass.\n *\n * **Dispatch strategy.** Anthropic has no per-error machine `code`; the\n * stable identifier is `error.type` on the response body (surfaced as\n * `APIError.type`). Dispatch prefers `type`, falls back to `status`\n * when the body was stripped (common with proxies). Name-based\n * detection catches transport-layer timeouts that never produced an\n * HTTP response. The `invalid_request_error` branch additionally\n * sniffs the message for Anthropic's \"prompt is too long\" phrasing,\n * which is the only signal that a 400 was a context-length overflow.\n *\n * `AIError` instances pass through unchanged so `catch/throw wrap(e)`\n * pipelines never double-wrap.\n *\n * @example\n * try {\n * return await this.client.messages.create(...);\n * } catch (thrown) {\n * throw wrapAnthropicError(thrown);\n * }\n */\nexport function wrapAnthropicError(thrown: unknown): AIError {\n if (thrown instanceof AIError) {\n return thrown;\n }\n\n const shape = toShape(thrown);\n const context = buildContext(shape);\n const message = shape.message ?? (thrown instanceof Error ? thrown.message : String(thrown));\n\n if (isTimeout(thrown, shape)) {\n return new ProviderTimeoutError(message, { cause: thrown, context });\n }\n\n if (shape.type === \"authentication_error\" || shape.type === \"permission_error\") {\n return new ProviderAuthError(message, { cause: thrown, context });\n }\n\n if (shape.status === 401 || shape.status === 403) {\n return new ProviderAuthError(message, { cause: thrown, context });\n }\n\n if (shape.type === \"billing_error\") {\n return new QuotaExceededError(message, { cause: thrown, context });\n }\n\n if (shape.type === \"rate_limit_error\" || shape.status === 429) {\n return new ProviderRateLimitError(message, {\n cause: thrown,\n context,\n retryAfter: parseRetryAfter(shape.headers),\n });\n }\n\n if (shape.type === \"invalid_request_error\" || isClientStatus(shape.status)) {\n if (/prompt is too long/i.test(message)) {\n return new ContextLengthExceededError(message, { cause: thrown, context });\n }\n\n return new InvalidRequestError(message, { cause: thrown, context });\n }\n\n return new ProviderError(message, { cause: thrown, context });\n}\n\n/**\n * Read the raw error shape without depending on `instanceof APIError`\n * — proxies and re-wrappers strip the prototype chain. Duck-typing on\n * the visible fields is resilient to both.\n */\nfunction toShape(thrown: unknown): AnthropicErrorShape {\n if (thrown instanceof APIError) {\n return {\n status: typeof thrown.status === \"number\" ? thrown.status : undefined,\n type: thrown.type,\n message: thrown.message,\n headers: thrown.headers,\n name: thrown.name,\n requestId: thrown.requestID ?? undefined,\n };\n }\n\n if (typeof thrown === \"object\" && thrown !== null) {\n const raw = thrown as Record<string, unknown>;\n\n return {\n status: typeof raw.status === \"number\" ? raw.status : undefined,\n type: typeof raw.type === \"string\" ? raw.type : undefined,\n message: typeof raw.message === \"string\" ? raw.message : undefined,\n headers: isHeaderBag(raw.headers) ? raw.headers : undefined,\n name: typeof raw.name === \"string\" ? raw.name : undefined,\n requestId: readRequestId(raw),\n };\n }\n\n return {};\n}\n\n/**\n * Decide whether the thrown value represents a timeout. The Anthropic\n * SDK throws `APIConnectionTimeoutError` for transport-level timeouts;\n * Node surfaces `ETIMEDOUT` / `ECONNABORTED` on the socket layer.\n * Either signal counts.\n */\nfunction isTimeout(thrown: unknown, shape: AnthropicErrorShape): boolean {\n if (thrown instanceof APIConnectionTimeoutError) {\n return true;\n }\n\n if (shape.name === \"APIConnectionTimeoutError\") {\n return true;\n }\n\n if (typeof thrown === \"object\" && thrown !== null) {\n const code = (thrown as Record<string, unknown>).code;\n\n if (code === \"ETIMEDOUT\" || code === \"ECONNABORTED\") {\n return true;\n }\n }\n\n return false;\n}\n\n/** True for HTTP 4xx — a client-side request problem, not a server fault. */\nfunction isClientStatus(status: number | undefined): boolean {\n return typeof status === \"number\" && status >= 400 && status < 500;\n}\n\n/**\n * Attach the raw diagnostic fields to `error.context` so consumers\n * have everything the provider surfaced without each subclass having\n * to redeclare them. Never includes `cause` — that lives on\n * `error.cause`.\n */\nfunction buildContext(shape: AnthropicErrorShape): Record<string, unknown> {\n const context: Record<string, unknown> = {};\n\n if (shape.status !== undefined) {\n context.status = shape.status;\n }\n\n if (shape.type) {\n context.type = shape.type;\n }\n\n if (shape.requestId) {\n context.requestId = shape.requestId;\n }\n\n return context;\n}\n\n/** Anthropic exposes the request id as `requestID`; some proxies use `request_id`. */\nfunction readRequestId(raw: Record<string, unknown>): string | undefined {\n if (typeof raw.requestID === \"string\") {\n return raw.requestID;\n }\n\n if (typeof raw.request_id === \"string\") {\n return raw.request_id;\n }\n\n return undefined;\n}\n\nfunction isHeaderBag(value: unknown): value is HeaderBag {\n return typeof value === \"object\" && value !== null;\n}\n\n/** Read a header value from either a `Headers` instance or a plain record. */\nfunction readHeader(headers: HeaderBag, name: string): string | undefined {\n if (typeof (headers as Headers).get === \"function\") {\n return (headers as Headers).get(name) ?? undefined;\n }\n\n const record = headers as Record<string, string>;\n\n return record[name] ?? record[name.toLowerCase()];\n}\n\n/**\n * Parse the `Retry-After` response header (seconds per HTTP spec) into\n * milliseconds so consumers can feed it straight to `setTimeout`.\n * Returns `undefined` when missing or unparseable.\n */\nfunction parseRetryAfter(headers: HeaderBag | undefined): number | undefined {\n if (!headers) {\n return undefined;\n }\n\n const raw = readHeader(headers, \"retry-after\") ?? readHeader(headers, \"Retry-After\");\n\n if (!raw) {\n return undefined;\n }\n\n const seconds = Number(raw);\n\n if (!Number.isFinite(seconds) || seconds < 0) {\n return undefined;\n }\n\n return Math.round(seconds * 1000);\n}\n","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\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\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 }\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 }\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 };\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\n return {\n model: this.name,\n max_tokens: options?.maxTokens ?? this.config.maxTokens ?? DEFAULT_MAX_TOKENS,\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 return toolUses.map((block) => ({\n id: block.id,\n name: block.name,\n input: (block.input ?? {}) as Record<string, unknown>,\n }));\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","import Anthropic from \"@anthropic-ai/sdk\";\nimport type { ModelContract, ModelPricing, SDKAdapterContract } from \"@warlock.js/ai\";\nimport { approximateTokenCount } from \"@warlock.js/ai\";\nimport type { AnthropicModelConfig, AnthropicSDKConfig } from \"./config.type\";\nimport { AnthropicModel } from \"./model\";\n\n/**\n * Anthropic-backed implementation of `SDKAdapterContract`.\n *\n * **Role.** The package entry point for Claude models via the official\n * `@anthropic-ai/sdk`. A single `AnthropicSDK` instance holds one live\n * `Anthropic` client, shared by every `ModelContract` it produces via\n * `model()`. Users construct one SDK per account and reuse it across\n * all agents, workflows, and supervisors that target Anthropic.\n *\n * **Responsibility.**\n * - Owns: a long-lived `Anthropic` client (authentication, base URL)\n * and its lifetime scope. Factory for `AnthropicModel` instances —\n * each model call gets a reference to the same client.\n * - Does NOT own: anything per-call (tool execution, message history,\n * streaming loop) — those live in `AnthropicModel` and the agent\n * runtime. Does NOT implement `embedder()`: Anthropic ships no\n * first-party embeddings API (the contract marks it optional).\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across many calls\"): the `Anthropic` client is heavy to construct\n * and designed to be reused; keeping it on `this` makes that reuse\n * explicit and aligns with the `new Anthropic(...)` upstream\n * convention.\n *\n * @example\n * const anthropic = new AnthropicSDK({ apiKey: process.env.ANTHROPIC_API_KEY! });\n * const model = anthropic.model({ name: \"claude-sonnet-4-6\", temperature: 0.7 });\n * const tokens = await anthropic.count(\"Hello world\");\n *\n * @example\n * // Compose into an `ai.anthropic` namespace for ergonomic agent wiring\n * const ai = { agent, tool, systemPrompt, anthropic: new AnthropicSDK({ apiKey }) };\n * const myAgent = ai.agent({ model: ai.anthropic.model({ name: \"claude-haiku-4-5\" }) });\n */\nexport class AnthropicSDK implements SDKAdapterContract {\n private readonly client: Anthropic;\n private readonly provider: string;\n private readonly pricing?: Record<string, ModelPricing>;\n\n public constructor(config: AnthropicSDKConfig) {\n // Peel off the framework-only keys and forward every other upstream\n // `ClientOptions` (timeout, maxRetries, defaultHeaders, fetch, …) verbatim\n // — they type-check, so dropping them is a silent footgun. Mirrors the\n // Bedrock/Google/Ollama adapters.\n const { provider, pricing, ...clientOptions } = config;\n\n this.client = new Anthropic(clientOptions);\n this.provider = provider ?? \"anthropic\";\n this.pricing = pricing;\n }\n\n /**\n * Build an `AnthropicModel` bound to this SDK's client. Each call\n * returns a fresh model instance, but all instances share the\n * underlying `Anthropic` client — connection pools, rate limits, and\n * authentication stay unified across every model produced here. The\n * SDK's `provider` label is forwarded so every model self-identifies\n * as coming from the same upstream.\n *\n * Pricing resolution: per-model `config.pricing` wins; otherwise the\n * SDK-level registry entry keyed by `config.name`; otherwise\n * `undefined` (no cost computed).\n */\n public model(config: AnthropicModelConfig): ModelContract {\n const resolvedPricing = config.pricing ?? this.pricing?.[config.name];\n const resolvedConfig: AnthropicModelConfig =\n resolvedPricing === config.pricing ? config : { ...config, pricing: resolvedPricing };\n\n return new AnthropicModel(this.client, resolvedConfig, this.provider);\n }\n\n /**\n * Rough token-count estimate for a given text. Uses the\n * character-heuristic (`approximateTokenCount`) from the core package\n * — good enough for budgeting and quota guards, not for billing.\n * Anthropic does expose a `messages.countTokens` endpoint, but that\n * is a network round-trip; `count()` is intentionally offline and\n * synchronous-cost. The optional model id is reserved for future\n * per-model tokenizer dispatch; currently ignored.\n */\n public async count(text: string, _model?: string): Promise<number> {\n return approximateTokenCount(text);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["AIError","ProviderTimeoutError","ProviderAuthError","QuotaExceededError","ProviderRateLimitError","ContextLengthExceededError","InvalidRequestError","ProviderError","APIError","APIConnectionTimeoutError","log","Anthropic"],"sources":["../../../../../../ai-anthropic/src/known-vision-models.ts","../../../../../../ai-anthropic/src/utils/map-stop-reason.ts","../../../../../../ai-anthropic/src/utils/to-anthropic-messages.ts","../../../../../../ai-anthropic/src/utils/to-anthropic-tools.ts","../../../../../../ai-anthropic/src/utils/wrap-anthropic-error.ts","../../../../../../ai-anthropic/src/model.ts","../../../../../../ai-anthropic/src/sdk.ts"],"sourcesContent":["/**\n * Model-name prefixes for Claude families that accept image input\n * (vision) on the Messages API.\n *\n * Every Claude 3, Claude 3.5/3.7, and Claude 4 family model is\n * multimodal, so the list covers both the dotted legacy naming\n * (`claude-3-haiku-...`, `claude-3-5-sonnet-...`) and the current\n * `claude-<tier>-4-*` naming (`claude-opus-4-7`, `claude-sonnet-4-6`,\n * `claude-haiku-4-5`). Pre-3 families (`claude-2`, `claude-instant`)\n * are text-only and intentionally absent.\n *\n * Matched as a prefix so dated variants (`claude-opus-4-20250514`) are\n * covered without listing every release tag. Devs can always override\n * per-model via `anthropic.model({ name, vision: true | false })` —\n * explicit config wins over inference in either direction.\n */\nconst VISION_CAPABLE_PREFIXES = [\n \"claude-3\",\n \"claude-4\",\n \"claude-opus-4\",\n \"claude-sonnet-4\",\n \"claude-haiku-4\",\n];\n\n/**\n * Infer whether a given Claude model name supports vision based on the\n * known-prefix list. Unknown models default to `false` so that passing\n * an image attachment to an unsupported model surfaces a clear,\n * agent-side capability error instead of an opaque Anthropic 400.\n *\n * @example\n * inferVisionCapability(\"claude-sonnet-4-6\"); // → true\n * inferVisionCapability(\"claude-3-5-sonnet-latest\"); // → true\n * inferVisionCapability(\"claude-2.1\"); // → false\n * inferVisionCapability(\"custom-proxy-llm\"); // → false\n */\nexport function inferVisionCapability(modelName: string): boolean {\n const normalized = modelName.toLowerCase();\n\n return VISION_CAPABLE_PREFIXES.some((prefix) => normalized.startsWith(prefix));\n}\n","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","import type { ContentPart, Message, ModelToolCallRequest } from \"@warlock.js/ai\";\nimport type Anthropic from \"@anthropic-ai/sdk\";\n\n/**\n * Result of splitting a vendor-neutral `Message[]` for the Anthropic\n * Messages API: the system prompt is hoisted to a top-level `system`\n * string (Anthropic has no `\"system\"` role inside `messages`), and the\n * remaining turns are mapped to `MessageParam[]`.\n */\nexport type AnthropicMessages = {\n system: string | undefined;\n messages: Anthropic.MessageParam[];\n};\n\nconst ANTHROPIC_IMAGE_MEDIA_TYPES = [\"image/jpeg\", \"image/png\", \"image/gif\", \"image/webp\"] as const;\n\ntype AnthropicImageMediaType = (typeof ANTHROPIC_IMAGE_MEDIA_TYPES)[number];\n\n/**\n * Convert vendor-neutral `Message[]` into Anthropic's request shape.\n *\n * Anthropic differs from the OpenAI Chat protocol in three ways this\n * function absorbs:\n *\n * 1. **No `system` role.** System messages are concatenated (newline-\n * separated) and returned separately as the top-level `system`\n * parameter.\n * 2. **Tool results are `user` turns.** A neutral `tool` message becomes\n * a `user` message whose content is a single `tool_result` block\n * keyed by `tool_use_id`.\n * 3. **Tool calls are `tool_use` content blocks.** An assistant message\n * carrying `toolCalls` becomes an `assistant` message whose content\n * is an optional leading `text` block followed by one `tool_use`\n * block per call.\n *\n * Consecutive same-role turns are left as-is — the Messages API merges\n * them server-side.\n *\n * @example\n * const { system, messages } = toAnthropicMessages([\n * { role: \"system\", content: \"Be concise.\" },\n * { role: \"user\", content: \"Hi\" },\n * ]);\n * // system === \"Be concise.\"\n * // messages === [{ role: \"user\", content: \"Hi\" }]\n */\nexport function toAnthropicMessages(messages: Message[]): AnthropicMessages {\n const systemParts: string[] = [];\n const mapped: Anthropic.MessageParam[] = [];\n\n for (const message of messages) {\n if (message.role === \"system\") {\n systemParts.push(stringifyContent(message.content));\n\n continue;\n }\n\n if (message.role === \"tool\") {\n mapped.push({\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n tool_use_id: message.toolCallId ?? \"\",\n content: stringifyContent(message.content),\n },\n ],\n });\n\n continue;\n }\n\n if (message.role === \"assistant\" && message.toolCalls && message.toolCalls.length > 0) {\n const blocks: Anthropic.ContentBlockParam[] = extractThinkingBlocks(message.toolCalls);\n const text = stringifyContent(message.content);\n\n if (text) {\n blocks.push({ type: \"text\", text });\n }\n\n for (const toolCall of message.toolCalls) {\n blocks.push({\n type: \"tool_use\",\n id: toolCall.id,\n name: toolCall.name,\n input: toolCall.input ?? {},\n });\n }\n\n mapped.push({ role: \"assistant\", content: blocks });\n\n continue;\n }\n\n if (message.role === \"user\" && Array.isArray(message.content)) {\n mapped.push({\n role: \"user\",\n content: message.content.map(toAnthropicContentBlock),\n });\n\n continue;\n }\n\n mapped.push({\n role: message.role === \"assistant\" ? \"assistant\" : \"user\",\n content: stringifyContent(message.content),\n });\n }\n\n return {\n system: systemParts.length > 0 ? systemParts.join(\"\\n\\n\") : undefined,\n messages: mapped,\n };\n}\n\n/**\n * Recover signed extended-thinking blocks stored by this adapter on a tool\n * request. Anthropic requires these blocks to be replayed before the\n * assistant's text and tool uses on the following request.\n */\nfunction extractThinkingBlocks(toolCalls: ModelToolCallRequest[]): Anthropic.ContentBlockParam[] {\n for (const toolCall of toolCalls) {\n const anthropic = toolCall.providerMetadata?.anthropic;\n\n if (!isRecord(anthropic) || !Array.isArray(anthropic.thinkingBlocks)) {\n continue;\n }\n\n return anthropic.thinkingBlocks.flatMap(toAnthropicThinkingBlock);\n }\n\n return [];\n}\n\nfunction toAnthropicThinkingBlock(block: unknown): Anthropic.ContentBlockParam[] {\n if (!isRecord(block) || typeof block.type !== \"string\") {\n return [];\n }\n\n if (\n block.type === \"thinking\" &&\n typeof block.thinking === \"string\" &&\n typeof block.signature === \"string\"\n ) {\n return [{ type: \"thinking\", thinking: block.thinking, signature: block.signature }];\n }\n\n if (block.type === \"redacted_thinking\" && typeof block.data === \"string\") {\n return [{ type: \"redacted_thinking\", data: block.data }];\n }\n\n return [];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\n/**\n * Multipart content is only meaningful on user messages — for any other\n * role collapse a `ContentPart[]` to its concatenated text so the wire\n * format stays valid. Plain strings pass through unchanged.\n */\nfunction stringifyContent(content: string | ContentPart[]): string {\n if (typeof content === \"string\") {\n return content;\n }\n\n return content\n .filter((part): part is { type: \"text\"; text: string } => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\");\n}\n\n/**\n * Map a single resolved `ContentPart` to an Anthropic content block.\n * Text passes straight through; images become an `image` block with a\n * `url` source (remote) or a `base64` source (inlined bytes). The\n * agent has already resolved every attachment before it reaches here,\n * so this never reads files or fetches URLs.\n */\nfunction toAnthropicContentBlock(part: ContentPart): Anthropic.ContentBlockParam {\n if (part.type === \"text\") {\n return { type: \"text\", text: part.text };\n }\n\n // PDF → Anthropic `document` block (A2). The Messages API accepts a\n // url or base64 `application/pdf` source on PDF-capable Claude models.\n if (part.type === \"pdf\") {\n const source =\n \"url\" in part.source\n ? { type: \"url\", url: part.source.url }\n : { type: \"base64\", media_type: \"application/pdf\", data: part.source.base64 };\n return { type: \"document\", source } as unknown as Anthropic.ContentBlockParam;\n }\n\n // Anthropic has no audio content block; the agent's capability gate\n // normally prevents this, but stay loud rather than mis-map it.\n if (part.type === \"audio\") {\n throw new Error(\"Anthropic adapter: audio attachments are not supported\");\n }\n\n if (\"url\" in part.source) {\n return { type: \"image\", source: { type: \"url\", url: part.source.url } };\n }\n\n return {\n type: \"image\",\n source: {\n type: \"base64\",\n media_type: part.source.mediaType as AnthropicImageMediaType,\n data: part.source.base64,\n },\n };\n}\n","import { extractJsonSchema, type ToolConfig } from \"@warlock.js/ai\";\nimport type Anthropic from \"@anthropic-ai/sdk\";\n\n/**\n * Convert vendor-neutral `ToolConfig[]` into Anthropic's `tools` array.\n * Uses the shared `extractJsonSchema` helper; Anthropic requires the\n * input schema to be a JSON-Schema object, so a non-object extraction\n * is coerced into an empty-object schema rather than rejected — the\n * tool still registers and the model simply sees no parameters.\n *\n * @example\n * const tools = toAnthropicTools([weatherTool, calculatorTool]);\n * await client.messages.create({ model, max_tokens, messages, tools });\n */\nexport function toAnthropicTools(\n tools: ToolConfig<unknown, unknown>[] | undefined,\n): Anthropic.Tool[] | undefined {\n if (!tools || tools.length === 0) {\n return undefined;\n }\n\n return tools.map((tool) => ({\n name: tool.name,\n description: tool.description,\n input_schema: toInputSchema(tool.input),\n }));\n}\n\n/**\n * Coerce the extracted JSON Schema into Anthropic's `Tool.InputSchema`\n * shape (root must be `{ type: \"object\" }`). Anything that isn't an\n * object schema degrades to a parameterless object so registration\n * never fails on a malformed extractor result.\n */\nfunction toInputSchema(input: ToolConfig<unknown, unknown>[\"input\"]): Anthropic.Tool.InputSchema {\n const schema = extractJsonSchema(input);\n\n if (schema && schema.type === \"object\") {\n return schema as Anthropic.Tool.InputSchema;\n }\n\n return { type: \"object\" };\n}\n","import {\n AIError,\n ContextLengthExceededError,\n InvalidRequestError,\n ProviderAuthError,\n ProviderError,\n ProviderRateLimitError,\n ProviderTimeoutError,\n QuotaExceededError,\n} from \"@warlock.js/ai\";\nimport { APIConnectionTimeoutError, APIError } from \"@anthropic-ai/sdk\";\n\n/**\n * Raw-error fields the wrapper reads off an Anthropic SDK error.\n *\n * `APIError` exposes `status`, `type` (the `error.type` from the\n * response body, e.g. `\"rate_limit_error\"`), `message`, `headers`, and\n * `requestID`. We duck-type because wrapped retries, proxied errors,\n * and custom subclasses sometimes lose the `instanceof` relationship.\n */\ntype AnthropicErrorShape = {\n status?: number;\n type?: string | null;\n message?: string;\n headers?: HeaderBag | undefined;\n name?: string;\n requestId?: string;\n};\n\n/** Either a fetch `Headers` instance or a plain record (duck-typed tests). */\ntype HeaderBag = Headers | Record<string, string>;\n\n/**\n * Wrap any thrown value caught inside the Anthropic adapter into the\n * appropriate `@warlock.js/ai` `AIError` subclass.\n *\n * **Dispatch strategy.** Anthropic has no per-error machine `code`; the\n * stable identifier is `error.type` on the response body (surfaced as\n * `APIError.type`). Dispatch prefers `type`, falls back to `status`\n * when the body was stripped (common with proxies). Name-based\n * detection catches transport-layer timeouts that never produced an\n * HTTP response. The `invalid_request_error` branch additionally\n * sniffs the message for Anthropic's \"prompt is too long\" phrasing,\n * which is the only signal that a 400 was a context-length overflow.\n *\n * `AIError` instances pass through unchanged so `catch/throw wrap(e)`\n * pipelines never double-wrap.\n *\n * @example\n * try {\n * return await this.client.messages.create(...);\n * } catch (thrown) {\n * throw wrapAnthropicError(thrown);\n * }\n */\nexport function wrapAnthropicError(thrown: unknown): AIError {\n if (thrown instanceof AIError) {\n return thrown;\n }\n\n const shape = toShape(thrown);\n const context = buildContext(shape);\n const message = shape.message ?? (thrown instanceof Error ? thrown.message : String(thrown));\n\n if (isTimeout(thrown, shape)) {\n return new ProviderTimeoutError(message, { cause: thrown, context });\n }\n\n if (shape.type === \"authentication_error\" || shape.type === \"permission_error\") {\n return new ProviderAuthError(message, { cause: thrown, context });\n }\n\n if (shape.status === 401 || shape.status === 403) {\n return new ProviderAuthError(message, { cause: thrown, context });\n }\n\n if (shape.type === \"billing_error\") {\n return new QuotaExceededError(message, { cause: thrown, context });\n }\n\n if (shape.type === \"rate_limit_error\" || shape.status === 429) {\n return new ProviderRateLimitError(message, {\n cause: thrown,\n context,\n retryAfter: parseRetryAfter(shape.headers),\n });\n }\n\n if (shape.type === \"invalid_request_error\" || isClientStatus(shape.status)) {\n if (/prompt is too long/i.test(message)) {\n return new ContextLengthExceededError(message, { cause: thrown, context });\n }\n\n return new InvalidRequestError(message, { cause: thrown, context });\n }\n\n return new ProviderError(message, { cause: thrown, context });\n}\n\n/**\n * Read the raw error shape without depending on `instanceof APIError`\n * — proxies and re-wrappers strip the prototype chain. Duck-typing on\n * the visible fields is resilient to both.\n */\nfunction toShape(thrown: unknown): AnthropicErrorShape {\n if (thrown instanceof APIError) {\n return {\n status: typeof thrown.status === \"number\" ? thrown.status : undefined,\n type: thrown.type,\n message: thrown.message,\n headers: thrown.headers,\n name: thrown.name,\n requestId: thrown.requestID ?? undefined,\n };\n }\n\n if (typeof thrown === \"object\" && thrown !== null) {\n const raw = thrown as Record<string, unknown>;\n\n return {\n status: typeof raw.status === \"number\" ? raw.status : undefined,\n type: typeof raw.type === \"string\" ? raw.type : undefined,\n message: typeof raw.message === \"string\" ? raw.message : undefined,\n headers: isHeaderBag(raw.headers) ? raw.headers : undefined,\n name: typeof raw.name === \"string\" ? raw.name : undefined,\n requestId: readRequestId(raw),\n };\n }\n\n return {};\n}\n\n/**\n * Decide whether the thrown value represents a timeout. The Anthropic\n * SDK throws `APIConnectionTimeoutError` for transport-level timeouts;\n * Node surfaces `ETIMEDOUT` / `ECONNABORTED` on the socket layer.\n * Either signal counts.\n */\nfunction isTimeout(thrown: unknown, shape: AnthropicErrorShape): boolean {\n if (thrown instanceof APIConnectionTimeoutError) {\n return true;\n }\n\n if (shape.name === \"APIConnectionTimeoutError\") {\n return true;\n }\n\n if (typeof thrown === \"object\" && thrown !== null) {\n const code = (thrown as Record<string, unknown>).code;\n\n if (code === \"ETIMEDOUT\" || code === \"ECONNABORTED\") {\n return true;\n }\n }\n\n return false;\n}\n\n/** True for HTTP 4xx — a client-side request problem, not a server fault. */\nfunction isClientStatus(status: number | undefined): boolean {\n return typeof status === \"number\" && status >= 400 && status < 500;\n}\n\n/**\n * Attach the raw diagnostic fields to `error.context` so consumers\n * have everything the provider surfaced without each subclass having\n * to redeclare them. Never includes `cause` — that lives on\n * `error.cause`.\n */\nfunction buildContext(shape: AnthropicErrorShape): Record<string, unknown> {\n const context: Record<string, unknown> = {};\n\n if (shape.status !== undefined) {\n context.status = shape.status;\n }\n\n if (shape.type) {\n context.type = shape.type;\n }\n\n if (shape.requestId) {\n context.requestId = shape.requestId;\n }\n\n return context;\n}\n\n/** Anthropic exposes the request id as `requestID`; some proxies use `request_id`. */\nfunction readRequestId(raw: Record<string, unknown>): string | undefined {\n if (typeof raw.requestID === \"string\") {\n return raw.requestID;\n }\n\n if (typeof raw.request_id === \"string\") {\n return raw.request_id;\n }\n\n return undefined;\n}\n\nfunction isHeaderBag(value: unknown): value is HeaderBag {\n return typeof value === \"object\" && value !== null;\n}\n\n/** Read a header value from either a `Headers` instance or a plain record. */\nfunction readHeader(headers: HeaderBag, name: string): string | undefined {\n if (typeof (headers as Headers).get === \"function\") {\n return (headers as Headers).get(name) ?? undefined;\n }\n\n const record = headers as Record<string, string>;\n\n return record[name] ?? record[name.toLowerCase()];\n}\n\n/**\n * Parse the `Retry-After` response header (seconds per HTTP spec) into\n * milliseconds so consumers can feed it straight to `setTimeout`.\n * Returns `undefined` when missing or unparseable.\n */\nfunction parseRetryAfter(headers: HeaderBag | undefined): number | undefined {\n if (!headers) {\n return undefined;\n }\n\n const raw = readHeader(headers, \"retry-after\") ?? readHeader(headers, \"Retry-After\");\n\n if (!raw) {\n return undefined;\n }\n\n const seconds = Number(raw);\n\n if (!Number.isFinite(seconds) || seconds < 0) {\n return undefined;\n }\n\n return Math.round(seconds * 1000);\n}\n","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","import Anthropic from \"@anthropic-ai/sdk\";\nimport type { ModelContract, ModelPricing, SDKAdapterContract } from \"@warlock.js/ai\";\nimport { approximateTokenCount } from \"@warlock.js/ai\";\nimport type { AnthropicModelConfig, AnthropicSDKConfig } from \"./config.type\";\nimport { AnthropicModel } from \"./model\";\n\n/**\n * Anthropic-backed implementation of `SDKAdapterContract`.\n *\n * **Role.** The package entry point for Claude models via the official\n * `@anthropic-ai/sdk`. A single `AnthropicSDK` instance holds one live\n * `Anthropic` client, shared by every `ModelContract` it produces via\n * `model()`. Users construct one SDK per account and reuse it across\n * all agents, workflows, and supervisors that target Anthropic.\n *\n * **Responsibility.**\n * - Owns: a long-lived `Anthropic` client (authentication, base URL)\n * and its lifetime scope. Factory for `AnthropicModel` instances —\n * each model call gets a reference to the same client.\n * - Does NOT own: anything per-call (tool execution, message history,\n * streaming loop) — those live in `AnthropicModel` and the agent\n * runtime. Does NOT implement `embedder()`: Anthropic ships no\n * first-party embeddings API (the contract marks it optional).\n *\n * Modeled as a class (see §4.2 of code-style.md — \"long-lived state\n * across many calls\"): the `Anthropic` client is heavy to construct\n * and designed to be reused; keeping it on `this` makes that reuse\n * explicit and aligns with the `new Anthropic(...)` upstream\n * convention.\n *\n * @example\n * const anthropic = new AnthropicSDK({ apiKey: process.env.ANTHROPIC_API_KEY! });\n * const model = anthropic.model({ name: \"claude-sonnet-4-6\", temperature: 0.7 });\n * const tokens = await anthropic.count(\"Hello world\");\n *\n * @example\n * // Compose into an `ai.anthropic` namespace for ergonomic agent wiring\n * const ai = { agent, tool, systemPrompt, anthropic: new AnthropicSDK({ apiKey }) };\n * const myAgent = ai.agent({ model: ai.anthropic.model({ name: \"claude-haiku-4-5\" }) });\n */\nexport class AnthropicSDK implements SDKAdapterContract {\n private readonly client: Anthropic;\n private readonly provider: string;\n private readonly pricing?: Record<string, ModelPricing>;\n\n public constructor(config: AnthropicSDKConfig) {\n // Peel off the framework-only keys and forward every other upstream\n // `ClientOptions` (timeout, maxRetries, defaultHeaders, fetch, …) verbatim\n // — they type-check, so dropping them is a silent footgun. Mirrors the\n // Bedrock/Google/Ollama adapters.\n const { provider, pricing, ...clientOptions } = config;\n\n this.client = new Anthropic(clientOptions);\n this.provider = provider ?? \"anthropic\";\n this.pricing = pricing;\n }\n\n /**\n * Build an `AnthropicModel` bound to this SDK's client. Each call\n * returns a fresh model instance, but all instances share the\n * underlying `Anthropic` client — connection pools, rate limits, and\n * authentication stay unified across every model produced here. The\n * SDK's `provider` label is forwarded so every model self-identifies\n * as coming from the same upstream.\n *\n * Pricing resolution: per-model `config.pricing` wins; otherwise the\n * SDK-level registry entry keyed by `config.name`; otherwise\n * `undefined` (no cost computed).\n */\n public model(config: AnthropicModelConfig): ModelContract {\n const resolvedPricing = config.pricing ?? this.pricing?.[config.name];\n const resolvedConfig: AnthropicModelConfig =\n resolvedPricing === config.pricing ? config : { ...config, pricing: resolvedPricing };\n\n return new AnthropicModel(this.client, resolvedConfig, this.provider);\n }\n\n /**\n * Rough token-count estimate for a given text. Uses the\n * character-heuristic (`approximateTokenCount`) from the core package\n * — good enough for budgeting and quota guards, not for billing.\n * Anthropic does expose a `messages.countTokens` endpoint, but that\n * is a network round-trip; `count()` is intentionally offline and\n * synchronous-cost. The optional model id is reserved for future\n * per-model tokenizer dispatch; currently ignored.\n */\n public async count(text: string, _model?: string): Promise<number> {\n return approximateTokenCount(text);\n }\n}\n"],"mappings":"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"}
package/esm/model.mjs CHANGED
@@ -155,6 +155,7 @@ var AnthropicModel = class {
155
155
  total: 0
156
156
  };
157
157
  const toolBlocks = /* @__PURE__ */ new Map();
158
+ const thinkingBlocks = /* @__PURE__ */ new Map();
158
159
  try {
159
160
  for await (const event of stream) {
160
161
  if (event.type === "message_start") {
@@ -172,6 +173,8 @@ var AnthropicModel = class {
172
173
  name: block.name,
173
174
  json: ""
174
175
  });
176
+ else if (block.type === "thinking") thinkingBlocks.set(event.index, { ...block });
177
+ else if (block.type === "redacted_thinking") thinkingBlocks.set(event.index, { ...block });
175
178
  continue;
176
179
  }
177
180
  if (event.type === "content_block_delta") {
@@ -182,6 +185,12 @@ var AnthropicModel = class {
182
185
  else if (event.delta.type === "input_json_delta") {
183
186
  const accumulator = toolBlocks.get(event.index);
184
187
  if (accumulator) accumulator.json += event.delta.partial_json;
188
+ } else if (event.delta.type === "thinking_delta") {
189
+ const thinking = thinkingBlocks.get(event.index);
190
+ if (thinking?.type === "thinking") thinking.thinking += event.delta.thinking;
191
+ } else if (event.delta.type === "signature_delta") {
192
+ const thinking = thinkingBlocks.get(event.index);
193
+ if (thinking?.type === "thinking") thinking.signature += event.delta.signature;
185
194
  }
186
195
  continue;
187
196
  }
@@ -192,7 +201,8 @@ var AnthropicModel = class {
192
201
  type: "tool-call",
193
202
  id: accumulator.id,
194
203
  name: accumulator.name,
195
- input: safeJsonParse(accumulator.json, {})
204
+ input: safeJsonParse(accumulator.json, {}),
205
+ ...this.buildThinkingMetadata(Array.from(thinkingBlocks.entries()).sort(([left], [right]) => left - right).map(([, block]) => block))
196
206
  };
197
207
  toolBlocks.delete(event.index);
198
208
  }
@@ -237,9 +247,13 @@ var AnthropicModel = class {
237
247
  const { system, messages: anthropicMessages } = toAnthropicMessages(messages);
238
248
  const thinking = this.buildThinking(options?.reasoning);
239
249
  const temperature = options?.temperature ?? this.config.temperature;
250
+ const configuredMaxTokens = options?.maxTokens ?? this.config.maxTokens;
251
+ 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);
253
+ if (thinkingBudget !== void 0 && configuredMaxTokens !== void 0 && configuredMaxTokens <= thinkingBudget) throw new Error(`Anthropic maxTokens (${configuredMaxTokens}) must be greater than thinking budget_tokens (${thinkingBudget})`);
240
254
  return {
241
255
  model: this.name,
242
- max_tokens: options?.maxTokens ?? this.config.maxTokens ?? DEFAULT_MAX_TOKENS,
256
+ max_tokens: maxTokens,
243
257
  messages: anthropicMessages,
244
258
  ...this.buildSystem(system, options?.cacheControl),
245
259
  ...temperature !== void 0 && !thinking.thinking ? { temperature } : {},
@@ -353,13 +367,24 @@ var AnthropicModel = class {
353
367
  extractToolCalls(content) {
354
368
  const toolUses = content.filter((block) => block.type === "tool_use");
355
369
  if (toolUses.length === 0) return;
370
+ const thinkingMetadata = this.buildThinkingMetadata(content);
356
371
  return toolUses.map((block) => ({
357
372
  id: block.id,
358
373
  name: block.name,
359
- input: block.input ?? {}
374
+ input: block.input ?? {},
375
+ ...thinkingMetadata
360
376
  }));
361
377
  }
362
378
  /**
379
+ * Preserve Anthropic's opaque extended-thinking blocks beside a tool
380
+ * request so the next assistant turn can replay their signatures exactly.
381
+ */
382
+ buildThinkingMetadata(blocks) {
383
+ const thinkingBlocks = Array.from(blocks).filter((block) => block.type === "thinking" || block.type === "redacted_thinking");
384
+ if (thinkingBlocks.length === 0) return {};
385
+ return { providerMetadata: { anthropic: { thinkingBlocks } } };
386
+ }
387
+ /**
363
388
  * Normalize Anthropic's `usage` block into the neutral `Usage` shape.
364
389
  * Anthropic reports `input_tokens` / `output_tokens` separately with
365
390
  * no pre-summed total, so `total` is computed. Cache-read tokens are
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\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\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 }\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 }\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 };\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\n return {\n model: this.name,\n max_tokens: options?.maxTokens ?? this.config.maxTokens ?? DEFAULT_MAX_TOKENS,\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 return toolUses.map((block) => ({\n id: block.id,\n name: block.name,\n input: (block.input ?? {}) as Record<string, unknown>,\n }));\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":"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"}
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":"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"}
@@ -47,7 +47,7 @@ function toAnthropicMessages(messages) {
47
47
  continue;
48
48
  }
49
49
  if (message.role === "assistant" && message.toolCalls && message.toolCalls.length > 0) {
50
- const blocks = [];
50
+ const blocks = extractThinkingBlocks(message.toolCalls);
51
51
  const text = stringifyContent(message.content);
52
52
  if (text) blocks.push({
53
53
  type: "text",
@@ -83,6 +83,35 @@ function toAnthropicMessages(messages) {
83
83
  };
84
84
  }
85
85
  /**
86
+ * Recover signed extended-thinking blocks stored by this adapter on a tool
87
+ * request. Anthropic requires these blocks to be replayed before the
88
+ * assistant's text and tool uses on the following request.
89
+ */
90
+ function extractThinkingBlocks(toolCalls) {
91
+ for (const toolCall of toolCalls) {
92
+ const anthropic = toolCall.providerMetadata?.anthropic;
93
+ if (!isRecord(anthropic) || !Array.isArray(anthropic.thinkingBlocks)) continue;
94
+ return anthropic.thinkingBlocks.flatMap(toAnthropicThinkingBlock);
95
+ }
96
+ return [];
97
+ }
98
+ function toAnthropicThinkingBlock(block) {
99
+ if (!isRecord(block) || typeof block.type !== "string") return [];
100
+ if (block.type === "thinking" && typeof block.thinking === "string" && typeof block.signature === "string") return [{
101
+ type: "thinking",
102
+ thinking: block.thinking,
103
+ signature: block.signature
104
+ }];
105
+ if (block.type === "redacted_thinking" && typeof block.data === "string") return [{
106
+ type: "redacted_thinking",
107
+ data: block.data
108
+ }];
109
+ return [];
110
+ }
111
+ function isRecord(value) {
112
+ return typeof value === "object" && value !== null;
113
+ }
114
+ /**
86
115
  * Multipart content is only meaningful on user messages — for any other
87
116
  * role collapse a `ContentPart[]` to its concatenated text so the wire
88
117
  * format stays valid. Plain strings pass through unchanged.
@@ -1 +1 @@
1
- {"version":3,"file":"to-anthropic-messages.mjs","names":[],"sources":["../../../../../../../ai-anthropic/src/utils/to-anthropic-messages.ts"],"sourcesContent":["import type { ContentPart, Message } from \"@warlock.js/ai\";\nimport type Anthropic from \"@anthropic-ai/sdk\";\n\n/**\n * Result of splitting a vendor-neutral `Message[]` for the Anthropic\n * Messages API: the system prompt is hoisted to a top-level `system`\n * string (Anthropic has no `\"system\"` role inside `messages`), and the\n * remaining turns are mapped to `MessageParam[]`.\n */\nexport type AnthropicMessages = {\n system: string | undefined;\n messages: Anthropic.MessageParam[];\n};\n\nconst ANTHROPIC_IMAGE_MEDIA_TYPES = [\"image/jpeg\", \"image/png\", \"image/gif\", \"image/webp\"] as const;\n\ntype AnthropicImageMediaType = (typeof ANTHROPIC_IMAGE_MEDIA_TYPES)[number];\n\n/**\n * Convert vendor-neutral `Message[]` into Anthropic's request shape.\n *\n * Anthropic differs from the OpenAI Chat protocol in three ways this\n * function absorbs:\n *\n * 1. **No `system` role.** System messages are concatenated (newline-\n * separated) and returned separately as the top-level `system`\n * parameter.\n * 2. **Tool results are `user` turns.** A neutral `tool` message becomes\n * a `user` message whose content is a single `tool_result` block\n * keyed by `tool_use_id`.\n * 3. **Tool calls are `tool_use` content blocks.** An assistant message\n * carrying `toolCalls` becomes an `assistant` message whose content\n * is an optional leading `text` block followed by one `tool_use`\n * block per call.\n *\n * Consecutive same-role turns are left as-is — the Messages API merges\n * them server-side.\n *\n * @example\n * const { system, messages } = toAnthropicMessages([\n * { role: \"system\", content: \"Be concise.\" },\n * { role: \"user\", content: \"Hi\" },\n * ]);\n * // system === \"Be concise.\"\n * // messages === [{ role: \"user\", content: \"Hi\" }]\n */\nexport function toAnthropicMessages(messages: Message[]): AnthropicMessages {\n const systemParts: string[] = [];\n const mapped: Anthropic.MessageParam[] = [];\n\n for (const message of messages) {\n if (message.role === \"system\") {\n systemParts.push(stringifyContent(message.content));\n\n continue;\n }\n\n if (message.role === \"tool\") {\n mapped.push({\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n tool_use_id: message.toolCallId ?? \"\",\n content: stringifyContent(message.content),\n },\n ],\n });\n\n continue;\n }\n\n if (message.role === \"assistant\" && message.toolCalls && message.toolCalls.length > 0) {\n const blocks: Anthropic.ContentBlockParam[] = [];\n const text = stringifyContent(message.content);\n\n if (text) {\n blocks.push({ type: \"text\", text });\n }\n\n for (const toolCall of message.toolCalls) {\n blocks.push({\n type: \"tool_use\",\n id: toolCall.id,\n name: toolCall.name,\n input: toolCall.input ?? {},\n });\n }\n\n mapped.push({ role: \"assistant\", content: blocks });\n\n continue;\n }\n\n if (message.role === \"user\" && Array.isArray(message.content)) {\n mapped.push({\n role: \"user\",\n content: message.content.map(toAnthropicContentBlock),\n });\n\n continue;\n }\n\n mapped.push({\n role: message.role === \"assistant\" ? \"assistant\" : \"user\",\n content: stringifyContent(message.content),\n });\n }\n\n return {\n system: systemParts.length > 0 ? systemParts.join(\"\\n\\n\") : undefined,\n messages: mapped,\n };\n}\n\n/**\n * Multipart content is only meaningful on user messages — for any other\n * role collapse a `ContentPart[]` to its concatenated text so the wire\n * format stays valid. Plain strings pass through unchanged.\n */\nfunction stringifyContent(content: string | ContentPart[]): string {\n if (typeof content === \"string\") {\n return content;\n }\n\n return content\n .filter((part): part is { type: \"text\"; text: string } => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\");\n}\n\n/**\n * Map a single resolved `ContentPart` to an Anthropic content block.\n * Text passes straight through; images become an `image` block with a\n * `url` source (remote) or a `base64` source (inlined bytes). The\n * agent has already resolved every attachment before it reaches here,\n * so this never reads files or fetches URLs.\n */\nfunction toAnthropicContentBlock(part: ContentPart): Anthropic.ContentBlockParam {\n if (part.type === \"text\") {\n return { type: \"text\", text: part.text };\n }\n\n // PDF → Anthropic `document` block (A2). The Messages API accepts a\n // url or base64 `application/pdf` source on PDF-capable Claude models.\n if (part.type === \"pdf\") {\n const source =\n \"url\" in part.source\n ? { type: \"url\", url: part.source.url }\n : { type: \"base64\", media_type: \"application/pdf\", data: part.source.base64 };\n return { type: \"document\", source } as unknown as Anthropic.ContentBlockParam;\n }\n\n // Anthropic has no audio content block; the agent's capability gate\n // normally prevents this, but stay loud rather than mis-map it.\n if (part.type === \"audio\") {\n throw new Error(\"Anthropic adapter: audio attachments are not supported\");\n }\n\n if (\"url\" in part.source) {\n return { type: \"image\", source: { type: \"url\", url: part.source.url } };\n }\n\n return {\n type: \"image\",\n source: {\n type: \"base64\",\n media_type: part.source.mediaType as AnthropicImageMediaType,\n data: part.source.base64,\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CA,SAAgB,oBAAoB,UAAwC;CAC1E,MAAM,cAAwB,CAAC;CAC/B,MAAM,SAAmC,CAAC;CAE1C,KAAK,MAAM,WAAW,UAAU;EAC9B,IAAI,QAAQ,SAAS,UAAU;GAC7B,YAAY,KAAK,iBAAiB,QAAQ,OAAO,CAAC;GAElD;EACF;EAEA,IAAI,QAAQ,SAAS,QAAQ;GAC3B,OAAO,KAAK;IACV,MAAM;IACN,SAAS,CACP;KACE,MAAM;KACN,aAAa,QAAQ,cAAc;KACnC,SAAS,iBAAiB,QAAQ,OAAO;IAC3C,CACF;GACF,CAAC;GAED;EACF;EAEA,IAAI,QAAQ,SAAS,eAAe,QAAQ,aAAa,QAAQ,UAAU,SAAS,GAAG;GACrF,MAAM,SAAwC,CAAC;GAC/C,MAAM,OAAO,iBAAiB,QAAQ,OAAO;GAE7C,IAAI,MACF,OAAO,KAAK;IAAE,MAAM;IAAQ;GAAK,CAAC;GAGpC,KAAK,MAAM,YAAY,QAAQ,WAC7B,OAAO,KAAK;IACV,MAAM;IACN,IAAI,SAAS;IACb,MAAM,SAAS;IACf,OAAO,SAAS,SAAS,CAAC;GAC5B,CAAC;GAGH,OAAO,KAAK;IAAE,MAAM;IAAa,SAAS;GAAO,CAAC;GAElD;EACF;EAEA,IAAI,QAAQ,SAAS,UAAU,MAAM,QAAQ,QAAQ,OAAO,GAAG;GAC7D,OAAO,KAAK;IACV,MAAM;IACN,SAAS,QAAQ,QAAQ,IAAI,uBAAuB;GACtD,CAAC;GAED;EACF;EAEA,OAAO,KAAK;GACV,MAAM,QAAQ,SAAS,cAAc,cAAc;GACnD,SAAS,iBAAiB,QAAQ,OAAO;EAC3C,CAAC;CACH;CAEA,OAAO;EACL,QAAQ,YAAY,SAAS,IAAI,YAAY,KAAK,MAAM,IAAI;EAC5D,UAAU;CACZ;AACF;;;;;;AAOA,SAAS,iBAAiB,SAAyC;CACjE,IAAI,OAAO,YAAY,UACrB,OAAO;CAGT,OAAO,QACJ,QAAQ,SAAiD,KAAK,SAAS,MAAM,CAAC,CAC9E,KAAK,SAAS,KAAK,IAAI,CAAC,CACxB,KAAK,EAAE;AACZ;;;;;;;;AASA,SAAS,wBAAwB,MAAgD;CAC/E,IAAI,KAAK,SAAS,QAChB,OAAO;EAAE,MAAM;EAAQ,MAAM,KAAK;CAAK;CAKzC,IAAI,KAAK,SAAS,OAKhB,OAAO;EAAE,MAAM;EAAY,QAHzB,SAAS,KAAK,SACV;GAAE,MAAM;GAAO,KAAK,KAAK,OAAO;EAAI,IACpC;GAAE,MAAM;GAAU,YAAY;GAAmB,MAAM,KAAK,OAAO;EAAO;CAC9C;CAKpC,IAAI,KAAK,SAAS,SAChB,MAAM,IAAI,MAAM,wDAAwD;CAG1E,IAAI,SAAS,KAAK,QAChB,OAAO;EAAE,MAAM;EAAS,QAAQ;GAAE,MAAM;GAAO,KAAK,KAAK,OAAO;EAAI;CAAE;CAGxE,OAAO;EACL,MAAM;EACN,QAAQ;GACN,MAAM;GACN,YAAY,KAAK,OAAO;GACxB,MAAM,KAAK,OAAO;EACpB;CACF;AACF"}
1
+ {"version":3,"file":"to-anthropic-messages.mjs","names":[],"sources":["../../../../../../../ai-anthropic/src/utils/to-anthropic-messages.ts"],"sourcesContent":["import type { ContentPart, Message, ModelToolCallRequest } from \"@warlock.js/ai\";\nimport type Anthropic from \"@anthropic-ai/sdk\";\n\n/**\n * Result of splitting a vendor-neutral `Message[]` for the Anthropic\n * Messages API: the system prompt is hoisted to a top-level `system`\n * string (Anthropic has no `\"system\"` role inside `messages`), and the\n * remaining turns are mapped to `MessageParam[]`.\n */\nexport type AnthropicMessages = {\n system: string | undefined;\n messages: Anthropic.MessageParam[];\n};\n\nconst ANTHROPIC_IMAGE_MEDIA_TYPES = [\"image/jpeg\", \"image/png\", \"image/gif\", \"image/webp\"] as const;\n\ntype AnthropicImageMediaType = (typeof ANTHROPIC_IMAGE_MEDIA_TYPES)[number];\n\n/**\n * Convert vendor-neutral `Message[]` into Anthropic's request shape.\n *\n * Anthropic differs from the OpenAI Chat protocol in three ways this\n * function absorbs:\n *\n * 1. **No `system` role.** System messages are concatenated (newline-\n * separated) and returned separately as the top-level `system`\n * parameter.\n * 2. **Tool results are `user` turns.** A neutral `tool` message becomes\n * a `user` message whose content is a single `tool_result` block\n * keyed by `tool_use_id`.\n * 3. **Tool calls are `tool_use` content blocks.** An assistant message\n * carrying `toolCalls` becomes an `assistant` message whose content\n * is an optional leading `text` block followed by one `tool_use`\n * block per call.\n *\n * Consecutive same-role turns are left as-is — the Messages API merges\n * them server-side.\n *\n * @example\n * const { system, messages } = toAnthropicMessages([\n * { role: \"system\", content: \"Be concise.\" },\n * { role: \"user\", content: \"Hi\" },\n * ]);\n * // system === \"Be concise.\"\n * // messages === [{ role: \"user\", content: \"Hi\" }]\n */\nexport function toAnthropicMessages(messages: Message[]): AnthropicMessages {\n const systemParts: string[] = [];\n const mapped: Anthropic.MessageParam[] = [];\n\n for (const message of messages) {\n if (message.role === \"system\") {\n systemParts.push(stringifyContent(message.content));\n\n continue;\n }\n\n if (message.role === \"tool\") {\n mapped.push({\n role: \"user\",\n content: [\n {\n type: \"tool_result\",\n tool_use_id: message.toolCallId ?? \"\",\n content: stringifyContent(message.content),\n },\n ],\n });\n\n continue;\n }\n\n if (message.role === \"assistant\" && message.toolCalls && message.toolCalls.length > 0) {\n const blocks: Anthropic.ContentBlockParam[] = extractThinkingBlocks(message.toolCalls);\n const text = stringifyContent(message.content);\n\n if (text) {\n blocks.push({ type: \"text\", text });\n }\n\n for (const toolCall of message.toolCalls) {\n blocks.push({\n type: \"tool_use\",\n id: toolCall.id,\n name: toolCall.name,\n input: toolCall.input ?? {},\n });\n }\n\n mapped.push({ role: \"assistant\", content: blocks });\n\n continue;\n }\n\n if (message.role === \"user\" && Array.isArray(message.content)) {\n mapped.push({\n role: \"user\",\n content: message.content.map(toAnthropicContentBlock),\n });\n\n continue;\n }\n\n mapped.push({\n role: message.role === \"assistant\" ? \"assistant\" : \"user\",\n content: stringifyContent(message.content),\n });\n }\n\n return {\n system: systemParts.length > 0 ? systemParts.join(\"\\n\\n\") : undefined,\n messages: mapped,\n };\n}\n\n/**\n * Recover signed extended-thinking blocks stored by this adapter on a tool\n * request. Anthropic requires these blocks to be replayed before the\n * assistant's text and tool uses on the following request.\n */\nfunction extractThinkingBlocks(toolCalls: ModelToolCallRequest[]): Anthropic.ContentBlockParam[] {\n for (const toolCall of toolCalls) {\n const anthropic = toolCall.providerMetadata?.anthropic;\n\n if (!isRecord(anthropic) || !Array.isArray(anthropic.thinkingBlocks)) {\n continue;\n }\n\n return anthropic.thinkingBlocks.flatMap(toAnthropicThinkingBlock);\n }\n\n return [];\n}\n\nfunction toAnthropicThinkingBlock(block: unknown): Anthropic.ContentBlockParam[] {\n if (!isRecord(block) || typeof block.type !== \"string\") {\n return [];\n }\n\n if (\n block.type === \"thinking\" &&\n typeof block.thinking === \"string\" &&\n typeof block.signature === \"string\"\n ) {\n return [{ type: \"thinking\", thinking: block.thinking, signature: block.signature }];\n }\n\n if (block.type === \"redacted_thinking\" && typeof block.data === \"string\") {\n return [{ type: \"redacted_thinking\", data: block.data }];\n }\n\n return [];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\n/**\n * Multipart content is only meaningful on user messages — for any other\n * role collapse a `ContentPart[]` to its concatenated text so the wire\n * format stays valid. Plain strings pass through unchanged.\n */\nfunction stringifyContent(content: string | ContentPart[]): string {\n if (typeof content === \"string\") {\n return content;\n }\n\n return content\n .filter((part): part is { type: \"text\"; text: string } => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\");\n}\n\n/**\n * Map a single resolved `ContentPart` to an Anthropic content block.\n * Text passes straight through; images become an `image` block with a\n * `url` source (remote) or a `base64` source (inlined bytes). The\n * agent has already resolved every attachment before it reaches here,\n * so this never reads files or fetches URLs.\n */\nfunction toAnthropicContentBlock(part: ContentPart): Anthropic.ContentBlockParam {\n if (part.type === \"text\") {\n return { type: \"text\", text: part.text };\n }\n\n // PDF → Anthropic `document` block (A2). The Messages API accepts a\n // url or base64 `application/pdf` source on PDF-capable Claude models.\n if (part.type === \"pdf\") {\n const source =\n \"url\" in part.source\n ? { type: \"url\", url: part.source.url }\n : { type: \"base64\", media_type: \"application/pdf\", data: part.source.base64 };\n return { type: \"document\", source } as unknown as Anthropic.ContentBlockParam;\n }\n\n // Anthropic has no audio content block; the agent's capability gate\n // normally prevents this, but stay loud rather than mis-map it.\n if (part.type === \"audio\") {\n throw new Error(\"Anthropic adapter: audio attachments are not supported\");\n }\n\n if (\"url\" in part.source) {\n return { type: \"image\", source: { type: \"url\", url: part.source.url } };\n }\n\n return {\n type: \"image\",\n source: {\n type: \"base64\",\n media_type: part.source.mediaType as AnthropicImageMediaType,\n data: part.source.base64,\n },\n };\n}\n"],"mappings":"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"}
package/llms-full.txt CHANGED
@@ -89,6 +89,8 @@ In streaming, `cachedTokens` / `cacheWriteTokens` are seeded from `message_start
89
89
 
90
90
  Unlike OpenAI, Anthropic **requires** `max_tokens` on every request. Resolution: per-call `options.maxTokens` > `config.maxTokens` > **default `4096`**. A caller who never sets a cap still gets a complete answer instead of a 400.
91
91
 
92
+ With extended thinking enabled and no explicit cap, the adapter reserves answer room: `max_tokens = thinking budget + 4096`. An explicit `maxTokens` must be greater than the thinking budget or the call throws before it reaches Anthropic.
93
+
92
94
  ## System prompt
93
95
 
94
96
  Anthropic has no `"system"` role inside `messages`. The adapter hoists every neutral `role: "system"` message into the top-level `system` parameter (multiple system messages join with a blank line). Transparent to the agent.
@@ -120,6 +122,8 @@ await agent.execute("Prove it.", { reasoning: { effort: "high" } }); // budget
120
122
  await model.complete(messages, { reasoning: { maxTokens: 8000 } }); // budget_tokens: 8000
121
123
  ```
122
124
 
125
+ When a thinking-enabled response also makes tool calls, its signed `thinking` and `redacted_thinking` blocks are retained in each call's `providerMetadata.anthropic.thinkingBlocks`. The adapter replays those opaque blocks automatically on the next assistant turn, preserving Anthropic's required signatures; application code should leave that metadata intact when persisting message history.
126
+
123
127
  ## Prompt caching (`cache_control`)
124
128
 
125
129
  Two independent breakpoint sites, both reported back via `usage.cachedTokens` / `usage.cacheWriteTokens`:
package/package.json CHANGED
@@ -14,13 +14,13 @@
14
14
  "url": "https://github.com/warlockjs/ai-anthropic"
15
15
  },
16
16
  "peerDependencies": {
17
- "@warlock.js/ai": "5.23.1"
17
+ "@warlock.js/ai": "5.24.0"
18
18
  },
19
19
  "dependencies": {
20
20
  "@anthropic-ai/sdk": "^0.96.0",
21
- "@warlock.js/logger": "5.23.1"
21
+ "@warlock.js/logger": "5.24.0"
22
22
  },
23
- "version": "5.23.1",
23
+ "version": "5.24.0",
24
24
  "main": "./cjs/index.cjs",
25
25
  "module": "./esm/index.mjs",
26
26
  "types": "./esm/index.d.mts",
@@ -81,6 +81,8 @@ In streaming, `cachedTokens` / `cacheWriteTokens` are seeded from `message_start
81
81
 
82
82
  Unlike OpenAI, Anthropic **requires** `max_tokens` on every request. Resolution: per-call `options.maxTokens` > `config.maxTokens` > **default `4096`**. A caller who never sets a cap still gets a complete answer instead of a 400.
83
83
 
84
+ With extended thinking enabled and no explicit cap, the adapter reserves answer room: `max_tokens = thinking budget + 4096`. An explicit `maxTokens` must be greater than the thinking budget or the call throws before it reaches Anthropic.
85
+
84
86
  ## System prompt
85
87
 
86
88
  Anthropic has no `"system"` role inside `messages`. The adapter hoists every neutral `role: "system"` message into the top-level `system` parameter (multiple system messages join with a blank line). Transparent to the agent.
@@ -112,6 +114,8 @@ await agent.execute("Prove it.", { reasoning: { effort: "high" } }); // budget
112
114
  await model.complete(messages, { reasoning: { maxTokens: 8000 } }); // budget_tokens: 8000
113
115
  ```
114
116
 
117
+ When a thinking-enabled response also makes tool calls, its signed `thinking` and `redacted_thinking` blocks are retained in each call's `providerMetadata.anthropic.thinkingBlocks`. The adapter replays those opaque blocks automatically on the next assistant turn, preserving Anthropic's required signatures; application code should leave that metadata intact when persisting message history.
118
+
115
119
  ## Prompt caching (`cache_control`)
116
120
 
117
121
  Two independent breakpoint sites, both reported back via `usage.cachedTokens` / `usage.cacheWriteTokens`: