@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
@@ -7,57 +7,98 @@ import { loadShortIdByTaskId, normalizeShortIdInput } from '../task/short-id.js'
7
7
  import { buildTuiCommand, renderPrompt, selectTui } from './tui.js';
8
8
  import { getSkillRunSpec } from './skills.js';
9
9
  import { runHostCommand } from './host.js';
10
- const USAGE = `Usage: ai run <skill> [task-ref] [args...] [--tui <name>] [--recreate]
10
+ const USAGE = `Usage: ai run --skill <skill> [--task <task-ref>] [--tui <name>] [--recreate] [-- <skill-args...>]
11
11
 
12
12
  Task skills are scheduled inside the sandbox tmux session; ai run returns once
13
13
  the tmux window is created. --recreate authorizes container-only replacement
14
14
  when in-place sandbox recovery fails; it is not included in the TUI prompt.
15
15
 
16
16
  Examples:
17
- ai run create-task "describe the task" --tui codex
18
- ai run code-task #7 --tui codex --recreate`;
19
- function extractTui(args) {
20
- const rest = [];
17
+ ai run --skill create-task "describe the task" --tui codex
18
+ ai run --skill code-task --task 7 --tui codex --recreate`;
19
+ function optionValue(args, index, option) {
20
+ const value = args[index + 1];
21
+ if (!value || value.startsWith('--'))
22
+ throw new Error(`${option} requires a value`);
23
+ return value;
24
+ }
25
+ function extractHostOptions(args) {
26
+ const positionals = [];
27
+ const forwarded = [];
28
+ let skill = null;
29
+ let taskRef = null;
21
30
  let tui = null;
31
+ let recreate = false;
32
+ let separator = false;
22
33
  for (let i = 0; i < args.length; i += 1) {
23
34
  const arg = args[i];
24
- if (arg === '--tui') {
25
- const value = args[i + 1];
26
- if (!value)
27
- throw new Error('--tui requires a value');
28
- tui = value;
35
+ if (separator) {
36
+ forwarded.push(arg);
37
+ }
38
+ else if (arg === '--') {
39
+ separator = true;
40
+ }
41
+ else if (arg === '--skill') {
42
+ if (skill !== null)
43
+ throw new Error("duplicate option '--skill'");
44
+ skill = optionValue(args, i, '--skill');
45
+ i += 1;
46
+ }
47
+ else if (arg === '--task') {
48
+ if (taskRef !== null)
49
+ throw new Error("duplicate option '--task'");
50
+ taskRef = optionValue(args, i, '--task');
51
+ i += 1;
52
+ }
53
+ else if (arg === '--tui') {
54
+ if (tui !== null)
55
+ throw new Error("duplicate option '--tui'");
56
+ tui = optionValue(args, i, '--tui');
29
57
  i += 1;
30
- continue;
31
58
  }
32
- rest.push(arg);
59
+ else if (arg === '--recreate') {
60
+ if (recreate)
61
+ throw new Error("duplicate option '--recreate'");
62
+ recreate = true;
63
+ }
64
+ else if (arg.startsWith('-')) {
65
+ throw new Error(`Unknown option '${arg}'`);
66
+ }
67
+ else {
68
+ positionals.push(arg);
69
+ }
33
70
  }
34
- return { rest, tui };
71
+ return { positionals, forwarded, skill, taskRef, tui, recreate };
35
72
  }
36
73
  export function parseRunArgs(args) {
37
74
  if (args.length === 0 || args[0] === '--help' || args[0] === '-h') {
38
75
  throw new Error(USAGE);
39
76
  }
40
- const { rest: withRecoveryFlags, tui } = extractTui(args);
41
- const recreate = withRecoveryFlags.includes('--recreate');
42
- const rest = withRecoveryFlags.filter((arg) => arg !== '--recreate');
43
- const [skill = '', maybeTaskRef, ...remaining] = rest;
77
+ const { positionals, forwarded, skill, taskRef, tui, recreate } = extractHostOptions(args);
78
+ if (!skill)
79
+ throw new Error("option '--skill' is required");
44
80
  const spec = getSkillRunSpec(skill);
45
81
  if (!spec)
46
82
  throw new Error(`Unknown skill '${skill}'`);
47
83
  if (spec.kind === 'create') {
84
+ if (taskRef !== null)
85
+ throw new Error("create-task does not accept '--task'");
48
86
  if (recreate)
49
87
  throw new Error('--recreate is only valid for sandbox task runs');
50
- const createArgs = rest.slice(1);
88
+ const createArgs = [...positionals, ...forwarded];
51
89
  if (createArgs.length === 0)
52
90
  throw new Error('create-task requires a description');
53
91
  return { skill, taskRef: null, args: createArgs, tui };
54
92
  }
55
- if (!maybeTaskRef)
93
+ if (positionals.length > 0) {
94
+ throw new Error('ai run requires --skill and --task; positional host arguments are not supported');
95
+ }
96
+ if (taskRef === null)
56
97
  throw new Error(`${skill} requires a task-ref`);
57
98
  return {
58
99
  skill,
59
- taskRef: maybeTaskRef,
60
- args: remaining,
100
+ taskRef,
101
+ args: forwarded,
61
102
  tui,
62
103
  ...(recreate ? { recreate: true } : {})
63
104
  };
@@ -155,8 +196,7 @@ export async function runSkill(args, options = {}) {
155
196
  const commandConfig = options.command ?? config?.command ?? {};
156
197
  assertAllowedByConfig(parsed.skill, commandConfig);
157
198
  const tui = selectTui(parsed.skill, { cliTui: parsed.tui, command: commandConfig });
158
- const promptArgs = parsed.taskRef === null ? parsed.args : [parsed.taskRef, ...parsed.args];
159
- const prompt = renderPrompt({ tui, skill: parsed.skill, args: promptArgs });
199
+ const prompt = renderPrompt({ tui, skill: parsed.skill, args: parsed.args });
160
200
  const [file, argv] = buildTuiCommand(tui, prompt);
161
201
  const command = [file, ...argv];
162
202
  if (parsed.taskRef === null) {
@@ -67,6 +67,7 @@ export function loadConfig({ platformFn = platform, writeStderr = (chunk) => pro
67
67
  const agentClients = normalizeAgentClients(airc);
68
68
  const defaults = cloneDefaults();
69
69
  const sandbox = airc.sandbox ?? {};
70
+ const delivery = airc.delivery ?? {};
70
71
  const engine = validateSandboxEngine(sandbox.engine ?? defaults.engine, { platformFn });
71
72
  const project = airc.project;
72
73
  if (!project || typeof project !== 'string') {
@@ -111,6 +112,10 @@ export function loadConfig({ platformFn = platform, writeStderr = (chunk) => pro
111
112
  tools,
112
113
  customTools,
113
114
  agentClientState: agentClients.state,
115
+ delivery: {
116
+ remote: typeof delivery.remote === 'string' ? delivery.remote : 'origin',
117
+ baseRef: typeof delivery.baseRef === 'string' ? delivery.baseRef : 'main'
118
+ },
114
119
  refreshIntervalDays: asNonNegativeIntegerOrDefault(sandbox.refreshIntervalDays, defaults.refreshIntervalDays),
115
120
  dockerfile,
116
121
  vm: {
@@ -3,23 +3,25 @@ import path from 'node:path';
3
3
  import { randomUUID } from 'node:crypto';
4
4
  import { SANDBOX_CONTROL_ADMISSION_WINDOW_MS, SANDBOX_CONTROL_MAX_BYTES, SANDBOX_CONTROL_STATUS_STALE_MS, isSandboxControlFamily } from './protocol.js';
5
5
  import { normalizeAgentToken } from '../../agent-clients/tokens.js';
6
- import { readSandboxControlStatus } from './state.js';
6
+ import { readSandboxControlPayload, readSandboxControlStatus } from './state.js';
7
7
  const SANDBOX_CONTROL_RESPONSE_SETTLE_MS = 250;
8
8
  export class SandboxControlClientError extends Error {
9
9
  detail;
10
10
  accepted;
11
- constructor(detail, accepted = false) {
11
+ requestId;
12
+ constructor(detail, accepted = false, requestId = null) {
12
13
  super(detail.message);
13
14
  this.name = 'SandboxControlClientError';
14
15
  this.detail = detail;
15
16
  this.accepted = accepted;
17
+ this.requestId = requestId;
16
18
  }
17
19
  }
18
20
  function sleep(milliseconds) {
19
21
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
20
22
  }
21
- function clientError(code, message, retryable, accepted = false) {
22
- throw new SandboxControlClientError({ code, message: `${code}: ${message}`, retryable }, accepted);
23
+ function clientError(code, message, retryable, accepted = false, requestId = null) {
24
+ throw new SandboxControlClientError({ code, message: `${code}: ${message}`, retryable }, accepted, requestId);
23
25
  }
24
26
  function preflight(statusDir, generation, now = Date.now()) {
25
27
  let status;
@@ -47,12 +49,35 @@ function preflight(statusDir, generation, now = Date.now()) {
47
49
  function parseResponse(raw, id) {
48
50
  const response = JSON.parse(raw);
49
51
  if (response.version !== 2 || response.id !== id
50
- || !['accepted', 'completed', 'rejected'].includes(response.phase)
51
- || (response.phase === 'completed' && !Number.isInteger(response.exitCode))) {
52
- clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response is invalid', false);
52
+ || !['completed', 'rejected'].includes(response.phase)
53
+ || (response.phase === 'completed' && !Number.isInteger(response.exitCode))
54
+ || (response.outputState !== undefined && response.outputState !== 'available' && response.outputState !== 'unavailable')
55
+ || (response.outputState === 'available' && !response.payload)
56
+ || (response.outputState === 'unavailable' && response.payload !== null && response.payload !== undefined)) {
57
+ clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response is invalid', false, false, id);
53
58
  }
54
59
  return response;
55
60
  }
61
+ function readPublishedResponse(raw, id, channelDir) {
62
+ const response = parseResponse(raw, id);
63
+ if (response.outputState !== 'available')
64
+ return response;
65
+ const filePath = path.join(channelDir, 'responses', `${id}.payload.json`);
66
+ let payload;
67
+ try {
68
+ payload = readSandboxControlPayload(filePath);
69
+ }
70
+ catch {
71
+ clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker payload is missing or invalid', false, true, id);
72
+ }
73
+ if (!response.payload || response.payload.version !== payload.version
74
+ || response.payload.id !== payload.id || response.payload.generation !== payload.generation
75
+ || response.payload.stdoutBytes !== payload.stdoutBytes || response.payload.stderrBytes !== payload.stderrBytes
76
+ || response.payload.stdoutSha256 !== payload.stdoutSha256 || response.payload.stderrSha256 !== payload.stderrSha256) {
77
+ clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker payload reference is invalid', false, true, id);
78
+ }
79
+ return { ...response, stdout: payload.stdout, stderr: payload.stderr };
80
+ }
56
81
  function cancelPendingRequest(requestPath) {
57
82
  try {
58
83
  fs.unlinkSync(requestPath);
@@ -70,13 +95,16 @@ function exchangeSandboxControl(request, params) {
70
95
  preflight(statusDir, request.generation);
71
96
  const encoded = `${JSON.stringify(request)}\n`;
72
97
  if (Buffer.byteLength(encoded, 'utf8') > SANDBOX_CONTROL_MAX_BYTES) {
73
- clientError('SANDBOX_CONTROL_REQUEST_TOO_LARGE', 'request exceeds the control limit', false);
98
+ clientError('SANDBOX_CONTROL_REQUEST_TOO_LARGE', 'request exceeds the control limit', false, false, request.id);
74
99
  }
75
100
  const requestsDir = path.join(channelDir, 'requests');
76
101
  const responsesDir = path.join(channelDir, 'responses');
77
102
  const temporary = path.join(requestsDir, `.${request.id}.tmp`);
78
103
  const requestPath = path.join(requestsDir, `${request.id}.json`);
79
104
  const responsePath = path.join(responsesDir, `${request.id}.json`);
105
+ if (fs.existsSync(responsePath)) {
106
+ clientError('SANDBOX_CONTROL_REQUEST_REPLAYED', 'request id already has a terminal response', false, false, request.id);
107
+ }
80
108
  fs.writeFileSync(temporary, encoded, { mode: 0o600, flag: 'wx' });
81
109
  fs.renameSync(temporary, requestPath);
82
110
  const deadline = Date.now() + (params.timeoutMs ?? 30_000);
@@ -84,6 +112,9 @@ function exchangeSandboxControl(request, params) {
84
112
  let malformedResponseRaw = null;
85
113
  let malformedResponseObservedAt = 0;
86
114
  while (Date.now() < deadline) {
115
+ const acceptedPath = path.join(responsesDir, `${request.id}.accepted.json`);
116
+ if (fs.existsSync(acceptedPath))
117
+ accepted = true;
87
118
  if (fs.existsSync(responsePath)) {
88
119
  let raw;
89
120
  try {
@@ -96,7 +127,7 @@ function exchangeSandboxControl(request, params) {
96
127
  }
97
128
  let response;
98
129
  try {
99
- response = parseResponse(raw, request.id);
130
+ response = readPublishedResponse(raw, request.id, channelDir);
100
131
  malformedResponseRaw = null;
101
132
  }
102
133
  catch (error) {
@@ -108,7 +139,7 @@ function exchangeSandboxControl(request, params) {
108
139
  malformedResponseObservedAt = now;
109
140
  }
110
141
  else if (now - malformedResponseObservedAt >= SANDBOX_CONTROL_RESPONSE_SETTLE_MS) {
111
- clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response remained malformed', false, accepted);
142
+ clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response remained malformed', false, accepted, request.id);
112
143
  }
113
144
  sleep(25);
114
145
  continue;
@@ -117,7 +148,6 @@ function exchangeSandboxControl(request, params) {
117
148
  accepted = true;
118
149
  }
119
150
  else {
120
- fs.rmSync(responsePath, { force: true });
121
151
  return response;
122
152
  }
123
153
  }
@@ -128,18 +158,69 @@ function exchangeSandboxControl(request, params) {
128
158
  catch (error) {
129
159
  if (cancelPendingRequest(requestPath))
130
160
  throw error;
131
- clientError('SANDBOX_CONTROL_RESULT_UNKNOWN', 'request was claimed before broker availability was lost; inspect domain state before retrying', false, true);
161
+ clientError('SANDBOX_CONTROL_RESULT_UNKNOWN', 'request was claimed before broker availability was lost; inspect domain state before retrying', false, true, request.id);
132
162
  }
133
163
  }
134
164
  sleep(25);
135
165
  }
136
166
  if (accepted) {
137
- clientError('SANDBOX_CONTROL_RESULT_UNKNOWN', 'accepted request did not produce a final result; inspect domain state before retrying', false, true);
167
+ clientError('SANDBOX_CONTROL_RESULT_UNKNOWN', 'accepted request did not produce a final result; inspect domain state before retrying', false, true, request.id);
138
168
  }
139
169
  if (!cancelPendingRequest(requestPath)) {
140
- clientError('SANDBOX_CONTROL_RESULT_UNKNOWN', 'request was claimed but no acceptance result was observed; inspect domain state before retrying', false, true);
170
+ clientError('SANDBOX_CONTROL_RESULT_UNKNOWN', 'request was claimed but no acceptance result was observed; inspect domain state before retrying', false, true, request.id);
171
+ }
172
+ clientError('SANDBOX_CONTROL_BROKER_UNAVAILABLE', `broker did not accept the request within ${params.timeoutMs ?? 30_000}ms`, true, false, request.id);
173
+ }
174
+ export function recoverSandboxControl(requestId, params = {}) {
175
+ if (!/^[a-f0-9-]{16,64}$/u.test(requestId)) {
176
+ clientError('SANDBOX_CONTROL_REQUEST_INVALID', 'request id is invalid', false, true);
177
+ }
178
+ const channelDir = params.channelDir ?? process.env.AGENT_INFRA_CONTROL_DIR ?? '/run/agent-infra/control';
179
+ const responsePath = path.join(channelDir, 'responses', `${requestId}.json`);
180
+ const deadline = Date.now() + (params.timeoutMs ?? 30_000);
181
+ let accepted = false;
182
+ let malformedResponseRaw = null;
183
+ let malformedResponseObservedAt = 0;
184
+ while (Date.now() < deadline) {
185
+ if (fs.existsSync(path.join(channelDir, 'responses', `${requestId}.accepted.json`)))
186
+ accepted = true;
187
+ if (fs.existsSync(responsePath)) {
188
+ let raw;
189
+ try {
190
+ raw = fs.readFileSync(responsePath, 'utf8');
191
+ }
192
+ catch (error) {
193
+ if (error.code === 'ENOENT')
194
+ continue;
195
+ throw error;
196
+ }
197
+ let response;
198
+ try {
199
+ response = readPublishedResponse(raw, requestId, channelDir);
200
+ malformedResponseRaw = null;
201
+ }
202
+ catch (error) {
203
+ if (!(error instanceof SyntaxError))
204
+ throw error;
205
+ const now = Date.now();
206
+ if (malformedResponseRaw !== raw) {
207
+ malformedResponseRaw = raw;
208
+ malformedResponseObservedAt = now;
209
+ }
210
+ else if (now - malformedResponseObservedAt >= SANDBOX_CONTROL_RESPONSE_SETTLE_MS) {
211
+ clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response remained malformed', false, accepted, requestId);
212
+ }
213
+ sleep(25);
214
+ continue;
215
+ }
216
+ if (response.phase === 'accepted')
217
+ accepted = true;
218
+ else
219
+ return response;
220
+ }
221
+ sleep(25);
141
222
  }
142
- clientError('SANDBOX_CONTROL_BROKER_UNAVAILABLE', `broker did not accept the request within ${params.timeoutMs ?? 30_000}ms`, true);
223
+ clientError('SANDBOX_CONTROL_RESULT_UNKNOWN', 'request did not produce a final result; inspect domain state before retrying', false, accepted, requestId);
143
224
  }
144
225
  function authority(params) {
145
226
  const token = params.token ?? process.env.AGENT_INFRA_CONTROL_TOKEN;
@@ -191,7 +191,7 @@ function finalizationResult(result) {
191
191
  }
192
192
  export async function executeRequest(manifest, manifestPath, request, options = {}) {
193
193
  if (request.family === 'task-create') {
194
- const result = createTask(request.candidate, { repoRoot: manifest.repoRoot });
194
+ const result = await createTask(request.candidate, { repoRoot: manifest.repoRoot });
195
195
  return {
196
196
  exitCode: result.status === 'blocked' ? 2 : result.status === 'failed' ? 1 : 0,
197
197
  stdout: `${JSON.stringify(result)}\n`,
@@ -255,7 +255,7 @@ export async function executeRequest(manifest, manifestPath, request, options =
255
255
  manifestPath,
256
256
  requestId: request.id
257
257
  }), operation);
258
- return finalizationResult(result);
258
+ return finalizationResult(await result);
259
259
  }
260
260
  const boundArgs = bindSandboxControlTask(request, manifest.taskId);
261
261
  if (request.family === 'task-orchestration') {
@@ -325,7 +325,7 @@ export async function executeRequest(manifest, manifestPath, request, options =
325
325
  }
326
326
  if (operation.family !== 'task-lifecycle')
327
327
  throw new Error('SANDBOX_CONTROL_LIFECYCLE_OPERATION_INVALID');
328
- return format(dispatchTaskControlOperation(context, operation));
328
+ return format(await dispatchTaskControlOperation(context, operation));
329
329
  }
330
330
  export async function runSandboxControlExecutor(requestPath, nonce) {
331
331
  const gateOwner = await waitForGate(nonce);
@@ -448,25 +448,27 @@ function readSandboxControlManifestValue(manifestPath) {
448
448
  'mode', 'processingDir', 'project', 'publicStatusDir', 'repoRoot', 'runtimeDir',
449
449
  'taskId', 'token', 'worktreeRoot'
450
450
  ];
451
- if ('version' in candidate || typeof candidate.runtimeDir !== 'string'
452
- || Object.keys(manifest).sort().join(',') !== expectedKeys.sort().join(',')) {
453
- throw new Error('SANDBOX_CONTROL_MANIFEST_REBUILD_REQUIRED: container-only recreation/rebuild is required');
451
+ if (Object.keys(manifest).sort().join(',') !== expectedKeys.sort().join(',')) {
452
+ throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
454
453
  }
455
454
  if (typeof candidate.containerIdentity !== 'object' || candidate.containerIdentity === null
456
455
  || Array.isArray(candidate.containerIdentity))
457
456
  throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
458
457
  const containerIdentity = candidate.containerIdentity;
459
- if (typeof candidate.engine !== 'string' || candidate.engine.length === 0
458
+ if (Object.keys(containerIdentity).sort().join(',') !== 'id,labels'
459
+ || typeof candidate.engine !== 'string' || candidate.engine.length === 0
460
460
  || typeof candidate.repoRoot !== 'string' || typeof candidate.worktreeRoot !== 'string'
461
461
  || typeof candidate.project !== 'string' || typeof candidate.container !== 'string'
462
462
  || typeof containerIdentity.id !== 'string' || containerIdentity.id.length === 0
463
463
  || typeof containerIdentity.labels !== 'object' || containerIdentity.labels === null
464
464
  || Array.isArray(containerIdentity.labels)
465
465
  || Object.values(containerIdentity.labels).some((value) => typeof value !== 'string')
466
- || typeof candidate.branch !== 'string' || !['task-bound', 'branch-only'].includes(candidate.mode ?? '')
466
+ || typeof candidate.branch !== 'string'
467
+ || (candidate.mode !== 'task-bound' && candidate.mode !== 'branch-only')
467
468
  || (candidate.taskId !== null && typeof candidate.taskId !== 'string')
468
469
  || typeof candidate.channelDir !== 'string' || typeof candidate.publicStatusDir !== 'string'
469
- || typeof candidate.processingDir !== 'string' || typeof candidate.token !== 'string'
470
+ || typeof candidate.processingDir !== 'string' || typeof candidate.runtimeDir !== 'string'
471
+ || typeof candidate.token !== 'string'
470
472
  || typeof candidate.generation !== 'string'
471
473
  || (candidate.mode === 'task-bound' && (typeof candidate.taskId !== 'string' || candidate.taskId.length === 0))
472
474
  || (candidate.mode === 'branch-only' && candidate.taskId !== null))
@@ -480,7 +482,27 @@ function readSandboxControlManifestValue(manifestPath) {
480
482
  if (path.resolve(candidate.runtimeDir) !== path.join(root, 'runtime')) {
481
483
  throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
482
484
  }
483
- return candidate;
485
+ const normalized = {
486
+ engine: candidate.engine,
487
+ repoRoot: candidate.repoRoot,
488
+ worktreeRoot: candidate.worktreeRoot,
489
+ project: candidate.project,
490
+ container: candidate.container,
491
+ containerIdentity: {
492
+ id: containerIdentity.id,
493
+ labels: { ...containerIdentity.labels }
494
+ },
495
+ branch: candidate.branch,
496
+ mode: candidate.mode,
497
+ taskId: candidate.taskId,
498
+ token: candidate.token,
499
+ generation: candidate.generation,
500
+ channelDir: candidate.channelDir,
501
+ publicStatusDir: candidate.publicStatusDir,
502
+ processingDir: candidate.processingDir,
503
+ runtimeDir: candidate.runtimeDir
504
+ };
505
+ return normalized;
484
506
  }
485
507
  export function readSandboxControlManifest(manifestPath) {
486
508
  return readSandboxControlManifestValue(manifestPath);
@@ -1,6 +1,11 @@
1
+ import { createHash } from 'node:crypto';
1
2
  import { validateTaskCreateCandidate } from '../../task/create.js';
2
3
  import { normalizeAgentToken } from '../../agent-clients/tokens.js';
3
4
  export const SANDBOX_CONTROL_MAX_BYTES = 64 * 1024;
5
+ export const SANDBOX_CONTROL_MAX_LOGICAL_RECORDS = 1024;
6
+ export const SANDBOX_CONTROL_MAX_RESPONSE_BYTES = 64 * 1024 * 1024;
7
+ export const SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES = 1024 * 1024;
8
+ export const SANDBOX_CONTROL_RESERVATION_BYTES = SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES + 4 * 1024;
4
9
  export const SANDBOX_CONTROL_ADMISSION_WINDOW_MS = 2_000;
5
10
  export const SANDBOX_CONTROL_STATUS_INTERVAL_MS = 250;
6
11
  export const SANDBOX_CONTROL_STATUS_STALE_MS = 1_500;
@@ -26,6 +31,78 @@ export class SandboxControlProtocolError extends Error {
26
31
  function fail(code, message, retryable = false) {
27
32
  throw new SandboxControlProtocolError(code, message, retryable);
28
33
  }
34
+ export function parseSandboxControlResultEvidence(value) {
35
+ const evidence = value;
36
+ const keys = evidence && typeof evidence === 'object' ? Object.keys(evidence).sort().join(',') : '';
37
+ if (keys !== 'captureState,exitCode,generation,id,stderrBytes,stderrSha256,stdoutBytes,stdoutSha256,version'
38
+ || evidence?.version !== 1
39
+ || typeof evidence.id !== 'string' || !/^[a-f0-9-]{16,64}$/u.test(evidence.id)
40
+ || typeof evidence.generation !== 'string' || evidence.generation.length === 0
41
+ || !Number.isSafeInteger(evidence.exitCode)
42
+ || !Number.isSafeInteger(evidence.stdoutBytes) || evidence.stdoutBytes < 0
43
+ || !Number.isSafeInteger(evidence.stderrBytes) || evidence.stderrBytes < 0
44
+ || evidence.stdoutBytes + evidence.stderrBytes > SANDBOX_CONTROL_MAX_RESPONSE_BYTES
45
+ || typeof evidence.stdoutSha256 !== 'string' || !/^[a-f0-9]{64}$/u.test(evidence.stdoutSha256)
46
+ || typeof evidence.stderrSha256 !== 'string' || !/^[a-f0-9]{64}$/u.test(evidence.stderrSha256)
47
+ || evidence.captureState !== 'metadata-only') {
48
+ throw new Error('SANDBOX_CONTROL_RESULT_EVIDENCE_INVALID');
49
+ }
50
+ return evidence;
51
+ }
52
+ function validPayloadReference(value, id, generation) {
53
+ const payload = value;
54
+ const keys = payload && typeof payload === 'object' ? Object.keys(payload).sort().join(',') : '';
55
+ return keys === 'generation,id,stderrBytes,stderrSha256,stdoutBytes,stdoutSha256,version'
56
+ && payload?.version === 1
57
+ && payload.id === id
58
+ && payload.generation === generation
59
+ && Number.isSafeInteger(payload.stdoutBytes) && payload.stdoutBytes >= 0
60
+ && Number.isSafeInteger(payload.stderrBytes) && payload.stderrBytes >= 0
61
+ && payload.stdoutBytes + payload.stderrBytes <= SANDBOX_CONTROL_MAX_RESPONSE_BYTES
62
+ && typeof payload.stdoutSha256 === 'string' && /^[a-f0-9]{64}$/u.test(payload.stdoutSha256)
63
+ && typeof payload.stderrSha256 === 'string' && /^[a-f0-9]{64}$/u.test(payload.stderrSha256);
64
+ }
65
+ export function parseSandboxControlPayload(value) {
66
+ if (!value || typeof value !== 'object' || Array.isArray(value))
67
+ throw new Error('SANDBOX_CONTROL_PAYLOAD_INVALID');
68
+ const payload = value;
69
+ const reference = {
70
+ version: payload.version,
71
+ id: payload.id,
72
+ generation: payload.generation,
73
+ stdoutBytes: payload.stdoutBytes,
74
+ stderrBytes: payload.stderrBytes,
75
+ stdoutSha256: payload.stdoutSha256,
76
+ stderrSha256: payload.stderrSha256
77
+ };
78
+ const keys = Object.keys(payload).sort().join(',');
79
+ if (keys !== 'generation,id,stderr,stderrBytes,stderrSha256,stdout,stdoutBytes,stdoutSha256,version'
80
+ || !validPayloadReference(reference, reference.id, reference.generation)
81
+ || typeof payload.stdout !== 'string' || typeof payload.stderr !== 'string'
82
+ || Buffer.byteLength(payload.stdout, 'utf8') !== payload.stdoutBytes
83
+ || Buffer.byteLength(payload.stderr, 'utf8') !== payload.stderrBytes
84
+ || createHash('sha256').update(payload.stdout, 'utf8').digest('hex') !== payload.stdoutSha256
85
+ || createHash('sha256').update(payload.stderr, 'utf8').digest('hex') !== payload.stderrSha256) {
86
+ throw new Error('SANDBOX_CONTROL_PAYLOAD_INVALID');
87
+ }
88
+ return payload;
89
+ }
90
+ export function parseSandboxControlReservation(value) {
91
+ if (!value || typeof value !== 'object' || Array.isArray(value))
92
+ throw new Error('SANDBOX_CONTROL_RESERVATION_INVALID');
93
+ const reservation = value;
94
+ const keys = Object.keys(reservation).sort().join(',');
95
+ if (keys !== 'bytes,createdAt,generation,id,logicalRecords,version'
96
+ || reservation.version !== 1
97
+ || typeof reservation.id !== 'string' || !/^[a-f0-9-]{16,64}$/u.test(reservation.id)
98
+ || typeof reservation.generation !== 'string' || reservation.generation.length === 0
99
+ || reservation.logicalRecords !== 1
100
+ || !Number.isSafeInteger(reservation.bytes) || reservation.bytes < SANDBOX_CONTROL_RESERVATION_BYTES
101
+ || !Number.isSafeInteger(reservation.createdAt)) {
102
+ throw new Error('SANDBOX_CONTROL_RESERVATION_INVALID');
103
+ }
104
+ return reservation;
105
+ }
29
106
  export function isSandboxControlFamily(value) {
30
107
  return SANDBOX_CONTROL_FAMILIES.includes(value);
31
108
  }