@opencode/ai 0.0.0-beta-19507 → 0.0.0-dev-19276

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 (114) hide show
  1. package/README.md +0 -200
  2. package/dist/protocols/anthropic-messages.d.ts +1 -24
  3. package/dist/protocols/anthropic-messages.js +12 -58
  4. package/dist/protocols/meta-messages.d.ts +0 -6
  5. package/dist/protocols/open-responses-channel.d.ts +0 -2
  6. package/dist/protocols/open-responses-channel.js +2 -2
  7. package/dist/protocols/open-responses-continuation.d.ts +3 -7
  8. package/dist/protocols/open-responses-continuation.js +7 -16
  9. package/dist/protocols/open-responses.d.ts +9 -141
  10. package/dist/protocols/open-responses.js +7 -29
  11. package/dist/provider-package.d.ts +0 -4
  12. package/dist/providers/amazon-bedrock-mantle.d.ts +62 -4
  13. package/dist/providers/amazon-bedrock-mantle.js +20 -23
  14. package/dist/providers/amazon-bedrock.js +3 -3
  15. package/dist/providers/anthropic-compatible.d.ts +2 -7
  16. package/dist/providers/anthropic-compatible.js +12 -18
  17. package/dist/providers/anthropic.d.ts +2 -7
  18. package/dist/providers/anthropic.js +9 -12
  19. package/dist/providers/azure.d.ts +2 -1
  20. package/dist/providers/azure.js +14 -14
  21. package/dist/providers/baseten.d.ts +3 -2
  22. package/dist/providers/baseten.js +6 -6
  23. package/dist/providers/cerebras.d.ts +3 -2
  24. package/dist/providers/cerebras.js +6 -6
  25. package/dist/providers/cloudflare-ai-gateway.d.ts +2 -1
  26. package/dist/providers/cloudflare-ai-gateway.js +10 -16
  27. package/dist/providers/cloudflare-workers-ai.d.ts +2 -1
  28. package/dist/providers/cloudflare-workers-ai.js +9 -15
  29. package/dist/providers/deepinfra.d.ts +3 -2
  30. package/dist/providers/deepinfra.js +6 -6
  31. package/dist/providers/deepseek.d.ts +3 -2
  32. package/dist/providers/deepseek.js +6 -6
  33. package/dist/providers/fireworks.d.ts +3 -2
  34. package/dist/providers/fireworks.js +6 -6
  35. package/dist/providers/google-vertex-chat.d.ts +3 -2
  36. package/dist/providers/google-vertex-chat.js +12 -12
  37. package/dist/providers/google-vertex-messages.d.ts +3 -8
  38. package/dist/providers/google-vertex-messages.js +12 -12
  39. package/dist/providers/google-vertex-responses.d.ts +3 -2
  40. package/dist/providers/google-vertex-responses.js +12 -12
  41. package/dist/providers/google-vertex-shared.js +3 -14
  42. package/dist/providers/google-vertex.d.ts +2 -1
  43. package/dist/providers/google-vertex.js +12 -18
  44. package/dist/providers/google.d.ts +3 -2
  45. package/dist/providers/google.js +6 -6
  46. package/dist/providers/groq.d.ts +3 -2
  47. package/dist/providers/groq.js +6 -6
  48. package/dist/providers/index.d.ts +0 -3
  49. package/dist/providers/index.js +0 -3
  50. package/dist/providers/meta.d.ts +3 -8
  51. package/dist/providers/meta.js +6 -6
  52. package/dist/providers/minimax/chat.js +6 -6
  53. package/dist/providers/minimax/responses.js +6 -6
  54. package/dist/providers/minimax.d.ts +3 -8
  55. package/dist/providers/minimax.js +6 -6
  56. package/dist/providers/mistral.d.ts +3 -2
  57. package/dist/providers/mistral.js +6 -6
  58. package/dist/providers/openai-compatible-responses.d.ts +3 -2
  59. package/dist/providers/openai-compatible-responses.js +7 -7
  60. package/dist/providers/openai-compatible.d.ts +3 -2
  61. package/dist/providers/openai-compatible.js +7 -7
  62. package/dist/providers/openai.d.ts +3 -2
  63. package/dist/providers/openai.js +9 -9
  64. package/dist/providers/openrouter.d.ts +3 -2
  65. package/dist/providers/openrouter.js +6 -6
  66. package/dist/providers/togetherai.d.ts +3 -2
  67. package/dist/providers/togetherai.js +6 -6
  68. package/dist/providers/xai.d.ts +3 -2
  69. package/dist/providers/xai.js +6 -10
  70. package/dist/providers/zai.d.ts +8 -133
  71. package/dist/providers/zai.js +0 -32
  72. package/dist/route/client.js +2 -5
  73. package/dist/route/transport/websocket.js +2 -6
  74. package/dist/schema/errors.d.ts +0 -12
  75. package/dist/schema/errors.js +0 -11
  76. package/dist/schema/options.d.ts +0 -2
  77. package/dist/schema/options.js +0 -2
  78. package/package.json +3 -3
  79. package/dist/protocols/alibaba-chat.d.ts +0 -225
  80. package/dist/protocols/alibaba-chat.js +0 -75
  81. package/dist/protocols/alibaba-messages.d.ts +0 -350
  82. package/dist/protocols/alibaba-messages.js +0 -40
  83. package/dist/protocols/alibaba-responses.d.ts +0 -255
  84. package/dist/protocols/alibaba-responses.js +0 -80
  85. package/dist/protocols/zai-chat.d.ts +0 -202
  86. package/dist/protocols/zai-chat.js +0 -49
  87. package/dist/protocols/zai-messages.d.ts +0 -344
  88. package/dist/protocols/zai-messages.js +0 -27
  89. package/dist/providers/alibaba/chat.d.ts +0 -1
  90. package/dist/providers/alibaba/chat.js +0 -1
  91. package/dist/providers/alibaba/messages.d.ts +0 -3
  92. package/dist/providers/alibaba/messages.js +0 -1
  93. package/dist/providers/alibaba/responses.d.ts +0 -3
  94. package/dist/providers/alibaba/responses.js +0 -1
  95. package/dist/providers/alibaba.d.ts +0 -711
  96. package/dist/providers/alibaba.js +0 -94
  97. package/dist/providers/moonshot/chat.d.ts +0 -1
  98. package/dist/providers/moonshot/chat.js +0 -1
  99. package/dist/providers/moonshot/messages.d.ts +0 -4
  100. package/dist/providers/moonshot/messages.js +0 -8
  101. package/dist/providers/moonshot/responses.d.ts +0 -4
  102. package/dist/providers/moonshot/responses.js +0 -8
  103. package/dist/providers/moonshot.d.ts +0 -596
  104. package/dist/providers/moonshot.js +0 -90
  105. package/dist/providers/zai/chat.d.ts +0 -1
  106. package/dist/providers/zai/chat.js +0 -1
  107. package/dist/providers/zai-coding-plan/chat.d.ts +0 -1
  108. package/dist/providers/zai-coding-plan/chat.js +0 -1
  109. package/dist/providers/zai-coding-plan/messages.d.ts +0 -4
  110. package/dist/providers/zai-coding-plan/messages.js +0 -8
  111. package/dist/providers/zai-coding-plan/responses.d.ts +0 -4
  112. package/dist/providers/zai-coding-plan/responses.js +0 -8
  113. package/dist/providers/zai-coding-plan.d.ts +0 -577
  114. package/dist/providers/zai-coding-plan.js +0 -63
