@fitchmultz/pi-subagents 0.0.0-stage → 0.44.5

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 (347) hide show
  1. package/CHANGELOG.md +1654 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1533 -2
  4. package/agents/context-builder.md +97 -0
  5. package/agents/debugger.md +51 -0
  6. package/agents/delegate.md +13 -0
  7. package/agents/fixer.md +37 -0
  8. package/agents/oracle.md +71 -0
  9. package/agents/planner.md +72 -0
  10. package/agents/researcher.md +51 -0
  11. package/agents/reviewer-claude.md +69 -0
  12. package/agents/reviewer-gpt.md +68 -0
  13. package/agents/reviewer-ponytail.md +82 -0
  14. package/agents/reviewer-security.md +81 -0
  15. package/agents/reviewer.md +69 -0
  16. package/agents/scout.md +66 -0
  17. package/agents/ui-designer.md +51 -0
  18. package/agents/watcher.md +26 -0
  19. package/agents/worker.md +46 -0
  20. package/agents/writer.md +36 -0
  21. package/dist/agents/agent-management.js +923 -0
  22. package/dist/agents/agent-scope.js +7 -0
  23. package/dist/agents/agent-selection.js +20 -0
  24. package/dist/agents/agent-serializer.js +100 -0
  25. package/dist/agents/agents.js +752 -0
  26. package/dist/agents/chain-serializer.js +367 -0
  27. package/dist/agents/frontmatter.js +21 -0
  28. package/dist/agents/identity.js +29 -0
  29. package/dist/agents/skills.js +570 -0
  30. package/dist/extension/build-info.js +1 -0
  31. package/dist/extension/compact-tools.js +112 -0
  32. package/dist/extension/config.js +22 -0
  33. package/dist/extension/control-notices.js +29 -0
  34. package/dist/extension/doctor.js +143 -0
  35. package/dist/extension/fanout-child.js +326 -0
  36. package/dist/extension/index.js +577 -0
  37. package/dist/extension/schemas.js +416 -0
  38. package/dist/extension/tool-input.js +20 -0
  39. package/dist/extension/tool-result.js +14 -0
  40. package/dist/history/canonical-result.js +81 -0
  41. package/dist/history/database.js +72 -0
  42. package/dist/history/index.js +199 -0
  43. package/dist/history/ingest.js +468 -0
  44. package/dist/history/queries.js +350 -0
  45. package/dist/history/store.js +216 -0
  46. package/dist/history/types.js +4 -0
  47. package/dist/history/worker.js +466 -0
  48. package/dist/intercom/intercom-bridge.js +112 -0
  49. package/dist/intercom/live-intercom.js +94 -0
  50. package/dist/intercom/result-intercom.js +373 -0
  51. package/dist/pi-intercom/agent-dir.js +10 -0
  52. package/dist/pi-intercom/bridge.js +750 -0
  53. package/dist/pi-intercom/broker/broker.js +426 -0
  54. package/dist/pi-intercom/broker/client.js +492 -0
  55. package/dist/pi-intercom/broker/framing.js +65 -0
  56. package/dist/pi-intercom/broker/paths.js +42 -0
  57. package/dist/pi-intercom/broker/pid.js +66 -0
  58. package/dist/pi-intercom/broker/spawn.js +221 -0
  59. package/dist/pi-intercom/config.js +102 -0
  60. package/dist/pi-intercom/index.js +2942 -0
  61. package/dist/pi-intercom/reply-tracker.js +182 -0
  62. package/dist/pi-intercom/session-targets.js +128 -0
  63. package/dist/pi-intercom/subagent-live-events.js +132 -0
  64. package/dist/pi-intercom/topics.js +157 -0
  65. package/dist/pi-intercom/types.js +163 -0
  66. package/dist/pi-intercom/ui/compose.js +269 -0
  67. package/dist/pi-intercom/ui/inline-message.js +80 -0
  68. package/dist/pi-intercom/ui/session-list.js +124 -0
  69. package/dist/runs/background/async-control.js +95 -0
  70. package/dist/runs/background/async-execution.js +676 -0
  71. package/dist/runs/background/async-job-tracker.js +383 -0
  72. package/dist/runs/background/async-result-file.js +58 -0
  73. package/dist/runs/background/async-resume.js +305 -0
  74. package/dist/runs/background/async-status.js +442 -0
  75. package/dist/runs/background/completion-dedupe.js +69 -0
  76. package/dist/runs/background/completion-delivery.js +245 -0
  77. package/dist/runs/background/notify.js +71 -0
  78. package/dist/runs/background/parallel-groups.js +55 -0
  79. package/dist/runs/background/result-watcher.js +426 -0
  80. package/dist/runs/background/run-id-resolver.js +64 -0
  81. package/dist/runs/background/run-status.js +382 -0
  82. package/dist/runs/background/stale-run-reconciler.js +335 -0
  83. package/dist/runs/background/subagent-runner-launcher.js +55 -0
  84. package/dist/runs/background/subagent-runner.js +1872 -0
  85. package/dist/runs/background/top-level-async.js +5 -0
  86. package/dist/runs/foreground/chain-clarify.js +1152 -0
  87. package/dist/runs/foreground/clarify-invocation.js +72 -0
  88. package/dist/runs/foreground/execution-input.js +423 -0
  89. package/dist/runs/foreground/foreground-control.js +817 -0
  90. package/dist/runs/foreground/question-control.js +88 -0
  91. package/dist/runs/foreground/run-async-path.js +205 -0
  92. package/dist/runs/foreground/subagent-executor.js +442 -0
  93. package/dist/runs/foreground/subagent-params.js +165 -0
  94. package/dist/runs/foreground/wait-run.js +141 -0
  95. package/dist/runs/shared/acceptance-contract.js +253 -0
  96. package/dist/runs/shared/acceptance-evaluation.js +256 -0
  97. package/dist/runs/shared/acceptance-finalization.js +280 -0
  98. package/dist/runs/shared/acceptance-reports.js +147 -0
  99. package/dist/runs/shared/acceptance.js +4 -0
  100. package/dist/runs/shared/chain-outputs.js +110 -0
  101. package/dist/runs/shared/child-attempt.js +693 -0
  102. package/dist/runs/shared/child-execution-cwd.js +96 -0
  103. package/dist/runs/shared/child-observations.js +114 -0
  104. package/dist/runs/shared/claude-code.js +199 -0
  105. package/dist/runs/shared/completion-guard.js +31 -0
  106. package/dist/runs/shared/dynamic-fanout.js +274 -0
  107. package/dist/runs/shared/history-index.js +72 -0
  108. package/dist/runs/shared/mcp-direct-tool-allowlist.js +334 -0
  109. package/dist/runs/shared/model-fallback.js +185 -0
  110. package/dist/runs/shared/mutating-tool-guard.js +170 -0
  111. package/dist/runs/shared/native-finalization.js +101 -0
  112. package/dist/runs/shared/native-usage.js +153 -0
  113. package/dist/runs/shared/nested-events.js +700 -0
  114. package/dist/runs/shared/nested-path.js +48 -0
  115. package/dist/runs/shared/nested-render.js +108 -0
  116. package/dist/runs/shared/parallel-utils.js +61 -0
  117. package/dist/runs/shared/parent-receipts.js +68 -0
  118. package/dist/runs/shared/parent-usage.js +121 -0
  119. package/dist/runs/shared/pi-args.js +310 -0
  120. package/dist/runs/shared/pi-spawn.js +52 -0
  121. package/dist/runs/shared/run-history-queries.js +45 -0
  122. package/dist/runs/shared/run-history.js +110 -0
  123. package/dist/runs/shared/run-id-ambiguity.js +4 -0
  124. package/dist/runs/shared/run-records.js +679 -0
  125. package/dist/runs/shared/session-cwd-preload.js +33 -0
  126. package/dist/runs/shared/single-output.js +222 -0
  127. package/dist/runs/shared/streaming-text.js +15 -0
  128. package/dist/runs/shared/structured-output.js +55 -0
  129. package/dist/runs/shared/subagent-control.js +179 -0
  130. package/dist/runs/shared/subagent-prompt-runtime.js +197 -0
  131. package/dist/runs/shared/subagent-tool-loop-guard.js +66 -0
  132. package/dist/runs/shared/supervisor-questions.js +350 -0
  133. package/dist/runs/shared/workflow-graph.js +214 -0
  134. package/dist/runs/shared/workflow-policy.js +84 -0
  135. package/dist/runs/shared/worktree.js +525 -0
  136. package/dist/shared/agent-context-policy.js +126 -0
  137. package/dist/shared/artifacts.js +96 -0
  138. package/dist/shared/atomic-json.js +13 -0
  139. package/dist/shared/cooperative.js +20 -0
  140. package/dist/shared/execution-cwd.js +14 -0
  141. package/dist/shared/file-coalescer.js +21 -0
  142. package/dist/shared/fork-context.js +49 -0
  143. package/dist/shared/formatters.js +85 -0
  144. package/dist/shared/journal-reader.js +407 -0
  145. package/dist/shared/lazy-tools.js +32 -0
  146. package/dist/shared/model-info.js +68 -0
  147. package/dist/shared/native-session.js +15 -0
  148. package/dist/shared/native-tui.js +18 -0
  149. package/dist/shared/native-typebox.js +20 -0
  150. package/dist/shared/post-exit-stdio-guard.js +197 -0
  151. package/dist/shared/prompt-sections.js +8 -0
  152. package/dist/shared/session-entries.js +52 -0
  153. package/dist/shared/session-identity.js +14 -0
  154. package/dist/shared/settings.js +254 -0
  155. package/dist/shared/status-format.js +111 -0
  156. package/dist/shared/temp-root.js +114 -0
  157. package/dist/shared/types.js +207 -0
  158. package/dist/shared/ui-mode.js +3 -0
  159. package/dist/shared/utils.js +334 -0
  160. package/dist/slash/prompt-template-bridge.js +298 -0
  161. package/dist/slash/slash-bridge.js +116 -0
  162. package/dist/slash/slash-commands.js +607 -0
  163. package/dist/slash/slash-live-state.js +246 -0
  164. package/dist/tui/action-hints.js +75 -0
  165. package/dist/tui/agent-history.js +197 -0
  166. package/dist/tui/agent-view.js +1656 -0
  167. package/dist/tui/render.js +1515 -0
  168. package/docs/intercom-bridge.md +212 -0
  169. package/docs/intercom.md +581 -0
  170. package/docs/performance.md +121 -0
  171. package/package.json +102 -4
  172. package/prompts/gather-context-and-clarify.md +14 -0
  173. package/prompts/parallel-cleanup.md +62 -0
  174. package/prompts/parallel-context-build.md +56 -0
  175. package/prompts/parallel-handoff-plan.md +62 -0
  176. package/prompts/parallel-research.md +51 -0
  177. package/prompts/parallel-review.md +59 -0
  178. package/prompts/review-loop.md +42 -0
  179. package/scripts/build.mjs +134 -0
  180. package/scripts/check-compat.mjs +48 -0
  181. package/scripts/compat-host.mjs +19 -0
  182. package/scripts/intercom-bridge-credentials.mjs +143 -0
  183. package/scripts/intercom-remote.mjs +103 -0
  184. package/scripts/linux-smoke.sh +51 -0
  185. package/scripts/local-install-smoke.mjs +66 -0
  186. package/scripts/native-package-smoke.mjs +82 -0
  187. package/scripts/package-smoke.mjs +209 -0
  188. package/scripts/prepare.mjs +67 -0
  189. package/scripts/real-pi-smoke.mjs +415 -0
  190. package/scripts/run-tests.mjs +149 -0
  191. package/skills/pi-intercom/SKILL.md +113 -0
  192. package/skills/pi-intercom/evals/evals.json +29 -0
  193. package/skills/pi-intercom/evals/trigger-evals.json +47 -0
  194. package/skills/pi-intercom/references/peer-sessions.md +69 -0
  195. package/skills/pi-subagents/SKILL.md +76 -0
  196. package/skills/pi-subagents/evals/evals.json +29 -0
  197. package/skills/pi-subagents/evals/trigger-evals.json +42 -0
  198. package/skills/pi-subagents/references/full-orchestration-guide.md +863 -0
  199. package/src/agents/agent-management.ts +813 -0
  200. package/src/agents/agent-scope.ts +7 -0
  201. package/src/agents/agent-selection.ts +23 -0
  202. package/src/agents/agent-serializer.ts +99 -0
  203. package/src/agents/agents.ts +916 -0
  204. package/src/agents/chain-serializer.ts +329 -0
  205. package/src/agents/frontmatter.ts +21 -0
  206. package/src/agents/identity.ts +30 -0
  207. package/src/agents/skills.ts +637 -0
  208. package/src/extension/build-info.ts +2 -0
  209. package/src/extension/compact-tools.ts +114 -0
  210. package/src/extension/config.ts +23 -0
  211. package/src/extension/control-notices.ts +47 -0
  212. package/src/extension/doctor.ts +171 -0
  213. package/src/extension/fanout-child.ts +312 -0
  214. package/src/extension/index.ts +649 -0
  215. package/src/extension/schemas.ts +444 -0
  216. package/src/extension/tool-input.ts +19 -0
  217. package/src/extension/tool-result.ts +16 -0
  218. package/src/history/canonical-result.ts +62 -0
  219. package/src/history/database.ts +59 -0
  220. package/src/history/index.ts +156 -0
  221. package/src/history/ingest.ts +349 -0
  222. package/src/history/queries.ts +241 -0
  223. package/src/history/store.ts +203 -0
  224. package/src/history/types.ts +166 -0
  225. package/src/history/worker.ts +321 -0
  226. package/src/intercom/intercom-bridge.ts +123 -0
  227. package/src/intercom/live-intercom.ts +104 -0
  228. package/src/intercom/result-intercom.ts +429 -0
  229. package/src/pi-intercom/agent-dir.ts +9 -0
  230. package/src/pi-intercom/bridge.ts +668 -0
  231. package/src/pi-intercom/broker/broker.ts +463 -0
  232. package/src/pi-intercom/broker/client.ts +558 -0
  233. package/src/pi-intercom/broker/framing.ts +77 -0
  234. package/src/pi-intercom/broker/paths.ts +47 -0
  235. package/src/pi-intercom/broker/pid.ts +59 -0
  236. package/src/pi-intercom/broker/spawn.ts +248 -0
  237. package/src/pi-intercom/config.ts +142 -0
  238. package/src/pi-intercom/index.ts +2929 -0
  239. package/src/pi-intercom/reply-tracker.ts +211 -0
  240. package/src/pi-intercom/session-targets.ts +172 -0
  241. package/src/pi-intercom/subagent-live-events.ts +133 -0
  242. package/src/pi-intercom/topics.ts +125 -0
  243. package/src/pi-intercom/types.ts +271 -0
  244. package/src/pi-intercom/ui/compose.ts +289 -0
  245. package/src/pi-intercom/ui/inline-message.ts +92 -0
  246. package/src/pi-intercom/ui/session-list.ts +139 -0
  247. package/src/runs/background/async-control.ts +89 -0
  248. package/src/runs/background/async-execution.ts +850 -0
  249. package/src/runs/background/async-job-tracker.ts +385 -0
  250. package/src/runs/background/async-result-file.ts +62 -0
  251. package/src/runs/background/async-resume.ts +333 -0
  252. package/src/runs/background/async-status.ts +481 -0
  253. package/src/runs/background/completion-dedupe.ts +79 -0
  254. package/src/runs/background/completion-delivery.ts +195 -0
  255. package/src/runs/background/notify.ts +123 -0
  256. package/src/runs/background/parallel-groups.ts +53 -0
  257. package/src/runs/background/result-watcher.ts +423 -0
  258. package/src/runs/background/run-id-resolver.ts +69 -0
  259. package/src/runs/background/run-status.ts +384 -0
  260. package/src/runs/background/stale-run-reconciler.ts +369 -0
  261. package/src/runs/background/subagent-runner-launcher.ts +56 -0
  262. package/src/runs/background/subagent-runner.ts +2053 -0
  263. package/src/runs/background/top-level-async.ts +13 -0
  264. package/src/runs/foreground/chain-clarify.ts +1340 -0
  265. package/src/runs/foreground/clarify-invocation.ts +77 -0
  266. package/src/runs/foreground/execution-input.ts +467 -0
  267. package/src/runs/foreground/foreground-control.ts +821 -0
  268. package/src/runs/foreground/question-control.ts +82 -0
  269. package/src/runs/foreground/run-async-path.ts +243 -0
  270. package/src/runs/foreground/subagent-executor.ts +501 -0
  271. package/src/runs/foreground/subagent-params.ts +320 -0
  272. package/src/runs/foreground/wait-run.ts +124 -0
  273. package/src/runs/shared/acceptance-contract.ts +257 -0
  274. package/src/runs/shared/acceptance-evaluation.ts +275 -0
  275. package/src/runs/shared/acceptance-finalization.ts +353 -0
  276. package/src/runs/shared/acceptance-reports.ts +152 -0
  277. package/src/runs/shared/acceptance.ts +33 -0
  278. package/src/runs/shared/chain-outputs.ts +114 -0
  279. package/src/runs/shared/child-attempt.ts +623 -0
  280. package/src/runs/shared/child-execution-cwd.ts +91 -0
  281. package/src/runs/shared/child-observations.ts +99 -0
  282. package/src/runs/shared/claude-code.ts +243 -0
  283. package/src/runs/shared/completion-guard.ts +35 -0
  284. package/src/runs/shared/dynamic-fanout.ts +295 -0
  285. package/src/runs/shared/history-index.ts +69 -0
  286. package/src/runs/shared/mcp-direct-tool-allowlist.ts +376 -0
  287. package/src/runs/shared/model-fallback.ts +219 -0
  288. package/src/runs/shared/mutating-tool-guard.ts +194 -0
  289. package/src/runs/shared/native-finalization.ts +128 -0
  290. package/src/runs/shared/native-usage.ts +113 -0
  291. package/src/runs/shared/nested-events.ts +752 -0
  292. package/src/runs/shared/nested-path.ts +52 -0
  293. package/src/runs/shared/nested-render.ts +119 -0
  294. package/src/runs/shared/parallel-utils.ts +153 -0
  295. package/src/runs/shared/parent-receipts.ts +59 -0
  296. package/src/runs/shared/parent-usage.ts +121 -0
  297. package/src/runs/shared/pi-args.ts +350 -0
  298. package/src/runs/shared/pi-spawn.ts +52 -0
  299. package/src/runs/shared/run-history-queries.ts +44 -0
  300. package/src/runs/shared/run-history.ts +100 -0
  301. package/src/runs/shared/run-id-ambiguity.ts +4 -0
  302. package/src/runs/shared/run-records.ts +607 -0
  303. package/src/runs/shared/session-cwd-preload.ts +27 -0
  304. package/src/runs/shared/single-output.ts +267 -0
  305. package/src/runs/shared/streaming-text.ts +18 -0
  306. package/src/runs/shared/structured-output.ts +68 -0
  307. package/src/runs/shared/subagent-control.ts +219 -0
  308. package/src/runs/shared/subagent-prompt-runtime.ts +201 -0
  309. package/src/runs/shared/subagent-tool-loop-guard.ts +89 -0
  310. package/src/runs/shared/supervisor-questions.ts +415 -0
  311. package/src/runs/shared/workflow-graph.ts +220 -0
  312. package/src/runs/shared/workflow-policy.ts +125 -0
  313. package/src/runs/shared/worktree.ts +635 -0
  314. package/src/shared/agent-context-policy.ts +184 -0
  315. package/src/shared/artifacts.ts +95 -0
  316. package/src/shared/atomic-json.ts +16 -0
  317. package/src/shared/cooperative.ts +21 -0
  318. package/src/shared/execution-cwd.ts +14 -0
  319. package/src/shared/file-coalescer.ts +29 -0
  320. package/src/shared/fork-context.ts +68 -0
  321. package/src/shared/formatters.ts +84 -0
  322. package/src/shared/journal-reader.ts +333 -0
  323. package/src/shared/lazy-tools.ts +36 -0
  324. package/src/shared/model-info.ts +81 -0
  325. package/src/shared/native-session.ts +8 -0
  326. package/src/shared/native-tui.ts +10 -0
  327. package/src/shared/native-typebox.ts +14 -0
  328. package/src/shared/post-exit-stdio-guard.ts +204 -0
  329. package/src/shared/prompt-sections.ts +10 -0
  330. package/src/shared/session-entries.ts +49 -0
  331. package/src/shared/session-identity.ts +18 -0
  332. package/src/shared/settings.ts +384 -0
  333. package/src/shared/status-format.ts +123 -0
  334. package/src/shared/temp-root.ts +107 -0
  335. package/src/shared/types.ts +1260 -0
  336. package/src/shared/ui-mode.ts +5 -0
  337. package/src/shared/utils.ts +342 -0
  338. package/src/slash/prompt-template-bridge.ts +408 -0
  339. package/src/slash/slash-bridge.ts +178 -0
  340. package/src/slash/slash-commands.ts +617 -0
  341. package/src/slash/slash-live-state.ts +292 -0
  342. package/src/tui/action-hints.ts +69 -0
  343. package/src/tui/agent-history.ts +210 -0
  344. package/src/tui/agent-view.ts +1323 -0
  345. package/src/tui/render.ts +1550 -0
  346. package/tsconfig.build.json +12 -0
  347. package/tsconfig.json +19 -0
