@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
@@ -1,10 +1,10 @@
1
1
  import * as p from '@clack/prompts';
2
2
  import pc from 'picocolors';
3
3
  import { loadConfig } from '../config.js';
4
- import { sandboxBranchLabel, sandboxLabel } from '../constants.js';
4
+ import { sandboxBranchLabel, sandboxLabel, sandboxTaskIdLabel, sandboxWorkspaceModeLabel } from '../constants.js';
5
5
  import { detectEngine } from '../engine.js';
6
6
  import { formatTable } from '../../table.js';
7
- import { lookupShortIdByBranch } from '../../task/short-id.js';
7
+ import { loadShortIdByTaskId } from '../../task/short-id.js';
8
8
  import { fetchSandboxRows } from './list-running.js';
9
9
  export { containerListFormat, parseLabels } from './list-running.js';
10
10
  const USAGE = `Usage: ai sandbox ls
@@ -19,9 +19,9 @@ to remove with "ai sandbox rm <branch>".
19
19
 
20
20
  Use "ai sandbox show <ref>" for a single sandbox's worktree and per-tool
21
21
  state paths.`;
22
- const CONTAINER_TABLE_HEADERS = ['#', 'SHORT', 'NAMES', 'STATUS', 'BRANCH'];
22
+ const CONTAINER_TABLE_HEADERS = ['#', 'SHORT', 'NAMES', 'STATUS', 'WORKSPACE', 'TASK', 'BRANCH'];
23
23
  export function formatContainerTable(rows, zebra = false) {
24
- return formatTable(CONTAINER_TABLE_HEADERS, rows.map((r) => [r.row, r.shortId, r.name, r.status, r.branch]), { zebra });
24
+ return formatTable(CONTAINER_TABLE_HEADERS, rows.map((r) => [r.row, r.shortId, r.name, r.status, r.workspace, r.taskId, r.branch]), { zebra });
25
25
  }
