@fitlab-ai/agent-infra 0.12.1 → 0.12.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 (303) hide show
  1. package/README.md +0 -2
  2. package/README.zh-CN.md +0 -2
  3. package/dist/bin/internal-cli.js +2 -17
  4. package/dist/lib/agent-clients/adapter.js +0 -13
  5. package/dist/lib/agent-clients/adapters/antigravity-cli.js +0 -5
  6. package/dist/lib/agent-clients/adapters/claude-code-credentials.js +0 -17
  7. package/dist/lib/agent-clients/adapters/claude-code.js +0 -9
  8. package/dist/lib/agent-clients/adapters/codex.js +0 -52
  9. package/dist/lib/agent-clients/adapters/opencode.js +0 -5
  10. package/dist/lib/agent-clients/adapters/traecli.js +0 -5
  11. package/dist/lib/agent-clients/config.js +19 -54
  12. package/dist/lib/agent-clients/registry.js +1 -12
  13. package/dist/lib/agent-clients/schema.js +0 -18
  14. package/dist/lib/agent-clients/tokens.js +1 -2
  15. package/dist/lib/agent-clients/types.js +0 -1
  16. package/dist/lib/decide.js +2 -0
  17. package/dist/lib/git/worktree-identity.js +1 -9
  18. package/dist/lib/internal/agent-client.js +2 -62
  19. package/dist/lib/internal/cli-route-inventory.js +1 -5
  20. package/dist/lib/internal/manual-validation.js +18 -10
  21. package/dist/lib/internal/task-create.js +1 -1
  22. package/dist/lib/internal/task-event.js +1 -8
  23. package/dist/lib/internal/task-finalization.js +0 -1
  24. package/dist/lib/internal/task-lifecycle.js +2 -2
  25. package/dist/lib/internal/task-operation-registry.js +4 -20
  26. package/dist/lib/internal/task-review.js +4 -4
  27. package/dist/lib/platform/comment-safety.js +1 -7
  28. package/dist/lib/platform/in-label-sync.js +1 -14
  29. package/dist/lib/platform/issue-comments.js +1 -16
  30. package/dist/lib/platform/issues.js +1 -1
  31. package/dist/lib/platform/operation-coordinator.js +4 -2
  32. package/dist/lib/platform/pr-change-report.js +1 -4
  33. package/dist/lib/platform/pr-checks.js +2 -8
  34. package/dist/lib/platform/provider-validation.js +0 -20
  35. package/dist/lib/platform/pull-requests.js +73 -84
  36. package/dist/lib/platform/releases.js +1 -43
  37. package/dist/lib/platform/verification-required.js +0 -2
  38. package/dist/lib/platform/verification-sync.js +1 -79
  39. package/dist/lib/process-data/normalize.js +1 -185
  40. package/dist/lib/process-data/privacy.js +1 -16
  41. package/dist/lib/process-data/sources.js +2 -279
  42. package/dist/lib/render.js +0 -12
  43. package/dist/lib/run/index.js +15 -10
  44. package/dist/lib/run/skills.js +0 -3
  45. package/dist/lib/sandbox/commands/create.js +40 -53
  46. package/dist/lib/sandbox/commands/list-running.js +0 -18
  47. package/dist/lib/sandbox/commands/start.js +7 -8
  48. package/dist/lib/sandbox/constants.js +0 -6
  49. package/dist/lib/sandbox/control/client.js +0 -1
  50. package/dist/lib/sandbox/control/container-identity.js +1 -16
  51. package/dist/lib/sandbox/control/executor.js +0 -4
  52. package/dist/lib/sandbox/control/identity-sentinel.js +0 -7
  53. package/dist/lib/sandbox/control/lifecycle.js +21 -37
  54. package/dist/lib/sandbox/control/native-file-lock.js +0 -3
  55. package/dist/lib/sandbox/control/server.js +18 -38
  56. package/dist/lib/sandbox/control/state.js +1 -9
  57. package/dist/lib/sandbox/dockerfile.js +0 -6
  58. package/dist/lib/sandbox/engine.js +0 -14
  59. package/dist/lib/sandbox/engines/docker-exact-discovery.js +1 -4
  60. package/dist/lib/sandbox/engines/wsl2-paths.js +0 -3
  61. package/dist/lib/sandbox/recovery.js +24 -26
  62. package/dist/lib/sandbox/removal.js +11 -519
  63. package/dist/lib/sandbox/task-resolver.js +0 -41
  64. package/dist/lib/sandbox/tools.js +1 -4
  65. package/dist/lib/sandbox/workspace-view.js +1 -2
  66. package/dist/lib/server/display.js +0 -6
  67. package/dist/lib/task/activity-intent.js +45 -40
  68. package/dist/lib/task/activity-log.js +9 -16
  69. package/dist/lib/task/artifact-command.js +1 -1
  70. package/dist/lib/task/artifact-lifecycle.js +61 -63
  71. package/dist/lib/task/artifact-name.js +0 -6
  72. package/dist/lib/task/artifact-operations.js +3 -3
  73. package/dist/lib/task/capabilities.js +4 -5
  74. package/dist/lib/task/commands/log.js +0 -6
  75. package/dist/lib/task/commands/status.js +17 -60
  76. package/dist/lib/task/commit-operation.js +28 -39
  77. package/dist/lib/task/completion-facts.js +56 -43
  78. package/dist/lib/task/control-authority.js +23 -256
  79. package/dist/lib/task/control-recovery.js +3 -5
  80. package/dist/lib/task/create.js +2 -4
  81. package/dist/lib/task/decision-intents.js +8 -9
  82. package/dist/lib/task/delivery-target.js +1 -40
  83. package/dist/lib/task/events.js +84 -107
  84. package/dist/lib/task/finalization.js +44 -8
  85. package/dist/lib/task/ledger-intents.js +1 -1
  86. package/dist/lib/task/lifecycle-execution.js +38 -33
  87. package/dist/lib/task/lifecycle.js +49 -41
  88. package/dist/lib/task/local-artifact-finalization.js +1 -4
  89. package/dist/lib/task/manual-validation-evidence.js +1 -37
  90. package/dist/lib/task/platform-operation-recovery.js +67 -24
  91. package/dist/lib/task/pr-delivery-fact.js +2 -5
  92. package/dist/lib/task/qualification-confirmation.js +5 -7
  93. package/dist/lib/task/resolve-ref.js +1 -25
  94. package/dist/lib/task/review-command.js +2 -2
  95. package/dist/lib/task/review-finalization.js +1 -21
  96. package/dist/lib/task/review-fingerprint.js +0 -4
  97. package/dist/lib/task/secure-fs.js +0 -56
  98. package/dist/lib/task/short-id.js +1 -39
  99. package/dist/lib/task/skill-scope.js +0 -1
  100. package/dist/lib/task/verification-engine.js +58 -282
  101. package/dist/lib/task/verification.js +6 -3
  102. package/dist/lib/task/write.js +11 -1
  103. package/lib/agent-clients/adapter.ts +0 -76
  104. package/lib/agent-clients/adapters/antigravity-cli.ts +0 -5
  105. package/lib/agent-clients/adapters/claude-code-credentials.ts +0 -19
  106. package/lib/agent-clients/adapters/claude-code.ts +0 -9
  107. package/lib/agent-clients/adapters/codex.ts +0 -56
  108. package/lib/agent-clients/adapters/opencode.ts +0 -5
  109. package/lib/agent-clients/adapters/traecli.ts +0 -5
  110. package/lib/agent-clients/config.ts +20 -61
  111. package/lib/agent-clients/registry.ts +1 -34
  112. package/lib/agent-clients/schema.ts +0 -18
  113. package/lib/agent-clients/tokens.ts +1 -2
  114. package/lib/agent-clients/types.ts +0 -15
  115. package/lib/decide.ts +1 -0
  116. package/lib/git/worktree-identity.ts +0 -10
  117. package/lib/internal/agent-client.ts +2 -70
  118. package/lib/internal/cli-route-inventory.ts +1 -4
  119. package/lib/internal/manual-validation.ts +16 -9
  120. package/lib/internal/task-create.ts +0 -1
  121. package/lib/internal/task-event.ts +1 -5
  122. package/lib/internal/task-finalization.ts +0 -1
  123. package/lib/internal/task-lifecycle.ts +3 -5
  124. package/lib/internal/task-operation-registry.ts +3 -19
  125. package/lib/internal/task-review.ts +4 -4
  126. package/lib/platform/comment-safety.ts +0 -10
  127. package/lib/platform/github-provider.ts +0 -2
  128. package/lib/platform/in-label-sync.ts +0 -16
  129. package/lib/platform/issue-comments.ts +0 -21
  130. package/lib/platform/issues.ts +0 -2
  131. package/lib/platform/operation-coordinator.ts +4 -2
  132. package/lib/platform/pr-change-report.ts +0 -5
  133. package/lib/platform/pr-checks.ts +1 -10
  134. package/lib/platform/pr-review.ts +1 -1
  135. package/lib/platform/provider-bridge.ts +0 -1
  136. package/lib/platform/provider-loader.ts +1 -5
  137. package/lib/platform/provider-validation.ts +1 -49
  138. package/lib/platform/pull-requests.ts +64 -99
  139. package/lib/platform/releases.ts +1 -40
  140. package/lib/platform/verification-required.ts +0 -2
  141. package/lib/platform/verification-sync.ts +0 -94
  142. package/lib/process-data/normalize.ts +1 -186
  143. package/lib/process-data/privacy.ts +2 -19
  144. package/lib/process-data/sources.ts +3 -283
  145. package/lib/render.ts +0 -15
  146. package/lib/run/index.ts +13 -7
  147. package/lib/run/skills.ts +0 -4
  148. package/lib/sandbox/commands/create.ts +37 -74
  149. package/lib/sandbox/commands/list-running.ts +0 -19
  150. package/lib/sandbox/commands/start.ts +7 -10
  151. package/lib/sandbox/constants.ts +0 -8
  152. package/lib/sandbox/control/client.ts +0 -1
  153. package/lib/sandbox/control/container-identity.ts +1 -16
  154. package/lib/sandbox/control/executor.ts +1 -5
  155. package/lib/sandbox/control/identity-sentinel.ts +0 -8
  156. package/lib/sandbox/control/lifecycle.ts +18 -51
  157. package/lib/sandbox/control/native-file-lock.ts +0 -8
  158. package/lib/sandbox/control/protocol.ts +0 -2
  159. package/lib/sandbox/control/server.ts +13 -39
  160. package/lib/sandbox/control/state.ts +1 -10
  161. package/lib/sandbox/dockerfile.ts +0 -11
  162. package/lib/sandbox/engine.ts +0 -17
  163. package/lib/sandbox/engines/docker-exact-discovery.ts +1 -5
  164. package/lib/sandbox/engines/wsl2-paths.ts +0 -4
  165. package/lib/sandbox/recovery.ts +24 -26
  166. package/lib/sandbox/removal.ts +11 -696
  167. package/lib/sandbox/shell.ts +1 -1
  168. package/lib/sandbox/task-resolver.ts +0 -48
  169. package/lib/sandbox/tools.ts +1 -5
  170. package/lib/sandbox/workspace-view.ts +2 -4
  171. package/lib/server/display.ts +0 -14
  172. package/lib/server/process-control.ts +1 -1
  173. package/lib/task/activity-intent.ts +36 -44
  174. package/lib/task/activity-log.ts +9 -22
  175. package/lib/task/artifact-command.ts +1 -1
  176. package/lib/task/artifact-lifecycle.ts +58 -67
  177. package/lib/task/artifact-name.ts +0 -7
  178. package/lib/task/artifact-operations.ts +3 -3
  179. package/lib/task/capabilities.ts +5 -6
  180. package/lib/task/commands/log.ts +0 -7
  181. package/lib/task/commands/status.ts +14 -79
  182. package/lib/task/commit-operation.ts +29 -51
  183. package/lib/task/completion-facts.ts +45 -42
  184. package/lib/task/control-authority.ts +27 -317
  185. package/lib/task/control-recovery.ts +2 -5
  186. package/lib/task/create.ts +2 -5
  187. package/lib/task/decision-intents.ts +5 -5
  188. package/lib/task/delivery-target.ts +0 -40
  189. package/lib/task/events.ts +89 -123
  190. package/lib/task/finalization.ts +41 -9
  191. package/lib/task/ledger-intents.ts +1 -1
  192. package/lib/task/lifecycle-execution.ts +37 -89
  193. package/lib/task/lifecycle.ts +48 -44
  194. package/lib/task/local-artifact-finalization.ts +0 -7
  195. package/lib/task/manual-validation-evidence.ts +1 -47
  196. package/lib/task/platform-operation-recovery.ts +50 -26
  197. package/lib/task/pr-delivery-fact.ts +2 -11
  198. package/lib/task/qualification-confirmation.ts +6 -6
  199. package/lib/task/resolve-ref.ts +0 -23
  200. package/lib/task/review-command.ts +2 -2
  201. package/lib/task/review-finalization.ts +1 -27
  202. package/lib/task/review-fingerprint.ts +0 -8
  203. package/lib/task/secure-fs.ts +0 -42
  204. package/lib/task/short-id.ts +0 -43
  205. package/lib/task/skill-scope.ts +0 -1
  206. package/lib/task/verification-engine.ts +56 -287
  207. package/lib/task/verification.ts +5 -5
  208. package/lib/task/write.ts +12 -1
  209. package/package.json +2 -2
  210. package/templates/.agents/README.en.md +1 -3
  211. package/templates/.agents/README.zh-CN.md +0 -2
  212. package/templates/.agents/rules/README.en.md +0 -1
  213. package/templates/.agents/rules/README.zh-CN.md +0 -1
  214. package/templates/.agents/rules/commit-and-pr.en.md +1 -1
  215. package/templates/.agents/rules/commit-and-pr.zh-CN.md +1 -1
  216. package/templates/.agents/rules/no-mid-flow-questions.en.md +0 -10
  217. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +0 -10
  218. package/templates/.agents/rules/pr-sync.en.md +1 -1
  219. package/templates/.agents/rules/pr-sync.zh-CN.md +1 -1
  220. package/templates/.agents/rules/task-management.en.md +3 -3
  221. package/templates/.agents/rules/task-management.zh-CN.md +3 -3
  222. package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -3
  223. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -3
  224. package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
  225. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
  226. package/templates/.agents/skills/code-task/SKILL.en.md +2 -2
  227. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +4 -4
  228. package/templates/.agents/skills/commit/SKILL.en.md +2 -2
  229. package/templates/.agents/skills/commit/SKILL.zh-CN.md +2 -2
  230. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +6 -6
  231. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +6 -6
  232. package/templates/.agents/skills/plan-task/SKILL.en.md +2 -2
  233. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -2
  234. package/templates/.agents/skills/review-analysis/SKILL.en.md +3 -3
  235. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +3 -3
  236. package/templates/.agents/skills/review-code/SKILL.en.md +5 -5
  237. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -5
  238. package/templates/.agents/skills/review-plan/SKILL.en.md +3 -3
  239. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +3 -3
  240. package/templates/.agents/skills/review-pr/SKILL.en.md +4 -4
  241. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +4 -4
  242. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +1 -1
  243. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +1 -1
  244. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +3 -75
  245. package/templates/.agents/templates/task.en.md +1 -0
  246. package/templates/.agents/templates/task.zh-CN.md +1 -0
  247. package/templates/.claude/settings.json +0 -24
  248. package/templates/.codex/hooks.json +0 -50
  249. package/dist/lib/agent-clients/adapters/codex-lifecycle/app-server.js +0 -582
  250. package/dist/lib/agent-clients/adapters/codex-lifecycle/binding.js +0 -114
  251. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +0 -237
  252. package/dist/lib/agent-clients/adapters/codex-lifecycle/cli.js +0 -443
  253. package/dist/lib/agent-clients/adapters/codex-lifecycle/evidence.js +0 -236
  254. package/dist/lib/agent-clients/adapters/codex-lifecycle/recovery.js +0 -292
  255. package/dist/lib/agent-clients/adapters/codex-lifecycle/store.js +0 -267
  256. package/dist/lib/agent-clients/adapters/codex-orchestration.js +0 -466
  257. package/dist/lib/internal/task-orchestration.js +0 -71
  258. package/dist/lib/platform/verification.js +0 -6
  259. package/dist/lib/run/prompt.js +0 -2
  260. package/dist/lib/sandbox/engines/authority.js +0 -289
  261. package/dist/lib/task/delegation-receipts.js +0 -410
  262. package/dist/lib/task/orchestration.js +0 -1233
  263. package/dist/lib/task/verification-checks.js +0 -9
  264. package/dist/lib/task/workspace-snapshot.js +0 -121
  265. package/dist/lifecycle-build-manifest.json +0 -6
  266. package/lib/agent-clients/adapters/codex-lifecycle/app-server.ts +0 -688
  267. package/lib/agent-clients/adapters/codex-lifecycle/binding.ts +0 -147
  268. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +0 -349
  269. package/lib/agent-clients/adapters/codex-lifecycle/cli.ts +0 -506
  270. package/lib/agent-clients/adapters/codex-lifecycle/evidence.ts +0 -385
  271. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +0 -41
  272. package/lib/agent-clients/adapters/codex-lifecycle/recovery.ts +0 -418
  273. package/lib/agent-clients/adapters/codex-lifecycle/store.ts +0 -354
  274. package/lib/agent-clients/adapters/codex-orchestration.ts +0 -630
  275. package/lib/internal/task-orchestration.ts +0 -74
  276. package/lib/platform/verification.ts +0 -11
  277. package/lib/run/prompt.ts +0 -1
  278. package/lib/sandbox/engines/authority.ts +0 -365
  279. package/lib/task/delegation-receipts.ts +0 -547
  280. package/lib/task/orchestration.ts +0 -1651
  281. package/lib/task/verification-checks.ts +0 -10
  282. package/lib/task/workspace-snapshot.ts +0 -144
  283. package/templates/.agents/hooks/lifecycle-delegation.js +0 -140
  284. package/templates/.agents/rules/lifecycle-orchestration.en.md +0 -44
  285. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +0 -44
  286. package/templates/.agents/skills/run-task/SKILL.en.md +0 -29
  287. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +0 -29
  288. package/templates/.agents/skills/run-task/config/verify.en.json +0 -10
  289. package/templates/.agents/skills/run-task/config/verify.zh-CN.json +0 -10
  290. package/templates/.agents/skills/run-task/reference/host-validation.en.md +0 -24
  291. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +0 -24
  292. package/templates/.claude/agents/agent-infra-lifecycle-executor.en.md +0 -6
  293. package/templates/.claude/agents/agent-infra-lifecycle-executor.zh-CN.md +0 -6
  294. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.en.md +0 -6
  295. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.zh-CN.md +0 -6
  296. package/templates/.claude/commands/run-task.en.md +0 -8
  297. package/templates/.claude/commands/run-task.zh-CN.md +0 -8
  298. package/templates/.codex/agents/agent-infra-lifecycle-executor.en.toml +0 -3
  299. package/templates/.codex/agents/agent-infra-lifecycle-executor.zh-CN.toml +0 -3
  300. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.en.toml +0 -3
  301. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.zh-CN.toml +0 -3
  302. package/templates/.opencode/commands/run-task.en.md +0 -11
  303. package/templates/.opencode/commands/run-task.zh-CN.md +0 -11
