@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.
@@ -187,7 +187,25 @@ function getPotentialStartIndex(text, searchedText) {
187
187
 
188
188
  // src/core/utils/id.ts
189
189
  function generateId() {
190
- return Math.random().toString(36).substring(2, 15);
190
+ return crypto.randomUUID().replace(/-/g, "").slice(0, 13);
191
+ }
192
+ var TOOL_CALL_ID_PREFIX = "call_";
193
+ var TOOL_CALL_ID_BODY_LENGTH = 24;
194
+ var TOOL_CALL_ID_ALPHANUM = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
195
+ function randomAlphaNumeric(length) {
196
+ var _a;
197
+ const bytes = new Uint8Array(length);
198
+ crypto.getRandomValues(bytes);
199
+ let out = "";
200
+ for (let i = 0; i < length; i += 1) {
201
+ const byte = bytes[i];
202
+ const index = (byte != null ? byte : 0) % TOOL_CALL_ID_ALPHANUM.length;
203
+ out += (_a = TOOL_CALL_ID_ALPHANUM[index]) != null ? _a : "0";
204
+ }
205
+ return out;
206
+ }
207
+ function generateToolCallId() {
208
+ return `${TOOL_CALL_ID_PREFIX}${randomAlphaNumeric(TOOL_CALL_ID_BODY_LENGTH)}`;
191
209
  }
192
210
 
193
211
  // src/core/utils/protocol-utils.ts
@@ -196,17 +214,55 @@ function addTextSegment(text, processedElements) {
196
214
  processedElements.push({ type: "text", text });
197
215
  }
198
216
  }
217
+ function createFlushTextHandler(getCurrentTextId, setCurrentTextId, getHasEmittedTextStart, setHasEmittedTextStart) {
218
+ return (controller, text) => {
219
+ const content = text;
220
+ if (content) {
221
+ if (!getCurrentTextId()) {
222
+ const newId = generateId();
223
+ setCurrentTextId(newId);
224
+ controller.enqueue({
225
+ type: "text-start",
226
+ id: newId
227
+ });
228
+ setHasEmittedTextStart(true);
229
+ }
230
+ controller.enqueue({
231
+ type: "text-delta",
232
+ id: getCurrentTextId(),
233
+ delta: content
234
+ });
235
+ }
236
+ const currentTextId = getCurrentTextId();
237
+ if (currentTextId && !text) {
238
+ if (getHasEmittedTextStart()) {
239
+ controller.enqueue({
240
+ type: "text-end",
241
+ id: currentTextId
242
+ });
243
+ setHasEmittedTextStart(false);
244
+ }
245
+ setCurrentTextId(null);
246
+ }
247
+ };
248
+ }
199
249
 
200
250
  // src/core/protocols/json-protocol.ts
