@fitlab-ai/agent-infra 0.9.1 → 0.9.3

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 (476) hide show
  1. package/README.md +24 -11
  2. package/README.zh-CN.md +24 -11
  3. package/bin/cli.ts +12 -0
  4. package/dist/bin/cli.js +14 -0
  5. package/dist/bin/internal-cli.js +30 -0
  6. package/dist/lib/agent-client.js +163 -0
  7. package/dist/lib/agent-clients/adapter.js +337 -0
  8. package/dist/lib/agent-clients/adapters/antigravity-cli.js +64 -0
  9. package/dist/lib/agent-clients/adapters/claude-code.js +71 -0
  10. package/dist/lib/agent-clients/adapters/codex-sandbox.js +210 -0
  11. package/dist/lib/agent-clients/adapters/codex.js +63 -0
  12. package/dist/lib/agent-clients/adapters/index.js +20 -0
  13. package/dist/lib/agent-clients/adapters/opencode.js +73 -0
  14. package/dist/lib/agent-clients/config.js +215 -0
  15. package/dist/lib/agent-clients/custom-tuis.js +99 -0
  16. package/dist/lib/agent-clients/invocation.js +9 -0
  17. package/dist/lib/agent-clients/next-steps.js +47 -0
  18. package/dist/lib/agent-clients/project-assets.js +45 -0
  19. package/dist/lib/agent-clients/reconcile.js +236 -0
  20. package/dist/lib/agent-clients/registry.js +78 -0
  21. package/dist/lib/agent-clients/schema.js +75 -0
  22. package/dist/lib/agent-clients/tokens.js +49 -0
  23. package/dist/lib/agent-clients/types.js +29 -0
  24. package/dist/lib/builtin-tuis.js +9 -14
  25. package/dist/lib/defaults.json +85 -73
  26. package/dist/lib/git/review-snapshot.js +25 -10
  27. package/dist/lib/git/reviewed-commit-equivalence.js +110 -0
  28. package/dist/lib/git/workflow.js +54 -1
  29. package/dist/lib/init.js +47 -91
  30. package/dist/lib/internal/agent-client.js +174 -0
  31. package/dist/lib/internal/git-workflow.js +7 -2
  32. package/dist/lib/internal/platform-checks.js +3 -3
  33. package/dist/lib/internal/platform-comment.js +29 -2
  34. package/dist/lib/internal/platform-issue.js +7 -0
  35. package/dist/lib/internal/platform-pr-review.js +132 -0
  36. package/dist/lib/internal/platform-pr.js +20 -2
  37. package/dist/lib/internal/platform-release-notes.js +52 -16
  38. package/dist/lib/internal/pr-review-grade.js +140 -0
  39. package/dist/lib/internal/release-workflow.js +82 -20
  40. package/dist/lib/internal/task-activity.js +73 -0
  41. package/dist/lib/internal/task-event.js +17 -1
  42. package/dist/lib/internal/task-lifecycle.js +7 -0
  43. package/dist/lib/internal/task-orchestration.js +196 -0
  44. package/dist/lib/internal/task-review.js +70 -0
  45. package/dist/lib/merge.js +2 -1
  46. package/dist/lib/platform/adapters.js +45 -1
  47. package/dist/lib/platform/change-request-git-evidence.js +113 -0
  48. package/dist/lib/platform/completion-backfill.js +122 -0
  49. package/dist/lib/platform/issue-comments.js +3 -2
  50. package/dist/lib/platform/merged-pr-equivalence.js +156 -16
  51. package/dist/lib/platform/pr-checks.js +101 -50
  52. package/dist/lib/platform/pr-review.js +166 -0
  53. package/dist/lib/platform/pull-requests.js +432 -7
  54. package/dist/lib/platform/release-notes.js +85 -3
  55. package/dist/lib/platform/releases.js +1 -1
  56. package/dist/lib/platform/verification-required.js +8 -10
  57. package/dist/lib/pr-review/evidence-grading.js +229 -0
  58. package/dist/lib/prompt.js +15 -3
  59. package/dist/lib/release/channels.js +4 -1
  60. package/dist/lib/release/workflow.js +4 -2
  61. package/dist/lib/remove-dir.js +25 -0
  62. package/dist/lib/run/tui.js +11 -7
  63. package/dist/lib/sandbox/agent-client-reconciler.js +234 -0
  64. package/dist/lib/sandbox/clipboard/linux.js +2 -1
  65. package/dist/lib/sandbox/commands/create.js +76 -200
  66. package/dist/lib/sandbox/commands/enter.js +17 -1
  67. package/dist/lib/sandbox/commands/prune.js +2 -2
  68. package/dist/lib/sandbox/commands/rm.js +3 -2
  69. package/dist/lib/sandbox/commands/show.js +1 -1
  70. package/dist/lib/sandbox/config.js +21 -4
  71. package/dist/lib/sandbox/constants.js +3 -0
  72. package/dist/lib/sandbox/dockerfile.js +2 -1
  73. package/dist/lib/sandbox/managed-fs.js +10 -5
  74. package/dist/lib/sandbox/recovery.js +52 -36
  75. package/dist/lib/sandbox/shell.js +1 -1
  76. package/dist/lib/sandbox/tool-types.js +8 -0
  77. package/dist/lib/sandbox/tools.js +2 -126
  78. package/dist/lib/task/activity-intent.js +101 -0
  79. package/dist/lib/task/artifact-lifecycle.js +3 -50
  80. package/dist/lib/task/commands/decisions.js +50 -20
  81. package/dist/lib/task/commands/log.js +15 -14
  82. package/dist/lib/task/delegation-receipts.js +128 -0
  83. package/dist/lib/task/events.js +146 -8
  84. package/dist/lib/task/finalization-artifacts.js +35 -0
  85. package/dist/lib/task/lifecycle-execution.js +46 -0
  86. package/dist/lib/task/orchestration.js +784 -0
  87. package/dist/lib/task/review-artifacts.js +143 -59
  88. package/dist/lib/task/review-finalization.js +186 -0
  89. package/dist/lib/task/review-fingerprint.js +4 -0
  90. package/dist/lib/task/task-execution-lock.js +231 -0
  91. package/dist/lib/task/verification-checks.js +2 -1
  92. package/dist/lib/task/verification-engine.js +372 -28
  93. package/dist/lib/task/verification-review.js +1 -1
  94. package/dist/lib/task/verification.js +5 -2
  95. package/dist/lib/task/workspace-snapshot.js +46 -0
  96. package/dist/lib/update.js +42 -161
  97. package/lib/agent-client.ts +214 -0
  98. package/lib/agent-clients/adapter.ts +534 -0
  99. package/lib/agent-clients/adapters/antigravity-cli.ts +65 -0
  100. package/lib/agent-clients/adapters/claude-code.ts +79 -0
  101. package/lib/agent-clients/adapters/codex-sandbox.ts +245 -0
  102. package/lib/agent-clients/adapters/codex.ts +67 -0
  103. package/lib/agent-clients/adapters/index.ts +27 -0
  104. package/lib/agent-clients/adapters/opencode.ts +75 -0
  105. package/lib/agent-clients/config.ts +323 -0
  106. package/lib/agent-clients/custom-tuis.ts +149 -0
  107. package/lib/agent-clients/invocation.ts +19 -0
  108. package/lib/agent-clients/next-steps.ts +85 -0
  109. package/lib/agent-clients/project-assets.ts +96 -0
  110. package/lib/agent-clients/reconcile.ts +325 -0
  111. package/lib/agent-clients/registry.ts +154 -0
  112. package/lib/agent-clients/schema.ts +80 -0
  113. package/lib/agent-clients/tokens.ts +53 -0
  114. package/lib/agent-clients/types.ts +92 -0
  115. package/lib/benchmark/schemas/v1.0.0/case-manifest.schema.json +120 -0
  116. package/lib/benchmark/schemas/v1.0.0/common.schema.json +109 -0
  117. package/lib/benchmark/schemas/v1.0.0/grader-result.schema.json +160 -0
  118. package/lib/benchmark/schemas/v1.0.0/run-manifest.schema.json +122 -0
  119. package/lib/benchmark/schemas/v1.0.0/subject.schema.json +227 -0
  120. package/lib/builtin-tuis.ts +16 -15
  121. package/lib/defaults.json +24 -12
  122. package/lib/git/review-snapshot.ts +39 -9
  123. package/lib/git/reviewed-commit-equivalence.ts +135 -0
  124. package/lib/git/workflow.ts +66 -2
  125. package/lib/init.ts +53 -121
  126. package/lib/internal/agent-client.ts +186 -0
  127. package/lib/internal/git-workflow.ts +6 -2
  128. package/lib/internal/platform-checks.ts +4 -4
  129. package/lib/internal/platform-comment.ts +17 -2
  130. package/lib/internal/platform-issue.ts +4 -0
  131. package/lib/internal/platform-pr-review.ts +104 -0
  132. package/lib/internal/platform-pr.ts +14 -1
  133. package/lib/internal/platform-release-notes.ts +59 -16
  134. package/lib/internal/pr-review-grade.ts +130 -0
  135. package/lib/internal/release-workflow.ts +79 -20
  136. package/lib/internal/task-activity.ts +55 -0
  137. package/lib/internal/task-event.ts +9 -1
  138. package/lib/internal/task-lifecycle.ts +4 -0
  139. package/lib/internal/task-orchestration.ts +171 -0
  140. package/lib/internal/task-review.ts +70 -0
  141. package/lib/merge.ts +2 -1
  142. package/lib/platform/adapters.ts +169 -3
  143. package/lib/platform/change-request-git-evidence.ts +159 -0
  144. package/lib/platform/completion-backfill.ts +134 -0
  145. package/lib/platform/issue-comments.ts +3 -2
  146. package/lib/platform/merged-pr-equivalence.ts +178 -19
  147. package/lib/platform/pr-checks.ts +121 -66
  148. package/lib/platform/pr-review.ts +201 -0
  149. package/lib/platform/pull-requests.ts +529 -27
  150. package/lib/platform/release-notes.ts +100 -4
  151. package/lib/platform/releases.ts +1 -1
  152. package/lib/platform/verification-required.ts +12 -10
  153. package/lib/pr-review/evidence-grading.ts +318 -0
  154. package/lib/prompt.ts +16 -3
  155. package/lib/release/channels.ts +4 -1
  156. package/lib/release/workflow.ts +4 -3
  157. package/lib/remove-dir.ts +28 -0
  158. package/lib/run/tui.ts +12 -5
  159. package/lib/sandbox/agent-client-reconciler.ts +378 -0
  160. package/lib/sandbox/clipboard/linux.ts +2 -1
  161. package/lib/sandbox/commands/create.ts +111 -212
  162. package/lib/sandbox/commands/enter.ts +23 -1
  163. package/lib/sandbox/commands/prune.ts +2 -2
  164. package/lib/sandbox/commands/rm.ts +3 -2
  165. package/lib/sandbox/commands/show.ts +1 -1
  166. package/lib/sandbox/config.ts +31 -4
  167. package/lib/sandbox/constants.ts +6 -0
  168. package/lib/sandbox/dockerfile.ts +2 -1
  169. package/lib/sandbox/managed-fs.ts +18 -5
  170. package/lib/sandbox/recovery.ts +98 -67
  171. package/lib/sandbox/shell.ts +1 -1
  172. package/lib/sandbox/tool-types.ts +143 -0
  173. package/lib/sandbox/tools.ts +5 -157
  174. package/lib/server/server.schema.json +2 -2
  175. package/lib/task/activity-intent.ts +128 -0
  176. package/lib/task/artifact-lifecycle.ts +3 -39
  177. package/lib/task/commands/decisions.ts +53 -20
  178. package/lib/task/commands/log.ts +15 -14
  179. package/lib/task/delegation-receipts.ts +199 -0
  180. package/lib/task/events.ts +164 -11
  181. package/lib/task/finalization-artifacts.ts +58 -0
  182. package/lib/task/lifecycle-execution.ts +106 -0
  183. package/lib/task/orchestration.ts +1022 -0
  184. package/lib/task/review-artifacts.ts +201 -69
  185. package/lib/task/review-finalization.ts +306 -0
  186. package/lib/task/review-fingerprint.ts +5 -0
  187. package/lib/task/task-execution-lock.ts +296 -0
  188. package/lib/task/verification-checks.ts +2 -1
  189. package/lib/task/verification-engine.ts +424 -28
  190. package/lib/task/verification-review.ts +1 -0
  191. package/lib/task/verification.ts +8 -3
  192. package/lib/task/workspace-snapshot.ts +57 -0
  193. package/lib/update.ts +48 -184
  194. package/package.json +4 -4
  195. package/templates/.agents/QUICKSTART.en.md +25 -8
  196. package/templates/.agents/QUICKSTART.zh-CN.md +25 -8
  197. package/templates/.agents/README.en.md +29 -25
  198. package/templates/.agents/README.zh-CN.md +29 -25
  199. package/templates/.agents/hooks/lifecycle-delegation.js +75 -0
  200. package/templates/.agents/rules/README.en.md +2 -0
  201. package/templates/.agents/rules/README.zh-CN.md +2 -0
  202. package/templates/.agents/rules/commit-and-pr.en.md +1 -0
  203. package/templates/.agents/rules/commit-and-pr.zh-CN.md +1 -0
  204. package/templates/.agents/rules/create-issue.github.en.md +4 -2
  205. package/templates/.agents/rules/create-issue.github.zh-CN.md +4 -2
  206. package/templates/.agents/rules/human-decision-context.en.md +14 -12
  207. package/templates/.agents/rules/human-decision-context.zh-CN.md +14 -12
  208. package/templates/.agents/rules/issue-fields.github.en.md +3 -1
  209. package/templates/.agents/rules/issue-fields.github.zh-CN.md +3 -1
  210. package/templates/.agents/rules/issue-pr-commands.en.md +5 -3
  211. package/templates/.agents/rules/issue-pr-commands.github.en.md +5 -3
  212. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +5 -3
  213. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +5 -3
  214. package/templates/.agents/rules/issue-sync.github.en.md +7 -3
  215. package/templates/.agents/rules/issue-sync.github.zh-CN.md +8 -4
  216. package/templates/.agents/rules/lifecycle-orchestration.en.md +24 -0
  217. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +24 -0
  218. package/templates/.agents/rules/milestone-inference.github.en.md +4 -2
  219. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +4 -2
  220. package/templates/.agents/rules/next-step-output.en.md +18 -0
  221. package/templates/.agents/rules/next-step-output.zh-CN.md +18 -0
  222. package/templates/.agents/rules/no-mid-flow-questions.en.md +19 -0
  223. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +19 -0
  224. package/templates/.agents/rules/pr-checks-commands.en.md +3 -3
  225. package/templates/.agents/rules/pr-checks-commands.github.en.md +2 -2
  226. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +2 -2
  227. package/templates/.agents/rules/pr-checks-commands.zh-CN.md +3 -3
  228. package/templates/.agents/rules/pr-sync.en.md +3 -1
  229. package/templates/.agents/rules/pr-sync.github.en.md +11 -1
  230. package/templates/.agents/rules/pr-sync.github.zh-CN.md +11 -1
  231. package/templates/.agents/rules/pr-sync.zh-CN.md +3 -1
  232. package/templates/.agents/rules/release-commands.en.md +1 -1
  233. package/templates/.agents/rules/release-commands.github.en.md +21 -5
  234. package/templates/.agents/rules/release-commands.github.zh-CN.md +21 -5
  235. package/templates/.agents/rules/release-commands.zh-CN.md +1 -1
  236. package/templates/.agents/rules/review-handshake.en.md +4 -4
  237. package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
  238. package/templates/.agents/rules/review-method.en.md +113 -0
  239. package/templates/.agents/rules/review-method.zh-CN.md +98 -0
  240. package/templates/.agents/rules/task-management.en.md +11 -0
  241. package/templates/.agents/rules/task-management.zh-CN.md +11 -0
  242. package/templates/.agents/skills/analyze-task/SKILL.en.md +15 -12
  243. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +15 -12
  244. package/templates/.agents/skills/block-task/SKILL.en.md +11 -9
  245. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +11 -9
  246. package/templates/.agents/skills/cancel-task/SKILL.en.md +10 -8
  247. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +11 -9
  248. package/templates/.agents/skills/check-task/SKILL.en.md +16 -16
  249. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -16
  250. package/templates/.agents/skills/close-codescan/SKILL.en.md +7 -5
  251. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +7 -5
  252. package/templates/.agents/skills/close-dependabot/SKILL.en.md +7 -5
  253. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +7 -5
  254. package/templates/.agents/skills/code-task/SKILL.en.md +10 -7
  255. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +13 -10
  256. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +14 -16
  257. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +14 -16
  258. package/templates/.agents/skills/code-task/reference/output-template.en.md +3 -3
  259. package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +3 -3
  260. package/templates/.agents/skills/commit/SKILL.en.md +13 -1
  261. package/templates/.agents/skills/commit/SKILL.zh-CN.md +13 -1
  262. package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
  263. package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
  264. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
  265. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
  266. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  267. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  268. package/templates/.agents/skills/commit/reference/task-status-update.en.md +24 -28
  269. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +24 -28
  270. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +6 -4
  271. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +6 -4
  272. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
  273. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
  274. package/templates/.agents/skills/complete-task/SKILL.en.md +23 -8
  275. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +23 -8
  276. package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
  277. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
  278. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +17 -0
  279. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +17 -0
  280. package/templates/.agents/skills/create-pr/SKILL.en.md +13 -11
  281. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +13 -11
  282. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +1 -1
  283. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +1 -1
  284. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +3 -3
  285. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +3 -3
  286. package/templates/.agents/skills/create-release-note/SKILL.en.md +17 -16
  287. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +17 -16
  288. package/templates/.agents/skills/create-task/SKILL.en.md +16 -14
  289. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +16 -14
  290. package/templates/.agents/skills/import-codescan/SKILL.en.md +5 -5
  291. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +5 -5
  292. package/templates/.agents/skills/import-dependabot/SKILL.en.md +5 -5
  293. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +5 -5
  294. package/templates/.agents/skills/import-issue/SKILL.en.md +10 -8
  295. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +10 -8
  296. package/templates/.agents/skills/init-labels/SKILL.en.md +4 -4
  297. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +4 -4
  298. package/templates/.agents/skills/init-milestones/SKILL.en.md +4 -4
  299. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +4 -4
  300. package/templates/.agents/skills/plan-task/SKILL.en.md +13 -10
  301. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +13 -10
  302. package/templates/.agents/skills/post-release/SKILL.en.md +2 -0
  303. package/templates/.agents/skills/post-release/SKILL.zh-CN.md +2 -0
  304. package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
  305. package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
  306. package/templates/.agents/skills/release/SKILL.en.md +16 -10
  307. package/templates/.agents/skills/release/SKILL.zh-CN.md +16 -10
  308. package/templates/.agents/skills/restore-task/SKILL.en.md +4 -1
  309. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +4 -1
  310. package/templates/.agents/skills/review-analysis/SKILL.en.md +18 -4
  311. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +19 -5
  312. package/templates/.agents/skills/review-analysis/config/verify.en.json +6 -0
  313. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +6 -0
  314. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +13 -15
  315. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +13 -15
  316. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +51 -5
  317. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +52 -6
  318. package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +25 -22
  319. package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +25 -22
  320. package/templates/.agents/skills/review-code/SKILL.en.md +35 -12
  321. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +35 -12
  322. package/templates/.agents/skills/review-code/config/verify.en.json +7 -0
  323. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +7 -0
  324. package/templates/.agents/skills/review-code/reference/concurrency-risks.en.md +25 -0
  325. package/templates/.agents/skills/review-code/reference/concurrency-risks.zh-CN.md +25 -0
  326. package/templates/.agents/skills/review-code/reference/cross-platform-risks.en.md +27 -0
  327. package/templates/.agents/skills/review-code/reference/cross-platform-risks.zh-CN.md +27 -0
  328. package/templates/.agents/skills/review-code/reference/documentation-antipatterns.en.md +13 -0
  329. package/templates/.agents/skills/review-code/reference/documentation-antipatterns.zh-CN.md +13 -0
  330. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +25 -0
  331. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +25 -0
  332. package/templates/.agents/skills/review-code/reference/output-templates.en.md +22 -24
  333. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +22 -24
  334. package/templates/.agents/skills/review-code/reference/report-template.en.md +54 -7
  335. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +54 -7
  336. package/templates/.agents/skills/review-code/reference/review-criteria.en.md +29 -24
  337. package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +29 -24
  338. package/templates/.agents/skills/review-code/reference/security-risks.en.md +25 -0
  339. package/templates/.agents/skills/review-code/reference/security-risks.zh-CN.md +25 -0
  340. package/templates/.agents/skills/review-plan/SKILL.en.md +18 -4
  341. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +19 -5
  342. package/templates/.agents/skills/review-plan/config/verify.en.json +6 -0
  343. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +6 -0
  344. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +13 -15
  345. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +13 -15
  346. package/templates/.agents/skills/review-plan/reference/report-template.en.md +61 -5
  347. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +61 -5
  348. package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +21 -24
  349. package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +21 -24
  350. package/templates/.agents/skills/review-pr/SKILL.en.md +158 -0
  351. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +158 -0
  352. package/templates/.agents/skills/review-pr/config/verify.en.json +37 -0
  353. package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +37 -0
  354. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +65 -0
  355. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +65 -0
  356. package/templates/.agents/skills/review-pr/reference/output-guidance.en.md +18 -0
  357. package/templates/.agents/skills/review-pr/reference/output-guidance.zh-CN.md +18 -0
  358. package/templates/.agents/skills/review-pr/reference/report-template.en.md +85 -0
  359. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +85 -0
  360. package/templates/.agents/skills/run-task/SKILL.en.md +22 -0
  361. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +22 -0
  362. package/templates/.agents/skills/run-task/config/verify.en.json +10 -0
  363. package/templates/.agents/skills/run-task/config/verify.zh-CN.json +10 -0
  364. package/templates/.agents/skills/run-task/reference/host-validation.en.md +20 -0
  365. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +20 -0
  366. package/templates/.agents/skills/test/SKILL.en.md +4 -4
  367. package/templates/.agents/skills/test/SKILL.zh-CN.md +4 -4
  368. package/templates/.agents/skills/test-integration/SKILL.en.md +4 -4
  369. package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +4 -4
  370. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +5 -5
  371. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +5 -5
  372. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +664 -246
  373. package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +4 -4
  374. package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +4 -4
  375. package/templates/.agents/skills/watch-pr/SKILL.en.md +21 -21
  376. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +21 -21
  377. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +27 -6
  378. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +26 -5
  379. package/templates/.agents/templates/task.en.md +1 -1
  380. package/templates/.agents/templates/task.zh-CN.md +1 -1
  381. package/templates/.agents/workflows/bug-fix.en.yaml +3 -3
  382. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +3 -3
  383. package/templates/.agents/workflows/code-review.en.yaml +1 -1
  384. package/templates/.agents/workflows/code-review.zh-CN.yaml +1 -1
  385. package/templates/.agents/workflows/feature-development.en.yaml +4 -4
  386. package/templates/.agents/workflows/feature-development.zh-CN.yaml +4 -4
  387. package/templates/.agents/workflows/refactoring.en.yaml +3 -3
  388. package/templates/.agents/workflows/refactoring.zh-CN.yaml +3 -3
  389. package/templates/.claude/agents/agent-infra-lifecycle-executor.en.md +6 -0
  390. package/templates/.claude/agents/agent-infra-lifecycle-executor.zh-CN.md +6 -0
  391. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.en.md +6 -0
  392. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.zh-CN.md +6 -0
  393. package/templates/.claude/commands/post-release.en.md +1 -0
  394. package/templates/.claude/commands/post-release.zh-CN.md +1 -0
  395. package/templates/.claude/commands/review-pr.en.md +8 -0
  396. package/templates/.claude/commands/review-pr.zh-CN.md +8 -0
  397. package/templates/.claude/commands/run-task.en.md +8 -0
  398. package/templates/.claude/commands/run-task.zh-CN.md +8 -0
  399. package/templates/.claude/settings.json +24 -0
  400. package/templates/.codex/agents/agent-infra-lifecycle-executor.en.toml +3 -0
  401. package/templates/.codex/agents/agent-infra-lifecycle-executor.zh-CN.toml +3 -0
  402. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.en.toml +3 -0
  403. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.zh-CN.toml +3 -0
  404. package/templates/.gemini/commands/_project_/review-pr.en.toml +10 -0
  405. package/templates/.gemini/commands/_project_/review-pr.zh-CN.toml +10 -0
  406. package/templates/.opencode/commands/post-release.en.md +2 -0
  407. package/templates/.opencode/commands/post-release.zh-CN.md +2 -0
  408. package/templates/.opencode/commands/review-pr.en.md +11 -0
  409. package/templates/.opencode/commands/review-pr.zh-CN.md +11 -0
  410. package/templates/.opencode/commands/run-task.en.md +11 -0
  411. package/templates/.opencode/commands/run-task.zh-CN.md +11 -0
  412. package/templates/.gemini/commands/_project_/analyze-task.en.toml +0 -10
  413. package/templates/.gemini/commands/_project_/analyze-task.zh-CN.toml +0 -10
  414. package/templates/.gemini/commands/_project_/archive-tasks.en.toml +0 -10
  415. package/templates/.gemini/commands/_project_/archive-tasks.zh-CN.toml +0 -10
  416. package/templates/.gemini/commands/_project_/block-task.en.toml +0 -10
  417. package/templates/.gemini/commands/_project_/block-task.zh-CN.toml +0 -10
  418. package/templates/.gemini/commands/_project_/cancel-task.en.toml +0 -10
  419. package/templates/.gemini/commands/_project_/cancel-task.zh-CN.toml +0 -10
  420. package/templates/.gemini/commands/_project_/check-task.en.toml +0 -10
  421. package/templates/.gemini/commands/_project_/check-task.zh-CN.toml +0 -10
  422. package/templates/.gemini/commands/_project_/close-codescan.en.toml +0 -8
  423. package/templates/.gemini/commands/_project_/close-codescan.zh-CN.toml +0 -8
  424. package/templates/.gemini/commands/_project_/close-dependabot.en.toml +0 -8
  425. package/templates/.gemini/commands/_project_/close-dependabot.zh-CN.toml +0 -8
  426. package/templates/.gemini/commands/_project_/code-task.en.toml +0 -10
  427. package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +0 -10
  428. package/templates/.gemini/commands/_project_/commit.en.toml +0 -10
  429. package/templates/.gemini/commands/_project_/commit.zh-CN.toml +0 -10
  430. package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +0 -10
  431. package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +0 -10
  432. package/templates/.gemini/commands/_project_/complete-task.en.toml +0 -10
  433. package/templates/.gemini/commands/_project_/complete-task.zh-CN.toml +0 -10
  434. package/templates/.gemini/commands/_project_/create-pr.en.toml +0 -10
  435. package/templates/.gemini/commands/_project_/create-pr.zh-CN.toml +0 -10
  436. package/templates/.gemini/commands/_project_/create-release-note.en.toml +0 -8
  437. package/templates/.gemini/commands/_project_/create-release-note.zh-CN.toml +0 -8
  438. package/templates/.gemini/commands/_project_/create-task.en.toml +0 -8
  439. package/templates/.gemini/commands/_project_/create-task.zh-CN.toml +0 -8
  440. package/templates/.gemini/commands/_project_/import-codescan.en.toml +0 -8
  441. package/templates/.gemini/commands/_project_/import-codescan.zh-CN.toml +0 -8
  442. package/templates/.gemini/commands/_project_/import-dependabot.en.toml +0 -8
  443. package/templates/.gemini/commands/_project_/import-dependabot.zh-CN.toml +0 -8
  444. package/templates/.gemini/commands/_project_/import-issue.en.toml +0 -8
  445. package/templates/.gemini/commands/_project_/import-issue.zh-CN.toml +0 -8
  446. package/templates/.gemini/commands/_project_/init-labels.en.toml +0 -8
  447. package/templates/.gemini/commands/_project_/init-labels.zh-CN.toml +0 -8
  448. package/templates/.gemini/commands/_project_/init-milestones.en.toml +0 -10
  449. package/templates/.gemini/commands/_project_/init-milestones.zh-CN.toml +0 -10
  450. package/templates/.gemini/commands/_project_/plan-task.en.toml +0 -10
  451. package/templates/.gemini/commands/_project_/plan-task.zh-CN.toml +0 -10
  452. package/templates/.gemini/commands/_project_/post-release.en.toml +0 -6
  453. package/templates/.gemini/commands/_project_/post-release.zh-CN.toml +0 -6
  454. package/templates/.gemini/commands/_project_/refine-title.en.toml +0 -8
  455. package/templates/.gemini/commands/_project_/refine-title.zh-CN.toml +0 -8
  456. package/templates/.gemini/commands/_project_/release.en.toml +0 -8
  457. package/templates/.gemini/commands/_project_/release.zh-CN.toml +0 -8
  458. package/templates/.gemini/commands/_project_/restore-task.en.toml +0 -8
  459. package/templates/.gemini/commands/_project_/restore-task.zh-CN.toml +0 -8
  460. package/templates/.gemini/commands/_project_/review-analysis.en.toml +0 -10
  461. package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +0 -10
  462. package/templates/.gemini/commands/_project_/review-code.en.toml +0 -10
  463. package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +0 -10
  464. package/templates/.gemini/commands/_project_/review-plan.en.toml +0 -10
  465. package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +0 -10
  466. package/templates/.gemini/commands/_project_/test-integration.en.toml +0 -6
  467. package/templates/.gemini/commands/_project_/test-integration.zh-CN.toml +0 -6
  468. package/templates/.gemini/commands/_project_/test.en.toml +0 -6
  469. package/templates/.gemini/commands/_project_/test.zh-CN.toml +0 -6
  470. package/templates/.gemini/commands/_project_/update-agent-infra.en.toml +0 -6
  471. package/templates/.gemini/commands/_project_/update-agent-infra.zh-CN.toml +0 -6
  472. package/templates/.gemini/commands/_project_/upgrade-dependency.en.toml +0 -8
  473. package/templates/.gemini/commands/_project_/upgrade-dependency.zh-CN.toml +0 -8
  474. package/templates/.gemini/commands/_project_/watch-pr.en.toml +0 -10
  475. package/templates/.gemini/commands/_project_/watch-pr.zh-CN.toml +0 -10
  476. package/templates/.gemini/settings.json +0 -3
