@wrongstack/tui 0.2.0 → 0.3.1

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
@@ -233,6 +233,27 @@ function fmtCount(n) {
233
233
  if (n === 0) return "\u2014";
234
234
  return String(n);
235
235
  }
236
+ function fmtDuration(ms) {
237
+ if (ms < 1e3) return `${ms}ms`;
238
+ return `${(ms / 1e3).toFixed(1)}s`;
239
+ }
240
+ function fmtBytes(n) {
241
+ if (n < 1024) return `${n}B`;
242
+ return `${(n / 1024).toFixed(1)}KB`;
243
+ }
244
+ function fmtRecentTool(tool) {
245
+ const status = tool.ok === false ? "fail" : "ok";
246
+ const name = tool.name.length > 24 ? `${tool.name.slice(0, 23)}...` : tool.name;
247
+ const parts = [status, name];
248
+ if (typeof tool.durationMs === "number") parts.push(fmtDuration(tool.durationMs));
249
+ if (typeof tool.outputBytes === "number" && tool.outputBytes > 0) parts.push(fmtBytes(tool.outputBytes));
250
+ if (typeof tool.outputLines === "number" && tool.outputLines > 0) parts.push(`${tool.outputLines}L`);
251
+ return parts.join(" ");
252
+ }
253
+ function fmtRecentMessage(message) {
254
+ const text = message.text.replace(/\s+/g, " ");
255
+ return text.length > 80 ? `${text.slice(0, 79)}...` : text;
256
+ }
236
257
  function fmtModel(provider, model) {
237
258
  if (!provider && !model) return "";
238
259
  const p = provider ?? "";
@@ -282,6 +303,8 @@ function FleetPanel({ entries, totalCost, roster }) {
282
303
  const si = STATUS_ICON[entry.status];
283
304
  const modelTag = fmtModel(entry.provider, entry.model);
284
305
  const name = resolveName(entry, roster);
306
+ const recentTools = (entry.recentTools ?? []).slice(-2).map(fmtRecentTool).join(" | ");
307
+ const recentMessages = (entry.recentMessages ?? []).slice(-2).map(fmtRecentMessage);
285
308
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
286
309
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
287
310
  /* @__PURE__ */ jsx(Text, { color: si.color, children: si.icon }),
@@ -313,6 +336,14 @@ function FleetPanel({ entries, totalCost, roster }) {
313
336
  "ms)"
314
337
  ] })
315
338
  ] }) : null,
339
+ recentTools ? /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
340
+ "tools: ",
341
+ recentTools
342
+ ] }) }) : null,
343
+ recentMessages.map((message, index) => /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
344
+ "msg: ",
345
+ message
346
+ ] }) }, `${entry.id}-msg-${index}-${message}`)),
316
347
  entry.status === "running" && entry.streamingText ? /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
317
348
  ">",
318
349
  " ",
@@ -335,6 +366,23 @@ function fmtElapsed(ms) {
335
366
  const s = Math.floor(ms % 6e4 / 1e3);
336
367
  return `${m}m${s.toString().padStart(2, "0")}s`;
337
368
  }
