pi-agent-flow 1.8.39 → 2.0.0
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/README.md +33 -37
- package/agents/audit.md +18 -22
- package/agents/build.md +20 -22
- package/agents/craft.md +20 -27
- package/agents/debug.md +21 -28
- package/agents/ideas.md +18 -101
- package/agents/scout.md +15 -19
- package/dist/batch/batch-bash.d.ts +2 -2
- package/dist/batch/batch-bash.d.ts.map +1 -1
- package/dist/batch/batch-bash.js +3 -3
- package/dist/batch/batch-bash.js.map +1 -1
- package/dist/batch/constants.d.ts +19 -5
- package/dist/batch/constants.d.ts.map +1 -1
- package/dist/batch/constants.js +4 -3
- package/dist/batch/constants.js.map +1 -1
- package/dist/batch/execute.d.ts +0 -1
- package/dist/batch/execute.d.ts.map +1 -1
- package/dist/batch/execute.js +101 -10
- package/dist/batch/execute.js.map +1 -1
- package/dist/batch/fuzzy-edit.d.ts +0 -6
- package/dist/batch/fuzzy-edit.d.ts.map +1 -1
- package/dist/batch/fuzzy-edit.js +1 -1
- package/dist/batch/fuzzy-edit.js.map +1 -1
- package/dist/batch/index.d.ts.map +1 -1
- package/dist/batch/index.js +87 -16
- package/dist/batch/index.js.map +1 -1
- package/dist/batch/render.d.ts +0 -1
- package/dist/batch/render.d.ts.map +1 -1
- package/dist/batch/render.js +7 -101
- package/dist/batch/render.js.map +1 -1
- package/dist/batch/summary.d.ts +5 -0
- package/dist/batch/summary.d.ts.map +1 -0
- package/dist/batch/summary.js +101 -0
- package/dist/batch/summary.js.map +1 -0
- package/dist/{config.d.ts → config/config.d.ts} +34 -2
- package/dist/config/config.d.ts.map +1 -0
- package/dist/{config.js → config/config.js} +157 -9
- package/dist/config/config.js.map +1 -0
- package/dist/config/log.d.ts +27 -0
- package/dist/config/log.d.ts.map +1 -0
- package/dist/config/log.js +104 -0
- package/dist/config/log.js.map +1 -0
- package/dist/{settings-resolver.d.ts → config/settings-resolver.d.ts} +9 -2
- package/dist/config/settings-resolver.d.ts.map +1 -0
- package/dist/config/settings-resolver.js +275 -0
- package/dist/config/settings-resolver.js.map +1 -0
- package/dist/core/agents.d.ts.map +1 -0
- package/dist/{agents.js → core/agents.js} +11 -10
- package/dist/core/agents.js.map +1 -0
- package/dist/core/delegation.d.ts +24 -0
- package/dist/core/delegation.d.ts.map +1 -0
- package/dist/core/delegation.js +55 -0
- package/dist/core/delegation.js.map +1 -0
- package/dist/core/depth.d.ts.map +1 -0
- package/dist/{depth.js → core/depth.js} +9 -8
- package/dist/core/depth.js.map +1 -0
- package/dist/{executor.d.ts → core/executor.d.ts} +13 -3
- package/dist/core/executor.d.ts.map +1 -0
- package/dist/{executor.js → core/executor.js} +79 -15
- package/dist/core/executor.js.map +1 -0
- package/dist/{flow.d.ts → core/flow.d.ts} +4 -1
- package/dist/core/flow.d.ts.map +1 -0
- package/dist/{flow.js → core/flow.js} +179 -25
- package/dist/core/flow.js.map +1 -0
- package/dist/{session-mode.d.ts → core/session-mode.d.ts} +2 -1
- package/dist/core/session-mode.d.ts.map +1 -0
- package/dist/{session-mode.js → core/session-mode.js} +1 -1
- package/dist/core/session-mode.js.map +1 -0
- package/dist/core/session-registry.d.ts +16 -0
- package/dist/core/session-registry.d.ts.map +1 -0
- package/dist/core/session-registry.js +30 -0
- package/dist/core/session-registry.js.map +1 -0
- package/dist/core/transitions.d.ts.map +1 -0
- package/dist/{transitions.js → core/transitions.js} +1 -1
- package/dist/core/transitions.js.map +1 -0
- package/dist/flow/command.d.ts +8 -0
- package/dist/flow/command.d.ts.map +1 -0
- package/dist/flow/command.js +189 -0
- package/dist/flow/command.js.map +1 -0
- package/dist/flow/continuation.d.ts +16 -0
- package/dist/flow/continuation.d.ts.map +1 -0
- package/dist/flow/continuation.js +151 -0
- package/dist/flow/continuation.js.map +1 -0
- package/dist/flow/index.d.ts +15 -0
- package/dist/flow/index.d.ts.map +1 -0
- package/dist/flow/index.js +22 -0
- package/dist/flow/index.js.map +1 -0
- package/dist/flow/settings-command.d.ts +51 -0
- package/dist/flow/settings-command.d.ts.map +1 -0
- package/dist/flow/settings-command.js +851 -0
- package/dist/flow/settings-command.js.map +1 -0
- package/dist/flow/store.d.ts +26 -0
- package/dist/flow/store.d.ts.map +1 -0
- package/dist/flow/store.js +158 -0
- package/dist/flow/store.js.map +1 -0
- package/dist/flow/template-strings.d.ts +8 -0
- package/dist/flow/template-strings.d.ts.map +1 -0
- package/dist/flow/template-strings.js +39 -0
- package/dist/flow/template-strings.js.map +1 -0
- package/dist/flow/types.d.ts +55 -0
- package/dist/flow/types.d.ts.map +1 -0
- package/dist/flow/types.js +5 -0
- package/dist/flow/types.js.map +1 -0
- package/dist/flow/warp-command.d.ts +9 -0
- package/dist/flow/warp-command.d.ts.map +1 -0
- package/dist/flow/warp-command.js +405 -0
- package/dist/flow/warp-command.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +115 -32
- package/dist/index.js.map +1 -1
- package/dist/{notify-state.d.ts → notify/notify-state.d.ts} +2 -1
- package/dist/notify/notify-state.d.ts.map +1 -0
- package/dist/notify/notify-state.js.map +1 -0
- package/dist/notify/notify.d.ts.map +1 -0
- package/dist/{notify.js → notify/notify.js} +3 -2
- package/dist/notify/notify.js.map +1 -0
- package/dist/{cli-args.d.ts → snapshot/cli-args.d.ts} +4 -2
- package/dist/snapshot/cli-args.d.ts.map +1 -0
- package/dist/{cli-args.js → snapshot/cli-args.js} +10 -1
- package/dist/snapshot/cli-args.js.map +1 -0
- package/dist/snapshot/index.d.ts +2 -0
- package/dist/snapshot/index.d.ts.map +1 -0
- package/dist/snapshot/index.js +2 -0
- package/dist/snapshot/index.js.map +1 -0
- package/dist/{reasoning-strip.d.ts → snapshot/reasoning-strip.d.ts} +0 -4
- package/dist/snapshot/reasoning-strip.d.ts.map +1 -0
- package/dist/{reasoning-strip.js → snapshot/reasoning-strip.js} +2 -2
- package/dist/snapshot/reasoning-strip.js.map +1 -0
- package/dist/{runner-events.d.ts → snapshot/runner-events.d.ts} +13 -1
- package/dist/snapshot/runner-events.d.ts.map +1 -0
- package/dist/{runner-events.js → snapshot/runner-events.js} +16 -4
- package/dist/snapshot/runner-events.js.map +1 -0
- package/dist/{snapshot.d.ts → snapshot/snapshot.d.ts} +29 -3
- package/dist/snapshot/snapshot.d.ts.map +1 -0
- package/dist/{snapshot.js → snapshot/snapshot.js} +347 -39
- package/dist/snapshot/snapshot.js.map +1 -0
- package/dist/{structured-output.d.ts → snapshot/structured-output.d.ts} +1 -1
- package/dist/snapshot/structured-output.d.ts.map +1 -0
- package/dist/{structured-output.js → snapshot/structured-output.js} +13 -0
- package/dist/snapshot/structured-output.js.map +1 -0
- package/dist/{flow-prompt.d.ts → steering/flow-prompt.d.ts} +2 -2
- package/dist/steering/flow-prompt.d.ts.map +1 -0
- package/dist/{flow-prompt.js → steering/flow-prompt.js} +3 -3
- package/dist/steering/flow-prompt.js.map +1 -0
- package/dist/{sliding-prompt.d.ts → steering/sliding-prompt.d.ts} +8 -7
- package/dist/steering/sliding-prompt.d.ts.map +1 -0
- package/dist/{sliding-prompt.js → steering/sliding-prompt.js} +15 -64
- package/dist/steering/sliding-prompt.js.map +1 -0
- package/dist/{tool-utils.d.ts → steering/tool-utils.d.ts} +1 -0
- package/dist/steering/tool-utils.d.ts.map +1 -0
- package/dist/{tool-utils.js → steering/tool-utils.js} +10 -3
- package/dist/steering/tool-utils.js.map +1 -0
- package/dist/{ask-user.d.ts → tools/ask-user.d.ts} +3 -15
- package/dist/tools/ask-user.d.ts.map +1 -0
- package/dist/tools/ask-user.js +778 -0
- package/dist/tools/ask-user.js.map +1 -0
- package/dist/{timed-bash.d.ts → tools/timed-bash.d.ts} +2 -7
- package/dist/tools/timed-bash.d.ts.map +1 -0
- package/dist/{timed-bash.js → tools/timed-bash.js} +2 -2
- package/dist/tools/timed-bash.js.map +1 -0
- package/dist/{web-tool.d.ts → tools/web-tool.d.ts} +1 -1
- package/dist/tools/web-tool.d.ts.map +1 -0
- package/dist/{web-tool.js → tools/web-tool.js} +8 -7
- package/dist/tools/web-tool.js.map +1 -0
- package/dist/tui/flow-colors.d.ts +55 -0
- package/dist/tui/flow-colors.d.ts.map +1 -0
- package/dist/tui/flow-colors.js +22 -0
- package/dist/tui/flow-colors.js.map +1 -0
- package/dist/{render-utils.d.ts → tui/render-utils.d.ts} +1 -1
- package/dist/tui/render-utils.d.ts.map +1 -0
- package/dist/{render-utils.js → tui/render-utils.js} +3 -3
- package/dist/tui/render-utils.js.map +1 -0
- package/dist/tui/render.d.ts +21 -0
- package/dist/tui/render.d.ts.map +1 -0
- package/dist/tui/render.js +813 -0
- package/dist/tui/render.js.map +1 -0
- package/dist/tui/scramble/algorithm.d.ts +7 -0
- package/dist/tui/scramble/algorithm.d.ts.map +1 -0
- package/dist/tui/scramble/algorithm.js +227 -0
- package/dist/tui/scramble/algorithm.js.map +1 -0
- package/dist/tui/scramble/constants.d.ts +99 -0
- package/dist/tui/scramble/constants.d.ts.map +1 -0
- package/dist/tui/scramble/constants.js +101 -0
- package/dist/tui/scramble/constants.js.map +1 -0
- package/dist/tui/scramble/index.d.ts +6 -0
- package/dist/tui/scramble/index.d.ts.map +1 -0
- package/dist/tui/scramble/index.js +6 -0
- package/dist/tui/scramble/index.js.map +1 -0
- package/dist/tui/scramble/manager.d.ts +48 -0
- package/dist/tui/scramble/manager.d.ts.map +1 -0
- package/dist/tui/scramble/manager.js +959 -0
- package/dist/tui/scramble/manager.js.map +1 -0
- package/dist/tui/scramble/utils.d.ts +18 -0
- package/dist/tui/scramble/utils.d.ts.map +1 -0
- package/dist/tui/scramble/utils.js +145 -0
- package/dist/tui/scramble/utils.js.map +1 -0
- package/dist/tui/single-select-layout.d.ts +17 -0
- package/dist/tui/single-select-layout.d.ts.map +1 -0
- package/dist/{single-select-layout.js → tui/single-select-layout.js} +8 -25
- package/dist/tui/single-select-layout.js.map +1 -0
- package/dist/types/flow.d.ts +110 -0
- package/dist/types/flow.d.ts.map +1 -0
- package/dist/{types.js → types/flow.js} +3 -54
- package/dist/types/flow.js.map +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/output.d.ts +104 -0
- package/dist/types/output.d.ts.map +1 -0
- package/dist/types/output.js +5 -0
- package/dist/types/output.js.map +1 -0
- package/dist/types/ui.d.ts +24 -0
- package/dist/types/ui.d.ts.map +1 -0
- package/dist/types/ui.js +55 -0
- package/dist/types/ui.js.map +1 -0
- package/package.json +7 -4
- package/dist/agents.d.ts.map +0 -1
- package/dist/agents.js.map +0 -1
- package/dist/ask-user.d.ts.map +0 -1
- package/dist/ask-user.js +0 -1405
- package/dist/ask-user.js.map +0 -1
- package/dist/batch.d.ts +0 -12
- package/dist/batch.d.ts.map +0 -1
- package/dist/batch.js +0 -11
- package/dist/batch.js.map +0 -1
- package/dist/cli-args.d.ts.map +0 -1
- package/dist/cli-args.js.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/depth.d.ts.map +0 -1
- package/dist/depth.js.map +0 -1
- package/dist/executor.d.ts.map +0 -1
- package/dist/executor.js.map +0 -1
- package/dist/flow-prompt.d.ts.map +0 -1
- package/dist/flow-prompt.js.map +0 -1
- package/dist/flow.d.ts.map +0 -1
- package/dist/flow.js.map +0 -1
- package/dist/notify-state.d.ts.map +0 -1
- package/dist/notify-state.js.map +0 -1
- package/dist/notify.d.ts.map +0 -1
- package/dist/notify.js.map +0 -1
- package/dist/reasoning-strip.d.ts.map +0 -1
- package/dist/reasoning-strip.js.map +0 -1
- package/dist/render-utils.d.ts.map +0 -1
- package/dist/render-utils.js.map +0 -1
- package/dist/render.d.ts +0 -24
- package/dist/render.d.ts.map +0 -1
- package/dist/render.js +0 -592
- package/dist/render.js.map +0 -1
- package/dist/runner-events.d.ts.map +0 -1
- package/dist/runner-events.js.map +0 -1
- package/dist/scramble.d.ts +0 -171
- package/dist/scramble.d.ts.map +0 -1
- package/dist/scramble.js +0 -2261
- package/dist/scramble.js.map +0 -1
- package/dist/session-mode.d.ts.map +0 -1
- package/dist/session-mode.js.map +0 -1
- package/dist/settings-resolver.d.ts.map +0 -1
- package/dist/settings-resolver.js +0 -148
- package/dist/settings-resolver.js.map +0 -1
- package/dist/single-select-layout.d.ts +0 -20
- package/dist/single-select-layout.d.ts.map +0 -1
- package/dist/single-select-layout.js.map +0 -1
- package/dist/sliding-prompt.d.ts.map +0 -1
- package/dist/sliding-prompt.js.map +0 -1
- package/dist/snapshot.d.ts.map +0 -1
- package/dist/snapshot.js.map +0 -1
- package/dist/spec-mode.d.ts +0 -13
- package/dist/spec-mode.d.ts.map +0 -1
- package/dist/spec-mode.js +0 -90
- package/dist/spec-mode.js.map +0 -1
- package/dist/structured-output.d.ts.map +0 -1
- package/dist/structured-output.js.map +0 -1
- package/dist/timed-bash.d.ts.map +0 -1
- package/dist/timed-bash.js.map +0 -1
- package/dist/tool-utils.d.ts.map +0 -1
- package/dist/tool-utils.js.map +0 -1
- package/dist/transitions.d.ts.map +0 -1
- package/dist/transitions.js.map +0 -1
- package/dist/types.d.ts +0 -208
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/web-tool.d.ts.map +0 -1
- package/dist/web-tool.js.map +0 -1
- /package/dist/{agents.d.ts → core/agents.d.ts} +0 -0
- /package/dist/{depth.d.ts → core/depth.d.ts} +0 -0
- /package/dist/{transitions.d.ts → core/transitions.d.ts} +0 -0
- /package/dist/{notify-state.js → notify/notify-state.js} +0 -0
- /package/dist/{notify.d.ts → notify/notify.d.ts} +0 -0
package/dist/render.js
DELETED
|
@@ -1,592 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TUI rendering for flow-state tool calls and results.
|
|
3
|
-
*
|
|
4
|
-
* Option B: collapsed view shows structured report (Summary/Done/Not Done/Next Steps).
|
|
5
|
-
* Expanded view adds raw tool call traces.
|
|
6
|
-
*/
|
|
7
|
-
import * as os from "node:os";
|
|
8
|
-
import { getMarkdownTheme } from "@mariozechner/pi-coding-agent";
|
|
9
|
-
import { Container, Markdown, Spacer, Text, TruncatedText } from "@mariozechner/pi-tui";
|
|
10
|
-
import { getFlowSummaryText } from "./runner-events.js";
|
|
11
|
-
import { aggregateFlowUsage, getFlowDisplayItems, getFlowOutput, getLastToolCall, getLastAssistantText, isFlowError, isFlowSuccess, } from "./types.js";
|
|
12
|
-
import { formatBatchOpsSummary } from "./batch/render.js";
|
|
13
|
-
import { scrambleManager, runScrambleTimer } from "./scramble.js";
|
|
14
|
-
import { formatCompactStats, formatCompactTokenPair, formatCountdown, formatFlowTypeName, italic, lowerFirstWord, truncateChars, tailText, getTruncationBudget, visibleLength, stripAnsi } from "./render-utils.js";
|
|
15
|
-
function shortenPath(p) {
|
|
16
|
-
const home = os.homedir();
|
|
17
|
-
return p.startsWith(home) ? `~${p.slice(home.length)}` : p;
|
|
18
|
-
}
|
|
19
|
-
function formatCollapsedFlowHeaderTypeName(type) {
|
|
20
|
-
return type.toLowerCase();
|
|
21
|
-
}
|
|
22
|
-
function formatFlowToolCall(toolName, args, fg) {
|
|
23
|
-
const pathArg = (args.file_path || args.path || "...");
|
|
24
|
-
switch (toolName) {
|
|
25
|
-
case "bash": {
|
|
26
|
-
const cmd = (args.command || "...").replace(/[\n\r\t]+/g, " ").replace(/ +/g, " ").trim();
|
|
27
|
-
return fg("muted", "$ ") + fg("toolOutput", cmd);
|
|
28
|
-
}
|
|
29
|
-
case "read": {
|
|
30
|
-
let text = fg("accent", shortenPath(pathArg));
|
|
31
|
-
const offset = args.offset;
|
|
32
|
-
const limit = args.limit;
|
|
33
|
-
if (offset !== undefined || limit !== undefined) {
|
|
34
|
-
const start = offset ?? 1;
|
|
35
|
-
const end = limit !== undefined ? start + limit - 1 : "";
|
|
36
|
-
text += fg("warning", `:${start}${end ? `-${end}` : ""}`);
|
|
37
|
-
}
|
|
38
|
-
return fg("muted", "read ") + text;
|
|
39
|
-
}
|
|
40
|
-
case "write": {
|
|
41
|
-
const lines = (args.content || "").split("\n").length;
|
|
42
|
-
let text = fg("muted", "write ") + fg("accent", shortenPath(pathArg));
|
|
43
|
-
if (lines > 1)
|
|
44
|
-
text += fg("dim", ` (${lines} lines)`);
|
|
45
|
-
return text;
|
|
46
|
-
}
|
|
47
|
-
case "edit":
|
|
48
|
-
return fg("muted", "edit ") + fg("accent", shortenPath(pathArg));
|
|
49
|
-
case "ls":
|
|
50
|
-
return fg("muted", "ls ") + fg("accent", shortenPath((args.path || ".")));
|
|
51
|
-
case "find":
|
|
52
|
-
return fg("muted", "find ") + fg("accent", (args.pattern || "*")) + fg("dim", ` in ${shortenPath((args.path || "."))}`);
|
|
53
|
-
case "grep":
|
|
54
|
-
return fg("muted", "grep ") + fg("accent", `/${(args.pattern || "")}/`) + fg("dim", ` in ${shortenPath((args.path || "."))}`);
|
|
55
|
-
case "batch":
|
|
56
|
-
case "batch_read": {
|
|
57
|
-
const summary = formatBatchOpsSummary(args);
|
|
58
|
-
return fg("muted", `${toolName} `) + fg("accent", summary);
|
|
59
|
-
}
|
|
60
|
-
default:
|
|
61
|
-
return fg("accent", toolName) + fg("dim", ` ${JSON.stringify(args)}`);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
// ---------------------------------------------------------------------------
|
|
65
|
-
// Shared rendering building blocks
|
|
66
|
-
// ---------------------------------------------------------------------------
|
|
67
|
-
function splitOutputLines(text) {
|
|
68
|
-
const lines = text.replace(/\r\n?/g, "\n").split("\n");
|
|
69
|
-
if (lines.length > 1 && lines[lines.length - 1] === "")
|
|
70
|
-
lines.pop();
|
|
71
|
-
return lines;
|
|
72
|
-
}
|
|
73
|
-
function renderToolTraces(items, theme) {
|
|
74
|
-
const lines = [];
|
|
75
|
-
for (const item of items) {
|
|
76
|
-
if (item.type === "toolCall") {
|
|
77
|
-
lines.push(theme.fg("muted", "→ ") + formatFlowToolCall(item.name, item.args, theme.fg.bind(theme)));
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return lines.join("\n");
|
|
81
|
-
}
|
|
82
|
-
function renderFlowReport(output, theme) {
|
|
83
|
-
const lines = splitOutputLines(output);
|
|
84
|
-
return lines.map((line) => theme.fg("toolOutput", line)).join("\n");
|
|
85
|
-
}
|
|
86
|
-
function flowStatusIcon(r, theme) {
|
|
87
|
-
if (r.exitCode === -1)
|
|
88
|
-
return theme.fg("warning", "⏳");
|
|
89
|
-
return isFlowError(r) ? theme.fg("error", "✖") : theme.fg("success", "✔");
|
|
90
|
-
}
|
|
91
|
-
/** Center a label in a fixed-width header using em-dashes. Total width = 20. */
|
|
92
|
-
function sectionHeader(label) {
|
|
93
|
-
const total = 20;
|
|
94
|
-
const innerLen = label.length + 2; // account for spaces around label
|
|
95
|
-
const side = (total - innerLen) / 2;
|
|
96
|
-
const left = "─".repeat(Math.floor(side));
|
|
97
|
-
const right = "─".repeat(Math.ceil(side));
|
|
98
|
-
return `${left} ${label} ${right}`;
|
|
99
|
-
}
|
|
100
|
-
function getLiveCountdown(r) {
|
|
101
|
-
if (r.exitCode !== -1 || typeof r.deadlineAtMs !== "number")
|
|
102
|
-
return undefined;
|
|
103
|
-
return formatCountdown(r.deadlineAtMs - Date.now());
|
|
104
|
-
}
|
|
105
|
-
// ---------------------------------------------------------------------------
|
|
106
|
-
// renderFlowCall — shown while the flow is being invoked
|
|
107
|
-
// ---------------------------------------------------------------------------
|
|
108
|
-
export function renderFlowCall(args, theme) {
|
|
109
|
-
const flows = args.flow;
|
|
110
|
-
// Minimal — renderFlowResult owns the full display
|
|
111
|
-
return new Text("", 0, 0);
|
|
112
|
-
}
|
|
113
|
-
// ---------------------------------------------------------------------------
|
|
114
|
-
// renderFlowResult — shown after the flow completes
|
|
115
|
-
// ---------------------------------------------------------------------------
|
|
116
|
-
export function renderFlowResult(result, expanded, theme, args) {
|
|
117
|
-
const details = result.details;
|
|
118
|
-
const streamingText = result.content?.[0]?.type === "text" ? result.content[0].text : undefined;
|
|
119
|
-
let container;
|
|
120
|
-
if (!details || details.results.length === 0) {
|
|
121
|
-
// Ghost Dashboard: render a placeholder status line during the zero state
|
|
122
|
-
const flowRequest = args?.flow?.[0];
|
|
123
|
-
if (flowRequest) {
|
|
124
|
-
const ghostResult = {
|
|
125
|
-
type: flowRequest.type || "unknown",
|
|
126
|
-
agentSource: "user",
|
|
127
|
-
intent: flowRequest.intent || "Processing...",
|
|
128
|
-
aim: flowRequest.aim || flowRequest.intent || "Processing...",
|
|
129
|
-
acceptance: flowRequest.acceptance,
|
|
130
|
-
exitCode: -1, // In progress
|
|
131
|
-
messages: [],
|
|
132
|
-
stderr: "",
|
|
133
|
-
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0, toolCalls: 0 },
|
|
134
|
-
};
|
|
135
|
-
if (expanded) {
|
|
136
|
-
const now = Date.now();
|
|
137
|
-
container = renderFlowExpanded(ghostResult, flowStatusIcon(ghostResult, theme), false, getFlowDisplayItems([]), getFlowOutput([]), theme, "ghost", now, false, streamingText || "");
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
container = renderFlowCollapsed(ghostResult, flowStatusIcon(ghostResult, theme), false, streamingText || "", theme);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
container = new Text(streamingText || "", 0, 0);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
else if (details.results.length === 1) {
|
|
148
|
-
container = renderSingleFlowResult(details.results[0], expanded, theme, streamingText);
|
|
149
|
-
}
|
|
150
|
-
else {
|
|
151
|
-
container = renderMultiFlowResult(details, expanded, theme);
|
|
152
|
-
}
|
|
153
|
-
// Scramble animation timer — shared helper so any renderer can animate.
|
|
154
|
-
runScrambleTimer(args);
|
|
155
|
-
return container;
|
|
156
|
-
}
|
|
157
|
-
// ---------------------------------------------------------------------------
|
|
158
|
-
// Single flow result
|
|
159
|
-
// ---------------------------------------------------------------------------
|
|
160
|
-
function renderSingleFlowResult(r, expanded, theme, streamingText, toolCallId) {
|
|
161
|
-
const id = toolCallId || "single";
|
|
162
|
-
const error = isFlowError(r);
|
|
163
|
-
const icon = flowStatusIcon(r, theme);
|
|
164
|
-
const displayItems = getFlowDisplayItems(r.messages);
|
|
165
|
-
const flowOutput = getFlowOutput(r.messages);
|
|
166
|
-
const now = Date.now();
|
|
167
|
-
const isComplete = r.exitCode !== -1;
|
|
168
|
-
if (expanded) {
|
|
169
|
-
return renderFlowExpanded(r, icon, error, displayItems, flowOutput, theme, id, now, isComplete, streamingText);
|
|
170
|
-
}
|
|
171
|
-
return renderFlowCollapsed(r, icon, error, flowOutput, theme, streamingText, id);
|
|
172
|
-
}
|
|
173
|
-
function renderFlowExpanded(r, icon, error, displayItems, flowOutput, theme, id, now, isComplete, streamingText) {
|
|
174
|
-
const mdTheme = getMarkdownTheme();
|
|
175
|
-
const container = new Container();
|
|
176
|
-
// Header: uppercase type name with dots, no icon, no source
|
|
177
|
-
const typeName = formatFlowTypeName(r.type);
|
|
178
|
-
let header = theme.fg("toolTitle", theme.bold(typeName));
|
|
179
|
-
if (error && r.stopReason)
|
|
180
|
-
header += ` ${theme.fg("error", `[${r.stopReason}]`)}`;
|
|
181
|
-
const plainHeader = typeName + (error && r.stopReason ? ` [${r.stopReason}]` : "");
|
|
182
|
-
const headerResult = scrambleManager.updateText(id, 'header', plainHeader, now, isComplete);
|
|
183
|
-
container.addChild(new Text(headerResult.isAnimating ? theme.fg("toolTitle", headerResult.content) : header, 0, 0));
|
|
184
|
-
if (error && r.errorMessage) {
|
|
185
|
-
container.addChild(new Text(theme.fg("error", `Error: ${r.errorMessage}`), 0, 0));
|
|
186
|
-
}
|
|
187
|
-
// Stats: dashboard format
|
|
188
|
-
const inlineStats = formatCompactStats(r.usage, r.model);
|
|
189
|
-
const statsResult = scrambleManager.updateText(id, 'stats', stripAnsi(inlineStats), now, isComplete);
|
|
190
|
-
container.addChild(new Text(statsResult.isAnimating ? theme.fg("dim", statsResult.content) : theme.fg("dim", inlineStats), 0, 0));
|
|
191
|
-
// Intent
|
|
192
|
-
container.addChild(new Spacer(1));
|
|
193
|
-
container.addChild(new Text(theme.fg("muted", sectionHeader("intent")), 0, 0));
|
|
194
|
-
const intentResult = scrambleManager.updateText(id, 'intent', r.intent, now, isComplete);
|
|
195
|
-
container.addChild(new Text(intentResult.isAnimating ? theme.fg("dim", intentResult.content) : theme.fg("dim", r.intent), 0, 0));
|
|
196
|
-
// Acceptance
|
|
197
|
-
if (r.acceptance) {
|
|
198
|
-
container.addChild(new Spacer(1));
|
|
199
|
-
container.addChild(new Text(theme.fg("muted", sectionHeader("acceptance")), 0, 0));
|
|
200
|
-
const acceptanceResult = scrambleManager.updateText(id, 'acceptance', r.acceptance, now, isComplete);
|
|
201
|
-
container.addChild(new Text(acceptanceResult.isAnimating ? theme.fg("dim", acceptanceResult.content) : theme.fg("dim", r.acceptance), 0, 0));
|
|
202
|
-
}
|
|
203
|
-
// Flow report (structured output)
|
|
204
|
-
container.addChild(new Spacer(1));
|
|
205
|
-
container.addChild(new Text(theme.fg("muted", sectionHeader("report")), 0, 0));
|
|
206
|
-
// Structured output summary (compact badge when available)
|
|
207
|
-
if (r.structuredOutput) {
|
|
208
|
-
const so = r.structuredOutput;
|
|
209
|
-
const statusColor = so.status === "complete" ? "success" : so.status === "partial" ? "warning" : "error";
|
|
210
|
-
const statusText = `[${so.status}] ${so.summary}`;
|
|
211
|
-
const statusResult = scrambleManager.updateText(id, 'report-status', statusText, now, isComplete, false);
|
|
212
|
-
container.addChild(new Text(statusResult.isAnimating ? `${theme.fg(statusColor, statusResult.content.split(' ')[0])} ${theme.fg("dim", statusResult.content.slice(statusResult.content.indexOf(' ') + 1))}` : `${theme.fg(statusColor, `[${so.status}]`)} ${theme.fg("dim", so.summary)}`, 0, 0));
|
|
213
|
-
if (so.files.length > 0) {
|
|
214
|
-
const filesText = `Files: ${so.files.map((f) => f.path).join(", ")}`;
|
|
215
|
-
const filesResult = scrambleManager.updateText(id, 'report-files', filesText, now, isComplete, false);
|
|
216
|
-
container.addChild(new Text(filesResult.isAnimating ? theme.fg("dim", filesResult.content) : theme.fg("dim", filesText), 0, 0));
|
|
217
|
-
}
|
|
218
|
-
if (so.commands?.length > 0) {
|
|
219
|
-
const cmdLabels = so.commands.map((c) => {
|
|
220
|
-
const short = c.command.length > 30 ? c.command.slice(0, 30) + "..." : c.command;
|
|
221
|
-
return `${c.tool ?? "cmd"}: ${short}`;
|
|
222
|
-
});
|
|
223
|
-
const commandsText = `Commands: ${cmdLabels.join(", ")}`;
|
|
224
|
-
const commandsResult = scrambleManager.updateText(id, 'report-commands', commandsText, now, isComplete, false);
|
|
225
|
-
container.addChild(new Text(commandsResult.isAnimating ? theme.fg("dim", commandsResult.content) : theme.fg("dim", commandsText), 0, 0));
|
|
226
|
-
}
|
|
227
|
-
if (so.notDone.length > 0) {
|
|
228
|
-
const notDoneText = `Not Done: ${so.notDone.map((item) => {
|
|
229
|
-
const details = [
|
|
230
|
-
item.reason ? `reason: ${item.reason}` : undefined,
|
|
231
|
-
item.blocker ? `blocker: ${item.blocker}` : undefined,
|
|
232
|
-
item.nextStep ? `next: ${item.nextStep}` : undefined,
|
|
233
|
-
].filter(Boolean).join("; ");
|
|
234
|
-
return details ? `${item.item} (${details})` : item.item;
|
|
235
|
-
}).join("; ")}`;
|
|
236
|
-
const notDoneResult = scrambleManager.updateText(id, 'report-notDone', notDoneText, now, isComplete, false);
|
|
237
|
-
container.addChild(new Text(notDoneResult.isAnimating ? theme.fg("dim", notDoneResult.content) : theme.fg("dim", notDoneText), 0, 0));
|
|
238
|
-
}
|
|
239
|
-
if (so.nextSteps.length > 0) {
|
|
240
|
-
const nextStepsText = `Next: ${so.nextSteps.join("; ")}`;
|
|
241
|
-
const nextStepsResult = scrambleManager.updateText(id, 'report-nextSteps', nextStepsText, now, isComplete, false);
|
|
242
|
-
container.addChild(new Text(nextStepsResult.isAnimating ? theme.fg("dim", nextStepsResult.content) : theme.fg("dim", nextStepsText), 0, 0));
|
|
243
|
-
}
|
|
244
|
-
container.addChild(new Spacer(1));
|
|
245
|
-
}
|
|
246
|
-
// Output: animate streaming text; show clean markdown when complete
|
|
247
|
-
if (!isComplete && streamingText) {
|
|
248
|
-
const scrambled = scrambleManager.updateMsg(id, stripAnsi(streamingText), now, isComplete).content;
|
|
249
|
-
container.addChild(new Text(scrambled, 0, 0));
|
|
250
|
-
}
|
|
251
|
-
else if (flowOutput) {
|
|
252
|
-
container.addChild(new Markdown(flowOutput.trim(), 0, 0, mdTheme));
|
|
253
|
-
}
|
|
254
|
-
else {
|
|
255
|
-
const summary = getFlowSummaryText(r);
|
|
256
|
-
const summaryResult = scrambleManager.updateText(id, 'output-summary', summary, now, isComplete, false);
|
|
257
|
-
container.addChild(new Text(summaryResult.isAnimating ? theme.fg("muted", summaryResult.content) : theme.fg("muted", summary), 0, 0));
|
|
258
|
-
}
|
|
259
|
-
// Tool traces (expanded only) — per-line scramble
|
|
260
|
-
const toolCallItems = displayItems.filter((item) => item.type === "toolCall");
|
|
261
|
-
if (toolCallItems.length > 0) {
|
|
262
|
-
container.addChild(new Spacer(1));
|
|
263
|
-
container.addChild(new Text(theme.fg("muted", sectionHeader("tool calls")), 0, 0));
|
|
264
|
-
for (let i = 0; i < toolCallItems.length; i++) {
|
|
265
|
-
const item = toolCallItems[i];
|
|
266
|
-
const lineText = theme.fg("muted", "→ ") + formatFlowToolCall(item.name, item.args, theme.fg.bind(theme));
|
|
267
|
-
const plainText = stripAnsi(lineText);
|
|
268
|
-
const scrambled = scrambleManager.updateText(id, `tool#${i}`, plainText, now, isComplete).content;
|
|
269
|
-
container.addChild(new Text(scrambled, 0, 0));
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
if (isComplete) {
|
|
273
|
-
scrambleManager.completeFlow(id);
|
|
274
|
-
}
|
|
275
|
-
return container;
|
|
276
|
-
}
|
|
277
|
-
function renderFlowCollapsed(r, icon, error, flowOutput, theme, streamingText, toolCallId) {
|
|
278
|
-
const id = toolCallId || "collapsed";
|
|
279
|
-
const now = Date.now();
|
|
280
|
-
const container = new Container();
|
|
281
|
-
const maxWidth = process.stdout.columns ?? 80;
|
|
282
|
-
const stats = formatCompactStats(r.usage, r.model, maxWidth, { skipTokens: true, skipContext: true, hideModel: true });
|
|
283
|
-
const isComplete = r.exitCode !== -1;
|
|
284
|
-
// Flash TPS value when it changes
|
|
285
|
-
const tpsMatch = stats.match(/tps:\s*(\S+)/);
|
|
286
|
-
let displayStats = stats;
|
|
287
|
-
if (tpsMatch) {
|
|
288
|
-
const scrambledTps = scrambleManager.updateTps(id, tpsMatch[1], now, isComplete, true);
|
|
289
|
-
if (scrambledTps !== tpsMatch[1]) {
|
|
290
|
-
displayStats = stats.replace(tpsMatch[1], scrambledTps);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
const typeName = formatCollapsedFlowHeaderTypeName(r.type);
|
|
294
|
-
const modelLabel = r.model ? r.model.replace(/^[^/]+\//, "").toLowerCase() : "";
|
|
295
|
-
let header = `${theme.fg("accent", theme.bold(typeName))}${theme.fg("dim", modelLabel ? ` - ${modelLabel} - ` : " - ")}${theme.fg("dim", displayStats)}`;
|
|
296
|
-
if (error && r.stopReason)
|
|
297
|
-
header += ` ${theme.fg("error", `[${r.stopReason}]`)}`;
|
|
298
|
-
// Scramble header on first render; show full styled header when complete
|
|
299
|
-
const plainHeader = typeName + (modelLabel ? ` - ${modelLabel} - ` : " - ") + stripAnsi(displayStats) + (error && r.stopReason ? ` [${r.stopReason}]` : "");
|
|
300
|
-
const headerResult = scrambleManager.updateText(id, 'header', plainHeader, now, isComplete, true);
|
|
301
|
-
const headerDisplay = headerResult.isAnimating ? theme.fg("accent", headerResult.content) : header;
|
|
302
|
-
container.addChild(new TruncatedText(headerDisplay, 0, 0));
|
|
303
|
-
// aim: line — cascade/ripple/illuminate on text change
|
|
304
|
-
if (r.aim) {
|
|
305
|
-
const countdown = getLiveCountdown(r);
|
|
306
|
-
const treePrefix = "├─";
|
|
307
|
-
const aimPrefix = countdown
|
|
308
|
-
? `${treePrefix} aim: [${countdown}] - `
|
|
309
|
-
: `${treePrefix} aim: `;
|
|
310
|
-
const budget = getTruncationBudget(visibleLength(aimPrefix));
|
|
311
|
-
const displayAim = truncateChars(lowerFirstWord(r.aim), budget);
|
|
312
|
-
const aimResult = scrambleManager.updateAim(id, displayAim, now, isComplete, true);
|
|
313
|
-
const aimContent = aimResult.content;
|
|
314
|
-
container.addChild(new TruncatedText(`${theme.fg("dim", aimPrefix)}${theme.fg("dim", italic(aimContent))}`, 0, 0));
|
|
315
|
-
}
|
|
316
|
-
// act: line (last tool call with count)
|
|
317
|
-
const lastTool = getLastToolCall(r.messages);
|
|
318
|
-
if (lastTool) {
|
|
319
|
-
const actStr = formatFlowToolCall(lastTool.name, lastTool.args, theme.fg.bind(theme));
|
|
320
|
-
const prefixStub = `├─ act: [${r.usage.toolCalls}] - `;
|
|
321
|
-
const budget = getTruncationBudget(visibleLength(prefixStub));
|
|
322
|
-
const actFullText = stripAnsi(lowerFirstWord(actStr));
|
|
323
|
-
let actContent;
|
|
324
|
-
if (scrambleManager.getMode() === 'stream') {
|
|
325
|
-
actContent = scrambleManager.streamAct(id, actFullText, now, isComplete, budget);
|
|
326
|
-
}
|
|
327
|
-
else {
|
|
328
|
-
const displayAct = truncateChars(actFullText, budget);
|
|
329
|
-
actContent = scrambleManager.updateAct(id, displayAct, now, isComplete, true).content;
|
|
330
|
-
}
|
|
331
|
-
let actKpi = String(r.usage.toolCalls);
|
|
332
|
-
const scrambledActKpi = scrambleManager.updateActKpi(id, actKpi, now, isComplete, true);
|
|
333
|
-
if (scrambledActKpi !== actKpi) {
|
|
334
|
-
actKpi = scrambledActKpi;
|
|
335
|
-
}
|
|
336
|
-
const actPrefix = `├─ act: [${actKpi}] - `;
|
|
337
|
-
container.addChild(new TruncatedText(`${theme.fg("dim", actPrefix)}${italic(actContent)}`, 0, 0));
|
|
338
|
-
}
|
|
339
|
-
// msg: line (last assistant text or streaming)
|
|
340
|
-
let msgKpi = formatCompactTokenPair(r.usage);
|
|
341
|
-
const scrambledMsgKpi = scrambleManager.updateMsgKpi(id, msgKpi, now, isComplete, false);
|
|
342
|
-
if (scrambledMsgKpi !== msgKpi) {
|
|
343
|
-
msgKpi = scrambledMsgKpi;
|
|
344
|
-
}
|
|
345
|
-
const msgPrefixStub = `└─ msg: [${msgKpi}] - `;
|
|
346
|
-
const msgBudget = getTruncationBudget(visibleLength(msgPrefixStub));
|
|
347
|
-
let rawMsg;
|
|
348
|
-
let useError = false;
|
|
349
|
-
if (r.exitCode === -1 && streamingText) {
|
|
350
|
-
rawMsg = stripAnsi(streamingText);
|
|
351
|
-
}
|
|
352
|
-
else if (r.structuredOutput?.summary) {
|
|
353
|
-
rawMsg = stripAnsi(r.structuredOutput.summary);
|
|
354
|
-
}
|
|
355
|
-
else if (flowOutput) {
|
|
356
|
-
rawMsg = stripAnsi(flowOutput);
|
|
357
|
-
}
|
|
358
|
-
else if (streamingText) {
|
|
359
|
-
rawMsg = stripAnsi(streamingText);
|
|
360
|
-
}
|
|
361
|
-
else if (error && r.errorMessage) {
|
|
362
|
-
rawMsg = stripAnsi(r.errorMessage);
|
|
363
|
-
useError = true;
|
|
364
|
-
}
|
|
365
|
-
else {
|
|
366
|
-
rawMsg = "[n/a]";
|
|
367
|
-
}
|
|
368
|
-
let msgContent;
|
|
369
|
-
if (scrambleManager.getMode() === 'stream') {
|
|
370
|
-
msgContent = scrambleManager.streamMsg(id, rawMsg, now, isComplete, msgBudget);
|
|
371
|
-
}
|
|
372
|
-
else {
|
|
373
|
-
// For active (incomplete) flows, pass full text to keep animation stable.
|
|
374
|
-
// TruncatedText handles display truncation. Completed flows truncate as before.
|
|
375
|
-
if (!isComplete) {
|
|
376
|
-
msgContent = scrambleManager.updateMsg(id, rawMsg, now, isComplete, undefined, true).content;
|
|
377
|
-
}
|
|
378
|
-
else {
|
|
379
|
-
const needsTail = (r.exitCode === -1 && streamingText) || streamingText;
|
|
380
|
-
const displayMsg = needsTail ? tailText(rawMsg, msgBudget) : truncateChars(rawMsg, msgBudget);
|
|
381
|
-
msgContent = scrambleManager.updateMsg(id, displayMsg, now, isComplete, undefined, true).content;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
const msgPrefix = `└─ msg: [${msgKpi}] - `;
|
|
385
|
-
container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg(useError ? "error" : "dim", italic(msgContent))}`, 0, 0));
|
|
386
|
-
if (isComplete) {
|
|
387
|
-
scrambleManager.completeFlow(id);
|
|
388
|
-
}
|
|
389
|
-
return container;
|
|
390
|
-
}
|
|
391
|
-
// ---------------------------------------------------------------------------
|
|
392
|
-
// Multi-flow result
|
|
393
|
-
// ---------------------------------------------------------------------------
|
|
394
|
-
function renderMultiFlowResult(details, expanded, theme, toolCallId) {
|
|
395
|
-
const baseId = toolCallId || "multi";
|
|
396
|
-
const results = details.results;
|
|
397
|
-
const successCount = results.filter((r) => isFlowSuccess(r)).length;
|
|
398
|
-
const failCount = results.filter((r) => isFlowError(r)).length;
|
|
399
|
-
const icon = failCount > 0 ? theme.fg("warning", "◐") : theme.fg("success", "✔");
|
|
400
|
-
const now = Date.now();
|
|
401
|
-
if (expanded) {
|
|
402
|
-
return renderMultiFlowExpanded(results, successCount, icon, theme, baseId, now);
|
|
403
|
-
}
|
|
404
|
-
return renderMultiFlowCollapsed(results, theme, baseId);
|
|
405
|
-
}
|
|
406
|
-
function renderMultiFlowExpanded(results, successCount, icon, theme, baseId, now) {
|
|
407
|
-
const mdTheme = getMarkdownTheme();
|
|
408
|
-
const container = new Container();
|
|
409
|
-
// Summary: just show count, no icon
|
|
410
|
-
container.addChild(new Text(theme.fg("accent", `${results.length} flows`), 0, 0));
|
|
411
|
-
for (let flowIdx = 0; flowIdx < results.length; flowIdx++) {
|
|
412
|
-
const r = results[flowIdx];
|
|
413
|
-
const flowId = `${baseId}#${flowIdx}`;
|
|
414
|
-
const isComplete = r.exitCode !== -1;
|
|
415
|
-
const displayItems = getFlowDisplayItems(r.messages);
|
|
416
|
-
const flowOutput = getFlowOutput(r.messages);
|
|
417
|
-
const typeName = formatFlowTypeName(r.type);
|
|
418
|
-
container.addChild(new Spacer(1));
|
|
419
|
-
// Per-flow header: ─── EXPLORER (no icon)
|
|
420
|
-
const headerResult = scrambleManager.updateText(flowId, 'header', typeName, now, isComplete, true);
|
|
421
|
-
container.addChild(new Text(headerResult.isAnimating ? theme.fg("muted", headerResult.content) : theme.fg("muted", sectionHeader(typeName)), 0, 0));
|
|
422
|
-
// Stats: dashboard format
|
|
423
|
-
const flowStats = formatCompactStats(r.usage, r.model);
|
|
424
|
-
const statsResult = scrambleManager.updateText(flowId, 'stats', stripAnsi(flowStats), now, isComplete, true);
|
|
425
|
-
container.addChild(new Text(statsResult.isAnimating ? theme.fg("dim", statsResult.content) : theme.fg("dim", flowStats), 0, 0));
|
|
426
|
-
// Intent: just show text, no prefix
|
|
427
|
-
const intentResult = scrambleManager.updateText(flowId, 'intent', r.intent, now, isComplete, true);
|
|
428
|
-
container.addChild(new Text(intentResult.isAnimating ? theme.fg("dim", intentResult.content) : theme.fg("dim", r.intent), 0, 0));
|
|
429
|
-
if (r.acceptance) {
|
|
430
|
-
const acceptanceResult = scrambleManager.updateText(flowId, 'acceptance', r.acceptance, now, isComplete, true);
|
|
431
|
-
container.addChild(new Text(acceptanceResult.isAnimating ? theme.fg("dim", acceptanceResult.content) : theme.fg("dim", `Acceptance: ${r.acceptance}`), 0, 0));
|
|
432
|
-
}
|
|
433
|
-
// Output: animate streaming text; show clean markdown when complete
|
|
434
|
-
if (!isComplete && r.streamingText) {
|
|
435
|
-
const scrambled = scrambleManager.updateMsg(flowId, stripAnsi(r.streamingText), now, isComplete, undefined, true).content;
|
|
436
|
-
container.addChild(new Text(scrambled, 0, 0));
|
|
437
|
-
}
|
|
438
|
-
else if (flowOutput) {
|
|
439
|
-
container.addChild(new Spacer(1));
|
|
440
|
-
container.addChild(new Markdown(flowOutput.trim(), 0, 0, mdTheme));
|
|
441
|
-
}
|
|
442
|
-
// Tool traces in expanded view — per-line scramble
|
|
443
|
-
const toolCallItems = displayItems.filter((item) => item.type === "toolCall");
|
|
444
|
-
if (toolCallItems.length > 0) {
|
|
445
|
-
container.addChild(new Spacer(1));
|
|
446
|
-
container.addChild(new Text(theme.fg("muted", sectionHeader("tool calls")), 0, 0));
|
|
447
|
-
for (let i = 0; i < toolCallItems.length; i++) {
|
|
448
|
-
const item = toolCallItems[i];
|
|
449
|
-
const lineText = theme.fg("muted", "→ ") + formatFlowToolCall(item.name, item.args, theme.fg.bind(theme));
|
|
450
|
-
const plainText = stripAnsi(lineText);
|
|
451
|
-
const scrambled = scrambleManager.updateText(flowId, `tool#${i}`, plainText, now, isComplete).content;
|
|
452
|
-
container.addChild(new Text(scrambled, 0, 0));
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
if (isComplete) {
|
|
456
|
-
scrambleManager.completeFlow(flowId);
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
// Total stats: dashboard format
|
|
460
|
-
const totalUsage = aggregateFlowUsage(results);
|
|
461
|
-
const totalModel = results[0]?.model;
|
|
462
|
-
const totalStats = formatCompactStats(totalUsage, totalModel);
|
|
463
|
-
container.addChild(new Spacer(1));
|
|
464
|
-
container.addChild(new Text(theme.fg("dim", totalStats), 0, 0));
|
|
465
|
-
return container;
|
|
466
|
-
}
|
|
467
|
-
function renderActivityPanel(results, theme, baseId) {
|
|
468
|
-
const idPrefix = baseId || "panel";
|
|
469
|
-
const container = new Container();
|
|
470
|
-
const maxWidth = process.stdout.columns ?? 80;
|
|
471
|
-
const now = Date.now();
|
|
472
|
-
for (let i = 0; i < results.length; i++) {
|
|
473
|
-
const r = results[i];
|
|
474
|
-
const isLast = i === results.length - 1;
|
|
475
|
-
const flowId = `${idPrefix}#${i}`;
|
|
476
|
-
const stats = formatCompactStats(r.usage, r.model, maxWidth, { skipTokens: true, skipContext: true, hideModel: true });
|
|
477
|
-
// Flash TPS value when it changes
|
|
478
|
-
const tpsMatch = stats.match(/tps:\s*(\S+)/);
|
|
479
|
-
const flowComplete = r.exitCode !== -1;
|
|
480
|
-
let displayStats = stats;
|
|
481
|
-
if (tpsMatch) {
|
|
482
|
-
const scrambledTps = scrambleManager.updateTps(flowId, tpsMatch[1], now, flowComplete, true);
|
|
483
|
-
if (scrambledTps !== tpsMatch[1]) {
|
|
484
|
-
displayStats = stats.replace(tpsMatch[1], scrambledTps);
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
const error = isFlowError(r);
|
|
488
|
-
const typeName = formatCollapsedFlowHeaderTypeName(r.type);
|
|
489
|
-
// Header line
|
|
490
|
-
const headerPrefix = isLast ? "└─" : "├─";
|
|
491
|
-
const modelLabel = r.model ? r.model.replace(/^[^/]+\//, "").toLowerCase() : "";
|
|
492
|
-
let headerLine = `${theme.fg("dim", headerPrefix)} ${theme.fg("accent", theme.bold(typeName))}${theme.fg("dim", modelLabel ? ` - ${modelLabel} - ` : " - ")}${theme.fg("dim", displayStats)}`;
|
|
493
|
-
if (error && r.stopReason) {
|
|
494
|
-
headerLine += ` ${theme.fg("error", `[${r.stopReason}]`)}`;
|
|
495
|
-
}
|
|
496
|
-
const plainHeader = headerPrefix + " " + typeName + (modelLabel ? ` - ${modelLabel} - ` : " - ") + stripAnsi(displayStats) + (error && r.stopReason ? ` [${r.stopReason}]` : "");
|
|
497
|
-
const headerResult = scrambleManager.updateText(flowId, 'header', plainHeader, now, flowComplete, true);
|
|
498
|
-
const headerDisplay = headerResult.isAnimating ? theme.fg("accent", headerResult.content) : headerLine;
|
|
499
|
-
container.addChild(new TruncatedText(headerDisplay, 0, 0));
|
|
500
|
-
// Continuation indent for sub-lines
|
|
501
|
-
const indent = isLast ? " " : "│ ";
|
|
502
|
-
// aim: line — cascade/ripple/illuminate on text change
|
|
503
|
-
if (r.aim) {
|
|
504
|
-
const countdown = getLiveCountdown(r);
|
|
505
|
-
const treePrefix = indent + "├─";
|
|
506
|
-
const aimPrefix = countdown
|
|
507
|
-
? `${treePrefix} aim: [${countdown}] - `
|
|
508
|
-
: `${treePrefix} aim: `;
|
|
509
|
-
const budget = getTruncationBudget(visibleLength(aimPrefix));
|
|
510
|
-
const displayAim = truncateChars(lowerFirstWord(r.aim), budget);
|
|
511
|
-
const aimResult = scrambleManager.updateAim(flowId, displayAim, now, flowComplete, true);
|
|
512
|
-
const aimContent = aimResult.content;
|
|
513
|
-
container.addChild(new TruncatedText(`${theme.fg("dim", aimPrefix)}${theme.fg("dim", italic(aimContent))}`, 0, 0));
|
|
514
|
-
}
|
|
515
|
-
// act: line (last tool call with count)
|
|
516
|
-
const lastTool = getLastToolCall(r.messages);
|
|
517
|
-
if (lastTool) {
|
|
518
|
-
const actStr = formatFlowToolCall(lastTool.name, lastTool.args, theme.fg.bind(theme));
|
|
519
|
-
const prefixStub = `${indent}├─ act: [${r.usage.toolCalls}] - `;
|
|
520
|
-
const budget = getTruncationBudget(visibleLength(prefixStub));
|
|
521
|
-
const actFullText = stripAnsi(lowerFirstWord(actStr));
|
|
522
|
-
let actContent;
|
|
523
|
-
if (scrambleManager.getMode() === 'stream') {
|
|
524
|
-
actContent = scrambleManager.streamAct(flowId, actFullText, now, flowComplete, budget);
|
|
525
|
-
}
|
|
526
|
-
else {
|
|
527
|
-
const displayAct = truncateChars(actFullText, budget);
|
|
528
|
-
actContent = scrambleManager.updateAct(flowId, displayAct, now, flowComplete, true).content;
|
|
529
|
-
}
|
|
530
|
-
let actKpi = String(r.usage.toolCalls);
|
|
531
|
-
const scrambledActKpi = scrambleManager.updateActKpi(flowId, actKpi, now, flowComplete, false);
|
|
532
|
-
if (scrambledActKpi !== actKpi) {
|
|
533
|
-
actKpi = scrambledActKpi;
|
|
534
|
-
}
|
|
535
|
-
const actPrefix = `${indent}├─ act: [${actKpi}] - `;
|
|
536
|
-
container.addChild(new TruncatedText(`${theme.fg("dim", actPrefix)}${italic(actContent)}`, 0, 0));
|
|
537
|
-
}
|
|
538
|
-
// msg: line (live streaming text or last assistant text)
|
|
539
|
-
let msgKpi = formatCompactTokenPair(r.usage);
|
|
540
|
-
const scrambledMsgKpi = scrambleManager.updateMsgKpi(flowId, msgKpi, now, flowComplete, false);
|
|
541
|
-
if (scrambledMsgKpi !== msgKpi) {
|
|
542
|
-
msgKpi = scrambledMsgKpi;
|
|
543
|
-
}
|
|
544
|
-
const msgPrefixStub = `${indent}└─ msg: [${msgKpi}] - `;
|
|
545
|
-
const msgBudget = getTruncationBudget(visibleLength(msgPrefixStub));
|
|
546
|
-
const liveText = r.exitCode === -1 ? r.streamingText : undefined;
|
|
547
|
-
const lastText = liveText || getLastAssistantText(r.messages);
|
|
548
|
-
let rawMsg;
|
|
549
|
-
let useError = false;
|
|
550
|
-
if (lastText) {
|
|
551
|
-
rawMsg = stripAnsi(lastText);
|
|
552
|
-
}
|
|
553
|
-
else if (error && r.errorMessage) {
|
|
554
|
-
rawMsg = stripAnsi(r.errorMessage);
|
|
555
|
-
useError = true;
|
|
556
|
-
}
|
|
557
|
-
else {
|
|
558
|
-
rawMsg = "[n/a]";
|
|
559
|
-
}
|
|
560
|
-
let msgContent;
|
|
561
|
-
if (scrambleManager.getMode() === 'stream') {
|
|
562
|
-
msgContent = scrambleManager.streamMsg(flowId, rawMsg, now, flowComplete, msgBudget);
|
|
563
|
-
}
|
|
564
|
-
else {
|
|
565
|
-
// For active (incomplete) flows, pass full text to keep animation stable.
|
|
566
|
-
// TruncatedText handles display truncation. Completed flows truncate as before.
|
|
567
|
-
if (!flowComplete) {
|
|
568
|
-
msgContent = scrambleManager.updateMsg(flowId, rawMsg, now, flowComplete, undefined, true).content;
|
|
569
|
-
}
|
|
570
|
-
else {
|
|
571
|
-
const needsTail = Boolean(liveText || lastText);
|
|
572
|
-
const displayMsg = needsTail ? tailText(rawMsg, msgBudget) : truncateChars(rawMsg, msgBudget);
|
|
573
|
-
msgContent = scrambleManager.updateMsg(flowId, displayMsg, now, flowComplete).content;
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
const msgPrefix = `${indent}└─ msg: [${msgKpi}] - `;
|
|
577
|
-
container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg(useError ? "error" : "dim", italic(msgContent))}`, 0, 0));
|
|
578
|
-
if (flowComplete) {
|
|
579
|
-
scrambleManager.completeFlow(flowId);
|
|
580
|
-
}
|
|
581
|
-
// Add blank line separator between flows (with continuation pipe)
|
|
582
|
-
if (!isLast) {
|
|
583
|
-
container.addChild(new TruncatedText(theme.fg("dim", "│"), 0, 0));
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
container.addChild(new TruncatedText(theme.fg("muted", "(Ctrl+O to expand tool traces)"), 0, 0));
|
|
587
|
-
return container;
|
|
588
|
-
}
|
|
589
|
-
function renderMultiFlowCollapsed(results, theme, baseId) {
|
|
590
|
-
return renderActivityPanel(results, theme, baseId);
|
|
591
|
-
}
|
|
592
|
-
//# sourceMappingURL=render.js.map
|