extrait 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -790,36 +790,36 @@ function unwrap(schema) {
790
790
  let optional = false;
791
791
  let nullable = false;
792
792
  while (true) {
793
- const typeName = current?._def?.type;
793
+ const typeName = current?.def?.type;
794
794
  if (!typeName) {
795
795
  break;
796
796
  }
797
797
  if (typeName === "optional") {
798
798
  optional = true;
799
- current = current._def?.innerType ?? current;
799
+ current = current.def?.innerType ?? current;
800
800
  continue;
801
801
  }
802
802
  if (typeName === "default") {
803
803
  optional = true;
804
- current = current._def?.innerType ?? current;
804
+ current = current.def?.innerType ?? current;
805
805
  continue;
806
806
  }
807
807
  if (typeName === "nullable") {
808
808
  nullable = true;
809
- current = current._def?.innerType ?? current;
809
+ current = current.def?.innerType ?? current;
810
810
  continue;
811
811
  }
812
812
  if (typeName === "pipe") {
813
- const outType = current._def?.out?._def?.type;
813
+ const outType = current.def?.out?.def?.type;
814
814
  if (outType === "transform") {
815
- current = current._def?.in ?? current;
815
+ current = current.def?.in ?? current;
816
816
  } else {
817
- current = current._def?.out ?? current;
817
+ current = current.def?.out ?? current;
818
818
  }
819
819
  continue;
820
820
  }
821
821
  if (typeName === "catch" || typeName === "readonly") {
822
- current = current._def?.innerType ?? current;
822
+ current = current.def?.innerType ?? current;
823
823
  continue;
824
824
  }
825
825
  break;
@@ -835,7 +835,7 @@ function formatCore(schema, depth, seen) {
835
835
  return "unknown";
836
836
  }
837
837
  seen.add(schema);
838
- const typeName = schema?._def?.type;
838
+ const typeName = schema?.def?.type;
839
839
  switch (typeName) {
840
840
  case "string":
841
841
  return "string";
@@ -860,44 +860,44 @@ function formatCore(schema, depth, seen) {
860
860
  case "void":
861
861
  return "void";
862
862
  case "literal": {
863
- const value = schema._def?.values?.[0];
863
+ const value = schema.def?.values?.[0];
864
864
  return JSON.stringify(value);
865
865
  }
866
866
  case "enum": {
867
- const entries = schema._def?.entries;
867
+ const entries = schema.def?.entries;
868
868
  const values = Object.values(entries ?? {});
869
869
  const unique = [...new Set(values.filter((v) => typeof v !== "string" || Number.isNaN(Number(v))))];
870
870
  return unique.map((v) => JSON.stringify(v)).join(" | ") || "string";
871
871
  }
872
872
  case "array": {
873
- const inner = formatType(schema._def?.element ?? schema, depth, seen);
873
+ const inner = formatType(schema.def?.element ?? schema, depth, seen);
874
874
  return requiresParentheses(inner) ? `(${inner})[]` : `${inner}[]`;
875
875
  }
876
876
  case "tuple": {
877
- const items = (schema._def?.items ?? []).map((item) => formatType(item, depth, seen));
877
+ const items = (schema.def?.items ?? []).map((item) => formatType(item, depth, seen));
878
878
  return `[${items.join(", ")}]`;
879
879
  }
880
880
  case "union": {
881
- const options = (schema._def?.options ?? []).map((option) => formatType(option, depth, seen));
881
+ const options = (schema.def?.options ?? []).map((option) => formatType(option, depth, seen));
882
882
  return options.join(" | ") || "unknown";
883
883
  }
884
884
  case "intersection": {
885
- const left = formatType(schema._def?.left ?? schema, depth, seen);
886
- const right = formatType(schema._def?.right ?? schema, depth, seen);
885
+ const left = formatType(schema.def?.left ?? schema, depth, seen);
886
+ const right = formatType(schema.def?.right ?? schema, depth, seen);
887
887
  return `${left} & ${right}`;
888
888
  }
889
889
  case "record": {
890
- const keyType = formatType(schema._def?.keyType ?? schema, depth, seen);
891
- const valueType = formatType(schema._def?.valueType ?? schema, depth, seen);
890
+ const keyType = formatType(schema.def?.keyType ?? schema, depth, seen);
891
+ const valueType = formatType(schema.def?.valueType ?? schema, depth, seen);
892
892
  return `Record<${keyType}, ${valueType}>`;
893
893
  }
894
894
  case "map": {
895
- const keyType = formatType(schema._def?.keyType ?? schema, depth, seen);
896
- const valueType = formatType(schema._def?.valueType ?? schema, depth, seen);
895
+ const keyType = formatType(schema.def?.keyType ?? schema, depth, seen);
896
+ const valueType = formatType(schema.def?.valueType ?? schema, depth, seen);
897
897
  return `Map<${keyType}, ${valueType}>`;
898
898
  }
899
899
  case "set": {
900
- const valueType = formatType(schema._def?.valueType ?? schema, depth, seen);
900
+ const valueType = formatType(schema.def?.valueType ?? schema, depth, seen);
901
901
  return `Set<${valueType}>`;
902
902
  }
903
903
  case "object":
@@ -911,7 +911,7 @@ function formatCore(schema, depth, seen) {
911
911
  function formatObject(schema, depth, seen) {
912
912
  const indent = " ".repeat(depth);
913
913
  const innerIndent = " ".repeat(depth + 1);
914
- const rawShape = schema._def?.shape;
914
+ const rawShape = schema.def?.shape;
915
915
  const shape = typeof rawShape === "function" ? rawShape() : rawShape ?? {};
916
916
  const entries = Object.entries(shape);
917
917
  if (entries.length === 0) {
@@ -937,27 +937,27 @@ function requiresParentheses(typeText) {
937
937
  return typeText.includes(" | ") || typeText.includes(" & ");
938
938
  }
939
939
  function isIntegerNumber(schema) {
940
- const checks = schema._def?.checks ?? [];
940
+ const checks = schema.def?.checks ?? [];
941
941
  return checks.some((check) => check.isInt === true);
942
942
  }
943
943
  function readSchemaDescription(schema) {
944
944
  let current = schema;
945
- while (current?._def?.type) {
945
+ while (current?.def?.type) {
946
946
  const desc = current.description;
947
947
  if (typeof desc === "string" && desc.trim().length > 0) {
948
948
  return sanitizeDescription(desc);
949
949
  }
950
- const typeName = current._def.type;
950
+ const typeName = current.def.type;
951
951
  if (typeName === "optional" || typeName === "default" || typeName === "nullable") {
952
- current = current._def.innerType ?? current;
952
+ current = current.def.innerType ?? current;
953
953
  continue;
954
954
  }
955
955
  if (typeName === "pipe") {
956
- current = current._def.in ?? current;
956
+ current = current.def.in ?? current;
957
957
  continue;
958
958
  }
959
959
  if (typeName === "catch" || typeName === "readonly") {
960
- current = current._def.innerType ?? current;
960
+ current = current.def.innerType ?? current;
961
961
  continue;
962
962
  }
963
963
  break;
@@ -1220,7 +1220,7 @@ function findSSEBoundary(buffer) {
1220
1220
  }
1221
1221
 
1222
1222
  // src/providers/mcp-runtime.ts
1223
- var DEFAULT_MAX_TOOL_ROUNDS = 8;
1223
+ var DEFAULT_MAX_TOOL_ROUNDS = 100;
1224
1224
  async function resolveMCPToolset(clients) {
1225
1225
  if (!Array.isArray(clients) || clients.length === 0) {
1226
1226
  return {
@@ -1553,7 +1553,15 @@ function normalizeBaseURL(baseURL) {
1553
1553
  return baseURL.endsWith("/") ? baseURL : `${baseURL}/`;
1554
1554
  }
1555
1555
  function buildURL(baseURL, path) {
1556
- return new URL(path, normalizeBaseURL(baseURL)).toString();
1556
+ try {
1557
+ return new URL(path).toString();
1558
+ } catch {}
1559
+ const base = new URL(normalizeBaseURL(baseURL));
1560
+ const resolvedPath = new URL(path, "http://provider-path.local");
1561
+ base.pathname = mergePathnames(base.pathname, resolvedPath.pathname);
1562
+ base.search = resolvedPath.search;
1563
+ base.hash = resolvedPath.hash;
1564
+ return base.toString();
1557
1565
  }
1558
1566
  function safeJSONParse(input) {
1559
1567
  try {
@@ -1628,6 +1636,36 @@ function addOptional(a, b) {
1628
1636
  }
1629
1637
  return (a ?? 0) + (b ?? 0);
1630
1638
  }
1639
+ function mergePathnames(basePathname, pathPathname) {
1640
+ const baseSegments = splitPathSegments(basePathname);
1641
+ const pathSegments = splitPathSegments(pathPathname);
1642
+ const overlap = findPathOverlap(baseSegments, pathSegments);
1643
+ const mergedSegments = [...baseSegments, ...pathSegments.slice(overlap)];
1644
+ if (mergedSegments.length === 0) {
1645
+ return "/";
1646
+ }
1647
+ const mergedPathname = `/${mergedSegments.join("/")}`;
1648
+ return pathPathname.endsWith("/") && pathPathname !== "/" ? `${mergedPathname}/` : mergedPathname;
1649
+ }
1650
+ function splitPathSegments(pathname) {
1651
+ return pathname.split("/").filter((segment) => segment.length > 0);
1652
+ }
1653
+ function findPathOverlap(baseSegments, pathSegments) {
1654
+ const maxOverlap = Math.min(baseSegments.length, pathSegments.length);
1655
+ for (let size = maxOverlap;size > 0; size -= 1) {
1656
+ let matches = true;
1657
+ for (let index = 0;index < size; index += 1) {
1658
+ if (baseSegments[baseSegments.length - size + index] !== pathSegments[index]) {
1659
+ matches = false;
1660
+ break;
1661
+ }
1662
+ }
1663
+ if (matches) {
1664
+ return size;
1665
+ }
1666
+ }
1667
+ return 0;
1668
+ }
1631
1669
 
1632
1670
  // src/providers/openai-compatible.ts
1633
1671
  function createOpenAICompatibleAdapter(options) {
@@ -1786,7 +1824,7 @@ async function completeWithChatCompletionsPassThrough(options, fetcher, path, re
1786
1824
  const message = await response.text();
1787
1825
  throw new Error(`HTTP ${response.status}: ${message}`);
1788
1826
  }
1789
- const payload = await response.json();
1827
+ const payload = await parseOpenAICompatibleJSONResponse(response, "Failed to parse OpenAI-compatible chat completion response");
1790
1828
  const assistantMessage = pickAssistantMessage(payload);
1791
1829
  if (!assistantMessage) {
1792
1830
  throw new Error("No assistant message in OpenAI-compatible response.");
@@ -1802,6 +1840,25 @@ async function completeWithChatCompletionsPassThrough(options, fetcher, path, re
1802
1840
  toolCalls: toolCalls.length > 0 ? toolCalls : undefined
1803
1841
  };
1804
1842
  }
1843
+ async function parseOpenAICompatibleJSONResponse(response, context) {
1844
+ const rawBody = await response.text();
1845
+ try {
1846
+ return JSON.parse(rawBody);
1847
+ } catch (error) {
1848
+ const message = error instanceof Error ? error.message : String(error);
1849
+ throw new Error(`${context} (HTTP ${response.status}): ${message}. Raw body: ${formatResponseBodyForError(rawBody)}`);
1850
+ }
1851
+ }
1852
+ function formatResponseBodyForError(rawBody, maxLength = 2000) {
1853
+ const normalized = rawBody.trim();
1854
+ if (normalized.length === 0) {
1855
+ return "[empty body]";
1856
+ }
1857
+ if (normalized.length <= maxLength) {
1858
+ return normalized;
1859
+ }
1860
+ return `${normalized.slice(0, maxLength)}...[truncated ${normalized.length - maxLength} chars]`;
1861
+ }
1805
1862
  async function completeWithChatCompletionsWithMCP(options, fetcher, path, request) {
1806
1863
  const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
1807
1864
  let messages = buildMessages(request);
@@ -1810,6 +1867,7 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
1810
1867
  let lastPayload;
1811
1868
  const toolCalls = [];
1812
1869
  const toolExecutions = [];
1870
+ const reasoningBlocks = [];
1813
1871
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
1814
1872
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
1815
1873
  const transportTools = toProviderFunctionTools(mcpToolset);
@@ -1840,14 +1898,17 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
1840
1898
  finishReason = pickFinishReason(payload);
1841
1899
  const assistantMessage = pickAssistantMessage(payload);
1842
1900
  const calledTools = pickChatToolCalls(payload);
1901
+ const roundReasoning = pickAssistantReasoning(payload);
1902
+ pushReasoningBlock(reasoningBlocks, round, roundReasoning);
1843
1903
  if (!assistantMessage) {
1844
1904
  throw new Error("No assistant message in OpenAI-compatible response.");
1845
1905
  }
1846
1906
  if (calledTools.length === 0) {
1847
- const reasoning = pickAssistantReasoning(payload);
1907
+ const reasoning = joinReasoningBlocks(reasoningBlocks) || undefined;
1848
1908
  return {
1849
1909
  text: pickAssistantText(payload),
1850
- reasoning: reasoning.length > 0 ? reasoning : undefined,
1910
+ reasoning,
1911
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
1851
1912
  raw: payload,
1852
1913
  usage: aggregatedUsage,
1853
1914
  finishReason,
@@ -1875,10 +1936,8 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
1875
1936
  }
1876
1937
  return {
1877
1938
  text: pickAssistantText(lastPayload ?? {}),
1878
- reasoning: (() => {
1879
- const value = pickAssistantReasoning(lastPayload ?? {});
1880
- return value.length > 0 ? value : undefined;
1881
- })(),
1939
+ reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
1940
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
1882
1941
  raw: lastPayload,
1883
1942
  usage: aggregatedUsage,
1884
1943
  finishReason,
@@ -1926,6 +1985,7 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
1926
1985
  let lastPayload;
1927
1986
  const executedToolCalls = [];
1928
1987
  const toolExecutions = [];
1988
+ const reasoningBlocks = [];
1929
1989
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
1930
1990
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
1931
1991
  const transportTools = toResponsesTools(toProviderFunctionTools(mcpToolset));
@@ -1955,12 +2015,15 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
1955
2015
  lastPayload = payload;
1956
2016
  aggregatedUsage = mergeUsage(aggregatedUsage, pickUsage(payload));
1957
2017
  finishReason = pickResponsesFinishReason(payload) ?? finishReason;
2018
+ pushReasoningBlock(reasoningBlocks, round, pickResponsesReasoning(payload));
1958
2019
  const providerToolCalls = pickResponsesToolCalls(payload);
1959
2020
  const functionCalls = providerToolCalls.filter((toolCall) => toolCall.type === "function" && typeof toolCall.id === "string" && typeof toolCall.name === "string");
1960
2021
  if (functionCalls.length === 0) {
1961
2022
  const text = pickResponsesText(payload) || pickAssistantText(payload);
1962
2023
  return {
1963
2024
  text,
2025
+ reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
2026
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
1964
2027
  raw: payload,
1965
2028
  usage: aggregatedUsage,
1966
2029
  finishReason,
@@ -1988,6 +2051,8 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
1988
2051
  }
1989
2052
  return {
1990
2053
  text: pickResponsesText(lastPayload ?? {}) || pickAssistantText(lastPayload ?? {}),
2054
+ reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
2055
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
1991
2056
  raw: lastPayload,
1992
2057
  usage: aggregatedUsage,
1993
2058
  finishReason,
@@ -2003,9 +2068,9 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2003
2068
  let lastPayload;
2004
2069
  const executedToolCalls = [];
2005
2070
  const toolExecutions = [];
2071
+ const reasoningBlocks = [];
2006
2072
  callbacks.onStart?.();
2007
2073
  let lastRoundText = "";
2008
- let lastRoundReasoning = "";
2009
2074
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
2010
2075
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
2011
2076
  const transportTools = toProviderFunctionTools(mcpToolset);
@@ -2067,6 +2132,7 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2067
2132
  const chunk = {
2068
2133
  textDelta: delta,
2069
2134
  reasoningDelta: reasoningDelta || undefined,
2135
+ turnIndex: round,
2070
2136
  raw: json,
2071
2137
  usage: chunkUsage,
2072
2138
  finishReason: chunkFinishReason
@@ -2079,22 +2145,41 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2079
2145
  finishReason = roundFinishReason;
2080
2146
  }
2081
2147
  const calledTools = buildOpenAIStreamToolCalls(streamedToolCalls);
2148
+ pushReasoningBlock(reasoningBlocks, round, roundReasoning);
2149
+ request.onTurnTransition?.({
2150
+ turnIndex: round,
2151
+ kind: "reasoningComplete",
2152
+ reasoningText: roundReasoning
2153
+ });
2082
2154
  if (calledTools.length === 0) {
2083
2155
  const out2 = {
2084
2156
  text: roundText,
2085
- reasoning: roundReasoning.length > 0 ? roundReasoning : undefined,
2157
+ reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
2158
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
2086
2159
  raw: lastPayload,
2087
2160
  usage: aggregatedUsage,
2088
2161
  finishReason,
2089
2162
  toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
2090
2163
  toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
2091
2164
  };
2165
+ request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
2092
2166
  callbacks.onComplete?.(out2);
2093
2167
  return out2;
2094
2168
  }
2095
2169
  if (round > maxToolRounds) {
2096
2170
  throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
2097
2171
  }
2172
+ request.onTurnTransition?.({
2173
+ turnIndex: round,
2174
+ kind: "toolCallsEmit",
2175
+ toolCalls: calledTools
2176
+ });
2177
+ callbacks.onChunk?.({
2178
+ textDelta: "",
2179
+ turnIndex: round,
2180
+ toolCalls: calledTools,
2181
+ finishReason: roundFinishReason
2182
+ });
2098
2183
  const outputs = await executeMCPToolCalls(calledTools, mcpToolset, {
2099
2184
  round,
2100
2185
  request,
@@ -2103,8 +2188,8 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2103
2188
  });
2104
2189
  executedToolCalls.push(...outputs.map((entry) => entry.call));
2105
2190
  toolExecutions.push(...outputs.map((entry) => entry.execution));
2191
+ request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
2106
2192
  lastRoundText = roundText;
2107
- lastRoundReasoning = roundReasoning;
2108
2193
  const assistantMessage = buildOpenAIAssistantToolMessage(roundText, calledTools, {
2109
2194
  reasoning: roundReasoning,
2110
2195
  reasoningFieldName
@@ -2118,13 +2203,15 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2118
2203
  }
2119
2204
  const out = {
2120
2205
  text: lastRoundText,
2121
- reasoning: lastRoundReasoning.length > 0 ? lastRoundReasoning : undefined,
2206
+ reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
2207
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
2122
2208
  raw: lastPayload,
2123
2209
  usage: aggregatedUsage,
2124
2210
  finishReason,
2125
2211
  toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
2126
2212
  toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
2127
2213
  };
2214
+ request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
2128
2215
  callbacks.onComplete?.(out);
2129
2216
  return out;
2130
2217
  }
@@ -2207,6 +2294,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2207
2294
  let lastPayload;
2208
2295
  const executedToolCalls = [];
2209
2296
  const toolExecutions = [];
2297
+ const reasoningBlocks = [];
2210
2298
  callbacks.onStart?.();
2211
2299
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
2212
2300
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
@@ -2235,6 +2323,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2235
2323
  throw new Error(`HTTP ${response.status}: ${message}`);
2236
2324
  }
2237
2325
  let roundText = "";
2326
+ let roundReasoning = "";
2238
2327
  let roundUsage;
2239
2328
  let roundFinishReason;
2240
2329
  let roundPayload;
@@ -2253,6 +2342,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2253
2342
  lastPayload = payload;
2254
2343
  }
2255
2344
  const delta = pickResponsesStreamTextDelta(json);
2345
+ const reasoningDelta = pickResponsesStreamReasoningDelta(json);
2256
2346
  const chunkUsage = pickResponsesStreamUsage(json);
2257
2347
  const chunkFinishReason = pickResponsesStreamFinishReason(json);
2258
2348
  collectResponsesStreamToolCalls(json, streamedToolCalls);
@@ -2264,9 +2354,14 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2264
2354
  roundText += delta;
2265
2355
  callbacks.onToken?.(delta);
2266
2356
  }
2267
- if (delta || chunkUsage || chunkFinishReason) {
2357
+ if (reasoningDelta) {
2358
+ roundReasoning += reasoningDelta;
2359
+ }
2360
+ if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
2268
2361
  const chunk = {
2269
2362
  textDelta: delta,
2363
+ reasoningDelta: reasoningDelta || undefined,
2364
+ turnIndex: round,
2270
2365
  raw: json,
2271
2366
  usage: chunkUsage,
2272
2367
  finishReason: chunkFinishReason
@@ -2282,25 +2377,48 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2282
2377
  finishReason = pickResponsesFinishReason(roundPayload) ?? finishReason;
2283
2378
  }
2284
2379
  const payloadToolCalls = roundPayload ? pickResponsesToolCalls(roundPayload) : [];
2380
+ if (roundPayload && roundReasoning.length === 0) {
2381
+ roundReasoning = pickResponsesReasoning(roundPayload);
2382
+ }
2285
2383
  const streamedCalls = buildResponsesStreamToolCalls(streamedToolCalls);
2286
2384
  const providerToolCalls = payloadToolCalls.length > 0 ? payloadToolCalls : streamedCalls;
2287
2385
  const functionCalls = providerToolCalls.filter((toolCall) => toolCall.type === "function" && typeof toolCall.id === "string" && typeof toolCall.name === "string");
2386
+ pushReasoningBlock(reasoningBlocks, round, roundReasoning);
2387
+ request.onTurnTransition?.({
2388
+ turnIndex: round,
2389
+ kind: "reasoningComplete",
2390
+ reasoningText: roundReasoning
2391
+ });
2288
2392
  if (functionCalls.length === 0) {
2289
2393
  const finalText = roundText.length > 0 ? roundText : roundPayload ? pickResponsesText(roundPayload) || pickAssistantText(roundPayload) : "";
2290
2394
  const out2 = {
2291
2395
  text: finalText,
2396
+ reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
2397
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
2292
2398
  raw: roundPayload ?? lastPayload,
2293
2399
  usage: aggregatedUsage,
2294
2400
  finishReason,
2295
2401
  toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
2296
2402
  toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
2297
2403
  };
2404
+ request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
2298
2405
  callbacks.onComplete?.(out2);
2299
2406
  return out2;
2300
2407
  }
2301
2408
  if (round > maxToolRounds) {
2302
2409
  throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
2303
2410
  }
2411
+ request.onTurnTransition?.({
2412
+ turnIndex: round,
2413
+ kind: "toolCallsEmit",
2414
+ toolCalls: functionCalls
2415
+ });
2416
+ callbacks.onChunk?.({
2417
+ textDelta: "",
2418
+ turnIndex: round,
2419
+ toolCalls: functionCalls,
2420
+ finishReason: roundFinishReason
2421
+ });
2304
2422
  const outputs = await executeMCPToolCalls(functionCalls, mcpToolset, {
2305
2423
  round,
2306
2424
  request,
@@ -2309,6 +2427,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2309
2427
  });
2310
2428
  executedToolCalls.push(...outputs.map((entry) => entry.call));
2311
2429
  toolExecutions.push(...outputs.map((entry) => entry.execution));
2430
+ request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
2312
2431
  input = outputs.map((entry) => ({
2313
2432
  type: "function_call_output",
2314
2433
  call_id: entry.call.id,
@@ -2318,12 +2437,15 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2318
2437
  }
2319
2438
  const out = {
2320
2439
  text: pickResponsesText(lastPayload ?? {}) || pickAssistantText(lastPayload ?? {}),
2440
+ reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
2441
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
2321
2442
  raw: lastPayload,
2322
2443
  usage: aggregatedUsage,
2323
2444
  finishReason,
2324
2445
  toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
2325
2446
  toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
2326
2447
  };
2448
+ request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
2327
2449
  callbacks.onComplete?.(out);
2328
2450
  return out;
2329
2451
  }
@@ -2608,6 +2730,20 @@ function pickResponsesStreamTextDelta(payload) {
2608
2730
  }
2609
2731
  return "";
2610
2732
  }
2733
+ function pickResponsesStreamReasoningDelta(payload) {
2734
+ const eventType = pickString(payload.type) ?? "";
2735
+ if (!eventType.includes("reasoning") && !eventType.includes("thinking")) {
2736
+ return "";
2737
+ }
2738
+ const direct = pickString(payload.delta);
2739
+ if (direct) {
2740
+ return direct;
2741
+ }
2742
+ if (isRecord2(payload.delta)) {
2743
+ return pickReasoningText(payload.delta) || pickString(payload.delta.text) || pickString(payload.delta.summary_text) || "";
2744
+ }
2745
+ return "";
2746
+ }
2611
2747
  function pickResponsesStreamUsage(payload) {
2612
2748
  const direct = pickUsage(payload);
2613
2749
  if (direct) {
@@ -2742,6 +2878,30 @@ function pickResponsesText(payload) {
2742
2878
  }).join("");
2743
2879
  }).join("");
2744
2880
  }
2881
+ function pickResponsesReasoning(payload) {
2882
+ const direct = pickReasoningText(payload);
2883
+ if (direct) {
2884
+ return direct;
2885
+ }
2886
+ const output = payload.output;
2887
+ if (!Array.isArray(output)) {
2888
+ return "";
2889
+ }
2890
+ return output.map((item) => {
2891
+ if (!isRecord2(item)) {
2892
+ return "";
2893
+ }
2894
+ const itemReasoning = pickReasoningText(item);
2895
+ if (itemReasoning) {
2896
+ return itemReasoning;
2897
+ }
2898
+ const itemType = pickString(item.type) ?? "";
2899
+ if ((itemType.includes("reasoning") || itemType.includes("thinking")) && Array.isArray(item.content)) {
2900
+ return item.content.map((part) => isRecord2(part) ? pickTextLike(part) : "").join("");
2901
+ }
2902
+ return "";
2903
+ }).join("");
2904
+ }
2745
2905
  function pickAssistantText(payload) {
2746
2906
  const message = pickAssistantMessage(payload);
2747
2907
  if (message) {
@@ -2762,6 +2922,18 @@ function pickAssistantText(payload) {
2762
2922
  function pickReasoningText(value) {
2763
2923
  return pickTextLike(value.reasoning) || pickTextLike(value.reasoning_content);
2764
2924
  }
2925
+ function pushReasoningBlock(blocks, turnIndex, text) {
2926
+ const clean = text?.replace(/<\/?think\s*>/gi, "").trim();
2927
+ if (!clean) {
2928
+ return;
2929
+ }
2930
+ blocks.push({ turnIndex, text: clean });
2931
+ }
2932
+ function joinReasoningBlocks(blocks) {
2933
+ return blocks.map((block) => block.text).filter(Boolean).join(`
2934
+
2935
+ `);
2936
+ }
2765
2937
  function pickTextFromOpenAIContent(value) {
2766
2938
  return pickTextLike(value);
2767
2939
  }
@@ -2942,6 +3114,7 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
2942
3114
  let lastPayload;
2943
3115
  const toolCalls = [];
2944
3116
  const toolExecutions = [];
3117
+ const reasoningBlocks = [];
2945
3118
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
2946
3119
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
2947
3120
  const tools = toAnthropicTools(toProviderFunctionTools(mcpToolset));
@@ -2971,9 +3144,12 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
2971
3144
  finishReason = pickFinishReason2(payload);
2972
3145
  const content = Array.isArray(payload.content) ? payload.content : [];
2973
3146
  const calledTools = pickAnthropicToolCalls(payload).filter((call) => call.type === "function");
3147
+ pushReasoningBlock2(reasoningBlocks, round, extractAnthropicReasoning(payload));
2974
3148
  if (calledTools.length === 0) {
2975
3149
  return {
2976
3150
  text: extractAnthropicText(payload),
3151
+ reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
3152
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
2977
3153
  raw: payload,
2978
3154
  usage: aggregatedUsage,
2979
3155
  finishReason,
@@ -3009,6 +3185,8 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
3009
3185
  }
3010
3186
  return {
3011
3187
  text: extractAnthropicText(lastPayload ?? {}),
3188
+ reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
3189
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
3012
3190
  raw: lastPayload,
3013
3191
  usage: aggregatedUsage,
3014
3192
  finishReason,
@@ -3025,6 +3203,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
3025
3203
  let lastPayload;
3026
3204
  const toolCalls = [];
3027
3205
  const toolExecutions = [];
3206
+ const reasoningBlocks = [];
3028
3207
  callbacks.onStart?.();
3029
3208
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
3030
3209
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
@@ -3050,6 +3229,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
3050
3229
  throw new Error(`HTTP ${response.status}: ${message}`);
3051
3230
  }
3052
3231
  let roundText = "";
3232
+ let roundReasoning = "";
3053
3233
  let roundUsage;
3054
3234
  let roundFinishReason;
3055
3235
  const streamedToolCalls = new Map;
@@ -3063,6 +3243,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
3063
3243
  }
3064
3244
  lastPayload = json;
3065
3245
  const delta = pickAnthropicDelta(json);
3246
+ const reasoningDelta = pickAnthropicReasoningDelta(json);
3066
3247
  const chunkUsage = pickUsage2(json);
3067
3248
  const chunkFinishReason = pickFinishReason2(json);
3068
3249
  collectAnthropicStreamToolCalls(json, streamedToolCalls);
@@ -3074,9 +3255,14 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
3074
3255
  roundText += delta;
3075
3256
  callbacks.onToken?.(delta);
3076
3257
  }
3077
- if (delta || chunkUsage || chunkFinishReason) {
3258
+ if (reasoningDelta) {
3259
+ roundReasoning += reasoningDelta;
3260
+ }
3261
+ if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
3078
3262
  const chunk = {
3079
3263
  textDelta: delta,
3264
+ reasoningDelta: reasoningDelta || undefined,
3265
+ turnIndex: round,
3080
3266
  raw: json,
3081
3267
  usage: chunkUsage,
3082
3268
  finishReason: chunkFinishReason
@@ -3089,21 +3275,41 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
3089
3275
  finishReason = roundFinishReason;
3090
3276
  }
3091
3277
  const calledTools = buildAnthropicStreamToolCalls(streamedToolCalls);
3278
+ pushReasoningBlock2(reasoningBlocks, round, roundReasoning);
3279
+ request.onTurnTransition?.({
3280
+ turnIndex: round,
3281
+ kind: "reasoningComplete",
3282
+ reasoningText: roundReasoning
3283
+ });
3092
3284
  if (calledTools.length === 0) {
3093
3285
  const out2 = {
3094
3286
  text: roundText,
3287
+ reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
3288
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
3095
3289
  raw: lastPayload,
3096
3290
  usage: aggregatedUsage,
3097
3291
  finishReason,
3098
3292
  toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
3099
3293
  toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
3100
3294
  };
3295
+ request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
3101
3296
  callbacks.onComplete?.(out2);
3102
3297
  return out2;
3103
3298
  }
3104
3299
  if (round > maxToolRounds) {
3105
3300
  throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
3106
3301
  }
3302
+ request.onTurnTransition?.({
3303
+ turnIndex: round,
3304
+ kind: "toolCallsEmit",
3305
+ toolCalls: calledTools
3306
+ });
3307
+ callbacks.onChunk?.({
3308
+ textDelta: "",
3309
+ turnIndex: round,
3310
+ toolCalls: calledTools,
3311
+ finishReason: roundFinishReason
3312
+ });
3107
3313
  const toolResultContent = [];
3108
3314
  const outputs = await executeMCPToolCalls(calledTools, mcpToolset, {
3109
3315
  round,
@@ -3113,6 +3319,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
3113
3319
  });
3114
3320
  toolCalls.push(...outputs.map((entry) => entry.call));
3115
3321
  toolExecutions.push(...outputs.map((entry) => entry.execution));
3322
+ request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
3116
3323
  for (const entry of outputs) {
3117
3324
  toolResultContent.push({
3118
3325
  type: "tool_result",
@@ -3129,12 +3336,15 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
3129
3336
  }
3130
3337
  const out = {
3131
3338
  text: "",
3339
+ reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
3340
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
3132
3341
  raw: lastPayload,
3133
3342
  usage: aggregatedUsage,
3134
3343
  finishReason,
3135
3344
  toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
3136
3345
  toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
3137
3346
  };
3347
+ request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
3138
3348
  callbacks.onComplete?.(out);
3139
3349
  return out;
3140
3350
  }
@@ -3255,6 +3465,22 @@ function extractAnthropicText(payload) {
3255
3465
  return typeof text === "string" ? text : "";
3256
3466
  }).join("");
3257
3467
  }
3468
+ function extractAnthropicReasoning(payload) {
3469
+ const content = payload.content;
3470
+ if (!Array.isArray(content)) {
3471
+ return "";
3472
+ }
3473
+ return content.map((part) => {
3474
+ if (!isRecord2(part)) {
3475
+ return "";
3476
+ }
3477
+ const type = pickString(part.type) ?? "";
3478
+ if (type !== "thinking" && type !== "reasoning") {
3479
+ return "";
3480
+ }
3481
+ return pickString(part.thinking) ?? pickString(part.text) ?? pickString(part.reasoning) ?? "";
3482
+ }).join("");
3483
+ }
3258
3484
  function pickAnthropicToolCalls(payload) {
3259
3485
  const content = payload.content;
3260
3486
  if (!Array.isArray(content)) {
@@ -3285,6 +3511,35 @@ function pickAnthropicDelta(payload) {
3285
3511
  }
3286
3512
  return "";
3287
3513
  }
3514
+ function pickAnthropicReasoningDelta(payload) {
3515
+ const deltaObject = payload.delta;
3516
+ if (isRecord2(deltaObject)) {
3517
+ const type = pickString(deltaObject.type) ?? "";
3518
+ if (type === "thinking_delta" || type === "reasoning_delta") {
3519
+ return pickString(deltaObject.thinking) ?? pickString(deltaObject.text) ?? "";
3520
+ }
3521
+ }
3522
+ const contentBlock = payload.content_block;
3523
+ if (isRecord2(contentBlock)) {
3524
+ const type = pickString(contentBlock.type) ?? "";
3525
+ if (type === "thinking" || type === "reasoning") {
3526
+ return pickString(contentBlock.thinking) ?? pickString(contentBlock.text) ?? "";
3527
+ }
3528
+ }
3529
+ return "";
3530
+ }
3531
+ function pushReasoningBlock2(blocks, turnIndex, text) {
3532
+ const clean = text?.replace(/<\/?think\s*>/gi, "").trim();
3533
+ if (!clean) {
3534
+ return;
3535
+ }
3536
+ blocks.push({ turnIndex, text: clean });
3537
+ }
3538
+ function joinReasoningBlocks2(blocks) {
3539
+ return blocks.map((block) => block.text).filter(Boolean).join(`
3540
+
3541
+ `);
3542
+ }
3288
3543
  function collectAnthropicStreamToolCalls(payload, state) {
3289
3544
  const eventType = pickString(payload.type);
3290
3545
  if (!eventType) {
@@ -3758,6 +4013,7 @@ function normalizeStreamConfig(option) {
3758
4013
  return {
3759
4014
  enabled: option.enabled ?? true,
3760
4015
  onData: option.onData,
4016
+ onTurnTransition: option.onTurnTransition,
3761
4017
  to: option.to
3762
4018
  };
3763
4019
  }
@@ -3825,6 +4081,7 @@ async function callModel(adapter, options) {
3825
4081
  transformToolCallParams: options.request?.transformToolCallParams,
3826
4082
  unknownToolError: options.request?.unknownToolError,
3827
4083
  toolDebug: options.request?.toolDebug,
4084
+ onTurnTransition: options.stream.onTurnTransition,
3828
4085
  body: options.request?.body,
3829
4086
  signal: requestSignal
3830
4087
  };
@@ -3852,13 +4109,21 @@ async function callModel(adapter, options) {
3852
4109
  let latestFinishReason;
3853
4110
  let streamedProviderText = "";
3854
4111
  let streamedDedicatedReasoning = "";
4112
+ let currentTurnIndex;
4113
+ let currentToolCalls;
4114
+ let streamedReasoningBlocks;
3855
4115
  let lastSnapshotFingerprint;
3856
4116
  let previousSnapshotText = "";
3857
4117
  let previousSnapshotReasoning = "";
3858
4118
  const emitStreamingData = (done, usage2, finishReason2) => {
3859
- const normalized2 = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning);
4119
+ const normalized2 = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning, streamedReasoningBlocks);
3860
4120
  const snapshot = options.buildSnapshot(normalized2);
3861
- const fingerprint = toStreamDataFingerprint(snapshot);
4121
+ const fingerprint = toStreamDataFingerprint({
4122
+ snapshot,
4123
+ done,
4124
+ turnIndex: currentTurnIndex,
4125
+ toolCalls: currentToolCalls
4126
+ });
3862
4127
  if (!done && fingerprint === lastSnapshotFingerprint) {
3863
4128
  return;
3864
4129
  }
@@ -3874,7 +4139,9 @@ async function callModel(adapter, options) {
3874
4139
  snapshot,
3875
4140
  done,
3876
4141
  usage: usage2,
3877
- finishReason: finishReason2
4142
+ finishReason: finishReason2,
4143
+ turnIndex: currentTurnIndex,
4144
+ toolCalls: currentToolCalls
3878
4145
  });
3879
4146
  if (options.stream.to === "stdout" && delta.text) {
3880
4147
  process.stdout.write(delta.text);
@@ -3909,8 +4176,21 @@ async function callModel(adapter, options) {
3909
4176
  streamedDedicatedReasoning += delta;
3910
4177
  emitStreamingData(false);
3911
4178
  };
3912
- const response2 = await adapter.stream(requestPayload, {
4179
+ const streamRequestPayload = {
4180
+ ...requestPayload,
4181
+ onTurnTransition: (transition) => {
4182
+ if (transition.kind === "reasoningComplete") {
4183
+ streamedReasoningBlocks = appendReasoningBlock(streamedReasoningBlocks, transition);
4184
+ }
4185
+ options.stream.onTurnTransition?.(transition);
4186
+ }
4187
+ };
4188
+ const response2 = await adapter.stream(streamRequestPayload, {
3913
4189
  onChunk: (chunk) => {
4190
+ if (chunk.turnIndex !== undefined) {
4191
+ currentTurnIndex = chunk.turnIndex;
4192
+ }
4193
+ currentToolCalls = chunk.toolCalls;
3914
4194
  if (chunk.textDelta) {
3915
4195
  handleTextDelta(chunk.textDelta);
3916
4196
  }
@@ -3923,11 +4203,15 @@ async function callModel(adapter, options) {
3923
4203
  if (chunk.finishReason) {
3924
4204
  latestFinishReason = chunk.finishReason;
3925
4205
  }
4206
+ if (!chunk.textDelta && !chunk.reasoningDelta && (chunk.turnIndex !== undefined || chunk.toolCalls)) {
4207
+ emitStreamingData(false, chunk.usage, chunk.finishReason);
4208
+ }
3926
4209
  }
3927
4210
  });
3928
4211
  streamedProviderText = typeof response2.text === "string" ? response2.text : streamedProviderText;
3929
4212
  streamedDedicatedReasoning = typeof response2.reasoning === "string" ? response2.reasoning : streamedDedicatedReasoning;
3930
- const finalNormalized = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning);
4213
+ streamedReasoningBlocks = response2.reasoningBlocks ?? streamedReasoningBlocks;
4214
+ const finalNormalized = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning, streamedReasoningBlocks);
3931
4215
  const usage = preferLatestUsage(latestUsage, response2.usage);
3932
4216
  const finishReason = response2.finishReason ?? latestFinishReason;
3933
4217
  emitStreamingData(true, usage, finishReason);
@@ -3959,11 +4243,12 @@ async function callModel(adapter, options) {
3959
4243
  parseSource: finalNormalized.parseSource,
3960
4244
  via: "stream",
3961
4245
  usage,
3962
- finishReason
4246
+ finishReason,
4247
+ reasoningBlocks: finalNormalized.reasoningBlocks
3963
4248
  };
3964
4249
  }
3965
4250
  const response = await adapter.complete(requestPayload);
3966
- const normalized = normalizeModelOutput(response.text, response.reasoning);
4251
+ const normalized = normalizeModelOutput(response.text, response.reasoning, response.reasoningBlocks);
3967
4252
  options.observe?.(options.buildEvent({
3968
4253
  stage: "llm.response",
3969
4254
  message: "Completion response received.",
@@ -3992,10 +4277,11 @@ async function callModel(adapter, options) {
3992
4277
  parseSource: normalized.parseSource,
3993
4278
  via: "complete",
3994
4279
  usage: response.usage,
3995
- finishReason: response.finishReason
4280
+ finishReason: response.finishReason,
4281
+ reasoningBlocks: normalized.reasoningBlocks
3996
4282
  };
3997
4283
  }
3998
- function normalizeModelOutput(text, dedicatedReasoning) {
4284
+ function normalizeModelOutput(text, dedicatedReasoning, reasoningBlocks) {
3999
4285
  const sanitized = sanitizeThink(text);
4000
4286
  const visibleText = stripThinkBlocks(text, sanitized.thinkBlocks);
4001
4287
  const reasoning = joinReasoningSegments([
@@ -4005,10 +4291,29 @@ function normalizeModelOutput(text, dedicatedReasoning) {
4005
4291
  return {
4006
4292
  text: visibleText,
4007
4293
  reasoning,
4294
+ reasoningBlocks: normalizeReasoningBlocks(reasoningBlocks),
4008
4295
  thinkBlocks: sanitized.thinkBlocks,
4009
4296
  parseSource: composeParseSource(visibleText, reasoning)
4010
4297
  };
4011
4298
  }
4299
+ function normalizeReasoningBlocks(blocks) {
4300
+ if (!Array.isArray(blocks)) {
4301
+ return;
4302
+ }
4303
+ const normalized = blocks.map((block) => ({
4304
+ turnIndex: block.turnIndex,
4305
+ text: block.text.replace(RE_THINK_TAGS, "").trim()
4306
+ })).filter((block) => Number.isFinite(block.turnIndex) && block.text.length > 0);
4307
+ return normalized.length > 0 ? normalized : undefined;
4308
+ }
4309
+ function appendReasoningBlock(blocks, transition) {
4310
+ const text = transition.reasoningText?.replace(RE_THINK_TAGS, "").trim();
4311
+ if (!text) {
4312
+ return blocks;
4313
+ }
4314
+ const next = [...blocks ?? [], { turnIndex: transition.turnIndex, text }];
4315
+ return normalizeReasoningBlocks(next);
4316
+ }
4012
4317
  function composeParseSource(text, reasoning) {
4013
4318
  if (typeof reasoning !== "string" || reasoning.length === 0) {
4014
4319
  return text;
@@ -4168,7 +4473,8 @@ async function generate(adapter, promptOrOptions, callOptions) {
4168
4473
  }),
4169
4474
  buildSnapshot: (model) => ({
4170
4475
  text: model.text,
4171
- reasoning: model.reasoning
4476
+ reasoning: model.reasoning,
4477
+ ...model.reasoningBlocks ? { reasoningBlocks: model.reasoningBlocks } : {}
4172
4478
  }),
4173
4479
  debug: debugConfig,
4174
4480
  debugLabel: "generate",
@@ -4183,7 +4489,8 @@ async function generate(adapter, promptOrOptions, callOptions) {
4183
4489
  text: response.text,
4184
4490
  reasoning: response.reasoning,
4185
4491
  usage: response.usage,
4186
- finishReason: response.finishReason
4492
+ finishReason: response.finishReason,
4493
+ ...response.reasoningBlocks ? { reasoningBlocks: response.reasoningBlocks } : {}
4187
4494
  };
4188
4495
  const attempts = [attempt];
4189
4496
  normalized.observe?.({
@@ -4200,7 +4507,8 @@ async function generate(adapter, promptOrOptions, callOptions) {
4200
4507
  reasoning: attempt.reasoning,
4201
4508
  attempts,
4202
4509
  usage: aggregateUsage(attempts),
4203
- finishReason: attempt.finishReason
4510
+ finishReason: attempt.finishReason,
4511
+ ...attempt.reasoningBlocks ? { reasoningBlocks: attempt.reasoningBlocks } : {}
4204
4512
  };
4205
4513
  }
4206
4514
  function normalizeGenerateInput(promptOrOptions, callOptions) {
@@ -5020,6 +5328,7 @@ async function executeAttempt(adapter, input) {
5020
5328
  success: parsed.success,
5021
5329
  usage: response.usage,
5022
5330
  finishReason: response.finishReason,
5331
+ ...response.reasoningBlocks ? { reasoningBlocks: response.reasoningBlocks } : {},
5023
5332
  parsed
5024
5333
  };
5025
5334
  return {
@@ -5040,6 +5349,7 @@ async function callModel2(adapter, options) {
5040
5349
  buildSnapshot: (normalized) => ({
5041
5350
  text: normalized.text,
5042
5351
  reasoning: normalized.reasoning,
5352
+ ...normalized.reasoningBlocks ? { reasoningBlocks: normalized.reasoningBlocks } : {},
5043
5353
  data: parseStreamingStructuredData(normalized.parseSource) ?? null
5044
5354
  }),
5045
5355
  debugLabel: "structured"
@@ -5140,7 +5450,8 @@ function buildSuccessResult(data, attempts) {
5140
5450
  json: final?.json ?? null,
5141
5451
  attempts,
5142
5452
  usage: aggregateUsage(attempts),
5143
- finishReason: final?.finishReason
5453
+ finishReason: final?.finishReason,
5454
+ ...final?.reasoningBlocks ? { reasoningBlocks: final.reasoningBlocks } : {}
5144
5455
  };
5145
5456
  }
5146
5457
  function toStructuredError(attempt) {
@@ -5603,11 +5914,11 @@ function inferSchemaExample(schema) {
5603
5914
  }
5604
5915
  function getObjectShape(schema) {
5605
5916
  const unwrapped = unwrap2(schema).schema;
5606
- const typeName = unwrapped._def?.type;
5917
+ const typeName = unwrapped.def?.type;
5607
5918
  if (typeName !== "object") {
5608
5919
  return null;
5609
5920
  }
5610
- const rawShape = unwrapped._def?.shape;
5921
+ const rawShape = unwrapped.def?.shape;
5611
5922
  if (typeof rawShape === "function") {
5612
5923
  return rawShape();
5613
5924
  }
@@ -5615,11 +5926,11 @@ function getObjectShape(schema) {
5615
5926
  }
5616
5927
  function readDefaultValue(schema) {
5617
5928
  let current = schema;
5618
- while (current?._def?.type) {
5619
- const typeName = current._def.type;
5929
+ while (current?.def?.type) {
5930
+ const typeName = current.def.type;
5620
5931
  if (typeName === "default") {
5621
5932
  try {
5622
- const raw = current._def.defaultValue;
5933
+ const raw = current.def.defaultValue;
5623
5934
  if (typeof raw === "function") {
5624
5935
  return raw();
5625
5936
  }
@@ -5629,11 +5940,11 @@ function readDefaultValue(schema) {
5629
5940
  }
5630
5941
  }
5631
5942
  if (typeName === "optional" || typeName === "nullable" || typeName === "catch" || typeName === "readonly") {
5632
- current = current._def.innerType ?? current;
5943
+ current = current.def.innerType ?? current;
5633
5944
  continue;
5634
5945
  }
5635
5946
  if (typeName === "pipe") {
5636
- current = current._def.in ?? current;
5947
+ current = current.def.in ?? current;
5637
5948
  continue;
5638
5949
  }
5639
5950
  return;
@@ -5642,22 +5953,22 @@ function readDefaultValue(schema) {
5642
5953
  }
5643
5954
  function readSchemaDescription2(schema) {
5644
5955
  let current = schema;
5645
- while (current?._def?.type) {
5956
+ while (current?.def?.type) {
5646
5957
  const desc = current.description;
5647
5958
  if (typeof desc === "string" && desc.trim().length > 0) {
5648
5959
  return desc.trim();
5649
5960
  }
5650
- const typeName = current._def.type;
5961
+ const typeName = current.def.type;
5651
5962
  if (typeName === "optional" || typeName === "default" || typeName === "nullable") {
5652
- current = current._def.innerType ?? current;
5963
+ current = current.def.innerType ?? current;
5653
5964
  continue;
5654
5965
  }
5655
5966
  if (typeName === "catch" || typeName === "readonly") {
5656
- current = current._def.innerType ?? current;
5967
+ current = current.def.innerType ?? current;
5657
5968
  continue;
5658
5969
  }
5659
5970
  if (typeName === "pipe") {
5660
- current = current._def.in ?? current;
5971
+ current = current.def.in ?? current;
5661
5972
  continue;
5662
5973
  }
5663
5974
  break;
@@ -5667,19 +5978,19 @@ function readSchemaDescription2(schema) {
5667
5978
  function unwrap2(schema) {
5668
5979
  let current = schema;
5669
5980
  let optional = false;
5670
- while (current?._def?.type) {
5671
- const typeName = current._def.type;
5981
+ while (current?.def?.type) {
5982
+ const typeName = current.def.type;
5672
5983
  if (typeName === "optional" || typeName === "default") {
5673
5984
  optional = true;
5674
- current = current._def.innerType ?? current;
5985
+ current = current.def.innerType ?? current;
5675
5986
  continue;
5676
5987
  }
5677
5988
  if (typeName === "nullable" || typeName === "catch" || typeName === "readonly") {
5678
- current = current._def.innerType ?? current;
5989
+ current = current.def.innerType ?? current;
5679
5990
  continue;
5680
5991
  }
5681
5992
  if (typeName === "pipe") {
5682
- current = current._def.in ?? current;
5993
+ current = current.def.in ?? current;
5683
5994
  continue;
5684
5995
  }
5685
5996
  break;