@selesai/code 0.8.5 → 0.8.7

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 (231) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/core/agent-session.js +13 -1
  3. package/dist/core/extensions/types.d.ts +2 -0
  4. package/dist/core/provider-composer.js +17 -4
  5. package/dist/core/settings-manager.d.ts +2 -0
  6. package/dist/core/settings-manager.js +3 -0
  7. package/dist/core/system-prompt.js +2 -8
  8. package/dist/core/system-prompt.test.js +6 -0
  9. package/dist/core/tools/index.d.ts +1 -0
  10. package/dist/core/tools/index.js +1 -0
  11. package/dist/core/tools/schema-prune.d.ts +16 -0
  12. package/dist/core/tools/schema-prune.js +39 -0
  13. package/dist/core/tools/schema-prune.test.d.ts +1 -0
  14. package/dist/core/tools/schema-prune.test.js +111 -0
  15. package/dist/defaults/models.json +5 -0
  16. package/dist/defaults/settings.json +1 -0
  17. package/dist/extensions/agent-browser.test.ts +178 -2
  18. package/dist/extensions/agent-browser.ts +2 -0
  19. package/dist/extensions/copy-turn.test.ts +229 -0
  20. package/dist/extensions/copy-turn.ts +11 -0
  21. package/dist/extensions/grep-app/index.test.ts +356 -1
  22. package/dist/extensions/grep-app/index.ts +6 -1
  23. package/dist/extensions/handoff-new.test.ts +351 -161
  24. package/dist/extensions/handoff-new.ts +3 -0
  25. package/dist/extensions/inline-skills.test.ts +93 -0
  26. package/dist/extensions/inline-skills.ts +3 -0
  27. package/dist/extensions/model-prompt-injector/config.json +10 -0
  28. package/dist/extensions/model-prompt-injector/index.test.ts +112 -0
  29. package/dist/extensions/model-prompt-injector/index.ts +151 -0
  30. package/dist/extensions/package.json +1 -0
  31. package/dist/extensions/pi-subagents/CHANGELOG.md +158 -90
  32. package/dist/extensions/pi-subagents/UPSTREAM-V0.50-MAPPING.md +172 -0
  33. package/dist/extensions/pi-subagents/agents/builder.md +5 -2
  34. package/dist/extensions/pi-subagents/agents/commentator.md +1 -1
  35. package/dist/extensions/pi-subagents/agents/oracle.md +7 -5
  36. package/dist/extensions/pi-subagents/agents/reviewer.md +2 -2
  37. package/dist/extensions/pi-subagents/agents/scout.md +1 -1
  38. package/dist/extensions/pi-subagents/agents/worker.md +1 -1
  39. package/dist/extensions/pi-subagents/docs/configuration.md +78 -11
  40. package/dist/extensions/pi-subagents/docs/extension-api.md +36 -0
  41. package/dist/extensions/pi-subagents/docs/missions.md +5 -3
  42. package/dist/extensions/pi-subagents/docs/models.md +1 -1
  43. package/dist/extensions/pi-subagents/docs/observability.md +42 -2
  44. package/dist/extensions/pi-subagents/docs/tool-reference.md +20 -3
  45. package/dist/extensions/pi-subagents/docs/workflows.md +4 -4
  46. package/dist/extensions/pi-subagents/install.mjs +2 -2
  47. package/dist/extensions/pi-subagents/package-lock.json +1470 -1472
  48. package/dist/extensions/pi-subagents/package.json +1 -1
  49. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +1 -1
  50. package/dist/extensions/pi-subagents/skills/pi-subagents/references/constraints-and-recipes.md +5 -4
  51. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +16 -15
  52. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
  53. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +46 -26
  54. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +2 -0
  55. package/dist/extensions/pi-subagents/src/agents/agents.ts +37 -12
  56. package/dist/extensions/pi-subagents/src/api/external-runs.ts +174 -84
  57. package/dist/extensions/pi-subagents/src/api/preflight.ts +13 -7
  58. package/dist/extensions/pi-subagents/src/extension/config.ts +59 -0
  59. package/dist/extensions/pi-subagents/src/extension/index.ts +146 -23
  60. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +34 -6
  61. package/dist/extensions/pi-subagents/src/extension/rpc.ts +5 -1
  62. package/dist/extensions/pi-subagents/src/extension/schemas.ts +31 -30
  63. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +10 -9
  64. package/dist/extensions/pi-subagents/src/inspectors/herdr/actions.ts +13 -8
  65. package/dist/extensions/pi-subagents/src/inspectors/herdr/inspector-runner.ts +16 -3
  66. package/dist/extensions/pi-subagents/src/inspectors/herdr/project-panes.ts +2 -6
  67. package/dist/extensions/pi-subagents/src/inspectors/herdr/shell-command.ts +16 -0
  68. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +5 -4
  69. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +19 -42
  70. package/dist/extensions/pi-subagents/src/missions/goal-driver.ts +3 -1
  71. package/dist/extensions/pi-subagents/src/missions/store.ts +8 -3
  72. package/dist/extensions/pi-subagents/src/runs/background/active-async-capacity.ts +82 -25
  73. package/dist/extensions/pi-subagents/src/runs/background/active-run-index.ts +71 -1
  74. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +73 -43
  75. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +17 -6
  76. package/dist/extensions/pi-subagents/src/runs/background/async-resume.ts +14 -6
  77. package/dist/extensions/pi-subagents/src/runs/background/async-status-snapshot.ts +277 -0
  78. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +11 -3
  79. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +2 -2
  80. package/dist/extensions/pi-subagents/src/runs/background/completion-replay.ts +11 -1
  81. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +21 -6
  82. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +5 -2
  83. package/dist/extensions/pi-subagents/src/runs/background/result-files.ts +437 -0
  84. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +224 -42
  85. package/dist/extensions/pi-subagents/src/runs/background/resume-guidance.ts +27 -7
  86. package/dist/extensions/pi-subagents/src/runs/background/retained-children.ts +76 -19
  87. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +35 -26
  88. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +108 -9
  89. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +54 -28
  90. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +27 -13
  91. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +298 -33
  92. package/dist/extensions/pi-subagents/src/runs/background/subagent-wait.ts +2 -0
  93. package/dist/extensions/pi-subagents/src/runs/background/wait-completions.ts +5 -2
  94. package/dist/extensions/pi-subagents/src/runs/background/wait-subscriptions.ts +2 -1
  95. package/dist/extensions/pi-subagents/src/runs/foreground/async-dismiss-action.ts +2 -1
  96. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +16 -0
  97. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +219 -15
  98. package/dist/extensions/pi-subagents/src/runs/foreground/foreground-history.ts +7 -4
  99. package/dist/extensions/pi-subagents/src/runs/foreground/prompt-audit.ts +4 -3
  100. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +368 -50
  101. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +107 -1
  102. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-runner.ts +4 -0
  103. package/dist/extensions/pi-subagents/src/runs/shared/llm-intent-arbiter.ts +39 -23
  104. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +16 -2
  105. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +66 -62
  106. package/dist/extensions/pi-subagents/src/runs/shared/orca-progress-tabs.ts +376 -0
  107. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +2 -0
  108. package/dist/extensions/pi-subagents/src/runs/shared/subagent-control.ts +15 -0
  109. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +1 -9
  110. package/dist/extensions/pi-subagents/src/runs/shared/subagent-startup-retry.ts +12 -0
  111. package/dist/extensions/pi-subagents/src/runs/shared/tool-timeout.ts +95 -0
  112. package/dist/extensions/pi-subagents/src/shared/agent-stream-options.ts +5 -0
  113. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +0 -4
  114. package/dist/extensions/pi-subagents/src/shared/display-text.ts +50 -0
  115. package/dist/extensions/pi-subagents/src/shared/node-executable.ts +21 -0
  116. package/dist/extensions/pi-subagents/src/shared/session-lineage.ts +71 -0
  117. package/dist/extensions/pi-subagents/src/shared/types.ts +66 -4
  118. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +34 -25
  119. package/dist/extensions/pi-subagents/src/slash/slash-live-state.ts +4 -0
  120. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +160 -45
  121. package/dist/extensions/pi-subagents/src/tui/fleet-transcript.ts +1 -48
  122. package/dist/extensions/pi-subagents/src/tui/fleet.ts +129 -17
  123. package/dist/extensions/pi-subagents/src/tui/render.ts +122 -44
  124. package/dist/extensions/pi-subagents/src/watchdog/permission-arbiter.ts +2 -1
  125. package/dist/extensions/pi-subagents/src/watchdog/review.ts +4 -3
  126. package/dist/extensions/pi-subagents/src/workflows/chat-progress.ts +10 -2
  127. package/dist/extensions/pi-subagents/src/workflows/scripted-workflow.ts +272 -76
  128. package/dist/extensions/pi-subagents/src/workflows/workflow-auto-relaunch.ts +28 -0
  129. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +2 -0
  130. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +2 -2
  131. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +68 -9
  132. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +27 -1
  133. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +1 -1
  134. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +49 -0
  135. package/dist/extensions/pi-subagents/test/integration/external-cli-runner.test.ts +1 -0
  136. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +26 -1
  137. package/dist/extensions/pi-subagents/test/integration/orca-progress-tabs.test.ts +144 -0
  138. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +26 -0
  139. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +369 -98
  140. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +111 -78
  141. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +49 -1
  142. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +12 -0
  143. package/dist/extensions/pi-subagents/test/support/node-command.ts +15 -0
  144. package/dist/extensions/pi-subagents/test/unit/active-async-capacity.test.ts +50 -0
  145. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +28 -0
  146. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +61 -0
  147. package/dist/extensions/pi-subagents/test/unit/agent-stream-options.test.ts +14 -0
  148. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +76 -0
  149. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +2 -0
  150. package/dist/extensions/pi-subagents/test/unit/async-status-snapshot.test.ts +162 -0
  151. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +209 -10
  152. package/dist/extensions/pi-subagents/test/unit/completion-replay.test.ts +41 -1
  153. package/dist/extensions/pi-subagents/test/unit/external-runs.test.ts +135 -42
  154. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +252 -1
  155. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +192 -6
  156. package/dist/extensions/pi-subagents/test/unit/handoff-adoption.test.ts +103 -0
  157. package/dist/extensions/pi-subagents/test/unit/herdr-inspector-bootstrap.test.ts +32 -0
  158. package/dist/extensions/pi-subagents/test/unit/herdr-shell-command.test.ts +59 -0
  159. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +91 -0
  160. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +23 -4
  161. package/dist/extensions/pi-subagents/test/unit/llm-intent-arbiter.test.ts +56 -1
  162. package/dist/extensions/pi-subagents/test/unit/mission-goal-driver.test.ts +67 -3
  163. package/dist/extensions/pi-subagents/test/unit/mission-lifecycle.test.ts +1 -1
  164. package/dist/extensions/pi-subagents/test/unit/mission-store.test.ts +14 -0
  165. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +18 -0
  166. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +2 -2
  167. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +25 -2
  168. package/dist/extensions/pi-subagents/test/unit/node-executable.test.ts +26 -0
  169. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +8 -0
  170. package/dist/extensions/pi-subagents/test/unit/orca-progress-tabs.test.ts +348 -0
  171. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +5 -2
  172. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +1 -1
  173. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +34 -0
  174. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +6 -1
  175. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +92 -0
  176. package/dist/extensions/pi-subagents/test/unit/result-files.test.ts +175 -0
  177. package/dist/extensions/pi-subagents/test/unit/retained-children.test.ts +127 -17
  178. package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +44 -0
  179. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +247 -0
  180. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +107 -2
  181. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +5 -8
  182. package/dist/extensions/pi-subagents/test/unit/scripted-workflow.test.ts +456 -4
  183. package/dist/extensions/pi-subagents/test/unit/session-lineage.test.ts +73 -0
  184. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +128 -0
  185. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +28 -0
  186. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +16 -7
  187. package/dist/extensions/pi-subagents/test/unit/subagent-startup-retry.test.ts +15 -0
  188. package/dist/extensions/pi-subagents/test/unit/subagent-wait.test.ts +79 -15
  189. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +12 -12
  190. package/dist/extensions/pi-subagents/test/unit/tool-timeout.test.ts +109 -0
  191. package/dist/extensions/pi-subagents/test/unit/wait-subscriptions.test.ts +34 -0
  192. package/dist/extensions/pi-subagents/test/unit/workflow-auto-relaunch.test.ts +45 -0
  193. package/dist/extensions/pi-subagents/test/unit/workflow-chat-progress.test.ts +94 -1
  194. package/dist/extensions/pi-subagents/test/unit/workflow-launch-params.test.ts +42 -2
  195. package/dist/extensions/ponytail/index.js +11 -0
  196. package/dist/extensions/ponytail/ponytail-config.cjs +2 -0
  197. package/dist/extensions/ponytail/ponytail-instructions.cjs +6 -0
  198. package/dist/extensions/ponytail/test/extension.test.js +274 -140
  199. package/dist/extensions/ponytail/test/helpers.test.js +280 -92
  200. package/dist/extensions/question/index.ts +33 -0
  201. package/dist/extensions/question/question-list.ts +28 -0
  202. package/dist/extensions/question/row-layout.ts +3 -0
  203. package/dist/extensions/question/tests/batch.test.ts +103 -65
  204. package/dist/extensions/question/tests/exp.test.ts +70 -0
  205. package/dist/extensions/question/tests/exp2.test.ts +59 -0
  206. package/dist/extensions/question/tests/exp3.test.ts +47 -0
  207. package/dist/extensions/question/tests/helpers.test.ts +75 -35
  208. package/dist/extensions/question/tests/question-list.test.ts +640 -198
  209. package/dist/extensions/question/tests/row-layout.test.ts +193 -114
  210. package/dist/extensions/question/tests/schemas.test.ts +42 -19
  211. package/dist/extensions/question/tests/selection-mode.test.ts +54 -0
  212. package/dist/extensions/question/tests/shortcuts.test.ts +106 -84
  213. package/dist/extensions/question/tests/wizard.test.ts +771 -50
  214. package/dist/extensions/question/tests/zz-ig3.test.ts +20 -0
  215. package/dist/extensions/question/tests/zz-probe.test.ts +29 -0
  216. package/dist/extensions/rtk.test.ts +180 -1
  217. package/dist/extensions/tokenin-onboarding.ts +3 -0
  218. package/dist/extensions/undo.test.ts +720 -0
  219. package/dist/extensions/undo.ts +6 -0
  220. package/dist/extensions/web-agent-onboarding.test.ts +415 -0
  221. package/dist/extensions/workflow/extension.ts +3 -3
  222. package/dist/extensions/workflow/modes.ts +41 -19
  223. package/dist/skills/pi-subagents/SKILL.md +1 -1
  224. package/dist/skills/pi-subagents/references/constraints-and-recipes.md +5 -4
  225. package/dist/skills/pi-subagents/references/execution-controls.md +16 -15
  226. package/dist/skills/pi-subagents/references/management-authoring-rpc.md +5 -5
  227. package/dist/skills/pi-subagents/references/prompting-and-roles.md +46 -26
  228. package/docs/plans/workflow-autoloop-reference.md +1 -2
  229. package/docs/plans/workflow-handoff-carryover.md +108 -0
  230. package/docs/settings.md +6 -0
  231. package/package.json +4 -1
