@ai-sdk-tool/parser 3.3.1 → 3.3.3

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
@@ -908,6 +908,13 @@ function generateId() {
908
908
  return Math.random().toString(36).substring(2, 15);
909
909
  }
910
910
 
911
+ // src/core/utils/protocol-utils.ts
912
+ function addTextSegment(text, processedElements) {
913
+ if (text.trim()) {
914
+ processedElements.push({ type: "text", text });
915
+ }
916
+ }
917
+
911
918
  // src/core/utils/regex.ts
912
919
  function escapeRegExp(literal) {
913
920
  return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -939,11 +946,6 @@ function processToolCallJson(toolCallJson, fullMatch, processedElements, options
939
946
  processedElements.push({ type: "text", text: fullMatch });
940
947
  }
941
948
  }
942
- function addTextSegment(text, processedElements) {
943
- if (text.trim()) {
944
- processedElements.push({ type: "text", text });
945
- }
946
- }
947
949
  function processMatchedToolCall(context) {
948
950
  const { match, text, currentIndex, processedElements, options } = context;
949
951
  const startIndex = match.index;
@@ -1575,6 +1577,13 @@ var EMPTY_OBJECT_REGEX = /^\{\s*\}$/s;
1575
1577
  var NEWLINE_SPLIT_REGEX = /\n+/;
1576
1578
  var COMMA_SPLIT_REGEX = /,\s*/;
1577
1579
  var DIGIT_KEY_REGEX = /^\d+$/;
1580
+ var WHITESPACE_REGEX2 = /\s+/g;
1581
+ var HAS_WHITESPACE_REGEX = /\s/;
1582
+ var SINGLE_QUOTE = "'";
1583
+ var DOUBLE_QUOTE = '"';
1584
+ var SNAKE_SEGMENT_REGEX = /_([a-zA-Z0-9])/g;
1585
+ var CAMEL_BOUNDARY_REGEX = /([a-z0-9])([A-Z])/g;
1586
+ var LEADING_UNDERSCORES_REGEX = /^_+/;
1578
1587
  function unwrapJsonSchema(schema) {
1579
1588
  if (!schema || typeof schema !== "object") {
1580
1589
  return schema;
@@ -1823,9 +1832,158 @@ function coerceStringToArray(s, unwrapped) {
1823
1832
  }
1824
1833
  return null;
1825
1834
  }
1835
+ function getStrictObjectSchemaInfo(unwrapped) {
1836
+ if (getSchemaType(unwrapped) !== "object") {
1837
+ return null;
1838
+ }
1839
+ if (unwrapped.additionalProperties !== false) {
1840
+ return null;
1841
+ }
1842
+ const properties = unwrapped.properties;
1843
+ if (!properties || typeof properties !== "object" || Array.isArray(properties)) {
1844
+ return null;
1845
+ }
1846
+ const propertyMap = properties;
1847
+ const required = Array.isArray(unwrapped.required) ? unwrapped.required.filter(
1848
+ (value) => typeof value === "string" && value.length > 0
1849
+ ) : [];
1850
+ const patternProps = unwrapped.patternProperties;
1851
+ const patternProperties = patternProps && typeof patternProps === "object" && !Array.isArray(patternProps) ? patternProps : void 0;
1852
+ return {
1853
+ properties: propertyMap,
1854
+ required,
1855
+ patternProperties
1856
+ };
1857
+ }
1858
+ function isSingularPluralPair(left, right) {
1859
+ return left.length > 1 && right.length > 1 && (left === `${right}s` || right === `${left}s`);
1860
+ }
1861
+ function snakeToCamel(value) {
1862
+ const trimmed = value.replace(LEADING_UNDERSCORES_REGEX, "");
1863
+ if (trimmed.length === 0) {
1864
+ return value;
1865
+ }
1866
+ const camelized = trimmed.replace(
1867
+ SNAKE_SEGMENT_REGEX,
1868
+ (_, c) => c.toUpperCase()
1869
+ );
1870
+ return camelized.charAt(0).toLowerCase() + camelized.slice(1);
1871
+ }
1872
+ function camelToSnake(value) {
1873
+ return value.replace(CAMEL_BOUNDARY_REGEX, "$1_$2").toLowerCase();
1874
+ }
1875
+ function isCaseStylePair(targetKey, sourceKey) {
1876
+ if (targetKey === sourceKey) {
1877
+ return false;
1878
+ }
1879
+ const sourceLooksSnake = sourceKey.includes("_");
1880
+ const targetLooksSnake = targetKey.includes("_");
1881
+ if (sourceLooksSnake && snakeToCamel(sourceKey) === targetKey) {
1882
+ return true;
1883
+ }
1884
+ if (!sourceLooksSnake && targetLooksSnake && camelToSnake(sourceKey) === targetKey) {
1885
+ return true;
1886
+ }
1887
+ return false;
1888
+ }
1889
+ function isUnexpectedKey(key, schemaInfo) {
1890
+ if (Object.hasOwn(schemaInfo.properties, key)) {
1891
+ return false;
1892
+ }
1893
+ const patternSchemas = getPatternSchemasForKey(
1894
+ schemaInfo.patternProperties,
1895
+ key
1896
+ );
1897
+ if (patternSchemas.length > 0) {
1898
+ return patternSchemas.every((schema) => schema === false);
1899
+ }
1900
+ return true;
1901
+ }
1902
+ function computeMissingAndUnexpectedKeys(input, schemaInfo) {
1903
+ const missingRequired = schemaInfo.required.filter(
1904
+ (key) => !Object.hasOwn(input, key)
1905
+ );
1906
+ const unexpectedKeys = Object.keys(input).filter(
1907
+ (key) => isUnexpectedKey(key, schemaInfo)
1908
+ );
1909
+ return { missingRequired, unexpectedKeys };
1910
+ }
1911
+ function applySingularPluralRequiredKeyRename(input, schemaInfo) {
1912
+ const { missingRequired, unexpectedKeys } = computeMissingAndUnexpectedKeys(
1913
+ input,
1914
+ schemaInfo
1915
+ );
1916
+ if (missingRequired.length !== 1 || unexpectedKeys.length !== 1) {
1917
+ return null;
1918
+ }
1919
+ const targetKey = missingRequired[0];
1920
+ const sourceKey = unexpectedKeys[0];
1921
+ if (!Object.hasOwn(schemaInfo.properties, targetKey)) {
1922
+ return null;
1923
+ }
1924
+ if (!isSingularPluralPair(targetKey, sourceKey)) {
1925
+ return null;
1926
+ }
1927
+ if (getSchemaType(schemaInfo.properties[targetKey]) !== "array") {
1928
+ return null;
1929
+ }
1930
+ if (!Array.isArray(input[sourceKey])) {
1931
+ return null;
1932
+ }
1933
+ if (!Object.hasOwn(input, sourceKey) || Object.hasOwn(input, targetKey)) {
1934
+ return null;
1935
+ }
1936
+ const output = { ...input };
1937
+ output[targetKey] = output[sourceKey];
1938
+ delete output[sourceKey];
1939
+ return output;
1940
+ }
1941
+ function applyCaseStyleRequiredKeyRename(input, schemaInfo) {
1942
+ const { missingRequired, unexpectedKeys } = computeMissingAndUnexpectedKeys(
1943
+ input,
1944
+ schemaInfo
1945
+ );
1946
+ if (missingRequired.length !== 1 || unexpectedKeys.length !== 1) {
1947
+ return null;
1948
+ }
1949
+ const targetKey = missingRequired[0];
1950
+ const sourceKey = unexpectedKeys[0];
1951
+ if (!Object.hasOwn(schemaInfo.properties, targetKey)) {
1952
+ return null;
1953
+ }
1954
+ if (!isCaseStylePair(targetKey, sourceKey)) {
1955
+ return null;
1956
+ }
1957
+ if (!Object.hasOwn(input, sourceKey) || Object.hasOwn(input, targetKey)) {
1958
+ return null;
1959
+ }
1960
+ const output = { ...input };
1961
+ output[targetKey] = output[sourceKey];
1962
+ delete output[sourceKey];
1963
+ return output;
1964
+ }
1965
+ function applyStrictRequiredKeyRename(input, unwrapped) {
1966
+ const schemaInfo = getStrictObjectSchemaInfo(unwrapped);
1967
+ if (!schemaInfo) {
1968
+ return input;
1969
+ }
1970
+ const singularPlural = applySingularPluralRequiredKeyRename(
1971
+ input,
1972
+ schemaInfo
1973
+ );
1974
+ if (singularPlural) {
1975
+ return singularPlural;
1976
+ }
1977
+ const caseStyle = applyCaseStyleRequiredKeyRename(input, schemaInfo);
1978
+ if (caseStyle) {
1979
+ return caseStyle;
1980
+ }
1981
+ return input;
1982
+ }
1826
1983
  function coerceObjectToObject(value, unwrapped) {
1984
+ const normalizedInput = applyStrictRequiredKeyRename(value, unwrapped);
1827
1985
  const out = {};
1828
- for (const [k, v] of Object.entries(value)) {
1986
+ for (const [k, v] of Object.entries(normalizedInput)) {
1829
1987
  out[k] = coerceValueForKey(v, k, unwrapped);
1830
1988
  }
1831
1989
  return out;
@@ -1836,6 +1994,109 @@ function coerceArrayToArray(value, prefixItems, itemsSchema) {
1836
1994
  }
1837
1995
  return value.map((v) => coerceBySchema(v, itemsSchema));
1838
1996
  }
1997
+ function isPrimitiveSchemaType(schemaType) {
1998
+ return schemaType === "string" || schemaType === "number" || schemaType === "integer" || schemaType === "boolean";
1999
+ }
2000
+ function isPrimitiveMatchForSchemaType(value, schemaType) {
2001
+ if (schemaType === "string") {
2002
+ return typeof value === "string";
2003
+ }
2004
+ if (schemaType === "number") {
2005
+ return typeof value === "number" && Number.isFinite(value);
2006
+ }
2007
+ if (schemaType === "integer") {
2008
+ return typeof value === "number" && Number.isFinite(value) && Number.isInteger(value);
2009
+ }
2010
+ return typeof value === "boolean";
2011
+ }
2012
+ function coercePrimitiveWrappedObject(value, itemsSchema) {
2013
+ const schemaType = getSchemaType(itemsSchema);
2014
+ if (!isPrimitiveSchemaType(schemaType)) {
2015
+ return null;
2016
+ }
2017
+ const keys = Object.keys(value);
2018
+ if (keys.length !== 1) {
2019
+ return null;
2020
+ }
2021
+ const singleValue = value[keys[0]];
2022
+ if (singleValue && typeof singleValue === "object") {
2023
+ return null;
2024
+ }
2025
+ const coerced = coerceBySchema(singleValue, itemsSchema);
2026
+ return isPrimitiveMatchForSchemaType(coerced, schemaType) ? coerced : null;
2027
+ }
2028
+ function coerceParallelArraysObjectToArray(maybe, prefixItems, itemsSchema) {
2029
+ if (prefixItems && prefixItems.length > 0) {
2030
+ return null;
2031
+ }
2032
+ const unwrappedItems = unwrapJsonSchema(itemsSchema);
2033
+ if (!unwrappedItems || typeof unwrappedItems !== "object" || Array.isArray(unwrappedItems)) {
2034
+ return null;
2035
+ }
2036
+ const itemSchema = unwrappedItems;
2037
+ if (getSchemaType(itemSchema) !== "object") {
2038
+ return null;
2039
+ }
2040
+ if (itemSchema.additionalProperties !== false) {
2041
+ return null;
2042
+ }
2043
+ const properties = itemSchema.properties;
2044
+ if (!properties || typeof properties !== "object" || Array.isArray(properties)) {
2045
+ return null;
2046
+ }
2047
+ const propertyMap = properties;
2048
+ const entries = Object.entries(maybe);
2049
+ if (entries.length < 2) {
2050
+ return null;
2051
+ }
2052
+ if (!entries.every(([, value]) => Array.isArray(value))) {
2053
+ return null;
2054
+ }
2055
+ if (!entries.every(([key]) => Object.hasOwn(propertyMap, key))) {
2056
+ return null;
2057
+ }
2058
+ if (!entries.every(([key]) => {
2059
+ const schemaType = getSchemaType(propertyMap[key]);
2060
+ return schemaType !== "array" && schemaType !== "object";
2061
+ })) {
2062
+ return null;
2063
+ }
2064
+ const lengths = [
2065
+ ...new Set(entries.map(([, value]) => value.length))
2066
+ ];
2067
+ if (lengths.length !== 1) {
2068
+ return null;
2069
+ }
2070
+ const length = lengths[0];
2071
+ if (length < 2) {
2072
+ return null;
2073
+ }
2074
+ const zipped = [];
2075
+ for (let index = 0; index < length; index += 1) {
2076
+ const item = {};
2077
+ for (const [key, value] of entries) {
2078
+ item[key] = value[index];
2079
+ }
2080
+ zipped.push(item);
2081
+ }
2082
+ return coerceArrayToArray(zipped, prefixItems, itemsSchema);
2083
+ }
2084
+ function coerceSingleKeyObjectToArray(singleValue, itemsSchema) {
2085
+ if (Array.isArray(singleValue)) {
2086
+ return singleValue.map((v) => coerceBySchema(v, itemsSchema));
2087
+ }
2088
+ if (singleValue && typeof singleValue === "object") {
2089
+ const primitiveWrapped = coercePrimitiveWrappedObject(
2090
+ singleValue,
2091
+ itemsSchema
2092
+ );
2093
+ if (primitiveWrapped !== null) {
2094
+ return [primitiveWrapped];
2095
+ }
2096
+ return [coerceBySchema(singleValue, itemsSchema)];
2097
+ }
2098
+ return null;
2099
+ }
1839
2100
  function coerceObjectToArray(maybe, prefixItems, itemsSchema) {
1840
2101
  if (Object.hasOwn(maybe, "item")) {
1841
2102
  const items = maybe.item;
@@ -1847,15 +2108,23 @@ function coerceObjectToArray(maybe, prefixItems, itemsSchema) {
1847
2108
  const arr = keys.sort((a, b) => Number(a) - Number(b)).map((k) => maybe[k]);
1848
2109
  return coerceArrayToArray(arr, prefixItems, itemsSchema);
1849
2110
  }
2111
+ const parallelArrays = coerceParallelArraysObjectToArray(
2112
+ maybe,
2113
+ prefixItems,
2114
+ itemsSchema
2115
+ );
2116
+ if (parallelArrays !== null) {
2117
+ return parallelArrays;
2118
+ }
1850
2119
  if (keys.length === 1) {
1851
2120
  const singleKey = keys[0];
1852
2121
  if (!(schemaIsUnconstrained(itemsSchema) || schemaHasProperty(itemsSchema, singleKey))) {
1853
- const singleValue = maybe[singleKey];
1854
- if (Array.isArray(singleValue)) {
1855
- return singleValue.map((v) => coerceBySchema(v, itemsSchema));
1856
- }
1857
- if (singleValue && typeof singleValue === "object") {
1858
- return [coerceBySchema(singleValue, itemsSchema)];
2122
+ const result = coerceSingleKeyObjectToArray(
2123
+ maybe[singleKey],
2124
+ itemsSchema
2125
+ );
2126
+ if (result !== null) {
2127
+ return result;
1859
2128
  }
1860
2129
  }
1861
2130
  }
@@ -1885,6 +2154,86 @@ function coerceStringToPrimitive(s, schemaType) {
1885
2154
  }
1886
2155
  return null;
1887
2156
  }
2157
+ function coercePrimitiveToString(value, schemaType) {
2158
+ if (schemaType !== "string") {
2159
+ return null;
2160
+ }
2161
+ if (typeof value === "boolean") {
2162
+ return value ? "true" : "false";
2163
+ }
2164
+ if (typeof value === "number" && Number.isFinite(value)) {
2165
+ return String(value);
2166
+ }
2167
+ return null;
2168
+ }
2169
+ function coerceStringByEnumWhitespace(rawValue, unwrapped) {
2170
+ const enumValues = unwrapped.enum;
2171
+ if (!Array.isArray(enumValues) || enumValues.length === 0) {
2172
+ return null;
2173
+ }
2174
+ if (!enumValues.every((item) => typeof item === "string")) {
2175
+ return null;
2176
+ }
2177
+ const normalizedEnumValues = enumValues;
2178
+ if (normalizedEnumValues.includes(rawValue)) {
2179
+ return null;
2180
+ }
2181
+ const unquoted = unwrapMatchingQuotes(rawValue);
2182
+ if (unquoted !== null) {
2183
+ const exactMatches = normalizedEnumValues.filter(
2184
+ (item) => item === unquoted
2185
+ );
2186
+ if (exactMatches.length === 1) {
2187
+ return exactMatches[0];
2188
+ }
2189
+ }
2190
+ const candidates = [rawValue, unquoted].filter(
2191
+ (item) => item !== null
2192
+ );
2193
+ for (const candidate of candidates) {
2194
+ if (!HAS_WHITESPACE_REGEX.test(candidate)) {
2195
+ continue;
2196
+ }
2197
+ const normalizedInput = candidate.replace(WHITESPACE_REGEX2, "");
2198
+ const matches = normalizedEnumValues.filter(
2199
+ (item) => item.replace(WHITESPACE_REGEX2, "") === normalizedInput
2200
+ );
2201
+ if (matches.length === 1) {
2202
+ return matches[0];
2203
+ }
2204
+ }
2205
+ return null;
2206
+ }
2207
+ function unwrapMatchingQuotes(value) {
2208
+ if (value.length < 2) {
2209
+ return null;
2210
+ }
2211
+ const first = value[0];
2212
+ const last = value.at(-1);
2213
+ const isQuote = (first === SINGLE_QUOTE || first === DOUBLE_QUOTE) && first === last;
2214
+ if (!isQuote) {
2215
+ return null;
2216
+ }
2217
+ return value.slice(1, -1);
2218
+ }
2219
+ function coerceObjectToPrimitive(value, schemaType, fullSchema) {
2220
+ if (!isPrimitiveSchemaType(schemaType)) {
2221
+ return null;
2222
+ }
2223
+ const keys = Object.keys(value);
2224
+ if (keys.length !== 1) {
2225
+ return null;
2226
+ }
2227
+ const singleValue = value[keys[0]];
2228
+ if (singleValue && typeof singleValue === "object") {
2229
+ return null;
2230
+ }
2231
+ const coerced = coerceBySchema(
2232
+ singleValue,
2233
+ fullSchema != null ? fullSchema : { type: schemaType }
2234
+ );
2235
+ return isPrimitiveMatchForSchemaType(coerced, schemaType) ? coerced : null;
2236
+ }
1888
2237
  function coerceStringValue(value, schemaType, u) {
1889
2238
  const s = value.trim();
1890
2239
  if (schemaType === "object") {
@@ -1903,6 +2252,10 @@ function coerceStringValue(value, schemaType, u) {
1903
2252
  if (primitiveResult !== null) {
1904
2253
  return primitiveResult;
1905
2254
  }
2255
+ const enumWhitespaceCanonical = coerceStringByEnumWhitespace(s, u);
2256
+ if (enumWhitespaceCanonical !== null) {
2257
+ return enumWhitespaceCanonical;
2258
+ }
1906
2259
  return value;
1907
2260
  }
1908
2261
  function coerceArrayValue(value, prefixItems, itemsSchema) {
@@ -1941,9 +2294,23 @@ function coerceBySchema(value, schema) {
1941
2294
  if (typeof value === "string") {
1942
2295
  return coerceStringValue(value, schemaType, u);
1943
2296
  }
2297
+ const primitiveString = coercePrimitiveToString(value, schemaType);
2298
+ if (primitiveString !== null) {
2299
+ return primitiveString;
2300
+ }
1944
2301
  if (schemaType === "object" && value && typeof value === "object" && !Array.isArray(value)) {
1945
2302
  return coerceObjectToObject(value, u);
1946
2303
  }
2304
+ if (value && typeof value === "object" && !Array.isArray(value) && isPrimitiveSchemaType(schemaType)) {
2305
+ const primitiveResult = coerceObjectToPrimitive(
2306
+ value,
2307
+ schemaType,
2308
+ u
2309
+ );
2310
+ if (primitiveResult !== null) {
2311
+ return primitiveResult;
2312
+ }
2313
+ }
1947
2314
  if (schemaType === "array") {
1948
2315
  const prefixItems = Array.isArray(u.prefixItems) ? u.prefixItems : void 0;
1949
2316
  const itemsSchema = u.items;
@@ -2958,7 +3325,7 @@ var XMLTokenizer = class {
2958
3325
  };
2959
3326
 
2960
3327
  // src/rxml/core/parser.ts
2961
- var WHITESPACE_REGEX2 = /\s/;
3328
+ var WHITESPACE_REGEX3 = /\s/;
2962
3329
  var NUMERIC_STRING_REGEX = /^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
2963
3330
  var DIGIT_KEY_REGEX2 = /^\d+$/;
2964
3331
  function getTopLevelStringProps(s) {
@@ -3128,7 +3495,7 @@ function parse2(xmlInner, schema, options = {}) {
3128
3495
  const closeHead = s.indexOf(`</${rootName}`, range.end);
3129
3496
  if (closeHead === range.end) {
3130
3497
  let p = closeHead + 2 + rootName.length;
3131
- while (p < s.length && WHITESPACE_REGEX2.test(s[p])) {
3498
+ while (p < s.length && WHITESPACE_REGEX3.test(s[p])) {
3132
3499
  p += 1;
3133
3500
  }
3134
3501
  if (s[p] === ">") {
@@ -3466,7 +3833,7 @@ function createIntermediateCall(toolName, rawSegment, schema) {
3466
3833
  var MALFORMED_CLOSE_RE_G = /<\/\s+([A-Za-z0-9_:-]+)\s*>/g;
3467
3834
  var MALFORMED_CLOSE_RE = /<\/\s+([A-Za-z0-9_:-]+)\s*>/;
3468
3835
  var STATUS_TO_STEP_BOUNDARY_RE = /<\/status>\s*<step>/g;
3469
- var WHITESPACE_REGEX3 = /\s/;
3836
+ var WHITESPACE_REGEX4 = /\s/;
3470
3837
  var NAME_CHAR_RE = /[A-Za-z0-9_:-]/;
3471
3838
  var NAME_START_CHAR_RE = /[A-Za-z_:]/;
3472
3839
  var STEP_TAG_RE = /<step>([\s\S]*?)<\/step>/i;
@@ -3607,7 +3974,7 @@ function balanceTags(xml) {
3607
3974
  }
3608
3975
  function skipWs(s, p, len) {
3609
3976
  let idx = p;
3610
- while (idx < len && WHITESPACE_REGEX3.test(s[idx])) {
3977
+ while (idx < len && WHITESPACE_REGEX4.test(s[idx])) {
3611
3978
  idx += 1;
3612
3979
  }
3613
3980
  return idx;
@@ -3660,7 +4027,7 @@ function handleOpeningTagSegment(src, lt, out, stack) {
3660
4027
  return len;
3661
4028
  }
3662
4029
  let r = q - 1;
3663
- while (r >= nameStart && WHITESPACE_REGEX3.test(src[r])) {
4030
+ while (r >= nameStart && WHITESPACE_REGEX4.test(src[r])) {
3664
4031
  r -= 1;
3665
4032
  }
3666
4033
  const selfClosing = src[r] === "/";
@@ -3703,12 +4070,9 @@ function getStringPropertyNames(schema) {
3703
4070
  }
3704
4071
  return names;
3705
4072
  }
3706
- function escapeRegExp2(s) {
3707
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3708
- }
3709
4073
  function dedupeSingleTag(xml, key) {
3710
4074
  var _a, _b;
3711
- const escaped = escapeRegExp2(key);
4075
+ const escaped = escapeRegExp(key);
3712
4076
  const re = new RegExp(`<${escaped}>([\\s\\S]*?)<\\/${escaped}>`, "g");
3713
4077
  const matches = Array.from(xml.matchAll(re));
3714
4078
  if (matches.length <= 1) {
@@ -3825,9 +4189,35 @@ function parse3(xml, schema, options = {}) {
3825
4189
  throw new RXMLParseError("Failed to parse XML with repair heuristics", error);
3826
4190
  }
3827
4191
 
3828
- // src/core/protocols/xml-protocol.ts
4192
+ // src/core/utils/regex-constants.ts
3829
4193
  var NAME_CHAR_RE2 = /[A-Za-z0-9_:-]/;
3830
- var WHITESPACE_REGEX4 = /\s/;
4194
+ var WHITESPACE_REGEX5 = /\s/;
4195
+
4196
+ // src/core/utils/xml-root-repair.ts
4197
+ var XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX = /^<([A-Za-z_][A-Za-z0-9_-]*)\s*\r?\n([\s\S]+?)\r?\n\s*\/>\s*$/;
4198
+ function tryRepairXmlSelfClosingRootWithBody(rawText, toolNames) {
4199
+ const trimmed = rawText.trim();
4200
+ if (trimmed.length === 0) {
4201
+ return null;
4202
+ }
4203
+ const match = trimmed.match(XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX);
4204
+ if (!match) {
4205
+ return null;
4206
+ }
4207
+ const rootTag = match[1];
4208
+ if (!toolNames.includes(rootTag)) {
4209
+ return null;
4210
+ }
4211
+ const body = match[2].trimEnd();
4212
+ if (body.trim().length === 0 || body.includes(`</${rootTag}>`)) {
4213
+ return null;
4214
+ }
4215
+ return `<${rootTag}>
4216
+ ${body}
4217
+ </${rootTag}>`;
4218
+ }
4219
+
4220
+ // src/core/protocols/xml-protocol.ts
3831
4221
  function getToolSchema(tools, toolName) {
3832
4222
  var _a;
3833
4223
  return (_a = tools.find((t) => t.name === toolName)) == null ? void 0 : _a.inputSchema;
@@ -3929,7 +4319,7 @@ function consumeClosingTag(text, lt) {
3929
4319
  }
3930
4320
  function consumeOpenTag(text, lt) {
3931
4321
  let p = lt + 1;
3932
- while (p < text.length && WHITESPACE_REGEX4.test(text[p])) {
4322
+ while (p < text.length && WHITESPACE_REGEX5.test(text[p])) {
3933
4323
  p += 1;
3934
4324
  }
3935
4325
  const nameStart = p;
@@ -3942,7 +4332,7 @@ function consumeOpenTag(text, lt) {
3942
4332
  return null;
3943
4333
  }
3944
4334
  let r = q - 1;
3945
- while (r >= nameStart && WHITESPACE_REGEX4.test(text[r])) {
4335
+ while (r >= nameStart && WHITESPACE_REGEX5.test(text[r])) {
3946
4336
  r -= 1;
3947
4337
  }
3948
4338
  const selfClosing = text[r] === "/";
@@ -3971,7 +4361,7 @@ function nextTagToken(text, fromPos) {
3971
4361
  if (next === "/") {
3972
4362
  const closing = consumeClosingTag(text, lt);
3973
4363
  let p = lt + 2;
3974
- while (p < text.length && WHITESPACE_REGEX4.test(text[p])) {
4364
+ while (p < text.length && WHITESPACE_REGEX5.test(text[p])) {
3975
4365
  p += 1;
3976
4366
  }
3977
4367
  const nameStart = p;
@@ -4108,6 +4498,102 @@ function findToolCalls(text, toolNames) {
4108
4498
  }
4109
4499
  return toolCalls.sort((a, b) => a.startIndex - b.startIndex);
4110
4500
  }
4501
+ function handleSpecialToken(depth) {
4502
+ return { depth, lastCompleteEnd: -1, shouldBreak: false };
4503
+ }
4504
+ function handleOpenToken(token, depth, lastCompleteEnd) {
4505
+ if (token.selfClosing) {
4506
+ return {
4507
+ depth,
4508
+ lastCompleteEnd: depth === 0 ? token.nextPos : lastCompleteEnd,
4509
+ shouldBreak: false
4510
+ };
4511
+ }
4512
+ return { depth: depth + 1, lastCompleteEnd, shouldBreak: false };
4513
+ }
4514
+ function handleCloseToken(token, depth) {
4515
+ if (depth <= 0) {
4516
+ return { depth, lastCompleteEnd: -1, shouldBreak: true };
4517
+ }
4518
+ const newDepth = depth - 1;
4519
+ return {
4520
+ depth: newDepth,
4521
+ lastCompleteEnd: newDepth === 0 ? token.nextPos : -1,
4522
+ shouldBreak: false
4523
+ };
4524
+ }
4525
+ function findLinePrefixedXmlBodyEnd(text, bodyStartIndex) {
4526
+ let cursor = bodyStartIndex;
4527
+ let depth = 0;
4528
+ let lastCompleteEnd = -1;
4529
+ while (cursor < text.length) {
4530
+ if (depth === 0) {
4531
+ cursor = consumeWhitespace(text, cursor);
4532
+ if (cursor >= text.length || text.charAt(cursor) !== "<") {
4533
+ break;
4534
+ }
4535
+ }
4536
+ const token = nextTagToken(text, cursor);
4537
+ if (token.kind === "eof") {
4538
+ break;
4539
+ }
4540
+ let result;
4541
+ if (token.kind === "special") {
4542
+ result = handleSpecialToken(depth);
4543
+ } else if (token.kind === "open") {
4544
+ result = handleOpenToken(token, depth, lastCompleteEnd);
4545
+ } else {
4546
+ result = handleCloseToken(token, depth);
4547
+ }
4548
+ depth = result.depth;
4549
+ if (result.lastCompleteEnd !== -1) {
4550
+ lastCompleteEnd = result.lastCompleteEnd;
4551
+ }
4552
+ if (result.shouldBreak) {
4553
+ break;
4554
+ }
4555
+ cursor = token.nextPos;
4556
+ }
4557
+ return lastCompleteEnd;
4558
+ }
4559
+ function findLinePrefixedToolCall(text, toolNames) {
4560
+ var _a;
4561
+ let best = null;
4562
+ for (const toolName of toolNames) {
4563
+ const linePattern = new RegExp(
4564
+ `(^|\\n)[\\t ]*${escapeRegExp(toolName)}[\\t ]*:?[\\t ]*(?:\\r?\\n|$)`,
4565
+ "g"
4566
+ );
4567
+ let match = linePattern.exec(text);
4568
+ while (match !== null) {
4569
+ const prefix = (_a = match[1]) != null ? _a : "";
4570
+ const startIndex = match.index + prefix.length;
4571
+ const contentStart = consumeWhitespace(text, linePattern.lastIndex);
4572
+ if (contentStart >= text.length || text.charAt(contentStart) !== "<") {
4573
+ match = linePattern.exec(text);
4574
+ continue;
4575
+ }
4576
+ const contentEnd = findLinePrefixedXmlBodyEnd(text, contentStart);
4577
+ if (contentEnd === -1 || contentEnd <= contentStart) {
4578
+ match = linePattern.exec(text);
4579
+ continue;
4580
+ }
4581
+ const content = text.slice(contentStart, contentEnd);
4582
+ const candidate = {
4583
+ toolName,
4584
+ startIndex,
4585
+ endIndex: contentEnd,
4586
+ content,
4587
+ segment: text.slice(startIndex, contentEnd)
4588
+ };
4589
+ if (best === null || candidate.startIndex < best.startIndex) {
4590
+ best = candidate;
4591
+ }
4592
+ break;
4593
+ }
4594
+ }
4595
+ return best;
4596
+ }
4111
4597
  function findEarliestToolTag(buffer, toolNames) {
4112
4598
  var _a, _b;
4113
4599
  let bestIndex = -1;
@@ -4146,7 +4632,7 @@ function isOpenTagPrefix(suffix, toolName) {
4146
4632
  }
4147
4633
  function consumeWhitespace(text, index) {
4148
4634
  let i = index;
4149
- while (i < text.length && WHITESPACE_REGEX4.test(text.charAt(i))) {
4635
+ while (i < text.length && WHITESPACE_REGEX5.test(text.charAt(i))) {
4150
4636
  i += 1;
4151
4637
  }
4152
4638
  return i;
@@ -4397,6 +4883,27 @@ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, se
4397
4883
  }
4398
4884
  };
4399
4885
  }
4886
+ function findToolCallsWithFallbacks(text, toolNames) {
4887
+ let parseText = text;
4888
+ let toolCalls = findToolCalls(parseText, toolNames);
4889
+ if (toolCalls.length === 0) {
4890
+ const fallbackToolCall = findLinePrefixedToolCall(parseText, toolNames);
4891
+ if (fallbackToolCall !== null) {
4892
+ toolCalls.push(fallbackToolCall);
4893
+ }
4894
+ }
4895
+ if (toolCalls.length === 0) {
4896
+ const repaired = tryRepairXmlSelfClosingRootWithBody(parseText, toolNames);
4897
+ if (repaired) {
4898
+ const repairedCalls = findToolCalls(repaired, toolNames);
4899
+ if (repairedCalls.length > 0) {
4900
+ parseText = repaired;
4901
+ toolCalls = repairedCalls;
4902
+ }
4903
+ }
4904
+ }
4905
+ return { parseText, toolCalls };
4906
+ }
4400
4907
  var xmlProtocol = (protocolOptions) => {
4401
4908
  var _a;
4402
4909
  const parseOptions = {
@@ -4430,28 +4937,31 @@ var xmlProtocol = (protocolOptions) => {
4430
4937
  }
4431
4938
  const processedElements = [];
4432
4939
  let currentIndex = 0;
4433
- const toolCalls = findToolCalls(text, toolNames);
4940
+ const { parseText, toolCalls } = findToolCallsWithFallbacks(
4941
+ text,
4942
+ toolNames
4943
+ );
4434
4944
  for (const tc of toolCalls) {
4435
4945
  if (tc.startIndex > currentIndex) {
4436
4946
  processedElements.push({
4437
4947
  type: "text",
4438
- text: text.substring(currentIndex, tc.startIndex)
4948
+ text: parseText.substring(currentIndex, tc.startIndex)
4439
4949
  });
4440
4950
  }
4441
4951
  processToolCall({
4442
4952
  toolCall: tc,
4443
4953
  tools,
4444
4954
  options,
4445
- text,
4955
+ text: parseText,
4446
4956
  processedElements,
4447
4957
  parseOptions
4448
4958
  });
4449
4959
  currentIndex = tc.endIndex;
4450
4960
  }
4451
- if (currentIndex < text.length) {
4961
+ if (currentIndex < parseText.length) {
4452
4962
  processedElements.push({
4453
4963
  type: "text",
4454
- text: text.substring(currentIndex)
4964
+ text: parseText.substring(currentIndex)
4455
4965
  });
4456
4966
  }
4457
4967
  return processedElements;
@@ -4490,6 +5000,20 @@ var xmlProtocol = (protocolOptions) => {
4490
5000
  return new TransformStream({
4491
5001
  transform(chunk, controller) {
4492
5002
  var _a2;
5003
+ if (chunk.type === "finish") {
5004
+ if (currentToolCall) {
5005
+ const unfinishedContent = `<${currentToolCall.name}>${currentToolCall.content}${buffer}`;
5006
+ flushText(controller, unfinishedContent);
5007
+ buffer = "";
5008
+ currentToolCall = null;
5009
+ } else if (buffer) {
5010
+ flushText(controller, buffer);
5011
+ buffer = "";
5012
+ }
5013
+ flushText(controller);
5014
+ controller.enqueue(chunk);
5015
+ return;
5016
+ }
4493
5017
  if (chunk.type !== "text-delta") {
4494
5018
  if (buffer) {
4495
5019
  flushText(controller, buffer);
@@ -4535,8 +5059,6 @@ var xmlProtocol = (protocolOptions) => {
4535
5059
 
4536
5060
  // src/core/protocols/yaml-protocol.ts
4537
5061
  var import_yaml = __toESM(require("yaml"), 1);
4538
- var NAME_CHAR_RE3 = /[A-Za-z0-9_:-]/;
4539
- var WHITESPACE_REGEX5 = /\s/;
4540
5062
  var LEADING_WHITESPACE_RE = /^(\s*)/;
4541
5063
  function findClosingTagEnd(text, contentStart, toolName) {
4542
5064
  let pos = contentStart;
@@ -4557,7 +5079,7 @@ function findClosingTagEnd(text, contentStart, toolName) {
4557
5079
  p++;
4558
5080
  }
4559
5081
  const nameStart = p;
4560
- while (p < gtIdx && NAME_CHAR_RE3.test(text.charAt(p))) {
5082
+ while (p < gtIdx && NAME_CHAR_RE2.test(text.charAt(p))) {
4561
5083
  p++;
4562
5084
  }
4563
5085
  const name = text.slice(nameStart, p);
@@ -4577,7 +5099,7 @@ function findClosingTagEnd(text, contentStart, toolName) {
4577
5099
  p++;
4578
5100
  }
4579
5101
  const nameStart = p;
4580
- while (p < text.length && NAME_CHAR_RE3.test(text.charAt(p))) {
5102
+ while (p < text.length && NAME_CHAR_RE2.test(text.charAt(p))) {
4581
5103
  p++;
4582
5104
  }
4583
5105
  const name = text.slice(nameStart, p);
@@ -4704,22 +5226,14 @@ function parseYamlContent(yamlContent, options) {
4704
5226
  return null;
4705
5227
  }
4706
5228
  }
4707
- function appendTextPart(processedElements, textPart) {
4708
- if (textPart.trim()) {
4709
- processedElements.push({
4710
- type: "text",
4711
- text: textPart
4712
- });
4713
- }
4714
- }
4715
5229
  function processToolCallMatch(text, tc, currentIndex, processedElements, options) {
4716
5230
  var _a;
4717
5231
  if (tc.startIndex < currentIndex) {
4718
5232
  return currentIndex;
4719
5233
  }
4720
- appendTextPart(
4721
- processedElements,
4722
- text.substring(currentIndex, tc.startIndex)
5234
+ addTextSegment(
5235
+ text.substring(currentIndex, tc.startIndex),
5236
+ processedElements
4723
5237
  );
4724
5238
  const parsedArgs = parseYamlContent(tc.content, options);
4725
5239
  if (parsedArgs !== null) {
@@ -4826,18 +5340,32 @@ ${yamlContent}</${toolCall.toolName}>`;
4826
5340
  }
4827
5341
  const processedElements = [];
4828
5342
  let currentIndex = 0;
4829
- const toolCalls = findToolCalls2(text, toolNames);
5343
+ let parseText = text;
5344
+ let toolCalls = findToolCalls2(parseText, toolNames);
5345
+ if (toolCalls.length === 0) {
5346
+ const repaired = tryRepairXmlSelfClosingRootWithBody(
5347
+ parseText,
5348
+ toolNames
5349
+ );
5350
+ if (repaired) {
5351
+ const repairedCalls = findToolCalls2(repaired, toolNames);
5352
+ if (repairedCalls.length > 0) {
5353
+ parseText = repaired;
5354
+ toolCalls = repairedCalls;
5355
+ }
5356
+ }
5357
+ }
4830
5358
  for (const tc of toolCalls) {
4831
5359
  currentIndex = processToolCallMatch(
4832
- text,
5360
+ parseText,
4833
5361
  tc,
4834
5362
  currentIndex,
4835
5363
  processedElements,
4836
5364
  options
4837
5365
  );
4838
5366
  }
4839
- if (currentIndex < text.length) {
4840
- appendTextPart(processedElements, text.substring(currentIndex));
5367
+ if (currentIndex < parseText.length) {
5368
+ addTextSegment(parseText.substring(currentIndex), processedElements);
4841
5369
  }
4842
5370
  return processedElements;
4843
5371
  },
@@ -4933,6 +5461,20 @@ ${yamlContent}</${toolCall.toolName}>`;
4933
5461
  return new TransformStream({
4934
5462
  transform(chunk, controller) {
4935
5463
  var _a;
5464
+ if (chunk.type === "finish") {
5465
+ if (currentToolCall) {
5466
+ const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
5467
+ flushText(controller, unfinishedContent);
5468
+ buffer = "";
5469
+ currentToolCall = null;
5470
+ } else if (buffer) {
5471
+ flushText(controller, buffer);
5472
+ buffer = "";
5473
+ }
5474
+ flushText(controller);
5475
+ controller.enqueue(chunk);
5476
+ return;
5477
+ }
4936
5478
  if (chunk.type !== "text-delta") {
4937
5479
  if (buffer) {
4938
5480
  flushText(controller, buffer);
@@ -5056,17 +5598,56 @@ function encodeOriginalTools(tools) {
5056
5598
  inputSchema: JSON.stringify(t.inputSchema)
5057
5599
  }))) || [];
5058
5600
  }
5059
- function decodeOriginalTools(originalTools) {
5601
+ function decodeOriginalTools(originalTools, options) {
5602
+ var _a, _b, _c;
5060
5603
  if (!originalTools) {
5061
5604
  return [];
5062
5605
  }
5063
- return originalTools.map(
5064
- (t) => ({
5065
- type: "function",
5066
- name: t.name,
5067
- inputSchema: JSON.parse(t.inputSchema)
5068
- })
5069
- );
5606
+ const decodedTools = [];
5607
+ for (const [index, tool] of originalTools.entries()) {
5608
+ if (!tool || typeof tool.name !== "string") {
5609
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "Invalid originalTools entry: missing tool name", {
5610
+ index,
5611
+ tool
5612
+ });
5613
+ continue;
5614
+ }
5615
+ if (typeof tool.inputSchema !== "string") {
5616
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
5617
+ options,
5618
+ "Invalid originalTools entry: inputSchema must be a string",
5619
+ {
5620
+ index,
5621
+ toolName: tool.name
5622
+ }
5623
+ );
5624
+ continue;
5625
+ }
5626
+ try {
5627
+ decodedTools.push({
5628
+ type: "function",
5629
+ name: tool.name,
5630
+ inputSchema: JSON.parse(tool.inputSchema)
5631
+ });
5632
+ } catch (error) {
5633
+ (_c = options == null ? void 0 : options.onError) == null ? void 0 : _c.call(
5634
+ options,
5635
+ "Failed to decode originalTools input schema, using permissive fallback schema",
5636
+ {
5637
+ index,
5638
+ toolName: tool.name,
5639
+ inputSchema: tool.inputSchema,
5640
+ error: error instanceof Error ? error.message : String(error)
5641
+ }
5642
+ );
5643
+ decodedTools.push({
5644
+ type: "function",
5645
+ name: tool.name,
5646
+ inputSchema: { type: "object" }
5647
+ });
5648
+ }
5649
+ }
5650
+ return decodedTools;
5070
5651
  }
5071
5652
  function extractToolNamesFromOriginalTools(originalTools) {
5072
5653
  return (originalTools == null ? void 0 : originalTools.map((t) => t.name)) || [];
@@ -5091,23 +5672,337 @@ function hasInputProperty(obj) {
5091
5672
 
5092
5673
  // src/generate-handler.ts
5093
5674
  var import_provider_utils = require("@ai-sdk/provider-utils");
5094
- function parseToolChoiceJson(text, providerOptions) {
5675
+
5676
+ // src/core/utils/generated-text-json-recovery.ts
5677
+ function isRecord(value) {
5678
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5679
+ }
5680
+ function safeStringify2(value) {
5681
+ try {
5682
+ return JSON.stringify(value != null ? value : {});
5683
+ } catch (e) {
5684
+ return "{}";
5685
+ }
5686
+ }
5687
+ function parseJsonCandidate(candidateText) {
5688
+ try {
5689
+ return parse(candidateText);
5690
+ } catch (e) {
5691
+ return void 0;
5692
+ }
5693
+ }
5694
+ function extractCodeBlockCandidates(text) {
5695
+ var _a, _b;
5696
+ const codeBlockRegex = /```(?:json|yaml|xml)?\s*([\s\S]*?)```/gi;
5697
+ const candidates = [];
5698
+ let match;
5699
+ while (true) {
5700
+ match = codeBlockRegex.exec(text);
5701
+ if (!match) {
5702
+ break;
5703
+ }
5704
+ const body = (_a = match[1]) == null ? void 0 : _a.trim();
5705
+ if (body) {
5706
+ const startIndex = (_b = match.index) != null ? _b : 0;
5707
+ const endIndex = startIndex + match[0].length;
5708
+ candidates.push({
5709
+ text: body,
5710
+ startIndex,
5711
+ endIndex
5712
+ });
5713
+ }
5714
+ }
5715
+ return candidates;
5716
+ }
5717
+ function scanJsonChar(state, char) {
5718
+ if (state.inString) {
5719
+ if (state.escaping) {
5720
+ return { ...state, escaping: false };
5721
+ }
5722
+ if (char === "\\") {
5723
+ return { ...state, escaping: true };
5724
+ }
5725
+ if (char === '"') {
5726
+ return { ...state, inString: false };
5727
+ }
5728
+ return state;
5729
+ }
5730
+ if (char === '"') {
5731
+ return { ...state, inString: true };
5732
+ }
5733
+ if (char === "{") {
5734
+ return { ...state, depth: state.depth + 1 };
5735
+ }
5736
+ if (char === "}") {
5737
+ return { ...state, depth: Math.max(0, state.depth - 1) };
5738
+ }
5739
+ return state;
5740
+ }
5741
+ function extractBalancedJsonObjects(text) {
5742
+ const maxCandidateLength = 1e4;
5743
+ const candidates = [];
5744
+ let state = { depth: 0, inString: false, escaping: false };
5745
+ let currentStart = null;
5746
+ let ignoreCurrent = false;
5747
+ for (let index = 0; index < text.length; index += 1) {
5748
+ const char = text[index];
5749
+ if (!state.inString && char === "{" && state.depth === 0) {
5750
+ currentStart = index;
5751
+ ignoreCurrent = false;
5752
+ }
5753
+ state = scanJsonChar(state, char);
5754
+ if (currentStart !== null && !ignoreCurrent && index - currentStart + 1 > maxCandidateLength) {
5755
+ ignoreCurrent = true;
5756
+ }
5757
+ if (!state.inString && char === "}" && state.depth === 0) {
5758
+ if (currentStart !== null && !ignoreCurrent) {
5759
+ const endIndex = index + 1;
5760
+ const candidate = text.slice(currentStart, endIndex);
5761
+ if (candidate.length > 1) {
5762
+ candidates.push({
5763
+ text: candidate,
5764
+ startIndex: currentStart,
5765
+ endIndex
5766
+ });
5767
+ }
5768
+ }
5769
+ currentStart = null;
5770
+ ignoreCurrent = false;
5771
+ }
5772
+ }
5773
+ return candidates;
5774
+ }
5775
+ function extractTaggedToolCallCandidates(rawText) {
5776
+ var _a, _b;
5777
+ const toolCallRegex = /<tool_call>([\s\S]*?)<\/tool_call>/gi;
5778
+ const candidates = [];
5779
+ let match;
5780
+ while (true) {
5781
+ match = toolCallRegex.exec(rawText);
5782
+ if (!match) {
5783
+ break;
5784
+ }
5785
+ const body = (_a = match[1]) == null ? void 0 : _a.trim();
5786
+ if (!body) {
5787
+ continue;
5788
+ }
5789
+ const startIndex = (_b = match.index) != null ? _b : 0;
5790
+ const endIndex = startIndex + match[0].length;
5791
+ candidates.push({
5792
+ text: body,
5793
+ startIndex,
5794
+ endIndex
5795
+ });
5796
+ }
5797
+ return candidates;
5798
+ }
5799
+ function extractJsonLikeCandidates(rawText) {
5800
+ return mergeJsonCandidatesByStart(
5801
+ extractTaggedToolCallCandidates(rawText),
5802
+ extractCodeBlockCandidates(rawText),
5803
+ extractBalancedJsonObjects(rawText)
5804
+ );
5805
+ }
5806
+ function mergeJsonCandidatesByStart(tagged, codeBlocks, balanced) {
5807
+ return [...tagged, ...codeBlocks, ...balanced].sort(
5808
+ (a, b) => a.startIndex !== b.startIndex ? a.startIndex - b.startIndex : b.endIndex - a.endIndex
5809
+ );
5810
+ }
5811
+ function toToolCallPart(candidate) {
5812
+ return {
5813
+ type: "tool-call",
5814
+ toolCallId: generateId(),
5815
+ toolName: candidate.toolName,
5816
+ input: candidate.input
5817
+ };
5818
+ }
5819
+ function toRecoveredParts(text, candidate, toolCallPart) {
5820
+ const out = [];
5821
+ const prefix = text.slice(0, candidate.startIndex);
5822
+ if (prefix.length > 0) {
5823
+ out.push({ type: "text", text: prefix });
5824
+ }
5825
+ out.push(toolCallPart);
5826
+ const suffix = text.slice(candidate.endIndex);
5827
+ if (suffix.length > 0) {
5828
+ out.push({ type: "text", text: suffix });
5829
+ }
5830
+ return out;
5831
+ }
5832
+ function parseAsToolPayload(payload, tools) {
5833
+ if (!isRecord(payload)) {
5834
+ return null;
5835
+ }
5836
+ const toolName = typeof payload.name === "string" && payload.name.trim().length > 0 ? payload.name.trim() : null;
5837
+ if (!toolName) {
5838
+ return null;
5839
+ }
5840
+ if (!tools.some((tool) => tool.name === toolName)) {
5841
+ return null;
5842
+ }
5843
+ const rawArgs = Object.hasOwn(payload, "arguments") ? payload.arguments : {};
5844
+ if (!isRecord(rawArgs)) {
5845
+ return null;
5846
+ }
5847
+ return {
5848
+ toolName,
5849
+ input: safeStringify2(rawArgs)
5850
+ };
5851
+ }
5852
+ function isLikelyArgumentsShapeForTool(args, tool) {
5853
+ const unwrapped = unwrapJsonSchema(tool.inputSchema);
5854
+ if (!isRecord(unwrapped)) {
5855
+ return false;
5856
+ }
5857
+ if (getSchemaType(unwrapped) !== "object") {
5858
+ return false;
5859
+ }
5860
+ const properties = unwrapped.properties;
5861
+ if (!isRecord(properties)) {
5862
+ return false;
5863
+ }
5864
+ const keys = Object.keys(args);
5865
+ if (keys.length === 0) {
5866
+ return false;
5867
+ }
5868
+ const knownKeys = keys.filter((key) => Object.hasOwn(properties, key));
5869
+ if (knownKeys.length === 0) {
5870
+ return false;
5871
+ }
5872
+ if (unwrapped.additionalProperties === false && knownKeys.length !== keys.length) {
5873
+ return false;
5874
+ }
5875
+ return true;
5876
+ }
5877
+ function parseAsArgumentsOnly(payload, tools) {
5878
+ if (tools.length !== 1) {
5879
+ return null;
5880
+ }
5881
+ if (!isRecord(payload)) {
5882
+ return null;
5883
+ }
5884
+ const hasNameEnvelope = Object.hasOwn(payload, "name") && typeof payload.name === "string" && payload.name.length > 0;
5885
+ const hasArgumentsEnvelope = Object.hasOwn(payload, "arguments") && (typeof payload.arguments === "string" || isRecord(payload.arguments));
5886
+ if (hasNameEnvelope || hasArgumentsEnvelope) {
5887
+ return null;
5888
+ }
5889
+ const tool = tools[0];
5890
+ if (!isLikelyArgumentsShapeForTool(payload, tool)) {
5891
+ return null;
5892
+ }
5893
+ return {
5894
+ toolName: tool.name,
5895
+ input: safeStringify2(payload)
5896
+ };
5897
+ }
5898
+ function recoverToolCallFromJsonCandidates(text, tools) {
5899
+ if (tools.length === 0) {
5900
+ return null;
5901
+ }
5902
+ const jsonCandidates = extractJsonLikeCandidates(text);
5903
+ for (const jsonCandidate of jsonCandidates) {
5904
+ const parsed = parseJsonCandidate(jsonCandidate.text);
5905
+ if (parsed === void 0) {
5906
+ continue;
5907
+ }
5908
+ const toolPayload = parseAsToolPayload(parsed, tools);
5909
+ if (toolPayload) {
5910
+ return toRecoveredParts(text, jsonCandidate, toToolCallPart(toolPayload));
5911
+ }
5912
+ const argsPayload = parseAsArgumentsOnly(parsed, tools);
5913
+ if (argsPayload) {
5914
+ return toRecoveredParts(text, jsonCandidate, toToolCallPart(argsPayload));
5915
+ }
5916
+ }
5917
+ return null;
5918
+ }
5919
+
5920
+ // src/core/utils/tool-call-coercion.ts
5921
+ function coerceToolCallInput(toolName, input, tools) {
5095
5922
  var _a;
5923
+ let args = {};
5924
+ if (typeof input === "string") {
5925
+ try {
5926
+ args = JSON.parse(input);
5927
+ } catch (e) {
5928
+ return;
5929
+ }
5930
+ } else if (input && typeof input === "object") {
5931
+ args = input;
5932
+ } else {
5933
+ return;
5934
+ }
5935
+ const schema = (_a = tools.find((t) => t.name === toolName)) == null ? void 0 : _a.inputSchema;
5936
+ const coerced = coerceBySchema(args, schema);
5937
+ return JSON.stringify(coerced != null ? coerced : {});
5938
+ }
5939
+ function coerceToolCallPart(part, tools) {
5940
+ const coercedInput = coerceToolCallInput(part.toolName, part.input, tools);
5941
+ if (coercedInput === void 0) {
5942
+ return part;
5943
+ }
5944
+ return {
5945
+ ...part,
5946
+ input: coercedInput
5947
+ };
5948
+ }
5949
+
5950
+ // src/core/utils/tool-choice.ts
5951
+ function ensureNonEmptyToolName(name) {
5952
+ if (typeof name !== "string") {
5953
+ return "unknown";
5954
+ }
5955
+ const trimmed = name.trim();
5956
+ return trimmed.length > 0 ? trimmed : "unknown";
5957
+ }
5958
+ function safeStringify3(value) {
5959
+ try {
5960
+ return JSON.stringify(value != null ? value : {});
5961
+ } catch (e) {
5962
+ return "{}";
5963
+ }
5964
+ }
5965
+ function parseToolChoicePayload({
5966
+ text,
5967
+ tools,
5968
+ onError,
5969
+ errorMessage
5970
+ }) {
5971
+ let parsed;
5096
5972
  try {
5097
- return JSON.parse(text);
5973
+ parsed = JSON.parse(text);
5098
5974
  } catch (error) {
5099
- const options = extractOnErrorOption(providerOptions);
5100
- (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
5101
- options,
5102
- "Failed to parse toolChoice JSON from generated model output",
5103
- {
5104
- text,
5105
- error: error instanceof Error ? error.message : String(error)
5106
- }
5107
- );
5108
- return {};
5975
+ onError == null ? void 0 : onError(errorMessage, {
5976
+ text,
5977
+ error: error instanceof Error ? error.message : String(error)
5978
+ });
5979
+ return { toolName: "unknown", input: "{}" };
5109
5980
  }
5981
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
5982
+ onError == null ? void 0 : onError("toolChoice JSON payload must be an object", {
5983
+ parsedType: typeof parsed,
5984
+ parsed
5985
+ });
5986
+ return { toolName: "unknown", input: "{}" };
5987
+ }
5988
+ const payload = parsed;
5989
+ const toolName = ensureNonEmptyToolName(payload.name);
5990
+ const rawArgs = Object.hasOwn(payload, "arguments") ? payload.arguments : {};
5991
+ if (rawArgs == null || typeof rawArgs !== "object" || Array.isArray(rawArgs)) {
5992
+ onError == null ? void 0 : onError("toolChoice arguments must be a JSON object", {
5993
+ toolName,
5994
+ arguments: rawArgs
5995
+ });
5996
+ return { toolName, input: "{}" };
5997
+ }
5998
+ const coercedInput = coerceToolCallInput(toolName, rawArgs, tools);
5999
+ return {
6000
+ toolName,
6001
+ input: coercedInput != null ? coercedInput : safeStringify3(rawArgs)
6002
+ };
5110
6003
  }
6004
+
6005
+ // src/generate-handler.ts
5111
6006
  function logDebugSummary(debugSummary, toolCall, originText) {
5112
6007
  if (debugSummary) {
5113
6008
  debugSummary.originalText = originText;
@@ -5121,25 +6016,34 @@ function logDebugSummary(debugSummary, toolCall, originText) {
5121
6016
  logParsedSummary({ toolCalls: [toolCall], originalText: originText });
5122
6017
  }
5123
6018
  }
5124
- async function handleToolChoice(doGenerate, params) {
5125
- var _a, _b, _c;
6019
+ async function handleToolChoice(doGenerate, params, tools) {
6020
+ var _a, _b, _c, _d;
5126
6021
  const result = await doGenerate();
5127
6022
  const first = (_a = result.content) == null ? void 0 : _a[0];
5128
- let parsed = {};
6023
+ const onError = (_b = extractOnErrorOption(params.providerOptions)) == null ? void 0 : _b.onError;
6024
+ let toolName = "unknown";
6025
+ let input = "{}";
5129
6026
  if (first && first.type === "text") {
5130
6027
  if (getDebugLevel() === "parse") {
5131
6028
  logRawChunk(first.text);
5132
6029
  }
5133
- parsed = parseToolChoiceJson(first.text, params.providerOptions);
6030
+ const parsed = parseToolChoicePayload({
6031
+ text: first.text,
6032
+ tools,
6033
+ onError,
6034
+ errorMessage: "Failed to parse toolChoice JSON from generated model output"
6035
+ });
6036
+ toolName = parsed.toolName;
6037
+ input = parsed.input;
5134
6038
  }
5135
6039
  const toolCall = {
5136
6040
  type: "tool-call",
5137
6041
  toolCallId: (0, import_provider_utils.generateId)(),
5138
- toolName: parsed.name || "unknown",
5139
- input: JSON.stringify(parsed.arguments || {})
6042
+ toolName,
6043
+ input
5140
6044
  };
5141
6045
  const originText = first && first.type === "text" ? first.text : "";
5142
- const debugSummary = (_c = (_b = params.providerOptions) == null ? void 0 : _b.toolCallMiddleware) == null ? void 0 : _c.debugSummary;
6046
+ const debugSummary = (_d = (_c = params.providerOptions) == null ? void 0 : _c.toolCallMiddleware) == null ? void 0 : _d.debugSummary;
5143
6047
  logDebugSummary(debugSummary, toolCall, originText);
5144
6048
  return {
5145
6049
  ...result,
@@ -5154,7 +6058,7 @@ function parseContent(content, protocol, tools, providerOptions) {
5154
6058
  if (getDebugLevel() === "stream") {
5155
6059
  logRawChunk(contentItem.text);
5156
6060
  }
5157
- return protocol.parseGeneratedText({
6061
+ const parsedByProtocol = protocol.parseGeneratedText({
5158
6062
  text: contentItem.text,
5159
6063
  tools,
5160
6064
  options: {
@@ -5162,9 +6066,20 @@ function parseContent(content, protocol, tools, providerOptions) {
5162
6066
  ...providerOptions == null ? void 0 : providerOptions.toolCallMiddleware
5163
6067
  }
5164
6068
  });
6069
+ const hasToolCall = parsedByProtocol.some(
6070
+ (part) => part.type === "tool-call"
6071
+ );
6072
+ if (hasToolCall) {
6073
+ return parsedByProtocol;
6074
+ }
6075
+ const recoveredFromJson = recoverToolCallFromJsonCandidates(
6076
+ contentItem.text,
6077
+ tools
6078
+ );
6079
+ return recoveredFromJson != null ? recoveredFromJson : parsedByProtocol;
5165
6080
  });
5166
6081
  return parsed.map(
5167
- (part) => fixToolCallWithSchema(part, tools)
6082
+ (part) => part.type === "tool-call" ? coerceToolCallPart(part, tools) : part
5168
6083
  );
5169
6084
  }
5170
6085
  function logParsedContent(content) {
@@ -5207,12 +6122,14 @@ async function wrapGenerate({
5207
6122
  params
5208
6123
  }) {
5209
6124
  var _a, _b;
5210
- if (isToolChoiceActive(params)) {
5211
- return handleToolChoice(doGenerate, params);
5212
- }
6125
+ const onError = extractOnErrorOption(params.providerOptions);
5213
6126
  const tools = originalToolsSchema.decode(
5214
- (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.originalTools
6127
+ (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.originalTools,
6128
+ onError
5215
6129
  );
6130
+ if (isToolChoiceActive(params)) {
6131
+ return handleToolChoice(doGenerate, params, tools);
6132
+ }
5216
6133
  const result = await doGenerate();
5217
6134
  if (result.content.length === 0) {
5218
6135
  return result;
@@ -5236,28 +6153,6 @@ async function wrapGenerate({
5236
6153
  content: newContent
5237
6154
  };
5238
6155
  }
5239
- function fixToolCallWithSchema(part, tools) {
5240
- var _a;
5241
- if (part.type !== "tool-call") {
5242
- return part;
5243
- }
5244
- let args = {};
5245
- if (typeof part.input === "string") {
5246
- try {
5247
- args = JSON.parse(part.input);
5248
- } catch (e) {
5249
- return part;
5250
- }
5251
- } else if (part.input && typeof part.input === "object") {
5252
- args = part.input;
5253
- }
5254
- const schema = (_a = tools.find((t) => t.name === part.toolName)) == null ? void 0 : _a.inputSchema;
5255
- const coerced = coerceBySchema(args, schema);
5256
- return {
5257
- ...part,
5258
- input: JSON.stringify(coerced != null ? coerced : {})
5259
- };
5260
- }
5261
6156
 
5262
6157
  // src/core/prompts/hermes-system-prompt.ts
5263
6158
  function hermesSystemPromptTemplate(tools) {
@@ -5661,19 +6556,22 @@ async function wrapStream({
5661
6556
  params
5662
6557
  }) {
5663
6558
  var _a, _b, _c;
6559
+ const onErrorOptions = extractOnErrorOption(params.providerOptions);
6560
+ const tools = originalToolsSchema.decode(
6561
+ (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.originalTools,
6562
+ onErrorOptions
6563
+ );
5664
6564
  if (isToolChoiceActive(params)) {
5665
6565
  return toolChoiceStream({
5666
6566
  doGenerate,
5667
- options: extractOnErrorOption(params.providerOptions)
6567
+ tools,
6568
+ options: onErrorOptions
5668
6569
  });
5669
6570
  }
5670
6571
  const { stream, ...rest } = await doStream();
5671
6572
  const debugLevel = getDebugLevel();
5672
- const tools = originalToolsSchema.decode(
5673
- (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.originalTools
5674
- );
5675
6573
  const options = {
5676
- ...extractOnErrorOption(params.providerOptions),
6574
+ ...onErrorOptions,
5677
6575
  ...((_c = params.providerOptions) == null ? void 0 : _c.toolCallMiddleware) || {}
5678
6576
  };
5679
6577
  const coreStream = stream.pipeThrough(
@@ -5691,10 +6589,11 @@ async function wrapStream({
5691
6589
  const v3Stream = coreStream.pipeThrough(
5692
6590
  new TransformStream({
5693
6591
  transform(part, controller) {
6592
+ const normalizedPart = part.type === "tool-call" ? coerceToolCallPart(part, tools) : part;
5694
6593
  if (debugLevel === "stream") {
5695
- logParsedChunk(part);
6594
+ logParsedChunk(normalizedPart);
5696
6595
  }
5697
- controller.enqueue(part);
6596
+ controller.enqueue(normalizedPart);
5698
6597
  }
5699
6598
  })
5700
6599
  );
@@ -5705,41 +6604,36 @@ async function wrapStream({
5705
6604
  }
5706
6605
  async function toolChoiceStream({
5707
6606
  doGenerate,
6607
+ tools,
5708
6608
  options
5709
6609
  }) {
5710
- var _a, _b;
6610
+ var _a;
6611
+ const normalizedTools = Array.isArray(tools) ? tools : [];
5711
6612
  const result = await doGenerate();
5712
- let toolJson = {};
6613
+ let toolName = "unknown";
6614
+ let input = "{}";
5713
6615
  if ((result == null ? void 0 : result.content) && result.content.length > 0 && ((_a = result.content[0]) == null ? void 0 : _a.type) === "text") {
5714
- try {
5715
- toolJson = JSON.parse(result.content[0].text);
5716
- } catch (error) {
5717
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
5718
- options,
5719
- "Failed to parse toolChoice JSON from streamed model output",
5720
- {
5721
- text: result.content[0].text,
5722
- error: error instanceof Error ? error.message : String(error)
5723
- }
5724
- );
5725
- toolJson = {};
5726
- }
6616
+ const parsed = parseToolChoicePayload({
6617
+ text: result.content[0].text,
6618
+ tools: normalizedTools,
6619
+ onError: options == null ? void 0 : options.onError,
6620
+ errorMessage: "Failed to parse toolChoice JSON from streamed model output"
6621
+ });
6622
+ toolName = parsed.toolName;
6623
+ input = parsed.input;
5727
6624
  }
5728
6625
  const stream = new ReadableStream({
5729
6626
  start(controller) {
5730
6627
  controller.enqueue({
5731
6628
  type: "tool-call",
5732
6629
  toolCallId: (0, import_provider_utils2.generateId)(),
5733
- toolName: toolJson.name || "unknown",
5734
- input: JSON.stringify(toolJson.arguments || {})
6630
+ toolName,
6631
+ input
5735
6632
  });
5736
6633
  controller.enqueue({
5737
6634
  type: "finish",
5738
- usage: (result == null ? void 0 : result.usage) || {
5739
- inputTokens: 0,
5740
- outputTokens: 0
5741
- },
5742
- finishReason: "tool-calls"
6635
+ usage: normalizeUsage(result == null ? void 0 : result.usage),
6636
+ finishReason: normalizeToolCallsFinishReason(result == null ? void 0 : result.finishReason)
5743
6637
  });
5744
6638
  controller.close();
5745
6639
  }
@@ -5750,6 +6644,60 @@ async function toolChoiceStream({
5750
6644
  stream
5751
6645
  };
5752
6646
  }
6647
+ var ZERO_USAGE = {
6648
+ inputTokens: {
6649
+ total: 0,
6650
+ noCache: void 0,
6651
+ cacheRead: void 0,
6652
+ cacheWrite: void 0
6653
+ },
6654
+ outputTokens: {
6655
+ total: 0,
6656
+ text: void 0,
6657
+ reasoning: void 0
6658
+ }
6659
+ };
6660
+ function normalizeToolCallsFinishReason(finishReason) {
6661
+ let raw = "tool-calls";
6662
+ if (typeof finishReason === "string") {
6663
+ raw = finishReason;
6664
+ } else if (finishReason && typeof finishReason === "object" && "raw" in finishReason && typeof finishReason.raw === "string") {
6665
+ raw = finishReason.raw;
6666
+ } else if (finishReason && typeof finishReason === "object" && "unified" in finishReason && typeof finishReason.unified === "string") {
6667
+ raw = finishReason.unified;
6668
+ }
6669
+ return {
6670
+ unified: "tool-calls",
6671
+ raw
6672
+ };
6673
+ }
6674
+ function normalizeUsage(usage) {
6675
+ if (!usage || typeof usage !== "object") {
6676
+ return ZERO_USAGE;
6677
+ }
6678
+ const usageRecord = usage;
6679
+ const input = usageRecord.inputTokens;
6680
+ const output = usageRecord.outputTokens;
6681
+ if (input && typeof input === "object" && output && typeof output === "object") {
6682
+ return usage;
6683
+ }
6684
+ if (typeof input === "number" && typeof output === "number") {
6685
+ return {
6686
+ inputTokens: {
6687
+ total: input,
6688
+ noCache: void 0,
6689
+ cacheRead: void 0,
6690
+ cacheWrite: void 0
6691
+ },
6692
+ outputTokens: {
6693
+ total: output,
6694
+ text: void 0,
6695
+ reasoning: void 0
6696
+ }
6697
+ };
6698
+ }
6699
+ return ZERO_USAGE;
6700
+ }
5753
6701
 
5754
6702
  // src/transform-handler.ts
5755
6703
  function buildFinalPrompt(systemPrompt, processedPrompt, placement) {
@@ -5875,6 +6823,11 @@ function handleToolChoiceRequired(params, baseReturnParams, functionTools) {
5875
6823
  "Tool choice type 'required' is set, but no tools are provided in params.tools."
5876
6824
  );
5877
6825
  }
6826
+ if (functionTools.length === 0) {
6827
+ throw new Error(
6828
+ "Tool choice type 'required' is set, but no function tools are provided. Provider-defined tools are not supported by this middleware."
6829
+ );
6830
+ }
5878
6831
  return {
5879
6832
  ...baseReturnParams,
5880
6833
  responseFormat: {