@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 +189 -74
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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 ${
|
|
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" ? "
|
|
633
|
-
|
|
634
|
-
|
|
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:
|
|
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(
|
|
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 ${
|
|
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
|
|
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 ${
|
|
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 [`${
|
|
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 ${
|
|
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 ${
|
|
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(
|
|
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(
|
|
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
|
|
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)
|
|
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]: {
|
|
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
|
-
|
|
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: "
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
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
|
-
|
|
3427
|
+
const trimmed = text.trim();
|
|
3428
|
+
if (!trimmed) continue;
|
|
3305
3429
|
const lbl = labelFor(id);
|
|
3306
|
-
dispatch({
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
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
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
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
|
-
|
|
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 (
|
|
3549
|
+
if (streamFlushTimer) {
|
|
3435
3550
|
clearTimeout(streamFlushTimer);
|
|
3436
3551
|
flushStreamBufs();
|
|
3437
3552
|
}
|