251
+ function shouldEmitRawToolCallTextOnError(options) {
252
+ return (options == null ? void 0 : options.emitRawToolCallTextOnError) === true;
253
+ }
254
+ function canonicalizeToolInput(argumentsValue) {
255
+ return JSON.stringify(argumentsValue != null ? argumentsValue : {});
256
+ }
201
257
  function processToolCallJson(toolCallJson, fullMatch, processedElements, options) {
202
- var _a, _b;
258
+ var _a;
203
259
  try {
204
260
  const parsedToolCall = parse(toolCallJson);
205
261
  processedElements.push({
206
262
  type: "tool-call",
207
- toolCallId: generateId(),
263
+ toolCallId: generateToolCallId(),
208
264
  toolName: parsedToolCall.name,
209
- input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
265
+ input: canonicalizeToolInput(parsedToolCall.arguments)
210
266
  });
211
267
  } catch (error) {
212
268
  logParseFailure({
@@ -215,7 +271,7 @@ function processToolCallJson(toolCallJson, fullMatch, processedElements, options
215
271
  snippet: fullMatch,
216
272
  error
217
273
  });
218
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
274
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
219
275
  options,
220
276
  "Could not process JSON tool call, keeping original text.",
221
277
  { toolCall: fullMatch, error }
@@ -236,6 +292,293 @@ function processMatchedToolCall(context) {
236
292
  }
237
293
  return startIndex + match[0].length;
238
294
  }
295
+ var WHITESPACE_JSON_REGEX = /\s/;
296
+ function skipJsonWhitespace(text, fromIndex) {
297
+ let index = fromIndex;
298
+ while (index < text.length && WHITESPACE_JSON_REGEX.test(text[index])) {
299
+ index += 1;
300
+ }
301
+ return index;
302
+ }
303
+ function findTopLevelPropertyValueStart(text, property) {
304
+ const objectStart = skipJsonWhitespace(text, 0);
305
+ if (objectStart >= text.length || text.charAt(objectStart) !== "{") {
306
+ return null;
307
+ }
308
+ let depth = 0;
309
+ let inString = false;
310
+ let escaping = false;
311
+ for (let index = objectStart; index < text.length; index += 1) {
312
+ const char = text.charAt(index);
313
+ if (inString) {
314
+ if (escaping) {
315
+ escaping = false;
316
+ continue;
317
+ }
318
+ if (char === "\\") {
319
+ escaping = true;
320
+ continue;
321
+ }
322
+ if (char === '"') {
323
+ inString = false;
324
+ }
325
+ continue;
326
+ }
327
+ if (char === "{") {
328
+ depth += 1;
329
+ continue;
330
+ }
331
+ if (char === "}") {
332
+ depth = Math.max(0, depth - 1);
333
+ continue;
334
+ }
335
+ if (char !== '"') {
336
+ continue;
337
+ }
338
+ if (depth !== 1) {
339
+ inString = true;
340
+ continue;
341
+ }
342
+ const keyStart = index + 1;
343
+ let keyEnd = keyStart;
344
+ let keyEscaped = false;
345
+ while (keyEnd < text.length) {
346
+ const keyChar = text.charAt(keyEnd);
347
+ if (keyEscaped) {
348
+ keyEscaped = false;
349
+ } else if (keyChar === "\\") {
350
+ keyEscaped = true;
351
+ } else if (keyChar === '"') {
352
+ break;
353
+ }
354
+ keyEnd += 1;
355
+ }
356
+ if (keyEnd >= text.length || text.charAt(keyEnd) !== '"') {
357
+ return null;
358
+ }
359
+ const key = text.slice(keyStart, keyEnd);
360
+ let valueCursor = skipJsonWhitespace(text, keyEnd + 1);
361
+ if (valueCursor >= text.length || text.charAt(valueCursor) !== ":") {
362
+ index = keyEnd;
363
+ continue;
364
+ }
365
+ valueCursor = skipJsonWhitespace(text, valueCursor + 1);
366
+ if (key === property) {
367
+ return valueCursor < text.length ? valueCursor : null;
368
+ }
369
+ index = valueCursor - 1;
370
+ }
371
+ return null;
372
+ }
373
+ function extractTopLevelStringProperty(text, property) {
374
+ const valueStart = findTopLevelPropertyValueStart(text, property);
375
+ if (valueStart == null || valueStart >= text.length) {
376
+ return void 0;
377
+ }
378
+ if (text.charAt(valueStart) !== '"') {
379
+ return void 0;
380
+ }
381
+ let valueEnd = valueStart + 1;
382
+ let escaped = false;
383
+ while (valueEnd < text.length) {
384
+ const char = text.charAt(valueEnd);
385
+ if (escaped) {
386
+ escaped = false;
387
+ } else if (char === "\\") {
388
+ escaped = true;
389
+ } else if (char === '"') {
390
+ return text.slice(valueStart + 1, valueEnd);
391
+ }
392
+ valueEnd += 1;
393
+ }
394
+ return void 0;
395
+ }
396
+ function extractJsonValueSlice(text, valueStart) {
397
+ if (valueStart >= text.length) {
398
+ return null;
399
+ }
400
+ const first = text.charAt(valueStart);
401
+ if (first === "{" || first === "[") {
402
+ const stack = [first];
403
+ let inString = false;
404
+ let escaped = false;
405
+ for (let index2 = valueStart + 1; index2 < text.length; index2 += 1) {
406
+ const char = text.charAt(index2);
407
+ if (inString) {
408
+ if (escaped) {
409
+ escaped = false;
410
+ } else if (char === "\\") {
411
+ escaped = true;
412
+ } else if (char === '"') {
413
+ inString = false;
414
+ }
415
+ continue;
416
+ }
417
+ if (char === '"') {
418
+ inString = true;
419
+ continue;
420
+ }
421
+ if (char === "{" || char === "[") {
422
+ stack.push(char);
423
+ continue;
424
+ }
425
+ if (char === "}" || char === "]") {
426
+ const open = stack.at(-1);
427
+ if (open === "{" && char === "}" || open === "[" && char === "]") {
428
+ stack.pop();
429
+ if (stack.length === 0) {
430
+ return {
431
+ text: text.slice(valueStart, index2 + 1),
432
+ complete: true
433
+ };
434
+ }
435
+ }
436
+ }
437
+ }
438
+ return {
439
+ text: text.slice(valueStart),
440
+ complete: false
441
+ };
442
+ }
443
+ if (first === '"') {
444
+ let escaped = false;
445
+ for (let index2 = valueStart + 1; index2 < text.length; index2 += 1) {
446
+ const char = text.charAt(index2);
447
+ if (escaped) {
448
+ escaped = false;
449
+ } else if (char === "\\") {
450
+ escaped = true;
451
+ } else if (char === '"') {
452
+ return {
453
+ text: text.slice(valueStart, index2 + 1),
454
+ complete: true
455
+ };
456
+ }
457
+ }
458
+ return {
459
+ text: text.slice(valueStart),
460
+ complete: false
461
+ };
462
+ }
463
+ let index = valueStart;
464
+ while (index < text.length) {
465
+ const char = text.charAt(index);
466
+ if (char === "," || char === "}" || WHITESPACE_JSON_REGEX.test(char)) {
467
+ break;
468
+ }
469
+ index += 1;
470
+ }
471
+ return {
472
+ text: text.slice(valueStart, index),
473
+ complete: index < text.length
474
+ };
475
+ }
476
+ function extractStreamingToolCallProgress(toolCallJson) {
477
+ var _a;
478
+ const toolName = extractTopLevelStringProperty(toolCallJson, "name");
479
+ const argsValueStart = findTopLevelPropertyValueStart(
480
+ toolCallJson,
481
+ "arguments"
482
+ );
483
+ if (argsValueStart == null) {
484
+ return {
485
+ toolName,
486
+ argumentsText: void 0,
487
+ argumentsComplete: false
488
+ };
489
+ }
490
+ const argsSlice = extractJsonValueSlice(toolCallJson, argsValueStart);
491
+ return {
492
+ toolName,
493
+ argumentsText: argsSlice == null ? void 0 : argsSlice.text,
494
+ argumentsComplete: (_a = argsSlice == null ? void 0 : argsSlice.complete) != null ? _a : false
495
+ };
496
+ }
497
+ function ensureToolInputStart(state, controller, toolName) {
498
+ if (!state.activeToolInput) {
499
+ const id = generateToolCallId();
500
+ state.activeToolInput = {
501
+ id,
502
+ toolName,
503
+ emittedInput: ""
504
+ };
505
+ controller.enqueue({
506
+ type: "tool-input-start",
507
+ id,
508
+ toolName
509
+ });
510
+ }
511
+ }
512
+ function emitToolInputDelta(state, controller, fullInput) {
513
+ const active = state.activeToolInput;
514
+ if (!active) {
515
+ return;
516
+ }
517
+ if (!fullInput.startsWith(active.emittedInput)) {
518
+ return;
519
+ }
520
+ const delta = fullInput.slice(active.emittedInput.length);
521
+ if (delta.length === 0) {
522
+ return;
523
+ }
524
+ controller.enqueue({
525
+ type: "tool-input-delta",
526
+ id: active.id,
527
+ delta
528
+ });
529
+ active.emittedInput = fullInput;
530
+ }
531
+ function closeToolInput(state, controller) {
532
+ if (!state.activeToolInput) {
533
+ return;
534
+ }
535
+ controller.enqueue({
536
+ type: "tool-input-end",
537
+ id: state.activeToolInput.id
538
+ });
539
+ state.activeToolInput = null;
540
+ }
541
+ function emitToolCallFromParsed(state, controller, parsedToolCall) {
542
+ var _a, _b, _c, _d;
543
+ closeTextBlock(state, controller);
544
+ const toolName = typeof parsedToolCall.name === "string" ? parsedToolCall.name : (_b = (_a = state.activeToolInput) == null ? void 0 : _a.toolName) != null ? _b : "unknown";
545
+ const input = canonicalizeToolInput(parsedToolCall.arguments);
546
+ ensureToolInputStart(state, controller, toolName);
547
+ emitToolInputDelta(state, controller, input);
548
+ const toolCallId = (_d = (_c = state.activeToolInput) == null ? void 0 : _c.id) != null ? _d : generateToolCallId();
549
+ closeToolInput(state, controller);
550
+ controller.enqueue({
551
+ type: "tool-call",
552
+ toolCallId,
553
+ toolName,
554
+ input
555
+ });
556
+ }
557
+ function canonicalizeArgumentsProgressInput(progress) {
558
+ if (progress.argumentsText === void 0 || !progress.argumentsComplete) {
559
+ return void 0;
560
+ }
561
+ try {
562
+ const parsedArguments = parse(progress.argumentsText);
563
+ return canonicalizeToolInput(parsedArguments);
564
+ } catch (e) {
565
+ return void 0;
566
+ }
567
+ }
568
+ function emitToolInputProgress(state, controller) {
569
+ if (!(state.isInsideToolCall && state.currentToolCallJson)) {
570
+ return;
571
+ }
572
+ const progress = extractStreamingToolCallProgress(state.currentToolCallJson);
573
+ if (!progress.toolName) {
574
+ return;
575
+ }
576
+ ensureToolInputStart(state, controller, progress.toolName);
577
+ const canonicalProgressInput = canonicalizeArgumentsProgressInput(progress);
578
+ if (canonicalProgressInput !== void 0) {
579
+ emitToolInputDelta(state, controller, canonicalProgressInput);
580
+ }
581
+ }
239
582
  function flushBuffer(state, controller, toolCallStart) {
240
583
  if (state.buffer.length === 0) {
241
584
  return;
@@ -266,44 +609,77 @@ function closeTextBlock(state, controller) {
266
609
  state.hasEmittedTextStart = false;
267
610
  }
268
611
  }
269
- function emitIncompleteToolCall(state, controller, toolCallStart) {
270
- if (!state.currentToolCallJson) {
612
+ function emitIncompleteToolCall(state, controller, toolCallStart, trailingBuffer, options) {
613
+ var _a;
614
+ if (!state.currentToolCallJson && trailingBuffer.length === 0) {
615
+ state.isInsideToolCall = false;
271
616
  return;
272
617
  }
618
+ if (state.currentToolCallJson) {
619
+ try {
620
+ const parsedToolCall = parse(state.currentToolCallJson);
621
+ emitToolCallFromParsed(state, controller, parsedToolCall);
622
+ state.currentToolCallJson = "";
623
+ state.isInsideToolCall = false;
624
+ return;
625
+ } catch (e) {
626
+ }
627
+ }
628
+ const rawToolCallContent = `${state.currentToolCallJson}${trailingBuffer}`;
629
+ const errorContent = `${toolCallStart}${rawToolCallContent}`;
630
+ const shouldEmitRawFallback = shouldEmitRawToolCallTextOnError(options);
273
631
  logParseFailure({
274
632
  phase: "stream",
275
- reason: "Incomplete streaming tool call segment emitted as text",
276
- snippet: `${toolCallStart}${state.currentToolCallJson}`
277
- });
278
- const errorId = generateId();
279
- const errorContent = `${toolCallStart}${state.currentToolCallJson}`;
280
- controller.enqueue({
281
- type: "text-start",
282
- id: errorId
283
- });
284
- controller.enqueue({
285
- type: "text-delta",
286
- id: errorId,
287
- delta: errorContent
288
- });
289
- controller.enqueue({
290
- type: "text-end",
291
- id: errorId
633
+ reason: shouldEmitRawFallback ? "Incomplete streaming tool call segment emitted as text" : "Incomplete streaming tool call segment suppressed without raw text fallback",
634
+ snippet: errorContent
292
635
  });
636
+ if (shouldEmitRawFallback) {
637
+ const errorId = generateId();
638
+ controller.enqueue({
639
+ type: "text-start",
640
+ id: errorId
641
+ });
642
+ controller.enqueue({
643
+ type: "text-delta",
644
+ id: errorId,
645
+ delta: errorContent
646
+ });
647
+ controller.enqueue({
648
+ type: "text-end",
649
+ id: errorId
650
+ });
651
+ }
652
+ closeToolInput(state, controller);
653
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
654
+ options,
655
+ shouldEmitRawFallback ? "Could not complete streaming JSON tool call at finish; emitting original text." : "Could not complete streaming JSON tool call at finish.",
656
+ { toolCall: errorContent }
657
+ );
293
658
  state.currentToolCallJson = "";
659
+ state.isInsideToolCall = false;
294
660
  }
295
- function handleFinishChunk(state, controller, toolCallStart, chunk) {
296
- if (state.buffer.length > 0) {
661
+ function handleFinishChunk(state, controller, toolCallStart, options, chunk) {
662
+ if (state.isInsideToolCall) {
663
+ const trailingBuffer = state.buffer;
664
+ state.buffer = "";
665
+ emitIncompleteToolCall(
666
+ state,
667
+ controller,
668
+ toolCallStart,
669
+ trailingBuffer,
670
+ options
671
+ );
672
+ } else if (state.buffer.length > 0) {
297
673
  flushBuffer(state, controller, toolCallStart);
298
674
  }
299
675
  closeTextBlock(state, controller);
300
- emitIncompleteToolCall(state, controller, toolCallStart);
301
676
  controller.enqueue(chunk);
302
677
  }
303
678
  function publishText(text, state, controller) {
304
679
  if (state.isInsideToolCall) {
305
680
  closeTextBlock(state, controller);
306
681
  state.currentToolCallJson += text;
682
+ emitToolInputProgress(state, controller);
307
683
  } else if (text.length > 0) {
308
684
  if (!state.currentTextId) {
309
685
  state.currentTextId = generateId();
@@ -321,42 +697,40 @@ function publishText(text, state, controller) {
321
697
  }
322
698
  }
323
699
  function emitToolCall(context) {
324
- var _a, _b;
700
+ var _a;
325
701
  const { state, controller, toolCallStart, toolCallEnd, options } = context;
326
702
  try {
327
703
  const parsedToolCall = parse(state.currentToolCallJson);
328
- closeTextBlock(state, controller);
329
- controller.enqueue({
330
- type: "tool-call",
331
- toolCallId: generateId(),
332
- toolName: parsedToolCall.name,
333
- input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
334
- });
704
+ emitToolCallFromParsed(state, controller, parsedToolCall);
335
705
  } catch (error) {
706
+ const errorContent = `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`;
707
+ const shouldEmitRawFallback = shouldEmitRawToolCallTextOnError(options);
336
708
  logParseFailure({
337
709
  phase: "stream",
338
710
  reason: "Failed to parse streaming tool call JSON segment",
339
- snippet: `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`,
711
+ snippet: errorContent,
340
712
  error
341
713
  });
342
- const errorId = generateId();
343
- const errorContent = `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`;
344
- controller.enqueue({
345
- type: "text-start",
346
- id: errorId
347
- });
348
- controller.enqueue({
349
- type: "text-delta",
350
- id: errorId,
351
- delta: errorContent
352
- });
353
- controller.enqueue({
354
- type: "text-end",
355
- id: errorId
356
- });
357
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
714
+ if (shouldEmitRawFallback) {
715
+ const errorId = generateId();
716
+ controller.enqueue({
717
+ type: "text-start",
718
+ id: errorId
719
+ });
720
+ controller.enqueue({
721
+ type: "text-delta",
722
+ id: errorId,
723
+ delta: errorContent
724
+ });
725
+ controller.enqueue({
726
+ type: "text-end",
727
+ id: errorId
728
+ });
729
+ }
730
+ closeToolInput(state, controller);
731
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
358
732
  options,
359
- "Could not process streaming JSON tool call; emitting original text.",
733
+ shouldEmitRawFallback ? "Could not process streaming JSON tool call; emitting original text." : "Could not process streaming JSON tool call.",
360
734
  {
361
735
  toolCall: errorContent
362
736
  }
@@ -372,6 +746,7 @@ function processTagMatch(context) {
372
746
  } else {
373
747
  state.currentToolCallJson = "";
374
748
  state.isInsideToolCall = true;
749
+ state.activeToolInput = null;
375
750
  }
376
751
  }
377
752
  function processBufferTags(context) {
@@ -394,8 +769,16 @@ function processBufferTags(context) {
394
769
  );
395
770
  }
396
771
  }
397
- function handlePartialTag(state, controller, toolCallStart) {
772
+ function handlePartialTag(state, controller, toolCallStart, toolCallEnd) {
398
773
  if (state.isInsideToolCall) {
774
+ const potentialEndIndex = getPotentialStartIndex(state.buffer, toolCallEnd);
775
+ if (potentialEndIndex != null && potentialEndIndex + toolCallEnd.length > state.buffer.length) {
776
+ publishText(state.buffer.slice(0, potentialEndIndex), state, controller);
777
+ state.buffer = state.buffer.slice(potentialEndIndex);
778
+ } else {
779
+ publishText(state.buffer, state, controller);
780
+ state.buffer = "";
781
+ }
399
782
  return;
400
783
  }
401
784
  const potentialIndex = getPotentialStartIndex(state.buffer, toolCallStart);
@@ -468,13 +851,14 @@ var jsonProtocol = ({
468
851
  buffer: "",
469
852
  currentToolCallJson: "",
470
853
  currentTextId: null,
471
- hasEmittedTextStart: false
854
+ hasEmittedTextStart: false,
855
+ activeToolInput: null
472
856
  };
473
857
  return new TransformStream({
474
858
  transform(chunk, controller) {
475
859
  var _a;
476
860
  if (chunk.type === "finish") {
477
- handleFinishChunk(state, controller, toolCallStart, chunk);
861
+ handleFinishChunk(state, controller, toolCallStart, options, chunk);
478
862
  return;
479
863
  }
480
864
  if (chunk.type !== "text-delta") {
@@ -490,7 +874,7 @@ var jsonProtocol = ({
490
874
  toolCallEnd,
491
875
  options
492
876
  });
493
- handlePartialTag(state, controller, toolCallStart);
877
+ handlePartialTag(state, controller, toolCallStart, toolCallEnd);
494
878
  }
495
879
  });
496
880
  },
@@ -520,6 +904,80 @@ function isTCMProtocolFactory(protocol) {
520
904
  var NAME_CHAR_RE = /[A-Za-z0-9_:-]/;
521
905
  var WHITESPACE_REGEX = /\s/;
522
906
 
907
+ // src/core/utils/streamed-tool-input-delta.ts
908
+ function emitDelta({
909
+ controller,
910
+ id,
911
+ state,
912
+ nextInput
913
+ }) {
914
+ if (!nextInput.startsWith(state.emittedInput)) {
915
+ return false;
916
+ }
917
+ const delta = nextInput.slice(state.emittedInput.length);
918
+ if (delta.length === 0) {
919
+ return false;
920
+ }
921
+ controller.enqueue({
922
+ type: "tool-input-delta",
923
+ id,
924
+ delta
925
+ });
926
+ state.emittedInput = nextInput;
927
+ return true;
928
+ }
929
+ function toIncompleteJsonPrefix(fullJson) {
930
+ const trimmed = fullJson.trim();
931
+ let prefix = trimmed;
932
+ while (prefix.endsWith("}") || prefix.endsWith("]")) {
933
+ prefix = prefix.slice(0, -1);
934
+ }
935
+ prefix = prefix.trimEnd();
936
+ if (prefix.endsWith('"')) {
937
+ prefix = prefix.slice(0, -1);
938
+ }
939
+ if (prefix.length === 0) {
940
+ if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
941
+ return trimmed.startsWith("{") ? "{" : "[";
942
+ }
943
+ if (trimmed.startsWith("]")) {
944
+ return "[";
945
+ }
946
+ if (trimmed.startsWith("}")) {
947
+ return "{";
948
+ }
949
+ if (trimmed.startsWith('"')) {
950
+ return '"';
951
+ }
952
+ return "{";
953
+ }
954
+ return prefix;
955
+ }
956
+ function emitPrefixDelta(params) {
957
+ return emitDelta({
958
+ ...params,
959
+ nextInput: params.candidate
960
+ });
961
+ }
962
+ function emitFinalRemainder(params) {
963
+ var _a;
964
+ const result = emitDelta({
965
+ ...params,
966
+ nextInput: params.finalFullJson
967
+ });
968
+ if (!result && params.state.emittedInput.length > 0) {
969
+ (_a = params.onMismatch) == null ? void 0 : _a.call(
970
+ params,
971
+ "Final JSON does not extend emitted tool-input prefix",
972
+ {
973
+ emittedLength: params.state.emittedInput.length,
974
+ finalLength: params.finalFullJson.length
975
+ }
976
+ );
977
+ }
978
+ return result;
979
+ }
980
+
523
981
  // src/core/utils/xml-root-repair.ts
524
982
  var XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX = /^<([A-Za-z_][A-Za-z0-9_-]*)\s*\r?\n([\s\S]+?)\r?\n\s*\/>\s*$/;
525
983
  function tryRepairXmlSelfClosingRootWithBody(rawText, toolNames) {
@@ -549,6 +1007,9 @@ function getToolSchema(tools, toolName) {
549
1007
  var _a;
550
1008
  return (_a = tools.find((t) => t.name === toolName)) == null ? void 0 : _a.inputSchema;
551
1009
  }
1010
+ function shouldEmitRawToolCallTextOnError2(options) {
1011
+ return (options == null ? void 0 : options.emitRawToolCallTextOnError) === true;
1012
+ }
552
1013
  function processToolCall(params) {
553
1014
  var _a, _b;
554
1015
  const { toolCall, tools, options, text, processedElements, parseOptions } = params;
@@ -561,7 +1022,7 @@ function processToolCall(params) {
561
1022
  const parsed = parse2(toolCall.content, toolSchema, parseConfig);
562
1023
  processedElements.push({
563
1024
  type: "tool-call",
564
- toolCallId: generateId(),
1025
+ toolCallId: generateToolCallId(),
565
1026
  toolName: toolCall.toolName,
566
1027
  input: JSON.stringify(parsed)
567
1028
  });
@@ -578,6 +1039,299 @@ function processToolCall(params) {
578
1039
  processedElements.push({ type: "text", text: originalCallText });
579
1040
  }
580
1041
  }
1042
+ function parseXmlTagName(rawTagBody) {
1043
+ let index = 0;
1044
+ while (index < rawTagBody.length && WHITESPACE_REGEX.test(rawTagBody[index])) {
1045
+ index += 1;
1046
+ }
1047
+ const nameStart = index;
1048
+ while (index < rawTagBody.length && NAME_CHAR_RE.test(rawTagBody.charAt(index))) {
1049
+ index += 1;
1050
+ }
1051
+ return rawTagBody.slice(nameStart, index);
1052
+ }
1053
+ function consumeXmlSpecialSection(fragment, ltIndex) {
1054
+ if (fragment.startsWith("<!--", ltIndex)) {
1055
+ const commentEnd = fragment.indexOf("-->", ltIndex + 4);
1056
+ return commentEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: commentEnd + 3 };
1057
+ }
1058
+ if (fragment.startsWith("<![CDATA[", ltIndex)) {
1059
+ const cdataEnd = fragment.indexOf("]]>", ltIndex + 9);
1060
+ return cdataEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: cdataEnd + 3 };
1061
+ }
1062
+ if (fragment.startsWith("<?", ltIndex)) {
1063
+ const processingEnd = fragment.indexOf("?>", ltIndex + 2);
1064
+ return processingEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: processingEnd + 2 };
1065
+ }
1066
+ if (fragment.startsWith("<!", ltIndex)) {
1067
+ const declarationEnd = fragment.indexOf(">", ltIndex + 2);
1068
+ return declarationEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: declarationEnd + 1 };
1069
+ }
1070
+ return { kind: "none" };
1071
+ }
1072
+ function parseXmlTagToken(fragment, ltIndex) {
1073
+ const gtIndex = fragment.indexOf(">", ltIndex + 1);
1074
+ if (gtIndex === -1) {
1075
+ return null;
1076
+ }
1077
+ const tagBody = fragment.slice(ltIndex + 1, gtIndex).trim();
1078
+ if (tagBody.length === 0) {
1079
+ return null;
1080
+ }
1081
+ if (tagBody.startsWith("/")) {
1082
+ const closeName = parseXmlTagName(tagBody.slice(1));
1083
+ if (closeName.length === 0) {
1084
+ return null;
1085
+ }
1086
+ return { kind: "close", name: closeName, nextPos: gtIndex + 1 };
1087
+ }
1088
+ const selfClosing = tagBody.endsWith("/");
1089
+ const openBody = selfClosing ? tagBody.slice(0, -1).trimEnd() : tagBody;
1090
+ const openName = parseXmlTagName(openBody);
1091
+ if (openName.length === 0) {
1092
+ return null;
1093
+ }
1094
+ return {
1095
+ kind: "open",
1096
+ name: openName,
1097
+ selfClosing,
1098
+ nextPos: gtIndex + 1
1099
+ };
1100
+ }
1101
+ function analyzeXmlFragmentForProgress(fragment) {
1102
+ const stack = [];
1103
+ const topLevelTagNames = [];
1104
+ let position = 0;
1105
+ while (position < fragment.length) {
1106
+ const ltIndex = fragment.indexOf("<", position);
1107
+ if (ltIndex === -1) {
1108
+ break;
1109
+ }
1110
+ const special = consumeXmlSpecialSection(fragment, ltIndex);
1111
+ if (special.kind === "incomplete") {
1112
+ return null;
1113
+ }
1114
+ if (special.kind === "consumed") {
1115
+ position = special.nextPos;
1116
+ continue;
1117
+ }
1118
+ const token = parseXmlTagToken(fragment, ltIndex);
1119
+ if (token === null) {
1120
+ return null;
1121
+ }
1122
+ if (token.kind === "close") {
1123
+ const openName = stack.pop();
1124
+ if (!openName || openName !== token.name) {
1125
+ return null;
1126
+ }
1127
+ position = token.nextPos;
1128
+ continue;
1129
+ }
1130
+ if (stack.length === 0) {
1131
+ topLevelTagNames.push(token.name);
1132
+ }
1133
+ if (!token.selfClosing) {
1134
+ stack.push(token.name);
1135
+ }
1136
+ position = token.nextPos;
1137
+ }
1138
+ if (stack.length > 0) {
1139
+ return null;
1140
+ }
1141
+ return { topLevelTagNames };
1142
+ }
1143
+ function scanXmlFragmentTopLevelTextStep(options) {
1144
+ const { fragment, position, stack } = options;
1145
+ const ltIndex = fragment.indexOf("<", position);
1146
+ if (ltIndex === -1) {
1147
+ const trailingText = fragment.slice(position);
1148
+ return {
1149
+ kind: "done",
1150
+ value: stack.length === 0 && trailingText.trim().length > 0
1151
+ };
1152
+ }
1153
+ const textBetweenTags = fragment.slice(position, ltIndex);
1154
+ if (stack.length === 0 && textBetweenTags.trim().length > 0) {
1155
+ return { kind: "found" };
1156
+ }
1157
+ const special = consumeXmlSpecialSection(fragment, ltIndex);
1158
+ if (special.kind === "incomplete") {
1159
+ return { kind: "invalid" };
1160
+ }
1161
+ if (special.kind === "consumed") {
1162
+ return { kind: "next", nextPos: special.nextPos };
1163
+ }
1164
+ const token = parseXmlTagToken(fragment, ltIndex);
1165
+ if (token === null) {
1166
+ return { kind: "invalid" };
1167
+ }
1168
+ if (token.kind === "close") {
1169
+ const openName = stack.pop();
1170
+ if (!openName || openName !== token.name) {
1171
+ return { kind: "invalid" };
1172
+ }
1173
+ } else if (!token.selfClosing) {
1174
+ stack.push(token.name);
1175
+ }
1176
+ return { kind: "next", nextPos: token.nextPos };
1177
+ }
1178
+ function hasNonWhitespaceTopLevelText(fragment) {
1179
+ if (!fragment.includes("<")) {
1180
+ return fragment.trim().length > 0;
1181
+ }
1182
+ const stack = [];
1183
+ let position = 0;
1184
+ while (position < fragment.length) {
1185
+ const step = scanXmlFragmentTopLevelTextStep({ fragment, position, stack });
1186
+ if (step.kind === "found") {
1187
+ return true;
1188
+ }
1189
+ if (step.kind === "invalid") {
1190
+ return false;
1191
+ }
1192
+ if (step.kind === "done") {
1193
+ return step.value;
1194
+ }
1195
+ position = step.nextPos;
1196
+ }
1197
+ return false;
1198
+ }
1199
+ function getObjectSchemaPropertyNames(schema) {
1200
+ if (!schema || typeof schema !== "object") {
1201
+ return null;
1202
+ }
1203
+ const schemaObject = schema;
1204
+ const typeValue = schemaObject.type;
1205
+ if (typeValue != null) {
1206
+ const isObjectType = typeValue === "object" || Array.isArray(typeValue) && typeValue.includes("object");
1207
+ if (!isObjectType) {
1208
+ return null;
1209
+ }
1210
+ }
1211
+ if (!schemaObject.properties || typeof schemaObject.properties !== "object") {
1212
+ return /* @__PURE__ */ new Set();
1213
+ }
1214
+ return new Set(
1215
+ Object.keys(schemaObject.properties)
1216
+ );
1217
+ }
1218
+ function schemaAllowsArrayType(schema) {
1219
+ if (!schema || typeof schema !== "object") {
1220
+ return false;
1221
+ }
1222
+ const schemaRecord = schema;
1223
+ const typeValue = schemaRecord.type;
1224
+ if (typeValue === "array") {
1225
+ return true;
1226
+ }
1227
+ if (Array.isArray(typeValue) && typeValue.includes("array")) {
1228
+ return true;
1229
+ }
1230
+ const unions = [schemaRecord.anyOf, schemaRecord.oneOf, schemaRecord.allOf];
1231
+ for (const union of unions) {
1232
+ if (!Array.isArray(union)) {
1233
+ continue;
1234
+ }
1235
+ if (union.some((entry) => schemaAllowsArrayType(entry))) {
1236
+ return true;
1237
+ }
1238
+ }
1239
+ return false;
1240
+ }
1241
+ function getSchemaObjectProperty(schema, propertyName) {
1242
+ if (!schema || typeof schema !== "object") {
1243
+ return null;
1244
+ }
1245
+ const schemaObject = schema;
1246
+ const properties = schemaObject.properties;
1247
+ if (!properties || typeof properties !== "object") {
1248
+ return null;
1249
+ }
1250
+ const property = properties[propertyName];
1251
+ if (!property) {
1252
+ return null;
1253
+ }
1254
+ return property;
1255
+ }
1256
+ function isStableXmlProgressCandidate(options) {
1257
+ const { candidate, parsed, toolSchema } = options;
1258
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1259
+ return false;
1260
+ }
1261
+ const structure = analyzeXmlFragmentForProgress(candidate);
1262
+ if (!structure) {
1263
+ return false;
1264
+ }
1265
+ const schemaProperties = getObjectSchemaPropertyNames(toolSchema);
1266
+ if (!schemaProperties || schemaProperties.size === 0) {
1267
+ return false;
1268
+ }
1269
+ const parsedObject = parsed;
1270
+ const uniqueTopLevelTags = new Set(structure.topLevelTagNames);
1271
+ for (const tagName of uniqueTopLevelTags) {
1272
+ if (!schemaProperties.has(tagName)) {
1273
+ continue;
1274
+ }
1275
+ const schemaProperty = getSchemaObjectProperty(toolSchema, tagName);
1276
+ if (schemaProperty && schemaAllowsArrayType(schemaProperty) && !Array.isArray(parsedObject[tagName])) {
1277
+ return false;
1278
+ }
1279
+ }
1280
+ if (structure.topLevelTagNames.length === 1) {
1281
+ const onlyTopLevelTag = structure.topLevelTagNames[0];
1282
+ if (!schemaProperties || schemaProperties.size === 0 || !schemaProperties.has(onlyTopLevelTag)) {
1283
+ return false;
1284
+ }
1285
+ }
1286
+ return true;
1287
+ }
1288
+ function parseXmlContentForStreamProgress({
1289
+ toolContent,
1290
+ toolSchema,
1291
+ parseOptions
1292
+ }) {
1293
+ const tryParse = (content) => {
1294
+ try {
1295
+ return parse2(content, toolSchema, {
1296
+ ...parseOptions != null ? parseOptions : {},
1297
+ repair: false,
1298
+ onError: void 0
1299
+ });
1300
+ } catch (e) {
1301
+ return null;
1302
+ }
1303
+ };
1304
+ const strictFull = tryParse(toolContent);
1305
+ if (strictFull !== null && isStableXmlProgressCandidate({
1306
+ candidate: toolContent,
1307
+ parsed: strictFull,
1308
+ toolSchema
1309
+ })) {
1310
+ return JSON.stringify(strictFull);
1311
+ }
1312
+ let searchEnd = toolContent.length;
1313
+ while (searchEnd > 0) {
1314
+ const gtIndex = toolContent.lastIndexOf(">", searchEnd - 1);
1315
+ if (gtIndex === -1) {
1316
+ break;
1317
+ }
1318
+ const candidate = toolContent.slice(0, gtIndex + 1);
1319
+ if (!analyzeXmlFragmentForProgress(candidate)) {
1320
+ searchEnd = gtIndex;
1321
+ continue;
1322
+ }
1323
+ const parsedCandidate = tryParse(candidate);
1324
+ if (parsedCandidate !== null && isStableXmlProgressCandidate({
1325
+ candidate,
1326
+ parsed: parsedCandidate,
1327
+ toolSchema
1328
+ })) {
1329
+ return JSON.stringify(parsedCandidate);
1330
+ }
1331
+ searchEnd = gtIndex;
1332
+ }
1333
+ return null;
1334
+ }
581
1335
  function handleStreamingToolCallEnd(params) {
582
1336
  var _a, _b;
583
1337
  const {
@@ -597,19 +1351,37 @@ function handleStreamingToolCallEnd(params) {
597
1351
  flushText(ctrl);
598
1352
  try {
599
1353
  const parsedResult = parse2(toolContent, toolSchema, parseConfig);
1354
+ const finalInput = JSON.stringify(parsedResult);
1355
+ emitFinalRemainder({
1356
+ controller: ctrl,
1357
+ id: currentToolCall.toolCallId,
1358
+ state: currentToolCall,
1359
+ finalFullJson: finalInput,
1360
+ onMismatch: options == null ? void 0 : options.onError
1361
+ });
1362
+ ctrl.enqueue({
1363
+ type: "tool-input-end",
1364
+ id: currentToolCall.toolCallId
1365
+ });
600
1366
  ctrl.enqueue({
601
1367
  type: "tool-call",
602
- toolCallId: generateId(),
1368
+ toolCallId: currentToolCall.toolCallId,
603
1369
  toolName: currentToolCall.name,
604
- input: JSON.stringify(parsedResult)
1370
+ input: finalInput
605
1371
  });
606
1372
  } catch (error) {
1373
+ ctrl.enqueue({
1374
+ type: "tool-input-end",
1375
+ id: currentToolCall.toolCallId
1376
+ });
607
1377
  const original = `<${currentToolCall.name}>${toolContent}</${currentToolCall.name}>`;
608
1378
  (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, "Could not process streaming XML tool call", {
609
1379
  toolCall: original,
610
1380
  error
611
1381
  });
612
- flushText(ctrl, original);
1382
+ if (shouldEmitRawToolCallTextOnError2(options)) {
1383
+ flushText(ctrl, original);
1384
+ }
613
1385
  }
614
1386
  }
615
1387
  function findClosingTagEndFlexible(text, contentStart, toolName) {
@@ -1031,38 +1803,6 @@ function findPotentialToolTagStart(buffer, toolNames) {
1031
1803
  }
1032
1804
  return -1;
1033
1805
  }
1034
- function createFlushTextHandler(getCurrentTextId, setCurrentTextId, getHasEmittedTextStart, setHasEmittedTextStart) {
1035
- return (controller, text) => {
1036
- const content = text;
1037
- if (content) {
1038
- if (!getCurrentTextId()) {
1039
- const newId = generateId();
1040
- setCurrentTextId(newId);
1041
- controller.enqueue({
1042
- type: "text-start",
1043
- id: newId
1044
- });
1045
- setHasEmittedTextStart(true);
1046
- }
1047
- controller.enqueue({
1048
- type: "text-delta",
1049
- id: getCurrentTextId(),
1050
- delta: content
1051
- });
1052
- }
1053
- const currentTextId = getCurrentTextId();
1054
- if (currentTextId && !text) {
1055
- if (getHasEmittedTextStart()) {
1056
- controller.enqueue({
1057
- type: "text-end",
1058
- id: currentTextId
1059
- });
1060
- setHasEmittedTextStart(false);
1061
- }
1062
- setCurrentTextId(null);
1063
- }
1064
- };
1065
- }
1066
1806
  function processToolCallInBuffer(params) {
1067
1807
  const {
1068
1808
  buffer,
@@ -1072,18 +1812,21 @@ function processToolCallInBuffer(params) {
1072
1812
  controller,
1073
1813
  flushText,
1074
1814
  setBuffer,
1075
- parseOptions
1815
+ parseOptions,
1816
+ emitToolInputProgress: emitToolInputProgress2
1076
1817
  } = params;
1077
1818
  const endTagPattern = new RegExp(
1078
1819
  `</\\s*${escapeRegExp(currentToolCall.name)}\\s*>`
1079
1820
  );
1080
1821
  const endMatch = endTagPattern.exec(buffer);
1081
1822
  if (!endMatch || endMatch.index === void 0) {
1823
+ emitToolInputProgress2(controller, currentToolCall, buffer);
1082
1824
  return { buffer, currentToolCall, shouldBreak: true };
1083
1825
  }
1084
1826
  const endIdx = endMatch.index;
1085
1827
  const endPos = endIdx + endMatch[0].length;
1086
1828
  const content = buffer.substring(0, endIdx);
1829
+ emitToolInputProgress2(controller, currentToolCall, content);
1087
1830
  const remainder = buffer.substring(endPos);
1088
1831
  setBuffer(remainder);
1089
1832
  handleStreamingToolCallEnd({
@@ -1110,7 +1853,8 @@ function processNoToolCallInBuffer(params) {
1110
1853
  tools,
1111
1854
  options,
1112
1855
  parseOptions,
1113
- setBuffer
1856
+ setBuffer,
1857
+ emitToolInputStart
1114
1858
  } = params;
1115
1859
  const {
1116
1860
  index: earliestStartTagIndex,
@@ -1140,9 +1884,10 @@ function processNoToolCallInBuffer(params) {
1140
1884
  if (selfClosing) {
1141
1885
  const newBuffer2 = buffer.substring(earliestStartTagIndex + tagLength);
1142
1886
  setBuffer(newBuffer2);
1887
+ const currentToolCall = emitToolInputStart(controller, earliestToolName);
1143
1888
  handleStreamingToolCallEnd({
1144
1889
  toolContent: "",
1145
- currentToolCall: { name: earliestToolName, content: "" },
1890
+ currentToolCall,
1146
1891
  tools,
1147
1892
  options,
1148
1893
  ctrl: controller,
@@ -1161,12 +1906,12 @@ function processNoToolCallInBuffer(params) {
1161
1906
  setBuffer(newBuffer);
1162
1907
  return {
1163
1908
  buffer: newBuffer,
1164
- currentToolCall: { name: earliestToolName, content: "" },
1909
+ currentToolCall: emitToolInputStart(controller, earliestToolName),
1165
1910
  shouldBreak: false,
1166
1911
  shouldContinue: true
1167
1912
  };
1168
1913
  }
1169
- function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, setCurrentToolCall, tools, options, toolNames, flushText, parseOptions) {
1914
+ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, setCurrentToolCall, tools, options, toolNames, flushText, parseOptions, emitToolInputProgress2, emitToolInputStart) {
1170
1915
  return (controller) => {
1171
1916
  while (true) {
1172
1917
  const currentToolCall = getCurrentToolCall();
@@ -1179,7 +1924,8 @@ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, se
1179
1924
  controller,
1180
1925
  flushText,
1181
1926
  setBuffer,
1182
- parseOptions
1927
+ parseOptions,
1928
+ emitToolInputProgress: emitToolInputProgress2
1183
1929
  });
1184
1930
  setBuffer(result.buffer);
1185
1931
  setCurrentToolCall(result.currentToolCall);
@@ -1195,7 +1941,8 @@ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, se
1195
1941
  tools,
1196
1942
  options,
1197
1943
  parseOptions,
1198
- setBuffer
1944
+ setBuffer,
1945
+ emitToolInputStart
1199
1946
  });
1200
1947
  setBuffer(result.buffer);
1201
1948
  setCurrentToolCall(result.currentToolCall);
@@ -1308,7 +2055,119 @@ var xmlProtocol = (protocolOptions) => {
1308
2055
  (value) => {
1309
2056
  hasEmittedTextStart = value;
1310
2057
  }
1311
- );
2058
+ );
2059
+ const emitToolInputStart = (controller, toolName) => {
2060
+ flushText(controller);
2061
+ const next = {
2062
+ name: toolName,
2063
+ toolCallId: generateToolCallId(),
2064
+ emittedInput: "",
2065
+ lastProgressGtIndex: null,
2066
+ lastProgressFullInput: null
2067
+ };
2068
+ controller.enqueue({
2069
+ type: "tool-input-start",
2070
+ id: next.toolCallId,
2071
+ toolName
2072
+ });
2073
+ return next;
2074
+ };
2075
+ const emitToolInputProgress2 = (controller, toolCall, toolContent) => {
2076
+ const progressGtIndex = toolContent.lastIndexOf(">");
2077
+ if (toolCall.lastProgressGtIndex === progressGtIndex) {
2078
+ const cached = toolCall.lastProgressFullInput;
2079
+ if (cached == null) {
2080
+ return;
2081
+ }
2082
+ if (cached === "{}" && toolContent.trim().length === 0) {
2083
+ return;
2084
+ }
2085
+ const prefixCandidate2 = toIncompleteJsonPrefix(cached);
2086
+ emitPrefixDelta({
2087
+ controller,
2088
+ id: toolCall.toolCallId,
2089
+ state: toolCall,
2090
+ candidate: prefixCandidate2
2091
+ });
2092
+ return;
2093
+ }
2094
+ const toolSchema = getToolSchema(tools, toolCall.name);
2095
+ const fullInput = parseXmlContentForStreamProgress({
2096
+ toolContent,
2097
+ toolSchema,
2098
+ parseOptions
2099
+ });
2100
+ toolCall.lastProgressGtIndex = progressGtIndex;
2101
+ toolCall.lastProgressFullInput = fullInput;
2102
+ if (fullInput == null) {
2103
+ return;
2104
+ }
2105
+ if (fullInput === "{}" && toolContent.trim().length === 0) {
2106
+ return;
2107
+ }
2108
+ const prefixCandidate = toIncompleteJsonPrefix(fullInput);
2109
+ emitPrefixDelta({
2110
+ controller,
2111
+ id: toolCall.toolCallId,
2112
+ state: toolCall,
2113
+ candidate: prefixCandidate
2114
+ });
2115
+ };
2116
+ const finalizeUnclosedToolCall = (controller) => {
2117
+ var _a2, _b;
2118
+ if (!currentToolCall) {
2119
+ return;
2120
+ }
2121
+ emitToolInputProgress2(controller, currentToolCall, buffer);
2122
+ const parseConfig = {
2123
+ ...parseOptions,
2124
+ onError: (_a2 = options == null ? void 0 : options.onError) != null ? _a2 : parseOptions == null ? void 0 : parseOptions.onError
2125
+ };
2126
+ const toolSchema = getToolSchema(tools, currentToolCall.name);
2127
+ flushText(controller);
2128
+ try {
2129
+ if (hasNonWhitespaceTopLevelText(buffer)) {
2130
+ throw new Error(
2131
+ "Cannot reconcile unclosed XML tool call with top-level plain text."
2132
+ );
2133
+ }
2134
+ const parsedResult = parse2(buffer, toolSchema, parseConfig);
2135
+ const finalInput = JSON.stringify(parsedResult);
2136
+ emitFinalRemainder({
2137
+ controller,
2138
+ id: currentToolCall.toolCallId,
2139
+ state: currentToolCall,
2140
+ finalFullJson: finalInput,
2141
+ onMismatch: options == null ? void 0 : options.onError
2142
+ });
2143
+ controller.enqueue({
2144
+ type: "tool-input-end",
2145
+ id: currentToolCall.toolCallId
2146
+ });
2147
+ controller.enqueue({
2148
+ type: "tool-call",
2149
+ toolCallId: currentToolCall.toolCallId,
2150
+ toolName: currentToolCall.name,
2151
+ input: finalInput
2152
+ });
2153
+ } catch (error) {
2154
+ controller.enqueue({
2155
+ type: "tool-input-end",
2156
+ id: currentToolCall.toolCallId
2157
+ });
2158
+ const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
2159
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
2160
+ options,
2161
+ "Could not complete streaming XML tool call at finish.",
2162
+ { toolCall: unfinishedContent, error }
2163
+ );
2164
+ if (shouldEmitRawToolCallTextOnError2(options)) {
2165
+ flushText(controller, unfinishedContent);
2166
+ }
2167
+ }
2168
+ buffer = "";
2169
+ currentToolCall = null;
2170
+ };
1312
2171
  const processBuffer = createProcessBufferHandler(
1313
2172
  () => buffer,
1314
2173
  (newBuffer) => {
@@ -1322,17 +2181,17 @@ var xmlProtocol = (protocolOptions) => {
1322
2181
  options,
1323
2182
  toolNames,
1324
2183
  flushText,
1325
- parseOptions
2184
+ parseOptions,
2185
+ emitToolInputProgress2,
2186
+ emitToolInputStart
1326
2187
  );
1327
2188
  return new TransformStream({
2189
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Stateful stream parsing requires branching over chunk lifecycle and parser states.
1328
2190
  transform(chunk, controller) {
1329
2191
  var _a2;
1330
2192
  if (chunk.type === "finish") {
1331
2193
  if (currentToolCall) {
1332
- const unfinishedContent = `<${currentToolCall.name}>${currentToolCall.content}${buffer}`;
1333
- flushText(controller, unfinishedContent);
1334
- buffer = "";
1335
- currentToolCall = null;
2194
+ finalizeUnclosedToolCall(controller);
1336
2195
  } else if (buffer) {
1337
2196
  flushText(controller, buffer);
1338
2197
  buffer = "";
@@ -1342,7 +2201,8 @@ var xmlProtocol = (protocolOptions) => {
1342
2201
  return;
1343
2202
  }
1344
2203
  if (chunk.type !== "text-delta") {
1345
- if (buffer) {
2204
+ if (currentToolCall) {
2205
+ } else if (buffer) {
1346
2206
  flushText(controller, buffer);
1347
2207
  buffer = "";
1348
2208
  }
@@ -1355,10 +2215,7 @@ var xmlProtocol = (protocolOptions) => {
1355
2215
  },
1356
2216
  flush(controller) {
1357
2217
  if (currentToolCall) {
1358
- const unfinishedContent = `<${currentToolCall.name}>${currentToolCall.content || ""}${buffer}`;
1359
- flushText(controller, unfinishedContent);
1360
- buffer = "";
1361
- currentToolCall = null;
2218
+ finalizeUnclosedToolCall(controller);
1362
2219
  } else if (buffer) {
1363
2220
  flushText(controller, buffer);
1364
2221
  buffer = "";
@@ -1386,7 +2243,205 @@ var xmlProtocol = (protocolOptions) => {
1386
2243
 
1387
2244
  // src/core/protocols/yaml-protocol.ts
1388
2245
  import YAML from "yaml";
2246
+ function shouldEmitRawToolCallTextOnError3(options) {
2247
+ return (options == null ? void 0 : options.emitRawToolCallTextOnError) === true;
2248
+ }
2249
+ var selfClosingTagCache2 = /* @__PURE__ */ new Map();
2250
+ function getSelfClosingTagPattern2(toolName) {
2251
+ let pattern = selfClosingTagCache2.get(toolName);
2252
+ if (!pattern) {
2253
+ pattern = new RegExp(`<\\s*${escapeRegExp(toolName)}\\s*/>`, "g");
2254
+ selfClosingTagCache2.set(toolName, pattern);
2255
+ }
2256
+ return pattern;
2257
+ }
1389
2258
  var LEADING_WHITESPACE_RE = /^(\s*)/;
2259
+ var INCOMPLETE_MAPPING_TAIL_RE = /^[^:[\]{}-][^:]*:\s*$/;
2260
+ var INCOMPLETE_SEQUENCE_TAIL_RE = /^-\s*$/;
2261
+ var BLOCK_SCALAR_KEY_RE = /:\s*[|>][-+0-9]*\s*$/;
2262
+ var PLAIN_MAPPING_VALUE_RE = /^[^:[\]{}-][^:]*:\s*(.+)$/;
2263
+ var PLAIN_SEQUENCE_VALUE_RE = /^-\s+(.+)$/;
2264
+ function normalizeYamlContent(yamlContent) {
2265
+ let normalized = yamlContent;
2266
+ if (normalized.startsWith("\n")) {
2267
+ normalized = normalized.slice(1);
2268
+ }
2269
+ const lines = normalized.split("\n");
2270
+ const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
2271
+ if (nonEmptyLines.length === 0) {
2272
+ return { normalized: "", nonEmptyLines };
2273
+ }
2274
+ const minIndent = Math.min(
2275
+ ...nonEmptyLines.map((line) => {
2276
+ const match = line.match(LEADING_WHITESPACE_RE);
2277
+ return match ? match[1].length : 0;
2278
+ })
2279
+ );
2280
+ if (minIndent > 0) {
2281
+ normalized = lines.map((line) => line.slice(minIndent)).join("\n");
2282
+ }
2283
+ return { normalized, nonEmptyLines };
2284
+ }
2285
+ function parseYamlDocumentAsMapping(normalized) {
2286
+ try {
2287
+ const doc = YAML.parseDocument(normalized);
2288
+ const errors = doc.errors.map((e) => e.message);
2289
+ const result = doc.toJSON();
2290
+ if (result === null) {
2291
+ return { value: {}, errors };
2292
+ }
2293
+ if (typeof result !== "object" || Array.isArray(result)) {
2294
+ return { value: null, errors };
2295
+ }
2296
+ return { value: result, errors };
2297
+ } catch (error) {
2298
+ return {
2299
+ value: null,
2300
+ errors: [
2301
+ error instanceof Error ? error.message : "Unknown YAML parsing error"
2302
+ ]
2303
+ };
2304
+ }
2305
+ }
2306
+ function getLastMeaningfulLineInfo(input) {
2307
+ var _a;
2308
+ const lines = input.split("\n");
2309
+ let index = lines.length - 1;
2310
+ while (index >= 0) {
2311
+ const raw = (_a = lines[index]) != null ? _a : "";
2312
+ const trimmed = raw.trim();
2313
+ if (trimmed.length > 0 && !trimmed.startsWith("#")) {
2314
+ return {
2315
+ index,
2316
+ raw,
2317
+ trimmed,
2318
+ indent: raw.length - raw.trimStart().length
2319
+ };
2320
+ }
2321
+ index -= 1;
2322
+ }
2323
+ return null;
2324
+ }
2325
+ function dropLastMeaningfulLine(input) {
2326
+ const lineInfo = getLastMeaningfulLineInfo(input);
2327
+ if (!lineInfo) {
2328
+ return null;
2329
+ }
2330
+ return input.split("\n").slice(0, lineInfo.index).join("\n").trimEnd();
2331
+ }
2332
+ function hasIncompleteMappingTail(normalized) {
2333
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
2334
+ if (!lineInfo) {
2335
+ return false;
2336
+ }
2337
+ return INCOMPLETE_MAPPING_TAIL_RE.test(lineInfo.trimmed);
2338
+ }
2339
+ function hasIncompleteSequenceTail(normalized) {
2340
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
2341
+ if (!lineInfo) {
2342
+ return false;
2343
+ }
2344
+ return INCOMPLETE_SEQUENCE_TAIL_RE.test(lineInfo.trimmed);
2345
+ }
2346
+ function hasSplitNestedKeyTail(normalized) {
2347
+ var _a;
2348
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
2349
+ if (!lineInfo) {
2350
+ return false;
2351
+ }
2352
+ const { trimmed, indent, index } = lineInfo;
2353
+ if (indent === 0) {
2354
+ return false;
2355
+ }
2356
+ if (trimmed.startsWith("#") || trimmed.startsWith("-") || trimmed.includes(":")) {
2357
+ return false;
2358
+ }
2359
+ const lines = normalized.split("\n");
2360
+ let parentIndex = index - 1;
2361
+ while (parentIndex >= 0) {
2362
+ const parentRaw = (_a = lines[parentIndex]) != null ? _a : "";
2363
+ const parentTrimmed = parentRaw.trim();
2364
+ if (parentTrimmed.length === 0 || parentTrimmed.startsWith("#")) {
2365
+ parentIndex -= 1;
2366
+ continue;
2367
+ }
2368
+ const parentIndent = parentRaw.length - parentRaw.trimStart().length;
2369
+ if (parentIndent >= indent) {
2370
+ parentIndex -= 1;
2371
+ continue;
2372
+ }
2373
+ if (!parentTrimmed.endsWith(":")) {
2374
+ return false;
2375
+ }
2376
+ if (BLOCK_SCALAR_KEY_RE.test(parentTrimmed)) {
2377
+ return false;
2378
+ }
2379
+ return true;
2380
+ }
2381
+ return false;
2382
+ }
2383
+ function extractTrailingPlainScalarValue(line) {
2384
+ var _a;
2385
+ if (BLOCK_SCALAR_KEY_RE.test(line)) {
2386
+ return null;
2387
+ }
2388
+ const mappingMatch = line.match(PLAIN_MAPPING_VALUE_RE);
2389
+ const sequenceMatch = line.match(PLAIN_SEQUENCE_VALUE_RE);
2390
+ const value = (_a = mappingMatch == null ? void 0 : mappingMatch[1]) != null ? _a : sequenceMatch == null ? void 0 : sequenceMatch[1];
2391
+ if (!value) {
2392
+ return null;
2393
+ }
2394
+ const trimmedValue = value.trim();
2395
+ if (trimmedValue.length === 0) {
2396
+ return null;
2397
+ }
2398
+ if (trimmedValue.startsWith('"') || trimmedValue.startsWith("'")) {
2399
+ return null;
2400
+ }
2401
+ if (trimmedValue.startsWith("{") || trimmedValue.startsWith("[") || trimmedValue.startsWith("|") || trimmedValue.startsWith(">")) {
2402
+ return null;
2403
+ }
2404
+ return trimmedValue;
2405
+ }
2406
+ function hasUnterminatedPlainScalarTail(normalized) {
2407
+ if (normalized.endsWith("\n")) {
2408
+ return false;
2409
+ }
2410
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
2411
+ if (!lineInfo) {
2412
+ return false;
2413
+ }
2414
+ return extractTrailingPlainScalarValue(lineInfo.trimmed) != null;
2415
+ }
2416
+ function hasUnstableProgressTail(normalized) {
2417
+ return hasIncompleteMappingTail(normalized) || hasIncompleteSequenceTail(normalized) || hasSplitNestedKeyTail(normalized) || hasUnterminatedPlainScalarTail(normalized);
2418
+ }
2419
+ function trimTrailingNewlineInUnknown(value) {
2420
+ if (typeof value === "string") {
2421
+ if (value.endsWith("\n")) {
2422
+ return value.slice(0, -1);
2423
+ }
2424
+ return value;
2425
+ }
2426
+ if (Array.isArray(value)) {
2427
+ return value.map((item) => trimTrailingNewlineInUnknown(item));
2428
+ }
2429
+ if (value && typeof value === "object") {
2430
+ return Object.fromEntries(
2431
+ Object.entries(value).map(([key, item]) => [
2432
+ key,
2433
+ trimTrailingNewlineInUnknown(item)
2434
+ ])
2435
+ );
2436
+ }
2437
+ return value;
2438
+ }
2439
+ function stabilizeParsedValueForStreamProgress(value, source) {
2440
+ if (source.endsWith("\n")) {
2441
+ return value;
2442
+ }
2443
+ return trimTrailingNewlineInUnknown(value);
2444
+ }
1390
2445
  function findClosingTagEnd(text, contentStart, toolName) {
1391
2446
  let pos = contentStart;
1392
2447
  let depth = 1;
@@ -1458,7 +2513,7 @@ function findEarliestTagPosition(openIdx, selfIdx) {
1458
2513
  function collectToolCallsForName(text, toolName) {
1459
2514
  const toolCalls = [];
1460
2515
  let searchIndex = 0;
1461
- const selfTagRegex = new RegExp(`<${toolName}\\s*/>`, "g");
2516
+ const selfTagRegex = getSelfClosingTagPattern2(toolName);
1462
2517
  while (searchIndex < text.length) {
1463
2518
  const startTag = `<${toolName}>`;
1464
2519
  const openIdx = text.indexOf(startTag, searchIndex);
@@ -1510,47 +2565,48 @@ function findToolCalls2(text, toolNames) {
1510
2565
  return toolCalls.sort((a, b) => a.startIndex - b.startIndex);
1511
2566
  }
1512
2567
  function parseYamlContent(yamlContent, options) {
1513
- var _a, _b, _c;
1514
- let normalized = yamlContent;
1515
- if (normalized.startsWith("\n")) {
1516
- normalized = normalized.slice(1);
1517
- }
1518
- const lines = normalized.split("\n");
1519
- const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
2568
+ var _a, _b;
2569
+ const { normalized, nonEmptyLines } = normalizeYamlContent(yamlContent);
1520
2570
  if (nonEmptyLines.length === 0) {
1521
2571
  return {};
1522
2572
  }
1523
- const minIndent = Math.min(
1524
- ...nonEmptyLines.map((line) => {
1525
- const match = line.match(LEADING_WHITESPACE_RE);
1526
- return match ? match[1].length : 0;
1527
- })
1528
- );
1529
- if (minIndent > 0) {
1530
- normalized = lines.map((line) => line.slice(minIndent)).join("\n");
2573
+ const parsed = parseYamlDocumentAsMapping(normalized);
2574
+ if (parsed.errors.length > 0) {
2575
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "YAML parse error", {
2576
+ errors: parsed.errors
2577
+ });
2578
+ return null;
1531
2579
  }
1532
- try {
1533
- const doc = YAML.parseDocument(normalized);
1534
- if (doc.errors && doc.errors.length > 0) {
1535
- (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "YAML parse error", {
1536
- errors: doc.errors.map((e) => e.message)
1537
- });
1538
- return null;
2580
+ if (parsed.value === null) {
2581
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, "YAML content must be a key-value mapping", {
2582
+ got: "non-mapping"
2583
+ });
2584
+ return null;
2585
+ }
2586
+ return parsed.value;
2587
+ }
2588
+ function parseYamlContentForStreamProgress(yamlContent) {
2589
+ const { normalized, nonEmptyLines } = normalizeYamlContent(yamlContent);
2590
+ if (nonEmptyLines.length === 0) {
2591
+ return {};
2592
+ }
2593
+ let candidate = normalized;
2594
+ while (true) {
2595
+ const parsed = parseYamlDocumentAsMapping(candidate);
2596
+ if (parsed.errors.length === 0 && !hasUnstableProgressTail(candidate)) {
2597
+ if (candidate.trim().length === 0 && normalized.trim().length > 0) {
2598
+ return null;
2599
+ }
2600
+ return stabilizeParsedValueForStreamProgress(parsed.value, candidate);
1539
2601
  }
1540
- const result = doc.toJSON();
1541
- if (result === null) {
1542
- return {};
2602
+ const truncated = dropLastMeaningfulLine(candidate);
2603
+ if (truncated == null) {
2604
+ return null;
1543
2605
  }
1544
- if (typeof result !== "object" || Array.isArray(result)) {
1545
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, "YAML content must be a key-value mapping", {
1546
- got: typeof result
1547
- });
2606
+ if (truncated === candidate) {
1548
2607
  return null;
1549
2608
  }
1550
- return result;
1551
- } catch (error) {
1552
- (_c = options == null ? void 0 : options.onError) == null ? void 0 : _c.call(options, "Failed to parse YAML content", { error });
1553
- return null;
2609
+ candidate = truncated;
1554
2610
  }
1555
2611
  }
1556
2612
  function processToolCallMatch(text, tc, currentIndex, processedElements, options) {
@@ -1566,7 +2622,7 @@ function processToolCallMatch(text, tc, currentIndex, processedElements, options
1566
2622
  if (parsedArgs !== null) {
1567
2623
  processedElements.push({
1568
2624
  type: "tool-call",
1569
- toolCallId: generateId(),
2625
+ toolCallId: generateToolCallId(),
1570
2626
  toolName: tc.toolName,
1571
2627
  input: JSON.stringify(parsedArgs)
1572
2628
  });
@@ -1579,38 +2635,6 @@ function processToolCallMatch(text, tc, currentIndex, processedElements, options
1579
2635
  }
1580
2636
  return tc.endIndex;
1581
2637
  }
1582
- function createFlushTextHandler2(getCurrentTextId, setCurrentTextId, getHasEmittedTextStart, setHasEmittedTextStart) {
1583
- return (controller, text) => {
1584
- const content = text;
1585
- if (content) {
1586
- if (!getCurrentTextId()) {
1587
- const newId = generateId();
1588
- setCurrentTextId(newId);
1589
- controller.enqueue({
1590
- type: "text-start",
1591
- id: newId
1592
- });
1593
- setHasEmittedTextStart(true);
1594
- }
1595
- controller.enqueue({
1596
- type: "text-delta",
1597
- id: getCurrentTextId(),
1598
- delta: content
1599
- });
1600
- }
1601
- const currentTextId = getCurrentTextId();
1602
- if (currentTextId && !text) {
1603
- if (getHasEmittedTextStart()) {
1604
- controller.enqueue({
1605
- type: "text-end",
1606
- id: currentTextId
1607
- });
1608
- setHasEmittedTextStart(false);
1609
- }
1610
- setCurrentTextId(null);
1611
- }
1612
- };
1613
- }
1614
2638
  function findEarliestToolTag2(buffer, toolNames) {
1615
2639
  let bestIndex = -1;
1616
2640
  let bestName = "";
@@ -1618,8 +2642,9 @@ function findEarliestToolTag2(buffer, toolNames) {
1618
2642
  let bestTagLength = 0;
1619
2643
  for (const name of toolNames) {
1620
2644
  const openTag = `<${name}>`;
1621
- const selfTagRegex = new RegExp(`<${name}\\s*/>`);
2645
+ const selfTagRegex = getSelfClosingTagPattern2(name);
1622
2646
  const idxOpen = buffer.indexOf(openTag);
2647
+ selfTagRegex.lastIndex = 0;
1623
2648
  const selfMatch = selfTagRegex.exec(buffer);
1624
2649
  const idxSelf = selfMatch ? selfMatch.index : -1;
1625
2650
  if (idxOpen !== -1 && (bestIndex === -1 || idxOpen < bestIndex)) {
@@ -1642,6 +2667,29 @@ function findEarliestToolTag2(buffer, toolNames) {
1642
2667
  tagLength: bestTagLength
1643
2668
  };
1644
2669
  }
2670
+ function stripTrailingPartialCloseTag(content, toolName) {
2671
+ const closeTag = `</${toolName}>`;
2672
+ const lastLineBreakIndex = Math.max(
2673
+ content.lastIndexOf("\n"),
2674
+ content.lastIndexOf("\r")
2675
+ );
2676
+ const lineStartIndex = lastLineBreakIndex === -1 ? 0 : lastLineBreakIndex + 1;
2677
+ const trailingLine = content.slice(lineStartIndex);
2678
+ const trimmedTrailingLine = trailingLine.trim();
2679
+ if (trimmedTrailingLine.length === 0 || !trimmedTrailingLine.startsWith("</") || trimmedTrailingLine === closeTag || !closeTag.startsWith(trimmedTrailingLine)) {
2680
+ return content;
2681
+ }
2682
+ const leadingWhitespaceLength = trailingLine.length - trailingLine.trimStart().length;
2683
+ const preservedLeadingWhitespace = trailingLine.slice(
2684
+ 0,
2685
+ leadingWhitespaceLength
2686
+ );
2687
+ const contentWithoutPartial = `${content.slice(
2688
+ 0,
2689
+ lineStartIndex
2690
+ )}${preservedLeadingWhitespace}`;
2691
+ return contentWithoutPartial.trimEnd();
2692
+ }
1645
2693
  var yamlProtocol = (_protocolOptions) => {
1646
2694
  return {
1647
2695
  formatTools({ tools, toolSystemPromptTemplate }) {
@@ -1702,7 +2750,7 @@ ${yamlContent}</${toolCall.toolName}>`;
1702
2750
  let currentToolCall = null;
1703
2751
  let currentTextId = null;
1704
2752
  let hasEmittedTextStart = false;
1705
- const flushText = createFlushTextHandler2(
2753
+ const flushText = createFlushTextHandler(
1706
2754
  () => currentTextId,
1707
2755
  (newId) => {
1708
2756
  currentTextId = newId;
@@ -1712,33 +2760,128 @@ ${yamlContent}</${toolCall.toolName}>`;
1712
2760
  hasEmittedTextStart = value;
1713
2761
  }
1714
2762
  );
1715
- const processToolCallEnd = (controller, toolContent, toolName) => {
2763
+ const emitToolInputProgress2 = (controller, toolContent) => {
2764
+ if (!currentToolCall) {
2765
+ return;
2766
+ }
2767
+ const parsedArgs = parseYamlContentForStreamProgress(toolContent);
2768
+ if (parsedArgs === null) {
2769
+ return;
2770
+ }
2771
+ const fullInput = JSON.stringify(parsedArgs);
2772
+ if (fullInput === "{}" && toolContent.trim().length === 0) {
2773
+ return;
2774
+ }
2775
+ const prefixCandidate = toIncompleteJsonPrefix(fullInput);
2776
+ emitPrefixDelta({
2777
+ controller,
2778
+ id: currentToolCall.toolCallId,
2779
+ state: currentToolCall,
2780
+ candidate: prefixCandidate
2781
+ });
2782
+ };
2783
+ const processToolCallEnd = (controller, toolContent, toolName, toolCallId) => {
1716
2784
  var _a;
1717
2785
  const parsedArgs = parseYamlContent(toolContent, options);
1718
2786
  flushText(controller);
1719
2787
  if (parsedArgs !== null) {
2788
+ const finalInput = JSON.stringify(parsedArgs);
2789
+ if (currentToolCall && currentToolCall.toolCallId === toolCallId) {
2790
+ emitFinalRemainder({
2791
+ controller,
2792
+ id: toolCallId,
2793
+ state: currentToolCall,
2794
+ finalFullJson: finalInput,
2795
+ onMismatch: options == null ? void 0 : options.onError
2796
+ });
2797
+ }
2798
+ controller.enqueue({
2799
+ type: "tool-input-end",
2800
+ id: toolCallId
2801
+ });
1720
2802
  controller.enqueue({
1721
2803
  type: "tool-call",
1722
- toolCallId: generateId(),
2804
+ toolCallId,
1723
2805
  toolName,
1724
- input: JSON.stringify(parsedArgs)
2806
+ input: finalInput
1725
2807
  });
1726
2808
  } else {
2809
+ controller.enqueue({
2810
+ type: "tool-input-end",
2811
+ id: toolCallId
2812
+ });
1727
2813
  const original = `<${toolName}>${toolContent}</${toolName}>`;
1728
2814
  (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "Could not parse streaming YAML tool call", {
1729
2815
  toolCall: original
1730
2816
  });
1731
- flushText(controller, original);
2817
+ if (shouldEmitRawToolCallTextOnError3(options)) {
2818
+ flushText(controller, original);
2819
+ }
2820
+ }
2821
+ };
2822
+ const finalizeUnclosedToolCall = (controller) => {
2823
+ var _a;
2824
+ if (!currentToolCall) {
2825
+ return;
2826
+ }
2827
+ emitToolInputProgress2(controller, buffer);
2828
+ const { name: toolName, toolCallId } = currentToolCall;
2829
+ const reconciledBuffer = stripTrailingPartialCloseTag(buffer, toolName);
2830
+ const parsedArgs = parseYamlContent(reconciledBuffer, options);
2831
+ flushText(controller);
2832
+ if (parsedArgs !== null) {
2833
+ const finalInput = JSON.stringify(parsedArgs);
2834
+ emitFinalRemainder({
2835
+ controller,
2836
+ id: toolCallId,
2837
+ state: currentToolCall,
2838
+ finalFullJson: finalInput,
2839
+ onMismatch: options == null ? void 0 : options.onError
2840
+ });
2841
+ controller.enqueue({
2842
+ type: "tool-input-end",
2843
+ id: toolCallId
2844
+ });
2845
+ controller.enqueue({
2846
+ type: "tool-call",
2847
+ toolCallId,
2848
+ toolName,
2849
+ input: finalInput
2850
+ });
2851
+ } else {
2852
+ controller.enqueue({
2853
+ type: "tool-input-end",
2854
+ id: toolCallId
2855
+ });
2856
+ const unfinishedContent = `<${toolName}>${buffer}`;
2857
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
2858
+ options,
2859
+ "Could not complete streaming YAML tool call at finish.",
2860
+ { toolCall: unfinishedContent }
2861
+ );
2862
+ if (shouldEmitRawToolCallTextOnError3(options)) {
2863
+ flushText(controller, unfinishedContent);
2864
+ }
1732
2865
  }
2866
+ buffer = "";
2867
+ currentToolCall = null;
1733
2868
  };
1734
2869
  const handlePendingToolCall = (controller, endTag, toolName) => {
2870
+ var _a;
1735
2871
  const endIdx = buffer.indexOf(endTag);
1736
2872
  if (endIdx === -1) {
2873
+ emitToolInputProgress2(controller, buffer);
1737
2874
  return false;
1738
2875
  }
1739
2876
  const content = buffer.substring(0, endIdx);
2877
+ emitToolInputProgress2(controller, content);
1740
2878
  buffer = buffer.substring(endIdx + endTag.length);
1741
- processToolCallEnd(controller, content, toolName);
2879
+ processToolCallEnd(
2880
+ controller,
2881
+ content,
2882
+ toolName,
2883
+ (_a = currentToolCall == null ? void 0 : currentToolCall.toolCallId) != null ? _a : generateToolCallId()
2884
+ );
1742
2885
  currentToolCall = null;
1743
2886
  return true;
1744
2887
  };
@@ -1755,13 +2898,35 @@ ${yamlContent}</${toolCall.toolName}>`;
1755
2898
  if (tagIndex > 0) {
1756
2899
  flushText(controller, buffer.substring(0, tagIndex));
1757
2900
  }
2901
+ flushText(controller);
1758
2902
  if (selfClosing) {
1759
2903
  buffer = buffer.substring(tagIndex + tagLength);
1760
- processToolCallEnd(controller, "", tagName);
2904
+ const toolCallId = generateToolCallId();
2905
+ currentToolCall = {
2906
+ name: tagName,
2907
+ toolCallId,
2908
+ emittedInput: ""
2909
+ };
2910
+ controller.enqueue({
2911
+ type: "tool-input-start",
2912
+ id: toolCallId,
2913
+ toolName: tagName
2914
+ });
2915
+ processToolCallEnd(controller, "", tagName, toolCallId);
2916
+ currentToolCall = null;
1761
2917
  } else {
1762
2918
  const startTag = `<${tagName}>`;
1763
2919
  buffer = buffer.substring(tagIndex + startTag.length);
1764
- currentToolCall = { name: tagName, content: "" };
2920
+ currentToolCall = {
2921
+ name: tagName,
2922
+ toolCallId: generateToolCallId(),
2923
+ emittedInput: ""
2924
+ };
2925
+ controller.enqueue({
2926
+ type: "tool-input-start",
2927
+ id: currentToolCall.toolCallId,
2928
+ toolName: tagName
2929
+ });
1765
2930
  }
1766
2931
  };
1767
2932
  const processBuffer = (controller) => {
@@ -1790,10 +2955,7 @@ ${yamlContent}</${toolCall.toolName}>`;
1790
2955
  var _a;
1791
2956
  if (chunk.type === "finish") {
1792
2957
  if (currentToolCall) {
1793
- const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
1794
- flushText(controller, unfinishedContent);
1795
- buffer = "";
1796
- currentToolCall = null;
2958
+ finalizeUnclosedToolCall(controller);
1797
2959
  } else if (buffer) {
1798
2960
  flushText(controller, buffer);
1799
2961
  buffer = "";
@@ -1803,7 +2965,7 @@ ${yamlContent}</${toolCall.toolName}>`;
1803
2965
  return;
1804
2966
  }
1805
2967
  if (chunk.type !== "text-delta") {
1806
- if (buffer) {
2968
+ if (!currentToolCall && buffer) {
1807
2969
  flushText(controller, buffer);
1808
2970
  buffer = "";
1809
2971
  }
@@ -1816,10 +2978,7 @@ ${yamlContent}</${toolCall.toolName}>`;
1816
2978
  },
1817
2979
  flush(controller) {
1818
2980
  if (currentToolCall) {
1819
- const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
1820
- flushText(controller, unfinishedContent);
1821
- buffer = "";
1822
- currentToolCall = null;
2981
+ finalizeUnclosedToolCall(controller);
1823
2982
  } else if (buffer) {
1824
2983
  flushText(controller, buffer);
1825
2984
  buffer = "";
@@ -1997,9 +3156,6 @@ function hasInputProperty(obj) {
1997
3156
  return typeof obj === "object" && obj !== null && "input" in obj;
1998
3157
  }
1999
3158
 
2000
- // src/generate-handler.ts
2001
- import { generateId as generateId2 } from "@ai-sdk/provider-utils";
2002
-
2003
3159
  // src/core/utils/generated-text-json-recovery.ts
2004
3160
  function isRecord(value) {
2005
3161
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -2138,7 +3294,7 @@ function mergeJsonCandidatesByStart(tagged, codeBlocks, balanced) {
2138
3294
  function toToolCallPart(candidate) {
2139
3295
  return {
2140
3296
  type: "tool-call",
2141
- toolCallId: generateId(),
3297
+ toolCallId: generateToolCallId(),
2142
3298
  toolName: candidate.toolName,
2143
3299
  input: candidate.input
2144
3300
  };
@@ -2365,7 +3521,7 @@ async function handleToolChoice(doGenerate, params, tools) {
2365
3521
  }
2366
3522
  const toolCall = {
2367
3523
  type: "tool-call",
2368
- toolCallId: generateId2(),
3524
+ toolCallId: generateToolCallId(),
2369
3525
  toolName,
2370
3526
  input
2371
3527
  };
@@ -2875,7 +4031,6 @@ unit: celsius
2875
4031
  }
2876
4032
 
2877
4033
  // src/stream-handler.ts
2878
- import { generateId as generateId3 } from "@ai-sdk/provider-utils";
2879
4034
  async function wrapStream({
2880
4035
  protocol,
2881
4036
  doStream,
@@ -2913,10 +4068,22 @@ async function wrapStream({
2913
4068
  }
2914
4069
  )
2915
4070
  ).pipeThrough(protocol.createStreamParser({ tools, options }));
4071
+ let seenToolCall = false;
2916
4072
  const v3Stream = coreStream.pipeThrough(
2917
4073
  new TransformStream({
2918
4074
  transform(part, controller) {
2919
- const normalizedPart = part.type === "tool-call" ? coerceToolCallPart(part, tools) : part;
4075
+ let normalizedPart = part.type === "tool-call" ? coerceToolCallPart(part, tools) : part;
4076
+ if (normalizedPart.type === "tool-call") {
4077
+ seenToolCall = true;
4078
+ }
4079
+ if (normalizedPart.type === "finish" && seenToolCall && normalizedPart.finishReason.unified === "stop") {
4080
+ normalizedPart = {
4081
+ ...normalizedPart,
4082
+ finishReason: normalizeToolCallsFinishReason(
4083
+ normalizedPart.finishReason
4084
+ )
4085
+ };
4086
+ }
2920
4087
  if (debugLevel === "stream") {
2921
4088
  logParsedChunk(normalizedPart);
2922
4089
  }
@@ -2953,7 +4120,7 @@ async function toolChoiceStream({
2953
4120
  start(controller) {
2954
4121
  controller.enqueue({
2955
4122
  type: "tool-call",
2956
- toolCallId: generateId3(),
4123
+ toolCallId: generateToolCallId(),
2957
4124
  toolName,
2958
4125
  input
2959
4126
  });
@@ -3465,4 +4632,4 @@ export {
3465
4632
  xmlToolMiddleware,
3466
4633
  yamlToolMiddleware
3467
4634
  };
3468
- //# sourceMappingURL=chunk-NAQSTPDQ.js.map
4635
+ //# sourceMappingURL=chunk-ERJKQKCR.js.map