@warlock.js/ai-anthropic 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm/sdk.d.mts ADDED
@@ -0,0 +1,70 @@
1
+ import { AnthropicModelConfig, AnthropicSDKConfig } from "./config.type.mjs";
2
+ import { ModelContract, SDKAdapterContract } from "@warlock.js/ai";
3
+
4
+ //#region ../../@warlock.js/ai-anthropic/src/sdk.d.ts
5
+ /**
6
+ * Anthropic-backed implementation of `SDKAdapterContract`.
7
+ *
8
+ * **Role.** The package entry point for Claude models via the official
9
+ * `@anthropic-ai/sdk`. A single `AnthropicSDK` instance holds one live
10
+ * `Anthropic` client, shared by every `ModelContract` it produces via
11
+ * `model()`. Users construct one SDK per account and reuse it across
12
+ * all agents, workflows, and supervisors that target Anthropic.
13
+ *
14
+ * **Responsibility.**
15
+ * - Owns: a long-lived `Anthropic` client (authentication, base URL)
16
+ * and its lifetime scope. Factory for `AnthropicModel` instances —
17
+ * each model call gets a reference to the same client.
18
+ * - Does NOT own: anything per-call (tool execution, message history,
19
+ * streaming loop) — those live in `AnthropicModel` and the agent
20
+ * runtime. Does NOT implement `embedder()`: Anthropic ships no
21
+ * first-party embeddings API (the contract marks it optional).
22
+ *
23
+ * Modeled as a class (see §4.2 of code-style.md — "long-lived state
24
+ * across many calls"): the `Anthropic` client is heavy to construct
25
+ * and designed to be reused; keeping it on `this` makes that reuse
26
+ * explicit and aligns with the `new Anthropic(...)` upstream
27
+ * convention.
28
+ *
29
+ * @example
30
+ * const anthropic = new AnthropicSDK({ apiKey: process.env.ANTHROPIC_API_KEY! });
31
+ * const model = anthropic.model({ name: "claude-sonnet-4-6", temperature: 0.7 });
32
+ * const tokens = await anthropic.count("Hello world");
33
+ *
34
+ * @example
35
+ * // Compose into an `ai.anthropic` namespace for ergonomic agent wiring
36
+ * const ai = { agent, tool, systemPrompt, anthropic: new AnthropicSDK({ apiKey }) };
37
+ * const myAgent = ai.agent({ model: ai.anthropic.model({ name: "claude-haiku-4-5" }) });
38
+ */
39
+ declare class AnthropicSDK implements SDKAdapterContract {
40
+ private readonly client;
41
+ private readonly provider;
42
+ private readonly pricing?;
43
+ constructor(config: AnthropicSDKConfig);
44
+ /**
45
+ * Build an `AnthropicModel` bound to this SDK's client. Each call
46
+ * returns a fresh model instance, but all instances share the
47
+ * underlying `Anthropic` client — connection pools, rate limits, and
48
+ * authentication stay unified across every model produced here. The
49
+ * SDK's `provider` label is forwarded so every model self-identifies
50
+ * as coming from the same upstream.
51
+ *
52
+ * Pricing resolution: per-model `config.pricing` wins; otherwise the
53
+ * SDK-level registry entry keyed by `config.name`; otherwise
54
+ * `undefined` (no cost computed).
55
+ */
56
+ model(config: AnthropicModelConfig): ModelContract;
57
+ /**
58
+ * Rough token-count estimate for a given text. Uses the
59
+ * character-heuristic (`approximateTokenCount`) from the core package
60
+ * — good enough for budgeting and quota guards, not for billing.
61
+ * Anthropic does expose a `messages.countTokens` endpoint, but that
62
+ * is a network round-trip; `count()` is intentionally offline and
63
+ * synchronous-cost. The optional model id is reserved for future
64
+ * per-model tokenizer dispatch; currently ignored.
65
+ */
66
+ count(text: string, _model?: string): Promise<number>;
67
+ }
68
+ //#endregion
69
+ export { AnthropicSDK };
70
+ //# sourceMappingURL=sdk.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk.d.mts","names":[],"sources":["../../../../../@warlock.js/ai-anthropic/src/sdk.ts"],"mappings":";;;;;;AAwCA;;;;;;;;;;;;;;;;;;;;;;;;;;AA2C4D;;;;;;cA3C/C,YAAA,YAAwB,kBAAA;EAAA,iBAClB,MAAA;EAAA,iBACA,QAAA;EAAA,iBACA,OAAA;cAEE,MAAA,EAAQ,kBAAA;;;;;;;;;;;;;EAqBpB,KAAA,CAAM,MAAA,EAAQ,oBAAA,GAAuB,aAAA;;;;;;;;;;EAiB/B,KAAA,CAAM,IAAA,UAAc,MAAA,YAAkB,OAAA;AAAA"}
package/esm/sdk.mjs ADDED
@@ -0,0 +1,85 @@
1
+ import { AnthropicModel } from "./model.mjs";
2
+ import Anthropic from "@anthropic-ai/sdk";
3
+ import { approximateTokenCount } from "@warlock.js/ai";
4
+
5
+ //#region ../../@warlock.js/ai-anthropic/src/sdk.ts
6
+ /**
7
+ * Anthropic-backed implementation of `SDKAdapterContract`.
8
+ *
9
+ * **Role.** The package entry point for Claude models via the official
10
+ * `@anthropic-ai/sdk`. A single `AnthropicSDK` instance holds one live
11
+ * `Anthropic` client, shared by every `ModelContract` it produces via
12
+ * `model()`. Users construct one SDK per account and reuse it across
13
+ * all agents, workflows, and supervisors that target Anthropic.
14
+ *
15
+ * **Responsibility.**
16
+ * - Owns: a long-lived `Anthropic` client (authentication, base URL)
17
+ * and its lifetime scope. Factory for `AnthropicModel` instances —
18
+ * each model call gets a reference to the same client.
19
+ * - Does NOT own: anything per-call (tool execution, message history,
20
+ * streaming loop) — those live in `AnthropicModel` and the agent
21
+ * runtime. Does NOT implement `embedder()`: Anthropic ships no
22
+ * first-party embeddings API (the contract marks it optional).
23
+ *
24
+ * Modeled as a class (see §4.2 of code-style.md — "long-lived state
25
+ * across many calls"): the `Anthropic` client is heavy to construct
26
+ * and designed to be reused; keeping it on `this` makes that reuse
27
+ * explicit and aligns with the `new Anthropic(...)` upstream
28
+ * convention.
29
+ *
30
+ * @example
31
+ * const anthropic = new AnthropicSDK({ apiKey: process.env.ANTHROPIC_API_KEY! });
32
+ * const model = anthropic.model({ name: "claude-sonnet-4-6", temperature: 0.7 });
33
+ * const tokens = await anthropic.count("Hello world");
34
+ *
35
+ * @example
36
+ * // Compose into an `ai.anthropic` namespace for ergonomic agent wiring
37
+ * const ai = { agent, tool, systemPrompt, anthropic: new AnthropicSDK({ apiKey }) };
38
+ * const myAgent = ai.agent({ model: ai.anthropic.model({ name: "claude-haiku-4-5" }) });
39
+ */
40
+ var AnthropicSDK = class {
41
+ constructor(config) {
42
+ this.client = new Anthropic({
43
+ apiKey: config.apiKey,
44
+ baseURL: config.baseURL
45
+ });
46
+ this.provider = config.provider ?? "anthropic";
47
+ this.pricing = config.pricing;
48
+ }
49
+ /**
50
+ * Build an `AnthropicModel` bound to this SDK's client. Each call
51
+ * returns a fresh model instance, but all instances share the
52
+ * underlying `Anthropic` client — connection pools, rate limits, and
53
+ * authentication stay unified across every model produced here. The
54
+ * SDK's `provider` label is forwarded so every model self-identifies
55
+ * as coming from the same upstream.
56
+ *
57
+ * Pricing resolution: per-model `config.pricing` wins; otherwise the
58
+ * SDK-level registry entry keyed by `config.name`; otherwise
59
+ * `undefined` (no cost computed).
60
+ */
61
+ model(config) {
62
+ const resolvedPricing = config.pricing ?? this.pricing?.[config.name];
63
+ const resolvedConfig = resolvedPricing === config.pricing ? config : {
64
+ ...config,
65
+ pricing: resolvedPricing
66
+ };
67
+ return new AnthropicModel(this.client, resolvedConfig, this.provider);
68
+ }
69
+ /**
70
+ * Rough token-count estimate for a given text. Uses the
71
+ * character-heuristic (`approximateTokenCount`) from the core package
72
+ * — good enough for budgeting and quota guards, not for billing.
73
+ * Anthropic does expose a `messages.countTokens` endpoint, but that
74
+ * is a network round-trip; `count()` is intentionally offline and
75
+ * synchronous-cost. The optional model id is reserved for future
76
+ * per-model tokenizer dispatch; currently ignored.
77
+ */
78
+ async count(text, _model) {
79
+ return approximateTokenCount(text);
80
+ }
81
+ };
82
+
83
+ //#endregion
84
+ export { AnthropicSDK };
85
+ //# sourceMappingURL=sdk.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk.mjs","names":[],"sources":["../../../../../@warlock.js/ai-anthropic/src/sdk.ts"],"sourcesContent":["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 this.client = new Anthropic({\n apiKey: config.apiKey,\n baseURL: config.baseURL,\n });\n this.provider = config.provider ?? \"anthropic\";\n this.pricing = config.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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,IAAa,eAAb,MAAwD;CAKtD,AAAO,YAAY,QAA4B;EAC7C,KAAK,SAAS,IAAI,UAAU;GAC1B,QAAQ,OAAO;GACf,SAAS,OAAO;EAClB,CAAC;EACD,KAAK,WAAW,OAAO,YAAY;EACnC,KAAK,UAAU,OAAO;CACxB;;;;;;;;;;;;;CAcA,AAAO,MAAM,QAA6C;EACxD,MAAM,kBAAkB,OAAO,WAAW,KAAK,UAAU,OAAO;EAChE,MAAM,iBACJ,oBAAoB,OAAO,UAAU,SAAS;GAAE,GAAG;GAAQ,SAAS;EAAgB;EAEtF,OAAO,IAAI,eAAe,KAAK,QAAQ,gBAAgB,KAAK,QAAQ;CACtE;;;;;;;;;;CAWA,MAAa,MAAM,MAAc,QAAkC;EACjE,OAAO,sBAAsB,IAAI;CACnC;AACF"}
@@ -0,0 +1,6 @@
1
+ import { mapStopReason } from "./map-stop-reason.mjs";
2
+ import { toAnthropicMessages } from "./to-anthropic-messages.mjs";
3
+ import { toAnthropicTools } from "./to-anthropic-tools.mjs";
4
+ import { wrapAnthropicError } from "./wrap-anthropic-error.mjs";
5
+
6
+ export { };
@@ -0,0 +1,30 @@
1
+ //#region ../../@warlock.js/ai-anthropic/src/utils/map-stop-reason.ts
2
+ const stopReasonMap = {
3
+ end_turn: "stop",
4
+ stop_sequence: "stop",
5
+ max_tokens: "length",
6
+ tool_use: "tool_calls"
7
+ };
8
+ /**
9
+ * Map Anthropic's `stop_reason` to the normalized `FinishReason` union.
10
+ *
11
+ * `end_turn` / `stop_sequence` are both natural stops. `max_tokens`
12
+ * maps to `length`. `tool_use` maps to `tool_calls`. Everything else —
13
+ * `refusal` (policy intervention), `pause_turn` (incomplete
14
+ * long-running turn), `null`, or any value a future API version adds —
15
+ * falls through to `"error"` so the agent treats the trip as a
16
+ * non-clean terminal rather than silently accepting a partial result.
17
+ *
18
+ * @example
19
+ * mapStopReason("end_turn"); // "stop"
20
+ * mapStopReason("tool_use"); // "tool_calls"
21
+ * mapStopReason("refusal"); // "error"
22
+ * mapStopReason(null); // "error"
23
+ */
24
+ function mapStopReason(raw) {
25
+ return stopReasonMap[raw ?? ""] ?? "error";
26
+ }
27
+
28
+ //#endregion
29
+ export { mapStopReason };
30
+ //# sourceMappingURL=map-stop-reason.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map-stop-reason.mjs","names":[],"sources":["../../../../../../@warlock.js/ai-anthropic/src/utils/map-stop-reason.ts"],"sourcesContent":["import type { FinishReason } from \"@warlock.js/ai\";\n\nconst stopReasonMap: Record<string, FinishReason> = {\n end_turn: \"stop\",\n stop_sequence: \"stop\",\n max_tokens: \"length\",\n tool_use: \"tool_calls\",\n};\n\n/**\n * Map Anthropic's `stop_reason` to the normalized `FinishReason` union.\n *\n * `end_turn` / `stop_sequence` are both natural stops. `max_tokens`\n * maps to `length`. `tool_use` maps to `tool_calls`. Everything else —\n * `refusal` (policy intervention), `pause_turn` (incomplete\n * long-running turn), `null`, or any value a future API version adds —\n * falls through to `\"error\"` so the agent treats the trip as a\n * non-clean terminal rather than silently accepting a partial result.\n *\n * @example\n * mapStopReason(\"end_turn\"); // \"stop\"\n * mapStopReason(\"tool_use\"); // \"tool_calls\"\n * mapStopReason(\"refusal\"); // \"error\"\n * mapStopReason(null); // \"error\"\n */\nexport function mapStopReason(raw: string | null | undefined): FinishReason {\n return stopReasonMap[raw ?? \"\"] ?? \"error\";\n}\n"],"mappings":";AAEA,MAAM,gBAA8C;CAClD,UAAU;CACV,eAAe;CACf,YAAY;CACZ,UAAU;AACZ;;;;;;;;;;;;;;;;;AAkBA,SAAgB,cAAc,KAA8C;CAC1E,OAAO,cAAc,OAAO,OAAO;AACrC"}
@@ -0,0 +1,125 @@
1
+ //#region ../../@warlock.js/ai-anthropic/src/utils/to-anthropic-messages.ts
2
+ /**
3
+ * Convert vendor-neutral `Message[]` into Anthropic's request shape.
4
+ *
5
+ * Anthropic differs from the OpenAI Chat protocol in three ways this
6
+ * function absorbs:
7
+ *
8
+ * 1. **No `system` role.** System messages are concatenated (newline-
9
+ * separated) and returned separately as the top-level `system`
10
+ * parameter.
11
+ * 2. **Tool results are `user` turns.** A neutral `tool` message becomes
12
+ * a `user` message whose content is a single `tool_result` block
13
+ * keyed by `tool_use_id`.
14
+ * 3. **Tool calls are `tool_use` content blocks.** An assistant message
15
+ * carrying `toolCalls` becomes an `assistant` message whose content
16
+ * is an optional leading `text` block followed by one `tool_use`
17
+ * block per call.
18
+ *
19
+ * Consecutive same-role turns are left as-is — the Messages API merges
20
+ * them server-side.
21
+ *
22
+ * @example
23
+ * const { system, messages } = toAnthropicMessages([
24
+ * { role: "system", content: "Be concise." },
25
+ * { role: "user", content: "Hi" },
26
+ * ]);
27
+ * // system === "Be concise."
28
+ * // messages === [{ role: "user", content: "Hi" }]
29
+ */
30
+ function toAnthropicMessages(messages) {
31
+ const systemParts = [];
32
+ const mapped = [];
33
+ for (const message of messages) {
34
+ if (message.role === "system") {
35
+ systemParts.push(stringifyContent(message.content));
36
+ continue;
37
+ }
38
+ if (message.role === "tool") {
39
+ mapped.push({
40
+ role: "user",
41
+ content: [{
42
+ type: "tool_result",
43
+ tool_use_id: message.toolCallId ?? "",
44
+ content: stringifyContent(message.content)
45
+ }]
46
+ });
47
+ continue;
48
+ }
49
+ if (message.role === "assistant" && message.toolCalls && message.toolCalls.length > 0) {
50
+ const blocks = [];
51
+ const text = stringifyContent(message.content);
52
+ if (text) blocks.push({
53
+ type: "text",
54
+ text
55
+ });
56
+ for (const toolCall of message.toolCalls) blocks.push({
57
+ type: "tool_use",
58
+ id: toolCall.id,
59
+ name: toolCall.name,
60
+ input: toolCall.input ?? {}
61
+ });
62
+ mapped.push({
63
+ role: "assistant",
64
+ content: blocks
65
+ });
66
+ continue;
67
+ }
68
+ if (message.role === "user" && Array.isArray(message.content)) {
69
+ mapped.push({
70
+ role: "user",
71
+ content: message.content.map(toAnthropicContentBlock)
72
+ });
73
+ continue;
74
+ }
75
+ mapped.push({
76
+ role: message.role === "assistant" ? "assistant" : "user",
77
+ content: stringifyContent(message.content)
78
+ });
79
+ }
80
+ return {
81
+ system: systemParts.length > 0 ? systemParts.join("\n\n") : void 0,
82
+ messages: mapped
83
+ };
84
+ }
85
+ /**
86
+ * Multipart content is only meaningful on user messages — for any other
87
+ * role collapse a `ContentPart[]` to its concatenated text so the wire
88
+ * format stays valid. Plain strings pass through unchanged.
89
+ */
90
+ function stringifyContent(content) {
91
+ if (typeof content === "string") return content;
92
+ return content.filter((part) => part.type === "text").map((part) => part.text).join("");
93
+ }
94
+ /**
95
+ * Map a single resolved `ContentPart` to an Anthropic content block.
96
+ * Text passes straight through; images become an `image` block with a
97
+ * `url` source (remote) or a `base64` source (inlined bytes). The
98
+ * agent has already resolved every attachment before it reaches here,
99
+ * so this never reads files or fetches URLs.
100
+ */
101
+ function toAnthropicContentBlock(part) {
102
+ if (part.type === "text") return {
103
+ type: "text",
104
+ text: part.text
105
+ };
106
+ if ("url" in part.source) return {
107
+ type: "image",
108
+ source: {
109
+ type: "url",
110
+ url: part.source.url
111
+ }
112
+ };
113
+ return {
114
+ type: "image",
115
+ source: {
116
+ type: "base64",
117
+ media_type: part.source.mediaType,
118
+ data: part.source.base64
119
+ }
120
+ };
121
+ }
122
+
123
+ //#endregion
124
+ export { toAnthropicMessages };
125
+ //# sourceMappingURL=to-anthropic-messages.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-anthropic-messages.mjs","names":[],"sources":["../../../../../../@warlock.js/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 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,EAC7E,KAAK,SAAS,KAAK,IAAI,EACvB,KAAK,EAAE;AACZ;;;;;;;;AASA,SAAS,wBAAwB,MAAgD;CAC/E,IAAI,KAAK,SAAS,QAChB,OAAO;EAAE,MAAM;EAAQ,MAAM,KAAK;CAAK;CAGzC,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"}
@@ -0,0 +1,37 @@
1
+ import { extractJsonSchema } from "@warlock.js/ai";
2
+
3
+ //#region ../../@warlock.js/ai-anthropic/src/utils/to-anthropic-tools.ts
4
+ /**
5
+ * Convert vendor-neutral `ToolConfig[]` into Anthropic's `tools` array.
6
+ * Uses the shared `extractJsonSchema` helper; Anthropic requires the
7
+ * input schema to be a JSON-Schema object, so a non-object extraction
8
+ * is coerced into an empty-object schema rather than rejected — the
9
+ * tool still registers and the model simply sees no parameters.
10
+ *
11
+ * @example
12
+ * const tools = toAnthropicTools([weatherTool, calculatorTool]);
13
+ * await client.messages.create({ model, max_tokens, messages, tools });
14
+ */
15
+ function toAnthropicTools(tools) {
16
+ if (!tools || tools.length === 0) return;
17
+ return tools.map((tool) => ({
18
+ name: tool.name,
19
+ description: tool.description,
20
+ input_schema: toInputSchema(tool.input)
21
+ }));
22
+ }
23
+ /**
24
+ * Coerce the extracted JSON Schema into Anthropic's `Tool.InputSchema`
25
+ * shape (root must be `{ type: "object" }`). Anything that isn't an
26
+ * object schema degrades to a parameterless object so registration
27
+ * never fails on a malformed extractor result.
28
+ */
29
+ function toInputSchema(input) {
30
+ const schema = extractJsonSchema(input);
31
+ if (schema && schema.type === "object") return schema;
32
+ return { type: "object" };
33
+ }
34
+
35
+ //#endregion
36
+ export { toAnthropicTools };
37
+ //# sourceMappingURL=to-anthropic-tools.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"to-anthropic-tools.mjs","names":[],"sources":["../../../../../../@warlock.js/ai-anthropic/src/utils/to-anthropic-tools.ts"],"sourcesContent":["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"],"mappings":";;;;;;;;;;;;;;AAcA,SAAgB,iBACd,OAC8B;CAC9B,IAAI,CAAC,SAAS,MAAM,WAAW,GAC7B;CAGF,OAAO,MAAM,KAAK,UAAU;EAC1B,MAAM,KAAK;EACX,aAAa,KAAK;EAClB,cAAc,cAAc,KAAK,KAAK;CACxC,EAAE;AACJ;;;;;;;AAQA,SAAS,cAAc,OAA0E;CAC/F,MAAM,SAAS,kBAAkB,KAAK;CAEtC,IAAI,UAAU,OAAO,SAAS,UAC5B,OAAO;CAGT,OAAO,EAAE,MAAM,SAAS;AAC1B"}
@@ -0,0 +1,158 @@
1
+ import { APIConnectionTimeoutError, APIError } from "@anthropic-ai/sdk";
2
+ import { AIError, ContextLengthExceededError, InvalidRequestError, ProviderAuthError, ProviderError, ProviderRateLimitError, ProviderTimeoutError, QuotaExceededError } from "@warlock.js/ai";
3
+
4
+ //#region ../../@warlock.js/ai-anthropic/src/utils/wrap-anthropic-error.ts
5
+ /**
6
+ * Wrap any thrown value caught inside the Anthropic adapter into the
7
+ * appropriate `@warlock.js/ai` `AIError` subclass.
8
+ *
9
+ * **Dispatch strategy.** Anthropic has no per-error machine `code`; the
10
+ * stable identifier is `error.type` on the response body (surfaced as
11
+ * `APIError.type`). Dispatch prefers `type`, falls back to `status`
12
+ * when the body was stripped (common with proxies). Name-based
13
+ * detection catches transport-layer timeouts that never produced an
14
+ * HTTP response. The `invalid_request_error` branch additionally
15
+ * sniffs the message for Anthropic's "prompt is too long" phrasing,
16
+ * which is the only signal that a 400 was a context-length overflow.
17
+ *
18
+ * `AIError` instances pass through unchanged so `catch/throw wrap(e)`
19
+ * pipelines never double-wrap.
20
+ *
21
+ * @example
22
+ * try {
23
+ * return await this.client.messages.create(...);
24
+ * } catch (thrown) {
25
+ * throw wrapAnthropicError(thrown);
26
+ * }
27
+ */
28
+ function wrapAnthropicError(thrown) {
29
+ if (thrown instanceof AIError) return thrown;
30
+ const shape = toShape(thrown);
31
+ const context = buildContext(shape);
32
+ const message = shape.message ?? (thrown instanceof Error ? thrown.message : String(thrown));
33
+ if (isTimeout(thrown, shape)) return new ProviderTimeoutError(message, {
34
+ cause: thrown,
35
+ context
36
+ });
37
+ if (shape.type === "authentication_error" || shape.type === "permission_error") return new ProviderAuthError(message, {
38
+ cause: thrown,
39
+ context
40
+ });
41
+ if (shape.status === 401 || shape.status === 403) return new ProviderAuthError(message, {
42
+ cause: thrown,
43
+ context
44
+ });
45
+ if (shape.type === "billing_error") return new QuotaExceededError(message, {
46
+ cause: thrown,
47
+ context
48
+ });
49
+ if (shape.type === "rate_limit_error" || shape.status === 429) return new ProviderRateLimitError(message, {
50
+ cause: thrown,
51
+ context,
52
+ retryAfter: parseRetryAfter(shape.headers)
53
+ });
54
+ if (shape.type === "invalid_request_error" || isClientStatus(shape.status)) {
55
+ if (/prompt is too long/i.test(message)) return new ContextLengthExceededError(message, {
56
+ cause: thrown,
57
+ context
58
+ });
59
+ return new InvalidRequestError(message, {
60
+ cause: thrown,
61
+ context
62
+ });
63
+ }
64
+ return new ProviderError(message, {
65
+ cause: thrown,
66
+ context
67
+ });
68
+ }
69
+ /**
70
+ * Read the raw error shape without depending on `instanceof APIError`
71
+ * — proxies and re-wrappers strip the prototype chain. Duck-typing on
72
+ * the visible fields is resilient to both.
73
+ */
74
+ function toShape(thrown) {
75
+ if (thrown instanceof APIError) return {
76
+ status: typeof thrown.status === "number" ? thrown.status : void 0,
77
+ type: thrown.type,
78
+ message: thrown.message,
79
+ headers: thrown.headers,
80
+ name: thrown.name,
81
+ requestId: thrown.requestID ?? void 0
82
+ };
83
+ if (typeof thrown === "object" && thrown !== null) {
84
+ const raw = thrown;
85
+ return {
86
+ status: typeof raw.status === "number" ? raw.status : void 0,
87
+ type: typeof raw.type === "string" ? raw.type : void 0,
88
+ message: typeof raw.message === "string" ? raw.message : void 0,
89
+ headers: isHeaderBag(raw.headers) ? raw.headers : void 0,
90
+ name: typeof raw.name === "string" ? raw.name : void 0,
91
+ requestId: readRequestId(raw)
92
+ };
93
+ }
94
+ return {};
95
+ }
96
+ /**
97
+ * Decide whether the thrown value represents a timeout. The Anthropic
98
+ * SDK throws `APIConnectionTimeoutError` for transport-level timeouts;
99
+ * Node surfaces `ETIMEDOUT` / `ECONNABORTED` on the socket layer.
100
+ * Either signal counts.
101
+ */
102
+ function isTimeout(thrown, shape) {
103
+ if (thrown instanceof APIConnectionTimeoutError) return true;
104
+ if (shape.name === "APIConnectionTimeoutError") return true;
105
+ if (typeof thrown === "object" && thrown !== null) {
106
+ const code = thrown.code;
107
+ if (code === "ETIMEDOUT" || code === "ECONNABORTED") return true;
108
+ }
109
+ return false;
110
+ }
111
+ /** True for HTTP 4xx — a client-side request problem, not a server fault. */
112
+ function isClientStatus(status) {
113
+ return typeof status === "number" && status >= 400 && status < 500;
114
+ }
115
+ /**
116
+ * Attach the raw diagnostic fields to `error.context` so consumers
117
+ * have everything the provider surfaced without each subclass having
118
+ * to redeclare them. Never includes `cause` — that lives on
119
+ * `error.cause`.
120
+ */
121
+ function buildContext(shape) {
122
+ const context = {};
123
+ if (shape.status !== void 0) context.status = shape.status;
124
+ if (shape.type) context.type = shape.type;
125
+ if (shape.requestId) context.requestId = shape.requestId;
126
+ return context;
127
+ }
128
+ /** Anthropic exposes the request id as `requestID`; some proxies use `request_id`. */
129
+ function readRequestId(raw) {
130
+ if (typeof raw.requestID === "string") return raw.requestID;
131
+ if (typeof raw.request_id === "string") return raw.request_id;
132
+ }
133
+ function isHeaderBag(value) {
134
+ return typeof value === "object" && value !== null;
135
+ }
136
+ /** Read a header value from either a `Headers` instance or a plain record. */
137
+ function readHeader(headers, name) {
138
+ if (typeof headers.get === "function") return headers.get(name) ?? void 0;
139
+ const record = headers;
140
+ return record[name] ?? record[name.toLowerCase()];
141
+ }
142
+ /**
143
+ * Parse the `Retry-After` response header (seconds per HTTP spec) into
144
+ * milliseconds so consumers can feed it straight to `setTimeout`.
145
+ * Returns `undefined` when missing or unparseable.
146
+ */
147
+ function parseRetryAfter(headers) {
148
+ if (!headers) return;
149
+ const raw = readHeader(headers, "retry-after") ?? readHeader(headers, "Retry-After");
150
+ if (!raw) return;
151
+ const seconds = Number(raw);
152
+ if (!Number.isFinite(seconds) || seconds < 0) return;
153
+ return Math.round(seconds * 1e3);
154
+ }
155
+
156
+ //#endregion
157
+ export { wrapAnthropicError };
158
+ //# sourceMappingURL=wrap-anthropic-error.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrap-anthropic-error.mjs","names":[],"sources":["../../../../../../@warlock.js/ai-anthropic/src/utils/wrap-anthropic-error.ts"],"sourcesContent":["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"],"mappings":"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"}