@fitlab-ai/agent-infra 0.9.2 → 0.9.4

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 +12 -9
  2. package/README.zh-CN.md +12 -9
  3. package/bin/cli.ts +12 -0
  4. package/dist/bin/cli.js +14 -0
  5. package/dist/bin/internal-cli.js +134 -108
  6. package/dist/lib/agent-clients/adapter.js +137 -4
  7. package/dist/lib/agent-clients/adapters/antigravity-cli.js +64 -0
  8. package/dist/lib/{sandbox/credentials.js → agent-clients/adapters/claude-code-credentials.js} +4 -14
  9. package/dist/lib/agent-clients/adapters/claude-code-refresh.js +85 -0
  10. package/dist/lib/agent-clients/adapters/claude-code-sandbox.js +247 -0
  11. package/dist/lib/agent-clients/adapters/claude-code.js +27 -4
  12. package/dist/lib/agent-clients/adapters/codex.js +9 -0
  13. package/dist/lib/agent-clients/adapters/index.js +2 -2
  14. package/dist/lib/agent-clients/adapters/opencode-sandbox.js +108 -0
  15. package/dist/lib/agent-clients/adapters/opencode.js +24 -3
  16. package/dist/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
  17. package/dist/lib/agent-clients/config.js +42 -4
  18. package/dist/lib/agent-clients/next-steps.js +11 -2
  19. package/dist/lib/agent-clients/reconcile.js +2 -1
  20. package/dist/lib/agent-clients/registry.js +16 -2
  21. package/dist/lib/agent-clients/schema.js +18 -0
  22. package/dist/lib/agent-clients/tokens.js +49 -0
  23. package/dist/lib/agent-clients/types.js +1 -1
  24. package/dist/lib/defaults.json +1 -1
  25. package/dist/lib/git/workflow.js +54 -1
  26. package/dist/lib/internal/agent-client.js +61 -4
  27. package/dist/lib/internal/git-workflow.js +6 -1
  28. package/dist/lib/internal/platform-checks.js +3 -3
  29. package/dist/lib/internal/platform-comment.js +29 -2
  30. package/dist/lib/internal/platform-issue.js +7 -0
  31. package/dist/lib/internal/platform-pr-review.js +132 -0
  32. package/dist/lib/internal/platform-pr.js +20 -2
  33. package/dist/lib/internal/platform-release-notes.js +52 -16
  34. package/dist/lib/internal/pr-review-grade.js +140 -0
  35. package/dist/lib/internal/release-workflow.js +13 -6
  36. package/dist/lib/internal/sandbox-control.js +24 -0
  37. package/dist/lib/internal/task-activity.js +105 -0
  38. package/dist/lib/internal/task-event.js +17 -1
  39. package/dist/lib/internal/task-ledger.js +12 -3
  40. package/dist/lib/internal/task-lifecycle.js +26 -1
  41. package/dist/lib/internal/task-orchestration.js +167 -18
  42. package/dist/lib/internal/task-review.js +8 -3
  43. package/dist/lib/platform/adapters.js +8 -1
  44. package/dist/lib/platform/completion-backfill.js +122 -0
  45. package/dist/lib/platform/github-client.js +19 -2
  46. package/dist/lib/platform/issue-comments.js +3 -2
  47. package/dist/lib/platform/merged-pr-equivalence.js +115 -5
  48. package/dist/lib/platform/pr-checks.js +65 -42
  49. package/dist/lib/platform/pr-review.js +166 -0
  50. package/dist/lib/platform/pull-requests.js +371 -4
  51. package/dist/lib/platform/release-notes.js +85 -3
  52. package/dist/lib/platform/releases.js +13 -5
  53. package/dist/lib/pr-review/evidence-grading.js +229 -0
  54. package/dist/lib/process-data/index.js +239 -0
  55. package/dist/lib/process-data/normalize.js +198 -0
  56. package/dist/lib/process-data/privacy.js +33 -0
  57. package/dist/lib/process-data/reconcile.js +95 -0
  58. package/dist/lib/process-data/repair.js +58 -0
  59. package/dist/lib/process-data/sources.js +345 -0
  60. package/dist/lib/process-data/store.js +282 -0
  61. package/dist/lib/process-data/types.js +2 -0
  62. package/dist/lib/run/index.js +4 -3
  63. package/dist/lib/run/tui.js +10 -13
  64. package/dist/lib/sandbox/agent-client-reconciler.js +26 -2
  65. package/dist/lib/sandbox/capture.js +7 -2
  66. package/dist/lib/sandbox/commands/create.js +109 -248
  67. package/dist/lib/sandbox/commands/enter.js +15 -47
  68. package/dist/lib/sandbox/commands/list-running.js +16 -6
  69. package/dist/lib/sandbox/commands/ls.js +10 -6
  70. package/dist/lib/sandbox/commands/rebuild.js +4 -3
  71. package/dist/lib/sandbox/commands/refresh.js +4 -84
  72. package/dist/lib/sandbox/commands/rm.js +24 -3
  73. package/dist/lib/sandbox/commands/show.js +13 -4
  74. package/dist/lib/sandbox/commands/start.js +7 -4
  75. package/dist/lib/sandbox/config.js +2 -0
  76. package/dist/lib/sandbox/constants.js +6 -0
  77. package/dist/lib/sandbox/control/client.js +44 -0
  78. package/dist/lib/sandbox/control/protocol.js +35 -0
  79. package/dist/lib/sandbox/control/server.js +109 -0
  80. package/dist/lib/sandbox/dockerfile.js +36 -13
  81. package/dist/lib/sandbox/mounts.js +17 -4
  82. package/dist/lib/sandbox/recovery.js +128 -5
  83. package/dist/lib/sandbox/redaction.js +12 -0
  84. package/dist/lib/sandbox/runtimes/base.dockerfile +24 -74
  85. package/dist/lib/sandbox/workspace-identity.js +95 -0
  86. package/dist/lib/sandbox/workspace-view.js +103 -0
  87. package/dist/lib/task/activity-intent.js +307 -0
  88. package/dist/lib/task/artifact-lifecycle.js +2 -1
  89. package/dist/lib/task/commands/log.js +15 -14
  90. package/dist/lib/task/commit-finalization.js +267 -0
  91. package/dist/lib/task/commit-intent.js +165 -0
  92. package/dist/lib/task/decision-intents.js +31 -17
  93. package/dist/lib/task/delegation-receipts.js +25 -4
  94. package/dist/lib/task/events.js +67 -28
  95. package/dist/lib/task/finalization-artifacts.js +35 -0
  96. package/dist/lib/task/implementation-inputs.js +54 -7
  97. package/dist/lib/task/ledger-intents.js +35 -1
  98. package/dist/lib/task/lifecycle-execution.js +46 -0
  99. package/dist/lib/task/orchestration.js +753 -54
  100. package/dist/lib/task/resolve-ref.js +41 -1
  101. package/dist/lib/task/review-finalization.js +29 -9
  102. package/dist/lib/task/task-execution-lock.js +231 -0
  103. package/dist/lib/task/verification-checks.js +1 -1
  104. package/dist/lib/task/verification-engine.js +218 -2
  105. package/dist/lib/task/verification.js +4 -3
  106. package/dist/lib/task/workspace-snapshot.js +17 -5
  107. package/lib/agent-clients/adapter.ts +201 -4
  108. package/lib/agent-clients/adapters/antigravity-cli.ts +65 -0
  109. package/lib/{sandbox/credentials.ts → agent-clients/adapters/claude-code-credentials.ts} +3 -18
  110. package/lib/agent-clients/adapters/claude-code-refresh.ts +128 -0
  111. package/lib/agent-clients/adapters/claude-code-sandbox.ts +295 -0
  112. package/lib/agent-clients/adapters/claude-code.ts +31 -4
  113. package/lib/agent-clients/adapters/codex.ts +9 -0
  114. package/lib/agent-clients/adapters/index.ts +2 -2
  115. package/lib/agent-clients/adapters/opencode-sandbox.ts +136 -0
  116. package/lib/agent-clients/adapters/opencode.ts +27 -3
  117. package/lib/agent-clients/adapters/runtimes/claude-code.dockerfile +65 -0
  118. package/lib/agent-clients/config.ts +49 -4
  119. package/lib/agent-clients/next-steps.ts +15 -2
  120. package/lib/agent-clients/reconcile.ts +2 -1
  121. package/lib/agent-clients/registry.ts +19 -1
  122. package/lib/agent-clients/schema.ts +18 -0
  123. package/lib/agent-clients/tokens.ts +53 -0
  124. package/lib/agent-clients/types.ts +15 -1
  125. package/lib/defaults.json +1 -1
  126. package/lib/git/workflow.ts +66 -2
  127. package/lib/internal/agent-client.ts +59 -4
  128. package/lib/internal/git-workflow.ts +5 -1
  129. package/lib/internal/platform-checks.ts +4 -4
  130. package/lib/internal/platform-comment.ts +17 -2
  131. package/lib/internal/platform-issue.ts +4 -0
  132. package/lib/internal/platform-pr-review.ts +104 -0
  133. package/lib/internal/platform-pr.ts +14 -1
  134. package/lib/internal/platform-release-notes.ts +59 -16
  135. package/lib/internal/pr-review-grade.ts +130 -0
  136. package/lib/internal/release-workflow.ts +13 -6
  137. package/lib/internal/sandbox-control.ts +24 -0
  138. package/lib/internal/task-activity.ts +91 -0
  139. package/lib/internal/task-event.ts +9 -1
  140. package/lib/internal/task-ledger.ts +12 -3
  141. package/lib/internal/task-lifecycle.ts +26 -1
  142. package/lib/internal/task-orchestration.ts +123 -14
  143. package/lib/internal/task-review.ts +8 -3
  144. package/lib/platform/adapters.ts +29 -2
  145. package/lib/platform/completion-backfill.ts +134 -0
  146. package/lib/platform/github-client.ts +20 -2
  147. package/lib/platform/issue-comments.ts +3 -2
  148. package/lib/platform/merged-pr-equivalence.ts +127 -5
  149. package/lib/platform/pr-checks.ts +73 -46
  150. package/lib/platform/pr-review.ts +201 -0
  151. package/lib/platform/pull-requests.ts +444 -3
  152. package/lib/platform/release-notes.ts +100 -4
  153. package/lib/platform/releases.ts +16 -4
  154. package/lib/pr-review/evidence-grading.ts +318 -0
  155. package/lib/process-data/index.ts +224 -0
  156. package/lib/process-data/normalize.ts +202 -0
  157. package/lib/process-data/privacy.ts +41 -0
  158. package/lib/process-data/reconcile.ts +110 -0
  159. package/lib/process-data/repair.ts +64 -0
  160. package/lib/process-data/sources.ts +354 -0
  161. package/lib/process-data/store.ts +315 -0
  162. package/lib/process-data/types.ts +135 -0
  163. package/lib/run/index.ts +5 -3
  164. package/lib/run/tui.ts +14 -12
  165. package/lib/sandbox/agent-client-reconciler.ts +44 -2
  166. package/lib/sandbox/capture.ts +9 -2
  167. package/lib/sandbox/commands/create.ts +126 -280
  168. package/lib/sandbox/commands/enter.ts +13 -59
  169. package/lib/sandbox/commands/list-running.ts +23 -6
  170. package/lib/sandbox/commands/ls.ts +22 -6
  171. package/lib/sandbox/commands/rebuild.ts +4 -3
  172. package/lib/sandbox/commands/refresh.ts +4 -126
  173. package/lib/sandbox/commands/rm.ts +24 -3
  174. package/lib/sandbox/commands/show.ts +30 -4
  175. package/lib/sandbox/commands/start.ts +6 -4
  176. package/lib/sandbox/config.ts +4 -0
  177. package/lib/sandbox/constants.ts +8 -0
  178. package/lib/sandbox/control/client.ts +55 -0
  179. package/lib/sandbox/control/protocol.ts +75 -0
  180. package/lib/sandbox/control/server.ts +117 -0
  181. package/lib/sandbox/dockerfile.ts +66 -13
  182. package/lib/sandbox/mounts.ts +24 -4
  183. package/lib/sandbox/recovery.ts +163 -6
  184. package/lib/sandbox/redaction.ts +14 -0
  185. package/lib/sandbox/runtimes/base.dockerfile +24 -74
  186. package/lib/sandbox/tool-types.ts +16 -0
  187. package/lib/sandbox/workspace-identity.ts +123 -0
  188. package/lib/sandbox/workspace-view.ts +143 -0
  189. package/lib/server/server.schema.json +2 -2
  190. package/lib/task/activity-intent.ts +417 -0
  191. package/lib/task/artifact-lifecycle.ts +2 -1
  192. package/lib/task/commands/log.ts +15 -14
  193. package/lib/task/commit-finalization.ts +356 -0
  194. package/lib/task/commit-intent.ts +230 -0
  195. package/lib/task/decision-intents.ts +32 -12
  196. package/lib/task/delegation-receipts.ts +34 -5
  197. package/lib/task/events.ts +75 -30
  198. package/lib/task/finalization-artifacts.ts +58 -0
  199. package/lib/task/implementation-inputs.ts +62 -8
  200. package/lib/task/ledger-intents.ts +43 -3
  201. package/lib/task/lifecycle-execution.ts +106 -0
  202. package/lib/task/orchestration.ts +1019 -72
  203. package/lib/task/resolve-ref.ts +46 -1
  204. package/lib/task/review-finalization.ts +40 -9
  205. package/lib/task/task-execution-lock.ts +296 -0
  206. package/lib/task/verification-checks.ts +1 -1
  207. package/lib/task/verification-engine.ts +249 -2
  208. package/lib/task/verification.ts +5 -3
  209. package/lib/task/workspace-snapshot.ts +26 -5
  210. package/package.json +3 -3
  211. package/templates/.agents/QUICKSTART.en.md +25 -8
  212. package/templates/.agents/QUICKSTART.zh-CN.md +25 -8
  213. package/templates/.agents/README.en.md +10 -7
  214. package/templates/.agents/README.zh-CN.md +10 -7
  215. package/templates/.agents/hooks/lifecycle-delegation.js +15 -4
  216. package/templates/.agents/rules/README.en.md +1 -1
  217. package/templates/.agents/rules/README.zh-CN.md +1 -1
  218. package/templates/.agents/rules/create-issue.github.en.md +4 -2
  219. package/templates/.agents/rules/create-issue.github.zh-CN.md +4 -2
  220. package/templates/.agents/rules/human-decision-context.en.md +11 -2
  221. package/templates/.agents/rules/human-decision-context.zh-CN.md +11 -2
  222. package/templates/.agents/rules/issue-fields.github.en.md +3 -1
  223. package/templates/.agents/rules/issue-fields.github.zh-CN.md +3 -1
  224. package/templates/.agents/rules/issue-pr-commands.en.md +5 -3
  225. package/templates/.agents/rules/issue-pr-commands.github.en.md +5 -3
  226. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +5 -3
  227. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +5 -3
  228. package/templates/.agents/rules/issue-sync.github.en.md +7 -3
  229. package/templates/.agents/rules/issue-sync.github.zh-CN.md +8 -4
  230. package/templates/.agents/rules/lifecycle-orchestration.en.md +5 -2
  231. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +5 -2
  232. package/templates/.agents/rules/milestone-inference.github.en.md +4 -2
  233. package/templates/.agents/rules/milestone-inference.github.zh-CN.md +4 -2
  234. package/templates/.agents/rules/next-step-output.en.md +3 -1
  235. package/templates/.agents/rules/next-step-output.zh-CN.md +3 -1
  236. package/templates/.agents/rules/no-mid-flow-questions.en.md +17 -0
  237. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +17 -0
  238. package/templates/.agents/rules/pr-checks-commands.en.md +3 -3
  239. package/templates/.agents/rules/pr-checks-commands.github.en.md +2 -2
  240. package/templates/.agents/rules/pr-checks-commands.github.zh-CN.md +3 -3
  241. package/templates/.agents/rules/pr-checks-commands.zh-CN.md +3 -3
  242. package/templates/.agents/rules/pr-sync.en.md +3 -1
  243. package/templates/.agents/rules/pr-sync.github.en.md +11 -1
  244. package/templates/.agents/rules/pr-sync.github.zh-CN.md +11 -1
  245. package/templates/.agents/rules/pr-sync.zh-CN.md +3 -1
  246. package/templates/.agents/rules/release-commands.en.md +1 -1
  247. package/templates/.agents/rules/release-commands.github.en.md +12 -2
  248. package/templates/.agents/rules/release-commands.github.zh-CN.md +12 -2
  249. package/templates/.agents/rules/release-commands.zh-CN.md +1 -1
  250. package/templates/.agents/rules/review-handshake.zh-CN.md +1 -1
  251. package/templates/.agents/rules/task-management.en.md +13 -0
  252. package/templates/.agents/rules/task-management.zh-CN.md +13 -0
  253. package/templates/.agents/skills/analyze-task/SKILL.en.md +10 -7
  254. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +10 -7
  255. package/templates/.agents/skills/block-task/SKILL.en.md +7 -5
  256. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +7 -5
  257. package/templates/.agents/skills/cancel-task/SKILL.en.md +6 -4
  258. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +6 -4
  259. package/templates/.agents/skills/close-codescan/SKILL.en.md +3 -1
  260. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +3 -1
  261. package/templates/.agents/skills/close-dependabot/SKILL.en.md +3 -1
  262. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +3 -1
  263. package/templates/.agents/skills/code-task/SKILL.en.md +10 -7
  264. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +12 -9
  265. package/templates/.agents/skills/commit/SKILL.en.md +23 -11
  266. package/templates/.agents/skills/commit/SKILL.zh-CN.md +29 -11
  267. package/templates/.agents/skills/commit/reference/commit-message.en.md +1 -1
  268. package/templates/.agents/skills/commit/reference/commit-message.zh-CN.md +1 -1
  269. package/templates/.agents/skills/commit/reference/commit-orchestration.en.md +49 -0
  270. package/templates/.agents/skills/commit/reference/commit-orchestration.zh-CN.md +55 -0
  271. package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +1 -1
  272. package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +1 -1
  273. package/templates/.agents/skills/commit/reference/pr-summary-sync.en.md +1 -1
  274. package/templates/.agents/skills/commit/reference/pr-summary-sync.zh-CN.md +1 -1
  275. package/templates/.agents/skills/commit/reference/task-status-update.en.md +4 -4
  276. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +4 -4
  277. package/templates/.agents/skills/complete-manual-validation/SKILL.en.md +6 -4
  278. package/templates/.agents/skills/complete-manual-validation/SKILL.zh-CN.md +6 -4
  279. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.en.md +1 -1
  280. package/templates/.agents/skills/complete-manual-validation/reference/summary-update.zh-CN.md +1 -1
  281. package/templates/.agents/skills/complete-task/SKILL.en.md +22 -7
  282. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +22 -7
  283. package/templates/.agents/skills/complete-task/config/verify.en.json +1 -0
  284. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -0
  285. package/templates/.agents/skills/complete-task/reference/external-delivery.en.md +17 -0
  286. package/templates/.agents/skills/complete-task/reference/external-delivery.zh-CN.md +17 -0
  287. package/templates/.agents/skills/create-pr/SKILL.en.md +7 -5
  288. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +7 -5
  289. package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +1 -1
  290. package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +1 -1
  291. package/templates/.agents/skills/create-release-note/SKILL.en.md +17 -16
  292. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +17 -16
  293. package/templates/.agents/skills/create-task/SKILL.en.md +5 -3
  294. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +5 -3
  295. package/templates/.agents/skills/create-task/reference/context-capture.en.md +34 -0
  296. package/templates/.agents/skills/create-task/reference/context-capture.zh-CN.md +34 -0
  297. package/templates/.agents/skills/import-issue/SKILL.en.md +5 -3
  298. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -3
  299. package/templates/.agents/skills/plan-task/SKILL.en.md +8 -5
  300. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +8 -5
  301. package/templates/.agents/skills/post-release/SKILL.en.md +2 -0
  302. package/templates/.agents/skills/post-release/SKILL.zh-CN.md +2 -0
  303. package/templates/.agents/skills/release/SKILL.en.md +16 -10
  304. package/templates/.agents/skills/release/SKILL.zh-CN.md +16 -10
  305. package/templates/.agents/skills/restore-task/SKILL.en.md +4 -1
  306. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +4 -1
  307. package/templates/.agents/skills/review-analysis/SKILL.en.md +7 -4
  308. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +7 -4
  309. package/templates/.agents/skills/review-code/SKILL.en.md +11 -8
  310. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +11 -8
  311. package/templates/.agents/skills/review-code/reference/output-templates.en.md +1 -1
  312. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +1 -1
  313. package/templates/.agents/skills/review-plan/SKILL.en.md +7 -4
  314. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +7 -4
  315. package/templates/.agents/skills/review-pr/SKILL.en.md +186 -0
  316. package/templates/.agents/skills/review-pr/SKILL.zh-CN.md +186 -0
  317. package/templates/.agents/skills/review-pr/config/verify.en.json +37 -0
  318. package/templates/.agents/skills/review-pr/config/verify.zh-CN.json +37 -0
  319. package/templates/.agents/skills/review-pr/reference/evidence-grading.en.md +65 -0
  320. package/templates/.agents/skills/review-pr/reference/evidence-grading.zh-CN.md +65 -0
  321. package/templates/.agents/skills/review-pr/reference/output-guidance.en.md +18 -0
  322. package/templates/.agents/skills/review-pr/reference/output-guidance.zh-CN.md +18 -0
  323. package/templates/.agents/skills/review-pr/reference/report-template.en.md +85 -0
  324. package/templates/.agents/skills/review-pr/reference/report-template.zh-CN.md +85 -0
  325. package/templates/.agents/skills/run-task/SKILL.en.md +8 -8
  326. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +8 -8
  327. package/templates/.agents/skills/run-task/config/verify.en.json +2 -1
  328. package/templates/.agents/skills/run-task/config/verify.zh-CN.json +2 -1
  329. package/templates/.agents/skills/run-task/reference/host-validation.en.md +20 -0
  330. package/templates/.agents/skills/run-task/reference/host-validation.zh-CN.md +20 -0
  331. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +98 -107
  332. package/templates/.agents/skills/watch-pr/SKILL.en.md +13 -13
  333. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +13 -13
  334. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.en.md +27 -6
  335. package/templates/.agents/skills/watch-pr/reference/monitor-and-heal.zh-CN.md +26 -5
  336. package/templates/.agents/templates/task.en.md +2 -2
  337. package/templates/.agents/templates/task.zh-CN.md +2 -2
  338. package/templates/.agents/workflows/bug-fix.en.yaml +3 -3
  339. package/templates/.agents/workflows/bug-fix.zh-CN.yaml +3 -3
  340. package/templates/.agents/workflows/code-review.en.yaml +1 -1
  341. package/templates/.agents/workflows/code-review.zh-CN.yaml +1 -1
  342. package/templates/.agents/workflows/feature-development.en.yaml +4 -4
  343. package/templates/.agents/workflows/feature-development.zh-CN.yaml +4 -4
  344. package/templates/.agents/workflows/refactoring.en.yaml +3 -3
  345. package/templates/.agents/workflows/refactoring.zh-CN.yaml +3 -3
  346. package/templates/.claude/commands/post-release.en.md +1 -0
  347. package/templates/.claude/commands/post-release.zh-CN.md +1 -0
  348. package/templates/.claude/commands/review-pr.en.md +8 -0
  349. package/templates/.claude/commands/review-pr.zh-CN.md +8 -0
  350. package/templates/.claude/commands/watch-pr.en.md +1 -1
  351. package/templates/.claude/commands/watch-pr.zh-CN.md +1 -1
  352. package/templates/.claude/rules/tool-preferences.en.md +20 -0
  353. package/templates/.claude/rules/tool-preferences.zh-CN.md +20 -0
  354. package/templates/.opencode/commands/post-release.en.md +2 -0
  355. package/templates/.opencode/commands/post-release.zh-CN.md +2 -0
  356. package/templates/.opencode/commands/review-pr.en.md +11 -0
  357. package/templates/.opencode/commands/review-pr.zh-CN.md +11 -0
  358. package/templates/.opencode/commands/watch-pr.en.md +1 -1
  359. package/templates/.opencode/commands/watch-pr.zh-CN.md +1 -1
  360. package/dist/lib/agent-clients/adapters/gemini-cli.js +0 -64
  361. package/lib/agent-clients/adapters/gemini-cli.ts +0 -65
  362. package/templates/.gemini/commands/_project_/analyze-task.en.toml +0 -10
  363. package/templates/.gemini/commands/_project_/analyze-task.zh-CN.toml +0 -10
  364. package/templates/.gemini/commands/_project_/archive-tasks.en.toml +0 -10
  365. package/templates/.gemini/commands/_project_/archive-tasks.zh-CN.toml +0 -10
  366. package/templates/.gemini/commands/_project_/block-task.en.toml +0 -10
  367. package/templates/.gemini/commands/_project_/block-task.zh-CN.toml +0 -10
  368. package/templates/.gemini/commands/_project_/cancel-task.en.toml +0 -10
  369. package/templates/.gemini/commands/_project_/cancel-task.zh-CN.toml +0 -10
  370. package/templates/.gemini/commands/_project_/check-task.en.toml +0 -10
  371. package/templates/.gemini/commands/_project_/check-task.zh-CN.toml +0 -10
  372. package/templates/.gemini/commands/_project_/close-codescan.en.toml +0 -8
  373. package/templates/.gemini/commands/_project_/close-codescan.zh-CN.toml +0 -8
  374. package/templates/.gemini/commands/_project_/close-dependabot.en.toml +0 -8
  375. package/templates/.gemini/commands/_project_/close-dependabot.zh-CN.toml +0 -8
  376. package/templates/.gemini/commands/_project_/code-task.en.toml +0 -10
  377. package/templates/.gemini/commands/_project_/code-task.zh-CN.toml +0 -10
  378. package/templates/.gemini/commands/_project_/commit.en.toml +0 -10
  379. package/templates/.gemini/commands/_project_/commit.zh-CN.toml +0 -10
  380. package/templates/.gemini/commands/_project_/complete-manual-validation.en.toml +0 -10
  381. package/templates/.gemini/commands/_project_/complete-manual-validation.zh-CN.toml +0 -10
  382. package/templates/.gemini/commands/_project_/complete-task.en.toml +0 -10
  383. package/templates/.gemini/commands/_project_/complete-task.zh-CN.toml +0 -10
  384. package/templates/.gemini/commands/_project_/create-pr.en.toml +0 -10
  385. package/templates/.gemini/commands/_project_/create-pr.zh-CN.toml +0 -10
  386. package/templates/.gemini/commands/_project_/create-release-note.en.toml +0 -8
  387. package/templates/.gemini/commands/_project_/create-release-note.zh-CN.toml +0 -8
  388. package/templates/.gemini/commands/_project_/create-task.en.toml +0 -8
  389. package/templates/.gemini/commands/_project_/create-task.zh-CN.toml +0 -8
  390. package/templates/.gemini/commands/_project_/import-codescan.en.toml +0 -8
  391. package/templates/.gemini/commands/_project_/import-codescan.zh-CN.toml +0 -8
  392. package/templates/.gemini/commands/_project_/import-dependabot.en.toml +0 -8
  393. package/templates/.gemini/commands/_project_/import-dependabot.zh-CN.toml +0 -8
  394. package/templates/.gemini/commands/_project_/import-issue.en.toml +0 -8
  395. package/templates/.gemini/commands/_project_/import-issue.zh-CN.toml +0 -8
  396. package/templates/.gemini/commands/_project_/init-labels.en.toml +0 -8
  397. package/templates/.gemini/commands/_project_/init-labels.zh-CN.toml +0 -8
  398. package/templates/.gemini/commands/_project_/init-milestones.en.toml +0 -10
  399. package/templates/.gemini/commands/_project_/init-milestones.zh-CN.toml +0 -10
  400. package/templates/.gemini/commands/_project_/plan-task.en.toml +0 -10
  401. package/templates/.gemini/commands/_project_/plan-task.zh-CN.toml +0 -10
  402. package/templates/.gemini/commands/_project_/post-release.en.toml +0 -6
  403. package/templates/.gemini/commands/_project_/post-release.zh-CN.toml +0 -6
  404. package/templates/.gemini/commands/_project_/refine-title.en.toml +0 -8
  405. package/templates/.gemini/commands/_project_/refine-title.zh-CN.toml +0 -8
  406. package/templates/.gemini/commands/_project_/release.en.toml +0 -8
  407. package/templates/.gemini/commands/_project_/release.zh-CN.toml +0 -8
  408. package/templates/.gemini/commands/_project_/restore-task.en.toml +0 -8
  409. package/templates/.gemini/commands/_project_/restore-task.zh-CN.toml +0 -8
  410. package/templates/.gemini/commands/_project_/review-analysis.en.toml +0 -10
  411. package/templates/.gemini/commands/_project_/review-analysis.zh-CN.toml +0 -10
  412. package/templates/.gemini/commands/_project_/review-code.en.toml +0 -10
  413. package/templates/.gemini/commands/_project_/review-code.zh-CN.toml +0 -10
  414. package/templates/.gemini/commands/_project_/review-plan.en.toml +0 -10
  415. package/templates/.gemini/commands/_project_/review-plan.zh-CN.toml +0 -10
  416. package/templates/.gemini/commands/_project_/run-task.en.toml +0 -8
  417. package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +0 -8
  418. package/templates/.gemini/commands/_project_/test-integration.en.toml +0 -6
  419. package/templates/.gemini/commands/_project_/test-integration.zh-CN.toml +0 -6
  420. package/templates/.gemini/commands/_project_/test.en.toml +0 -6
  421. package/templates/.gemini/commands/_project_/test.zh-CN.toml +0 -6
  422. package/templates/.gemini/commands/_project_/update-agent-infra.en.toml +0 -6
  423. package/templates/.gemini/commands/_project_/update-agent-infra.zh-CN.toml +0 -6
  424. package/templates/.gemini/commands/_project_/upgrade-dependency.en.toml +0 -8
  425. package/templates/.gemini/commands/_project_/upgrade-dependency.zh-CN.toml +0 -8
  426. package/templates/.gemini/commands/_project_/watch-pr.en.toml +0 -10
  427. package/templates/.gemini/commands/_project_/watch-pr.zh-CN.toml +0 -10
  428. package/templates/.gemini/settings.json +0 -3
