@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
@@ -1,15 +1,71 @@
1
+ import { completedReentryView } from './completed-reentry.js';
1
2
  import fs from 'node:fs';
2
3
  import path from 'node:path';
3
- import { randomUUID } from 'node:crypto';
4
+ import { createHash, randomUUID } from 'node:crypto';
4
5
  import { getProcessStartTime } from '../../server/process-state.js';
5
6
  import { assertGitWorktreeBinding } from '../../git/worktree-identity.js';
6
- import { controlError, DEFAULT_SANDBOX_CONTROL_TIMING } from './protocol.js';
7
+ import { controlError, DEFAULT_SANDBOX_CONTROL_TIMING, SANDBOX_CONTROL_MAX_RESPONSE_BYTES, SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES } from './protocol.js';
7
8
  import { prepareSandboxControlExecution } from './executor.js';
8
- import { appendSandboxControlAudit, atomicWriteJson, cleanupStaleSandboxControlLease, executionPath, readActiveLease, readExecution, terminateSandboxControlExecution, writeSandboxControlStatus } from './state.js';
9
+ import { appendSandboxControlAudit, atomicWriteJsonNoReplace, createSandboxControlPayload, cleanupStaleSandboxControlLease, executionPath, readJsonFile, readActiveLease, readExecution, readSandboxControlPayload, readSandboxControlResultEvidence, sandboxControlEncodedJsonBytes, sandboxControlGenerationUsage, sanitizeSandboxControlOutput, sanitizeSandboxControlResult, payloadPath, writeSandboxControlPayload, writeSandboxControlReservation, writeSandboxControlResultEvidence, terminateSandboxControlExecution, writeSandboxControlStatus, readSandboxControlStatus } from './state.js';
9
10
  import { validateSandboxControlRequest } from './protocol.js';
10
11
  import { inspectSandboxControlContainer } from './container-identity.js';
11
12
  import { acquireSandboxControlBrokerStartup, garbageCollectSandboxControlRoot, isSandboxControlRootQuiescing, readSandboxControlManifest } from './lifecycle.js';
12
13
  import { nextSandboxControlBackoff } from './timing.js';
