@fitlab-ai/agent-infra 0.9.10 → 0.9.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. package/README.md +13 -3
  2. package/README.zh-CN.md +13 -3
  3. package/bin/cli.ts +17 -4
  4. package/dist/bin/cli.js +19 -4
  5. package/dist/bin/internal-cli.js +50 -4
  6. package/dist/lib/agent-client.js +0 -1
  7. package/dist/lib/agent-clients/adapters/opencode-sandbox.js +0 -4
  8. package/dist/lib/agent-clients/adapters/traecli.js +7 -8
  9. package/dist/lib/agent-clients/config.js +14 -99
  10. package/dist/lib/agent-clients/reconcile.js +1 -11
  11. package/dist/lib/agent-clients/schema.js +5 -4
  12. package/dist/lib/agent-clients/tokens.js +5 -15
  13. package/dist/lib/decide.js +14 -30
  14. package/dist/lib/defaults.json +4 -0
  15. package/dist/lib/init.js +2 -2
  16. package/dist/lib/internal/release-workflow.js +0 -1
  17. package/dist/lib/internal/task-finalization.js +19 -9
  18. package/dist/lib/internal/task-ledger.js +8 -2
  19. package/dist/lib/internal/task-lifecycle.js +17 -93
  20. package/dist/lib/internal/task-orchestration.js +30 -216
  21. package/dist/lib/internal/task-validate.js +2 -1
  22. package/dist/lib/merge.js +205 -103
  23. package/dist/lib/process-data/normalize.js +2 -9
  24. package/dist/lib/run/host.js +46 -16
  25. package/dist/lib/sandbox/agent-client-reconciler.js +4 -12
  26. package/dist/lib/sandbox/commands/create.js +3 -3
  27. package/dist/lib/sandbox/commands/enter.js +18 -4
  28. package/dist/lib/sandbox/config.js +3 -12
  29. package/dist/lib/sandbox/control/executor.js +131 -39
  30. package/dist/lib/sandbox/control/lifecycle.js +29 -20
  31. package/dist/lib/sandbox/control/server.js +18 -16
  32. package/dist/lib/sandbox/mounts.js +3 -2
  33. package/dist/lib/sandbox/recovery.js +123 -22
  34. package/dist/lib/sandbox/workspace-identity.js +14 -0
  35. package/dist/lib/sandbox/workspace-view.js +8 -9
  36. package/dist/lib/self-update.js +278 -0
  37. package/dist/lib/server/protocol.js +5 -5
  38. package/dist/lib/task/activity-log.js +4 -1
  39. package/dist/lib/task/artifact-lifecycle.js +4 -2
  40. package/dist/lib/task/codex-orchestration.js +1 -1
  41. package/dist/lib/task/command-options.js +1 -3
  42. package/dist/lib/task/commands/cat.js +0 -5
  43. package/dist/lib/task/commands/decisions.js +76 -92
  44. package/dist/lib/task/commands/files.js +6 -6
  45. package/dist/lib/task/commands/grep.js +25 -61
  46. package/dist/lib/task/commands/issue-body.js +6 -6
  47. package/dist/lib/task/commands/log.js +25 -10
  48. package/dist/lib/task/commands/show.js +5 -6
  49. package/dist/lib/task/commands/status.js +8 -8
  50. package/dist/lib/task/commit-operation.js +21 -25
  51. package/dist/lib/task/commit-policy.js +1 -1
  52. package/dist/lib/task/control-authority.js +431 -0
  53. package/dist/lib/task/create.js +6 -1
  54. package/dist/lib/task/current-contract.js +35 -0
  55. package/dist/lib/task/decision-details.js +263 -0
  56. package/dist/lib/task/decision-intents.js +5 -2
  57. package/dist/lib/task/events.js +11 -5
  58. package/dist/lib/task/index.js +19 -20
  59. package/dist/lib/task/ledger-intents.js +8 -9
  60. package/dist/lib/task/ledger.js +13 -6
  61. package/dist/lib/task/lifecycle.js +16 -0
  62. package/dist/lib/task/orchestration.js +50 -13
  63. package/dist/lib/task/review-artifacts.js +31 -12
  64. package/dist/lib/task/review-finalization.js +19 -6
  65. package/dist/lib/task/verification-engine.js +38 -15
  66. package/dist/lib/task/verification-review.js +1 -1
  67. package/dist/lib/task/workspace-snapshot.js +5 -11
  68. package/dist/lib/task/write.js +11 -1
  69. package/dist/lib/update.js +6 -37
  70. package/dist/lib/version.js +5 -1
  71. package/dist/lifecycle-build-manifest.json +3 -3
  72. package/lib/agent-client.ts +0 -1
  73. package/lib/agent-clients/adapters/opencode-sandbox.ts +0 -5
  74. package/lib/agent-clients/adapters/traecli.ts +7 -8
  75. package/lib/agent-clients/config.ts +14 -144
  76. package/lib/agent-clients/reconcile.ts +1 -12
  77. package/lib/agent-clients/schema.ts +5 -4
  78. package/lib/agent-clients/tokens.ts +5 -18
  79. package/lib/decide.ts +12 -22
  80. package/lib/defaults.json +4 -0
  81. package/lib/init.ts +2 -2
  82. package/lib/internal/release-workflow.ts +0 -1
  83. package/lib/internal/task-finalization.ts +27 -13
  84. package/lib/internal/task-ledger.ts +8 -2
  85. package/lib/internal/task-lifecycle.ts +33 -76
  86. package/lib/internal/task-orchestration.ts +33 -190
  87. package/lib/internal/task-validate.ts +3 -2
  88. package/lib/merge.ts +236 -120
  89. package/lib/process-data/normalize.ts +2 -8
  90. package/lib/run/host.ts +63 -14
  91. package/lib/sandbox/agent-client-reconciler.ts +5 -15
  92. package/lib/sandbox/commands/create.ts +4 -4
  93. package/lib/sandbox/commands/enter.ts +24 -4
  94. package/lib/sandbox/config.ts +2 -14
  95. package/lib/sandbox/control/container-identity.ts +2 -2
  96. package/lib/sandbox/control/executor.ts +148 -51
  97. package/lib/sandbox/control/lifecycle.ts +34 -28
  98. package/lib/sandbox/control/protocol.ts +1 -3
  99. package/lib/sandbox/control/server.ts +15 -12
  100. package/lib/sandbox/control/state.ts +7 -7
  101. package/lib/sandbox/mounts.ts +4 -3
  102. package/lib/sandbox/recovery.ts +162 -34
  103. package/lib/sandbox/tools.ts +1 -1
  104. package/lib/sandbox/workspace-identity.ts +28 -0
  105. package/lib/sandbox/workspace-view.ts +14 -10
  106. package/lib/self-update.ts +341 -0
  107. package/lib/server/protocol.ts +5 -5
  108. package/lib/task/activity-log.ts +6 -1
  109. package/lib/task/artifact-lifecycle.ts +5 -3
  110. package/lib/task/codex-orchestration.ts +2 -1
  111. package/lib/task/command-options.ts +1 -2
  112. package/lib/task/commands/cat.ts +0 -4
  113. package/lib/task/commands/decisions.ts +80 -75
  114. package/lib/task/commands/files.ts +6 -6
  115. package/lib/task/commands/grep.ts +23 -51
  116. package/lib/task/commands/issue-body.ts +6 -6
  117. package/lib/task/commands/log.ts +22 -10
  118. package/lib/task/commands/show.ts +5 -6
  119. package/lib/task/commands/status.ts +8 -8
  120. package/lib/task/commit-operation.ts +25 -30
  121. package/lib/task/commit-policy.ts +1 -2
  122. package/lib/task/control-authority.ts +563 -0
  123. package/lib/task/create.ts +6 -1
  124. package/lib/task/current-contract.ts +49 -0
  125. package/lib/task/decision-details.ts +340 -0
  126. package/lib/task/decision-intents.ts +7 -2
  127. package/lib/task/events.ts +10 -5
  128. package/lib/task/index.ts +19 -20
  129. package/lib/task/ledger-intents.ts +6 -8
  130. package/lib/task/ledger.ts +17 -7
  131. package/lib/task/lifecycle.ts +20 -0
  132. package/lib/task/orchestration.ts +40 -15
  133. package/lib/task/review-artifacts.ts +53 -15
  134. package/lib/task/review-finalization.ts +33 -7
  135. package/lib/task/verification-engine.ts +33 -18
  136. package/lib/task/verification-review.ts +1 -1
  137. package/lib/task/workspace-snapshot.ts +4 -13
  138. package/lib/task/write.ts +20 -1
  139. package/lib/update.ts +6 -40
  140. package/lib/version.ts +6 -1
  141. package/package.json +1 -1
  142. package/templates/.agents/README.en.md +5 -3
  143. package/templates/.agents/README.zh-CN.md +5 -3
  144. package/templates/.agents/rules/compatibility-policy.en.md +5 -4
  145. package/templates/.agents/rules/compatibility-policy.zh-CN.md +5 -4
  146. package/templates/.agents/rules/create-issue.github.en.md +1 -1
  147. package/templates/.agents/rules/create-issue.github.zh-CN.md +1 -1
  148. package/templates/.agents/rules/human-decision-context.en.md +9 -7
  149. package/templates/.agents/rules/human-decision-context.zh-CN.md +9 -7
  150. package/templates/.agents/rules/issue-fields.github.en.md +1 -1
  151. package/templates/.agents/rules/issue-fields.github.zh-CN.md +1 -1
  152. package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
  153. package/templates/.agents/rules/issue-pr-commands.github.en.md +1 -1
  154. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +1 -1
  155. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
  156. package/templates/.agents/rules/issue-sync.github.en.md +1 -1
  157. package/templates/.agents/rules/issue-sync.github.zh-CN.md +2 -2
  158. package/templates/.agents/rules/milestone-inference.github.en.md +1 -1
  159. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +1 -1
  160. package/templates/.agents/rules/next-step-output.en.md +3 -3
  161. package/templates/.agents/rules/next-step-output.zh-CN.md +3 -3
  162. package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -1
  163. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -1
  164. package/templates/.agents/rules/pr-sync.en.md +1 -1
  165. package/templates/.agents/rules/pr-sync.github.en.md +1 -1
  166. package/templates/.agents/rules/pr-sync.github.zh-CN.md +1 -1
  167. package/templates/.agents/rules/pr-sync.zh-CN.md +1 -1
  168. package/templates/.agents/rules/release-commands.github.en.md +6 -0
  169. package/templates/.agents/rules/release-commands.github.zh-CN.md +6 -0
  170. package/templates/.agents/rules/review-handshake.en.md +1 -1
  171. package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
  172. package/templates/.agents/rules/task-management.en.md +1 -2
  173. package/templates/.agents/rules/task-management.zh-CN.md +1 -2
  174. package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -3
  175. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -3
  176. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  177. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  178. package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
  179. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
  180. package/templates/.agents/skills/check-task/SKILL.en.md +2 -2
  181. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +2 -2
  182. package/templates/.agents/skills/close-codescan/SKILL.en.md +1 -1
  183. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +1 -1
  184. package/templates/.agents/skills/close-dependabot/SKILL.en.md +1 -1
  185. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +1 -1
  186. package/templates/.agents/skills/code-task/SKILL.en.md +3 -3
  187. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +3 -3
  188. package/templates/.agents/skills/commit/SKILL.en.md +27 -7
  189. package/templates/.agents/skills/commit/SKILL.zh-CN.md +3 -4
  190. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +5 -5
  191. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +5 -5
  192. package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
  193. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
  194. package/templates/.agents/skills/create-pr/SKILL.en.md +5 -7
  195. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +5 -7
  196. package/templates/.agents/skills/create-pr/reference/change-report.en.md +9 -4
  197. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +9 -4
  198. package/templates/.agents/skills/create-pr/scripts/change-report.mjs +203 -0
  199. package/templates/.agents/skills/create-release-note/SKILL.en.md +5 -6
  200. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +5 -6
  201. package/templates/.agents/skills/create-task/SKILL.en.md +1 -1
  202. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +1 -1
  203. package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
  204. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
  205. package/templates/.agents/skills/plan-task/SKILL.en.md +3 -3
  206. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +3 -3
  207. package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
  208. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
  209. package/templates/.agents/skills/review-analysis/SKILL.en.md +5 -3
  210. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +5 -3
  211. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -0
  212. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -0
  213. package/templates/.agents/skills/review-code/SKILL.en.md +5 -3
  214. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -3
  215. package/templates/.agents/skills/review-code/config/verify.en.json +1 -0
  216. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -0
  217. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +2 -1
  218. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +2 -1
  219. package/templates/.agents/skills/review-plan/SKILL.en.md +5 -3
  220. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +5 -3
  221. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -0
  222. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -0
  223. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +4 -0
  224. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +4 -0
  225. package/templates/.agents/skills/run-task/SKILL.en.md +4 -0
  226. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +4 -0
  227. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +2 -2
  228. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +2 -2
  229. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +140 -109
  230. package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
  231. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
  232. package/templates/.agents/templates/task.en.md +1 -1
  233. package/templates/.agents/templates/task.zh-CN.md +1 -1
  234. package/templates/.claude/commands/analyze-task.en.md +1 -1
  235. package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
  236. package/templates/.claude/commands/block-task.en.md +1 -1
  237. package/templates/.claude/commands/block-task.zh-CN.md +1 -1
  238. package/templates/.claude/commands/cancel-task.en.md +1 -1
  239. package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
  240. package/templates/.claude/commands/check-task.en.md +1 -1
  241. package/templates/.claude/commands/check-task.zh-CN.md +1 -1
  242. package/templates/.claude/commands/code-task.en.md +1 -1
  243. package/templates/.claude/commands/code-task.zh-CN.md +1 -1
  244. package/templates/.claude/commands/commit.en.md +1 -1
  245. package/templates/.claude/commands/commit.zh-CN.md +1 -1
  246. package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
  247. package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
  248. package/templates/.claude/commands/complete-task.en.md +1 -1
  249. package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
  250. package/templates/.claude/commands/create-pr.en.md +1 -1
  251. package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
  252. package/templates/.claude/commands/plan-task.en.md +1 -1
  253. package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
  254. package/templates/.claude/commands/review-analysis.en.md +1 -1
  255. package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
  256. package/templates/.claude/commands/review-code.en.md +1 -1
  257. package/templates/.claude/commands/review-code.zh-CN.md +1 -1
  258. package/templates/.claude/commands/review-plan.en.md +1 -1
  259. package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
  260. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  261. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  262. package/templates/.claude/commands/run-task.en.md +1 -1
  263. package/templates/.claude/commands/run-task.zh-CN.md +1 -1
  264. package/templates/.claude/commands/watch-pr.en.md +1 -1
  265. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  266. package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.en.md +0 -1
  267. package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.zh-CN.md +0 -1
  268. package/dist/lib/builtin-tuis.js +0 -40
  269. package/lib/builtin-tuis.ts +0 -56