@@ -64,69 +64,9 @@ RUN printf '%s\n' \
64
64
  'set -g mouse on' \
65
65
  'set -g status-interval 1' \
66
66
  'set -g status-right-length 80' \
67
- "set -g status-right '#(/usr/local/bin/cc-token-status) | %H:%M'" \
67
+ "set -g status-right '%H:%M'" \
68
68
  > /etc/tmux.conf
69
69
 
70
- RUN cat > /usr/local/bin/cc-token-status <<'SCRIPT' && chmod +x /usr/local/bin/cc-token-status
71
- #!/bin/sh
72
- set -eu
73
-
74
- SETTINGS_FILE="/home/devuser/.claude/settings.json"
75
- if [ -r "$SETTINGS_FILE" ] && jq -e '
76
- def has_nonempty($v):
77
- if ($v | type) == "string" then ($v | gsub("^\\s+|\\s+$"; "") | length) > 0 else false end;
78
- (if type == "object" then . else {} end) as $settings
79
- | (if ($settings.env | type) == "object" then $settings.env else {} end) as $env
80
- | has_nonempty($env.ANTHROPIC_AUTH_TOKEN)
81
- or has_nonempty($env.ANTHROPIC_API_KEY)
82
- or has_nonempty($settings.apiKeyHelper)
83
- ' "$SETTINGS_FILE" >/dev/null 2>&1; then
84
- exit 0
85
- fi
86
-
87
- CRED_FILE="/home/devuser/.claude/.credentials.json"
88
- [ -r "$CRED_FILE" ] || exit 0
89
-
90
- EXPIRES_MS=$(jq -r '(.claudeAiOauth.expiresAt // .expiresAt) // empty' "$CRED_FILE" 2>/dev/null || true)
91
- case "$EXPIRES_MS" in
92
- ''|*[!0-9]*) exit 0 ;;
93
- esac
94
-
95
- NOW_MS=$(($(date +%s) * 1000))
96
- DIFF_MS=$((EXPIRES_MS - NOW_MS))
97
- DIFF_S=$((DIFF_MS / 1000))
98
-
99
- DIM='#[fg=colour245]'
100
- YELLOW='#[fg=yellow]'
101
- YELLOW_BOLD='#[fg=yellow,bold]'
102
- RED_BOLD='#[fg=red,bold]'
103
- RED_REV='#[fg=red,reverse]'
104
- RESET='#[default]'
105
-
106
- if [ "$DIFF_S" -le 0 ]; then
107
- ELAPSED=$(( -DIFF_S ))
108
- M=$((ELAPSED / 60))
109
- printf '%sClaude Code auth EXPIRED %dm ago%s' "$RED_REV" "$M" "$RESET"
110
- elif [ "$DIFF_S" -lt 60 ]; then
111
- printf '%sClaude Code auth expires in %ds%s' "$RED_BOLD" "$DIFF_S" "$RESET"
112
- elif [ "$DIFF_S" -lt 300 ]; then
113
- M=$((DIFF_S / 60))
114
- S=$((DIFF_S % 60))
115
- printf '%sClaude Code auth expires in %dm %ds%s' "$RED_BOLD" "$M" "$S" "$RESET"
116
- elif [ "$DIFF_S" -lt 1800 ]; then
117
- M=$((DIFF_S / 60))
118
- printf '%sClaude Code auth expires in %dm%s' "$YELLOW_BOLD" "$M" "$RESET"
119
- elif [ "$DIFF_S" -lt 3600 ]; then
120
- M=$((DIFF_S / 60))
121
- printf '%sClaude Code auth expires in %dm%s' "$YELLOW" "$M" "$RESET"
122
- else
123
- TOTAL_M=$((DIFF_S / 60))
124
- H=$((TOTAL_M / 60))
125
- M=$((TOTAL_M % 60))
126
- printf '%sClaude Code auth expires in %dh %dm%s' "$DIM" "$H" "$M" "$RESET"
127
- fi
128
- SCRIPT
129
-
130
70
  RUN cat > /usr/local/bin/sandbox-dotfiles-link <<'SCRIPT' && chmod +x /usr/local/bin/sandbox-dotfiles-link