@@ -0,0 +1,348 @@
1
+ import assert from "node:assert/strict";
2
+ import { spawn } from "node:child_process";
3
+ import { createHash } from "node:crypto";
4
+ import { afterEach, test } from "node:test";
5
+ import * as fs from "node:fs";
6
+ import * as os from "node:os";
7
+ import * as path from "node:path";
8
+ import { createOrcaProgressTab, resolveOrcaCommand, resolvePiSessionId } from "../../src/runs/shared/orca-progress-tabs.ts";
9
+ import { TEMP_ROOT_DIR } from "../../src/shared/types.ts";
10
+ import { writeNodeCommand } from "../support/node-command.ts";
11
+
12
+ const tempDirs: string[] = [];
13
+
14
+ function removeProgressFiles(prefix: string): void {
15
+ const root = path.join(TEMP_ROOT_DIR, "orca-progress");
16
+ if (!fs.existsSync(root)) return;
17
+ for (const name of fs.readdirSync(root)) {
18
+ if (name.startsWith(prefix)) fs.rmSync(path.join(root, name), { force: true });
19
+ }
20
+ }
21
+
22
+ afterEach(() => {
23
+ const progressRoot = path.join(TEMP_ROOT_DIR, "orca-progress");
24
+ for (const dir of tempDirs.splice(0)) {
25
+ const key = createHash("sha256").update(path.resolve(dir)).digest("hex").slice(0, 20);
26
+ fs.rmSync(path.join(progressRoot, `counter-${key}`), { force: true });
27
+ fs.rmSync(path.join(progressRoot, `counter-${key}.lock`), { recursive: true, force: true });
28
+ fs.rmSync(dir, { recursive: true, force: true });
29
+ }
30
+ removeProgressFiles("progress-");
31
+ removeProgressFiles("disabled-run-");
32
+ removeProgressFiles("standalone-pi-");
33
+ });
34
+
35
+ function tempDir(): string {
36
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-orca-tabs-test-"));
37
+ tempDirs.push(dir);
38
+ return dir;
39
+ }
40
+
41
+ async function waitForFile(file: string): Promise<void> {
42
+ const deadline = Date.now() + 5_000;
43
+ while (!fs.existsSync(file)) {
44
+ if (Date.now() >= deadline) throw new Error(`Timed out waiting for ${file}`);
45
+ await new Promise((resolve) => setTimeout(resolve, 20));
46
+ }
47
+ }
48
+
49
+ function progressFile(prefix: string, suffix: ".log" | ".done"): string {
50
+ const root = path.join(TEMP_ROOT_DIR, "orca-progress");
51
+ const name = fs.readdirSync(root).find((candidate) => candidate.startsWith(prefix) && candidate.endsWith(suffix));
52
+ assert.ok(name, `Expected ${suffix} file for ${prefix}`);
53
+ return path.join(root, name);
54
+ }
55
+
56
+ function captureCommand(command: string, cwd: string): Promise<string> {
57
+ return new Promise((resolve, reject) => {
58
+ const child = spawn(command, { cwd, shell: true, stdio: ["ignore", "pipe", "pipe"] });
59
+ let output = "";
60
+ child.stdout.setEncoding("utf-8");
61
+ child.stdout.on("data", (chunk: string) => { output += chunk; });
62
+ child.once("error", reject);
63
+ child.once("close", (code) => code === 0 ? resolve(output) : reject(new Error(`Viewer exited ${code}: ${output}`)));
64
+ });
65
+ }
66
+
67
+ function writeCaptureOrca(dir: string): string {
68
+ return writeNodeCommand(dir, "orca", "require('fs').writeFileSync(process.env.ORCA_TEST_CAPTURE, JSON.stringify(process.argv.slice(2)))");
69
+ }
70
+
71
+ test("Orca progress tabs are disabled on Windows", { skip: process.platform === "win32" ? undefined : "Windows-only platform boundary" }, () => {
72
+ const dir = tempDir();
73
+ assert.equal(createOrcaProgressTab({
74
+ cwd: dir,
75
+ runId: "windows-disabled",
76
+ agent: "worker",
77
+ index: 0,
78
+ config: { enabled: true },
79
+ command: process.execPath,
80
+ }), undefined);
81
+ });
82
+
83
+ test("resolveOrcaCommand only returns executable commands", () => {
84
+ const dir = tempDir();
85
+ const executable = writeNodeCommand(dir, "orca", "process.exit(0)");
86
+ assert.equal(resolveOrcaCommand({ PATH: dir, PATHEXT: ".CMD" }), executable);
87
+ assert.equal(resolveOrcaCommand({ PATH: "", SELESAI_SUBAGENT_ORCA_BINARY: path.join(dir, "missing") }), undefined);
88
+ });
89
+
90
+ test("an unavailable Orca command leaves native execution untouched", () => {
91
+ const dir = tempDir();
92
+ assert.equal(createOrcaProgressTab({
93
+ cwd: dir,
94
+ runId: "missing-orca",
95
+ agent: "worker",
96
+ index: 0,
97
+ config: { enabled: true },
98
+ env: { PATH: "", SELESAI_SUBAGENT_ORCA_BINARY: path.join(dir, "missing") },
99
+ }), undefined);
100
+ });
101
+
102
+ test("standalone Pi executables use PATH Node for the watchdog and viewer", { skip: process.platform === "win32" ? "Orca progress tabs are not supported on Windows" : undefined }, async () => {
103
+ const dir = tempDir();
104
+ const capture = path.join(dir, "capture.json");
105
+ const fakeOrca = writeCaptureOrca(dir);
106
+ const fakePi = path.join(dir, "pi");
107
+ const originalExecPath = process.execPath;
108
+ try {
109
+ process.execPath = fakePi;
110
+ const tab = createOrcaProgressTab({
111
+ cwd: dir,
112
+ runId: "standalone-pi",
113
+ agent: "worker",
114
+ index: 0,
115
+ config: { enabled: true },
116
+ command: fakeOrca,
117
+ env: { ...process.env, ORCA_TEST_CAPTURE: capture },
118
+ });
119
+ assert.ok(tab);
120
+ tab.finish("failed");
121
+ await waitForFile(capture);
122
+ const args = JSON.parse(fs.readFileSync(capture, "utf-8")) as string[];
123
+ const viewer = args[args.indexOf("--command") + 1]!;
124
+ assert.match(viewer, /^'node' '-e' /);
125
+ assert.equal(viewer.includes(fakePi), false);
126
+ assert.match(await captureCommand(viewer, dir), /failed/);
127
+ } finally {
128
+ process.execPath = originalExecPath;
129
+ }
130
+ });
131
+
132
+ test("hung Orca terminal creation does not delay the owning process", { skip: process.platform === "win32" ? "Orca progress tabs are not supported on Windows" : undefined }, async () => {
133
+ const dir = tempDir();
134
+ const fakeOrca = writeNodeCommand(dir, "orca", "require('fs').writeFileSync(process.env.ORCA_TEST_PID, String(process.pid));setInterval(()=>{},1000)");
135
+ const pidFile = path.join(dir, "orca.pid");
136
+ const moduleUrl = new URL("../../src/runs/shared/orca-progress-tabs.ts", import.meta.url).href;
137
+ const ownerScript = `import {createOrcaProgressTab} from ${JSON.stringify(moduleUrl)};const tab=createOrcaProgressTab({cwd:${JSON.stringify(dir)},runId:'progress-hung-owner',agent:'worker',index:0,config:{enabled:true},command:${JSON.stringify(fakeOrca)},env:{...process.env,ORCA_TEST_PID:${JSON.stringify(pidFile)}}});if(!tab)throw new Error('tab unavailable');`;
138
+ const startedAt = Date.now();
139
+ const owner = spawn(process.execPath, ["--experimental-strip-types", "--input-type=module", "--eval", ownerScript], { cwd: dir, stdio: "ignore" });
140
+ const ownerClosed = new Promise<number | null>((resolve, reject) => {
141
+ owner.once("error", reject);
142
+ owner.once("close", resolve);
143
+ });
144
+ let fakePid: number | undefined;
145
+ try {
146
+ assert.equal(await ownerClosed, 0);
147
+ assert.ok(Date.now() - startedAt < 2_000, "the Orca observer delayed runner completion");
148
+ await waitForFile(pidFile);
149
+ fakePid = Number.parseInt(fs.readFileSync(pidFile, "utf-8"), 10);
150
+ process.kill(fakePid, 0);
151
+ } finally {
152
+ if (fakePid !== undefined) {
153
+ try { process.kill(fakePid, "SIGKILL"); } catch { /* already stopped */ }
154
+ }
155
+ if (owner.exitCode === null) owner.kill("SIGKILL");
156
+ }
157
+ });
158
+
159
+ test("malformed optional observer metadata cannot break child execution", { skip: process.platform === "win32" ? "Orca progress tabs are not supported on Windows" : undefined }, async () => {
160
+ const dir = tempDir();
161
+ const capture = path.join(dir, "capture.json");
162
+ const fakeOrca = writeCaptureOrca(dir);
163
+ const tab = createOrcaProgressTab({
164
+ cwd: dir,
165
+ runId: undefined,
166
+ agent: undefined,
167
+ index: undefined,
168
+ config: { enabled: true },
169
+ command: fakeOrca,
170
+ env: { ...process.env, ORCA_TEST_CAPTURE: capture },
171
+ } as never);
172
+ assert.ok(tab);
173
+ tab.finish("completed");
174
+ await waitForFile(capture);
175
+ const args = JSON.parse(fs.readFileSync(capture, "utf-8")) as string[];
176
+ assert.equal(args[args.indexOf("--title") + 1], "subagent · subagent · 1");
177
+ removeProgressFiles("run-0-");
178
+ });
179
+
180
+ test("disabled Orca progress tabs do not invoke Orca", async () => {
181
+ const dir = tempDir();
182
+ const capture = path.join(dir, "capture.json");
183
+ const fakeOrca = writeCaptureOrca(dir);
184
+ const tab = createOrcaProgressTab({
185
+ cwd: dir,
186
+ runId: "disabled-run",
187
+ agent: "worker",
188
+ index: 0,
189
+ config: { enabled: false },
190
+ command: fakeOrca,
191
+ env: { ...process.env, ORCA_TEST_CAPTURE: capture },
192
+ });
193
+ assert.equal(tab, undefined);
194
+ await new Promise((resolve) => setTimeout(resolve, 100));
195
+ assert.equal(fs.existsSync(capture), false);
196
+ });
197
+
198
+ test("enabled tabs use a worktree sequence and successful Pi sessions get cleanup guidance", { skip: process.platform === "win32" ? "Orca progress tabs are not supported on Windows" : undefined }, async () => {
199
+ const dir = tempDir();
200
+ const capture = path.join(dir, "capture.json");
201
+ const secondCapture = path.join(dir, "capture-2.json");
202
+ const fakeOrca = writeCaptureOrca(dir);
203
+ fs.mkdirSync(path.join(dir, ".git"));
204
+ const runId = `progress-${Date.now()}`;
205
+ const tab = createOrcaProgressTab({
206
+ cwd: dir,
207
+ runId,
208
+ agent: "worker",
209
+ index: 2,
210
+ config: { enabled: true },
211
+ command: fakeOrca,
212
+ env: { ...process.env, ORCA_TEST_CAPTURE: capture },
213
+ });
214
+ assert.ok(tab);
215
+ tab.append("starting\n");
216
+ tab.event({ type: "tool_execution_start", toolName: "read", args: { path: "README.md" } });
217
+ tab.event({ type: "message_end", message: { role: "assistant", content: [{ type: "text", text: "done output" }] } as never });
218
+ const sessionId = "019ffd40-4859-7015-94e4-7d15c31885ef";
219
+ const sessionFile = path.join(dir, `session file's ${sessionId}.jsonl`);
220
+ fs.writeFileSync(sessionFile, `${JSON.stringify({ type: "session", version: 3, id: sessionId })}\n`);
221
+ assert.equal(resolvePiSessionId(sessionFile), sessionId);
222
+ assert.equal(resolvePiSessionId(path.join(dir, `missing_${sessionId}.jsonl`)), undefined);
223
+ tab.finish("completed", sessionFile);
224
+
225
+ await waitForFile(capture);
226
+ const args = JSON.parse(fs.readFileSync(capture, "utf-8")) as string[];
227
+ assert.deepEqual(args.slice(0, 2), ["terminal", "create"]);
228
+ assert.equal(args[args.indexOf("--worktree") + 1], `path:${path.resolve(dir)}`);
229
+ assert.equal(args[args.indexOf("--title") + 1], "subagent · worker · 1");
230
+ const viewer = args[args.indexOf("--command") + 1];
231
+ assert.ok(viewer.includes(process.execPath));
232
+ assert.doesNotMatch(viewer, /(?:^|;)\s*exec\s/);
233
+ assert.doesNotMatch(viewer, /(?:&|;)\s*exit(?:\s|$)/);
234
+
235
+ const progressDir = path.join(TEMP_ROOT_DIR, "orca-progress");
236
+ const log = fs.readdirSync(progressDir).find((name) => name.startsWith(`${runId}-2-`) && name.endsWith(".log"));
237
+ assert.ok(log);
238
+ const text = fs.readFileSync(path.join(progressDir, log), "utf-8");
239
+ assert.match(text, /starting/);
240
+ assert.match(text, /› read: README\.md/);
241
+ assert.match(text, /done output/);
242
+ const quotedSessionFile = `'${fs.realpathSync(sessionFile).replace(/'/g, `'"'"'`)}'`;
243
+ assert.ok(text.includes(`completed. To remove the Pi session of this subagent, run rm -- ${quotedSessionFile}`));
244
+ assert.doesNotMatch(text, /find ~\/\.pi\/agent\/sessions/);
245
+
246
+ const nestedCwd = path.join(dir, "packages", "app");
247
+ fs.mkdirSync(nestedCwd, { recursive: true });
248
+ const secondTab = createOrcaProgressTab({
249
+ cwd: nestedCwd,
250
+ runId: `${runId}-second`,
251
+ agent: "worker",
252
+ index: 0,
253
+ config: { enabled: true },
254
+ command: fakeOrca,
255
+ env: { ...process.env, ORCA_TEST_CAPTURE: secondCapture },
256
+ });
257
+ assert.ok(secondTab);
258
+ secondTab.finish("failed", sessionFile);
259
+ await waitForFile(secondCapture);
260
+ const secondArgs = JSON.parse(fs.readFileSync(secondCapture, "utf-8")) as string[];
261
+ assert.equal(secondArgs[secondArgs.indexOf("--title") + 1], "subagent · worker · 2");
262
+ const secondLog = fs.readdirSync(progressDir).find((name) => name.startsWith(`${runId}-second-0-`) && name.endsWith(".log"));
263
+ assert.ok(secondLog);
264
+ const secondText = fs.readFileSync(path.join(progressDir, secondLog), "utf-8");
265
+ assert.match(secondText, /failed/);
266
+ assert.doesNotMatch(secondText, /To remove the Pi session/);
267
+ });
268
+
269
+ test("viewer strips split terminal control sequences across poll ticks", { skip: process.platform === "win32" ? "Orca progress tabs are not supported on Windows" : undefined }, async () => {
270
+ const dir = tempDir();
271
+ const capture = path.join(dir, "capture.json");
272
+ const fakeOrca = writeCaptureOrca(dir);
273
+ const runId = `progress-sanitize-${Date.now()}`;
274
+ const tab = createOrcaProgressTab({
275
+ cwd: dir,
276
+ runId,
277
+ agent: "worker",
278
+ index: 0,
279
+ config: { enabled: true },
280
+ command: fakeOrca,
281
+ env: { ...process.env, ORCA_TEST_CAPTURE: capture },
282
+ });
283
+ assert.ok(tab);
284
+ await waitForFile(capture);
285
+ const args = JSON.parse(fs.readFileSync(capture, "utf-8")) as string[];
286
+ const viewer = args[args.indexOf("--command") + 1]!;
287
+ const outputPromise = captureCommand(viewer, dir);
288
+ await new Promise((resolve) => setTimeout(resolve, 200));
289
+ tab.append("safe CSI \u001b[");
290
+ await new Promise((resolve) => setTimeout(resolve, 200));
291
+ tab.append("31mred OSC \u001b]0;secret");
292
+ await new Promise((resolve) => setTimeout(resolve, 200));
293
+ tab.append(" title\u0007visible\u0000\u0001\r\t\u007f\n");
294
+ tab.finish("failed");
295
+ const output = await outputPromise;
296
+ assert.match(output, /safe CSI red OSC visible\n/);
297
+ assert.doesNotMatch(output, /\u001b|31m|secret|title|\u0000|\u0001|\r|\t|\u007f/);
298
+ });
299
+
300
+ test("mirror output keeps small writes that hit stream backpressure before the byte limit", { skip: process.platform === "win32" ? "Orca progress tabs are not supported on Windows" : undefined }, async () => {
301
+ const dir = tempDir();
302
+ const capture = path.join(dir, "capture.json");
303
+ const fakeOrca = writeCaptureOrca(dir);
304
+ const runId = `progress-backpressure-${Date.now()}`;
305
+ const tab = createOrcaProgressTab({
306
+ cwd: dir,
307
+ runId,
308
+ agent: "worker",
309
+ index: 0,
310
+ config: { enabled: true },
311
+ command: fakeOrca,
312
+ env: { ...process.env, ORCA_TEST_CAPTURE: capture },
313
+ });
314
+ assert.ok(tab);
315
+ for (let index = 0; index < 2_000; index++) tab.append(`line-${index.toString().padStart(4, "0")} ${"x".repeat(80)}\n`);
316
+ tab.finish("completed");
317
+ const log = progressFile(`${runId}-0-`, ".log");
318
+ await waitForFile(log.replace(/\.log$/, ".done"));
319
+ const text = fs.readFileSync(log, "utf-8");
320
+ assert.match(text, /line-1999/);
321
+ assert.doesNotMatch(text, /progress mirror truncated/);
322
+ });
323
+
324
+ test("mirror output truncates at a finite byte bound", { skip: process.platform === "win32" ? "Orca progress tabs are not supported on Windows" : undefined }, async () => {
325
+ const dir = tempDir();
326
+ const capture = path.join(dir, "capture.json");
327
+ const fakeOrca = writeCaptureOrca(dir);
328
+ const runId = `progress-bounded-${Date.now()}`;
329
+ const tab = createOrcaProgressTab({
330
+ cwd: dir,
331
+ runId,
332
+ agent: "worker",
333
+ index: 0,
334
+ config: { enabled: true },
335
+ command: fakeOrca,
336
+ env: { ...process.env, ORCA_TEST_CAPTURE: capture },
337
+ });
338
+ assert.ok(tab);
339
+ tab.append("x".repeat(2 * 1024 * 1024));
340
+ tab.append("must be dropped");
341
+ tab.finish("completed");
342
+ const log = progressFile(`${runId}-0-`, ".log");
343
+ await waitForFile(log.replace(/\.log$/, ".done"));
344
+ assert.ok(fs.statSync(log).size <= 1024 * 1024);
345
+ const text = fs.readFileSync(log, "utf-8");
346
+ assert.match(text, /progress mirror truncated at 1048576 bytes/);
347
+ assert.doesNotMatch(text, /must be dropped/);
348
+ });
@@ -69,8 +69,11 @@ test("published extension APIs use supported package entrypoints", async () => {
69
69
  assert.equal(backgroundWork.BACKGROUND_WORK_PROTOCOL_VERSION, 1);
70
70
  assert.equal(backgroundWork.BACKGROUND_WORK_REGISTRY_KEY, "pi-subagents.background-work.v1");
71
71
  const externalRuns = await import("pi-subagents/external-runs");
72
- assert.equal(externalRuns.EXTERNAL_RUN_REGISTRY_VERSION, 1);
73
- assert.equal(typeof externalRuns.registerExternalRunProvider, "function");
72
+ assert.equal(externalRuns.EXTERNAL_RUN_REGISTRY_VERSION, 2);
73
+ assert.equal(typeof externalRuns.registerExternalRun, "function");
74
+ assert.equal(typeof externalRuns.updateExternalRun, "function");
75
+ assert.equal(typeof externalRuns.snapshotExternalRuns, "function");
76
+ assert.equal(typeof externalRuns.unregisterExternalRun, "function");
74
77
  const capability = await import("pi-subagents/capability-ceiling");
75
78
  assert.equal(capability.SUBAGENT_CAPABILITY_CEILING_VERSION, 1);
76
79
  assert.equal(capability.SUBAGENT_CAPABILITY_CEILING_REGISTRY_KEY, "pi-subagents.capability-ceiling.v1");
@@ -825,7 +825,7 @@ describe("buildPiArgs system prompt mode wiring", () => {
825
825
  tools: reviewer.tools,
826
826
  });
827
827
 
828
- assert.equal(args[args.indexOf("--tools") + 1], "read,grep,find,ls,intercom");
828
+ assert.equal(args[args.indexOf("--tools") + 1], "read,grep,find,ls");
829
829
  assert.doesNotMatch(args[args.indexOf("--tools") + 1] ?? "", /\b(?:bash|edit|write)\b/);
830
830
  });
