@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.
package/dist/index.cjs CHANGED
@@ -905,7 +905,25 @@ function getPotentialStartIndex(text, searchedText) {
905
905
 
906
906
  // src/core/utils/id.ts
907
907
  function generateId() {
908
- return Math.random().toString(36).substring(2, 15);
908
+ return crypto.randomUUID().replace(/-/g, "").slice(0, 13);
909
+ }
910
+ var TOOL_CALL_ID_PREFIX = "call_";
911
+ var TOOL_CALL_ID_BODY_LENGTH = 24;
912
+ var TOOL_CALL_ID_ALPHANUM = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
913
+ function randomAlphaNumeric(length) {
914
+ var _a;
915
+ const bytes = new Uint8Array(length);
916
+ crypto.getRandomValues(bytes);
917
+ let out = "";
918
+ for (let i = 0; i < length; i += 1) {
919
+ const byte = bytes[i];
920
+ const index = (byte != null ? byte : 0) % TOOL_CALL_ID_ALPHANUM.length;
921
+ out += (_a = TOOL_CALL_ID_ALPHANUM[index]) != null ? _a : "0";
922
+ }
923
+ return out;
924
+ }
925
+ function generateToolCallId() {
926
+ return `${TOOL_CALL_ID_PREFIX}${randomAlphaNumeric(TOOL_CALL_ID_BODY_LENGTH)}`;
909
927
  }
910
928
 
911
929
  // src/core/utils/protocol-utils.ts
@@ -914,6 +932,38 @@ function addTextSegment(text, processedElements) {
914
932
  processedElements.push({ type: "text", text });
915
933
  }
916
934
  }
935
+ function createFlushTextHandler(getCurrentTextId, setCurrentTextId, getHasEmittedTextStart, setHasEmittedTextStart) {
936
+ return (controller, text) => {
937
+ const content = text;
938
+ if (content) {
939
+ if (!getCurrentTextId()) {
940
+ const newId = generateId();
941
+ setCurrentTextId(newId);
942
+ controller.enqueue({
943
+ type: "text-start",
944
+ id: newId
945
+ });
946
+ setHasEmittedTextStart(true);
947
+ }
948
+ controller.enqueue({
949
+ type: "text-delta",
950
+ id: getCurrentTextId(),
951
+ delta: content
952
+ });
953
+ }
954
+ const currentTextId = getCurrentTextId();
955
+ if (currentTextId && !text) {
956
+ if (getHasEmittedTextStart()) {
957
+ controller.enqueue({
958
+ type: "text-end",
959
+ id: currentTextId
960
+ });
961
+ setHasEmittedTextStart(false);
962
+ }
963
+ setCurrentTextId(null);
964
+ }
965
+ };
966
+ }
917
967
 
918
968
  // src/core/utils/regex.ts
