@ai-sdk-tool/parser 3.3.3 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -847,7 +847,25 @@ function getPotentialStartIndex(text, searchedText) {
847
847
 
848
848
  // src/core/utils/id.ts
849
849
  function generateId() {
850
- return Math.random().toString(36).substring(2, 15);
850
+ return crypto.randomUUID().replace(/-/g, "").slice(0, 13);
851
+ }
852
+ var TOOL_CALL_ID_PREFIX = "call_";
853
+ var TOOL_CALL_ID_BODY_LENGTH = 24;
854
+ var TOOL_CALL_ID_ALPHANUM = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
855
+ function randomAlphaNumeric(length) {
856
+ var _a;
857
+ const bytes = new Uint8Array(length);
858
+ crypto.getRandomValues(bytes);
859
+ let out = "";
860
+ for (let i = 0; i < length; i += 1) {
861
+ const byte = bytes[i];
862
+ const index = (byte != null ? byte : 0) % TOOL_CALL_ID_ALPHANUM.length;
863
+ out += (_a = TOOL_CALL_ID_ALPHANUM[index]) != null ? _a : "0";
864
+ }
865
+ return out;
866
+ }
867
+ function generateToolCallId() {
868
+ return `${TOOL_CALL_ID_PREFIX}${randomAlphaNumeric(TOOL_CALL_ID_BODY_LENGTH)}`;
851
869
  }
852
870
 
853
871
  // src/core/utils/protocol-utils.ts
@@ -856,6 +874,38 @@ function addTextSegment(text, processedElements) {
856
874
  processedElements.push({ type: "text", text });
857
875
  }
858
876
  }
877
+ function createFlushTextHandler(getCurrentTextId, setCurrentTextId, getHasEmittedTextStart, setHasEmittedTextStart) {
878
+ return (controller, text) => {
879
+ const content = text;
880
+ if (content) {
881
+ if (!getCurrentTextId()) {
882
+ const newId = generateId();
883
+ setCurrentTextId(newId);
884
+ controller.enqueue({
885
+ type: "text-start",
886
+ id: newId
887
+ });
888
+ setHasEmittedTextStart(true);
889
+ }
890
+ controller.enqueue({
891
+ type: "text-delta",
892
+ id: getCurrentTextId(),
893
+ delta: content
894
+ });
895
+ }
896
+ const currentTextId = getCurrentTextId();
897
+ if (currentTextId && !text) {
898
+ if (getHasEmittedTextStart()) {
899
+ controller.enqueue({
900
+ type: "text-end",
901
+ id: currentTextId
902
+ });
903
+ setHasEmittedTextStart(false);
904
+ }
905
+ setCurrentTextId(null);
906
+ }
907
+ };
908
+ }
859
909
 
860
910
  // src/core/utils/regex.ts
