@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,45 @@
1
+ import assert from "node:assert/strict";
2
+ import { describe, it } from "node:test";
3
+ import {
4
+ DEFAULT_MAX_WORKFLOW_AUTO_RELAUNCHES,
5
+ resolveAutoRelaunchDecision,
6
+ resolveMaxWorkflowAutoRelaunches,
7
+ } from "../../src/workflows/workflow-auto-relaunch.ts";
8
+
9
+ describe("workflow auto-relaunch policy", () => {
10
+ it("relaunches on a budget result while under the cap", () => {
11
+ const decision = resolveAutoRelaunchDecision({ result: "budget", rounds: 3 }, 2, 12);
12
+ assert.deepEqual(decision, { relaunch: true, capped: false });
13
+ });
14
+
15
+ it("does not relaunch a clean result", () => {
16
+ const decision = resolveAutoRelaunchDecision({ result: "clean", rounds: 3 }, 0, 12);
17
+ assert.deepEqual(decision, { relaunch: false, capped: false });
18
+ });
19
+
20
+ it("does not relaunch non-workflow values", () => {
21
+ for (const value of [undefined, null, 5, "budget", ["budget"], { result: "failed" }]) {
22
+ const decision = resolveAutoRelaunchDecision(value, 0, 12);
23
+ assert.deepEqual(decision, { relaunch: false, capped: false });
24
+ }
25
+ });
26
+
27
+ it("marks the cap as reached when the relaunch count equals the cap", () => {
28
+ const decision = resolveAutoRelaunchDecision({ result: "budget" }, 12, 12);
29
+ assert.deepEqual(decision, { relaunch: false, capped: true });
30
+ });
31
+
32
+ it("relaunches indefinitely when the cap is unlimited", () => {
33
+ const decision = resolveAutoRelaunchDecision({ result: "budget" }, 50, undefined);
34
+ assert.deepEqual(decision, { relaunch: true, capped: false });
35
+ });
36
+
37
+ it("resolves the configured cap with a safe default and 0 as unlimited", () => {
38
+ assert.equal(resolveMaxWorkflowAutoRelaunches(undefined), DEFAULT_MAX_WORKFLOW_AUTO_RELAUNCHES);
39
+ assert.equal(resolveMaxWorkflowAutoRelaunches(5), 5);
40
+ assert.equal(resolveMaxWorkflowAutoRelaunches(0), undefined);
41
+ assert.equal(resolveMaxWorkflowAutoRelaunches(-3), DEFAULT_MAX_WORKFLOW_AUTO_RELAUNCHES);
42
+ assert.equal(resolveMaxWorkflowAutoRelaunches("12"), DEFAULT_MAX_WORKFLOW_AUTO_RELAUNCHES);
43
+ assert.equal(resolveMaxWorkflowAutoRelaunches(2.5), DEFAULT_MAX_WORKFLOW_AUTO_RELAUNCHES);
44
+ });
45
+ });
@@ -4,7 +4,7 @@ import * as fs from "node:fs";
4
4
  import * as os from "node:os";
5
5
  import * as path from "node:path";
6
6
  import { describe, it } from "node:test";
7
- import { isSameGitRepository, resolveWorkflowChatProgress } from "../../src/workflows/chat-progress.ts";
7
+ import { buildWorkflowChatProgressRows, isSameGitRepository, resolveWorkflowChatProgress } from "../../src/workflows/chat-progress.ts";
8
8
  import { renderSubagentResult } from "../../src/tui/render.ts";
9
9
  import { bindMissionWorkflowChildAsyncLaunch, createSubagentExecutor, foregroundResultIntercomStatus, missionWorkflowChildStatus, runMissionWorkflowChild, shouldSuppressRoutineResultIntercom } from "../../src/runs/foreground/subagent-executor.ts";
10
10
  import { readMissionBinding } from "../../src/missions/lifecycle.ts";
@@ -202,6 +202,38 @@ describe("workflow chat progress rendering", () => {
202
202
  }
203
203
  });
204
204
 