369
+ function fmtBytes2(n) {
370
+ if (n < 1024) return `${n}B`;
371
+ return `${(n / 1024).toFixed(1)}KB`;
372
+ }
373
+ function fmtRecentTool2(tool) {
374
+ const status = tool.ok === false ? "fail" : "ok";
375
+ const name = tool.name.length > 18 ? `${tool.name.slice(0, 17)}...` : tool.name;
376
+ const parts = [status, name];
377
+ if (typeof tool.durationMs === "number") parts.push(fmtElapsed(tool.durationMs));
378
+ if (typeof tool.outputBytes === "number" && tool.outputBytes > 0) parts.push(fmtBytes2(tool.outputBytes));
379
+ if (typeof tool.outputLines === "number" && tool.outputLines > 0) parts.push(`${tool.outputLines}L`);
380
+ return parts.join(" ");
381
+ }
382
+ function fmtRecentMessage2(message) {
383
+ const text = message.text.replace(/\s+/g, " ");
384
+ return text.length > 48 ? `${text.slice(0, 47)}...` : text;
385
+ }
338
386
  function LiveActivityStrip({
339
387
  entries,
340
388
  nowTick,
@@ -348,6 +396,8 @@ function LiveActivityStrip({
348
396
  const toolElapsed = e.currentTool ? now - e.currentTool.startedAt : 0;
349
397
  const taskElapsed = now - e.startedAt;
350
398
  const toolSeg = e.currentTool ? `\u2192 ${e.currentTool.name} (${fmtElapsed(toolElapsed)})` : "idle between tools";
399
+ const recentTools = (e.recentTools ?? []).slice(-2).map(fmtRecentTool2).join(" | ");
400
+ const messageText = e.streamingText.trim() || (e.recentMessages ?? []).slice(-1).map(fmtRecentMessage2).join("");
351
401
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
352
402
  /* @__PURE__ */ jsx(Text, { color: "cyan", children: "\u25CF" }),
353
403
  /* @__PURE__ */ jsx(Text, { children: e.name.slice(0, 14).padEnd(14) }),
@@ -360,7 +410,21 @@ function LiveActivityStrip({
360
410
  e.toolCalls,
361
411
  "tc \xB7 ",
362
412
  fmtElapsed(taskElapsed)
363
- ] })
413
+ ] }),
414
+ recentTools ? /* @__PURE__ */ jsxs(Fragment, { children: [
415
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "|" }),
416
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
417
+ "last: ",
418
+ recentTools
419
+ ] })
420
+ ] }) : null,
421
+ messageText ? /* @__PURE__ */ jsxs(Fragment, { children: [
422
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: "|" }),
423
+ /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
424
+ "msg: ",
425
+ fmtRecentMessage2({ text: messageText})
426
+ ] })
427
+ ] }) : null
364
428
  ] }, e.id);
365
429
  }),
366
430
  Object.values(entries).filter((e) => e.status === "running").length > maxRows ? /* @__PURE__ */ jsx(Box, { paddingLeft: 2, children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
@@ -594,7 +658,7 @@ function ToolStreamBox({
594
658
  /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
595
659
  /* @__PURE__ */ jsx(Text, { color: "yellow", children: "\u25C6 " }),
596
660
  /* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: name }),
597
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u23F1 ${fmtDuration(elapsedMs)}` }),
661
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \u23F1 ${fmtDuration2(elapsedMs)}` }),
598
662
  hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` (${totalLines} lines, showing last ${MAX_STREAM_LINES})` }) : null
599
663
  ] }),
600
664
  /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [
@@ -617,6 +681,15 @@ function tailForDisplay(text, maxChars) {
617
681
  return `\u2026 ${text.slice(cut)}`;
618
682
  }
619
683
  function DiffBlock({ rows, hidden }) {
684
+ let gutterWidth = 1;
685
+ for (const r of rows) {
686
+ const n = r.kind === "del" ? r.oldLine : r.newLine;
687
+ if (typeof n === "number") {
688
+ const w = String(n).length;
689
+ if (w > gutterWidth) gutterWidth = w;
690
+ }
691
+ }
692
+ const blank = " ".repeat(gutterWidth);
620
693
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 4, marginTop: 0, children: [
621
694
  rows.map((row, i) => {
622
695
  const key = i;
@@ -624,16 +697,20 @@ function DiffBlock({ rows, hidden }) {
624
697
  return /* @__PURE__ */ jsx(Text, { color: "cyan", dimColor: true, children: row.text }, key);
625
698
  }
626
699
  if (row.kind === "meta") {
627
- return /* @__PURE__ */ jsx(Text, { dimColor: true, children: row.text }, key);
700
+ return /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${blank} ${row.text}` }, key);
628
701
  }
702
+ const lnNumber = row.kind === "del" ? row.oldLine : row.newLine;
703
+ const lnText = typeof lnNumber === "number" ? String(lnNumber).padStart(gutterWidth, " ") : blank;
629
704
  if (row.kind === "ctx") {
630
- return /* @__PURE__ */ jsx(Text, { dimColor: true, children: row.text }, key);
705
+ return /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${lnText} ${row.text}` }, key);
631
706
  }
632
- const bg = row.kind === "add" ? "green" : "red";
633
- const fg = row.kind === "add" ? "black" : "white";
634
- return /* @__PURE__ */ jsx(Text, { backgroundColor: bg, color: fg, children: row.text }, key);
707
+ const bg = row.kind === "add" ? "greenBright" : "redBright";
708
+ return /* @__PURE__ */ jsxs(Text, { children: [
709
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: `${lnText} ` }),
710
+ /* @__PURE__ */ jsx(Text, { backgroundColor: bg, color: "black", children: row.text })
711
+ ] }, key);
635
712
  }),
