@fitlab-ai/agent-infra 0.9.12 → 0.9.14

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 (402) hide show
  1. package/README.md +11 -11
  2. package/README.zh-CN.md +11 -11
  3. package/bin/cli.ts +26 -9
  4. package/dist/bin/cli.js +173 -157
  5. package/dist/bin/internal-cli.js +66 -40
  6. package/dist/lib/agent-client.js +5 -0
  7. package/dist/lib/agent-clients/next-steps.js +6 -1
  8. package/dist/lib/decide.js +3 -3
  9. package/dist/lib/internal/agent-client.js +5 -2
  10. package/dist/lib/internal/cli-route-inventory.js +152 -0
  11. package/dist/lib/internal/codex-lifecycle.js +9 -6
  12. package/dist/lib/internal/codex-sandbox-controller.js +8 -2
  13. package/dist/lib/internal/git-workflow.js +10 -6
  14. package/dist/lib/internal/platform-checks.js +19 -7
  15. package/dist/lib/internal/platform-comment.js +25 -13
  16. package/dist/lib/internal/platform-context.js +6 -3
  17. package/dist/lib/internal/platform-issue.js +20 -13
  18. package/dist/lib/internal/platform-metadata.js +88 -0
  19. package/dist/lib/internal/platform-pr-review.js +26 -13
  20. package/dist/lib/internal/platform-pr.js +84 -31
  21. package/dist/lib/internal/platform-release-notes.js +9 -6
  22. package/dist/lib/internal/platform-security.js +93 -0
  23. package/dist/lib/internal/pr-review-grade.js +25 -12
  24. package/dist/lib/internal/release-workflow.js +23 -9
  25. package/dist/lib/internal/sandbox-control.js +50 -7
  26. package/dist/lib/internal/task-activity.js +16 -7
  27. package/dist/lib/internal/task-artifact.js +235 -24
  28. package/dist/lib/internal/task-context.js +4 -1
  29. package/dist/lib/internal/task-create.js +50 -17
  30. package/dist/lib/internal/task-delivery.js +4 -1
  31. package/dist/lib/internal/task-event.js +10 -4
  32. package/dist/lib/internal/task-finalization.js +6 -3
  33. package/dist/lib/internal/task-invalidation.js +63 -0
  34. package/dist/lib/internal/task-ledger.js +20 -6
  35. package/dist/lib/internal/task-lifecycle.js +6 -3
  36. package/dist/lib/internal/task-operation-registry.js +421 -0
  37. package/dist/lib/internal/task-orchestration.js +12 -0
  38. package/dist/lib/internal/task-override.js +14 -7
  39. package/dist/lib/internal/task-qualification.js +90 -0
  40. package/dist/lib/internal/task-review.js +7 -4
  41. package/dist/lib/internal/task-short-id.js +11 -1
  42. package/dist/lib/internal/task-snapshot.js +4 -1
  43. package/dist/lib/internal/task-validate.js +7 -2
  44. package/dist/lib/internal/task-verify.js +11 -7
  45. package/dist/lib/internal/task-warning.js +11 -4
  46. package/dist/lib/platform/adapters.js +1 -3
  47. package/dist/lib/platform/change-request-git-evidence.js +27 -7
  48. package/dist/lib/platform/comment-safety.js +226 -0
  49. package/dist/lib/platform/completion-backfill.js +6 -5
  50. package/dist/lib/platform/context.js +70 -130
  51. package/dist/lib/platform/github-client.js +1 -1
  52. package/dist/lib/platform/github-provider.js +1130 -0
  53. package/dist/lib/platform/in-label-sync.js +143 -0
  54. package/dist/lib/platform/issue-comments.js +237 -72
  55. package/dist/lib/platform/issues.js +335 -189
  56. package/dist/lib/platform/metadata-labels.js +6 -2
  57. package/dist/lib/platform/none-provider.js +28 -0
  58. package/dist/lib/platform/pr-change-report.js +545 -0
  59. package/dist/lib/platform/pr-checks.js +119 -104
  60. package/dist/lib/platform/pr-review.js +88 -72
  61. package/dist/lib/platform/pr-summary.js +541 -50
  62. package/dist/lib/platform/provider-bridge.js +45 -0
  63. package/dist/lib/platform/provider-contract.d.ts +553 -0
  64. package/dist/lib/platform/provider-contract.js +99 -0
  65. package/dist/lib/platform/provider-loader.js +251 -0
  66. package/dist/lib/platform/provider-validation.js +577 -0
  67. package/dist/lib/platform/pull-request-metadata.js +8 -6
  68. package/dist/lib/platform/pull-requests.js +734 -543
  69. package/dist/lib/platform/release-notes.js +78 -55
  70. package/dist/lib/platform/releases.js +79 -50
  71. package/dist/lib/platform/repository-metadata.js +377 -0
  72. package/dist/lib/platform/resource-identity.d.ts +26 -0
  73. package/dist/lib/platform/resource-identity.js +92 -0
  74. package/dist/lib/platform/security-alerts.js +155 -0
  75. package/dist/lib/platform/task-identities.js +31 -0
  76. package/dist/lib/platform/verification-required.js +11 -12
  77. package/dist/lib/platform/verification-sync.js +129 -248
  78. package/dist/lib/pr-review/evidence-grading.js +19 -6
  79. package/dist/lib/process-data/index.js +7 -0
  80. package/dist/lib/run/index.js +68 -23
  81. package/dist/lib/sandbox/commands/control-status.js +22 -0
  82. package/dist/lib/sandbox/commands/create.js +32 -6
  83. package/dist/lib/sandbox/commands/ls.js +5 -0
  84. package/dist/lib/sandbox/commands/rm.js +827 -120
  85. package/dist/lib/sandbox/commands/show.js +16 -0
  86. package/dist/lib/sandbox/control/client.js +174 -19
  87. package/dist/lib/sandbox/control/completed-reentry.js +85 -0
  88. package/dist/lib/sandbox/control/container-identity.js +28 -31
  89. package/dist/lib/sandbox/control/executor.js +151 -16
  90. package/dist/lib/sandbox/control/lifecycle.js +621 -127
  91. package/dist/lib/sandbox/control/native-file-lock.js +177 -0
  92. package/dist/lib/sandbox/control/protocol.js +77 -0
  93. package/dist/lib/sandbox/control/server.js +677 -34
  94. package/dist/lib/sandbox/control/state.js +209 -4
  95. package/dist/lib/sandbox/control/task-view.js +183 -0
  96. package/dist/lib/sandbox/engines/authority.js +289 -0
  97. package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
  98. package/dist/lib/sandbox/index.js +5 -0
  99. package/dist/lib/sandbox/recovery.js +21 -1
  100. package/dist/lib/sandbox/workspace-view.js +2 -1
  101. package/dist/lib/sandbox/worktree-safety.js +4 -1
  102. package/dist/lib/server/index.js +8 -0
  103. package/dist/lib/server/protocol.js +38 -1
  104. package/dist/lib/task/activity-log.js +8 -1
  105. package/dist/lib/task/artifact-lifecycle.js +75 -18
  106. package/dist/lib/task/artifact-operations.js +520 -0
  107. package/dist/lib/task/artifact-repair-intent.js +59 -0
  108. package/dist/lib/task/artifact-schema.js +135 -0
  109. package/dist/lib/task/capabilities.js +310 -0
  110. package/dist/lib/task/commands/status.js +32 -0
  111. package/dist/lib/task/commit-operation.js +12 -1
  112. package/dist/lib/task/control-authority.js +12 -5
  113. package/dist/lib/task/create-service.js +111 -12
  114. package/dist/lib/task/create.js +25 -0
  115. package/dist/lib/task/current-contract.js +5 -1
  116. package/dist/lib/task/decision-details.js +1 -1
  117. package/dist/lib/task/events.js +416 -17
  118. package/dist/lib/task/finalization.js +37 -13
  119. package/dist/lib/task/human-override.js +58 -36
  120. package/dist/lib/task/index.js +11 -3
  121. package/dist/lib/task/invalidation-command.js +86 -0
  122. package/dist/lib/task/invalidation.js +260 -0
  123. package/dist/lib/task/ledger-intents.js +37 -0
  124. package/dist/lib/task/local-artifact-finalization.js +299 -0
  125. package/dist/lib/task/orchestration.js +240 -144
  126. package/dist/lib/task/pr-delivery-fact.js +102 -101
  127. package/dist/lib/task/qualification-audit.js +490 -0
  128. package/dist/lib/task/qualification-confirmation.js +109 -0
  129. package/dist/lib/task/qualification-intents.js +148 -0
  130. package/dist/lib/task/review-artifacts.js +22 -23
  131. package/dist/lib/task/review-finalization.js +121 -4
  132. package/dist/lib/task/review-fingerprint.js +24 -2
  133. package/dist/lib/task/rework-intent.js +138 -0
  134. package/dist/lib/task/sections.js +6 -4
  135. package/dist/lib/task/skill-scope.js +23 -0
  136. package/dist/lib/task/task-execution-lock.js +27 -17
  137. package/dist/lib/task/verification-config.js +11 -0
  138. package/dist/lib/task/verification-engine.js +93 -16
  139. package/dist/lib/task/verification.js +3 -3
  140. package/dist/lib/task/workflow-warning-intents.js +1 -1
  141. package/dist/lib/task/write.js +11 -0
  142. package/dist/lib/version.js +11 -1
  143. package/dist/lifecycle-build-manifest.json +3 -3
  144. package/lib/agent-client.ts +9 -0
  145. package/lib/agent-clients/next-steps.ts +6 -1
  146. package/lib/decide.ts +3 -3
  147. package/lib/init.ts +1 -1
  148. package/lib/internal/agent-client.ts +4 -2
  149. package/lib/internal/cli-route-inventory.ts +140 -0
  150. package/lib/internal/codex-lifecycle.ts +8 -6
  151. package/lib/internal/codex-sandbox-controller.ts +7 -2
  152. package/lib/internal/git-workflow.ts +9 -6
  153. package/lib/internal/platform-checks.ts +14 -6
  154. package/lib/internal/platform-comment.ts +20 -12
  155. package/lib/internal/platform-context.ts +5 -3
  156. package/lib/internal/platform-issue.ts +17 -11
  157. package/lib/internal/platform-metadata.ts +70 -0
  158. package/lib/internal/platform-pr-review.ts +23 -12
  159. package/lib/internal/platform-pr.ts +65 -27
  160. package/lib/internal/platform-release-notes.ts +8 -6
  161. package/lib/internal/platform-security.ts +83 -0
  162. package/lib/internal/pr-review-grade.ts +20 -11
  163. package/lib/internal/release-workflow.ts +21 -9
  164. package/lib/internal/sandbox-control.ts +44 -6
  165. package/lib/internal/task-activity.ts +15 -7
  166. package/lib/internal/task-artifact.ts +204 -22
  167. package/lib/internal/task-context.ts +3 -1
  168. package/lib/internal/task-create.ts +78 -17
  169. package/lib/internal/task-delivery.ts +3 -1
  170. package/lib/internal/task-event.ts +9 -4
  171. package/lib/internal/task-finalization.ts +6 -4
  172. package/lib/internal/task-invalidation.ts +41 -0
  173. package/lib/internal/task-ledger.ts +21 -8
  174. package/lib/internal/task-lifecycle.ts +5 -3
  175. package/lib/internal/task-operation-registry.ts +482 -0
  176. package/lib/internal/task-orchestration.ts +12 -0
  177. package/lib/internal/task-override.ts +15 -9
  178. package/lib/internal/task-qualification.ts +61 -0
  179. package/lib/internal/task-review.ts +6 -4
  180. package/lib/internal/task-short-id.ts +10 -1
  181. package/lib/internal/task-snapshot.ts +3 -1
  182. package/lib/internal/task-validate.ts +6 -2
  183. package/lib/internal/task-verify.ts +10 -7
  184. package/lib/internal/task-warning.ts +10 -4
  185. package/lib/platform/adapters.ts +3 -2
  186. package/lib/platform/change-request-git-evidence.ts +36 -9
  187. package/lib/platform/comment-safety.ts +250 -0
  188. package/lib/platform/completion-backfill.ts +9 -8
  189. package/lib/platform/context.ts +97 -134
  190. package/lib/platform/github-client.ts +2 -2
  191. package/lib/platform/github-provider.ts +1132 -0
  192. package/lib/platform/in-label-sync.ts +208 -0
  193. package/lib/platform/issue-comments.ts +255 -80
  194. package/lib/platform/issues.ts +344 -174
  195. package/lib/platform/metadata-labels.ts +5 -2
  196. package/lib/platform/none-provider.ts +35 -0
  197. package/lib/platform/pr-change-report.ts +639 -0
  198. package/lib/platform/pr-checks.ts +126 -106
  199. package/lib/platform/pr-review.ts +88 -85
  200. package/lib/platform/pr-summary.ts +581 -53
  201. package/lib/platform/provider-bridge.ts +82 -0
  202. package/lib/platform/provider-contract.ts +611 -0
  203. package/lib/platform/provider-loader.ts +307 -0
  204. package/lib/platform/provider-validation.ts +611 -0
  205. package/lib/platform/pull-request-metadata.ts +10 -7
  206. package/lib/platform/pull-requests.ts +781 -550
  207. package/lib/platform/release-notes.ts +76 -59
  208. package/lib/platform/releases.ts +77 -43
  209. package/lib/platform/repository-metadata.ts +453 -0
  210. package/lib/platform/resource-identity.ts +121 -0
  211. package/lib/platform/security-alerts.ts +191 -0
  212. package/lib/platform/task-identities.ts +40 -0
  213. package/lib/platform/types.ts +4 -2
  214. package/lib/platform/verification-required.ts +11 -13
  215. package/lib/platform/verification-sync.ts +128 -304
  216. package/lib/pr-review/evidence-grading.ts +25 -8
  217. package/lib/process-data/index.ts +7 -0
  218. package/lib/run/index.ts +62 -22
  219. package/lib/sandbox/commands/control-status.ts +26 -0
  220. package/lib/sandbox/commands/create.ts +46 -6
  221. package/lib/sandbox/commands/ls.ts +7 -0
  222. package/lib/sandbox/commands/rm.ts +1000 -87
  223. package/lib/sandbox/commands/show.ts +16 -0
  224. package/lib/sandbox/control/client.ts +203 -19
  225. package/lib/sandbox/control/completed-reentry.ts +98 -0
  226. package/lib/sandbox/control/container-identity.ts +27 -43
  227. package/lib/sandbox/control/executor.ts +180 -15
  228. package/lib/sandbox/control/lifecycle.ts +629 -17
  229. package/lib/sandbox/control/native-file-lock.ts +209 -0
  230. package/lib/sandbox/control/protocol.ts +118 -2
  231. package/lib/sandbox/control/server.ts +724 -34
  232. package/lib/sandbox/control/state.ts +257 -4
  233. package/lib/sandbox/control/task-view.ts +240 -0
  234. package/lib/sandbox/engines/authority.ts +365 -0
  235. package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
  236. package/lib/sandbox/index.ts +7 -0
  237. package/lib/sandbox/recovery.ts +21 -1
  238. package/lib/sandbox/workspace-view.ts +9 -3
  239. package/lib/sandbox/worktree-safety.ts +4 -1
  240. package/lib/server/index.ts +10 -0
  241. package/lib/server/protocol.ts +36 -1
  242. package/lib/task/activity-log.ts +8 -0
  243. package/lib/task/artifact-lifecycle.ts +70 -22
  244. package/lib/task/artifact-operations.ts +626 -0
  245. package/lib/task/artifact-repair-intent.ts +71 -0
  246. package/lib/task/artifact-schema.ts +192 -0
  247. package/lib/task/capabilities.ts +331 -0
  248. package/lib/task/commands/status.ts +38 -0
  249. package/lib/task/commit-operation.ts +12 -1
  250. package/lib/task/control-authority.ts +22 -9
  251. package/lib/task/create-service.ts +139 -14
  252. package/lib/task/create.ts +27 -0
  253. package/lib/task/current-contract.ts +6 -1
  254. package/lib/task/decision-details.ts +6 -2
  255. package/lib/task/events.ts +392 -16
  256. package/lib/task/finalization.ts +50 -14
  257. package/lib/task/human-override.ts +70 -46
  258. package/lib/task/index.ts +16 -3
  259. package/lib/task/invalidation-command.ts +86 -0
  260. package/lib/task/invalidation.ts +332 -0
  261. package/lib/task/ledger-intents.ts +35 -1
  262. package/lib/task/local-artifact-finalization.ts +432 -0
  263. package/lib/task/orchestration.ts +223 -133
  264. package/lib/task/pr-delivery-fact.ts +131 -148
  265. package/lib/task/qualification-audit.ts +599 -0
  266. package/lib/task/qualification-confirmation.ts +117 -0
  267. package/lib/task/qualification-intents.ts +150 -0
  268. package/lib/task/review-artifacts.ts +21 -22
  269. package/lib/task/review-finalization.ts +133 -4
  270. package/lib/task/review-fingerprint.ts +23 -2
  271. package/lib/task/rework-intent.ts +165 -0
  272. package/lib/task/sections.ts +6 -4
  273. package/lib/task/skill-scope.ts +24 -0
  274. package/lib/task/task-execution-lock.ts +23 -14
  275. package/lib/task/verification-config.ts +12 -0
  276. package/lib/task/verification-engine.ts +103 -17
  277. package/lib/task/verification.ts +4 -4
  278. package/lib/task/workflow-warning-intents.ts +1 -1
  279. package/lib/task/write.ts +19 -0
  280. package/lib/update.ts +1 -1
  281. package/lib/version.ts +15 -1
  282. package/package.json +12 -1
  283. package/templates/.agents/rules/README.en.md +3 -1
  284. package/templates/.agents/rules/README.zh-CN.md +3 -1
  285. package/templates/.agents/rules/decision-qualification.en.md +22 -0
  286. package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
  287. package/templates/.agents/rules/evidence-reporting.en.md +28 -0
  288. package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
  289. package/templates/.agents/rules/issue-pr-commands.en.md +1 -1
  290. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
  291. package/templates/.agents/rules/issue-sync.en.md +2 -0
  292. package/templates/.agents/rules/issue-sync.zh-CN.md +2 -0
  293. package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
  294. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
  295. package/templates/.agents/rules/local-artifact-repair.en.md +13 -2
  296. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +13 -2
  297. package/templates/.agents/rules/pr-sync.en.md +10 -4
  298. package/templates/.agents/rules/pr-sync.zh-CN.md +17 -4
  299. package/templates/.agents/rules/security-alerts.en.md +8 -8
  300. package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
  301. package/templates/.agents/rules/task-short-id.en.md +1 -4
  302. package/templates/.agents/rules/task-short-id.zh-CN.md +1 -3
  303. package/templates/.agents/skills/analyze-task/SKILL.en.md +28 -5
  304. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +29 -5
  305. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
  306. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
  307. package/templates/.agents/skills/check-task/SKILL.en.md +16 -37
  308. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -37
  309. package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
  310. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
  311. package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
  312. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
  313. package/templates/.agents/skills/code-task/SKILL.en.md +41 -9
  314. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +43 -11
  315. package/templates/.agents/skills/code-task/config/verify.en.json +6 -14
  316. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +6 -14
  317. package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
  318. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
  319. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
  320. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
  321. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
  322. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
  323. package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
  324. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
  325. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +2 -0
  326. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +2 -0
  327. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +3 -3
  328. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +3 -3
  329. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +10 -4
  330. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +10 -4
  331. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +2 -0
  332. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +2 -0
  333. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +4 -3
  334. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +4 -3
  335. package/templates/.agents/skills/complete-task/SKILL.en.md +26 -21
  336. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +25 -21
  337. package/templates/.agents/skills/create-pr/SKILL.en.md +7 -6
  338. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -7
  339. package/templates/.agents/skills/create-pr/config/verify.json +1 -0
  340. package/templates/.agents/skills/create-pr/reference/change-report.en.md +16 -4
  341. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +16 -4
  342. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +5 -3
  343. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +5 -3
  344. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +1 -1
  345. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +1 -1
  346. package/templates/.agents/skills/create-pr/scripts/change-report.mjs +3 -0
  347. package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
  348. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -0
  349. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  350. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  351. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  352. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  353. package/templates/.agents/skills/init-labels/SKILL.en.md +8 -8
  354. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
  355. package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
  356. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
  357. package/templates/.agents/skills/plan-task/SKILL.en.md +27 -4
  358. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +26 -4
  359. package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
  360. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
  361. package/templates/.agents/skills/review-analysis/SKILL.en.md +17 -4
  362. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +17 -4
  363. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
  364. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
  365. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
  366. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
  367. package/templates/.agents/skills/review-code/SKILL.en.md +18 -5
  368. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +18 -5
  369. package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
  370. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
  371. package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
  372. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
  373. package/templates/.agents/skills/review-plan/SKILL.en.md +17 -4
  374. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +17 -4
  375. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
  376. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
  377. package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
  378. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
  379. package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
  380. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
  381. package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
  382. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
  383. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +9 -3
  384. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +9 -3
  385. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +3 -1
  386. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +3 -1
  387. package/templates/.agents/skills/test/SKILL.en.md +13 -2
  388. package/templates/.agents/skills/test/SKILL.zh-CN.md +13 -2
  389. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
  390. package/templates/.agents/skills/watch-pr/SKILL.en.md +2 -2
  391. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +2 -2
  392. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +6 -0
  393. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +6 -0
  394. package/templates/.agents/templates/task.en.md +24 -1
  395. package/templates/.agents/templates/task.zh-CN.md +24 -1
  396. package/templates/.github/workflows/pr-label.yml +7 -26
  397. package/templates/.agents/scripts/security-alerts.github.sh +0 -151
  398. package/templates/.agents/scripts/security-alerts.sh +0 -31
  399. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
  400. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
  401. package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
  402. package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
