@fitlab-ai/agent-infra 0.9.10 → 0.9.12

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 (375) hide show
  1. package/README.md +19 -9
  2. package/README.zh-CN.md +19 -9
  3. package/bin/cli.ts +17 -4
  4. package/dist/bin/cli.js +19 -4
  5. package/dist/bin/internal-cli.js +55 -4
  6. package/dist/lib/agent-client.js +0 -1
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +71 -10
  8. package/dist/lib/agent-clients/adapters/opencode-sandbox.js +0 -4
  9. package/dist/lib/agent-clients/adapters/traecli.js +7 -8
  10. package/dist/lib/agent-clients/config.js +14 -99
  11. package/dist/lib/agent-clients/reconcile.js +1 -11
  12. package/dist/lib/agent-clients/schema.js +5 -4
  13. package/dist/lib/agent-clients/tokens.js +5 -15
  14. package/dist/lib/decide.js +14 -30
  15. package/dist/lib/defaults.json +8 -0
  16. package/dist/lib/git/workflow.js +43 -1
  17. package/dist/lib/init.js +3 -2
  18. package/dist/lib/internal/git-workflow.js +8 -1
  19. package/dist/lib/internal/platform-pr.js +8 -2
  20. package/dist/lib/internal/release-workflow.js +0 -1
  21. package/dist/lib/internal/task-delivery.js +59 -0
  22. package/dist/lib/internal/task-finalization.js +19 -9
  23. package/dist/lib/internal/task-ledger.js +8 -2
  24. package/dist/lib/internal/task-lifecycle.js +17 -93
  25. package/dist/lib/internal/task-orchestration.js +30 -216
  26. package/dist/lib/internal/task-validate.js +2 -1
  27. package/dist/lib/merge.js +205 -103
  28. package/dist/lib/platform/github-client.js +7 -1
  29. package/dist/lib/platform/pr-checks.js +8 -5
  30. package/dist/lib/platform/pr-summary.js +12 -8
  31. package/dist/lib/platform/pull-requests.js +393 -91
  32. package/dist/lib/platform/verification-required.js +13 -2
  33. package/dist/lib/platform/verification-sync.js +9 -4
  34. package/dist/lib/pr-review/evidence-grading.js +12 -4
  35. package/dist/lib/process-data/normalize.js +2 -9
  36. package/dist/lib/run/host.js +46 -16
  37. package/dist/lib/sandbox/agent-client-reconciler.js +4 -12
  38. package/dist/lib/sandbox/commands/create.js +3 -3
  39. package/dist/lib/sandbox/commands/enter.js +18 -4
  40. package/dist/lib/sandbox/config.js +8 -12
  41. package/dist/lib/sandbox/control/executor.js +131 -39
  42. package/dist/lib/sandbox/control/lifecycle.js +55 -24
  43. package/dist/lib/sandbox/control/server.js +18 -16
  44. package/dist/lib/sandbox/mounts.js +3 -2
  45. package/dist/lib/sandbox/recovery.js +123 -22
  46. package/dist/lib/sandbox/workspace-identity.js +14 -0
  47. package/dist/lib/sandbox/workspace-view.js +8 -9
  48. package/dist/lib/self-update.js +278 -0
  49. package/dist/lib/server/process-state.js +2 -2
  50. package/dist/lib/server/protocol.js +5 -5
  51. package/dist/lib/task/activity-log.js +4 -1
  52. package/dist/lib/task/artifact-lifecycle.js +6 -4
  53. package/dist/lib/task/codex-orchestration.js +1 -1
  54. package/dist/lib/task/command-options.js +1 -3
  55. package/dist/lib/task/commands/cat.js +0 -5
  56. package/dist/lib/task/commands/decisions.js +76 -92
  57. package/dist/lib/task/commands/files.js +6 -6
  58. package/dist/lib/task/commands/grep.js +25 -61
  59. package/dist/lib/task/commands/issue-body.js +6 -6
  60. package/dist/lib/task/commands/log.js +25 -10
  61. package/dist/lib/task/commands/show.js +5 -6
  62. package/dist/lib/task/commands/status.js +8 -8
  63. package/dist/lib/task/commit-intent.js +100 -0
  64. package/dist/lib/task/commit-operation.js +181 -91
  65. package/dist/lib/task/commit-policy.js +1 -1
  66. package/dist/lib/task/control-authority.js +431 -0
  67. package/dist/lib/task/create.js +39 -8
  68. package/dist/lib/task/current-contract.js +35 -0
  69. package/dist/lib/task/decision-details.js +218 -0
  70. package/dist/lib/task/decision-intents.js +5 -2
  71. package/dist/lib/task/delivery-target.js +97 -0
  72. package/dist/lib/task/delivery.js +153 -0
  73. package/dist/lib/task/events.js +11 -5
  74. package/dist/lib/task/human-override.js +5 -2
  75. package/dist/lib/task/index.js +19 -20
  76. package/dist/lib/task/ledger-intents.js +8 -9
  77. package/dist/lib/task/ledger.js +13 -6
  78. package/dist/lib/task/lifecycle.js +122 -9
  79. package/dist/lib/task/orchestration.js +105 -75
  80. package/dist/lib/task/pr-delivery-fact.js +182 -0
  81. package/dist/lib/task/review-artifacts.js +31 -12
  82. package/dist/lib/task/review-finalization.js +13 -4
  83. package/dist/lib/task/review-fingerprint.js +8 -0
  84. package/dist/lib/task/verification-engine.js +86 -39
  85. package/dist/lib/task/verification-review.js +2 -2
  86. package/dist/lib/task/workspace-snapshot.js +5 -11
  87. package/dist/lib/task/write.js +11 -1
  88. package/dist/lib/update.js +13 -35
  89. package/dist/lib/version.js +5 -1
  90. package/dist/lifecycle-build-manifest.json +3 -3
  91. package/lib/agent-client.ts +0 -1
  92. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +105 -10
  93. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +12 -1
  94. package/lib/agent-clients/adapters/opencode-sandbox.ts +0 -5
  95. package/lib/agent-clients/adapters/traecli.ts +7 -8
  96. package/lib/agent-clients/config.ts +14 -144
  97. package/lib/agent-clients/reconcile.ts +1 -12
  98. package/lib/agent-clients/schema.ts +5 -4
  99. package/lib/agent-clients/tokens.ts +5 -18
  100. package/lib/decide.ts +12 -22
  101. package/lib/defaults.json +8 -0
  102. package/lib/git/workflow.ts +38 -1
  103. package/lib/init.ts +5 -2
  104. package/lib/internal/git-workflow.ts +7 -1
  105. package/lib/internal/platform-pr.ts +8 -1
  106. package/lib/internal/release-workflow.ts +0 -1
  107. package/lib/internal/task-delivery.ts +53 -0
  108. package/lib/internal/task-finalization.ts +27 -13
  109. package/lib/internal/task-ledger.ts +8 -2
  110. package/lib/internal/task-lifecycle.ts +33 -76
  111. package/lib/internal/task-orchestration.ts +33 -190
  112. package/lib/internal/task-validate.ts +3 -2
  113. package/lib/merge.ts +236 -120
  114. package/lib/platform/github-client.ts +8 -1
  115. package/lib/platform/pr-checks.ts +7 -5
  116. package/lib/platform/pr-summary.ts +10 -7
  117. package/lib/platform/pull-requests.ts +425 -87
  118. package/lib/platform/verification-required.ts +12 -2
  119. package/lib/platform/verification-sync.ts +9 -4
  120. package/lib/pr-review/evidence-grading.ts +7 -4
  121. package/lib/process-data/normalize.ts +2 -8
  122. package/lib/run/host.ts +63 -14
  123. package/lib/sandbox/agent-client-reconciler.ts +5 -15
  124. package/lib/sandbox/commands/create.ts +4 -4
  125. package/lib/sandbox/commands/enter.ts +24 -4
  126. package/lib/sandbox/config.ts +9 -14
  127. package/lib/sandbox/control/container-identity.ts +2 -2
  128. package/lib/sandbox/control/executor.ts +148 -51
  129. package/lib/sandbox/control/lifecycle.ts +59 -31
  130. package/lib/sandbox/control/protocol.ts +1 -3
  131. package/lib/sandbox/control/server.ts +15 -12
  132. package/lib/sandbox/control/state.ts +7 -7
  133. package/lib/sandbox/mounts.ts +4 -3
  134. package/lib/sandbox/recovery.ts +162 -34
  135. package/lib/sandbox/tools.ts +1 -1
  136. package/lib/sandbox/workspace-identity.ts +28 -0
  137. package/lib/sandbox/workspace-view.ts +14 -10
  138. package/lib/self-update.ts +341 -0
  139. package/lib/server/process-state.ts +2 -2
  140. package/lib/server/protocol.ts +5 -5
  141. package/lib/task/activity-log.ts +6 -1
  142. package/lib/task/artifact-lifecycle.ts +7 -5
  143. package/lib/task/codex-orchestration.ts +2 -1
  144. package/lib/task/command-options.ts +1 -2
  145. package/lib/task/commands/cat.ts +0 -4
  146. package/lib/task/commands/decisions.ts +80 -75
  147. package/lib/task/commands/files.ts +6 -6
  148. package/lib/task/commands/grep.ts +23 -51
  149. package/lib/task/commands/issue-body.ts +6 -6
  150. package/lib/task/commands/log.ts +22 -10
  151. package/lib/task/commands/show.ts +5 -6
  152. package/lib/task/commands/status.ts +8 -8
  153. package/lib/task/commit-intent.ts +137 -0
  154. package/lib/task/commit-operation.ts +161 -79
  155. package/lib/task/commit-policy.ts +1 -2
  156. package/lib/task/control-authority.ts +563 -0
  157. package/lib/task/create.ts +40 -10
  158. package/lib/task/current-contract.ts +49 -0
  159. package/lib/task/decision-details.ts +280 -0
  160. package/lib/task/decision-intents.ts +7 -2
  161. package/lib/task/delivery-target.ts +130 -0
  162. package/lib/task/delivery.ts +160 -0
  163. package/lib/task/events.ts +10 -5
  164. package/lib/task/human-override.ts +5 -2
  165. package/lib/task/index.ts +19 -20
  166. package/lib/task/ledger-intents.ts +6 -8
  167. package/lib/task/ledger.ts +17 -7
  168. package/lib/task/lifecycle.ts +113 -4
  169. package/lib/task/orchestration.ts +102 -80
  170. package/lib/task/pr-delivery-fact.ts +254 -0
  171. package/lib/task/review-artifacts.ts +53 -15
  172. package/lib/task/review-finalization.ts +21 -5
  173. package/lib/task/review-fingerprint.ts +10 -0
  174. package/lib/task/verification-engine.ts +78 -46
  175. package/lib/task/verification-review.ts +3 -1
  176. package/lib/task/workspace-snapshot.ts +4 -13
  177. package/lib/task/write.ts +20 -1
  178. package/lib/update.ts +14 -38
  179. package/lib/version.ts +6 -1
  180. package/package.json +1 -1
  181. package/templates/.agents/README.en.md +5 -3
  182. package/templates/.agents/README.zh-CN.md +5 -3
  183. package/templates/.agents/rules/README.en.md +4 -2
  184. package/templates/.agents/rules/README.zh-CN.md +4 -2
  185. package/templates/.agents/rules/compatibility-policy.en.md +5 -4
  186. package/templates/.agents/rules/compatibility-policy.zh-CN.md +5 -4
  187. package/templates/.agents/rules/create-issue.en.md +11 -2
  188. package/templates/.agents/rules/create-issue.zh-CN.md +24 -2
  189. package/templates/.agents/rules/human-decision-context.en.md +9 -7
  190. package/templates/.agents/rules/human-decision-context.zh-CN.md +9 -7
  191. package/templates/.agents/rules/{issue-fields.github.en.md → issue-fields.en.md} +1 -1
  192. package/templates/.agents/rules/{issue-fields.github.zh-CN.md → issue-fields.zh-CN.md} +1 -1
  193. package/templates/.agents/rules/issue-pr-commands.en.md +5 -18
  194. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
  195. package/templates/.agents/rules/issue-sync.en.md +26 -5
  196. package/templates/.agents/rules/issue-sync.zh-CN.md +62 -5
  197. package/templates/.agents/rules/label-milestone-setup.en.md +15 -2
  198. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +15 -2
  199. package/templates/.agents/rules/local-artifact-repair.en.md +46 -0
  200. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +46 -0
  201. package/templates/.agents/rules/milestone-inference.en.md +12 -2
  202. package/templates/.agents/rules/milestone-inference.zh-CN.md +25 -3
  203. package/templates/.agents/rules/next-step-output.en.md +3 -3
  204. package/templates/.agents/rules/next-step-output.zh-CN.md +3 -3
  205. package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -1
  206. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -1
  207. package/templates/.agents/rules/pr-sync.en.md +23 -14
  208. package/templates/.agents/rules/pr-sync.zh-CN.md +23 -2
  209. package/templates/.agents/rules/release-commands.en.md +42 -3
  210. package/templates/.agents/rules/release-commands.zh-CN.md +42 -3
  211. package/templates/.agents/rules/review-handshake.en.md +1 -1
  212. package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
  213. package/templates/.agents/rules/security-alerts.en.md +17 -2
  214. package/templates/.agents/rules/security-alerts.zh-CN.md +17 -2
  215. package/templates/.agents/rules/sync-content-generation.en.md +8 -0
  216. package/templates/.agents/rules/sync-content-generation.zh-CN.md +8 -0
  217. package/templates/.agents/rules/task-management.en.md +2 -3
  218. package/templates/.agents/rules/task-management.zh-CN.md +2 -3
  219. package/templates/.agents/scripts/security-alerts.github.sh +151 -0
  220. package/templates/.agents/scripts/security-alerts.sh +31 -0
  221. package/templates/.agents/skills/analyze-task/SKILL.en.md +4 -3
  222. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +4 -3
  223. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  224. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  225. package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
  226. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
  227. package/templates/.agents/skills/check-task/SKILL.en.md +2 -2
  228. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +2 -2
  229. package/templates/.agents/skills/close-codescan/SKILL.en.md +3 -3
  230. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -3
  231. package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -3
  232. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -3
  233. package/templates/.agents/skills/code-task/SKILL.en.md +8 -5
  234. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +9 -6
  235. package/templates/.agents/skills/commit/SKILL.en.md +30 -10
  236. package/templates/.agents/skills/commit/SKILL.zh-CN.md +6 -7
  237. package/templates/.agents/skills/commit/config/verify.json +1 -1
  238. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +3 -3
  239. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +3 -3
  240. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  241. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  242. package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -2
  243. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -2
  244. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +7 -6
  245. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +7 -6
  246. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  247. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  248. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
  249. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
  250. package/templates/.agents/skills/complete-task/SKILL.en.md +13 -13
  251. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +13 -13
  252. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +2 -2
  253. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +2 -2
  254. package/templates/.agents/skills/create-pr/SKILL.en.md +9 -11
  255. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +9 -11
  256. package/templates/.agents/skills/create-pr/reference/change-report.en.md +9 -4
  257. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +9 -4
  258. package/templates/.agents/skills/create-pr/scripts/change-report.mjs +203 -0
  259. package/templates/.agents/skills/create-release-note/SKILL.en.md +5 -6
  260. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +5 -6
  261. package/templates/.agents/skills/create-task/SKILL.en.md +2 -1
  262. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +2 -1
  263. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  264. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  265. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  266. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  267. package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
  268. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
  269. package/templates/.agents/skills/init-labels/SKILL.en.md +9 -10
  270. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +9 -10
  271. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +57 -1
  272. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +27 -1
  273. package/templates/.agents/skills/init-milestones/SKILL.en.md +3 -5
  274. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +3 -5
  275. package/templates/.agents/skills/plan-task/SKILL.en.md +4 -3
  276. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -3
  277. package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
  278. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
  279. package/templates/.agents/skills/review-analysis/SKILL.en.md +8 -5
  280. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +8 -5
  281. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -0
  282. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -0
  283. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +18 -0
  284. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +18 -0
  285. package/templates/.agents/skills/review-code/SKILL.en.md +11 -8
  286. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +11 -8
  287. package/templates/.agents/skills/review-code/config/verify.en.json +1 -0
  288. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -0
  289. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +2 -1
  290. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +2 -1
  291. package/templates/.agents/skills/review-code/reference/output-templates.en.md +19 -1
  292. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +19 -1
  293. package/templates/.agents/skills/review-code/reference/report-template.en.md +4 -2
  294. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +4 -2
  295. package/templates/.agents/skills/review-plan/SKILL.en.md +8 -5
  296. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +8 -5
  297. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -0
  298. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -0
  299. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +18 -0
  300. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +18 -0
  301. package/templates/.agents/skills/review-pr/SKILL.en.md +1 -0
  302. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +1 -0
  303. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +2 -2
  304. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +2 -2
  305. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -0
  306. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -0
  307. package/templates/.agents/skills/run-task/SKILL.en.md +5 -1
  308. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -1
  309. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +2 -2
  310. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +2 -2
  311. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +144 -109
  312. package/templates/.agents/skills/watch-pr/SKILL.en.md +5 -5
  313. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +5 -5
  314. package/templates/.agents/templates/task.en.md +6 -2
  315. package/templates/.agents/templates/task.zh-CN.md +6 -2
  316. package/templates/.agents/workflows/bug-fix.en.yaml +9 -8
  317. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +9 -8
  318. package/templates/.agents/workflows/feature-development.en.yaml +9 -8
  319. package/templates/.agents/workflows/feature-development.zh-CN.yaml +9 -8
  320. package/templates/.agents/workflows/refactoring.en.yaml +9 -8
  321. package/templates/.agents/workflows/refactoring.zh-CN.yaml +9 -8
  322. package/templates/.claude/commands/analyze-task.en.md +1 -1
  323. package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
  324. package/templates/.claude/commands/block-task.en.md +1 -1
  325. package/templates/.claude/commands/block-task.zh-CN.md +1 -1
  326. package/templates/.claude/commands/cancel-task.en.md +1 -1
  327. package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
  328. package/templates/.claude/commands/check-task.en.md +1 -1
  329. package/templates/.claude/commands/check-task.zh-CN.md +1 -1
  330. package/templates/.claude/commands/code-task.en.md +1 -1
  331. package/templates/.claude/commands/code-task.zh-CN.md +1 -1
  332. package/templates/.claude/commands/commit.en.md +1 -1
  333. package/templates/.claude/commands/commit.zh-CN.md +1 -1
  334. package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
  335. package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
  336. package/templates/.claude/commands/complete-task.en.md +1 -1
  337. package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
  338. package/templates/.claude/commands/create-pr.en.md +1 -1
  339. package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
  340. package/templates/.claude/commands/plan-task.en.md +1 -1
  341. package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
  342. package/templates/.claude/commands/review-analysis.en.md +1 -1
  343. package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
  344. package/templates/.claude/commands/review-code.en.md +1 -1
  345. package/templates/.claude/commands/review-code.zh-CN.md +1 -1
  346. package/templates/.claude/commands/review-plan.en.md +1 -1
  347. package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
  348. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  349. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  350. package/templates/.claude/commands/run-task.en.md +1 -1
  351. package/templates/.claude/commands/run-task.zh-CN.md +1 -1
  352. package/templates/.claude/commands/watch-pr.en.md +1 -1
  353. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  354. package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.en.md +0 -1
  355. package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.zh-CN.md +0 -1
  356. package/dist/lib/builtin-tuis.js +0 -40
  357. package/lib/builtin-tuis.ts +0 -56
  358. package/templates/.agents/rules/create-issue.github.en.md +0 -14
  359. package/templates/.agents/rules/create-issue.github.zh-CN.md +0 -27
  360. package/templates/.agents/rules/issue-pr-commands.github.en.md +0 -34
  361. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +0 -49
  362. package/templates/.agents/rules/issue-sync.github.en.md +0 -40
  363. package/templates/.agents/rules/issue-sync.github.zh-CN.md +0 -76
  364. package/templates/.agents/rules/label-milestone-setup.github.en.md +0 -60
  365. package/templates/.agents/rules/label-milestone-setup.github.zh-CN.md +0 -60
  366. package/templates/.agents/rules/milestone-inference.github.en.md +0 -15
  367. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +0 -27
  368. package/templates/.agents/rules/pr-checks-commands.github.en.md +0 -18
  369. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +0 -34
  370. package/templates/.agents/rules/pr-sync.github.en.md +0 -43
  371. package/templates/.agents/rules/pr-sync.github.zh-CN.md +0 -69
  372. package/templates/.agents/rules/release-commands.github.en.md +0 -38
  373. package/templates/.agents/rules/release-commands.github.zh-CN.md +0 -38
  374. package/templates/.agents/rules/security-alerts.github.en.md +0 -43
  375. package/templates/.agents/rules/security-alerts.github.zh-CN.md +0 -43
