@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
@@ -1,13 +1,19 @@
1
- import { spawn, spawnSync, type ChildProcess } from 'node:child_process';
1
+ import { spawn, type ChildProcess } 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.ts';
6
6
  import { createTask } from '../../task/create-service.ts';
7
7
  import { applyTaskFinalization } from '../../task/finalization.ts';
8
- import { verifyTaskEvent } from '../../task/verification.ts';
9
- import { bindSandboxControlTask, type SandboxControlExecution, type SandboxControlManifest, type SandboxControlRequest } from './protocol.ts';
10
- import { atomicWriteJson, executionPath, terminateSandboxControlExecution } from './state.ts';
8
+ import { bindSandboxControlTask, validateSandboxControlRequest, type SandboxControlExecution, type SandboxControlManifest, type SandboxControlRequest } from './protocol.ts';
9
+ import { atomicWriteJson, executionPath, readActiveLease, terminateSandboxControlExecution } from './state.ts';
10
+ import {
11
+ createSandboxExecutorExecutionContext,
12
+ dispatchTaskControlOperation,
13
+ parseTaskControlOperation,
14
+ type TaskControlOperation
15
+ } from '../../task/control-authority.ts';
16
+ import { assertSandboxControlBrokerOwner, readSandboxControlManifest, type BrokerOwner } from './lifecycle.ts';
11
17
  import { computeLifecycleBuildIdentity } from '../../agent-clients/adapters/codex-lifecycle/build-identity.ts';
