@opencode-ai/ai 0.0.0-beta-17492

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/README.md +392 -0
  2. package/dist/cache-policy.d.ts +2 -0
  3. package/dist/cache-policy.js +121 -0
  4. package/dist/image-client.d.ts +19 -0
  5. package/dist/image-client.js +19 -0
  6. package/dist/image.d.ts +110 -0
  7. package/dist/image.js +102 -0
  8. package/dist/index.d.ts +18 -0
  9. package/dist/index.js +12 -0
  10. package/dist/llm.d.ts +230 -0
  11. package/dist/llm.js +77 -0
  12. package/dist/protocols/anthropic-messages.d.ts +633 -0
  13. package/dist/protocols/anthropic-messages.js +860 -0
  14. package/dist/protocols/bedrock-converse.d.ts +533 -0
  15. package/dist/protocols/bedrock-converse.js +584 -0
  16. package/dist/protocols/bedrock-event-stream.d.ts +9 -0
  17. package/dist/protocols/bedrock-event-stream.js +73 -0
  18. package/dist/protocols/gemini.d.ts +300 -0
  19. package/dist/protocols/gemini.js +512 -0
  20. package/dist/protocols/google-images.d.ts +30 -0
  21. package/dist/protocols/google-images.js +191 -0
  22. package/dist/protocols/index.d.ts +10 -0
  23. package/dist/protocols/index.js +10 -0
  24. package/dist/protocols/open-responses-channel.d.ts +26 -0
  25. package/dist/protocols/open-responses-channel.js +120 -0
  26. package/dist/protocols/open-responses.d.ts +951 -0
  27. package/dist/protocols/open-responses.js +873 -0
  28. package/dist/protocols/openai-chat.d.ts +795 -0
  29. package/dist/protocols/openai-chat.js +718 -0
  30. package/dist/protocols/openai-compatible-chat.d.ts +107 -0
  31. package/dist/protocols/openai-compatible-chat.js +20 -0
  32. package/dist/protocols/openai-compatible-responses.d.ts +96 -0
  33. package/dist/protocols/openai-compatible-responses.js +17 -0
  34. package/dist/protocols/openai-images.d.ts +32 -0
  35. package/dist/protocols/openai-images.js +188 -0
  36. package/dist/protocols/openai-responses-channel.d.ts +12 -0
  37. package/dist/protocols/openai-responses-channel.js +142 -0
  38. package/dist/protocols/openai-responses.d.ts +596 -0
  39. package/dist/protocols/openai-responses.js +208 -0
  40. package/dist/protocols/shared.d.ts +194 -0
  41. package/dist/protocols/shared.js +238 -0
  42. package/dist/protocols/utils/bedrock-auth.d.ts +21 -0
  43. package/dist/protocols/utils/bedrock-auth.js +43 -0
  44. package/dist/protocols/utils/bedrock-cache.d.ts +15 -0
  45. package/dist/protocols/utils/bedrock-cache.js +29 -0
  46. package/dist/protocols/utils/bedrock-media.d.ts +49 -0
  47. package/dist/protocols/utils/bedrock-media.js +70 -0
  48. package/dist/protocols/utils/cache.d.ts +6 -0
  49. package/dist/protocols/utils/cache.js +8 -0
  50. package/dist/protocols/utils/gemini-tool-schema.d.ts +2 -0
  51. package/dist/protocols/utils/gemini-tool-schema.js +103 -0
  52. package/dist/protocols/utils/image-input.d.ts +21 -0
  53. package/dist/protocols/utils/image-input.js +22 -0
  54. package/dist/protocols/utils/lifecycle.d.ts +20 -0
  55. package/dist/protocols/utils/lifecycle.js +68 -0
  56. package/dist/protocols/utils/open-responses-options.d.ts +21 -0
  57. package/dist/protocols/utils/open-responses-options.js +41 -0
  58. package/dist/protocols/utils/openai-image.d.ts +5 -0
  59. package/dist/protocols/utils/openai-image.js +18 -0
  60. package/dist/protocols/utils/openai-options.d.ts +14 -0
  61. package/dist/protocols/utils/openai-options.js +14 -0
  62. package/dist/protocols/utils/tool-schema.d.ts +8 -0
  63. package/dist/protocols/utils/tool-schema.js +81 -0
  64. package/dist/protocols/utils/tool-stream.d.ts +639 -0
  65. package/dist/protocols/utils/tool-stream.js +141 -0
  66. package/dist/protocols/xai-images.d.ts +26 -0
  67. package/dist/protocols/xai-images.js +111 -0
  68. package/dist/protocols/zai-images.d.ts +22 -0
  69. package/dist/protocols/zai-images.js +84 -0
  70. package/dist/protocols.d.ts +1 -0
  71. package/dist/protocols.js +1 -0
  72. package/dist/provider-error.d.ts +14 -0
  73. package/dist/provider-error.js +130 -0
  74. package/dist/provider-package.d.ts +15 -0
  75. package/dist/provider-package.js +1 -0
  76. package/dist/provider.d.ts +23 -0
  77. package/dist/provider.js +2 -0
  78. package/dist/providers/amazon-bedrock/mantle/chat.d.ts +2 -0
  79. package/dist/providers/amazon-bedrock/mantle/chat.js +1 -0
  80. package/dist/providers/amazon-bedrock/mantle/responses.d.ts +2 -0
  81. package/dist/providers/amazon-bedrock/mantle/responses.js +1 -0
  82. package/dist/providers/amazon-bedrock/mantle.d.ts +2 -0
  83. package/dist/providers/amazon-bedrock/mantle.js +1 -0
  84. package/dist/providers/amazon-bedrock-mantle.d.ts +247 -0
  85. package/dist/providers/amazon-bedrock-mantle.js +68 -0
  86. package/dist/providers/amazon-bedrock.d.ts +172 -0
  87. package/dist/providers/amazon-bedrock.js +41 -0
  88. package/dist/providers/anthropic-compatible.d.ts +218 -0
  89. package/dist/providers/anthropic-compatible.js +45 -0
  90. package/dist/providers/anthropic.d.ts +216 -0
  91. package/dist/providers/anthropic.js +40 -0
  92. package/dist/providers/azure/chat.d.ts +2 -0
  93. package/dist/providers/azure/chat.js +1 -0
  94. package/dist/providers/azure/responses.d.ts +2 -0
  95. package/dist/providers/azure/responses.js +1 -0
  96. package/dist/providers/azure.d.ts +248 -0
  97. package/dist/providers/azure.js +90 -0
  98. package/dist/providers/cloudflare.d.ts +338 -0
  99. package/dist/providers/cloudflare.js +87 -0
  100. package/dist/providers/google-vertex/chat.d.ts +2 -0
  101. package/dist/providers/google-vertex/chat.js +1 -0
  102. package/dist/providers/google-vertex/gemini.d.ts +2 -0
  103. package/dist/providers/google-vertex/gemini.js +1 -0
  104. package/dist/providers/google-vertex/messages.d.ts +2 -0
  105. package/dist/providers/google-vertex/messages.js +1 -0
  106. package/dist/providers/google-vertex/responses.d.ts +2 -0
  107. package/dist/providers/google-vertex/responses.js +1 -0
  108. package/dist/providers/google-vertex-chat.d.ts +131 -0
  109. package/dist/providers/google-vertex-chat.js +50 -0
  110. package/dist/providers/google-vertex-messages.d.ts +215 -0
  111. package/dist/providers/google-vertex-messages.js +75 -0
  112. package/dist/providers/google-vertex-responses.d.ts +122 -0
  113. package/dist/providers/google-vertex-responses.js +51 -0
  114. package/dist/providers/google-vertex-shared.d.ts +22 -0
  115. package/dist/providers/google-vertex-shared.js +61 -0
  116. package/dist/providers/google-vertex.d.ts +115 -0
  117. package/dist/providers/google-vertex.js +64 -0
  118. package/dist/providers/google.d.ts +112 -0
  119. package/dist/providers/google.js +43 -0
  120. package/dist/providers/index.d.ts +18 -0
  121. package/dist/providers/index.js +18 -0
  122. package/dist/providers/open-responses-options.d.ts +16 -0
  123. package/dist/providers/open-responses-options.js +1 -0
  124. package/dist/providers/openai/chat.d.ts +2 -0
  125. package/dist/providers/openai/chat.js +1 -0
  126. package/dist/providers/openai/responses.d.ts +2 -0
  127. package/dist/providers/openai/responses.js +1 -0
  128. package/dist/providers/openai-compatible/responses.d.ts +1 -0
  129. package/dist/providers/openai-compatible/responses.js +1 -0
  130. package/dist/providers/openai-compatible-profile.d.ts +43 -0
  131. package/dist/providers/openai-compatible-profile.js +12 -0
  132. package/dist/providers/openai-compatible-responses.d.ts +120 -0
  133. package/dist/providers/openai-compatible-responses.js +33 -0
  134. package/dist/providers/openai-compatible.d.ts +167 -0
  135. package/dist/providers/openai-compatible.js +55 -0
  136. package/dist/providers/openai-options.d.ts +21 -0
  137. package/dist/providers/openai-options.js +41 -0
  138. package/dist/providers/openai.d.ts +264 -0
  139. package/dist/providers/openai.js +83 -0
  140. package/dist/providers/openrouter.d.ts +537 -0
  141. package/dist/providers/openrouter.js +117 -0
  142. package/dist/providers/xai.d.ts +249 -0
  143. package/dist/providers/xai.js +79 -0
  144. package/dist/providers/zai.d.ts +24 -0
  145. package/dist/providers/zai.js +21 -0
  146. package/dist/providers.d.ts +1 -0
  147. package/dist/providers.js +1 -0
  148. package/dist/route/auth-options.d.ts +34 -0
  149. package/dist/route/auth-options.js +14 -0
  150. package/dist/route/auth.d.ts +51 -0
  151. package/dist/route/auth.js +89 -0
  152. package/dist/route/client.d.ts +348 -0
  153. package/dist/route/client.js +214 -0
  154. package/dist/route/endpoint.d.ts +28 -0
  155. package/dist/route/endpoint.js +21 -0
  156. package/dist/route/executor.d.ts +23 -0
  157. package/dist/route/executor.js +227 -0
  158. package/dist/route/framing.d.ts +23 -0
  159. package/dist/route/framing.js +4 -0
  160. package/dist/route/index.d.ts +16 -0
  161. package/dist/route/index.js +9 -0
  162. package/dist/route/protocol.d.ts +77 -0
  163. package/dist/route/protocol.js +17 -0
  164. package/dist/route/transport/http.d.ts +34 -0
  165. package/dist/route/transport/http.js +63 -0
  166. package/dist/route/transport/index.d.ts +39 -0
  167. package/dist/route/transport/index.js +4 -0
  168. package/dist/route/transport/websocket-channel.d.ts +56 -0
  169. package/dist/route/transport/websocket-channel.js +1 -0
  170. package/dist/route/transport/websocket.d.ts +55 -0
  171. package/dist/route/transport/websocket.js +326 -0
  172. package/dist/route.d.ts +1 -0
  173. package/dist/route.js +1 -0
  174. package/dist/schema/errors.d.ts +159 -0
  175. package/dist/schema/errors.js +155 -0
  176. package/dist/schema/events.d.ts +4587 -0
  177. package/dist/schema/events.js +492 -0
  178. package/dist/schema/ids.d.ts +30 -0
  179. package/dist/schema/ids.js +19 -0
  180. package/dist/schema/index.d.ts +5 -0
  181. package/dist/schema/index.js +5 -0
  182. package/dist/schema/messages.d.ts +402 -0
  183. package/dist/schema/messages.js +244 -0
  184. package/dist/schema/options.d.ts +159 -0
  185. package/dist/schema/options.js +202 -0
  186. package/dist/testing.d.ts +1493 -0
  187. package/dist/testing.js +88 -0
  188. package/dist/tool-runtime.d.ts +15 -0
  189. package/dist/tool-runtime.js +54 -0
  190. package/dist/tool.d.ts +135 -0
  191. package/dist/tool.js +66 -0
  192. package/dist/utils/record.d.ts +2 -0
  193. package/dist/utils/record.js +2 -0
  194. package/package.json +47 -0