@@ -0,0 +1,1022 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { randomUUID } from 'node:crypto';
4
+
5
+ import { parseTypedTaskFrontmatter } from './frontmatter.ts';
6
+ import { parseReviewSummary } from './review-artifacts.ts';
7
+ import { inspectArtifactDirectory } from './artifact-lifecycle.ts';
8
+ import { parseLedger, summarizeLedgerStage, validateLedgerRows } from './ledger.ts';
9
+ import { resolveTaskRef } from './resolve-ref.ts';
10
+ import {
11
+ activateDelegation,
12
+ completeDelegationStage,
13
+ consumeDelegation,
14
+ managedDelegationRole,
15
+ prepareDelegation,
16
+ sealDelegation
17
+ } from './delegation-receipts.ts';
18
+ import type { DelegationReceipt, DelegationRole, DelegationStage } from './delegation-receipts.ts';
19
+ import { normalizeAgentClients } from '../agent-clients/config.ts';
20
+ import { inspectPlatformPullRequest } from '../platform/pull-requests.ts';
21
+ import {
22
+ getAgentClientCapability,
23
+ getAgentClientDelegationEvidence,
24
+ getAgentClientModelSelection
25
+ } from '../agent-clients/registry.ts';
26
+ import { isAgentClientId } from '../agent-clients/types.ts';
27
+ import type {
28
+ AgentClientId,
29
+ OrchestrationModelPolicy,
30
+ OrchestrationRolePolicy
31
+ } from '../agent-clients/types.ts';
32
+ import {
33
+ captureRepositorySnapshot,
34
+ captureWorkspaceSnapshot,
35
+ diffWorkspaceSnapshots
36
+ } from './workspace-snapshot.ts';
37
+ import type { RepositorySnapshot } from './workspace-snapshot.ts';
38
+ import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
39
+
40
+ type OrchestrationStatus = 'running' | 'paused' | 'completed';
41
+ type LegacyOrchestrationModelPolicy = Readonly<{
42
+ executor: string;
43
+ reviewer: string;
44
+ }>;
45
+ type ModelPolicySource = Readonly<{
46
+ kind: 'explicit' | 'project-config';
47
+ client: AgentClientId;
48
+ resolvedAt: string;
49
+ }>;
50
+ type OrchestrationRecovery = Readonly<{
51
+ code: 'MODEL_POLICY_SUPPLEMENTED';
52
+ recoveredAt: string;
53
+ previousSchemaVersion: 1;
54
+ previousStatus: OrchestrationStatus;
55
+ previousPause: Readonly<{ code: string; message: string; recoverable: boolean }> | null;
56
+ policySource: ModelPolicySource;
57
+ receiptCount: 0;
58
+ pendingDelegation: false;
59
+ resultingStatus: OrchestrationStatus;
60
+ }>;
61
+ type CleanCompletionEvidence = Readonly<{
62
+ kind: 'reviewed-head-clean';
63
+ observedAt: string;
64
+ head: string;
65
+ headTree: string;
66
+ worktreeTree: string;
67
+ lastReviewedCommit: string;
68
+ prNumber: number;
69
+ prHead: string;
70
+ }>;
71
+ type OrchestrationRun = Readonly<{
72
+ schemaVersion: 1 | 2;
73
+ taskId: string;
74
+ runId: string;
75
+ status: OrchestrationStatus;
76
+ nextStage: DelegationStage | null;
77
+ stepCount: number;
78
+ maxSteps: number;
79
+ modelPolicy?: OrchestrationModelPolicy | LegacyOrchestrationModelPolicy;
80
+ modelPolicySource?: ModelPolicySource;
81
+ recoveryHistory?: readonly OrchestrationRecovery[];
82
+ baseline: string;
83
+ pendingDelegation: DelegationReceipt | null;
84
+ receipts: readonly DelegationReceipt[];
85
+ pause: Readonly<{ code: string; message: string; recoverable: boolean }> | null;
86
+ commitAuthorization: Readonly<{ issuedAt: string | null; consumedAt: string | null }>;
87
+ completionEvidence?: CleanCompletionEvidence | null;
88
+ createdAt: string;
89
+ updatedAt: string;
90
+ }>;
91
+ type OrchestrationNext = Readonly<{
92
+ action: 'analyze-task' | 'review-analysis' | 'plan-task' | 'review-plan' | 'code-task' | 'review-code' | 'commit';
93
+ role: DelegationRole;
94
+ stage: DelegationStage;
95
+ round: number;
96
+ artifact: string;
97
+ requestedModel: string | null;
98
+ requestedReasoningEffort: string | null;
99
+ }>;
100
+ type OrchestrationResult = Readonly<{
101
+ status: OrchestrationStatus | 'failed';
102
+ changed: boolean;
103
+ taskId: string | null;
104
+ run: OrchestrationRun | null;
105
+ next: OrchestrationNext | null;
106
+ error: Readonly<{
107
+ code: string;
108
+ message: string;
109
+ client?: AgentClientId;
110
+ missingFields?: readonly string[];
111
+ modelSelectionContext?: ReturnType<typeof getAgentClientModelSelection>;
112
+ }> | null;
113
+ }>;
114
+ type OrchestrationStageIdentity = Readonly<{
115
+ stage: DelegationStage;
116
+ round: number;
117
+ artifact: string;
118
+ role: DelegationRole;
119
+ }>;
120
+ type OrchestrationStageCompletion = Readonly<{
121
+ taskId: string;
122
+ taskDir: string;
123
+ updatedRun: OrchestrationRun;
124
+ }>;
125
+ type OrchestrationCompletionPlanResult = Readonly<{
126
+ result: OrchestrationResult;
127
+ plan: OrchestrationStageCompletion | null;
128
+ }>;
129
+ type OrchestrationOptions = {
130
+ repoRoot?: string;
131
+ id?: () => string;
132
+ now?: () => string;
133
+ maxSteps?: number;
134
+ client?: AgentClientId;
135
+ modelPolicy?: OrchestrationModelPolicy;
136
+ captureWorkspace?: (repoRoot: string, taskId: string | null) => string;
137
+ captureRepository?: (repoRoot: string) => RepositorySnapshot;
138
+ inspectPullRequest?: (taskId: string, options: { cwd: string }) => Readonly<{
139
+ status: string;
140
+ task?: Readonly<{ prNumber?: number | null }>;
141
+ prNumber?: number | null;
142
+ pullRequest?: Readonly<{ head?: Readonly<{ sha?: string }> }> | null;
143
+ error?: Readonly<{ message?: string }> | null;
144
+ }>;
145
+ diffWorkspace?: (repoRoot: string, before: string, after: string) => string[];
146
+ supportsLifecycleDelegation?: (client: AgentClientId) => boolean;
147
+ };
148
+
149
+ function supportsLifecycleDelegation(client: AgentClientId): boolean {
150
+ return getAgentClientCapability(client, 'subagents').level !== 'unsupported'
151
+ && getAgentClientCapability(client, 'orchestration').level !== 'unsupported'
152
+ && getAgentClientDelegationEvidence(client).actualModel !== 'unavailable'
153
+ && getAgentClientDelegationEvidence(client).actualReasoningEffort !== 'unavailable';
154
+ }
155
+
156
+ function orchestrationPath(taskDir: string): string {
157
+ return path.join(taskDir, 'orchestration.json');
158
+ }
159
+
160
+ function readRun(taskDir: string): OrchestrationRun | null {
161
+ const file = orchestrationPath(taskDir);
162
+ if (!fs.existsSync(file)) return null;
163
+ const run = JSON.parse(fs.readFileSync(file, 'utf8')) as OrchestrationRun;
164
+ if (run.schemaVersion !== 2 || !isV2Policy(run.modelPolicy)) return run;
165
+ return {
166
+ ...run,
167
+ modelPolicy: {
168
+ executor: {
169
+ model: run.modelPolicy.executor.model,
170
+ reasoningEffort: run.modelPolicy.executor.reasoningEffort
171
+ },
172
+ reviewer: {
173
+ model: run.modelPolicy.reviewer.model,
174
+ reasoningEffort: run.modelPolicy.reviewer.reasoningEffort
175
+ }
176
+ }
177
+ };
178
+ }
179
+
180
+ function atomicWrite(file: string, value: unknown): void {
181
+ const temp = `${file}.${process.pid}.${randomUUID()}.tmp`;
182
+ fs.writeFileSync(temp, `${JSON.stringify(value, null, 2)}\n`, { encoding: 'utf8', flag: 'wx' });
183
+ fs.renameSync(temp, file);
184
+ }
185
+
186
+ function saveRun(taskDir: string, run: OrchestrationRun): void {
187
+ atomicWrite(orchestrationPath(taskDir), run);
188
+ }
189
+
190
+ function withUpdatedRun(run: OrchestrationRun, updates: Partial<OrchestrationRun>): OrchestrationRun {
191
+ return Object.freeze({ ...run, ...updates, updatedAt: new Date().toISOString() });
192
+ }
193
+
194
+ function failed(
195
+ code: string,
196
+ message: string,
197
+ taskId: string | null = null,
198
+ details: Omit<NonNullable<OrchestrationResult['error']>, 'code' | 'message'> = {}
199
+ ): OrchestrationResult {
200
+ return { status: 'failed', changed: false, taskId, run: null, next: null, error: { code, message, ...details } };
201
+ }
202
+
203
+ function validModel(value: unknown): value is string {
204
+ return typeof value === 'string' && value.length > 0 && value.trim() === value;
205
+ }
206
+
207
+ function validateModelPolicy(policy: OrchestrationModelPolicy | undefined): Readonly<{ code: string; message: string }> | null {
208
+ if (
209
+ !policy
210
+ || !validModel(policy.executor?.model)
211
+ || !validModel(policy.executor?.reasoningEffort)
212
+ || !validModel(policy.reviewer?.model)
213
+ || !validModel(policy.reviewer?.reasoningEffort)
214
+ ) {
215
+ return { code: 'ORCHESTRATION_MODEL_POLICY_REQUIRED', message: 'executor and reviewer model and reasoning effort are required' };
216
+ }
217
+ return null;
218
+ }
219
+
220
+ function sameModelPolicy(left: OrchestrationModelPolicy, right: OrchestrationModelPolicy): boolean {
221
+ return left.executor.model === right.executor.model
222
+ && left.executor.reasoningEffort === right.executor.reasoningEffort
223
+ && left.reviewer.model === right.reviewer.model
224
+ && left.reviewer.reasoningEffort === right.reviewer.reasoningEffort;
225
+ }
226
+
227
+ function isV2Policy(policy: OrchestrationRun['modelPolicy']): policy is OrchestrationModelPolicy {
228
+ return typeof policy?.executor === 'object' && typeof policy?.reviewer === 'object';
229
+ }
230
+
231
+ function rolePolicy(run: OrchestrationRun, role: DelegationRole): OrchestrationRolePolicy | null {
232
+ return isV2Policy(run.modelPolicy) ? run.modelPolicy[role] : null;
233
+ }
234
+
235
+ function resolveProjectPolicy(repoRoot: string, client: AgentClientId): OrchestrationModelPolicy | undefined {
236
+ const configPath = path.join(repoRoot, '.agents', '.airc.json');
237
+ if (!fs.existsSync(configPath)) return undefined;
238
+ const raw = JSON.parse(fs.readFileSync(configPath, 'utf8')) as Record<string, unknown>;
239
+ return normalizeAgentClients(raw).state[client].orchestration;
240
+ }
241
+
242
+ function beginOrResumeOrchestration(taskRef: string, options: OrchestrationOptions = {}): OrchestrationResult {
243
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
244
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
245
+ if (!isAgentClientId(options.client)) {
246
+ return failed('ORCHESTRATION_PAYLOAD_INVALID', 'begin-or-resume requires a known client', resolved.taskId);
247
+ }
248
+ let existing: OrchestrationRun | null;
249
+ try {
250
+ existing = readRun(resolved.taskDir);
251
+ } catch (error) {
252
+ return failed('ORCHESTRATION_STATE_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
253
+ }
254
+ if (existing) {
255
+ if (
256
+ ![1, 2].includes(existing.schemaVersion)
257
+ || !Array.isArray(existing.receipts)
258
+ || !Object.prototype.hasOwnProperty.call(existing, 'pendingDelegation')
259
+ ) {
260
+ return failed('ORCHESTRATION_STATE_INVALID', 'orchestration state has an invalid schema', resolved.taskId);
261
+ }
262
+ if (existing.status === 'completed' && existing.schemaVersion === 1) {
263
+ return { status: 'completed', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
264
+ }
265
+ if (existing.schemaVersion === 2) {
266
+ const policyError = validateModelPolicy(isV2Policy(existing.modelPolicy) ? existing.modelPolicy : undefined);
267
+ if (
268
+ policyError
269
+ || !existing.modelPolicySource
270
+ || !isAgentClientId(existing.modelPolicySource.client)
271
+ || !Array.isArray(existing.recoveryHistory)
272
+ ) {
273
+ return failed('ORCHESTRATION_STATE_INVALID', 'schemaVersion 2 orchestration state is incomplete', resolved.taskId);
274
+ }
275
+ if (existing.status === 'completed') {
276
+ return { status: 'completed', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
277
+ }
278
+ if (existing.modelPolicySource.client !== options.client) {
279
+ return failed('ORCHESTRATION_CLIENT_MISMATCH', 'provided client does not match the persisted run policy source', resolved.taskId);
280
+ }
281
+ if (options.modelPolicy) {
282
+ const suppliedError = validateModelPolicy(options.modelPolicy);
283
+ if (suppliedError) return failed(suppliedError.code, suppliedError.message, resolved.taskId);
284
+ if (!sameModelPolicy(existing.modelPolicy as OrchestrationModelPolicy, options.modelPolicy)) {
285
+ return failed('ORCHESTRATION_MODEL_POLICY_MISMATCH', 'provided model policy does not match the persisted run policy', resolved.taskId);
286
+ }
287
+ }
288
+ if (existing.status === 'paused' && existing.pause?.recoverable && existing.pendingDelegation === null) {
289
+ const resumed = withUpdatedRun(existing, { status: 'running', pause: null });
290
+ saveRun(resolved.taskDir, resumed);
291
+ return { status: 'running', changed: true, taskId: resolved.taskId, run: resumed, next: null, error: null };
292
+ }
293
+ return { status: existing.status, changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
294
+ }
295
+
296
+ let policy: OrchestrationModelPolicy | undefined = options.modelPolicy;
297
+ let sourceKind: ModelPolicySource['kind'] = 'explicit';
298
+ if (!policy) {
299
+ try {
300
+ policy = resolveProjectPolicy(resolved.repoRoot, options.client);
301
+ sourceKind = 'project-config';
302
+ } catch (error) {
303
+ return failed('ORCHESTRATION_CONFIG_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
304
+ }
305
+ }
306
+ const policyError = validateModelPolicy(policy);
307
+ if (policyError) {
308
+ return failed(policyError.code, policyError.message, resolved.taskId, {
309
+ client: options.client,
310
+ missingFields: ['executor.model', 'executor.reasoningEffort', 'reviewer.model', 'reviewer.reasoningEffort'],
311
+ modelSelectionContext: getAgentClientModelSelection(options.client)
312
+ });
313
+ }
314
+ if (existing.pendingDelegation !== null) {
315
+ const alreadyPaused = existing.status === 'paused' && existing.pause?.code === 'ORCHESTRATION_DELEGATION_BUSY';
316
+ if (alreadyPaused) return { status: 'paused', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
317
+ const paused = withUpdatedRun(existing, {
318
+ status: 'paused',
319
+ pause: { code: 'ORCHESTRATION_DELEGATION_BUSY', message: 'legacy run has a pending delegation', recoverable: false }
320
+ });
321
+ saveRun(resolved.taskDir, paused);
322
+ return { status: 'paused', changed: true, taskId: resolved.taskId, run: paused, next: null, error: null };
323
+ }
324
+ if (existing.receipts.length > 0) {
325
+ const alreadyPaused = existing.status === 'paused' && existing.pause?.code === 'ORCHESTRATION_HISTORICAL_EFFORT_UNVERIFIED';
326
+ if (alreadyPaused) return { status: 'paused', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
327
+ const paused = withUpdatedRun(existing, {
328
+ status: 'paused',
329
+ pause: {
330
+ code: 'ORCHESTRATION_HISTORICAL_EFFORT_UNVERIFIED',
331
+ message: 'legacy receipts do not contain actual reasoning-effort evidence',
332
+ recoverable: false
333
+ }
334
+ });
335
+ saveRun(resolved.taskDir, paused);
336
+ return { status: 'paused', changed: true, taskId: resolved.taskId, run: paused, next: null, error: null };
337
+ }
338
+ if (
339
+ existing.modelPolicy
340
+ && typeof existing.modelPolicy.executor === 'string'
341
+ && (
342
+ existing.modelPolicy.executor !== policy!.executor.model
343
+ || existing.modelPolicy.reviewer !== policy!.reviewer.model
344
+ )
345
+ ) {
346
+ return failed('ORCHESTRATION_MODEL_POLICY_MISMATCH', 'provided model policy does not match the persisted run policy', resolved.taskId);
347
+ }
348
+ const now = (options.now ?? (() => new Date().toISOString()))();
349
+ const source: ModelPolicySource = { kind: sourceKind, client: options.client, resolvedAt: now };
350
+ const clearsModelPause = existing.pause?.code === 'ORCHESTRATION_MODEL_EVIDENCE_MISSING';
351
+ const resultingStatus = clearsModelPause ? 'running' : existing.status;
352
+ const recovery: OrchestrationRecovery = {
353
+ code: 'MODEL_POLICY_SUPPLEMENTED',
354
+ recoveredAt: now,
355
+ previousSchemaVersion: 1,
356
+ previousStatus: existing.status,
357
+ previousPause: existing.pause,
358
+ policySource: source,
359
+ receiptCount: 0,
360
+ pendingDelegation: false,
361
+ resultingStatus
362
+ };
363
+ const recovered: OrchestrationRun = Object.freeze({
364
+ ...existing,
365
+ schemaVersion: 2,
366
+ status: resultingStatus,
367
+ modelPolicy: policy!,
368
+ modelPolicySource: source,
369
+ recoveryHistory: Object.freeze([recovery]),
370
+ pause: clearsModelPause ? null : existing.pause,
371
+ updatedAt: now
372
+ });
373
+ saveRun(resolved.taskDir, recovered);
374
+ return { status: resultingStatus, changed: true, taskId: resolved.taskId, run: recovered, next: null, error: null };
375
+ }
376
+
377
+ let policy = options.modelPolicy;
378
+ let sourceKind: ModelPolicySource['kind'] = 'explicit';
379
+ if (!policy) {
380
+ try {
381
+ policy = resolveProjectPolicy(resolved.repoRoot, options.client);
382
+ sourceKind = 'project-config';
383
+ } catch (error) {
384
+ return failed('ORCHESTRATION_CONFIG_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
385
+ }
386
+ }
387
+ const policyError = validateModelPolicy(policy);
388
+ if (policyError) return failed(policyError.code, policyError.message, resolved.taskId, {
389
+ client: options.client,
390
+ missingFields: ['executor.model', 'executor.reasoningEffort', 'reviewer.model', 'reviewer.reasoningEffort'],
391
+ modelSelectionContext: getAgentClientModelSelection(options.client)
392
+ });
393
+ const now = (options.now ?? (() => new Date().toISOString()))();
394
+ const source: ModelPolicySource = { kind: sourceKind, client: options.client, resolvedAt: now };
395
+ const run: OrchestrationRun = {
396
+ schemaVersion: 2,
397
+ taskId: resolved.taskId,
398
+ runId: (options.id ?? randomUUID)(),
399
+ status: 'running',
400
+ nextStage: null,
401
+ stepCount: 0,
402
+ maxSteps: options.maxSteps ?? 24,
403
+ modelPolicy: policy!,
404
+ modelPolicySource: source,
405
+ recoveryHistory: [],
406
+ baseline: '',
407
+ pendingDelegation: null,
408
+ receipts: [],
409
+ pause: null,
410
+ commitAuthorization: { issuedAt: null, consumedAt: null },
411
+ completionEvidence: null,
412
+ createdAt: now,
413
+ updatedAt: now
414
+ };
415
+ atomicWrite(orchestrationPath(resolved.taskDir), run);
416
+ return { status: 'running', changed: true, taskId: resolved.taskId, run, next: null, error: null };
417
+ }
418
+
419
+ function highestRound(taskDir: string, family: string): number {
420
+ const pattern = new RegExp(`^${family}(?:-r(\\d+))?\\.md$`);
421
+ return fs.readdirSync(taskDir).reduce((max, name) => {
422
+ const match = pattern.exec(name);
423
+ return match ? Math.max(max, match[1] ? Number(match[1]) : 1) : max;
424
+ }, 0);
425
+ }
426
+
427
+ function artifactName(family: string, round: number): string {
428
+ return round === 1 ? `${family}.md` : `${family}-r${round}.md`;
429
+ }
430
+
431
+ function hasReviewAfterArtifact(taskDir: string, family: string, reviewFamily: string): boolean {
432
+ const artifact = inspectArtifactDirectory(taskDir, family as 'analysis' | 'plan' | 'code');
433
+ const review = inspectArtifactDirectory(taskDir, reviewFamily as 'review-analysis' | 'review-plan' | 'review-code');
434
+ return artifact.status === 'ready'
435
+ && review.status === 'ready'
436
+ && artifact.latest !== null
437
+ && review.reviewedInput?.name === artifact.latest.name;
438
+ }
439
+
440
+ function latestReviewApproved(taskDir: string, family: 'review-analysis' | 'review-plan' | 'review-code'): boolean {
441
+ const round = highestRound(taskDir, family);
442
+ if (round === 0) return false;
443
+ const parsed = parseReviewSummary(fs.readFileSync(path.join(taskDir, artifactName(family, round)), 'utf8'));
444
+ return parsed.ok && parsed.summary.verdict === 'Approved' && parsed.summary.counts !== null;
445
+ }
446
+
447
+ function routeFromFacts(taskDir: string): Omit<OrchestrationNext, 'requestedModel' | 'requestedReasoningEffort'> | null {
448
+ const analysisRound = highestRound(taskDir, 'analysis');
449
+ const analysisReviewRound = highestRound(taskDir, 'review-analysis');
450
+ const planRound = highestRound(taskDir, 'plan');
451
+ const planReviewRound = highestRound(taskDir, 'review-plan');
452
+ const codeRound = highestRound(taskDir, 'code');
453
+ const codeReviewRound = highestRound(taskDir, 'review-code');
454
+ if (analysisRound === 0) {
455
+ const round = analysisRound + 1;
456
+ return { action: 'analyze-task', role: 'executor', stage: 'analysis', round, artifact: artifactName('analysis', round) };
457
+ }
458
+ if (!hasReviewAfterArtifact(taskDir, 'analysis', 'review-analysis')) {
459
+ const round = analysisReviewRound + 1;
460
+ return { action: 'review-analysis', role: 'reviewer', stage: 'review-analysis', round, artifact: artifactName('review-analysis', round) };
461
+ }
462
+ if (!latestReviewApproved(taskDir, 'review-analysis')) {
463
+ const round = analysisRound + 1;
464
+ return { action: 'analyze-task', role: 'executor', stage: 'analysis', round, artifact: artifactName('analysis', round) };
465
+ }
466
+ if (planRound === 0) {
467
+ const round = planRound + 1;
468
+ return { action: 'plan-task', role: 'executor', stage: 'plan', round, artifact: artifactName('plan', round) };
469
+ }
470
+ if (!hasReviewAfterArtifact(taskDir, 'plan', 'review-plan')) {
471
+ const round = planReviewRound + 1;
472
+ return { action: 'review-plan', role: 'reviewer', stage: 'review-plan', round, artifact: artifactName('review-plan', round) };
473
+ }
474
+ if (!latestReviewApproved(taskDir, 'review-plan')) {
475
+ const round = planRound + 1;
476
+ return { action: 'plan-task', role: 'executor', stage: 'plan', round, artifact: artifactName('plan', round) };
477
+ }
478
+ if (codeRound === 0) {
479
+ const round = codeRound + 1;
480
+ return { action: 'code-task', role: 'executor', stage: 'code', round, artifact: artifactName('code', round) };
481
+ }
482
+ if (!hasReviewAfterArtifact(taskDir, 'code', 'review-code')) {
483
+ const round = codeReviewRound + 1;
484
+ return { action: 'review-code', role: 'reviewer', stage: 'review-code', round, artifact: artifactName('review-code', round) };
485
+ }
486
+ if (!latestReviewApproved(taskDir, 'review-code')) {
487
+ const round = codeRound + 1;
488
+ return { action: 'code-task', role: 'executor', stage: 'code', round, artifact: artifactName('code', round) };
489
+ }
490
+ return { action: 'commit', role: 'executor', stage: 'commit', round: 1, artifact: 'commit' };
491
+ }
492
+
493
+ function routeOrchestration(taskRef: string, options: OrchestrationOptions = {}): OrchestrationResult {
494
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
495
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
496
+ let content: string;
497
+ try {
498
+ content = fs.readFileSync(resolved.taskMdPath, 'utf8');
499
+ } catch (error) {
500
+ return failed('ORCHESTRATION_TASK_READ_FAILED', String(error), resolved.taskId);
501
+ }
502
+ let metadata: ReturnType<typeof parseTypedTaskFrontmatter>;
503
+ try {
504
+ metadata = parseTypedTaskFrontmatter(content);
505
+ } catch (error) {
506
+ return failed('ORCHESTRATION_TASK_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
507
+ }
508
+ const routed = routeFromFacts(resolved.taskDir);
509
+ if (!routed) return failed('ORCHESTRATION_ROUTE_UNKNOWN', 'cannot determine a unique lifecycle action', resolved.taskId);
510
+ const run = readRun(resolved.taskDir);
511
+ const policy = run ? rolePolicy(run, routed.role) : null;
512
+ const next: OrchestrationNext = {
513
+ ...routed,
514
+ requestedModel: policy?.model ?? null,
515
+ requestedReasoningEffort: policy?.reasoningEffort ?? null
516
+ };
517
+ if (next.stage === 'commit') {
518
+ const reviewRound = highestRound(resolved.taskDir, 'review-code');
519
+ const review = parseReviewSummary(fs.readFileSync(
520
+ path.join(resolved.taskDir, artifactName('review-code', reviewRound)),
521
+ 'utf8'
522
+ ));
523
+ if (!review.ok || review.summary.manualValidation === null) {
524
+ return failed('ORCHESTRATION_REVIEW_INVALID', 'latest code review has no numeric manual-validation count', resolved.taskId);
525
+ }
526
+ const rows = parseLedger(content);
527
+ const ledgerError = validateLedgerRows(rows);
528
+ if (ledgerError) return failed('ORCHESTRATION_LEDGER_INVALID', ledgerError.message, resolved.taskId);
529
+ if (!summarizeLedgerStage(rows, 'code').canAdvance) {
530
+ return failed('ORCHESTRATION_LEDGER_BLOCKED', 'code review ledger has unresolved findings or human decisions', resolved.taskId);
531
+ }
532
+ if (run?.status === 'completed') {
533
+ return { status: 'completed', changed: false, taskId: resolved.taskId, run, next: null, error: null };
534
+ }
535
+
536
+ let config: unknown;
537
+ try {
538
+ config = JSON.parse(fs.readFileSync(path.join(resolved.repoRoot, '.agents', '.airc.json'), 'utf8'));
539
+ } catch (error) {
540
+ return failed('ORCHESTRATION_CONFIG_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
541
+ }
542
+ if (!config || typeof config !== 'object' || Array.isArray(config)) {
543
+ return failed('ORCHESTRATION_CONFIG_INVALID', 'project configuration must be an object', resolved.taskId);
544
+ }
545
+ const projectConfig = config as Record<string, unknown>;
546
+ if (!Object.hasOwn(projectConfig, 'prFlow') || projectConfig.prFlow === 'disabled') {
547
+ return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
548
+ }
549
+ if (projectConfig.prFlow !== 'required') {
550
+ return failed('ORCHESTRATION_CONFIG_INVALID', "project configuration 'prFlow' must be 'required' or 'disabled'", resolved.taskId);
551
+ }
552
+ const prNumber = Number(metadata.pr_number);
553
+ if (!Number.isInteger(prNumber) || prNumber <= 0) {
554
+ return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
555
+ }
556
+ if (!run) {
557
+ return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
558
+ }
559
+ if (run.status !== 'running' || run.pendingDelegation !== null) {
560
+ return failed('ORCHESTRATION_RUN_NOT_RUNNING', 'clean completion requires an idle running orchestration', resolved.taskId);
561
+ }
562
+
563
+ const capture = options.captureRepository ?? captureRepositorySnapshot;
564
+ let before: RepositorySnapshot;
565
+ try {
566
+ before = capture(resolved.repoRoot);
567
+ } catch (error) {
568
+ return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
569
+ }
570
+ if (before.headTree !== before.worktreeTree) {
571
+ return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
572
+ }
573
+ const lastReviewedCommit = String(metadata.last_reviewed_commit ?? '');
574
+ if (before.head !== lastReviewedCommit) {
575
+ return failed('ORCHESTRATION_REVIEWED_HEAD_MISMATCH', 'local HEAD does not match last_reviewed_commit', resolved.taskId);
576
+ }
577
+
578
+ const inspect: NonNullable<OrchestrationOptions['inspectPullRequest']> =
579
+ options.inspectPullRequest ?? inspectPlatformPullRequest;
580
+ let inspection: ReturnType<NonNullable<OrchestrationOptions['inspectPullRequest']>>;
581
+ try {
582
+ inspection = inspect(resolved.taskId, { cwd: resolved.repoRoot });
583
+ } catch (error) {
584
+ return failed('ORCHESTRATION_PR_INSPECTION_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
585
+ }
586
+ if (inspection.status === 'blocked') {
587
+ return failed('ORCHESTRATION_PR_INSPECTION_BLOCKED', inspection.error?.message ?? 'pull request inspection is blocked', resolved.taskId);
588
+ }
589
+ if (inspection.status === 'failed') {
590
+ return failed('ORCHESTRATION_PR_INSPECTION_FAILED', inspection.error?.message ?? 'pull request inspection failed', resolved.taskId);
591
+ }
592
+ const inspectedPrNumber = inspection.task?.prNumber ?? inspection.prNumber;
593
+ const prHead = inspection.pullRequest?.head?.sha;
594
+ if (typeof prHead !== 'string') {
595
+ return failed('ORCHESTRATION_PR_INSPECTION_INVALID', 'pull request inspection returned incomplete identity evidence', resolved.taskId);
596
+ }
597
+ if (inspectedPrNumber !== prNumber || prHead !== before.head) {
598
+ return failed('ORCHESTRATION_PR_HEAD_MISMATCH', 'pull request identity does not match the reviewed local HEAD', resolved.taskId);
599
+ }
600
+
601
+ let after: RepositorySnapshot;
602
+ try {
603
+ after = capture(resolved.repoRoot);
604
+ } catch (error) {
605
+ return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
606
+ }
607
+ if (after.headTree !== after.worktreeTree) {
608
+ return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
609
+ }
610
+ if (after.head !== before.head || after.headTree !== before.headTree) {
611
+ return failed('ORCHESTRATION_REPOSITORY_CHANGED', 'repository changed while clean completion evidence was collected', resolved.taskId);
612
+ }
613
+
614
+ const observedAt = (options.now ?? (() => new Date().toISOString()))();
615
+ const completionEvidence: CleanCompletionEvidence = {
616
+ kind: 'reviewed-head-clean',
617
+ observedAt,
618
+ head: after.head,
619
+ headTree: after.headTree,
620
+ worktreeTree: after.worktreeTree,
621
+ lastReviewedCommit,
622
+ prNumber,
623
+ prHead
624
+ };
625
+ const completed = withUpdatedRun(run, {
626
+ status: 'completed',
627
+ nextStage: null,
628
+ pause: null,
629
+ completionEvidence
630
+ });
631
+ saveRun(resolved.taskDir, completed);
632
+ return { status: 'completed', changed: true, taskId: resolved.taskId, run: completed, next: null, error: null };
633
+ }
634
+ return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
635
+ }
636
+
637
+ function statusOrchestration(taskRef: string, options: OrchestrationOptions = {}): OrchestrationResult {
638
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
639
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
640
+ const run = readRun(resolved.taskDir);
641
+ if (!run) return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
642
+ return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
643
+ }
644
+
645
+ function prepareOrchestrationDelegationUnlocked(
646
+ taskRef: string,
647
+ input: Readonly<{ client: AgentClientId; requestedModel?: string; requestedReasoningEffort?: string }>,
648
+ options: OrchestrationOptions = {}
649
+ ): OrchestrationResult {
650
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
651
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
652
+ if (!(options.supportsLifecycleDelegation ?? supportsLifecycleDelegation)(input.client)) {
653
+ return failed('ORCHESTRATION_CLIENT_UNSUPPORTED', `client '${input.client}' does not support lifecycle orchestration`, resolved.taskId);
654
+ }
655
+ const run = readRun(resolved.taskDir);
656
+ if (!run || run.status !== 'running') return failed('ORCHESTRATION_RUN_NOT_RUNNING', 'a running orchestration is required', resolved.taskId);
657
+ if (!isV2Policy(run.modelPolicy)) return failed('ORCHESTRATION_MODEL_EVIDENCE_MISSING', 'running orchestration has no persisted model policy', resolved.taskId);
658
+ if (run.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_BUSY', 'the run already has a pending delegation', resolved.taskId);
659
+ const repositoryPending = (['claude-code', 'codex'] as AgentClientId[])
660
+ .flatMap((client) => matchingDelegations(client, () => true, options));
661
+ if (repositoryPending.length > 0) {
662
+ return failed('ORCHESTRATION_DELEGATION_BUSY', 'the repository already has a pending lifecycle delegation', resolved.taskId);
663
+ }
664
+ if (run.stepCount >= run.maxSteps) return pauseOrchestration(taskRef, 'ORCHESTRATION_MAX_STEPS', 'maximum orchestration steps reached', true, options);
665
+ const routed = routeOrchestration(taskRef, options);
666
+ if (!routed.next) return routed;
667
+ const next = routed.next;
668
+ if (!validModel(input.requestedModel)) {
669
+ return failed('ORCHESTRATION_REQUESTED_MODEL_REQUIRED', 'prepare requires the exact requested model identity', resolved.taskId);
670
+ }
671
+ if (!validModel(input.requestedReasoningEffort)) {
672
+ return failed('ORCHESTRATION_REQUESTED_REASONING_EFFORT_REQUIRED', 'prepare requires the exact requested reasoning effort', resolved.taskId);
673
+ }
674
+ const expectedPolicy = run.modelPolicy[next.role];
675
+ if (input.requestedModel !== expectedPolicy.model) {
676
+ return failed('ORCHESTRATION_REQUESTED_MODEL_MISMATCH', `requested model does not match the persisted ${next.role} model`, resolved.taskId);
677
+ }
678
+ if (input.requestedReasoningEffort !== expectedPolicy.reasoningEffort) {
679
+ return failed('ORCHESTRATION_REQUESTED_REASONING_EFFORT_MISMATCH', `requested reasoning effort does not match the persisted ${next.role} policy`, resolved.taskId);
680
+ }
681
+ let beforeFingerprint: string;
682
+ try {
683
+ beforeFingerprint = (options.captureWorkspace ?? captureWorkspaceSnapshot)(resolved.repoRoot, resolved.taskId);
684
+ } catch (error) {
685
+ return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
686
+ }
687
+ const receipt = prepareDelegation({
688
+ taskId: resolved.taskId,
689
+ runId: run.runId,
690
+ role: next.role,
691
+ stage: next.stage,
692
+ round: next.round,
693
+ artifact: next.artifact,
694
+ client: input.client,
695
+ requestedModel: input.requestedModel,
696
+ requestedReasoningEffort: input.requestedReasoningEffort,
697
+ workspaceSnapshotScope: 'task',
698
+ beforeFingerprint
699
+ }, { id: options.id, now: options.now });
700
+ const updated = withUpdatedRun(run, {
701
+ nextStage: next.stage,
702
+ pendingDelegation: receipt,
703
+ commitAuthorization: next.stage === 'commit' && run.commitAuthorization.issuedAt === null
704
+ ? { issuedAt: (options.now ?? (() => new Date().toISOString()))(), consumedAt: null }
705
+ : run.commitAuthorization
706
+ });
707
+ saveRun(resolved.taskDir, updated);
708
+ return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next, error: null };
709
+ }
710
+
711
+ function prepareOrchestrationDelegation(
712
+ taskRef: string,
713
+ input: Readonly<{ client: AgentClientId; requestedModel?: string; requestedReasoningEffort?: string }>,
714
+ options: OrchestrationOptions = {}
715
+ ): OrchestrationResult {
716
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
717
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
718
+ try {
719
+ return withTaskExecutionLock(
720
+ resolved.repoRoot,
721
+ resolved.taskId,
722
+ 'task-orchestration.prepare',
723
+ () => prepareOrchestrationDelegationUnlocked(taskRef, input, options)
724
+ );
725
+ } catch (error) {
726
+ if (error instanceof TaskExecutionLockError) return failed(error.code, error.message, resolved.taskId);
727
+ throw error;
728
+ }
729
+ }
730
+
731
+ function matchingDelegations(
732
+ client: AgentClientId,
733
+ predicate: (receipt: DelegationReceipt) => boolean,
734
+ options: OrchestrationOptions
735
+ ): Array<{ taskId: string; run: OrchestrationRun }> {
736
+ const repoRoot = options.repoRoot ?? process.cwd();
737
+ const activeRoot = path.join(repoRoot, '.agents', 'workspace', 'active');
738
+ if (!fs.existsSync(activeRoot)) return [];
739
+ return fs.readdirSync(activeRoot, { withFileTypes: true })
740
+ .filter((entry) => entry.isDirectory())
741
+ .flatMap((entry) => {
742
+ const run = readRun(path.join(activeRoot, entry.name));
743
+ if (!run?.pendingDelegation) return [];
744
+ const receipt = run.pendingDelegation;
745
+ return receipt.client === client && predicate(receipt)
746
+ ? [{ taskId: entry.name, run }]
747
+ : [];
748
+ });
749
+ }
750
+
751
+ function uniqueMatchingDelegation(
752
+ client: AgentClientId,
753
+ predicate: (receipt: DelegationReceipt) => boolean,
754
+ options: OrchestrationOptions
755
+ ): { taskId: string; run: OrchestrationRun } | OrchestrationResult {
756
+ const matches = matchingDelegations(client, predicate, options);
757
+ if (matches.length === 0) return failed('ORCHESTRATION_DELEGATION_MISSING', 'no matching lifecycle delegation exists');
758
+ if (matches.length > 1) return failed('ORCHESTRATION_DELEGATION_AMBIGUOUS', 'multiple lifecycle delegations match the native hook event');
759
+ return matches[0]!;
760
+ }
761
+
762
+ function activateMatchingOrchestrationDelegation(
763
+ client: AgentClientId,
764
+ event: Parameters<typeof activateDelegation>[1],
765
+ options: OrchestrationOptions = {}
766
+ ): OrchestrationResult {
767
+ const role = managedDelegationRole(event.nativeAgent);
768
+ if (!role) return failed('DELEGATION_IGNORED', `subagent '${event.nativeAgent}' is not lifecycle-managed`);
769
+ const matched = uniqueMatchingDelegation(
770
+ client,
771
+ (receipt) => receipt.status === 'prepared',
772
+ options
773
+ );
774
+ if ('status' in matched) return matched;
775
+ if (matched.run.receipts.some((receipt) => receipt.childId === event.childId)) {
776
+ return pauseOrchestration(matched.taskId, 'DELEGATION_IDENTITY_REUSED', 'native child identity was already used by this run', true, options);
777
+ }
778
+ return activateOrchestrationDelegation(matched.taskId, event, options);
779
+ }
780
+
781
+ function sealMatchingOrchestrationDelegation(
782
+ client: AgentClientId,
783
+ event: Readonly<{ nativeAgent: string; childId: string }>,
784
+ options: OrchestrationOptions = {}
785
+ ): OrchestrationResult {
786
+ const role = managedDelegationRole(event.nativeAgent);
787
+ if (!role) return failed('DELEGATION_IGNORED', `subagent '${event.nativeAgent}' is not lifecycle-managed`);
788
+ const matched = uniqueMatchingDelegation(
789
+ client,
790
+ (receipt) => receipt.status !== 'prepared',
791
+ options
792
+ );
793
+ if ('status' in matched) return matched;
794
+ const receipt = matched.run.pendingDelegation!;
795
+ if (receipt.role !== role) {
796
+ return pauseOrchestration(matched.taskId, 'DELEGATION_ROLE_MISMATCH', `managed role ${role} does not match ${receipt.role}`, true, options);
797
+ }
798
+ const repoRoot = options.repoRoot ?? process.cwd();
799
+ try {
800
+ const snapshotTaskId = receipt.workspaceSnapshotScope === 'task' ? receipt.taskId : null;
801
+ const afterFingerprint = (options.captureWorkspace ?? captureWorkspaceSnapshot)(repoRoot, snapshotTaskId);
802
+ const changedPaths = (options.diffWorkspace ?? diffWorkspaceSnapshots)(repoRoot, receipt.beforeFingerprint, afterFingerprint);
803
+ return sealOrchestrationDelegation(matched.taskId, {
804
+ childId: event.childId,
805
+ exitCode: 0,
806
+ afterFingerprint,
807
+ changedPaths
808
+ }, options);
809
+ } catch (error) {
810
+ return pauseOrchestration(
811
+ matched.taskId,
812
+ 'ORCHESTRATION_SNAPSHOT_FAILED',
813
+ error instanceof Error ? error.message : String(error),
814
+ true,
815
+ options
816
+ );
817
+ }
818
+ }
819
+
820
+ function activateOrchestrationDelegation(
821
+ taskRef: string,
822
+ event: Parameters<typeof activateDelegation>[1],
823
+ options: OrchestrationOptions = {}
824
+ ): OrchestrationResult {
825
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
826
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
827
+ const run = readRun(resolved.taskDir);
828
+ if (!run?.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
829
+ const result = activateDelegation(run.pendingDelegation, event, { now: options.now });
830
+ if (!result.ok) {
831
+ if (result.code === 'DELEGATION_IGNORED') return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
832
+ return pauseOrchestration(taskRef, result.code, result.message, true, options);
833
+ }
834
+ const updated = withUpdatedRun(run, { pendingDelegation: result.receipt });
835
+ saveRun(resolved.taskDir, updated);
836
+ return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
837
+ }
838
+
839
+ function completeOrchestrationStage(
840
+ taskRef: string,
841
+ event: Parameters<typeof completeDelegationStage>[1],
842
+ options: OrchestrationOptions = {}
843
+ ): OrchestrationResult {
844
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
845
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
846
+ const run = readRun(resolved.taskDir);
847
+ if (!run) return { status: 'running', changed: false, taskId: resolved.taskId, run: null, next: null, error: null };
848
+ if (!run.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'active run has no pending delegation', resolved.taskId);
849
+ const result = completeDelegationStage(run.pendingDelegation, event);
850
+ if (!result.ok) return pauseOrchestration(taskRef, result.code, result.message, true, options);
851
+ const updated = withUpdatedRun(run, { pendingDelegation: result.receipt });
852
+ saveRun(resolved.taskDir, updated);
853
+ return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
854
+ }
855
+
856
+ function inspectOrchestrationStage(
857
+ taskRef: string,
858
+ event: OrchestrationStageIdentity,
859
+ options: OrchestrationOptions = {}
860
+ ): OrchestrationResult {
861
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
862
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
863
+ let run: OrchestrationRun | null;
864
+ try {
865
+ run = readRun(resolved.taskDir);
866
+ } catch (error) {
867
+ return failed('ORCHESTRATION_STATE_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
868
+ }
869
+ if (!run) return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
870
+ const receipt = run.pendingDelegation;
871
+ if (
872
+ !receipt
873
+ || receipt.status !== 'activated'
874
+ || receipt.stage !== event.stage
875
+ || receipt.round !== event.round
876
+ || receipt.artifact !== event.artifact
877
+ || receipt.role !== event.role
878
+ ) {
879
+ return failed(
880
+ 'ORCHESTRATION_PROVENANCE_MISMATCH',
881
+ 'active run does not have one matching activated delegation',
882
+ resolved.taskId
883
+ );
884
+ }
885
+ return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
886
+ }
887
+
888
+ function planOrchestrationStageCompletion(
889
+ taskRef: string,
890
+ event: Readonly<{ stage: DelegationStage; round: number; artifact: string; role: DelegationRole; agent: string }>,
891
+ options: OrchestrationOptions = {}
892
+ ): OrchestrationCompletionPlanResult {
893
+ const inspected = inspectOrchestrationStage(taskRef, event, options);
894
+ if (inspected.status === 'failed' || !inspected.run?.pendingDelegation) {
895
+ return { result: inspected, plan: null };
896
+ }
897
+ const receipt = inspected.run.pendingDelegation;
898
+ const completed = completeDelegationStage(receipt, event);
899
+ if (!completed.ok) {
900
+ return { result: failed(completed.code, completed.message, inspected.taskId), plan: null };
901
+ }
902
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
903
+ if (!resolved.ok) return { result: failed(resolved.code, resolved.message, resolved.taskId), plan: null };
904
+ const updatedRun = withUpdatedRun(inspected.run, { pendingDelegation: completed.receipt });
905
+ return {
906
+ result: { status: 'running', changed: true, taskId: resolved.taskId, run: updatedRun, next: null, error: null },
907
+ plan: {
908
+ taskId: resolved.taskId,
909
+ taskDir: resolved.taskDir,
910
+ updatedRun
911
+ }
912
+ };
913
+ }
914
+
915
+ function commitOrchestrationStageCompletion(plan: OrchestrationStageCompletion): void {
916
+ saveRun(plan.taskDir, plan.updatedRun);
917
+ }
918
+
919
+ function completeCommitOrchestrationStage(
920
+ taskRef: string,
921
+ agent: string,
922
+ options: OrchestrationOptions = {}
923
+ ): OrchestrationResult {
924
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
925
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
926
+ const run = readRun(resolved.taskDir);
927
+ if (!run) return { status: 'running', changed: false, taskId: resolved.taskId, run: null, next: null, error: null };
928
+ const receipt = run.pendingDelegation;
929
+ if (!receipt || receipt.stage !== 'commit') {
930
+ return failed('ORCHESTRATION_DELEGATION_MISSING', 'active run has no pending commit delegation', resolved.taskId);
931
+ }
932
+ return completeOrchestrationStage(taskRef, {
933
+ stage: receipt.stage,
934
+ round: receipt.round,
935
+ artifact: receipt.artifact,
936
+ agent
937
+ }, options);
938
+ }
939
+
940
+ function sealOrchestrationDelegation(
941
+ taskRef: string,
942
+ event: Parameters<typeof sealDelegation>[1],
943
+ options: OrchestrationOptions = {}
944
+ ): OrchestrationResult {
945
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
946
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
947
+ const run = readRun(resolved.taskDir);
948
+ if (!run?.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
949
+ const result = sealDelegation(run.pendingDelegation, event, { now: options.now });
950
+ if (!result.ok) return pauseOrchestration(taskRef, result.code, result.message, true, options);
951
+ const updated = withUpdatedRun(run, { pendingDelegation: result.receipt });
952
+ saveRun(resolved.taskDir, updated);
953
+ return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
954
+ }
955
+
956
+ function advanceOrchestration(taskRef: string, options: OrchestrationOptions = {}): OrchestrationResult {
957
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
958
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
959
+ const run = readRun(resolved.taskDir);
960
+ if (!run?.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
961
+ const result = consumeDelegation(run.pendingDelegation, { now: options.now });
962
+ if (!result.ok) return pauseOrchestration(taskRef, result.code, result.message, true, options);
963
+ const completed = result.receipt.stage === 'commit';
964
+ const updated = withUpdatedRun(run, {
965
+ status: completed ? 'completed' : 'running',
966
+ nextStage: null,
967
+ stepCount: run.stepCount + 1,
968
+ pendingDelegation: null,
969
+ receipts: Object.freeze([...run.receipts, result.receipt]),
970
+ commitAuthorization: completed
971
+ ? { ...run.commitAuthorization, consumedAt: (options.now ?? (() => new Date().toISOString()))() }
972
+ : run.commitAuthorization
973
+ });
974
+ saveRun(resolved.taskDir, updated);
975
+ return { status: updated.status, changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
976
+ }
977
+
978
+ function pauseOrchestration(
979
+ taskRef: string,
980
+ code: string,
981
+ message: string,
982
+ recoverable: boolean,
983
+ options: OrchestrationOptions = {}
984
+ ): OrchestrationResult {
985
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
986
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
987
+ const run = readRun(resolved.taskDir);
988
+ if (!run) return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
989
+ const updated = withUpdatedRun(run, { status: 'paused', pause: { code, message, recoverable } });
990
+ saveRun(resolved.taskDir, updated);
991
+ return { status: 'paused', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
992
+ }
993
+
994
+ export {
995
+ activateMatchingOrchestrationDelegation,
996
+ activateOrchestrationDelegation,
997
+ advanceOrchestration,
998
+ beginOrResumeOrchestration,
999
+ commitOrchestrationStageCompletion,
1000
+ completeCommitOrchestrationStage,
1001
+ completeOrchestrationStage,
1002
+ inspectOrchestrationStage,
1003
+ orchestrationPath,
1004
+ pauseOrchestration,
1005
+ planOrchestrationStageCompletion,
1006
+ prepareOrchestrationDelegation,
1007
+ readRun,
1008
+ routeOrchestration,
1009
+ sealMatchingOrchestrationDelegation,
1010
+ sealOrchestrationDelegation,
1011
+ statusOrchestration
1012
+ };
1013
+ export type {
1014
+ OrchestrationCompletionPlanResult,
1015
+ OrchestrationModelPolicy,
1016
+ OrchestrationNext,
1017
+ OrchestrationResult,
1018
+ OrchestrationRun,
1019
+ OrchestrationStageCompletion,
1020
+ OrchestrationStageIdentity,
1021
+ OrchestrationStatus
1022
+ };