@fitlab-ai/agent-infra 0.9.13 → 0.9.15

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 (428) hide show
  1. package/README.md +10 -0
  2. package/README.zh-CN.md +10 -0
  3. package/bin/cli.ts +26 -9
  4. package/bin/internal-cli.sh +45 -0
  5. package/dist/bin/cli.js +173 -157
  6. package/dist/bin/internal-cli.js +126 -42
  7. package/dist/lib/agent-client.js +5 -0
  8. package/dist/lib/agent-clients/adapters/codex-lifecycle/capability-store.js +165 -27
  9. package/dist/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.js +3 -2
  10. package/dist/lib/defaults.json +103 -103
  11. package/dist/lib/fs/durable-write.js +37 -0
  12. package/dist/lib/fs/file-lock.js +111 -0
  13. package/dist/lib/host-control/audit.js +19 -0
  14. package/dist/lib/host-control/client.js +142 -0
  15. package/dist/lib/host-control/command.js +49 -0
  16. package/dist/lib/host-control/path.js +160 -0
  17. package/dist/lib/host-control/secure-fs.js +140 -0
  18. package/dist/lib/host-control/server.js +159 -0
  19. package/dist/lib/host-control/service.js +112 -0
  20. package/dist/lib/init.js +11 -2
  21. package/dist/lib/internal/agent-client.js +5 -2
  22. package/dist/lib/internal/cli-route-inventory.js +154 -0
  23. package/dist/lib/internal/codex-lifecycle.js +15 -12
  24. package/dist/lib/internal/codex-sandbox-controller.js +8 -2
  25. package/dist/lib/internal/demo-promotion.js +322 -0
  26. package/dist/lib/internal/demo-transcript.js +454 -0
  27. package/dist/lib/internal/git-workflow.js +10 -6
  28. package/dist/lib/internal/host-control.js +28 -0
  29. package/dist/lib/internal/manual-validation.js +322 -0
  30. package/dist/lib/internal/platform-checks.js +16 -4
  31. package/dist/lib/internal/platform-comment.js +16 -4
  32. package/dist/lib/internal/platform-context.js +4 -1
  33. package/dist/lib/internal/platform-issue.js +12 -4
  34. package/dist/lib/internal/platform-metadata.js +88 -0
  35. package/dist/lib/internal/platform-pr-review.js +16 -3
  36. package/dist/lib/internal/platform-pr.js +42 -14
  37. package/dist/lib/internal/platform-release-notes.js +6 -3
  38. package/dist/lib/internal/platform-security.js +93 -0
  39. package/dist/lib/internal/pr-review-grade.js +14 -3
  40. package/dist/lib/internal/release-workflow.js +82 -89
  41. package/dist/lib/internal/sandbox-control.js +171 -7
  42. package/dist/lib/internal/task-activity.js +13 -4
  43. package/dist/lib/internal/task-artifact.js +19 -107
  44. package/dist/lib/internal/task-context.js +4 -1
  45. package/dist/lib/internal/task-create.js +48 -14
  46. package/dist/lib/internal/task-delivery.js +4 -1
  47. package/dist/lib/internal/task-event.js +38 -35
  48. package/dist/lib/internal/task-finalization.js +4 -1
  49. package/dist/lib/internal/task-invalidation.js +4 -1
  50. package/dist/lib/internal/task-ledger.js +12 -1
  51. package/dist/lib/internal/task-lifecycle.js +4 -1
  52. package/dist/lib/internal/task-operation-registry.js +519 -0
  53. package/dist/lib/internal/task-orchestration.js +12 -0
  54. package/dist/lib/internal/task-override.js +10 -3
  55. package/dist/lib/internal/task-qualification.js +90 -0
  56. package/dist/lib/internal/task-review.js +10 -52
  57. package/dist/lib/internal/task-short-id.js +11 -1
  58. package/dist/lib/internal/task-snapshot.js +4 -1
  59. package/dist/lib/internal/task-validate.js +37 -12
  60. package/dist/lib/internal/task-verify.js +5 -1
  61. package/dist/lib/internal/task-warning.js +8 -1
  62. package/dist/lib/owned-path.js +33 -0
  63. package/dist/lib/platform/check-status.js +11 -0
  64. package/dist/lib/platform/comment-safety.js +229 -0
  65. package/dist/lib/platform/completion-backfill.js +1 -1
  66. package/dist/lib/platform/github-data.js +241 -0
  67. package/dist/lib/platform/github-provider.js +248 -26
  68. package/dist/lib/platform/issue-comments.js +146 -34
  69. package/dist/lib/platform/issues.js +1 -179
  70. package/dist/lib/platform/pr-change-report.js +6 -12
  71. package/dist/lib/platform/pr-checks.js +12 -65
  72. package/dist/lib/platform/pr-summary.js +83 -10
  73. package/dist/lib/platform/provider-contract.d.ts +67 -1
  74. package/dist/lib/platform/provider-contract.js +6 -2
  75. package/dist/lib/platform/provider-validation.js +76 -3
  76. package/dist/lib/platform/pull-requests.js +117 -275
  77. package/dist/lib/platform/repository-metadata.js +377 -0
  78. package/dist/lib/platform/security-alerts.js +155 -0
  79. package/dist/lib/platform/snapshots.js +2 -0
  80. package/dist/lib/platform/verification-sync.js +154 -162
  81. package/dist/lib/process-data/index.js +7 -0
  82. package/dist/lib/release/workflow.js +1 -1
  83. package/dist/lib/run/index.js +5 -0
  84. package/dist/lib/sandbox/commands/control-status.js +22 -0
  85. package/dist/lib/sandbox/commands/create.js +39 -8
  86. package/dist/lib/sandbox/commands/ls.js +5 -0
  87. package/dist/lib/sandbox/commands/rm.js +3 -807
  88. package/dist/lib/sandbox/commands/show.js +16 -0
  89. package/dist/lib/sandbox/control/audit.js +203 -0
  90. package/dist/lib/sandbox/control/client.js +125 -12
  91. package/dist/lib/sandbox/control/completed-reentry.js +85 -0
  92. package/dist/lib/sandbox/control/container-identity.js +28 -31
  93. package/dist/lib/sandbox/control/executor.js +233 -34
  94. package/dist/lib/sandbox/control/finalization-response.js +27 -0
  95. package/dist/lib/sandbox/control/identity-sentinel.js +107 -0
  96. package/dist/lib/sandbox/control/lifecycle.js +656 -128
  97. package/dist/lib/sandbox/control/native-file-lock.js +85 -0
  98. package/dist/lib/sandbox/control/protocol.js +55 -4
  99. package/dist/lib/sandbox/control/server.js +720 -79
  100. package/dist/lib/sandbox/control/state.js +63 -3
  101. package/dist/lib/sandbox/control/task-view.js +183 -0
  102. package/dist/lib/sandbox/control/task-workflow.js +74 -0
  103. package/dist/lib/sandbox/control/workflow-executor.js +99 -0
  104. package/dist/lib/sandbox/engines/authority.js +289 -0
  105. package/dist/lib/sandbox/engines/docker-exact-discovery.js +97 -0
  106. package/dist/lib/sandbox/index.js +7 -2
  107. package/dist/lib/sandbox/recovery.js +49 -5
  108. package/dist/lib/sandbox/removal.js +1717 -0
  109. package/dist/lib/sandbox/task-cleanup.js +217 -0
  110. package/dist/lib/sandbox/workspace-view.js +15 -15
  111. package/dist/lib/sandbox/worktree-safety.js +4 -1
  112. package/dist/lib/server/index.js +8 -0
  113. package/dist/lib/task/activity-intent.js +2 -1
  114. package/dist/lib/task/activity-log.js +30 -24
  115. package/dist/lib/task/artifact-command.js +101 -0
  116. package/dist/lib/task/artifact-lifecycle.js +71 -40
  117. package/dist/lib/task/artifact-name.js +36 -0
  118. package/dist/lib/task/artifact-operations.js +474 -0
  119. package/dist/lib/task/artifact-receipts.js +3 -15
  120. package/dist/lib/task/artifact-repair-intent.js +130 -0
  121. package/dist/lib/task/artifact-schema.js +127 -0
  122. package/dist/lib/task/capabilities.js +83 -19
  123. package/dist/lib/task/codex-orchestration.js +71 -25
  124. package/dist/lib/task/commit-identity.js +28 -0
  125. package/dist/lib/task/commit-operation.js +2 -16
  126. package/dist/lib/task/control-authority.js +375 -5
  127. package/dist/lib/task/control-recovery.js +128 -0
  128. package/dist/lib/task/create-service.js +103 -4
  129. package/dist/lib/task/create.js +80 -2
  130. package/dist/lib/task/decision-details.js +2 -46
  131. package/dist/lib/task/events.js +306 -76
  132. package/dist/lib/task/finalization-state.js +9 -0
  133. package/dist/lib/task/human-override.js +3 -6
  134. package/dist/lib/task/implementation-inputs.js +3 -3
  135. package/dist/lib/task/index.js +11 -3
  136. package/dist/lib/task/intermediate-cleanup.js +383 -0
  137. package/dist/lib/task/invalidation.js +18 -65
  138. package/dist/lib/task/ledger-intents.js +7 -22
  139. package/dist/lib/task/ledger.js +1 -3
  140. package/dist/lib/task/lifecycle.js +19 -1
  141. package/dist/lib/task/local-artifact-finalization.js +190 -290
  142. package/dist/lib/task/manual-validation-completion.js +26 -0
  143. package/dist/lib/task/manual-validation-evidence.js +130 -0
  144. package/dist/lib/task/manual-validation-receipt.js +101 -0
  145. package/dist/lib/task/manual-validation-shared.js +40 -0
  146. package/dist/lib/task/manual-validation-transaction.js +243 -0
  147. package/dist/lib/task/markdown.js +47 -0
  148. package/dist/lib/task/orchestration.js +206 -69
  149. package/dist/lib/task/qualification-audit.js +476 -0
  150. package/dist/lib/task/qualification-confirmation.js +109 -0
  151. package/dist/lib/task/qualification-intents.js +148 -0
  152. package/dist/lib/task/review-artifacts.js +23 -40
  153. package/dist/lib/task/review-command.js +25 -0
  154. package/dist/lib/task/review-finalization.js +146 -97
  155. package/dist/lib/task/review-fingerprint.js +2 -2
  156. package/dist/lib/task/sections.js +34 -54
  157. package/dist/lib/task/verification-config.js +11 -0
  158. package/dist/lib/task/verification-engine.js +170 -327
  159. package/dist/lib/task/verification.js +1 -1
  160. package/dist/lib/task/workflow-command.js +17 -0
  161. package/dist/lib/task/workflow-warning-intents.js +0 -16
  162. package/dist/lib/task/workflow-warnings.js +28 -2
  163. package/dist/lib/update.js +2 -0
  164. package/dist/lifecycle-build-manifest.json +3 -3
  165. package/lib/agent-client.ts +9 -0
  166. package/lib/agent-clients/adapters/codex-lifecycle/capability-store.ts +223 -33
  167. package/lib/agent-clients/adapters/codex-lifecycle/sandbox-controller.ts +3 -2
  168. package/lib/fs/durable-write.ts +29 -0
  169. package/lib/fs/file-lock.ts +113 -0
  170. package/lib/host-control/audit.ts +22 -0
  171. package/lib/host-control/client.ts +172 -0
  172. package/lib/host-control/command.ts +60 -0
  173. package/lib/host-control/path.ts +167 -0
  174. package/lib/host-control/secure-fs.ts +135 -0
  175. package/lib/host-control/server.ts +180 -0
  176. package/lib/host-control/service.ts +133 -0
  177. package/lib/init.ts +15 -2
  178. package/lib/internal/agent-client.ts +4 -2
  179. package/lib/internal/cli-route-inventory.ts +142 -0
  180. package/lib/internal/codex-lifecycle.ts +14 -12
  181. package/lib/internal/codex-sandbox-controller.ts +7 -2
  182. package/lib/internal/demo-promotion.ts +354 -0
  183. package/lib/internal/demo-transcript.ts +419 -0
  184. package/lib/internal/git-workflow.ts +9 -6
  185. package/lib/internal/host-control.ts +28 -0
  186. package/lib/internal/manual-validation.ts +306 -0
  187. package/lib/internal/platform-checks.ts +12 -4
  188. package/lib/internal/platform-comment.ts +12 -4
  189. package/lib/internal/platform-context.ts +3 -1
  190. package/lib/internal/platform-issue.ts +11 -4
  191. package/lib/internal/platform-metadata.ts +70 -0
  192. package/lib/internal/platform-pr-review.ts +14 -3
  193. package/lib/internal/platform-pr.ts +38 -14
  194. package/lib/internal/platform-release-notes.ts +5 -3
  195. package/lib/internal/platform-security.ts +83 -0
  196. package/lib/internal/pr-review-grade.ts +10 -3
  197. package/lib/internal/release-workflow.ts +91 -87
  198. package/lib/internal/sandbox-control.ts +181 -6
  199. package/lib/internal/task-activity.ts +12 -4
  200. package/lib/internal/task-artifact.ts +15 -79
  201. package/lib/internal/task-context.ts +3 -1
  202. package/lib/internal/task-create.ts +76 -15
  203. package/lib/internal/task-delivery.ts +3 -1
  204. package/lib/internal/task-event.ts +21 -10
  205. package/lib/internal/task-finalization.ts +3 -1
  206. package/lib/internal/task-invalidation.ts +3 -1
  207. package/lib/internal/task-ledger.ts +11 -1
  208. package/lib/internal/task-lifecycle.ts +3 -1
  209. package/lib/internal/task-operation-registry.ts +590 -0
  210. package/lib/internal/task-orchestration.ts +12 -0
  211. package/lib/internal/task-override.ts +9 -3
  212. package/lib/internal/task-qualification.ts +61 -0
  213. package/lib/internal/task-review.ts +7 -50
  214. package/lib/internal/task-short-id.ts +10 -1
  215. package/lib/internal/task-snapshot.ts +3 -1
  216. package/lib/internal/task-validate.ts +32 -12
  217. package/lib/internal/task-verify.ts +4 -1
  218. package/lib/internal/task-warning.ts +7 -1
  219. package/lib/owned-path.ts +26 -0
  220. package/lib/platform/check-status.ts +9 -0
  221. package/lib/platform/comment-safety.ts +255 -0
  222. package/lib/platform/completion-backfill.ts +1 -1
  223. package/lib/platform/github-data.ts +322 -0
  224. package/lib/platform/github-provider.ts +244 -27
  225. package/lib/platform/issue-comments.ts +159 -35
  226. package/lib/platform/issues.ts +4 -245
  227. package/lib/platform/pr-change-report.ts +6 -12
  228. package/lib/platform/pr-checks.ts +13 -77
  229. package/lib/platform/pr-summary.ts +88 -11
  230. package/lib/platform/provider-contract.ts +78 -3
  231. package/lib/platform/provider-validation.ts +85 -3
  232. package/lib/platform/pull-requests.ts +129 -348
  233. package/lib/platform/repository-metadata.ts +453 -0
  234. package/lib/platform/security-alerts.ts +191 -0
  235. package/lib/platform/snapshots.ts +53 -0
  236. package/lib/platform/verification-sync.ts +171 -177
  237. package/lib/process-data/index.ts +7 -0
  238. package/lib/release/workflow.ts +3 -3
  239. package/lib/run/index.ts +5 -0
  240. package/lib/sandbox/commands/control-status.ts +26 -0
  241. package/lib/sandbox/commands/create.ts +53 -12
  242. package/lib/sandbox/commands/ls.ts +7 -0
  243. package/lib/sandbox/commands/rm.ts +3 -996
  244. package/lib/sandbox/commands/show.ts +16 -0
  245. package/lib/sandbox/control/audit.ts +253 -0
  246. package/lib/sandbox/control/client.ts +150 -13
  247. package/lib/sandbox/control/completed-reentry.ts +98 -0
  248. package/lib/sandbox/control/container-identity.ts +27 -43
  249. package/lib/sandbox/control/executor.ts +262 -37
  250. package/lib/sandbox/control/finalization-response.ts +29 -0
  251. package/lib/sandbox/control/identity-sentinel.ts +137 -0
  252. package/lib/sandbox/control/lifecycle.ts +672 -18
  253. package/lib/sandbox/control/native-file-lock.ts +108 -0
  254. package/lib/sandbox/control/protocol.ts +80 -8
  255. package/lib/sandbox/control/server.ts +779 -82
  256. package/lib/sandbox/control/state.ts +84 -3
  257. package/lib/sandbox/control/task-view.ts +240 -0
  258. package/lib/sandbox/control/task-workflow.ts +95 -0
  259. package/lib/sandbox/control/workflow-executor.ts +102 -0
  260. package/lib/sandbox/engines/authority.ts +365 -0
  261. package/lib/sandbox/engines/docker-exact-discovery.ts +132 -0
  262. package/lib/sandbox/index.ts +9 -2
  263. package/lib/sandbox/recovery.ts +49 -6
  264. package/lib/sandbox/removal.ts +2144 -0
  265. package/lib/sandbox/task-cleanup.ts +209 -0
  266. package/lib/sandbox/workspace-view.ts +26 -18
  267. package/lib/sandbox/worktree-safety.ts +4 -1
  268. package/lib/server/index.ts +10 -0
  269. package/lib/task/activity-intent.ts +2 -2
  270. package/lib/task/activity-log.ts +27 -19
  271. package/lib/task/artifact-command.ts +104 -0
  272. package/lib/task/artifact-lifecycle.ts +66 -39
  273. package/lib/task/artifact-name.ts +55 -0
  274. package/lib/task/artifact-operations.ts +580 -0
  275. package/lib/task/artifact-receipts.ts +3 -14
  276. package/lib/task/artifact-repair-intent.ts +152 -0
  277. package/lib/task/artifact-schema.ts +163 -0
  278. package/lib/task/capabilities.ts +82 -22
  279. package/lib/task/codex-orchestration.ts +85 -24
  280. package/lib/task/commit-identity.ts +31 -0
  281. package/lib/task/commit-operation.ts +2 -16
  282. package/lib/task/control-authority.ts +513 -6
  283. package/lib/task/control-recovery.ts +174 -0
  284. package/lib/task/create-service.ts +131 -6
  285. package/lib/task/create.ts +83 -1
  286. package/lib/task/decision-details.ts +3 -71
  287. package/lib/task/events.ts +292 -75
  288. package/lib/task/finalization-state.ts +11 -0
  289. package/lib/task/human-override.ts +3 -8
  290. package/lib/task/implementation-inputs.ts +3 -3
  291. package/lib/task/index.ts +16 -3
  292. package/lib/task/intermediate-cleanup.ts +455 -0
  293. package/lib/task/invalidation.ts +18 -62
  294. package/lib/task/ledger-intents.ts +7 -24
  295. package/lib/task/ledger.ts +1 -3
  296. package/lib/task/lifecycle.ts +17 -1
  297. package/lib/task/local-artifact-finalization.ts +223 -388
  298. package/lib/task/manual-validation-completion.ts +40 -0
  299. package/lib/task/manual-validation-evidence.ts +209 -0
  300. package/lib/task/manual-validation-receipt.ts +152 -0
  301. package/lib/task/manual-validation-shared.ts +50 -0
  302. package/lib/task/manual-validation-transaction.ts +304 -0
  303. package/lib/task/markdown.ts +70 -0
  304. package/lib/task/orchestration.ts +191 -68
  305. package/lib/task/qualification-audit.ts +588 -0
  306. package/lib/task/qualification-confirmation.ts +117 -0
  307. package/lib/task/qualification-intents.ts +150 -0
  308. package/lib/task/review-artifacts.ts +21 -41
  309. package/lib/task/review-command.ts +23 -0
  310. package/lib/task/review-finalization.ts +154 -153
  311. package/lib/task/review-fingerprint.ts +2 -2
  312. package/lib/task/sections.ts +33 -52
  313. package/lib/task/verification-config.ts +12 -0
  314. package/lib/task/verification-engine.ts +165 -370
  315. package/lib/task/verification-types.ts +17 -0
  316. package/lib/task/verification.ts +1 -1
  317. package/lib/task/workflow-command.ts +18 -0
  318. package/lib/task/workflow-warning-intents.ts +0 -13
  319. package/lib/task/workflow-warnings.ts +21 -2
  320. package/lib/update.ts +6 -0
  321. package/package.json +7 -2
  322. package/templates/.agents/hooks/lifecycle-delegation.js +6 -6
  323. package/templates/.agents/rules/README.en.md +2 -0
  324. package/templates/.agents/rules/README.zh-CN.md +2 -0
  325. package/templates/.agents/rules/decision-qualification.en.md +22 -0
  326. package/templates/.agents/rules/decision-qualification.zh-CN.md +22 -0
  327. package/templates/.agents/rules/evidence-reporting.en.md +28 -0
  328. package/templates/.agents/rules/evidence-reporting.zh-CN.md +28 -0
  329. package/templates/.agents/rules/label-milestone-setup.en.md +7 -7
  330. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +7 -7
  331. package/templates/.agents/rules/local-artifact-repair.en.md +5 -1
  332. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +5 -1
  333. package/templates/.agents/rules/security-alerts.en.md +8 -8
  334. package/templates/.agents/rules/security-alerts.zh-CN.md +8 -8
  335. package/templates/.agents/rules/testing-discipline.en.md +2 -0
  336. package/templates/.agents/rules/testing-discipline.zh-CN.md +2 -0
  337. package/templates/.agents/scripts/package.json +3 -0
  338. package/templates/.agents/skills/analyze-task/SKILL.en.md +15 -2
  339. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +19 -2
  340. package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -12
  341. package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -12
  342. package/templates/.agents/skills/close-codescan/SKILL.en.md +2 -2
  343. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +2 -2
  344. package/templates/.agents/skills/close-dependabot/SKILL.en.md +2 -2
  345. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +2 -2
  346. package/templates/.agents/skills/code-task/SKILL.en.md +14 -1
  347. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +14 -1
  348. package/templates/.agents/skills/code-task/config/verify.en.json +5 -14
  349. package/templates/.agents/skills/code-task/config/verify.zh-CN.json +5 -14
  350. package/templates/.agents/skills/code-task/reference/code-rules.en.md +5 -4
  351. package/templates/.agents/skills/code-task/reference/code-rules.zh-CN.md +5 -4
  352. package/templates/.agents/skills/code-task/reference/dual-mode.en.md +1 -1
  353. package/templates/.agents/skills/code-task/reference/dual-mode.zh-CN.md +1 -1
  354. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +3 -1
  355. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +3 -1
  356. package/templates/.agents/skills/code-task/reference/report-template.en.md +42 -15
  357. package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +39 -12
  358. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +37 -21
  359. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +37 -21
  360. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  361. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  362. package/templates/.agents/skills/complete-manual-validation/reference/report-template.en.md +4 -0
  363. package/templates/.agents/skills/complete-manual-validation/reference/report-template.zh-CN.md +4 -0
  364. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +16 -7
  365. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +16 -7
  366. package/templates/.agents/skills/complete-task/SKILL.en.md +10 -23
  367. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +10 -23
  368. package/templates/.agents/skills/create-task/SKILL.en.md +8 -0
  369. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +7 -0
  370. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  371. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  372. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  373. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  374. package/templates/.agents/skills/init-labels/SKILL.en.md +8 -8
  375. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +8 -8
  376. package/templates/.agents/skills/init-milestones/SKILL.en.md +9 -11
  377. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +9 -11
  378. package/templates/.agents/skills/plan-task/SKILL.en.md +14 -1
  379. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +16 -1
  380. package/templates/.agents/skills/plan-task/config/verify.en.json +4 -14
  381. package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +4 -14
  382. package/templates/.agents/skills/release/SKILL.en.md +7 -3
  383. package/templates/.agents/skills/release/SKILL.zh-CN.md +7 -3
  384. package/templates/.agents/skills/review-analysis/SKILL.en.md +14 -1
  385. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -1
  386. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -18
  387. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -18
  388. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +50 -23
  389. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +45 -18
  390. package/templates/.agents/skills/review-code/SKILL.en.md +14 -1
  391. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +14 -1
  392. package/templates/.agents/skills/review-code/config/verify.en.json +1 -23
  393. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -23
  394. package/templates/.agents/skills/review-code/reference/report-template.en.md +49 -22
  395. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +45 -18
  396. package/templates/.agents/skills/review-plan/SKILL.en.md +14 -1
  397. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -1
  398. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -18
  399. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -18
  400. package/templates/.agents/skills/review-plan/reference/report-template.en.md +49 -22
  401. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +45 -18
  402. package/templates/.agents/skills/review-pr/SKILL.en.md +5 -1
  403. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +5 -1
  404. package/templates/.agents/skills/review-pr/reference/report-template.en.md +4 -3
  405. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +4 -3
  406. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +25 -4
  407. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +25 -4
  408. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.en.md +4 -1
  409. package/templates/.agents/skills/run-manual-validation/reference/discovery-and-execution.zh-CN.md +4 -1
  410. package/templates/.agents/skills/run-manual-validation/reference/report-template.en.md +5 -1
  411. package/templates/.agents/skills/run-manual-validation/reference/report-template.zh-CN.md +5 -1
  412. package/templates/.agents/skills/run-task/SKILL.en.md +1 -1
  413. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +1 -1
  414. package/templates/.agents/skills/test/SKILL.en.md +29 -7
  415. package/templates/.agents/skills/test/SKILL.zh-CN.md +29 -7
  416. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +14 -0
  417. package/templates/.agents/templates/task.en.md +6 -0
  418. package/templates/.agents/templates/task.zh-CN.md +6 -0
  419. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  420. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  421. package/dist/lib/platform/adapters.js +0 -56
  422. package/lib/platform/adapters.ts +0 -197
  423. package/templates/.agents/scripts/security-alerts.github.sh +0 -151
  424. package/templates/.agents/scripts/security-alerts.sh +0 -31
  425. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +0 -146
  426. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +0 -32
  427. package/templates/.agents/skills/init-milestones/scripts/init-milestones.github.sh +0 -294
  428. package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +0 -6
