@wrongstack/tui 0.7.2 → 0.7.3

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 {
@@ -2965,6 +3109,9 @@ function reducer(state, action) {
2965
3109
  case "toggleMonitor": {
2966
3110
  return { ...state, monitorOpen: !state.monitorOpen };
2967
3111
  }
3112
+ case "toggleAgentsMonitor": {
3113
+ return { ...state, agentsMonitorOpen: !state.agentsMonitorOpen };
3114
+ }
2968
3115
  case "checkpointReceived": {
2969
3116
  const existing = state.checkpoints.find((c) => c.promptIndex === action.cp.promptIndex);
2970
3117
  if (existing) return state;
@@ -3125,6 +3272,7 @@ function App({
3125
3272
  buffer: "",
3126
3273
  cursor: 0,
3127
3274
  placeholders: [],
3275
+ placeholderContents: [],
3128
3276
  streamingText: "",
3129
3277
  toolStream: null,
3130
3278
  status: "idle",
@@ -3155,6 +3303,7 @@ function App({
3155
3303
  fleetTokens: { input: 0, output: 0 },
3156
3304
  streamFleet: true,
3157
3305
  monitorOpen: false,
3306
+ agentsMonitorOpen: false,
3158
3307
  checkpoints: [],
3159
3308
  rewindOverlay: null,
3160
3309
  eternalStage: null,
@@ -3196,6 +3345,10 @@ function App({
3196
3345
  draftRef.current = { buffer: "", cursor: 0 };
3197
3346
  dispatch({ type: "clearInput" });
3198
3347
  };
3348
+ const clearPlaceholdersOnly = () => {
3349
+ draftRef.current = { buffer: "", cursor: 0 };
3350
+ dispatch({ type: "clearPlaceholdersOnly" });
3351
+ };
3199
3352
  const startedAtRef = useRef(Date.now());
3200
3353
  const [nowTick, setNowTick] = React2.useState(Date.now());
3201
3354
  useEffect(() => {
@@ -3634,6 +3787,20 @@ function App({
3634
3787
  slashRegistry.unregister("rewind");
3635
3788
  };
3636
3789
  }, [slashRegistry, handleRewindTo]);
3790
+ useEffect(() => {
3791
+ const cmd = {
3792
+ name: "agents",
3793
+ description: "Toggle the agents monitor overlay.",
3794
+ async run() {
3795
+ dispatch({ type: "toggleAgentsMonitor" });
3796
+ return { message: void 0 };
3797
+ }
3798
+ };
3799
+ slashRegistry.register(cmd);
3800
+ return () => {
3801
+ slashRegistry.unregister("agents");
3802
+ };
3803
+ }, [slashRegistry]);
3637
3804
  useEffect(() => {
3638
3805
  if (!getPickableProviders || !switchProviderAndModel) return;
3639
3806
  const cmd = {
@@ -3651,6 +3818,23 @@ function App({
3651
3818
  slashRegistry.unregister("model");
3652
3819
  };
3653
3820
  }, [slashRegistry, getPickableProviders, switchProviderAndModel]);
3821
+ useEffect(() => {
3822
+ const cmd = {
3823
+ name: "agents",
3824
+ description: "Open or close the agents monitor overlay.",
3825
+ async run(args) {
3826
+ if (args.trim().toLowerCase() === "monitor") {
3827
+ dispatch({ type: "toggleAgentsMonitor" });
3828
+ return { message: "Agents monitor toggled." };
3829
+ }
3830
+ return { message: "Usage: /agents monitor" };
3831
+ }
3832
+ };
3833
+ slashRegistry.register(cmd);
3834
+ return () => {
3835
+ slashRegistry.unregister("agents");
3836
+ };
3837
+ }, [slashRegistry]);
3654
3838
  useEffect(() => {
3655
3839
  if (!switchAutonomy) return;
3656
3840
  const cmd = {
@@ -4307,13 +4491,25 @@ function App({
4307
4491
  process.off("SIGINT", onSigint);
4308
4492
  };
4309
4493
  }, [director, getEternalEngine, getParallelEngine, switchAutonomy, onExit, exit]);
4494
+ const truncatePastePreview = (text, lines) => {
4495
+ const all = text.split("\n");
4496
+ if (all.length <= lines) return text;
4497
+ const head = all.slice(0, lines).join("\n");
4498
+ return `${head}
4499
+ ... (${all.length - lines} more lines)`;
4500
+ };
4310
4501
  const commitPaste = async (full) => {
4311
4502
  const builder = builderRef.current;
4312
4503
  if (!builder || !full) return;
4313
4504
  if (builder.wouldCollapse(full) || full.includes("\n")) {
4314
4505
  const lineCount = full.split("\n").length;
4315
4506
  const ph = await builder.appendPaste(full);
4316
- dispatch({ type: "addPlaceholder", ph: `${ph ?? "[pasted]"} (${lineCount} lines)` });
4507
+ const preview = truncatePastePreview(full, 6);
4508
+ dispatch({
4509
+ type: "addPlaceholder",
4510
+ ph: `${ph ?? "[pasted]"} (${lineCount} lines)`,
4511
+ content: preview
4512
+ });
4317
4513
  return;
4318
4514
  }
4319
4515
  const { buffer, cursor } = draftRef.current;
@@ -4534,10 +4730,18 @@ function App({
4534
4730
  dispatch({ type: "toggleMonitor" });
4535
4731
  return;
4536
4732
  }
4733
+ if (key.ctrl && key.shift && input === "M") {
4734
+ dispatch({ type: "toggleAgentsMonitor" });
4735
+ return;
4736
+ }
4537
4737
  if (key.escape && state.monitorOpen) {
4538
4738
  dispatch({ type: "toggleMonitor" });
4539
4739
  return;
4540
4740
  }
4741
+ if (key.escape && state.agentsMonitorOpen) {
4742
+ dispatch({ type: "toggleAgentsMonitor" });
4743
+ return;
4744
+ }
4541
4745
  if (isEnter) {
4542
4746
  const now = Date.now();
4543
4747
  if (now - lastEnterAtRef.current < 50) return;
@@ -4929,18 +5133,26 @@ User message:
4929
5133
  }
4930
5134
  if (steering) dispatch({ type: "steerConsume" });
4931
5135
  const displayText = trimmed ? steering ? `\u21AF ${trimmed}` : trimmed : "(attachments only)";
5136
+ const pasteParts = [];
5137
+ for (let i = 0; i < state.placeholders.length; i++) {
5138
+ const label = state.placeholders[i];
5139
+ const content = state.placeholderContents[i] ?? "";
5140
+ pasteParts.push(label);
5141
+ if (content) pasteParts.push(` ${content.split("\n").slice(0, 6).join("\n ")}`);
5142
+ }
5143
+ const pasteContent = pasteParts.length > 0 ? pasteParts.join("\n") : void 0;
4932
5144
  pushSubmittedHistory();
4933
- clearDraft();
5145
+ clearPlaceholdersOnly();
4934
5146
  const blocks = await builder.submit();
4935
5147
  if (state.status !== "idle") {
4936
5148
  dispatch({
4937
5149
  type: "addEntry",
4938
- entry: { kind: "user", text: displayText, queued: true }
5150
+ entry: { kind: "user", text: displayText, queued: true, pasteContent }
4939
5151
  });
4940
5152
  dispatch({ type: "enqueue", item: { displayText, blocks } });
4941
5153
  return;
4942
5154
  }
4943
- dispatch({ type: "addEntry", entry: { kind: "user", text: displayText } });
5155
+ dispatch({ type: "addEntry", entry: { kind: "user", text: displayText, pasteContent } });
4944
5156
  await runBlocks(blocks);
4945
5157
  };
4946
5158
  const bootInjectedRef = useRef(false);
@@ -5089,6 +5301,15 @@ User message:
5089
5301
  goalSummary: state.goalSummary
5090
5302
  }
5091
5303
  ),
5304
+ state.agentsMonitorOpen ? /* @__PURE__ */ jsx(
5305
+ AgentsMonitor,
5306
+ {
5307
+ entries: state.fleet,
5308
+ totalCost: state.fleetCost,
5309
+ totalTokens: state.fleetTokens,
5310
+ nowTick
5311
+ }
5312
+ ) : null,
5092
5313
  state.monitorOpen ? /* @__PURE__ */ jsx(
5093
5314
  FleetMonitor,
5094
5315
  {