@fitlab-ai/agent-infra 0.9.11 → 0.9.13

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 (356) hide show
  1. package/README.md +13 -13
  2. package/README.zh-CN.md +13 -13
  3. package/dist/bin/internal-cli.js +29 -19
  4. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +71 -10
  5. package/dist/lib/agent-clients/next-steps.js +6 -1
  6. package/dist/lib/decide.js +3 -3
  7. package/dist/lib/defaults.json +4 -0
  8. package/dist/lib/git/workflow.js +43 -1
  9. package/dist/lib/init.js +1 -0
  10. package/dist/lib/internal/git-workflow.js +8 -1
  11. package/dist/lib/internal/platform-checks.js +3 -3
  12. package/dist/lib/internal/platform-comment.js +9 -9
  13. package/dist/lib/internal/platform-context.js +2 -2
  14. package/dist/lib/internal/platform-issue.js +8 -9
  15. package/dist/lib/internal/platform-pr-review.js +10 -10
  16. package/dist/lib/internal/platform-pr.js +68 -23
  17. package/dist/lib/internal/platform-release-notes.js +3 -3
  18. package/dist/lib/internal/pr-review-grade.js +11 -9
  19. package/dist/lib/internal/release-workflow.js +4 -4
  20. package/dist/lib/internal/sandbox-control.js +30 -4
  21. package/dist/lib/internal/task-activity.js +3 -3
  22. package/dist/lib/internal/task-artifact.js +76 -24
  23. package/dist/lib/internal/task-create.js +2 -2
  24. package/dist/lib/internal/task-delivery.js +59 -0
  25. package/dist/lib/internal/task-event.js +6 -3
  26. package/dist/lib/internal/task-finalization.js +2 -2
  27. package/dist/lib/internal/task-invalidation.js +60 -0
  28. package/dist/lib/internal/task-ledger.js +9 -6
  29. package/dist/lib/internal/task-lifecycle.js +2 -2
  30. package/dist/lib/internal/task-override.js +4 -4
  31. package/dist/lib/internal/task-review.js +3 -3
  32. package/dist/lib/internal/task-verify.js +6 -6
  33. package/dist/lib/internal/task-warning.js +3 -3
  34. package/dist/lib/platform/adapters.js +1 -3
  35. package/dist/lib/platform/change-request-git-evidence.js +27 -7
  36. package/dist/lib/platform/completion-backfill.js +6 -5
  37. package/dist/lib/platform/context.js +70 -130
  38. package/dist/lib/platform/github-client.js +8 -2
  39. package/dist/lib/platform/github-provider.js +903 -0
  40. package/dist/lib/platform/in-label-sync.js +143 -0
  41. package/dist/lib/platform/issue-comments.js +94 -41
  42. package/dist/lib/platform/issues.js +335 -189
  43. package/dist/lib/platform/metadata-labels.js +6 -2
  44. package/dist/lib/platform/none-provider.js +28 -0
  45. package/dist/lib/platform/pr-change-report.js +551 -0
  46. package/dist/lib/platform/pr-checks.js +124 -106
  47. package/dist/lib/platform/pr-review.js +88 -72
  48. package/dist/lib/platform/pr-summary.js +523 -49
  49. package/dist/lib/platform/provider-bridge.js +45 -0
  50. package/dist/lib/platform/provider-contract.d.ts +487 -0
  51. package/dist/lib/platform/provider-contract.js +95 -0
  52. package/dist/lib/platform/provider-loader.js +251 -0
  53. package/dist/lib/platform/provider-validation.js +504 -0
  54. package/dist/lib/platform/pull-request-metadata.js +8 -6
  55. package/dist/lib/platform/pull-requests.js +1050 -502
  56. package/dist/lib/platform/release-notes.js +78 -55
  57. package/dist/lib/platform/releases.js +79 -50
  58. package/dist/lib/platform/resource-identity.d.ts +26 -0
  59. package/dist/lib/platform/resource-identity.js +92 -0
  60. package/dist/lib/platform/task-identities.js +31 -0
  61. package/dist/lib/platform/verification-required.js +22 -12
  62. package/dist/lib/platform/verification-sync.js +118 -239
  63. package/dist/lib/pr-review/evidence-grading.js +30 -9
  64. package/dist/lib/run/index.js +63 -23
  65. package/dist/lib/sandbox/config.js +5 -0
  66. package/dist/lib/sandbox/control/client.js +96 -15
  67. package/dist/lib/sandbox/control/executor.js +3 -3
  68. package/dist/lib/sandbox/control/lifecycle.js +29 -7
  69. package/dist/lib/sandbox/control/protocol.js +77 -0
  70. package/dist/lib/sandbox/control/server.js +398 -25
  71. package/dist/lib/sandbox/control/state.js +204 -1
  72. package/dist/lib/server/process-state.js +2 -2
  73. package/dist/lib/server/protocol.js +38 -1
  74. package/dist/lib/task/activity-log.js +8 -1
  75. package/dist/lib/task/artifact-lifecycle.js +35 -7
  76. package/dist/lib/task/capabilities.js +239 -0
  77. package/dist/lib/task/commands/status.js +32 -0
  78. package/dist/lib/task/commit-intent.js +100 -0
  79. package/dist/lib/task/commit-operation.js +207 -102
  80. package/dist/lib/task/control-authority.js +8 -5
  81. package/dist/lib/task/create-service.js +8 -8
  82. package/dist/lib/task/create.js +33 -7
  83. package/dist/lib/task/current-contract.js +5 -1
  84. package/dist/lib/task/decision-details.js +3 -48
  85. package/dist/lib/task/delivery-target.js +97 -0
  86. package/dist/lib/task/delivery.js +153 -0
  87. package/dist/lib/task/events.js +297 -17
  88. package/dist/lib/task/finalization.js +37 -13
  89. package/dist/lib/task/human-override.js +61 -36
  90. package/dist/lib/task/invalidation-command.js +86 -0
  91. package/dist/lib/task/invalidation.js +260 -0
  92. package/dist/lib/task/ledger-intents.js +37 -0
  93. package/dist/lib/task/lifecycle.js +106 -9
  94. package/dist/lib/task/local-artifact-finalization.js +374 -0
  95. package/dist/lib/task/orchestration.js +100 -151
  96. package/dist/lib/task/pr-delivery-fact.js +183 -0
  97. package/dist/lib/task/review-finalization.js +4 -8
  98. package/dist/lib/task/review-fingerprint.js +32 -2
  99. package/dist/lib/task/rework-intent.js +138 -0
  100. package/dist/lib/task/skill-scope.js +23 -0
  101. package/dist/lib/task/task-execution-lock.js +27 -17
  102. package/dist/lib/task/verification-engine.js +89 -38
  103. package/dist/lib/task/verification-review.js +1 -1
  104. package/dist/lib/task/verification.js +3 -3
  105. package/dist/lib/task/workflow-warning-intents.js +1 -1
  106. package/dist/lib/task/write.js +11 -0
  107. package/dist/lib/update.js +9 -0
  108. package/dist/lib/version.js +11 -1
  109. package/dist/lifecycle-build-manifest.json +3 -3
  110. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +105 -10
  111. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +12 -1
  112. package/lib/agent-clients/next-steps.ts +6 -1
  113. package/lib/decide.ts +3 -3
  114. package/lib/defaults.json +4 -0
  115. package/lib/git/workflow.ts +38 -1
  116. package/lib/init.ts +4 -1
  117. package/lib/internal/git-workflow.ts +7 -1
  118. package/lib/internal/platform-checks.ts +3 -3
  119. package/lib/internal/platform-comment.ts +8 -8
  120. package/lib/internal/platform-context.ts +2 -2
  121. package/lib/internal/platform-issue.ts +7 -8
  122. package/lib/internal/platform-pr-review.ts +9 -9
  123. package/lib/internal/platform-pr.ts +53 -21
  124. package/lib/internal/platform-release-notes.ts +3 -3
  125. package/lib/internal/pr-review-grade.ts +10 -8
  126. package/lib/internal/release-workflow.ts +4 -4
  127. package/lib/internal/sandbox-control.ts +27 -3
  128. package/lib/internal/task-activity.ts +3 -3
  129. package/lib/internal/task-artifact.ts +65 -22
  130. package/lib/internal/task-create.ts +2 -2
  131. package/lib/internal/task-delivery.ts +53 -0
  132. package/lib/internal/task-event.ts +6 -3
  133. package/lib/internal/task-finalization.ts +3 -3
  134. package/lib/internal/task-invalidation.ts +39 -0
  135. package/lib/internal/task-ledger.ts +11 -8
  136. package/lib/internal/task-lifecycle.ts +2 -2
  137. package/lib/internal/task-override.ts +6 -6
  138. package/lib/internal/task-review.ts +3 -3
  139. package/lib/internal/task-verify.ts +6 -6
  140. package/lib/internal/task-warning.ts +3 -3
  141. package/lib/platform/adapters.ts +3 -2
  142. package/lib/platform/change-request-git-evidence.ts +36 -9
  143. package/lib/platform/completion-backfill.ts +9 -8
  144. package/lib/platform/context.ts +97 -134
  145. package/lib/platform/github-client.ts +10 -3
  146. package/lib/platform/github-provider.ts +910 -0
  147. package/lib/platform/in-label-sync.ts +208 -0
  148. package/lib/platform/issue-comments.ts +97 -47
  149. package/lib/platform/issues.ts +344 -174
  150. package/lib/platform/metadata-labels.ts +5 -2
  151. package/lib/platform/none-provider.ts +35 -0
  152. package/lib/platform/pr-change-report.ts +645 -0
  153. package/lib/platform/pr-checks.ts +129 -107
  154. package/lib/platform/pr-review.ts +88 -85
  155. package/lib/platform/pr-summary.ts +562 -52
  156. package/lib/platform/provider-bridge.ts +82 -0
  157. package/lib/platform/provider-contract.ts +536 -0
  158. package/lib/platform/provider-loader.ts +307 -0
  159. package/lib/platform/provider-validation.ts +529 -0
  160. package/lib/platform/pull-request-metadata.ts +10 -7
  161. package/lib/platform/pull-requests.ts +1126 -505
  162. package/lib/platform/release-notes.ts +76 -59
  163. package/lib/platform/releases.ts +77 -43
  164. package/lib/platform/resource-identity.ts +121 -0
  165. package/lib/platform/task-identities.ts +40 -0
  166. package/lib/platform/types.ts +4 -2
  167. package/lib/platform/verification-required.ts +21 -13
  168. package/lib/platform/verification-sync.ts +118 -293
  169. package/lib/pr-review/evidence-grading.ts +31 -11
  170. package/lib/run/index.ts +57 -22
  171. package/lib/sandbox/config.ts +7 -0
  172. package/lib/sandbox/control/client.ts +105 -15
  173. package/lib/sandbox/control/executor.ts +4 -4
  174. package/lib/sandbox/control/lifecycle.ts +30 -8
  175. package/lib/sandbox/control/protocol.ts +113 -0
  176. package/lib/sandbox/control/server.ts +431 -23
  177. package/lib/sandbox/control/state.ts +251 -1
  178. package/lib/server/process-state.ts +2 -2
  179. package/lib/server/protocol.ts +36 -1
  180. package/lib/task/activity-log.ts +8 -0
  181. package/lib/task/artifact-lifecycle.ts +34 -8
  182. package/lib/task/capabilities.ts +262 -0
  183. package/lib/task/commands/status.ts +38 -0
  184. package/lib/task/commit-intent.ts +137 -0
  185. package/lib/task/commit-operation.ts +186 -88
  186. package/lib/task/control-authority.ts +11 -8
  187. package/lib/task/create-service.ts +8 -8
  188. package/lib/task/create.ts +34 -9
  189. package/lib/task/current-contract.ts +6 -1
  190. package/lib/task/decision-details.ts +8 -64
  191. package/lib/task/delivery-target.ts +130 -0
  192. package/lib/task/delivery.ts +160 -0
  193. package/lib/task/events.ts +296 -16
  194. package/lib/task/finalization.ts +50 -14
  195. package/lib/task/human-override.ts +73 -46
  196. package/lib/task/invalidation-command.ts +86 -0
  197. package/lib/task/invalidation.ts +332 -0
  198. package/lib/task/ledger-intents.ts +35 -1
  199. package/lib/task/lifecycle.ts +93 -4
  200. package/lib/task/local-artifact-finalization.ts +563 -0
  201. package/lib/task/orchestration.ts +110 -146
  202. package/lib/task/pr-delivery-fact.ts +237 -0
  203. package/lib/task/review-finalization.ts +4 -14
  204. package/lib/task/review-fingerprint.ts +33 -2
  205. package/lib/task/rework-intent.ts +165 -0
  206. package/lib/task/skill-scope.ts +24 -0
  207. package/lib/task/task-execution-lock.ts +23 -14
  208. package/lib/task/verification-engine.ts +95 -43
  209. package/lib/task/verification-review.ts +2 -0
  210. package/lib/task/verification.ts +4 -4
  211. package/lib/task/workflow-warning-intents.ts +1 -1
  212. package/lib/task/write.ts +19 -0
  213. package/lib/update.ts +11 -1
  214. package/lib/version.ts +15 -1
  215. package/package.json +7 -1
  216. package/templates/.agents/rules/README.en.md +4 -2
  217. package/templates/.agents/rules/README.zh-CN.md +4 -2
  218. package/templates/.agents/rules/create-issue.en.md +11 -2
  219. package/templates/.agents/rules/create-issue.zh-CN.md +24 -2
  220. package/templates/.agents/rules/issue-pr-commands.en.md +4 -17
  221. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
  222. package/templates/.agents/rules/issue-sync.en.md +28 -5
  223. package/templates/.agents/rules/issue-sync.zh-CN.md +64 -5
  224. package/templates/.agents/rules/label-milestone-setup.en.md +15 -2
  225. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +15 -2
  226. package/templates/.agents/rules/local-artifact-repair.en.md +53 -0
  227. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +53 -0
  228. package/templates/.agents/rules/milestone-inference.en.md +12 -2
  229. package/templates/.agents/rules/milestone-inference.zh-CN.md +25 -3
  230. package/templates/.agents/rules/pr-sync.en.md +28 -13
  231. package/templates/.agents/rules/pr-sync.zh-CN.md +39 -5
  232. package/templates/.agents/rules/release-commands.en.md +42 -3
  233. package/templates/.agents/rules/release-commands.zh-CN.md +42 -3
  234. package/templates/.agents/rules/security-alerts.en.md +17 -2
  235. package/templates/.agents/rules/security-alerts.zh-CN.md +17 -2
  236. package/templates/.agents/rules/sync-content-generation.en.md +8 -0
  237. package/templates/.agents/rules/sync-content-generation.zh-CN.md +8 -0
  238. package/templates/.agents/rules/task-management.en.md +1 -1
  239. package/templates/.agents/rules/task-management.zh-CN.md +1 -1
  240. package/templates/.agents/rules/task-short-id.en.md +1 -4
  241. package/templates/.agents/rules/task-short-id.zh-CN.md +1 -3
  242. package/templates/.agents/scripts/security-alerts.github.sh +151 -0
  243. package/templates/.agents/scripts/security-alerts.sh +31 -0
  244. package/templates/.agents/skills/analyze-task/SKILL.en.md +14 -3
  245. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +11 -3
  246. package/templates/.agents/skills/check-task/SKILL.en.md +16 -37
  247. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
  248. package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
  249. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
  250. package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
  251. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
  252. package/templates/.agents/skills/code-task/SKILL.en.md +32 -10
  253. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +35 -13
  254. package/templates/.agents/skills/code-task/config/verify.en.json +1 -0
  255. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +1 -0
  256. package/templates/.agents/skills/commit/SKILL.en.md +3 -3
  257. package/templates/.agents/skills/commit/SKILL.zh-CN.md +3 -3
  258. package/templates/.agents/skills/commit/config/verify.json +1 -1
  259. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +3 -3
  260. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +3 -3
  261. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +2 -0
  262. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
  263. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +4 -4
  264. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +4 -4
  265. package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -2
  266. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -2
  267. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +7 -4
  268. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +7 -4
  269. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  270. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  271. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +5 -4
  272. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +5 -4
  273. package/templates/.agents/skills/complete-task/SKILL.en.md +31 -13
  274. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +30 -13
  275. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +2 -2
  276. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +2 -2
  277. package/templates/.agents/skills/create-pr/SKILL.en.md +11 -10
  278. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +11 -11
  279. package/templates/.agents/skills/create-pr/config/verify.json +1 -0
  280. package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
  281. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
  282. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
  283. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
  284. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
  285. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
  286. package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
  287. package/templates/.agents/skills/create-task/SKILL.en.md +1 -0
  288. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +1 -0
  289. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  290. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  291. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  292. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  293. package/templates/.agents/skills/init-labels/SKILL.en.md +9 -10
  294. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +9 -10
  295. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +57 -1
  296. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +27 -1
  297. package/templates/.agents/skills/init-milestones/SKILL.en.md +3 -5
  298. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +3 -5
  299. package/templates/.agents/skills/plan-task/SKILL.en.md +14 -3
  300. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +11 -3
  301. package/templates/.agents/skills/review-analysis/SKILL.en.md +6 -5
  302. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +6 -5
  303. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +18 -0
  304. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +18 -0
  305. package/templates/.agents/skills/review-code/SKILL.en.md +10 -9
  306. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +10 -9
  307. package/templates/.agents/skills/review-code/reference/output-templates.en.md +19 -1
  308. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +19 -1
  309. package/templates/.agents/skills/review-code/reference/report-template.en.md +4 -2
  310. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +4 -2
  311. package/templates/.agents/skills/review-plan/SKILL.en.md +6 -5
  312. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +6 -5
  313. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +18 -0
  314. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +18 -0
  315. package/templates/.agents/skills/review-pr/SKILL.en.md +1 -0
  316. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +1 -0
  317. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +2 -2
  318. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +2 -2
  319. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -2
  320. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -2
  321. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  322. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  323. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +4 -0
  324. package/templates/.agents/skills/watch-pr/SKILL.en.md +6 -6
  325. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +6 -6
  326. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
  327. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
  328. package/templates/.agents/templates/task.en.md +23 -2
  329. package/templates/.agents/templates/task.zh-CN.md +23 -2
  330. package/templates/.agents/workflows/bug-fix.en.yaml +9 -8
  331. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +9 -8
  332. package/templates/.agents/workflows/feature-development.en.yaml +9 -8
  333. package/templates/.agents/workflows/feature-development.zh-CN.yaml +9 -8
  334. package/templates/.agents/workflows/refactoring.en.yaml +9 -8
  335. package/templates/.agents/workflows/refactoring.zh-CN.yaml +9 -8
  336. package/templates/.github/workflows/pr-label.yml +7 -26
  337. package/templates/.agents/rules/create-issue.github.en.md +0 -14
  338. package/templates/.agents/rules/create-issue.github.zh-CN.md +0 -27
  339. package/templates/.agents/rules/issue-pr-commands.github.en.md +0 -34
  340. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +0 -49
  341. package/templates/.agents/rules/issue-sync.github.en.md +0 -40
  342. package/templates/.agents/rules/issue-sync.github.zh-CN.md +0 -76
  343. package/templates/.agents/rules/label-milestone-setup.github.en.md +0 -60
  344. package/templates/.agents/rules/label-milestone-setup.github.zh-CN.md +0 -60
  345. package/templates/.agents/rules/milestone-inference.github.en.md +0 -15
  346. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +0 -27
  347. package/templates/.agents/rules/pr-checks-commands.github.en.md +0 -18
  348. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +0 -34
  349. package/templates/.agents/rules/pr-sync.github.en.md +0 -43
  350. package/templates/.agents/rules/pr-sync.github.zh-CN.md +0 -69
  351. package/templates/.agents/rules/release-commands.github.en.md +0 -44
  352. package/templates/.agents/rules/release-commands.github.zh-CN.md +0 -44
  353. package/templates/.agents/rules/security-alerts.github.en.md +0 -43
  354. package/templates/.agents/rules/security-alerts.github.zh-CN.md +0 -43
  355. /package/templates/.agents/rules/{issue-fields.github.en.md → issue-fields.en.md} +0 -0
  356. /package/templates/.agents/rules/{issue-fields.github.zh-CN.md → issue-fields.zh-CN.md} +0 -0