@@ -0,0 +1,72 @@
1
+ import { discoverAvailableSkills, normalizeSkillInput } from "../../agents/skills.js";
2
+ import { validateForkContextModelPolicy } from "../../shared/agent-context-policy.js";
3
+ import { toModelInfo } from "../../shared/model-info.js";
4
+ import { createChainDir, isDynamicParallelStep, isParallelStep, removeChainDir, resolveChainTemplates, resolveStepBehavior } from "../../shared/settings.js";
5
+ import { resolveModelCandidate } from "../shared/model-fallback.js";
6
+ import { normalizeSingleOutputOverride } from "../shared/single-output.js";
7
+ import { ChainClarifyComponent } from "./chain-clarify.js";
8
+ /** Preview changes launch input only; waiting and background runs share the same owner. */
9
+ export async function clarifyInvocation(input) {
10
+ const { params, agents, ctx, cwd, runId } = input;
11
+ if (params.clarify !== true || !ctx.hasUI)
12
+ return params;
13
+ if (params.chain?.some((step) => isParallelStep(step) || isDynamicParallelStep(step)))
14
+ return params;
15
+ const mode = params.chain ? "chain" : params.tasks ? "parallel" : "single";
16
+ const steps = params.chain ?? params.tasks ?? [{ ...params, agent: params.agent, reads: undefined }];
17
+ const profiles = steps.map((step) => {
18
+ const profile = agents.find((agent) => agent.name === step.agent);
19
+ if (!profile)
20
+ throw new Error(`Unknown agent: ${step.agent}`);
21
+ return profile;
22
+ });
23
+ const templates = params.chain ? resolveChainTemplates(params.chain) : steps.map((step) => step.task ?? "");
24
+ const originalTask = params.task ?? (mode === "chain" ? templates[0] ?? "" : "");
25
+ const chainSkills = mode === "chain" ? normalizeSkillInput(params.skill) ?? [] : [];
26
+ const behaviors = profiles.map((profile, index) => {
27
+ const step = steps[index];
28
+ return resolveStepBehavior(profile, {
29
+ output: normalizeSingleOutputOverride(step.output, profile.output),
30
+ outputMode: step.outputMode,
31
+ reads: step.reads === true ? undefined : step.reads,
32
+ progress: step.progress,
33
+ skills: normalizeSkillInput(step.skill),
34
+ model: step.model,
35
+ }, chainSkills);
36
+ });
37
+ const models = ctx.modelRegistry.getAvailable().map(toModelInfo);
38
+ const skills = discoverAvailableSkills(cwd, { projectTrusted: ctx.isProjectTrusted() });
39
+ const chainDir = mode === "chain" ? createChainDir(runId, params.chainDir, cwd) : undefined;
40
+ let confirmed = false;
41
+ try {
42
+ const result = await ctx.ui.custom((tui, theme, _kb, done) => new ChainClarifyComponent(tui, theme, profiles, templates, originalTask, chainDir, behaviors, models, ctx.model?.provider, skills, done, mode), { overlay: true, overlayOptions: { anchor: "center", width: 84, maxHeight: "80%" } });
43
+ if (!result?.confirmed)
44
+ return undefined;
45
+ const updated = steps.map((step, index) => {
46
+ const override = result.behaviorOverrides[index];
47
+ return {
48
+ ...step, task: result.templates[index],
49
+ ...(override?.model ? { model: override.model } : {}),
50
+ ...(override?.output !== undefined ? { output: override.output } : {}),
51
+ ...(override?.reads !== undefined ? { reads: override.reads } : {}),
52
+ ...(override?.progress !== undefined ? { progress: override.progress } : {}),
53
+ ...(override?.skills !== undefined ? { skill: override.skills } : {}),
54
+ };
55
+ });
56
+ const next = {
57
+ ...params,
58
+ ...(mode === "chain" ? { chain: updated }
59
+ : mode === "parallel" ? { tasks: updated } : updated[0]),
60
+ clarify: false, async: result.runInBackground === true,
61
+ };
62
+ const error = validateForkContextModelPolicy(next, agents, (model) => resolveModelCandidate(model, models, ctx.model?.provider));
63
+ if (error)
64
+ throw new Error(error);
65
+ confirmed = true;
66
+ return next;
67
+ }
68
+ finally {
69
+ if (!confirmed && chainDir)
70
+ removeChainDir(chainDir);
71
+ }
72
+ }
@@ -0,0 +1,423 @@
1
+ import * as path from "node:path";
2
+ import {} from "../../agents/agents.js";
3
+ import { loadRunsForAgent } from "../shared/run-history.js";
4
+ import { getStepAgents, isParallelStep, isDynamicParallelStep, } from "../../shared/settings.js";
5
+ import { findDuplicateOutputPath, resolveSingleOutputPath } from "../shared/single-output.js";
6
+ import { resolveChildCwd } from "../../shared/utils.js";
7
+ import { validateAcceptanceInput } from "../shared/acceptance.js";
8
+ import { findWorktreeTaskCwdConflict, formatWorktreeTaskCwdConflict } from "../shared/worktree.js";
9
+ import {} from "../../shared/types.js";
10
+ import {} from "./subagent-params.js";
11
+ function validationErrorResult(mode, text) {
12
+ return { content: [{ type: "text", text }], isError: true, details: { mode, results: [] } };
13
+ }
14
+ const MIN_REVIEWER_FOREGROUND_TIMEOUT_MS = 900_000;
15
+ const LONG_RUNNING_HISTORY_SAMPLE_SIZE = 20;
16
+ const LONG_RUNNING_HISTORY_MIN_SAMPLES = 3;
17
+ const LONG_RUNNING_HISTORY_HEADROOM = 1.25;
18
+ const LONG_RUNNING_HISTORY_MAX_TIMEOUT_MS = 1_800_000;
19
+ function resolveTimeoutRole(agentName) {
20
+ if (typeof agentName !== "string")
21
+ return undefined;
22
+ if (/(^|[._-])reviewer($|[._-])/i.test(agentName))
23
+ return "reviewer";
24
+ if (/(^|[._-])planner($|[._-])/i.test(agentName))
25
+ return "planner";
26
+ if (/(^|[._-])researcher($|[._-])/i.test(agentName))
27
+ return "researcher";
28
+ return undefined;
29
+ }
30
+ function historicalForegroundTimeoutFloor(agentName) {
31
+ const durations = loadRunsForAgent(agentName)
32
+ .filter((entry) => entry.status === "ok" && Number.isFinite(entry.duration) && entry.duration > 0)
33
+ .slice(0, LONG_RUNNING_HISTORY_SAMPLE_SIZE)
34
+ .map((entry) => entry.duration)
35
+ .sort((left, right) => left - right);
36
+ if (durations.length < LONG_RUNNING_HISTORY_MIN_SAMPLES)
37
+ return undefined;
38
+ const p75 = durations[Math.ceil(durations.length * 0.75) - 1];
39
+ return p75 ? Math.min(LONG_RUNNING_HISTORY_MAX_TIMEOUT_MS, Math.ceil(p75 * LONG_RUNNING_HISTORY_HEADROOM)) : undefined;
40
+ }
41
+ function roleForegroundTimeoutFloor(agentName) {
42
+ const role = resolveTimeoutRole(agentName);
43
+ if (!role)
44
+ return undefined;
45
+ const historical = historicalForegroundTimeoutFloor(agentName);
46
+ if (role === "reviewer")
47
+ return Math.max(MIN_REVIEWER_FOREGROUND_TIMEOUT_MS, historical ?? 0);
48
+ return historical;
49
+ }
50
+ export function resolveForegroundTimeoutMs(params) {
51
+ const rawTimeout = params.timeoutMs;
52
+ const rawMaxRuntime = params.maxRuntimeMs;
53
+ for (const [name, value] of [["timeoutMs", rawTimeout], ["maxRuntimeMs", rawMaxRuntime]]) {
54
+ if (value !== undefined && (typeof value !== "number" || !Number.isInteger(value) || value < 1)) {
55
+ return { error: `${name} must be a positive integer.` };
56
+ }
57
+ }
58
+ if (rawTimeout !== undefined && rawMaxRuntime !== undefined && rawTimeout !== rawMaxRuntime) {
59
+ return { error: "timeoutMs and maxRuntimeMs are aliases; provide only one or use identical values." };
60
+ }
61
+ const timeoutMs = typeof rawTimeout === "number" ? rawTimeout : typeof rawMaxRuntime === "number" ? rawMaxRuntime : undefined;
62
+ return timeoutMs === undefined ? {} : { timeoutMs };
63
+ }
64
+ function timeoutRoleAgentsInRequest(params) {
65
+ const names = new Set();
66
+ if ((params.chain?.length ?? 0) > 0) {
67
+ for (const step of params.chain ?? []) {
68
+ for (const agent of getStepAgents(step)) {
69
+ if (resolveTimeoutRole(agent))
70
+ names.add(agent);
71
+ }
72
+ }
73
+ return [...names];
74
+ }
75
+ if ((params.tasks?.length ?? 0) > 0) {
76
+ for (const task of params.tasks ?? []) {
77
+ if (resolveTimeoutRole(task.agent))
78
+ names.add(task.agent);
79
+ }
80
+ return [...names];
81
+ }
82
+ if (resolveTimeoutRole(params.agent))
83
+ names.add(params.agent);
84
+ return [...names];
85
+ }
86
+ export function normalizeRoleForegroundTimeout(params, timeoutMs) {
87
+ if (timeoutMs === undefined)
88
+ return timeoutMs;
89
+ let normalized = timeoutMs;
90
+ for (const agentName of timeoutRoleAgentsInRequest(params)) {
91
+ const floor = roleForegroundTimeoutFloor(agentName);
92
+ if (floor !== undefined && normalized < floor)
93
+ normalized = floor;
94
+ }
95
+ return normalized;
96
+ }
97
+ function validateAcceptanceForExecution(params) {
98
+ const topLevelErrors = validateAcceptanceInput(params.acceptance);
99
+ if (topLevelErrors.length > 0)
100
+ return validationErrorResult("single", topLevelErrors.join(" "));
101
+ for (const [index, task] of (params.tasks ?? []).entries()) {
102
+ const errors = validateAcceptanceInput(task.acceptance, `tasks[${index}].acceptance`);
103
+ if (errors.length > 0)
104
+ return validationErrorResult("parallel", errors.join(" "));
105
+ }
106
+ for (const [stepIndex, step] of (params.chain ?? []).entries()) {
107
+ if (isParallelStep(step)) {
108
+ if (Object.hasOwn(step, "acceptance"))
109
+ return validationErrorResult("chain", `chain[${stepIndex}].acceptance is not supported on static parallel groups; set acceptance on each parallel task.`);
110
+ for (const [taskIndex, task] of step.parallel.entries()) {
111
+ const errors = validateAcceptanceInput(task.acceptance, `chain[${stepIndex}].parallel[${taskIndex}].acceptance`);
112
+ if (errors.length > 0)
113
+ return validationErrorResult("chain", errors.join(" "));
114
+ }
115
+ }
116
+ else if (isDynamicParallelStep(step)) {
117
+ if (Object.hasOwn(step, "acceptance"))
118
+ return validationErrorResult("chain", `chain[${stepIndex}].acceptance is not supported on dynamic fanout groups; set acceptance on chain[${stepIndex}].parallel.acceptance for each materialized child.`);
119
+ const errors = validateAcceptanceInput(step.parallel.acceptance, `chain[${stepIndex}].parallel.acceptance`);
120
+ if (errors.length > 0)
121
+ return validationErrorResult("chain", errors.join(" "));
122
+ }
123
+ else {
124
+ const stepErrors = validateAcceptanceInput(step.acceptance, `chain[${stepIndex}].acceptance`);
125
+ if (stepErrors.length > 0)
126
+ return validationErrorResult("chain", stepErrors.join(" "));
127
+ }
128
+ }
129
+ return null;
130
+ }
131
+ const SEQUENTIAL_CHAIN_STEP_KEYS = new Set(["agent", "task", "phase", "label", "as", "outputSchema", "cwd", "output", "outputMode", "reads", "progress", "skill", "model", "acceptance"]);
132
+ const STATIC_PARALLEL_STEP_KEYS = new Set(["parallel", "concurrency", "failFast", "worktree", "cwd"]);
133
+ const DYNAMIC_PARALLEL_STEP_KEYS = new Set(["expand", "parallel", "collect", "concurrency", "failFast", "phase", "label"]);
134
+ function unsupportedChainStepFields(step, allowed) {
135
+ return Object.keys(step).filter((key) => !allowed.has(key));
136
+ }
137
+ export function validateExecutionInput(params, agents, hasChain, hasTasks, hasSingle, allowClarifyTaskPrompt) {
138
+ const acceptanceError = validateAcceptanceForExecution(params);
139
+ if (acceptanceError)
140
+ return acceptanceError;
141
+ if (params.tasks && params.tasks.length === 0)
142
+ return validationErrorResult("parallel", "tasks must contain at least one task.");
143
+ if (params.worktree !== undefined && !hasTasks)
144
+ return validationErrorResult(getRequestedModeLabel(params), "Top-level worktree is supported only with tasks parallel mode.");
145
+ if (hasSingle && params.task !== undefined && (typeof params.task !== "string" || params.task.trim().length === 0)) {
146
+ return validationErrorResult("single", "task must be a non-empty string when provided.");
147
+ }
148
+ if (Number(hasChain) + Number(hasTasks) + Number(hasSingle) !== 1) {
149
+ return {
150
+ content: [
151
+ {
152
+ type: "text",
153
+ text: `Provide exactly one mode. Agents: ${agents.map((a) => a.name).join(", ") || "none"}`,
154
+ },
155
+ ],
156
+ isError: true,
157
+ details: { mode: "single", results: [] },
158
+ };
159
+ }
160
+ const timeoutResolution = resolveForegroundTimeoutMs(params);
161
+ if (timeoutResolution.error)
162
+ return validationErrorResult(getRequestedModeLabel(params), timeoutResolution.error);
163
+ if (hasSingle && params.agent && !agents.find((agent) => agent.name === params.agent)) {
164
+ return {
165
+ content: [{ type: "text", text: `Unknown agent: ${params.agent}` }],
166
+ isError: true,
167
+ details: { mode: "single", results: [] },
168
+ };
169
+ }
170
+ if (hasTasks && params.tasks) {
171
+ for (let i = 0; i < params.tasks.length; i++) {
172
+ const task = params.tasks[i];
173
+ if (typeof task.task !== "string" || task.task.trim().length === 0)
174
+ return validationErrorResult("parallel", `tasks[${i}].task must be a non-empty string.`);
175
+ if (!agents.find((agent) => agent.name === task.agent)) {
176
+ return {
177
+ content: [{ type: "text", text: `Unknown agent: ${task.agent} (task ${i + 1})` }],
178
+ isError: true,
179
+ details: { mode: "parallel", results: [] },
180
+ };
181
+ }
182
+ }
183
+ }
184
+ if (hasChain && params.chain) {
185
+ if (params.chain.length === 0) {
186
+ return {
187
+ content: [{ type: "text", text: "Chain must have at least one step" }],
188
+ isError: true,
189
+ details: { mode: "chain", results: [] },
190
+ };
191
+ }
192
+ const firstStep = params.chain[0];
193
+ if (isParallelStep(firstStep)) {
194
+ const missingTaskIndex = firstStep.parallel.findIndex((t) => !t.task);
195
+ if (missingTaskIndex !== -1) {
196
+ return {
197
+ content: [{ type: "text", text: `First parallel step: task ${missingTaskIndex + 1} must have a task (no previous output to reference)` }],
198
+ isError: true,
199
+ details: { mode: "chain", results: [] },
200
+ };
201
+ }
202
+ }
203
+ else if (isDynamicParallelStep(firstStep)) {
204
+ return {
205
+ content: [{ type: "text", text: "First step in chain cannot be dynamic fanout; expand.from requires a prior structured named output" }],
206
+ isError: true,
207
+ details: { mode: "chain", results: [] },
208
+ };
209
+ }
210
+ else if (!firstStep.task && !params.task && !allowClarifyTaskPrompt) {
211
+ return {
212
+ content: [{ type: "text", text: "First step in chain must have a task" }],
213
+ isError: true,
214
+ details: { mode: "chain", results: [] },
215
+ };
216
+ }
217
+ for (let i = 0; i < params.chain.length; i++) {
218
+ const step = params.chain[i];
219
+ const allowedKeys = isParallelStep(step) ? STATIC_PARALLEL_STEP_KEYS : isDynamicParallelStep(step) ? DYNAMIC_PARALLEL_STEP_KEYS : SEQUENTIAL_CHAIN_STEP_KEYS;
220
+ const unsupportedFields = unsupportedChainStepFields(step, allowedKeys);
221
+ if (unsupportedFields.length > 0)
222
+ return validationErrorResult("chain", `chain[${i}] fields are not supported for this step mode: ${unsupportedFields.join(", ")}.`);
223
+ if (Object.hasOwn(step, "task") && (typeof step.task !== "string" || !step.task.trim())) {
224
+ return validationErrorResult("chain", `chain[${i}].task must be a non-empty string when provided.`);
225
+ }
226
+ if (isParallelStep(step)) {
227
+ const emptyTaskIndex = step.parallel.findIndex((task) => Object.hasOwn(task, "task") && (typeof task.task !== "string" || !task.task.trim()));
228
+ if (emptyTaskIndex >= 0)
229
+ return validationErrorResult("chain", `chain[${i}].parallel[${emptyTaskIndex}].task must be a non-empty string when provided.`);
230
+ }
231
+ else if (isDynamicParallelStep(step) && Object.hasOwn(step.parallel, "task") && (typeof step.parallel.task !== "string" || !step.parallel.task.trim())) {
232
+ return validationErrorResult("chain", `chain[${i}].parallel.task must be a non-empty string when provided.`);
233
+ }
234
+ const stepAgents = getStepAgents(step);
235
+ for (const agentName of stepAgents) {
236
+ if (!agents.find((a) => a.name === agentName)) {
237
+ return {
238
+ content: [{ type: "text", text: `Unknown agent: ${agentName} (step ${i + 1})` }],
239
+ isError: true,
240
+ details: { mode: "chain", results: [] },
241
+ };
242
+ }
243
+ }
244
+ if (isParallelStep(step) && step.parallel.length === 0) {
245
+ return {
246
+ content: [{ type: "text", text: `Parallel step ${i + 1} must have at least one task` }],
247
+ isError: true,
248
+ details: { mode: "chain", results: [] },
249
+ };
250
+ }
251
+ }
252
+ }
253
+ return null;
254
+ }
255
+ function getRequestedModeLabel(params) {
256
+ if ((params.chain?.length ?? 0) > 0)
257
+ return "chain";
258
+ if ((params.tasks?.length ?? 0) > 0)
259
+ return "parallel";
260
+ if (params.agent)
261
+ return "single";
262
+ return "single";
263
+ }
264
+ export function buildRequestedModeError(params, message) {
265
+ return withForkContext({
266
+ content: [{ type: "text", text: message }],
267
+ isError: true,
268
+ details: { mode: getRequestedModeLabel(params), results: [] },
269
+ }, params.context);
270
+ }
271
+ function expandTopLevelTaskCounts(tasks) {
272
+ const expanded = [];
273
+ for (let taskIndex = 0; taskIndex < tasks.length; taskIndex++) {
274
+ const task = tasks[taskIndex];
275
+ const rawCount = task.count;
276
+ if (rawCount !== undefined && (typeof rawCount !== "number" || !Number.isInteger(rawCount) || rawCount < 1)) {
277
+ return { error: `tasks[${taskIndex}].count must be an integer >= 1` };
278
+ }
279
+ const { count, ...concreteTask } = task;
280
+ for (let repeat = 0; repeat < (rawCount ?? 1); repeat++) {
281
+ expanded.push({ ...concreteTask });
282
+ }
283
+ }
284
+ return { tasks: expanded };
285
+ }
286
+ function expandChainParallelCounts(chain) {
287
+ const expandedChain = [];
288
+ for (let stepIndex = 0; stepIndex < chain.length; stepIndex++) {
289
+ const step = chain[stepIndex];
290
+ if (!isParallelStep(step)) {
291
+ expandedChain.push(step);
292
+ continue;
293
+ }
294
+ const expandedParallel = [];
295
+ for (let taskIndex = 0; taskIndex < step.parallel.length; taskIndex++) {
296
+ const task = step.parallel[taskIndex];
297
+ const rawCount = task.count;
298
+ if (rawCount !== undefined && (typeof rawCount !== "number" || !Number.isInteger(rawCount) || rawCount < 1)) {
299
+ return { error: `chain[${stepIndex}].parallel[${taskIndex}].count must be an integer >= 1` };
300
+ }
301
+ const { count, ...concreteTask } = task;
302
+ for (let repeat = 0; repeat < (rawCount ?? 1); repeat++) {
303
+ expandedParallel.push({ ...concreteTask });
304
+ }
305
+ }
306
+ expandedChain.push({ ...step, parallel: expandedParallel });
307
+ }
308
+ return { chain: expandedChain };
309
+ }
310
+ export function normalizeRepeatedParallelCounts(params) {
311
+ if (params.tasks) {
312
+ const expandedTasks = expandTopLevelTaskCounts(params.tasks);
313
+ if (expandedTasks.error) {
314
+ return { error: buildRequestedModeError(params, expandedTasks.error) };
315
+ }
316
+ return { params: { ...params, tasks: expandedTasks.tasks } };
317
+ }
318
+ if (params.chain) {
319
+ const expandedChain = expandChainParallelCounts(params.chain);
320
+ if (expandedChain.error) {
321
+ return { error: buildRequestedModeError(params, expandedChain.error) };
322
+ }
323
+ return { params: { ...params, chain: expandedChain.chain } };
324
+ }
325
+ return { params };
326
+ }
327
+ export function withForkContext(result, context) {
328
+ if (context !== "fork" || !result.details)
329
+ return result;
330
+ return {
331
+ ...result,
332
+ details: {
333
+ ...result.details,
334
+ context: "fork",
335
+ },
336
+ };
337
+ }
338
+ export function toExecutionErrorResult(params, error, context = params.context) {
339
+ const message = error instanceof Error ? error.message : String(error);
340
+ return withForkContext({
341
+ content: [{ type: "text", text: message }],
342
+ isError: true,
343
+ details: { mode: getRequestedModeLabel(params), results: [] },
344
+ }, context);
345
+ }
346
+ export function collectChainSessionFiles(chain, sessionFileForIndex, sessionFileForAgentIndex, dynamicFanoutMaxItems) {
347
+ const sessionFiles = [];
348
+ let flatIndex = 0;
349
+ for (const step of chain) {
350
+ if (isParallelStep(step)) {
351
+ for (let i = 0; i < step.parallel.length; i++) {
352
+ const agentName = step.parallel[i]?.agent;
353
+ sessionFiles.push(sessionFileForAgentIndex(agentName, flatIndex) ?? sessionFileForIndex(flatIndex));
354
+ flatIndex++;
355
+ }
356
+ continue;
357
+ }
358
+ if (isDynamicParallelStep(step)) {
359
+ const maxItems = step.expand.maxItems ?? dynamicFanoutMaxItems ?? 0;
360
+ for (let i = 0; i < maxItems; i++) {
361
+ sessionFiles.push(sessionFileForAgentIndex(step.parallel.agent, flatIndex) ?? sessionFileForIndex(flatIndex));
362
+ flatIndex++;
363
+ }
364
+ continue;
365
+ }
366
+ const agentName = getStepAgents(step)[0];
367
+ sessionFiles.push(sessionFileForAgentIndex(agentName, flatIndex) ?? sessionFileForIndex(flatIndex));
368
+ flatIndex++;
369
+ }
370
+ return sessionFiles;
371
+ }
372
+ export function buildParallelModeError(message) {
373
+ return {
374
+ content: [{ type: "text", text: message }],
375
+ isError: true,
376
+ details: { mode: "parallel", results: [] },
377
+ };
378
+ }
379
+ export function buildParallelWorktreeTaskCwdError(tasks, sharedCwd) {
380
+ const conflict = findWorktreeTaskCwdConflict(tasks, sharedCwd);
381
+ if (!conflict)
382
+ return undefined;
383
+ return formatWorktreeTaskCwdConflict(conflict, sharedCwd);
384
+ }
385
+ export function buildChainWorktreeTaskCwdError(chain, sharedCwd) {
386
+ for (let stepIndex = 0; stepIndex < chain.length; stepIndex++) {
387
+ const step = chain[stepIndex];
388
+ if (!isParallelStep(step) || !step.worktree)
389
+ continue;
390
+ const stepCwd = resolveChildCwd(sharedCwd, step.cwd);
391
+ const conflict = findWorktreeTaskCwdConflict(step.parallel, stepCwd);
392
+ if (!conflict)
393
+ continue;
394
+ const detail = formatWorktreeTaskCwdConflict(conflict, stepCwd);
395
+ return `parallel chain step ${stepIndex + 1}: ${detail}`;
396
+ }
397
+ return undefined;
398
+ }
399
+ export function resolveParallelTaskCwd(task, paramsCwd, worktreeSetup, index) {
400
+ if (worktreeSetup)
401
+ return worktreeSetup.worktrees[index].agentCwd;
402
+ return resolveChildCwd(paramsCwd, task.cwd);
403
+ }
404
+ export function findDuplicateParallelOutputPath(input) {
405
+ return findDuplicateOutputPath(input.tasks.map((task, index) => {
406
+ const behavior = input.behaviors[index];
407
+ if (!behavior?.output)
408
+ return { agent: task.agent };
409
+ const taskCwd = resolveParallelTaskCwd(task, input.paramsCwd, input.worktreeSetup, index);
410
+ return {
411
+ agent: task.agent,
412
+ outputPath: resolveSingleOutputPath(behavior.output, input.ctxCwd, taskCwd),
413
+ };
414
+ }));
415
+ }
416
+ export function findDuplicateAbsoluteParallelOutputPath(input) {
417
+ return findDuplicateOutputPath(input.tasks.map((task, index) => {
418
+ const behavior = input.behaviors[index];
419
+ if (typeof behavior?.output !== "string" || !path.isAbsolute(behavior.output))
420
+ return { agent: task.agent };
421
+ return { agent: task.agent, outputPath: path.resolve(behavior.output) };
422
+ }));
423
+ }