@wrongstack/tui 0.7.2 → 0.7.4

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
@@ -847,6 +847,133 @@ function FleetMonitor({
847
847
  ] }) : null
848
848
  ] });
849
849
  }
850
+ var STATUS2 = {
851
+ idle: { icon: "\u25CB", color: "gray" },
852
+ running: { icon: "\u25B6", color: "yellow" },
853
+ success: { icon: "\u2713", color: "green" },
854
+ failed: { icon: "\u2717", color: "red" },
855
+ timeout: { icon: "\u23F1", color: "yellow" },
856
+ stopped: { icon: "\u2298", color: "gray" }
857
+ };
858
+ function fmtTokens2(n) {
859
+ if (n < 1e3) return String(n);
860
+ if (n < 1e6) return `${(n / 1e3).toFixed(1)}k`;
861
+ return `${(n / 1e6).toFixed(1)}M`;
862
+ }
863
+ function AgentsMonitor({
864
+ entries,
865
+ totalCost,
866
+ totalTokens,
867
+ nowTick
868
+ }) {
869
+ 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;
878
+ return a.startedAt - b.startedAt;
879
+ });
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);
904
+ return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "magenta", paddingX: 1, children: [
905
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
906
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "magenta", children: "AGENTS MONITOR" }),
907
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\u2502" }),
908
+ /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
909
+ "\u25B6",
910
+ running.length
911
+ ] }),
912
+ /* @__PURE__ */ jsxs(Text, { color: "green", children: [
913
+ "\u2713",
914
+ done
915
+ ] }),
916
+ failed > 0 ? /* @__PURE__ */ jsxs(Text, { color: "red", children: [
917
+ "\u2717",
918
+ failed
919
+ ] }) : null,
920
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "\xB7 Ctrl+Shift+M to close" })
921
+ ] }),
922
+ /* @__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 }),
925
+ totalTokens ? /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
926
+ fmtTokens2(totalTokens.input),
927
+ "\u2191 ",
928
+ fmtTokens2(totalTokens.output),
929
+ "\u2193"
930
+ ] }) : null,
931
+ /* @__PURE__ */ jsx(Text, { color: "green", children: ` $${totalCost.toFixed(3)}` })
932
+ ] }),
933
+ shown.length === 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: "No subagents yet \u2014 spawn with /spawn or /fleet dispatch." }) : null,
934
+ shown.map((e) => {
935
+ const s = STATUS2[e.status];
936
+ const elapsed = e.status === "running" ? fmtElapsed(Math.max(0, nowTick - e.startedAt)) : e.status;
937
+ 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: [
940
+ /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
941
+ /* @__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) }),
945
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
946
+ "L",
947
+ e.iterations,
948
+ " ",
949
+ e.toolCalls,
950
+ "t"
951
+ ] }),
952
+ e.extensions && e.extensions > 0 ? /* @__PURE__ */ jsxs(Text, { color: "yellow", children: [
953
+ "\u26A1\xD7",
954
+ e.extensions
955
+ ] }) : null
956
+ ] }),
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
+ ] })
962
+ ] }, 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
975
+ ] });
976
+ }
850
977
 
851
978
  // src/markdown-table.ts
852
979
  function renderMarkdownTables(text, maxWidth) {
@@ -1136,10 +1263,16 @@ function Entry({
1136
1263
  switch (entry.kind) {
1137
1264
  case "user":
1138
1265
  return /* @__PURE__ */ jsxs(Text, { children: [
1139
- /* @__PURE__ */ jsx(Text, { color: entry.queued ? "yellow" : "cyan", children: entry.queued ? "\u231B" : "\u203A" }),
1140
- " ",
1141
- /* @__PURE__ */ jsx(Text, { dimColor: entry.queued ?? false, children: entry.text }),
1142
- entry.queued ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: " (queued)" }) : null
1266
+ /* @__PURE__ */ jsx(Text, { bold: true, color: "yellow", children: "USER: " }),
1267
+ /* @__PURE__ */ jsx(Text, { color: "white", children: entry.text }),
1268
+ entry.queued ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: " (queued)" }) : null,
1269
+ entry.pasteContent ? /* @__PURE__ */ jsxs(Fragment, { children: [
1270
+ entry.text ? "\n" : null,
1271
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
1272
+ " \u21B3 ",
1273
+ entry.pasteContent
1274
+ ] })
1275
+ ] }) : null
1143
1276
  ] });
1144
1277
  case "assistant":
