@ai-sdk-tool/parser 3.3.2 → 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.
@@ -2,13 +2,15 @@ import {
2
2
  escapeRegExp,
3
3
  parse as parse2,
4
4
  stringify
5
- } from "./chunk-ZDBNJWLY.js";
5
+ } from "./chunk-CXWS24JX.js";
6
6
  import {
7
7
  parse
8
8
  } from "./chunk-IX4FJELL.js";
9
9
  import {
10
- coerceBySchema
11
- } from "./chunk-OUGMLYAW.js";
10
+ coerceBySchema,
11
+ getSchemaType,
12
+ unwrapJsonSchema
13
+ } from "./chunk-2KK5BDZF.js";
12
14
 
13
15
  // src/core/utils/debug.ts
14
16
  var LINE_SPLIT_REGEX = /\r?\n/;
@@ -185,7 +187,25 @@ function getPotentialStartIndex(text, searchedText) {
185
187
 
186
188
  // src/core/utils/id.ts
187
189
  function generateId() {
188
- 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)}`;
189
209
  }
190
210
 
191
211
  // src/core/utils/protocol-utils.ts
@@ -194,17 +214,55 @@ function addTextSegment(text, processedElements) {
194
214
  processedElements.push({ type: "text", text });
195
215
  }
196
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
+ }
197
249
 
198
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
+ }
199
257
  function processToolCallJson(toolCallJson, fullMatch, processedElements, options) {
200
- var _a, _b;
258
+ var _a;
201
259
  try {
202
260
  const parsedToolCall = parse(toolCallJson);
203
261
  processedElements.push({
204
262
  type: "tool-call",
205
- toolCallId: generateId(),
263
+ toolCallId: generateToolCallId(),
206
264
  toolName: parsedToolCall.name,
207
- input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
265
+ input: canonicalizeToolInput(parsedToolCall.arguments)
208
266
  });
209
267
  } catch (error) {
210
268
  logParseFailure({
@@ -213,7 +271,7 @@ function processToolCallJson(toolCallJson, fullMatch, processedElements, options
213
271
  snippet: fullMatch,
214
272
  error
215
273
  });
216
- (_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(
217
275
  options,
218
276
  "Could not process JSON tool call, keeping original text.",
219
277
  { toolCall: fullMatch, error }
@@ -234,6 +292,293 @@ function processMatchedToolCall(context) {
234
292
  }
235
293
  return startIndex + match[0].length;
236
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
+ }
237
582
  function flushBuffer(state, controller, toolCallStart) {
238
583
  if (state.buffer.length === 0) {
239
584
  return;
@@ -264,44 +609,77 @@ function closeTextBlock(state, controller) {
264
609
  state.hasEmittedTextStart = false;
265
610
  }
266
611
  }
267
- function emitIncompleteToolCall(state, controller, toolCallStart) {
268
- 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;
269
616
  return;
270
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);
271
631
  logParseFailure({
272
632
  phase: "stream",
273
- reason: "Incomplete streaming tool call segment emitted as text",
274
- snippet: `${toolCallStart}${state.currentToolCallJson}`
275
- });
276
- const errorId = generateId();
277
- const errorContent = `${toolCallStart}${state.currentToolCallJson}`;
278
- controller.enqueue({
279
- type: "text-start",
280
- id: errorId
281
- });
282
- controller.enqueue({
283
- type: "text-delta",
284
- id: errorId,
285
- delta: errorContent
286
- });
287
- controller.enqueue({
288
- type: "text-end",
289
- 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
290
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
+ );
291
658
  state.currentToolCallJson = "";
659
+ state.isInsideToolCall = false;
292
660
  }
293
- function handleFinishChunk(state, controller, toolCallStart, chunk) {
294
- 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) {
295
673
  flushBuffer(state, controller, toolCallStart);
296
674
  }
297
675
  closeTextBlock(state, controller);
298
- emitIncompleteToolCall(state, controller, toolCallStart);
299
676
  controller.enqueue(chunk);
300
677
  }
301
678
  function publishText(text, state, controller) {
302
679
  if (state.isInsideToolCall) {
303
680
  closeTextBlock(state, controller);
304
681
  state.currentToolCallJson += text;
682
+ emitToolInputProgress(state, controller);
305
683
  } else if (text.length > 0) {
306
684
  if (!state.currentTextId) {
307
685
  state.currentTextId = generateId();
@@ -319,42 +697,40 @@ function publishText(text, state, controller) {
319
697
  }
320
698
  }
321
699
  function emitToolCall(context) {
322
- var _a, _b;
700
+ var _a;
323
701
  const { state, controller, toolCallStart, toolCallEnd, options } = context;
324
702
  try {
325
703
  const parsedToolCall = parse(state.currentToolCallJson);
326
- closeTextBlock(state, controller);
327
- controller.enqueue({
328
- type: "tool-call",
329
- toolCallId: generateId(),
330
- toolName: parsedToolCall.name,
331
- input: JSON.stringify((_a = parsedToolCall.arguments) != null ? _a : {})
332
- });
704
+ emitToolCallFromParsed(state, controller, parsedToolCall);
333
705
  } catch (error) {
706
+ const errorContent = `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`;
707
+ const shouldEmitRawFallback = shouldEmitRawToolCallTextOnError(options);
334
708
  logParseFailure({
335
709
  phase: "stream",
336
710
  reason: "Failed to parse streaming tool call JSON segment",
337
- snippet: `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`,
711
+ snippet: errorContent,
338
712
  error
339
713
  });
340
- const errorId = generateId();
341
- const errorContent = `${toolCallStart}${state.currentToolCallJson}${toolCallEnd}`;
342
- controller.enqueue({
343
- type: "text-start",
344
- id: errorId
345
- });
346
- controller.enqueue({
347
- type: "text-delta",
348
- id: errorId,
349
- delta: errorContent
350
- });
351
- controller.enqueue({
352
- type: "text-end",
353
- id: errorId
354
- });
355
- (_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(
356
732
  options,
357
- "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.",
358
734
  {
359
735
  toolCall: errorContent
360
736
  }
@@ -370,6 +746,7 @@ function processTagMatch(context) {
370
746
  } else {
371
747
  state.currentToolCallJson = "";
372
748
  state.isInsideToolCall = true;
749
+ state.activeToolInput = null;
373
750
  }
374
751
  }
375
752
  function processBufferTags(context) {
@@ -392,8 +769,16 @@ function processBufferTags(context) {
392
769
  );
393
770
  }
394
771
  }
395
- function handlePartialTag(state, controller, toolCallStart) {
772
+ function handlePartialTag(state, controller, toolCallStart, toolCallEnd) {
396
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
+ }
397
782
  return;
398
783
  }
399
784
  const potentialIndex = getPotentialStartIndex(state.buffer, toolCallStart);
@@ -466,13 +851,14 @@ var jsonProtocol = ({
466
851
  buffer: "",
467
852
  currentToolCallJson: "",
468
853
  currentTextId: null,
469
- hasEmittedTextStart: false
854
+ hasEmittedTextStart: false,
855
+ activeToolInput: null
470
856
  };
471
857
  return new TransformStream({
472
858
  transform(chunk, controller) {
473
859
  var _a;
474
860
  if (chunk.type === "finish") {
475
- handleFinishChunk(state, controller, toolCallStart, chunk);
861
+ handleFinishChunk(state, controller, toolCallStart, options, chunk);
476
862
  return;
477
863
  }
478
864
  if (chunk.type !== "text-delta") {
@@ -488,7 +874,7 @@ var jsonProtocol = ({
488
874
  toolCallEnd,
489
875
  options
490
876
  });
491
- handlePartialTag(state, controller, toolCallStart);
877
+ handlePartialTag(state, controller, toolCallStart, toolCallEnd);
492
878
  }
493
879
  });
494
880
  },
@@ -518,11 +904,112 @@ function isTCMProtocolFactory(protocol) {
518
904
  var NAME_CHAR_RE = /[A-Za-z0-9_:-]/;
519
905
  var WHITESPACE_REGEX = /\s/;
520
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
+
981
+ // src/core/utils/xml-root-repair.ts
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*$/;
983
+ function tryRepairXmlSelfClosingRootWithBody(rawText, toolNames) {
984
+ const trimmed = rawText.trim();
985
+ if (trimmed.length === 0) {
986
+ return null;
987
+ }
988
+ const match = trimmed.match(XML_SELF_CLOSING_ROOT_WITH_BODY_REGEX);
989
+ if (!match) {
990
+ return null;
991
+ }
992
+ const rootTag = match[1];
993
+ if (!toolNames.includes(rootTag)) {
994
+ return null;
995
+ }
996
+ const body = match[2].trimEnd();
997
+ if (body.trim().length === 0 || body.includes(`</${rootTag}>`)) {
998
+ return null;
999
+ }
1000
+ return `<${rootTag}>
1001
+ ${body}
1002
+ </${rootTag}>`;
1003
+ }
1004
+
521
1005
  // src/core/protocols/xml-protocol.ts
522
1006
  function getToolSchema(tools, toolName) {
523
1007
  var _a;
524
1008
  return (_a = tools.find((t) => t.name === toolName)) == null ? void 0 : _a.inputSchema;
525
1009
  }
1010
+ function shouldEmitRawToolCallTextOnError2(options) {
1011
+ return (options == null ? void 0 : options.emitRawToolCallTextOnError) === true;
1012
+ }
526
1013
  function processToolCall(params) {
527
1014
  var _a, _b;
528
1015
  const { toolCall, tools, options, text, processedElements, parseOptions } = params;
@@ -535,7 +1022,7 @@ function processToolCall(params) {
535
1022
  const parsed = parse2(toolCall.content, toolSchema, parseConfig);
536
1023
  processedElements.push({
537
1024
  type: "tool-call",
538
- toolCallId: generateId(),
1025
+ toolCallId: generateToolCallId(),
539
1026
  toolName: toolCall.toolName,
540
1027
  input: JSON.stringify(parsed)
541
1028
  });
@@ -552,6 +1039,299 @@ function processToolCall(params) {
552
1039
  processedElements.push({ type: "text", text: originalCallText });
553
1040
  }
554
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
+ }
555
1335
  function handleStreamingToolCallEnd(params) {
556
1336
  var _a, _b;
557
1337
  const {
@@ -571,19 +1351,37 @@ function handleStreamingToolCallEnd(params) {
571
1351
  flushText(ctrl);
572
1352
  try {
573
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
+ });
574
1366
  ctrl.enqueue({
575
1367
  type: "tool-call",
576
- toolCallId: generateId(),
1368
+ toolCallId: currentToolCall.toolCallId,
577
1369
  toolName: currentToolCall.name,
578
- input: JSON.stringify(parsedResult)
1370
+ input: finalInput
579
1371
  });
580
1372
  } catch (error) {
1373
+ ctrl.enqueue({
1374
+ type: "tool-input-end",
1375
+ id: currentToolCall.toolCallId
1376
+ });
581
1377
  const original = `<${currentToolCall.name}>${toolContent}</${currentToolCall.name}>`;
582
1378
  (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, "Could not process streaming XML tool call", {
583
1379
  toolCall: original,
584
1380
  error
585
1381
  });
586
- flushText(ctrl, original);
1382
+ if (shouldEmitRawToolCallTextOnError2(options)) {
1383
+ flushText(ctrl, original);
1384
+ }
587
1385
  }
588
1386
  }
589
1387
  function findClosingTagEndFlexible(text, contentStart, toolName) {
@@ -799,6 +1597,102 @@ function findToolCalls(text, toolNames) {
799
1597
  }
800
1598
  return toolCalls.sort((a, b) => a.startIndex - b.startIndex);
801
1599
  }
1600
+ function handleSpecialToken(depth) {
1601
+ return { depth, lastCompleteEnd: -1, shouldBreak: false };
1602
+ }
1603
+ function handleOpenToken(token, depth, lastCompleteEnd) {
1604
+ if (token.selfClosing) {
1605
+ return {
1606
+ depth,
1607
+ lastCompleteEnd: depth === 0 ? token.nextPos : lastCompleteEnd,
1608
+ shouldBreak: false
1609
+ };
1610
+ }
1611
+ return { depth: depth + 1, lastCompleteEnd, shouldBreak: false };
1612
+ }
1613
+ function handleCloseToken(token, depth) {
1614
+ if (depth <= 0) {
1615
+ return { depth, lastCompleteEnd: -1, shouldBreak: true };
1616
+ }
1617
+ const newDepth = depth - 1;
1618
+ return {
1619
+ depth: newDepth,
1620
+ lastCompleteEnd: newDepth === 0 ? token.nextPos : -1,
1621
+ shouldBreak: false
1622
+ };
1623
+ }
1624
+ function findLinePrefixedXmlBodyEnd(text, bodyStartIndex) {
1625
+ let cursor = bodyStartIndex;
1626
+ let depth = 0;
1627
+ let lastCompleteEnd = -1;
1628
+ while (cursor < text.length) {
1629
+ if (depth === 0) {
1630
+ cursor = consumeWhitespace(text, cursor);
1631
+ if (cursor >= text.length || text.charAt(cursor) !== "<") {
1632
+ break;
1633
+ }
1634
+ }
1635
+ const token = nextTagToken(text, cursor);
1636
+ if (token.kind === "eof") {
1637
+ break;
1638
+ }
1639
+ let result;
1640
+ if (token.kind === "special") {
1641
+ result = handleSpecialToken(depth);
1642
+ } else if (token.kind === "open") {
1643
+ result = handleOpenToken(token, depth, lastCompleteEnd);
1644
+ } else {
1645
+ result = handleCloseToken(token, depth);
1646
+ }
1647
+ depth = result.depth;
1648
+ if (result.lastCompleteEnd !== -1) {
1649
+ lastCompleteEnd = result.lastCompleteEnd;
1650
+ }
1651
+ if (result.shouldBreak) {
1652
+ break;
1653
+ }
1654
+ cursor = token.nextPos;
1655
+ }
1656
+ return lastCompleteEnd;
1657
+ }
1658
+ function findLinePrefixedToolCall(text, toolNames) {
1659
+ var _a;
1660
+ let best = null;
1661
+ for (const toolName of toolNames) {
1662
+ const linePattern = new RegExp(
1663
+ `(^|\\n)[\\t ]*${escapeRegExp(toolName)}[\\t ]*:?[\\t ]*(?:\\r?\\n|$)`,
1664
+ "g"
1665
+ );
1666
+ let match = linePattern.exec(text);
1667
+ while (match !== null) {
1668
+ const prefix = (_a = match[1]) != null ? _a : "";
1669
+ const startIndex = match.index + prefix.length;
1670
+ const contentStart = consumeWhitespace(text, linePattern.lastIndex);
1671
+ if (contentStart >= text.length || text.charAt(contentStart) !== "<") {
1672
+ match = linePattern.exec(text);
1673
+ continue;
1674
+ }
1675
+ const contentEnd = findLinePrefixedXmlBodyEnd(text, contentStart);
1676
+ if (contentEnd === -1 || contentEnd <= contentStart) {
1677
+ match = linePattern.exec(text);
1678
+ continue;
1679
+ }
1680
+ const content = text.slice(contentStart, contentEnd);
1681
+ const candidate = {
1682
+ toolName,
1683
+ startIndex,
1684
+ endIndex: contentEnd,
1685
+ content,
1686
+ segment: text.slice(startIndex, contentEnd)
1687
+ };
1688
+ if (best === null || candidate.startIndex < best.startIndex) {
1689
+ best = candidate;
1690
+ }
1691
+ break;
1692
+ }
1693
+ }
1694
+ return best;
1695
+ }
802
1696
  function findEarliestToolTag(buffer, toolNames) {
803
1697
  var _a, _b;
804
1698
  let bestIndex = -1;
@@ -909,38 +1803,6 @@ function findPotentialToolTagStart(buffer, toolNames) {
909
1803
  }
910
1804
  return -1;
911
1805
  }
912
- function createFlushTextHandler(getCurrentTextId, setCurrentTextId, getHasEmittedTextStart, setHasEmittedTextStart) {
913
- return (controller, text) => {
914
- const content = text;
915
- if (content) {
916
- if (!getCurrentTextId()) {
917
- const newId = generateId();
918
- setCurrentTextId(newId);
919
- controller.enqueue({
920
- type: "text-start",
921
- id: newId
922
- });
923
- setHasEmittedTextStart(true);
924
- }
925
- controller.enqueue({
926
- type: "text-delta",
927
- id: getCurrentTextId(),
928
- delta: content
929
- });
930
- }
931
- const currentTextId = getCurrentTextId();
932
- if (currentTextId && !text) {
933
- if (getHasEmittedTextStart()) {
934
- controller.enqueue({
935
- type: "text-end",
936
- id: currentTextId
937
- });
938
- setHasEmittedTextStart(false);
939
- }
940
- setCurrentTextId(null);
941
- }
942
- };
943
- }
944
1806
  function processToolCallInBuffer(params) {
945
1807
  const {
946
1808
  buffer,
@@ -950,18 +1812,21 @@ function processToolCallInBuffer(params) {
950
1812
  controller,
951
1813
  flushText,
952
1814
  setBuffer,
953
- parseOptions
1815
+ parseOptions,
1816
+ emitToolInputProgress: emitToolInputProgress2
954
1817
  } = params;
955
1818
  const endTagPattern = new RegExp(
956
1819
  `</\\s*${escapeRegExp(currentToolCall.name)}\\s*>`
957
1820
  );
958
1821
  const endMatch = endTagPattern.exec(buffer);
959
1822
  if (!endMatch || endMatch.index === void 0) {
1823
+ emitToolInputProgress2(controller, currentToolCall, buffer);
960
1824
  return { buffer, currentToolCall, shouldBreak: true };
961
1825
  }
962
1826
  const endIdx = endMatch.index;
963
1827
  const endPos = endIdx + endMatch[0].length;
964
1828
  const content = buffer.substring(0, endIdx);
1829
+ emitToolInputProgress2(controller, currentToolCall, content);
965
1830
  const remainder = buffer.substring(endPos);
966
1831
  setBuffer(remainder);
967
1832
  handleStreamingToolCallEnd({
@@ -988,7 +1853,8 @@ function processNoToolCallInBuffer(params) {
988
1853
  tools,
989
1854
  options,
990
1855
  parseOptions,
991
- setBuffer
1856
+ setBuffer,
1857
+ emitToolInputStart
992
1858
  } = params;
993
1859
  const {
994
1860
  index: earliestStartTagIndex,
@@ -1018,9 +1884,10 @@ function processNoToolCallInBuffer(params) {
1018
1884
  if (selfClosing) {
1019
1885
  const newBuffer2 = buffer.substring(earliestStartTagIndex + tagLength);
1020
1886
  setBuffer(newBuffer2);
1887
+ const currentToolCall = emitToolInputStart(controller, earliestToolName);
1021
1888
  handleStreamingToolCallEnd({
1022
1889
  toolContent: "",
1023
- currentToolCall: { name: earliestToolName, content: "" },
1890
+ currentToolCall,
1024
1891
  tools,
1025
1892
  options,
1026
1893
  ctrl: controller,
@@ -1039,12 +1906,12 @@ function processNoToolCallInBuffer(params) {
1039
1906
  setBuffer(newBuffer);
1040
1907
  return {
1041
1908
  buffer: newBuffer,
1042
- currentToolCall: { name: earliestToolName, content: "" },
1909
+ currentToolCall: emitToolInputStart(controller, earliestToolName),
1043
1910
  shouldBreak: false,
1044
1911
  shouldContinue: true
1045
1912
  };
1046
1913
  }
1047
- 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) {
1048
1915
  return (controller) => {
1049
1916
  while (true) {
1050
1917
  const currentToolCall = getCurrentToolCall();
@@ -1057,7 +1924,8 @@ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, se
1057
1924
  controller,
1058
1925
  flushText,
1059
1926
  setBuffer,
1060
- parseOptions
1927
+ parseOptions,
1928
+ emitToolInputProgress: emitToolInputProgress2
1061
1929
  });
1062
1930
  setBuffer(result.buffer);
1063
1931
  setCurrentToolCall(result.currentToolCall);
@@ -1073,7 +1941,8 @@ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, se
1073
1941
  tools,
1074
1942
  options,
1075
1943
  parseOptions,
1076
- setBuffer
1944
+ setBuffer,
1945
+ emitToolInputStart
1077
1946
  });
1078
1947
  setBuffer(result.buffer);
1079
1948
  setCurrentToolCall(result.currentToolCall);
@@ -1088,6 +1957,27 @@ function createProcessBufferHandler(getBuffer, setBuffer, getCurrentToolCall, se
1088
1957
  }
1089
1958
  };
1090
1959
  }
1960
+ function findToolCallsWithFallbacks(text, toolNames) {
1961
+ let parseText = text;
1962
+ let toolCalls = findToolCalls(parseText, toolNames);
1963
+ if (toolCalls.length === 0) {
1964
+ const fallbackToolCall = findLinePrefixedToolCall(parseText, toolNames);
1965
+ if (fallbackToolCall !== null) {
1966
+ toolCalls.push(fallbackToolCall);
1967
+ }
1968
+ }
1969
+ if (toolCalls.length === 0) {
1970
+ const repaired = tryRepairXmlSelfClosingRootWithBody(parseText, toolNames);
1971
+ if (repaired) {
1972
+ const repairedCalls = findToolCalls(repaired, toolNames);
1973
+ if (repairedCalls.length > 0) {
1974
+ parseText = repaired;
1975
+ toolCalls = repairedCalls;
1976
+ }
1977
+ }
1978
+ }
1979
+ return { parseText, toolCalls };
1980
+ }
1091
1981
  var xmlProtocol = (protocolOptions) => {
1092
1982
  var _a;
1093
1983
  const parseOptions = {
@@ -1121,28 +2011,31 @@ var xmlProtocol = (protocolOptions) => {
1121
2011
  }
1122
2012
  const processedElements = [];
1123
2013
  let currentIndex = 0;
1124
- const toolCalls = findToolCalls(text, toolNames);
2014
+ const { parseText, toolCalls } = findToolCallsWithFallbacks(
2015
+ text,
2016
+ toolNames
2017
+ );
1125
2018
  for (const tc of toolCalls) {
1126
2019
  if (tc.startIndex > currentIndex) {
1127
2020
  processedElements.push({
1128
2021
  type: "text",
1129
- text: text.substring(currentIndex, tc.startIndex)
2022
+ text: parseText.substring(currentIndex, tc.startIndex)
1130
2023
  });
1131
2024
  }
1132
2025
  processToolCall({
1133
2026
  toolCall: tc,
1134
2027
  tools,
1135
2028
  options,
1136
- text,
2029
+ text: parseText,
1137
2030
  processedElements,
1138
2031
  parseOptions
1139
2032
  });
1140
2033
  currentIndex = tc.endIndex;
1141
2034
  }
1142
- if (currentIndex < text.length) {
2035
+ if (currentIndex < parseText.length) {
1143
2036
  processedElements.push({
1144
2037
  type: "text",
1145
- text: text.substring(currentIndex)
2038
+ text: parseText.substring(currentIndex)
1146
2039
  });
1147
2040
  }
1148
2041
  return processedElements;
@@ -1163,6 +2056,118 @@ var xmlProtocol = (protocolOptions) => {
1163
2056
  hasEmittedTextStart = value;
1164
2057
  }
1165
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
+ };
1166
2171
  const processBuffer = createProcessBufferHandler(
1167
2172
  () => buffer,
1168
2173
  (newBuffer) => {
@@ -1176,13 +2181,28 @@ var xmlProtocol = (protocolOptions) => {
1176
2181
  options,
1177
2182
  toolNames,
1178
2183
  flushText,
1179
- parseOptions
2184
+ parseOptions,
2185
+ emitToolInputProgress2,
2186
+ emitToolInputStart
1180
2187
  );
1181
2188
  return new TransformStream({
2189
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Stateful stream parsing requires branching over chunk lifecycle and parser states.
1182
2190
  transform(chunk, controller) {
1183
2191
  var _a2;
2192
+ if (chunk.type === "finish") {
2193
+ if (currentToolCall) {
2194
+ finalizeUnclosedToolCall(controller);
2195
+ } else if (buffer) {
2196
+ flushText(controller, buffer);
2197
+ buffer = "";
2198
+ }
2199
+ flushText(controller);
2200
+ controller.enqueue(chunk);
2201
+ return;
2202
+ }
1184
2203
  if (chunk.type !== "text-delta") {
1185
- if (buffer) {
2204
+ if (currentToolCall) {
2205
+ } else if (buffer) {
1186
2206
  flushText(controller, buffer);
1187
2207
  buffer = "";
1188
2208
  }
@@ -1195,10 +2215,7 @@ var xmlProtocol = (protocolOptions) => {
1195
2215
  },
1196
2216
  flush(controller) {
1197
2217
  if (currentToolCall) {
1198
- const unfinishedContent = `<${currentToolCall.name}>${currentToolCall.content || ""}${buffer}`;
1199
- flushText(controller, unfinishedContent);
1200
- buffer = "";
1201
- currentToolCall = null;
2218
+ finalizeUnclosedToolCall(controller);
1202
2219
  } else if (buffer) {
1203
2220
  flushText(controller, buffer);
1204
2221
  buffer = "";
@@ -1226,7 +2243,205 @@ var xmlProtocol = (protocolOptions) => {
1226
2243
 
1227
2244
  // src/core/protocols/yaml-protocol.ts
1228
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
+ }
1229
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
+ }
1230
2445
  function findClosingTagEnd(text, contentStart, toolName) {
1231
2446
  let pos = contentStart;
1232
2447
  let depth = 1;
@@ -1298,7 +2513,7 @@ function findEarliestTagPosition(openIdx, selfIdx) {
1298
2513
  function collectToolCallsForName(text, toolName) {
1299
2514
  const toolCalls = [];
1300
2515
  let searchIndex = 0;
1301
- const selfTagRegex = new RegExp(`<${toolName}\\s*/>`, "g");
2516
+ const selfTagRegex = getSelfClosingTagPattern2(toolName);
1302
2517
  while (searchIndex < text.length) {
1303
2518
  const startTag = `<${toolName}>`;
1304
2519
  const openIdx = text.indexOf(startTag, searchIndex);
@@ -1350,47 +2565,48 @@ function findToolCalls2(text, toolNames) {
1350
2565
  return toolCalls.sort((a, b) => a.startIndex - b.startIndex);
1351
2566
  }
1352
2567
  function parseYamlContent(yamlContent, options) {
1353
- var _a, _b, _c;
1354
- let normalized = yamlContent;
1355
- if (normalized.startsWith("\n")) {
1356
- normalized = normalized.slice(1);
1357
- }
1358
- const lines = normalized.split("\n");
1359
- const nonEmptyLines = lines.filter((line) => line.trim().length > 0);
2568
+ var _a, _b;
2569
+ const { normalized, nonEmptyLines } = normalizeYamlContent(yamlContent);
1360
2570
  if (nonEmptyLines.length === 0) {
1361
2571
  return {};
1362
2572
  }
1363
- const minIndent = Math.min(
1364
- ...nonEmptyLines.map((line) => {
1365
- const match = line.match(LEADING_WHITESPACE_RE);
1366
- return match ? match[1].length : 0;
1367
- })
1368
- );
1369
- if (minIndent > 0) {
1370
- 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;
1371
2579
  }
1372
- try {
1373
- const doc = YAML.parseDocument(normalized);
1374
- if (doc.errors && doc.errors.length > 0) {
1375
- (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "YAML parse error", {
1376
- errors: doc.errors.map((e) => e.message)
1377
- });
1378
- 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);
1379
2601
  }
1380
- const result = doc.toJSON();
1381
- if (result === null) {
1382
- return {};
2602
+ const truncated = dropLastMeaningfulLine(candidate);
2603
+ if (truncated == null) {
2604
+ return null;
1383
2605
  }
1384
- if (typeof result !== "object" || Array.isArray(result)) {
1385
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(options, "YAML content must be a key-value mapping", {
1386
- got: typeof result
1387
- });
2606
+ if (truncated === candidate) {
1388
2607
  return null;
1389
2608
  }
1390
- return result;
1391
- } catch (error) {
1392
- (_c = options == null ? void 0 : options.onError) == null ? void 0 : _c.call(options, "Failed to parse YAML content", { error });
1393
- return null;
2609
+ candidate = truncated;
1394
2610
  }
1395
2611
  }
1396
2612
  function processToolCallMatch(text, tc, currentIndex, processedElements, options) {
@@ -1406,7 +2622,7 @@ function processToolCallMatch(text, tc, currentIndex, processedElements, options
1406
2622
  if (parsedArgs !== null) {
1407
2623
  processedElements.push({
1408
2624
  type: "tool-call",
1409
- toolCallId: generateId(),
2625
+ toolCallId: generateToolCallId(),
1410
2626
  toolName: tc.toolName,
1411
2627
  input: JSON.stringify(parsedArgs)
1412
2628
  });
@@ -1419,38 +2635,6 @@ function processToolCallMatch(text, tc, currentIndex, processedElements, options
1419
2635
  }
1420
2636
  return tc.endIndex;
1421
2637
  }
1422
- function createFlushTextHandler2(getCurrentTextId, setCurrentTextId, getHasEmittedTextStart, setHasEmittedTextStart) {
1423
- return (controller, text) => {
1424
- const content = text;
1425
- if (content) {
1426
- if (!getCurrentTextId()) {
1427
- const newId = generateId();
1428
- setCurrentTextId(newId);
1429
- controller.enqueue({
1430
- type: "text-start",
1431
- id: newId
1432
- });
1433
- setHasEmittedTextStart(true);
1434
- }
1435
- controller.enqueue({
1436
- type: "text-delta",
1437
- id: getCurrentTextId(),
1438
- delta: content
1439
- });
1440
- }
1441
- const currentTextId = getCurrentTextId();
1442
- if (currentTextId && !text) {
1443
- if (getHasEmittedTextStart()) {
1444
- controller.enqueue({
1445
- type: "text-end",
1446
- id: currentTextId
1447
- });
1448
- setHasEmittedTextStart(false);
1449
- }
1450
- setCurrentTextId(null);
1451
- }
1452
- };
1453
- }
1454
2638
  function findEarliestToolTag2(buffer, toolNames) {
1455
2639
  let bestIndex = -1;
1456
2640
  let bestName = "";
@@ -1458,8 +2642,9 @@ function findEarliestToolTag2(buffer, toolNames) {
1458
2642
  let bestTagLength = 0;
1459
2643
  for (const name of toolNames) {
1460
2644
  const openTag = `<${name}>`;
1461
- const selfTagRegex = new RegExp(`<${name}\\s*/>`);
2645
+ const selfTagRegex = getSelfClosingTagPattern2(name);
1462
2646
  const idxOpen = buffer.indexOf(openTag);
2647
+ selfTagRegex.lastIndex = 0;
1463
2648
  const selfMatch = selfTagRegex.exec(buffer);
1464
2649
  const idxSelf = selfMatch ? selfMatch.index : -1;
1465
2650
  if (idxOpen !== -1 && (bestIndex === -1 || idxOpen < bestIndex)) {
@@ -1482,6 +2667,29 @@ function findEarliestToolTag2(buffer, toolNames) {
1482
2667
  tagLength: bestTagLength
1483
2668
  };
1484
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
+ }
1485
2693
  var yamlProtocol = (_protocolOptions) => {
1486
2694
  return {
1487
2695
  formatTools({ tools, toolSystemPromptTemplate }) {
@@ -1507,18 +2715,32 @@ ${yamlContent}</${toolCall.toolName}>`;
1507
2715
  }
