u-foo 2.3.30 → 2.3.31
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/agent.js +4 -0
- package/src/code/nativeRunner.js +74 -0
- package/src/code/tui.js +30 -11
package/package.json
CHANGED
package/src/code/agent.js
CHANGED
|
@@ -491,6 +491,8 @@ async function runNaturalLanguageTask(task = "", state = {}, options = {}) {
|
|
|
491
491
|
const runNativeAgentImpl = typeof options.runNativeAgentImpl === "function"
|
|
492
492
|
? options.runNativeAgentImpl
|
|
493
493
|
: runNativeAgentTask;
|
|
494
|
+
const onPhase = typeof options.onPhase === "function" ? options.onPhase : null;
|
|
495
|
+
const onThinkingDelta = typeof options.onThinkingDelta === "function" ? options.onThinkingDelta : null;
|
|
494
496
|
const invokeNative = (sessionIdValue = "", timeoutOverrideMs = timeoutMs) => runNativeAgentImpl({
|
|
495
497
|
workspaceRoot,
|
|
496
498
|
provider,
|
|
@@ -501,6 +503,8 @@ async function runNaturalLanguageTask(task = "", state = {}, options = {}) {
|
|
|
501
503
|
sessionId: String(sessionIdValue || ""),
|
|
502
504
|
timeoutMs: timeoutOverrideMs,
|
|
503
505
|
onStreamDelta: onStream,
|
|
506
|
+
onThinkingDelta,
|
|
507
|
+
onPhase,
|
|
504
508
|
onToolEvent: (event) => {
|
|
505
509
|
pushToolLog(event);
|
|
506
510
|
},
|
package/src/code/nativeRunner.js
CHANGED
|
@@ -512,12 +512,23 @@ function runCoreTool({ tool = "", args = {}, workspaceRoot = process.cwd(), onTo
|
|
|
512
512
|
return result;
|
|
513
513
|
}
|
|
514
514
|
|
|
515
|
+
function emitPhase(callback, event = {}) {
|
|
516
|
+
if (typeof callback !== "function") return;
|
|
517
|
+
try {
|
|
518
|
+
callback(event);
|
|
519
|
+
} catch {
|
|
520
|
+
// ignore phase callback failures
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
515
524
|
async function runOpenAiLikeTurn({
|
|
516
525
|
url = "",
|
|
517
526
|
apiKey = "",
|
|
518
527
|
model = "",
|
|
519
528
|
messages = [],
|
|
520
529
|
onTextDelta = null,
|
|
530
|
+
onThinkingDelta = null,
|
|
531
|
+
onPhase = null,
|
|
521
532
|
signal = null,
|
|
522
533
|
timeoutMs = 300000,
|
|
523
534
|
} = {}) {
|
|
@@ -540,6 +551,8 @@ async function runOpenAiLikeTurn({
|
|
|
540
551
|
|
|
541
552
|
const request = createRequestController({ signal, timeoutMs });
|
|
542
553
|
|
|
554
|
+
emitPhase(onPhase, { type: "request_start" });
|
|
555
|
+
|
|
543
556
|
try {
|
|
544
557
|
const response = await fetch(url, {
|
|
545
558
|
method: "POST",
|
|
@@ -574,6 +587,7 @@ async function runOpenAiLikeTurn({
|
|
|
574
587
|
const toolCallMap = new Map();
|
|
575
588
|
let rawBuffer = "";
|
|
576
589
|
let responseText = "";
|
|
590
|
+
const announcedToolNames = new Set();
|
|
577
591
|
|
|
578
592
|
while (true) {
|
|
579
593
|
const { done, value } = await reader.read();
|
|
@@ -599,8 +613,19 @@ async function runOpenAiLikeTurn({
|
|
|
599
613
|
|
|
600
614
|
const delta = choice.delta && typeof choice.delta === "object" ? choice.delta : {};
|
|
601
615
|
|
|
616
|
+
const reasoningChunk = typeof delta.reasoning_content === "string"
|
|
617
|
+
? delta.reasoning_content
|
|
618
|
+
: (typeof delta.reasoning === "string" ? delta.reasoning : "");
|
|
619
|
+
if (reasoningChunk) {
|
|
620
|
+
emitPhase(onPhase, { type: "thinking_delta", text: reasoningChunk });
|
|
621
|
+
if (typeof onThinkingDelta === "function") {
|
|
622
|
+
onThinkingDelta(reasoningChunk);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
602
626
|
if (typeof delta.content === "string" && delta.content) {
|
|
603
627
|
responseText += delta.content;
|
|
628
|
+
emitPhase(onPhase, { type: "text_delta", text: delta.content });
|
|
604
629
|
if (typeof onTextDelta === "function") {
|
|
605
630
|
onTextDelta(delta.content);
|
|
606
631
|
}
|
|
@@ -629,6 +654,13 @@ async function runOpenAiLikeTurn({
|
|
|
629
654
|
}
|
|
630
655
|
|
|
631
656
|
toolCallMap.set(index, previous);
|
|
657
|
+
|
|
658
|
+
const toolName = previous.function.name;
|
|
659
|
+
const announceKey = `${index}:${toolName}`;
|
|
660
|
+
if (toolName && !announcedToolNames.has(announceKey)) {
|
|
661
|
+
announcedToolNames.add(announceKey);
|
|
662
|
+
emitPhase(onPhase, { type: "tool_request", name: toolName });
|
|
663
|
+
}
|
|
632
664
|
}
|
|
633
665
|
}
|
|
634
666
|
}
|
|
@@ -716,6 +748,8 @@ async function runAnthropicTurn({
|
|
|
716
748
|
systemPrompt = "",
|
|
717
749
|
messages = [],
|
|
718
750
|
onTextDelta = null,
|
|
751
|
+
onThinkingDelta = null,
|
|
752
|
+
onPhase = null,
|
|
719
753
|
signal = null,
|
|
720
754
|
timeoutMs = 300000,
|
|
721
755
|
} = {}) {
|
|
@@ -741,6 +775,8 @@ async function runAnthropicTurn({
|
|
|
741
775
|
|
|
742
776
|
const request = createRequestController({ signal, timeoutMs });
|
|
743
777
|
|
|
778
|
+
emitPhase(onPhase, { type: "request_start" });
|
|
779
|
+
|
|
744
780
|
try {
|
|
745
781
|
const response = await fetch(url, {
|
|
746
782
|
method: "POST",
|
|
@@ -811,6 +847,12 @@ async function runAnthropicTurn({
|
|
|
811
847
|
type: "text",
|
|
812
848
|
text: String(contentBlock.text || ""),
|
|
813
849
|
});
|
|
850
|
+
} else if (contentBlock.type === "thinking") {
|
|
851
|
+
blockMap.set(index, {
|
|
852
|
+
order: index,
|
|
853
|
+
type: "thinking",
|
|
854
|
+
text: String(contentBlock.thinking || ""),
|
|
855
|
+
});
|
|
814
856
|
} else if (contentBlock.type === "tool_use") {
|
|
815
857
|
blockMap.set(index, {
|
|
816
858
|
order: index,
|
|
@@ -822,6 +864,10 @@ async function runAnthropicTurn({
|
|
|
822
864
|
: {},
|
|
823
865
|
inputJson: "",
|
|
824
866
|
});
|
|
867
|
+
const toolName = String(contentBlock.name || "");
|
|
868
|
+
if (toolName) {
|
|
869
|
+
emitPhase(onPhase, { type: "tool_request", name: toolName });
|
|
870
|
+
}
|
|
825
871
|
}
|
|
826
872
|
continue;
|
|
827
873
|
}
|
|
@@ -840,6 +886,7 @@ async function runAnthropicTurn({
|
|
|
840
886
|
blockMap.set(index, current);
|
|
841
887
|
if (deltaText) {
|
|
842
888
|
responseText += deltaText;
|
|
889
|
+
emitPhase(onPhase, { type: "text_delta", text: deltaText });
|
|
843
890
|
if (typeof onTextDelta === "function") {
|
|
844
891
|
onTextDelta(deltaText);
|
|
845
892
|
}
|
|
@@ -847,6 +894,20 @@ async function runAnthropicTurn({
|
|
|
847
894
|
continue;
|
|
848
895
|
}
|
|
849
896
|
|
|
897
|
+
if (delta.type === "thinking_delta") {
|
|
898
|
+
const deltaText = String(delta.thinking || "");
|
|
899
|
+
current.type = "thinking";
|
|
900
|
+
current.text = `${String(current.text || "")}${deltaText}`;
|
|
901
|
+
blockMap.set(index, current);
|
|
902
|
+
if (deltaText) {
|
|
903
|
+
emitPhase(onPhase, { type: "thinking_delta", text: deltaText });
|
|
904
|
+
if (typeof onThinkingDelta === "function") {
|
|
905
|
+
onThinkingDelta(deltaText);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
continue;
|
|
909
|
+
}
|
|
910
|
+
|
|
850
911
|
if (delta.type === "input_json_delta") {
|
|
851
912
|
current.type = "tool_use";
|
|
852
913
|
current.inputJson = `${String(current.inputJson || "")}${String(delta.partial_json || "")}`;
|
|
@@ -859,6 +920,7 @@ async function runAnthropicTurn({
|
|
|
859
920
|
|
|
860
921
|
const assistantContent = Array.from(blockMap.values())
|
|
861
922
|
.sort((a, b) => a.order - b.order)
|
|
923
|
+
.filter((item) => item.type !== "thinking")
|
|
862
924
|
.map((item) => {
|
|
863
925
|
if (item.type === "text") {
|
|
864
926
|
return {
|
|
@@ -919,6 +981,8 @@ async function runNativeLoopOpenAi({
|
|
|
919
981
|
apiKey = "",
|
|
920
982
|
timeoutMs = 300000,
|
|
921
983
|
onStreamDelta = null,
|
|
984
|
+
onThinkingDelta = null,
|
|
985
|
+
onPhase = null,
|
|
922
986
|
onToolEvent = null,
|
|
923
987
|
signal = null,
|
|
924
988
|
guards,
|
|
@@ -957,6 +1021,8 @@ async function runNativeLoopOpenAi({
|
|
|
957
1021
|
messages,
|
|
958
1022
|
signal,
|
|
959
1023
|
timeoutMs,
|
|
1024
|
+
onPhase,
|
|
1025
|
+
onThinkingDelta,
|
|
960
1026
|
onTextDelta: (chunk) => {
|
|
961
1027
|
const text = String(chunk || "");
|
|
962
1028
|
if (!text) return;
|
|
@@ -1061,6 +1127,8 @@ async function runNativeLoopAnthropic({
|
|
|
1061
1127
|
apiKey = "",
|
|
1062
1128
|
timeoutMs = 300000,
|
|
1063
1129
|
onStreamDelta = null,
|
|
1130
|
+
onThinkingDelta = null,
|
|
1131
|
+
onPhase = null,
|
|
1064
1132
|
onToolEvent = null,
|
|
1065
1133
|
signal = null,
|
|
1066
1134
|
guards,
|
|
@@ -1098,6 +1166,8 @@ async function runNativeLoopAnthropic({
|
|
|
1098
1166
|
messages,
|
|
1099
1167
|
signal,
|
|
1100
1168
|
timeoutMs,
|
|
1169
|
+
onPhase,
|
|
1170
|
+
onThinkingDelta,
|
|
1101
1171
|
onTextDelta: (chunk) => {
|
|
1102
1172
|
const text = String(chunk || "");
|
|
1103
1173
|
if (!text) return;
|
|
@@ -1198,6 +1268,8 @@ async function runNativeAgentTask({
|
|
|
1198
1268
|
sessionId = "",
|
|
1199
1269
|
timeoutMs = 300000,
|
|
1200
1270
|
onStreamDelta = null,
|
|
1271
|
+
onThinkingDelta = null,
|
|
1272
|
+
onPhase = null,
|
|
1201
1273
|
onToolEvent = null,
|
|
1202
1274
|
signal = null,
|
|
1203
1275
|
} = {}) {
|
|
@@ -1238,6 +1310,8 @@ async function runNativeAgentTask({
|
|
|
1238
1310
|
apiKey: runtime.apiKey,
|
|
1239
1311
|
timeoutMs,
|
|
1240
1312
|
onStreamDelta,
|
|
1313
|
+
onThinkingDelta,
|
|
1314
|
+
onPhase,
|
|
1241
1315
|
onToolEvent,
|
|
1242
1316
|
signal,
|
|
1243
1317
|
guards,
|
package/src/code/tui.js
CHANGED
|
@@ -1615,29 +1615,44 @@ function runUcodeTui({
|
|
|
1615
1615
|
}
|
|
1616
1616
|
|
|
1617
1617
|
if (result.kind === "nl") {
|
|
1618
|
-
const statusMessages = [
|
|
1619
|
-
"Thinking...",
|
|
1620
|
-
"Processing your request...",
|
|
1621
|
-
"Analyzing...",
|
|
1622
|
-
"Working on it...",
|
|
1623
|
-
];
|
|
1624
|
-
const randomStatus = statusMessages[Math.floor(Math.random() * statusMessages.length)];
|
|
1625
1618
|
const abortController = new AbortController();
|
|
1626
1619
|
const escapeStripper = createEscapeTagStripper();
|
|
1627
1620
|
pendingTask = {
|
|
1628
1621
|
abortController,
|
|
1629
1622
|
startedAt: Date.now(),
|
|
1630
1623
|
};
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1624
|
+
const TOOL_LABELS = {
|
|
1625
|
+
read: "Reading file",
|
|
1626
|
+
write: "Writing file",
|
|
1627
|
+
edit: "Editing file",
|
|
1628
|
+
bash: "Running command",
|
|
1629
|
+
};
|
|
1630
|
+
const setNlStatus = (msg) => {
|
|
1631
|
+
updateStatus(msg, "thinking", {
|
|
1632
|
+
showTimer: true,
|
|
1633
|
+
startedAt: pendingTask.startedAt,
|
|
1634
|
+
});
|
|
1635
|
+
};
|
|
1636
|
+
setNlStatus("Waiting for model...");
|
|
1635
1637
|
let streamState = null;
|
|
1636
1638
|
let renderedToolLogCount = 0;
|
|
1637
1639
|
let nlResult = null;
|
|
1638
1640
|
try {
|
|
1639
1641
|
nlResult = await runNaturalLanguageTask(result.task, state, {
|
|
1640
1642
|
signal: abortController.signal,
|
|
1643
|
+
onPhase: (event) => {
|
|
1644
|
+
if (!event || typeof event !== "object") return;
|
|
1645
|
+
if (event.type === "request_start") {
|
|
1646
|
+
setNlStatus("Waiting for model...");
|
|
1647
|
+
} else if (event.type === "thinking_delta") {
|
|
1648
|
+
setNlStatus("Thinking...");
|
|
1649
|
+
} else if (event.type === "text_delta") {
|
|
1650
|
+
setNlStatus("Generating response...");
|
|
1651
|
+
} else if (event.type === "tool_request") {
|
|
1652
|
+
const label = TOOL_LABELS[String(event.name || "").toLowerCase()] || `Calling ${event.name}`;
|
|
1653
|
+
setNlStatus(`${label}...`);
|
|
1654
|
+
}
|
|
1655
|
+
},
|
|
1641
1656
|
onDelta: (delta) => {
|
|
1642
1657
|
const text = escapeStripper.write(String(delta || ""));
|
|
1643
1658
|
if (!text) return;
|
|
@@ -1648,6 +1663,10 @@ function runUcodeTui({
|
|
|
1648
1663
|
},
|
|
1649
1664
|
onToolLog: (entry) => {
|
|
1650
1665
|
renderedToolLogCount += 1;
|
|
1666
|
+
if (entry && entry.tool && entry.phase === "start") {
|
|
1667
|
+
const label = TOOL_LABELS[String(entry.tool || "").toLowerCase()] || `Calling ${entry.tool}`;
|
|
1668
|
+
setNlStatus(`${label}...`);
|
|
1669
|
+
}
|
|
1651
1670
|
logToolHint(entry);
|
|
1652
1671
|
},
|
|
1653
1672
|
});
|