831
831
 
@@ -214,6 +214,7 @@ Package skill content.
214
214
  it("resolves configured artifact directory preferences", () => {
215
215
  const sessionFile = path.join(agentDir, "sessions", "session-1", "session.jsonl");
216
216
 
217
+ // Selesai fork default: project-scoped .pi-subagents artifacts.
217
218
  assert.equal(getArtifactsDir(sessionFile, cwd), getProjectArtifactsDir(cwd));
218
219
  assert.equal(getArtifactsDir(sessionFile, cwd, "project"), getProjectArtifactsDir(cwd));
219
220
  assert.equal(getArtifactsDir(sessionFile, cwd, "session"), path.join(path.dirname(sessionFile), "subagent-artifacts"));
@@ -241,6 +242,39 @@ Package skill content.
241
242
  assert.throws(() => updateConfig((config) => config), /config\.fleetKeybindings\.pageUp entries must be non-empty strings/);
242
243
  });
243
244
 
245
+ it("loads and validates the foreground detach shortcut", () => {
246
+ const configPath = path.join(agentDir, "extensions", "subagent", "config.json");
247
+ writeFile(configPath, JSON.stringify({ foregroundDetachShortcut: "ctrl+b" }));
248
+ assert.equal(loadConfig().foregroundDetachShortcut, "ctrl+b");
249
+
250
+ writeFile(configPath, JSON.stringify({ foregroundDetachShortcut: "banana" }));
251
+ assert.throws(() => updateConfig((config) => config), /config\.foregroundDetachShortcut must be a valid keybinding string/);
252
+ });
253
+
254
+ it("loads and validates main-window renderer density config", () => {
255
+ const configPath = path.join(agentDir, "extensions", "subagent", "config.json");
256
+ writeFile(configPath, JSON.stringify({ mainWindowRenderer: { horizontalSpacing: 0, compactResultMaxLines: 4 } }));
257
+ assert.deepEqual(loadConfig().mainWindowRenderer, { horizontalSpacing: 0, compactResultMaxLines: 4 });
258
+
259
+ writeFile(configPath, JSON.stringify({ mainWindowRenderer: { horizontalSpacing: -1 } }));
260
+ assert.throws(() => updateConfig((config) => config), /config\.mainWindowRenderer\.horizontalSpacing must be an integer from 0 to 4/);
261
+
262
+ writeFile(configPath, JSON.stringify({ mainWindowRenderer: { compactResultMaxLines: 0 } }));
263
+ assert.throws(() => updateConfig((config) => config), /config\.mainWindowRenderer\.compactResultMaxLines must be a positive integer/);
264
+ });
265
+
266
+ it("loads and validates experimental Orca progress-tab config", () => {
267
+ const configPath = path.join(agentDir, "extensions", "subagent", "config.json");
268
+ writeFile(configPath, JSON.stringify({ orcaProgressTabs: { enabled: true } }));
269
+ assert.deepEqual(loadConfig().orcaProgressTabs, { enabled: true });
270
+
271
+ writeFile(configPath, JSON.stringify({ orcaProgressTabs: { enabled: "yes" } }));
272
+ assert.throws(() => updateConfig((config) => config), /config\.orcaProgressTabs\.enabled must be a boolean/);
273
+
274
+ writeFile(configPath, JSON.stringify({ orcaProgressTabs: { enabled: true, focus: true } }));
275
+ assert.throws(() => updateConfig((config) => config), /config\.orcaProgressTabs\.focus is not supported/);
276
+ });
277
+
244
278
  it("hardens and redacts existing run history while recording", () => {
245
279
  const historyPath = path.join(agentDir, "run-history.jsonl");
246
280
  fs.mkdirSync(agentDir, { recursive: true, mode: 0o755 });
@@ -5,7 +5,12 @@ import { normalizePublicSubagentExecution } from "../../src/extension/public-exe
5
5
  describe("public subagent execution normalization", () => {
6
6
  it("accepts all four execution modes, management, and direct-field schedules", () => {
7
7
  assert.deepEqual(normalizePublicSubagentExecution({ workflowScript: "return 1" }), { ok: true, params: { workflowScript: "return 1" } });
8
- assert.deepEqual(normalizePublicSubagentExecution({ agent: "worker", task: "work" }), { ok: true, params: { agent: "worker", task: "work" } });
8
+ const single = normalizePublicSubagentExecution({ agent: "worker", task: "work" });
9
+ assert.equal(single.ok, true);
10
+ if (single.ok) {
11
+ assert.equal(typeof (single.params as { workflowScript?: unknown }).workflowScript, "string");
12
+ assert.match((single.params as { workflowScript: string }).workflowScript, /runs\.run\("main", \{"agent":"worker","task":"work","output":true\}\)/);
13
+ }
9
14
  assert.deepEqual(normalizePublicSubagentExecution({ tasks: [{ agent: "worker" }] }), { ok: true, params: { tasks: [{ agent: "worker" }] } });
10
15
  assert.deepEqual(normalizePublicSubagentExecution({ chain: [{ agent: "worker" }] }), { ok: true, params: { chain: [{ agent: "worker" }] } });
11
16
  assert.deepEqual(normalizePublicSubagentExecution({ action: " list " }), { ok: true, params: { action: "list" } });
@@ -80,6 +80,50 @@ test("multiline rendering omits two-column graphemes at one-column width", () =>
80
80
  }
81
81
  });
82
82
 
83
+ test("running single-subagent cards show the configured detach shortcut", () => {
84
+ const running = {
85
+ ...result("reviewer", ""),
86
+ progress: { status: "running", index: 0, agent: "reviewer", toolCount: 0, tokens: 0, durationMs: 0 },
87
+ };
88
+ const toolResult = {
89
+ content: [{ type: "text", text: "running" }],
90
+ details: { mode: "single", results: [running] },
91
+ };
92
+
93
+ const configured = componentText(renderSubagentResult(
94
+ toolResult as never,
95
+ { expanded: false },
96
+ theme as any,
97
+ undefined,
98
+ undefined,
99
+ "ctrl+b",
100
+ ));
101
+ assert.match(configured, /Ctrl\+B to run in background/);
102
+
103
+ const unconfigured = componentText(renderSubagentResult(toolResult as never, { expanded: false }, theme as any));
104
+ assert.doesNotMatch(unconfigured, /run in background/);
105
+
106
+ const alreadyBackground = componentText(renderSubagentResult(
107
+ { ...toolResult, details: { ...toolResult.details, asyncId: "async-123" } } as never,
108
+ { expanded: false },
109
+ theme as any,
110
+ undefined,
111
+ undefined,
112
+ "ctrl+b",
113
+ ));
114
+ assert.doesNotMatch(alreadyBackground, /run in background/);
115
+
116
+ const pendingBackground = componentText(renderSubagentResult(
117
+ { ...toolResult, details: { ...toolResult.details, background: true } } as never,
118
+ { expanded: false },
119
+ theme as any,
120
+ undefined,
121
+ undefined,
122
+ "ctrl+b",
123
+ ));
124
+ assert.doesNotMatch(pendingBackground, /run in background/);
125
+ });
126
+
83
127
  test("compact chain rendering uses workflow graph spans for dynamic fanout results", () => {
84
128
  const component = renderSubagentResult({
85
129
  content: [{ type: "text", text: "done" }],
@@ -238,3 +282,51 @@ test("static sequential and static parallel chain rendering keep existing labels
238
282
  assert.match(parallel, /Agent 2\/2: auditor/);
239
283
  assert.match(parallel, /Step 3: writer/);
240
284
  });
285
+
286
+ test("main-window renderer config removes compact result indentation without changing status glyphs", () => {
287
+ const component = renderSubagentResult({
288
+ content: [{ type: "text", text: "done" }],
289
+ details: {
290
+ mode: "parallel",
291
+ results: [result("scout", "a"), { ...result("reviewer", ""), exitCode: 1, error: "failed" }],
292
+ },
293
+ }, { expanded: false }, theme as any, undefined, { horizontalSpacing: 0 });
294
+
295
+ const text = componentText(component);
296
+ assert.match(text, /^✗ parallel/m);
297
+ assert.match(text, /^✓ Agent 1\/2: scout/m);
298
+ assert.match(text, /^✗ Agent 2\/2: reviewer/m);
299
+ assert.match(text, /^⎿ Error: failed/m);
300
+ });
301
+
302
+ test("main-window renderer config caps only collapsed rich result rows", () => {
303
+ const rendered = renderSubagentResult({
304
+ content: [{ type: "text", text: "done" }],
305
+ details: {
306
+ mode: "parallel",
307
+ results: [
308
+ result("scout", "a"),
309
+ result("reviewer", "b"),
310
+ result("writer", "c"),
311
+ ],
312
+ },
313
+ }, { expanded: false }, theme as any, undefined, { compactResultMaxLines: 3 }).render(120);
314
+
315
+ assert.equal(rendered.length, 3);
316
+ assert.match(rendered[2]!, /rows hidden/);
317
+
318
+ const expanded = renderSubagentResult({
319
+ content: [{ type: "text", text: "done" }],
320
+ details: {
321
+ mode: "parallel",
322
+ results: [
323
+ result("scout", "a"),
324
+ result("reviewer", "b"),
325
+ result("writer", "c"),
326
+ ],
327
+ },
328
+ }, { expanded: true }, theme as any, undefined, { compactResultMaxLines: 3 }).render(120);
329
+
330
+ assert.ok(expanded.length > 3);
331
+ assert.doesNotMatch(expanded.join("\n"), /rows hidden/);
332
+ });