extrait 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -58,6 +58,7 @@ __export(exports_src, {
58
58
  extractMarkdownCodeBlocks: () => extractMarkdownCodeBlocks,
59
59
  extractJsonCandidates: () => extractJsonCandidates,
60
60
  extractFirstMarkdownCode: () => extractFirstMarkdownCode,
61
+ dedent: () => dedent,
61
62
  createProviderRegistry: () => createProviderRegistry,
62
63
  createOpenAICompatibleAdapter: () => createOpenAICompatibleAdapter,
63
64
  createModelAdapter: () => createModelAdapter,
@@ -91,9 +92,6 @@ __export(exports_src, {
91
92
  });
92
93
  module.exports = __toCommonJS(exports_src);
93
94
 
94
- // src/extract.ts
95
- var import_jsonrepair = require("jsonrepair");
96
-
97
95
  // src/utils/common.ts
98
96
  function toErrorMessage(error) {
99
97
  if (error instanceof Error) {
@@ -311,184 +309,77 @@ function isOnlyWhitespace(value) {
311
309
  return true;
312
310
  }
313
311
 
314
- // src/extract.ts
315
- var RE_EMPTY_OBJECT = /^\{\s*\}$/;
316
- var RE_EMPTY_ARRAY = /^\[\s*\]$/;
317
- var RE_BOUNDARY_CHAR = /[\s,.;:!?`"'()\[\]{}<>]/;
318
- var DEFAULT_EXTRACTION_HEURISTICS = {
319
- firstPassMin: 12,
320
- firstPassCap: 30,
321
- firstPassMultiplier: 6,
322
- secondPassMin: 4,
323
- secondPassCap: 8,
324
- secondPassMultiplier: 2,
325
- hintMaxLength: 50000
326
- };
327
- function extractJsonCandidates(input, options = {}) {
328
- const maxCandidates = options.maxCandidates ?? 5;
329
- const acceptArrays = options.acceptArrays ?? true;
330
- const allowRepairHints = options.allowRepairHints ?? true;
331
- const heuristics = resolveExtractionHeuristics(options.heuristics);
332
- const extractionInput = input;
333
- const candidates = [];
334
- candidates.push(...extractFromMarkdown(extractionInput, acceptArrays));
335
- candidates.push(...scanBalancedSegments(extractionInput, acceptArrays));
336
- if (candidates.length === 0 && extractionInput.trim()) {
337
- const content = extractionInput.trim();
338
- candidates.push({
339
- id: "raw:fallback",
340
- source: "raw",
341
- content,
342
- start: 0,
343
- end: extractionInput.length,
344
- score: 10 + Math.floor(lengthScore(content.length) / 3)
345
- });
346
- }
347
- const prefiltered = prefilterByJsonShape(candidates, acceptArrays);
348
- const firstPassLimit = clamp(maxCandidates * heuristics.firstPassMultiplier, heuristics.firstPassMin, heuristics.firstPassCap);
349
- const firstPass = prefiltered.slice(0, firstPassLimit);
350
- const secondPassLimit = Math.min(firstPass.length, clamp(maxCandidates * heuristics.secondPassMultiplier, heuristics.secondPassMin, heuristics.secondPassCap));
351
- for (let i = 0;i < secondPassLimit; i += 1) {
352
- const candidate = firstPass[i];
353
- if (!candidate) {
354
- continue;
355
- }
356
- const parseHint = buildParseHint(candidate.content, allowRepairHints, heuristics.hintMaxLength);
357
- if (!parseHint) {
358
- continue;
359
- }
360
- candidate.parseHint = parseHint;
361
- candidate.score += parseHintBonus(parseHint);
312
+ // src/extract-parse-hint.ts
313
+ var import_jsonrepair = require("jsonrepair");
314
+ function buildParseHint(content, allowRepair, hintMaxLength) {
315
+ if (content.length > hintMaxLength) {
316
+ return null;
362
317
  }
363
- const sorted = sortCandidates(firstPass);
364
- const deduped = dedupeCandidates(sorted);
365
- return deduped.slice(0, maxCandidates).map((candidate, index) => ({
366
- id: `${candidate.source}:${index}`,
367
- source: candidate.source,
368
- content: candidate.content,
369
- language: candidate.language,
370
- parseHint: candidate.parseHint,
371
- start: candidate.start,
372
- end: candidate.end,
373
- score: candidate.score
374
- }));
375
- }
376
- function prefilterByJsonShape(candidates, acceptArrays) {
377
- const shaped = candidates.map((candidate) => {
378
- const shapeScore = jsonShapeScore(candidate.content, acceptArrays);
318
+ try {
379
319
  return {
380
- ...candidate,
381
- shapeScore,
382
- score: candidate.score + shapeScore
320
+ success: true,
321
+ parsed: JSON.parse(content),
322
+ repaired: null,
323
+ usedRepair: false,
324
+ stage: "parse",
325
+ error: ""
383
326
  };
384
- });
385
- const sorted = sortCandidates(shaped);
386
- const deduped = dedupeCandidates(sorted);
387
- const filtered = deduped.filter((candidate) => passesShapeFilter(candidate));
388
- if (filtered.length > 0) {
389
- const fallback = deduped.find((candidate) => !passesShapeFilter(candidate));
390
- if (fallback) {
391
- filtered.push(fallback);
327
+ } catch (directError) {
328
+ if (!allowRepair) {
329
+ return {
330
+ success: false,
331
+ parsed: null,
332
+ repaired: null,
333
+ usedRepair: false,
334
+ stage: "parse",
335
+ error: toErrorMessage(directError)
336
+ };
392
337
  }
393
- return sortCandidates(filtered);
394
- }
395
- return deduped.slice(0, Math.min(1, deduped.length));
396
- }
397
- function extractFromMarkdown(input, acceptArrays) {
398
- const blocks = extractMarkdownCodeBlocks(input);
399
- return blocks.flatMap((block, index) => {
400
- const language = block.language || null;
401
- const content = block.code.trim();
402
- if (!content) {
403
- return [];
338
+ let repaired;
339
+ try {
340
+ repaired = import_jsonrepair.jsonrepair(content);
341
+ } catch (repairError) {
342
+ return {
343
+ success: false,
344
+ parsed: null,
345
+ repaired: null,
346
+ usedRepair: true,
347
+ stage: "repair",
348
+ error: toErrorMessage(repairError)
349
+ };
404
350
  }
405
- if (!looksLikeJsonEnvelope(content, acceptArrays)) {
406
- return [];
351
+ try {
352
+ return {
353
+ success: true,
354
+ parsed: JSON.parse(repaired),
355
+ repaired,
356
+ usedRepair: true,
357
+ stage: "parse",
358
+ error: ""
359
+ };
360
+ } catch (parseError) {
361
+ return {
362
+ success: false,
363
+ parsed: null,
364
+ repaired,
365
+ usedRepair: true,
366
+ stage: "parse",
367
+ error: toErrorMessage(parseError || directError)
368
+ };
407
369
  }
408
- const langBonus = languageBonus(language);
409
- return [
410
- {
411
- id: `fenced:${index}`,
412
- source: "fenced",
413
- language,
414
- content,
415
- start: block.start,
416
- end: block.end,
417
- score: 260 + langBonus + lengthScore(content.length)
418
- }
419
- ];
420
- });
370
+ }
421
371
  }
422
- function scanBalancedSegments(input, acceptArrays) {
423
- const results = [];
424
- const stack = [];
425
- let inString = false;
426
- let quote = null;
427
- let escaped = false;
428
- for (let i = 0;i < input.length; i += 1) {
429
- const char = input[i];
430
- if (!char) {
431
- continue;
432
- }
433
- if (inString) {
434
- if (escaped) {
435
- escaped = false;
436
- continue;
437
- }
438
- if (char === "\\") {
439
- escaped = true;
440
- continue;
441
- }
442
- if (char === quote) {
443
- inString = false;
444
- quote = null;
445
- }
446
- continue;
447
- }
448
- const allowSingleQuoted = stack.length > 0;
449
- if (char === '"' || allowSingleQuoted && (char === "'" || char === "`")) {
450
- inString = true;
451
- quote = char;
452
- continue;
453
- }
454
- if (char === "{" || char === "[") {
455
- stack.push({ char, index: i });
456
- continue;
457
- }
458
- if (char !== "}" && char !== "]") {
459
- continue;
460
- }
461
- const expectedOpen = char === "}" ? "{" : "[";
462
- while (stack.length > 0 && stack[stack.length - 1]?.char !== expectedOpen) {
463
- stack.pop();
464
- }
465
- const opened = stack.pop();
466
- if (!opened) {
467
- continue;
468
- }
469
- if (stack.length > 0) {
470
- continue;
471
- }
472
- if (!acceptArrays && opened.char === "[") {
473
- continue;
474
- }
475
- const content = input.slice(opened.index, i + 1).trim();
476
- if (!content) {
477
- continue;
478
- }
479
- const rootBonus = opened.char === "{" ? 40 : 20;
480
- const boundaryBonus = boundaryScore(input, opened.index, i + 1);
481
- results.push({
482
- id: `scan:${results.length}`,
483
- source: "scan",
484
- content,
485
- start: opened.index,
486
- end: i + 1,
487
- score: 120 + rootBonus + boundaryBonus + lengthScore(content.length)
488
- });
372
+ function parseHintBonus(hint) {
373
+ if (hint.success) {
374
+ return hint.usedRepair ? 70 : 120;
489
375
  }
490
- return results;
376
+ return hint.usedRepair ? -20 : -10;
491
377
  }
378
+
379
+ // src/extract-shape.ts
380
+ var RE_EMPTY_OBJECT = /^\{\s*\}$/;
381
+ var RE_EMPTY_ARRAY = /^\[\s*\]$/;
382
+ var RE_BOUNDARY_CHAR = /[\s,.;:!?`"'()[\]{}<>]/;
492
383
  function looksLikeJsonEnvelope(content, acceptArrays) {
493
384
  const trimmed = content.trim();
494
385
  if (trimmed.startsWith("{")) {
@@ -597,95 +488,6 @@ function passesShapeFilter(candidate) {
597
488
  }
598
489
  return candidate.shapeScore >= 35;
599
490
  }
600
- function buildParseHint(content, allowRepair, hintMaxLength) {
601
- if (content.length > hintMaxLength) {
602
- return null;
603
- }
604
- try {
605
- return {
606
- success: true,
607
- parsed: JSON.parse(content),
608
- repaired: null,
609
- usedRepair: false,
610
- stage: "parse",
611
- error: ""
612
- };
613
- } catch (directError) {
614
- if (!allowRepair) {
615
- return {
616
- success: false,
617
- parsed: null,
618
- repaired: null,
619
- usedRepair: false,
620
- stage: "parse",
621
- error: toErrorMessage(directError)
622
- };
623
- }
624
- let repaired;
625
- try {
626
- repaired = import_jsonrepair.jsonrepair(content);
627
- } catch (repairError) {
628
- return {
629
- success: false,
630
- parsed: null,
631
- repaired: null,
632
- usedRepair: true,
633
- stage: "repair",
634
- error: toErrorMessage(repairError)
635
- };
636
- }
637
- try {
638
- return {
639
- success: true,
640
- parsed: JSON.parse(repaired),
641
- repaired,
642
- usedRepair: true,
643
- stage: "parse",
644
- error: ""
645
- };
646
- } catch (parseError) {
647
- return {
648
- success: false,
649
- parsed: null,
650
- repaired,
651
- usedRepair: true,
652
- stage: "parse",
653
- error: toErrorMessage(parseError || directError)
654
- };
655
- }
656
- }
657
- }
658
- function resolveExtractionHeuristics(input) {
659
- const merged = {
660
- ...DEFAULT_EXTRACTION_HEURISTICS,
661
- ...input
662
- };
663
- const firstPassMin = normalizeInteger(merged.firstPassMin, DEFAULT_EXTRACTION_HEURISTICS.firstPassMin);
664
- const firstPassCap = Math.max(firstPassMin, normalizeInteger(merged.firstPassCap, DEFAULT_EXTRACTION_HEURISTICS.firstPassCap));
665
- const secondPassMin = normalizeInteger(merged.secondPassMin, DEFAULT_EXTRACTION_HEURISTICS.secondPassMin);
666
- const secondPassCap = Math.max(secondPassMin, normalizeInteger(merged.secondPassCap, DEFAULT_EXTRACTION_HEURISTICS.secondPassCap));
667
- return {
668
- firstPassMin,
669
- firstPassCap,
670
- firstPassMultiplier: normalizeInteger(merged.firstPassMultiplier, DEFAULT_EXTRACTION_HEURISTICS.firstPassMultiplier),
671
- secondPassMin,
672
- secondPassCap,
673
- secondPassMultiplier: normalizeInteger(merged.secondPassMultiplier, DEFAULT_EXTRACTION_HEURISTICS.secondPassMultiplier),
674
- hintMaxLength: normalizeInteger(merged.hintMaxLength, DEFAULT_EXTRACTION_HEURISTICS.hintMaxLength)
675
- };
676
- }
677
- function normalizeInteger(value, fallback) {
678
- if (typeof value !== "number" || !Number.isFinite(value)) {
679
- return fallback;
680
- }
681
- return Math.max(0, Math.floor(value));
682
- }
683
- function parseHintBonus(hint) {
684
- if (hint.success) {
685
- return hint.usedRepair ? 70 : 120;
686
- }
687
- return hint.usedRepair ? -20 : -10;
688
- }
689
491
  function hasBalancedJsonDelimiters(input) {
690
492
  const stack = [];
691
493
  let inString = false;
@@ -771,6 +573,207 @@ function dedupeCandidates(candidates) {
771
573
  function clamp(value, min, max) {
772
574
  return Math.max(min, Math.min(max, Math.floor(value)));
773
575
  }
576
+ function normalizeInteger(value, fallback) {
577
+ if (typeof value !== "number" || !Number.isFinite(value)) {
578
+ return fallback;
579
+ }
580
+ return Math.max(0, Math.floor(value));
581
+ }
582
+ function resolveExtractionHeuristics(input, defaults) {
583
+ const merged = {
584
+ ...defaults,
585
+ ...input
586
+ };
587
+ const firstPassMin = normalizeInteger(merged.firstPassMin, defaults.firstPassMin);
588
+ const firstPassCap = Math.max(firstPassMin, normalizeInteger(merged.firstPassCap, defaults.firstPassCap));
589
+ const secondPassMin = normalizeInteger(merged.secondPassMin, defaults.secondPassMin);
590
+ const secondPassCap = Math.max(secondPassMin, normalizeInteger(merged.secondPassCap, defaults.secondPassCap));
591
+ return {
592
+ firstPassMin,
593
+ firstPassCap,
594
+ firstPassMultiplier: normalizeInteger(merged.firstPassMultiplier, defaults.firstPassMultiplier),
595
+ secondPassMin,
596
+ secondPassCap,
597
+ secondPassMultiplier: normalizeInteger(merged.secondPassMultiplier, defaults.secondPassMultiplier),
598
+ hintMaxLength: normalizeInteger(merged.hintMaxLength, defaults.hintMaxLength)
599
+ };
600
+ }
601
+
602
+ // src/extract.ts
603
+ var DEFAULT_EXTRACTION_HEURISTICS = {
604
+ firstPassMin: 12,
605
+ firstPassCap: 30,
606
+ firstPassMultiplier: 6,
607
+ secondPassMin: 4,
608
+ secondPassCap: 8,
609
+ secondPassMultiplier: 2,
610
+ hintMaxLength: 50000
611
+ };
612
+ function extractJsonCandidates(input, options = {}) {
613
+ const maxCandidates = options.maxCandidates ?? 5;
614
+ const acceptArrays = options.acceptArrays ?? true;
615
+ const allowRepairHints = options.allowRepairHints ?? true;
616
+ const heuristics = resolveExtractionHeuristics(options.heuristics, DEFAULT_EXTRACTION_HEURISTICS);
617
+ const extractionInput = input;
618
+ const candidates = [];
619
+ candidates.push(...extractFromMarkdown(extractionInput, acceptArrays));
620
+ candidates.push(...scanBalancedSegments(extractionInput, acceptArrays));
621
+ if (candidates.length === 0 && extractionInput.trim()) {
622
+ const content = extractionInput.trim();
623
+ candidates.push({
624
+ id: "raw:fallback",
625
+ source: "raw",
626
+ content,
627
+ start: 0,
628
+ end: extractionInput.length,
629
+ score: 10 + Math.floor(lengthScore(content.length) / 3)
630
+ });
631
+ }
632
+ const prefiltered = prefilterByJsonShape(candidates, acceptArrays);
633
+ const firstPassLimit = clamp(maxCandidates * heuristics.firstPassMultiplier, heuristics.firstPassMin, heuristics.firstPassCap);
634
+ const firstPass = prefiltered.slice(0, firstPassLimit);
635
+ const secondPassLimit = Math.min(firstPass.length, clamp(maxCandidates * heuristics.secondPassMultiplier, heuristics.secondPassMin, heuristics.secondPassCap));
636
+ for (let i = 0;i < secondPassLimit; i += 1) {
637
+ const candidate = firstPass[i];
638
+ if (!candidate) {
639
+ continue;
640
+ }
641
+ const parseHint = buildParseHint(candidate.content, allowRepairHints, heuristics.hintMaxLength);
642
+ if (!parseHint) {
643
+ continue;
644
+ }
645
+ candidate.parseHint = parseHint;
646
+ candidate.score += parseHintBonus(parseHint);
647
+ }
648
+ const sorted = sortCandidates(firstPass);
649
+ const deduped = dedupeCandidates(sorted);
650
+ return deduped.slice(0, maxCandidates).map((candidate, index) => ({
651
+ id: `${candidate.source}:${index}`,
652
+ source: candidate.source,
653
+ content: candidate.content,
654
+ language: candidate.language,
655
+ parseHint: candidate.parseHint,
656
+ start: candidate.start,
657
+ end: candidate.end,
658
+ score: candidate.score
659
+ }));
660
+ }
661
+ function prefilterByJsonShape(candidates, acceptArrays) {
662
+ const shaped = candidates.map((candidate) => {
663
+ const shapeScore = jsonShapeScore(candidate.content, acceptArrays);
664
+ return {
665
+ ...candidate,
666
+ shapeScore,
667
+ score: candidate.score + shapeScore
668
+ };
669
+ });
670
+ const sorted = sortCandidates(shaped);
671
+ const deduped = dedupeCandidates(sorted);
672
+ const filtered = deduped.filter((candidate) => passesShapeFilter(candidate));
673
+ if (filtered.length > 0) {
674
+ const fallback = deduped.find((candidate) => !passesShapeFilter(candidate));
675
+ if (fallback) {
676
+ filtered.push(fallback);
677
+ }
678
+ return sortCandidates(filtered);
679
+ }
680
+ return deduped.slice(0, Math.min(1, deduped.length));
681
+ }
682
+ function extractFromMarkdown(input, acceptArrays) {
683
+ const blocks = extractMarkdownCodeBlocks(input);
684
+ return blocks.flatMap((block, index) => {
685
+ const language = block.language || null;
686
+ const content = block.code.trim();
687
+ if (!content) {
688
+ return [];
689
+ }
690
+ if (!looksLikeJsonEnvelope(content, acceptArrays)) {
691
+ return [];
692
+ }
693
+ const langBonus = languageBonus(language);
694
+ return [
695
+ {
696
+ id: `fenced:${index}`,
697
+ source: "fenced",
698
+ language,
699
+ content,
700
+ start: block.start,
701
+ end: block.end,
702
+ score: 260 + langBonus + lengthScore(content.length)
703
+ }
704
+ ];
705
+ });
706
+ }
707
+ function scanBalancedSegments(input, acceptArrays) {
708
+ const results = [];
709
+ const stack = [];
710
+ let inString = false;
711
+ let quote = null;
712
+ let escaped = false;
713
+ for (let i = 0;i < input.length; i += 1) {
714
+ const char = input[i];
715
+ if (!char) {
716
+ continue;
717
+ }
718
+ if (inString) {
719
+ if (escaped) {
720
+ escaped = false;
721
+ continue;
722
+ }
723
+ if (char === "\\") {
724
+ escaped = true;
725
+ continue;
726
+ }
727
+ if (char === quote) {
728
+ inString = false;
729
+ quote = null;
730
+ }
731
+ continue;
732
+ }
733
+ const allowSingleQuoted = stack.length > 0;
734
+ if (char === '"' || allowSingleQuoted && (char === "'" || char === "`")) {
735
+ inString = true;
736
+ quote = char;
737
+ continue;
738
+ }
739
+ if (char === "{" || char === "[") {
740
+ stack.push({ char, index: i });
741
+ continue;
742
+ }
743
+ if (char !== "}" && char !== "]") {
744
+ continue;
745
+ }
746
+ const expectedOpen = char === "}" ? "{" : "[";
747
+ while (stack.length > 0 && stack[stack.length - 1]?.char !== expectedOpen) {
748
+ stack.pop();
749
+ }
750
+ const opened = stack.pop();
751
+ if (!opened) {
752
+ continue;
753
+ }
754
+ if (stack.length > 0) {
755
+ continue;
756
+ }
757
+ if (!acceptArrays && opened.char === "[") {
758
+ continue;
759
+ }
760
+ const content = input.slice(opened.index, i + 1).trim();
761
+ if (!content) {
762
+ continue;
763
+ }
764
+ const rootBonus = opened.char === "{" ? 40 : 20;
765
+ const boundaryBonus = boundaryScore(input, opened.index, i + 1);
766
+ results.push({
767
+ id: `scan:${results.length}`,
768
+ source: "scan",
769
+ content,
770
+ start: opened.index,
771
+ end: i + 1,
772
+ score: 120 + rootBonus + boundaryBonus + lengthScore(content.length)
773
+ });
774
+ }
775
+ return results;
776
+ }
774
777
  // src/schema.ts
775
778
  var RE_SIMPLE_IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
776
779
  var RE_WHITESPACE = /\s+/g;
@@ -1219,17 +1222,107 @@ function findSSEBoundary(buffer) {
1219
1222
  return Math.max(crlfIndex, lfIndex);
1220
1223
  }
1221
1224
 
1222
- // src/providers/mcp-runtime.ts
1223
- var DEFAULT_MAX_TOOL_ROUNDS = 100;
1224
- async function resolveMCPToolset(clients) {
1225
- if (!Array.isArray(clients) || clients.length === 0) {
1226
- return {
1227
- tools: [],
1228
- byName: new Map
1229
- };
1230
- }
1231
- const listed = [];
1232
- for (const client of clients) {
1225
+ // src/providers/mcp-runtime-debug.ts
1226
+ function formatToolExecutionDebugLine(execution) {
1227
+ const status = execution.error ? "error" : "ok";
1228
+ const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
1229
+ const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
1230
+ const duration = typeof execution.durationMs === "number" ? ` ${execution.durationMs}ms` : "";
1231
+ const base = `[tool:mcp:${status}] ${scope} ${toolRef}#${execution.callId}${duration}`;
1232
+ if (execution.error) {
1233
+ return `${base} -> ${execution.error}`;
1234
+ }
1235
+ return base;
1236
+ }
1237
+ function emitToolExecution(request, execution) {
1238
+ request.onToolExecution?.(execution);
1239
+ const debug = resolveToolDebugOptions(request.toolDebug);
1240
+ if (!debug.enabled) {
1241
+ return;
1242
+ }
1243
+ debug.logger(formatToolExecutionDebugLine(execution));
1244
+ if (debug.includeRequest) {
1245
+ debug.logger(formatToolExecutionRequestDebugLine(execution, debug));
1246
+ }
1247
+ if (debug.includeResult && (!execution.error || debug.includeResultOnError)) {
1248
+ debug.logger(formatToolExecutionResultDebugLine(execution, debug));
1249
+ }
1250
+ }
1251
+ function resolveToolDebugOptions(value) {
1252
+ if (value === true) {
1253
+ return {
1254
+ enabled: true,
1255
+ logger: defaultToolDebugLogger,
1256
+ includeRequest: true,
1257
+ includeResult: true,
1258
+ includeResultOnError: true,
1259
+ pretty: false
1260
+ };
1261
+ }
1262
+ if (value === undefined || value === false) {
1263
+ return {
1264
+ enabled: false,
1265
+ logger: () => {
1266
+ return;
1267
+ },
1268
+ includeRequest: false,
1269
+ includeResult: false,
1270
+ includeResultOnError: false,
1271
+ pretty: false
1272
+ };
1273
+ }
1274
+ return {
1275
+ enabled: value.enabled ?? true,
1276
+ logger: value.logger ?? defaultToolDebugLogger,
1277
+ includeRequest: value.includeRequest ?? true,
1278
+ includeResult: value.includeResult ?? true,
1279
+ includeResultOnError: value.includeResultOnError ?? true,
1280
+ pretty: value.pretty ?? false
1281
+ };
1282
+ }
1283
+ function defaultToolDebugLogger(line) {
1284
+ const { log } = globalThis.console;
1285
+ log(line);
1286
+ }
1287
+ function formatToolExecutionRequestDebugLine(execution, debug) {
1288
+ const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
1289
+ const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
1290
+ const payload = formatDebugPayload(execution.arguments, debug.pretty);
1291
+ return `[tool:mcp:request] ${scope} ${toolRef}#${execution.callId} arguments=${payload}`;
1292
+ }
1293
+ function formatToolExecutionResultDebugLine(execution, debug) {
1294
+ const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
1295
+ const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
1296
+ if (execution.error) {
1297
+ const payload2 = formatDebugPayload({ error: execution.error }, debug.pretty);
1298
+ return `[tool:mcp:result:error] ${scope} ${toolRef}#${execution.callId} output=${payload2}`;
1299
+ }
1300
+ const payload = formatDebugPayload(execution.output, debug.pretty);
1301
+ return `[tool:mcp:result:ok] ${scope} ${toolRef}#${execution.callId} output=${payload}`;
1302
+ }
1303
+ function formatDebugPayload(value, pretty) {
1304
+ if (value === undefined) {
1305
+ return "undefined";
1306
+ }
1307
+ try {
1308
+ const serialized = JSON.stringify(value, null, pretty ? 2 : 0);
1309
+ return serialized ?? "undefined";
1310
+ } catch {
1311
+ return String(value);
1312
+ }
1313
+ }
1314
+
1315
+ // src/providers/mcp-runtime.ts
1316
+ var DEFAULT_MAX_TOOL_ROUNDS = 100;
1317
+ async function resolveMCPToolset(clients) {
1318
+ if (!Array.isArray(clients) || clients.length === 0) {
1319
+ return {
1320
+ tools: [],
1321
+ byName: new Map
1322
+ };
1323
+ }
1324
+ const listed = [];
1325
+ for (const client of clients) {
1233
1326
  let cursor;
1234
1327
  do {
1235
1328
  const page = await client.listTools(cursor ? { cursor } : undefined);
@@ -1411,90 +1504,6 @@ function stringifyToolOutput(value) {
1411
1504
  }
1412
1505
  return JSON.stringify(value ?? null);
1413
1506
  }
1414
- function formatToolExecutionDebugLine(execution) {
1415
- const status = execution.error ? "error" : "ok";
1416
- const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
1417
- const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
1418
- const duration = typeof execution.durationMs === "number" ? ` ${execution.durationMs}ms` : "";
1419
- const base = `[tool:mcp:${status}] ${scope} ${toolRef}#${execution.callId}${duration}`;
1420
- if (execution.error) {
1421
- return `${base} -> ${execution.error}`;
1422
- }
1423
- return base;
1424
- }
1425
- function emitToolExecution(request, execution) {
1426
- request.onToolExecution?.(execution);
1427
- const debug = resolveToolDebugOptions(request.toolDebug);
1428
- if (!debug.enabled) {
1429
- return;
1430
- }
1431
- debug.logger(formatToolExecutionDebugLine(execution));
1432
- if (debug.includeRequest) {
1433
- debug.logger(formatToolExecutionRequestDebugLine(execution, debug));
1434
- }
1435
- if (debug.includeResult && (!execution.error || debug.includeResultOnError)) {
1436
- debug.logger(formatToolExecutionResultDebugLine(execution, debug));
1437
- }
1438
- }
1439
- function resolveToolDebugOptions(value) {
1440
- if (value === true) {
1441
- return {
1442
- enabled: true,
1443
- logger: (line) => console.log(line),
1444
- includeRequest: true,
1445
- includeResult: true,
1446
- includeResultOnError: true,
1447
- pretty: false
1448
- };
1449
- }
1450
- if (value === undefined || value === false) {
1451
- return {
1452
- enabled: false,
1453
- logger: () => {
1454
- return;
1455
- },
1456
- includeRequest: false,
1457
- includeResult: false,
1458
- includeResultOnError: false,
1459
- pretty: false
1460
- };
1461
- }
1462
- return {
1463
- enabled: value.enabled ?? true,
1464
- logger: value.logger ?? ((line) => console.log(line)),
1465
- includeRequest: value.includeRequest ?? true,
1466
- includeResult: value.includeResult ?? true,
1467
- includeResultOnError: value.includeResultOnError ?? true,
1468
- pretty: value.pretty ?? false
1469
- };
1470
- }
1471
- function formatToolExecutionRequestDebugLine(execution, debug) {
1472
- const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
1473
- const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
1474
- const payload = formatDebugPayload(execution.arguments, debug.pretty);
1475
- return `[tool:mcp:request] ${scope} ${toolRef}#${execution.callId} arguments=${payload}`;
1476
- }
1477
- function formatToolExecutionResultDebugLine(execution, debug) {
1478
- const scope = [execution.provider, execution.model].filter(Boolean).join("/") || "unknown";
1479
- const toolRef = execution.clientId ? `${execution.clientId}:${execution.name ?? "unknown"}` : execution.name ?? "unknown";
1480
- if (execution.error) {
1481
- const payload2 = formatDebugPayload({ error: execution.error }, debug.pretty);
1482
- return `[tool:mcp:result:error] ${scope} ${toolRef}#${execution.callId} output=${payload2}`;
1483
- }
1484
- const payload = formatDebugPayload(execution.output, debug.pretty);
1485
- return `[tool:mcp:result:ok] ${scope} ${toolRef}#${execution.callId} output=${payload}`;
1486
- }
1487
- function formatDebugPayload(value, pretty) {
1488
- if (value === undefined) {
1489
- return "undefined";
1490
- }
1491
- try {
1492
- const serialized = JSON.stringify(value, null, pretty ? 2 : 0);
1493
- return serialized ?? "undefined";
1494
- } catch {
1495
- return String(value);
1496
- }
1497
- }
1498
1507
  function countNameCollisions(names) {
1499
1508
  const out = new Map;
1500
1509
  for (const name of names) {
@@ -1691,103 +1700,89 @@ function createOpenAICompatibleAdapter(options) {
1691
1700
  if (usesMCP) {
1692
1701
  return streamWithChatCompletionsWithMCP(options, fetcher, path, request, callbacks);
1693
1702
  }
1694
- const response = await fetcher(buildURL(options.baseURL, path), {
1695
- method: "POST",
1696
- headers: buildHeaders(options),
1697
- body: JSON.stringify(cleanUndefined({
1698
- ...options.defaultBody,
1699
- ...request.body,
1700
- model: options.model,
1701
- messages: buildMessages(request),
1702
- temperature: request.temperature,
1703
- reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1704
- max_tokens: request.maxTokens,
1705
- stream: true
1706
- })),
1707
- signal: request.signal
1708
- });
1709
- if (!response.ok) {
1710
- const message = await response.text();
1711
- throw new Error(`HTTP ${response.status}: ${message}`);
1712
- }
1713
- callbacks.onStart?.();
1714
- let text = "";
1715
- let reasoning = "";
1716
- let usage;
1717
- let finishReason;
1718
- await consumeSSE(response, (data) => {
1719
- if (data === "[DONE]") {
1720
- return;
1721
- }
1722
- const json = safeJSONParse(data);
1723
- if (!isRecord2(json)) {
1724
- return;
1725
- }
1726
- const delta = pickAssistantDelta(json);
1727
- const reasoningDelta = pickAssistantReasoningDelta(json);
1728
- const chunkUsage = pickUsage(json);
1729
- const chunkFinishReason = pickFinishReason(json);
1730
- usage = preferLatestUsage(usage, chunkUsage);
1731
- if (chunkFinishReason) {
1732
- finishReason = chunkFinishReason;
1733
- }
1734
- if (delta) {
1735
- text += delta;
1736
- callbacks.onToken?.(delta);
1737
- }
1738
- if (reasoningDelta) {
1739
- reasoning += reasoningDelta;
1740
- }
1741
- if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
1742
- const chunk = {
1743
- textDelta: delta,
1744
- reasoningDelta: reasoningDelta || undefined,
1745
- raw: json,
1746
- usage: chunkUsage,
1747
- finishReason: chunkFinishReason
1748
- };
1749
- callbacks.onChunk?.(chunk);
1750
- }
1751
- });
1752
- const out = {
1753
- text,
1754
- reasoning: reasoning.length > 0 ? reasoning : undefined,
1755
- usage,
1756
- finishReason
1757
- };
1758
- callbacks.onComplete?.(out);
1759
- return out;
1703
+ return streamWithChatCompletionsPassThrough(options, fetcher, path, request, callbacks);
1760
1704
  },
1761
1705
  async embed(request) {
1762
- const body = cleanUndefined({
1763
- ...options.defaultBody,
1764
- ...request.body,
1765
- model: request.model ?? options.model,
1766
- input: request.input,
1767
- dimensions: request.dimensions,
1768
- encoding_format: "float"
1769
- });
1770
- const response = await fetcher(buildURL(options.baseURL, embeddingPath), {
1771
- method: "POST",
1772
- headers: buildHeaders(options),
1773
- body: JSON.stringify(body)
1774
- });
1775
- if (!response.ok) {
1776
- const message = await response.text();
1777
- throw new Error(`HTTP ${response.status}: ${message}`);
1778
- }
1779
- const json = await response.json();
1780
- const data = json.data;
1781
- if (!Array.isArray(data)) {
1782
- throw new Error("Unexpected embedding response: missing data array");
1783
- }
1784
- return {
1785
- embeddings: data.map((d) => isRecord2(d) && Array.isArray(d.embedding) ? d.embedding : []),
1786
- model: pickString(json.model) ?? body.model,
1787
- usage: pickUsage(json),
1788
- raw: json
1789
- };
1706
+ return embedOpenAI(options, fetcher, embeddingPath, request);
1707
+ }
1708
+ };
1709
+ }
1710
+ async function streamWithChatCompletionsPassThrough(options, fetcher, path, request, callbacks) {
1711
+ const response = await sendOpenAIRequest(options, fetcher, path, request, buildChatCompletionsBody(options, request, {
1712
+ messages: buildMessages(request),
1713
+ stream: true
1714
+ }));
1715
+ if (!response.ok) {
1716
+ const message = await response.text();
1717
+ throw new Error(`HTTP ${response.status}: ${message}`);
1718
+ }
1719
+ callbacks.onStart?.();
1720
+ let text = "";
1721
+ let reasoning = "";
1722
+ let usage;
1723
+ let finishReason;
1724
+ await consumeSSE(response, (data) => {
1725
+ if (data === "[DONE]") {
1726
+ return;
1727
+ }
1728
+ const json = safeJSONParse(data);
1729
+ if (!isRecord2(json)) {
1730
+ return;
1731
+ }
1732
+ const delta = pickAssistantDelta(json);
1733
+ const reasoningDelta = pickAssistantReasoningDelta(json);
1734
+ const chunkUsage = pickUsage(json);
1735
+ const chunkFinishReason = pickFinishReason(json);
1736
+ usage = preferLatestUsage(usage, chunkUsage);
1737
+ if (chunkFinishReason) {
1738
+ finishReason = chunkFinishReason;
1739
+ }
1740
+ if (delta) {
1741
+ text += delta;
1742
+ callbacks.onToken?.(delta);
1790
1743
  }
1744
+ if (reasoningDelta) {
1745
+ reasoning += reasoningDelta;
1746
+ }
1747
+ emitOpenAIStreamChunk(callbacks, undefined, json, delta, reasoningDelta, chunkUsage, chunkFinishReason);
1748
+ });
1749
+ const out = {
1750
+ text,
1751
+ reasoning: reasoning.length > 0 ? reasoning : undefined,
1752
+ usage,
1753
+ finishReason
1754
+ };
1755
+ callbacks.onComplete?.(out);
1756
+ return out;
1757
+ }
1758
+ async function embedOpenAI(options, fetcher, path, request) {
1759
+ const body = cleanUndefined({
1760
+ ...options.defaultBody,
1761
+ ...request.body,
1762
+ model: request.model ?? options.model,
1763
+ input: request.input,
1764
+ dimensions: request.dimensions,
1765
+ encoding_format: "float"
1766
+ });
1767
+ const response = await fetcher(buildURL(options.baseURL, path), {
1768
+ method: "POST",
1769
+ headers: buildHeaders(options),
1770
+ body: JSON.stringify(body)
1771
+ });
1772
+ if (!response.ok) {
1773
+ const message = await response.text();
1774
+ throw new Error(`HTTP ${response.status}: ${message}`);
1775
+ }
1776
+ const json = await response.json();
1777
+ const data = json.data;
1778
+ if (!Array.isArray(data)) {
1779
+ throw new Error("Unexpected embedding response: missing data array");
1780
+ }
1781
+ return {
1782
+ embeddings: data.map((d) => isRecord2(d) && Array.isArray(d.embedding) ? d.embedding : []),
1783
+ model: pickString(json.model) ?? body.model,
1784
+ usage: pickUsage(json),
1785
+ raw: json
1791
1786
  };
1792
1787
  }
1793
1788
  async function completeOpenAIRequest(options, fetcher, chatPath, responsesPath, request) {
@@ -1804,22 +1799,80 @@ async function completeOpenAIRequest(options, fetcher, chatPath, responsesPath,
1804
1799
  }
1805
1800
  return completeWithChatCompletionsPassThrough(options, fetcher, chatPath, request);
1806
1801
  }
1807
- async function completeWithChatCompletionsPassThrough(options, fetcher, path, request) {
1808
- const response = await fetcher(buildURL(options.baseURL, path), {
1802
+ function buildChatCompletionsBody(options, request, overrides) {
1803
+ return buildOpenAIRequestBody(options, request, "max_tokens", overrides);
1804
+ }
1805
+ function buildResponsesBody(options, request, overrides) {
1806
+ return buildOpenAIRequestBody(options, request, "max_output_tokens", overrides);
1807
+ }
1808
+ function buildOpenAIRequestBody(options, request, maxTokenKey, overrides) {
1809
+ return cleanUndefined({
1810
+ ...options.defaultBody,
1811
+ ...request.body,
1812
+ model: options.model,
1813
+ temperature: request.temperature,
1814
+ reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1815
+ [maxTokenKey]: request.maxTokens,
1816
+ ...overrides
1817
+ });
1818
+ }
1819
+ function sendOpenAIRequest(options, fetcher, path, request, body) {
1820
+ return fetcher(buildURL(options.baseURL, path), {
1809
1821
  method: "POST",
1810
1822
  headers: buildHeaders(options),
1811
- body: JSON.stringify(cleanUndefined({
1812
- ...options.defaultBody,
1813
- ...request.body,
1814
- model: options.model,
1815
- messages: buildMessages(request),
1816
- temperature: request.temperature,
1817
- reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1818
- max_tokens: request.maxTokens,
1819
- stream: false
1820
- })),
1823
+ body: JSON.stringify(body),
1821
1824
  signal: request.signal
1822
1825
  });
1826
+ }
1827
+ async function sendOpenAIJsonRequest(options, fetcher, path, request, body) {
1828
+ const response = await sendOpenAIRequest(options, fetcher, path, request, body);
1829
+ if (!response.ok) {
1830
+ const message = await response.text();
1831
+ throw new Error(`HTTP ${response.status}: ${message}`);
1832
+ }
1833
+ return await response.json();
1834
+ }
1835
+ function createResponsesMCPState(request) {
1836
+ return {
1837
+ input: buildResponsesInput(request),
1838
+ previousResponseId: pickString(isRecord2(request.body) ? request.body.previous_response_id : undefined),
1839
+ aggregatedUsage: undefined,
1840
+ finishReason: undefined,
1841
+ lastPayload: undefined,
1842
+ executedToolCalls: [],
1843
+ toolExecutions: [],
1844
+ reasoningBlocks: []
1845
+ };
1846
+ }
1847
+ function buildResponsesMCPResult(state, text, raw) {
1848
+ return {
1849
+ text,
1850
+ reasoning: joinReasoningBlocks(state.reasoningBlocks) || undefined,
1851
+ reasoningBlocks: state.reasoningBlocks.length > 0 ? state.reasoningBlocks : undefined,
1852
+ raw,
1853
+ usage: state.aggregatedUsage,
1854
+ finishReason: state.finishReason,
1855
+ toolCalls: state.executedToolCalls.length > 0 ? state.executedToolCalls : undefined,
1856
+ toolExecutions: state.toolExecutions.length > 0 ? state.toolExecutions : undefined
1857
+ };
1858
+ }
1859
+ function emitOpenAIStreamChunk(callbacks, round, raw, delta, reasoningDelta, usage, finishReason) {
1860
+ if (delta || reasoningDelta || usage || finishReason) {
1861
+ callbacks.onChunk?.({
1862
+ textDelta: delta,
1863
+ reasoningDelta: reasoningDelta || undefined,
1864
+ ...round !== undefined ? { turnIndex: round } : {},
1865
+ raw,
1866
+ usage,
1867
+ finishReason
1868
+ });
1869
+ }
1870
+ }
1871
+ async function completeWithChatCompletionsPassThrough(options, fetcher, path, request) {
1872
+ const response = await sendOpenAIRequest(options, fetcher, path, request, buildChatCompletionsBody(options, request, {
1873
+ messages: buildMessages(request),
1874
+ stream: false
1875
+ }));
1823
1876
  if (!response.ok) {
1824
1877
  const message = await response.text();
1825
1878
  throw new Error(`HTTP ${response.status}: ${message}`);
@@ -1871,28 +1924,12 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
1871
1924
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
1872
1925
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
1873
1926
  const transportTools = toProviderFunctionTools(mcpToolset);
1874
- const response = await fetcher(buildURL(options.baseURL, path), {
1875
- method: "POST",
1876
- headers: buildHeaders(options),
1877
- body: JSON.stringify(cleanUndefined({
1878
- ...options.defaultBody,
1879
- ...request.body,
1880
- model: options.model,
1881
- messages,
1882
- temperature: request.temperature,
1883
- reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1884
- max_tokens: request.maxTokens,
1885
- tools: transportTools,
1886
- tool_choice: request.toolChoice,
1887
- parallel_tool_calls: request.parallelToolCalls
1888
- })),
1889
- signal: request.signal
1890
- });
1891
- if (!response.ok) {
1892
- const message = await response.text();
1893
- throw new Error(`HTTP ${response.status}: ${message}`);
1894
- }
1895
- const payload = await response.json();
1927
+ const payload = await sendOpenAIJsonRequest(options, fetcher, path, request, buildChatCompletionsBody(options, request, {
1928
+ messages,
1929
+ tools: transportTools,
1930
+ tool_choice: request.toolChoice,
1931
+ parallel_tool_calls: request.parallelToolCalls
1932
+ }));
1896
1933
  lastPayload = payload;
1897
1934
  aggregatedUsage = mergeUsage(aggregatedUsage, pickUsage(payload));
1898
1935
  finishReason = pickFinishReason(payload);
@@ -1947,26 +1984,10 @@ async function completeWithChatCompletionsWithMCP(options, fetcher, path, reques
1947
1984
  }
1948
1985
  async function completeWithResponsesAPIPassThrough(options, fetcher, path, request) {
1949
1986
  const body = isRecord2(request.body) ? request.body : undefined;
1950
- const response = await fetcher(buildURL(options.baseURL, path), {
1951
- method: "POST",
1952
- headers: buildHeaders(options),
1953
- body: JSON.stringify(cleanUndefined({
1954
- ...options.defaultBody,
1955
- ...request.body,
1956
- model: options.model,
1957
- input: buildResponsesInput(request),
1958
- previous_response_id: pickString(body?.previous_response_id),
1959
- temperature: request.temperature,
1960
- reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
1961
- max_output_tokens: request.maxTokens
1962
- })),
1963
- signal: request.signal
1964
- });
1965
- if (!response.ok) {
1966
- const message = await response.text();
1967
- throw new Error(`HTTP ${response.status}: ${message}`);
1968
- }
1969
- const payload = await response.json();
1987
+ const payload = await sendOpenAIJsonRequest(options, fetcher, path, request, buildResponsesBody(options, request, {
1988
+ input: buildResponsesInput(request),
1989
+ previous_response_id: pickString(body?.previous_response_id)
1990
+ }));
1970
1991
  const toolCalls = pickResponsesToolCalls(payload);
1971
1992
  return {
1972
1993
  text: pickResponsesText(payload) || pickAssistantText(payload),
@@ -1978,58 +1999,26 @@ async function completeWithResponsesAPIPassThrough(options, fetcher, path, reque
1978
1999
  }
1979
2000
  async function completeWithResponsesAPIWithMCP(options, fetcher, path, request) {
1980
2001
  const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
1981
- let input = buildResponsesInput(request);
1982
- let previousResponseId = pickString(isRecord2(request.body) ? request.body.previous_response_id : undefined);
1983
- let aggregatedUsage;
1984
- let finishReason;
1985
- let lastPayload;
1986
- const executedToolCalls = [];
1987
- const toolExecutions = [];
1988
- const reasoningBlocks = [];
2002
+ const state = createResponsesMCPState(request);
1989
2003
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
1990
2004
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
1991
2005
  const transportTools = toResponsesTools(toProviderFunctionTools(mcpToolset));
1992
- const response = await fetcher(buildURL(options.baseURL, path), {
1993
- method: "POST",
1994
- headers: buildHeaders(options),
1995
- body: JSON.stringify(cleanUndefined({
1996
- ...options.defaultBody,
1997
- ...request.body,
1998
- model: options.model,
1999
- input,
2000
- previous_response_id: previousResponseId,
2001
- temperature: request.temperature,
2002
- reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
2003
- max_output_tokens: request.maxTokens,
2004
- tools: transportTools,
2005
- tool_choice: request.toolChoice,
2006
- parallel_tool_calls: request.parallelToolCalls
2007
- })),
2008
- signal: request.signal
2009
- });
2010
- if (!response.ok) {
2011
- const message = await response.text();
2012
- throw new Error(`HTTP ${response.status}: ${message}`);
2013
- }
2014
- const payload = await response.json();
2015
- lastPayload = payload;
2016
- aggregatedUsage = mergeUsage(aggregatedUsage, pickUsage(payload));
2017
- finishReason = pickResponsesFinishReason(payload) ?? finishReason;
2018
- pushReasoningBlock(reasoningBlocks, round, pickResponsesReasoning(payload));
2006
+ const payload = await sendOpenAIJsonRequest(options, fetcher, path, request, buildResponsesBody(options, request, {
2007
+ input: state.input,
2008
+ previous_response_id: state.previousResponseId,
2009
+ tools: transportTools,
2010
+ tool_choice: request.toolChoice,
2011
+ parallel_tool_calls: request.parallelToolCalls
2012
+ }));
2013
+ state.lastPayload = payload;
2014
+ state.aggregatedUsage = mergeUsage(state.aggregatedUsage, pickUsage(payload));
2015
+ state.finishReason = pickResponsesFinishReason(payload) ?? state.finishReason;
2016
+ pushReasoningBlock(state.reasoningBlocks, round, pickResponsesReasoning(payload));
2019
2017
  const providerToolCalls = pickResponsesToolCalls(payload);
2020
2018
  const functionCalls = providerToolCalls.filter((toolCall) => toolCall.type === "function" && typeof toolCall.id === "string" && typeof toolCall.name === "string");
2021
2019
  if (functionCalls.length === 0) {
2022
2020
  const text = pickResponsesText(payload) || pickAssistantText(payload);
2023
- return {
2024
- text,
2025
- reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
2026
- reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
2027
- raw: payload,
2028
- usage: aggregatedUsage,
2029
- finishReason,
2030
- toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
2031
- toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
2032
- };
2021
+ return buildResponsesMCPResult(state, text, payload);
2033
2022
  }
2034
2023
  if (round > maxToolRounds) {
2035
2024
  throw new Error(`Tool call loop exceeded maxToolRounds (${maxToolRounds}).`);
@@ -2040,25 +2029,16 @@ async function completeWithResponsesAPIWithMCP(options, fetcher, path, request)
2040
2029
  provider: "openai-compatible",
2041
2030
  model: options.model
2042
2031
  });
2043
- executedToolCalls.push(...outputs.map((entry) => entry.call));
2044
- toolExecutions.push(...outputs.map((entry) => entry.execution));
2045
- input = outputs.map((entry) => ({
2032
+ state.executedToolCalls.push(...outputs.map((entry) => entry.call));
2033
+ state.toolExecutions.push(...outputs.map((entry) => entry.execution));
2034
+ state.input = outputs.map((entry) => ({
2046
2035
  type: "function_call_output",
2047
2036
  call_id: entry.call.id,
2048
2037
  output: stringifyToolOutput(entry.call.error ? { error: entry.call.error } : entry.call.output)
2049
2038
  }));
2050
- previousResponseId = pickString(payload.id);
2039
+ state.previousResponseId = pickString(payload.id);
2051
2040
  }
2052
- return {
2053
- text: pickResponsesText(lastPayload ?? {}) || pickAssistantText(lastPayload ?? {}),
2054
- reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
2055
- reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
2056
- raw: lastPayload,
2057
- usage: aggregatedUsage,
2058
- finishReason,
2059
- toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
2060
- toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
2061
- };
2041
+ return buildResponsesMCPResult(state, pickResponsesText(state.lastPayload ?? {}) || pickAssistantText(state.lastPayload ?? {}), state.lastPayload);
2062
2042
  }
2063
2043
  async function streamWithChatCompletionsWithMCP(options, fetcher, path, request, callbacks) {
2064
2044
  const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
@@ -2074,24 +2054,13 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2074
2054
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
2075
2055
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
2076
2056
  const transportTools = toProviderFunctionTools(mcpToolset);
2077
- const response = await fetcher(buildURL(options.baseURL, path), {
2078
- method: "POST",
2079
- headers: buildHeaders(options),
2080
- body: JSON.stringify(cleanUndefined({
2081
- ...options.defaultBody,
2082
- ...request.body,
2083
- model: options.model,
2084
- messages,
2085
- temperature: request.temperature,
2086
- reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
2087
- max_tokens: request.maxTokens,
2088
- tools: transportTools,
2089
- tool_choice: request.toolChoice,
2090
- parallel_tool_calls: request.parallelToolCalls,
2091
- stream: true
2092
- })),
2093
- signal: request.signal
2094
- });
2057
+ const response = await sendOpenAIRequest(options, fetcher, path, request, buildChatCompletionsBody(options, request, {
2058
+ messages,
2059
+ tools: transportTools,
2060
+ tool_choice: request.toolChoice,
2061
+ parallel_tool_calls: request.parallelToolCalls,
2062
+ stream: true
2063
+ }));
2095
2064
  if (!response.ok) {
2096
2065
  const message = await response.text();
2097
2066
  throw new Error(`HTTP ${response.status}: ${message}`);
@@ -2128,17 +2097,7 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2128
2097
  roundReasoning += reasoningDelta;
2129
2098
  reasoningFieldName ??= pickAssistantReasoningDeltaFieldName(json);
2130
2099
  }
2131
- if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
2132
- const chunk = {
2133
- textDelta: delta,
2134
- reasoningDelta: reasoningDelta || undefined,
2135
- turnIndex: round,
2136
- raw: json,
2137
- usage: chunkUsage,
2138
- finishReason: chunkFinishReason
2139
- };
2140
- callbacks.onChunk?.(chunk);
2141
- }
2100
+ emitOpenAIStreamChunk(callbacks, round, json, delta, reasoningDelta, chunkUsage, chunkFinishReason);
2142
2101
  });
2143
2102
  aggregatedUsage = mergeUsage(aggregatedUsage, roundUsage);
2144
2103
  if (roundFinishReason) {
@@ -2217,22 +2176,11 @@ async function streamWithChatCompletionsWithMCP(options, fetcher, path, request,
2217
2176
  }
2218
2177
  async function streamWithResponsesAPIPassThrough(options, fetcher, path, request, callbacks) {
2219
2178
  const body = isRecord2(request.body) ? request.body : undefined;
2220
- const response = await fetcher(buildURL(options.baseURL, path), {
2221
- method: "POST",
2222
- headers: buildHeaders(options),
2223
- body: JSON.stringify(cleanUndefined({
2224
- ...options.defaultBody,
2225
- ...request.body,
2226
- model: options.model,
2227
- input: buildResponsesInput(request),
2228
- previous_response_id: pickString(body?.previous_response_id),
2229
- temperature: request.temperature,
2230
- reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
2231
- max_output_tokens: request.maxTokens,
2232
- stream: true
2233
- })),
2234
- signal: request.signal
2235
- });
2179
+ const response = await sendOpenAIRequest(options, fetcher, path, request, buildResponsesBody(options, request, {
2180
+ input: buildResponsesInput(request),
2181
+ previous_response_id: pickString(body?.previous_response_id),
2182
+ stream: true
2183
+ }));
2236
2184
  if (!response.ok) {
2237
2185
  const message = await response.text();
2238
2186
  throw new Error(`HTTP ${response.status}: ${message}`);
@@ -2265,15 +2213,7 @@ async function streamWithResponsesAPIPassThrough(options, fetcher, path, request
2265
2213
  text += delta;
2266
2214
  callbacks.onToken?.(delta);
2267
2215
  }
2268
- if (delta || chunkUsage || chunkFinishReason) {
2269
- const chunk = {
2270
- textDelta: delta,
2271
- raw: json,
2272
- usage: chunkUsage,
2273
- finishReason: chunkFinishReason
2274
- };
2275
- callbacks.onChunk?.(chunk);
2276
- }
2216
+ emitOpenAIStreamChunk(callbacks, undefined, json, delta, "", chunkUsage, chunkFinishReason);
2277
2217
  });
2278
2218
  const finalPayload = lastPayload ?? {};
2279
2219
  const out = {
@@ -2287,37 +2227,19 @@ async function streamWithResponsesAPIPassThrough(options, fetcher, path, request
2287
2227
  }
2288
2228
  async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, callbacks) {
2289
2229
  const maxToolRounds = normalizeMaxToolRounds(request.maxToolRounds ?? options.defaultMaxToolRounds);
2290
- let input = buildResponsesInput(request);
2291
- let previousResponseId = pickString(isRecord2(request.body) ? request.body.previous_response_id : undefined);
2292
- let aggregatedUsage;
2293
- let finishReason;
2294
- let lastPayload;
2295
- const executedToolCalls = [];
2296
- const toolExecutions = [];
2297
- const reasoningBlocks = [];
2230
+ const state = createResponsesMCPState(request);
2298
2231
  callbacks.onStart?.();
2299
2232
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
2300
2233
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
2301
2234
  const transportTools = toResponsesTools(toProviderFunctionTools(mcpToolset));
2302
- const response = await fetcher(buildURL(options.baseURL, path), {
2303
- method: "POST",
2304
- headers: buildHeaders(options),
2305
- body: JSON.stringify(cleanUndefined({
2306
- ...options.defaultBody,
2307
- ...request.body,
2308
- model: options.model,
2309
- input,
2310
- previous_response_id: previousResponseId,
2311
- temperature: request.temperature,
2312
- reasoning_effort: toOpenAIReasoningEffort(request.reasoningEffort),
2313
- max_output_tokens: request.maxTokens,
2314
- tools: transportTools,
2315
- tool_choice: request.toolChoice,
2316
- parallel_tool_calls: request.parallelToolCalls,
2317
- stream: true
2318
- })),
2319
- signal: request.signal
2320
- });
2235
+ const response = await sendOpenAIRequest(options, fetcher, path, request, buildResponsesBody(options, request, {
2236
+ input: state.input,
2237
+ previous_response_id: state.previousResponseId,
2238
+ tools: transportTools,
2239
+ tool_choice: request.toolChoice,
2240
+ parallel_tool_calls: request.parallelToolCalls,
2241
+ stream: true
2242
+ }));
2321
2243
  if (!response.ok) {
2322
2244
  const message = await response.text();
2323
2245
  throw new Error(`HTTP ${response.status}: ${message}`);
@@ -2339,7 +2261,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2339
2261
  const payload = pickResponsesStreamPayload(json);
2340
2262
  if (payload) {
2341
2263
  roundPayload = payload;
2342
- lastPayload = payload;
2264
+ state.lastPayload = payload;
2343
2265
  }
2344
2266
  const delta = pickResponsesStreamTextDelta(json);
2345
2267
  const reasoningDelta = pickResponsesStreamReasoningDelta(json);
@@ -2357,24 +2279,14 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2357
2279
  if (reasoningDelta) {
2358
2280
  roundReasoning += reasoningDelta;
2359
2281
  }
2360
- if (delta || reasoningDelta || chunkUsage || chunkFinishReason) {
2361
- const chunk = {
2362
- textDelta: delta,
2363
- reasoningDelta: reasoningDelta || undefined,
2364
- turnIndex: round,
2365
- raw: json,
2366
- usage: chunkUsage,
2367
- finishReason: chunkFinishReason
2368
- };
2369
- callbacks.onChunk?.(chunk);
2370
- }
2282
+ emitOpenAIStreamChunk(callbacks, round, json, delta, reasoningDelta, chunkUsage, chunkFinishReason);
2371
2283
  });
2372
2284
  const resolvedRoundUsage = preferLatestUsage(roundUsage, roundPayload ? pickUsage(roundPayload) : undefined);
2373
- aggregatedUsage = mergeUsage(aggregatedUsage, resolvedRoundUsage);
2285
+ state.aggregatedUsage = mergeUsage(state.aggregatedUsage, resolvedRoundUsage);
2374
2286
  if (roundFinishReason) {
2375
- finishReason = roundFinishReason;
2287
+ state.finishReason = roundFinishReason;
2376
2288
  } else if (roundPayload) {
2377
- finishReason = pickResponsesFinishReason(roundPayload) ?? finishReason;
2289
+ state.finishReason = pickResponsesFinishReason(roundPayload) ?? state.finishReason;
2378
2290
  }
2379
2291
  const payloadToolCalls = roundPayload ? pickResponsesToolCalls(roundPayload) : [];
2380
2292
  if (roundPayload && roundReasoning.length === 0) {
@@ -2383,7 +2295,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2383
2295
  const streamedCalls = buildResponsesStreamToolCalls(streamedToolCalls);
2384
2296
  const providerToolCalls = payloadToolCalls.length > 0 ? payloadToolCalls : streamedCalls;
2385
2297
  const functionCalls = providerToolCalls.filter((toolCall) => toolCall.type === "function" && typeof toolCall.id === "string" && typeof toolCall.name === "string");
2386
- pushReasoningBlock(reasoningBlocks, round, roundReasoning);
2298
+ pushReasoningBlock(state.reasoningBlocks, round, roundReasoning);
2387
2299
  request.onTurnTransition?.({
2388
2300
  turnIndex: round,
2389
2301
  kind: "reasoningComplete",
@@ -2391,16 +2303,7 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2391
2303
  });
2392
2304
  if (functionCalls.length === 0) {
2393
2305
  const finalText = roundText.length > 0 ? roundText : roundPayload ? pickResponsesText(roundPayload) || pickAssistantText(roundPayload) : "";
2394
- const out2 = {
2395
- text: finalText,
2396
- reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
2397
- reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
2398
- raw: roundPayload ?? lastPayload,
2399
- usage: aggregatedUsage,
2400
- finishReason,
2401
- toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
2402
- toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
2403
- };
2306
+ const out2 = buildResponsesMCPResult(state, finalText, roundPayload ?? state.lastPayload);
2404
2307
  request.onTurnTransition?.({ turnIndex: round, kind: "streamEnd" });
2405
2308
  callbacks.onComplete?.(out2);
2406
2309
  return out2;
@@ -2425,26 +2328,17 @@ async function streamWithResponsesAPIWithMCP(options, fetcher, path, request, ca
2425
2328
  provider: "openai-compatible",
2426
2329
  model: options.model
2427
2330
  });
2428
- executedToolCalls.push(...outputs.map((entry) => entry.call));
2429
- toolExecutions.push(...outputs.map((entry) => entry.execution));
2331
+ state.executedToolCalls.push(...outputs.map((entry) => entry.call));
2332
+ state.toolExecutions.push(...outputs.map((entry) => entry.execution));
2430
2333
  request.onTurnTransition?.({ turnIndex: round, kind: "toolResultsReceived" });
2431
- input = outputs.map((entry) => ({
2334
+ state.input = outputs.map((entry) => ({
2432
2335
  type: "function_call_output",
2433
2336
  call_id: entry.call.id,
2434
2337
  output: stringifyToolOutput(entry.call.error ? { error: entry.call.error } : entry.call.output)
2435
2338
  }));
2436
- previousResponseId = pickString(roundPayload?.id);
2339
+ state.previousResponseId = pickString(roundPayload?.id);
2437
2340
  }
2438
- const out = {
2439
- text: pickResponsesText(lastPayload ?? {}) || pickAssistantText(lastPayload ?? {}),
2440
- reasoning: joinReasoningBlocks(reasoningBlocks) || undefined,
2441
- reasoningBlocks: reasoningBlocks.length > 0 ? reasoningBlocks : undefined,
2442
- raw: lastPayload,
2443
- usage: aggregatedUsage,
2444
- finishReason,
2445
- toolCalls: executedToolCalls.length > 0 ? executedToolCalls : undefined,
2446
- toolExecutions: toolExecutions.length > 0 ? toolExecutions : undefined
2447
- };
2341
+ const out = buildResponsesMCPResult(state, pickResponsesText(state.lastPayload ?? {}) || pickAssistantText(state.lastPayload ?? {}), state.lastPayload);
2448
2342
  request.onTurnTransition?.({ turnIndex: maxToolRounds + 1, kind: "streamEnd" });
2449
2343
  callbacks.onComplete?.(out);
2450
2344
  return out;
@@ -2597,7 +2491,7 @@ function pickAssistantDelta(payload) {
2597
2491
  if (!isRecord2(delta)) {
2598
2492
  return "";
2599
2493
  }
2600
- return pickTextFromOpenAIContent(delta.content);
2494
+ return pickTextLike(delta.content);
2601
2495
  }
2602
2496
  function pickAssistantReasoning(payload) {
2603
2497
  const message = pickAssistantMessage(payload);
@@ -2905,7 +2799,7 @@ function pickResponsesReasoning(payload) {
2905
2799
  function pickAssistantText(payload) {
2906
2800
  const message = pickAssistantMessage(payload);
2907
2801
  if (message) {
2908
- const text = pickTextFromOpenAIContent(message.content);
2802
+ const text = pickTextLike(message.content);
2909
2803
  if (text.length > 0) {
2910
2804
  return text;
2911
2805
  }
@@ -2934,9 +2828,6 @@ function joinReasoningBlocks(blocks) {
2934
2828
 
2935
2829
  `);
2936
2830
  }
2937
- function pickTextFromOpenAIContent(value) {
2938
- return pickTextLike(value);
2939
- }
2940
2831
  function pickTextLike(value) {
2941
2832
  if (typeof value === "string") {
2942
2833
  return value;
@@ -3010,83 +2901,81 @@ function createAnthropicCompatibleAdapter(options) {
3010
2901
  if (hasMCPClients(request.mcpClients)) {
3011
2902
  return streamWithMCPToolLoop(options, fetcher, path, request, callbacks);
3012
2903
  }
3013
- const input = resolveAnthropicInput(request);
3014
- const response = await fetcher(buildURL(options.baseURL, path), {
3015
- method: "POST",
3016
- headers: buildHeaders2(options),
3017
- body: JSON.stringify(buildAnthropicRequestBody(options, request, {
3018
- ...options.defaultBody,
3019
- ...request.body,
3020
- model: options.model,
3021
- system: input.systemPrompt,
3022
- messages: input.messages,
3023
- temperature: request.temperature,
3024
- stream: true
3025
- })),
3026
- signal: request.signal
3027
- });
3028
- if (!response.ok) {
3029
- const message = await response.text();
3030
- throw new Error(`HTTP ${response.status}: ${message}`);
3031
- }
3032
- callbacks.onStart?.();
3033
- let text = "";
3034
- let usage;
3035
- let finishReason;
3036
- await consumeSSE(response, (data) => {
3037
- if (data === "[DONE]") {
3038
- return;
3039
- }
3040
- const json = safeJSONParse(data);
3041
- if (!isRecord2(json)) {
3042
- return;
3043
- }
3044
- const delta = pickAnthropicDelta(json);
3045
- const chunkUsage = pickUsage2(json);
3046
- const chunkFinishReason = pickFinishReason2(json);
3047
- usage = preferLatestUsage(usage, chunkUsage);
3048
- if (chunkFinishReason) {
3049
- finishReason = chunkFinishReason;
3050
- }
3051
- if (delta) {
3052
- text += delta;
3053
- callbacks.onToken?.(delta);
3054
- }
3055
- if (delta || chunkUsage || chunkFinishReason) {
3056
- const chunk = {
3057
- textDelta: delta,
3058
- raw: json,
3059
- usage: chunkUsage,
3060
- finishReason: chunkFinishReason
3061
- };
3062
- callbacks.onChunk?.(chunk);
3063
- }
3064
- });
3065
- const out = { text, usage, finishReason };
3066
- callbacks.onComplete?.(out);
3067
- return out;
2904
+ return streamPassThrough(options, fetcher, path, request, callbacks);
3068
2905
  },
3069
2906
  async embed() {
3070
2907
  throw new Error("Anthropic does not provide a native embedding API. " + "Use the openai-compatible provider with Voyage AI (https://api.voyageai.com) — " + "Anthropic's recommended embedding solution, which uses the same request format.");
3071
2908
  }
3072
2909
  };
3073
2910
  }
3074
- async function completePassThrough(options, fetcher, path, request) {
2911
+ async function streamPassThrough(options, fetcher, path, request, callbacks) {
3075
2912
  const input = resolveAnthropicInput(request);
3076
- const response = await fetcher(buildURL(options.baseURL, path), {
2913
+ const response = await sendAnthropicMessage(options, fetcher, path, request, {
2914
+ system: input.systemPrompt,
2915
+ messages: input.messages,
2916
+ stream: true
2917
+ });
2918
+ if (!response.ok) {
2919
+ const message = await response.text();
2920
+ throw new Error(`HTTP ${response.status}: ${message}`);
2921
+ }
2922
+ callbacks.onStart?.();
2923
+ let text = "";
2924
+ let usage;
2925
+ let finishReason;
2926
+ await consumeSSE(response, (data) => {
2927
+ if (data === "[DONE]") {
2928
+ return;
2929
+ }
2930
+ const json = safeJSONParse(data);
2931
+ if (!isRecord2(json)) {
2932
+ return;
2933
+ }
2934
+ const delta = pickAnthropicDelta(json);
2935
+ const chunkUsage = pickUsage2(json);
2936
+ const chunkFinishReason = pickFinishReason2(json);
2937
+ usage = preferLatestUsage(usage, chunkUsage);
2938
+ if (chunkFinishReason) {
2939
+ finishReason = chunkFinishReason;
2940
+ }
2941
+ if (delta) {
2942
+ text += delta;
2943
+ callbacks.onToken?.(delta);
2944
+ }
2945
+ if (delta || chunkUsage || chunkFinishReason) {
2946
+ callbacks.onChunk?.({
2947
+ textDelta: delta,
2948
+ raw: json,
2949
+ usage: chunkUsage,
2950
+ finishReason: chunkFinishReason
2951
+ });
2952
+ }
2953
+ });
2954
+ const out = { text, usage, finishReason };
2955
+ callbacks.onComplete?.(out);
2956
+ return out;
2957
+ }
2958
+ function sendAnthropicMessage(options, fetcher, path, request, body) {
2959
+ return fetcher(buildURL(options.baseURL, path), {
3077
2960
  method: "POST",
3078
2961
  headers: buildHeaders2(options),
3079
2962
  body: JSON.stringify(buildAnthropicRequestBody(options, request, {
3080
2963
  ...options.defaultBody,
3081
2964
  ...request.body,
3082
2965
  model: options.model,
3083
- system: input.systemPrompt,
3084
- messages: input.messages,
3085
2966
  temperature: request.temperature,
3086
- stream: false
2967
+ ...body
3087
2968
  })),
3088
2969
  signal: request.signal
3089
2970
  });
2971
+ }
2972
+ async function completePassThrough(options, fetcher, path, request) {
2973
+ const input = resolveAnthropicInput(request);
2974
+ const response = await sendAnthropicMessage(options, fetcher, path, request, {
2975
+ system: input.systemPrompt,
2976
+ messages: input.messages,
2977
+ stream: false
2978
+ });
3090
2979
  if (!response.ok) {
3091
2980
  const message = await response.text();
3092
2981
  throw new Error(`HTTP ${response.status}: ${message}`);
@@ -3118,21 +3007,12 @@ async function completeWithMCPToolLoop(options, fetcher, path, request) {
3118
3007
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
3119
3008
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
3120
3009
  const tools = toAnthropicTools(toProviderFunctionTools(mcpToolset));
3121
- const response = await fetcher(buildURL(options.baseURL, path), {
3122
- method: "POST",
3123
- headers: buildHeaders2(options),
3124
- body: JSON.stringify(buildAnthropicRequestBody(options, request, {
3125
- ...options.defaultBody,
3126
- ...request.body,
3127
- model: options.model,
3128
- system: input.systemPrompt,
3129
- messages,
3130
- temperature: request.temperature,
3131
- tools,
3132
- tool_choice: toAnthropicToolChoice(request.toolChoice),
3133
- stream: false
3134
- })),
3135
- signal: request.signal
3010
+ const response = await sendAnthropicMessage(options, fetcher, path, request, {
3011
+ system: input.systemPrompt,
3012
+ messages,
3013
+ tools,
3014
+ tool_choice: toAnthropicToolChoice(request.toolChoice),
3015
+ stream: false
3136
3016
  });
3137
3017
  if (!response.ok) {
3138
3018
  const message = await response.text();
@@ -3208,21 +3088,12 @@ async function streamWithMCPToolLoop(options, fetcher, path, request, callbacks)
3208
3088
  for (let round = 1;round <= maxToolRounds + 1; round += 1) {
3209
3089
  const mcpToolset = await resolveMCPToolset(request.mcpClients);
3210
3090
  const tools = toAnthropicTools(toProviderFunctionTools(mcpToolset));
3211
- const response = await fetcher(buildURL(options.baseURL, path), {
3212
- method: "POST",
3213
- headers: buildHeaders2(options),
3214
- body: JSON.stringify(buildAnthropicRequestBody(options, request, {
3215
- ...options.defaultBody,
3216
- ...request.body,
3217
- model: options.model,
3218
- system: input.systemPrompt,
3219
- messages,
3220
- temperature: request.temperature,
3221
- tools,
3222
- tool_choice: toAnthropicToolChoice(request.toolChoice),
3223
- stream: true
3224
- })),
3225
- signal: request.signal
3091
+ const response = await sendAnthropicMessage(options, fetcher, path, request, {
3092
+ system: input.systemPrompt,
3093
+ messages,
3094
+ tools,
3095
+ tool_choice: toAnthropicToolChoice(request.toolChoice),
3096
+ stream: true
3226
3097
  });
3227
3098
  if (!response.ok) {
3228
3099
  const message = await response.text();
@@ -3766,39 +3637,10 @@ function buildProviderOptions(config) {
3766
3637
  return {
3767
3638
  model: config.model,
3768
3639
  ...transport,
3769
- ...config.options ?? {}
3640
+ ...config.options
3770
3641
  };
3771
3642
  }
3772
3643
 
3773
- // src/utils/debug-colors.ts
3774
- var ANSI = {
3775
- reset: "\x1B[0m",
3776
- bold: "\x1B[1m",
3777
- cyan: "\x1B[36m",
3778
- yellow: "\x1B[33m",
3779
- green: "\x1B[32m",
3780
- red: "\x1B[31m",
3781
- dim: "\x1B[2m"
3782
- };
3783
- function color(config, text, tone) {
3784
- if (!config.colors) {
3785
- return text;
3786
- }
3787
- return `${ANSI[tone]}${text}${ANSI.reset}`;
3788
- }
3789
- function dim(config, text) {
3790
- if (!config.colors) {
3791
- return text;
3792
- }
3793
- return `${ANSI.dim}${text}${ANSI.reset}`;
3794
- }
3795
- function title(config, text) {
3796
- if (!config.colors) {
3797
- return text;
3798
- }
3799
- return `${ANSI.bold}${text}${ANSI.reset}`;
3800
- }
3801
-
3802
3644
  // src/outdent.ts
3803
3645
  var DEFAULT_OPTIONS = {
3804
3646
  trimLeadingNewline: true,
@@ -3936,132 +3778,213 @@ function createOutdent(options = {}) {
3936
3778
  return outdent;
3937
3779
  }
3938
3780
 
3939
- // src/generate-shared.ts
3940
- var sharedOutdent = createOutdent({
3941
- trimLeadingNewline: true,
3942
- trimTrailingNewline: true,
3943
- newline: `
3944
- `
3945
- });
3781
+ // src/generate-tool-timeout.ts
3782
+ function withToolTimeout(client, toolTimeoutMs) {
3783
+ return {
3784
+ id: client.id,
3785
+ listTools: client.listTools.bind(client),
3786
+ close: client.close?.bind(client),
3787
+ async callTool(params) {
3788
+ let timeoutId;
3789
+ const timeoutPromise = new Promise((_, reject) => {
3790
+ timeoutId = setTimeout(() => reject(new Error(`Tool call timed out after ${toolTimeoutMs}ms`)), toolTimeoutMs);
3791
+ });
3792
+ try {
3793
+ return await Promise.race([client.callTool(params), timeoutPromise]);
3794
+ } finally {
3795
+ clearTimeout(timeoutId);
3796
+ }
3797
+ }
3798
+ };
3799
+ }
3800
+ function applyToolTimeout(clients, toolTimeoutMs) {
3801
+ return clients.map((client) => withToolTimeout(client, toolTimeoutMs));
3802
+ }
3803
+ // src/generate-output.ts
3946
3804
  var RE_THINK_TAGS = /<\/?think\s*>/gi;
3947
- function resolvePrompt(prompt, context) {
3948
- const resolved = typeof prompt === "function" ? prompt(context) : prompt;
3949
- return normalizePromptValue(resolved, context);
3805
+ function normalizeModelOutput(text, dedicatedReasoning, reasoningBlocks) {
3806
+ const sanitized = sanitizeThink(text);
3807
+ const visibleText = stripThinkBlocks(text, sanitized.thinkBlocks);
3808
+ const reasoning = joinReasoningSegments([
3809
+ dedicatedReasoning,
3810
+ ...sanitized.thinkBlocks.map((block) => block.content)
3811
+ ]);
3812
+ return {
3813
+ text: visibleText,
3814
+ reasoning,
3815
+ reasoningBlocks: normalizeReasoningBlocks(reasoningBlocks),
3816
+ thinkBlocks: sanitized.thinkBlocks,
3817
+ parseSource: composeParseSource(visibleText, reasoning)
3818
+ };
3950
3819
  }
3951
- function normalizePromptValue(value, _context) {
3952
- if (typeof value === "string") {
3953
- return {
3954
- prompt: value
3955
- };
3956
- }
3957
- if (isPromptResolver(value)) {
3958
- return normalizePromptPayload(value.resolvePrompt(_context));
3820
+ function normalizeReasoningBlocks(blocks) {
3821
+ if (!Array.isArray(blocks)) {
3822
+ return;
3959
3823
  }
3960
- return normalizePromptPayload(value);
3824
+ const normalized = blocks.map((block) => ({
3825
+ turnIndex: block.turnIndex,
3826
+ text: block.text.replace(RE_THINK_TAGS, "").trim()
3827
+ })).filter((block) => Number.isFinite(block.turnIndex) && block.text.length > 0);
3828
+ return normalized.length > 0 ? normalized : undefined;
3961
3829
  }
3962
- function normalizePromptPayload(value) {
3963
- const prompt = typeof value.prompt === "string" ? value.prompt : undefined;
3964
- const messages = Array.isArray(value.messages) ? value.messages.filter(isLLMMessage) : undefined;
3965
- if ((!prompt || prompt.trim().length === 0) && (!messages || messages.length === 0)) {
3966
- throw new Error("Structured prompt payload must include a non-empty prompt or messages.");
3830
+ function appendReasoningBlock(blocks, transition) {
3831
+ const text = transition.reasoningText?.replace(RE_THINK_TAGS, "").trim();
3832
+ if (!text) {
3833
+ return blocks;
3967
3834
  }
3968
- return {
3969
- prompt,
3970
- systemPrompt: typeof value.systemPrompt === "string" ? value.systemPrompt : undefined,
3971
- messages: messages && messages.length > 0 ? messages.map((message) => ({ ...message })) : undefined
3972
- };
3835
+ const next = [...blocks ?? [], { turnIndex: transition.turnIndex, text }];
3836
+ return normalizeReasoningBlocks(next);
3973
3837
  }
3974
- function applyPromptOutdent(payload, enabled) {
3975
- if (!enabled) {
3976
- return payload;
3838
+ function composeParseSource(text, reasoning) {
3839
+ if (typeof reasoning !== "string" || reasoning.length === 0) {
3840
+ return text;
3977
3841
  }
3978
- return {
3979
- prompt: typeof payload.prompt === "string" ? sharedOutdent.string(payload.prompt) : undefined,
3980
- systemPrompt: applyOutdentToOptionalPrompt(payload.systemPrompt, enabled),
3981
- messages: payload.messages?.map((message) => ({
3982
- ...message,
3983
- content: typeof message.content === "string" ? sharedOutdent.string(message.content) : message.content
3984
- }))
3985
- };
3842
+ const sanitized = reasoning.replace(RE_THINK_TAGS, "");
3843
+ if (sanitized.length === 0) {
3844
+ return text;
3845
+ }
3846
+ return `<think>${sanitized}</think>${text}`;
3986
3847
  }
3987
- function applyOutdentToOptionalPrompt(value, enabled) {
3988
- if (!enabled || typeof value !== "string") {
3989
- return value;
3848
+ function aggregateUsage(attempts) {
3849
+ let usage;
3850
+ for (const attempt of attempts) {
3851
+ usage = mergeUsage2(usage, attempt.usage);
3990
3852
  }
3991
- return sharedOutdent.string(value);
3853
+ return usage;
3992
3854
  }
3993
- function mergeSystemPrompts(primary, secondary) {
3994
- const prompts = [primary, secondary].map((value) => value?.trim()).filter((value) => Boolean(value));
3995
- if (prompts.length === 0) {
3855
+ function mergeUsage2(base, next) {
3856
+ if (!base && !next) {
3996
3857
  return;
3997
3858
  }
3998
- return prompts.join(`
3859
+ return {
3860
+ inputTokens: (base?.inputTokens ?? 0) + (next?.inputTokens ?? 0),
3861
+ outputTokens: (base?.outputTokens ?? 0) + (next?.outputTokens ?? 0),
3862
+ totalTokens: (base?.totalTokens ?? 0) + (next?.totalTokens ?? 0),
3863
+ cost: (base?.cost ?? 0) + (next?.cost ?? 0)
3864
+ };
3865
+ }
3866
+ function joinReasoningSegments(parts) {
3867
+ return parts.map((value) => value?.trim()).filter((value) => Boolean(value)).join(`
3999
3868
 
4000
3869
  `);
4001
3870
  }
4002
- function normalizeStreamConfig(option) {
4003
- if (typeof option === "boolean") {
4004
- return {
4005
- enabled: option
4006
- };
3871
+ function stripThinkBlocks(text, thinkBlocks) {
3872
+ if (thinkBlocks.length === 0) {
3873
+ return text;
4007
3874
  }
4008
- if (!option) {
4009
- return {
4010
- enabled: false
4011
- };
3875
+ let output = "";
3876
+ let cursor = 0;
3877
+ for (const block of thinkBlocks) {
3878
+ output += text.slice(cursor, block.start);
3879
+ cursor = block.end;
4012
3880
  }
4013
- return {
4014
- enabled: option.enabled ?? true,
4015
- onData: option.onData,
4016
- onTurnTransition: option.onTurnTransition,
4017
- to: option.to
4018
- };
3881
+ output += text.slice(cursor);
3882
+ return output;
4019
3883
  }
4020
- function normalizeDebugConfig(option) {
4021
- if (typeof option === "boolean") {
4022
- return {
4023
- enabled: option,
4024
- colors: true,
4025
- verbose: false,
4026
- logger: (line) => console.log(line)
4027
- };
3884
+ function toStreamDataFingerprint(value) {
3885
+ try {
3886
+ return JSON.stringify(value);
3887
+ } catch {
3888
+ return "__unserializable__";
4028
3889
  }
4029
- if (!option) {
4030
- return {
4031
- enabled: false,
4032
- colors: true,
4033
- verbose: false,
4034
- logger: (line) => console.log(line)
4035
- };
3890
+ }
3891
+
3892
+ // src/utils/debug-colors.ts
3893
+ var ANSI = {
3894
+ reset: "\x1B[0m",
3895
+ bold: "\x1B[1m",
3896
+ cyan: "\x1B[36m",
3897
+ yellow: "\x1B[33m",
3898
+ green: "\x1B[32m",
3899
+ red: "\x1B[31m",
3900
+ dim: "\x1B[2m"
3901
+ };
3902
+ function color(config, text, tone) {
3903
+ if (!config.colors) {
3904
+ return text;
4036
3905
  }
4037
- return {
4038
- enabled: option.enabled ?? true,
4039
- colors: option.colors ?? true,
4040
- verbose: option.verbose ?? false,
4041
- logger: option.logger ?? ((line) => console.log(line))
4042
- };
3906
+ return `${ANSI[tone]}${text}${ANSI.reset}`;
4043
3907
  }
4044
- function withToolTimeout(client, toolTimeoutMs) {
4045
- return {
4046
- id: client.id,
4047
- listTools: client.listTools.bind(client),
4048
- close: client.close?.bind(client),
4049
- async callTool(params) {
4050
- let timeoutId;
4051
- const timeoutPromise = new Promise((_, reject) => {
4052
- timeoutId = setTimeout(() => reject(new Error(`Tool call timed out after ${toolTimeoutMs}ms`)), toolTimeoutMs);
4053
- });
4054
- try {
4055
- return await Promise.race([client.callTool(params), timeoutPromise]);
4056
- } finally {
4057
- clearTimeout(timeoutId);
4058
- }
4059
- }
4060
- };
3908
+ function dim(config, text) {
3909
+ if (!config.colors) {
3910
+ return text;
3911
+ }
3912
+ return `${ANSI.dim}${text}${ANSI.reset}`;
4061
3913
  }
4062
- function applyToolTimeout(clients, toolTimeoutMs) {
4063
- return clients.map((client) => withToolTimeout(client, toolTimeoutMs));
3914
+ function title(config, text) {
3915
+ if (!config.colors) {
3916
+ return text;
3917
+ }
3918
+ return `${ANSI.bold}${text}${ANSI.reset}`;
4064
3919
  }
3920
+
3921
+ // src/generate-debug.ts
3922
+ function emitDebugRequest(config, input) {
3923
+ const requestBody = input.requestPayload.body !== undefined ? JSON.stringify(input.requestPayload.body, null, 2) : "(none)";
3924
+ const requestMessages = input.requestPayload.messages !== undefined ? JSON.stringify(input.requestPayload.messages, null, 2) : "(none)";
3925
+ const lines = [
3926
+ color(config, title(config, [
3927
+ `[${input.label}][request]`,
3928
+ `attempt=${input.attempt}`,
3929
+ `selfHealEnabled=${input.selfHealEnabled}`,
3930
+ `selfHealAttempt=${input.selfHealAttempt}`
3931
+ ].join(" ")), "cyan"),
3932
+ dim(config, [
3933
+ `provider=${input.provider ?? "unknown"}`,
3934
+ `model=${input.model ?? "unknown"}`,
3935
+ `stream=${input.stream}`
3936
+ ].join(" ")),
3937
+ color(config, "prompt:", "yellow"),
3938
+ input.requestPayload.prompt ?? "(none)",
3939
+ color(config, "messages:", "yellow"),
3940
+ requestMessages,
3941
+ color(config, "systemPrompt:", "yellow"),
3942
+ input.requestPayload.systemPrompt ?? "(none)",
3943
+ color(config, "request.body:", "yellow"),
3944
+ requestBody
3945
+ ];
3946
+ emitDebug(config, lines.join(`
3947
+ `));
3948
+ }
3949
+ function emitDebugResponse(config, input) {
3950
+ const text = input.text.length > 0 ? input.text : "(none)";
3951
+ const reasoning = input.reasoning.length > 0 ? input.reasoning : "(none)";
3952
+ const metadata = [
3953
+ `via=${input.via}`,
3954
+ `textChars=${input.text.length}`,
3955
+ `reasoningChars=${input.reasoning.length}`
3956
+ ];
3957
+ if (config.verbose) {
3958
+ metadata.push(`parseSourceChars=${input.parseSource.length}`);
3959
+ }
3960
+ metadata.push(`finishReason=${input.finishReason ?? "unknown"}`, `usage=${JSON.stringify(input.usage ?? {})}`);
3961
+ const lines = [
3962
+ color(config, title(config, [
3963
+ `[${input.label}][response]`,
3964
+ `attempt=${input.attempt}`,
3965
+ `selfHealEnabled=${input.selfHealEnabled}`,
3966
+ `selfHealAttempt=${input.selfHealAttempt}`
3967
+ ].join(" ")), "green"),
3968
+ dim(config, metadata.join(" ")),
3969
+ color(config, "text:", "yellow"),
3970
+ text,
3971
+ color(config, "reasoning:", "yellow"),
3972
+ reasoning
3973
+ ];
3974
+ if (config.verbose) {
3975
+ lines.push(color(config, "parseSource:", "yellow"), input.parseSource);
3976
+ }
3977
+ emitDebug(config, lines.join(`
3978
+ `));
3979
+ }
3980
+ function emitDebug(config, message) {
3981
+ if (!config.enabled) {
3982
+ return;
3983
+ }
3984
+ config.logger(message);
3985
+ }
3986
+
3987
+ // src/generate-model-call.ts
4065
3988
  async function callModel(adapter, options) {
4066
3989
  const requestSignal = options.request?.signal ?? (options.timeout?.request !== undefined ? AbortSignal.timeout(options.timeout.request) : undefined);
4067
3990
  const requestPayload = {
@@ -4281,67 +4204,115 @@ async function callModel(adapter, options) {
4281
4204
  reasoningBlocks: normalized.reasoningBlocks
4282
4205
  };
4283
4206
  }
4284
- function normalizeModelOutput(text, dedicatedReasoning, reasoningBlocks) {
4285
- const sanitized = sanitizeThink(text);
4286
- const visibleText = stripThinkBlocks(text, sanitized.thinkBlocks);
4287
- const reasoning = joinReasoningSegments([
4288
- dedicatedReasoning,
4289
- ...sanitized.thinkBlocks.map((block) => block.content)
4290
- ]);
4207
+
4208
+ // src/generate-shared.ts
4209
+ var sharedOutdent = createOutdent({
4210
+ trimLeadingNewline: true,
4211
+ trimTrailingNewline: true,
4212
+ newline: `
4213
+ `
4214
+ });
4215
+ function resolvePrompt(prompt, context) {
4216
+ const resolved = typeof prompt === "function" ? prompt(context) : prompt;
4217
+ return normalizePromptValue(resolved, context);
4218
+ }
4219
+ function normalizePromptValue(value, _context) {
4220
+ if (typeof value === "string") {
4221
+ return {
4222
+ prompt: value
4223
+ };
4224
+ }
4225
+ if (isPromptResolver(value)) {
4226
+ return normalizePromptPayload(value.resolvePrompt(_context));
4227
+ }
4228
+ return normalizePromptPayload(value);
4229
+ }
4230
+ function normalizePromptPayload(value) {
4231
+ const prompt = typeof value.prompt === "string" ? value.prompt : undefined;
4232
+ const messages = Array.isArray(value.messages) ? value.messages.filter(isLLMMessage) : undefined;
4233
+ if ((!prompt || prompt.trim().length === 0) && (!messages || messages.length === 0)) {
4234
+ throw new Error("Structured prompt payload must include a non-empty prompt or messages.");
4235
+ }
4291
4236
  return {
4292
- text: visibleText,
4293
- reasoning,
4294
- reasoningBlocks: normalizeReasoningBlocks(reasoningBlocks),
4295
- thinkBlocks: sanitized.thinkBlocks,
4296
- parseSource: composeParseSource(visibleText, reasoning)
4237
+ prompt,
4238
+ systemPrompt: typeof value.systemPrompt === "string" ? value.systemPrompt : undefined,
4239
+ messages: messages && messages.length > 0 ? messages.map((message) => ({ ...message })) : undefined
4297
4240
  };
4298
4241
  }
4299
- function normalizeReasoningBlocks(blocks) {
4300
- if (!Array.isArray(blocks)) {
4301
- return;
4242
+ function applyPromptOutdent(payload, enabled) {
4243
+ if (!enabled) {
4244
+ return payload;
4302
4245
  }
4303
- const normalized = blocks.map((block) => ({
4304
- turnIndex: block.turnIndex,
4305
- text: block.text.replace(RE_THINK_TAGS, "").trim()
4306
- })).filter((block) => Number.isFinite(block.turnIndex) && block.text.length > 0);
4307
- return normalized.length > 0 ? normalized : undefined;
4246
+ return {
4247
+ prompt: typeof payload.prompt === "string" ? sharedOutdent.string(payload.prompt) : undefined,
4248
+ systemPrompt: applyOutdentToOptionalPrompt(payload.systemPrompt, enabled),
4249
+ messages: payload.messages?.map((message) => ({
4250
+ ...message,
4251
+ content: typeof message.content === "string" ? sharedOutdent.string(message.content) : message.content
4252
+ }))
4253
+ };
4308
4254
  }
4309
- function appendReasoningBlock(blocks, transition) {
4310
- const text = transition.reasoningText?.replace(RE_THINK_TAGS, "").trim();
4311
- if (!text) {
4312
- return blocks;
4255
+ function applyOutdentToOptionalPrompt(value, enabled) {
4256
+ if (!enabled || typeof value !== "string") {
4257
+ return value;
4313
4258
  }
4314
- const next = [...blocks ?? [], { turnIndex: transition.turnIndex, text }];
4315
- return normalizeReasoningBlocks(next);
4259
+ return sharedOutdent.string(value);
4316
4260
  }
4317
- function composeParseSource(text, reasoning) {
4318
- if (typeof reasoning !== "string" || reasoning.length === 0) {
4319
- return text;
4320
- }
4321
- const sanitized = reasoning.replace(RE_THINK_TAGS, "");
4322
- if (sanitized.length === 0) {
4323
- return text;
4261
+ function mergeSystemPrompts(primary, secondary) {
4262
+ const prompts = [primary, secondary].map((value) => value?.trim()).filter((value) => Boolean(value));
4263
+ if (prompts.length === 0) {
4264
+ return;
4324
4265
  }
4325
- return `<think>${sanitized}</think>${text}`;
4266
+ return prompts.join(`
4267
+
4268
+ `);
4326
4269
  }
4327
- function aggregateUsage(attempts) {
4328
- let usage;
4329
- for (const attempt of attempts) {
4330
- usage = mergeUsage2(usage, attempt.usage);
4270
+ function normalizeStreamConfig(option) {
4271
+ if (typeof option === "boolean") {
4272
+ return {
4273
+ enabled: option
4274
+ };
4275
+ }
4276
+ if (!option) {
4277
+ return {
4278
+ enabled: false
4279
+ };
4331
4280
  }
4332
- return usage;
4281
+ return {
4282
+ enabled: option.enabled ?? true,
4283
+ onData: option.onData,
4284
+ onTurnTransition: option.onTurnTransition,
4285
+ to: option.to
4286
+ };
4333
4287
  }
4334
- function mergeUsage2(base, next) {
4335
- if (!base && !next) {
4336
- return;
4288
+ function normalizeDebugConfig(option) {
4289
+ if (typeof option === "boolean") {
4290
+ return {
4291
+ enabled: option,
4292
+ colors: true,
4293
+ verbose: false,
4294
+ logger: defaultDebugLogger
4295
+ };
4296
+ }
4297
+ if (!option) {
4298
+ return {
4299
+ enabled: false,
4300
+ colors: true,
4301
+ verbose: false,
4302
+ logger: defaultDebugLogger
4303
+ };
4337
4304
  }
4338
4305
  return {
4339
- inputTokens: (base?.inputTokens ?? 0) + (next?.inputTokens ?? 0),
4340
- outputTokens: (base?.outputTokens ?? 0) + (next?.outputTokens ?? 0),
4341
- totalTokens: (base?.totalTokens ?? 0) + (next?.totalTokens ?? 0),
4342
- cost: (base?.cost ?? 0) + (next?.cost ?? 0)
4306
+ enabled: option.enabled ?? true,
4307
+ colors: option.colors ?? true,
4308
+ verbose: option.verbose ?? false,
4309
+ logger: option.logger ?? defaultDebugLogger
4343
4310
  };
4344
4311
  }
4312
+ function defaultDebugLogger(line) {
4313
+ const { log } = globalThis.console;
4314
+ log(line);
4315
+ }
4345
4316
  function isPromptResolver(value) {
4346
4317
  return typeof value === "object" && value !== null && "resolvePrompt" in value && typeof value.resolvePrompt === "function";
4347
4318
  }
@@ -4355,95 +4326,6 @@ function isLLMMessage(value) {
4355
4326
  }
4356
4327
  return "content" in candidate;
4357
4328
  }
4358
- function joinReasoningSegments(parts) {
4359
- return parts.map((value) => value?.trim()).filter((value) => Boolean(value)).join(`
4360
-
4361
- `);
4362
- }
4363
- function stripThinkBlocks(text, thinkBlocks) {
4364
- if (thinkBlocks.length === 0) {
4365
- return text;
4366
- }
4367
- let output = "";
4368
- let cursor = 0;
4369
- for (const block of thinkBlocks) {
4370
- output += text.slice(cursor, block.start);
4371
- cursor = block.end;
4372
- }
4373
- output += text.slice(cursor);
4374
- return output;
4375
- }
4376
- function toStreamDataFingerprint(value) {
4377
- try {
4378
- return JSON.stringify(value);
4379
- } catch {
4380
- return "__unserializable__";
4381
- }
4382
- }
4383
- function emitDebugRequest(config, input) {
4384
- const requestBody = input.requestPayload.body !== undefined ? JSON.stringify(input.requestPayload.body, null, 2) : "(none)";
4385
- const requestMessages = input.requestPayload.messages !== undefined ? JSON.stringify(input.requestPayload.messages, null, 2) : "(none)";
4386
- const lines = [
4387
- color(config, title(config, [
4388
- `[${input.label}][request]`,
4389
- `attempt=${input.attempt}`,
4390
- `selfHealEnabled=${input.selfHealEnabled}`,
4391
- `selfHealAttempt=${input.selfHealAttempt}`
4392
- ].join(" ")), "cyan"),
4393
- dim(config, [
4394
- `provider=${input.provider ?? "unknown"}`,
4395
- `model=${input.model ?? "unknown"}`,
4396
- `stream=${input.stream}`
4397
- ].join(" ")),
4398
- color(config, "prompt:", "yellow"),
4399
- input.requestPayload.prompt ?? "(none)",
4400
- color(config, "messages:", "yellow"),
4401
- requestMessages,
4402
- color(config, "systemPrompt:", "yellow"),
4403
- input.requestPayload.systemPrompt ?? "(none)",
4404
- color(config, "request.body:", "yellow"),
4405
- requestBody
4406
- ];
4407
- emitDebug(config, lines.join(`
4408
- `));
4409
- }
4410
- function emitDebugResponse(config, input) {
4411
- const text = input.text.length > 0 ? input.text : "(none)";
4412
- const reasoning = input.reasoning.length > 0 ? input.reasoning : "(none)";
4413
- const metadata = [
4414
- `via=${input.via}`,
4415
- `textChars=${input.text.length}`,
4416
- `reasoningChars=${input.reasoning.length}`
4417
- ];
4418
- if (config.verbose) {
4419
- metadata.push(`parseSourceChars=${input.parseSource.length}`);
4420
- }
4421
- metadata.push(`finishReason=${input.finishReason ?? "unknown"}`, `usage=${JSON.stringify(input.usage ?? {})}`);
4422
- const lines = [
4423
- color(config, title(config, [
4424
- `[${input.label}][response]`,
4425
- `attempt=${input.attempt}`,
4426
- `selfHealEnabled=${input.selfHealEnabled}`,
4427
- `selfHealAttempt=${input.selfHealAttempt}`
4428
- ].join(" ")), "green"),
4429
- dim(config, metadata.join(" ")),
4430
- color(config, "text:", "yellow"),
4431
- text,
4432
- color(config, "reasoning:", "yellow"),
4433
- reasoning
4434
- ];
4435
- if (config.verbose) {
4436
- lines.push(color(config, "parseSource:", "yellow"), input.parseSource);
4437
- }
4438
- emitDebug(config, lines.join(`
4439
- `));
4440
- }
4441
- function emitDebug(config, message) {
4442
- if (!config.enabled) {
4443
- return;
4444
- }
4445
- config.logger(message);
4446
- }
4447
4329
 
4448
4330
  // src/generate.ts
4449
4331
  async function generate(adapter, promptOrOptions, callOptions) {
@@ -4519,7 +4401,7 @@ function normalizeGenerateInput(promptOrOptions, callOptions) {
4519
4401
  throw new Error("Missing prompt in generate(adapter, prompt, options?) call.");
4520
4402
  }
4521
4403
  return {
4522
- ...callOptions ?? {},
4404
+ ...callOptions,
4523
4405
  prompt: promptOrOptions
4524
4406
  };
4525
4407
  }
@@ -4545,9 +4427,6 @@ function prepareGeneratePromptPayload(payload, systemPrompt) {
4545
4427
  };
4546
4428
  }
4547
4429
 
4548
- // src/structured.ts
4549
- var import_jsonrepair3 = require("jsonrepair");
4550
-
4551
4430
  // src/parse.ts
4552
4431
  var import_jsonrepair2 = require("jsonrepair");
4553
4432
  function parseLLMOutput(output, schema, options = {}) {
@@ -4847,6 +4726,80 @@ function formatZodIssues(issues) {
4847
4726
  `);
4848
4727
  }
4849
4728
 
4729
+ // src/structured-streaming.ts
4730
+ var import_jsonrepair3 = require("jsonrepair");
4731
+ function parseStreamingStructuredData(parseSource) {
4732
+ const sanitized = sanitizeThink(parseSource);
4733
+ const start = findFirstJsonRootStart(sanitized.visibleText);
4734
+ if (start < 0) {
4735
+ return null;
4736
+ }
4737
+ const candidate = sanitized.visibleText.slice(start).trim();
4738
+ if (!candidate) {
4739
+ return null;
4740
+ }
4741
+ try {
4742
+ const repaired = import_jsonrepair3.jsonrepair(candidate);
4743
+ const parsed = JSON.parse(repaired);
4744
+ if (typeof parsed !== "object" || parsed === null) {
4745
+ return null;
4746
+ }
4747
+ return parsed;
4748
+ } catch {
4749
+ return null;
4750
+ }
4751
+ }
4752
+ function findFirstJsonRootStart(input) {
4753
+ const unquotedRootStart = findFirstUnquotedJsonRootStart(input);
4754
+ if (unquotedRootStart >= 0) {
4755
+ return unquotedRootStart;
4756
+ }
4757
+ return findFirstRawJsonRootStart(input);
4758
+ }
4759
+ function findFirstUnquotedJsonRootStart(input) {
4760
+ let inString = false;
4761
+ let escaped = false;
4762
+ for (let index = 0;index < input.length; index += 1) {
4763
+ const char = input[index];
4764
+ if (!char) {
4765
+ continue;
4766
+ }
4767
+ if (inString) {
4768
+ if (escaped) {
4769
+ escaped = false;
4770
+ continue;
4771
+ }
4772
+ if (char === "\\") {
4773
+ escaped = true;
4774
+ continue;
4775
+ }
4776
+ if (char === '"') {
4777
+ inString = false;
4778
+ }
4779
+ continue;
4780
+ }
4781
+ if (char === '"') {
4782
+ inString = true;
4783
+ continue;
4784
+ }
4785
+ if (char === "{" || char === "[") {
4786
+ return index;
4787
+ }
4788
+ }
4789
+ return -1;
4790
+ }
4791
+ function findFirstRawJsonRootStart(input) {
4792
+ const objectStart = input.indexOf("{");
4793
+ const arrayStart = input.indexOf("[");
4794
+ if (objectStart < 0) {
4795
+ return arrayStart;
4796
+ }
4797
+ if (arrayStart < 0) {
4798
+ return objectStart;
4799
+ }
4800
+ return Math.min(objectStart, arrayStart);
4801
+ }
4802
+
4850
4803
  // src/structured.ts
4851
4804
  class StructuredParseError extends Error {
4852
4805
  name = "StructuredParseError";
@@ -5084,7 +5037,7 @@ function normalizeStructuredInput(schemaOrOptions, promptInput, callOptions) {
5084
5037
  throw new Error("Missing prompt in structured(adapter, schema, prompt, options?) call.");
5085
5038
  }
5086
5039
  return {
5087
- ...callOptions ?? {},
5040
+ ...callOptions,
5088
5041
  schema: schemaOrOptions,
5089
5042
  prompt: promptInput
5090
5043
  };
@@ -5355,67 +5308,6 @@ async function callModel2(adapter, options) {
5355
5308
  debugLabel: "structured"
5356
5309
  });
5357
5310
  }
5358
- function parseStreamingStructuredData(parseSource) {
5359
- const sanitized = sanitizeThink(parseSource);
5360
- const start = findFirstJsonRootStart(sanitized.visibleText);
5361
- if (start < 0) {
5362
- return null;
5363
- }
5364
- const candidate = sanitized.visibleText.slice(start).trim();
5365
- if (!candidate) {
5366
- return null;
5367
- }
5368
- try {
5369
- const repaired = import_jsonrepair3.jsonrepair(candidate);
5370
- const parsed = JSON.parse(repaired);
5371
- if (typeof parsed !== "object" || parsed === null) {
5372
- return null;
5373
- }
5374
- return parsed;
5375
- } catch {
5376
- return null;
5377
- }
5378
- }
5379
- function findFirstJsonRootStart(input) {
5380
- let inString = false;
5381
- let escaped = false;
5382
- for (let index = 0;index < input.length; index += 1) {
5383
- const char = input[index];
5384
- if (!char) {
5385
- continue;
5386
- }
5387
- if (inString) {
5388
- if (escaped) {
5389
- escaped = false;
5390
- continue;
5391
- }
5392
- if (char === "\\") {
5393
- escaped = true;
5394
- continue;
5395
- }
5396
- if (char === '"') {
5397
- inString = false;
5398
- }
5399
- continue;
5400
- }
5401
- if (char === '"') {
5402
- inString = true;
5403
- continue;
5404
- }
5405
- if (char === "{" || char === "[") {
5406
- return index;
5407
- }
5408
- }
5409
- const objectStart = input.indexOf("{");
5410
- const arrayStart = input.indexOf("[");
5411
- if (objectStart < 0) {
5412
- return arrayStart;
5413
- }
5414
- if (arrayStart < 0) {
5415
- return objectStart;
5416
- }
5417
- return Math.min(objectStart, arrayStart);
5418
- }
5419
5311
  function parseWithObserve(output, schema, parseOptions, context) {
5420
5312
  const userParseTrace = parseOptions.onTrace;
5421
5313
  return parseLLMOutput(output, schema, {
@@ -5518,12 +5410,12 @@ function mergeStructuredOptions(defaults, overrides) {
5518
5410
  ...defaults,
5519
5411
  ...overrides,
5520
5412
  parse: {
5521
- ...defaults?.parse ?? {},
5522
- ...overrides?.parse ?? {}
5413
+ ...defaults?.parse,
5414
+ ...overrides?.parse
5523
5415
  },
5524
5416
  request: {
5525
- ...defaults?.request ?? {},
5526
- ...overrides?.request ?? {}
5417
+ ...defaults?.request,
5418
+ ...overrides?.request
5527
5419
  },
5528
5420
  stream: mergeObjectLike(defaults?.stream, overrides?.stream),
5529
5421
  selfHeal: mergeObjectLike(defaults?.selfHeal, overrides?.selfHeal),
@@ -5539,8 +5431,8 @@ function mergeGenerateOptions(defaults, overrides) {
5539
5431
  outdent: overrides?.outdent ?? defaults?.outdent,
5540
5432
  systemPrompt: overrides?.systemPrompt ?? defaults?.systemPrompt,
5541
5433
  request: {
5542
- ...defaults?.request ?? {},
5543
- ...overrides?.request ?? {}
5434
+ ...defaults?.request,
5435
+ ...overrides?.request
5544
5436
  },
5545
5437
  stream: mergeObjectLike(defaults?.stream, overrides?.stream),
5546
5438
  debug: mergeObjectLike(defaults?.debug, overrides?.debug),
@@ -5733,7 +5625,7 @@ function toPromptString(value) {
5733
5625
  return String(value);
5734
5626
  }
5735
5627
  }
5736
- var dedent = createOutdent({
5628
+ var outdent = createOutdent({
5737
5629
  trimLeadingNewline: true,
5738
5630
  trimTrailingNewline: true,
5739
5631
  newline: `
@@ -5766,7 +5658,7 @@ function stripOuterBlankLines(text) {
5766
5658
  `);
5767
5659
  }
5768
5660
  function renderPromptTemplate(strings, values) {
5769
- return stripOuterBlankLines(dedent(strings, ...values.map(toPromptString)));
5661
+ return stripOuterBlankLines(outdent(strings, ...values.map(toPromptString)));
5770
5662
  }
5771
5663
  function isTemplateStringsArray(value) {
5772
5664
  return Array.isArray(value) && "raw" in value;
@@ -5814,6 +5706,9 @@ class PromptMessageBuilderImpl {
5814
5706
  function createPromptMessageBuilder() {
5815
5707
  return new PromptMessageBuilderImpl;
5816
5708
  }
5709
+ function dedent(strings, ...values) {
5710
+ return renderPromptTemplate(strings, values);
5711
+ }
5817
5712
  function prompt(input, ...values) {
5818
5713
  if (isTemplateStringsArray(input)) {
5819
5714
  return renderPromptTemplate(input, values);