u-foo 2.5.7 → 2.5.8
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/package.json +1 -1
- package/src/code/nativeRunner.js +466 -509
package/package.json
CHANGED
package/src/code/nativeRunner.js
CHANGED
|
@@ -437,34 +437,22 @@ function emitPhase(callback, event = {}) {
|
|
|
437
437
|
}
|
|
438
438
|
}
|
|
439
439
|
|
|
440
|
-
|
|
440
|
+
// Shared SSE transport skeleton: POST the payload, then read the stream as
|
|
441
|
+
// SSE blocks, dispatch each non-[DONE] block to onEvent, and stop after the
|
|
442
|
+
// batch that carried [DONE]. Timeout/cancel translation and request cleanup
|
|
443
|
+
// live here so each protocol turn only declares its event handling.
|
|
444
|
+
async function runSseRequest({
|
|
441
445
|
url = "",
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
messages = [],
|
|
445
|
-
onTextDelta = null,
|
|
446
|
-
onThinkingDelta = null,
|
|
447
|
-
onPhase = null,
|
|
446
|
+
headers = {},
|
|
447
|
+
payload = {},
|
|
448
448
|
signal = null,
|
|
449
449
|
timeoutMs = 300000,
|
|
450
|
+
onPhase = null,
|
|
451
|
+
onNonStream,
|
|
452
|
+
onEvent,
|
|
453
|
+
onTail = null,
|
|
454
|
+
buildResult,
|
|
450
455
|
} = {}) {
|
|
451
|
-
const payload = {
|
|
452
|
-
model,
|
|
453
|
-
max_tokens: resolveMaxTokens(DEFAULT_OPENAI_MAX_TOKENS),
|
|
454
|
-
messages,
|
|
455
|
-
tools: buildCoreToolSpecs(),
|
|
456
|
-
tool_choice: "auto",
|
|
457
|
-
stream: true,
|
|
458
|
-
temperature: 0,
|
|
459
|
-
};
|
|
460
|
-
|
|
461
|
-
const headers = {
|
|
462
|
-
"content-type": "application/json",
|
|
463
|
-
};
|
|
464
|
-
if (apiKey) {
|
|
465
|
-
headers.authorization = `Bearer ${apiKey}`;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
456
|
const request = createRequestController({ signal, timeoutMs });
|
|
469
457
|
|
|
470
458
|
emitPhase(onPhase, { type: "request_start" });
|
|
@@ -484,28 +472,12 @@ async function runOpenAiLikeTurn({
|
|
|
484
472
|
|
|
485
473
|
if (!response.body || typeof response.body.getReader !== "function") {
|
|
486
474
|
const data = await response.json();
|
|
487
|
-
|
|
488
|
-
? data.choices[0].message
|
|
489
|
-
: {};
|
|
490
|
-
const text = typeof message.content === "string" ? message.content : "";
|
|
491
|
-
const toolCalls = Array.isArray(message.tool_calls) ? message.tool_calls : [];
|
|
492
|
-
if (text && typeof onTextDelta === "function") {
|
|
493
|
-
onTextDelta(text);
|
|
494
|
-
}
|
|
495
|
-
return {
|
|
496
|
-
text,
|
|
497
|
-
toolCalls,
|
|
498
|
-
};
|
|
475
|
+
return onNonStream(data);
|
|
499
476
|
}
|
|
500
477
|
|
|
501
478
|
const reader = response.body.getReader();
|
|
502
479
|
const decoder = new TextDecoder();
|
|
503
|
-
const toolCallMap = new Map();
|
|
504
480
|
let rawBuffer = "";
|
|
505
|
-
let responseText = "";
|
|
506
|
-
const announcedToolNames = new Set();
|
|
507
|
-
let nextSyntheticIndex = 0;
|
|
508
|
-
let lastSyntheticIndex = -1;
|
|
509
481
|
let sawDone = false;
|
|
510
482
|
|
|
511
483
|
while (true) {
|
|
@@ -517,96 +489,178 @@ async function runOpenAiLikeTurn({
|
|
|
517
489
|
rawBuffer = parsed.rest;
|
|
518
490
|
|
|
519
491
|
for (const block of parsed.blocks) {
|
|
520
|
-
const
|
|
521
|
-
if (!
|
|
522
|
-
if (
|
|
523
|
-
// Stop reading after this batch
|
|
524
|
-
//
|
|
525
|
-
//
|
|
492
|
+
const { event, data } = parseSseEventBlock(block);
|
|
493
|
+
if (!data) continue;
|
|
494
|
+
if (data === "[DONE]") {
|
|
495
|
+
// Stop reading after this batch instead of waiting for the server
|
|
496
|
+
// to close the connection, but keep the buffered tail and finish
|
|
497
|
+
// the blocks already parsed alongside [DONE] instead of silently
|
|
498
|
+
// dropping them.
|
|
526
499
|
sawDone = true;
|
|
527
500
|
continue;
|
|
528
501
|
}
|
|
529
502
|
|
|
530
|
-
|
|
531
|
-
|
|
503
|
+
onEvent({ event, data });
|
|
504
|
+
}
|
|
532
505
|
|
|
533
|
-
|
|
534
|
-
|
|
506
|
+
if (sawDone) break;
|
|
507
|
+
}
|
|
535
508
|
|
|
536
|
-
|
|
509
|
+
if (typeof onTail === "function") {
|
|
510
|
+
onTail(rawBuffer);
|
|
511
|
+
}
|
|
537
512
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
513
|
+
return buildResult();
|
|
514
|
+
} catch (err) {
|
|
515
|
+
if (request.timedOut()) {
|
|
516
|
+
const timeoutError = new Error(`CLI timeout (${normalizeTimeoutMs(timeoutMs)}ms)`);
|
|
517
|
+
timeoutError.code = "timeout";
|
|
518
|
+
throw timeoutError;
|
|
519
|
+
}
|
|
520
|
+
if (signal && typeof signal === "object" && signal.aborted) {
|
|
521
|
+
const cancelError = new Error("CLI cancelled");
|
|
522
|
+
cancelError.code = "cancelled";
|
|
523
|
+
throw cancelError;
|
|
524
|
+
}
|
|
525
|
+
throw err;
|
|
526
|
+
} finally {
|
|
527
|
+
request.cleanup();
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
async function runOpenAiLikeTurn({
|
|
532
|
+
url = "",
|
|
533
|
+
apiKey = "",
|
|
534
|
+
model = "",
|
|
535
|
+
messages = [],
|
|
536
|
+
onTextDelta = null,
|
|
537
|
+
onThinkingDelta = null,
|
|
538
|
+
onPhase = null,
|
|
539
|
+
signal = null,
|
|
540
|
+
timeoutMs = 300000,
|
|
541
|
+
} = {}) {
|
|
542
|
+
const payload = {
|
|
543
|
+
model,
|
|
544
|
+
max_tokens: resolveMaxTokens(DEFAULT_OPENAI_MAX_TOKENS),
|
|
545
|
+
messages,
|
|
546
|
+
tools: buildCoreToolSpecs(),
|
|
547
|
+
tool_choice: "auto",
|
|
548
|
+
stream: true,
|
|
549
|
+
temperature: 0,
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
const headers = {
|
|
553
|
+
"content-type": "application/json",
|
|
554
|
+
};
|
|
555
|
+
if (apiKey) {
|
|
556
|
+
headers.authorization = `Bearer ${apiKey}`;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const toolCallMap = new Map();
|
|
560
|
+
const announcedToolNames = new Set();
|
|
561
|
+
let responseText = "";
|
|
562
|
+
let nextSyntheticIndex = 0;
|
|
563
|
+
let lastSyntheticIndex = -1;
|
|
564
|
+
|
|
565
|
+
return runSseRequest({
|
|
566
|
+
url,
|
|
567
|
+
headers,
|
|
568
|
+
payload,
|
|
569
|
+
signal,
|
|
570
|
+
timeoutMs,
|
|
571
|
+
onPhase,
|
|
572
|
+
onNonStream: (data) => {
|
|
573
|
+
const message = data && data.choices && data.choices[0] && data.choices[0].message
|
|
574
|
+
? data.choices[0].message
|
|
575
|
+
: {};
|
|
576
|
+
const text = typeof message.content === "string" ? message.content : "";
|
|
577
|
+
const toolCalls = Array.isArray(message.tool_calls) ? message.tool_calls : [];
|
|
578
|
+
if (text && typeof onTextDelta === "function") {
|
|
579
|
+
onTextDelta(text);
|
|
580
|
+
}
|
|
581
|
+
return {
|
|
582
|
+
text,
|
|
583
|
+
toolCalls,
|
|
584
|
+
};
|
|
585
|
+
},
|
|
586
|
+
onEvent: ({ data }) => {
|
|
587
|
+
const chunk = parseJsonSafe(data, null);
|
|
588
|
+
if (!chunk || typeof chunk !== "object") return;
|
|
589
|
+
|
|
590
|
+
const choice = chunk.choices && chunk.choices[0] ? chunk.choices[0] : null;
|
|
591
|
+
if (!choice || typeof choice !== "object") return;
|
|
592
|
+
|
|
593
|
+
const delta = choice.delta && typeof choice.delta === "object" ? choice.delta : {};
|
|
594
|
+
|
|
595
|
+
const reasoningChunk = typeof delta.reasoning_content === "string"
|
|
596
|
+
? delta.reasoning_content
|
|
597
|
+
: (typeof delta.reasoning === "string" ? delta.reasoning : "");
|
|
598
|
+
if (reasoningChunk) {
|
|
599
|
+
emitPhase(onPhase, { type: "thinking_delta", text: reasoningChunk });
|
|
600
|
+
if (typeof onThinkingDelta === "function") {
|
|
601
|
+
onThinkingDelta(reasoningChunk);
|
|
546
602
|
}
|
|
603
|
+
}
|
|
547
604
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
}
|
|
605
|
+
if (typeof delta.content === "string" && delta.content) {
|
|
606
|
+
responseText += delta.content;
|
|
607
|
+
emitPhase(onPhase, { type: "text_delta", text: delta.content });
|
|
608
|
+
if (typeof onTextDelta === "function") {
|
|
609
|
+
onTextDelta(delta.content);
|
|
554
610
|
}
|
|
611
|
+
}
|
|
555
612
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
613
|
+
if (Array.isArray(delta.tool_calls)) {
|
|
614
|
+
for (const callPart of delta.tool_calls) {
|
|
615
|
+
let index;
|
|
616
|
+
if (Number.isFinite(callPart.index)) {
|
|
617
|
+
index = callPart.index;
|
|
618
|
+
} else if (typeof callPart.id === "string" && callPart.id) {
|
|
619
|
+
// Provider omitted index: a chunk carrying an id starts a new
|
|
620
|
+
// call, so give it its own synthetic index instead of
|
|
621
|
+
// collapsing every call into slot 0.
|
|
622
|
+
while (toolCallMap.has(nextSyntheticIndex)) nextSyntheticIndex += 1;
|
|
623
|
+
index = nextSyntheticIndex;
|
|
624
|
+
nextSyntheticIndex += 1;
|
|
625
|
+
lastSyntheticIndex = index;
|
|
626
|
+
} else if (lastSyntheticIndex >= 0) {
|
|
627
|
+
// No index and no id: continuation of the latest synthetic call.
|
|
628
|
+
index = lastSyntheticIndex;
|
|
629
|
+
} else {
|
|
630
|
+
index = 0;
|
|
631
|
+
}
|
|
632
|
+
const previous = toolCallMap.get(index) || {
|
|
633
|
+
id: "",
|
|
634
|
+
type: "function",
|
|
635
|
+
function: {
|
|
636
|
+
name: "",
|
|
637
|
+
arguments: "",
|
|
638
|
+
},
|
|
639
|
+
};
|
|
583
640
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
}
|
|
641
|
+
if (typeof callPart.id === "string" && callPart.id) previous.id = callPart.id;
|
|
642
|
+
if (callPart.function && typeof callPart.function === "object") {
|
|
643
|
+
if (typeof callPart.function.name === "string" && callPart.function.name) {
|
|
644
|
+
previous.function.name = callPart.function.name;
|
|
645
|
+
}
|
|
646
|
+
if (typeof callPart.function.arguments === "string" && callPart.function.arguments) {
|
|
647
|
+
previous.function.arguments += callPart.function.arguments;
|
|
592
648
|
}
|
|
649
|
+
}
|
|
593
650
|
|
|
594
|
-
|
|
651
|
+
toolCallMap.set(index, previous);
|
|
595
652
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
}
|
|
653
|
+
const toolName = previous.function.name;
|
|
654
|
+
const announceKey = `${index}:${toolName}`;
|
|
655
|
+
if (toolName && !announcedToolNames.has(announceKey)) {
|
|
656
|
+
announcedToolNames.add(announceKey);
|
|
657
|
+
emitPhase(onPhase, { type: "tool_request", name: toolName });
|
|
602
658
|
}
|
|
603
659
|
}
|
|
604
660
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
if (rawBuffer.trim()) {
|
|
661
|
+
},
|
|
662
|
+
onTail: (rawBuffer) => {
|
|
663
|
+
if (!rawBuffer.trim()) return;
|
|
610
664
|
const fallbackBlock = parseSseDataBlock(rawBuffer);
|
|
611
665
|
if (fallbackBlock && fallbackBlock !== "[DONE]") {
|
|
612
666
|
const chunk = parseJsonSafe(fallbackBlock, null);
|
|
@@ -618,29 +672,14 @@ async function runOpenAiLikeTurn({
|
|
|
618
672
|
}
|
|
619
673
|
}
|
|
620
674
|
}
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
return {
|
|
675
|
+
},
|
|
676
|
+
buildResult: () => ({
|
|
624
677
|
text: responseText,
|
|
625
678
|
toolCalls: Array.from(toolCallMap.entries())
|
|
626
679
|
.sort((a, b) => a[0] - b[0])
|
|
627
680
|
.map((entry) => entry[1]),
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
if (request.timedOut()) {
|
|
631
|
-
const timeoutError = new Error(`CLI timeout (${normalizeTimeoutMs(timeoutMs)}ms)`);
|
|
632
|
-
timeoutError.code = "timeout";
|
|
633
|
-
throw timeoutError;
|
|
634
|
-
}
|
|
635
|
-
if (signal && typeof signal === "object" && signal.aborted) {
|
|
636
|
-
const cancelError = new Error("CLI cancelled");
|
|
637
|
-
cancelError.code = "cancelled";
|
|
638
|
-
throw cancelError;
|
|
639
|
-
}
|
|
640
|
-
throw err;
|
|
641
|
-
} finally {
|
|
642
|
-
request.cleanup();
|
|
643
|
-
}
|
|
681
|
+
}),
|
|
682
|
+
});
|
|
644
683
|
}
|
|
645
684
|
|
|
646
685
|
function normalizeAnthropicMessageContent(raw = []) {
|
|
@@ -713,25 +752,19 @@ async function runAnthropicTurn({
|
|
|
713
752
|
headers["x-api-key"] = apiKey;
|
|
714
753
|
}
|
|
715
754
|
|
|
716
|
-
const
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
const body = await response.text().catch(() => "");
|
|
730
|
-
throw new Error(`provider request failed (${response.status}): ${clipText(body, 500)}`);
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
if (!response.body || typeof response.body.getReader !== "function") {
|
|
734
|
-
const data = await response.json();
|
|
755
|
+
const blockMap = new Map();
|
|
756
|
+
let responseText = "";
|
|
757
|
+
let nextSyntheticBlockIndex = 0;
|
|
758
|
+
let lastBlockIndex = -1;
|
|
759
|
+
|
|
760
|
+
return runSseRequest({
|
|
761
|
+
url,
|
|
762
|
+
headers,
|
|
763
|
+
payload,
|
|
764
|
+
signal,
|
|
765
|
+
timeoutMs,
|
|
766
|
+
onPhase,
|
|
767
|
+
onNonStream: (data) => {
|
|
735
768
|
const content = normalizeAnthropicMessageContent(data && data.content);
|
|
736
769
|
const text = content
|
|
737
770
|
.filter((item) => item.type === "text")
|
|
@@ -745,270 +778,181 @@ async function runAnthropicTurn({
|
|
|
745
778
|
assistantContent: content,
|
|
746
779
|
toolCalls: extractAnthropicToolCalls(content),
|
|
747
780
|
};
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
while (true) {
|
|
760
|
-
const { done, value } = await reader.read();
|
|
761
|
-
if (done) break;
|
|
762
|
-
|
|
763
|
-
rawBuffer += decoder.decode(value, { stream: true });
|
|
764
|
-
const parsed = parseSseBlocks(rawBuffer);
|
|
765
|
-
rawBuffer = parsed.rest;
|
|
766
|
-
|
|
767
|
-
for (const rawBlock of parsed.blocks) {
|
|
768
|
-
const { event, data } = parseSseEventBlock(rawBlock);
|
|
769
|
-
if (!data) continue;
|
|
770
|
-
if (data === "[DONE]") {
|
|
771
|
-
// Stop reading after this batch instead of waiting for the server
|
|
772
|
-
// to close the connection, mirroring the OpenAI transport.
|
|
773
|
-
sawDone = true;
|
|
774
|
-
continue;
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
const payloadChunk = parseJsonSafe(data, null);
|
|
778
|
-
if (!payloadChunk || typeof payloadChunk !== "object") continue;
|
|
781
|
+
},
|
|
782
|
+
onEvent: ({ event, data }) => {
|
|
783
|
+
const payloadChunk = parseJsonSafe(data, null);
|
|
784
|
+
if (!payloadChunk || typeof payloadChunk !== "object") return;
|
|
785
|
+
|
|
786
|
+
if (event === "error") {
|
|
787
|
+
const errMsg = payloadChunk.error && payloadChunk.error.message
|
|
788
|
+
? String(payloadChunk.error.message)
|
|
789
|
+
: "anthropic stream error";
|
|
790
|
+
throw new Error(errMsg);
|
|
791
|
+
}
|
|
779
792
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
793
|
+
if (event === "content_block_start") {
|
|
794
|
+
let index;
|
|
795
|
+
if (Number.isFinite(payloadChunk.index)) {
|
|
796
|
+
index = payloadChunk.index;
|
|
797
|
+
} else {
|
|
798
|
+
// Provider omitted index: each start opens a new block, so give
|
|
799
|
+
// it its own synthetic index instead of collapsing every block
|
|
800
|
+
// into slot 0.
|
|
801
|
+
while (blockMap.has(nextSyntheticBlockIndex)) nextSyntheticBlockIndex += 1;
|
|
802
|
+
index = nextSyntheticBlockIndex;
|
|
803
|
+
nextSyntheticBlockIndex += 1;
|
|
785
804
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
blockMap.set(index, {
|
|
818
|
-
order: index,
|
|
819
|
-
type: "tool_use",
|
|
820
|
-
id: String(contentBlock.id || ""),
|
|
821
|
-
name: String(contentBlock.name || ""),
|
|
822
|
-
input: contentBlock.input && typeof contentBlock.input === "object" && !Array.isArray(contentBlock.input)
|
|
823
|
-
? { ...contentBlock.input }
|
|
824
|
-
: {},
|
|
825
|
-
inputJson: "",
|
|
826
|
-
});
|
|
827
|
-
const toolName = String(contentBlock.name || "");
|
|
828
|
-
if (toolName) {
|
|
829
|
-
emitPhase(onPhase, { type: "tool_request", name: toolName });
|
|
830
|
-
}
|
|
805
|
+
lastBlockIndex = index;
|
|
806
|
+
const contentBlock = payloadChunk.content_block && typeof payloadChunk.content_block === "object"
|
|
807
|
+
? payloadChunk.content_block
|
|
808
|
+
: {};
|
|
809
|
+
|
|
810
|
+
if (contentBlock.type === "text") {
|
|
811
|
+
blockMap.set(index, {
|
|
812
|
+
order: index,
|
|
813
|
+
type: "text",
|
|
814
|
+
text: String(contentBlock.text || ""),
|
|
815
|
+
});
|
|
816
|
+
} else if (contentBlock.type === "thinking") {
|
|
817
|
+
blockMap.set(index, {
|
|
818
|
+
order: index,
|
|
819
|
+
type: "thinking",
|
|
820
|
+
text: String(contentBlock.thinking || ""),
|
|
821
|
+
});
|
|
822
|
+
} else if (contentBlock.type === "tool_use") {
|
|
823
|
+
blockMap.set(index, {
|
|
824
|
+
order: index,
|
|
825
|
+
type: "tool_use",
|
|
826
|
+
id: String(contentBlock.id || ""),
|
|
827
|
+
name: String(contentBlock.name || ""),
|
|
828
|
+
input: contentBlock.input && typeof contentBlock.input === "object" && !Array.isArray(contentBlock.input)
|
|
829
|
+
? { ...contentBlock.input }
|
|
830
|
+
: {},
|
|
831
|
+
inputJson: "",
|
|
832
|
+
});
|
|
833
|
+
const toolName = String(contentBlock.name || "");
|
|
834
|
+
if (toolName) {
|
|
835
|
+
emitPhase(onPhase, { type: "tool_request", name: toolName });
|
|
831
836
|
}
|
|
832
|
-
continue;
|
|
833
837
|
}
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
834
840
|
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
}
|
|
841
|
+
if (event === "content_block_delta") {
|
|
842
|
+
let index;
|
|
843
|
+
if (Number.isFinite(payloadChunk.index)) {
|
|
844
|
+
index = payloadChunk.index;
|
|
845
|
+
} else if (lastBlockIndex >= 0) {
|
|
846
|
+
// No index: continuation of the most recently started block.
|
|
847
|
+
index = lastBlockIndex;
|
|
848
|
+
} else {
|
|
849
|
+
index = 0;
|
|
850
|
+
}
|
|
851
|
+
const delta = payloadChunk.delta && typeof payloadChunk.delta === "object"
|
|
852
|
+
? payloadChunk.delta
|
|
853
|
+
: {};
|
|
854
|
+
const current = blockMap.get(index) || { order: index, type: "text", text: "" };
|
|
855
|
+
|
|
856
|
+
if (delta.type === "text_delta") {
|
|
857
|
+
const deltaText = String(delta.text || "");
|
|
858
|
+
current.type = "text";
|
|
859
|
+
current.text = `${String(current.text || "")}${deltaText}`;
|
|
860
|
+
blockMap.set(index, current);
|
|
861
|
+
if (deltaText) {
|
|
862
|
+
responseText += deltaText;
|
|
863
|
+
emitPhase(onPhase, { type: "text_delta", text: deltaText });
|
|
864
|
+
if (typeof onTextDelta === "function") {
|
|
865
|
+
onTextDelta(deltaText);
|
|
861
866
|
}
|
|
862
|
-
continue;
|
|
863
867
|
}
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
864
870
|
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
}
|
|
871
|
+
if (delta.type === "thinking_delta") {
|
|
872
|
+
const deltaText = String(delta.thinking || "");
|
|
873
|
+
current.type = "thinking";
|
|
874
|
+
current.text = `${String(current.text || "")}${deltaText}`;
|
|
875
|
+
blockMap.set(index, current);
|
|
876
|
+
if (deltaText) {
|
|
877
|
+
emitPhase(onPhase, { type: "thinking_delta", text: deltaText });
|
|
878
|
+
if (typeof onThinkingDelta === "function") {
|
|
879
|
+
onThinkingDelta(deltaText);
|
|
875
880
|
}
|
|
876
|
-
continue;
|
|
877
881
|
}
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
878
884
|
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
}
|
|
885
|
+
if (delta.type === "input_json_delta") {
|
|
886
|
+
current.type = "tool_use";
|
|
887
|
+
current.inputJson = `${String(current.inputJson || "")}${String(delta.partial_json || "")}`;
|
|
888
|
+
blockMap.set(index, current);
|
|
889
|
+
return;
|
|
885
890
|
}
|
|
886
891
|
}
|
|
892
|
+
},
|
|
893
|
+
buildResult: () => {
|
|
894
|
+
const assistantContent = Array.from(blockMap.values())
|
|
895
|
+
.sort((a, b) => a.order - b.order)
|
|
896
|
+
.filter((item) => item.type !== "thinking")
|
|
897
|
+
.map((item) => {
|
|
898
|
+
if (item.type === "text") {
|
|
899
|
+
return {
|
|
900
|
+
type: "text",
|
|
901
|
+
text: String(item.text || ""),
|
|
902
|
+
};
|
|
903
|
+
}
|
|
887
904
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
.filter((item) => item.type !== "thinking")
|
|
894
|
-
.map((item) => {
|
|
895
|
-
if (item.type === "text") {
|
|
905
|
+
const inputFromDelta = normalizeToolCallArgs(item.inputJson || "");
|
|
906
|
+
const mergedInput = {
|
|
907
|
+
...(item.input && typeof item.input === "object" ? item.input : {}),
|
|
908
|
+
...(inputFromDelta && typeof inputFromDelta === "object" ? inputFromDelta : {}),
|
|
909
|
+
};
|
|
896
910
|
return {
|
|
897
|
-
type: "
|
|
898
|
-
|
|
911
|
+
type: "tool_use",
|
|
912
|
+
id: String(item.id || `tool_${randomUUID()}`),
|
|
913
|
+
name: String(item.name || ""),
|
|
914
|
+
input: mergedInput,
|
|
899
915
|
};
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
const inputFromDelta = normalizeToolCallArgs(item.inputJson || "");
|
|
903
|
-
const mergedInput = {
|
|
904
|
-
...(item.input && typeof item.input === "object" ? item.input : {}),
|
|
905
|
-
...(inputFromDelta && typeof inputFromDelta === "object" ? inputFromDelta : {}),
|
|
906
|
-
};
|
|
907
|
-
return {
|
|
908
|
-
type: "tool_use",
|
|
909
|
-
id: String(item.id || `tool_${randomUUID()}`),
|
|
910
|
-
name: String(item.name || ""),
|
|
911
|
-
input: mergedInput,
|
|
912
|
-
};
|
|
913
|
-
});
|
|
916
|
+
});
|
|
914
917
|
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
918
|
+
if (!responseText) {
|
|
919
|
+
responseText = assistantContent
|
|
920
|
+
.filter((item) => item.type === "text")
|
|
921
|
+
.map((item) => item.text)
|
|
922
|
+
.join("");
|
|
923
|
+
}
|
|
921
924
|
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
const timeoutError = new Error(`CLI timeout (${normalizeTimeoutMs(timeoutMs)}ms)`);
|
|
930
|
-
timeoutError.code = "timeout";
|
|
931
|
-
throw timeoutError;
|
|
932
|
-
}
|
|
933
|
-
if (signal && typeof signal === "object" && signal.aborted) {
|
|
934
|
-
const cancelError = new Error("CLI cancelled");
|
|
935
|
-
cancelError.code = "cancelled";
|
|
936
|
-
throw cancelError;
|
|
937
|
-
}
|
|
938
|
-
throw err;
|
|
939
|
-
} finally {
|
|
940
|
-
request.cleanup();
|
|
941
|
-
}
|
|
925
|
+
return {
|
|
926
|
+
text: responseText,
|
|
927
|
+
assistantContent,
|
|
928
|
+
toolCalls: extractAnthropicToolCalls(assistantContent),
|
|
929
|
+
};
|
|
930
|
+
},
|
|
931
|
+
});
|
|
942
932
|
}
|
|
943
933
|
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
if (!requestUrl) {
|
|
967
|
-
throw new Error("ucode baseUrl is not configured");
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
const messages = cloneMessageList(historyMessages);
|
|
971
|
-
const systemText = String(systemPrompt || "").trim();
|
|
972
|
-
const hasSystem = messages.some((entry) => String(entry.role || "").trim() === "system");
|
|
973
|
-
if (systemText && !hasSystem) {
|
|
974
|
-
messages.unshift({ role: "system", content: systemText });
|
|
975
|
-
}
|
|
976
|
-
messages.push({ role: "user", content: String(prompt || "") });
|
|
977
|
-
|
|
978
|
-
let aggregated = "";
|
|
979
|
-
let streamed = false;
|
|
980
|
-
let toolCallsExecuted = 0;
|
|
981
|
-
let toolErrors = 0;
|
|
982
|
-
const toolBudget = resolveNativeToolBudget();
|
|
983
|
-
|
|
984
|
-
while (true) {
|
|
985
|
-
guards.ensureActive();
|
|
986
|
-
|
|
987
|
-
const turnResult = await runOpenAiLikeTurn({
|
|
988
|
-
url: requestUrl,
|
|
989
|
-
apiKey,
|
|
990
|
-
model: requestModel,
|
|
991
|
-
messages,
|
|
992
|
-
signal,
|
|
993
|
-
timeoutMs,
|
|
994
|
-
onPhase,
|
|
995
|
-
onThinkingDelta,
|
|
996
|
-
onTextDelta: (chunk) => {
|
|
997
|
-
const text = String(chunk || "");
|
|
998
|
-
if (!text) return;
|
|
999
|
-
aggregated += text;
|
|
1000
|
-
if (typeof onStreamDelta === "function") {
|
|
1001
|
-
streamed = true;
|
|
1002
|
-
onStreamDelta(text);
|
|
1003
|
-
}
|
|
1004
|
-
},
|
|
1005
|
-
});
|
|
1006
|
-
|
|
1007
|
-
const toolCalls = Array.isArray(turnResult.toolCalls)
|
|
1008
|
-
? turnResult.toolCalls.filter((call) => call && call.function && typeof call.function === "object")
|
|
1009
|
-
: [];
|
|
1010
|
-
|
|
1011
|
-
if (toolCalls.length === 0) {
|
|
934
|
+
// Transport descriptors: everything the shared native loop needs that differs
|
|
935
|
+
// between the OpenAI chat-completions and Anthropic messages protocols —
|
|
936
|
+
// request URL resolution, initial message shaping, turn execution, and
|
|
937
|
+
// assistant/tool-result message formatting.
|
|
938
|
+
const TRANSPORTS = {
|
|
939
|
+
"openai-chat": {
|
|
940
|
+
resolveUrl: resolveCompletionUrl,
|
|
941
|
+
prepareMessages({ messages, systemPrompt, prompt }) {
|
|
942
|
+
const systemText = String(systemPrompt || "").trim();
|
|
943
|
+
const hasSystem = messages.some((entry) => String(entry.role || "").trim() === "system");
|
|
944
|
+
if (systemText && !hasSystem) {
|
|
945
|
+
messages.unshift({ role: "system", content: systemText });
|
|
946
|
+
}
|
|
947
|
+
messages.push({ role: "user", content: String(prompt || "") });
|
|
948
|
+
},
|
|
949
|
+
runTurn: runOpenAiLikeTurn,
|
|
950
|
+
getToolCalls(turnResult) {
|
|
951
|
+
return Array.isArray(turnResult.toolCalls)
|
|
952
|
+
? turnResult.toolCalls.filter((call) => call && call.function && typeof call.function === "object")
|
|
953
|
+
: [];
|
|
954
|
+
},
|
|
955
|
+
appendFinalAssistantMessage({ messages, turnResult }) {
|
|
1012
956
|
const text = String(turnResult.text || "").trim();
|
|
1013
957
|
if (text) {
|
|
1014
958
|
messages.push({
|
|
@@ -1016,78 +960,114 @@ async function runNativeLoopOpenAi({
|
|
|
1016
960
|
content: text,
|
|
1017
961
|
});
|
|
1018
962
|
}
|
|
1019
|
-
|
|
1020
|
-
|
|
963
|
+
},
|
|
964
|
+
prepareToolCalls({ messages, toolCalls }) {
|
|
965
|
+
const assistantToolCalls = [];
|
|
966
|
+
for (const call of toolCalls) {
|
|
967
|
+
const callId = String(call.id || `call_${randomUUID()}`);
|
|
968
|
+
const name = normalizeToolName(call.function.name || "");
|
|
969
|
+
const args = normalizeToolCallArgs(call.function.arguments || "");
|
|
970
|
+
|
|
971
|
+
assistantToolCalls.push({
|
|
972
|
+
id: callId,
|
|
973
|
+
type: "function",
|
|
974
|
+
function: {
|
|
975
|
+
name: name || String(call.function.name || ""),
|
|
976
|
+
arguments: toJsonString(args),
|
|
977
|
+
},
|
|
978
|
+
});
|
|
1021
979
|
}
|
|
1022
|
-
return {
|
|
1023
|
-
text: aggregated,
|
|
1024
|
-
streamed,
|
|
1025
|
-
toolCallsExecuted,
|
|
1026
|
-
messages,
|
|
1027
|
-
};
|
|
1028
|
-
}
|
|
1029
980
|
|
|
1030
|
-
|
|
1031
|
-
for (const call of toolCalls) {
|
|
1032
|
-
const callId = String(call.id || `call_${randomUUID()}`);
|
|
1033
|
-
const name = normalizeToolName(call.function.name || "");
|
|
1034
|
-
const args = normalizeToolCallArgs(call.function.arguments || "");
|
|
1035
|
-
|
|
1036
|
-
assistantToolCalls.push({
|
|
1037
|
-
id: callId,
|
|
1038
|
-
type: "function",
|
|
1039
|
-
function: {
|
|
1040
|
-
name: name || String(call.function.name || ""),
|
|
1041
|
-
arguments: toJsonString(args),
|
|
1042
|
-
},
|
|
1043
|
-
});
|
|
1044
|
-
}
|
|
981
|
+
if (assistantToolCalls.length === 0) return null;
|
|
1045
982
|
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
messages,
|
|
1052
|
-
};
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
messages.push({
|
|
1056
|
-
role: "assistant",
|
|
1057
|
-
content: null,
|
|
1058
|
-
tool_calls: assistantToolCalls,
|
|
1059
|
-
});
|
|
983
|
+
messages.push({
|
|
984
|
+
role: "assistant",
|
|
985
|
+
content: null,
|
|
986
|
+
tool_calls: assistantToolCalls,
|
|
987
|
+
});
|
|
1060
988
|
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
tool: toolCall.function.name,
|
|
989
|
+
return assistantToolCalls.map((toolCall) => ({
|
|
990
|
+
name: toolCall.function.name,
|
|
1064
991
|
args: normalizeToolCallArgs(toolCall.function.arguments),
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
if (!toolResult || toolResult.ok === false) {
|
|
1070
|
-
toolErrors += 1;
|
|
1071
|
-
}
|
|
1072
|
-
enforceNativeToolBudget({
|
|
1073
|
-
toolCallsExecuted,
|
|
1074
|
-
toolErrors,
|
|
1075
|
-
maxToolCalls: toolBudget.maxToolCalls,
|
|
1076
|
-
maxToolErrors: toolBudget.maxToolErrors,
|
|
1077
|
-
lastTool: toolCall.function.name,
|
|
1078
|
-
lastError: toolResult && toolResult.error ? String(toolResult.error) : "",
|
|
1079
|
-
});
|
|
992
|
+
source: toolCall,
|
|
993
|
+
}));
|
|
994
|
+
},
|
|
995
|
+
appendToolResult({ messages, call, toolResult }) {
|
|
1080
996
|
messages.push({
|
|
1081
997
|
role: "tool",
|
|
1082
|
-
tool_call_id:
|
|
998
|
+
tool_call_id: call.source.id,
|
|
1083
999
|
content: clipText(toJsonString(toolResult), 12000),
|
|
1084
1000
|
});
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1001
|
+
},
|
|
1002
|
+
},
|
|
1003
|
+
"anthropic-messages": {
|
|
1004
|
+
resolveUrl: resolveAnthropicMessagesUrl,
|
|
1005
|
+
prepareMessages({ messages, prompt }) {
|
|
1006
|
+
messages.push({
|
|
1007
|
+
role: "user",
|
|
1008
|
+
content: String(prompt || ""),
|
|
1009
|
+
});
|
|
1010
|
+
},
|
|
1011
|
+
runTurn: runAnthropicTurn,
|
|
1012
|
+
getToolCalls(turnResult) {
|
|
1013
|
+
return Array.isArray(turnResult.toolCalls) ? turnResult.toolCalls : [];
|
|
1014
|
+
},
|
|
1015
|
+
appendFinalAssistantMessage({ messages, turnResult }) {
|
|
1016
|
+
const assistantContent = Array.isArray(turnResult.assistantContent)
|
|
1017
|
+
? turnResult.assistantContent
|
|
1018
|
+
: [];
|
|
1019
|
+
if (assistantContent.length > 0) {
|
|
1020
|
+
messages.push({
|
|
1021
|
+
role: "assistant",
|
|
1022
|
+
content: assistantContent,
|
|
1023
|
+
});
|
|
1024
|
+
} else if (String(turnResult.text || "").trim()) {
|
|
1025
|
+
messages.push({
|
|
1026
|
+
role: "assistant",
|
|
1027
|
+
content: [
|
|
1028
|
+
{
|
|
1029
|
+
type: "text",
|
|
1030
|
+
text: String(turnResult.text || ""),
|
|
1031
|
+
},
|
|
1032
|
+
],
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
},
|
|
1036
|
+
prepareToolCalls({ messages, turnResult, toolCalls }) {
|
|
1037
|
+
const assistantContent = Array.isArray(turnResult.assistantContent)
|
|
1038
|
+
? turnResult.assistantContent
|
|
1039
|
+
: [];
|
|
1087
1040
|
|
|
1088
|
-
|
|
1041
|
+
messages.push({
|
|
1042
|
+
role: "assistant",
|
|
1043
|
+
content: assistantContent,
|
|
1044
|
+
});
|
|
1089
1045
|
|
|
1090
|
-
|
|
1046
|
+
return toolCalls.map((call) => ({
|
|
1047
|
+
name: call.name,
|
|
1048
|
+
args: call.args,
|
|
1049
|
+
source: call,
|
|
1050
|
+
}));
|
|
1051
|
+
},
|
|
1052
|
+
appendToolResult({ collected, call, toolResult }) {
|
|
1053
|
+
collected.push({
|
|
1054
|
+
type: "tool_result",
|
|
1055
|
+
tool_use_id: String(call.source.id || ""),
|
|
1056
|
+
content: clipText(toJsonString(toolResult), 12000),
|
|
1057
|
+
is_error: Boolean(!toolResult || toolResult.ok === false),
|
|
1058
|
+
});
|
|
1059
|
+
},
|
|
1060
|
+
flushToolResults({ messages, collected }) {
|
|
1061
|
+
messages.push({
|
|
1062
|
+
role: "user",
|
|
1063
|
+
content: collected,
|
|
1064
|
+
});
|
|
1065
|
+
},
|
|
1066
|
+
},
|
|
1067
|
+
};
|
|
1068
|
+
|
|
1069
|
+
async function runNativeLoop({
|
|
1070
|
+
transport,
|
|
1091
1071
|
workspaceRoot = process.cwd(),
|
|
1092
1072
|
prompt = "",
|
|
1093
1073
|
systemPrompt = "",
|
|
@@ -1108,16 +1088,13 @@ async function runNativeLoopAnthropic({
|
|
|
1108
1088
|
throw new Error("ucode model is not configured");
|
|
1109
1089
|
}
|
|
1110
1090
|
|
|
1111
|
-
const requestUrl =
|
|
1091
|
+
const requestUrl = transport.resolveUrl(baseUrl);
|
|
1112
1092
|
if (!requestUrl) {
|
|
1113
1093
|
throw new Error("ucode baseUrl is not configured");
|
|
1114
1094
|
}
|
|
1115
1095
|
|
|
1116
1096
|
const messages = cloneMessageList(historyMessages);
|
|
1117
|
-
|
|
1118
|
-
role: "user",
|
|
1119
|
-
content: String(prompt || ""),
|
|
1120
|
-
});
|
|
1097
|
+
transport.prepareMessages({ messages, systemPrompt, prompt });
|
|
1121
1098
|
|
|
1122
1099
|
let aggregated = "";
|
|
1123
1100
|
let streamed = false;
|
|
@@ -1128,7 +1105,7 @@ async function runNativeLoopAnthropic({
|
|
|
1128
1105
|
while (true) {
|
|
1129
1106
|
guards.ensureActive();
|
|
1130
1107
|
|
|
1131
|
-
const turnResult = await
|
|
1108
|
+
const turnResult = await transport.runTurn({
|
|
1132
1109
|
url: requestUrl,
|
|
1133
1110
|
apiKey,
|
|
1134
1111
|
model: requestModel,
|
|
@@ -1149,28 +1126,10 @@ async function runNativeLoopAnthropic({
|
|
|
1149
1126
|
},
|
|
1150
1127
|
});
|
|
1151
1128
|
|
|
1152
|
-
const toolCalls =
|
|
1129
|
+
const toolCalls = transport.getToolCalls(turnResult);
|
|
1153
1130
|
|
|
1154
1131
|
if (toolCalls.length === 0) {
|
|
1155
|
-
|
|
1156
|
-
? turnResult.assistantContent
|
|
1157
|
-
: [];
|
|
1158
|
-
if (assistantContent.length > 0) {
|
|
1159
|
-
messages.push({
|
|
1160
|
-
role: "assistant",
|
|
1161
|
-
content: assistantContent,
|
|
1162
|
-
});
|
|
1163
|
-
} else if (String(turnResult.text || "").trim()) {
|
|
1164
|
-
messages.push({
|
|
1165
|
-
role: "assistant",
|
|
1166
|
-
content: [
|
|
1167
|
-
{
|
|
1168
|
-
type: "text",
|
|
1169
|
-
text: String(turnResult.text || ""),
|
|
1170
|
-
},
|
|
1171
|
-
],
|
|
1172
|
-
});
|
|
1173
|
-
}
|
|
1132
|
+
transport.appendFinalAssistantMessage({ messages, turnResult });
|
|
1174
1133
|
const text = String(turnResult.text || "").trim();
|
|
1175
1134
|
if (!aggregated.trim() && text) {
|
|
1176
1135
|
aggregated = text;
|
|
@@ -1183,20 +1142,21 @@ async function runNativeLoopAnthropic({
|
|
|
1183
1142
|
};
|
|
1184
1143
|
}
|
|
1185
1144
|
|
|
1186
|
-
const
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1145
|
+
const pendingCalls = transport.prepareToolCalls({ messages, turnResult, toolCalls });
|
|
1146
|
+
if (!pendingCalls) {
|
|
1147
|
+
return {
|
|
1148
|
+
text: aggregated,
|
|
1149
|
+
streamed,
|
|
1150
|
+
toolCallsExecuted,
|
|
1151
|
+
messages,
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1194
1154
|
|
|
1195
|
-
const
|
|
1196
|
-
for (const
|
|
1155
|
+
const collectedResults = [];
|
|
1156
|
+
for (const pending of pendingCalls) {
|
|
1197
1157
|
const toolResult = runCoreTool({
|
|
1198
|
-
tool:
|
|
1199
|
-
args:
|
|
1158
|
+
tool: pending.name,
|
|
1159
|
+
args: pending.args,
|
|
1200
1160
|
workspaceRoot,
|
|
1201
1161
|
onToolEvent,
|
|
1202
1162
|
});
|
|
@@ -1209,23 +1169,21 @@ async function runNativeLoopAnthropic({
|
|
|
1209
1169
|
toolErrors,
|
|
1210
1170
|
maxToolCalls: toolBudget.maxToolCalls,
|
|
1211
1171
|
maxToolErrors: toolBudget.maxToolErrors,
|
|
1212
|
-
lastTool:
|
|
1172
|
+
lastTool: pending.name,
|
|
1213
1173
|
lastError: toolResult && toolResult.error ? String(toolResult.error) : "",
|
|
1214
1174
|
});
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1175
|
+
transport.appendToolResult({
|
|
1176
|
+
messages,
|
|
1177
|
+
collected: collectedResults,
|
|
1178
|
+
call: pending,
|
|
1179
|
+
toolResult,
|
|
1220
1180
|
});
|
|
1221
1181
|
}
|
|
1222
1182
|
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
});
|
|
1183
|
+
if (typeof transport.flushToolResults === "function") {
|
|
1184
|
+
transport.flushToolResults({ messages, collected: collectedResults });
|
|
1185
|
+
}
|
|
1227
1186
|
}
|
|
1228
|
-
|
|
1229
1187
|
}
|
|
1230
1188
|
|
|
1231
1189
|
async function runNativeAgentTask({
|
|
@@ -1277,11 +1235,10 @@ async function runNativeAgentTask({
|
|
|
1277
1235
|
model,
|
|
1278
1236
|
});
|
|
1279
1237
|
|
|
1280
|
-
const
|
|
1281
|
-
? runNativeLoopAnthropic
|
|
1282
|
-
: runNativeLoopOpenAi;
|
|
1238
|
+
const transport = TRANSPORTS[runtime.transport] || TRANSPORTS["openai-chat"];
|
|
1283
1239
|
|
|
1284
|
-
const runResult = await
|
|
1240
|
+
const runResult = await runNativeLoop({
|
|
1241
|
+
transport,
|
|
1285
1242
|
workspaceRoot,
|
|
1286
1243
|
prompt: promptText,
|
|
1287
1244
|
systemPrompt,
|