@ai-sdk-tool/parser 3.3.3 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@ import {
2
2
  escapeRegExp,
3
3
  parse as parse2,
4
4
  stringify
5
- } from "./chunk-CXWS24JX.js";
5
+ } from "./chunk-76E6H46R.js";
6
6
  import {
7
7
  parse
8
8
  } from "./chunk-IX4FJELL.js";
@@ -10,7 +10,7 @@ import {
10
10
  coerceBySchema,
11
11
  getSchemaType,
12
12
  unwrapJsonSchema
13
- } from "./chunk-2KK5BDZF.js";
13
+ } from "./chunk-DJB4DAZO.js";
14
14
 
15
15
  // src/core/utils/debug.ts
16
16
  var LINE_SPLIT_REGEX = /\r?\n/;
@@ -176,9 +176,19 @@ function getPotentialStartIndex(text, searchedText) {
176
176
  if (directIndex !== -1) {
177
177
  return directIndex;
178
178
  }
179
- for (let i = text.length - 1; i >= 0; i -= 1) {
180
- const suffix = text.substring(i);
181
- if (searchedText.startsWith(suffix)) {
179
+ const textLength = text.length;
180
+ const searchedTextLength = searchedText.length;
181
+ const startAt = Math.max(0, textLength - searchedTextLength + 1);
182
+ for (let i = startAt; i < textLength; i++) {
183
+ let match = true;
184
+ const currentSuffixLength = textLength - i;
185
+ for (let j = 0; j < currentSuffixLength; j++) {
186
+ if (text[i + j] !== searchedText[j]) {
187
+ match = false;
188
+ break;
189
+ }
190
+ }
191
+ if (match) {
182
192
  return i;
183
193
  }
184
194
  }
@@ -187,7 +197,25 @@ function getPotentialStartIndex(text, searchedText) {
187
197
 
188
198
  // src/core/utils/id.ts
189
199
  function generateId() {
190
- return Math.random().toString(36).substring(2, 15);
200
+ return crypto.randomUUID().replace(/-/g, "").slice(0, 13);
201
+ }
202
+ var TOOL_CALL_ID_PREFIX = "call_";
203
+ var TOOL_CALL_ID_BODY_LENGTH = 24;
204
+ var TOOL_CALL_ID_ALPHANUM = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
205
+ function randomAlphaNumeric(length) {
206
+ var _a;
207
+ const bytes = new Uint8Array(length);
208
+ crypto.getRandomValues(bytes);
209
+ let out = "";
210
+ for (let i = 0; i < length; i += 1) {
211
+ const byte = bytes[i];
212
+ const index = (byte != null ? byte : 0) % TOOL_CALL_ID_ALPHANUM.length;
213
+ out += (_a = TOOL_CALL_ID_ALPHANUM[index]) != null ? _a : "0";
214
+ }
215
+ return out;
216
+ }
217
+ function generateToolCallId() {
218
+ return `${TOOL_CALL_ID_PREFIX}${randomAlphaNumeric(TOOL_CALL_ID_BODY_LENGTH)}`;
191
219
  }
192
220
 
193
221
  // src/core/utils/protocol-utils.ts
@@ -196,17 +224,55 @@ function addTextSegment(text, processedElements) {
196
224
  processedElements.push({ type: "text", text });
197
225
  }
198
226
  }
227
+ function createFlushTextHandler(getCurrentTextId, setCurrentTextId, getHasEmittedTextStart, setHasEmittedTextStart) {
228
+ return (controller, text) => {
229
+ const content = text;
230
+ if (content) {
231
+ if (!getCurrentTextId()) {
232
+ const newId = generateId();
233
+ setCurrentTextId(newId);
234
+ controller.enqueue({
235
+ type: "text-start",
236
+ id: newId
237
+ });
238
+ setHasEmittedTextStart(true);
239
+ }
240
+ controller.enqueue({
241
+ type: "text-delta",
242
+ id: getCurrentTextId(),
243
+ delta: content
244
+ });
245
+ }
246
+ const currentTextId = getCurrentTextId();
247
+ if (currentTextId && !text) {
248
+ if (getHasEmittedTextStart()) {
249
+ controller.enqueue({
250
+ type: "text-end",
251
+ id: currentTextId
252
+ });
253
+ setHasEmittedTextStart(false);
254
+ }
255
+ setCurrentTextId(null);
256
+ }
257
+ };
258
+ }
199
259
 
200
260
  // src/core/protocols/json-protocol.ts
261
+ function shouldEmitRawToolCallTextOnError(options) {
262
+ return (options == null ? void 0 : options.emitRawToolCallTextOnError) === true;
263
+ }
264
+ function canonicalizeToolInput(argumentsValue) {
265
+ return JSON.stringify(argumentsValue != null ? argumentsValue : {});
266
+ }
201
267
  function processToolCallJson(toolCallJson, fullMatch, processedElements, options) {
202
- var _a, _b;
268
+ var _a;
203
269
  try {
204
270
  const parsedToolCall = parse(toolCallJson);
205
271
  processedElements.push({
206
272
  type: "tool-call",
207
- toolCallId: generateId(),
273
+ toolCallId: generateToolCallId(),
208
274
  toolName: parsedToolCall.name,
209
- input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
275
+ input: canonicalizeToolInput(parsedToolCall.arguments)
210
276
  });
211
277
  } catch (error) {
212
278
  logParseFailure({
@@ -215,7 +281,7 @@ function processToolCallJson(toolCallJson, fullMatch, processedElements, options
215
281
  snippet: fullMatch,
216
282
  error
217
283
  });
218
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
284
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
219
285
  options,
220
286
  "Could not process JSON tool call, keeping original text.",
221
287
  { toolCall: fullMatch, error }
@@ -236,6 +302,293 @@ function processMatchedToolCall(context) {
236
302
  }
237
303
  return startIndex + match[0].length;
238
304
  }
305
+ var WHITESPACE_JSON_REGEX = /\s/;
306
+ function skipJsonWhitespace(text, fromIndex) {
307
+ let index = fromIndex;
308
+ while (index < text.length && WHITESPACE_JSON_REGEX.test(text[index])) {
309
+ index += 1;
310
+ }
311
+ return index;
312
+ }
313
+ function findTopLevelPropertyValueStart(text, property) {
314
+ const objectStart = skipJsonWhitespace(text, 0);
315
+ if (objectStart >= text.length || text.charAt(objectStart) !== "{") {
316
+ return null;
317
+ }
318
+ let depth = 0;
319
+ let inString = false;
320
+ let escaping = false;
321
+ for (let index = objectStart; index < text.length; index += 1) {
322
+ const char = text.charAt(index);
323
+ if (inString) {
324
+ if (escaping) {
325
+ escaping = false;
326
+ continue;
327
+ }
328
+ if (char === "\\") {
329
+ escaping = true;
330
+ continue;
331
+ }
332
+ if (char === '"') {
333
+ inString = false;
334
+ }
335
+ continue;
336
+ }
337
+ if (char === "{") {
338
+ depth += 1;
339
+ continue;
340
+ }
341
+ if (char === "}") {
342
+ depth = Math.max(0, depth - 1);
343
+ continue;
344
+ }
345
+ if (char !== '"') {
346
+ continue;
347
+ }
348
+ if (depth !== 1) {
349
+ inString = true;
350
+ continue;
351
+ }
352
+ const keyStart = index + 1;
353
+ let keyEnd = keyStart;
354
+ let keyEscaped = false;
355
+ while (keyEnd < text.length) {
356
+ const keyChar = text.charAt(keyEnd);
357
+ if (keyEscaped) {
358
+ keyEscaped = false;
359
+ } else if (keyChar === "\\") {
360
+ keyEscaped = true;
361
+ } else if (keyChar === '"') {
362
+ break;
363
+ }
364
+ keyEnd += 1;
365
+ }
366
+ if (keyEnd >= text.length || text.charAt(keyEnd) !== '"') {
367
+ return null;
368
+ }
369
+ const key = text.slice(keyStart, keyEnd);
370
+ let valueCursor = skipJsonWhitespace(text, keyEnd + 1);
371
+ if (valueCursor >= text.length || text.charAt(valueCursor) !== ":") {
372
+ index = keyEnd;
373
+ continue;
374
+ }
375
+ valueCursor = skipJsonWhitespace(text, valueCursor + 1);
376
+ if (key === property) {
377
+ return valueCursor < text.length ? valueCursor : null;
378
+ }
379
+ index = valueCursor - 1;
380
+ }
381
+ return null;
382
+ }
383
+ function extractTopLevelStringProperty(text, property) {
384
+ const valueStart = findTopLevelPropertyValueStart(text, property);
385
+ if (valueStart == null || valueStart >= text.length) {
386
+ return void 0;
387
+ }
388
+ if (text.charAt(valueStart) !== '"') {
389
+ return void 0;
390
+ }
391
+ let valueEnd = valueStart + 1;
392
+ let escaped = false;
393
+ while (valueEnd < text.length) {
394
+ const char = text.charAt(valueEnd);
395
+ if (escaped) {
396
+ escaped = false;
397
+ } else if (char === "\\") {
398
+ escaped = true;
399
+ } else if (char === '"') {
400
+ return text.slice(valueStart + 1, valueEnd);
401
+ }
402
+ valueEnd += 1;
403
+ }
404
+ return void 0;
405
+ }
406
+ function extractJsonValueSlice(text, valueStart) {
407
+ if (valueStart >= text.length) {
408
+ return null;
409
+ }
410
+ const first = text.charAt(valueStart);
411
+ if (first === "{" || first === "[") {
412
+ const stack = [first];
413
+ let inString = false;
414
+ let escaped = false;
415
+ for (let index2 = valueStart + 1; index2 < text.length; index2 += 1) {
416
+ const char = text.charAt(index2);
417
+ if (inString) {
418
+ if (escaped) {
419
+ escaped = false;
420
+ } else if (char === "\\") {
421
+ escaped = true;
422
+ } else if (char === '"') {
423
+ inString = false;
424
+ }
425
+ continue;
426
+ }
427
+ if (char === '"') {
428
+ inString = true;
429
+ continue;
430
+ }
431
+ if (char === "{" || char === "[") {
432
+ stack.push(char);
433
+ continue;
434
+ }
435
+ if (char === "}" || char === "]") {
436
+ const open = stack.at(-1);
437
+ if (open === "{" && char === "}" || open === "[" && char === "]") {
438
+ stack.pop();
439
+ if (stack.length === 0) {
440
+ return {
441
+ text: text.slice(valueStart, index2 + 1),
442
+ complete: true
443
+ };
444
+ }
445
+ }
446
+ }
447
+ }
448
+ return {
449
+ text: text.slice(valueStart),
450
+ complete: false
451
+ };
452
+ }
453
+ if (first === '"') {
454
+ let escaped = false;
455
+ for (let index2 = valueStart + 1; index2 < text.length; index2 += 1) {
456
+ const char = text.charAt(index2);
457
+ if (escaped) {
458
+ escaped = false;
459
+ } else if (char === "\\") {
460
+ escaped = true;
461
+ } else if (char === '"') {
462
+ return {
463
+ text: text.slice(valueStart, index2 + 1),
464
+ complete: true
465
+ };
466
+ }
467
+ }
468
+ return {
469
+ text: text.slice(valueStart),
470
+ complete: false
471
+ };
472
+ }
473
+ let index = valueStart;
474
+ while (index < text.length) {
475
+ const char = text.charAt(index);
476
+ if (char === "," || char === "}" || WHITESPACE_JSON_REGEX.test(char)) {
477
+ break;
478
+ }
479
+ index += 1;
480
+ }
481
+ return {
482
+ text: text.slice(valueStart, index),
483
+ complete: index < text.length
484
+ };
485
+ }
486
+ function extractStreamingToolCallProgress(toolCallJson) {
487
+ var _a;
488
+ const toolName = extractTopLevelStringProperty(toolCallJson, "name");
489
+ const argsValueStart = findTopLevelPropertyValueStart(
490
+ toolCallJson,
491
+ "arguments"
492
+ );
493
+ if (argsValueStart == null) {
494
+ return {
495
+ toolName,
496
+ argumentsText: void 0,
497
+ argumentsComplete: false
498
+ };
499
+ }
500
+ const argsSlice = extractJsonValueSlice(toolCallJson, argsValueStart);
501
+ return {
502
+ toolName,
503
+ argumentsText: argsSlice == null ? void 0 : argsSlice.text,
504
+ argumentsComplete: (_a = argsSlice == null ? void 0 : argsSlice.complete) != null ? _a : false
505
+ };
506
+ }
507
+ function ensureToolInputStart(state, controller, toolName) {
508
+ if (!state.activeToolInput) {
509
+ const id = generateToolCallId();
510
+ state.activeToolInput = {
511
+ id,
512
+ toolName,
513
+ emittedInput: ""
514
+ };
515
+ controller.enqueue({
516
+ type: "tool-input-start",
517
+ id,
518
+ toolName
519
+ });
520
+ }
521
+ }
522
+ function emitToolInputDelta(state, controller, fullInput) {
523
+ const active = state.activeToolInput;
524
+ if (!active) {
525
+ return;
526
+ }
527
+ if (!fullInput.startsWith(active.emittedInput)) {
528
+ return;
529
+ }
530
+ const delta = fullInput.slice(active.emittedInput.length);
531
+ if (delta.length === 0) {
532
+ return;
533
+ }
534
+ controller.enqueue({
535
+ type: "tool-input-delta",
536
+ id: active.id,
537
+ delta
538
+ });
539
+ active.emittedInput = fullInput;
540
+ }
541
+ function closeToolInput(state, controller) {
542
+ if (!state.activeToolInput) {
543
+ return;
544
+ }
545
+ controller.enqueue({
546
+ type: "tool-input-end",
547
+ id: state.activeToolInput.id
548
+ });
549
+ state.activeToolInput = null;
550
+ }
551
+ function emitToolCallFromParsed(state, controller, parsedToolCall) {
552
+ var _a, _b, _c, _d;
553
+ closeTextBlock(state, controller);
554
+ const toolName = typeof parsedToolCall.name === "string" ? parsedToolCall.name : (_b = (_a = state.activeToolInput) == null ? void 0 : _a.toolName) != null ? _b : "unknown";
555
+ const input = canonicalizeToolInput(parsedToolCall.arguments);
556
+ ensureToolInputStart(state, controller, toolName);
557
+ emitToolInputDelta(state, controller, input);
558
+ const toolCallId = (_d = (_c = state.activeToolInput) == null ? void 0 : _c.id) != null ? _d : generateToolCallId();
559
+ closeToolInput(state, controller);
560
+ controller.enqueue({
561
+ type: "tool-call",
562
+ toolCallId,
563
+ toolName,
564
+ input
565
+ });
566
+ }
567
+ function canonicalizeArgumentsProgressInput(progress) {
568
+ if (progress.argumentsText === void 0 || !progress.argumentsComplete) {
569
+ return void 0;
570
+ }
571
+ try {
572
+ const parsedArguments = parse(progress.argumentsText);
573
+ return canonicalizeToolInput(parsedArguments);
574
+ } catch (e) {
575
+ return void 0;
576
+ }
577
+ }
578
+ function emitToolInputProgress(state, controller) {
579
+ if (!(state.isInsideToolCall && state.currentToolCallJson)) {
580
+ return;
581
+ }
582
+ const progress = extractStreamingToolCallProgress(state.currentToolCallJson);
583
+ if (!progress.toolName) {
584
+ return;
585
+ }
586
+ ensureToolInputStart(state, controller, progress.toolName);
587
+ const canonicalProgressInput = canonicalizeArgumentsProgressInput(progress);
588
+ if (canonicalProgressInput !== void 0) {
589
+ emitToolInputDelta(state, controller, canonicalProgressInput);
590
+ }
591
+ }
239
592
  function flushBuffer(state, controller, toolCallStart) {
240
593
  if (state.buffer.length === 0) {
241
594
  return;
@@ -266,44 +619,77 @@ function closeTextBlock(state, controller) {
266
619
  state.hasEmittedTextStart = false;
267
620
  }
268
621
  }
269
- function emitIncompleteToolCall(state, controller, toolCallStart) {
270
- if (!state.currentToolCallJson) {
622
+ function emitIncompleteToolCall(state, controller, toolCallStart, trailingBuffer, options) {
623
+ var _a;
624
+ if (!state.currentToolCallJson && trailingBuffer.length === 0) {
625
+ state.isInsideToolCall = false;
271
626
  return;
272
627
  }
628
+ if (state.currentToolCallJson) {
629
+ try {
630
+ const parsedToolCall = parse(state.currentToolCallJson);
631
+ emitToolCallFromParsed(state, controller, parsedToolCall);
632
+ state.currentToolCallJson = "";
633
+ state.isInsideToolCall = false;
634
+ return;
635
+ } catch (e) {
636
+ }
637
+ }
638
+ const rawToolCallContent = `${state.currentToolCallJson}${trailingBuffer}`;
639
+ const errorContent = `${toolCallStart}${rawToolCallContent}`;
640
+ const shouldEmitRawFallback = shouldEmitRawToolCallTextOnError(options);
273
641
  logParseFailure({
274
642
  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
643
+ reason: shouldEmitRawFallback ? "Incomplete streaming tool call segment emitted as text" : "Incomplete streaming tool call segment suppressed without raw text fallback",
644
+ snippet: errorContent
292
645
  });
646
+ if (shouldEmitRawFallback) {
647
+ const errorId = generateId();
648
+ controller.enqueue({
649
+ type: "text-start",
650
+ id: errorId
651
+ });
652
+ controller.enqueue({
653
+ type: "text-delta",
654
+ id: errorId,
655
+ delta: errorContent
656
+ });
657
+ controller.enqueue({
658
+ type: "text-end",
659
+ id: errorId
660
+ });
661
+ }
662
+ closeToolInput(state, controller);
663
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
664
+ options,
665
+ shouldEmitRawFallback ? "Could not complete streaming JSON tool call at finish; emitting original text." : "Could not complete streaming JSON tool call at finish.",
666
+ { toolCall: errorContent }
667
+ );
293
668
  state.currentToolCallJson = "";
669
+ state.isInsideToolCall = false;
294
670
  }
295
- function handleFinishChunk(state, controller, toolCallStart, chunk) {
296
- if (state.buffer.length > 0) {
671
+ function handleFinishChunk(state, controller, toolCallStart, options, chunk) {
672
+ if (state.isInsideToolCall) {
673
+ const trailingBuffer = state.buffer;
674
+ state.buffer = "";
675
+ emitIncompleteToolCall(
676
+ state,
677
+ controller,
678
+ toolCallStart,
679
+ trailingBuffer,
680
+ options
681
+ );
682
+ } else if (state.buffer.length > 0) {
297
683
  flushBuffer(state, controller, toolCallStart);
298
684
  }
299
685
  closeTextBlock(state, controller);
300
- emitIncompleteToolCall(state, controller, toolCallStart);
301
686
  controller.enqueue(chunk);
302
687
  }
303
688
  function publishText(text, state, controller) {
304
689
  if (state.isInsideToolCall) {
305
690
  closeTextBlock(state, controller);
306
691
  state.currentToolCallJson += text;
692
+ emitToolInputProgress(state, controller);
307
693
  } else if (text.length > 0) {
308
694
  if (!state.currentTextId) {
309
695
  state.currentTextId = generateId();
@@ -321,42 +707,40 @@ function publishText(text, state, controller) {
321
707
  }
322
708
  }
323
709
  function emitToolCall(context) {
324
- var _a, _b;
710
+ var _a;
325
711
  const { state, controller, toolCallStart, toolCallEnd, options } = context;
326
712
  try {
327
713
  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
- });
714
+ emitToolCallFromParsed(state, controller, parsedToolCall);
335
715
  } catch (error) {
716
+ const errorContent = `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`;
717
+ const shouldEmitRawFallback = shouldEmitRawToolCallTextOnError(options);
336
718
  logParseFailure({
337
719
  phase: "stream",
338
720
  reason: "Failed to parse streaming tool call JSON segment",
339
- snippet: `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`,
721
+ snippet: errorContent,
340
722
  error
341
723
  });
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(
724
+ if (shouldEmitRawFallback) {
725
+ const errorId = generateId();
726
+ controller.enqueue({
727
+ type: "text-start",
728
+ id: errorId
729
+ });
730
+ controller.enqueue({
731
+ type: "text-delta",
732
+ id: errorId,
733
+ delta: errorContent
734
+ });
735
+ controller.enqueue({
736
+ type: "text-end",
737
+ id: errorId
738
+ });
739
+ }
740
+ closeToolInput(state, controller);
741
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
358
742
  options,
359
- "Could not process streaming JSON tool call; emitting original text.",
743
+ shouldEmitRawFallback ? "Could not process streaming JSON tool call; emitting original text." : "Could not process streaming JSON tool call.",
360
744
  {
361
745
  toolCall: errorContent
362
746
  }
@@ -372,6 +756,7 @@ function processTagMatch(context) {
372
756
  } else {
373
757
  state.currentToolCallJson = "";
374
758
  state.isInsideToolCall = true;
759
+ state.activeToolInput = null;
375
760
  }
376
761
  }
377
762
  function processBufferTags(context) {
@@ -394,8 +779,16 @@ function processBufferTags(context) {
394
779
  );
395
780
  }
396
781
  }
397
- function handlePartialTag(state, controller, toolCallStart) {
782
+ function handlePartialTag(state, controller, toolCallStart, toolCallEnd) {
398
783
  if (state.isInsideToolCall) {
784
+ const potentialEndIndex = getPotentialStartIndex(state.buffer, toolCallEnd);
785
+ if (potentialEndIndex != null && potentialEndIndex + toolCallEnd.length > state.buffer.length) {
786
+ publishText(state.buffer.slice(0, potentialEndIndex), state, controller);
787
+ state.buffer = state.buffer.slice(potentialEndIndex);
788
+ } else {
789
+ publishText(state.buffer, state, controller);
790
+ state.buffer = "";
791
+ }
399
792
  return;
400
793
  }
401
794
  const potentialIndex = getPotentialStartIndex(state.buffer, toolCallStart);
@@ -468,13 +861,14 @@ var jsonProtocol = ({
468
861
  buffer: "",
469
862
  currentToolCallJson: "",
470
863
  currentTextId: null,
471
- hasEmittedTextStart: false
864
+ hasEmittedTextStart: false,
865
+ activeToolInput: null
472
866
  };
473
867
  return new TransformStream({
474
868
  transform(chunk, controller) {
475
869
  var _a;
476
870
  if (chunk.type === "finish") {
477
- handleFinishChunk(state, controller, toolCallStart, chunk);
871
+ handleFinishChunk(state, controller, toolCallStart, options, chunk);
478
872
  return;
479
873
  }
480
874
  if (chunk.type !== "text-delta") {
@@ -490,7 +884,7 @@ var jsonProtocol = ({
490
884
  toolCallEnd,
491
885
  options
492
886
  });
493
- handlePartialTag(state, controller, toolCallStart);
887
+ handlePartialTag(state, controller, toolCallStart, toolCallEnd);
494
888
  }
495
889
  });
496
890
  },
@@ -520,6 +914,80 @@ function isTCMProtocolFactory(protocol) {
520
914
  var NAME_CHAR_RE = /[A-Za-z0-9_:-]/;
521
915
  var WHITESPACE_REGEX = /\s/;
522
916
 
917
+ // src/core/utils/streamed-tool-input-delta.ts
918
+ function emitDelta({
919
+ controller,
920
+ id,
921
+ state,
922
+ nextInput
923
+ }) {
924
+ if (!nextInput.startsWith(state.emittedInput)) {
925
+ return false;
926
+ }
927
+ const delta = nextInput.slice(state.emittedInput.length);
928
+ if (delta.length === 0) {
929
+ return false;
930
+ }
931
+ controller.enqueue({
932
+ type: "tool-input-delta",
933
+ id,
934
+ delta
935
+ });
936
+ state.emittedInput = nextInput;
937
+ return true;
938
+ }
939
+ function toIncompleteJsonPrefix(fullJson) {
940
+ const trimmed = fullJson.trim();
941
+ let prefix = trimmed;
942
+ while (prefix.endsWith("}") || prefix.endsWith("]")) {
943
+ prefix = prefix.slice(0, -1);
944
+ }
945
+ prefix = prefix.trimEnd();
946
+ if (prefix.endsWith('"')) {
947
+ prefix = prefix.slice(0, -1);
948
+ }
949
+ if (prefix.length === 0) {
950
+ if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
951
+ return trimmed.startsWith("{") ? "{" : "[";
952
+ }
953
+ if (trimmed.startsWith("]")) {
954
+ return "[";
955
+ }
956
+ if (trimmed.startsWith("}")) {
957
+ return "{";
958
+ }
959
+ if (trimmed.startsWith('"')) {
960
+ return '"';
961
+ }
962
+ return "{";
963
+ }
964
+ return prefix;
965
+ }
966
+ function emitPrefixDelta(params) {
967
+ return emitDelta({
968
+ ...params,
969
+ nextInput: params.candidate
970
+ });
971
+ }
972
+ function emitFinalRemainder(params) {
973
+ var _a;
974
+ const result = emitDelta({
975
+ ...params,
976
+ nextInput: params.finalFullJson
977
+ });
978
+ if (!result && params.state.emittedInput.length > 0) {
979
+ (_a = params.onMismatch) == null ? void 0 : _a.call(
980
+ params,
981
+ "Final JSON does not extend emitted tool-input prefix",
982
+ {
983
+ emittedLength: params.state.emittedInput.length,
984
+ finalLength: params.finalFullJson.length
985
+ }
986
+ );
987
+ }
988
+ return result;
989
+ }
990
+
523
991
  // src/core/utils/xml-root-repair.ts
524
992
  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
993
  function tryRepairXmlSelfClosingRootWithBody(rawText, toolNames) {
@@ -549,6 +1017,9 @@ function getToolSchema(tools, toolName) {
549
1017
  var _a;
550
1018
  return (_a = tools.find((t) => t.name === toolName)) == null ? void 0 : _a.inputSchema;
551
1019
  }
1020
+ function shouldEmitRawToolCallTextOnError2(options) {
1021
+ return (options == null ? void 0 : options.emitRawToolCallTextOnError) === true;
1022
+ }
552
1023
  function processToolCall(params) {
553
1024
  var _a, _b;
554
1025
  const { toolCall, tools, options, text, processedElements, parseOptions } = params;
@@ -561,7 +1032,7 @@ function processToolCall(params) {
561
1032
  const parsed = parse2(toolCall.content, toolSchema, parseConfig);
562
1033
  processedElements.push({
563
1034
  type: "tool-call",
564
- toolCallId: generateId(),
1035
+ toolCallId: generateToolCallId(),
565
1036
  toolName: toolCall.toolName,
566
1037
  input: JSON.stringify(parsed)
567
1038
  });
@@ -578,6 +1049,299 @@ function processToolCall(params) {
578
1049
  processedElements.push({ type: "text", text: originalCallText });
579
1050
  }
580
1051
  }
1052
+ function parseXmlTagName(rawTagBody) {
1053
+ let index = 0;
1054
+ while (index < rawTagBody.length && WHITESPACE_REGEX.test(rawTagBody[index])) {
1055
+ index += 1;
1056
+ }
1057
+ const nameStart = index;
1058
+ while (index < rawTagBody.length && NAME_CHAR_RE.test(rawTagBody.charAt(index))) {
1059
+ index += 1;
1060
+ }
1061
+ return rawTagBody.slice(nameStart, index);
1062
+ }
1063
+ function consumeXmlSpecialSection(fragment, ltIndex) {
1064
+ if (fragment.startsWith("<!--", ltIndex)) {
1065
+ const commentEnd = fragment.indexOf("-->", ltIndex + 4);
1066
+ return commentEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: commentEnd + 3 };
1067
+ }
1068
+ if (fragment.startsWith("<![CDATA[", ltIndex)) {
1069
+ const cdataEnd = fragment.indexOf("]]>", ltIndex + 9);
1070
+ return cdataEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: cdataEnd + 3 };
1071
+ }
1072
+ if (fragment.startsWith("<?", ltIndex)) {
1073
+ const processingEnd = fragment.indexOf("?>", ltIndex + 2);
1074
+ return processingEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: processingEnd + 2 };
1075
+ }
1076
+ if (fragment.startsWith("<!", ltIndex)) {
1077
+ const declarationEnd = fragment.indexOf(">", ltIndex + 2);
1078
+ return declarationEnd === -1 ? { kind: "incomplete" } : { kind: "consumed", nextPos: declarationEnd + 1 };
1079
+ }
1080
+ return { kind: "none" };
1081
+ }
1082
+ function parseXmlTagToken(fragment, ltIndex) {
1083
+ const gtIndex = fragment.indexOf(">", ltIndex + 1);
1084
+ if (gtIndex === -1) {
1085
+ return null;
1086
+ }
1087
+ const tagBody = fragment.slice(ltIndex + 1, gtIndex).trim();
1088
+ if (tagBody.length === 0) {
1089
+ return null;
1090
+ }
1091
+ if (tagBody.startsWith("/")) {
1092
+ const closeName = parseXmlTagName(tagBody.slice(1));
1093
+ if (closeName.length === 0) {
1094
+ return null;
1095
+ }
1096
+ return { kind: "close", name: closeName, nextPos: gtIndex + 1 };
1097
+ }
1098
+ const selfClosing = tagBody.endsWith("/");
1099
+ const openBody = selfClosing ? tagBody.slice(0, -1).trimEnd() : tagBody;
1100
+ const openName = parseXmlTagName(openBody);
1101
+ if (openName.length === 0) {
1102
+ return null;
1103
+ }
1104
+ return {
1105
+ kind: "open",
1106
+ name: openName,
1107
+ selfClosing,
1108
+ nextPos: gtIndex + 1
1109
+ };
1110
+ }
1111
+ function analyzeXmlFragmentForProgress(fragment) {
1112
+ const stack = [];
1113
+ const topLevelTagNames = [];
1114
+ let position = 0;
1115
+ while (position < fragment.length) {
1116
+ const ltIndex = fragment.indexOf("<", position);
1117
+ if (ltIndex === -1) {
1118
+ break;
1119
+ }
1120
+ const special = consumeXmlSpecialSection(fragment, ltIndex);
1121
+ if (special.kind === "incomplete") {
1122
+ return null;
1123
+ }
1124
+ if (special.kind === "consumed") {
1125
+ position = special.nextPos;
1126
+ continue;
1127
+ }
1128
+ const token = parseXmlTagToken(fragment, ltIndex);
1129
+ if (token === null) {
1130
+ return null;
1131
+ }
1132
+ if (token.kind === "close") {
1133
+ const openName = stack.pop();
1134
+ if (!openName || openName !== token.name) {
1135
+ return null;
1136
+ }
1137
+ position = token.nextPos;
1138
+ continue;
1139
+ }
1140
+ if (stack.length === 0) {
1141
+ topLevelTagNames.push(token.name);
1142
+ }
1143
+ if (!token.selfClosing) {
1144
+ stack.push(token.name);
1145
+ }
1146
+ position = token.nextPos;
1147
+ }
1148
+ if (stack.length > 0) {
1149
+ return null;
1150
+ }
1151
+ return { topLevelTagNames };
1152
+ }
1153
+ function scanXmlFragmentTopLevelTextStep(options) {
1154
+ const { fragment, position, stack } = options;
1155
+ const ltIndex = fragment.indexOf("<", position);
1156
+ if (ltIndex === -1) {
1157
+ const trailingText = fragment.slice(position);
1158
+ return {
1159
+ kind: "done",
1160
+ value: stack.length === 0 && trailingText.trim().length > 0
1161
+ };
1162
+ }
1163
+ const textBetweenTags = fragment.slice(position, ltIndex);
1164
+ if (stack.length === 0 && textBetweenTags.trim().length > 0) {
1165
+ return { kind: "found" };
1166
+ }
1167
+ const special = consumeXmlSpecialSection(fragment, ltIndex);
1168
+ if (special.kind === "incomplete") {
1169
+ return { kind: "invalid" };
1170
+ }
1171
+ if (special.kind === "consumed") {
1172
+ return { kind: "next", nextPos: special.nextPos };
1173
+ }
1174
+ const token = parseXmlTagToken(fragment, ltIndex);
1175
+ if (token === null) {
1176
+ return { kind: "invalid" };
1177
+ }
1178
+ if (token.kind === "close") {
1179
+ const openName = stack.pop();
1180
+ if (!openName || openName !== token.name) {
1181
+ return { kind: "invalid" };
1182
+ }
1183
+ } else if (!token.selfClosing) {
1184
+ stack.push(token.name);
1185
+ }
1186
+ return { kind: "next", nextPos: token.nextPos };
1187
+ }
1188
+ function hasNonWhitespaceTopLevelText(fragment) {
1189
+ if (!fragment.includes("<")) {
1190
+ return fragment.trim().length > 0;
1191
+ }
1192
+ const stack = [];
1193
+ let position = 0;
1194
+ while (position < fragment.length) {
1195
+ const step = scanXmlFragmentTopLevelTextStep({ fragment, position, stack });
1196
+ if (step.kind === "found") {
1197
+ return true;
1198
+ }
1199
+ if (step.kind === "invalid") {
1200
+ return false;
1201
+ }
1202
+ if (step.kind === "done") {
1203
+ return step.value;
1204
+ }
1205
+ position = step.nextPos;
1206
+ }
1207
+ return false;
1208
+ }
1209
+ function getObjectSchemaPropertyNames(schema) {
1210
+ if (!schema || typeof schema !== "object") {
1211
+ return null;
1212
+ }
1213
+ const schemaObject = schema;
1214
+ const typeValue = schemaObject.type;
1215
+ if (typeValue != null) {
1216
+ const isObjectType = typeValue === "object" || Array.isArray(typeValue) && typeValue.includes("object");
1217
+ if (!isObjectType) {
1218
+ return null;
1219
+ }
1220
+ }
1221
+ if (!schemaObject.properties || typeof schemaObject.properties !== "object") {
1222
+ return /* @__PURE__ */ new Set();
1223
+ }
1224
+ return new Set(
1225
+ Object.keys(schemaObject.properties)
1226
+ );
1227
+ }
1228
+ function schemaAllowsArrayType(schema) {
1229
+ if (!schema || typeof schema !== "object") {
1230
+ return false;
1231
+ }
1232
+ const schemaRecord = schema;
1233
+ const typeValue = schemaRecord.type;
1234
+ if (typeValue === "array") {
1235
+ return true;
1236
+ }
1237
+ if (Array.isArray(typeValue) && typeValue.includes("array")) {
1238
+ return true;
1239
+ }
1240
+ const unions = [schemaRecord.anyOf, schemaRecord.oneOf, schemaRecord.allOf];
1241
+ for (const union of unions) {
1242
+ if (!Array.isArray(union)) {
1243
+ continue;
1244
+ }
1245
+ if (union.some((entry) => schemaAllowsArrayType(entry))) {
1246
+ return true;
1247
+ }
1248
+ }
1249
+ return false;
1250
+ }
1251
+ function getSchemaObjectProperty(schema, propertyName) {
1252
+ if (!schema || typeof schema !== "object") {
1253
+ return null;
1254
+ }
1255
+ const schemaObject = schema;
1256
+ const properties = schemaObject.properties;
1257
+ if (!properties || typeof properties !== "object") {
1258
+ return null;
1259
+ }
1260
+ const property = properties[propertyName];
1261
+ if (!property) {
1262
+ return null;
1263
+ }
1264
+ return property;
1265
+ }
1266
+ function isStableXmlProgressCandidate(options) {
1267
+ const { candidate, parsed, toolSchema } = options;
1268
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1269
+ return false;
1270
+ }
1271
+ const structure = analyzeXmlFragmentForProgress(candidate);
1272
+ if (!structure) {
1273
+ return false;
1274
+ }
1275
+ const schemaProperties = getObjectSchemaPropertyNames(toolSchema);
1276
+ if (!schemaProperties || schemaProperties.size === 0) {
1277
+ return false;
1278
+ }
1279
+ const parsedObject = parsed;
1280
+ const uniqueTopLevelTags = new Set(structure.topLevelTagNames);
1281
+ for (const tagName of uniqueTopLevelTags) {
1282
+ if (!schemaProperties.has(tagName)) {
1283
+ continue;
1284
+ }
1285
+ const schemaProperty = getSchemaObjectProperty(toolSchema, tagName);
1286
+ if (schemaProperty && schemaAllowsArrayType(schemaProperty) && !Array.isArray(parsedObject[tagName])) {
1287
+ return false;
1288
+ }
1289
+ }
1290
+ if (structure.topLevelTagNames.length === 1) {
1291
+ const onlyTopLevelTag = structure.topLevelTagNames[0];
1292
+ if (!schemaProperties || schemaProperties.size === 0 || !schemaProperties.has(onlyTopLevelTag)) {
1293
+ return false;
1294
+ }
1295
+ }
1296
+ return true;
1297
+ }
1298
+ function parseXmlContentForStreamProgress({
1299
+ toolContent,
1300
+ toolSchema,
1301
+ parseOptions
1302
+ }) {
1303
+ const tryParse = (content) => {
1304
+ try {
1305
+ return parse2(content, toolSchema, {
1306
+ ...parseOptions != null ? parseOptions : {},
1307
+ repair: false,
1308
+ onError: void 0
1309
+ });
1310
+ } catch (e) {
1311
+ return null;
1312
+ }
1313
+ };
1314
+ const strictFull = tryParse(toolContent);
1315
+ if (strictFull !== null && isStableXmlProgressCandidate({
1316
+ candidate: toolContent,
1317
+ parsed: strictFull,
1318
+ toolSchema
1319
+ })) {
1320
+ return JSON.stringify(strictFull);
1321
+ }
1322
+ let searchEnd = toolContent.length;
1323
+ while (searchEnd > 0) {
1324
+ const gtIndex = toolContent.lastIndexOf(">", searchEnd - 1);
1325
+ if (gtIndex === -1) {
1326
+ break;
1327
+ }
1328
+ const candidate = toolContent.slice(0, gtIndex + 1);
1329
+ if (!analyzeXmlFragmentForProgress(candidate)) {
1330
+ searchEnd = gtIndex;
1331
+ continue;
1332
+ }
1333
+ const parsedCandidate = tryParse(candidate);
1334
+ if (parsedCandidate !== null && isStableXmlProgressCandidate({
1335
+ candidate,
1336
+ parsed: parsedCandidate,
1337
+ toolSchema
1338
+ })) {
1339
+ return JSON.stringify(parsedCandidate);
1340
+ }
1341
+ searchEnd = gtIndex;
1342
+ }
1343
+ return null;
1344
+ }
581
1345
  function handleStreamingToolCallEnd(params) {
582
1346
  var _a, _b;
583
1347
  const {
@@ -597,19 +1361,37 @@ function handleStreamingToolCallEnd(params) {
597
1361
  flushText(ctrl);
598
1362
  try {
599
1363
  const parsedResult = parse2(toolContent, toolSchema, parseConfig);
1364
+ const finalInput = JSON.stringify(parsedResult);
1365
+ emitFinalRemainder({
1366
+ controller: ctrl,
1367
+ id: currentToolCall.toolCallId,
1368
+ state: currentToolCall,
1369
+ finalFullJson: finalInput,
1370
+ onMismatch: options == null ? void 0 : options.onError
1371
+ });
1372
+ ctrl.enqueue({
1373
+ type: "tool-input-end",
1374
+ id: currentToolCall.toolCallId
1375
+ });
600
1376
  ctrl.enqueue({
601
1377
  type: "tool-call",
602
- toolCallId: generateId(),
1378
+ toolCallId: currentToolCall.toolCallId,
603
1379
  toolName: currentToolCall.name,
604
- input: JSON.stringify(parsedResult)
1380
+ input: finalInput
605
1381
  });
606
1382
  } catch (error) {
1383
+ ctrl.enqueue({
1384
+ type: "tool-input-end",
1385
+ id: currentToolCall.toolCallId
1386
+ });
607
1387
  const original = `<${currentToolCall.name}>${toolContent}</${currentToolCall.name}>`;
608
1388
  (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, "Could not process streaming XML tool call", {
609
1389
  toolCall: original,
610
1390
  error
611
1391
  });
612
- flushText(ctrl, original);
1392
+ if (shouldEmitRawToolCallTextOnError2(options)) {
1393
+ flushText(ctrl, original);
1394
+ }
613
1395
  }
614
1396
  }
615
1397
  function findClosingTagEndFlexible(text, contentStart, toolName) {
@@ -1031,38 +1813,6 @@ function findPotentialToolTagStart(buffer, toolNames) {
1031
1813
  }
1032
1814
  return -1;
1033
1815
  }
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
1816
  function processToolCallInBuffer(params) {
1067
1817
  const {
1068
1818
  buffer,
@@ -1072,18 +1822,21 @@ function processToolCallInBuffer(params) {
1072
1822
  controller,
1073
1823
  flushText,
1074
1824
  setBuffer,
1075
- parseOptions
1825
+ parseOptions,
1826
+ emitToolInputProgress: emitToolInputProgress2
1076
1827
  } = params;
1077
1828
  const endTagPattern = new RegExp(
1078
1829
  `</\\s*${escapeRegExp(currentToolCall.name)}\\s*>`
1079
1830
  );
1080
1831
  const endMatch = endTagPattern.exec(buffer);
1081
1832
  if (!endMatch || endMatch.index === void 0) {
1833
+ emitToolInputProgress2(controller, currentToolCall, buffer);
1082
1834
  return { buffer, currentToolCall, shouldBreak: true };
1083
1835
  }
1084
1836
  const endIdx = endMatch.index;
1085
1837
  const endPos = endIdx + endMatch[0].length;
1086
1838
  const content = buffer.substring(0, endIdx);
1839
+ emitToolInputProgress2(controller, currentToolCall, content);
1087
1840
  const remainder = buffer.substring(endPos);
1088
1841
  setBuffer(remainder);
1089
1842
  handleStreamingToolCallEnd({
@@ -1110,7 +1863,8 @@ function processNoToolCallInBuffer(params) {
1110
1863
  tools,
1111
1864
  options,
1112
1865
  parseOptions,
1113
- setBuffer
1866
+ setBuffer,
1867
+ emitToolInputStart
1114
1868
  } = params;
1115
1869
  const {
1116
1870
  index: earliestStartTagIndex,
@@ -1140,9 +1894,10 @@ function processNoToolCallInBuffer(params) {
1140
1894
  if (selfClosing) {
1141
1895
  const newBuffer2 = buffer.substring(earliestStartTagIndex + tagLength);
1142
1896
  setBuffer(newBuffer2);
1897
+ const currentToolCall = emitToolInputStart(controller, earliestToolName);
1143
1898
  handleStreamingToolCallEnd({
1144
1899
  toolContent: "",
1145
- currentToolCall: { name: earliestToolName, content: "" },
1900
+ currentToolCall,
1146
1901
  tools,
1147
1902
  options,
1148
1903
  ctrl: controller,
@@ -1161,12 +1916,12 @@ function processNoToolCallInBuffer(params) {
1161
1916
  setBuffer(newBuffer);
1162
1917
  return {
1163
1918
  buffer: newBuffer,
1164
- currentToolCall: { name: earliestToolName, content: "" },
1919
+ currentToolCall: emitToolInputStart(controller, earliestToolName),
1165
1920
  shouldBreak: false,
1166
1921
  shouldContinue: true
1167
1922
  };
1168
1923
  }
1169
- function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, setCurrentToolCall, tools, options, toolNames, flushText, parseOptions) {
1924
+ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, setCurrentToolCall, tools, options, toolNames, flushText, parseOptions, emitToolInputProgress2, emitToolInputStart) {
1170
1925
  return (controller) => {
1171
1926
  while (true) {
1172
1927
  const currentToolCall = getCurrentToolCall();
@@ -1179,7 +1934,8 @@ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, se
1179
1934
  controller,
1180
1935
  flushText,
1181
1936
  setBuffer,
1182
- parseOptions
1937
+ parseOptions,
1938
+ emitToolInputProgress: emitToolInputProgress2
1183
1939
  });
1184
1940
  setBuffer(result.buffer);
1185
1941
  setCurrentToolCall(result.currentToolCall);
@@ -1195,7 +1951,8 @@ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, se
1195
1951
  tools,
1196
1952
  options,
1197
1953
  parseOptions,
1198
- setBuffer
1954
+ setBuffer,
1955
+ emitToolInputStart
1199
1956
  });
1200
1957
  setBuffer(result.buffer);
1201
1958
  setCurrentToolCall(result.currentToolCall);
@@ -1308,7 +2065,119 @@ var xmlProtocol = (protocolOptions) => {
1308
2065
  (value) => {
1309
2066
  hasEmittedTextStart = value;
1310
2067
  }
1311
- );
2068
+ );
2069
+ const emitToolInputStart = (controller, toolName) => {
2070
+ flushText(controller);
2071
+ const next = {
2072
+ name: toolName,
2073
+ toolCallId: generateToolCallId(),
2074
+ emittedInput: "",
2075
+ lastProgressGtIndex: null,
2076
+ lastProgressFullInput: null
2077
+ };
2078
+ controller.enqueue({
2079
+ type: "tool-input-start",
2080
+ id: next.toolCallId,
2081
+ toolName
2082
+ });
2083
+ return next;
2084
+ };
2085
+ const emitToolInputProgress2 = (controller, toolCall, toolContent) => {
2086
+ const progressGtIndex = toolContent.lastIndexOf(">");
2087
+ if (toolCall.lastProgressGtIndex === progressGtIndex) {
2088
+ const cached = toolCall.lastProgressFullInput;
2089
+ if (cached == null) {
2090
+ return;
2091
+ }
2092
+ if (cached === "{}" && toolContent.trim().length === 0) {
2093
+ return;
2094
+ }
2095
+ const prefixCandidate2 = toIncompleteJsonPrefix(cached);
2096
+ emitPrefixDelta({
2097
+ controller,
2098
+ id: toolCall.toolCallId,
2099
+ state: toolCall,
2100
+ candidate: prefixCandidate2
2101
+ });
2102
+ return;
2103
+ }
2104
+ const toolSchema = getToolSchema(tools, toolCall.name);
2105
+ const fullInput = parseXmlContentForStreamProgress({
2106
+ toolContent,
2107
+ toolSchema,
2108
+ parseOptions
2109
+ });
2110
+ toolCall.lastProgressGtIndex = progressGtIndex;
2111
+ toolCall.lastProgressFullInput = fullInput;
2112
+ if (fullInput == null) {
2113
+ return;
2114
+ }
2115
+ if (fullInput === "{}" && toolContent.trim().length === 0) {
2116
+ return;
2117
+ }
2118
+ const prefixCandidate = toIncompleteJsonPrefix(fullInput);
2119
+ emitPrefixDelta({
2120
+ controller,
2121
+ id: toolCall.toolCallId,
2122
+ state: toolCall,
2123
+ candidate: prefixCandidate
2124
+ });
2125
+ };
2126
+ const finalizeUnclosedToolCall = (controller) => {
2127
+ var _a2, _b;
2128
+ if (!currentToolCall) {
2129
+ return;
2130
+ }
2131
+ emitToolInputProgress2(controller, currentToolCall, buffer);
2132
+ const parseConfig = {
2133
+ ...parseOptions,
2134
+ onError: (_a2 = options == null ? void 0 : options.onError) != null ? _a2 : parseOptions == null ? void 0 : parseOptions.onError
2135
+ };
2136
+ const toolSchema = getToolSchema(tools, currentToolCall.name);
2137
+ flushText(controller);
2138
+ try {
2139
+ if (hasNonWhitespaceTopLevelText(buffer)) {
2140
+ throw new Error(
2141
+ "Cannot reconcile unclosed XML tool call with top-level plain text."
2142
+ );
2143
+ }
2144
+ const parsedResult = parse2(buffer, toolSchema, parseConfig);
2145
+ const finalInput = JSON.stringify(parsedResult);
2146
+ emitFinalRemainder({
2147
+ controller,
2148
+ id: currentToolCall.toolCallId,
2149
+ state: currentToolCall,
2150
+ finalFullJson: finalInput,
2151
+ onMismatch: options == null ? void 0 : options.onError
2152
+ });
2153
+ controller.enqueue({
2154
+ type: "tool-input-end",
2155
+ id: currentToolCall.toolCallId
2156
+ });
2157
+ controller.enqueue({
2158
+ type: "tool-call",
2159
+ toolCallId: currentToolCall.toolCallId,
2160
+ toolName: currentToolCall.name,
2161
+ input: finalInput
2162
+ });
2163
+ } catch (error) {
2164
+ controller.enqueue({
2165
+ type: "tool-input-end",
2166
+ id: currentToolCall.toolCallId
2167
+ });
2168
+ const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
2169
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
2170
+ options,
2171
+ "Could not complete streaming XML tool call at finish.",
2172
+ { toolCall: unfinishedContent, error }
2173
+ );
2174
+ if (shouldEmitRawToolCallTextOnError2(options)) {
2175
+ flushText(controller, unfinishedContent);
2176
+ }
2177
+ }
2178
+ buffer = "";
2179
+ currentToolCall = null;
2180
+ };
1312
2181
  const processBuffer = createProcessBufferHandler(
1313
2182
  () => buffer,
1314
2183
  (newBuffer) => {
@@ -1322,17 +2191,17 @@ var xmlProtocol = (protocolOptions) => {
1322
2191
  options,
1323
2192
  toolNames,
1324
2193
  flushText,
1325
- parseOptions
2194
+ parseOptions,
2195
+ emitToolInputProgress2,
2196
+ emitToolInputStart
1326
2197
  );
1327
2198
  return new TransformStream({
2199
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Stateful stream parsing requires branching over chunk lifecycle and parser states.
1328
2200
  transform(chunk, controller) {
1329
2201
  var _a2;
1330
2202
  if (chunk.type === "finish") {
1331
2203
  if (currentToolCall) {
1332
- const unfinishedContent = `<${currentToolCall.name}>${currentToolCall.content}${buffer}`;
1333
- flushText(controller, unfinishedContent);
1334
- buffer = "";
1335
- currentToolCall = null;
2204
+ finalizeUnclosedToolCall(controller);
1336
2205
  } else if (buffer) {
1337
2206
  flushText(controller, buffer);
1338
2207
  buffer = "";
@@ -1342,7 +2211,8 @@ var xmlProtocol = (protocolOptions) => {
1342
2211
  return;
1343
2212
  }
1344
2213
  if (chunk.type !== "text-delta") {
1345
- if (buffer) {
2214
+ if (currentToolCall) {
2215
+ } else if (buffer) {
1346
2216
  flushText(controller, buffer);
1347
2217
  buffer = "";
1348
2218
  }
@@ -1355,10 +2225,7 @@ var xmlProtocol = (protocolOptions) => {
1355
2225
  },
1356
2226
  flush(controller) {
1357
2227
  if (currentToolCall) {
1358
- const unfinishedContent = `<${currentToolCall.name}>${currentToolCall.content || ""}${buffer}`;
1359
- flushText(controller, unfinishedContent);
1360
- buffer = "";
1361
- currentToolCall = null;
2228
+ finalizeUnclosedToolCall(controller);
1362
2229
  } else if (buffer) {
1363
2230
  flushText(controller, buffer);
1364
2231
  buffer = "";
@@ -1386,7 +2253,205 @@ var xmlProtocol = (protocolOptions) => {
1386
2253
 
1387
2254
  // src/core/protocols/yaml-protocol.ts
1388
2255
  import YAML from "yaml";
2256
+ function shouldEmitRawToolCallTextOnError3(options) {
2257
+ return (options == null ? void 0 : options.emitRawToolCallTextOnError) === true;
2258
+ }
2259
+ var selfClosingTagCache2 = /* @__PURE__ */ new Map();
2260
+ function getSelfClosingTagPattern2(toolName) {
2261
+ let pattern = selfClosingTagCache2.get(toolName);
2262
+ if (!pattern) {
2263
+ pattern = new RegExp(`<\\s*${escapeRegExp(toolName)}\\s*/>`, "g");
2264
+ selfClosingTagCache2.set(toolName, pattern);
2265
+ }
2266
+ return pattern;
2267
+ }
1389
2268
  var LEADING_WHITESPACE_RE = /^(\s*)/;
2269
+ var INCOMPLETE_MAPPING_TAIL_RE = /^[^:[\]{}-][^:]*:\s*$/;
2270
+ var INCOMPLETE_SEQUENCE_TAIL_RE = /^-\s*$/;
2271
+ var BLOCK_SCALAR_KEY_RE = /:\s*[|>][-+0-9]*\s*$/;
2272
+ var PLAIN_MAPPING_VALUE_RE = /^[^:[\]{}-][^:]*:\s*(.+)$/;
2273
+ var PLAIN_SEQUENCE_VALUE_RE = /^-\s+(.+)$/;
2274
+ function normalizeYamlContent(yamlContent) {
2275
+ let normalized = yamlContent;
2276
+ if (normalized.startsWith("\n")) {
2277
+ normalized = normalized.slice(1);
2278
+ }
2279
+ const lines = normalized.split("\n");
2280
+ const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
2281
+ if (nonEmptyLines.length === 0) {
2282
+ return { normalized: "", nonEmptyLines };
2283
+ }
2284
+ const minIndent = Math.min(
2285
+ ...nonEmptyLines.map((line) => {
2286
+ const match = line.match(LEADING_WHITESPACE_RE);
2287
+ return match ? match[1].length : 0;
2288
+ })
2289
+ );
2290
+ if (minIndent > 0) {
2291
+ normalized = lines.map((line) => line.slice(minIndent)).join("\n");
2292
+ }
2293
+ return { normalized, nonEmptyLines };
2294
+ }
2295
+ function parseYamlDocumentAsMapping(normalized) {
2296
+ try {
2297
+ const doc = YAML.parseDocument(normalized);
2298
+ const errors = doc.errors.map((e) => e.message);
2299
+ const result = doc.toJSON();
2300
+ if (result === null) {
2301
+ return { value: {}, errors };
2302
+ }
2303
+ if (typeof result !== "object" || Array.isArray(result)) {
2304
+ return { value: null, errors };
2305
+ }
2306
+ return { value: result, errors };
2307
+ } catch (error) {
2308
+ return {
2309
+ value: null,
2310
+ errors: [
2311
+ error instanceof Error ? error.message : "Unknown YAML parsing error"
2312
+ ]
2313
+ };
2314
+ }
2315
+ }
2316
+ function getLastMeaningfulLineInfo(input) {
2317
+ var _a;
2318
+ const lines = input.split("\n");
2319
+ let index = lines.length - 1;
2320
+ while (index >= 0) {
2321
+ const raw = (_a = lines[index]) != null ? _a : "";
2322
+ const trimmed = raw.trim();
2323
+ if (trimmed.length > 0 && !trimmed.startsWith("#")) {
2324
+ return {
2325
+ index,
2326
+ raw,
2327
+ trimmed,
2328
+ indent: raw.length - raw.trimStart().length
2329
+ };
2330
+ }
2331
+ index -= 1;
2332
+ }
2333
+ return null;
2334
+ }
2335
+ function dropLastMeaningfulLine(input) {
2336
+ const lineInfo = getLastMeaningfulLineInfo(input);
2337
+ if (!lineInfo) {
2338
+ return null;
2339
+ }
2340
+ return input.split("\n").slice(0, lineInfo.index).join("\n").trimEnd();
2341
+ }
2342
+ function hasIncompleteMappingTail(normalized) {
2343
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
2344
+ if (!lineInfo) {
2345
+ return false;
2346
+ }
2347
+ return INCOMPLETE_MAPPING_TAIL_RE.test(lineInfo.trimmed);
2348
+ }
2349
+ function hasIncompleteSequenceTail(normalized) {
2350
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
2351
+ if (!lineInfo) {
2352
+ return false;
2353
+ }
2354
+ return INCOMPLETE_SEQUENCE_TAIL_RE.test(lineInfo.trimmed);
2355
+ }
2356
+ function hasSplitNestedKeyTail(normalized) {
2357
+ var _a;
2358
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
2359
+ if (!lineInfo) {
2360
+ return false;
2361
+ }
2362
+ const { trimmed, indent, index } = lineInfo;
2363
+ if (indent === 0) {
2364
+ return false;
2365
+ }
2366
+ if (trimmed.startsWith("#") || trimmed.startsWith("-") || trimmed.includes(":")) {
2367
+ return false;
2368
+ }
2369
+ const lines = normalized.split("\n");
2370
+ let parentIndex = index - 1;
2371
+ while (parentIndex >= 0) {
2372
+ const parentRaw = (_a = lines[parentIndex]) != null ? _a : "";
2373
+ const parentTrimmed = parentRaw.trim();
2374
+ if (parentTrimmed.length === 0 || parentTrimmed.startsWith("#")) {
2375
+ parentIndex -= 1;
2376
+ continue;
2377
+ }
2378
+ const parentIndent = parentRaw.length - parentRaw.trimStart().length;
2379
+ if (parentIndent >= indent) {
2380
+ parentIndex -= 1;
2381
+ continue;
2382
+ }
2383
+ if (!parentTrimmed.endsWith(":")) {
2384
+ return false;
2385
+ }
2386
+ if (BLOCK_SCALAR_KEY_RE.test(parentTrimmed)) {
2387
+ return false;
2388
+ }
2389
+ return true;
2390
+ }
2391
+ return false;
2392
+ }
2393
+ function extractTrailingPlainScalarValue(line) {
2394
+ var _a;
2395
+ if (BLOCK_SCALAR_KEY_RE.test(line)) {
2396
+ return null;
2397
+ }
2398
+ const mappingMatch = line.match(PLAIN_MAPPING_VALUE_RE);
2399
+ const sequenceMatch = line.match(PLAIN_SEQUENCE_VALUE_RE);
2400
+ const value = (_a = mappingMatch == null ? void 0 : mappingMatch[1]) != null ? _a : sequenceMatch == null ? void 0 : sequenceMatch[1];
2401
+ if (!value) {
2402
+ return null;
2403
+ }
2404
+ const trimmedValue = value.trim();
2405
+ if (trimmedValue.length === 0) {
2406
+ return null;
2407
+ }
2408
+ if (trimmedValue.startsWith('"') || trimmedValue.startsWith("'")) {
2409
+ return null;
2410
+ }
2411
+ if (trimmedValue.startsWith("{") || trimmedValue.startsWith("[") || trimmedValue.startsWith("|") || trimmedValue.startsWith(">")) {
2412
+ return null;
2413
+ }
2414
+ return trimmedValue;
2415
+ }
2416
+ function hasUnterminatedPlainScalarTail(normalized) {
2417
+ if (normalized.endsWith("\n")) {
2418
+ return false;
2419
+ }
2420
+ const lineInfo = getLastMeaningfulLineInfo(normalized);
2421
+ if (!lineInfo) {
2422
+ return false;
2423
+ }
2424
+ return extractTrailingPlainScalarValue(lineInfo.trimmed) != null;
2425
+ }
2426
+ function hasUnstableProgressTail(normalized) {
2427
+ return hasIncompleteMappingTail(normalized) || hasIncompleteSequenceTail(normalized) || hasSplitNestedKeyTail(normalized) || hasUnterminatedPlainScalarTail(normalized);
2428
+ }
2429
+ function trimTrailingNewlineInUnknown(value) {
2430
+ if (typeof value === "string") {
2431
+ if (value.endsWith("\n")) {
2432
+ return value.slice(0, -1);
2433
+ }
2434
+ return value;
2435
+ }
2436
+ if (Array.isArray(value)) {
2437
+ return value.map((item) => trimTrailingNewlineInUnknown(item));
2438
+ }
2439
+ if (value && typeof value === "object") {
2440
+ return Object.fromEntries(
2441
+ Object.entries(value).map(([key, item]) => [
2442
+ key,
2443
+ trimTrailingNewlineInUnknown(item)
2444
+ ])
2445
+ );
2446
+ }
2447
+ return value;
2448
+ }
2449
+ function stabilizeParsedValueForStreamProgress(value, source) {
2450
+ if (source.endsWith("\n")) {
2451
+ return value;
2452
+ }
2453
+ return trimTrailingNewlineInUnknown(value);
2454
+ }
1390
2455
  function findClosingTagEnd(text, contentStart, toolName) {
1391
2456
  let pos = contentStart;
1392
2457
  let depth = 1;
@@ -1458,9 +2523,9 @@ function findEarliestTagPosition(openIdx, selfIdx) {
1458
2523
  function collectToolCallsForName(text, toolName) {
1459
2524
  const toolCalls = [];
1460
2525
  let searchIndex = 0;
1461
- const selfTagRegex = new RegExp(`<${toolName}\\s*/>`, "g");
2526
+ const startTag = `<${toolName}>`;
2527
+ const selfTagRegex = getSelfClosingTagPattern2(toolName);
1462
2528
  while (searchIndex < text.length) {
1463
- const startTag = `<${toolName}>`;
1464
2529
  const openIdx = text.indexOf(startTag, searchIndex);
1465
2530
  selfTagRegex.lastIndex = searchIndex;
1466
2531
  const selfMatch = selfTagRegex.exec(text);
@@ -1510,47 +2575,48 @@ function findToolCalls2(text, toolNames) {
1510
2575
  return toolCalls.sort((a, b) => a.startIndex - b.startIndex);
1511
2576
  }
1512
2577
  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);
2578
+ var _a, _b;
2579
+ const { normalized, nonEmptyLines } = normalizeYamlContent(yamlContent);
1520
2580
  if (nonEmptyLines.length === 0) {
1521
2581
  return {};
1522
2582
  }
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");
2583
+ const parsed = parseYamlDocumentAsMapping(normalized);
2584
+ if (parsed.errors.length > 0) {
2585
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "YAML parse error", {
2586
+ errors: parsed.errors
2587
+ });
2588
+ return null;
1531
2589
  }
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;
2590
+ if (parsed.value === null) {
2591
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, "YAML content must be a key-value mapping", {
2592
+ got: "non-mapping"
2593
+ });
2594
+ return null;
2595
+ }
2596
+ return parsed.value;
2597
+ }
2598
+ function parseYamlContentForStreamProgress(yamlContent) {
2599
+ const { normalized, nonEmptyLines } = normalizeYamlContent(yamlContent);
2600
+ if (nonEmptyLines.length === 0) {
2601
+ return {};
2602
+ }
2603
+ let candidate = normalized;
2604
+ while (true) {
2605
+ const parsed = parseYamlDocumentAsMapping(candidate);
2606
+ if (parsed.errors.length === 0 && !hasUnstableProgressTail(candidate)) {
2607
+ if (candidate.trim().length === 0 && normalized.trim().length > 0) {
2608
+ return null;
2609
+ }
2610
+ return stabilizeParsedValueForStreamProgress(parsed.value, candidate);
1539
2611
  }
1540
- const result = doc.toJSON();
1541
- if (result === null) {
1542
- return {};
2612
+ const truncated = dropLastMeaningfulLine(candidate);
2613
+ if (truncated == null) {
2614
+ return null;
1543
2615
  }
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
- });
2616
+ if (truncated === candidate) {
1548
2617
  return null;
1549
2618
  }
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;
2619
+ candidate = truncated;
1554
2620
  }
1555
2621
  }
1556
2622
  function processToolCallMatch(text, tc, currentIndex, processedElements, options) {
@@ -1566,7 +2632,7 @@ function processToolCallMatch(text, tc, currentIndex, processedElements, options
1566
2632
  if (parsedArgs !== null) {
1567
2633
  processedElements.push({
1568
2634
  type: "tool-call",
1569
- toolCallId: generateId(),
2635
+ toolCallId: generateToolCallId(),
1570
2636
  toolName: tc.toolName,
1571
2637
  input: JSON.stringify(parsedArgs)
1572
2638
  });
@@ -1579,38 +2645,6 @@ function processToolCallMatch(text, tc, currentIndex, processedElements, options
1579
2645
  }
1580
2646
  return tc.endIndex;
1581
2647
  }
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
2648
  function findEarliestToolTag2(buffer, toolNames) {
1615
2649
  let bestIndex = -1;
1616
2650
  let bestName = "";
@@ -1618,8 +2652,9 @@ function findEarliestToolTag2(buffer, toolNames) {
1618
2652
  let bestTagLength = 0;
1619
2653
  for (const name of toolNames) {
1620
2654
  const openTag = `<${name}>`;
1621
- const selfTagRegex = new RegExp(`<${name}\\s*/>`);
2655
+ const selfTagRegex = getSelfClosingTagPattern2(name);
1622
2656
  const idxOpen = buffer.indexOf(openTag);
2657
+ selfTagRegex.lastIndex = 0;
1623
2658
  const selfMatch = selfTagRegex.exec(buffer);
1624
2659
  const idxSelf = selfMatch ? selfMatch.index : -1;
1625
2660
  if (idxOpen !== -1 && (bestIndex === -1 || idxOpen < bestIndex)) {
@@ -1642,6 +2677,29 @@ function findEarliestToolTag2(buffer, toolNames) {
1642
2677
  tagLength: bestTagLength
1643
2678
  };
1644
2679
  }
2680
+ function stripTrailingPartialCloseTag(content, toolName) {
2681
+ const closeTag = `</${toolName}>`;
2682
+ const lastLineBreakIndex = Math.max(
2683
+ content.lastIndexOf("\n"),
2684
+ content.lastIndexOf("\r")
2685
+ );
2686
+ const lineStartIndex = lastLineBreakIndex === -1 ? 0 : lastLineBreakIndex + 1;
2687
+ const trailingLine = content.slice(lineStartIndex);
2688
+ const trimmedTrailingLine = trailingLine.trim();
2689
+ if (trimmedTrailingLine.length === 0 || !trimmedTrailingLine.startsWith("</") || trimmedTrailingLine === closeTag || !closeTag.startsWith(trimmedTrailingLine)) {
2690
+ return content;
2691
+ }
2692
+ const leadingWhitespaceLength = trailingLine.length - trailingLine.trimStart().length;
2693
+ const preservedLeadingWhitespace = trailingLine.slice(
2694
+ 0,
2695
+ leadingWhitespaceLength
2696
+ );
2697
+ const contentWithoutPartial = `${content.slice(
2698
+ 0,
2699
+ lineStartIndex
2700
+ )}${preservedLeadingWhitespace}`;
2701
+ return contentWithoutPartial.trimEnd();
2702
+ }
1645
2703
  var yamlProtocol = (_protocolOptions) => {
1646
2704
  return {
1647
2705
  formatTools({ tools, toolSystemPromptTemplate }) {
@@ -1702,7 +2760,7 @@ ${yamlContent}</${toolCall.toolName}>`;
1702
2760
  let currentToolCall = null;
1703
2761
  let currentTextId = null;
1704
2762
  let hasEmittedTextStart = false;
1705
- const flushText = createFlushTextHandler2(
2763
+ const flushText = createFlushTextHandler(
1706
2764
  () => currentTextId,
1707
2765
  (newId) => {
1708
2766
  currentTextId = newId;
@@ -1712,33 +2770,128 @@ ${yamlContent}</${toolCall.toolName}>`;
1712
2770
  hasEmittedTextStart = value;
1713
2771
  }
1714
2772
  );
1715
- const processToolCallEnd = (controller, toolContent, toolName) => {
2773
+ const emitToolInputProgress2 = (controller, toolContent) => {
2774
+ if (!currentToolCall) {
2775
+ return;
2776
+ }
2777
+ const parsedArgs = parseYamlContentForStreamProgress(toolContent);
2778
+ if (parsedArgs === null) {
2779
+ return;
2780
+ }
2781
+ const fullInput = JSON.stringify(parsedArgs);
2782
+ if (fullInput === "{}" && toolContent.trim().length === 0) {
2783
+ return;
2784
+ }
2785
+ const prefixCandidate = toIncompleteJsonPrefix(fullInput);
2786
+ emitPrefixDelta({
2787
+ controller,
2788
+ id: currentToolCall.toolCallId,
2789
+ state: currentToolCall,
2790
+ candidate: prefixCandidate
2791
+ });
2792
+ };
2793
+ const processToolCallEnd = (controller, toolContent, toolName, toolCallId) => {
1716
2794
  var _a;
1717
2795
  const parsedArgs = parseYamlContent(toolContent, options);
1718
2796
  flushText(controller);
1719
2797
  if (parsedArgs !== null) {
2798
+ const finalInput = JSON.stringify(parsedArgs);
2799
+ if (currentToolCall && currentToolCall.toolCallId === toolCallId) {
2800
+ emitFinalRemainder({
2801
+ controller,
2802
+ id: toolCallId,
2803
+ state: currentToolCall,
2804
+ finalFullJson: finalInput,
2805
+ onMismatch: options == null ? void 0 : options.onError
2806
+ });
2807
+ }
2808
+ controller.enqueue({
2809
+ type: "tool-input-end",
2810
+ id: toolCallId
2811
+ });
1720
2812
  controller.enqueue({
1721
2813
  type: "tool-call",
1722
- toolCallId: generateId(),
2814
+ toolCallId,
1723
2815
  toolName,
1724
- input: JSON.stringify(parsedArgs)
2816
+ input: finalInput
1725
2817
  });
1726
2818
  } else {
2819
+ controller.enqueue({
2820
+ type: "tool-input-end",
2821
+ id: toolCallId
2822
+ });
1727
2823
  const original = `<${toolName}>${toolContent}</${toolName}>`;
1728
2824
  (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "Could not parse streaming YAML tool call", {
1729
2825
  toolCall: original
1730
2826
  });
1731
- flushText(controller, original);
2827
+ if (shouldEmitRawToolCallTextOnError3(options)) {
2828
+ flushText(controller, original);
2829
+ }
2830
+ }
2831
+ };
2832
+ const finalizeUnclosedToolCall = (controller) => {
2833
+ var _a;
2834
+ if (!currentToolCall) {
2835
+ return;
2836
+ }
2837
+ emitToolInputProgress2(controller, buffer);
2838
+ const { name: toolName, toolCallId } = currentToolCall;
2839
+ const reconciledBuffer = stripTrailingPartialCloseTag(buffer, toolName);
2840
+ const parsedArgs = parseYamlContent(reconciledBuffer, options);
2841
+ flushText(controller);
2842
+ if (parsedArgs !== null) {
2843
+ const finalInput = JSON.stringify(parsedArgs);
2844
+ emitFinalRemainder({
2845
+ controller,
2846
+ id: toolCallId,
2847
+ state: currentToolCall,
2848
+ finalFullJson: finalInput,
2849
+ onMismatch: options == null ? void 0 : options.onError
2850
+ });
2851
+ controller.enqueue({
2852
+ type: "tool-input-end",
2853
+ id: toolCallId
2854
+ });
2855
+ controller.enqueue({
2856
+ type: "tool-call",
2857
+ toolCallId,
2858
+ toolName,
2859
+ input: finalInput
2860
+ });
2861
+ } else {
2862
+ controller.enqueue({
2863
+ type: "tool-input-end",
2864
+ id: toolCallId
2865
+ });
2866
+ const unfinishedContent = `<${toolName}>${buffer}`;
2867
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
2868
+ options,
2869
+ "Could not complete streaming YAML tool call at finish.",
2870
+ { toolCall: unfinishedContent }
2871
+ );
2872
+ if (shouldEmitRawToolCallTextOnError3(options)) {
2873
+ flushText(controller, unfinishedContent);
2874
+ }
1732
2875
  }
2876
+ buffer = "";
2877
+ currentToolCall = null;
1733
2878
  };
1734
2879
  const handlePendingToolCall = (controller, endTag, toolName) => {
2880
+ var _a;
1735
2881
  const endIdx = buffer.indexOf(endTag);
1736
2882
  if (endIdx === -1) {
2883
+ emitToolInputProgress2(controller, buffer);
1737
2884
  return false;
1738
2885
  }
1739
2886
  const content = buffer.substring(0, endIdx);
2887
+ emitToolInputProgress2(controller, content);
1740
2888
  buffer = buffer.substring(endIdx + endTag.length);
1741
- processToolCallEnd(controller, content, toolName);
2889
+ processToolCallEnd(
2890
+ controller,
2891
+ content,
2892
+ toolName,
2893
+ (_a = currentToolCall == null ? void 0 : currentToolCall.toolCallId) != null ? _a : generateToolCallId()
2894
+ );
1742
2895
  currentToolCall = null;
1743
2896
  return true;
1744
2897
  };
@@ -1755,13 +2908,35 @@ ${yamlContent}</${toolCall.toolName}>`;
1755
2908
  if (tagIndex > 0) {
1756
2909
  flushText(controller, buffer.substring(0, tagIndex));
1757
2910
  }
2911
+ flushText(controller);
1758
2912
  if (selfClosing) {
1759
2913
  buffer = buffer.substring(tagIndex + tagLength);
1760
- processToolCallEnd(controller, "", tagName);
2914
+ const toolCallId = generateToolCallId();
2915
+ currentToolCall = {
2916
+ name: tagName,
2917
+ toolCallId,
2918
+ emittedInput: ""
2919
+ };
2920
+ controller.enqueue({
2921
+ type: "tool-input-start",
2922
+ id: toolCallId,
2923
+ toolName: tagName
2924
+ });
2925
+ processToolCallEnd(controller, "", tagName, toolCallId);
2926
+ currentToolCall = null;
1761
2927
  } else {
1762
2928
  const startTag = `<${tagName}>`;
1763
2929
  buffer = buffer.substring(tagIndex + startTag.length);
1764
- currentToolCall = { name: tagName, content: "" };
2930
+ currentToolCall = {
2931
+ name: tagName,
2932
+ toolCallId: generateToolCallId(),
2933
+ emittedInput: ""
2934
+ };
2935
+ controller.enqueue({
2936
+ type: "tool-input-start",
2937
+ id: currentToolCall.toolCallId,
2938
+ toolName: tagName
2939
+ });
1765
2940
  }
1766
2941
  };
1767
2942
  const processBuffer = (controller) => {
@@ -1790,10 +2965,7 @@ ${yamlContent}</${toolCall.toolName}>`;
1790
2965
  var _a;
1791
2966
  if (chunk.type === "finish") {
1792
2967
  if (currentToolCall) {
1793
- const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
1794
- flushText(controller, unfinishedContent);
1795
- buffer = "";
1796
- currentToolCall = null;
2968
+ finalizeUnclosedToolCall(controller);
1797
2969
  } else if (buffer) {
1798
2970
  flushText(controller, buffer);
1799
2971
  buffer = "";
@@ -1803,7 +2975,7 @@ ${yamlContent}</${toolCall.toolName}>`;
1803
2975
  return;
1804
2976
  }
1805
2977
  if (chunk.type !== "text-delta") {
1806
- if (buffer) {
2978
+ if (!currentToolCall && buffer) {
1807
2979
  flushText(controller, buffer);
1808
2980
  buffer = "";
1809
2981
  }
@@ -1816,10 +2988,7 @@ ${yamlContent}</${toolCall.toolName}>`;
1816
2988
  },
1817
2989
  flush(controller) {
1818
2990
  if (currentToolCall) {
1819
- const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
1820
- flushText(controller, unfinishedContent);
1821
- buffer = "";
1822
- currentToolCall = null;
2991
+ finalizeUnclosedToolCall(controller);
1823
2992
  } else if (buffer) {
1824
2993
  flushText(controller, buffer);
1825
2994
  buffer = "";
@@ -1997,9 +3166,6 @@ function hasInputProperty(obj) {
1997
3166
  return typeof obj === "object" && obj !== null && "input" in obj;
1998
3167
  }
1999
3168
 
2000
- // src/generate-handler.ts
2001
- import { generateId as generateId2 } from "@ai-sdk/provider-utils";
2002
-
2003
3169
  // src/core/utils/generated-text-json-recovery.ts
2004
3170
  function isRecord(value) {
2005
3171
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -2138,7 +3304,7 @@ function mergeJsonCandidatesByStart(tagged, codeBlocks, balanced) {
2138
3304
  function toToolCallPart(candidate) {
2139
3305
  return {
2140
3306
  type: "tool-call",
2141
- toolCallId: generateId(),
3307
+ toolCallId: generateToolCallId(),
2142
3308
  toolName: candidate.toolName,
2143
3309
  input: candidate.input
2144
3310
  };
@@ -2365,7 +3531,7 @@ async function handleToolChoice(doGenerate, params, tools) {
2365
3531
  }
2366
3532
  const toolCall = {
2367
3533
  type: "tool-call",
2368
- toolCallId: generateId2(),
3534
+ toolCallId: generateToolCallId(),
2369
3535
  toolName,
2370
3536
  input
2371
3537
  };
@@ -2875,7 +4041,6 @@ unit: celsius
2875
4041
  }
2876
4042
 
2877
4043
  // src/stream-handler.ts
2878
- import { generateId as generateId3 } from "@ai-sdk/provider-utils";
2879
4044
  async function wrapStream({
2880
4045
  protocol,
2881
4046
  doStream,
@@ -2913,10 +4078,22 @@ async function wrapStream({
2913
4078
  }
2914
4079
  )
2915
4080
  ).pipeThrough(protocol.createStreamParser({ tools, options }));
4081
+ let seenToolCall = false;
2916
4082
  const v3Stream = coreStream.pipeThrough(
2917
4083
  new TransformStream({
2918
4084
  transform(part, controller) {
2919
- const normalizedPart = part.type === "tool-call" ? coerceToolCallPart(part, tools) : part;
4085
+ let normalizedPart = part.type === "tool-call" ? coerceToolCallPart(part, tools) : part;
4086
+ if (normalizedPart.type === "tool-call") {
4087
+ seenToolCall = true;
4088
+ }
4089
+ if (normalizedPart.type === "finish" && seenToolCall && normalizedPart.finishReason.unified === "stop") {
4090
+ normalizedPart = {
4091
+ ...normalizedPart,
4092
+ finishReason: normalizeToolCallsFinishReason(
4093
+ normalizedPart.finishReason
4094
+ )
4095
+ };
4096
+ }
2920
4097
  if (debugLevel === "stream") {
2921
4098
  logParsedChunk(normalizedPart);
2922
4099
  }
@@ -2953,7 +4130,7 @@ async function toolChoiceStream({
2953
4130
  start(controller) {
2954
4131
  controller.enqueue({
2955
4132
  type: "tool-call",
2956
- toolCallId: generateId3(),
4133
+ toolCallId: generateToolCallId(),
2957
4134
  toolName,
2958
4135
  input
2959
4136
  });
@@ -3465,4 +4642,4 @@ export {
3465
4642
  xmlToolMiddleware,
3466
4643
  yamlToolMiddleware
3467
4644
  };
3468
- //# sourceMappingURL=chunk-NAQSTPDQ.js.map
4645
+ //# sourceMappingURL=chunk-DPGORNPB.js.map