package/README.md CHANGED
@@ -29,206 +29,6 @@ await Effect.runPromise(program.pipe(Effect.provide(llmLayer)))
29
29
 
30
30
  Run `LLMClient.stream(request)` instead of `generate` when you want incremental `LLMEvent`s. The event stream is provider-neutral — same shape across OpenAI Chat, OpenAI Responses, Anthropic Messages, Gemini, Bedrock Converse, and any OpenAI-compatible deployment.
31
31
 
32
- ## Alibaba Cloud Model Studio
33
-
34
- `Alibaba` provides standard Model Studio inference. Configure a region explicitly, then select
35
- Chat Completions (`.model` or `.chat`), Anthropic-compatible Messages (`.messages`), or OpenAI-compatible
36
- Responses (`.responses`). These routes use HTTP/SSE.
37
-
38
- ```ts
39
- import { LLM } from "@opencode/ai"
40
- import { Alibaba } from "@opencode/ai/providers"
41
-
42
- const alibaba = Alibaba.configure({
43
- region: "ap-southeast-1", // Singapore
44
- apiKey: process.env.DASHSCOPE_API_KEY,
45
- // workspaceID: "llm-your-workspace", // use a workspace-dedicated endpoint
46
- })
47
-
48
- const request = LLM.request({
49
- model: alibaba.model("qwen3.8-max"),
50
- prompt: "Explain this design.",
51
- providerOptions: { reasoningEffort: "medium" },
52
- })
53
- ```
54
-
55
- ### Regions and credentials
56
-
57
- | Region | `region` | Shared host when `workspaceID` is omitted |
58
- | ------------------- | ---------------- | ----------------------------------------- |
59
- | Singapore | `ap-southeast-1` | `dashscope-intl.aliyuncs.com` |
60
- | China (Beijing) | `cn-beijing` | `dashscope.aliyuncs.com` |
61
- | China (Hong Kong) | `cn-hongkong` | `cn-hongkong.dashscope.aliyuncs.com` |
62
- | US (Virginia) | `us-east-1` | `dashscope-us.aliyuncs.com` |
63
- | Germany (Frankfurt) | `eu-central-1` | Supply `workspaceID` or `baseURL` |
64
- | Japan (Tokyo) | `ap-northeast-1` | Supply `workspaceID` or `baseURL` |
65
-
66
- With `workspaceID`, the host is `{workspaceID}.{region}.maas.aliyuncs.com`. A complete `baseURL`
67
- overrides regional setup, including the API prefix: `/compatible-mode/v1` for Chat/Responses,
68
- or `/apps/anthropic/v1` for Messages. The selector appends its operation path.
69
-
70
- Keys and model availability are region-specific. Auth resolves from explicit `auth` or `apiKey`,
71
- then `DASHSCOPE_API_KEY`, then `ALIBABA_API_KEY`.
72
-
73
- The access region and inference scope differ: Virginia's `-us` model IDs request US-only inference;
74
- some regions select scope through their workspace. Model IDs pass through unchanged.
75
- Alibaba's [regional guide](https://www.alibabacloud.com/help/en/model-studio/regions) and
76
- [base URL table](https://www.alibabacloud.com/help/en/model-studio/base-url) disagree about Virginia's
77
- shared host; the entry above follows the base URL table. Dedicated hosts can be copied from the console.
78
-
79
- ### Native options
80
-
81
- - **Chat:** `reasoningEffort` → `reasoning_effort`, `enableThinking` → `enable_thinking`,
82
- `thinkingBudget` → `thinking_budget`, and `preserveThinking` → `preserve_thinking`.
83
- Replay complete `response.message` values to retain `reasoning_content` separately from answer text.
84
- Qwen 3.8 defaults to preserving thinking; older models have different defaults.
85
- Additional options include `toolStream`, `parallelToolCalls`, `repetitionPenalty`, `responseFormat`,
86
- `enableSearch`, and native `searchOptions`. `generation.topK` lowers to `top_k`.
87
- `clearThinking` is a hosted GLM control, and `thinking.type` is available for hosted MiniMax models.
88
- - **Messages:** `effort` → `output_config.effort`. `thinking.type` accepts enabled/disabled with an
89
- optional `budgetTokens` (or native `budget_tokens`). `outputConfig.format` accepts a JSON schema.
90
- Model Studio's empty thinking signatures are accepted; supplied signatures are replayed unchanged.
91
- - **Responses:** `reasoningEffort` → `reasoning.effort`, plus `enableThinking`, `store`,
92
- `previousResponseId`, and `conversation`. Omitted `store` retains the API's default (`true`);
93
- set it to `false` for client-managed history. `previousResponseId` requires a stored response.
94
- Hosted tools are `Alibaba.webSearch()`, `Alibaba.webExtractor()`, and `Alibaba.codeInterpreter()`.
95
- Web extraction is used together with web search. Hosted calls/results carry `providerExecuted: true`.
96
-
97
- Omitted options preserve provider defaults. Effort values pass through unchanged and accept future
98
- strings. Qwen 3.8 Chat rejects requests combining a thinking budget with effort.
99
-
100
- Package entrypoints are `@opencode/ai/providers/alibaba`, `alibaba/chat`, `alibaba/messages`,
101
- and `alibaba/responses`. Live recordings cover all three APIs in Singapore; regional URL construction
102
- is unit-tested for all six regions.
103
-
104
- ## Z.AI
105
-
106
- `ZAI` uses the standard API. Chat Completions is the default language-model API;
107
- the existing `.image(...)` selector provides image generation.
108
-
109
- ```ts
110
- import { LLM } from "@opencode/ai"
111
- import { ZAI, ZAICodingPlan } from "@opencode/ai/providers"
112
-
113
- const zai = ZAI.configure({ apiKey: process.env.ZAI_API_KEY })
114
- const request = LLM.request({
115
- model: zai.model("glm-5.3"), // also zai.chat("glm-5.3")
116
- prompt: "Explain this design.",
117
- providerOptions: {
118
- reasoningEffort: "high",
119
- thinking: { type: "enabled", clear_thinking: false },
120
- },
121
- })
122
-
123
- const coding = ZAICodingPlan.configure({ apiKey: process.env.ZAI_API_KEY })
124
- const messages = LLM.request({
125
- model: coding.messages("glm-5.3"),
126
- prompt: "Explain this design.",
127
- providerOptions: { effort: "high" },
128
- })
129
- ```
130
-
131
- The products have distinct provider identities and endpoints:
132
-
133
- | Provider | Selector | Default base URL |
134
- | ----------------------------------- | --------------------------- | ------------------------------------- |
135
- | `ZAI` (`zai`) | `.model`, `.chat`, `.image` | `https://api.z.ai/api/paas/v4` |
136
- | `ZAICodingPlan` (`zai-coding-plan`) | `.model`, `.chat` | `https://api.z.ai/api/coding/paas/v4` |
137
- | `ZAICodingPlan` | `.messages` | `https://api.z.ai/api/anthropic/v1` |
138
- | `ZAICodingPlan` | `.responses` | `https://api.z.ai/api/v1` |
139
-
140
- Both read `ZAI_API_KEY` when `apiKey` is omitted and support an explicit `auth` override.
141
- Coding Plan requires an active subscription. `baseURL` overrides the selected API's
142
- complete base, including its version prefix. Language-model routes use HTTP/SSE.
143
-
144
- Options retain the selected API's native semantics:
145
-
146
- - Chat `reasoningEffort` lowers to `reasoning_effort`; Responses lowers it to `reasoning.effort`.
147
- Messages `effort` lowers to `output_config.effort`. Omission preserves provider defaults.
148
- - Chat `thinking` passes `type` and `clear_thinking` through unchanged. Set
149
- `clear_thinking: false` and replay complete `response.message` values to preserve reasoning
150
- across user messages and tool loops. The standard API defaults to clearing historical thinking;
151
- Coding Plan documents preservation by default.
152
- - Messages accepts `thinking: { type: "enabled" | "adaptive" | "disabled" }` without requiring
153
- an Anthropic token budget. Coding Plan documents a disabled toggle as low-effort thinking
154
- for GLM-5.3, with explicit effort taking precedence.
155
- - Chat also offers `toolStream`, `doSample`, `responseFormat`, `requestID`, and `userID`.
156
- Tool-argument streaming is enabled when tools are present on GLM-4.6/4.7/5.x;
157
- `toolStream: false` explicitly disables it. Older model families omit the opt-in.
158
- - Effort and thinking values remain forward-compatible strings. Their meaning is model-specific:
159
- GLM-5.3 accepts `low`, `high`, and `max` effort and rejects disabled thinking with HTTP 400;
160
- the direct GLM-5.2 recordings returned reasoning even with `none` and `minimal` effort,
161
- whereas explicit `thinking.type: "disabled"` disabled it on GLM-5.2 and GLM-4.7.
162
-
163
- Standard API recordings cover GLM-5.3 efforts and a full preserved-reasoning tool loop with
164
- a subsequent user follow-up, GLM-5.2 efforts, older-model thinking toggles, GLM-4.5 tool calls,
165
- GLM-5.3-Flash image input, and JSON output. Coding Plan has unit coverage for routing,
166
- request options, and reasoning replay; successful live recordings are pending.
167
-
168
- Package entrypoints are `@opencode/ai/providers/zai`, `zai/chat`, `zai-coding-plan`,
169
- `zai-coding-plan/chat`, `zai-coding-plan/messages`, and `zai-coding-plan/responses`.
170
-
171
- ## Moonshot
172
-
173
- Moonshot defaults to Chat Completions, with Messages and Responses selectors for Kimi K3:
174
-
175
- ```ts
176
- import { LLM } from "@opencode/ai"
177
- import { Moonshot } from "@opencode/ai/providers"
178
-
179
- const moonshot = Moonshot.configure({ apiKey: process.env.MOONSHOT_API_KEY })
180
-
181
- const request = LLM.request({
182
- model: moonshot.model("kimi-k3"), // also moonshot.chat("kimi-k3")
183
- prompt: "Explain the tradeoffs in this design.",
184
- providerOptions: { reasoningEffort: "high" },
185
- })
186
-
187
- const messages = LLM.request({
188
- model: moonshot.messages("kimi-k3"),
189
- prompt: "Explain the tradeoffs in this design.",
190
- providerOptions: { effort: "high" },
191
- })
192
-
193
- const responses = LLM.request({
194
- model: moonshot.responses("kimi-k3"),
195
- prompt: "Explain the tradeoffs in this design.",
196
- providerOptions: { reasoningEffort: "high" },
197
- })
198
- ```
199
-
200
- When `apiKey` is omitted, authentication reads `MOONSHOT_API_KEY`, then `MOONSHOTAI_API_KEY`.
201
- Chat and Responses use `https://api.moonshot.ai/v1`; Messages uses
202
- `https://api.moonshot.ai/anthropic/v1`. `baseURL` overrides the selected API's complete base,
203
- including the version prefix, for regional endpoints or gateways. Each endpoint requires its own valid credentials.
204
- All three routes use HTTP/SSE.
205
-
206
- Reasoning options stay native to the selected API and model:
207
-
208
- | Model/API | Provider options |
209
- | --------------------------- | --------------------------------------------------------------------------------------- |
210
- | K3 Chat / Responses | `reasoningEffort: "low" \| "high" \| "max"`; default is `max` |
211
- | K3 Messages | `effort: "low" \| "high" \| "max"`; default is `max` |
212
- | K2.6 Chat | `thinking: { type: "enabled" \| "disabled", keep?: "all" \| null }`; default is enabled |
213
- | K2.7 Code / high-speed Chat | Omit `thinking` to use always-on, preserved reasoning |
214
-
215
- Omitting options preserves the model's defaults. K3 uses effort rather than the K2.x `thinking`
216
- parameter. Known effort values have autocomplete while future strings remain accepted.
217
- For K2.6, `thinking.keep: "all"` enables preservation of reasoning across user messages.
218
- K3 and both K2.7 Code variants always preserve reasoning. Continue with the returned
219
- `response.message` and matching tool results so reasoning content and any Messages signatures are retained.
220
- Leave sampling options such as `temperature` unset to use these models' fixed defaults.
221
-
222
- The recorded suite covers all three K3 APIs, default and explicit efforts, K2.6 thinking modes,
223
- both K2.7 Code variants, generated tool loops with a subsequent user follow-up, required/disabled
224
- tool choice, image-byte input, and native structured output through `http.body` overlays.
225
- K3 Chat and Messages accept required and disabled tool choice. Responses supports automatic tool
226
- choice only; explicit `required` and `none` produce a provider `InvalidRequest` error, also covered by recordings.
227
- The provider targets the Moonshot Open Platform; Kimi Code is a separate product and endpoint.
228
-
229
- Package entrypoints are `@opencode/ai/providers/moonshot`, `moonshot/chat`, `moonshot/messages`,
230
- and `moonshot/responses`; each exports `model(modelID, settings)`.
231
-
232
32
  ## MiniMax
233
33
 
234
34
  MiniMax defaults to its Messages API and reads `MINIMAX_API_KEY` when `apiKey` is omitted:
@@ -10,19 +10,14 @@ export declare const DEFAULT_BASE_URL = "https://api.anthropic.com/v1";
10
10
  export declare const PATH = "/messages";
11
11
  export declare const DEFAULT_MAX_TOKENS = 32000;
12
12
  export declare const framing: Framing.Definition<string>;
13
- export type ThinkingBlockBinding = {
14
- readonly prefix_mismatch_behavior?: "error" | "drop_block" | (string & {});
15
- };
16
13
  export type ThinkingInput = {
17
14
  readonly type: "adaptive";
18
15
  readonly display?: "summarized" | "omitted";
19
- readonly block_binding?: ThinkingBlockBinding;
20
16
  } | {
21
17
  readonly type: "disabled";
22
18
  } | ({
23
19
  readonly type: "enabled";
24
20
  readonly display?: "summarized" | "omitted";
25
- readonly block_binding?: ThinkingBlockBinding;
26
21
  } & ({
27
22
  readonly budgetTokens: number;
28
23
  readonly budget_tokens?: number;
@@ -314,15 +309,9 @@ export declare const AnthropicMessagesBody: Schema.Struct<{
314
309
  readonly type: Schema.tag<"enabled">;
315
310
  readonly budget_tokens: Schema.Number;
316
311
  readonly display: Schema.optional<Schema.Literals<readonly ["summarized", "omitted"]>>;
317
- readonly block_binding: Schema.optional<Schema.Struct<{
318
- readonly prefix_mismatch_behavior: Schema.optional<Schema.String>;
319
- }>>;
320
312
  }>, Schema.Struct<{
321
313
  readonly type: Schema.tag<"adaptive">;
322
314
  readonly display: Schema.optional<Schema.Literals<readonly ["summarized", "omitted"]>>;
323
- readonly block_binding: Schema.optional<Schema.Struct<{
324
- readonly prefix_mismatch_behavior: Schema.optional<Schema.String>;
325
- }>>;
326
315
  }>, Schema.Struct<{
327
316
  readonly type: Schema.tag<"disabled">;
328
317
  }>]>>;
@@ -567,15 +556,9 @@ export declare const protocol: Protocol<{
567
556
  readonly type: "enabled";
568
557
  readonly budget_tokens: number;
569
558
  readonly display?: "summarized" | "omitted" | undefined;
570
- readonly block_binding?: {
571
- readonly prefix_mismatch_behavior?: string | undefined;
572
- } | undefined;
573
559
  } | {
574
560
  readonly type: "adaptive";
575
561
  readonly display?: "summarized" | "omitted" | undefined;
576
- readonly block_binding?: {
577
- readonly prefix_mismatch_behavior?: string | undefined;
578
- } | undefined;
579
562
  } | {
580
563
  readonly type: "disabled";
581
564
  } | undefined;
@@ -701,7 +684,7 @@ export declare const protocol: Protocol<{
701
684
  reasoningSignatures: {};
702
685
  lifecycle: Lifecycle.State;
703
686
  }>;
704
- export declare const transport: <Body extends Pick<AnthropicMessagesBody, "messages" | "context_management" | "thinking">>() => {
687
+ export declare const transport: <Body extends Pick<AnthropicMessagesBody, "messages" | "context_management">>() => {
705
688
  prepare: (input: Parameters<(input: import("../route/transport/index.js").TransportPrepareInput<Body>) => Effect.Effect<HttpTransport.HttpPrepared<string>, AIError, never>>[0]) => Effect.Effect<HttpTransport.HttpPrepared<string>, AIError, never>;
706
689
  with: (patch: Partial<HttpTransport.HttpJsonInput<Body, string>>) => HttpTransport.HttpJsonTransport<Body, string>;
707
690
  id: string;
@@ -921,15 +904,9 @@ export declare const route: Route<{
921
904
  readonly type: "enabled";
922
905
  readonly budget_tokens: number;
923
906
  readonly display?: "summarized" | "omitted" | undefined;
924
- readonly block_binding?: {
925
- readonly prefix_mismatch_behavior?: string | undefined;
926
- } | undefined;
927
907
  } | {
928
908
  readonly type: "adaptive";
929
909
  readonly display?: "summarized" | "omitted" | undefined;
930
- readonly block_binding?: {
931
- readonly prefix_mismatch_behavior?: string | undefined;
932
- } | undefined;
933
910
  } | {
934
911
  readonly type: "disabled";
935
912
  } | undefined;
@@ -202,20 +202,15 @@ const AnthropicToolChoice = Schema.Union([
202
202
  disable_parallel_tool_use: Schema.optional(Schema.Boolean),
203
203
  }),
204
204
  ]);
205
- const AnthropicThinkingBlockBinding = Schema.Struct({
206
- prefix_mismatch_behavior: Schema.optional(Schema.String),
207
- });
208
205
  const AnthropicThinking = Schema.Union([
209
206
  Schema.Struct({
210
207
  type: Schema.tag("enabled"),
211
208
  budget_tokens: Schema.Number,
212
209
  display: Schema.optional(Schema.Literals(["summarized", "omitted"])),
213
- block_binding: Schema.optional(AnthropicThinkingBlockBinding),
214
210
  }),
215
211
  Schema.Struct({
216
212
  type: Schema.tag("adaptive"),
217
213
  display: Schema.optional(Schema.Literals(["summarized", "omitted"])),
218
- block_binding: Schema.optional(AnthropicThinkingBlockBinding),
219
214
  }),
220
215
  Schema.Struct({
221
216
  type: Schema.tag("disabled"),
@@ -848,9 +843,8 @@ const resolveOptions = Effect.fn("AnthropicMessages.resolveOptions")(function* (
848
843
  ...(outputConfigEffort === undefined ? {} : { effort: outputConfigEffort }),
849
844
  ...(outputConfigFormat === undefined ? {} : { format: outputConfigFormat }),
850
845
  };
851
- const thinking = yield* resolveThinking(input?.thinking);
852
846
  return {
853
- thinking: applyThinkingBindingDefault(request.model, thinking),
847
+ thinking: yield* resolveThinking(input?.thinking),
854
848
  effort: outputConfigEffort,
855
849
  output_config,
856
850
  service_tier,
@@ -860,44 +854,18 @@ const resolveOptions = Effect.fn("AnthropicMessages.resolveOptions")(function* (
860
854
  cache_control,
861
855
  };
862
856
  });
863
- const supportsThinkingBlockBinding = (model) => {
864
- const override = model.compatibility?.supportsThinkingBlockBinding;
865
- if (override !== undefined)
866
- return override;
867
- // Accept gateway namespaces and Vertex suffixes without treating a snapshot date as a minor version.
868
- const version = /(?:^|[./])claude-[a-z]+-(?<major>\d+)(?:[.-](?<minor>\d{1,2}))?(?:$|[-:@])/i.exec(model.id)?.groups;
869
- if (!version)
870
- return false;
871
- const major = Number(version.major);
872
- const minor = Number(version.minor ?? 0);
873
- return major > 5 || (major === 5 && minor >= 1);
874
- };
875
- const applyThinkingBindingDefault = (model, thinking) => {
876
- if (thinking?.type === "disabled")
877
- return thinking;
878
- if (!supportsThinkingBlockBinding(model))
879
- return thinking;
880
- return {
881
- ...(thinking ?? { type: "adaptive" }),
882
- block_binding: {
883
- prefix_mismatch_behavior: "drop_block",
884
- ...thinking?.block_binding,
885
- },
886
- };
887
- };
888
857
  const resolveThinking = Effect.fn("AnthropicMessages.resolveThinking")(function* (input) {
889
858
  if (!ProviderShared.isRecord(input))
890
859
  return undefined;
891
- if (input.type === "disabled")
892
- return { type: "disabled" };
893
- if (input.type !== "adaptive" && input.type !== "enabled")
894
- return undefined;
895
- const block_binding = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(Schema.UndefinedOr(AnthropicThinkingBlockBinding)))(input.block_binding);
896
860
  const display = input.display === "summarized" || input.display === "omitted"
897
861
  ? input.display
898
862
  : undefined;
899
863
  if (input.type === "adaptive")
900
- return { type: "adaptive", display, block_binding };
864
+ return { type: "adaptive", ...(display === undefined ? {} : { display }) };
865
+ if (input.type === "disabled")
866
+ return { type: "disabled" };
867
+ if (input.type !== "enabled")
868
+ return undefined;
901
869
  const budget = typeof input.budgetTokens === "number"
902
870
  ? input.budgetTokens
903
871
  : typeof input.budget_tokens === "number"
@@ -905,7 +873,7 @@ const resolveThinking = Effect.fn("AnthropicMessages.resolveThinking")(function*
905
873
  : undefined;
906
874
  if (budget === undefined)
907
875
  return yield* ProviderShared.invalidRequest("Anthropic thinking provider option requires budgetTokens");
908
- return { type: "enabled", budget_tokens: budget, display, block_binding };
876
+ return { type: "enabled", budget_tokens: budget, ...(display === undefined ? {} : { display }) };
909
877
  });
910
878
  const fromRequest = Effect.fn("AnthropicMessages.fromRequest")(function* (request) {
911
879
  const management = yield* ProviderShared.validateWith(Schema.decodeUnknownEffect(Schema.UndefinedOr(ContextManagement)))(request.providerOptions?.contextManagement);
@@ -1410,15 +1378,15 @@ export const transport = () => {
1410
1378
  return {
1411
1379
  ...http,
1412
1380
  prepare: (input) => {
1413
- const requiredBetas = requiredBetaHeaders(input.body);
1414
- if (requiredBetas.length === 0)
1381
+ if (!input.body.context_management?.edits.length &&
1382
+ !input.body.messages.some((message) => message.content.some((block) => block.type === "compaction")))
1415
1383
  return http.prepare(input);
1416
1384
  const headers = Headers.fromInput(input.request.http?.headers);
1417
- const existingBetas = (headers["anthropic-beta"] ?? "")
1385
+ const betas = new Set((headers["anthropic-beta"] ?? "")
1418
1386
  .split(",")
1419
1387
  .map((item) => item.trim())
1420
- .filter(Boolean);
1421
- const betas = new Set([...existingBetas, ...requiredBetas]);
1388
+ .filter(Boolean));
1389
+ betas.add("compact-2026-01-12");
1422
1390
  return http.prepare({
1423
1391
  ...input,
1424
1392
  request: LLMRequest.update(input.request, {
@@ -1431,20 +1399,6 @@ export const transport = () => {
1431
1399
  },
1432
1400
  };
1433
1401
  };
1434
- function requiredBetaHeaders(body) {
1435
- // Always request interleaved thinking. The API accepts the header on any
1436
- // model and ignores it where unsupported, while manual-thinking models need
1437
- // it for thinking between tool calls.
1438
- const betas = ["interleaved-thinking-2025-05-14"];
1439
- const requestsCompaction = (body.context_management?.edits.length ?? 0) > 0;
1440
- const replaysCompaction = body.messages.some((message) => message.content.some((block) => block.type === "compaction"));
1441
- if (requestsCompaction || replaysCompaction)
1442
- betas.push("compact-2026-01-12");
1443
- const thinking = body.thinking;
1444
- if (thinking && thinking.type !== "disabled" && thinking.block_binding)
1445
- betas.push("thinking-binding-controls-2026-08-01");
1446
- return betas;
1447
- }
1448
1402
  export const route = Route.make({
1449
1403
  id: ADAPTER,
1450
1404
  provider: "anthropic",
@@ -219,15 +219,9 @@ export declare const protocol: Protocol<{
219
219
  readonly type: "enabled";
220
220
  readonly budget_tokens: number;
221
221
  readonly display?: "summarized" | "omitted" | undefined;
222
- readonly block_binding?: {
223
- readonly prefix_mismatch_behavior?: string | undefined;
224
- } | undefined;
225
222
  } | {
226
223
  readonly type: "adaptive";
227
224
  readonly display?: "summarized" | "omitted" | undefined;
228
- readonly block_binding?: {
229
- readonly prefix_mismatch_behavior?: string | undefined;
230
- } | undefined;
231
225
  } | {
232
226
  readonly type: "disabled";
233
227
  } | undefined;
@@ -1,6 +1,5 @@
1
1
  import { Headers } from "effect/unstable/http";
2
2
  import { HttpTransport, type Transport, type WebSocketChannelDriver } from "../route/transport/index.js";
3
- import { OpenResponsesContinuation } from "./open-responses-continuation.js";
4
3
  export interface Options {
5
4
  readonly id: string;
6
5
  readonly name: string;
@@ -8,7 +7,6 @@ export interface Options {
8
7
  readonly enabled?: (url: string) => boolean;
9
8
  readonly url?: (url: string) => string;
10
9
  readonly headers?: (headers: Headers.Headers) => Headers.Headers;
11
- readonly continuation?: OpenResponsesContinuation.Shape;
12
10
  }
13
11
  export interface Prepared {
14
12
  readonly http: HttpTransport.HttpPrepared<string>;
@@ -10,6 +10,7 @@ const WebSocketResponseCreate = Schema.StructWithRest(Schema.Struct({ type: Sche
10
10
  ]);
11
11
  const decodeMessage = ProviderShared.validateWith(Schema.decodeUnknownEffect(WebSocketResponseCreate));
12
12
  const encodeMessage = Schema.encodeSync(Schema.fromJsonString(WebSocketResponseCreate));
13
+ const decodeEvent = Schema.decodeUnknownEffect(OpenResponses.protocol.stream.event);
13
14
  const message = (body) => Effect.gen(function* () {
14
15
  if (!ProviderShared.isRecord(body))
15
16
  return yield* ProviderShared.invalidRequest("Open Responses WebSocket body must be a JSON object");
@@ -27,7 +28,7 @@ const driver = (options, body) => {
27
28
  return { message: body, mode: "full" };
28
29
  }),
29
30
  observe: (_create, frame) => Effect.gen(function* () {
30
- const event = yield* OpenResponses.decodeChannelEvent(frame).pipe(Effect.mapError((cause) => ProviderShared.eventError(options.id, `Invalid ${options.name} WebSocket event`, frame, cause)));
31
+ const event = yield* decodeEvent(frame).pipe(Effect.mapError((cause) => ProviderShared.eventError(options.id, `Invalid ${options.name} WebSocket event`, frame, cause)));
31
32
  if (terminal)
32
33
  return yield* ProviderShared.eventError(options.id, `${options.name} emitted ${event.type} after a terminal event`, frame);
33
34
  if (event.type === "error") {
@@ -96,7 +97,6 @@ export const transport = (options) => {
96
97
  request: create.request,
97
98
  message: create.message,
98
99
  base,
99
- continuation: options.continuation,
100
100
  }),
101
101
  };
102
102
  })
@@ -1,16 +1,12 @@
1
1
  import type { WebSocketChannelDriver } from "../route/transport/index.js";
2
- /**
3
- * Fields to send next to `previous_response_id` on an incremental step, or undefined to send the step in full.
4
- * Whether omitted fields carry over from the continued response is provider behavior the route must know.
5
- */
6
- export type Shape = (request: Readonly<Record<string, unknown>>) => Readonly<Record<string, unknown>> | undefined;
7
2
  export interface DriverInput {
8
3
  readonly id: string;
9
4
  readonly name: string;
10
5
  readonly request: Readonly<Record<string, unknown>>;
11
6
  readonly message: string;
12
7
  readonly base: WebSocketChannelDriver;
13
- readonly continuation?: Shape;
14
8
  }
15
9
  export declare const driver: (input: DriverInput) => WebSocketChannelDriver;
16
- export * as OpenResponsesContinuation from "./open-responses-continuation.js";
10
+ export declare const OpenResponsesContinuation: {
11
+ readonly driver: (input: DriverInput) => WebSocketChannelDriver;
12
+ };
@@ -4,6 +4,7 @@ import * as ProviderShared from "./shared.js";
4
4
  import { OpenResponses } from "./open-responses.js";
5
5
  const PROTOCOL = "open-responses.websocket.v1";
6
6
  const VERSION = 1;
7
+ const decodeEvent = Schema.decodeUnknownEffect(OpenResponses.protocol.stream.event);
7
8
  const checkpointValue = (checkpoint) => {
8
9
  if (checkpoint?.protocol !== PROTOCOL || !ProviderShared.isRecord(checkpoint.value))
9
10
  return undefined;
@@ -103,24 +104,21 @@ const rejected = (observation, recovery) => ({
103
104
  });
104
105
  export const driver = (input) => {
105
106
  const { previous_response_id: _previousResponseID, ...request } = input.request;
106
- const shape = input.continuation ?? ((fields) => fields);
107
107
  let output = [];
108
108
  return {
109
109
  create: (checkpoint) => Effect.sync(() => {
110
110
  output = [];
111
111
  const previous = checkpointValue(checkpoint);
112
- // Ask the route first: diffing the whole history is wasted when it declines the continuation.
113
- const fields = previous ? shape(request) : undefined;
114
- const delta = previous && fields ? incremental(request, previous) : undefined;
115
- if (!previous || !fields || !delta)
112
+ const delta = previous ? incremental(request, previous) : undefined;
113
+ if (!previous || !delta)
116
114
  return { message: ProviderShared.encodeJson(request), mode: "full" };
117
115
  return {
118
- message: ProviderShared.encodeJson({ ...fields, input: delta, previous_response_id: previous.responseID }),
116
+ message: ProviderShared.encodeJson({ ...request, input: delta, previous_response_id: previous.responseID }),
119
117
  mode: "incremental",
120
118
  };
121
119
  }),
122
120
  observe: (create, frame) => Effect.gen(function* () {
123
- const event = yield* OpenResponses.decodeChannelEvent(frame).pipe(Effect.mapError((cause) => ProviderShared.eventError(input.id, `Invalid ${input.name} WebSocket event`, frame, cause)));
121
+ const event = yield* decodeEvent(frame).pipe(Effect.mapError((cause) => ProviderShared.eventError(input.id, `Invalid ${input.name} WebSocket event`, frame, cause)));
124
122
  const observation = yield* input.base.observe(create, frame);
125
123
  if (event.type === "response.output_item.done" && event.item)
126
124
  output.push(event.item);
@@ -130,13 +128,6 @@ export const driver = (input) => {
130
128
  return rejected(observation, "retry-full");
131
129
  if (rejection === "websocket_connection_limit_reached")
132
130
  return rejected(observation, "rotate-and-retry-full");
133
- // Only the continuation distinguishes an incremental send from a full one, so an unclassified
134
- // invalid request there is retried full; Codex reports a stale previous_response_id that way, with
135
- // no code. Classified failures such as context overflow keep their runner-owned recovery.
136
- if (create.mode === "incremental" &&
137
- observation.error.reason._tag === "InvalidRequest" &&
138
- observation.error.reason.classification === undefined)
139
- return rejected(observation, "retry-full");
140
131
  }
141
132
  if (observation.type !== "completed")
142
133
  return observation;
@@ -156,7 +147,7 @@ export const driver = (input) => {
156
147
  responseID,
157
148
  request,
158
149
  // Completion can re-encrypt reasoning. Callers replay the item already emitted by output_item.done.
159
- output: event.response?.output?.length
150
+ output: event.response?.output
160
151
  ? event.response.output.map((item) => item.type === "reasoning" && item.id !== undefined
161
152
  ? (output.find((done) => done.type === item.type && done.id === item.id) ?? item)
162
153
  : item)
@@ -167,4 +158,4 @@ export const driver = (input) => {
167
158
  }),
168
159
  };
169
160
  };
170
- export * as OpenResponsesContinuation from "./open-responses-continuation.js";
161
+ export const OpenResponsesContinuation = { driver };