@fitlab-ai/agent-infra 0.9.1 → 0.9.2

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 (283) hide show
  1. package/README.md +16 -4
  2. package/README.zh-CN.md +16 -4
  3. package/bin/cli.ts +12 -0
  4. package/dist/bin/cli.js +14 -0
  5. package/dist/bin/internal-cli.js +15 -0
  6. package/dist/lib/agent-client.js +163 -0
  7. package/dist/lib/agent-clients/adapter.js +294 -0
  8. package/dist/lib/agent-clients/adapters/claude-code.js +62 -0
  9. package/dist/lib/agent-clients/adapters/codex-sandbox.js +210 -0
  10. package/dist/lib/agent-clients/adapters/codex.js +54 -0
  11. package/dist/lib/agent-clients/adapters/gemini-cli.js +64 -0
  12. package/dist/lib/agent-clients/adapters/index.js +20 -0
  13. package/dist/lib/agent-clients/adapters/opencode.js +64 -0
  14. package/dist/lib/agent-clients/config.js +177 -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 +38 -0
  18. package/dist/lib/agent-clients/project-assets.js +45 -0
  19. package/dist/lib/agent-clients/reconcile.js +235 -0
  20. package/dist/lib/agent-clients/registry.js +72 -0
  21. package/dist/lib/agent-clients/schema.js +57 -0
  22. package/dist/lib/agent-clients/types.js +29 -0
  23. package/dist/lib/builtin-tuis.js +9 -14
  24. package/dist/lib/defaults.json +85 -73
  25. package/dist/lib/git/review-snapshot.js +25 -10
  26. package/dist/lib/git/reviewed-commit-equivalence.js +110 -0
  27. package/dist/lib/init.js +47 -91
  28. package/dist/lib/internal/agent-client.js +117 -0
  29. package/dist/lib/internal/git-workflow.js +1 -1
  30. package/dist/lib/internal/release-workflow.js +71 -16
  31. package/dist/lib/internal/task-orchestration.js +151 -0
  32. package/dist/lib/internal/task-review.js +65 -0
  33. package/dist/lib/merge.js +2 -1
  34. package/dist/lib/platform/adapters.js +38 -1
  35. package/dist/lib/platform/change-request-git-evidence.js +113 -0
  36. package/dist/lib/platform/merged-pr-equivalence.js +41 -11
  37. package/dist/lib/platform/pr-checks.js +38 -10
  38. package/dist/lib/platform/pull-requests.js +110 -6
  39. package/dist/lib/platform/releases.js +1 -1
  40. package/dist/lib/platform/verification-required.js +8 -10
  41. package/dist/lib/prompt.js +15 -3
  42. package/dist/lib/release/channels.js +4 -1
  43. package/dist/lib/release/workflow.js +4 -2
  44. package/dist/lib/remove-dir.js +25 -0
  45. package/dist/lib/run/tui.js +8 -2
  46. package/dist/lib/sandbox/agent-client-reconciler.js +234 -0
  47. package/dist/lib/sandbox/clipboard/linux.js +2 -1
  48. package/dist/lib/sandbox/commands/create.js +76 -180
  49. package/dist/lib/sandbox/commands/enter.js +17 -1
  50. package/dist/lib/sandbox/commands/prune.js +2 -2
  51. package/dist/lib/sandbox/commands/rm.js +3 -2
  52. package/dist/lib/sandbox/config.js +21 -4
  53. package/dist/lib/sandbox/constants.js +3 -0
  54. package/dist/lib/sandbox/dockerfile.js +2 -1
  55. package/dist/lib/sandbox/managed-fs.js +10 -5
  56. package/dist/lib/sandbox/recovery.js +52 -36
  57. package/dist/lib/sandbox/shell.js +1 -1
  58. package/dist/lib/sandbox/tool-types.js +8 -0
  59. package/dist/lib/sandbox/tools.js +2 -126
  60. package/dist/lib/task/artifact-lifecycle.js +1 -49
  61. package/dist/lib/task/commands/decisions.js +50 -20
  62. package/dist/lib/task/delegation-receipts.js +107 -0
  63. package/dist/lib/task/events.js +100 -1
  64. package/dist/lib/task/orchestration.js +420 -0
  65. package/dist/lib/task/review-artifacts.js +143 -59
  66. package/dist/lib/task/review-finalization.js +166 -0
  67. package/dist/lib/task/review-fingerprint.js +4 -0
  68. package/dist/lib/task/verification-checks.js +2 -1
  69. package/dist/lib/task/verification-engine.js +156 -28
  70. package/dist/lib/task/verification-review.js +1 -1
  71. package/dist/lib/task/verification.js +3 -1
  72. package/dist/lib/task/workspace-snapshot.js +34 -0
  73. package/dist/lib/update.js +42 -161
  74. package/lib/agent-client.ts +214 -0
  75. package/lib/agent-clients/adapter.ts +461 -0
  76. package/lib/agent-clients/adapters/claude-code.ts +70 -0
  77. package/lib/agent-clients/adapters/codex-sandbox.ts +245 -0
  78. package/lib/agent-clients/adapters/codex.ts +58 -0
  79. package/lib/agent-clients/adapters/gemini-cli.ts +65 -0
  80. package/lib/agent-clients/adapters/index.ts +27 -0
  81. package/lib/agent-clients/adapters/opencode.ts +66 -0
  82. package/lib/agent-clients/config.ts +278 -0
  83. package/lib/agent-clients/custom-tuis.ts +149 -0
  84. package/lib/agent-clients/invocation.ts +19 -0
  85. package/lib/agent-clients/next-steps.ts +72 -0
  86. package/lib/agent-clients/project-assets.ts +96 -0
  87. package/lib/agent-clients/reconcile.ts +324 -0
  88. package/lib/agent-clients/registry.ts +144 -0
  89. package/lib/agent-clients/schema.ts +62 -0
  90. package/lib/agent-clients/types.ts +78 -0
  91. package/lib/benchmark/schemas/v1.0.0/case-manifest.schema.json +120 -0
  92. package/lib/benchmark/schemas/v1.0.0/common.schema.json +109 -0
  93. package/lib/benchmark/schemas/v1.0.0/grader-result.schema.json +160 -0
  94. package/lib/benchmark/schemas/v1.0.0/run-manifest.schema.json +122 -0
  95. package/lib/benchmark/schemas/v1.0.0/subject.schema.json +227 -0
  96. package/lib/builtin-tuis.ts +16 -15
  97. package/lib/defaults.json +24 -12
  98. package/lib/git/review-snapshot.ts +39 -9
  99. package/lib/git/reviewed-commit-equivalence.ts +135 -0
  100. package/lib/init.ts +53 -121
  101. package/lib/internal/agent-client.ts +131 -0
  102. package/lib/internal/git-workflow.ts +1 -1
  103. package/lib/internal/release-workflow.ts +68 -16
  104. package/lib/internal/task-orchestration.ts +132 -0
  105. package/lib/internal/task-review.ts +65 -0
  106. package/lib/merge.ts +2 -1
  107. package/lib/platform/adapters.ts +142 -3
  108. package/lib/platform/change-request-git-evidence.ts +159 -0
  109. package/lib/platform/merged-pr-equivalence.ts +51 -14
  110. package/lib/platform/pr-checks.ts +50 -22
  111. package/lib/platform/pull-requests.ts +134 -25
  112. package/lib/platform/releases.ts +1 -1
  113. package/lib/platform/verification-required.ts +12 -10
  114. package/lib/prompt.ts +16 -3
  115. package/lib/release/channels.ts +4 -1
  116. package/lib/release/workflow.ts +4 -3
  117. package/lib/remove-dir.ts +28 -0
  118. package/lib/run/tui.ts +9 -1
  119. package/lib/sandbox/agent-client-reconciler.ts +378 -0
  120. package/lib/sandbox/clipboard/linux.ts +2 -1
  121. package/lib/sandbox/commands/create.ts +111 -192
  122. package/lib/sandbox/commands/enter.ts +23 -1
  123. package/lib/sandbox/commands/prune.ts +2 -2
  124. package/lib/sandbox/commands/rm.ts +3 -2
  125. package/lib/sandbox/config.ts +31 -4
  126. package/lib/sandbox/constants.ts +6 -0
  127. package/lib/sandbox/dockerfile.ts +2 -1
  128. package/lib/sandbox/managed-fs.ts +18 -5
  129. package/lib/sandbox/recovery.ts +98 -67
  130. package/lib/sandbox/shell.ts +1 -1
  131. package/lib/sandbox/tool-types.ts +143 -0
  132. package/lib/sandbox/tools.ts +5 -157
  133. package/lib/task/artifact-lifecycle.ts +1 -38
  134. package/lib/task/commands/decisions.ts +53 -20
  135. package/lib/task/delegation-receipts.ts +170 -0
  136. package/lib/task/events.ts +110 -2
  137. package/lib/task/orchestration.ts +543 -0
  138. package/lib/task/review-artifacts.ts +201 -69
  139. package/lib/task/review-finalization.ts +275 -0
  140. package/lib/task/review-fingerprint.ts +5 -0
  141. package/lib/task/verification-checks.ts +2 -1
  142. package/lib/task/verification-engine.ts +177 -28
  143. package/lib/task/verification-review.ts +1 -0
  144. package/lib/task/verification.ts +5 -2
  145. package/lib/task/workspace-snapshot.ts +36 -0
  146. package/lib/update.ts +48 -184
  147. package/package.json +2 -2
  148. package/templates/.agents/README.en.md +21 -20
  149. package/templates/.agents/README.zh-CN.md +21 -20
  150. package/templates/.agents/hooks/lifecycle-delegation.js +64 -0
  151. package/templates/.agents/rules/README.en.md +2 -0
  152. package/templates/.agents/rules/README.zh-CN.md +2 -0
  153. package/templates/.agents/rules/commit-and-pr.en.md +1 -0
  154. package/templates/.agents/rules/commit-and-pr.zh-CN.md +1 -0
  155. package/templates/.agents/rules/human-decision-context.en.md +14 -12
  156. package/templates/.agents/rules/human-decision-context.zh-CN.md +14 -12
  157. package/templates/.agents/rules/lifecycle-orchestration.en.md +21 -0
  158. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +21 -0
  159. package/templates/.agents/rules/next-step-output.en.md +16 -0
  160. package/templates/.agents/rules/next-step-output.zh-CN.md +16 -0
  161. package/templates/.agents/rules/no-mid-flow-questions.en.md +2 -0
  162. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +2 -0
  163. package/templates/.agents/rules/release-commands.github.en.md +11 -5
  164. package/templates/.agents/rules/release-commands.github.zh-CN.md +11 -5
  165. package/templates/.agents/rules/review-handshake.en.md +4 -4
  166. package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
  167. package/templates/.agents/rules/review-method.en.md +113 -0
  168. package/templates/.agents/rules/review-method.zh-CN.md +98 -0
  169. package/templates/.agents/skills/analyze-task/SKILL.en.md +5 -5
  170. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +5 -5
  171. package/templates/.agents/skills/block-task/SKILL.en.md +4 -4
  172. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +4 -4
  173. package/templates/.agents/skills/cancel-task/SKILL.en.md +4 -4
  174. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +5 -5
  175. package/templates/.agents/skills/check-task/SKILL.en.md +16 -16
  176. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -16
  177. package/templates/.agents/skills/close-codescan/SKILL.en.md +4 -4
  178. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +4 -4
  179. package/templates/.agents/skills/close-dependabot/SKILL.en.md +4 -4
  180. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +4 -4
  181. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  182. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -2
  183. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +14 -16
  184. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +14 -16
  185. package/templates/.agents/skills/code-task/reference/output-template.en.md +3 -3
  186. package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +3 -3
  187. package/templates/.agents/skills/commit/SKILL.en.md +11 -1
  188. package/templates/.agents/skills/commit/SKILL.zh-CN.md +11 -1
  189. package/templates/.agents/skills/commit/reference/task-status-update.en.md +24 -28
  190. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +24 -28
  191. package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
  192. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
  193. package/templates/.agents/skills/create-pr/SKILL.en.md +10 -10
  194. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +10 -10
  195. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +3 -3
  196. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +3 -3
  197. package/templates/.agents/skills/create-task/SKILL.en.md +11 -11
  198. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +11 -11
  199. package/templates/.agents/skills/import-codescan/SKILL.en.md +5 -5
  200. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +5 -5
  201. package/templates/.agents/skills/import-dependabot/SKILL.en.md +5 -5
  202. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +5 -5
  203. package/templates/.agents/skills/import-issue/SKILL.en.md +5 -5
  204. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -5
  205. package/templates/.agents/skills/init-labels/SKILL.en.md +4 -4
  206. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +4 -4
  207. package/templates/.agents/skills/init-milestones/SKILL.en.md +4 -4
  208. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +4 -4
  209. package/templates/.agents/skills/plan-task/SKILL.en.md +5 -5
  210. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -5
  211. package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
  212. package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
  213. package/templates/.agents/skills/release/SKILL.en.md +1 -1
  214. package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -2
  215. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -3
  216. package/templates/.agents/skills/review-analysis/config/verify.en.json +6 -0
  217. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +6 -0
  218. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +13 -15
  219. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +13 -15
  220. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +51 -5
  221. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +52 -6
  222. package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +25 -22
  223. package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +25 -22
  224. package/templates/.agents/skills/review-code/SKILL.en.md +28 -8
  225. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +28 -8
  226. package/templates/.agents/skills/review-code/config/verify.en.json +7 -0
  227. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +7 -0
  228. package/templates/.agents/skills/review-code/reference/concurrency-risks.en.md +25 -0
  229. package/templates/.agents/skills/review-code/reference/concurrency-risks.zh-CN.md +25 -0
  230. package/templates/.agents/skills/review-code/reference/cross-platform-risks.en.md +27 -0
  231. package/templates/.agents/skills/review-code/reference/cross-platform-risks.zh-CN.md +27 -0
  232. package/templates/.agents/skills/review-code/reference/documentation-antipatterns.en.md +13 -0
  233. package/templates/.agents/skills/review-code/reference/documentation-antipatterns.zh-CN.md +13 -0
  234. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +25 -0
  235. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +25 -0
  236. package/templates/.agents/skills/review-code/reference/output-templates.en.md +22 -24
  237. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +22 -24
  238. package/templates/.agents/skills/review-code/reference/report-template.en.md +54 -7
  239. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +54 -7
  240. package/templates/.agents/skills/review-code/reference/review-criteria.en.md +29 -24
  241. package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +29 -24
  242. package/templates/.agents/skills/review-code/reference/security-risks.en.md +25 -0
  243. package/templates/.agents/skills/review-code/reference/security-risks.zh-CN.md +25 -0
  244. package/templates/.agents/skills/review-plan/SKILL.en.md +13 -2
  245. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -3
  246. package/templates/.agents/skills/review-plan/config/verify.en.json +6 -0
  247. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +6 -0
  248. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +13 -15
  249. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +13 -15
  250. package/templates/.agents/skills/review-plan/reference/report-template.en.md +61 -5
  251. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +61 -5
  252. package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +21 -24
  253. package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +21 -24
  254. package/templates/.agents/skills/run-task/SKILL.en.md +22 -0
  255. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +22 -0
  256. package/templates/.agents/skills/run-task/config/verify.en.json +9 -0
  257. package/templates/.agents/skills/run-task/config/verify.zh-CN.json +9 -0
  258. package/templates/.agents/skills/test/SKILL.en.md +4 -4
  259. package/templates/.agents/skills/test/SKILL.zh-CN.md +4 -4
  260. package/templates/.agents/skills/test-integration/SKILL.en.md +4 -4
  261. package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +4 -4
  262. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +5 -5
  263. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +5 -5
  264. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +647 -203
  265. package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +4 -4
  266. package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +4 -4
  267. package/templates/.agents/skills/watch-pr/SKILL.en.md +8 -8
  268. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +8 -8
  269. package/templates/.claude/agents/agent-infra-lifecycle-executor.en.md +6 -0
  270. package/templates/.claude/agents/agent-infra-lifecycle-executor.zh-CN.md +6 -0
  271. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.en.md +6 -0
  272. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.zh-CN.md +6 -0
  273. package/templates/.claude/commands/run-task.en.md +8 -0
  274. package/templates/.claude/commands/run-task.zh-CN.md +8 -0
  275. package/templates/.claude/settings.json +24 -0
  276. package/templates/.codex/agents/agent-infra-lifecycle-executor.en.toml +3 -0
  277. package/templates/.codex/agents/agent-infra-lifecycle-executor.zh-CN.toml +3 -0
  278. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.en.toml +3 -0
  279. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.zh-CN.toml +3 -0
  280. package/templates/.gemini/commands/_project_/run-task.en.toml +8 -0
  281. package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +8 -0
  282. package/templates/.opencode/commands/run-task.en.md +11 -0
  283. package/templates/.opencode/commands/run-task.zh-CN.md +11 -0
