@ai-sdk-tool/parser 4.1.20 → 4.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -988,11 +988,6 @@ function createFlushTextHandler(getCurrentTextId, setCurrentTextId, getHasEmitte
988
988
  };
989
989
  }
990
990
 
991
- // src/core/utils/regex.ts
992
- function escapeRegExp(literal) {
993
- return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
994
- }
995
-
996
991
  // src/core/utils/streamed-tool-input-delta.ts
997
992
  function emitDelta({
998
993
  controller,
@@ -1974,13 +1969,312 @@ function shouldEmitRawToolCallTextOnError(options) {
1974
1969
  }
1975
1970
 
1976
1971
  // src/core/protocols/hermes-protocol.ts
1972
+ var RJSON_IDENTIFIER_CHAR_REGEX = /[$a-zA-Z0-9_\-+.*?!|&%^/#\\]/;
1973
+ var RJSON_NUMBER_TOKEN_REGEX = /^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
1974
+ function validateNonEmptyDelimiters(toolCallStart, toolCallEnd) {
1975
+ if (toolCallStart.length === 0) {
1976
+ throw new TypeError("hermesProtocol toolCallStart must not be empty");
1977
+ }
1978
+ if (toolCallEnd.length === 0) {
1979
+ throw new TypeError("hermesProtocol toolCallEnd must not be empty");
1980
+ }
1981
+ return {};
1982
+ }
1983
+ function isRjsonIdentifierChar(ch) {
1984
+ return ch != null && RJSON_IDENTIFIER_CHAR_REGEX.test(ch);
1985
+ }
1986
+ function isRjsonPropertyLikeDelimiter(startTag) {
1987
+ const key = startTag.endsWith(":") ? startTag.slice(0, -1) : "";
1988
+ return key.length > 0 && [...key].every((ch) => isRjsonIdentifierChar(ch));
1989
+ }
1990
+ function previousRjsonToken(json, index, minIndex = 0) {
1991
+ let start = index - 1;
1992
+ while (start >= minIndex && isRjsonIdentifierChar(json[start])) {
1993
+ start -= 1;
1994
+ }
1995
+ return json.slice(start + 1, index);
1996
+ }
1997
+ function previousTokenAllowsComment(json, index, minIndex = 0) {
1998
+ const previous = previousRjsonToken(json, index, minIndex);
1999
+ if (previous.length === 0) {
2000
+ return true;
2001
+ }
2002
+ return RJSON_NUMBER_TOKEN_REGEX.test(previous) || previous === "true" || previous === "false" || previous === "null";
2003
+ }
2004
+ function startsRjsonComment(json, index, minIndex = 0) {
2005
+ if (!(json[index] === "/" && json[index + 1] === "/" || json[index] === "/" && json[index + 1] === "*")) {
2006
+ return false;
2007
+ }
2008
+ if (index > minIndex && isRjsonIdentifierChar(json[index - 1])) {
2009
+ return previousTokenAllowsComment(json, index, minIndex);
2010
+ }
2011
+ return true;
2012
+ }
2013
+ function hasNestedStartBoundary(segment, startIndex) {
2014
+ const previous = segment[startIndex - 1];
2015
+ return previous == null || WHITESPACE_JSON_REGEX.test(previous) || previous === "}";
2016
+ }
2017
+ function isLikelyNestedToolCallStart(segment, startIndex, startTag) {
2018
+ if (isRjsonPropertyLikeDelimiter(startTag)) {
2019
+ return false;
2020
+ }
2021
+ const jsonStart = skipJsonWhitespace(segment, startIndex + startTag.length);
2022
+ return segment[jsonStart] === "{" && hasNestedStartBoundary(segment, startIndex);
2023
+ }
2024
+ function findToolCallBoundaryOutsideRjsonSyntax(text, scanFrom, startTag, endTag) {
2025
+ let quote = null;
2026
+ let esc = false;
2027
+ let inLineComment = false;
2028
+ let inBlockComment = false;
2029
+ let lineCommentSawEndTag = false;
2030
+ let blockCommentSawEndTag = false;
2031
+ let nestedStartIndex = null;
2032
+ for (let index = scanFrom; index < text.length; index += 1) {
2033
+ const ch = text[index];
2034
+ if (esc) {
2035
+ esc = false;
2036
+ continue;
2037
+ }
2038
+ if (quote !== null) {
2039
+ if (ch === "\\") {
2040
+ esc = true;
2041
+ continue;
2042
+ }
2043
+ if (ch === quote) {
2044
+ quote = null;
2045
+ }
2046
+ continue;
2047
+ }
2048
+ if (inLineComment) {
2049
+ if (ch === "\n" || ch === "\r") {
2050
+ inLineComment = false;
2051
+ lineCommentSawEndTag = false;
2052
+ continue;
2053
+ }
2054
+ if (text.startsWith(endTag, index)) {
2055
+ lineCommentSawEndTag = true;
2056
+ index += endTag.length - 1;
2057
+ continue;
2058
+ }
2059
+ if (lineCommentSawEndTag && text.startsWith(startTag, index) && text[skipJsonWhitespace(text, index + startTag.length)] === "{") {
2060
+ nestedStartIndex = index;
2061
+ inLineComment = false;
2062
+ lineCommentSawEndTag = false;
2063
+ index += startTag.length - 1;
2064
+ continue;
2065
+ }
2066
+ continue;
2067
+ }
2068
+ if (inBlockComment) {
2069
+ if (ch === "*" && text[index + 1] === "/") {
2070
+ inBlockComment = false;
2071
+ blockCommentSawEndTag = false;
2072
+ index += 1;
2073
+ continue;
2074
+ }
2075
+ if (text.startsWith(endTag, index)) {
2076
+ blockCommentSawEndTag = true;
2077
+ index += endTag.length - 1;
2078
+ continue;
2079
+ }
2080
+ if (blockCommentSawEndTag && text.startsWith(startTag, index) && text[skipJsonWhitespace(text, index + startTag.length)] === "{") {
2081
+ nestedStartIndex = index;
2082
+ inBlockComment = false;
2083
+ blockCommentSawEndTag = false;
2084
+ index += startTag.length - 1;
2085
+ continue;
2086
+ }
2087
+ continue;
2088
+ }
2089
+ if (startsRjsonComment(text, index, scanFrom)) {
2090
+ if (text[index + 1] === "/") {
2091
+ inLineComment = true;
2092
+ lineCommentSawEndTag = false;
2093
+ index += 1;
2094
+ continue;
2095
+ }
2096
+ if (text[index + 1] === "*") {
2097
+ inBlockComment = true;
2098
+ blockCommentSawEndTag = false;
2099
+ index += 1;
2100
+ continue;
2101
+ }
2102
+ }
2103
+ if (text.startsWith(endTag, index)) {
2104
+ return nestedStartIndex == null ? { kind: "end", endIdx: index } : { kind: "nested", endIdx: index, nestedStartIndex };
2105
+ }
2106
+ if (nestedStartIndex == null && text.startsWith(startTag, index) && isLikelyNestedToolCallStart(text, index, startTag)) {
2107
+ nestedStartIndex = index;
2108
+ index += startTag.length - 1;
2109
+ continue;
2110
+ }
2111
+ if (ch === '"' || ch === "'") {
2112
+ quote = ch;
2113
+ }
2114
+ }
2115
+ return null;
2116
+ }
2117
+ function findNextToolCallSpan(text, searchFrom, startTag, endTag) {
2118
+ const startIdx = text.indexOf(startTag, searchFrom);
2119
+ if (startIdx === -1) {
2120
+ return null;
2121
+ }
2122
+ const jsonStart = startIdx + startTag.length;
2123
+ const boundary = findToolCallBoundaryOutsideRjsonSyntax(
2124
+ text,
2125
+ jsonStart,
2126
+ startTag,
2127
+ endTag
2128
+ );
2129
+ if (boundary == null) {
2130
+ return { startIdx, found: false };
2131
+ }
2132
+ if (boundary.kind === "nested") {
2133
+ return { startIdx, found: false };
2134
+ }
2135
+ return { startIdx, found: true, jsonStart, endIdx: boundary.endIdx };
2136
+ }
1977
2137
  function canonicalizeToolInput(argumentsValue) {
1978
2138
  return JSON.stringify(argumentsValue != null ? argumentsValue : {});
1979
2139
  }
2140
+ var CHAR_CODE_BACKSLASH = 92;
2141
+ var CHAR_CODE_QUOTE = 34;
2142
+ var CHAR_CODE_LF = 10;
2143
+ var CHAR_CODE_CR = 13;
2144
+ var CHAR_CODE_TAB = 9;
2145
+ var CHAR_CODE_SLASH = 47;
2146
+ var CHAR_CODE_STAR = 42;
2147
+ var CHAR_CODE_CONTROL_UPPER = 31;
2148
+ var CHAR_CODE_SINGLE_QUOTE = 39;
2149
+ function hasControlCharInString(json) {
2150
+ let quote = null;
2151
+ let esc = false;
2152
+ for (let i = 0; i < json.length; i += 1) {
2153
+ const code = json.charCodeAt(i);
2154
+ if (esc) {
2155
+ esc = false;
2156
+ if (code <= CHAR_CODE_CONTROL_UPPER) {
2157
+ return true;
2158
+ }
2159
+ continue;
2160
+ }
2161
+ if (quote !== null && code === CHAR_CODE_BACKSLASH) {
2162
+ esc = true;
2163
+ continue;
2164
+ }
2165
+ if (quote !== null) {
2166
+ if (code === quote) {
2167
+ quote = null;
2168
+ continue;
2169
+ }
2170
+ if (code <= CHAR_CODE_CONTROL_UPPER) {
2171
+ return true;
2172
+ }
2173
+ continue;
2174
+ }
2175
+ if (code === CHAR_CODE_SLASH && json.charCodeAt(i + 1) === CHAR_CODE_SLASH) {
2176
+ i += 2;
2177
+ while (i < json.length && json.charCodeAt(i) !== CHAR_CODE_LF && json.charCodeAt(i) !== CHAR_CODE_CR) {
2178
+ i += 1;
2179
+ }
2180
+ continue;
2181
+ }
2182
+ if (code === CHAR_CODE_SLASH && json.charCodeAt(i + 1) === CHAR_CODE_STAR) {
2183
+ i += 2;
2184
+ while (i + 1 < json.length && !(json.charCodeAt(i) === CHAR_CODE_STAR && json.charCodeAt(i + 1) === CHAR_CODE_SLASH)) {
2185
+ i += 1;
2186
+ }
2187
+ i += 1;
2188
+ continue;
2189
+ }
2190
+ if (code === CHAR_CODE_QUOTE || code === CHAR_CODE_SINGLE_QUOTE) {
2191
+ quote = code;
2192
+ }
2193
+ }
2194
+ return false;
2195
+ }
2196
+ function normalizeJsonStringCtrl(json) {
2197
+ if (!hasControlCharInString(json)) {
2198
+ return json;
2199
+ }
2200
+ const parts = [];
2201
+ let chunkStart = 0;
2202
+ let quote = null;
2203
+ let esc = false;
2204
+ const flushUpTo = (end) => {
2205
+ if (chunkStart < end) {
2206
+ parts.push(json.slice(chunkStart, end));
2207
+ }
2208
+ };
2209
+ const escapeForCode = (code) => {
2210
+ switch (code) {
2211
+ case CHAR_CODE_LF:
2212
+ return "\\n";
2213
+ case CHAR_CODE_CR:
2214
+ return "\\r";
2215
+ case CHAR_CODE_TAB:
2216
+ return "\\t";
2217
+ default:
2218
+ return `\\u${code.toString(16).padStart(4, "0")}`;
2219
+ }
2220
+ };
2221
+ for (let i = 0; i < json.length; i += 1) {
2222
+ const code = json.charCodeAt(i);
2223
+ if (esc) {
2224
+ esc = false;
2225
+ if (code <= CHAR_CODE_CONTROL_UPPER) {
2226
+ flushUpTo(i - 1);
2227
+ parts.push(escapeForCode(code));
2228
+ chunkStart = i + 1;
2229
+ }
2230
+ continue;
2231
+ }
2232
+ if (quote !== null && code === CHAR_CODE_BACKSLASH) {
2233
+ esc = true;
2234
+ continue;
2235
+ }
2236
+ if (quote !== null) {
2237
+ if (code === quote) {
2238
+ quote = null;
2239
+ continue;
2240
+ }
2241
+ if (code <= CHAR_CODE_CONTROL_UPPER) {
2242
+ flushUpTo(i);
2243
+ parts.push(escapeForCode(code));
2244
+ chunkStart = i + 1;
2245
+ }
2246
+ continue;
2247
+ }
2248
+ if (code === CHAR_CODE_SLASH && json.charCodeAt(i + 1) === CHAR_CODE_SLASH) {
2249
+ i += 2;
2250
+ while (i < json.length && json.charCodeAt(i) !== CHAR_CODE_LF && json.charCodeAt(i) !== CHAR_CODE_CR) {
2251
+ i += 1;
2252
+ }
2253
+ continue;
2254
+ }
2255
+ if (code === CHAR_CODE_SLASH && json.charCodeAt(i + 1) === CHAR_CODE_STAR) {
2256
+ i += 2;
2257
+ while (i + 1 < json.length && !(json.charCodeAt(i) === CHAR_CODE_STAR && json.charCodeAt(i + 1) === CHAR_CODE_SLASH)) {
2258
+ i += 1;
2259
+ }
2260
+ i += 1;
2261
+ continue;
2262
+ }
2263
+ if (code === CHAR_CODE_QUOTE || code === CHAR_CODE_SINGLE_QUOTE) {
2264
+ quote = code;
2265
+ }
2266
+ }
2267
+ if (chunkStart < json.length) {
2268
+ parts.push(json.slice(chunkStart));
2269
+ }
2270
+ return parts.join("");
2271
+ }
1980
2272
  function processToolCallJson(toolCallJson, fullMatch, processedElements, options) {
1981
2273
  var _a;
1982
2274
  try {
1983
- const parsedToolCall = parse(toolCallJson);
2275
+ const parsedToolCall = parse(
2276
+ normalizeJsonStringCtrl(toolCallJson)
2277
+ );
1984
2278
  processedElements.push({
1985
2279
  type: "tool-call",
1986
2280
  toolCallId: generateToolCallId(),
@@ -1988,6 +2282,8 @@ function processToolCallJson(toolCallJson, fullMatch, processedElements, options
1988
2282
  input: canonicalizeToolInput(parsedToolCall.arguments)
1989
2283
  });
1990
2284
  } catch (error) {
2285
+ const salvagedToolName = extractStreamingToolCallProgress(toolCallJson).toolName;
2286
+ const salvagedToolCallId = generateToolCallId();
1991
2287
  logParseFailure({
1992
2288
  phase: "generated-text",
1993
2289
  reason: "Failed to parse tool call JSON segment",
@@ -1997,24 +2293,17 @@ function processToolCallJson(toolCallJson, fullMatch, processedElements, options
1997
2293
  (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
1998
2294
  options,
1999
2295
  "Could not process JSON tool call, keeping original text.",
2000
- { toolCall: fullMatch, error }
2296
+ {
2297
+ toolCall: fullMatch,
2298
+ error,
2299
+ toolName: salvagedToolName,
2300
+ toolCallId: salvagedToolCallId,
2301
+ dropReason: "malformed-tool-call-body"
2302
+ }
2001
2303
  );
2002
2304
  processedElements.push({ type: "text", text: fullMatch });
2003
2305
  }
2004
2306
  }
2005
- function processMatchedToolCall(context) {
2006
- const { match, text, currentIndex, processedElements, options } = context;
2007
- const startIndex = match.index;
2008
- const toolCallJson = match[1];
2009
- if (startIndex > currentIndex) {
2010
- const textSegment = text.slice(currentIndex, startIndex);
2011
- addTextSegment(textSegment, processedElements);
2012
- }
2013
- if (toolCallJson) {
2014
- processToolCallJson(toolCallJson, match[0], processedElements, options);
2015
- }
2016
- return startIndex + match[0].length;
2017
- }
2018
2307
  var WHITESPACE_JSON_REGEX = /\s/;
2019
2308
  function skipJsonWhitespace(text, fromIndex) {
2020
2309
  let index = fromIndex;
@@ -2281,7 +2570,9 @@ function canonicalizeArgumentsProgressInput(progress, toolName, tools) {
2281
2570
  return void 0;
2282
2571
  }
2283
2572
  try {
2284
- const parsedArguments = parse(progress.argumentsText);
2573
+ const parsedArguments = parse(
2574
+ normalizeJsonStringCtrl(progress.argumentsText)
2575
+ );
2285
2576
  return stringifyToolInputWithSchema({
2286
2577
  toolName,
2287
2578
  args: parsedArguments,
@@ -2341,14 +2632,16 @@ function closeTextBlock(state, controller) {
2341
2632
  }
2342
2633
  }
2343
2634
  function emitIncompleteToolCall(state, controller, toolCallStart, trailingBuffer, tools, options) {
2344
- var _a;
2635
+ var _a, _b, _c, _d;
2345
2636
  if (!state.currentToolCallJson && trailingBuffer.length === 0) {
2346
2637
  state.isInsideToolCall = false;
2347
2638
  return;
2348
2639
  }
2349
2640
  if (state.currentToolCallJson) {
2350
2641
  try {
2351
- const parsedToolCall = parse(state.currentToolCallJson);
2642
+ const parsedToolCall = parse(
2643
+ normalizeJsonStringCtrl(state.currentToolCallJson)
2644
+ );
2352
2645
  emitToolCallFromParsed(state, controller, parsedToolCall, tools);
2353
2646
  state.currentToolCallJson = "";
2354
2647
  state.isInsideToolCall = false;
@@ -2380,11 +2673,19 @@ function emitIncompleteToolCall(state, controller, toolCallStart, trailingBuffer
2380
2673
  id: errorId
2381
2674
  });
2382
2675
  }
2676
+ const streamingToolCallId = (_b = (_a = state.activeToolInput) == null ? void 0 : _a.id) != null ? _b : generateToolCallId();
2677
+ const streamingToolName = (_c = state.activeToolInput) == null ? void 0 : _c.toolName;
2383
2678
  closeToolInput(state, controller);
2384
- (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
2679
+ const toolName = streamingToolName != null ? streamingToolName : state.currentToolCallJson ? extractStreamingToolCallProgress(state.currentToolCallJson).toolName : void 0;
2680
+ (_d = options == null ? void 0 : options.onError) == null ? void 0 : _d.call(
2385
2681
  options,
2386
2682
  shouldEmitRawFallback ? "Could not complete streaming JSON tool call at finish; emitting original text." : "Could not complete streaming JSON tool call at finish.",
2387
- { toolCall: errorContent }
2683
+ {
2684
+ toolCall: errorContent,
2685
+ toolCallId: streamingToolCallId,
2686
+ toolName,
2687
+ dropReason: "unfinished-tool-call"
2688
+ }
2388
2689
  );
2389
2690
  state.currentToolCallJson = "";
2390
2691
  state.isInsideToolCall = false;
@@ -2429,14 +2730,18 @@ function publishText(text, state, controller, tools) {
2429
2730
  }
2430
2731
  }
2431
2732
  function emitToolCall(context) {
2432
- var _a;
2733
+ var _a, _b, _c, _d, _e;
2433
2734
  const { state, controller, toolCallStart, toolCallEnd, options, tools } = context;
2434
2735
  try {
2435
- const parsedToolCall = parse(state.currentToolCallJson);
2736
+ const parsedToolCall = parse(
2737
+ normalizeJsonStringCtrl(state.currentToolCallJson)
2738
+ );
2436
2739
  emitToolCallFromParsed(state, controller, parsedToolCall, tools);
2437
2740
  } catch (error) {
2438
2741
  const errorContent = `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`;
2439
2742
  const shouldEmitRawFallback = shouldEmitRawToolCallTextOnError(options);
2743
+ const streamingToolCallId = (_b = (_a = state.activeToolInput) == null ? void 0 : _a.id) != null ? _b : generateToolCallId();
2744
+ const streamingToolName = (_d = (_c = state.activeToolInput) == null ? void 0 : _c.toolName) != null ? _d : extractStreamingToolCallProgress(state.currentToolCallJson).toolName;
2440
2745
  logParseFailure({
2441
2746
  phase: "stream",
2442
2747
  reason: "Failed to parse streaming tool call JSON segment",
@@ -2460,11 +2765,15 @@ function emitToolCall(context) {
2460
2765
  });
2461
2766
  }
2462
2767
  closeToolInput(state, controller);
2463
- (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
2768
+ (_e = options == null ? void 0 : options.onError) == null ? void 0 : _e.call(
2464
2769
  options,
2465
2770
  shouldEmitRawFallback ? "Could not process streaming JSON tool call; emitting original text." : "Could not process streaming JSON tool call.",
2466
2771
  {
2467
- toolCall: errorContent
2772
+ toolCall: errorContent,
2773
+ error,
2774
+ toolCallId: streamingToolCallId,
2775
+ toolName: streamingToolName,
2776
+ dropReason: "malformed-tool-call-body"
2468
2777
  }
2469
2778
  );
2470
2779
  }
@@ -2481,24 +2790,118 @@ function processTagMatch(context) {
2481
2790
  state.activeToolInput = null;
2482
2791
  }
2483
2792
  }
2484
- function processBufferTags(context) {
2793
+ function recoverNestedStreamingToolCall(options) {
2794
+ var _a, _b, _c, _d;
2795
+ const { context, jsonSoFar, nestedStartIndex, startIndex, tag } = options;
2796
+ const {
2797
+ state,
2798
+ controller,
2799
+ toolCallStart,
2800
+ toolCallEnd,
2801
+ options: parserOptions
2802
+ } = context;
2803
+ const droppedToolCall = `${toolCallStart}${jsonSoFar.slice(
2804
+ 0,
2805
+ nestedStartIndex
2806
+ )}`;
2807
+ const shouldEmitRawFallback = shouldEmitRawToolCallTextOnError(parserOptions);
2808
+ const streamingToolCallId = (_a = state.activeToolInput) == null ? void 0 : _a.id;
2809
+ const streamingToolName = (_c = (_b = state.activeToolInput) == null ? void 0 : _b.toolName) != null ? _c : extractStreamingToolCallProgress(jsonSoFar.slice(0, nestedStartIndex)).toolName;
2810
+ logParseFailure({
2811
+ phase: "stream",
2812
+ reason: "Abandoning malformed streaming tool call before nested start tag",
2813
+ snippet: droppedToolCall
2814
+ });
2815
+ if (shouldEmitRawFallback) {
2816
+ const errorId = generateId();
2817
+ controller.enqueue({
2818
+ type: "text-start",
2819
+ id: errorId
2820
+ });
2821
+ controller.enqueue({
2822
+ type: "text-delta",
2823
+ id: errorId,
2824
+ delta: droppedToolCall
2825
+ });
2826
+ controller.enqueue({
2827
+ type: "text-end",
2828
+ id: errorId
2829
+ });
2830
+ }
2831
+ closeToolInput(state, controller);
2832
+ (_d = parserOptions == null ? void 0 : parserOptions.onError) == null ? void 0 : _d.call(
2833
+ parserOptions,
2834
+ shouldEmitRawFallback ? "Could not process malformed streaming JSON tool call before nested start; emitting original text." : "Could not process malformed streaming JSON tool call before nested start.",
2835
+ {
2836
+ toolCall: droppedToolCall,
2837
+ toolCallId: streamingToolCallId,
2838
+ toolName: streamingToolName,
2839
+ dropReason: "malformed-nested-tool-call"
2840
+ }
2841
+ );
2842
+ state.currentToolCallJson = "";
2843
+ state.isInsideToolCall = false;
2844
+ state.buffer = jsonSoFar.slice(nestedStartIndex) + toolCallEnd + state.buffer.slice(startIndex + tag.length);
2845
+ return getPotentialStartIndex(state.buffer, toolCallStart);
2846
+ }
2847
+ function processInsideToolCallBoundary(context) {
2485
2848
  const { state, controller, toolCallStart, toolCallEnd, tools } = context;
2486
- let startIndex = getPotentialStartIndex(
2487
- state.buffer,
2488
- state.isInsideToolCall ? toolCallEnd : toolCallStart
2849
+ const currentLength = state.currentToolCallJson.length;
2850
+ const combined = state.currentToolCallJson + state.buffer;
2851
+ const boundary = findToolCallBoundaryOutsideRjsonSyntax(
2852
+ combined,
2853
+ 0,
2854
+ toolCallStart,
2855
+ toolCallEnd
2489
2856
  );
2857
+ if (boundary == null) {
2858
+ return false;
2859
+ }
2860
+ const relativeEndIndex = boundary.endIdx - currentLength;
2861
+ if (relativeEndIndex < 0) {
2862
+ return false;
2863
+ }
2864
+ if (boundary.kind === "nested") {
2865
+ recoverNestedStreamingToolCall({
2866
+ context,
2867
+ jsonSoFar: combined.slice(0, boundary.endIdx),
2868
+ nestedStartIndex: boundary.nestedStartIndex,
2869
+ startIndex: relativeEndIndex,
2870
+ tag: toolCallEnd
2871
+ });
2872
+ return true;
2873
+ }
2874
+ publishText(
2875
+ state.buffer.slice(0, relativeEndIndex),
2876
+ state,
2877
+ controller,
2878
+ tools
2879
+ );
2880
+ state.buffer = state.buffer.slice(relativeEndIndex + toolCallEnd.length);
2881
+ processTagMatch(context);
2882
+ return true;
2883
+ }
2884
+ function processBufferTags(context) {
2885
+ const { state, controller, toolCallStart, tools } = context;
2886
+ while (state.isInsideToolCall) {
2887
+ if (!processInsideToolCallBoundary(context)) {
2888
+ return;
2889
+ }
2890
+ }
2891
+ let startIndex = getPotentialStartIndex(state.buffer, toolCallStart);
2490
2892
  while (startIndex != null) {
2491
- const tag = state.isInsideToolCall ? toolCallEnd : toolCallStart;
2492
- if (startIndex + tag.length > state.buffer.length) {
2893
+ if (startIndex + toolCallStart.length > state.buffer.length) {
2493
2894
  break;
2494
2895
  }
2495
2896
  publishText(state.buffer.slice(0, startIndex), state, controller, tools);
2496
- state.buffer = state.buffer.slice(startIndex + tag.length);
2897
+ state.buffer = state.buffer.slice(startIndex + toolCallStart.length);
2497
2898
  processTagMatch(context);
2498
- startIndex = getPotentialStartIndex(
2499
- state.buffer,
2500
- state.isInsideToolCall ? toolCallEnd : toolCallStart
2501
- );
2899
+ while (state.isInsideToolCall) {
2900
+ if (!processInsideToolCallBoundary(context)) {
2901
+ return;
2902
+ }
2903
+ }
2904
+ startIndex = getPotentialStartIndex(state.buffer, toolCallStart);
2502
2905
  }
2503
2906
  }
2504
2907
  function handlePartialTag(state, controller, toolCallStart, toolCallEnd, tools) {
@@ -2536,6 +2939,7 @@ var hermesProtocol = ({
2536
2939
  toolCallStart = "<tool_call>",
2537
2940
  toolCallEnd = "</tool_call>"
2538
2941
  } = {}) => ({
2942
+ ...validateNonEmptyDelimiters(toolCallStart, toolCallEnd),
2539
2943
  formatTools({
2540
2944
  tools,
2541
2945
  toolSystemPromptTemplate
@@ -2560,24 +2964,42 @@ var hermesProtocol = ({
2560
2964
  text,
2561
2965
  options
2562
2966
  }) {
2563
- const startEsc = escapeRegExp(toolCallStart);
2564
- const endEsc = escapeRegExp(toolCallEnd);
2565
- const toolCallRegex = new RegExp(
2566
- `${startEsc}([\0-\uFFFF]*?)${endEsc}`,
2567
- "gs"
2568
- );
2569
2967
  const processedElements = [];
2570
2968
  let currentIndex = 0;
2571
- let match = toolCallRegex.exec(text);
2572
- while (match !== null) {
2573
- currentIndex = processMatchedToolCall({
2574
- match,
2969
+ let searchFrom = 0;
2970
+ while (searchFrom < text.length) {
2971
+ const span = findNextToolCallSpan(
2575
2972
  text,
2576
- currentIndex,
2577
- processedElements,
2578
- options
2579
- });
2580
- match = toolCallRegex.exec(text);
2973
+ searchFrom,
2974
+ toolCallStart,
2975
+ toolCallEnd
2976
+ );
2977
+ if (span === null) {
2978
+ break;
2979
+ }
2980
+ if (!span.found) {
2981
+ const skipTo = span.startIdx + toolCallStart.length;
2982
+ if (skipTo > currentIndex) {
2983
+ addTextSegment(text.slice(currentIndex, skipTo), processedElements);
2984
+ currentIndex = skipTo;
2985
+ }
2986
+ searchFrom = skipTo;
2987
+ continue;
2988
+ }
2989
+ const toolCallJson = text.slice(span.jsonStart, span.endIdx);
2990
+ const fullMatch = text.slice(
2991
+ span.startIdx,
2992
+ span.endIdx + toolCallEnd.length
2993
+ );
2994
+ if (span.startIdx > currentIndex) {
2995
+ addTextSegment(
2996
+ text.slice(currentIndex, span.startIdx),
2997
+ processedElements
2998
+ );
2999
+ }
3000
+ processToolCallJson(toolCallJson, fullMatch, processedElements, options);
3001
+ currentIndex = span.endIdx + toolCallEnd.length;
3002
+ searchFrom = currentIndex;
2581
3003
  }
2582
3004
  if (currentIndex < text.length) {
2583
3005
  const remainingText = text.slice(currentIndex);
@@ -2630,14 +3052,26 @@ var hermesProtocol = ({
2630
3052
  });
2631
3053
  },
2632
3054
  extractToolCallSegments({ text }) {
2633
- const startEsc = escapeRegExp(toolCallStart);
2634
- const endEsc = escapeRegExp(toolCallEnd);
2635
- const regex = new RegExp(`${startEsc}([\0-\uFFFF]*?)${endEsc}`, "gs");
2636
3055
  const segments = [];
2637
- let m = regex.exec(text);
2638
- while (m != null) {
2639
- segments.push(m[0]);
2640
- m = regex.exec(text);
3056
+ let searchFrom = 0;
3057
+ while (searchFrom < text.length) {
3058
+ const span = findNextToolCallSpan(
3059
+ text,
3060
+ searchFrom,
3061
+ toolCallStart,
3062
+ toolCallEnd
3063
+ );
3064
+ if (span === null) {
3065
+ break;
3066
+ }
3067
+ if (!span.found) {
3068
+ searchFrom = span.startIdx + toolCallStart.length;
3069
+ continue;
3070
+ }
3071
+ segments.push(
3072
+ text.slice(span.startIdx, span.endIdx + toolCallEnd.length)
3073
+ );
3074
+ searchFrom = span.endIdx + toolCallEnd.length;
2641
3075
  }
2642
3076
  return segments;
2643
3077
  }
@@ -4485,6 +4919,11 @@ function createIntermediateCall(toolName, rawSegment, schema) {
4485
4919
  };
4486
4920
  }
4487
4921
 
4922
+ // src/core/utils/regex.ts
4923
+ function escapeRegExp(literal) {
4924
+ return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4925
+ }
4926
+
4488
4927
  // src/rxml/heuristics/xml-defaults.ts
4489
4928
  var MALFORMED_CLOSE_RE_G = /<\/\s+([A-Za-z0-9_:-]+)\s*>/g;
4490
4929
  var MALFORMED_CLOSE_RE = /<\/\s+([A-Za-z0-9_:-]+)\s*>/;
@@ -5128,7 +5567,13 @@ function processToolCall(params) {
5128
5567
  (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
5129
5568
  options,
5130
5569
  `Could not process XML tool call: ${toolCall.toolName}`,
5131
- { toolCall: originalCallText, error }
5570
+ {
5571
+ toolCall: originalCallText,
5572
+ error,
5573
+ toolName: toolCall.toolName,
5574
+ toolCallId: generateToolCallId(),
5575
+ dropReason: "malformed-tool-call-body"
5576
+ }
5132
5577
  );
5133
5578
  processedElements.push({ type: "text", text: originalCallText });
5134
5579
  }
@@ -5570,7 +6015,10 @@ function handleStreamingToolCallEnd(params) {
5570
6015
  });
5571
6016
  (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, "Could not process streaming XML tool call", {
5572
6017
  toolCall: original,
5573
- error
6018
+ error,
6019
+ toolName: currentToolCall.name,
6020
+ toolCallId: currentToolCall.toolCallId,
6021
+ dropReason: "malformed-tool-call-body"
5574
6022
  });
5575
6023
  }
5576
6024
  }
@@ -6132,7 +6580,13 @@ var morphXmlProtocol = (protocolOptions) => {
6132
6580
  (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
6133
6581
  options,
6134
6582
  "Could not complete streaming XML tool call at finish.",
6135
- { toolCall: unfinishedContent, error }
6583
+ {
6584
+ toolCall: unfinishedContent,
6585
+ toolCallId: currentToolCall.toolCallId,
6586
+ toolName: currentToolCall.name,
6587
+ dropReason: "unfinished-tool-call",
6588
+ error
6589
+ }
6136
6590
  );
6137
6591
  }
6138
6592
  buffer = "";
@@ -6358,7 +6812,8 @@ var QWEN3CODER_TOOL_PARSER_CALL_TAG_NAMES = /* @__PURE__ */ new Set([
6358
6812
  "invoke",
6359
6813
  "tool_call"
6360
6814
  ]);
6361
- var CALL_SHORTHAND_VALUE_RE = /^<\s*(call|function|tool|invoke)\b\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>/]+))/i;
6815
+ var CALL_SHORTHAND_VALUE_RE = /^<\s*(call|function|tool|invoke)\b\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>/<]+))/i;
6816
+ var NESTED_CALL_SHORTHAND_VALUE_RE = /<\s*(?:call|function|tool|invoke)\b\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>/<]+))/i;
6362
6817
  var QWEN3CODER_TOOL_PARSER_STREAM_CALL_OPEN_START_RE = /<\s*(?!\/)\s*(call|function|tool|invoke)\b/i;
6363
6818
  var QWEN3CODER_TOOL_PARSER_STREAM_CALL_OPEN_TAG_RE = /<\s*(?!\/)\s*(call|function|tool|invoke)\b[^>]*>/i;
6364
6819
  var QWEN3CODER_TOOL_PARSER_STREAM_TOOL_CALL_CLOSE_TAG_RE = /<\s*\/\s*tool_call\s*>/i;
@@ -6757,6 +7212,12 @@ function getShorthandValue(openTag) {
6757
7212
  }
6758
7213
  return unescapeXml(value);
6759
7214
  }
7215
+ function extractShorthandToolNameFromRaw(rawText) {
7216
+ var _a, _b;
7217
+ const match = NESTED_CALL_SHORTHAND_VALUE_RE.exec(rawText);
7218
+ const value = (_b = (_a = match == null ? void 0 : match[1]) != null ? _a : match == null ? void 0 : match[2]) != null ? _b : match == null ? void 0 : match[3];
7219
+ return value ? unescapeXml(value) : null;
7220
+ }
6760
7221
  function extractFirstTagText(xml, tagName) {
6761
7222
  var _a;
6762
7223
  const lower = xml.toLowerCase();
@@ -7078,6 +7539,20 @@ function parseQwen3CoderToolParserClosedMatches(inner, outerNameAttr) {
7078
7539
  }
7079
7540
  return closedCalls.concat(trailingCalls);
7080
7541
  }
7542
+ var QWEN3CODER_TOOL_NAME_SALVAGE_REGEX = /<(?:function|call|tool|invoke)(?:\s*=\s*"([^"]+)"|\s*=\s*'([^']+)'|\s*=\s*([^\s>/]+)|\s+name\s*=\s*"([^"]+)"|\s+name\s*=\s*'([^']+)')|<(?:name|tool_name)\b[^>]*>([\s\S]*?)<\s*\/\s*(?:name|tool_name)\s*>/i;
7543
+ function extractQwen3CoderToolNameFromMarkup(markup) {
7544
+ var _a, _b, _c, _d, _e;
7545
+ const match = markup.match(QWEN3CODER_TOOL_NAME_SALVAGE_REGEX);
7546
+ if (!match) {
7547
+ return void 0;
7548
+ }
7549
+ const name = (_e = (_d = (_c = (_b = (_a = match[1]) != null ? _a : match[2]) != null ? _b : match[3]) != null ? _c : match[4]) != null ? _d : match[5]) != null ? _e : match[6];
7550
+ if (!name) {
7551
+ return void 0;
7552
+ }
7553
+ const trimmed = name.trim();
7554
+ return trimmed.length > 0 ? trimmed : void 0;
7555
+ }
7081
7556
  function parseQwen3CoderToolParserToolCallSegment(segment) {
7082
7557
  var _a;
7083
7558
  const extracted = extractToolCallInnerXml(segment);
@@ -7202,7 +7677,12 @@ var qwen3CoderProtocol = () => ({
7202
7677
  (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
7203
7678
  options,
7204
7679
  "Could not process Qwen3CoderToolParser XML tool call; keeping original text.",
7205
- { toolCall: fallbackText }
7680
+ {
7681
+ toolCall: fallbackText,
7682
+ toolName: extractQwen3CoderToolNameFromMarkup(segment),
7683
+ toolCallId: generateToolCallId(),
7684
+ dropReason: "malformed-tool-call-body"
7685
+ }
7206
7686
  );
7207
7687
  processedElements.push({ type: "text", text: fallbackText });
7208
7688
  return false;
@@ -7215,7 +7695,12 @@ var qwen3CoderProtocol = () => ({
7215
7695
  (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
7216
7696
  options,
7217
7697
  "Could not process Qwen3CoderToolParser <function> call; keeping original text.",
7218
- { toolCall: raw }
7698
+ {
7699
+ toolCall: raw,
7700
+ toolName: extractQwen3CoderToolNameFromMarkup(raw),
7701
+ toolCallId: generateToolCallId(),
7702
+ dropReason: "malformed-tool-call-body"
7703
+ }
7219
7704
  );
7220
7705
  processedElements.push({ type: "text", text: raw });
7221
7706
  };
@@ -7465,7 +7950,7 @@ var qwen3CoderProtocol = () => ({
7465
7950
  });
7466
7951
  };
7467
7952
  const finalizeCall = (controller, callState, fallbackToolName, rawToolCallText = null) => {
7468
- var _a, _b;
7953
+ var _a, _b, _c, _d;
7469
7954
  const resolvedToolName = (_a = callState.toolName) != null ? _a : fallbackToolName;
7470
7955
  if (!resolvedToolName || resolvedToolName.trim().length === 0) {
7471
7956
  const shouldEmitRaw = shouldEmitRawToolCallTextOnError(options);
@@ -7479,12 +7964,14 @@ var qwen3CoderProtocol = () => ({
7479
7964
  flushText(controller, rawText);
7480
7965
  }
7481
7966
  });
7482
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
7967
+ (_d = options == null ? void 0 : options.onError) == null ? void 0 : _d.call(
7483
7968
  options,
7484
7969
  shouldEmitRaw && rawToolCallText ? "Could not resolve Qwen3CoderToolParser tool name for tool call; emitting original text." : "Could not resolve Qwen3CoderToolParser tool name for tool call",
7485
7970
  {
7486
7971
  toolCallId: callState.toolCallId,
7487
- toolCall: rawToolCallText
7972
+ toolCall: rawToolCallText,
7973
+ toolName: (_c = (_b = callState.toolName) != null ? _b : fallbackToolName) != null ? _c : void 0,
7974
+ dropReason: "unresolved-tool-name"
7488
7975
  }
7489
7976
  );
7490
7977
  return false;
@@ -7940,25 +8427,36 @@ var qwen3CoderProtocol = () => ({
7940
8427
  }
7941
8428
  }
7942
8429
  };
7943
- const reportUnfinishedToolCallAtFinish = (controller, rawToolCall) => {
7944
- var _a;
8430
+ const reportUnfinishedToolCallAtFinish = (controller, rawToolCall, metadata = {}) => {
8431
+ var _a, _b;
7945
8432
  const shouldEmitRaw = shouldEmitRawToolCallTextOnError(options);
7946
- (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
8433
+ const toolName = (_a = metadata.toolName) != null ? _a : extractShorthandToolNameFromRaw(rawToolCall);
8434
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
7947
8435
  options,
7948
8436
  shouldEmitRaw ? "Could not complete streaming Qwen3CoderToolParser XML tool call at finish; emitting original text." : "Could not complete streaming Qwen3CoderToolParser XML tool call at finish.",
7949
- { toolCall: rawToolCall }
8437
+ {
8438
+ toolCall: rawToolCall,
8439
+ ...metadata.toolCallId ? { toolCallId: metadata.toolCallId } : {},
8440
+ ...toolName ? { toolName } : {},
8441
+ dropReason: "unfinished-tool-call"
8442
+ }
7950
8443
  );
7951
8444
  if (shouldEmitRaw) {
7952
8445
  flushText(controller, rawToolCall);
7953
8446
  }
7954
8447
  };
7955
- const reportUnfinishedImplicitCallAtFinish = (controller, rawCallText) => {
8448
+ const reportUnfinishedImplicitCallAtFinish = (controller, rawCallText, callState) => {
7956
8449
  var _a;
7957
8450
  const shouldEmitRaw = shouldEmitRawToolCallTextOnError(options);
7958
8451
  (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
7959
8452
  options,
7960
8453
  shouldEmitRaw ? "Could not complete streaming Qwen3CoderToolParser call block at finish; emitting original text." : "Could not complete streaming Qwen3CoderToolParser call block at finish.",
7961
- { toolCall: rawCallText }
8454
+ {
8455
+ toolCall: rawCallText,
8456
+ toolCallId: callState.toolCallId,
8457
+ ...callState.toolName ? { toolName: callState.toolName } : {},
8458
+ dropReason: "unfinished-tool-call"
8459
+ }
7962
8460
  );
7963
8461
  if (shouldEmitRaw) {
7964
8462
  flushText(controller, rawCallText);
@@ -8009,7 +8507,10 @@ var qwen3CoderProtocol = () => ({
8009
8507
  flushText(controller, result.trailingText);
8010
8508
  }
8011
8509
  if (!result.ok && toolCall.emittedToolCallCount === 0) {
8012
- reportUnfinishedToolCallAtFinish(controller, toolCall.raw);
8510
+ reportUnfinishedToolCallAtFinish(controller, toolCall.raw, {
8511
+ toolCallId: toolCall.activeCall.toolCallId,
8512
+ ...toolCall.activeCall.toolName ? { toolName: toolCall.activeCall.toolName } : {}
8513
+ });
8013
8514
  }
8014
8515
  } else if (toolCall.mode === "multi") {
8015
8516
  if (toolCall.activeCall) {
@@ -8026,14 +8527,21 @@ var qwen3CoderProtocol = () => ({
8026
8527
  flushText(controller, result.trailingText);
8027
8528
  }
8028
8529
  if (!result.ok && toolCall.emittedToolCallCount === 0) {
8029
- reportUnfinishedToolCallAtFinish(controller, toolCall.raw);
8530
+ reportUnfinishedToolCallAtFinish(controller, toolCall.raw, {
8531
+ toolCallId: toolCall.activeCall.toolCallId,
8532
+ ...toolCall.activeCall.toolName ? { toolName: toolCall.activeCall.toolName } : {}
8533
+ });
8030
8534
  }
8031
8535
  toolCall.activeCall = null;
8032
8536
  } else if (toolCall.emittedToolCallCount === 0) {
8033
- reportUnfinishedToolCallAtFinish(controller, toolCall.raw);
8537
+ reportUnfinishedToolCallAtFinish(controller, toolCall.raw, {
8538
+ toolName: toolCall.outerNameAttr
8539
+ });
8034
8540
  }
8035
8541
  } else {
8036
- reportUnfinishedToolCallAtFinish(controller, toolCall.raw);
8542
+ reportUnfinishedToolCallAtFinish(controller, toolCall.raw, {
8543
+ toolName: toolCall.outerNameAttr
8544
+ });
8037
8545
  }
8038
8546
  toolCall = null;
8039
8547
  }
@@ -8051,7 +8559,8 @@ var qwen3CoderProtocol = () => ({
8051
8559
  if (!result.ok && openTag) {
8052
8560
  reportUnfinishedImplicitCallAtFinish(
8053
8561
  controller,
8054
- callState.raw || openTag + callState.buffer
8562
+ callState.raw || openTag + callState.buffer,
8563
+ callState
8055
8564
  );
8056
8565
  }
8057
8566
  } else {
@@ -8430,26 +8939,28 @@ function findToolCalls2(text, toolNames) {
8430
8939
  );
8431
8940
  return toolCalls.sort((a, b) => a.startIndex - b.startIndex);
8432
8941
  }
8433
- function parseYamlContent(yamlContent, options) {
8434
- var _a, _b;
8942
+ function yamlFailureCause(failure) {
8943
+ if (failure.kind === "yaml-parse-error") {
8944
+ return { kind: "yaml-parse-error", errors: failure.errors };
8945
+ }
8946
+ return { kind: "yaml-non-mapping" };
8947
+ }
8948
+ function parseYamlContent(yamlContent) {
8435
8949
  const { normalized, nonEmptyLines } = normalizeYamlContent(yamlContent);
8436
8950
  if (nonEmptyLines.length === 0) {
8437
- return {};
8951
+ return { ok: true, value: {} };
8438
8952
  }
8439
8953
  const parsed = parseYamlDocumentAsMapping(normalized);
8440
8954
  if (parsed.errors.length > 0) {
8441
- (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "YAML parse error", {
8442
- errors: parsed.errors
8443
- });
8444
- return null;
8955
+ return {
8956
+ ok: false,
8957
+ failure: { kind: "yaml-parse-error", errors: parsed.errors }
8958
+ };
8445
8959
  }
8446
8960
  if (parsed.value === null) {
8447
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, "YAML content must be a key-value mapping", {
8448
- got: "non-mapping"
8449
- });
8450
- return null;
8961
+ return { ok: false, failure: { kind: "yaml-non-mapping" } };
8451
8962
  }
8452
- return parsed.value;
8963
+ return { ok: true, value: parsed.value };
8453
8964
  }
8454
8965
  function parseYamlContentForStreamProgress(yamlContent) {
8455
8966
  const { normalized, nonEmptyLines } = normalizeYamlContent(yamlContent);
@@ -8481,11 +8992,16 @@ function processToolCallMatch(text, tc, currentIndex, processedElements, options
8481
8992
  return currentIndex;
8482
8993
  }
8483
8994
  addTextSegment(text.slice(currentIndex, tc.startIndex), processedElements);
8484
- const parsedArgs = parseYamlContent(tc.content, options);
8485
- if (parsedArgs === null) {
8995
+ const result = parseYamlContent(tc.content);
8996
+ if (!result.ok) {
8486
8997
  const originalText = text.slice(tc.startIndex, tc.endIndex);
8998
+ const cause = yamlFailureCause(result.failure);
8487
8999
  (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "Could not parse YAML tool call", {
8488
- toolCall: originalText
9000
+ toolCall: originalText,
9001
+ toolName: tc.toolName,
9002
+ toolCallId: generateToolCallId(),
9003
+ dropReason: "malformed-tool-call-body",
9004
+ cause
8489
9005
  });
8490
9006
  processedElements.push({ type: "text", text: originalText });
8491
9007
  } else {
@@ -8493,7 +9009,7 @@ function processToolCallMatch(text, tc, currentIndex, processedElements, options
8493
9009
  type: "tool-call",
8494
9010
  toolCallId: generateToolCallId(),
8495
9011
  toolName: tc.toolName,
8496
- input: JSON.stringify(parsedArgs)
9012
+ input: JSON.stringify(result.value)
8497
9013
  });
8498
9014
  }
8499
9015
  return tc.endIndex;
@@ -8616,9 +9132,9 @@ ${yamlContent}</${toolCall.toolName}>`;
8616
9132
  };
8617
9133
  const processToolCallEnd = (controller, toolContent, toolName, toolCallId) => {
8618
9134
  var _a;
8619
- const parsedArgs = parseYamlContent(toolContent, options);
9135
+ const result = parseYamlContent(toolContent);
8620
9136
  flushText(controller);
8621
- if (parsedArgs === null) {
9137
+ if (!result.ok) {
8622
9138
  const original = `<${toolName}>${toolContent}</${toolName}>`;
8623
9139
  const emitRawFallback = shouldEmitRawToolCallTextOnError(options);
8624
9140
  emitFailedToolInputLifecycle({
@@ -8631,12 +9147,16 @@ ${yamlContent}</${toolCall.toolName}>`;
8631
9147
  }
8632
9148
  });
8633
9149
  (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "Could not parse streaming YAML tool call", {
8634
- toolCall: original
9150
+ toolCall: original,
9151
+ toolName,
9152
+ toolCallId,
9153
+ dropReason: "malformed-tool-call-body",
9154
+ cause: yamlFailureCause(result.failure)
8635
9155
  });
8636
9156
  } else {
8637
9157
  const finalInput = stringifyToolInputWithSchema({
8638
9158
  toolName,
8639
- args: parsedArgs,
9159
+ args: result.value,
8640
9160
  tools
8641
9161
  });
8642
9162
  if (currentToolCall && currentToolCall.toolCallId === toolCallId) {
@@ -8666,9 +9186,9 @@ ${yamlContent}</${toolCall.toolName}>`;
8666
9186
  emitToolInputProgress2(controller, buffer);
8667
9187
  const { name: toolName, toolCallId } = currentToolCall;
8668
9188
  const reconciledBuffer = stripTrailingPartialCloseTag(buffer, toolName);
8669
- const parsedArgs = parseYamlContent(reconciledBuffer, options);
9189
+ const result = parseYamlContent(reconciledBuffer);
8670
9190
  flushText(controller);
8671
- if (parsedArgs === null) {
9191
+ if (!result.ok) {
8672
9192
  const unfinishedContent = `<${toolName}>${buffer}`;
8673
9193
  const emitRawFallback = shouldEmitRawToolCallTextOnError(options);
8674
9194
  emitFailedToolInputLifecycle({
@@ -8683,12 +9203,18 @@ ${yamlContent}</${toolCall.toolName}>`;
8683
9203
  (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
8684
9204
  options,
8685
9205
  "Could not complete streaming YAML tool call at finish.",
8686
- { toolCall: unfinishedContent }
9206
+ {
9207
+ toolCall: unfinishedContent,
9208
+ toolCallId,
9209
+ toolName,
9210
+ dropReason: "unfinished-tool-call",
9211
+ cause: yamlFailureCause(result.failure)
9212
+ }
8687
9213
  );
8688
9214
  } else {
8689
9215
  const finalInput = stringifyToolInputWithSchema({
8690
9216
  toolName,
8691
- args: parsedArgs,
9217
+ args: result.value,
8692
9218
  tools
8693
9219
  });
8694
9220
  emitFinalizedToolInputLifecycle({