@selesai/code 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/dist/core/diagnostics.d.ts +1 -0
  2. package/dist/core/diagnostics.d.ts.map +1 -1
  3. package/dist/core/diagnostics.js.map +1 -1
  4. package/dist/core/package-manager.d.ts +10 -0
  5. package/dist/core/package-manager.d.ts.map +1 -1
  6. package/dist/core/package-manager.js +93 -8
  7. package/dist/core/package-manager.js.map +1 -1
  8. package/dist/core/package-manager.test.d.ts +2 -0
  9. package/dist/core/package-manager.test.d.ts.map +1 -0
  10. package/dist/core/package-manager.test.js +20 -0
  11. package/dist/core/package-manager.test.js.map +1 -0
  12. package/dist/core/resource-loader.d.ts +3 -0
  13. package/dist/core/resource-loader.d.ts.map +1 -1
  14. package/dist/core/resource-loader.js +10 -0
  15. package/dist/core/resource-loader.js.map +1 -1
  16. package/dist/core/settings-manager.d.ts +1 -0
  17. package/dist/core/settings-manager.d.ts.map +1 -1
  18. package/dist/core/settings-manager.js.map +1 -1
  19. package/dist/extensions/caveman/caveman-instructions.cjs +39 -0
  20. package/dist/extensions/caveman/index.js +116 -0
  21. package/dist/extensions/caveman/package.json +8 -0
  22. package/dist/extensions/caveman/test/extension.test.js +185 -0
  23. package/dist/extensions/caveman/test/helpers.test.js +67 -0
  24. package/dist/extensions/pi-intercom/broker/broker.ts +2 -3
  25. package/dist/extensions/pi-intercom/broker/paths.ts +10 -5
  26. package/dist/extensions/pi-intercom/broker/spawn.ts +2 -3
  27. package/dist/extensions/pi-intercom/config.ts +2 -2
  28. package/dist/extensions/pi-intercom/index.ts +2 -2
  29. package/dist/extensions/pi-intercom/package.json +2 -2
  30. package/dist/extensions/pi-intercom/ui/compose.ts +3 -3
  31. package/dist/extensions/pi-intercom/ui/inline-message.ts +3 -3
  32. package/dist/extensions/pi-intercom/ui/session-list.ts +3 -3
  33. package/dist/extensions/pi-powerline-footer/bash-mode/history.ts +2 -1
  34. package/dist/extensions/pi-powerline-footer/index.ts +9 -16
  35. package/dist/extensions/pi-powerline-footer/welcome.ts +23 -16
  36. package/dist/extensions/pi-powerline-footer/working-vibes.ts +2 -9
  37. package/dist/extensions/pi-rewind-hook/index.ts +1 -1
  38. package/dist/extensions/pi-rewind-hook/package.json +1 -1
  39. package/dist/extensions/pi-subagents/README.md +224 -52
  40. package/dist/extensions/pi-subagents/install.mjs +93 -0
  41. package/dist/extensions/pi-subagents/package.json +4 -5
  42. package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +1 -1
  43. package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +1 -1
  44. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +76 -13
  45. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +179 -2
  46. package/dist/extensions/pi-subagents/src/agents/agent-memory.ts +254 -0
  47. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +11 -0
  48. package/dist/extensions/pi-subagents/src/agents/agents.ts +193 -19
  49. package/dist/extensions/pi-subagents/src/agents/chain-serializer.ts +27 -2
  50. package/dist/extensions/pi-subagents/src/extension/config.ts +27 -4
  51. package/dist/extensions/pi-subagents/src/extension/doctor.ts +1 -7
  52. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +3 -2
  53. package/dist/extensions/pi-subagents/src/extension/index.ts +73 -71
  54. package/dist/extensions/pi-subagents/src/extension/rpc.ts +369 -0
  55. package/dist/extensions/pi-subagents/src/extension/schemas.ts +54 -10
  56. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +200 -0
  57. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +21 -253
  58. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +519 -0
  59. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +1 -1
  60. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +195 -105
  61. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +88 -2
  62. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +67 -10
  63. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +34 -4
  64. package/dist/extensions/pi-subagents/src/runs/background/completion-batcher.ts +166 -0
  65. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +156 -1
  66. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +515 -0
  67. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +161 -44
  68. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +1 -2
  69. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +3 -2
  70. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +167 -6
  71. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +514 -0
  72. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +28 -1
  73. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +873 -204
  74. package/dist/extensions/pi-subagents/src/runs/background/wait.ts +353 -0
  75. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +123 -27
  76. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +174 -70
  77. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +569 -81
  78. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +45 -22
  79. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +3 -1
  80. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +2 -2
  81. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +172 -21
  82. package/dist/extensions/pi-subagents/src/runs/shared/model-scope.ts +128 -0
  83. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +89 -0
  84. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +50 -1
  85. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +35 -4
  86. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +63 -45
  87. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +2 -0
  88. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +125 -5
  89. package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +74 -0
  90. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +52 -0
  91. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +28 -5
  92. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +16 -1
  93. package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +15 -2
  94. package/dist/extensions/pi-subagents/src/shared/child-transcript.ts +212 -0
  95. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +133 -22
  96. package/dist/extensions/pi-subagents/src/shared/settings.ts +3 -1
  97. package/dist/extensions/pi-subagents/src/shared/types.ts +197 -4
  98. package/dist/extensions/pi-subagents/src/shared/utils.ts +100 -15
  99. package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +330 -0
  100. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +135 -4
  101. package/dist/extensions/pi-subagents/src/tui/render.ts +32 -12
  102. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +94 -0
  103. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +2772 -0
  104. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +1107 -0
  105. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +463 -0
  106. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +274 -0
  107. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +1535 -0
  108. package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +242 -0
  109. package/dist/extensions/pi-subagents/test/integration/doctor-executor.test.ts +128 -0
  110. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +252 -0
  111. package/dist/extensions/pi-subagents/test/integration/foreground-result-size.test.ts +188 -0
  112. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +1306 -0
  113. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +1033 -0
  114. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +397 -0
  115. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +583 -0
  116. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +691 -0
  117. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +892 -0
  118. package/dist/extensions/pi-subagents/test/integration/session-tokens.test.ts +62 -0
  119. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1824 -0
  120. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1284 -0
  121. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +127 -0
  122. package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +299 -0
  123. package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +37 -0
  124. package/dist/extensions/pi-subagents/test/support/helpers.ts +189 -0
  125. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +335 -0
  126. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +129 -0
  127. package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +184 -0
  128. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +285 -0
  129. package/dist/extensions/pi-subagents/test/support/register-loader.mjs +15 -0
  130. package/dist/extensions/pi-subagents/test/support/ts-loader.mjs +108 -0
  131. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +421 -0
  132. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +213 -0
  133. package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +343 -0
  134. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +1199 -0
  135. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +556 -0
  136. package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +420 -0
  137. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +579 -0
  138. package/dist/extensions/pi-subagents/test/unit/agent-scope.test.ts +20 -0
  139. package/dist/extensions/pi-subagents/test/unit/agent-selection.test.ts +88 -0
  140. package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +24 -0
  141. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +86 -0
  142. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +221 -0
  143. package/dist/extensions/pi-subagents/test/unit/async-permission-session.test.ts +98 -0
  144. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +285 -0
  145. package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +112 -0
  146. package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +206 -0
  147. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +159 -0
  148. package/dist/extensions/pi-subagents/test/unit/chain-serializer.test.ts +229 -0
  149. package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +225 -0
  150. package/dist/extensions/pi-subagents/test/unit/close-grace-timer.test.ts +101 -0
  151. package/dist/extensions/pi-subagents/test/unit/completion-batcher.test.ts +228 -0
  152. package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +36 -0
  153. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +152 -0
  154. package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +72 -0
  155. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +327 -0
  156. package/dist/extensions/pi-subagents/test/unit/control-notices.test.ts +161 -0
  157. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +159 -0
  158. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +217 -0
  159. package/dist/extensions/pi-subagents/test/unit/extra-agent-dirs.test.ts +88 -0
  160. package/dist/extensions/pi-subagents/test/unit/file-coalescer.test.ts +68 -0
  161. package/dist/extensions/pi-subagents/test/unit/foreground-tool-call-compaction.test.ts +78 -0
  162. package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +415 -0
  163. package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +126 -0
  164. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +271 -0
  165. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +173 -0
  166. package/dist/extensions/pi-subagents/test/unit/jsonl-writer.test.ts +119 -0
  167. package/dist/extensions/pi-subagents/test/unit/live-async-resume.test.ts +170 -0
  168. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +346 -0
  169. package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +62 -0
  170. package/dist/extensions/pi-subagents/test/unit/model-scope-settings.test.ts +87 -0
  171. package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +145 -0
  172. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +162 -0
  173. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +470 -0
  174. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +329 -0
  175. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +319 -0
  176. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +70 -0
  177. package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +247 -0
  178. package/dist/extensions/pi-subagents/test/unit/path-handling.test.ts +113 -0
  179. package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +94 -0
  180. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +803 -0
  181. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +209 -0
  182. package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +238 -0
  183. package/dist/extensions/pi-subagents/test/unit/proactive-skills.test.ts +118 -0
  184. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +299 -0
  185. package/dist/extensions/pi-subagents/test/unit/prompt-template-bridge.test.ts +408 -0
  186. package/dist/extensions/pi-subagents/test/unit/prompt-workflows.test.ts +134 -0
  187. package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +257 -0
  188. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +209 -0
  189. package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +226 -0
  190. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +294 -0
  191. package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +147 -0
  192. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +997 -0
  193. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +478 -0
  194. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +562 -0
  195. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +213 -0
  196. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +438 -0
  197. package/dist/extensions/pi-subagents/test/unit/slash-bridge.test.ts +54 -0
  198. package/dist/extensions/pi-subagents/test/unit/slash-chain-groups.test.ts +464 -0
  199. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +255 -0
  200. package/dist/extensions/pi-subagents/test/unit/status-format.test.ts +19 -0
  201. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +245 -0
  202. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +517 -0
  203. package/dist/extensions/pi-subagents/test/unit/temp-paths.test.ts +72 -0
  204. package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +52 -0
  205. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +237 -0
  206. package/dist/extensions/pi-subagents/test/unit/total-cost.test.ts +60 -0
  207. package/dist/extensions/pi-subagents/test/unit/ts-loader.test.ts +33 -0
  208. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +202 -0
  209. package/dist/extensions/pi-subagents/test/unit/types-fork-preamble.test.ts +23 -0
  210. package/dist/extensions/pi-subagents/test/unit/wait.test.ts +398 -0
  211. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +88 -0
  212. package/dist/extensions/pi-subagents/test/unit/windows-hide-spawn.test.ts +26 -0
  213. package/dist/extensions/pi-subagents/test/unit/workflow-graph.test.ts +134 -0
  214. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +479 -0
  215. package/dist/extensions/pi-web-agent/src/changelog-notice.ts +5 -2
  216. package/dist/extensions/pi-web-agent/src/presentation/config-store.ts +6 -4
  217. package/dist/main.d.ts.map +1 -1
  218. package/dist/main.js +4 -0
  219. package/dist/main.js.map +1 -1
  220. package/dist/modes/interactive/components/first-time-setup.d.ts +1 -1
  221. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  222. package/dist/modes/interactive/components/first-time-setup.js +19 -5
  223. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  224. package/dist/skills/caveman/SKILL.md +49 -0
  225. package/dist/skills/handoff-text/SKILL.md +13 -0
  226. package/docs/docs.json +4 -0
  227. package/docs/extensions.md +2 -0
  228. package/docs/settings.md +15 -0
  229. package/docs/shared-host-extensions.md +109 -0
  230. package/package.json +1 -1
