@ashx-j/lunr 0.2.20 → 0.2.21

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 (172) hide show
  1. package/CHANGELOG.md +13 -1
  2. package/dist/builtin-extensions/lunr-todos.d.ts +6 -5
  3. package/dist/builtin-extensions/lunr-todos.d.ts.map +1 -1
  4. package/dist/builtin-extensions/lunr-todos.js +9 -7
  5. package/dist/builtin-extensions/lunr-todos.js.map +1 -1
  6. package/dist/builtin-extensions/pi-intercom/broker/spawn.d.ts.map +1 -1
  7. package/dist/builtin-extensions/pi-intercom/broker/spawn.js +30 -21
  8. package/dist/builtin-extensions/pi-intercom/broker/spawn.js.map +1 -1
  9. package/dist/builtin-extensions/pi-intercom/index.d.ts.map +1 -1
  10. package/dist/builtin-extensions/pi-intercom/index.js +12 -3
  11. package/dist/builtin-extensions/pi-intercom/index.js.map +1 -1
  12. package/dist/builtin-extensions/pi-subagents/src/extension/fanout-child.d.ts.map +1 -1
  13. package/dist/builtin-extensions/pi-subagents/src/extension/fanout-child.js +2 -2
  14. package/dist/builtin-extensions/pi-subagents/src/extension/fanout-child.js.map +1 -1
  15. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts +7 -6
  16. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
  17. package/dist/builtin-extensions/pi-subagents/src/extension/index.js +63 -29
  18. package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
  19. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts.map +1 -1
  20. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js +2 -2
  21. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js.map +1 -1
  22. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts +3 -3
  23. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts.map +1 -1
  24. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js +3 -2
  25. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js.map +1 -1
  26. package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.d.ts +1 -0
  27. package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.d.ts.map +1 -1
  28. package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js +26 -7
  29. package/dist/builtin-extensions/pi-subagents/src/intercom/native-supervisor-channel.js.map +1 -1
  30. package/dist/builtin-extensions/pi-subagents/src/intercom/result-intercom.d.ts +1 -0
  31. package/dist/builtin-extensions/pi-subagents/src/intercom/result-intercom.d.ts.map +1 -1
  32. package/dist/builtin-extensions/pi-subagents/src/intercom/result-intercom.js +1 -1
  33. package/dist/builtin-extensions/pi-subagents/src/intercom/result-intercom.js.map +1 -1
  34. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.d.ts +2 -0
  35. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.d.ts.map +1 -1
  36. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js +4 -3
  37. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-execution.js.map +1 -1
  38. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.d.ts.map +1 -1
  39. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.js +2 -0
  40. package/dist/builtin-extensions/pi-subagents/src/runs/background/async-job-tracker.js.map +1 -1
  41. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts +6 -2
  42. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.d.ts.map +1 -1
  43. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js +23 -1
  44. package/dist/builtin-extensions/pi-subagents/src/runs/background/notify.js.map +1 -1
  45. package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.d.ts.map +1 -1
  46. package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js +1 -0
  47. package/dist/builtin-extensions/pi-subagents/src/runs/background/result-watcher.js.map +1 -1
  48. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts +100 -0
  49. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.d.ts.map +1 -1
  50. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js +46 -31
  51. package/dist/builtin-extensions/pi-subagents/src/runs/background/subagent-runner.js.map +1 -1
  52. package/dist/builtin-extensions/pi-subagents/src/runs/background/top-level-async.d.ts +15 -2
  53. package/dist/builtin-extensions/pi-subagents/src/runs/background/top-level-async.d.ts.map +1 -1
  54. package/dist/builtin-extensions/pi-subagents/src/runs/background/top-level-async.js +14 -4
  55. package/dist/builtin-extensions/pi-subagents/src/runs/background/top-level-async.js.map +1 -1
  56. package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.d.ts.map +1 -1
  57. package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.js +1 -1
  58. package/dist/builtin-extensions/pi-subagents/src/runs/background/wait-tool.js.map +1 -1
  59. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
  60. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +1 -1
  61. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
  62. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts +0 -1
  63. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  64. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +9 -9
  65. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
  66. package/dist/builtin-extensions/pi-subagents/src/runs/shared/completion-guard.d.ts +1 -1
  67. package/dist/builtin-extensions/pi-subagents/src/runs/shared/completion-guard.d.ts.map +1 -1
  68. package/dist/builtin-extensions/pi-subagents/src/runs/shared/completion-guard.js +1 -1
  69. package/dist/builtin-extensions/pi-subagents/src/runs/shared/completion-guard.js.map +1 -1
  70. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts +1 -0
  71. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.d.ts.map +1 -1
  72. package/dist/builtin-extensions/pi-subagents/src/runs/shared/parallel-utils.js.map +1 -1
  73. package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.d.ts +3 -0
  74. package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.d.ts.map +1 -1
  75. package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.js +2 -0
  76. package/dist/builtin-extensions/pi-subagents/src/runs/shared/pi-args.js.map +1 -1
  77. package/dist/builtin-extensions/pi-subagents/src/runs/shared/task-intent.d.ts +2 -2
  78. package/dist/builtin-extensions/pi-subagents/src/runs/shared/task-intent.d.ts.map +1 -1
  79. package/dist/builtin-extensions/pi-subagents/src/runs/shared/task-intent.js +5 -10
  80. package/dist/builtin-extensions/pi-subagents/src/runs/shared/task-intent.js.map +1 -1
  81. package/dist/builtin-extensions/pi-subagents/src/shared/async-guidance.d.ts +2 -0
  82. package/dist/builtin-extensions/pi-subagents/src/shared/async-guidance.d.ts.map +1 -0
  83. package/dist/builtin-extensions/pi-subagents/src/shared/async-guidance.js +3 -0
  84. package/dist/builtin-extensions/pi-subagents/src/shared/async-guidance.js.map +1 -0
  85. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +5 -0
  86. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
  87. package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
  88. package/dist/builtin-extensions/pi-subagents/src/slash/delegation-adapters.d.ts.map +1 -1
  89. package/dist/builtin-extensions/pi-subagents/src/slash/delegation-adapters.js.map +1 -1
  90. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts +7 -0
  91. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts.map +1 -1
  92. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js +11 -7
  93. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js.map +1 -1
  94. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts +4 -0
  95. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts.map +1 -1
  96. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js +22 -23
  97. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js.map +1 -1
  98. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +2 -0
  99. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
  100. package/dist/builtin-extensions/pi-subagents/src/tui/render.js +237 -374
  101. package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
  102. package/dist/core/customize.d.ts +2 -0
  103. package/dist/core/customize.d.ts.map +1 -1
  104. package/dist/core/customize.js +3 -0
  105. package/dist/core/customize.js.map +1 -1
  106. package/dist/core/settings-manager.d.ts +4 -0
  107. package/dist/core/settings-manager.d.ts.map +1 -1
  108. package/dist/core/settings-manager.js +11 -0
  109. package/dist/core/settings-manager.js.map +1 -1
  110. package/dist/core/subagent-spinner-setting.d.ts +6 -0
  111. package/dist/core/subagent-spinner-setting.d.ts.map +1 -0
  112. package/dist/core/subagent-spinner-setting.js +12 -0
  113. package/dist/core/subagent-spinner-setting.js.map +1 -0
  114. package/dist/core/subagent-spinner.d.ts +4 -0
  115. package/dist/core/subagent-spinner.d.ts.map +1 -0
  116. package/dist/core/subagent-spinner.js +6 -0
  117. package/dist/core/subagent-spinner.js.map +1 -0
  118. package/dist/modes/interactive/components/settings-selector.d.ts +4 -0
  119. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  120. package/dist/modes/interactive/components/settings-selector.js +73 -1
  121. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  122. package/dist/modes/interactive/extension-widget-order.d.ts +8 -0
  123. package/dist/modes/interactive/extension-widget-order.d.ts.map +1 -0
  124. package/dist/modes/interactive/extension-widget-order.js +37 -0
  125. package/dist/modes/interactive/extension-widget-order.js.map +1 -0
  126. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  127. package/dist/modes/interactive/interactive-mode.js +15 -22
  128. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  129. package/dist/node-runtime/{agent-session-services-75JKASCW.js → agent-session-services-C743TYQE.js} +4 -3
  130. package/dist/node-runtime/{builtin-extensions-X4JMM4ZS.js → builtin-extensions-JCOPAAB5.js} +2 -1
  131. package/dist/node-runtime/{chunk-QKFMBRAL.js → chunk-2BUAYZ7K.js} +94 -25
  132. package/dist/node-runtime/chunk-5ZH3ZFIZ.js +308 -0
  133. package/dist/node-runtime/{chunk-BBUVIEZB.js → chunk-A3KMVBZV.js} +10 -1
  134. package/dist/node-runtime/{chunk-K3646JTM.js → chunk-AWVD5IEY.js} +7 -307
  135. package/dist/node-runtime/{chunk-ELWEXVO5.js → chunk-GB2MGFLI.js} +11 -11
  136. package/dist/node-runtime/chunk-LISUTZS3.js +19 -0
  137. package/dist/node-runtime/{chunk-AL2FMKKM.js → chunk-PDNXVXMD.js} +12 -12
  138. package/dist/node-runtime/{chunk-46ECNOLF.js → chunk-SY2TFMLO.js} +57 -17
  139. package/dist/node-runtime/{chunk-HQCKFM2O.js → chunk-VGYZ3APQ.js} +3 -0
  140. package/dist/node-runtime/{chunk-4DJ6HEGH.js → chunk-VZR3NCM7.js} +3 -3
  141. package/dist/node-runtime/{chunk-AOFXZQKA.js → chunk-W3SILANP.js} +1 -1
  142. package/dist/node-runtime/{chunk-CBGB57XT.js → chunk-WLLDXJRT.js} +10 -0
  143. package/dist/node-runtime/{chunk-JYNNB7DU.js → chunk-XI77KVKA.js} +2 -2
  144. package/dist/node-runtime/{chunk-4CSFRBCN.js → chunk-Y5KOEMBH.js} +1 -1
  145. package/dist/node-runtime/{chunk-AROOY4BB.js → chunk-YMEAYRBE.js} +1 -1
  146. package/dist/node-runtime/{command-RBTRL55A.js → command-P4NYCJ2M.js} +1 -1
  147. package/dist/node-runtime/{customize-XEXL2DGQ.js → customize-LF5VST47.js} +1 -1
  148. package/dist/node-runtime/{gateway-L4ANRTVE.js → gateway-BM6NLOH4.js} +1 -1
  149. package/dist/node-runtime/index.js +13 -11
  150. package/dist/node-runtime/{interactive-mode-HPUNH7SV.js → interactive-mode-EVVBA64D.js} +9 -7
  151. package/dist/node-runtime/{lunr-cron-NNYTWTGF.js → lunr-cron-4VHP4E4B.js} +1 -1
  152. package/dist/node-runtime/main.js +11 -9
  153. package/dist/node-runtime/{package-manager-cli-7RKSZGEN.js → package-manager-cli-N37546V5.js} +5 -4
  154. package/dist/node-runtime/{pi-intercom-DDQMRNPJ.js → pi-intercom-NV2LW6X7.js} +34 -33
  155. package/dist/node-runtime/{pi-subagents-T5WMMLBB.js → pi-subagents-6MUIFNDM.js} +286 -345
  156. package/dist/node-runtime/rpc-entry.js +11 -9
  157. package/dist/node-runtime/{runtime-bridges-NJQERQ72.js → runtime-bridges-2VQTLPOZ.js} +2 -2
  158. package/dist/node-runtime/{settings-manager-GHPTUN7R.js → settings-manager-CUGAZWAB.js} +2 -1
  159. package/dist/node-runtime/{subagent-executor-SRFC26SJ.js → subagent-executor-MH4A7CCF.js} +14 -46
  160. package/dist/node-runtime/{update-cli-L3JIBH2U.js → update-cli-XTZMZGOL.js} +2 -1
  161. package/docs/features.md +6 -2
  162. package/docs/settings.md +2 -1
  163. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  164. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  165. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  166. package/examples/extensions/gondolin/package-lock.json +2 -2
  167. package/examples/extensions/gondolin/package.json +1 -1
  168. package/examples/extensions/sandbox/package-lock.json +2 -2
  169. package/examples/extensions/sandbox/package.json +1 -1
  170. package/examples/extensions/with-deps/package-lock.json +2 -2
  171. package/examples/extensions/with-deps/package.json +1 -1
  172. package/package.json +4 -4