14
+ import { readTaskFinalizationReceipt } from '../../task/finalization.js';
15
+ import { resolveTaskRef } from '../../task/resolve-ref.js';
16
+ import { mergeSandboxTaskView, taskViewAfterFinalization, taskViewForManifest } from './task-view.js';
17
+ import { taskCreateOutputUnavailableResult } from '../../task/create-service.js';
18
+ function safeRealpath(value) {
19
+ try {
20
+ return fs.realpathSync.native(value);
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
26
+ function appendBrokerAudit(manifest, event, fields = {}) {
27
+ try {
28
+ appendSandboxControlAudit(manifest, event, { source: 'broker', ...fields });
29
+ }
30
+ catch {
31
+ // Diagnostics must never change the control protocol.
32
+ }
33
+ }
34
+ function requestAuditFields(manifest, manifestPath, request) {
35
+ const args = 'args' in request ? request.args : [];
36
+ const encodedArgs = JSON.stringify(args);
37
+ return {
38
+ requestId: request.id,
39
+ requestFamily: request.family,
40
+ sandboxTaskId: manifest.taskId,
41
+ requestGeneration: request.generation,
42
+ requestIssuedAt: request.issuedAt,
43
+ requestExpiresAt: request.expiresAt,
44
+ requestArgCount: args.length,
45
+ requestArgsSha256: createHash('sha256').update(encodedArgs, 'utf8').digest('hex'),
46
+ requestTaskRef: args[0] ?? null,
47
+ requestCommand: args[1] ?? null,
48
+ controllerProofPresent: request.controllerProof !== null,
49
+ hostCwd: process.cwd(),
50
+ manifestPath,
51
+ manifestPathRealpath: safeRealpath(manifestPath),
52
+ repoRoot: manifest.repoRoot,
53
+ repoRootRealpath: safeRealpath(manifest.repoRoot),
54
+ worktreeRoot: manifest.worktreeRoot,
55
+ worktreeRootRealpath: safeRealpath(manifest.worktreeRoot),
56
+ runtimeDir: manifest.runtimeDir,
57
+ runtimeDirRealpath: safeRealpath(manifest.runtimeDir)
58
+ };
59
+ }
60
+ function resultAuditFields(result) {
61
+ return {
62
+ exitCode: result.exitCode,
63
+ stdoutBytes: Buffer.byteLength(result.stdout, 'utf8'),
64
+ stderrBytes: Buffer.byteLength(result.stderr, 'utf8'),
65
+ stdoutSha256: createHash('sha256').update(result.stdout, 'utf8').digest('hex'),
66
+ stderrSha256: createHash('sha256').update(result.stderr, 'utf8').digest('hex')
67
+ };
68
+ }
13
69
  function assertRealDirectory(directory, parent) {
14
70
  const stat = fs.lstatSync(directory);
15
71
  if (!stat.isDirectory() || stat.isSymbolicLink())
@@ -21,8 +77,262 @@ function assertRealDirectory(directory, parent) {
21
77
  function responsePath(manifest, id) {
22
78
  return path.join(manifest.channelDir, 'responses', `${id}.json`);
23
79
  }
24
- function writeResponse(manifest, response) {
25
- atomicWriteJson(responsePath(manifest, response.id), response);
80
+ function acceptedResponsePath(manifest, id) {
81
+ return path.join(manifest.channelDir, 'responses', `${id}.accepted.json`);
82
+ }
83
+ export function writeSandboxControlResponse(manifest, response) {
84
+ const filePath = responsePath(manifest, response.id);
85
+ const normalized = {
86
+ ...response,
87
+ stdout: sanitizeSandboxControlOutput(manifest, response.stdout),
88
+ stderr: sanitizeSandboxControlOutput(manifest, response.stderr)
89
+ };
90
+ const terminal = sandboxControlEncodedJsonBytes(normalized) <= SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES
91
+ ? normalized
92
+ : {
93
+ ...normalized,
94
+ stdout: '',
95
+ stderr: 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: terminal output exceeded the compact record limit\n',
96
+ error: {
97
+ code: 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE',
98
+ message: 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: terminal output exceeded the compact record limit',
99
+ retryable: false
100
+ }
101
+ };
102
+ try {
103
+ atomicWriteJsonNoReplace(filePath, terminal);
104
+ }
105
+ catch (error) {
106
+ if (!(error instanceof Error) || error.message !== 'SANDBOX_CONTROL_TERMINAL_ALREADY_EXISTS')
107
+ throw error;
108
+ let persisted;
109
+ try {
110
+ persisted = readJsonFile(filePath);
111
+ }
112
+ catch {
113
+ throw new Error('SANDBOX_CONTROL_TERMINAL_READBACK_FAILED');
114
+ }
115
+ if (JSON.stringify(persisted) !== JSON.stringify(terminal)) {
116
+ throw new Error('SANDBOX_CONTROL_TERMINAL_CONFLICT');
117
+ }
118
+ return true;
119
+ }
120
+ let persisted;
121
+ try {
122
+ persisted = readJsonFile(filePath);
123
+ }
124
+ catch {
125
+ throw new Error('SANDBOX_CONTROL_TERMINAL_READBACK_FAILED');
126
+ }
127
+ if (JSON.stringify(persisted) !== JSON.stringify(terminal)) {
128
+ throw new Error('SANDBOX_CONTROL_TERMINAL_READBACK_FAILED');
129
+ }
130
+ return true;
131
+ }
132
+ function finalizationRecoveryResponse(manifest, requestId, exitCode) {
133
+ if (!manifest.taskId || exitCode !== 0)
134
+ return { status: 'not-applicable' };
135
+ let receipt;
136
+ try {
137
+ receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
138
+ if (!receipt || !receipt.controlBinding
139
+ || receipt.controlBinding.generation !== manifest.generation
140
+ || receipt.controlBinding.requestId !== requestId)
141
+ return { status: 'deferred' };
142
+ const resolved = resolveTaskRef(manifest.taskId, { repoRoot: manifest.repoRoot });
143
+ if (!resolved.ok || resolved.state !== 'completed' || receipt.lifecycle !== 'done')
144
+ return { status: 'deferred' };
145
+ }
146
+ catch {
147
+ return { status: 'deferred' };
148
+ }
149
+ const pendingSteps = [
150
+ receipt.taskComment === 'pending' ? 'task-comment' : null,
151
+ receipt.verification === 'pending' ? 'verification' : null
152
+ ].filter((step) => step !== null);
153
+ const completedSteps = ['lifecycle', receipt.taskComment === 'pending' ? null : 'task-comment', receipt.verification === 'pending' ? null : 'verification']
154
+ .filter((step) => step !== null);
155
+ const warnings = receipt.warnings
156
+ .filter((warning) => warning.status === 'open')
157
+ .map(({ status: _status, resolvedAt: _resolvedAt, ...warning }) => warning);
158
+ const result = {
159
+ status: 'completed', changed: false, taskId: manifest.taskId,
160
+ lifecycle: { status: 'no-op', changed: false, error: null },
161
+ taskComment: receipt.taskComment === 'pending' ? null : { status: 'no-op', changed: false, error: null },
162
+ verification: receipt.verification === 'pending' ? null : { status: 'no-op', changed: false, error: null },
163
+ completedSteps, pendingSteps,
164
+ result: pendingSteps.length > 0 || receipt.warningProjection === 'pending' || warnings.length > 0
165
+ ? 'completed_with_warnings' : 'completed',
166
+ warnings, error: null
167
+ };
168
+ return {
169
+ status: 'matched',
170
+ response: {
171
+ version: 2, id: requestId, phase: 'completed', exitCode: 0,
172
+ stdout: `${JSON.stringify({ version: 1, status: 'completed', changed: false, accepted: true, result, error: null })}\n`,
173
+ stderr: '', error: null
174
+ }
175
+ };
176
+ }
177
+ function publishFinalizationTaskView(manifest, broker, requestId, state, reasonCode, activeRequestId) {
178
+ let view;
179
+ try {
180
+ const resolved = resolveTaskRef(manifest.taskId ?? '', { repoRoot: manifest.repoRoot });
181
+ const receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId ?? '');
182
+ view = resolved.ok && resolved.state === 'completed'
183
+ ? taskViewAfterFinalization({
184
+ taskId: manifest.taskId ?? '', generation: manifest.generation, requestId, receipt
185
+ })
186
+ : {
187
+ state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
188
+ reasonCode: 'SANDBOX_TASK_VIEW_SOURCE_UNCONFIRMED'
189
+ };
190
+ }
191
+ catch {
192
+ view = {
193
+ state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
194
+ reasonCode: 'SANDBOX_TASK_VIEW_RECEIPT_INVALID'
195
+ };
196
+ }
197
+ writeSandboxControlStatus(manifest, broker, state, reasonCode, activeRequestId, Date.now(), view);
198
+ return view;
199
+ }
200
+ function payloadReference(payload) {
201
+ return {
202
+ version: payload.version,
203
+ id: payload.id,
204
+ generation: payload.generation,
205
+ stdoutBytes: payload.stdoutBytes,
206
+ stderrBytes: payload.stderrBytes,
207
+ stdoutSha256: payload.stdoutSha256,
208
+ stderrSha256: payload.stderrSha256
209
+ };
210
+ }
211
+ function payloadMatchesEvidence(payload, evidence) {
212
+ return payload.id === evidence.id
213
+ && payload.generation === evidence.generation
214
+ && payload.stdoutBytes === evidence.stdoutBytes
215
+ && payload.stderrBytes === evidence.stderrBytes
216
+ && payload.stdoutSha256 === evidence.stdoutSha256
217
+ && payload.stderrSha256 === evidence.stderrSha256;
218
+ }
219
+ function outputMatchesEvidence(output, bytes, sha256) {
220
+ return Buffer.byteLength(output, 'utf8') === bytes
221
+ && createHash('sha256').update(output, 'utf8').digest('hex') === sha256;
222
+ }
223
+ function genericRecoveryResponse(request, exitCode, payload, cause = 'recovery') {
224
+ const outputUnavailable = request.family === 'task-create' && !payload;
225
+ return {
226
+ version: 2,
227
+ id: request.id,
228
+ phase: 'completed',
229
+ exitCode,
230
+ stdout: outputUnavailable ? `${JSON.stringify(taskCreateOutputUnavailableResult(request.id))}\n` : '',
231
+ stderr: payload ? '' : cause === 'publish'
232
+ ? 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: output payload was not retained\n'
233
+ : 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: broker restarted after executor completion\n',
234
+ error: null,
235
+ outputState: payload ? 'available' : 'unavailable',
236
+ payload: payload ? payloadReference(payload) : null
237
+ };
238
+ }
239
+ function recoveryResponse(manifest, request, evidence, payload) {
240
+ if (request.family === 'task-finalization' && evidence.exitCode === 0) {
241
+ const finalization = finalizationRecoveryResponse(manifest, request.id, evidence.exitCode);
242
+ return finalization.status === 'matched' ? finalization.response ?? null : null;
243
+ }
244
+ return genericRecoveryResponse(request, evidence.exitCode, payload);
245
+ }
246
+ function terminalMatchesEvidence(manifest, request, response, evidence, payload, payloadInvalid) {
247
+ if (request.family === 'task-finalization' && evidence.exitCode === 0) {
248
+ const expected = recoveryResponse(manifest, request, evidence, payload);
249
+ return {
250
+ valid: expected !== null && JSON.stringify(response) === JSON.stringify(expected),
251
+ payloadReferenced: false
252
+ };
253
+ }
254
+ if (response.version !== 2 || response.id !== request.id || response.phase !== 'completed'
255
+ || response.exitCode !== evidence.exitCode || response.error !== null) {
256
+ return { valid: false, payloadReferenced: false };
257
+ }
258
+ if (response.outputState === 'available') {
259
+ const reference = response.payload;
260
+ return {
261
+ valid: !payloadInvalid && payload !== null && response.stdout === '' && response.stderr === ''
262
+ && reference !== undefined && reference !== null
263
+ && reference.version === payload.version
264
+ && reference.id === payload.id
265
+ && reference.generation === payload.generation
266
+ && reference.stdoutBytes === payload.stdoutBytes
267
+ && reference.stderrBytes === payload.stderrBytes
268
+ && reference.stdoutSha256 === payload.stdoutSha256
269
+ && reference.stderrSha256 === payload.stderrSha256
270
+ && payloadMatchesEvidence(payload, evidence),
271
+ payloadReferenced: true
272
+ };
273
+ }
274
+ if (response.outputState === 'unavailable') {
275
+ const causes = ['recovery', 'publish'];
276
+ const valid = causes.some((cause) => JSON.stringify(response)
277
+ === JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause)));
278
+ return { valid, payloadReferenced: false };
279
+ }
280
+ return {
281
+ valid: response.payload === undefined
282
+ && outputMatchesEvidence(response.stdout, evidence.stdoutBytes, evidence.stdoutSha256)
283
+ && outputMatchesEvidence(response.stderr, evidence.stderrBytes, evidence.stderrSha256),
284
+ payloadReferenced: false
285
+ };
286
+ }
287
+ function publishExecutionResult(manifest, request, result, broker, brokerOwns) {
288
+ const normalized = sanitizeSandboxControlResult(manifest, result);
289
+ let terminal = null;
290
+ if (request.family === 'task-finalization') {
291
+ const finalization = finalizationRecoveryResponse(manifest, request.id, normalized.exitCode);
292
+ if (finalization.status === 'deferred')
293
+ return false;
294
+ terminal = finalization.response ?? null;
295
+ }
296
+ if (!terminal) {
297
+ const inline = {
298
+ version: 2, id: request.id, phase: 'completed', exitCode: normalized.exitCode,
299
+ stdout: normalized.stdout, stderr: normalized.stderr, error: null
300
+ };
301
+ if (sandboxControlEncodedJsonBytes(inline) <= SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES) {
302
+ terminal = inline;
303
+ }
304
+ let payload = null;
305
+ if (!terminal) {
306
+ try {
307
+ payload = createSandboxControlPayload(manifest, request.id, normalized);
308
+ const usage = sandboxControlGenerationUsage(manifest);
309
+ if (usage.bytes + sandboxControlEncodedJsonBytes(payload) <= SANDBOX_CONTROL_MAX_RESPONSE_BYTES) {
310
+ writeSandboxControlPayload(manifest, request.id, normalized);
311
+ }
312
+ else {
313
+ payload = null;
314
+ }
315
+ }
316
+ catch {
317
+ payload = null;
318
+ }
319
+ terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish');
320
+ }
321
+ }
322
+ if (!brokerOwns())
323
+ return false;
324
+ if (request.family === 'task-finalization' && normalized.exitCode === 0) {
325
+ const view = publishFinalizationTaskView(manifest, broker, request.id, 'healthy', null, null);
326
+ if (view.state === 'unknown')
327
+ return false;
328
+ }
329
+ return writeSandboxControlResponse(manifest, terminal);
330
+ }
331
+ function writeAcceptedResponse(manifest, response) {
332
+ atomicWriteJsonNoReplace(acceptedResponsePath(manifest, response.id), response);
333
+ }
334
+ function removeAcceptedResponse(manifest, id) {
335
+ fs.rmSync(acceptedResponsePath(manifest, id), { force: true });
26
336
  }
27
337
  function rejected(id, error) {
28
338
  const detail = controlError(error);
@@ -76,7 +386,7 @@ function bindingReason(manifest) {
76
386
  return 'SANDBOX_WORKTREE_BINDING_LOST';
77
387
  }
78
388
  }
79
- function recoverProcessing(manifest, brokerOwns) {
389
+ function recoverProcessing(manifest, broker, brokerOwns) {
80
390
  for (const entry of fs.readdirSync(manifest.processingDir, { withFileTypes: true })) {
81
391
  if (!entry.isDirectory() || !/^[a-f0-9-]{16,64}$/.test(entry.name))
82
392
  continue;
@@ -84,8 +394,49 @@ function recoverProcessing(manifest, brokerOwns) {
84
394
  return false;
85
395
  const descriptor = executionPath(manifest, entry.name);
86
396
  const existingResponse = responsePath(manifest, entry.name);
397
+ const processingDirectory = path.join(manifest.processingDir, entry.name);
398
+ let payloadReferenced = false;
87
399
  if (fs.existsSync(descriptor)) {
88
400
  const execution = readExecution(descriptor);
401
+ let resultEvidence = null;
402
+ let payload = null;
403
+ let payloadInvalid = false;
404
+ let request = null;
405
+ let existingTerminalResponse = null;
406
+ try {
407
+ const rawRequest = JSON.parse(fs.readFileSync(path.join(processingDirectory, 'request.json'), 'utf8'));
408
+ request = validateSandboxControlRequest(rawRequest, manifest, {
409
+ now: typeof rawRequest.issuedAt === 'number' ? rawRequest.issuedAt : undefined
410
+ });
411
+ }
412
+ catch {
413
+ // Missing or malformed request evidence remains fail-closed below.
414
+ }
415
+ const resultPath = path.join(manifest.processingDir, entry.name, 'result.json');
416
+ if (fs.existsSync(resultPath)) {
417
+ try {
418
+ resultEvidence = readSandboxControlResultEvidence(resultPath);
419
+ if (resultEvidence.id !== entry.name || resultEvidence.generation !== manifest.generation) {
420
+ resultEvidence = null;
421
+ }
422
+ }
423
+ catch {
424
+ resultEvidence = null;
425
+ }
426
+ }
427
+ const publishedPayloadPath = payloadPath(manifest, entry.name);
428
+ if (fs.existsSync(publishedPayloadPath)) {
429
+ try {
430
+ payload = readSandboxControlPayload(publishedPayloadPath);
431
+ if (payload.id !== entry.name || payload.generation !== manifest.generation
432
+ || (resultEvidence !== null && !payloadMatchesEvidence(payload, resultEvidence))) {
433
+ payloadInvalid = true;
434
+ }
435
+ }
436
+ catch {
437
+ payloadInvalid = true;
438
+ }
439
+ }
89
440
  if (!terminateSandboxControlExecution(execution)) {
90
441
  throw new Error(`SANDBOX_CONTROL_EXECUTION_STILL_RUNNING: ${entry.name}`);
91
442
  }
@@ -96,27 +447,73 @@ function recoverProcessing(manifest, brokerOwns) {
96
447
  if (fs.existsSync(existingResponse)) {
97
448
  try {
98
449
  const response = JSON.parse(fs.readFileSync(existingResponse, 'utf8'));
450
+ existingTerminalResponse = response;
99
451
  terminal = response.version === 2 && response.id === entry.name
100
452
  && (response.phase === 'completed' || response.phase === 'rejected');
453
+ if (terminal && response.outputState === 'available') {
454
+ if (!response.payload || payloadInvalid || !payload
455
+ || response.payload.version !== payload.version
456
+ || response.payload.id !== payload.id
457
+ || response.payload.generation !== payload.generation
458
+ || response.payload.stdoutBytes !== payload.stdoutBytes
459
+ || response.payload.stderrBytes !== payload.stderrBytes
460
+ || response.payload.stdoutSha256 !== payload.stdoutSha256
461
+ || response.payload.stderrSha256 !== payload.stderrSha256)
462
+ throw new Error('payload missing or mismatched');
463
+ payloadReferenced = true;
464
+ }
465
+ else if (terminal && ((response.outputState !== undefined && response.outputState !== 'unavailable')
466
+ || response.payload !== undefined && response.payload !== null)) {
467
+ throw new Error('payload state invalid');
468
+ }
469
+ if (!terminal)
470
+ throw new Error('SANDBOX_CONTROL_RESPONSE_LAYOUT_INVALID');
101
471
  }
102
472
  catch {
103
- terminal = false;
473
+ throw new Error('SANDBOX_CONTROL_RESPONSE_LAYOUT_INVALID');
474
+ }
475
+ }
476
+ if (terminal && (!request || !resultEvidence))
477
+ continue;
478
+ if (terminal && request && resultEvidence) {
479
+ const reconciliation = terminalMatchesEvidence(manifest, request, existingTerminalResponse, resultEvidence, payload, payloadInvalid);
480
+ if (!reconciliation.valid)
481
+ continue;
482
+ payloadReferenced = reconciliation.payloadReferenced;
483
+ if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
484
+ const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
485
+ if (view.state === 'unknown')
486
+ continue;
104
487
  }
105
488
  }
106
489
  if (!terminal) {
107
490
  if (!brokerOwns())
108
491
  return false;
109
- writeResponse(manifest, unknown(entry.name));
492
+ if (!resultEvidence || !request || payloadInvalid)
493
+ continue;
494
+ const recovered = recoveryResponse(manifest, request, resultEvidence, payload);
495
+ if (!recovered)
496
+ continue;
497
+ if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
498
+ const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
499
+ if (view.state === 'unknown')
500
+ continue;
501
+ }
502
+ writeSandboxControlResponse(manifest, recovered);
503
+ payloadReferenced = Boolean(payload) && request.family !== 'task-finalization';
110
504
  }
111
505
  }
112
506
  else {
113
507
  if (!brokerOwns())
114
508
  return false;
115
- writeResponse(manifest, notExecuted(entry.name));
509
+ writeSandboxControlResponse(manifest, notExecuted(entry.name));
116
510
  }
117
511
  if (!brokerOwns())
118
512
  return false;
119
- fs.rmSync(path.join(manifest.processingDir, entry.name), { recursive: true, force: true });
513
+ removeAcceptedResponse(manifest, entry.name);
514
+ fs.rmSync(processingDirectory, { recursive: true, force: true });
515
+ if (!payloadReferenced)
516
+ fs.rmSync(payloadPath(manifest, entry.name), { force: true });
120
517
  }
121
518
  return true;
122
519
  }
@@ -124,12 +521,19 @@ function delay(ms) {
124
521
  return new Promise((resolve) => setTimeout(resolve, ms));
125
522
  }
126
523
  export function sandboxControlSafeEnv(env = process.env) {
127
- return Object.fromEntries(Object.entries(env).filter(([key]) => !key.toUpperCase().startsWith('AGENT_INFRA_CONTROL_')));
524
+ return Object.fromEntries(Object.entries(env).filter(([key]) => {
525
+ const normalized = key.toUpperCase();
526
+ return !normalized.startsWith('AGENT_INFRA_CONTROL_')
527
+ && normalized !== 'AGENT_INFRA_TASK_ID'
528
+ && normalized !== 'AGENT_INFRA_RUNTIME_DIR'
529
+ && normalized !== 'AGENT_INFRA_EXECUTOR_MANIFEST';
530
+ }));
128
531
  }
129
532
  export async function serveSandboxControl(manifestPath, signal = new AbortController().signal, options = {}) {
130
533
  const timing = options.timing ?? DEFAULT_SANDBOX_CONTROL_TIMING;
131
534
  const inspectContainer = options.inspectContainer ?? ((value) => inspectSandboxControlContainer(value));
132
535
  const bindingCheck = options.bindingCheck ?? bindingReason;
536
+ const prepareExecution = options.prepareExecution ?? prepareSandboxControlExecution;
133
537
  const manifest = readSandboxControlManifest(manifestPath);
134
538
  const root = path.dirname(manifestPath);
135
539
  const startTime = getProcessStartTime(process.pid);
@@ -179,6 +583,28 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
179
583
  releaseStartup();
180
584
  }
181
585
  let active = null;
586
+ let startupReceipt = null;
587
+ if (manifest.taskId) {
588
+ try {
589
+ startupReceipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
590
+ }
591
+ catch {
592
+ // Preserve malformed receipt evidence as unknown rather than treating it as no receipt.
593
+ startupReceipt = {};
594
+ }
595
+ }
596
+ let taskView = taskViewForManifest({
597
+ ...manifest,
598
+ receipt: startupReceipt
599
+ });
600
+ try {
601
+ const previous = readSandboxControlStatus(manifest.publicStatusDir);
602
+ if (previous.generation === manifest.generation)
603
+ taskView = mergeSandboxTaskView(taskView, previous.taskView);
604
+ }
605
+ catch {
606
+ // A fresh control root has no previous task-view projection.
607
+ }
182
608
  let lastState = '';
183
609
  let lastStatusAt = 0;
184
610
  let nextBindingCheckAt = 0;
@@ -190,12 +616,49 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
190
616
  try {
191
617
  if (!brokerOwns())
192
618
  return;
193
- writeSandboxControlStatus(manifest, broker, 'starting', null, null);
619
+ if (taskView.state === 'current' && taskView.observedSource === 'completed') {
620
+ // Recheck durable re-entry evidence before publishing a trusted startup view.
621
+ const unverified = { ...taskView, state: 'unknown',
622
+ reasonCode: 'SANDBOX_COMPLETED_REENTRY_EVIDENCE_INVALID' };
623
+ try {
624
+ taskView = await completedReentryView(manifest, inspectContainer) ?? unverified;
625
+ }
626
+ catch {
627
+ taskView = unverified;
628
+ }
629
+ if (!brokerOwns())
630
+ return;
631
+ }
632
+ writeSandboxControlStatus(manifest, broker, 'starting', null, null, Date.now(), taskView);
194
633
  if (!brokerOwns())
195
634
  return;
196
- appendSandboxControlAudit(manifest, 'broker-start', { pid: broker.pid });
197
- if (!recoverProcessing(manifest, brokerOwns))
635
+ appendBrokerAudit(manifest, 'broker-start', {
636
+ pid: broker.pid,
637
+ brokerId: broker.brokerId,
638
+ hostCwd: process.cwd(),
639
+ manifestPath,
640
+ manifestPathRealpath: safeRealpath(manifestPath),
641
+ repoRoot: manifest.repoRoot,
642
+ repoRootRealpath: safeRealpath(manifest.repoRoot),
643
+ worktreeRoot: manifest.worktreeRoot,
644
+ worktreeRootRealpath: safeRealpath(manifest.worktreeRoot),
645
+ runtimeDir: manifest.runtimeDir,
646
+ runtimeDirRealpath: safeRealpath(manifest.runtimeDir),
647
+ channelDir: manifest.channelDir,
648
+ publicStatusDir: manifest.publicStatusDir,
649
+ processingDir: manifest.processingDir,
650
+ internalCliPath: options.internalCliPath ?? process.argv[1] ?? null
651
+ });
652
+ if (!recoverProcessing(manifest, broker, brokerOwns))
198
653
  return;
654
+ try {
655
+ const published = readSandboxControlStatus(manifest.publicStatusDir);
656
+ if (published.generation === manifest.generation)
657
+ taskView = published.taskView;
658
+ }
659
+ catch {
660
+ // The regular heartbeat will publish an invalid or missing projection as unknown.
661
+ }
199
662
  while (!signal.aborted) {
200
663
  let settledExecution = null;
201
664
  if (!brokerOwns())
@@ -209,6 +672,18 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
209
672
  }
210
673
  if (current.token !== manifest.token || current.generation !== manifest.generation)
211
674
  break;
675
+ if (active?.result && !active.resultEvidenceWritten) {
676
+ try {
677
+ active.result = sanitizeSandboxControlResult(manifest, active.result);
678
+ writeSandboxControlResultEvidence(manifest, active.request.id, active.result);
679
+ active.resultEvidenceWritten = true;
680
+ active.failure = null;
681
+ active.settled = true;
682
+ }
683
+ catch (error) {
684
+ active.failure = error;
685
+ }
686
+ }
212
687
  const heartbeatNow = Date.now();
213
688
  if (heartbeatNow >= nextContainerHeartbeatAt) {
214
689
  let observation;
@@ -266,7 +741,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
266
741
  if (cleanupStaleSandboxControlLease(manifest)) {
267
742
  if (!brokerOwns())
268
743
  break;
269
- appendSandboxControlAudit(manifest, 'lease-stale-cleanup');
744
+ appendBrokerAudit(manifest, 'lease-stale-cleanup');
270
745
  }
271
746
  if (readActiveLease(manifest))
272
747
  reasonCode = 'SANDBOX_CONTROL_HANDOFF_ACTIVE';
@@ -289,30 +764,58 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
289
764
  }
290
765
  reasonCode ??= containerReasonCode;
291
766
  reasonCode ??= bindingReasonCode;
767
+ if (!reasonCode && !active && taskView.state !== 'current') {
768
+ try {
769
+ const reentered = await completedReentryView(manifest, inspectContainer);
770
+ if (reentered && brokerOwns() && !readActiveLease(manifest) && !bindingCheck(manifest)) {
771
+ taskView = reentered;
772
+ }
773
+ }
774
+ catch {
775
+ // Invalid or changed re-entry evidence never clears the existing task view.
776
+ }
777
+ }
292
778
  const state = reasonCode ? 'parked' : active ? 'busy' : 'healthy';
293
779
  const stateKey = `${state}:${reasonCode ?? ''}:${active?.request.id ?? ''}`;
294
780
  if (brokerOwns() && (stateKey !== lastState || now - lastStatusAt >= timing.controlTickMs)) {
295
- writeSandboxControlStatus(manifest, broker, state, reasonCode, active?.request.id ?? null, now);
781
+ writeSandboxControlStatus(manifest, broker, state, reasonCode, active?.request.id ?? null, now, taskView);
296
782
  lastStatusAt = now;
297
783
  }
298
784
  if (brokerOwns() && stateKey !== lastState) {
299
- appendSandboxControlAudit(manifest, 'broker-state', { state, reasonCode, requestId: active?.request.id ?? null });
785
+ appendBrokerAudit(manifest, 'broker-state', { state, reasonCode, requestId: active?.request.id ?? null });
300
786
  lastState = stateKey;
301
787
  }
302
788
  if (settledExecution) {
303
789
  if (!brokerOwns())
304
790
  break;
305
- if (settledExecution.result) {
306
- writeResponse(manifest, {
307
- version: 2, id: settledExecution.request.id, phase: 'completed', exitCode: settledExecution.result.exitCode,
308
- stdout: settledExecution.result.stdout, stderr: settledExecution.result.stderr, error: null
309
- });
791
+ let terminalCommitted = false;
792
+ if (settledExecution.result && settledExecution.resultEvidenceWritten) {
793
+ terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, broker, brokerOwns);
794
+ if (terminalCommitted && settledExecution.request.family === 'task-finalization') {
795
+ try {
796
+ const published = readSandboxControlStatus(manifest.publicStatusDir);
797
+ if (published.generation === manifest.generation)
798
+ taskView = published.taskView;
799
+ }
800
+ catch {
801
+ taskView = { state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null, reasonCode: 'SANDBOX_TASK_VIEW_STATUS_INVALID' };
802
+ }
803
+ }
310
804
  }
311
805
  else {
312
- writeResponse(manifest, unknown(settledExecution.request.id));
806
+ terminalCommitted = writeSandboxControlResponse(manifest, unknown(settledExecution.request.id));
313
807
  }
808
+ if (!terminalCommitted)
809
+ return;
810
+ appendBrokerAudit(manifest, 'executor-result-published', {
811
+ ...requestAuditFields(manifest, manifestPath, settledExecution.request),
812
+ resultAvailable: settledExecution.result !== null,
813
+ resultEvidenceWritten: settledExecution.resultEvidenceWritten,
814
+ ...(settledExecution.result ? resultAuditFields(settledExecution.result) : {})
815
+ });
314
816
  if (!brokerOwns())
315
817
  break;
818
+ removeAcceptedResponse(manifest, settledExecution.request.id);
316
819
  fs.rmSync(path.join(manifest.processingDir, settledExecution.request.id), { recursive: true, force: true });
317
820
  }
318
821
  let retiring = false;
@@ -322,12 +825,19 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
322
825
  const id = name.slice(0, -5);
323
826
  const source = path.join(requestsDir, name);
324
827
  let claimed = null;
828
+ let validatedRequest = null;
325
829
  try {
326
830
  if (!brokerOwns()) {
327
831
  retiring = true;
328
832
  break;
329
833
  }
330
834
  claimed = claimRequest(manifest, source, id);
835
+ appendBrokerAudit(manifest, 'request-claimed', {
836
+ requestId: id,
837
+ requestSourcePath: source,
838
+ requestClaimedPath: claimed,
839
+ requestProcessingDir: path.dirname(claimed)
840
+ });
331
841
  if (!brokerOwns()) {
332
842
  retiring = true;
333
843
  break;
@@ -342,16 +852,85 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
342
852
  if (active)
343
853
  throw new Error('SANDBOX_CONTROL_BUSY');
344
854
  const request = validateSandboxControlRequest(JSON.parse(fs.readFileSync(claimed, 'utf8')), manifest);
855
+ validatedRequest = request;
856
+ appendBrokerAudit(manifest, 'request-validated', {
857
+ ...requestAuditFields(manifest, manifestPath, request),
858
+ requestPath: claimed
859
+ });
345
860
  if (bindingCheck(manifest))
346
861
  throw new Error('SANDBOX_WORKTREE_BINDING_LOST');
347
862
  if (readActiveLease(manifest))
348
863
  throw new Error('SANDBOX_CONTROL_HANDOFF_ACTIVE');
349
- const prepared = await prepareSandboxControlExecution({
350
- manifest, manifestPath, request, requestPath: claimed, internalCliPath: process.argv[1]
864
+ appendBrokerAudit(manifest, 'request-gates-passed', {
865
+ ...requestAuditFields(manifest, manifestPath, request),
866
+ requestPath: claimed,
867
+ bindingChecked: true,
868
+ handoffLeaseChecked: true
869
+ });
870
+ writeSandboxControlReservation(manifest, request.id, sandboxControlGenerationUsage(manifest));
871
+ appendBrokerAudit(manifest, 'executor-reservation-written', {
872
+ ...requestAuditFields(manifest, manifestPath, request),
873
+ requestPath: claimed,
874
+ executionPath: executionPath(manifest, request.id)
875
+ });
876
+ appendBrokerAudit(manifest, 'executor-prepare-start', {
877
+ ...requestAuditFields(manifest, manifestPath, request),
878
+ requestPath: claimed,
879
+ executorCwd: manifest.repoRoot,
880
+ executorEntry: options.internalCliPath ?? process.argv[1] ?? null
881
+ });
882
+ const prepared = await prepareExecution({
883
+ manifest, manifestPath, request, requestPath: claimed,
884
+ internalCliPath: options.internalCliPath ?? process.argv[1]
351
885
  });
352
- const execution = { request, prepared, result: null, failure: null, settled: false };
886
+ appendBrokerAudit(manifest, 'executor-prepared', {
887
+ ...requestAuditFields(manifest, manifestPath, request),
888
+ requestPath: claimed,
889
+ executorCwd: manifest.repoRoot,
890
+ childPid: prepared.execution.child.pid,
891
+ childStartTime: prepared.execution.child.startTime,
892
+ childProcessGroupId: prepared.execution.child.processGroupId,
893
+ executionPath: executionPath(manifest, request.id)
894
+ });
895
+ const execution = {
896
+ request, prepared, result: null, resultEvidenceWritten: false, failure: null, settled: false
897
+ };
353
898
  active = execution;
354
- prepared.completion.then((result) => { execution.result = result; execution.settled = true; }, (error) => { execution.failure = error; execution.settled = true; });
899
+ prepared.completion.then((result) => {
900
+ execution.result = sanitizeSandboxControlResult(manifest, result);
901
+ appendBrokerAudit(manifest, 'executor-completed', {
902
+ ...requestAuditFields(manifest, manifestPath, request),
903
+ ...resultAuditFields(execution.result),
904
+ childPid: prepared.execution.child.pid,
905
+ childStartTime: prepared.execution.child.startTime
906
+ });
907
+ try {
908
+ writeSandboxControlResultEvidence(manifest, request.id, execution.result);
909
+ execution.resultEvidenceWritten = true;
910
+ execution.settled = true;
911
+ appendBrokerAudit(manifest, 'executor-result-evidence-written', {
912
+ ...requestAuditFields(manifest, manifestPath, request),
913
+ resultPath: path.join(manifest.processingDir, request.id, 'result.json'),
914
+ ...resultAuditFields(execution.result)
915
+ });
916
+ }
917
+ catch (error) {
918
+ execution.failure = error;
919
+ appendBrokerAudit(manifest, 'executor-result-evidence-failed', {
920
+ ...requestAuditFields(manifest, manifestPath, request),
921
+ errorType: error instanceof Error ? error.name : typeof error,
922
+ });
923
+ }
924
+ }, (error) => {
925
+ execution.failure = error;
926
+ execution.settled = true;
927
+ appendBrokerAudit(manifest, 'executor-failed', {
928
+ ...requestAuditFields(manifest, manifestPath, request),
929
+ childPid: prepared.execution.child.pid,
930
+ childStartTime: prepared.execution.child.startTime,
931
+ errorType: error instanceof Error ? error.name : typeof error,
932
+ });
933
+ });
355
934
  if (!brokerOwns()) {
356
935
  prepared.terminate(false);
357
936
  active = null;
@@ -364,9 +943,14 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
364
943
  retiring = true;
365
944
  continue;
366
945
  }
367
- writeResponse(manifest, {
946
+ writeAcceptedResponse(manifest, {
368
947
  version: 2, id, phase: 'accepted', exitCode: null, stdout: '', stderr: '', error: null
369
948
  });
949
+ appendBrokerAudit(manifest, 'request-accepted', {
950
+ ...requestAuditFields(manifest, manifestPath, request),
951
+ acceptedPath: acceptedResponsePath(manifest, id),
952
+ executionPhase: 'prepared'
953
+ });
370
954
  if (!brokerOwns()) {
371
955
  prepared.terminate(false);
372
956
  active = null;
@@ -374,9 +958,19 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
374
958
  break;
375
959
  }
376
960
  try {
961
+ appendBrokerAudit(manifest, 'executor-start', {
962
+ ...requestAuditFields(manifest, manifestPath, request),
963
+ childPid: prepared.execution.child.pid,
964
+ childStartTime: prepared.execution.child.startTime,
965
+ executionPath: executionPath(manifest, request.id)
966
+ });
377
967
  prepared.start(brokerOwns);
378
968
  }
379
969
  catch (error) {
970
+ appendBrokerAudit(manifest, 'executor-start-failed', {
971
+ ...requestAuditFields(manifest, manifestPath, request),
972
+ errorType: error instanceof Error ? error.name : typeof error,
973
+ });
380
974
  const owned = brokerOwns();
381
975
  prepared.terminate(owned);
382
976
  if (!owned || !brokerOwns()) {
@@ -384,12 +978,13 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
384
978
  retiring = true;
385
979
  break;
386
980
  }
387
- writeResponse(manifest, unknown(id));
981
+ writeSandboxControlResponse(manifest, unknown(id));
388
982
  if (!brokerOwns()) {
389
983
  active = null;
390
984
  retiring = true;
391
985
  break;
392
986
  }
987
+ removeAcceptedResponse(manifest, id);
393
988
  fs.rmSync(path.join(manifest.processingDir, id), { recursive: true, force: true });
394
989
  active = null;
395
990
  claimed = null;
@@ -397,7 +992,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
397
992
  retiring = true;
398
993
  break;
399
994
  }
400
- appendSandboxControlAudit(manifest, 'executor-gate-failed', { requestId: id });
995
+ appendBrokerAudit(manifest, 'executor-gate-failed', { requestId: id });
401
996
  continue;
402
997
  }
403
998
  }
@@ -406,7 +1001,17 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
406
1001
  retiring = true;
407
1002
  break;
408
1003
  }
409
- writeResponse(manifest, rejected(id, error));
1004
+ const detail = controlError(error);
1005
+ appendBrokerAudit(manifest, 'request-rejected', {
1006
+ requestId: id,
1007
+ requestFamily: validatedRequest?.family ?? null,
1008
+ sandboxTaskId: manifest.taskId,
1009
+ requestPath: claimed,
1010
+ errorCode: detail.code,
1011
+ errorRetryable: detail.retryable,
1012
+ errorType: error instanceof Error ? error.name : typeof error,
1013
+ });
1014
+ writeSandboxControlResponse(manifest, rejected(id, error));
410
1015
  if (!brokerOwns()) {
411
1016
  retiring = true;
412
1017
  break;
@@ -423,9 +1028,47 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
423
1028
  finally {
424
1029
  if (active) {
425
1030
  const owned = brokerOwns();
426
- active.prepared.terminate(owned);
427
- if (owned && brokerOwns())
428
- writeResponse(manifest, unknown(active.request.id));
1031
+ if (owned && active.result && !active.resultEvidenceWritten) {
1032
+ try {
1033
+ active.result = sanitizeSandboxControlResult(manifest, active.result);
1034
+ writeSandboxControlResultEvidence(manifest, active.request.id, active.result);
1035
+ active.resultEvidenceWritten = true;
1036
+ active.failure = null;
1037
+ active.settled = true;
1038
+ }
1039
+ catch (error) {
1040
+ active.failure = error;
1041
+ }
1042
+ }
1043
+ if (owned && active.result && active.resultEvidenceWritten) {
1044
+ if (publishExecutionResult(manifest, active.request, active.result, broker, brokerOwns)) {
1045
+ if (brokerOwns()) {
1046
+ removeAcceptedResponse(manifest, active.request.id);
1047
+ fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
1048
+ active = null;
1049
+ }
1050
+ }
1051
+ }
1052
+ if (active) {
1053
+ const terminationConfirmed = active.prepared.terminate(owned);
1054
+ if (owned && brokerOwns() && active.request.family === 'task-finalization') {
1055
+ const recovered = finalizationRecoveryResponse(manifest, active.request.id, 0);
1056
+ const view = recovered.status === 'matched'
1057
+ ? publishFinalizationTaskView(manifest, broker, active.request.id, 'healthy', null, null)
1058
+ : null;
1059
+ if (view?.state !== 'unknown' && recovered.status === 'matched' && recovered.response
1060
+ && writeSandboxControlResponse(manifest, recovered.response)) {
1061
+ if (terminationConfirmed && brokerOwns()) {
1062
+ removeAcceptedResponse(manifest, active.request.id);
1063
+ fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
1064
+ active = null;
1065
+ }
1066
+ }
1067
+ }
1068
+ if (owned && brokerOwns() && active && active.request.family !== 'task-finalization' && !active.resultEvidenceWritten) {
1069
+ writeSandboxControlResponse(manifest, unknown(active.request.id));
1070
+ }
1071
+ }
429
1072
  }
430
1073
  if (brokerOwns()) {
431
1074
  appendSandboxControlAudit(manifest, 'broker-stop', { pid: broker.pid, brokerId: broker.brokerId });