@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
@@ -2,32 +2,33 @@ import fs from 'node:fs';
2
2
  import os from 'node:os';
3
3
  import path from 'node:path';
4
4
  import { createHash } from 'node:crypto';
5
- import { execFileSync } from 'node:child_process';
5
+ import { execFileSync, spawn } from 'node:child_process';
6
6
  import { parseArgs } from 'node:util';
7
+ import { fileURLToPath } from 'node:url';
7
8
  import * as p from '@clack/prompts';
8
9
  import pc from 'picocolors';
9
10
  import { listAgentClientAdapters } from '../../agent-clients/registry.js';
10
11
  import { loadConfig } from '../config.js';
11
- import { assertValidBranchName, containerName, containerNameCandidates, parsePositiveIntegerOption, sandboxBranchLabel, sandboxImageConfigLabel, sandboxImageRefreshLabel, sandboxLabel, sandboxRuntimeCapabilityLabel, shareBranchDir, shareCommonDir, shellConfigDir, worktreeDirCandidates } from '../constants.js';
12
+ import { assertValidBranchName, containerName, containerNameCandidates, parsePositiveIntegerOption, sandboxBranchLabel, sandboxImageConfigLabel, sandboxImageRefreshLabel, sandboxLabel, sandboxRuntimeCapabilityLabel, sandboxTaskIdLabel, sandboxWorkspaceModeLabel, shareBranchDir, shareCommonDir, shellConfigDir, worktreeDirCandidates } from '../constants.js';
12
13
  import { prepareDockerfile } from '../dockerfile.js';
13
14
  import { assertBuildProxyCompatibility, buildProxyFailureHint, prepareBuildProxy, redactBuildProxyValues } from '../build-proxy.js';
14
15
  import { detectEngine, ensureDocker } from '../engine.js';
15
16
  import { commandForEngine, execEngine, run, runEngine, runOk, runOkEngine, runSafe, runSafeEngine, runVerboseEngine } from '../shell.js';
16
- import { resolveTaskBranch } from '../task-resolver.js';
17
+ import { parseSandboxWorkspaceIdentity, resolveSandboxTarget, sameSandboxWorkspaceIdentity } from '../workspace-identity.js';
17
18
  import { createSandboxCapabilityPlan, runSandboxHooks, runBoundedSandboxHookCommand } from '../agent-client-reconciler.js';
18
19
  import { resolveTools, tmpfsSeedStagingPath, tmpfsSeedTargetPath, toolConfigDirCandidates } from '../tools.js';
19
20
  import { assertFreshSandboxReady, hydrateTmpfsSeedEntries, prepareTmpfsMounts } from '../recovery.js';
20
21
  import { hostJoin, toEnginePath, volumeArg } from '../engines/wsl2-paths.js';
21
22
  import { sandboxCoreBindMounts } from '../mounts.js';
23
+ import { assertSandboxTaskSource, materializeSandboxControl, materializeSandboxWorkspaceView } from '../workspace-view.js';
22
24
  import { clipboardHostDir, CONTAINER_CLIPBOARD_MOUNT } from '../clipboard/paths.js';
23
25
  import { validateSelinuxDisableEnv } from '../engines/selinux.js';
24
26
  import { dotfilesCacheDir, materializeDotfiles } from '../dotfiles.js';
25
27
  import { ensureSandboxDiscoveryReadmes } from '../readme-scaffold.js';
26
28
  import { removeDirRecursive } from '../../remove-dir.js';
27
- import { prepareClaudeCredentials, redactCommandError, validateClaudeCredentialsEnvOverride } from '../credentials.js';
29
+ import { redactCommandError } from '../redaction.js';
28
30
  import { detectHostTimezone } from '../host-timezone.js';
29
31
  import { buildImageSignature, buildSandboxImageArgs, isRefreshDisabled, isRefreshDue, parseImageLabels, parseRefreshTimestamp } from '../image-build.js';
30
- export { ensureCodexModelInheritance, ensureCodexWorkspaceTrust } from '../../agent-clients/adapters/codex-sandbox.js';
31
32
  const SANDBOX_ALIAS_BLOCK_BEGIN = '# >>> agent-infra managed aliases >>>';
32
33
  const SANDBOX_ALIAS_BLOCK_END = '# <<< agent-infra managed aliases <<<';
33
34
  const CONTAINER_HOME = '/home/devuser';
@@ -64,6 +65,11 @@ const MANAGED_GITHUB_GIT_CONFIG = [
64
65
  '[url "https://github.com/"]',
65
66
  '\tinsteadOf = git@github.com:'
66
67
  ];