@@ -1,1233 +0,0 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
- import { createHash, randomUUID } from 'node:crypto';
4
- import { inspectArtifactDirectory } from './artifact-lifecycle.js';
5
- import { parseTypedTaskFrontmatter } from './frontmatter.js';
6
- import { validateCurrentTaskContract } from './current-contract.js';
7
- import { parseReviewSummary } from './review-artifacts.js';
8
- import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, summarizeLedgerStage, validateLedgerRows } from './ledger.js';
9
- import { resolveTaskRef, TASK_ID_RE } from './resolve-ref.js';
10
- import { resolveProjectTaskWorkspaceRoot } from './workspace-root.js';
11
- import { activateDelegation, abortActivatedDelegation, abortPreparedDelegation, completeDelegationStage, consumeDelegation, dispatchDelegation, foldBlankToNull, isDelegationReceipt, managedDelegationRole, prepareDelegation, sealDelegation } from './delegation-receipts.js';
12
- import { normalizeAgentClients } from '../agent-clients/config.js';
13
- import { getAgentClientAdapter, getAgentClientCapability, getAgentClientDelegationEvidence, getAgentClientModelSelection } from '../agent-clients/registry.js';
14
- import { isAgentClientId } from '../agent-clients/types.js';
15
- import { captureRepositorySnapshot, captureWorkspaceSnapshot, diffWorkspaceSnapshots } from './workspace-snapshot.js';
16
- import { assertGitRepositoryBinding } from '../git/worktree-identity.js';
17
- import { extractReviewBaseline, extractReviewDiffBase, extractReviewTargetHead, extractReviewedHead } from './review-fingerprint.js';
18
- import { resolveDeliveryTarget, resolveDiffBase, resolveTargetHead } from './delivery-target.js';
19
- import { buildLifecycleFacts, canStart, recommendNext } from './capabilities.js';
20
- import { hasOpenLifecycleExecution } from './activity-log.js';
21
- import { normalizeAgentToken } from '../agent-clients/tokens.js';
22
- import { resolveArtifactContextForResolvedTask } from './artifact-lifecycle.js';
23
- function supportsLifecycleDelegation(client) {
24
- return getAgentClientCapability(client, 'subagents').level !== 'unsupported'
25
- && getAgentClientCapability(client, 'orchestration').level !== 'unsupported'
26
- && getAgentClientDelegationEvidence(client).actualModel !== 'unavailable'
27
- && getAgentClientDelegationEvidence(client).actualReasoningEffort !== 'unavailable';
28
- }
29
- function orchestrationPath(taskDir) {
30
- return path.join(taskDir, '.runtime', 'orchestration.json');
31
- }
32
- const ORCHESTRATION_STATE_INVALID_MESSAGE = 'orchestration.json does not match the current runtime structure; the file was left unchanged; rebuild the sandbox or manually repair the state before retrying';
33
- class OrchestrationStateError extends Error {
34
- code = 'ORCHESTRATION_STATE_INVALID';
35
- taskId;
36
- reasonCodes;
37
- constructor(taskId = null, reasonCodes = []) {
38
- super(ORCHESTRATION_STATE_INVALID_MESSAGE);
39
- this.name = 'OrchestrationStateError';
40
- this.taskId = taskId;
41
- this.reasonCodes = Object.freeze([...reasonCodes]);
42
- }
43
- }
44
- function isRecord(value) {
45
- return typeof value === 'object' && value !== null && !Array.isArray(value);
46
- }
47
- function hasExactKeys(value, keys) {
48
- if (!isRecord(value))
49
- return false;
50
- const actual = Object.keys(value);
51
- return actual.length === keys.length && keys.every((key) => Object.prototype.hasOwnProperty.call(value, key));
52
- }
53
- function exactText(value) {
54
- return typeof value === 'string' && value.length > 0 && value.trim() === value;
55
- }
56
- function nullableText(value) {
57
- return value === null || exactText(value);
58
- }
59
- function isPause(value) {
60
- return hasExactKeys(value, ['code', 'message', 'recoverable'])
61
- && exactText(value.code)
62
- && exactText(value.message)
63
- && typeof value.recoverable === 'boolean';
64
- }
65
- function isModelPolicySource(value) {
66
- return hasExactKeys(value, ['kind', 'client', 'resolvedAt'])
67
- && ['explicit', 'project-config'].includes(value.kind)
68
- && isAgentClientId(value.client)
69
- && exactText(value.resolvedAt);
70
- }
71
- function isModelPolicy(value) {
72
- if (!hasExactKeys(value, ['executor', 'reviewer']))
73
- return false;
74
- return [value.executor, value.reviewer].every((role) => hasExactKeys(role, ['model', 'reasoningEffort'])
75
- && exactText(role.model)
76
- && exactText(role.reasoningEffort));
77
- }
78
- const RECOVERY_GUARD_KEYS = [
79
- 'stepCount', 'nextStage', 'baselineEmpty', 'receiptCount', 'pendingDelegation',
80
- 'completionEvidenceAbsent'
81
- ];
82
- function isRecovery(value) {
83
- if (!hasExactKeys(value, [
84
- 'code', 'recoveredAt', 'previousStatus', 'previousPause', 'client', 'guards', 'resultingStatus'
85
- ]))
86
- return false;
87
- const guards = value.guards;
88
- return value.code === 'CLIENT_CAPABILITY_ENABLED'
89
- && exactText(value.recoveredAt)
90
- && value.previousStatus === 'paused'
91
- && isPause(value.previousPause)
92
- && value.previousPause.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED'
93
- && isAgentClientId(value.client)
94
- && hasExactKeys(guards, RECOVERY_GUARD_KEYS)
95
- && guards.stepCount === 0
96
- && guards.nextStage === null
97
- && guards.baselineEmpty === true
98
- && guards.receiptCount === 0
99
- && guards.pendingDelegation === false
100
- && guards.completionEvidenceAbsent === true
101
- && value.resultingStatus === 'running';
102
- }
103
- export function isCompletionEvidence(value) {
104
- return hasExactKeys(value, [
105
- 'kind', 'observedAt', 'head', 'headTree', 'worktreeTree', 'lastReviewedCommit', 'prNumber', 'prHead'
106
- ])
107
- && value.kind === 'reviewed-head-clean'
108
- && exactText(value.observedAt)
109
- && exactText(value.head)
110
- && exactText(value.headTree)
111
- && exactText(value.worktreeTree)
112
- && exactText(value.lastReviewedCommit)
113
- && (value.prNumber === null || (Number.isSafeInteger(value.prNumber) && value.prNumber > 0))
114
- && (value.prHead === null || exactText(value.prHead));
115
- }
116
- const ORCHESTRATION_RUN_KEYS = [
117
- 'taskId', 'runId', 'status', 'nextStage', 'stepCount', 'maxSteps', 'modelPolicy',
118
- 'modelPolicySource', 'recoveryHistory', 'baseline', 'pendingDelegation', 'receipts',
119
- 'pause', 'completionEvidence', 'createdAt', 'updatedAt'
120
- ];
121
- function orchestrationStateReasonCodes(value) {
122
- if (!hasExactKeys(value, ORCHESTRATION_RUN_KEYS))
123
- return ['top-level-keys'];
124
- const record = value;
125
- const reasons = [];
126
- if (!exactText(record.taskId))
127
- reasons.push('taskId');
128
- if (!exactText(record.runId))
129
- reasons.push('runId');
130
- if (!['running', 'paused', 'completed'].includes(record.status))
131
- reasons.push('status');
132
- if (!(record.nextStage === null || ['analysis', 'review-analysis', 'plan', 'review-plan', 'code', 'review-code', 'commit'].includes(record.nextStage))) {
133
- reasons.push('nextStage');
134
- }
135
- if (!Number.isSafeInteger(record.stepCount) || record.stepCount < 0)
136
- reasons.push('stepCount');
137
- if (!Number.isSafeInteger(record.maxSteps) || record.maxSteps < 1)
138
- reasons.push('maxSteps');
139
- if (!isModelPolicy(record.modelPolicy))
140
- reasons.push('modelPolicy');
141
- if (!isModelPolicySource(record.modelPolicySource))
142
- reasons.push('modelPolicySource');
143
- if (!Array.isArray(record.recoveryHistory) || !record.recoveryHistory.every(isRecovery))
144
- reasons.push('recoveryHistory');
145
- if (typeof record.baseline !== 'string')
146
- reasons.push('baseline');
147
- if (!(record.pendingDelegation === null || isDelegationReceipt(record.pendingDelegation)))
148
- reasons.push('pendingDelegation');
149
- if (!Array.isArray(record.receipts) || !record.receipts.every(isDelegationReceipt))
150
- reasons.push('receipts');
151
- if (!(record.pause === null || isPause(record.pause)))
152
- reasons.push('pause');
153
- if (!(record.completionEvidence === null || isCompletionEvidence(record.completionEvidence)))
154
- reasons.push('completionEvidence');
155
- if (!exactText(record.createdAt))
156
- reasons.push('createdAt');
157
- if (!exactText(record.updatedAt))
158
- reasons.push('updatedAt');
159
- if (Array.isArray(record.receipts) && record.receipts.every(isDelegationReceipt)
160
- && (record.pendingDelegation === null || isDelegationReceipt(record.pendingDelegation))) {
161
- const receipts = [...record.receipts, ...(record.pendingDelegation ? [record.pendingDelegation] : [])];
162
- if (receipts.some((receipt) => receipt.runId !== record.runId)) {
163
- reasons.push('receipt-identity');
164
- }
165
- }
166
- return reasons;
167
- }
168
- function emitDiagnostic(options, event, fields) {
169
- try {
170
- options?.diagnosticLog?.(event, fields);
171
- }
172
- catch {
173
- // Diagnostics must never change lifecycle behavior.
174
- }
175
- }
176
- function fileObservation(file) {
177
- try {
178
- const stat = fs.lstatSync(file);
179
- let realpath = null;
180
- try {
181
- realpath = fs.realpathSync.native(file);
182
- }
183
- catch { /* retain the lstat observation */ }
184
- return {
185
- file,
186
- exists: true,
187
- fileType: stat.isFile() ? 'file' : stat.isDirectory() ? 'directory' : 'other',
188
- symbolicLink: stat.isSymbolicLink(),
189
- bytes: stat.isFile() ? stat.size : null,
190
- mtimeMs: stat.mtimeMs,
191
- device: stat.dev,
192
- inode: stat.ino,
193
- realpath
194
- };
195
- }
196
- catch (error) {
197
- return {
198
- file,
199
- exists: false,
200
- fileType: null,
201
- symbolicLink: null,
202
- bytes: null,
203
- mtimeMs: null,
204
- device: null,
205
- inode: null,
206
- realpath: null,
207
- fsError: error instanceof Error && 'code' in error ? String(error.code) : null
208
- };
209
- }
210
- }
211
- function parseOrchestrationRun(value, expectedTaskId) {
212
- const reasonCodes = orchestrationStateReasonCodes(value);
213
- if (reasonCodes.length > 0)
214
- throw new OrchestrationStateError(expectedTaskId ?? null, reasonCodes);
215
- return value;
216
- }
217
- function readRun(taskDir, options = {}) {
218
- const file = orchestrationPath(taskDir);
219
- const taskId = path.basename(taskDir);
220
- const observed = options.diagnosticLog ? fileObservation(file) : {};
221
- if (!fs.existsSync(file)) {
222
- emitDiagnostic(options, 'orchestration-state-missing', { taskId, taskDir, ...observed });
223
- return null;
224
- }
225
- emitDiagnostic(options, 'orchestration-state-read-start', { taskId, taskDir, ...observed });
226
- let rawBytes = null;
227
- let rawSha256 = null;
228
- try {
229
- const raw = fs.readFileSync(file, 'utf8');
230
- if (options.diagnosticLog) {
231
- rawBytes = Buffer.byteLength(raw, 'utf8');
232
- rawSha256 = createHash('sha256').update(raw, 'utf8').digest('hex');
233
- }
234
- const run = parseOrchestrationRun(JSON.parse(raw), taskId);
235
- emitDiagnostic(options, 'orchestration-state-read-ok', {
236
- taskId, taskDir, file, rawBytes, rawSha256, status: run.status, runId: run.runId,
237
- stepCount: run.stepCount, pendingDelegation: run.pendingDelegation !== null
238
- });
239
- return run;
240
- }
241
- catch (error) {
242
- const reasonCodes = error instanceof OrchestrationStateError && error.reasonCodes.length > 0
243
- ? error.reasonCodes
244
- : [error instanceof SyntaxError ? 'json-parse' : 'file-read'];
245
- emitDiagnostic(options, 'orchestration-state-read-failed', {
246
- taskId,
247
- taskDir,
248
- ...observed,
249
- rawBytes,
250
- rawSha256,
251
- reasonCodes: reasonCodes.join(','),
252
- errorType: error instanceof Error ? error.name : typeof error,
253
- });
254
- throw new OrchestrationStateError(taskId, reasonCodes);
255
- }
256
- }
257
- function findDelegation(run, receiptId) {
258
- if (run.pendingDelegation?.id === receiptId)
259
- return { receipt: run.pendingDelegation, location: 'pending' };
260
- const receipt = run.receipts.find((entry) => entry.id === receiptId);
261
- return receipt ? { receipt, location: 'history' } : null;
262
- }
263
- function readDelegationAdapterEvidence(taskRef, receiptId, adapterId, options = {}) {
264
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
265
- if (!resolved.ok)
266
- throw new Error(`${resolved.code}: ${resolved.message}`);
267
- const run = readRun(resolved.taskDir, options);
268
- const found = run && findDelegation(run, receiptId);
269
- if (!found || found.receipt.client !== adapterId)
270
- return null;
271
- return found.receipt.adapterEvidence?.[adapterId] ?? null;
272
- }
273
- function mutateDelegationAdapterEvidence(taskRef, receiptId, adapterId, mutate, options = {}) {
274
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
275
- if (!resolved.ok)
276
- throw new Error(`${resolved.code}: ${resolved.message}`);
277
- return mutateDelegationAdapterEvidenceAtResolved(resolved, receiptId, adapterId, mutate, options);
278
- }
279
- function mutateDelegationAdapterEvidenceAtResolved(resolved, receiptId, adapterId, mutate, options) {
280
- const update = () => {
281
- try {
282
- const run = readRun(resolved.taskDir, options);
283
- const found = run && findDelegation(run, receiptId);
284
- if (!run || !found || found.receipt.client !== adapterId) {
285
- throw new Error('ORCHESTRATION_DELEGATION_MISSING: adapter evidence has no matching task receipt');
286
- }
287
- const current = found.receipt.adapterEvidence?.[adapterId] ?? null;
288
- const nextState = mutate(current);
289
- const serialized = JSON.stringify(nextState);
290
- if (serialized === undefined)
291
- throw new Error('Agent adapter evidence must be JSON serializable');
292
- if (serialized === JSON.stringify(current))
293
- return JSON.parse(serialized);
294
- const adapterEvidence = Object.freeze({
295
- ...(found.receipt.adapterEvidence ?? {}),
296
- [adapterId]: JSON.parse(serialized)
297
- });
298
- const nextReceipt = Object.freeze({ ...found.receipt, adapterEvidence });
299
- const updated = found.location === 'pending'
300
- ? withUpdatedRun(run, { pendingDelegation: nextReceipt }, options.now)
301
- : withUpdatedRun(run, {
302
- receipts: Object.freeze(run.receipts.map((receipt) => receipt.id === receiptId ? nextReceipt : receipt))
303
- }, options.now);
304
- saveRun(resolved.taskDir, updated);
305
- return JSON.parse(serialized);
306
- }
307
- catch (error) {
308
- mutationError = error;
309
- return null;
310
- }
311
- };
312
- let mutationError;
313
- const result = update();
314
- if (mutationError)
315
- throw mutationError;
316
- return result;
317
- }
318
- function atomicWrite(file, value) {
319
- fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
320
- const temp = `${file}.${process.pid}.${randomUUID()}.tmp`;
321
- fs.writeFileSync(temp, `${JSON.stringify(value, null, 2)}\n`, { encoding: 'utf8', flag: 'wx' });
322
- fs.renameSync(temp, file);
323
- }
324
- function saveRun(taskDir, run) {
325
- atomicWrite(orchestrationPath(taskDir), run);
326
- }
327
- function withUpdatedRun(run, updates, now) {
328
- return Object.freeze({ ...run, ...updates, updatedAt: (now ?? (() => new Date().toISOString()))() });
329
- }
330
- function failed(code, message, taskId = null, details = {}) {
331
- return { status: 'failed', changed: false, taskId, run: null, next: null, error: { code, message, ...details } };
332
- }
333
- function gitRootFor(stateRoot, options) {
334
- if (!options.gitWorktreeRoot)
335
- return stateRoot;
336
- return assertGitRepositoryBinding(stateRoot, options.gitWorktreeRoot).worktreeRoot;
337
- }
338
- function validModel(value) {
339
- return typeof value === 'string' && value.length > 0 && value.trim() === value;
340
- }
341
- function validateModelPolicy(policy) {
342
- if (!policy
343
- || !validModel(policy.executor?.model)
344
- || !validModel(policy.executor?.reasoningEffort)
345
- || !validModel(policy.reviewer?.model)
346
- || !validModel(policy.reviewer?.reasoningEffort)) {
347
- return { code: 'ORCHESTRATION_MODEL_POLICY_REQUIRED', message: 'executor and reviewer model and reasoning effort are required' };
348
- }
349
- return null;
350
- }
351
- function sameModelPolicy(left, right) {
352
- return left.executor.model === right.executor.model
353
- && left.executor.reasoningEffort === right.executor.reasoningEffort
354
- && left.reviewer.model === right.reviewer.model
355
- && left.reviewer.reasoningEffort === right.reviewer.reasoningEffort;
356
- }
357
- function rolePolicy(run, role) {
358
- return run.modelPolicy[role];
359
- }
360
- function resolveProjectPolicy(repoRoot, client) {
361
- const configPath = path.join(repoRoot, '.agents', '.airc.json');
362
- if (!fs.existsSync(configPath))
363
- return undefined;
364
- const raw = JSON.parse(fs.readFileSync(configPath, 'utf8'));
365
- return normalizeAgentClients(raw).state[client].orchestration;
366
- }
367
- function canRecoverUnsupportedClientPause(run) {
368
- return run.status === 'paused'
369
- && run.pause?.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED'
370
- && isAgentClientId(run.modelPolicySource?.client)
371
- && supportsLifecycleDelegation(run.modelPolicySource.client)
372
- && run.stepCount === 0
373
- && run.nextStage === null
374
- && run.baseline === ''
375
- && run.pendingDelegation === null
376
- && run.receipts.length === 0
377
- && run.completionEvidence == null
378
- && run.recoveryHistory.length === 0;
379
- }
380
- function beginOrResumeOrchestration(taskRef, options = {}) {
381
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
382
- if (!resolved.ok)
383
- return failed(resolved.code, resolved.message, resolved.taskId);
384
- if (!isAgentClientId(options.client)) {
385
- return failed('ORCHESTRATION_PAYLOAD_INVALID', 'begin-or-resume requires a known client', resolved.taskId);
386
- }
387
- let existing;
388
- try {
389
- existing = readRun(resolved.taskDir, options);
390
- }
391
- catch (error) {
392
- return failed('ORCHESTRATION_STATE_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
393
- }
394
- if (existing) {
395
- if (existing.status === 'completed') {
396
- return { status: 'completed', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
397
- }
398
- if (existing.modelPolicySource.client !== options.client) {
399
- return failed('ORCHESTRATION_CLIENT_MISMATCH', 'provided client does not match the persisted run policy source', resolved.taskId);
400
- }
401
- if (options.modelPolicy) {
402
- const suppliedError = validateModelPolicy(options.modelPolicy);
403
- if (suppliedError)
404
- return failed(suppliedError.code, suppliedError.message, resolved.taskId);
405
- if (!sameModelPolicy(existing.modelPolicy, options.modelPolicy)) {
406
- return failed('ORCHESTRATION_MODEL_POLICY_MISMATCH', 'provided model policy does not match the persisted run policy', resolved.taskId);
407
- }
408
- }
409
- if (existing.status === 'paused' && existing.pause?.code === 'ORCHESTRATION_CLIENT_UNSUPPORTED') {
410
- if (!canRecoverUnsupportedClientPause(existing)) {
411
- return { status: 'paused', changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
412
- }
413
- const now = (options.now ?? (() => new Date().toISOString()))();
414
- const recovery = {
415
- code: 'CLIENT_CAPABILITY_ENABLED',
416
- recoveredAt: now,
417
- previousStatus: 'paused',
418
- previousPause: existing.pause,
419
- client: existing.modelPolicySource.client,
420
- guards: {
421
- stepCount: 0,
422
- nextStage: null,
423
- baselineEmpty: true,
424
- receiptCount: 0,
425
- pendingDelegation: false,
426
- completionEvidenceAbsent: true
427
- },
428
- resultingStatus: 'running'
429
- };
430
- const resumed = withUpdatedRun(existing, {
431
- status: 'running',
432
- pause: null,
433
- recoveryHistory: Object.freeze([...existing.recoveryHistory, recovery])
434
- }, () => now);
435
- saveRun(resolved.taskDir, resumed);
436
- return { status: 'running', changed: true, taskId: resolved.taskId, run: resumed, next: null, error: null };
437
- }
438
- if (existing.status === 'paused' && existing.pause?.recoverable && existing.pendingDelegation === null) {
439
- const resumed = withUpdatedRun(existing, { status: 'running', pause: null }, options.now);
440
- saveRun(resolved.taskDir, resumed);
441
- return { status: 'running', changed: true, taskId: resolved.taskId, run: resumed, next: null, error: null };
442
- }
443
- return { status: existing.status, changed: false, taskId: resolved.taskId, run: existing, next: null, error: null };
444
- }
445
- let policy = options.modelPolicy;
446
- let sourceKind = 'explicit';
447
- if (!policy) {
448
- try {
449
- policy = resolveProjectPolicy(resolved.repoRoot, options.client);
450
- sourceKind = 'project-config';
451
- }
452
- catch (error) {
453
- return failed('ORCHESTRATION_CONFIG_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
454
- }
455
- }
456
- const policyError = validateModelPolicy(policy);
457
- if (policyError)
458
- return failed(policyError.code, policyError.message, resolved.taskId, {
459
- client: options.client,
460
- missingFields: ['executor.model', 'executor.reasoningEffort', 'reviewer.model', 'reviewer.reasoningEffort'],
461
- modelSelectionContext: getAgentClientModelSelection(options.client)
462
- });
463
- const now = (options.now ?? (() => new Date().toISOString()))();
464
- const source = { kind: sourceKind, client: options.client, resolvedAt: now };
465
- const run = {
466
- taskId: resolved.taskId,
467
- runId: (options.id ?? randomUUID)(),
468
- status: 'running',
469
- nextStage: null,
470
- stepCount: 0,
471
- maxSteps: options.maxSteps ?? 24,
472
- modelPolicy: policy,
473
- modelPolicySource: source,
474
- recoveryHistory: [],
475
- baseline: '',
476
- pendingDelegation: null,
477
- receipts: [],
478
- pause: null,
479
- completionEvidence: null,
480
- createdAt: now,
481
- updatedAt: now
482
- };
483
- atomicWrite(orchestrationPath(resolved.taskDir), run);
484
- return { status: 'running', changed: true, taskId: resolved.taskId, run, next: null, error: null };
485
- }
486
- function validateSavedCurrentDiffBase(repoRoot, metadata, taskDir, reviewedHead) {
487
- const reviewInventory = inspectArtifactDirectory(taskDir, 'review-code');
488
- if (reviewInventory.status !== 'ready' || !reviewInventory.latest) {
489
- return { code: 'ORCHESTRATION_REVIEW_INVALID', message: 'latest completed review-code artifact is unavailable' };
490
- }
491
- const reviewContent = fs.readFileSync(reviewInventory.latest.path, 'utf8');
492
- const savedTargetHead = extractReviewTargetHead(reviewContent);
493
- const savedDiffBase = extractReviewDiffBase(reviewContent);
494
- const savedReviewedHead = extractReviewedHead(reviewContent) || extractReviewBaseline(reviewContent);
495
- if (!savedTargetHead && !savedDiffBase && !savedReviewedHead)
496
- return null;
497
- if (savedReviewedHead && savedReviewedHead !== reviewedHead) {
498
- return { code: 'ORCHESTRATION_REVIEWED_HEAD_MISMATCH', message: 'review-code saved reviewed head does not match last_reviewed_commit' };
499
- }
500
- if (!savedTargetHead || !savedDiffBase) {
501
- return { code: 'ORCHESTRATION_REVIEW_TARGET_INVALID', message: 'review-code must save target head and diff base together' };
502
- }
503
- const savedBase = resolveDiffBase(repoRoot, reviewedHead, savedTargetHead);
504
- if (!savedBase.ok)
505
- return { code: 'ORCHESTRATION_REVIEW_TARGET_INVALID', message: savedBase.message };
506
- if (savedBase.diffBase !== savedDiffBase) {
507
- return { code: 'ORCHESTRATION_REVIEW_DIFF_BASE_MISMATCH', message: 'saved review diff base does not match reviewed head and saved target head' };
508
- }
509
- const target = resolveDeliveryTarget(repoRoot, {
510
- remote: typeof metadata.delivery_remote === 'string' ? metadata.delivery_remote : undefined,
511
- baseRef: typeof metadata.delivery_base_ref === 'string' ? metadata.delivery_base_ref : undefined
512
- });
513
- if (!target.ok)
514
- return { code: target.code, message: target.message };
515
- const currentTarget = resolveTargetHead(repoRoot, target.value);
516
- if (!currentTarget.ok)
517
- return { code: currentTarget.code, message: currentTarget.message };
518
- const currentBase = resolveDiffBase(repoRoot, reviewedHead, currentTarget.head);
519
- if (!currentBase.ok)
520
- return { code: currentBase.code, message: currentBase.message };
521
- if (currentBase.diffBase !== savedDiffBase) {
522
- return { code: 'ORCHESTRATION_REVIEW_TARGET_CHANGED', message: 'current delivery target changed the reviewed diff base; re-run review-code' };
523
- }
524
- return null;
525
- }
526
- function routeFromFacts(facts) {
527
- const recommendation = recommendNext(facts);
528
- if (!recommendation.action)
529
- return recommendation.reasonCode === 'LIFECYCLE_REVIEWED' ? { completion: true } : null;
530
- const action = recommendation.action;
531
- if (action === 'manual-validation' || action === 'validation-run')
532
- return null;
533
- const command = action === 'analysis' ? 'analyze-task' : action === 'plan' ? 'plan-task' : action === 'code' ? 'code-task' : action;
534
- const role = action.startsWith('review-') ? 'reviewer' : 'executor';
535
- return { action: command, role, stage: action };
536
- }
537
- function routeResolvedTask(taskRef, resolved, options) {
538
- let gitRoot;
539
- try {
540
- gitRoot = gitRootFor(resolved.repoRoot, options);
541
- }
542
- catch (error) {
543
- return failed('ORCHESTRATION_WORKTREE_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
544
- }
545
- let content;
546
- try {
547
- content = fs.readFileSync(resolved.taskMdPath, 'utf8');
548
- }
549
- catch (error) {
550
- return failed('ORCHESTRATION_TASK_READ_FAILED', String(error), resolved.taskId);
551
- }
552
- let metadata;
553
- let currentLedger = null;
554
- if (resolved.state === 'active') {
555
- const contract = validateCurrentTaskContract(content);
556
- if (!contract.ok)
557
- return failed('ORCHESTRATION_CURRENT_CONTRACT_INVALID', contract.message, resolved.taskId);
558
- metadata = contract.metadata;
559
- currentLedger = contract.ledger;
560
- }
561
- else {
562
- try {
563
- metadata = parseTypedTaskFrontmatter(content);
564
- }
565
- catch (error) {
566
- return failed('ORCHESTRATION_TASK_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
567
- }
568
- }
569
- let run;
570
- try {
571
- run = readRun(resolved.taskDir, options);
572
- }
573
- catch (error) {
574
- return failed('ORCHESTRATION_STATE_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
575
- }
576
- const facts = buildLifecycleFacts(resolved.taskDir, content, resolved.state, hasOpenLifecycleExecution(content) || Boolean(run?.pendingDelegation), resolved.repoRoot);
577
- if (!facts.ok)
578
- return failed('ORCHESTRATION_CAPABILITY_FACTS_INVALID', facts.message, resolved.taskId);
579
- const routed = routeFromFacts(facts.facts);
580
- if (!routed)
581
- return failed('ORCHESTRATION_ROUTE_UNKNOWN', 'cannot determine a unique lifecycle action', resolved.taskId);
582
- if ('completion' in routed) {
583
- if (facts.facts.executionBusy && !run?.pendingDelegation) {
584
- return failed('ORCHESTRATION_EXECUTION_BUSY', 'clean completion requires no open lifecycle execution', resolved.taskId);
585
- }
586
- const reviewInventory = inspectArtifactDirectory(resolved.taskDir, 'review-code');
587
- if (reviewInventory.status !== 'ready' || !reviewInventory.latest) {
588
- return failed('ORCHESTRATION_REVIEW_INVALID', 'latest completed review-code artifact is unavailable', resolved.taskId);
589
- }
590
- const review = parseReviewSummary(fs.readFileSync(reviewInventory.latest.path, 'utf8'));
591
- if (!review.ok || review.summary.manualValidation === null) {
592
- return failed('ORCHESTRATION_REVIEW_INVALID', 'latest code review has no numeric manual-validation count', resolved.taskId);
593
- }
594
- const ledger = currentLedger ?? parseLedgerDocument(content);
595
- if (!ledger.present)
596
- return failed('ORCHESTRATION_CURRENT_CONTRACT_INVALID', `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}`, resolved.taskId);
597
- const rows = ledger.rows;
598
- const ledgerError = validateLedgerRows(rows);
599
- if (ledgerError)
600
- return failed('ORCHESTRATION_LEDGER_INVALID', ledgerError.message, resolved.taskId);
601
- if (!summarizeLedgerStage(rows, 'code').canAdvance) {
602
- return failed('ORCHESTRATION_LEDGER_BLOCKED', 'code review ledger has unresolved findings or human decisions', resolved.taskId);
603
- }
604
- if (run?.status === 'completed') {
605
- return { status: 'completed', changed: false, taskId: resolved.taskId, run, next: null, error: null };
606
- }
607
- if (!run) {
608
- return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
609
- }
610
- if (run.status !== 'running' || run.pendingDelegation !== null) {
611
- return failed('ORCHESTRATION_RUN_NOT_RUNNING', 'clean completion requires an idle running orchestration', resolved.taskId);
612
- }
613
- const capture = options.captureRepository ?? captureRepositorySnapshot;
614
- let before;
615
- try {
616
- before = capture(gitRoot);
617
- }
618
- catch (error) {
619
- return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
620
- }
621
- if (before.headTree !== before.worktreeTree) {
622
- return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
623
- }
624
- const lastReviewedCommit = String(metadata.last_reviewed_commit ?? '');
625
- if (before.head !== lastReviewedCommit) {
626
- return failed('ORCHESTRATION_REVIEWED_HEAD_MISMATCH', 'local HEAD does not match last_reviewed_commit', resolved.taskId);
627
- }
628
- const diffBaseError = validateSavedCurrentDiffBase(resolved.repoRoot, metadata, resolved.taskDir, lastReviewedCommit);
629
- if (diffBaseError)
630
- return failed(diffBaseError.code, diffBaseError.message, resolved.taskId);
631
- let after;
632
- try {
633
- after = capture(gitRoot);
634
- }
635
- catch (error) {
636
- return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
637
- }
638
- if (after.headTree !== after.worktreeTree) {
639
- return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
640
- }
641
- if (after.head !== before.head || after.headTree !== before.headTree) {
642
- return failed('ORCHESTRATION_REPOSITORY_CHANGED', 'repository changed while clean completion evidence was collected', resolved.taskId);
643
- }
644
- const observedAt = (options.now ?? (() => new Date().toISOString()))();
645
- const completionEvidence = {
646
- kind: 'reviewed-head-clean',
647
- observedAt,
648
- head: after.head,
649
- headTree: after.headTree,
650
- worktreeTree: after.worktreeTree,
651
- lastReviewedCommit,
652
- prNumber: null,
653
- prHead: null
654
- };
655
- const completed = withUpdatedRun(run, {
656
- status: 'completed',
657
- nextStage: null,
658
- pause: null,
659
- completionEvidence
660
- });
661
- saveRun(resolved.taskDir, completed);
662
- return { status: 'completed', changed: true, taskId: resolved.taskId, run: completed, next: null, error: null };
663
- }
664
- const action = routed.stage;
665
- const family = action === 'analysis' ? 'analysis' : action;
666
- const artifactContext = resolveArtifactContextForResolvedTask(resolved, family, {
667
- repoRoot: resolved.repoRoot
668
- });
669
- if (artifactContext.status !== 'ready' || !artifactContext.selection) {
670
- return failed(artifactContext.error?.code ?? 'ORCHESTRATION_ARTIFACT_SELECTION_FAILED', artifactContext.error?.message ?? 'cannot determine artifact selection', resolved.taskId);
671
- }
672
- if (artifactContext.selection.disposition === 'reuse') {
673
- return failed('ORCHESTRATION_REUSE_STALLED', `lifecycle still routes to ${action} after reusing ${artifactContext.selection.artifact.name}`, resolved.taskId);
674
- }
675
- const selected = artifactContext.selection.artifact;
676
- const capability = canStart(action, facts.facts, {
677
- initiator: 'orchestrator', requestId: `orchestration:${resolved.taskId}:${routed.stage}:${selected.round}`,
678
- requestedAction: action, reasonCode: 'user-request', explicitRequest: true
679
- });
680
- if (!capability.allowed)
681
- return failed('ORCHESTRATION_CAPABILITY_DENIED', `${capability.reasonCode}: ${capability.evidence.join(', ')}`, resolved.taskId);
682
- const policy = run ? rolePolicy(run, routed.role) : null;
683
- const next = {
684
- ...routed,
685
- round: selected.round,
686
- artifact: selected.name,
687
- requestedModel: policy?.model ?? null,
688
- requestedReasoningEffort: policy?.reasoningEffort ?? null
689
- };
690
- return { status: 'running', changed: false, taskId: resolved.taskId, run, next, error: null };
691
- }
692
- function routeOrchestration(taskRef, options = {}) {
693
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
694
- if (!resolved.ok)
695
- return failed(resolved.code, resolved.message, resolved.taskId);
696
- return routeResolvedTask(taskRef, resolved, options);
697
- }
698
- function statusOrchestration(taskRef, options = {}) {
699
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
700
- if (!resolved.ok)
701
- return failed(resolved.code, resolved.message, resolved.taskId);
702
- const run = readRun(resolved.taskDir, options);
703
- if (!run)
704
- return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
705
- return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
706
- }
707
- function validatePreparedModelPolicy(taskId, run, next, input) {
708
- if (!validModel(input.requestedModel)) {
709
- return failed('ORCHESTRATION_REQUESTED_MODEL_REQUIRED', 'prepare requires the exact requested model identity', taskId);
710
- }
711
- if (!validModel(input.requestedReasoningEffort)) {
712
- return failed('ORCHESTRATION_REQUESTED_REASONING_EFFORT_REQUIRED', 'prepare requires the exact requested reasoning effort', taskId);
713
- }
714
- const expectedPolicy = run.modelPolicy[next.role];
715
- if (input.requestedModel !== expectedPolicy.model) {
716
- return failed('ORCHESTRATION_REQUESTED_MODEL_MISMATCH', `requested model does not match the persisted ${next.role} model`, taskId);
717
- }
718
- if (input.requestedReasoningEffort !== expectedPolicy.reasoningEffort) {
719
- return failed('ORCHESTRATION_REQUESTED_REASONING_EFFORT_MISMATCH', `requested reasoning effort does not match the persisted ${next.role} policy`, taskId);
720
- }
721
- return null;
722
- }
723
- function captureTaskSnapshot(taskId, repoRoot, options) {
724
- return (options.captureWorkspace ?? captureWorkspaceSnapshot)({
725
- gitRoot: gitRootFor(repoRoot, options),
726
- stateRoot: repoRoot,
727
- taskId
728
- });
729
- }
730
- function validateDelegationRun(taskRef, taskId, run, options) {
731
- if (!run || run.status !== 'running') {
732
- return failed('ORCHESTRATION_RUN_NOT_RUNNING', 'a running orchestration is required', taskId);
733
- }
734
- if (run.pendingDelegation)
735
- return failed('ORCHESTRATION_DELEGATION_BUSY', 'the run already has a pending delegation', taskId);
736
- if (run.stepCount >= run.maxSteps) {
737
- return pauseOrchestration(taskRef, 'ORCHESTRATION_MAX_STEPS', 'maximum orchestration steps reached', true, options);
738
- }
739
- return null;
740
- }
741
- function prepareOrchestrationDelegation(taskRef, input, options = {}) {
742
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
743
- if (!resolved.ok)
744
- return failed(resolved.code, resolved.message, resolved.taskId);
745
- if (!(options.supportsLifecycleDelegation ?? supportsLifecycleDelegation)(input.client)) {
746
- return failed('ORCHESTRATION_CLIENT_UNSUPPORTED', `client '${input.client}' does not support lifecycle orchestration`, resolved.taskId);
747
- }
748
- emitDiagnostic(options, 'orchestration-prepare-start', {
749
- taskId: resolved.taskId,
750
- taskDir: resolved.taskDir,
751
- repoRoot: resolved.repoRoot,
752
- client: input.client,
753
- requestedModel: input.requestedModel ?? null,
754
- requestedReasoningEffort: input.requestedReasoningEffort ?? null
755
- });
756
- const run = readRun(resolved.taskDir, options);
757
- const runFailure = validateDelegationRun(taskRef, resolved.taskId, run, options);
758
- if (runFailure)
759
- return runFailure;
760
- const activeRun = run;
761
- const routed = routeResolvedTask(taskRef, resolved, options);
762
- if (!routed.next)
763
- return routed;
764
- const next = routed.next;
765
- const policyFailure = validatePreparedModelPolicy(resolved.taskId, activeRun, next, input);
766
- if (policyFailure)
767
- return policyFailure;
768
- let beforeFingerprint;
769
- try {
770
- beforeFingerprint = captureTaskSnapshot(resolved.taskId, resolved.repoRoot, options);
771
- }
772
- catch (error) {
773
- return failed('ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
774
- }
775
- const receipt = prepareDelegation({
776
- taskId: resolved.taskId,
777
- runId: activeRun.runId,
778
- role: next.role,
779
- stage: next.stage,
780
- round: next.round,
781
- artifact: next.artifact,
782
- client: input.client,
783
- requestedModel: input.requestedModel,
784
- requestedReasoningEffort: input.requestedReasoningEffort,
785
- workspaceSnapshotScope: 'task',
786
- beforeFingerprint
787
- }, { id: options.id, now: options.now, monotonicNow: options.monotonicNow });
788
- const updated = withUpdatedRun(activeRun, {
789
- nextStage: next.stage,
790
- pendingDelegation: receipt
791
- });
792
- saveRun(resolved.taskDir, updated);
793
- return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next, error: null };
794
- }
795
- function dispatchOrchestrationDelegation(taskRef, options = {}) {
796
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
797
- if (!resolved.ok)
798
- return failed(resolved.code, resolved.message, resolved.taskId);
799
- const run = readRun(resolved.taskDir, options);
800
- if (!run?.pendingDelegation)
801
- return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
802
- if (run.status !== 'running') {
803
- return failed('ORCHESTRATION_STATE_INVALID', 'spawn dispatch requires a running orchestration', resolved.taskId);
804
- }
805
- const result = dispatchDelegation(run.pendingDelegation, {
806
- now: options.now,
807
- monotonicNow: options.monotonicNow
808
- });
809
- if (!result.ok)
810
- return pauseOrchestration(taskRef, result.code, result.message, true, options);
811
- const updated = withUpdatedRun(run, { pendingDelegation: result.receipt });
812
- saveRun(resolved.taskDir, updated);
813
- return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
814
- }
815
- function matchingDelegations(predicate, options) {
816
- const taskId = options.taskId;
817
- if (taskId === undefined || taskId === '') {
818
- return { ok: false, error: failed('ORCHESTRATION_TASK_ID_REQUIRED', 'taskId is required for lifecycle matching') };
819
- }
820
- if (typeof taskId !== 'string' || !TASK_ID_RE.test(taskId)) {
821
- return { ok: false, error: failed('ORCHESTRATION_TASK_ID_INVALID', 'taskId must be a canonical TASK-id') };
822
- }
823
- const repoRoot = options.repoRoot ?? process.cwd();
824
- const taskDir = path.join(resolveProjectTaskWorkspaceRoot(repoRoot), 'active', taskId);
825
- const run = readRun(taskDir, options);
826
- const matches = run && run.status === 'running' && run.pendingDelegation && predicate(run.pendingDelegation)
827
- ? [{ taskId, run }]
828
- : [];
829
- return { ok: true, matches };
830
- }
831
- function uniqueMatchingDelegation(client, predicate, options) {
832
- const lookup = matchingDelegations((receipt) => receipt.client === client && predicate(receipt), options);
833
- if (!lookup.ok)
834
- return lookup.error;
835
- const matches = lookup.matches;
836
- if (matches.length === 0)
837
- return failed('ORCHESTRATION_DELEGATION_MISSING', 'no matching lifecycle delegation exists');
838
- return matches[0];
839
- }
840
- function hasActivatableOrchestrationDelegation(client, childId, options = {}) {
841
- const lookup = matchingDelegations((receipt) => receipt.client === client && (receipt.status === 'prepared'
842
- || (receipt.childId === childId && ['activated', 'stage-completed'].includes(receipt.status))), options);
843
- return lookup.ok && lookup.matches.length > 0;
844
- }
845
- function hasSealableOrchestrationDelegation(client, childId, options = {}) {
846
- const lookup = matchingDelegations((receipt) => receipt.client === client
847
- && receipt.childId === childId
848
- && ['stage-completed', 'sealed'].includes(receipt.status), options);
849
- return lookup.ok && lookup.matches.length > 0;
850
- }
851
- function activateMatchingOrchestrationDelegation(client, event, options = {}) {
852
- const role = managedDelegationRole(event.nativeAgent);
853
- if (!role)
854
- return failed('DELEGATION_IGNORED', `subagent '${event.nativeAgent}' is not lifecycle-managed`);
855
- const matched = uniqueMatchingDelegation(client, (receipt) => receipt.status === 'prepared', options);
856
- if ('status' in matched) {
857
- if (matched.error?.code !== 'ORCHESTRATION_DELEGATION_MISSING')
858
- return matched;
859
- const replay = uniqueMatchingDelegation(client, (receipt) => receipt.childId === event.childId && ['activated', 'stage-completed'].includes(receipt.status), options);
860
- if ('status' in replay)
861
- return matched;
862
- const receipt = replay.run.pendingDelegation;
863
- const sameEvidence = receipt.parentId === event.parentId
864
- && (receipt.spawnMode ?? null) === (event.spawnMode ?? null)
865
- && (receipt.actualModel ?? null) === foldBlankToNull(event.actualModel)
866
- && (receipt.actualReasoningEffort ?? null) === foldBlankToNull(event.actualReasoningEffort)
867
- && receipt.modelFallbackReason === (event.modelFallbackReason ?? null)
868
- && receipt.reasoningEffortFallbackReason === (event.reasoningEffortFallbackReason ?? null)
869
- && (event.clientEvidence === undefined
870
- || JSON.stringify(receipt.adapterEvidence?.[client]) === JSON.stringify(event.clientEvidence));
871
- if (!sameEvidence) {
872
- return pauseOrchestration(replay.taskId, 'DELEGATION_REPLAY_CONFLICT', 'replayed native start evidence conflicts with the active receipt', true, options);
873
- }
874
- return { status: replay.run.status, changed: false, taskId: replay.taskId, run: replay.run, next: null, error: null };
875
- }
876
- if (matched.run.receipts.some((receipt) => receipt.childId === event.childId)) {
877
- return pauseOrchestration(matched.taskId, 'DELEGATION_IDENTITY_REUSED', 'native child identity was already used by this run', true, options);
878
- }
879
- return activateOrchestrationDelegation(matched.taskId, event, options);
880
- }
881
- function pauseMatchingOrchestrationDelegation(client, code, message, options = {}) {
882
- const matched = uniqueMatchingDelegation(client, () => true, options);
883
- if ('status' in matched)
884
- return matched;
885
- return pauseOrchestration(matched.taskId, code, message, true, options);
886
- }
887
- function sealMatchingOrchestrationDelegation(client, event, options = {}, beforeSeal) {
888
- const role = managedDelegationRole(event.nativeAgent);
889
- if (!role)
890
- return failed('DELEGATION_IGNORED', `subagent '${event.nativeAgent}' is not lifecycle-managed`);
891
- const matched = uniqueMatchingDelegation(client, (receipt) => receipt.status !== 'prepared', options);
892
- if ('status' in matched)
893
- return matched;
894
- const receipt = matched.run.pendingDelegation;
895
- if (receipt.role !== role) {
896
- return pauseOrchestration(matched.taskId, 'DELEGATION_ROLE_MISMATCH', `managed role ${role} does not match ${receipt.role}`, true, options);
897
- }
898
- const repoRoot = options.repoRoot ?? process.cwd();
899
- try {
900
- const gitRoot = gitRootFor(repoRoot, options);
901
- const snapshotTaskId = receipt.workspaceSnapshotScope === 'task' ? receipt.taskId : null;
902
- const afterFingerprint = (options.captureWorkspace ?? captureWorkspaceSnapshot)({
903
- gitRoot,
904
- stateRoot: repoRoot,
905
- taskId: snapshotTaskId
906
- });
907
- const changedPaths = (options.diffWorkspace ?? diffWorkspaceSnapshots)(gitRoot, receipt.beforeFingerprint, afterFingerprint);
908
- return sealOrchestrationDelegation(matched.taskId, {
909
- childId: event.childId,
910
- exitCode: 0,
911
- afterFingerprint,
912
- changedPaths,
913
- actualModel: event.actualModel,
914
- actualReasoningEffort: event.actualReasoningEffort,
915
- modelFallbackReason: event.modelFallbackReason,
916
- reasoningEffortFallbackReason: event.reasoningEffortFallbackReason
917
- }, { runId: receipt.runId, receiptId: receipt.id }, options, beforeSeal);
918
- }
919
- catch (error) {
920
- return pauseOrchestration(matched.taskId, 'ORCHESTRATION_SNAPSHOT_FAILED', error instanceof Error ? error.message : String(error), true, options);
921
- }
922
- }
923
- function reconcileMatchingOrchestrationDelegation(client, childId, options = {}) {
924
- const matched = uniqueMatchingDelegation(client, (receipt) => receipt.childId === childId, options);
925
- if ('status' in matched)
926
- return matched;
927
- const receipt = matched.run.pendingDelegation;
928
- if (receipt.status !== 'sealed' || receipt.childId !== childId) {
929
- return pauseOrchestration(matched.taskId, 'ORCHESTRATION_DELEGATION_RECONCILIATION_FAILED', 'native child did not produce one sealed delegation receipt', true, options);
930
- }
931
- return { status: matched.run.status, changed: false, taskId: matched.taskId, run: matched.run, next: null, error: null };
932
- }
933
- function activateOrchestrationDelegation(taskRef, event, options = {}) {
934
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
935
- if (!resolved.ok)
936
- return failed(resolved.code, resolved.message, resolved.taskId);
937
- const run = readRun(resolved.taskDir, options);
938
- if (!run?.pendingDelegation)
939
- return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
940
- if (run.status !== 'running') {
941
- return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
942
- }
943
- const validateAdapterEvidence = getAgentClientAdapter(run.pendingDelegation.client)
944
- .orchestrationAdapter?.validateActivationEvidence;
945
- if (validateAdapterEvidence && !validateAdapterEvidence(event.clientEvidence, event.parentId)) {
946
- return pauseOrchestration(taskRef, 'DELEGATION_ADAPTER_EVIDENCE_REQUIRED', 'native activation lacks valid evidence from the agent adapter', true, options);
947
- }
948
- const result = activateDelegation(run.pendingDelegation, event, {
949
- now: options.now,
950
- monotonicNow: options.monotonicNow,
951
- evidencePolicy: getAgentClientDelegationEvidence(run.pendingDelegation.client)
952
- });
953
- if (!result.ok) {
954
- if (result.code === 'DELEGATION_IGNORED')
955
- return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
956
- return pauseOrchestration(taskRef, result.code, result.message, true, options);
957
- }
958
- const updated = withUpdatedRun(run, { pendingDelegation: result.receipt });
959
- saveRun(resolved.taskDir, updated);
960
- return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
961
- }
962
- async function awaitOrchestrationDelegationActivation(taskRef, event, options = {}) {
963
- const sleep = options.sleep
964
- ?? ((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)));
965
- while (true) {
966
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
967
- if (!resolved.ok)
968
- return failed(resolved.code, resolved.message, resolved.taskId);
969
- const run = readRun(resolved.taskDir, options);
970
- if (!run?.pendingDelegation) {
971
- return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
972
- }
973
- const receipt = run.pendingDelegation;
974
- const matches = receipt.stage === event.stage
975
- && receipt.round === event.round
976
- && receipt.artifact === event.artifact
977
- && receipt.role === event.role;
978
- if (!matches) {
979
- return pauseOrchestration(taskRef, 'ORCHESTRATION_PROVENANCE_MISMATCH', 'activation barrier identity does not match the pending delegation', true, options);
980
- }
981
- if (receipt.status === 'activated' || receipt.status === 'stage-completed') {
982
- return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
983
- }
984
- if (run.status !== 'running') {
985
- return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
986
- }
987
- if (receipt.status !== 'prepared') {
988
- return pauseOrchestration(taskRef, 'ORCHESTRATION_ACTIVATION_STATE_INVALID', `activation barrier found receipt status '${receipt.status}'`, true, options);
989
- }
990
- if (receipt.activationDeadlineAt == null
991
- || !Number.isFinite(Date.parse(receipt.activationDeadlineAt))) {
992
- return pauseOrchestration(taskRef, 'ORCHESTRATION_DELEGATION_NOT_DISPATCHED', 'activation barrier was entered before the native spawn dispatch boundary', true, options);
993
- }
994
- if (Date.parse((options.now ?? (() => new Date().toISOString()))()) >= Date.parse(receipt.activationDeadlineAt)) {
995
- return pauseOrchestration(taskRef, 'ORCHESTRATION_ACTIVATION_TIMEOUT', 'fresh child did not receive trusted activation evidence before the deadline', true, options);
996
- }
997
- await sleep(100);
998
- }
999
- }
1000
- function recoverPreparedOrchestrationDelegation(taskRef, options = {}) {
1001
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
1002
- if (!resolved.ok)
1003
- return failed(resolved.code, resolved.message, resolved.taskId);
1004
- try {
1005
- return (() => {
1006
- const run = readRun(resolved.taskDir, options);
1007
- const receipt = run?.pendingDelegation;
1008
- if (!run || !receipt) {
1009
- return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
1010
- }
1011
- if (receipt.status !== 'prepared') {
1012
- return failed('ORCHESTRATION_PREPARED_RECOVERY_UNSAFE', 'only a never-activated prepared receipt can recover', resolved.taskId);
1013
- }
1014
- const wallNow = Date.parse((options.now ?? (() => new Date().toISOString()))());
1015
- if (receipt.activationDeadlineAt != null
1016
- && Number.isFinite(Date.parse(receipt.activationDeadlineAt))
1017
- && wallNow <= Date.parse(receipt.activationDeadlineAt)) {
1018
- return failed('ORCHESTRATION_PREPARED_RECOVERY_EARLY', 'prepared receipt activation deadline has not elapsed', resolved.taskId);
1019
- }
1020
- const activeAdapterEvidence = options.hasActiveLifecycleEvidence?.(receipt)
1021
- ?? Object.keys(receipt.adapterEvidence ?? {}).length > 0;
1022
- if (activeAdapterEvidence) {
1023
- return failed('ORCHESTRATION_PREPARED_RECOVERY_ACTIVE_EVIDENCE', 'active agent adapter evidence blocks prepared receipt recovery', resolved.taskId);
1024
- }
1025
- const current = (options.captureWorkspace ?? captureWorkspaceSnapshot)({
1026
- gitRoot: gitRootFor(resolved.repoRoot, options),
1027
- stateRoot: resolved.repoRoot,
1028
- taskId: receipt.workspaceSnapshotScope === 'task' ? receipt.taskId : null
1029
- });
1030
- if (current !== receipt.beforeFingerprint) {
1031
- return failed('ORCHESTRATION_PREPARED_RECOVERY_DRIFT', 'workspace changed after delegation preparation', resolved.taskId);
1032
- }
1033
- const aborted = abortPreparedDelegation(receipt);
1034
- if (!aborted.ok)
1035
- return failed(aborted.code, aborted.message, resolved.taskId);
1036
- const updated = withUpdatedRun(run, {
1037
- status: 'running',
1038
- pause: null,
1039
- nextStage: null,
1040
- pendingDelegation: null,
1041
- receipts: Object.freeze([...run.receipts, aborted.receipt])
1042
- }, options.now);
1043
- saveRun(resolved.taskDir, updated);
1044
- return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
1045
- })();
1046
- }
1047
- catch (error) {
1048
- return failed('ORCHESTRATION_PREPARED_RECOVERY_FAILED', error instanceof Error ? error.message : String(error), resolved.taskId);
1049
- }
1050
- }
1051
- function recoveryReceiptMatches(receipt, event) {
1052
- return receipt.stage === event.stage
1053
- && receipt.round === event.round
1054
- && receipt.artifact === event.artifact
1055
- && receipt.childId === event.childId
1056
- && normalizeAgentToken(event.startedAgent) === normalizeAgentToken(receipt.client);
1057
- }
1058
- function recoverActivatedOrchestrationDelegation(taskRef, event, options = {}) {
1059
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
1060
- if (!resolved.ok)
1061
- return failed(resolved.code, resolved.message, resolved.taskId);
1062
- const run = readRun(resolved.taskDir, options);
1063
- if (!run)
1064
- return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
1065
- const matches = [
1066
- ...(run.pendingDelegation ? [run.pendingDelegation] : []),
1067
- ...run.receipts.filter((receipt) => receipt.id === event.receiptId)
1068
- ];
1069
- const receipt = matches.find((candidate) => candidate.id === event.receiptId);
1070
- if (!receipt)
1071
- return failed('ORCHESTRATION_DELEGATION_MISSING', 'recovery receipt does not exist', resolved.taskId);
1072
- if (!recoveryReceiptMatches(receipt, event)) {
1073
- return failed('ORCHESTRATION_PROVENANCE_MISMATCH', 'recovery selector does not match the delegation receipt', resolved.taskId);
1074
- }
1075
- if (receipt.status === 'aborted' && run.pendingDelegation === null) {
1076
- return { status: run.status, changed: false, taskId: resolved.taskId, run, next: null, error: null };
1077
- }
1078
- if (run.pendingDelegation?.id !== receipt.id || receipt.status !== 'activated') {
1079
- return failed('ORCHESTRATION_RECOVERY_UNSAFE', 'recovery requires one matching activated pending delegation', resolved.taskId);
1080
- }
1081
- if (run.receipts.some((candidate) => candidate.id === receipt.id)) {
1082
- return failed('ORCHESTRATION_RECEIPT_DUPLICATE', 'recovery receipt already exists in the completed receipt history', resolved.taskId);
1083
- }
1084
- if (run.status !== 'running' || run.pause !== null) {
1085
- return failed('ORCHESTRATION_RECOVERY_UNSAFE', 'activated recovery cannot replace an unrelated orchestration pause', resolved.taskId);
1086
- }
1087
- const aborted = abortActivatedDelegation(receipt, {
1088
- childId: event.childId,
1089
- clientEvidence: event.clientEvidence
1090
- });
1091
- if (!aborted.ok)
1092
- return failed(aborted.code, aborted.message, resolved.taskId);
1093
- const updated = withUpdatedRun(run, {
1094
- status: 'running',
1095
- pause: null,
1096
- nextStage: null,
1097
- pendingDelegation: null,
1098
- receipts: Object.freeze([...run.receipts, aborted.receipt])
1099
- }, options.now);
1100
- saveRun(resolved.taskDir, updated);
1101
- return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
1102
- }
1103
- function completeOrchestrationStage(taskRef, event, options = {}) {
1104
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
1105
- if (!resolved.ok)
1106
- return failed(resolved.code, resolved.message, resolved.taskId);
1107
- const run = readRun(resolved.taskDir, options);
1108
- if (!run)
1109
- return { status: 'running', changed: false, taskId: resolved.taskId, run: null, next: null, error: null };
1110
- if (!run.pendingDelegation)
1111
- return failed('ORCHESTRATION_DELEGATION_MISSING', 'active run has no pending delegation', resolved.taskId);
1112
- const result = completeDelegationStage(run.pendingDelegation, event);
1113
- if (!result.ok)
1114
- return pauseOrchestration(taskRef, result.code, result.message, true, options);
1115
- const updated = withUpdatedRun(run, { pendingDelegation: result.receipt });
1116
- saveRun(resolved.taskDir, updated);
1117
- return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
1118
- }
1119
- function inspectOrchestrationStage(taskRef, event, options = {}) {
1120
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
1121
- if (!resolved.ok)
1122
- return failed(resolved.code, resolved.message, resolved.taskId);
1123
- let run;
1124
- try {
1125
- run = readRun(resolved.taskDir, options);
1126
- }
1127
- catch (error) {
1128
- return failed('ORCHESTRATION_STATE_INVALID', error instanceof Error ? error.message : String(error), resolved.taskId);
1129
- }
1130
- if (!run)
1131
- return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
1132
- const receipt = run.pendingDelegation;
1133
- if (!receipt
1134
- || receipt.status !== 'activated'
1135
- || receipt.stage !== event.stage
1136
- || receipt.round !== event.round
1137
- || receipt.artifact !== event.artifact
1138
- || receipt.role !== event.role) {
1139
- return failed('ORCHESTRATION_PROVENANCE_MISMATCH', 'active run does not have one matching activated delegation', resolved.taskId);
1140
- }
1141
- return { status: 'running', changed: false, taskId: resolved.taskId, run, next: null, error: null };
1142
- }
1143
- function planOrchestrationStageCompletion(taskRef, event, options = {}) {
1144
- const inspected = inspectOrchestrationStage(taskRef, event, options);
1145
- if (inspected.status === 'failed' || !inspected.run?.pendingDelegation) {
1146
- return { result: inspected, plan: null };
1147
- }
1148
- const receipt = inspected.run.pendingDelegation;
1149
- const completed = completeDelegationStage(receipt, event);
1150
- if (!completed.ok) {
1151
- return { result: failed(completed.code, completed.message, inspected.taskId), plan: null };
1152
- }
1153
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
1154
- if (!resolved.ok)
1155
- return { result: failed(resolved.code, resolved.message, resolved.taskId), plan: null };
1156
- const updatedRun = withUpdatedRun(inspected.run, { pendingDelegation: completed.receipt }, options.now);
1157
- return {
1158
- result: { status: 'running', changed: true, taskId: resolved.taskId, run: updatedRun, next: null, error: null },
1159
- plan: {
1160
- taskId: resolved.taskId,
1161
- taskDir: resolved.taskDir,
1162
- updatedRun
1163
- }
1164
- };
1165
- }
1166
- function commitOrchestrationStageCompletion(plan) {
1167
- saveRun(plan.taskDir, plan.updatedRun);
1168
- }
1169
- function sealOrchestrationDelegation(taskRef, event, expected, options = {}, beforeSeal) {
1170
- const orchestrationOptions = options;
1171
- const resolved = resolveTaskRef(taskRef, { repoRoot: orchestrationOptions.repoRoot });
1172
- if (!resolved.ok)
1173
- return failed(resolved.code, resolved.message, resolved.taskId);
1174
- return (() => {
1175
- const run = readRun(resolved.taskDir, orchestrationOptions);
1176
- if (!run?.pendingDelegation)
1177
- return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
1178
- if (expected && (run.runId !== expected.runId || run.pendingDelegation.id !== expected.receiptId)) {
1179
- return failed('ORCHESTRATION_DELEGATION_CHANGED', 'pending delegation changed while collecting completion evidence', resolved.taskId);
1180
- }
1181
- const preSealFailure = beforeSeal?.();
1182
- if (preSealFailure)
1183
- return preSealFailure;
1184
- const current = beforeSeal ? readRun(resolved.taskDir, orchestrationOptions) : run;
1185
- if (!current?.pendingDelegation)
1186
- return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
1187
- if (expected && (current.runId !== expected.runId || current.pendingDelegation.id !== expected.receiptId)) {
1188
- return failed('ORCHESTRATION_DELEGATION_CHANGED', 'pending delegation changed before it was sealed', resolved.taskId);
1189
- }
1190
- const result = sealDelegation(current.pendingDelegation, event, { now: orchestrationOptions.now });
1191
- if (!result.ok)
1192
- return pauseOrchestration(taskRef, result.code, result.message, true, orchestrationOptions);
1193
- const updated = withUpdatedRun(current, { pendingDelegation: result.receipt });
1194
- saveRun(resolved.taskDir, updated);
1195
- return { status: 'running', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
1196
- })();
1197
- }
1198
- function advanceOrchestration(taskRef, options = {}) {
1199
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
1200
- if (!resolved.ok)
1201
- return failed(resolved.code, resolved.message, resolved.taskId);
1202
- const run = readRun(resolved.taskDir, options);
1203
- if (!run?.pendingDelegation)
1204
- return failed('ORCHESTRATION_DELEGATION_MISSING', 'no pending delegation exists', resolved.taskId);
1205
- const result = consumeDelegation(run.pendingDelegation, { now: options.now });
1206
- if (!result.ok)
1207
- return pauseOrchestration(taskRef, result.code, result.message, true, options);
1208
- if (result.receipt.stage === 'commit') {
1209
- return failed('ORCHESTRATED_COMMIT_REMOVED', 'Orchestrated commit is no longer a lifecycle stage; use local checkpoint delivery from code-task', resolved.taskId);
1210
- }
1211
- const updated = withUpdatedRun(run, {
1212
- status: 'running',
1213
- nextStage: null,
1214
- stepCount: run.stepCount + 1,
1215
- pendingDelegation: null,
1216
- receipts: Object.freeze([...run.receipts, result.receipt])
1217
- });
1218
- saveRun(resolved.taskDir, updated);
1219
- return { status: updated.status, changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
1220
- }
1221
- function pauseOrchestration(taskRef, code, message, recoverable, options = {}) {
1222
- const resolved = resolveTaskRef(taskRef, { repoRoot: options.repoRoot });
1223
- if (!resolved.ok)
1224
- return failed(resolved.code, resolved.message, resolved.taskId);
1225
- const run = readRun(resolved.taskDir, options);
1226
- if (!run)
1227
- return failed('ORCHESTRATION_RUN_MISSING', 'no orchestration run exists', resolved.taskId);
1228
- const updated = withUpdatedRun(run, { status: 'paused', pause: { code, message, recoverable } });
1229
- saveRun(resolved.taskDir, updated);
1230
- return { status: 'paused', changed: true, taskId: resolved.taskId, run: updated, next: null, error: null };
1231
- }
1232
- export { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, awaitOrchestrationDelegationActivation, advanceOrchestration, beginOrResumeOrchestration, commitOrchestrationStageCompletion, completeOrchestrationStage, dispatchOrchestrationDelegation, inspectOrchestrationStage, hasActivatableOrchestrationDelegation, hasSealableOrchestrationDelegation, OrchestrationStateError, orchestrationPath, readDelegationAdapterEvidence, mutateDelegationAdapterEvidence, pauseMatchingOrchestrationDelegation, pauseOrchestration, planOrchestrationStageCompletion, prepareOrchestrationDelegation, readRun, reconcileMatchingOrchestrationDelegation, recoverPreparedOrchestrationDelegation, recoverActivatedOrchestrationDelegation, routeOrchestration, sealMatchingOrchestrationDelegation, sealOrchestrationDelegation, statusOrchestration, };
1233
- //# sourceMappingURL=orchestration.js.map