205
+ it("warns once when a workflow child outlives its mission record", async () => {
206
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), "pi-workflow-missing-mission-"));
207
+ const originalWarn = console.warn;
208
+ const warnings: string[] = [];
209
+ try {
210
+ const location = {
211
+ projectRoot: root,
212
+ missionDir: path.join(root, ".pi/subagents", "missions"),
213
+ globalIndexDir: path.join(root, ".pi/subagents", "mission-index"),
214
+ writeGlobalIndex: false,
215
+ };
216
+ const mission = createMission(location, { title: "Workflow", objective: "Track child" });
217
+ fs.rmSync(path.join(location.missionDir, `${mission.id}.json`));
218
+ console.warn = (...args: unknown[]) => warnings.push(args.map(String).join(" "));
219
+
220
+ for (const key of ["first", "second"]) {
221
+ await assert.rejects(
222
+ () => runMissionWorkflowChild({ missionId: mission.id, location, autoCreated: false }, "wf-missing-mission", key, "Prep", async () => {
223
+ throw new Error("child failed");
224
+ }),
225
+ /child failed/,
226
+ );
227
+ }
228
+
229
+ assert.equal(warnings.length, 1);
230
+ assert.match(warnings[0] ?? "", new RegExp(`Mission '${mission.id}' is no longer in the mission store`));
231
+ } finally {
232
+ console.warn = originalWarn;
233
+ fs.rmSync(root, { recursive: true, force: true });
234
+ }
235
+ });
236
+
205
237
  it("renders stable rows keyed by workflow trace entries", () => {
206
238
  const text = componentText(renderSubagentResult({
207
239
  content: [{ type: "text", text: "Workflow running." }],
@@ -215,6 +247,7 @@ describe("workflow chat progress rendering", () => {
215
247
  { operation: "run", key: "scout", state: "completed", runId: "run-scout", phase: "Validation", label: "Found renderer seam", durationMs: 12 },
216
248
  { operation: "run", key: "tests", state: "started", phase: "Validation", label: "focused integration suite" },
217
249
  { operation: "run", key: "review", state: "failed", phase: "Validation", label: "fresh-context UX review", error: "needs fixes" },
250
+ { operation: "run", key: "stale", state: "stopped", phase: "Validation", label: "superseded exact-head review", error: "Workflow stopped by user." },
218
251
  ],
219
252
  emits: [],
220
253
  console: [],
@@ -228,6 +261,66 @@ describe("workflow chat progress rendering", () => {
228
261
  assert.match(text, /complete\s+scout Found renderer seam/);
229
262
  assert.match(text, /running\s+tests focused integration suite/);
230
263
  assert.match(text, /failed\s+review fresh-context UX review .* needs fixes/);
264
+ assert.match(text, /stopped\s+stale superseded exact-head review .* Workflow stopped by user/);
265
+ });
266
+
267
+ it("renders detached workflow trace rows as paused attention", () => {
268
+ const trace: NonNullable<Details["workflow"]>["trace"] = [{
269
+ operation: "run",
270
+ key: "detaches",
271
+ state: "detached",
272
+ phase: "Decision",
273
+ label: "supervisor handoff",
274
+ error: "Detached for intercom coordination. Reply to the supervisor request first.",
275
+ }];
276
+ const rows = buildWorkflowChatProgressRows(trace);
277
+ assert.equal(rows[0]?.state, "detached");
278
+
279
+ const text = componentText(renderSubagentResult({
280
+ content: [{ type: "text", text: "Workflow paused." }],
281
+ details: {
282
+ mode: "workflow",
283
+ runId: "wf_detached",
284
+ results: [],
285
+ chatProgress: { mode: "live-card", repoRelation: "same", repoLabel: "pi-subagents" },
286
+ workflow: { trace, emits: [], console: [] },
287
+ },
288
+ }, { expanded: false }, theme as any));
289
+
290
+ assert.match(text, /workflow wf_detached .* same repo .* paused/);
291
+ assert.match(text, /Phase Decision/);
292
+ assert.match(text, /detached\s+detaches supervisor handoff .* Detached for intercom coordination/);
293
+ assert.doesNotMatch(text, /running\s+detaches/);
294
+ assert.doesNotMatch(text, /failed\s+detaches/);
295
+ });
296
+
297
+ it("applies main-window density settings to collapsed workflow live cards", () => {
298
+ const trace = Array.from({ length: 10 }, (_, index) => ({
299
+ operation: "run" as const,
300
+ key: `step-${index}`,
301
+ state: "started" as const,
302
+ label: `review ${index}`,
303
+ }));
304
+ const result = {
305
+ content: [{ type: "text" as const, text: "Workflow running." }],
306
+ details: {
307
+ mode: "workflow" as const,
308
+ runId: "wf_density",
309
+ results: [],
310
+ chatProgress: { mode: "live-card" as const, repoRelation: "same" as const, repoLabel: "pi-subagents" },
311
+ workflow: { trace, emits: [], console: [] },
312
+ },
313
+ };
314
+
315
+ const compact = renderSubagentResult(result, { expanded: false }, theme as any, undefined, { horizontalSpacing: 0, compactResultMaxLines: 3 }).render(120);
316
+ assert.equal(compact.length, 3);
317
+ assert.match(compact[1]!, /^Repo pi-subagents\s*$/);
318
+ assert.match(compact[2]!, /rows hidden/);
319
+
320
+ const expanded = renderSubagentResult(result, { expanded: true }, theme as any, undefined, { horizontalSpacing: 0, compactResultMaxLines: 3 }).render(120);
321
+ assert.ok(expanded.length > 3);
322
+ assert.match(expanded[1]!, /^ Repo pi-subagents\s*$/);
323
+ assert.doesNotMatch(expanded.join("\n"), /rows hidden · .* expands/);
231
324
  });
232
325
 
233
326
  it("bounds workflow live-card rows and keeps old failed children visible", () => {
@@ -39,6 +39,26 @@ describe("workflow launch params", () => {
39
39
  );
40
40
  });
41
41
 
42
+ it("keeps a bridge override scoped to the target workflow child", () => {
43
+ assert.deepEqual(
44
+ prepareWorkflowLaunchParams(
45
+ {},
46
+ { agent: "worker", task: "Run", intercomBridge: { mode: "off" } },
47
+ "workflow-run",
48
+ "isolated",
49
+ ),
50
+ {
51
+ agent: "worker",
52
+ task: "Run",
53
+ intercomBridge: { mode: "off" },
54
+ async: false,
55
+ workflowParentRunId: "workflow-run",
56
+ workflowKey: "isolated",
57
+ },
58
+ );
59
+ assert.equal(prepareWorkflowLaunchParams({}, { agent: "worker", task: "Run" }, "workflow-run", "sibling").intercomBridge, undefined);
60
+ });
61
+
42
62
  it("places workflow child gates inside managed worktree tasks", () => {
43
63
  assert.deepEqual(
44
64
  prepareWorkflowLaunchParams(
@@ -61,6 +81,25 @@ describe("workflow launch params", () => {
61
81
  );
62
82
  });
63
83
 
84
+ it("preserves a bridge override for retained workflow children", () => {
85
+ assert.deepEqual(
86
+ prepareWorkflowLaunchParams(
87
+ {},
88
+ { resume: "retained-run", task: "Continue", intercomBridge: { mode: "off" } },
89
+ "workflow-run",
90
+ "continue",
91
+ ),
92
+ {
93
+ action: "resume",
94
+ id: "retained-run",
95
+ message: "Continue",
96
+ workflowParentRunId: "workflow-run",
97
+ workflowKey: "continue",
98
+ intercomBridge: { mode: "off" },
99
+ },
100
+ );
101
+ });
102
+
64
103
  it("rejects gate defaults on retained resume items", () => {
65
104
  assert.throws(
66
105
  () => prepareWorkflowLaunchParams(
@@ -82,7 +121,7 @@ describe("workflow launch params", () => {
82
121
  );
83
122
  });
84
123
 
85
- it("preserves execution limits when routing retained resume items", () => {
124
+ it("preserves execution limits and fan-out identity when routing retained resume items", () => {
86
125
  assert.deepEqual(
87
126
  prepareWorkflowLaunchParams(
88
127
  { turnBudget: { maxTurns: 8 }, toolBudget: { hard: 12, block: ["read"] } },
@@ -95,7 +134,7 @@ describe("workflow launch params", () => {
95
134
  },
96
135
  "workflow-run",
97
136
  "continue",
98
- { missionDetached: true },
137
+ { missionDetached: true, runFanoutBudget: { version: 1, rootRunId: "root-run", directory: "/tmp/fanout", limit: 64, parentPath: "parent" } },
99
138
  ),
100
139
  {
101
140
  action: "resume",
@@ -103,6 +142,7 @@ describe("workflow launch params", () => {
103
142
  message: "Continue carefully",
104
143
  workflowParentRunId: "workflow-run",
105
144
  workflowKey: "continue",
145
+ runFanoutBudget: { version: 1, rootRunId: "root-run", directory: "/tmp/fanout", limit: 64, parentPath: "parent/workflow[continue]" },
106
146
  mission: false,
107
147
  timeoutMs: 5_000,
108
148
  turnBudget: { maxTurns: 3, graceTurns: 1 },
@@ -16,6 +16,9 @@ export { filterSkillBodyForMode };
16
16
  export const readDefaultMode = getDefaultMode;
17
17
 
18
18
  export function resolveSessionMode(entries, fallbackMode = DEFAULT_MODE) {
19
+ // Unreachable: normalizePersistedMode always returns a valid mode or null,
20
+ // and the caller passes a valid default.
21
+ /* v8 ignore next 1 */
19
22
  const fallback = normalizePersistedMode(fallbackMode) || DEFAULT_MODE;
20
23
  if (!Array.isArray(entries)) return fallback;
21
24
 
@@ -59,6 +62,9 @@ export default function ponytailExtension(pi) {
59
62
 
60
63
  const setMode = (mode, ctx) => {
61
64
  const normalized = normalizePersistedMode(mode);
65
+ // Unreachable: setMode is only called with validated modes from the
66
+ // command handler and the deactivation input handler.
67
+ /* v8 ignore next 1 */
62
68
  if (!normalized) return;
63
69
 
64
70
  currentMode = normalized;
@@ -68,6 +74,8 @@ export default function ponytailExtension(pi) {
68
74
 
69
75
  const sendAlias = (skillName, args, ctx) => {
70
76
  const normalized = String(args || "").trim();
77
+ // Unreachable: the alias command handlers always pass empty args.
78
+ /* v8 ignore next 1 */
71
79
  const message = normalized ? `${skillName} ${normalized}` : skillName;
72
80
 
73
81
  if (ctx?.isIdle?.() === false) {
@@ -91,6 +99,9 @@ export default function ponytailExtension(pi) {
91
99
 
92
100
  if (parsed.type === "set-default") {
93
101
  const written = writeDefaultMode(parsed.mode);
102
+ // Unreachable: parsePonytailCommand only returns set-default for
103
+ // valid config modes, so writeDefaultMode always succeeds.
104
+ /* v8 ignore next 1 */
94
105
  if (written) {
95
106
  configuredDefaultMode = getDefaultMode();
96
107
  const message = configuredDefaultMode === written
@@ -57,6 +57,8 @@ function getConfigDir() {
57
57
  }
58
58
  if (process.platform === 'win32') {
59
59
  return path.join(
60
+ // Unreachable: APPDATA is always set on Windows.
61
+ /* v8 ignore next 1 */
60
62
  process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'),
61
63
  'ponytail'
62
64
  );
@@ -14,6 +14,9 @@ const COMPACT_MODE_RULES = {
14
14
  };
15
15
 
16
16
  function filterSkillBodyForMode(body, mode) {
17
+ // Unreachable: normalizeMode returns a valid mode or null, and the caller
18
+ // passes a valid mode.
19
+ /* v8 ignore next 1 */
17
20
  const effectiveMode = normalizeMode(mode) || DEFAULT_MODE;
18
21
  const withoutFrontmatter = String(body || '').replace(/^---[\s\S]*?---\s*/, '');
19
22
 
@@ -90,6 +93,9 @@ function getPonytailInstructions(mode, options) {
90
93
  return 'PONYTAIL MODE ACTIVE — level: ' + configuredMode + '. Behavior defined by /ponytail-' + configuredMode + ' skill.';
91
94
  }
92
95
 
96
+ // Unreachable: normalizePersistedMode above already falls back to
97
+ // DEFAULT_MODE, so normalizeMode always returns a valid mode here.
98
+ /* v8 ignore next 1 */
93
99
  const effectiveMode = normalizeMode(configuredMode) || DEFAULT_MODE;
94
100
  if (options?.compact === true) return getCompactInstructions(effectiveMode);
95
101