68
+ function internalCliPath() {
69
+ const directory = path.dirname(fileURLToPath(import.meta.url));
70
+ const extension = path.extname(fileURLToPath(import.meta.url));
71
+ return path.resolve(directory, '..', '..', '..', 'bin', `internal-cli${extension}`);
72
+ }
67
73
  function resolveToolDirs(config, tools, branch) {
68
74
  return tools.map((tool) => {
69
75
  const candidates = toolConfigDirCandidates(tool, config.project, branch);
@@ -544,207 +550,6 @@ export function assertBranchAvailable(repoRoot, branch, { allowedWorktrees = [],
544
550
  }
545
551
  }
546
552
  }
547
- function readHostJsonSafe(filePath) {
548
- if (!filePath || !fs.existsSync(filePath)) {
549
- return null;
550
- }
551
- try {
552
- const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'));
553
- return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
554
- }
555
- catch {
556
- return null;
557
- }
558
- }
559
- const CLAUDE_SETTINGS_INHERIT_TOP_LEVEL_KEYS = [
560
- 'model',
561
- 'fallbackModel',
562
- 'availableModels',
563
- 'modelOverrides',
564
- 'enforceAvailableModels',
565
- 'advisorModel',
566
- 'apiKeyHelper',
567
- 'effortLevel'
568
- ];
569
- function isJsonObjectRecord(value) {
570
- return value !== null && typeof value === 'object' && !Array.isArray(value);
571
- }
572
- function mergeMissingStringEnvFields(target, source) {
573
- if (!isJsonObjectRecord(source.env)) {
574
- return false;
575
- }
576
- if (Object.hasOwn(target, 'env') && !isJsonObjectRecord(target.env)) {
577
- return false;
578
- }
579
- let targetEnv = target.env;
580
- let changed = false;
581
- for (const [key, value] of Object.entries(source.env)) {
582
- if (typeof value !== 'string' || value === '') {
583
- continue;
584
- }
585
- if (!targetEnv) {
586
- targetEnv = {};
587
- target.env = targetEnv;
588
- }
589
- if (!Object.hasOwn(targetEnv, key)) {
590
- targetEnv[key] = value;
591
- changed = true;
592
- }
593
- }
594
- return changed;
595
- }
596
- function mergeMissingTopLevelSettings(target, source) {
597
- let changed = false;
598
- for (const key of CLAUDE_SETTINGS_INHERIT_TOP_LEVEL_KEYS) {
599
- if (!Object.hasOwn(source, key) || Object.hasOwn(target, key)) {
600
- continue;
601
- }
602
- const value = source[key];
603
- if (value === null || value === undefined || value === '') {
604
- continue;
605
- }
606
- target[key] = value;
607
- changed = true;
608
- }
609
- return changed;
610
- }
611
- export function ensureClaudeOnboarding(toolDir, hostHomeDir) {
612
- const claudeJsonPath = path.join(toolDir, '.claude.json');
613
- let data = {};
614
- if (fs.existsSync(claudeJsonPath)) {
615
- try {
616
- data = JSON.parse(fs.readFileSync(claudeJsonPath, 'utf8'));
617
- }
618
- catch {
619
- // malformed JSON, start fresh
620
- }
621
- }
622
- let changed = false;
623
- if (!data.hasCompletedOnboarding) {
624
- data.hasCompletedOnboarding = true;
625
- changed = true;
626
- }
627
- if (!data.projects) {
628
- data.projects = {};
629
- changed = true;
630
- }
631
- if (!data.projects['/workspace']) {
632
- data.projects['/workspace'] = {};
633
- changed = true;
634
- }
635
- if (!data.projects['/workspace'].hasTrustDialogAccepted) {
636
- data.projects['/workspace'].hasTrustDialogAccepted = true;
637
- changed = true;
638
- }
639
- if (hostHomeDir) {
640
- const hostClaudeJson = readHostJsonSafe(path.join(hostHomeDir, '.claude.json'));
641
- if (hostClaudeJson
642
- && typeof hostClaudeJson.model === 'string'
643
- && hostClaudeJson.model !== ''
644
- && !Object.hasOwn(data, 'model')) {
645
- data.model = hostClaudeJson.model;
646
- changed = true;
647
- }
648
- // Claude Code launch-pins a default effort per model generation (for
649
- // example xhigh for Opus 4.7). The saved effortLevel is honored only after
650
- // a top-level boolean `unpin*LaunchEffort: true` flag unlocks it, so mirror
651
- // those flags here with the existing first-write semantics.
652
- //
653
- // Pattern matching avoids one patch per future model generation. If
654
- // Anthropic changes the naming convention, this block will no-op and should
655
- // be revisited.
656
- if (hostClaudeJson) {
657
- for (const key of Object.keys(hostClaudeJson)) {
658
- if (/^unpin.*LaunchEffort$/.test(key)
659
- && hostClaudeJson[key] === true
660
- && !Object.hasOwn(data, key)) {
661
- data[key] = true;
662
- changed = true;
663
- }
664
- }
665
- }
666
- }
667
- if (changed) {
668
- fs.writeFileSync(claudeJsonPath, JSON.stringify(data, null, 4), 'utf8');
669
- }
670
- }
671
- export function ensureClaudeSettings(toolDir, hostHomeDir) {
672
- const settingsPath = path.join(toolDir, 'settings.json');
673
- let data = {};
674
- if (fs.existsSync(settingsPath)) {
675
- try {
676
- data = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
677
- }
678
- catch {
679
- // malformed JSON, start fresh
680
- }
681
- }
682
- let changed = false;
683
- if (data.skipDangerousModePermissionPrompt !== true) {
684
- data.skipDangerousModePermissionPrompt = true;
685
- changed = true;
686
- }
687
- if (hostHomeDir) {
688
- const hostSettings = readHostJsonSafe(path.join(hostHomeDir, '.claude', 'settings.json'));
689
- if (hostSettings) {
690
- changed = mergeMissingStringEnvFields(data, hostSettings) || changed;
691
- changed = mergeMissingTopLevelSettings(data, hostSettings) || changed;
692
- }
693
- }
694
- if (changed) {
695
- fs.writeFileSync(settingsPath, JSON.stringify(data, null, 4), 'utf8');
696
- }
697
- }
698
- export function ensureOpenCodeModelInheritance(toolDir, hostHomeDir) {
699
- if (!hostHomeDir) {
700
- return;
701
- }
702
- const hostConfigPath = path.join(hostHomeDir, '.config', 'opencode', 'opencode.json');
703
- const hostJson = readHostJsonSafe(hostConfigPath);
704
- if (!hostJson) {
705
- return;
706
- }
707
- const sandboxConfigPath = path.join(toolDir, 'opencode.json');
708
- let sandboxJson = {};
709
- if (fs.existsSync(sandboxConfigPath)) {
710
- const existing = readHostJsonSafe(sandboxConfigPath);
711
- if (!existing) {
712
- return;
713
- }
714
- sandboxJson = existing;
715
- }
716
- let changed = false;
717
- for (const key of ['model', 'small_model']) {
718
- if (Object.hasOwn(sandboxJson, key)) {
719
- continue;
720
- }
721
- const value = hostJson[key];
722
- if (typeof value !== 'string' || value === '') {
723
- continue;
724
- }
725
- sandboxJson[key] = value;
726
- changed = true;
727
- }
728
- if (changed) {
729
- fs.writeFileSync(sandboxConfigPath, JSON.stringify(sandboxJson, null, 2), 'utf8');
730
- }
731
- }
732
- export function ensureGeminiWorkspaceTrust(toolDir) {
733
- const trustPath = path.join(toolDir, 'trustedFolders.json');
734
- let data = {};
735
- if (fs.existsSync(trustPath)) {
736
- try {
737
- data = JSON.parse(fs.readFileSync(trustPath, 'utf8'));
738
- }
739
- catch {
740
- // malformed JSON, start fresh
741
- }
742
- }
743
- if (data['/workspace'] !== 'TRUST_FOLDER') {
744
- data['/workspace'] = 'TRUST_FOLDER';
745
- fs.writeFileSync(trustPath, JSON.stringify(data, null, 2), 'utf8');
746
- }
747
- }
748
553
  export function sandboxAliasesPath(home) {
749
554
  return hostJoin(home, '.agent-infra', 'aliases', 'sandbox.sh');
750
555
  }
@@ -864,10 +669,10 @@ export async function create(args) {
864
669
  throw new Error(USAGE);
865
670
  }
866
671
  validateSelinuxDisableEnv();
867
- validateClaudeCredentialsEnvOverride();
868
672
  const config = loadConfig();
869
673
  const [branchOrTaskId = '', base] = positionals;
870
- const branch = resolveTaskBranch(branchOrTaskId, config.repoRoot);
674
+ const target = resolveSandboxTarget(branchOrTaskId, config.repoRoot);
675
+ const branch = target.branch;
871
676
  assertValidBranchName(branch);
872
677
  const effectiveConfig = {
873
678
  ...config,
@@ -882,39 +687,43 @@ export async function create(args) {
882
687
  const capabilityPlan = createSandboxCapabilityPlan(effectiveConfig);
883
688
  const tools = [...capabilityPlan.tools];
884
689
  const resolvedTools = resolveToolDirs(effectiveConfig, tools, branch);
885
- // Fatal credential states still fail before filesystem/docker side effects.
886
- // Missing credentials leave the selected Claude Code capability installed
887
- // and mounted; only the absent live credential file is omitted.
888
- const credentialOutcome = prepareClaudeCredentials(effectiveConfig.home, effectiveConfig.project, resolvedTools);
690
+ const prepareHookResults = await runSandboxHooks({
691
+ hooks: capabilityPlan.hooksByPhase.prepare,
692
+ phase: 'prepare',
693
+ context: {
694
+ config: effectiveConfig,
695
+ plan: capabilityPlan,
696
+ create: {
697
+ hostHome: effectiveConfig.home,
698
+ hostEnv: { ...process.env },
699
+ project: effectiveConfig.project,
700
+ resolvedTools
701
+ }
702
+ },
703
+ runCommand: runBoundedSandboxHookCommand
704
+ });
705
+ const prepareFailure = prepareHookResults.find((result) => result.status === 'fatal');
706
+ if (prepareFailure) {
707
+ throw new Error(prepareFailure.message ?? `Sandbox hook '${prepareFailure.hookId}' failed.`);
708
+ }
889
709
  const effectiveResolvedTools = resolvedTools;
890
710
  const container = containerName(effectiveConfig, branch);
891
711
  const worktree = worktreeCandidates.find((candidate) => fs.existsSync(candidate)) ?? worktreeCandidates[0] ?? '';
892
712
  const shareCommon = shareCommonDir(effectiveConfig);
893
713
  const shareBranch = shareBranchDir(effectiveConfig, branch);
894
- const preparedDockerfile = prepareDockerfile(effectiveConfig);
714
+ const preparedDockerfile = prepareDockerfile(effectiveConfig, capabilityPlan.image);
895
715
  const baseBranch = base ?? runSafe('git', ['-C', effectiveConfig.repoRoot, 'branch', '--show-current']);
896
716
  const expectedImageSignature = buildImageSignature(preparedDockerfile, tools);
897
717
  const engine = detectEngine(effectiveConfig);
898
718
  let createdTmpfsSeedPlan = [];
899
719
  p.intro(pc.cyan('AI Sandbox'));
900
720
  p.log.info(`Project: ${pc.bold(effectiveConfig.project)} | Branch: ${pc.bold(branch)} | Base: ${pc.bold(baseBranch || 'HEAD')}`);
901
- if (credentialOutcome.status === 'SKIPPED') {
902
- p.log.warn('Claude Code credentials not found on host - creating this sandbox WITHOUT Claude Code credentials.\n'
903
- + ' Claude Code is still installed in the image but will not be authenticated.\n'
904
- + ' To enable it: run "claude" once on the host to complete login, then re-run "ai sandbox create".');
721
+ for (const result of prepareHookResults) {
722
+ if (result.status === 'warning' && result.message)
723
+ p.log.warn(result.message);
905
724
  }
906
725
  try {
907
726
  p.log.step('Checking container engine...');
908
- const prepareHookResults = await runSandboxHooks({
909
- hooks: capabilityPlan.hooksByPhase.prepare,
910
- phase: 'prepare',
911
- context: { config: effectiveConfig, plan: capabilityPlan },
912
- runCommand: runBoundedSandboxHookCommand
913
- });
914
- const prepareFailure = prepareHookResults.find((result) => result.status === 'fatal');
915
- if (prepareFailure) {
916
- throw new Error(prepareFailure.message ?? `Sandbox hook '${prepareFailure.hookId}' failed.`);
917
- }
918
727
  await ensureDocker(effectiveConfig, (detail) => {
919
728
  p.log.info(` ${detail}`);
920
729
  });
@@ -1064,6 +873,32 @@ export async function create(args) {
1064
873
  const matchedContainers = containerNameCandidates(effectiveConfig, branch)
1065
874
  .filter((name) => existing.includes(name));
1066
875
  if (matchedContainers.length > 0) {
876
+ for (const name of matchedContainers) {
877
+ const rawLabels = runSafeEngine(engine, 'docker', [
878
+ 'inspect', '--format', '{{json .Config.Labels}}', name
879
+ ]).trim();
880
+ let labels = {};
881
+ try {
882
+ labels = JSON.parse(rawLabels);
883
+ }
884
+ catch {
885
+ labels = {};
886
+ }
887
+ const existingIdentity = parseSandboxWorkspaceIdentity(labels, {
888
+ mode: sandboxWorkspaceModeLabel(effectiveConfig),
889
+ taskId: sandboxTaskIdLabel(effectiveConfig)
890
+ });
891
+ if (!sameSandboxWorkspaceIdentity(existingIdentity, target.workspace)) {
892
+ const existingDescription = existingIdentity.mode === 'task-bound'
893
+ ? `task-bound:${existingIdentity.taskId}`
894
+ : existingIdentity.mode;
895
+ const requestedDescription = target.workspace.mode === 'task-bound'
896
+ ? `task-bound:${target.workspace.taskId}`
897
+ : target.workspace.mode;
898
+ throw new Error(`SANDBOX_WORKSPACE_IDENTITY_CONFLICT: container '${name}' is ${existingDescription}, `
899
+ + `but this request is ${requestedDescription}. Run 'ai sandbox rm ${branch}' and retry.`);
900
+ }
901
+ }
1067
902
  message('Removing old container instance...');
1068
903
  for (const name of matchedContainers) {
1069
904
  runSafeEngine(engine, 'docker', ['stop', name]);
@@ -1107,6 +942,8 @@ export async function create(args) {
1107
942
  plan: capabilityPlan,
1108
943
  create: {
1109
944
  hostHome: effectiveConfig.home,
945
+ hostEnv: { ...process.env },
946
+ project: effectiveConfig.project,
1110
947
  resolvedTools: effectiveResolvedTools
1111
948
  }
1112
949
  },
@@ -1117,26 +954,22 @@ export async function create(args) {
1117
954
  throw new Error(beforeCreateFailure.message
1118
955
  ?? `Sandbox hook '${beforeCreateFailure.hookId}' failed.`);
1119
956
  }
1120
- const claudeCodeEntry = effectiveResolvedTools.find(({ tool }) => tool.id === 'claude-code');
1121
- if (claudeCodeEntry) {
1122
- ensureClaudeOnboarding(claudeCodeEntry.dir, effectiveConfig.home);
1123
- ensureClaudeSettings(claudeCodeEntry.dir, effectiveConfig.home);
1124
- // prepareClaudeCredentials wrote OAuth credentials or confirmed
1125
- // provider/API settings are enough for Claude Code. If no usable
1126
- // auth was present, the claude-code entry was removed above.
1127
- }
1128
- const geminiEntry = effectiveResolvedTools.find(({ tool }) => tool.id === 'gemini-cli');
1129
- if (geminiEntry) {
1130
- ensureGeminiWorkspaceTrust(geminiEntry.dir);
1131
- }
1132
- const opencodeEntry = effectiveResolvedTools.find(({ tool }) => tool.id === 'opencode');
1133
- if (opencodeEntry) {
1134
- // The TUI reads <toolDir>/opencode.json via OPENCODE_CONFIG pinned in tools.js.
1135
- ensureOpenCodeModelInheritance(opencodeEntry.dir, effectiveConfig.home);
1136
- }
1137
957
  const toolVolumes = effectiveResolvedTools.flatMap(({ tool, dir }) => tool.tmpfs ? [] : ['-v', volumeArg(engine, dir, tool.containerMount)]);
1138
958
  const tmpfsArgs = effectiveResolvedTools.flatMap(({ tool }) => tool.tmpfs ? buildTmpfsRunArgs(tool.containerMount, tool.tmpfs) : []);
1139
- const workspaceDir = path.join(effectiveConfig.repoRoot, '.agents', 'workspace');
959
+ const workspaceView = materializeSandboxWorkspaceView({
960
+ base: effectiveConfig.workspaceViewBase,
961
+ project: effectiveConfig.project,
962
+ container,
963
+ identity: target.workspace
964
+ });
965
+ const control = materializeSandboxControl({
966
+ base: effectiveConfig.controlBase,
967
+ repoRoot: effectiveConfig.repoRoot,
968
+ project: effectiveConfig.project,
969
+ container,
970
+ branch,
971
+ identity: target.workspace
972
+ });
1140
973
  hostShellConfig = prepareHostShellConfig({
1141
974
  home: effectiveConfig.home,
1142
975
  project: effectiveConfig.project,
@@ -1145,7 +978,15 @@ export async function create(args) {
1145
978
  });
1146
979
  const coreVolumes = sandboxCoreBindMounts(effectiveConfig, branch, {
1147
980
  worktree,
1148
- shellConfigHostDir: hostShellConfig.hostDir
981
+ shellConfigHostDir: hostShellConfig.hostDir,
982
+ workspaceViewRoot: workspaceView.root,
983
+ controlDir: control.channelDir,
984
+ ...(target.workspace.mode === 'task-bound'
985
+ ? {
986
+ taskSource: assertSandboxTaskSource(effectiveConfig.repoRoot, target.workspace.taskId),
987
+ taskId: target.workspace.taskId
988
+ }
989
+ : {})
1149
990
  }).flatMap(({ hostPaths, containerPath, readOnly }) => [
1150
991
  '-v',
1151
992
  volumeArg(engine, hostPaths[0], containerPath, readOnly ? ':ro' : '')
@@ -1177,7 +1018,6 @@ export async function create(args) {
1177
1018
  '-v',
1178
1019
  volumeArg(engine, hostPath, path.posix.join(tool.containerMount, containerSubpath))
1179
1020
  ]));
1180
- fs.mkdirSync(workspaceDir, { recursive: true });
1181
1021
  fs.mkdirSync(shareCommon, { recursive: true });
1182
1022
  fs.mkdirSync(shareBranch, { recursive: true });
1183
1023
  fs.mkdirSync(clipboardHostDir(effectiveConfig.home), { recursive: true, mode: 0o700 });
@@ -1199,6 +1039,20 @@ export async function create(args) {
1199
1039
  '--label',
1200
1040
  `${sandboxBranchLabel(effectiveConfig)}=${branch}`,
1201
1041
  '--label',
1042
+ `${sandboxWorkspaceModeLabel(effectiveConfig)}=${target.workspace.mode}`,
1043
+ ...(target.workspace.mode === 'task-bound'
1044
+ ? [
1045
+ '--label',
1046
+ `${sandboxTaskIdLabel(effectiveConfig)}=${target.workspace.taskId}`,
1047
+ '-e',
1048
+ `AGENT_INFRA_TASK_ID=${target.workspace.taskId}`
1049
+ ]
1050
+ : []),
1051
+ '-e',
1052
+ `AGENT_INFRA_CONTROL_TOKEN=${control.token}`,
1053
+ '-e',
1054
+ 'AGENT_INFRA_CONTROL_DIR=/run/agent-infra/control',
1055
+ '--label',
1202
1056
  `${sandboxRuntimeCapabilityLabel(effectiveConfig)}=${capabilityPlan.runtimeSignature}`,
1203
1057
  ...coreVolumes,
1204
1058
  ...buildClipboardVolumeArgs(engine, effectiveConfig.home),
@@ -1215,6 +1069,12 @@ export async function create(args) {
1215
1069
  '/workspace',
1216
1070
  effectiveConfig.imageName
1217
1071
  ]);
1072
+ const broker = spawn(process.execPath, [internalCliPath(), 'sandbox-control', 'serve', '--manifest', control.manifestPath], { cwd: effectiveConfig.repoRoot, detached: true, stdio: 'ignore' });
1073
+ broker.once('error', () => {
1074
+ // Fresh readiness verification below remains the source of truth;
1075
+ // consume detached spawn errors so they cannot crash the command.
1076
+ });
1077
+ broker.unref();
1218
1078
  createdTmpfsSeedPlan = tmpfsSeedPlan;
1219
1079
  }
1220
1080
  finally {
@@ -1297,10 +1157,11 @@ export async function create(args) {
1297
1157
  preparedDockerfile.cleanup();
1298
1158
  }
1299
1159
  p.log.step('Verifying setup...');
1300
- assertFreshSandboxReady({
1160
+ await assertFreshSandboxReady({
1301
1161
  config: effectiveConfig,
1302
1162
  engine,
1303
1163
  branch,
1164
+ workspace: target.workspace,
1304
1165
  container,
1305
1166
  copiedEntries: createdTmpfsSeedPlan
1306
1167
  });
@@ -3,12 +3,13 @@ import { assertValidBranchName, containerNameCandidates, sandboxBranchLabel, san
3
3
  import { detectEngine } from '../engine.js';
4
4
  import { ensureSandboxReady } from '../recovery.js';
5
5
  import { createSandboxCapabilityPlan, runBoundedSandboxHookCommand, runSandboxHooks } from '../agent-client-reconciler.js';
6
- import { formatCredentialWarnings, formatRemaining, hasClaudeProviderAuth, reconcileClaudeCredentials, redactCommandError, validateClaudeCredentialsEnvOverride } from '../credentials.js';
7
6
  import { runInteractiveEngine } from '../shell.js';
8
7
  import { dotfilesCacheDir, materializeDotfiles } from '../dotfiles.js';
8
+ import { redactCommandError } from '../redaction.js';
9
9
  import { runInteractiveWithClipboardBridge } from '../clipboard/bridge.js';
10
10
  import { detectHostTimezone } from '../host-timezone.js';
11
- import { fetchSandboxRows, resolveBranchArg, selectSandboxContainer, } from './list-running.js';
11
+ import { fetchSandboxRows, selectSandboxContainer, } from './list-running.js';
12
+ import { resolveSandboxTarget } from '../workspace-identity.js';
12
13
  const USAGE = `Usage: ai sandbox exec [--recreate] <branch | TASK-id | N> [cmd...]
13
14
 
14
15
  N references an active task short id from
@@ -53,32 +54,6 @@ export function runSandboxInteractive(params) {
53
54
  }
54
55
  return runBridge({ engine, dockerArgs, container, home });
55
56
  }
56
- export function formatCredentialSyncStatus(result, isTTY = process.stderr.isTTY, providerAuthAvailable = false) {
57
- if (providerAuthAvailable && (result.status === 'STALE_ACCESS' || result.status === 'MISSING')) {
58
- return null;
59
- }
60
- if (result.status === 'STALE_ACCESS') {
61
- return 'Warning: Claude Code credentials on host appear stale. Run "ai sandbox refresh" or "claude /login" to renew.\n';
62
- }
63
- if (result.status === 'MISSING') {
64
- return 'Warning: Claude Code credentials missing on host. Run "claude /login" to authenticate.\n';
65
- }
66
- if (result.status === 'KEYCHAIN_WRITE_FAILED') {
67
- return `Warning: A sandbox refresh produced newer credentials but host Keychain write failed (${formatCredentialWarnings(result.warnings)}). Run "ai sandbox refresh" again or "claude /status" on the host to retry.\n`;
68
- }
69
- if (result.status === 'KEYCHAIN_LOCKED' || result.status === 'KEYCHAIN_ERROR') {
70
- return 'Warning: Host keychain is unavailable; Claude credential sync skipped. Run "ai sandbox refresh" for details.\n';
71
- }
72
- if (result.status === 'OK' && result.authoritative !== 'host') {
73
- const message = `Synced Claude Code credentials from sandbox refresh back to host (expires in ${formatRemaining(result.expiresAt)})`;
74
- return isTTY ? `\x1b[2m${message}\x1b[0m\n` : `${message}\n`;
75
- }
76
- if (result.status === 'OK' && result.filesWritten.length > 0) {
77
- const message = `Synced Claude Code credentials from host Keychain (expires in ${formatRemaining(result.expiresAt)})`;
78
- return isTTY ? `\x1b[2m${message}\x1b[0m\n` : `${message}\n`;
79
- }
80
- return null;
81
- }
82
57
  export function parseEnterArgs(args) {
83
58
  let recreate = false;
84
59
  let index = 0;
@@ -101,10 +76,10 @@ export async function enter(args) {
101
76
  return 0;
102
77
  }
103
78
  const config = loadConfig();
104
- validateClaudeCredentialsEnvOverride();
105
79
  const engine = detectEngine(config);
106
80
  const parsed = parseEnterArgs(args);
107
- const branch = resolveBranchArg(parsed.target, { repoRoot: config.repoRoot });
81
+ const target = resolveSandboxTarget(parsed.target, config.repoRoot);
82
+ const branch = target.branch;
108
83
  assertValidBranchName(branch);
109
84
  const { running, nonRunning } = fetchSandboxRows(engine, sandboxLabel(config), sandboxBranchLabel(config));
110
85
  const found = selectSandboxContainer([...running, ...nonRunning], containerNameCandidates(config, branch));
@@ -115,11 +90,12 @@ export async function enter(args) {
115
90
  config,
116
91
  engine,
117
92
  branch,
93
+ workspace: target.workspace,
118
94
  row: found,
119
95
  allowRecreate: parsed.recreate,
120
- recreate: async (targetBranch) => {
96
+ recreate: async () => {
121
97
  const { create } = await import('./create.js');
122
- await create([targetBranch, '--no-refresh']);
98
+ await create([target.requestedRef, '--no-refresh']);
123
99
  }
124
100
  });
125
101
  const container = ready.container;
@@ -128,7 +104,11 @@ export async function enter(args) {
128
104
  const enterHookResults = await runSandboxHooks({
129
105
  hooks: capabilityPlan.hooksByPhase['before-enter'],
130
106
  phase: 'before-enter',
131
- context: { config, plan: capabilityPlan },
107
+ context: {
108
+ config,
109
+ plan: capabilityPlan,
110
+ enter: { hostHome: config.home, hostEnv: { ...process.env } }
111
+ },
132
112
  runCommand: runBoundedSandboxHookCommand
133
113
  });
134
114
  for (const result of enterHookResults) {
@@ -138,20 +118,8 @@ export async function enter(args) {
138
118
  if (result.status === 'warning') {
139
119
  process.stderr.write(`Warning: ${result.message ?? `Sandbox hook '${result.hookId}' did not complete.`}\n`);
140
120
  }
141
- }
142
- if (capabilityPlan.selectedAgentClients.some((adapter) => adapter.id === 'claude-code')) {
143
- try {
144
- // Scan all projects so a refresh from a neighbouring sandbox can still flow back to the host.
145
- const providerAuthAvailable = hasClaudeProviderAuth(config.home);
146
- const result = reconcileClaudeCredentials(config.home);
147
- const message = formatCredentialSyncStatus(result, process.stderr.isTTY, providerAuthAvailable);
148
- if (message) {
149
- process.stderr.write(message);
150
- }
151
- }
152
- catch (error) {
153
- process.stderr.write(`Warning: Failed to sync Claude Code credentials: ${redactCommandError(error instanceof Error ? error.message : 'unknown error')}\n`);
154
- }
121
+ if (result.message && result.status !== 'warning')
122
+ process.stderr.write(result.message);
155
123
  }
156
124
  const envFlags = [...terminalEnvFlags(), ...hostTimezoneEnvFlags()];
157
125
  if (cmd.length === 0) {
@@ -4,6 +4,7 @@ import path from 'node:path';
4
4
  import { runSafeEngine, runVerboseEngine } from '../shell.js';
5
5
  import { resolveTaskBranch } from '../task-resolver.js';
6
6
  import { isRemovedHashShortIdInput } from '../../task/short-id.js';
7
+ import { parseSandboxWorkspaceIdentity } from '../workspace-identity.js';
7
8
  export function containerListFormat() {
8
9
  return '{{.Names}}\t{{.Status}}\t{{.Labels}}';
9
10
  }
@@ -24,19 +25,28 @@ export function parseLabels(csv) {
24
25
  }
25
26
  return labels;
26
27
  }
27
- export function parseSandboxRows(rawOutput, branchKey) {
28
+ export function parseSandboxRows(rawOutput, branchKey, workspaceKeys) {
28
29
  if (!rawOutput) {
29
30
  return [];
30
31
  }
31
32
  return rawOutput.split('\n').map((line) => {
32
33
  const [name = '', status = '', labelsCsv = ''] = line.split('\t');
33
- const branch = parseLabels(labelsCsv)[branchKey] ?? '';
34
+ const labels = parseLabels(labelsCsv);
35
+ const workspace = workspaceKeys
36
+ ? parseSandboxWorkspaceIdentity(labels, workspaceKeys)
37
+ : null;
34
38
  return {
35
39
  name,
36
40
  status,
37
- branch,
41
+ branch: labels[branchKey] ?? '',
38
42
  running: status.startsWith('Up '),
39
- index: null
43
+ index: null,
44
+ ...(workspace
45
+ ? {
46
+ workspaceMode: workspace.mode,
47
+ taskId: workspace.mode === 'task-bound' ? workspace.taskId : null
48
+ }
49
+ : {})
40
50
  };
41
51
  });
42
52
  }
@@ -58,7 +68,7 @@ export function sortAndIndexSandboxRows(rows) {
58
68
  }));
59
69
  return { running, nonRunning };
60
70
  }
61
- export function fetchSandboxRows(engine, label, branchKey) {
71
+ export function fetchSandboxRows(engine, label, branchKey, workspaceKeys) {
62
72
  const raw = runSafeEngine(engine, 'docker', [
63
73
  'ps',
64
74
  '-a',
@@ -67,7 +77,7 @@ export function fetchSandboxRows(engine, label, branchKey) {
67
77
  '--format',
68
78
  containerListFormat()
69
79
  ]);
70
- return sortAndIndexSandboxRows(parseSandboxRows(raw, branchKey));
80
+ return sortAndIndexSandboxRows(parseSandboxRows(raw, branchKey, workspaceKeys));
71
81
  }
72
82
  /**
73
83
  * Returns true iff `arg` is a syntactically valid task short reference.