@esso0428/pi-subagents 0.15.14 → 0.15.16
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/CHANGELOG.md +9 -32
- package/dist/ui/agent-widget.d.ts +8 -0
- package/dist/ui/agent-widget.d.ts.map +1 -1
- package/dist/ui/agent-widget.js +40 -12
- package/dist/ui/agent-widget.js.map +1 -1
- package/dist/ui/ccstyle/diff/diff-renderer.d.ts.map +1 -1
- package/dist/ui/ccstyle/diff/diff-renderer.js +2 -4
- package/dist/ui/ccstyle/diff/diff-renderer.js.map +1 -1
- package/dist/ui/ccstyle/tool-renderer.d.ts.map +1 -1
- package/dist/ui/ccstyle/tool-renderer.js +4 -10
- package/dist/ui/ccstyle/tool-renderer.js.map +1 -1
- package/dist/ui/ccstyle/tool-result.d.ts +9 -16
- package/dist/ui/ccstyle/tool-result.d.ts.map +1 -1
- package/dist/ui/ccstyle/tool-result.js +71 -121
- package/dist/ui/ccstyle/tool-result.js.map +1 -1
- package/dist/ui/conversation-timeline.d.ts +14 -9
- package/dist/ui/conversation-timeline.d.ts.map +1 -1
- package/dist/ui/conversation-timeline.js +60 -72
- package/dist/ui/conversation-timeline.js.map +1 -1
- package/dist/ui/conversation-viewer.d.ts +4 -1
- package/dist/ui/conversation-viewer.d.ts.map +1 -1
- package/dist/ui/conversation-viewer.js +95 -94
- package/dist/ui/conversation-viewer.js.map +1 -1
- package/dist/write-execution.d.ts +0 -2
- package/dist/write-execution.d.ts.map +1 -1
- package/dist/write-execution.js +1 -5
- package/dist/write-execution.js.map +1 -1
- package/package.json +1 -1
- package/src/ui/agent-widget.ts +106 -20
- package/src/ui/ccstyle/diff/diff-renderer.ts +2 -7
- package/src/ui/ccstyle/tool-renderer.ts +9 -22
- package/src/ui/ccstyle/tool-result.ts +82 -186
- package/src/ui/conversation-blocks.ts +13 -11
- package/src/ui/conversation-timeline.ts +211 -122
- package/src/ui/conversation-viewer.ts +149 -125
- package/src/ui/fleet-list.ts +34 -1
- package/src/write-execution.ts +1 -7
- package/test/ui/agent-widget.test.ts +161 -0
- package/test/ui/conversation-diff.test.ts +1 -47
- package/test/ui/conversation-viewer.test.ts +181 -112
- package/test/ui/fleet-list.test.ts +5 -1
- package/test/write-execution.test.ts +0 -2
- package/.pi/tasks/tasks-01a09263-0ac6-77be-97f5-664b3ffea76b.json +0 -53
- package/.pi/workflows/index.json +0 -5
|
@@ -51,21 +51,16 @@ export function outputLineCount(result: any): number {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export function countLines(text: string): number {
|
|
54
|
-
return text.trim() ? text.trim().split("\n").filter((line) => line.trim()
|
|
54
|
+
return text.trim() ? text.trim().split("\n").filter((line) => line.trim()).length : 0;
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/** Format arguments as stable, readable `key: value` rows for the Input rail. */
|
|
58
58
|
export function formatToolInputArgs(args: unknown, maxChars = 8_000): string {
|
|
59
59
|
if (args === undefined || args === null) return "";
|
|
60
|
-
if (typeof args !== "object") {
|
|
61
|
-
const
|
|
62
|
-
return
|
|
60
|
+
if (typeof args !== "object" || Array.isArray(args)) {
|
|
61
|
+
const rendered = typeof args === "string" ? sanitizeToolResultText(args) : safeJson(args);
|
|
62
|
+
return rendered.length > maxChars ? `${rendered.slice(0, maxChars)}…` : rendered;
|
|
63
63
|
}
|
|
64
|
-
if (Array.isArray(args)) {
|
|
65
|
-
const text = safeJson(args);
|
|
66
|
-
return text.length > maxChars ? `${text.slice(0, maxChars)}…` : text;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
64
|
const preferred = [
|
|
70
65
|
"path", "file_path", "command", "query", "pattern", "url", "name",
|
|
71
66
|
"message", "content", "old_string", "new_string",
|
|
@@ -81,32 +76,24 @@ export function formatToolInputArgs(args: unknown, maxChars = 8_000): string {
|
|
|
81
76
|
return li - ri;
|
|
82
77
|
});
|
|
83
78
|
const lines: string[] = [];
|
|
84
|
-
for (const [
|
|
85
|
-
const
|
|
79
|
+
for (const [key, value] of entries) {
|
|
80
|
+
const safeKey = sanitizeToolResultText(key);
|
|
86
81
|
if (typeof value === "string") {
|
|
87
82
|
const safeValue = sanitizeToolResultText(value);
|
|
88
83
|
if (safeValue.includes("\n")) {
|
|
89
|
-
lines.push(`${
|
|
90
|
-
|
|
91
|
-
} else {
|
|
92
|
-
lines.push(`${key}: ${safeValue}`);
|
|
93
|
-
}
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
if (typeof value === "number" || typeof value === "boolean" || value === null) {
|
|
97
|
-
lines.push(`${key}: ${String(value)}`);
|
|
98
|
-
continue;
|
|
99
|
-
}
|
|
100
|
-
const json = safeJson(value);
|
|
101
|
-
if (json.includes("\n")) {
|
|
102
|
-
lines.push(`${key}:`);
|
|
103
|
-
for (const line of json.split("\n")) lines.push(` ${line}`);
|
|
84
|
+
lines.push(`${safeKey}:`);
|
|
85
|
+
lines.push(...safeValue.split("\n").map((line) => ` ${line}`));
|
|
86
|
+
} else lines.push(`${safeKey}: ${safeValue}`);
|
|
104
87
|
} else {
|
|
105
|
-
|
|
88
|
+
const rendered = safeJson(value);
|
|
89
|
+
if (rendered.includes("\n")) {
|
|
90
|
+
lines.push(`${safeKey}:`);
|
|
91
|
+
lines.push(...rendered.split("\n").map((line) => ` ${line}`));
|
|
92
|
+
} else lines.push(`${safeKey}: ${rendered}`);
|
|
106
93
|
}
|
|
107
94
|
}
|
|
108
|
-
const
|
|
109
|
-
return
|
|
95
|
+
const rendered = lines.join("\n");
|
|
96
|
+
return rendered.length > maxChars ? `${rendered.slice(0, maxChars)}…` : rendered;
|
|
110
97
|
}
|
|
111
98
|
|
|
112
99
|
function hasExpandableDetail(text: string, args: unknown): boolean {
|
|
@@ -115,18 +102,8 @@ function hasExpandableDetail(text: string, args: unknown): boolean {
|
|
|
115
102
|
|
|
116
103
|
export type ToolIoSection = "input" | "output";
|
|
117
104
|
|
|
118
|
-
/**
|
|
119
|
-
|
|
120
|
-
* mirror ccstyle, but state is kept on this component so history remains
|
|
121
|
-
* deterministic and no global terminal mouse handler is needed.
|
|
122
|
-
*/
|
|
123
|
-
class ExpandedToolIoView implements Component {
|
|
124
|
-
private readonly isError: boolean;
|
|
125
|
-
private input: string;
|
|
126
|
-
private output: string;
|
|
127
|
-
private maxOutputLines: number;
|
|
128
|
-
private maxInputLines: number;
|
|
129
|
-
private theme: any;
|
|
105
|
+
/** Viewer-local adaptation of pi-cc's structured Input/Output tool card. */
|
|
106
|
+
export class ExpandedToolIoView implements Component {
|
|
130
107
|
private cachedWidth: number | undefined;
|
|
131
108
|
private cachedLines: string[] | undefined;
|
|
132
109
|
private lineSections: Array<ToolIoSection | null> = [];
|
|
@@ -134,28 +111,16 @@ class ExpandedToolIoView implements Component {
|
|
|
134
111
|
private showMoreRows: Partial<Record<ToolIoSection, number>> = {};
|
|
135
112
|
|
|
136
113
|
constructor(
|
|
137
|
-
input: string,
|
|
138
|
-
output: string,
|
|
139
|
-
isError: boolean,
|
|
140
|
-
maxOutputLines = DEFAULT_TOOL_DISPLAY_CONFIG.expandedPreviewMaxLines,
|
|
141
|
-
maxInputLines = DEFAULT_TOOL_DISPLAY_CONFIG.expandedPreviewMaxLines,
|
|
142
|
-
theme?: any,
|
|
143
|
-
) {
|
|
144
|
-
this.input = input;
|
|
145
|
-
this.output = output;
|
|
146
|
-
this.isError = isError;
|
|
147
|
-
this.maxOutputLines = Math.max(1, maxOutputLines);
|
|
148
|
-
this.maxInputLines = Math.max(1, maxInputLines);
|
|
149
|
-
this.theme = theme;
|
|
150
|
-
}
|
|
114
|
+
private input: string,
|
|
115
|
+
private output: string,
|
|
116
|
+
private readonly isError: boolean,
|
|
117
|
+
private readonly maxOutputLines = DEFAULT_TOOL_DISPLAY_CONFIG.expandedPreviewMaxLines,
|
|
118
|
+
private readonly maxInputLines = DEFAULT_TOOL_DISPLAY_CONFIG.expandedPreviewMaxLines,
|
|
119
|
+
private readonly theme?: any,
|
|
120
|
+
) {}
|
|
151
121
|
|
|
152
|
-
getInputBody(): string {
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
getOutputBody(): string {
|
|
157
|
-
return this.output.trim() ? this.output : "Done";
|
|
158
|
-
}
|
|
122
|
+
getInputBody(): string { return this.input; }
|
|
123
|
+
getOutputBody(): string { return this.output.trim() ? this.output : "Done"; }
|
|
159
124
|
|
|
160
125
|
setHoveredSection(section: ToolIoSection | null): void {
|
|
161
126
|
if (this.hoveredSection === section) return;
|
|
@@ -163,47 +128,30 @@ class ExpandedToolIoView implements Component {
|
|
|
163
128
|
this.invalidate();
|
|
164
129
|
}
|
|
165
130
|
|
|
166
|
-
sectionAtLine(line: number): ToolIoSection | null {
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
showMoreLine(section: ToolIoSection): number | undefined {
|
|
171
|
-
return this.showMoreRows[section];
|
|
172
|
-
}
|
|
131
|
+
sectionAtLine(line: number): ToolIoSection | null { return this.lineSections[line] ?? null; }
|
|
132
|
+
showMoreLine(section: ToolIoSection): number | undefined { return this.showMoreRows[section]; }
|
|
173
133
|
|
|
174
134
|
render(width: number): string[] {
|
|
175
135
|
const safeWidth = toolViewportWidth(width);
|
|
176
136
|
if (this.cachedLines && this.cachedWidth === safeWidth) return this.cachedLines;
|
|
177
|
-
|
|
178
|
-
const
|
|
137
|
+
const theme = this.theme;
|
|
138
|
+
const color = (name: string, value: string): string => typeof theme?.fg === "function" ? theme.fg(name, value) : value;
|
|
139
|
+
const bold = (value: string): string => typeof theme?.bold === "function" ? theme.bold(value) : value;
|
|
179
140
|
const rail = " │ ";
|
|
180
141
|
const contentWidth = Math.max(1, safeWidth - visibleWidth(rail));
|
|
142
|
+
const bodyColor = this.isError ? "error" : "toolOutput";
|
|
181
143
|
const rows: string[] = [];
|
|
182
144
|
this.lineSections = [];
|
|
183
145
|
this.showMoreRows = {};
|
|
184
|
-
|
|
185
146
|
const push = (line: string, section: ToolIoSection | null) => {
|
|
186
147
|
rows.push(truncateToWidth(line, safeWidth, ""));
|
|
187
148
|
this.lineSections.push(section);
|
|
188
149
|
};
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
color("dim", ` ${corner} `) + color("accent", bold(title)),
|
|
195
|
-
null,
|
|
196
|
-
);
|
|
197
|
-
const pushRail = (value: string, section: ToolIoSection, continued: boolean) => push(
|
|
198
|
-
color("dim", continued ? rail : " ") + value,
|
|
199
|
-
section,
|
|
200
|
-
);
|
|
201
|
-
const pushBlank = () => push(color("dim", " │"), null);
|
|
202
|
-
const styleInput = (line: string): string => {
|
|
203
|
-
const match = line.match(/^([A-Za-z_][\w.-]*)(:\s*)(.*)$/);
|
|
204
|
-
return match
|
|
205
|
-
? color("dim", `${match[1]}${match[2]}`) + color("muted", match[3] ?? "")
|
|
206
|
-
: color("muted", line);
|
|
150
|
+
const pushHeader = (corner: "├" | "└", title: string) => {
|
|
151
|
+
push(color("dim", ` ${corner} `) + color("accent", bold(title)), null);
|
|
152
|
+
};
|
|
153
|
+
const pushRail = (line: string, section: ToolIoSection, continued: boolean) => {
|
|
154
|
+
push(color("dim", continued ? rail : " ") + line, section);
|
|
207
155
|
};
|
|
208
156
|
const pushBody = (body: string, section: ToolIoSection, limit: number, continued: boolean, input: boolean) => {
|
|
209
157
|
const raw = body.replace(/\t/g, " ").replace(/\n+$/, "");
|
|
@@ -213,20 +161,25 @@ class ExpandedToolIoView implements Component {
|
|
|
213
161
|
}
|
|
214
162
|
const sources = raw.split("\n");
|
|
215
163
|
const wrapped: string[] = [];
|
|
164
|
+
const styleInput = (line: string): string => {
|
|
165
|
+
const match = line.match(/^([A-Za-z_][\w.-]*)(:\s*)(.*)$/);
|
|
166
|
+
return match
|
|
167
|
+
? color("dim", `${match[1]}${match[2]}`) + color("muted", match[3] ?? "")
|
|
168
|
+
: color("muted", line);
|
|
169
|
+
};
|
|
216
170
|
for (const source of sources) {
|
|
217
171
|
const styled = input ? styleInput(source) : color(bodyColor, source);
|
|
218
|
-
const
|
|
219
|
-
wrapped.push(...(
|
|
172
|
+
const lines = wrapTextWithAnsi(styled, contentWidth);
|
|
173
|
+
wrapped.push(...(lines.length ? lines : [styled]));
|
|
220
174
|
}
|
|
221
175
|
const truncated = sources.length > limit || wrapped.length > limit;
|
|
222
176
|
const visible = truncated ? wrapped.slice(0, limit) : wrapped;
|
|
223
177
|
for (const line of visible) pushRail(line, section, continued);
|
|
224
|
-
if (truncated) {
|
|
225
|
-
const hidden =
|
|
178
|
+
if (truncated && wrapped.length > visible.length) {
|
|
179
|
+
const hidden = wrapped.length - visible.length;
|
|
226
180
|
const hintColor = this.hoveredSection === section ? "text" : "dim";
|
|
227
181
|
pushRail(
|
|
228
|
-
color("dim", `… +${hidden} more lines`) +
|
|
229
|
-
color("dim", " •") + color(hintColor, ` ${showMoreHintText()}`),
|
|
182
|
+
color("dim", `… +${hidden} more lines`) + color("dim", " •") + color(hintColor, ` ${showMoreHintText()}`),
|
|
230
183
|
section,
|
|
231
184
|
continued,
|
|
232
185
|
);
|
|
@@ -235,15 +188,13 @@ class ExpandedToolIoView implements Component {
|
|
|
235
188
|
};
|
|
236
189
|
|
|
237
190
|
const hasInput = this.input.trim().length > 0;
|
|
238
|
-
const output = this.getOutputBody();
|
|
239
191
|
if (hasInput) {
|
|
240
192
|
pushHeader("├", "Input");
|
|
241
|
-
pushBody(this.input, "input", this.maxInputLines, true, true);
|
|
242
|
-
|
|
193
|
+
pushBody(this.input, "input", Math.max(1, this.maxInputLines), true, true);
|
|
194
|
+
push(color("dim", " │"), null);
|
|
243
195
|
}
|
|
244
196
|
pushHeader("└", "Output");
|
|
245
|
-
pushBody(
|
|
246
|
-
|
|
197
|
+
pushBody(this.getOutputBody(), "output", Math.max(1, this.maxOutputLines), false, false);
|
|
247
198
|
this.cachedWidth = safeWidth;
|
|
248
199
|
this.cachedLines = rows;
|
|
249
200
|
return rows;
|
|
@@ -253,7 +204,6 @@ class ExpandedToolIoView implements Component {
|
|
|
253
204
|
this.cachedWidth = undefined;
|
|
254
205
|
this.cachedLines = undefined;
|
|
255
206
|
}
|
|
256
|
-
|
|
257
207
|
}
|
|
258
208
|
|
|
259
209
|
function unavailableComponent(reason: string, theme: any): Component {
|
|
@@ -265,34 +215,24 @@ function unavailableComponent(reason: string, theme: any): Component {
|
|
|
265
215
|
};
|
|
266
216
|
}
|
|
267
217
|
|
|
268
|
-
function resultText(result: any): string {
|
|
269
|
-
return rawTextFromResult(result);
|
|
270
|
-
}
|
|
218
|
+
function resultText(result: any): string { return rawTextFromResult(result); }
|
|
271
219
|
|
|
272
220
|
function persistedFromDetails(details: any): PersistedDiff | undefined {
|
|
273
221
|
if (!details || typeof details !== "object") return undefined;
|
|
274
222
|
const persisted: PersistedDiff = {};
|
|
275
223
|
if (typeof details.diff === "string") persisted.diff = details.diff;
|
|
276
224
|
if (typeof details.patch === "string") persisted.patch = details.patch;
|
|
277
|
-
if (typeof details.diffUnavailableReason === "string")
|
|
278
|
-
persisted.diffUnavailableReason = details.diffUnavailableReason;
|
|
279
|
-
}
|
|
225
|
+
if (typeof details.diffUnavailableReason === "string") persisted.diffUnavailableReason = details.diffUnavailableReason;
|
|
280
226
|
return Object.keys(persisted).length ? persisted : undefined;
|
|
281
227
|
}
|
|
282
228
|
|
|
283
229
|
function viewerConfig(context: any): ViewerDiffConfig {
|
|
284
230
|
const candidate = context?.diffConfig;
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
...candidate,
|
|
289
|
-
};
|
|
231
|
+
return candidate && typeof candidate === "object"
|
|
232
|
+
? { ...DEFAULT_TOOL_DISPLAY_CONFIG, ...candidate }
|
|
233
|
+
: DEFAULT_TOOL_DISPLAY_CONFIG;
|
|
290
234
|
}
|
|
291
235
|
|
|
292
|
-
/**
|
|
293
|
-
* Rich edit/write rendering for the conversation viewer. Durable details are
|
|
294
|
-
* passed through before any live/ephemeral context so history is deterministic.
|
|
295
|
-
*/
|
|
296
236
|
export function renderRichToolResult(
|
|
297
237
|
toolName: string,
|
|
298
238
|
result: any,
|
|
@@ -303,61 +243,35 @@ export function renderRichToolResult(
|
|
|
303
243
|
if (options?.isPartial || options?.isError || context?.isError) return undefined;
|
|
304
244
|
const expanded = options?.expanded === true || context?.expanded === true;
|
|
305
245
|
const args = context?.args ?? options?.args;
|
|
306
|
-
const filePath = args?.file_path ?? args?.path;
|
|
307
246
|
const details = result?.details;
|
|
308
247
|
const persistedDiff = context?.persistedDiff ?? persistedFromDetails(details);
|
|
309
248
|
const fileExistedBeforeWrite = typeof context?.fileExistedBeforeWrite === "boolean"
|
|
310
249
|
? context.fileExistedBeforeWrite
|
|
311
|
-
: typeof details?.fileExistedBeforeWrite === "boolean"
|
|
312
|
-
? details.fileExistedBeforeWrite
|
|
313
|
-
: undefined;
|
|
250
|
+
: typeof details?.fileExistedBeforeWrite === "boolean" ? details.fileExistedBeforeWrite : undefined;
|
|
314
251
|
|
|
315
252
|
if (toolName === "edit") {
|
|
316
|
-
return renderEditDiffResult(
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
theme,
|
|
321
|
-
resultText(result),
|
|
322
|
-
);
|
|
253
|
+
return renderEditDiffResult(details, {
|
|
254
|
+
expanded, filePath: args?.file_path ?? args?.path, persistedDiff,
|
|
255
|
+
isHovered: options?.isHovered, invalidate: context?.invalidate,
|
|
256
|
+
}, viewerConfig(context), theme, resultText(result));
|
|
323
257
|
}
|
|
324
258
|
if (toolName !== "write") return undefined;
|
|
325
259
|
if (persistedDiff?.diffUnavailableReason && !persistedDiff.diff && !persistedDiff.patch) {
|
|
326
260
|
return unavailableComponent(persistedDiff.diffUnavailableReason, theme);
|
|
327
261
|
}
|
|
328
|
-
|
|
329
262
|
const content = typeof args?.content === "string" ? args.content : undefined;
|
|
330
|
-
if (
|
|
263
|
+
if (typeof content !== "string" && !persistedDiff?.diff && !persistedDiff?.patch) {
|
|
331
264
|
return unavailableComponent("execution metadata is unavailable", theme);
|
|
332
265
|
}
|
|
333
|
-
return renderWriteDiffResult(
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
invalidate: context?.invalidate,
|
|
343
|
-
},
|
|
344
|
-
viewerConfig(context),
|
|
345
|
-
theme,
|
|
346
|
-
resultText(result),
|
|
347
|
-
);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
function renderCallSummary(toolName: string, args: any, theme: any): string {
|
|
351
|
-
const title = toolName.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ");
|
|
352
|
-
const value = args?.path ?? args?.file_path ?? args?.command ?? args?.query ?? args?.name;
|
|
353
|
-
const summary = value === undefined ? title : `${title} ${oneLine(value)}`;
|
|
354
|
-
return theme.fg("toolTitle", summary);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
function settledIcon(context: any): string {
|
|
358
|
-
if (context?.isError) return "✗";
|
|
359
|
-
if (context?.isPartial || context?.executionStarted) return "●";
|
|
360
|
-
return "✓";
|
|
266
|
+
return renderWriteDiffResult(content, {
|
|
267
|
+
expanded,
|
|
268
|
+
filePath: args?.file_path ?? args?.path,
|
|
269
|
+
persistedDiff,
|
|
270
|
+
previousContent: context?.previousContent,
|
|
271
|
+
fileExistedBeforeWrite,
|
|
272
|
+
isHovered: options?.isHovered,
|
|
273
|
+
invalidate: context?.invalidate,
|
|
274
|
+
}, viewerConfig(context), theme, resultText(result));
|
|
361
275
|
}
|
|
362
276
|
|
|
363
277
|
function renderFallbackResult(result: any, options: any, theme: any, context: any): Component {
|
|
@@ -365,49 +279,31 @@ function renderFallbackResult(result: any, options: any, theme: any, context: an
|
|
|
365
279
|
const expanded = options?.expanded === true || context?.expanded === true;
|
|
366
280
|
const maxChars = Number.isFinite(context?.maxChars) ? Math.max(0, Math.floor(context.maxChars)) : 16_384;
|
|
367
281
|
const text = textFromResult(result, expanded, maxChars);
|
|
368
|
-
|
|
282
|
+
const input = formatToolInputArgs(context?.args, maxChars);
|
|
283
|
+
if (expanded && (text.trim() || input.trim())) {
|
|
369
284
|
const maxLines = Number.isFinite(context?.diffConfig?.expandedPreviewMaxLines)
|
|
370
285
|
? Math.max(1, Math.floor(context.diffConfig.expandedPreviewMaxLines))
|
|
371
286
|
: DEFAULT_TOOL_DISPLAY_CONFIG.expandedPreviewMaxLines;
|
|
372
|
-
return new ExpandedToolIoView(
|
|
373
|
-
formatToolInputArgs(context?.args, maxChars),
|
|
374
|
-
text,
|
|
375
|
-
Boolean(error),
|
|
376
|
-
maxLines,
|
|
377
|
-
maxLines,
|
|
378
|
-
theme,
|
|
379
|
-
);
|
|
287
|
+
return new ExpandedToolIoView(input, text, Boolean(error), maxLines, maxLines, theme);
|
|
380
288
|
}
|
|
381
|
-
const
|
|
382
|
-
const count = lines.length;
|
|
289
|
+
const count = text ? text.split("\n").length : 0;
|
|
383
290
|
const summary = error ? oneLine(text) || "Failed" : count ? `${count} ${count === 1 ? "line" : "lines"} returned` : "Done";
|
|
384
291
|
const expandable = !expanded && hasExpandableDetail(text, context?.args);
|
|
385
292
|
return {
|
|
386
293
|
render(width: number): string[] {
|
|
387
294
|
const hovered = options?.isHovered?.() ?? context?.isHovered?.() ?? false;
|
|
388
|
-
const
|
|
389
|
-
const hint = expandable ? ` • ${clickLabel}` : "";
|
|
390
|
-
const color = error ? "error" : "muted";
|
|
295
|
+
const hint = expandable ? ` • ${showMoreHintText()}` : "";
|
|
391
296
|
const line = expandable && hovered
|
|
392
|
-
? theme.fg(
|
|
393
|
-
: theme.fg(
|
|
297
|
+
? theme.fg(error ? "error" : "muted", ` ↳ ${summary} • `) + theme.fg("text", showMoreHintText())
|
|
298
|
+
: theme.fg(error ? "error" : "muted", ` ↳ ${summary}${hint}`);
|
|
394
299
|
return [truncateToWidth(line, toolViewportWidth(width), "")];
|
|
395
300
|
},
|
|
396
301
|
invalidate() {},
|
|
397
302
|
};
|
|
398
303
|
}
|
|
399
304
|
|
|
400
|
-
export function createViewerCcstyleResult(
|
|
401
|
-
toolName
|
|
402
|
-
result: any,
|
|
403
|
-
options: any,
|
|
404
|
-
theme: any,
|
|
405
|
-
context: any,
|
|
406
|
-
): Component {
|
|
407
|
-
return (
|
|
408
|
-
renderRichToolResult(toolName, result, options, theme, context) ??
|
|
409
|
-
renderFallbackResult(result, options, theme, context)
|
|
410
|
-
);
|
|
305
|
+
export function createViewerCcstyleResult(toolName: string, result: any, options: any, theme: any, context: any): Component {
|
|
306
|
+
return renderRichToolResult(toolName, result, options, theme, context) ?? renderFallbackResult(result, options, theme, context);
|
|
411
307
|
}
|
|
412
308
|
|
|
413
|
-
export {
|
|
309
|
+
export type { DisplayConfigInput };
|
|
@@ -300,23 +300,25 @@ export function formatConversationMessages(
|
|
|
300
300
|
const blocks: ConversationBlock[] = [];
|
|
301
301
|
const usedBlockIds = new Set<string>();
|
|
302
302
|
const callsById = new Map<string, ConversationBlock>();
|
|
303
|
-
const callsByName = new Map<string, ConversationBlock[]>();
|
|
303
|
+
const callsByName = new Map<string, { blocks: ConversationBlock[]; next: number }>();
|
|
304
304
|
|
|
305
305
|
const registerCall = (name: string, block: ConversationBlock) => {
|
|
306
306
|
callsById.set(block.toolCallId!, block);
|
|
307
307
|
const nameKey = name.toLowerCase();
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
callsByName.set(nameKey,
|
|
308
|
+
const queue = callsByName.get(nameKey) ?? { blocks: [], next: 0 };
|
|
309
|
+
queue.blocks.push(block);
|
|
310
|
+
callsByName.set(nameKey, queue);
|
|
311
311
|
};
|
|
312
312
|
const unregisterCall = (block: ConversationBlock) => {
|
|
313
|
+
// Calls remain in their name queue as tombstones. Advancing its cursor is
|
|
314
|
+
// amortized O(1), unlike removing from the middle of a same-name array.
|
|
313
315
|
if (block.toolCallId) callsById.delete(block.toolCallId);
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
316
|
+
};
|
|
317
|
+
const nextCallByName = (name: string): ConversationBlock | undefined => {
|
|
318
|
+
const queue = callsByName.get(name.toLowerCase());
|
|
319
|
+
if (!queue) return undefined;
|
|
320
|
+
while (queue.next < queue.blocks.length && queue.blocks[queue.next]?.toolStatus !== "pending") queue.next++;
|
|
321
|
+
return queue.blocks[queue.next];
|
|
320
322
|
};
|
|
321
323
|
|
|
322
324
|
for (let messageIndex = 0; messageIndex < messages.length; messageIndex++) {
|
|
@@ -368,7 +370,7 @@ export function formatConversationMessages(
|
|
|
368
370
|
const direct = result.toolCallId ? callsById.get(result.toolCallId) : undefined;
|
|
369
371
|
let match = direct;
|
|
370
372
|
if (!match && result.toolName) {
|
|
371
|
-
match =
|
|
373
|
+
match = nextCallByName(result.toolName);
|
|
372
374
|
}
|
|
373
375
|
if (match) {
|
|
374
376
|
applyToolResult(match, result, maxToolResultChars);
|