@wrongstack/tui 0.8.0 → 0.8.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.js +206 -126
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -721,6 +721,10 @@ function fmtTokens(n) {
|
|
|
721
721
|
if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
|
|
722
722
|
return `${(n / 1e6).toFixed(1)}M`;
|
|
723
723
|
}
|
|
724
|
+
function fmtCost2(n) {
|
|
725
|
+
if (n === 0) return "\u2014";
|
|
726
|
+
return `$${n.toFixed(3)}`;
|
|
727
|
+
}
|
|
724
728
|
function FleetMonitor({
|
|
725
729
|
entries,
|
|
726
730
|
totalCost,
|
|
@@ -730,17 +734,19 @@ function FleetMonitor({
|
|
|
730
734
|
}) {
|
|
731
735
|
const all = Object.values(entries);
|
|
732
736
|
const running = all.filter((e) => e.status === "running");
|
|
737
|
+
const idle = all.filter((e) => e.status === "idle").length;
|
|
733
738
|
const done = all.filter((e) => e.status === "success").length;
|
|
734
739
|
const failed = all.filter((e) => e.status === "failed" || e.status === "timeout").length;
|
|
735
740
|
const concurrencyRatio = maxConcurrent > 0 ? running.length / maxConcurrent : 0;
|
|
736
|
-
const maxTools = Math.max(1, ...all.map((e) => e.toolCalls));
|
|
737
741
|
const ordered = [...all].sort((a, b) => {
|
|
738
742
|
const ra = a.status === "running" ? 0 : a.status === "idle" ? 1 : 2;
|
|
739
743
|
const rb = b.status === "running" ? 0 : b.status === "idle" ? 1 : 2;
|
|
740
744
|
if (ra !== rb) return ra - rb;
|
|
745
|
+
if (ra === 2) return b.lastEventAt - a.lastEventAt;
|
|
741
746
|
return a.startedAt - b.startedAt;
|
|
742
747
|
});
|
|
743
|
-
const shown = ordered.slice(0,
|
|
748
|
+
const shown = ordered.slice(0, 12);
|
|
749
|
+
const overflow = all.length - shown.length;
|
|
744
750
|
const events = [];
|
|
745
751
|
for (const e of all) {
|
|
746
752
|
events.push({ at: e.startedAt, icon: "\u25CF", color: "cyan", text: `${e.name} spawned` });
|
|
@@ -763,15 +769,19 @@ function FleetMonitor({
|
|
|
763
769
|
}
|
|
764
770
|
}
|
|
765
771
|
events.sort((a, b) => b.at - a.at);
|
|
766
|
-
const timeline = events.slice(0,
|
|
772
|
+
const timeline = events.slice(0, 8);
|
|
767
773
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
|
|
768
774
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
769
|
-
/* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "FLEET
|
|
775
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "FLEET \xB7 ORCHESTRATION" }),
|
|
770
776
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
|
|
771
777
|
/* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
|
|
772
778
|
"\u25B6",
|
|
773
779
|
running.length
|
|
774
780
|
] }),
|
|
781
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
782
|
+
"\u25CB",
|
|
783
|
+
idle
|
|
784
|
+
] }),
|
|
775
785
|
/* @__PURE__ */ jsxs(Text, { color: "green", children: [
|
|
776
786
|
"\u2713",
|
|
777
787
|
done
|
|
@@ -786,7 +796,7 @@ function FleetMonitor({
|
|
|
786
796
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "concurrency" }),
|
|
787
797
|
/* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
|
|
788
798
|
"[",
|
|
789
|
-
renderProgress(concurrencyRatio,
|
|
799
|
+
renderProgress(concurrencyRatio, 12),
|
|
790
800
|
"]"
|
|
791
801
|
] }),
|
|
792
802
|
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
@@ -804,36 +814,33 @@ function FleetMonitor({
|
|
|
804
814
|
/* @__PURE__ */ jsx(Text, { color: "green", children: ` $${totalCost.toFixed(3)}` })
|
|
805
815
|
] }),
|
|
806
816
|
shown.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No subagents yet \u2014 spawn with /fleet spawn or /fleet dispatch." }) : null,
|
|
807
|
-
shown.
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
/* @__PURE__ */
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
817
|
+
shown.length > 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
818
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
819
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
|
|
820
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "name".padEnd(16) }),
|
|
821
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "status".padEnd(10) }),
|
|
822
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "L/t".padEnd(8) }),
|
|
823
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "elapsed".padEnd(8) }),
|
|
824
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "cost" })
|
|
825
|
+
] }),
|
|
826
|
+
shown.map((e) => {
|
|
827
|
+
const s = STATUS[e.status];
|
|
828
|
+
const elapsed = e.status === "running" ? fmtElapsed(Math.max(0, nowTick - e.startedAt)) : fmtElapsed(Math.max(0, nowTick - e.lastEventAt)) + " ago";
|
|
829
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
830
|
+
/* @__PURE__ */ jsx(Text, { color: s.color, children: s.icon }),
|
|
831
|
+
/* @__PURE__ */ jsx(Text, { children: e.name.padEnd(16).slice(0, 16) }),
|
|
832
|
+
/* @__PURE__ */ jsx(Text, { color: s.color, children: e.status.padEnd(10) }),
|
|
833
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: `L${e.iterations} ${e.toolCalls}t`.padEnd(8) }),
|
|
834
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: elapsed.padEnd(8).slice(0, 8) }),
|
|
835
|
+
/* @__PURE__ */ jsx(Text, { color: "yellow", children: fmtCost2(e.cost) }),
|
|
825
836
|
e.extensions && e.extensions > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
|
|
826
|
-
"\u26A1\xD7",
|
|
837
|
+
" \u26A1\xD7",
|
|
827
838
|
e.extensions
|
|
828
839
|
] }) : null
|
|
829
|
-
] })
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
tool ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: tool }) : null
|
|
834
|
-
] })
|
|
835
|
-
] }, e.id);
|
|
836
|
-
}),
|
|
840
|
+
] }, e.id);
|
|
841
|
+
}),
|
|
842
|
+
overflow > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u2026 +${overflow} more` }) : null
|
|
843
|
+
] }) : null,
|
|
837
844
|
timeline.length > 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
838
845
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "timeline" }),
|
|
839
846
|
timeline.map((ev, i) => (
|
|
@@ -855,11 +862,19 @@ var STATUS2 = {
|
|
|
855
862
|
timeout: { icon: "\u23F1", color: "yellow" },
|
|
856
863
|
stopped: { icon: "\u2298", color: "gray" }
|
|
857
864
|
};
|
|
865
|
+
function isTerminal(status) {
|
|
866
|
+
return status === "success" || status === "failed" || status === "timeout" || status === "stopped";
|
|
867
|
+
}
|
|
858
868
|
function fmtTokens2(n) {
|
|
859
869
|
if (n < 1e3) return String(n);
|
|
860
870
|
if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
|
|
861
871
|
return `${(n / 1e6).toFixed(1)}M`;
|
|
862
872
|
}
|
|
873
|
+
function snippet(s, max = 72) {
|
|
874
|
+
const oneLine2 = s.replace(/\s+/g, " ").trim();
|
|
875
|
+
if (oneLine2.length <= max) return oneLine2;
|
|
876
|
+
return `${oneLine2.slice(0, max - 1)}\u2026`;
|
|
877
|
+
}
|
|
863
878
|
function AgentsMonitor({
|
|
864
879
|
entries,
|
|
865
880
|
totalCost,
|
|
@@ -867,81 +882,69 @@ function AgentsMonitor({
|
|
|
867
882
|
nowTick
|
|
868
883
|
}) {
|
|
869
884
|
const all = Object.values(entries);
|
|
870
|
-
const
|
|
871
|
-
const
|
|
872
|
-
const
|
|
873
|
-
const
|
|
874
|
-
const ordered = [...
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
if (ra !== rb) return ra - rb;
|
|
885
|
+
const live = all.filter((e) => !isTerminal(e.status));
|
|
886
|
+
const running = live.filter((e) => e.status === "running").length;
|
|
887
|
+
const totalDone = all.filter((e) => e.status === "success").length;
|
|
888
|
+
const totalFailed = all.filter((e) => e.status === "failed" || e.status === "timeout").length;
|
|
889
|
+
const ordered = [...live].sort((a, b) => {
|
|
890
|
+
if (a.status === "running" && b.status !== "running") return -1;
|
|
891
|
+
if (a.status !== "running" && b.status === "running") return 1;
|
|
878
892
|
return a.startedAt - b.startedAt;
|
|
879
893
|
});
|
|
880
|
-
const shown = ordered.slice(0,
|
|
881
|
-
const events = [];
|
|
882
|
-
for (const e of all) {
|
|
883
|
-
events.push({ at: e.startedAt, icon: "\u25CF", color: "cyan", text: `${e.name} spawned` });
|
|
884
|
-
if (e.status !== "running" && e.status !== "idle") {
|
|
885
|
-
const s = STATUS2[e.status];
|
|
886
|
-
events.push({
|
|
887
|
-
at: e.lastEventAt,
|
|
888
|
-
icon: s.icon,
|
|
889
|
-
color: s.color,
|
|
890
|
-
text: `${e.name} ${e.status} (${e.toolCalls}t)`
|
|
891
|
-
});
|
|
892
|
-
}
|
|
893
|
-
if (e.budgetWarning) {
|
|
894
|
-
events.push({
|
|
895
|
-
at: e.budgetWarning.at,
|
|
896
|
-
icon: "\u26A1",
|
|
897
|
-
color: "yellow",
|
|
898
|
-
text: `${e.name} ${e.budgetWarning.kind} ${e.budgetWarning.used}/${e.budgetWarning.limit} \u2014 extending`
|
|
899
|
-
});
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
events.sort((a, b) => b.at - a.at);
|
|
903
|
-
const timeline = events.slice(0, 6);
|
|
894
|
+
const shown = ordered.slice(0, 8);
|
|
904
895
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, children: [
|
|
905
896
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
906
|
-
/* @__PURE__ */ jsx(Text, { bold: true, color: "magenta", children: "AGENTS
|
|
897
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: "magenta", children: "AGENTS \xB7 LIVE" }),
|
|
907
898
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
|
|
908
899
|
/* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
|
|
909
900
|
"\u25B6",
|
|
910
|
-
running
|
|
901
|
+
running
|
|
911
902
|
] }),
|
|
903
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
|
|
904
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "done" }),
|
|
912
905
|
/* @__PURE__ */ jsxs(Text, { color: "green", children: [
|
|
913
906
|
"\u2713",
|
|
914
|
-
|
|
907
|
+
totalDone
|
|
915
908
|
] }),
|
|
916
|
-
|
|
909
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
|
|
910
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "failed" }),
|
|
911
|
+
totalFailed > 0 ? /* @__PURE__ */ jsxs(Text, { color: "red", children: [
|
|
917
912
|
"\u2717",
|
|
918
|
-
|
|
913
|
+
totalFailed
|
|
919
914
|
] }) : null,
|
|
920
915
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 Ctrl+G to close" })
|
|
921
916
|
] }),
|
|
922
917
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
923
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "
|
|
924
|
-
/* @__PURE__ */ jsx(Text, { color: "magenta", children:
|
|
918
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "shown" }),
|
|
919
|
+
/* @__PURE__ */ jsx(Text, { color: "magenta", children: live.length }),
|
|
925
920
|
totalTokens ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
921
|
+
" ",
|
|
926
922
|
fmtTokens2(totalTokens.input),
|
|
927
923
|
"\u2191 ",
|
|
928
924
|
fmtTokens2(totalTokens.output),
|
|
929
925
|
"\u2193"
|
|
930
926
|
] }) : null,
|
|
931
|
-
/* @__PURE__ */
|
|
927
|
+
/* @__PURE__ */ jsxs(Text, { color: "green", children: [
|
|
928
|
+
"$",
|
|
929
|
+
totalCost.toFixed(3)
|
|
930
|
+
] })
|
|
932
931
|
] }),
|
|
933
|
-
shown.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No
|
|
932
|
+
shown.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No live agents \u2014 spawn with /spawn or /fleet dispatch." }) : null,
|
|
934
933
|
shown.map((e) => {
|
|
935
934
|
const s = STATUS2[e.status];
|
|
936
935
|
const elapsed = e.status === "running" ? fmtElapsed(Math.max(0, nowTick - e.startedAt)) : e.status;
|
|
937
936
|
const spark = sparkline(bucketActivity(e.recentTools, nowTick));
|
|
938
|
-
const
|
|
939
|
-
|
|
937
|
+
const lastTool = e.recentTools[e.recentTools.length - 1];
|
|
938
|
+
const lastMessage = e.recentMessages[e.recentMessages.length - 1];
|
|
939
|
+
const streamTail = e.streamingText ? snippet(e.streamingText.slice(-160)) : "";
|
|
940
|
+
const toolElapsed = e.currentTool ? Math.max(0, nowTick - e.currentTool.startedAt) : 0;
|
|
941
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
940
942
|
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
941
943
|
/* @__PURE__ */ jsx(Text, { color: s.color, bold: true, children: s.icon }),
|
|
942
|
-
/* @__PURE__ */ jsx(Text, { bold: true, children: e.name
|
|
943
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children:
|
|
944
|
-
/* @__PURE__ */ jsx(Text, {
|
|
944
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: e.name }),
|
|
945
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
|
|
946
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: elapsed }),
|
|
947
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7" }),
|
|
945
948
|
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
946
949
|
"L",
|
|
947
950
|
e.iterations,
|
|
@@ -954,24 +957,46 @@ function AgentsMonitor({
|
|
|
954
957
|
e.extensions
|
|
955
958
|
] }) : null
|
|
956
959
|
] }),
|
|
957
|
-
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
958
|
-
/* @__PURE__ */
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
960
|
+
e.status === "running" && e.currentTool ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, paddingLeft: 2, children: [
|
|
961
|
+
/* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
|
|
962
|
+
"\u2192 ",
|
|
963
|
+
e.currentTool.name
|
|
964
|
+
] }),
|
|
965
|
+
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
966
|
+
"(",
|
|
967
|
+
toolElapsed,
|
|
968
|
+
"ms)"
|
|
969
|
+
] })
|
|
970
|
+
] }) : null,
|
|
971
|
+
spark || lastTool ? /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, paddingLeft: 2, children: [
|
|
972
|
+
/* @__PURE__ */ jsx(Text, { color: "green", children: spark || "" }),
|
|
973
|
+
lastTool ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
974
|
+
"last: ",
|
|
975
|
+
lastTool.name,
|
|
976
|
+
typeof lastTool.durationMs === "number" ? ` ${lastTool.durationMs}ms` : "",
|
|
977
|
+
lastTool.ok === false ? " \u2717" : ""
|
|
978
|
+
] }) : null
|
|
979
|
+
] }) : null,
|
|
980
|
+
e.status === "running" && streamTail ? /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
981
|
+
">",
|
|
982
|
+
" ",
|
|
983
|
+
streamTail
|
|
984
|
+
] }) }) : null,
|
|
985
|
+
(e.status !== "running" || !streamTail) && lastMessage ? /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
986
|
+
"msg: ",
|
|
987
|
+
snippet(lastMessage.text)
|
|
988
|
+
] }) }) : null,
|
|
989
|
+
e.budgetWarning ? /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
|
|
990
|
+
"\u26A1 ",
|
|
991
|
+
e.budgetWarning.kind,
|
|
992
|
+
" ",
|
|
993
|
+
e.budgetWarning.used,
|
|
994
|
+
"/",
|
|
995
|
+
e.budgetWarning.limit,
|
|
996
|
+
" \u2014 extending"
|
|
997
|
+
] }) }) : null
|
|
962
998
|
] }, e.id);
|
|
963
|
-
})
|
|
964
|
-
timeline.length > 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
965
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "timeline" }),
|
|
966
|
-
timeline.map((ev, i) => (
|
|
967
|
-
// biome-ignore lint/suspicious/noArrayIndexKey: timeline is rebuilt per render
|
|
968
|
-
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
969
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: `${fmtElapsed(Math.max(0, nowTick - ev.at))} ago`.padEnd(10) }),
|
|
970
|
-
/* @__PURE__ */ jsx(Text, { color: ev.color, children: ev.icon }),
|
|
971
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: ev.text })
|
|
972
|
-
] }, i)
|
|
973
|
-
))
|
|
974
|
-
] }) : null
|
|
999
|
+
})
|
|
975
1000
|
] });
|
|
976
1001
|
}
|
|
977
1002
|
|
|
@@ -1954,12 +1979,12 @@ function formatMatchHit(hit) {
|
|
|
1954
1979
|
const o = hit;
|
|
1955
1980
|
const file = stringOf(o["file"]) ?? stringOf(o["path"]);
|
|
1956
1981
|
const line = numOf(o["line"]) ?? numOf(o["lineNumber"]);
|
|
1957
|
-
const
|
|
1982
|
+
const snippet2 = stringOf(o["text"]) ?? stringOf(o["match"]) ?? stringOf(o["preview"]);
|
|
1958
1983
|
if (file) {
|
|
1959
1984
|
const head = line !== void 0 ? `${shortenPath(file, 40)}:${line}` : shortenPath(file, 50);
|
|
1960
|
-
return
|
|
1985
|
+
return snippet2 ? `${head} ${truncMid(snippet2.replace(/\s+/g, " "), 40)}` : head;
|
|
1961
1986
|
}
|
|
1962
|
-
if (
|
|
1987
|
+
if (snippet2) return truncMid(snippet2, 70);
|
|
1963
1988
|
}
|
|
1964
1989
|
return void 0;
|
|
1965
1990
|
}
|
|
@@ -3102,6 +3127,50 @@ function reducer(state, action) {
|
|
|
3102
3127
|
fleetTokens: action.input !== void 0 || action.output !== void 0 ? { input: action.input ?? state.fleetTokens.input, output: action.output ?? state.fleetTokens.output } : state.fleetTokens
|
|
3103
3128
|
};
|
|
3104
3129
|
}
|
|
3130
|
+
case "leaderIterStart": {
|
|
3131
|
+
return {
|
|
3132
|
+
...state,
|
|
3133
|
+
leader: {
|
|
3134
|
+
...state.leader,
|
|
3135
|
+
iterations: state.leader.iterations + 1,
|
|
3136
|
+
iterating: true,
|
|
3137
|
+
lastEventAt: Date.now()
|
|
3138
|
+
}
|
|
3139
|
+
};
|
|
3140
|
+
}
|
|
3141
|
+
case "leaderIterEnd": {
|
|
3142
|
+
return {
|
|
3143
|
+
...state,
|
|
3144
|
+
leader: { ...state.leader, iterating: false, lastEventAt: Date.now() }
|
|
3145
|
+
};
|
|
3146
|
+
}
|
|
3147
|
+
case "leaderToolStart": {
|
|
3148
|
+
return {
|
|
3149
|
+
...state,
|
|
3150
|
+
leader: {
|
|
3151
|
+
...state.leader,
|
|
3152
|
+
currentTool: { name: action.name, startedAt: Date.now() },
|
|
3153
|
+
lastEventAt: Date.now()
|
|
3154
|
+
}
|
|
3155
|
+
};
|
|
3156
|
+
}
|
|
3157
|
+
case "leaderToolEnd": {
|
|
3158
|
+
const now = Date.now();
|
|
3159
|
+
const recentTools = [
|
|
3160
|
+
...state.leader.recentTools,
|
|
3161
|
+
{ name: action.name, ok: action.ok, durationMs: action.durationMs, at: now }
|
|
3162
|
+
].slice(-8);
|
|
3163
|
+
return {
|
|
3164
|
+
...state,
|
|
3165
|
+
leader: {
|
|
3166
|
+
...state.leader,
|
|
3167
|
+
toolCalls: state.leader.toolCalls + 1,
|
|
3168
|
+
currentTool: void 0,
|
|
3169
|
+
recentTools,
|
|
3170
|
+
lastEventAt: now
|
|
3171
|
+
}
|
|
3172
|
+
};
|
|
3173
|
+
}
|
|
3105
3174
|
case "setStreamFleet": {
|
|
3106
3175
|
return { ...state, streamFleet: action.enabled };
|
|
3107
3176
|
}
|
|
@@ -3299,6 +3368,15 @@ function App({
|
|
|
3299
3368
|
confirmQueue: [],
|
|
3300
3369
|
contextChipVersion: 0,
|
|
3301
3370
|
fleet: {},
|
|
3371
|
+
leader: {
|
|
3372
|
+
iterations: 0,
|
|
3373
|
+
toolCalls: 0,
|
|
3374
|
+
recentTools: [],
|
|
3375
|
+
currentTool: void 0,
|
|
3376
|
+
startedAt: Date.now(),
|
|
3377
|
+
lastEventAt: Date.now(),
|
|
3378
|
+
iterating: false
|
|
3379
|
+
},
|
|
3302
3380
|
fleetCost: 0,
|
|
3303
3381
|
fleetTokens: { input: 0, output: 0 },
|
|
3304
3382
|
streamFleet: true,
|
|
@@ -3396,6 +3474,25 @@ function App({
|
|
|
3396
3474
|
}
|
|
3397
3475
|
return { running, idle, pending: 0, completed };
|
|
3398
3476
|
}, [state.fleet]);
|
|
3477
|
+
const entriesWithLeader = useMemo(() => {
|
|
3478
|
+
const leaderEntry = {
|
|
3479
|
+
id: "leader",
|
|
3480
|
+
name: "LEADER",
|
|
3481
|
+
provider,
|
|
3482
|
+
model,
|
|
3483
|
+
status: state.status === "running" || state.status === "streaming" || state.leader.iterating ? "running" : "idle",
|
|
3484
|
+
streamingText: "",
|
|
3485
|
+
iterations: state.leader.iterations,
|
|
3486
|
+
toolCalls: state.leader.toolCalls,
|
|
3487
|
+
recentTools: state.leader.recentTools,
|
|
3488
|
+
recentMessages: [],
|
|
3489
|
+
cost: 0,
|
|
3490
|
+
startedAt: state.leader.startedAt,
|
|
3491
|
+
lastEventAt: state.leader.lastEventAt,
|
|
3492
|
+
currentTool: state.leader.currentTool
|
|
3493
|
+
};
|
|
3494
|
+
return { leader: leaderEntry, ...state.fleet };
|
|
3495
|
+
}, [state.fleet, state.leader, state.status, provider, model]);
|
|
3399
3496
|
const STREAM_COLORS = ["cyan", "magenta", "yellow", "green", "blue"];
|
|
3400
3497
|
const labelsRef = useRef(/* @__PURE__ */ new Map());
|
|
3401
3498
|
const labelFor = (id, name) => {
|
|
@@ -3411,32 +3508,6 @@ function App({
|
|
|
3411
3508
|
m.set(id, v);
|
|
3412
3509
|
return v;
|
|
3413
3510
|
};
|
|
3414
|
-
const fleetAgents = useMemo(() => {
|
|
3415
|
-
const entries = Object.entries(state.fleet);
|
|
3416
|
-
if (entries.length === 0) return void 0;
|
|
3417
|
-
const active = entries.filter(([_id, e]) => e.status === "running" || e.status === "idle");
|
|
3418
|
-
if (active.length === 0) return void 0;
|
|
3419
|
-
active.sort((a, b) => {
|
|
3420
|
-
const sa = a[1].status === "running" ? 0 : 1;
|
|
3421
|
-
const sb = b[1].status === "running" ? 0 : 1;
|
|
3422
|
-
if (sa !== sb) return sa - sb;
|
|
3423
|
-
return a[1].startedAt - b[1].startedAt;
|
|
3424
|
-
});
|
|
3425
|
-
return active.slice(0, 4).map(([id, e]) => {
|
|
3426
|
-
const lbl = labelFor(id, e.name);
|
|
3427
|
-
return {
|
|
3428
|
-
label: lbl.label,
|
|
3429
|
-
color: lbl.color,
|
|
3430
|
-
elapsedMs: Math.max(0, nowTick - e.startedAt),
|
|
3431
|
-
toolCalls: e.toolCalls,
|
|
3432
|
-
running: e.status === "running",
|
|
3433
|
-
// Last/current action, so the 4th line shows what each agent is
|
|
3434
|
-
// doing right now (e.g. "▶ 12s · 8t · bash") rather than just counts.
|
|
3435
|
-
tool: e.currentTool?.name,
|
|
3436
|
-
extensions: e.extensions
|
|
3437
|
-
};
|
|
3438
|
-
});
|
|
3439
|
-
}, [state.fleet, nowTick]);
|
|
3440
3511
|
const [planCounts, setPlanCounts] = useState(null);
|
|
3441
3512
|
useEffect(() => {
|
|
3442
3513
|
const planPath = agent.ctx.meta["plan.path"];
|
|
@@ -3851,6 +3922,13 @@ function App({
|
|
|
3851
3922
|
});
|
|
3852
3923
|
const offToolStart = events.on("tool.started", (e) => {
|
|
3853
3924
|
dispatch({ type: "toolStarted", id: e.id, name: e.name });
|
|
3925
|
+
dispatch({ type: "leaderToolStart", name: e.name });
|
|
3926
|
+
});
|
|
3927
|
+
const offIterStart = events.on("iteration.started", () => {
|
|
3928
|
+
dispatch({ type: "leaderIterStart" });
|
|
3929
|
+
});
|
|
3930
|
+
const offIterEnd = events.on("iteration.completed", () => {
|
|
3931
|
+
dispatch({ type: "leaderIterEnd" });
|
|
3854
3932
|
});
|
|
3855
3933
|
const offToolProgress = events.on("tool.progress", (e) => {
|
|
3856
3934
|
if (e.event.type !== "partial_output" || !e.event.text) return;
|
|
@@ -3882,6 +3960,7 @@ function App({
|
|
|
3882
3960
|
});
|
|
3883
3961
|
dispatch({ type: "toolEnded", name: e.name });
|
|
3884
3962
|
dispatch({ type: "toolStreamClear", name: e.name });
|
|
3963
|
+
dispatch({ type: "leaderToolEnd", name: e.name, ok: e.ok, durationMs: e.durationMs });
|
|
3885
3964
|
if (e.ok && e.name === "todo") {
|
|
3886
3965
|
dispatch({
|
|
3887
3966
|
type: "addEntry",
|
|
@@ -3941,6 +4020,8 @@ function App({
|
|
|
3941
4020
|
return () => {
|
|
3942
4021
|
offDelta();
|
|
3943
4022
|
offToolStart();
|
|
4023
|
+
offIterStart();
|
|
4024
|
+
offIterEnd();
|
|
3944
4025
|
offToolProgress();
|
|
3945
4026
|
offTool();
|
|
3946
4027
|
offRetry();
|
|
@@ -5314,7 +5395,6 @@ User message:
|
|
|
5314
5395
|
todos,
|
|
5315
5396
|
plan: planCounts ?? void 0,
|
|
5316
5397
|
fleet: fleetCounts,
|
|
5317
|
-
fleetAgents,
|
|
5318
5398
|
git: gitInfo,
|
|
5319
5399
|
context: contextWindow,
|
|
5320
5400
|
projectName,
|
|
@@ -5328,7 +5408,7 @@ User message:
|
|
|
5328
5408
|
state.agentsMonitorOpen ? /* @__PURE__ */ jsx(
|
|
5329
5409
|
AgentsMonitor,
|
|
5330
5410
|
{
|
|
5331
|
-
entries:
|
|
5411
|
+
entries: entriesWithLeader,
|
|
5332
5412
|
totalCost: state.fleetCost,
|
|
5333
5413
|
totalTokens: state.fleetTokens,
|
|
5334
5414
|
nowTick
|