636
- hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: ` \u2026 ${hidden} more line${hidden === 1 ? "" : "s"}` }) : null
713
+ hidden > 0 ? /* @__PURE__ */ jsx(Text, { dimColor: true, italic: true, children: `${blank} \u2026 ${hidden} more line${hidden === 1 ? "" : "s"}` }) : null
637
714
  ] });
638
715
  }
639
716
  function Entry({
@@ -667,7 +744,7 @@ function Entry({
667
744
  parts.push(`${entry.outputLines} L`);
668
745
  }
669
746
  if (entry.outputBytes && entry.outputBytes > 0) {
670
- parts.push(fmtBytes(entry.outputBytes));
747
+ parts.push(fmtBytes3(entry.outputBytes));
671
748
  }
672
749
  if (entry.outputTokens && entry.outputTokens > 0) {
673
750
  parts.push(`\u2248${fmtTok(entry.outputTokens)} tok`);
@@ -683,7 +760,7 @@ function Entry({
683
760
  /* @__PURE__ */ jsx(Text, { children: " " }),
684
761
  /* @__PURE__ */ jsx(Text, { dimColor: true, children: argSummary })
685
762
  ] }) : null,
686
- /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \xB7 ${fmtDuration(entry.durationMs)}` }),
763
+ /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \xB7 ${fmtDuration2(entry.durationMs)}` }),
687
764
  sizeChip ? /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` \xB7 ${sizeChip}` }) : null
688
765
  ] }),