@@ -16,6 +16,7 @@ import { fetchSandboxRows, selectSandboxContainer } from './list-running.ts';
16
16
  import { resolveTools, toolConfigDirCandidates } from '../tools.ts';
17
17
  import { detectEngine } from '../engine.ts';
18
18
  import { resolveSandboxTarget } from '../workspace-identity.ts';
19
+ import { readSandboxControlStatusForRow } from './control-status.ts';
19
20
 
20
21
  const USAGE = `Usage: ai sandbox show <branch | TASK-id | N>
21
22
 
@@ -93,6 +94,21 @@ export function show(args: string[] = []): void {
93
94
  process.stdout.write(` Mode: ${container?.workspaceMode ?? 'legacy-invalid'}\n`);
94
95
  process.stdout.write(` Task: ${container?.taskId ?? '-'}\n`);
95
96
 
97
+ p.log.step('Control status');
98
+ const controlStatus = container
99
+ ? readSandboxControlStatusForRow(config, container)
100
+ : null;
101
+ if (!controlStatus) {
102
+ p.log.warn(' Control status unavailable');
103
+ } else {
104
+ process.stdout.write(` Health: ${controlStatus.state}\n`);
105
+ process.stdout.write(` Task view: ${controlStatus.taskView.state}\n`);
106
+ process.stdout.write(` Task source: ${controlStatus.taskView.observedSource ?? '-'}\n`);
107
+ if (controlStatus.taskView.reasonCode) {
108
+ process.stdout.write(` Task view reason: ${controlStatus.taskView.reasonCode}\n`);
109
+ }
110
+ }
111
+
96
112
  for (const tool of detail.toolStates) {
97
113
  p.log.step(`${tool.name} state`);
98
114
  if (tool.entries.length === 0) {
@@ -20,19 +20,78 @@ import type {
20
20
  } from './controller-registration.ts';
21
21
  import type { ProcessIdentity } from '../../server/process-state.ts';
22
22
  import { normalizeAgentToken } from '../../agent-clients/tokens.ts';
23
- import { readSandboxControlStatus } from './state.ts';
23
+ import { readSandboxControlPayload, readSandboxControlStatus } from './state.ts';
24
24
  import type { TaskCreateCandidateV1 } from '../../task/create.ts';
25
+ import { accessSandboxTaskView, taskViewFromStatus, type TaskViewAccessEffect } from './task-view.ts';
25
26
 
26
27
  const SANDBOX_CONTROL_RESPONSE_SETTLE_MS = 250;
27
28
 
29
+ type SandboxControlEnvironmentClassification = Readonly<{
30
+ kind: 'direct' | 'controlled' | 'invalid';
31
+ mode?: 'task-bound' | 'branch-only';
32
+ code?: 'TASK_CONTROL_TRANSPORT_INVALID';
33
+ message?: string;
34
+ }>;
35
+
36
+ export function classifySandboxControlEnvironment(
37
+ env: NodeJS.ProcessEnv = process.env
38
+ ): SandboxControlEnvironmentClassification {
39
+ const controlKeys = [
40
+ 'AGENT_INFRA_CONTROL_TOKEN',
41
+ 'AGENT_INFRA_CONTROL_GENERATION',
42
+ 'AGENT_INFRA_CONTROL_DIR',
43
+ 'AGENT_INFRA_CONTROL_STATUS_DIR'
44
+ ] as const;
45
+ const hasControlMarker = controlKeys.some((key) => Boolean(env[key]));
46
+ const hasTaskIdentity = Boolean(env.AGENT_INFRA_TASK_ID);
47
+ const hasRuntime = Boolean(env.AGENT_INFRA_RUNTIME_DIR);
48
+ const hasExecutorMarker = Boolean(env.AGENT_INFRA_EXECUTOR_MANIFEST);
49
+ const hasControllerBinding = Boolean(env.AGENT_INFRA_CONTROL_CONTROLLER_BINDING);
50
+ const hasAnyMarker = hasControlMarker || hasTaskIdentity || hasRuntime || hasExecutorMarker || hasControllerBinding;
51
+ const hasCompleteControl = controlKeys.every((key) => Boolean(env[key]));
52
+
53
+ if (!hasAnyMarker) return { kind: 'direct' };
54
+ if (hasExecutorMarker) {
55
+ return {
56
+ kind: 'invalid',
57
+ code: 'TASK_CONTROL_TRANSPORT_INVALID',
58
+ message: 'executor context is only valid for sandbox-control execute'
59
+ };
60
+ }
61
+ if (hasControllerBinding) {
62
+ return {
63
+ kind: 'invalid',
64
+ code: 'TASK_CONTROL_TRANSPORT_INVALID',
65
+ message: 'sandbox client control configuration is incomplete or conflicting'
66
+ };
67
+ }
68
+ if (!hasCompleteControl) {
69
+ return {
70
+ kind: 'invalid',
71
+ code: 'TASK_CONTROL_TRANSPORT_INVALID',
72
+ message: 'sandbox client control configuration is incomplete or conflicting'
73
+ };
74
+ }
75
+ if (hasTaskIdentity !== hasRuntime) {
76
+ return {
77
+ kind: 'invalid',
78
+ code: 'TASK_CONTROL_TRANSPORT_INVALID',
79
+ message: 'sandbox task identity and runtime binding must be provided together'
80
+ };
81
+ }
82
+ return { kind: 'controlled', mode: hasTaskIdentity ? 'task-bound' : 'branch-only' };
83
+ }
84
+
28
85
  export class SandboxControlClientError extends Error {
29
86
  readonly detail: SandboxControlError;
30
87
  readonly accepted: boolean;
31
- constructor(detail: SandboxControlError, accepted = false) {
88
+ readonly requestId: string | null;
89
+ constructor(detail: SandboxControlError, accepted = false, requestId: string | null = null) {
32
90
  super(detail.message);
33
91
  this.name = 'SandboxControlClientError';
34
92
  this.detail = detail;
35
93
  this.accepted = accepted;
94
+ this.requestId = requestId;
36
95
  }
37
96
  }
38
97
 
@@ -40,11 +99,29 @@ function sleep(milliseconds: number): void {
40
99
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
41
100
  }
42
101
 
43
- function clientError(code: string, message: string, retryable: boolean, accepted = false): never {
44
- throw new SandboxControlClientError({ code, message: `${code}: ${message}`, retryable }, accepted);
102
+ function clientError(
103
+ code: string,
104
+ message: string,
105
+ retryable: boolean,
106
+ accepted = false,
107
+ requestId: string | null = null
108
+ ): never {
109
+ throw new SandboxControlClientError({ code, message: `${code}: ${message}`, retryable }, accepted, requestId);
110
+ }
111
+
112
+ function hasTaskBoundMarker(env: NodeJS.ProcessEnv = process.env): boolean {
113
+ return Boolean(env.AGENT_INFRA_TASK_ID)
114
+ && Boolean(env.AGENT_INFRA_CONTROL_TOKEN || env.AGENT_INFRA_CONTROL_GENERATION
115
+ || env.AGENT_INFRA_CONTROL_STATUS_DIR || env.AGENT_INFRA_RUNTIME_DIR || env.AGENT_INFRA_CONTROL_DIR);
45
116
  }
46
117
 
47
- function preflight(statusDir: string, generation: string, now = Date.now()): void {
118
+ function preflight(
119
+ statusDir: string,
120
+ generation: string,
121
+ taskViewEffect: TaskViewAccessEffect | null = null,
122
+ now = Date.now(),
123
+ env: NodeJS.ProcessEnv = process.env
124
+ ): void {
48
125
  let status;
49
126
  try {
50
127
  status = readSandboxControlStatus(statusDir);
@@ -63,18 +140,60 @@ function preflight(statusDir: string, generation: string, now = Date.now()): voi
63
140
  clientError(code, 'broker is parked until the host restores a valid binding', true);
64
141
  }
65
142
  if (status.state !== 'healthy') clientError('SANDBOX_CONTROL_BROKER_UNAVAILABLE', 'broker is not ready', true);
143
+ if (taskViewEffect && hasTaskBoundMarker(env)) {
144
+ const view = taskViewFromStatus(status);
145
+ if (view.taskId !== env.AGENT_INFRA_TASK_ID) {
146
+ clientError('SANDBOX_TASK_VIEW_TASK_ID_MISMATCH', 'task view task id does not match the sandbox task marker', false);
147
+ }
148
+ const access = accessSandboxTaskView(view, taskViewEffect);
149
+ if (!access.allowed) {
150
+ clientError(
151
+ access.reasonCode ?? 'SANDBOX_TASK_VIEW_DENIED',
152
+ access.message ?? 'task view denied',
153
+ false
154
+ );
155
+ }
156
+ }
157
+ }
158
+
159
+ function taskViewEffectForRequest(request: SandboxControlRequest): TaskViewAccessEffect | null {
160
+ if (request.family === 'task-lifecycle' || request.family === 'task-finalization') return 'progress';
161
+ if (request.family === 'task-orchestration') return request.args[1] === 'status' ? 'diagnostic' : 'progress';
162
+ return null;
66
163
  }
67
164
 
68
- function parseResponse(raw: string, id: string): SandboxControlResponse {
165
+ function parseResponse(raw: string, id: string, accepted = false): SandboxControlResponse {
69
166
  const response = JSON.parse(raw) as SandboxControlResponse;
70
167
  if (response.version !== 2 || response.id !== id
71
- || !['accepted', 'completed', 'rejected'].includes(response.phase)
72
- || (response.phase === 'completed' && !Number.isInteger(response.exitCode))) {
73
- clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response is invalid', false);
168
+ || !['completed', 'rejected'].includes(response.phase)
169
+ || (response.phase === 'completed' && !Number.isInteger(response.exitCode))
170
+ || (response.outputState !== undefined && response.outputState !== 'available' && response.outputState !== 'unavailable')
171
+ || (response.outputState === 'available' && !response.payload)
172
+ || (response.outputState === 'unavailable' && response.payload !== null && response.payload !== undefined)) {
173
+ clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response is invalid', false, accepted, id);
74
174
  }
75
175
  return response;
76
176
  }
77
177
 
178
+ function readPublishedResponse(raw: string, id: string, channelDir: string, accepted = false): SandboxControlResponse {
179
+ const response = parseResponse(raw, id, accepted);
180
+ if (response.outputState !== 'available') return response;
181
+ const filePath = path.join(channelDir, 'responses', `${id}.payload.json`);
182
+ let payload;
183
+ try {
184
+ payload = readSandboxControlPayload(filePath);
185
+ } catch {
186
+ clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker payload is missing or invalid', false, true, id);
187
+ }
188
+ if (!response.payload || response.payload.version !== payload.version
189
+ || response.payload.id !== payload.id || response.payload.generation !== payload.generation
190
+ || response.payload.stdoutBytes !== payload.stdoutBytes || response.payload.stderrBytes !== payload.stderrBytes
191
+ || response.payload.stdoutSha256 !== payload.stdoutSha256 || response.payload.stderrSha256 !== payload.stderrSha256) {
192
+ clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker payload reference is invalid', false, true, id);
193
+ }
194
+ return { ...response, stdout: payload.stdout, stderr: payload.stderr };
195
+ }
196
+
78
197
  function cancelPendingRequest(requestPath: string): boolean {
79
198
  try {
80
199
  fs.unlinkSync(requestPath);
@@ -92,16 +211,20 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
92
211
  }>): SandboxControlResponse {
93
212
  const channelDir = params.channelDir ?? process.env.AGENT_INFRA_CONTROL_DIR ?? '/run/agent-infra/control';
94
213
  const statusDir = params.statusDir ?? process.env.AGENT_INFRA_CONTROL_STATUS_DIR ?? '/run/agent-infra/control-status';
95
- preflight(statusDir, request.generation);
214
+ const taskViewEffect = taskViewEffectForRequest(request);
215
+ preflight(statusDir, request.generation, taskViewEffect);
96
216
  const encoded = `${JSON.stringify(request)}\n`;
97
217
  if (Buffer.byteLength(encoded, 'utf8') > SANDBOX_CONTROL_MAX_BYTES) {
98
- clientError('SANDBOX_CONTROL_REQUEST_TOO_LARGE', 'request exceeds the control limit', false);
218
+ clientError('SANDBOX_CONTROL_REQUEST_TOO_LARGE', 'request exceeds the control limit', false, false, request.id);
99
219
  }
100
220
  const requestsDir = path.join(channelDir, 'requests');
101
221
  const responsesDir = path.join(channelDir, 'responses');
102
222
  const temporary = path.join(requestsDir, `.${request.id}.tmp`);
103
223
  const requestPath = path.join(requestsDir, `${request.id}.json`);
104
224
  const responsePath = path.join(responsesDir, `${request.id}.json`);
225
+ if (fs.existsSync(responsePath)) {
226
+ clientError('SANDBOX_CONTROL_REQUEST_REPLAYED', 'request id already has a terminal response', false, false, request.id);
227
+ }
105
228
  fs.writeFileSync(temporary, encoded, { mode: 0o600, flag: 'wx' });
106
229
  fs.renameSync(temporary, requestPath);
107
230
  const deadline = Date.now() + (params.timeoutMs ?? 30_000);
@@ -109,6 +232,8 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
109
232
  let malformedResponseRaw: string | null = null;
110
233
  let malformedResponseObservedAt = 0;
111
234
  while (Date.now() < deadline) {
235
+ const acceptedPath = path.join(responsesDir, `${request.id}.accepted.json`);
236
+ if (fs.existsSync(acceptedPath)) accepted = true;
112
237
  if (fs.existsSync(responsePath)) {
113
238
  let raw: string;
114
239
  try {
@@ -119,7 +244,9 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
119
244
  }
120
245
  let response: SandboxControlResponse;
121
246
  try {
122
- response = parseResponse(raw, request.id);
247
+ // The request-specific terminal proves the broker claimed the request; preserve recovery evidence if marker cleanup won the race.
248
+ accepted = true;
249
+ response = readPublishedResponse(raw, request.id, channelDir, accepted);
123
250
  malformedResponseRaw = null;
124
251
  } catch (error) {
125
252
  if (!(error instanceof SyntaxError)) throw error;
@@ -128,7 +255,7 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
128
255
  malformedResponseRaw = raw;
129
256
  malformedResponseObservedAt = now;
130
257
  } else if (now - malformedResponseObservedAt >= SANDBOX_CONTROL_RESPONSE_SETTLE_MS) {
131
- clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response remained malformed', false, accepted);
258
+ clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response remained malformed', false, accepted, request.id);
132
259
  }
133
260
  sleep(25);
134
261
  continue;
@@ -136,20 +263,20 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
136
263
  if (response.phase === 'accepted') {
137
264
  accepted = true;
138
265
  } else {
139
- fs.rmSync(responsePath, { force: true });
140
266
  return response;
141
267
  }
142
268
  }
143
269
  if (!accepted) {
144
270
  try {
145
- preflight(statusDir, request.generation);
271
+ preflight(statusDir, request.generation, taskViewEffect);
146
272
  } catch (error) {
147
273
  if (cancelPendingRequest(requestPath)) throw error;
148
274
  clientError(
149
275
  'SANDBOX_CONTROL_RESULT_UNKNOWN',
150
276
  'request was claimed before broker availability was lost; inspect domain state before retrying',
151
277
  false,
152
- true
278
+ true,
279
+ request.id
153
280
  );
154
281
  }
155
282
  }
@@ -160,7 +287,8 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
160
287
  'SANDBOX_CONTROL_RESULT_UNKNOWN',
161
288
  'accepted request did not produce a final result; inspect domain state before retrying',
162
289
  false,
163
- true
290
+ true,
291
+ request.id
164
292
  );
165
293
  }
166
294
  if (!cancelPendingRequest(requestPath)) {
@@ -168,10 +296,66 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
168
296
  'SANDBOX_CONTROL_RESULT_UNKNOWN',
169
297
  'request was claimed but no acceptance result was observed; inspect domain state before retrying',
170
298
  false,
171
- true
299
+ true,
300
+ request.id
172
301
  );
173
302
  }
174
- clientError('SANDBOX_CONTROL_BROKER_UNAVAILABLE', `broker did not accept the request within ${params.timeoutMs ?? 30_000}ms`, true);
303
+ clientError('SANDBOX_CONTROL_BROKER_UNAVAILABLE', `broker did not accept the request within ${params.timeoutMs ?? 30_000}ms`, true, false, request.id);
304
+ }
305
+
306
+ export function recoverSandboxControl(requestId: string, params: Readonly<{
307
+ channelDir?: string;
308
+ timeoutMs?: number;
309
+ }> = {}): SandboxControlResponse {
310
+ if (!/^[a-f0-9-]{16,64}$/u.test(requestId)) {
311
+ clientError('SANDBOX_CONTROL_REQUEST_INVALID', 'request id is invalid', false, true);
312
+ }
313
+ const channelDir = params.channelDir ?? process.env.AGENT_INFRA_CONTROL_DIR ?? '/run/agent-infra/control';
314
+ const responsePath = path.join(channelDir, 'responses', `${requestId}.json`);
315
+ const deadline = Date.now() + (params.timeoutMs ?? 30_000);
316
+ let accepted = false;
317
+ let malformedResponseRaw: string | null = null;
318
+ let malformedResponseObservedAt = 0;
319
+ while (Date.now() < deadline) {
320
+ if (fs.existsSync(path.join(channelDir, 'responses', `${requestId}.accepted.json`))) accepted = true;
321
+ if (fs.existsSync(responsePath)) {
322
+ let raw: string;
323
+ try {
324
+ raw = fs.readFileSync(responsePath, 'utf8');
325
+ } catch (error) {
326
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') continue;
327
+ throw error;
328
+ }
329
+ let response: SandboxControlResponse;
330
+ try {
331
+ // The request-specific terminal proves the broker claimed the request; preserve recovery evidence if marker cleanup won the race.
332
+ accepted = true;
333
+ response = readPublishedResponse(raw, requestId, channelDir, accepted);
334
+ malformedResponseRaw = null;
335
+ } catch (error) {
336
+ if (!(error instanceof SyntaxError)) throw error;
337
+ const now = Date.now();
338
+ if (malformedResponseRaw !== raw) {
339
+ malformedResponseRaw = raw;
340
+ malformedResponseObservedAt = now;
341
+ } else if (now - malformedResponseObservedAt >= SANDBOX_CONTROL_RESPONSE_SETTLE_MS) {
342
+ clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response remained malformed', false, accepted, requestId);
343
+ }
344
+ sleep(25);
345
+ continue;
346
+ }
347
+ if (response.phase === 'accepted') accepted = true;
348
+ else return response;
349
+ }
350
+ sleep(25);
351
+ }
352
+ clientError(
353
+ 'SANDBOX_CONTROL_RESULT_UNKNOWN',
354
+ 'request did not produce a final result; inspect domain state before retrying',
355
+ false,
356
+ accepted,
357
+ requestId
358
+ );
175
359
  }
176
360
 
177
361
  function authority(params: { token?: string; generation?: string }): { token: string; generation: string } {
@@ -0,0 +1,98 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { resolveTaskWorkspace } from '../task-resolver.ts';
4
+ import { assertGitWorktreeBinding } from '../../git/worktree-identity.ts';
5
+ import { parseTypedTaskFrontmatter } from '../../task/frontmatter.ts';
6
+ import { readTaskFinalizationReceipt } from '../../task/finalization.ts';
7
+ import { inspectSandboxControlContainer } from './container-identity.ts';
8
+ import type { SandboxControlManifest } from './protocol.ts';
9
+ import { atomicWriteJson, readSandboxControlStatus } from './state.ts';
10
+ import { taskViewForManifest, type SandboxTaskView } from './task-view.ts';
11
+
12
+ type ReentryEvidence = Readonly<{
13
+ taskId: string;
14
+ generation: string;
15
+ containerId: string;
16
+ branch: string;
17
+ worktreeRoot: string;
18
+ source: string;
19
+ sourceDevice: string;
20
+ sourceInode: string;
21
+ receipt: NonNullable<SandboxTaskView['receipt']>;
22
+ }>;
23
+
24
+ function evidencePath(manifest: SandboxControlManifest): string {
25
+ return path.join(path.dirname(manifest.processingDir), 'completed-reentry.json');
26
+ }
27
+
28
+ function invalid(): Error {
29
+ return new Error('SANDBOX_COMPLETED_REENTRY_EVIDENCE_INVALID');
30
+ }
31
+
32
+ export async function prepareCompletedReentry(
33
+ manifest: SandboxControlManifest,
34
+ inspect: typeof inspectSandboxControlContainer = inspectSandboxControlContainer
35
+ ): Promise<ReentryEvidence> {
36
+ if (manifest.mode !== 'task-bound' || !manifest.taskId) throw invalid();
37
+ assertGitWorktreeBinding(manifest.repoRoot, manifest.worktreeRoot, manifest.branch);
38
+ const container = await inspect(manifest);
39
+ if (container.state !== 'found' || container.id !== manifest.containerIdentity.id) throw invalid();
40
+ const task = resolveTaskWorkspace(manifest.taskId, manifest.repoRoot);
41
+ const metadata = parseTypedTaskFrontmatter(fs.readFileSync(task.taskMd, 'utf8'));
42
+ if (task.state !== 'completed' || task.branch !== manifest.branch
43
+ || metadata.id !== manifest.taskId || metadata.status !== 'completed') throw invalid();
44
+ const receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
45
+ if (!receipt || receipt.taskId !== manifest.taskId) throw invalid();
46
+ const view = taskViewForManifest({ ...manifest, receipt });
47
+ if (view.state !== 'current' || view.observedSource !== 'completed' || !view.receipt) throw invalid();
48
+ const status = readSandboxControlStatus(manifest.publicStatusDir);
49
+ const previous = status.taskView;
50
+ if (status.generation !== manifest.generation || status.activeRequestId !== null
51
+ || previous.taskId !== manifest.taskId || !previous.receipt
52
+ || previous.receipt.receiptId !== view.receipt.receiptId
53
+ || previous.receipt.generation !== view.receipt.generation
54
+ || previous.receipt.requestId !== view.receipt.requestId
55
+ || previous.receipt.revision > view.receipt.revision) throw invalid();
56
+ const completedRoot = fs.realpathSync.native(path.join(manifest.repoRoot, '.agents', 'workspace', 'completed'));
57
+ const source = fs.realpathSync.native(path.join(completedRoot, manifest.taskId));
58
+ if (source !== path.join(completedRoot, manifest.taskId)) throw invalid();
59
+ const stat = fs.statSync(source, { bigint: true });
60
+ return {
61
+ taskId: manifest.taskId, generation: manifest.generation,
62
+ containerId: container.id, branch: manifest.branch,
63
+ worktreeRoot: fs.realpathSync.native(manifest.worktreeRoot),
64
+ source, sourceDevice: String(stat.dev), sourceInode: String(stat.ino), receipt: view.receipt
65
+ };
66
+ }
67
+
68
+ export function publishCompletedReentry(manifest: SandboxControlManifest, evidence: ReentryEvidence): void {
69
+ atomicWriteJson(evidencePath(manifest), evidence);
70
+ }
71
+
72
+ export async function completedReentryView(
73
+ manifest: SandboxControlManifest,
74
+ inspect: typeof inspectSandboxControlContainer = inspectSandboxControlContainer
75
+ ): Promise<SandboxTaskView | null> {
76
+ const file = evidencePath(manifest);
77
+ if (!fs.existsSync(file)) return null;
78
+ const evidence: unknown = JSON.parse(fs.readFileSync(file, 'utf8'));
79
+ const current = await prepareCompletedReentry(manifest, inspect);
80
+ if (JSON.stringify(evidence) !== JSON.stringify(current)) throw invalid();
81
+ return {
82
+ state: 'current', taskId: current.taskId, observedSource: 'completed',
83
+ receipt: current.receipt, reasonCode: null
84
+ };
85
+ }
86
+
87
+ export async function waitForCompletedReentry(manifest: SandboxControlManifest, evidence: ReentryEvidence): Promise<void> {
88
+ const deadline = Date.now() + 10_000;
89
+ while (Date.now() < deadline) {
90
+ const status = readSandboxControlStatus(manifest.publicStatusDir);
91
+ if (status.generation !== evidence.generation) throw invalid();
92
+ if (status.taskView.state === 'current' && status.taskView.observedSource === 'completed'
93
+ && status.taskView.taskId === evidence.taskId
94
+ && JSON.stringify(status.taskView.receipt) === JSON.stringify(evidence.receipt)) return;
95
+ await new Promise((resolve) => setTimeout(resolve, 100));
96
+ }
97
+ throw new Error('SANDBOX_COMPLETED_REENTRY_STATUS_TIMEOUT');
98
+ }
@@ -1,5 +1,7 @@
1
1
  import type { SandboxControlManifest } from './protocol.ts';
2
- import { commandForEngine, runEngine, runProbe } from '../shell.ts';
2
+ import { runProbe } from '../shell.ts';
3
+ import { captureSandboxAuthority, isSandboxAuthorityEvidence, verifySandboxAuthority } from '../engines/authority.ts';
4
+ import { discoverExactContainer } from '../engines/docker-exact-discovery.ts';
3
5
 
4
6
  export type ContainerObservation =
5
7
  | Readonly<{ state: 'found'; id: string; running: boolean; labels: Readonly<Record<string, string>> }>
@@ -12,20 +14,13 @@ export type ContainerInspection = Readonly<{
12
14
  labels?: unknown;
13
15
  }>;
14
16
 
15
- function isAuthoritativeNotFound(error: unknown): boolean {
16
- const message = error instanceof Error ? error.message : String(error);
17
- return /(?:no such (?:container|object)|container .* not found|cannot find container)/i.test(message);
18
- }
19
-
20
17
  export function classifySandboxContainerInspection(
21
18
  expected: SandboxControlManifest['containerIdentity'],
22
19
  inspection: ContainerInspection | { notFound: true } | { error: unknown }
23
20
  ): ContainerObservation {
24
21
  if ('notFound' in inspection) return { state: 'absent', id: expected.id };
25
22
  if ('error' in inspection) {
26
- return isAuthoritativeNotFound(inspection.error)
27
- ? { state: 'absent', id: expected.id }
28
- : { state: 'unknown', reason: `container inspection failed: ${inspection.error instanceof Error ? inspection.error.message : String(inspection.error)}` };
23
+ return { state: 'unknown', reason: `container inspection failed: ${inspection.error instanceof Error ? inspection.error.message : String(inspection.error)}` };
29
24
  }
30
25
  const response = inspection as ContainerInspection;
31
26
  if (typeof response.id !== 'string' || response.id !== expected.id
@@ -49,44 +44,33 @@ export function classifySandboxContainerInspection(
49
44
 
50
45
  export async function inspectSandboxControlContainer(
51
46
  manifest: SandboxControlManifest,
52
- options: { run?: typeof runEngine; probe?: typeof runProbe; timeoutMs?: number } = {}
47
+ options: { probe?: typeof runProbe; timeoutMs?: number } = {}
53
48
  ): Promise<ContainerObservation> {
49
+ if (!isSandboxAuthorityEvidence(manifest.authorityEvidence)) {
50
+ return { state: 'unknown', reason: 'SANDBOX_AUTHORITY_EVIDENCE_MISSING' };
51
+ }
54
52
  try {
55
- const args = ['inspect', '--format', '{{json .}}', manifest.containerIdentity.id];
56
- let raw: string;
57
- if (options.run) {
58
- raw = options.run(manifest.engine, 'docker', args, options.timeoutMs === undefined ? {} : { timeout: options.timeoutMs });
59
- } else {
60
- const command = commandForEngine(manifest.engine, 'docker', args);
61
- const result = (options.probe ?? runProbe)(
62
- command.cmd,
63
- command.args,
64
- options.timeoutMs === undefined ? {} : { timeout: options.timeoutMs }
65
- );
66
- if (result.status !== 0) {
67
- const stderr = typeof result.stderr === 'string' ? result.stderr : result.stderr?.toString('utf8') ?? '';
68
- return classifySandboxContainerInspection(manifest.containerIdentity, {
69
- error: new Error(stderr || `container inspection exited with ${result.status ?? 'unknown'}`)
70
- });
71
- }
72
- raw = typeof result.stdout === 'string' ? result.stdout : result.stdout?.toString('utf8') ?? '';
53
+ const authority = captureSandboxAuthority(manifest.engine, {
54
+ probe: options.probe,
55
+ timeoutMs: options.timeoutMs,
56
+ lockDomain: manifest.authorityEvidence.lockDomain,
57
+ route: manifest.authorityEvidence
58
+ });
59
+ const authorityState = verifySandboxAuthority(manifest.authorityEvidence, authority);
60
+ if (authorityState.state !== 'verified') {
61
+ return { state: 'unknown', reason: authorityState.reason ?? 'SANDBOX_AUTHORITY_UNAVAILABLE' };
73
62
  }
74
- const parsed = JSON.parse(raw) as {
75
- Id?: unknown;
76
- State?: { Running?: unknown };
77
- Config?: { Labels?: unknown };
78
- } | Array<{
79
- Id?: unknown;
80
- State?: { Running?: unknown };
81
- Config?: { Labels?: unknown };
82
- }>;
83
- const value = Array.isArray(parsed) && parsed.length === 1 ? parsed[0] : parsed;
84
- if (!value || Array.isArray(value)) return { state: 'unknown', reason: 'container inspection response is incomplete' };
85
- return classifySandboxContainerInspection(manifest.containerIdentity, {
86
- id: value.Id,
87
- running: value.State?.Running,
88
- labels: value.Config?.Labels
63
+ const discovered = discoverExactContainer(manifest.engine, manifest.containerIdentity.id, {
64
+ probe: options.probe,
65
+ timeoutMs: options.timeoutMs,
66
+ authority: manifest.authorityEvidence
89
67
  });
68
+ if (discovered.state === 'absent') return { state: 'absent', id: discovered.id };
69
+ if (discovered.state === 'conflict') {
70
+ return { state: 'unknown', reason: `SANDBOX_CONTROL_CONTAINER_CONFLICT: ${discovered.reason}` };
71
+ }
72
+ if (discovered.state === 'unknown') return discovered;
73
+ return classifySandboxContainerInspection(manifest.containerIdentity, discovered);
90
74
  } catch (error) {
91
75
  return classifySandboxContainerInspection(manifest.containerIdentity, { error });
92
76
  }