@@ -0,0 +1,543 @@
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 { getAgentClientCapability } from '../agent-clients/registry.ts';
20
+ import type { AgentClientId } from '../agent-clients/types.ts';
21
+ import { captureWorkspaceSnapshot, diffWorkspaceSnapshots } from './workspace-snapshot.ts';
22
+
23
+ type OrchestrationStatus = 'running' | 'paused' | 'completed';
24
+ type OrchestrationRun = Readonly<{
25
+ schemaVersion: 1;
26
+ taskId: string;
27
+ runId: string;
28
+ status: OrchestrationStatus;
29
+ nextStage: DelegationStage | null;
30
+ stepCount: number;
31
+ maxSteps: number;
32
+ baseline: string;
33
+ pendingDelegation: DelegationReceipt | null;
34
+ receipts: readonly DelegationReceipt[];
35
+ pause: Readonly<{ code: string; message: string; recoverable: boolean }> | null;
36
+ commitAuthorization: Readonly<{ issuedAt: string | null; consumedAt: string | null }>;
37
+ createdAt: string;
38
+ updatedAt: string;
39
+ }>;
40
+ type OrchestrationNext = Readonly<{
41
+ action: 'analyze-task' | 'review-analysis' | 'plan-task' | 'review-plan' | 'code-task' | 'review-code' | 'commit';
42
+ role: DelegationRole;
43
+ stage: DelegationStage;
44
+ round: number;
45
+ artifact: string;
46
+ }>;
47
+ type OrchestrationResult = Readonly<{
48
+ status: OrchestrationStatus | 'failed';
49
+ changed: boolean;
50
+ taskId: string | null;
51
+ run: OrchestrationRun | null;
52
+ next: OrchestrationNext | null;
53
+ error: Readonly<{ code: string; message: string }> | null;
54
+ }>;
55
+ type OrchestrationOptions = {
56
+ repoRoot?: string;
57
+ id?: () => string;
58
+ now?: () => string;
59
+ maxSteps?: number;
60
+ captureWorkspace?: (repoRoot: string) => string;
61
+ diffWorkspace?: (repoRoot: string, before: string, after: string) => string[];
62
+ };
63
+
64
+ function orchestrationPath(taskDir: string): string {
65
+ return path.join(taskDir, 'orchestration.json');
66
+ }
67
+
68
+ function readRun(taskDir: string): OrchestrationRun | null {
69
+ const file = orchestrationPath(taskDir);
70
+ if (!fs.existsSync(file)) return null;
71
+ return JSON.parse(fs.readFileSync(file, 'utf8')) as OrchestrationRun;
72
+ }
73
+
74
+ function atomicWrite(file: string, value: unknown): void {
75
+ const temp = `${file}.${process.pid}.${randomUUID()}.tmp`;
76
+ fs.writeFileSync(temp, `${JSON.stringify(value, null, 2)}\n`, { encoding: 'utf8', flag: 'wx' });
77
+ fs.renameSync(temp, file);
78
+ }
79
+
80
+ function saveRun(taskDir: string, run: OrchestrationRun): void {
81
+ atomicWrite(orchestrationPath(taskDir), run);
82
+ }
83
+
84
+ function withUpdatedRun(run: OrchestrationRun, updates: Partial<OrchestrationRun>): OrchestrationRun {
85
+ return Object.freeze({ ...run, ...updates, updatedAt: new Date().toISOString() });
86
+ }
87
+
88
+ function failed(code: string, message: string, taskId: string | null = null): OrchestrationResult {
89
+ return { status: 'failed', changed: false, taskId, run: null, next: null, error: { code, message } };
90
+ }
91
+
92
+ function beginOrResumeOrchestration(taskRef: string, options: OrchestrationOptions = {}): OrchestrationResult {
93
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
94
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
95
+ const existing = readRun(resolved.taskDir);
96
+ if (existing) {
97
+ if (existing.status === 'paused' && existing.pause?.recoverable && existing.pendingDelegation === null) {
98
+ const resumed = withUpdatedRun(existing, { status: 'running', pause: null });
99
+ saveRun(resolved.taskDir, resumed);
100
+ return { status: 'running', changed: true, taskId: resolved.taskId, run: resumed, next: null, error: null };
101
+ }
102
+ return { status: existing.status, changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
103
+ }
104
+ const now = (options.now ?? (() => new Date().toISOString()))();
105
+ const run: OrchestrationRun = {
106
+ schemaVersion: 1,
107
+ taskId: resolved.taskId,
108
+ runId: (options.id ?? randomUUID)(),
109
+ status: 'running',
110
+ nextStage: null,
111
+ stepCount: 0,
112
+ maxSteps: options.maxSteps ?? 24,
113
+ baseline: '',
114
+ pendingDelegation: null,
115
+ receipts: [],
116
+ pause: null,
117
+ commitAuthorization: { issuedAt: null, consumedAt: null },
118
+ createdAt: now,
119
+ updatedAt: now
120
+ };
121
+ atomicWrite(orchestrationPath(resolved.taskDir), run);
122
+ return { status: 'running', changed: true, taskId: resolved.taskId, run, next: null, error: null };
123
+ }
124
+
125
+ function highestRound(taskDir: string, family: string): number {
126
+ const pattern = new RegExp(`^${family}(?:-r(\\d+))?\\.md$`);
127
+ return fs.readdirSync(taskDir).reduce((max, name) => {
128
+ const match = pattern.exec(name);
129
+ return match ? Math.max(max, match[1] ? Number(match[1]) : 1) : max;
130
+ }, 0);
131
+ }
132
+
133
+ function artifactName(family: string, round: number): string {
134
+ return round === 1 ? `${family}.md` : `${family}-r${round}.md`;
135
+ }
136
+
137
+ function hasReviewAfterArtifact(taskDir: string, family: string, reviewFamily: string): boolean {
138
+ const artifact = inspectArtifactDirectory(taskDir, family as 'analysis' | 'plan' | 'code');
139
+ const review = inspectArtifactDirectory(taskDir, reviewFamily as 'review-analysis' | 'review-plan' | 'review-code');
140
+ return artifact.status === 'ready'
141
+ && review.status === 'ready'
142
+ && artifact.latest !== null
143
+ && review.reviewedInput?.name === artifact.latest.name;
144
+ }
145
+
146
+ function latestReviewApproved(taskDir: string, family: 'review-analysis' | 'review-plan' | 'review-code'): boolean {
147
+ const round = highestRound(taskDir, family);
148
+ if (round === 0) return false;
149
+ const parsed = parseReviewSummary(fs.readFileSync(path.join(taskDir, artifactName(family, round)), 'utf8'));
150
+ return parsed.ok && parsed.summary.verdict === 'Approved' && parsed.summary.counts !== null;
151
+ }
152
+
153
+ function routeFromFacts(taskDir: string): OrchestrationNext | null {
154
+ const analysisRound = highestRound(taskDir, 'analysis');
155
+ const analysisReviewRound = highestRound(taskDir, 'review-analysis');
156
+ const planRound = highestRound(taskDir, 'plan');
157
+ const planReviewRound = highestRound(taskDir, 'review-plan');
158
+ const codeRound = highestRound(taskDir, 'code');
159
+ const codeReviewRound = highestRound(taskDir, 'review-code');
160
+ if (analysisRound === 0) {
161
+ const round = analysisRound + 1;
162
+ return { action: 'analyze-task', role: 'executor', stage: 'analysis', round, artifact: artifactName('analysis', round) };
163
+ }
164
+ if (!hasReviewAfterArtifact(taskDir, 'analysis', 'review-analysis')) {
165
+ const round = analysisReviewRound + 1;
166
+ return { action: 'review-analysis', role: 'reviewer', stage: 'review-analysis', round, artifact: artifactName('review-analysis', round) };
167
+ }
168
+ if (!latestReviewApproved(taskDir, 'review-analysis')) {
169
+ const round = analysisRound + 1;
170
+ return { action: 'analyze-task', role: 'executor', stage: 'analysis', round, artifact: artifactName('analysis', round) };
171
+ }
172
+ if (planRound === 0) {
173
+ const round = planRound + 1;
174
+ return { action: 'plan-task', role: 'executor', stage: 'plan', round, artifact: artifactName('plan', round) };
175
+ }
176
+ if (!hasReviewAfterArtifact(taskDir, 'plan', 'review-plan')) {
177
+ const round = planReviewRound + 1;
178
+ return { action: 'review-plan', role: 'reviewer', stage: 'review-plan', round, artifact: artifactName('review-plan', round) };
179
+ }
180
+ if (!latestReviewApproved(taskDir, 'review-plan')) {
181
+ const round = planRound + 1;
182
+ return { action: 'plan-task', role: 'executor', stage: 'plan', round, artifact: artifactName('plan', round) };
183
+ }
184
+ if (codeRound === 0) {
185
+ const round = codeRound + 1;
186
+ return { action: 'code-task', role: 'executor', stage: 'code', round, artifact: artifactName('code', round) };
187
+ }
188
+ if (!hasReviewAfterArtifact(taskDir, 'code', 'review-code')) {
189
+ const round = codeReviewRound + 1;
190
+ return { action: 'review-code', role: 'reviewer', stage: 'review-code', round, artifact: artifactName('review-code', round) };
191
+ }
192
+ if (!latestReviewApproved(taskDir, 'review-code')) {
193
+ const round = codeRound + 1;
194
+ return { action: 'code-task', role: 'executor', stage: 'code', round, artifact: artifactName('code', round) };
195
+ }
196
+ return { action: 'commit', role: 'executor', stage: 'commit', round: 1, artifact: 'commit' };
197
+ }
198
+
199
+ function routeOrchestration(taskRef: string, options: OrchestrationOptions = {}): OrchestrationResult {
200
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
201
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
202
+ let content: string;
203
+ try {
204
+ content = fs.readFileSync(resolved.taskMdPath, 'utf8');
205
+ } catch (error) {
206
+ return failed('ORCHESTRATION_TASK_READ_FAILED', String(error), resolved.taskId);
207
+ }
208
+ try {
209
+ parseTypedTaskFrontmatter(content);
210
+ } catch (error) {
211
+ return failed('ORCHESTRATION_TASK_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
212
+ }
213
+ const next = routeFromFacts(resolved.taskDir);
214
+ if (!next) return failed('ORCHESTRATION_ROUTE_UNKNOWN', 'cannot determine a unique lifecycle action', resolved.taskId);
215
+ if (next.stage === 'commit') {
216
+ const reviewRound = highestRound(resolved.taskDir, 'review-code');
217
+ const review = parseReviewSummary(fs.readFileSync(
218
+ path.join(resolved.taskDir, artifactName('review-code', reviewRound)),
219
+ 'utf8'
220
+ ));
221
+ if (!review.ok || review.summary.manualValidation === null) {
222
+ return failed('ORCHESTRATION_REVIEW_INVALID', 'latest code review has no numeric manual-validation count', resolved.taskId);
223
+ }
224
+ if (review.summary.manualValidation > 0) {
225
+ return failed('ORCHESTRATION_MANUAL_VALIDATION_PENDING', 'manual validation must be completed before commit', resolved.taskId);
226
+ }
227
+ const rows = parseLedger(content);
228
+ const ledgerError = validateLedgerRows(rows);
229
+ if (ledgerError) return failed('ORCHESTRATION_LEDGER_INVALID', ledgerError.message, resolved.taskId);
230
+ if (!summarizeLedgerStage(rows, 'code').canAdvance) {
231
+ return failed('ORCHESTRATION_LEDGER_BLOCKED', 'code review ledger has unresolved findings or human decisions', resolved.taskId);
232
+ }
233
+ }
234
+ return { status: 'running', changed: false, taskId: resolved.taskId, run: readRun(resolved.taskDir), next, error: null };
235
+ }
236
+
237
+ function statusOrchestration(taskRef: string, options: OrchestrationOptions = {}): OrchestrationResult {
238
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
239
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
240
+ const run = readRun(resolved.taskDir);
241
+ if (!run) return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
242
+ return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
243
+ }
244
+
245
+ function prepareOrchestrationDelegation(
246
+ taskRef: string,
247
+ input: Readonly<{ client: AgentClientId; requestedModel?: string }>,
248
+ options: OrchestrationOptions = {}
249
+ ): OrchestrationResult {
250
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
251
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
252
+ if (
253
+ getAgentClientCapability(input.client, 'subagents').level === 'unsupported'
254
+ || getAgentClientCapability(input.client, 'orchestration').level === 'unsupported'
255
+ ) {
256
+ return failed('ORCHESTRATION_CLIENT_UNSUPPORTED', `client '${input.client}' does not support lifecycle orchestration`, resolved.taskId);
257
+ }
258
+ const run = readRun(resolved.taskDir);
259
+ if (!run || run.status !== 'running') return failed('ORCHESTRATION_RUN_NOT_RUNNING', 'a running orchestration is required', resolved.taskId);
260
+ if (run.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_BUSY', 'the run already has a pending delegation', resolved.taskId);
261
+ const repositoryPending = (['claude-code', 'codex'] as AgentClientId[])
262
+ .flatMap((client) => matchingDelegations(client, () => true, options));
263
+ if (repositoryPending.length > 0) {
264
+ return failed('ORCHESTRATION_DELEGATION_BUSY', 'the repository already has a pending lifecycle delegation', resolved.taskId);
265
+ }
266
+ if (run.stepCount >= run.maxSteps) return pauseOrchestration(taskRef, 'ORCHESTRATION_MAX_STEPS', 'maximum orchestration steps reached', true, options);
267
+ const routed = routeOrchestration(taskRef, options);
268
+ if (!routed.next) return routed;
269
+ const next = routed.next;
270
+ let beforeFingerprint: string;
271
+ try {
272
+ beforeFingerprint = (options.captureWorkspace ?? captureWorkspaceSnapshot)(resolved.repoRoot);
273
+ } catch (error) {
274
+ return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
275
+ }
276
+ const receipt = prepareDelegation({
277
+ taskId: resolved.taskId,
278
+ runId: run.runId,
279
+ role: next.role,
280
+ stage: next.stage,
281
+ round: next.round,
282
+ artifact: next.artifact,
283
+ client: input.client,
284
+ requestedModel: input.requestedModel ?? null,
285
+ beforeFingerprint
286
+ }, { id: options.id, now: options.now });
287
+ const updated = withUpdatedRun(run, {
288
+ nextStage: next.stage,
289
+ pendingDelegation: receipt,
290
+ commitAuthorization: next.stage === 'commit' && run.commitAuthorization.issuedAt === null
291
+ ? { issuedAt: (options.now ?? (() => new Date().toISOString()))(), consumedAt: null }
292
+ : run.commitAuthorization
293
+ });
294
+ saveRun(resolved.taskDir, updated);
295
+ return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next, error: null };
296
+ }
297
+
298
+ function matchingDelegations(
299
+ client: AgentClientId,
300
+ predicate: (receipt: DelegationReceipt) => boolean,
301
+ options: OrchestrationOptions
302
+ ): Array<{ taskId: string; run: OrchestrationRun }> {
303
+ const repoRoot = options.repoRoot ?? process.cwd();
304
+ const activeRoot = path.join(repoRoot, '.agents', 'workspace', 'active');
305
+ if (!fs.existsSync(activeRoot)) return [];
306
+ return fs.readdirSync(activeRoot, { withFileTypes: true })
307
+ .filter((entry) => entry.isDirectory())
308
+ .flatMap((entry) => {
309
+ const run = readRun(path.join(activeRoot, entry.name));
310
+ if (!run?.pendingDelegation) return [];
311
+ const receipt = run.pendingDelegation;
312
+ return receipt.client === client && predicate(receipt)
313
+ ? [{ taskId: entry.name, run }]
314
+ : [];
315
+ });
316
+ }
317
+
318
+ function uniqueMatchingDelegation(
319
+ client: AgentClientId,
320
+ predicate: (receipt: DelegationReceipt) => boolean,
321
+ options: OrchestrationOptions
322
+ ): { taskId: string; run: OrchestrationRun } | OrchestrationResult {
323
+ const matches = matchingDelegations(client, predicate, options);
324
+ if (matches.length === 0) return failed('ORCHESTRATION_DELEGATION_MISSING', 'no matching lifecycle delegation exists');
325
+ if (matches.length > 1) return failed('ORCHESTRATION_DELEGATION_AMBIGUOUS', 'multiple lifecycle delegations match the native hook event');
326
+ return matches[0]!;
327
+ }
328
+
329
+ function activateMatchingOrchestrationDelegation(
330
+ client: AgentClientId,
331
+ event: Parameters<typeof activateDelegation>[1],
332
+ options: OrchestrationOptions = {}
333
+ ): OrchestrationResult {
334
+ const role = managedDelegationRole(event.nativeAgent);
335
+ if (!role) return failed('DELEGATION_IGNORED', `subagent '${event.nativeAgent}' is not lifecycle-managed`);
336
+ const matched = uniqueMatchingDelegation(
337
+ client,
338
+ (receipt) => receipt.status === 'prepared',
339
+ options
340
+ );
341
+ if ('status' in matched) return matched;
342
+ if (matched.run.receipts.some((receipt) => receipt.childId === event.childId)) {
343
+ return pauseOrchestration(matched.taskId, 'DELEGATION_IDENTITY_REUSED', 'native child identity was already used by this run', true, options);
344
+ }
345
+ return activateOrchestrationDelegation(matched.taskId, event, options);
346
+ }
347
+
348
+ function sealMatchingOrchestrationDelegation(
349
+ client: AgentClientId,
350
+ event: Readonly<{ nativeAgent: string; childId: string }>,
351
+ options: OrchestrationOptions = {}
352
+ ): OrchestrationResult {
353
+ const role = managedDelegationRole(event.nativeAgent);
354
+ if (!role) return failed('DELEGATION_IGNORED', `subagent '${event.nativeAgent}' is not lifecycle-managed`);
355
+ const matched = uniqueMatchingDelegation(
356
+ client,
357
+ (receipt) => receipt.status !== 'prepared',
358
+ options
359
+ );
360
+ if ('status' in matched) return matched;
361
+ const receipt = matched.run.pendingDelegation!;
362
+ if (receipt.role !== role) {
363
+ return pauseOrchestration(matched.taskId, 'DELEGATION_ROLE_MISMATCH', `managed role ${role} does not match ${receipt.role}`, true, options);
364
+ }
365
+ const repoRoot = options.repoRoot ?? process.cwd();
366
+ try {
367
+ const afterFingerprint = (options.captureWorkspace ?? captureWorkspaceSnapshot)(repoRoot);
368
+ const changedPaths = (options.diffWorkspace ?? diffWorkspaceSnapshots)(repoRoot, receipt.beforeFingerprint, afterFingerprint);
369
+ return sealOrchestrationDelegation(matched.taskId, {
370
+ childId: event.childId,
371
+ exitCode: 0,
372
+ afterFingerprint,
373
+ changedPaths
374
+ }, options);
375
+ } catch (error) {
376
+ return pauseOrchestration(
377
+ matched.taskId,
378
+ 'ORCHESTRATION_SNAPSHOT_FAILED',
379
+ error instanceof Error ? error.message : String(error),
380
+ true,
381
+ options
382
+ );
383
+ }
384
+ }
385
+
386
+ function activateOrchestrationDelegation(
387
+ taskRef: string,
388
+ event: Parameters<typeof activateDelegation>[1],
389
+ options: OrchestrationOptions = {}
390
+ ): OrchestrationResult {
391
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
392
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
393
+ const run = readRun(resolved.taskDir);
394
+ if (!run?.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
395
+ const result = activateDelegation(run.pendingDelegation, event, { now: options.now });
396
+ if (!result.ok) {
397
+ if (result.code === 'DELEGATION_IGNORED') return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
398
+ return pauseOrchestration(taskRef, result.code, result.message, true, options);
399
+ }
400
+ const updated = withUpdatedRun(run, { pendingDelegation: result.receipt });
401
+ saveRun(resolved.taskDir, updated);
402
+ return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
403
+ }
404
+
405
+ function completeOrchestrationStage(
406
+ taskRef: string,
407
+ event: Parameters<typeof completeDelegationStage>[1],
408
+ options: OrchestrationOptions = {}
409
+ ): OrchestrationResult {
410
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
411
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
412
+ const run = readRun(resolved.taskDir);
413
+ if (!run) return { status: 'running', changed: false, taskId: resolved.taskId, run: null, next: null, error: null };
414
+ if (!run.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'active run has no pending delegation', resolved.taskId);
415
+ const result = completeDelegationStage(run.pendingDelegation, event);
416
+ if (!result.ok) return pauseOrchestration(taskRef, result.code, result.message, true, options);
417
+ const updated = withUpdatedRun(run, { pendingDelegation: result.receipt });
418
+ saveRun(resolved.taskDir, updated);
419
+ return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
420
+ }
421
+
422
+ function completeCommitOrchestrationStage(
423
+ taskRef: string,
424
+ agent: string,
425
+ options: OrchestrationOptions = {}
426
+ ): OrchestrationResult {
427
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
428
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
429
+ const run = readRun(resolved.taskDir);
430
+ if (!run) return { status: 'running', changed: false, taskId: resolved.taskId, run: null, next: null, error: null };
431
+ const receipt = run.pendingDelegation;
432
+ if (!receipt || receipt.stage !== 'commit') {
433
+ return failed('ORCHESTRATION_DELEGATION_MISSING', 'active run has no pending commit delegation', resolved.taskId);
434
+ }
435
+ return completeOrchestrationStage(taskRef, {
436
+ stage: receipt.stage,
437
+ round: receipt.round,
438
+ artifact: receipt.artifact,
439
+ agent
440
+ }, options);
441
+ }
442
+
443
+ function validateOrchestrationStage(
444
+ taskRef: string,
445
+ event: Readonly<{ stage: DelegationStage; round: number; artifact: string; role: DelegationRole }>,
446
+ options: OrchestrationOptions = {}
447
+ ): OrchestrationResult {
448
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
449
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
450
+ const run = readRun(resolved.taskDir);
451
+ if (!run) return { status: 'running', changed: false, taskId: resolved.taskId, run: null, next: null, error: null };
452
+ const receipt = run.pendingDelegation;
453
+ if (
454
+ !receipt
455
+ || receipt.status !== 'activated'
456
+ || receipt.stage !== event.stage
457
+ || receipt.round !== event.round
458
+ || receipt.artifact !== event.artifact
459
+ || receipt.role !== event.role
460
+ ) {
461
+ return pauseOrchestration(
462
+ taskRef,
463
+ 'ORCHESTRATION_PROVENANCE_MISMATCH',
464
+ 'active run does not have one matching activated delegation',
465
+ true,
466
+ options
467
+ );
468
+ }
469
+ return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
470
+ }
471
+
472
+ function sealOrchestrationDelegation(
473
+ taskRef: string,
474
+ event: Parameters<typeof sealDelegation>[1],
475
+ options: OrchestrationOptions = {}
476
+ ): OrchestrationResult {
477
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
478
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
479
+ const run = readRun(resolved.taskDir);
480
+ if (!run?.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
481
+ const result = sealDelegation(run.pendingDelegation, event, { now: options.now });
482
+ if (!result.ok) return pauseOrchestration(taskRef, result.code, result.message, true, options);
483
+ const updated = withUpdatedRun(run, { pendingDelegation: result.receipt });
484
+ saveRun(resolved.taskDir, updated);
485
+ return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
486
+ }
487
+
488
+ function advanceOrchestration(taskRef: string, options: OrchestrationOptions = {}): OrchestrationResult {
489
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
490
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
491
+ const run = readRun(resolved.taskDir);
492
+ if (!run?.pendingDelegation) return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
493
+ const result = consumeDelegation(run.pendingDelegation, { now: options.now });
494
+ if (!result.ok) return pauseOrchestration(taskRef, result.code, result.message, true, options);
495
+ const completed = result.receipt.stage === 'commit';
496
+ const updated = withUpdatedRun(run, {
497
+ status: completed ? 'completed' : 'running',
498
+ nextStage: null,
499
+ stepCount: run.stepCount + 1,
500
+ pendingDelegation: null,
501
+ receipts: Object.freeze([...run.receipts, result.receipt]),
502
+ commitAuthorization: completed
503
+ ? { ...run.commitAuthorization, consumedAt: (options.now ?? (() => new Date().toISOString()))() }
504
+ : run.commitAuthorization
505
+ });
506
+ saveRun(resolved.taskDir, updated);
507
+ return { status: updated.status, changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
508
+ }
509
+
510
+ function pauseOrchestration(
511
+ taskRef: string,
512
+ code: string,
513
+ message: string,
514
+ recoverable: boolean,
515
+ options: OrchestrationOptions = {}
516
+ ): OrchestrationResult {
517
+ const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
518
+ if (!resolved.ok) return failed(resolved.code, resolved.message, resolved.taskId);
519
+ const run = readRun(resolved.taskDir);
520
+ if (!run) return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
521
+ const updated = withUpdatedRun(run, { status: 'paused', pause: { code, message, recoverable } });
522
+ saveRun(resolved.taskDir, updated);
523
+ return { status: 'paused', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
524
+ }
525
+
526
+ export {
527
+ activateMatchingOrchestrationDelegation,
528
+ activateOrchestrationDelegation,
529
+ advanceOrchestration,
530
+ beginOrResumeOrchestration,
531
+ completeCommitOrchestrationStage,
532
+ completeOrchestrationStage,
533
+ orchestrationPath,
534
+ pauseOrchestration,
535
+ prepareOrchestrationDelegation,
536
+ readRun,
537
+ routeOrchestration,
538
+ sealMatchingOrchestrationDelegation,
539
+ sealOrchestrationDelegation,
540
+ statusOrchestration,
541
+ validateOrchestrationStage
542
+ };
543
+ export type { OrchestrationNext, OrchestrationResult, OrchestrationRun, OrchestrationStatus };