131
71
  #!/bin/sh
132
72
  # Mirror /dotfiles/ tree as symlinks under $HOME/, overwriting any image-baked
@@ -136,23 +76,34 @@ set -eu
136
76
  DOTFILES_SRC=/dotfiles
137
77
  [ -d "$DOTFILES_SRC" ] || exit 0
138
78
 
139
- cd "$DOTFILES_SRC"
140
- find . -type f -print | while IFS= read -r rel; do
141
- rel=${rel#./}
142
- target="$HOME/$rel"
143
- case "$rel" in
79
+ is_dotfiles_excluded() {
80
+ candidate=$1
81
+ case "$candidate" in
144
82
  .ssh|.ssh/*|\
145
83
  .gnupg|.gnupg/*|\
146
- .claude|.claude/*|\
147
- .codex|.codex/*|\
148
- .gemini|.gemini/*|\
149
- .config/opencode|.config/opencode/*|\
150
- .local/share/opencode|.local/share/opencode/*|\
151
84
  .host-shell-config|.host-shell-config/*|\
152
85
  .gitconfig|.gitignore_global|.stCommitMsg|.bash_aliases|README.md)
153
- continue ;;
86
+ return 0 ;;
154
87
  esac
155
88
 
89
+ [ -r /etc/agent-infra/dotfiles-exclusions ] || return 1
90
+ while IFS= read -r prefix; do
91
+ [ -n "$prefix" ] || continue
92
+ case "$candidate" in
93
+ "$prefix"|"$prefix"/*) return 0 ;;
94
+ esac
95
+ done < /etc/agent-infra/dotfiles-exclusions
96
+ return 1
97
+ }
98
+
99
+ cd "$DOTFILES_SRC"
100
+ find . -type f -print | while IFS= read -r rel; do
101
+ rel=${rel#./}
102
+ target="$HOME/$rel"
103
+ if is_dotfiles_excluded "$rel"; then
104
+ continue
105
+ fi
106
+
156
107
  mkdir -p "$(dirname "$target")"
157
108
  if [ -d "$target" ] && [ ! -L "$target" ]; then
158
109
  printf 'sandbox-dotfiles-link: skipping %s (existing directory; use nested path like %s/<file> instead)\n' "$target" "$rel" >&2
@@ -198,8 +149,7 @@ fi
198
149
  exec tmux new-session -s "$SESSION"
199
150
  SCRIPT
200
151
 
201
- RUN test -s /usr/local/bin/cc-token-status && test -x /usr/local/bin/cc-token-status && \
202
- test -s /usr/local/bin/sandbox-dotfiles-link && test -x /usr/local/bin/sandbox-dotfiles-link && \
152
+ RUN test -s /usr/local/bin/sandbox-dotfiles-link && test -x /usr/local/bin/sandbox-dotfiles-link && \
203
153
  test -s /usr/local/bin/sandbox-tmux-entry && test -x /usr/local/bin/sandbox-tmux-entry
204
154
 
205
155
  ENV LANG=en_US.UTF-8
@@ -64,15 +64,23 @@ type SandboxResolvedToolState = Readonly<{
64
64
 
65
65
  type SandboxHookCreateContext = Readonly<{
66
66
  hostHome: string;
67
+ hostEnv?: Readonly<NodeJS.ProcessEnv>;
68
+ project: string;
67
69
  resolvedTools: readonly SandboxResolvedToolState[];
68
70
  }>;
69
71
 
72
+ type SandboxHookEnterContext = Readonly<{
73
+ hostHome: string;
74
+ hostEnv?: Readonly<NodeJS.ProcessEnv>;
75
+ }>;
76
+
70
77
  type AgentClientSandboxHookContext = Readonly<{
71
78
  signal: AbortSignal;
72
79
  runCommand(command: SandboxHookCommand): Promise<SandboxHookCommandResult>;
73
80
  config?: Readonly<Record<string, unknown>>;
74
81
  plan?: Readonly<Record<string, unknown>>;
75
82
  create?: SandboxHookCreateContext;
83
+ enter?: SandboxHookEnterContext;
76
84
  }>;
77
85
 
78
86
  type AgentClientSandboxHook = Readonly<{
@@ -113,11 +121,17 @@ type AgentClientSandboxRecoveryCheck = Readonly<{
113
121
  repair?: SandboxRecoveryRepair;
114
122
  }>;
115
123
 
124
+ type AgentClientSandboxImageDescriptor = Readonly<{
125
+ dockerfileFragment?: string;
126
+ dotfilesExclusions?: readonly string[];
127
+ }>;
128
+
116
129
  type AgentClientSandboxDescriptor = Readonly<{
117
130
  createTool(context: SandboxToolContext): SandboxTool;
118
131
  aliases: readonly SandboxAlias[];
119
132
  hooks: readonly AgentClientSandboxHook[];
120
133
  recoveryChecks?: readonly AgentClientSandboxRecoveryCheck[];
134
+ image?: AgentClientSandboxImageDescriptor;
121
135
  }>;
122
136
 
123
137
  export { SANDBOX_HOOK_PHASES };
@@ -126,6 +140,7 @@ export type {
126
140
  AgentClientSandboxHook,
127
141
  AgentClientSandboxHookContext,
128
142
  AgentClientSandboxHookResult,
143
+ AgentClientSandboxImageDescriptor,
129
144
  AgentClientSandboxRecoveryCheck,
130
145
  SandboxAlias,
131
146
  SandboxHookCommand,
@@ -133,6 +148,7 @@ export type {
133
148
  SandboxHookPhase,
134
149
  SandboxHookStatus,
135
150
  SandboxHookCreateContext,
151
+ SandboxHookEnterContext,
136
152
  SandboxRecoveryFindingDescriptor,
137
153
  SandboxRecoveryProbe,
138
154
  SandboxRecoveryRepair,
@@ -0,0 +1,123 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { isRemovedHashShortIdInput } from '../task/short-id.ts';
4
+
5
+ const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
6
+ const SHORT_ID_RE = /^\d+$/;
7
+
8
+ export type SandboxWorkspaceIdentity =
9
+ | Readonly<{ mode: 'task-bound'; taskId: string; shortId: string }>
10
+ | Readonly<{ mode: 'branch-only' }>;
11
+
12
+ export type SandboxContainerWorkspaceIdentity =
13
+ | Readonly<{ mode: 'task-bound'; taskId: string }>
14
+ | Readonly<{ mode: 'branch-only' }>
15
+ | Readonly<{ mode: 'legacy-invalid' }>;
16
+
17
+ export type SandboxTarget = Readonly<{
18
+ requestedRef: string;
19
+ branch: string;
20
+ workspace: SandboxWorkspaceIdentity;
21
+ }>;
22
+
23
+ function stripQuotes(value: string): string {
24
+ return value.replace(/^(?:"([^"]*)"|'([^']*)')$/, '$1$2');
25
+ }
26
+
27
+ function taskBranch(taskMd: string, taskId: string): string {
28
+ const content = fs.readFileSync(taskMd, 'utf8');
29
+ const value = content.match(/^branch:\s*(.+)$/m)?.[1]?.trim()
30
+ ?? content.match(/^- \*\*(?:分支|Branch)\*\*:[ \t]*`?([^`\n]+)`?$/m)?.[1]?.trim();
31
+ if (!value) throw new Error(`Task ${taskId} has no branch field in task.md`);
32
+ return stripQuotes(value);
33
+ }
34
+
35
+ function activeRegistry(repoRoot: string): Record<string, string> {
36
+ const registryPath = path.join(repoRoot, '.agents', 'workspace', 'active', '.short-ids.json');
37
+ if (!fs.existsSync(registryPath)) return {};
38
+ const value = JSON.parse(fs.readFileSync(registryPath, 'utf8')) as unknown;
39
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
40
+ throw new Error('Active task short-id registry is invalid');
41
+ }
42
+ const ids = 'ids' in value && value.ids && typeof value.ids === 'object' && !Array.isArray(value.ids)
43
+ ? value.ids as Record<string, unknown>
44
+ : value as Record<string, unknown>;
45
+ return Object.fromEntries(
46
+ Object.entries(ids).filter((entry): entry is [string, string] =>
47
+ SHORT_ID_RE.test(entry[0]) && typeof entry[1] === 'string' && TASK_ID_RE.test(entry[1])
48
+ )
49
+ );
50
+ }
51
+
52
+ function taskIdentity(repoRoot: string, taskId: string, registry: Record<string, string>): SandboxWorkspaceIdentity {
53
+ const taskMd = path.join(repoRoot, '.agents', 'workspace', 'active', taskId, 'task.md');
54
+ if (!fs.existsSync(taskMd)) throw new Error(`Active task not found: ${taskId}`);
55
+ const shortIds = Object.entries(registry).filter(([, id]) => id === taskId).map(([shortId]) => shortId);
56
+ if (shortIds.length !== 1) {
57
+ throw new Error(`Task ${taskId} must have exactly one active short id`);
58
+ }
59
+ return { mode: 'task-bound', taskId, shortId: shortIds[0]! };
60
+ }
61
+
62
+ export function resolveSandboxTarget(requestedRef: string, repoRoot: string): SandboxTarget {
63
+ if (isRemovedHashShortIdInput(requestedRef)) {
64
+ throw new Error(`Invalid task short id '${requestedRef}': task short ids must use bare digits`);
65
+ }
66
+ const registry = activeRegistry(repoRoot);
67
+ if (SHORT_ID_RE.test(requestedRef)) {
68
+ const matchingShortIds = Object.keys(registry).filter((shortId) =>
69
+ shortId === requestedRef || Number(shortId) === Number(requestedRef)
70
+ );
71
+ if (matchingShortIds.length > 1) {
72
+ throw new Error(`Short id '${requestedRef}' is ambiguous in active task registry`);
73
+ }
74
+ const canonicalShortId = matchingShortIds[0];
75
+ const taskId = canonicalShortId ? registry[canonicalShortId] : undefined;
76
+ if (!taskId) throw new Error(`Short id '${requestedRef}' not found in active task registry`);
77
+ const workspace = taskIdentity(repoRoot, taskId, registry);
78
+ return { requestedRef, branch: taskBranch(path.join(repoRoot, '.agents', 'workspace', 'active', taskId, 'task.md'), taskId), workspace };
79
+ }
80
+ if (TASK_ID_RE.test(requestedRef)) {
81
+ const workspace = taskIdentity(repoRoot, requestedRef, registry);
82
+ return { requestedRef, branch: taskBranch(path.join(repoRoot, '.agents', 'workspace', 'active', requestedRef, 'task.md'), requestedRef), workspace };
83
+ }
84
+
85
+ const activeRoot = path.join(repoRoot, '.agents', 'workspace', 'active');
86
+ const matches = fs.existsSync(activeRoot)
87
+ ? fs.readdirSync(activeRoot, { withFileTypes: true }).flatMap((entry) => {
88
+ if (!entry.isDirectory() || !TASK_ID_RE.test(entry.name)) return [];
89
+ const taskMd = path.join(activeRoot, entry.name, 'task.md');
90
+ if (!fs.existsSync(taskMd)) return [];
91
+ return taskBranch(taskMd, entry.name) === requestedRef ? [entry.name] : [];
92
+ })
93
+ : [];
94
+ if (matches.length > 1) {
95
+ throw new Error(`SANDBOX_TASK_IDENTITY_AMBIGUOUS: branch '${requestedRef}' is bound to multiple active tasks`);
96
+ }
97
+ if (matches.length === 1) {
98
+ const taskId = matches[0]!;
99
+ return { requestedRef, branch: requestedRef, workspace: taskIdentity(repoRoot, taskId, registry) };
100
+ }
101
+ return { requestedRef, branch: requestedRef, workspace: { mode: 'branch-only' } };
102
+ }
103
+
104
+ export function parseSandboxWorkspaceIdentity(
105
+ labels: Readonly<Record<string, string>>,
106
+ keys: Readonly<{ mode: string; taskId: string }>
107
+ ): SandboxContainerWorkspaceIdentity {
108
+ const mode = labels[keys.mode];
109
+ const taskId = labels[keys.taskId];
110
+ if (mode === 'branch-only' && taskId === undefined) return { mode: 'branch-only' };
111
+ if (mode === 'task-bound' && typeof taskId === 'string' && TASK_ID_RE.test(taskId)) {
112
+ return { mode: 'task-bound', taskId };
113
+ }
114
+ return { mode: 'legacy-invalid' };
115
+ }
116
+
117
+ export function sameSandboxWorkspaceIdentity(
118
+ left: SandboxWorkspaceIdentity | SandboxContainerWorkspaceIdentity,
119
+ right: SandboxWorkspaceIdentity | SandboxContainerWorkspaceIdentity
120
+ ): boolean {
121
+ return left.mode === right.mode
122
+ && (left.mode !== 'task-bound' || (right.mode === 'task-bound' && left.taskId === right.taskId));
123
+ }
@@ -0,0 +1,143 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import type { SandboxWorkspaceIdentity } from './workspace-identity.ts';
5
+ import type { SandboxControlManifest } from './control/protocol.ts';
6
+
7
+ export type SandboxWorkspaceView = Readonly<{
8
+ root: string;
9
+ taskMountPath: string | null;
10
+ }>;
11
+
12
+ export function sandboxWorkspaceViewPaths(params: Readonly<{
13
+ base: string;
14
+ project: string;
15
+ container: string;
16
+ identity: SandboxWorkspaceIdentity;
17
+ }>): SandboxWorkspaceView {
18
+ const identityKey = params.identity.mode === 'task-bound'
19
+ ? `task-bound:${params.identity.taskId}`
20
+ : 'branch-only';
21
+ const digest = createHash('sha256').update(identityKey).digest('hex').slice(0, 16);
22
+ const projectRoot = path.resolve(params.base, params.project);
23
+ const root = path.resolve(projectRoot, params.container, digest);
24
+ return {
25
+ root,
26
+ taskMountPath: params.identity.mode === 'task-bound'
27
+ ? path.join(root, 'active', params.identity.taskId)
28
+ : null
29
+ };
30
+ }
31
+
32
+ export type SandboxControlSetup = Readonly<{
33
+ channelDir: string;
34
+ manifestPath: string;
35
+ token: string;
36
+ }>;
37
+
38
+ export function sandboxControlPaths(params: Readonly<{
39
+ base: string;
40
+ project: string;
41
+ container: string;
42
+ identity: SandboxWorkspaceIdentity;
43
+ }>): Readonly<{ root: string; channelDir: string; manifestPath: string }> {
44
+ const identityKey = params.identity.mode === 'task-bound'
45
+ ? `task-bound:${params.identity.taskId}`
46
+ : 'branch-only';
47
+ const digest = createHash('sha256').update(identityKey).digest('hex').slice(0, 16);
48
+ const root = path.resolve(params.base, params.project, params.container, digest);
49
+ return { root, channelDir: path.join(root, 'channel'), manifestPath: path.join(root, 'manifest.json') };
50
+ }
51
+
52
+ function assertSafeDirectory(directory: string, expectedBase: string): void {
53
+ const relative = path.relative(expectedBase, directory);
54
+ if (relative.startsWith('..') || path.isAbsolute(relative)) {
55
+ throw new Error(`Sandbox workspace view escapes its configured root: ${directory}`);
56
+ }
57
+ if (fs.existsSync(directory) && fs.lstatSync(directory).isSymbolicLink()) {
58
+ throw new Error(`Sandbox workspace view must not be a symbolic link: ${directory}`);
59
+ }
60
+ }
61
+
62
+ export function assertSandboxTaskSource(repoRoot: string, taskId: string): string {
63
+ if (!/^TASK-\d{8}-\d{6}$/.test(taskId)) throw new Error('SANDBOX_TASK_SOURCE_INVALID');
64
+ const activeRoot = fs.realpathSync.native(path.join(repoRoot, '.agents', 'workspace', 'active'));
65
+ const source = path.join(activeRoot, taskId);
66
+ const stat = fs.lstatSync(source);
67
+ if (!stat.isDirectory() || stat.isSymbolicLink()) {
68
+ throw new Error(`SANDBOX_TASK_SOURCE_INVALID: ${source} must be a real directory`);
69
+ }
70
+ const canonical = fs.realpathSync.native(source);
71
+ if (path.dirname(canonical) !== activeRoot) {
72
+ throw new Error(`SANDBOX_TASK_SOURCE_INVALID: ${source} escapes the active workspace`);
73
+ }
74
+ return canonical;
75
+ }
76
+
77
+ export function materializeSandboxWorkspaceView(params: Readonly<{
78
+ base: string;
79
+ project: string;
80
+ container: string;
81
+ identity: SandboxWorkspaceIdentity;
82
+ }>): SandboxWorkspaceView {
83
+ const projectRoot = path.resolve(params.base, params.project);
84
+ const { root, taskMountPath } = sandboxWorkspaceViewPaths(params);
85
+ assertSafeDirectory(projectRoot, path.resolve(params.base));
86
+ assertSafeDirectory(root, projectRoot);
87
+ fs.mkdirSync(root, { recursive: true, mode: 0o700 });
88
+ for (const state of ['active', 'completed', 'blocked', 'archive']) {
89
+ const stateDir = path.join(root, state);
90
+ assertSafeDirectory(stateDir, root);
91
+ fs.rmSync(stateDir, { recursive: true, force: true });
92
+ fs.mkdirSync(stateDir, { recursive: true, mode: 0o700 });
93
+ }
94
+
95
+ const active = path.join(root, 'active');
96
+ const registry = {
97
+ version: 1,
98
+ ids: params.identity.mode === 'task-bound'
99
+ ? { [params.identity.shortId]: params.identity.taskId }
100
+ : {}
101
+ };
102
+ fs.writeFileSync(path.join(active, '.short-ids.json'), `${JSON.stringify(registry)}\n`, {
103
+ encoding: 'utf8',
104
+ mode: 0o600
105
+ });
106
+ if (taskMountPath) {
107
+ assertSafeDirectory(taskMountPath, active);
108
+ fs.mkdirSync(taskMountPath, { recursive: true, mode: 0o700 });
109
+ }
110
+ return { root, taskMountPath };
111
+ }
112
+
113
+ export function materializeSandboxControl(params: Readonly<{
114
+ base: string;
115
+ repoRoot: string;
116
+ project: string;
117
+ container: string;
118
+ branch: string;
119
+ identity: SandboxWorkspaceIdentity;
120
+ }>): SandboxControlSetup {
121
+ const { root, channelDir, manifestPath } = sandboxControlPaths(params);
122
+ assertSafeDirectory(root, path.resolve(params.base));
123
+ for (const queue of ['requests', 'responses']) {
124
+ const directory = path.join(channelDir, queue);
125
+ assertSafeDirectory(directory, root);
126
+ fs.rmSync(directory, { recursive: true, force: true });
127
+ fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
128
+ }
129
+ const token = randomBytes(32).toString('hex');
130
+ const manifest: SandboxControlManifest = {
131
+ version: 1,
132
+ repoRoot: fs.realpathSync.native(params.repoRoot),
133
+ project: params.project,
134
+ container: params.container,
135
+ branch: params.branch,
136
+ mode: params.identity.mode,
137
+ taskId: params.identity.mode === 'task-bound' ? params.identity.taskId : null,
138
+ token,
139
+ channelDir
140
+ };
141
+ fs.writeFileSync(manifestPath, `${JSON.stringify(manifest)}\n`, { mode: 0o600 });
142
+ return { channelDir, manifestPath, token };
143
+ }
@@ -75,14 +75,14 @@
75
75
  "properties": {
76
76
  "defaultTui": {
77
77
  "type": "string",
78
- "enum": ["claude", "codex", "gemini", "opencode"],
78
+ "enum": ["claude", "codex", "antigravity", "opencode"],
79
79
  "default": "codex"
80
80
  },
81
81
  "skillTuiDefaults": {
82
82
  "type": "object",
83
83
  "additionalProperties": {
84
84
  "type": "string",
85
- "enum": ["claude", "codex", "gemini", "opencode"]
85
+ "enum": ["claude", "codex", "antigravity", "opencode"]
86
86
  }
87
87
  },
88
88
  "allowedSkills": {