@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/embed.mjs
CHANGED
|
@@ -67,6 +67,11 @@ var MODES = /* @__PURE__ */ new Set(["none", "control", "mirror"]);
|
|
|
67
67
|
var STRATEGIES = /* @__PURE__ */ new Set(["start", "resume", "attach", "branch", "reduce"]);
|
|
68
68
|
var STARTUP = /* @__PURE__ */ new Set(["connecting", "starting", "discovering", "ready"]);
|
|
69
69
|
var FIDELITY = /* @__PURE__ */ new Set(["byte_lossless", "value_lossless", "semantic"]);
|
|
70
|
+
var TOOL_CATEGORIES = /* @__PURE__ */ new Set(["read", "search", "edit", "command", "test", "web", "agent", "plan", "other"]);
|
|
71
|
+
var TOOL_DETAILS = /* @__PURE__ */ new Set(["file", "matches", "diff", "terminal", "web", "agent", "plan", "fields"]);
|
|
72
|
+
var MAX_TOOL_FIELDS = 8;
|
|
73
|
+
var MAX_TOOL_FIELD_CHARS = 800;
|
|
74
|
+
var MAX_TOOL_PREVIEW_CHARS = 4e3;
|
|
70
75
|
function record(value) {
|
|
71
76
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
72
77
|
}
|
|
@@ -79,6 +84,206 @@ function number(value, fallback = 0) {
|
|
|
79
84
|
function nullableNumber(value) {
|
|
80
85
|
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
81
86
|
}
|
|
87
|
+
function boundedString(value, max) {
|
|
88
|
+
if (typeof value !== "string") return "";
|
|
89
|
+
return value.length <= max ? value : `${value.slice(0, max)}\u2026`;
|
|
90
|
+
}
|
|
91
|
+
function argumentValue(argumentsText) {
|
|
92
|
+
if (!argumentsText) return null;
|
|
93
|
+
try {
|
|
94
|
+
return JSON.parse(argumentsText);
|
|
95
|
+
} catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function firstString(source, keys) {
|
|
100
|
+
for (const key of keys) {
|
|
101
|
+
if (typeof source?.[key] === "string" && source[key].trim()) return source[key].trim();
|
|
102
|
+
}
|
|
103
|
+
return "";
|
|
104
|
+
}
|
|
105
|
+
function decodedLiteral(value) {
|
|
106
|
+
if (!value) return "";
|
|
107
|
+
if (value.startsWith('"')) {
|
|
108
|
+
try {
|
|
109
|
+
return JSON.parse(value);
|
|
110
|
+
} catch {
|
|
111
|
+
return "";
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return value.slice(1, -1).replaceAll("\\n", "\n").replaceAll("\\t", " ").replaceAll("\\r", "\r").replaceAll("\\`", "`").replaceAll("\\'", "'").replaceAll("\\\\", "\\");
|
|
115
|
+
}
|
|
116
|
+
function sourceString(source, keys) {
|
|
117
|
+
if (!source) return "";
|
|
118
|
+
const names = keys.join("|");
|
|
119
|
+
const match = new RegExp(`\\b(?:${names})\\s*:\\s*("(?:\\\\.|[^"\\\\])*"|'(?:\\\\.|[^'\\\\])*'|\`(?:\\\\.|[^\`\\\\])*\`)`).exec(source);
|
|
120
|
+
return decodedLiteral(match?.[1]);
|
|
121
|
+
}
|
|
122
|
+
function toolEnvelope(entry) {
|
|
123
|
+
const value = argumentValue(entry.arguments);
|
|
124
|
+
const source = typeof value === "string" ? value : "";
|
|
125
|
+
const tools = source ? [...new Set([...source.matchAll(/\btools\.([A-Za-z0-9_]+)/g)].map((match) => match[1]))].slice(0, 8) : [];
|
|
126
|
+
const name = tools.length === 1 ? tools[0] : entry.label ?? "tool";
|
|
127
|
+
return { args: record(value), source, tools, name };
|
|
128
|
+
}
|
|
129
|
+
function patchPath(source) {
|
|
130
|
+
return /\*\*\* (?:Update|Add|Delete) File:\s*([^\r\n]+)/.exec(source)?.[1]?.trim() ?? "";
|
|
131
|
+
}
|
|
132
|
+
function explicitNumber(sources, keys) {
|
|
133
|
+
for (const source of sources) {
|
|
134
|
+
for (const key of keys) {
|
|
135
|
+
const value = source?.[key];
|
|
136
|
+
const parsed = typeof value === "string" && value.trim() !== "" ? Number(value) : value;
|
|
137
|
+
if (typeof parsed === "number" && Number.isFinite(parsed)) return parsed;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
function toolDetail(category) {
|
|
143
|
+
return { read: "file", search: "matches", edit: "diff", command: "terminal", test: "terminal", web: "web", agent: "agent", plan: "plan" }[category] ?? "fields";
|
|
144
|
+
}
|
|
145
|
+
function usefulToolFields(args) {
|
|
146
|
+
if (!args) return [];
|
|
147
|
+
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"]);
|
|
148
|
+
return Object.entries(args).flatMap(([key, value]) => {
|
|
149
|
+
if (hidden.has(key) || value === null || value === void 0) return [];
|
|
150
|
+
const rendered = typeof value === "string" ? value : JSON.stringify(value);
|
|
151
|
+
if (!rendered) return [];
|
|
152
|
+
return [{ label: key.replaceAll(/[_-]+/g, " ").replace(/^./, (letter) => letter.toLocaleUpperCase()), value: boundedString(rendered, MAX_TOOL_FIELD_CHARS) }];
|
|
153
|
+
}).slice(0, MAX_TOOL_FIELDS);
|
|
154
|
+
}
|
|
155
|
+
function planItems(args) {
|
|
156
|
+
const source = Array.isArray(args?.plan) ? args.plan : Array.isArray(args?.todos) ? args.todos : [];
|
|
157
|
+
return source.flatMap((item) => {
|
|
158
|
+
if (typeof item === "string" && item.trim()) return [{ label: boundedString(item.trim(), 300), status: "" }];
|
|
159
|
+
const value = record(item);
|
|
160
|
+
const label = firstString(value, ["step", "title", "content", "text"]);
|
|
161
|
+
if (!label) return [];
|
|
162
|
+
return [{ label: boundedString(label, 300), status: firstString(value, ["status"]) }];
|
|
163
|
+
}).slice(0, 12);
|
|
164
|
+
}
|
|
165
|
+
function editPreview(args, resultText, source) {
|
|
166
|
+
const direct = firstString(args, ["patch", "diff"]);
|
|
167
|
+
if (direct) return direct;
|
|
168
|
+
const oldText = firstString(args, ["old_string"]);
|
|
169
|
+
const newText = firstString(args, ["new_string"]);
|
|
170
|
+
if (oldText || newText) {
|
|
171
|
+
return [
|
|
172
|
+
...oldText.split("\n").map((line) => `- ${line}`),
|
|
173
|
+
...newText.split("\n").map((line) => `+ ${line}`)
|
|
174
|
+
].join("\n");
|
|
175
|
+
}
|
|
176
|
+
const patch = /\*\*\* Begin Patch[\s\S]*?\*\*\* End Patch/.exec(source ?? "")?.[0];
|
|
177
|
+
if (patch) return patch;
|
|
178
|
+
return /^(?:diff --git|@@ |--- |\+\+\+ )/m.test(resultText ?? "") ? resultText : "";
|
|
179
|
+
}
|
|
180
|
+
function classifyTool(name, command) {
|
|
181
|
+
const normalized = name.toLocaleLowerCase();
|
|
182
|
+
if (/write_stdin|^wait$/.test(normalized)) return "command";
|
|
183
|
+
if (/update.?plan|todo|checklist|taskcreate|taskupdate/.test(normalized)) return "plan";
|
|
184
|
+
if (/search.?replace|edit|write|patch|replace|create_file|apply_patch/.test(normalized)) return "edit";
|
|
185
|
+
if (/read|view|open_file|list_dir/.test(normalized)) return "read";
|
|
186
|
+
if (/search|find|grep|glob|toolsearch/.test(normalized)) return "search";
|
|
187
|
+
if (/browser|web|fetch|url/.test(normalized)) return "web";
|
|
188
|
+
if (/agent|subagent|sendmessage|delegate|^task$/.test(normalized)) return "agent";
|
|
189
|
+
if (/test|typecheck|lint|build/.test(normalized)) return "test";
|
|
190
|
+
if (/terminal|bash|shell|command|exec|write_stdin|^wait$/.test(normalized)) return /\b(test|typecheck|lint|build)\b/i.test(command) ? "test" : "command";
|
|
191
|
+
return "other";
|
|
192
|
+
}
|
|
193
|
+
function toolAction(status, category, name, tools) {
|
|
194
|
+
const position = status === "pending" ? 0 : status === "error" ? 2 : 1;
|
|
195
|
+
if (tools.length > 1) return [`Running ${tools.length} actions`, `Ran ${tools.length} actions`, `${tools.length} actions failed`][position];
|
|
196
|
+
const normalized = name.toLocaleLowerCase();
|
|
197
|
+
if (/sendmessage/.test(normalized)) return ["Messaging agent", "Messaged agent", "Agent message failed"][position];
|
|
198
|
+
if (/kill_command_or_subagent/.test(normalized)) return ["Stopping", "Stopped", "Stop failed"][position];
|
|
199
|
+
if (/get_command_or_subagent_output|write_stdin|^wait$/.test(normalized)) return ["Waiting for", "Checked", "Check failed"][position];
|
|
200
|
+
const actions = {
|
|
201
|
+
read: ["Reading", "Read", "Read failed"],
|
|
202
|
+
search: ["Searching", "Searched", "Search failed"],
|
|
203
|
+
edit: ["Editing", "Edited", "Edit failed"],
|
|
204
|
+
command: ["Running command", "Ran command", "Command failed"],
|
|
205
|
+
test: ["Running tests", "Ran tests", "Tests failed"],
|
|
206
|
+
web: ["Browsing", "Browsed", "Browser action failed"],
|
|
207
|
+
agent: ["Starting agent", "Started agent", "Agent failed"],
|
|
208
|
+
plan: ["Updating plan", "Updated plan", "Plan update failed"]
|
|
209
|
+
};
|
|
210
|
+
return actions[category]?.[position] ?? (status === "error" ? `${name} failed` : name.replaceAll(/[_-]+/g, " "));
|
|
211
|
+
}
|
|
212
|
+
function createToolPresentation(entry) {
|
|
213
|
+
const envelope = toolEnvelope(entry);
|
|
214
|
+
const args = envelope.args;
|
|
215
|
+
const command = firstString(args, ["command", "cmd"]) || sourceString(envelope.source, ["command", "cmd"]);
|
|
216
|
+
const category = classifyTool(envelope.name, command || envelope.source || entry.arguments || "");
|
|
217
|
+
const path = firstString(args, ["file_path", "target_file", "target_directory", "path"]) || sourceString(envelope.source, ["file_path", "target_file", "target_directory", "path"]) || patchPath(envelope.source);
|
|
218
|
+
const query = firstString(args, ["query", "pattern"]) || sourceString(envelope.source, ["query", "pattern", "q"]);
|
|
219
|
+
const url = firstString(args, ["url"]) || sourceString(envelope.source, ["url", "ref_id"]);
|
|
220
|
+
const subject = firstString(args, ["subject", "description", "summary", "task", "prompt"]) || sourceString(envelope.source, ["subject", "description", "summary", "task", "prompt"]);
|
|
221
|
+
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" : "";
|
|
222
|
+
const items = planItems(args);
|
|
223
|
+
const taskId = firstString(args, ["taskId", "task_id"]);
|
|
224
|
+
const planTarget = category === "plan" ? items.length ? `${items.length} ${items.length === 1 ? "item" : "items"}` : taskId ? `task ${taskId}` : "" : "";
|
|
225
|
+
const target = path || command || query || url || subject || background || planTarget || (envelope.tools.length > 1 ? `${envelope.tools.length} coordinated actions` : toolTarget(entry.arguments));
|
|
226
|
+
const previewSource = category === "edit" ? editPreview(args, entry.resultText, envelope.source) : entry.resultText ?? "";
|
|
227
|
+
const result = record(entry.resultContent);
|
|
228
|
+
const metadata = record(entry.metadata);
|
|
229
|
+
const resultMetadata = record(result?.metadata);
|
|
230
|
+
return {
|
|
231
|
+
name: boundedString(envelope.name, 120),
|
|
232
|
+
action: boundedString(toolAction(entry.status ?? "completed", category, envelope.name, envelope.tools), 120),
|
|
233
|
+
category,
|
|
234
|
+
detail: toolDetail(category),
|
|
235
|
+
target: boundedString(target, 300),
|
|
236
|
+
command: boundedString(command, MAX_TOOL_FIELD_CHARS),
|
|
237
|
+
path: boundedString(path, MAX_TOOL_FIELD_CHARS),
|
|
238
|
+
query: boundedString(query, MAX_TOOL_FIELD_CHARS),
|
|
239
|
+
url: boundedString(url, MAX_TOOL_FIELD_CHARS),
|
|
240
|
+
subject: boundedString(subject, MAX_TOOL_FIELD_CHARS),
|
|
241
|
+
preview: boundedString(previewSource, MAX_TOOL_PREVIEW_CHARS),
|
|
242
|
+
fields: usefulToolFields(args),
|
|
243
|
+
items,
|
|
244
|
+
tools: envelope.tools,
|
|
245
|
+
exitCode: explicitNumber([result, resultMetadata, metadata], ["exit_code", "exitCode", "pi_bash_exit_code"]),
|
|
246
|
+
durationMs: explicitNumber([result, resultMetadata, metadata], ["duration_ms", "durationMs", "elapsed_ms", "elapsedMs", "totalDurationMs"]),
|
|
247
|
+
additions: explicitNumber([result, resultMetadata, metadata], ["additions", "lines_added"]),
|
|
248
|
+
deletions: explicitNumber([result, resultMetadata, metadata], ["deletions", "lines_removed"]),
|
|
249
|
+
matches: explicitNumber([result, resultMetadata, metadata], ["matches", "match_count", "result_count"])
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function readToolPresentation(value, entry) {
|
|
253
|
+
const generated = createToolPresentation(entry);
|
|
254
|
+
const item = record(value);
|
|
255
|
+
if (!item) return generated;
|
|
256
|
+
const fields = Array.isArray(item.fields) ? item.fields.flatMap((raw) => {
|
|
257
|
+
const field = record(raw);
|
|
258
|
+
return field && typeof field.label === "string" && typeof field.value === "string" ? [{ label: boundedString(field.label, 80), value: boundedString(field.value, MAX_TOOL_FIELD_CHARS) }] : [];
|
|
259
|
+
}).slice(0, MAX_TOOL_FIELDS) : generated.fields;
|
|
260
|
+
const items = Array.isArray(item.items) ? item.items.flatMap((raw) => {
|
|
261
|
+
const planItem = record(raw);
|
|
262
|
+
return planItem && typeof planItem.label === "string" ? [{ label: boundedString(planItem.label, 300), status: boundedString(planItem.status, 40) }] : [];
|
|
263
|
+
}).slice(0, 12) : generated.items;
|
|
264
|
+
const tools = Array.isArray(item.tools) ? item.tools.filter((tool) => typeof tool === "string").map((tool) => boundedString(tool, 120)).slice(0, 8) : generated.tools;
|
|
265
|
+
return {
|
|
266
|
+
name: boundedString(item.name, 120) || generated.name,
|
|
267
|
+
action: boundedString(item.action, 120) || generated.action,
|
|
268
|
+
category: TOOL_CATEGORIES.has(item.category) ? item.category : generated.category,
|
|
269
|
+
detail: TOOL_DETAILS.has(item.detail) ? item.detail : generated.detail,
|
|
270
|
+
target: boundedString(item.target, 300) || generated.target,
|
|
271
|
+
command: boundedString(item.command, MAX_TOOL_FIELD_CHARS) || generated.command,
|
|
272
|
+
path: boundedString(item.path, MAX_TOOL_FIELD_CHARS) || generated.path,
|
|
273
|
+
query: boundedString(item.query, MAX_TOOL_FIELD_CHARS) || generated.query,
|
|
274
|
+
url: boundedString(item.url, MAX_TOOL_FIELD_CHARS) || generated.url,
|
|
275
|
+
subject: boundedString(item.subject, MAX_TOOL_FIELD_CHARS) || generated.subject,
|
|
276
|
+
preview: boundedString(item.preview, MAX_TOOL_PREVIEW_CHARS) || generated.preview,
|
|
277
|
+
fields,
|
|
278
|
+
items,
|
|
279
|
+
tools,
|
|
280
|
+
exitCode: nullableNumber(item.exitCode) ?? generated.exitCode,
|
|
281
|
+
durationMs: nullableNumber(item.durationMs) ?? generated.durationMs,
|
|
282
|
+
additions: nullableNumber(item.additions) ?? generated.additions,
|
|
283
|
+
deletions: nullableNumber(item.deletions) ?? generated.deletions,
|
|
284
|
+
matches: nullableNumber(item.matches) ?? generated.matches
|
|
285
|
+
};
|
|
286
|
+
}
|
|
82
287
|
function readTranscript(value) {
|
|
83
288
|
if (!Array.isArray(value)) return [];
|
|
84
289
|
const result = [];
|
|
@@ -96,6 +301,7 @@ function readTranscript(value) {
|
|
|
96
301
|
if (typeof item[key] === "string") entry[key] = item[key];
|
|
97
302
|
}
|
|
98
303
|
if (["pending", "completed", "error"].includes(item.status)) entry.status = item.status;
|
|
304
|
+
if (item.role === "tool") entry.presentation = readToolPresentation(item.presentation, entry);
|
|
99
305
|
if (typeof item.streaming === "boolean") entry.streaming = item.streaming;
|
|
100
306
|
if (Array.isArray(item.context)) {
|
|
101
307
|
entry.context = item.context.flatMap((raw) => {
|
|
@@ -303,15 +509,10 @@ function groupConversation(entries) {
|
|
|
303
509
|
return blocks;
|
|
304
510
|
}
|
|
305
511
|
function toolCategory(entry) {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
if (/test|typecheck|lint|build/.test(name)) return "test";
|
|
311
|
-
if (/terminal|bash|shell|command|exec/.test(name)) return /\b(test|typecheck|lint|build)\b/i.test(entry.arguments ?? "") ? "test" : "command";
|
|
312
|
-
if (/browser|web|fetch|url/.test(name)) return "web";
|
|
313
|
-
if (/subagent|spawn|task/.test(name)) return "agent";
|
|
314
|
-
return "other";
|
|
512
|
+
if (TOOL_CATEGORIES.has(entry.presentation?.category)) return entry.presentation.category;
|
|
513
|
+
const envelope = toolEnvelope(entry);
|
|
514
|
+
const command = firstString(envelope.args, ["command", "cmd"]) || sourceString(envelope.source, ["command", "cmd"]);
|
|
515
|
+
return classifyTool(envelope.name, command || envelope.source || entry.arguments || "");
|
|
315
516
|
}
|
|
316
517
|
function toolTarget(argumentsText) {
|
|
317
518
|
if (!argumentsText) return "";
|
|
@@ -337,7 +538,8 @@ function activitySummary(entries) {
|
|
|
337
538
|
if (pending) return `${entries.length} actions in progress`;
|
|
338
539
|
if (failed) return `${entries.length} actions \xB7 ${failed} failed`;
|
|
339
540
|
if ([...counts.keys()].every((key) => key === "read" || key === "search")) return `Explored ${entries.length} ${entries.length === 1 ? "item" : "items"}`;
|
|
340
|
-
|
|
541
|
+
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"] };
|
|
542
|
+
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 ");
|
|
341
543
|
}
|
|
342
544
|
function canContinueHere(state) {
|
|
343
545
|
if (state.mode !== "mirror" || state.canSend) return false;
|
|
@@ -550,6 +752,7 @@ var TOOL_ICONS = {
|
|
|
550
752
|
/* @__PURE__ */ jsx3("circle", { cx: "9", cy: "6", r: "2.5" }),
|
|
551
753
|
/* @__PURE__ */ jsx3("path", { d: "M4 15c.4-3 2-4.5 5-4.5s4.6 1.5 5 4.5" })
|
|
552
754
|
] }),
|
|
755
|
+
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" }) }),
|
|
553
756
|
other: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
554
757
|
/* @__PURE__ */ jsx3("path", { d: "M9 2.5v3M9 12.5v3M2.5 9h3M12.5 9h3" }),
|
|
555
758
|
/* @__PURE__ */ jsx3("circle", { cx: "9", cy: "9", r: "3.5" })
|
|
@@ -559,7 +762,8 @@ function ToolIcon({ category }) {
|
|
|
559
762
|
const Glyph = TOOL_ICONS[category] ?? TOOL_ICONS.other;
|
|
560
763
|
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, {}) });
|
|
561
764
|
}
|
|
562
|
-
function
|
|
765
|
+
function toolAction2(entry, category, presentation) {
|
|
766
|
+
if (presentation?.action) return presentation.action;
|
|
563
767
|
const status = entry.status ?? "completed";
|
|
564
768
|
const actions = {
|
|
565
769
|
read: ["Reading", "Read", "Read failed"],
|
|
@@ -568,7 +772,8 @@ function toolAction(entry, category) {
|
|
|
568
772
|
command: ["Running command", "Ran command", "Command failed"],
|
|
569
773
|
test: ["Running tests", "Ran tests", "Tests failed"],
|
|
570
774
|
web: ["Browsing", "Browsed", "Browser action failed"],
|
|
571
|
-
agent: ["Starting agent", "Started agent", "Agent failed"]
|
|
775
|
+
agent: ["Starting agent", "Started agent", "Agent failed"],
|
|
776
|
+
plan: ["Updating plan", "Updated plan", "Plan update failed"]
|
|
572
777
|
};
|
|
573
778
|
const position = status === "pending" ? 0 : status === "error" ? 2 : 1;
|
|
574
779
|
if (actions[category]) return actions[category][position];
|
|
@@ -594,6 +799,138 @@ function argumentRows(argumentsText) {
|
|
|
594
799
|
}
|
|
595
800
|
return [{ key: "arguments", label: "Details", value: argumentsText }];
|
|
596
801
|
}
|
|
802
|
+
function stripAnsi(value) {
|
|
803
|
+
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, "") ?? "";
|
|
804
|
+
}
|
|
805
|
+
function formatDuration(value) {
|
|
806
|
+
if (value === null) return "";
|
|
807
|
+
if (value < 1e3) return `${Math.round(value)}ms`;
|
|
808
|
+
return `${(value / 1e3).toFixed(value < 1e4 ? 1 : 0)}s`;
|
|
809
|
+
}
|
|
810
|
+
function ToolMetrics({ presentation }) {
|
|
811
|
+
const metrics = [
|
|
812
|
+
presentation.matches === null ? "" : `${presentation.matches} ${presentation.matches === 1 ? "match" : "matches"}`,
|
|
813
|
+
presentation.additions === null ? "" : `+${presentation.additions}`,
|
|
814
|
+
presentation.deletions === null ? "" : `\u2212${presentation.deletions}`,
|
|
815
|
+
presentation.exitCode === null ? "" : `exit ${presentation.exitCode}`,
|
|
816
|
+
formatDuration(presentation.durationMs)
|
|
817
|
+
].filter(Boolean);
|
|
818
|
+
return metrics.length ? /* @__PURE__ */ jsx3("div", { class: "scui-tool-metrics", children: metrics.map((metric) => /* @__PURE__ */ jsx3("span", { children: metric }, metric)) }) : null;
|
|
819
|
+
}
|
|
820
|
+
function PendingElapsed({ now }) {
|
|
821
|
+
const clock = now ?? Date.now;
|
|
822
|
+
const started = useRef2(clock());
|
|
823
|
+
const [elapsed, setElapsed] = useState2(0);
|
|
824
|
+
useEffect2(() => {
|
|
825
|
+
const timer = setInterval(() => setElapsed(Math.max(0, clock() - started.current)), 1e3);
|
|
826
|
+
return () => clearInterval(timer);
|
|
827
|
+
}, [clock]);
|
|
828
|
+
return /* @__PURE__ */ jsx3("small", { class: "scui-tool-elapsed", children: elapsed < 1e3 ? "now" : formatDuration(elapsed) });
|
|
829
|
+
}
|
|
830
|
+
function LinePreview({ value, kind }) {
|
|
831
|
+
if (!value) return null;
|
|
832
|
+
return /* @__PURE__ */ jsx3("ol", { class: "scui-code-preview", "data-kind": kind, children: stripAnsi(value).split("\n").map((line, index) => {
|
|
833
|
+
const tone = kind === "diff" ? line.startsWith("+") && !line.startsWith("+++") ? "add" : line.startsWith("-") && !line.startsWith("---") ? "remove" : line.startsWith("@@") ? "hunk" : "plain" : "plain";
|
|
834
|
+
return /* @__PURE__ */ jsxs2("li", { "data-tone": tone, children: [
|
|
835
|
+
/* @__PURE__ */ jsx3("span", { children: index + 1 }),
|
|
836
|
+
/* @__PURE__ */ jsx3("code", { children: line || " " })
|
|
837
|
+
] }, index);
|
|
838
|
+
}) });
|
|
839
|
+
}
|
|
840
|
+
function TerminalPreview({ presentation, pending, failed }) {
|
|
841
|
+
return /* @__PURE__ */ jsxs2("section", { class: "scui-terminal", children: [
|
|
842
|
+
/* @__PURE__ */ jsxs2("header", { children: [
|
|
843
|
+
/* @__PURE__ */ jsxs2("span", { "aria-hidden": "true", children: [
|
|
844
|
+
/* @__PURE__ */ jsx3("i", {}),
|
|
845
|
+
/* @__PURE__ */ jsx3("i", {}),
|
|
846
|
+
/* @__PURE__ */ jsx3("i", {})
|
|
847
|
+
] }),
|
|
848
|
+
/* @__PURE__ */ jsx3("code", { children: presentation.command ? `$ ${presentation.command}` : "Terminal" })
|
|
849
|
+
] }),
|
|
850
|
+
presentation.preview ? /* @__PURE__ */ jsx3("pre", { "data-error": failed, children: stripAnsi(presentation.preview) }) : pending ? /* @__PURE__ */ jsxs2("div", { class: "scui-terminal-wait", children: [
|
|
851
|
+
/* @__PURE__ */ jsx3("i", {}),
|
|
852
|
+
" Waiting for output"
|
|
853
|
+
] }) : /* @__PURE__ */ jsx3("div", { class: "scui-terminal-empty", children: "No output" })
|
|
854
|
+
] });
|
|
855
|
+
}
|
|
856
|
+
function SearchPreview({ presentation }) {
|
|
857
|
+
const lines = stripAnsi(presentation.preview).split("\n").filter(Boolean);
|
|
858
|
+
return /* @__PURE__ */ jsxs2("section", { class: "scui-search-preview", children: [
|
|
859
|
+
presentation.query ? /* @__PURE__ */ jsxs2("header", { children: [
|
|
860
|
+
/* @__PURE__ */ jsx3("span", { children: "Search" }),
|
|
861
|
+
/* @__PURE__ */ jsx3("code", { children: presentation.query })
|
|
862
|
+
] }) : null,
|
|
863
|
+
lines.length ? /* @__PURE__ */ jsx3("ol", { children: lines.map((line, index) => {
|
|
864
|
+
const match = /^(.*?):(\d+)(?::(\d+))?:(.*)$/.exec(line);
|
|
865
|
+
return /* @__PURE__ */ jsx3("li", { children: match ? /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
866
|
+
/* @__PURE__ */ jsx3("code", { children: match[1] }),
|
|
867
|
+
/* @__PURE__ */ jsxs2("small", { children: [
|
|
868
|
+
match[2],
|
|
869
|
+
match[3] ? `:${match[3]}` : ""
|
|
870
|
+
] }),
|
|
871
|
+
/* @__PURE__ */ jsx3("span", { children: match[4] })
|
|
872
|
+
] }) : /* @__PURE__ */ jsx3("span", { children: line }) }, index);
|
|
873
|
+
}) }) : /* @__PURE__ */ jsx3("p", { children: "No textual results" })
|
|
874
|
+
] });
|
|
875
|
+
}
|
|
876
|
+
function ToolPreview({ presentation, entry }) {
|
|
877
|
+
const pending = entry.status === "pending";
|
|
878
|
+
const failed = entry.status === "error";
|
|
879
|
+
if (presentation.detail === "terminal") return /* @__PURE__ */ jsx3(TerminalPreview, { presentation, pending, failed });
|
|
880
|
+
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" });
|
|
881
|
+
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" });
|
|
882
|
+
if (presentation.detail === "matches") return /* @__PURE__ */ jsx3(SearchPreview, { presentation });
|
|
883
|
+
if (presentation.detail === "web") return /* @__PURE__ */ jsxs2("section", { class: "scui-web-preview", children: [
|
|
884
|
+
presentation.url ? /* @__PURE__ */ jsx3("code", { children: presentation.url }) : null,
|
|
885
|
+
presentation.preview ? /* @__PURE__ */ jsx3("p", { children: stripAnsi(presentation.preview) }) : /* @__PURE__ */ jsx3("small", { children: pending ? "Waiting for the page" : "No page summary returned" })
|
|
886
|
+
] });
|
|
887
|
+
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" }) });
|
|
888
|
+
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: [
|
|
889
|
+
/* @__PURE__ */ jsx3("i", { "aria-hidden": "true" }),
|
|
890
|
+
/* @__PURE__ */ jsx3("span", { children: item.label })
|
|
891
|
+
] }, `${item.label}:${index}`)) });
|
|
892
|
+
return presentation.preview ? /* @__PURE__ */ jsx3("pre", { class: "scui-tool-output", "data-error": failed, children: stripAnsi(presentation.preview) }) : null;
|
|
893
|
+
}
|
|
894
|
+
function ToolActions({ presentation, adapter }) {
|
|
895
|
+
const [copied, setCopied] = useState2(false);
|
|
896
|
+
const reset = useRef2(null);
|
|
897
|
+
useEffect2(() => () => clearTimeout(reset.current), []);
|
|
898
|
+
if (!adapter?.copyText) return null;
|
|
899
|
+
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;
|
|
900
|
+
const copy = async () => {
|
|
901
|
+
await adapter.copyText(action[1]);
|
|
902
|
+
setCopied(true);
|
|
903
|
+
clearTimeout(reset.current);
|
|
904
|
+
reset.current = setTimeout(() => setCopied(false), 1500);
|
|
905
|
+
};
|
|
906
|
+
return action ? /* @__PURE__ */ jsx3("div", { class: "scui-tool-actions", children: /* @__PURE__ */ jsx3("button", { type: "button", onClick: copy, children: copied ? "Copied" : action[0] }) }) : null;
|
|
907
|
+
}
|
|
908
|
+
function ToolStack({ tools }) {
|
|
909
|
+
if (tools.length < 2) return null;
|
|
910
|
+
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)) });
|
|
911
|
+
}
|
|
912
|
+
function TechnicalDetails({ entry }) {
|
|
913
|
+
if (!entry.arguments && !entry.resultText) return null;
|
|
914
|
+
return /* @__PURE__ */ jsxs2("details", { class: "scui-tool-technical", children: [
|
|
915
|
+
/* @__PURE__ */ jsx3("summary", { children: "Technical details" }),
|
|
916
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
917
|
+
entry.arguments ? /* @__PURE__ */ jsxs2("section", { children: [
|
|
918
|
+
/* @__PURE__ */ jsx3("strong", { children: "Native arguments" }),
|
|
919
|
+
/* @__PURE__ */ jsx3("dl", { children: argumentRows(entry.arguments).map((row) => /* @__PURE__ */ jsxs2("div", { children: [
|
|
920
|
+
/* @__PURE__ */ jsx3("dt", { children: row.label }),
|
|
921
|
+
/* @__PURE__ */ jsx3("dd", { children: /* @__PURE__ */ jsx3("pre", { children: row.value }) })
|
|
922
|
+
] }, row.key)) })
|
|
923
|
+
] }) : null,
|
|
924
|
+
entry.resultText ? /* @__PURE__ */ jsxs2("section", { children: [
|
|
925
|
+
/* @__PURE__ */ jsx3("strong", { children: "Native result" }),
|
|
926
|
+
/* @__PURE__ */ jsxs2("pre", { "data-error": entry.status === "error", children: [
|
|
927
|
+
entry.resultText,
|
|
928
|
+
entry.truncated ? "\n[truncated]" : ""
|
|
929
|
+
] })
|
|
930
|
+
] }) : null
|
|
931
|
+
] })
|
|
932
|
+
] });
|
|
933
|
+
}
|
|
597
934
|
function TranscriptEntry({ entry, state, adapter }) {
|
|
598
935
|
if (entry.role === "request") return /* @__PURE__ */ jsx3(RequestCard, { entry, adapter, canRespond: state.canRespond });
|
|
599
936
|
if (entry.role === "reasoning") {
|
|
@@ -609,38 +946,42 @@ function TranscriptEntry({ entry, state, adapter }) {
|
|
|
609
946
|
entry.truncated ? /* @__PURE__ */ jsx3("small", { class: "scui-truncated", children: "Entry truncated by host \xB7 load native session for the complete value" }) : null
|
|
610
947
|
] });
|
|
611
948
|
}
|
|
612
|
-
function ToolRow({ entry, workspace }) {
|
|
613
|
-
const
|
|
614
|
-
const
|
|
615
|
-
|
|
949
|
+
function ToolRow({ entry, workspace, open = false, adapter }) {
|
|
950
|
+
const presentation = entry.presentation ?? createToolPresentation(entry);
|
|
951
|
+
const [expanded, setExpanded] = useState2(open || entry.status === "pending");
|
|
952
|
+
useEffect2(() => {
|
|
953
|
+
if (entry.status === "pending") setExpanded(true);
|
|
954
|
+
}, [entry.status]);
|
|
955
|
+
const target = compactToolTarget(presentation.target, workspace);
|
|
956
|
+
const hasDetail = Boolean(entry.arguments || entry.resultText || presentation.preview || presentation.fields.length);
|
|
957
|
+
const category = presentation.category ?? toolCategory(entry);
|
|
616
958
|
const summary = /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
617
959
|
/* @__PURE__ */ jsx3(ToolIcon, { category }),
|
|
618
|
-
/* @__PURE__ */ jsx3("strong", { children:
|
|
619
|
-
target ? /* @__PURE__ */ jsx3("code", { class: "scui-tool-target", title:
|
|
960
|
+
/* @__PURE__ */ jsx3("strong", { children: toolAction2(entry, category, presentation) }),
|
|
961
|
+
target ? /* @__PURE__ */ jsx3("code", { class: "scui-tool-target", title: presentation.target, children: target }) : null,
|
|
620
962
|
/* @__PURE__ */ jsx3("span", { class: "scui-spacer" }),
|
|
963
|
+
entry.status === "pending" ? /* @__PURE__ */ jsx3(PendingElapsed, { now: adapter?.now }) : null,
|
|
621
964
|
/* @__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" }),
|
|
622
965
|
hasDetail ? /* @__PURE__ */ jsx3("span", { class: "scui-tool-chevron", "aria-hidden": "true", children: "\u203A" }) : null
|
|
623
966
|
] });
|
|
624
967
|
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 }) });
|
|
625
|
-
return /* @__PURE__ */ jsxs2("details", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: [
|
|
968
|
+
return /* @__PURE__ */ jsxs2("details", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, open: expanded, onToggle: (event) => setExpanded(event.currentTarget.open), children: [
|
|
626
969
|
/* @__PURE__ */ jsx3("summary", { class: "scui-tool-head", children: summary }),
|
|
627
970
|
/* @__PURE__ */ jsxs2("div", { class: "scui-tool-detail", children: [
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
/* @__PURE__ */ jsx3("
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
] })
|
|
638
|
-
] }) : null
|
|
971
|
+
/* @__PURE__ */ jsx3(ToolMetrics, { presentation }),
|
|
972
|
+
/* @__PURE__ */ jsx3(ToolStack, { tools: presentation.tools ?? [] }),
|
|
973
|
+
/* @__PURE__ */ jsx3(ToolPreview, { presentation, entry }),
|
|
974
|
+
presentation.fields.length ? /* @__PURE__ */ jsx3("dl", { class: "scui-tool-fields", children: presentation.fields.map((field) => /* @__PURE__ */ jsxs2("div", { children: [
|
|
975
|
+
/* @__PURE__ */ jsx3("dt", { children: field.label }),
|
|
976
|
+
/* @__PURE__ */ jsx3("dd", { children: field.value })
|
|
977
|
+
] }, field.label)) }) : null,
|
|
978
|
+
/* @__PURE__ */ jsx3(ToolActions, { presentation, adapter }),
|
|
979
|
+
/* @__PURE__ */ jsx3(TechnicalDetails, { entry })
|
|
639
980
|
] })
|
|
640
981
|
] });
|
|
641
982
|
}
|
|
642
|
-
function ActivityGroup({ entries, state }) {
|
|
643
|
-
if (entries.length === 1) return /* @__PURE__ */ jsx3("section", { class: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx3(ToolRow, { entry: entries[0], workspace: state.workspace }) });
|
|
983
|
+
function ActivityGroup({ entries, state, adapter }) {
|
|
984
|
+
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 }) });
|
|
644
985
|
const active = entries.some((entry) => entry.status === "pending");
|
|
645
986
|
const [open, setOpen] = useState2(active);
|
|
646
987
|
const id = useId();
|
|
@@ -658,7 +999,7 @@ function ActivityGroup({ entries, state }) {
|
|
|
658
999
|
entries.length
|
|
659
1000
|
] })
|
|
660
1001
|
] }),
|
|
661
|
-
open ? /* @__PURE__ */ jsx3("div", { id, children: entries.map((entry) => /* @__PURE__ */ jsx3(ToolRow, { entry, workspace: state.workspace }, entry.id)) }) : null
|
|
1002
|
+
open ? /* @__PURE__ */ jsx3("div", { id, children: entries.map((entry) => /* @__PURE__ */ jsx3(ToolRow, { entry, workspace: state.workspace, adapter }, entry.id)) }) : null
|
|
662
1003
|
] });
|
|
663
1004
|
}
|
|
664
1005
|
function TaskPlan({ plan }) {
|
package/index.d.ts
CHANGED
|
@@ -74,6 +74,31 @@ export interface TranscriptEntryModel {
|
|
|
74
74
|
request?: TranscriptRequest;
|
|
75
75
|
code?: string;
|
|
76
76
|
context?: TranscriptContext[];
|
|
77
|
+
presentation?: ToolPresentationModel;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type ToolCategory = 'read' | 'search' | 'edit' | 'command' | 'test' | 'web' | 'agent' | 'plan' | 'other';
|
|
81
|
+
|
|
82
|
+
export interface ToolPresentationModel {
|
|
83
|
+
name: string;
|
|
84
|
+
action: string;
|
|
85
|
+
category: ToolCategory;
|
|
86
|
+
detail: 'file' | 'matches' | 'diff' | 'terminal' | 'web' | 'agent' | 'plan' | 'fields';
|
|
87
|
+
target: string;
|
|
88
|
+
command: string;
|
|
89
|
+
path: string;
|
|
90
|
+
query: string;
|
|
91
|
+
url: string;
|
|
92
|
+
subject: string;
|
|
93
|
+
preview: string;
|
|
94
|
+
fields: Array<{ label: string; value: string }>;
|
|
95
|
+
items: Array<{ label: string; status: string }>;
|
|
96
|
+
tools: string[];
|
|
97
|
+
exitCode: number | null;
|
|
98
|
+
durationMs: number | null;
|
|
99
|
+
additions: number | null;
|
|
100
|
+
deletions: number | null;
|
|
101
|
+
matches: number | null;
|
|
77
102
|
}
|
|
78
103
|
|
|
79
104
|
export interface TaskPlanItem {
|
|
@@ -240,6 +265,13 @@ export interface ActivityGroupProps {
|
|
|
240
265
|
adapter: UiAdapter;
|
|
241
266
|
}
|
|
242
267
|
|
|
268
|
+
export interface ToolRowProps {
|
|
269
|
+
entry: TranscriptEntryModel;
|
|
270
|
+
workspace?: string;
|
|
271
|
+
open?: boolean;
|
|
272
|
+
adapter?: Pick<UiAdapter, 'copyText' | 'now'>;
|
|
273
|
+
}
|
|
274
|
+
|
|
243
275
|
export interface TaskPlanProps {
|
|
244
276
|
plan: TaskPlanModel;
|
|
245
277
|
value?: TaskPlanModel;
|
|
@@ -285,7 +317,8 @@ export function groupConversation(entries: readonly TranscriptEntryModel[]): Arr
|
|
|
285
317
|
| { kind: 'entry'; id: string; entry: TranscriptEntryModel }
|
|
286
318
|
| { kind: 'activity'; id: string; entries: TranscriptEntryModel[] }
|
|
287
319
|
>;
|
|
288
|
-
export function toolCategory(entry: Pick<TranscriptEntryModel, 'label' | 'arguments'
|
|
320
|
+
export function toolCategory(entry: Pick<TranscriptEntryModel, 'label' | 'arguments' | 'presentation'>): ToolCategory;
|
|
321
|
+
export function createToolPresentation(entry: Pick<TranscriptEntryModel, 'label' | 'arguments' | 'resultText' | 'status'> & { resultContent?: unknown; metadata?: Record<string, string> }): ToolPresentationModel;
|
|
289
322
|
export function toolTarget(argumentsText?: string): string;
|
|
290
323
|
export function compactToolTarget(target: string, workspace: string): string;
|
|
291
324
|
export function activitySummary(entries: readonly TranscriptEntryModel[]): string;
|
|
@@ -300,6 +333,7 @@ export function LoadingStatus(props: { state: SupercodeUiState; compact?: boolea
|
|
|
300
333
|
export function RequestCard(props: { entry: TranscriptEntryModel; adapter: UiAdapter; canRespond: boolean }): VNode | null;
|
|
301
334
|
export function TranscriptEntry(props: { entry: TranscriptEntryModel; state: SupercodeUiState; adapter: UiAdapter }): VNode;
|
|
302
335
|
export function ActivityGroup(props: { entries: TranscriptEntryModel[]; state: SupercodeUiState; adapter: UiAdapter }): VNode;
|
|
336
|
+
export function ToolRow(props: ToolRowProps): VNode;
|
|
303
337
|
export function TaskPlan(props: { plan: TaskPlanModel }): VNode | null;
|
|
304
338
|
export function SessionDetails(props: { semantics: SessionSemanticsModel }): VNode | null;
|
|
305
339
|
export function Conversation(props: { state: SupercodeUiState; adapter: UiAdapter; components?: MessengerComponents; slots?: MessengerSlots; memoryKey?: string; pending?: string | null }): VNode;
|