@fitlab-ai/agent-infra 0.9.10 → 0.9.12

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 (375) hide show
  1. package/README.md +19 -9
  2. package/README.zh-CN.md +19 -9
  3. package/bin/cli.ts +17 -4
  4. package/dist/bin/cli.js +19 -4
  5. package/dist/bin/internal-cli.js +55 -4
  6. package/dist/lib/agent-client.js +0 -1
  7. package/dist/lib/agent-clients/adapters/codex-lifecycle/build-identity.js +71 -10
  8. package/dist/lib/agent-clients/adapters/opencode-sandbox.js +0 -4
  9. package/dist/lib/agent-clients/adapters/traecli.js +7 -8
  10. package/dist/lib/agent-clients/config.js +14 -99
  11. package/dist/lib/agent-clients/reconcile.js +1 -11
  12. package/dist/lib/agent-clients/schema.js +5 -4
  13. package/dist/lib/agent-clients/tokens.js +5 -15
  14. package/dist/lib/decide.js +14 -30
  15. package/dist/lib/defaults.json +8 -0
  16. package/dist/lib/git/workflow.js +43 -1
  17. package/dist/lib/init.js +3 -2
  18. package/dist/lib/internal/git-workflow.js +8 -1
  19. package/dist/lib/internal/platform-pr.js +8 -2
  20. package/dist/lib/internal/release-workflow.js +0 -1
  21. package/dist/lib/internal/task-delivery.js +59 -0
  22. package/dist/lib/internal/task-finalization.js +19 -9
  23. package/dist/lib/internal/task-ledger.js +8 -2
  24. package/dist/lib/internal/task-lifecycle.js +17 -93
  25. package/dist/lib/internal/task-orchestration.js +30 -216
  26. package/dist/lib/internal/task-validate.js +2 -1
  27. package/dist/lib/merge.js +205 -103
  28. package/dist/lib/platform/github-client.js +7 -1
  29. package/dist/lib/platform/pr-checks.js +8 -5
  30. package/dist/lib/platform/pr-summary.js +12 -8
  31. package/dist/lib/platform/pull-requests.js +393 -91
  32. package/dist/lib/platform/verification-required.js +13 -2
  33. package/dist/lib/platform/verification-sync.js +9 -4
  34. package/dist/lib/pr-review/evidence-grading.js +12 -4
  35. package/dist/lib/process-data/normalize.js +2 -9
  36. package/dist/lib/run/host.js +46 -16
  37. package/dist/lib/sandbox/agent-client-reconciler.js +4 -12
  38. package/dist/lib/sandbox/commands/create.js +3 -3
  39. package/dist/lib/sandbox/commands/enter.js +18 -4
  40. package/dist/lib/sandbox/config.js +8 -12
  41. package/dist/lib/sandbox/control/executor.js +131 -39
  42. package/dist/lib/sandbox/control/lifecycle.js +55 -24
  43. package/dist/lib/sandbox/control/server.js +18 -16
  44. package/dist/lib/sandbox/mounts.js +3 -2
  45. package/dist/lib/sandbox/recovery.js +123 -22
  46. package/dist/lib/sandbox/workspace-identity.js +14 -0
  47. package/dist/lib/sandbox/workspace-view.js +8 -9
  48. package/dist/lib/self-update.js +278 -0
  49. package/dist/lib/server/process-state.js +2 -2
  50. package/dist/lib/server/protocol.js +5 -5
  51. package/dist/lib/task/activity-log.js +4 -1
  52. package/dist/lib/task/artifact-lifecycle.js +6 -4
  53. package/dist/lib/task/codex-orchestration.js +1 -1
  54. package/dist/lib/task/command-options.js +1 -3
  55. package/dist/lib/task/commands/cat.js +0 -5
  56. package/dist/lib/task/commands/decisions.js +76 -92
  57. package/dist/lib/task/commands/files.js +6 -6
  58. package/dist/lib/task/commands/grep.js +25 -61
  59. package/dist/lib/task/commands/issue-body.js +6 -6
  60. package/dist/lib/task/commands/log.js +25 -10
  61. package/dist/lib/task/commands/show.js +5 -6
  62. package/dist/lib/task/commands/status.js +8 -8
  63. package/dist/lib/task/commit-intent.js +100 -0
  64. package/dist/lib/task/commit-operation.js +181 -91
  65. package/dist/lib/task/commit-policy.js +1 -1
  66. package/dist/lib/task/control-authority.js +431 -0
  67. package/dist/lib/task/create.js +39 -8
  68. package/dist/lib/task/current-contract.js +35 -0
  69. package/dist/lib/task/decision-details.js +218 -0
  70. package/dist/lib/task/decision-intents.js +5 -2
  71. package/dist/lib/task/delivery-target.js +97 -0
  72. package/dist/lib/task/delivery.js +153 -0
  73. package/dist/lib/task/events.js +11 -5
  74. package/dist/lib/task/human-override.js +5 -2
  75. package/dist/lib/task/index.js +19 -20
  76. package/dist/lib/task/ledger-intents.js +8 -9
  77. package/dist/lib/task/ledger.js +13 -6
  78. package/dist/lib/task/lifecycle.js +122 -9
  79. package/dist/lib/task/orchestration.js +105 -75
  80. package/dist/lib/task/pr-delivery-fact.js +182 -0
  81. package/dist/lib/task/review-artifacts.js +31 -12
  82. package/dist/lib/task/review-finalization.js +13 -4
  83. package/dist/lib/task/review-fingerprint.js +8 -0
  84. package/dist/lib/task/verification-engine.js +86 -39
  85. package/dist/lib/task/verification-review.js +2 -2
  86. package/dist/lib/task/workspace-snapshot.js +5 -11
  87. package/dist/lib/task/write.js +11 -1
  88. package/dist/lib/update.js +13 -35
  89. package/dist/lib/version.js +5 -1
  90. package/dist/lifecycle-build-manifest.json +3 -3
  91. package/lib/agent-client.ts +0 -1
  92. package/lib/agent-clients/adapters/codex-lifecycle/build-identity.ts +105 -10
  93. package/lib/agent-clients/adapters/codex-lifecycle/manifest-files.json +12 -1
  94. package/lib/agent-clients/adapters/opencode-sandbox.ts +0 -5
  95. package/lib/agent-clients/adapters/traecli.ts +7 -8
  96. package/lib/agent-clients/config.ts +14 -144
  97. package/lib/agent-clients/reconcile.ts +1 -12
  98. package/lib/agent-clients/schema.ts +5 -4
  99. package/lib/agent-clients/tokens.ts +5 -18
  100. package/lib/decide.ts +12 -22
  101. package/lib/defaults.json +8 -0
  102. package/lib/git/workflow.ts +38 -1
  103. package/lib/init.ts +5 -2
  104. package/lib/internal/git-workflow.ts +7 -1
  105. package/lib/internal/platform-pr.ts +8 -1
  106. package/lib/internal/release-workflow.ts +0 -1
  107. package/lib/internal/task-delivery.ts +53 -0
  108. package/lib/internal/task-finalization.ts +27 -13
  109. package/lib/internal/task-ledger.ts +8 -2
  110. package/lib/internal/task-lifecycle.ts +33 -76
  111. package/lib/internal/task-orchestration.ts +33 -190
  112. package/lib/internal/task-validate.ts +3 -2
  113. package/lib/merge.ts +236 -120
  114. package/lib/platform/github-client.ts +8 -1
  115. package/lib/platform/pr-checks.ts +7 -5
  116. package/lib/platform/pr-summary.ts +10 -7
  117. package/lib/platform/pull-requests.ts +425 -87
  118. package/lib/platform/verification-required.ts +12 -2
  119. package/lib/platform/verification-sync.ts +9 -4
  120. package/lib/pr-review/evidence-grading.ts +7 -4
  121. package/lib/process-data/normalize.ts +2 -8
  122. package/lib/run/host.ts +63 -14
  123. package/lib/sandbox/agent-client-reconciler.ts +5 -15
  124. package/lib/sandbox/commands/create.ts +4 -4
  125. package/lib/sandbox/commands/enter.ts +24 -4
  126. package/lib/sandbox/config.ts +9 -14
  127. package/lib/sandbox/control/container-identity.ts +2 -2
  128. package/lib/sandbox/control/executor.ts +148 -51
  129. package/lib/sandbox/control/lifecycle.ts +59 -31
  130. package/lib/sandbox/control/protocol.ts +1 -3
  131. package/lib/sandbox/control/server.ts +15 -12
  132. package/lib/sandbox/control/state.ts +7 -7
  133. package/lib/sandbox/mounts.ts +4 -3
  134. package/lib/sandbox/recovery.ts +162 -34
  135. package/lib/sandbox/tools.ts +1 -1
  136. package/lib/sandbox/workspace-identity.ts +28 -0
  137. package/lib/sandbox/workspace-view.ts +14 -10
  138. package/lib/self-update.ts +341 -0
  139. package/lib/server/process-state.ts +2 -2
  140. package/lib/server/protocol.ts +5 -5
  141. package/lib/task/activity-log.ts +6 -1
  142. package/lib/task/artifact-lifecycle.ts +7 -5
  143. package/lib/task/codex-orchestration.ts +2 -1
  144. package/lib/task/command-options.ts +1 -2
  145. package/lib/task/commands/cat.ts +0 -4
  146. package/lib/task/commands/decisions.ts +80 -75
  147. package/lib/task/commands/files.ts +6 -6
  148. package/lib/task/commands/grep.ts +23 -51
  149. package/lib/task/commands/issue-body.ts +6 -6
  150. package/lib/task/commands/log.ts +22 -10
  151. package/lib/task/commands/show.ts +5 -6
  152. package/lib/task/commands/status.ts +8 -8
  153. package/lib/task/commit-intent.ts +137 -0
  154. package/lib/task/commit-operation.ts +161 -79
  155. package/lib/task/commit-policy.ts +1 -2
  156. package/lib/task/control-authority.ts +563 -0
  157. package/lib/task/create.ts +40 -10
  158. package/lib/task/current-contract.ts +49 -0
  159. package/lib/task/decision-details.ts +280 -0
  160. package/lib/task/decision-intents.ts +7 -2
  161. package/lib/task/delivery-target.ts +130 -0
  162. package/lib/task/delivery.ts +160 -0
  163. package/lib/task/events.ts +10 -5
  164. package/lib/task/human-override.ts +5 -2
  165. package/lib/task/index.ts +19 -20
  166. package/lib/task/ledger-intents.ts +6 -8
  167. package/lib/task/ledger.ts +17 -7
  168. package/lib/task/lifecycle.ts +113 -4
  169. package/lib/task/orchestration.ts +102 -80
  170. package/lib/task/pr-delivery-fact.ts +254 -0
  171. package/lib/task/review-artifacts.ts +53 -15
  172. package/lib/task/review-finalization.ts +21 -5
  173. package/lib/task/review-fingerprint.ts +10 -0
  174. package/lib/task/verification-engine.ts +78 -46
  175. package/lib/task/verification-review.ts +3 -1
  176. package/lib/task/workspace-snapshot.ts +4 -13
  177. package/lib/task/write.ts +20 -1
  178. package/lib/update.ts +14 -38
  179. package/lib/version.ts +6 -1
  180. package/package.json +1 -1
  181. package/templates/.agents/README.en.md +5 -3
  182. package/templates/.agents/README.zh-CN.md +5 -3
  183. package/templates/.agents/rules/README.en.md +4 -2
  184. package/templates/.agents/rules/README.zh-CN.md +4 -2
  185. package/templates/.agents/rules/compatibility-policy.en.md +5 -4
  186. package/templates/.agents/rules/compatibility-policy.zh-CN.md +5 -4
  187. package/templates/.agents/rules/create-issue.en.md +11 -2
  188. package/templates/.agents/rules/create-issue.zh-CN.md +24 -2
  189. package/templates/.agents/rules/human-decision-context.en.md +9 -7
  190. package/templates/.agents/rules/human-decision-context.zh-CN.md +9 -7
  191. package/templates/.agents/rules/{issue-fields.github.en.md → issue-fields.en.md} +1 -1
  192. package/templates/.agents/rules/{issue-fields.github.zh-CN.md → issue-fields.zh-CN.md} +1 -1
  193. package/templates/.agents/rules/issue-pr-commands.en.md +5 -18
  194. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +1 -1
  195. package/templates/.agents/rules/issue-sync.en.md +26 -5
  196. package/templates/.agents/rules/issue-sync.zh-CN.md +62 -5
  197. package/templates/.agents/rules/label-milestone-setup.en.md +15 -2
  198. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +15 -2
  199. package/templates/.agents/rules/local-artifact-repair.en.md +46 -0
  200. package/templates/.agents/rules/local-artifact-repair.zh-CN.md +46 -0
  201. package/templates/.agents/rules/milestone-inference.en.md +12 -2
  202. package/templates/.agents/rules/milestone-inference.zh-CN.md +25 -3
  203. package/templates/.agents/rules/next-step-output.en.md +3 -3
  204. package/templates/.agents/rules/next-step-output.zh-CN.md +3 -3
  205. package/templates/.agents/rules/no-mid-flow-questions.en.md +1 -1
  206. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +1 -1
  207. package/templates/.agents/rules/pr-sync.en.md +23 -14
  208. package/templates/.agents/rules/pr-sync.zh-CN.md +23 -2
  209. package/templates/.agents/rules/release-commands.en.md +42 -3
  210. package/templates/.agents/rules/release-commands.zh-CN.md +42 -3
  211. package/templates/.agents/rules/review-handshake.en.md +1 -1
  212. package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
  213. package/templates/.agents/rules/security-alerts.en.md +17 -2
  214. package/templates/.agents/rules/security-alerts.zh-CN.md +17 -2
  215. package/templates/.agents/rules/sync-content-generation.en.md +8 -0
  216. package/templates/.agents/rules/sync-content-generation.zh-CN.md +8 -0
  217. package/templates/.agents/rules/task-management.en.md +2 -3
  218. package/templates/.agents/rules/task-management.zh-CN.md +2 -3
  219. package/templates/.agents/scripts/security-alerts.github.sh +151 -0
  220. package/templates/.agents/scripts/security-alerts.sh +31 -0
  221. package/templates/.agents/skills/analyze-task/SKILL.en.md +4 -3
  222. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +4 -3
  223. package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
  224. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
  225. package/templates/.agents/skills/cancel-task/SKILL.en.md +2 -2
  226. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
  227. package/templates/.agents/skills/check-task/SKILL.en.md +2 -2
  228. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +2 -2
  229. package/templates/.agents/skills/close-codescan/SKILL.en.md +3 -3
  230. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -3
  231. package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -3
  232. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -3
  233. package/templates/.agents/skills/code-task/SKILL.en.md +8 -5
  234. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +9 -6
  235. package/templates/.agents/skills/commit/SKILL.en.md +30 -10
  236. package/templates/.agents/skills/commit/SKILL.zh-CN.md +6 -7
  237. package/templates/.agents/skills/commit/config/verify.json +1 -1
  238. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +3 -3
  239. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +3 -3
  240. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  241. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  242. package/templates/.agents/skills/commit/reference/task-status-update.en.md +2 -2
  243. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +2 -2
  244. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +7 -6
  245. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +7 -6
  246. package/templates/.agents/skills/complete-manual-validation/config/verify.en.json +1 -1
  247. package/templates/.agents/skills/complete-manual-validation/config/verify.zh-CN.json +1 -1
  248. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
  249. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
  250. package/templates/.agents/skills/complete-task/SKILL.en.md +13 -13
  251. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +13 -13
  252. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +2 -2
  253. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +2 -2
  254. package/templates/.agents/skills/create-pr/SKILL.en.md +9 -11
  255. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +9 -11
  256. package/templates/.agents/skills/create-pr/reference/change-report.en.md +9 -4
  257. package/templates/.agents/skills/create-pr/reference/change-report.zh-CN.md +9 -4
  258. package/templates/.agents/skills/create-pr/scripts/change-report.mjs +203 -0
  259. package/templates/.agents/skills/create-release-note/SKILL.en.md +5 -6
  260. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +5 -6
  261. package/templates/.agents/skills/create-task/SKILL.en.md +2 -1
  262. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +2 -1
  263. package/templates/.agents/skills/import-codescan/SKILL.en.md +1 -1
  264. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +1 -1
  265. package/templates/.agents/skills/import-dependabot/SKILL.en.md +1 -1
  266. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +1 -1
  267. package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
  268. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
  269. package/templates/.agents/skills/init-labels/SKILL.en.md +9 -10
  270. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +9 -10
  271. package/templates/.agents/skills/init-labels/scripts/init-labels.github.sh +57 -1
  272. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +27 -1
  273. package/templates/.agents/skills/init-milestones/SKILL.en.md +3 -5
  274. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +3 -5
  275. package/templates/.agents/skills/plan-task/SKILL.en.md +4 -3
  276. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +4 -3
  277. package/templates/.agents/skills/restore-task/SKILL.en.md +1 -1
  278. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
  279. package/templates/.agents/skills/review-analysis/SKILL.en.md +8 -5
  280. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +8 -5
  281. package/templates/.agents/skills/review-analysis/config/verify.en.json +1 -0
  282. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +1 -0
  283. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +18 -0
  284. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +18 -0
  285. package/templates/.agents/skills/review-code/SKILL.en.md +11 -8
  286. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +11 -8
  287. package/templates/.agents/skills/review-code/config/verify.en.json +1 -0
  288. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +1 -0
  289. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +2 -1
  290. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +2 -1
  291. package/templates/.agents/skills/review-code/reference/output-templates.en.md +19 -1
  292. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +19 -1
  293. package/templates/.agents/skills/review-code/reference/report-template.en.md +4 -2
  294. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +4 -2
  295. package/templates/.agents/skills/review-plan/SKILL.en.md +8 -5
  296. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +8 -5
  297. package/templates/.agents/skills/review-plan/config/verify.en.json +1 -0
  298. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +1 -0
  299. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +18 -0
  300. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +18 -0
  301. package/templates/.agents/skills/review-pr/SKILL.en.md +1 -0
  302. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +1 -0
  303. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +2 -2
  304. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +2 -2
  305. package/templates/.agents/skills/run-manual-validation/SKILL.en.md +5 -0
  306. package/templates/.agents/skills/run-manual-validation/SKILL.zh-CN.md +5 -0
  307. package/templates/.agents/skills/run-task/SKILL.en.md +5 -1
  308. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +5 -1
  309. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +2 -2
  310. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +2 -2
  311. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +144 -109
  312. package/templates/.agents/skills/watch-pr/SKILL.en.md +5 -5
  313. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +5 -5
  314. package/templates/.agents/templates/task.en.md +6 -2
  315. package/templates/.agents/templates/task.zh-CN.md +6 -2
  316. package/templates/.agents/workflows/bug-fix.en.yaml +9 -8
  317. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +9 -8
  318. package/templates/.agents/workflows/feature-development.en.yaml +9 -8
  319. package/templates/.agents/workflows/feature-development.zh-CN.yaml +9 -8
  320. package/templates/.agents/workflows/refactoring.en.yaml +9 -8
  321. package/templates/.agents/workflows/refactoring.zh-CN.yaml +9 -8
  322. package/templates/.claude/commands/analyze-task.en.md +1 -1
  323. package/templates/.claude/commands/analyze-task.zh-CN.md +1 -1
  324. package/templates/.claude/commands/block-task.en.md +1 -1
  325. package/templates/.claude/commands/block-task.zh-CN.md +1 -1
  326. package/templates/.claude/commands/cancel-task.en.md +1 -1
  327. package/templates/.claude/commands/cancel-task.zh-CN.md +1 -1
  328. package/templates/.claude/commands/check-task.en.md +1 -1
  329. package/templates/.claude/commands/check-task.zh-CN.md +1 -1
  330. package/templates/.claude/commands/code-task.en.md +1 -1
  331. package/templates/.claude/commands/code-task.zh-CN.md +1 -1
  332. package/templates/.claude/commands/commit.en.md +1 -1
  333. package/templates/.claude/commands/commit.zh-CN.md +1 -1
  334. package/templates/.claude/commands/complete-manual-validation.en.md +1 -1
  335. package/templates/.claude/commands/complete-manual-validation.zh-CN.md +1 -1
  336. package/templates/.claude/commands/complete-task.en.md +1 -1
  337. package/templates/.claude/commands/complete-task.zh-CN.md +1 -1
  338. package/templates/.claude/commands/create-pr.en.md +1 -1
  339. package/templates/.claude/commands/create-pr.zh-CN.md +1 -1
  340. package/templates/.claude/commands/plan-task.en.md +1 -1
  341. package/templates/.claude/commands/plan-task.zh-CN.md +1 -1
  342. package/templates/.claude/commands/review-analysis.en.md +1 -1
  343. package/templates/.claude/commands/review-analysis.zh-CN.md +1 -1
  344. package/templates/.claude/commands/review-code.en.md +1 -1
  345. package/templates/.claude/commands/review-code.zh-CN.md +1 -1
  346. package/templates/.claude/commands/review-plan.en.md +1 -1
  347. package/templates/.claude/commands/review-plan.zh-CN.md +1 -1
  348. package/templates/.claude/commands/run-manual-validation.en.md +1 -1
  349. package/templates/.claude/commands/run-manual-validation.zh-CN.md +1 -1
  350. package/templates/.claude/commands/run-task.en.md +1 -1
  351. package/templates/.claude/commands/run-task.zh-CN.md +1 -1
  352. package/templates/.claude/commands/watch-pr.en.md +1 -1
  353. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  354. package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.en.md +0 -1
  355. package/templates/{.trae/skills → .traecli/commands}/update-agent-infra.zh-CN.md +0 -1
  356. package/dist/lib/builtin-tuis.js +0 -40
  357. package/lib/builtin-tuis.ts +0 -56
  358. package/templates/.agents/rules/create-issue.github.en.md +0 -14
  359. package/templates/.agents/rules/create-issue.github.zh-CN.md +0 -27
  360. package/templates/.agents/rules/issue-pr-commands.github.en.md +0 -34
  361. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +0 -49
  362. package/templates/.agents/rules/issue-sync.github.en.md +0 -40
  363. package/templates/.agents/rules/issue-sync.github.zh-CN.md +0 -76
  364. package/templates/.agents/rules/label-milestone-setup.github.en.md +0 -60
  365. package/templates/.agents/rules/label-milestone-setup.github.zh-CN.md +0 -60
  366. package/templates/.agents/rules/milestone-inference.github.en.md +0 -15
  367. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +0 -27
  368. package/templates/.agents/rules/pr-checks-commands.github.en.md +0 -18
  369. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +0 -34
  370. package/templates/.agents/rules/pr-sync.github.en.md +0 -43
  371. package/templates/.agents/rules/pr-sync.github.zh-CN.md +0 -69
  372. package/templates/.agents/rules/release-commands.github.en.md +0 -38
  373. package/templates/.agents/rules/release-commands.github.zh-CN.md +0 -38
  374. package/templates/.agents/rules/security-alerts.github.en.md +0 -43
  375. package/templates/.agents/rules/security-alerts.github.zh-CN.md +0 -43