1508
2716
  const processedElements = [];
1509
2717
  let currentIndex = 0;
1510
- const toolCalls = findToolCalls2(text, toolNames);
2718
+ let parseText = text;
2719
+ let toolCalls = findToolCalls2(parseText, toolNames);
2720
+ if (toolCalls.length === 0) {
2721
+ const repaired = tryRepairXmlSelfClosingRootWithBody(
2722
+ parseText,
2723
+ toolNames
2724
+ );
2725
+ if (repaired) {
2726
+ const repairedCalls = findToolCalls2(repaired, toolNames);
2727
+ if (repairedCalls.length > 0) {
2728
+ parseText = repaired;
2729
+ toolCalls = repairedCalls;
2730
+ }
2731
+ }
2732
+ }
1511
2733
  for (const tc of toolCalls) {
1512
2734
  currentIndex = processToolCallMatch(
1513
- text,
2735
+ parseText,
1514
2736
  tc,
1515
2737
  currentIndex,
1516
2738
  processedElements,
1517
2739
  options
1518
2740
  );
1519
2741
  }
1520
- if (currentIndex < text.length) {
1521
- addTextSegment(text.substring(currentIndex), processedElements);
2742
+ if (currentIndex < parseText.length) {
2743
+ addTextSegment(parseText.substring(currentIndex), processedElements);
1522
2744
  }
1523
2745
  return processedElements;
1524
2746
  },
