@volter-ai-dev/supercode-ui 0.1.9 → 0.1.11
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.mjs +142 -41
- package/conversation.mjs +137 -40
- package/core.mjs +112 -30
- package/embed.mjs +142 -41
- package/index.d.ts +5 -1
- package/messenger.mjs +142 -41
- package/package.json +1 -1
- package/styles.css +3 -0
package/conversation.mjs
CHANGED
|
@@ -70,10 +70,10 @@ function boundedString(value, max) {
|
|
|
70
70
|
if (typeof value !== "string") return "";
|
|
71
71
|
return value.length <= max ? value : `${value.slice(0, max)}\u2026`;
|
|
72
72
|
}
|
|
73
|
-
function
|
|
73
|
+
function argumentValue(argumentsText) {
|
|
74
74
|
if (!argumentsText) return null;
|
|
75
75
|
try {
|
|
76
|
-
return
|
|
76
|
+
return JSON.parse(argumentsText);
|
|
77
77
|
} catch {
|
|
78
78
|
return null;
|
|
79
79
|
}
|
|
@@ -84,6 +84,33 @@ function firstString(source, keys) {
|
|
|
84
84
|
}
|
|
85
85
|
return "";
|
|
86
86
|
}
|
|
87
|
+
function decodedLiteral(value) {
|
|
88
|
+
if (!value) return "";
|
|
89
|
+
if (value.startsWith('"')) {
|
|
90
|
+
try {
|
|
91
|
+
return JSON.parse(value);
|
|
92
|
+
} catch {
|
|
93
|
+
return "";
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return value.slice(1, -1).replaceAll("\\n", "\n").replaceAll("\\t", " ").replaceAll("\\r", "\r").replaceAll("\\`", "`").replaceAll("\\'", "'").replaceAll("\\\\", "\\");
|
|
97
|
+
}
|
|
98
|
+
function sourceString(source, keys) {
|
|
99
|
+
if (!source) return "";
|
|
100
|
+
const names = keys.join("|");
|
|
101
|
+
const match = new RegExp(`\\b(?:${names})\\s*:\\s*("(?:\\\\.|[^"\\\\])*"|'(?:\\\\.|[^'\\\\])*'|\`(?:\\\\.|[^\`\\\\])*\`)`).exec(source);
|
|
102
|
+
return decodedLiteral(match?.[1]);
|
|
103
|
+
}
|
|
104
|
+
function toolEnvelope(entry) {
|
|
105
|
+
const value = argumentValue(entry.arguments);
|
|
106
|
+
const source = typeof value === "string" ? value : "";
|
|
107
|
+
const tools = source ? [...new Set([...source.matchAll(/\btools\.([A-Za-z0-9_]+)/g)].map((match) => match[1]))].slice(0, 8) : [];
|
|
108
|
+
const name = tools.length === 1 ? tools[0] : entry.label ?? "tool";
|
|
109
|
+
return { args: record(value), source, tools, name };
|
|
110
|
+
}
|
|
111
|
+
function patchPath(source) {
|
|
112
|
+
return /\*\*\* (?:Update|Add|Delete) File:\s*([^\r\n]+)/.exec(source)?.[1]?.trim() ?? "";
|
|
113
|
+
}
|
|
87
114
|
function explicitNumber(sources, keys) {
|
|
88
115
|
for (const source of sources) {
|
|
89
116
|
for (const key of keys) {
|
|
@@ -117,7 +144,7 @@ function planItems(args) {
|
|
|
117
144
|
return [{ label: boundedString(label, 300), status: firstString(value, ["status"]) }];
|
|
118
145
|
}).slice(0, 12);
|
|
119
146
|
}
|
|
120
|
-
function editPreview(args, resultText) {
|
|
147
|
+
function editPreview(args, resultText, source) {
|
|
121
148
|
const direct = firstString(args, ["patch", "diff"]);
|
|
122
149
|
if (direct) return direct;
|
|
123
150
|
const oldText = firstString(args, ["old_string"]);
|
|
@@ -128,21 +155,63 @@ function editPreview(args, resultText) {
|
|
|
128
155
|
...newText.split("\n").map((line) => `+ ${line}`)
|
|
129
156
|
].join("\n");
|
|
130
157
|
}
|
|
158
|
+
const patch = /\*\*\* Begin Patch[\s\S]*?\*\*\* End Patch/.exec(source ?? "")?.[0];
|
|
159
|
+
if (patch) return patch;
|
|
131
160
|
return /^(?:diff --git|@@ |--- |\+\+\+ )/m.test(resultText ?? "") ? resultText : "";
|
|
132
161
|
}
|
|
162
|
+
function classifyTool(name, command) {
|
|
163
|
+
const normalized = name.toLocaleLowerCase();
|
|
164
|
+
if (/write_stdin|^wait$/.test(normalized)) return "command";
|
|
165
|
+
if (/update.?plan|todo|checklist|taskcreate|taskupdate/.test(normalized)) return "plan";
|
|
166
|
+
if (/search.?replace|edit|write|patch|replace|create_file|apply_patch/.test(normalized)) return "edit";
|
|
167
|
+
if (/read|view|open_file|list_dir/.test(normalized)) return "read";
|
|
168
|
+
if (/search|find|grep|glob|toolsearch/.test(normalized)) return "search";
|
|
169
|
+
if (/browser|web|fetch|url/.test(normalized)) return "web";
|
|
170
|
+
if (/agent|subagent|sendmessage|delegate|^task$/.test(normalized)) return "agent";
|
|
171
|
+
if (/test|typecheck|lint|build/.test(normalized)) return "test";
|
|
172
|
+
if (/terminal|bash|shell|command|exec|write_stdin|^wait$/.test(normalized)) return /\b(test|typecheck|lint|build)\b/i.test(command) ? "test" : "command";
|
|
173
|
+
return "other";
|
|
174
|
+
}
|
|
175
|
+
function toolAction(status, category, name, tools) {
|
|
176
|
+
const position = status === "pending" ? 0 : status === "error" ? 2 : 1;
|
|
177
|
+
if (tools.length > 1) return [`Running ${tools.length} actions`, `Ran ${tools.length} actions`, `${tools.length} actions failed`][position];
|
|
178
|
+
const normalized = name.toLocaleLowerCase();
|
|
179
|
+
if (/sendmessage/.test(normalized)) return ["Messaging agent", "Messaged agent", "Agent message failed"][position];
|
|
180
|
+
if (/kill_command_or_subagent/.test(normalized)) return ["Stopping", "Stopped", "Stop failed"][position];
|
|
181
|
+
if (/get_command_or_subagent_output|write_stdin|^wait$/.test(normalized)) return ["Waiting for", "Checked", "Check failed"][position];
|
|
182
|
+
const actions = {
|
|
183
|
+
read: ["Reading", "Read", "Read failed"],
|
|
184
|
+
search: ["Searching", "Searched", "Search failed"],
|
|
185
|
+
edit: ["Editing", "Edited", "Edit failed"],
|
|
186
|
+
command: ["Running command", "Ran command", "Command failed"],
|
|
187
|
+
test: ["Running tests", "Ran tests", "Tests failed"],
|
|
188
|
+
web: ["Browsing", "Browsed", "Browser action failed"],
|
|
189
|
+
agent: ["Starting agent", "Started agent", "Agent failed"],
|
|
190
|
+
plan: ["Updating plan", "Updated plan", "Plan update failed"]
|
|
191
|
+
};
|
|
192
|
+
return actions[category]?.[position] ?? (status === "error" ? `${name} failed` : name.replaceAll(/[_-]+/g, " "));
|
|
193
|
+
}
|
|
133
194
|
function createToolPresentation(entry) {
|
|
134
|
-
const
|
|
135
|
-
const
|
|
136
|
-
const command = firstString(args, ["command", "cmd"]);
|
|
137
|
-
const
|
|
138
|
-
const
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
const
|
|
142
|
-
const
|
|
195
|
+
const envelope = toolEnvelope(entry);
|
|
196
|
+
const args = envelope.args;
|
|
197
|
+
const command = firstString(args, ["command", "cmd"]) || sourceString(envelope.source, ["command", "cmd"]);
|
|
198
|
+
const category = classifyTool(envelope.name, command || envelope.source || entry.arguments || "");
|
|
199
|
+
const path = firstString(args, ["file_path", "target_file", "target_directory", "path"]) || sourceString(envelope.source, ["file_path", "target_file", "target_directory", "path"]) || patchPath(envelope.source);
|
|
200
|
+
const query = firstString(args, ["query", "pattern"]) || sourceString(envelope.source, ["query", "pattern", "q"]);
|
|
201
|
+
const url = firstString(args, ["url"]) || sourceString(envelope.source, ["url", "ref_id"]);
|
|
202
|
+
const subject = firstString(args, ["subject", "description", "summary", "task", "prompt"]) || sourceString(envelope.source, ["subject", "description", "summary", "task", "prompt"]);
|
|
203
|
+
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" : "";
|
|
204
|
+
const items = planItems(args);
|
|
205
|
+
const taskId = firstString(args, ["taskId", "task_id"]);
|
|
206
|
+
const planTarget = category === "plan" ? items.length ? `${items.length} ${items.length === 1 ? "item" : "items"}` : taskId ? `task ${taskId}` : "" : "";
|
|
207
|
+
const target = path || command || query || url || subject || background || planTarget || (envelope.tools.length > 1 ? `${envelope.tools.length} coordinated actions` : toolTarget(entry.arguments));
|
|
208
|
+
const previewSource = category === "edit" ? editPreview(args, entry.resultText, envelope.source) : entry.resultText ?? "";
|
|
143
209
|
const result = record(entry.resultContent);
|
|
144
210
|
const metadata = record(entry.metadata);
|
|
211
|
+
const resultMetadata = record(result?.metadata);
|
|
145
212
|
return {
|
|
213
|
+
name: boundedString(envelope.name, 120),
|
|
214
|
+
action: boundedString(toolAction(entry.status ?? "completed", category, envelope.name, envelope.tools), 120),
|
|
146
215
|
category,
|
|
147
216
|
detail: toolDetail(category),
|
|
148
217
|
target: boundedString(target, 300),
|
|
@@ -153,12 +222,13 @@ function createToolPresentation(entry) {
|
|
|
153
222
|
subject: boundedString(subject, MAX_TOOL_FIELD_CHARS),
|
|
154
223
|
preview: boundedString(previewSource, MAX_TOOL_PREVIEW_CHARS),
|
|
155
224
|
fields: usefulToolFields(args),
|
|
156
|
-
items
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
225
|
+
items,
|
|
226
|
+
tools: envelope.tools,
|
|
227
|
+
exitCode: explicitNumber([result, resultMetadata, metadata], ["exit_code", "exitCode", "pi_bash_exit_code"]),
|
|
228
|
+
durationMs: explicitNumber([result, resultMetadata, metadata], ["duration_ms", "durationMs", "elapsed_ms", "elapsedMs", "totalDurationMs"]),
|
|
229
|
+
additions: explicitNumber([result, resultMetadata, metadata], ["additions", "lines_added"]),
|
|
230
|
+
deletions: explicitNumber([result, resultMetadata, metadata], ["deletions", "lines_removed"]),
|
|
231
|
+
matches: explicitNumber([result, resultMetadata, metadata], ["matches", "match_count", "result_count"])
|
|
162
232
|
};
|
|
163
233
|
}
|
|
164
234
|
function harnessDisplayName(id) {
|
|
@@ -179,16 +249,9 @@ function groupConversation(entries) {
|
|
|
179
249
|
}
|
|
180
250
|
function toolCategory(entry) {
|
|
181
251
|
if (TOOL_CATEGORIES.has(entry.presentation?.category)) return entry.presentation.category;
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if (/edit|write|patch|replace|create_file/.test(name)) return "edit";
|
|
186
|
-
if (/test|typecheck|lint|build/.test(name)) return "test";
|
|
187
|
-
if (/terminal|bash|shell|command|exec/.test(name)) return /\b(test|typecheck|lint|build)\b/i.test(entry.arguments ?? "") ? "test" : "command";
|
|
188
|
-
if (/browser|web|fetch|url/.test(name)) return "web";
|
|
189
|
-
if (/update.?plan|todo|checklist/.test(name)) return "plan";
|
|
190
|
-
if (/subagent|spawn.?agent|delegate|^task$/.test(name)) return "agent";
|
|
191
|
-
return "other";
|
|
252
|
+
const envelope = toolEnvelope(entry);
|
|
253
|
+
const command = firstString(envelope.args, ["command", "cmd"]) || sourceString(envelope.source, ["command", "cmd"]);
|
|
254
|
+
return classifyTool(envelope.name, command || envelope.source || entry.arguments || "");
|
|
192
255
|
}
|
|
193
256
|
function toolTarget(argumentsText) {
|
|
194
257
|
if (!argumentsText) return "";
|
|
@@ -214,7 +277,8 @@ function activitySummary(entries) {
|
|
|
214
277
|
if (pending) return `${entries.length} actions in progress`;
|
|
215
278
|
if (failed) return `${entries.length} actions \xB7 ${failed} failed`;
|
|
216
279
|
if ([...counts.keys()].every((key) => key === "read" || key === "search")) return `Explored ${entries.length} ${entries.length === 1 ? "item" : "items"}`;
|
|
217
|
-
|
|
280
|
+
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"] };
|
|
281
|
+
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 ");
|
|
218
282
|
}
|
|
219
283
|
|
|
220
284
|
// src/markdown.jsx
|
|
@@ -326,7 +390,8 @@ function ToolIcon({ category }) {
|
|
|
326
390
|
const Glyph = TOOL_ICONS[category] ?? TOOL_ICONS.other;
|
|
327
391
|
return /* @__PURE__ */ jsx2("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__ */ jsx2(Glyph, {}) });
|
|
328
392
|
}
|
|
329
|
-
function
|
|
393
|
+
function toolAction2(entry, category, presentation) {
|
|
394
|
+
if (presentation?.action) return presentation.action;
|
|
330
395
|
const status = entry.status ?? "completed";
|
|
331
396
|
const actions = {
|
|
332
397
|
read: ["Reading", "Read", "Read failed"],
|
|
@@ -380,6 +445,16 @@ function ToolMetrics({ presentation }) {
|
|
|
380
445
|
].filter(Boolean);
|
|
381
446
|
return metrics.length ? /* @__PURE__ */ jsx2("div", { class: "scui-tool-metrics", children: metrics.map((metric) => /* @__PURE__ */ jsx2("span", { children: metric }, metric)) }) : null;
|
|
382
447
|
}
|
|
448
|
+
function PendingElapsed({ now }) {
|
|
449
|
+
const clock = now ?? Date.now;
|
|
450
|
+
const started = useRef(clock());
|
|
451
|
+
const [elapsed, setElapsed] = useState(0);
|
|
452
|
+
useEffect(() => {
|
|
453
|
+
const timer = setInterval(() => setElapsed(Math.max(0, clock() - started.current)), 1e3);
|
|
454
|
+
return () => clearInterval(timer);
|
|
455
|
+
}, [clock]);
|
|
456
|
+
return /* @__PURE__ */ jsx2("small", { class: "scui-tool-elapsed", children: elapsed < 1e3 ? "now" : formatDuration(elapsed) });
|
|
457
|
+
}
|
|
383
458
|
function LinePreview({ value, kind }) {
|
|
384
459
|
if (!value) return null;
|
|
385
460
|
return /* @__PURE__ */ jsx2("ol", { class: "scui-code-preview", "data-kind": kind, children: stripAnsi(value).split("\n").map((line, index) => {
|
|
@@ -437,16 +512,31 @@ function ToolPreview({ presentation, entry }) {
|
|
|
437
512
|
presentation.url ? /* @__PURE__ */ jsx2("code", { children: presentation.url }) : null,
|
|
438
513
|
presentation.preview ? /* @__PURE__ */ jsx2("p", { children: stripAnsi(presentation.preview) }) : /* @__PURE__ */ jsx2("small", { children: pending ? "Waiting for the page" : "No page summary returned" })
|
|
439
514
|
] });
|
|
440
|
-
if (presentation.detail === "agent") return /* @__PURE__ */
|
|
441
|
-
presentation.subject ? /* @__PURE__ */ jsx2("p", { children: presentation.subject }) : null,
|
|
442
|
-
presentation.preview ? /* @__PURE__ */ jsx2("pre", { children: stripAnsi(presentation.preview) }) : /* @__PURE__ */ jsx2("small", { children: pending ? "Agent is working" : "No textual handoff returned" })
|
|
443
|
-
] });
|
|
515
|
+
if (presentation.detail === "agent") return /* @__PURE__ */ jsx2("section", { class: "scui-agent-preview", children: presentation.preview ? /* @__PURE__ */ jsx2("pre", { children: stripAnsi(presentation.preview) }) : /* @__PURE__ */ jsx2("small", { children: pending ? "Agent is working" : "No textual handoff returned" }) });
|
|
444
516
|
if (presentation.detail === "plan") return /* @__PURE__ */ jsx2("ol", { class: "scui-plan-preview", children: presentation.items?.map((item, index) => /* @__PURE__ */ jsxs("li", { "data-status": item.status, children: [
|
|
445
517
|
/* @__PURE__ */ jsx2("i", { "aria-hidden": "true" }),
|
|
446
518
|
/* @__PURE__ */ jsx2("span", { children: item.label })
|
|
447
519
|
] }, `${item.label}:${index}`)) });
|
|
448
520
|
return presentation.preview ? /* @__PURE__ */ jsx2("pre", { class: "scui-tool-output", "data-error": failed, children: stripAnsi(presentation.preview) }) : null;
|
|
449
521
|
}
|
|
522
|
+
function ToolActions({ presentation, adapter }) {
|
|
523
|
+
const [copied, setCopied] = useState(false);
|
|
524
|
+
const reset = useRef(null);
|
|
525
|
+
useEffect(() => () => clearTimeout(reset.current), []);
|
|
526
|
+
if (!adapter?.copyText) return null;
|
|
527
|
+
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;
|
|
528
|
+
const copy = async () => {
|
|
529
|
+
await adapter.copyText(action[1]);
|
|
530
|
+
setCopied(true);
|
|
531
|
+
clearTimeout(reset.current);
|
|
532
|
+
reset.current = setTimeout(() => setCopied(false), 1500);
|
|
533
|
+
};
|
|
534
|
+
return action ? /* @__PURE__ */ jsx2("div", { class: "scui-tool-actions", children: /* @__PURE__ */ jsx2("button", { type: "button", onClick: copy, children: copied ? "Copied" : action[0] }) }) : null;
|
|
535
|
+
}
|
|
536
|
+
function ToolStack({ tools }) {
|
|
537
|
+
if (tools.length < 2) return null;
|
|
538
|
+
return /* @__PURE__ */ jsx2("div", { class: "scui-tool-stack", "aria-label": "Coordinated tools", children: tools.map((tool) => /* @__PURE__ */ jsx2("span", { children: tool.replaceAll("__", " \xB7 ").replaceAll("_", " ") }, tool)) });
|
|
539
|
+
}
|
|
450
540
|
function TechnicalDetails({ entry }) {
|
|
451
541
|
if (!entry.arguments && !entry.resultText) return null;
|
|
452
542
|
return /* @__PURE__ */ jsxs("details", { class: "scui-tool-technical", children: [
|
|
@@ -484,35 +574,42 @@ function TranscriptEntry({ entry, state, adapter }) {
|
|
|
484
574
|
entry.truncated ? /* @__PURE__ */ jsx2("small", { class: "scui-truncated", children: "Entry truncated by host \xB7 load native session for the complete value" }) : null
|
|
485
575
|
] });
|
|
486
576
|
}
|
|
487
|
-
function ToolRow({ entry, workspace, open = false }) {
|
|
577
|
+
function ToolRow({ entry, workspace, open = false, adapter }) {
|
|
488
578
|
const presentation = entry.presentation ?? createToolPresentation(entry);
|
|
579
|
+
const [expanded, setExpanded] = useState(open || entry.status === "pending");
|
|
580
|
+
useEffect(() => {
|
|
581
|
+
if (entry.status === "pending") setExpanded(true);
|
|
582
|
+
}, [entry.status]);
|
|
489
583
|
const target = compactToolTarget(presentation.target, workspace);
|
|
490
584
|
const hasDetail = Boolean(entry.arguments || entry.resultText || presentation.preview || presentation.fields.length);
|
|
491
585
|
const category = presentation.category ?? toolCategory(entry);
|
|
492
586
|
const summary = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
493
587
|
/* @__PURE__ */ jsx2(ToolIcon, { category }),
|
|
494
|
-
/* @__PURE__ */ jsx2("strong", { children:
|
|
588
|
+
/* @__PURE__ */ jsx2("strong", { children: toolAction2(entry, category, presentation) }),
|
|
495
589
|
target ? /* @__PURE__ */ jsx2("code", { class: "scui-tool-target", title: presentation.target, children: target }) : null,
|
|
496
590
|
/* @__PURE__ */ jsx2("span", { class: "scui-spacer" }),
|
|
591
|
+
entry.status === "pending" ? /* @__PURE__ */ jsx2(PendingElapsed, { now: adapter?.now }) : null,
|
|
497
592
|
/* @__PURE__ */ jsx2("span", { class: "scui-tool-status", role: "status", "data-status": entry.status ?? "completed", "aria-label": entry.status ?? "completed", children: entry.status === "pending" ? /* @__PURE__ */ jsx2("i", {}) : entry.status === "error" ? "\xD7" : "\u2713" }),
|
|
498
593
|
hasDetail ? /* @__PURE__ */ jsx2("span", { class: "scui-tool-chevron", "aria-hidden": "true", children: "\u203A" }) : null
|
|
499
594
|
] });
|
|
500
595
|
if (!hasDetail) return /* @__PURE__ */ jsx2("div", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: /* @__PURE__ */ jsx2("div", { class: "scui-tool-head", children: summary }) });
|
|
501
|
-
return /* @__PURE__ */ jsxs("details", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, open:
|
|
596
|
+
return /* @__PURE__ */ jsxs("details", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, open: expanded, onToggle: (event) => setExpanded(event.currentTarget.open), children: [
|
|
502
597
|
/* @__PURE__ */ jsx2("summary", { class: "scui-tool-head", children: summary }),
|
|
503
598
|
/* @__PURE__ */ jsxs("div", { class: "scui-tool-detail", children: [
|
|
504
599
|
/* @__PURE__ */ jsx2(ToolMetrics, { presentation }),
|
|
600
|
+
/* @__PURE__ */ jsx2(ToolStack, { tools: presentation.tools ?? [] }),
|
|
505
601
|
/* @__PURE__ */ jsx2(ToolPreview, { presentation, entry }),
|
|
506
602
|
presentation.fields.length ? /* @__PURE__ */ jsx2("dl", { class: "scui-tool-fields", children: presentation.fields.map((field) => /* @__PURE__ */ jsxs("div", { children: [
|
|
507
603
|
/* @__PURE__ */ jsx2("dt", { children: field.label }),
|
|
508
604
|
/* @__PURE__ */ jsx2("dd", { children: field.value })
|
|
509
605
|
] }, field.label)) }) : null,
|
|
606
|
+
/* @__PURE__ */ jsx2(ToolActions, { presentation, adapter }),
|
|
510
607
|
/* @__PURE__ */ jsx2(TechnicalDetails, { entry })
|
|
511
608
|
] })
|
|
512
609
|
] });
|
|
513
610
|
}
|
|
514
|
-
function ActivityGroup({ entries, state }) {
|
|
515
|
-
if (entries.length === 1) return /* @__PURE__ */ jsx2("section", { class: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx2(ToolRow, { entry: entries[0], workspace: state.workspace }) });
|
|
611
|
+
function ActivityGroup({ entries, state, adapter }) {
|
|
612
|
+
if (entries.length === 1) return /* @__PURE__ */ jsx2("section", { class: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx2(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
|
|
516
613
|
const active = entries.some((entry) => entry.status === "pending");
|
|
517
614
|
const [open, setOpen] = useState(active);
|
|
518
615
|
const id = useId();
|
|
@@ -530,7 +627,7 @@ function ActivityGroup({ entries, state }) {
|
|
|
530
627
|
entries.length
|
|
531
628
|
] })
|
|
532
629
|
] }),
|
|
533
|
-
open ? /* @__PURE__ */ jsx2("div", { id, children: entries.map((entry) => /* @__PURE__ */ jsx2(ToolRow, { entry, workspace: state.workspace }, entry.id)) }) : null
|
|
630
|
+
open ? /* @__PURE__ */ jsx2("div", { id, children: entries.map((entry) => /* @__PURE__ */ jsx2(ToolRow, { entry, workspace: state.workspace, adapter }, entry.id)) }) : null
|
|
534
631
|
] });
|
|
535
632
|
}
|
|
536
633
|
function TaskPlan({ plan }) {
|
package/core.mjs
CHANGED
|
@@ -90,15 +90,19 @@ function boundedString(value, max) {
|
|
|
90
90
|
return value.length <= max ? value : `${value.slice(0, max)}…`;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
function
|
|
93
|
+
function argumentValue(argumentsText) {
|
|
94
94
|
if (!argumentsText) return null;
|
|
95
95
|
try {
|
|
96
|
-
return
|
|
96
|
+
return JSON.parse(argumentsText);
|
|
97
97
|
} catch {
|
|
98
98
|
return null;
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
function argumentObject(argumentsText) {
|
|
103
|
+
return record(argumentValue(argumentsText));
|
|
104
|
+
}
|
|
105
|
+
|
|
102
106
|
function firstString(source, keys) {
|
|
103
107
|
for (const key of keys) {
|
|
104
108
|
if (typeof source?.[key] === 'string' && source[key].trim()) return source[key].trim();
|
|
@@ -106,6 +110,41 @@ function firstString(source, keys) {
|
|
|
106
110
|
return '';
|
|
107
111
|
}
|
|
108
112
|
|
|
113
|
+
function decodedLiteral(value) {
|
|
114
|
+
if (!value) return '';
|
|
115
|
+
if (value.startsWith('"')) {
|
|
116
|
+
try { return JSON.parse(value); } catch { return ''; }
|
|
117
|
+
}
|
|
118
|
+
return value.slice(1, -1)
|
|
119
|
+
.replaceAll('\\n', '\n')
|
|
120
|
+
.replaceAll('\\t', '\t')
|
|
121
|
+
.replaceAll('\\r', '\r')
|
|
122
|
+
.replaceAll('\\`', '`')
|
|
123
|
+
.replaceAll("\\'", "'")
|
|
124
|
+
.replaceAll('\\\\', '\\');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function sourceString(source, keys) {
|
|
128
|
+
if (!source) return '';
|
|
129
|
+
const names = keys.join('|');
|
|
130
|
+
const match = new RegExp(`\\b(?:${names})\\s*:\\s*(\"(?:\\\\.|[^\"\\\\])*\"|'(?:\\\\.|[^'\\\\])*'|\`(?:\\\\.|[^\`\\\\])*\`)`).exec(source);
|
|
131
|
+
return decodedLiteral(match?.[1]);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function toolEnvelope(entry) {
|
|
135
|
+
const value = argumentValue(entry.arguments);
|
|
136
|
+
const source = typeof value === 'string' ? value : '';
|
|
137
|
+
const tools = source
|
|
138
|
+
? [...new Set([...source.matchAll(/\btools\.([A-Za-z0-9_]+)/g)].map((match) => match[1]))].slice(0, 8)
|
|
139
|
+
: [];
|
|
140
|
+
const name = tools.length === 1 ? tools[0] : entry.label ?? 'tool';
|
|
141
|
+
return { args: record(value), source, tools, name };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function patchPath(source) {
|
|
145
|
+
return /\*\*\* (?:Update|Add|Delete) File:\s*([^\r\n]+)/.exec(source)?.[1]?.trim() ?? '';
|
|
146
|
+
}
|
|
147
|
+
|
|
109
148
|
function explicitNumber(sources, keys) {
|
|
110
149
|
for (const source of sources) {
|
|
111
150
|
for (const key of keys) {
|
|
@@ -143,7 +182,7 @@ function planItems(args) {
|
|
|
143
182
|
}).slice(0, 12);
|
|
144
183
|
}
|
|
145
184
|
|
|
146
|
-
function editPreview(args, resultText) {
|
|
185
|
+
function editPreview(args, resultText, source) {
|
|
147
186
|
const direct = firstString(args, ['patch', 'diff']);
|
|
148
187
|
if (direct) return direct;
|
|
149
188
|
const oldText = firstString(args, ['old_string']);
|
|
@@ -154,22 +193,66 @@ function editPreview(args, resultText) {
|
|
|
154
193
|
...newText.split('\n').map((line) => `+ ${line}`),
|
|
155
194
|
].join('\n');
|
|
156
195
|
}
|
|
196
|
+
const patch = /\*\*\* Begin Patch[\s\S]*?\*\*\* End Patch/.exec(source ?? '')?.[0];
|
|
197
|
+
if (patch) return patch;
|
|
157
198
|
return /^(?:diff --git|@@ |--- |\+\+\+ )/m.test(resultText ?? '') ? resultText : '';
|
|
158
199
|
}
|
|
159
200
|
|
|
201
|
+
function classifyTool(name, command) {
|
|
202
|
+
const normalized = name.toLocaleLowerCase();
|
|
203
|
+
if (/write_stdin|^wait$/.test(normalized)) return 'command';
|
|
204
|
+
if (/update.?plan|todo|checklist|taskcreate|taskupdate/.test(normalized)) return 'plan';
|
|
205
|
+
if (/search.?replace|edit|write|patch|replace|create_file|apply_patch/.test(normalized)) return 'edit';
|
|
206
|
+
if (/read|view|open_file|list_dir/.test(normalized)) return 'read';
|
|
207
|
+
if (/search|find|grep|glob|toolsearch/.test(normalized)) return 'search';
|
|
208
|
+
if (/browser|web|fetch|url/.test(normalized)) return 'web';
|
|
209
|
+
if (/agent|subagent|sendmessage|delegate|^task$/.test(normalized)) return 'agent';
|
|
210
|
+
if (/test|typecheck|lint|build/.test(normalized)) return 'test';
|
|
211
|
+
if (/terminal|bash|shell|command|exec|write_stdin|^wait$/.test(normalized)) return /\b(test|typecheck|lint|build)\b/i.test(command) ? 'test' : 'command';
|
|
212
|
+
return 'other';
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function toolAction(status, category, name, tools) {
|
|
216
|
+
const position = status === 'pending' ? 0 : status === 'error' ? 2 : 1;
|
|
217
|
+
if (tools.length > 1) return [`Running ${tools.length} actions`, `Ran ${tools.length} actions`, `${tools.length} actions failed`][position];
|
|
218
|
+
const normalized = name.toLocaleLowerCase();
|
|
219
|
+
if (/sendmessage/.test(normalized)) return ['Messaging agent', 'Messaged agent', 'Agent message failed'][position];
|
|
220
|
+
if (/kill_command_or_subagent/.test(normalized)) return ['Stopping', 'Stopped', 'Stop failed'][position];
|
|
221
|
+
if (/get_command_or_subagent_output|write_stdin|^wait$/.test(normalized)) return ['Waiting for', 'Checked', 'Check failed'][position];
|
|
222
|
+
const actions = {
|
|
223
|
+
read: ['Reading', 'Read', 'Read failed'],
|
|
224
|
+
search: ['Searching', 'Searched', 'Search failed'],
|
|
225
|
+
edit: ['Editing', 'Edited', 'Edit failed'],
|
|
226
|
+
command: ['Running command', 'Ran command', 'Command failed'],
|
|
227
|
+
test: ['Running tests', 'Ran tests', 'Tests failed'],
|
|
228
|
+
web: ['Browsing', 'Browsed', 'Browser action failed'],
|
|
229
|
+
agent: ['Starting agent', 'Started agent', 'Agent failed'],
|
|
230
|
+
plan: ['Updating plan', 'Updated plan', 'Plan update failed'],
|
|
231
|
+
};
|
|
232
|
+
return actions[category]?.[position] ?? (status === 'error' ? `${name} failed` : name.replaceAll(/[_-]+/g, ' '));
|
|
233
|
+
}
|
|
234
|
+
|
|
160
235
|
export function createToolPresentation(entry) {
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
const command = firstString(args, ['command', 'cmd']);
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
const
|
|
167
|
-
const
|
|
168
|
-
const
|
|
169
|
-
const
|
|
236
|
+
const envelope = toolEnvelope(entry);
|
|
237
|
+
const args = envelope.args;
|
|
238
|
+
const command = firstString(args, ['command', 'cmd']) || sourceString(envelope.source, ['command', 'cmd']);
|
|
239
|
+
const category = classifyTool(envelope.name, command || envelope.source || entry.arguments || '');
|
|
240
|
+
const path = firstString(args, ['file_path', 'target_file', 'target_directory', 'path']) || sourceString(envelope.source, ['file_path', 'target_file', 'target_directory', 'path']) || patchPath(envelope.source);
|
|
241
|
+
const query = firstString(args, ['query', 'pattern']) || sourceString(envelope.source, ['query', 'pattern', 'q']);
|
|
242
|
+
const url = firstString(args, ['url']) || sourceString(envelope.source, ['url', 'ref_id']);
|
|
243
|
+
const subject = firstString(args, ['subject', 'description', 'summary', 'task', 'prompt']) || sourceString(envelope.source, ['subject', 'description', 'summary', 'task', 'prompt']);
|
|
244
|
+
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' : '';
|
|
245
|
+
const items = planItems(args);
|
|
246
|
+
const taskId = firstString(args, ['taskId', 'task_id']);
|
|
247
|
+
const planTarget = category === 'plan' ? items.length ? `${items.length} ${items.length === 1 ? 'item' : 'items'}` : taskId ? `task ${taskId}` : '' : '';
|
|
248
|
+
const target = path || command || query || url || subject || background || planTarget || (envelope.tools.length > 1 ? `${envelope.tools.length} coordinated actions` : toolTarget(entry.arguments));
|
|
249
|
+
const previewSource = category === 'edit' ? editPreview(args, entry.resultText, envelope.source) : (entry.resultText ?? '');
|
|
170
250
|
const result = record(entry.resultContent);
|
|
171
251
|
const metadata = record(entry.metadata);
|
|
252
|
+
const resultMetadata = record(result?.metadata);
|
|
172
253
|
return {
|
|
254
|
+
name: boundedString(envelope.name, 120),
|
|
255
|
+
action: boundedString(toolAction(entry.status ?? 'completed', category, envelope.name, envelope.tools), 120),
|
|
173
256
|
category,
|
|
174
257
|
detail: toolDetail(category),
|
|
175
258
|
target: boundedString(target, 300),
|
|
@@ -180,12 +263,13 @@ export function createToolPresentation(entry) {
|
|
|
180
263
|
subject: boundedString(subject, MAX_TOOL_FIELD_CHARS),
|
|
181
264
|
preview: boundedString(previewSource, MAX_TOOL_PREVIEW_CHARS),
|
|
182
265
|
fields: usefulToolFields(args),
|
|
183
|
-
items
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
266
|
+
items,
|
|
267
|
+
tools: envelope.tools,
|
|
268
|
+
exitCode: explicitNumber([result, resultMetadata, metadata], ['exit_code', 'exitCode', 'pi_bash_exit_code']),
|
|
269
|
+
durationMs: explicitNumber([result, resultMetadata, metadata], ['duration_ms', 'durationMs', 'elapsed_ms', 'elapsedMs', 'totalDurationMs']),
|
|
270
|
+
additions: explicitNumber([result, resultMetadata, metadata], ['additions', 'lines_added']),
|
|
271
|
+
deletions: explicitNumber([result, resultMetadata, metadata], ['deletions', 'lines_removed']),
|
|
272
|
+
matches: explicitNumber([result, resultMetadata, metadata], ['matches', 'match_count', 'result_count']),
|
|
189
273
|
};
|
|
190
274
|
}
|
|
191
275
|
|
|
@@ -205,7 +289,10 @@ function readToolPresentation(value, entry) {
|
|
|
205
289
|
? [{ label: boundedString(planItem.label, 300), status: boundedString(planItem.status, 40) }]
|
|
206
290
|
: [];
|
|
207
291
|
}).slice(0, 12) : generated.items;
|
|
292
|
+
const tools = Array.isArray(item.tools) ? item.tools.filter((tool) => typeof tool === 'string').map((tool) => boundedString(tool, 120)).slice(0, 8) : generated.tools;
|
|
208
293
|
return {
|
|
294
|
+
name: boundedString(item.name, 120) || generated.name,
|
|
295
|
+
action: boundedString(item.action, 120) || generated.action,
|
|
209
296
|
category: TOOL_CATEGORIES.has(item.category) ? item.category : generated.category,
|
|
210
297
|
detail: TOOL_DETAILS.has(item.detail) ? item.detail : generated.detail,
|
|
211
298
|
target: boundedString(item.target, 300) || generated.target,
|
|
@@ -217,6 +304,7 @@ function readToolPresentation(value, entry) {
|
|
|
217
304
|
preview: boundedString(item.preview, MAX_TOOL_PREVIEW_CHARS) || generated.preview,
|
|
218
305
|
fields,
|
|
219
306
|
items,
|
|
307
|
+
tools,
|
|
220
308
|
exitCode: nullableNumber(item.exitCode) ?? generated.exitCode,
|
|
221
309
|
durationMs: nullableNumber(item.durationMs) ?? generated.durationMs,
|
|
222
310
|
additions: nullableNumber(item.additions) ?? generated.additions,
|
|
@@ -505,16 +593,9 @@ export function groupConversation(entries) {
|
|
|
505
593
|
|
|
506
594
|
export function toolCategory(entry) {
|
|
507
595
|
if (TOOL_CATEGORIES.has(entry.presentation?.category)) return entry.presentation.category;
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
if (/edit|write|patch|replace|create_file/.test(name)) return 'edit';
|
|
512
|
-
if (/test|typecheck|lint|build/.test(name)) return 'test';
|
|
513
|
-
if (/terminal|bash|shell|command|exec/.test(name)) return /\b(test|typecheck|lint|build)\b/i.test(entry.arguments ?? '') ? 'test' : 'command';
|
|
514
|
-
if (/browser|web|fetch|url/.test(name)) return 'web';
|
|
515
|
-
if (/update.?plan|todo|checklist/.test(name)) return 'plan';
|
|
516
|
-
if (/subagent|spawn.?agent|delegate|^task$/.test(name)) return 'agent';
|
|
517
|
-
return 'other';
|
|
596
|
+
const envelope = toolEnvelope(entry);
|
|
597
|
+
const command = firstString(envelope.args, ['command', 'cmd']) || sourceString(envelope.source, ['command', 'cmd']);
|
|
598
|
+
return classifyTool(envelope.name, command || envelope.source || entry.arguments || '');
|
|
518
599
|
}
|
|
519
600
|
|
|
520
601
|
export function toolTarget(argumentsText) {
|
|
@@ -543,7 +624,8 @@ export function activitySummary(entries) {
|
|
|
543
624
|
if (pending) return `${entries.length} actions in progress`;
|
|
544
625
|
if (failed) return `${entries.length} actions · ${failed} failed`;
|
|
545
626
|
if ([...counts.keys()].every((key) => key === 'read' || key === 'search')) return `Explored ${entries.length} ${entries.length === 1 ? 'item' : 'items'}`;
|
|
546
|
-
|
|
627
|
+
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'] };
|
|
628
|
+
return [...counts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 3).map(([category, count]) => `${count} ${nouns[category][count === 1 ? 0 : 1]}`).join(' · ');
|
|
547
629
|
}
|
|
548
630
|
|
|
549
631
|
export function canContinueHere(state) {
|
|
@@ -554,7 +636,7 @@ export function canContinueHere(state) {
|
|
|
554
636
|
|
|
555
637
|
export function operationLabel(operation) {
|
|
556
638
|
if (!operation) return '';
|
|
557
|
-
const labels = { discover: 'Refreshing chats…', attach: 'Opening chat…', resume: 'Continuing here…', branch: 'Starting continuation…', reduce: 'Reducing context and verifying reversibility…', terminal: 'Preparing terminal handoff…', export: 'Exporting losslessly…', refresh: 'Retrying…' };
|
|
639
|
+
const labels = { discover: 'Refreshing chats…', observe: 'Loading recent messages…', attach: 'Opening chat…', resume: 'Continuing here…', branch: 'Starting continuation…', reduce: 'Reducing context and verifying reversibility…', terminal: 'Preparing terminal handoff…', export: 'Exporting losslessly…', refresh: 'Retrying…' };
|
|
558
640
|
return labels[operation] ?? `${operation.replaceAll('_', ' ')}…`;
|
|
559
641
|
}
|
|
560
642
|
|