@hheei/pi-ext-ui 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/extension.js +12 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +5 -2
- package/dist/message-view.d.ts +15 -0
- package/dist/message-view.js +130 -0
- package/dist/nested-calls-view.d.ts +28 -0
- package/dist/nested-calls-view.js +405 -0
- package/dist/payload-parser.d.ts +55 -0
- package/dist/payload-parser.js +370 -0
- package/dist/renderer.d.ts +10 -28
- package/dist/renderer.js +322 -1005
- package/dist/tool-view.d.ts +50 -3
- package/dist/tool-view.js +209 -55
- package/dist/tracker.d.ts +26 -0
- package/dist/tracker.js +81 -0
- package/package.json +51 -49
- package/dist/apply-patch-glyph.d.ts +0 -10
- package/dist/apply-patch-glyph.js +0 -29
- package/dist/apply-patch-preview.d.ts +0 -30
- package/dist/apply-patch-preview.js +0 -89
- package/dist/apply-patch-view.d.ts +0 -47
- package/dist/apply-patch-view.js +0 -198
- package/dist/collapsible-tool-frame.d.ts +0 -1
- package/dist/collapsible-tool-frame.js +0 -1
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type AgentToolResult } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type DiffSource } from "./diff-view.js";
|
|
3
|
+
import type { ToolStatusKind } from "./tool-view.js";
|
|
4
|
+
export declare const EDIT_TOOL_NAME = "edit";
|
|
5
|
+
export declare const WRITE_TOOL_NAME = "write";
|
|
6
|
+
export declare const CODEMODE_OPTIONS_PREFIX = "// @options:";
|
|
7
|
+
export declare const CODEMODE_SCRIPT_HEADER: RegExp;
|
|
8
|
+
export interface NestedCallRecord {
|
|
9
|
+
readonly toolName: string;
|
|
10
|
+
readonly summary?: string | undefined;
|
|
11
|
+
readonly status?: "running" | "ok" | "error" | "cancelled" | undefined;
|
|
12
|
+
readonly isError?: boolean | undefined;
|
|
13
|
+
readonly isWarning?: boolean | undefined;
|
|
14
|
+
readonly durationMs?: number | undefined;
|
|
15
|
+
readonly cost?: number | undefined;
|
|
16
|
+
readonly error?: string | undefined;
|
|
17
|
+
}
|
|
18
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
19
|
+
export declare function estimateStreamingTokens(args: unknown): number;
|
|
20
|
+
export declare function formatDurationMs(ms: number | undefined): string;
|
|
21
|
+
export declare function formatCostUsd(cost: number | undefined): string;
|
|
22
|
+
/** One header fact for a deadline, in the same shape codemode's `timeout_ms` renders as. */
|
|
23
|
+
export declare function formatTimeoutFact(timeoutMs: number): string;
|
|
24
|
+
export declare function extractCodemodeHeaderFacts(args: unknown): string;
|
|
25
|
+
export declare function parseCallArgs(raw: unknown): Record<string, unknown> | undefined;
|
|
26
|
+
export declare function extractSummaryArg(toolName: string, args: unknown): string;
|
|
27
|
+
export declare function extractHeavyInputCommand(toolName: string, args: unknown): string | undefined;
|
|
28
|
+
export declare function extractNestedCalls(result: AgentToolResult<unknown> | undefined): readonly NestedCallRecord[];
|
|
29
|
+
export declare function extractResultText(toolName: string, result: AgentToolResult<unknown>): {
|
|
30
|
+
readonly outputText: string;
|
|
31
|
+
readonly wallTimeSeconds: number | undefined;
|
|
32
|
+
};
|
|
33
|
+
export declare function callStatsParts(total: number, failCount: number): string[];
|
|
34
|
+
export declare function resolveContainerStatus(params: {
|
|
35
|
+
readonly isError: boolean;
|
|
36
|
+
readonly isPartial: boolean;
|
|
37
|
+
readonly hasChildren: boolean;
|
|
38
|
+
readonly selfFailed: boolean;
|
|
39
|
+
}): ToolStatusKind;
|
|
40
|
+
export declare function toolPath(args: unknown): string;
|
|
41
|
+
/** Compact nested-row result: a one-line summary plus the status it reports. */
|
|
42
|
+
export interface InlinePresentation {
|
|
43
|
+
readonly summary: string;
|
|
44
|
+
readonly status: ToolStatusKind;
|
|
45
|
+
}
|
|
46
|
+
export declare function fileToolPresentation(toolName: string, args: unknown, _details: unknown, isError: boolean): InlinePresentation | undefined;
|
|
47
|
+
/** Pi native edit persists a unified patch plus the same change as a numbered display diff. */
|
|
48
|
+
export declare function editDiffSource(details: unknown): DiffSource | undefined;
|
|
49
|
+
export declare function displayDiffSource(diff: string): DiffSource | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* One compact nested summary for a tool call. Adopts the tool's standard collapsed
|
|
52
|
+
* single-line format when recognized, degrading to undefined so callers can use
|
|
53
|
+
* the default argument rendering.
|
|
54
|
+
*/
|
|
55
|
+
export declare function inlinePresentation(toolName: string, args: unknown, details: unknown, isError: boolean): InlinePresentation | undefined;
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { formatReadLineRange } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { stripTerminalSequences } from "@earendil-works/pi-tui";
|
|
3
|
+
import { diffStats } from "./diff-view.js";
|
|
4
|
+
export const EDIT_TOOL_NAME = "edit";
|
|
5
|
+
export const WRITE_TOOL_NAME = "write";
|
|
6
|
+
export const CODEMODE_OPTIONS_PREFIX = "// @options:";
|
|
7
|
+
export const CODEMODE_SCRIPT_HEADER = /^Script (?:completed|failed)\r?\nWall time ([\d.]+) seconds\r?\nOutput:\r?\n/;
|
|
8
|
+
export function isRecord(value) {
|
|
9
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
export function estimateStreamingTokens(args) {
|
|
12
|
+
if (args === undefined || args === null)
|
|
13
|
+
return 0;
|
|
14
|
+
try {
|
|
15
|
+
const serialized = typeof args === "string" ? args : JSON.stringify(args);
|
|
16
|
+
if (!serialized || serialized === "{}")
|
|
17
|
+
return 0;
|
|
18
|
+
return Math.max(1, Math.ceil(serialized.length / 4));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function formatDurationMs(ms) {
|
|
25
|
+
if (ms === undefined || !Number.isFinite(ms))
|
|
26
|
+
return "";
|
|
27
|
+
return ms < 1000 ? `${String(Math.round(ms))}ms` : `${(ms / 1000).toFixed(1)}s`;
|
|
28
|
+
}
|
|
29
|
+
export function formatCostUsd(cost) {
|
|
30
|
+
if (cost === undefined || !Number.isFinite(cost) || cost <= 0)
|
|
31
|
+
return "";
|
|
32
|
+
return `$${cost >= 0.01 ? cost.toFixed(2) : cost.toPrecision(2)}`;
|
|
33
|
+
}
|
|
34
|
+
/** One header fact for a deadline, in the same shape codemode's `timeout_ms` renders as. */
|
|
35
|
+
export function formatTimeoutFact(timeoutMs) {
|
|
36
|
+
const label = timeoutMs >= 1000 ? `${String(Math.round(timeoutMs / 1000))}s` : `${String(timeoutMs)}ms`;
|
|
37
|
+
return `(timeout ${label})`;
|
|
38
|
+
}
|
|
39
|
+
export function extractCodemodeHeaderFacts(args) {
|
|
40
|
+
if (!isRecord(args))
|
|
41
|
+
return "";
|
|
42
|
+
const options = {};
|
|
43
|
+
for (const [key, value] of Object.entries(args)) {
|
|
44
|
+
if (key !== "code" && value !== undefined && value !== null) {
|
|
45
|
+
options[key] = value;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (typeof args.code === "string") {
|
|
49
|
+
const firstLine = args.code.split(/\r?\n/, 1)[0]?.trim() ?? "";
|
|
50
|
+
if (firstLine.startsWith(CODEMODE_OPTIONS_PREFIX)) {
|
|
51
|
+
try {
|
|
52
|
+
const parsed = JSON.parse(firstLine.slice(CODEMODE_OPTIONS_PREFIX.length).trim());
|
|
53
|
+
if (isRecord(parsed)) {
|
|
54
|
+
Object.assign(options, parsed);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch { }
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const facts = [];
|
|
61
|
+
const timeoutMs = typeof options.timeout_ms === "number" ? options.timeout_ms : undefined;
|
|
62
|
+
if (timeoutMs !== undefined)
|
|
63
|
+
facts.push(formatTimeoutFact(timeoutMs));
|
|
64
|
+
if (typeof options.max_output_tokens === "number") {
|
|
65
|
+
facts.push(`(tokens ${String(options.max_output_tokens)})`);
|
|
66
|
+
}
|
|
67
|
+
if (typeof options.target === "string" && options.target !== "local") {
|
|
68
|
+
facts.push(`(${options.target})`);
|
|
69
|
+
}
|
|
70
|
+
return facts.join(" ");
|
|
71
|
+
}
|
|
72
|
+
export function parseCallArgs(raw) {
|
|
73
|
+
if (isRecord(raw))
|
|
74
|
+
return raw;
|
|
75
|
+
if (typeof raw !== "string")
|
|
76
|
+
return undefined;
|
|
77
|
+
const text = raw.trim();
|
|
78
|
+
if (text === "")
|
|
79
|
+
return undefined;
|
|
80
|
+
if (text.startsWith("{")) {
|
|
81
|
+
try {
|
|
82
|
+
const parsed = JSON.parse(text);
|
|
83
|
+
if (isRecord(parsed))
|
|
84
|
+
return parsed;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
const record = {};
|
|
88
|
+
const extractString = (key) => {
|
|
89
|
+
const regex = new RegExp(`"${key}"\\s*:\\s*"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"`);
|
|
90
|
+
const match = regex.exec(text);
|
|
91
|
+
if (match?.[1]) {
|
|
92
|
+
record[key] = match[1].replace(/\\"/g, '"');
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
const extractNumber = (key) => {
|
|
96
|
+
const regex = new RegExp(`"${key}"\\s*:\\s*(-?\\d+(?:\\.\\d+)?)`);
|
|
97
|
+
const match = regex.exec(text);
|
|
98
|
+
if (match?.[1]) {
|
|
99
|
+
record[key] = Number(match[1]);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
extractString("path");
|
|
103
|
+
extractString("file_path");
|
|
104
|
+
extractString("command");
|
|
105
|
+
extractString("pattern");
|
|
106
|
+
extractString("glob");
|
|
107
|
+
extractString("query");
|
|
108
|
+
extractString("url");
|
|
109
|
+
extractString("id");
|
|
110
|
+
extractString("agent");
|
|
111
|
+
extractString("topic");
|
|
112
|
+
extractString("source");
|
|
113
|
+
extractNumber("offset");
|
|
114
|
+
extractNumber("limit");
|
|
115
|
+
if (Object.keys(record).length > 0)
|
|
116
|
+
return record;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
export function extractSummaryArg(toolName, args) {
|
|
122
|
+
if (toolName === "codemode") {
|
|
123
|
+
return extractCodemodeHeaderFacts(args);
|
|
124
|
+
}
|
|
125
|
+
const parsed = parseCallArgs(args);
|
|
126
|
+
if (!parsed)
|
|
127
|
+
return "";
|
|
128
|
+
for (const key of [
|
|
129
|
+
"command",
|
|
130
|
+
"query",
|
|
131
|
+
"queries",
|
|
132
|
+
"url",
|
|
133
|
+
"urls",
|
|
134
|
+
"source",
|
|
135
|
+
"claim",
|
|
136
|
+
"id",
|
|
137
|
+
"search_id",
|
|
138
|
+
"topic",
|
|
139
|
+
"path",
|
|
140
|
+
"file_path",
|
|
141
|
+
"pattern",
|
|
142
|
+
"agent",
|
|
143
|
+
]) {
|
|
144
|
+
const val = parsed[key];
|
|
145
|
+
if (typeof val === "string" && val.trim() !== "") {
|
|
146
|
+
return stripTerminalSequences(val.trim());
|
|
147
|
+
}
|
|
148
|
+
if (Array.isArray(val) && val.length > 0 && typeof val[0] === "string") {
|
|
149
|
+
const first = stripTerminalSequences(val[0].trim());
|
|
150
|
+
return val.length > 1 ? `${first} (+${String(val.length - 1)})` : first;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return "";
|
|
154
|
+
}
|
|
155
|
+
export function extractHeavyInputCommand(toolName, args) {
|
|
156
|
+
if (!isRecord(args))
|
|
157
|
+
return undefined;
|
|
158
|
+
if (toolName === "bash" && typeof args.command === "string") {
|
|
159
|
+
return args.command.trim();
|
|
160
|
+
}
|
|
161
|
+
if (toolName === "python_eval" && typeof args.code === "string") {
|
|
162
|
+
return args.code.trim();
|
|
163
|
+
}
|
|
164
|
+
if (toolName === "codemode" && typeof args.code === "string") {
|
|
165
|
+
return args.code.trim();
|
|
166
|
+
}
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
export function extractNestedCalls(result) {
|
|
170
|
+
if (result === undefined || !isRecord(result.details))
|
|
171
|
+
return [];
|
|
172
|
+
const rawList = Array.isArray(result.details.calls)
|
|
173
|
+
? result.details.calls
|
|
174
|
+
: Array.isArray(result.details.nestedCalls)
|
|
175
|
+
? result.details.nestedCalls
|
|
176
|
+
: undefined;
|
|
177
|
+
if (rawList === undefined)
|
|
178
|
+
return [];
|
|
179
|
+
const items = [];
|
|
180
|
+
for (const entry of rawList) {
|
|
181
|
+
if (!isRecord(entry))
|
|
182
|
+
continue;
|
|
183
|
+
const name = typeof entry.name === "string"
|
|
184
|
+
? entry.name
|
|
185
|
+
: typeof entry.toolName === "string"
|
|
186
|
+
? entry.toolName
|
|
187
|
+
: undefined;
|
|
188
|
+
if (name === undefined)
|
|
189
|
+
continue;
|
|
190
|
+
const rawStatus = entry.status;
|
|
191
|
+
const status = rawStatus === "running" ||
|
|
192
|
+
rawStatus === "ok" ||
|
|
193
|
+
rawStatus === "error" ||
|
|
194
|
+
rawStatus === "cancelled"
|
|
195
|
+
? rawStatus
|
|
196
|
+
: undefined;
|
|
197
|
+
const summary = typeof entry.args === "string" && entry.args.trim() !== ""
|
|
198
|
+
? entry.args
|
|
199
|
+
: typeof entry.summary === "string"
|
|
200
|
+
? entry.summary
|
|
201
|
+
: undefined;
|
|
202
|
+
items.push({
|
|
203
|
+
toolName: name,
|
|
204
|
+
summary,
|
|
205
|
+
status,
|
|
206
|
+
isError: entry.isError === true || status === "error",
|
|
207
|
+
isWarning: entry.isWarning === true || status === "cancelled",
|
|
208
|
+
durationMs: typeof entry.durationMs === "number" ? entry.durationMs : undefined,
|
|
209
|
+
cost: typeof entry.cost === "number" ? entry.cost : undefined,
|
|
210
|
+
error: typeof entry.error === "string" ? entry.error : undefined,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
return items;
|
|
214
|
+
}
|
|
215
|
+
export function extractResultText(toolName, result) {
|
|
216
|
+
const parts = [];
|
|
217
|
+
for (const block of result.content) {
|
|
218
|
+
if (block.type === "text" && block.text.trim() !== "") {
|
|
219
|
+
parts.push(block.text);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const joined = parts.join("\n");
|
|
223
|
+
if (toolName === "codemode") {
|
|
224
|
+
const stripped = stripTerminalSequences(joined);
|
|
225
|
+
const match = CODEMODE_SCRIPT_HEADER.exec(stripped);
|
|
226
|
+
const wallTimeSeconds = match?.[1] !== undefined ? Number.parseFloat(match[1]) : undefined;
|
|
227
|
+
const cleaned = joined.replace(CODEMODE_SCRIPT_HEADER, "").trim();
|
|
228
|
+
return { outputText: cleaned, wallTimeSeconds };
|
|
229
|
+
}
|
|
230
|
+
return { outputText: joined, wallTimeSeconds: undefined };
|
|
231
|
+
}
|
|
232
|
+
export function callStatsParts(total, failCount) {
|
|
233
|
+
if (failCount > 0) {
|
|
234
|
+
return [`${String(total - failCount)} ok`, `${String(failCount)} failed`];
|
|
235
|
+
}
|
|
236
|
+
return [`${String(total)} ${total === 1 ? "call" : "calls"}`];
|
|
237
|
+
}
|
|
238
|
+
export function resolveContainerStatus(params) {
|
|
239
|
+
if (params.selfFailed)
|
|
240
|
+
return "error";
|
|
241
|
+
if (params.hasChildren)
|
|
242
|
+
return "none";
|
|
243
|
+
if (params.isPartial)
|
|
244
|
+
return "running";
|
|
245
|
+
return params.isError ? "error" : "ok";
|
|
246
|
+
}
|
|
247
|
+
export function toolPath(args) {
|
|
248
|
+
const parsed = parseCallArgs(args);
|
|
249
|
+
if (!parsed)
|
|
250
|
+
return "";
|
|
251
|
+
const val = parsed.path ?? parsed.file_path;
|
|
252
|
+
return typeof val === "string" ? val.trim() : "";
|
|
253
|
+
}
|
|
254
|
+
export function fileToolPresentation(toolName, args, _details, isError) {
|
|
255
|
+
const path = toolPath(args);
|
|
256
|
+
if (toolName === "read") {
|
|
257
|
+
if (path === "")
|
|
258
|
+
return undefined;
|
|
259
|
+
const parsed = parseCallArgs(args);
|
|
260
|
+
const range = formatReadLineRange(parsed?.offset, parsed?.limit);
|
|
261
|
+
return { summary: `${path}${range}`, status: isError ? "error" : "ok" };
|
|
262
|
+
}
|
|
263
|
+
if (toolName === WRITE_TOOL_NAME) {
|
|
264
|
+
return path === "" ? undefined : { summary: path, status: isError ? "error" : "ok" };
|
|
265
|
+
}
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
/** Pi native edit persists a unified patch plus the same change as a numbered display diff. */
|
|
269
|
+
export function editDiffSource(details) {
|
|
270
|
+
if (!isRecord(details))
|
|
271
|
+
return undefined;
|
|
272
|
+
if (typeof details.patch === "string" && details.patch.trim() !== "") {
|
|
273
|
+
return { kind: "unifiedPatch", text: details.patch };
|
|
274
|
+
}
|
|
275
|
+
if (typeof details.diff === "string" && details.diff.trim() !== "") {
|
|
276
|
+
return displayDiffSource(details.diff);
|
|
277
|
+
}
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
export function displayDiffSource(diff) {
|
|
281
|
+
const hunks = [];
|
|
282
|
+
let current = [];
|
|
283
|
+
let pendingBreak = false;
|
|
284
|
+
for (const raw of diff.split(/\r?\n/)) {
|
|
285
|
+
if (raw === "")
|
|
286
|
+
continue;
|
|
287
|
+
const sign = raw.charAt(0);
|
|
288
|
+
const match = /^ *(\d+)? ?(.*)$/.exec(raw.slice(1));
|
|
289
|
+
const lineNumber = match?.[1];
|
|
290
|
+
if (lineNumber === undefined) {
|
|
291
|
+
pendingBreak = true;
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
if (pendingBreak && current.length > 0) {
|
|
295
|
+
hunks.push(current);
|
|
296
|
+
current = [];
|
|
297
|
+
}
|
|
298
|
+
pendingBreak = false;
|
|
299
|
+
const kind = sign === "+" ? "add" : sign === "-" ? "del" : "ctx";
|
|
300
|
+
current.push({ kind, line: Number(lineNumber), text: match?.[2] ?? "" });
|
|
301
|
+
}
|
|
302
|
+
if (current.length === 0)
|
|
303
|
+
return undefined;
|
|
304
|
+
hunks.push(current);
|
|
305
|
+
return { kind: "hunks", hunks: hunks.map((lines) => ({ lines })) };
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* The name-free part of a summary for tools whose call arguments read well on their own. The tool
|
|
309
|
+
* name is the row's title, so repeating it here would print it twice.
|
|
310
|
+
*/
|
|
311
|
+
function formatArgumentSummary(toolName, parsed) {
|
|
312
|
+
const path = typeof parsed.path === "string"
|
|
313
|
+
? parsed.path.trim()
|
|
314
|
+
: typeof parsed.file_path === "string"
|
|
315
|
+
? parsed.file_path.trim()
|
|
316
|
+
: "";
|
|
317
|
+
switch (toolName) {
|
|
318
|
+
case "bash": {
|
|
319
|
+
const cmd = typeof parsed.command === "string" ? parsed.command.trim() : "";
|
|
320
|
+
if (!cmd)
|
|
321
|
+
return undefined;
|
|
322
|
+
return cmd.length > 70 ? `${cmd.slice(0, 67)}…` : cmd;
|
|
323
|
+
}
|
|
324
|
+
case "grep":
|
|
325
|
+
case "find": {
|
|
326
|
+
const pattern = typeof parsed.pattern === "string" ? parsed.pattern.trim() : "";
|
|
327
|
+
if (!pattern)
|
|
328
|
+
return undefined;
|
|
329
|
+
const target = path && path !== "." ? ` ${path}` : "";
|
|
330
|
+
return `${pattern}${target}`;
|
|
331
|
+
}
|
|
332
|
+
default: {
|
|
333
|
+
const keyArg = extractSummaryArg(toolName, parsed);
|
|
334
|
+
return keyArg ? keyArg : undefined;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* One compact nested summary for a tool call. Adopts the tool's standard collapsed
|
|
340
|
+
* single-line format when recognized, degrading to undefined so callers can use
|
|
341
|
+
* the default argument rendering.
|
|
342
|
+
*/
|
|
343
|
+
export function inlinePresentation(toolName, args, details, isError) {
|
|
344
|
+
const fileTool = fileToolPresentation(toolName, args, details, isError);
|
|
345
|
+
if (fileTool !== undefined)
|
|
346
|
+
return fileTool;
|
|
347
|
+
const parsed = parseCallArgs(args);
|
|
348
|
+
if (parsed === undefined)
|
|
349
|
+
return undefined;
|
|
350
|
+
if (toolName === EDIT_TOOL_NAME) {
|
|
351
|
+
const path = toolPath(parsed);
|
|
352
|
+
const source = isError ? undefined : editDiffSource(details);
|
|
353
|
+
const stats = source === undefined ? undefined : diffStats(source);
|
|
354
|
+
if (path === "" && stats === undefined)
|
|
355
|
+
return undefined;
|
|
356
|
+
return {
|
|
357
|
+
summary: stats === undefined
|
|
358
|
+
? path
|
|
359
|
+
: `${path} (+${String(stats.added)} -${String(stats.removed)})`.trim(),
|
|
360
|
+
status: isError ? "error" : "ok",
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
const baseSummary = formatArgumentSummary(toolName, parsed);
|
|
364
|
+
if (baseSummary === undefined)
|
|
365
|
+
return undefined;
|
|
366
|
+
return {
|
|
367
|
+
summary: baseSummary,
|
|
368
|
+
status: isError ? "error" : "ok",
|
|
369
|
+
};
|
|
370
|
+
}
|
package/dist/renderer.d.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import { type ToolStatusKind } from "./tool-view.js";
|
|
1
|
+
import { type ExtensionAPI, type ToolRendererResolver, type ToolRenderers } from "@earendil-works/pi-coding-agent";
|
|
3
2
|
export type ToolRenderContext = Parameters<NonNullable<ToolRenderers["renderCall"]>>[2];
|
|
4
|
-
/**
|
|
5
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Upstream tools whose call and result views pi-ext-ui still frames itself. Concrete tool packages
|
|
5
|
+
* own their own presentation through ext-core's optional renderer point, so their names never
|
|
6
|
+
* appear here.
|
|
7
|
+
*/
|
|
8
|
+
export declare const SPECIALIZED_TOOL_RENDERERS: readonly ["codemode"];
|
|
6
9
|
export type SpecializedToolRendererName = (typeof SPECIALIZED_TOOL_RENDERERS)[number];
|
|
7
10
|
/** Web-access tools use the shared folding shell while retaining their native body renderer. */
|
|
8
11
|
export declare const WEB_ACCESS_TOOLS: readonly ["web_search", "web_fetch", "web_open"];
|
|
9
12
|
export type WebAccessToolName = (typeof WEB_ACCESS_TOOLS)[number];
|
|
13
|
+
export { estimateStreamingTokens, inlinePresentation, type NestedCallRecord, } from "./payload-parser.js";
|
|
10
14
|
export interface CollapsibleToolRendererOptions {
|
|
11
15
|
readonly tools?: readonly string[] | undefined;
|
|
12
16
|
readonly match?: ((toolName: string) => boolean) | undefined;
|
|
@@ -14,29 +18,7 @@ export interface CollapsibleToolRendererOptions {
|
|
|
14
18
|
readonly defaultCollapsed?: boolean | undefined;
|
|
15
19
|
readonly copyToClipboard?: ((text: string) => Promise<void>) | undefined;
|
|
16
20
|
}
|
|
17
|
-
export interface NestedCallRecord {
|
|
18
|
-
readonly toolName: string;
|
|
19
|
-
readonly summary?: string | undefined;
|
|
20
|
-
readonly status?: "running" | "ok" | "error" | "cancelled" | undefined;
|
|
21
|
-
readonly isError?: boolean | undefined;
|
|
22
|
-
readonly isWarning?: boolean | undefined;
|
|
23
|
-
readonly durationMs?: number | undefined;
|
|
24
|
-
readonly cost?: number | undefined;
|
|
25
|
-
readonly error?: string | undefined;
|
|
26
|
-
}
|
|
27
|
-
export declare function estimateStreamingTokens(args: unknown): number;
|
|
28
|
-
export declare function extractCodemodeHeaderFacts(args: unknown): string;
|
|
29
|
-
/** Compact nested-row result: a one-line summary plus the status it reports. */
|
|
30
|
-
interface InlinePresentation {
|
|
31
|
-
readonly summary: string;
|
|
32
|
-
readonly status: ToolStatusKind;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* One compact nested summary for a file tool. Reports the delta only when the
|
|
36
|
-
* caller actually has result details; a codemode nested record keeps arguments
|
|
37
|
-
* alone, so it degrades to `<tool> <path>` instead of inventing counts.
|
|
38
|
-
*/
|
|
39
|
-
export declare function inlinePresentation(toolName: string, args: unknown, details: unknown, isError: boolean): InlinePresentation | undefined;
|
|
40
21
|
export declare function createCollapsibleToolRendererResolver(options?: CollapsibleToolRendererOptions): ToolRendererResolver;
|
|
22
|
+
/** UI factory consumed by package-owned direct renderers, independently of host resolver precedence. */
|
|
23
|
+
export declare function createOptionalToolRendererFactory(pi: ExtensionAPI, options?: CollapsibleToolRendererOptions): import("@hheei/pi-ext-core").ToolRendererFactory;
|
|
41
24
|
export declare function registerCollapsibleToolRenderer(pi: ExtensionAPI, options?: CollapsibleToolRendererOptions): void;
|
|
42
|
-
export {};
|