pi-agent-flow 0.2.5 → 0.2.7
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/package.json +1 -1
- package/render-utils.ts +91 -6
- package/render.ts +1 -1
package/package.json
CHANGED
package/render-utils.ts
CHANGED
|
@@ -84,15 +84,100 @@ export function formatExpandedStats(usage: Partial<UsageStats>, model?: string):
|
|
|
84
84
|
return { stats, contextTokens };
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
/** Regex matching ANSI escape sequences. */
|
|
88
|
+
const ANSI_RE = /\x1b\[[0-9;]*m/g;
|
|
89
|
+
|
|
90
|
+
/** Return the visible (ANSI-stripped) character count. */
|
|
91
|
+
export function visibleLength(text: string): number {
|
|
92
|
+
return text.replace(ANSI_RE, "").length;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Truncate an ANSI-colored string to at most `max` visible characters,
|
|
97
|
+
* preserving ANSI codes in the kept portions and appending a reset before
|
|
98
|
+
* the ellipsis so colors don't bleed.
|
|
99
|
+
*/
|
|
100
|
+
function truncateAnsi(text: string, max: number): string {
|
|
101
|
+
if (visibleLength(text) <= max) return text;
|
|
102
|
+
|
|
89
103
|
const head = Math.ceil(max * 0.6);
|
|
90
|
-
const tail = max - head -
|
|
91
|
-
|
|
104
|
+
const tail = max - head - 5; // 5 = " ... ".length
|
|
105
|
+
|
|
106
|
+
// Walk through the string, collecting raw chars until we've consumed
|
|
107
|
+
// `count` visible characters. ANSI sequences are copied through without
|
|
108
|
+
// counting toward the limit.
|
|
109
|
+
function takeVisible(src: string, count: number): { raw: string; consumed: number } {
|
|
110
|
+
let raw = "";
|
|
111
|
+
let visible = 0;
|
|
112
|
+
let i = 0;
|
|
113
|
+
while (i < src.length && visible < count) {
|
|
114
|
+
// Check for ANSI escape sequence at current position
|
|
115
|
+
if (src[i] === "\x1b" && src[i + 1] === "[") {
|
|
116
|
+
const end = src.indexOf("m", i + 2);
|
|
117
|
+
if (end !== -1) {
|
|
118
|
+
raw += src.slice(i, end + 1);
|
|
119
|
+
i = end + 1;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
raw += src[i];
|
|
124
|
+
visible++;
|
|
125
|
+
i++;
|
|
126
|
+
}
|
|
127
|
+
return { raw, consumed: visible };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Take head from the start
|
|
131
|
+
const headResult = takeVisible(text, head);
|
|
132
|
+
|
|
133
|
+
// Take tail from the end (walk backwards)
|
|
134
|
+
function takeVisibleFromEnd(src: string, count: number): string {
|
|
135
|
+
let visible = 0;
|
|
136
|
+
let i = src.length - 1;
|
|
137
|
+
while (i >= 0 && visible < count) {
|
|
138
|
+
// Check if current char is end of an ANSI sequence
|
|
139
|
+
if (src[i] === "m") {
|
|
140
|
+
const escStart = src.lastIndexOf("\x1b[", i);
|
|
141
|
+
if (escStart !== -1 && escStart < i) {
|
|
142
|
+
// This is an ANSI sequence — don't count, skip past it
|
|
143
|
+
i = escStart - 1;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
visible++;
|
|
148
|
+
i--;
|
|
149
|
+
}
|
|
150
|
+
// Extract the raw substring from (i+1) to end, including any trailing ANSI
|
|
151
|
+
return src.slice(i + 1);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const tailRaw = takeVisibleFromEnd(text, tail);
|
|
155
|
+
|
|
156
|
+
return headResult.raw + "\x1b[39m ... " + tailRaw;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function truncateChars(text: string, max: number): string {
|
|
160
|
+
if (visibleLength(text) <= max) return text;
|
|
161
|
+
return truncateAnsi(text, max);
|
|
92
162
|
}
|
|
93
163
|
|
|
94
164
|
export function tailText(text: string, max: number): string {
|
|
95
165
|
const flat = text.replace(/[\n\r\t]+/g, " ").replace(/ +/g, " ").trim();
|
|
96
|
-
if (flat
|
|
97
|
-
|
|
166
|
+
if (visibleLength(flat) <= max) return flat;
|
|
167
|
+
|
|
168
|
+
// Take last `max` visible characters from the end
|
|
169
|
+
let visible = 0;
|
|
170
|
+
let i = flat.length - 1;
|
|
171
|
+
while (i >= 0 && visible < max) {
|
|
172
|
+
if (flat[i] === "m") {
|
|
173
|
+
const escStart = flat.lastIndexOf("\x1b[", i);
|
|
174
|
+
if (escStart !== -1 && escStart < i) {
|
|
175
|
+
i = escStart - 1;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
visible++;
|
|
180
|
+
i--;
|
|
181
|
+
}
|
|
182
|
+
return flat.slice(i + 1);
|
|
98
183
|
}
|
package/render.ts
CHANGED
|
@@ -38,7 +38,7 @@ function formatFlowToolCall(toolName: string, args: Record<string, unknown>, fg:
|
|
|
38
38
|
|
|
39
39
|
switch (toolName) {
|
|
40
40
|
case "bash": {
|
|
41
|
-
const cmd = (args.command as string) || "...";
|
|
41
|
+
const cmd = ((args.command as string) || "...").replace(/[\n\r\t]+/g, " ").replace(/ +/g, " ").trim();
|
|
42
42
|
return fg("muted", "$ ") + fg("toolOutput", cmd);
|
|
43
43
|
}
|
|
44
44
|
case "read": {
|