1145
1278
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginY: 1, children: [
@@ -2501,20 +2634,31 @@ function reducer(state, action) {
2501
2634
  case "setBuffer":
2502
2635
  return { ...state, buffer: action.buffer, cursor: action.cursor };
2503
2636
  case "addPlaceholder":
2504
- return { ...state, placeholders: [...state.placeholders, action.ph] };
2637
+ return {
2638
+ ...state,
2639
+ placeholders: [...state.placeholders, action.ph],
2640
+ placeholderContents: [...state.placeholderContents, action.content ?? ""]
2641
+ };
2505
2642
  case "removeLastPlaceholder":
2506
2643
  if (state.placeholders.length === 0) return state;
2507
- return { ...state, placeholders: state.placeholders.slice(0, -1) };
2644
+ return {
2645
+ ...state,
2646
+ placeholders: state.placeholders.slice(0, -1),
2647
+ placeholderContents: state.placeholderContents.slice(0, -1)
2648
+ };
2508
2649
  case "clearInput":
2509
2650
  return {
2510
2651
  ...state,
2511
2652
  buffer: "",
2512
2653
  cursor: 0,
2513
2654
  placeholders: [],
2655
+ placeholderContents: [],
2514
2656
  historyIndex: 0,
2515
2657
  picker: { open: false, query: "", matches: [], selected: 0 },
2516
2658
  slashPicker: { open: false, query: "", matches: [], selected: 0 }
2517
2659
  };
2660
+ case "clearPlaceholdersOnly":
2661
+ return { ...state, placeholders: [], placeholderContents: [] };
2518
2662
  case "clearHistory": {
2519
2663
  const last = state.entries[state.entries.length - 1];
2520
2664
  return {
@@ -2893,10 +3037,9 @@ function reducer(state, action) {
2893
3037
  case "fleetUsage": {
2894
3038
  const cur = state.fleet[action.id];
2895
3039
  if (!cur) return state;
2896
- const cost = cur.cost;
2897
3040
  return {
2898
3041
  ...state,
2899
- fleet: { ...state.fleet, [action.id]: { ...cur, cost, lastEventAt: Date.now() } }
3042
+ fleet: { ...state.fleet, [action.id]: { ...cur, lastEventAt: Date.now() } }
2900
3043
  };
2901
3044
  }
2902
3045
  case "fleetDone": {
@@ -2965,6 +3108,9 @@ function reducer(state, action) {
2965
3108
  case "toggleMonitor": {
2966
3109
  return { ...state, monitorOpen: !state.monitorOpen };
2967
3110
  }
3111
+ case "toggleAgentsMonitor": {
3112
+ return { ...state, agentsMonitorOpen: !state.agentsMonitorOpen };
3113
+ }
2968
3114
  case "checkpointReceived": {
2969
3115
  const existing = state.checkpoints.find((c) => c.promptIndex === action.cp.promptIndex);
2970
3116
  if (existing) return state;
@@ -3125,6 +3271,7 @@ function App({
3125
3271
  buffer: "",
3126
3272
  cursor: 0,
3127
3273
  placeholders: [],
3274
+ placeholderContents: [],
3128
3275
  streamingText: "",
3129
3276
  toolStream: null,
3130
3277
  status: "idle",
@@ -3155,6 +3302,7 @@ function App({
3155
3302
  fleetTokens: { input: 0, output: 0 },
3156
3303
  streamFleet: true,
3157
3304
  monitorOpen: false,
3305
+ agentsMonitorOpen: false,
3158
3306
  checkpoints: [],
3159
3307
  rewindOverlay: null,
3160
3308
  eternalStage: null,
@@ -3196,6 +3344,10 @@ function App({
3196
3344
  draftRef.current = { buffer: "", cursor: 0 };
3197
3345
  dispatch({ type: "clearInput" });
3198
3346
  };
3347
+ const clearPlaceholdersOnly = () => {
3348
+ draftRef.current = { buffer: "", cursor: 0 };
3349
+ dispatch({ type: "clearPlaceholdersOnly" });
3350
+ };
3199
3351
  const startedAtRef = useRef(Date.now());
3200
3352
  const [nowTick, setNowTick] = React2.useState(Date.now());
3201
3353
  useEffect(() => {
@@ -3634,6 +3786,20 @@ function App({
3634
3786
  slashRegistry.unregister("rewind");
3635
3787
  };
3636
3788
  }, [slashRegistry, handleRewindTo]);
3789
+ useEffect(() => {
3790
+ const cmd = {
3791
+ name: "agents",
3792
+ description: "Toggle the agents monitor overlay.",
3793
+ async run() {
3794
+ dispatch({ type: "toggleAgentsMonitor" });
3795
+ return { message: void 0 };
3796
+ }
3797
+ };
3798
+ slashRegistry.register(cmd);
3799
+ return () => {
3800
+ slashRegistry.unregister("agents");
3801
+ };
3802
+ }, [slashRegistry]);
3637
3803
  useEffect(() => {
3638
3804
  if (!getPickableProviders || !switchProviderAndModel) return;
3639
3805
  const cmd = {
@@ -3651,6 +3817,23 @@ function App({
3651
3817
  slashRegistry.unregister("model");
3652
3818
  };
3653
3819
  }, [slashRegistry, getPickableProviders, switchProviderAndModel]);
3820
+ useEffect(() => {
3821
+ const cmd = {
3822
+ name: "agents",
3823
+ description: "Open or close the agents monitor overlay.",
3824
+ async run(args) {
3825
+ if (args.trim().toLowerCase() === "monitor") {
3826
+ dispatch({ type: "toggleAgentsMonitor" });
3827
+ return { message: "Agents monitor toggled." };
3828
+ }
3829
+ return { message: "Usage: /agents monitor" };
3830
+ }
3831
+ };
3832
+ slashRegistry.register(cmd);
3833
+ return () => {
3834
+ slashRegistry.unregister("agents");
3835
+ };
3836
+ }, [slashRegistry]);
3654
3837
  useEffect(() => {
3655
3838
  if (!switchAutonomy) return;
3656
3839
  const cmd = {
@@ -4307,13 +4490,25 @@ function App({
4307
4490
  process.off("SIGINT", onSigint);
4308
4491
  };
4309
4492
  }, [director, getEternalEngine, getParallelEngine, switchAutonomy, onExit, exit]);
4493
+ const truncatePastePreview = (text, lines) => {
4494
+ const all = text.split("\n");
4495
+ if (all.length <= lines) return text;
4496
+ const head = all.slice(0, lines).join("\n");
4497
+ return `${head}
4498
+ ... (${all.length - lines} more lines)`;
4499
+ };
4310
4500
  const commitPaste = async (full) => {
4311
4501
  const builder = builderRef.current;
4312
4502
  if (!builder || !full) return;
4313
4503
  if (builder.wouldCollapse(full) || full.includes("\n")) {
4314
4504
  const lineCount = full.split("\n").length;
4315
4505
  const ph = await builder.appendPaste(full);
4316
- dispatch({ type: "addPlaceholder", ph: `${ph ?? "[pasted]"} (${lineCount} lines)` });
4506
+ const preview = truncatePastePreview(full, 6);
4507
+ dispatch({
4508
+ type: "addPlaceholder",
4509
+ ph: `${ph ?? "[pasted]"} (${lineCount} lines)`,
4510
+ content: preview
4511
+ });
4317
4512
  return;
4318
4513
  }
4319
4514
  const { buffer, cursor } = draftRef.current;
@@ -4534,10 +4729,18 @@ function App({
4534
4729
  dispatch({ type: "toggleMonitor" });
4535
4730
  return;
4536
4731
  }
4732
+ if (key.ctrl && key.shift && input === "M") {
4733
+ dispatch({ type: "toggleAgentsMonitor" });
4734
+ return;
4735
+ }
4537
4736
  if (key.escape && state.monitorOpen) {
4538
4737
  dispatch({ type: "toggleMonitor" });
4539
4738
  return;
4540
4739
  }
4740
+ if (key.escape && state.agentsMonitorOpen) {
4741
+ dispatch({ type: "toggleAgentsMonitor" });
4742
+ return;
4743
+ }
4541
4744
  if (isEnter) {
4542
4745
  const now = Date.now();
4543
4746
  if (now - lastEnterAtRef.current < 50) return;
@@ -4929,18 +5132,26 @@ User message:
4929
5132
  }
4930
5133
  if (steering) dispatch({ type: "steerConsume" });
4931
5134
  const displayText = trimmed ? steering ? `\u21AF ${trimmed}` : trimmed : "(attachments only)";
5135
+ const pasteParts = [];
5136
+ for (let i = 0; i < state.placeholders.length; i++) {
5137
+ const label = state.placeholders[i];
5138
+ const content = state.placeholderContents[i] ?? "";
5139
+ pasteParts.push(label);
5140
+ if (content) pasteParts.push(` ${content.split("\n").slice(0, 6).join("\n ")}`);
5141
+ }
5142
+ const pasteContent = pasteParts.length > 0 ? pasteParts.join("\n") : void 0;
4932
5143
  pushSubmittedHistory();
4933
- clearDraft();
5144
+ clearPlaceholdersOnly();
4934
5145
  const blocks = await builder.submit();
4935
5146
  if (state.status !== "idle") {
4936
5147
  dispatch({
4937
5148
  type: "addEntry",
4938
- entry: { kind: "user", text: displayText, queued: true }
5149
+ entry: { kind: "user", text: displayText, queued: true, pasteContent }
4939
5150
  });
4940
5151
  dispatch({ type: "enqueue", item: { displayText, blocks } });
4941
5152
  return;
4942
5153
  }
4943
- dispatch({ type: "addEntry", entry: { kind: "user", text: displayText } });
5154
+ dispatch({ type: "addEntry", entry: { kind: "user", text: displayText, pasteContent } });
4944
5155
  await runBlocks(blocks);
4945
5156
  };
4946
5157
  const bootInjectedRef = useRef(false);
@@ -5089,6 +5300,15 @@ User message:
5089
5300
  goalSummary: state.goalSummary
5090
5301
  }
5091
5302
  ),
5303
+ state.agentsMonitorOpen ? /* @__PURE__ */ jsx(
5304
+ AgentsMonitor,
5305
+ {
5306
+ entries: state.fleet,
5307
+ totalCost: state.fleetCost,
5308
+ totalTokens: state.fleetTokens,
5309
+ nowTick
5310
+ }
5311
+ ) : null,
5092
5312
  state.monitorOpen ? /* @__PURE__ */ jsx(
5093
5313
  FleetMonitor,
5094
5314
  {