@@ -1528,7 +2750,7 @@ ${yamlContent}</${toolCall.toolName}>`;
1528
2750
  let currentToolCall = null;
1529
2751
  let currentTextId = null;
1530
2752
  let hasEmittedTextStart = false;
1531
- const flushText = createFlushTextHandler2(
2753
+ const flushText = createFlushTextHandler(
1532
2754
  () => currentTextId,
1533
2755
  (newId) => {
1534
2756
  currentTextId = newId;
@@ -1538,33 +2760,128 @@ ${yamlContent}</${toolCall.toolName}>`;
1538
2760
  hasEmittedTextStart = value;
1539
2761
  }
1540
2762
  );
1541
- 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) => {
2784
+ var _a;
2785
+ const parsedArgs = parseYamlContent(toolContent, options);
2786
+ flushText(controller);
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
+ });
2802
+ controller.enqueue({
2803
+ type: "tool-call",
2804
+ toolCallId,
2805
+ toolName,
2806
+ input: finalInput
2807
+ });
2808
+ } else {
2809
+ controller.enqueue({
2810
+ type: "tool-input-end",
2811
+ id: toolCallId
2812
+ });
2813
+ const original = `<${toolName}>${toolContent}</${toolName}>`;
2814
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "Could not parse streaming YAML tool call", {
2815
+ toolCall: original
2816
+ });
2817
+ if (shouldEmitRawToolCallTextOnError3(options)) {
2818
+ flushText(controller, original);
2819
+ }
2820
+ }
2821
+ };
2822
+ const finalizeUnclosedToolCall = (controller) => {
1542
2823
  var _a;
1543
- const parsedArgs = parseYamlContent(toolContent, options);
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);
1544
2831
  flushText(controller);