@@ -82,12 +82,12 @@ function boundTask(input, taskId, taskDir, taskMdPath, repoRoot, state) {
82
82
  function validateCommon(input, branch) {
83
83
  if (!input.message.trim())
84
84
  return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Commit message is required' };
85
- if (input.paths.length === 0 && !input.push)
86
- return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'At least one explicit path is required unless this is a push-only retry' };
87
85
  if (!/^[a-f0-9]{40,64}$/.test(input.expectedHead))
88
86
  return { code: 'GIT_HEAD_EXPECTATION_REQUIRED', message: 'A valid expected HEAD is required' };
89
87
  if (!/^[a-f0-9]{40,64}$/.test(input.expectedTree))
90
88
  return { code: 'GIT_TREE_EXPECTATION_REQUIRED', message: 'A valid expected tree is required' };
89
+ if (!input.push)
90
+ return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Commit delivery requires a push policy' };
91
91
  if (input.paths.some((candidate) => !candidate || candidate.startsWith('/') || candidate.split(/[\\/]/).includes('..'))) {
92
92
  return { code: 'GIT_COMMIT_INPUT_INVALID', message: 'Commit paths must stay inside the repository' };
93
93
  }
@@ -96,13 +96,11 @@ function validateCommon(input, branch) {
96
96
  }
97
97
  if (branch !== null && branch === '')
98
98
  return { code: 'GIT_BRANCH_INVALID', message: 'A named branch is required' };
99
- if (input.push) {
100
- if (!input.push.remote || input.push.refs.length !== 1)
101
- return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Push requires one remote and one ref' };
102
- const ref = input.push.refs[0] ?? '';
103
- if (!HEAD_REF.test(ref) || HEAD_REF.exec(ref)?.[1] !== branch) {
104
- return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Push ref must be the current full heads ref' };
105
- }
99
+ if (!input.push.remote || input.push.refs.length !== 1)
100
+ return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Push requires one remote and one ref' };
101
+ const ref = input.push.refs[0] ?? '';
102
+ if (!HEAD_REF.test(ref) || HEAD_REF.exec(ref)?.[1] !== branch) {
103
+ return { code: 'GIT_PUSH_INPUT_INVALID', message: 'Push ref must be the current full heads ref' };
106
104
  }
107
105
  return null;
108
106
  }
@@ -253,22 +251,20 @@ function executeUnlocked(input, task, mode) {
253
251
  }
254
252
  let status = committed.status;
255
253
  let result = committed.status === 'applied' ? 'committed' : 'no_op';
256
- if (input.push) {
257
- const target = `${input.push.remote}:${input.push.refs[0]}`;
258
- const decision = commitPushDecision({ branch, automatic: input.push.automatic }, target);
259
- if (!decision.shouldPush) {
260
- warnings.push(decision.warning);
261
- }
262
- else {
263
- const pushed = pushGitRefs({
264
- cwd: input.cwd,
265
- remote: input.push.remote,
266
- refs: input.push.refs,
267
- expectedSha: committed.snapshot?.head ?? gitText(input.cwd, ['rev-parse', 'HEAD'])
268
- });
269
- if (pushed.status !== 'applied')
270
- warnings.push(warning('COMMIT_PUSH_FAILED', pushed.error?.message ?? 'Git push failed', true, 'push', target, 'ACTION_REQUIRED'));
271
- }
254
+ const target = `${input.push.remote}:${input.push.refs[0]}`;
255
+ const decision = commitPushDecision({ branch }, target);
256
+ if (!decision.shouldPush) {
257
+ warnings.push(decision.warning);
258
+ }
259
+ else {
260
+ const pushed = pushGitRefs({
261
+ cwd: input.cwd,
262
+ remote: input.push.remote,
263
+ refs: input.push.refs,
264
+ expectedSha: committed.snapshot?.head ?? gitText(input.cwd, ['rev-parse', 'HEAD'])
265
+ });
266
+ if (pushed.status !== 'applied')
267
+ warnings.push(warning('COMMIT_PUSH_FAILED', pushed.error?.message ?? 'Git push failed', true, 'push', target, 'ACTION_REQUIRED'));
272
268
  }
273
269
  if (warnings.length > 0) {
274
270
  status = 'applied';
@@ -1,6 +1,6 @@
1
1
  function commitPushDecision(policy, target) {
2
2
  const branch = policy.branch.trim();
3
- if (policy.automatic && (branch === 'main' || branch === 'master'))
3
+ if (branch === 'main' || branch === 'master')
4
4
  return {
5
5
  shouldPush: false,
6
6
  warning: {
@@ -0,0 +1,431 @@
1
+ import path from 'node:path';
2
+ import { normalizeAgentToken } from '../agent-clients/tokens.js';
3
+ import { isAgentClientId } from '../agent-clients/types.js';
4
+ import { consumeHumanOverride, failureId, overrideDryRunConflict } from './human-override.js';
5
+ import { activateMatchingOrchestrationDelegation, activateOrchestrationDelegation, advanceOrchestration, awaitOrchestrationDelegationActivation, beginOrResumeOrchestration, dispatchOrchestrationDelegation, OrchestrationStateError, pauseOrchestration, prepareOrchestrationDelegation, recoverPreparedOrchestrationDelegation, routeOrchestration, sealMatchingOrchestrationDelegation, sealOrchestrationDelegation, statusOrchestration } from './orchestration.js';
6
+ import { prepareCodexOrchestrationDelegation } from './codex-orchestration.js';
7
+ import { applyTaskFinalization } from './finalization.js';
8
+ import { applyTaskLifecycle, taskLifecycleFailure } from './lifecycle.js';
9
+ import { resolveTaskRef, TASK_ID_RE } from './resolve-ref.js';
10
+ import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
11
+ import { verifyTaskEvent } from './verification.js';
12
+ function invalidContext(message) {
13
+ throw new Error(`TASK_CONTROL_CONTEXT_INVALID: ${message}`);
14
+ }
15
+ function absolute(name, value) {
16
+ if (!value || !path.isAbsolute(value))
17
+ invalidContext(`${name} must be absolute`);
18
+ return path.resolve(value);
19
+ }
20
+ function binding(value) {
21
+ if (value === null || value === undefined)
22
+ return value;
23
+ if (!/^[a-f0-9]{64}$/u.test(value.instanceDigest) || !value.controlGeneration) {
24
+ invalidContext('controller binding is invalid');
25
+ }
26
+ return value;
27
+ }
28
+ export function createDirectHostExecutionContext(params) {
29
+ return {
30
+ source: 'direct-host',
31
+ mode: 'direct-host',
32
+ repoRoot: absolute('repoRoot', params.repoRoot),
33
+ ...(params.runtimeDir === undefined ? {} : { runtimeDir: absolute('runtimeDir', params.runtimeDir) }),
34
+ ...(params.controllerBinding === undefined ? {} : { controllerBinding: binding(params.controllerBinding) })
35
+ };
36
+ }
37
+ export function createSandboxExecutorExecutionContext(params) {
38
+ if (!/^TASK-\d{8}-\d{6}$/u.test(params.taskId) || !params.generation || !params.requestId) {
39
+ invalidContext('sandbox binding is incomplete');
40
+ }
41
+ const controlRoot = path.dirname(path.resolve(params.manifestPath));
42
+ if (path.resolve(params.runtimeDir) !== path.join(controlRoot, 'runtime')) {
43
+ invalidContext('runtimeDir is not bound to the manifest control root');
44
+ }
45
+ return {
46
+ source: 'sandbox-executor',
47
+ mode: 'task-bound-sandbox',
48
+ repoRoot: absolute('repoRoot', params.repoRoot),
49
+ worktreeRoot: absolute('worktreeRoot', params.worktreeRoot),
50
+ runtimeDir: absolute('runtimeDir', params.runtimeDir),
51
+ taskId: params.taskId,
52
+ generation: params.generation,
53
+ manifestPath: absolute('manifestPath', params.manifestPath),
54
+ requestId: params.requestId,
55
+ ...(params.controllerBinding === undefined ? {} : { controllerBinding: binding(params.controllerBinding) })
56
+ };
57
+ }
58
+ export function assertTaskControlExecutionContext(context) {
59
+ if (context.source === 'direct-host') {
60
+ if (context.mode !== 'direct-host')
61
+ invalidContext('direct-host mode is inconsistent');
62
+ absolute('repoRoot', context.repoRoot);
63
+ if (context.runtimeDir !== undefined)
64
+ absolute('runtimeDir', context.runtimeDir);
65
+ binding(context.controllerBinding);
66
+ return;
67
+ }
68
+ if (context.mode !== 'task-bound-sandbox' || !/^TASK-\d{8}-\d{6}$/u.test(context.taskId)
69
+ || !context.generation || !context.requestId) {
70
+ invalidContext('sandbox executor context is incomplete');
71
+ }
72
+ absolute('repoRoot', context.repoRoot);
73
+ absolute('worktreeRoot', context.worktreeRoot);
74
+ absolute('runtimeDir', context.runtimeDir);
75
+ absolute('manifestPath', context.manifestPath);
76
+ if (path.resolve(context.runtimeDir) !== path.join(path.dirname(path.resolve(context.manifestPath)), 'runtime')) {
77
+ invalidContext('runtimeDir is not bound to the manifest control root');
78
+ }
79
+ binding(context.controllerBinding);
80
+ }
81
+ function operationTaskId(context, taskRef) {
82
+ if (context.source === 'sandbox-executor' && taskRef !== context.taskId) {
83
+ throw new Error('TASK_CONTROL_CONTEXT_TASK_MISMATCH: operation task does not match the sandbox manifest');
84
+ }
85
+ }
86
+ function domainOptions(context, options) {
87
+ return {
88
+ ...options,
89
+ repoRoot: context.repoRoot,
90
+ ...(context.source === 'sandbox-executor' ? { gitWorktreeRoot: context.worktreeRoot } : {})
91
+ };
92
+ }
93
+ function lifecycleTaskId(request, repoRoot) {
94
+ if (request.intent === 'restore')
95
+ return TASK_ID_RE.test(request.taskRef) ? request.taskRef : null;
96
+ try {
97
+ const resolved = resolveTaskRef(request.taskRef, { repoRoot });
98
+ return resolved.ok ? resolved.taskId : null;
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ }
104
+ function applyLifecycleWithAuthority(context, request) {
105
+ const conflict = overrideDryRunConflict(request);
106
+ if (conflict) {
107
+ return taskLifecycleFailure(request, { code: 'LIFECYCLE_PAYLOAD_INVALID', message: conflict.message });
108
+ }
109
+ const execute = () => {
110
+ const lifecycleResult = applyTaskLifecycle(request, { repoRoot: context.repoRoot });
111
+ if (lifecycleResult.status !== 'failed' || !request.overrideTicket)
112
+ return lifecycleResult;
113
+ if (!request.overrideTarget || !request.overrideScope) {
114
+ return {
115
+ ...lifecycleResult,
116
+ error: { code: 'OVERRIDE_PAYLOAD_INVALID', message: 'override ticket requires --override-target and --override-scope' }
117
+ };
118
+ }
119
+ const override = consumeHumanOverride({
120
+ taskRef: request.taskRef,
121
+ ticketId: request.overrideTicket,
122
+ failureId: failureId('lifecycle.apply', lifecycleResult.error?.code ?? 'LIFECYCLE_FAILED'),
123
+ target: request.overrideTarget,
124
+ scope: request.overrideScope,
125
+ intent: request.intent,
126
+ ...('alertNumber' in request && request.alertNumber ? { alertNumber: request.alertNumber } : {}),
127
+ ...('issueNumber' in request && request.issueNumber ? { issueNumber: request.issueNumber } : {}),
128
+ ...('stagingDir' in request && request.stagingDir ? { stagingDir: request.stagingDir } : {})
129
+ }, { repoRoot: context.repoRoot });
130
+ if (override.status === 'failed')
131
+ return { ...lifecycleResult, humanOverride: override };
132
+ return { ...lifecycleResult, ...override, humanOverride: override, error: null };
133
+ };
134
+ const taskId = lifecycleTaskId(request, context.repoRoot);
135
+ if (!taskId)
136
+ return execute();
137
+ try {
138
+ return withTaskExecutionLock(context.repoRoot, taskId, `task-lifecycle.${request.intent}`, execute);
139
+ }
140
+ catch (error) {
141
+ if (!(error instanceof TaskExecutionLockError))
142
+ throw error;
143
+ return taskLifecycleFailure(request, { code: error.code, message: error.message }, taskId);
144
+ }
145
+ }
146
+ function orchestration(context, operation) {
147
+ operationTaskId(context, operation.taskRef);
148
+ const input = operation.input;
149
+ const options = domainOptions(context, operation.options);
150
+ switch (operation.intent) {
151
+ case 'begin-or-resume':
152
+ return beginOrResumeOrchestration(operation.taskRef, {
153
+ ...options,
154
+ client: input.client,
155
+ maxSteps: input.maxSteps,
156
+ modelPolicy: input.modelPolicy
157
+ });
158
+ case 'route': return routeOrchestration(operation.taskRef, options);
159
+ case 'status': return statusOrchestration(operation.taskRef, options);
160
+ case 'prepare': {
161
+ const prepareInput = {
162
+ client: input.client,
163
+ requestedModel: input.requestedModel,
164
+ requestedReasoningEffort: input.requestedReasoningEffort,
165
+ ...(input.capabilityToken === undefined ? {} : { capabilityToken: input.capabilityToken })
166
+ };
167
+ if (prepareInput.client === 'codex') {
168
+ return prepareCodexOrchestrationDelegation(operation.taskRef, prepareInput, {
169
+ repoRoot: context.repoRoot,
170
+ orchestrationOptions: options,
171
+ ...(context.controllerBinding ? { controllerBinding: context.controllerBinding } : {})
172
+ });
173
+ }
174
+ return prepareOrchestrationDelegation(operation.taskRef, prepareInput, options);
175
+ }
176
+ case 'dispatch': return dispatchOrchestrationDelegation(operation.taskRef, options);
177
+ case 'await-activation':
178
+ return awaitOrchestrationDelegationActivation(operation.taskRef, input.event, options);
179
+ case 'recover-prepared': return recoverPreparedOrchestrationDelegation(operation.taskRef, options);
180
+ case 'hook-start':
181
+ return input.auto === true
182
+ ? activateMatchingOrchestrationDelegation(input.client, input.event, options)
183
+ : activateOrchestrationDelegation(operation.taskRef, input.event, options);
184
+ case 'hook-stop':
185
+ return input.auto === true
186
+ ? sealMatchingOrchestrationDelegation(input.client, input.event, options)
187
+ : sealOrchestrationDelegation(operation.taskRef, input.event, options);
188
+ case 'advance': return advanceOrchestration(operation.taskRef, options);
189
+ case 'pause':
190
+ return pauseOrchestration(operation.taskRef, input.code, input.message, input.recoverable, options);
191
+ }
192
+ }
193
+ function orchestrationFailure(error) {
194
+ if (error instanceof OrchestrationStateError) {
195
+ return {
196
+ status: 'failed', changed: false, taskId: error.taskId, run: null, next: null,
197
+ error: { code: error.code, message: error.message }
198
+ };
199
+ }
200
+ throw error;
201
+ }
202
+ export function dispatchTaskControlOperation(context, operation) {
203
+ assertTaskControlExecutionContext(context);
204
+ assertTaskControlOperation(operation);
205
+ if (operation.family === 'task-lifecycle') {
206
+ operationTaskId(context, operation.request.taskRef);
207
+ return applyLifecycleWithAuthority(context, operation.request);
208
+ }
209
+ if (operation.family === 'task-finalization') {
210
+ operationTaskId(context, operation.request.taskRef);
211
+ return applyTaskFinalization(operation.request, {
212
+ repoRoot: context.repoRoot,
213
+ preflight: (request, options) => verifyTaskEvent({ ...request, event: 'complete-task.hard-preflight' }, options)
214
+ });
215
+ }
216
+ try {
217
+ const result = orchestration(context, operation);
218
+ return result instanceof Promise ? result.catch(orchestrationFailure) : result;
219
+ }
220
+ catch (error) {
221
+ return orchestrationFailure(error);
222
+ }
223
+ }
224
+ export function assertTaskControlOperation(operation) {
225
+ if (operation.family === 'task-lifecycle') {
226
+ if (!operation.request.taskRef || !normalizeAgentToken(operation.request.agent)) {
227
+ throw new Error('TASK_CONTROL_OPERATION_INVALID: lifecycle request is invalid');
228
+ }
229
+ return;
230
+ }
231
+ if (operation.family === 'task-finalization') {
232
+ if (!operation.request.taskRef || !normalizeAgentToken(operation.request.agent)) {
233
+ throw new Error('TASK_CONTROL_OPERATION_INVALID: finalization request is invalid');
234
+ }
235
+ return;
236
+ }
237
+ if (!operation.taskRef || !operation.intent || !operation.input || typeof operation.input !== 'object' || Array.isArray(operation.input)) {
238
+ throw new Error('TASK_CONTROL_OPERATION_INVALID: orchestration operation is invalid');
239
+ }
240
+ }
241
+ function operationInvalid(message) {
242
+ throw new Error(`TASK_CONTROL_OPERATION_INVALID: ${message}`);
243
+ }
244
+ const LIFECYCLE_FLAGS = new Set([
245
+ '--agent', '--reason', '--unblock-condition', '--note', '--alert-number', '--staging-dir', '--issue-number',
246
+ '--override-ticket', '--override-target', '--override-scope', '--dry-run'
247
+ ]);
248
+ const FINALIZATION_FLAGS = new Set(['--agent']);
249
+ const ORCHESTRATION_FLAGS = new Set([
250
+ '--agent', '--max-steps', '--executor-model', '--executor-reasoning-effort', '--reviewer-model',
251
+ '--reviewer-reasoning-effort', '--client', '--requested-model', '--requested-reasoning-effort',
252
+ '--capability-token', '--parent-id', '--before-fingerprint', '--stage', '--round', '--artifact', '--role',
253
+ '--native-agent', '--child-id', '--spawn-mode', '--actual-model', '--actual-reasoning-effort',
254
+ '--model-fallback-reason', '--reasoning-effort-fallback-reason', '--exit-code', '--after-fingerprint',
255
+ '--changed-paths', '--code', '--message', '--recoverable', '--git-worktree-root'
256
+ ]);
257
+ function parseValues(args, start, flags) {
258
+ const values = {};
259
+ for (let index = start; index < args.length; index += 1) {
260
+ const flag = args[index];
261
+ if (flag === '--dry-run') {
262
+ if (!flags.has(flag))
263
+ operationInvalid(`unknown option '${flag}'`);
264
+ if (values[flag] !== undefined)
265
+ operationInvalid(`duplicate option '${flag}'`);
266
+ values[flag] = true;
267
+ continue;
268
+ }
269
+ if (!flags.has(flag))
270
+ operationInvalid(`unknown option '${flag}'`);
271
+ if (values[flag] !== undefined)
272
+ operationInvalid(`duplicate option '${flag}'`);
273
+ const value = args[++index];
274
+ if (!value || value.startsWith('--'))
275
+ operationInvalid(`option '${flag}' requires a value`);
276
+ values[flag] = value;
277
+ }
278
+ return values;
279
+ }
280
+ function value(values, flag) {
281
+ const current = values[flag];
282
+ return typeof current === 'string' ? current : undefined;
283
+ }
284
+ function required(values, flags) {
285
+ const missing = flags.find((flag) => value(values, flag) === undefined);
286
+ if (missing)
287
+ operationInvalid(`option '${missing}' is required`);
288
+ }
289
+ export function parseTaskControlOperation(family, args) {
290
+ if (family === 'task-finalization') {
291
+ if (args.length !== 4 || args[1] !== 'complete' || args[2] !== '--agent' || !args[0] || !args[3]) {
292
+ operationInvalid('task ref, complete intent, and --agent are required');
293
+ }
294
+ const values = parseValues(args, 2, FINALIZATION_FLAGS);
295
+ const agent = normalizeAgentToken(value(values, '--agent') ?? '');
296
+ if (!agent)
297
+ operationInvalid('finalization agent is invalid');
298
+ return { family, request: { taskRef: args[0], intent: 'complete', agent } };
299
+ }
300
+ if (args.length < 2)
301
+ operationInvalid('task ref and intent are required');
302
+ const taskRef = args[0];
303
+ const intent = args[1];
304
+ if (family === 'task-lifecycle') {
305
+ const values = parseValues(args, 2, LIFECYCLE_FLAGS);
306
+ const agent = normalizeAgentToken(value(values, '--agent') ?? '');
307
+ if (!agent)
308
+ operationInvalid('lifecycle agent is invalid');
309
+ const input = {
310
+ taskRef,
311
+ intent,
312
+ agent,
313
+ ...(value(values, '--reason') ? { reason: value(values, '--reason') } : {}),
314
+ ...(value(values, '--unblock-condition') ? { unblockCondition: value(values, '--unblock-condition') } : {}),
315
+ ...(value(values, '--note') ? { note: value(values, '--note') } : {}),
316
+ ...(value(values, '--staging-dir') ? { stagingDir: value(values, '--staging-dir') } : {}),
317
+ ...(value(values, '--override-ticket') ? { overrideTicket: value(values, '--override-ticket') } : {}),
318
+ ...(value(values, '--override-target') ? { overrideTarget: value(values, '--override-target') } : {}),
319
+ ...(value(values, '--override-scope') ? { overrideScope: value(values, '--override-scope') } : {}),
320
+ ...(value(values, '--alert-number') ? { alertNumber: Number(value(values, '--alert-number')) } : {}),
321
+ ...(value(values, '--issue-number') ? { issueNumber: Number(value(values, '--issue-number')) } : {}),
322
+ ...(values['--dry-run'] === true ? { dryRun: true } : {})
323
+ };
324
+ return { family, request: input };
325
+ }
326
+ const values = parseValues(args, 2, ORCHESTRATION_FLAGS);
327
+ const orchestrationIntents = new Set([
328
+ 'begin-or-resume', 'route', 'prepare', 'dispatch', 'await-activation', 'recover-prepared',
329
+ 'hook-start', 'hook-stop', 'advance', 'pause', 'status'
330
+ ]);
331
+ if (!orchestrationIntents.has(intent)) {
332
+ operationInvalid(`unknown orchestration intent '${intent}'`);
333
+ }
334
+ const client = value(values, '--client');
335
+ if (client !== undefined && !isAgentClientId(client))
336
+ operationInvalid(`unknown client '${client}'`);
337
+ const parsedIntent = intent;
338
+ const input = {};
339
+ if (parsedIntent === 'begin-or-resume' || parsedIntent === 'prepare') {
340
+ required(values, ['--client']);
341
+ if (!client || !isAgentClientId(client))
342
+ operationInvalid(`unknown client '${client ?? ''}'`);
343
+ input.client = client;
344
+ }
345
+ if (parsedIntent === 'begin-or-resume') {
346
+ const maxSteps = value(values, '--max-steps');
347
+ if (maxSteps !== undefined) {
348
+ const parsed = Number(maxSteps);
349
+ if (!Number.isInteger(parsed) || parsed < 1)
350
+ operationInvalid('--max-steps must be a positive integer');
351
+ input.maxSteps = parsed;
352
+ }
353
+ const policyFlags = ['--executor-model', '--executor-reasoning-effort', '--reviewer-model', '--reviewer-reasoning-effort'];
354
+ const hasPolicy = policyFlags.some((flag) => value(values, flag) !== undefined);
355
+ if (hasPolicy && policyFlags.some((flag) => value(values, flag) === undefined)) {
356
+ operationInvalid('explicit model policy requires executor/reviewer model and reasoning effort');
357
+ }
358
+ if (hasPolicy) {
359
+ input.modelPolicy = {
360
+ executor: { model: value(values, '--executor-model'), reasoningEffort: value(values, '--executor-reasoning-effort') },
361
+ reviewer: { model: value(values, '--reviewer-model'), reasoningEffort: value(values, '--reviewer-reasoning-effort') }
362
+ };
363
+ }
364
+ }
365
+ if (parsedIntent === 'prepare') {
366
+ input.requestedModel = value(values, '--requested-model');
367
+ input.requestedReasoningEffort = value(values, '--requested-reasoning-effort');
368
+ input.capabilityToken = value(values, '--capability-token');
369
+ }
370
+ if (parsedIntent === 'await-activation') {
371
+ required(values, ['--stage', '--round', '--artifact', '--role']);
372
+ const round = Number(value(values, '--round'));
373
+ if (!Number.isSafeInteger(round) || round < 1)
374
+ operationInvalid('--round must be a positive integer');
375
+ const role = value(values, '--role');
376
+ if (role !== 'executor' && role !== 'reviewer')
377
+ operationInvalid('--role must be executor or reviewer');
378
+ input.event = { stage: value(values, '--stage'), round, artifact: value(values, '--artifact'), role };
379
+ }
380
+ if (parsedIntent === 'hook-start') {
381
+ required(values, ['--native-agent', '--child-id', '--parent-id']);
382
+ if (taskRef === 'auto')
383
+ required(values, ['--client']);
384
+ input.auto = taskRef === 'auto';
385
+ input.client = value(values, '--client');
386
+ input.event = {
387
+ nativeAgent: value(values, '--native-agent'), childId: value(values, '--child-id'), parentId: value(values, '--parent-id'),
388
+ spawnMode: value(values, '--spawn-mode'), actualModel: value(values, '--actual-model'),
389
+ actualReasoningEffort: value(values, '--actual-reasoning-effort'), modelFallbackReason: value(values, '--model-fallback-reason'),
390
+ reasoningEffortFallbackReason: value(values, '--reasoning-effort-fallback-reason')
391
+ };
392
+ }
393
+ if (parsedIntent === 'hook-stop') {
394
+ input.auto = taskRef === 'auto';
395
+ if (input.auto) {
396
+ required(values, ['--client', '--native-agent', '--child-id']);
397
+ input.client = value(values, '--client');
398
+ input.event = {
399
+ nativeAgent: value(values, '--native-agent'), childId: value(values, '--child-id'), actualModel: value(values, '--actual-model'),
400
+ actualReasoningEffort: value(values, '--actual-reasoning-effort'), modelFallbackReason: value(values, '--model-fallback-reason'),
401
+ reasoningEffortFallbackReason: value(values, '--reasoning-effort-fallback-reason')
402
+ };
403
+ }
404
+ else {
405
+ required(values, ['--child-id', '--exit-code', '--after-fingerprint']);
406
+ const exitCode = Number(value(values, '--exit-code'));
407
+ if (!Number.isInteger(exitCode))
408
+ operationInvalid('--exit-code must be an integer');
409
+ input.event = {
410
+ childId: value(values, '--child-id'), exitCode, afterFingerprint: value(values, '--after-fingerprint'),
411
+ changedPaths: value(values, '--changed-paths')?.split(',').filter(Boolean) ?? [], actualModel: value(values, '--actual-model'),
412
+ actualReasoningEffort: value(values, '--actual-reasoning-effort'), modelFallbackReason: value(values, '--model-fallback-reason'),
413
+ reasoningEffortFallbackReason: value(values, '--reasoning-effort-fallback-reason')
414
+ };
415
+ }
416
+ }
417
+ if (parsedIntent === 'pause') {
418
+ required(values, ['--code', '--message', '--recoverable']);
419
+ const recoverable = value(values, '--recoverable');
420
+ if (recoverable !== 'true' && recoverable !== 'false')
421
+ operationInvalid('--recoverable must be true or false');
422
+ input.code = value(values, '--code');
423
+ input.message = value(values, '--message');
424
+ input.recoverable = recoverable === 'true';
425
+ }
426
+ const options = value(values, '--git-worktree-root') === undefined
427
+ ? undefined
428
+ : { gitWorktreeRoot: value(values, '--git-worktree-root') };
429
+ return { family, taskRef, intent: parsedIntent, input, ...(options ? { options } : {}) };
430
+ }
431
+ //# sourceMappingURL=control-authority.js.map
@@ -1,6 +1,7 @@
1
1
  import { createHash, randomUUID } from 'node:crypto';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
+ import { isValidAgentInfraVersion, VERSION } from '../version.js';
4
5
  import { loadShortIdByTaskId, mutateShortIdRegistry } from './short-id.js';
5
6
  import { TaskExecutionLockError, withTaskExecutionLock } from './task-execution-lock.js';
6
7
  const AGENTS = ['claude', 'codex', 'antigravity', 'opencode', 'cursor'];
@@ -258,6 +259,10 @@ function recoverPublishedTask(activeRoot, repoRoot, keyDigest, candidateDigest)
258
259
  }
259
260
  function createLocalTask(value, options) {
260
261
  const candidate = validateTaskCreateCandidate(value);
262
+ const agentInfraVersion = options.agentInfraVersion ?? VERSION;
263
+ if (!isValidAgentInfraVersion(agentInfraVersion)) {
264
+ throw new Error('TASK_CREATE_VERSION_INVALID: agentInfraVersion must be a valid v-prefixed semver');
265
+ }
261
266
  const repoRoot = fs.realpathSync.native(options.repoRoot);
262
267
  const workspaceRoot = path.join(repoRoot, '.agents', 'workspace');
263
268
  const activeRoot = path.join(workspaceRoot, 'active');
@@ -308,7 +313,7 @@ function createLocalTask(value, options) {
308
313
  throw new Error('TASK_CREATE_TEMPLATE_INVALID');
309
314
  const rendered = renderTask({
310
315
  template: fs.readFileSync(templatePath, 'utf8'), candidate, taskId, project, timestamp,
311
- agentInfraVersion: options.agentInfraVersion ?? 'unknown', keyDigest, candidateDigest
316
+ agentInfraVersion, keyDigest, candidateDigest
312
317
  });
313
318
  const temporary = path.join(workspaceRoot, `.task-create.tmp.${process.pid}.${randomUUID()}`);
314
319
  fs.mkdirSync(temporary, { mode: 0o700 });
@@ -0,0 +1,35 @@
1
+ import { isValidAgentInfraVersion } from '../version.js';
2
+ import { parseTypedTaskFrontmatter } from './frontmatter.js';
3
+ import { LEDGER_SECTION_MISSING_CODE, LEDGER_SECTION_MISSING_MESSAGE, parseLedgerDocument, validateLedgerRows } from './ledger.js';
4
+ function validateCurrentTaskContract(content) {
5
+ let metadata;
6
+ try {
7
+ metadata = parseTypedTaskFrontmatter(content);
8
+ }
9
+ catch (error) {
10
+ return { ok: false, code: 'TASK_DOCUMENT_INVALID', message: error instanceof Error ? error.message : String(error) };
11
+ }
12
+ if (!isValidAgentInfraVersion(metadata.agent_infra_version)) {
13
+ return {
14
+ ok: false,
15
+ code: 'TASK_CURRENT_CONTRACT_INVALID',
16
+ message: `agent_infra_version must be a valid v-prefixed semver (received ${String(metadata.agent_infra_version ?? 'missing')})`
17
+ };
18
+ }
19
+ let ledger;
20
+ try {
21
+ ledger = parseLedgerDocument(content);
22
+ }
23
+ catch (error) {
24
+ return { ok: false, code: 'TASK_DOCUMENT_INVALID', message: error instanceof Error ? error.message : String(error) };
25
+ }
26
+ if (!ledger.present) {
27
+ return { ok: false, code: 'TASK_CURRENT_CONTRACT_INVALID', message: `${LEDGER_SECTION_MISSING_CODE}: ${LEDGER_SECTION_MISSING_MESSAGE}` };
28
+ }
29
+ const invalid = validateLedgerRows(ledger.rows);
30
+ if (invalid)
31
+ return { ok: false, code: 'TASK_CURRENT_CONTRACT_INVALID', message: `${invalid.code}: ${invalid.message}` };
32
+ return { ok: true, metadata, ledger };
33
+ }
34
+ export { validateCurrentTaskContract };
35
+ //# sourceMappingURL=current-contract.js.map