@fitlab-ai/agent-infra 0.9.9 → 0.9.11

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 (309) hide show
  1. package/README.md +13 -3
  2. package/README.zh-CN.md +13 -3
  3. package/bin/cli.ts +17 -4
  4. package/dist/bin/cli.js +19 -4
  5. package/dist/bin/internal-cli.js +55 -4
  6. package/dist/lib/agent-client.js +0 -1
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +17 -10
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/controller-context.js +11 -31
  9. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +15 -33
  10. package/dist/lib/agent-clients/adapters/codex-sandbox.js +72 -11
  11. package/dist/lib/agent-clients/adapters/index.js +3 -1
  12. package/dist/lib/agent-clients/adapters/opencode-sandbox.js +0 -4
  13. package/dist/lib/agent-clients/adapters/traecli.js +65 -0
  14. package/dist/lib/agent-clients/config.js +14 -99
  15. package/dist/lib/agent-clients/reconcile.js +1 -11
  16. package/dist/lib/agent-clients/schema.js +5 -4
  17. package/dist/lib/agent-clients/tokens.js +7 -17
  18. package/dist/lib/agent-clients/types.js +2 -1
  19. package/dist/lib/decide.js +14 -30
  20. package/dist/lib/defaults.json +9 -0
  21. package/dist/lib/init.js +2 -2
  22. package/dist/lib/internal/codex-lifecycle.js +2 -2
  23. package/dist/lib/internal/codex-sandbox-controller.js +4 -3
  24. package/dist/lib/internal/release-workflow.js +0 -1
  25. package/dist/lib/internal/sandbox-control.js +2 -2
  26. package/dist/lib/internal/task-finalization.js +19 -9
  27. package/dist/lib/internal/task-ledger.js +8 -2
  28. package/dist/lib/internal/task-lifecycle.js +17 -93
  29. package/dist/lib/internal/task-orchestration.js +30 -216
  30. package/dist/lib/{task/commands/validate.js → internal/task-validate.js} +77 -43
  31. package/dist/lib/merge.js +205 -103
  32. package/dist/lib/process-data/normalize.js +2 -9
  33. package/dist/lib/run/host.js +46 -16
  34. package/dist/lib/run/tui.js +5 -2
  35. package/dist/lib/sandbox/agent-client-reconciler.js +4 -12
  36. package/dist/lib/sandbox/commands/create.js +3 -3
  37. package/dist/lib/sandbox/commands/enter.js +18 -4
  38. package/dist/lib/sandbox/config.js +3 -12
  39. package/dist/lib/sandbox/control/controller-registration.js +4 -2
  40. package/dist/lib/sandbox/control/executor.js +135 -40
  41. package/dist/lib/sandbox/control/lifecycle.js +29 -20
  42. package/dist/lib/sandbox/control/server.js +18 -16
  43. package/dist/lib/sandbox/mounts.js +3 -2
  44. package/dist/lib/sandbox/recovery.js +123 -22
  45. package/dist/lib/sandbox/workspace-identity.js +14 -0
  46. package/dist/lib/sandbox/workspace-view.js +8 -9
  47. package/dist/lib/self-update.js +278 -0
  48. package/dist/lib/server/protocol.js +5 -5
  49. package/dist/lib/task/activity-log.js +4 -1
  50. package/dist/lib/task/artifact-lifecycle.js +4 -2
  51. package/dist/lib/task/codex-orchestration.js +32 -35
  52. package/dist/lib/task/command-options.js +1 -3
  53. package/dist/lib/task/commands/cat.js +0 -5
  54. package/dist/lib/task/commands/decisions.js +76 -92
  55. package/dist/lib/task/commands/files.js +6 -6
  56. package/dist/lib/task/commands/grep.js +25 -61
  57. package/dist/lib/task/commands/issue-body.js +6 -6
  58. package/dist/lib/task/commands/log.js +25 -10
  59. package/dist/lib/task/commands/show.js +5 -6
  60. package/dist/lib/task/commands/status.js +8 -8
  61. package/dist/lib/task/commit-operation.js +21 -25
  62. package/dist/lib/task/commit-policy.js +1 -1
  63. package/dist/lib/task/control-authority.js +431 -0
  64. package/dist/lib/task/create.js +6 -1
  65. package/dist/lib/task/current-contract.js +35 -0
  66. package/dist/lib/task/decision-details.js +263 -0
  67. package/dist/lib/task/decision-intents.js +5 -2
  68. package/dist/lib/task/delegation-receipts.js +8 -7
  69. package/dist/lib/task/events.js +11 -5
  70. package/dist/lib/task/finalization.js +72 -46
  71. package/dist/lib/task/index.js +19 -29
  72. package/dist/lib/task/ledger-intents.js +8 -9
  73. package/dist/lib/task/ledger.js +13 -6
  74. package/dist/lib/task/lifecycle.js +16 -0
  75. package/dist/lib/task/orchestration.js +51 -14
  76. package/dist/lib/task/review-artifacts.js +31 -12
  77. package/dist/lib/task/review-finalization.js +19 -6
  78. package/dist/lib/task/verification-engine.js +38 -18
  79. package/dist/lib/task/verification-review.js +1 -1
  80. package/dist/lib/task/workspace-snapshot.js +5 -11
  81. package/dist/lib/task/write.js +11 -1
  82. package/dist/lib/update.js +6 -37
  83. package/dist/lib/version.js +5 -1
  84. package/dist/lifecycle-build-manifest.json +3 -3
  85. package/lib/agent-client.ts +0 -1
  86. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +25 -10
  87. package/lib/agent-clients/adapters/codex-lifecycle/controller-context.ts +16 -33
  88. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +16 -35
  89. package/lib/agent-clients/adapters/codex-sandbox.ts +89 -14
  90. package/lib/agent-clients/adapters/index.ts +3 -1
  91. package/lib/agent-clients/adapters/opencode-sandbox.ts +0 -5
  92. package/lib/agent-clients/adapters/traecli.ts +66 -0
  93. package/lib/agent-clients/config.ts +14 -144
  94. package/lib/agent-clients/reconcile.ts +1 -12
  95. package/lib/agent-clients/schema.ts +5 -4
  96. package/lib/agent-clients/tokens.ts +7 -20
  97. package/lib/agent-clients/types.ts +2 -1
  98. package/lib/decide.ts +12 -22
  99. package/lib/defaults.json +9 -0
  100. package/lib/init.ts +2 -2
  101. package/lib/internal/codex-lifecycle.ts +2 -2
  102. package/lib/internal/codex-sandbox-controller.ts +4 -3
  103. package/lib/internal/release-workflow.ts +0 -1
  104. package/lib/internal/sandbox-control.ts +2 -2
  105. package/lib/internal/task-finalization.ts +27 -13
  106. package/lib/internal/task-ledger.ts +8 -2
  107. package/lib/internal/task-lifecycle.ts +33 -76
  108. package/lib/internal/task-orchestration.ts +33 -190
  109. package/lib/{task/commands/validate.ts → internal/task-validate.ts} +77 -41
  110. package/lib/merge.ts +236 -120
  111. package/lib/process-data/normalize.ts +2 -8
  112. package/lib/run/host.ts +63 -14
  113. package/lib/run/tui.ts +5 -3
  114. package/lib/sandbox/agent-client-reconciler.ts +5 -15
  115. package/lib/sandbox/commands/create.ts +4 -4
  116. package/lib/sandbox/commands/enter.ts +24 -4
  117. package/lib/sandbox/config.ts +2 -14
  118. package/lib/sandbox/control/container-identity.ts +2 -2
  119. package/lib/sandbox/control/controller-registration.ts +8 -3
  120. package/lib/sandbox/control/executor.ts +156 -53
  121. package/lib/sandbox/control/lifecycle.ts +34 -28
  122. package/lib/sandbox/control/protocol.ts +1 -3
  123. package/lib/sandbox/control/server.ts +15 -12
  124. package/lib/sandbox/control/state.ts +7 -7
  125. package/lib/sandbox/mounts.ts +4 -3
  126. package/lib/sandbox/recovery.ts +162 -34
  127. package/lib/sandbox/tools.ts +1 -1
  128. package/lib/sandbox/workspace-identity.ts +28 -0
  129. package/lib/sandbox/workspace-view.ts +14 -10
  130. package/lib/self-update.ts +341 -0
  131. package/lib/server/protocol.ts +5 -5
  132. package/lib/task/activity-log.ts +6 -1
  133. package/lib/task/artifact-lifecycle.ts +5 -3
  134. package/lib/task/codex-orchestration.ts +40 -45
  135. package/lib/task/command-options.ts +1 -2
  136. package/lib/task/commands/cat.ts +0 -4
  137. package/lib/task/commands/decisions.ts +80 -75
  138. package/lib/task/commands/files.ts +6 -6
  139. package/lib/task/commands/grep.ts +23 -51
  140. package/lib/task/commands/issue-body.ts +6 -6
  141. package/lib/task/commands/log.ts +22 -10
  142. package/lib/task/commands/show.ts +5 -6
  143. package/lib/task/commands/status.ts +8 -8
  144. package/lib/task/commit-operation.ts +25 -30
  145. package/lib/task/commit-policy.ts +1 -2
  146. package/lib/task/control-authority.ts +563 -0
  147. package/lib/task/create.ts +6 -1
  148. package/lib/task/current-contract.ts +49 -0
  149. package/lib/task/decision-details.ts +340 -0
  150. package/lib/task/decision-intents.ts +7 -2
  151. package/lib/task/delegation-receipts.ts +8 -6
  152. package/lib/task/events.ts +10 -5
  153. package/lib/task/finalization.ts +84 -42
  154. package/lib/task/index.ts +19 -28
  155. package/lib/task/ledger-intents.ts +6 -8
  156. package/lib/task/ledger.ts +17 -7
  157. package/lib/task/lifecycle.ts +20 -0
  158. package/lib/task/orchestration.ts +42 -16
  159. package/lib/task/review-artifacts.ts +53 -15
  160. package/lib/task/review-finalization.ts +33 -7
  161. package/lib/task/verification-engine.ts +33 -21
  162. package/lib/task/verification-review.ts +1 -1
  163. package/lib/task/workspace-snapshot.ts +4 -13
  164. package/lib/task/write.ts +20 -1
  165. package/lib/update.ts +6 -40
  166. package/lib/version.ts +6 -1
  167. package/package.json +1 -1
  168. package/templates/.agents/QUICKSTART.en.md +2 -3
  169. package/templates/.agents/QUICKSTART.zh-CN.md +2 -3
  170. package/templates/.agents/README.en.md +5 -3
  171. package/templates/.agents/README.zh-CN.md +5 -3
  172. package/templates/.agents/rules/README.en.md +1 -0
  173. package/templates/.agents/rules/README.zh-CN.md +1 -0
  174. package/templates/.agents/rules/compatibility-policy.en.md +40 -0
  175. package/templates/.agents/rules/compatibility-policy.zh-CN.md +40 -0
  176. package/templates/.agents/rules/create-issue.github.en.md +1 -1
  177. package/templates/.agents/rules/create-issue.github.zh-CN.md +1 -1
  178. package/templates/.agents/rules/human-decision-context.en.md +9 -7
  179. package/templates/.agents/rules/human-decision-context.zh-CN.md +9 -7
  180. package/templates/.agents/rules/issue-fields.github.en.md +1 -1
  181. package/templates/.agents/rules/issue-fields.github.zh-CN.md +1 -1
  182. package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
  183. package/templates/.agents/rules/issue-pr-commands.github.en.md +1 -1
  184. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +1 -1
  185. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
  186. package/templates/.agents/rules/issue-sync.github.en.md +1 -1
  187. package/templates/.agents/rules/issue-sync.github.zh-CN.md +2 -2
  188. package/templates/.agents/rules/lifecycle-orchestration.en.md +4 -4
  189. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +4 -4
  190. package/templates/.agents/rules/milestone-inference.github.en.md +1 -1
  191. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +1 -1
  192. package/templates/.agents/rules/next-step-output.en.md +3 -3
  193. package/templates/.agents/rules/next-step-output.zh-CN.md +3 -3
  194. package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -1
  195. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -1
  196. package/templates/.agents/rules/pr-sync.en.md +2 -2
  197. package/templates/.agents/rules/pr-sync.github.en.md +3 -2
  198. package/templates/.agents/rules/pr-sync.github.zh-CN.md +3 -1
  199. package/templates/.agents/rules/pr-sync.zh-CN.md +2 -1
  200. package/templates/.agents/rules/release-commands.github.en.md +6 -0
  201. package/templates/.agents/rules/release-commands.github.zh-CN.md +6 -0
  202. package/templates/.agents/rules/review-handshake.en.md +1 -1
  203. package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
  204. package/templates/.agents/rules/review-method.en.md +1 -0
  205. package/templates/.agents/rules/review-method.zh-CN.md +1 -0
  206. package/templates/.agents/rules/task-management.en.md +1 -2
  207. package/templates/.agents/rules/task-management.zh-CN.md +1 -2
  208. package/templates/.agents/skills/analyze-task/SKILL.en.md +4 -3
  209. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +4 -3
  210. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  211. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  212. package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
  213. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
  214. package/templates/.agents/skills/check-task/SKILL.en.md +2 -2
  215. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +2 -2
  216. package/templates/.agents/skills/close-codescan/SKILL.en.md +1 -1
  217. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +1 -1
  218. package/templates/.agents/skills/close-dependabot/SKILL.en.md +1 -1
  219. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +1 -1
  220. package/templates/.agents/skills/code-task/SKILL.en.md +4 -3
  221. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +5 -3
  222. package/templates/.agents/skills/commit/SKILL.en.md +27 -7
  223. package/templates/.agents/skills/commit/SKILL.zh-CN.md +3 -4
  224. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -5
  225. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -5
  226. package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
  227. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
  228. package/templates/.agents/skills/create-pr/SKILL.en.md +15 -11
  229. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +15 -11
  230. package/templates/.agents/skills/create-pr/reference/change-report.en.md +46 -0
  231. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +46 -0
  232. package/templates/.agents/skills/create-pr/scripts/change-report.mjs +203 -0
  233. package/templates/.agents/skills/create-release-note/SKILL.en.md +5 -6
  234. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +5 -6
  235. package/templates/.agents/skills/create-task/SKILL.en.md +1 -1
  236. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +1 -1
  237. package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
  238. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
  239. package/templates/.agents/skills/plan-task/SKILL.en.md +5 -4
  240. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -4
  241. package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
  242. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
  243. package/templates/.agents/skills/review-analysis/SKILL.en.md +5 -3
  244. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +5 -3
  245. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -0
  246. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -0
  247. package/templates/.agents/skills/review-code/SKILL.en.md +5 -3
  248. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -3
  249. package/templates/.agents/skills/review-code/config/verify.en.json +1 -0
  250. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -0
  251. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +9 -6
  252. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +9 -6
  253. package/templates/.agents/skills/review-plan/SKILL.en.md +5 -3
  254. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +5 -3
  255. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -0
  256. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -0
  257. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +7 -3
  258. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +7 -3
  259. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +1 -1
  260. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +1 -1
  261. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +2 -2
  262. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +2 -2
  263. package/templates/.agents/skills/run-task/SKILL.en.md +6 -2
  264. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +6 -2
  265. package/templates/.agents/skills/run-task/reference/host-validation.en.md +2 -2
  266. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +2 -2
  267. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +2 -2
  268. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +2 -2
  269. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +159 -104
  270. package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
  271. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
  272. package/templates/.agents/templates/task.en.md +1 -1
  273. package/templates/.agents/templates/task.zh-CN.md +1 -1
  274. package/templates/.claude/commands/analyze-task.en.md +1 -1
  275. package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
  276. package/templates/.claude/commands/block-task.en.md +1 -1
  277. package/templates/.claude/commands/block-task.zh-CN.md +1 -1
  278. package/templates/.claude/commands/cancel-task.en.md +1 -1
  279. package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
  280. package/templates/.claude/commands/check-task.en.md +1 -1
  281. package/templates/.claude/commands/check-task.zh-CN.md +1 -1
  282. package/templates/.claude/commands/code-task.en.md +1 -1
  283. package/templates/.claude/commands/code-task.zh-CN.md +1 -1
  284. package/templates/.claude/commands/commit.en.md +1 -1
  285. package/templates/.claude/commands/commit.zh-CN.md +1 -1
  286. package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
  287. package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
  288. package/templates/.claude/commands/complete-task.en.md +1 -1
  289. package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
  290. package/templates/.claude/commands/create-pr.en.md +1 -1
  291. package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
  292. package/templates/.claude/commands/plan-task.en.md +1 -1
  293. package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
  294. package/templates/.claude/commands/review-analysis.en.md +1 -1
  295. package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
  296. package/templates/.claude/commands/review-code.en.md +1 -1
  297. package/templates/.claude/commands/review-code.zh-CN.md +1 -1
  298. package/templates/.claude/commands/review-plan.en.md +1 -1
  299. package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
  300. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  301. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  302. package/templates/.claude/commands/run-task.en.md +1 -1
  303. package/templates/.claude/commands/run-task.zh-CN.md +1 -1
  304. package/templates/.claude/commands/watch-pr.en.md +1 -1
  305. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  306. package/templates/.traecli/commands/update-agent-infra.en.md +7 -0
  307. package/templates/.traecli/commands/update-agent-infra.zh-CN.md +7 -0
  308. package/dist/lib/builtin-tuis.js +0 -40
  309. package/lib/builtin-tuis.ts +0 -56
