@opencode/ai 2.0.19 → 2.0.21

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 (56) hide show
  1. package/dist/cache-policy.js +21 -3
  2. package/dist/llm.d.ts +1 -1
  3. package/dist/promise.d.ts +2 -2
  4. package/dist/protocols/alibaba-chat.d.ts +16 -10
  5. package/dist/protocols/alibaba-responses.d.ts +14 -14
  6. package/dist/protocols/anthropic-messages.js +2 -15
  7. package/dist/protocols/bedrock-converse.d.ts +1 -1
  8. package/dist/protocols/bedrock-converse.js +38 -12
  9. package/dist/protocols/meta-messages.d.ts +5 -1
  10. package/dist/protocols/meta-messages.js +11 -4
  11. package/dist/protocols/meta-responses.d.ts +16 -16
  12. package/dist/protocols/mistral-chat.d.ts +6 -0
  13. package/dist/protocols/mistral-chat.js +8 -5
  14. package/dist/protocols/open-responses.d.ts +31 -31
  15. package/dist/protocols/openai-chat.d.ts +114 -71
  16. package/dist/protocols/openai-chat.js +31 -32
  17. package/dist/protocols/openai-compatible-chat.d.ts +16 -10
  18. package/dist/protocols/openai-compatible-responses.d.ts +2 -2
  19. package/dist/protocols/openai-responses.d.ts +22 -22
  20. package/dist/protocols/utils/claude-model.d.ts +8 -0
  21. package/dist/protocols/utils/claude-model.js +15 -0
  22. package/dist/protocols/utils/tool-stream.d.ts +3 -3
  23. package/dist/protocols/xai-responses.d.ts +14 -14
  24. package/dist/protocols/zai-chat.d.ts +16 -10
  25. package/dist/provider-error.d.ts +1 -0
  26. package/dist/provider-error.js +23 -1
  27. package/dist/providers/alibaba.d.ts +18 -12
  28. package/dist/providers/amazon-bedrock-mantle.d.ts +18 -12
  29. package/dist/providers/azure.d.ts +20 -14
  30. package/dist/providers/baseten.d.ts +32 -20
  31. package/dist/providers/cerebras.d.ts +32 -20
  32. package/dist/providers/cloudflare-ai-gateway.d.ts +36 -24
  33. package/dist/providers/cloudflare-ai-gateway.js +43 -35
  34. package/dist/providers/cloudflare-workers-ai.d.ts +32 -20
  35. package/dist/providers/deepinfra.d.ts +32 -20
  36. package/dist/providers/deepseek.d.ts +32 -20
  37. package/dist/providers/fireworks.d.ts +32 -20
  38. package/dist/providers/google-vertex-chat.d.ts +16 -10
  39. package/dist/providers/google-vertex-responses.d.ts +2 -2
  40. package/dist/providers/groq.d.ts +32 -20
  41. package/dist/providers/meta.d.ts +23 -13
  42. package/dist/providers/minimax.d.ts +18 -12
  43. package/dist/providers/moonshot.d.ts +18 -12
  44. package/dist/providers/openai-compatible-responses.d.ts +2 -2
  45. package/dist/providers/openai-compatible.d.ts +16 -10
  46. package/dist/providers/openai.d.ts +18 -12
  47. package/dist/providers/openrouter.d.ts +64 -40
  48. package/dist/providers/togetherai.d.ts +32 -20
  49. package/dist/providers/xai.d.ts +16 -10
  50. package/dist/providers/zai-coding-plan.d.ts +18 -12
  51. package/dist/providers/zai.d.ts +16 -10
  52. package/dist/route/client.d.ts +1 -1
  53. package/dist/route/executor.js +12 -8
  54. package/dist/schema/events.d.ts +30 -30
  55. package/dist/testing.d.ts +8 -8
  56. package/package.json +3 -3
