pi-agent-flow 1.8.1 → 1.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -30
- package/agents/audit.md +1 -2
- package/agents/build.md +1 -0
- package/agents/craft.md +12 -8
- package/agents/debug.md +2 -2
- package/agents/ideas.md +1 -0
- package/agents/scout.md +1 -0
- package/dist/agents.d.ts +41 -0
- package/dist/agents.d.ts.map +1 -0
- package/dist/agents.js +283 -0
- package/dist/agents.js.map +1 -0
- package/dist/batch/batch-bash.d.ts +87 -0
- package/dist/batch/batch-bash.d.ts.map +1 -0
- package/dist/batch/batch-bash.js +369 -0
- package/dist/batch/batch-bash.js.map +1 -0
- package/dist/batch/constants.d.ts +100 -0
- package/dist/batch/constants.d.ts.map +1 -0
- package/dist/batch/constants.js +15 -0
- package/dist/batch/constants.js.map +1 -0
- package/dist/batch/execute.d.ts +21 -0
- package/dist/batch/execute.d.ts.map +1 -0
- package/dist/batch/execute.js +440 -0
- package/dist/batch/execute.js.map +1 -0
- package/dist/batch/fuzzy-edit.d.ts +29 -0
- package/dist/batch/fuzzy-edit.d.ts.map +1 -0
- package/dist/batch/fuzzy-edit.js +257 -0
- package/dist/batch/fuzzy-edit.js.map +1 -0
- package/dist/batch/index.d.ts +85 -0
- package/dist/batch/index.d.ts.map +1 -0
- package/dist/batch/index.js +422 -0
- package/dist/batch/index.js.map +1 -0
- package/dist/batch/render.d.ts +14 -0
- package/dist/batch/render.d.ts.map +1 -0
- package/dist/batch/render.js +74 -0
- package/dist/batch/render.js.map +1 -0
- package/dist/batch/symbols.d.ts +9 -0
- package/dist/batch/symbols.d.ts.map +1 -0
- package/dist/batch/symbols.js +310 -0
- package/dist/batch/symbols.js.map +1 -0
- package/dist/batch.d.ts +12 -0
- package/dist/batch.d.ts.map +1 -0
- package/dist/batch.js +11 -0
- package/dist/batch.js.map +1 -0
- package/dist/cli-args.d.ts +27 -0
- package/dist/cli-args.d.ts.map +1 -0
- package/dist/cli-args.js +265 -0
- package/dist/cli-args.js.map +1 -0
- package/dist/config.d.ts +58 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +296 -0
- package/dist/config.js.map +1 -0
- package/dist/depth.d.ts +25 -0
- package/dist/depth.d.ts.map +1 -0
- package/dist/depth.js +160 -0
- package/dist/depth.js.map +1 -0
- package/dist/executor.d.ts +87 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +295 -0
- package/dist/executor.js.map +1 -0
- package/dist/flow-prompt.d.ts +23 -0
- package/dist/flow-prompt.d.ts.map +1 -0
- package/dist/flow-prompt.js +99 -0
- package/dist/flow-prompt.js.map +1 -0
- package/dist/flow.d.ts +76 -0
- package/dist/flow.d.ts.map +1 -0
- package/dist/flow.js +704 -0
- package/dist/flow.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +327 -0
- package/dist/index.js.map +1 -0
- package/dist/reasoning-strip.d.ts +26 -0
- package/dist/reasoning-strip.d.ts.map +1 -0
- package/dist/reasoning-strip.js +58 -0
- package/dist/reasoning-strip.js.map +1 -0
- package/dist/render-utils.d.ts +42 -0
- package/dist/render-utils.d.ts.map +1 -0
- package/dist/render-utils.js +182 -0
- package/dist/render-utils.js.map +1 -0
- package/dist/render.d.ts +24 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +409 -0
- package/dist/render.js.map +1 -0
- package/dist/runner-events.d.ts +59 -0
- package/dist/runner-events.d.ts.map +1 -0
- package/dist/runner-events.js +539 -0
- package/dist/runner-events.js.map +1 -0
- package/dist/session-mode.d.ts +10 -0
- package/dist/session-mode.d.ts.map +1 -0
- package/dist/session-mode.js +25 -0
- package/dist/session-mode.js.map +1 -0
- package/dist/settings-resolver.d.ts +28 -0
- package/dist/settings-resolver.d.ts.map +1 -0
- package/dist/settings-resolver.js +148 -0
- package/dist/settings-resolver.js.map +1 -0
- package/dist/sliding-prompt.d.ts +40 -0
- package/dist/sliding-prompt.d.ts.map +1 -0
- package/dist/sliding-prompt.js +121 -0
- package/dist/sliding-prompt.js.map +1 -0
- package/dist/snapshot.d.ts +29 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +199 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/structured-output.d.ts +36 -0
- package/dist/structured-output.d.ts.map +1 -0
- package/dist/structured-output.js +244 -0
- package/dist/structured-output.js.map +1 -0
- package/dist/timed-bash.d.ts +45 -0
- package/dist/timed-bash.d.ts.map +1 -0
- package/dist/timed-bash.js +219 -0
- package/dist/timed-bash.js.map +1 -0
- package/dist/tool-utils.d.ts +20 -0
- package/dist/tool-utils.d.ts.map +1 -0
- package/dist/tool-utils.js +38 -0
- package/dist/tool-utils.js.map +1 -0
- package/dist/transitions.d.ts +39 -0
- package/dist/transitions.d.ts.map +1 -0
- package/dist/transitions.js +59 -0
- package/dist/transitions.js.map +1 -0
- package/dist/types.d.ts +207 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +143 -0
- package/dist/types.js.map +1 -0
- package/dist/web-tool.d.ts +35 -0
- package/dist/web-tool.d.ts.map +1 -0
- package/dist/web-tool.js +545 -0
- package/dist/web-tool.js.map +1 -0
- package/package.json +7 -5
- package/src/agents.ts +0 -299
- package/src/ambient.d.ts +0 -107
- package/src/batch/batch-bash.ts +0 -443
- package/src/batch/constants.ts +0 -128
- package/src/batch/execute.ts +0 -551
- package/src/batch/fuzzy-edit.ts +0 -323
- package/src/batch/index.ts +0 -494
- package/src/batch/render.ts +0 -81
- package/src/batch/symbols.ts +0 -341
- package/src/batch.ts +0 -28
- package/src/cli-args.ts +0 -315
- package/src/config.ts +0 -391
- package/src/executor.ts +0 -445
- package/src/flow.ts +0 -834
- package/src/hooks.ts +0 -294
- package/src/index.ts +0 -1132
- package/src/render-utils.ts +0 -205
- package/src/render.ts +0 -524
- package/src/runner-events.ts +0 -692
- package/src/session-mode.ts +0 -33
- package/src/sliding-prompt.ts +0 -144
- package/src/structured-output.ts +0 -195
- package/src/timed-bash.ts +0 -270
- package/src/transitions.ts +0 -86
- package/src/types.ts +0 -386
- package/src/web-tool.ts +0 -663
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure utility functions for rendering - extracted for testability.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Format a token count to exactly 5 characters with leading spaces.
|
|
6
|
+
* Shifts from k to M when value would exceed 5 chars.
|
|
7
|
+
* Examples: 500 → " 500", 1300 → " 1.3k", 32000 → "32.0k", 950500 → "0.95M"
|
|
8
|
+
*/
|
|
9
|
+
export function formatFixedTokens(count) {
|
|
10
|
+
if (count < 1000) {
|
|
11
|
+
return count.toString().padStart(5);
|
|
12
|
+
}
|
|
13
|
+
const k = count / 1000;
|
|
14
|
+
if (k < 100) {
|
|
15
|
+
return (k.toFixed(1) + "k").padStart(5);
|
|
16
|
+
}
|
|
17
|
+
else if (k < 1000) {
|
|
18
|
+
const m = count / 1000000;
|
|
19
|
+
return (m.toFixed(2) + "M").padStart(5);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
const m = count / 1000000;
|
|
23
|
+
return (m.toFixed(2) + "M").padStart(5);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Format flow type name to fixed width (5 chars) in lowercase with space padding.
|
|
28
|
+
* Examples: "debug" → "debug", "scout" → "scout", "build" → "build"
|
|
29
|
+
*/
|
|
30
|
+
export function formatFlowTypeName(type) {
|
|
31
|
+
const lower = type.toLowerCase();
|
|
32
|
+
const targetWidth = 5;
|
|
33
|
+
if (lower.length >= targetWidth)
|
|
34
|
+
return lower.slice(0, targetWidth);
|
|
35
|
+
return lower.padEnd(targetWidth, " ");
|
|
36
|
+
}
|
|
37
|
+
/** Format tokens-per-second to a 5-char display (e.g., " 42.3", " -"). */
|
|
38
|
+
function formatTps(value) {
|
|
39
|
+
if (!value || value <= 0)
|
|
40
|
+
return " -";
|
|
41
|
+
return value.toFixed(1).padStart(5);
|
|
42
|
+
}
|
|
43
|
+
export function formatCompactTokenPair(usage) {
|
|
44
|
+
return `↑ ${formatFixedTokens(usage.input || 0)} · ↓ ${formatFixedTokens(usage.output || 0)}`;
|
|
45
|
+
}
|
|
46
|
+
export function formatCompactStats(usage, model, maxWidth, options = {}) {
|
|
47
|
+
const tokenParts = [`↑ ${formatFixedTokens(usage.input || 0)}`, `↓ ${formatFixedTokens(usage.output || 0)}`];
|
|
48
|
+
let runtimeParts = [`tps: ${formatTps(usage.smoothedTps)}`];
|
|
49
|
+
if (!options.skipContext) {
|
|
50
|
+
runtimeParts.push(`ctx: ${formatFixedTokens(usage.contextTokens || 0)}`);
|
|
51
|
+
}
|
|
52
|
+
const parts = options.skipTokens ? runtimeParts : [...tokenParts, ...runtimeParts];
|
|
53
|
+
const displayModel = options.hideModel ? undefined : (model ? model.replace(/^[^/]+\//, "") : undefined);
|
|
54
|
+
let result = parts.join(" · ") + (displayModel ? ` · ${displayModel}` : "");
|
|
55
|
+
if (maxWidth && visibleLength(result) > maxWidth) {
|
|
56
|
+
// Drop model first.
|
|
57
|
+
let narrow = parts.join(" · ");
|
|
58
|
+
if (visibleLength(narrow) <= maxWidth)
|
|
59
|
+
return narrow;
|
|
60
|
+
// Drop context tokens next.
|
|
61
|
+
const withoutContext = parts.filter((part) => !part.startsWith("ctx:"));
|
|
62
|
+
narrow = withoutContext.join(" · ");
|
|
63
|
+
if (visibleLength(narrow) <= maxWidth)
|
|
64
|
+
return narrow;
|
|
65
|
+
// Bare minimum: token pair for normal stats, tps for token-free headers.
|
|
66
|
+
narrow = options.skipTokens ? runtimeParts[0] : tokenParts.join(" · ");
|
|
67
|
+
if (visibleLength(narrow) <= maxWidth)
|
|
68
|
+
return narrow;
|
|
69
|
+
return truncateChars(result, maxWidth);
|
|
70
|
+
}
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
export function formatCountdown(ms) {
|
|
74
|
+
const totalSeconds = Math.max(0, Math.ceil(ms / 1000));
|
|
75
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
76
|
+
const seconds = totalSeconds % 60;
|
|
77
|
+
return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
|
|
78
|
+
}
|
|
79
|
+
/** Regex matching ANSI escape sequences. */
|
|
80
|
+
const ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
81
|
+
/** Return the visible (ANSI-stripped) character count. */
|
|
82
|
+
export function visibleLength(text) {
|
|
83
|
+
return text.replace(ANSI_RE, "").length;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Compute the remaining visible-character budget for a line,
|
|
87
|
+
* given the length of its prefix (indent + label + space).
|
|
88
|
+
* Respects `process.stdout.columns` with a floor of 40 and default of 80.
|
|
89
|
+
*/
|
|
90
|
+
export function getTruncationBudget(prefixLength) {
|
|
91
|
+
const cols = process.stdout.columns ?? 80;
|
|
92
|
+
const width = Math.max(cols, 40);
|
|
93
|
+
return Math.max(width - prefixLength, 1);
|
|
94
|
+
}
|
|
95
|
+
/** Fixed content budget for collapsed-line text (dir/act/log). */
|
|
96
|
+
export const CONTENT_MAX = 60;
|
|
97
|
+
/** Longer budget for detail lines (act/msg) to show more content. */
|
|
98
|
+
export const DETAIL_CONTENT_MAX = 80;
|
|
99
|
+
/**
|
|
100
|
+
* Compute how many visible chars of content fit after a prefix,
|
|
101
|
+
* using the given budget (defaults to CONTENT_MAX). Floor of 8 to keep things readable.
|
|
102
|
+
*/
|
|
103
|
+
export function contentBudget(prefixVisibleLen, max = CONTENT_MAX) {
|
|
104
|
+
return Math.max(max - prefixVisibleLen, 8);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Truncate an ANSI-colored string to at most `max` visible characters,
|
|
108
|
+
* preserving ANSI codes in the kept portions. Does not inject reset codes
|
|
109
|
+
* — the caller is responsible for closing any open styles.
|
|
110
|
+
*
|
|
111
|
+
* Refactored to perform head-truncation (start + ...) instead of middle-truncation.
|
|
112
|
+
*/
|
|
113
|
+
function truncateAnsi(text, max) {
|
|
114
|
+
if (visibleLength(text) <= max)
|
|
115
|
+
return text;
|
|
116
|
+
// Walk through the string, collecting raw chars until we've consumed
|
|
117
|
+
// `count` visible characters. ANSI sequences are copied through without
|
|
118
|
+
// counting toward the limit.
|
|
119
|
+
function takeVisible(src, count) {
|
|
120
|
+
let raw = "";
|
|
121
|
+
let visible = 0;
|
|
122
|
+
let i = 0;
|
|
123
|
+
while (i < src.length && visible < count) {
|
|
124
|
+
// Check for ANSI escape sequence at current position
|
|
125
|
+
if (src[i] === "\x1b" && src[i + 1] === "[") {
|
|
126
|
+
const end = src.indexOf("m", i + 2);
|
|
127
|
+
if (end !== -1) {
|
|
128
|
+
const seq = src.slice(i, end + 1);
|
|
129
|
+
if (/^\x1b\[[0-9;]*m$/.test(seq)) {
|
|
130
|
+
raw += seq;
|
|
131
|
+
i = end + 1;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
raw += src[i];
|
|
137
|
+
visible++;
|
|
138
|
+
i++;
|
|
139
|
+
}
|
|
140
|
+
return { raw, consumed: visible };
|
|
141
|
+
}
|
|
142
|
+
if (max < 3) {
|
|
143
|
+
// Not enough room for '...' — just truncate without ellipsis
|
|
144
|
+
const { raw } = takeVisible(text, max);
|
|
145
|
+
return raw;
|
|
146
|
+
}
|
|
147
|
+
const keep = max - 3; // 3 = '...'.length
|
|
148
|
+
const ellipsis = "...";
|
|
149
|
+
// Take head from the start
|
|
150
|
+
const headResult = takeVisible(text, keep);
|
|
151
|
+
return headResult.raw + ellipsis;
|
|
152
|
+
}
|
|
153
|
+
export function truncateChars(text, max) {
|
|
154
|
+
text = text.replace(/[\n\r\t]+/g, " ").replace(/ +/g, " ").trim();
|
|
155
|
+
if (visibleLength(text) <= max)
|
|
156
|
+
return text;
|
|
157
|
+
return truncateAnsi(text, max);
|
|
158
|
+
}
|
|
159
|
+
export function tailText(text, max) {
|
|
160
|
+
const flat = text.replace(/[\n\r\t]+/g, " ").replace(/ +/g, " ").trim();
|
|
161
|
+
if (visibleLength(flat) <= max)
|
|
162
|
+
return flat;
|
|
163
|
+
// Take last `max` visible characters from the end
|
|
164
|
+
let visible = 0;
|
|
165
|
+
let i = flat.length - 1;
|
|
166
|
+
while (i >= 0 && visible < max) {
|
|
167
|
+
if (flat[i] === "m") {
|
|
168
|
+
const escStart = flat.lastIndexOf("\x1b[", i);
|
|
169
|
+
if (escStart !== -1 && escStart < i) {
|
|
170
|
+
const seq = flat.slice(escStart, i + 1);
|
|
171
|
+
if (/^\x1b\[[0-9;]*m$/.test(seq)) {
|
|
172
|
+
i = escStart - 1;
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
visible++;
|
|
178
|
+
i--;
|
|
179
|
+
}
|
|
180
|
+
return flat.slice(i + 1);
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=render-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render-utils.js","sourceRoot":"","sources":["../src/render-utils.ts"],"names":[],"mappings":"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"}
|
package/dist/render.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
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 { Container, Text } from "@mariozechner/pi-tui";
|
|
8
|
+
type ThemeFg = (color: string, text: string) => string;
|
|
9
|
+
type ThemeBg = (color: string, text: string) => string;
|
|
10
|
+
type FlowTheme = {
|
|
11
|
+
fg: ThemeFg;
|
|
12
|
+
bold: (s: string) => string;
|
|
13
|
+
bg: ThemeBg;
|
|
14
|
+
};
|
|
15
|
+
export declare function renderFlowCall(args: Record<string, any>, theme: FlowTheme): Text;
|
|
16
|
+
export declare function renderFlowResult(result: {
|
|
17
|
+
content: Array<{
|
|
18
|
+
type: string;
|
|
19
|
+
text?: string;
|
|
20
|
+
}>;
|
|
21
|
+
details?: unknown;
|
|
22
|
+
}, expanded: boolean, theme: FlowTheme, args?: Record<string, any>): Container | Text;
|
|
23
|
+
export {};
|
|
24
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,SAAS,EAAoB,IAAI,EAAiB,MAAM,sBAAsB,CAAC;AAsBxF,KAAK,OAAO,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AACvD,KAAK,OAAO,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AACvD,KAAK,SAAS,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAAC,EAAE,EAAE,OAAO,CAAA;CAAE,CAAC;AAiI3E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAKhF;AAMD,wBAAgB,gBAAgB,CAC/B,MAAM,EAAE;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,EAC9E,QAAQ,EAAE,OAAO,EACjB,KAAK,EAAE,SAAS,EAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GACxB,SAAS,GAAG,IAAI,CA4BlB"}
|
package/dist/render.js
ADDED
|
@@ -0,0 +1,409 @@
|
|
|
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 { formatCompactStats, formatCompactTokenPair, formatCountdown, formatFlowTypeName, truncateChars, tailText, contentBudget, visibleLength, DETAIL_CONTENT_MAX } from "./render-utils.js";
|
|
13
|
+
function shortenPath(p) {
|
|
14
|
+
const home = os.homedir();
|
|
15
|
+
return p.startsWith(home) ? `~${p.slice(home.length)}` : p;
|
|
16
|
+
}
|
|
17
|
+
function formatCollapsedFlowHeaderTypeName(type) {
|
|
18
|
+
return type.toLowerCase();
|
|
19
|
+
}
|
|
20
|
+
function formatFlowToolCall(toolName, args, fg) {
|
|
21
|
+
const pathArg = (args.file_path || args.path || "...");
|
|
22
|
+
switch (toolName) {
|
|
23
|
+
case "bash": {
|
|
24
|
+
const cmd = (args.command || "...").replace(/[\n\r\t]+/g, " ").replace(/ +/g, " ").trim();
|
|
25
|
+
return fg("muted", "$ ") + fg("toolOutput", cmd);
|
|
26
|
+
}
|
|
27
|
+
case "read": {
|
|
28
|
+
let text = fg("accent", shortenPath(pathArg));
|
|
29
|
+
const offset = args.offset;
|
|
30
|
+
const limit = args.limit;
|
|
31
|
+
if (offset !== undefined || limit !== undefined) {
|
|
32
|
+
const start = offset ?? 1;
|
|
33
|
+
const end = limit !== undefined ? start + limit - 1 : "";
|
|
34
|
+
text += fg("warning", `:${start}${end ? `-${end}` : ""}`);
|
|
35
|
+
}
|
|
36
|
+
return fg("muted", "read ") + text;
|
|
37
|
+
}
|
|
38
|
+
case "write": {
|
|
39
|
+
const lines = (args.content || "").split("\n").length;
|
|
40
|
+
let text = fg("muted", "write ") + fg("accent", shortenPath(pathArg));
|
|
41
|
+
if (lines > 1)
|
|
42
|
+
text += fg("dim", ` (${lines} lines)`);
|
|
43
|
+
return text;
|
|
44
|
+
}
|
|
45
|
+
case "edit":
|
|
46
|
+
return fg("muted", "edit ") + fg("accent", shortenPath(pathArg));
|
|
47
|
+
case "ls":
|
|
48
|
+
return fg("muted", "ls ") + fg("accent", shortenPath((args.path || ".")));
|
|
49
|
+
case "find":
|
|
50
|
+
return fg("muted", "find ") + fg("accent", (args.pattern || "*")) + fg("dim", ` in ${shortenPath((args.path || "."))}`);
|
|
51
|
+
case "grep":
|
|
52
|
+
return fg("muted", "grep ") + fg("accent", `/${(args.pattern || "")}/`) + fg("dim", ` in ${shortenPath((args.path || "."))}`);
|
|
53
|
+
case "batch":
|
|
54
|
+
case "batch_read": {
|
|
55
|
+
const ops = Array.isArray(args.o) ? args.o : Array.isArray(args.op) ? args.op : Array.isArray(args.operations) ? args.operations : Array.isArray(args) ? args : [];
|
|
56
|
+
if (ops.length === 0)
|
|
57
|
+
return fg("muted", `${toolName} (empty)`);
|
|
58
|
+
const parts = [];
|
|
59
|
+
for (const op of ops) {
|
|
60
|
+
const opObj = op;
|
|
61
|
+
const opName = (opObj.o ?? opObj.op ?? "?");
|
|
62
|
+
const opPath = (opObj.p ?? opObj.path ?? "?");
|
|
63
|
+
const shortPath = shortenPath(opPath);
|
|
64
|
+
if (opName === "edit") {
|
|
65
|
+
const edits = (opObj.e ?? opObj.edits);
|
|
66
|
+
const blockInfo = edits && edits.length > 1 ? ` (${edits.length} blocks)` : "";
|
|
67
|
+
parts.push(`edit ${shortPath}${blockInfo}`);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
parts.push(`${opName} ${shortPath}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const summary = parts.length <= 3 ? parts.join(", ") : `${parts.slice(0, 2).join(", ")} +${parts.length - 2} more`;
|
|
74
|
+
return fg("muted", `${toolName} `) + fg("accent", summary);
|
|
75
|
+
}
|
|
76
|
+
default:
|
|
77
|
+
return fg("accent", toolName) + fg("dim", ` ${JSON.stringify(args)}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// Shared rendering building blocks
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
function splitOutputLines(text) {
|
|
84
|
+
const lines = text.replace(/\r\n?/g, "\n").split("\n");
|
|
85
|
+
if (lines.length > 1 && lines[lines.length - 1] === "")
|
|
86
|
+
lines.pop();
|
|
87
|
+
return lines;
|
|
88
|
+
}
|
|
89
|
+
function renderToolTraces(items, theme) {
|
|
90
|
+
const lines = [];
|
|
91
|
+
for (const item of items) {
|
|
92
|
+
if (item.type === "toolCall") {
|
|
93
|
+
lines.push(theme.fg("muted", "→ ") + formatFlowToolCall(item.name, item.args, theme.fg.bind(theme)));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return lines.join("\n");
|
|
97
|
+
}
|
|
98
|
+
function renderFlowReport(output, theme) {
|
|
99
|
+
const lines = splitOutputLines(output);
|
|
100
|
+
return lines.map((line) => theme.fg("toolOutput", line)).join("\n");
|
|
101
|
+
}
|
|
102
|
+
function flowStatusIcon(r, theme) {
|
|
103
|
+
if (r.exitCode === -1)
|
|
104
|
+
return theme.fg("warning", "⏳");
|
|
105
|
+
return isFlowError(r) ? theme.fg("error", "✗") : theme.fg("success", "✓");
|
|
106
|
+
}
|
|
107
|
+
/** Center a label in a fixed-width header using em-dashes. Total width = 20. */
|
|
108
|
+
function sectionHeader(label) {
|
|
109
|
+
const total = 20;
|
|
110
|
+
const innerLen = label.length + 2; // account for spaces around label
|
|
111
|
+
const side = (total - innerLen) / 2;
|
|
112
|
+
const left = "─".repeat(Math.floor(side));
|
|
113
|
+
const right = "─".repeat(Math.ceil(side));
|
|
114
|
+
return `${left} ${label} ${right}`;
|
|
115
|
+
}
|
|
116
|
+
function getLiveCountdown(r) {
|
|
117
|
+
if (r.exitCode !== -1 || typeof r.deadlineAtMs !== "number")
|
|
118
|
+
return undefined;
|
|
119
|
+
return formatCountdown(r.deadlineAtMs - Date.now());
|
|
120
|
+
}
|
|
121
|
+
function formatAimLinePrefix(treePrefix, r) {
|
|
122
|
+
const countdown = getLiveCountdown(r);
|
|
123
|
+
return countdown ? `${treePrefix} aim: [${countdown}] - ` : `${treePrefix} aim: `;
|
|
124
|
+
}
|
|
125
|
+
function formatMsgLinePrefix(treePrefix, r) {
|
|
126
|
+
return `${treePrefix} msg: [${formatCompactTokenPair(r.usage)}] - `;
|
|
127
|
+
}
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
// renderFlowCall — shown while the flow is being invoked
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
export function renderFlowCall(args, theme) {
|
|
132
|
+
const flows = args.flow;
|
|
133
|
+
// Minimal — renderFlowResult owns the full display
|
|
134
|
+
return new Text("", 0, 0);
|
|
135
|
+
}
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// renderFlowResult — shown after the flow completes
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
export function renderFlowResult(result, expanded, theme, args) {
|
|
140
|
+
const details = result.details;
|
|
141
|
+
const streamingText = result.content?.[0]?.type === "text" ? result.content[0].text : undefined;
|
|
142
|
+
if (!details || details.results.length === 0) {
|
|
143
|
+
// Ghost Dashboard: render a placeholder status line during the zero state
|
|
144
|
+
const flowRequest = args?.flow?.[0];
|
|
145
|
+
if (flowRequest) {
|
|
146
|
+
const ghostResult = {
|
|
147
|
+
type: flowRequest.type || "unknown",
|
|
148
|
+
agentSource: "user",
|
|
149
|
+
intent: flowRequest.intent || "Processing...",
|
|
150
|
+
aim: flowRequest.aim || flowRequest.intent || "Processing...",
|
|
151
|
+
exitCode: -1, // In progress
|
|
152
|
+
messages: [],
|
|
153
|
+
stderr: "",
|
|
154
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0, toolCalls: 0 },
|
|
155
|
+
};
|
|
156
|
+
return renderFlowCollapsed(ghostResult, flowStatusIcon(ghostResult, theme), false, streamingText || "", theme);
|
|
157
|
+
}
|
|
158
|
+
return new Text(streamingText || "", 0, 0);
|
|
159
|
+
}
|
|
160
|
+
if (details.results.length === 1) {
|
|
161
|
+
return renderSingleFlowResult(details.results[0], expanded, theme, streamingText);
|
|
162
|
+
}
|
|
163
|
+
return renderMultiFlowResult(details, expanded, theme);
|
|
164
|
+
}
|
|
165
|
+
// ---------------------------------------------------------------------------
|
|
166
|
+
// Single flow result
|
|
167
|
+
// ---------------------------------------------------------------------------
|
|
168
|
+
function renderSingleFlowResult(r, expanded, theme, streamingText) {
|
|
169
|
+
const error = isFlowError(r);
|
|
170
|
+
const icon = flowStatusIcon(r, theme);
|
|
171
|
+
const displayItems = getFlowDisplayItems(r.messages);
|
|
172
|
+
const flowOutput = getFlowOutput(r.messages);
|
|
173
|
+
if (expanded) {
|
|
174
|
+
return renderFlowExpanded(r, icon, error, displayItems, flowOutput, theme);
|
|
175
|
+
}
|
|
176
|
+
return renderFlowCollapsed(r, icon, error, flowOutput, theme, streamingText);
|
|
177
|
+
}
|
|
178
|
+
function renderFlowExpanded(r, icon, error, displayItems, flowOutput, theme) {
|
|
179
|
+
const mdTheme = getMarkdownTheme();
|
|
180
|
+
const container = new Container();
|
|
181
|
+
// Header: uppercase type name with dots, no icon, no source
|
|
182
|
+
const typeName = formatFlowTypeName(r.type);
|
|
183
|
+
let header = theme.fg("toolTitle", theme.bold(typeName));
|
|
184
|
+
if (error && r.stopReason)
|
|
185
|
+
header += ` ${theme.fg("error", `[${r.stopReason}]`)}`;
|
|
186
|
+
container.addChild(new Text(header, 0, 0));
|
|
187
|
+
if (error && r.errorMessage) {
|
|
188
|
+
container.addChild(new Text(theme.fg("error", `Error: ${r.errorMessage}`), 0, 0));
|
|
189
|
+
}
|
|
190
|
+
// Stats: dashboard format
|
|
191
|
+
const inlineStats = formatCompactStats(r.usage, r.model);
|
|
192
|
+
container.addChild(new Text(theme.fg("dim", inlineStats), 0, 0));
|
|
193
|
+
// Intent
|
|
194
|
+
container.addChild(new Spacer(1));
|
|
195
|
+
container.addChild(new Text(theme.fg("muted", sectionHeader("intent")), 0, 0));
|
|
196
|
+
container.addChild(new Text(theme.fg("dim", r.intent), 0, 0));
|
|
197
|
+
// Flow report (structured output)
|
|
198
|
+
container.addChild(new Spacer(1));
|
|
199
|
+
container.addChild(new Text(theme.fg("muted", sectionHeader("report")), 0, 0));
|
|
200
|
+
// Structured output summary (compact badge when available)
|
|
201
|
+
if (r.structuredOutput) {
|
|
202
|
+
const so = r.structuredOutput;
|
|
203
|
+
const statusColor = so.status === "complete" ? "success" : so.status === "partial" ? "warning" : "error";
|
|
204
|
+
container.addChild(new Text(`${theme.fg(statusColor, `[${so.status}]`)} ${theme.fg("dim", so.summary)}`, 0, 0));
|
|
205
|
+
if (so.files.length > 0) {
|
|
206
|
+
container.addChild(new Text(theme.fg("dim", `Files: ${so.files.map((f) => f.path).join(", ")}`), 0, 0));
|
|
207
|
+
}
|
|
208
|
+
if (so.commands?.length > 0) {
|
|
209
|
+
const cmdLabels = so.commands.map((c) => {
|
|
210
|
+
const short = c.command.length > 30 ? c.command.slice(0, 30) + "..." : c.command;
|
|
211
|
+
return `${c.tool ?? "cmd"}: ${short}`;
|
|
212
|
+
});
|
|
213
|
+
container.addChild(new Text(theme.fg("dim", `Commands: ${cmdLabels.join(", ")}`), 0, 0));
|
|
214
|
+
}
|
|
215
|
+
if (so.notDone.length > 0) {
|
|
216
|
+
const notDoneText = so.notDone.map((item) => {
|
|
217
|
+
const details = [
|
|
218
|
+
item.reason ? `reason: ${item.reason}` : undefined,
|
|
219
|
+
item.blocker ? `blocker: ${item.blocker}` : undefined,
|
|
220
|
+
item.nextStep ? `next: ${item.nextStep}` : undefined,
|
|
221
|
+
].filter(Boolean).join("; ");
|
|
222
|
+
return details ? `${item.item} (${details})` : item.item;
|
|
223
|
+
}).join("; ");
|
|
224
|
+
container.addChild(new Text(theme.fg("dim", `Not Done: ${notDoneText}`), 0, 0));
|
|
225
|
+
}
|
|
226
|
+
if (so.nextSteps.length > 0) {
|
|
227
|
+
container.addChild(new Text(theme.fg("dim", `Next: ${so.nextSteps.join("; ")}`), 0, 0));
|
|
228
|
+
}
|
|
229
|
+
container.addChild(new Spacer(1));
|
|
230
|
+
}
|
|
231
|
+
if (flowOutput) {
|
|
232
|
+
container.addChild(new Markdown(flowOutput.trim(), 0, 0, mdTheme));
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
const summary = getFlowSummaryText(r);
|
|
236
|
+
container.addChild(new Text(theme.fg("muted", summary), 0, 0));
|
|
237
|
+
}
|
|
238
|
+
// Tool traces (expanded only)
|
|
239
|
+
const toolTraces = renderToolTraces(displayItems, theme);
|
|
240
|
+
if (toolTraces) {
|
|
241
|
+
container.addChild(new Spacer(1));
|
|
242
|
+
container.addChild(new Text(theme.fg("muted", sectionHeader("tool calls")), 0, 0));
|
|
243
|
+
container.addChild(new Text(toolTraces, 0, 0));
|
|
244
|
+
}
|
|
245
|
+
return container;
|
|
246
|
+
}
|
|
247
|
+
function renderFlowCollapsed(r, icon, error, flowOutput, theme, streamingText) {
|
|
248
|
+
const container = new Container();
|
|
249
|
+
const maxWidth = process.stdout.columns ?? 80;
|
|
250
|
+
const stats = formatCompactStats(r.usage, r.model, maxWidth, { skipTokens: true, skipContext: true, hideModel: true });
|
|
251
|
+
const typeName = formatCollapsedFlowHeaderTypeName(r.type);
|
|
252
|
+
const modelLabel = r.model ? r.model.replace(/^[^/]+\//, "") : "";
|
|
253
|
+
let header = `${theme.fg("accent", theme.bold(typeName))}${theme.fg("dim", modelLabel ? ` · ${modelLabel} · ` : " · ")}${theme.fg("dim", stats)}`;
|
|
254
|
+
if (error && r.stopReason)
|
|
255
|
+
header += ` ${theme.fg("error", `[${r.stopReason}]`)}`;
|
|
256
|
+
container.addChild(new TruncatedText(header, 0, 0));
|
|
257
|
+
// aim: line (short headline)
|
|
258
|
+
if (r.aim) {
|
|
259
|
+
const aimPrefix = formatAimLinePrefix("├─", r);
|
|
260
|
+
const dirContent = truncateChars(r.aim, contentBudget(visibleLength(aimPrefix)));
|
|
261
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", aimPrefix)}${theme.fg("dim", dirContent)}`, 0, 0));
|
|
262
|
+
}
|
|
263
|
+
// act: line (last tool call with count)
|
|
264
|
+
const lastTool = getLastToolCall(r.messages);
|
|
265
|
+
if (lastTool) {
|
|
266
|
+
const actStr = formatFlowToolCall(lastTool.name, lastTool.args, theme.fg.bind(theme));
|
|
267
|
+
const actPrefix = `├─ act: [${r.usage.toolCalls}] - `;
|
|
268
|
+
const actContent = truncateChars(actStr, contentBudget(visibleLength(actPrefix), DETAIL_CONTENT_MAX));
|
|
269
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", actPrefix)}${actContent}`, 0, 0));
|
|
270
|
+
}
|
|
271
|
+
// msg: line (last assistant text or streaming)
|
|
272
|
+
const msgPrefix = formatMsgLinePrefix("└─", r);
|
|
273
|
+
const msgBudget = contentBudget(visibleLength(msgPrefix), DETAIL_CONTENT_MAX);
|
|
274
|
+
if (r.exitCode === -1 && streamingText) {
|
|
275
|
+
const logContent = tailText(streamingText, msgBudget);
|
|
276
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg("dim", logContent)}`, 0, 0));
|
|
277
|
+
}
|
|
278
|
+
else if (r.structuredOutput?.summary) {
|
|
279
|
+
const logContent = truncateChars(r.structuredOutput.summary, msgBudget);
|
|
280
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg("dim", logContent)}`, 0, 0));
|
|
281
|
+
}
|
|
282
|
+
else if (flowOutput) {
|
|
283
|
+
const logContent = tailText(flowOutput, msgBudget);
|
|
284
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg("dim", logContent)}`, 0, 0));
|
|
285
|
+
}
|
|
286
|
+
else if (streamingText) {
|
|
287
|
+
const logContent = tailText(streamingText, msgBudget);
|
|
288
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg("dim", logContent)}`, 0, 0));
|
|
289
|
+
}
|
|
290
|
+
else if (error && r.errorMessage) {
|
|
291
|
+
const logContent = truncateChars(r.errorMessage, msgBudget);
|
|
292
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg("error", logContent)}`, 0, 0));
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg("dim", "[n/a]")}`, 0, 0));
|
|
296
|
+
}
|
|
297
|
+
return container;
|
|
298
|
+
}
|
|
299
|
+
// ---------------------------------------------------------------------------
|
|
300
|
+
// Multi-flow result
|
|
301
|
+
// ---------------------------------------------------------------------------
|
|
302
|
+
function renderMultiFlowResult(details, expanded, theme) {
|
|
303
|
+
const results = details.results;
|
|
304
|
+
const successCount = results.filter((r) => isFlowSuccess(r)).length;
|
|
305
|
+
const failCount = results.filter((r) => isFlowError(r)).length;
|
|
306
|
+
const icon = failCount > 0 ? theme.fg("warning", "◐") : theme.fg("success", "✓");
|
|
307
|
+
if (expanded) {
|
|
308
|
+
return renderMultiFlowExpanded(results, successCount, icon, theme);
|
|
309
|
+
}
|
|
310
|
+
return renderMultiFlowCollapsed(results, theme);
|
|
311
|
+
}
|
|
312
|
+
function renderMultiFlowExpanded(results, successCount, icon, theme) {
|
|
313
|
+
const mdTheme = getMarkdownTheme();
|
|
314
|
+
const container = new Container();
|
|
315
|
+
// Summary: just show count, no icon
|
|
316
|
+
container.addChild(new Text(theme.fg("accent", `${results.length} flows`), 0, 0));
|
|
317
|
+
for (const r of results) {
|
|
318
|
+
const displayItems = getFlowDisplayItems(r.messages);
|
|
319
|
+
const flowOutput = getFlowOutput(r.messages);
|
|
320
|
+
const typeName = formatFlowTypeName(r.type);
|
|
321
|
+
container.addChild(new Spacer(1));
|
|
322
|
+
// Per-flow header: ─── EXPLORER (no icon)
|
|
323
|
+
container.addChild(new Text(theme.fg("muted", sectionHeader(typeName)), 0, 0));
|
|
324
|
+
// Stats: dashboard format
|
|
325
|
+
const flowStats = formatCompactStats(r.usage, r.model);
|
|
326
|
+
container.addChild(new Text(theme.fg("dim", flowStats), 0, 0));
|
|
327
|
+
// Intent: just show text, no prefix
|
|
328
|
+
container.addChild(new Text(theme.fg("dim", r.intent), 0, 0));
|
|
329
|
+
if (flowOutput) {
|
|
330
|
+
container.addChild(new Spacer(1));
|
|
331
|
+
container.addChild(new Markdown(flowOutput.trim(), 0, 0, mdTheme));
|
|
332
|
+
}
|
|
333
|
+
// Tool traces in expanded view
|
|
334
|
+
const toolTraces = renderToolTraces(displayItems, theme);
|
|
335
|
+
if (toolTraces) {
|
|
336
|
+
container.addChild(new Spacer(1));
|
|
337
|
+
container.addChild(new Text(theme.fg("muted", sectionHeader("tool calls")), 0, 0));
|
|
338
|
+
container.addChild(new Text(toolTraces, 0, 0));
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
// Total stats: dashboard format
|
|
342
|
+
const totalUsage = aggregateFlowUsage(results);
|
|
343
|
+
const totalModel = results[0]?.model;
|
|
344
|
+
const totalStats = formatCompactStats(totalUsage, totalModel);
|
|
345
|
+
container.addChild(new Spacer(1));
|
|
346
|
+
container.addChild(new Text(theme.fg("dim", totalStats), 0, 0));
|
|
347
|
+
return container;
|
|
348
|
+
}
|
|
349
|
+
function renderActivityPanel(results, theme) {
|
|
350
|
+
const container = new Container();
|
|
351
|
+
const maxWidth = process.stdout.columns ?? 80;
|
|
352
|
+
for (let i = 0; i < results.length; i++) {
|
|
353
|
+
const r = results[i];
|
|
354
|
+
const isLast = i === results.length - 1;
|
|
355
|
+
const stats = formatCompactStats(r.usage, r.model, maxWidth, { skipTokens: true, skipContext: true, hideModel: true });
|
|
356
|
+
const error = isFlowError(r);
|
|
357
|
+
const typeName = formatCollapsedFlowHeaderTypeName(r.type);
|
|
358
|
+
// Header line
|
|
359
|
+
const headerPrefix = isLast ? "└─" : "├─";
|
|
360
|
+
const modelLabel = r.model ? r.model.replace(/^[^/]+\//, "") : "";
|
|
361
|
+
let headerLine = `${theme.fg("dim", headerPrefix)} ${theme.fg("accent", theme.bold(typeName))}${theme.fg("dim", modelLabel ? ` · ${modelLabel} · ` : " · ")}${theme.fg("dim", stats)}`;
|
|
362
|
+
if (error && r.stopReason) {
|
|
363
|
+
headerLine += ` ${theme.fg("error", `[${r.stopReason}]`)}`;
|
|
364
|
+
}
|
|
365
|
+
container.addChild(new TruncatedText(headerLine, 0, 0));
|
|
366
|
+
// Continuation indent for sub-lines
|
|
367
|
+
const indent = isLast ? " " : "│ ";
|
|
368
|
+
// aim: line (short headline)
|
|
369
|
+
if (r.aim) {
|
|
370
|
+
const aimPrefix = formatAimLinePrefix(indent + "├─", r);
|
|
371
|
+
const dirContent = truncateChars(r.aim, contentBudget(visibleLength(aimPrefix)));
|
|
372
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", aimPrefix)}${theme.fg("dim", dirContent)}`, 0, 0));
|
|
373
|
+
}
|
|
374
|
+
// act: line (last tool call with count)
|
|
375
|
+
const lastTool = getLastToolCall(r.messages);
|
|
376
|
+
if (lastTool) {
|
|
377
|
+
const actStr = formatFlowToolCall(lastTool.name, lastTool.args, theme.fg.bind(theme));
|
|
378
|
+
const actPrefix = `${indent}├─ act: [${r.usage.toolCalls}] - `;
|
|
379
|
+
const actContent = truncateChars(actStr, contentBudget(visibleLength(actPrefix), DETAIL_CONTENT_MAX));
|
|
380
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", actPrefix)}${actContent}`, 0, 0));
|
|
381
|
+
}
|
|
382
|
+
// msg: line (live streaming text or last assistant text)
|
|
383
|
+
const msgPrefix = formatMsgLinePrefix(indent + "└─", r);
|
|
384
|
+
const msgBudget = contentBudget(visibleLength(msgPrefix), DETAIL_CONTENT_MAX);
|
|
385
|
+
const liveText = r.exitCode === -1 ? r.streamingText : undefined;
|
|
386
|
+
const lastText = liveText || getLastAssistantText(r.messages);
|
|
387
|
+
if (lastText) {
|
|
388
|
+
const logContent = tailText(lastText, msgBudget);
|
|
389
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg("dim", logContent)}`, 0, 0));
|
|
390
|
+
}
|
|
391
|
+
else if (error && r.errorMessage) {
|
|
392
|
+
const logContent = truncateChars(r.errorMessage, msgBudget);
|
|
393
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg("error", logContent)}`, 0, 0));
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
container.addChild(new TruncatedText(`${theme.fg("dim", msgPrefix)}${theme.fg("dim", "[n/a]")}`, 0, 0));
|
|
397
|
+
}
|
|
398
|
+
// Add blank line separator between flows (with continuation pipe)
|
|
399
|
+
if (!isLast) {
|
|
400
|
+
container.addChild(new TruncatedText(theme.fg("dim", "│"), 0, 0));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
container.addChild(new TruncatedText(theme.fg("muted", "(Ctrl+O to expand tool traces)"), 0, 0));
|
|
404
|
+
return container;
|
|
405
|
+
}
|
|
406
|
+
function renderMultiFlowCollapsed(results, theme) {
|
|
407
|
+
return renderActivityPanel(results, theme);
|
|
408
|
+
}
|
|
409
|
+
//# sourceMappingURL=render.js.map
|