@ai-sdk-tool/parser 3.3.3 → 4.0.1

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.
@@ -836,9 +836,19 @@ function getPotentialStartIndex(text, searchedText) {
836
836
  if (directIndex !== -1) {
837
837
  return directIndex;
838
838
  }
839
- for (let i = text.length - 1; i >= 0; i -= 1) {
840
- const suffix = text.substring(i);
841
- if (searchedText.startsWith(suffix)) {
839
+ const textLength = text.length;
840
+ const searchedTextLength = searchedText.length;
841
+ const startAt = Math.max(0, textLength - searchedTextLength + 1);
842
+ for (let i = startAt; i < textLength; i++) {
843
+ let match = true;
844
+ const currentSuffixLength = textLength - i;
845
+ for (let j = 0; j < currentSuffixLength; j++) {
846
+ if (text[i + j] !== searchedText[j]) {
847
+ match = false;
848
+ break;
849
+ }
850
+ }
851
+ if (match) {
842
852
  return i;
843
853
  }
844
854
  }
@@ -847,7 +857,25 @@ function getPotentialStartIndex(text, searchedText) {
847
857
 
848
858
  // src/core/utils/id.ts
849
859
  function generateId() {
850
- return Math.random().toString(36).substring(2, 15);
860
+ return crypto.randomUUID().replace(/-/g, "").slice(0, 13);
861
+ }
862
+ var TOOL_CALL_ID_PREFIX = "call_";
863
+ var TOOL_CALL_ID_BODY_LENGTH = 24;
864
+ var TOOL_CALL_ID_ALPHANUM = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
865
+ function randomAlphaNumeric(length) {
866
+ var _a;
867
+ const bytes = new Uint8Array(length);
868
+ crypto.getRandomValues(bytes);
869
+ let out = "";
870
+ for (let i = 0; i < length; i += 1) {
871
+ const byte = bytes[i];
872
+ const index = (byte != null ? byte : 0) % TOOL_CALL_ID_ALPHANUM.length;
873
+ out += (_a = TOOL_CALL_ID_ALPHANUM[index]) != null ? _a : "0";
874
+ }
875
+ return out;
876
+ }
877
+ function generateToolCallId() {
878
+ return `${TOOL_CALL_ID_PREFIX}${randomAlphaNumeric(TOOL_CALL_ID_BODY_LENGTH)}`;
851
879
  }
852
880
 
853
881
  // src/core/utils/protocol-utils.ts
@@ -856,6 +884,38 @@ function addTextSegment(text, processedElements) {
856
884
  processedElements.push({ type: "text", text });
857
885
  }
858
886
  }
887
+ function createFlushTextHandler(getCurrentTextId, setCurrentTextId, getHasEmittedTextStart, setHasEmittedTextStart) {
888
+ return (controller, text) => {
889
+ const content = text;
890
+ if (content) {
891
+ if (!getCurrentTextId()) {
892
+ const newId = generateId();
893
+ setCurrentTextId(newId);
894
+ controller.enqueue({
895
+ type: "text-start",
896
+ id: newId
897
+ });
898
+ setHasEmittedTextStart(true);
899
+ }
900
+ controller.enqueue({
901
+ type: "text-delta",
902
+ id: getCurrentTextId(),
903
+ delta: content
904
+ });
905
+ }
906
+ const currentTextId = getCurrentTextId();
907
+ if (currentTextId && !text) {
908
+ if (getHasEmittedTextStart()) {
909
+ controller.enqueue({
910
+ type: "text-end",
911
+ id: currentTextId
912
+ });
913
+ setHasEmittedTextStart(false);
914
+ }
915
+ setCurrentTextId(null);
916
+ }
917
+ };
918
+ }
859
919
 
860
920
  // src/core/utils/regex.ts
861
921
  function escapeRegExp(literal) {
@@ -863,15 +923,21 @@ function escapeRegExp(literal) {
863
923
  }
864
924
 
865
925
  // src/core/protocols/json-protocol.ts
926
+ function shouldEmitRawToolCallTextOnError(options) {
927
+ return (options == null ? void 0 : options.emitRawToolCallTextOnError) === true;
928
+ }
929
+ function canonicalizeToolInput(argumentsValue) {
930
+ return JSON.stringify(argumentsValue != null ? argumentsValue : {});
931
+ }
866
932
  function processToolCallJson(toolCallJson, fullMatch, processedElements, options) {
867
- var _a, _b;
933
+ var _a;
868
934
  try {
869
935
  const parsedToolCall = parse(toolCallJson);
870
936
  processedElements.push({
871
937
  type: "tool-call",
872
- toolCallId: generateId(),
938
+ toolCallId: generateToolCallId(),
873
939
  toolName: parsedToolCall.name,
874
- input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
940
+ input: canonicalizeToolInput(parsedToolCall.arguments)
875
941
  });
876
942
  } catch (error) {
877
943
  logParseFailure({
@@ -880,7 +946,7 @@ function processToolCallJson(toolCallJson, fullMatch, processedElements, options
880
946
  snippet: fullMatch,
881
947
  error
882
948
  });
883
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
949
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
884
950
  options,
885
951
  "Could not process JSON tool call, keeping original text.",
886
952
  { toolCall: fullMatch, error }
@@ -901,6 +967,293 @@ function processMatchedToolCall(context) {
901
967
  }
902
968
  return startIndex + match[0].length;
903
969
  }
970
+ var WHITESPACE_JSON_REGEX = /\s/;
971
+ function skipJsonWhitespace(text, fromIndex) {
972
+ let index = fromIndex;
973
+ while (index < text.length && WHITESPACE_JSON_REGEX.test(text[index])) {
974
+ index += 1;
975
+ }
976
+ return index;
977
+ }
978
+ function findTopLevelPropertyValueStart(text, property) {
979
+ const objectStart = skipJsonWhitespace(text, 0);
980
+ if (objectStart >= text.length || text.charAt(objectStart) !== "{") {
981
+ return null;
982
+ }
983
+ let depth = 0;
984
+ let inString = false;
985
+ let escaping = false;
986
+ for (let index = objectStart; index < text.length; index += 1) {
987
+ const char = text.charAt(index);
988
+ if (inString) {
989
+ if (escaping) {
990
+ escaping = false;
991
+ continue;
992
+ }
993
+ if (char === "\\") {
994
+ escaping = true;
995
+ continue;
996
+ }
997
+ if (char === '"') {
998
+ inString = false;
999
+ }
1000
+ continue;
1001
+ }
1002
+ if (char === "{") {
1003
+ depth += 1;
1004
+ continue;
1005
+ }
1006
+ if (char === "}") {
1007
+ depth = Math.max(0, depth - 1);
1008
+ continue;
1009
+ }
1010
+ if (char !== '"') {
1011
+ continue;
1012
+ }
1013
+ if (depth !== 1) {
1014
+ inString = true;
1015
+ continue;
1016
+ }
1017
+ const keyStart = index + 1;
1018
+ let keyEnd = keyStart;
1019
+ let keyEscaped = false;
1020
+ while (keyEnd < text.length) {
1021
+ const keyChar = text.charAt(keyEnd);
1022
+ if (keyEscaped) {
1023
+ keyEscaped = false;
1024
+ } else if (keyChar === "\\") {
1025
+ keyEscaped = true;
1026
+ } else if (keyChar === '"') {
1027
+ break;
1028
+ }
1029
+ keyEnd += 1;
1030
+ }
1031
+ if (keyEnd >= text.length || text.charAt(keyEnd) !== '"') {
1032
+ return null;
1033
+ }
1034
+ const key = text.slice(keyStart, keyEnd);
1035
+ let valueCursor = skipJsonWhitespace(text, keyEnd + 1);
1036
+ if (valueCursor >= text.length || text.charAt(valueCursor) !== ":") {
1037
+ index = keyEnd;
1038
+ continue;
1039
+ }
1040
+ valueCursor = skipJsonWhitespace(text, valueCursor + 1);
1041
+ if (key === property) {
1042
+ return valueCursor < text.length ? valueCursor : null;
1043
+ }
1044
+ index = valueCursor - 1;
1045
+ }
1046
+ return null;
1047
+ }
1048
+ function extractTopLevelStringProperty(text, property) {
1049
+ const valueStart = findTopLevelPropertyValueStart(text, property);
1050
+ if (valueStart == null || valueStart >= text.length) {
1051
+ return void 0;
1052
+ }
1053
+ if (text.charAt(valueStart) !== '"') {
1054
+ return void 0;
1055
+ }
1056
+ let valueEnd = valueStart + 1;
1057
+ let escaped = false;
1058
+ while (valueEnd < text.length) {
1059
+ const char = text.charAt(valueEnd);
1060
+ if (escaped) {
1061
+ escaped = false;
1062
+ } else if (char === "\\") {
1063
+ escaped = true;
1064
+ } else if (char === '"') {
1065
+ return text.slice(valueStart + 1, valueEnd);
1066
+ }
1067
+ valueEnd += 1;
1068
+ }
1069
+ return void 0;
1070
+ }
1071
+ function extractJsonValueSlice(text, valueStart) {
1072
+ if (valueStart >= text.length) {
1073
+ return null;
1074
+ }
1075
+ const first = text.charAt(valueStart);
1076
+ if (first === "{" || first === "[") {
1077
+ const stack = [first];
1078
+ let inString = false;
1079
+ let escaped = false;
1080
+ for (let index2 = valueStart + 1; index2 < text.length; index2 += 1) {
1081
+ const char = text.charAt(index2);
1082
+ if (inString) {
1083
+ if (escaped) {
1084
+ escaped = false;
1085
+ } else if (char === "\\") {
1086
+ escaped = true;
1087
+ } else if (char === '"') {
1088
+ inString = false;
1089
+ }
1090
+ continue;
1091
+ }
1092
+ if (char === '"') {
1093
+ inString = true;
1094
+ continue;
1095
+ }
1096
+ if (char === "{" || char === "[") {
1097
+ stack.push(char);
1098
+ continue;
1099
+ }
1100
+ if (char === "}" || char === "]") {
1101
+ const open = stack.at(-1);
1102
+ if (open === "{" && char === "}" || open === "[" && char === "]") {
1103
+ stack.pop();
1104
+ if (stack.length === 0) {
1105
+ return {
1106
+ text: text.slice(valueStart, index2 + 1),
1107
+ complete: true
1108
+ };
1109
+ }
1110
+ }
1111
+ }
1112
+ }
1113
+ return {
1114
+ text: text.slice(valueStart),
1115
+ complete: false
1116
+ };
1117
+ }
1118
+ if (first === '"') {
1119
+ let escaped = false;
1120
+ for (let index2 = valueStart + 1; index2 < text.length; index2 += 1) {
1121
+ const char = text.charAt(index2);
1122
+ if (escaped) {
1123
+ escaped = false;
1124
+ } else if (char === "\\") {
1125
+ escaped = true;
1126
+ } else if (char === '"') {
1127
+ return {
1128
+ text: text.slice(valueStart, index2 + 1),
1129
+ complete: true
1130
+ };
1131
+ }
1132
+ }
1133
+ return {
1134
+ text: text.slice(valueStart),
1135
+ complete: false
1136
+ };
1137
+ }
1138
+ let index = valueStart;
1139
+ while (index < text.length) {
1140
+ const char = text.charAt(index);
1141
+ if (char === "," || char === "}" || WHITESPACE_JSON_REGEX.test(char)) {
1142
+ break;
1143
+ }
1144
+ index += 1;
1145
+ }
1146
+ return {
1147
+ text: text.slice(valueStart, index),
1148
+ complete: index < text.length
1149
+ };
1150
+ }
1151
+ function extractStreamingToolCallProgress(toolCallJson) {
1152
+ var _a;
1153
+ const toolName = extractTopLevelStringProperty(toolCallJson, "name");
1154
+ const argsValueStart = findTopLevelPropertyValueStart(
1155
+ toolCallJson,
1156
+ "arguments"
1157
+ );
1158
+ if (argsValueStart == null) {
1159
+ return {
1160
+ toolName,
1161
+ argumentsText: void 0,
1162
+ argumentsComplete: false
1163
+ };
1164
+ }
1165
+ const argsSlice = extractJsonValueSlice(toolCallJson, argsValueStart);
1166
+ return {
1167
+ toolName,
1168
+ argumentsText: argsSlice == null ? void 0 : argsSlice.text,
1169
+ argumentsComplete: (_a = argsSlice == null ? void 0 : argsSlice.complete) != null ? _a : false
1170
+ };
1171
+ }
1172
+ function ensureToolInputStart(state, controller, toolName) {
1173
+ if (!state.activeToolInput) {
1174
+ const id = generateToolCallId();
1175
+ state.activeToolInput = {
1176
+ id,
1177
+ toolName,
1178
+ emittedInput: ""
1179
+ };
1180
+ controller.enqueue({
1181
+ type: "tool-input-start",
1182
+ id,
1183
+ toolName
1184
+ });
1185
+ }
1186
+ }
1187
+ function emitToolInputDelta(state, controller, fullInput) {
1188
+ const active = state.activeToolInput;
1189
+ if (!active) {
1190
+ return;
1191
+ }
1192
+ if (!fullInput.startsWith(active.emittedInput)) {
1193
+ return;
1194
+ }
1195
+ const delta = fullInput.slice(active.emittedInput.length);
1196
+ if (delta.length === 0) {
1197
+ return;
1198
+ }
1199
+ controller.enqueue({
1200
+ type: "tool-input-delta",
1201
+ id: active.id,
1202
+ delta
1203
+ });
1204
+ active.emittedInput = fullInput;
1205
+ }
1206
+ function closeToolInput(state, controller) {
1207
+ if (!state.activeToolInput) {
1208
+ return;
1209
+ }
1210
+ controller.enqueue({
1211
+ type: "tool-input-end",
1212
+ id: state.activeToolInput.id
1213
+ });
1214
+ state.activeToolInput = null;
1215
+ }
1216
+ function emitToolCallFromParsed(state, controller, parsedToolCall) {
1217
+ var _a, _b, _c, _d;
1218
+ closeTextBlock(state, controller);
1219
+ const toolName = typeof parsedToolCall.name === "string" ? parsedToolCall.name : (_b = (_a = state.activeToolInput) == null ? void 0 : _a.toolName) != null ? _b : "unknown";
1220
+ const input = canonicalizeToolInput(parsedToolCall.arguments);
1221
+ ensureToolInputStart(state, controller, toolName);
1222
+ emitToolInputDelta(state, controller, input);
1223
+ const toolCallId = (_d = (_c = state.activeToolInput) == null ? void 0 : _c.id) != null ? _d : generateToolCallId();
1224
+ closeToolInput(state, controller);
1225
+ controller.enqueue({
1226
+ type: "tool-call",
1227
+ toolCallId,
1228
+ toolName,
1229
+ input
1230
+ });
1231
+ }
1232
+ function canonicalizeArgumentsProgressInput(progress) {
1233
+ if (progress.argumentsText === void 0 || !progress.argumentsComplete) {
1234
+ return void 0;
1235
+ }
1236
+ try {
1237
+ const parsedArguments = parse(progress.argumentsText);
1238
+ return canonicalizeToolInput(parsedArguments);
1239
+ } catch (e) {
1240
+ return void 0;
1241
+ }
1242
+ }
1243
+ function emitToolInputProgress(state, controller) {
1244
+ if (!(state.isInsideToolCall && state.currentToolCallJson)) {
1245
+ return;
1246
+ }
1247
+ const progress = extractStreamingToolCallProgress(state.currentToolCallJson);
1248
+ if (!progress.toolName) {
1249
+ return;
1250
+ }
1251
+ ensureToolInputStart(state, controller, progress.toolName);
1252
+ const canonicalProgressInput = canonicalizeArgumentsProgressInput(progress);
1253
+ if (canonicalProgressInput !== void 0) {
1254
+ emitToolInputDelta(state, controller, canonicalProgressInput);
1255
+ }
1256
+ }
904
1257
  function flushBuffer(state, controller, toolCallStart) {
905
1258
  if (state.buffer.length === 0) {
906
1259
  return;
@@ -931,44 +1284,77 @@ function closeTextBlock(state, controller) {
931
1284
  state.hasEmittedTextStart = false;
932
1285
  }
933
1286
  }
934
- function emitIncompleteToolCall(state, controller, toolCallStart) {
935
- if (!state.currentToolCallJson) {
1287
+ function emitIncompleteToolCall(state, controller, toolCallStart, trailingBuffer, options) {
1288
+ var _a;
1289
+ if (!state.currentToolCallJson && trailingBuffer.length === 0) {
1290
+ state.isInsideToolCall = false;
936
1291
  return;
937
1292
  }
1293
+ if (state.currentToolCallJson) {
1294
+ try {
1295
+ const parsedToolCall = parse(state.currentToolCallJson);
1296
+ emitToolCallFromParsed(state, controller, parsedToolCall);
1297
+ state.currentToolCallJson = "";
1298
+ state.isInsideToolCall = false;
1299
+ return;
1300
+ } catch (e) {
1301
+ }
1302
+ }
1303
+ const rawToolCallContent = `${state.currentToolCallJson}${trailingBuffer}`;
1304
+ const errorContent = `${toolCallStart}${rawToolCallContent}`;
1305
+ const shouldEmitRawFallback = shouldEmitRawToolCallTextOnError(options);
938
1306
  logParseFailure({
939
1307
  phase: "stream",
940
- reason: "Incomplete streaming tool call segment emitted as text",
941
- snippet: `${toolCallStart}${state.currentToolCallJson}`
942
- });
943
- const errorId = generateId();
944
- const errorContent = `${toolCallStart}${state.currentToolCallJson}`;
945
- controller.enqueue({
946
- type: "text-start",
947
- id: errorId
948
- });
949
- controller.enqueue({
950
- type: "text-delta",
951
- id: errorId,
952
- delta: errorContent
953
- });
954
- controller.enqueue({
955
- type: "text-end",
956
- id: errorId
1308
+ reason: shouldEmitRawFallback ? "Incomplete streaming tool call segment emitted as text" : "Incomplete streaming tool call segment suppressed without raw text fallback",
1309
+ snippet: errorContent
957
1310
  });
1311
+ if (shouldEmitRawFallback) {
1312
+ const errorId = generateId();
1313
+ controller.enqueue({
1314
+ type: "text-start",
1315
+ id: errorId
1316
+ });
1317
+ controller.enqueue({
1318
+ type: "text-delta",
1319
+ id: errorId,
1320
+ delta: errorContent
1321
+ });
1322
+ controller.enqueue({
1323
+ type: "text-end",
1324
+ id: errorId
1325
+ });
1326
+ }
1327
+ closeToolInput(state, controller);
1328
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
1329
+ options,
1330
+ shouldEmitRawFallback ? "Could not complete streaming JSON tool call at finish; emitting original text." : "Could not complete streaming JSON tool call at finish.",
1331
+ { toolCall: errorContent }
1332
+ );
958
1333
  state.currentToolCallJson = "";
1334
+ state.isInsideToolCall = false;
959
1335
  }
960
- function handleFinishChunk(state, controller, toolCallStart, chunk) {
961
- if (state.buffer.length > 0) {
1336
+ function handleFinishChunk(state, controller, toolCallStart, options, chunk) {
1337
+ if (state.isInsideToolCall) {
1338
+ const trailingBuffer = state.buffer;
1339
+ state.buffer = "";
1340
+ emitIncompleteToolCall(
1341
+ state,
1342
+ controller,
1343
+ toolCallStart,
1344
+ trailingBuffer,
1345
+ options
1346
+ );
1347
+ } else if (state.buffer.length > 0) {
962
1348
  flushBuffer(state, controller, toolCallStart);
963
1349
  }
964
1350
  closeTextBlock(state, controller);
965
- emitIncompleteToolCall(state, controller, toolCallStart);
966
1351
  controller.enqueue(chunk);
967
1352
  }
968
1353
  function publishText(text, state, controller) {
969
1354
  if (state.isInsideToolCall) {
970
1355
  closeTextBlock(state, controller);
971
1356
  state.currentToolCallJson += text;
1357
+ emitToolInputProgress(state, controller);
972
1358
  } else if (text.length > 0) {
973
1359
  if (!state.currentTextId) {
974
1360
  state.currentTextId = generateId();
@@ -986,42 +1372,40 @@ function publishText(text, state, controller) {
986
1372
  }
987
1373
  }
988
1374
  function emitToolCall(context) {
989
- var _a, _b;
1375
+ var _a;
990
1376
  const { state, controller, toolCallStart, toolCallEnd, options } = context;
991
1377
  try {
992
1378
  const parsedToolCall = parse(state.currentToolCallJson);
993
- closeTextBlock(state, controller);
994
- controller.enqueue({
995
- type: "tool-call",
996
- toolCallId: generateId(),
997
- toolName: parsedToolCall.name,
998
- input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
999
- });
1379
+ emitToolCallFromParsed(state, controller, parsedToolCall);
1000
1380
  } catch (error) {
1381
+ const errorContent = `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`;
1382
+ const shouldEmitRawFallback = shouldEmitRawToolCallTextOnError(options);
1001
1383
  logParseFailure({
1002
1384
  phase: "stream",
1003
1385
  reason: "Failed to parse streaming tool call JSON segment",
1004
- snippet: `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`,
1386
+ snippet: errorContent,
1005
1387
  error
1006
1388
  });
1007
- const errorId = generateId();
1008
- const errorContent = `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`;
1009
- controller.enqueue({
1010
- type: "text-start",
1011
- id: errorId
1012
- });
1013
- controller.enqueue({
1014
- type: "text-delta",
1015
- id: errorId,
1016
- delta: errorContent
1017
- });
1018
- controller.enqueue({
1019
- type: "text-end",
1020
- id: errorId
1021
- });
1022
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
1389
+ if (shouldEmitRawFallback) {
1390
+ const errorId = generateId();
1391
+ controller.enqueue({
1392
+ type: "text-start",
1393
+ id: errorId
1394
+ });
1395
+ controller.enqueue({
1396
+ type: "text-delta",
1397
+ id: errorId,
1398
+ delta: errorContent
1399
+ });
1400
+ controller.enqueue({
1401
+ type: "text-end",
1402
+ id: errorId
1403
+ });
1404
+ }
1405
+ closeToolInput(state, controller);
1406
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
1023
1407
  options,
1024
- "Could not process streaming JSON tool call; emitting original text.",
1408
+ shouldEmitRawFallback ? "Could not process streaming JSON tool call; emitting original text." : "Could not process streaming JSON tool call.",
1025
1409
  {
1026
1410
  toolCall: errorContent
1027
1411
  }
@@ -1037,6 +1421,7 @@ function processTagMatch(context) {
1037
1421
  } else {
1038
1422
  state.currentToolCallJson = "";
1039
1423
  state.isInsideToolCall = true;
1424
+ state.activeToolInput = null;
1040
1425
  }
1041
1426
  }
1042
1427
  function processBufferTags(context) {
@@ -1059,8 +1444,16 @@ function processBufferTags(context) {
1059
1444
  );
1060
1445
  }
1061
1446
  }
1062
- function handlePartialTag(state, controller, toolCallStart) {
1447
+ function handlePartialTag(state, controller, toolCallStart, toolCallEnd) {
1063
1448
  if (state.isInsideToolCall) {
1449
+ const potentialEndIndex = getPotentialStartIndex(state.buffer, toolCallEnd);
1450
+ if (potentialEndIndex != null && potentialEndIndex + toolCallEnd.length > state.buffer.length) {
1451
+ publishText(state.buffer.slice(0, potentialEndIndex), state, controller);
1452
+ state.buffer = state.buffer.slice(potentialEndIndex);
1453
+ } else {
1454
+ publishText(state.buffer, state, controller);
1455
+ state.buffer = "";
1456
+ }
1064
1457
  return;
1065
1458
  }
1066
1459
  const potentialIndex = getPotentialStartIndex(state.buffer, toolCallStart);
@@ -1133,13 +1526,14 @@ var jsonProtocol = ({
1133
1526
  buffer: "",
1134
1527
  currentToolCallJson: "",
1135
1528
  currentTextId: null,
1136
- hasEmittedTextStart: false
1529
+ hasEmittedTextStart: false,
1530
+ activeToolInput: null
1137
1531
  };
1138
1532
  return new TransformStream({
1139
1533
  transform(chunk, controller) {
1140
1534
  var _a;
1141
1535
  if (chunk.type === "finish") {
1142
- handleFinishChunk(state, controller, toolCallStart, chunk);
1536
+ handleFinishChunk(state, controller, toolCallStart, options, chunk);
1143
1537
  return;
1144
1538
  }
1145
1539
  if (chunk.type !== "text-delta") {
@@ -1155,7 +1549,7 @@ var jsonProtocol = ({
1155
1549
  toolCallEnd,
1156
1550
  options
1157
1551
  });
1158
- handlePartialTag(state, controller, toolCallStart);
1552
+ handlePartialTag(state, controller, toolCallStart, toolCallEnd);
1159
1553
  }
1160
1554
  });
1161
1555
  },
@@ -1739,13 +2133,20 @@ function coerceStringWithoutSchema(value) {
1739
2133
  }
1740
2134
  function coerceStringToObject(s, unwrapped) {
1741
2135
  try {
1742
- let normalized = s.replace(/'/g, '"');
1743
- normalized = normalized.replace(EMPTY_OBJECT_REGEX, "{}");
1744
- const obj = JSON.parse(normalized);
2136
+ const obj = JSON.parse(s);
1745
2137
  if (obj && typeof obj === "object" && !Array.isArray(obj)) {
1746
2138
  return coerceObjectToObject(obj, unwrapped);
1747
2139
  }
1748
2140
  } catch (e) {
2141
+ try {
2142
+ let normalized = s.replace(/'/g, '"');
2143
+ normalized = normalized.replace(EMPTY_OBJECT_REGEX, "{}");
2144
+ const obj = JSON.parse(normalized);
2145
+ if (obj && typeof obj === "object" && !Array.isArray(obj)) {
2146
+ return coerceObjectToObject(obj, unwrapped);
2147
+ }
2148
+ } catch (e2) {
2149
+ }
1749
2150
  }
1750
2151
  return null;
1751
2152
  }
@@ -1753,8 +2154,7 @@ function coerceStringToArray(s, unwrapped) {
1753
2154
  const prefixItems = Array.isArray(unwrapped.prefixItems) ? unwrapped.prefixItems : void 0;
1754
2155
  const itemsSchema = unwrapped.items;
1755
2156
  try {
1756
- const normalized = s.replace(/'/g, '"');
1757
- const arr = JSON.parse(normalized);
2157
+ const arr = JSON.parse(s);
1758
2158
  if (Array.isArray(arr)) {
1759
2159
  if (prefixItems && arr.length === prefixItems.length) {
1760
2160
  return arr.map((v, i) => coerceBySchema(v, prefixItems[i]));
@@ -1762,6 +2162,17 @@ function coerceStringToArray(s, unwrapped) {
1762
2162
  return arr.map((v) => coerceBySchema(v, itemsSchema));
1763
2163
  }
1764
2164
  } catch (e) {
2165
+ try {
2166
+ const normalized = s.replace(/'/g, '"');
2167
+ const arr = JSON.parse(normalized);
2168
+ if (Array.isArray(arr)) {
2169
+ if (prefixItems && arr.length === prefixItems.length) {
2170
+ return arr.map((v, i) => coerceBySchema(v, prefixItems[i]));
2171
+ }
2172
+ return arr.map((v) => coerceBySchema(v, itemsSchema));
2173
+ }
2174
+ } catch (e2) {
2175
+ }
1765
2176
  const csv = s.includes("\n") ? s.split(NEWLINE_SPLIT_REGEX) : s.split(COMMA_SPLIT_REGEX);
1766
2177
  const trimmed = csv.map((x) => x.trim()).filter((x) => x.length > 0);
1767
2178
  if (prefixItems && trimmed.length === prefixItems.length) {
@@ -4132,22 +4543,96 @@ function parse3(xml, schema, options = {}) {
4132
4543
  var NAME_CHAR_RE2 = /[A-Za-z0-9_:-]/;
4133
4544
  var WHITESPACE_REGEX5 = /\s/;
4134
4545
 
4135
- // src/core/utils/xml-root-repair.ts
4136
- var XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX = /^<([A-Za-z_][A-Za-z0-9_-]*)\s*\r?\n([\s\S]+?)\r?\n\s*\/>\s*$/;
4137
- function tryRepairXmlSelfClosingRootWithBody(rawText, toolNames) {
4138
- const trimmed = rawText.trim();
4139
- if (trimmed.length === 0) {
4140
- return null;
4141
- }
4142
- const match = trimmed.match(XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX);
4143
- if (!match) {
4144
- return null;
4546
+ // src/core/utils/streamed-tool-input-delta.ts
4547
+ function emitDelta({
4548
+ controller,
4549
+ id,
4550
+ state,
4551
+ nextInput
4552
+ }) {
4553
+ if (!nextInput.startsWith(state.emittedInput)) {
4554
+ return false;
4145
4555
  }
4146
- const rootTag = match[1];
4147
- if (!toolNames.includes(rootTag)) {
4148
- return null;
4556
+ const delta = nextInput.slice(state.emittedInput.length);
4557
+ if (delta.length === 0) {
4558
+ return false;
4149
4559
  }
4150
- const body = match[2].trimEnd();
4560
+ controller.enqueue({
4561
+ type: "tool-input-delta",
4562
+ id,
4563
+ delta
4564
+ });
4565
+ state.emittedInput = nextInput;
4566
+ return true;
4567
+ }
4568
+ function toIncompleteJsonPrefix(fullJson) {
4569
+ const trimmed = fullJson.trim();
4570
+ let prefix = trimmed;
4571
+ while (prefix.endsWith("}") || prefix.endsWith("]")) {
4572
+ prefix = prefix.slice(0, -1);
4573
+ }
4574
+ prefix = prefix.trimEnd();
4575
+ if (prefix.endsWith('"')) {
4576
+ prefix = prefix.slice(0, -1);
4577
+ }
4578
+ if (prefix.length === 0) {
4579
+ if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
4580
+ return trimmed.startsWith("{") ? "{" : "[";
4581
+ }
4582
+ if (trimmed.startsWith("]")) {
4583
+ return "[";
4584
+ }
4585
+ if (trimmed.startsWith("}")) {
4586
+ return "{";
4587
+ }
4588
+ if (trimmed.startsWith('"')) {
4589
+ return '"';
4590
+ }
4591
+ return "{";
4592
+ }
4593
+ return prefix;
4594
+ }
4595
+ function emitPrefixDelta(params) {
4596
+ return emitDelta({
4597
+ ...params,
4598
+ nextInput: params.candidate
4599
+ });
4600
+ }
4601
+ function emitFinalRemainder(params) {
4602
+ var _a;
4603
+ const result = emitDelta({
4604
+ ...params,
4605
+ nextInput: params.finalFullJson
4606
+ });
4607
+ if (!result && params.state.emittedInput.length > 0) {
4608
+ (_a = params.onMismatch) == null ? void 0 : _a.call(
4609
+ params,
4610
+ "Final JSON does not extend emitted tool-input prefix",
4611
+ {
4612
+ emittedLength: params.state.emittedInput.length,
4613
+ finalLength: params.finalFullJson.length
4614
+ }
4615
+ );
4616
+ }
4617
+ return result;
4618
+ }
4619
+
4620
+ // src/core/utils/xml-root-repair.ts
4621
+ var XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX = /^<([A-Za-z_][A-Za-z0-9_-]*)\s*\r?\n([\s\S]+?)\r?\n\s*\/>\s*$/;
4622
+ function tryRepairXmlSelfClosingRootWithBody(rawText, toolNames) {
4623
+ const trimmed = rawText.trim();
4624
+ if (trimmed.length === 0) {
4625
+ return null;
4626
+ }
4627
+ const match = trimmed.match(XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX);
4628
+ if (!match) {
4629
+ return null;
4630
+ }
4631
+ const rootTag = match[1];
4632
+ if (!toolNames.includes(rootTag)) {
4633
+ return null;
4634
+ }
4635
+ const body = match[2].trimEnd();
4151
4636
  if (body.trim().length === 0 || body.includes(`</${rootTag}>`)) {
4152
4637
  return null;
4153
4638
  }
@@ -4161,6 +4646,9 @@ function getToolSchema(tools, toolName) {
4161
4646
  var _a;
4162
4647
  return (_a = tools.find((t) => t.name === toolName)) == null ? void 0 : _a.inputSchema;
4163
4648
  }
4649
+ function shouldEmitRawToolCallTextOnError2(options) {
4650
+ return (options == null ? void 0 : options.emitRawToolCallTextOnError) === true;
4651
+ }
4164
4652
  function processToolCall(params) {
4165
4653
  var _a, _b;
4166
4654
  const { toolCall, tools, options, text, processedElements, parseOptions } = params;
@@ -4173,7 +4661,7 @@ function processToolCall(params) {
4173
4661
  const parsed = parse3(toolCall.content, toolSchema, parseConfig);
4174
4662
  processedElements.push({
4175
4663
  type: "tool-call",
4176
- toolCallId: generateId(),
4664
+ toolCallId: generateToolCallId(),
4177
4665
  toolName: toolCall.toolName,
4178
4666
  input: JSON.stringify(parsed)
4179
4667
  });
@@ -4190,6 +4678,299 @@ function processToolCall(params) {
4190
4678
  processedElements.push({ type: "text", text: originalCallText });
4191
4679
  }
4192
4680
  }
4681
+ function parseXmlTagName(rawTagBody) {
4682
+ let index = 0;
4683
+ while (index < rawTagBody.length && WHITESPACE_REGEX5.test(rawTagBody[index])) {
4684
+ index += 1;
4685
+ }
4686
+ const nameStart = index;
4687
+ while (index < rawTagBody.length && NAME_CHAR_RE2.test(rawTagBody.charAt(index))) {
4688
+ index += 1;
4689
+ }
4690
+ return rawTagBody.slice(nameStart, index);
4691
+ }
4692
+ function consumeXmlSpecialSection(fragment, ltIndex) {
4693
+ if (fragment.startsWith("<!--", ltIndex)) {
4694
+ const commentEnd = fragment.indexOf("-->", ltIndex + 4);
4695
+ return commentEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: commentEnd + 3 };
4696
+ }
4697
+ if (fragment.startsWith("<![CDATA[", ltIndex)) {
4698
+ const cdataEnd = fragment.indexOf("]]>", ltIndex + 9);
4699
+ return cdataEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: cdataEnd + 3 };
4700
+ }
4701
+ if (fragment.startsWith("<?", ltIndex)) {
4702
+ const processingEnd = fragment.indexOf("?>", ltIndex + 2);
4703
+ return processingEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: processingEnd + 2 };
4704
+ }
4705
+ if (fragment.startsWith("<!", ltIndex)) {
4706
+ const declarationEnd = fragment.indexOf(">", ltIndex + 2);
4707
+ return declarationEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: declarationEnd + 1 };
4708
+ }
4709
+ return { kind: "none" };
4710
+ }
4711
+ function parseXmlTagToken(fragment, ltIndex) {
4712
+ const gtIndex = fragment.indexOf(">", ltIndex + 1);
4713
+ if (gtIndex === -1) {
4714
+ return null;
4715
+ }
4716
+ const tagBody = fragment.slice(ltIndex + 1, gtIndex).trim();
4717
+ if (tagBody.length === 0) {
4718
+ return null;
4719
+ }
4720
+ if (tagBody.startsWith("/")) {
4721
+ const closeName = parseXmlTagName(tagBody.slice(1));
4722
+ if (closeName.length === 0) {
4723
+ return null;
4724
+ }
4725
+ return { kind: "close", name: closeName, nextPos: gtIndex + 1 };
4726
+ }
4727
+ const selfClosing = tagBody.endsWith("/");
4728
+ const openBody = selfClosing ? tagBody.slice(0, -1).trimEnd() : tagBody;
4729
+ const openName = parseXmlTagName(openBody);
4730
+ if (openName.length === 0) {
4731
+ return null;
4732
+ }
4733
+ return {
4734
+ kind: "open",
4735
+ name: openName,
4736
+ selfClosing,
4737
+ nextPos: gtIndex + 1
4738
+ };
4739
+ }
4740
+ function analyzeXmlFragmentForProgress(fragment) {
4741
+ const stack = [];
4742
+ const topLevelTagNames = [];
4743
+ let position = 0;
4744
+ while (position < fragment.length) {
4745
+ const ltIndex = fragment.indexOf("<", position);
4746
+ if (ltIndex === -1) {
4747
+ break;
4748
+ }
4749
+ const special = consumeXmlSpecialSection(fragment, ltIndex);
4750
+ if (special.kind === "incomplete") {
4751
+ return null;
4752
+ }
4753
+ if (special.kind === "consumed") {
4754
+ position = special.nextPos;
4755
+ continue;
4756
+ }
4757
+ const token = parseXmlTagToken(fragment, ltIndex);
4758
+ if (token === null) {
4759
+ return null;
4760
+ }
4761
+ if (token.kind === "close") {
4762
+ const openName = stack.pop();
4763
+ if (!openName || openName !== token.name) {
4764
+ return null;
4765
+ }
4766
+ position = token.nextPos;
4767
+ continue;
4768
+ }
4769
+ if (stack.length === 0) {
4770
+ topLevelTagNames.push(token.name);
4771
+ }
4772
+ if (!token.selfClosing) {
4773
+ stack.push(token.name);
4774
+ }
4775
+ position = token.nextPos;
4776
+ }
4777
+ if (stack.length > 0) {
4778
+ return null;
4779
+ }
4780
+ return { topLevelTagNames };
4781
+ }
4782
+ function scanXmlFragmentTopLevelTextStep(options) {
4783
+ const { fragment, position, stack } = options;
4784
+ const ltIndex = fragment.indexOf("<", position);
4785
+ if (ltIndex === -1) {
4786
+ const trailingText = fragment.slice(position);
4787
+ return {
4788
+ kind: "done",
4789
+ value: stack.length === 0 && trailingText.trim().length > 0
4790
+ };
4791
+ }
4792
+ const textBetweenTags = fragment.slice(position, ltIndex);
4793
+ if (stack.length === 0 && textBetweenTags.trim().length > 0) {
4794
+ return { kind: "found" };
4795
+ }
4796
+ const special = consumeXmlSpecialSection(fragment, ltIndex);
4797
+ if (special.kind === "incomplete") {
4798
+ return { kind: "invalid" };
4799
+ }
4800
+ if (special.kind === "consumed") {
4801
+ return { kind: "next", nextPos: special.nextPos };
4802
+ }
4803
+ const token = parseXmlTagToken(fragment, ltIndex);
4804
+ if (token === null) {
4805
+ return { kind: "invalid" };
4806
+ }
4807
+ if (token.kind === "close") {
4808
+ const openName = stack.pop();
4809
+ if (!openName || openName !== token.name) {
4810
+ return { kind: "invalid" };
4811
+ }
4812
+ } else if (!token.selfClosing) {
4813
+ stack.push(token.name);
4814
+ }
4815
+ return { kind: "next", nextPos: token.nextPos };
4816
+ }
4817
+ function hasNonWhitespaceTopLevelText(fragment) {
4818
+ if (!fragment.includes("<")) {
4819
+ return fragment.trim().length > 0;
4820
+ }
4821
+ const stack = [];
4822
+ let position = 0;
4823
+ while (position < fragment.length) {
4824
+ const step = scanXmlFragmentTopLevelTextStep({ fragment, position, stack });
4825
+ if (step.kind === "found") {
4826
+ return true;
4827
+ }
4828
+ if (step.kind === "invalid") {
4829
+ return false;
4830
+ }
4831
+ if (step.kind === "done") {
4832
+ return step.value;
4833
+ }
4834
+ position = step.nextPos;
4835
+ }
4836
+ return false;
4837
+ }
4838
+ function getObjectSchemaPropertyNames(schema) {
4839
+ if (!schema || typeof schema !== "object") {
4840
+ return null;
4841
+ }
4842
+ const schemaObject = schema;
4843
+ const typeValue = schemaObject.type;
4844
+ if (typeValue != null) {
4845
+ const isObjectType = typeValue === "object" || Array.isArray(typeValue) && typeValue.includes("object");
4846
+ if (!isObjectType) {
4847
+ return null;
4848
+ }
4849
+ }
4850
+ if (!schemaObject.properties || typeof schemaObject.properties !== "object") {
4851
+ return /* @__PURE__ */ new Set();
4852
+ }
4853
+ return new Set(
4854
+ Object.keys(schemaObject.properties)
4855
+ );
4856
+ }
4857
+ function schemaAllowsArrayType(schema) {
4858
+ if (!schema || typeof schema !== "object") {
4859
+ return false;
4860
+ }
4861
+ const schemaRecord = schema;
4862
+ const typeValue = schemaRecord.type;
4863
+ if (typeValue === "array") {
4864
+ return true;
4865
+ }
4866
+ if (Array.isArray(typeValue) && typeValue.includes("array")) {
4867
+ return true;
4868
+ }
4869
+ const unions = [schemaRecord.anyOf, schemaRecord.oneOf, schemaRecord.allOf];
4870
+ for (const union of unions) {
4871
+ if (!Array.isArray(union)) {
4872
+ continue;
4873
+ }
4874
+ if (union.some((entry) => schemaAllowsArrayType(entry))) {
4875
+ return true;
4876
+ }
4877
+ }
4878
+ return false;
4879
+ }
4880
+ function getSchemaObjectProperty(schema, propertyName) {
4881
+ if (!schema || typeof schema !== "object") {
4882
+ return null;
4883
+ }
4884
+ const schemaObject = schema;
4885
+ const properties = schemaObject.properties;
4886
+ if (!properties || typeof properties !== "object") {
4887
+ return null;
4888
+ }
4889
+ const property = properties[propertyName];
4890
+ if (!property) {
4891
+ return null;
4892
+ }
4893
+ return property;
4894
+ }
4895
+ function isStableXmlProgressCandidate(options) {
4896
+ const { candidate, parsed, toolSchema } = options;
4897
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
4898
+ return false;
4899
+ }
4900
+ const structure = analyzeXmlFragmentForProgress(candidate);
4901
+ if (!structure) {
4902
+ return false;
4903
+ }
4904
+ const schemaProperties = getObjectSchemaPropertyNames(toolSchema);
4905
+ if (!schemaProperties || schemaProperties.size === 0) {
4906
+ return false;
4907
+ }
4908
+ const parsedObject = parsed;
4909
+ const uniqueTopLevelTags = new Set(structure.topLevelTagNames);
4910
+ for (const tagName of uniqueTopLevelTags) {
4911
+ if (!schemaProperties.has(tagName)) {
4912
+ continue;
4913
+ }
4914
+ const schemaProperty = getSchemaObjectProperty(toolSchema, tagName);
4915
+ if (schemaProperty && schemaAllowsArrayType(schemaProperty) && !Array.isArray(parsedObject[tagName])) {
4916
+ return false;
4917
+ }
4918
+ }
4919
+ if (structure.topLevelTagNames.length === 1) {
4920
+ const onlyTopLevelTag = structure.topLevelTagNames[0];
4921
+ if (!schemaProperties || schemaProperties.size === 0 || !schemaProperties.has(onlyTopLevelTag)) {
4922
+ return false;
4923
+ }
4924
+ }
4925
+ return true;
4926
+ }
4927
+ function parseXmlContentForStreamProgress({
4928
+ toolContent,
4929
+ toolSchema,
4930
+ parseOptions
4931
+ }) {
4932
+ const tryParse = (content) => {
4933
+ try {
4934
+ return parse3(content, toolSchema, {
4935
+ ...parseOptions != null ? parseOptions : {},
4936
+ repair: false,
4937
+ onError: void 0
4938
+ });
4939
+ } catch (e) {
4940
+ return null;
4941
+ }
4942
+ };
4943
+ const strictFull = tryParse(toolContent);
4944
+ if (strictFull !== null && isStableXmlProgressCandidate({
4945
+ candidate: toolContent,
4946
+ parsed: strictFull,
4947
+ toolSchema
4948
+ })) {
4949
+ return JSON.stringify(strictFull);
4950
+ }
4951
+ let searchEnd = toolContent.length;
4952
+ while (searchEnd > 0) {
4953
+ const gtIndex = toolContent.lastIndexOf(">", searchEnd - 1);
4954
+ if (gtIndex === -1) {
4955
+ break;
4956
+ }
4957
+ const candidate = toolContent.slice(0, gtIndex + 1);
4958
+ if (!analyzeXmlFragmentForProgress(candidate)) {
4959
+ searchEnd = gtIndex;
4960
+ continue;
4961
+ }
4962
+ const parsedCandidate = tryParse(candidate);
4963
+ if (parsedCandidate !== null && isStableXmlProgressCandidate({
4964
+ candidate,
4965
+ parsed: parsedCandidate,
4966
+ toolSchema
4967
+ })) {
4968
+ return JSON.stringify(parsedCandidate);
4969
+ }
4970
+ searchEnd = gtIndex;
4971
+ }
4972
+ return null;
4973
+ }
4193
4974
  function handleStreamingToolCallEnd(params) {
4194
4975
  var _a, _b;
4195
4976
  const {
@@ -4209,19 +4990,37 @@ function handleStreamingToolCallEnd(params) {
4209
4990
  flushText(ctrl);
4210
4991
  try {
4211
4992
  const parsedResult = parse3(toolContent, toolSchema, parseConfig);
4993
+ const finalInput = JSON.stringify(parsedResult);
4994
+ emitFinalRemainder({
4995
+ controller: ctrl,
4996
+ id: currentToolCall.toolCallId,
4997
+ state: currentToolCall,
4998
+ finalFullJson: finalInput,
4999
+ onMismatch: options == null ? void 0 : options.onError
5000
+ });
5001
+ ctrl.enqueue({
5002
+ type: "tool-input-end",
5003
+ id: currentToolCall.toolCallId
5004
+ });
4212
5005
  ctrl.enqueue({
4213
5006
  type: "tool-call",
4214
- toolCallId: generateId(),
5007
+ toolCallId: currentToolCall.toolCallId,
4215
5008
  toolName: currentToolCall.name,
4216
- input: JSON.stringify(parsedResult)
5009
+ input: finalInput
4217
5010
  });
4218
5011
  } catch (error) {
5012
+ ctrl.enqueue({
5013
+ type: "tool-input-end",
5014
+ id: currentToolCall.toolCallId
5015
+ });
4219
5016
  const original = `<${currentToolCall.name}>${toolContent}</${currentToolCall.name}>`;
4220
5017
  (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, "Could not process streaming XML tool call", {
4221
5018
  toolCall: original,
4222
5019
  error
4223
5020
  });
4224
- flushText(ctrl, original);
5021
+ if (shouldEmitRawToolCallTextOnError2(options)) {
5022
+ flushText(ctrl, original);
5023
+ }
4225
5024
  }
4226
5025
  }
4227
5026
  function findClosingTagEndFlexible(text, contentStart, toolName) {
@@ -4643,38 +5442,6 @@ function findPotentialToolTagStart(buffer, toolNames) {
4643
5442
  }
4644
5443
  return -1;
4645
5444
  }
4646
- function createFlushTextHandler(getCurrentTextId, setCurrentTextId, getHasEmittedTextStart, setHasEmittedTextStart) {
4647
- return (controller, text) => {
4648
- const content = text;
4649
- if (content) {
4650
- if (!getCurrentTextId()) {
4651
- const newId = generateId();
4652
- setCurrentTextId(newId);
4653
- controller.enqueue({
4654
- type: "text-start",
4655
- id: newId
4656
- });
4657
- setHasEmittedTextStart(true);
4658
- }
4659
- controller.enqueue({
4660
- type: "text-delta",
4661
- id: getCurrentTextId(),
4662
- delta: content
4663
- });
4664
- }
4665
- const currentTextId = getCurrentTextId();
4666
- if (currentTextId && !text) {
4667
- if (getHasEmittedTextStart()) {
4668
- controller.enqueue({
4669
- type: "text-end",
4670
- id: currentTextId
4671
- });
4672
- setHasEmittedTextStart(false);
4673
- }
4674
- setCurrentTextId(null);
4675
- }
4676
- };
4677
- }
4678
5445
  function processToolCallInBuffer(params) {
4679
5446
  const {
4680
5447
  buffer,
@@ -4684,18 +5451,21 @@ function processToolCallInBuffer(params) {
4684
5451
  controller,
4685
5452
  flushText,
4686
5453
  setBuffer,
4687
- parseOptions
5454
+ parseOptions,
5455
+ emitToolInputProgress: emitToolInputProgress2
4688
5456
  } = params;
4689
5457
  const endTagPattern = new RegExp(
4690
5458
  `</\\s*${escapeRegExp(currentToolCall.name)}\\s*>`
4691
5459
  );
4692
5460
  const endMatch = endTagPattern.exec(buffer);
4693
5461
  if (!endMatch || endMatch.index === void 0) {
5462
+ emitToolInputProgress2(controller, currentToolCall, buffer);
4694
5463
  return { buffer, currentToolCall, shouldBreak: true };
4695
5464
  }
4696
5465
  const endIdx = endMatch.index;
4697
5466
  const endPos = endIdx + endMatch[0].length;
4698
5467
  const content = buffer.substring(0, endIdx);
5468
+ emitToolInputProgress2(controller, currentToolCall, content);
4699
5469
  const remainder = buffer.substring(endPos);
4700
5470
  setBuffer(remainder);
4701
5471
  handleStreamingToolCallEnd({
@@ -4722,7 +5492,8 @@ function processNoToolCallInBuffer(params) {
4722
5492
  tools,
4723
5493
  options,
4724
5494
  parseOptions,
4725
- setBuffer
5495
+ setBuffer,
5496
+ emitToolInputStart
4726
5497
  } = params;
4727
5498
  const {
4728
5499
  index: earliestStartTagIndex,
@@ -4752,9 +5523,10 @@ function processNoToolCallInBuffer(params) {
4752
5523
  if (selfClosing) {
4753
5524
  const newBuffer2 = buffer.substring(earliestStartTagIndex + tagLength);
4754
5525
  setBuffer(newBuffer2);
5526
+ const currentToolCall = emitToolInputStart(controller, earliestToolName);
4755
5527
  handleStreamingToolCallEnd({
4756
5528
  toolContent: "",
4757
- currentToolCall: { name: earliestToolName, content: "" },
5529
+ currentToolCall,
4758
5530
  tools,
4759
5531
  options,
4760
5532
  ctrl: controller,
@@ -4773,12 +5545,12 @@ function processNoToolCallInBuffer(params) {
4773
5545
  setBuffer(newBuffer);
4774
5546
  return {
4775
5547
  buffer: newBuffer,
4776
- currentToolCall: { name: earliestToolName, content: "" },
5548
+ currentToolCall: emitToolInputStart(controller, earliestToolName),
4777
5549
  shouldBreak: false,
4778
5550
  shouldContinue: true
4779
5551
  };
4780
5552
  }
4781
- function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, setCurrentToolCall, tools, options, toolNames, flushText, parseOptions) {
5553
+ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, setCurrentToolCall, tools, options, toolNames, flushText, parseOptions, emitToolInputProgress2, emitToolInputStart) {
4782
5554
  return (controller) => {
4783
5555
  while (true) {
4784
5556
  const currentToolCall = getCurrentToolCall();
@@ -4791,7 +5563,8 @@ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, se
4791
5563
  controller,
4792
5564
  flushText,
4793
5565
  setBuffer,
4794
- parseOptions
5566
+ parseOptions,
5567
+ emitToolInputProgress: emitToolInputProgress2
4795
5568
  });
4796
5569
  setBuffer(result.buffer);
4797
5570
  setCurrentToolCall(result.currentToolCall);
@@ -4807,7 +5580,8 @@ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, se
4807
5580
  tools,
4808
5581
  options,
4809
5582
  parseOptions,
4810
- setBuffer
5583
+ setBuffer,
5584
+ emitToolInputStart
4811
5585
  });
4812
5586
  setBuffer(result.buffer);
4813
5587
  setCurrentToolCall(result.currentToolCall);
@@ -4921,6 +5695,118 @@ var xmlProtocol = (protocolOptions) => {
4921
5695
  hasEmittedTextStart = value;
4922
5696
  }
4923
5697
  );
5698
+ const emitToolInputStart = (controller, toolName) => {
5699
+ flushText(controller);
5700
+ const next = {
5701
+ name: toolName,
5702
+ toolCallId: generateToolCallId(),
5703
+ emittedInput: "",
5704
+ lastProgressGtIndex: null,
5705
+ lastProgressFullInput: null
5706
+ };
5707
+ controller.enqueue({
5708
+ type: "tool-input-start",
5709
+ id: next.toolCallId,
5710
+ toolName
5711
+ });
5712
+ return next;
5713
+ };
5714
+ const emitToolInputProgress2 = (controller, toolCall, toolContent) => {
5715
+ const progressGtIndex = toolContent.lastIndexOf(">");
5716
+ if (toolCall.lastProgressGtIndex === progressGtIndex) {
5717
+ const cached = toolCall.lastProgressFullInput;
5718
+ if (cached == null) {
5719
+ return;
5720
+ }
5721
+ if (cached === "{}" && toolContent.trim().length === 0) {
5722
+ return;
5723
+ }
5724
+ const prefixCandidate2 = toIncompleteJsonPrefix(cached);
5725
+ emitPrefixDelta({
5726
+ controller,
5727
+ id: toolCall.toolCallId,
5728
+ state: toolCall,
5729
+ candidate: prefixCandidate2
5730
+ });
5731
+ return;
5732
+ }
5733
+ const toolSchema = getToolSchema(tools, toolCall.name);
5734
+ const fullInput = parseXmlContentForStreamProgress({
5735
+ toolContent,
5736
+ toolSchema,
5737
+ parseOptions
5738
+ });
5739
+ toolCall.lastProgressGtIndex = progressGtIndex;
5740
+ toolCall.lastProgressFullInput = fullInput;
5741
+ if (fullInput == null) {
5742
+ return;
5743
+ }
5744
+ if (fullInput === "{}" && toolContent.trim().length === 0) {
5745
+ return;
5746
+ }
5747
+ const prefixCandidate = toIncompleteJsonPrefix(fullInput);
5748
+ emitPrefixDelta({
5749
+ controller,
5750
+ id: toolCall.toolCallId,
5751
+ state: toolCall,
5752
+ candidate: prefixCandidate
5753
+ });
5754
+ };
5755
+ const finalizeUnclosedToolCall = (controller) => {
5756
+ var _a2, _b;
5757
+ if (!currentToolCall) {
5758
+ return;
5759
+ }
5760
+ emitToolInputProgress2(controller, currentToolCall, buffer);
5761
+ const parseConfig = {
5762
+ ...parseOptions,
5763
+ onError: (_a2 = options == null ? void 0 : options.onError) != null ? _a2 : parseOptions == null ? void 0 : parseOptions.onError
5764
+ };
5765
+ const toolSchema = getToolSchema(tools, currentToolCall.name);
5766
+ flushText(controller);
5767
+ try {
5768
+ if (hasNonWhitespaceTopLevelText(buffer)) {
5769
+ throw new Error(
5770
+ "Cannot reconcile unclosed XML tool call with top-level plain text."
5771
+ );
5772
+ }
5773
+ const parsedResult = parse3(buffer, toolSchema, parseConfig);
5774
+ const finalInput = JSON.stringify(parsedResult);
5775
+ emitFinalRemainder({
5776
+ controller,
5777
+ id: currentToolCall.toolCallId,
5778
+ state: currentToolCall,
5779
+ finalFullJson: finalInput,
5780
+ onMismatch: options == null ? void 0 : options.onError
5781
+ });
5782
+ controller.enqueue({
5783
+ type: "tool-input-end",
5784
+ id: currentToolCall.toolCallId
5785
+ });
5786
+ controller.enqueue({
5787
+ type: "tool-call",
5788
+ toolCallId: currentToolCall.toolCallId,
5789
+ toolName: currentToolCall.name,
5790
+ input: finalInput
5791
+ });
5792
+ } catch (error) {
5793
+ controller.enqueue({
5794
+ type: "tool-input-end",
5795
+ id: currentToolCall.toolCallId
5796
+ });
5797
+ const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
5798
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
5799
+ options,
5800
+ "Could not complete streaming XML tool call at finish.",
5801
+ { toolCall: unfinishedContent, error }
5802
+ );
5803
+ if (shouldEmitRawToolCallTextOnError2(options)) {
5804
+ flushText(controller, unfinishedContent);
5805
+ }
5806
+ }
5807
+ buffer = "";
5808
+ currentToolCall = null;
5809
+ };
4924
5810
  const processBuffer = createProcessBufferHandler(
4925
5811
  () => buffer,
4926
5812
  (newBuffer) => {
@@ -4934,17 +5820,17 @@ var xmlProtocol = (protocolOptions) => {
4934
5820
  options,
4935
5821
  toolNames,
4936
5822
  flushText,
4937
- parseOptions
5823
+ parseOptions,
5824
+ emitToolInputProgress2,
5825
+ emitToolInputStart
4938
5826
  );
4939
5827
  return new TransformStream({
5828
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Stateful stream parsing requires branching over chunk lifecycle and parser states.
4940
5829
  transform(chunk, controller) {
4941
5830
  var _a2;
4942
5831
  if (chunk.type === "finish") {
4943
5832
  if (currentToolCall) {
4944
- const unfinishedContent = `<${currentToolCall.name}>${currentToolCall.content}${buffer}`;
4945
- flushText(controller, unfinishedContent);
4946
- buffer = "";
4947
- currentToolCall = null;
5833
+ finalizeUnclosedToolCall(controller);
4948
5834
  } else if (buffer) {
4949
5835
  flushText(controller, buffer);
4950
5836
  buffer = "";
@@ -4954,7 +5840,8 @@ var xmlProtocol = (protocolOptions) => {
4954
5840
  return;
4955
5841
  }
4956
5842
  if (chunk.type !== "text-delta") {
4957
- if (buffer) {
5843
+ if (currentToolCall) {
5844
+ } else if (buffer) {
4958
5845
  flushText(controller, buffer);
4959
5846
  buffer = "";
4960
5847
  }
@@ -4967,10 +5854,7 @@ var xmlProtocol = (protocolOptions) => {
4967
5854
  },
4968
5855
  flush(controller) {
4969
5856
  if (currentToolCall) {
4970
- const unfinishedContent = `<${currentToolCall.name}>${currentToolCall.content || ""}${buffer}`;
4971
- flushText(controller, unfinishedContent);
4972
- buffer = "";
4973
- currentToolCall = null;
5857
+ finalizeUnclosedToolCall(controller);
4974
5858
  } else if (buffer) {
4975
5859
  flushText(controller, buffer);
4976
5860
  buffer = "";
@@ -4998,7 +5882,205 @@ var xmlProtocol = (protocolOptions) => {
4998
5882
 
4999
5883
  // src/core/protocols/yaml-protocol.ts
5000
5884
  var import_yaml = __toESM(require("yaml"), 1);
5885
+ function shouldEmitRawToolCallTextOnError3(options) {
5886
+ return (options == null ? void 0 : options.emitRawToolCallTextOnError) === true;
5887
+ }
5888
+ var selfClosingTagCache2 = /* @__PURE__ */ new Map();
5889
+ function getSelfClosingTagPattern2(toolName) {
5890
+ let pattern = selfClosingTagCache2.get(toolName);
5891
+ if (!pattern) {
5892
+ pattern = new RegExp(`<\\s*${escapeRegExp(toolName)}\\s*/>`, "g");
5893
+ selfClosingTagCache2.set(toolName, pattern);
5894
+ }
5895
+ return pattern;
5896
+ }
5001
5897
  var LEADING_WHITESPACE_RE = /^(\s*)/;
5898
+ var INCOMPLETE_MAPPING_TAIL_RE = /^[^:[\]{}-][^:]*:\s*$/;
5899
+ var INCOMPLETE_SEQUENCE_TAIL_RE = /^-\s*$/;
5900
+ var BLOCK_SCALAR_KEY_RE = /:\s*[|>][-+0-9]*\s*$/;
5901
+ var PLAIN_MAPPING_VALUE_RE = /^[^:[\]{}-][^:]*:\s*(.+)$/;
5902
+ var PLAIN_SEQUENCE_VALUE_RE = /^-\s+(.+)$/;
5903
+ function normalizeYamlContent(yamlContent) {
5904
+ let normalized = yamlContent;
5905
+ if (normalized.startsWith("\n")) {
5906
+ normalized = normalized.slice(1);
5907
+ }
5908
+ const lines = normalized.split("\n");
5909
+ const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
5910
+ if (nonEmptyLines.length === 0) {
5911
+ return { normalized: "", nonEmptyLines };
5912
+ }
5913
+ const minIndent = Math.min(
5914
+ ...nonEmptyLines.map((line) => {
5915
+ const match = line.match(LEADING_WHITESPACE_RE);
5916
+ return match ? match[1].length : 0;
5917
+ })
5918
+ );
5919
+ if (minIndent > 0) {
5920
+ normalized = lines.map((line) => line.slice(minIndent)).join("\n");
5921
+ }
5922
+ return { normalized, nonEmptyLines };
5923
+ }
5924
+ function parseYamlDocumentAsMapping(normalized) {
5925
+ try {
5926
+ const doc = import_yaml.default.parseDocument(normalized);
5927
+ const errors = doc.errors.map((e) => e.message);
5928
+ const result = doc.toJSON();
5929
+ if (result === null) {
5930
+ return { value: {}, errors };
5931
+ }
5932
+ if (typeof result !== "object" || Array.isArray(result)) {
5933
+ return { value: null, errors };
5934
+ }
5935
+ return { value: result, errors };
5936
+ } catch (error) {
5937
+ return {
5938
+ value: null,
5939
+ errors: [
5940
+ error instanceof Error ? error.message : "Unknown YAML parsing error"
5941
+ ]
5942
+ };
5943
+ }
5944
+ }
5945
+ function getLastMeaningfulLineInfo(input) {
5946
+ var _a;
5947
+ const lines = input.split("\n");
5948
+ let index = lines.length - 1;
5949
+ while (index >= 0) {
5950
+ const raw = (_a = lines[index]) != null ? _a : "";
5951
+ const trimmed = raw.trim();
5952
+ if (trimmed.length > 0 && !trimmed.startsWith("#")) {
5953
+ return {
5954
+ index,
5955
+ raw,
5956
+ trimmed,
5957
+ indent: raw.length - raw.trimStart().length
5958
+ };
5959
+ }
5960
+ index -= 1;
5961
+ }
5962
+ return null;
5963
+ }
5964
+ function dropLastMeaningfulLine(input) {
5965
+ const lineInfo = getLastMeaningfulLineInfo(input);
5966
+ if (!lineInfo) {
5967
+ return null;
5968
+ }
5969
+ return input.split("\n").slice(0, lineInfo.index).join("\n").trimEnd();
5970
+ }
5971
+ function hasIncompleteMappingTail(normalized) {
5972
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
5973
+ if (!lineInfo) {
5974
+ return false;
5975
+ }
5976
+ return INCOMPLETE_MAPPING_TAIL_RE.test(lineInfo.trimmed);
5977
+ }
5978
+ function hasIncompleteSequenceTail(normalized) {
5979
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
5980
+ if (!lineInfo) {
5981
+ return false;
5982
+ }
5983
+ return INCOMPLETE_SEQUENCE_TAIL_RE.test(lineInfo.trimmed);
5984
+ }
5985
+ function hasSplitNestedKeyTail(normalized) {
5986
+ var _a;
5987
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
5988
+ if (!lineInfo) {
5989
+ return false;
5990
+ }
5991
+ const { trimmed, indent, index } = lineInfo;
5992
+ if (indent === 0) {
5993
+ return false;
5994
+ }
5995
+ if (trimmed.startsWith("#") || trimmed.startsWith("-") || trimmed.includes(":")) {
5996
+ return false;
5997
+ }
5998
+ const lines = normalized.split("\n");
5999
+ let parentIndex = index - 1;
6000
+ while (parentIndex >= 0) {
6001
+ const parentRaw = (_a = lines[parentIndex]) != null ? _a : "";
6002
+ const parentTrimmed = parentRaw.trim();
6003
+ if (parentTrimmed.length === 0 || parentTrimmed.startsWith("#")) {
6004
+ parentIndex -= 1;
6005
+ continue;
6006
+ }
6007
+ const parentIndent = parentRaw.length - parentRaw.trimStart().length;
6008
+ if (parentIndent >= indent) {
6009
+ parentIndex -= 1;
6010
+ continue;
6011
+ }
6012
+ if (!parentTrimmed.endsWith(":")) {
6013
+ return false;
6014
+ }
6015
+ if (BLOCK_SCALAR_KEY_RE.test(parentTrimmed)) {
6016
+ return false;
6017
+ }
6018
+ return true;
6019
+ }
6020
+ return false;
6021
+ }
6022
+ function extractTrailingPlainScalarValue(line) {
6023
+ var _a;
6024
+ if (BLOCK_SCALAR_KEY_RE.test(line)) {
6025
+ return null;
6026
+ }
6027
+ const mappingMatch = line.match(PLAIN_MAPPING_VALUE_RE);
6028
+ const sequenceMatch = line.match(PLAIN_SEQUENCE_VALUE_RE);
6029
+ const value = (_a = mappingMatch == null ? void 0 : mappingMatch[1]) != null ? _a : sequenceMatch == null ? void 0 : sequenceMatch[1];
6030
+ if (!value) {
6031
+ return null;
6032
+ }
6033
+ const trimmedValue = value.trim();
6034
+ if (trimmedValue.length === 0) {
6035
+ return null;
6036
+ }
6037
+ if (trimmedValue.startsWith('"') || trimmedValue.startsWith("'")) {
6038
+ return null;
6039
+ }
6040
+ if (trimmedValue.startsWith("{") || trimmedValue.startsWith("[") || trimmedValue.startsWith("|") || trimmedValue.startsWith(">")) {
6041
+ return null;
6042
+ }
6043
+ return trimmedValue;
6044
+ }
6045
+ function hasUnterminatedPlainScalarTail(normalized) {
6046
+ if (normalized.endsWith("\n")) {
6047
+ return false;
6048
+ }
6049
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
6050
+ if (!lineInfo) {
6051
+ return false;
6052
+ }
6053
+ return extractTrailingPlainScalarValue(lineInfo.trimmed) != null;
6054
+ }
6055
+ function hasUnstableProgressTail(normalized) {
6056
+ return hasIncompleteMappingTail(normalized) || hasIncompleteSequenceTail(normalized) || hasSplitNestedKeyTail(normalized) || hasUnterminatedPlainScalarTail(normalized);
6057
+ }
6058
+ function trimTrailingNewlineInUnknown(value) {
6059
+ if (typeof value === "string") {
6060
+ if (value.endsWith("\n")) {
6061
+ return value.slice(0, -1);
6062
+ }
6063
+ return value;
6064
+ }
6065
+ if (Array.isArray(value)) {
6066
+ return value.map((item) => trimTrailingNewlineInUnknown(item));
6067
+ }
6068
+ if (value && typeof value === "object") {
6069
+ return Object.fromEntries(
6070
+ Object.entries(value).map(([key, item]) => [
6071
+ key,
6072
+ trimTrailingNewlineInUnknown(item)
6073
+ ])
6074
+ );
6075
+ }
6076
+ return value;
6077
+ }
6078
+ function stabilizeParsedValueForStreamProgress(value, source) {
6079
+ if (source.endsWith("\n")) {
6080
+ return value;
6081
+ }
6082
+ return trimTrailingNewlineInUnknown(value);
6083
+ }
5002
6084
  function findClosingTagEnd(text, contentStart, toolName) {
5003
6085
  let pos = contentStart;
5004
6086
  let depth = 1;
@@ -5070,9 +6152,9 @@ function findEarliestTagPosition(openIdx, selfIdx) {
5070
6152
  function collectToolCallsForName(text, toolName) {
5071
6153
  const toolCalls = [];
5072
6154
  let searchIndex = 0;
5073
- const selfTagRegex = new RegExp(`<${toolName}\\s*/>`, "g");
6155
+ const startTag = `<${toolName}>`;
6156
+ const selfTagRegex = getSelfClosingTagPattern2(toolName);
5074
6157
  while (searchIndex < text.length) {
5075
- const startTag = `<${toolName}>`;
5076
6158
  const openIdx = text.indexOf(startTag, searchIndex);
5077
6159
  selfTagRegex.lastIndex = searchIndex;
5078
6160
  const selfMatch = selfTagRegex.exec(text);
@@ -5122,47 +6204,48 @@ function findToolCalls2(text, toolNames) {
5122
6204
  return toolCalls.sort((a, b) => a.startIndex - b.startIndex);
5123
6205
  }
5124
6206
  function parseYamlContent(yamlContent, options) {
5125
- var _a, _b, _c;
5126
- let normalized = yamlContent;
5127
- if (normalized.startsWith("\n")) {
5128
- normalized = normalized.slice(1);
5129
- }
5130
- const lines = normalized.split("\n");
5131
- const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
6207
+ var _a, _b;
6208
+ const { normalized, nonEmptyLines } = normalizeYamlContent(yamlContent);
5132
6209
  if (nonEmptyLines.length === 0) {
5133
6210
  return {};
5134
6211
  }
5135
- const minIndent = Math.min(
5136
- ...nonEmptyLines.map((line) => {
5137
- const match = line.match(LEADING_WHITESPACE_RE);
5138
- return match ? match[1].length : 0;
5139
- })
5140
- );
5141
- if (minIndent > 0) {
5142
- normalized = lines.map((line) => line.slice(minIndent)).join("\n");
6212
+ const parsed = parseYamlDocumentAsMapping(normalized);
6213
+ if (parsed.errors.length > 0) {
6214
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "YAML parse error", {
6215
+ errors: parsed.errors
6216
+ });
6217
+ return null;
5143
6218
  }
5144
- try {
5145
- const doc = import_yaml.default.parseDocument(normalized);
5146
- if (doc.errors && doc.errors.length > 0) {
5147
- (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "YAML parse error", {
5148
- errors: doc.errors.map((e) => e.message)
5149
- });
5150
- return null;
6219
+ if (parsed.value === null) {
6220
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, "YAML content must be a key-value mapping", {
6221
+ got: "non-mapping"
6222
+ });
6223
+ return null;
6224
+ }
6225
+ return parsed.value;
6226
+ }
6227
+ function parseYamlContentForStreamProgress(yamlContent) {
6228
+ const { normalized, nonEmptyLines } = normalizeYamlContent(yamlContent);
6229
+ if (nonEmptyLines.length === 0) {
6230
+ return {};
6231
+ }
6232
+ let candidate = normalized;
6233
+ while (true) {
6234
+ const parsed = parseYamlDocumentAsMapping(candidate);
6235
+ if (parsed.errors.length === 0 && !hasUnstableProgressTail(candidate)) {
6236
+ if (candidate.trim().length === 0 && normalized.trim().length > 0) {
6237
+ return null;
6238
+ }
6239
+ return stabilizeParsedValueForStreamProgress(parsed.value, candidate);
5151
6240
  }
5152
- const result = doc.toJSON();
5153
- if (result === null) {
5154
- return {};
6241
+ const truncated = dropLastMeaningfulLine(candidate);
6242
+ if (truncated == null) {
6243
+ return null;
5155
6244
  }
5156
- if (typeof result !== "object" || Array.isArray(result)) {
5157
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, "YAML content must be a key-value mapping", {
5158
- got: typeof result
5159
- });
6245
+ if (truncated === candidate) {
5160
6246
  return null;
5161
6247
  }
5162
- return result;
5163
- } catch (error) {
5164
- (_c = options == null ? void 0 : options.onError) == null ? void 0 : _c.call(options, "Failed to parse YAML content", { error });
5165
- return null;
6248
+ candidate = truncated;
5166
6249
  }
5167
6250
  }
5168
6251
  function processToolCallMatch(text, tc, currentIndex, processedElements, options) {
@@ -5178,7 +6261,7 @@ function processToolCallMatch(text, tc, currentIndex, processedElements, options
5178
6261
  if (parsedArgs !== null) {
5179
6262
  processedElements.push({
5180
6263
  type: "tool-call",
5181
- toolCallId: generateId(),
6264
+ toolCallId: generateToolCallId(),
5182
6265
  toolName: tc.toolName,
5183
6266
  input: JSON.stringify(parsedArgs)
5184
6267
  });
@@ -5191,38 +6274,6 @@ function processToolCallMatch(text, tc, currentIndex, processedElements, options
5191
6274
  }
5192
6275
  return tc.endIndex;
5193
6276
  }
5194
- function createFlushTextHandler2(getCurrentTextId, setCurrentTextId, getHasEmittedTextStart, setHasEmittedTextStart) {
5195
- return (controller, text) => {
5196
- const content = text;
5197
- if (content) {
5198
- if (!getCurrentTextId()) {
5199
- const newId = generateId();
5200
- setCurrentTextId(newId);
5201
- controller.enqueue({
5202
- type: "text-start",
5203
- id: newId
5204
- });
5205
- setHasEmittedTextStart(true);
5206
- }
5207
- controller.enqueue({
5208
- type: "text-delta",
5209
- id: getCurrentTextId(),
5210
- delta: content
5211
- });
5212
- }
5213
- const currentTextId = getCurrentTextId();
5214
- if (currentTextId && !text) {
5215
- if (getHasEmittedTextStart()) {
5216
- controller.enqueue({
5217
- type: "text-end",
5218
- id: currentTextId
5219
- });
5220
- setHasEmittedTextStart(false);
5221
- }
5222
- setCurrentTextId(null);
5223
- }
5224
- };
5225
- }
5226
6277
  function findEarliestToolTag2(buffer, toolNames) {
5227
6278
  let bestIndex = -1;
5228
6279
  let bestName = "";
@@ -5230,8 +6281,9 @@ function findEarliestToolTag2(buffer, toolNames) {
5230
6281
  let bestTagLength = 0;
5231
6282
  for (const name of toolNames) {
5232
6283
  const openTag = `<${name}>`;
5233
- const selfTagRegex = new RegExp(`<${name}\\s*/>`);
6284
+ const selfTagRegex = getSelfClosingTagPattern2(name);
5234
6285
  const idxOpen = buffer.indexOf(openTag);
6286
+ selfTagRegex.lastIndex = 0;
5235
6287
  const selfMatch = selfTagRegex.exec(buffer);
5236
6288
  const idxSelf = selfMatch ? selfMatch.index : -1;
5237
6289
  if (idxOpen !== -1 && (bestIndex === -1 || idxOpen < bestIndex)) {
@@ -5254,6 +6306,29 @@ function findEarliestToolTag2(buffer, toolNames) {
5254
6306
  tagLength: bestTagLength
5255
6307
  };
5256
6308
  }
6309
+ function stripTrailingPartialCloseTag(content, toolName) {
6310
+ const closeTag = `</${toolName}>`;
6311
+ const lastLineBreakIndex = Math.max(
6312
+ content.lastIndexOf("\n"),
6313
+ content.lastIndexOf("\r")
6314
+ );
6315
+ const lineStartIndex = lastLineBreakIndex === -1 ? 0 : lastLineBreakIndex + 1;
6316
+ const trailingLine = content.slice(lineStartIndex);
6317
+ const trimmedTrailingLine = trailingLine.trim();
6318
+ if (trimmedTrailingLine.length === 0 || !trimmedTrailingLine.startsWith("</") || trimmedTrailingLine === closeTag || !closeTag.startsWith(trimmedTrailingLine)) {
6319
+ return content;
6320
+ }
6321
+ const leadingWhitespaceLength = trailingLine.length - trailingLine.trimStart().length;
6322
+ const preservedLeadingWhitespace = trailingLine.slice(
6323
+ 0,
6324
+ leadingWhitespaceLength
6325
+ );
6326
+ const contentWithoutPartial = `${content.slice(
6327
+ 0,
6328
+ lineStartIndex
6329
+ )}${preservedLeadingWhitespace}`;
6330
+ return contentWithoutPartial.trimEnd();
6331
+ }
5257
6332
  var yamlProtocol = (_protocolOptions) => {
5258
6333
  return {
5259
6334
  formatTools({ tools, toolSystemPromptTemplate }) {
@@ -5314,7 +6389,7 @@ ${yamlContent}</${toolCall.toolName}>`;
5314
6389
  let currentToolCall = null;
5315
6390
  let currentTextId = null;
5316
6391
  let hasEmittedTextStart = false;
5317
- const flushText = createFlushTextHandler2(
6392
+ const flushText = createFlushTextHandler(
5318
6393
  () => currentTextId,
5319
6394
  (newId) => {
5320
6395
  currentTextId = newId;
@@ -5324,33 +6399,128 @@ ${yamlContent}</${toolCall.toolName}>`;
5324
6399
  hasEmittedTextStart = value;
5325
6400
  }
5326
6401
  );
5327
- const processToolCallEnd = (controller, toolContent, toolName) => {
6402
+ const emitToolInputProgress2 = (controller, toolContent) => {
6403
+ if (!currentToolCall) {
6404
+ return;
6405
+ }
6406
+ const parsedArgs = parseYamlContentForStreamProgress(toolContent);
6407
+ if (parsedArgs === null) {
6408
+ return;
6409
+ }
6410
+ const fullInput = JSON.stringify(parsedArgs);
6411
+ if (fullInput === "{}" && toolContent.trim().length === 0) {
6412
+ return;
6413
+ }
6414
+ const prefixCandidate = toIncompleteJsonPrefix(fullInput);
6415
+ emitPrefixDelta({
6416
+ controller,
6417
+ id: currentToolCall.toolCallId,
6418
+ state: currentToolCall,
6419
+ candidate: prefixCandidate
6420
+ });
6421
+ };
6422
+ const processToolCallEnd = (controller, toolContent, toolName, toolCallId) => {
5328
6423
  var _a;
5329
6424
  const parsedArgs = parseYamlContent(toolContent, options);
5330
6425
  flushText(controller);
5331
6426
  if (parsedArgs !== null) {
6427
+ const finalInput = JSON.stringify(parsedArgs);
6428
+ if (currentToolCall && currentToolCall.toolCallId === toolCallId) {
6429
+ emitFinalRemainder({
6430
+ controller,
6431
+ id: toolCallId,
6432
+ state: currentToolCall,
6433
+ finalFullJson: finalInput,
6434
+ onMismatch: options == null ? void 0 : options.onError
6435
+ });
6436
+ }
6437
+ controller.enqueue({
6438
+ type: "tool-input-end",
6439
+ id: toolCallId
6440
+ });
5332
6441
  controller.enqueue({
5333
6442
  type: "tool-call",
5334
- toolCallId: generateId(),
6443
+ toolCallId,
5335
6444
  toolName,
5336
- input: JSON.stringify(parsedArgs)
6445
+ input: finalInput
5337
6446
  });
5338
6447
  } else {
6448
+ controller.enqueue({
6449
+ type: "tool-input-end",
6450
+ id: toolCallId
6451
+ });
5339
6452
  const original = `<${toolName}>${toolContent}</${toolName}>`;
5340
6453
  (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "Could not parse streaming YAML tool call", {
5341
6454
  toolCall: original
5342
6455
  });
5343
- flushText(controller, original);
6456
+ if (shouldEmitRawToolCallTextOnError3(options)) {
6457
+ flushText(controller, original);
6458
+ }
5344
6459
  }
5345
6460
  };
6461
+ const finalizeUnclosedToolCall = (controller) => {
6462
+ var _a;
6463
+ if (!currentToolCall) {
6464
+ return;
6465
+ }
6466
+ emitToolInputProgress2(controller, buffer);
6467
+ const { name: toolName, toolCallId } = currentToolCall;
6468
+ const reconciledBuffer = stripTrailingPartialCloseTag(buffer, toolName);
6469
+ const parsedArgs = parseYamlContent(reconciledBuffer, options);
6470
+ flushText(controller);
6471
+ if (parsedArgs !== null) {
6472
+ const finalInput = JSON.stringify(parsedArgs);
6473
+ emitFinalRemainder({
6474
+ controller,
6475
+ id: toolCallId,
6476
+ state: currentToolCall,
6477
+ finalFullJson: finalInput,
6478
+ onMismatch: options == null ? void 0 : options.onError
6479
+ });
6480
+ controller.enqueue({
6481
+ type: "tool-input-end",
6482
+ id: toolCallId
6483
+ });
6484
+ controller.enqueue({
6485
+ type: "tool-call",
6486
+ toolCallId,
6487
+ toolName,
6488
+ input: finalInput
6489
+ });
6490
+ } else {
6491
+ controller.enqueue({
6492
+ type: "tool-input-end",
6493
+ id: toolCallId
6494
+ });
6495
+ const unfinishedContent = `<${toolName}>${buffer}`;
6496
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
6497
+ options,
6498
+ "Could not complete streaming YAML tool call at finish.",
6499
+ { toolCall: unfinishedContent }
6500
+ );
6501
+ if (shouldEmitRawToolCallTextOnError3(options)) {
6502
+ flushText(controller, unfinishedContent);
6503
+ }
6504
+ }
6505
+ buffer = "";
6506
+ currentToolCall = null;
6507
+ };
5346
6508
  const handlePendingToolCall = (controller, endTag, toolName) => {
6509
+ var _a;
5347
6510
  const endIdx = buffer.indexOf(endTag);
5348
6511
  if (endIdx === -1) {
6512
+ emitToolInputProgress2(controller, buffer);
5349
6513
  return false;
5350
6514
  }
5351
6515
  const content = buffer.substring(0, endIdx);
6516
+ emitToolInputProgress2(controller, content);
5352
6517
  buffer = buffer.substring(endIdx + endTag.length);
5353
- processToolCallEnd(controller, content, toolName);
6518
+ processToolCallEnd(
6519
+ controller,
6520
+ content,
6521
+ toolName,
6522
+ (_a = currentToolCall == null ? void 0 : currentToolCall.toolCallId) != null ? _a : generateToolCallId()
6523
+ );
5354
6524
  currentToolCall = null;
5355
6525
  return true;
5356
6526
  };
@@ -5367,13 +6537,35 @@ ${yamlContent}</${toolCall.toolName}>`;
5367
6537
  if (tagIndex > 0) {
5368
6538
  flushText(controller, buffer.substring(0, tagIndex));
5369
6539
  }
6540
+ flushText(controller);
5370
6541
  if (selfClosing) {
5371
6542
  buffer = buffer.substring(tagIndex + tagLength);
5372
- processToolCallEnd(controller, "", tagName);
6543
+ const toolCallId = generateToolCallId();
6544
+ currentToolCall = {
6545
+ name: tagName,
6546
+ toolCallId,
6547
+ emittedInput: ""
6548
+ };
6549
+ controller.enqueue({
6550
+ type: "tool-input-start",
6551
+ id: toolCallId,
6552
+ toolName: tagName
6553
+ });
6554
+ processToolCallEnd(controller, "", tagName, toolCallId);
6555
+ currentToolCall = null;
5373
6556
  } else {
5374
6557
  const startTag = `<${tagName}>`;
5375
6558
  buffer = buffer.substring(tagIndex + startTag.length);
5376
- currentToolCall = { name: tagName, content: "" };
6559
+ currentToolCall = {
6560
+ name: tagName,
6561
+ toolCallId: generateToolCallId(),
6562
+ emittedInput: ""
6563
+ };
6564
+ controller.enqueue({
6565
+ type: "tool-input-start",
6566
+ id: currentToolCall.toolCallId,
6567
+ toolName: tagName
6568
+ });
5377
6569
  }
5378
6570
  };
5379
6571
  const processBuffer = (controller) => {
@@ -5402,10 +6594,7 @@ ${yamlContent}</${toolCall.toolName}>`;
5402
6594
  var _a;
5403
6595
  if (chunk.type === "finish") {
5404
6596
  if (currentToolCall) {
5405
- const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
5406
- flushText(controller, unfinishedContent);
5407
- buffer = "";
5408
- currentToolCall = null;
6597
+ finalizeUnclosedToolCall(controller);
5409
6598
  } else if (buffer) {
5410
6599
  flushText(controller, buffer);
5411
6600
  buffer = "";
@@ -5415,7 +6604,7 @@ ${yamlContent}</${toolCall.toolName}>`;
5415
6604
  return;
5416
6605
  }
5417
6606
  if (chunk.type !== "text-delta") {
5418
- if (buffer) {
6607
+ if (!currentToolCall && buffer) {
5419
6608
  flushText(controller, buffer);
5420
6609
  buffer = "";
5421
6610
  }
@@ -5428,10 +6617,7 @@ ${yamlContent}</${toolCall.toolName}>`;
5428
6617
  },
5429
6618
  flush(controller) {
5430
6619
  if (currentToolCall) {
5431
- const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
5432
- flushText(controller, unfinishedContent);
5433
- buffer = "";
5434
- currentToolCall = null;
6620
+ finalizeUnclosedToolCall(controller);
5435
6621
  } else if (buffer) {
5436
6622
  flushText(controller, buffer);
5437
6623
  buffer = "";
@@ -5594,9 +6780,6 @@ function isToolChoiceActive(params) {
5594
6780
  return !!(typeof params.providerOptions === "object" && params.providerOptions !== null && typeof ((_c = params.providerOptions) == null ? void 0 : _c.toolCallMiddleware) === "object" && toolChoice && typeof toolChoice === "object" && (toolChoice.type === "tool" || toolChoice.type === "required"));
5595
6781
  }
5596
6782
 
5597
- // src/generate-handler.ts
5598
- var import_provider_utils = require("@ai-sdk/provider-utils");
5599
-
5600
6783
  // src/core/utils/generated-text-json-recovery.ts
5601
6784
  function isRecord(value) {
5602
6785
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -5735,7 +6918,7 @@ function mergeJsonCandidatesByStart(tagged, codeBlocks, balanced) {
5735
6918
  function toToolCallPart(candidate) {
5736
6919
  return {
5737
6920
  type: "tool-call",
5738
- toolCallId: generateId(),
6921
+ toolCallId: generateToolCallId(),
5739
6922
  toolName: candidate.toolName,
5740
6923
  input: candidate.input
5741
6924
  };
@@ -5962,7 +7145,7 @@ async function handleToolChoice(doGenerate, params, tools) {
5962
7145
  }
5963
7146
  const toolCall = {
5964
7147
  type: "tool-call",
5965
- toolCallId: (0, import_provider_utils.generateId)(),
7148
+ toolCallId: generateToolCallId(),
5966
7149
  toolName,
5967
7150
  input
5968
7151
  };
@@ -6472,7 +7655,6 @@ unit: celsius
6472
7655
  }
6473
7656
 
6474
7657
  // src/stream-handler.ts
6475
- var import_provider_utils2 = require("@ai-sdk/provider-utils");
6476
7658
  async function wrapStream({
6477
7659
  protocol,
6478
7660
  doStream,
@@ -6510,10 +7692,22 @@ async function wrapStream({
6510
7692
  }
6511
7693
  )
6512
7694
  ).pipeThrough(protocol.createStreamParser({ tools, options }));
7695
+ let seenToolCall = false;
6513
7696
  const v3Stream = coreStream.pipeThrough(
6514
7697
  new TransformStream({
6515
7698
  transform(part, controller) {
6516
- const normalizedPart = part.type === "tool-call" ? coerceToolCallPart(part, tools) : part;
7699
+ let normalizedPart = part.type === "tool-call" ? coerceToolCallPart(part, tools) : part;
7700
+ if (normalizedPart.type === "tool-call") {
7701
+ seenToolCall = true;
7702
+ }
7703
+ if (normalizedPart.type === "finish" && seenToolCall && normalizedPart.finishReason.unified === "stop") {
7704
+ normalizedPart = {
7705
+ ...normalizedPart,
7706
+ finishReason: normalizeToolCallsFinishReason(
7707
+ normalizedPart.finishReason
7708
+ )
7709
+ };
7710
+ }
6517
7711
  if (debugLevel === "stream") {
6518
7712
  logParsedChunk(normalizedPart);
6519
7713
  }
@@ -6550,7 +7744,7 @@ async function toolChoiceStream({
6550
7744
  start(controller) {
6551
7745
  controller.enqueue({
6552
7746
  type: "tool-call",
6553
- toolCallId: (0, import_provider_utils2.generateId)(),
7747
+ toolCallId: generateToolCallId(),
6554
7748
  toolName,
6555
7749
  input
6556
7750
  });