@@ -1,3 +1,6 @@
1
+ import { finalizationTerminalResponse } from './finalization-response.js';
2
+ import { isDeepStrictEqual } from 'node:util';
3
+ import { completedReentryView } from './completed-reentry.js';
1
4
  import fs from 'node:fs';
2
5
  import path from 'node:path';
3
6
  import { createHash, randomUUID } from 'node:crypto';
@@ -5,13 +8,126 @@ import { getProcessStartTime } from '../../server/process-state.js';
5
8
  import { assertGitWorktreeBinding } from '../../git/worktree-identity.js';
6
9
  import { controlError, DEFAULT_SANDBOX_CONTROL_TIMING, SANDBOX_CONTROL_MAX_RESPONSE_BYTES, SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES } from './protocol.js';
7
10
  import { prepareSandboxControlExecution } from './executor.js';
8
- import { appendSandboxControlAudit, atomicWriteJsonNoReplace, createSandboxControlPayload, cleanupStaleSandboxControlLease, executionPath, readJsonFile, readActiveLease, readExecution, readSandboxControlPayload, readSandboxControlResultEvidence, sandboxControlEncodedJsonBytes, sandboxControlGenerationUsage, sanitizeSandboxControlOutput, sanitizeSandboxControlResult, payloadPath, writeSandboxControlPayload, writeSandboxControlReservation, writeSandboxControlResultEvidence, terminateSandboxControlExecution, writeSandboxControlStatus } from './state.js';
11
+ import { atomicWriteJsonNoReplace, createSandboxControlPayload, cleanupStaleSandboxControlLease, executionPath, readJsonFile, readActiveLease, readExecution, readSandboxControlPayload, readSandboxControlResultEvidence, readSandboxControlTerminalResult, createSandboxControlTerminalResult, sandboxControlEncodedJsonBytes, sandboxControlGenerationUsage, sanitizeSandboxControlOutput, sanitizeSandboxControlResult, payloadPath, writeSandboxControlPayload, writeSandboxControlReservation, writeSandboxControlResultEvidence, writeSandboxControlTerminalResult, appendSandboxControlAudit, terminateSandboxControlExecution, writeSandboxControlStatus, readSandboxControlStatus } from './state.js';
9
12
  import { validateSandboxControlRequest } from './protocol.js';
