@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.
package/dist/index.cjs CHANGED
@@ -894,9 +894,19 @@ function getPotentialStartIndex(text, searchedText) {
894
894
  if (directIndex !== -1) {
895
895
  return directIndex;
896
896
  }
897
- for (let i = text.length - 1; i >= 0; i -= 1) {
898
- const suffix = text.substring(i);
899
- if (searchedText.startsWith(suffix)) {
897
+ const textLength = text.length;
898
+ const searchedTextLength = searchedText.length;
899
+ const startAt = Math.max(0, textLength - searchedTextLength + 1);
900
+ for (let i = startAt; i < textLength; i++) {
901
+ let match = true;
902
+ const currentSuffixLength = textLength - i;
903
+ for (let j = 0; j < currentSuffixLength; j++) {
904
+ if (text[i + j] !== searchedText[j]) {
905
+ match = false;
906
+ break;
907
+ }
908
+ }
909
+ if (match) {
900
910
  return i;
901
911
  }
902
912
  }
@@ -905,7 +915,25 @@ function getPotentialStartIndex(text, searchedText) {
905
915
 
906
916
  // src/core/utils/id.ts
907
917
  function generateId() {
908
- return Math.random().toString(36).substring(2, 15);
918
+ return crypto.randomUUID().replace(/-/g, "").slice(0, 13);
919
+ }
920
+ var TOOL_CALL_ID_PREFIX = "call_";
921
+ var TOOL_CALL_ID_BODY_LENGTH = 24;
922
+ var TOOL_CALL_ID_ALPHANUM = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
923
+ function randomAlphaNumeric(length) {
924
+ var _a;
925
+ const bytes = new Uint8Array(length);
926
+ crypto.getRandomValues(bytes);
927
+ let out = "";
928
+ for (let i = 0; i < length; i += 1) {
929
+ const byte = bytes[i];
930
+ const index = (byte != null ? byte : 0) % TOOL_CALL_ID_ALPHANUM.length;
931
+ out += (_a = TOOL_CALL_ID_ALPHANUM[index]) != null ? _a : "0";
932
+ }
933
+ return out;
934
+ }
935
+ function generateToolCallId() {
936
+ return `${TOOL_CALL_ID_PREFIX}${randomAlphaNumeric(TOOL_CALL_ID_BODY_LENGTH)}`;
909
937
  }
910
938
 
911
939
  // src/core/utils/protocol-utils.ts
@@ -914,6 +942,38 @@ function addTextSegment(text, processedElements) {
914
942
  processedElements.push({ type: "text", text });
915
943
  }
916
944
  }
945
+ function createFlushTextHandler(getCurrentTextId, setCurrentTextId, getHasEmittedTextStart, setHasEmittedTextStart) {
946
+ return (controller, text) => {
947
+ const content = text;
948
+ if (content) {
949
+ if (!getCurrentTextId()) {
950
+ const newId = generateId();
951
+ setCurrentTextId(newId);
952
+ controller.enqueue({
953
+ type: "text-start",
954
+ id: newId
955
+ });
956
+ setHasEmittedTextStart(true);
957
+ }
958
+ controller.enqueue({
959
+ type: "text-delta",
960
+ id: getCurrentTextId(),
961
+ delta: content
962
+ });
963
+ }
964
+ const currentTextId = getCurrentTextId();
965
+ if (currentTextId && !text) {
966
+ if (getHasEmittedTextStart()) {
967
+ controller.enqueue({
968
+ type: "text-end",
969
+ id: currentTextId
970
+ });
971
+ setHasEmittedTextStart(false);
972
+ }
973
+ setCurrentTextId(null);
974
+ }
975
+ };
976
+ }
917
977
 
918
978
  // src/core/utils/regex.ts
919
979
  function escapeRegExp(literal) {
@@ -921,15 +981,21 @@ function escapeRegExp(literal) {
921
981
  }
922
982
 
923
983
  // src/core/protocols/json-protocol.ts
984
+ function shouldEmitRawToolCallTextOnError(options) {
985
+ return (options == null ? void 0 : options.emitRawToolCallTextOnError) === true;
986
+ }
987
+ function canonicalizeToolInput(argumentsValue) {
988
+ return JSON.stringify(argumentsValue != null ? argumentsValue : {});
989
+ }
924
990
  function processToolCallJson(toolCallJson, fullMatch, processedElements, options) {
925
- var _a, _b;
991
+ var _a;
926
992
  try {
927
993
  const parsedToolCall = parse(toolCallJson);
928
994
  processedElements.push({
929
995
  type: "tool-call",
930
- toolCallId: generateId(),
996
+ toolCallId: generateToolCallId(),
931
997
  toolName: parsedToolCall.name,
932
- input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
998
+ input: canonicalizeToolInput(parsedToolCall.arguments)
933
999
  });
934
1000
  } catch (error) {
935
1001
  logParseFailure({
@@ -938,7 +1004,7 @@ function processToolCallJson(toolCallJson, fullMatch, processedElements, options
938
1004
  snippet: fullMatch,
939
1005
  error
940
1006
  });
941
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
1007
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
942
1008
  options,
943
1009
  "Could not process JSON tool call, keeping original text.",
944
1010
  { toolCall: fullMatch, error }
@@ -959,6 +1025,293 @@ function processMatchedToolCall(context) {
959
1025
  }
960
1026
  return startIndex + match[0].length;
961
1027
  }
1028
+ var WHITESPACE_JSON_REGEX = /\s/;
1029
+ function skipJsonWhitespace(text, fromIndex) {
1030
+ let index = fromIndex;
1031
+ while (index < text.length && WHITESPACE_JSON_REGEX.test(text[index])) {
1032
+ index += 1;
1033
+ }
1034
+ return index;
1035
+ }
1036
+ function findTopLevelPropertyValueStart(text, property) {
1037
+ const objectStart = skipJsonWhitespace(text, 0);
1038
+ if (objectStart >= text.length || text.charAt(objectStart) !== "{") {
1039
+ return null;
1040
+ }
1041
+ let depth = 0;
1042
+ let inString = false;
1043
+ let escaping = false;
1044
+ for (let index = objectStart; index < text.length; index += 1) {
1045
+ const char = text.charAt(index);
1046
+ if (inString) {
1047
+ if (escaping) {
1048
+ escaping = false;
1049
+ continue;
1050
+ }
1051
+ if (char === "\\") {
1052
+ escaping = true;
1053
+ continue;
1054
+ }
1055
+ if (char === '"') {
1056
+ inString = false;
1057
+ }
1058
+ continue;
1059
+ }
1060
+ if (char === "{") {
1061
+ depth += 1;
1062
+ continue;
1063
+ }
1064
+ if (char === "}") {
1065
+ depth = Math.max(0, depth - 1);
1066
+ continue;
1067
+ }
1068
+ if (char !== '"') {
1069
+ continue;
1070
+ }
1071
+ if (depth !== 1) {
1072
+ inString = true;
1073
+ continue;
1074
+ }
1075
+ const keyStart = index + 1;
1076
+ let keyEnd = keyStart;
1077
+ let keyEscaped = false;
1078
+ while (keyEnd < text.length) {
1079
+ const keyChar = text.charAt(keyEnd);
1080
+ if (keyEscaped) {
1081
+ keyEscaped = false;
1082
+ } else if (keyChar === "\\") {
1083
+ keyEscaped = true;
1084
+ } else if (keyChar === '"') {
1085
+ break;
1086
+ }
1087
+ keyEnd += 1;
1088
+ }
1089
+ if (keyEnd >= text.length || text.charAt(keyEnd) !== '"') {
1090
+ return null;
1091
+ }
1092
+ const key = text.slice(keyStart, keyEnd);
1093
+ let valueCursor = skipJsonWhitespace(text, keyEnd + 1);
1094
+ if (valueCursor >= text.length || text.charAt(valueCursor) !== ":") {
1095
+ index = keyEnd;
1096
+ continue;
1097
+ }
1098
+ valueCursor = skipJsonWhitespace(text, valueCursor + 1);
1099
+ if (key === property) {
1100
+ return valueCursor < text.length ? valueCursor : null;
1101
+ }
1102
+ index = valueCursor - 1;
1103
+ }
1104
+ return null;
1105
+ }
1106
+ function extractTopLevelStringProperty(text, property) {
1107
+ const valueStart = findTopLevelPropertyValueStart(text, property);
1108
+ if (valueStart == null || valueStart >= text.length) {
1109
+ return void 0;
1110
+ }
1111
+ if (text.charAt(valueStart) !== '"') {
1112
+ return void 0;
1113
+ }
1114
+ let valueEnd = valueStart + 1;
1115
+ let escaped = false;
1116
+ while (valueEnd < text.length) {
1117
+ const char = text.charAt(valueEnd);
1118
+ if (escaped) {
1119
+ escaped = false;
1120
+ } else if (char === "\\") {
1121
+ escaped = true;
1122
+ } else if (char === '"') {
1123
+ return text.slice(valueStart + 1, valueEnd);
1124
+ }
1125
+ valueEnd += 1;
1126
+ }
1127
+ return void 0;
1128
+ }
1129
+ function extractJsonValueSlice(text, valueStart) {
1130
+ if (valueStart >= text.length) {
1131
+ return null;
1132
+ }
1133
+ const first = text.charAt(valueStart);
1134
+ if (first === "{" || first === "[") {
1135
+ const stack = [first];
1136
+ let inString = false;
1137
+ let escaped = false;
1138
+ for (let index2 = valueStart + 1; index2 < text.length; index2 += 1) {
1139
+ const char = text.charAt(index2);
1140
+ if (inString) {
1141
+ if (escaped) {
1142
+ escaped = false;
1143
+ } else if (char === "\\") {
1144
+ escaped = true;
1145
+ } else if (char === '"') {
1146
+ inString = false;
1147
+ }
1148
+ continue;
1149
+ }
1150
+ if (char === '"') {
1151
+ inString = true;
1152
+ continue;
1153
+ }
1154
+ if (char === "{" || char === "[") {
1155
+ stack.push(char);
1156
+ continue;
1157
+ }
1158
+ if (char === "}" || char === "]") {
1159
+ const open = stack.at(-1);
1160
+ if (open === "{" && char === "}" || open === "[" && char === "]") {
1161
+ stack.pop();
1162
+ if (stack.length === 0) {
1163
+ return {
1164
+ text: text.slice(valueStart, index2 + 1),
1165
+ complete: true
1166
+ };
1167
+ }
1168
+ }
1169
+ }
1170
+ }
1171
+ return {
1172
+ text: text.slice(valueStart),
1173
+ complete: false
1174
+ };
1175
+ }
1176
+ if (first === '"') {
1177
+ let escaped = false;
1178
+ for (let index2 = valueStart + 1; index2 < text.length; index2 += 1) {
1179
+ const char = text.charAt(index2);
1180
+ if (escaped) {
1181
+ escaped = false;
1182
+ } else if (char === "\\") {
1183
+ escaped = true;
1184
+ } else if (char === '"') {
1185
+ return {
1186
+ text: text.slice(valueStart, index2 + 1),
1187
+ complete: true
1188
+ };
1189
+ }
1190
+ }
1191
+ return {
1192
+ text: text.slice(valueStart),
1193
+ complete: false
1194
+ };
1195
+ }
1196
+ let index = valueStart;
1197
+ while (index < text.length) {
1198
+ const char = text.charAt(index);
1199
+ if (char === "," || char === "}" || WHITESPACE_JSON_REGEX.test(char)) {
1200
+ break;
1201
+ }
1202
+ index += 1;
1203
+ }
1204
+ return {
1205
+ text: text.slice(valueStart, index),
1206
+ complete: index < text.length
1207
+ };
1208
+ }
1209
+ function extractStreamingToolCallProgress(toolCallJson) {
1210
+ var _a;
1211
+ const toolName = extractTopLevelStringProperty(toolCallJson, "name");
1212
+ const argsValueStart = findTopLevelPropertyValueStart(
1213
+ toolCallJson,
1214
+ "arguments"
1215
+ );
1216
+ if (argsValueStart == null) {
1217
+ return {
1218
+ toolName,
1219
+ argumentsText: void 0,
1220
+ argumentsComplete: false
1221
+ };
1222
+ }
1223
+ const argsSlice = extractJsonValueSlice(toolCallJson, argsValueStart);
1224
+ return {
1225
+ toolName,
1226
+ argumentsText: argsSlice == null ? void 0 : argsSlice.text,
1227
+ argumentsComplete: (_a = argsSlice == null ? void 0 : argsSlice.complete) != null ? _a : false
1228
+ };
1229
+ }
1230
+ function ensureToolInputStart(state, controller, toolName) {
1231
+ if (!state.activeToolInput) {
1232
+ const id = generateToolCallId();
1233
+ state.activeToolInput = {
1234
+ id,
1235
+ toolName,
1236
+ emittedInput: ""
1237
+ };
1238
+ controller.enqueue({
1239
+ type: "tool-input-start",
1240
+ id,
1241
+ toolName
1242
+ });
1243
+ }
1244
+ }
1245
+ function emitToolInputDelta(state, controller, fullInput) {
1246
+ const active = state.activeToolInput;
1247
+ if (!active) {
1248
+ return;
1249
+ }
1250
+ if (!fullInput.startsWith(active.emittedInput)) {
1251
+ return;
1252
+ }
1253
+ const delta = fullInput.slice(active.emittedInput.length);
1254
+ if (delta.length === 0) {
1255
+ return;
1256
+ }
1257
+ controller.enqueue({
1258
+ type: "tool-input-delta",
1259
+ id: active.id,
1260
+ delta
1261
+ });
1262
+ active.emittedInput = fullInput;
1263
+ }
1264
+ function closeToolInput(state, controller) {
1265
+ if (!state.activeToolInput) {
1266
+ return;
1267
+ }
1268
+ controller.enqueue({
1269
+ type: "tool-input-end",
1270
+ id: state.activeToolInput.id
1271
+ });
1272
+ state.activeToolInput = null;
1273
+ }
1274
+ function emitToolCallFromParsed(state, controller, parsedToolCall) {
1275
+ var _a, _b, _c, _d;
1276
+ closeTextBlock(state, controller);
1277
+ const toolName = typeof parsedToolCall.name === "string" ? parsedToolCall.name : (_b = (_a = state.activeToolInput) == null ? void 0 : _a.toolName) != null ? _b : "unknown";
1278
+ const input = canonicalizeToolInput(parsedToolCall.arguments);
1279
+ ensureToolInputStart(state, controller, toolName);
1280
+ emitToolInputDelta(state, controller, input);
1281
+ const toolCallId = (_d = (_c = state.activeToolInput) == null ? void 0 : _c.id) != null ? _d : generateToolCallId();
1282
+ closeToolInput(state, controller);
1283
+ controller.enqueue({
1284
+ type: "tool-call",
1285
+ toolCallId,
1286
+ toolName,
1287
+ input
1288
+ });
1289
+ }
1290
+ function canonicalizeArgumentsProgressInput(progress) {
1291
+ if (progress.argumentsText === void 0 || !progress.argumentsComplete) {
1292
+ return void 0;
1293
+ }
1294
+ try {
1295
+ const parsedArguments = parse(progress.argumentsText);
1296
+ return canonicalizeToolInput(parsedArguments);
1297
+ } catch (e) {
1298
+ return void 0;
1299
+ }
1300
+ }
1301
+ function emitToolInputProgress(state, controller) {
1302
+ if (!(state.isInsideToolCall && state.currentToolCallJson)) {
1303
+ return;
1304
+ }
1305
+ const progress = extractStreamingToolCallProgress(state.currentToolCallJson);
1306
+ if (!progress.toolName) {
1307
+ return;
1308
+ }
1309
+ ensureToolInputStart(state, controller, progress.toolName);
1310
+ const canonicalProgressInput = canonicalizeArgumentsProgressInput(progress);
1311
+ if (canonicalProgressInput !== void 0) {
1312
+ emitToolInputDelta(state, controller, canonicalProgressInput);
1313
+ }
1314
+ }
962
1315
  function flushBuffer(state, controller, toolCallStart) {
963
1316
  if (state.buffer.length === 0) {
964
1317
  return;
@@ -989,44 +1342,77 @@ function closeTextBlock(state, controller) {
989
1342
  state.hasEmittedTextStart = false;
990
1343
  }
991
1344
  }
992
- function emitIncompleteToolCall(state, controller, toolCallStart) {
993
- if (!state.currentToolCallJson) {
1345
+ function emitIncompleteToolCall(state, controller, toolCallStart, trailingBuffer, options) {
1346
+ var _a;
1347
+ if (!state.currentToolCallJson && trailingBuffer.length === 0) {
1348
+ state.isInsideToolCall = false;
994
1349
  return;
995
1350
  }
1351
+ if (state.currentToolCallJson) {
1352
+ try {
1353
+ const parsedToolCall = parse(state.currentToolCallJson);
1354
+ emitToolCallFromParsed(state, controller, parsedToolCall);
1355
+ state.currentToolCallJson = "";
1356
+ state.isInsideToolCall = false;
1357
+ return;
1358
+ } catch (e) {
1359
+ }
1360
+ }
1361
+ const rawToolCallContent = `${state.currentToolCallJson}${trailingBuffer}`;
1362
+ const errorContent = `${toolCallStart}${rawToolCallContent}`;
1363
+ const shouldEmitRawFallback = shouldEmitRawToolCallTextOnError(options);
996
1364
  logParseFailure({
997
1365
  phase: "stream",
998
- reason: "Incomplete streaming tool call segment emitted as text",
999
- snippet: `${toolCallStart}${state.currentToolCallJson}`
1000
- });
1001
- const errorId = generateId();
1002
- const errorContent = `${toolCallStart}${state.currentToolCallJson}`;
1003
- controller.enqueue({
1004
- type: "text-start",
1005
- id: errorId
1006
- });
1007
- controller.enqueue({
1008
- type: "text-delta",
1009
- id: errorId,
1010
- delta: errorContent
1011
- });
1012
- controller.enqueue({
1013
- type: "text-end",
1014
- id: errorId
1366
+ reason: shouldEmitRawFallback ? "Incomplete streaming tool call segment emitted as text" : "Incomplete streaming tool call segment suppressed without raw text fallback",
1367
+ snippet: errorContent
1015
1368
  });
1369
+ if (shouldEmitRawFallback) {
1370
+ const errorId = generateId();
1371
+ controller.enqueue({
1372
+ type: "text-start",
1373
+ id: errorId
1374
+ });
1375
+ controller.enqueue({
1376
+ type: "text-delta",
1377
+ id: errorId,
1378
+ delta: errorContent
1379
+ });
1380
+ controller.enqueue({
1381
+ type: "text-end",
1382
+ id: errorId
1383
+ });
1384
+ }
1385
+ closeToolInput(state, controller);
1386
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
1387
+ options,
1388
+ shouldEmitRawFallback ? "Could not complete streaming JSON tool call at finish; emitting original text." : "Could not complete streaming JSON tool call at finish.",
1389
+ { toolCall: errorContent }
1390
+ );
1016
1391
  state.currentToolCallJson = "";
1392
+ state.isInsideToolCall = false;
1017
1393
  }
1018
- function handleFinishChunk(state, controller, toolCallStart, chunk) {
1019
- if (state.buffer.length > 0) {
1394
+ function handleFinishChunk(state, controller, toolCallStart, options, chunk) {
1395
+ if (state.isInsideToolCall) {
1396
+ const trailingBuffer = state.buffer;
1397
+ state.buffer = "";
1398
+ emitIncompleteToolCall(
1399
+ state,
1400
+ controller,
1401
+ toolCallStart,
1402
+ trailingBuffer,
1403
+ options
1404
+ );
1405
+ } else if (state.buffer.length > 0) {
1020
1406
  flushBuffer(state, controller, toolCallStart);
1021
1407
  }
1022
1408
  closeTextBlock(state, controller);
1023
- emitIncompleteToolCall(state, controller, toolCallStart);
1024
1409
  controller.enqueue(chunk);
1025
1410
  }
1026
1411
  function publishText(text, state, controller) {
1027
1412
  if (state.isInsideToolCall) {
1028
1413
  closeTextBlock(state, controller);
1029
1414
  state.currentToolCallJson += text;
1415
+ emitToolInputProgress(state, controller);
1030
1416
  } else if (text.length > 0) {
1031
1417
  if (!state.currentTextId) {
1032
1418
  state.currentTextId = generateId();
@@ -1044,42 +1430,40 @@ function publishText(text, state, controller) {
1044
1430
  }
1045
1431
  }
1046
1432
  function emitToolCall(context) {
1047
- var _a, _b;
1433
+ var _a;
1048
1434
  const { state, controller, toolCallStart, toolCallEnd, options } = context;
1049
1435
  try {
1050
1436
  const parsedToolCall = parse(state.currentToolCallJson);
1051
- closeTextBlock(state, controller);
1052
- controller.enqueue({
1053
- type: "tool-call",
1054
- toolCallId: generateId(),
1055
- toolName: parsedToolCall.name,
1056
- input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
1057
- });
1437
+ emitToolCallFromParsed(state, controller, parsedToolCall);
1058
1438
  } catch (error) {
1439
+ const errorContent = `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`;
1440
+ const shouldEmitRawFallback = shouldEmitRawToolCallTextOnError(options);
1059
1441
  logParseFailure({
1060
1442
  phase: "stream",
1061
1443
  reason: "Failed to parse streaming tool call JSON segment",
1062
- snippet: `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`,
1444
+ snippet: errorContent,
1063
1445
  error
1064
1446
  });
1065
- const errorId = generateId();
1066
- const errorContent = `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`;
1067
- controller.enqueue({
1068
- type: "text-start",
1069
- id: errorId
1070
- });
1071
- controller.enqueue({
1072
- type: "text-delta",
1073
- id: errorId,
1074
- delta: errorContent
1075
- });
1076
- controller.enqueue({
1077
- type: "text-end",
1078
- id: errorId
1079
- });
1080
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
1447
+ if (shouldEmitRawFallback) {
1448
+ const errorId = generateId();
1449
+ controller.enqueue({
1450
+ type: "text-start",
1451
+ id: errorId
1452
+ });
1453
+ controller.enqueue({
1454
+ type: "text-delta",
1455
+ id: errorId,
1456
+ delta: errorContent
1457
+ });
1458
+ controller.enqueue({
1459
+ type: "text-end",
1460
+ id: errorId
1461
+ });
1462
+ }
1463
+ closeToolInput(state, controller);
1464
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
1081
1465
  options,
1082
- "Could not process streaming JSON tool call; emitting original text.",
1466
+ shouldEmitRawFallback ? "Could not process streaming JSON tool call; emitting original text." : "Could not process streaming JSON tool call.",
1083
1467
  {
1084
1468
  toolCall: errorContent
1085
1469
  }
@@ -1095,6 +1479,7 @@ function processTagMatch(context) {
1095
1479
  } else {
1096
1480
  state.currentToolCallJson = "";
1097
1481
  state.isInsideToolCall = true;
1482
+ state.activeToolInput = null;
1098
1483
  }
1099
1484
  }
1100
1485
  function processBufferTags(context) {
@@ -1117,8 +1502,16 @@ function processBufferTags(context) {
1117
1502
  );
1118
1503
  }
1119
1504
  }
1120
- function handlePartialTag(state, controller, toolCallStart) {
1505
+ function handlePartialTag(state, controller, toolCallStart, toolCallEnd) {
1121
1506
  if (state.isInsideToolCall) {
1507
+ const potentialEndIndex = getPotentialStartIndex(state.buffer, toolCallEnd);
1508
+ if (potentialEndIndex != null && potentialEndIndex + toolCallEnd.length > state.buffer.length) {
1509
+ publishText(state.buffer.slice(0, potentialEndIndex), state, controller);
1510
+ state.buffer = state.buffer.slice(potentialEndIndex);
1511
+ } else {
1512
+ publishText(state.buffer, state, controller);
1513
+ state.buffer = "";
1514
+ }
1122
1515
  return;
1123
1516
  }
1124
1517
  const potentialIndex = getPotentialStartIndex(state.buffer, toolCallStart);
@@ -1191,13 +1584,14 @@ var jsonProtocol = ({
1191
1584
  buffer: "",
1192
1585
  currentToolCallJson: "",
1193
1586
  currentTextId: null,
1194
- hasEmittedTextStart: false
1587
+ hasEmittedTextStart: false,
1588
+ activeToolInput: null
1195
1589
  };
1196
1590
  return new TransformStream({
1197
1591
  transform(chunk, controller) {
1198
1592
  var _a;
1199
1593
  if (chunk.type === "finish") {
1200
- handleFinishChunk(state, controller, toolCallStart, chunk);
1594
+ handleFinishChunk(state, controller, toolCallStart, options, chunk);
1201
1595
  return;
1202
1596
  }
1203
1597
  if (chunk.type !== "text-delta") {
@@ -1213,7 +1607,7 @@ var jsonProtocol = ({
1213
1607
  toolCallEnd,
1214
1608
  options
1215
1609
  });
1216
- handlePartialTag(state, controller, toolCallStart);
1610
+ handlePartialTag(state, controller, toolCallStart, toolCallEnd);
1217
1611
  }
1218
1612
  });
1219
1613
  },
@@ -1800,13 +2194,20 @@ function coerceStringWithoutSchema(value) {
1800
2194
  }
1801
2195
  function coerceStringToObject(s, unwrapped) {
1802
2196
  try {
1803
- let normalized = s.replace(/'/g, '"');
1804
- normalized = normalized.replace(EMPTY_OBJECT_REGEX, "{}");
1805
- const obj = JSON.parse(normalized);
2197
+ const obj = JSON.parse(s);
1806
2198
  if (obj && typeof obj === "object" && !Array.isArray(obj)) {
1807
2199
  return coerceObjectToObject(obj, unwrapped);
1808
2200
  }
1809
2201
  } catch (e) {
2202
+ try {
2203
+ let normalized = s.replace(/'/g, '"');
2204
+ normalized = normalized.replace(EMPTY_OBJECT_REGEX, "{}");
2205
+ const obj = JSON.parse(normalized);
2206
+ if (obj && typeof obj === "object" && !Array.isArray(obj)) {
2207
+ return coerceObjectToObject(obj, unwrapped);
2208
+ }
2209
+ } catch (e2) {
2210
+ }
1810
2211
  }
1811
2212
  return null;
1812
2213
  }
@@ -1814,8 +2215,7 @@ function coerceStringToArray(s, unwrapped) {
1814
2215
  const prefixItems = Array.isArray(unwrapped.prefixItems) ? unwrapped.prefixItems : void 0;
1815
2216
  const itemsSchema = unwrapped.items;
1816
2217
  try {
1817
- const normalized = s.replace(/'/g, '"');
1818
- const arr = JSON.parse(normalized);
2218
+ const arr = JSON.parse(s);
1819
2219
  if (Array.isArray(arr)) {
1820
2220
  if (prefixItems && arr.length === prefixItems.length) {
1821
2221
  return arr.map((v, i) => coerceBySchema(v, prefixItems[i]));
@@ -1823,6 +2223,17 @@ function coerceStringToArray(s, unwrapped) {
1823
2223
  return arr.map((v) => coerceBySchema(v, itemsSchema));
1824
2224
  }
1825
2225
  } catch (e) {
2226
+ try {
2227
+ const normalized = s.replace(/'/g, '"');
2228
+ const arr = JSON.parse(normalized);
2229
+ if (Array.isArray(arr)) {
2230
+ if (prefixItems && arr.length === prefixItems.length) {
2231
+ return arr.map((v, i) => coerceBySchema(v, prefixItems[i]));
2232
+ }
2233
+ return arr.map((v) => coerceBySchema(v, itemsSchema));
2234
+ }
2235
+ } catch (e2) {
2236
+ }
1826
2237
  const csv = s.includes("\n") ? s.split(NEWLINE_SPLIT_REGEX) : s.split(COMMA_SPLIT_REGEX);
1827
2238
  const trimmed = csv.map((x) => x.trim()).filter((x) => x.length > 0);
1828
2239
  if (prefixItems && trimmed.length === prefixItems.length) {
@@ -4193,22 +4604,96 @@ function parse3(xml, schema, options = {}) {
4193
4604
  var NAME_CHAR_RE2 = /[A-Za-z0-9_:-]/;
4194
4605
  var WHITESPACE_REGEX5 = /\s/;
4195
4606
 
4196
- // src/core/utils/xml-root-repair.ts
4197
- var XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX = /^<([A-Za-z_][A-Za-z0-9_-]*)\s*\r?\n([\s\S]+?)\r?\n\s*\/>\s*$/;
4198
- function tryRepairXmlSelfClosingRootWithBody(rawText, toolNames) {
4199
- const trimmed = rawText.trim();
4200
- if (trimmed.length === 0) {
4201
- return null;
4202
- }
4203
- const match = trimmed.match(XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX);
4204
- if (!match) {
4205
- return null;
4607
+ // src/core/utils/streamed-tool-input-delta.ts
4608
+ function emitDelta({
4609
+ controller,
4610
+ id,
4611
+ state,
4612
+ nextInput
4613
+ }) {
4614
+ if (!nextInput.startsWith(state.emittedInput)) {
4615
+ return false;
4206
4616
  }
4207
- const rootTag = match[1];
4208
- if (!toolNames.includes(rootTag)) {
4209
- return null;
4617
+ const delta = nextInput.slice(state.emittedInput.length);
4618
+ if (delta.length === 0) {
4619
+ return false;
4210
4620
  }
4211
- const body = match[2].trimEnd();
4621
+ controller.enqueue({
4622
+ type: "tool-input-delta",
4623
+ id,
4624
+ delta
4625
+ });
4626
+ state.emittedInput = nextInput;
4627
+ return true;
4628
+ }
4629
+ function toIncompleteJsonPrefix(fullJson) {
4630
+ const trimmed = fullJson.trim();
4631
+ let prefix = trimmed;
4632
+ while (prefix.endsWith("}") || prefix.endsWith("]")) {
4633
+ prefix = prefix.slice(0, -1);
4634
+ }
4635
+ prefix = prefix.trimEnd();
4636
+ if (prefix.endsWith('"')) {
4637
+ prefix = prefix.slice(0, -1);
4638
+ }
4639
+ if (prefix.length === 0) {
4640
+ if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
4641
+ return trimmed.startsWith("{") ? "{" : "[";
4642
+ }
4643
+ if (trimmed.startsWith("]")) {
4644
+ return "[";
4645
+ }
4646
+ if (trimmed.startsWith("}")) {
4647
+ return "{";
4648
+ }
4649
+ if (trimmed.startsWith('"')) {
4650
+ return '"';
4651
+ }
4652
+ return "{";
4653
+ }
4654
+ return prefix;
4655
+ }
4656
+ function emitPrefixDelta(params) {
4657
+ return emitDelta({
4658
+ ...params,
4659
+ nextInput: params.candidate
4660
+ });
4661
+ }
4662
+ function emitFinalRemainder(params) {
4663
+ var _a;
4664
+ const result = emitDelta({
4665
+ ...params,
4666
+ nextInput: params.finalFullJson
4667
+ });
4668
+ if (!result && params.state.emittedInput.length > 0) {
4669
+ (_a = params.onMismatch) == null ? void 0 : _a.call(
4670
+ params,
4671
+ "Final JSON does not extend emitted tool-input prefix",
4672
+ {
4673
+ emittedLength: params.state.emittedInput.length,
4674
+ finalLength: params.finalFullJson.length
4675
+ }
4676
+ );
4677
+ }
4678
+ return result;
4679
+ }
4680
+
4681
+ // src/core/utils/xml-root-repair.ts
4682
+ var XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX = /^<([A-Za-z_][A-Za-z0-9_-]*)\s*\r?\n([\s\S]+?)\r?\n\s*\/>\s*$/;
4683
+ function tryRepairXmlSelfClosingRootWithBody(rawText, toolNames) {
4684
+ const trimmed = rawText.trim();
4685
+ if (trimmed.length === 0) {
4686
+ return null;
4687
+ }
4688
+ const match = trimmed.match(XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX);
4689
+ if (!match) {
4690
+ return null;
4691
+ }
4692
+ const rootTag = match[1];
4693
+ if (!toolNames.includes(rootTag)) {
4694
+ return null;
4695
+ }
4696
+ const body = match[2].trimEnd();
4212
4697
  if (body.trim().length === 0 || body.includes(`</${rootTag}>`)) {
4213
4698
  return null;
4214
4699
  }
@@ -4222,6 +4707,9 @@ function getToolSchema(tools, toolName) {
4222
4707
  var _a;
4223
4708
  return (_a = tools.find((t) => t.name === toolName)) == null ? void 0 : _a.inputSchema;
4224
4709
  }
4710
+ function shouldEmitRawToolCallTextOnError2(options) {
4711
+ return (options == null ? void 0 : options.emitRawToolCallTextOnError) === true;
4712
+ }
4225
4713
  function processToolCall(params) {
4226
4714
  var _a, _b;
4227
4715
  const { toolCall, tools, options, text, processedElements, parseOptions } = params;
@@ -4234,7 +4722,7 @@ function processToolCall(params) {
4234
4722
  const parsed = parse3(toolCall.content, toolSchema, parseConfig);
4235
4723
  processedElements.push({
4236
4724
  type: "tool-call",
4237
- toolCallId: generateId(),
4725
+ toolCallId: generateToolCallId(),
4238
4726
  toolName: toolCall.toolName,
4239
4727
  input: JSON.stringify(parsed)
4240
4728
  });
@@ -4251,6 +4739,299 @@ function processToolCall(params) {
4251
4739
  processedElements.push({ type: "text", text: originalCallText });
4252
4740
  }
4253
4741
  }
4742
+ function parseXmlTagName(rawTagBody) {
4743
+ let index = 0;
4744
+ while (index < rawTagBody.length && WHITESPACE_REGEX5.test(rawTagBody[index])) {
4745
+ index += 1;
4746
+ }
4747
+ const nameStart = index;
4748
+ while (index < rawTagBody.length && NAME_CHAR_RE2.test(rawTagBody.charAt(index))) {
4749
+ index += 1;
4750
+ }
4751
+ return rawTagBody.slice(nameStart, index);
4752
+ }
4753
+ function consumeXmlSpecialSection(fragment, ltIndex) {
4754
+ if (fragment.startsWith("<!--", ltIndex)) {
4755
+ const commentEnd = fragment.indexOf("-->", ltIndex + 4);
4756
+ return commentEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: commentEnd + 3 };
4757
+ }
4758
+ if (fragment.startsWith("<![CDATA[", ltIndex)) {
4759
+ const cdataEnd = fragment.indexOf("]]>", ltIndex + 9);
4760
+ return cdataEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: cdataEnd + 3 };
4761
+ }
4762
+ if (fragment.startsWith("<?", ltIndex)) {
4763
+ const processingEnd = fragment.indexOf("?>", ltIndex + 2);
4764
+ return processingEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: processingEnd + 2 };
4765
+ }
4766
+ if (fragment.startsWith("<!", ltIndex)) {
4767
+ const declarationEnd = fragment.indexOf(">", ltIndex + 2);
4768
+ return declarationEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: declarationEnd + 1 };
4769
+ }
4770
+ return { kind: "none" };
4771
+ }
4772
+ function parseXmlTagToken(fragment, ltIndex) {
4773
+ const gtIndex = fragment.indexOf(">", ltIndex + 1);
4774
+ if (gtIndex === -1) {
4775
+ return null;
4776
+ }
4777
+ const tagBody = fragment.slice(ltIndex + 1, gtIndex).trim();
4778
+ if (tagBody.length === 0) {
4779
+ return null;
4780
+ }
4781
+ if (tagBody.startsWith("/")) {
4782
+ const closeName = parseXmlTagName(tagBody.slice(1));
4783
+ if (closeName.length === 0) {
4784
+ return null;
4785
+ }
4786
+ return { kind: "close", name: closeName, nextPos: gtIndex + 1 };
4787
+ }
4788
+ const selfClosing = tagBody.endsWith("/");
4789
+ const openBody = selfClosing ? tagBody.slice(0, -1).trimEnd() : tagBody;
4790
+ const openName = parseXmlTagName(openBody);
4791
+ if (openName.length === 0) {
4792
+ return null;
4793
+ }
4794
+ return {
4795
+ kind: "open",
4796
+ name: openName,
4797
+ selfClosing,
4798
+ nextPos: gtIndex + 1
4799
+ };
4800
+ }
4801
+ function analyzeXmlFragmentForProgress(fragment) {
4802
+ const stack = [];
4803
+ const topLevelTagNames = [];
4804
+ let position = 0;
4805
+ while (position < fragment.length) {
4806
+ const ltIndex = fragment.indexOf("<", position);
4807
+ if (ltIndex === -1) {
4808
+ break;
4809
+ }
4810
+ const special = consumeXmlSpecialSection(fragment, ltIndex);
4811
+ if (special.kind === "incomplete") {
4812
+ return null;
4813
+ }
4814
+ if (special.kind === "consumed") {
4815
+ position = special.nextPos;
4816
+ continue;
4817
+ }
4818
+ const token = parseXmlTagToken(fragment, ltIndex);
4819
+ if (token === null) {
4820
+ return null;
4821
+ }
4822
+ if (token.kind === "close") {
4823
+ const openName = stack.pop();
4824
+ if (!openName || openName !== token.name) {
4825
+ return null;
4826
+ }
4827
+ position = token.nextPos;
4828
+ continue;
4829
+ }
4830
+ if (stack.length === 0) {
4831
+ topLevelTagNames.push(token.name);
4832
+ }
4833
+ if (!token.selfClosing) {
4834
+ stack.push(token.name);
4835
+ }
4836
+ position = token.nextPos;
4837
+ }
4838
+ if (stack.length > 0) {
4839
+ return null;
4840
+ }
4841
+ return { topLevelTagNames };
4842
+ }
4843
+ function scanXmlFragmentTopLevelTextStep(options) {
4844
+ const { fragment, position, stack } = options;
4845
+ const ltIndex = fragment.indexOf("<", position);
4846
+ if (ltIndex === -1) {
4847
+ const trailingText = fragment.slice(position);
4848
+ return {
4849
+ kind: "done",
4850
+ value: stack.length === 0 && trailingText.trim().length > 0
4851
+ };
4852
+ }
4853
+ const textBetweenTags = fragment.slice(position, ltIndex);
4854
+ if (stack.length === 0 && textBetweenTags.trim().length > 0) {
4855
+ return { kind: "found" };
4856
+ }
4857
+ const special = consumeXmlSpecialSection(fragment, ltIndex);
4858
+ if (special.kind === "incomplete") {
4859
+ return { kind: "invalid" };
4860
+ }
4861
+ if (special.kind === "consumed") {
4862
+ return { kind: "next", nextPos: special.nextPos };
4863
+ }
4864
+ const token = parseXmlTagToken(fragment, ltIndex);
4865
+ if (token === null) {
4866
+ return { kind: "invalid" };
4867
+ }
4868
+ if (token.kind === "close") {
4869
+ const openName = stack.pop();
4870
+ if (!openName || openName !== token.name) {
4871
+ return { kind: "invalid" };
4872
+ }
4873
+ } else if (!token.selfClosing) {
4874
+ stack.push(token.name);
4875
+ }
4876
+ return { kind: "next", nextPos: token.nextPos };
4877
+ }
4878
+ function hasNonWhitespaceTopLevelText(fragment) {
4879
+ if (!fragment.includes("<")) {
4880
+ return fragment.trim().length > 0;
4881
+ }
4882
+ const stack = [];
4883
+ let position = 0;
4884
+ while (position < fragment.length) {
4885
+ const step = scanXmlFragmentTopLevelTextStep({ fragment, position, stack });
4886
+ if (step.kind === "found") {
4887
+ return true;
4888
+ }
4889
+ if (step.kind === "invalid") {
4890
+ return false;
4891
+ }
4892
+ if (step.kind === "done") {
4893
+ return step.value;
4894
+ }
4895
+ position = step.nextPos;
4896
+ }
4897
+ return false;
4898
+ }
4899
+ function getObjectSchemaPropertyNames(schema) {
4900
+ if (!schema || typeof schema !== "object") {
4901
+ return null;
4902
+ }
4903
+ const schemaObject = schema;
4904
+ const typeValue = schemaObject.type;
4905
+ if (typeValue != null) {
4906
+ const isObjectType = typeValue === "object" || Array.isArray(typeValue) && typeValue.includes("object");
4907
+ if (!isObjectType) {
4908
+ return null;
4909
+ }
4910
+ }
4911
+ if (!schemaObject.properties || typeof schemaObject.properties !== "object") {
4912
+ return /* @__PURE__ */ new Set();
4913
+ }
4914
+ return new Set(
4915
+ Object.keys(schemaObject.properties)
4916
+ );
4917
+ }
4918
+ function schemaAllowsArrayType(schema) {
4919
+ if (!schema || typeof schema !== "object") {
4920
+ return false;
4921
+ }
4922
+ const schemaRecord = schema;
4923
+ const typeValue = schemaRecord.type;
4924
+ if (typeValue === "array") {
4925
+ return true;
4926
+ }
4927
+ if (Array.isArray(typeValue) && typeValue.includes("array")) {
4928
+ return true;
4929
+ }
4930
+ const unions = [schemaRecord.anyOf, schemaRecord.oneOf, schemaRecord.allOf];
4931
+ for (const union of unions) {
4932
+ if (!Array.isArray(union)) {
4933
+ continue;
4934
+ }
4935
+ if (union.some((entry) => schemaAllowsArrayType(entry))) {
4936
+ return true;
4937
+ }
4938
+ }
4939
+ return false;
4940
+ }
4941
+ function getSchemaObjectProperty(schema, propertyName) {
4942
+ if (!schema || typeof schema !== "object") {
4943
+ return null;
4944
+ }
4945
+ const schemaObject = schema;
4946
+ const properties = schemaObject.properties;
4947
+ if (!properties || typeof properties !== "object") {
4948
+ return null;
4949
+ }
4950
+ const property = properties[propertyName];
4951
+ if (!property) {
4952
+ return null;
4953
+ }
4954
+ return property;
4955
+ }
4956
+ function isStableXmlProgressCandidate(options) {
4957
+ const { candidate, parsed, toolSchema } = options;
4958
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
4959
+ return false;
4960
+ }
4961
+ const structure = analyzeXmlFragmentForProgress(candidate);
4962
+ if (!structure) {
4963
+ return false;
4964
+ }
4965
+ const schemaProperties = getObjectSchemaPropertyNames(toolSchema);
4966
+ if (!schemaProperties || schemaProperties.size === 0) {
4967
+ return false;
4968
+ }
4969
+ const parsedObject = parsed;
4970
+ const uniqueTopLevelTags = new Set(structure.topLevelTagNames);
4971
+ for (const tagName of uniqueTopLevelTags) {
4972
+ if (!schemaProperties.has(tagName)) {
4973
+ continue;
4974
+ }
4975
+ const schemaProperty = getSchemaObjectProperty(toolSchema, tagName);
4976
+ if (schemaProperty && schemaAllowsArrayType(schemaProperty) && !Array.isArray(parsedObject[tagName])) {
4977
+ return false;
4978
+ }
4979
+ }
4980
+ if (structure.topLevelTagNames.length === 1) {
4981
+ const onlyTopLevelTag = structure.topLevelTagNames[0];
4982
+ if (!schemaProperties || schemaProperties.size === 0 || !schemaProperties.has(onlyTopLevelTag)) {
4983
+ return false;
4984
+ }
4985
+ }
4986
+ return true;
4987
+ }
4988
+ function parseXmlContentForStreamProgress({
4989
+ toolContent,
4990
+ toolSchema,
4991
+ parseOptions
4992
+ }) {
4993
+ const tryParse = (content) => {
4994
+ try {
4995
+ return parse3(content, toolSchema, {
4996
+ ...parseOptions != null ? parseOptions : {},
4997
+ repair: false,
4998
+ onError: void 0
4999
+ });
5000
+ } catch (e) {
5001
+ return null;
5002
+ }
5003
+ };
5004
+ const strictFull = tryParse(toolContent);
5005
+ if (strictFull !== null && isStableXmlProgressCandidate({
5006
+ candidate: toolContent,
5007
+ parsed: strictFull,
5008
+ toolSchema
5009
+ })) {
5010
+ return JSON.stringify(strictFull);
5011
+ }
5012
+ let searchEnd = toolContent.length;
5013
+ while (searchEnd > 0) {
5014
+ const gtIndex = toolContent.lastIndexOf(">", searchEnd - 1);
5015
+ if (gtIndex === -1) {
5016
+ break;
5017
+ }
5018
+ const candidate = toolContent.slice(0, gtIndex + 1);
5019
+ if (!analyzeXmlFragmentForProgress(candidate)) {
5020
+ searchEnd = gtIndex;
5021
+ continue;
5022
+ }
5023
+ const parsedCandidate = tryParse(candidate);
5024
+ if (parsedCandidate !== null && isStableXmlProgressCandidate({
5025
+ candidate,
5026
+ parsed: parsedCandidate,
5027
+ toolSchema
5028
+ })) {
5029
+ return JSON.stringify(parsedCandidate);
5030
+ }
5031
+ searchEnd = gtIndex;
5032
+ }
5033
+ return null;
5034
+ }
4254
5035
  function handleStreamingToolCallEnd(params) {
4255
5036
  var _a, _b;
4256
5037
  const {
@@ -4270,19 +5051,37 @@ function handleStreamingToolCallEnd(params) {
4270
5051
  flushText(ctrl);
4271
5052
  try {
4272
5053
  const parsedResult = parse3(toolContent, toolSchema, parseConfig);
5054
+ const finalInput = JSON.stringify(parsedResult);
5055
+ emitFinalRemainder({
5056
+ controller: ctrl,
5057
+ id: currentToolCall.toolCallId,
5058
+ state: currentToolCall,
5059
+ finalFullJson: finalInput,
5060
+ onMismatch: options == null ? void 0 : options.onError
5061
+ });
5062
+ ctrl.enqueue({
5063
+ type: "tool-input-end",
5064
+ id: currentToolCall.toolCallId
5065
+ });
4273
5066
  ctrl.enqueue({
4274
5067
  type: "tool-call",
4275
- toolCallId: generateId(),
5068
+ toolCallId: currentToolCall.toolCallId,
4276
5069
  toolName: currentToolCall.name,
4277
- input: JSON.stringify(parsedResult)
5070
+ input: finalInput
4278
5071
  });
4279
5072
  } catch (error) {
5073
+ ctrl.enqueue({
5074
+ type: "tool-input-end",
5075
+ id: currentToolCall.toolCallId
5076
+ });
4280
5077
  const original = `<${currentToolCall.name}>${toolContent}</${currentToolCall.name}>`;
4281
5078
  (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, "Could not process streaming XML tool call", {
4282
5079
  toolCall: original,
4283
5080
  error
4284
5081
  });
4285
- flushText(ctrl, original);
5082
+ if (shouldEmitRawToolCallTextOnError2(options)) {
5083
+ flushText(ctrl, original);
5084
+ }
4286
5085
  }
4287
5086
  }
4288
5087
  function findClosingTagEndFlexible(text, contentStart, toolName) {
@@ -4704,38 +5503,6 @@ function findPotentialToolTagStart(buffer, toolNames) {
4704
5503
  }
4705
5504
  return -1;
4706
5505
  }
4707
- function createFlushTextHandler(getCurrentTextId, setCurrentTextId, getHasEmittedTextStart, setHasEmittedTextStart) {
4708
- return (controller, text) => {
4709
- const content = text;
4710
- if (content) {
4711
- if (!getCurrentTextId()) {
4712
- const newId = generateId();
4713
- setCurrentTextId(newId);
4714
- controller.enqueue({
4715
- type: "text-start",
4716
- id: newId
4717
- });
4718
- setHasEmittedTextStart(true);
4719
- }
4720
- controller.enqueue({
4721
- type: "text-delta",
4722
- id: getCurrentTextId(),
4723
- delta: content
4724
- });
4725
- }
4726
- const currentTextId = getCurrentTextId();
4727
- if (currentTextId && !text) {
4728
- if (getHasEmittedTextStart()) {
4729
- controller.enqueue({
4730
- type: "text-end",
4731
- id: currentTextId
4732
- });
4733
- setHasEmittedTextStart(false);
4734
- }
4735
- setCurrentTextId(null);
4736
- }
4737
- };
4738
- }
4739
5506
  function processToolCallInBuffer(params) {
4740
5507
  const {
4741
5508
  buffer,
@@ -4745,18 +5512,21 @@ function processToolCallInBuffer(params) {
4745
5512
  controller,
4746
5513
  flushText,
4747
5514
  setBuffer,
4748
- parseOptions
5515
+ parseOptions,
5516
+ emitToolInputProgress: emitToolInputProgress2
4749
5517
  } = params;
4750
5518
  const endTagPattern = new RegExp(
4751
5519
  `</\\s*${escapeRegExp(currentToolCall.name)}\\s*>`
4752
5520
  );
4753
5521
  const endMatch = endTagPattern.exec(buffer);
4754
5522
  if (!endMatch || endMatch.index === void 0) {
5523
+ emitToolInputProgress2(controller, currentToolCall, buffer);
4755
5524
  return { buffer, currentToolCall, shouldBreak: true };
4756
5525
  }
4757
5526
  const endIdx = endMatch.index;
4758
5527
  const endPos = endIdx + endMatch[0].length;
4759
5528
  const content = buffer.substring(0, endIdx);
5529
+ emitToolInputProgress2(controller, currentToolCall, content);
4760
5530
  const remainder = buffer.substring(endPos);
4761
5531
  setBuffer(remainder);
4762
5532
  handleStreamingToolCallEnd({
@@ -4783,7 +5553,8 @@ function processNoToolCallInBuffer(params) {
4783
5553
  tools,
4784
5554
  options,
4785
5555
  parseOptions,
4786
- setBuffer
5556
+ setBuffer,
5557
+ emitToolInputStart
4787
5558
  } = params;
4788
5559
  const {
4789
5560
  index: earliestStartTagIndex,
@@ -4813,9 +5584,10 @@ function processNoToolCallInBuffer(params) {
4813
5584
  if (selfClosing) {
4814
5585
  const newBuffer2 = buffer.substring(earliestStartTagIndex + tagLength);
4815
5586
  setBuffer(newBuffer2);
5587
+ const currentToolCall = emitToolInputStart(controller, earliestToolName);
4816
5588
  handleStreamingToolCallEnd({
4817
5589
  toolContent: "",
4818
- currentToolCall: { name: earliestToolName, content: "" },
5590
+ currentToolCall,
4819
5591
  tools,
4820
5592
  options,
4821
5593
  ctrl: controller,
@@ -4834,12 +5606,12 @@ function processNoToolCallInBuffer(params) {
4834
5606
  setBuffer(newBuffer);
4835
5607
  return {
4836
5608
  buffer: newBuffer,
4837
- currentToolCall: { name: earliestToolName, content: "" },
5609
+ currentToolCall: emitToolInputStart(controller, earliestToolName),
4838
5610
  shouldBreak: false,
4839
5611
  shouldContinue: true
4840
5612
  };
4841
5613
  }
4842
- function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, setCurrentToolCall, tools, options, toolNames, flushText, parseOptions) {
5614
+ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, setCurrentToolCall, tools, options, toolNames, flushText, parseOptions, emitToolInputProgress2, emitToolInputStart) {
4843
5615
  return (controller) => {
4844
5616
  while (true) {
4845
5617
  const currentToolCall = getCurrentToolCall();
@@ -4852,7 +5624,8 @@ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, se
4852
5624
  controller,
4853
5625
  flushText,
4854
5626
  setBuffer,
4855
- parseOptions
5627
+ parseOptions,
5628
+ emitToolInputProgress: emitToolInputProgress2
4856
5629
  });
4857
5630
  setBuffer(result.buffer);
4858
5631
  setCurrentToolCall(result.currentToolCall);
@@ -4868,7 +5641,8 @@ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, se
4868
5641
  tools,
4869
5642
  options,
4870
5643
  parseOptions,
4871
- setBuffer
5644
+ setBuffer,
5645
+ emitToolInputStart
4872
5646
  });
4873
5647
  setBuffer(result.buffer);
4874
5648
  setCurrentToolCall(result.currentToolCall);
@@ -4982,6 +5756,118 @@ var xmlProtocol = (protocolOptions) => {
4982
5756
  hasEmittedTextStart = value;
4983
5757
  }
4984
5758
  );
5759
+ const emitToolInputStart = (controller, toolName) => {
5760
+ flushText(controller);
5761
+ const next = {
5762
+ name: toolName,
5763
+ toolCallId: generateToolCallId(),
5764
+ emittedInput: "",
5765
+ lastProgressGtIndex: null,
5766
+ lastProgressFullInput: null
5767
+ };
5768
+ controller.enqueue({
5769
+ type: "tool-input-start",
5770
+ id: next.toolCallId,
5771
+ toolName
5772
+ });
5773
+ return next;
5774
+ };
5775
+ const emitToolInputProgress2 = (controller, toolCall, toolContent) => {
5776
+ const progressGtIndex = toolContent.lastIndexOf(">");
5777
+ if (toolCall.lastProgressGtIndex === progressGtIndex) {
5778
+ const cached = toolCall.lastProgressFullInput;
5779
+ if (cached == null) {
5780
+ return;
5781
+ }
5782
+ if (cached === "{}" && toolContent.trim().length === 0) {
5783
+ return;
5784
+ }
5785
+ const prefixCandidate2 = toIncompleteJsonPrefix(cached);
5786
+ emitPrefixDelta({
5787
+ controller,
5788
+ id: toolCall.toolCallId,
5789
+ state: toolCall,
5790
+ candidate: prefixCandidate2
5791
+ });
5792
+ return;
5793
+ }
5794
+ const toolSchema = getToolSchema(tools, toolCall.name);
5795
+ const fullInput = parseXmlContentForStreamProgress({
5796
+ toolContent,
5797
+ toolSchema,
5798
+ parseOptions
5799
+ });
5800
+ toolCall.lastProgressGtIndex = progressGtIndex;
5801
+ toolCall.lastProgressFullInput = fullInput;
5802
+ if (fullInput == null) {
5803
+ return;
5804
+ }
5805
+ if (fullInput === "{}" && toolContent.trim().length === 0) {
5806
+ return;
5807
+ }
5808
+ const prefixCandidate = toIncompleteJsonPrefix(fullInput);
5809
+ emitPrefixDelta({
5810
+ controller,
5811
+ id: toolCall.toolCallId,
5812
+ state: toolCall,
5813
+ candidate: prefixCandidate
5814
+ });
5815
+ };
5816
+ const finalizeUnclosedToolCall = (controller) => {
5817
+ var _a2, _b;
5818
+ if (!currentToolCall) {
5819
+ return;
5820
+ }
5821
+ emitToolInputProgress2(controller, currentToolCall, buffer);
5822
+ const parseConfig = {
5823
+ ...parseOptions,
5824
+ onError: (_a2 = options == null ? void 0 : options.onError) != null ? _a2 : parseOptions == null ? void 0 : parseOptions.onError
5825
+ };
5826
+ const toolSchema = getToolSchema(tools, currentToolCall.name);
5827
+ flushText(controller);
5828
+ try {
5829
+ if (hasNonWhitespaceTopLevelText(buffer)) {
5830
+ throw new Error(
5831
+ "Cannot reconcile unclosed XML tool call with top-level plain text."
5832
+ );
5833
+ }
5834
+ const parsedResult = parse3(buffer, toolSchema, parseConfig);
5835
+ const finalInput = JSON.stringify(parsedResult);
5836
+ emitFinalRemainder({
5837
+ controller,
5838
+ id: currentToolCall.toolCallId,
5839
+ state: currentToolCall,
5840
+ finalFullJson: finalInput,
5841
+ onMismatch: options == null ? void 0 : options.onError
5842
+ });
5843
+ controller.enqueue({
5844
+ type: "tool-input-end",
5845
+ id: currentToolCall.toolCallId
5846
+ });
5847
+ controller.enqueue({
5848
+ type: "tool-call",
5849
+ toolCallId: currentToolCall.toolCallId,
5850
+ toolName: currentToolCall.name,
5851
+ input: finalInput
5852
+ });
5853
+ } catch (error) {
5854
+ controller.enqueue({
5855
+ type: "tool-input-end",
5856
+ id: currentToolCall.toolCallId
5857
+ });
5858
+ const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
5859
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
5860
+ options,
5861
+ "Could not complete streaming XML tool call at finish.",
5862
+ { toolCall: unfinishedContent, error }
5863
+ );
5864
+ if (shouldEmitRawToolCallTextOnError2(options)) {
5865
+ flushText(controller, unfinishedContent);
5866
+ }
5867
+ }
5868
+ buffer = "";
5869
+ currentToolCall = null;
5870
+ };
4985
5871
  const processBuffer = createProcessBufferHandler(
4986
5872
  () => buffer,
4987
5873
  (newBuffer) => {
@@ -4995,17 +5881,17 @@ var xmlProtocol = (protocolOptions) => {
4995
5881
  options,
4996
5882
  toolNames,
4997
5883
  flushText,
4998
- parseOptions
5884
+ parseOptions,
5885
+ emitToolInputProgress2,
5886
+ emitToolInputStart
4999
5887
  );
5000
5888
  return new TransformStream({
5889
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Stateful stream parsing requires branching over chunk lifecycle and parser states.
5001
5890
  transform(chunk, controller) {
5002
5891
  var _a2;
5003
5892
  if (chunk.type === "finish") {
5004
5893
  if (currentToolCall) {
5005
- const unfinishedContent = `<${currentToolCall.name}>${currentToolCall.content}${buffer}`;
5006
- flushText(controller, unfinishedContent);
5007
- buffer = "";
5008
- currentToolCall = null;
5894
+ finalizeUnclosedToolCall(controller);
5009
5895
  } else if (buffer) {
5010
5896
  flushText(controller, buffer);
5011
5897
  buffer = "";
@@ -5015,7 +5901,8 @@ var xmlProtocol = (protocolOptions) => {
5015
5901
  return;
5016
5902
  }
5017
5903
  if (chunk.type !== "text-delta") {
5018
- if (buffer) {
5904
+ if (currentToolCall) {
5905
+ } else if (buffer) {
5019
5906
  flushText(controller, buffer);
5020
5907
  buffer = "";
5021
5908
  }
@@ -5028,10 +5915,7 @@ var xmlProtocol = (protocolOptions) => {
5028
5915
  },
5029
5916
  flush(controller) {
5030
5917
  if (currentToolCall) {
5031
- const unfinishedContent = `<${currentToolCall.name}>${currentToolCall.content || ""}${buffer}`;
5032
- flushText(controller, unfinishedContent);
5033
- buffer = "";
5034
- currentToolCall = null;
5918
+ finalizeUnclosedToolCall(controller);
5035
5919
  } else if (buffer) {
5036
5920
  flushText(controller, buffer);
5037
5921
  buffer = "";
@@ -5059,7 +5943,205 @@ var xmlProtocol = (protocolOptions) => {
5059
5943
 
5060
5944
  // src/core/protocols/yaml-protocol.ts
5061
5945
  var import_yaml = __toESM(require("yaml"), 1);
5946
+ function shouldEmitRawToolCallTextOnError3(options) {
5947
+ return (options == null ? void 0 : options.emitRawToolCallTextOnError) === true;
5948
+ }
5949
+ var selfClosingTagCache2 = /* @__PURE__ */ new Map();
5950
+ function getSelfClosingTagPattern2(toolName) {
5951
+ let pattern = selfClosingTagCache2.get(toolName);
5952
+ if (!pattern) {
5953
+ pattern = new RegExp(`<\\s*${escapeRegExp(toolName)}\\s*/>`, "g");
5954
+ selfClosingTagCache2.set(toolName, pattern);
5955
+ }
5956
+ return pattern;
5957
+ }
5062
5958
  var LEADING_WHITESPACE_RE = /^(\s*)/;
5959
+ var INCOMPLETE_MAPPING_TAIL_RE = /^[^:[\]{}-][^:]*:\s*$/;
5960
+ var INCOMPLETE_SEQUENCE_TAIL_RE = /^-\s*$/;
5961
+ var BLOCK_SCALAR_KEY_RE = /:\s*[|>][-+0-9]*\s*$/;
5962
+ var PLAIN_MAPPING_VALUE_RE = /^[^:[\]{}-][^:]*:\s*(.+)$/;
5963
+ var PLAIN_SEQUENCE_VALUE_RE = /^-\s+(.+)$/;
5964
+ function normalizeYamlContent(yamlContent) {
5965
+ let normalized = yamlContent;
5966
+ if (normalized.startsWith("\n")) {
5967
+ normalized = normalized.slice(1);
5968
+ }
5969
+ const lines = normalized.split("\n");
5970
+ const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
5971
+ if (nonEmptyLines.length === 0) {
5972
+ return { normalized: "", nonEmptyLines };
5973
+ }
5974
+ const minIndent = Math.min(
5975
+ ...nonEmptyLines.map((line) => {
5976
+ const match = line.match(LEADING_WHITESPACE_RE);
5977
+ return match ? match[1].length : 0;
5978
+ })
5979
+ );
5980
+ if (minIndent > 0) {
5981
+ normalized = lines.map((line) => line.slice(minIndent)).join("\n");
5982
+ }
5983
+ return { normalized, nonEmptyLines };
5984
+ }
5985
+ function parseYamlDocumentAsMapping(normalized) {
5986
+ try {
5987
+ const doc = import_yaml.default.parseDocument(normalized);
5988
+ const errors = doc.errors.map((e) => e.message);
5989
+ const result = doc.toJSON();
5990
+ if (result === null) {
5991
+ return { value: {}, errors };
5992
+ }
5993
+ if (typeof result !== "object" || Array.isArray(result)) {
5994
+ return { value: null, errors };
5995
+ }
5996
+ return { value: result, errors };
5997
+ } catch (error) {
5998
+ return {
5999
+ value: null,
6000
+ errors: [
6001
+ error instanceof Error ? error.message : "Unknown YAML parsing error"
6002
+ ]
6003
+ };
6004
+ }
6005
+ }
6006
+ function getLastMeaningfulLineInfo(input) {
6007
+ var _a;
6008
+ const lines = input.split("\n");
6009
+ let index = lines.length - 1;
6010
+ while (index >= 0) {
6011
+ const raw = (_a = lines[index]) != null ? _a : "";
6012
+ const trimmed = raw.trim();
6013
+ if (trimmed.length > 0 && !trimmed.startsWith("#")) {
6014
+ return {
6015
+ index,
6016
+ raw,
6017
+ trimmed,
6018
+ indent: raw.length - raw.trimStart().length
6019
+ };
6020
+ }
6021
+ index -= 1;
6022
+ }
6023
+ return null;
6024
+ }
6025
+ function dropLastMeaningfulLine(input) {
6026
+ const lineInfo = getLastMeaningfulLineInfo(input);
6027
+ if (!lineInfo) {
6028
+ return null;
6029
+ }
6030
+ return input.split("\n").slice(0, lineInfo.index).join("\n").trimEnd();
6031
+ }
6032
+ function hasIncompleteMappingTail(normalized) {
6033
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
6034
+ if (!lineInfo) {
6035
+ return false;
6036
+ }
6037
+ return INCOMPLETE_MAPPING_TAIL_RE.test(lineInfo.trimmed);
6038
+ }
6039
+ function hasIncompleteSequenceTail(normalized) {
6040
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
6041
+ if (!lineInfo) {
6042
+ return false;
6043
+ }
6044
+ return INCOMPLETE_SEQUENCE_TAIL_RE.test(lineInfo.trimmed);
6045
+ }
6046
+ function hasSplitNestedKeyTail(normalized) {
6047
+ var _a;
6048
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
6049
+ if (!lineInfo) {
6050
+ return false;
6051
+ }
6052
+ const { trimmed, indent, index } = lineInfo;
6053
+ if (indent === 0) {
6054
+ return false;
6055
+ }
6056
+ if (trimmed.startsWith("#") || trimmed.startsWith("-") || trimmed.includes(":")) {
6057
+ return false;
6058
+ }
6059
+ const lines = normalized.split("\n");
6060
+ let parentIndex = index - 1;
6061
+ while (parentIndex >= 0) {
6062
+ const parentRaw = (_a = lines[parentIndex]) != null ? _a : "";
6063
+ const parentTrimmed = parentRaw.trim();
6064
+ if (parentTrimmed.length === 0 || parentTrimmed.startsWith("#")) {
6065
+ parentIndex -= 1;
6066
+ continue;
6067
+ }
6068
+ const parentIndent = parentRaw.length - parentRaw.trimStart().length;
6069
+ if (parentIndent >= indent) {
6070
+ parentIndex -= 1;
6071
+ continue;
6072
+ }
6073
+ if (!parentTrimmed.endsWith(":")) {
6074
+ return false;
6075
+ }
6076
+ if (BLOCK_SCALAR_KEY_RE.test(parentTrimmed)) {
6077
+ return false;
6078
+ }
6079
+ return true;
6080
+ }
6081
+ return false;
6082
+ }
6083
+ function extractTrailingPlainScalarValue(line) {
6084
+ var _a;
6085
+ if (BLOCK_SCALAR_KEY_RE.test(line)) {
6086
+ return null;
6087
+ }
6088
+ const mappingMatch = line.match(PLAIN_MAPPING_VALUE_RE);
6089
+ const sequenceMatch = line.match(PLAIN_SEQUENCE_VALUE_RE);
6090
+ const value = (_a = mappingMatch == null ? void 0 : mappingMatch[1]) != null ? _a : sequenceMatch == null ? void 0 : sequenceMatch[1];
6091
+ if (!value) {
6092
+ return null;
6093
+ }
6094
+ const trimmedValue = value.trim();
6095
+ if (trimmedValue.length === 0) {
6096
+ return null;
6097
+ }
6098
+ if (trimmedValue.startsWith('"') || trimmedValue.startsWith("'")) {
6099
+ return null;
6100
+ }
6101
+ if (trimmedValue.startsWith("{") || trimmedValue.startsWith("[") || trimmedValue.startsWith("|") || trimmedValue.startsWith(">")) {
6102
+ return null;
6103
+ }
6104
+ return trimmedValue;
6105
+ }
6106
+ function hasUnterminatedPlainScalarTail(normalized) {
6107
+ if (normalized.endsWith("\n")) {
6108
+ return false;
6109
+ }
6110
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
6111
+ if (!lineInfo) {
6112
+ return false;
6113
+ }
6114
+ return extractTrailingPlainScalarValue(lineInfo.trimmed) != null;
6115
+ }
6116
+ function hasUnstableProgressTail(normalized) {
6117
+ return hasIncompleteMappingTail(normalized) || hasIncompleteSequenceTail(normalized) || hasSplitNestedKeyTail(normalized) || hasUnterminatedPlainScalarTail(normalized);
6118
+ }
6119
+ function trimTrailingNewlineInUnknown(value) {
6120
+ if (typeof value === "string") {
6121
+ if (value.endsWith("\n")) {
6122
+ return value.slice(0, -1);
6123
+ }
6124
+ return value;
6125
+ }
6126
+ if (Array.isArray(value)) {
6127
+ return value.map((item) => trimTrailingNewlineInUnknown(item));
6128
+ }
6129
+ if (value && typeof value === "object") {
6130
+ return Object.fromEntries(
6131
+ Object.entries(value).map(([key, item]) => [
6132
+ key,
6133
+ trimTrailingNewlineInUnknown(item)
6134
+ ])
6135
+ );
6136
+ }
6137
+ return value;
6138
+ }
6139
+ function stabilizeParsedValueForStreamProgress(value, source) {
6140
+ if (source.endsWith("\n")) {
6141
+ return value;
6142
+ }
6143
+ return trimTrailingNewlineInUnknown(value);
6144
+ }
5063
6145
  function findClosingTagEnd(text, contentStart, toolName) {
5064
6146
  let pos = contentStart;
5065
6147
  let depth = 1;
@@ -5131,9 +6213,9 @@ function findEarliestTagPosition(openIdx, selfIdx) {
5131
6213
  function collectToolCallsForName(text, toolName) {
5132
6214
  const toolCalls = [];
5133
6215
  let searchIndex = 0;
5134
- const selfTagRegex = new RegExp(`<${toolName}\\s*/>`, "g");
6216
+ const startTag = `<${toolName}>`;
6217
+ const selfTagRegex = getSelfClosingTagPattern2(toolName);
5135
6218
  while (searchIndex < text.length) {
5136
- const startTag = `<${toolName}>`;
5137
6219
  const openIdx = text.indexOf(startTag, searchIndex);
5138
6220
  selfTagRegex.lastIndex = searchIndex;
5139
6221
  const selfMatch = selfTagRegex.exec(text);
@@ -5183,47 +6265,48 @@ function findToolCalls2(text, toolNames) {
5183
6265
  return toolCalls.sort((a, b) => a.startIndex - b.startIndex);
5184
6266
  }
5185
6267
  function parseYamlContent(yamlContent, options) {
5186
- var _a, _b, _c;
5187
- let normalized = yamlContent;
5188
- if (normalized.startsWith("\n")) {
5189
- normalized = normalized.slice(1);
5190
- }
5191
- const lines = normalized.split("\n");
5192
- const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
6268
+ var _a, _b;
6269
+ const { normalized, nonEmptyLines } = normalizeYamlContent(yamlContent);
5193
6270
  if (nonEmptyLines.length === 0) {
5194
6271
  return {};
5195
6272
  }
5196
- const minIndent = Math.min(
5197
- ...nonEmptyLines.map((line) => {
5198
- const match = line.match(LEADING_WHITESPACE_RE);
5199
- return match ? match[1].length : 0;
5200
- })
5201
- );
5202
- if (minIndent > 0) {
5203
- normalized = lines.map((line) => line.slice(minIndent)).join("\n");
6273
+ const parsed = parseYamlDocumentAsMapping(normalized);
6274
+ if (parsed.errors.length > 0) {
6275
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "YAML parse error", {
6276
+ errors: parsed.errors
6277
+ });
6278
+ return null;
5204
6279
  }
5205
- try {
5206
- const doc = import_yaml.default.parseDocument(normalized);
5207
- if (doc.errors && doc.errors.length > 0) {
5208
- (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "YAML parse error", {
5209
- errors: doc.errors.map((e) => e.message)
5210
- });
5211
- return null;
6280
+ if (parsed.value === null) {
6281
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, "YAML content must be a key-value mapping", {
6282
+ got: "non-mapping"
6283
+ });
6284
+ return null;
6285
+ }
6286
+ return parsed.value;
6287
+ }
6288
+ function parseYamlContentForStreamProgress(yamlContent) {
6289
+ const { normalized, nonEmptyLines } = normalizeYamlContent(yamlContent);
6290
+ if (nonEmptyLines.length === 0) {
6291
+ return {};
6292
+ }
6293
+ let candidate = normalized;
6294
+ while (true) {
6295
+ const parsed = parseYamlDocumentAsMapping(candidate);
6296
+ if (parsed.errors.length === 0 && !hasUnstableProgressTail(candidate)) {
6297
+ if (candidate.trim().length === 0 && normalized.trim().length > 0) {
6298
+ return null;
6299
+ }
6300
+ return stabilizeParsedValueForStreamProgress(parsed.value, candidate);
5212
6301
  }
5213
- const result = doc.toJSON();
5214
- if (result === null) {
5215
- return {};
6302
+ const truncated = dropLastMeaningfulLine(candidate);
6303
+ if (truncated == null) {
6304
+ return null;
5216
6305
  }
5217
- if (typeof result !== "object" || Array.isArray(result)) {
5218
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, "YAML content must be a key-value mapping", {
5219
- got: typeof result
5220
- });
6306
+ if (truncated === candidate) {
5221
6307
  return null;
5222
6308
  }
5223
- return result;
5224
- } catch (error) {
5225
- (_c = options == null ? void 0 : options.onError) == null ? void 0 : _c.call(options, "Failed to parse YAML content", { error });
5226
- return null;
6309
+ candidate = truncated;
5227
6310
  }
5228
6311
  }
5229
6312
  function processToolCallMatch(text, tc, currentIndex, processedElements, options) {
@@ -5239,7 +6322,7 @@ function processToolCallMatch(text, tc, currentIndex, processedElements, options
5239
6322
  if (parsedArgs !== null) {
5240
6323
  processedElements.push({
5241
6324
  type: "tool-call",
5242
- toolCallId: generateId(),
6325
+ toolCallId: generateToolCallId(),
5243
6326
  toolName: tc.toolName,
5244
6327
  input: JSON.stringify(parsedArgs)
5245
6328
  });
@@ -5252,38 +6335,6 @@ function processToolCallMatch(text, tc, currentIndex, processedElements, options
5252
6335
  }
5253
6336
  return tc.endIndex;
5254
6337
  }
5255
- function createFlushTextHandler2(getCurrentTextId, setCurrentTextId, getHasEmittedTextStart, setHasEmittedTextStart) {
5256
- return (controller, text) => {
5257
- const content = text;
5258
- if (content) {
5259
- if (!getCurrentTextId()) {
5260
- const newId = generateId();
5261
- setCurrentTextId(newId);
5262
- controller.enqueue({
5263
- type: "text-start",
5264
- id: newId
5265
- });
5266
- setHasEmittedTextStart(true);
5267
- }
5268
- controller.enqueue({
5269
- type: "text-delta",
5270
- id: getCurrentTextId(),
5271
- delta: content
5272
- });
5273
- }
5274
- const currentTextId = getCurrentTextId();
5275
- if (currentTextId && !text) {
5276
- if (getHasEmittedTextStart()) {
5277
- controller.enqueue({
5278
- type: "text-end",
5279
- id: currentTextId
5280
- });
5281
- setHasEmittedTextStart(false);
5282
- }
5283
- setCurrentTextId(null);
5284
- }
5285
- };
5286
- }
5287
6338
  function findEarliestToolTag2(buffer, toolNames) {
5288
6339
  let bestIndex = -1;
5289
6340
  let bestName = "";
@@ -5291,8 +6342,9 @@ function findEarliestToolTag2(buffer, toolNames) {
5291
6342
  let bestTagLength = 0;
5292
6343
  for (const name of toolNames) {
5293
6344
  const openTag = `<${name}>`;
5294
- const selfTagRegex = new RegExp(`<${name}\\s*/>`);
6345
+ const selfTagRegex = getSelfClosingTagPattern2(name);
5295
6346
  const idxOpen = buffer.indexOf(openTag);
6347
+ selfTagRegex.lastIndex = 0;
5296
6348
  const selfMatch = selfTagRegex.exec(buffer);
5297
6349
  const idxSelf = selfMatch ? selfMatch.index : -1;
5298
6350
  if (idxOpen !== -1 && (bestIndex === -1 || idxOpen < bestIndex)) {
@@ -5315,6 +6367,29 @@ function findEarliestToolTag2(buffer, toolNames) {
5315
6367
  tagLength: bestTagLength
5316
6368
  };
5317
6369
  }
6370
+ function stripTrailingPartialCloseTag(content, toolName) {
6371
+ const closeTag = `</${toolName}>`;
6372
+ const lastLineBreakIndex = Math.max(
6373
+ content.lastIndexOf("\n"),
6374
+ content.lastIndexOf("\r")
6375
+ );
6376
+ const lineStartIndex = lastLineBreakIndex === -1 ? 0 : lastLineBreakIndex + 1;
6377
+ const trailingLine = content.slice(lineStartIndex);
6378
+ const trimmedTrailingLine = trailingLine.trim();
6379
+ if (trimmedTrailingLine.length === 0 || !trimmedTrailingLine.startsWith("</") || trimmedTrailingLine === closeTag || !closeTag.startsWith(trimmedTrailingLine)) {
6380
+ return content;
6381
+ }
6382
+ const leadingWhitespaceLength = trailingLine.length - trailingLine.trimStart().length;
6383
+ const preservedLeadingWhitespace = trailingLine.slice(
6384
+ 0,
6385
+ leadingWhitespaceLength
6386
+ );
6387
+ const contentWithoutPartial = `${content.slice(
6388
+ 0,
6389
+ lineStartIndex
6390
+ )}${preservedLeadingWhitespace}`;
6391
+ return contentWithoutPartial.trimEnd();
6392
+ }
5318
6393
  var yamlProtocol = (_protocolOptions) => {
5319
6394
  return {
5320
6395
  formatTools({ tools, toolSystemPromptTemplate }) {
@@ -5375,7 +6450,7 @@ ${yamlContent}</${toolCall.toolName}>`;
5375
6450
  let currentToolCall = null;
5376
6451
  let currentTextId = null;
5377
6452
  let hasEmittedTextStart = false;
5378
- const flushText = createFlushTextHandler2(
6453
+ const flushText = createFlushTextHandler(
5379
6454
  () => currentTextId,
5380
6455
  (newId) => {
5381
6456
  currentTextId = newId;
@@ -5385,33 +6460,128 @@ ${yamlContent}</${toolCall.toolName}>`;
5385
6460
  hasEmittedTextStart = value;
5386
6461
  }
5387
6462
  );
5388
- const processToolCallEnd = (controller, toolContent, toolName) => {
6463
+ const emitToolInputProgress2 = (controller, toolContent) => {
6464
+ if (!currentToolCall) {
6465
+ return;
6466
+ }
6467
+ const parsedArgs = parseYamlContentForStreamProgress(toolContent);
6468
+ if (parsedArgs === null) {
6469
+ return;
6470
+ }
6471
+ const fullInput = JSON.stringify(parsedArgs);
6472
+ if (fullInput === "{}" && toolContent.trim().length === 0) {
6473
+ return;
6474
+ }
6475
+ const prefixCandidate = toIncompleteJsonPrefix(fullInput);
6476
+ emitPrefixDelta({
6477
+ controller,
6478
+ id: currentToolCall.toolCallId,
6479
+ state: currentToolCall,
6480
+ candidate: prefixCandidate
6481
+ });
6482
+ };
6483
+ const processToolCallEnd = (controller, toolContent, toolName, toolCallId) => {
5389
6484
  var _a;
5390
6485
  const parsedArgs = parseYamlContent(toolContent, options);
5391
6486
  flushText(controller);
5392
6487
  if (parsedArgs !== null) {
6488
+ const finalInput = JSON.stringify(parsedArgs);
6489
+ if (currentToolCall && currentToolCall.toolCallId === toolCallId) {
6490
+ emitFinalRemainder({
6491
+ controller,
6492
+ id: toolCallId,
6493
+ state: currentToolCall,
6494
+ finalFullJson: finalInput,
6495
+ onMismatch: options == null ? void 0 : options.onError
6496
+ });
6497
+ }
6498
+ controller.enqueue({
6499
+ type: "tool-input-end",
6500
+ id: toolCallId
6501
+ });
5393
6502
  controller.enqueue({
5394
6503
  type: "tool-call",
5395
- toolCallId: generateId(),
6504
+ toolCallId,
5396
6505
  toolName,
5397
- input: JSON.stringify(parsedArgs)
6506
+ input: finalInput
5398
6507
  });
5399
6508
  } else {
6509
+ controller.enqueue({
6510
+ type: "tool-input-end",
6511
+ id: toolCallId
6512
+ });
5400
6513
  const original = `<${toolName}>${toolContent}</${toolName}>`;
5401
6514
  (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "Could not parse streaming YAML tool call", {
5402
6515
  toolCall: original
5403
6516
  });
5404
- flushText(controller, original);
6517
+ if (shouldEmitRawToolCallTextOnError3(options)) {
6518
+ flushText(controller, original);
6519
+ }
5405
6520
  }
5406
6521
  };
6522
+ const finalizeUnclosedToolCall = (controller) => {
6523
+ var _a;
6524
+ if (!currentToolCall) {
6525
+ return;
6526
+ }
6527
+ emitToolInputProgress2(controller, buffer);
6528
+ const { name: toolName, toolCallId } = currentToolCall;
6529
+ const reconciledBuffer = stripTrailingPartialCloseTag(buffer, toolName);
6530
+ const parsedArgs = parseYamlContent(reconciledBuffer, options);
6531
+ flushText(controller);
6532
+ if (parsedArgs !== null) {
6533
+ const finalInput = JSON.stringify(parsedArgs);
6534
+ emitFinalRemainder({
6535
+ controller,
6536
+ id: toolCallId,
6537
+ state: currentToolCall,
6538
+ finalFullJson: finalInput,
6539
+ onMismatch: options == null ? void 0 : options.onError
6540
+ });
6541
+ controller.enqueue({
6542
+ type: "tool-input-end",
6543
+ id: toolCallId
6544
+ });
6545
+ controller.enqueue({
6546
+ type: "tool-call",
6547
+ toolCallId,
6548
+ toolName,
6549
+ input: finalInput
6550
+ });
6551
+ } else {
6552
+ controller.enqueue({
6553
+ type: "tool-input-end",
6554
+ id: toolCallId
6555
+ });
6556
+ const unfinishedContent = `<${toolName}>${buffer}`;
6557
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
6558
+ options,
6559
+ "Could not complete streaming YAML tool call at finish.",
6560
+ { toolCall: unfinishedContent }
6561
+ );
6562
+ if (shouldEmitRawToolCallTextOnError3(options)) {
6563
+ flushText(controller, unfinishedContent);
6564
+ }
6565
+ }
6566
+ buffer = "";
6567
+ currentToolCall = null;
6568
+ };
5407
6569
  const handlePendingToolCall = (controller, endTag, toolName) => {
6570
+ var _a;
5408
6571
  const endIdx = buffer.indexOf(endTag);
5409
6572
  if (endIdx === -1) {
6573
+ emitToolInputProgress2(controller, buffer);
5410
6574
  return false;
5411
6575
  }
5412
6576
  const content = buffer.substring(0, endIdx);
6577
+ emitToolInputProgress2(controller, content);
5413
6578
  buffer = buffer.substring(endIdx + endTag.length);
5414
- processToolCallEnd(controller, content, toolName);
6579
+ processToolCallEnd(
6580
+ controller,
6581
+ content,
6582
+ toolName,
6583
+ (_a = currentToolCall == null ? void 0 : currentToolCall.toolCallId) != null ? _a : generateToolCallId()
6584
+ );
5415
6585
  currentToolCall = null;
5416
6586
  return true;
5417
6587
  };
@@ -5428,13 +6598,35 @@ ${yamlContent}</${toolCall.toolName}>`;
5428
6598
  if (tagIndex > 0) {
5429
6599
  flushText(controller, buffer.substring(0, tagIndex));
5430
6600
  }
6601
+ flushText(controller);
5431
6602
  if (selfClosing) {
5432
6603
  buffer = buffer.substring(tagIndex + tagLength);
5433
- processToolCallEnd(controller, "", tagName);
6604
+ const toolCallId = generateToolCallId();
6605
+ currentToolCall = {
6606
+ name: tagName,
6607
+ toolCallId,
6608
+ emittedInput: ""
6609
+ };
6610
+ controller.enqueue({
6611
+ type: "tool-input-start",
6612
+ id: toolCallId,
6613
+ toolName: tagName
6614
+ });
6615
+ processToolCallEnd(controller, "", tagName, toolCallId);
6616
+ currentToolCall = null;
5434
6617
  } else {
5435
6618
  const startTag = `<${tagName}>`;
5436
6619
  buffer = buffer.substring(tagIndex + startTag.length);
5437
- currentToolCall = { name: tagName, content: "" };
6620
+ currentToolCall = {
6621
+ name: tagName,
6622
+ toolCallId: generateToolCallId(),
6623
+ emittedInput: ""
6624
+ };
6625
+ controller.enqueue({
6626
+ type: "tool-input-start",
6627
+ id: currentToolCall.toolCallId,
6628
+ toolName: tagName
6629
+ });
5438
6630
  }
5439
6631
  };
5440
6632
  const processBuffer = (controller) => {
@@ -5463,10 +6655,7 @@ ${yamlContent}</${toolCall.toolName}>`;
5463
6655
  var _a;
5464
6656
  if (chunk.type === "finish") {
5465
6657
  if (currentToolCall) {
5466
- const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
5467
- flushText(controller, unfinishedContent);
5468
- buffer = "";
5469
- currentToolCall = null;
6658
+ finalizeUnclosedToolCall(controller);
5470
6659
  } else if (buffer) {
5471
6660
  flushText(controller, buffer);
5472
6661
  buffer = "";
@@ -5476,7 +6665,7 @@ ${yamlContent}</${toolCall.toolName}>`;
5476
6665
  return;
5477
6666
  }
5478
6667
  if (chunk.type !== "text-delta") {
5479
- if (buffer) {
6668
+ if (!currentToolCall && buffer) {
5480
6669
  flushText(controller, buffer);
5481
6670
  buffer = "";
5482
6671
  }
@@ -5489,10 +6678,7 @@ ${yamlContent}</${toolCall.toolName}>`;
5489
6678
  },
5490
6679
  flush(controller) {
5491
6680
  if (currentToolCall) {
5492
- const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
5493
- flushText(controller, unfinishedContent);
5494
- buffer = "";
5495
- currentToolCall = null;
6681
+ finalizeUnclosedToolCall(controller);
5496
6682
  } else if (buffer) {
5497
6683
  flushText(controller, buffer);
5498
6684
  buffer = "";
@@ -5670,9 +6856,6 @@ function hasInputProperty(obj) {
5670
6856
  return typeof obj === "object" && obj !== null && "input" in obj;
5671
6857
  }
5672
6858
 
5673
- // src/generate-handler.ts
5674
- var import_provider_utils = require("@ai-sdk/provider-utils");
5675
-
5676
6859
  // src/core/utils/generated-text-json-recovery.ts
5677
6860
  function isRecord(value) {
5678
6861
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -5811,7 +6994,7 @@ function mergeJsonCandidatesByStart(tagged, codeBlocks, balanced) {
5811
6994
  function toToolCallPart(candidate) {
5812
6995
  return {
5813
6996
  type: "tool-call",
5814
- toolCallId: generateId(),
6997
+ toolCallId: generateToolCallId(),
5815
6998
  toolName: candidate.toolName,
5816
6999
  input: candidate.input
5817
7000
  };
@@ -6038,7 +7221,7 @@ async function handleToolChoice(doGenerate, params, tools) {
6038
7221
  }
6039
7222
  const toolCall = {
6040
7223
  type: "tool-call",
6041
- toolCallId: (0, import_provider_utils.generateId)(),
7224
+ toolCallId: generateToolCallId(),
6042
7225
  toolName,
6043
7226
  input
6044
7227
  };
@@ -6548,7 +7731,6 @@ unit: celsius
6548
7731
  }
6549
7732
 
6550
7733
  // src/stream-handler.ts
6551
- var import_provider_utils2 = require("@ai-sdk/provider-utils");
6552
7734
  async function wrapStream({
6553
7735
  protocol,
6554
7736
  doStream,
@@ -6586,10 +7768,22 @@ async function wrapStream({
6586
7768
  }
6587
7769
  )
6588
7770
  ).pipeThrough(protocol.createStreamParser({ tools, options }));
7771
+ let seenToolCall = false;
6589
7772
  const v3Stream = coreStream.pipeThrough(
6590
7773
  new TransformStream({
6591
7774
  transform(part, controller) {
6592
- const normalizedPart = part.type === "tool-call" ? coerceToolCallPart(part, tools) : part;
7775
+ let normalizedPart = part.type === "tool-call" ? coerceToolCallPart(part, tools) : part;
7776
+ if (normalizedPart.type === "tool-call") {
7777
+ seenToolCall = true;
7778
+ }
7779
+ if (normalizedPart.type === "finish" && seenToolCall && normalizedPart.finishReason.unified === "stop") {
7780
+ normalizedPart = {
7781
+ ...normalizedPart,
7782
+ finishReason: normalizeToolCallsFinishReason(
7783
+ normalizedPart.finishReason
7784
+ )
7785
+ };
7786
+ }
6593
7787
  if (debugLevel === "stream") {
6594
7788
  logParsedChunk(normalizedPart);
6595
7789
  }
@@ -6626,7 +7820,7 @@ async function toolChoiceStream({
6626
7820
  start(controller) {
6627
7821
  controller.enqueue({
6628
7822
  type: "tool-call",
6629
- toolCallId: (0, import_provider_utils2.generateId)(),
7823
+ toolCallId: generateToolCallId(),
6630
7824
  toolName,
6631
7825
  input
6632
7826
  });