@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
@@ -16,6 +16,7 @@ import { fetchSandboxRows, selectSandboxContainer } from './list-running.ts';
16
16
  import { resolveTools, toolConfigDirCandidates } from '../tools.ts';
17
17
  import { detectEngine } from '../engine.ts';
18
18
  import { resolveSandboxTarget } from '../workspace-identity.ts';
19
+ import { readSandboxControlStatusForRow } from './control-status.ts';
19
20
 
20
21
  const USAGE = `Usage: ai sandbox show <branch | TASK-id | N>
21
22
 
@@ -93,6 +94,21 @@ export function show(args: string[] = []): void {
93
94
  process.stdout.write(` Mode: ${container?.workspaceMode ?? 'legacy-invalid'}\n`);
94
95
  process.stdout.write(` Task: ${container?.taskId ?? '-'}\n`);
95
96
 
97
+ p.log.step('Control status');
98
+ const controlStatus = container
99
+ ? readSandboxControlStatusForRow(config, container)
100
+ : null;
101
+ if (!controlStatus) {
102
+ p.log.warn(' Control status unavailable');
103
+ } else {
104
+ process.stdout.write(` Health: ${controlStatus.state}\n`);
105
+ process.stdout.write(` Task view: ${controlStatus.taskView.state}\n`);
106
+ process.stdout.write(` Task source: ${controlStatus.taskView.observedSource ?? '-'}\n`);
107
+ if (controlStatus.taskView.reasonCode) {
108
+ process.stdout.write(` Task view reason: ${controlStatus.taskView.reasonCode}\n`);
109
+ }
110
+ }
111
+
96
112
  for (const tool of detail.toolStates) {
97
113
  p.log.step(`${tool.name} state`);
98
114
  if (tool.entries.length === 0) {
@@ -0,0 +1,253 @@
1
+ import { createHash } from 'node:crypto';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import type { SandboxControlManifest, SandboxControlFamily, SandboxControlRequest } from './protocol.ts';
5
+ import { identityDigest as digestIdentity } from './identity-sentinel.ts';
6
+ import { acquireSandboxResourceLock } from './native-file-lock.ts';
7
+ import { fsyncDirectory, writeDurableFile } from '../../fs/durable-write.ts';
8
+
9
+ export const SANDBOX_CONTROL_AUDIT_PHASES = Object.freeze([
10
+ 'validated', 'gated', 'reserved', 'prepared', 'accepted-authorized', 'accepted-committed',
11
+ 'start-authorized', 'started-committed', 'completed', 'evidence-written',
12
+ 'publish-authorized', 'published-committed', 'rejected', 'recovered'
13
+ ] as const);
14
+ export type SandboxControlAuditPhase = typeof SANDBOX_CONTROL_AUDIT_PHASES[number];
15
+ export type SandboxControlAuditOutcome = 'not-executed' | 'in-progress' | 'success' | 'failure' | 'unknown' | 'rejected';
16
+
17
+ export type SandboxControlAuditContext = Readonly<{
18
+ requestId: string;
19
+ taskId: string | null;
20
+ family: SandboxControlFamily | null;
21
+ operation: string | null;
22
+ phase: SandboxControlAuditPhase;
23
+ outcome: SandboxControlAuditOutcome;
24
+ generation: string;
25
+ identityDigest: string;
26
+ }>;
27
+
28
+ export type SandboxControlTransition = Readonly<{
29
+ version: 1;
30
+ requestId: string;
31
+ generation: string;
32
+ phase: SandboxControlAuditPhase;
33
+ at: number;
34
+ identityDigest: string;
35
+ reference: string | null;
36
+ }>;
37
+
38
+ const REQUEST_ID = /^[a-f0-9-]{16,64}$/u;
39
+ const SAFE_OPERATION = /^[a-z][a-z0-9.-]{0,96}$/u;
40
+ const AUDIT_MAX_BYTES = 1024 * 1024;
41
+
42
+ export function requestAuditFields(manifest: SandboxControlManifest, request: SandboxControlRequest) {
43
+ return {
44
+ requestId: request.id, requestFamily: request.family, sandboxTaskId: manifest.taskId,
45
+ requestGeneration: request.generation, requestIssuedAt: request.issuedAt, requestExpiresAt: request.expiresAt
46
+ };
47
+ }
48
+
49
+ export function resultAuditFields(result: Readonly<{ exitCode: number; stdout: string; stderr: string }>) {
50
+ return {
51
+ exitCode: result.exitCode,
52
+ outputBytes: Buffer.byteLength(result.stdout, 'utf8'), errorBytes: Buffer.byteLength(result.stderr, 'utf8'),
53
+ outputDigest: createHash('sha256').update(result.stdout, 'utf8').digest('hex'),
54
+ errorDigest: createHash('sha256').update(result.stderr, 'utf8').digest('hex')
55
+ };
56
+ }
57
+
58
+ function auditRoot(manifest: SandboxControlManifest): string {
59
+ return path.dirname(path.resolve(manifest.publicStatusDir));
60
+ }
61
+
62
+ function auditPath(manifest: SandboxControlManifest): string {
63
+ return path.join(auditRoot(manifest), 'audit.ndjson');
64
+ }
65
+
66
+ function manifestIdentityDigest(manifest: SandboxControlManifest): string {
67
+ return digestIdentity({
68
+ version: 1,
69
+ mode: manifest.mode,
70
+ taskId: manifest.taskId,
71
+ generation: manifest.generation,
72
+ controlRootId: manifest.controlRootId
73
+ });
74
+ }
75
+
76
+ export function createSandboxControlAuditContext(
77
+ manifest: SandboxControlManifest,
78
+ params: Readonly<{
79
+ requestId: string;
80
+ family: SandboxControlFamily | null;
81
+ operation?: string | null;
82
+ phase: SandboxControlAuditPhase;
83
+ outcome: SandboxControlAuditOutcome;
84
+ }>
85
+ ): SandboxControlAuditContext {
86
+ if (!REQUEST_ID.test(params.requestId)) throw new Error('SANDBOX_CONTROL_AUDIT_REQUEST_INVALID');
87
+ if (params.operation !== null && params.operation !== undefined && !SAFE_OPERATION.test(params.operation)) {
88
+ throw new Error('SANDBOX_CONTROL_AUDIT_OPERATION_INVALID');
89
+ }
90
+ if (!SANDBOX_CONTROL_AUDIT_PHASES.includes(params.phase)) throw new Error('SANDBOX_CONTROL_AUDIT_PHASE_INVALID');
91
+ return {
92
+ requestId: params.requestId,
93
+ taskId: manifest.taskId,
94
+ family: params.family,
95
+ operation: params.operation ?? null,
96
+ phase: params.phase,
97
+ outcome: params.outcome,
98
+ generation: manifest.generation,
99
+ identityDigest: manifestIdentityDigest(manifest)
100
+ };
101
+ }
102
+
103
+ function safeFields(fields: Readonly<Record<string, string | number | boolean | null>>): Record<string, string | number | boolean | null> {
104
+ const result: Record<string, string | number | boolean | null> = {};
105
+ for (const [key, value] of Object.entries(fields)) {
106
+ if (!/^[a-z][a-zA-Z0-9]{0,63}$/u.test(key)) continue;
107
+ if (/(token|secret|credential|proof|password|path|root|cwd|stdout|stderr|args|command|taskref)/iu.test(key)) continue;
108
+ if (typeof value === 'string' && value.length > 256) continue;
109
+ result[key] = value;
110
+ }
111
+ return result;
112
+ }
113
+
114
+ function requestHasTerminalTransition(directory: string): boolean {
115
+ const transitions = path.join(directory, 'transitions');
116
+ if (!fs.existsSync(transitions)) return false;
117
+ return ['published-committed', 'recovered'].some((phase) => fs.existsSync(path.join(transitions, `${phase}.json`)));
118
+ }
119
+
120
+ function hasActiveRequest(manifest: SandboxControlManifest): boolean {
121
+ if (!fs.existsSync(manifest.processingDir)) return false;
122
+ return fs.readdirSync(manifest.processingDir, { withFileTypes: true })
123
+ .some((entry) => entry.isDirectory() && REQUEST_ID.test(entry.name)
124
+ && !requestHasTerminalTransition(path.join(manifest.processingDir, entry.name)));
125
+ }
126
+
127
+ function rotateAuditUnderLock(manifest: SandboxControlManifest, filePath: string): void {
128
+ if (!fs.existsSync(filePath) || fs.statSync(filePath).size < AUDIT_MAX_BYTES) return;
129
+ if (hasActiveRequest(manifest)) return;
130
+ fs.rmSync(`${filePath}.1`, { force: true });
131
+ fs.renameSync(filePath, `${filePath}.1`);
132
+ fsyncDirectory(auditRoot(manifest));
133
+ const descriptor = fs.openSync(filePath, 'a', 0o600);
134
+ try {
135
+ fs.fsyncSync(descriptor);
136
+ } finally {
137
+ fs.closeSync(descriptor);
138
+ }
139
+ }
140
+
141
+ function appendAuditRecord(
142
+ manifest: SandboxControlManifest,
143
+ record: Readonly<Record<string, unknown>>,
144
+ critical: boolean
145
+ ): void {
146
+ const root = auditRoot(manifest);
147
+ fs.mkdirSync(root, { recursive: true, mode: 0o700 });
148
+ const lock = acquireSandboxResourceLock(`sandbox-control-audit:${root}`);
149
+ try {
150
+ const filePath = auditPath(manifest);
151
+ rotateAuditUnderLock(manifest, filePath);
152
+ const descriptor = fs.openSync(filePath, 'a', 0o600);
153
+ try {
154
+ fs.writeSync(descriptor, `${JSON.stringify(record)}\n`, undefined, 'utf8');
155
+ if (critical) fs.fsyncSync(descriptor);
156
+ } finally {
157
+ fs.closeSync(descriptor);
158
+ }
159
+ } catch (error) {
160
+ if (critical) throw new Error(`SANDBOX_CONTROL_AUDIT_UNAVAILABLE: ${error instanceof Error ? error.message : String(error)}`);
161
+ } finally {
162
+ lock.release();
163
+ }
164
+ }
165
+
166
+ export function appendCriticalAudit(
167
+ manifest: SandboxControlManifest,
168
+ context: SandboxControlAuditContext,
169
+ fields: Readonly<Record<string, string | number | boolean | null>> = {},
170
+ now = Date.now()
171
+ ): void {
172
+ appendAuditRecord(manifest, {
173
+ version: 2,
174
+ at: now,
175
+ event: context.phase,
176
+ ...context,
177
+ ...safeFields(fields)
178
+ }, true);
179
+ }
180
+
181
+ export function appendDiagnosticAudit(
182
+ manifest: SandboxControlManifest,
183
+ event: string,
184
+ fields: Readonly<Record<string, string | number | boolean | null>> = {},
185
+ now = Date.now()
186
+ ): void {
187
+ try {
188
+ appendAuditRecord(manifest, {
189
+ version: 2,
190
+ at: now,
191
+ event,
192
+ generation: manifest.generation,
193
+ identityDigest: manifestIdentityDigest(manifest),
194
+ ...safeFields(fields)
195
+ }, false);
196
+ } catch {
197
+ // Diagnostics are deliberately best effort.
198
+ }
199
+ }
200
+
201
+ function transitionPath(manifest: SandboxControlManifest, requestId: string, phase: SandboxControlAuditPhase): string {
202
+ if (!REQUEST_ID.test(requestId)) throw new Error('SANDBOX_CONTROL_TRANSITION_REQUEST_INVALID');
203
+ return path.join(manifest.processingDir, requestId, 'transitions', `${phase}.json`);
204
+ }
205
+
206
+ export function writeSandboxControlTransition(
207
+ manifest: SandboxControlManifest,
208
+ params: Readonly<{
209
+ requestId: string;
210
+ phase: SandboxControlAuditPhase;
211
+ reference?: string | null;
212
+ now?: number;
213
+ }>
214
+ ): SandboxControlTransition {
215
+ const transition: SandboxControlTransition = {
216
+ version: 1,
217
+ requestId: params.requestId,
218
+ generation: manifest.generation,
219
+ phase: params.phase,
220
+ at: params.now ?? Date.now(),
221
+ identityDigest: manifestIdentityDigest(manifest),
222
+ reference: params.reference ?? null
223
+ };
224
+ const target = transitionPath(manifest, params.requestId, params.phase);
225
+ fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });
226
+ try {
227
+ writeDurableFile(target, `${JSON.stringify(transition)}\n`, { mode: 0o600, replace: false });
228
+ } catch (error) {
229
+ if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
230
+ const existing = readSandboxControlTransition(manifest, params.requestId, params.phase);
231
+ if (JSON.stringify(existing) !== JSON.stringify(transition)) throw new Error('SANDBOX_CONTROL_TRANSITION_CONFLICT');
232
+ }
233
+ return transition;
234
+ }
235
+
236
+ export function readSandboxControlTransition(
237
+ manifest: SandboxControlManifest,
238
+ requestId: string,
239
+ phase: SandboxControlAuditPhase
240
+ ): SandboxControlTransition {
241
+ const filePath = transitionPath(manifest, requestId, phase);
242
+ if (!fs.existsSync(filePath)) throw new Error('SANDBOX_CONTROL_TRANSITION_MISSING');
243
+ let value: unknown;
244
+ try { value = JSON.parse(fs.readFileSync(filePath, 'utf8')); } catch { throw new Error('SANDBOX_CONTROL_TRANSITION_INVALID'); }
245
+ const candidate = value as Partial<SandboxControlTransition> | null;
246
+ if (!candidate || candidate.version !== 1 || candidate.requestId !== requestId
247
+ || candidate.generation !== manifest.generation || candidate.phase !== phase
248
+ || !Number.isSafeInteger(candidate.at) || typeof candidate.identityDigest !== 'string'
249
+ || (candidate.reference !== null && typeof candidate.reference !== 'string')) {
250
+ throw new Error('SANDBOX_CONTROL_TRANSITION_INVALID');
251
+ }
252
+ return candidate as SandboxControlTransition;
253
+ }
@@ -1,4 +1,5 @@
1
1
  import fs from 'node:fs';
2
+ import { hasTaskBoundMarker } from '../../internal/task-operation-registry.ts';
2
3
  import path from 'node:path';
3
4
  import { randomUUID } from 'node:crypto';
4
5
  import {
@@ -12,7 +13,8 @@ import {
12
13
  type SandboxTaskCreateRequest,
13
14
  type SandboxTaskCommandRequest,
14
15
  type SandboxTaskFinalizationRequest,
15
- type SandboxCodexControllerRequest
16
+ type SandboxCodexControllerRequest,
17
+ type SandboxTaskWorkflowRequest
16
18
  } from './protocol.ts';
17
19
  import type {
18
20
  CodexControllerLeaseProofV1,
@@ -22,6 +24,11 @@ import type { ProcessIdentity } from '../../server/process-state.ts';
22
24
  import { normalizeAgentToken } from '../../agent-clients/tokens.ts';
23
25
  import { readSandboxControlPayload, readSandboxControlStatus } from './state.ts';
24
26
  import type { TaskCreateCandidateV1 } from '../../task/create.ts';
27
+ import { accessSandboxTaskView, taskViewFromStatus, type TaskViewAccessEffect } from './task-view.ts';
28
+ import { readSandboxControlIdentitySentinel } from './identity-sentinel.ts';
29
+ import type { TaskWorkflowRequest } from './task-workflow.ts';
30
+ import type { LifecycleAuthorityRequestV1 } from '../../task/control-authority.ts';
31
+ import { configuredShortIdLength, resolveShortIdReadOnly } from '../../task/short-id.ts';
25
32
 
26
33
  const SANDBOX_CONTROL_RESPONSE_SETTLE_MS = 250;
27
34
 
@@ -52,7 +59,30 @@ function clientError(
52
59
  throw new SandboxControlClientError({ code, message: `${code}: ${message}`, retryable }, accepted, requestId);
53
60
  }
54
61
 
55
- function preflight(statusDir: string, generation: string, now = Date.now()): void {
62
+ function preflight(
63
+ statusDir: string,
64
+ generation: string,
65
+ taskViewEffect: TaskViewAccessEffect | null = null,
66
+ now = Date.now(),
67
+ env: NodeJS.ProcessEnv = process.env
68
+ ): void {
69
+ if (fs.existsSync(statusDir)) {
70
+ let identity;
71
+ try {
72
+ identity = readSandboxControlIdentitySentinel(statusDir);
73
+ } catch (error) {
74
+ const code = error instanceof Error && error.message.endsWith('MISSING')
75
+ ? 'SANDBOX_CONTROL_IDENTITY_MISSING'
76
+ : 'SANDBOX_CONTROL_IDENTITY_MALFORMED';
77
+ clientError(code, 'sandbox control identity is missing or invalid', false);
78
+ }
79
+ if (identity.generation !== generation) {
80
+ clientError('SANDBOX_CONTROL_IDENTITY_GENERATION_MISMATCH', 'sandbox control identity generation does not match the request', false);
81
+ }
82
+ if (env.AGENT_INFRA_CONTROL_ROOT_ID && identity.controlRootId !== env.AGENT_INFRA_CONTROL_ROOT_ID) {
83
+ clientError('SANDBOX_CONTROL_IDENTITY_ROOT_ID_MISMATCH', 'sandbox control identity root does not match the client configuration', false);
84
+ }
85
+ }
56
86
  let status;
57
87
  try {
58
88
  status = readSandboxControlStatus(statusDir);
@@ -71,9 +101,30 @@ function preflight(statusDir: string, generation: string, now = Date.now()): voi
71
101
  clientError(code, 'broker is parked until the host restores a valid binding', true);
72
102
  }
73
103
  if (status.state !== 'healthy') clientError('SANDBOX_CONTROL_BROKER_UNAVAILABLE', 'broker is not ready', true);
104
+ if (taskViewEffect && hasTaskBoundMarker(env)) {
105
+ const view = taskViewFromStatus(status);
106
+ if (view.taskId !== env.AGENT_INFRA_TASK_ID) {
107
+ clientError('SANDBOX_TASK_VIEW_TASK_ID_MISMATCH', 'task view task id does not match the sandbox task marker', false);
108
+ }
109
+ const access = accessSandboxTaskView(view, taskViewEffect);
110
+ if (!access.allowed) {
111
+ clientError(
112
+ access.reasonCode ?? 'SANDBOX_TASK_VIEW_DENIED',
113
+ access.message ?? 'task view denied',
114
+ false
115
+ );
116
+ }
117
+ }
118
+ }
119
+
120
+ function taskViewEffectForRequest(request: SandboxControlRequest): TaskViewAccessEffect | null {
121
+ if (request.family === 'task-lifecycle' || request.family === 'task-finalization') return 'progress';
122
+ if (request.family === 'task-orchestration') return request.args[1] === 'status' ? 'diagnostic' : 'progress';
123
+ if (request.family === 'task-workflow') return request.workflow.operation === 'artifact-inspect' || request.workflow.operation === 'decision-next-id' ? 'diagnostic' : 'progress';
124
+ return null;
74
125
  }
75
126
 
76
- function parseResponse(raw: string, id: string): SandboxControlResponse {
127
+ function parseResponse(raw: string, id: string, accepted = false): SandboxControlResponse {
77
128
  const response = JSON.parse(raw) as SandboxControlResponse;
78
129
  if (response.version !== 2 || response.id !== id
79
130
  || !['completed', 'rejected'].includes(response.phase)
@@ -81,13 +132,13 @@ function parseResponse(raw: string, id: string): SandboxControlResponse {
81
132
  || (response.outputState !== undefined && response.outputState !== 'available' && response.outputState !== 'unavailable')
82
133
  || (response.outputState === 'available' && !response.payload)
83
134
  || (response.outputState === 'unavailable' && response.payload !== null && response.payload !== undefined)) {
84
- clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response is invalid', false, false, id);
135
+ clientError('SANDBOX_CONTROL_RESPONSE_INVALID', 'broker response is invalid', false, accepted, id);
85
136
  }
86
137
  return response;
87
138
  }
88
139
 
89
- function readPublishedResponse(raw: string, id: string, channelDir: string): SandboxControlResponse {
90
- const response = parseResponse(raw, id);
140
+ function readPublishedResponse(raw: string, id: string, channelDir: string, accepted = false): SandboxControlResponse {
141
+ const response = parseResponse(raw, id, accepted);
91
142
  if (response.outputState !== 'available') return response;
92
143
  const filePath = path.join(channelDir, 'responses', `${id}.payload.json`);
93
144
  let payload;
@@ -115,6 +166,44 @@ function cancelPendingRequest(requestPath: string): boolean {
115
166
  }
116
167
  }
117
168
 
169
+ const CONTROL_TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
170
+ const CONTROL_SHORT_ID_RE = /^\d+$/;
171
+
172
+ type CanonicalRequestTask =
173
+ | Readonly<{ kind: 'none' }>
174
+ | Readonly<{ kind: 'task'; taskId: string }>
175
+ | Readonly<{ kind: 'unresolved' }>;
176
+
177
+ // Use the current short-id contract for the first visible active registry.
178
+ export function resolveVisibleActiveShortId(ref: string): string | null {
179
+ let dir = process.cwd();
180
+ for (let depth = 0; depth < 64; depth += 1) {
181
+ const registryPath = path.join(dir, '.agents', 'workspace', 'active', '.short-ids.json');
182
+ if (fs.existsSync(registryPath)) {
183
+ const resolved = resolveShortIdReadOnly(ref, dir, { shortIdLength: configuredShortIdLength(dir) });
184
+ return resolved.ok ? resolved.taskId : null;
185
+ }
186
+ const parent = path.dirname(dir);
187
+ if (parent === dir) break;
188
+ dir = parent;
189
+ }
190
+ return null;
191
+ }
192
+
193
+ // A request's leading argument is only a task reference for some families and
194
+ // may be a documented short id. Compare canonical ids so a bound sandbox keeps
195
+ // refusing other tasks without rejecting its own task's short id.
196
+ function canonicalRequestTask(request: SandboxControlRequest): CanonicalRequestTask {
197
+ const ref = request.family === 'task-workflow'
198
+ ? request.workflow.taskId
199
+ : 'args' in request ? request.args[0] ?? null : null;
200
+ if (!ref) return { kind: 'none' };
201
+ if (CONTROL_TASK_ID_RE.test(ref)) return { kind: 'task', taskId: ref };
202
+ if (!CONTROL_SHORT_ID_RE.test(ref)) return { kind: 'none' };
203
+ const resolved = resolveVisibleActiveShortId(ref);
204
+ return resolved ? { kind: 'task', taskId: resolved } : { kind: 'unresolved' };
205
+ }
206
+
118
207
  function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly<{
119
208
  channelDir?: string;
120
209
  statusDir?: string;
@@ -122,7 +211,22 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
122
211
  }>): SandboxControlResponse {
123
212
  const channelDir = params.channelDir ?? process.env.AGENT_INFRA_CONTROL_DIR ?? '/run/agent-infra/control';
124
213
  const statusDir = params.statusDir ?? process.env.AGENT_INFRA_CONTROL_STATUS_DIR ?? '/run/agent-infra/control-status';
125
- preflight(statusDir, request.generation);
214
+ const taskViewEffect = taskViewEffectForRequest(request);
215
+ preflight(statusDir, request.generation, taskViewEffect);
216
+ const identityPath = path.join(statusDir, 'identity.json');
217
+ if (fs.existsSync(statusDir)) {
218
+ const identity = readSandboxControlIdentitySentinel(statusDir);
219
+ const requestTask = canonicalRequestTask(request);
220
+ if (identity.mode === 'task-bound'
221
+ && ((process.env.AGENT_INFRA_TASK_ID && process.env.AGENT_INFRA_TASK_ID !== identity.taskId)
222
+ || requestTask.kind === 'unresolved'
223
+ || (requestTask.kind === 'task' && requestTask.taskId !== identity.taskId))) {
224
+ clientError('SANDBOX_CONTROL_IDENTITY_TOPOLOGY_MISMATCH', 'request task does not match the sandbox identity', false);
225
+ }
226
+ if (identity.mode === 'branch-only' && (request.family === 'task-finalization' || request.family === 'task-workflow')) {
227
+ clientError('SANDBOX_CONTROL_BRANCH_ONLY', 'branch-only sandboxes cannot finalize tasks', false);
228
+ }
229
+ }
126
230
  const encoded = `${JSON.stringify(request)}\n`;
127
231
  if (Buffer.byteLength(encoded, 'utf8') > SANDBOX_CONTROL_MAX_BYTES) {
128
232
  clientError('SANDBOX_CONTROL_REQUEST_TOO_LARGE', 'request exceeds the control limit', false, false, request.id);
@@ -154,7 +258,9 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
154
258
  }
155
259
  let response: SandboxControlResponse;
156
260
  try {
157
- response = readPublishedResponse(raw, request.id, channelDir);
261
+ // The request-specific terminal proves the broker claimed the request; preserve recovery evidence if marker cleanup won the race.
262
+ accepted = true;
263
+ response = readPublishedResponse(raw, request.id, channelDir, accepted);
158
264
  malformedResponseRaw = null;
159
265
  } catch (error) {
160
266
  if (!(error instanceof SyntaxError)) throw error;
@@ -176,7 +282,7 @@ function exchangeSandboxControl(request: SandboxControlRequest, params: Readonly
176
282
  }
177
283
  if (!accepted) {
178
284
  try {
179
- preflight(statusDir, request.generation);
285
+ preflight(statusDir, request.generation, taskViewEffect);
180
286
  } catch (error) {
181
287
  if (cancelPendingRequest(requestPath)) throw error;
182
288
  clientError(
@@ -236,7 +342,9 @@ export function recoverSandboxControl(requestId: string, params: Readonly<{
236
342
  }
237
343
  let response: SandboxControlResponse;
238
344
  try {
239
- response = readPublishedResponse(raw, requestId, channelDir);
345
+ // The request-specific terminal proves the broker claimed the request; preserve recovery evidence if marker cleanup won the race.
346
+ accepted = true;
347
+ response = readPublishedResponse(raw, requestId, channelDir, accepted);
240
348
  malformedResponseRaw = null;
241
349
  } catch (error) {
242
350
  if (!(error instanceof SyntaxError)) throw error;
@@ -276,7 +384,7 @@ export function requestSandboxControl(params: Readonly<{
276
384
  token?: string; generation?: string; timeoutMs?: number;
277
385
  }>): SandboxControlResponse {
278
386
  if (!isSandboxControlFamily(params.family)) clientError('SANDBOX_CONTROL_COMMAND_DENIED', `'${params.family}' is not allowed`, false);
279
- if (params.family === 'task-create' || params.family === 'codex-controller' || params.family === 'task-finalization') {
387
+ if (params.family === 'task-create' || params.family === 'codex-controller' || params.family === 'task-finalization' || params.family === 'task-workflow') {
280
388
  clientError('SANDBOX_CONTROL_COMMAND_DENIED', `'${params.family}' requires a typed request`, false);
281
389
  }
282
390
  const auth = authority(params);
@@ -295,6 +403,7 @@ export function requestSandboxTaskControl(params: Readonly<{
295
403
  family: 'task-lifecycle' | 'task-orchestration';
296
404
  args: string[];
297
405
  controllerProof: CodexControllerLeaseProofV1 | null;
406
+ authority?: LifecycleAuthorityRequestV1;
298
407
  channelDir?: string;
299
408
  statusDir?: string;
300
409
  token?: string;
@@ -305,14 +414,15 @@ export function requestSandboxTaskControl(params: Readonly<{
305
414
  const issuedAt = Date.now();
306
415
  const request: SandboxTaskCommandRequest = {
307
416
  version: 3,
308
- id: randomUUID(),
417
+ id: params.authority?.requestId ?? randomUUID(),
309
418
  ...auth,
310
419
  issuedAt,
311
420
  expiresAt: issuedAt + SANDBOX_CONTROL_ADMISSION_WINDOW_MS,
312
421
  family: params.family,
313
422
  args: params.args,
314
423
  controllerProcess: null,
315
- controllerProof: params.controllerProof
424
+ controllerProof: params.authority ? null : params.controllerProof,
425
+ ...(params.authority ? { authority: params.authority } : {})
316
426
  };
317
427
  return exchangeSandboxControl(request, params);
318
428
  }
@@ -345,6 +455,33 @@ export function requestSandboxTaskFinalization(params: Readonly<{
345
455
  return exchangeSandboxControl(request, params);
346
456
  }
347
457
 
458
+ export function requestSandboxTaskWorkflow(params: Readonly<{
459
+ workflow: TaskWorkflowRequest;
460
+ channelDir?: string;
461
+ statusDir?: string;
462
+ token?: string;
463
+ generation?: string;
464
+ timeoutMs?: number;
465
+ authority?: LifecycleAuthorityRequestV1;
466
+ }>): SandboxControlResponse {
467
+ const auth = authority(params);
468
+ const issuedAt = Date.now();
469
+ const request: SandboxTaskWorkflowRequest = {
470
+ version: 3,
471
+ id: params.workflow.id,
472
+ ...auth,
473
+ issuedAt,
474
+ expiresAt: issuedAt + SANDBOX_CONTROL_ADMISSION_WINDOW_MS,
475
+ family: 'task-workflow',
476
+ args: [],
477
+ controllerProcess: null,
478
+ controllerProof: null,
479
+ workflow: params.workflow,
480
+ ...(params.authority ? { authority: params.authority } : {})
481
+ };
482
+ return exchangeSandboxControl(request, params);
483
+ }
484
+
348
485
  type CodexControllerClosed = Readonly<{
349
486
  version: 1;
350
487
  status: 'closed';
@@ -0,0 +1,98 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { resolveTaskWorkspace } from '../task-resolver.ts';
4
+ import { assertGitWorktreeBinding } from '../../git/worktree-identity.ts';
5
+ import { parseTypedTaskFrontmatter } from '../../task/frontmatter.ts';
6
+ import { readTaskFinalizationReceipt } from '../../task/finalization.ts';
7
+ import { inspectSandboxControlContainer } from './container-identity.ts';
8
+ import type { SandboxControlManifest } from './protocol.ts';
9
+ import { atomicWriteJson, readSandboxControlStatus } from './state.ts';
10
+ import { taskViewForManifest, type SandboxTaskView } from './task-view.ts';
11
+
12
+ type ReentryEvidence = Readonly<{
13
+ taskId: string;
14
+ generation: string;
15
+ containerId: string;
16
+ branch: string;
17
+ worktreeRoot: string;
18
+ source: string;
19
+ sourceDevice: string;
20
+ sourceInode: string;
21
+ receipt: NonNullable<SandboxTaskView['receipt']>;
22
+ }>;
23
+
24
+ function evidencePath(manifest: SandboxControlManifest): string {
25
+ return path.join(path.dirname(manifest.processingDir), 'completed-reentry.json');
26
+ }
27
+
28
+ function invalid(): Error {
29
+ return new Error('SANDBOX_COMPLETED_REENTRY_EVIDENCE_INVALID');
30
+ }
31
+
32
+ export async function prepareCompletedReentry(
33
+ manifest: SandboxControlManifest,
34
+ inspect: typeof inspectSandboxControlContainer = inspectSandboxControlContainer
35
+ ): Promise<ReentryEvidence> {
36
+ if (manifest.mode !== 'task-bound' || !manifest.taskId) throw invalid();
37
+ assertGitWorktreeBinding(manifest.repoRoot, manifest.worktreeRoot, manifest.branch);
38
+ const container = await inspect(manifest);
39
+ if (container.state !== 'found' || container.id !== manifest.containerIdentity.id) throw invalid();
40
+ const task = resolveTaskWorkspace(manifest.taskId, manifest.repoRoot);
41
+ const metadata = parseTypedTaskFrontmatter(fs.readFileSync(task.taskMd, 'utf8'));
42
+ if (task.state !== 'completed' || task.branch !== manifest.branch
43
+ || metadata.id !== manifest.taskId || metadata.status !== 'completed') throw invalid();
44
+ const receipt = readTaskFinalizationReceipt(manifest.repoRoot, manifest.taskId);
45
+ if (!receipt || receipt.taskId !== manifest.taskId) throw invalid();
46
+ const view = taskViewForManifest({ ...manifest, receipt });
47
+ if (view.state !== 'current' || view.observedSource !== 'completed' || !view.receipt) throw invalid();
48
+ const status = readSandboxControlStatus(manifest.publicStatusDir);
49
+ const previous = status.taskView;
50
+ if (status.generation !== manifest.generation || status.activeRequestId !== null
51
+ || previous.taskId !== manifest.taskId || !previous.receipt
52
+ || previous.receipt.receiptId !== view.receipt.receiptId
53
+ || previous.receipt.generation !== view.receipt.generation
54
+ || previous.receipt.requestId !== view.receipt.requestId
55
+ || previous.receipt.revision > view.receipt.revision) throw invalid();
56
+ const completedRoot = fs.realpathSync.native(path.join(manifest.repoRoot, '.agents', 'workspace', 'completed'));
57
+ const source = fs.realpathSync.native(path.join(completedRoot, manifest.taskId));
58
+ if (source !== path.join(completedRoot, manifest.taskId)) throw invalid();
59
+ const stat = fs.statSync(source, { bigint: true });
60
+ return {
61
+ taskId: manifest.taskId, generation: manifest.generation,
62
+ containerId: container.id, branch: manifest.branch,
63
+ worktreeRoot: fs.realpathSync.native(manifest.worktreeRoot),
64
+ source, sourceDevice: String(stat.dev), sourceInode: String(stat.ino), receipt: view.receipt
65
+ };
66
+ }
67
+
68
+ export function publishCompletedReentry(manifest: SandboxControlManifest, evidence: ReentryEvidence): void {
69
+ atomicWriteJson(evidencePath(manifest), evidence);
70
+ }
71
+
72
+ export async function completedReentryView(
73
+ manifest: SandboxControlManifest,
74
+ inspect: typeof inspectSandboxControlContainer = inspectSandboxControlContainer
75
+ ): Promise<SandboxTaskView | null> {
76
+ const file = evidencePath(manifest);
77
+ if (!fs.existsSync(file)) return null;
78
+ const evidence: unknown = JSON.parse(fs.readFileSync(file, 'utf8'));
79
+ const current = await prepareCompletedReentry(manifest, inspect);
80
+ if (JSON.stringify(evidence) !== JSON.stringify(current)) throw invalid();
81
+ return {
82
+ state: 'current', taskId: current.taskId, observedSource: 'completed',
83
+ receipt: current.receipt, reasonCode: null
84
+ };
85
+ }
86
+
87
+ export async function waitForCompletedReentry(manifest: SandboxControlManifest, evidence: ReentryEvidence): Promise<void> {
88
+ const deadline = Date.now() + 10_000;
89
+ while (Date.now() < deadline) {
90
+ const status = readSandboxControlStatus(manifest.publicStatusDir);
91
+ if (status.generation !== evidence.generation) throw invalid();
92
+ if (status.taskView.state === 'current' && status.taskView.observedSource === 'completed'
93
+ && status.taskView.taskId === evidence.taskId
94
+ && JSON.stringify(status.taskView.receipt) === JSON.stringify(evidence.receipt)) return;
95
+ await new Promise((resolve) => setTimeout(resolve, 100));
96
+ }
97
+ throw new Error('SANDBOX_COMPLETED_REENTRY_STATUS_TIMEOUT');
98
+ }