13
+ import { requestAuditFields, resultAuditFields, appendCriticalAudit, appendDiagnosticAudit, createSandboxControlAuditContext, writeSandboxControlTransition, readSandboxControlTransition } from './audit.js';
14
+ import { parseTaskControlOperation } from '../../task/control-authority.js';
15
+ import { parseControlOutput, classifySandboxControlRecovery, findSandboxControlRecoveryOperation, operationRecoveryBinding, SANDBOX_CONTROL_REQUIRED_COMPLETION_PHASES } from '../../task/control-recovery.js';
16
+ import { readRun } from '../../task/orchestration.js';
17
+ import { captureRepositorySnapshot } from '../../task/workspace-snapshot.js';
18
+ import { parseTypedTaskFrontmatter } from '../../task/frontmatter.js';
19
+ import { readLifecycleJournalEvidence } from '../../task/lifecycle.js';
20
+ import { resolveTaskRef } from '../../task/resolve-ref.js';
21
+ import { loadShortIdByTaskId } from '../../task/short-id.js';
10
22
  import { inspectSandboxControlContainer } from './container-identity.js';
11
23
  import { acquireSandboxControlBrokerStartup, garbageCollectSandboxControlRoot, isSandboxControlRootQuiescing, readSandboxControlManifest } from './lifecycle.js';
12
24
  import { nextSandboxControlBackoff } from './timing.js';
13
25
  import { readTaskFinalizationReceipt } from '../../task/finalization.js';