@@ -0,0 +1,860 @@
1
+ import { Effect, Schema } from "effect";
2
+ import { Tool } from "@opencode-ai/schema/tool";
3
+ import { Route } from "../route/client.js";
4
+ import { Auth } from "../route/auth.js";
5
+ import { Endpoint } from "../route/endpoint.js";
6
+ import { Framing } from "../route/framing.js";
7
+ import { Protocol } from "../route/protocol.js";
8
+ import { AIError, LLMEvent, mergeJsonRecords, Usage, } from "../schema/index.js";
9
+ import { JsonObject, optionalArray, optionalNull, ProviderShared } from "./shared.js";
10
+ import { classifyProviderFailure } from "../provider-error.js";
11
+ import * as Cache from "./utils/cache.js";
12
+ import { Lifecycle } from "./utils/lifecycle.js";
13
+ import { ToolSchemaProjection } from "./utils/tool-schema.js";
14
+ import { ToolStream } from "./utils/tool-stream.js";
15
+ const ADAPTER = "anthropic-messages";
16
+ const MEDIA_MIMES = new Set([...ProviderShared.IMAGE_MIMES, ...ProviderShared.PDF_MIMES]);
17
+ export const DEFAULT_BASE_URL = "https://api.anthropic.com/v1";
18
+ export const PATH = "/messages";
19
+ // =============================================================================
20
+ // Request Body Schema
21
+ // =============================================================================
22
+ const AnthropicCacheControl = Schema.Struct({
23
+ type: Schema.tag("ephemeral"),
24
+ ttl: Schema.optional(Schema.Literals(["5m", "1h"])),
25
+ });
26
+ const AnthropicTextBlock = Schema.Struct({
27
+ type: Schema.tag("text"),
28
+ text: Schema.String,
29
+ cache_control: Schema.optional(AnthropicCacheControl),
30
+ });
31
+ const AnthropicImageBlock = Schema.Struct({
32
+ type: Schema.tag("image"),
33
+ source: Schema.Struct({
34
+ type: Schema.tag("base64"),
35
+ media_type: Schema.String,
36
+ data: Schema.String,
37
+ }),
38
+ cache_control: Schema.optional(AnthropicCacheControl),
39
+ });
40
+ const AnthropicDocumentBlock = Schema.Struct({
41
+ type: Schema.tag("document"),
42
+ source: Schema.Struct({
43
+ type: Schema.tag("base64"),
44
+ media_type: Schema.Literal("application/pdf"),
45
+ data: Schema.String,
46
+ }),
47
+ cache_control: Schema.optional(AnthropicCacheControl),
48
+ });
49
+ const AnthropicThinkingBlock = Schema.Struct({
50
+ type: Schema.tag("thinking"),
51
+ thinking: Schema.String,
52
+ signature: Schema.optional(Schema.String),
53
+ cache_control: Schema.optional(AnthropicCacheControl),
54
+ });
55
+ // Safety-filtered thinking arrives as an opaque encrypted `data` payload with
56
+ // no visible text. It must round-trip verbatim so multi-turn thinking + tool
57
+ // use conversations keep their reasoning continuity.
58
+ const AnthropicRedactedThinkingBlock = Schema.Struct({
59
+ type: Schema.tag("redacted_thinking"),
60
+ data: Schema.String,
61
+ cache_control: Schema.optional(AnthropicCacheControl),
62
+ });
63
+ const AnthropicToolUseBlock = Schema.Struct({
64
+ type: Schema.tag("tool_use"),
65
+ id: Schema.String,
66
+ name: Schema.String,
67
+ input: Schema.Unknown,
68
+ cache_control: Schema.optional(AnthropicCacheControl),
69
+ });
70
+ const AnthropicServerToolUseBlock = Schema.Struct({
71
+ type: Schema.tag("server_tool_use"),
72
+ id: Schema.String,
73
+ name: Schema.String,
74
+ input: Schema.Unknown,
75
+ cache_control: Schema.optional(AnthropicCacheControl),
76
+ });
77
+ // Server tool result blocks: web_search_tool_result, code_execution_tool_result,
78
+ // and web_fetch_tool_result. The provider executes the tool and inlines the
79
+ // structured result into the assistant turn — there is no client tool_result
80
+ // round-trip. We round-trip the structured `content` payload as opaque JSON so
81
+ // the next request can echo it back when continuing the conversation.
82
+ const AnthropicServerToolResultType = Schema.Literals([
83
+ "web_search_tool_result",
84
+ "code_execution_tool_result",
85
+ "web_fetch_tool_result",
86
+ ]);
87
+ const AnthropicServerToolResultBlock = Schema.Struct({
88
+ type: AnthropicServerToolResultType,
89
+ tool_use_id: Schema.String,
90
+ content: Schema.Unknown,
91
+ cache_control: Schema.optional(AnthropicCacheControl),
92
+ });
93
+ // Anthropic accepts either a plain string or an ordered array of text, image, and
94
+ // document blocks inside `tool_result.content`. The array form keeps media as native
95
+ // model input instead of JSON-stringifying base64 into prompt text.
96
+ const AnthropicToolResultContent = Schema.Union([AnthropicTextBlock, AnthropicImageBlock, AnthropicDocumentBlock]);
97
+ const AnthropicToolResultBlock = Schema.Struct({
98
+ type: Schema.tag("tool_result"),
99
+ tool_use_id: Schema.String,
100
+ content: Schema.Union([Schema.String, Schema.Array(AnthropicToolResultContent)]),
101
+ is_error: Schema.optional(Schema.Boolean),
102
+ cache_control: Schema.optional(AnthropicCacheControl),
103
+ });
104
+ const AnthropicUserBlock = Schema.Union([
105
+ AnthropicTextBlock,
106
+ AnthropicImageBlock,
107
+ AnthropicDocumentBlock,
108
+ AnthropicToolResultBlock,
109
+ ]);
110
+ const AnthropicAssistantBlock = Schema.Union([
111
+ AnthropicTextBlock,
112
+ AnthropicThinkingBlock,
113
+ AnthropicRedactedThinkingBlock,
114
+ AnthropicToolUseBlock,
115
+ AnthropicServerToolUseBlock,
116
+ AnthropicServerToolResultBlock,
117
+ ]);
118
+ const AnthropicMessage = Schema.Union([
119
+ Schema.Struct({ role: Schema.Literal("user"), content: Schema.Array(AnthropicUserBlock) }),
120
+ Schema.Struct({ role: Schema.Literal("assistant"), content: Schema.Array(AnthropicAssistantBlock) }),
121
+ Schema.Struct({ role: Schema.Literal("system"), content: Schema.Array(AnthropicTextBlock) }),
122
+ ]).pipe(Schema.toTaggedUnion("role"));
123
+ const AnthropicTool = Schema.Struct({
124
+ name: Schema.String,
125
+ description: Schema.String,
126
+ input_schema: JsonObject,
127
+ cache_control: Schema.optional(AnthropicCacheControl),
128
+ });
129
+ const AnthropicToolChoice = Schema.Union([
130
+ Schema.Struct({ type: Schema.Literals(["auto", "any", "none"]) }),
131
+ Schema.Struct({ type: Schema.tag("tool"), name: Schema.String }),
132
+ ]);
133
+ const AnthropicThinking = Schema.Union([
134
+ Schema.Struct({
135
+ type: Schema.tag("enabled"),
136
+ budget_tokens: Schema.Number,
137
+ }),
138
+ Schema.Struct({
139
+ type: Schema.tag("adaptive"),
140
+ display: Schema.optional(Schema.Literals(["summarized", "omitted"])),
141
+ }),
142
+ Schema.Struct({
143
+ type: Schema.tag("disabled"),
144
+ }),
145
+ ]);
146
+ const AnthropicOutputConfig = Schema.Struct({
147
+ effort: Schema.optional(Schema.String),
148
+ });
149
+ const AnthropicBodyFields = {
150
+ model: Schema.String,
151
+ system: optionalArray(AnthropicTextBlock),
152
+ messages: Schema.Array(AnthropicMessage),
153
+ tools: optionalArray(AnthropicTool),
154
+ tool_choice: Schema.optional(AnthropicToolChoice),
155
+ stream: Schema.Literal(true),
156
+ max_tokens: Schema.Number,
157
+ temperature: Schema.optional(Schema.Number),
158
+ top_p: Schema.optional(Schema.Number),
159
+ top_k: Schema.optional(Schema.Number),
160
+ stop_sequences: optionalArray(Schema.String),
161
+ thinking: Schema.optional(AnthropicThinking),
162
+ output_config: Schema.optional(AnthropicOutputConfig),
163
+ };
164
+ export const AnthropicMessagesBody = Schema.Struct(AnthropicBodyFields);
165
+ const AnthropicUsage = Schema.StructWithRest(Schema.Struct({
166
+ input_tokens: Schema.optional(Schema.Number),
167
+ output_tokens: Schema.optional(Schema.Number),
168
+ cache_creation_input_tokens: optionalNull(Schema.Number),
169
+ cache_read_input_tokens: optionalNull(Schema.Number),
170
+ server_tool_use: optionalNull(Schema.StructWithRest(Schema.Struct({ web_search_requests: Schema.optional(Schema.Number) }), [
171
+ Schema.Record(Schema.String, Schema.Unknown),
172
+ ])),
173
+ output_tokens_details: optionalNull(Schema.StructWithRest(Schema.Struct({ thinking_tokens: Schema.optional(Schema.Number) }), [
174
+ Schema.Record(Schema.String, Schema.Unknown),
175
+ ])),
176
+ }), [Schema.Record(Schema.String, Schema.Unknown)]);
177
+ const AnthropicStreamBlock = Schema.Struct({
178
+ type: Schema.String,
179
+ id: Schema.optional(Schema.String),
180
+ name: Schema.optional(Schema.String),
181
+ text: Schema.optional(Schema.String),
182
+ thinking: Schema.optional(Schema.String),
183
+ signature: Schema.optional(Schema.String),
184
+ // redacted_thinking blocks arrive whole in content_block_start with the
185
+ // encrypted payload in `data`; there is no streaming delta sequence.
186
+ data: Schema.optional(Schema.String),
187
+ input: Schema.optional(Schema.Unknown),
188
+ // *_tool_result blocks arrive whole as content_block_start (no streaming
189
+ // delta) with the structured payload in `content` and the originating
190
+ // server_tool_use id in `tool_use_id`.
191
+ tool_use_id: Schema.optional(Schema.String),
192
+ content: Schema.optional(Schema.Unknown),
193
+ });
194
+ const AnthropicStreamDelta = Schema.Struct({
195
+ type: Schema.optional(Schema.String),
196
+ text: Schema.optional(Schema.String),
197
+ thinking: Schema.optional(Schema.String),
198
+ partial_json: Schema.optional(Schema.String),
199
+ signature: Schema.optional(Schema.String),
200
+ stop_reason: optionalNull(Schema.String),
201
+ stop_sequence: optionalNull(Schema.String),
202
+ });
203
+ const AnthropicEvent = Schema.Struct({
204
+ type: Schema.String,
205
+ index: Schema.optional(Schema.Number),
206
+ message: Schema.optional(Schema.Struct({ usage: Schema.optional(AnthropicUsage) })),
207
+ content_block: Schema.optional(AnthropicStreamBlock),
208
+ delta: Schema.optional(AnthropicStreamDelta),
209
+ usage: Schema.optional(AnthropicUsage),
210
+ // `type` and `message` are both required per Anthropic's spec, but
211
+ // OpenAI-compatible proxies and gateway translations occasionally drop one
212
+ // or the other; mark them optional so a partial payload still parses and
213
+ // the parser can fall back to whichever field is populated.
214
+ error: Schema.optional(Schema.Struct({ type: Schema.optional(Schema.String), message: Schema.optional(Schema.String) })),
215
+ });
216
+ const invalid = ProviderShared.invalidRequest;
217
+ // =============================================================================
218
+ // Request Lowering
219
+ // =============================================================================
220
+ // Anthropic accepts at most 4 explicit cache_control breakpoints per request,
221
+ // across `tools`, `system`, and `messages`. Beyond the cap the API returns a
222
+ // 400 — so the lowering layer counts emitted markers and silently drops any
223
+ // that exceed it.
224
+ const ANTHROPIC_BREAKPOINT_CAP = 4;
225
+ const EPHEMERAL_5M = { type: "ephemeral" };
226
+ const EPHEMERAL_1H = { type: "ephemeral", ttl: "1h" };
227
+ const cacheControl = (breakpoints, cache) => {
228
+ if (cache?.type !== "ephemeral" && cache?.type !== "persistent")
229
+ return undefined;
230
+ if (breakpoints.remaining <= 0) {
231
+ breakpoints.dropped += 1;
232
+ return undefined;
233
+ }
234
+ breakpoints.remaining -= 1;
235
+ return Cache.ttlBucket(cache.ttlSeconds) === "1h" ? EPHEMERAL_1H : EPHEMERAL_5M;
236
+ };
237
+ const anthropicMetadata = (metadata) => ({ anthropic: metadata });
238
+ const signatureFromMetadata = (metadata) => {
239
+ const anthropic = metadata?.anthropic;
240
+ if (!ProviderShared.isRecord(anthropic))
241
+ return undefined;
242
+ return typeof anthropic.signature === "string" ? anthropic.signature : undefined;
243
+ };
244
+ const redactedDataFromMetadata = (metadata) => {
245
+ const anthropic = metadata?.anthropic;
246
+ if (!ProviderShared.isRecord(anthropic))
247
+ return undefined;
248
+ return typeof anthropic.redactedData === "string" ? anthropic.redactedData : undefined;
249
+ };
250
+ const lowerTool = (breakpoints, tool, inputSchema) => ({
251
+ name: tool.name,
252
+ description: tool.description,
253
+ input_schema: inputSchema,
254
+ cache_control: cacheControl(breakpoints, tool.cache),
255
+ });
256
+ const lowerToolChoice = (toolChoice) => ProviderShared.matchToolChoice("Anthropic Messages", toolChoice, {
257
+ auto: () => ({ type: "auto" }),
258
+ none: () => ({ type: "none" }),
259
+ required: () => ({ type: "any" }),
260
+ tool: (name) => ({ type: "tool", name }),
261
+ });
262
+ const lowerToolCall = (part) => ({
263
+ type: "tool_use",
264
+ id: part.id,
265
+ name: part.name,
266
+ input: part.input,
267
+ });
268
+ const lowerServerToolCall = (part) => ({
269
+ type: "server_tool_use",
270
+ id: part.id,
271
+ name: part.name,
272
+ input: part.input,
273
+ });
274
+ // Server tool result blocks are typed by name. Anthropic ships three today;
275
+ // extend this list when new server tools land. The block content is the
276
+ // structured payload returned by the provider, which we round-trip as-is.
277
+ const serverToolResultType = (name) => {
278
+ if (name === "web_search")
279
+ return "web_search_tool_result";
280
+ if (name === "code_execution")
281
+ return "code_execution_tool_result";
282
+ if (name === "web_fetch")
283
+ return "web_fetch_tool_result";
284
+ return undefined;
285
+ };
286
+ const lowerServerToolResult = Effect.fn("AnthropicMessages.lowerServerToolResult")(function* (part) {
287
+ const wireType = serverToolResultType(part.name);
288
+ if (!wireType)
289
+ return yield* invalid(`Anthropic Messages does not know how to round-trip server tool result for ${part.name}`);
290
+ // Prefer the provider-owned replay payload; fall back to the result value for
291
+ // histories constructed directly from provider events.
292
+ const payload = part.providerMetadata?.anthropic?.["result"] ?? part.result.value;
293
+ return { type: wireType, tool_use_id: part.id, content: payload };
294
+ });
295
+ const lowerMedia = Effect.fn("AnthropicMessages.lowerMedia")(function* (part) {
296
+ const media = yield* ProviderShared.validateMedia("Anthropic Messages", part, MEDIA_MIMES);
297
+ if (media.mime === "application/pdf")
298
+ return {
299
+ type: "document",
300
+ source: {
301
+ type: "base64",
302
+ media_type: "application/pdf",
303
+ data: media.base64,
304
+ },
305
+ };
306
+ return {
307
+ type: "image",
308
+ source: {
309
+ type: "base64",
310
+ media_type: media.mime,
311
+ data: media.base64,
312
+ },
313
+ };
314
+ });
315
+ // Tool results may carry structured text, images, and documents. Keep media as provider-native
316
+ // content instead of JSON-stringifying base64 into a prompt string.
317
+ const lowerToolResultContentItem = Effect.fnUntraced(function* (item) {
318
+ if (item.type === "text")
319
+ return { type: "text", text: item.text };
320
+ return yield* lowerMedia({ type: "media", mediaType: item.mime, data: item.uri, filename: item.name });
321
+ });
322
+ const lowerToolResultContent = Effect.fnUntraced(function* (part) {
323
+ // Text / json / error results stay as a string for backward compatibility
324
+ // with existing cassettes and provider expectations.
325
+ if (part.result.type !== "content")
326
+ return ProviderShared.toolResultText(part);
327
+ // Preserve the narrowed array element type when compiled through a consumer package.
328
+ const content = part.result.value;
329
+ return yield* Effect.forEach(content, lowerToolResultContentItem);
330
+ });
331
+ // Mid-conversation system messages are a native Claude API feature only for
332
+ // Opus 4.8. Other Anthropic models intentionally use the same visible wrapped-
333
+ // user fallback as non-Anthropic routes rather than sending a role they reject.
334
+ const supportsNativeSystemUpdates = (request) => String(request.model.id) === "claude-opus-4-8";
335
+ const endsInServerToolUse = (message) => {
336
+ const last = message.content.at(-1);
337
+ return message.role === "assistant" && last?.type === "tool-call" && last.providerExecuted === true;
338
+ };
339
+ const canUseNativeSystemUpdate = (messages, index) => {
340
+ const previous = messages[index - 1];
341
+ const next = messages[index + 1];
342
+ return (previous !== undefined &&
343
+ previous.role !== "system" &&
344
+ (previous.role === "user" || previous.role === "tool" || endsInServerToolUse(previous)) &&
345
+ next?.role !== "system" &&
346
+ (next === undefined || next.role === "assistant"));
347
+ };
348
+ const splitsLocalToolResults = (messages, index) => {
349
+ const pending = new Set();
350
+ for (const message of messages.slice(0, index)) {
351
+ for (const part of message.content) {
352
+ if (message.role === "assistant" && part.type === "tool-call" && part.providerExecuted !== true)
353
+ pending.add(part.id);
354
+ if (message.role === "tool" && part.type === "tool-result")
355
+ pending.delete(part.id);
356
+ }
357
+ }
358
+ return pending.size > 0;
359
+ };
360
+ const lowerNativeSystemUpdate = Effect.fn("AnthropicMessages.lowerNativeSystemUpdate")(function* (message, breakpoints) {
361
+ const content = yield* ProviderShared.systemUpdateText("Anthropic Messages", message);
362
+ return {
363
+ role: "system",
364
+ content: content.map((part) => ({
365
+ type: "text",
366
+ text: part.text,
367
+ cache_control: cacheControl(breakpoints, part.cache),
368
+ })),
369
+ };
370
+ });
371
+ const lowerMessages = Effect.fn("AnthropicMessages.lowerMessages")(function* (request, breakpoints) {
372
+ const messages = [];
373
+ for (const [index, message] of request.messages.entries()) {
374
+ if (message.role === "system") {
375
+ if (splitsLocalToolResults(request.messages, index))
376
+ return yield* invalid("Anthropic Messages system updates cannot split a local tool call from its tool result");
377
+ if (supportsNativeSystemUpdates(request) && canUseNativeSystemUpdate(request.messages, index)) {
378
+ messages.push(yield* lowerNativeSystemUpdate(message, breakpoints));
379
+ continue;
380
+ }
381
+ const part = yield* ProviderShared.wrappedSystemUpdate("Anthropic Messages", message);
382
+ const block = { type: "text", text: part.text, cache_control: cacheControl(breakpoints, part.cache) };
383
+ const previous = messages.at(-1);
384
+ if (previous?.role === "user")
385
+ messages[messages.length - 1] = { role: "user", content: [...previous.content, block] };
386
+ else
387
+ messages.push({ role: "user", content: [block] });
388
+ continue;
389
+ }
390
+ if (message.role === "user") {
391
+ const content = [];
392
+ for (const part of message.content) {
393
+ if (part.type === "text") {
394
+ content.push({ type: "text", text: part.text, cache_control: cacheControl(breakpoints, part.cache) });
395
+ continue;
396
+ }
397
+ if (part.type === "media") {
398
+ content.push(yield* lowerMedia(part));
399
+ continue;
400
+ }
401
+ return yield* ProviderShared.unsupportedContent("Anthropic Messages", "user", ["text", "media"]);
402
+ }
403
+ messages.push({ role: "user", content });
404
+ continue;
405
+ }
406
+ if (message.role === "assistant") {
407
+ const content = [];
408
+ for (const part of message.content) {
409
+ if (part.type === "text") {
410
+ content.push({ type: "text", text: part.text, cache_control: cacheControl(breakpoints, part.cache) });
411
+ continue;
412
+ }
413
+ if (part.type === "reasoning") {
414
+ // Mirrors Vercel's @ai-sdk/anthropic: a signature marks visible
415
+ // thinking; only signature-less parts carrying redactedData
416
+ // round-trip as opaque redacted_thinking blocks.
417
+ const signature = part.encrypted ?? signatureFromMetadata(part.providerMetadata);
418
+ const redactedData = redactedDataFromMetadata(part.providerMetadata);
419
+ if (signature === undefined && redactedData !== undefined) {
420
+ content.push({ type: "redacted_thinking", data: redactedData });
421
+ continue;
422
+ }
423
+ content.push({ type: "thinking", thinking: part.text, signature });
424
+ continue;
425
+ }
426
+ if (part.type === "tool-call") {
427
+ content.push(part.providerExecuted ? lowerServerToolCall(part) : lowerToolCall(part));
428
+ continue;
429
+ }
430
+ if (part.type === "tool-result" && part.providerExecuted) {
431
+ content.push(yield* lowerServerToolResult(part));
432
+ continue;
433
+ }
434
+ return yield* invalid(`Anthropic Messages assistant messages only support text, reasoning, and tool-call content for now`);
435
+ }
436
+ messages.push({ role: "assistant", content });
437
+ continue;
438
+ }
439
+ const content = [];
440
+ for (const part of message.content) {
441
+ if (!ProviderShared.supportsContent(part, ["tool-result"]))
442
+ return yield* ProviderShared.unsupportedContent("Anthropic Messages", "tool", ["tool-result"]);
443
+ content.push({
444
+ type: "tool_result",
445
+ tool_use_id: part.id,
446
+ content: yield* lowerToolResultContent(part),
447
+ is_error: part.result.type === "error" ? true : undefined,
448
+ cache_control: cacheControl(breakpoints, part.cache),
449
+ });
450
+ }
451
+ const previous = messages.at(-1);
452
+ if (previous?.role === "user" && previous.content.every((block) => block.type === "tool_result"))
453
+ messages[messages.length - 1] = { role: "user", content: [...previous.content, ...content] };
454
+ else
455
+ messages.push({ role: "user", content });
456
+ }
457
+ return messages;
458
+ });
459
+ const resolveOptions = Effect.fn("AnthropicMessages.resolveOptions")(function* (request) {
460
+ const input = request.providerOptions?.anthropic;
461
+ return {
462
+ thinking: yield* resolveThinking(input?.thinking),
463
+ effort: typeof input?.effort === "string" ? input.effort : undefined,
464
+ };
465
+ });
466
+ const resolveThinking = Effect.fn("AnthropicMessages.resolveThinking")(function* (input) {
467
+ if (!ProviderShared.isRecord(input))
468
+ return undefined;
469
+ if (input.type === "adaptive") {
470
+ const display = input.display === "summarized"
471
+ ? "summarized"
472
+ : input.display === "omitted"
473
+ ? "omitted"
474
+ : undefined;
475
+ return { type: "adaptive", ...(display === undefined ? {} : { display }) };
476
+ }
477
+ if (input.type === "disabled")
478
+ return { type: "disabled" };
479
+ if (input.type !== "enabled")
480
+ return undefined;
481
+ const budget = typeof input.budgetTokens === "number"
482
+ ? input.budgetTokens
483
+ : typeof input.budget_tokens === "number"
484
+ ? input.budget_tokens
485
+ : undefined;
486
+ if (budget === undefined)
487
+ return yield* ProviderShared.invalidRequest("Anthropic thinking provider option requires budgetTokens");
488
+ return { type: "enabled", budget_tokens: budget };
489
+ });
490
+ const fromRequest = Effect.fn("AnthropicMessages.fromRequest")(function* (request) {
491
+ const generation = request.generation;
492
+ const toolSchemaCompatibility = request.model.compatibility?.toolSchema;
493
+ const outputLimit = request.model.defaults?.limits?.output ?? request.model.route.defaults.limits?.output ?? 4096;
494
+ // Allocate the 4-breakpoint budget in invalidation order: tools → system →
495
+ // messages. Tools live highest in the cache hierarchy, so when callers
496
+ // over-mark we keep their tool hints and shed the message-tail ones first.
497
+ const breakpoints = Cache.newBreakpoints(ANTHROPIC_BREAKPOINT_CAP);
498
+ const tools = request.tools.length === 0
499
+ ? undefined
500
+ : request.tools.map((tool) => lowerTool(breakpoints, tool, ToolSchemaProjection.modelCompatibility(tool.inputSchema, toolSchemaCompatibility)));
501
+ // Anthropic rejects tool_choice when tools are absent; "none" is only meaningful with tools present.
502
+ const toolChoice = tools === undefined || !request.toolChoice ? undefined : yield* lowerToolChoice(request.toolChoice);
503
+ const system = request.system.length === 0
504
+ ? undefined
505
+ : request.system.map((part) => ({
506
+ type: "text",
507
+ text: part.text,
508
+ cache_control: cacheControl(breakpoints, part.cache),
509
+ }));
510
+ const messages = yield* lowerMessages(request, breakpoints);
511
+ if (breakpoints.dropped > 0) {
512
+ yield* Effect.logWarning(`Anthropic Messages: dropped ${breakpoints.dropped} cache breakpoint(s); the API allows at most ${ANTHROPIC_BREAKPOINT_CAP} per request.`);
513
+ }
514
+ const options = yield* resolveOptions(request);
515
+ return {
516
+ model: request.model.id,
517
+ system,
518
+ messages,
519
+ tools,
520
+ tool_choice: toolChoice,
521
+ stream: true,
522
+ max_tokens: generation?.maxTokens ?? outputLimit,
523
+ temperature: generation?.temperature,
524
+ top_p: generation?.topP,
525
+ top_k: generation?.topK,
526
+ stop_sequences: generation?.stop,
527
+ thinking: options.thinking,
528
+ output_config: options.effort === undefined ? undefined : { effort: options.effort },
529
+ };
530
+ });
531
+ // =============================================================================
532
+ // Stream Parsing
533
+ // =============================================================================
534
+ const mapFinishReason = (reason) => {
535
+ if (reason === "end_turn" || reason === "stop_sequence" || reason === "pause_turn")
536
+ return "stop";
537
+ if (reason === "max_tokens" || reason === "model_context_window_exceeded")
538
+ return "length";
539
+ if (reason === "tool_use")
540
+ return "tool-calls";
541
+ if (reason === "refusal")
542
+ return "content-filter";
543
+ return "unknown";
544
+ };
545
+ // Anthropic reports the non-overlapping breakdown natively — its
546
+ // `input_tokens` is the *non-cached* count per the Messages API docs, with
547
+ // cache reads and writes as separate fields. We sum them to derive the
548
+ // inclusive `inputTokens` the rest of the contract expects. Extended
549
+ // thinking tokens are included in `output_tokens`; newer responses also
550
+ // expose that subset through `output_tokens_details.thinking_tokens`.
551
+ const mapUsage = (usage) => {
552
+ if (!usage)
553
+ return undefined;
554
+ const nonCached = usage.input_tokens;
555
+ const cacheRead = usage.cache_read_input_tokens ?? undefined;
556
+ const cacheWrite = usage.cache_creation_input_tokens ?? undefined;
557
+ const inputTokens = ProviderShared.sumTokens(nonCached, cacheRead, cacheWrite);
558
+ return new Usage({
559
+ inputTokens,
560
+ outputTokens: usage.output_tokens,
561
+ nonCachedInputTokens: nonCached,
562
+ cacheReadInputTokens: cacheRead,
563
+ cacheWriteInputTokens: cacheWrite,
564
+ reasoningTokens: usage.output_tokens_details?.thinking_tokens,
565
+ totalTokens: ProviderShared.totalTokens(inputTokens, usage.output_tokens, undefined),
566
+ providerMetadata: { anthropic: usage },
567
+ });
568
+ };
569
+ // Anthropic emits usage on `message_start` and again on `message_delta` — the
570
+ // final delta carries the authoritative totals. Right-biased merge: each
571
+ // field prefers `right` when defined, falls back to `left`. `inputTokens` is
572
+ // recomputed from the merged breakdown so the inclusive total stays
573
+ // consistent with `nonCached + cacheRead + cacheWrite`.
574
+ const mergeUsage = (left, right) => {
575
+ if (!left)
576
+ return right;
577
+ if (!right)
578
+ return left;
579
+ const nonCachedInputTokens = right.nonCachedInputTokens ?? left.nonCachedInputTokens;
580
+ const cacheReadInputTokens = right.cacheReadInputTokens ?? left.cacheReadInputTokens;
581
+ const cacheWriteInputTokens = right.cacheWriteInputTokens ?? left.cacheWriteInputTokens;
582
+ const inputTokens = ProviderShared.sumTokens(nonCachedInputTokens, cacheReadInputTokens, cacheWriteInputTokens);
583
+ const outputTokens = right.outputTokens ?? left.outputTokens;
584
+ const reasoningTokens = right.reasoningTokens ?? left.reasoningTokens;
585
+ return new Usage({
586
+ inputTokens,
587
+ outputTokens,
588
+ nonCachedInputTokens,
589
+ cacheReadInputTokens,
590
+ cacheWriteInputTokens,
591
+ reasoningTokens,
592
+ totalTokens: ProviderShared.totalTokens(inputTokens, outputTokens, undefined),
593
+ providerMetadata: {
594
+ anthropic: mergeJsonRecords(left.providerMetadata?.["anthropic"], right.providerMetadata?.["anthropic"]) ?? {},
595
+ },
596
+ });
597
+ };
598
+ // Server tool result blocks come whole in `content_block_start` (no streaming
599
+ // delta sequence). We convert the payload to a `tool-result` event with
600
+ // `providerExecuted: true`. The runtime appends it to the assistant message
601
+ // for round-trip; downstream consumers can inspect `result.value` for the
602
+ // structured payload.
603
+ const SERVER_TOOL_RESULT_NAMES = {
604
+ web_search_tool_result: "web_search",
605
+ code_execution_tool_result: "code_execution",
606
+ web_fetch_tool_result: "web_fetch",
607
+ };
608
+ const isServerToolResultType = (type) => type in SERVER_TOOL_RESULT_NAMES;
609
+ const serverToolResultEvent = (block) => {
610
+ if (!block.type || !isServerToolResultType(block.type))
611
+ return undefined;
612
+ const errorPayload = typeof block.content === "object" && block.content !== null && "type" in block.content
613
+ ? String(block.content.type)
614
+ : "";
615
+ const isError = errorPayload.endsWith("_tool_result_error");
616
+ return LLMEvent.toolResult({
617
+ id: block.tool_use_id ?? "",
618
+ name: SERVER_TOOL_RESULT_NAMES[block.type],
619
+ result: isError ? { type: "error", value: block.content } : { type: "json", value: block.content },
620
+ providerExecuted: true,
621
+ // The complete payload is irreducible provider replay state: subsequent
622
+ // stateless requests must round-trip the typed result block verbatim.
623
+ providerMetadata: anthropicMetadata({ blockType: block.type, result: block.content }),
624
+ });
625
+ };
626
+ const NO_EVENTS = [];
627
+ const onMessageStart = (state, event) => {
628
+ const usage = mapUsage(event.message?.usage);
629
+ return [usage ? { ...state, usage: mergeUsage(state.usage, usage) } : state, NO_EVENTS];
630
+ };
631
+ const onContentBlockStart = (state, event) => {
632
+ const block = event.content_block;
633
+ if (!block)
634
+ return [state, NO_EVENTS];
635
+ if ((block.type === "tool_use" || block.type === "server_tool_use") && event.index !== undefined) {
636
+ const events = [];
637
+ const lifecycle = Lifecycle.stepStart(state.lifecycle, events);
638
+ return [
639
+ {
640
+ ...state,
641
+ lifecycle,
642
+ tools: ToolStream.start(state.tools, event.index, {
643
+ id: block.id ?? String(event.index),
644
+ name: block.name ?? "",
645
+ input: block.input !== undefined && (!ProviderShared.isRecord(block.input) || Object.keys(block.input).length > 0)
646
+ ? ProviderShared.encodeJson(block.input)
647
+ : undefined,
648
+ providerExecuted: block.type === "server_tool_use",
649
+ }),
650
+ },
651
+ [
652
+ ...events,
653
+ LLMEvent.toolInputStart({
654
+ id: block.id ?? String(event.index),
655
+ name: block.name ?? "",
656
+ providerExecuted: block.type === "server_tool_use" ? true : undefined,
657
+ }),
658
+ ],
659
+ ];
660
+ }
661
+ if (block.type === "text" && block.text !== undefined) {
662
+ const events = [];
663
+ const id = `text-${event.index ?? 0}`;
664
+ const lifecycle = Lifecycle.textStart(state.lifecycle, events, id);
665
+ return [
666
+ { ...state, lifecycle: block.text ? Lifecycle.textDelta(lifecycle, events, id, block.text) : lifecycle },
667
+ events,
668
+ ];
669
+ }
670
+ if (block.type === "thinking" && block.thinking !== undefined) {
671
+ const events = [];
672
+ const id = `reasoning-${event.index ?? 0}`;
673
+ const providerMetadata = block.signature === undefined ? undefined : anthropicMetadata({ signature: block.signature });
674
+ const lifecycle = Lifecycle.reasoningStart(state.lifecycle, events, id, providerMetadata);
675
+ return [
676
+ {
677
+ ...state,
678
+ lifecycle: block.thinking
679
+ ? Lifecycle.reasoningDelta(lifecycle, events, id, block.thinking, providerMetadata)
680
+ : lifecycle,
681
+ reasoningSignatures: event.index === undefined || block.signature === undefined
682
+ ? state.reasoningSignatures
683
+ : { ...state.reasoningSignatures, [event.index]: block.signature },
684
+ },
685
+ events,
686
+ ];
687
+ }
688
+ // Redacted thinking surfaces as an empty reasoning part carrying the opaque
689
+ // payload as `redactedData` metadata (same model as Vercel's
690
+ // @ai-sdk/anthropic). The existing content_block_stop closes the part.
691
+ if (block.type === "redacted_thinking" && block.data !== undefined) {
692
+ const events = [];
693
+ return [
694
+ {
695
+ ...state,
696
+ lifecycle: Lifecycle.reasoningStart(state.lifecycle, events, `reasoning-${event.index ?? 0}`, anthropicMetadata({ redactedData: block.data })),
697
+ },
698
+ events,
699
+ ];
700
+ }
701
+ const result = serverToolResultEvent(block);
702
+ if (!result)
703
+ return [state, NO_EVENTS];
704
+ const events = [];
705
+ return [{ ...state, lifecycle: Lifecycle.stepStart(state.lifecycle, events) }, [...events, result]];
706
+ };
707
+ const onContentBlockDelta = Effect.fn("AnthropicMessages.onContentBlockDelta")(function* (state, event) {
708
+ const delta = event.delta;
709
+ if (delta?.type === "text_delta" && delta.text) {
710
+ const events = [];
711
+ return [
712
+ { ...state, lifecycle: Lifecycle.textDelta(state.lifecycle, events, `text-${event.index ?? 0}`, delta.text) },
713
+ events,
714
+ ];
715
+ }
716
+ if (delta?.type === "thinking_delta" && delta.thinking) {
717
+ const events = [];
718
+ return [
719
+ {
720
+ ...state,
721
+ lifecycle: Lifecycle.reasoningDelta(state.lifecycle, events, `reasoning-${event.index ?? 0}`, delta.thinking),
722
+ },
723
+ events,
724
+ ];
725
+ }
726
+ if (delta?.type === "signature_delta" && delta.signature) {
727
+ const index = event.index ?? 0;
728
+ return [
729
+ {
730
+ ...state,
731
+ reasoningSignatures: { ...state.reasoningSignatures, [index]: delta.signature },
732
+ },
733
+ NO_EVENTS,
734
+ ];
735
+ }
736
+ if (delta?.type === "input_json_delta" && event.index !== undefined) {
737
+ if (!delta.partial_json)
738
+ return [state, NO_EVENTS];
739
+ const result = ToolStream.appendExisting(ADAPTER, state.tools, event.index, delta.partial_json, "Anthropic Messages tool argument delta is missing its tool call");
740
+ if (ToolStream.isError(result))
741
+ return yield* result;
742
+ const events = [];
743
+ const lifecycle = result.events.length ? Lifecycle.stepStart(state.lifecycle, events) : state.lifecycle;
744
+ events.push(...result.events);
745
+ return [{ ...state, lifecycle, tools: result.tools }, events];
746
+ }
747
+ return [state, NO_EVENTS];
748
+ });
749
+ const onContentBlockStop = Effect.fn("AnthropicMessages.onContentBlockStop")(function* (state, event) {
750
+ if (event.index === undefined)
751
+ return [state, NO_EVENTS];
752
+ const result = yield* ToolStream.finish(ADAPTER, state.tools, event.index);
753
+ const events = [];
754
+ const resultEvents = result.events ?? [];
755
+ const signature = state.reasoningSignatures[event.index];
756
+ const lifecycle = resultEvents.length
757
+ ? Lifecycle.stepStart(state.lifecycle, events)
758
+ : Lifecycle.reasoningEnd(Lifecycle.textEnd(state.lifecycle, events, `text-${event.index}`), events, `reasoning-${event.index}`, signature === undefined ? undefined : anthropicMetadata({ signature }));
759
+ events.push(...resultEvents);
760
+ const reasoningSignatures = { ...state.reasoningSignatures };
761
+ delete reasoningSignatures[event.index];
762
+ return [{ ...state, lifecycle, tools: result.tools, reasoningSignatures }, events];
763
+ });
764
+ const onMessageDelta = (state, event) => {
765
+ const usage = mergeUsage(state.usage, mapUsage(event.usage));
766
+ return [
767
+ {
768
+ ...state,
769
+ usage,
770
+ pendingFinish: {
771
+ reason: {
772
+ normalized: mapFinishReason(event.delta?.stop_reason),
773
+ raw: event.delta?.stop_reason ?? undefined,
774
+ },
775
+ providerMetadata: event.delta?.stop_sequence === null || event.delta?.stop_sequence === undefined
776
+ ? undefined
777
+ : anthropicMetadata({ stopSequence: event.delta.stop_sequence }),
778
+ },
779
+ },
780
+ NO_EVENTS,
781
+ ];
782
+ };
783
+ const onMessageStop = (state) => {
784
+ const events = [];
785
+ const lifecycle = Lifecycle.finish(state.lifecycle, events, {
786
+ reason: state.pendingFinish?.reason ?? {
787
+ normalized: "unknown",
788
+ raw: undefined,
789
+ },
790
+ usage: state.usage,
791
+ providerMetadata: state.pendingFinish?.providerMetadata,
792
+ });
793
+ return [{ ...state, lifecycle }, events];
794
+ };
795
+ // Prefix `error.type` so overloads, rate limits, and quota errors are visible
796
+ // even when the provider message is generic or empty.
797
+ const providerErrorMessage = (event) => {
798
+ const type = event.error?.type;
799
+ const message = event.error?.message;
800
+ if (type && message)
801
+ return `${type}: ${message}`;
802
+ return message || type || "Anthropic Messages stream error";
803
+ };
804
+ const onError = (event) => new AIError({
805
+ module: ADAPTER,
806
+ method: "stream",
807
+ reason: classifyProviderFailure({ message: providerErrorMessage(event), code: event.error?.type }),
808
+ });
809
+ const step = (state, event) => {
810
+ if (event.type === "message_start")
811
+ return Effect.succeed(onMessageStart(state, event));
812
+ if (event.type === "content_block_start")
813
+ return Effect.succeed(onContentBlockStart(state, event));
814
+ if (event.type === "content_block_delta")
815
+ return onContentBlockDelta(state, event);
816
+ if (event.type === "content_block_stop")
817
+ return onContentBlockStop(state, event);
818
+ if (event.type === "message_delta")
819
+ return Effect.succeed(onMessageDelta(state, event));
820
+ if (event.type === "message_stop")
821
+ return Effect.succeed(onMessageStop(state));
822
+ if (event.type === "error")
823
+ return onError(event);
824
+ return Effect.succeed([state, NO_EVENTS]);
825
+ };
826
+ // =============================================================================
827
+ // Protocol And Anthropic Route
828
+ // =============================================================================
829
+ /**
830
+ * The Anthropic Messages protocol — request body construction, body schema,
831
+ * and the streaming-event state machine. Used by native Anthropic Cloud and
832
+ * (once registered) Vertex Anthropic / Bedrock-hosted Anthropic passthrough.
833
+ */
834
+ export const protocol = Protocol.make({
835
+ id: ADAPTER,
836
+ body: {
837
+ schema: AnthropicMessagesBody,
838
+ from: fromRequest,
839
+ },
840
+ stream: {
841
+ event: Protocol.jsonEvent(AnthropicEvent),
842
+ initial: () => ({
843
+ tools: ToolStream.empty(),
844
+ reasoningSignatures: {},
845
+ lifecycle: Lifecycle.initial(),
846
+ }),
847
+ step,
848
+ },
849
+ });
850
+ export const route = Route.make({
851
+ id: ADAPTER,
852
+ provider: "anthropic",
853
+ providerMetadataKey: "anthropic",
854
+ protocol,
855
+ endpoint: Endpoint.path(PATH, { baseURL: DEFAULT_BASE_URL }),
856
+ auth: Auth.none,
857
+ framing: Framing.sse,
858
+ headers: () => ({ "anthropic-version": "2023-06-01" }),
859
+ });
860
+ export * as AnthropicMessages from "./anthropic-messages.js";