pi-subagents 0.62.0 → 0.63.0
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 +28 -0
- package/docs/agents.md +5 -4
- package/docs/configuration.md +28 -2
- package/docs/models.md +5 -5
- package/docs/observability.md +4 -1
- package/docs/tool-reference.md +2 -2
- package/package.json +1 -1
- package/skills/pi-subagents/references/management-authoring-rpc.md +1 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +3 -3
- package/src/agents/agent-management.ts +22 -4
- package/src/agents/agents.ts +107 -125
- package/src/api/shared-types.ts +3 -0
- package/src/extension/config.ts +20 -0
- package/src/inspectors/herdr/inspector-runner.ts +19 -13
- package/src/runs/background/active-async-capacity.ts +0 -1
- package/src/runs/background/async-execution.ts +53 -7
- package/src/runs/background/async-resume.ts +3 -0
- package/src/runs/background/async-status.ts +18 -2
- package/src/runs/background/notify.ts +13 -1
- package/src/runs/background/run-status.ts +22 -2
- package/src/runs/background/subagent-runner.ts +30 -2
- package/src/runs/background/wait-completions.ts +13 -0
- package/src/runs/foreground/subagent-executor.ts +35 -3
- package/src/runs/shared/acceptance.ts +15 -9
- package/src/runs/shared/lane-metadata.ts +24 -3
- package/src/runs/shared/parallel-handoff.ts +4 -0
- package/src/runs/shared/pi-args.ts +8 -2
- package/src/runs/shared/task-intent.ts +5 -2
- package/src/runs/shared/worktree.ts +467 -63
- package/src/shared/types.ts +29 -0
- package/src/shared/utils.ts +18 -7
- package/src/slash/subagents-admin.ts +24 -12
- package/src/tui/fleet-status.ts +61 -2
- package/src/tui/fleet.ts +12 -7
- package/src/tui/render.ts +222 -14
- package/src/workflows/workflow-checklist.ts +441 -0
package/src/tui/render.ts
CHANGED
|
@@ -39,6 +39,7 @@ import { encodeAsyncStatusSnapshotWidget } from "../runs/background/async-status
|
|
|
39
39
|
import { projectAsyncWorkflowRows, type AsyncStatusWorkflowRow } from "../runs/shared/async-status-projection.ts";
|
|
40
40
|
import { hostStepReportName, hostStepVerdictLabel } from "../runs/shared/host-step-status.ts";
|
|
41
41
|
import { workflowGraphStageNodes } from "../runs/shared/workflow-graph.ts";
|
|
42
|
+
import { formatWorkflowChecklistBottleneck, formatWorkflowChecklistPhase, formatWorkflowChecklistSummary, projectWorkflowChecklist, type WorkflowChecklistItem, type WorkflowChecklistProjection, type WorkflowChecklistState, type WorkflowChecklistStep } from "../workflows/workflow-checklist.ts";
|
|
42
43
|
|
|
43
44
|
type Theme = ExtensionContext["ui"]["theme"];
|
|
44
45
|
|
|
@@ -47,6 +48,7 @@ interface WorkflowWidgetProjection {
|
|
|
47
48
|
steps: AsyncJobStep[];
|
|
48
49
|
stageProgress?: { total: number; current?: number };
|
|
49
50
|
plannedKeys?: Set<string>;
|
|
51
|
+
checklist?: WorkflowChecklistProjection;
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
type WorkflowWidgetProjectionLookup = (job: AsyncJobState) => WorkflowWidgetProjection;
|
|
@@ -442,6 +444,17 @@ function buildWorkflowWidgetProjection(job: AsyncJobState): WorkflowWidgetProjec
|
|
|
442
444
|
const projection: WorkflowWidgetProjection = { stages, steps };
|
|
443
445
|
if (stageProgress) projection.stageProgress = stageProgress;
|
|
444
446
|
if (stages.length) projection.plannedKeys = new Set(stages.map((node) => node.id));
|
|
447
|
+
if (job.mode === "workflow") {
|
|
448
|
+
const checklist = projectWorkflowChecklist({
|
|
449
|
+
graph: job.workflowGraph,
|
|
450
|
+
steps: job.steps,
|
|
451
|
+
hostSteps: job.hostSteps,
|
|
452
|
+
preflight: job.preflight,
|
|
453
|
+
trace: job.workflow?.trace,
|
|
454
|
+
now: job.updatedAt ?? Date.now(),
|
|
455
|
+
});
|
|
456
|
+
if (checklist.total > 0) projection.checklist = checklist;
|
|
457
|
+
}
|
|
445
458
|
return projection;
|
|
446
459
|
}
|
|
447
460
|
|
|
@@ -1059,6 +1072,15 @@ export function widgetRenderKey(job: AsyncJobState, expanded = false): string {
|
|
|
1059
1072
|
} : undefined,
|
|
1060
1073
|
preflight: expanded ? job.preflight : job.preflight ? formatWorkflowPreflightPlanSummary(job.preflight) : undefined,
|
|
1061
1074
|
preflightWarnings: expanded ? job.workflow?.preflightWarnings : job.workflow?.preflightWarnings?.length || undefined,
|
|
1075
|
+
checklist: projection.checklist ? {
|
|
1076
|
+
total: projection.checklist.total,
|
|
1077
|
+
done: projection.checklist.done,
|
|
1078
|
+
running: projection.checklist.running,
|
|
1079
|
+
queued: projection.checklist.queued,
|
|
1080
|
+
blocked: projection.checklist.blocked,
|
|
1081
|
+
failed: projection.checklist.failed,
|
|
1082
|
+
phases: projection.checklist.phases.map((phase) => [phase.key, phase.state, phase.done, phase.total, phase.running, phase.queued, phase.blocked, phase.failed, phase.paused, phase.stopped, phase.items.map((item) => [item.key, item.state, item.currentTool, item.currentPath, item.durationMs, item.toolCount, item.error])]),
|
|
1083
|
+
} : undefined,
|
|
1062
1084
|
steps: job.steps?.map((step, index) => widgetStepRenderKey(step, index, expanded)),
|
|
1063
1085
|
nestedChildren: nestedRenderKey(job.nestedChildren, expanded),
|
|
1064
1086
|
lane: laneRenderKey(job, projection),
|
|
@@ -1234,6 +1256,85 @@ function widgetStepStatus(status: AsyncJobStep["status"], theme: Theme): string
|
|
|
1234
1256
|
return theme.fg("dim", status);
|
|
1235
1257
|
}
|
|
1236
1258
|
|
|
1259
|
+
function workflowChecklistStateLabel(state: WorkflowChecklistState): string {
|
|
1260
|
+
if (state === "running") return "running";
|
|
1261
|
+
if (state === "complete") return "complete";
|
|
1262
|
+
return state;
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
function workflowChecklistGlyph(item: { state: WorkflowChecklistState; startedAt?: number; durationMs?: number; toolCount?: number; currentToolStartedAt?: number }, theme: Theme, frame?: number): string {
|
|
1266
|
+
if (item.state === "running") return theme.fg("accent", runningGlyph(animatedSeed(runningSeed(item.startedAt, item.durationMs, item.toolCount, item.currentToolStartedAt), frame)));
|
|
1267
|
+
if (item.state === "complete") return theme.fg("success", "✓");
|
|
1268
|
+
if (item.state === "blocked") return theme.fg("error", "!");
|
|
1269
|
+
if (item.state === "failed") return theme.fg("error", "✗");
|
|
1270
|
+
if (item.state === "paused" || item.state === "stopped") return theme.fg("warning", "■");
|
|
1271
|
+
return theme.fg("muted", "◦");
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
function workflowChecklistItemPriority(state: WorkflowChecklistState): number {
|
|
1275
|
+
if (state === "blocked") return 0;
|
|
1276
|
+
if (state === "failed") return 1;
|
|
1277
|
+
if (state === "running") return 2;
|
|
1278
|
+
if (state === "paused") return 3;
|
|
1279
|
+
if (state === "stopped") return 4;
|
|
1280
|
+
if (state === "queued") return 5;
|
|
1281
|
+
return 6;
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
function workflowChecklistItemLine(item: WorkflowChecklistItem, theme: Theme, indent: string, frame?: number): string {
|
|
1285
|
+
const identity = [item.label, item.agent && item.agent !== item.label ? item.agent : undefined].filter(Boolean).join(" · ");
|
|
1286
|
+
const context = contextModeBadge(theme, item.context);
|
|
1287
|
+
const state = item.state === "complete" ? "" : ` ${theme.fg("dim", `· ${workflowChecklistStateLabel(item.state)}`)}`;
|
|
1288
|
+
const details = [
|
|
1289
|
+
item.currentTool ? `${item.currentTool}${item.durationMs !== undefined ? ` ${formatDuration(item.durationMs)}` : ""}` : undefined,
|
|
1290
|
+
!item.currentTool && item.currentPath ? shortenPath(item.currentPath) : undefined,
|
|
1291
|
+
!item.currentTool && item.durationMs !== undefined ? formatDuration(item.durationMs) : undefined,
|
|
1292
|
+
item.toolCount !== undefined ? `${item.toolCount} tools` : undefined,
|
|
1293
|
+
item.outputName ? `out:${item.outputName}` : undefined,
|
|
1294
|
+
item.error ? `error:${oneLine(item.error)}` : undefined,
|
|
1295
|
+
].filter(Boolean).join(" · ");
|
|
1296
|
+
return `${indent}${workflowChecklistGlyph(item, theme, frame)} ${theme.bold(identity || item.key)}${context}${state}${details ? ` ${theme.fg("dim", `· ${details}`)}` : ""}`;
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
const COLLAPSED_WORKFLOW_PHASE_LIMIT = 4;
|
|
1300
|
+
|
|
1301
|
+
function workflowChecklistWidgetLines(checklist: WorkflowChecklistProjection | undefined, theme: Theme, indent: string, expanded: boolean, frame?: number, includeSummary = true, limitPhases = !expanded, includeBottleneckOutput = expanded): string[] {
|
|
1302
|
+
if (!checklist?.total) return [];
|
|
1303
|
+
const lines = includeSummary ? [`${indent}${theme.fg("dim", `Checklist ${formatWorkflowChecklistSummary(checklist)}`)}`] : [];
|
|
1304
|
+
const phases = !limitPhases || checklist.phases.length <= COLLAPSED_WORKFLOW_PHASE_LIMIT
|
|
1305
|
+
? checklist.phases
|
|
1306
|
+
: checklist.phases
|
|
1307
|
+
.map((phase, index) => ({ phase, index }))
|
|
1308
|
+
.sort((left, right) => Number(left.phase.state !== "running") - Number(right.phase.state !== "running")
|
|
1309
|
+
|| Number(left.phase.state === "complete") - Number(right.phase.state === "complete")
|
|
1310
|
+
|| left.index - right.index)
|
|
1311
|
+
.slice(0, COLLAPSED_WORKFLOW_PHASE_LIMIT)
|
|
1312
|
+
.sort((left, right) => left.index - right.index)
|
|
1313
|
+
.map(({ phase }) => phase);
|
|
1314
|
+
for (const phase of phases) {
|
|
1315
|
+
const phaseItem = phase.items.find((item) => item.state === "running") ?? phase.items[0];
|
|
1316
|
+
const glyph = workflowChecklistGlyph({
|
|
1317
|
+
state: phase.state,
|
|
1318
|
+
startedAt: phaseItem?.startedAt,
|
|
1319
|
+
durationMs: phaseItem?.durationMs,
|
|
1320
|
+
toolCount: phaseItem?.toolCount,
|
|
1321
|
+
currentToolStartedAt: phaseItem?.currentToolStartedAt,
|
|
1322
|
+
}, theme, frame);
|
|
1323
|
+
lines.push(`${indent}${glyph} ${theme.bold(formatWorkflowChecklistPhase(phase))}`);
|
|
1324
|
+
if (expanded) {
|
|
1325
|
+
for (const item of [...phase.items].sort((left, right) => workflowChecklistItemPriority(left.state) - workflowChecklistItemPriority(right.state))) {
|
|
1326
|
+
lines.push(workflowChecklistItemLine(item, theme, `${indent} `, frame));
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
const bottleneck = formatWorkflowChecklistBottleneck(checklist.bottleneck, { includeOutput: includeBottleneckOutput });
|
|
1331
|
+
if (bottleneck) {
|
|
1332
|
+
const tone = checklist.bottleneck?.state === "blocked" || checklist.bottleneck?.state === "failed" ? "error" : checklist.bottleneck?.state === "running" ? "accent" : "warning";
|
|
1333
|
+
lines.push(`${indent}${theme.fg(tone, `bottleneck · ${bottleneck}`)}`);
|
|
1334
|
+
}
|
|
1335
|
+
return lines;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1237
1338
|
function widgetStepActivity(step: NonNullable<AsyncJobState["steps"]>[number], snapshotNow?: number): string {
|
|
1238
1339
|
const facts: string[] = [];
|
|
1239
1340
|
if (step.currentTool && step.currentToolStartedAt !== undefined && snapshotNow !== undefined) facts.push(`${step.currentTool} ${formatDuration(Math.max(0, snapshotNow - step.currentToolStartedAt))}`);
|
|
@@ -1725,12 +1826,15 @@ function buildForegroundResultEntries(
|
|
|
1725
1826
|
return label.itemTitle === "Agent" ? withDuplicateForegroundLabels(entries, label.totalCount) : entries;
|
|
1726
1827
|
}
|
|
1727
1828
|
|
|
1728
|
-
function widgetStats(job: AsyncJobState, theme: Theme, projection = buildWorkflowWidgetProjection(job)): string {
|
|
1829
|
+
function widgetStats(job: AsyncJobState, theme: Theme, projection = buildWorkflowWidgetProjection(job), collapsed = false): string {
|
|
1729
1830
|
const parts: string[] = [];
|
|
1730
1831
|
const { stageProgress } = projection;
|
|
1731
1832
|
const stepsTotal = stageProgress?.total ?? job.stepsTotal ?? (job.agents?.length ?? 1);
|
|
1732
1833
|
const isSingleChild = isSingleChildAsyncJob(job);
|
|
1733
|
-
|
|
1834
|
+
const checklistPrimary = collapsed && job.mode === "workflow" && projection.checklist !== undefined;
|
|
1835
|
+
if (checklistPrimary) {
|
|
1836
|
+
parts.push(formatWorkflowChecklistSummary(projection.checklist!));
|
|
1837
|
+
} else if (stageProgress) {
|
|
1734
1838
|
const currentStage = stageProgress.current !== undefined ? projection.stages[stageProgress.current] : undefined;
|
|
1735
1839
|
const focus = currentStage
|
|
1736
1840
|
? [compactTaskText(undefined, currentStage.label) ?? boundedLaneValue(currentStage.id), currentStage.agent ? boundedLaneValue(currentStage.agent) : "", workflowNodeStatusLabel(currentStage.status)].filter(Boolean)
|
|
@@ -1762,6 +1866,7 @@ function widgetStats(job: AsyncJobState, theme: Theme, projection = buildWorkflo
|
|
|
1762
1866
|
} else if (stepsTotal > 1) {
|
|
1763
1867
|
parts.push(`steps ${stepsTotal}`);
|
|
1764
1868
|
}
|
|
1869
|
+
if (projection.checklist && !checklistPrimary) parts.push(formatWorkflowChecklistSummary(projection.checklist));
|
|
1765
1870
|
if (job.toolCount !== undefined) parts.push(formatToolUseStat(job.toolCount));
|
|
1766
1871
|
if (job.totalTokens?.total) parts.push(formatTokenUsage(job.totalTokens, "token"));
|
|
1767
1872
|
if (job.startedAt !== undefined && job.updatedAt !== undefined) parts.push(formatDuration(Math.max(0, job.updatedAt - job.startedAt)));
|
|
@@ -2044,10 +2149,20 @@ function hostStepWidgetLines(job: AsyncJobState, theme: Theme, indent: string):
|
|
|
2044
2149
|
|
|
2045
2150
|
function foregroundStyleWidgetDetails(job: AsyncJobState, theme: Theme, expanded: boolean, width: number, frame?: number, projection = buildWorkflowWidgetProjection(job)): string[] {
|
|
2046
2151
|
const { steps } = projection;
|
|
2152
|
+
const checklistPrimary = !expanded && job.mode === "workflow" && projection.checklist !== undefined;
|
|
2153
|
+
if (checklistPrimary) {
|
|
2154
|
+
const lines = [
|
|
2155
|
+
...workflowPreflightLines(job, false),
|
|
2156
|
+
...workflowChecklistWidgetLines(projection.checklist, theme, " ", false, frame, false),
|
|
2157
|
+
];
|
|
2158
|
+
if (job.status === "running" || projection.checklist!.running > 0) lines.push(` ${theme.fg("accent", liveDetailHintText())}`);
|
|
2159
|
+
return lines;
|
|
2160
|
+
}
|
|
2047
2161
|
if (!steps.length) {
|
|
2048
2162
|
const lane = projectAsyncLane(job, laneStepForJob(job, steps));
|
|
2049
2163
|
return [
|
|
2050
2164
|
...workflowPreflightLines(job, expanded),
|
|
2165
|
+
...workflowChecklistWidgetLines(projection.checklist, theme, " ", expanded, frame),
|
|
2051
2166
|
...(lane ? formatLaneProjectionLines(lane, theme, " ") : []),
|
|
2052
2167
|
...hostStepWidgetLines(job, theme, " "),
|
|
2053
2168
|
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
@@ -2055,7 +2170,10 @@ function foregroundStyleWidgetDetails(job: AsyncJobState, theme: Theme, expanded
|
|
|
2055
2170
|
];
|
|
2056
2171
|
}
|
|
2057
2172
|
if (job.mode === "chain" && !job.activeParallelGroup && job.parallelGroups?.length) return widgetChainDetails(job, theme, expanded, width, frame);
|
|
2058
|
-
const lines: string[] =
|
|
2173
|
+
const lines: string[] = [
|
|
2174
|
+
...workflowPreflightLines(job, expanded),
|
|
2175
|
+
...workflowChecklistWidgetLines(projection.checklist, theme, " ", expanded, frame),
|
|
2176
|
+
];
|
|
2059
2177
|
const group = activeParallelWidgetGroup(job);
|
|
2060
2178
|
if (group) {
|
|
2061
2179
|
lines.push(...parallelWidgetGroupDetails(job, theme, group, expanded, width, frame, Boolean(job.activeParallelGroup)));
|
|
@@ -2087,13 +2205,16 @@ function foregroundStyleWidgetDetails(job: AsyncJobState, theme: Theme, expanded
|
|
|
2087
2205
|
}
|
|
2088
2206
|
|
|
2089
2207
|
function buildSingleWidgetLines(job: AsyncJobState, theme: Theme, width: number, expanded: boolean, frame?: number, projection = buildWorkflowWidgetProjection(job)): string[] {
|
|
2090
|
-
const stats = widgetStats(job, theme, projection);
|
|
2091
|
-
const count = job.mode === "
|
|
2208
|
+
const stats = widgetStats(job, theme, projection, !expanded);
|
|
2209
|
+
const count = job.mode === "workflow"
|
|
2210
|
+
? projection.checklist?.total ?? projection.stageProgress?.total ?? job.stepsTotal ?? job.agents?.length ?? job.steps?.length
|
|
2211
|
+
: job.mode === "chain" ? job.chainStepCount : projection.stageProgress?.total ?? job.stepsTotal ?? job.agents?.length ?? job.steps?.length;
|
|
2092
2212
|
const mode = widgetJobName(job);
|
|
2093
2213
|
const title = isSingleChildAsyncJob(job)
|
|
2094
2214
|
? "async subagent"
|
|
2095
2215
|
: `async subagent ${mode}${count && count > 1 ? ` (${count})` : ""}`;
|
|
2096
|
-
const
|
|
2216
|
+
const checklistPrimary = !expanded && job.mode === "workflow" && projection.checklist !== undefined;
|
|
2217
|
+
const collapseDetails = !checklistPrimary && job.steps?.length === 1 && shouldCollapseSingleChildDetails(job, job.steps[0]!);
|
|
2097
2218
|
const summary = `${widgetStatusGlyph(job, theme, frame)} ${themeBold(theme, mode)}${contextModeBadge(theme, job.context)}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`;
|
|
2098
2219
|
return [
|
|
2099
2220
|
`${theme.fg("toolTitle", themeBold(theme, title))} ${theme.fg("dim", "· background")}`,
|
|
@@ -2260,10 +2381,11 @@ function progressiveHeaderLine(jobs: AsyncJobState[], theme: Theme, width: numbe
|
|
|
2260
2381
|
}
|
|
2261
2382
|
|
|
2262
2383
|
function progressiveJobLine(job: AsyncJobState, theme: Theme, width: number, frame?: number, projection = buildWorkflowWidgetProjection(job)): string {
|
|
2263
|
-
const
|
|
2264
|
-
const
|
|
2384
|
+
const checklistPrimary = job.mode === "workflow" && projection.checklist !== undefined;
|
|
2385
|
+
const stats = widgetStats(job, theme, projection, true);
|
|
2386
|
+
const activity = checklistPrimary ? "" : widgetActivity(job);
|
|
2265
2387
|
const status = job.status === "complete" ? "done" : job.status;
|
|
2266
|
-
const lane = projectAsyncLane(job, laneStepForJob(job, projection.steps));
|
|
2388
|
+
const lane = checklistPrimary ? undefined : projectAsyncLane(job, laneStepForJob(job, projection.steps));
|
|
2267
2389
|
const laneSummary = lane
|
|
2268
2390
|
? [lane.label ?? lane.role, lane.phase ? `phase:${lane.phase}` : undefined, lane.output ? `out:${lane.output}` : undefined, lane.workspace ? `workspace:${lane.workspace}` : `ref:${lane.ref}`].filter(Boolean).join(" · ")
|
|
2269
2391
|
: "";
|
|
@@ -2430,12 +2552,22 @@ function buildWidgetLinesWithProjection(jobs: AsyncJobState[], theme: Theme, wid
|
|
|
2430
2552
|
let slots = MAX_WIDGET_JOBS;
|
|
2431
2553
|
const appendJob = (job: AsyncJobState): void => {
|
|
2432
2554
|
const projection = projectionFor(job);
|
|
2433
|
-
const
|
|
2555
|
+
const checklistPrimary = !expanded && job.mode === "workflow" && projection.checklist !== undefined;
|
|
2556
|
+
const stats = widgetStats(job, theme, projection, !expanded);
|
|
2557
|
+
const details = checklistPrimary
|
|
2558
|
+
? [
|
|
2559
|
+
...workflowChecklistWidgetLines(projection.checklist, theme, " ", false, frame, false),
|
|
2560
|
+
...(job.status === "running" || projection.checklist!.running > 0 ? [` ${theme.fg("accent", liveDetailHintText())}`] : []),
|
|
2561
|
+
]
|
|
2562
|
+
: [
|
|
2563
|
+
` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
|
|
2564
|
+
...widgetLaneDetailLines(job, theme, projection),
|
|
2565
|
+
...workflowChecklistWidgetLines(projection.checklist, theme, " ", expanded, frame),
|
|
2566
|
+
...widgetParallelAgentDetails(job, theme, expanded, width, frame),
|
|
2567
|
+
];
|
|
2434
2568
|
items.push([
|
|
2435
2569
|
`${widgetStatusGlyph(job, theme, frame)} ${themeBold(theme, widgetJobName(job))}${contextModeBadge(theme, job.context)}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`,
|
|
2436
|
-
|
|
2437
|
-
...widgetLaneDetailLines(job, theme, projection),
|
|
2438
|
-
...widgetParallelAgentDetails(job, theme, expanded, width, frame),
|
|
2570
|
+
...details,
|
|
2439
2571
|
]);
|
|
2440
2572
|
};
|
|
2441
2573
|
|
|
@@ -2580,6 +2712,62 @@ function workflowOverallState(rows: WorkflowChatProgressRow[], hasTerminalValue:
|
|
|
2580
2712
|
return "running";
|
|
2581
2713
|
}
|
|
2582
2714
|
|
|
2715
|
+
function foregroundWorkflowChecklist(details: Details): WorkflowChecklistProjection | undefined {
|
|
2716
|
+
if (details.mode !== "workflow") return undefined;
|
|
2717
|
+
const stageNodes = details.workflowGraph ? workflowGraphStageNodes(details.workflowGraph) : [];
|
|
2718
|
+
const steps: WorkflowChecklistStep[] = details.results.flatMap((result, resultIndex) => {
|
|
2719
|
+
const stableIndex = foregroundResultIndex(details, resultIndex);
|
|
2720
|
+
const workflowKey = (result as { workflowKey?: unknown }).workflowKey;
|
|
2721
|
+
if (details.workflowGraph && typeof workflowKey !== "string") return [];
|
|
2722
|
+
const node = typeof workflowKey === "string"
|
|
2723
|
+
? stageNodes.find((candidate) => candidate.id === workflowKey)
|
|
2724
|
+
: undefined;
|
|
2725
|
+
const progress = foregroundProgressForResult(details, resultIndex);
|
|
2726
|
+
const status = isResultRunning(result) || progress?.status === "running"
|
|
2727
|
+
? "running"
|
|
2728
|
+
: result.detached
|
|
2729
|
+
? "paused"
|
|
2730
|
+
: result.stopped
|
|
2731
|
+
? "stopped"
|
|
2732
|
+
: result.exitCode === 0
|
|
2733
|
+
? "complete"
|
|
2734
|
+
: "failed";
|
|
2735
|
+
return [{
|
|
2736
|
+
key: node?.id ?? (typeof workflowKey === "string" ? workflowKey : `result-${resultIndex + 1}`),
|
|
2737
|
+
label: node?.label ?? workflowLabelForResult(details, resultIndex) ?? result.task ?? result.agent,
|
|
2738
|
+
phase: node?.phase,
|
|
2739
|
+
agent: result.agent,
|
|
2740
|
+
status,
|
|
2741
|
+
context: result.context,
|
|
2742
|
+
activityState: progress?.activityState,
|
|
2743
|
+
startedAt: progress?.lastActivityAt !== undefined && progress.durationMs !== undefined ? progress.lastActivityAt - progress.durationMs : undefined,
|
|
2744
|
+
durationMs: progress?.durationMs,
|
|
2745
|
+
currentTool: progress?.currentTool,
|
|
2746
|
+
currentToolStartedAt: progress?.currentToolStartedAt,
|
|
2747
|
+
currentPath: progress?.currentPath,
|
|
2748
|
+
turnCount: progress?.turnCount,
|
|
2749
|
+
toolCount: progress?.toolCount ?? result.progressSummary?.toolCount,
|
|
2750
|
+
error: result.error,
|
|
2751
|
+
toolBudgetBlocked: result.toolBudgetBlocked,
|
|
2752
|
+
turnBudgetExceeded: result.turnBudgetExceeded,
|
|
2753
|
+
timedOut: result.timedOut,
|
|
2754
|
+
stopped: result.stopped,
|
|
2755
|
+
acceptance: result.acceptance ? { status: result.acceptance.status, reviewResult: result.acceptance.reviewResult ? { status: result.acceptance.reviewResult.status } : undefined } : undefined,
|
|
2756
|
+
review: result.review ? { status: result.review.status } : undefined,
|
|
2757
|
+
watchdog: result.watchdog,
|
|
2758
|
+
}];
|
|
2759
|
+
});
|
|
2760
|
+
const checklist = projectWorkflowChecklist({
|
|
2761
|
+
graph: details.workflowGraph,
|
|
2762
|
+
steps,
|
|
2763
|
+
hostSteps: details.workflow?.receipt?.hostSteps,
|
|
2764
|
+
preflight: details.preflight,
|
|
2765
|
+
trace: details.workflow?.trace,
|
|
2766
|
+
now: Date.now(),
|
|
2767
|
+
});
|
|
2768
|
+
return checklist.total > 0 ? checklist : undefined;
|
|
2769
|
+
}
|
|
2770
|
+
|
|
2583
2771
|
function renderWorkflowChatProgress(d: Details, result: AgentToolResult<Details>, theme: Theme, layout: MainWindowRenderLayout, frame?: number, expanded = false): Component {
|
|
2584
2772
|
const workflow = d.workflow;
|
|
2585
2773
|
const rows = workflow ? buildWorkflowChatProgressRows(workflow.trace, d.preflight) : d.preflight ? buildWorkflowChatProgressRows([], d.preflight) : [];
|
|
@@ -2595,6 +2783,10 @@ function renderWorkflowChatProgress(d: Details, result: AgentToolResult<Details>
|
|
|
2595
2783
|
c.addChild(new Text(truncLine(theme.fg("dim", `${rowIndent}Repo ${repoLabel}`), width), 0, 0));
|
|
2596
2784
|
if (d.preflight) c.addChild(new Text(truncLine(theme.fg("dim", formatWorkflowPreflightPlanSummary(d.preflight, { indent: rowIndent })), width), 0, 0));
|
|
2597
2785
|
if (phase) c.addChild(new Text(truncLine(theme.fg("dim", `${rowIndent}Phase ${phase}`), width), 0, 0));
|
|
2786
|
+
const checklist = projectWorkflowChecklist({ hostSteps: workflow?.receipt?.hostSteps, preflight: d.preflight, trace: workflow?.trace, now: Date.now() });
|
|
2787
|
+
for (const line of workflowChecklistWidgetLines(checklist, theme, rowIndent, false, frame, true, !expanded, expanded)) {
|
|
2788
|
+
c.addChild(new Text(truncLine(line, width), 0, 0));
|
|
2789
|
+
}
|
|
2598
2790
|
if (rows.length === 0) {
|
|
2599
2791
|
c.addChild(new Text(truncLine(theme.fg("dim", `${rowIndent}◦ waiting for workflow child launches`), width), 0, 0));
|
|
2600
2792
|
return c;
|
|
@@ -2653,7 +2845,9 @@ function renderMultiCompact(d: Details, theme: Theme, layout: MainWindowRenderLa
|
|
|
2653
2845
|
}
|
|
2654
2846
|
const multiLabel = buildMultiProgressLabel(d, hasRunning);
|
|
2655
2847
|
const itemTitle = multiLabel.itemTitle;
|
|
2656
|
-
const
|
|
2848
|
+
const workflowChecklist = foregroundWorkflowChecklist(d);
|
|
2849
|
+
const checklistPrimary = d.mode === "workflow" && workflowChecklist !== undefined;
|
|
2850
|
+
const stats = statJoin(theme, [checklistPrimary ? formatWorkflowChecklistSummary(workflowChecklist) : multiLabel.headerLabel, formatProgressStats(theme, totalSummary), formatTotalCostStat(d.totalCost)]);
|
|
2657
2851
|
const aggregatePresentation = semanticResultPresentation({
|
|
2658
2852
|
running: hasRunning,
|
|
2659
2853
|
detached,
|
|
@@ -2671,6 +2865,16 @@ function renderMultiCompact(d: Details, theme: Theme, layout: MainWindowRenderLa
|
|
|
2671
2865
|
const rowIndent = mainWindowIndent(layout, 1);
|
|
2672
2866
|
const detailIndent = mainWindowIndent(layout, 2);
|
|
2673
2867
|
c.addChild(new Text(truncLine(`${glyph} ${theme.fg("toolTitle", theme.bold(d.mode))}${contextBadge}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`, width), 0, 0));
|
|
2868
|
+
if (checklistPrimary) {
|
|
2869
|
+
for (const line of workflowChecklistWidgetLines(workflowChecklist, theme, rowIndent, false, frame, false)) {
|
|
2870
|
+
c.addChild(new Text(truncLine(line, width), 0, 0));
|
|
2871
|
+
}
|
|
2872
|
+
if (hasRunning || workflowChecklist.running > 0) c.addChild(new Text(truncLine(theme.fg("accent", `${rowIndent}${liveDetailHintText()}`), width), 0, 0));
|
|
2873
|
+
return c;
|
|
2874
|
+
}
|
|
2875
|
+
for (const line of workflowChecklistWidgetLines(workflowChecklist, theme, rowIndent, false, frame)) {
|
|
2876
|
+
c.addChild(new Text(truncLine(line, width), 0, 0));
|
|
2877
|
+
}
|
|
2674
2878
|
|
|
2675
2879
|
const useResultsDirectly = multiLabel.hasParallelInChain || !d.chainAgents?.length;
|
|
2676
2880
|
const displayStart = multiLabel.showActiveGroupOnly ? multiLabel.groupStartIndex : 0;
|
|
@@ -3012,6 +3216,10 @@ export function renderSubagentResult(
|
|
|
3012
3216
|
if (chainVis) {
|
|
3013
3217
|
c.addChild(new Text(fit(` ${chainVis}`), 0, 0));
|
|
3014
3218
|
}
|
|
3219
|
+
const workflowChecklist = foregroundWorkflowChecklist(d);
|
|
3220
|
+
for (const line of workflowChecklistWidgetLines(workflowChecklist, theme, " ", true, frame)) {
|
|
3221
|
+
c.addChild(new Text(fit(line), 0, 0));
|
|
3222
|
+
}
|
|
3015
3223
|
|
|
3016
3224
|
const useResultsDirectly = multiLabel.hasParallelInChain || !d.chainAgents?.length;
|
|
3017
3225
|
const displayStart = multiLabel.showActiveGroupOnly ? multiLabel.groupStartIndex : 0;
|