@@ -414,8 +414,6 @@ function readProcessingSnapshot(root) {
414
414
  export function captureSandboxControlCutoverSnapshot(root) {
415
415
  const resolvedRoot = path.resolve(root);
416
416
  const manifestPath = path.join(resolvedRoot, 'manifest.json');
417
- if (fs.existsSync(manifestPath))
418
- readSandboxControlManifestForTransition(manifestPath);
419
417
  return {
420
418
  root: resolvedRoot,
421
419
  manifestRaw: readOptionalRaw(manifestPath),
@@ -430,7 +428,7 @@ export function assertSandboxControlCutoverSnapshot(snapshot) {
430
428
  throw new Error('SANDBOX_CONTROL_OWNER_TRANSITION');
431
429
  }
432
430
  }
433
- function readSandboxControlManifestValue(manifestPath, allowLegacy) {
431
+ function readSandboxControlManifestValue(manifestPath) {
434
432
  if (!regularFile(manifestPath))
435
433
  throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
436
434
  let manifest;
@@ -445,25 +443,35 @@ function readSandboxControlManifestValue(manifestPath, allowLegacy) {
445
443
  throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
446
444
  }
447
445
  const candidate = manifest;
448
- if (candidate.version !== 4 && candidate.version !== 5) {
449
- throw new Error('SANDBOX_CONTROL_MANIFEST_VERSION_INVALID: expected version 5; container-only recreation is required');
446
+ const expectedKeys = [
447
+ 'branch', 'channelDir', 'container', 'containerIdentity', 'engine', 'generation',
448
+ 'mode', 'processingDir', 'project', 'publicStatusDir', 'repoRoot', 'runtimeDir',
449
+ 'taskId', 'token', 'worktreeRoot'
450
+ ];
451
+ if (Object.keys(manifest).sort().join(',') !== expectedKeys.sort().join(',')) {
452
+ throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
450
453
  }
451
454
  if (typeof candidate.containerIdentity !== 'object' || candidate.containerIdentity === null
452
455
  || Array.isArray(candidate.containerIdentity))
453
456
  throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
454
457
  const containerIdentity = candidate.containerIdentity;
455
- if (typeof candidate.engine !== 'string' || candidate.engine.length === 0
458
+ if (Object.keys(containerIdentity).sort().join(',') !== 'id,labels'
459
+ || typeof candidate.engine !== 'string' || candidate.engine.length === 0
456
460
  || typeof candidate.repoRoot !== 'string' || typeof candidate.worktreeRoot !== 'string'
457
461
  || typeof candidate.project !== 'string' || typeof candidate.container !== 'string'
458
462
  || typeof containerIdentity.id !== 'string' || containerIdentity.id.length === 0
459
463
  || typeof containerIdentity.labels !== 'object' || containerIdentity.labels === null
460
464
  || Array.isArray(containerIdentity.labels)
461
465
  || Object.values(containerIdentity.labels).some((value) => typeof value !== 'string')
462
- || typeof candidate.branch !== 'string' || !['task-bound', 'branch-only'].includes(candidate.mode ?? '')
466
+ || typeof candidate.branch !== 'string'
467
+ || (candidate.mode !== 'task-bound' && candidate.mode !== 'branch-only')
463
468
  || (candidate.taskId !== null && typeof candidate.taskId !== 'string')
464
469
  || typeof candidate.channelDir !== 'string' || typeof candidate.publicStatusDir !== 'string'
465
- || typeof candidate.processingDir !== 'string' || typeof candidate.token !== 'string'
466
- || typeof candidate.generation !== 'string')
470
+ || typeof candidate.processingDir !== 'string' || typeof candidate.runtimeDir !== 'string'
471
+ || typeof candidate.token !== 'string'
472
+ || typeof candidate.generation !== 'string'
473
+ || (candidate.mode === 'task-bound' && (typeof candidate.taskId !== 'string' || candidate.taskId.length === 0))
474
+ || (candidate.mode === 'branch-only' && candidate.taskId !== null))
467
475
  throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
468
476
  const root = path.dirname(path.resolve(manifestPath));
469
477
  if (path.resolve(candidate.channelDir) !== path.join(root, 'channel')
@@ -471,22 +479,33 @@ function readSandboxControlManifestValue(manifestPath, allowLegacy) {
471
479
  || path.resolve(candidate.processingDir) !== path.join(root, 'processing')) {
472
480
  throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
473
481
  }
474
- if (candidate.version === 4) {
475
- if (!allowLegacy) {
476
- throw new Error('SANDBOX_CONTROL_MANIFEST_VERSION_INVALID: expected version 5; container-only recreation is required');
477
- }
478
- return candidate;
479
- }
480
- if (typeof candidate.runtimeDir !== 'string' || path.resolve(candidate.runtimeDir) !== path.join(root, 'runtime')) {
482
+ if (path.resolve(candidate.runtimeDir) !== path.join(root, 'runtime')) {
481
483
  throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
482
484
  }
483
- return candidate;
485
+ const normalized = {
486
+ engine: candidate.engine,
487
+ repoRoot: candidate.repoRoot,
488
+ worktreeRoot: candidate.worktreeRoot,
489
+ project: candidate.project,
490
+ container: candidate.container,
491
+ containerIdentity: {
492
+ id: containerIdentity.id,
493
+ labels: { ...containerIdentity.labels }
494
+ },
495
+ branch: candidate.branch,
496
+ mode: candidate.mode,
497
+ taskId: candidate.taskId,
498
+ token: candidate.token,
499
+ generation: candidate.generation,
500
+ channelDir: candidate.channelDir,
501
+ publicStatusDir: candidate.publicStatusDir,
502
+ processingDir: candidate.processingDir,
503
+ runtimeDir: candidate.runtimeDir
504
+ };
505
+ return normalized;
484
506
  }
485
507
  export function readSandboxControlManifest(manifestPath) {
486
- return readSandboxControlManifestValue(manifestPath, false);
487
- }
488
- export function readSandboxControlManifestForTransition(manifestPath) {
489
- return readSandboxControlManifestValue(manifestPath, true);
508
+ return readSandboxControlManifestValue(manifestPath);
490
509
  }
491
510
  function readBrokerOwner(filePath) {
492
511
  if (!fs.existsSync(filePath))
@@ -507,6 +526,18 @@ function readBrokerOwner(filePath) {
507
526
  throw new Error('SANDBOX_CONTROL_OWNER_MISMATCH');
508
527
  return owner;
509
528
  }
529
+ export function assertSandboxControlBrokerOwner(manifest, options = {}) {
530
+ const root = path.dirname(path.resolve(manifest.publicStatusDir));
531
+ const owner = readBrokerOwner(path.join(root, 'broker.json'));
532
+ if (!owner)
533
+ throw new Error('SANDBOX_CONTROL_OWNER_EVIDENCE_MISSING');
534
+ if (owner.token !== manifest.token || owner.generation !== manifest.generation) {
535
+ throw new Error('SANDBOX_CONTROL_OWNER_MISMATCH');
536
+ }
537
+ if (!ownerLive(owner, options.identityProbe))
538
+ throw new Error('SANDBOX_CONTROL_OWNER_LOST');
539
+ return owner;
540
+ }
510
541
  function readStatusOwner(filePath) {
511
542
  if (!fs.existsSync(filePath))
512
543
  return null;
@@ -641,7 +672,7 @@ export async function quiesceSandboxControlRoot(root, options = {}) {
641
672
  markSandboxControlRootQuiescing(resolvedRoot);
642
673
  await waitForStartupTransition(resolvedRoot, Math.max(0, forceAt - Date.now()), identityProbe);
643
674
  const manifestPath = path.join(resolvedRoot, 'manifest.json');
644
- const manifest = fs.existsSync(manifestPath) ? readSandboxControlManifestForTransition(manifestPath) : null;
675
+ const manifest = fs.existsSync(manifestPath) ? readSandboxControlManifest(manifestPath) : null;
645
676
  const broker = readBrokerOwner(path.join(resolvedRoot, 'broker.json'));
646
677
  const status = readStatusOwner(path.join(resolvedRoot, 'public', 'status.json'));
647
678
  const statusOwner = status ? status.broker : null;
@@ -730,7 +761,7 @@ export async function removeSandboxControlRoot(root, options) {
730
761
  if (!stat.isDirectory() || stat.isSymbolicLink())
731
762
  throw new Error('SANDBOX_CONTROL_CHANNEL_INVALID');
732
763
  const manifestPath = path.join(resolvedRoot, 'manifest.json');
733
- const manifest = readSandboxControlManifestForTransition(manifestPath);
764
+ const manifest = readSandboxControlManifest(manifestPath);
734
765
  const inspectContainer = options.inspectContainer
735
766
  ?? ((timeoutMs) => inspectSandboxControlContainer(manifest, { timeoutMs }));
736
767
  const timeoutMs = options.timeoutMs ?? options.timing?.quiesceDeadlineMs ?? DEFAULT_QUIESCE_TIMEOUT_MS;
@@ -792,7 +823,7 @@ export async function removeSandboxControlRoot(root, options) {
792
823
  : 'SANDBOX_CONTROL_CONTAINER_STILL_EXISTS');
793
824
  }
794
825
  }
795
- const currentManifest = readSandboxControlManifestForTransition(manifestPath);
826
+ const currentManifest = readSandboxControlManifest(manifestPath);
796
827
  if (currentManifest.token !== manifest.token || currentManifest.generation !== manifest.generation
797
828
  || !isSandboxControlRootQuiescing(resolvedRoot)) {
798
829
  throw new Error('SANDBOX_CONTROL_OWNER_TRANSITION');
@@ -197,6 +197,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
197
197
  if (!recoverProcessing(manifest, brokerOwns))
198
198
  return;
199
199
  while (!signal.aborted) {
200
+ let settledExecution = null;
200
201
  if (!brokerOwns())
201
202
  break;
202
203
  let current;
@@ -255,22 +256,7 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
255
256
  if (active?.settled) {
256
257
  if (!brokerOwns())
257
258
  break;
258
- if (active.result) {
259
- if (!brokerOwns())
260
- break;
261
- writeResponse(manifest, {
262
- version: 2, id: active.request.id, phase: 'completed', exitCode: active.result.exitCode,
263
- stdout: active.result.stdout, stderr: active.result.stderr, error: null
264
- });
265
- }
266
- else {
267
- if (!brokerOwns())
268
- break;
269
- writeResponse(manifest, unknown(active.request.id));
270
- }
271
- if (!brokerOwns())
272
- break;
273
- fs.rmSync(path.join(manifest.processingDir, active.request.id), { recursive: true, force: true });
259
+ settledExecution = active;
274
260
  active = null;
275
261
  }
276
262
  let reasonCode = null;
@@ -313,6 +299,22 @@ export async function serveSandboxControl(manifestPath, signal = new AbortContro
313
299
  appendSandboxControlAudit(manifest, 'broker-state', { state, reasonCode, requestId: active?.request.id ?? null });
314
300
  lastState = stateKey;
315
301
  }
302
+ if (settledExecution) {
303
+ if (!brokerOwns())
304
+ break;
305
+ if (settledExecution.result) {
306
+ writeResponse(manifest, {
307
+ version: 2, id: settledExecution.request.id, phase: 'completed', exitCode: settledExecution.result.exitCode,
308
+ stdout: settledExecution.result.stdout, stderr: settledExecution.result.stderr, error: null
309
+ });
310
+ }
311
+ else {
312
+ writeResponse(manifest, unknown(settledExecution.request.id));
313
+ }
314
+ if (!brokerOwns())
315
+ break;
316
+ fs.rmSync(path.join(manifest.processingDir, settledExecution.request.id), { recursive: true, force: true });
317
+ }
316
318
  let retiring = false;
317
319
  for (const name of fs.readdirSync(requestsDir).sort()) {
318
320
  if (!/^[a-f0-9-]{16,64}\.json$/.test(name))
@@ -2,7 +2,8 @@ import path from 'node:path';
2
2
  import { shareBranchDir, shareCommonDir, shellConfigDirCandidates, worktreeDirCandidates } from './constants.js';
3
3
  import { sandboxWorkspaceViewStatePaths } from './workspace-view.js';
4
4
  export function sandboxCoreBindMounts(config, branch, overrides) {
5
- const taskBound = Boolean(overrides.taskSource && overrides.taskId);
5
+ const taskSources = overrides.taskSources ?? [];
6
+ const taskBound = Boolean(taskSources.length > 0 && overrides.taskId);
6
7
  const workspaceMounts = sandboxWorkspaceViewStatePaths(overrides.workspaceViewRoot).map(({ state, hostPath }) => taskBound && state === 'active'
7
8
  ? {
8
9
  hostPaths: [path.join(hostPath, '.short-ids.json')],
@@ -26,7 +27,7 @@ export function sandboxCoreBindMounts(config, branch, overrides) {
26
27
  ];
27
28
  if (taskBound) {
28
29
  mounts.push({
29
- hostPaths: [overrides.taskSource],
30
+ hostPaths: taskSources,
30
31
  containerPath: path.posix.join('/workspace/.agents/workspace/active', overrides.taskId),
31
32
  readOnly: false
32
33
  });
@@ -9,6 +9,7 @@ import { createSandboxCapabilityPlan } from './agent-client-reconciler.js';
9
9
  import { toEnginePath } from './engines/wsl2-paths.js';
10
10
  import { sandboxCoreBindMounts } from './mounts.js';
11
11
  import { assertSandboxTaskSource, sandboxControlPaths, sandboxWorkspaceViewPaths } from './workspace-view.js';
12
+ import { resolveTaskWorkspace } from './task-resolver.js';
12
13
  import { parseSandboxWorkspaceIdentity, sameSandboxWorkspaceIdentity } from './workspace-identity.js';
13
14
  import { inspectWorktree } from './worktree-safety.js';
14
15
  import { commandForEngine, runEngine, runOkEngine, runProbe, runVerboseEngine } from './shell.js';
@@ -407,6 +408,66 @@ function sourceCandidates(engine, hostPath) {
407
408
  }
408
409
  return [...new Set(candidates.map((candidate) => normalizeMountSource(engine, candidate)))];
409
410
  }
411
+ function recoveryTaskSources(repoRoot, taskId) {
412
+ const activePath = path.join(repoRoot, '.agents', 'workspace', 'active', taskId);
413
+ const completedPath = path.join(repoRoot, '.agents', 'workspace', 'completed', taskId);
414
+ if (fs.existsSync(activePath) && !fs.existsSync(completedPath)) {
415
+ const source = assertSandboxTaskSource(repoRoot, taskId);
416
+ return { mountPaths: [source], accessiblePaths: [source] };
417
+ }
418
+ const task = resolveTaskWorkspace(taskId, repoRoot);
419
+ if (task.state === 'active') {
420
+ const source = assertSandboxTaskSource(repoRoot, taskId);
421
+ return { mountPaths: [source], accessiblePaths: [source] };
422
+ }
423
+ if (task.state !== 'completed') {
424
+ throw new Error(`SANDBOX_TASK_SOURCE_STATE_UNSUPPORTED: task ${taskId} is ${task.state}`);
425
+ }
426
+ const completedRoot = fs.realpathSync.native(path.join(repoRoot, '.agents', 'workspace', 'completed'));
427
+ const completedSource = fs.realpathSync.native(path.dirname(task.taskMd));
428
+ if (path.dirname(completedSource) !== completedRoot) {
429
+ throw new Error(`SANDBOX_TASK_SOURCE_INVALID: ${completedSource} is not under completed workspace`);
430
+ }
431
+ const historicalActivePath = path.join(repoRoot, '.agents', 'workspace', 'active', taskId);
432
+ const mountPaths = [completedSource, historicalActivePath];
433
+ if (fs.existsSync(historicalActivePath)) {
434
+ try {
435
+ mountPaths[1] = fs.realpathSync.native(historicalActivePath);
436
+ }
437
+ catch {
438
+ // Keep the exact historical path as a candidate; the current completed
439
+ // source remains the only source that must still be accessible.
440
+ }
441
+ }
442
+ return { mountPaths, accessiblePaths: [completedSource] };
443
+ }
444
+ function startCompletedSandboxContainer(params) {
445
+ const taskSources = recoveryTaskSources(params.config.repoRoot, params.taskId);
446
+ const completedSource = taskSources.accessiblePaths[0];
447
+ const activeRoot = path.join(params.config.repoRoot, '.agents', 'workspace', 'active');
448
+ const historicalSource = path.join(activeRoot, params.taskId);
449
+ fs.mkdirSync(activeRoot, { recursive: true });
450
+ try {
451
+ fs.lstatSync(historicalSource);
452
+ throw new Error(`SANDBOX_COMPLETED_SOURCE_CONFLICT: historical task source already exists at ${historicalSource}`);
453
+ }
454
+ catch (error) {
455
+ if (!(error instanceof Error && 'code' in error && error.code === 'ENOENT'))
456
+ throw error;
457
+ }
458
+ fs.symlinkSync(completedSource, historicalSource, process.platform === 'win32' ? 'junction' : 'dir');
459
+ try {
460
+ params.start(params.engine, params.container);
461
+ }
462
+ finally {
463
+ const temporarySource = fs.lstatSync(historicalSource);
464
+ const resolvedSource = fs.realpathSync.native(historicalSource);
465
+ if (!temporarySource.isSymbolicLink() || resolvedSource !== completedSource) {
466
+ throw new Error(`SANDBOX_COMPLETED_SOURCE_CHANGED: refusing to remove unexpected path at ${historicalSource}`);
467
+ }
468
+ fs.unlinkSync(historicalSource);
469
+ }
470
+ }
410
471
  function sameHostSource(left, right) {
411
472
  try {
412
473
  const leftStat = fs.statSync(left, { bigint: true });
@@ -440,21 +501,29 @@ function expectedMounts(params) {
440
501
  container: params.container,
441
502
  identity: params.workspace
442
503
  });
504
+ const taskSources = params.workspace.mode === 'task-bound'
505
+ ? recoveryTaskSources(config.repoRoot, params.workspace.taskId)
506
+ : null;
507
+ const taskId = params.workspace.mode === 'task-bound' ? params.workspace.taskId : null;
443
508
  const core = sandboxCoreBindMounts(config, branch, {
444
509
  workspaceViewRoot: view.root,
445
510
  controlDir: control.channelDir,
446
511
  controlStatusDir: control.statusDir,
447
- ...(params.workspace.mode === 'task-bound'
448
- ? {
512
+ ...(taskSources === null
513
+ ? {}
514
+ : {
449
515
  runtimeDir: control.runtimeDir,
450
- taskSource: assertSandboxTaskSource(config.repoRoot, params.workspace.taskId),
451
- taskId: params.workspace.taskId
452
- }
453
- : {})
516
+ taskSources: taskSources.mountPaths,
517
+ taskId: taskId
518
+ })
454
519
  }).map((mount) => ({
455
520
  path: mount.containerPath,
456
521
  expectedType: 'bind',
457
522
  hostPaths: mount.hostPaths,
523
+ ...(taskSources !== null && taskId !== null
524
+ && mount.containerPath === `/workspace/.agents/workspace/active/${taskId}`
525
+ ? { sourceAccessiblePaths: taskSources.accessiblePaths }
526
+ : {}),
458
527
  expectedRW: !mount.readOnly
459
528
  }));
460
529
  const live = tools.flatMap((tool) => (tool.hostLiveMounts ?? []).flatMap(({ hostPath, containerSubpath }) => {
@@ -469,9 +538,7 @@ function expectedMounts(params) {
469
538
  }];
470
539
  }));
471
540
  const persistentTools = tools
472
- .filter((tool) => !tool.tmpfs
473
- && (config.agentClientSource === 'canonical'
474
- || actualMounts.has(tool.containerMount)))
541
+ .filter((tool) => !tool.tmpfs)
475
542
  .map((tool) => ({
476
543
  path: tool.containerMount,
477
544
  expectedType: 'bind',
@@ -577,13 +644,10 @@ export function collectSandboxRecoverySnapshot(params) {
577
644
  taskId: sandboxTaskIdLabel(params.config)
578
645
  });
579
646
  const selectedToolIds = new Set(tools.map((tool) => tool.id));
580
- const enforceCanonicalPlan = params.config.agentClientSource === 'canonical';
581
- const unexpectedCapabilityMounts = enforceCanonicalPlan
582
- ? capabilityPlan.cleanupInventory
583
- .filter((tool) => isAgentClientId(tool.id) && !selectedToolIds.has(tool.id))
584
- .map((tool) => tool.containerMount)
585
- .filter((mountPath) => mountsByDestination.has(mountPath))
586
- : [];
647
+ const unexpectedCapabilityMounts = capabilityPlan.cleanupInventory
648
+ .filter((tool) => isAgentClientId(tool.id) && !selectedToolIds.has(tool.id))
649
+ .map((tool) => tool.containerMount)
650
+ .filter((mountPath) => mountsByDestination.has(mountPath));
587
651
  const mountSnapshots = expectedMounts({
588
652
  config: params.config,
589
653
  branch: params.branch,
@@ -616,7 +680,7 @@ export function collectSandboxRecoverySnapshot(params) {
616
680
  sourceAccessible: expected.expectedType === 'tmpfs'
617
681
  ? true
618
682
  : matchedHostPath !== undefined
619
- && hostSourceAccessible(matchedHostPath, expected.expectedRW)
683
+ && (expected.sourceAccessiblePaths ?? [matchedHostPath]).some((hostPath) => hostSourceAccessible(hostPath, expected.expectedRW))
620
684
  };
621
685
  });
622
686
  return {
@@ -636,9 +700,7 @@ export function collectSandboxRecoverySnapshot(params) {
636
700
  identityOk: typeof inspection.Id === 'string' && inspection.Id.length > 0
637
701
  && branchLabel === params.branch
638
702
  && sameSandboxWorkspaceIdentity(containerWorkspace, workspace),
639
- runtimeCapabilityOk: enforceCanonicalPlan
640
- ? labels[sandboxRuntimeCapabilityLabel(params.config)] === capabilityPlan.runtimeSignature
641
- : undefined,
703
+ runtimeCapabilityOk: labels[sandboxRuntimeCapabilityLabel(params.config)] === capabilityPlan.runtimeSignature,
642
704
  unexpectedCapabilityMounts,
643
705
  mounts: mountSnapshots,
644
706
  tmpfs,
@@ -813,11 +875,22 @@ export async function ensureSandboxReady(params) {
813
875
  workspace: params.workspace ?? { mode: 'branch-only' }
814
876
  });
815
877
  }
816
- if (params.forceRecreate) {
878
+ if (params.forceRecreate || (params.reentry === 'completed' && params.allowRecreate)) {
817
879
  throw new Error('Explicit container recreation requested.');
818
880
  }
819
881
  if (!params.row.running) {
820
- startFn(params.engine, params.row.name);
882
+ if (params.reentry === 'completed' && params.workspace?.mode === 'task-bound') {
883
+ startCompletedSandboxContainer({
884
+ config: params.config,
885
+ engine: params.engine,
886
+ container: params.row.name,
887
+ taskId: params.workspace.taskId,
888
+ start: startFn
889
+ });
890
+ }
891
+ else {
892
+ startFn(params.engine, params.row.name);
893
+ }
821
894
  const initial = assess({
822
895
  config: params.config,
823
896
  engine: params.engine,
@@ -930,6 +1003,34 @@ export async function ensureSandboxReady(params) {
930
1003
  catch (error) {
931
1004
  failure = error instanceof Error ? error : new Error(String(error));
932
1005
  }
1006
+ if (params.reentry === 'completed') {
1007
+ if (params.workspace?.mode !== 'task-bound') {
1008
+ throw new Error('SANDBOX_COMPLETED_REENTRY_INVALID: completed re-entry requires a task-bound workspace');
1009
+ }
1010
+ const taskId = params.workspace.taskId;
1011
+ const commandChain = [
1012
+ `docker exec -it ${params.row.name} bash /usr/local/bin/sandbox-tmux-entry`,
1013
+ `ai sandbox rm ${taskId}`,
1014
+ `ai sandbox create ${params.branch}`
1015
+ ];
1016
+ const code = params.allowRecreate || params.forceRecreate
1017
+ ? 'SANDBOX_COMPLETED_RECREATE_UNSUPPORTED'
1018
+ : 'SANDBOX_COMPLETED_REENTRY_FAILED';
1019
+ const lead = code === 'SANDBOX_COMPLETED_RECREATE_UNSUPPORTED'
1020
+ ? 'Completed sandbox replacement is unsupported; the existing container was not replaced.'
1021
+ : 'Completed sandbox recovery failed; the existing container was not replaced.';
1022
+ throw new Error([
1023
+ `${code}: ${lead}`,
1024
+ `Task: ${taskId}; container: ${params.row.name}; branch: ${params.branch}.`,
1025
+ 'To preserve the current container state, enter it manually with:',
1026
+ ` ${commandChain[0]}`,
1027
+ 'If you intentionally want a replacement, the following is the full interactive sandbox cleanup, not a container-only delete.',
1028
+ 'It may remove the worktree, local branch, tool/shell state, and branch share:',
1029
+ ` ${commandChain[1]}`,
1030
+ ` ${commandChain[2]}`,
1031
+ `Details: ${failure.message}`
1032
+ ].join('\n'));
1033
+ }
933
1034
  const dataBoundary = 'The existing container writable layer, ordinary /tmp data, processes, tmux sessions, and RAM state may be lost by replacement; worktree and host-managed sandbox data are preserved.';
934
1035
  const recoveryTarget = params.workspace?.mode === 'task-bound'
935
1036
  ? params.workspace.taskId
@@ -79,6 +79,20 @@ export function resolveSandboxTarget(requestedRef, repoRoot) {
79
79
  }
80
80
  return { requestedRef, branch: requestedRef, workspace: { mode: 'branch-only' } };
81
81
  }
82
+ export function resolveSandboxReentryContext(params) {
83
+ if (params.target.workspace.mode !== 'branch-only'
84
+ || params.containerWorkspace.mode !== 'task-bound') {
85
+ return { workspace: params.target.workspace, reentry: 'standard' };
86
+ }
87
+ const task = resolveTaskWorkspace(params.containerWorkspace.taskId, params.repoRoot);
88
+ if (task.state !== 'completed' || task.branch !== params.target.branch) {
89
+ return { workspace: params.target.workspace, reentry: 'standard' };
90
+ }
91
+ return {
92
+ workspace: { mode: 'task-bound', taskId: task.taskId },
93
+ reentry: 'completed'
94
+ };
95
+ }
82
96
  export function resolveSandboxCleanupTarget(requestedRef, repoRoot, options = {}) {
83
97
  if (isRemovedHashShortIdInput(requestedRef)) {
84
98
  throw new Error(`Invalid task short id '${requestedRef}': task short ids must use bare digits`);
@@ -171,14 +171,12 @@ export function materializeSandboxControl(params) {
171
171
  const token = randomBytes(32).toString('hex');
172
172
  const generation = randomBytes(16).toString('hex');
173
173
  const repoRoot = fs.realpathSync.native(params.repoRoot);
174
- const manifest = {
175
- version: 5,
174
+ const manifestDraft = {
176
175
  engine: params.engine ?? 'docker',
177
176
  repoRoot,
178
177
  worktreeRoot: fs.realpathSync.native(params.worktreeRoot),
179
178
  project: params.project,
180
179
  container: params.container,
181
- containerIdentity: { id: '', labels: {} },
182
180
  branch: params.branch,
183
181
  mode: params.identity.mode,
184
182
  taskId: params.identity.mode === 'task-bound' ? params.identity.taskId : null,
@@ -189,16 +187,16 @@ export function materializeSandboxControl(params) {
189
187
  processingDir,
190
188
  runtimeDir
191
189
  };
192
- fs.writeFileSync(manifestPath, `${JSON.stringify(manifest)}\n`, { mode: 0o600 });
193
- return { root, channelDir, statusDir, runtimeDir, manifestPath, token, generation };
190
+ return { root, channelDir, statusDir, runtimeDir, manifestPath, manifestDraft, token, generation };
194
191
  }
195
192
  export function finalizeSandboxControlManifest(setup, identity) {
196
193
  if (!identity.engine || !identity.id)
197
194
  throw new Error('SANDBOX_CONTROL_CONTAINER_ID_INVALID');
198
- const manifest = JSON.parse(fs.readFileSync(setup.manifestPath, 'utf8'));
199
- manifest.version = 5;
200
- manifest.engine = identity.engine;
201
- manifest.containerIdentity = { id: identity.id, labels: { ...identity.labels } };
195
+ const manifest = {
196
+ ...setup.manifestDraft,
197
+ engine: identity.engine,
198
+ containerIdentity: { id: identity.id, labels: { ...identity.labels } }
199
+ };
202
200
  const temporary = `${setup.manifestPath}.${process.pid}.${randomBytes(8).toString('hex')}.tmp`;
203
201
  try {
204
202
  fs.writeFileSync(temporary, `${JSON.stringify(manifest)}\n`, { mode: 0o600, flag: 'wx' });
@@ -207,5 +205,6 @@ export function finalizeSandboxControlManifest(setup, identity) {
207
205
  finally {
208
206
  fs.rmSync(temporary, { force: true });
209
207
  }
208
+ return manifest;
210
209
  }
211
210
  //# sourceMappingURL=workspace-view.js.map