@@ -2,10 +2,11 @@
2
2
  /**
3
3
  * Rendering functions for subagent results
4
4
  */
5
+ import { getSubagentSpinnerDefinition } from "../../../../core/subagent-spinner.js";
5
6
  import { getMarkdownTheme } from "../../../../modes/interactive/theme/theme.js";
6
7
  import { keyText } from "../../../../modes/interactive/components/keybinding-hints.js";
7
8
  import { Container, Markdown, Spacer, Text, visibleWidth } from "@ashx-j/lunr-tui";
8
- import { MAX_WIDGET_JOBS, WIDGET_KEY, } from "../shared/types.js";
9
+ import { WIDGET_KEY, } from "../shared/types.js";
9
10
  import { formatTokens, formatUsage, formatDuration, formatModelSelection, formatCompactModelBadge, formatToolCall, shortenPath } from "../shared/formatters.js";
10
11
  import { sanitizeChildDescription } from "../shared/child-spec.js";
11
12
  import { getDisplayItems, getSingleResultOutput } from "../shared/utils.js";
@@ -98,8 +99,12 @@ function wrapPlainText(text, maxWidth) {
98
99
  }
99
100
  return lines;
100
101
  }
101
- const RUNNING_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
102
102
  const STATIC_RUNNING_GLYPH = "●";
