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.
Files changed (36) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/docs/agents.md +5 -4
  3. package/docs/configuration.md +28 -2
  4. package/docs/models.md +5 -5
  5. package/docs/observability.md +4 -1
  6. package/docs/tool-reference.md +2 -2
  7. package/package.json +1 -1
  8. package/skills/pi-subagents/references/management-authoring-rpc.md +1 -1
  9. package/skills/pi-subagents/references/prompting-and-roles.md +3 -3
  10. package/src/agents/agent-management.ts +22 -4
  11. package/src/agents/agents.ts +107 -125
  12. package/src/api/shared-types.ts +3 -0
  13. package/src/extension/config.ts +20 -0
  14. package/src/inspectors/herdr/inspector-runner.ts +19 -13
  15. package/src/runs/background/active-async-capacity.ts +0 -1
  16. package/src/runs/background/async-execution.ts +53 -7
  17. package/src/runs/background/async-resume.ts +3 -0
  18. package/src/runs/background/async-status.ts +18 -2
  19. package/src/runs/background/notify.ts +13 -1
  20. package/src/runs/background/run-status.ts +22 -2
  21. package/src/runs/background/subagent-runner.ts +30 -2
  22. package/src/runs/background/wait-completions.ts +13 -0
  23. package/src/runs/foreground/subagent-executor.ts +35 -3
  24. package/src/runs/shared/acceptance.ts +15 -9
  25. package/src/runs/shared/lane-metadata.ts +24 -3
  26. package/src/runs/shared/parallel-handoff.ts +4 -0
  27. package/src/runs/shared/pi-args.ts +8 -2
  28. package/src/runs/shared/task-intent.ts +5 -2
  29. package/src/runs/shared/worktree.ts +467 -63
  30. package/src/shared/types.ts +29 -0
  31. package/src/shared/utils.ts +18 -7
  32. package/src/slash/subagents-admin.ts +24 -12
  33. package/src/tui/fleet-status.ts +61 -2
  34. package/src/tui/fleet.ts +12 -7
  35. package/src/tui/render.ts +222 -14
  36. 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
- if (stageProgress) {
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[] = workflowPreflightLines(job, expanded);
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 === "chain" ? job.chainStepCount : projection.stageProgress?.total ?? job.stepsTotal ?? job.agents?.length ?? job.steps?.length;
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 collapseDetails = job.steps?.length === 1 && shouldCollapseSingleChildDetails(job, job.steps[0]!);
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 stats = widgetStats(job, theme, projection);
2264
- const activity = widgetActivity(job);
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 stats = widgetStats(job, theme, projection);
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
- ` ${theme.fg("dim", `⎿ ${widgetActivity(job)}`)}`,
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 stats = statJoin(theme, [multiLabel.headerLabel, formatProgressStats(theme, totalSummary), formatTotalCostStat(d.totalCost)]);
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;