14
- import { resolveTaskRef } from '../../task/resolve-ref.js';
26
+ import { readCodexControllerRegistration } from './controller-registration.js';
27
+ import { validateSandboxControlIdentity } from './identity-sentinel.js';
28
+ import { mergeSandboxTaskView, taskViewAfterFinalization, taskViewForManifest } from './task-view.js';
29
+ import { taskCreateOutputUnavailableResult } from '../../task/create-service.js';
30
+ import { consumeLifecycleRecoveryAttestation, issueLifecycleRecoveryAttestation, recoverLifecycleRecoveryOperation } from '../../task/control-authority.js';
31
+ import { createCodexCapabilityStore } from '../../agent-clients/adapters/codex-lifecycle/capability-store.js';
32
+ import { computeLifecycleBuildIdentity } from '../../agent-clients/adapters/codex-lifecycle/build-identity.js';
33
+ function consumeLifecycleAuthorityForResult(manifest, execution) {
34
+ const authority = execution.lifecycleAuthority?.attestation;
35
+ if (!authority || execution.result?.exitCode !== 0)
36
+ return true;
37
+ try {
38
+ consumeLifecycleRecoveryAttestation(authority);
39
+ return true;
40
+ }
41
+ catch (error) {
42
+ appendBrokerAudit(manifest, 'lifecycle-authority-consume-retry', {
43
+ ...requestAuditFields(manifest, execution.request),
44
+ errorCode: error instanceof Error ? error.name : 'LIFECYCLE_AUTHORITY_CONSUME_FAILED'
45
+ });
46
+ return false;
47
+ }
48
+ }
49
+ function lifecycleAuthorityForRequest(manifest, manifestPath, request) {
50
+ const selector = request.family === 'task-workflow' || request.family === 'task-orchestration'
51
+ ? request.authority
52
+ : undefined;
53
+ if (!selector)
54
+ return undefined;
55
+ let controllerBinding = null;
56
+ try {
57
+ const registration = readCodexControllerRegistration(manifestPath);
58
+ controllerBinding = {
59
+ instanceDigest: registration.controllerInstanceDigest,
60
+ controlGeneration: registration.controlGeneration
61
+ };
62
+ }
63
+ catch {
64
+ // The issuer reports the stable unavailable result; the broker never trusts
65
+ // a controller binding supplied by the request.
66
+ }
67
+ return issueLifecycleRecoveryAttestation(selector, {
68
+ controllerBinding,
69
+ capabilityStore: createCodexCapabilityStore(),
70
+ buildIdentity: computeLifecycleBuildIdentity(manifest.repoRoot)
71
+ });
72
+ }
73
+ function appendBrokerAudit(manifest, event, fields = {}) {
74
+ appendDiagnosticAudit(manifest, event, { source: 'broker', ...fields });
75
+ }
76
+ function operationKey(request, output) {
77
+ if (request.family === 'task-finalization')
78
+ return request.operation;
79
+ if (request.family === 'task-create')
80
+ return 'create';
81
+ if (request.family === 'codex-controller')
82
+ return request.command;
83
+ if (request.family === 'task-workflow')
84
+ return request.workflow.operation;
85
+ if (request.family !== 'task-lifecycle' && request.family !== 'task-orchestration')
86
+ return null;
87
+ if (request.family === 'task-orchestration' && request.args[1] === 'route') {
88
+ if (!output)
89
+ return 'route';
90
+ const result = parseControlOutput(output);
91
+ return result?.changed === true && result.status === 'completed' ? 'route.clean-completion' : 'route.read';
92
+ }
93
+ try {
94
+ const operation = parseTaskControlOperation(request.family, request.args);
95
+ return operation.family === 'task-orchestration' ? operation.intent : operation.request.intent;
96
+ }
97
+ catch {
98
+ return null;
99
+ }
100
+ }
101
+ function recoveryOperationKey(request, terminalResult, output) {
102
+ const operation = operationKey(request, output);
103
+ if (operation !== 'route')
104
+ return operation;
105
+ const digest = terminalResult?.intentDigest;
106
+ for (const candidate of ['route.read', 'route.clean-completion']) {
107
+ const expected = operationRecoveryBinding(request.id, request.generation, null, request.family, candidate).intentDigest;
108
+ if (digest === expected)
109
+ return candidate;
110
+ }
111
+ return null;
112
+ }
113
+ function terminalResultMatchesRequest(request, terminalResult) {
114
+ const operation = operationKey(request);
115
+ const candidates = operation === 'route'
116
+ ? ['route.read', 'route.clean-completion']
117
+ : operation ? [operation] : [];
118
+ return candidates.some((candidate) => createHash('sha256').update(`${request.family}\0${candidate}`, 'utf8').digest('hex') === terminalResult.intentDigest);
119
+ }
120
+ function criticalRequestPhase(manifest, request, phase, outcome, reference) {
121
+ const context = createSandboxControlAuditContext(manifest, {
122
+ requestId: request.id,
123
+ family: request.family,
124
+ operation: operationKey(request),
125
+ phase,
126
+ outcome
127
+ });
128
+ appendCriticalAudit(manifest, context, { reference: reference ?? null });
129
+ writeSandboxControlTransition(manifest, { requestId: request.id, phase, reference });
130
+ }
15
131
  function assertRealDirectory(directory, parent) {
16
132
  const stat = fs.lstatSync(directory);
17
133
  if (!stat.isDirectory() || stat.isSymbolicLink())
@@ -92,33 +208,30 @@ function finalizationRecoveryResponse(manifest, requestId, exitCode) {
92
208
  catch {
93
209
  return { status: 'deferred' };
94
210
  }
95
- const pendingSteps = [
96
- receipt.taskComment === 'pending' ? 'task-comment' : null,
97
- receipt.verification === 'pending' ? 'verification' : null
98
- ].filter((step) => step !== null);
99
- const completedSteps = ['lifecycle', receipt.taskComment === 'pending' ? null : 'task-comment', receipt.verification === 'pending' ? null : 'verification']
100
- .filter((step) => step !== null);
101
- const warnings = receipt.warnings
102
- .filter((warning) => warning.status === 'open')
103
- .map(({ status: _status, resolvedAt: _resolvedAt, ...warning }) => warning);
104
- const result = {
105
- status: 'completed', changed: false, taskId: manifest.taskId,
106
- lifecycle: { status: 'no-op', changed: false, error: null },
107
- taskComment: receipt.taskComment === 'pending' ? null : { status: 'no-op', changed: false, error: null },
108
- verification: receipt.verification === 'pending' ? null : { status: 'no-op', changed: false, error: null },
109
- completedSteps, pendingSteps,
110
- result: pendingSteps.length > 0 || receipt.warningProjection === 'pending' || warnings.length > 0
111
- ? 'completed_with_warnings' : 'completed',
112
- warnings, error: null
113
- };
114
- return {
115
- status: 'matched',
116
- response: {
117
- version: 2, id: requestId, phase: 'completed', exitCode: 0,
118
- stdout: `${JSON.stringify({ version: 1, status: 'completed', changed: false, accepted: true, result, error: null })}\n`,
119
- stderr: '', error: null
120
- }
121
- };
211
+ return { status: 'matched', response: finalizationTerminalResponse(manifest.taskId, requestId, receipt) };
212
+ }
213
+ function publishFinalizationTaskView(manifest, broker, requestId, state, reasonCode, activeRequestId) {
214
+ let view;
215
+ try {
216
+ const resolved = resolveTaskRef(manifest.taskId ?? '', { repoRoot: manifest.repoRoot });
217
+ const receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId ?? '');
218
+ view = resolved.ok && resolved.state === 'completed'
219
+ ? taskViewAfterFinalization({
220
+ taskId: manifest.taskId ?? '', generation: manifest.generation, requestId, receipt
221
+ })
222
+ : {
223
+ state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
224
+ reasonCode: 'SANDBOX_TASK_VIEW_SOURCE_UNCONFIRMED'
225
+ };
226
+ }
227
+ catch {
228
+ view = {
229
+ state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null,
230
+ reasonCode: 'SANDBOX_TASK_VIEW_RECEIPT_INVALID'
231
+ };
232
+ }
233
+ writeSandboxControlStatus(manifest, broker, state, reasonCode, activeRequestId, Date.now(), view);
234
+ return view;
122
235
  }