861
911
  function escapeRegExp(literal) {
@@ -863,15 +913,21 @@ function escapeRegExp(literal) {
863
913
  }
864
914
 
865
915
  // src/core/protocols/json-protocol.ts
916
+ function shouldEmitRawToolCallTextOnError(options) {
917
+ return (options == null ? void 0 : options.emitRawToolCallTextOnError) === true;
918
+ }
919
+ function canonicalizeToolInput(argumentsValue) {
920
+ return JSON.stringify(argumentsValue != null ? argumentsValue : {});
921
+ }
866
922
  function processToolCallJson(toolCallJson, fullMatch, processedElements, options) {
867
- var _a, _b;
923
+ var _a;
868
924
  try {
869
925
  const parsedToolCall = parse(toolCallJson);
870
926
  processedElements.push({
871
927
  type: "tool-call",
872
- toolCallId: generateId(),
928
+ toolCallId: generateToolCallId(),
873
929
  toolName: parsedToolCall.name,
874
- input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
930
+ input: canonicalizeToolInput(parsedToolCall.arguments)
875
931
  });
876
932
  } catch (error) {
877
933
  logParseFailure({
@@ -880,7 +936,7 @@ function processToolCallJson(toolCallJson, fullMatch, processedElements, options
880
936
  snippet: fullMatch,
881
937
  error
882
938
  });
883
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
939
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
884
940
  options,
885
941
  "Could not process JSON tool call, keeping original text.",
886
942
  { toolCall: fullMatch, error }
@@ -901,6 +957,293 @@ function processMatchedToolCall(context) {
901
957
  }
902
958
  return startIndex + match[0].length;
903
959
  }
960
+ var WHITESPACE_JSON_REGEX = /\s/;
961
+ function skipJsonWhitespace(text, fromIndex) {
962
+ let index = fromIndex;
963
+ while (index < text.length && WHITESPACE_JSON_REGEX.test(text[index])) {
964
+ index += 1;
965
+ }
966
+ return index;
967
+ }
968
+ function findTopLevelPropertyValueStart(text, property) {
969
+ const objectStart = skipJsonWhitespace(text, 0);
970
+ if (objectStart >= text.length || text.charAt(objectStart) !== "{") {
971
+ return null;
972
+ }
973
+ let depth = 0;
974
+ let inString = false;
975
+ let escaping = false;
976
+ for (let index = objectStart; index < text.length; index += 1) {
977
+ const char = text.charAt(index);
978
+ if (inString) {
979
+ if (escaping) {
980
+ escaping = false;
981
+ continue;
982
+ }
983
+ if (char === "\\") {
984
+ escaping = true;
985
+ continue;
986
+ }
987
+ if (char === '"') {
988
+ inString = false;
989
+ }
990
+ continue;
991
+ }
992
+ if (char === "{") {
993
+ depth += 1;
994
+ continue;
995
+ }
996
+ if (char === "}") {
997
+ depth = Math.max(0, depth - 1);
998
+ continue;
999
+ }
1000
+ if (char !== '"') {
1001
+ continue;
1002
+ }
1003
+ if (depth !== 1) {
1004
+ inString = true;
1005
+ continue;
1006
+ }
1007
+ const keyStart = index + 1;
1008
+ let keyEnd = keyStart;
1009
+ let keyEscaped = false;
1010
+ while (keyEnd < text.length) {
1011
+ const keyChar = text.charAt(keyEnd);
1012
+ if (keyEscaped) {
1013
+ keyEscaped = false;
1014
+ } else if (keyChar === "\\") {
1015
+ keyEscaped = true;
1016
+ } else if (keyChar === '"') {
1017
+ break;
1018
+ }
1019
+ keyEnd += 1;
1020
+ }
1021
+ if (keyEnd >= text.length || text.charAt(keyEnd) !== '"') {
1022
+ return null;
1023
+ }
1024
+ const key = text.slice(keyStart, keyEnd);
1025
+ let valueCursor = skipJsonWhitespace(text, keyEnd + 1);
1026
+ if (valueCursor >= text.length || text.charAt(valueCursor) !== ":") {
1027
+ index = keyEnd;
1028
+ continue;
1029
+ }
1030
+ valueCursor = skipJsonWhitespace(text, valueCursor + 1);
1031
+ if (key === property) {
1032
+ return valueCursor < text.length ? valueCursor : null;
1033
+ }
1034
+ index = valueCursor - 1;
1035
+ }
1036
+ return null;
1037
+ }
1038
+ function extractTopLevelStringProperty(text, property) {
1039
+ const valueStart = findTopLevelPropertyValueStart(text, property);
1040
+ if (valueStart == null || valueStart >= text.length) {
1041
+ return void 0;
1042
+ }
1043
+ if (text.charAt(valueStart) !== '"') {
1044
+ return void 0;
1045
+ }
1046
+ let valueEnd = valueStart + 1;
1047
+ let escaped = false;
1048
+ while (valueEnd < text.length) {
1049
+ const char = text.charAt(valueEnd);
1050
+ if (escaped) {
1051
+ escaped = false;
1052
+ } else if (char === "\\") {
1053
+ escaped = true;
1054
+ } else if (char === '"') {
1055
+ return text.slice(valueStart + 1, valueEnd);
1056
+ }
1057
+ valueEnd += 1;
1058
+ }
1059
+ return void 0;
1060
+ }
1061
+ function extractJsonValueSlice(text, valueStart) {
1062
+ if (valueStart >= text.length) {
1063
+ return null;
1064
+ }
1065
+ const first = text.charAt(valueStart);
1066
+ if (first === "{" || first === "[") {
1067
+ const stack = [first];
1068
+ let inString = false;
1069
+ let escaped = false;
1070
+ for (let index2 = valueStart + 1; index2 < text.length; index2 += 1) {
1071
+ const char = text.charAt(index2);
1072
+ if (inString) {
1073
+ if (escaped) {
1074
+ escaped = false;
1075
+ } else if (char === "\\") {
1076
+ escaped = true;
1077
+ } else if (char === '"') {
1078
+ inString = false;
1079
+ }
1080
+ continue;
1081
+ }
1082
+ if (char === '"') {
1083
+ inString = true;
1084
+ continue;
1085
+ }
1086
+ if (char === "{" || char === "[") {
1087
+ stack.push(char);
1088
+ continue;
1089
+ }
1090
+ if (char === "}" || char === "]") {
1091
+ const open = stack.at(-1);
1092
+ if (open === "{" && char === "}" || open === "[" && char === "]") {
1093
+ stack.pop();
1094
+ if (stack.length === 0) {
1095
+ return {
1096
+ text: text.slice(valueStart, index2 + 1),
1097
+ complete: true
1098
+ };
1099
+ }
1100
+ }
1101
+ }
1102
+ }
1103
+ return {
1104
+ text: text.slice(valueStart),
1105
+ complete: false
1106
+ };
1107
+ }
1108
+ if (first === '"') {
1109
+ let escaped = false;
1110
+ for (let index2 = valueStart + 1; index2 < text.length; index2 += 1) {
1111
+ const char = text.charAt(index2);
1112
+ if (escaped) {
1113
+ escaped = false;
1114
+ } else if (char === "\\") {
1115
+ escaped = true;
1116
+ } else if (char === '"') {
1117
+ return {
1118
+ text: text.slice(valueStart, index2 + 1),
1119
+ complete: true
1120
+ };
1121
+ }
1122
+ }
1123
+ return {
1124
+ text: text.slice(valueStart),
1125
+ complete: false
1126
+ };
1127
+ }
1128
+ let index = valueStart;
1129
+ while (index < text.length) {
1130
+ const char = text.charAt(index);
1131
+ if (char === "," || char === "}" || WHITESPACE_JSON_REGEX.test(char)) {
1132
+ break;
1133
+ }
1134
+ index += 1;
1135
+ }
1136
+ return {
1137
+ text: text.slice(valueStart, index),
1138
+ complete: index < text.length
1139
+ };
1140
+ }
1141
+ function extractStreamingToolCallProgress(toolCallJson) {
1142
+ var _a;
1143
+ const toolName = extractTopLevelStringProperty(toolCallJson, "name");
1144
+ const argsValueStart = findTopLevelPropertyValueStart(
1145
+ toolCallJson,
1146
+ "arguments"
1147
+ );
1148
+ if (argsValueStart == null) {
1149
+ return {
1150
+ toolName,
1151
+ argumentsText: void 0,
1152
+ argumentsComplete: false
1153
+ };
1154
+ }
1155
+ const argsSlice = extractJsonValueSlice(toolCallJson, argsValueStart);
1156
+ return {
1157
+ toolName,
1158
+ argumentsText: argsSlice == null ? void 0 : argsSlice.text,
1159
+ argumentsComplete: (_a = argsSlice == null ? void 0 : argsSlice.complete) != null ? _a : false
1160
+ };
1161
+ }
1162
+ function ensureToolInputStart(state, controller, toolName) {
1163
+ if (!state.activeToolInput) {
1164
+ const id = generateToolCallId();
1165
+ state.activeToolInput = {
1166
+ id,
1167
+ toolName,
1168
+ emittedInput: ""
1169
+ };
1170
+ controller.enqueue({
1171
+ type: "tool-input-start",
1172
+ id,
1173
+ toolName
1174
+ });
1175
+ }
1176
+ }
1177
+ function emitToolInputDelta(state, controller, fullInput) {
1178
+ const active = state.activeToolInput;
1179
+ if (!active) {
1180
+ return;
1181
+ }
1182
+ if (!fullInput.startsWith(active.emittedInput)) {
1183
+ return;
1184
+ }
1185
+ const delta = fullInput.slice(active.emittedInput.length);
1186
+ if (delta.length === 0) {
1187
+ return;
1188
+ }
1189
+ controller.enqueue({
1190
+ type: "tool-input-delta",
1191
+ id: active.id,
1192
+ delta
1193
+ });
1194
+ active.emittedInput = fullInput;
1195
+ }
1196
+ function closeToolInput(state, controller) {
1197
+ if (!state.activeToolInput) {
1198
+ return;
1199
+ }
1200
+ controller.enqueue({
1201
+ type: "tool-input-end",
1202
+ id: state.activeToolInput.id
1203
+ });
1204
+ state.activeToolInput = null;
1205
+ }
1206
+ function emitToolCallFromParsed(state, controller, parsedToolCall) {
1207
+ var _a, _b, _c, _d;
1208
+ closeTextBlock(state, controller);
1209
+ const toolName = typeof parsedToolCall.name === "string" ? parsedToolCall.name : (_b = (_a = state.activeToolInput) == null ? void 0 : _a.toolName) != null ? _b : "unknown";
1210
+ const input = canonicalizeToolInput(parsedToolCall.arguments);
1211
+ ensureToolInputStart(state, controller, toolName);
1212
+ emitToolInputDelta(state, controller, input);
1213
+ const toolCallId = (_d = (_c = state.activeToolInput) == null ? void 0 : _c.id) != null ? _d : generateToolCallId();
1214
+ closeToolInput(state, controller);
1215
+ controller.enqueue({
1216
+ type: "tool-call",
1217
+ toolCallId,
1218
+ toolName,
1219
+ input
1220
+ });
1221
+ }
1222
+ function canonicalizeArgumentsProgressInput(progress) {
1223
+ if (progress.argumentsText === void 0 || !progress.argumentsComplete) {
1224
+ return void 0;
1225
+ }
1226
+ try {
1227
+ const parsedArguments = parse(progress.argumentsText);
1228
+ return canonicalizeToolInput(parsedArguments);
1229
+ } catch (e) {
1230
+ return void 0;
1231
+ }
1232
+ }
1233
+ function emitToolInputProgress(state, controller) {
1234
+ if (!(state.isInsideToolCall && state.currentToolCallJson)) {
1235
+ return;
1236
+ }
1237
+ const progress = extractStreamingToolCallProgress(state.currentToolCallJson);
1238
+ if (!progress.toolName) {
1239
+ return;
1240
+ }
1241
+ ensureToolInputStart(state, controller, progress.toolName);
1242
+ const canonicalProgressInput = canonicalizeArgumentsProgressInput(progress);
1243
+ if (canonicalProgressInput !== void 0) {
1244
+ emitToolInputDelta(state, controller, canonicalProgressInput);
1245
+ }
1246
+ }
904
1247
  function flushBuffer(state, controller, toolCallStart) {
905
1248
  if (state.buffer.length === 0) {
906
1249
  return;
@@ -931,44 +1274,77 @@ function closeTextBlock(state, controller) {
931
1274
  state.hasEmittedTextStart = false;
932
1275
  }
933
1276
  }
934
- function emitIncompleteToolCall(state, controller, toolCallStart) {
935
- if (!state.currentToolCallJson) {
1277
+ function emitIncompleteToolCall(state, controller, toolCallStart, trailingBuffer, options) {
1278
+ var _a;
1279
+ if (!state.currentToolCallJson && trailingBuffer.length === 0) {
1280
+ state.isInsideToolCall = false;
936
1281
  return;
937
1282
  }
1283
+ if (state.currentToolCallJson) {
1284
+ try {
1285
+ const parsedToolCall = parse(state.currentToolCallJson);
1286
+ emitToolCallFromParsed(state, controller, parsedToolCall);
1287
+ state.currentToolCallJson = "";
1288
+ state.isInsideToolCall = false;
1289
+ return;
1290
+ } catch (e) {
1291
+ }
1292
+ }
1293
+ const rawToolCallContent = `${state.currentToolCallJson}${trailingBuffer}`;
1294
+ const errorContent = `${toolCallStart}${rawToolCallContent}`;
1295
+ const shouldEmitRawFallback = shouldEmitRawToolCallTextOnError(options);
938
1296
  logParseFailure({
939
1297
  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
1298
+ reason: shouldEmitRawFallback ? "Incomplete streaming tool call segment emitted as text" : "Incomplete streaming tool call segment suppressed without raw text fallback",
1299
+ snippet: errorContent
957
1300
  });
1301
+ if (shouldEmitRawFallback) {
1302
+ const errorId = generateId();
1303
+ controller.enqueue({
1304
+ type: "text-start",
1305
+ id: errorId
1306
+ });
1307
+ controller.enqueue({
1308
+ type: "text-delta",
1309
+ id: errorId,
1310
+ delta: errorContent
1311
+ });
1312
+ controller.enqueue({
1313
+ type: "text-end",
1314
+ id: errorId
1315
+ });
1316
+ }
1317
+ closeToolInput(state, controller);
1318
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
1319
+ options,
1320
+ shouldEmitRawFallback ? "Could not complete streaming JSON tool call at finish; emitting original text." : "Could not complete streaming JSON tool call at finish.",
1321
+ { toolCall: errorContent }
1322
+ );
958
1323
  state.currentToolCallJson = "";
1324
+ state.isInsideToolCall = false;
959
1325
  }
960
- function handleFinishChunk(state, controller, toolCallStart, chunk) {
961
- if (state.buffer.length > 0) {
1326
+ function handleFinishChunk(state, controller, toolCallStart, options, chunk) {
1327
+ if (state.isInsideToolCall) {
1328
+ const trailingBuffer = state.buffer;
1329
+ state.buffer = "";
1330
+ emitIncompleteToolCall(
1331
+ state,
1332
+ controller,
1333
+ toolCallStart,
1334
+ trailingBuffer,
1335
+ options
1336
+ );
1337
+ } else if (state.buffer.length > 0) {
962
1338
  flushBuffer(state, controller, toolCallStart);
963
1339
  }
964
1340
  closeTextBlock(state, controller);
965
- emitIncompleteToolCall(state, controller, toolCallStart);
966
1341
  controller.enqueue(chunk);
967
1342
  }
968
1343
  function publishText(text, state, controller) {
969
1344
  if (state.isInsideToolCall) {
970
1345
  closeTextBlock(state, controller);
971
1346
  state.currentToolCallJson += text;
1347
+ emitToolInputProgress(state, controller);
972
1348
  } else if (text.length > 0) {
973
1349
  if (!state.currentTextId) {
974
1350
  state.currentTextId = generateId();
@@ -986,42 +1362,40 @@ function publishText(text, state, controller) {
986
1362
  }
987
1363
  }
988
1364
  function emitToolCall(context) {
989
- var _a, _b;
1365
+ var _a;
990
1366
  const { state, controller, toolCallStart, toolCallEnd, options } = context;
991
1367
  try {
992
1368
  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
- });
1369
+ emitToolCallFromParsed(state, controller, parsedToolCall);
1000
1370
  } catch (error) {
1371
+ const errorContent = `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`;
1372
+ const shouldEmitRawFallback = shouldEmitRawToolCallTextOnError(options);
1001
1373
  logParseFailure({
1002
1374
  phase: "stream",
1003
1375
  reason: "Failed to parse streaming tool call JSON segment",
1004
- snippet: `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`,
1376
+ snippet: errorContent,
1005
1377
  error
1006
1378
  });
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(
1379
+ if (shouldEmitRawFallback) {
1380
+ const errorId = generateId();
1381
+ controller.enqueue({
1382
+ type: "text-start",
1383
+ id: errorId
1384
+ });
1385
+ controller.enqueue({
1386
+ type: "text-delta",
1387
+ id: errorId,
1388
+ delta: errorContent
1389
+ });
1390
+ controller.enqueue({
1391
+ type: "text-end",
1392
+ id: errorId
1393
+ });
1394
+ }
1395
+ closeToolInput(state, controller);
1396
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
1023
1397
  options,
1024
- "Could not process streaming JSON tool call; emitting original text.",
1398
+ shouldEmitRawFallback ? "Could not process streaming JSON tool call; emitting original text." : "Could not process streaming JSON tool call.",
1025
1399
  {
1026
1400
  toolCall: errorContent
1027
1401
  }
@@ -1037,6 +1411,7 @@ function processTagMatch(context) {
1037
1411
  } else {
1038
1412
  state.currentToolCallJson = "";
1039
1413
  state.isInsideToolCall = true;
1414
+ state.activeToolInput = null;
1040
1415
  }
1041
1416
  }
1042
1417
  function processBufferTags(context) {
@@ -1059,8 +1434,16 @@ function processBufferTags(context) {
1059
1434
  );
1060
1435
  }
1061
1436
  }
1062
- function handlePartialTag(state, controller, toolCallStart) {
1437
+ function handlePartialTag(state, controller, toolCallStart, toolCallEnd) {
1063
1438
  if (state.isInsideToolCall) {
1439
+ const potentialEndIndex = getPotentialStartIndex(state.buffer, toolCallEnd);
1440
+ if (potentialEndIndex != null && potentialEndIndex + toolCallEnd.length > state.buffer.length) {
1441
+ publishText(state.buffer.slice(0, potentialEndIndex), state, controller);
1442
+ state.buffer = state.buffer.slice(potentialEndIndex);
1443
+ } else {
1444
+ publishText(state.buffer, state, controller);
1445
+ state.buffer = "";
1446
+ }
1064
1447
  return;
1065
1448
  }
1066
1449
  const potentialIndex = getPotentialStartIndex(state.buffer, toolCallStart);
@@ -1133,13 +1516,14 @@ var jsonProtocol = ({
1133
1516
  buffer: "",
1134
1517
  currentToolCallJson: "",
1135
1518
  currentTextId: null,
1136
- hasEmittedTextStart: false
1519
+ hasEmittedTextStart: false,
1520
+ activeToolInput: null
1137
1521
  };
1138
1522
  return new TransformStream({
1139
1523
  transform(chunk, controller) {
1140
1524
  var _a;
1141
1525
  if (chunk.type === "finish") {
1142
- handleFinishChunk(state, controller, toolCallStart, chunk);
1526
+ handleFinishChunk(state, controller, toolCallStart, options, chunk);
1143
1527
  return;
1144
1528
  }
1145
1529
  if (chunk.type !== "text-delta") {
@@ -1155,7 +1539,7 @@ var jsonProtocol = ({
1155
1539
  toolCallEnd,
1156
1540
  options
1157
1541
  });
1158
- handlePartialTag(state, controller, toolCallStart);
1542
+ handlePartialTag(state, controller, toolCallStart, toolCallEnd);
1159
1543
  }
1160
1544
  });
1161
1545
  },
@@ -4132,35 +4516,112 @@ function parse3(xml, schema, options = {}) {
4132
4516
  var NAME_CHAR_RE2 = /[A-Za-z0-9_:-]/;
4133
4517
  var WHITESPACE_REGEX5 = /\s/;
4134
4518
 
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;
4519
+ // src/core/utils/streamed-tool-input-delta.ts
4520
+ function emitDelta({
4521
+ controller,
4522
+ id,
4523
+ state,
4524
+ nextInput
4525
+ }) {
4526
+ if (!nextInput.startsWith(state.emittedInput)) {
4527
+ return false;
4141
4528
  }
4142
- const match = trimmed.match(XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX);
4143
- if (!match) {
4144
- return null;
4529
+ const delta = nextInput.slice(state.emittedInput.length);
4530
+ if (delta.length === 0) {
4531
+ return false;
4145
4532
  }
4146
- const rootTag = match[1];
4147
- if (!toolNames.includes(rootTag)) {
4148
- return null;
4533
+ controller.enqueue({
4534
+ type: "tool-input-delta",
4535
+ id,
4536
+ delta
4537
+ });
4538
+ state.emittedInput = nextInput;
4539
+ return true;
4540
+ }
4541
+ function toIncompleteJsonPrefix(fullJson) {
4542
+ const trimmed = fullJson.trim();
4543
+ let prefix = trimmed;
4544
+ while (prefix.endsWith("}") || prefix.endsWith("]")) {
4545
+ prefix = prefix.slice(0, -1);
4149
4546
  }
4150
- const body = match[2].trimEnd();
4151
- if (body.trim().length === 0 || body.includes(`</${rootTag}>`)) {
4152
- return null;
4547
+ prefix = prefix.trimEnd();
4548
+ if (prefix.endsWith('"')) {
4549
+ prefix = prefix.slice(0, -1);
4153
4550
  }
4154
- return `<${rootTag}>
4155
- ${body}
4156
- </${rootTag}>`;
4157
- }
4158
-
4551
+ if (prefix.length === 0) {
4552
+ if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
4553
+ return trimmed.startsWith("{") ? "{" : "[";
4554
+ }
4555
+ if (trimmed.startsWith("]")) {
4556
+ return "[";
4557
+ }
4558
+ if (trimmed.startsWith("}")) {
4559
+ return "{";
4560
+ }
4561
+ if (trimmed.startsWith('"')) {
4562
+ return '"';
4563
+ }
4564
+ return "{";
4565
+ }
4566
+ return prefix;
4567
+ }
4568
+ function emitPrefixDelta(params) {
4569
+ return emitDelta({
4570
+ ...params,
4571
+ nextInput: params.candidate
4572
+ });
4573
+ }
4574
+ function emitFinalRemainder(params) {
4575
+ var _a;
4576
+ const result = emitDelta({
4577
+ ...params,
4578
+ nextInput: params.finalFullJson
4579
+ });
4580
+ if (!result && params.state.emittedInput.length > 0) {
4581
+ (_a = params.onMismatch) == null ? void 0 : _a.call(
4582
+ params,
4583
+ "Final JSON does not extend emitted tool-input prefix",
4584
+ {
4585
+ emittedLength: params.state.emittedInput.length,
4586
+ finalLength: params.finalFullJson.length
4587
+ }
4588
+ );
4589
+ }
4590
+ return result;
4591
+ }
4592
+
4593
+ // src/core/utils/xml-root-repair.ts
4594
+ var XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX = /^<([A-Za-z_][A-Za-z0-9_-]*)\s*\r?\n([\s\S]+?)\r?\n\s*\/>\s*$/;
4595
+ function tryRepairXmlSelfClosingRootWithBody(rawText, toolNames) {
4596
+ const trimmed = rawText.trim();
4597
+ if (trimmed.length === 0) {
4598
+ return null;
4599
+ }
4600
+ const match = trimmed.match(XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX);
4601
+ if (!match) {
4602
+ return null;
4603
+ }
4604
+ const rootTag = match[1];
4605
+ if (!toolNames.includes(rootTag)) {
4606
+ return null;
4607
+ }
4608
+ const body = match[2].trimEnd();
4609
+ if (body.trim().length === 0 || body.includes(`</${rootTag}>`)) {
4610
+ return null;
4611
+ }
4612
+ return `<${rootTag}>
4613
+ ${body}
4614
+ </${rootTag}>`;
4615
+ }
4616
+
4159
4617
  // src/core/protocols/xml-protocol.ts
4160
4618
  function getToolSchema(tools, toolName) {
4161
4619
  var _a;
4162
4620
  return (_a = tools.find((t) => t.name === toolName)) == null ? void 0 : _a.inputSchema;
4163
4621
  }
4622
+ function shouldEmitRawToolCallTextOnError2(options) {
4623
+ return (options == null ? void 0 : options.emitRawToolCallTextOnError) === true;
4624
+ }
4164
4625
  function processToolCall(params) {
4165
4626
  var _a, _b;
4166
4627
  const { toolCall, tools, options, text, processedElements, parseOptions } = params;
@@ -4173,7 +4634,7 @@ function processToolCall(params) {
4173
4634
  const parsed = parse3(toolCall.content, toolSchema, parseConfig);
4174
4635
  processedElements.push({
4175
4636
  type: "tool-call",
4176
- toolCallId: generateId(),
4637
+ toolCallId: generateToolCallId(),
4177
4638
  toolName: toolCall.toolName,
4178
4639
  input: JSON.stringify(parsed)
4179
4640
  });
@@ -4190,6 +4651,299 @@ function processToolCall(params) {
4190
4651
  processedElements.push({ type: "text", text: originalCallText });
4191
4652
  }
4192
4653
  }
4654
+ function parseXmlTagName(rawTagBody) {
4655
+ let index = 0;
4656
+ while (index < rawTagBody.length && WHITESPACE_REGEX5.test(rawTagBody[index])) {
4657
+ index += 1;
4658
+ }
4659
+ const nameStart = index;
4660
+ while (index < rawTagBody.length && NAME_CHAR_RE2.test(rawTagBody.charAt(index))) {
4661
+ index += 1;
4662
+ }
4663
+ return rawTagBody.slice(nameStart, index);
4664
+ }
4665
+ function consumeXmlSpecialSection(fragment, ltIndex) {
4666
+ if (fragment.startsWith("<!--", ltIndex)) {
4667
+ const commentEnd = fragment.indexOf("-->", ltIndex + 4);
4668
+ return commentEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: commentEnd + 3 };
4669
+ }
4670
+ if (fragment.startsWith("<![CDATA[", ltIndex)) {
4671
+ const cdataEnd = fragment.indexOf("]]>", ltIndex + 9);
4672
+ return cdataEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: cdataEnd + 3 };
4673
+ }
4674
+ if (fragment.startsWith("<?", ltIndex)) {
4675
+ const processingEnd = fragment.indexOf("?>", ltIndex + 2);
4676
+ return processingEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: processingEnd + 2 };
4677
+ }
4678
+ if (fragment.startsWith("<!", ltIndex)) {
4679
+ const declarationEnd = fragment.indexOf(">", ltIndex + 2);
4680
+ return declarationEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: declarationEnd + 1 };
4681
+ }
4682
+ return { kind: "none" };
4683
+ }
4684
+ function parseXmlTagToken(fragment, ltIndex) {
4685
+ const gtIndex = fragment.indexOf(">", ltIndex + 1);
4686
+ if (gtIndex === -1) {
4687
+ return null;
4688
+ }
4689
+ const tagBody = fragment.slice(ltIndex + 1, gtIndex).trim();
4690
+ if (tagBody.length === 0) {
4691
+ return null;
4692
+ }
4693
+ if (tagBody.startsWith("/")) {
4694
+ const closeName = parseXmlTagName(tagBody.slice(1));
4695
+ if (closeName.length === 0) {
4696
+ return null;
4697
+ }
4698
+ return { kind: "close", name: closeName, nextPos: gtIndex + 1 };
4699
+ }
4700
+ const selfClosing = tagBody.endsWith("/");
4701
+ const openBody = selfClosing ? tagBody.slice(0, -1).trimEnd() : tagBody;
4702
+ const openName = parseXmlTagName(openBody);
4703
+ if (openName.length === 0) {
4704
+ return null;
4705
+ }
4706
+ return {
4707
+ kind: "open",
4708
+ name: openName,
4709
+ selfClosing,
4710
+ nextPos: gtIndex + 1
4711
+ };
4712
+ }
4713
+ function analyzeXmlFragmentForProgress(fragment) {
4714
+ const stack = [];
4715
+ const topLevelTagNames = [];
4716
+ let position = 0;
4717
+ while (position < fragment.length) {
4718
+ const ltIndex = fragment.indexOf("<", position);
4719
+ if (ltIndex === -1) {
4720
+ break;
4721
+ }
4722
+ const special = consumeXmlSpecialSection(fragment, ltIndex);
4723
+ if (special.kind === "incomplete") {
4724
+ return null;
4725
+ }
4726
+ if (special.kind === "consumed") {
4727
+ position = special.nextPos;
4728
+ continue;
4729
+ }
4730
+ const token = parseXmlTagToken(fragment, ltIndex);
4731
+ if (token === null) {
4732
+ return null;
4733
+ }
4734
+ if (token.kind === "close") {
4735
+ const openName = stack.pop();
4736
+ if (!openName || openName !== token.name) {
4737
+ return null;
4738
+ }
4739
+ position = token.nextPos;
4740
+ continue;
4741
+ }
4742
+ if (stack.length === 0) {
4743
+ topLevelTagNames.push(token.name);
4744
+ }
4745
+ if (!token.selfClosing) {
4746
+ stack.push(token.name);
4747
+ }
4748
+ position = token.nextPos;
4749
+ }
4750
+ if (stack.length > 0) {
4751
+ return null;
4752
+ }
4753
+ return { topLevelTagNames };
4754
+ }
4755
+ function scanXmlFragmentTopLevelTextStep(options) {
4756
+ const { fragment, position, stack } = options;
4757
+ const ltIndex = fragment.indexOf("<", position);
4758
+ if (ltIndex === -1) {
4759
+ const trailingText = fragment.slice(position);
4760
+ return {
4761
+ kind: "done",
4762
+ value: stack.length === 0 && trailingText.trim().length > 0
4763
+ };
4764
+ }
4765
+ const textBetweenTags = fragment.slice(position, ltIndex);
4766
+ if (stack.length === 0 && textBetweenTags.trim().length > 0) {
4767
+ return { kind: "found" };
4768
+ }
4769
+ const special = consumeXmlSpecialSection(fragment, ltIndex);
4770
+ if (special.kind === "incomplete") {
4771
+ return { kind: "invalid" };
4772
+ }
4773
+ if (special.kind === "consumed") {
4774
+ return { kind: "next", nextPos: special.nextPos };
4775
+ }
4776
+ const token = parseXmlTagToken(fragment, ltIndex);
4777
+ if (token === null) {
4778
+ return { kind: "invalid" };
4779
+ }
4780
+ if (token.kind === "close") {
4781
+ const openName = stack.pop();
4782
+ if (!openName || openName !== token.name) {
4783
+ return { kind: "invalid" };
4784
+ }
4785
+ } else if (!token.selfClosing) {
4786
+ stack.push(token.name);
4787
+ }
4788
+ return { kind: "next", nextPos: token.nextPos };
4789
+ }
4790
+ function hasNonWhitespaceTopLevelText(fragment) {
4791
+ if (!fragment.includes("<")) {
4792
+ return fragment.trim().length > 0;
4793
+ }
4794
+ const stack = [];
4795
+ let position = 0;
4796
+ while (position < fragment.length) {
4797
+ const step = scanXmlFragmentTopLevelTextStep({ fragment, position, stack });
4798
+ if (step.kind === "found") {
4799
+ return true;
4800
+ }
4801
+ if (step.kind === "invalid") {
4802
+ return false;
4803
+ }
4804
+ if (step.kind === "done") {
4805
+ return step.value;
4806
+ }
4807
+ position = step.nextPos;
4808
+ }
4809
+ return false;
4810
+ }
4811
+ function getObjectSchemaPropertyNames(schema) {
4812
+ if (!schema || typeof schema !== "object") {
4813
+ return null;
4814
+ }
4815
+ const schemaObject = schema;
4816
+ const typeValue = schemaObject.type;
4817
+ if (typeValue != null) {
4818
+ const isObjectType = typeValue === "object" || Array.isArray(typeValue) && typeValue.includes("object");
4819
+ if (!isObjectType) {
4820
+ return null;
4821
+ }
4822
+ }
4823
+ if (!schemaObject.properties || typeof schemaObject.properties !== "object") {
4824
+ return /* @__PURE__ */ new Set();
4825
+ }
4826
+ return new Set(
4827
+ Object.keys(schemaObject.properties)
4828
+ );
4829
+ }
4830
+ function schemaAllowsArrayType(schema) {
4831
+ if (!schema || typeof schema !== "object") {
4832
+ return false;
4833
+ }
4834
+ const schemaRecord = schema;
4835
+ const typeValue = schemaRecord.type;
4836
+ if (typeValue === "array") {
4837
+ return true;
4838
+ }
4839
+ if (Array.isArray(typeValue) && typeValue.includes("array")) {
4840
+ return true;
4841
+ }
4842
+ const unions = [schemaRecord.anyOf, schemaRecord.oneOf, schemaRecord.allOf];
4843
+ for (const union of unions) {
4844
+ if (!Array.isArray(union)) {
4845
+ continue;
4846
+ }
4847
+ if (union.some((entry) => schemaAllowsArrayType(entry))) {
4848
+ return true;
4849
+ }
4850
+ }
4851
+ return false;
4852
+ }
4853
+ function getSchemaObjectProperty(schema, propertyName) {
4854
+ if (!schema || typeof schema !== "object") {
4855
+ return null;
4856
+ }
4857
+ const schemaObject = schema;
4858
+ const properties = schemaObject.properties;
4859
+ if (!properties || typeof properties !== "object") {
4860
+ return null;
4861
+ }
4862
+ const property = properties[propertyName];
4863
+ if (!property) {
4864
+ return null;
4865
+ }
4866
+ return property;
4867
+ }
4868
+ function isStableXmlProgressCandidate(options) {
4869
+ const { candidate, parsed, toolSchema } = options;
4870
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
4871
+ return false;
4872
+ }
4873
+ const structure = analyzeXmlFragmentForProgress(candidate);
4874
+ if (!structure) {
4875
+ return false;
4876
+ }
4877
+ const schemaProperties = getObjectSchemaPropertyNames(toolSchema);
4878
+ if (!schemaProperties || schemaProperties.size === 0) {
4879
+ return false;
4880
+ }
4881
+ const parsedObject = parsed;
4882
+ const uniqueTopLevelTags = new Set(structure.topLevelTagNames);
4883
+ for (const tagName of uniqueTopLevelTags) {
4884
+ if (!schemaProperties.has(tagName)) {
4885
+ continue;
4886
+ }
4887
+ const schemaProperty = getSchemaObjectProperty(toolSchema, tagName);
4888
+ if (schemaProperty && schemaAllowsArrayType(schemaProperty) && !Array.isArray(parsedObject[tagName])) {
4889
+ return false;
4890
+ }
4891
+ }
4892
+ if (structure.topLevelTagNames.length === 1) {
4893
+ const onlyTopLevelTag = structure.topLevelTagNames[0];
4894
+ if (!schemaProperties || schemaProperties.size === 0 || !schemaProperties.has(onlyTopLevelTag)) {
4895
+ return false;
4896
+ }
4897
+ }
4898
+ return true;
4899
+ }
4900
+ function parseXmlContentForStreamProgress({
4901
+ toolContent,
4902
+ toolSchema,
4903
+ parseOptions
4904
+ }) {
4905
+ const tryParse = (content) => {
4906
+ try {
4907
+ return parse3(content, toolSchema, {
4908
+ ...parseOptions != null ? parseOptions : {},
4909
+ repair: false,
4910
+ onError: void 0
4911
+ });
4912
+ } catch (e) {
4913
+ return null;
4914
+ }
4915
+ };
4916
+ const strictFull = tryParse(toolContent);
4917
+ if (strictFull !== null && isStableXmlProgressCandidate({
4918
+ candidate: toolContent,
4919
+ parsed: strictFull,
4920
+ toolSchema
4921
+ })) {
4922
+ return JSON.stringify(strictFull);
4923
+ }
4924
+ let searchEnd = toolContent.length;
4925
+ while (searchEnd > 0) {
4926
+ const gtIndex = toolContent.lastIndexOf(">", searchEnd - 1);
4927
+ if (gtIndex === -1) {
4928
+ break;
4929
+ }
4930
+ const candidate = toolContent.slice(0, gtIndex + 1);
4931
+ if (!analyzeXmlFragmentForProgress(candidate)) {
4932
+ searchEnd = gtIndex;
4933
+ continue;
4934
+ }
4935
+ const parsedCandidate = tryParse(candidate);
4936
+ if (parsedCandidate !== null && isStableXmlProgressCandidate({
4937
+ candidate,
4938
+ parsed: parsedCandidate,
4939
+ toolSchema
4940
+ })) {
4941
+ return JSON.stringify(parsedCandidate);
4942
+ }
4943
+ searchEnd = gtIndex;
4944
+ }
4945
+ return null;
4946
+ }
4193
4947
  function handleStreamingToolCallEnd(params) {
4194
4948
  var _a, _b;
4195
4949
  const {
@@ -4209,19 +4963,37 @@ function handleStreamingToolCallEnd(params) {
4209
4963
  flushText(ctrl);
4210
4964
  try {
4211
4965
  const parsedResult = parse3(toolContent, toolSchema, parseConfig);
4966
+ const finalInput = JSON.stringify(parsedResult);
4967
+ emitFinalRemainder({
4968
+ controller: ctrl,
4969
+ id: currentToolCall.toolCallId,
4970
+ state: currentToolCall,
4971
+ finalFullJson: finalInput,
4972
+ onMismatch: options == null ? void 0 : options.onError
4973
+ });
4974
+ ctrl.enqueue({
4975
+ type: "tool-input-end",
4976
+ id: currentToolCall.toolCallId
4977
+ });
4212
4978
  ctrl.enqueue({
4213
4979
  type: "tool-call",
4214
- toolCallId: generateId(),
4980
+ toolCallId: currentToolCall.toolCallId,
4215
4981
  toolName: currentToolCall.name,
4216
- input: JSON.stringify(parsedResult)
4982
+ input: finalInput
4217
4983
  });
4218
4984
  } catch (error) {
4985
+ ctrl.enqueue({
4986
+ type: "tool-input-end",
4987
+ id: currentToolCall.toolCallId
4988
+ });
4219
4989
  const original = `<${currentToolCall.name}>${toolContent}</${currentToolCall.name}>`;
4220
4990
  (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, "Could not process streaming XML tool call", {
4221
4991
  toolCall: original,
4222
4992
  error
4223
4993
  });
4224
- flushText(ctrl, original);
4994
+ if (shouldEmitRawToolCallTextOnError2(options)) {
4995
+ flushText(ctrl, original);
4996
+ }
4225
4997
  }
4226
4998
  }
4227
4999
  function findClosingTagEndFlexible(text, contentStart, toolName) {
@@ -4643,38 +5415,6 @@ function findPotentialToolTagStart(buffer, toolNames) {
4643
5415
  }
4644
5416
  return -1;
4645
5417
  }
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
5418
  function processToolCallInBuffer(params) {
4679
5419
  const {
4680
5420
  buffer,
@@ -4684,18 +5424,21 @@ function processToolCallInBuffer(params) {
4684
5424
  controller,
4685
5425
  flushText,
4686
5426
  setBuffer,
4687
- parseOptions
5427
+ parseOptions,
5428
+ emitToolInputProgress: emitToolInputProgress2
4688
5429
  } = params;
4689
5430
  const endTagPattern = new RegExp(
4690
5431
  `</\\s*${escapeRegExp(currentToolCall.name)}\\s*>`
4691
5432
  );
4692
5433
  const endMatch = endTagPattern.exec(buffer);
4693
5434
  if (!endMatch || endMatch.index === void 0) {
5435
+ emitToolInputProgress2(controller, currentToolCall, buffer);
4694
5436
  return { buffer, currentToolCall, shouldBreak: true };
4695
5437
  }
4696
5438
  const endIdx = endMatch.index;
4697
5439
  const endPos = endIdx + endMatch[0].length;
4698
5440
  const content = buffer.substring(0, endIdx);
5441
+ emitToolInputProgress2(controller, currentToolCall, content);
4699
5442
  const remainder = buffer.substring(endPos);
4700
5443
  setBuffer(remainder);
4701
5444
  handleStreamingToolCallEnd({
@@ -4722,7 +5465,8 @@ function processNoToolCallInBuffer(params) {
4722
5465
  tools,
4723
5466
  options,
4724
5467
  parseOptions,
4725
- setBuffer
5468
+ setBuffer,
5469
+ emitToolInputStart
4726
5470
  } = params;
4727
5471
  const {
4728
5472
  index: earliestStartTagIndex,
@@ -4752,9 +5496,10 @@ function processNoToolCallInBuffer(params) {
4752
5496
  if (selfClosing) {
4753
5497
  const newBuffer2 = buffer.substring(earliestStartTagIndex + tagLength);
4754
5498
  setBuffer(newBuffer2);
5499
+ const currentToolCall = emitToolInputStart(controller, earliestToolName);
4755
5500
  handleStreamingToolCallEnd({
4756
5501
  toolContent: "",
4757
- currentToolCall: { name: earliestToolName, content: "" },
5502
+ currentToolCall,
4758
5503
  tools,
4759
5504
  options,
4760
5505
  ctrl: controller,
@@ -4773,12 +5518,12 @@ function processNoToolCallInBuffer(params) {
4773
5518
  setBuffer(newBuffer);
4774
5519
  return {
4775
5520
  buffer: newBuffer,
4776
- currentToolCall: { name: earliestToolName, content: "" },
5521
+ currentToolCall: emitToolInputStart(controller, earliestToolName),
4777
5522
  shouldBreak: false,
4778
5523
  shouldContinue: true
4779
5524
  };
4780
5525
  }
4781
- function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, setCurrentToolCall, tools, options, toolNames, flushText, parseOptions) {
5526
+ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, setCurrentToolCall, tools, options, toolNames, flushText, parseOptions, emitToolInputProgress2, emitToolInputStart) {
4782
5527
  return (controller) => {
4783
5528
  while (true) {
4784
5529
  const currentToolCall = getCurrentToolCall();
@@ -4791,7 +5536,8 @@ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, se
4791
5536
  controller,
4792
5537
  flushText,
4793
5538
  setBuffer,
4794
- parseOptions
5539
+ parseOptions,
5540
+ emitToolInputProgress: emitToolInputProgress2
4795
5541
  });
4796
5542
  setBuffer(result.buffer);
4797
5543
  setCurrentToolCall(result.currentToolCall);
@@ -4807,7 +5553,8 @@ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, se
4807
5553
  tools,
4808
5554
  options,
4809
5555
  parseOptions,
4810
- setBuffer
5556
+ setBuffer,
5557
+ emitToolInputStart
4811
5558
  });
4812
5559
  setBuffer(result.buffer);
4813
5560
  setCurrentToolCall(result.currentToolCall);
@@ -4921,6 +5668,118 @@ var xmlProtocol = (protocolOptions) => {
4921
5668
  hasEmittedTextStart = value;
4922
5669
  }
4923
5670
  );
5671
+ const emitToolInputStart = (controller, toolName) => {
5672
+ flushText(controller);
5673
+ const next = {
5674
+ name: toolName,
5675
+ toolCallId: generateToolCallId(),
5676
+ emittedInput: "",
5677
+ lastProgressGtIndex: null,
5678
+ lastProgressFullInput: null
5679
+ };
5680
+ controller.enqueue({
5681
+ type: "tool-input-start",
5682
+ id: next.toolCallId,
5683
+ toolName
5684
+ });
5685
+ return next;
5686
+ };
5687
+ const emitToolInputProgress2 = (controller, toolCall, toolContent) => {
5688
+ const progressGtIndex = toolContent.lastIndexOf(">");
5689
+ if (toolCall.lastProgressGtIndex === progressGtIndex) {
5690
+ const cached = toolCall.lastProgressFullInput;
5691
+ if (cached == null) {
5692
+ return;
5693
+ }
5694
+ if (cached === "{}" && toolContent.trim().length === 0) {
5695
+ return;
5696
+ }
5697
+ const prefixCandidate2 = toIncompleteJsonPrefix(cached);
5698
+ emitPrefixDelta({
5699
+ controller,
5700
+ id: toolCall.toolCallId,
5701
+ state: toolCall,
5702
+ candidate: prefixCandidate2
5703
+ });
5704
+ return;
5705
+ }
5706
+ const toolSchema = getToolSchema(tools, toolCall.name);
5707
+ const fullInput = parseXmlContentForStreamProgress({
5708
+ toolContent,
5709
+ toolSchema,
5710
+ parseOptions
5711
+ });
5712
+ toolCall.lastProgressGtIndex = progressGtIndex;
5713
+ toolCall.lastProgressFullInput = fullInput;
5714
+ if (fullInput == null) {
5715
+ return;
5716
+ }
5717
+ if (fullInput === "{}" && toolContent.trim().length === 0) {
5718
+ return;
5719
+ }
5720
+ const prefixCandidate = toIncompleteJsonPrefix(fullInput);
5721
+ emitPrefixDelta({
5722
+ controller,
5723
+ id: toolCall.toolCallId,
5724
+ state: toolCall,
5725
+ candidate: prefixCandidate
5726
+ });
5727
+ };
5728
+ const finalizeUnclosedToolCall = (controller) => {
5729
+ var _a2, _b;
5730
+ if (!currentToolCall) {
5731
+ return;
5732
+ }
5733
+ emitToolInputProgress2(controller, currentToolCall, buffer);
5734
+ const parseConfig = {
5735
+ ...parseOptions,
5736
+ onError: (_a2 = options == null ? void 0 : options.onError) != null ? _a2 : parseOptions == null ? void 0 : parseOptions.onError
5737
+ };
5738
+ const toolSchema = getToolSchema(tools, currentToolCall.name);
5739
+ flushText(controller);
5740
+ try {
5741
+ if (hasNonWhitespaceTopLevelText(buffer)) {
5742
+ throw new Error(
5743
+ "Cannot reconcile unclosed XML tool call with top-level plain text."
5744
+ );
5745
+ }
5746
+ const parsedResult = parse3(buffer, toolSchema, parseConfig);
5747
+ const finalInput = JSON.stringify(parsedResult);
5748
+ emitFinalRemainder({
5749
+ controller,
5750
+ id: currentToolCall.toolCallId,
5751
+ state: currentToolCall,
5752
+ finalFullJson: finalInput,
5753
+ onMismatch: options == null ? void 0 : options.onError
5754
+ });
5755
+ controller.enqueue({
5756
+ type: "tool-input-end",
5757
+ id: currentToolCall.toolCallId
5758
+ });
5759
+ controller.enqueue({
5760
+ type: "tool-call",
5761
+ toolCallId: currentToolCall.toolCallId,
5762
+ toolName: currentToolCall.name,
5763
+ input: finalInput
5764
+ });
5765
+ } catch (error) {
5766
+ controller.enqueue({
5767
+ type: "tool-input-end",
5768
+ id: currentToolCall.toolCallId
5769
+ });
5770
+ const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
5771
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
5772
+ options,
5773
+ "Could not complete streaming XML tool call at finish.",
5774
+ { toolCall: unfinishedContent, error }
5775
+ );
5776
+ if (shouldEmitRawToolCallTextOnError2(options)) {
5777
+ flushText(controller, unfinishedContent);
5778
+ }
5779
+ }
5780
+ buffer = "";
5781
+ currentToolCall = null;
5782
+ };
4924
5783
  const processBuffer = createProcessBufferHandler(
4925
5784
  () => buffer,
4926
5785
  (newBuffer) => {
@@ -4934,17 +5793,17 @@ var xmlProtocol = (protocolOptions) => {
4934
5793
  options,
4935
5794
  toolNames,
4936
5795
  flushText,
4937
- parseOptions
5796
+ parseOptions,
5797
+ emitToolInputProgress2,
5798
+ emitToolInputStart
4938
5799
  );
4939
5800
  return new TransformStream({
5801
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Stateful stream parsing requires branching over chunk lifecycle and parser states.
4940
5802
  transform(chunk, controller) {
4941
5803
  var _a2;
4942
5804
  if (chunk.type === "finish") {
4943
5805
  if (currentToolCall) {
4944
- const unfinishedContent = `<${currentToolCall.name}>${currentToolCall.content}${buffer}`;
4945
- flushText(controller, unfinishedContent);
4946
- buffer = "";
4947
- currentToolCall = null;
5806
+ finalizeUnclosedToolCall(controller);
4948
5807
  } else if (buffer) {
4949
5808
  flushText(controller, buffer);
4950
5809
  buffer = "";
@@ -4954,7 +5813,8 @@ var xmlProtocol = (protocolOptions) => {
4954
5813
  return;
4955
5814
  }
4956
5815
  if (chunk.type !== "text-delta") {
4957
- if (buffer) {
5816
+ if (currentToolCall) {
5817
+ } else if (buffer) {
4958
5818
  flushText(controller, buffer);
4959
5819
  buffer = "";
4960
5820
  }
@@ -4967,10 +5827,7 @@ var xmlProtocol = (protocolOptions) => {
4967
5827
  },
4968
5828
  flush(controller) {
4969
5829
  if (currentToolCall) {
4970
- const unfinishedContent = `<${currentToolCall.name}>${currentToolCall.content || ""}${buffer}`;
4971
- flushText(controller, unfinishedContent);
4972
- buffer = "";
4973
- currentToolCall = null;
5830
+ finalizeUnclosedToolCall(controller);
4974
5831
  } else if (buffer) {
4975
5832
  flushText(controller, buffer);
4976
5833
  buffer = "";
@@ -4998,7 +5855,205 @@ var xmlProtocol = (protocolOptions) => {
4998
5855
 
4999
5856
  // src/core/protocols/yaml-protocol.ts
5000
5857
  var import_yaml = __toESM(require("yaml"), 1);
5858
+ function shouldEmitRawToolCallTextOnError3(options) {
5859
+ return (options == null ? void 0 : options.emitRawToolCallTextOnError) === true;
5860
+ }
5861
+ var selfClosingTagCache2 = /* @__PURE__ */ new Map();
5862
+ function getSelfClosingTagPattern2(toolName) {
5863
+ let pattern = selfClosingTagCache2.get(toolName);
5864
+ if (!pattern) {
5865
+ pattern = new RegExp(`<\\s*${escapeRegExp(toolName)}\\s*/>`, "g");
5866
+ selfClosingTagCache2.set(toolName, pattern);
5867
+ }
5868
+ return pattern;
5869
+ }
5001
5870
  var LEADING_WHITESPACE_RE = /^(\s*)/;
5871
+ var INCOMPLETE_MAPPING_TAIL_RE = /^[^:[\]{}-][^:]*:\s*$/;
5872
+ var INCOMPLETE_SEQUENCE_TAIL_RE = /^-\s*$/;
5873
+ var BLOCK_SCALAR_KEY_RE = /:\s*[|>][-+0-9]*\s*$/;
5874
+ var PLAIN_MAPPING_VALUE_RE = /^[^:[\]{}-][^:]*:\s*(.+)$/;
5875
+ var PLAIN_SEQUENCE_VALUE_RE = /^-\s+(.+)$/;
5876
+ function normalizeYamlContent(yamlContent) {
5877
+ let normalized = yamlContent;
5878
+ if (normalized.startsWith("\n")) {
5879
+ normalized = normalized.slice(1);
5880
+ }
5881
+ const lines = normalized.split("\n");
5882
+ const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
5883
+ if (nonEmptyLines.length === 0) {
5884
+ return { normalized: "", nonEmptyLines };
5885
+ }
5886
+ const minIndent = Math.min(
5887
+ ...nonEmptyLines.map((line) => {
5888
+ const match = line.match(LEADING_WHITESPACE_RE);
5889
+ return match ? match[1].length : 0;
5890
+ })
5891
+ );
5892
+ if (minIndent > 0) {
5893
+ normalized = lines.map((line) => line.slice(minIndent)).join("\n");
5894
+ }
5895
+ return { normalized, nonEmptyLines };
5896
+ }
5897
+ function parseYamlDocumentAsMapping(normalized) {
5898
+ try {
5899
+ const doc = import_yaml.default.parseDocument(normalized);
5900
+ const errors = doc.errors.map((e) => e.message);
5901
+ const result = doc.toJSON();
5902
+ if (result === null) {
5903
+ return { value: {}, errors };
5904
+ }
5905
+ if (typeof result !== "object" || Array.isArray(result)) {
5906
+ return { value: null, errors };
5907
+ }
5908
+ return { value: result, errors };
5909
+ } catch (error) {
5910
+ return {
5911
+ value: null,
5912
+ errors: [
5913
+ error instanceof Error ? error.message : "Unknown YAML parsing error"
5914
+ ]
5915
+ };
5916
+ }
5917
+ }
5918
+ function getLastMeaningfulLineInfo(input) {
5919
+ var _a;
5920
+ const lines = input.split("\n");
5921
+ let index = lines.length - 1;
5922
+ while (index >= 0) {
5923
+ const raw = (_a = lines[index]) != null ? _a : "";
5924
+ const trimmed = raw.trim();
5925
+ if (trimmed.length > 0 && !trimmed.startsWith("#")) {
5926
+ return {
5927
+ index,
5928
+ raw,
5929
+ trimmed,
5930
+ indent: raw.length - raw.trimStart().length
5931
+ };
5932
+ }
5933
+ index -= 1;
5934
+ }
5935
+ return null;
5936
+ }
5937
+ function dropLastMeaningfulLine(input) {
5938
+ const lineInfo = getLastMeaningfulLineInfo(input);
5939
+ if (!lineInfo) {
5940
+ return null;
5941
+ }
5942
+ return input.split("\n").slice(0, lineInfo.index).join("\n").trimEnd();
5943
+ }
5944
+ function hasIncompleteMappingTail(normalized) {
5945
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
5946
+ if (!lineInfo) {
5947
+ return false;
5948
+ }
5949
+ return INCOMPLETE_MAPPING_TAIL_RE.test(lineInfo.trimmed);
5950
+ }
5951
+ function hasIncompleteSequenceTail(normalized) {
5952
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
5953
+ if (!lineInfo) {
5954
+ return false;
5955
+ }
5956
+ return INCOMPLETE_SEQUENCE_TAIL_RE.test(lineInfo.trimmed);
5957
+ }
5958
+ function hasSplitNestedKeyTail(normalized) {
5959
+ var _a;
5960
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
5961
+ if (!lineInfo) {
5962
+ return false;
5963
+ }
5964
+ const { trimmed, indent, index } = lineInfo;
5965
+ if (indent === 0) {
5966
+ return false;
5967
+ }
5968
+ if (trimmed.startsWith("#") || trimmed.startsWith("-") || trimmed.includes(":")) {
5969
+ return false;
5970
+ }
5971
+ const lines = normalized.split("\n");
5972
+ let parentIndex = index - 1;
5973
+ while (parentIndex >= 0) {
5974
+ const parentRaw = (_a = lines[parentIndex]) != null ? _a : "";
5975
+ const parentTrimmed = parentRaw.trim();
5976
+ if (parentTrimmed.length === 0 || parentTrimmed.startsWith("#")) {
5977
+ parentIndex -= 1;
5978
+ continue;
5979
+ }
5980
+ const parentIndent = parentRaw.length - parentRaw.trimStart().length;
5981
+ if (parentIndent >= indent) {
5982
+ parentIndex -= 1;
5983
+ continue;
5984
+ }
5985
+ if (!parentTrimmed.endsWith(":")) {
5986
+ return false;
5987
+ }
5988
+ if (BLOCK_SCALAR_KEY_RE.test(parentTrimmed)) {
5989
+ return false;
5990
+ }
5991
+ return true;
5992
+ }
5993
+ return false;
5994
+ }
5995
+ function extractTrailingPlainScalarValue(line) {
5996
+ var _a;
5997
+ if (BLOCK_SCALAR_KEY_RE.test(line)) {
5998
+ return null;
5999
+ }
6000
+ const mappingMatch = line.match(PLAIN_MAPPING_VALUE_RE);
6001
+ const sequenceMatch = line.match(PLAIN_SEQUENCE_VALUE_RE);
6002
+ const value = (_a = mappingMatch == null ? void 0 : mappingMatch[1]) != null ? _a : sequenceMatch == null ? void 0 : sequenceMatch[1];
6003
+ if (!value) {
6004
+ return null;
6005
+ }
6006
+ const trimmedValue = value.trim();
6007
+ if (trimmedValue.length === 0) {
6008
+ return null;
6009
+ }
6010
+ if (trimmedValue.startsWith('"') || trimmedValue.startsWith("'")) {
6011
+ return null;
6012
+ }
6013
+ if (trimmedValue.startsWith("{") || trimmedValue.startsWith("[") || trimmedValue.startsWith("|") || trimmedValue.startsWith(">")) {
6014
+ return null;
6015
+ }
6016
+ return trimmedValue;
6017
+ }
6018
+ function hasUnterminatedPlainScalarTail(normalized) {
6019
+ if (normalized.endsWith("\n")) {
6020
+ return false;
6021
+ }
6022
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
6023
+ if (!lineInfo) {
6024
+ return false;
6025
+ }
6026
+ return extractTrailingPlainScalarValue(lineInfo.trimmed) != null;
6027
+ }
6028
+ function hasUnstableProgressTail(normalized) {
6029
+ return hasIncompleteMappingTail(normalized) || hasIncompleteSequenceTail(normalized) || hasSplitNestedKeyTail(normalized) || hasUnterminatedPlainScalarTail(normalized);
6030
+ }
6031
+ function trimTrailingNewlineInUnknown(value) {
6032
+ if (typeof value === "string") {
6033
+ if (value.endsWith("\n")) {
6034
+ return value.slice(0, -1);
6035
+ }
6036
+ return value;
6037
+ }
6038
+ if (Array.isArray(value)) {
6039
+ return value.map((item) => trimTrailingNewlineInUnknown(item));
6040
+ }
6041
+ if (value && typeof value === "object") {
6042
+ return Object.fromEntries(
6043
+ Object.entries(value).map(([key, item]) => [
6044
+ key,
6045
+ trimTrailingNewlineInUnknown(item)
6046
+ ])
6047
+ );
6048
+ }
6049
+ return value;
6050
+ }
6051
+ function stabilizeParsedValueForStreamProgress(value, source) {
6052
+ if (source.endsWith("\n")) {
6053
+ return value;
6054
+ }
6055
+ return trimTrailingNewlineInUnknown(value);
6056
+ }
5002
6057
  function findClosingTagEnd(text, contentStart, toolName) {
5003
6058
  let pos = contentStart;
5004
6059
  let depth = 1;
@@ -5070,7 +6125,7 @@ function findEarliestTagPosition(openIdx, selfIdx) {
5070
6125
  function collectToolCallsForName(text, toolName) {
5071
6126
  const toolCalls = [];
5072
6127
  let searchIndex = 0;
5073
- const selfTagRegex = new RegExp(`<${toolName}\\s*/>`, "g");
6128
+ const selfTagRegex = getSelfClosingTagPattern2(toolName);
5074
6129
  while (searchIndex < text.length) {
5075
6130
  const startTag = `<${toolName}>`;
5076
6131
  const openIdx = text.indexOf(startTag, searchIndex);
@@ -5122,47 +6177,48 @@ function findToolCalls2(text, toolNames) {
5122
6177
  return toolCalls.sort((a, b) => a.startIndex - b.startIndex);
5123
6178
  }
5124
6179
  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);
6180
+ var _a, _b;
6181
+ const { normalized, nonEmptyLines } = normalizeYamlContent(yamlContent);
5132
6182
  if (nonEmptyLines.length === 0) {
5133
6183
  return {};
5134
6184
  }
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");
6185
+ const parsed = parseYamlDocumentAsMapping(normalized);
6186
+ if (parsed.errors.length > 0) {
6187
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "YAML parse error", {
6188
+ errors: parsed.errors
6189
+ });
6190
+ return null;
5143
6191
  }
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;
6192
+ if (parsed.value === null) {
6193
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, "YAML content must be a key-value mapping", {
6194
+ got: "non-mapping"
6195
+ });
6196
+ return null;
6197
+ }
6198
+ return parsed.value;
6199
+ }
6200
+ function parseYamlContentForStreamProgress(yamlContent) {
6201
+ const { normalized, nonEmptyLines } = normalizeYamlContent(yamlContent);
6202
+ if (nonEmptyLines.length === 0) {
6203
+ return {};
6204
+ }
6205
+ let candidate = normalized;
6206
+ while (true) {
6207
+ const parsed = parseYamlDocumentAsMapping(candidate);
6208
+ if (parsed.errors.length === 0 && !hasUnstableProgressTail(candidate)) {
6209
+ if (candidate.trim().length === 0 && normalized.trim().length > 0) {
6210
+ return null;
6211
+ }
6212
+ return stabilizeParsedValueForStreamProgress(parsed.value, candidate);
5151
6213
  }
5152
- const result = doc.toJSON();
5153
- if (result === null) {
5154
- return {};
6214
+ const truncated = dropLastMeaningfulLine(candidate);
6215
+ if (truncated == null) {
6216
+ return null;
5155
6217
  }
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
- });
6218
+ if (truncated === candidate) {
5160
6219
  return null;
5161
6220
  }
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;
6221
+ candidate = truncated;
5166
6222
  }
5167
6223
  }
5168
6224
  function processToolCallMatch(text, tc, currentIndex, processedElements, options) {
@@ -5178,7 +6234,7 @@ function processToolCallMatch(text, tc, currentIndex, processedElements, options
5178
6234
  if (parsedArgs !== null) {
5179
6235
  processedElements.push({
5180
6236
  type: "tool-call",
5181
- toolCallId: generateId(),
6237
+ toolCallId: generateToolCallId(),
5182
6238
  toolName: tc.toolName,
5183
6239
  input: JSON.stringify(parsedArgs)
5184
6240
  });
@@ -5191,38 +6247,6 @@ function processToolCallMatch(text, tc, currentIndex, processedElements, options
5191
6247
  }
5192
6248
  return tc.endIndex;
5193
6249
  }
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
6250
  function findEarliestToolTag2(buffer, toolNames) {
5227
6251
  let bestIndex = -1;
5228
6252
  let bestName = "";
@@ -5230,8 +6254,9 @@ function findEarliestToolTag2(buffer, toolNames) {
5230
6254
  let bestTagLength = 0;
5231
6255
  for (const name of toolNames) {
5232
6256
  const openTag = `<${name}>`;
5233
- const selfTagRegex = new RegExp(`<${name}\\s*/>`);
6257
+ const selfTagRegex = getSelfClosingTagPattern2(name);
5234
6258
  const idxOpen = buffer.indexOf(openTag);
6259
+ selfTagRegex.lastIndex = 0;
5235
6260
  const selfMatch = selfTagRegex.exec(buffer);
5236
6261
  const idxSelf = selfMatch ? selfMatch.index : -1;
5237
6262
  if (idxOpen !== -1 && (bestIndex === -1 || idxOpen < bestIndex)) {
@@ -5254,6 +6279,29 @@ function findEarliestToolTag2(buffer, toolNames) {
5254
6279
  tagLength: bestTagLength
5255
6280
  };
5256
6281
  }
6282
+ function stripTrailingPartialCloseTag(content, toolName) {
6283
+ const closeTag = `</${toolName}>`;
6284
+ const lastLineBreakIndex = Math.max(
6285
+ content.lastIndexOf("\n"),
6286
+ content.lastIndexOf("\r")
6287
+ );
6288
+ const lineStartIndex = lastLineBreakIndex === -1 ? 0 : lastLineBreakIndex + 1;
6289
+ const trailingLine = content.slice(lineStartIndex);
6290
+ const trimmedTrailingLine = trailingLine.trim();
6291
+ if (trimmedTrailingLine.length === 0 || !trimmedTrailingLine.startsWith("</") || trimmedTrailingLine === closeTag || !closeTag.startsWith(trimmedTrailingLine)) {
6292
+ return content;
6293
+ }
6294
+ const leadingWhitespaceLength = trailingLine.length - trailingLine.trimStart().length;
6295
+ const preservedLeadingWhitespace = trailingLine.slice(
6296
+ 0,
6297
+ leadingWhitespaceLength
6298
+ );
6299
+ const contentWithoutPartial = `${content.slice(
6300
+ 0,
6301
+ lineStartIndex
6302
+ )}${preservedLeadingWhitespace}`;
6303
+ return contentWithoutPartial.trimEnd();
6304
+ }
5257
6305
  var yamlProtocol = (_protocolOptions) => {
5258
6306
  return {
5259
6307
  formatTools({ tools, toolSystemPromptTemplate }) {
@@ -5314,7 +6362,7 @@ ${yamlContent}</${toolCall.toolName}>`;
5314
6362
  let currentToolCall = null;
5315
6363
  let currentTextId = null;
5316
6364
  let hasEmittedTextStart = false;
5317
- const flushText = createFlushTextHandler2(
6365
+ const flushText = createFlushTextHandler(
5318
6366
  () => currentTextId,
5319
6367
  (newId) => {
5320
6368
  currentTextId = newId;
@@ -5324,33 +6372,128 @@ ${yamlContent}</${toolCall.toolName}>`;
5324
6372
  hasEmittedTextStart = value;
5325
6373
  }
5326
6374
  );
5327
- const processToolCallEnd = (controller, toolContent, toolName) => {
6375
+ const emitToolInputProgress2 = (controller, toolContent) => {
6376
+ if (!currentToolCall) {
6377
+ return;
6378
+ }
6379
+ const parsedArgs = parseYamlContentForStreamProgress(toolContent);
6380
+ if (parsedArgs === null) {
6381
+ return;
6382
+ }
6383
+ const fullInput = JSON.stringify(parsedArgs);
6384
+ if (fullInput === "{}" && toolContent.trim().length === 0) {
6385
+ return;
6386
+ }
6387
+ const prefixCandidate = toIncompleteJsonPrefix(fullInput);
6388
+ emitPrefixDelta({
6389
+ controller,
6390
+ id: currentToolCall.toolCallId,
6391
+ state: currentToolCall,
6392
+ candidate: prefixCandidate
6393
+ });
6394
+ };
6395
+ const processToolCallEnd = (controller, toolContent, toolName, toolCallId) => {
5328
6396
  var _a;
5329
6397
  const parsedArgs = parseYamlContent(toolContent, options);
5330
6398
  flushText(controller);
5331
6399
  if (parsedArgs !== null) {
6400
+ const finalInput = JSON.stringify(parsedArgs);
6401
+ if (currentToolCall && currentToolCall.toolCallId === toolCallId) {
6402
+ emitFinalRemainder({
6403
+ controller,
6404
+ id: toolCallId,
6405
+ state: currentToolCall,
6406
+ finalFullJson: finalInput,
6407
+ onMismatch: options == null ? void 0 : options.onError
6408
+ });
6409
+ }
6410
+ controller.enqueue({
6411
+ type: "tool-input-end",
6412
+ id: toolCallId
6413
+ });
5332
6414
  controller.enqueue({
5333
6415
  type: "tool-call",
5334
- toolCallId: generateId(),
6416
+ toolCallId,
5335
6417
  toolName,
5336
- input: JSON.stringify(parsedArgs)
6418
+ input: finalInput
5337
6419
  });
5338
6420
  } else {
6421
+ controller.enqueue({
6422
+ type: "tool-input-end",
6423
+ id: toolCallId
6424
+ });
5339
6425
  const original = `<${toolName}>${toolContent}</${toolName}>`;
5340
6426
  (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "Could not parse streaming YAML tool call", {
5341
6427
  toolCall: original
5342
6428
  });
5343
- flushText(controller, original);
6429
+ if (shouldEmitRawToolCallTextOnError3(options)) {
6430
+ flushText(controller, original);
6431
+ }
5344
6432
  }
5345
6433
  };
6434
+ const finalizeUnclosedToolCall = (controller) => {
6435
+ var _a;
6436
+ if (!currentToolCall) {
6437
+ return;
6438
+ }
6439
+ emitToolInputProgress2(controller, buffer);
6440
+ const { name: toolName, toolCallId } = currentToolCall;
6441
+ const reconciledBuffer = stripTrailingPartialCloseTag(buffer, toolName);
6442
+ const parsedArgs = parseYamlContent(reconciledBuffer, options);
6443
+ flushText(controller);
6444
+ if (parsedArgs !== null) {
6445
+ const finalInput = JSON.stringify(parsedArgs);
6446
+ emitFinalRemainder({
6447
+ controller,
6448
+ id: toolCallId,
6449
+ state: currentToolCall,
6450
+ finalFullJson: finalInput,
6451
+ onMismatch: options == null ? void 0 : options.onError
6452
+ });
6453
+ controller.enqueue({
6454
+ type: "tool-input-end",
6455
+ id: toolCallId
6456
+ });
6457
+ controller.enqueue({
6458
+ type: "tool-call",
6459
+ toolCallId,
6460
+ toolName,
6461
+ input: finalInput
6462
+ });
6463
+ } else {
6464
+ controller.enqueue({
6465
+ type: "tool-input-end",
6466
+ id: toolCallId
6467
+ });
6468
+ const unfinishedContent = `<${toolName}>${buffer}`;
6469
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
6470
+ options,
6471
+ "Could not complete streaming YAML tool call at finish.",
6472
+ { toolCall: unfinishedContent }
6473
+ );
6474
+ if (shouldEmitRawToolCallTextOnError3(options)) {
6475
+ flushText(controller, unfinishedContent);
6476
+ }
6477
+ }
6478
+ buffer = "";
6479
+ currentToolCall = null;
6480
+ };
5346
6481
  const handlePendingToolCall = (controller, endTag, toolName) => {
6482
+ var _a;
5347
6483
  const endIdx = buffer.indexOf(endTag);
5348
6484
  if (endIdx === -1) {
6485
+ emitToolInputProgress2(controller, buffer);
5349
6486
  return false;
5350
6487
  }
5351
6488
  const content = buffer.substring(0, endIdx);
6489
+ emitToolInputProgress2(controller, content);
5352
6490
  buffer = buffer.substring(endIdx + endTag.length);
5353
- processToolCallEnd(controller, content, toolName);
6491
+ processToolCallEnd(
6492
+ controller,
6493
+ content,
6494
+ toolName,
6495
+ (_a = currentToolCall == null ? void 0 : currentToolCall.toolCallId) != null ? _a : generateToolCallId()
6496
+ );
5354
6497
  currentToolCall = null;
5355
6498
  return true;
5356
6499
  };
@@ -5367,13 +6510,35 @@ ${yamlContent}</${toolCall.toolName}>`;
5367
6510
  if (tagIndex > 0) {
5368
6511
  flushText(controller, buffer.substring(0, tagIndex));
5369
6512
  }
6513
+ flushText(controller);
5370
6514
  if (selfClosing) {
5371
6515
  buffer = buffer.substring(tagIndex + tagLength);
5372
- processToolCallEnd(controller, "", tagName);
6516
+ const toolCallId = generateToolCallId();
6517
+ currentToolCall = {
6518
+ name: tagName,
6519
+ toolCallId,
6520
+ emittedInput: ""
6521
+ };
6522
+ controller.enqueue({
6523
+ type: "tool-input-start",
6524
+ id: toolCallId,
6525
+ toolName: tagName
6526
+ });
6527
+ processToolCallEnd(controller, "", tagName, toolCallId);
6528
+ currentToolCall = null;
5373
6529
  } else {
5374
6530
  const startTag = `<${tagName}>`;
5375
6531
  buffer = buffer.substring(tagIndex + startTag.length);
5376
- currentToolCall = { name: tagName, content: "" };
6532
+ currentToolCall = {
6533
+ name: tagName,
6534
+ toolCallId: generateToolCallId(),
6535
+ emittedInput: ""
6536
+ };
6537
+ controller.enqueue({
6538
+ type: "tool-input-start",
6539
+ id: currentToolCall.toolCallId,
6540
+ toolName: tagName
6541
+ });
5377
6542
  }
5378
6543
  };
5379
6544
  const processBuffer = (controller) => {
@@ -5402,10 +6567,7 @@ ${yamlContent}</${toolCall.toolName}>`;
5402
6567
  var _a;
5403
6568
  if (chunk.type === "finish") {
5404
6569
  if (currentToolCall) {
5405
- const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
5406
- flushText(controller, unfinishedContent);
5407
- buffer = "";
5408
- currentToolCall = null;
6570
+ finalizeUnclosedToolCall(controller);
5409
6571
  } else if (buffer) {
5410
6572
  flushText(controller, buffer);
5411
6573
  buffer = "";
@@ -5415,7 +6577,7 @@ ${yamlContent}</${toolCall.toolName}>`;
5415
6577
  return;
5416
6578
  }
5417
6579
  if (chunk.type !== "text-delta") {
5418
- if (buffer) {
6580
+ if (!currentToolCall && buffer) {
5419
6581
  flushText(controller, buffer);
5420
6582
  buffer = "";
5421
6583
  }
@@ -5428,10 +6590,7 @@ ${yamlContent}</${toolCall.toolName}>`;
5428
6590
  },
5429
6591
  flush(controller) {
5430
6592
  if (currentToolCall) {
5431
- const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
5432
- flushText(controller, unfinishedContent);
5433
- buffer = "";
5434
- currentToolCall = null;
6593
+ finalizeUnclosedToolCall(controller);
5435
6594
  } else if (buffer) {
5436
6595
  flushText(controller, buffer);
5437
6596
  buffer = "";
@@ -5594,9 +6753,6 @@ function isToolChoiceActive(params) {
5594
6753
  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
6754
  }
5596
6755
 
5597
- // src/generate-handler.ts
5598
- var import_provider_utils = require("@ai-sdk/provider-utils");
5599
-
5600
6756
  // src/core/utils/generated-text-json-recovery.ts
5601
6757
  function isRecord(value) {
5602
6758
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -5735,7 +6891,7 @@ function mergeJsonCandidatesByStart(tagged, codeBlocks, balanced) {
5735
6891
  function toToolCallPart(candidate) {
5736
6892
  return {
5737
6893
  type: "tool-call",
5738
- toolCallId: generateId(),
6894
+ toolCallId: generateToolCallId(),
5739
6895
  toolName: candidate.toolName,
5740
6896
  input: candidate.input
5741
6897
  };
@@ -5962,7 +7118,7 @@ async function handleToolChoice(doGenerate, params, tools) {
5962
7118
  }
5963
7119
  const toolCall = {
5964
7120
  type: "tool-call",
5965
- toolCallId: (0, import_provider_utils.generateId)(),
7121
+ toolCallId: generateToolCallId(),
5966
7122
  toolName,
5967
7123
  input
5968
7124
  };
@@ -6472,7 +7628,6 @@ unit: celsius
6472
7628
  }
6473
7629
 
6474
7630
  // src/stream-handler.ts
6475
- var import_provider_utils2 = require("@ai-sdk/provider-utils");
6476
7631
  async function wrapStream({
6477
7632
  protocol,
6478
7633
  doStream,
@@ -6510,10 +7665,22 @@ async function wrapStream({
6510
7665
  }
6511
7666
  )
6512
7667
  ).pipeThrough(protocol.createStreamParser({ tools, options }));
7668
+ let seenToolCall = false;
6513
7669
  const v3Stream = coreStream.pipeThrough(
6514
7670
  new TransformStream({
6515
7671
  transform(part, controller) {
6516
- const normalizedPart = part.type === "tool-call" ? coerceToolCallPart(part, tools) : part;
7672
+ let normalizedPart = part.type === "tool-call" ? coerceToolCallPart(part, tools) : part;
7673
+ if (normalizedPart.type === "tool-call") {
7674
+ seenToolCall = true;
7675
+ }
7676
+ if (normalizedPart.type === "finish" && seenToolCall && normalizedPart.finishReason.unified === "stop") {
7677
+ normalizedPart = {
7678
+ ...normalizedPart,
7679
+ finishReason: normalizeToolCallsFinishReason(
7680
+ normalizedPart.finishReason
7681
+ )
7682
+ };
7683
+ }
6517
7684
  if (debugLevel === "stream") {
6518
7685
  logParsedChunk(normalizedPart);
6519
7686
  }
@@ -6550,7 +7717,7 @@ async function toolChoiceStream({
6550
7717
  start(controller) {
6551
7718
  controller.enqueue({
6552
7719
  type: "tool-call",
6553
- toolCallId: (0, import_provider_utils2.generateId)(),
7720
+ toolCallId: generateToolCallId(),
6554
7721
  toolName,
6555
7722
  input
6556
7723
  });