103
+ export const SUBAGENT_PAINT_INTERVAL_MS = 80;
104
+ function selectedSubagentSpinner() {
105
+ const bridge = globalThis[Symbol.for("@lunr/customize")];
106
+ return getSubagentSpinnerDefinition(bridge?.getSubagentSpinner?.() ?? "braille");
107
+ }
103
108
  function runningSeed(...values) {
104
109
  let seed;
105
110
  for (const value of values) {
@@ -112,7 +117,12 @@ function runningSeed(...values) {
112
117
  function runningGlyph(seed) {
113
118
  if (seed === undefined)
114
119
  return STATIC_RUNNING_GLYPH;
115
- return RUNNING_FRAMES[Math.abs(seed) % RUNNING_FRAMES.length];
120
+ const frames = selectedSubagentSpinner().frames;
121
+ return frames[Math.abs(seed) % frames.length];
122
+ }
123
+ function foregroundSpinnerFrame(timerFrame) {
124
+ const { interval } = selectedSubagentSpinner();
125
+ return Math.floor((Math.max(0, timerFrame) * SUBAGENT_PAINT_INTERVAL_MS) / interval);
116
126
  }
117
127
  function progressRunningSeed(progress) {
118
128
  if (!progress)
@@ -324,6 +334,7 @@ export function formatCompactSubagentRow(theme, width, parts) {
324
334
  `${parts.prefix ?? ""}${parts.glyph} ${themeBold(theme, parts.description)}`,
325
335
  parts.modelBadge ? theme.fg("dim", parts.modelBadge) : "",
326
336
  theme.fg("dim", stats),
337
+ parts.terminalStatus ? theme.fg("dim", parts.terminalStatus) : "",
327
338
  ].filter(Boolean).join(` ${theme.fg("dim", "·")} `);
328
339
  return truncLine(body, width);
329
340
  }
@@ -417,14 +428,9 @@ function widgetStepsRunningSeed(steps) {
417
428
  function widgetJobRunningSeed(job) {
418
429
  return runningSeed(job.currentStep, job.runningSteps, widgetStepsRunningSeed(job.steps));
419
430
  }
420
- function widgetJobsRunningSeed(jobs) {
421
- let seed;
422
- for (const job of jobs)
423
- seed = runningSeed(seed, widgetJobRunningSeed(job));
424
- return seed;
425
- }
426
431
  function widgetAnimFrame(now = Date.now()) {
427
- return Math.floor(now / 80) % RUNNING_FRAMES.length;
432
+ const definition = selectedSubagentSpinner();
433
+ return Math.floor(now / definition.interval) % definition.frames.length;
428
434
  }
429
435
  function widgetStatusGlyph(job, theme, now = Date.now()) {
430
436
  if (job.status === "running")
@@ -605,7 +611,11 @@ function isDoneResult(result) {
605
611
  return result.exitCode === 0;
606
612
  }
607
613
  function workflowGraphHasStatus(details, statuses) {
608
- return details.workflowGraph?.nodes.some((node) => statuses.includes(node.status)) ?? false;
614
+ const matches = (nodes) => nodes.some((node) => statuses.includes(node.status) || matches(node.children ?? []));
615
+ return matches(details.workflowGraph?.nodes ?? []);
616
+ }
617
+ function resultAtFlatIndex(details, flatIndex) {
618
+ return details.results.find((result, arrayIndex) => (result.progress?.index ?? result.progressSummary?.index ?? arrayIndex) === flatIndex);
609
619
  }
610
620
  function buildChainRenderEntries(details, label) {
611
621
  if (details.mode !== "chain" || !label.hasParallelInChain || label.showActiveGroupOnly)
@@ -624,11 +634,20 @@ function buildChainRenderEntries(details, label) {
624
634
  continue;
625
635
  }
626
636
  for (let index = span.start; index < span.start + span.count; index++) {
637
+ const workflowStep = details.workflowGraph?.nodes.find((node) => node.stepIndex === span.stepIndex);
638
+ const workflowNode = workflowStep?.children?.find((node) => node.flatIndex === index)
639
+ ?? (workflowStep?.flatIndex === index ? workflowStep : undefined);
640
+ const result = resultAtFlatIndex(details, index);
627
641
  entries.push({
628
642
  kind: "result",
629
643
  resultIndex: index,
630
644
  rowNumber: index + 1,
631
- agentName: details.results[index]?.agent ?? details.chainAgents?.[span.stepIndex] ?? `step-${span.stepIndex + 1}`,
645
+ agentName: result?.agent
646
+ ?? workflowNode?.label
647
+ ?? details.chainAgents?.[span.stepIndex]
648
+ ?? `step-${span.stepIndex + 1}`,
649
+ status: workflowNode?.status ?? span.status,
650
+ error: workflowNode?.error ?? span.error,
632
651
  });
633
652
  }
634
653
  }
@@ -969,6 +988,17 @@ function foregroundStyleWidgetDetails(job, theme, expanded, width) {
969
988
  }
970
989
  return lines;
971
990
  }
991
+ function compactTerminalStatus(status) {
992
+ if (status === "failed")
993
+ return "failed";
994
+ if (status === "paused")
995
+ return "paused";
996
+ if (status === "stopped")
997
+ return "stopped";
998
+ if (status === "detached")
999
+ return "detached";
1000
+ return undefined;
1001
+ }
972
1002
  function compactJobProgress(job, step, now = Date.now()) {
973
1003
  const running = (step?.status ?? job.status) === "running";
974
1004
  const tokens = step?.tokens?.total ?? job.totalTokens?.total ?? 0;
@@ -990,18 +1020,6 @@ function compactJobLead(job, step) {
990
1020
  task: step?.description ?? step?.agent,
991
1021
  }) || widgetJobName(job);
992
1022
  }
993
- function activeJobStep(job) {
994
- const steps = job.steps ?? [];
995
- return steps.find((step) => step.status === "running")
996
- ?? steps.find((step) => step.status === "pending" || step.status === "queued")
997
- ?? steps[steps.length - 1];
998
- }
999
- function compactJobChildRows(job, theme, width, now = Date.now()) {
1000
- const steps = job.steps ?? [];
1001
- if (steps.length <= 1)
1002
- return compactJobLines(job, steps[0], theme, width, now);
1003
- return steps.flatMap((step) => compactJobLines(job, step, theme, width, now));
1004
- }
1005
1023
  function compactJobLines(job, step, theme, width, now = Date.now()) {
1006
1024
  const running = (step?.status ?? job.status) === "running";
1007
1025
  const lead = compactJobLead(job, step);
@@ -1016,17 +1034,9 @@ function compactJobLines(job, step, theme, width, now = Date.now()) {
1016
1034
  modelBadge,
1017
1035
  tokens: progress.tokens,
1018
1036
  durationMs: progress.durationMs,
1037
+ terminalStatus: compactTerminalStatus(step?.status ?? job.status),
1019
1038
  })];
1020
1039
  }
1021
- function buildSingleWidgetLines(job, theme, width, _expanded, now = Date.now()) {
1022
- const steps = job.steps ?? [];
1023
- if (steps.length <= 1)
1024
- return compactJobLines(job, steps[0], theme, width, now);
1025
- return steps.flatMap((step) => compactJobLines(job, step, theme, width, now));
1026
- }
1027
- function compactSingleWidgetLines(job, theme, width, now = Date.now()) {
1028
- return buildSingleWidgetLines(job, theme, width, false, now);
1029
- }
1030
1040
  const RESERVED_NON_WIDGET_ROWS = 19;
1031
1041
  let widgetLayoutSession;
1032
1042
  function resetWidgetLayoutSession() {
@@ -1047,37 +1057,6 @@ function widgetSessionMatches(expanded) {
1047
1057
  && widgetLayoutSession.rows === currentTerminalRows()
1048
1058
  && widgetLayoutSession.columns === currentTerminalColumns();
1049
1059
  }
1050
- function widgetHeaderCounts(jobs) {
1051
- return {
1052
- running: jobs.filter((job) => job.status === "running"),
1053
- queued: jobs.filter((job) => job.status === "queued"),
1054
- complete: jobs.filter((job) => job.status === "complete"),
1055
- failed: jobs.filter((job) => job.status === "failed"),
1056
- paused: jobs.filter((job) => job.status === "paused"),
1057
- stopped: jobs.filter((job) => job.status === "stopped"),
1058
- };
1059
- }
1060
- function buildSingleLineWidgetLines(jobs, theme, width) {
1061
- const counts = widgetHeaderCounts(jobs);
1062
- const hasActive = counts.running.length > 0 || counts.queued.length > 0;
1063
- const glyph = counts.running.length > 0
1064
- ? runningGlyph((widgetJobsRunningSeed(counts.running) ?? 0) + widgetAnimFrame())
1065
- : hasActive ? "●" : "○";
1066
- const parts = [];
1067
- if (counts.running.length > 0)
1068
- parts.push(`${counts.running.length}/${jobs.length} running`);
1069
- if (counts.queued.length > 0)
1070
- parts.push(`${counts.queued.length} queued`);
1071
- if (counts.failed.length > 0)
1072
- parts.push(`${counts.failed.length} failed`);
1073
- if (counts.stopped.length > 0)
1074
- parts.push(`${counts.stopped.length} stopped`);
1075
- if (counts.paused.length > 0)
1076
- parts.push(`${counts.paused.length} paused`);
1077
- if (!hasActive && counts.complete.length > 0)
1078
- parts.push(`${counts.complete.length}/${jobs.length} done`);
1079
- return [truncLine(`${theme.fg(hasActive ? "accent" : "dim", glyph)} ${theme.fg(hasActive ? "accent" : "dim", "subagents")} (${parts.join(", ") || `${jobs.length} total`})`, width)];
1080
- }
1081
1060
  function orderedWidgetJobs(jobs) {
1082
1061
  return [
1083
1062
  ...jobs.filter((job) => job.status === "running"),
@@ -1085,118 +1064,6 @@ function orderedWidgetJobs(jobs) {
1085
1064
  ...jobs.filter((job) => job.status !== "running" && job.status !== "queued"),
1086
1065
  ];
1087
1066
  }
1088
- function progressiveJobKey(job) {
1089
- return job.asyncId;
1090
- }
1091
- function isProgressiveActiveJob(job) {
1092
- return job?.status === "running" || job?.status === "queued";
1093
- }
1094
- function selectProgressiveJobKeys(jobs, previousKeys, bodyRows) {
1095
- if (bodyRows <= 0)
1096
- return [];
1097
- const jobsByKey = new Map(jobs.map((job) => [progressiveJobKey(job), job]));
1098
- const selected = [];
1099
- const append = (key) => {
1100
- if (selected.includes(key) || !jobsByKey.has(key))
1101
- return;
1102
- selected.push(key);
1103
- };
1104
- for (const key of previousKeys) {
1105
- if (!isProgressiveActiveJob(jobsByKey.get(key)))
1106
- continue;
1107
- append(key);
1108
- if (selected.length >= bodyRows)
1109
- return selected;
1110
- }
1111
- for (const job of orderedWidgetJobs(jobs)) {
1112
- if (!isProgressiveActiveJob(job))
1113
- continue;
1114
- const key = progressiveJobKey(job);
1115
- append(key);
1116
- if (selected.length >= bodyRows)
1117
- break;
1118
- }
1119
- if (selected.length >= bodyRows)
1120
- return selected;
1121
- for (const key of previousKeys) {
1122
- if (isProgressiveActiveJob(jobsByKey.get(key)))
1123
- continue;
1124
- append(key);
1125
- if (selected.length >= bodyRows)
1126
- return selected;
1127
- }
1128
- for (const job of orderedWidgetJobs(jobs)) {
1129
- const key = progressiveJobKey(job);
1130
- append(key);
1131
- if (selected.length >= bodyRows)
1132
- break;
1133
- }
1134
- return selected;
1135
- }
1136
- function progressiveHeaderLine(jobs, theme, width) {
1137
- const counts = widgetHeaderCounts(jobs);
1138
- const hasActive = counts.running.length > 0 || counts.queued.length > 0;
1139
- const glyph = counts.running.length > 0
1140
- ? runningGlyph((widgetJobsRunningSeed(counts.running) ?? 0) + widgetAnimFrame())
1141
- : hasActive ? "●" : "○";
1142
- const parts = [];
1143
- if (counts.running.length > 0)
1144
- parts.push(formatAgentRunningLabel(counts.running.length));
1145
- if (counts.queued.length > 0)
1146
- parts.push(`${counts.queued.length} queued`);
1147
- if (!hasActive) {
1148
- if (counts.failed.length > 0)
1149
- parts.push(`${counts.failed.length} failed`);
1150
- if (counts.stopped.length > 0)
1151
- parts.push(`${counts.stopped.length} stopped`);
1152
- if (counts.paused.length > 0)
1153
- parts.push(`${counts.paused.length} paused`);
1154
- if (counts.complete.length > 0)
1155
- parts.push(`${counts.complete.length}/${jobs.length} done`);
1156
- }
1157
- return truncLine(`${theme.fg(hasActive ? "accent" : "dim", glyph)} ${theme.fg(hasActive ? "accent" : "dim", "Async agents")} ${theme.fg("dim", "·")} ${theme.fg("dim", parts.join(", ") || `${jobs.length} total`)}`, width);
1158
- }
1159
- function progressiveJobLine(job, theme, width, now = Date.now()) {
1160
- const row = compactJobLines(job, activeJobStep(job), theme, Math.max(1, width - 2), now)[0] ?? "";
1161
- return row ? ` ${row}` : "";
1162
- }
1163
- function progressiveHiddenLine(hiddenJobs, theme, width) {
1164
- const counts = widgetHeaderCounts(hiddenJobs);
1165
- const parts = [];
1166
- if (counts.running.length > 0)
1167
- parts.push(`${counts.running.length} running`);
1168
- if (counts.queued.length > 0)
1169
- parts.push(`${counts.queued.length} queued`);
1170
- const finished = counts.complete.length + counts.failed.length + counts.paused.length + counts.stopped.length;
1171
- if (finished > 0)
1172
- parts.push(`${finished} finished`);
1173
- return truncLine(theme.fg("dim", ` +${hiddenJobs.length} more${parts.length ? ` (${parts.join(", ")})` : ""}`), width);
1174
- }
1175
- function buildProgressiveWidgetLines(jobs, theme, width, lockedRows, previousKeys) {
1176
- const rowCount = Math.max(1, lockedRows);
1177
- if (rowCount === 1)
1178
- return { lines: buildSingleLineWidgetLines(jobs, theme, width), visibleJobKeys: [] };
1179
- const bodyRows = rowCount - 1;
1180
- let visibleJobKeys = selectProgressiveJobKeys(jobs, previousKeys, bodyRows);
1181
- const jobsByKey = new Map(jobs.map((job) => [progressiveJobKey(job), job]));
1182
- let visibleJobs = visibleJobKeys.map((key) => jobsByKey.get(key)).filter((job) => Boolean(job));
1183
- let hiddenJobs = jobs.filter((job) => !visibleJobKeys.includes(progressiveJobKey(job)));
1184
- const needsHiddenLine = hiddenJobs.length > 0;
1185
- if (needsHiddenLine && visibleJobs.length >= bodyRows && bodyRows > 0) {
1186
- visibleJobs = visibleJobs.slice(0, bodyRows - 1);
1187
- visibleJobKeys = visibleJobs.map(progressiveJobKey);
1188
- hiddenJobs = jobs.filter((job) => !visibleJobKeys.includes(progressiveJobKey(job)));
1189
- }
1190
- const lines = [
1191
- progressiveHeaderLine(jobs, theme, width),
1192
- ...visibleJobs.map((job) => progressiveJobLine(job, theme, width)),
1193
- ];
1194
- if (hiddenJobs.length > 0 && lines.length < rowCount)
1195
- lines.push(progressiveHiddenLine(hiddenJobs, theme, width));
1196
- while (lines.length < rowCount)
1197
- lines.push(" ");
1198
- return { lines: lines.slice(0, rowCount), visibleJobKeys };
1199
- }
1200
1067
  function collapsedWidgetLineBudget(rows) {
1201
1068
  return Math.max(10, Math.min(14, Math.floor(rows * 0.35)));
1202
1069
  }
@@ -1214,98 +1081,70 @@ function fitWidgetLineBudget(lines, theme, width, expanded) {
1214
1081
  : `… ${hiddenCount} lines hidden · ${liveDetailKeyText()} expands`;
1215
1082
  return [...lines.slice(0, visibleLines), truncLine(theme.fg("dim", hint), width)];
1216
1083
  }
1217
- function fitAdaptiveWidgetLines(jobs, lines, theme, width, expanded) {
1084
+ function fitAdaptiveWidgetLines(_jobs, lines, theme, width, expanded) {
1218
1085
  if (expanded) {
1219
1086
  resetWidgetLayoutSession();
1220
1087
  return fitWidgetLineBudget(lines, theme, width, true);
1221
1088
  }
1222
- const hasMatchingSession = widgetSessionMatches(expanded);
1223
1089
  const rows = currentTerminalRows();
1224
1090
  const columns = currentTerminalColumns();
1225
1091
  const availableRows = estimateAvailableWidgetRows();
1226
- if (hasMatchingSession && widgetLayoutSession?.tier === "single-line") {
1227
- return buildSingleLineWidgetLines(jobs, theme, width);
1228
- }
1229
- if (hasMatchingSession && widgetLayoutSession?.tier === "progressive" && widgetLayoutSession.lockedRows !== undefined) {
1230
- const rendered = buildProgressiveWidgetLines(jobs, theme, width, widgetLayoutSession.lockedRows, widgetLayoutSession.visibleJobKeys);
1231
- widgetLayoutSession.visibleJobKeys = rendered.visibleJobKeys;
1232
- return rendered.lines;
1233
- }
1234
1092
  if (lines.length <= availableRows) {
1235
- widgetLayoutSession = { expanded, rows, columns, tier: "full", visibleJobKeys: [] };
1093
+ widgetLayoutSession = { expanded, rows, columns, tier: "full" };
1236
1094
  return fitWidgetLineBudget(lines, theme, width, false);
1237
1095
  }
1238
- if (availableRows <= 2) {
1239
- widgetLayoutSession = { expanded, rows, columns, tier: "single-line", visibleJobKeys: [] };
1240
- return buildSingleLineWidgetLines(jobs, theme, width);
1241
- }
1242
- const lockedRows = Math.min(availableRows, collapsedWidgetLineBudget(rows));
1243
- const rendered = buildProgressiveWidgetLines(jobs, theme, width, lockedRows, []);
1244
- widgetLayoutSession = { expanded, rows, columns, tier: "progressive", lockedRows, visibleJobKeys: rendered.visibleJobKeys };
1245
- return rendered.lines;
1246
- }
1247
- export function buildWidgetLines(jobs, theme, width = getTermWidth(), expanded = false, now = Date.now()) {
1248
- if (jobs.length === 0)
1249
- return [];
1250
- if (jobs.length === 1)
1251
- return buildSingleWidgetLines(jobs[0], theme, width, expanded, now);
1252
- const running = jobs.filter((job) => job.status === "running");
1253
- const queued = jobs.filter((job) => job.status === "queued");
1254
- const finished = jobs.filter((job) => job.status !== "running" && job.status !== "queued");
1096
+ const lockedRows = widgetSessionMatches(expanded) && widgetLayoutSession?.tier === "progressive"
1097
+ ? widgetLayoutSession.lockedRows ?? 1
1098
+ : Math.max(1, Math.min(availableRows, collapsedWidgetLineBudget(rows)));
1099
+ widgetLayoutSession = { expanded, rows, columns, tier: "progressive", lockedRows };
1100
+ if (lockedRows === 1)
1101
+ return lines.slice(0, 1);
1102
+ const visibleCount = Math.max(1, lockedRows - 1);
1103
+ const visible = lines.slice(0, visibleCount);
1104
+ const hidden = lines.length - visible.length;
1105
+ if (hidden > 0)
1106
+ visible.push(truncLine(theme.fg("dim", `+${hidden} more`), width));
1107
+ while (visible.length < lockedRows)
1108
+ visible.push(" ");
1109
+ return visible.slice(0, lockedRows);
1110
+ }
1111
+ function widgetExpandedDiagnostics(job, step, index, theme, width, now) {
1255
1112
  const lines = [];
1256
- const hasActive = running.length > 0 || queued.length > 0;
1257
- const headerGlyph = running.length > 0
1258
- ? runningGlyph((widgetJobsRunningSeed(running) ?? 0) + widgetAnimFrame(now))
1259
- : hasActive ? "●" : "○";
1260
- lines.push(truncLine(`${theme.fg(hasActive ? "accent" : "dim", headerGlyph)} ${theme.fg(hasActive ? "accent" : "dim", "Async agents")} ${theme.fg("dim", "· background")}`, width));
1261
- const items = [];
1262
- let hiddenRunning = 0;
1263
- let hiddenFinished = 0;
1264
- let queuedSummaryShown = false;
1265
- let slots = MAX_WIDGET_JOBS;
1266
- for (const job of running) {
1267
- if (slots <= 0) {
1268
- hiddenRunning++;
1269
- continue;
1270
- }
1271
- const row = compactJobChildRows(job, theme, Math.max(1, width - 3), now);
1272
- items.push(expanded ? [...row, ...widgetParallelAgentDetails(job, theme, true, width)] : row);
1273
- slots--;
1274
- }
1275
- if (queued.length > 0 && slots > 0) {
1276
- items.push([`${theme.fg("muted", "◦")} ${theme.fg("dim", `${queued.length} queued`)}`]);
1277
- queuedSummaryShown = true;
1278
- slots--;
1113
+ const status = step?.status ?? job.status;
1114
+ const runLabel = step ? `${job.asyncId}:${step.index ?? index}` : job.asyncId;
1115
+ lines.push(truncLine(theme.fg("dim", ` ⎿ run ${runLabel} · ${status}`), width));
1116
+ const error = step?.error;
1117
+ if (error)
1118
+ lines.push(truncLine(theme.fg("error", ` ${error}`), width));
1119
+ const snapshotNow = status === "running" ? now : step?.endedAt ?? job.updatedAt;
1120
+ const activity = step
1121
+ ? widgetStepActivityLine(step, Math.max(1, width - 4), true, snapshotNow)
1122
+ : widgetActivity(job, now);
1123
+ if (activity)
1124
+ lines.push(truncLine(theme.fg("dim", ` ${activity}`), width));
1125
+ for (const tool of step?.recentTools?.slice(-3) ?? []) {
1126
+ lines.push(truncLine(theme.fg("dim", ` ${tool.tool}${tool.args ? `: ${tool.args}` : ""}`), width));
1279
1127
  }
1280
- for (const job of finished) {
1281
- if (slots <= 0) {
1282
- hiddenFinished++;
1283
- continue;
1284
- }
1285
- const row = compactJobChildRows(job, theme, Math.max(1, width - 3), now);
1286
- items.push(expanded ? [...row, ...widgetParallelAgentDetails(job, theme, true, width)] : row);
1287
- slots--;
1128
+ for (const output of step?.recentOutput?.slice(-5) ?? []) {
1129
+ lines.push(truncLine(theme.fg("dim", ` ${output}`), width));
1288
1130
  }
1289
- const hiddenQueued = queued.length > 0 && !queuedSummaryShown ? queued.length : 0;
1290
- const hiddenTotal = hiddenRunning + hiddenFinished + hiddenQueued;
1291
- if (hiddenTotal > 0) {
1292
- const parts = [];
1293
- if (hiddenRunning > 0)
1294
- parts.push(`${hiddenRunning} running`);
1295
- if (hiddenQueued > 0)
1296
- parts.push(`${hiddenQueued} queued`);
1297
- if (hiddenFinished > 0)
1298
- parts.push(`${hiddenFinished} finished`);
1299
- items.push([theme.fg("dim", `+${hiddenTotal} more (${parts.join(", ")})`)]);
1131
+ for (const nestedLine of formatNestedWidgetLines(step?.children ?? job.nestedChildren, theme, width, true, snapshotNow)) {
1132
+ lines.push(truncLine(` ${nestedLine}`, width));
1300
1133
  }
1301
- for (let i = 0; i < items.length; i++) {
1302
- const item = items[i];
1303
- const last = i === items.length - 1;
1304
- const branch = last ? "└─" : "├─";
1305
- const continuation = last ? " " : "│ ";
1306
- lines.push(truncLine(`${theme.fg("dim", branch)} ${item[0]}`, width));
1307
- for (const detail of item.slice(1)) {
1308
- lines.push(truncLine(`${theme.fg("dim", continuation)} ${detail}`, width));
1134
+ return lines;
1135
+ }
1136
+ export function buildWidgetLines(jobs, theme, width = getTermWidth(), expanded = false, now = Date.now()) {
1137
+ const lines = [];
1138
+ for (const job of orderedWidgetJobs(jobs)) {
1139
+ const steps = job.steps?.length
1140
+ ? job.steps
1141
+ : job.agents?.length
1142
+ ? job.agents.map((agent, index) => ({ agent, description: agent, status: job.status, index }))
1143
+ : [undefined];
1144
+ for (const [index, step] of steps.entries()) {
1145
+ lines.push(...compactJobLines(job, step, theme, width, now));
1146
+ if (expanded)
1147
+ lines.push(...widgetExpandedDiagnostics(job, step, index, theme, width, now));
1309
1148
  }
1310
1149
  }
1311
1150
  return lines;
@@ -1366,7 +1205,7 @@ export class SubagentAsyncWidget {
1366
1205
  return;
1367
1206
  this.invalidate();
1368
1207
  this.requestRender?.();
1369
- }, 80);
1208
+ }, SUBAGENT_PAINT_INTERVAL_MS);
1370
1209
  this.timer.unref?.();
1371
1210
  }
1372
1211
  stopTimer() {
@@ -1393,11 +1232,7 @@ export class SubagentAsyncWidget {
1393
1232
  return this.cachedLines;
1394
1233
  const now = this.nowFn();
1395
1234
  const innerWidth = Math.max(1, width - 2);
1396
- const lines = this.expanded
1397
- ? buildWidgetLines(this.jobs, theme, innerWidth, true, now)
1398
- : this.jobs.length === 1
1399
- ? compactSingleWidgetLines(this.jobs[0], theme, innerWidth, now)
1400
- : buildWidgetLines(this.jobs, theme, innerWidth, false, now);
1235
+ const lines = buildWidgetLines(this.jobs, theme, innerWidth, this.expanded, now);
1401
1236
  const fitted = fitAdaptiveWidgetLines(this.jobs, lines, theme, innerWidth, this.expanded);
1402
1237
  const padded = fitted.map((line) => {
1403
1238
  const withPad = ` ${line}`;
@@ -1448,123 +1283,113 @@ export function renderWidget(ctx, jobs) {
1448
1283
  return widget;
1449
1284
  });
1450
1285
  }
1286
+ function compactResultTerminalStatus(result) {
1287
+ if (result.stopped)
1288
+ return "stopped";
1289
+ if (result.detached)
1290
+ return "detached";
1291
+ if (result.interrupted)
1292
+ return "paused";
1293
+ if (result.exitCode !== 0)
1294
+ return "failed";
1295
+ return undefined;
1296
+ }
1451
1297
  function renderSingleCompact(d, r, theme, frame) {
1452
1298
  const output = r.truncation?.text || getSingleResultOutput(r);
1453
1299
  const progress = r.progress || r.progressSummary;
1454
1300
  const isRunning = r.progress?.status === "running";
1455
- const c = new Container();
1456
1301
  const width = getTermWidth() - 4;
1457
1302
  const lead = compactRowLead({ description: r.description ?? r.agent, task: r.task });
1458
1303
  const modelBadge = formatCompactModelBadge(r.modelSelection ?? progress?.modelSelection, r.model ?? progress?.model);
1459
- c.addChild(animatedLine((f, now) => formatCompactSubagentRow(theme, width, {
1460
- glyph: resultGlyph(r, output, theme, isRunning, undefined, f),
1304
+ return animatedLine((animationTick, now) => formatCompactSubagentRow(theme, width, {
1305
+ glyph: resultGlyph(r, output, theme, isRunning, undefined, foregroundSpinnerFrame(animationTick)),
1461
1306
  description: lead,
1462
1307
  modelBadge,
1463
1308
  tokens: compactResultTokens(r),
1464
1309
  durationMs: liveDurationMs(progress, isRunning, now),
1465
- }), frame, isRunning));
1466
- if (!isRunning && (r.exitCode !== 0 || r.interrupted || r.detached || r.stopped)) {
1467
- c.addChild(new Text(truncLine(theme.fg(r.exitCode !== 0 ? "error" : "dim", ` ⎿ ${resultStatusLine(r, output)}`), width), 0, 0));
1310
+ terminalStatus: compactResultTerminalStatus(r),
1311
+ }), frame, isRunning);
1312
+ }
1313
+ function progressStatusGlyph(progress, theme, frame) {
1314
+ if (!progress || progress.status === "pending")
1315
+ return theme.fg("muted", "◦");
1316
+ if (progress.status === "running")
1317
+ return theme.fg("accent", runningGlyph((progressRunningSeed(progress) ?? 0) + (frame ?? 0)));
1318
+ if (progress.status === "completed")
1319
+ return theme.fg("success", "✓");
1320
+ if (progress.status === "failed")
1321
+ return theme.fg("error", "✗");
1322
+ return theme.fg("warning", "■");
1323
+ }
1324
+ function flattenCompactWorkflowNodes(nodes) {
1325
+ const rows = [];
1326
+ for (const node of nodes) {
1327
+ if (node.children?.length)
1328
+ rows.push(...flattenCompactWorkflowNodes(node.children));
1329
+ else
1330
+ rows.push(node);
1331
+ }
1332
+ return rows;
1333
+ }
1334
+ function compactForegroundEntries(d) {
1335
+ const workflowNodes = flattenCompactWorkflowNodes(d.workflowGraph?.nodes ?? []);
1336
+ if (workflowNodes.length === 0) {
1337
+ const progressCount = d.progress?.reduce((count, entry) => Math.max(count, entry.index + 1), 0) ?? 0;
1338
+ const chainCount = d.mode === "chain"
1339
+ ? buildChainStepSpans(d).reduce((count, span) => Math.max(count, span.start + span.count), 0)
1340
+ : 0;
1341
+ const count = Math.max(d.results.length, progressCount, d.chainChildren?.length ?? 0, d.chainAgents?.length ?? 0, chainCount);
1342
+ return Array.from({ length: count }, (_, flatIndex) => ({ flatIndex }));
1343
+ }
1344
+ const entries = workflowNodes.map((node) => ({ flatIndex: node.flatIndex, node }));
1345
+ const seen = new Set(entries.flatMap((entry) => entry.flatIndex === undefined ? [] : [entry.flatIndex]));
1346
+ const progressCount = d.progress?.reduce((count, entry) => Math.max(count, entry.index + 1), 0) ?? 0;
1347
+ const dataCount = Math.max(d.results.length, progressCount, d.chainChildren?.length ?? 0);
1348
+ for (let flatIndex = 0; flatIndex < dataCount; flatIndex++) {
1349
+ if (!seen.has(flatIndex))
1350
+ entries.push({ flatIndex });
1468
1351
  }
1469
- return c;
1352
+ return entries;
1470
1353
  }
1471
1354
  function renderMultiCompact(d, theme, frame) {
1472
- const hasRunning = d.progress?.some((p) => p.status === "running")
1473
- || d.results.some((r) => r.progress?.status === "running")
1474
- || workflowGraphHasStatus(d, ["running"]);
1475
- const stopped = d.results.some((r) => r.stopped && r.progress?.status !== "running")
1476
- || workflowGraphHasStatus(d, ["stopped"]);
1477
- const failed = d.results.some((r) => !r.stopped && r.exitCode !== 0 && r.progress?.status !== "running")
1478
- || workflowGraphHasStatus(d, ["failed"]);
1479
- const paused = d.results.some((r) => (r.interrupted || r.detached) && r.progress?.status !== "running")
1480
- || workflowGraphHasStatus(d, ["paused", "detached"]);
1481
- let totalSummary = d.progressSummary;
1482
- if (!totalSummary) {
1483
- let sawProgress = false;
1484
- const summary = { toolCount: 0, tokens: 0, durationMs: 0, lastActivityAt: undefined };
1485
- for (const r of d.results) {
1486
- const prog = r.progress || r.progressSummary;
1487
- if (!prog)
1488
- continue;
1489
- sawProgress = true;
1490
- summary.toolCount += prog.toolCount;
1491
- summary.tokens += prog.tokens;
1492
- summary.durationMs = d.mode === "chain" ? summary.durationMs + prog.durationMs : Math.max(summary.durationMs, prog.durationMs);
1493
- if (prog.status === "running" && prog.lastActivityAt !== undefined) {
1494
- summary.lastActivityAt = Math.max(summary.lastActivityAt ?? 0, prog.lastActivityAt);
1495
- }
1496
- }
1497
- if (sawProgress)
1498
- totalSummary = summary;
1499
- }
1500
- const multiLabel = buildMultiProgressLabel(d, hasRunning);
1501
- const itemTitle = multiLabel.itemTitle;
1502
- const glyph = (f) => hasRunning
1503
- ? theme.fg("accent", runningGlyph(f !== undefined ? (runningSeed(progressRunningSeed(totalSummary), d.currentStepIndex) ?? 0) + f : runningSeed(progressRunningSeed(totalSummary), d.currentStepIndex)))
1504
- : stopped
1505
- ? theme.fg("warning", "■")
1506
- : failed
1507
- ? theme.fg("error", "✗")
1508
- : paused
1509
- ? theme.fg("warning", "■")
1510
- : theme.fg("success", "✓");
1511
- const contextBadge = d.context === "fork" ? theme.fg("warning", " [fork]") : "";
1512
1355
  const c = new Container();
1513
1356
  const width = getTermWidth() - 4;
1514
- c.addChild(animatedLine((f) => {
1515
- const stats = statJoin(theme, [multiLabel.headerLabel, formatTotalCostStat(d.totalCost)]);
1516
- return truncLine(`${glyph(f)} ${theme.fg("toolTitle", theme.bold(d.mode))}${contextBadge}${stats ? ` ${theme.fg("dim", "·")} ${stats}` : ""}`, width);
1517
- }, frame, hasRunning));
1518
- const useResultsDirectly = multiLabel.hasParallelInChain || !d.chainAgents?.length;
1519
- const displayStart = multiLabel.showActiveGroupOnly ? multiLabel.groupStartIndex : 0;
1520
- const displayEnd = multiLabel.showActiveGroupOnly ? multiLabel.groupEndIndex : (useResultsDirectly ? d.results.length : d.chainAgents.length);
1521
- const chainEntries = buildChainRenderEntries(d, multiLabel);
1522
- const renderEntries = chainEntries ?? Array.from({ length: displayEnd - displayStart }, (_, offset) => {
1523
- const i = displayStart + offset;
1524
- const r = d.results[i];
1525
- const fallbackLabel = itemTitle.toLowerCase();
1526
- const rowNumber = multiLabel.showActiveGroupOnly ? (i - multiLabel.groupStartIndex + 1) : (i + 1);
1527
- return { kind: "result", resultIndex: i, rowNumber, agentName: useResultsDirectly ? (r?.agent || `${fallbackLabel}-${rowNumber}`) : (d.chainAgents[i] || r?.agent || `${fallbackLabel}-${rowNumber}`) };
1528
- });
1529
- for (const entry of renderEntries) {
1530
- if (entry.kind === "placeholder") {
1531
- const glyph = widgetStepGlyph(entry.status, theme);
1532
- const statusLabel = widgetStepStatus(entry.status, theme);
1533
- c.addChild(new Text(truncLine(` ${glyph} ${entry.stepLabel}: ${themeBold(theme, entry.agentName)} ${theme.fg("dim", "·")} ${statusLabel}`, width), 0, 0));
1534
- if (entry.error)
1535
- c.addChild(new Text(truncLine(theme.fg("error", ` ⎿ Error: ${entry.error}`), width), 0, 0));
1536
- continue;
1537
- }
1538
- const i = entry.resultIndex;
1539
- const r = d.results[i];
1540
- const rowNumber = entry.rowNumber;
1541
- const agentName = entry.agentName;
1542
- if (!r) {
1543
- const pendingLabel = chainEntries ? resultRowLabel(d, multiLabel, i, rowNumber) : `${itemTitle} ${rowNumber}`;
1544
- c.addChild(new Text(truncLine(theme.fg("dim", ` ◦ ${pendingLabel}: ${agentName} · pending`), width), 0, 0));
1545
- continue;
1546
- }
1547
- const output = getSingleResultOutput(r);
1548
- const progressFromArray = d.progress?.find((p) => p.index === i) || d.progress?.find((p) => p.agent === r.agent && p.status === "running");
1549
- const rProg = r.progress || progressFromArray || r.progressSummary;
1550
- const rRunning = rProg && "status" in rProg && rProg.status === "running";
1551
- const rPending = rProg && "status" in rProg && rProg.status === "pending";
1552
- const lead = compactRowLead({ description: r.description ?? agentName, task: r.task });
1553
- const modelBadge = formatCompactModelBadge(r.modelSelection ?? (rProg && "modelSelection" in rProg ? rProg.modelSelection : undefined), r.model ?? (rProg && "model" in rProg ? rProg.model : undefined));
1554
- c.addChild(animatedLine((f, now) => {
1555
- const rowGlyph = rPending ? theme.fg("dim", "◦") : resultGlyph(r, output, theme, rRunning, progressRunningSeed(rProg), f);
1357
+ const entries = compactForegroundEntries(d);
1358
+ for (const [rowIndex, entry] of entries.entries()) {
1359
+ const flatIndex = entry.flatIndex;
1360
+ const result = flatIndex === undefined ? undefined : resultAtFlatIndex(d, flatIndex);
1361
+ const progress = result?.progress
1362
+ ?? (flatIndex === undefined ? undefined : d.progress?.find((candidate) => candidate.index === flatIndex))
1363
+ ?? result?.progressSummary;
1364
+ const status = progress?.status ?? entry.node?.status ?? "pending";
1365
+ const description = compactRowLead({
1366
+ description: result?.description
1367
+ ?? progress?.description
1368
+ ?? entry.node?.label
1369
+ ?? (flatIndex === undefined ? undefined : d.chainChildren?.[flatIndex]?.description)
1370
+ ?? (flatIndex === undefined ? undefined : d.chainAgents?.[flatIndex])
1371
+ ?? result?.agent
1372
+ ?? progress?.agent,
1373
+ task: result?.task ?? progress?.task,
1374
+ }) || `child ${rowIndex + 1}`;
1375
+ const running = status === "running";
1376
+ const output = result ? getSingleResultOutput(result) : "";
1377
+ const modelBadge = formatCompactModelBadge(result?.modelSelection ?? progress?.modelSelection, result?.model ?? progress?.model);
1378
+ c.addChild(animatedLine((animationTick, now) => {
1379
+ const animationFrame = foregroundSpinnerFrame(animationTick);
1556
1380
  return formatCompactSubagentRow(theme, width, {
1557
- glyph: rowGlyph,
1558
- description: lead,
1381
+ glyph: result
1382
+ ? resultGlyph(result, output, theme, running, progressRunningSeed(progress), animationFrame)
1383
+ : progressStatusGlyph({ ...progress, status, index: flatIndex }, theme, animationFrame),
1384
+ description,
1559
1385
  modelBadge,
1560
- tokens: compactResultTokens(r),
1561
- durationMs: liveDurationMs(rProg, !!rRunning, now),
1562
- prefix: " ",
1386
+ tokens: result ? compactResultTokens(result) : progress?.tokens ?? 0,
1387
+ durationMs: liveDurationMs(progress, running, now),
1388
+ terminalStatus: result
1389
+ ? compactResultTerminalStatus(result)
1390
+ : compactTerminalStatus(status),
1563
1391
  });
1564
- }, frame, !!rRunning));
1565
- if (!rPending && !rRunning && (r.exitCode !== 0 || r.interrupted || r.detached || r.stopped)) {
1566
- c.addChild(new Text(truncLine(theme.fg(r.exitCode !== 0 ? "error" : "dim", ` ⎿ ${resultStatusLine(r, output)}`), width), 0, 0));
1567
- }
1392
+ }, frame, running));
1568
1393
  }
1569
1394
  return c;
1570
1395
  }
@@ -1573,7 +1398,9 @@ function renderMultiCompact(d, theme, frame) {
1573
1398
  */
1574
1399
  export function renderSubagentResult(result, options, theme, frame) {
1575
1400
  const d = result.details;
1576
- if (!d || !d.results.length) {
1401
+ const hasWorkflowRows = d?.mode !== "management"
1402
+ && Boolean(d?.workflowGraph?.nodes.length || d?.progress?.length || d?.chainChildren?.length);
1403
+ if (!d || (!d.results.length && !hasWorkflowRows)) {
1577
1404
  const t = result.content[0];
1578
1405
  const text = t?.type === "text" ? t.text : "(no output)";
1579
1406
  const contextPrefix = d?.context === "fork" ? `${theme.fg("warning", "[fork]")} ` : "";
@@ -1618,6 +1445,9 @@ export function renderSubagentResult(result, options, theme, frame) {
1618
1445
  ? r.task
1619
1446
  : `${r.task.slice(0, taskMaxLen)}...`;
1620
1447
  c.addChild(new Text(fit(theme.fg("dim", `Task: ${taskPreview}`)), 0, 0));
1448
+ if (!isRunning && compactResultTerminalStatus(r)) {
1449
+ c.addChild(new Text(fit(theme.fg(r.exitCode !== 0 ? "error" : "warning", resultStatusLine(r, output))), 0, 0));
1450
+ }
1621
1451
  c.addChild(new Spacer(1));
1622
1452
  if (isRunning && r.progress) {
1623
1453
  const progressSnapshotNow = snapshotNowForProgress(r.progress);
@@ -1690,6 +1520,7 @@ export function renderSubagentResult(result, options, theme, frame) {
1690
1520
  const hasWorkflowFailure = workflowGraphHasStatus(d, ["failed"]);
1691
1521
  const hasWorkflowStop = d.results.some((r) => r.stopped && r.progress?.status !== "running") || workflowGraphHasStatus(d, ["stopped"]);
1692
1522
  const hasWorkflowPause = workflowGraphHasStatus(d, ["paused", "detached"]);
1523
+ const hasWorkflowPending = workflowGraphHasStatus(d, ["pending"]);
1693
1524
  const icon = hasRunning
1694
1525
  ? theme.fg("warning", "running")
1695
1526
  : hasEmptyWithoutTarget
@@ -1700,9 +1531,11 @@ export function renderSubagentResult(result, options, theme, frame) {
1700
1531
  ? theme.fg("warning", "stopped")
1701
1532
  : hasWorkflowPause
1702
1533
  ? theme.fg("warning", "paused")
1703
- : ok === d.results.length
1704
- ? theme.fg("success", "ok")
1705
- : theme.fg("error", "failed");
1534
+ : hasWorkflowPending
1535
+ ? theme.fg("dim", "pending")
1536
+ : ok === d.results.length
1537
+ ? theme.fg("success", "ok")
1538
+ : theme.fg("error", "failed");
1706
1539
  const totalSummary = d.progressSummary ||
1707
1540
  d.results.reduce((acc, r) => {
1708
1541
  const prog = r.progress || r.progressSummary;
@@ -1764,16 +1597,40 @@ export function renderSubagentResult(result, options, theme, frame) {
1764
1597
  if (chainVis) {
1765
1598
  c.addChild(new Text(fit(` ${chainVis}`), 0, 0));
1766
1599
  }
1767
- const useResultsDirectly = multiLabel.hasParallelInChain || !d.chainAgents?.length;
1768
- const displayStart = multiLabel.showActiveGroupOnly ? multiLabel.groupStartIndex : 0;
1769
- const displayEnd = multiLabel.showActiveGroupOnly ? multiLabel.groupEndIndex : (useResultsDirectly ? d.results.length : d.chainAgents.length);
1770
1600
  const chainEntries = buildChainRenderEntries(d, multiLabel);
1771
- const renderEntries = chainEntries ?? Array.from({ length: displayEnd - displayStart }, (_, offset) => {
1772
- const i = displayStart + offset;
1773
- const r = d.results[i];
1774
- const rowNumber = multiLabel.showActiveGroupOnly ? (i - multiLabel.groupStartIndex + 1) : (i + 1);
1775
- return { kind: "result", resultIndex: i, rowNumber, agentName: useResultsDirectly ? (r?.agent || `step-${rowNumber}`) : (d.chainAgents[i] || r?.agent || `step-${rowNumber}`) };
1601
+ const fallbackEntries = compactForegroundEntries(d)
1602
+ .filter((entry) => !multiLabel.showActiveGroupOnly
1603
+ || entry.flatIndex === undefined
1604
+ || (entry.flatIndex >= multiLabel.groupStartIndex && entry.flatIndex < multiLabel.groupEndIndex))
1605
+ .map((entry, rowIndex) => {
1606
+ if (entry.flatIndex === undefined) {
1607
+ return {
1608
+ kind: "placeholder",
1609
+ rowNumber: entry.node?.stepIndex !== undefined ? entry.node.stepIndex + 1 : rowIndex + 1,
1610
+ stepLabel: `Step ${entry.node?.stepIndex !== undefined ? entry.node.stepIndex + 1 : rowIndex + 1}`,
1611
+ agentName: entry.node?.label ?? `step-${rowIndex + 1}`,
1612
+ status: entry.node?.status ?? "pending",
1613
+ error: entry.node?.error,
1614
+ };
1615
+ }
1616
+ const result = resultAtFlatIndex(d, entry.flatIndex);
1617
+ const rowNumber = multiLabel.showActiveGroupOnly
1618
+ ? entry.flatIndex - multiLabel.groupStartIndex + 1
1619
+ : entry.flatIndex + 1;
1620
+ return {
1621
+ kind: "result",
1622
+ resultIndex: entry.flatIndex,
1623
+ rowNumber,
1624
+ agentName: result?.agent
1625
+ ?? entry.node?.label
1626
+ ?? d.chainChildren?.[entry.flatIndex]?.description
1627
+ ?? d.chainAgents?.[entry.flatIndex]
1628
+ ?? `step-${rowNumber}`,
1629
+ status: entry.node?.status,
1630
+ error: entry.node?.error,
1631
+ };
1776
1632
  });
1633
+ const renderEntries = chainEntries ?? fallbackEntries;
1777
1634
  c.addChild(new Spacer(1));
1778
1635
  for (const entry of renderEntries) {
1779
1636
  if (entry.kind === "placeholder") {
@@ -1786,13 +1643,16 @@ export function renderSubagentResult(result, options, theme, frame) {
1786
1643
  continue;
1787
1644
  }
1788
1645
  const i = entry.resultIndex;
1789
- const r = d.results[i];
1646
+ const r = resultAtFlatIndex(d, i);
1790
1647
  const rowNumber = entry.rowNumber;
1791
1648
  const agentName = entry.agentName;
1792
1649
  if (!r) {
1793
- const pendingLabel = chainEntries ? resultRowLabel(d, multiLabel, i, rowNumber) : `${itemTitle} ${rowNumber}`;
1794
- c.addChild(new Text(fit(theme.fg("dim", ` ${pendingLabel}: ${agentName}`)), 0, 0));
1795
- c.addChild(new Text(theme.fg("dim", ` status: pending`), 0, 0));
1650
+ const rowLabel = chainEntries ? resultRowLabel(d, multiLabel, i, rowNumber) : `${itemTitle} ${rowNumber}`;
1651
+ const status = entry.status ?? "pending";
1652
+ c.addChild(new Text(fit(theme.fg(status === "failed" ? "error" : "dim", ` ${rowLabel}: ${agentName}`)), 0, 0));
1653
+ c.addChild(new Text(theme.fg(status === "failed" ? "error" : "dim", ` status: ${status}`), 0, 0));
1654
+ if (entry.error)
1655
+ c.addChild(new Text(theme.fg("error", ` error: ${entry.error}`), 0, 0));
1796
1656
  c.addChild(new Spacer(1));
1797
1657
  continue;
1798
1658
  }
@@ -1823,6 +1683,9 @@ export function renderSubagentResult(result, options, theme, frame) {
1823
1683
  ? r.task
1824
1684
  : `${r.task.slice(0, taskMaxLen)}...`;
1825
1685
  c.addChild(new Text(fit(theme.fg("dim", ` task: ${taskPreview}`)), 0, 0));
1686
+ if (!rRunning && compactResultTerminalStatus(r)) {
1687
+ c.addChild(new Text(fit(theme.fg(r.exitCode !== 0 ? "error" : "warning", ` ${resultStatusLine(r, resultOutput)}`)), 0, 0));
1688
+ }
1826
1689
  if (r.skills?.length) {
1827
1690
  c.addChild(new Text(fit(theme.fg("dim", ` skills: ${r.skills.join(", ")}`)), 0, 0));
1828
1691
  }