@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 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, 8);
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, 6);
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 MONITOR" }),
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, 10),
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.map((e) => {
808
- const s = STATUS[e.status];
809
- const elapsed = e.status === "running" ? fmtElapsed(Math.max(0, nowTick - e.startedAt)) : e.status;
810
- const spark = sparkline(bucketActivity(e.recentTools, nowTick));
811
- const tool = e.currentTool?.name ?? e.recentTools[e.recentTools.length - 1]?.name ?? "";
812
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
813
- /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
814
- /* @__PURE__ */ jsx(Text, { color: s.color, bold: true, children: s.icon }),
815
- /* @__PURE__ */ jsx(Text, { bold: true, children: e.name.padEnd(12).slice(0, 12) }),
816
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: elapsed.padEnd(7).slice(0, 7) }),
817
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: renderProgress(e.toolCalls / maxTools, 10) }),
818
- /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
819
- "L",
820
- e.iterations,
821
- " ",
822
- e.toolCalls,
823
- "t"
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
- /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
831
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: " " }),
832
- /* @__PURE__ */ jsx(Text, { color: "green", children: spark }),
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 running = all.filter((e) => e.status === "running");
871
- const done = all.filter((e) => e.status === "success").length;
872
- const failed = all.filter((e) => e.status === "failed" || e.status === "timeout").length;
873
- const maxTools = Math.max(1, ...all.map((e) => e.toolCalls));
874
- const ordered = [...all].sort((a, b) => {
875
- const ra = a.status === "running" ? 0 : a.status === "idle" ? 1 : 2;
876
- const rb = b.status === "running" ? 0 : b.status === "idle" ? 1 : 2;
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, 20);
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 MONITOR" }),
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.length
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
- done
907
+ totalDone
915
908
  ] }),
916
- failed > 0 ? /* @__PURE__ */ jsxs(Text, { color: "red", children: [
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
- failed
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: "agents" }),
924
- /* @__PURE__ */ jsx(Text, { color: "magenta", children: all.length }),
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__ */ jsx(Text, { color: "green", children: ` $${totalCost.toFixed(3)}` })
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 subagents yet \u2014 spawn with /spawn or /fleet dispatch." }) : null,
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 tool = e.currentTool?.name ?? e.recentTools[e.recentTools.length - 1]?.name ?? "";
939
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
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.padEnd(14).slice(0, 14) }),
943
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: elapsed.padEnd(8).slice(0, 8) }),
944
- /* @__PURE__ */ jsx(Text, { color: "cyan", children: renderProgress(e.toolCalls / maxTools, 10) }),
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__ */ jsx(Text, { dimColor: true, children: " " }),
959
- /* @__PURE__ */ jsx(Text, { color: "green", children: spark }),
960
- tool ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: tool }) : null
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 snippet = stringOf(o["text"]) ?? stringOf(o["match"]) ?? stringOf(o["preview"]);
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 snippet ? `${head} ${truncMid(snippet.replace(/\s+/g, " "), 40)}` : head;
1985
+ return snippet2 ? `${head} ${truncMid(snippet2.replace(/\s+/g, " "), 40)}` : head;
1961
1986
  }
1962
- if (snippet) return truncMid(snippet, 70);
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: state.fleet,
5411
+ entries: entriesWithLeader,
5332
5412
  totalCost: state.fleetCost,
5333
5413
  totalTokens: state.fleetTokens,
5334
5414
  nowTick