1545
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
+ });
1546
2845
  controller.enqueue({
1547
2846
  type: "tool-call",
1548
- toolCallId: generateId(),
2847
+ toolCallId,
1549
2848
  toolName,
1550
- input: JSON.stringify(parsedArgs)
2849
+ input: finalInput
1551
2850
  });
1552
2851
  } else {
1553
- const original = `<${toolName}>${toolContent}</${toolName}>`;
1554
- (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "Could not parse streaming YAML tool call", {
1555
- toolCall: original
2852
+ controller.enqueue({
2853
+ type: "tool-input-end",
2854
+ id: toolCallId
1556
2855
  });
1557
- flushText(controller, original);
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
+ }
1558
2865
  }
2866
+ buffer = "";
2867
+ currentToolCall = null;
1559
2868
  };
1560
2869
  const handlePendingToolCall = (controller, endTag, toolName) => {
2870
+ var _a;
1561
2871
  const endIdx = buffer.indexOf(endTag);
1562
2872
  if (endIdx === -1) {
2873
+ emitToolInputProgress2(controller, buffer);
1563
2874
  return false;
1564
2875
  }
1565
2876
  const content = buffer.substring(0, endIdx);
2877
+ emitToolInputProgress2(controller, content);
1566
2878
  buffer = buffer.substring(endIdx + endTag.length);
1567
- processToolCallEnd(controller, content, toolName);
2879
+ processToolCallEnd(
2880
+ controller,
2881
+ content,
2882
+ toolName,
2883
+ (_a = currentToolCall == null ? void 0 : currentToolCall.toolCallId) != null ? _a : generateToolCallId()
2884
+ );
1568
2885
  currentToolCall = null;
1569
2886
  return true;
1570
2887
  };
