@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,5 @@
1
+ import { isCompletionEvidence, type CleanCompletionEvidence } from '../../task/orchestration.ts';
2
+ import { parseControlOutput } from '../../task/control-recovery.ts';
1
3
  import { execFileSync } from 'node:child_process';
2
4
  import fs from 'node:fs';
3
5
  import path from 'node:path';
@@ -23,6 +25,7 @@ import {
23
25
  SANDBOX_CONTROL_MAX_TERMINAL_RECORD_BYTES,
24
26
  SANDBOX_CONTROL_RESERVATION_BYTES
25
27
  } from './protocol.ts';
28
+ import { parseSandboxTaskView, taskViewForManifest, type SandboxTaskView } from './task-view.ts';
26
29
 
27
30
  export const SANDBOX_CONTROL_AUDIT_MAX_BYTES = 1024 * 1024;
28
31
  export const SANDBOX_CONTROL_EXECUTION_STOP_MS = 2_000;
@@ -100,6 +103,82 @@ export function reservationPath(manifest: SandboxControlManifest, requestId: str
100
103
  return path.join(manifest.processingDir, requestId, 'reservation.json');
101
104
  }
102
105
 
106
+ export type SandboxControlTerminalResult = Readonly<{
107
+ version: 1;
108
+ requestId: string;
109
+ generation: string;
110
+ taskId: string | null;
111
+ intentDigest: string;
112
+ targetState: string | null;
113
+ status: string;
114
+ changed: boolean | null;
115
+ completionEvidence: CleanCompletionEvidence | null;
116
+ }>;
117
+
118
+ export function terminalResultPath(manifest: SandboxControlManifest, requestId: string): string {
119
+ if (!/^[a-f0-9-]{16,64}$/u.test(requestId)) throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
120
+ return path.join(manifest.processingDir, requestId, 'terminal-result.json');
121
+ }
122
+
123
+ export function createSandboxControlTerminalResult(
124
+ manifest: SandboxControlManifest,
125
+ request: Readonly<{ id: string; family: string; operation?: string | null }>,
126
+ output: string
127
+ ): SandboxControlTerminalResult {
128
+ const nested = parseControlOutput(output) ?? {};
129
+ const run = nested.run && typeof nested.run === 'object' && !Array.isArray(nested.run)
130
+ ? nested.run as Record<string, unknown> : null;
131
+ const rawCompletion = nested.completionEvidence ?? run?.completionEvidence;
132
+ const operation = request.operation ?? (typeof nested.intent === 'string' ? nested.intent : null) ?? '';
133
+ return {
134
+ version: 1,
135
+ requestId: request.id,
136
+ generation: manifest.generation,
137
+ taskId: manifest.taskId,
138
+ intentDigest: createHash('sha256').update(`${request.family}\0${operation}`, 'utf8').digest('hex'),
139
+ targetState: typeof nested.targetState === 'string' ? nested.targetState : null,
140
+ status: typeof nested.status === 'string' ? nested.status : 'completed',
141
+ changed: typeof nested.changed === 'boolean' ? nested.changed : null,
142
+ completionEvidence: isCompletionEvidence(rawCompletion) ? rawCompletion : null
143
+ };
144
+ }
145
+
146
+ export function writeSandboxControlTerminalResult(
147
+ manifest: SandboxControlManifest,
148
+ request: Readonly<{ id: string; family: string; operation?: string | null }>,
149
+ output: string
150
+ ): SandboxControlTerminalResult {
151
+ const result = createSandboxControlTerminalResult(manifest, request, output);
152
+ const filePath = terminalResultPath(manifest, request.id);
153
+ try {
154
+ atomicWriteJsonNoReplace(filePath, result);
155
+ } catch (error) {
156
+ if (!(error instanceof Error) || error.message !== 'SANDBOX_CONTROL_TERMINAL_ALREADY_EXISTS') throw error;
157
+ const existing = readSandboxControlTerminalResult(filePath);
158
+ if (JSON.stringify(existing) !== JSON.stringify(result)) throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_CONFLICT');
159
+ }
160
+ return readSandboxControlTerminalResult(filePath);
161
+ }
162
+
163
+ export function readSandboxControlTerminalResult(filePath: string): SandboxControlTerminalResult {
164
+ const value = readJsonFile(filePath) as Partial<SandboxControlTerminalResult> | null;
165
+ if (!value || value.version !== 1 || typeof value.requestId !== 'string'
166
+ || typeof value.generation !== 'string' || (value.taskId !== null && typeof value.taskId !== 'string')
167
+ || typeof value.intentDigest !== 'string' || !/^[a-f0-9]{64}$/u.test(value.intentDigest)
168
+ || (value.targetState !== null && typeof value.targetState !== 'string')
169
+ || typeof value.status !== 'string'
170
+ || (value.changed !== null && typeof value.changed !== 'boolean')) {
171
+ throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
172
+ }
173
+ if (value.completionEvidence !== null && !isCompletionEvidence(value.completionEvidence)) {
174
+ throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
175
+ }
176
+ if (value.changed === undefined) {
177
+ throw new Error('SANDBOX_CONTROL_TERMINAL_RESULT_INVALID');
178
+ }
179
+ return value as SandboxControlTerminalResult;
180
+ }
181
+
103
182
  export function readSandboxControlResultEvidence(filePath: string): SandboxControlResultEvidence {
104
183
  assertRegularFile(filePath);
105
184
  const raw = fs.readFileSync(filePath, 'utf8');
@@ -296,10 +375,11 @@ export function writeSandboxControlStatus(
296
375
  state: SandboxControlStatus['state'],
297
376
  reasonCode: string | null,
298
377
  activeRequestId: string | null,
299
- now = Date.now()
378
+ now = Date.now(),
379
+ taskView: SandboxTaskView = taskViewForManifest(manifest)
300
380
  ): SandboxControlStatus {
301
381
  const status: SandboxControlStatus = {
302
- version: 2, generation: manifest.generation, broker, state, reasonCode, activeRequestId, updatedAt: now
382
+ version: 3, generation: manifest.generation, broker, state, reasonCode, activeRequestId, updatedAt: now, taskView
303
383
  };
304
384
  atomicWriteJson(statusPath(manifest), status, 0o600, false);
305
385
  return status;
@@ -307,7 +387,7 @@ export function writeSandboxControlStatus(
307
387
 
308
388
  export function parseSandboxControlStatus(value: unknown): SandboxControlStatus {
309
389
  const status = value as Partial<SandboxControlStatus> | null;
310
- if (!status || status.version !== 2 || typeof status.generation !== 'string'
390
+ if (!status || status.version !== 3 || typeof status.generation !== 'string'
311
391
  || !status.broker || !Number.isSafeInteger(status.broker.pid) || typeof status.broker.startTime !== 'number'
312
392
  || !Number.isSafeInteger(status.broker.startTime) || typeof status.broker.brokerId !== 'string'
313
393
  || status.broker.brokerId.length === 0
@@ -315,6 +395,7 @@ export function parseSandboxControlStatus(value: unknown): SandboxControlStatus
315
395
  || (status.reasonCode !== null && typeof status.reasonCode !== 'string')
316
396
  || (status.activeRequestId !== null && typeof status.activeRequestId !== 'string')
317
397
  || !Number.isSafeInteger(status.updatedAt)) throw new Error('SANDBOX_CONTROL_STATUS_INVALID');
398
+ parseSandboxTaskView(status.taskView);
318
399
  return status as SandboxControlStatus;
319
400
  }
320
401
 
@@ -0,0 +1,240 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ export const SANDBOX_TASK_VIEW_STATES = ['not-applicable', 'current', 'finalized-stale', 'unknown'] as const;
5
+ export const SANDBOX_TASK_VIEW_SOURCES = ['active', 'completed', 'unknown'] as const;
6
+
7
+ export type SandboxTaskViewState = typeof SANDBOX_TASK_VIEW_STATES[number];
8
+ export type SandboxTaskViewSource = typeof SANDBOX_TASK_VIEW_SOURCES[number];
9
+ export type SandboxTaskViewReceipt = Readonly<{
10
+ receiptId: string;
11
+ revision: number;
12
+ generation: string;
13
+ requestId: string;
14
+ }>;
15
+ export type SandboxTaskView = Readonly<{
16
+ state: SandboxTaskViewState;
17
+ taskId: string | null;
18
+ observedSource: SandboxTaskViewSource | null;
19
+ receipt: SandboxTaskViewReceipt | null;
20
+ reasonCode: string | null;
21
+ }>;
22
+
23
+ export type TaskViewProjectionInput = Readonly<{
24
+ mode: 'task-bound' | 'branch-only';
25
+ taskId: string | null;
26
+ generation: string;
27
+ source: SandboxTaskViewSource;
28
+ sourceMatches: boolean;
29
+ receipt?: unknown;
30
+ requestId?: string;
31
+ }>;
32
+
33
+ export type TaskViewAccessEffect =
34
+ | 'diagnostic'
35
+ | 'progress'
36
+ | 'artifact-write'
37
+ | 'terminal-verdict'
38
+ | 'recovery'
39
+ | 'cleanup'
40
+ | 'remote-write';
41
+
42
+ export type TaskViewAccess = Readonly<{
43
+ allowed: boolean;
44
+ state: SandboxTaskViewState;
45
+ reasonCode: string | null;
46
+ message: string | null;
47
+ }>;
48
+
49
+ function invalidView(message: string): Error {
50
+ return new Error(`SANDBOX_TASK_VIEW_INVALID: ${message}`);
51
+ }
52
+
53
+ function receiptIdentity(value: unknown, generation: string, requestId?: string): SandboxTaskViewReceipt | null {
54
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
55
+ const receipt = value as Record<string, unknown>;
56
+ const binding = receipt.controlBinding;
57
+ if (!binding || typeof binding !== 'object' || Array.isArray(binding)) return null;
58
+ const controlBinding = binding as Record<string, unknown>;
59
+ if (receipt.version !== 2 || typeof receipt.receiptId !== 'string' || receipt.receiptId.length === 0
60
+ || !Number.isSafeInteger(receipt.revision) || (receipt.revision as number) < 0
61
+ || receipt.lifecycle !== 'done'
62
+ || controlBinding.generation !== generation
63
+ || typeof controlBinding.requestId !== 'string'
64
+ || !/^[a-f0-9-]{16,64}$/u.test(controlBinding.requestId)
65
+ || (requestId !== undefined && controlBinding.requestId !== requestId)) return null;
66
+ return {
67
+ receiptId: receipt.receiptId,
68
+ revision: receipt.revision as number,
69
+ generation,
70
+ requestId: controlBinding.requestId
71
+ };
72
+ }
73
+
74
+ export function parseSandboxTaskView(value: unknown): SandboxTaskView {
75
+ if (!value || typeof value !== 'object' || Array.isArray(value)) throw invalidView('taskView must be an object');
76
+ const view = value as Record<string, unknown>;
77
+ const receipt = view.receipt === null ? null : parseSandboxTaskViewReceipt(view.receipt);
78
+ if (!SANDBOX_TASK_VIEW_STATES.includes(view.state as SandboxTaskViewState)
79
+ || (view.taskId !== null && typeof view.taskId !== 'string')
80
+ || (view.observedSource !== null && !SANDBOX_TASK_VIEW_SOURCES.includes(view.observedSource as SandboxTaskViewSource))
81
+ || (view.reasonCode !== null && typeof view.reasonCode !== 'string')
82
+ || (view.receipt !== null && receipt === null)) {
83
+ throw invalidView('taskView schema is invalid');
84
+ }
85
+ if (view.state === 'not-applicable' && (view.taskId !== null || view.observedSource !== null || view.receipt !== null)) {
86
+ throw invalidView('not-applicable taskView cannot carry task evidence');
87
+ }
88
+ if (view.state === 'current' && (view.taskId === null || view.observedSource === null)) {
89
+ throw invalidView('current taskView requires taskId and observedSource');
90
+ }
91
+ if (view.state === 'current' && view.observedSource === 'completed' && view.receipt === null) {
92
+ throw invalidView('completed current taskView requires receipt');
93
+ }
94
+ if (view.state === 'finalized-stale' && (view.taskId === null || view.receipt === null)) {
95
+ throw invalidView('finalized-stale taskView requires taskId and receipt');
96
+ }
97
+ return view as SandboxTaskView;
98
+ }
99
+
100
+ function parseSandboxTaskViewReceipt(value: unknown): SandboxTaskViewReceipt | null {
101
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
102
+ const receipt = value as Record<string, unknown>;
103
+ if (Object.keys(receipt).sort().join(',') !== 'generation,receiptId,requestId,revision'
104
+ || typeof receipt.receiptId !== 'string' || receipt.receiptId.length === 0
105
+ || !Number.isSafeInteger(receipt.revision) || (receipt.revision as number) < 0
106
+ || typeof receipt.generation !== 'string' || receipt.generation.length === 0
107
+ || typeof receipt.requestId !== 'string' || !/^[a-f0-9-]{16,64}$/u.test(receipt.requestId)) return null;
108
+ return receipt as SandboxTaskViewReceipt;
109
+ }
110
+
111
+ export function taskViewForManifest(params: Readonly<{
112
+ repoRoot: string;
113
+ mode: 'task-bound' | 'branch-only';
114
+ taskId: string | null;
115
+ generation: string;
116
+ receipt?: unknown;
117
+ }>): SandboxTaskView {
118
+ if (params.mode !== 'task-bound' || !params.taskId) {
119
+ return { state: 'not-applicable', taskId: null, observedSource: null, receipt: null, reasonCode: null };
120
+ }
121
+ const active = path.join(params.repoRoot, '.agents', 'workspace', 'active', params.taskId, 'task.md');
122
+ const completed = path.join(params.repoRoot, '.agents', 'workspace', 'completed', params.taskId, 'task.md');
123
+ const activeExists = fs.existsSync(active);
124
+ const completedExists = fs.existsSync(completed);
125
+ if (activeExists === completedExists) {
126
+ return {
127
+ state: 'unknown', taskId: params.taskId,
128
+ observedSource: activeExists ? 'unknown' : null,
129
+ receipt: null, reasonCode: activeExists ? 'SANDBOX_TASK_VIEW_SOURCE_CONFLICT' : 'SANDBOX_TASK_VIEW_SOURCE_MISSING'
130
+ };
131
+ }
132
+ const source = completedExists ? 'completed' : 'active';
133
+ const receipt = receiptIdentity(params.receipt, params.generation);
134
+ const receiptProvided = params.receipt !== undefined && params.receipt !== null;
135
+ if (source === 'completed' && receipt) {
136
+ return { state: 'current', taskId: params.taskId, observedSource: source, receipt, reasonCode: null };
137
+ }
138
+ if (source === 'active' && receipt) {
139
+ return {
140
+ state: 'finalized-stale', taskId: params.taskId, observedSource: source, receipt,
141
+ reasonCode: 'SANDBOX_TASK_VIEW_SOURCE_STALE'
142
+ };
143
+ }
144
+ if (receiptProvided) {
145
+ return {
146
+ state: 'unknown', taskId: params.taskId, observedSource: source, receipt: null,
147
+ reasonCode: 'SANDBOX_TASK_VIEW_RECEIPT_INVALID'
148
+ };
149
+ }
150
+ if (source === 'active') {
151
+ return { state: 'current', taskId: params.taskId, observedSource: source, receipt: null, reasonCode: null };
152
+ }
153
+ return {
154
+ state: 'unknown', taskId: params.taskId, observedSource: source, receipt: null,
155
+ reasonCode: 'SANDBOX_TASK_VIEW_EVIDENCE_UNAVAILABLE'
156
+ };
157
+ }
158
+
159
+ export function projectSandboxTaskView(input: TaskViewProjectionInput): SandboxTaskView {
160
+ if (input.mode !== 'task-bound' || !input.taskId) {
161
+ return { state: 'not-applicable', taskId: null, observedSource: null, receipt: null, reasonCode: null };
162
+ }
163
+ const receipt = receiptIdentity(input.receipt, input.generation, input.requestId);
164
+ if (input.source === 'completed' && receipt && input.sourceMatches) {
165
+ return { state: 'current', taskId: input.taskId, observedSource: 'completed', receipt, reasonCode: null };
166
+ }
167
+ if (receipt && input.source === 'active' && !input.sourceMatches) {
168
+ return { state: 'finalized-stale', taskId: input.taskId, observedSource: 'active', receipt, reasonCode: 'SANDBOX_TASK_VIEW_SOURCE_STALE' };
169
+ }
170
+ if (!receipt && input.source === 'active' && input.sourceMatches) {
171
+ return { state: 'current', taskId: input.taskId, observedSource: 'active', receipt: null, reasonCode: null };
172
+ }
173
+ return {
174
+ state: 'unknown',
175
+ taskId: input.taskId,
176
+ observedSource: input.source,
177
+ receipt,
178
+ reasonCode: receipt ? 'SANDBOX_TASK_VIEW_SOURCE_UNCONFIRMED' : 'SANDBOX_TASK_VIEW_EVIDENCE_UNAVAILABLE'
179
+ };
180
+ }
181
+
182
+ export function taskViewAfterFinalization(params: Readonly<{
183
+ taskId: string;
184
+ generation: string;
185
+ requestId: string;
186
+ receipt: unknown;
187
+ }>): SandboxTaskView {
188
+ const receipt = receiptIdentity(params.receipt, params.generation, params.requestId);
189
+ if (!receipt) {
190
+ return {
191
+ state: 'unknown', taskId: params.taskId, observedSource: 'unknown', receipt: null,
192
+ reasonCode: 'SANDBOX_TASK_VIEW_RECEIPT_INVALID'
193
+ };
194
+ }
195
+ return {
196
+ state: 'finalized-stale', taskId: params.taskId, observedSource: 'active', receipt,
197
+ reasonCode: 'SANDBOX_TASK_VIEW_FINALIZED'
198
+ };
199
+ }
200
+
201
+ export function mergeSandboxTaskView(
202
+ canonical: SandboxTaskView,
203
+ previous: SandboxTaskView
204
+ ): SandboxTaskView {
205
+ if (previous.state !== 'finalized-stale' && previous.state !== 'unknown') return canonical;
206
+ return previous;
207
+ }
208
+
209
+ export function accessSandboxTaskView(view: SandboxTaskView, effect: TaskViewAccessEffect): TaskViewAccess {
210
+ if (effect === 'diagnostic' || effect === 'cleanup' || effect === 'recovery') {
211
+ return { allowed: true, state: view.state, reasonCode: view.reasonCode, message: null };
212
+ }
213
+ if (view.state === 'current' && view.observedSource === 'active') {
214
+ return { allowed: true, state: view.state, reasonCode: null, message: null };
215
+ }
216
+ const reasonCode = view.state === 'current'
217
+ ? 'SANDBOX_TASK_VIEW_COMPLETED_READ_ONLY'
218
+ : view.reasonCode ?? `SANDBOX_TASK_VIEW_${view.state.toUpperCase().replaceAll('-', '_')}`;
219
+ return {
220
+ allowed: false,
221
+ state: view.state,
222
+ reasonCode,
223
+ message: view.state === 'unknown'
224
+ ? 'task view cannot be confirmed; inspect status, recover the original request, or explicitly clean up the sandbox'
225
+ : view.state === 'finalized-stale'
226
+ ? 'task view is finalized/stale; exit or clean up the sandbox, or use a validated re-entry'
227
+ : 'completed task views are read-only'
228
+ };
229
+ }
230
+
231
+ export function taskViewFromStatus(value: unknown): SandboxTaskView {
232
+ if (!value || typeof value !== 'object' || Array.isArray(value)) throw invalidView('status must be an object');
233
+ const status = value as Record<string, unknown>;
234
+ if (status.version !== 3) throw invalidView('status version is unsupported');
235
+ const taskView = parseSandboxTaskView(status.taskView);
236
+ if (taskView.receipt && taskView.receipt.generation !== status.generation) {
237
+ throw invalidView('taskView receipt generation does not match status generation');
238
+ }
239
+ return taskView;
240
+ }
@@ -0,0 +1,95 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { randomUUID } from 'node:crypto';
4
+
5
+ import { readStableFile, SecureFileError, writeAtomicFile } from '../../host-control/secure-fs.ts';
6
+
7
+ import { TASK_WORKFLOW_COMMANDS, TASK_WORKFLOW_OPERATIONS, type TaskWorkflowOperation, type WorkflowCommand } from '../../task/workflow-command.ts';
8
+
9
+ export type TaskWorkflowRequest = Readonly<{
10
+ version: 1;
11
+ id: string;
12
+ taskId: string;
13
+ generation: string;
14
+ operation: TaskWorkflowOperation;
15
+ args: readonly string[];
16
+ }>;
17
+
18
+ export function validateTaskWorkflowRequest(value: unknown): TaskWorkflowRequest {
19
+ if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('TASK_WORKFLOW_REQUEST_INVALID');
20
+ const request = value as TaskWorkflowRequest;
21
+ if (Object.keys(request).sort().join(',') !== 'args,generation,id,operation,taskId,version'
22
+ || request.version !== 1 || typeof request.id !== 'string' || !/^[a-f0-9-]{16,64}$/u.test(request.id)
23
+ || typeof request.taskId !== 'string' || !/^TASK-\d{8}-\d{6}$/u.test(request.taskId)
24
+ || typeof request.generation !== 'string' || !request.generation
25
+ || !Object.hasOwn(TASK_WORKFLOW_COMMANDS, request.operation)
26
+ || !Array.isArray(request.args) || request.args.length < 2 || request.args.length > 128
27
+ || !request.args.every((arg) => typeof arg === 'string' && !/[\r\n]/u.test(arg))
28
+ || request.args[0] !== request.taskId) throw new Error('TASK_WORKFLOW_REQUEST_INVALID');
29
+ const [, selector] = TASK_WORKFLOW_COMMANDS[request.operation];
30
+ if (selector !== null && request.args[1] !== selector) throw new Error('TASK_WORKFLOW_REQUEST_INVALID');
31
+ return request;
32
+ }
33
+
34
+ /** Preserve argv verbatim; the domain command owns option parsing and validation. */
35
+ export function createTaskWorkflowRequest(
36
+ command: WorkflowCommand | undefined,
37
+ args: readonly string[],
38
+ taskId: string,
39
+ generation: string
40
+ ): TaskWorkflowRequest {
41
+ const operation = TASK_WORKFLOW_OPERATIONS.find((key) => {
42
+ const [name, selector] = TASK_WORKFLOW_COMMANDS[key];
43
+ return name === command && (selector === null || selector === args[1]);
44
+ });
45
+ if (!operation) throw new Error('TASK_WORKFLOW_OPERATION_UNSUPPORTED');
46
+ return validateTaskWorkflowRequest({ version: 1, id: randomUUID(), taskId, generation, operation, args: [...args] });
47
+ }
48
+
49
+ export function canonicalArtifactName(value: string): boolean {
50
+ return /^(?:analysis|plan|code|review-analysis|review-plan|review-code)(?:-r[1-9]\d*)?\.md$/u.test(value)
51
+ && path.basename(value) === value;
52
+ }
53
+
54
+ function assertTaskArtifactDirectory(directory: string): string {
55
+ const resolved = path.resolve(directory);
56
+ let stat: fs.Stats;
57
+ try {
58
+ stat = fs.lstatSync(resolved);
59
+ } catch {
60
+ throw new SecureFileError('TASK_ARTIFACT_WRITE_DENIED', 'task directory is unavailable');
61
+ }
62
+ if (!stat.isDirectory() || stat.isSymbolicLink()) {
63
+ throw new SecureFileError('TASK_ARTIFACT_WRITE_DENIED', 'task directory must be a real directory');
64
+ }
65
+ return resolved;
66
+ }
67
+
68
+ export async function readTaskArtifact(
69
+ taskDir: string,
70
+ input: Readonly<{
71
+ artifact: string;
72
+ expectedSha256?: string;
73
+ }>
74
+ ): Promise<Readonly<{ artifact: string; bytes: Buffer; sha256: string }>> {
75
+ if (!canonicalArtifactName(input.artifact)) throw new SecureFileError('TASK_ARTIFACT_WRITE_DENIED', 'artifact must be a canonical top-level basename');
76
+ const root = assertTaskArtifactDirectory(taskDir);
77
+ const candidate = path.join(root, input.artifact);
78
+ if (path.dirname(candidate) !== root) throw new SecureFileError('TASK_ARTIFACT_WRITE_DENIED', 'artifact escapes task directory');
79
+ const stable = await readStableFile(candidate, {
80
+ maxBytes: 1024 * 1024,
81
+ ...(input.expectedSha256 ? { expectedSha256: input.expectedSha256 } : {})
82
+ });
83
+ return { artifact: input.artifact, bytes: stable.bytes, sha256: stable.sha256 };
84
+ }
85
+
86
+ export async function writeTaskArtifact(
87
+ taskDir: string,
88
+ artifact: Readonly<{ artifact: string; bytes: Buffer; expectedSha256?: string }>
89
+ ): Promise<void> {
90
+ if (!canonicalArtifactName(artifact.artifact)) throw new SecureFileError('TASK_ARTIFACT_WRITE_DENIED', 'artifact must be a canonical top-level basename');
91
+ const root = assertTaskArtifactDirectory(taskDir);
92
+ const target = path.join(root, artifact.artifact);
93
+ if (path.dirname(target) !== root) throw new SecureFileError('TASK_ARTIFACT_WRITE_DENIED', 'artifact escapes task directory');
94
+ await writeAtomicFile(target, artifact.bytes, 0o600, artifact.expectedSha256);
95
+ }
@@ -0,0 +1,102 @@
1
+ import { TASK_WORKFLOW_COMMANDS } from '../../task/workflow-command.ts';
2
+
3
+ import { parseArtifactCommand, executeArtifactCommand } from '../../task/artifact-command.ts';
4
+ import { parseReviewCommand } from '../../task/review-command.ts';
5
+ import { prepareLocalArtifact, commitLocalArtifactProvenance } from '../../task/local-artifact-finalization.ts';
6
+ import { prepareReviewSummaryCandidate, commitReviewSummaryProvenance } from '../../task/review-finalization.ts';
7
+ import { withTaskExecutionLock } from '../../task/task-execution-lock.ts';
8
+ import { dispatchHostControlCommand } from '../../host-control/command.ts';
9
+ import { assertSandboxTaskSource } from '../workspace-view.ts';
10
+ import { appendDiagnosticAudit } from './audit.ts';
11
+ import {
12
+ readTaskArtifact, writeTaskArtifact, type TaskWorkflowRequest
13
+ } from './task-workflow.ts';
14
+ import type { SandboxControlManifest } from './protocol.ts';
15
+ import type { SandboxControlExecutionResult } from './executor.ts';
16
+ import type { LifecycleRecoveryAttestationV1 } from '../../task/control-authority.ts';
17
+ import { applyTaskEvent } from '../../task/events.ts';
18
+ import { parseTaskEventRequest } from '../../internal/task-event.ts';
19
+
20
+ function executionResult(result: Record<string, unknown>): SandboxControlExecutionResult {
21
+ return { exitCode: result.status === 'failed' || result.status === 'refused' ? 1 : 0, stdout: `${JSON.stringify(result)}\n`, stderr: '' };
22
+ }
23
+
24
+ /** The broker already authorized this host executor; domain operations own validation. */
25
+ export async function executeTaskWorkflow(
26
+ manifest: SandboxControlManifest,
27
+ request: TaskWorkflowRequest,
28
+ lifecycleRecoveryAttestation: LifecycleRecoveryAttestationV1 | null = null
29
+ ): Promise<SandboxControlExecutionResult> {
30
+ let publicationStarted = false;
31
+ try {
32
+ const taskDir = assertSandboxTaskSource(manifest.repoRoot, request.taskId);
33
+ const [command] = TASK_WORKFLOW_COMMANDS[request.operation];
34
+ if (request.operation === 'event') {
35
+ const eventRequest = parseTaskEventRequest(request.args);
36
+ const boundEventRequest = lifecycleRecoveryAttestation && eventRequest.requestId === undefined
37
+ ? { ...eventRequest, requestId: lifecycleRecoveryAttestation.lifecycleRequestId }
38
+ : eventRequest;
39
+ const result = applyTaskEvent(boundEventRequest, {
40
+ repoRoot: manifest.repoRoot,
41
+ lifecycleRecoveryAttestation,
42
+ deferLifecycleRecoveryConsumption: true
43
+ });
44
+ return executionResult(result);
45
+ }
46
+ if (command !== 'task-artifact' && command !== 'task-review') {
47
+ publicationStarted = true;
48
+ // Inherit this executor's process group so its existing recovery owns the worker too.
49
+ const result = await dispatchHostControlCommand({
50
+ operation: command, payload: { workingDirectory: manifest.repoRoot, args: request.args }
51
+ });
52
+ return result;
53
+ }
54
+ const input = command === 'task-artifact' ? parseArtifactCommand(request.args) : parseReviewCommand(request.args);
55
+ if (command === 'task-artifact' && 'operation' in input && input.operation === 'inspect') {
56
+ return executionResult(executeArtifactCommand(input, { repoRoot: manifest.repoRoot }));
57
+ }
58
+ if ('operation' in input && (input.operation === 'init' || input.operation === 'repair')) {
59
+ return executionResult(executeArtifactCommand(input, { repoRoot: manifest.repoRoot, artifactDir: taskDir }));
60
+ }
61
+ return await withTaskExecutionLock(manifest.repoRoot, request.taskId, `sandbox-control.${request.operation}`, async () => {
62
+ const artifact = await readTaskArtifact(taskDir, { artifact: input.artifact });
63
+ const content = artifact.bytes.toString('utf8');
64
+ let result: Record<string, unknown>;
65
+ if ('operation' in input) {
66
+ const { family } = input;
67
+ if (family !== 'analysis' && family !== 'plan' && family !== 'code') throw new Error('ARTIFACT_IDENTITY_INVALID');
68
+ const local = { taskRef: request.taskId, family, artifact: input.artifact, repoRoot: manifest.repoRoot } as const;
69
+ const prepared = prepareLocalArtifact(local, content, lifecycleRecoveryAttestation ?? undefined);
70
+ if (prepared.result.status === 'failed') return executionResult(commitLocalArtifactProvenance(prepared));
71
+ publicationStarted = true;
72
+ await writeTaskArtifact(taskDir, {
73
+ artifact: artifact.artifact,
74
+ bytes: artifact.bytes,
75
+ expectedSha256: artifact.sha256
76
+ });
77
+ result = commitLocalArtifactProvenance(prepared);
78
+ } else {
79
+ if (input.overrideTicket) throw new Error('TASK_WORKFLOW_OVERRIDE_UNSUPPORTED');
80
+ const prepared = prepareReviewSummaryCandidate(input, content, { repoRoot: manifest.repoRoot });
81
+ if (input.dryRun || prepared.result.status === 'planned') return executionResult(prepared.result);
82
+ if (prepared.result.status === 'failed') return executionResult(commitReviewSummaryProvenance(prepared, manifest.repoRoot));
83
+ publicationStarted = true;
84
+ await writeTaskArtifact(taskDir, {
85
+ artifact: input.artifact,
86
+ bytes: Buffer.from(prepared.content, 'utf8'),
87
+ expectedSha256: artifact.sha256
88
+ });
89
+ result = commitReviewSummaryProvenance(prepared, manifest.repoRoot);
90
+ }
91
+ if (result.status !== 'failed') appendDiagnosticAudit(manifest, 'task-workflow-artifact-finalized', {
92
+ requestId: request.id, sandboxTaskId: request.taskId, workflowOperation: request.operation,
93
+ artifact: input.artifact, sha256: result.artifactSha256 as string, semanticDigest: result.semanticDigest as string
94
+ });
95
+ return executionResult({ ...result, changed: true });
96
+ });
97
+ } catch (error) {
98
+ const message = error instanceof Error ? error.message : String(error);
99
+ const code = (error as { code?: string })?.code ?? /^([A-Z][A-Z0-9_]+)/u.exec(message)?.[1] ?? 'TASK_WORKFLOW_REQUEST_INVALID';
100
+ return executionResult({ status: 'failed', changed: publicationStarted ? null : false, error: { code, message } });
101
+ }
102
+ }