@@ -1,11 +1,13 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { randomUUID } from 'node:crypto';
3
+ import { createHash, randomUUID } from 'node:crypto';
4
4
  import { getProcessStartTime } from '../../server/process-state.ts';
5
5
  import { assertGitWorktreeBinding } from '../../git/worktree-identity.ts';
6
6
  import {
7
7
  controlError,
8
8
  DEFAULT_SANDBOX_CONTROL_TIMING,
9
+ SANDBOX_CONTROL_MAX_RESPONSE_BYTES,
10
+ SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES,
9
11
  type SandboxControlManifest,
10
12
  type SandboxControlRequest,
11
13
  type SandboxControlResponse,
@@ -14,11 +16,23 @@ import {
14
16
  import { prepareSandboxControlExecution, type PreparedSandboxControlExecution, type SandboxControlExecutionResult } from './executor.ts';
15
17
  import {
16
18
  appendSandboxControlAudit,
17
- atomicWriteJson,
19
+ atomicWriteJsonNoReplace,
20
+ createSandboxControlPayload,
18
21
  cleanupStaleSandboxControlLease,
19
22
  executionPath,
23
+ readJsonFile,
20
24
  readActiveLease,
21
25
  readExecution,
26
+ readSandboxControlPayload,
27
+ readSandboxControlResultEvidence,
28
+ sandboxControlEncodedJsonBytes,
29
+ sandboxControlGenerationUsage,
30
+ sanitizeSandboxControlOutput,
31
+ sanitizeSandboxControlResult,
32
+ payloadPath,
33
+ writeSandboxControlPayload,
34
+ writeSandboxControlReservation,
35
+ writeSandboxControlResultEvidence,
22
36
  terminateSandboxControlExecution,
23
37
  writeSandboxControlStatus
24
38
  } from './state.ts';
@@ -32,11 +46,14 @@ import {
32
46
  } from './lifecycle.ts';
33
47
  import type { BrokerOwner } from './lifecycle.ts';
34
48
  import { nextSandboxControlBackoff } from './timing.ts';
49
+ import { readTaskFinalizationReceipt } from '../../task/finalization.ts';
50
+ import { resolveTaskRef } from '../../task/resolve-ref.ts';
35
51
 
36
52
  type ActiveExecution = {
37
53
  request: SandboxControlRequest;
38
54
  prepared: PreparedSandboxControlExecution;
39
55
  result: SandboxControlExecutionResult | null;
56
+ resultEvidenceWritten: boolean;
40
57
  failure: unknown;
41
58
  settled: boolean;
42
59
  };
@@ -53,8 +70,271 @@ function responsePath(manifest: SandboxControlManifest, id: string): string {
53
70
  return path.join(manifest.channelDir, 'responses', `${id}.json`);
54
71
  }
55
72
 
56
- function writeResponse(manifest: SandboxControlManifest, response: SandboxControlResponse): void {
57
- atomicWriteJson(responsePath(manifest, response.id), response);
73
+ function acceptedResponsePath(manifest: SandboxControlManifest, id: string): string {
74
+ return path.join(manifest.channelDir, 'responses', `${id}.accepted.json`);
75
+ }
76
+
77
+ export function writeSandboxControlResponse(manifest: SandboxControlManifest, response: SandboxControlResponse): boolean {
78
+ const filePath = responsePath(manifest, response.id);
79
+ const normalized = {
80
+ ...response,
81
+ stdout: sanitizeSandboxControlOutput(manifest, response.stdout),
82
+ stderr: sanitizeSandboxControlOutput(manifest, response.stderr)
83
+ } satisfies SandboxControlResponse;
84
+ const terminal = sandboxControlEncodedJsonBytes(normalized) <= SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES
85
+ ? normalized
86
+ : {
87
+ ...normalized,
88
+ stdout: '',
89
+ stderr: 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: terminal output exceeded the compact record limit\n',
90
+ error: {
91
+ code: 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE',
92
+ message: 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: terminal output exceeded the compact record limit',
93
+ retryable: false
94
+ }
95
+ } satisfies SandboxControlResponse;
96
+ try {
97
+ atomicWriteJsonNoReplace(filePath, terminal);
98
+ } catch (error) {
99
+ if (!(error instanceof Error) || error.message !== 'SANDBOX_CONTROL_TERMINAL_ALREADY_EXISTS') throw error;
100
+ let persisted: unknown;
101
+ try {
102
+ persisted = readJsonFile(filePath);
103
+ } catch {
104
+ throw new Error('SANDBOX_CONTROL_TERMINAL_READBACK_FAILED');
105
+ }
106
+ if (JSON.stringify(persisted) !== JSON.stringify(terminal)) {
107
+ throw new Error('SANDBOX_CONTROL_TERMINAL_CONFLICT');
108
+ }
109
+ return true;
110
+ }
111
+ let persisted: unknown;
112
+ try {
113
+ persisted = readJsonFile(filePath);
114
+ } catch {
115
+ throw new Error('SANDBOX_CONTROL_TERMINAL_READBACK_FAILED');
116
+ }
117
+ if (JSON.stringify(persisted) !== JSON.stringify(terminal)) {
118
+ throw new Error('SANDBOX_CONTROL_TERMINAL_READBACK_FAILED');
119
+ }
120
+ return true;
121
+ }
122
+
123
+ type FinalizationRecovery = Readonly<{
124
+ status: 'matched' | 'deferred' | 'not-applicable';
125
+ response?: SandboxControlResponse;
126
+ }>;
127
+
128
+ function finalizationRecoveryResponse(
129
+ manifest: SandboxControlManifest,
130
+ requestId: string,
131
+ exitCode: number
132
+ ): FinalizationRecovery {
133
+ if (!manifest.taskId || exitCode !== 0) return { status: 'not-applicable' };
134
+ let receipt;
135
+ try {
136
+ receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
137
+ if (!receipt || !receipt.controlBinding
138
+ || receipt.controlBinding.generation !== manifest.generation
139
+ || receipt.controlBinding.requestId !== requestId) return { status: 'deferred' };
140
+ const resolved = resolveTaskRef(manifest.taskId, { repoRoot: manifest.repoRoot });
141
+ if (!resolved.ok || resolved.state !== 'completed' || receipt.lifecycle !== 'done') return { status: 'deferred' };
142
+ } catch {
143
+ return { status: 'deferred' };
144
+ }
145
+ const pendingSteps = [
146
+ receipt.taskComment === 'pending' ? 'task-comment' : null,
147
+ receipt.verification === 'pending' ? 'verification' : null
148
+ ].filter((step): step is string => step !== null);
149
+ const completedSteps = ['lifecycle', receipt.taskComment === 'pending' ? null : 'task-comment', receipt.verification === 'pending' ? null : 'verification']
150
+ .filter((step): step is string => step !== null);
151
+ const warnings = receipt.warnings
152
+ .filter((warning) => warning.status === 'open')
153
+ .map(({ status: _status, resolvedAt: _resolvedAt, ...warning }) => warning);
154
+ const result = {
155
+ status: 'completed', changed: false, taskId: manifest.taskId,
156
+ lifecycle: { status: 'no-op', changed: false, error: null },
157
+ taskComment: receipt.taskComment === 'pending' ? null : { status: 'no-op', changed: false, error: null },
158
+ verification: receipt.verification === 'pending' ? null : { status: 'no-op', changed: false, error: null },
159
+ completedSteps, pendingSteps,
160
+ result: pendingSteps.length > 0 || receipt.warningProjection === 'pending' || warnings.length > 0
161
+ ? 'completed_with_warnings' : 'completed',
162
+ warnings, error: null
163
+ };
164
+ return {
165
+ status: 'matched',
166
+ response: {
167
+ version: 2, id: requestId, phase: 'completed', exitCode: 0,
168
+ stdout: `${JSON.stringify({ version: 1, status: 'completed', changed: false, accepted: true, result, error: null })}\n`,
169
+ stderr: '', error: null
170
+ }
171
+ };
172
+ }
173
+
174
+ function payloadReference(payload: ReturnType<typeof createSandboxControlPayload>) {
175
+ return {
176
+ version: payload.version,
177
+ id: payload.id,
178
+ generation: payload.generation,
179
+ stdoutBytes: payload.stdoutBytes,
180
+ stderrBytes: payload.stderrBytes,
181
+ stdoutSha256: payload.stdoutSha256,
182
+ stderrSha256: payload.stderrSha256
183
+ };
184
+ }
185
+
186
+ function payloadMatchesEvidence(
187
+ payload: ReturnType<typeof readSandboxControlPayload>,
188
+ evidence: ReturnType<typeof readSandboxControlResultEvidence>
189
+ ): boolean {
190
+ return payload.id === evidence.id
191
+ && payload.generation === evidence.generation
192
+ && payload.stdoutBytes === evidence.stdoutBytes
193
+ && payload.stderrBytes === evidence.stderrBytes
194
+ && payload.stdoutSha256 === evidence.stdoutSha256
195
+ && payload.stderrSha256 === evidence.stderrSha256;
196
+ }
197
+
198
+ function outputMatchesEvidence(output: string, bytes: number, sha256: string): boolean {
199
+ return Buffer.byteLength(output, 'utf8') === bytes
200
+ && createHash('sha256').update(output, 'utf8').digest('hex') === sha256;
201
+ }
202
+
203
+ function genericRecoveryResponse(
204
+ requestId: string,
205
+ evidence: ReturnType<typeof readSandboxControlResultEvidence>,
206
+ payload: ReturnType<typeof readSandboxControlPayload> | null
207
+ ): SandboxControlResponse {
208
+ return {
209
+ version: 2,
210
+ id: requestId,
211
+ phase: 'completed',
212
+ exitCode: evidence.exitCode,
213
+ stdout: '',
214
+ stderr: payload ? '' : 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: broker restarted after executor completion\n',
215
+ error: null,
216
+ outputState: payload ? 'available' : 'unavailable',
217
+ payload: payload ? payloadReference(payload) : null
218
+ };
219
+ }
220
+
221
+ function recoveryResponse(
222
+ manifest: SandboxControlManifest,
223
+ request: SandboxControlRequest,
224
+ evidence: ReturnType<typeof readSandboxControlResultEvidence>,
225
+ payload: ReturnType<typeof readSandboxControlPayload> | null
226
+ ): SandboxControlResponse | null {
227
+ if (request.family === 'task-finalization' && evidence.exitCode === 0) {
228
+ const finalization = finalizationRecoveryResponse(manifest, request.id, evidence.exitCode);
229
+ return finalization.status === 'matched' ? finalization.response ?? null : null;
230
+ }
231
+ return genericRecoveryResponse(request.id, evidence, payload);
232
+ }
233
+
234
+ function terminalMatchesEvidence(
235
+ manifest: SandboxControlManifest,
236
+ request: SandboxControlRequest,
237
+ response: SandboxControlResponse,
238
+ evidence: ReturnType<typeof readSandboxControlResultEvidence>,
239
+ payload: ReturnType<typeof readSandboxControlPayload> | null,
240
+ payloadInvalid: boolean
241
+ ): { valid: boolean; payloadReferenced: boolean } {
242
+ if (request.family === 'task-finalization' && evidence.exitCode === 0) {
243
+ const expected = recoveryResponse(manifest, request, evidence, payload);
244
+ return {
245
+ valid: expected !== null && JSON.stringify(response) === JSON.stringify(expected),
246
+ payloadReferenced: false
247
+ };
248
+ }
249
+ if (response.version !== 2 || response.id !== request.id || response.phase !== 'completed'
250
+ || response.exitCode !== evidence.exitCode || response.error !== null) {
251
+ return { valid: false, payloadReferenced: false };
252
+ }
253
+ if (response.outputState === 'available') {
254
+ const reference = response.payload;
255
+ return {
256
+ valid: !payloadInvalid && payload !== null && response.stdout === '' && response.stderr === ''
257
+ && reference !== undefined && reference !== null
258
+ && reference.version === payload.version
259
+ && reference.id === payload.id
260
+ && reference.generation === payload.generation
261
+ && reference.stdoutBytes === payload.stdoutBytes
262
+ && reference.stderrBytes === payload.stderrBytes
263
+ && reference.stdoutSha256 === payload.stdoutSha256
264
+ && reference.stderrSha256 === payload.stderrSha256
265
+ && payloadMatchesEvidence(payload, evidence),
266
+ payloadReferenced: true
267
+ };
268
+ }
269
+ if (response.outputState === 'unavailable') {
270
+ const expected = genericRecoveryResponse(request.id, evidence, null);
271
+ return { valid: JSON.stringify(response) === JSON.stringify(expected), payloadReferenced: false };
272
+ }
273
+ return {
274
+ valid: response.payload === undefined
275
+ && outputMatchesEvidence(response.stdout, evidence.stdoutBytes, evidence.stdoutSha256)
276
+ && outputMatchesEvidence(response.stderr, evidence.stderrBytes, evidence.stderrSha256),
277
+ payloadReferenced: false
278
+ };
279
+ }
280
+
281
+ function publishExecutionResult(
282
+ manifest: SandboxControlManifest,
283
+ request: SandboxControlRequest,
284
+ result: SandboxControlExecutionResult,
285
+ brokerOwns: () => boolean
286
+ ): boolean {
287
+ const normalized = sanitizeSandboxControlResult(manifest, result);
288
+ let terminal: SandboxControlResponse | null = null;
289
+ if (request.family === 'task-finalization') {
290
+ const finalization = finalizationRecoveryResponse(manifest, request.id, normalized.exitCode);
291
+ if (finalization.status === 'deferred') return false;
292
+ terminal = finalization.response ?? null;
293
+ }
294
+ if (!terminal) {
295
+ const inline: SandboxControlResponse = {
296
+ version: 2, id: request.id, phase: 'completed', exitCode: normalized.exitCode,
297
+ stdout: normalized.stdout, stderr: normalized.stderr, error: null
298
+ };
299
+ if (sandboxControlEncodedJsonBytes(inline) <= SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES) {
300
+ terminal = inline;
301
+ }
302
+ let payload = null;
303
+ if (!terminal) {
304
+ try {
305
+ payload = createSandboxControlPayload(manifest, request.id, normalized);
306
+ const usage = sandboxControlGenerationUsage(manifest);
307
+ if (usage.bytes + sandboxControlEncodedJsonBytes(payload) <= SANDBOX_CONTROL_MAX_RESPONSE_BYTES) {
308
+ writeSandboxControlPayload(manifest, request.id, normalized);
309
+ } else {
310
+ payload = null;
311
+ }
312
+ } catch {
313
+ payload = null;
314
+ }
315
+ terminal = {
316
+ version: 2,
317
+ id: request.id,
318
+ phase: 'completed',
319
+ exitCode: normalized.exitCode,
320
+ stdout: '',
321
+ stderr: payload ? '' : 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: output payload was not retained\n',
322
+ error: null,
323
+ outputState: payload ? 'available' : 'unavailable',
324
+ payload: payload ? payloadReference(payload) : null
325
+ };
326
+ }
327
+ }
328
+ if (!brokerOwns()) return false;
329
+ return writeSandboxControlResponse(manifest, terminal);
330
+ }
331
+
332
+ function writeAcceptedResponse(manifest: SandboxControlManifest, response: SandboxControlResponse): void {
333
+ atomicWriteJsonNoReplace(acceptedResponsePath(manifest, response.id), response);
334
+ }
335
+
336
+ function removeAcceptedResponse(manifest: SandboxControlManifest, id: string): void {
337
+ fs.rmSync(acceptedResponsePath(manifest, id), { force: true });
58
338
  }
59
339
 
60
340
  function rejected(id: string, error: unknown): SandboxControlResponse {
@@ -117,8 +397,46 @@ function recoverProcessing(manifest: SandboxControlManifest, brokerOwns: () => b
117
397
  if (!brokerOwns()) return false;
118
398
  const descriptor = executionPath(manifest, entry.name);
119
399
  const existingResponse = responsePath(manifest, entry.name);
400
+ const processingDirectory = path.join(manifest.processingDir, entry.name);
401
+ let payloadReferenced = false;
120
402
  if (fs.existsSync(descriptor)) {
121
403
  const execution = readExecution(descriptor);
404
+ let resultEvidence: ReturnType<typeof readSandboxControlResultEvidence> | null = null;
405
+ let payload: ReturnType<typeof readSandboxControlPayload> | null = null;
406
+ let payloadInvalid = false;
407
+ let request: SandboxControlRequest | null = null;
408
+ let existingTerminalResponse: SandboxControlResponse | null = null;
409
+ try {
410
+ const rawRequest = JSON.parse(fs.readFileSync(path.join(processingDirectory, 'request.json'), 'utf8')) as Record<string, unknown>;
411
+ request = validateSandboxControlRequest(rawRequest, manifest, {
412
+ now: typeof rawRequest.issuedAt === 'number' ? rawRequest.issuedAt : undefined
413
+ });
414
+ } catch {
415
+ // Missing or malformed request evidence remains fail-closed below.
416
+ }
417
+ const resultPath = path.join(manifest.processingDir, entry.name, 'result.json');
418
+ if (fs.existsSync(resultPath)) {
419
+ try {
420
+ resultEvidence = readSandboxControlResultEvidence(resultPath);
421
+ if (resultEvidence.id !== entry.name || resultEvidence.generation !== manifest.generation) {
422
+ resultEvidence = null;
423
+ }
424
+ } catch {
425
+ resultEvidence = null;
426
+ }
427
+ }
428
+ const publishedPayloadPath = payloadPath(manifest, entry.name);
429
+ if (fs.existsSync(publishedPayloadPath)) {
430
+ try {
431
+ payload = readSandboxControlPayload(publishedPayloadPath);
432
+ if (payload.id !== entry.name || payload.generation !== manifest.generation
433
+ || (resultEvidence !== null && !payloadMatchesEvidence(payload, resultEvidence))) {
434
+ payloadInvalid = true;
435
+ }
436
+ } catch {
437
+ payloadInvalid = true;
438
+ }
439
+ }
122
440
  if (!terminateSandboxControlExecution(execution)) {
123
441
  throw new Error(`SANDBOX_CONTROL_EXECUTION_STILL_RUNNING: ${entry.name}`);
124
442
  }
@@ -128,22 +446,50 @@ function recoverProcessing(manifest: SandboxControlManifest, brokerOwns: () => b
128
446
  if (fs.existsSync(existingResponse)) {
129
447
  try {
130
448
  const response = JSON.parse(fs.readFileSync(existingResponse, 'utf8')) as SandboxControlResponse;
449
+ existingTerminalResponse = response;
131
450
  terminal = response.version === 2 && response.id === entry.name
132
451
  && (response.phase === 'completed' || response.phase === 'rejected');
452
+ if (terminal && response.outputState === 'available') {
453
+ if (!response.payload || payloadInvalid || !payload
454
+ || response.payload.version !== payload.version
455
+ || response.payload.id !== payload.id
456
+ || response.payload.generation !== payload.generation
457
+ || response.payload.stdoutBytes !== payload.stdoutBytes
458
+ || response.payload.stderrBytes !== payload.stderrBytes
459
+ || response.payload.stdoutSha256 !== payload.stdoutSha256
460
+ || response.payload.stderrSha256 !== payload.stderrSha256) throw new Error('payload missing or mismatched');
461
+ payloadReferenced = true;
462
+ } else if (terminal && (response.outputState !== undefined
463
+ || response.payload !== undefined && response.payload !== null)) {
464
+ throw new Error('payload state invalid');
465
+ }
466
+ if (!terminal) throw new Error('SANDBOX_CONTROL_RESPONSE_LAYOUT_INVALID');
133
467
  } catch {
134
- terminal = false;
468
+ throw new Error('SANDBOX_CONTROL_RESPONSE_LAYOUT_INVALID');
135
469
  }
136
470
  }
471
+ if (terminal && (!request || !resultEvidence)) continue;
472
+ if (terminal && request && resultEvidence) {
473
+ const reconciliation = terminalMatchesEvidence(manifest, request, existingTerminalResponse!, resultEvidence, payload, payloadInvalid);
474
+ if (!reconciliation.valid) continue;
475
+ payloadReferenced = reconciliation.payloadReferenced;
476
+ }
137
477
  if (!terminal) {
138
478
  if (!brokerOwns()) return false;
139
- writeResponse(manifest, unknown(entry.name));
479
+ if (!resultEvidence || !request || payloadInvalid) continue;
480
+ const recovered = recoveryResponse(manifest, request, resultEvidence, payload);
481
+ if (!recovered) continue;
482
+ writeSandboxControlResponse(manifest, recovered);
483
+ payloadReferenced = Boolean(payload) && request.family !== 'task-finalization';
140
484
  }
141
485
  } else {
142
486
  if (!brokerOwns()) return false;
143
- writeResponse(manifest, notExecuted(entry.name));
487
+ writeSandboxControlResponse(manifest, notExecuted(entry.name));
144
488
  }
145
489
  if (!brokerOwns()) return false;
146
- fs.rmSync(path.join(manifest.processingDir, entry.name), { recursive: true, force: true });
490
+ removeAcceptedResponse(manifest, entry.name);
491
+ fs.rmSync(processingDirectory, { recursive: true, force: true });
492
+ if (!payloadReferenced) fs.rmSync(payloadPath(manifest, entry.name), { force: true });
147
493
  }
148
494
  return true;
149
495
  }
@@ -163,11 +509,14 @@ export async function serveSandboxControl(
163
509
  timing?: SandboxControlTimingPolicy;
164
510
  inspectContainer?: (manifest: SandboxControlManifest) => Promise<ContainerObservation>;
165
511
  bindingCheck?: (manifest: SandboxControlManifest) => string | null;
512
+ prepareExecution?: typeof prepareSandboxControlExecution;
513
+ internalCliPath?: string;
166
514
  } = {}
167
515
  ): Promise<void> {
168
516
  const timing = options.timing ?? DEFAULT_SANDBOX_CONTROL_TIMING;
169
517
  const inspectContainer = options.inspectContainer ?? ((value: SandboxControlManifest) => inspectSandboxControlContainer(value));
170
518
  const bindingCheck = options.bindingCheck ?? bindingReason;
519
+ const prepareExecution = options.prepareExecution ?? prepareSandboxControlExecution;
171
520
  const manifest = readSandboxControlManifest(manifestPath);
172
521
  const root = path.dirname(manifestPath);
173
522
  const startTime = getProcessStartTime(process.pid);
@@ -237,6 +586,17 @@ export async function serveSandboxControl(
237
586
  break;
238
587
  }
239
588
  if (current.token !== manifest.token || current.generation !== manifest.generation) break;
589
+ if (active?.result && !active.resultEvidenceWritten) {
590
+ try {
591
+ active.result = sanitizeSandboxControlResult(manifest, active.result);
592
+ writeSandboxControlResultEvidence(manifest, active.request.id, active.result);
593
+ active.resultEvidenceWritten = true;
594
+ active.failure = null;
595
+ active.settled = true;
596
+ } catch (error) {
597
+ active.failure = error;
598
+ }
599
+ }
240
600
  const heartbeatNow = Date.now();
241
601
  if (heartbeatNow >= nextContainerHeartbeatAt) {
242
602
  let observation: ContainerObservation;
@@ -319,15 +679,15 @@ export async function serveSandboxControl(
319
679
  }
320
680
  if (settledExecution) {
321
681
  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
- });
682
+ let terminalCommitted = false;
683
+ if (settledExecution.result && settledExecution.resultEvidenceWritten) {
684
+ terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, brokerOwns);
327
685
  } else {
328
- writeResponse(manifest, unknown(settledExecution.request.id));
686
+ terminalCommitted = writeSandboxControlResponse(manifest, unknown(settledExecution.request.id));
329
687
  }
688
+ if (!terminalCommitted) return;
330
689
  if (!brokerOwns()) break;
690
+ removeAcceptedResponse(manifest, settledExecution.request.id);
331
691
  fs.rmSync(path.join(manifest.processingDir, settledExecution.request.id), { recursive: true, force: true });
332
692
  }
333
693
 
@@ -357,13 +717,26 @@ export async function serveSandboxControl(
357
717
  const request = validateSandboxControlRequest(JSON.parse(fs.readFileSync(claimed, 'utf8')), manifest);
358
718
  if (bindingCheck(manifest)) throw new Error('SANDBOX_WORKTREE_BINDING_LOST');
359
719
  if (readActiveLease(manifest)) throw new Error('SANDBOX_CONTROL_HANDOFF_ACTIVE');
360
- const prepared = await prepareSandboxControlExecution({
361
- manifest, manifestPath, request, requestPath: claimed, internalCliPath: process.argv[1]!
720
+ writeSandboxControlReservation(manifest, request.id, sandboxControlGenerationUsage(manifest));
721
+ const prepared = await prepareExecution({
722
+ manifest, manifestPath, request, requestPath: claimed,
723
+ internalCliPath: options.internalCliPath ?? process.argv[1]!
362
724
  });
363
- const execution: ActiveExecution = { request, prepared, result: null, failure: null, settled: false };
725
+ const execution: ActiveExecution = {
726
+ request, prepared, result: null, resultEvidenceWritten: false, failure: null, settled: false
727
+ };
364
728
  active = execution;
365
729
  prepared.completion.then(
366
- (result) => { execution.result = result; execution.settled = true; },
730
+ (result) => {
731
+ execution.result = sanitizeSandboxControlResult(manifest, result);
732
+ try {
733
+ writeSandboxControlResultEvidence(manifest, request.id, execution.result);
734
+ execution.resultEvidenceWritten = true;
735
+ execution.settled = true;
736
+ } catch (error) {
737
+ execution.failure = error;
738
+ }
739
+ },
367
740
  (error) => { execution.failure = error; execution.settled = true; }
368
741
  );
369
742
  if (!brokerOwns()) {
@@ -378,7 +751,7 @@ export async function serveSandboxControl(
378
751
  retiring = true;
379
752
  continue;
380
753
  }
381
- writeResponse(manifest, {
754
+ writeAcceptedResponse(manifest, {
382
755
  version: 2, id, phase: 'accepted', exitCode: null, stdout: '', stderr: '', error: null
383
756
  });
384
757
  if (!brokerOwns()) {
@@ -397,12 +770,13 @@ export async function serveSandboxControl(
397
770
  retiring = true;
398
771
  break;
399
772
  }
400
- writeResponse(manifest, unknown(id));
773
+ writeSandboxControlResponse(manifest, unknown(id));
401
774
  if (!brokerOwns()) {
402
775
  active = null;
403
776
  retiring = true;
404
777
  break;
405
778
  }
779
+ removeAcceptedResponse(manifest, id);
406
780
  fs.rmSync(path.join(manifest.processingDir, id), { recursive: true, force: true });
407
781
  active = null;
408
782
  claimed = null;
@@ -418,7 +792,7 @@ export async function serveSandboxControl(
418
792
  retiring = true;
419
793
  break;
420
794
  }
421
- writeResponse(manifest, rejected(id, error));
795
+ writeSandboxControlResponse(manifest, rejected(id, error));
422
796
  if (!brokerOwns()) {
423
797
  retiring = true;
424
798
  break;
@@ -432,8 +806,42 @@ export async function serveSandboxControl(
432
806
  } finally {
433
807
  if (active) {
434
808
  const owned = brokerOwns();
435
- active.prepared.terminate(owned);
436
- if (owned && brokerOwns()) writeResponse(manifest, unknown(active.request.id));
809
+ if (owned && active.result && !active.resultEvidenceWritten) {
810
+ try {
811
+ active.result = sanitizeSandboxControlResult(manifest, active.result);
812
+ writeSandboxControlResultEvidence(manifest, active.request.id, active.result);
813
+ active.resultEvidenceWritten = true;
814
+ active.failure = null;
815
+ active.settled = true;
816
+ } catch (error) {
817
+ active.failure = error;
818
+ }
819
+ }
820
+ if (owned && active.result && active.resultEvidenceWritten) {
821
+ if (publishExecutionResult(manifest, active.request, active.result, brokerOwns)) {
822
+ if (brokerOwns()) {
823
+ removeAcceptedResponse(manifest, active.request.id);
824
+ fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
825
+ active = null;
826
+ }
827
+ }
828
+ }
829
+ if (active) {
830
+ const terminationConfirmed = active.prepared.terminate(owned);
831
+ if (owned && brokerOwns() && active.request.family === 'task-finalization') {
832
+ const recovered = finalizationRecoveryResponse(manifest, active.request.id, 0);
833
+ if (recovered.status === 'matched' && recovered.response && writeSandboxControlResponse(manifest, recovered.response)) {
834
+ if (terminationConfirmed && brokerOwns()) {
835
+ removeAcceptedResponse(manifest, active.request.id);
836
+ fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
837
+ active = null;
838
+ }
839
+ }
840
+ }
841
+ if (owned && brokerOwns() && active && active.request.family !== 'task-finalization' && !active.resultEvidenceWritten) {
842
+ writeSandboxControlResponse(manifest, unknown(active.request.id));
843
+ }
844
+ }
437
845
  }
438
846
  if (brokerOwns()) {
439
847
  appendSandboxControlAudit(manifest, 'broker-stop', { pid: broker.pid, brokerId: broker.brokerId });