919
969
  function escapeRegExp(literal) {
@@ -921,15 +971,21 @@ function escapeRegExp(literal) {
921
971
  }
922
972
 
923
973
  // src/core/protocols/json-protocol.ts
974
+ function shouldEmitRawToolCallTextOnError(options) {
975
+ return (options == null ? void 0 : options.emitRawToolCallTextOnError) === true;
976
+ }
977
+ function canonicalizeToolInput(argumentsValue) {
978
+ return JSON.stringify(argumentsValue != null ? argumentsValue : {});
979
+ }
924
980
  function processToolCallJson(toolCallJson, fullMatch, processedElements, options) {
925
- var _a, _b;
981
+ var _a;
926
982
  try {
927
983
  const parsedToolCall = parse(toolCallJson);
928
984
  processedElements.push({
929
985
  type: "tool-call",
930
- toolCallId: generateId(),
986
+ toolCallId: generateToolCallId(),
931
987
  toolName: parsedToolCall.name,
932
- input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
988
+ input: canonicalizeToolInput(parsedToolCall.arguments)
933
989
  });
934
990
  } catch (error) {
935
991
  logParseFailure({
@@ -938,7 +994,7 @@ function processToolCallJson(toolCallJson, fullMatch, processedElements, options
938
994
  snippet: fullMatch,
939
995
  error
940
996
  });
941
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
997
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
942
998
  options,
943
999
  "Could not process JSON tool call, keeping original text.",
944
1000
  { toolCall: fullMatch, error }
@@ -959,6 +1015,293 @@ function processMatchedToolCall(context) {
959
1015
  }
960
1016
  return startIndex + match[0].length;
961
1017
  }
1018
+ var WHITESPACE_JSON_REGEX = /\s/;
1019
+ function skipJsonWhitespace(text, fromIndex) {
1020
+ let index = fromIndex;
1021
+ while (index < text.length && WHITESPACE_JSON_REGEX.test(text[index])) {
1022
+ index += 1;
1023
+ }
1024
+ return index;
1025
+ }
1026
+ function findTopLevelPropertyValueStart(text, property) {
1027
+ const objectStart = skipJsonWhitespace(text, 0);
1028
+ if (objectStart >= text.length || text.charAt(objectStart) !== "{") {
1029
+ return null;
1030
+ }
1031
+ let depth = 0;
1032
+ let inString = false;
1033
+ let escaping = false;
1034
+ for (let index = objectStart; index < text.length; index += 1) {
1035
+ const char = text.charAt(index);
1036
+ if (inString) {
1037
+ if (escaping) {
1038
+ escaping = false;
1039
+ continue;
1040
+ }
1041
+ if (char === "\\") {
1042
+ escaping = true;
1043
+ continue;
1044
+ }
1045
+ if (char === '"') {
1046
+ inString = false;
1047
+ }
1048
+ continue;
1049
+ }
1050
+ if (char === "{") {
1051
+ depth += 1;
1052
+ continue;
1053
+ }
1054
+ if (char === "}") {
1055
+ depth = Math.max(0, depth - 1);
1056
+ continue;
1057
+ }
1058
+ if (char !== '"') {
1059
+ continue;
1060
+ }
1061
+ if (depth !== 1) {
1062
+ inString = true;
1063
+ continue;
1064
+ }
1065
+ const keyStart = index + 1;
1066
+ let keyEnd = keyStart;
1067
+ let keyEscaped = false;
1068
+ while (keyEnd < text.length) {
1069
+ const keyChar = text.charAt(keyEnd);
1070
+ if (keyEscaped) {
1071
+ keyEscaped = false;
1072
+ } else if (keyChar === "\\") {
1073
+ keyEscaped = true;
1074
+ } else if (keyChar === '"') {
1075
+ break;
1076
+ }
1077
+ keyEnd += 1;
1078
+ }
1079
+ if (keyEnd >= text.length || text.charAt(keyEnd) !== '"') {
1080
+ return null;
1081
+ }
1082
+ const key = text.slice(keyStart, keyEnd);
1083
+ let valueCursor = skipJsonWhitespace(text, keyEnd + 1);
1084
+ if (valueCursor >= text.length || text.charAt(valueCursor) !== ":") {
1085
+ index = keyEnd;
1086
+ continue;
1087
+ }
1088
+ valueCursor = skipJsonWhitespace(text, valueCursor + 1);
1089
+ if (key === property) {
1090
+ return valueCursor < text.length ? valueCursor : null;
1091
+ }
1092
+ index = valueCursor - 1;
1093
+ }
1094
+ return null;
1095
+ }
1096
+ function extractTopLevelStringProperty(text, property) {
1097
+ const valueStart = findTopLevelPropertyValueStart(text, property);
1098
+ if (valueStart == null || valueStart >= text.length) {
1099
+ return void 0;
1100
+ }
1101
+ if (text.charAt(valueStart) !== '"') {
1102
+ return void 0;
1103
+ }
1104
+ let valueEnd = valueStart + 1;
1105
+ let escaped = false;
1106
+ while (valueEnd < text.length) {
1107
+ const char = text.charAt(valueEnd);
1108
+ if (escaped) {
1109
+ escaped = false;
1110
+ } else if (char === "\\") {
1111
+ escaped = true;
1112
+ } else if (char === '"') {
1113
+ return text.slice(valueStart + 1, valueEnd);
1114
+ }
1115
+ valueEnd += 1;
1116
+ }
1117
+ return void 0;
1118
+ }
1119
+ function extractJsonValueSlice(text, valueStart) {
1120
+ if (valueStart >= text.length) {
1121
+ return null;
1122
+ }
1123
+ const first = text.charAt(valueStart);
1124
+ if (first === "{" || first === "[") {
1125
+ const stack = [first];
1126
+ let inString = false;
1127
+ let escaped = false;
1128
+ for (let index2 = valueStart + 1; index2 < text.length; index2 += 1) {
1129
+ const char = text.charAt(index2);
1130
+ if (inString) {
1131
+ if (escaped) {
1132
+ escaped = false;
1133
+ } else if (char === "\\") {
1134
+ escaped = true;
1135
+ } else if (char === '"') {
1136
+ inString = false;
1137
+ }
1138
+ continue;
1139
+ }
1140
+ if (char === '"') {
1141
+ inString = true;
1142
+ continue;
1143
+ }
1144
+ if (char === "{" || char === "[") {
1145
+ stack.push(char);
1146
+ continue;
1147
+ }
1148
+ if (char === "}" || char === "]") {
1149
+ const open = stack.at(-1);
1150
+ if (open === "{" && char === "}" || open === "[" && char === "]") {
1151
+ stack.pop();
1152
+ if (stack.length === 0) {
1153
+ return {
1154
+ text: text.slice(valueStart, index2 + 1),
1155
+ complete: true
1156
+ };
1157
+ }
1158
+ }
1159
+ }
1160
+ }
1161
+ return {
1162
+ text: text.slice(valueStart),
1163
+ complete: false
1164
+ };
1165
+ }
1166
+ if (first === '"') {
1167
+ let escaped = false;
1168
+ for (let index2 = valueStart + 1; index2 < text.length; index2 += 1) {
1169
+ const char = text.charAt(index2);
1170
+ if (escaped) {
1171
+ escaped = false;
1172
+ } else if (char === "\\") {
1173
+ escaped = true;
1174
+ } else if (char === '"') {
1175
+ return {
1176
+ text: text.slice(valueStart, index2 + 1),
1177
+ complete: true
1178
+ };
1179
+ }
1180
+ }
1181
+ return {
1182
+ text: text.slice(valueStart),
1183
+ complete: false
1184
+ };
1185
+ }
1186
+ let index = valueStart;
1187
+ while (index < text.length) {
1188
+ const char = text.charAt(index);
1189
+ if (char === "," || char === "}" || WHITESPACE_JSON_REGEX.test(char)) {
1190
+ break;
1191
+ }
1192
+ index += 1;
1193
+ }
1194
+ return {
1195
+ text: text.slice(valueStart, index),
1196
+ complete: index < text.length
1197
+ };
1198
+ }
1199
+ function extractStreamingToolCallProgress(toolCallJson) {
1200
+ var _a;
1201
+ const toolName = extractTopLevelStringProperty(toolCallJson, "name");
1202
+ const argsValueStart = findTopLevelPropertyValueStart(
1203
+ toolCallJson,
1204
+ "arguments"
1205
+ );
1206
+ if (argsValueStart == null) {
1207
+ return {
1208
+ toolName,
1209
+ argumentsText: void 0,
1210
+ argumentsComplete: false
1211
+ };
1212
+ }
1213
+ const argsSlice = extractJsonValueSlice(toolCallJson, argsValueStart);
1214
+ return {
1215
+ toolName,
1216
+ argumentsText: argsSlice == null ? void 0 : argsSlice.text,
1217
+ argumentsComplete: (_a = argsSlice == null ? void 0 : argsSlice.complete) != null ? _a : false
1218
+ };
1219
+ }
1220
+ function ensureToolInputStart(state, controller, toolName) {
1221
+ if (!state.activeToolInput) {
1222
+ const id = generateToolCallId();
1223
+ state.activeToolInput = {
1224
+ id,
1225
+ toolName,
1226
+ emittedInput: ""
1227
+ };
1228
+ controller.enqueue({
1229
+ type: "tool-input-start",
1230
+ id,
1231
+ toolName
1232
+ });
1233
+ }
1234
+ }
1235
+ function emitToolInputDelta(state, controller, fullInput) {
1236
+ const active = state.activeToolInput;
1237
+ if (!active) {
1238
+ return;
1239
+ }
1240
+ if (!fullInput.startsWith(active.emittedInput)) {
1241
+ return;
1242
+ }
1243
+ const delta = fullInput.slice(active.emittedInput.length);
1244
+ if (delta.length === 0) {
1245
+ return;
1246
+ }
1247
+ controller.enqueue({
1248
+ type: "tool-input-delta",
1249
+ id: active.id,
1250
+ delta
1251
+ });
1252
+ active.emittedInput = fullInput;
1253
+ }
1254
+ function closeToolInput(state, controller) {
1255
+ if (!state.activeToolInput) {
1256
+ return;
1257
+ }
1258
+ controller.enqueue({
1259
+ type: "tool-input-end",
1260
+ id: state.activeToolInput.id
1261
+ });
1262
+ state.activeToolInput = null;
1263
+ }
1264
+ function emitToolCallFromParsed(state, controller, parsedToolCall) {
1265
+ var _a, _b, _c, _d;
1266
+ closeTextBlock(state, controller);
1267
+ const toolName = typeof parsedToolCall.name === "string" ? parsedToolCall.name : (_b = (_a = state.activeToolInput) == null ? void 0 : _a.toolName) != null ? _b : "unknown";
1268
+ const input = canonicalizeToolInput(parsedToolCall.arguments);
1269
+ ensureToolInputStart(state, controller, toolName);
1270
+ emitToolInputDelta(state, controller, input);
1271
+ const toolCallId = (_d = (_c = state.activeToolInput) == null ? void 0 : _c.id) != null ? _d : generateToolCallId();
1272
+ closeToolInput(state, controller);
1273
+ controller.enqueue({
1274
+ type: "tool-call",
1275
+ toolCallId,
1276
+ toolName,
1277
+ input
1278
+ });
1279
+ }
1280
+ function canonicalizeArgumentsProgressInput(progress) {
1281
+ if (progress.argumentsText === void 0 || !progress.argumentsComplete) {
1282
+ return void 0;
1283
+ }
1284
+ try {
1285
+ const parsedArguments = parse(progress.argumentsText);
1286
+ return canonicalizeToolInput(parsedArguments);
1287
+ } catch (e) {
1288
+ return void 0;
1289
+ }
1290
+ }
1291
+ function emitToolInputProgress(state, controller) {
1292
+ if (!(state.isInsideToolCall && state.currentToolCallJson)) {
1293
+ return;
1294
+ }
1295
+ const progress = extractStreamingToolCallProgress(state.currentToolCallJson);
1296
+ if (!progress.toolName) {
1297
+ return;
1298
+ }
1299
+ ensureToolInputStart(state, controller, progress.toolName);
1300
+ const canonicalProgressInput = canonicalizeArgumentsProgressInput(progress);
1301
+ if (canonicalProgressInput !== void 0) {
1302
+ emitToolInputDelta(state, controller, canonicalProgressInput);
1303
+ }
1304
+ }
962
1305
  function flushBuffer(state, controller, toolCallStart) {
963
1306
  if (state.buffer.length === 0) {
964
1307
  return;
@@ -989,44 +1332,77 @@ function closeTextBlock(state, controller) {
989
1332
  state.hasEmittedTextStart = false;
990
1333
  }
991
1334
  }
992
- function emitIncompleteToolCall(state, controller, toolCallStart) {
993
- if (!state.currentToolCallJson) {
1335
+ function emitIncompleteToolCall(state, controller, toolCallStart, trailingBuffer, options) {
1336
+ var _a;
1337
+ if (!state.currentToolCallJson && trailingBuffer.length === 0) {
1338
+ state.isInsideToolCall = false;
994
1339
  return;
995
1340
  }
1341
+ if (state.currentToolCallJson) {
1342
+ try {
1343
+ const parsedToolCall = parse(state.currentToolCallJson);
1344
+ emitToolCallFromParsed(state, controller, parsedToolCall);
1345
+ state.currentToolCallJson = "";
1346
+ state.isInsideToolCall = false;
1347
+ return;
1348
+ } catch (e) {
1349
+ }
1350
+ }
1351
+ const rawToolCallContent = `${state.currentToolCallJson}${trailingBuffer}`;
1352
+ const errorContent = `${toolCallStart}${rawToolCallContent}`;
1353
+ const shouldEmitRawFallback = shouldEmitRawToolCallTextOnError(options);
996
1354
  logParseFailure({
997
1355
  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
1356
+ reason: shouldEmitRawFallback ? "Incomplete streaming tool call segment emitted as text" : "Incomplete streaming tool call segment suppressed without raw text fallback",
1357
+ snippet: errorContent
1015
1358
  });
1359
+ if (shouldEmitRawFallback) {
1360
+ const errorId = generateId();
1361
+ controller.enqueue({
1362
+ type: "text-start",
1363
+ id: errorId
1364
+ });
1365
+ controller.enqueue({
1366
+ type: "text-delta",
1367
+ id: errorId,
1368
+ delta: errorContent
1369
+ });
1370
+ controller.enqueue({
1371
+ type: "text-end",
1372
+ id: errorId
1373
+ });
1374
+ }
1375
+ closeToolInput(state, controller);
1376
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
1377
+ options,
1378
+ shouldEmitRawFallback ? "Could not complete streaming JSON tool call at finish; emitting original text." : "Could not complete streaming JSON tool call at finish.",
1379
+ { toolCall: errorContent }
1380
+ );
1016
1381
  state.currentToolCallJson = "";
1382
+ state.isInsideToolCall = false;
1017
1383
  }
1018
- function handleFinishChunk(state, controller, toolCallStart, chunk) {
1019
- if (state.buffer.length > 0) {
1384
+ function handleFinishChunk(state, controller, toolCallStart, options, chunk) {
1385
+ if (state.isInsideToolCall) {
1386
+ const trailingBuffer = state.buffer;
1387
+ state.buffer = "";
1388
+ emitIncompleteToolCall(
1389
+ state,
1390
+ controller,
1391
+ toolCallStart,
1392
+ trailingBuffer,
1393
+ options
1394
+ );
1395
+ } else if (state.buffer.length > 0) {
1020
1396
  flushBuffer(state, controller, toolCallStart);
1021
1397
  }
1022
1398
  closeTextBlock(state, controller);
1023
- emitIncompleteToolCall(state, controller, toolCallStart);
1024
1399
  controller.enqueue(chunk);
1025
1400
  }
1026
1401
  function publishText(text, state, controller) {
1027
1402
  if (state.isInsideToolCall) {
1028
1403
  closeTextBlock(state, controller);
1029
1404
  state.currentToolCallJson += text;
1405
+ emitToolInputProgress(state, controller);
1030
1406
  } else if (text.length > 0) {
1031
1407
  if (!state.currentTextId) {
1032
1408
  state.currentTextId = generateId();
@@ -1044,42 +1420,40 @@ function publishText(text, state, controller) {
1044
1420
  }
1045
1421
  }
1046
1422
  function emitToolCall(context) {
1047
- var _a, _b;
1423
+ var _a;
1048
1424
  const { state, controller, toolCallStart, toolCallEnd, options } = context;
1049
1425
  try {
1050
1426
  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
- });
1427
+ emitToolCallFromParsed(state, controller, parsedToolCall);
1058
1428
  } catch (error) {
1429
+ const errorContent = `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`;
1430
+ const shouldEmitRawFallback = shouldEmitRawToolCallTextOnError(options);
1059
1431
  logParseFailure({
1060
1432
  phase: "stream",
1061
1433
  reason: "Failed to parse streaming tool call JSON segment",
1062
- snippet: `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`,
1434
+ snippet: errorContent,
1063
1435
  error
1064
1436
  });
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(
1437
+ if (shouldEmitRawFallback) {
1438
+ const errorId = generateId();
1439
+ controller.enqueue({
1440
+ type: "text-start",
1441
+ id: errorId
1442
+ });
1443
+ controller.enqueue({
1444
+ type: "text-delta",
1445
+ id: errorId,
1446
+ delta: errorContent
1447
+ });
1448
+ controller.enqueue({
1449
+ type: "text-end",
1450
+ id: errorId
1451
+ });
1452
+ }
1453
+ closeToolInput(state, controller);
1454
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
1081
1455
  options,
1082
- "Could not process streaming JSON tool call; emitting original text.",
1456
+ shouldEmitRawFallback ? "Could not process streaming JSON tool call; emitting original text." : "Could not process streaming JSON tool call.",
1083
1457
  {
1084
1458
  toolCall: errorContent
1085
1459
  }
@@ -1095,6 +1469,7 @@ function processTagMatch(context) {
1095
1469
  } else {
1096
1470
  state.currentToolCallJson = "";
1097
1471
  state.isInsideToolCall = true;
1472
+ state.activeToolInput = null;
1098
1473
  }
1099
1474
  }
1100
1475
  function processBufferTags(context) {
@@ -1117,8 +1492,16 @@ function processBufferTags(context) {
1117
1492
  );
1118
1493
  }
1119
1494
  }
1120
- function handlePartialTag(state, controller, toolCallStart) {
1495
+ function handlePartialTag(state, controller, toolCallStart, toolCallEnd) {
1121
1496
  if (state.isInsideToolCall) {
1497
+ const potentialEndIndex = getPotentialStartIndex(state.buffer, toolCallEnd);
1498
+ if (potentialEndIndex != null && potentialEndIndex + toolCallEnd.length > state.buffer.length) {
1499
+ publishText(state.buffer.slice(0, potentialEndIndex), state, controller);
1500
+ state.buffer = state.buffer.slice(potentialEndIndex);
1501
+ } else {
1502
+ publishText(state.buffer, state, controller);
1503
+ state.buffer = "";
1504
+ }
1122
1505
  return;
1123
1506
  }
1124
1507
  const potentialIndex = getPotentialStartIndex(state.buffer, toolCallStart);
@@ -1191,13 +1574,14 @@ var jsonProtocol = ({
1191
1574
  buffer: "",
1192
1575
  currentToolCallJson: "",
1193
1576
  currentTextId: null,
1194
- hasEmittedTextStart: false
1577
+ hasEmittedTextStart: false,
1578
+ activeToolInput: null
1195
1579
  };
1196
1580
  return new TransformStream({
1197
1581
  transform(chunk, controller) {
1198
1582
  var _a;
1199
1583
  if (chunk.type === "finish") {
1200
- handleFinishChunk(state, controller, toolCallStart, chunk);
1584
+ handleFinishChunk(state, controller, toolCallStart, options, chunk);
1201
1585
  return;
1202
1586
  }
1203
1587
  if (chunk.type !== "text-delta") {
@@ -1213,7 +1597,7 @@ var jsonProtocol = ({
1213
1597
  toolCallEnd,
1214
1598
  options
1215
1599
  });
1216
- handlePartialTag(state, controller, toolCallStart);
1600
+ handlePartialTag(state, controller, toolCallStart, toolCallEnd);
1217
1601
  }
1218
1602
  });
1219
1603
  },
@@ -4193,35 +4577,112 @@ function parse3(xml, schema, options = {}) {
4193
4577
  var NAME_CHAR_RE2 = /[A-Za-z0-9_:-]/;
4194
4578
  var WHITESPACE_REGEX5 = /\s/;
4195
4579
 
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;
4580
+ // src/core/utils/streamed-tool-input-delta.ts
4581
+ function emitDelta({
4582
+ controller,
4583
+ id,
4584
+ state,
4585
+ nextInput
4586
+ }) {
4587
+ if (!nextInput.startsWith(state.emittedInput)) {
4588
+ return false;
4202
4589
  }
4203
- const match = trimmed.match(XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX);
4204
- if (!match) {
4205
- return null;
4590
+ const delta = nextInput.slice(state.emittedInput.length);
4591
+ if (delta.length === 0) {
4592
+ return false;
4206
4593
  }
4207
- const rootTag = match[1];
4208
- if (!toolNames.includes(rootTag)) {
4209
- return null;
4594
+ controller.enqueue({
4595
+ type: "tool-input-delta",
4596
+ id,
4597
+ delta
4598
+ });
4599
+ state.emittedInput = nextInput;
4600
+ return true;
4601
+ }
4602
+ function toIncompleteJsonPrefix(fullJson) {
4603
+ const trimmed = fullJson.trim();
4604
+ let prefix = trimmed;
4605
+ while (prefix.endsWith("}") || prefix.endsWith("]")) {
4606
+ prefix = prefix.slice(0, -1);
4210
4607
  }
4211
- const body = match[2].trimEnd();
4212
- if (body.trim().length === 0 || body.includes(`</${rootTag}>`)) {
4213
- return null;
4608
+ prefix = prefix.trimEnd();
4609
+ if (prefix.endsWith('"')) {
4610
+ prefix = prefix.slice(0, -1);
4214
4611
  }
4215
- return `<${rootTag}>
4216
- ${body}
4217
- </${rootTag}>`;
4218
- }
4219
-
4612
+ if (prefix.length === 0) {
4613
+ if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
4614
+ return trimmed.startsWith("{") ? "{" : "[";
4615
+ }
4616
+ if (trimmed.startsWith("]")) {
4617
+ return "[";
4618
+ }
4619
+ if (trimmed.startsWith("}")) {
4620
+ return "{";
4621
+ }
4622
+ if (trimmed.startsWith('"')) {
4623
+ return '"';
4624
+ }
4625
+ return "{";
4626
+ }
4627
+ return prefix;
4628
+ }
4629
+ function emitPrefixDelta(params) {
4630
+ return emitDelta({
4631
+ ...params,
4632
+ nextInput: params.candidate
4633
+ });
4634
+ }
4635
+ function emitFinalRemainder(params) {
4636
+ var _a;
4637
+ const result = emitDelta({
4638
+ ...params,
4639
+ nextInput: params.finalFullJson
4640
+ });
4641
+ if (!result && params.state.emittedInput.length > 0) {
4642
+ (_a = params.onMismatch) == null ? void 0 : _a.call(
4643
+ params,
4644
+ "Final JSON does not extend emitted tool-input prefix",
4645
+ {
4646
+ emittedLength: params.state.emittedInput.length,
4647
+ finalLength: params.finalFullJson.length
4648
+ }
4649
+ );
4650
+ }
4651
+ return result;
4652
+ }
4653
+
4654
+ // src/core/utils/xml-root-repair.ts
4655
+ var XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX = /^<([A-Za-z_][A-Za-z0-9_-]*)\s*\r?\n([\s\S]+?)\r?\n\s*\/>\s*$/;
4656
+ function tryRepairXmlSelfClosingRootWithBody(rawText, toolNames) {
4657
+ const trimmed = rawText.trim();
4658
+ if (trimmed.length === 0) {
4659
+ return null;
4660
+ }
4661
+ const match = trimmed.match(XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX);
4662
+ if (!match) {
4663
+ return null;
4664
+ }
4665
+ const rootTag = match[1];
4666
+ if (!toolNames.includes(rootTag)) {
4667
+ return null;
4668
+ }
4669
+ const body = match[2].trimEnd();
4670
+ if (body.trim().length === 0 || body.includes(`</${rootTag}>`)) {
4671
+ return null;
4672
+ }
4673
+ return `<${rootTag}>
4674
+ ${body}
4675
+ </${rootTag}>`;
4676
+ }
4677
+
4220
4678
  // src/core/protocols/xml-protocol.ts
4221
4679
  function getToolSchema(tools, toolName) {
4222
4680
  var _a;
4223
4681
  return (_a = tools.find((t) => t.name === toolName)) == null ? void 0 : _a.inputSchema;
4224
4682
  }
4683
+ function shouldEmitRawToolCallTextOnError2(options) {
4684
+ return (options == null ? void 0 : options.emitRawToolCallTextOnError) === true;
4685
+ }
4225
4686
  function processToolCall(params) {
4226
4687
  var _a, _b;
4227
4688
  const { toolCall, tools, options, text, processedElements, parseOptions } = params;
@@ -4234,7 +4695,7 @@ function processToolCall(params) {
4234
4695
  const parsed = parse3(toolCall.content, toolSchema, parseConfig);
4235
4696
  processedElements.push({
4236
4697
  type: "tool-call",
4237
- toolCallId: generateId(),
4698
+ toolCallId: generateToolCallId(),
4238
4699
  toolName: toolCall.toolName,
4239
4700
  input: JSON.stringify(parsed)
4240
4701
  });
@@ -4251,6 +4712,299 @@ function processToolCall(params) {
4251
4712
  processedElements.push({ type: "text", text: originalCallText });
4252
4713
  }
4253
4714
  }
4715
+ function parseXmlTagName(rawTagBody) {
4716
+ let index = 0;
4717
+ while (index < rawTagBody.length && WHITESPACE_REGEX5.test(rawTagBody[index])) {
4718
+ index += 1;
4719
+ }
4720
+ const nameStart = index;
4721
+ while (index < rawTagBody.length && NAME_CHAR_RE2.test(rawTagBody.charAt(index))) {
4722
+ index += 1;
4723
+ }
4724
+ return rawTagBody.slice(nameStart, index);
4725
+ }
4726
+ function consumeXmlSpecialSection(fragment, ltIndex) {
4727
+ if (fragment.startsWith("<!--", ltIndex)) {
4728
+ const commentEnd = fragment.indexOf("-->", ltIndex + 4);
4729
+ return commentEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: commentEnd + 3 };
4730
+ }
4731
+ if (fragment.startsWith("<![CDATA[", ltIndex)) {
4732
+ const cdataEnd = fragment.indexOf("]]>", ltIndex + 9);
4733
+ return cdataEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: cdataEnd + 3 };
4734
+ }
4735
+ if (fragment.startsWith("<?", ltIndex)) {
4736
+ const processingEnd = fragment.indexOf("?>", ltIndex + 2);
4737
+ return processingEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: processingEnd + 2 };
4738
+ }
4739
+ if (fragment.startsWith("<!", ltIndex)) {
4740
+ const declarationEnd = fragment.indexOf(">", ltIndex + 2);
4741
+ return declarationEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: declarationEnd + 1 };
4742
+ }
4743
+ return { kind: "none" };
4744
+ }
4745
+ function parseXmlTagToken(fragment, ltIndex) {
4746
+ const gtIndex = fragment.indexOf(">", ltIndex + 1);
4747
+ if (gtIndex === -1) {
4748
+ return null;
4749
+ }
4750
+ const tagBody = fragment.slice(ltIndex + 1, gtIndex).trim();
4751
+ if (tagBody.length === 0) {
4752
+ return null;
4753
+ }
4754
+ if (tagBody.startsWith("/")) {
4755
+ const closeName = parseXmlTagName(tagBody.slice(1));
4756
+ if (closeName.length === 0) {
4757
+ return null;
4758
+ }
4759
+ return { kind: "close", name: closeName, nextPos: gtIndex + 1 };
4760
+ }
4761
+ const selfClosing = tagBody.endsWith("/");
4762
+ const openBody = selfClosing ? tagBody.slice(0, -1).trimEnd() : tagBody;
4763
+ const openName = parseXmlTagName(openBody);
4764
+ if (openName.length === 0) {
4765
+ return null;
4766
+ }
4767
+ return {
4768
+ kind: "open",
4769
+ name: openName,
4770
+ selfClosing,
4771
+ nextPos: gtIndex + 1
4772
+ };
4773
+ }
4774
+ function analyzeXmlFragmentForProgress(fragment) {
4775
+ const stack = [];
4776
+ const topLevelTagNames = [];
4777
+ let position = 0;
4778
+ while (position < fragment.length) {
4779
+ const ltIndex = fragment.indexOf("<", position);
4780
+ if (ltIndex === -1) {
4781
+ break;
4782
+ }
4783
+ const special = consumeXmlSpecialSection(fragment, ltIndex);
4784
+ if (special.kind === "incomplete") {
4785
+ return null;
4786
+ }
4787
+ if (special.kind === "consumed") {
4788
+ position = special.nextPos;
4789
+ continue;
4790
+ }
4791
+ const token = parseXmlTagToken(fragment, ltIndex);
4792
+ if (token === null) {
4793
+ return null;
4794
+ }
4795
+ if (token.kind === "close") {
4796
+ const openName = stack.pop();
4797
+ if (!openName || openName !== token.name) {
4798
+ return null;
4799
+ }
4800
+ position = token.nextPos;
4801
+ continue;
4802
+ }
4803
+ if (stack.length === 0) {
4804
+ topLevelTagNames.push(token.name);
4805
+ }
4806
+ if (!token.selfClosing) {
4807
+ stack.push(token.name);
4808
+ }
4809
+ position = token.nextPos;
4810
+ }
4811
+ if (stack.length > 0) {
4812
+ return null;
4813
+ }
4814
+ return { topLevelTagNames };
4815
+ }
4816
+ function scanXmlFragmentTopLevelTextStep(options) {
4817
+ const { fragment, position, stack } = options;
4818
+ const ltIndex = fragment.indexOf("<", position);
4819
+ if (ltIndex === -1) {
4820
+ const trailingText = fragment.slice(position);
4821
+ return {
4822
+ kind: "done",
4823
+ value: stack.length === 0 && trailingText.trim().length > 0
4824
+ };
4825
+ }
4826
+ const textBetweenTags = fragment.slice(position, ltIndex);
4827
+ if (stack.length === 0 && textBetweenTags.trim().length > 0) {
4828
+ return { kind: "found" };
4829
+ }
4830
+ const special = consumeXmlSpecialSection(fragment, ltIndex);
4831
+ if (special.kind === "incomplete") {
4832
+ return { kind: "invalid" };
4833
+ }
4834
+ if (special.kind === "consumed") {
4835
+ return { kind: "next", nextPos: special.nextPos };
4836
+ }
4837
+ const token = parseXmlTagToken(fragment, ltIndex);
4838
+ if (token === null) {
4839
+ return { kind: "invalid" };
4840
+ }
4841
+ if (token.kind === "close") {
4842
+ const openName = stack.pop();
4843
+ if (!openName || openName !== token.name) {
4844
+ return { kind: "invalid" };
4845
+ }
4846
+ } else if (!token.selfClosing) {
4847
+ stack.push(token.name);
4848
+ }
4849
+ return { kind: "next", nextPos: token.nextPos };
4850
+ }
4851
+ function hasNonWhitespaceTopLevelText(fragment) {
4852
+ if (!fragment.includes("<")) {
4853
+ return fragment.trim().length > 0;
4854
+ }
4855
+ const stack = [];
4856
+ let position = 0;
4857
+ while (position < fragment.length) {
4858
+ const step = scanXmlFragmentTopLevelTextStep({ fragment, position, stack });
4859
+ if (step.kind === "found") {
4860
+ return true;
4861
+ }
4862
+ if (step.kind === "invalid") {
4863
+ return false;
4864
+ }
4865
+ if (step.kind === "done") {
4866
+ return step.value;
4867
+ }
4868
+ position = step.nextPos;
4869
+ }
4870
+ return false;
4871
+ }
4872
+ function getObjectSchemaPropertyNames(schema) {
4873
+ if (!schema || typeof schema !== "object") {
4874
+ return null;
4875
+ }
4876
+ const schemaObject = schema;
4877
+ const typeValue = schemaObject.type;
4878
+ if (typeValue != null) {
4879
+ const isObjectType = typeValue === "object" || Array.isArray(typeValue) && typeValue.includes("object");
4880
+ if (!isObjectType) {
4881
+ return null;
4882
+ }
4883
+ }
4884
+ if (!schemaObject.properties || typeof schemaObject.properties !== "object") {
4885
+ return /* @__PURE__ */ new Set();
4886
+ }
4887
+ return new Set(
4888
+ Object.keys(schemaObject.properties)
4889
+ );
4890
+ }
4891
+ function schemaAllowsArrayType(schema) {
4892
+ if (!schema || typeof schema !== "object") {
4893
+ return false;
4894
+ }
4895
+ const schemaRecord = schema;
4896
+ const typeValue = schemaRecord.type;
4897
+ if (typeValue === "array") {
4898
+ return true;
4899
+ }
4900
+ if (Array.isArray(typeValue) && typeValue.includes("array")) {
4901
+ return true;
4902
+ }
4903
+ const unions = [schemaRecord.anyOf, schemaRecord.oneOf, schemaRecord.allOf];
4904
+ for (const union of unions) {
4905
+ if (!Array.isArray(union)) {
4906
+ continue;
4907
+ }
4908
+ if (union.some((entry) => schemaAllowsArrayType(entry))) {
4909
+ return true;
4910
+ }
4911
+ }
4912
+ return false;
4913
+ }
4914
+ function getSchemaObjectProperty(schema, propertyName) {
4915
+ if (!schema || typeof schema !== "object") {
4916
+ return null;
4917
+ }
4918
+ const schemaObject = schema;
4919
+ const properties = schemaObject.properties;
4920
+ if (!properties || typeof properties !== "object") {
4921
+ return null;
4922
+ }
4923
+ const property = properties[propertyName];
4924
+ if (!property) {
4925
+ return null;
4926
+ }
4927
+ return property;
4928
+ }
4929
+ function isStableXmlProgressCandidate(options) {
4930
+ const { candidate, parsed, toolSchema } = options;
4931
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
4932
+ return false;
4933
+ }
4934
+ const structure = analyzeXmlFragmentForProgress(candidate);
4935
+ if (!structure) {
4936
+ return false;
4937
+ }
4938
+ const schemaProperties = getObjectSchemaPropertyNames(toolSchema);
4939
+ if (!schemaProperties || schemaProperties.size === 0) {
4940
+ return false;
4941
+ }
4942
+ const parsedObject = parsed;
4943
+ const uniqueTopLevelTags = new Set(structure.topLevelTagNames);
4944
+ for (const tagName of uniqueTopLevelTags) {
4945
+ if (!schemaProperties.has(tagName)) {
4946
+ continue;
4947
+ }
4948
+ const schemaProperty = getSchemaObjectProperty(toolSchema, tagName);
4949
+ if (schemaProperty && schemaAllowsArrayType(schemaProperty) && !Array.isArray(parsedObject[tagName])) {
4950
+ return false;
4951
+ }
4952
+ }
4953
+ if (structure.topLevelTagNames.length === 1) {
4954
+ const onlyTopLevelTag = structure.topLevelTagNames[0];
4955
+ if (!schemaProperties || schemaProperties.size === 0 || !schemaProperties.has(onlyTopLevelTag)) {
4956
+ return false;
4957
+ }
4958
+ }
4959
+ return true;
4960
+ }
4961
+ function parseXmlContentForStreamProgress({
4962
+ toolContent,
4963
+ toolSchema,
4964
+ parseOptions
4965
+ }) {
4966
+ const tryParse = (content) => {
4967
+ try {
4968
+ return parse3(content, toolSchema, {
4969
+ ...parseOptions != null ? parseOptions : {},
4970
+ repair: false,
4971
+ onError: void 0
4972
+ });
4973
+ } catch (e) {
4974
+ return null;
4975
+ }
4976
+ };
4977
+ const strictFull = tryParse(toolContent);
4978
+ if (strictFull !== null && isStableXmlProgressCandidate({
4979
+ candidate: toolContent,
4980
+ parsed: strictFull,
4981
+ toolSchema
4982
+ })) {
4983
+ return JSON.stringify(strictFull);
4984
+ }
4985
+ let searchEnd = toolContent.length;
4986
+ while (searchEnd > 0) {
4987
+ const gtIndex = toolContent.lastIndexOf(">", searchEnd - 1);
4988
+ if (gtIndex === -1) {
4989
+ break;
4990
+ }
4991
+ const candidate = toolContent.slice(0, gtIndex + 1);
4992
+ if (!analyzeXmlFragmentForProgress(candidate)) {
4993
+ searchEnd = gtIndex;
4994
+ continue;
4995
+ }
4996
+ const parsedCandidate = tryParse(candidate);
4997
+ if (parsedCandidate !== null && isStableXmlProgressCandidate({
4998
+ candidate,
4999
+ parsed: parsedCandidate,
5000
+ toolSchema
5001
+ })) {
5002
+ return JSON.stringify(parsedCandidate);
5003
+ }
5004
+ searchEnd = gtIndex;
5005
+ }
5006
+ return null;
5007
+ }
4254
5008
  function handleStreamingToolCallEnd(params) {
4255
5009
  var _a, _b;
4256
5010
  const {
@@ -4270,19 +5024,37 @@ function handleStreamingToolCallEnd(params) {
4270
5024
  flushText(ctrl);
4271
5025
  try {
4272
5026
  const parsedResult = parse3(toolContent, toolSchema, parseConfig);
5027
+ const finalInput = JSON.stringify(parsedResult);
5028
+ emitFinalRemainder({
5029
+ controller: ctrl,
5030
+ id: currentToolCall.toolCallId,
5031
+ state: currentToolCall,
5032
+ finalFullJson: finalInput,
5033
+ onMismatch: options == null ? void 0 : options.onError
5034
+ });
5035
+ ctrl.enqueue({
5036
+ type: "tool-input-end",
5037
+ id: currentToolCall.toolCallId
5038
+ });
4273
5039
  ctrl.enqueue({
4274
5040
  type: "tool-call",
4275
- toolCallId: generateId(),
5041
+ toolCallId: currentToolCall.toolCallId,
4276
5042
  toolName: currentToolCall.name,
4277
- input: JSON.stringify(parsedResult)
5043
+ input: finalInput
4278
5044
  });
4279
5045
  } catch (error) {
5046
+ ctrl.enqueue({
5047
+ type: "tool-input-end",
5048
+ id: currentToolCall.toolCallId
5049
+ });
4280
5050
  const original = `<${currentToolCall.name}>${toolContent}</${currentToolCall.name}>`;
4281
5051
  (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, "Could not process streaming XML tool call", {
4282
5052
  toolCall: original,
4283
5053
  error
4284
5054
  });
4285
- flushText(ctrl, original);
5055
+ if (shouldEmitRawToolCallTextOnError2(options)) {
5056
+ flushText(ctrl, original);
5057
+ }
4286
5058
  }
4287
5059
  }
4288
5060
  function findClosingTagEndFlexible(text, contentStart, toolName) {
@@ -4704,38 +5476,6 @@ function findPotentialToolTagStart(buffer, toolNames) {
4704
5476
  }
4705
5477
  return -1;
4706
5478
  }
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
5479
  function processToolCallInBuffer(params) {
4740
5480
  const {
4741
5481
  buffer,
@@ -4745,18 +5485,21 @@ function processToolCallInBuffer(params) {
4745
5485
  controller,
4746
5486
  flushText,
4747
5487
  setBuffer,
4748
- parseOptions
5488
+ parseOptions,
5489
+ emitToolInputProgress: emitToolInputProgress2
4749
5490
  } = params;
4750
5491
  const endTagPattern = new RegExp(
4751
5492
  `</\\s*${escapeRegExp(currentToolCall.name)}\\s*>`
4752
5493
  );
4753
5494
  const endMatch = endTagPattern.exec(buffer);
4754
5495
  if (!endMatch || endMatch.index === void 0) {
5496
+ emitToolInputProgress2(controller, currentToolCall, buffer);
4755
5497
  return { buffer, currentToolCall, shouldBreak: true };
4756
5498
  }
4757
5499
  const endIdx = endMatch.index;
4758
5500
  const endPos = endIdx + endMatch[0].length;
4759
5501
  const content = buffer.substring(0, endIdx);
5502
+ emitToolInputProgress2(controller, currentToolCall, content);
4760
5503
  const remainder = buffer.substring(endPos);
4761
5504
  setBuffer(remainder);
4762
5505
  handleStreamingToolCallEnd({
@@ -4783,7 +5526,8 @@ function processNoToolCallInBuffer(params) {
4783
5526
  tools,
4784
5527
  options,
4785
5528
  parseOptions,
4786
- setBuffer
5529
+ setBuffer,
5530
+ emitToolInputStart
4787
5531
  } = params;
4788
5532
  const {
4789
5533
  index: earliestStartTagIndex,
@@ -4813,9 +5557,10 @@ function processNoToolCallInBuffer(params) {
4813
5557
  if (selfClosing) {
4814
5558
  const newBuffer2 = buffer.substring(earliestStartTagIndex + tagLength);
4815
5559
  setBuffer(newBuffer2);
5560
+ const currentToolCall = emitToolInputStart(controller, earliestToolName);
4816
5561
  handleStreamingToolCallEnd({
4817
5562
  toolContent: "",
4818
- currentToolCall: { name: earliestToolName, content: "" },
5563
+ currentToolCall,
4819
5564
  tools,
4820
5565
  options,
4821
5566
  ctrl: controller,
@@ -4834,12 +5579,12 @@ function processNoToolCallInBuffer(params) {
4834
5579
  setBuffer(newBuffer);
4835
5580
  return {
4836
5581
  buffer: newBuffer,
4837
- currentToolCall: { name: earliestToolName, content: "" },
5582
+ currentToolCall: emitToolInputStart(controller, earliestToolName),
4838
5583
  shouldBreak: false,
4839
5584
  shouldContinue: true
4840
5585
  };
4841
5586
  }
4842
- function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, setCurrentToolCall, tools, options, toolNames, flushText, parseOptions) {
5587
+ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, setCurrentToolCall, tools, options, toolNames, flushText, parseOptions, emitToolInputProgress2, emitToolInputStart) {
4843
5588
  return (controller) => {
4844
5589
  while (true) {
4845
5590
  const currentToolCall = getCurrentToolCall();
@@ -4852,7 +5597,8 @@ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, se
4852
5597
  controller,
4853
5598
  flushText,
4854
5599
  setBuffer,
4855
- parseOptions
5600
+ parseOptions,
5601
+ emitToolInputProgress: emitToolInputProgress2
4856
5602
  });
4857
5603
  setBuffer(result.buffer);
4858
5604
  setCurrentToolCall(result.currentToolCall);
@@ -4868,7 +5614,8 @@ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, se
4868
5614
  tools,
4869
5615
  options,
4870
5616
  parseOptions,
4871
- setBuffer
5617
+ setBuffer,
5618
+ emitToolInputStart
4872
5619
  });
4873
5620
  setBuffer(result.buffer);
4874
5621
  setCurrentToolCall(result.currentToolCall);
@@ -4982,6 +5729,118 @@ var xmlProtocol = (protocolOptions) => {
4982
5729
  hasEmittedTextStart = value;
4983
5730
  }
4984
5731
  );
5732
+ const emitToolInputStart = (controller, toolName) => {
5733
+ flushText(controller);
5734
+ const next = {
5735
+ name: toolName,
5736
+ toolCallId: generateToolCallId(),
5737
+ emittedInput: "",
5738
+ lastProgressGtIndex: null,
5739
+ lastProgressFullInput: null
5740
+ };
5741
+ controller.enqueue({
5742
+ type: "tool-input-start",
5743
+ id: next.toolCallId,
5744
+ toolName
5745
+ });
5746
+ return next;
5747
+ };
5748
+ const emitToolInputProgress2 = (controller, toolCall, toolContent) => {
5749
+ const progressGtIndex = toolContent.lastIndexOf(">");
5750
+ if (toolCall.lastProgressGtIndex === progressGtIndex) {
5751
+ const cached = toolCall.lastProgressFullInput;
5752
+ if (cached == null) {
5753
+ return;
5754
+ }
5755
+ if (cached === "{}" && toolContent.trim().length === 0) {
5756
+ return;
5757
+ }
5758
+ const prefixCandidate2 = toIncompleteJsonPrefix(cached);
5759
+ emitPrefixDelta({
5760
+ controller,
5761
+ id: toolCall.toolCallId,
5762
+ state: toolCall,
5763
+ candidate: prefixCandidate2
5764
+ });
5765
+ return;
5766
+ }
5767
+ const toolSchema = getToolSchema(tools, toolCall.name);
5768
+ const fullInput = parseXmlContentForStreamProgress({
5769
+ toolContent,
5770
+ toolSchema,
5771
+ parseOptions
5772
+ });
5773
+ toolCall.lastProgressGtIndex = progressGtIndex;
5774
+ toolCall.lastProgressFullInput = fullInput;
5775
+ if (fullInput == null) {
5776
+ return;
5777
+ }
5778
+ if (fullInput === "{}" && toolContent.trim().length === 0) {
5779
+ return;
5780
+ }
5781
+ const prefixCandidate = toIncompleteJsonPrefix(fullInput);
5782
+ emitPrefixDelta({
5783
+ controller,
5784
+ id: toolCall.toolCallId,
5785
+ state: toolCall,
5786
+ candidate: prefixCandidate
5787
+ });
5788
+ };
5789
+ const finalizeUnclosedToolCall = (controller) => {
5790
+ var _a2, _b;
5791
+ if (!currentToolCall) {
5792
+ return;
5793
+ }
5794
+ emitToolInputProgress2(controller, currentToolCall, buffer);
5795
+ const parseConfig = {
5796
+ ...parseOptions,
5797
+ onError: (_a2 = options == null ? void 0 : options.onError) != null ? _a2 : parseOptions == null ? void 0 : parseOptions.onError
5798
+ };
5799
+ const toolSchema = getToolSchema(tools, currentToolCall.name);
5800
+ flushText(controller);
5801
+ try {
5802
+ if (hasNonWhitespaceTopLevelText(buffer)) {
5803
+ throw new Error(
5804
+ "Cannot reconcile unclosed XML tool call with top-level plain text."
5805
+ );
5806
+ }
5807
+ const parsedResult = parse3(buffer, toolSchema, parseConfig);
5808
+ const finalInput = JSON.stringify(parsedResult);
5809
+ emitFinalRemainder({
5810
+ controller,
5811
+ id: currentToolCall.toolCallId,
5812
+ state: currentToolCall,
5813
+ finalFullJson: finalInput,
5814
+ onMismatch: options == null ? void 0 : options.onError
5815
+ });
5816
+ controller.enqueue({
5817
+ type: "tool-input-end",
5818
+ id: currentToolCall.toolCallId
5819
+ });
5820
+ controller.enqueue({
5821
+ type: "tool-call",
5822
+ toolCallId: currentToolCall.toolCallId,
5823
+ toolName: currentToolCall.name,
5824
+ input: finalInput
5825
+ });
5826
+ } catch (error) {
5827
+ controller.enqueue({
5828
+ type: "tool-input-end",
5829
+ id: currentToolCall.toolCallId
5830
+ });
5831
+ const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
5832
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
5833
+ options,
5834
+ "Could not complete streaming XML tool call at finish.",
5835
+ { toolCall: unfinishedContent, error }
5836
+ );
5837
+ if (shouldEmitRawToolCallTextOnError2(options)) {
5838
+ flushText(controller, unfinishedContent);
5839
+ }
5840
+ }
5841
+ buffer = "";
5842
+ currentToolCall = null;
5843
+ };
4985
5844
  const processBuffer = createProcessBufferHandler(
4986
5845
  () => buffer,
4987
5846
  (newBuffer) => {
@@ -4995,17 +5854,17 @@ var xmlProtocol = (protocolOptions) => {
4995
5854
  options,
4996
5855
  toolNames,
4997
5856
  flushText,
4998
- parseOptions
5857
+ parseOptions,
5858
+ emitToolInputProgress2,
5859
+ emitToolInputStart
4999
5860
  );
5000
5861
  return new TransformStream({
5862
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Stateful stream parsing requires branching over chunk lifecycle and parser states.
5001
5863
  transform(chunk, controller) {
5002
5864
  var _a2;
5003
5865
  if (chunk.type === "finish") {
5004
5866
  if (currentToolCall) {
5005
- const unfinishedContent = `<${currentToolCall.name}>${currentToolCall.content}${buffer}`;
5006
- flushText(controller, unfinishedContent);
5007
- buffer = "";
5008
- currentToolCall = null;
5867
+ finalizeUnclosedToolCall(controller);
5009
5868
  } else if (buffer) {
5010
5869
  flushText(controller, buffer);
5011
5870
  buffer = "";
@@ -5015,7 +5874,8 @@ var xmlProtocol = (protocolOptions) => {
5015
5874
  return;
5016
5875
  }
5017
5876
  if (chunk.type !== "text-delta") {
5018
- if (buffer) {
5877
+ if (currentToolCall) {
5878
+ } else if (buffer) {
5019
5879
  flushText(controller, buffer);
5020
5880
  buffer = "";
5021
5881
  }
@@ -5028,10 +5888,7 @@ var xmlProtocol = (protocolOptions) => {
5028
5888
  },
5029
5889
  flush(controller) {
5030
5890
  if (currentToolCall) {
5031
- const unfinishedContent = `<${currentToolCall.name}>${currentToolCall.content || ""}${buffer}`;
5032
- flushText(controller, unfinishedContent);
5033
- buffer = "";
5034
- currentToolCall = null;
5891
+ finalizeUnclosedToolCall(controller);
5035
5892
  } else if (buffer) {
5036
5893
  flushText(controller, buffer);
5037
5894
  buffer = "";
@@ -5059,7 +5916,205 @@ var xmlProtocol = (protocolOptions) => {
5059
5916
 
5060
5917
  // src/core/protocols/yaml-protocol.ts
5061
5918
  var import_yaml = __toESM(require("yaml"), 1);
5919
+ function shouldEmitRawToolCallTextOnError3(options) {
5920
+ return (options == null ? void 0 : options.emitRawToolCallTextOnError) === true;
5921
+ }
5922
+ var selfClosingTagCache2 = /* @__PURE__ */ new Map();
5923
+ function getSelfClosingTagPattern2(toolName) {
5924
+ let pattern = selfClosingTagCache2.get(toolName);
5925
+ if (!pattern) {
5926
+ pattern = new RegExp(`<\\s*${escapeRegExp(toolName)}\\s*/>`, "g");
5927
+ selfClosingTagCache2.set(toolName, pattern);
5928
+ }
5929
+ return pattern;
5930
+ }
5062
5931
  var LEADING_WHITESPACE_RE = /^(\s*)/;
5932
+ var INCOMPLETE_MAPPING_TAIL_RE = /^[^:[\]{}-][^:]*:\s*$/;
5933
+ var INCOMPLETE_SEQUENCE_TAIL_RE = /^-\s*$/;
5934
+ var BLOCK_SCALAR_KEY_RE = /:\s*[|>][-+0-9]*\s*$/;
5935
+ var PLAIN_MAPPING_VALUE_RE = /^[^:[\]{}-][^:]*:\s*(.+)$/;
5936
+ var PLAIN_SEQUENCE_VALUE_RE = /^-\s+(.+)$/;
5937
+ function normalizeYamlContent(yamlContent) {
5938
+ let normalized = yamlContent;
5939
+ if (normalized.startsWith("\n")) {
5940
+ normalized = normalized.slice(1);
5941
+ }
5942
+ const lines = normalized.split("\n");
5943
+ const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
5944
+ if (nonEmptyLines.length === 0) {
5945
+ return { normalized: "", nonEmptyLines };
5946
+ }
5947
+ const minIndent = Math.min(
5948
+ ...nonEmptyLines.map((line) => {
5949
+ const match = line.match(LEADING_WHITESPACE_RE);
5950
+ return match ? match[1].length : 0;
5951
+ })
5952
+ );
5953
+ if (minIndent > 0) {
5954
+ normalized = lines.map((line) => line.slice(minIndent)).join("\n");
5955
+ }
5956
+ return { normalized, nonEmptyLines };
5957
+ }
5958
+ function parseYamlDocumentAsMapping(normalized) {
5959
+ try {
5960
+ const doc = import_yaml.default.parseDocument(normalized);
5961
+ const errors = doc.errors.map((e) => e.message);
5962
+ const result = doc.toJSON();
5963
+ if (result === null) {
5964
+ return { value: {}, errors };
5965
+ }
5966
+ if (typeof result !== "object" || Array.isArray(result)) {
5967
+ return { value: null, errors };
5968
+ }
5969
+ return { value: result, errors };
5970
+ } catch (error) {
5971
+ return {
5972
+ value: null,
5973
+ errors: [
5974
+ error instanceof Error ? error.message : "Unknown YAML parsing error"
5975
+ ]
5976
+ };
5977
+ }
5978
+ }
5979
+ function getLastMeaningfulLineInfo(input) {
5980
+ var _a;
5981
+ const lines = input.split("\n");
5982
+ let index = lines.length - 1;
5983
+ while (index >= 0) {
5984
+ const raw = (_a = lines[index]) != null ? _a : "";
5985
+ const trimmed = raw.trim();
5986
+ if (trimmed.length > 0 && !trimmed.startsWith("#")) {
5987
+ return {
5988
+ index,
5989
+ raw,
5990
+ trimmed,
5991
+ indent: raw.length - raw.trimStart().length
5992
+ };
5993
+ }
5994
+ index -= 1;
5995
+ }
5996
+ return null;
5997
+ }
5998
+ function dropLastMeaningfulLine(input) {
5999
+ const lineInfo = getLastMeaningfulLineInfo(input);
6000
+ if (!lineInfo) {
6001
+ return null;
6002
+ }
6003
+ return input.split("\n").slice(0, lineInfo.index).join("\n").trimEnd();
6004
+ }
6005
+ function hasIncompleteMappingTail(normalized) {
6006
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
6007
+ if (!lineInfo) {
6008
+ return false;
6009
+ }
6010
+ return INCOMPLETE_MAPPING_TAIL_RE.test(lineInfo.trimmed);
6011
+ }
6012
+ function hasIncompleteSequenceTail(normalized) {
6013
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
6014
+ if (!lineInfo) {
6015
+ return false;
6016
+ }
6017
+ return INCOMPLETE_SEQUENCE_TAIL_RE.test(lineInfo.trimmed);
6018
+ }
6019
+ function hasSplitNestedKeyTail(normalized) {
6020
+ var _a;
6021
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
6022
+ if (!lineInfo) {
6023
+ return false;
6024
+ }
6025
+ const { trimmed, indent, index } = lineInfo;
6026
+ if (indent === 0) {
6027
+ return false;
6028
+ }
6029
+ if (trimmed.startsWith("#") || trimmed.startsWith("-") || trimmed.includes(":")) {
6030
+ return false;
6031
+ }
6032
+ const lines = normalized.split("\n");
6033
+ let parentIndex = index - 1;
6034
+ while (parentIndex >= 0) {
6035
+ const parentRaw = (_a = lines[parentIndex]) != null ? _a : "";
6036
+ const parentTrimmed = parentRaw.trim();
6037
+ if (parentTrimmed.length === 0 || parentTrimmed.startsWith("#")) {
6038
+ parentIndex -= 1;
6039
+ continue;
6040
+ }
6041
+ const parentIndent = parentRaw.length - parentRaw.trimStart().length;
6042
+ if (parentIndent >= indent) {
6043
+ parentIndex -= 1;
6044
+ continue;
6045
+ }
6046
+ if (!parentTrimmed.endsWith(":")) {
6047
+ return false;
6048
+ }
6049
+ if (BLOCK_SCALAR_KEY_RE.test(parentTrimmed)) {
6050
+ return false;
6051
+ }
6052
+ return true;
6053
+ }
6054
+ return false;
6055
+ }
6056
+ function extractTrailingPlainScalarValue(line) {
6057
+ var _a;
6058
+ if (BLOCK_SCALAR_KEY_RE.test(line)) {
6059
+ return null;
6060
+ }
6061
+ const mappingMatch = line.match(PLAIN_MAPPING_VALUE_RE);
6062
+ const sequenceMatch = line.match(PLAIN_SEQUENCE_VALUE_RE);
6063
+ const value = (_a = mappingMatch == null ? void 0 : mappingMatch[1]) != null ? _a : sequenceMatch == null ? void 0 : sequenceMatch[1];
6064
+ if (!value) {
6065
+ return null;
6066
+ }
6067
+ const trimmedValue = value.trim();
6068
+ if (trimmedValue.length === 0) {
6069
+ return null;
6070
+ }
6071
+ if (trimmedValue.startsWith('"') || trimmedValue.startsWith("'")) {
6072
+ return null;
6073
+ }
6074
+ if (trimmedValue.startsWith("{") || trimmedValue.startsWith("[") || trimmedValue.startsWith("|") || trimmedValue.startsWith(">")) {
6075
+ return null;
6076
+ }
6077
+ return trimmedValue;
6078
+ }
6079
+ function hasUnterminatedPlainScalarTail(normalized) {
6080
+ if (normalized.endsWith("\n")) {
6081
+ return false;
6082
+ }
6083
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
6084
+ if (!lineInfo) {
6085
+ return false;
6086
+ }
6087
+ return extractTrailingPlainScalarValue(lineInfo.trimmed) != null;
6088
+ }
6089
+ function hasUnstableProgressTail(normalized) {
6090
+ return hasIncompleteMappingTail(normalized) || hasIncompleteSequenceTail(normalized) || hasSplitNestedKeyTail(normalized) || hasUnterminatedPlainScalarTail(normalized);
6091
+ }
6092
+ function trimTrailingNewlineInUnknown(value) {
6093
+ if (typeof value === "string") {
6094
+ if (value.endsWith("\n")) {
6095
+ return value.slice(0, -1);
6096
+ }
6097
+ return value;
6098
+ }
6099
+ if (Array.isArray(value)) {
6100
+ return value.map((item) => trimTrailingNewlineInUnknown(item));
6101
+ }
6102
+ if (value && typeof value === "object") {
6103
+ return Object.fromEntries(
6104
+ Object.entries(value).map(([key, item]) => [
6105
+ key,
6106
+ trimTrailingNewlineInUnknown(item)
6107
+ ])
6108
+ );
6109
+ }
6110
+ return value;
6111
+ }
6112
+ function stabilizeParsedValueForStreamProgress(value, source) {
6113
+ if (source.endsWith("\n")) {
6114
+ return value;
6115
+ }
6116
+ return trimTrailingNewlineInUnknown(value);
6117
+ }
5063
6118
  function findClosingTagEnd(text, contentStart, toolName) {
5064
6119
  let pos = contentStart;
5065
6120
  let depth = 1;
@@ -5131,7 +6186,7 @@ function findEarliestTagPosition(openIdx, selfIdx) {
5131
6186
  function collectToolCallsForName(text, toolName) {
5132
6187
  const toolCalls = [];
5133
6188
  let searchIndex = 0;
5134
- const selfTagRegex = new RegExp(`<${toolName}\\s*/>`, "g");
6189
+ const selfTagRegex = getSelfClosingTagPattern2(toolName);
5135
6190
  while (searchIndex < text.length) {
5136
6191
  const startTag = `<${toolName}>`;
5137
6192
  const openIdx = text.indexOf(startTag, searchIndex);
@@ -5183,47 +6238,48 @@ function findToolCalls2(text, toolNames) {
5183
6238
  return toolCalls.sort((a, b) => a.startIndex - b.startIndex);
5184
6239
  }
5185
6240
  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);
6241
+ var _a, _b;
6242
+ const { normalized, nonEmptyLines } = normalizeYamlContent(yamlContent);
5193
6243
  if (nonEmptyLines.length === 0) {
5194
6244
  return {};
5195
6245
  }
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");
6246
+ const parsed = parseYamlDocumentAsMapping(normalized);
6247
+ if (parsed.errors.length > 0) {
6248
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "YAML parse error", {
6249
+ errors: parsed.errors
6250
+ });
6251
+ return null;
5204
6252
  }
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;
6253
+ if (parsed.value === null) {
6254
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, "YAML content must be a key-value mapping", {
6255
+ got: "non-mapping"
6256
+ });
6257
+ return null;
6258
+ }
6259
+ return parsed.value;
6260
+ }
6261
+ function parseYamlContentForStreamProgress(yamlContent) {
6262
+ const { normalized, nonEmptyLines } = normalizeYamlContent(yamlContent);
6263
+ if (nonEmptyLines.length === 0) {
6264
+ return {};
6265
+ }
6266
+ let candidate = normalized;
6267
+ while (true) {
6268
+ const parsed = parseYamlDocumentAsMapping(candidate);
6269
+ if (parsed.errors.length === 0 && !hasUnstableProgressTail(candidate)) {
6270
+ if (candidate.trim().length === 0 && normalized.trim().length > 0) {
6271
+ return null;
6272
+ }
6273
+ return stabilizeParsedValueForStreamProgress(parsed.value, candidate);
5212
6274
  }
5213
- const result = doc.toJSON();
5214
- if (result === null) {
5215
- return {};
6275
+ const truncated = dropLastMeaningfulLine(candidate);
6276
+ if (truncated == null) {
6277
+ return null;
5216
6278
  }
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
- });
6279
+ if (truncated === candidate) {
5221
6280
  return null;
5222
6281
  }
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;
6282
+ candidate = truncated;
5227
6283
  }
5228
6284
  }
5229
6285
  function processToolCallMatch(text, tc, currentIndex, processedElements, options) {
@@ -5239,7 +6295,7 @@ function processToolCallMatch(text, tc, currentIndex, processedElements, options
5239
6295
  if (parsedArgs !== null) {
5240
6296
  processedElements.push({
5241
6297
  type: "tool-call",
5242
- toolCallId: generateId(),
6298
+ toolCallId: generateToolCallId(),
5243
6299
  toolName: tc.toolName,
5244
6300
  input: JSON.stringify(parsedArgs)
5245
6301
  });
@@ -5252,38 +6308,6 @@ function processToolCallMatch(text, tc, currentIndex, processedElements, options
5252
6308
  }
5253
6309
  return tc.endIndex;
5254
6310
  }
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
6311
  function findEarliestToolTag2(buffer, toolNames) {
5288
6312
  let bestIndex = -1;
5289
6313
  let bestName = "";
@@ -5291,8 +6315,9 @@ function findEarliestToolTag2(buffer, toolNames) {
5291
6315
  let bestTagLength = 0;
5292
6316
  for (const name of toolNames) {
5293
6317
  const openTag = `<${name}>`;
5294
- const selfTagRegex = new RegExp(`<${name}\\s*/>`);
6318
+ const selfTagRegex = getSelfClosingTagPattern2(name);
5295
6319
  const idxOpen = buffer.indexOf(openTag);
6320
+ selfTagRegex.lastIndex = 0;
5296
6321
  const selfMatch = selfTagRegex.exec(buffer);
5297
6322
  const idxSelf = selfMatch ? selfMatch.index : -1;
5298
6323
  if (idxOpen !== -1 && (bestIndex === -1 || idxOpen < bestIndex)) {
@@ -5315,6 +6340,29 @@ function findEarliestToolTag2(buffer, toolNames) {
5315
6340
  tagLength: bestTagLength
5316
6341
  };
5317
6342
  }
6343
+ function stripTrailingPartialCloseTag(content, toolName) {
6344
+ const closeTag = `</${toolName}>`;
6345
+ const lastLineBreakIndex = Math.max(
6346
+ content.lastIndexOf("\n"),
6347
+ content.lastIndexOf("\r")
6348
+ );
6349
+ const lineStartIndex = lastLineBreakIndex === -1 ? 0 : lastLineBreakIndex + 1;
6350
+ const trailingLine = content.slice(lineStartIndex);
6351
+ const trimmedTrailingLine = trailingLine.trim();
6352
+ if (trimmedTrailingLine.length === 0 || !trimmedTrailingLine.startsWith("</") || trimmedTrailingLine === closeTag || !closeTag.startsWith(trimmedTrailingLine)) {
6353
+ return content;
6354
+ }
6355
+ const leadingWhitespaceLength = trailingLine.length - trailingLine.trimStart().length;
6356
+ const preservedLeadingWhitespace = trailingLine.slice(
6357
+ 0,
6358
+ leadingWhitespaceLength
6359
+ );
6360
+ const contentWithoutPartial = `${content.slice(
6361
+ 0,
6362
+ lineStartIndex
6363
+ )}${preservedLeadingWhitespace}`;
6364
+ return contentWithoutPartial.trimEnd();
6365
+ }
5318
6366
  var yamlProtocol = (_protocolOptions) => {
5319
6367
  return {
5320
6368
  formatTools({ tools, toolSystemPromptTemplate }) {
@@ -5375,7 +6423,7 @@ ${yamlContent}</${toolCall.toolName}>`;
5375
6423
  let currentToolCall = null;
5376
6424
  let currentTextId = null;
5377
6425
  let hasEmittedTextStart = false;
5378
- const flushText = createFlushTextHandler2(
6426
+ const flushText = createFlushTextHandler(
5379
6427
  () => currentTextId,
5380
6428
  (newId) => {
5381
6429
  currentTextId = newId;
@@ -5385,33 +6433,128 @@ ${yamlContent}</${toolCall.toolName}>`;
5385
6433
  hasEmittedTextStart = value;
5386
6434
  }
5387
6435
  );
5388
- const processToolCallEnd = (controller, toolContent, toolName) => {
6436
+ const emitToolInputProgress2 = (controller, toolContent) => {
6437
+ if (!currentToolCall) {
6438
+ return;
6439
+ }
6440
+ const parsedArgs = parseYamlContentForStreamProgress(toolContent);
6441
+ if (parsedArgs === null) {
6442
+ return;
6443
+ }
6444
+ const fullInput = JSON.stringify(parsedArgs);
6445
+ if (fullInput === "{}" && toolContent.trim().length === 0) {
6446
+ return;
6447
+ }
6448
+ const prefixCandidate = toIncompleteJsonPrefix(fullInput);
6449
+ emitPrefixDelta({
6450
+ controller,
6451
+ id: currentToolCall.toolCallId,
6452
+ state: currentToolCall,
6453
+ candidate: prefixCandidate
6454
+ });
6455
+ };
6456
+ const processToolCallEnd = (controller, toolContent, toolName, toolCallId) => {
5389
6457
  var _a;
5390
6458
  const parsedArgs = parseYamlContent(toolContent, options);
5391
6459
  flushText(controller);
5392
6460
  if (parsedArgs !== null) {
6461
+ const finalInput = JSON.stringify(parsedArgs);
6462
+ if (currentToolCall && currentToolCall.toolCallId === toolCallId) {
6463
+ emitFinalRemainder({
6464
+ controller,
6465
+ id: toolCallId,
6466
+ state: currentToolCall,
6467
+ finalFullJson: finalInput,
6468
+ onMismatch: options == null ? void 0 : options.onError
6469
+ });
6470
+ }
6471
+ controller.enqueue({
6472
+ type: "tool-input-end",
6473
+ id: toolCallId
6474
+ });
5393
6475
  controller.enqueue({
5394
6476
  type: "tool-call",
5395
- toolCallId: generateId(),
6477
+ toolCallId,
5396
6478
  toolName,
5397
- input: JSON.stringify(parsedArgs)
6479
+ input: finalInput
5398
6480
  });
5399
6481
  } else {
6482
+ controller.enqueue({
6483
+ type: "tool-input-end",
6484
+ id: toolCallId
6485
+ });
5400
6486
  const original = `<${toolName}>${toolContent}</${toolName}>`;
5401
6487
  (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "Could not parse streaming YAML tool call", {
5402
6488
  toolCall: original
5403
6489
  });
5404
- flushText(controller, original);
6490
+ if (shouldEmitRawToolCallTextOnError3(options)) {
6491
+ flushText(controller, original);
6492
+ }
5405
6493
  }
5406
6494
  };
6495
+ const finalizeUnclosedToolCall = (controller) => {
6496
+ var _a;
6497
+ if (!currentToolCall) {
6498
+ return;
6499
+ }
6500
+ emitToolInputProgress2(controller, buffer);
6501
+ const { name: toolName, toolCallId } = currentToolCall;
6502
+ const reconciledBuffer = stripTrailingPartialCloseTag(buffer, toolName);
6503
+ const parsedArgs = parseYamlContent(reconciledBuffer, options);
6504
+ flushText(controller);
6505
+ if (parsedArgs !== null) {
6506
+ const finalInput = JSON.stringify(parsedArgs);
6507
+ emitFinalRemainder({
6508
+ controller,
6509
+ id: toolCallId,
6510
+ state: currentToolCall,
6511
+ finalFullJson: finalInput,
6512
+ onMismatch: options == null ? void 0 : options.onError
6513
+ });
6514
+ controller.enqueue({
6515
+ type: "tool-input-end",
6516
+ id: toolCallId
6517
+ });
6518
+ controller.enqueue({
6519
+ type: "tool-call",
6520
+ toolCallId,
6521
+ toolName,
6522
+ input: finalInput
6523
+ });
6524
+ } else {
6525
+ controller.enqueue({
6526
+ type: "tool-input-end",
6527
+ id: toolCallId
6528
+ });
6529
+ const unfinishedContent = `<${toolName}>${buffer}`;
6530
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
6531
+ options,
6532
+ "Could not complete streaming YAML tool call at finish.",
6533
+ { toolCall: unfinishedContent }
6534
+ );
6535
+ if (shouldEmitRawToolCallTextOnError3(options)) {
6536
+ flushText(controller, unfinishedContent);
6537
+ }
6538
+ }
6539
+ buffer = "";
6540
+ currentToolCall = null;
6541
+ };
5407
6542
  const handlePendingToolCall = (controller, endTag, toolName) => {
6543
+ var _a;
5408
6544
  const endIdx = buffer.indexOf(endTag);
5409
6545
  if (endIdx === -1) {
6546
+ emitToolInputProgress2(controller, buffer);
5410
6547
  return false;
5411
6548
  }
5412
6549
  const content = buffer.substring(0, endIdx);
6550
+ emitToolInputProgress2(controller, content);
5413
6551
  buffer = buffer.substring(endIdx + endTag.length);
5414
- processToolCallEnd(controller, content, toolName);
6552
+ processToolCallEnd(
6553
+ controller,
6554
+ content,
6555
+ toolName,
6556
+ (_a = currentToolCall == null ? void 0 : currentToolCall.toolCallId) != null ? _a : generateToolCallId()
6557
+ );
5415
6558
  currentToolCall = null;
5416
6559
  return true;
5417
6560
  };
@@ -5428,13 +6571,35 @@ ${yamlContent}</${toolCall.toolName}>`;
5428
6571
  if (tagIndex > 0) {
5429
6572
  flushText(controller, buffer.substring(0, tagIndex));
5430
6573
  }
6574
+ flushText(controller);
5431
6575
  if (selfClosing) {
5432
6576
  buffer = buffer.substring(tagIndex + tagLength);
5433
- processToolCallEnd(controller, "", tagName);
6577
+ const toolCallId = generateToolCallId();
6578
+ currentToolCall = {
6579
+ name: tagName,
6580
+ toolCallId,
6581
+ emittedInput: ""
6582
+ };
6583
+ controller.enqueue({
6584
+ type: "tool-input-start",
6585
+ id: toolCallId,
6586
+ toolName: tagName
6587
+ });
6588
+ processToolCallEnd(controller, "", tagName, toolCallId);
6589
+ currentToolCall = null;
5434
6590
  } else {
5435
6591
  const startTag = `<${tagName}>`;
5436
6592
  buffer = buffer.substring(tagIndex + startTag.length);
5437
- currentToolCall = { name: tagName, content: "" };
6593
+ currentToolCall = {
6594
+ name: tagName,
6595
+ toolCallId: generateToolCallId(),
6596
+ emittedInput: ""
6597
+ };
6598
+ controller.enqueue({
6599
+ type: "tool-input-start",
6600
+ id: currentToolCall.toolCallId,
6601
+ toolName: tagName
6602
+ });
5438
6603
  }
5439
6604
  };
5440
6605
  const processBuffer = (controller) => {
@@ -5463,10 +6628,7 @@ ${yamlContent}</${toolCall.toolName}>`;
5463
6628
  var _a;
5464
6629
  if (chunk.type === "finish") {
5465
6630
  if (currentToolCall) {
5466
- const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
5467
- flushText(controller, unfinishedContent);
5468
- buffer = "";
5469
- currentToolCall = null;
6631
+ finalizeUnclosedToolCall(controller);
5470
6632
  } else if (buffer) {
5471
6633
  flushText(controller, buffer);
5472
6634
  buffer = "";
@@ -5476,7 +6638,7 @@ ${yamlContent}</${toolCall.toolName}>`;
5476
6638
  return;
5477
6639
  }
5478
6640
  if (chunk.type !== "text-delta") {
5479
- if (buffer) {
6641
+ if (!currentToolCall && buffer) {
5480
6642
  flushText(controller, buffer);
5481
6643
  buffer = "";
5482
6644
  }
@@ -5489,10 +6651,7 @@ ${yamlContent}</${toolCall.toolName}>`;
5489
6651
  },
5490
6652
  flush(controller) {
5491
6653
  if (currentToolCall) {
5492
- const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
5493
- flushText(controller, unfinishedContent);
5494
- buffer = "";
5495
- currentToolCall = null;
6654
+ finalizeUnclosedToolCall(controller);
5496
6655
  } else if (buffer) {
5497
6656
  flushText(controller, buffer);
5498
6657
  buffer = "";
@@ -5670,9 +6829,6 @@ function hasInputProperty(obj) {
5670
6829
  return typeof obj === "object" && obj !== null && "input" in obj;
5671
6830
  }
5672
6831
 
5673
- // src/generate-handler.ts
5674
- var import_provider_utils = require("@ai-sdk/provider-utils");
5675
-
5676
6832
  // src/core/utils/generated-text-json-recovery.ts
5677
6833
  function isRecord(value) {
5678
6834
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -5811,7 +6967,7 @@ function mergeJsonCandidatesByStart(tagged, codeBlocks, balanced) {
5811
6967
  function toToolCallPart(candidate) {
5812
6968
  return {
5813
6969
  type: "tool-call",
5814
- toolCallId: generateId(),
6970
+ toolCallId: generateToolCallId(),
5815
6971
  toolName: candidate.toolName,
5816
6972
  input: candidate.input
5817
6973
  };
@@ -6038,7 +7194,7 @@ async function handleToolChoice(doGenerate, params, tools) {
6038
7194
  }
6039
7195
  const toolCall = {
6040
7196
  type: "tool-call",
6041
- toolCallId: (0, import_provider_utils.generateId)(),
7197
+ toolCallId: generateToolCallId(),
6042
7198
  toolName,
6043
7199
  input
6044
7200
  };
@@ -6548,7 +7704,6 @@ unit: celsius
6548
7704
  }
6549
7705
 
6550
7706
  // src/stream-handler.ts
6551
- var import_provider_utils2 = require("@ai-sdk/provider-utils");
6552
7707
  async function wrapStream({
6553
7708
  protocol,
6554
7709
  doStream,
@@ -6586,10 +7741,22 @@ async function wrapStream({
6586
7741
  }
6587
7742
  )
6588
7743
  ).pipeThrough(protocol.createStreamParser({ tools, options }));
7744
+ let seenToolCall = false;
6589
7745
  const v3Stream = coreStream.pipeThrough(
6590
7746
  new TransformStream({
6591
7747
  transform(part, controller) {
6592
- const normalizedPart = part.type === "tool-call" ? coerceToolCallPart(part, tools) : part;
7748
+ let normalizedPart = part.type === "tool-call" ? coerceToolCallPart(part, tools) : part;
7749
+ if (normalizedPart.type === "tool-call") {
7750
+ seenToolCall = true;
7751
+ }
7752
+ if (normalizedPart.type === "finish" && seenToolCall && normalizedPart.finishReason.unified === "stop") {
7753
+ normalizedPart = {
7754
+ ...normalizedPart,
7755
+ finishReason: normalizeToolCallsFinishReason(
7756
+ normalizedPart.finishReason
7757
+ )
7758
+ };
7759
+ }
6593
7760
  if (debugLevel === "stream") {
6594
7761
  logParsedChunk(normalizedPart);
6595
7762
  }
@@ -6626,7 +7793,7 @@ async function toolChoiceStream({
6626
7793
  start(controller) {
6627
7794
  controller.enqueue({
6628
7795
  type: "tool-call",
6629
- toolCallId: (0, import_provider_utils2.generateId)(),
7796
+ toolCallId: generateToolCallId(),
6630
7797
  toolName,
6631
7798
  input
6632
7799
  });