@@ -0,0 +1,583 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+
4
+ type RenderSubagentResult = (
5
+ result: {
6
+ content: Array<{ type: "text"; text: string }>;
7
+ details?: {
8
+ mode: "single" | "parallel" | "chain" | "management";
9
+ context?: "fresh" | "fork";
10
+ results: unknown[];
11
+ };
12
+ },
13
+ options: { expanded: boolean },
14
+ theme: {
15
+ fg(name: string, text: string): string;
16
+ bold(text: string): string;
17
+ },
18
+ ) => { render(width: number): string[] };
19
+
20
+ let renderSubagentResult: RenderSubagentResult | undefined;
21
+ ({ renderSubagentResult } = await import("../../src/tui/render.ts") as {
22
+ renderSubagentResult?: RenderSubagentResult;
23
+ });
24
+
25
+ const theme = {
26
+ fg: (_name: string, text: string) => text,
27
+ bold: (text: string) => text,
28
+ };
29
+
30
+ const emptyUsage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };
31
+
32
+ function firstGrapheme(text: string): string {
33
+ return Array.from(text.trimStart())[0] ?? "";
34
+ }
35
+
36
+ function withTerminalWidth<T>(columns: number, fn: () => T): T {
37
+ const original = process.stdout.columns;
38
+ Object.defineProperty(process.stdout, "columns", {
39
+ value: columns,
40
+ configurable: true,
41
+ });
42
+ try {
43
+ return fn();
44
+ } finally {
45
+ Object.defineProperty(process.stdout, "columns", {
46
+ value: original,
47
+ configurable: true,
48
+ });
49
+ }
50
+ }
51
+
52
+ describe("renderSubagentResult fork indicator", () => {
53
+ it("shows [fork] when details are empty but context is fork", () => {
54
+ const widget = renderSubagentResult!({
55
+ content: [{ type: "text", text: "Async: reviewer [abc123]" }],
56
+ details: { mode: "single", context: "fork", results: [] },
57
+ }, { expanded: false }, theme);
58
+
59
+ const text = widget.render(120).join("\n");
60
+ assert.match(text, /\[fork\]/);
61
+ });
62
+
63
+ it("shows [fork] on single-result header", () => {
64
+ const widget = renderSubagentResult!({
65
+ content: [{ type: "text", text: "done" }],
66
+ details: {
67
+ mode: "single",
68
+ context: "fork",
69
+ results: [{
70
+ agent: "reviewer",
71
+ task: "review",
72
+ exitCode: 0,
73
+ messages: [],
74
+ usage: emptyUsage,
75
+ }],
76
+ },
77
+ }, { expanded: false }, theme);
78
+
79
+ const text = widget.render(120).join("\n");
80
+ assert.match(text, /\[fork\]/);
81
+ });
82
+
83
+ it("uses compacted tool-call summaries when messages were stripped", () => {
84
+ const widget = renderSubagentResult!({
85
+ content: [{ type: "text", text: "done" }],
86
+ details: {
87
+ mode: "single",
88
+ results: [{
89
+ agent: "reviewer",
90
+ task: "review",
91
+ exitCode: 0,
92
+ messages: undefined,
93
+ toolCalls: [{
94
+ text: "$ npm test -- --watch...",
95
+ expandedText: "$ npm test -- --watch --runInBand --reporter=dot",
96
+ }],
97
+ usage: emptyUsage,
98
+ }],
99
+ },
100
+ }, { expanded: true }, theme);
101
+
102
+ const text = widget.render(120).join("\n");
103
+ assert.match(text, /npm test -- --watch --runInBand --reporter=dot/);
104
+ });
105
+
106
+ it("shows the full task in expanded mode", () => {
107
+ const longTask = "Review the auth flow, trace the race condition, and document the precise failing tool sequence at the end.";
108
+ const collapsed = withTerminalWidth(40, () => renderSubagentResult!({
109
+ content: [{ type: "text", text: "done" }],
110
+ details: {
111
+ mode: "single",
112
+ results: [{
113
+ agent: "reviewer",
114
+ task: longTask,
115
+ exitCode: 0,
116
+ messages: [],
117
+ usage: emptyUsage,
118
+ }],
119
+ },
120
+ }, { expanded: false }, theme).render(40).join("\n"));
121
+
122
+ const expanded = withTerminalWidth(40, () => renderSubagentResult!({
123
+ content: [{ type: "text", text: "done" }],
124
+ details: {
125
+ mode: "single",
126
+ results: [{
127
+ agent: "reviewer",
128
+ task: longTask,
129
+ exitCode: 0,
130
+ messages: [],
131
+ usage: emptyUsage,
132
+ }],
133
+ },
134
+ }, { expanded: true }, theme).render(40).join("\n"));
135
+
136
+ const unwrap = (text: string) => text.replace(/\s+/g, "");
137
+ assert.doesNotMatch(unwrap(collapsed), /precisefailingtoolsequenceattheend\./);
138
+ assert.match(unwrap(expanded), /precisefailingtoolsequenceattheend\./);
139
+ });
140
+
141
+ it("uses glyph-first compact rendering for completed subagents", () => {
142
+ const widget = renderSubagentResult!({
143
+ content: [{ type: "text", text: "done" }],
144
+ details: {
145
+ mode: "single",
146
+ results: [{
147
+ agent: "reviewer",
148
+ task: "review",
149
+ exitCode: 0,
150
+ messages: [],
151
+ usage: { ...emptyUsage, turns: 2 },
152
+ progressSummary: { toolCount: 3, tokens: 1200, durationMs: 1500 },
153
+ sessionFile: "/tmp/session.jsonl",
154
+ }],
155
+ },
156
+ }, { expanded: false }, theme);
157
+
158
+ const text = widget.render(120).join("\n");
159
+ assert.match(text, /^✓ reviewer/);
160
+ assert.match(text, /⟳ 2/);
161
+ assert.match(text, /3 tool uses/);
162
+ assert.match(text, /1\.2k token/);
163
+ assert.match(text, /⎿ Done/);
164
+ assert.match(text, /session: \/tmp\/session\.jsonl/);
165
+ });
166
+
167
+ it("keeps failure reasons visible in compact rendering", () => {
168
+ const widget = renderSubagentResult!({
169
+ content: [{ type: "text", text: "failed" }],
170
+ details: {
171
+ mode: "single",
172
+ results: [{
173
+ agent: "reviewer",
174
+ task: "review",
175
+ exitCode: 1,
176
+ error: "boom",
177
+ messages: [],
178
+ usage: emptyUsage,
179
+ }],
180
+ },
181
+ }, { expanded: false }, theme);
182
+
183
+ const text = widget.render(120).join("\n");
184
+ assert.match(text, /^✗ reviewer/);
185
+ assert.match(text, /⎿ Error: boom/);
186
+ });
187
+
188
+ it("shows live detail hints for running subagents", () => {
189
+ const now = Date.now();
190
+ const widget = renderSubagentResult!({
191
+ content: [{ type: "text", text: "(running...)" }],
192
+ details: {
193
+ mode: "single",
194
+ results: [{
195
+ agent: "reviewer",
196
+ task: "review",
197
+ exitCode: 0,
198
+ messages: [],
199
+ artifactPaths: {
200
+ outputPath: "/tmp/reviewer_output.md",
201
+ },
202
+ usage: emptyUsage,
203
+ progress: {
204
+ index: 0,
205
+ agent: "reviewer",
206
+ status: "running",
207
+ task: "review",
208
+ lastActivityAt: now - 2_000,
209
+ currentTool: "read",
210
+ currentToolArgs: "package.json",
211
+ currentToolStartedAt: now - 3_000,
212
+ recentTools: [],
213
+ recentOutput: [],
214
+ toolCount: 1,
215
+ tokens: 42,
216
+ durationMs: 3_000,
217
+ },
218
+ }],
219
+ },
220
+ }, { expanded: false }, theme);
221
+
222
+ const text = widget.render(120).join("\n");
223
+ assert.match(text, /Press configured-expand-key for live detail/);
224
+ assert.match(text, /active 2s ago/);
225
+ assert.match(text, /⎿ read: package\.json \| 3\.0s/);
226
+ assert.match(text, /output: \/tmp\/reviewer_output\.md/);
227
+ });
228
+
229
+ it("keeps running compact result output stable when progress is unchanged", async () => {
230
+ const result = {
231
+ content: [{ type: "text" as const, text: "(running...)" }],
232
+ details: {
233
+ mode: "single" as const,
234
+ results: [{
235
+ agent: "reviewer",
236
+ task: "review",
237
+ exitCode: 0,
238
+ messages: [],
239
+ usage: emptyUsage,
240
+ progress: {
241
+ index: 0,
242
+ agent: "reviewer",
243
+ status: "running" as const,
244
+ task: "review",
245
+ lastActivityAt: 2_000,
246
+ currentTool: "read",
247
+ currentToolArgs: "package.json",
248
+ currentToolStartedAt: 1_000,
249
+ recentTools: [],
250
+ recentOutput: [],
251
+ toolCount: 1,
252
+ tokens: 42,
253
+ durationMs: 3_000,
254
+ },
255
+ }],
256
+ },
257
+ };
258
+ const first = renderSubagentResult!(result, { expanded: false }, theme).render(120);
259
+ await new Promise((resolve) => setTimeout(resolve, 120));
260
+ const second = renderSubagentResult!(result, { expanded: false }, theme).render(120);
261
+
262
+ assert.deepEqual(second, first);
263
+ });
264
+
265
+ it("advances running compact result glyphs when progress changes", () => {
266
+ const renderGlyph = (toolCount: number) => firstGrapheme(renderSubagentResult!({
267
+ content: [{ type: "text", text: "(running...)" }],
268
+ details: {
269
+ mode: "single",
270
+ results: [{
271
+ agent: "reviewer",
272
+ task: "review",
273
+ exitCode: 0,
274
+ messages: [],
275
+ usage: emptyUsage,
276
+ progress: {
277
+ index: 0,
278
+ agent: "reviewer",
279
+ status: "running",
280
+ task: "review",
281
+ recentTools: [],
282
+ recentOutput: [],
283
+ toolCount,
284
+ tokens: 0,
285
+ durationMs: 0,
286
+ },
287
+ }],
288
+ },
289
+ }, { expanded: false }, theme).render(120)[0] ?? "");
290
+
291
+ assert.notEqual(renderGlyph(1), renderGlyph(2));
292
+ });
293
+
294
+ it("keeps paused multi-result runs visible in the compact headline", () => {
295
+ const widget = renderSubagentResult!({
296
+ content: [{ type: "text", text: "paused" }],
297
+ details: {
298
+ mode: "chain",
299
+ chainAgents: ["worker"],
300
+ results: [{
301
+ agent: "worker",
302
+ task: "pause",
303
+ exitCode: 0,
304
+ interrupted: true,
305
+ messages: [],
306
+ usage: emptyUsage,
307
+ }],
308
+ },
309
+ }, { expanded: false }, theme);
310
+
311
+ const text = widget.render(120).join("\n");
312
+ assert.match(text, /^■ chain/);
313
+ assert.match(text, /⎿ Paused/);
314
+ });
315
+
316
+ it("keeps empty-output warnings visible in compact multi-result rendering", () => {
317
+ const widget = renderSubagentResult!({
318
+ content: [{ type: "text", text: "done" }],
319
+ details: {
320
+ mode: "chain",
321
+ chainAgents: ["worker"],
322
+ results: [{
323
+ agent: "worker",
324
+ task: "check without output target",
325
+ exitCode: 0,
326
+ messages: [],
327
+ usage: emptyUsage,
328
+ }],
329
+ },
330
+ }, { expanded: false }, theme);
331
+
332
+ const text = widget.render(120).join("\n");
333
+ assert.match(text, /⎿ Done \(no text output\)/);
334
+ assert.doesNotMatch(text, /0ms/);
335
+ });
336
+
337
+ it("keeps pending placeholder steps pending in compact rendering", () => {
338
+ const widget = renderSubagentResult!({
339
+ content: [{ type: "text", text: "running" }],
340
+ details: {
341
+ mode: "chain",
342
+ chainAgents: ["a", "b"],
343
+ totalSteps: 2,
344
+ currentStepIndex: 0,
345
+ results: [{
346
+ agent: "a",
347
+ task: "first",
348
+ exitCode: 0,
349
+ messages: [],
350
+ usage: emptyUsage,
351
+ progress: { index: 0, agent: "a", status: "running", task: "first", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 0 },
352
+ }, {
353
+ agent: "b",
354
+ task: "second",
355
+ exitCode: 0,
356
+ messages: [],
357
+ usage: emptyUsage,
358
+ progress: { index: 1, agent: "b", status: "pending", task: "second", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 0 },
359
+ }],
360
+ },
361
+ }, { expanded: false }, theme);
362
+
363
+ const lines = widget.render(120);
364
+ const pendingIndex = lines.findIndex((line) => /Step 2: b/.test(line));
365
+ assert.notEqual(pendingIndex, -1);
366
+ assert.match(lines[pendingIndex]!, /◦ Step 2: b · pending/);
367
+ assert.doesNotMatch(lines[pendingIndex]!, /0ms/);
368
+ assert.doesNotMatch(lines[pendingIndex + 1] ?? "", /Done \(no text output\)/);
369
+ });
370
+
371
+ it("uses running/done wording and agent fractions for live parallel rendering", () => {
372
+ const widget = renderSubagentResult!({
373
+ content: [{ type: "text", text: "(running...)" }],
374
+ details: {
375
+ mode: "parallel",
376
+ totalSteps: 3,
377
+ results: [{
378
+ agent: "worker",
379
+ task: "third task",
380
+ exitCode: 0,
381
+ messages: [],
382
+ usage: emptyUsage,
383
+ progress: {
384
+ index: 2,
385
+ agent: "worker",
386
+ status: "running",
387
+ task: "third task",
388
+ recentTools: [],
389
+ recentOutput: [],
390
+ toolCount: 1,
391
+ tokens: 0,
392
+ durationMs: 10,
393
+ },
394
+ }],
395
+ progress: [{
396
+ index: 0,
397
+ agent: "scout",
398
+ status: "running",
399
+ task: "first",
400
+ recentTools: [],
401
+ recentOutput: [],
402
+ toolCount: 0,
403
+ tokens: 0,
404
+ durationMs: 10,
405
+ }],
406
+ },
407
+ }, { expanded: false }, theme);
408
+
409
+ const text = widget.render(120).join("\n");
410
+ assert.match(text, /parallel · 2 agents running · 0\/3 done/);
411
+ assert.match(text, /Agent 3\/3: worker/);
412
+ assert.doesNotMatch(text, /Step 3: worker/);
413
+ assert.doesNotMatch(text, /Agent 1: worker/);
414
+ });
415
+
416
+ it("shows mixed done/running counters for top-level parallel mode", () => {
417
+ const widget = renderSubagentResult!({
418
+ content: [{ type: "text", text: "(running...)" }],
419
+ details: {
420
+ mode: "parallel",
421
+ totalSteps: 3,
422
+ results: [{
423
+ agent: "scout",
424
+ task: "first",
425
+ exitCode: 0,
426
+ messages: [],
427
+ usage: emptyUsage,
428
+ progress: { index: 0, agent: "scout", status: "completed", task: "first", recentTools: [], recentOutput: [], toolCount: 1, tokens: 0, durationMs: 10 },
429
+ }, {
430
+ agent: "reviewer",
431
+ task: "second",
432
+ exitCode: 0,
433
+ messages: [],
434
+ usage: emptyUsage,
435
+ progress: { index: 1, agent: "reviewer", status: "running", task: "second", recentTools: [], recentOutput: [], toolCount: 1, tokens: 0, durationMs: 10 },
436
+ }],
437
+ progress: [{ index: 0, agent: "scout", status: "completed", task: "first", recentTools: [], recentOutput: [], toolCount: 1, tokens: 0, durationMs: 10 }, { index: 1, agent: "reviewer", status: "running", task: "second", recentTools: [], recentOutput: [], toolCount: 1, tokens: 0, durationMs: 10 }],
438
+ },
439
+ }, { expanded: false }, theme);
440
+
441
+ const text = widget.render(120).join("\n");
442
+ assert.match(text, /parallel · 1 agent running · 1\/3 done/);
443
+ });
444
+
445
+ it("labels active chain parallel groups with chain step and agent fractions", () => {
446
+ const widget = renderSubagentResult!({
447
+ content: [{ type: "text", text: "running" }],
448
+ details: {
449
+ mode: "chain",
450
+ totalSteps: 3,
451
+ currentStepIndex: 0,
452
+ chainAgents: ["[scout+reviewer+worker]", "planner", "writer"],
453
+ results: [{
454
+ agent: "scout",
455
+ task: "scan",
456
+ exitCode: 0,
457
+ messages: [],
458
+ usage: emptyUsage,
459
+ progress: { index: 0, agent: "scout", status: "running", task: "scan", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 0 },
460
+ }, {
461
+ agent: "reviewer",
462
+ task: "review",
463
+ exitCode: 0,
464
+ messages: [],
465
+ usage: emptyUsage,
466
+ progress: { index: 1, agent: "reviewer", status: "running", task: "review", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 0 },
467
+ }],
468
+ progress: [{ index: 0, agent: "scout", status: "running", task: "scan", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 0 }, { index: 1, agent: "reviewer", status: "running", task: "review", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 0 }],
469
+ },
470
+ }, { expanded: false }, theme);
471
+
472
+ const text = widget.render(120).join("\n");
473
+ assert.match(text, /chain · step 1\/3 · parallel group: 2 agents running · 0\/3 done/);
474
+ assert.match(text, /Agent 1\/3: scout/);
475
+ assert.match(text, /Agent 2\/3: reviewer/);
476
+ assert.doesNotMatch(text, /Step 1: scout/);
477
+ });
478
+
479
+ it("shows only the active parallel group for mixed chains after a serial step", () => {
480
+ const widget = renderSubagentResult!({
481
+ content: [{ type: "text", text: "running" }],
482
+ details: {
483
+ mode: "chain",
484
+ totalSteps: 3,
485
+ currentStepIndex: 1,
486
+ chainAgents: ["planner", "[scout+reviewer]", "writer"],
487
+ results: [{
488
+ agent: "planner",
489
+ task: "plan",
490
+ exitCode: 0,
491
+ messages: [],
492
+ usage: emptyUsage,
493
+ progress: { index: 0, agent: "planner", status: "completed", task: "plan", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 0 },
494
+ }, {
495
+ agent: "scout",
496
+ task: "scan",
497
+ exitCode: 0,
498
+ messages: [],
499
+ usage: emptyUsage,
500
+ progress: { index: 1, agent: "scout", status: "running", task: "scan", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 0 },
501
+ }, {
502
+ agent: "reviewer",
503
+ task: "review",
504
+ exitCode: 0,
505
+ messages: [],
506
+ usage: emptyUsage,
507
+ progress: { index: 2, agent: "reviewer", status: "running", task: "review", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 0 },
508
+ }],
509
+ progress: [
510
+ { index: 0, agent: "planner", status: "completed", task: "plan", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 0 },
511
+ { index: 1, agent: "scout", status: "running", task: "scan", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 0 },
512
+ { index: 2, agent: "reviewer", status: "running", task: "review", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 0 },
513
+ ],
514
+ },
515
+ }, { expanded: false }, theme);
516
+
517
+ const text = widget.render(120).join("\n");
518
+ assert.match(text, /chain · step 2\/3 · parallel group: 2 agents running · 0\/2 done/);
519
+ assert.match(text, /Agent 1\/2: scout/);
520
+ assert.match(text, /Agent 2\/2: reviewer/);
521
+ assert.doesNotMatch(text, /planner/);
522
+ assert.doesNotMatch(text, /Agent 1\/2: planner/);
523
+ });
524
+
525
+ it("uses logical chain progress and agent labels for completed mixed chains", () => {
526
+ const progress = [
527
+ { index: 0, agent: "planner", status: "completed" as const, task: "plan", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 1 },
528
+ { index: 1, agent: "scout", status: "completed" as const, task: "scan", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 1 },
529
+ { index: 2, agent: "reviewer", status: "completed" as const, task: "review", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 1 },
530
+ { index: 3, agent: "writer", status: "completed" as const, task: "write", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 1 },
531
+ ];
532
+ const widget = renderSubagentResult!({
533
+ content: [{ type: "text", text: "done" }],
534
+ details: {
535
+ mode: "chain",
536
+ totalSteps: 3,
537
+ chainAgents: ["planner", "[scout+reviewer]", "writer"],
538
+ results: progress.map((entry) => ({
539
+ agent: entry.agent,
540
+ task: entry.task,
541
+ exitCode: 0,
542
+ messages: [],
543
+ usage: emptyUsage,
544
+ progressSummary: { toolCount: 0, tokens: 0, durationMs: 1 },
545
+ })),
546
+ progress,
547
+ },
548
+ }, { expanded: false }, theme);
549
+
550
+ const text = widget.render(120).join("\n");
551
+ assert.match(text, /chain · step 3\/3/);
552
+ assert.match(text, /Step 1: planner/);
553
+ assert.match(text, /Agent 1\/2: scout/);
554
+ assert.match(text, /Agent 2\/2: reviewer/);
555
+ assert.match(text, /Step 3: writer/);
556
+ assert.doesNotMatch(text, /step 4\/4/);
557
+ });
558
+
559
+ it("keeps serial chain wording for non-parallel steps", () => {
560
+ const widget = renderSubagentResult!({
561
+ content: [{ type: "text", text: "running" }],
562
+ details: {
563
+ mode: "chain",
564
+ totalSteps: 3,
565
+ currentStepIndex: 0,
566
+ chainAgents: ["scout", "reviewer", "worker"],
567
+ results: [{
568
+ agent: "scout",
569
+ task: "scan",
570
+ exitCode: 0,
571
+ messages: [],
572
+ usage: emptyUsage,
573
+ progress: { index: 0, agent: "scout", status: "running", task: "scan", recentTools: [], recentOutput: [], toolCount: 0, tokens: 0, durationMs: 0 },
574
+ }],
575
+ },
576
+ }, { expanded: false }, theme);
577
+
578
+ const text = widget.render(120).join("\n");
579
+ assert.match(text, /chain · step 1\/3/);
580
+ assert.match(text, /Step 1: scout/);
581
+ assert.doesNotMatch(text, /parallel group:/);
582
+ });
583
+ });