12
18
  import {
13
19
  closeCodexControllerRegistration,
@@ -29,6 +35,26 @@ export type PreparedSandboxControlExecution = {
29
35
  terminate(updateState?: boolean): boolean;
30
36
  };
31
37
 
38
+ function sameBrokerOwner(left: BrokerOwner, right: BrokerOwner): boolean {
39
+ return left.version === right.version
40
+ && left.pid === right.pid
41
+ && left.startTime === right.startTime
42
+ && left.brokerId === right.brokerId
43
+ && left.token === right.token
44
+ && left.generation === right.generation;
45
+ }
46
+
47
+ export function assertSandboxControlExecutorAuthority(
48
+ manifest: SandboxControlManifest,
49
+ gateOwner?: BrokerOwner
50
+ ): void {
51
+ const owner = assertSandboxControlBrokerOwner(manifest);
52
+ if (gateOwner && !sameBrokerOwner(owner, gateOwner)) {
53
+ throw new Error('SANDBOX_CONTROL_GATE_OWNER_MISMATCH');
54
+ }
55
+ if (readActiveLease(manifest)) throw new Error('SANDBOX_CONTROL_HANDOFF_ACTIVE');
56
+ }
57
+
32
58
  function safeEnv(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {
33
59
  return Object.fromEntries(Object.entries(env).filter(([key]) => !key.toUpperCase().startsWith('AGENT_INFRA_CONTROL_')));
34
60
  }
@@ -104,9 +130,11 @@ export async function prepareSandboxControlExecution(params: {
104
130
  start(canWrite = () => true) {
105
131
  if (!child.connected) throw new Error('SANDBOX_CONTROL_EXECUTOR_GATE_CLOSED');
106
132
  if (!canWrite()) throw new Error('SANDBOX_CONTROL_OWNER_LOST');
133
+ const gateOwner = assertSandboxControlBrokerOwner(params.manifest);
134
+ if (!canWrite()) throw new Error('SANDBOX_CONTROL_OWNER_LOST');
107
135
  atomicWriteJson(executionPath(params.manifest, params.request.id), { ...execution, phase: 'running', updatedAt: Date.now() });
108
136
  if (!canWrite()) throw new Error('SANDBOX_CONTROL_OWNER_LOST');
109
- child.send({ version: 1, nonce });
137
+ child.send({ version: 1, nonce, owner: gateOwner });
110
138
  },
111
139
  completion,
112
140
  terminate(updateState = true) {
@@ -120,7 +148,7 @@ export async function prepareSandboxControlExecution(params: {
120
148
  };
121
149
  }
122
150
 
123
- function waitForGate(nonce: string, timeoutMs = 2_000): Promise<void> {
151
+ function waitForGate(nonce: string, timeoutMs = 2_000): Promise<BrokerOwner> {
124
152
  return new Promise((resolve, reject) => {
125
153
  const timer = setTimeout(() => reject(new Error('SANDBOX_CONTROL_EXECUTOR_GATE_TIMEOUT')), timeoutMs);
126
154
  const onDisconnect = () => {
@@ -131,12 +159,21 @@ function waitForGate(nonce: string, timeoutMs = 2_000): Promise<void> {
131
159
  process.once('message', (message: unknown) => {
132
160
  clearTimeout(timer);
133
161
  process.off('disconnect', onDisconnect);
134
- const value = message as { version?: unknown; nonce?: unknown } | null;
135
- if (!value || value.version !== 1 || value.nonce !== nonce) {
162
+ const value = message as {
163
+ version?: unknown;
164
+ nonce?: unknown;
165
+ owner?: Partial<BrokerOwner> | null;
166
+ } | null;
167
+ const owner = value?.owner;
168
+ if (!value || value.version !== 1 || value.nonce !== nonce
169
+ || !owner || owner.version !== 3 || !Number.isSafeInteger(owner.pid) || (owner.pid ?? 0) <= 0
170
+ || typeof owner.startTime !== 'number' || !Number.isSafeInteger(owner.startTime)
171
+ || typeof owner.brokerId !== 'string' || owner.brokerId.length === 0
172
+ || typeof owner.token !== 'string' || typeof owner.generation !== 'string') {
136
173
  reject(new Error('SANDBOX_CONTROL_EXECUTOR_GATE_INVALID'));
137
174
  return;
138
175
  }
139
- resolve();
176
+ resolve(owner as BrokerOwner);
140
177
  });
141
178
  });
142
179
  }
@@ -170,6 +207,17 @@ function orchestrationFailure(code: string, message: string): SandboxControlExec
170
207
  };
171
208
  }
172
209
 
210
+ function lifecycleFailure(code: string, message: string): SandboxControlExecutionResult {
211
+ return {
212
+ exitCode: 1,
213
+ stdout: `${JSON.stringify({
214
+ status: 'failed', changed: false,
215
+ error: { code, message }
216
+ })}\n`,
217
+ stderr: ''
218
+ };
219
+ }
220
+
173
221
  function finalizationResult(result: ReturnType<typeof applyTaskFinalization>): SandboxControlExecutionResult {
174
222
  const payload = {
175
223
  version: 1,
@@ -186,28 +234,17 @@ function finalizationResult(result: ReturnType<typeof applyTaskFinalization>): S
186
234
  };
187
235
  }
188
236
 
189
- function isCodexPrepare(args: readonly string[]): boolean {
190
- if (args[1] !== 'prepare') return false;
191
- const values: string[] = [];
192
- for (let index = 2; index < args.length; index += 1) {
193
- if (args[index] === '--client') values.push(args[index + 1] ?? '');
194
- else if (args[index]?.startsWith('--client=')) values.push(args[index]!.slice('--client='.length));
195
- }
196
- return values.length === 1 && values[0] === 'codex';
197
- }
198
-
199
237
  type ExecuteRequestOptions = Readonly<{
200
238
  buildIdentity?: typeof computeLifecycleBuildIdentity;
201
239
  resolveControllerBinding?: typeof resolveCodexControllerBinding;
202
- spawnDomain?: typeof spawnSync;
203
240
  }>;
204
241
 
205
- export function executeRequest(
242
+ export async function executeRequest(
206
243
  manifest: SandboxControlManifest,
207
244
  manifestPath: string,
208
245
  request: SandboxControlRequest,
209
246
  options: ExecuteRequestOptions = {}
210
- ): SandboxControlExecutionResult {
247
+ ): Promise<SandboxControlExecutionResult> {
211
248
  if (request.family === 'task-create') {
212
249
  const result = createTask(request.candidate, { repoRoot: manifest.repoRoot });
213
250
  return {
@@ -260,20 +297,47 @@ export function executeRequest(
260
297
  }
261
298
  }
262
299
  if (request.family === 'task-finalization') {
263
- return finalizationResult(applyTaskFinalization(
264
- { taskRef: manifest.taskId!, intent: 'complete', agent: request.agent },
265
- { repoRoot: manifest.repoRoot, preflight: (input, options) => verifyTaskEvent(
266
- { ...input, event: 'complete-task.hard-preflight' }, options
267
- ) }
268
- ));
300
+ const operation = parseTaskControlOperation(
301
+ 'task-finalization', [manifest.taskId!, 'complete', '--agent', request.agent]
302
+ );
303
+ if (operation.family !== 'task-finalization') throw new Error('SANDBOX_CONTROL_FINALIZATION_OPERATION_INVALID');
304
+ const result = dispatchTaskControlOperation(
305
+ createSandboxExecutorExecutionContext({
306
+ repoRoot: manifest.repoRoot,
307
+ worktreeRoot: manifest.worktreeRoot,
308
+ runtimeDir: manifest.runtimeDir,
309
+ taskId: manifest.taskId!,
310
+ generation: manifest.generation,
311
+ manifestPath,
312
+ requestId: request.id
313
+ }),
314
+ operation
315
+ );
316
+ return finalizationResult(result);
269
317
  }
270
318
  const boundArgs = bindSandboxControlTask(request, manifest.taskId!);
319
+ if (request.family === 'task-orchestration') {
320
+ boundArgs.push('--git-worktree-root', manifest.worktreeRoot);
321
+ }
322
+ let operation: TaskControlOperation;
323
+ try {
324
+ operation = parseTaskControlOperation(request.family, boundArgs);
325
+ } catch (error) {
326
+ const message = error instanceof Error ? error.message : String(error);
327
+ const code = /^([A-Z][A-Z0-9_]+)/u.exec(message)?.[1] ?? 'TASK_CONTROL_OPERATION_INVALID';
328
+ return request.family === 'task-lifecycle'
329
+ ? lifecycleFailure('LIFECYCLE_PAYLOAD_INVALID', message)
330
+ : orchestrationFailure(code, message);
331
+ }
271
332
  let controllerBinding: Readonly<{
272
333
  instanceDigest: string;
273
334
  controlGeneration: string;
274
335
  }> | null = null;
275
336
  if (request.family === 'task-orchestration') {
276
- if (isCodexPrepare(request.args)) {
337
+ const codexPrepare = operation.family === 'task-orchestration'
338
+ && operation.intent === 'prepare'
339
+ && operation.input.client === 'codex';
340
+ if (codexPrepare) {
277
341
  if (!request.controllerProof) {
278
342
  return orchestrationFailure(
279
343
  'CODEX_SANDBOX_CONTROLLER_PROOF_REQUIRED',
@@ -299,36 +363,69 @@ export function executeRequest(
299
363
  'Controller proof is only accepted for canonical Codex prepare'
300
364
  );
301
365
  }
302
- boundArgs.push('--git-worktree-root', manifest.worktreeRoot);
303
366
  }
304
- const result = (options.spawnDomain ?? spawnSync)(
305
- process.execPath,
306
- nodeEntryArgs(process.argv[1]!, [request.family, ...boundArgs]),
307
- {
308
- cwd: manifest.repoRoot,
309
- encoding: 'utf8',
310
- env: {
311
- ...safeEnv(process.env),
312
- AGENT_INFRA_RUNTIME_DIR: manifest.runtimeDir,
313
- ...(controllerBinding
314
- ? { AGENT_INFRA_CONTROL_CONTROLLER_BINDING: JSON.stringify(controllerBinding) }
315
- : {})
316
- }
317
- }
318
- );
319
- return { exitCode: result.status ?? 1, stdout: result.stdout ?? '', stderr: result.stderr ?? '' };
367
+ const context = createSandboxExecutorExecutionContext({
368
+ repoRoot: manifest.repoRoot,
369
+ worktreeRoot: manifest.worktreeRoot,
370
+ runtimeDir: manifest.runtimeDir,
371
+ taskId: manifest.taskId!,
372
+ generation: manifest.generation,
373
+ manifestPath,
374
+ requestId: request.id,
375
+ ...(controllerBinding ? { controllerBinding } : {})
376
+ });
377
+ const format = (value: unknown): SandboxControlExecutionResult => {
378
+ const result = value as { status?: string };
379
+ return {
380
+ exitCode: result.status === 'failed' ? 1 : 0,
381
+ stdout: `${JSON.stringify(value)}\n`,
382
+ stderr: ''
383
+ };
384
+ };
385
+ if (request.family === 'task-orchestration') {
386
+ if (operation.family !== 'task-orchestration') throw new Error('SANDBOX_CONTROL_ORCHESTRATION_OPERATION_INVALID');
387
+ return format(await dispatchTaskControlOperation(context, operation));
388
+ }
389
+ if (operation.family !== 'task-lifecycle') throw new Error('SANDBOX_CONTROL_LIFECYCLE_OPERATION_INVALID');
390
+ return format(dispatchTaskControlOperation(context, operation));
320
391
  }
321
392
 
322
393
  export async function runSandboxControlExecutor(requestPath: string, nonce: string): Promise<void> {
323
- await waitForGate(nonce);
394
+ const gateOwner = await waitForGate(nonce);
324
395
  process.disconnect?.();
325
- const root = fs.realpathSync.native(process.cwd());
326
- const request = JSON.parse(fs.readFileSync(requestPath, 'utf8')) as SandboxControlRequest;
327
396
  const manifestPath = process.env.AGENT_INFRA_EXECUTOR_MANIFEST;
328
397
  if (!manifestPath) throw new Error('SANDBOX_CONTROL_EXECUTOR_MANIFEST_MISSING');
329
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as SandboxControlManifest;
398
+ const manifest = readSandboxControlManifest(manifestPath);
399
+ const root = fs.realpathSync.native(process.cwd());
330
400
  if (fs.realpathSync.native(manifest.repoRoot) !== root) throw new Error('SANDBOX_CONTROL_EXECUTOR_ROOT_INVALID');
331
- const result = executeRequest(manifest, manifestPath, request);
401
+ const raw = JSON.parse(fs.readFileSync(requestPath, 'utf8')) as unknown;
402
+ const request = validateSandboxControlRequest(raw, manifest);
403
+ const requestDirectory = path.resolve(path.dirname(requestPath));
404
+ const processingDirectory = path.resolve(path.join(manifest.processingDir, request.id));
405
+ const channelRequestDirectory = path.resolve(path.join(manifest.channelDir, 'requests'));
406
+ if (requestDirectory !== processingDirectory && requestDirectory !== channelRequestDirectory) {
407
+ throw new Error('SANDBOX_CONTROL_EXECUTOR_REQUEST_INVALID');
408
+ }
409
+ let result: SandboxControlExecutionResult;
410
+ try {
411
+ assertSandboxControlExecutorAuthority(manifest, gateOwner);
412
+ result = await executeRequest(manifest, manifestPath, request);
413
+ } catch (error) {
414
+ const detail = error instanceof Error ? error.message : String(error);
415
+ const code = /^([A-Z][A-Z0-9_]+)/u.exec(detail)?.[1] ?? 'SANDBOX_CONTROL_EXECUTOR_FAILED';
416
+ result = request.family === 'task-finalization'
417
+ ? {
418
+ exitCode: 1,
419
+ stdout: `${JSON.stringify({
420
+ version: 1, status: 'failed', changed: false, accepted: true, result: null,
421
+ error: { code, message: detail, retryable: false }
422
+ })}\n`,
423
+ stderr: ''
424
+ }
425
+ : request.family === 'task-lifecycle'
426
+ ? lifecycleFailure(code, detail)
427
+ : orchestrationFailure(code, detail);
428
+ }
332
429
  process.stdout.write(result.stdout);
333
430
  process.stderr.write(result.stderr);
334
431
  process.exitCode = result.exitCode;
@@ -10,9 +10,7 @@ import {
10
10
  import type { ProcessIdentity, ProcessIdentityProbe, ProcessIdentityState } from '../../server/process-state.ts';
11
11
  import type {
12
12
  SandboxControlExecution,
13
- SandboxControlManifestLike,
14
13
  SandboxControlManifest,
15
- SandboxControlLegacyManifest,
16
14
  SandboxControlStatus,
17
15
  SandboxControlTimingPolicy
18
16
  } from './protocol.ts';
@@ -468,7 +466,6 @@ function readProcessingSnapshot(root: string): SandboxControlCutoverSnapshot['pr
468
466
  export function captureSandboxControlCutoverSnapshot(root: string): SandboxControlCutoverSnapshot {
469
467
  const resolvedRoot = path.resolve(root);
470
468
  const manifestPath = path.join(resolvedRoot, 'manifest.json');
471
- if (fs.existsSync(manifestPath)) readSandboxControlManifestForTransition(manifestPath);
472
469
  return {
473
470
  root: resolvedRoot,
474
471
  manifestRaw: readOptionalRaw(manifestPath),
@@ -485,10 +482,7 @@ export function assertSandboxControlCutoverSnapshot(snapshot: SandboxControlCuto
485
482
  }
486
483
  }
487
484
 
488
- function readSandboxControlManifestValue(
489
- manifestPath: string,
490
- allowLegacy: boolean
491
- ): SandboxControlManifestLike {
485
+ function readSandboxControlManifestValue(manifestPath: string): SandboxControlManifest {
492
486
  if (!regularFile(manifestPath)) throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
493
487
  let manifest: Record<string, unknown>;
494
488
  try {
@@ -500,49 +494,69 @@ function readSandboxControlManifestValue(
500
494
  } catch {
501
495
  throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
502
496
  }
503
- const candidate = manifest as Partial<SandboxControlManifest> & Partial<SandboxControlLegacyManifest>;
504
- if (candidate.version !== 4 && candidate.version !== 5) {
505
- throw new Error('SANDBOX_CONTROL_MANIFEST_VERSION_INVALID: expected version 5; container-only recreation is required');
497
+ const candidate = manifest as Partial<SandboxControlManifest>;
498
+ const expectedKeys = [
499
+ 'branch', 'channelDir', 'container', 'containerIdentity', 'engine', 'generation',
500
+ 'mode', 'processingDir', 'project', 'publicStatusDir', 'repoRoot', 'runtimeDir',
501
+ 'taskId', 'token', 'worktreeRoot'
502
+ ];
503
+ if (Object.keys(manifest).sort().join(',') !== expectedKeys.sort().join(',')) {
504
+ throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
506
505
  }
507
506
  if (typeof candidate.containerIdentity !== 'object' || candidate.containerIdentity === null
508
507
  || Array.isArray(candidate.containerIdentity)) throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
509
508
  const containerIdentity = candidate.containerIdentity as Record<string, unknown>;
510
- if (typeof candidate.engine !== 'string' || candidate.engine.length === 0
509
+ if (Object.keys(containerIdentity).sort().join(',') !== 'id,labels'
510
+ || typeof candidate.engine !== 'string' || candidate.engine.length === 0
511
511
  || typeof candidate.repoRoot !== 'string' || typeof candidate.worktreeRoot !== 'string'
512
512
  || typeof candidate.project !== 'string' || typeof candidate.container !== 'string'
513
513
  || typeof containerIdentity.id !== 'string' || containerIdentity.id.length === 0
514
514
  || typeof containerIdentity.labels !== 'object' || containerIdentity.labels === null
515
515
  || Array.isArray(containerIdentity.labels)
516
516
  || Object.values(containerIdentity.labels).some((value) => typeof value !== 'string')
517
- || typeof candidate.branch !== 'string' || !['task-bound', 'branch-only'].includes(candidate.mode ?? '')
517
+ || typeof candidate.branch !== 'string'
518
+ || (candidate.mode !== 'task-bound' && candidate.mode !== 'branch-only')
518
519
  || (candidate.taskId !== null && typeof candidate.taskId !== 'string')
519
520
  || typeof candidate.channelDir !== 'string' || typeof candidate.publicStatusDir !== 'string'
520
- || typeof candidate.processingDir !== 'string' || typeof candidate.token !== 'string'
521
- || typeof candidate.generation !== 'string') throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
521
+ || typeof candidate.processingDir !== 'string' || typeof candidate.runtimeDir !== 'string'
522
+ || typeof candidate.token !== 'string'
523
+ || typeof candidate.generation !== 'string'
524
+ || (candidate.mode === 'task-bound' && (typeof candidate.taskId !== 'string' || candidate.taskId.length === 0))
525
+ || (candidate.mode === 'branch-only' && candidate.taskId !== null)) throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
522
526
  const root = path.dirname(path.resolve(manifestPath));
523
527
  if (path.resolve(candidate.channelDir) !== path.join(root, 'channel')
524
528
  || path.resolve(candidate.publicStatusDir) !== path.join(root, 'public')
525
529
  || path.resolve(candidate.processingDir) !== path.join(root, 'processing')) {
526
530
  throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
527
531
  }
528
- if (candidate.version === 4) {
529
- if (!allowLegacy) {
530
- throw new Error('SANDBOX_CONTROL_MANIFEST_VERSION_INVALID: expected version 5; container-only recreation is required');
531
- }
532
- return candidate as unknown as SandboxControlLegacyManifest;
533
- }
534
- if (typeof candidate.runtimeDir !== 'string' || path.resolve(candidate.runtimeDir) !== path.join(root, 'runtime')) {
532
+ if (path.resolve(candidate.runtimeDir) !== path.join(root, 'runtime')) {
535
533
  throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
536
534
  }
537
- return candidate as unknown as SandboxControlManifest;
535
+ const normalized: SandboxControlManifest = {
536
+ engine: candidate.engine,
537
+ repoRoot: candidate.repoRoot,
538
+ worktreeRoot: candidate.worktreeRoot,
539
+ project: candidate.project,
540
+ container: candidate.container,
541
+ containerIdentity: {
542
+ id: containerIdentity.id as string,
543
+ labels: { ...(containerIdentity.labels as Record<string, string>) }
544
+ },
545
+ branch: candidate.branch,
546
+ mode: candidate.mode,
547
+ taskId: candidate.taskId,
548
+ token: candidate.token,
549
+ generation: candidate.generation,
550
+ channelDir: candidate.channelDir,
551
+ publicStatusDir: candidate.publicStatusDir,
552
+ processingDir: candidate.processingDir,
553
+ runtimeDir: candidate.runtimeDir
554
+ };
555
+ return normalized;
538
556
  }
539
557
 
540
558
  export function readSandboxControlManifest(manifestPath: string): SandboxControlManifest {
541
- return readSandboxControlManifestValue(manifestPath, false) as SandboxControlManifest;
542
- }
543
-
544
- export function readSandboxControlManifestForTransition(manifestPath: string): SandboxControlManifestLike {
545
- return readSandboxControlManifestValue(manifestPath, true);
559
+ return readSandboxControlManifestValue(manifestPath);
546
560
  }
547
561
 
548
562
  function readBrokerOwner(filePath: string): BrokerOwner | null {
@@ -561,6 +575,20 @@ function readBrokerOwner(filePath: string): BrokerOwner | null {
561
575
  return owner as BrokerOwner;
562
576
  }
563
577
 
578
+ export function assertSandboxControlBrokerOwner(
579
+ manifest: SandboxControlManifest,
580
+ options: Readonly<{ identityProbe?: ProcessIdentityProbe }> = {}
581
+ ): BrokerOwner {
582
+ const root = path.dirname(path.resolve(manifest.publicStatusDir));
583
+ const owner = readBrokerOwner(path.join(root, 'broker.json'));
584
+ if (!owner) throw new Error('SANDBOX_CONTROL_OWNER_EVIDENCE_MISSING');
585
+ if (owner.token !== manifest.token || owner.generation !== manifest.generation) {
586
+ throw new Error('SANDBOX_CONTROL_OWNER_MISMATCH');
587
+ }
588
+ if (!ownerLive(owner, options.identityProbe)) throw new Error('SANDBOX_CONTROL_OWNER_LOST');
589
+ return owner;
590
+ }
591
+
564
592
  function readStatusOwner(filePath: string): SandboxControlStatus | null {
565
593
  if (!fs.existsSync(filePath)) return null;
566
594
  if (!regularFile(filePath)) throw new Error('SANDBOX_CONTROL_OWNER_MISMATCH');
@@ -585,7 +613,7 @@ function ownerLive(
585
613
  return state === 'alive';
586
614
  }
587
615
 
588
- function readExecutions(manifest: SandboxControlManifestLike): SandboxControlExecution[] {
616
+ function readExecutions(manifest: SandboxControlManifest): SandboxControlExecution[] {
589
617
  if (!fs.existsSync(manifest.processingDir)) return [];
590
618
  const stat = fs.lstatSync(manifest.processingDir);
591
619
  if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error('SANDBOX_CONTROL_EXECUTION_STILL_RUNNING');
@@ -705,7 +733,7 @@ export async function quiesceSandboxControlRoot(
705
733
  await waitForStartupTransition(resolvedRoot, Math.max(0, forceAt - Date.now()), identityProbe);
706
734
 
707
735
  const manifestPath = path.join(resolvedRoot, 'manifest.json');
708
- const manifest = fs.existsSync(manifestPath) ? readSandboxControlManifestForTransition(manifestPath) : null;
736
+ const manifest = fs.existsSync(manifestPath) ? readSandboxControlManifest(manifestPath) : null;
709
737
  const broker = readBrokerOwner(path.join(resolvedRoot, 'broker.json'));
710
738
  const status = readStatusOwner(path.join(resolvedRoot, 'public', 'status.json'));
711
739
  const statusOwner = status ? status.broker : null;
@@ -799,7 +827,7 @@ export async function removeSandboxControlRoot(
799
827
  const stat = fs.lstatSync(resolvedRoot);
800
828
  if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error('SANDBOX_CONTROL_CHANNEL_INVALID');
801
829
  const manifestPath = path.join(resolvedRoot, 'manifest.json');
802
- const manifest = readSandboxControlManifestForTransition(manifestPath);
830
+ const manifest = readSandboxControlManifest(manifestPath);
803
831
  const inspectContainer = options.inspectContainer
804
832
  ?? ((timeoutMs) => inspectSandboxControlContainer(manifest, { timeoutMs }));
805
833
  const timeoutMs = options.timeoutMs ?? options.timing?.quiesceDeadlineMs ?? DEFAULT_QUIESCE_TIMEOUT_MS;
@@ -870,7 +898,7 @@ export async function removeSandboxControlRoot(
870
898
  }
871
899
  }
872
900
 
873
- const currentManifest = readSandboxControlManifestForTransition(manifestPath);
901
+ const currentManifest = readSandboxControlManifest(manifestPath);
874
902
  if (currentManifest.token !== manifest.token || currentManifest.generation !== manifest.generation
875
903
  || !isSandboxControlRootQuiescing(resolvedRoot)) {
876
904
  throw new Error('SANDBOX_CONTROL_OWNER_TRANSITION');
@@ -35,9 +35,7 @@ export type SandboxControlManifestBase = Readonly<{
35
35
  branch: string; mode: 'task-bound' | 'branch-only'; taskId: string | null; token: string;
36
36
  generation: string; channelDir: string; publicStatusDir: string; processingDir: string;
37
37
  }>;
38
- export type SandboxControlLegacyManifest = SandboxControlManifestBase & Readonly<{ version: 4 }>;
39
- export type SandboxControlManifest = SandboxControlManifestBase & Readonly<{ version: 5; runtimeDir: string }>;
40
- export type SandboxControlManifestLike = SandboxControlLegacyManifest | SandboxControlManifest;
38
+ export type SandboxControlManifest = SandboxControlManifestBase & Readonly<{ runtimeDir: string }>;
41
39
  export type SandboxControlBrokerOwner = ProcessIdentity & Readonly<{
42
40
  version: 3;
43
41
  brokerId: string;
@@ -228,6 +228,7 @@ export async function serveSandboxControl(
228
228
  appendSandboxControlAudit(manifest, 'broker-start', { pid: broker.pid });
229
229
  if (!recoverProcessing(manifest, brokerOwns)) return;
230
230
  while (!signal.aborted) {
231
+ let settledExecution: ActiveExecution | null = null;
231
232
  if (!brokerOwns()) break;
232
233
  let current: SandboxControlManifest;
233
234
  try {
@@ -277,18 +278,7 @@ export async function serveSandboxControl(
277
278
 
278
279
  if (active?.settled) {
279
280
  if (!brokerOwns()) break;
280
- if (active.result) {
281
- if (!brokerOwns()) break;
282
- writeResponse(manifest, {
283
- version: 2, id: active.request.id, phase: 'completed', exitCode: active.result.exitCode,
284
- stdout: active.result.stdout, stderr: active.result.stderr, error: null
285
- });
286
- } else {
287
- if (!brokerOwns()) break;
288
- writeResponse(manifest, unknown(active.request.id));
289
- }
290
- if (!brokerOwns()) break;
291
- fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
281
+ settledExecution = active;
292
282
  active = null;
293
283
  }
294
284
 
@@ -327,6 +317,19 @@ export async function serveSandboxControl(
327
317
  appendSandboxControlAudit(manifest, 'broker-state', { state, reasonCode, requestId: active?.request.id ?? null });
328
318
  lastState = stateKey;
329
319
  }
320
+ if (settledExecution) {
321
+ if (!brokerOwns()) break;
322
+ if (settledExecution.result) {
323
+ writeResponse(manifest, {
324
+ version: 2, id: settledExecution.request.id, phase: 'completed', exitCode: settledExecution.result.exitCode,
325
+ stdout: settledExecution.result.stdout, stderr: settledExecution.result.stderr, error: null
326
+ });
327
+ } else {
328
+ writeResponse(manifest, unknown(settledExecution.request.id));
329
+ }
330
+ if (!brokerOwns()) break;
331
+ fs.rmSync(path.join(manifest.processingDir, settledExecution.request.id), { recursive: true, force: true });
332
+ }
330
333
 
331
334
  let retiring = false;
332
335
  for (const name of fs.readdirSync(requestsDir).sort()) {
@@ -8,7 +8,7 @@ import type { ProcessIdentity, ProcessIdentityProbe } from '../../server/process
8
8
  import type {
9
9
  SandboxControlExecution,
10
10
  SandboxControlLease,
11
- SandboxControlManifestLike,
11
+ SandboxControlManifest,
12
12
  SandboxControlStatus
13
13
  } from './protocol.ts';
14
14
 
@@ -36,12 +36,12 @@ export function readJsonFile(filePath: string): unknown {
36
36
  return JSON.parse(fs.readFileSync(filePath, 'utf8'));
37
37
  }
38
38
 
39
- export function statusPath(manifest: SandboxControlManifestLike): string {
39
+ export function statusPath(manifest: SandboxControlManifest): string {
40
40
  return path.join(manifest.publicStatusDir, 'status.json');
41
41
  }
42
42
 
43
43
  export function writeSandboxControlStatus(
44
- manifest: SandboxControlManifestLike,
44
+ manifest: SandboxControlManifest,
45
45
  broker: ProcessIdentity & { brokerId: string },
46
46
  state: SandboxControlStatus['state'],
47
47
  reasonCode: string | null,
@@ -72,7 +72,7 @@ export function readSandboxControlStatus(directory: string): SandboxControlStatu
72
72
  return parseSandboxControlStatus(readJsonFile(path.join(directory, 'status.json')));
73
73
  }
74
74
 
75
- export function readActiveLease(manifest: SandboxControlManifestLike, now = Date.now()): SandboxControlLease | null {
75
+ export function readActiveLease(manifest: SandboxControlManifest, now = Date.now()): SandboxControlLease | null {
76
76
  const filePath = path.join(path.dirname(manifest.publicStatusDir), 'lease.json');
77
77
  if (!fs.existsSync(filePath)) return null;
78
78
  const lease = readJsonFile(filePath) as Partial<SandboxControlLease> | null;
@@ -91,7 +91,7 @@ export function readActiveLease(manifest: SandboxControlManifestLike, now = Date
91
91
  }
92
92
 
93
93
  export function cleanupStaleSandboxControlLease(
94
- manifest: SandboxControlManifestLike,
94
+ manifest: SandboxControlManifest,
95
95
  now = Date.now(),
96
96
  options: Readonly<{ identityProbe?: ProcessIdentityProbe }> = {}
97
97
  ): boolean {
@@ -121,7 +121,7 @@ export function cleanupStaleSandboxControlLease(
121
121
  }
122
122
 
123
123
  export function appendSandboxControlAudit(
124
- manifest: SandboxControlManifestLike,
124
+ manifest: SandboxControlManifest,
125
125
  event: string,
126
126
  fields: Record<string, string | number | boolean | null> = {},
127
127
  now = Date.now()
@@ -136,7 +136,7 @@ export function appendSandboxControlAudit(
136
136
  });
137
137
  }
138
138
 
139
- export function executionPath(manifest: SandboxControlManifestLike, requestId: string): string {
139
+ export function executionPath(manifest: SandboxControlManifest, requestId: string): string {
140
140
  return path.join(manifest.processingDir, requestId, 'execution.json');
141
141
  }
142
142
 
@@ -27,11 +27,12 @@ export function sandboxCoreBindMounts(
27
27
  controlDir: string;
28
28
  controlStatusDir: string;
29
29
  runtimeDir?: string;
30
- taskSource?: string;
30
+ taskSources?: string[];
31
31
  taskId?: string;
32
32
  }
33
33
  ): SandboxBindMountDeclaration[] {
34
- const taskBound = Boolean(overrides.taskSource && overrides.taskId);
34
+ const taskSources = overrides.taskSources ?? [];
35
+ const taskBound = Boolean(taskSources.length > 0 && overrides.taskId);
35
36
  const workspaceMounts = sandboxWorkspaceViewStatePaths(overrides.workspaceViewRoot).map(
36
37
  ({ state, hostPath }) => taskBound && state === 'active'
37
38
  ? {
@@ -57,7 +58,7 @@ export function sandboxCoreBindMounts(
57
58
  ];
58
59
  if (taskBound) {
59
60
  mounts.push({
60
- hostPaths: [overrides.taskSource!],
61
+ hostPaths: taskSources,
61
62
  containerPath: path.posix.join('/workspace/.agents/workspace/active', overrides.taskId!),
62
63
  readOnly: false
63
64
  });