123
236
  function payloadReference(payload) {
124
237
  return {
@@ -143,29 +256,260 @@ function outputMatchesEvidence(output, bytes, sha256) {
143
256
  return Buffer.byteLength(output, 'utf8') === bytes
144
257
  && createHash('sha256').update(output, 'utf8').digest('hex') === sha256;
145
258
  }
146
- function genericRecoveryResponse(requestId, evidence, payload) {
259
+ function genericRecoveryResponse(request, exitCode, payload, cause = 'recovery') {
260
+ const outputUnavailable = request.family === 'task-create' && !payload;
147
261
  return {
148
262
  version: 2,
149
- id: requestId,
263
+ id: request.id,
150
264
  phase: 'completed',
151
- exitCode: evidence.exitCode,
152
- stdout: '',
153
- stderr: payload ? '' : 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: broker restarted after executor completion\n',
265
+ exitCode,
266
+ stdout: outputUnavailable ? `${JSON.stringify(taskCreateOutputUnavailableResult(request.id))}\n` : '',
267
+ stderr: payload ? '' : cause === 'publish'
268
+ ? 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: output payload was not retained\n'
269
+ : 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: broker restarted after executor completion\n',
154
270
  error: null,
155
271
  outputState: payload ? 'available' : 'unavailable',
156
272
  payload: payload ? payloadReference(payload) : null
157
273
  };
158
274
  }
159
- function recoveryResponse(manifest, request, evidence, payload) {
275
+ function taskCreateDomainEvidence(manifest, output) {
276
+ if (!output || !['applied', 'no-op', 'degraded'].includes(String(output.status))) {
277
+ return { consistent: false };
278
+ }
279
+ const task = output.task && typeof output.task === 'object' && !Array.isArray(output.task)
280
+ ? output.task : null;
281
+ const taskId = typeof task?.id === 'string' ? task.id : null;
282
+ const shortId = typeof task?.shortId === 'string' ? task.shortId : null;
283
+ if (!taskId || !shortId)
284
+ return { consistent: false };
285
+ try {
286
+ const resolved = resolveTaskRef(taskId, { repoRoot: manifest.repoRoot });
287
+ const shortIds = loadShortIdByTaskId(manifest.repoRoot);
288
+ return {
289
+ consistent: resolved.ok && resolved.taskId === taskId && resolved.state === 'active' && shortIds.get(taskId) === shortId,
290
+ taskId,
291
+ shortId
292
+ };
293
+ }
294
+ catch {
295
+ return { consistent: false };
296
+ }
297
+ }
298
+ function controllerDomainEvidence(manifest, manifestPath, request, output) {
299
+ if (!output || output.error !== null || typeof output.status !== 'string')
300
+ return { consistent: false };
301
+ try {
302
+ const registration = readCodexControllerRegistration(manifestPath);
303
+ if (request.family !== 'codex-controller')
304
+ return { consistent: false };
305
+ if (request.command === 'open') {
306
+ const lease = output.lease && typeof output.lease === 'object' && !Array.isArray(output.lease)
307
+ ? output.lease : null;
308
+ return {
309
+ consistent: output.status === 'opened' && output.changed === true
310
+ && lease?.taskId === registration.taskId
311
+ && lease.controlGeneration === registration.controlGeneration
312
+ && lease.controllerInstanceDigest === registration.controllerInstanceDigest
313
+ && registration.taskId === manifest.taskId
314
+ && registration.controlGeneration === manifest.generation
315
+ };
316
+ }
317
+ if (request.command === 'verify') {
318
+ const binding = output.binding && typeof output.binding === 'object' && !Array.isArray(output.binding)
319
+ ? output.binding : null;
320
+ return {
321
+ consistent: output.status === 'verified' && output.changed === false
322
+ && binding?.taskId === registration.taskId
323
+ && binding.controlGeneration === registration.controlGeneration
324
+ && binding.controllerInstanceDigest === registration.controllerInstanceDigest
325
+ };
326
+ }
327
+ return { consistent: false };
328
+ }
329
+ catch (error) {
330
+ return { consistent: request.family === 'codex-controller' && request.command === 'close'
331
+ && error.code === 'CODEX_SANDBOX_CONTROLLER_REGISTRATION_MISSING'
332
+ && output?.status === 'closed' && typeof output.changed === 'boolean' };
333
+ }
334
+ }
335
+ function orchestrationDomainEvidence(operation, terminalResult, output, run) {
336
+ if (!operation || !output)
337
+ return { consistent: false };
338
+ const outputRun = output.run && typeof output.run === 'object' && !Array.isArray(output.run)
339
+ ? output.run : null;
340
+ const runMatches = outputRun === null ? run === null : run !== null && JSON.stringify(run) === JSON.stringify(outputRun);
341
+ if (operation.class === 'read-only') {
342
+ return {
343
+ consistent: output.status === terminalResult.status && output.changed === terminalResult.changed && runMatches,
344
+ snapshotValid: true,
345
+ status: run?.status ?? terminalResult.status,
346
+ pendingDelegation: run?.pendingDelegation ?? null
347
+ };
348
+ }
349
+ if (!run || !outputRun)
350
+ return { consistent: false };
351
+ return {
352
+ consistent: output.status === terminalResult.status && output.changed === terminalResult.changed && runMatches,
353
+ status: run.status,
354
+ pendingDelegation: run.pendingDelegation
355
+ };
356
+ }
357
+ function readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payloadOutput) {
358
+ if (!operation)
359
+ return { domain: null };
360
+ const taskRef = request.family === 'task-finalization' || request.family === 'task-workflow'
361
+ ? manifest.taskId
362
+ : 'args' in request ? request.args[0] ?? null : manifest.taskId;
363
+ const output = parseControlOutput(payloadOutput);
364
+ if (!taskRef && operation.family !== 'task-create' && operation.family !== 'codex-controller') {
365
+ return { domain: null };
366
+ }
367
+ if (operation.family === 'task-lifecycle') {
368
+ if (!taskRef || !terminalResult.targetState)
369
+ return { domain: null };
370
+ const journal = readLifecycleJournalEvidence(manifest.repoRoot, taskRef);
371
+ try {
372
+ const resolved = resolveTaskRef(taskRef, { repoRoot: manifest.repoRoot });
373
+ const shortIds = loadShortIdByTaskId(manifest.repoRoot);
374
+ const shortIdMatches = terminalResult.targetState === 'active'
375
+ ? shortIds.has(resolved.ok ? resolved.taskId : taskRef)
376
+ : !shortIds.has(resolved.ok ? resolved.taskId : taskRef);
377
+ return {
378
+ domain: { consistent: resolved.ok && resolved.state === terminalResult.targetState && shortIdMatches },
379
+ journal
380
+ };
381
+ }
382
+ catch {
383
+ return { domain: { consistent: false }, journal };
384
+ }
385
+ }
386
+ if (operation.family === 'task-orchestration') {
387
+ if (!taskRef)
388
+ return { domain: null };
389
+ try {
390
+ const resolved = resolveTaskRef(taskRef, { repoRoot: manifest.repoRoot });
391
+ if (!resolved.ok)
392
+ return { domain: { consistent: false } };
393
+ const run = readRun(resolved.taskDir);
394
+ if (!run && operation.class !== 'read-only')
395
+ return { domain: { consistent: false } };
396
+ if (operation.class === 'route.clean-completion') {
397
+ const snapshot = captureRepositorySnapshot(manifest.repoRoot);
398
+ const metadata = parseTypedTaskFrontmatter(fs.readFileSync(resolved.taskMdPath, 'utf8'));
399
+ const completion = run?.completionEvidence ?? null;
400
+ const consistent = run !== null && completion !== null
401
+ && terminalResult.completionEvidence !== null
402
+ && isDeepStrictEqual(completion, terminalResult.completionEvidence);
403
+ return {
404
+ domain: {
405
+ consistent,
406
+ status: run?.status ?? null,
407
+ pendingDelegation: run?.pendingDelegation ?? null,
408
+ completionEvidence: completion,
409
+ snapshot,
410
+ lastReviewedCommit: metadata.last_reviewed_commit
411
+ }
412
+ };
413
+ }
414
+ return { domain: orchestrationDomainEvidence(operation, terminalResult, output, run) };
415
+ }
416
+ catch {
417
+ return { domain: { consistent: false } };
418
+ }
419
+ }
420
+ if (operation.family === 'task-create') {
421
+ return { domain: taskCreateDomainEvidence(manifest, output) };
422
+ }
423
+ if (operation.family === 'codex-controller') {
424
+ return { domain: controllerDomainEvidence(manifest, manifestPath, request, output) };
425
+ }
426
+ if (operation.family === 'task-workflow') {
427
+ return {
428
+ domain: { consistent: output?.status === terminalResult.status && output.changed === terminalResult.changed, snapshotValid: true }
429
+ };
430
+ }
431
+ return { domain: null };
432
+ }
433
+ function readCommittedCriticalPhases(manifest, requestId) {
434
+ return SANDBOX_CONTROL_REQUIRED_COMPLETION_PHASES.filter((phase) => {
435
+ try {
436
+ readSandboxControlTransition(manifest, requestId, phase);
437
+ return true;
438
+ }
439
+ catch {
440
+ return false;
441
+ }
442
+ });
443
+ }
444
+ function recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult) {
445
+ const operationName = recoveryOperationKey(request, terminalResult, payload?.stdout);
446
+ const operation = operationName ? findSandboxControlRecoveryOperation(request.family, operationName) : null;
447
+ if (!operation)
448
+ return null;
449
+ if (payload) {
450
+ const payloadTerminal = createSandboxControlTerminalResult(manifest, {
451
+ id: request.id, family: request.family, operation: operationName
452
+ }, payload.stdout);
453
+ if (JSON.stringify(payloadTerminal) !== JSON.stringify(terminalResult))
454
+ return unknown(request.id);
455
+ }
456
+ const finalization = request.family === 'task-finalization' ? finalizationRecoveryResponse(manifest, request.id, 0) : null;
457
+ if (evidence.exitCode === 0 && finalization?.status === 'deferred')
458
+ return null;
459
+ if (evidence.exitCode === 0 && request.family === 'task-workflow' && request.authority?.phase === 'task-event.completed') {
460
+ try {
461
+ const registration = readCodexControllerRegistration(manifestPath);
462
+ const recovered = recoverLifecycleRecoveryOperation(request.authority, {
463
+ repoRoot: manifest.repoRoot,
464
+ capabilityStore: createCodexCapabilityStore(),
465
+ buildIdentity: computeLifecycleBuildIdentity(manifest.repoRoot),
466
+ controllerBinding: {
467
+ instanceDigest: registration.controllerInstanceDigest,
468
+ controlGeneration: registration.controlGeneration
469
+ }
470
+ });
471
+ if (recovered.status !== 'committed')
472
+ return null;
473
+ }
474
+ catch (error) {
475
+ appendBrokerAudit(manifest, 'lifecycle-authority-recovery-failed', {
476
+ ...requestAuditFields(manifest, request),
477
+ errorCode: error instanceof Error ? error.name : 'LIFECYCLE_AUTHORITY_RECOVERY_FAILED'
478
+ });
479
+ return null;
480
+ }
481
+ }
482
+ const recovery = finalization
483
+ ? { domain: { consistent: finalization.status === 'matched' } }
484
+ : readRecoveryDomain(manifest, manifestPath, request, operation, terminalResult, payload?.stdout ?? null);
485
+ const binding = operationRecoveryBinding(request.id, manifest.generation, manifest.taskId, request.family, operationName);
486
+ const decision = classifySandboxControlRecovery({
487
+ operation,
488
+ binding,
489
+ startedCommitted: true,
490
+ terminalResult,
491
+ criticalPhases: readCommittedCriticalPhases(manifest, request.id),
492
+ domain: recovery.domain,
493
+ journal: recovery.journal
494
+ });
495
+ if (decision.outcome === 'unknown' || decision.outcome === 'in-progress')
496
+ return unknown(request.id);
497
+ if (decision.outcome === 'not-executed')
498
+ return notExecuted(request.id);
499
+ if (decision.outcome !== 'success' && decision.outcome !== 'failure')
500
+ return null;
160
501
  if (request.family === 'task-finalization' && evidence.exitCode === 0) {
161
- const finalization = finalizationRecoveryResponse(manifest, request.id, evidence.exitCode);
162
- return finalization.status === 'matched' ? finalization.response ?? null : null;
502
+ if (finalization?.status !== 'matched')
503
+ return null;
504
+ return finalization.response ?? null;
163
505
  }
164
- return genericRecoveryResponse(request.id, evidence, payload);
506
+ return genericRecoveryResponse(request, evidence.exitCode, payload);
165
507
  }
166
- function terminalMatchesEvidence(manifest, request, response, evidence, payload, payloadInvalid) {
508
+ function terminalMatchesEvidence(manifest, manifestPath, request, response, evidence, payload, payloadInvalid, terminalResult) {
167
509
  if (request.family === 'task-finalization' && evidence.exitCode === 0) {
168
- const expected = recoveryResponse(manifest, request, evidence, payload);
510
+ if (!terminalResult)
511
+ return { valid: false, payloadReferenced: false };
512
+ const expected = recoveryResponse(manifest, manifestPath, request, evidence, payload, terminalResult);
169
513
  return {
170
514
  valid: expected !== null && JSON.stringify(response) === JSON.stringify(expected),
171
515
  payloadReferenced: false
@@ -192,8 +536,10 @@ function terminalMatchesEvidence(manifest, request, response, evidence, payload,
192
536
  };
193
537
  }
194
538
  if (response.outputState === 'unavailable') {
195
- const expected = genericRecoveryResponse(request.id, evidence, null);
196
- return { valid: JSON.stringify(response) === JSON.stringify(expected), payloadReferenced: false };
539
+ const causes = ['recovery', 'publish'];
540
+ const valid = causes.some((cause) => JSON.stringify(response)
541
+ === JSON.stringify(genericRecoveryResponse(request, evidence.exitCode, null, cause)));
542
+ return { valid, payloadReferenced: false };
197
543
  }
198
544
  return {
199
545
  valid: response.payload === undefined
@@ -202,8 +548,18 @@ function terminalMatchesEvidence(manifest, request, response, evidence, payload,
202
548
  payloadReferenced: false
203
549
  };
204
550
  }
205
- function publishExecutionResult(manifest, request, result, brokerOwns) {
551
+ function publishExecutionResult(manifest, request, result, broker, brokerOwns) {
206
552
  const normalized = sanitizeSandboxControlResult(manifest, result);
553
+ writeSandboxControlTerminalResult(manifest, {
554
+ id: request.id,
555
+ family: request.family,
556
+ operation: operationKey(request, normalized.stdout)
557
+ }, normalized.stdout);
558
+ if (fs.existsSync(path.join(manifest.processingDir, request.id, 'transitions'))) {
559
+ criticalRequestPhase(manifest, request, 'completed', normalized.exitCode === 0 ? 'success' : 'failure');
560
+ criticalRequestPhase(manifest, request, 'evidence-written', normalized.exitCode === 0 ? 'success' : 'failure');
561
+ criticalRequestPhase(manifest, request, 'publish-authorized', normalized.exitCode === 0 ? 'success' : 'failure');
562
+ }
207
563
  let terminal = null;
208
564
  if (request.family === 'task-finalization') {
209
565
  const finalization = finalizationRecoveryResponse(manifest, request.id, normalized.exitCode);
@@ -234,22 +590,21 @@ function publishExecutionResult(manifest, request, result, brokerOwns) {
234
590
  catch {
235
591
  payload = null;
236
592
  }
237
- terminal = {
238
- version: 2,
239
- id: request.id,
240
- phase: 'completed',
241
- exitCode: normalized.exitCode,
242
- stdout: '',
243
- stderr: payload ? '' : 'SANDBOX_CONTROL_OUTPUT_UNAVAILABLE: output payload was not retained\n',
244
- error: null,
245
- outputState: payload ? 'available' : 'unavailable',
246
- payload: payload ? payloadReference(payload) : null
247
- };
593
+ terminal = genericRecoveryResponse(request, normalized.exitCode, payload, 'publish');
248
594
  }
249
595
  }
250
596
  if (!brokerOwns())
251
597
  return false;
252
- return writeSandboxControlResponse(manifest, terminal);
598
+ if (request.family === 'task-finalization' && normalized.exitCode === 0) {
599
+ const view = publishFinalizationTaskView(manifest, broker, request.id, 'healthy', null, null);
600
+ if (view.state === 'unknown')
601
+ return false;
602
+ }
603
+ const committed = writeSandboxControlResponse(manifest, terminal);
604
+ if (committed && fs.existsSync(path.join(manifest.processingDir, request.id, 'transitions'))) {
605
+ criticalRequestPhase(manifest, request, 'published-committed', normalized.exitCode === 0 ? 'success' : 'failure');
606
+ }
607
+ return committed;
253
608
  }
254
609
  function writeAcceptedResponse(manifest, response) {
255
610
  atomicWriteJsonNoReplace(acceptedResponsePath(manifest, response.id), response);
@@ -309,7 +664,20 @@ function bindingReason(manifest) {
309
664
  return 'SANDBOX_WORKTREE_BINDING_LOST';
310
665
  }
311
666
  }
312
- function recoverProcessing(manifest, brokerOwns) {
667
+ function assertCurrentSandboxControlIdentity(manifest, manifestPath) {
668
+ const result = validateSandboxControlIdentity({
669
+ publicStatusDir: manifest.publicStatusDir,
670
+ root: path.dirname(path.resolve(manifestPath)),
671
+ mode: manifest.mode,
672
+ taskId: manifest.taskId,
673
+ generation: manifest.generation,
674
+ controlRootId: manifest.controlRootId
675
+ });
676
+ if (result.state !== 'valid') {
677
+ throw new Error(`SANDBOX_CONTROL_IDENTITY_${result.state.replaceAll('-', '_').toUpperCase()}`);
678
+ }
679
+ }
680
+ function recoverProcessing(manifest, manifestPath, broker, brokerOwns) {
313
681
  for (const entry of fs.readdirSync(manifest.processingDir, { withFileTypes: true })) {
314
682
  if (!entry.isDirectory() || !/^[a-f0-9-]{16,64}$/.test(entry.name))
315
683
  continue;
@@ -318,14 +686,19 @@ function recoverProcessing(manifest, brokerOwns) {
318
686
  const descriptor = executionPath(manifest, entry.name);
319
687
  const existingResponse = responsePath(manifest, entry.name);
320
688
  const processingDirectory = path.join(manifest.processingDir, entry.name);
689
+ const transitionsDirectory = path.join(processingDirectory, 'transitions');
690
+ const transitionProtocolActive = fs.existsSync(transitionsDirectory);
691
+ const startedCommitted = fs.existsSync(path.join(transitionsDirectory, 'started-committed.json'));
321
692
  let payloadReferenced = false;
322
693
  if (fs.existsSync(descriptor)) {
323
694
  const execution = readExecution(descriptor);
324
695
  let resultEvidence = null;
696
+ let terminalResult = null;
325
697
  let payload = null;
326
698
  let payloadInvalid = false;
327
699
  let request = null;
328
700
  let existingTerminalResponse = null;
701
+ let terminalReconciled = false;
329
702
  try {
330
703
  const rawRequest = JSON.parse(fs.readFileSync(path.join(processingDirectory, 'request.json'), 'utf8'));
331
704
  request = validateSandboxControlRequest(rawRequest, manifest, {
@@ -347,6 +720,19 @@ function recoverProcessing(manifest, brokerOwns) {
347
720
  resultEvidence = null;
348
721
  }
349
722
  }
723
+ const terminalResultPathValue = path.join(processingDirectory, 'terminal-result.json');
724
+ if (fs.existsSync(terminalResultPathValue)) {
725
+ try {
726
+ terminalResult = readSandboxControlTerminalResult(terminalResultPathValue);
727
+ if (terminalResult.requestId !== entry.name || terminalResult.generation !== manifest.generation
728
+ || !request || !terminalResultMatchesRequest(request, terminalResult)) {
729
+ terminalResult = null;
730
+ }
731
+ }
732
+ catch {
733
+ terminalResult = null;
734
+ }
735
+ }
350
736
  const publishedPayloadPath = payloadPath(manifest, entry.name);
351
737
  if (fs.existsSync(publishedPayloadPath)) {
352
738
  try {
@@ -360,12 +746,24 @@ function recoverProcessing(manifest, brokerOwns) {
360
746
  payloadInvalid = true;
361
747
  }
362
748
  }
749
+ if (transitionProtocolActive && startedCommitted && !terminalResult) {
750
+ if (!brokerOwns())
751
+ return false;
752
+ if (!terminateSandboxControlExecution(execution)) {
753
+ throw new Error(`SANDBOX_CONTROL_EXECUTION_STILL_RUNNING: ${entry.name}`);
754
+ }
755
+ appendDiagnosticAudit(manifest, 'orphan-tree-terminated', { requestId: entry.name });
756
+ writeSandboxControlResponse(manifest, unknown(entry.name));
757
+ continue;
758
+ }
759
+ if (transitionProtocolActive && !startedCommitted && terminalResult)
760
+ continue;
363
761
  if (!terminateSandboxControlExecution(execution)) {
364
762
  throw new Error(`SANDBOX_CONTROL_EXECUTION_STILL_RUNNING: ${entry.name}`);
365
763
  }
366
764
  if (!brokerOwns())
367
765
  return false;
368
- appendSandboxControlAudit(manifest, 'orphan-tree-terminated', { requestId: entry.name });
766
+ appendDiagnosticAudit(manifest, 'orphan-tree-terminated', { requestId: entry.name });
369
767
  let terminal = false;
370
768
  if (fs.existsSync(existingResponse)) {
371
769
  try {
@@ -385,7 +783,7 @@ function recoverProcessing(manifest, brokerOwns) {
385
783
  throw new Error('payload missing or mismatched');
386
784
  payloadReferenced = true;
387
785
  }
388
- else if (terminal && (response.outputState !== undefined
786
+ else if (terminal && ((response.outputState !== undefined && response.outputState !== 'unavailable')
389
787
  || response.payload !== undefined && response.payload !== null)) {
390
788
  throw new Error('payload state invalid');
391
789
  }
@@ -399,26 +797,66 @@ function recoverProcessing(manifest, brokerOwns) {
399
797
  if (terminal && (!request || !resultEvidence))
400
798
  continue;
401
799
  if (terminal && request && resultEvidence) {
402
- const reconciliation = terminalMatchesEvidence(manifest, request, existingTerminalResponse, resultEvidence, payload, payloadInvalid);
800
+ if (!terminalResult && request.family !== 'task-create')
801
+ continue;
802
+ const reconciliation = terminalMatchesEvidence(manifest, manifestPath, request, existingTerminalResponse, resultEvidence, payload, payloadInvalid, terminalResult);
403
803
  if (!reconciliation.valid)
404
804
  continue;
805
+ terminalReconciled = true;
405
806
  payloadReferenced = reconciliation.payloadReferenced;
807
+ if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
808
+ const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
809
+ if (view.state === 'unknown')
810
+ continue;
811
+ }
812
+ }
813
+ if (!terminalResult && !terminalReconciled) {
814
+ if (terminal)
815
+ continue;
816
+ if (!brokerOwns())
817
+ return false;
818
+ if (!resultEvidence || !request || payloadInvalid)
819
+ continue;
820
+ if (request.family === 'task-create' && resultEvidence.exitCode === 0 && payload === null) {
821
+ writeSandboxControlResponse(manifest, genericRecoveryResponse(request, resultEvidence.exitCode, null));
822
+ terminalReconciled = true;
823
+ terminal = true;
824
+ }
825
+ else {
826
+ writeSandboxControlResponse(manifest, unknown(entry.name));
827
+ continue;
828
+ }
406
829
  }
407
830
  if (!terminal) {
408
831
  if (!brokerOwns())
409
832
  return false;
410
833
  if (!resultEvidence || !request || payloadInvalid)
411
834
  continue;
412
- const recovered = recoveryResponse(manifest, request, resultEvidence, payload);
835
+ const recovered = recoveryResponse(manifest, manifestPath, request, resultEvidence, payload, terminalResult);
413
836
  if (!recovered)
414
837
  continue;
838
+ if (request.family === 'task-finalization' && resultEvidence.exitCode === 0) {
839
+ const view = publishFinalizationTaskView(manifest, broker, request.id, 'starting', null, null);
840
+ if (view.state === 'unknown')
841
+ continue;
842
+ }
415
843
  writeSandboxControlResponse(manifest, recovered);
844
+ const preserveRecoveryEvidence = recovered.error?.code === 'SANDBOX_CONTROL_RESULT_UNKNOWN';
845
+ if (transitionProtocolActive && !preserveRecoveryEvidence) {
846
+ writeSandboxControlTransition(manifest, { requestId: entry.name, phase: 'recovered' });
847
+ }
848
+ if (preserveRecoveryEvidence)
849
+ continue;
416
850
  payloadReferenced = Boolean(payload) && request.family !== 'task-finalization';
417
851
  }
418
852
  }
419
853
  else {
420
854
  if (!brokerOwns())
421
855
  return false;
856
+ if (transitionProtocolActive && startedCommitted) {
857
+ writeSandboxControlResponse(manifest, unknown(entry.name));
858
+ continue;
859
+ }
422
860
  writeSandboxControlResponse(manifest, notExecuted(entry.name));
423
861
  }
424
862
  if (!brokerOwns())
@@ -434,7 +872,13 @@ function delay(ms) {
434
872
  return new Promise((resolve) => setTimeout(resolve, ms));
435
873
  }
436
874
  export function sandboxControlSafeEnv(env = process.env) {
437
- return Object.fromEntries(Object.entries(env).filter(([key]) => !key.toUpperCase().startsWith('AGENT_INFRA_CONTROL_')));
875
+ return Object.fromEntries(Object.entries(env).filter(([key]) => {
876
+ const normalized = key.toUpperCase();
877
+ return !normalized.startsWith('AGENT_INFRA_CONTROL_')
878
+ && normalized !== 'AGENT_INFRA_TASK_ID'
879
+ && normalized !== 'AGENT_INFRA_RUNTIME_DIR'
880
+ && normalized !== 'AGENT_INFRA_EXECUTOR_MANIFEST';
881
+ }));
438
882
  }
439
883
  export async function serveSandboxControl(manifestPath, signal = new AbortController().signal, options = {}) {
440
884
  const timing = options.timing ?? DEFAULT_SANDBOX_CONTROL_TIMING;
@@ -443,6 +887,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
443
887
  const prepareExecution = options.prepareExecution ?? prepareSandboxControlExecution;
444
888
  const manifest = readSandboxControlManifest(manifestPath);
445
889
  const root = path.dirname(manifestPath);
890
+ assertCurrentSandboxControlIdentity(manifest, manifestPath);
446
891
  const startTime = getProcessStartTime(process.pid);
447
892
  if (!startTime)
448
893
  throw new Error('SANDBOX_CONTROL_BROKER_IDENTITY_UNAVAILABLE');
@@ -490,6 +935,28 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
490
935
  releaseStartup();
491
936
  }
492
937
  let active = null;
938
+ let startupReceipt = null;
939
+ if (manifest.taskId) {
940
+ try {
941
+ startupReceipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
942
+ }
943
+ catch {
944
+ // Preserve malformed receipt evidence as unknown rather than treating it as no receipt.
945
+ startupReceipt = {};
946
+ }
947
+ }
948
+ let taskView = taskViewForManifest({
949
+ ...manifest,
950
+ receipt: startupReceipt
951
+ });
952
+ try {
953
+ const previous = readSandboxControlStatus(manifest.publicStatusDir);
954
+ if (previous.generation === manifest.generation)
955
+ taskView = mergeSandboxTaskView(taskView, previous.taskView);
956
+ }
957
+ catch {
958
+ // A fresh control root has no previous task-view projection.
959
+ }
493
960
  let lastState = '';
494
961
  let lastStatusAt = 0;
495
962
  let nextBindingCheckAt = 0;
@@ -501,12 +968,36 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
501
968
  try {
502
969
  if (!brokerOwns())
503
970
  return;
504
- writeSandboxControlStatus(manifest, broker, 'starting', null, null);
971
+ if (taskView.state === 'current' && taskView.observedSource === 'completed') {
972
+ // Recheck durable re-entry evidence before publishing a trusted startup view.
973
+ const unverified = { ...taskView, state: 'unknown',
974
+ reasonCode: 'SANDBOX_COMPLETED_REENTRY_EVIDENCE_INVALID' };
975
+ try {
976
+ taskView = await completedReentryView(manifest, inspectContainer) ?? unverified;
977
+ }
978
+ catch {
979
+ taskView = unverified;
980
+ }
981
+ if (!brokerOwns())
982
+ return;
983
+ }
984
+ writeSandboxControlStatus(manifest, broker, 'starting', null, null, Date.now(), taskView);
505
985
  if (!brokerOwns())
506
986
  return;
507
- appendSandboxControlAudit(manifest, 'broker-start', { pid: broker.pid });
508
- if (!recoverProcessing(manifest, brokerOwns))
987
+ appendBrokerAudit(manifest, 'broker-start', {
988
+ pid: broker.pid,
989
+ brokerId: broker.brokerId
990
+ });
991
+ if (!recoverProcessing(manifest, manifestPath, broker, brokerOwns))
509
992
  return;
993
+ try {
994
+ const published = readSandboxControlStatus(manifest.publicStatusDir);
995
+ if (published.generation === manifest.generation)
996
+ taskView = published.taskView;
997
+ }
998
+ catch {
999
+ // The regular heartbeat will publish an invalid or missing projection as unknown.
1000
+ }
510
1001
  while (!signal.aborted) {
511
1002
  let settledExecution = null;
512
1003
  if (!brokerOwns())
@@ -514,6 +1005,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
514
1005
  let current;
515
1006
  try {
516
1007
  current = readSandboxControlManifest(manifestPath);
1008
+ assertCurrentSandboxControlIdentity(current, manifestPath);
517
1009
  }
518
1010
  catch {
519
1011
  break;
@@ -550,7 +1042,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
550
1042
  }
551
1043
  else if (observation.state === 'unknown') {
552
1044
  containerReasonCode = 'SANDBOX_CONTROL_CONTAINER_UNKNOWN';
553
- appendSandboxControlAudit(manifest, 'container-heartbeat-unknown', { reason: observation.reason });
1045
+ appendDiagnosticAudit(manifest, 'container-heartbeat-unknown', { reason: observation.reason });
554
1046
  const backoff = nextSandboxControlBackoff(containerBackoffMs, timing.slowCheckMs);
555
1047
  nextContainerHeartbeatAt = Date.now() + backoff.delayMs;
556
1048
  containerBackoffMs = backoff.nextDelayMs;
@@ -569,7 +1061,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
569
1061
  if (!brokerOwns())
570
1062
  break;
571
1063
  containerReasonCode = 'SANDBOX_CONTROL_CONTAINER_ABSENT_GC_FAILED';
572
- appendSandboxControlAudit(manifest, 'container-gc-failed', { reason: controlError(error).code });
1064
+ appendDiagnosticAudit(manifest, 'container-gc-failed', { reason: controlError(error).code });
573
1065
  const backoff = nextSandboxControlBackoff(containerBackoffMs, timing.slowCheckMs);
574
1066
  nextContainerHeartbeatAt = Date.now() + backoff.delayMs;
575
1067
  containerBackoffMs = backoff.nextDelayMs;
@@ -589,7 +1081,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
589
1081
  if (cleanupStaleSandboxControlLease(manifest)) {
590
1082
  if (!brokerOwns())
591
1083
  break;
592
- appendSandboxControlAudit(manifest, 'lease-stale-cleanup');
1084
+ appendBrokerAudit(manifest, 'lease-stale-cleanup');
593
1085
  }
594
1086
  if (readActiveLease(manifest))
595
1087
  reasonCode = 'SANDBOX_CONTROL_HANDOFF_ACTIVE';
@@ -612,14 +1104,25 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
612
1104
  }
613
1105
  reasonCode ??= containerReasonCode;
614
1106
  reasonCode ??= bindingReasonCode;
1107
+ if (!reasonCode && !active && taskView.state !== 'current') {
1108
+ try {
1109
+ const reentered = await completedReentryView(manifest, inspectContainer);
1110
+ if (reentered && brokerOwns() && !readActiveLease(manifest) && !bindingCheck(manifest)) {
1111
+ taskView = reentered;
1112
+ }
1113
+ }
1114
+ catch {
1115
+ // Invalid or changed re-entry evidence never clears the existing task view.
1116
+ }
1117
+ }
615
1118
  const state = reasonCode ? 'parked' : active ? 'busy' : 'healthy';
616
1119
  const stateKey = `${state}:${reasonCode ?? ''}:${active?.request.id ?? ''}`;
617
1120
  if (brokerOwns() && (stateKey !== lastState || now - lastStatusAt >= timing.controlTickMs)) {
618
- writeSandboxControlStatus(manifest, broker, state, reasonCode, active?.request.id ?? null, now);
1121
+ writeSandboxControlStatus(manifest, broker, state, reasonCode, active?.request.id ?? null, now, taskView);
619
1122
  lastStatusAt = now;
620
1123
  }
621
1124
  if (brokerOwns() && stateKey !== lastState) {
622
- appendSandboxControlAudit(manifest, 'broker-state', { state, reasonCode, requestId: active?.request.id ?? null });
1125
+ appendBrokerAudit(manifest, 'broker-state', { state, reasonCode, requestId: active?.request.id ?? null });
623
1126
  lastState = stateKey;
624
1127
  }
625
1128
  if (settledExecution) {
@@ -627,13 +1130,33 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
627
1130
  break;
628
1131
  let terminalCommitted = false;
629
1132
  if (settledExecution.result && settledExecution.resultEvidenceWritten) {
630
- terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, brokerOwns);
1133
+ if (!consumeLifecycleAuthorityForResult(manifest, settledExecution)) {
1134
+ active = settledExecution;
1135
+ continue;
1136
+ }
1137
+ terminalCommitted = publishExecutionResult(manifest, settledExecution.request, settledExecution.result, broker, brokerOwns);
1138
+ if (terminalCommitted && settledExecution.request.family === 'task-finalization') {
1139
+ try {
1140
+ const published = readSandboxControlStatus(manifest.publicStatusDir);
1141
+ if (published.generation === manifest.generation)
1142
+ taskView = published.taskView;
1143
+ }
1144
+ catch {
1145
+ taskView = { state: 'unknown', taskId: manifest.taskId, observedSource: 'unknown', receipt: null, reasonCode: 'SANDBOX_TASK_VIEW_STATUS_INVALID' };
1146
+ }
1147
+ }
631
1148
  }
632
1149
  else {
633
1150
  terminalCommitted = writeSandboxControlResponse(manifest, unknown(settledExecution.request.id));
634
1151
  }
635
1152
  if (!terminalCommitted)
636
1153
  return;
1154
+ appendBrokerAudit(manifest, 'executor-result-published', {
1155
+ ...requestAuditFields(manifest, settledExecution.request),
1156
+ resultAvailable: settledExecution.result !== null,
1157
+ resultEvidenceWritten: settledExecution.resultEvidenceWritten,
1158
+ ...(settledExecution.result ? resultAuditFields(settledExecution.result) : {})
1159
+ });
637
1160
  if (!brokerOwns())
638
1161
  break;
639
1162
  removeAcceptedResponse(manifest, settledExecution.request.id);
@@ -646,12 +1169,20 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
646
1169
  const id = name.slice(0, -5);
647
1170
  const source = path.join(requestsDir, name);
648
1171
  let claimed = null;
1172
+ let validatedRequest = null;
1173
+ let prepared = null;
649
1174
  try {
650
1175
  if (!brokerOwns()) {
651
1176
  retiring = true;
652
1177
  break;
653
1178
  }
654
1179
  claimed = claimRequest(manifest, source, id);
1180
+ appendBrokerAudit(manifest, 'request-claimed', {
1181
+ requestId: id,
1182
+ requestSourcePath: source,
1183
+ requestClaimedPath: claimed,
1184
+ requestProcessingDir: path.dirname(claimed)
1185
+ });
655
1186
  if (!brokerOwns()) {
656
1187
  retiring = true;
657
1188
  break;
@@ -666,30 +1197,98 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
666
1197
  if (active)
667
1198
  throw new Error('SANDBOX_CONTROL_BUSY');
668
1199
  const request = validateSandboxControlRequest(JSON.parse(fs.readFileSync(claimed, 'utf8')), manifest);
1200
+ validatedRequest = request;
1201
+ criticalRequestPhase(manifest, request, 'validated', 'in-progress');
1202
+ appendBrokerAudit(manifest, 'request-validated', {
1203
+ ...requestAuditFields(manifest, request),
1204
+ requestPath: claimed
1205
+ });
669
1206
  if (bindingCheck(manifest))
670
1207
  throw new Error('SANDBOX_WORKTREE_BINDING_LOST');
671
1208
  if (readActiveLease(manifest))
672
1209
  throw new Error('SANDBOX_CONTROL_HANDOFF_ACTIVE');
1210
+ criticalRequestPhase(manifest, request, 'gated', 'in-progress');
1211
+ appendBrokerAudit(manifest, 'request-gates-passed', {
1212
+ ...requestAuditFields(manifest, request),
1213
+ requestPath: claimed,
1214
+ bindingChecked: true,
1215
+ handoffLeaseChecked: true
1216
+ });
1217
+ criticalRequestPhase(manifest, request, 'reserved', 'in-progress');
673
1218
  writeSandboxControlReservation(manifest, request.id, sandboxControlGenerationUsage(manifest));
674
- const prepared = await prepareExecution({
1219
+ appendBrokerAudit(manifest, 'executor-reservation-written', {
1220
+ ...requestAuditFields(manifest, request),
1221
+ requestPath: claimed,
1222
+ executionPath: executionPath(manifest, request.id)
1223
+ });
1224
+ appendBrokerAudit(manifest, 'executor-prepare-start', {
1225
+ ...requestAuditFields(manifest, request),
1226
+ requestPath: claimed,
1227
+ executorCwd: manifest.repoRoot,
1228
+ executorEntry: options.internalCliPath ?? process.argv[1] ?? null
1229
+ });
1230
+ const lifecycleAuthority = lifecycleAuthorityForRequest(manifest, manifestPath, request);
1231
+ if (lifecycleAuthority?.status === 'rejected') {
1232
+ throw Object.assign(new Error(lifecycleAuthority.error?.message ?? 'lifecycle authority was rejected'), { code: lifecycleAuthority.error?.code ?? 'LIFECYCLE_AUTHORITY_REJECTED' });
1233
+ }
1234
+ prepared = await prepareExecution({
675
1235
  manifest, manifestPath, request, requestPath: claimed,
676
- internalCliPath: options.internalCliPath ?? process.argv[1]
1236
+ internalCliPath: options.internalCliPath ?? process.argv[1],
1237
+ ...(lifecycleAuthority ? { lifecycleAuthority } : {})
1238
+ });
1239
+ const preparedExecution = prepared;
1240
+ if (!preparedExecution)
1241
+ throw new Error('SANDBOX_CONTROL_EXECUTION_PREPARE_INVALID');
1242
+ criticalRequestPhase(manifest, request, 'prepared', 'in-progress', `${preparedExecution.execution.child.pid}`);
1243
+ appendBrokerAudit(manifest, 'executor-prepared', {
1244
+ ...requestAuditFields(manifest, request),
1245
+ requestPath: claimed,
1246
+ executorCwd: manifest.repoRoot,
1247
+ childPid: preparedExecution.execution.child.pid,
1248
+ childStartTime: preparedExecution.execution.child.startTime,
1249
+ childProcessGroupId: preparedExecution.execution.child.processGroupId,
1250
+ executionPath: executionPath(manifest, request.id)
677
1251
  });
678
1252
  const execution = {
679
- request, prepared, result: null, resultEvidenceWritten: false, failure: null, settled: false
1253
+ request, prepared: preparedExecution, lifecycleAuthority, result: null,
1254
+ resultEvidenceWritten: false, failure: null, settled: false
680
1255
  };
681
1256
  active = execution;
682
- prepared.completion.then((result) => {
1257
+ preparedExecution.completion.then((result) => {
683
1258
  execution.result = sanitizeSandboxControlResult(manifest, result);
1259
+ appendBrokerAudit(manifest, 'executor-completed', {
1260
+ ...requestAuditFields(manifest, request),
1261
+ ...resultAuditFields(execution.result),
1262
+ childPid: preparedExecution.execution.child.pid,
1263
+ childStartTime: preparedExecution.execution.child.startTime
1264
+ });
684
1265
  try {
685
1266
  writeSandboxControlResultEvidence(manifest, request.id, execution.result);
686
1267
  execution.resultEvidenceWritten = true;
687
1268
  execution.settled = true;
1269
+ appendBrokerAudit(manifest, 'executor-result-evidence-written', {
1270
+ ...requestAuditFields(manifest, request),
1271
+ resultPath: path.join(manifest.processingDir, request.id, 'result.json'),
1272
+ ...resultAuditFields(execution.result)
1273
+ });
688
1274
  }
689
1275
  catch (error) {
690
1276
  execution.failure = error;
1277
+ appendBrokerAudit(manifest, 'executor-result-evidence-failed', {
1278
+ ...requestAuditFields(manifest, request),
1279
+ errorType: error instanceof Error ? error.name : typeof error,
1280
+ });
691
1281
  }
692
- }, (error) => { execution.failure = error; execution.settled = true; });
1282
+ }, (error) => {
1283
+ execution.failure = error;
1284
+ execution.settled = true;
1285
+ appendBrokerAudit(manifest, 'executor-failed', {
1286
+ ...requestAuditFields(manifest, request),
1287
+ childPid: preparedExecution.execution.child.pid,
1288
+ childStartTime: preparedExecution.execution.child.startTime,
1289
+ errorType: error instanceof Error ? error.name : typeof error,
1290
+ });
1291
+ });
693
1292
  if (!brokerOwns()) {
694
1293
  prepared.terminate(false);
695
1294
  active = null;
@@ -702,9 +1301,16 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
702
1301
  retiring = true;
703
1302
  continue;
704
1303
  }
1304
+ criticalRequestPhase(manifest, request, 'accepted-authorized', 'in-progress');
705
1305
  writeAcceptedResponse(manifest, {
706
1306
  version: 2, id, phase: 'accepted', exitCode: null, stdout: '', stderr: '', error: null
707
1307
  });
1308
+ writeSandboxControlTransition(manifest, { requestId: request.id, phase: 'accepted-committed' });
1309
+ appendBrokerAudit(manifest, 'request-accepted', {
1310
+ ...requestAuditFields(manifest, request),
1311
+ acceptedPath: acceptedResponsePath(manifest, id),
1312
+ executionPhase: 'prepared'
1313
+ });
708
1314
  if (!brokerOwns()) {
709
1315
  prepared.terminate(false);
710
1316
  active = null;
@@ -712,9 +1318,20 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
712
1318
  break;
713
1319
  }
714
1320
  try {
1321
+ criticalRequestPhase(manifest, request, 'start-authorized', 'in-progress', `${prepared.execution.child.pid}`);
1322
+ appendBrokerAudit(manifest, 'executor-start', {
1323
+ ...requestAuditFields(manifest, request),
1324
+ childPid: prepared.execution.child.pid,
1325
+ childStartTime: prepared.execution.child.startTime,
1326
+ executionPath: executionPath(manifest, request.id)
1327
+ });
715
1328
  prepared.start(brokerOwns);
716
1329
  }
717
1330
  catch (error) {
1331
+ appendBrokerAudit(manifest, 'executor-start-failed', {
1332
+ ...requestAuditFields(manifest, request),
1333
+ errorType: error instanceof Error ? error.name : typeof error,
1334
+ });
718
1335
  const owned = brokerOwns();
719
1336
  prepared.terminate(owned);
720
1337
  if (!owned || !brokerOwns()) {
@@ -736,20 +1353,39 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
736
1353
  retiring = true;
737
1354
  break;
738
1355
  }
739
- appendSandboxControlAudit(manifest, 'executor-gate-failed', { requestId: id });
1356
+ appendBrokerAudit(manifest, 'executor-gate-failed', { requestId: id });
740
1357
  continue;
741
1358
  }
742
1359
  }
743
1360
  catch (error) {
1361
+ if (prepared) {
1362
+ try {
1363
+ prepared.terminate(brokerOwns());
1364
+ }
1365
+ catch { /* cleanup is best effort after a failed admission */ }
1366
+ prepared = null;
1367
+ active = null;
1368
+ }
744
1369
  if (!brokerOwns()) {
745
1370
  retiring = true;
746
1371
  break;
747
1372
  }
1373
+ const detail = controlError(error);
1374
+ appendBrokerAudit(manifest, 'request-rejected', {
1375
+ requestId: id,
1376
+ requestFamily: validatedRequest?.family ?? null,
1377
+ sandboxTaskId: manifest.taskId,
1378
+ requestPath: claimed,
1379
+ errorCode: detail.code,
1380
+ errorRetryable: detail.retryable,
1381
+ errorType: error instanceof Error ? error.name : typeof error,
1382
+ });
748
1383
  writeSandboxControlResponse(manifest, rejected(id, error));
749
1384
  if (!brokerOwns()) {
750
1385
  retiring = true;
751
1386
  break;
752
1387
  }
1388
+ removeAcceptedResponse(manifest, id);
753
1389
  if (claimed)
754
1390
  fs.rmSync(path.dirname(claimed), { recursive: true, force: true });
755
1391
  }
@@ -775,7 +1411,8 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
775
1411
  }
776
1412
  }
777
1413
  if (owned && active.result && active.resultEvidenceWritten) {
778
- if (publishExecutionResult(manifest, active.request, active.result, brokerOwns)) {
1414
+ if (consumeLifecycleAuthorityForResult(manifest, active)
1415
+ && publishExecutionResult(manifest, active.request, active.result, broker, brokerOwns)) {
779
1416
  if (brokerOwns()) {
780
1417
  removeAcceptedResponse(manifest, active.request.id);
781
1418
  fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
@@ -787,7 +1424,11 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
787
1424
  const terminationConfirmed = active.prepared.terminate(owned);
788
1425
  if (owned && brokerOwns() && active.request.family === 'task-finalization') {
789
1426
  const recovered = finalizationRecoveryResponse(manifest, active.request.id, 0);
790
- if (recovered.status === 'matched' && recovered.response && writeSandboxControlResponse(manifest, recovered.response)) {
1427
+ const view = recovered.status === 'matched'
1428
+ ? publishFinalizationTaskView(manifest, broker, active.request.id, 'healthy', null, null)
1429
+ : null;
1430
+ if (view?.state !== 'unknown' && recovered.status === 'matched' && recovered.response
1431
+ && writeSandboxControlResponse(manifest, recovered.response)) {
791
1432
  if (terminationConfirmed && brokerOwns()) {
792
1433
  removeAcceptedResponse(manifest, active.request.id);
793
1434
  fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
@@ -801,7 +1442,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
801
1442
  }
802
1443
  }
803
1444
  if (brokerOwns()) {
804
- appendSandboxControlAudit(manifest, 'broker-stop', { pid: broker.pid, brokerId: broker.brokerId });
1445
+ appendDiagnosticAudit(manifest, 'broker-stop', { pid: broker.pid, brokerId: broker.brokerId });
805
1446
  try {
806
1447
  if (fs.readFileSync(brokerPath, 'utf8') === brokerRecord)
807
1448
  fs.unlinkSync(brokerPath);