@volter-ai-dev/supercode-ui 0.1.8 → 0.1.10
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/components.d.ts +1 -0
- package/components.mjs +375 -33
- package/controller.mjs +8 -2
- package/conversation.d.ts +2 -0
- package/conversation.mjs +341 -33
- package/core.d.ts +3 -0
- package/core.mjs +240 -10
- package/embed.mjs +374 -33
- package/index.d.ts +35 -1
- package/messenger.mjs +374 -33
- package/package.json +1 -1
- package/styles.css +12 -1
package/components.d.ts
CHANGED
package/components.mjs
CHANGED
|
@@ -64,6 +64,11 @@ var MODES = /* @__PURE__ */ new Set(["none", "control", "mirror"]);
|
|
|
64
64
|
var STRATEGIES = /* @__PURE__ */ new Set(["start", "resume", "attach", "branch", "reduce"]);
|
|
65
65
|
var STARTUP = /* @__PURE__ */ new Set(["connecting", "starting", "discovering", "ready"]);
|
|
66
66
|
var FIDELITY = /* @__PURE__ */ new Set(["byte_lossless", "value_lossless", "semantic"]);
|
|
67
|
+
var TOOL_CATEGORIES = /* @__PURE__ */ new Set(["read", "search", "edit", "command", "test", "web", "agent", "plan", "other"]);
|
|
68
|
+
var TOOL_DETAILS = /* @__PURE__ */ new Set(["file", "matches", "diff", "terminal", "web", "agent", "plan", "fields"]);
|
|
69
|
+
var MAX_TOOL_FIELDS = 8;
|
|
70
|
+
var MAX_TOOL_FIELD_CHARS = 800;
|
|
71
|
+
var MAX_TOOL_PREVIEW_CHARS = 4e3;
|
|
67
72
|
function record(value) {
|
|
68
73
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
69
74
|
}
|
|
@@ -76,6 +81,206 @@ function number(value, fallback = 0) {
|
|
|
76
81
|
function nullableNumber(value) {
|
|
77
82
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
78
83
|
}
|
|
84
|
+
function boundedString(value, max) {
|
|
85
|
+
if (typeof value !== "string") return "";
|
|
86
|
+
return value.length <= max ? value : `${value.slice(0, max)}\u2026`;
|
|
87
|
+
}
|
|
88
|
+
function argumentValue(argumentsText) {
|
|
89
|
+
if (!argumentsText) return null;
|
|
90
|
+
try {
|
|
91
|
+
return JSON.parse(argumentsText);
|
|
92
|
+
} catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function firstString(source, keys) {
|
|
97
|
+
for (const key of keys) {
|
|
98
|
+
if (typeof source?.[key] === "string" && source[key].trim()) return source[key].trim();
|
|
99
|
+
}
|
|
100
|
+
return "";
|
|
101
|
+
}
|
|
102
|
+
function decodedLiteral(value) {
|
|
103
|
+
if (!value) return "";
|
|
104
|
+
if (value.startsWith('"')) {
|
|
105
|
+
try {
|
|
106
|
+
return JSON.parse(value);
|
|
107
|
+
} catch {
|
|
108
|
+
return "";
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return value.slice(1, -1).replaceAll("\\n", "\n").replaceAll("\\t", " ").replaceAll("\\r", "\r").replaceAll("\\`", "`").replaceAll("\\'", "'").replaceAll("\\\\", "\\");
|
|
112
|
+
}
|
|
113
|
+
function sourceString(source, keys) {
|
|
114
|
+
if (!source) return "";
|
|
115
|
+
const names = keys.join("|");
|
|
116
|
+
const match = new RegExp(`\\b(?:${names})\\s*:\\s*("(?:\\\\.|[^"\\\\])*"|'(?:\\\\.|[^'\\\\])*'|\`(?:\\\\.|[^\`\\\\])*\`)`).exec(source);
|
|
117
|
+
return decodedLiteral(match?.[1]);
|
|
118
|
+
}
|
|
119
|
+
function toolEnvelope(entry) {
|
|
120
|
+
const value = argumentValue(entry.arguments);
|
|
121
|
+
const source = typeof value === "string" ? value : "";
|
|
122
|
+
const tools = source ? [...new Set([...source.matchAll(/\btools\.([A-Za-z0-9_]+)/g)].map((match) => match[1]))].slice(0, 8) : [];
|
|
123
|
+
const name = tools.length === 1 ? tools[0] : entry.label ?? "tool";
|
|
124
|
+
return { args: record(value), source, tools, name };
|
|
125
|
+
}
|
|
126
|
+
function patchPath(source) {
|
|
127
|
+
return /\*\*\* (?:Update|Add|Delete) File:\s*([^\r\n]+)/.exec(source)?.[1]?.trim() ?? "";
|
|
128
|
+
}
|
|
129
|
+
function explicitNumber(sources, keys) {
|
|
130
|
+
for (const source of sources) {
|
|
131
|
+
for (const key of keys) {
|
|
132
|
+
const value = source?.[key];
|
|
133
|
+
const parsed = typeof value === "string" && value.trim() !== "" ? Number(value) : value;
|
|
134
|
+
if (typeof parsed === "number" && Number.isFinite(parsed)) return parsed;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
function toolDetail(category) {
|
|
140
|
+
return { read: "file", search: "matches", edit: "diff", command: "terminal", test: "terminal", web: "web", agent: "agent", plan: "plan" }[category] ?? "fields";
|
|
141
|
+
}
|
|
142
|
+
function usefulToolFields(args) {
|
|
143
|
+
if (!args) return [];
|
|
144
|
+
const hidden = /* @__PURE__ */ new Set(["command", "cmd", "file_path", "target_file", "path", "query", "pattern", "url", "patch", "diff", "old_string", "new_string", "content", "prompt", "description", "task", "plan", "todos"]);
|
|
145
|
+
return Object.entries(args).flatMap(([key, value]) => {
|
|
146
|
+
if (hidden.has(key) || value === null || value === void 0) return [];
|
|
147
|
+
const rendered = typeof value === "string" ? value : JSON.stringify(value);
|
|
148
|
+
if (!rendered) return [];
|
|
149
|
+
return [{ label: key.replaceAll(/[_-]+/g, " ").replace(/^./, (letter) => letter.toLocaleUpperCase()), value: boundedString(rendered, MAX_TOOL_FIELD_CHARS) }];
|
|
150
|
+
}).slice(0, MAX_TOOL_FIELDS);
|
|
151
|
+
}
|
|
152
|
+
function planItems(args) {
|
|
153
|
+
const source = Array.isArray(args?.plan) ? args.plan : Array.isArray(args?.todos) ? args.todos : [];
|
|
154
|
+
return source.flatMap((item) => {
|
|
155
|
+
if (typeof item === "string" && item.trim()) return [{ label: boundedString(item.trim(), 300), status: "" }];
|
|
156
|
+
const value = record(item);
|
|
157
|
+
const label = firstString(value, ["step", "title", "content", "text"]);
|
|
158
|
+
if (!label) return [];
|
|
159
|
+
return [{ label: boundedString(label, 300), status: firstString(value, ["status"]) }];
|
|
160
|
+
}).slice(0, 12);
|
|
161
|
+
}
|
|
162
|
+
function editPreview(args, resultText, source) {
|
|
163
|
+
const direct = firstString(args, ["patch", "diff"]);
|
|
164
|
+
if (direct) return direct;
|
|
165
|
+
const oldText = firstString(args, ["old_string"]);
|
|
166
|
+
const newText = firstString(args, ["new_string"]);
|
|
167
|
+
if (oldText || newText) {
|
|
168
|
+
return [
|
|
169
|
+
...oldText.split("\n").map((line) => `- ${line}`),
|
|
170
|
+
...newText.split("\n").map((line) => `+ ${line}`)
|
|
171
|
+
].join("\n");
|
|
172
|
+
}
|
|
173
|
+
const patch = /\*\*\* Begin Patch[\s\S]*?\*\*\* End Patch/.exec(source ?? "")?.[0];
|
|
174
|
+
if (patch) return patch;
|
|
175
|
+
return /^(?:diff --git|@@ |--- |\+\+\+ )/m.test(resultText ?? "") ? resultText : "";
|
|
176
|
+
}
|
|
177
|
+
function classifyTool(name, command) {
|
|
178
|
+
const normalized = name.toLocaleLowerCase();
|
|
179
|
+
if (/write_stdin|^wait$/.test(normalized)) return "command";
|
|
180
|
+
if (/update.?plan|todo|checklist|taskcreate|taskupdate/.test(normalized)) return "plan";
|
|
181
|
+
if (/search.?replace|edit|write|patch|replace|create_file|apply_patch/.test(normalized)) return "edit";
|
|
182
|
+
if (/read|view|open_file|list_dir/.test(normalized)) return "read";
|
|
183
|
+
if (/search|find|grep|glob|toolsearch/.test(normalized)) return "search";
|
|
184
|
+
if (/browser|web|fetch|url/.test(normalized)) return "web";
|
|
185
|
+
if (/agent|subagent|sendmessage|delegate|^task$/.test(normalized)) return "agent";
|
|
186
|
+
if (/test|typecheck|lint|build/.test(normalized)) return "test";
|
|
187
|
+
if (/terminal|bash|shell|command|exec|write_stdin|^wait$/.test(normalized)) return /\b(test|typecheck|lint|build)\b/i.test(command) ? "test" : "command";
|
|
188
|
+
return "other";
|
|
189
|
+
}
|
|
190
|
+
function toolAction(status, category, name, tools) {
|
|
191
|
+
const position = status === "pending" ? 0 : status === "error" ? 2 : 1;
|
|
192
|
+
if (tools.length > 1) return [`Running ${tools.length} actions`, `Ran ${tools.length} actions`, `${tools.length} actions failed`][position];
|
|
193
|
+
const normalized = name.toLocaleLowerCase();
|
|
194
|
+
if (/sendmessage/.test(normalized)) return ["Messaging agent", "Messaged agent", "Agent message failed"][position];
|
|
195
|
+
if (/kill_command_or_subagent/.test(normalized)) return ["Stopping", "Stopped", "Stop failed"][position];
|
|
196
|
+
if (/get_command_or_subagent_output|write_stdin|^wait$/.test(normalized)) return ["Waiting for", "Checked", "Check failed"][position];
|
|
197
|
+
const actions = {
|
|
198
|
+
read: ["Reading", "Read", "Read failed"],
|
|
199
|
+
search: ["Searching", "Searched", "Search failed"],
|
|
200
|
+
edit: ["Editing", "Edited", "Edit failed"],
|
|
201
|
+
command: ["Running command", "Ran command", "Command failed"],
|
|
202
|
+
test: ["Running tests", "Ran tests", "Tests failed"],
|
|
203
|
+
web: ["Browsing", "Browsed", "Browser action failed"],
|
|
204
|
+
agent: ["Starting agent", "Started agent", "Agent failed"],
|
|
205
|
+
plan: ["Updating plan", "Updated plan", "Plan update failed"]
|
|
206
|
+
};
|
|
207
|
+
return actions[category]?.[position] ?? (status === "error" ? `${name} failed` : name.replaceAll(/[_-]+/g, " "));
|
|
208
|
+
}
|
|
209
|
+
function createToolPresentation(entry) {
|
|
210
|
+
const envelope = toolEnvelope(entry);
|
|
211
|
+
const args = envelope.args;
|
|
212
|
+
const command = firstString(args, ["command", "cmd"]) || sourceString(envelope.source, ["command", "cmd"]);
|
|
213
|
+
const category = classifyTool(envelope.name, command || envelope.source || entry.arguments || "");
|
|
214
|
+
const path = firstString(args, ["file_path", "target_file", "target_directory", "path"]) || sourceString(envelope.source, ["file_path", "target_file", "target_directory", "path"]) || patchPath(envelope.source);
|
|
215
|
+
const query = firstString(args, ["query", "pattern"]) || sourceString(envelope.source, ["query", "pattern", "q"]);
|
|
216
|
+
const url = firstString(args, ["url"]) || sourceString(envelope.source, ["url", "ref_id"]);
|
|
217
|
+
const subject = firstString(args, ["subject", "description", "summary", "task", "prompt"]) || sourceString(envelope.source, ["subject", "description", "summary", "task", "prompt"]);
|
|
218
|
+
const background = /get_command_or_subagent_output|kill_command_or_subagent/i.test(envelope.name) ? "background task" : /write_stdin|^wait$/i.test(envelope.name) ? "background command" : "";
|
|
219
|
+
const items = planItems(args);
|
|
220
|
+
const taskId = firstString(args, ["taskId", "task_id"]);
|
|
221
|
+
const planTarget = category === "plan" ? items.length ? `${items.length} ${items.length === 1 ? "item" : "items"}` : taskId ? `task ${taskId}` : "" : "";
|
|
222
|
+
const target = path || command || query || url || subject || background || planTarget || (envelope.tools.length > 1 ? `${envelope.tools.length} coordinated actions` : toolTarget(entry.arguments));
|
|
223
|
+
const previewSource = category === "edit" ? editPreview(args, entry.resultText, envelope.source) : entry.resultText ?? "";
|
|
224
|
+
const result = record(entry.resultContent);
|
|
225
|
+
const metadata = record(entry.metadata);
|
|
226
|
+
const resultMetadata = record(result?.metadata);
|
|
227
|
+
return {
|
|
228
|
+
name: boundedString(envelope.name, 120),
|
|
229
|
+
action: boundedString(toolAction(entry.status ?? "completed", category, envelope.name, envelope.tools), 120),
|
|
230
|
+
category,
|
|
231
|
+
detail: toolDetail(category),
|
|
232
|
+
target: boundedString(target, 300),
|
|
233
|
+
command: boundedString(command, MAX_TOOL_FIELD_CHARS),
|
|
234
|
+
path: boundedString(path, MAX_TOOL_FIELD_CHARS),
|
|
235
|
+
query: boundedString(query, MAX_TOOL_FIELD_CHARS),
|
|
236
|
+
url: boundedString(url, MAX_TOOL_FIELD_CHARS),
|
|
237
|
+
subject: boundedString(subject, MAX_TOOL_FIELD_CHARS),
|
|
238
|
+
preview: boundedString(previewSource, MAX_TOOL_PREVIEW_CHARS),
|
|
239
|
+
fields: usefulToolFields(args),
|
|
240
|
+
items,
|
|
241
|
+
tools: envelope.tools,
|
|
242
|
+
exitCode: explicitNumber([result, resultMetadata, metadata], ["exit_code", "exitCode", "pi_bash_exit_code"]),
|
|
243
|
+
durationMs: explicitNumber([result, resultMetadata, metadata], ["duration_ms", "durationMs", "elapsed_ms", "elapsedMs", "totalDurationMs"]),
|
|
244
|
+
additions: explicitNumber([result, resultMetadata, metadata], ["additions", "lines_added"]),
|
|
245
|
+
deletions: explicitNumber([result, resultMetadata, metadata], ["deletions", "lines_removed"]),
|
|
246
|
+
matches: explicitNumber([result, resultMetadata, metadata], ["matches", "match_count", "result_count"])
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function readToolPresentation(value, entry) {
|
|
250
|
+
const generated = createToolPresentation(entry);
|
|
251
|
+
const item = record(value);
|
|
252
|
+
if (!item) return generated;
|
|
253
|
+
const fields = Array.isArray(item.fields) ? item.fields.flatMap((raw) => {
|
|
254
|
+
const field = record(raw);
|
|
255
|
+
return field && typeof field.label === "string" && typeof field.value === "string" ? [{ label: boundedString(field.label, 80), value: boundedString(field.value, MAX_TOOL_FIELD_CHARS) }] : [];
|
|
256
|
+
}).slice(0, MAX_TOOL_FIELDS) : generated.fields;
|
|
257
|
+
const items = Array.isArray(item.items) ? item.items.flatMap((raw) => {
|
|
258
|
+
const planItem = record(raw);
|
|
259
|
+
return planItem && typeof planItem.label === "string" ? [{ label: boundedString(planItem.label, 300), status: boundedString(planItem.status, 40) }] : [];
|
|
260
|
+
}).slice(0, 12) : generated.items;
|
|
261
|
+
const tools = Array.isArray(item.tools) ? item.tools.filter((tool) => typeof tool === "string").map((tool) => boundedString(tool, 120)).slice(0, 8) : generated.tools;
|
|
262
|
+
return {
|
|
263
|
+
name: boundedString(item.name, 120) || generated.name,
|
|
264
|
+
action: boundedString(item.action, 120) || generated.action,
|
|
265
|
+
category: TOOL_CATEGORIES.has(item.category) ? item.category : generated.category,
|
|
266
|
+
detail: TOOL_DETAILS.has(item.detail) ? item.detail : generated.detail,
|
|
267
|
+
target: boundedString(item.target, 300) || generated.target,
|
|
268
|
+
command: boundedString(item.command, MAX_TOOL_FIELD_CHARS) || generated.command,
|
|
269
|
+
path: boundedString(item.path, MAX_TOOL_FIELD_CHARS) || generated.path,
|
|
270
|
+
query: boundedString(item.query, MAX_TOOL_FIELD_CHARS) || generated.query,
|
|
271
|
+
url: boundedString(item.url, MAX_TOOL_FIELD_CHARS) || generated.url,
|
|
272
|
+
subject: boundedString(item.subject, MAX_TOOL_FIELD_CHARS) || generated.subject,
|
|
273
|
+
preview: boundedString(item.preview, MAX_TOOL_PREVIEW_CHARS) || generated.preview,
|
|
274
|
+
fields,
|
|
275
|
+
items,
|
|
276
|
+
tools,
|
|
277
|
+
exitCode: nullableNumber(item.exitCode) ?? generated.exitCode,
|
|
278
|
+
durationMs: nullableNumber(item.durationMs) ?? generated.durationMs,
|
|
279
|
+
additions: nullableNumber(item.additions) ?? generated.additions,
|
|
280
|
+
deletions: nullableNumber(item.deletions) ?? generated.deletions,
|
|
281
|
+
matches: nullableNumber(item.matches) ?? generated.matches
|
|
282
|
+
};
|
|
283
|
+
}
|
|
79
284
|
function readTranscript(value) {
|
|
80
285
|
if (!Array.isArray(value)) return [];
|
|
81
286
|
const result = [];
|
|
@@ -93,6 +298,7 @@ function readTranscript(value) {
|
|
|
93
298
|
if (typeof item[key] === "string") entry[key] = item[key];
|
|
94
299
|
}
|
|
95
300
|
if (["pending", "completed", "error"].includes(item.status)) entry.status = item.status;
|
|
301
|
+
if (item.role === "tool") entry.presentation = readToolPresentation(item.presentation, entry);
|
|
96
302
|
if (typeof item.streaming === "boolean") entry.streaming = item.streaming;
|
|
97
303
|
if (Array.isArray(item.context)) {
|
|
98
304
|
entry.context = item.context.flatMap((raw) => {
|
|
@@ -300,15 +506,10 @@ function groupConversation(entries) {
|
|
|
300
506
|
return blocks;
|
|
301
507
|
}
|
|
302
508
|
function toolCategory(entry) {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if (/test|typecheck|lint|build/.test(name)) return "test";
|
|
308
|
-
if (/terminal|bash|shell|command|exec/.test(name)) return /\b(test|typecheck|lint|build)\b/i.test(entry.arguments ?? "") ? "test" : "command";
|
|
309
|
-
if (/browser|web|fetch|url/.test(name)) return "web";
|
|
310
|
-
if (/subagent|spawn|task/.test(name)) return "agent";
|
|
311
|
-
return "other";
|
|
509
|
+
if (TOOL_CATEGORIES.has(entry.presentation?.category)) return entry.presentation.category;
|
|
510
|
+
const envelope = toolEnvelope(entry);
|
|
511
|
+
const command = firstString(envelope.args, ["command", "cmd"]) || sourceString(envelope.source, ["command", "cmd"]);
|
|
512
|
+
return classifyTool(envelope.name, command || envelope.source || entry.arguments || "");
|
|
312
513
|
}
|
|
313
514
|
function toolTarget(argumentsText) {
|
|
314
515
|
if (!argumentsText) return "";
|
|
@@ -334,7 +535,8 @@ function activitySummary(entries) {
|
|
|
334
535
|
if (pending) return `${entries.length} actions in progress`;
|
|
335
536
|
if (failed) return `${entries.length} actions \xB7 ${failed} failed`;
|
|
336
537
|
if ([...counts.keys()].every((key) => key === "read" || key === "search")) return `Explored ${entries.length} ${entries.length === 1 ? "item" : "items"}`;
|
|
337
|
-
|
|
538
|
+
const nouns = { read: ["read", "reads"], search: ["search", "searches"], edit: ["edit", "edits"], command: ["command", "commands"], test: ["test run", "test runs"], web: ["web action", "web actions"], agent: ["agent action", "agent actions"], plan: ["plan update", "plan updates"], other: ["action", "actions"] };
|
|
539
|
+
return [...counts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3).map(([category, count]) => `${count} ${nouns[category][count === 1 ? 0 : 1]}`).join(" \xB7 ");
|
|
338
540
|
}
|
|
339
541
|
function canContinueHere(state) {
|
|
340
542
|
if (state.mode !== "mirror" || state.canSend) return false;
|
|
@@ -544,6 +746,7 @@ var TOOL_ICONS = {
|
|
|
544
746
|
/* @__PURE__ */ jsx3("circle", { cx: "9", cy: "6", r: "2.5" }),
|
|
545
747
|
/* @__PURE__ */ jsx3("path", { d: "M4 15c.4-3 2-4.5 5-4.5s4.6 1.5 5 4.5" })
|
|
546
748
|
] }),
|
|
749
|
+
plan: () => /* @__PURE__ */ jsx3(Fragment, { children: /* @__PURE__ */ jsx3("path", { d: "m3 5 1 1 2-2M3 9l1 1 2-2M3 13l1 1 2-2M8 5h7M8 9h7M8 13h7" }) }),
|
|
547
750
|
other: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
548
751
|
/* @__PURE__ */ jsx3("path", { d: "M9 2.5v3M9 12.5v3M2.5 9h3M12.5 9h3" }),
|
|
549
752
|
/* @__PURE__ */ jsx3("circle", { cx: "9", cy: "9", r: "3.5" })
|
|
@@ -553,7 +756,8 @@ function ToolIcon({ category }) {
|
|
|
553
756
|
const Glyph = TOOL_ICONS[category] ?? TOOL_ICONS.other;
|
|
554
757
|
return /* @__PURE__ */ jsx3("svg", { class: "scui-tool-icon", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", "stroke-width": "1.35", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx3(Glyph, {}) });
|
|
555
758
|
}
|
|
556
|
-
function
|
|
759
|
+
function toolAction2(entry, category, presentation) {
|
|
760
|
+
if (presentation?.action) return presentation.action;
|
|
557
761
|
const status = entry.status ?? "completed";
|
|
558
762
|
const actions = {
|
|
559
763
|
read: ["Reading", "Read", "Read failed"],
|
|
@@ -562,7 +766,8 @@ function toolAction(entry, category) {
|
|
|
562
766
|
command: ["Running command", "Ran command", "Command failed"],
|
|
563
767
|
test: ["Running tests", "Ran tests", "Tests failed"],
|
|
564
768
|
web: ["Browsing", "Browsed", "Browser action failed"],
|
|
565
|
-
agent: ["Starting agent", "Started agent", "Agent failed"]
|
|
769
|
+
agent: ["Starting agent", "Started agent", "Agent failed"],
|
|
770
|
+
plan: ["Updating plan", "Updated plan", "Plan update failed"]
|
|
566
771
|
};
|
|
567
772
|
const position = status === "pending" ? 0 : status === "error" ? 2 : 1;
|
|
568
773
|
if (actions[category]) return actions[category][position];
|
|
@@ -588,6 +793,138 @@ function argumentRows(argumentsText) {
|
|
|
588
793
|
}
|
|
589
794
|
return [{ key: "arguments", label: "Details", value: argumentsText }];
|
|
590
795
|
}
|
|
796
|
+
function stripAnsi(value) {
|
|
797
|
+
return value?.replaceAll(/[\u001B\u009B][[\]()#;?]*(?:(?:(?:[a-zA-Z\d]*(?:;[-a-zA-Z\d\/#&.:=?%@~_]+)*)?\u0007)|(?:(?:\d{1,4}(?:[;:]\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g, "") ?? "";
|
|
798
|
+
}
|
|
799
|
+
function formatDuration(value) {
|
|
800
|
+
if (value === null) return "";
|
|
801
|
+
if (value < 1e3) return `${Math.round(value)}ms`;
|
|
802
|
+
return `${(value / 1e3).toFixed(value < 1e4 ? 1 : 0)}s`;
|
|
803
|
+
}
|
|
804
|
+
function ToolMetrics({ presentation }) {
|
|
805
|
+
const metrics = [
|
|
806
|
+
presentation.matches === null ? "" : `${presentation.matches} ${presentation.matches === 1 ? "match" : "matches"}`,
|
|
807
|
+
presentation.additions === null ? "" : `+${presentation.additions}`,
|
|
808
|
+
presentation.deletions === null ? "" : `\u2212${presentation.deletions}`,
|
|
809
|
+
presentation.exitCode === null ? "" : `exit ${presentation.exitCode}`,
|
|
810
|
+
formatDuration(presentation.durationMs)
|
|
811
|
+
].filter(Boolean);
|
|
812
|
+
return metrics.length ? /* @__PURE__ */ jsx3("div", { class: "scui-tool-metrics", children: metrics.map((metric) => /* @__PURE__ */ jsx3("span", { children: metric }, metric)) }) : null;
|
|
813
|
+
}
|
|
814
|
+
function PendingElapsed({ now }) {
|
|
815
|
+
const clock = now ?? Date.now;
|
|
816
|
+
const started = useRef2(clock());
|
|
817
|
+
const [elapsed, setElapsed] = useState2(0);
|
|
818
|
+
useEffect2(() => {
|
|
819
|
+
const timer = setInterval(() => setElapsed(Math.max(0, clock() - started.current)), 1e3);
|
|
820
|
+
return () => clearInterval(timer);
|
|
821
|
+
}, [clock]);
|
|
822
|
+
return /* @__PURE__ */ jsx3("small", { class: "scui-tool-elapsed", children: elapsed < 1e3 ? "now" : formatDuration(elapsed) });
|
|
823
|
+
}
|
|
824
|
+
function LinePreview({ value, kind }) {
|
|
825
|
+
if (!value) return null;
|
|
826
|
+
return /* @__PURE__ */ jsx3("ol", { class: "scui-code-preview", "data-kind": kind, children: stripAnsi(value).split("\n").map((line, index) => {
|
|
827
|
+
const tone = kind === "diff" ? line.startsWith("+") && !line.startsWith("+++") ? "add" : line.startsWith("-") && !line.startsWith("---") ? "remove" : line.startsWith("@@") ? "hunk" : "plain" : "plain";
|
|
828
|
+
return /* @__PURE__ */ jsxs2("li", { "data-tone": tone, children: [
|
|
829
|
+
/* @__PURE__ */ jsx3("span", { children: index + 1 }),
|
|
830
|
+
/* @__PURE__ */ jsx3("code", { children: line || " " })
|
|
831
|
+
] }, index);
|
|
832
|
+
}) });
|
|
833
|
+
}
|
|
834
|
+
function TerminalPreview({ presentation, pending, failed }) {
|
|
835
|
+
return /* @__PURE__ */ jsxs2("section", { class: "scui-terminal", children: [
|
|
836
|
+
/* @__PURE__ */ jsxs2("header", { children: [
|
|
837
|
+
/* @__PURE__ */ jsxs2("span", { "aria-hidden": "true", children: [
|
|
838
|
+
/* @__PURE__ */ jsx3("i", {}),
|
|
839
|
+
/* @__PURE__ */ jsx3("i", {}),
|
|
840
|
+
/* @__PURE__ */ jsx3("i", {})
|
|
841
|
+
] }),
|
|
842
|
+
/* @__PURE__ */ jsx3("code", { children: presentation.command ? `$ ${presentation.command}` : "Terminal" })
|
|
843
|
+
] }),
|
|
844
|
+
presentation.preview ? /* @__PURE__ */ jsx3("pre", { "data-error": failed, children: stripAnsi(presentation.preview) }) : pending ? /* @__PURE__ */ jsxs2("div", { class: "scui-terminal-wait", children: [
|
|
845
|
+
/* @__PURE__ */ jsx3("i", {}),
|
|
846
|
+
" Waiting for output"
|
|
847
|
+
] }) : /* @__PURE__ */ jsx3("div", { class: "scui-terminal-empty", children: "No output" })
|
|
848
|
+
] });
|
|
849
|
+
}
|
|
850
|
+
function SearchPreview({ presentation }) {
|
|
851
|
+
const lines = stripAnsi(presentation.preview).split("\n").filter(Boolean);
|
|
852
|
+
return /* @__PURE__ */ jsxs2("section", { class: "scui-search-preview", children: [
|
|
853
|
+
presentation.query ? /* @__PURE__ */ jsxs2("header", { children: [
|
|
854
|
+
/* @__PURE__ */ jsx3("span", { children: "Search" }),
|
|
855
|
+
/* @__PURE__ */ jsx3("code", { children: presentation.query })
|
|
856
|
+
] }) : null,
|
|
857
|
+
lines.length ? /* @__PURE__ */ jsx3("ol", { children: lines.map((line, index) => {
|
|
858
|
+
const match = /^(.*?):(\d+)(?::(\d+))?:(.*)$/.exec(line);
|
|
859
|
+
return /* @__PURE__ */ jsx3("li", { children: match ? /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
860
|
+
/* @__PURE__ */ jsx3("code", { children: match[1] }),
|
|
861
|
+
/* @__PURE__ */ jsxs2("small", { children: [
|
|
862
|
+
match[2],
|
|
863
|
+
match[3] ? `:${match[3]}` : ""
|
|
864
|
+
] }),
|
|
865
|
+
/* @__PURE__ */ jsx3("span", { children: match[4] })
|
|
866
|
+
] }) : /* @__PURE__ */ jsx3("span", { children: line }) }, index);
|
|
867
|
+
}) }) : /* @__PURE__ */ jsx3("p", { children: "No textual results" })
|
|
868
|
+
] });
|
|
869
|
+
}
|
|
870
|
+
function ToolPreview({ presentation, entry }) {
|
|
871
|
+
const pending = entry.status === "pending";
|
|
872
|
+
const failed = entry.status === "error";
|
|
873
|
+
if (presentation.detail === "terminal") return /* @__PURE__ */ jsx3(TerminalPreview, { presentation, pending, failed });
|
|
874
|
+
if (presentation.detail === "diff") return presentation.preview ? /* @__PURE__ */ jsx3(LinePreview, { value: presentation.preview, kind: "diff" }) : /* @__PURE__ */ jsx3("div", { class: "scui-tool-empty", children: "Edit completed without a textual diff" });
|
|
875
|
+
if (presentation.detail === "file") return presentation.preview ? /* @__PURE__ */ jsx3(LinePreview, { value: presentation.preview, kind: "file" }) : /* @__PURE__ */ jsx3("div", { class: "scui-tool-empty", children: "File contents were not included in this event" });
|
|
876
|
+
if (presentation.detail === "matches") return /* @__PURE__ */ jsx3(SearchPreview, { presentation });
|
|
877
|
+
if (presentation.detail === "web") return /* @__PURE__ */ jsxs2("section", { class: "scui-web-preview", children: [
|
|
878
|
+
presentation.url ? /* @__PURE__ */ jsx3("code", { children: presentation.url }) : null,
|
|
879
|
+
presentation.preview ? /* @__PURE__ */ jsx3("p", { children: stripAnsi(presentation.preview) }) : /* @__PURE__ */ jsx3("small", { children: pending ? "Waiting for the page" : "No page summary returned" })
|
|
880
|
+
] });
|
|
881
|
+
if (presentation.detail === "agent") return /* @__PURE__ */ jsx3("section", { class: "scui-agent-preview", children: presentation.preview ? /* @__PURE__ */ jsx3("pre", { children: stripAnsi(presentation.preview) }) : /* @__PURE__ */ jsx3("small", { children: pending ? "Agent is working" : "No textual handoff returned" }) });
|
|
882
|
+
if (presentation.detail === "plan") return /* @__PURE__ */ jsx3("ol", { class: "scui-plan-preview", children: presentation.items?.map((item, index) => /* @__PURE__ */ jsxs2("li", { "data-status": item.status, children: [
|
|
883
|
+
/* @__PURE__ */ jsx3("i", { "aria-hidden": "true" }),
|
|
884
|
+
/* @__PURE__ */ jsx3("span", { children: item.label })
|
|
885
|
+
] }, `${item.label}:${index}`)) });
|
|
886
|
+
return presentation.preview ? /* @__PURE__ */ jsx3("pre", { class: "scui-tool-output", "data-error": failed, children: stripAnsi(presentation.preview) }) : null;
|
|
887
|
+
}
|
|
888
|
+
function ToolActions({ presentation, adapter }) {
|
|
889
|
+
const [copied, setCopied] = useState2(false);
|
|
890
|
+
const reset = useRef2(null);
|
|
891
|
+
useEffect2(() => () => clearTimeout(reset.current), []);
|
|
892
|
+
if (!adapter?.copyText) return null;
|
|
893
|
+
const action = presentation.command ? ["Copy command", presentation.command] : presentation.path ? ["Copy path", presentation.path] : presentation.url ? ["Copy URL", presentation.url] : presentation.query ? ["Copy query", presentation.query] : null;
|
|
894
|
+
const copy = async () => {
|
|
895
|
+
await adapter.copyText(action[1]);
|
|
896
|
+
setCopied(true);
|
|
897
|
+
clearTimeout(reset.current);
|
|
898
|
+
reset.current = setTimeout(() => setCopied(false), 1500);
|
|
899
|
+
};
|
|
900
|
+
return action ? /* @__PURE__ */ jsx3("div", { class: "scui-tool-actions", children: /* @__PURE__ */ jsx3("button", { type: "button", onClick: copy, children: copied ? "Copied" : action[0] }) }) : null;
|
|
901
|
+
}
|
|
902
|
+
function ToolStack({ tools }) {
|
|
903
|
+
if (tools.length < 2) return null;
|
|
904
|
+
return /* @__PURE__ */ jsx3("div", { class: "scui-tool-stack", "aria-label": "Coordinated tools", children: tools.map((tool) => /* @__PURE__ */ jsx3("span", { children: tool.replaceAll("__", " \xB7 ").replaceAll("_", " ") }, tool)) });
|
|
905
|
+
}
|
|
906
|
+
function TechnicalDetails({ entry }) {
|
|
907
|
+
if (!entry.arguments && !entry.resultText) return null;
|
|
908
|
+
return /* @__PURE__ */ jsxs2("details", { class: "scui-tool-technical", children: [
|
|
909
|
+
/* @__PURE__ */ jsx3("summary", { children: "Technical details" }),
|
|
910
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
911
|
+
entry.arguments ? /* @__PURE__ */ jsxs2("section", { children: [
|
|
912
|
+
/* @__PURE__ */ jsx3("strong", { children: "Native arguments" }),
|
|
913
|
+
/* @__PURE__ */ jsx3("dl", { children: argumentRows(entry.arguments).map((row) => /* @__PURE__ */ jsxs2("div", { children: [
|
|
914
|
+
/* @__PURE__ */ jsx3("dt", { children: row.label }),
|
|
915
|
+
/* @__PURE__ */ jsx3("dd", { children: /* @__PURE__ */ jsx3("pre", { children: row.value }) })
|
|
916
|
+
] }, row.key)) })
|
|
917
|
+
] }) : null,
|
|
918
|
+
entry.resultText ? /* @__PURE__ */ jsxs2("section", { children: [
|
|
919
|
+
/* @__PURE__ */ jsx3("strong", { children: "Native result" }),
|
|
920
|
+
/* @__PURE__ */ jsxs2("pre", { "data-error": entry.status === "error", children: [
|
|
921
|
+
entry.resultText,
|
|
922
|
+
entry.truncated ? "\n[truncated]" : ""
|
|
923
|
+
] })
|
|
924
|
+
] }) : null
|
|
925
|
+
] })
|
|
926
|
+
] });
|
|
927
|
+
}
|
|
591
928
|
function TranscriptEntry({ entry, state, adapter }) {
|
|
592
929
|
if (entry.role === "request") return /* @__PURE__ */ jsx3(RequestCard, { entry, adapter, canRespond: state.canRespond });
|
|
593
930
|
if (entry.role === "reasoning") {
|
|
@@ -603,38 +940,42 @@ function TranscriptEntry({ entry, state, adapter }) {
|
|
|
603
940
|
entry.truncated ? /* @__PURE__ */ jsx3("small", { class: "scui-truncated", children: "Entry truncated by host \xB7 load native session for the complete value" }) : null
|
|
604
941
|
] });
|
|
605
942
|
}
|
|
606
|
-
function ToolRow({ entry, workspace }) {
|
|
607
|
-
const
|
|
608
|
-
const
|
|
609
|
-
|
|
943
|
+
function ToolRow({ entry, workspace, open = false, adapter }) {
|
|
944
|
+
const presentation = entry.presentation ?? createToolPresentation(entry);
|
|
945
|
+
const [expanded, setExpanded] = useState2(open || entry.status === "pending");
|
|
946
|
+
useEffect2(() => {
|
|
947
|
+
if (entry.status === "pending") setExpanded(true);
|
|
948
|
+
}, [entry.status]);
|
|
949
|
+
const target = compactToolTarget(presentation.target, workspace);
|
|
950
|
+
const hasDetail = Boolean(entry.arguments || entry.resultText || presentation.preview || presentation.fields.length);
|
|
951
|
+
const category = presentation.category ?? toolCategory(entry);
|
|
610
952
|
const summary = /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
611
953
|
/* @__PURE__ */ jsx3(ToolIcon, { category }),
|
|
612
|
-
/* @__PURE__ */ jsx3("strong", { children:
|
|
613
|
-
target ? /* @__PURE__ */ jsx3("code", { class: "scui-tool-target", title:
|
|
954
|
+
/* @__PURE__ */ jsx3("strong", { children: toolAction2(entry, category, presentation) }),
|
|
955
|
+
target ? /* @__PURE__ */ jsx3("code", { class: "scui-tool-target", title: presentation.target, children: target }) : null,
|
|
614
956
|
/* @__PURE__ */ jsx3("span", { class: "scui-spacer" }),
|
|
957
|
+
entry.status === "pending" ? /* @__PURE__ */ jsx3(PendingElapsed, { now: adapter?.now }) : null,
|
|
615
958
|
/* @__PURE__ */ jsx3("span", { class: "scui-tool-status", role: "status", "data-status": entry.status ?? "completed", "aria-label": entry.status ?? "completed", children: entry.status === "pending" ? /* @__PURE__ */ jsx3("i", {}) : entry.status === "error" ? "\xD7" : "\u2713" }),
|
|
616
959
|
hasDetail ? /* @__PURE__ */ jsx3("span", { class: "scui-tool-chevron", "aria-hidden": "true", children: "\u203A" }) : null
|
|
617
960
|
] });
|
|
618
961
|
if (!hasDetail) return /* @__PURE__ */ jsx3("div", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: /* @__PURE__ */ jsx3("div", { class: "scui-tool-head", children: summary }) });
|
|
619
|
-
return /* @__PURE__ */ jsxs2("details", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: [
|
|
962
|
+
return /* @__PURE__ */ jsxs2("details", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, open: expanded, onToggle: (event) => setExpanded(event.currentTarget.open), children: [
|
|
620
963
|
/* @__PURE__ */ jsx3("summary", { class: "scui-tool-head", children: summary }),
|
|
621
964
|
/* @__PURE__ */ jsxs2("div", { class: "scui-tool-detail", children: [
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
/* @__PURE__ */ jsx3("
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
] })
|
|
632
|
-
] }) : null
|
|
965
|
+
/* @__PURE__ */ jsx3(ToolMetrics, { presentation }),
|
|
966
|
+
/* @__PURE__ */ jsx3(ToolStack, { tools: presentation.tools ?? [] }),
|
|
967
|
+
/* @__PURE__ */ jsx3(ToolPreview, { presentation, entry }),
|
|
968
|
+
presentation.fields.length ? /* @__PURE__ */ jsx3("dl", { class: "scui-tool-fields", children: presentation.fields.map((field) => /* @__PURE__ */ jsxs2("div", { children: [
|
|
969
|
+
/* @__PURE__ */ jsx3("dt", { children: field.label }),
|
|
970
|
+
/* @__PURE__ */ jsx3("dd", { children: field.value })
|
|
971
|
+
] }, field.label)) }) : null,
|
|
972
|
+
/* @__PURE__ */ jsx3(ToolActions, { presentation, adapter }),
|
|
973
|
+
/* @__PURE__ */ jsx3(TechnicalDetails, { entry })
|
|
633
974
|
] })
|
|
634
975
|
] });
|
|
635
976
|
}
|
|
636
|
-
function ActivityGroup({ entries, state }) {
|
|
637
|
-
if (entries.length === 1) return /* @__PURE__ */ jsx3("section", { class: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx3(ToolRow, { entry: entries[0], workspace: state.workspace }) });
|
|
977
|
+
function ActivityGroup({ entries, state, adapter }) {
|
|
978
|
+
if (entries.length === 1) return /* @__PURE__ */ jsx3("section", { class: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx3(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
|
|
638
979
|
const active = entries.some((entry) => entry.status === "pending");
|
|
639
980
|
const [open, setOpen] = useState2(active);
|
|
640
981
|
const id = useId();
|
|
@@ -652,7 +993,7 @@ function ActivityGroup({ entries, state }) {
|
|
|
652
993
|
entries.length
|
|
653
994
|
] })
|
|
654
995
|
] }),
|
|
655
|
-
open ? /* @__PURE__ */ jsx3("div", { id, children: entries.map((entry) => /* @__PURE__ */ jsx3(ToolRow, { entry, workspace: state.workspace }, entry.id)) }) : null
|
|
996
|
+
open ? /* @__PURE__ */ jsx3("div", { id, children: entries.map((entry) => /* @__PURE__ */ jsx3(ToolRow, { entry, workspace: state.workspace, adapter }, entry.id)) }) : null
|
|
656
997
|
] });
|
|
657
998
|
}
|
|
658
999
|
function TaskPlan({ plan }) {
|
|
@@ -1123,6 +1464,7 @@ export {
|
|
|
1123
1464
|
SessionRow,
|
|
1124
1465
|
SupercodeMessenger,
|
|
1125
1466
|
TaskPlan,
|
|
1467
|
+
ToolRow,
|
|
1126
1468
|
TranscriptEntry,
|
|
1127
1469
|
hasHarnessLogo
|
|
1128
1470
|
};
|
package/controller.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { normalizeUiState } from './core.mjs';
|
|
1
|
+
import { createToolPresentation, normalizeUiState } from './core.mjs';
|
|
2
2
|
|
|
3
3
|
const HARNESS_LABELS = {
|
|
4
4
|
'claude-code': 'Claude Code',
|
|
@@ -126,7 +126,7 @@ function projectConversationEntry(entry, maxEntryChars) {
|
|
|
126
126
|
const argumentsText = truncate((entry.arguments ?? '').trim(), maxEntryChars);
|
|
127
127
|
const result = truncate((entry.resultText ?? '').trim(), maxEntryChars);
|
|
128
128
|
const primary = entry.status === 'pending' ? argumentsText : result;
|
|
129
|
-
|
|
129
|
+
const projected = {
|
|
130
130
|
id: entry.id,
|
|
131
131
|
role: 'tool',
|
|
132
132
|
text: primary.text,
|
|
@@ -137,6 +137,12 @@ function projectConversationEntry(entry, maxEntryChars) {
|
|
|
137
137
|
resultText: result.text,
|
|
138
138
|
status: entry.status,
|
|
139
139
|
};
|
|
140
|
+
projected.presentation = createToolPresentation({
|
|
141
|
+
...projected,
|
|
142
|
+
resultContent: entry.resultContent,
|
|
143
|
+
metadata: entry.metadata,
|
|
144
|
+
});
|
|
145
|
+
return projected;
|
|
140
146
|
}
|
|
141
147
|
if (entry.kind === 'reasoning') {
|
|
142
148
|
const body = truncate(entry.text, maxEntryChars);
|
package/conversation.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export type {
|
|
|
5
5
|
SessionSemanticsModel,
|
|
6
6
|
SupercodeUiState,
|
|
7
7
|
TaskPlanProps,
|
|
8
|
+
ToolRowProps,
|
|
8
9
|
TaskPlanModel,
|
|
9
10
|
TranscriptEntryModel,
|
|
10
11
|
TranscriptEntryProps,
|
|
@@ -17,5 +18,6 @@ export {
|
|
|
17
18
|
RequestCard,
|
|
18
19
|
SessionDetails,
|
|
19
20
|
TaskPlan,
|
|
21
|
+
ToolRow,
|
|
20
22
|
TranscriptEntry,
|
|
21
23
|
} from './index.js';
|