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.js CHANGED
@@ -700,36 +700,36 @@ function unwrap(schema) {
700
700
  let optional = false;
701
701
  let nullable = false;
702
702
  while (true) {
703
- const typeName = current?._def?.type;
703
+ const typeName = current?.def?.type;
704
704
  if (!typeName) {
705
705
  break;
706
706
  }
707
707
  if (typeName === "optional") {
708
708
  optional = true;
709
- current = current._def?.innerType ?? current;
709
+ current = current.def?.innerType ?? current;
710
710
  continue;
711
711
  }
712
712
  if (typeName === "default") {
713
713
  optional = true;
714
- current = current._def?.innerType ?? current;
714
+ current = current.def?.innerType ?? current;
715
715
  continue;
716
716
  }
717
717
  if (typeName === "nullable") {
718
718
  nullable = true;
719
- current = current._def?.innerType ?? current;
719
+ current = current.def?.innerType ?? current;
720
720
  continue;
721
721
  }
722
722
  if (typeName === "pipe") {
723
- const outType = current._def?.out?._def?.type;
723
+ const outType = current.def?.out?.def?.type;
724
724
  if (outType === "transform") {
725
- current = current._def?.in ?? current;
725
+ current = current.def?.in ?? current;
726
726
  } else {
727
- current = current._def?.out ?? current;
727
+ current = current.def?.out ?? current;
728
728
  }
729
729
  continue;
730
730
  }
731
731
  if (typeName === "catch" || typeName === "readonly") {
732
- current = current._def?.innerType ?? current;
732
+ current = current.def?.innerType ?? current;
733
733
  continue;
734
734
  }
735
735
  break;
@@ -745,7 +745,7 @@ function formatCore(schema, depth, seen) {
745
745
  return "unknown";
746
746
  }
747
747
  seen.add(schema);
748
- const typeName = schema?._def?.type;
748
+ const typeName = schema?.def?.type;
749
749
  switch (typeName) {
750
750
  case "string":
751
751
  return "string";
@@ -770,44 +770,44 @@ function formatCore(schema, depth, seen) {
770
770
  case "void":
771
771
  return "void";
772
772
  case "literal": {
773
- const value = schema._def?.values?.[0];
773
+ const value = schema.def?.values?.[0];
774
774
  return JSON.stringify(value);
775
775
  }
776
776
  case "enum": {
777
- const entries = schema._def?.entries;
777
+ const entries = schema.def?.entries;
778
778
  const values = Object.values(entries ?? {});
779
779
  const unique = [...new Set(values.filter((v) => typeof v !== "string" || Number.isNaN(Number(v))))];
780
780
  return unique.map((v) => JSON.stringify(v)).join(" | ") || "string";
781
781
  }
782
782
  case "array": {
783
- const inner = formatType(schema._def?.element ?? schema, depth, seen);
783
+ const inner = formatType(schema.def?.element ?? schema, depth, seen);
784
784
  return requiresParentheses(inner) ? `(${inner})[]` : `${inner}[]`;
785
785
  }
786
786
  case "tuple": {
787
- const items = (schema._def?.items ?? []).map((item) => formatType(item, depth, seen));
787
+ const items = (schema.def?.items ?? []).map((item) => formatType(item, depth, seen));
788
788
  return `[${items.join(", ")}]`;
789
789
  }
790
790
  case "union": {
791
- const options = (schema._def?.options ?? []).map((option) => formatType(option, depth, seen));
791
+ const options = (schema.def?.options ?? []).map((option) => formatType(option, depth, seen));
792
792
  return options.join(" | ") || "unknown";
793
793
  }
794
794
  case "intersection": {
795
- const left = formatType(schema._def?.left ?? schema, depth, seen);
796
- const right = formatType(schema._def?.right ?? schema, depth, seen);
795
+ const left = formatType(schema.def?.left ?? schema, depth, seen);
796
+ const right = formatType(schema.def?.right ?? schema, depth, seen);
797
797
  return `${left} & ${right}`;
798
798
  }
799
799
  case "record": {
800
- const keyType = formatType(schema._def?.keyType ?? schema, depth, seen);
801
- const valueType = formatType(schema._def?.valueType ?? schema, depth, seen);
800
+ const keyType = formatType(schema.def?.keyType ?? schema, depth, seen);
801
+ const valueType = formatType(schema.def?.valueType ?? schema, depth, seen);
802
802
  return `Record<${keyType}, ${valueType}>`;
803
803
  }
804
804
  case "map": {
805
- const keyType = formatType(schema._def?.keyType ?? schema, depth, seen);
806
- const valueType = formatType(schema._def?.valueType ?? schema, depth, seen);
805
+ const keyType = formatType(schema.def?.keyType ?? schema, depth, seen);
806
+ const valueType = formatType(schema.def?.valueType ?? schema, depth, seen);
807
807
  return `Map<${keyType}, ${valueType}>`;
808
808
  }
809
809
  case "set": {
810
- const valueType = formatType(schema._def?.valueType ?? schema, depth, seen);
810
+ const valueType = formatType(schema.def?.valueType ?? schema, depth, seen);
811
811
  return `Set<${valueType}>`;
812
812
  }
813
813
  case "object":
@@ -821,7 +821,7 @@ function formatCore(schema, depth, seen) {
821
821
  function formatObject(schema, depth, seen) {
822
822
  const indent = " ".repeat(depth);
823
823
  const innerIndent = " ".repeat(depth + 1);
824
- const rawShape = schema._def?.shape;
824
+ const rawShape = schema.def?.shape;
825
825
  const shape = typeof rawShape === "function" ? rawShape() : rawShape ?? {};
826
826
  const entries = Object.entries(shape);
827
827
  if (entries.length === 0) {
@@ -847,27 +847,27 @@ function requiresParentheses(typeText) {
847
847
  return typeText.includes(" | ") || typeText.includes(" & ");
848
848
  }
849
849
  function isIntegerNumber(schema) {
850
- const checks = schema._def?.checks ?? [];
850
+ const checks = schema.def?.checks ?? [];
851
851
  return checks.some((check) => check.isInt === true);
852
852
  }
853
853
  function readSchemaDescription(schema) {
854
854
  let current = schema;
855
- while (current?._def?.type) {
855
+ while (current?.def?.type) {
856
856
  const desc = current.description;
857
857
  if (typeof desc === "string" && desc.trim().length > 0) {
858
858
  return sanitizeDescription(desc);
859
859
  }
860
- const typeName = current._def.type;
860
+ const typeName = current.def.type;
861
861
  if (typeName === "optional" || typeName === "default" || typeName === "nullable") {
862
- current = current._def.innerType ?? current;
862
+ current = current.def.innerType ?? current;
863
863
  continue;
864
864
  }
865
865
  if (typeName === "pipe") {
866
- current = current._def.in ?? current;
866
+ current = current.def.in ?? current;
867
867
  continue;
868
868
  }
869
869
  if (typeName === "catch" || typeName === "readonly") {
870
- current = current._def.innerType ?? current;
870
+ current = current.def.innerType ?? current;
871
871
  continue;
872
872
  }
873
873
  break;
@@ -1130,7 +1130,7 @@ function findSSEBoundary(buffer) {
1130
1130
  }
1131
1131
 
1132
1132
  // src/providers/mcp-runtime.ts
1133
- var DEFAULT_MAX_TOOL_ROUNDS = 8;
1133
+ var DEFAULT_MAX_TOOL_ROUNDS = 100;
1134
1134
  async function resolveMCPToolset(clients) {
1135
1135
  if (!Array.isArray(clients) || clients.length === 0) {
1136
1136
  return {
@@ -1463,7 +1463,15 @@ function normalizeBaseURL(baseURL) {
1463
1463
  return baseURL.endsWith("/") ? baseURL : `${baseURL}/`;
1464
1464
  }
1465
1465
  function buildURL(baseURL, path) {
1466
- return new URL(path, normalizeBaseURL(baseURL)).toString();
1466
+ try {
1467
+ return new URL(path).toString();
1468
+ } catch {}
1469
+ const base = new URL(normalizeBaseURL(baseURL));
1470
+ const resolvedPath = new URL(path, "http://provider-path.local");
1471
+ base.pathname = mergePathnames(base.pathname, resolvedPath.pathname);
1472
+ base.search = resolvedPath.search;
1473
+ base.hash = resolvedPath.hash;
1474
+ return base.toString();
1467
1475
  }
1468
1476
  function safeJSONParse(input) {
1469
1477
  try {
@@ -1538,6 +1546,36 @@ function addOptional(a, b) {
1538
1546
  }
1539
1547
  return (a ?? 0) + (b ?? 0);
1540
1548
  }
1549
+ function mergePathnames(basePathname, pathPathname) {
1550
+ const baseSegments = splitPathSegments(basePathname);
1551
+ const pathSegments = splitPathSegments(pathPathname);
1552
+ const overlap = findPathOverlap(baseSegments, pathSegments);
1553
+ const mergedSegments = [...baseSegments, ...pathSegments.slice(overlap)];
1554
+ if (mergedSegments.length === 0) {
1555
+ return "/";
1556
+ }
1557
+ const mergedPathname = `/${mergedSegments.join("/")}`;
1558
+ return pathPathname.endsWith("/") && pathPathname !== "/" ? `${mergedPathname}/` : mergedPathname;
1559
+ }
1560
+ function splitPathSegments(pathname) {
1561
+ return pathname.split("/").filter((segment) => segment.length > 0);
1562
+ }
1563
+ function findPathOverlap(baseSegments, pathSegments) {
1564
+ const maxOverlap = Math.min(baseSegments.length, pathSegments.length);
1565
+ for (let size = maxOverlap;size > 0; size -= 1) {
1566
+ let matches = true;
1567
+ for (let index = 0;index < size; index += 1) {
1568
+ if (baseSegments[baseSegments.length - size + index] !== pathSegments[index]) {
1569
+ matches = false;
1570
+ break;
1571
+ }
1572
+ }
1573
+ if (matches) {
1574
+ return size;
1575
+ }
1576
+ }
1577
+ return 0;
1578
+ }
1541
1579
 
1542
1580
  // src/providers/openai-compatible.ts
1543
1581
  function createOpenAICompatibleAdapter(options) {
@@ -1696,7 +1734,7 @@ async function completeWithChatCompletionsPassThrough(options, fetcher, path, re
1696
1734
  const message = await response.text();
1697
1735
  throw new Error(`HTTP ${response.status}: ${message}`);
1698
1736
  }
1699
- const payload = await response.json();
1737
+ const payload = await parseOpenAICompatibleJSONResponse(response, "Failed to parse OpenAI-compatible chat completion response");
1700
1738
  const assistantMessage = pickAssistantMessage(payload);
1701
1739
  if (!assistantMessage) {
1702
1740
  throw new Error("No assistant message in OpenAI-compatible response.");
@@ -1712,6 +1750,25 @@ async function completeWithChatCompletionsPassThrough(options, fetcher, path, re
1712
1750
  toolCalls: toolCalls.length > 0 ? toolCalls : undefined
1713
1751
  };
1714
1752
  }
1753
+ async function parseOpenAICompatibleJSONResponse(response, context) {
1754
+ const rawBody = await response.text();
1755
+ try {
1756
+ return JSON.parse(rawBody);
1757
+ } catch (error) {
1758
+ const message = error instanceof Error ? error.message : String(error);
1759
+ throw new Error(`${context} (HTTP ${response.status}): ${message}. Raw body: ${formatResponseBodyForError(rawBody)}`);
1760
+ }
1761
+ }
1762
+ function formatResponseBodyForError(rawBody, maxLength = 2000) {
1763
+ const normalized = rawBody.trim();
1764
+ if (normalized.length === 0) {
1765
+ return "[empty body]";
1766
+ }
1767
+ if (normalized.length <= maxLength) {
1768
+ return normalized;
1769
+ }
1770
+ return `${normalized.slice(0, maxLength)}...[truncated ${normalized.length - maxLength} chars]`;
1771
+ }
1715
1772
  async function completeWithChatCompletionsWithMCP(options, fetcher, path, request) {
1716
1773
  const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
1717
1774
  let messages = buildMessages(request);
@@ -1720,6 +1777,7 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
1720
1777
  let lastPayload;
1721
1778
  const toolCalls = [];
1722
1779
  const toolExecutions = [];
1780
+ const reasoningBlocks = [];
1723
1781
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
1724
1782
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
1725
1783
  const transportTools = toProviderFunctionTools(mcpToolset);
@@ -1750,14 +1808,17 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
1750
1808
  finishReason = pickFinishReason(payload);
1751
1809
  const assistantMessage = pickAssistantMessage(payload);
1752
1810
  const calledTools = pickChatToolCalls(payload);
1811
+ const roundReasoning = pickAssistantReasoning(payload);
1812
+ pushReasoningBlock(reasoningBlocks, round, roundReasoning);
1753
1813
  if (!assistantMessage) {
1754
1814
  throw new Error("No assistant message in OpenAI-compatible response.");
1755
1815
  }
1756
1816
  if (calledTools.length === 0) {
1757
- const reasoning = pickAssistantReasoning(payload);
1817
+ const reasoning = joinReasoningBlocks(reasoningBlocks) || undefined;
1758
1818
  return {
1759
1819
  text: pickAssistantText(payload),
1760
- reasoning: reasoning.length > 0 ? reasoning : undefined,
1820
+ reasoning,
1821
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
1761
1822
  raw: payload,
1762
1823
  usage: aggregatedUsage,
1763
1824
  finishReason,
@@ -1785,10 +1846,8 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
1785
1846
  }
1786
1847
  return {
1787
1848
  text: pickAssistantText(lastPayload ?? {}),
1788
- reasoning: (() => {
1789
- const value = pickAssistantReasoning(lastPayload ?? {});
1790
- return value.length > 0 ? value : undefined;
1791
- })(),
1849
+ reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
1850
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
1792
1851
  raw: lastPayload,
1793
1852
  usage: aggregatedUsage,
1794
1853
  finishReason,
@@ -1836,6 +1895,7 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
1836
1895
  let lastPayload;
1837
1896
  const executedToolCalls = [];
1838
1897
  const toolExecutions = [];
1898
+ const reasoningBlocks = [];
1839
1899
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
1840
1900
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
1841
1901
  const transportTools = toResponsesTools(toProviderFunctionTools(mcpToolset));
@@ -1865,12 +1925,15 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
1865
1925
  lastPayload = payload;
1866
1926
  aggregatedUsage = mergeUsage(aggregatedUsage, pickUsage(payload));
1867
1927
  finishReason = pickResponsesFinishReason(payload) ?? finishReason;
1928
+ pushReasoningBlock(reasoningBlocks, round, pickResponsesReasoning(payload));
1868
1929
  const providerToolCalls = pickResponsesToolCalls(payload);
1869
1930
  const functionCalls = providerToolCalls.filter((toolCall) => toolCall.type === "function" && typeof toolCall.id === "string" && typeof toolCall.name === "string");
1870
1931
  if (functionCalls.length === 0) {
1871
1932
  const text = pickResponsesText(payload) || pickAssistantText(payload);
1872
1933
  return {
1873
1934
  text,
1935
+ reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
1936
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
1874
1937
  raw: payload,
1875
1938
  usage: aggregatedUsage,
1876
1939
  finishReason,
@@ -1898,6 +1961,8 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
1898
1961
  }
1899
1962
  return {
1900
1963
  text: pickResponsesText(lastPayload ?? {}) || pickAssistantText(lastPayload ?? {}),
1964
+ reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
1965
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
1901
1966
  raw: lastPayload,
1902
1967
  usage: aggregatedUsage,
1903
1968
  finishReason,
@@ -1913,9 +1978,9 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
1913
1978
  let lastPayload;
1914
1979
  const executedToolCalls = [];
1915
1980
  const toolExecutions = [];
1981
+ const reasoningBlocks = [];
1916
1982
  callbacks.onStart?.();
1917
1983
  let lastRoundText = "";
1918
- let lastRoundReasoning = "";
1919
1984
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
1920
1985
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
1921
1986
  const transportTools = toProviderFunctionTools(mcpToolset);
@@ -1977,6 +2042,7 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
1977
2042
  const chunk = {
1978
2043
  textDelta: delta,
1979
2044
  reasoningDelta: reasoningDelta || undefined,
2045
+ turnIndex: round,
1980
2046
  raw: json,
1981
2047
  usage: chunkUsage,
1982
2048
  finishReason: chunkFinishReason
@@ -1989,22 +2055,41 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
1989
2055
  finishReason = roundFinishReason;
1990
2056
  }
1991
2057
  const calledTools = buildOpenAIStreamToolCalls(streamedToolCalls);
2058
+ pushReasoningBlock(reasoningBlocks, round, roundReasoning);
2059
+ request.onTurnTransition?.({
2060
+ turnIndex: round,
2061
+ kind: "reasoningComplete",
2062
+ reasoningText: roundReasoning
2063
+ });
1992
2064
  if (calledTools.length === 0) {
1993
2065
  const out2 = {
1994
2066
  text: roundText,
1995
- reasoning: roundReasoning.length > 0 ? roundReasoning : undefined,
2067
+ reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
2068
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
1996
2069
  raw: lastPayload,
1997
2070
  usage: aggregatedUsage,
1998
2071
  finishReason,
1999
2072
  toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
2000
2073
  toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
2001
2074
  };
2075
+ request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
2002
2076
  callbacks.onComplete?.(out2);
2003
2077
  return out2;
2004
2078
  }
2005
2079
  if (round > maxToolRounds) {
2006
2080
  throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
2007
2081
  }
2082
+ request.onTurnTransition?.({
2083
+ turnIndex: round,
2084
+ kind: "toolCallsEmit",
2085
+ toolCalls: calledTools
2086
+ });
2087
+ callbacks.onChunk?.({
2088
+ textDelta: "",
2089
+ turnIndex: round,
2090
+ toolCalls: calledTools,
2091
+ finishReason: roundFinishReason
2092
+ });
2008
2093
  const outputs = await executeMCPToolCalls(calledTools, mcpToolset, {
2009
2094
  round,
2010
2095
  request,
@@ -2013,8 +2098,8 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2013
2098
  });
2014
2099
  executedToolCalls.push(...outputs.map((entry) => entry.call));
2015
2100
  toolExecutions.push(...outputs.map((entry) => entry.execution));
2101
+ request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
2016
2102
  lastRoundText = roundText;
2017
- lastRoundReasoning = roundReasoning;
2018
2103
  const assistantMessage = buildOpenAIAssistantToolMessage(roundText, calledTools, {
2019
2104
  reasoning: roundReasoning,
2020
2105
  reasoningFieldName
@@ -2028,13 +2113,15 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2028
2113
  }
2029
2114
  const out = {
2030
2115
  text: lastRoundText,
2031
- reasoning: lastRoundReasoning.length > 0 ? lastRoundReasoning : undefined,
2116
+ reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
2117
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
2032
2118
  raw: lastPayload,
2033
2119
  usage: aggregatedUsage,
2034
2120
  finishReason,
2035
2121
  toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
2036
2122
  toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
2037
2123
  };
2124
+ request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
2038
2125
  callbacks.onComplete?.(out);
2039
2126
  return out;
2040
2127
  }
@@ -2117,6 +2204,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2117
2204
  let lastPayload;
2118
2205
  const executedToolCalls = [];
2119
2206
  const toolExecutions = [];
2207
+ const reasoningBlocks = [];
2120
2208
  callbacks.onStart?.();
2121
2209
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
2122
2210
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
@@ -2145,6 +2233,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2145
2233
  throw new Error(`HTTP ${response.status}: ${message}`);
2146
2234
  }
2147
2235
  let roundText = "";
2236
+ let roundReasoning = "";
2148
2237
  let roundUsage;
2149
2238
  let roundFinishReason;
2150
2239
  let roundPayload;
@@ -2163,6 +2252,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2163
2252
  lastPayload = payload;
2164
2253
  }
2165
2254
  const delta = pickResponsesStreamTextDelta(json);
2255
+ const reasoningDelta = pickResponsesStreamReasoningDelta(json);
2166
2256
  const chunkUsage = pickResponsesStreamUsage(json);
2167
2257
  const chunkFinishReason = pickResponsesStreamFinishReason(json);
2168
2258
  collectResponsesStreamToolCalls(json, streamedToolCalls);
@@ -2174,9 +2264,14 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2174
2264
  roundText += delta;
2175
2265
  callbacks.onToken?.(delta);
2176
2266
  }
2177
- if (delta || chunkUsage || chunkFinishReason) {
2267
+ if (reasoningDelta) {
2268
+ roundReasoning += reasoningDelta;
2269
+ }
2270
+ if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
2178
2271
  const chunk = {
2179
2272
  textDelta: delta,
2273
+ reasoningDelta: reasoningDelta || undefined,
2274
+ turnIndex: round,
2180
2275
  raw: json,
2181
2276
  usage: chunkUsage,
2182
2277
  finishReason: chunkFinishReason
@@ -2192,25 +2287,48 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2192
2287
  finishReason = pickResponsesFinishReason(roundPayload) ?? finishReason;
2193
2288
  }
2194
2289
  const payloadToolCalls = roundPayload ? pickResponsesToolCalls(roundPayload) : [];
2290
+ if (roundPayload && roundReasoning.length === 0) {
2291
+ roundReasoning = pickResponsesReasoning(roundPayload);
2292
+ }
2195
2293
  const streamedCalls = buildResponsesStreamToolCalls(streamedToolCalls);
2196
2294
  const providerToolCalls = payloadToolCalls.length > 0 ? payloadToolCalls : streamedCalls;
2197
2295
  const functionCalls = providerToolCalls.filter((toolCall) => toolCall.type === "function" && typeof toolCall.id === "string" && typeof toolCall.name === "string");
2296
+ pushReasoningBlock(reasoningBlocks, round, roundReasoning);
2297
+ request.onTurnTransition?.({
2298
+ turnIndex: round,
2299
+ kind: "reasoningComplete",
2300
+ reasoningText: roundReasoning
2301
+ });
2198
2302
  if (functionCalls.length === 0) {
2199
2303
  const finalText = roundText.length > 0 ? roundText : roundPayload ? pickResponsesText(roundPayload) || pickAssistantText(roundPayload) : "";
2200
2304
  const out2 = {
2201
2305
  text: finalText,
2306
+ reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
2307
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
2202
2308
  raw: roundPayload ?? lastPayload,
2203
2309
  usage: aggregatedUsage,
2204
2310
  finishReason,
2205
2311
  toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
2206
2312
  toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
2207
2313
  };
2314
+ request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
2208
2315
  callbacks.onComplete?.(out2);
2209
2316
  return out2;
2210
2317
  }
2211
2318
  if (round > maxToolRounds) {
2212
2319
  throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
2213
2320
  }
2321
+ request.onTurnTransition?.({
2322
+ turnIndex: round,
2323
+ kind: "toolCallsEmit",
2324
+ toolCalls: functionCalls
2325
+ });
2326
+ callbacks.onChunk?.({
2327
+ textDelta: "",
2328
+ turnIndex: round,
2329
+ toolCalls: functionCalls,
2330
+ finishReason: roundFinishReason
2331
+ });
2214
2332
  const outputs = await executeMCPToolCalls(functionCalls, mcpToolset, {
2215
2333
  round,
2216
2334
  request,
@@ -2219,6 +2337,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2219
2337
  });
2220
2338
  executedToolCalls.push(...outputs.map((entry) => entry.call));
2221
2339
  toolExecutions.push(...outputs.map((entry) => entry.execution));
2340
+ request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
2222
2341
  input = outputs.map((entry) => ({
2223
2342
  type: "function_call_output",
2224
2343
  call_id: entry.call.id,
@@ -2228,12 +2347,15 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2228
2347
  }
2229
2348
  const out = {
2230
2349
  text: pickResponsesText(lastPayload ?? {}) || pickAssistantText(lastPayload ?? {}),
2350
+ reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
2351
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
2231
2352
  raw: lastPayload,
2232
2353
  usage: aggregatedUsage,
2233
2354
  finishReason,
2234
2355
  toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
2235
2356
  toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
2236
2357
  };
2358
+ request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
2237
2359
  callbacks.onComplete?.(out);
2238
2360
  return out;
2239
2361
  }
@@ -2518,6 +2640,20 @@ function pickResponsesStreamTextDelta(payload) {
2518
2640
  }
2519
2641
  return "";
2520
2642
  }
2643
+ function pickResponsesStreamReasoningDelta(payload) {
2644
+ const eventType = pickString(payload.type) ?? "";
2645
+ if (!eventType.includes("reasoning") && !eventType.includes("thinking")) {
2646
+ return "";
2647
+ }
2648
+ const direct = pickString(payload.delta);
2649
+ if (direct) {
2650
+ return direct;
2651
+ }
2652
+ if (isRecord2(payload.delta)) {
2653
+ return pickReasoningText(payload.delta) || pickString(payload.delta.text) || pickString(payload.delta.summary_text) || "";
2654
+ }
2655
+ return "";
2656
+ }
2521
2657
  function pickResponsesStreamUsage(payload) {
2522
2658
  const direct = pickUsage(payload);
2523
2659
  if (direct) {
@@ -2652,6 +2788,30 @@ function pickResponsesText(payload) {
2652
2788
  }).join("");
2653
2789
  }).join("");
2654
2790
  }
2791
+ function pickResponsesReasoning(payload) {
2792
+ const direct = pickReasoningText(payload);
2793
+ if (direct) {
2794
+ return direct;
2795
+ }
2796
+ const output = payload.output;
2797
+ if (!Array.isArray(output)) {
2798
+ return "";
2799
+ }
2800
+ return output.map((item) => {
2801
+ if (!isRecord2(item)) {
2802
+ return "";
2803
+ }
2804
+ const itemReasoning = pickReasoningText(item);
2805
+ if (itemReasoning) {
2806
+ return itemReasoning;
2807
+ }
2808
+ const itemType = pickString(item.type) ?? "";
2809
+ if ((itemType.includes("reasoning") || itemType.includes("thinking")) && Array.isArray(item.content)) {
2810
+ return item.content.map((part) => isRecord2(part) ? pickTextLike(part) : "").join("");
2811
+ }
2812
+ return "";
2813
+ }).join("");
2814
+ }
2655
2815
  function pickAssistantText(payload) {
2656
2816
  const message = pickAssistantMessage(payload);
2657
2817
  if (message) {
@@ -2672,6 +2832,18 @@ function pickAssistantText(payload) {
2672
2832
  function pickReasoningText(value) {
2673
2833
  return pickTextLike(value.reasoning) || pickTextLike(value.reasoning_content);
2674
2834
  }
2835
+ function pushReasoningBlock(blocks, turnIndex, text) {
2836
+ const clean = text?.replace(/<\/?think\s*>/gi, "").trim();
2837
+ if (!clean) {
2838
+ return;
2839
+ }
2840
+ blocks.push({ turnIndex, text: clean });
2841
+ }
2842
+ function joinReasoningBlocks(blocks) {
2843
+ return blocks.map((block) => block.text).filter(Boolean).join(`
2844
+
2845
+ `);
2846
+ }
2675
2847
  function pickTextFromOpenAIContent(value) {
2676
2848
  return pickTextLike(value);
2677
2849
  }
@@ -2852,6 +3024,7 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
2852
3024
  let lastPayload;
2853
3025
  const toolCalls = [];
2854
3026
  const toolExecutions = [];
3027
+ const reasoningBlocks = [];
2855
3028
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
2856
3029
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
2857
3030
  const tools = toAnthropicTools(toProviderFunctionTools(mcpToolset));
@@ -2881,9 +3054,12 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
2881
3054
  finishReason = pickFinishReason2(payload);
2882
3055
  const content = Array.isArray(payload.content) ? payload.content : [];
2883
3056
  const calledTools = pickAnthropicToolCalls(payload).filter((call) => call.type === "function");
3057
+ pushReasoningBlock2(reasoningBlocks, round, extractAnthropicReasoning(payload));
2884
3058
  if (calledTools.length === 0) {
2885
3059
  return {
2886
3060
  text: extractAnthropicText(payload),
3061
+ reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
3062
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
2887
3063
  raw: payload,
2888
3064
  usage: aggregatedUsage,
2889
3065
  finishReason,
@@ -2919,6 +3095,8 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
2919
3095
  }
2920
3096
  return {
2921
3097
  text: extractAnthropicText(lastPayload ?? {}),
3098
+ reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
3099
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
2922
3100
  raw: lastPayload,
2923
3101
  usage: aggregatedUsage,
2924
3102
  finishReason,
@@ -2935,6 +3113,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
2935
3113
  let lastPayload;
2936
3114
  const toolCalls = [];
2937
3115
  const toolExecutions = [];
3116
+ const reasoningBlocks = [];
2938
3117
  callbacks.onStart?.();
2939
3118
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
2940
3119
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
@@ -2960,6 +3139,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
2960
3139
  throw new Error(`HTTP ${response.status}: ${message}`);
2961
3140
  }
2962
3141
  let roundText = "";
3142
+ let roundReasoning = "";
2963
3143
  let roundUsage;
2964
3144
  let roundFinishReason;
2965
3145
  const streamedToolCalls = new Map;
@@ -2973,6 +3153,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
2973
3153
  }
2974
3154
  lastPayload = json;
2975
3155
  const delta = pickAnthropicDelta(json);
3156
+ const reasoningDelta = pickAnthropicReasoningDelta(json);
2976
3157
  const chunkUsage = pickUsage2(json);
2977
3158
  const chunkFinishReason = pickFinishReason2(json);
2978
3159
  collectAnthropicStreamToolCalls(json, streamedToolCalls);
@@ -2984,9 +3165,14 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
2984
3165
  roundText += delta;
2985
3166
  callbacks.onToken?.(delta);
2986
3167
  }
2987
- if (delta || chunkUsage || chunkFinishReason) {
3168
+ if (reasoningDelta) {
3169
+ roundReasoning += reasoningDelta;
3170
+ }
3171
+ if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
2988
3172
  const chunk = {
2989
3173
  textDelta: delta,
3174
+ reasoningDelta: reasoningDelta || undefined,
3175
+ turnIndex: round,
2990
3176
  raw: json,
2991
3177
  usage: chunkUsage,
2992
3178
  finishReason: chunkFinishReason
@@ -2999,21 +3185,41 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
2999
3185
  finishReason = roundFinishReason;
3000
3186
  }
3001
3187
  const calledTools = buildAnthropicStreamToolCalls(streamedToolCalls);
3188
+ pushReasoningBlock2(reasoningBlocks, round, roundReasoning);
3189
+ request.onTurnTransition?.({
3190
+ turnIndex: round,
3191
+ kind: "reasoningComplete",
3192
+ reasoningText: roundReasoning
3193
+ });
3002
3194
  if (calledTools.length === 0) {
3003
3195
  const out2 = {
3004
3196
  text: roundText,
3197
+ reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
3198
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
3005
3199
  raw: lastPayload,
3006
3200
  usage: aggregatedUsage,
3007
3201
  finishReason,
3008
3202
  toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
3009
3203
  toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
3010
3204
  };
3205
+ request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
3011
3206
  callbacks.onComplete?.(out2);
3012
3207
  return out2;
3013
3208
  }
3014
3209
  if (round > maxToolRounds) {
3015
3210
  throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
3016
3211
  }
3212
+ request.onTurnTransition?.({
3213
+ turnIndex: round,
3214
+ kind: "toolCallsEmit",
3215
+ toolCalls: calledTools
3216
+ });
3217
+ callbacks.onChunk?.({
3218
+ textDelta: "",
3219
+ turnIndex: round,
3220
+ toolCalls: calledTools,
3221
+ finishReason: roundFinishReason
3222
+ });
3017
3223
  const toolResultContent = [];
3018
3224
  const outputs = await executeMCPToolCalls(calledTools, mcpToolset, {
3019
3225
  round,
@@ -3023,6 +3229,7 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
3023
3229
  });
3024
3230
  toolCalls.push(...outputs.map((entry) => entry.call));
3025
3231
  toolExecutions.push(...outputs.map((entry) => entry.execution));
3232
+ request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
3026
3233
  for (const entry of outputs) {
3027
3234
  toolResultContent.push({
3028
3235
  type: "tool_result",
@@ -3039,12 +3246,15 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
3039
3246
  }
3040
3247
  const out = {
3041
3248
  text: "",
3249
+ reasoning: joinReasoningBlocks2(reasoningBlocks) || undefined,
3250
+ reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
3042
3251
  raw: lastPayload,
3043
3252
  usage: aggregatedUsage,
3044
3253
  finishReason,
3045
3254
  toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
3046
3255
  toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
3047
3256
  };
3257
+ request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
3048
3258
  callbacks.onComplete?.(out);
3049
3259
  return out;
3050
3260
  }
@@ -3165,6 +3375,22 @@ function extractAnthropicText(payload) {
3165
3375
  return typeof text === "string" ? text : "";
3166
3376
  }).join("");
3167
3377
  }
3378
+ function extractAnthropicReasoning(payload) {
3379
+ const content = payload.content;
3380
+ if (!Array.isArray(content)) {
3381
+ return "";
3382
+ }
3383
+ return content.map((part) => {
3384
+ if (!isRecord2(part)) {
3385
+ return "";
3386
+ }
3387
+ const type = pickString(part.type) ?? "";
3388
+ if (type !== "thinking" && type !== "reasoning") {
3389
+ return "";
3390
+ }
3391
+ return pickString(part.thinking) ?? pickString(part.text) ?? pickString(part.reasoning) ?? "";
3392
+ }).join("");
3393
+ }
3168
3394
  function pickAnthropicToolCalls(payload) {
3169
3395
  const content = payload.content;
3170
3396
  if (!Array.isArray(content)) {
@@ -3195,6 +3421,35 @@ function pickAnthropicDelta(payload) {
3195
3421
  }
3196
3422
  return "";
3197
3423
  }
3424
+ function pickAnthropicReasoningDelta(payload) {
3425
+ const deltaObject = payload.delta;
3426
+ if (isRecord2(deltaObject)) {
3427
+ const type = pickString(deltaObject.type) ?? "";
3428
+ if (type === "thinking_delta" || type === "reasoning_delta") {
3429
+ return pickString(deltaObject.thinking) ?? pickString(deltaObject.text) ?? "";
3430
+ }
3431
+ }
3432
+ const contentBlock = payload.content_block;
3433
+ if (isRecord2(contentBlock)) {
3434
+ const type = pickString(contentBlock.type) ?? "";
3435
+ if (type === "thinking" || type === "reasoning") {
3436
+ return pickString(contentBlock.thinking) ?? pickString(contentBlock.text) ?? "";
3437
+ }
3438
+ }
3439
+ return "";
3440
+ }
3441
+ function pushReasoningBlock2(blocks, turnIndex, text) {
3442
+ const clean = text?.replace(/<\/?think\s*>/gi, "").trim();
3443
+ if (!clean) {
3444
+ return;
3445
+ }
3446
+ blocks.push({ turnIndex, text: clean });
3447
+ }
3448
+ function joinReasoningBlocks2(blocks) {
3449
+ return blocks.map((block) => block.text).filter(Boolean).join(`
3450
+
3451
+ `);
3452
+ }
3198
3453
  function collectAnthropicStreamToolCalls(payload, state) {
3199
3454
  const eventType = pickString(payload.type);
3200
3455
  if (!eventType) {
@@ -3668,6 +3923,7 @@ function normalizeStreamConfig(option) {
3668
3923
  return {
3669
3924
  enabled: option.enabled ?? true,
3670
3925
  onData: option.onData,
3926
+ onTurnTransition: option.onTurnTransition,
3671
3927
  to: option.to
3672
3928
  };
3673
3929
  }
@@ -3735,6 +3991,7 @@ async function callModel(adapter, options) {
3735
3991
  transformToolCallParams: options.request?.transformToolCallParams,
3736
3992
  unknownToolError: options.request?.unknownToolError,
3737
3993
  toolDebug: options.request?.toolDebug,
3994
+ onTurnTransition: options.stream.onTurnTransition,
3738
3995
  body: options.request?.body,
3739
3996
  signal: requestSignal
3740
3997
  };
@@ -3762,13 +4019,21 @@ async function callModel(adapter, options) {
3762
4019
  let latestFinishReason;
3763
4020
  let streamedProviderText = "";
3764
4021
  let streamedDedicatedReasoning = "";
4022
+ let currentTurnIndex;
4023
+ let currentToolCalls;
4024
+ let streamedReasoningBlocks;
3765
4025
  let lastSnapshotFingerprint;
3766
4026
  let previousSnapshotText = "";
3767
4027
  let previousSnapshotReasoning = "";
3768
4028
  const emitStreamingData = (done, usage2, finishReason2) => {
3769
- const normalized2 = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning);
4029
+ const normalized2 = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning, streamedReasoningBlocks);
3770
4030
  const snapshot = options.buildSnapshot(normalized2);
3771
- const fingerprint = toStreamDataFingerprint(snapshot);
4031
+ const fingerprint = toStreamDataFingerprint({
4032
+ snapshot,
4033
+ done,
4034
+ turnIndex: currentTurnIndex,
4035
+ toolCalls: currentToolCalls
4036
+ });
3772
4037
  if (!done && fingerprint === lastSnapshotFingerprint) {
3773
4038
  return;
3774
4039
  }
@@ -3784,7 +4049,9 @@ async function callModel(adapter, options) {
3784
4049
  snapshot,
3785
4050
  done,
3786
4051
  usage: usage2,
3787
- finishReason: finishReason2
4052
+ finishReason: finishReason2,
4053
+ turnIndex: currentTurnIndex,
4054
+ toolCalls: currentToolCalls
3788
4055
  });
3789
4056
  if (options.stream.to === "stdout" && delta.text) {
3790
4057
  process.stdout.write(delta.text);
@@ -3819,8 +4086,21 @@ async function callModel(adapter, options) {
3819
4086
  streamedDedicatedReasoning += delta;
3820
4087
  emitStreamingData(false);
3821
4088
  };
3822
- const response2 = await adapter.stream(requestPayload, {
4089
+ const streamRequestPayload = {
4090
+ ...requestPayload,
4091
+ onTurnTransition: (transition) => {
4092
+ if (transition.kind === "reasoningComplete") {
4093
+ streamedReasoningBlocks = appendReasoningBlock(streamedReasoningBlocks, transition);
4094
+ }
4095
+ options.stream.onTurnTransition?.(transition);
4096
+ }
4097
+ };
4098
+ const response2 = await adapter.stream(streamRequestPayload, {
3823
4099
  onChunk: (chunk) => {
4100
+ if (chunk.turnIndex !== undefined) {
4101
+ currentTurnIndex = chunk.turnIndex;
4102
+ }
4103
+ currentToolCalls = chunk.toolCalls;
3824
4104
  if (chunk.textDelta) {
3825
4105
  handleTextDelta(chunk.textDelta);
3826
4106
  }
@@ -3833,11 +4113,15 @@ async function callModel(adapter, options) {
3833
4113
  if (chunk.finishReason) {
3834
4114
  latestFinishReason = chunk.finishReason;
3835
4115
  }
4116
+ if (!chunk.textDelta && !chunk.reasoningDelta && (chunk.turnIndex !== undefined || chunk.toolCalls)) {
4117
+ emitStreamingData(false, chunk.usage, chunk.finishReason);
4118
+ }
3836
4119
  }
3837
4120
  });
3838
4121
  streamedProviderText = typeof response2.text === "string" ? response2.text : streamedProviderText;
3839
4122
  streamedDedicatedReasoning = typeof response2.reasoning === "string" ? response2.reasoning : streamedDedicatedReasoning;
3840
- const finalNormalized = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning);
4123
+ streamedReasoningBlocks = response2.reasoningBlocks ?? streamedReasoningBlocks;
4124
+ const finalNormalized = normalizeModelOutput(streamedProviderText, streamedDedicatedReasoning, streamedReasoningBlocks);
3841
4125
  const usage = preferLatestUsage(latestUsage, response2.usage);
3842
4126
  const finishReason = response2.finishReason ?? latestFinishReason;
3843
4127
  emitStreamingData(true, usage, finishReason);
@@ -3869,11 +4153,12 @@ async function callModel(adapter, options) {
3869
4153
  parseSource: finalNormalized.parseSource,
3870
4154
  via: "stream",
3871
4155
  usage,
3872
- finishReason
4156
+ finishReason,
4157
+ reasoningBlocks: finalNormalized.reasoningBlocks
3873
4158
  };
3874
4159
  }
3875
4160
  const response = await adapter.complete(requestPayload);
3876
- const normalized = normalizeModelOutput(response.text, response.reasoning);
4161
+ const normalized = normalizeModelOutput(response.text, response.reasoning, response.reasoningBlocks);
3877
4162
  options.observe?.(options.buildEvent({
3878
4163
  stage: "llm.response",
3879
4164
  message: "Completion response received.",
@@ -3902,10 +4187,11 @@ async function callModel(adapter, options) {
3902
4187
  parseSource: normalized.parseSource,
3903
4188
  via: "complete",
3904
4189
  usage: response.usage,
3905
- finishReason: response.finishReason
4190
+ finishReason: response.finishReason,
4191
+ reasoningBlocks: normalized.reasoningBlocks
3906
4192
  };
3907
4193
  }
3908
- function normalizeModelOutput(text, dedicatedReasoning) {
4194
+ function normalizeModelOutput(text, dedicatedReasoning, reasoningBlocks) {
3909
4195
  const sanitized = sanitizeThink(text);
3910
4196
  const visibleText = stripThinkBlocks(text, sanitized.thinkBlocks);
3911
4197
  const reasoning = joinReasoningSegments([
@@ -3915,10 +4201,29 @@ function normalizeModelOutput(text, dedicatedReasoning) {
3915
4201
  return {
3916
4202
  text: visibleText,
3917
4203
  reasoning,
4204
+ reasoningBlocks: normalizeReasoningBlocks(reasoningBlocks),
3918
4205
  thinkBlocks: sanitized.thinkBlocks,
3919
4206
  parseSource: composeParseSource(visibleText, reasoning)
3920
4207
  };
3921
4208
  }
4209
+ function normalizeReasoningBlocks(blocks) {
4210
+ if (!Array.isArray(blocks)) {
4211
+ return;
4212
+ }
4213
+ const normalized = blocks.map((block) => ({
4214
+ turnIndex: block.turnIndex,
4215
+ text: block.text.replace(RE_THINK_TAGS, "").trim()
4216
+ })).filter((block) => Number.isFinite(block.turnIndex) && block.text.length > 0);
4217
+ return normalized.length > 0 ? normalized : undefined;
4218
+ }
4219
+ function appendReasoningBlock(blocks, transition) {
4220
+ const text = transition.reasoningText?.replace(RE_THINK_TAGS, "").trim();
4221
+ if (!text) {
4222
+ return blocks;
4223
+ }
4224
+ const next = [...blocks ?? [], { turnIndex: transition.turnIndex, text }];
4225
+ return normalizeReasoningBlocks(next);
4226
+ }
3922
4227
  function composeParseSource(text, reasoning) {
3923
4228
  if (typeof reasoning !== "string" || reasoning.length === 0) {
3924
4229
  return text;
@@ -4078,7 +4383,8 @@ async function generate(adapter, promptOrOptions, callOptions) {
4078
4383
  }),
4079
4384
  buildSnapshot: (model) => ({
4080
4385
  text: model.text,
4081
- reasoning: model.reasoning
4386
+ reasoning: model.reasoning,
4387
+ ...model.reasoningBlocks ? { reasoningBlocks: model.reasoningBlocks } : {}
4082
4388
  }),
4083
4389
  debug: debugConfig,
4084
4390
  debugLabel: "generate",
@@ -4093,7 +4399,8 @@ async function generate(adapter, promptOrOptions, callOptions) {
4093
4399
  text: response.text,
4094
4400
  reasoning: response.reasoning,
4095
4401
  usage: response.usage,
4096
- finishReason: response.finishReason
4402
+ finishReason: response.finishReason,
4403
+ ...response.reasoningBlocks ? { reasoningBlocks: response.reasoningBlocks } : {}
4097
4404
  };
4098
4405
  const attempts = [attempt];
4099
4406
  normalized.observe?.({
@@ -4110,7 +4417,8 @@ async function generate(adapter, promptOrOptions, callOptions) {
4110
4417
  reasoning: attempt.reasoning,
4111
4418
  attempts,
4112
4419
  usage: aggregateUsage(attempts),
4113
- finishReason: attempt.finishReason
4420
+ finishReason: attempt.finishReason,
4421
+ ...attempt.reasoningBlocks ? { reasoningBlocks: attempt.reasoningBlocks } : {}
4114
4422
  };
4115
4423
  }
4116
4424
  function normalizeGenerateInput(promptOrOptions, callOptions) {
@@ -4930,6 +5238,7 @@ async function executeAttempt(adapter, input) {
4930
5238
  success: parsed.success,
4931
5239
  usage: response.usage,
4932
5240
  finishReason: response.finishReason,
5241
+ ...response.reasoningBlocks ? { reasoningBlocks: response.reasoningBlocks } : {},
4933
5242
  parsed
4934
5243
  };
4935
5244
  return {
@@ -4950,6 +5259,7 @@ async function callModel2(adapter, options) {
4950
5259
  buildSnapshot: (normalized) => ({
4951
5260
  text: normalized.text,
4952
5261
  reasoning: normalized.reasoning,
5262
+ ...normalized.reasoningBlocks ? { reasoningBlocks: normalized.reasoningBlocks } : {},
4953
5263
  data: parseStreamingStructuredData(normalized.parseSource) ?? null
4954
5264
  }),
4955
5265
  debugLabel: "structured"
@@ -5050,7 +5360,8 @@ function buildSuccessResult(data, attempts) {
5050
5360
  json: final?.json ?? null,
5051
5361
  attempts,
5052
5362
  usage: aggregateUsage(attempts),
5053
- finishReason: final?.finishReason
5363
+ finishReason: final?.finishReason,
5364
+ ...final?.reasoningBlocks ? { reasoningBlocks: final.reasoningBlocks } : {}
5054
5365
  };
5055
5366
  }
5056
5367
  function toStructuredError(attempt) {
@@ -5517,11 +5828,11 @@ function inferSchemaExample(schema) {
5517
5828
  }
5518
5829
  function getObjectShape(schema) {
5519
5830
  const unwrapped = unwrap2(schema).schema;
5520
- const typeName = unwrapped._def?.type;
5831
+ const typeName = unwrapped.def?.type;
5521
5832
  if (typeName !== "object") {
5522
5833
  return null;
5523
5834
  }
5524
- const rawShape = unwrapped._def?.shape;
5835
+ const rawShape = unwrapped.def?.shape;
5525
5836
  if (typeof rawShape === "function") {
5526
5837
  return rawShape();
5527
5838
  }
@@ -5529,11 +5840,11 @@ function getObjectShape(schema) {
5529
5840
  }
5530
5841
  function readDefaultValue(schema) {
5531
5842
  let current = schema;
5532
- while (current?._def?.type) {
5533
- const typeName = current._def.type;
5843
+ while (current?.def?.type) {
5844
+ const typeName = current.def.type;
5534
5845
  if (typeName === "default") {
5535
5846
  try {
5536
- const raw = current._def.defaultValue;
5847
+ const raw = current.def.defaultValue;
5537
5848
  if (typeof raw === "function") {
5538
5849
  return raw();
5539
5850
  }
@@ -5543,11 +5854,11 @@ function readDefaultValue(schema) {
5543
5854
  }
5544
5855
  }
5545
5856
  if (typeName === "optional" || typeName === "nullable" || typeName === "catch" || typeName === "readonly") {
5546
- current = current._def.innerType ?? current;
5857
+ current = current.def.innerType ?? current;
5547
5858
  continue;
5548
5859
  }
5549
5860
  if (typeName === "pipe") {
5550
- current = current._def.in ?? current;
5861
+ current = current.def.in ?? current;
5551
5862
  continue;
5552
5863
  }
5553
5864
  return;
@@ -5556,22 +5867,22 @@ function readDefaultValue(schema) {
5556
5867
  }
5557
5868
  function readSchemaDescription2(schema) {
5558
5869
  let current = schema;
5559
- while (current?._def?.type) {
5870
+ while (current?.def?.type) {
5560
5871
  const desc = current.description;
5561
5872
  if (typeof desc === "string" && desc.trim().length > 0) {
5562
5873
  return desc.trim();
5563
5874
  }
5564
- const typeName = current._def.type;
5875
+ const typeName = current.def.type;
5565
5876
  if (typeName === "optional" || typeName === "default" || typeName === "nullable") {
5566
- current = current._def.innerType ?? current;
5877
+ current = current.def.innerType ?? current;
5567
5878
  continue;
5568
5879
  }
5569
5880
  if (typeName === "catch" || typeName === "readonly") {
5570
- current = current._def.innerType ?? current;
5881
+ current = current.def.innerType ?? current;
5571
5882
  continue;
5572
5883
  }
5573
5884
  if (typeName === "pipe") {
5574
- current = current._def.in ?? current;
5885
+ current = current.def.in ?? current;
5575
5886
  continue;
5576
5887
  }
5577
5888
  break;
@@ -5581,19 +5892,19 @@ function readSchemaDescription2(schema) {
5581
5892
  function unwrap2(schema) {
5582
5893
  let current = schema;
5583
5894
  let optional = false;
5584
- while (current?._def?.type) {
5585
- const typeName = current._def.type;
5895
+ while (current?.def?.type) {
5896
+ const typeName = current.def.type;
5586
5897
  if (typeName === "optional" || typeName === "default") {
5587
5898
  optional = true;
5588
- current = current._def.innerType ?? current;
5899
+ current = current.def.innerType ?? current;
5589
5900
  continue;
5590
5901
  }
5591
5902
  if (typeName === "nullable" || typeName === "catch" || typeName === "readonly") {
5592
- current = current._def.innerType ?? current;
5903
+ current = current.def.innerType ?? current;
5593
5904
  continue;
5594
5905
  }
5595
5906
  if (typeName === "pipe") {
5596
- current = current._def.in ?? current;
5907
+ current = current.def.in ?? current;
5597
5908
  continue;
5598
5909
  }
5599
5910
  break;