@marcoscale98/pi-extensible-workflows 5.14.1-fork.1 → 5.19.1-fork.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +135 -0
- package/dist/src/agent-execution.js +114 -87
- package/dist/src/background-widget.d.ts +61 -0
- package/dist/src/background-widget.js +23 -16
- package/dist/src/decoders.js +28 -1
- package/dist/src/execution.d.ts +4 -2
- package/dist/src/execution.js +43 -2
- package/dist/src/host-delivery.js +1 -0
- package/dist/src/host-navigator.d.ts +18 -0
- package/dist/src/host-navigator.js +71 -13
- package/dist/src/host-recovery.d.ts +5 -2
- package/dist/src/host-recovery.js +3 -3
- package/dist/src/host-view.d.ts +21 -1
- package/dist/src/host-view.js +138 -47
- package/dist/src/host.d.ts +2 -2
- package/dist/src/host.js +246 -60
- package/dist/src/index.d.ts +3 -1
- package/dist/src/index.js +1346 -622
- package/dist/src/index.js.map +3 -3
- package/dist/src/paths.d.ts +2 -0
- package/dist/src/paths.js +2 -0
- package/dist/src/registry.js +3 -2
- package/dist/src/roles.d.ts +70 -0
- package/dist/src/roles.js +326 -0
- package/dist/src/script-tools.d.ts +27 -0
- package/dist/src/script-tools.js +87 -0
- package/dist/src/settings.d.ts +26 -0
- package/dist/src/settings.js +277 -0
- package/dist/src/trajectory.d.ts +39 -4
- package/dist/src/trajectory.js +82 -5
- package/dist/src/types.d.ts +30 -3
- package/dist/src/types.js +1 -1
- package/dist/src/utils.d.ts +21 -1
- package/dist/src/utils.js +40 -0
- package/dist/src/validation.d.ts +10 -17
- package/dist/src/validation.js +29 -458
- package/dist/starter/index.js +0 -2
- package/dist/starter/index.js.map +1 -1
- package/dist/starter/review-loop.d.ts +0 -13
- package/dist/starter/review-loop.js +0 -2
- package/dist/subagents/index.js +1344 -798
- package/dist/subagents/index.js.map +3 -3
- package/dist/subagents/src/contracts.d.ts +49 -29
- package/dist/subagents/src/contracts.js +18 -7
- package/dist/subagents/src/index.d.ts +1 -1
- package/dist/subagents/src/index.js +15 -7
- package/dist/subagents/src/manager.js +32 -13
- package/dist/subagents/src/navigator.js +396 -247
- package/dist/subagents/src/view.d.ts +24 -2
- package/dist/subagents/src/view.js +107 -80
- package/dist/trajectory/assets/index.html +370 -232
- package/dist/trajectory/assets/prism.min.js +13 -0
- package/dist/trajectory/index.js +445 -277
- package/dist/trajectory/index.js.map +3 -3
- package/dist/trajectory/src/assets/index.html +370 -232
- package/dist/trajectory/src/assets/prism.min.js +13 -0
- package/dist/trajectory/src/export.js +4 -2
- package/dist/trajectory/src/index.d.ts +2 -0
- package/dist/trajectory/src/index.js +151 -60
- package/dist/trajectory/src/server.js +138 -11
- package/dist/trajectory/src/server.js.map +2 -2
- package/package.json +7 -6
- package/skills/pi-extensible-workflows/SKILL.md +2 -0
- package/src/agent-execution.ts +110 -80
- package/src/background-widget.ts +26 -19
- package/src/decoders.ts +22 -1
- package/src/execution.ts +41 -4
- package/src/host-delivery.ts +1 -0
- package/src/host-navigator.ts +58 -14
- package/src/host-recovery.ts +8 -7
- package/src/host-view.ts +116 -42
- package/src/host.ts +223 -55
- package/src/index.ts +3 -1
- package/src/paths.ts +3 -0
- package/src/registry.ts +3 -2
- package/src/roles.ts +327 -0
- package/src/script-tools.ts +76 -0
- package/src/settings.ts +224 -0
- package/src/trajectory.ts +97 -9
- package/src/types.ts +12 -5
- package/src/utils.ts +36 -1
- package/src/validation.ts +30 -356
- package/starter/review-loop.ts +0 -2
- package/subagents/README.md +3 -3
- package/subagents/src/contracts.ts +22 -7
- package/subagents/src/index.ts +18 -7
- package/subagents/src/manager.ts +31 -13
- package/subagents/src/navigator.ts +336 -180
- package/subagents/src/view.ts +108 -76
- package/trajectory/src/assets/index.html +370 -232
- package/trajectory/src/assets/prism.min.js +13 -0
- package/trajectory/src/export.ts +4 -2
- package/trajectory/src/index.ts +124 -47
- package/trajectory/src/server.ts +114 -14
package/subagents/src/view.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { AgentToolResult, ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
1
|
+
import type { AgentToolResult, ExtensionAPI, ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
3
|
-
import { formatCost, formatStalledDuration, sanitizeDisplayText, WORKFLOW_AGENT_STALL_THRESHOLD_MS } from "../../src/index.js";
|
|
3
|
+
import { formatCost, formatStalledDuration, formatWorkflowRuntime, sanitizeDisplayText, WORKFLOW_AGENT_STALL_THRESHOLD_MS } from "../../src/index.js";
|
|
4
|
+
import { agentActivityLabel, stalledDuration, textBlock } from "../../src/host-view.js";
|
|
5
|
+
import { drawFrame, formatElapsed, formatTokens, mark, QUIET_MS, REPAINT_MS, type Paint } from "../../src/background-widget.js";
|
|
4
6
|
import type { SubagentRunRequest, SubagentStatus } from "./contracts.js";
|
|
5
7
|
|
|
6
8
|
const SPINNER = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] as const;
|
|
@@ -18,13 +20,6 @@ export type SubagentRenderState = {
|
|
|
18
20
|
subagentProgressFrozenAt?: number;
|
|
19
21
|
};
|
|
20
22
|
|
|
21
|
-
function textBlock(text: string) {
|
|
22
|
-
return {
|
|
23
|
-
render(width: number): string[] { return text.split("\n").map((line) => truncateToWidth(line, Math.max(1, width), "…")); },
|
|
24
|
-
invalidate() {},
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
23
|
function roleName(value: unknown): string | undefined {
|
|
29
24
|
if (typeof value === "string") return value.trim() || undefined;
|
|
30
25
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined;
|
|
@@ -72,18 +67,6 @@ function statusValue(value: unknown): SubagentStatus | undefined {
|
|
|
72
67
|
return value as SubagentStatus;
|
|
73
68
|
}
|
|
74
69
|
|
|
75
|
-
function runtime(startedAt: number | undefined, finishedAt: number | undefined, now: number): string {
|
|
76
|
-
if (startedAt === undefined) return "";
|
|
77
|
-
const seconds = Math.max(0, Math.floor(((finishedAt ?? now) - startedAt) / 1000));
|
|
78
|
-
if (seconds < 60) return `${String(seconds)}s`;
|
|
79
|
-
const minutes = Math.floor(seconds / 60);
|
|
80
|
-
const remainingSeconds = seconds % 60;
|
|
81
|
-
if (minutes < 60) return `${String(minutes)}m${remainingSeconds ? ` ${String(remainingSeconds)}s` : ""}`;
|
|
82
|
-
const hours = Math.floor(minutes / 60);
|
|
83
|
-
const remainingMinutes = minutes % 60;
|
|
84
|
-
return `${String(hours)}h${remainingMinutes ? ` ${String(remainingMinutes)}m` : ""}`;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
70
|
function stateGlyph(state: SubagentStatus["state"], spinner: string): string {
|
|
88
71
|
if (state === "running") return spinner;
|
|
89
72
|
return state === "completed" ? "✓" : "✗";
|
|
@@ -94,30 +77,14 @@ function stateColor(state: SubagentStatus["state"]): "accent" | "success" | "err
|
|
|
94
77
|
return state === "completed" ? "success" : "error";
|
|
95
78
|
}
|
|
96
79
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const current = status.progress?.activity;
|
|
100
|
-
if (current?.kind === "reasoning" || current?.kind === "text") {
|
|
101
|
-
const name = current.kind === "reasoning" ? "reasoning" : "responding";
|
|
102
|
-
const text = sanitizeDisplayText(current.text);
|
|
103
|
-
return text && text !== name ? `${name} · ${text}` : name;
|
|
104
|
-
}
|
|
105
|
-
if (current?.kind === "tool") return sanitizeDisplayText(current.text);
|
|
106
|
-
return sanitizeDisplayText([...(status.progress?.toolCalls ?? [])].reverse().find(({ state }) => state === "running")?.name ?? "");
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function stalledDuration(status: SubagentStatus, now: number): number | undefined {
|
|
110
|
-
const lastEventAt = status.progress?.lastEventAt;
|
|
111
|
-
if (status.state !== "running" || lastEventAt === undefined || !Number.isFinite(lastEventAt)) return undefined;
|
|
112
|
-
const duration = now - lastEventAt;
|
|
113
|
-
return duration >= WORKFLOW_AGENT_STALL_THRESHOLD_MS ? duration : undefined;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function accounting(status: SubagentStatus): string | undefined {
|
|
80
|
+
/** Tokens and cost as the workflow header shows them. */
|
|
81
|
+
function usageStats(status: SubagentStatus): string {
|
|
117
82
|
const value = status.progress?.accounting;
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
83
|
+
return value ? [formatTokens(value.input + value.output), formatCost(value.cost)].filter(Boolean).join(" · ") : "";
|
|
84
|
+
}
|
|
85
|
+
function modelName(status: SubagentStatus): string {
|
|
86
|
+
const model = status.progress?.state?.model ?? status.attemptDetails?.at(-1)?.setup.model;
|
|
87
|
+
return model ? `${model.model}${model.thinking ? `:${model.thinking}` : ""}` : "";
|
|
121
88
|
}
|
|
122
89
|
|
|
123
90
|
function timestamp(value: number | undefined): string | undefined {
|
|
@@ -139,22 +106,21 @@ function boundedValue(value: unknown): string {
|
|
|
139
106
|
|
|
140
107
|
function formatSubagentProgress(status: SubagentStatus, args: SubagentRenderArgs, theme: Theme, spinner: string, now: number, expanded: boolean): string {
|
|
141
108
|
const color = stateColor(status.state);
|
|
142
|
-
const elapsed =
|
|
109
|
+
const elapsed = status.startedAt === undefined ? "" : formatWorkflowRuntime((status.finishedAt ?? now) - status.startedAt);
|
|
143
110
|
const metadata = requestMetadata(args, args.id === undefined);
|
|
111
|
+
const stats = usageStats(status);
|
|
144
112
|
const lines = [
|
|
145
|
-
`${theme.fg(color, stateGlyph(status.state,
|
|
113
|
+
`${theme.fg(color, stateGlyph(status.state, "●"))} ${theme.bold(theme.fg("accent", `Subagent: ${label({ ...args, id: status.id })}`))} ${theme.fg(color, `[${status.state}]`)}${metadata ? ` ${theme.fg("dim", metadata)}` : ""}${stats ? ` ${stats}` : ""}${elapsed ? ` runtime=${elapsed}` : ""}`,
|
|
146
114
|
];
|
|
147
|
-
const current =
|
|
148
|
-
const stalled = stalledDuration(status, now);
|
|
149
|
-
if (current) lines.push(` ${theme.fg("accent", spinner)} ${theme.fg("dim", current)}${stalled === undefined ? "" : ` ${theme.fg("warning", `- stalled? ${formatStalledDuration(stalled)}`)}`}`);
|
|
115
|
+
const current = status.state === "running" ? agentActivityLabel(status.progress ?? {}) : undefined;
|
|
116
|
+
const stalled = stalledDuration({ ...status.progress, state: status.state }, now);
|
|
117
|
+
if (current || status.state === "running") lines.push(` ${theme.fg("accent", spinner)}${current ? ` ${theme.fg("dim", current)}` : ""}${stalled === undefined ? "" : ` ${theme.fg("warning", `- stalled? ${formatStalledDuration(stalled)}`)}`}`);
|
|
150
118
|
else if (stalled !== undefined) lines.push(` ${theme.fg("warning", `stalled? ${formatStalledDuration(stalled)}`)}`);
|
|
151
119
|
if (status.error) lines.push(` ${theme.fg("error", `${status.error.code}: ${status.error.message}`)}`);
|
|
152
120
|
if (expanded) {
|
|
153
121
|
lines.push(` ${theme.fg("dim", `id=${status.id}`)}`);
|
|
154
122
|
const model = status.progress?.state?.model;
|
|
155
123
|
if (model) lines.push(` ${theme.fg("dim", `model=${model.provider}/${model.model}${model.thinking ? `:${model.thinking}` : ""}`)}`);
|
|
156
|
-
const usage = accounting(status);
|
|
157
|
-
if (usage) lines.push(` ${theme.fg("dim", usage)}`);
|
|
158
124
|
if (status.worktree) lines.push(` ${theme.fg("dim", `worktree=${status.worktree.path} branch=${status.worktree.branch}`)}`);
|
|
159
125
|
}
|
|
160
126
|
return lines.join("\n");
|
|
@@ -215,7 +181,7 @@ export function subagentProgressBlock(status: SubagentStatus, args: SubagentRend
|
|
|
215
181
|
render(width: number): string[] {
|
|
216
182
|
const now = TERMINAL_STATES.has(current.state) ? frozenAt : Date.now();
|
|
217
183
|
if (!TERMINAL_STATES.has(current.state)) frozenAt = now;
|
|
218
|
-
const frame = SPINNER[Math.floor(now /
|
|
184
|
+
const frame = SPINNER[Math.floor(now / REPAINT_MS) % SPINNER.length] ?? "◇";
|
|
219
185
|
return formatSubagentProgress(current, args, currentTheme, frame, now, expanded).split("\n").map((line) => truncateToWidth(line, Math.max(1, width), "…"));
|
|
220
186
|
},
|
|
221
187
|
invalidate() {},
|
|
@@ -233,7 +199,7 @@ export function renderSubagentResult(result: AgentToolResult<unknown>, options:
|
|
|
233
199
|
const status = state.subagentStatus;
|
|
234
200
|
|
|
235
201
|
if (status?.state === "running" && options.isPartial && !state.subagentSpinner) {
|
|
236
|
-
state.subagentSpinner = setInterval(() => { context.invalidate(); },
|
|
202
|
+
state.subagentSpinner = setInterval(() => { context.invalidate(); }, REPAINT_MS);
|
|
237
203
|
state.subagentSpinner.unref();
|
|
238
204
|
} else if ((!options.isPartial || status?.state !== "running") && state.subagentSpinner) {
|
|
239
205
|
clearInterval(state.subagentSpinner);
|
|
@@ -257,9 +223,77 @@ export function renderSubagentResult(result: AgentToolResult<unknown>, options:
|
|
|
257
223
|
|
|
258
224
|
type WidgetRun = { status: Readonly<SubagentStatus>; request: Readonly<SubagentRunRequest> };
|
|
259
225
|
|
|
260
|
-
|
|
226
|
+
/** One run as a workflow agent row: a quiet or stalled warning on the left, model and figures against the right rule. */
|
|
227
|
+
function widgetRow({ status, request }: WidgetRun, now: number, paint: Paint): { text: string; right: string } {
|
|
228
|
+
const lastEventAt = status.progress?.lastEventAt;
|
|
229
|
+
const silent = lastEventAt === undefined || !Number.isFinite(lastEventAt) || now - lastEventAt < QUIET_MS ? undefined : now - lastEventAt;
|
|
230
|
+
const role = silent !== undefined && silent >= WORKFLOW_AGENT_STALL_THRESHOLD_MS ? "error" : "warning";
|
|
231
|
+
const icon = silent === undefined ? mark(status.state, now, paint) : paint(role, "⚠");
|
|
232
|
+
const warning = silent === undefined ? "" : ` ${paint(role, `${role === "error" ? "stalled" : "quiet"} ${formatElapsed(silent)}`)}`;
|
|
233
|
+
const accounting = status.progress?.accounting;
|
|
234
|
+
const attempts = status.attempts ?? 1;
|
|
235
|
+
const right = [
|
|
236
|
+
modelName(status),
|
|
237
|
+
formatTokens(accounting === undefined ? undefined : accounting.input + accounting.output),
|
|
238
|
+
formatCost(accounting?.cost),
|
|
239
|
+
status.startedAt === undefined ? "" : formatElapsed(now - status.startedAt),
|
|
240
|
+
attempts > 1 ? `attempt ${String(attempts)}` : "",
|
|
241
|
+
].filter(Boolean).join(" · ");
|
|
242
|
+
return { text: `${icon} ${sanitizeDisplayText(label({ ...request, id: status.id }))}${warning}`, right };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const RECEIPT_ENTRY_TYPE = "piewf-subagent-receipt";
|
|
246
|
+
type SubagentReceipt = { readonly id: string; readonly label: string; readonly state: string; readonly model?: string; readonly role?: string; readonly tools?: readonly string[]; readonly input: number; readonly output: number; readonly cacheRead: number; readonly costUsd: number; readonly durationMs: number; readonly attempts: number; readonly error?: string };
|
|
247
|
+
|
|
248
|
+
function receiptFor(status: Readonly<SubagentStatus>, request: Readonly<SubagentRunRequest>): SubagentReceipt {
|
|
249
|
+
const accounting = status.progress?.accounting;
|
|
250
|
+
const model = modelName(status);
|
|
251
|
+
const role = roleName(request.role);
|
|
252
|
+
const tools = status.progress?.state?.tools ?? status.attemptDetails?.at(-1)?.setup.tools;
|
|
253
|
+
return {
|
|
254
|
+
id: status.id,
|
|
255
|
+
label: label({ ...request, id: status.id }),
|
|
256
|
+
state: status.state,
|
|
257
|
+
...(model ? { model } : {}),
|
|
258
|
+
...(role === undefined ? {} : { role }),
|
|
259
|
+
...(tools === undefined ? {} : { tools: [...tools] }),
|
|
260
|
+
input: accounting?.input ?? 0,
|
|
261
|
+
output: accounting?.output ?? 0,
|
|
262
|
+
cacheRead: accounting?.cacheRead ?? 0,
|
|
263
|
+
costUsd: accounting?.cost ?? 0,
|
|
264
|
+
durationMs: status.startedAt === undefined ? 0 : Math.max(0, (status.finishedAt ?? Date.now()) - status.startedAt),
|
|
265
|
+
attempts: status.attempts ?? 1,
|
|
266
|
+
...(status.error === undefined ? {} : { error: `${status.error.code}: ${status.error.message}` }),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** The transcript line a finished background run leaves behind, in the shape of the workflow receipt. */
|
|
271
|
+
export function renderSubagentReceipt(data: SubagentReceipt, expanded: boolean, theme: Theme): string[] {
|
|
272
|
+
const glyph = data.state === "completed" ? theme.fg("success", "✓") : data.state === "failed" ? theme.fg("error", "✗") : theme.fg("muted", "·");
|
|
273
|
+
const headline = [formatTokens(data.input + data.output), formatCost(data.costUsd) || "$0.00", formatElapsed(data.durationMs), data.state].filter(Boolean).join(" · ");
|
|
274
|
+
const lines = [`${glyph} ${theme.bold(sanitizeDisplayText(data.label))} ${theme.fg("muted", headline)}`];
|
|
275
|
+
if (!expanded) return lines;
|
|
276
|
+
const meta = [data.model ?? "", data.role ? `role ${data.role}` : "", data.attempts > 1 ? `${String(data.attempts)} attempts` : "", data.tools?.join(" ") ?? ""].filter(Boolean);
|
|
277
|
+
if (meta.length > 0) lines.push(theme.fg("muted", ` ${meta.join(" · ")}`));
|
|
278
|
+
const split = [data.input ? `in ${formatTokens(data.input)}` : "", data.output ? `out ${formatTokens(data.output)}` : "", data.cacheRead ? `cache ${formatTokens(data.cacheRead)}` : ""].filter(Boolean);
|
|
279
|
+
if (split.length > 0) lines.push(theme.fg("muted", ` ${split.join(" · ")}`));
|
|
280
|
+
if (data.error !== undefined) lines.push(theme.fg("error", ` ${sanitizeDisplayText(data.error)}`));
|
|
281
|
+
lines.push(theme.fg("muted", ` run ${data.id}`));
|
|
282
|
+
return lines;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
type ReceiptHost = { appendEntry?: (customType: string, data: SubagentReceipt) => void; registerEntryRenderer?: ExtensionAPI["registerEntryRenderer"] };
|
|
286
|
+
|
|
287
|
+
export function createSubagentBackgroundWidget(host: ReceiptHost = {}) {
|
|
261
288
|
const key = "piewf-subagents-background";
|
|
262
289
|
const runs = new Map<string, WidgetRun>();
|
|
290
|
+
const receipted = new Set<string>();
|
|
291
|
+
host.registerEntryRenderer?.<SubagentReceipt>(RECEIPT_ENTRY_TYPE, (entry, options, theme) => {
|
|
292
|
+
if (!entry.data) return undefined;
|
|
293
|
+
const lines = renderSubagentReceipt(entry.data, options.expanded, theme);
|
|
294
|
+
// Pi treats an over-wide line as fatal, and an error message has no bound.
|
|
295
|
+
return { render: (width: number): string[] => lines.map((line) => truncateToWidth(line, Math.max(1, width), "…")), invalidate() {} };
|
|
296
|
+
});
|
|
263
297
|
let context: ExtensionContext | undefined;
|
|
264
298
|
let requestRender: (() => void) | undefined;
|
|
265
299
|
let timer: ReturnType<typeof setInterval> | undefined;
|
|
@@ -289,29 +323,20 @@ export function createSubagentBackgroundWidget() {
|
|
|
289
323
|
return {
|
|
290
324
|
render(width: number): string[] {
|
|
291
325
|
const now = Date.now();
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
const
|
|
296
|
-
const
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
for (const block of blocks) {
|
|
301
|
-
if (runRows + block.length > runRowBudget) break;
|
|
302
|
-
lines.push(...block);
|
|
303
|
-
runRows += block.length;
|
|
304
|
-
displayedRuns += 1;
|
|
305
|
-
}
|
|
306
|
-
const hiddenRuns = blocks.length - displayedRuns;
|
|
307
|
-
if (hiddenRuns > 0) lines.push(`… ${String(hiddenRuns)} more`);
|
|
308
|
-
return lines.map((line) => truncateToWidth(line, Math.max(1, width), "…"));
|
|
326
|
+
const paintRole: Paint = (role, text) => theme.fg(role, text);
|
|
327
|
+
// One row per run, so the default concurrency of eight fills the frame exactly.
|
|
328
|
+
// ponytail: no Alt+O scrolling as in the workflow widget; add it if runs routinely exceed the frame.
|
|
329
|
+
const budget = MAX_BACKGROUND_WIDGET_ROWS - 2;
|
|
330
|
+
const rows = [...runs.values()].map((run) => widgetRow(run, now, paintRole));
|
|
331
|
+
const body = rows.length <= budget ? rows : [...rows.slice(0, budget - 1), { text: theme.fg("muted", `… ${String(rows.length - budget + 1)} more`) }];
|
|
332
|
+
const title = runs.size === 1 ? "Subagent" : `Subagents · ${String(runs.size)} runs`;
|
|
333
|
+
return drawFrame(title, "", body, width, paintRole, (text) => theme.fg("accent", text)).map((line) => truncateToWidth(line, Math.max(1, width), ""));
|
|
309
334
|
},
|
|
310
335
|
invalidate() {},
|
|
311
336
|
};
|
|
312
337
|
}, { placement: "belowEditor" });
|
|
313
338
|
showing = true;
|
|
314
|
-
timer = setInterval(() => { requestRender?.(); },
|
|
339
|
+
timer = setInterval(() => { requestRender?.(); }, REPAINT_MS);
|
|
315
340
|
timer.unref();
|
|
316
341
|
} catch {
|
|
317
342
|
hide();
|
|
@@ -322,12 +347,19 @@ export function createSubagentBackgroundWidget() {
|
|
|
322
347
|
start(next: ExtensionContext): void {
|
|
323
348
|
hide();
|
|
324
349
|
runs.clear();
|
|
350
|
+
receipted.clear();
|
|
325
351
|
context = next.mode === "tui" ? next : undefined;
|
|
326
352
|
},
|
|
327
353
|
update(status: Readonly<SubagentStatus>, request: Readonly<SubagentRunRequest>): void {
|
|
328
354
|
if (request.mode !== "background" || context === undefined) return;
|
|
329
|
-
if (TERMINAL_STATES.has(status.state))
|
|
330
|
-
|
|
355
|
+
if (TERMINAL_STATES.has(status.state)) {
|
|
356
|
+
runs.delete(status.id);
|
|
357
|
+
// A foreground run already has its tool block; a background one leaves a receipt, once, like a workflow run.
|
|
358
|
+
if (!receipted.has(status.id)) {
|
|
359
|
+
receipted.add(status.id);
|
|
360
|
+
try { host.appendEntry?.(RECEIPT_ENTRY_TYPE, receiptFor(status, request)); } catch { /* A receipt is display-only. */ }
|
|
361
|
+
}
|
|
362
|
+
} else runs.set(status.id, { status, request });
|
|
331
363
|
paint();
|
|
332
364
|
},
|
|
333
365
|
dispose(): void {
|
|
@@ -339,9 +371,9 @@ export function createSubagentBackgroundWidget() {
|
|
|
339
371
|
}
|
|
340
372
|
|
|
341
373
|
|
|
342
|
-
export function renderSubagentInspectCall(args: { id?: string }, theme: Theme) {
|
|
374
|
+
export function renderSubagentInspectCall(args: { id?: string; scope?: string }, theme: Theme) {
|
|
343
375
|
const title = theme.fg("toolTitle", theme.bold("subagents_inspect"));
|
|
344
|
-
return textBlock(args.id ? `${title} ${theme.fg("accent", args.id)}` : `${title} ${theme.fg("muted", "all")}`);
|
|
376
|
+
return textBlock(args.id ? `${title} ${theme.fg("accent", args.id)}` : `${title} ${theme.fg("muted", args.scope === "all" ? "all" : "session")}`);
|
|
345
377
|
}
|
|
346
378
|
|
|
347
379
|
export function renderSubagentInspectResult(result: AgentToolResult<unknown>, options: { expanded: boolean }, theme: Theme, args: { id?: string }) {
|