@@ -27,10 +27,9 @@ type CommitOperationInput = Readonly<{
27
27
  taskRef?: string;
28
28
  agent?: string;
29
29
  mode?: CommitExecutionMode;
30
- push?: Readonly<{
30
+ push: Readonly<{
31
31
  remote: string;
32
32
  refs: readonly string[];
33
- automatic: boolean;
34
33
  }>;
35
34
  }>;
36
35
 
@@ -155,9 +154,9 @@ function boundTask(
155
154
 
156
155
  function validateCommon(input: CommitOperationInput, branch: string | null): Readonly<{ code: string; message: string }> | null {
157
156
  if (!input.message.trim()) return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Commit message is required' };
158
- if (input.paths.length === 0 && !input.push) return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'At least one explicit path is required unless this is a push-only retry' };
159
157
  if (!/^[a-f0-9]{40,64}$/.test(input.expectedHead)) return { code: 'GIT_HEAD_EXPECTATION_REQUIRED', message: 'A valid expected HEAD is required' };
160
158
  if (!/^[a-f0-9]{40,64}$/.test(input.expectedTree)) return { code: 'GIT_TREE_EXPECTATION_REQUIRED', message: 'A valid expected tree is required' };
159
+ if (!input.push) return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Commit delivery requires a push policy' };
161
160
  if (input.paths.some((candidate) => !candidate || candidate.startsWith('/') || candidate.split(/[\\/]/).includes('..'))) {
162
161
  return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Commit paths must stay inside the repository' };
163
162
  }
@@ -165,12 +164,10 @@ function validateCommon(input: CommitOperationInput, branch: string | null): Rea
165
164
  return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Sensitive paths cannot be committed' };
166
165
  }
167
166
  if (branch !== null && branch === '') return { code: 'GIT_BRANCH_INVALID', message: 'A named branch is required' };
168
- if (input.push) {
169
- if (!input.push.remote || input.push.refs.length !== 1) return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Push requires one remote and one ref' };
170
- const ref = input.push.refs[0] ?? '';
171
- if (!HEAD_REF.test(ref) || HEAD_REF.exec(ref)?.[1] !== branch) {
172
- return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Push ref must be the current full heads ref' };
173
- }
167
+ if (!input.push.remote || input.push.refs.length !== 1) return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Push requires one remote and one ref' };
168
+ const ref = input.push.refs[0] ?? '';
169
+ if (!HEAD_REF.test(ref) || HEAD_REF.exec(ref)?.[1] !== branch) {
170
+ return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Push ref must be the current full heads ref' };
174
171
  }
175
172
  return null;
176
173
  }
@@ -346,27 +343,25 @@ function executeUnlocked(input: CommitOperationInput, task: BoundTask | null, mo
346
343
  }
347
344
  let status: CommitOperationResult['status'] = committed.status;
348
345
  let result: CommitOperationResult['result'] = committed.status === 'applied' ? 'committed' : 'no_op';
349
- if (input.push) {
350
- const target = `${input.push.remote}:${input.push.refs[0]}`;
351
- const decision = commitPushDecision({ branch, automatic: input.push.automatic }, target);
352
- if (!decision.shouldPush) {
353
- warnings.push(decision.warning!);
354
- } else {
355
- const pushed = pushGitRefs({
356
- cwd: input.cwd,
357
- remote: input.push.remote,
358
- refs: input.push.refs,
359
- expectedSha: committed.snapshot?.head ?? gitText(input.cwd, ['rev-parse', 'HEAD'])
360
- });
361
- if (pushed.status !== 'applied') warnings.push(warning(
362
- 'COMMIT_PUSH_FAILED',
363
- pushed.error?.message ?? 'Git push failed',
364
- true,
365
- 'push',
366
- target,
367
- 'ACTION_REQUIRED'
368
- ));
369
- }
346
+ const target = `${input.push.remote}:${input.push.refs[0]}`;
347
+ const decision = commitPushDecision({ branch }, target);
348
+ if (!decision.shouldPush) {
349
+ warnings.push(decision.warning!);
350
+ } else {
351
+ const pushed = pushGitRefs({
352
+ cwd: input.cwd,
353
+ remote: input.push.remote,
354
+ refs: input.push.refs,
355
+ expectedSha: committed.snapshot?.head ?? gitText(input.cwd, ['rev-parse', 'HEAD'])
356
+ });
357
+ if (pushed.status !== 'applied') warnings.push(warning(
358
+ 'COMMIT_PUSH_FAILED',
359
+ pushed.error?.message ?? 'Git push failed',
360
+ true,
361
+ 'push',
362
+ target,
363
+ 'ACTION_REQUIRED'
364
+ ));
370
365
  }
371
366
  if (warnings.length > 0) {
372
367
  status = 'applied';
@@ -2,7 +2,6 @@ import type { OperationWarning } from './operation-outcome.ts';
2
2
 
3
3
  type CommitPushPolicy = Readonly<{
4
4
  branch: string;
5
- automatic: boolean;
6
5
  }>;
7
6
 
8
7
  type CommitPushDecision = Readonly<{
@@ -12,7 +11,7 @@ type CommitPushDecision = Readonly<{
12
11
 
13
12
  function commitPushDecision(policy: CommitPushPolicy, target: string): CommitPushDecision {
14
13
  const branch = policy.branch.trim();
15
- if (policy.automatic && (branch === 'main' || branch === 'master')) return {
14
+ if (branch === 'main' || branch === 'master') return {
16
15
  shouldPush: false,
17
16
  warning: {
18
17
  code: 'COMMIT_AUTOPUSH_PROTECTED_BRANCH',
@@ -0,0 +1,563 @@
1
+ import path from 'node:path';
2
+
3
+ import { normalizeAgentToken } from '../agent-clients/tokens.ts';
4
+ import { isAgentClientId } from '../agent-clients/types.ts';
5
+ import type { AgentClientId } from '../agent-clients/types.ts';
6
+ import { consumeHumanOverride, failureId, overrideDryRunConflict } from './human-override.ts';
7
+ import {
8
+ activateMatchingOrchestrationDelegation,
9
+ activateOrchestrationDelegation,
10
+ advanceOrchestration,
11
+ awaitOrchestrationDelegationActivation,
12
+ beginOrResumeOrchestration,
13
+ dispatchOrchestrationDelegation,
14
+ OrchestrationStateError,
15
+ pauseOrchestration,
16
+ prepareOrchestrationDelegation,
17
+ recoverPreparedOrchestrationDelegation,
18
+ routeOrchestration,
19
+ sealMatchingOrchestrationDelegation,
20
+ sealOrchestrationDelegation,
21
+ statusOrchestration
22
+ } from './orchestration.ts';
23
+ import type { OrchestrationOptions, OrchestrationResult } from './orchestration.ts';
24
+ import { prepareCodexOrchestrationDelegation } from './codex-orchestration.ts';
25
+ import {
26
+ applyTaskFinalization,
27
+ type TaskFinalizationRequest,
28
+ type TaskFinalizationResult
29
+ } from './finalization.ts';
30
+ import {
31
+ applyTaskLifecycle,
32
+ taskLifecycleFailure,
33
+ type TaskLifecycleRequest,
34
+ type TaskLifecycleResult
35
+ } from './lifecycle.ts';
36
+ import { resolveTaskRef, TASK_ID_RE } from './resolve-ref.ts';
37
+ import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.ts';
38
+ import { verifyTaskEvent } from './verification.ts';
39
+
40
+ export type TaskControlControllerBinding = Readonly<{
41
+ instanceDigest: string;
42
+ controlGeneration: string;
43
+ }>;
44
+
45
+ export type TaskControlExecutionContext =
46
+ | Readonly<{
47
+ source: 'direct-host';
48
+ mode: 'direct-host';
49
+ repoRoot: string;
50
+ runtimeDir?: string;
51
+ controllerBinding?: TaskControlControllerBinding | null;
52
+ }>
53
+ | Readonly<{
54
+ source: 'sandbox-executor';
55
+ mode: 'task-bound-sandbox';
56
+ repoRoot: string;
57
+ worktreeRoot: string;
58
+ runtimeDir: string;
59
+ taskId: string;
60
+ generation: string;
61
+ manifestPath: string;
62
+ requestId: string;
63
+ controllerBinding?: TaskControlControllerBinding | null;
64
+ }>;
65
+
66
+ export type TaskControlOrchestrationIntent =
67
+ | 'begin-or-resume'
68
+ | 'route'
69
+ | 'prepare'
70
+ | 'dispatch'
71
+ | 'await-activation'
72
+ | 'recover-prepared'
73
+ | 'hook-start'
74
+ | 'hook-stop'
75
+ | 'advance'
76
+ | 'pause'
77
+ | 'status';
78
+
79
+ export type TaskControlOperation =
80
+ | Readonly<{ family: 'task-lifecycle'; request: TaskLifecycleControlRequest }>
81
+ | Readonly<{ family: 'task-finalization'; request: TaskFinalizationRequest }>
82
+ | Readonly<{
83
+ family: 'task-orchestration';
84
+ taskRef: string;
85
+ intent: TaskControlOrchestrationIntent;
86
+ input: Readonly<Record<string, unknown>>;
87
+ options?: Omit<OrchestrationOptions, 'repoRoot'>;
88
+ }>;
89
+
90
+ export type TaskControlDispatchResult = TaskLifecycleResult | TaskFinalizationResult | OrchestrationResult;
91
+
92
+ export type TaskLifecycleControlRequest = TaskLifecycleRequest & Readonly<{
93
+ overrideTicket?: string;
94
+ overrideTarget?: string;
95
+ overrideScope?: string;
96
+ }>;
97
+
98
+ function invalidContext(message: string): never {
99
+ throw new Error(`TASK_CONTROL_CONTEXT_INVALID: ${message}`);
100
+ }
101
+
102
+ function absolute(name: string, value: string): string {
103
+ if (!value || !path.isAbsolute(value)) invalidContext(`${name} must be absolute`);
104
+ return path.resolve(value);
105
+ }
106
+
107
+ function binding(value: TaskControlControllerBinding | null | undefined): TaskControlControllerBinding | null | undefined {
108
+ if (value === null || value === undefined) return value;
109
+ if (!/^[a-f0-9]{64}$/u.test(value.instanceDigest) || !value.controlGeneration) {
110
+ invalidContext('controller binding is invalid');
111
+ }
112
+ return value;
113
+ }
114
+
115
+ export function createDirectHostExecutionContext(params: Readonly<{
116
+ repoRoot: string;
117
+ runtimeDir?: string;
118
+ controllerBinding?: TaskControlControllerBinding | null;
119
+ }>): TaskControlExecutionContext {
120
+ return {
121
+ source: 'direct-host',
122
+ mode: 'direct-host',
123
+ repoRoot: absolute('repoRoot', params.repoRoot),
124
+ ...(params.runtimeDir === undefined ? {} : { runtimeDir: absolute('runtimeDir', params.runtimeDir) }),
125
+ ...(params.controllerBinding === undefined ? {} : { controllerBinding: binding(params.controllerBinding) })
126
+ };
127
+ }
128
+
129
+ export function createSandboxExecutorExecutionContext(params: Readonly<{
130
+ repoRoot: string;
131
+ worktreeRoot: string;
132
+ runtimeDir: string;
133
+ taskId: string;
134
+ generation: string;
135
+ manifestPath: string;
136
+ requestId: string;
137
+ controllerBinding?: TaskControlControllerBinding | null;
138
+ }>): TaskControlExecutionContext {
139
+ if (!/^TASK-\d{8}-\d{6}$/u.test(params.taskId) || !params.generation || !params.requestId) {
140
+ invalidContext('sandbox binding is incomplete');
141
+ }
142
+ const controlRoot = path.dirname(path.resolve(params.manifestPath));
143
+ if (path.resolve(params.runtimeDir) !== path.join(controlRoot, 'runtime')) {
144
+ invalidContext('runtimeDir is not bound to the manifest control root');
145
+ }
146
+ return {
147
+ source: 'sandbox-executor',
148
+ mode: 'task-bound-sandbox',
149
+ repoRoot: absolute('repoRoot', params.repoRoot),
150
+ worktreeRoot: absolute('worktreeRoot', params.worktreeRoot),
151
+ runtimeDir: absolute('runtimeDir', params.runtimeDir),
152
+ taskId: params.taskId,
153
+ generation: params.generation,
154
+ manifestPath: absolute('manifestPath', params.manifestPath),
155
+ requestId: params.requestId,
156
+ ...(params.controllerBinding === undefined ? {} : { controllerBinding: binding(params.controllerBinding) })
157
+ };
158
+ }
159
+
160
+ export function assertTaskControlExecutionContext(context: TaskControlExecutionContext): void {
161
+ if (context.source === 'direct-host') {
162
+ if (context.mode !== 'direct-host') invalidContext('direct-host mode is inconsistent');
163
+ absolute('repoRoot', context.repoRoot);
164
+ if (context.runtimeDir !== undefined) absolute('runtimeDir', context.runtimeDir);
165
+ binding(context.controllerBinding);
166
+ return;
167
+ }
168
+ if (context.mode !== 'task-bound-sandbox' || !/^TASK-\d{8}-\d{6}$/u.test(context.taskId)
169
+ || !context.generation || !context.requestId) {
170
+ invalidContext('sandbox executor context is incomplete');
171
+ }
172
+ absolute('repoRoot', context.repoRoot);
173
+ absolute('worktreeRoot', context.worktreeRoot);
174
+ absolute('runtimeDir', context.runtimeDir);
175
+ absolute('manifestPath', context.manifestPath);
176
+ if (path.resolve(context.runtimeDir) !== path.join(path.dirname(path.resolve(context.manifestPath)), 'runtime')) {
177
+ invalidContext('runtimeDir is not bound to the manifest control root');
178
+ }
179
+ binding(context.controllerBinding);
180
+ }
181
+
182
+ function operationTaskId(context: TaskControlExecutionContext, taskRef: string): void {
183
+ if (context.source === 'sandbox-executor' && taskRef !== context.taskId) {
184
+ throw new Error('TASK_CONTROL_CONTEXT_TASK_MISMATCH: operation task does not match the sandbox manifest');
185
+ }
186
+ }
187
+
188
+ function domainOptions(
189
+ context: TaskControlExecutionContext,
190
+ options: Omit<OrchestrationOptions, 'repoRoot'> | undefined
191
+ ): OrchestrationOptions {
192
+ return {
193
+ ...options,
194
+ repoRoot: context.repoRoot,
195
+ ...(context.source === 'sandbox-executor' ? { gitWorktreeRoot: context.worktreeRoot } : {})
196
+ };
197
+ }
198
+
199
+ function lifecycleTaskId(request: TaskLifecycleControlRequest, repoRoot: string): string | null {
200
+ if (request.intent === 'restore') return TASK_ID_RE.test(request.taskRef) ? request.taskRef : null;
201
+ try {
202
+ const resolved = resolveTaskRef(request.taskRef, { repoRoot });
203
+ return resolved.ok ? resolved.taskId : null;
204
+ } catch {
205
+ return null;
206
+ }
207
+ }
208
+
209
+ function applyLifecycleWithAuthority(
210
+ context: TaskControlExecutionContext,
211
+ request: TaskLifecycleControlRequest
212
+ ): TaskLifecycleResult & { humanOverride?: unknown } {
213
+ const conflict = overrideDryRunConflict(request as unknown as Record<string, unknown>);
214
+ if (conflict) {
215
+ return taskLifecycleFailure(request, { code: 'LIFECYCLE_PAYLOAD_INVALID', message: conflict.message });
216
+ }
217
+ const execute = (): TaskLifecycleResult & { humanOverride?: unknown } => {
218
+ const lifecycleResult = applyTaskLifecycle(request, { repoRoot: context.repoRoot });
219
+ if (lifecycleResult.status !== 'failed' || !request.overrideTicket) return lifecycleResult;
220
+ if (!request.overrideTarget || !request.overrideScope) {
221
+ return {
222
+ ...lifecycleResult,
223
+ error: { code: 'OVERRIDE_PAYLOAD_INVALID', message: 'override ticket requires --override-target and --override-scope' }
224
+ };
225
+ }
226
+ const override = consumeHumanOverride({
227
+ taskRef: request.taskRef,
228
+ ticketId: request.overrideTicket,
229
+ failureId: failureId('lifecycle.apply', lifecycleResult.error?.code ?? 'LIFECYCLE_FAILED'),
230
+ target: request.overrideTarget,
231
+ scope: request.overrideScope,
232
+ intent: request.intent,
233
+ ...('alertNumber' in request && request.alertNumber ? { alertNumber: request.alertNumber } : {}),
234
+ ...('issueNumber' in request && request.issueNumber ? { issueNumber: request.issueNumber } : {}),
235
+ ...('stagingDir' in request && request.stagingDir ? { stagingDir: request.stagingDir } : {})
236
+ }, { repoRoot: context.repoRoot });
237
+ if (override.status === 'failed') return { ...lifecycleResult, humanOverride: override };
238
+ return { ...lifecycleResult, ...override, humanOverride: override, error: null };
239
+ };
240
+ const taskId = lifecycleTaskId(request, context.repoRoot);
241
+ if (!taskId) return execute();
242
+ try {
243
+ return withTaskExecutionLock(
244
+ context.repoRoot,
245
+ taskId,
246
+ `task-lifecycle.${request.intent}`,
247
+ execute
248
+ );
249
+ } catch (error) {
250
+ if (!(error instanceof TaskExecutionLockError)) throw error;
251
+ return taskLifecycleFailure(request, { code: error.code, message: error.message }, taskId);
252
+ }
253
+ }
254
+
255
+ function orchestration(
256
+ context: TaskControlExecutionContext,
257
+ operation: Extract<TaskControlOperation, { family: 'task-orchestration' }>
258
+ ): OrchestrationResult | Promise<OrchestrationResult> {
259
+ operationTaskId(context, operation.taskRef);
260
+ const input = operation.input;
261
+ const options = domainOptions(context, operation.options);
262
+ switch (operation.intent) {
263
+ case 'begin-or-resume':
264
+ return beginOrResumeOrchestration(operation.taskRef, {
265
+ ...options,
266
+ client: input.client as AgentClientId,
267
+ maxSteps: input.maxSteps as number | undefined,
268
+ modelPolicy: input.modelPolicy as OrchestrationOptions['modelPolicy']
269
+ });
270
+ case 'route': return routeOrchestration(operation.taskRef, options);
271
+ case 'status': return statusOrchestration(operation.taskRef, options);
272
+ case 'prepare': {
273
+ const prepareInput = {
274
+ client: input.client as AgentClientId,
275
+ requestedModel: input.requestedModel as string | undefined,
276
+ requestedReasoningEffort: input.requestedReasoningEffort as string | undefined,
277
+ ...(input.capabilityToken === undefined ? {} : { capabilityToken: input.capabilityToken as string })
278
+ };
279
+ if (prepareInput.client === 'codex') {
280
+ return prepareCodexOrchestrationDelegation(operation.taskRef, prepareInput, {
281
+ repoRoot: context.repoRoot,
282
+ orchestrationOptions: options,
283
+ ...(context.controllerBinding ? { controllerBinding: context.controllerBinding } : {})
284
+ });
285
+ }
286
+ return prepareOrchestrationDelegation(operation.taskRef, prepareInput, options);
287
+ }
288
+ case 'dispatch': return dispatchOrchestrationDelegation(operation.taskRef, options);
289
+ case 'await-activation':
290
+ return awaitOrchestrationDelegationActivation(operation.taskRef, input.event as never, options);
291
+ case 'recover-prepared': return recoverPreparedOrchestrationDelegation(operation.taskRef, options);
292
+ case 'hook-start':
293
+ return input.auto === true
294
+ ? activateMatchingOrchestrationDelegation(input.client as AgentClientId, input.event as never, options)
295
+ : activateOrchestrationDelegation(operation.taskRef, input.event as never, options);
296
+ case 'hook-stop':
297
+ return input.auto === true
298
+ ? sealMatchingOrchestrationDelegation(input.client as AgentClientId, input.event as never, options)
299
+ : sealOrchestrationDelegation(operation.taskRef, input.event as never, options);
300
+ case 'advance': return advanceOrchestration(operation.taskRef, options);
301
+ case 'pause':
302
+ return pauseOrchestration(
303
+ operation.taskRef,
304
+ input.code as string,
305
+ input.message as string,
306
+ input.recoverable as boolean,
307
+ options
308
+ );
309
+ }
310
+ }
311
+
312
+ function orchestrationFailure(error: unknown): OrchestrationResult {
313
+ if (error instanceof OrchestrationStateError) {
314
+ return {
315
+ status: 'failed', changed: false, taskId: error.taskId, run: null, next: null,
316
+ error: { code: error.code, message: error.message }
317
+ };
318
+ }
319
+ throw error;
320
+ }
321
+
322
+ export function dispatchTaskControlOperation(
323
+ context: TaskControlExecutionContext,
324
+ operation: Extract<TaskControlOperation, { family: 'task-lifecycle' }>
325
+ ): TaskLifecycleResult;
326
+ export function dispatchTaskControlOperation(
327
+ context: TaskControlExecutionContext,
328
+ operation: Extract<TaskControlOperation, { family: 'task-finalization' }>
329
+ ): TaskFinalizationResult;
330
+ export function dispatchTaskControlOperation(
331
+ context: TaskControlExecutionContext,
332
+ operation: Extract<TaskControlOperation, { family: 'task-orchestration' }>
333
+ ): OrchestrationResult | Promise<OrchestrationResult>;
334
+ export function dispatchTaskControlOperation(
335
+ context: TaskControlExecutionContext,
336
+ operation: TaskControlOperation
337
+ ): TaskControlDispatchResult | Promise<TaskControlDispatchResult> {
338
+ assertTaskControlExecutionContext(context);
339
+ assertTaskControlOperation(operation);
340
+ if (operation.family === 'task-lifecycle') {
341
+ operationTaskId(context, operation.request.taskRef);
342
+ return applyLifecycleWithAuthority(context, operation.request);
343
+ }
344
+ if (operation.family === 'task-finalization') {
345
+ operationTaskId(context, operation.request.taskRef);
346
+ return applyTaskFinalization(operation.request, {
347
+ repoRoot: context.repoRoot,
348
+ preflight: (request, options) => verifyTaskEvent(
349
+ { ...request, event: 'complete-task.hard-preflight' }, options
350
+ )
351
+ });
352
+ }
353
+ try {
354
+ const result = orchestration(context, operation);
355
+ return result instanceof Promise ? result.catch(orchestrationFailure) : result;
356
+ } catch (error) {
357
+ return orchestrationFailure(error);
358
+ }
359
+ }
360
+
361
+ export function assertTaskControlOperation(operation: TaskControlOperation): void {
362
+ if (operation.family === 'task-lifecycle') {
363
+ if (!operation.request.taskRef || !normalizeAgentToken(operation.request.agent)) {
364
+ throw new Error('TASK_CONTROL_OPERATION_INVALID: lifecycle request is invalid');
365
+ }
366
+ return;
367
+ }
368
+ if (operation.family === 'task-finalization') {
369
+ if (!operation.request.taskRef || !normalizeAgentToken(operation.request.agent)) {
370
+ throw new Error('TASK_CONTROL_OPERATION_INVALID: finalization request is invalid');
371
+ }
372
+ return;
373
+ }
374
+ if (!operation.taskRef || !operation.intent || !operation.input || typeof operation.input !== 'object' || Array.isArray(operation.input)) {
375
+ throw new Error('TASK_CONTROL_OPERATION_INVALID: orchestration operation is invalid');
376
+ }
377
+ }
378
+
379
+ function operationInvalid(message: string): never {
380
+ throw new Error(`TASK_CONTROL_OPERATION_INVALID: ${message}`);
381
+ }
382
+
383
+ const LIFECYCLE_FLAGS = new Set([
384
+ '--agent', '--reason', '--unblock-condition', '--note', '--alert-number', '--staging-dir', '--issue-number',
385
+ '--override-ticket', '--override-target', '--override-scope', '--dry-run'
386
+ ]);
387
+
388
+ const FINALIZATION_FLAGS = new Set(['--agent']);
389
+
390
+ const ORCHESTRATION_FLAGS = new Set([
391
+ '--agent', '--max-steps', '--executor-model', '--executor-reasoning-effort', '--reviewer-model',
392
+ '--reviewer-reasoning-effort', '--client', '--requested-model', '--requested-reasoning-effort',
393
+ '--capability-token', '--parent-id', '--before-fingerprint', '--stage', '--round', '--artifact', '--role',
394
+ '--native-agent', '--child-id', '--spawn-mode', '--actual-model', '--actual-reasoning-effort',
395
+ '--model-fallback-reason', '--reasoning-effort-fallback-reason', '--exit-code', '--after-fingerprint',
396
+ '--changed-paths', '--code', '--message', '--recoverable', '--git-worktree-root'
397
+ ]);
398
+
399
+ function parseValues(
400
+ args: readonly string[],
401
+ start: number,
402
+ flags: ReadonlySet<string>
403
+ ): Record<string, string | boolean> {
404
+ const values: Record<string, string | boolean> = {};
405
+ for (let index = start; index < args.length; index += 1) {
406
+ const flag = args[index]!;
407
+ if (flag === '--dry-run') {
408
+ if (!flags.has(flag)) operationInvalid(`unknown option '${flag}'`);
409
+ if (values[flag] !== undefined) operationInvalid(`duplicate option '${flag}'`);
410
+ values[flag] = true;
411
+ continue;
412
+ }
413
+ if (!flags.has(flag)) operationInvalid(`unknown option '${flag}'`);
414
+ if (values[flag] !== undefined) operationInvalid(`duplicate option '${flag}'`);
415
+ const value = args[++index];
416
+ if (!value || value.startsWith('--')) operationInvalid(`option '${flag}' requires a value`);
417
+ values[flag] = value;
418
+ }
419
+ return values;
420
+ }
421
+
422
+ function value(values: Record<string, string | boolean>, flag: string): string | undefined {
423
+ const current = values[flag];
424
+ return typeof current === 'string' ? current : undefined;
425
+ }
426
+
427
+ function required(values: Record<string, string | boolean>, flags: readonly string[]): void {
428
+ const missing = flags.find((flag) => value(values, flag) === undefined);
429
+ if (missing) operationInvalid(`option '${missing}' is required`);
430
+ }
431
+
432
+ export function parseTaskControlOperation(
433
+ family: 'task-lifecycle' | 'task-orchestration' | 'task-finalization',
434
+ args: readonly string[]
435
+ ): TaskControlOperation {
436
+ if (family === 'task-finalization') {
437
+ if (args.length !== 4 || args[1] !== 'complete' || args[2] !== '--agent' || !args[0] || !args[3]) {
438
+ operationInvalid('task ref, complete intent, and --agent are required');
439
+ }
440
+ const values = parseValues(args, 2, FINALIZATION_FLAGS);
441
+ const agent = normalizeAgentToken(value(values, '--agent') ?? '');
442
+ if (!agent) operationInvalid('finalization agent is invalid');
443
+ return { family, request: { taskRef: args[0]!, intent: 'complete', agent } };
444
+ }
445
+ if (args.length < 2) operationInvalid('task ref and intent are required');
446
+ const taskRef = args[0]!;
447
+ const intent = args[1]!;
448
+ if (family === 'task-lifecycle') {
449
+ const values = parseValues(args, 2, LIFECYCLE_FLAGS);
450
+ const agent = normalizeAgentToken(value(values, '--agent') ?? '');
451
+ if (!agent) operationInvalid('lifecycle agent is invalid');
452
+ const input: Record<string, unknown> = {
453
+ taskRef,
454
+ intent,
455
+ agent,
456
+ ...(value(values, '--reason') ? { reason: value(values, '--reason') } : {}),
457
+ ...(value(values, '--unblock-condition') ? { unblockCondition: value(values, '--unblock-condition') } : {}),
458
+ ...(value(values, '--note') ? { note: value(values, '--note') } : {}),
459
+ ...(value(values, '--staging-dir') ? { stagingDir: value(values, '--staging-dir') } : {}),
460
+ ...(value(values, '--override-ticket') ? { overrideTicket: value(values, '--override-ticket') } : {}),
461
+ ...(value(values, '--override-target') ? { overrideTarget: value(values, '--override-target') } : {}),
462
+ ...(value(values, '--override-scope') ? { overrideScope: value(values, '--override-scope') } : {}),
463
+ ...(value(values, '--alert-number') ? { alertNumber: Number(value(values, '--alert-number')) } : {}),
464
+ ...(value(values, '--issue-number') ? { issueNumber: Number(value(values, '--issue-number')) } : {}),
465
+ ...(values['--dry-run'] === true ? { dryRun: true } : {})
466
+ };
467
+ return { family, request: input as unknown as TaskLifecycleControlRequest };
468
+ }
469
+
470
+ const values = parseValues(args, 2, ORCHESTRATION_FLAGS);
471
+ const orchestrationIntents = new Set<TaskControlOrchestrationIntent>([
472
+ 'begin-or-resume', 'route', 'prepare', 'dispatch', 'await-activation', 'recover-prepared',
473
+ 'hook-start', 'hook-stop', 'advance', 'pause', 'status'
474
+ ]);
475
+ if (!orchestrationIntents.has(intent as TaskControlOrchestrationIntent)) {
476
+ operationInvalid(`unknown orchestration intent '${intent}'`);
477
+ }
478
+ const client = value(values, '--client');
479
+ if (client !== undefined && !isAgentClientId(client)) operationInvalid(`unknown client '${client}'`);
480
+ const parsedIntent = intent as TaskControlOrchestrationIntent;
481
+ const input: Record<string, unknown> = {};
482
+ if (parsedIntent === 'begin-or-resume' || parsedIntent === 'prepare') {
483
+ required(values, ['--client']);
484
+ if (!client || !isAgentClientId(client)) operationInvalid(`unknown client '${client ?? ''}'`);
485
+ input.client = client;
486
+ }
487
+ if (parsedIntent === 'begin-or-resume') {
488
+ const maxSteps = value(values, '--max-steps');
489
+ if (maxSteps !== undefined) {
490
+ const parsed = Number(maxSteps);
491
+ if (!Number.isInteger(parsed) || parsed < 1) operationInvalid('--max-steps must be a positive integer');
492
+ input.maxSteps = parsed;
493
+ }
494
+ const policyFlags = ['--executor-model', '--executor-reasoning-effort', '--reviewer-model', '--reviewer-reasoning-effort'];
495
+ const hasPolicy = policyFlags.some((flag) => value(values, flag) !== undefined);
496
+ if (hasPolicy && policyFlags.some((flag) => value(values, flag) === undefined)) {
497
+ operationInvalid('explicit model policy requires executor/reviewer model and reasoning effort');
498
+ }
499
+ if (hasPolicy) {
500
+ input.modelPolicy = {
501
+ executor: { model: value(values, '--executor-model'), reasoningEffort: value(values, '--executor-reasoning-effort') },
502
+ reviewer: { model: value(values, '--reviewer-model'), reasoningEffort: value(values, '--reviewer-reasoning-effort') }
503
+ };
504
+ }
505
+ }
506
+ if (parsedIntent === 'prepare') {
507
+ input.requestedModel = value(values, '--requested-model');
508
+ input.requestedReasoningEffort = value(values, '--requested-reasoning-effort');
509
+ input.capabilityToken = value(values, '--capability-token');
510
+ }
511
+ if (parsedIntent === 'await-activation') {
512
+ required(values, ['--stage', '--round', '--artifact', '--role']);
513
+ const round = Number(value(values, '--round'));
514
+ if (!Number.isSafeInteger(round) || round < 1) operationInvalid('--round must be a positive integer');
515
+ const role = value(values, '--role');
516
+ if (role !== 'executor' && role !== 'reviewer') operationInvalid('--role must be executor or reviewer');
517
+ input.event = { stage: value(values, '--stage'), round, artifact: value(values, '--artifact'), role };
518
+ }
519
+ if (parsedIntent === 'hook-start') {
520
+ required(values, ['--native-agent', '--child-id', '--parent-id']);
521
+ if (taskRef === 'auto') required(values, ['--client']);
522
+ input.auto = taskRef === 'auto';
523
+ input.client = value(values, '--client');
524
+ input.event = {
525
+ nativeAgent: value(values, '--native-agent'), childId: value(values, '--child-id'), parentId: value(values, '--parent-id'),
526
+ spawnMode: value(values, '--spawn-mode'), actualModel: value(values, '--actual-model'),
527
+ actualReasoningEffort: value(values, '--actual-reasoning-effort'), modelFallbackReason: value(values, '--model-fallback-reason'),
528
+ reasoningEffortFallbackReason: value(values, '--reasoning-effort-fallback-reason')
529
+ };
530
+ }
531
+ if (parsedIntent === 'hook-stop') {
532
+ input.auto = taskRef === 'auto';
533
+ if (input.auto) {
534
+ required(values, ['--client', '--native-agent', '--child-id']);
535
+ input.client = value(values, '--client');
536
+ input.event = {
537
+ nativeAgent: value(values, '--native-agent'), childId: value(values, '--child-id'), actualModel: value(values, '--actual-model'),
538
+ actualReasoningEffort: value(values, '--actual-reasoning-effort'), modelFallbackReason: value(values, '--model-fallback-reason'),
539
+ reasoningEffortFallbackReason: value(values, '--reasoning-effort-fallback-reason')
540
+ };
541
+ } else {
542
+ required(values, ['--child-id', '--exit-code', '--after-fingerprint']);
543
+ const exitCode = Number(value(values, '--exit-code'));
544
+ if (!Number.isInteger(exitCode)) operationInvalid('--exit-code must be an integer');
545
+ input.event = {
546
+ childId: value(values, '--child-id'), exitCode, afterFingerprint: value(values, '--after-fingerprint'),
547
+ changedPaths: value(values, '--changed-paths')?.split(',').filter(Boolean) ?? [], actualModel: value(values, '--actual-model'),
548
+ actualReasoningEffort: value(values, '--actual-reasoning-effort'), modelFallbackReason: value(values, '--model-fallback-reason'),
549
+ reasoningEffortFallbackReason: value(values, '--reasoning-effort-fallback-reason')
550
+ };
551
+ }
552
+ }
553
+ if (parsedIntent === 'pause') {
554
+ required(values, ['--code', '--message', '--recoverable']);
555
+ const recoverable = value(values, '--recoverable');
556
+ if (recoverable !== 'true' && recoverable !== 'false') operationInvalid('--recoverable must be true or false');
557
+ input.code = value(values, '--code'); input.message = value(values, '--message'); input.recoverable = recoverable === 'true';
558
+ }
559
+ const options = value(values, '--git-worktree-root') === undefined
560
+ ? undefined
561
+ : { gitWorktreeRoot: value(values, '--git-worktree-root') };
562
+ return { family, taskRef, intent: parsedIntent, input, ...(options ? { options } : {}) };
563
+ }