@@ -5,6 +5,7 @@ import { hasPlatformCapability } from "./adapters.js";
5
5
  import { inspectRequiredChecks } from "./pr-checks.js";
6
6
  import { resolvePlatformContext } from "./context.js";
7
7
  import { resolveReviewedHeadRelation } from "./merged-pr-equivalence.js";
8
+ import { readPrDeliveryFact } from "../task/pr-delivery-fact.js";
8
9
  const CHECK_TYPE = "required-checks";
9
10
  const SHA_PATTERN = /^[0-9a-f]{7,40}$/i;
10
11
  function validPrNumber(value) {
@@ -30,7 +31,13 @@ function readHead(repoRoot) {
30
31
  }
31
32
  export function evaluateRequiredChecks(context, shared) {
32
33
  const { metadata, localHead, inspection, prFlow } = context;
33
- if (prFlow === "disabled" || metadata.pr_status === "skipped" || !validPrNumber(metadata.pr_number)) {
34
+ const factRead = readPrDeliveryFact(metadata);
35
+ if (factRead.status === "invalid") {
36
+ return shared.failResult(CHECK_TYPE, factRead.error.message, "check_failed");
37
+ }
38
+ const fact = factRead.status === "valid" ? factRead.fact : null;
39
+ const prNumber = fact?.state === "bound" ? fact.identity.number : null;
40
+ if (prFlow === "disabled" || fact?.state === "skipped" || !validPrNumber(prNumber)) {
34
41
  return shared.passResult(CHECK_TYPE, "Skipped: required checks are not applicable to this task");
35
42
  }
36
43
  const reviewedHead = String(metadata.last_reviewed_commit || "");
@@ -78,7 +85,11 @@ export function check({ taskDir }, shared) {
78
85
  if (!task.ok)
79
86
  return shared.failResult(CHECK_TYPE, task.message);
80
87
  const prFlow = readPrFlow(shared.repoRoot);
81
- if (prFlow === "disabled" || task.metadata.pr_status === "skipped" || !validPrNumber(task.metadata.pr_number)) {
88
+ const factRead = readPrDeliveryFact(task.metadata);
89
+ if (factRead.status === "invalid")
90
+ return shared.failResult(CHECK_TYPE, factRead.error.message, "check_failed");
91
+ const bound = factRead.status === "valid" && factRead.fact.state === "bound";
92
+ if (prFlow === "disabled" || (factRead.status === "valid" && factRead.fact.state === "skipped") || !bound) {
82
93
  return evaluateRequiredChecks({ metadata: task.metadata, localHead: null, inspection: null, prFlow }, shared);
83
94
  }
84
95
  const platform = resolvePlatformContext({ cwd: shared.repoRoot });
@@ -9,6 +9,7 @@ import { inspectGitHubIssueMetadata, requirementSectionAnchors } from "./issues.
9
9
  import { listRemoteComments } from "./issue-comments.js";
10
10
  import { taskTypeLabel } from "./metadata-labels.js";
11
11
  import { inspectGitHubPullRequest } from "./pull-requests.js";
12
+ import { readPrDeliveryFact } from "../task/pr-delivery-fact.js";
12
13
  const CHECK_TYPE = "platform-sync";
13
14
  const VERSION_LINE_REGEX = /^[0-9]+\.[0-9]+\.x$/;
14
15
  const FRONTMATTER_FIELD_MAP = {
@@ -127,12 +128,16 @@ function buildSyncContext({ taskDir, config, artifactFile }) {
127
128
  return { earlyReturn: failResult(CHECK_TYPE, task.message) };
128
129
  }
129
130
  const issueNumber = parseIssueNumber(task.metadata.issue_number);
130
- const prNumber = parsePrNumber(task.metadata.pr_number);
131
+ const fact = readPrDeliveryFact(task.metadata);
132
+ if (fact.status === "invalid") {
133
+ return { earlyReturn: failResult(CHECK_TYPE, fact.error.message, "check_failed") };
134
+ }
135
+ const prNumber = fact.status === "valid" && fact.fact.state === "bound" ? fact.fact.identity.number : null;
131
136
  if (config.when === "issue_number_exists" && !issueNumber) {
132
137
  return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no issue_number") };
133
138
  }
134
- if (config.when === "pr_number_exists" && !prNumber) {
135
- return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no pr_number") };
139
+ if (config.when === "pr_fact_bound" && !prNumber) {
140
+ return { earlyReturn: passResult(CHECK_TYPE, "Skipped: task has no verified bound pull request") };
136
141
  }
137
142
  if (!issueNumber) {
138
143
  return { earlyReturn: passResult(CHECK_TYPE, "Skipped: platform-sync not required for this task") };
@@ -276,7 +281,7 @@ function fetchRemoteData(context) {
276
281
  if (context.prMarker) {
277
282
  if (!context.prNumber) {
278
283
  return {
279
- earlyReturn: failResult(CHECK_TYPE, "Expected a valid pr_number for PR comment verification", "check_failed")
284
+ earlyReturn: failResult(CHECK_TYPE, "Expected a verified bound pull request for PR comment verification", "check_failed")
280
285
  };
281
286
  }
282
287
  const prCommentsResult = listRemoteComments(githubClient, context.upstreamRepo, context.prNumber, context.taskDir);
@@ -1,6 +1,7 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { parseTaskFrontmatter } from '../task/frontmatter.js';
3
+ import { parseTypedTaskFrontmatter } from '../task/frontmatter.js';
4
+ import { readPrDeliveryFact } from '../task/pr-delivery-fact.js';
4
5
  export class HostResolutionError extends Error {
5
6
  code;
6
7
  constructor(code, message) {
@@ -38,7 +39,7 @@ export function extractClosingIssueNumbers(body) {
38
39
  /**
39
40
  * Scan `.agents/workspace/active/{task-id}/task.md` files for tasks bound to the
40
41
  * PR number or to one of the PR's closing issues. A task is emitted at most once;
41
- * a direct `pr_number` hit takes priority over the issue-number reverse lookup.
42
+ * a verified fact identity hit takes priority over the issue-number reverse lookup.
42
43
  */
43
44
  export function collectHostCandidates(input) {
44
45
  const activeRoot = path.join(input.workspaceRoot, '.agents', 'workspace', 'active');
@@ -62,9 +63,16 @@ export function collectHostCandidates(input) {
62
63
  catch {
63
64
  continue;
64
65
  }
65
- const frontmatter = parseTaskFrontmatter(content);
66
+ let frontmatter;
67
+ try {
68
+ frontmatter = parseTypedTaskFrontmatter(content);
69
+ }
70
+ catch {
71
+ continue;
72
+ }
66
73
  const issueNumber = toPositiveNumber(frontmatter.issue_number);
67
- const prNumber = toPositiveNumber(frontmatter.pr_number);
74
+ const fact = readPrDeliveryFact(frontmatter);
75
+ const prNumber = fact.status === 'valid' && fact.fact.state === 'bound' ? fact.fact.identity.number : null;
68
76
  if (prNumber === input.prNumber) {
69
77
  candidates.push({ taskId: entry.name, taskDir, issueNumber, prNumber });
70
78
  }
@@ -1,4 +1,5 @@
1
1
  import { sha256 } from './store.js';
2
+ import { normalizeAgentToken } from '../agent-clients/tokens.js';
2
3
  const TASK_ID_RE = /TASK-\d{8}-\d{6}/;
3
4
  const ACTIVITY_RE = /^- (\d{4}-\d{2}-\d{2} [^ ]+) — \*\*(.+?)\*\* by ([^ ]+) — (.+)$/gm;
4
5
  const LEDGER_RE = /^\|\s*((?:AN|PL|CD)-\d+)\s*\|\s*(analysis|plan|code)\s*\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|\s*([^|]+?)\s*\|$/gm;
@@ -9,15 +10,7 @@ function recordId(kind, identity, suffix = '') {
9
10
  }
10
11
  function canonicalActor(raw) {
11
12
  const lowered = raw.toLowerCase();
12
- const aliases = {
13
- 'claude-code': 'claude',
14
- 'antigravity-cli': 'antigravity',
15
- 'gemini-cli': 'gemini',
16
- gemini: 'gemini'
17
- };
18
- if (['claude', 'codex', 'antigravity', 'opencode', 'cursor', 'human'].includes(lowered))
19
- return lowered;
20
- return aliases[lowered] ?? null;
13
+ return normalizeAgentToken(lowered);
21
14
  }
22
15
  function nextOccurrence(occurrences, identity) {
23
16
  const occurrence = (occurrences.get(identity) ?? 0) + 1;
@@ -1,26 +1,55 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
- function resolveCommand(file) {
5
- if (process.platform !== 'win32' || path.extname(file)) {
6
- return file;
7
- }
8
- const pathValue = process.env.Path || process.env.PATH || '';
9
- const extensions = (process.env.PATHEXT || '.COM;.EXE;.BAT;.CMD').split(';').filter(Boolean);
10
- for (const dir of pathValue.split(path.delimiter).filter(Boolean)) {
4
+ function resolveCommandDetails(file, platform = process.platform, env = process.env) {
5
+ if (path.isAbsolute(file))
6
+ return { path: file, source: 'absolute' };
7
+ const pathValue = platform === 'win32'
8
+ ? env.Path || env.PATH || ''
9
+ : env.PATH || '';
10
+ const extensions = platform === 'win32'
11
+ ? (path.extname(file)
12
+ ? ['']
13
+ : (env.PATHEXT || '.COM;.EXE;.BAT;.CMD').split(';').filter(Boolean))
14
+ : [''];
15
+ for (const dir of pathValue.split(path.delimiter)) {
11
16
  for (const extension of extensions) {
12
- const lowerCandidate = path.join(dir, `${file}${extension.toLowerCase()}`);
13
- if (fs.existsSync(lowerCandidate))
14
- return lowerCandidate;
15
- const upperCandidate = path.join(dir, `${file}${extension.toUpperCase()}`);
16
- if (fs.existsSync(upperCandidate))
17
- return upperCandidate;
17
+ const candidates = extension
18
+ ? [`${file}${extension.toLowerCase()}`, `${file}${extension.toUpperCase()}`]
19
+ : [file];
20
+ for (const candidateName of candidates) {
21
+ const candidate = path.resolve(dir || '.', candidateName);
22
+ try {
23
+ const stat = fs.statSync(candidate);
24
+ if (!stat.isFile())
25
+ continue;
26
+ if (platform !== 'win32' && (stat.mode & 0o111) === 0)
27
+ continue;
28
+ return {
29
+ path: candidate,
30
+ source: dir ? 'path' : 'current-directory',
31
+ pathEntry: dir
32
+ };
33
+ }
34
+ catch {
35
+ // Keep searching the remaining PATH entries.
36
+ }
37
+ }
18
38
  }
19
39
  }
20
- return file;
40
+ return { path: file, source: 'unresolved' };
41
+ }
42
+ function resolveCommand(file, platform = process.platform, env = process.env) {
43
+ return resolveCommandDetails(file, platform, env).path;
44
+ }
45
+ function resolveCommandFromAbsolutePath(file, platform = process.platform, env = process.env) {
46
+ const resolution = resolveCommandDetails(file, platform, env);
47
+ return resolution.source === 'path' && resolution.pathEntry && path.isAbsolute(resolution.pathEntry)
48
+ ? resolution.path
49
+ : null;
21
50
  }
22
- function needsShell(file) {
23
- return process.platform === 'win32' && /\.(?:bat|cmd)$/i.test(file);
51
+ function needsShell(file, platform = process.platform) {
52
+ return platform === 'win32' && /\.(?:bat|cmd)$/i.test(file);
24
53
  }
25
54
  export async function runHostCommand(command) {
26
55
  const [file, ...args] = command;
@@ -36,4 +65,5 @@ export async function runHostCommand(command) {
36
65
  child.on('close', (exitCode, signal) => resolve({ exitCode, signal }));
37
66
  });
38
67
  }
68
+ export { needsShell, resolveCommand, resolveCommandFromAbsolutePath };
39
69
  //# sourceMappingURL=host.js.map
@@ -15,15 +15,6 @@ function agentInfraTool(home) {
15
15
  setupHint: 'Provides the ai and agent-infra CLI commands inside the sandbox.'
16
16
  };
17
17
  }
18
- function compatAgentClientState(tools) {
19
- return Object.fromEntries(listAgentClientAdapters().map((adapter) => [
20
- adapter.id,
21
- {
22
- enabled: true,
23
- installInSandbox: tools.includes(adapter.id)
24
- }
25
- ]));
26
- }
27
18
  function stableValue(value) {
28
19
  if (Array.isArray(value)) {
29
20
  return value.map(stableValue);
@@ -96,7 +87,7 @@ function hooksByPhase(adapters) {
96
87
  ])));
97
88
  }
98
89
  function createSandboxCapabilityPlan(config) {
99
- const state = config.agentClientState ?? compatAgentClientState(config.tools);
90
+ const state = config.agentClientState;
100
91
  const selectedAgentClients = listInstalledAgentClientAdapters(state);
101
92
  const context = { home: config.home, project: config.project };
102
93
  const selectedTools = selectedAgentClients.map((adapter) => adapter.sandbox.createTool(context));
@@ -113,8 +104,9 @@ function createSandboxCapabilityPlan(config) {
113
104
  const nonClientTools = [];
114
105
  const selectedCustomTools = [];
115
106
  for (const id of config.tools) {
116
- if (isAgentClientId(id))
117
- continue;
107
+ if (isAgentClientId(id)) {
108
+ throw new Error(`Agent Client '${id}' must be provided by agentClientState`);
109
+ }
118
110
  if (id === 'agent-infra') {
119
111
  if (!nonClientTools.some((tool) => tool.id === id)) {
120
112
  nonClientTools.push(agentInfraTool(config.home));
@@ -17,7 +17,7 @@ import { parseSandboxWorkspaceIdentity, resolveSandboxTarget, sameSandboxWorkspa
17
17
  import { createSandboxCapabilityPlan, runSandboxHooks, runBoundedSandboxHookCommand } from '../agent-client-reconciler.js';
18
18
  import { resolveTools, tmpfsSeedStagingPath, tmpfsSeedTargetPath, toolConfigDirCandidates } from '../tools.js';
19
19
  import { assertFreshSandboxReady, hydrateTmpfsSeedEntries, prepareTmpfsMounts, startSandboxControlBroker } from '../recovery.js';
20
- import { acquireSandboxControlReplacement, assertSandboxControlCutoverSnapshot, beginSandboxControlReplacement, captureSandboxControlCutoverSnapshot, commitSandboxControlReplacement, quiesceSandboxControlRoot, recoverSandboxControlReplacement, readSandboxControlManifestForTransition } from '../control/lifecycle.js';
20
+ import { acquireSandboxControlReplacement, assertSandboxControlCutoverSnapshot, beginSandboxControlReplacement, captureSandboxControlCutoverSnapshot, commitSandboxControlReplacement, quiesceSandboxControlRoot, recoverSandboxControlReplacement, readSandboxControlManifest } from '../control/lifecycle.js';
21
21
  import { inspectSandboxControlContainer } from '../control/container-identity.js';
22
22
  import { hostJoin, toEnginePath, volumeArg } from '../engines/wsl2-paths.js';
23
23
  import { sandboxCoreBindMounts } from '../mounts.js';
@@ -879,7 +879,7 @@ export async function create(args) {
879
879
  const recoveryResult = await recoverSandboxControlReplacement(controlPaths.root, replacementLease);
880
880
  const hadExistingControlRoot = hadControlRootBeforeAcquire || recoveryResult === 'restored';
881
881
  const previousManifest = fs.existsSync(controlPaths.manifestPath)
882
- ? readSandboxControlManifestForTransition(controlPaths.manifestPath)
882
+ ? readSandboxControlManifest(controlPaths.manifestPath)
883
883
  : null;
884
884
  if (hadExistingControlRoot) {
885
885
  await quiesceSandboxControlRoot(controlPaths.root);
@@ -1031,7 +1031,7 @@ export async function create(args) {
1031
1031
  ...(target.workspace.mode === 'task-bound' ? { runtimeDir: control.runtimeDir } : {}),
1032
1032
  ...(target.workspace.mode === 'task-bound'
1033
1033
  ? {
1034
- taskSource: assertSandboxTaskSource(effectiveConfig.repoRoot, target.workspace.taskId),
1034
+ taskSources: [assertSandboxTaskSource(effectiveConfig.repoRoot, target.workspace.taskId)],
1035
1035
  taskId: target.workspace.taskId
1036
1036
  }
1037
1037
  : {})
@@ -1,5 +1,5 @@
1
1
  import { loadConfig } from '../config.js';
2
- import { assertValidBranchName, containerNameCandidates, sandboxBranchLabel, sandboxLabel } from '../constants.js';
2
+ import { assertValidBranchName, containerNameCandidates, sandboxBranchLabel, sandboxLabel, sandboxTaskIdLabel, sandboxWorkspaceModeLabel } from '../constants.js';
3
3
  import { detectEngine } from '../engine.js';
4
4
  import { ensureSandboxReady } from '../recovery.js';
5
5
  import { createSandboxCapabilityPlan, runBoundedSandboxHookCommand, runSandboxHooks } from '../agent-client-reconciler.js';
@@ -9,7 +9,7 @@ import { redactCommandError } from '../redaction.js';
9
9
  import { runInteractiveWithClipboardBridge } from '../clipboard/bridge.js';
10
10
  import { detectHostTimezone } from '../host-timezone.js';
11
11
  import { fetchSandboxRows, selectSandboxContainer, } from './list-running.js';
12
- import { resolveSandboxTarget } from '../workspace-identity.js';
12
+ import { resolveSandboxReentryContext, resolveSandboxTarget } from '../workspace-identity.js';
13
13
  const USAGE = `Usage: ai sandbox exec [--recreate] <branch | TASK-id | N> [cmd...]
14
14
 
15
15
  N references an active task short id from
@@ -81,16 +81,30 @@ export async function enter(args) {
81
81
  const target = resolveSandboxTarget(parsed.target, config.repoRoot);
82
82
  const branch = target.branch;
83
83
  assertValidBranchName(branch);
84
- const { running, nonRunning } = fetchSandboxRows(engine, sandboxLabel(config), sandboxBranchLabel(config));
84
+ const { running, nonRunning } = fetchSandboxRows(engine, sandboxLabel(config), sandboxBranchLabel(config), {
85
+ mode: sandboxWorkspaceModeLabel(config),
86
+ taskId: sandboxTaskIdLabel(config)
87
+ });
85
88
  const found = selectSandboxContainer([...running, ...nonRunning], containerNameCandidates(config, branch));
86
89
  if (!found) {
87
90
  throw new Error(`No sandbox found for branch '${branch}'. Run 'ai sandbox create ${branch}' to create one.`);
88
91
  }
92
+ const containerWorkspace = found.workspaceMode === 'task-bound' && found.taskId
93
+ ? { mode: 'task-bound', taskId: found.taskId }
94
+ : found.workspaceMode === 'branch-only'
95
+ ? { mode: 'branch-only' }
96
+ : { mode: 'legacy-invalid' };
97
+ const reentry = resolveSandboxReentryContext({
98
+ target,
99
+ containerWorkspace,
100
+ repoRoot: config.repoRoot
101
+ });
89
102
  const ready = await ensureSandboxReady({
90
103
  config,
91
104
  engine,
92
105
  branch,
93
- workspace: target.workspace,
106
+ workspace: reentry.workspace,
107
+ reentry: reentry.reentry,
94
108
  row: found,
95
109
  allowRecreate: parsed.recreate,
96
110
  recreate: async () => {
@@ -4,7 +4,6 @@ import { homedir, platform } from 'node:os';
4
4
  import { execFileSync } from 'node:child_process';
5
5
  import pc from 'picocolors';
6
6
  import { normalizeAgentClients } from '../agent-clients/config.js';
7
- import { isAgentClientId } from '../agent-clients/types.js';
8
7
  import { validateSandboxEngine } from './engine.js';
9
8
  import { hostJoin } from './engines/wsl2-paths.js';
10
9
  import { findRuntimeEngineMismatches } from './runtime-engines.js';
@@ -48,15 +47,9 @@ function cloneDefaults() {
48
47
  vm: { ...DEFAULTS.vm }
49
48
  };
50
49
  }
51
- function resolveSandboxToolIds(sandbox, agentClients, defaults) {
50
+ function resolveSandboxToolIds(sandbox, defaults) {
52
51
  if (Array.isArray(sandbox.tools)) {
53
- const tools = sandbox.tools.filter((tool) => typeof tool === 'string');
54
- return agentClients.source === 'canonical'
55
- ? tools.filter((tool) => !isAgentClientId(tool))
56
- : tools;
57
- }
58
- if (agentClients.remainingSandboxTools !== undefined) {
59
- return [...agentClients.remainingSandboxTools];
52
+ return sandbox.tools.filter((tool) => typeof tool === 'string');
60
53
  }
61
54
  return [...defaults];
62
55
  }
@@ -71,10 +64,10 @@ export function loadConfig({ platformFn = platform, writeStderr = (chunk) => pro
71
64
  throw new Error('No .agents/.airc.json found. Run "ai init" first.');
72
65
  }
73
66
  const airc = JSON.parse(fs.readFileSync(configPath, 'utf8'));
74
- const sandboxInput = airc.sandbox;
75
67
  const agentClients = normalizeAgentClients(airc);
76
68
  const defaults = cloneDefaults();
77
69
  const sandbox = airc.sandbox ?? {};
70
+ const delivery = airc.delivery ?? {};
78
71
  const engine = validateSandboxEngine(sandbox.engine ?? defaults.engine, { platformFn });
79
72
  const project = airc.project;
80
73
  if (!project || typeof project !== 'string') {
@@ -99,7 +92,7 @@ export function loadConfig({ platformFn = platform, writeStderr = (chunk) => pro
99
92
  }
100
93
  }
101
94
  const customTools = parseCustomTools(sandbox.customTools, { home });
102
- const tools = resolveSandboxToolIds(sandbox, agentClients, defaults.tools);
95
+ const tools = resolveSandboxToolIds(sandbox, defaults.tools);
103
96
  return {
104
97
  repoRoot,
105
98
  configPath,
@@ -119,7 +112,10 @@ export function loadConfig({ platformFn = platform, writeStderr = (chunk) => pro
119
112
  tools,
120
113
  customTools,
121
114
  agentClientState: agentClients.state,
122
- agentClientSource: agentClients.source,
115
+ delivery: {
116
+ remote: typeof delivery.remote === 'string' ? delivery.remote : 'origin',
117
+ baseRef: typeof delivery.baseRef === 'string' ? delivery.baseRef : 'main'
118
+ },
123
119
  refreshIntervalDays: asNonNegativeIntegerOrDefault(sandbox.refreshIntervalDays, defaults.refreshIntervalDays),
124
120
  dockerfile,
125
121
  vm: {
@@ -1,15 +1,32 @@
1
- import { spawn, spawnSync } from 'node:child_process';
1
+ import { spawn } from 'node:child_process';
2
2
  import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { randomUUID } from 'node:crypto';
5
5
  import { getProcessStartTime } from '../../server/process-state.js';
6
6
  import { createTask } from '../../task/create-service.js';
7
7
  import { applyTaskFinalization } from '../../task/finalization.js';
8
- import { verifyTaskEvent } from '../../task/verification.js';
9
- import { bindSandboxControlTask } from './protocol.js';
10
- import { atomicWriteJson, executionPath, terminateSandboxControlExecution } from './state.js';
8
+ import { bindSandboxControlTask, validateSandboxControlRequest } from './protocol.js';
9
+ import { atomicWriteJson, executionPath, readActiveLease, terminateSandboxControlExecution } from './state.js';
10
+ import { createSandboxExecutorExecutionContext, dispatchTaskControlOperation, parseTaskControlOperation } from '../../task/control-authority.js';
11
+ import { assertSandboxControlBrokerOwner, readSandboxControlManifest } from './lifecycle.js';
11
12
  import { computeLifecycleBuildIdentity } from '../../agent-clients/adapters/codex-lifecycle/build-identity.js';
12
13
  import { closeCodexControllerRegistration, CodexControllerRegistrationError, openCodexControllerRegistration, resolveCodexControllerBinding } from './controller-registration.js';
14
+ function sameBrokerOwner(left, right) {
15
+ return left.version === right.version
16
+ && left.pid === right.pid
17
+ && left.startTime === right.startTime
18
+ && left.brokerId === right.brokerId
19
+ && left.token === right.token
20
+ && left.generation === right.generation;
21
+ }
22
+ export function assertSandboxControlExecutorAuthority(manifest, gateOwner) {
23
+ const owner = assertSandboxControlBrokerOwner(manifest);
24
+ if (gateOwner && !sameBrokerOwner(owner, gateOwner)) {
25
+ throw new Error('SANDBOX_CONTROL_GATE_OWNER_MISMATCH');
26
+ }
27
+ if (readActiveLease(manifest))
28
+ throw new Error('SANDBOX_CONTROL_HANDOFF_ACTIVE');
29
+ }
13
30
  function safeEnv(env) {
14
31
  return Object.fromEntries(Object.entries(env).filter(([key]) => !key.toUpperCase().startsWith('AGENT_INFRA_CONTROL_')));
15
32
  }
@@ -74,12 +91,15 @@ export async function prepareSandboxControlExecution(params) {
74
91
  start(canWrite = () => true) {
75
92
  if (!child.connected)
76
93
  throw new Error('SANDBOX_CONTROL_EXECUTOR_GATE_CLOSED');
94
+ if (!canWrite())
95
+ throw new Error('SANDBOX_CONTROL_OWNER_LOST');
96
+ const gateOwner = assertSandboxControlBrokerOwner(params.manifest);
77
97
  if (!canWrite())
78
98
  throw new Error('SANDBOX_CONTROL_OWNER_LOST');
79
99
  atomicWriteJson(executionPath(params.manifest, params.request.id), { ...execution, phase: 'running', updatedAt: Date.now() });
80
100
  if (!canWrite())
81
101
  throw new Error('SANDBOX_CONTROL_OWNER_LOST');
82
- child.send({ version: 1, nonce });
102
+ child.send({ version: 1, nonce, owner: gateOwner });
83
103
  },
84
104
  completion,
85
105
  terminate(updateState = true) {
@@ -104,11 +124,16 @@ function waitForGate(nonce, timeoutMs = 2_000) {
104
124
  clearTimeout(timer);
105
125
  process.off('disconnect', onDisconnect);
106
126
  const value = message;
107
- if (!value || value.version !== 1 || value.nonce !== nonce) {
127
+ const owner = value?.owner;
128
+ if (!value || value.version !== 1 || value.nonce !== nonce
129
+ || !owner || owner.version !== 3 || !Number.isSafeInteger(owner.pid) || (owner.pid ?? 0) <= 0
130
+ || typeof owner.startTime !== 'number' || !Number.isSafeInteger(owner.startTime)
131
+ || typeof owner.brokerId !== 'string' || owner.brokerId.length === 0
132
+ || typeof owner.token !== 'string' || typeof owner.generation !== 'string') {
108
133
  reject(new Error('SANDBOX_CONTROL_EXECUTOR_GATE_INVALID'));
109
134
  return;
110
135
  }
111
- resolve();
136
+ resolve(owner);
112
137
  });
113
138
  });
114
139
  }
@@ -139,6 +164,16 @@ function orchestrationFailure(code, message) {
139
164
  stderr: ''
140
165
  };
141
166
  }
167
+ function lifecycleFailure(code, message) {
168
+ return {
169
+ exitCode: 1,
170
+ stdout: `${JSON.stringify({
171
+ status: 'failed', changed: false,
172
+ error: { code, message }
173
+ })}\n`,
174
+ stderr: ''
175
+ };
176
+ }
142
177
  function finalizationResult(result) {
143
178
  const payload = {
144
179
  version: 1,
@@ -154,19 +189,7 @@ function finalizationResult(result) {
154
189
  stderr: ''
155
190
  };
156
191
  }
157
- function isCodexPrepare(args) {
158
- if (args[1] !== 'prepare')
159
- return false;
160
- const values = [];
161
- for (let index = 2; index < args.length; index += 1) {
162
- if (args[index] === '--client')
163
- values.push(args[index + 1] ?? '');
164
- else if (args[index]?.startsWith('--client='))
165
- values.push(args[index].slice('--client='.length));
166
- }
167
- return values.length === 1 && values[0] === 'codex';
168
- }
169
- export function executeRequest(manifest, manifestPath, request, options = {}) {
192
+ export async function executeRequest(manifest, manifestPath, request, options = {}) {
170
193
  if (request.family === 'task-create') {
171
194
  const result = createTask(request.candidate, { repoRoot: manifest.repoRoot });
172
195
  return {
@@ -220,12 +243,41 @@ export function executeRequest(manifest, manifestPath, request, options = {}) {
220
243
  }
221
244
  }
222
245
  if (request.family === 'task-finalization') {
223
- return finalizationResult(applyTaskFinalization({ taskRef: manifest.taskId, intent: 'complete', agent: request.agent }, { repoRoot: manifest.repoRoot, preflight: (input, options) => verifyTaskEvent({ ...input, event: 'complete-task.hard-preflight' }, options) }));
246
+ const operation = parseTaskControlOperation('task-finalization', [manifest.taskId, 'complete', '--agent', request.agent]);
247
+ if (operation.family !== 'task-finalization')
248
+ throw new Error('SANDBOX_CONTROL_FINALIZATION_OPERATION_INVALID');
249
+ const result = dispatchTaskControlOperation(createSandboxExecutorExecutionContext({
250
+ repoRoot: manifest.repoRoot,
251
+ worktreeRoot: manifest.worktreeRoot,
252
+ runtimeDir: manifest.runtimeDir,
253
+ taskId: manifest.taskId,
254
+ generation: manifest.generation,
255
+ manifestPath,
256
+ requestId: request.id
257
+ }), operation);
258
+ return finalizationResult(result);
224
259
  }
225
260
  const boundArgs = bindSandboxControlTask(request, manifest.taskId);
261
+ if (request.family === 'task-orchestration') {
262
+ boundArgs.push('--git-worktree-root', manifest.worktreeRoot);
263
+ }
264
+ let operation;
265
+ try {
266
+ operation = parseTaskControlOperation(request.family, boundArgs);
267
+ }
268
+ catch (error) {
269
+ const message = error instanceof Error ? error.message : String(error);
270
+ const code = /^([A-Z][A-Z0-9_]+)/u.exec(message)?.[1] ?? 'TASK_CONTROL_OPERATION_INVALID';
271
+ return request.family === 'task-lifecycle'
272
+ ? lifecycleFailure('LIFECYCLE_PAYLOAD_INVALID', message)
273
+ : orchestrationFailure(code, message);
274
+ }
226
275
  let controllerBinding = null;
227
276
  if (request.family === 'task-orchestration') {
228
- if (isCodexPrepare(request.args)) {
277
+ const codexPrepare = operation.family === 'task-orchestration'
278
+ && operation.intent === 'prepare'
279
+ && operation.input.client === 'codex';
280
+ if (codexPrepare) {
229
281
  if (!request.controllerProof) {
230
282
  return orchestrationFailure('CODEX_SANDBOX_CONTROLLER_PROOF_REQUIRED', 'Codex prepare requires a current controller lease proof');
231
283
  }
@@ -247,33 +299,73 @@ export function executeRequest(manifest, manifestPath, request, options = {}) {
247
299
  else if (request.controllerProof !== null) {
248
300
  return orchestrationFailure('CODEX_SANDBOX_CONTROLLER_PROOF_INVALID', 'Controller proof is only accepted for canonical Codex prepare');
249
301
  }
250
- boundArgs.push('--git-worktree-root', manifest.worktreeRoot);
251
302
  }
252
- const result = (options.spawnDomain ?? spawnSync)(process.execPath, nodeEntryArgs(process.argv[1], [request.family, ...boundArgs]), {
253
- cwd: manifest.repoRoot,
254
- encoding: 'utf8',
255
- env: {
256
- ...safeEnv(process.env),
257
- AGENT_INFRA_RUNTIME_DIR: manifest.runtimeDir,
258
- ...(controllerBinding
259
- ? { AGENT_INFRA_CONTROL_CONTROLLER_BINDING: JSON.stringify(controllerBinding) }
260
- : {})
261
- }
303
+ const context = createSandboxExecutorExecutionContext({
304
+ repoRoot: manifest.repoRoot,
305
+ worktreeRoot: manifest.worktreeRoot,
306
+ runtimeDir: manifest.runtimeDir,
307
+ taskId: manifest.taskId,
308
+ generation: manifest.generation,
309
+ manifestPath,
310
+ requestId: request.id,
311
+ ...(controllerBinding ? { controllerBinding } : {})
262
312
  });
263
- return { exitCode: result.status ?? 1, stdout: result.stdout ?? '', stderr: result.stderr ?? '' };
313
+ const format = (value) => {
314
+ const result = value;
315
+ return {
316
+ exitCode: result.status === 'failed' ? 1 : 0,
317
+ stdout: `${JSON.stringify(value)}\n`,
318
+ stderr: ''
319
+ };
320
+ };
321
+ if (request.family === 'task-orchestration') {
322
+ if (operation.family !== 'task-orchestration')
323
+ throw new Error('SANDBOX_CONTROL_ORCHESTRATION_OPERATION_INVALID');
324
+ return format(await dispatchTaskControlOperation(context, operation));
325
+ }
326
+ if (operation.family !== 'task-lifecycle')
327
+ throw new Error('SANDBOX_CONTROL_LIFECYCLE_OPERATION_INVALID');
328
+ return format(dispatchTaskControlOperation(context, operation));
264
329
  }
265
330
  export async function runSandboxControlExecutor(requestPath, nonce) {
266
- await waitForGate(nonce);
331
+ const gateOwner = await waitForGate(nonce);
267
332
  process.disconnect?.();
268
- const root = fs.realpathSync.native(process.cwd());
269
- const request = JSON.parse(fs.readFileSync(requestPath, 'utf8'));
270
333
  const manifestPath = process.env.AGENT_INFRA_EXECUTOR_MANIFEST;
271
334
  if (!manifestPath)
272
335
  throw new Error('SANDBOX_CONTROL_EXECUTOR_MANIFEST_MISSING');
273
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
336
+ const manifest = readSandboxControlManifest(manifestPath);
337
+ const root = fs.realpathSync.native(process.cwd());
274
338
  if (fs.realpathSync.native(manifest.repoRoot) !== root)
275
339
  throw new Error('SANDBOX_CONTROL_EXECUTOR_ROOT_INVALID');
276
- const result = executeRequest(manifest, manifestPath, request);
340
+ const raw = JSON.parse(fs.readFileSync(requestPath, 'utf8'));
341
+ const request = validateSandboxControlRequest(raw, manifest);
342
+ const requestDirectory = path.resolve(path.dirname(requestPath));
343
+ const processingDirectory = path.resolve(path.join(manifest.processingDir, request.id));
344
+ const channelRequestDirectory = path.resolve(path.join(manifest.channelDir, 'requests'));
345
+ if (requestDirectory !== processingDirectory && requestDirectory !== channelRequestDirectory) {
346
+ throw new Error('SANDBOX_CONTROL_EXECUTOR_REQUEST_INVALID');
347
+ }
348
+ let result;
349
+ try {
350
+ assertSandboxControlExecutorAuthority(manifest, gateOwner);
351
+ result = await executeRequest(manifest, manifestPath, request);
352
+ }
353
+ catch (error) {
354
+ const detail = error instanceof Error ? error.message : String(error);
355
+ const code = /^([A-Z][A-Z0-9_]+)/u.exec(detail)?.[1] ?? 'SANDBOX_CONTROL_EXECUTOR_FAILED';
356
+ result = request.family === 'task-finalization'
357
+ ? {
358
+ exitCode: 1,
359
+ stdout: `${JSON.stringify({
360
+ version: 1, status: 'failed', changed: false, accepted: true, result: null,
361
+ error: { code, message: detail, retryable: false }
362
+ })}\n`,
363
+ stderr: ''
364
+ }
365
+ : request.family === 'task-lifecycle'
366
+ ? lifecycleFailure(code, detail)
367
+ : orchestrationFailure(code, detail);
368
+ }
277
369
  process.stdout.write(result.stdout);
278
370
  process.stderr.write(result.stderr);
279
371
  process.exitCode = result.exitCode;