689
766
  outLines.map((line, i) => (
@@ -823,7 +900,7 @@ function fmtTok(n) {
823
900
  if (n >= 1e3) return `${(n / 1e3).toFixed(n >= 1e4 ? 0 : 1)}k`;
824
901
  return String(n);
825
902
  }
826
- function fmtDuration(ms) {
903
+ function fmtDuration2(ms) {
827
904
  if (ms < 1e3) return `${ms}ms`;
828
905
  if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
829
906
  const totalSec = Math.floor(ms / 1e3);
@@ -951,7 +1028,7 @@ function formatToolOutput(toolName, output, ok, _outputBytes, outputLines) {
951
1028
  const bytes = numOf(o["bytes_written"]) ?? numOf(o["bytes"]);
952
1029
  const created = o["created"] === true;
953
1030
  const tag = created ? "created" : "updated";
954
- if (bytes !== void 0) return [`${tag} \xB7 ${fmtBytes(bytes)}`];
1031
+ if (bytes !== void 0) return [`${tag} \xB7 ${fmtBytes3(bytes)}`];
955
1032
  return [tag];
956
1033
  }
957
1034
  }
@@ -1016,17 +1093,17 @@ function formatToolOutput(toolName, output, ok, _outputBytes, outputLines) {
1016
1093
  if (json && typeof json === "object") {
1017
1094
  const o = json;
1018
1095
  const bytes = numOf(o["bytes"]);
1019
- if (bytes !== void 0) return [`${fmtBytes(bytes)} read`];
1096
+ if (bytes !== void 0) return [`${fmtBytes3(bytes)} read`];
1020
1097
  }
1021
1098
  const range = scanNumberedRange(text);
1022
1099
  if (range.count > 0 && range.first !== void 0 && range.last !== void 0) {
1023
1100
  if (range.first === range.last) {
1024
- return [`L${range.first} \xB7 ${fmtBytes(text.length)}`];
1101
+ return [`L${range.first} \xB7 ${fmtBytes3(text.length)}`];
1025
1102
  }
1026
1103
  const contiguous = range.count === range.last - range.first + 1;
1027
1104
  const head = `L${range.first}\u2013${range.last}`;
1028
1105
  const tail = contiguous ? `${range.count} line${range.count === 1 ? "" : "s"}` : `${range.count} lines (gaps)`;
1029
- return [`${head} \xB7 ${tail} \xB7 ${fmtBytes(text.length)}`];
1106
+ return [`${head} \xB7 ${tail} \xB7 ${fmtBytes3(text.length)}`];
1030
1107
  }
1031
1108
  }
1032
1109
  if (toolName === "grep" || toolName === "glob") {
@@ -1084,7 +1161,7 @@ function formatToolOutput(toolName, output, ok, _outputBytes, outputLines) {
1084
1161
  const head = [];
1085
1162
  if (status !== void 0) head.push(`HTTP ${status}`);
1086
1163
  if (ct) head.push(ct.split(";")[0] ?? ct);
1087
- if (content) head.push(fmtBytes(Buffer.byteLength(content, "utf8")));
1164
+ if (content) head.push(fmtBytes3(Buffer.byteLength(content, "utf8")));
1088
1165
  const lines = [];
1089
1166
  if (head.length > 0) lines.push(head.join(" \xB7 "));
1090
1167
  if (url && status !== void 0 && (status < 200 || status >= 400)) {
@@ -1175,7 +1252,7 @@ function formatToolOutput(toolName, output, ok, _outputBytes, outputLines) {
1175
1252
  if (runner && runner !== "none") head.push(runner);
1176
1253
  head.push(`${passed}/${total} passed`);
1177
1254
  if (failed > 0) head.push(`${failed} failed`);
1178
- if (duration !== void 0) head.push(fmtDuration(duration));
1255
+ if (duration !== void 0) head.push(fmtDuration2(duration));
1179
1256
  return [head.join(" \xB7 ")];
1180
1257
  }
1181
1258
  }
@@ -1386,20 +1463,29 @@ function extractDiffPreview(toolName, output) {
1386
1463
  }
1387
1464
  function parseUnifiedDiff(diff, maxLines) {
1388
1465
  const all = [];
1466
+ let oldLn = 0;
1467
+ let newLn = 0;
1389
1468
  for (const raw of diff.split("\n")) {
1390
1469
  const line = raw.replace(/\r$/, "");
1391
1470
  if (line.startsWith("+++") || line.startsWith("---")) continue;
1392
1471
  if (line.startsWith("diff --git") || line.startsWith("index ")) continue;
1393
1472
  if (line.startsWith("@@")) {
1473
+ const m = line.match(/^@@\s+-(\d+)(?:,\d+)?\s+\+(\d+)(?:,\d+)?\s+@@/);
1474
+ if (m) {
1475
+ oldLn = Number.parseInt(m[1] ?? "0", 10) || 0;
1476
+ newLn = Number.parseInt(m[2] ?? "0", 10) || 0;
1477
+ }
1394
1478
  all.push({ kind: "hunk", text: truncMid(line, 60) });
1395
1479
  continue;
1396
1480
  }
1397
1481
  if (line.startsWith("+")) {
1398
- all.push({ kind: "add", text: truncMid(line, 100) });
1482
+ all.push({ kind: "add", text: truncMid(line, 100), newLine: newLn });
1483
+ newLn++;
1399
1484
  continue;
1400
1485
  }
1401
1486
  if (line.startsWith("-")) {
1402
- all.push({ kind: "del", text: truncMid(line, 100) });
1487
+ all.push({ kind: "del", text: truncMid(line, 100), oldLine: oldLn });
1488
+ oldLn++;
1403
1489
  continue;
1404
1490
  }
1405
1491
  if (line.startsWith("\\ No newline")) {
@@ -1407,7 +1493,9 @@ function parseUnifiedDiff(diff, maxLines) {
1407
1493
  continue;
1408
1494
  }
1409
1495
  if (line.length === 0) continue;
1410
- all.push({ kind: "ctx", text: truncMid(line, 100) });
1496
+ all.push({ kind: "ctx", text: truncMid(line, 100), oldLine: oldLn, newLine: newLn });
1497
+ oldLn++;
1498
+ newLn++;
1411
1499
  }
1412
1500
  if (all.length === 0) return { rows: [], hidden: 0 };
1413
1501
  if (all.length <= maxLines) return { rows: all, hidden: 0 };
@@ -1449,7 +1537,7 @@ function countLines(text) {
1449
1537
  if (!text) return 0;
1450
1538
  return text.replace(/\n$/, "").split("\n").length;
1451
1539
  }
1452
- function fmtBytes(n) {
1540
+ function fmtBytes3(n) {
1453
1541
  if (n < 1024) return `${n}B`;
1454
1542
  if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)}KB`;
1455
1543
  return `${(n / (1024 * 1024)).toFixed(1)}MB`;
@@ -2308,7 +2396,13 @@ function reducer(state, action) {
2308
2396
  // --- Fleet ---
2309
2397
  case "fleetSeed": {
2310
2398
  const seeded = {};
2311
- for (const e of action.entries) seeded[e.id] = e;
2399
+ for (const e of action.entries) {
2400
+ seeded[e.id] = {
2401
+ ...e,
2402
+ recentTools: e.recentTools ?? [],
2403
+ recentMessages: e.recentMessages ?? []
2404
+ };
2405
+ }
2312
2406
  return { ...state, fleet: seeded, fleetCost: action.cost };
2313
2407
  }
2314
2408
  case "fleetSpawn": {
@@ -2322,6 +2416,8 @@ function reducer(state, action) {
2322
2416
  streamingText: "",
2323
2417
  iterations: 0,
2324
2418
  toolCalls: 0,
2419
+ recentTools: [],
2420
+ recentMessages: [],
2325
2421
  cost: 0,
2326
2422
  startedAt: Date.now(),
2327
2423
  lastEventAt: Date.now(),
@@ -2383,14 +2479,45 @@ function reducer(state, action) {
2383
2479
  }
2384
2480
  };
2385
2481
  }
2482
+ case "fleetMessage": {
2483
+ const cur = state.fleet[action.id];
2484
+ const text = action.text.trim().replace(/\s+/g, " ");
2485
+ if (!cur || !text) return state;
2486
+ const now = Date.now();
2487
+ const recentMessages = [...cur.recentMessages ?? [], { text, at: now }].slice(-2);
2488
+ return {
2489
+ ...state,
2490
+ fleet: {
2491
+ ...state.fleet,
2492
+ [action.id]: { ...cur, recentMessages, lastEventAt: now }
2493
+ }
2494
+ };
2495
+ }
2386
2496
  case "fleetTool": {
2387
2497
  const cur = state.fleet[action.id];
2388
2498
  if (!cur) return state;
2499
+ const now = Date.now();
2500
+ const recentTools = action.name !== void 0 ? [
2501
+ ...cur.recentTools ?? [],
2502
+ {
2503
+ name: action.name,
2504
+ ok: action.ok,
2505
+ durationMs: action.durationMs,
2506
+ outputBytes: action.outputBytes,
2507
+ outputLines: action.outputLines,
2508
+ at: now
2509
+ }
2510
+ ].slice(-2) : cur.recentTools ?? [];
2389
2511
  return {
2390
2512
  ...state,
2391
2513
  fleet: {
2392
2514
  ...state.fleet,
2393
- [action.id]: { ...cur, toolCalls: cur.toolCalls + 1, lastEventAt: Date.now() }
2515
+ [action.id]: {
2516
+ ...cur,
2517
+ toolCalls: cur.toolCalls + 1,
2518
+ recentTools,
2519
+ lastEventAt: now
2520
+ }
2394
2521
  }
2395
2522
  };
2396
2523
  }
@@ -2416,6 +2543,7 @@ function reducer(state, action) {
2416
2543
  iterations: action.iterations,
2417
2544
  toolCalls: action.toolCalls,
2418
2545
  streamingText: "",
2546
+ currentTool: void 0,
2419
2547
  lastEventAt: Date.now()
2420
2548
  }
2421
2549
  }
@@ -3255,21 +3383,16 @@ function App({
3255
3383
  });
3256
3384
  });
3257
3385
  const offTool = events.on("subagent.tool_executed", (e) => {
3258
- const lbl = labelFor(e.subagentId);
3259
- dispatch({ type: "fleetTool", id: e.subagentId });
3260
- dispatch({ type: "fleetToolEnd", id: e.subagentId });
3261
- const bytesTag = typeof e.outputBytes === "number" && e.outputBytes > 0 ? ` \xB7 ${e.outputBytes < 1024 ? `${e.outputBytes}B` : `${(e.outputBytes / 1024).toFixed(1)}KB`}` : "";
3386
+ if (director) return;
3262
3387
  dispatch({
3263
- type: "addEntry",
3264
- entry: {
3265
- kind: "subagent",
3266
- agentLabel: lbl.label,
3267
- agentColor: lbl.color,
3268
- icon: e.ok === false ? "\u2717" : "\u25CF",
3269
- text: e.name,
3270
- detail: `${e.durationMs}ms${bytesTag}`
3271
- }
3388
+ type: "fleetTool",
3389
+ id: e.subagentId,
3390
+ name: e.name,
3391
+ ok: e.ok,
3392
+ durationMs: e.durationMs,
3393
+ outputBytes: e.outputBytes
3272
3394
  });
3395
+ dispatch({ type: "fleetToolEnd", id: e.subagentId });
3273
3396
  });
3274
3397
  return () => {
3275
3398
  offSpawned();
@@ -3277,7 +3400,7 @@ function App({
3277
3400
  offCompleted();
3278
3401
  offTool();
3279
3402
  };
3280
- }, [events]);
3403
+ }, [events, director]);
3281
3404
  useEffect(() => {
3282
3405
  if (!fleetStreamController) return;
3283
3406
  fleetStreamController.enabled = state.streamFleet;
@@ -3301,18 +3424,22 @@ function App({
3301
3424
  let streamFlushTimer = null;
3302
3425
  const flushStreamBufs = () => {
3303
3426
  for (const [id, text] of streamBuf) {
3304
- if (!text.trim()) continue;
3427
+ const trimmed = text.trim();
3428
+ if (!trimmed) continue;
3305
3429
  const lbl = labelFor(id);
3306
- dispatch({
3307
- type: "addEntry",
3308
- entry: {
3309
- kind: "subagent",
3310
- agentLabel: lbl.label,
3311
- agentColor: lbl.color,
3312
- icon: "\u{1F4AC}",
3313
- text: text.trim()
3314
- }
3315
- });
3430
+ dispatch({ type: "fleetMessage", id, text: trimmed });
3431
+ if (streamFleetRef.current) {
3432
+ dispatch({
3433
+ type: "addEntry",
3434
+ entry: {
3435
+ kind: "subagent",
3436
+ agentLabel: lbl.label,
3437
+ agentColor: lbl.color,
3438
+ icon: "\u{1F4AC}",
3439
+ text: trimmed
3440
+ }
3441
+ });
3442
+ }
3316
3443
  }
3317
3444
  streamBuf.clear();
3318
3445
  streamFlushTimer = null;
@@ -3377,10 +3504,9 @@ function App({
3377
3504
  const cur = pending.get(e.subagentId) ?? "";
3378
3505
  pending.set(e.subagentId, cur + p.text);
3379
3506
  if (!flushTimer) flushTimer = setTimeout(doFlush, FLUSH_MS);
3380
- if (streamFleetRef.current) {
3381
- streamBuf.set(e.subagentId, (streamBuf.get(e.subagentId) ?? "") + p.text);
3382
- if (!streamFlushTimer) streamFlushTimer = setTimeout(flushStreamBufs, FLUSH_MS * 4);
3383
- }
3507
+ streamBuf.set(e.subagentId, (streamBuf.get(e.subagentId) ?? "") + p.text);
3508
+ if (streamFlushTimer) clearTimeout(streamFlushTimer);
3509
+ streamFlushTimer = setTimeout(flushStreamBufs, FLUSH_MS * 4);
3384
3510
  }
3385
3511
  break;
3386
3512
  }
@@ -3392,28 +3518,17 @@ function App({
3392
3518
  break;
3393
3519
  }
3394
3520
  case "tool.executed": {
3395
- dispatch({ type: "fleetTool", id: e.subagentId });
3521
+ const p = e.payload;
3522
+ dispatch({
3523
+ type: "fleetTool",
3524
+ id: e.subagentId,
3525
+ name: p?.name,
3526
+ ok: p?.ok,
3527
+ durationMs: p?.durationMs,
3528
+ outputBytes: p?.outputBytes,
3529
+ outputLines: p?.outputLines
3530
+ });
3396
3531
  dispatch({ type: "fleetToolEnd", id: e.subagentId });
3397
- if (streamFleetRef.current) {
3398
- if (streamFlushTimer) {
3399
- clearTimeout(streamFlushTimer);
3400
- flushStreamBufs();
3401
- }
3402
- const p = e.payload;
3403
- const args = p?.input ? formatToolArgs(p.name ?? "", p.input) : "";
3404
- const lbl = labelFor(e.subagentId);
3405
- dispatch({
3406
- type: "addEntry",
3407
- entry: {
3408
- kind: "subagent",
3409
- agentLabel: lbl.label,
3410
- agentColor: lbl.color,
3411
- icon: p?.ok === false ? "\u2717" : "\u25CF",
3412
- text: args ? `${p?.name ?? "tool"} ${args}` : p?.name ?? "tool",
3413
- detail: typeof p?.durationMs === "number" ? `${p.durationMs}ms` : void 0
3414
- }
3415
- });
3416
- }
3417
3532
  break;
3418
3533
  }
3419
3534
  case "provider.response": {
@@ -3431,7 +3546,7 @@ function App({
3431
3546
  toolCalls: payload.result.toolCalls
3432
3547
  });
3433
3548
  dispatch({ type: "fleetCost", cost: d.snapshot().total.cost });
3434
- if (streamFleetRef.current && streamFlushTimer) {
3549
+ if (streamFlushTimer) {
3435
3550
  clearTimeout(streamFlushTimer);
3436
3551
  flushStreamBufs();
3437
3552
  }