@opencode-ai/ai 0.0.0-next-15845 → 0.0.0-next-15848

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.
@@ -37,6 +37,7 @@ export declare const bodyFields: {
37
37
  readonly reasoning_content: Schema.optional<Schema.String>;
38
38
  readonly reasoning: Schema.optional<Schema.String>;
39
39
  readonly reasoning_text: Schema.optional<Schema.String>;
40
+ readonly reasoning_details: Schema.optional<Schema.$Array<Schema.Unknown>>;
40
41
  }>, Schema.Struct<{
41
42
  readonly role: Schema.Literal<"tool">;
42
43
  readonly tool_call_id: Schema.String;
@@ -100,6 +101,7 @@ declare const OpenAIChatBody: Schema.Struct<{
100
101
  readonly reasoning_content: Schema.optional<Schema.String>;
101
102
  readonly reasoning: Schema.optional<Schema.String>;
102
103
  readonly reasoning_text: Schema.optional<Schema.String>;
104
+ readonly reasoning_details: Schema.optional<Schema.$Array<Schema.Unknown>>;
103
105
  }>, Schema.Struct<{
104
106
  readonly role: Schema.Literal<"tool">;
105
107
  readonly tool_call_id: Schema.String;
@@ -141,6 +143,7 @@ export declare const OpenAIChatEvent: Schema.Struct<{
141
143
  readonly reasoning_content: Schema.optional<Schema.NullOr<Schema.String>>;
142
144
  readonly reasoning: Schema.optional<Schema.NullOr<Schema.String>>;
143
145
  readonly reasoning_text: Schema.optional<Schema.NullOr<Schema.String>>;
146
+ readonly reasoning_details: Schema.optional<Schema.NullOr<Schema.$Array<Schema.Unknown>>>;
144
147
  readonly tool_calls: Schema.optional<Schema.NullOr<Schema.$Array<Schema.Struct<{
145
148
  readonly index: Schema.Number;
146
149
  readonly id: Schema.optional<Schema.NullOr<Schema.String>>;
@@ -178,6 +181,9 @@ export interface ParserState {
178
181
  readonly finishReason?: FinishReason;
179
182
  readonly lifecycle: Lifecycle.State;
180
183
  readonly reasoningField?: "reasoning" | "reasoning_content" | "reasoning_text";
184
+ readonly reasoningDetails: Array<unknown>;
185
+ readonly reasoningDetailsObserved: boolean;
186
+ readonly reasoningEmitted: boolean;
181
187
  }
182
188
  /**
183
189
  * The OpenAI Chat protocol — request body construction, body schema, and the
@@ -214,6 +220,7 @@ export declare const protocol: Protocol<{
214
220
  }, "Type">[] | undefined;
215
221
  readonly reasoning_content?: string | undefined;
216
222
  readonly reasoning_text?: string | undefined;
223
+ readonly reasoning_details?: readonly unknown[] | undefined;
217
224
  } | Schema.Struct.ReadonlySide<{
218
225
  readonly role: Schema.Literal<"tool">;
219
226
  readonly tool_call_id: Schema.String;
@@ -262,6 +269,7 @@ export declare const protocol: Protocol<{
262
269
  }[] | null | undefined;
263
270
  readonly reasoning_content?: string | null | undefined;
264
271
  readonly reasoning_text?: string | null | undefined;
272
+ readonly reasoning_details?: readonly unknown[] | null | undefined;
265
273
  } | null | undefined;
266
274
  readonly finish_reason?: string | null | undefined;
267
275
  }[];
@@ -306,6 +314,7 @@ export declare const httpTransport: HttpTransport.HttpJsonTransport<{
306
314
  }, "Type">[] | undefined;
307
315
  readonly reasoning_content?: string | undefined;
308
316
  readonly reasoning_text?: string | undefined;
317
+ readonly reasoning_details?: readonly unknown[] | undefined;
309
318
  } | Schema.Struct.ReadonlySide<{
310
319
  readonly role: Schema.Literal<"tool">;
311
320
  readonly tool_call_id: Schema.String;
@@ -369,6 +378,7 @@ export declare const route: Route<{
369
378
  }, "Type">[] | undefined;
370
379
  readonly reasoning_content?: string | undefined;
371
380
  readonly reasoning_text?: string | undefined;
381
+ readonly reasoning_details?: readonly unknown[] | undefined;
372
382
  } | Schema.Struct.ReadonlySide<{
373
383
  readonly role: Schema.Literal<"tool">;
374
384
  readonly tool_call_id: Schema.String;
@@ -57,6 +57,7 @@ const OpenAIChatMessage = Schema.Union([
57
57
  reasoning_content: Schema.optional(Schema.String),
58
58
  reasoning: Schema.optional(Schema.String),
59
59
  reasoning_text: Schema.optional(Schema.String),
60
+ reasoning_details: optionalArray(Schema.Unknown),
60
61
  }),
61
62
  Schema.Struct({ role: Schema.Literal("tool"), tool_call_id: Schema.String, content: Schema.String }),
62
63
  ]).pipe(Schema.toTaggedUnion("role"));
@@ -116,6 +117,7 @@ const OpenAIChatDelta = Schema.Struct({
116
117
  reasoning_content: optionalNull(Schema.String),
117
118
  reasoning: optionalNull(Schema.String),
118
119
  reasoning_text: optionalNull(Schema.String),
120
+ reasoning_details: optionalNull(Schema.Array(Schema.Unknown)),
119
121
  tool_calls: optionalNull(Schema.Array(OpenAIChatToolCallDelta)),
120
122
  });
121
123
  const OpenAIChatChoice = Schema.Struct({
@@ -163,7 +165,16 @@ const reasoningField = (part) => {
163
165
  const field = part.providerMetadata?.openai?.reasoningField;
164
166
  if (field === "reasoning" || field === "reasoning_content" || field === "reasoning_text")
165
167
  return field;
166
- return "reasoning_content";
168
+ };
169
+ const reasoningDetails = (parts, native) => {
170
+ const observed = parts.flatMap((part) => {
171
+ const details = part.providerMetadata?.openai?.reasoningDetails;
172
+ return Array.isArray(details) ? details : [];
173
+ });
174
+ if (parts.some((part) => Array.isArray(part.providerMetadata?.openai?.reasoningDetails)))
175
+ return observed;
176
+ if (isRecord(native) && Array.isArray(native.reasoning_details))
177
+ return native.reasoning_details;
167
178
  };
168
179
  const lowerUserMessage = Effect.fn("OpenAIChat.lowerUserMessage")(function* (message) {
169
180
  const content = [];
@@ -203,18 +214,34 @@ const lowerAssistantMessage = Effect.fn("OpenAIChat.lowerAssistantMessage")(func
203
214
  }
204
215
  }
205
216
  const text = reasoning.map((part) => part.text).join("");
206
- const field = reasoning[0] ? reasoningField(reasoning[0]) : "reasoning_content";
217
+ const details = reasoningDetails(reasoning, message.native?.openaiCompatible);
218
+ const observedField = reasoning.map(reasoningField).find((value) => value !== undefined);
219
+ const nativeReasoning = openAICompatibleReasoningContent(message.native?.openaiCompatible);
220
+ const fullyStructured = reasoning.every((part) => Array.isArray(part.providerMetadata?.openai?.reasoningDetails));
221
+ const field = (() => {
222
+ if (reasoning.length === 0)
223
+ return;
224
+ if (observedField !== undefined)
225
+ return observedField;
226
+ if (nativeReasoning !== undefined)
227
+ return "reasoning_content";
228
+ if (!fullyStructured)
229
+ return "reasoning_content";
230
+ })();
231
+ const reasoningContent = (() => {
232
+ if (reasoning.length === 0)
233
+ return nativeReasoning;
234
+ if (field === "reasoning_content")
235
+ return text;
236
+ })();
207
237
  return {
208
238
  role: "assistant",
209
239
  content: content.length === 0 ? null : ProviderShared.joinText(content),
210
240
  tool_calls: toolCalls.length === 0 ? undefined : toolCalls,
211
- reasoning_content: reasoning.length === 0
212
- ? openAICompatibleReasoningContent(message.native?.openaiCompatible)
213
- : field === "reasoning_content"
214
- ? text
215
- : undefined,
241
+ reasoning_content: reasoningContent,
216
242
  reasoning: reasoning.length > 0 && field === "reasoning" ? text : undefined,
217
243
  reasoning_text: reasoning.length > 0 && field === "reasoning_text" ? text : undefined,
244
+ reasoning_details: details,
218
245
  };
219
246
  });
220
247
  const lowerToolMessages = Effect.fn("OpenAIChat.lowerToolMessages")(function* (message) {
@@ -360,6 +387,51 @@ const reasoningDelta = (delta) => {
360
387
  if (delta?.reasoning_text)
361
388
  return { field: "reasoning_text", text: delta.reasoning_text };
362
389
  };
390
+ const detailText = (details) => {
391
+ const text = details.flatMap((detail) => {
392
+ if (!isRecord(detail))
393
+ return [];
394
+ if (detail.type === "reasoning.text" && typeof detail.text === "string" && detail.text)
395
+ return [detail.text];
396
+ if (detail.type === "reasoning.summary" && typeof detail.summary === "string" && detail.summary)
397
+ return [detail.summary];
398
+ return [];
399
+ });
400
+ if (text.length > 0)
401
+ return text.join("");
402
+ };
403
+ const appendReasoningDetails = (result, details) => {
404
+ for (const detail of details) {
405
+ const previous = result.at(-1);
406
+ if (!isRecord(previous) ||
407
+ previous.type !== "reasoning.text" ||
408
+ !isRecord(detail) ||
409
+ detail.type !== "reasoning.text" ||
410
+ conflictingReasoningTextDetails(previous, detail)) {
411
+ result.push(detail);
412
+ continue;
413
+ }
414
+ result[result.length - 1] = {
415
+ ...previous,
416
+ ...Object.fromEntries(Object.entries(detail).filter((entry) => entry[1] !== undefined)),
417
+ text: `${typeof previous.text === "string" ? previous.text : ""}${typeof detail.text === "string" ? detail.text : ""}`,
418
+ signature: mergeDetailValue(previous.signature, detail.signature),
419
+ format: mergeDetailValue(previous.format, detail.format),
420
+ };
421
+ }
422
+ };
423
+ const mergeDetailValue = (previous, current) => previous || current || (previous !== undefined ? previous : current);
424
+ const conflictingReasoningTextDetails = (previous, current) => conflictingDetailValue(previous.id, current.id) ||
425
+ conflictingDetailValue(previous.index, current.index) ||
426
+ conflictingDetailValue(previous.format, current.format) ||
427
+ (Boolean(previous.signature) && Boolean(current.signature) && previous.signature !== current.signature);
428
+ const conflictingDetailValue = (previous, current) => previous !== undefined && previous !== null && current !== undefined && current !== null && previous !== current;
429
+ const reasoningMetadata = (field, details) => ({
430
+ openai: {
431
+ ...(field ? { reasoningField: field } : {}),
432
+ ...(details ? { reasoningDetails: details } : {}),
433
+ },
434
+ });
363
435
  const step = (state, event) => Effect.gen(function* () {
364
436
  const events = [];
365
437
  const usage = mapUsage(event.usage) ?? state.usage;
@@ -371,17 +443,24 @@ const step = (state, event) => Effect.gen(function* () {
371
443
  let pendingTools = state.pendingTools;
372
444
  let lifecycle = state.lifecycle;
373
445
  const reasoning = reasoningDelta(delta);
374
- const reasoningField = state.reasoningField ?? reasoning?.field;
375
- if (reasoning)
376
- lifecycle = Lifecycle.reasoningDelta(lifecycle, events, "reasoning-0", reasoning.text, {
377
- openai: { reasoningField: reasoningField ?? reasoning.field },
378
- });
446
+ const reasoningField = state.reasoningField ?? (!state.lifecycle.text.has("text-0") ? reasoning?.field : undefined);
447
+ const detailDelta = Array.isArray(delta?.reasoning_details) ? delta.reasoning_details : undefined;
448
+ if (detailDelta !== undefined)
449
+ appendReasoningDetails(state.reasoningDetails, detailDelta);
450
+ const reasoningDetailsObserved = state.reasoningDetailsObserved || detailDelta !== undefined;
451
+ const deltaMetadata = reasoningMetadata(reasoningField);
452
+ const text = detailDelta?.length ? (detailText(detailDelta) ?? reasoning?.text) : reasoning?.text;
453
+ if (!state.lifecycle.text.has("text-0") && text !== undefined)
454
+ lifecycle = Lifecycle.reasoningDelta(lifecycle, events, "reasoning-0", text, deltaMetadata);
455
+ else if (reasoningDetailsObserved &&
456
+ !lifecycle.reasoning.has("reasoning-0") &&
457
+ (Boolean(delta?.content) || toolDeltas.length > 0))
458
+ lifecycle = Lifecycle.reasoningStart(lifecycle, events, "reasoning-0", deltaMetadata);
459
+ const reasoningEmitted = state.reasoningEmitted || lifecycle.reasoning.has("reasoning-0");
379
460
  if (delta?.content) {
380
- lifecycle = Lifecycle.reasoningEnd(lifecycle, events, "reasoning-0");
461
+ lifecycle = Lifecycle.reasoningEnd(lifecycle, events, "reasoning-0", reasoningMetadata(reasoningField, reasoningDetailsObserved ? state.reasoningDetails : undefined));
381
462
  lifecycle = Lifecycle.textDelta(lifecycle, events, "text-0", delta.content);
382
463
  }
383
- if (toolDeltas.length)
384
- lifecycle = Lifecycle.reasoningEnd(lifecycle, events, "reasoning-0");
385
464
  for (const tool of toolDeltas) {
386
465
  const current = tools[tool.index];
387
466
  const pending = pendingTools[tool.index];
@@ -420,6 +499,9 @@ const step = (state, event) => Effect.gen(function* () {
420
499
  finishReason,
421
500
  lifecycle,
422
501
  reasoningField,
502
+ reasoningDetails: state.reasoningDetails,
503
+ reasoningDetailsObserved,
504
+ reasoningEmitted,
423
505
  },
424
506
  events,
425
507
  ];
@@ -428,7 +510,12 @@ const finishEvents = (state) => {
428
510
  const events = [];
429
511
  const hasToolCalls = state.toolCallEvents.length > 0;
430
512
  const reason = state.finishReason === "stop" && hasToolCalls ? "tool-calls" : state.finishReason;
431
- const lifecycle = state.toolCallEvents.length ? Lifecycle.stepStart(state.lifecycle, events) : state.lifecycle;
513
+ const metadata = reasoningMetadata(state.reasoningField, state.reasoningDetailsObserved ? state.reasoningDetails : undefined);
514
+ const started = state.reasoningDetailsObserved && !state.reasoningEmitted
515
+ ? Lifecycle.reasoningStart(state.lifecycle, events, "reasoning-0", reasoningMetadata(state.reasoningField))
516
+ : state.lifecycle;
517
+ const ended = Lifecycle.reasoningEnd(started, events, "reasoning-0", metadata);
518
+ const lifecycle = state.toolCallEvents.length ? Lifecycle.stepStart(ended, events) : ended;
432
519
  events.push(...state.toolCallEvents);
433
520
  if (reason)
434
521
  Lifecycle.finish(lifecycle, events, { reason, usage: state.usage });
@@ -457,6 +544,9 @@ export const protocol = Protocol.make({
457
544
  toolCallEvents: [],
458
545
  lifecycle: Lifecycle.initial(),
459
546
  reasoningField: undefined,
547
+ reasoningDetails: [],
548
+ reasoningDetailsObserved: false,
549
+ reasoningEmitted: false,
460
550
  }),
461
551
  step,
462
552
  onHalt: finishEvents,
@@ -36,6 +36,7 @@ export declare const route: Route<{
36
36
  }, "Type">[] | undefined;
37
37
  readonly reasoning_content?: string | undefined;
38
38
  readonly reasoning_text?: string | undefined;
39
+ readonly reasoning_details?: readonly unknown[] | undefined;
39
40
  } | import("effect/Schema").Struct.ReadonlySide<{
40
41
  readonly role: import("effect/Schema").Literal<"tool">;
41
42
  readonly tool_call_id: import("effect/Schema").String;
@@ -24,7 +24,7 @@ export const reasoningStart = (state, events, id, providerMetadata) => {
24
24
  };
25
25
  export const reasoningDelta = (state, events, id, text, providerMetadata) => {
26
26
  const started = reasoningStart(state, events, id, providerMetadata);
27
- events.push(LLMEvent.reasoningDelta({ id, text }));
27
+ events.push(LLMEvent.reasoningDelta({ id, text, providerMetadata }));
28
28
  return started;
29
29
  };
30
30
  export const reasoningEnd = (state, events, id, providerMetadata) => {
@@ -2,13 +2,17 @@ import { Option, Schema } from "effect";
2
2
  import { AuthenticationReason, ContentPolicyReason, InvalidRequestReason, LLMError, ProviderErrorEvent, ProviderInternalReason, QuotaExceededReason, RateLimitReason, UnknownProviderReason, } from "./schema";
3
3
  const patterns = [
4
4
  /prompt is too long/i,
5
+ /request_too_large/i,
5
6
  /input is too long for requested model/i,
6
7
  /exceeds the context window/i,
8
+ /exceeds (?:the )?(?:model'?s )?maximum context length(?: of [\d,]+ tokens?|\s*\([\d,]+\))/i,
7
9
  /input token count.*exceeds the maximum/i,
8
10
  /tokens in request more than max tokens allowed/i,
9
11
  /maximum prompt length is \d+/i,
10
12
  /reduce the length of the messages/i,
11
13
  /maximum context length is \d+ tokens/i,
14
+ /exceeds (?:the )?maximum allowed input length of [\d,]+ tokens?/i,
15
+ /input \(\d+ tokens\) is longer than the model'?s context length \(\d+ tokens\)/i,
12
16
  /exceeds the limit of \d+/i,
13
17
  /exceeds the available context size/i,
14
18
  /greater than the context length/i,
@@ -20,9 +24,14 @@ const patterns = [
20
24
  /input length.*exceeds.*context length/i,
21
25
  /prompt too long; exceeded (?:max )?context length/i,
22
26
  /too large for model with \d+ maximum context length/i,
27
+ /prompt has [\d,]+ tokens?, but the configured context size is [\d,]+ tokens?/i,
23
28
  /model_context_window_exceeded/i,
29
+ /too many tokens/i,
30
+ /token limit exceeded/i,
24
31
  ];
25
- export const isContextOverflow = (message) => patterns.some((pattern) => pattern.test(message)) || /^4(00|13)\s*(status code)?\s*\(no body\)/i.test(message);
32
+ const exclusions = [/^(throttling error|service unavailable):/i, /rate limit/i, /too many requests/i];
33
+ export const isContextOverflow = (message) => !exclusions.some((pattern) => pattern.test(message)) &&
34
+ (patterns.some((pattern) => pattern.test(message)) || /^4(00|13)\s*(status code)?\s*\(no body\)/i.test(message));
26
35
  export const isContextOverflowFailure = (failure) => failure instanceof LLMError
27
36
  ? failure.reason._tag === "InvalidRequest" && failure.reason.classification === "context-overflow"
28
37
  : Schema.is(ProviderErrorEvent)(failure) && failure.classification === "context-overflow";
@@ -50,6 +50,7 @@ export declare const routes: (RouteDef<{
50
50
  }, "Type">[] | undefined;
51
51
  readonly reasoning_content?: string | undefined;
52
52
  readonly reasoning_text?: string | undefined;
53
+ readonly reasoning_details?: readonly unknown[] | undefined;
53
54
  } | import("effect/Schema").Struct.ReadonlySide<{
54
55
  readonly role: import("effect/Schema").Literal<"tool">;
55
56
  readonly tool_call_id: import("effect/Schema").String;
@@ -53,6 +53,7 @@ export declare const aiGatewayRoute: import("../route").Route<{
53
53
  }, "Type">[] | undefined;
54
54
  readonly reasoning_content?: string | undefined;
55
55
  readonly reasoning_text?: string | undefined;
56
+ readonly reasoning_details?: readonly unknown[] | undefined;
56
57
  } | import("effect/Schema").Struct.ReadonlySide<{
57
58
  readonly role: import("effect/Schema").Literal<"tool">;
58
59
  readonly tool_call_id: import("effect/Schema").String;
@@ -116,6 +117,7 @@ export declare const workersAIRoute: import("../route").Route<{
116
117
  }, "Type">[] | undefined;
117
118
  readonly reasoning_content?: string | undefined;
118
119
  readonly reasoning_text?: string | undefined;
120
+ readonly reasoning_details?: readonly unknown[] | undefined;
119
121
  } | import("effect/Schema").Struct.ReadonlySide<{
120
122
  readonly role: import("effect/Schema").Literal<"tool">;
121
123
  readonly tool_call_id: import("effect/Schema").String;
@@ -179,6 +181,7 @@ export declare const routes: import("../route").Route<{
179
181
  }, "Type">[] | undefined;
180
182
  readonly reasoning_content?: string | undefined;
181
183
  readonly reasoning_text?: string | undefined;
184
+ readonly reasoning_details?: readonly unknown[] | undefined;
182
185
  } | import("effect/Schema").Struct.ReadonlySide<{
183
186
  readonly role: import("effect/Schema").Literal<"tool">;
184
187
  readonly tool_call_id: import("effect/Schema").String;
@@ -38,6 +38,7 @@ export declare const routes: (import("../route").Route<{
38
38
  }, "Type">[] | undefined;
39
39
  readonly reasoning_content?: string | undefined;
40
40
  readonly reasoning_text?: string | undefined;
41
+ readonly reasoning_details?: readonly unknown[] | undefined;
41
42
  } | import("effect/Schema").Struct.ReadonlySide<{
42
43
  readonly role: import("effect/Schema").Literal<"tool">;
43
44
  readonly tool_call_id: import("effect/Schema").String;
@@ -45,6 +45,7 @@ export declare const routes: import("../route").Route<{
45
45
  }, "Type">[] | undefined;
46
46
  readonly reasoning_content?: string | undefined;
47
47
  readonly reasoning_text?: string | undefined;
48
+ readonly reasoning_details?: readonly unknown[] | undefined;
48
49
  } | import("effect/Schema").Struct.ReadonlySide<{
49
50
  readonly role: import("effect/Schema").Literal<"tool">;
50
51
  readonly tool_call_id: import("effect/Schema").String;
@@ -44,6 +44,7 @@ export declare const routes: import("../route").Route<{
44
44
  }, "Type">[] | undefined;
45
45
  readonly reasoning_content?: string | undefined;
46
46
  readonly reasoning_text?: string | undefined;
47
+ readonly reasoning_details?: readonly unknown[] | undefined;
47
48
  } | import("effect/Schema").Struct.ReadonlySide<{
48
49
  readonly role: import("effect/Schema").Literal<"tool">;
49
50
  readonly tool_call_id: import("effect/Schema").String;
@@ -36,6 +36,7 @@ export declare const routes: (Route<{
36
36
  }, "Type">[] | undefined;
37
37
  readonly reasoning_content?: string | undefined;
38
38
  readonly reasoning_text?: string | undefined;
39
+ readonly reasoning_details?: readonly unknown[] | undefined;
39
40
  } | import("effect/Schema").Struct.ReadonlySide<{
40
41
  readonly role: import("effect/Schema").Literal<"tool">;
41
42
  readonly tool_call_id: import("effect/Schema").String;
@@ -52,6 +52,7 @@ declare const OpenRouterBody: Schema.StructWithRest<Schema.Struct<{
52
52
  readonly reasoning_content: Schema.optional<Schema.String>;
53
53
  readonly reasoning: Schema.optional<Schema.String>;
54
54
  readonly reasoning_text: Schema.optional<Schema.String>;
55
+ readonly reasoning_details: Schema.optional<Schema.$Array<Schema.Unknown>>;
55
56
  }>, Schema.Struct<{
56
57
  readonly role: Schema.Literal<"tool">;
57
58
  readonly tool_call_id: Schema.String;
@@ -116,6 +117,7 @@ export declare const protocol: Protocol<{
116
117
  }, "Type">[] | undefined;
117
118
  readonly reasoning_content?: string | undefined;
118
119
  readonly reasoning_text?: string | undefined;
120
+ readonly reasoning_details?: readonly unknown[] | undefined;
119
121
  } | Schema.Struct.ReadonlySide<{
120
122
  readonly role: Schema.Literal<"tool">;
121
123
  readonly tool_call_id: Schema.String;
@@ -164,6 +166,7 @@ export declare const protocol: Protocol<{
164
166
  }[] | null | undefined;
165
167
  readonly reasoning_content?: string | null | undefined;
166
168
  readonly reasoning_text?: string | null | undefined;
169
+ readonly reasoning_details?: readonly unknown[] | null | undefined;
167
170
  } | null | undefined;
168
171
  readonly finish_reason?: string | null | undefined;
169
172
  }[];
@@ -209,6 +212,7 @@ export declare const route: Route<{
209
212
  }, "Type">[] | undefined;
210
213
  readonly reasoning_content?: string | undefined;
211
214
  readonly reasoning_text?: string | undefined;
215
+ readonly reasoning_details?: readonly unknown[] | undefined;
212
216
  } | Schema.Struct.ReadonlySide<{
213
217
  readonly role: Schema.Literal<"tool">;
214
218
  readonly tool_call_id: Schema.String;
@@ -273,6 +277,7 @@ export declare const routes: Route<{
273
277
  }, "Type">[] | undefined;
274
278
  readonly reasoning_content?: string | undefined;
275
279
  readonly reasoning_text?: string | undefined;
280
+ readonly reasoning_details?: readonly unknown[] | undefined;
276
281
  } | Schema.Struct.ReadonlySide<{
277
282
  readonly role: Schema.Literal<"tool">;
278
283
  readonly tool_call_id: Schema.String;
@@ -18,10 +18,32 @@ export const protocol = Protocol.make({
18
18
  id: "openrouter-chat",
19
19
  body: {
20
20
  schema: OpenRouterBody,
21
- from: (request) => OpenAIChat.protocol.body.from(request).pipe(Effect.map((body) => ({
22
- ...body,
23
- ...bodyOptions(request.providerOptions?.openrouter),
24
- }))),
21
+ from: (request) => OpenAIChat.protocol.body.from(request).pipe(Effect.map((body) => {
22
+ const sourceAssistants = request.messages.filter((message) => message.role === "assistant");
23
+ let assistantIndex = 0;
24
+ const messages = body.messages.map((message) => {
25
+ if (message.role !== "assistant")
26
+ return message;
27
+ const source = sourceAssistants[assistantIndex++];
28
+ const reasoning = source?.content
29
+ .filter((part) => part.type === "reasoning")
30
+ .map((part) => part.text)
31
+ .join("");
32
+ const reasoningDetails = Array.isArray(message.reasoning_details) ? message.reasoning_details : undefined;
33
+ return {
34
+ ...message,
35
+ reasoning_content: undefined,
36
+ reasoning_text: undefined,
37
+ reasoning: reasoning && reasoningDetails && reasoningDetails.length > 0 ? reasoning : undefined,
38
+ reasoning_details: reasoningDetails,
39
+ };
40
+ });
41
+ return {
42
+ ...body,
43
+ messages,
44
+ ...bodyOptions(request.providerOptions?.openrouter),
45
+ };
46
+ })),
25
47
  },
26
48
  stream: OpenAIChat.protocol.stream,
27
49
  });
@@ -34,6 +34,7 @@ export declare const routes: (import("../route").Route<{
34
34
  }, "Type">[] | undefined;
35
35
  readonly reasoning_content?: string | undefined;
36
36
  readonly reasoning_text?: string | undefined;
37
+ readonly reasoning_details?: readonly unknown[] | undefined;
37
38
  } | import("effect/Schema").Struct.ReadonlySide<{
38
39
  readonly role: import("effect/Schema").Literal<"tool">;
39
40
  readonly tool_call_id: import("effect/Schema").String;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "0.0.0-next-15845",
3
+ "version": "0.0.0-next-15848",
4
4
  "name": "@opencode-ai/ai",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,7 +26,7 @@
26
26
  "devDependencies": {
27
27
  "@clack/prompts": "1.0.0-alpha.1",
28
28
  "@effect/platform-node": "4.0.0-beta.98",
29
- "@opencode-ai/http-recorder": "0.0.0-next-15845",
29
+ "@opencode-ai/http-recorder": "0.0.0-next-15848",
30
30
  "@tsconfig/bun": "1.0.9",
31
31
  "@types/bun": "1.3.13",
32
32
  "@typescript/native-preview": "7.0.0-dev.20251207.1",
@@ -35,7 +35,7 @@
35
35
  "dependencies": {
36
36
  "@smithy/eventstream-codec": "4.2.14",
37
37
  "@smithy/util-utf8": "4.2.2",
38
- "@opencode-ai/schema": "0.0.0-next-15845",
38
+ "@opencode-ai/schema": "0.0.0-next-15848",
39
39
  "aws4fetch": "1.0.20",
40
40
  "effect": "4.0.0-beta.98",
41
41
  "google-auth-library": "10.5.0"