26
26
  export function ls(args = []) {
27
27
  if (args.length > 0 && (args[0] === '--help' || args[0] === '-h')) {
@@ -31,7 +31,7 @@ export function ls(args = []) {
31
31
  const config = loadConfig();
32
32
  const engine = detectEngine(config);
33
33
  const label = sandboxLabel(config);
34
- const { running, nonRunning } = fetchSandboxRows(engine, label, sandboxBranchLabel(config));
34
+ const { running, nonRunning } = fetchSandboxRows(engine, label, sandboxBranchLabel(config), { mode: sandboxWorkspaceModeLabel(config), taskId: sandboxTaskIdLabel(config) });
35
35
  p.intro(pc.cyan(`Sandbox status for ${config.project}`));
36
36
  p.log.step('Containers');
37
37
  const ordered = [...running, ...nonRunning];
@@ -40,12 +40,16 @@ export function ls(args = []) {
40
40
  }
41
41
  else {
42
42
  const tableRows = ordered.map((container, i) => {
43
- const shortId = container.branch ? lookupShortIdByBranch(container.branch, config.repoRoot) : null;
43
+ const shortId = container.taskId
44
+ ? loadShortIdByTaskId(config.repoRoot).get(container.taskId) ?? null
45
+ : null;
44
46
  return {
45
47
  row: String(i + 1),
46
48
  shortId: shortId ?? '-',
47
49
  name: container.name,
48
50
  status: container.status,
51
+ workspace: container.workspaceMode ?? 'legacy-invalid',
52
+ taskId: container.taskId ?? '-',
49
53
  branch: container.branch
50
54
  };
51
55
  });
@@ -7,7 +7,7 @@ import { sandboxImageRefreshLabel } from '../constants.js';
7
7
  import { detectEngine, ensureDocker } from '../engine.js';
8
8
  import { runEngine, runOkEngine, runSafeEngine, runVerboseEngine } from '../shell.js';
9
9
  import { pruneSandboxDanglingImages } from '../image-prune.js';
10
- import { resolveTools } from '../tools.js';
10
+ import { createSandboxCapabilityPlan } from '../agent-client-reconciler.js';
11
11
  import { buildImageSignature, buildSandboxImageArgs, parseImageLabels, parseRefreshTimestamp } from '../image-build.js';
12
12
  import { assertBuildProxyCompatibility, buildProxyFailureHint, prepareBuildProxy, redactBuildProxyValues } from '../build-proxy.js';
13
13
  const USAGE = `Usage: ai sandbox rebuild [--quiet] [--refresh] [--inherit-build-proxy|-B]`;
@@ -52,8 +52,9 @@ export async function rebuild(args) {
52
52
  return;
53
53
  }
54
54
  const config = loadConfig();
55
- const tools = resolveTools(config);
56
- const preparedDockerfile = prepareDockerfile(config);
55
+ const capabilityPlan = createSandboxCapabilityPlan(config);
56
+ const tools = [...capabilityPlan.tools];
57
+ const preparedDockerfile = prepareDockerfile(config, capabilityPlan.image);
57
58
  const imageSignature = buildImageSignature(preparedDockerfile, tools);
58
59
  const quiet = values.quiet ?? false;
59
60
  const refresh = values.refresh ?? false;
@@ -1,85 +1,5 @@
1
- import { homedir } from 'node:os';
2
- import { parseArgs } from 'node:util';
3
- import { buildLockedGuidance, discoverProjects, formatCredentialWarnings, formatRemaining, reconcileClaudeCredentials, redactCommandError, validateClaudeCredentialsEnvOverride } from '../credentials.js';
4
- import { runProbe } from '../shell.js';
5
- const USAGE = 'Usage: ai sandbox refresh';
6
- export function probeClaudeStatus(spawnFn = runProbe) {
7
- const result = spawnFn('claude', ['/status'], {
8
- encoding: 'utf8',
9
- stdio: ['ignore', 'pipe', 'pipe'],
10
- timeout: 30_000
11
- });
12
- return {
13
- ok: result.status === 0,
14
- stderr: result.stderr?.toString() ?? '',
15
- error: result.error?.message ?? null
16
- };
17
- }
18
- export async function refresh(args, deps = {}) {
19
- const { spawnFn = runProbe, execFn, readFn, existsFn, writeFn, writeHostFn, discoverFn = discoverProjects, writeStdout = (chunk) => process.stdout.write(chunk), writeStderr = (chunk) => process.stderr.write(chunk) } = deps;
20
- if (args[0] === '--help' || args[0] === '-h' || args[0] === 'help') {
21
- writeStdout(`${USAGE}\n`);
22
- return 0;
23
- }
24
- const { positionals } = parseArgs({ args, allowPositionals: true, strict: true });
25
- if (positionals.length > 0) {
26
- throw new Error(USAGE);
27
- }
28
- validateClaudeCredentialsEnvOverride();
29
- const home = homedir();
30
- if (!home) {
31
- throw new Error('sandbox: home directory is required');
32
- }
33
- const projects = discoverFn(home);
34
- if (projects.length === 0) {
35
- writeStdout('No project credentials to refresh.\n');
36
- return 0;
37
- }
38
- const reconcileOptions = { execFn, readFn, existsFn, writeFn, writeHostFn, projects };
39
- let result = reconcileClaudeCredentials(home, reconcileOptions);
40
- if (result.status === 'STALE_ACCESS' && result.authoritative === null) {
41
- writeStdout('Host credentials appear stale; probing claude /status to trigger refresh...\n');
42
- const probe = probeClaudeStatus(spawnFn);
43
- if (!probe.ok) {
44
- writeStderr(`Probe failed: ${redactCommandError(probe.stderr || probe.error || 'unknown error')}\n`);
45
- writeStderr('Run "claude /login" on the host to renew credentials.\n');
46
- return 1;
47
- }
48
- writeStdout('Probe succeeded; re-inspecting host credentials.\n');
49
- result = reconcileClaudeCredentials(home, reconcileOptions);
50
- }
51
- if (result.status === 'MISSING') {
52
- writeStderr('No Claude Code credentials found on host.\n');
53
- writeStderr('Run "claude /login" on the host to authenticate.\n');
54
- return 1;
55
- }
56
- if (result.status === 'KEYCHAIN_LOCKED') {
57
- writeStderr(`${buildLockedGuidance()}\n`);
58
- return 1;
59
- }
60
- if (result.status === 'KEYCHAIN_ERROR') {
61
- writeStderr(`Host keychain error: ${redactCommandError(result.detail || 'unknown error')}\n`);
62
- writeStderr(`${buildLockedGuidance()}\n`);
63
- return 1;
64
- }
65
- if (result.status === 'KEYCHAIN_WRITE_FAILED') {
66
- writeStderr(`[host] keychain write failed: ${formatCredentialWarnings(result.warnings) || 'unknown error'}\n`);
67
- return 1;
68
- }
69
- if (result.status !== 'OK') {
70
- writeStderr('Host credentials still invalid after probe; run "claude /login".\n');
71
- return 1;
72
- }
73
- if (result.authoritative && result.authoritative !== 'host' && result.hostWritten) {
74
- writeStdout(`[host] reconciled from ${result.authoritative}\n`);
75
- }
76
- for (const project of projects) {
77
- const action = result.filesWritten.includes(project) ? 'updated' : 'unchanged';
78
- writeStdout(`[${project}] ${action}; expires in ${formatRemaining(result.expiresAt)}\n`);
79
- }
80
- for (const failure of result.fileErrors) {
81
- writeStderr(`[${failure.project}] sync failed: ${failure.error}\n`);
82
- }
83
- return result.fileErrors.length > 0 ? 1 : 0;
84
- }
1
+ import { runClaudeCodeRefresh } from '../../agent-clients/adapters/claude-code-refresh.js';
2
+ const refresh = runClaudeCodeRefresh;
3
+ export { probeClaudeStatus, runClaudeCodeRefresh } from '../../agent-clients/adapters/claude-code-refresh.js';
4
+ export { refresh };
85
5
  //# sourceMappingURL=refresh.js.map
@@ -9,7 +9,7 @@ import { ENGINES, detectEngine, engineDisplayName, isManagedEngine, stopManagedV
9
9
  import { pruneSandboxDanglingImages } from '../image-prune.js';
10
10
  import { removeManagedDir, removeWorktreeDir } from '../managed-fs.js';
11
11
  import { runOk, runSafe, runSafeEngine } from '../shell.js';
12
- import { resolveTaskBranch } from '../task-resolver.js';
12
+ import { resolveSandboxTarget } from '../workspace-identity.js';
13
13
  import { toolConfigDirCandidates, toolProjectDirCandidates } from '../tools.js';
14
14
  import { createSandboxCapabilityPlan } from '../agent-client-reconciler.js';
15
15
  import { fetchSandboxRows } from './list-running.js';
@@ -57,6 +57,17 @@ async function rmOne(config, tools, branch, options = {}) {
57
57
  for (const name of matchedContainers) {
58
58
  runSafeEngine(engine, 'docker', ['stop', name]);
59
59
  runSafeEngine(engine, 'docker', ['rm', name]);
60
+ for (const [base, label] of [
61
+ [path.join(config.workspaceViewBase, config.project), 'Workspace view'],
62
+ [path.join(config.controlBase, config.project), 'Control channel']
63
+ ]) {
64
+ const directory = path.join(base, name);
65
+ if (fs.existsSync(directory)) {
66
+ removeManagedDir(base, directory);
67
+ if (!options.quiet)
68
+ p.log.success(`${label} removed: ${directory}`);
69
+ }
70
+ }
60
71
  }
61
72
  spinner.stop(pc.green(`Removed container(s): ${matchedContainers.join(', ')}`));
62
73
  }
@@ -184,6 +195,16 @@ async function rmPurge(config, tools) {
184
195
  p.log.success(`Project share dirs removed: ${config.shareBase}`);
185
196
  }
186
197
  }
198
+ for (const [base, label] of [
199
+ [config.workspaceViewBase, 'workspace views'],
200
+ [config.controlBase, 'control channels']
201
+ ]) {
202
+ const projectDir = path.join(base, config.project);
203
+ if (fs.existsSync(projectDir)) {
204
+ removeManagedDir(base, projectDir);
205
+ p.log.success(`Removed project ${label}: ${projectDir}`);
206
+ }
207
+ }
187
208
  const shouldRemoveImage = await p.confirm({
188
209
  message: `Remove image ${config.imageName}?`,
189
210
  initialValue: false
@@ -297,7 +318,7 @@ export async function rm(args) {
297
318
  });
298
319
  return;
299
320
  }
300
- const branch = resolveTaskBranch(positionals[0] ?? '', config.repoRoot);
301
- await rmOne(config, tools, branch);
321
+ const target = resolveSandboxTarget(positionals[0] ?? '', config.repoRoot);
322
+ await rmOne(config, tools, target.branch);
302
323
  }
303
324
  //# sourceMappingURL=rm.js.map
@@ -2,13 +2,15 @@ import fs from 'node:fs';
2
2
  import * as p from '@clack/prompts';
3
3
  import pc from 'picocolors';
4
4
  import { loadConfig } from '../config.js';
5
- import { assertValidBranchName, worktreeDirCandidates } from '../constants.js';
6
- import { resolveBranchArg } from './list-running.js';
5
+ import { assertValidBranchName, containerNameCandidates, sandboxBranchLabel, sandboxLabel, sandboxTaskIdLabel, sandboxWorkspaceModeLabel, worktreeDirCandidates } from '../constants.js';
6
+ import { fetchSandboxRows, selectSandboxContainer } from './list-running.js';
7
7
  import { resolveTools, toolConfigDirCandidates } from '../tools.js';
8
+ import { detectEngine } from '../engine.js';
9
+ import { resolveSandboxTarget } from '../workspace-identity.js';
8
10
  const USAGE = `Usage: ai sandbox show <branch | TASK-id | N>
9
11
 
10
12
  Shows one sandbox's worktree path and per-tool state paths (Claude Code,
11
- Codex, Gemini CLI, OpenCode). The argument follows the same contract as
13
+ Codex, Antigravity CLI, OpenCode). The argument follows the same contract as
12
14
  'ai sandbox exec' and 'ai sandbox start': N (bare) is the recommended form
13
15
  for task short ids (e.g. 'ai sandbox show 11'); a TASK-id or a plain
14
16
  branch name are also accepted. Use 'ai sandbox ls' for the container list.`;
@@ -40,9 +42,13 @@ export function show(args = []) {
40
42
  return;
41
43
  }
42
44
  const config = loadConfig();
43
- const branch = resolveBranchArg(args[0], { repoRoot: config.repoRoot });
45
+ const target = resolveSandboxTarget(args[0], config.repoRoot);
46
+ const branch = target.branch;
44
47
  assertValidBranchName(branch);
45
48
  const detail = collectSandboxDetail(config, branch);
49
+ const engine = detectEngine(config);
50
+ const rows = fetchSandboxRows(engine, sandboxLabel(config), sandboxBranchLabel(config), { mode: sandboxWorkspaceModeLabel(config), taskId: sandboxTaskIdLabel(config) });
51
+ const container = selectSandboxContainer([...rows.running, ...rows.nonRunning], containerNameCandidates(config, branch));
46
52
  p.intro(pc.cyan(`Sandbox detail for ${config.project} · ${branch}`));
47
53
  p.log.step('Worktree');
48
54
  if (detail.worktrees.length === 0) {
@@ -53,6 +59,9 @@ export function show(args = []) {
53
59
  process.stdout.write(` ${worktree}\n`);
54
60
  }
55
61
  }
62
+ p.log.step('Workspace identity');
63
+ process.stdout.write(` Mode: ${container?.workspaceMode ?? 'legacy-invalid'}\n`);
64
+ process.stdout.write(` Task: ${container?.taskId ?? '-'}\n`);
56
65
  for (const tool of detail.toolStates) {
57
66
  p.log.step(`${tool.name} state`);
58
67
  if (tool.entries.length === 0) {
@@ -2,7 +2,8 @@ import { loadConfig } from '../config.js';
2
2
  import { assertValidBranchName, containerNameCandidates, sandboxBranchLabel, sandboxLabel } from '../constants.js';
3
3
  import { detectEngine } from '../engine.js';
4
4
  import { ensureSandboxReady } from '../recovery.js';
5
- import { fetchSandboxRows, resolveBranchArg, selectSandboxContainer, } from './list-running.js';
5
+ import { fetchSandboxRows, selectSandboxContainer, } from './list-running.js';
6
+ import { resolveSandboxTarget } from '../workspace-identity.js';
6
7
  const USAGE = `Usage: ai sandbox start [--recreate] <branch | TASK-id | N>
7
8
 
8
9
  Start an existing sandbox container that has stopped (for example after the
@@ -46,7 +47,8 @@ export async function start(args) {
46
47
  }
47
48
  const config = loadConfig();
48
49
  const engine = detectEngine(config);
49
- const branch = resolveBranchArg(parsed.target, { repoRoot: config.repoRoot });
50
+ const target = resolveSandboxTarget(parsed.target, config.repoRoot);
51
+ const branch = target.branch;
50
52
  assertValidBranchName(branch);
51
53
  const { running, nonRunning } = fetchSandboxRows(engine, sandboxLabel(config), sandboxBranchLabel(config));
52
54
  const found = selectSandboxContainer([...running, ...nonRunning], containerNameCandidates(config, branch));
@@ -57,11 +59,12 @@ export async function start(args) {
57
59
  config,
58
60
  engine,
59
61
  branch,
62
+ workspace: target.workspace,
60
63
  row: found,
61
64
  allowRecreate: parsed.recreate,
62
- recreate: async (targetBranch) => {
65
+ recreate: async () => {
63
66
  const { create } = await import('./create.js');
64
- await create([targetBranch, '--no-refresh']);
67
+ await create([target.requestedRef, '--no-refresh']);
65
68
  }
66
69
  });
67
70
  const message = ready.path === 'healthy'
@@ -111,6 +111,8 @@ export function loadConfig({ platformFn = platform, writeStderr = (chunk) => pro
111
111
  worktreeBase: hostJoin(home, '.agent-infra', 'worktrees', project),
112
112
  shareBase: hostJoin(home, '.agent-infra', 'share', project),
113
113
  shellConfigBase: hostJoin(home, '.agent-infra', 'config', project),
114
+ workspaceViewBase: hostJoin(home, '.agent-infra', 'workspace-views'),
115
+ controlBase: hostJoin(home, '.agent-infra', 'sandbox-control'),
114
116
  dotfilesDir: hostJoin(home, '.agent-infra', 'dotfiles'),
115
117
  engine,
116
118
  runtimes,
@@ -69,6 +69,12 @@ export function sandboxLabel(config) {
69
69
  export function sandboxBranchLabel(config) {
70
70
  return `${sandboxLabel(config)}.branch`;
71
71
  }
72
+ export function sandboxWorkspaceModeLabel(config) {
73
+ return `${sandboxLabel(config)}.workspace-mode`;
74
+ }
75
+ export function sandboxTaskIdLabel(config) {
76
+ return `${sandboxLabel(config)}.task-id`;
77
+ }
72
78
  export function sandboxImageConfigLabel(config) {
73
79
  return `${sandboxLabel(config)}.image-config`;
74
80
  }
@@ -0,0 +1,44 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { SANDBOX_CONTROL_MAX_BYTES, isSandboxControlFamily } from './protocol.js';
5
+ function sleep(milliseconds) {
6
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
7
+ }
8
+ export function requestSandboxControl(params) {
9
+ if (!isSandboxControlFamily(params.family)) {
10
+ throw new Error(`SANDBOX_CONTROL_COMMAND_DENIED: '${params.family}' is not allowed`);
11
+ }
12
+ const channelDir = params.channelDir ?? process.env.AGENT_INFRA_CONTROL_DIR ?? '/run/agent-infra/control';
13
+ const token = params.token ?? process.env.AGENT_INFRA_CONTROL_TOKEN;
14
+ if (!token)
15
+ throw new Error('SANDBOX_CONTROL_UNAVAILABLE: control token is missing');
16
+ const requestsDir = path.join(channelDir, 'requests');
17
+ const responsesDir = path.join(channelDir, 'responses');
18
+ const id = randomUUID();
19
+ const request = { version: 1, id, token, family: params.family, args: params.args };
20
+ const encoded = `${JSON.stringify(request)}\n`;
21
+ if (Buffer.byteLength(encoded, 'utf8') > SANDBOX_CONTROL_MAX_BYTES) {
22
+ throw new Error('SANDBOX_CONTROL_REQUEST_TOO_LARGE: request exceeds the control limit');
23
+ }
24
+ const temporary = path.join(requestsDir, `.${id}.tmp`);
25
+ const requestPath = path.join(requestsDir, `${id}.json`);
26
+ const responsePath = path.join(responsesDir, `${id}.json`);
27
+ fs.writeFileSync(temporary, encoded, { mode: 0o600, flag: 'wx' });
28
+ fs.renameSync(temporary, requestPath);
29
+ const deadline = Date.now() + (params.timeoutMs ?? 30_000);
30
+ while (Date.now() < deadline) {
31
+ if (fs.existsSync(responsePath)) {
32
+ const raw = fs.readFileSync(responsePath, 'utf8');
33
+ fs.unlinkSync(responsePath);
34
+ const response = JSON.parse(raw);
35
+ if (response.version !== 1 || response.id !== id || !Number.isInteger(response.exitCode)) {
36
+ throw new Error('SANDBOX_CONTROL_RESPONSE_INVALID: broker response is invalid');
37
+ }
38
+ return response;
39
+ }
40
+ sleep(25);
41
+ }
42
+ throw new Error(`SANDBOX_CONTROL_TIMEOUT: broker did not respond within ${params.timeoutMs ?? 30_000}ms`);
43
+ }
44
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1,35 @@
1
+ export const SANDBOX_CONTROL_MAX_BYTES = 64 * 1024;
2
+ export const SANDBOX_CONTROL_FAMILIES = ['task-lifecycle', 'task-orchestration'];
3
+ export function isSandboxControlFamily(value) {
4
+ return SANDBOX_CONTROL_FAMILIES.includes(value);
5
+ }
6
+ export function validateSandboxControlRequest(value, manifest) {
7
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
8
+ throw new Error('SANDBOX_CONTROL_REQUEST_INVALID: request must be an object');
9
+ }
10
+ const request = value;
11
+ if (request.version !== 1
12
+ || typeof request.id !== 'string'
13
+ || !/^[a-f0-9-]{16,64}$/.test(request.id)
14
+ || request.token !== manifest.token
15
+ || typeof request.family !== 'string'
16
+ || !isSandboxControlFamily(request.family)
17
+ || !Array.isArray(request.args)
18
+ || !request.args.every((arg) => typeof arg === 'string')) {
19
+ throw new Error('SANDBOX_CONTROL_REQUEST_INVALID: request schema or authorization is invalid');
20
+ }
21
+ if (manifest.mode !== 'task-bound' || !manifest.taskId) {
22
+ throw new Error('SANDBOX_CONTROL_BRANCH_ONLY: branch-only sandboxes cannot coordinate tasks');
23
+ }
24
+ if (Buffer.byteLength(JSON.stringify(request), 'utf8') > SANDBOX_CONTROL_MAX_BYTES) {
25
+ throw new Error('SANDBOX_CONTROL_REQUEST_TOO_LARGE: request exceeds the control limit');
26
+ }
27
+ return request;
28
+ }
29
+ export function bindSandboxControlTask(request, taskId) {
30
+ if (request.args.length === 0) {
31
+ throw new Error('SANDBOX_CONTROL_REQUEST_INVALID: command arguments are required');
32
+ }
33
+ return [taskId, ...request.args.slice(1)];
34
+ }
35
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1,109 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { getProcessStartTime } from '../../server/process-state.js';
5
+ import { bindSandboxControlTask, validateSandboxControlRequest } from './protocol.js';
6
+ function readManifest(manifestPath) {
7
+ const stat = fs.lstatSync(manifestPath);
8
+ if (!stat.isFile() || stat.isSymbolicLink())
9
+ throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
10
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
11
+ if (manifest.version !== 1
12
+ || typeof manifest.repoRoot !== 'string'
13
+ || typeof manifest.channelDir !== 'string'
14
+ || typeof manifest.token !== 'string')
15
+ throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
16
+ if (path.resolve(manifest.channelDir) !== path.join(path.dirname(path.resolve(manifestPath)), 'channel')) {
17
+ throw new Error('SANDBOX_CONTROL_MANIFEST_INVALID');
18
+ }
19
+ return manifest;
20
+ }
21
+ function assertRealDirectory(directory, parent) {
22
+ const stat = fs.lstatSync(directory);
23
+ if (!stat.isDirectory() || stat.isSymbolicLink())
24
+ throw new Error('SANDBOX_CONTROL_CHANNEL_INVALID');
25
+ if (parent && path.dirname(fs.realpathSync.native(directory)) !== fs.realpathSync.native(parent)) {
26
+ throw new Error('SANDBOX_CONTROL_CHANNEL_INVALID');
27
+ }
28
+ }
29
+ function writeResponse(directory, response) {
30
+ const temporary = path.join(directory, `.${response.id}.tmp`);
31
+ fs.writeFileSync(temporary, `${JSON.stringify(response)}\n`, { mode: 0o600, flag: 'wx' });
32
+ fs.renameSync(temporary, path.join(directory, `${response.id}.json`));
33
+ }
34
+ export function serveSandboxControl(manifestPath, signal = new AbortController().signal) {
35
+ const manifest = readManifest(manifestPath);
36
+ const brokerPath = path.join(path.dirname(manifestPath), 'broker.json');
37
+ const startTime = getProcessStartTime(process.pid);
38
+ if (!startTime)
39
+ throw new Error('SANDBOX_CONTROL_BROKER_IDENTITY_UNAVAILABLE');
40
+ fs.writeFileSync(brokerPath, `${JSON.stringify({
41
+ version: 1,
42
+ pid: process.pid,
43
+ startTime,
44
+ token: manifest.token
45
+ })}\n`, { mode: 0o600, flag: 'wx' });
46
+ const requestsDir = path.join(manifest.channelDir, 'requests');
47
+ const responsesDir = path.join(manifest.channelDir, 'responses');
48
+ assertRealDirectory(manifest.channelDir);
49
+ fs.mkdirSync(requestsDir, { recursive: true, mode: 0o700 });
50
+ fs.mkdirSync(responsesDir, { recursive: true, mode: 0o700 });
51
+ assertRealDirectory(requestsDir, manifest.channelDir);
52
+ assertRealDirectory(responsesDir, manifest.channelDir);
53
+ const consumed = new Set();
54
+ while (!signal.aborted) {
55
+ const current = readManifest(manifestPath);
56
+ if (current.token !== manifest.token)
57
+ return;
58
+ assertRealDirectory(requestsDir, manifest.channelDir);
59
+ assertRealDirectory(responsesDir, manifest.channelDir);
60
+ for (const name of fs.readdirSync(requestsDir).sort()) {
61
+ if (!/^[a-f0-9-]{16,64}\.json$/.test(name))
62
+ continue;
63
+ const requestPath = path.join(requestsDir, name);
64
+ let id = name.slice(0, -5);
65
+ let response;
66
+ try {
67
+ const stat = fs.lstatSync(requestPath);
68
+ if (!stat.isFile() || stat.isSymbolicLink())
69
+ throw new Error('SANDBOX_CONTROL_REQUEST_INVALID');
70
+ const request = validateSandboxControlRequest(JSON.parse(fs.readFileSync(requestPath, 'utf8')), manifest);
71
+ id = request.id;
72
+ if (consumed.has(id))
73
+ throw new Error('SANDBOX_CONTROL_REQUEST_REPLAYED');
74
+ consumed.add(id);
75
+ const result = spawnSync(process.execPath, ['--experimental-strip-types', '--no-warnings', process.argv[1], request.family, ...bindSandboxControlTask(request, manifest.taskId)], {
76
+ cwd: manifest.repoRoot,
77
+ encoding: 'utf8',
78
+ env: Object.fromEntries(Object.entries(process.env).filter(([key]) => !key.startsWith('AGENT_INFRA_CONTROL_')))
79
+ });
80
+ response = {
81
+ version: 1,
82
+ id,
83
+ exitCode: result.status ?? 1,
84
+ stdout: result.stdout ?? '',
85
+ stderr: result.stderr ?? ''
86
+ };
87
+ }
88
+ catch (error) {
89
+ response = {
90
+ version: 1,
91
+ id,
92
+ exitCode: 1,
93
+ stdout: '',
94
+ stderr: `${error instanceof Error ? error.message : String(error)}\n`
95
+ };
96
+ }
97
+ try {
98
+ fs.unlinkSync(requestPath);
99
+ writeResponse(responsesDir, response);
100
+ }
101
+ catch (error) {
102
+ if (error.code !== 'ENOENT')
103
+ throw error;
104
+ }
105
+ }
106
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
107
+ }
108
+ }
109
+ //# sourceMappingURL=server.js.map
@@ -5,6 +5,11 @@ import { createHash } from 'node:crypto';
5
5
  import { fileURLToPath } from 'node:url';
6
6
  import { removeDirRecursive } from '../remove-dir.js';
7
7
  const RUNTIMES_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), 'runtimes');
8
+ const AGENT_CLIENT_RUNTIMES_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'agent-clients', 'adapters', 'runtimes');
9
+ const EMPTY_IMAGE_CONTRIBUTION = Object.freeze({
10
+ dockerfileFragments: Object.freeze([]),
11
+ dotfilesExclusions: Object.freeze([])
12
+ });
8
13
  function listRuntimeFragments() {
9
14
  return fs.readdirSync(RUNTIMES_DIR)
10
15
  .filter((file) => file.endsWith('.dockerfile'))
@@ -15,7 +20,24 @@ export function availableRuntimes() {
15
20
  .filter((name) => name !== 'base' && name !== 'ai-tools')
16
21
  .sort();
17
22
  }
18
- function dockerfileContent(config) {
23
+ function exclusionsFragment(exclusions) {
24
+ return [
25
+ "RUN mkdir -p /etc/agent-infra && cat > /etc/agent-infra/dotfiles-exclusions <<'EXCLUSIONS'",
26
+ ...exclusions,
27
+ 'EXCLUSIONS'
28
+ ].join('\n');
29
+ }
30
+ function adapterFragmentPath(fragment) {
31
+ if (!/^[a-z0-9][a-z0-9-]*$/.test(fragment)) {
32
+ throw new Error(`Invalid Agent Client Dockerfile fragment: ${fragment}`);
33
+ }
34
+ const fragmentPath = path.join(AGENT_CLIENT_RUNTIMES_DIR, `${fragment}.dockerfile`);
35
+ if (!fs.existsSync(fragmentPath)) {
36
+ throw new Error(`Unknown Agent Client Dockerfile fragment: ${fragment}`);
37
+ }
38
+ return fragmentPath;
39
+ }
40
+ function dockerfileContent(config, image = EMPTY_IMAGE_CONTRIBUTION) {
19
41
  if (config.dockerfile) {
20
42
  const customPath = path.resolve(config.repoRoot, config.dockerfile);
21
43
  if (!fs.existsSync(customPath)) {
@@ -30,22 +52,23 @@ function dockerfileContent(config) {
30
52
  }
31
53
  }
32
54
  const fragments = [
33
- 'base.dockerfile',
34
- ...config.runtimes.map((runtime) => `${runtime}.dockerfile`),
35
- 'ai-tools.dockerfile'
55
+ fs.readFileSync(path.join(RUNTIMES_DIR, 'base.dockerfile'), 'utf8').trimEnd(),
56
+ exclusionsFragment(image.dotfilesExclusions),
57
+ ...image.dockerfileFragments.map((fragment) => fs.readFileSync(adapterFragmentPath(fragment), 'utf8').trimEnd()),
58
+ ...config.runtimes.map((runtime) => fs.readFileSync(path.join(RUNTIMES_DIR, `${runtime}.dockerfile`), 'utf8').trimEnd()),
59
+ fs.readFileSync(path.join(RUNTIMES_DIR, 'ai-tools.dockerfile'), 'utf8').trimEnd()
36
60
  ];
37
61
  const content = fragments
38
- .map((fragment) => fs.readFileSync(path.join(RUNTIMES_DIR, fragment), 'utf8').trimEnd())
39
62
  .join('\n\n');
40
63
  return `${content}\n`;
41
64
  }
42
- export function dockerfileSignature(config) {
65
+ export function dockerfileSignature(config, image = EMPTY_IMAGE_CONTRIBUTION) {
43
66
  return createHash('sha256')
44
- .update(dockerfileContent(config))
67
+ .update(dockerfileContent(config, image))
45
68
  .digest('hex')
46
69
  .slice(0, 12);
47
70
  }
48
- export function prepareDockerfile(config) {
71
+ export function prepareDockerfile(config, image = EMPTY_IMAGE_CONTRIBUTION) {
49
72
  if (config.dockerfile) {
50
73
  const customPath = path.resolve(config.repoRoot, config.dockerfile);
51
74
  if (!fs.existsSync(customPath)) {
@@ -53,23 +76,23 @@ export function prepareDockerfile(config) {
53
76
  }
54
77
  return {
55
78
  path: customPath,
56
- signature: dockerfileSignature(config),
79
+ signature: dockerfileSignature(config, image),
57
80
  cleanup() { }
58
81
  };
59
82
  }
60
83
  const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), `${config.project}-sandbox-`));
61
84
  const tempPath = path.join(tempDir, 'Dockerfile');
62
- fs.writeFileSync(tempPath, dockerfileContent(config), 'utf8');
85
+ fs.writeFileSync(tempPath, dockerfileContent(config, image), 'utf8');
63
86
  return {
64
87
  path: tempPath,
65
- signature: dockerfileSignature(config),
88
+ signature: dockerfileSignature(config, image),
66
89
  cleanup() {
67
90
  removeDirRecursive(tempDir);
68
91
  }
69
92
  };
70
93
  }
71
- export function composeDockerfile(config) {
72
- const content = dockerfileContent(config);
94
+ export function composeDockerfile(config, image = EMPTY_IMAGE_CONTRIBUTION) {
95
+ const content = dockerfileContent(config, image);
73
96
  const tempPath = path.join(os.tmpdir(), `${config.project}-sandbox.Dockerfile`);
74
97
  fs.writeFileSync(tempPath, content, 'utf8');
75
98
  return tempPath;
@@ -1,7 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import { shareBranchDir, shareCommonDir, shellConfigDirCandidates, worktreeDirCandidates } from './constants.js';
3
- export function sandboxCoreBindMounts(config, branch, overrides = {}) {
4
- return [
3
+ export function sandboxCoreBindMounts(config, branch, overrides) {
4
+ const mounts = [
5
5
  {
6
6
  hostPaths: overrides.worktree
7
7
  ? [overrides.worktree]
@@ -10,9 +10,9 @@ export function sandboxCoreBindMounts(config, branch, overrides = {}) {
10
10
  readOnly: false
11
11
  },
12
12
  {
13
- hostPaths: [path.join(config.repoRoot, '.agents', 'workspace')],
13
+ hostPaths: [overrides.workspaceViewRoot],
14
14
  containerPath: '/workspace/.agents/workspace',
15
- readOnly: false
15
+ readOnly: true
16
16
  },
17
17
  {
18
18
  hostPaths: [shareCommonDir(config)],
@@ -32,5 +32,18 @@ export function sandboxCoreBindMounts(config, branch, overrides = {}) {
32
32
  readOnly: true
33
33
  }
34
34
  ];
35
+ if (overrides.taskSource && overrides.taskId) {
36
+ mounts.splice(2, 0, {
37
+ hostPaths: [overrides.taskSource],
38
+ containerPath: path.posix.join('/workspace/.agents/workspace/active', overrides.taskId),
39
+ readOnly: false
40
+ });
41
+ }
42
+ mounts.push({
43
+ hostPaths: [overrides.controlDir],
44
+ containerPath: '/run/agent-infra/control',
45
+ readOnly: false
46
+ });
47
+ return mounts;
35
48
  }
36
49
  //# sourceMappingURL=mounts.js.map