@@ -1581,13 +2898,35 @@ ${yamlContent}</${toolCall.toolName}>`;
1581
2898
  if (tagIndex > 0) {
1582
2899
  flushText(controller, buffer.substring(0, tagIndex));
1583
2900
  }
2901
+ flushText(controller);
1584
2902
  if (selfClosing) {
1585
2903
  buffer = buffer.substring(tagIndex + tagLength);
1586
- 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;
1587
2917
  } else {
1588
2918
  const startTag = `<${tagName}>`;
1589
2919
  buffer = buffer.substring(tagIndex + startTag.length);
1590
- 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
+ });
1591
2930
  }
1592
2931
  };
1593
2932
  const processBuffer = (controller) => {
@@ -1614,8 +2953,19 @@ ${yamlContent}</${toolCall.toolName}>`;
1614
2953
  return new TransformStream({
1615
2954
  transform(chunk, controller) {
1616
2955
  var _a;
2956
+ if (chunk.type === "finish") {
2957
+ if (currentToolCall) {
2958
+ finalizeUnclosedToolCall(controller);
2959
+ } else if (buffer) {
2960
+ flushText(controller, buffer);
2961
+ buffer = "";
2962
+ }
2963
+ flushText(controller);
2964
+ controller.enqueue(chunk);
2965
+ return;
2966
+ }
1617
2967
  if (chunk.type !== "text-delta") {
1618
- if (buffer) {
2968
+ if (!currentToolCall && buffer) {
1619
2969
  flushText(controller, buffer);
1620
2970
  buffer = "";
1621
2971
  }
@@ -1628,10 +2978,7 @@ ${yamlContent}</${toolCall.toolName}>`;
1628
2978
  },
1629
2979
  flush(controller) {
1630
2980
  if (currentToolCall) {
1631
- const unfinishedContent = `<${currentToolCall.name}>${buffer}`;
1632
- flushText(controller, unfinishedContent);
1633
- buffer = "";
1634
- currentToolCall = null;
2981
+ finalizeUnclosedToolCall(controller);
1635
2982
  } else if (buffer) {
1636
2983
  flushText(controller, buffer);
1637
2984
  buffer = "";
@@ -1737,17 +3084,56 @@ function encodeOriginalTools(tools) {
1737
3084
  inputSchema: JSON.stringify(t.inputSchema)
1738
3085
  }))) || [];
1739
3086
  }
1740
- function decodeOriginalTools(originalTools) {
3087
+ function decodeOriginalTools(originalTools, options) {
3088
+ var _a, _b, _c;
1741
3089
  if (!originalTools) {
1742
3090
  return [];
1743
3091
  }
1744
- return originalTools.map(
1745
- (t) => ({
1746
- type: "function",
1747
- name: t.name,
1748
- inputSchema: JSON.parse(t.inputSchema)
1749
- })
1750
- );
3092
+ const decodedTools = [];
3093
+ for (const [index, tool] of originalTools.entries()) {
3094
+ if (!tool || typeof tool.name !== "string") {
3095
+ (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(options, "Invalid originalTools entry: missing tool name", {
3096
+ index,
3097
+ tool
3098
+ });
3099
+ continue;
3100
+ }
3101
+ if (typeof tool.inputSchema !== "string") {
3102
+ (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
3103
+ options,
3104
+ "Invalid originalTools entry: inputSchema must be a string",
3105
+ {
3106
+ index,
3107
+ toolName: tool.name
3108
+ }
3109
+ );
3110
+ continue;
3111
+ }
3112
+ try {
3113
+ decodedTools.push({
3114
+ type: "function",
3115
+ name: tool.name,
3116
+ inputSchema: JSON.parse(tool.inputSchema)
3117
+ });
3118
+ } catch (error) {
3119
+ (_c = options == null ? void 0 : options.onError) == null ? void 0 : _c.call(
3120
+ options,
3121
+ "Failed to decode originalTools input schema, using permissive fallback schema",
3122
+ {
3123
+ index,
3124
+ toolName: tool.name,
3125
+ inputSchema: tool.inputSchema,
3126
+ error: error instanceof Error ? error.message : String(error)
3127
+ }
3128
+ );
3129
+ decodedTools.push({
3130
+ type: "function",
3131
+ name: tool.name,
3132
+ inputSchema: { type: "object" }
3133
+ });
3134
+ }
3135
+ }
3136
+ return decodedTools;
1751
3137
  }
1752
3138
  function extractToolNamesFromOriginalTools(originalTools) {
1753
3139
  return (originalTools == null ? void 0 : originalTools.map((t) => t.name)) || [];
@@ -1770,25 +3156,336 @@ function hasInputProperty(obj) {
1770
3156
  return typeof obj === "object" && obj !== null && "input" in obj;
1771
3157
  }
1772
3158
 
1773
- // src/generate-handler.ts
1774
- import { generateId as generateId2 } from "@ai-sdk/provider-utils";
1775
- function parseToolChoiceJson(text, providerOptions) {
3159
+ // src/core/utils/generated-text-json-recovery.ts
3160
+ function isRecord(value) {
3161
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3162
+ }
3163
+ function safeStringify2(value) {
3164
+ try {
3165
+ return JSON.stringify(value != null ? value : {});
3166
+ } catch (e) {
3167
+ return "{}";
3168
+ }
3169
+ }
3170
+ function parseJsonCandidate(candidateText) {
3171
+ try {
3172
+ return parse(candidateText);
3173
+ } catch (e) {
3174
+ return void 0;
3175
+ }
3176
+ }
3177
+ function extractCodeBlockCandidates(text) {
3178
+ var _a, _b;
3179
+ const codeBlockRegex = /```(?:json|yaml|xml)?\s*([\s\S]*?)```/gi;
3180
+ const candidates = [];
3181
+ let match;
3182
+ while (true) {
3183
+ match = codeBlockRegex.exec(text);
3184
+ if (!match) {
3185
+ break;
3186
+ }
3187
+ const body = (_a = match[1]) == null ? void 0 : _a.trim();
3188
+ if (body) {
3189
+ const startIndex = (_b = match.index) != null ? _b : 0;
3190
+ const endIndex = startIndex + match[0].length;
3191
+ candidates.push({
3192
+ text: body,
3193
+ startIndex,
3194
+ endIndex
3195
+ });
3196
+ }
3197
+ }
3198
+ return candidates;
3199
+ }
3200
+ function scanJsonChar(state, char) {
3201
+ if (state.inString) {
3202
+ if (state.escaping) {
3203
+ return { ...state, escaping: false };
3204
+ }
3205
+ if (char === "\\") {
3206
+ return { ...state, escaping: true };
3207
+ }
3208
+ if (char === '"') {
3209
+ return { ...state, inString: false };
3210
+ }
3211
+ return state;
3212
+ }
3213
+ if (char === '"') {
3214
+ return { ...state, inString: true };
3215
+ }
3216
+ if (char === "{") {
3217
+ return { ...state, depth: state.depth + 1 };
3218
+ }
3219
+ if (char === "}") {
3220
+ return { ...state, depth: Math.max(0, state.depth - 1) };
3221
+ }
3222
+ return state;
3223
+ }
3224
+ function extractBalancedJsonObjects(text) {
3225
+ const maxCandidateLength = 1e4;
3226
+ const candidates = [];
3227
+ let state = { depth: 0, inString: false, escaping: false };
3228
+ let currentStart = null;
3229
+ let ignoreCurrent = false;
3230
+ for (let index = 0; index < text.length; index += 1) {
3231
+ const char = text[index];
3232
+ if (!state.inString && char === "{" && state.depth === 0) {
3233
+ currentStart = index;
3234
+ ignoreCurrent = false;
3235
+ }
3236
+ state = scanJsonChar(state, char);
3237
+ if (currentStart !== null && !ignoreCurrent && index - currentStart + 1 > maxCandidateLength) {
3238
+ ignoreCurrent = true;
3239
+ }
3240
+ if (!state.inString && char === "}" && state.depth === 0) {
3241
+ if (currentStart !== null && !ignoreCurrent) {
3242
+ const endIndex = index + 1;
3243
+ const candidate = text.slice(currentStart, endIndex);
3244
+ if (candidate.length > 1) {
3245
+ candidates.push({
3246
+ text: candidate,
3247
+ startIndex: currentStart,
3248
+ endIndex
3249
+ });
3250
+ }
3251
+ }
3252
+ currentStart = null;
3253
+ ignoreCurrent = false;
3254
+ }
3255
+ }
3256
+ return candidates;
3257
+ }
3258
+ function extractTaggedToolCallCandidates(rawText) {
3259
+ var _a, _b;
3260
+ const toolCallRegex = /<tool_call>([\s\S]*?)<\/tool_call>/gi;
3261
+ const candidates = [];
3262
+ let match;
3263
+ while (true) {
3264
+ match = toolCallRegex.exec(rawText);
3265
+ if (!match) {
3266
+ break;
3267
+ }
3268
+ const body = (_a = match[1]) == null ? void 0 : _a.trim();
3269
+ if (!body) {
3270
+ continue;
3271
+ }
3272
+ const startIndex = (_b = match.index) != null ? _b : 0;
3273
+ const endIndex = startIndex + match[0].length;
3274
+ candidates.push({
3275
+ text: body,
3276
+ startIndex,
3277
+ endIndex
3278
+ });
3279
+ }
3280
+ return candidates;
3281
+ }
3282
+ function extractJsonLikeCandidates(rawText) {
3283
+ return mergeJsonCandidatesByStart(
3284
+ extractTaggedToolCallCandidates(rawText),
3285
+ extractCodeBlockCandidates(rawText),
3286
+ extractBalancedJsonObjects(rawText)
3287
+ );
3288
+ }
3289
+ function mergeJsonCandidatesByStart(tagged, codeBlocks, balanced) {
3290
+ return [...tagged, ...codeBlocks, ...balanced].sort(
3291
+ (a, b) => a.startIndex !== b.startIndex ? a.startIndex - b.startIndex : b.endIndex - a.endIndex
3292
+ );
3293
+ }
3294
+ function toToolCallPart(candidate) {
3295
+ return {
3296
+ type: "tool-call",
3297
+ toolCallId: generateToolCallId(),
3298
+ toolName: candidate.toolName,
3299
+ input: candidate.input
3300
+ };
3301
+ }
3302
+ function toRecoveredParts(text, candidate, toolCallPart) {
3303
+ const out = [];
3304
+ const prefix = text.slice(0, candidate.startIndex);
3305
+ if (prefix.length > 0) {
3306
+ out.push({ type: "text", text: prefix });
3307
+ }
3308
+ out.push(toolCallPart);
3309
+ const suffix = text.slice(candidate.endIndex);
3310
+ if (suffix.length > 0) {
3311
+ out.push({ type: "text", text: suffix });
3312
+ }
3313
+ return out;
3314
+ }
3315
+ function parseAsToolPayload(payload, tools) {
3316
+ if (!isRecord(payload)) {
3317
+ return null;
3318
+ }
3319
+ const toolName = typeof payload.name === "string" && payload.name.trim().length > 0 ? payload.name.trim() : null;
3320
+ if (!toolName) {
3321
+ return null;
3322
+ }
3323
+ if (!tools.some((tool) => tool.name === toolName)) {
3324
+ return null;
3325
+ }
3326
+ const rawArgs = Object.hasOwn(payload, "arguments") ? payload.arguments : {};
3327
+ if (!isRecord(rawArgs)) {
3328
+ return null;
3329
+ }
3330
+ return {
3331
+ toolName,
3332
+ input: safeStringify2(rawArgs)
3333
+ };
3334
+ }
3335
+ function isLikelyArgumentsShapeForTool(args, tool) {
3336
+ const unwrapped = unwrapJsonSchema(tool.inputSchema);
3337
+ if (!isRecord(unwrapped)) {
3338
+ return false;
3339
+ }
3340
+ if (getSchemaType(unwrapped) !== "object") {
3341
+ return false;
3342
+ }
3343
+ const properties = unwrapped.properties;
3344
+ if (!isRecord(properties)) {
3345
+ return false;
3346
+ }
3347
+ const keys = Object.keys(args);
3348
+ if (keys.length === 0) {
3349
+ return false;
3350
+ }
3351
+ const knownKeys = keys.filter((key) => Object.hasOwn(properties, key));
3352
+ if (knownKeys.length === 0) {
3353
+ return false;
3354
+ }
3355
+ if (unwrapped.additionalProperties === false && knownKeys.length !== keys.length) {
3356
+ return false;
3357
+ }
3358
+ return true;
3359
+ }
3360
+ function parseAsArgumentsOnly(payload, tools) {
3361
+ if (tools.length !== 1) {
3362
+ return null;
3363
+ }
3364
+ if (!isRecord(payload)) {
3365
+ return null;
3366
+ }
3367
+ const hasNameEnvelope = Object.hasOwn(payload, "name") && typeof payload.name === "string" && payload.name.length > 0;
3368
+ const hasArgumentsEnvelope = Object.hasOwn(payload, "arguments") && (typeof payload.arguments === "string" || isRecord(payload.arguments));
3369
+ if (hasNameEnvelope || hasArgumentsEnvelope) {
3370
+ return null;
3371
+ }
3372
+ const tool = tools[0];
3373
+ if (!isLikelyArgumentsShapeForTool(payload, tool)) {
3374
+ return null;
3375
+ }
3376
+ return {
3377
+ toolName: tool.name,
3378
+ input: safeStringify2(payload)
3379
+ };
3380
+ }
3381
+ function recoverToolCallFromJsonCandidates(text, tools) {
3382
+ if (tools.length === 0) {
3383
+ return null;
3384
+ }
3385
+ const jsonCandidates = extractJsonLikeCandidates(text);
3386
+ for (const jsonCandidate of jsonCandidates) {
3387
+ const parsed = parseJsonCandidate(jsonCandidate.text);
3388
+ if (parsed === void 0) {
3389
+ continue;
3390
+ }
3391
+ const toolPayload = parseAsToolPayload(parsed, tools);
3392
+ if (toolPayload) {
3393
+ return toRecoveredParts(text, jsonCandidate, toToolCallPart(toolPayload));
3394
+ }
3395
+ const argsPayload = parseAsArgumentsOnly(parsed, tools);
3396
+ if (argsPayload) {
3397
+ return toRecoveredParts(text, jsonCandidate, toToolCallPart(argsPayload));
3398
+ }
3399
+ }
3400
+ return null;
3401
+ }
3402
+
3403
+ // src/core/utils/tool-call-coercion.ts
3404
+ function coerceToolCallInput(toolName, input, tools) {
1776
3405
  var _a;
3406
+ let args = {};
3407
+ if (typeof input === "string") {
3408
+ try {
3409
+ args = JSON.parse(input);
3410
+ } catch (e) {
3411
+ return;
3412
+ }
3413
+ } else if (input && typeof input === "object") {
3414
+ args = input;
3415
+ } else {
3416
+ return;
3417
+ }
3418
+ const schema = (_a = tools.find((t) => t.name === toolName)) == null ? void 0 : _a.inputSchema;
3419
+ const coerced = coerceBySchema(args, schema);
3420
+ return JSON.stringify(coerced != null ? coerced : {});
3421
+ }
3422
+ function coerceToolCallPart(part, tools) {
3423
+ const coercedInput = coerceToolCallInput(part.toolName, part.input, tools);
3424
+ if (coercedInput === void 0) {
3425
+ return part;
3426
+ }
3427
+ return {
3428
+ ...part,
3429
+ input: coercedInput
3430
+ };
3431
+ }
3432
+
3433
+ // src/core/utils/tool-choice.ts
3434
+ function ensureNonEmptyToolName(name) {
3435
+ if (typeof name !== "string") {
3436
+ return "unknown";
3437
+ }
3438
+ const trimmed = name.trim();
3439
+ return trimmed.length > 0 ? trimmed : "unknown";
3440
+ }
3441
+ function safeStringify3(value) {
1777
3442
  try {
1778
- return JSON.parse(text);
3443
+ return JSON.stringify(value != null ? value : {});
3444
+ } catch (e) {
3445
+ return "{}";
3446
+ }
3447
+ }
3448
+ function parseToolChoicePayload({
3449
+ text,
3450
+ tools,
3451
+ onError,
3452
+ errorMessage
3453
+ }) {
3454
+ let parsed;
3455
+ try {
3456
+ parsed = JSON.parse(text);
1779
3457
  } catch (error) {
1780
- const options = extractOnErrorOption(providerOptions);
1781
- (_a = options == null ? void 0 : options.onError) == null ? void 0 : _a.call(
1782
- options,
1783
- "Failed to parse toolChoice JSON from generated model output",
1784
- {
1785
- text,
1786
- error: error instanceof Error ? error.message : String(error)
1787
- }
1788
- );
1789
- return {};
3458
+ onError == null ? void 0 : onError(errorMessage, {
3459
+ text,
3460
+ error: error instanceof Error ? error.message : String(error)
3461
+ });
3462
+ return { toolName: "unknown", input: "{}" };
3463
+ }
3464
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
3465
+ onError == null ? void 0 : onError("toolChoice JSON payload must be an object", {
3466
+ parsedType: typeof parsed,
3467
+ parsed
3468
+ });
3469
+ return { toolName: "unknown", input: "{}" };
1790
3470
  }
3471
+ const payload = parsed;
3472
+ const toolName = ensureNonEmptyToolName(payload.name);
3473
+ const rawArgs = Object.hasOwn(payload, "arguments") ? payload.arguments : {};
3474
+ if (rawArgs == null || typeof rawArgs !== "object" || Array.isArray(rawArgs)) {
3475
+ onError == null ? void 0 : onError("toolChoice arguments must be a JSON object", {
3476
+ toolName,
3477
+ arguments: rawArgs
3478
+ });
3479
+ return { toolName, input: "{}" };
3480
+ }
3481
+ const coercedInput = coerceToolCallInput(toolName, rawArgs, tools);
3482
+ return {
3483
+ toolName,
3484
+ input: coercedInput != null ? coercedInput : safeStringify3(rawArgs)
3485
+ };
1791
3486
  }
3487
+
3488
+ // src/generate-handler.ts
1792
3489
  function logDebugSummary(debugSummary, toolCall, originText) {
1793
3490
  if (debugSummary) {
1794
3491
  debugSummary.originalText = originText;
@@ -1802,25 +3499,34 @@ function logDebugSummary(debugSummary, toolCall, originText) {
1802
3499
  logParsedSummary({ toolCalls: [toolCall], originalText: originText });
1803
3500
  }
1804
3501
  }
1805
- async function handleToolChoice(doGenerate, params) {
1806
- var _a, _b, _c;
3502
+ async function handleToolChoice(doGenerate, params, tools) {
3503
+ var _a, _b, _c, _d;
1807
3504
  const result = await doGenerate();
1808
3505
  const first = (_a = result.content) == null ? void 0 : _a[0];
1809
- let parsed = {};
3506
+ const onError = (_b = extractOnErrorOption(params.providerOptions)) == null ? void 0 : _b.onError;
3507
+ let toolName = "unknown";
3508
+ let input = "{}";
1810
3509
  if (first && first.type === "text") {
1811
3510
  if (getDebugLevel() === "parse") {
1812
3511
  logRawChunk(first.text);
1813
3512
  }
1814
- parsed = parseToolChoiceJson(first.text, params.providerOptions);
3513
+ const parsed = parseToolChoicePayload({
3514
+ text: first.text,
3515
+ tools,
3516
+ onError,
3517
+ errorMessage: "Failed to parse toolChoice JSON from generated model output"
3518
+ });
3519
+ toolName = parsed.toolName;
3520
+ input = parsed.input;
1815
3521
  }
1816
3522
  const toolCall = {
1817
3523
  type: "tool-call",
1818
- toolCallId: generateId2(),
1819
- toolName: parsed.name || "unknown",
1820
- input: JSON.stringify(parsed.arguments || {})
3524
+ toolCallId: generateToolCallId(),
3525
+ toolName,
3526
+ input
1821
3527
  };
1822
3528
  const originText = first && first.type === "text" ? first.text : "";
1823
- const debugSummary = (_c = (_b = params.providerOptions) == null ? void 0 : _b.toolCallMiddleware) == null ? void 0 : _c.debugSummary;
3529
+ const debugSummary = (_d = (_c = params.providerOptions) == null ? void 0 : _c.toolCallMiddleware) == null ? void 0 : _d.debugSummary;
1824
3530
  logDebugSummary(debugSummary, toolCall, originText);
1825
3531
  return {
1826
3532
  ...result,
@@ -1835,7 +3541,7 @@ function parseContent(content, protocol, tools, providerOptions) {
1835
3541
  if (getDebugLevel() === "stream") {
1836
3542
  logRawChunk(contentItem.text);
1837
3543
  }
1838
- return protocol.parseGeneratedText({
3544
+ const parsedByProtocol = protocol.parseGeneratedText({
1839
3545
  text: contentItem.text,
1840
3546
  tools,
1841
3547
  options: {
@@ -1843,9 +3549,20 @@ function parseContent(content, protocol, tools, providerOptions) {
1843
3549
  ...providerOptions == null ? void 0 : providerOptions.toolCallMiddleware
1844
3550
  }
1845
3551
  });
3552
+ const hasToolCall = parsedByProtocol.some(
3553
+ (part) => part.type === "tool-call"
3554
+ );
3555
+ if (hasToolCall) {
3556
+ return parsedByProtocol;
3557
+ }
3558
+ const recoveredFromJson = recoverToolCallFromJsonCandidates(
3559
+ contentItem.text,
3560
+ tools
3561
+ );
3562
+ return recoveredFromJson != null ? recoveredFromJson : parsedByProtocol;
1846
3563
  });
1847
3564
  return parsed.map(
1848
- (part) => fixToolCallWithSchema(part, tools)
3565
+ (part) => part.type === "tool-call" ? coerceToolCallPart(part, tools) : part
1849
3566
  );
1850
3567
  }
1851
3568
  function logParsedContent(content) {
@@ -1888,12 +3605,14 @@ async function wrapGenerate({
1888
3605
  params
1889
3606
  }) {
1890
3607
  var _a, _b;
1891
- if (isToolChoiceActive(params)) {
1892
- return handleToolChoice(doGenerate, params);
1893
- }
3608
+ const onError = extractOnErrorOption(params.providerOptions);
1894
3609
  const tools = originalToolsSchema.decode(
1895
- (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.originalTools
3610
+ (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.originalTools,
3611
+ onError
1896
3612
  );
3613
+ if (isToolChoiceActive(params)) {
3614
+ return handleToolChoice(doGenerate, params, tools);
3615
+ }
1897
3616
  const result = await doGenerate();
1898
3617
  if (result.content.length === 0) {
1899
3618
  return result;
@@ -1917,28 +3636,6 @@ async function wrapGenerate({
1917
3636
  content: newContent
1918
3637
  };
1919
3638
  }
1920
- function fixToolCallWithSchema(part, tools) {
1921
- var _a;
1922
- if (part.type !== "tool-call") {
1923
- return part;
1924
- }
1925
- let args = {};
1926
- if (typeof part.input === "string") {
1927
- try {
1928
- args = JSON.parse(part.input);
1929
- } catch (e) {
1930
- return part;
1931
- }
1932
- } else if (part.input && typeof part.input === "object") {
1933
- args = part.input;
1934
- }
1935
- const schema = (_a = tools.find((t) => t.name === part.toolName)) == null ? void 0 : _a.inputSchema;
1936
- const coerced = coerceBySchema(args, schema);
1937
- return {
1938
- ...part,
1939
- input: JSON.stringify(coerced != null ? coerced : {})
1940
- };
1941
- }
1942
3639
 
1943
3640
  // src/core/prompts/hermes-system-prompt.ts
1944
3641
  function hermesSystemPromptTemplate(tools) {
@@ -2334,7 +4031,6 @@ unit: celsius
2334
4031
  }
2335
4032
 
2336
4033
  // src/stream-handler.ts
2337
- import { generateId as generateId3 } from "@ai-sdk/provider-utils";
2338
4034
  async function wrapStream({
2339
4035
  protocol,
2340
4036
  doStream,
@@ -2342,19 +4038,22 @@ async function wrapStream({
2342
4038
  params
2343
4039
  }) {
2344
4040
  var _a, _b, _c;
4041
+ const onErrorOptions = extractOnErrorOption(params.providerOptions);
4042
+ const tools = originalToolsSchema.decode(
4043
+ (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.originalTools,
4044
+ onErrorOptions
4045
+ );
2345
4046
  if (isToolChoiceActive(params)) {
2346
4047
  return toolChoiceStream({
2347
4048
  doGenerate,
2348
- options: extractOnErrorOption(params.providerOptions)
4049
+ tools,
4050
+ options: onErrorOptions
2349
4051
  });
2350
4052
  }
2351
4053
  const { stream, ...rest } = await doStream();
2352
4054
  const debugLevel = getDebugLevel();
2353
- const tools = originalToolsSchema.decode(
2354
- (_b = (_a = params.providerOptions) == null ? void 0 : _a.toolCallMiddleware) == null ? void 0 : _b.originalTools
2355
- );
2356
4055
  const options = {
2357
- ...extractOnErrorOption(params.providerOptions),
4056
+ ...onErrorOptions,
2358
4057
  ...((_c = params.providerOptions) == null ? void 0 : _c.toolCallMiddleware) || {}
2359
4058
  };
2360
4059
  const coreStream = stream.pipeThrough(
@@ -2369,13 +4068,26 @@ async function wrapStream({
2369
4068
  }
2370
4069
  )
2371
4070
  ).pipeThrough(protocol.createStreamParser({ tools, options }));
4071
+ let seenToolCall = false;
2372
4072
  const v3Stream = coreStream.pipeThrough(
2373
4073
  new TransformStream({
2374
4074
  transform(part, controller) {
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
+ }
2375
4087
  if (debugLevel === "stream") {
2376
- logParsedChunk(part);
4088
+ logParsedChunk(normalizedPart);
2377
4089
  }
2378
- controller.enqueue(part);
4090
+ controller.enqueue(normalizedPart);
2379
4091
  }
2380
4092
  })
2381
4093
  );
@@ -2386,41 +4098,36 @@ async function wrapStream({
2386
4098
  }
2387
4099
  async function toolChoiceStream({
2388
4100
  doGenerate,
4101
+ tools,
2389
4102
  options
2390
4103
  }) {
2391
- var _a, _b;
4104
+ var _a;
4105
+ const normalizedTools = Array.isArray(tools) ? tools : [];
2392
4106
  const result = await doGenerate();
2393
- let toolJson = {};
4107
+ let toolName = "unknown";
4108
+ let input = "{}";
2394
4109
  if ((result == null ? void 0 : result.content) && result.content.length > 0 && ((_a = result.content[0]) == null ? void 0 : _a.type) === "text") {
2395
- try {
2396
- toolJson = JSON.parse(result.content[0].text);
2397
- } catch (error) {
2398
- (_b = options == null ? void 0 : options.onError) == null ? void 0 : _b.call(
2399
- options,
2400
- "Failed to parse toolChoice JSON from streamed model output",
2401
- {
2402
- text: result.content[0].text,
2403
- error: error instanceof Error ? error.message : String(error)
2404
- }
2405
- );
2406
- toolJson = {};
2407
- }
4110
+ const parsed = parseToolChoicePayload({
4111
+ text: result.content[0].text,
4112
+ tools: normalizedTools,
4113
+ onError: options == null ? void 0 : options.onError,
4114
+ errorMessage: "Failed to parse toolChoice JSON from streamed model output"
4115
+ });
4116
+ toolName = parsed.toolName;
4117
+ input = parsed.input;
2408
4118
  }
2409
4119
  const stream = new ReadableStream({
2410
4120
  start(controller) {
2411
4121
  controller.enqueue({
2412
4122
  type: "tool-call",
2413
- toolCallId: generateId3(),
2414
- toolName: toolJson.name || "unknown",
2415
- input: JSON.stringify(toolJson.arguments || {})
4123
+ toolCallId: generateToolCallId(),
4124
+ toolName,
4125
+ input
2416
4126
  });
2417
4127
  controller.enqueue({
2418
4128
  type: "finish",
2419
- usage: (result == null ? void 0 : result.usage) || {
2420
- inputTokens: 0,
2421
- outputTokens: 0
2422
- },
2423
- finishReason: "tool-calls"
4129
+ usage: normalizeUsage(result == null ? void 0 : result.usage),
4130
+ finishReason: normalizeToolCallsFinishReason(result == null ? void 0 : result.finishReason)
2424
4131
  });
2425
4132
  controller.close();
2426
4133
  }
@@ -2431,6 +4138,60 @@ async function toolChoiceStream({
2431
4138
  stream
2432
4139
  };
2433
4140
  }
4141
+ var ZERO_USAGE = {
4142
+ inputTokens: {
4143
+ total: 0,
4144
+ noCache: void 0,
4145
+ cacheRead: void 0,
4146
+ cacheWrite: void 0
4147
+ },
4148
+ outputTokens: {
4149
+ total: 0,
4150
+ text: void 0,
4151
+ reasoning: void 0
4152
+ }
4153
+ };
4154
+ function normalizeToolCallsFinishReason(finishReason) {
4155
+ let raw = "tool-calls";
4156
+ if (typeof finishReason === "string") {
4157
+ raw = finishReason;
4158
+ } else if (finishReason && typeof finishReason === "object" && "raw" in finishReason && typeof finishReason.raw === "string") {
4159
+ raw = finishReason.raw;
4160
+ } else if (finishReason && typeof finishReason === "object" && "unified" in finishReason && typeof finishReason.unified === "string") {
4161
+ raw = finishReason.unified;
4162
+ }
4163
+ return {
4164
+ unified: "tool-calls",
4165
+ raw
4166
+ };
4167
+ }
4168
+ function normalizeUsage(usage) {
4169
+ if (!usage || typeof usage !== "object") {
4170
+ return ZERO_USAGE;
4171
+ }
4172
+ const usageRecord = usage;
4173
+ const input = usageRecord.inputTokens;
4174
+ const output = usageRecord.outputTokens;
4175
+ if (input && typeof input === "object" && output && typeof output === "object") {
4176
+ return usage;
4177
+ }
4178
+ if (typeof input === "number" && typeof output === "number") {
4179
+ return {
4180
+ inputTokens: {
4181
+ total: input,
4182
+ noCache: void 0,
4183
+ cacheRead: void 0,
4184
+ cacheWrite: void 0
4185
+ },
4186
+ outputTokens: {
4187
+ total: output,
4188
+ text: void 0,
4189
+ reasoning: void 0
4190
+ }
4191
+ };
4192
+ }
4193
+ return ZERO_USAGE;
4194
+ }
2434
4195
 
2435
4196
  // src/transform-handler.ts
2436
4197
  function buildFinalPrompt(systemPrompt, processedPrompt, placement) {
@@ -2556,6 +4317,11 @@ function handleToolChoiceRequired(params, baseReturnParams, functionTools) {
2556
4317
  "Tool choice type 'required' is set, but no tools are provided in params.tools."
2557
4318
  );
2558
4319
  }
4320
+ if (functionTools.length === 0) {
4321
+ throw new Error(
4322
+ "Tool choice type 'required' is set, but no function tools are provided. Provider-defined tools are not supported by this middleware."
4323
+ );
4324
+ }
2559
4325
  return {
2560
4326
  ...baseReturnParams,
2561
4327
  responseFormat: {
@@ -2866,4 +4632,4 @@ export {
2866
4632
  xmlToolMiddleware,
2867
4633
  yamlToolMiddleware
2868
4634
  };
2869
- //# sourceMappingURL=chunk-5WKXBBCU.js.map
4635
+ //# sourceMappingURL=chunk-ERJKQKCR.js.map