@@ -36,12 +36,30 @@ const resolve = (policy) => {
36
36
  // prefix caching, Gemini's implicit + out-of-band CachedContent). Skip the
37
37
  // whole policy pass for these — emitting hints would be harmless but pointless.
38
38
  const RESPECTS_INLINE_HINTS = new Set([
39
+ "alibaba-messages",
39
40
  "anthropic-messages",
40
41
  "anthropic-compatible-messages",
42
+ "cloudflare-ai-gateway-messages",
41
43
  "google-vertex-messages",
44
+ "meta-messages",
45
+ "minimax-messages",
46
+ "moonshot-messages",
47
+ "zai-coding-messages",
42
48
  "bedrock-converse",
43
49
  "openrouter",
44
50
  ]);
51
+ // OpenRouter upstreams other than Anthropic and Alibaba Qwen cache without breakpoints. Gemini uses only the last
52
+ // breakpoint, so a conversation-tail breakpoint writes a new cache every step and costs more than none. Qwen ignores
53
+ // breakpoints on tool definitions and caches tools with the system prompt.
54
+ const openRouterPolicy = (modelID) => {
55
+ // `~anthropic/claude-sonnet-latest` style IDs are OpenRouter aliases for the latest model in a family.
56
+ const id = modelID.replace(/^~/, "");
57
+ if (id.startsWith("anthropic/"))
58
+ return AUTO;
59
+ if (id.startsWith("qwen/"))
60
+ return { system: true, messages: { tail: 1 } };
61
+ return NONE;
62
+ };
45
63
  const makeHint = (ttlSeconds) => ttlSeconds !== undefined ? new CacheHint({ type: "ephemeral", ttlSeconds }) : new CacheHint({ type: "ephemeral" });
46
64
  const markLastTool = (tools, hint, budget) => {
47
65
  const target = tools.at(-1);
@@ -122,9 +140,9 @@ const countHints = (request) => countToolHints(request.tools) +
122
140
  export const applyCachePolicy = (request) => {
123
141
  if (!RESPECTS_INLINE_HINTS.has(request.model.route.id))
124
142
  return request;
125
- if (request.model.route.id === "openrouter" && (request.cache === undefined || request.cache === "auto"))
126
- return request;
127
- const policy = resolve(request.cache);
143
+ const policy = request.model.route.id === "openrouter" && (request.cache === undefined || request.cache === "auto")
144
+ ? openRouterPolicy(request.model.id)
145
+ : resolve(request.cache);
128
146
  if (!policy.tools && !policy.system && !policy.messages)
129
147
  return request;
130
148
  const hint = makeHint(policy.ttlSeconds);
package/dist/llm.d.ts CHANGED
@@ -184,8 +184,8 @@ export declare class GenerateObjectResponse<T> {
184
184
  } | {
185
185
  readonly id: string;
186
186
  readonly type: "tool-error";
187
- readonly name: string;
188
187
  readonly message: string;
188
+ readonly name: string;
189
189
  readonly error?: unknown;
190
190
  readonly providerMetadata?: {
191
191
  readonly [x: string]: {
package/dist/promise.d.ts CHANGED
@@ -211,8 +211,8 @@ export declare const make: (options?: Options) => {
211
211
  } | {
212
212
  readonly id: string;
213
213
  readonly type: "tool-error";
214
- readonly name: string;
215
214
  readonly message: string;
215
+ readonly name: string;
216
216
  readonly error?: unknown;
217
217
  readonly providerMetadata?: {
218
218
  readonly [x: string]: {
@@ -698,8 +698,8 @@ export declare const ai: {
698
698
  } | {
699
699
  readonly id: string;
700
700
  readonly type: "tool-error";
701
- readonly name: string;
702
701
  readonly message: string;
702
+ readonly name: string;
703
703
  readonly error?: unknown;
704
704
  readonly providerMetadata?: {
705
705
  readonly [x: string]: {
@@ -80,15 +80,18 @@ export declare const protocol: Protocol<{
80
80
  })[];
81
81
  } | {
82
82
  readonly [x: string]: unknown;
83
- readonly content: string | null;
83
+ readonly content: string | readonly {
84
+ readonly type: "text";
85
+ readonly text: string;
86
+ readonly cache_control?: {
87
+ readonly type: "ephemeral";
88
+ readonly ttl?: string | undefined;
89
+ } | undefined;
90
+ }[] | null;
84
91
  readonly role: "assistant";
85
92
  readonly reasoning?: string | undefined;
86
93
  readonly reasoning_content?: string | undefined;
87
94
  readonly reasoning_text?: string | undefined;
88
- readonly cache_control?: {
89
- readonly type: "ephemeral";
90
- readonly ttl?: string | undefined;
91
- } | undefined;
92
95
  readonly tool_calls?: readonly {
93
96
  readonly function: {
94
97
  readonly name: string;
@@ -104,13 +107,16 @@ export declare const protocol: Protocol<{
104
107
  }[] | undefined;
105
108
  readonly reasoning_details?: unknown;
106
109
  } | {
107
- readonly content: string;
108
110
  readonly role: "tool";
109
111
  readonly tool_call_id: string;
110
- readonly cache_control?: {
111
- readonly type: "ephemeral";
112
- readonly ttl?: string | undefined;
113
- } | undefined;
112
+ readonly content: string | readonly {
113
+ readonly type: "text";
114
+ readonly text: string;
115
+ readonly cache_control?: {
116
+ readonly type: "ephemeral";
117
+ readonly ttl?: string | undefined;
118
+ } | undefined;
119
+ }[];
114
120
  })[];
115
121
  readonly stream: true;
116
122
  readonly max_completion_tokens?: number | undefined;
@@ -77,8 +77,8 @@ export declare const protocol: Protocol<{
77
77
  } | {
78
78
  readonly type: "input_file";
79
79
  readonly filename: string;
80
- readonly file_data?: string | undefined;
81
80
  readonly detail?: string | undefined;
81
+ readonly file_data?: string | undefined;
82
82
  readonly file_url?: string | undefined;
83
83
  } | {
84
84
  readonly type: "input_text";
@@ -114,8 +114,8 @@ export declare const protocol: Protocol<{
114
114
  } | {
115
115
  readonly type: "input_file";
116
116
  readonly filename: string;
117
- readonly file_data?: string | undefined;
118
117
  readonly detail?: string | undefined;
118
+ readonly file_data?: string | undefined;
119
119
  readonly file_url?: string | undefined;
120
120
  } | {
121
121
  readonly type: "input_text";
@@ -190,21 +190,10 @@ export declare const protocol: Protocol<{
190
190
  readonly [x: string]: unknown;
191
191
  readonly type: string;
192
192
  readonly status?: unknown;
193
- readonly code?: unknown;
194
193
  readonly message?: unknown;
194
+ readonly code?: unknown;
195
195
  readonly text?: string | undefined;
196
196
  readonly error?: unknown;
197
- readonly item?: {
198
- readonly [x: string]: unknown;
199
- readonly type: string;
200
- readonly id?: string | undefined;
201
- readonly name?: string | undefined;
202
- readonly namespace?: string | undefined;
203
- readonly arguments?: string | undefined;
204
- readonly encrypted_content?: string | null | undefined;
205
- readonly call_id?: string | undefined;
206
- } | null | undefined;
207
- readonly delta?: string | undefined;
208
197
  readonly response?: {
209
198
  readonly [x: string]: unknown;
210
199
  readonly id?: string | undefined;
@@ -236,6 +225,17 @@ export declare const protocol: Protocol<{
236
225
  readonly reason?: string | undefined;
237
226
  } | null | undefined;
238
227
  } | undefined;
228
+ readonly item?: {
229
+ readonly [x: string]: unknown;
230
+ readonly type: string;
231
+ readonly id?: string | undefined;
232
+ readonly name?: string | undefined;
233
+ readonly namespace?: string | undefined;
234
+ readonly arguments?: string | undefined;
235
+ readonly encrypted_content?: string | null | undefined;
236
+ readonly call_id?: string | undefined;
237
+ } | null | undefined;
238
+ readonly delta?: string | undefined;
239
239
  readonly arguments?: string | undefined;
240
240
  readonly item_id?: string | undefined;
241
241
  readonly output_index?: number | undefined;
@@ -13,6 +13,7 @@ import { JsonObject, knownString, optionalArray, optionalNull, ProviderShared }
13
13
  import { classifyProviderFailure } from "../provider-error.js";
14
14
  import { effortUpdate, resolveEffortUpdates } from "../effort-updates.js";
15
15
  import * as Cache from "./utils/cache.js";
16
+ import { claudeVersion, supportsThinkingBlockBinding, THINKING_BINDING_BETA } from "./utils/claude-model.js";
16
17
  import { Lifecycle } from "./utils/lifecycle.js";
17
18
  import { ToolStream } from "./utils/tool-stream.js";
18
19
  const ADAPTER = "anthropic-messages";
@@ -850,20 +851,6 @@ const lowerMessages = Effect.fn("AnthropicMessages.lowerMessages")(function* (re
850
851
  }
851
852
  return messages;
852
853
  });
853
- // Accept gateway namespaces and Vertex suffixes without treating a snapshot date as a minor version.
854
- const claudeVersion = (id) => {
855
- const match = /(?:^|[./])claude-(?<family>[a-z]+)-(?<major>\d+)(?:[.-](?<minor>\d{1,2}))?(?:$|[-:@])/.exec(id.toLowerCase())?.groups;
856
- if (!match)
857
- return undefined;
858
- return { family: match.family, major: Number(match.major), minor: Number(match.minor ?? 0) };
859
- };
860
- const supportsThinkingBlockBinding = (model) => {
861
- const override = model.compatibility?.supportsThinkingBlockBinding;
862
- if (override !== undefined)
863
- return override;
864
- const version = claudeVersion(model.id);
865
- return version !== undefined && (version.major > 5 || (version.major === 5 && version.minor >= 1));
866
- };
867
854
  const supportsEffortUpdates = (model) => {
868
855
  const override = model.compatibility?.supportsEffortUpdates;
869
856
  if (override !== undefined)
@@ -1437,7 +1424,7 @@ function requiredBetaHeaders(body) {
1437
1424
  betas.push("mid-conversation-output-config-2026-07-01");
1438
1425
  const thinking = body.thinking;
1439
1426
  if (thinking && thinking.type !== "disabled" && thinking.block_binding)
1440
- betas.push("thinking-binding-controls-2026-08-01");
1427
+ betas.push(THINKING_BINDING_BETA);
1441
1428
  return betas;
1442
1429
  }
1443
1430
  export const route = Route.make({
@@ -141,7 +141,7 @@ interface ParserState {
141
141
  readonly hasToolCalls: boolean;
142
142
  readonly lifecycle: Lifecycle.State;
143
143
  readonly reasoningSignatures: Readonly<Record<number, string>>;
144
- readonly reasoningRedactedContent: Readonly<Record<number, ReadonlyArray<Uint8Array>>>;
144
+ readonly reasoningRedactedContent: Readonly<Record<number, Uint8Array[]>>;
145
145
  }
146
146
  /**
147
147
  * The Bedrock Converse protocol — request body construction, body schema, and
@@ -9,6 +9,7 @@ import { JsonObject, optionalArray, ProviderShared } from "./shared.js";
9
9
  import { BedrockAuth } from "./utils/bedrock-auth.js";
10
10
  import { BedrockCache } from "./utils/bedrock-cache.js";
11
11
  import { BedrockMedia } from "./utils/bedrock-media.js";
12
+ import { supportsThinkingBlockBinding, THINKING_BINDING_BETA } from "./utils/claude-model.js";
12
13
  import { Lifecycle } from "./utils/lifecycle.js";
13
14
  import { MistralToolID } from "./utils/mistral-tool-id.js";
14
15
  import { ToolStream } from "./utils/tool-stream.js";
@@ -351,18 +352,33 @@ const Options = Schema.Struct({
351
352
  const decodeOptions = ProviderShared.validateWith(Schema.decodeUnknownEffect(Options));
352
353
  // Claude on Bedrock requires the thinking budget below `maxTokens`, with a minimum of 1,024.
353
354
  const MIN_THINKING_BUDGET = 1_024;
355
+ const isThinkingDisabled = Schema.is(Schema.Struct({
356
+ additionalModelRequestFields: Schema.Struct({ thinking: Schema.Struct({ type: Schema.Literal("disabled") }) }),
357
+ }));
358
+ // Claude 5.1+ binds each thinking signature to the prefix above it. Ask Bedrock to drop the affected blocks instead of
359
+ // failing when that prefix changes. `http.body` overlays this field by field, so callers can still override it.
360
+ const applyThinkingBindingDefault = (request, thinking) => {
361
+ if (isThinkingDisabled(request.http?.body))
362
+ return thinking;
363
+ if (!supportsThinkingBlockBinding(request.model))
364
+ return thinking;
365
+ return {
366
+ ...(thinking ?? { type: "adaptive" }),
367
+ block_binding: { prefix_mismatch_behavior: "drop_block" },
368
+ };
369
+ };
354
370
  const fromRequest = Effect.fn("BedrockConverse.fromRequest")(function* (request) {
355
371
  const toolChoice = request.toolChoice ? yield* lowerToolChoice(request.toolChoice) : undefined;
356
372
  const flattened = ProviderShared.flattenToolRequest(request);
357
373
  const generation = request.generation;
358
374
  const options = yield* decodeOptions(request.providerOptions ?? {});
359
375
  const maxTokens = isNova2(request.model) && isHighReasoningEffort(request.http?.body) ? undefined : generation?.maxTokens;
360
- const thinking = options.thinking === undefined
376
+ const thinking = applyThinkingBindingDefault(request, options.thinking === undefined
361
377
  ? undefined
362
378
  : {
363
379
  type: "enabled",
364
380
  budget_tokens: ProviderShared.fitThinkingBudget(options.thinking.budgetTokens, maxTokens, MIN_THINKING_BUDGET),
365
- };
381
+ });
366
382
  // Bedrock-Claude shares Anthropic's 4-breakpoint cap. Spend the budget in
367
383
  // tools → system → messages order to favour the highest-impact prefixes.
368
384
  const breakpoints = BedrockCache.breakpoints(request.model.id);
@@ -407,6 +423,8 @@ const fromRequest = Effect.fn("BedrockConverse.fromRequest")(function* (request)
407
423
  : {
408
424
  ...(generation?.topK === undefined ? {} : { top_k: generation.topK }),
409
425
  ...(thinking === undefined ? {} : { thinking }),
426
+ // Converse takes Anthropic betas in the body, and Bedrock rejects `block_binding` without this one.
427
+ ...(thinking?.block_binding === undefined ? {} : { anthropic_beta: [THINKING_BINDING_BETA] }),
410
428
  },
411
429
  };
412
430
  });
@@ -478,25 +496,29 @@ const step = (state, event) => Effect.gen(function* () {
478
496
  const index = event.contentBlockDelta.contentBlockIndex;
479
497
  const reasoning = event.contentBlockDelta.delta.reasoningContent;
480
498
  const events = [];
481
- const redactedChunks = yield* (() => {
499
+ const redactedChunk = yield* (() => {
482
500
  if (reasoning.redactedContent === undefined)
483
501
  return Effect.succeed(undefined);
484
- return Effect.fromResult(Encoding.decodeBase64(reasoning.redactedContent)).pipe(Effect.map((chunk) => [...(state.reasoningRedactedContent[index] ?? []), chunk]), Effect.mapError((cause) => ProviderShared.eventError(ADAPTER, "Bedrock Converse reasoningContent.redactedContent contains invalid base64 data", undefined, cause)));
502
+ return Effect.fromResult(Encoding.decodeBase64(reasoning.redactedContent)).pipe(Effect.mapError((cause) => ProviderShared.eventError(ADAPTER, "Bedrock Converse reasoningContent.redactedContent contains invalid base64 data", undefined, cause)));
485
503
  })();
486
- const redactedData = redactedChunks === undefined ? reasoning.data : encodeRedactedContent(redactedChunks);
504
+ const redactedChunks = state.reasoningRedactedContent[index] ?? [];
505
+ if (redactedChunk !== undefined)
506
+ redactedChunks.push(redactedChunk);
487
507
  const metadata = (() => {
488
508
  if (reasoning.signature)
489
509
  return providerMetadata(state.providerMetadataKey, { signature: reasoning.signature });
490
- if (redactedData !== undefined)
491
- return providerMetadata(state.providerMetadataKey, { redactedData });
510
+ if (redactedChunk === undefined && reasoning.data !== undefined)
511
+ return providerMetadata(state.providerMetadataKey, { redactedData: reasoning.data });
492
512
  })();
493
513
  const lifecycle = (() => {
494
- if (reasoning.text === undefined && metadata === undefined)
495
- return state.lifecycle;
496
- return Lifecycle.reasoningDelta(state.lifecycle, events, `reasoning-${index}`, reasoning.text ?? "", metadata);
514
+ if (reasoning.text !== undefined || metadata !== undefined)
515
+ return Lifecycle.reasoningDelta(state.lifecycle, events, `reasoning-${index}`, reasoning.text ?? "", metadata);
516
+ if (redactedChunk !== undefined)
517
+ return Lifecycle.reasoningStart(state.lifecycle, events, `reasoning-${index}`);
518
+ return state.lifecycle;
497
519
  })();
498
520
  const reasoningRedactedContent = (() => {
499
- if (redactedChunks !== undefined)
521
+ if (redactedChunk !== undefined)
500
522
  return { ...state.reasoningRedactedContent, [index]: redactedChunks };
501
523
  if (reasoning.data === undefined)
502
524
  return state.reasoningRedactedContent;
@@ -609,7 +631,11 @@ const onHalt = (state) => {
609
631
  return state.finishReason.normalized;
610
632
  })();
611
633
  const events = [];
612
- Lifecycle.finish(state.lifecycle, events, {
634
+ const lifecycle = Object.entries(state.reasoningRedactedContent).reduce((current, [index, chunks]) => {
635
+ const signature = state.reasoningSignatures[Number(index)];
636
+ return Lifecycle.reasoningEnd(current, events, `reasoning-${index}`, providerMetadata(state.providerMetadataKey, signature ? { signature } : { redactedData: encodeRedactedContent(chunks) }));
637
+ }, state.lifecycle);
638
+ Lifecycle.finish(lifecycle, events, {
613
639
  reason: {
614
640
  ...state.finishReason,
615
641
  normalized,
@@ -203,11 +203,15 @@ export declare const protocol: Protocol<{
203
203
  readonly timezone?: string | undefined;
204
204
  } | undefined;
205
205
  } | {
206
- readonly name: string;
207
206
  readonly description: string;
207
+ readonly name: string;
208
208
  readonly input_schema: {
209
209
  readonly [x: string]: unknown;
210
210
  };
211
+ readonly cache_control?: {
212
+ readonly type: "ephemeral";
213
+ readonly ttl?: "1h" | "5m" | undefined;
214
+ } | undefined;
211
215
  })[] | undefined;
212
216
  readonly system?: readonly {
213
217
  readonly type: "text";
@@ -8,12 +8,19 @@ const WebSearch = Schema.Struct({
8
8
  name: Schema.Literal("web_search"),
9
9
  user_location: MetaResponses.WebSearch.fields.user_location,
10
10
  });
11
+ const MetaCacheControl = Schema.Struct({
12
+ type: Schema.tag("ephemeral"),
13
+ ttl: Schema.optional(Schema.Literals(["5m", "1h"])),
14
+ });
15
+ const FunctionTool = Schema.Struct({
16
+ name: Schema.String,
17
+ description: Schema.String,
18
+ input_schema: JsonObject,
19
+ cache_control: Schema.optional(MetaCacheControl),
20
+ });
11
21
  const Body = Schema.Struct({
12
22
  ...AnthropicMessages.AnthropicMessagesBody.fields,
13
- tools: optionalArray(Schema.Union([
14
- Schema.Struct({ name: Schema.String, description: Schema.String, input_schema: JsonObject }),
15
- WebSearch,
16
- ])),
23
+ tools: optionalArray(Schema.Union([FunctionTool, WebSearch])),
17
24
  });
18
25
  const fromRequest = Effect.fn("MetaMessages.fromRequest")(function* (request) {
19
26
  const projected = ProviderShared.flattenToolRequest(request);
@@ -92,8 +92,8 @@ export declare const protocol: Protocol<{
92
92
  } | {
93
93
  readonly type: "input_file";
94
94
  readonly filename: string;
95
- readonly file_data?: string | undefined;
96
95
  readonly detail?: string | undefined;
96
+ readonly file_data?: string | undefined;
97
97
  readonly file_url?: string | undefined;
98
98
  } | {
99
99
  readonly type: "input_text";
@@ -129,8 +129,8 @@ export declare const protocol: Protocol<{
129
129
  } | {
130
130
  readonly type: "input_file";
131
131
  readonly filename: string;
132
- readonly file_data?: string | undefined;
133
132
  readonly detail?: string | undefined;
133
+ readonly file_data?: string | undefined;
134
134
  readonly file_url?: string | undefined;
135
135
  } | {
136
136
  readonly type: "input_text";
@@ -219,21 +219,10 @@ export declare const protocol: Protocol<{
219
219
  readonly [x: string]: unknown;
220
220
  readonly type: string;
221
221
  readonly status?: unknown;
222
- readonly code?: unknown;
223
222
  readonly message?: unknown;
223
+ readonly code?: unknown;
224
224
  readonly text?: string | undefined;
225
225
  readonly error?: unknown;
226
- readonly item?: {
227
- readonly [x: string]: unknown;
228
- readonly type: string;
229
- readonly id?: string | undefined;
230
- readonly name?: string | undefined;
231
- readonly namespace?: string | undefined;
232
- readonly arguments?: string | undefined;
233
- readonly encrypted_content?: string | null | undefined;
234
- readonly call_id?: string | undefined;
235
- } | null | undefined;
236
- readonly delta?: string | undefined;
237
226
  readonly response?: {
238
227
  readonly [x: string]: unknown;
239
228
  readonly id?: string | undefined;
@@ -265,6 +254,17 @@ export declare const protocol: Protocol<{
265
254
  readonly reason?: string | undefined;
266
255
  } | null | undefined;
267
256
  } | undefined;
257
+ readonly item?: {
258
+ readonly [x: string]: unknown;
259
+ readonly type: string;
260
+ readonly id?: string | undefined;
261
+ readonly name?: string | undefined;
262
+ readonly namespace?: string | undefined;
263
+ readonly arguments?: string | undefined;
264
+ readonly encrypted_content?: string | null | undefined;
265
+ readonly call_id?: string | undefined;
266
+ } | null | undefined;
267
+ readonly delta?: string | undefined;
268
268
  readonly arguments?: string | undefined;
269
269
  readonly item_id?: string | undefined;
270
270
  readonly output_index?: number | undefined;
@@ -338,8 +338,8 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
338
338
  } | {
339
339
  readonly type: "input_file";
340
340
  readonly filename: string;
341
- readonly file_data?: string | undefined;
342
341
  readonly detail?: string | undefined;
342
+ readonly file_data?: string | undefined;
343
343
  readonly file_url?: string | undefined;
344
344
  } | {
345
345
  readonly type: "input_text";
@@ -375,8 +375,8 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
375
375
  } | {
376
376
  readonly type: "input_file";
377
377
  readonly filename: string;
378
- readonly file_data?: string | undefined;
379
378
  readonly detail?: string | undefined;
379
+ readonly file_data?: string | undefined;
380
380
  readonly file_url?: string | undefined;
381
381
  } | {
382
382
  readonly type: "input_text";
@@ -8,6 +8,11 @@ import { Lifecycle } from "./utils/lifecycle.js";
8
8
  import { ToolStream } from "./utils/tool-stream.js";
9
9
  export declare const DEFAULT_BASE_URL = "https://api.mistral.ai/v1";
10
10
  export declare const PATH = "/chat/completions";
11
+ declare const MistralThinkingUnit: Schema.StructWithRest<Schema.Struct<{
12
+ readonly type: Schema.optional<Schema.String>;
13
+ readonly text: Schema.optional<Schema.String>;
14
+ }>, readonly [Schema.$Record<Schema.String, Schema.Unknown>]>;
15
+ type MistralThinkingUnit = Schema.Schema.Type<typeof MistralThinkingUnit>;
11
16
  declare const MistralThinkingContent: Schema.StructWithRest<Schema.Struct<{
12
17
  readonly type: Schema.Literal<"thinking">;
13
18
  readonly thinking: Schema.$Array<Schema.StructWithRest<Schema.Struct<{
@@ -212,6 +217,7 @@ interface ActiveContent {
212
217
  readonly type: "text" | "reasoning";
213
218
  readonly id: string;
214
219
  readonly thinking?: MistralThinkingContent;
220
+ readonly thinkingUnits?: MistralThinkingUnit[];
215
221
  }
216
222
  export interface ParserState {
217
223
  readonly tools: ToolStream.State<ToolKey>;
@@ -365,7 +365,7 @@ const closeActive = (state, events) => {
365
365
  return state;
366
366
  const lifecycle = state.active.type === "text"
367
367
  ? Lifecycle.textEnd(state.lifecycle, events, state.active.id)
368
- : Lifecycle.reasoningEnd(state.lifecycle, events, state.active.id, thinkingMetadata(state.active.thinking ?? { type: "thinking", thinking: [] }), thinkingText(state.active.thinking?.thinking ?? []));
368
+ : Lifecycle.reasoningEnd(state.lifecycle, events, state.active.id, thinkingMetadata({ ...state.active.thinking, type: "thinking", thinking: state.active.thinkingUnits ?? [] }), thinkingText(state.active.thinkingUnits ?? []));
369
369
  return { ...state, lifecycle, active: undefined };
370
370
  };
371
371
  const appendText = (state, events, text) => {
@@ -384,19 +384,22 @@ const appendThinking = (state, events, part) => {
384
384
  const current = state.active?.type === "reasoning" ? state : closeActive(state, events);
385
385
  const units = thinkingUnits(part.thinking);
386
386
  const active = current.active ?? { type: "reasoning", id: `reasoning-${current.nextContent}` };
387
+ // Keep native units out of streamed events until the block is complete.
388
+ const accumulated = active.thinkingUnits ?? [];
389
+ accumulated.push(...units);
387
390
  const thinking = {
388
391
  ...active.thinking,
389
392
  ...part,
390
393
  type: "thinking",
391
- thinking: [...(active.thinking?.thinking ?? []), ...units],
394
+ thinking: [],
392
395
  };
393
396
  const text = thinkingText(units);
394
397
  return {
395
398
  ...current,
396
399
  lifecycle: text.length > 0
397
- ? Lifecycle.reasoningDelta(current.lifecycle, events, active.id, text, thinkingMetadata(thinking))
398
- : Lifecycle.reasoningStart(current.lifecycle, events, active.id, thinkingMetadata(thinking)),
399
- active: { ...active, thinking },
400
+ ? Lifecycle.reasoningDelta(current.lifecycle, events, active.id, text)
401
+ : Lifecycle.reasoningStart(current.lifecycle, events, active.id),
402
+ active: { ...active, thinking, thinkingUnits: accumulated },
400
403
  nextContent: current.active ? current.nextContent : current.nextContent + 1,
401
404
  };
402
405
  };
@@ -629,21 +629,10 @@ export declare const decodeChannelEvent: (frame: string) => Effect.Effect<{
629
629
  readonly [x: string]: unknown;
630
630
  readonly type: string;
631
631
  readonly status?: unknown;
632
- readonly code?: unknown;
633
632
  readonly message?: unknown;
633
+ readonly code?: unknown;
634
634
  readonly text?: string | undefined;
635
635
  readonly error?: unknown;
636
- readonly item?: {
637
- readonly [x: string]: unknown;
638
- readonly type: string;
639
- readonly id?: string | undefined;
640
- readonly name?: string | undefined;
641
- readonly namespace?: string | undefined;
642
- readonly arguments?: string | undefined;
643
- readonly encrypted_content?: string | null | undefined;
644
- readonly call_id?: string | undefined;
645
- } | null | undefined;
646
- readonly delta?: string | undefined;
647
636
  readonly response?: {
648
637
  readonly [x: string]: unknown;
649
638
  readonly id?: string | undefined;
@@ -675,6 +664,17 @@ export declare const decodeChannelEvent: (frame: string) => Effect.Effect<{
675
664
  readonly reason?: string | undefined;
676
665
  } | null | undefined;
677
666
  } | undefined;
667
+ readonly item?: {
668
+ readonly [x: string]: unknown;
669
+ readonly type: string;
670
+ readonly id?: string | undefined;
671
+ readonly name?: string | undefined;
672
+ readonly namespace?: string | undefined;
673
+ readonly arguments?: string | undefined;
674
+ readonly encrypted_content?: string | null | undefined;
675
+ readonly call_id?: string | undefined;
676
+ } | null | undefined;
677
+ readonly delta?: string | undefined;
678
678
  readonly arguments?: string | undefined;
679
679
  readonly item_id?: string | undefined;
680
680
  readonly output_index?: number | undefined;
@@ -896,8 +896,8 @@ export declare const fromRequest: (request: LLMRequest<import("../schema/options
896
896
  } | {
897
897
  readonly type: "input_file";
898
898
  readonly filename: string;
899
- readonly file_data?: string | undefined;
900
899
  readonly detail?: string | undefined;
900
+ readonly file_data?: string | undefined;
901
901
  readonly file_url?: string | undefined;
902
902
  } | {
903
903
  readonly type: "input_text";
@@ -933,8 +933,8 @@ export declare const fromRequest: (request: LLMRequest<import("../schema/options
933
933
  } | {
934
934
  readonly type: "input_file";
935
935
  readonly filename: string;
936
- readonly file_data?: string | undefined;
937
936
  readonly detail?: string | undefined;
937
+ readonly file_data?: string | undefined;
938
938
  readonly file_url?: string | undefined;
939
939
  } | {
940
940
  readonly type: "input_text";
@@ -1169,8 +1169,8 @@ export declare const step: (state: ParserState, event: NormalizedEvent) => AIErr
1169
1169
  } | {
1170
1170
  readonly id: string;
1171
1171
  readonly type: "tool-error";
1172
- readonly name: string;
1173
1172
  readonly message: string;
1173
+ readonly name: string;
1174
1174
  readonly error?: unknown;
1175
1175
  readonly providerMetadata?: {
1176
1176
  readonly [x: string]: {
@@ -1293,8 +1293,8 @@ export declare const protocol: Protocol<{
1293
1293
  } | {
1294
1294
  readonly type: "input_file";
1295
1295
  readonly filename: string;
1296
- readonly file_data?: string | undefined;
1297
1296
  readonly detail?: string | undefined;
1297
+ readonly file_data?: string | undefined;
1298
1298
  readonly file_url?: string | undefined;
1299
1299
  } | {
1300
1300
  readonly type: "input_text";
@@ -1330,8 +1330,8 @@ export declare const protocol: Protocol<{
1330
1330
  } | {
1331
1331
  readonly type: "input_file";
1332
1332
  readonly filename: string;
1333
- readonly file_data?: string | undefined;
1334
1333
  readonly detail?: string | undefined;
1334
+ readonly file_data?: string | undefined;
1335
1335
  readonly file_url?: string | undefined;
1336
1336
  } | {
1337
1337
  readonly type: "input_text";
@@ -1395,21 +1395,10 @@ export declare const protocol: Protocol<{
1395
1395
  readonly [x: string]: unknown;
1396
1396
  readonly type: string;
1397
1397
  readonly status?: unknown;
1398
- readonly code?: unknown;
1399
1398
  readonly message?: unknown;
1399
+ readonly code?: unknown;
1400
1400
  readonly text?: string | undefined;
1401
1401
  readonly error?: unknown;
1402
- readonly item?: {
1403
- readonly [x: string]: unknown;
1404
- readonly type: string;
1405
- readonly id?: string | undefined;
1406
- readonly name?: string | undefined;
1407
- readonly namespace?: string | undefined;
1408
- readonly arguments?: string | undefined;
1409
- readonly encrypted_content?: string | null | undefined;
1410
- readonly call_id?: string | undefined;
1411
- } | null | undefined;
1412
- readonly delta?: string | undefined;
1413
1402
  readonly response?: {
1414
1403
  readonly [x: string]: unknown;
1415
1404
  readonly id?: string | undefined;
@@ -1441,6 +1430,17 @@ export declare const protocol: Protocol<{
1441
1430
  readonly reason?: string | undefined;
1442
1431
  } | null | undefined;
1443
1432
  } | undefined;
1433
+ readonly item?: {
1434
+ readonly [x: string]: unknown;
1435
+ readonly type: string;
1436
+ readonly id?: string | undefined;
1437
+ readonly name?: string | undefined;
1438
+ readonly namespace?: string | undefined;
1439
+ readonly arguments?: string | undefined;
1440
+ readonly encrypted_content?: string | null | undefined;
1441
+ readonly call_id?: string | undefined;
1442
+ } | null | undefined;
1443
+ readonly delta?: string | undefined;
1444
1444
  readonly arguments?: string | undefined;
1445
1445
  readonly item_id?: string | undefined;
1446
1446
  readonly output_index?: number | undefined;
@@ -1514,8 +1514,8 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
1514
1514
  } | {
1515
1515
  readonly type: "input_file";
1516
1516
  readonly filename: string;
1517
- readonly file_data?: string | undefined;
1518
1517
  readonly detail?: string | undefined;
1518
+ readonly file_data?: string | undefined;
1519
1519
  readonly file_url?: string | undefined;
1520
1520
  } | {
1521
1521
  readonly type: "input_text";
@@ -1551,8 +1551,8 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
1551
1551
  } | {
1552
1552
  readonly type: "input_file";
1553
1553
  readonly filename: string;
1554
- readonly file_data?: string | undefined;
1555
1554
  readonly detail?: string | undefined;
1555
+ readonly file_data?: string | undefined;
1556
1556
  readonly file_url?: string | undefined;
1557
1557
  } | {
1558
1558
  readonly type: "input_text";