@fitlab-ai/agent-infra 0.9.0 → 0.9.2

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 (327) hide show
  1. package/README.md +22 -5
  2. package/README.zh-CN.md +21 -5
  3. package/bin/cli.ts +12 -0
  4. package/dist/bin/cli.js +14 -0
  5. package/dist/bin/internal-cli.js +20 -0
  6. package/dist/lib/agent-client.js +163 -0
  7. package/dist/lib/agent-clients/adapter.js +294 -0
  8. package/dist/lib/agent-clients/adapters/claude-code.js +62 -0
  9. package/dist/lib/agent-clients/adapters/codex-sandbox.js +210 -0
  10. package/dist/lib/agent-clients/adapters/codex.js +54 -0
  11. package/dist/lib/agent-clients/adapters/gemini-cli.js +64 -0
  12. package/dist/lib/agent-clients/adapters/index.js +20 -0
  13. package/dist/lib/agent-clients/adapters/opencode.js +64 -0
  14. package/dist/lib/agent-clients/config.js +177 -0
  15. package/dist/lib/agent-clients/custom-tuis.js +99 -0
  16. package/dist/lib/agent-clients/invocation.js +9 -0
  17. package/dist/lib/agent-clients/next-steps.js +38 -0
  18. package/dist/lib/agent-clients/project-assets.js +45 -0
  19. package/dist/lib/agent-clients/reconcile.js +235 -0
  20. package/dist/lib/agent-clients/registry.js +72 -0
  21. package/dist/lib/agent-clients/schema.js +57 -0
  22. package/dist/lib/agent-clients/types.js +29 -0
  23. package/dist/lib/builtin-tuis.js +9 -14
  24. package/dist/lib/defaults.json +85 -73
  25. package/dist/lib/git/review-snapshot.js +25 -10
  26. package/dist/lib/git/reviewed-commit-equivalence.js +110 -0
  27. package/dist/lib/init.js +47 -91
  28. package/dist/lib/internal/agent-client.js +117 -0
  29. package/dist/lib/internal/git-workflow.js +1 -1
  30. package/dist/lib/internal/platform-release-notes.js +53 -0
  31. package/dist/lib/internal/release-workflow.js +175 -17
  32. package/dist/lib/internal/task-orchestration.js +151 -0
  33. package/dist/lib/internal/task-review.js +65 -0
  34. package/dist/lib/merge.js +2 -1
  35. package/dist/lib/platform/adapters.js +38 -1
  36. package/dist/lib/platform/change-request-git-evidence.js +113 -0
  37. package/dist/lib/platform/github-release-notes.js +96 -0
  38. package/dist/lib/platform/issues.js +4 -2
  39. package/dist/lib/platform/merged-pr-equivalence.js +84 -0
  40. package/dist/lib/platform/pr-checks.js +43 -15
  41. package/dist/lib/platform/pull-requests.js +113 -7
  42. package/dist/lib/platform/release-notes.js +143 -0
  43. package/dist/lib/platform/releases.js +1 -1
  44. package/dist/lib/platform/verification-required.js +31 -9
  45. package/dist/lib/prompt.js +15 -3
  46. package/dist/lib/release/channels.js +4 -1
  47. package/dist/lib/release/workflow.js +4 -2
  48. package/dist/lib/remove-dir.js +25 -0
  49. package/dist/lib/run/tui.js +8 -2
  50. package/dist/lib/sandbox/agent-client-reconciler.js +234 -0
  51. package/dist/lib/sandbox/build-proxy.js +63 -0
  52. package/dist/lib/sandbox/clipboard/linux.js +2 -1
  53. package/dist/lib/sandbox/commands/create.js +133 -189
  54. package/dist/lib/sandbox/commands/enter.js +17 -1
  55. package/dist/lib/sandbox/commands/prune.js +2 -2
  56. package/dist/lib/sandbox/commands/rebuild.js +24 -7
  57. package/dist/lib/sandbox/commands/rm.js +3 -2
  58. package/dist/lib/sandbox/config.js +21 -4
  59. package/dist/lib/sandbox/constants.js +3 -0
  60. package/dist/lib/sandbox/dockerfile.js +2 -1
  61. package/dist/lib/sandbox/image-build.js +2 -1
  62. package/dist/lib/sandbox/managed-fs.js +10 -5
  63. package/dist/lib/sandbox/recovery.js +52 -36
  64. package/dist/lib/sandbox/runtimes/ai-tools.dockerfile +24 -3
  65. package/dist/lib/sandbox/runtimes/base.dockerfile +8 -1
  66. package/dist/lib/sandbox/runtimes/java17.dockerfile +8 -1
  67. package/dist/lib/sandbox/runtimes/java21.dockerfile +8 -1
  68. package/dist/lib/sandbox/runtimes/node20.dockerfile +8 -1
  69. package/dist/lib/sandbox/runtimes/node22.dockerfile +8 -1
  70. package/dist/lib/sandbox/runtimes/python3.dockerfile +8 -1
  71. package/dist/lib/sandbox/shell.js +3 -2
  72. package/dist/lib/sandbox/tool-types.js +8 -0
  73. package/dist/lib/sandbox/tools.js +2 -126
  74. package/dist/lib/task/artifact-lifecycle.js +1 -49
  75. package/dist/lib/task/commands/decisions.js +50 -20
  76. package/dist/lib/task/commands/issue-body.js +10 -5
  77. package/dist/lib/task/delegation-receipts.js +107 -0
  78. package/dist/lib/task/events.js +101 -2
  79. package/dist/lib/task/issue-form.js +2 -0
  80. package/dist/lib/task/orchestration.js +420 -0
  81. package/dist/lib/task/review-artifacts.js +143 -59
  82. package/dist/lib/task/review-finalization.js +166 -0
  83. package/dist/lib/task/review-fingerprint.js +13 -0
  84. package/dist/lib/task/verification-checks.js +2 -1
  85. package/dist/lib/task/verification-engine.js +168 -23
  86. package/dist/lib/task/verification-review.js +1 -1
  87. package/dist/lib/task/verification.js +4 -2
  88. package/dist/lib/task/workspace-snapshot.js +34 -0
  89. package/dist/lib/update.js +42 -161
  90. package/lib/agent-client.ts +214 -0
  91. package/lib/agent-clients/adapter.ts +461 -0
  92. package/lib/agent-clients/adapters/claude-code.ts +70 -0
  93. package/lib/agent-clients/adapters/codex-sandbox.ts +245 -0
  94. package/lib/agent-clients/adapters/codex.ts +58 -0
  95. package/lib/agent-clients/adapters/gemini-cli.ts +65 -0
  96. package/lib/agent-clients/adapters/index.ts +27 -0
  97. package/lib/agent-clients/adapters/opencode.ts +66 -0
  98. package/lib/agent-clients/config.ts +278 -0
  99. package/lib/agent-clients/custom-tuis.ts +149 -0
  100. package/lib/agent-clients/invocation.ts +19 -0
  101. package/lib/agent-clients/next-steps.ts +72 -0
  102. package/lib/agent-clients/project-assets.ts +96 -0
  103. package/lib/agent-clients/reconcile.ts +324 -0
  104. package/lib/agent-clients/registry.ts +144 -0
  105. package/lib/agent-clients/schema.ts +62 -0
  106. package/lib/agent-clients/types.ts +78 -0
  107. package/lib/benchmark/schemas/v1.0.0/case-manifest.schema.json +120 -0
  108. package/lib/benchmark/schemas/v1.0.0/common.schema.json +109 -0
  109. package/lib/benchmark/schemas/v1.0.0/grader-result.schema.json +160 -0
  110. package/lib/benchmark/schemas/v1.0.0/run-manifest.schema.json +122 -0
  111. package/lib/benchmark/schemas/v1.0.0/subject.schema.json +227 -0
  112. package/lib/builtin-tuis.ts +16 -15
  113. package/lib/defaults.json +24 -12
  114. package/lib/git/review-snapshot.ts +39 -9
  115. package/lib/git/reviewed-commit-equivalence.ts +135 -0
  116. package/lib/init.ts +53 -121
  117. package/lib/internal/agent-client.ts +131 -0
  118. package/lib/internal/git-workflow.ts +1 -1
  119. package/lib/internal/platform-release-notes.ts +55 -0
  120. package/lib/internal/release-workflow.ts +172 -18
  121. package/lib/internal/task-orchestration.ts +132 -0
  122. package/lib/internal/task-review.ts +65 -0
  123. package/lib/merge.ts +2 -1
  124. package/lib/platform/adapters.ts +142 -3
  125. package/lib/platform/change-request-git-evidence.ts +159 -0
  126. package/lib/platform/github-release-notes.ts +132 -0
  127. package/lib/platform/issues.ts +6 -4
  128. package/lib/platform/merged-pr-equivalence.ts +107 -0
  129. package/lib/platform/pr-checks.ts +57 -28
  130. package/lib/platform/pull-requests.ts +140 -25
  131. package/lib/platform/release-notes.ts +167 -0
  132. package/lib/platform/releases.ts +1 -1
  133. package/lib/platform/verification-required.ts +37 -9
  134. package/lib/prompt.ts +16 -3
  135. package/lib/release/channels.ts +4 -1
  136. package/lib/release/workflow.ts +4 -3
  137. package/lib/remove-dir.ts +28 -0
  138. package/lib/run/tui.ts +9 -1
  139. package/lib/sandbox/agent-client-reconciler.ts +378 -0
  140. package/lib/sandbox/build-proxy.ts +94 -0
  141. package/lib/sandbox/clipboard/linux.ts +2 -1
  142. package/lib/sandbox/commands/create.ts +188 -203
  143. package/lib/sandbox/commands/enter.ts +23 -1
  144. package/lib/sandbox/commands/prune.ts +2 -2
  145. package/lib/sandbox/commands/rebuild.ts +29 -7
  146. package/lib/sandbox/commands/rm.ts +3 -2
  147. package/lib/sandbox/config.ts +31 -4
  148. package/lib/sandbox/constants.ts +6 -0
  149. package/lib/sandbox/dockerfile.ts +2 -1
  150. package/lib/sandbox/image-build.ts +4 -1
  151. package/lib/sandbox/managed-fs.ts +18 -5
  152. package/lib/sandbox/recovery.ts +98 -67
  153. package/lib/sandbox/runtimes/ai-tools.dockerfile +24 -3
  154. package/lib/sandbox/runtimes/base.dockerfile +8 -1
  155. package/lib/sandbox/runtimes/java17.dockerfile +8 -1
  156. package/lib/sandbox/runtimes/java21.dockerfile +8 -1
  157. package/lib/sandbox/runtimes/node20.dockerfile +8 -1
  158. package/lib/sandbox/runtimes/node22.dockerfile +8 -1
  159. package/lib/sandbox/runtimes/python3.dockerfile +8 -1
  160. package/lib/sandbox/shell.ts +4 -2
  161. package/lib/sandbox/tool-types.ts +143 -0
  162. package/lib/sandbox/tools.ts +5 -157
  163. package/lib/task/artifact-lifecycle.ts +1 -38
  164. package/lib/task/commands/decisions.ts +53 -20
  165. package/lib/task/commands/issue-body.ts +10 -5
  166. package/lib/task/delegation-receipts.ts +170 -0
  167. package/lib/task/events.ts +111 -3
  168. package/lib/task/issue-form.ts +4 -0
  169. package/lib/task/orchestration.ts +543 -0
  170. package/lib/task/review-artifacts.ts +201 -69
  171. package/lib/task/review-finalization.ts +275 -0
  172. package/lib/task/review-fingerprint.ts +15 -1
  173. package/lib/task/verification-checks.ts +2 -1
  174. package/lib/task/verification-engine.ts +190 -25
  175. package/lib/task/verification-review.ts +2 -0
  176. package/lib/task/verification.ts +6 -3
  177. package/lib/task/workspace-snapshot.ts +36 -0
  178. package/lib/update.ts +48 -184
  179. package/package.json +2 -2
  180. package/templates/.agents/README.en.md +21 -20
  181. package/templates/.agents/README.zh-CN.md +21 -20
  182. package/templates/.agents/hooks/lifecycle-delegation.js +64 -0
  183. package/templates/.agents/rules/README.en.md +2 -0
  184. package/templates/.agents/rules/README.zh-CN.md +2 -0
  185. package/templates/.agents/rules/commit-and-pr.en.md +1 -0
  186. package/templates/.agents/rules/commit-and-pr.zh-CN.md +1 -0
  187. package/templates/.agents/rules/human-decision-context.en.md +14 -12
  188. package/templates/.agents/rules/human-decision-context.zh-CN.md +14 -12
  189. package/templates/.agents/rules/lifecycle-orchestration.en.md +21 -0
  190. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +21 -0
  191. package/templates/.agents/rules/next-step-output.en.md +16 -0
  192. package/templates/.agents/rules/next-step-output.zh-CN.md +16 -0
  193. package/templates/.agents/rules/no-mid-flow-questions.en.md +2 -0
  194. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +2 -0
  195. package/templates/.agents/rules/release-commands.en.md +2 -2
  196. package/templates/.agents/rules/release-commands.github.en.md +15 -39
  197. package/templates/.agents/rules/release-commands.github.zh-CN.md +14 -38
  198. package/templates/.agents/rules/release-commands.zh-CN.md +2 -2
  199. package/templates/.agents/rules/review-handshake.en.md +4 -3
  200. package/templates/.agents/rules/review-handshake.zh-CN.md +4 -3
  201. package/templates/.agents/rules/review-method.en.md +113 -0
  202. package/templates/.agents/rules/review-method.zh-CN.md +98 -0
  203. package/templates/.agents/rules/testing-discipline.en.md +20 -2
  204. package/templates/.agents/rules/testing-discipline.zh-CN.md +20 -2
  205. package/templates/.agents/skills/analyze-task/SKILL.en.md +9 -6
  206. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +9 -6
  207. package/templates/.agents/skills/block-task/SKILL.en.md +4 -4
  208. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +4 -4
  209. package/templates/.agents/skills/cancel-task/SKILL.en.md +4 -4
  210. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +5 -5
  211. package/templates/.agents/skills/check-task/SKILL.en.md +16 -16
  212. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -16
  213. package/templates/.agents/skills/close-codescan/SKILL.en.md +4 -4
  214. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +4 -4
  215. package/templates/.agents/skills/close-dependabot/SKILL.en.md +4 -4
  216. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +4 -4
  217. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  218. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -2
  219. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +14 -16
  220. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +14 -16
  221. package/templates/.agents/skills/code-task/reference/output-template.en.md +3 -3
  222. package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +3 -3
  223. package/templates/.agents/skills/commit/SKILL.en.md +11 -1
  224. package/templates/.agents/skills/commit/SKILL.zh-CN.md +11 -1
  225. package/templates/.agents/skills/commit/reference/task-status-update.en.md +24 -28
  226. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +24 -28
  227. package/templates/.agents/skills/complete-task/SKILL.en.md +3 -3
  228. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +3 -3
  229. package/templates/.agents/skills/complete-task/config/verify.en.json +0 -1
  230. package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +0 -1
  231. package/templates/.agents/skills/create-pr/SKILL.en.md +10 -10
  232. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +10 -10
  233. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +3 -3
  234. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +3 -3
  235. package/templates/.agents/skills/create-release-note/SKILL.en.md +17 -38
  236. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +16 -37
  237. package/templates/.agents/skills/create-task/SKILL.en.md +15 -11
  238. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +15 -11
  239. package/templates/.agents/skills/create-task/reference/context-capture.en.md +29 -0
  240. package/templates/.agents/skills/create-task/reference/context-capture.zh-CN.md +29 -0
  241. package/templates/.agents/skills/import-codescan/SKILL.en.md +5 -5
  242. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +5 -5
  243. package/templates/.agents/skills/import-dependabot/SKILL.en.md +5 -5
  244. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +5 -5
  245. package/templates/.agents/skills/import-issue/SKILL.en.md +5 -5
  246. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -5
  247. package/templates/.agents/skills/init-labels/SKILL.en.md +4 -4
  248. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +4 -4
  249. package/templates/.agents/skills/init-milestones/SKILL.en.md +4 -4
  250. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +4 -4
  251. package/templates/.agents/skills/plan-task/SKILL.en.md +5 -5
  252. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -5
  253. package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
  254. package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
  255. package/templates/.agents/skills/release/SKILL.en.md +1 -1
  256. package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -2
  257. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -3
  258. package/templates/.agents/skills/review-analysis/config/verify.en.json +6 -0
  259. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +6 -0
  260. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +13 -15
  261. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +13 -15
  262. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +51 -5
  263. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +52 -6
  264. package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +25 -22
  265. package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +25 -22
  266. package/templates/.agents/skills/review-code/SKILL.en.md +28 -8
  267. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +28 -8
  268. package/templates/.agents/skills/review-code/config/verify.en.json +7 -0
  269. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +7 -0
  270. package/templates/.agents/skills/review-code/reference/concurrency-risks.en.md +25 -0
  271. package/templates/.agents/skills/review-code/reference/concurrency-risks.zh-CN.md +25 -0
  272. package/templates/.agents/skills/review-code/reference/cross-platform-risks.en.md +27 -0
  273. package/templates/.agents/skills/review-code/reference/cross-platform-risks.zh-CN.md +27 -0
  274. package/templates/.agents/skills/review-code/reference/documentation-antipatterns.en.md +13 -0
  275. package/templates/.agents/skills/review-code/reference/documentation-antipatterns.zh-CN.md +13 -0
  276. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +25 -0
  277. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +25 -0
  278. package/templates/.agents/skills/review-code/reference/output-templates.en.md +22 -24
  279. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +22 -24
  280. package/templates/.agents/skills/review-code/reference/report-template.en.md +54 -7
  281. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +54 -7
  282. package/templates/.agents/skills/review-code/reference/review-criteria.en.md +29 -24
  283. package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +29 -24
  284. package/templates/.agents/skills/review-code/reference/security-risks.en.md +25 -0
  285. package/templates/.agents/skills/review-code/reference/security-risks.zh-CN.md +25 -0
  286. package/templates/.agents/skills/review-plan/SKILL.en.md +13 -2
  287. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -3
  288. package/templates/.agents/skills/review-plan/config/verify.en.json +6 -0
  289. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +6 -0
  290. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +13 -15
  291. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +13 -15
  292. package/templates/.agents/skills/review-plan/reference/report-template.en.md +61 -5
  293. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +61 -5
  294. package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +21 -24
  295. package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +21 -24
  296. package/templates/.agents/skills/run-task/SKILL.en.md +22 -0
  297. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +22 -0
  298. package/templates/.agents/skills/run-task/config/verify.en.json +9 -0
  299. package/templates/.agents/skills/run-task/config/verify.zh-CN.json +9 -0
  300. package/templates/.agents/skills/test/SKILL.en.md +4 -4
  301. package/templates/.agents/skills/test/SKILL.zh-CN.md +4 -4
  302. package/templates/.agents/skills/test-integration/SKILL.en.md +4 -4
  303. package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +4 -4
  304. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +5 -5
  305. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +5 -5
  306. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +647 -203
  307. package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +4 -4
  308. package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +4 -4
  309. package/templates/.agents/skills/watch-pr/SKILL.en.md +8 -8
  310. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +8 -8
  311. package/templates/.agents/templates/task.en.md +18 -0
  312. package/templates/.agents/templates/task.zh-CN.md +18 -0
  313. package/templates/.claude/agents/agent-infra-lifecycle-executor.en.md +6 -0
  314. package/templates/.claude/agents/agent-infra-lifecycle-executor.zh-CN.md +6 -0
  315. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.en.md +6 -0
  316. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.zh-CN.md +6 -0
  317. package/templates/.claude/commands/run-task.en.md +8 -0
  318. package/templates/.claude/commands/run-task.zh-CN.md +8 -0
  319. package/templates/.claude/settings.json +24 -0
  320. package/templates/.codex/agents/agent-infra-lifecycle-executor.en.toml +3 -0
  321. package/templates/.codex/agents/agent-infra-lifecycle-executor.zh-CN.toml +3 -0
  322. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.en.toml +3 -0
  323. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.zh-CN.toml +3 -0
  324. package/templates/.gemini/commands/_project_/run-task.en.toml +8 -0
  325. package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +8 -0
  326. package/templates/.opencode/commands/run-task.en.md +11 -0
  327. package/templates/.opencode/commands/run-task.zh-CN.md +11 -0
@@ -10,7 +10,8 @@ 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
12
  import { resolveTaskBranch } from '../task-resolver.js';
13
- import { resolveTools, toolConfigDirCandidates, toolProjectDirCandidates } from '../tools.js';
13
+ import { toolConfigDirCandidates, toolProjectDirCandidates } from '../tools.js';
14
+ import { createSandboxCapabilityPlan } from '../agent-client-reconciler.js';
14
15
  import { fetchSandboxRows } from './list-running.js';
15
16
  import { lookupShortIdByBranch } from '../../task/short-id.js';
16
17
  const USAGE = `Usage:
@@ -284,7 +285,7 @@ export async function rm(args) {
284
285
  throw new Error(USAGE);
285
286
  }
286
287
  const config = loadConfig();
287
- const tools = resolveTools(config);
288
+ const tools = [...createSandboxCapabilityPlan(config).cleanupInventory];
288
289
  if (values.purge) {
289
290
  await rmPurge(config, tools);
290
291
  return;
@@ -3,6 +3,8 @@ import path from 'node:path';
3
3
  import { homedir, platform } from 'node:os';
4
4
  import { execFileSync } from 'node:child_process';
5
5
  import pc from 'picocolors';
6
+ import { normalizeAgentClients } from '../agent-clients/config.js';
7
+ import { isAgentClientId } from '../agent-clients/types.js';
6
8
  import { validateSandboxEngine } from './engine.js';
7
9
  import { hostJoin } from './engines/wsl2-paths.js';
8
10
  import { findRuntimeEngineMismatches } from './runtime-engines.js';
@@ -10,7 +12,7 @@ import { parseCustomTools } from './tools.js';
10
12
  const DEFAULTS = Object.freeze({
11
13
  engine: null,
12
14
  runtimes: ['node22'],
13
- tools: ['agent-infra', 'claude-code', 'codex', 'gemini-cli', 'opencode'],
15
+ tools: ['agent-infra'],
14
16
  refreshIntervalDays: 7,
15
17
  dockerfile: null,
16
18
  vm: {
@@ -46,6 +48,18 @@ function cloneDefaults() {
46
48
  vm: { ...DEFAULTS.vm }
47
49
  };
48
50
  }
51
+ function resolveSandboxToolIds(sandbox, agentClients, defaults) {
52
+ if (Array.isArray(sandbox.tools)) {
53
+ const tools = sandbox.tools.filter((tool) => typeof tool === 'string');
54
+ return agentClients.source === 'canonical'
55
+ ? tools.filter((tool) => !isAgentClientId(tool))
56
+ : tools;
57
+ }
58
+ if (agentClients.remainingSandboxTools !== undefined) {
59
+ return [...agentClients.remainingSandboxTools];
60
+ }
61
+ return [...defaults];
62
+ }
49
63
  export function loadConfig({ platformFn = platform, writeStderr = (chunk) => process.stderr.write(chunk) } = {}) {
50
64
  const repoRoot = detectRepoRoot();
51
65
  const home = homedir();
@@ -57,6 +71,8 @@ export function loadConfig({ platformFn = platform, writeStderr = (chunk) => pro
57
71
  throw new Error('No .agents/.airc.json found. Run "ai init" first.');
58
72
  }
59
73
  const airc = JSON.parse(fs.readFileSync(configPath, 'utf8'));
74
+ const sandboxInput = airc.sandbox;
75
+ const agentClients = normalizeAgentClients(airc);
60
76
  const defaults = cloneDefaults();
61
77
  const sandbox = airc.sandbox ?? {};
62
78
  const engine = validateSandboxEngine(sandbox.engine ?? defaults.engine, { platformFn });
@@ -83,6 +99,7 @@ export function loadConfig({ platformFn = platform, writeStderr = (chunk) => pro
83
99
  }
84
100
  }
85
101
  const customTools = parseCustomTools(sandbox.customTools, { home });
102
+ const tools = resolveSandboxToolIds(sandbox, agentClients, defaults.tools);
86
103
  return {
87
104
  repoRoot,
88
105
  configPath,
@@ -97,10 +114,10 @@ export function loadConfig({ platformFn = platform, writeStderr = (chunk) => pro
97
114
  dotfilesDir: hostJoin(home, '.agent-infra', 'dotfiles'),
98
115
  engine,
99
116
  runtimes,
100
- tools: Array.isArray(sandbox.tools) && sandbox.tools.length > 0
101
- ? [...sandbox.tools]
102
- : defaults.tools,
117
+ tools,
103
118
  customTools,
119
+ agentClientState: agentClients.state,
120
+ agentClientSource: agentClients.source,
104
121
  refreshIntervalDays: asNonNegativeIntegerOrDefault(sandbox.refreshIntervalDays, defaults.refreshIntervalDays),
105
122
  dockerfile,
106
123
  vm: {
@@ -75,6 +75,9 @@ export function sandboxImageConfigLabel(config) {
75
75
  export function sandboxImageRefreshLabel(config) {
76
76
  return `${sandboxLabel(config)}.last-refresh`;
77
77
  }
78
+ export function sandboxRuntimeCapabilityLabel(config) {
79
+ return `agent-infra.sandbox.runtime-capability.${config.project}`;
80
+ }
78
81
  export function parsePositiveIntegerOption(value, optionName) {
79
82
  if (value === undefined || value === null) {
80
83
  return undefined;
@@ -3,6 +3,7 @@ import os from 'node:os';
3
3
  import path from 'node:path';
4
4
  import { createHash } from 'node:crypto';
5
5
  import { fileURLToPath } from 'node:url';
6
+ import { removeDirRecursive } from '../remove-dir.js';
6
7
  const RUNTIMES_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), 'runtimes');
7
8
  function listRuntimeFragments() {
8
9
  return fs.readdirSync(RUNTIMES_DIR)
@@ -63,7 +64,7 @@ export function prepareDockerfile(config) {
63
64
  path: tempPath,
64
65
  signature: dockerfileSignature(config),
65
66
  cleanup() {
66
- fs.rmSync(tempDir, { recursive: true, force: true });
67
+ removeDirRecursive(tempDir);
67
68
  }
68
69
  };
69
70
  }
@@ -14,7 +14,7 @@ export function buildImageSignature(preparedDockerfile, tools) {
14
14
  .digest('hex')
15
15
  .slice(0, 12);
16
16
  }
17
- export function buildSandboxImageArgs(config, tools, dockerfilePath, imageSignature, { engine, runFn = runEngine, runSafeFn = runSafeEngine, env = process.env, refresh = false, lastRefresh } = {}) {
17
+ export function buildSandboxImageArgs(config, tools, dockerfilePath, imageSignature, { engine, runFn = runEngine, runSafeFn = runSafeEngine, env = process.env, refresh = false, lastRefresh, buildProxyArgs = [] } = {}) {
18
18
  const selectedEngine = engine ?? detectEngine({ engine: config.engine });
19
19
  const { uid: hostUid, gid: hostGid } = resolveBuildUid({
20
20
  engine: selectedEngine,
@@ -39,6 +39,7 @@ export function buildSandboxImageArgs(config, tools, dockerfilePath, imageSignat
39
39
  '--label',
40
40
  `${sandboxImageConfigLabel(config)}=${imageSignature}`
41
41
  ];
42
+ args.push(...buildProxyArgs);
42
43
  if (lastRefresh !== undefined) {
43
44
  args.push('--label', `${sandboxImageRefreshLabel(config)}=${lastRefresh}`);
44
45
  }
@@ -1,6 +1,6 @@
1
- import fs from 'node:fs';
2
1
  import path from 'node:path';
3
- import { run } from './shell.js';
2
+ import { run, runSafe } from './shell.js';
3
+ import { removeDirRecursive } from '../remove-dir.js';
4
4
  export function assertManagedPath(root, target) {
5
5
  const resolvedRoot = path.resolve(root);
6
6
  const resolvedTarget = path.resolve(target);
@@ -12,14 +12,19 @@ export function assertManagedPath(root, target) {
12
12
  }
13
13
  export function removeManagedDir(root, dir) {
14
14
  assertManagedPath(root, dir);
15
- fs.rmSync(dir, { recursive: true, force: true });
15
+ removeDirRecursive(dir);
16
16
  }
17
- export function removeWorktreeDir(repoRoot, worktreeBase, dir) {
17
+ export function removeWorktreeDir(repoRoot, worktreeBase, dir, { runFn = run, runSafeFn = runSafe } = {}) {
18
18
  try {
19
- run('git', ['-C', repoRoot, 'worktree', 'remove', dir, '--force']);
19
+ runFn('git', ['-C', repoRoot, 'worktree', 'remove', dir, '--force']);
20
20
  }
21
21
  catch {
22
+ // On WSL2 the worktree is registered under its `/mnt/<drive>/...` path, so
23
+ // `git worktree remove <windows-path>` cannot match it. Delete the managed
24
+ // directory directly, then prune the now-dangling worktree metadata so the
25
+ // branch is no longer reported as checked out.
22
26
  removeManagedDir(worktreeBase, dir);
27
+ runSafeFn('git', ['-C', repoRoot, 'worktree', 'prune']);
23
28
  }
24
29
  }
25
30
  //# sourceMappingURL=managed-fs.js.map
@@ -1,6 +1,8 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { containerNameCandidates, sandboxBranchLabel, sandboxLabel } from './constants.js';
3
+ import { containerNameCandidates, sandboxBranchLabel, sandboxLabel, sandboxRuntimeCapabilityLabel } from './constants.js';
4
+ import { isAgentClientId } from '../agent-clients/types.js';
5
+ import { createSandboxCapabilityPlan } from './agent-client-reconciler.js';
4
6
  import { toEnginePath } from './engines/wsl2-paths.js';
5
7
  import { sandboxCoreBindMounts } from './mounts.js';
6
8
  import { runEngine, runOkEngine, runVerboseEngine } from './shell.js';
@@ -17,6 +19,19 @@ export function classifySandboxRecovery(snapshot) {
17
19
  message: 'Container identity does not match the requested sandbox branch.'
18
20
  });
19
21
  }
22
+ if (snapshot.runtimeCapabilityOk === false) {
23
+ findings.push({
24
+ repairKind: 'hard-failure',
25
+ message: 'Container runtime capability signature does not match the current sandbox plan.'
26
+ });
27
+ }
28
+ for (const mountPath of snapshot.unexpectedCapabilityMounts ?? []) {
29
+ findings.push({
30
+ repairKind: 'hard-failure',
31
+ message: `Disabled Agent Client capability remains mounted at ${mountPath}.`,
32
+ path: mountPath
33
+ });
34
+ }
20
35
  for (const mount of snapshot.mounts) {
21
36
  if (mount.actualType !== mount.expectedType
22
37
  || !mount.sourceMatches
@@ -74,25 +89,11 @@ export function classifySandboxRecovery(snapshot) {
74
89
  path: '/home/devuser/.bash_aliases'
75
90
  });
76
91
  }
77
- if (snapshot.codex) {
78
- if (!snapshot.codex.commandAvailable) {
79
- findings.push({
80
- repairKind: 'hard-failure',
81
- message: 'Codex is not available on PATH inside the sandbox.'
82
- });
83
- }
84
- if (!snapshot.codex.stateWritable) {
92
+ for (const check of snapshot.agentClientChecks) {
93
+ if (check.applicable && !check.healthy) {
85
94
  findings.push({
86
- repairKind: 'permissions',
87
- message: 'Codex state directory is not writable by devuser.',
88
- path: '/home/devuser/.codex'
89
- });
90
- }
91
- if (snapshot.codex.promptsSourceExists && !snapshot.codex.promptsValid) {
92
- findings.push({
93
- repairKind: 'builtin-link',
94
- message: 'Codex prompts link does not point to the workspace commands directory.',
95
- path: '/home/devuser/.codex/prompts'
95
+ ...check.finding,
96
+ ...(check.repair === undefined ? {} : { repair: check.repair })
96
97
  });
97
98
  }
98
99
  }
@@ -154,8 +155,7 @@ function hostSourceAccessible(hostPath, writable) {
154
155
  }
155
156
  }
156
157
  function expectedMounts(params) {
157
- const { config, branch, actualMounts } = params;
158
- const tools = resolveTools(config);
158
+ const { config, branch, tools, actualMounts } = params;
159
159
  const core = sandboxCoreBindMounts(config, branch).map((mount) => ({
160
160
  path: mount.containerPath,
161
161
  expectedType: 'bind',
@@ -174,7 +174,9 @@ function expectedMounts(params) {
174
174
  }];
175
175
  }));
176
176
  const persistentTools = tools
177
- .filter((tool) => !tool.tmpfs && actualMounts.has(tool.containerMount))
177
+ .filter((tool) => !tool.tmpfs
178
+ && (config.agentClientSource === 'canonical'
179
+ || actualMounts.has(tool.containerMount)))
178
180
  .map((tool) => ({
179
181
  path: tool.containerMount,
180
182
  expectedType: 'bind',
@@ -245,7 +247,8 @@ export function collectSandboxRecoverySnapshot(params) {
245
247
  });
246
248
  }
247
249
  }
248
- const tools = resolveTools(params.config);
250
+ const capabilityPlan = createSandboxCapabilityPlan(params.config);
251
+ const tools = [...capabilityPlan.tools];
249
252
  const seeds = declaredTmpfsSeedEntries(tools).map((seed) => {
250
253
  const mounted = mountsByDestination.get(seed.stagingPath)?.Type === 'bind';
251
254
  return {
@@ -266,11 +269,18 @@ export function collectSandboxRecoverySnapshot(params) {
266
269
  }));
267
270
  const labels = inspection.Config?.Labels ?? {};
268
271
  const branchLabel = labels[sandboxBranchLabel(params.config)];
269
- const hasCodex = tools.some((tool) => tool.id === 'codex');
272
+ const selectedToolIds = new Set(tools.map((tool) => tool.id));
273
+ const enforceCanonicalPlan = params.config.agentClientSource === 'canonical';
274
+ const unexpectedCapabilityMounts = enforceCanonicalPlan
275
+ ? capabilityPlan.cleanupInventory
276
+ .filter((tool) => isAgentClientId(tool.id) && !selectedToolIds.has(tool.id))
277
+ .map((tool) => tool.containerMount)
278
+ .filter((mountPath) => mountsByDestination.has(mountPath))
279
+ : [];
270
280
  const mountSnapshots = expectedMounts({
271
281
  config: params.config,
272
282
  branch: params.branch,
273
- engine: params.engine,
283
+ tools,
274
284
  actualMounts: mountsByDestination
275
285
  }).map((expected) => {
276
286
  const actual = mountsByDestination.get(expected.path);
@@ -304,18 +314,25 @@ export function collectSandboxRecoverySnapshot(params) {
304
314
  identityOk: typeof inspection.Id === 'string'
305
315
  && inspection.Id.length > 0
306
316
  && branchLabel === params.branch,
317
+ runtimeCapabilityOk: enforceCanonicalPlan
318
+ ? labels[sandboxRuntimeCapabilityLabel(params.config)] === capabilityPlan.runtimeSignature
319
+ : undefined,
320
+ unexpectedCapabilityMounts,
307
321
  mounts: mountSnapshots,
308
322
  tmpfs,
309
323
  seeds,
310
324
  aliasesReadable: probe(params.engine, params.container, 'test -r "$1"', ['/home/devuser/.bash_aliases'], runOkFn),
311
- codex: hasCodex
312
- ? {
313
- commandAvailable: probe(params.engine, params.container, 'command -v codex', [], runOkFn),
314
- stateWritable: probe(params.engine, params.container, 'probe="$1/.agent-infra-codex-state-$$"; trap \'rm -f -- "$probe"\' EXIT; : > "$probe"', ['/home/devuser/.codex'], runOkFn),
315
- promptsSourceExists: probe(params.engine, params.container, 'test -d "$1"', ['/workspace/.codex/commands'], runOkFn),
316
- promptsValid: probe(params.engine, params.container, 'test "$(readlink -- "$1")" = "$2"', ['/home/devuser/.codex/prompts', '/workspace/.codex/commands'], runOkFn)
317
- }
318
- : undefined
325
+ agentClientChecks: capabilityPlan.recoveryChecks.map(({ adapterId, check }) => {
326
+ const applicable = check.when === undefined || probe(params.engine, params.container, check.when.script, [...check.when.args], runOkFn, check.when.user);
327
+ return {
328
+ adapterId,
329
+ checkId: check.id,
330
+ applicable,
331
+ healthy: !applicable || probe(params.engine, params.container, check.probe.script, [...check.probe.args], runOkFn, check.probe.user),
332
+ finding: check.finding,
333
+ ...(check.repair === undefined ? {} : { repair: check.repair })
334
+ };
335
+ })
319
336
  };
320
337
  }
321
338
  export function prepareTmpfsMounts(params) {
@@ -394,10 +411,9 @@ function repairFindings(params) {
394
411
  deps: params.deps
395
412
  });
396
413
  const runVerboseFn = params.deps?.runVerbose ?? runVerboseEngine;
397
- if (params.findings.some((finding) => finding.repairKind === 'builtin-link')) {
414
+ for (const repair of params.findings.flatMap((finding) => finding.repair === undefined ? [] : [finding.repair])) {
398
415
  runVerboseFn(params.engine, 'docker', [
399
- 'exec', '--user', 'devuser', params.container, 'ln', '-sfn',
400
- '/workspace/.codex/commands', '/home/devuser/.codex/prompts'
416
+ 'exec', '--user', repair.user, params.container, repair.command, ...repair.args
401
417
  ]);
402
418
  }
403
419
  }
@@ -5,20 +5,41 @@ ENV NPM_CONFIG_PREFIX=/home/devuser/.npm-global
5
5
  ENV PATH="/home/devuser/.npm-global/bin:${PATH}"
6
6
 
7
7
  ARG AI_TOOL_PACKAGES=
8
- RUN set -e && \
8
+ RUN --mount=type=secret,id=HTTP_PROXY \
9
+ --mount=type=secret,id=HTTPS_PROXY \
10
+ --mount=type=secret,id=NO_PROXY \
11
+ export HTTP_PROXY="$(cat /run/secrets/HTTP_PROXY 2>/dev/null || true)" && \
12
+ export HTTPS_PROXY="$(cat /run/secrets/HTTPS_PROXY 2>/dev/null || true)" && \
13
+ export NO_PROXY="$(cat /run/secrets/NO_PROXY 2>/dev/null || true)" && \
14
+ export http_proxy="${HTTP_PROXY}" https_proxy="${HTTPS_PROXY}" no_proxy="${NO_PROXY}" && \
15
+ set -e && \
9
16
  for pkg in ${AI_TOOL_PACKAGES}; do \
10
17
  npm install -g "$pkg"; \
11
18
  done
12
19
 
13
20
  ARG AI_TOOLS_SHELL_INSTALL_B64=
14
- RUN if [ -n "${AI_TOOLS_SHELL_INSTALL_B64}" ]; then \
21
+ RUN --mount=type=secret,id=HTTP_PROXY \
22
+ --mount=type=secret,id=HTTPS_PROXY \
23
+ --mount=type=secret,id=NO_PROXY \
24
+ export HTTP_PROXY="$(cat /run/secrets/HTTP_PROXY 2>/dev/null || true)" && \
25
+ export HTTPS_PROXY="$(cat /run/secrets/HTTPS_PROXY 2>/dev/null || true)" && \
26
+ export NO_PROXY="$(cat /run/secrets/NO_PROXY 2>/dev/null || true)" && \
27
+ export http_proxy="${HTTP_PROXY}" https_proxy="${HTTPS_PROXY}" no_proxy="${NO_PROXY}" && \
28
+ if [ -n "${AI_TOOLS_SHELL_INSTALL_B64}" ]; then \
15
29
  set -e && \
16
30
  echo "${AI_TOOLS_SHELL_INSTALL_B64}" | base64 -d > /tmp/ai-tools-install.sh && \
17
31
  bash /tmp/ai-tools-install.sh && \
18
32
  rm /tmp/ai-tools-install.sh; \
19
33
  fi
20
34
 
21
- RUN npm install -g pyright
35
+ RUN --mount=type=secret,id=HTTP_PROXY \
36
+ --mount=type=secret,id=HTTPS_PROXY \
37
+ --mount=type=secret,id=NO_PROXY \
38
+ export HTTP_PROXY="$(cat /run/secrets/HTTP_PROXY 2>/dev/null || true)" && \
39
+ export HTTPS_PROXY="$(cat /run/secrets/HTTPS_PROXY 2>/dev/null || true)" && \
40
+ export NO_PROXY="$(cat /run/secrets/NO_PROXY 2>/dev/null || true)" && \
41
+ export http_proxy="${HTTP_PROXY}" https_proxy="${HTTPS_PROXY}" no_proxy="${NO_PROXY}" && \
42
+ npm install -g pyright
22
43
 
23
44
  RUN mkdir -p /home/devuser/.local/share /home/devuser/.local/state
24
45
 
@@ -19,7 +19,14 @@ RUN if [ "${HOST_UID}" = "0" ]; then \
19
19
  useradd -u ${HOST_UID} -g ${HOST_GID} -m -s /bin/bash devuser; \
20
20
  fi
21
21
 
22
- RUN apt-get update && apt-get install -y \
22
+ RUN --mount=type=secret,id=HTTP_PROXY \
23
+ --mount=type=secret,id=HTTPS_PROXY \
24
+ --mount=type=secret,id=NO_PROXY \
25
+ export HTTP_PROXY="$(cat /run/secrets/HTTP_PROXY 2>/dev/null || true)" && \
26
+ export HTTPS_PROXY="$(cat /run/secrets/HTTPS_PROXY 2>/dev/null || true)" && \
27
+ export NO_PROXY="$(cat /run/secrets/NO_PROXY 2>/dev/null || true)" && \
28
+ export http_proxy="${HTTP_PROXY}" https_proxy="${HTTPS_PROXY}" no_proxy="${NO_PROXY}" && \
29
+ apt-get update && apt-get install -y \
23
30
  curl wget git vim file jq \
24
31
  build-essential ca-certificates gnupg lsb-release \
25
32
  libevent-core-2.1-7 libncursesw6 libtinfo6 \
@@ -1,3 +1,10 @@
1
- RUN apt-get update && apt-get install -y \
1
+ RUN --mount=type=secret,id=HTTP_PROXY \
2
+ --mount=type=secret,id=HTTPS_PROXY \
3
+ --mount=type=secret,id=NO_PROXY \
4
+ export HTTP_PROXY="$(cat /run/secrets/HTTP_PROXY 2>/dev/null || true)" && \
5
+ export HTTPS_PROXY="$(cat /run/secrets/HTTPS_PROXY 2>/dev/null || true)" && \
6
+ export NO_PROXY="$(cat /run/secrets/NO_PROXY 2>/dev/null || true)" && \
7
+ export http_proxy="${HTTP_PROXY}" https_proxy="${HTTPS_PROXY}" no_proxy="${NO_PROXY}" && \
8
+ apt-get update && apt-get install -y \
2
9
  openjdk-17-jdk maven \
3
10
  && rm -rf /var/lib/apt/lists/*
@@ -1,3 +1,10 @@
1
- RUN apt-get update && apt-get install -y \
1
+ RUN --mount=type=secret,id=HTTP_PROXY \
2
+ --mount=type=secret,id=HTTPS_PROXY \
3
+ --mount=type=secret,id=NO_PROXY \
4
+ export HTTP_PROXY="$(cat /run/secrets/HTTP_PROXY 2>/dev/null || true)" && \
5
+ export HTTPS_PROXY="$(cat /run/secrets/HTTPS_PROXY 2>/dev/null || true)" && \
6
+ export NO_PROXY="$(cat /run/secrets/NO_PROXY 2>/dev/null || true)" && \
7
+ export http_proxy="${HTTP_PROXY}" https_proxy="${HTTPS_PROXY}" no_proxy="${NO_PROXY}" && \
8
+ apt-get update && apt-get install -y \
2
9
  openjdk-21-jdk maven \
3
10
  && rm -rf /var/lib/apt/lists/*
@@ -1,3 +1,10 @@
1
- RUN bash -o pipefail -c 'curl -fsSL --retry 5 --retry-delay 2 --retry-all-errors https://deb.nodesource.com/setup_20.x | bash -' && \
1
+ RUN --mount=type=secret,id=HTTP_PROXY \
2
+ --mount=type=secret,id=HTTPS_PROXY \
3
+ --mount=type=secret,id=NO_PROXY \
4
+ export HTTP_PROXY="$(cat /run/secrets/HTTP_PROXY 2>/dev/null || true)" && \
5
+ export HTTPS_PROXY="$(cat /run/secrets/HTTPS_PROXY 2>/dev/null || true)" && \
6
+ export NO_PROXY="$(cat /run/secrets/NO_PROXY 2>/dev/null || true)" && \
7
+ export http_proxy="${HTTP_PROXY}" https_proxy="${HTTPS_PROXY}" no_proxy="${NO_PROXY}" && \
8
+ bash -o pipefail -c 'curl -fsSL --retry 5 --retry-delay 2 --retry-all-errors https://deb.nodesource.com/setup_20.x | bash -' && \
2
9
  apt-get install -y nodejs && \
3
10
  rm -rf /var/lib/apt/lists/*
@@ -1,3 +1,10 @@
1
- RUN bash -o pipefail -c 'curl -fsSL --retry 5 --retry-delay 2 --retry-all-errors https://deb.nodesource.com/setup_22.x | bash -' && \
1
+ RUN --mount=type=secret,id=HTTP_PROXY \
2
+ --mount=type=secret,id=HTTPS_PROXY \
3
+ --mount=type=secret,id=NO_PROXY \
4
+ export HTTP_PROXY="$(cat /run/secrets/HTTP_PROXY 2>/dev/null || true)" && \
5
+ export HTTPS_PROXY="$(cat /run/secrets/HTTPS_PROXY 2>/dev/null || true)" && \
6
+ export NO_PROXY="$(cat /run/secrets/NO_PROXY 2>/dev/null || true)" && \
7
+ export http_proxy="${HTTP_PROXY}" https_proxy="${HTTPS_PROXY}" no_proxy="${NO_PROXY}" && \
8
+ bash -o pipefail -c 'curl -fsSL --retry 5 --retry-delay 2 --retry-all-errors https://deb.nodesource.com/setup_22.x | bash -' && \
2
9
  apt-get install -y nodejs && \
3
10
  rm -rf /var/lib/apt/lists/*
@@ -1,3 +1,10 @@
1
- RUN apt-get update && apt-get install -y \
1
+ RUN --mount=type=secret,id=HTTP_PROXY \
2
+ --mount=type=secret,id=HTTPS_PROXY \
3
+ --mount=type=secret,id=NO_PROXY \
4
+ export HTTP_PROXY="$(cat /run/secrets/HTTP_PROXY 2>/dev/null || true)" && \
5
+ export HTTPS_PROXY="$(cat /run/secrets/HTTPS_PROXY 2>/dev/null || true)" && \
6
+ export NO_PROXY="$(cat /run/secrets/NO_PROXY 2>/dev/null || true)" && \
7
+ export http_proxy="${HTTP_PROXY}" https_proxy="${HTTPS_PROXY}" no_proxy="${NO_PROXY}" && \
8
+ apt-get update && apt-get install -y \
2
9
  python3 python3-pip python3-venv \
3
10
  && rm -rf /var/lib/apt/lists/*
@@ -8,7 +8,8 @@ function normalizeOptions(opts = {}, stdio) {
8
8
  cwd: opts.cwd,
9
9
  encoding: opts.encoding,
10
10
  stdio,
11
- timeout: opts.timeout ?? DEFAULT_TIMEOUT_MS
11
+ timeout: opts.timeout ?? DEFAULT_TIMEOUT_MS,
12
+ env: opts.env
12
13
  };
13
14
  }
14
15
  function resolveCommand(cmd) {
@@ -113,7 +114,7 @@ export function runSafe(cmd, args, opts = {}) {
113
114
  export function commandForEngine(engine, cmd, args = []) {
114
115
  if (engine === 'wsl2') {
115
116
  const resolvedWrapper = resolveCommand('wsl.exe');
116
- return { cmd: resolvedWrapper, args: ['--', cmd, ...args] };
117
+ return { cmd: resolvedWrapper, args: ['--exec', cmd, ...args] };
117
118
  }
118
119
  if (cmd === 'docker') {
119
120
  const context = getAdapter(engine).dockerContext;
@@ -0,0 +1,8 @@
1
+ const SANDBOX_HOOK_PHASES = [
2
+ 'prepare',
3
+ 'before-container-create',
4
+ 'after-container-start',
5
+ 'before-enter'
6
+ ];
7
+ export { SANDBOX_HOOK_PHASES };
8
+ //# sourceMappingURL=tool-types.js.map
@@ -1,4 +1,5 @@
1
1
  import path from 'node:path';
2
+ import { createSandboxCapabilityPlan } from './agent-client-reconciler.js';
2
3
  import { safeNameCandidates, sanitizeBranchName } from './constants.js';
3
4
  import { hostJoin } from './engines/wsl2-paths.js';
4
5
  export function tmpfsSeedStagingPath(toolId, index) {
@@ -20,108 +21,6 @@ export function declaredTmpfsSeedEntries(tools) {
20
21
  })));
21
22
  }
22
23
  const TOOL_ID_PATTERN = /^[a-z0-9][a-z0-9-]*$/;
23
- function createBuiltinTools(home, project) {
24
- return {
25
- 'claude-code': {
26
- id: 'claude-code',
27
- name: 'Claude Code',
28
- install: { type: 'npm', cmd: '@anthropic-ai/claude-code@latest' },
29
- sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'claude-code'),
30
- containerMount: '/home/devuser/.claude',
31
- versionCmd: 'claude --version',
32
- setupHint: 'Authenticates via host credentials live-mounted at ~/.claude/.credentials.json',
33
- // Claude Code stores user data (.claude.json — onboarding state, theme,
34
- // workspace trust) at $HOME/.claude.json by default, which sits OUTSIDE
35
- // the bind-mounted /home/devuser/.claude tree, so our preseeded
36
- // .claude.json never gets read and the theme picker re-runs on every
37
- // container start. Pinning CLAUDE_CONFIG_DIR to the tool mount relocates
38
- // .claude.json into the same directory as .credentials.json/settings.json,
39
- // letting ensureClaudeOnboarding actually take effect.
40
- envVars: { CLAUDE_CONFIG_DIR: '/home/devuser/.claude' },
41
- hostPreSeedDirs: [
42
- { hostDir: hostJoin(home, '.claude', 'plugins'), sandboxSubdir: 'plugins' }
43
- ],
44
- pathRewriteFiles: [
45
- 'plugins/installed_plugins.json',
46
- 'plugins/known_marketplaces.json'
47
- ],
48
- hostLiveMounts: [
49
- {
50
- hostPath: hostJoin(home, '.agent-infra', 'credentials', project, 'claude-code', '.credentials.json'),
51
- containerSubpath: '.credentials.json'
52
- }
53
- ]
54
- },
55
- codex: {
56
- id: 'codex',
57
- name: 'Codex',
58
- install: { type: 'npm', cmd: '@openai/codex' },
59
- sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'codex'),
60
- containerMount: '/home/devuser/.codex',
61
- versionCmd: 'codex --version',
62
- setupHint: 'Run codex once inside the container and choose Device Code login if needed.',
63
- // codex churns ~/.codex/logs_2.sqlite heavily (upstream openai/codex#24275);
64
- // a bind-mount would write-amplify onto the host SSD via virtiofs. Mount the
65
- // codex home as tmpfs so those logs stay in RAM and die with the container.
66
- // Seeded config (config.toml, model-catalogs) is copied into the tmpfs at
67
- // startup; runtime files like logs_2.sqlite stay in RAM.
68
- tmpfs: { size: '512m', seed: ['config.toml', 'model-catalogs'] },
69
- hostLiveMounts: [
70
- { hostPath: hostJoin(home, '.codex', 'auth.json'), containerSubpath: 'auth.json' }
71
- ],
72
- postSetupCmds: [
73
- 'test -d /workspace/.codex/commands && ln -sfn /workspace/.codex/commands /home/devuser/.codex/prompts || true'
74
- ]
75
- },
76
- opencode: {
77
- id: 'opencode',
78
- name: 'OpenCode',
79
- install: { type: 'npm', cmd: 'opencode-ai' },
80
- sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'opencode'),
81
- containerMount: '/home/devuser/.local/share/opencode',
82
- versionCmd: 'opencode version',
83
- setupHint: 'Configure OpenCode credentials inside the container before first use.',
84
- // OpenCode reads opencode.json from $XDG_CONFIG_HOME/opencode by default,
85
- // outside this tool mount. Pin the config file path so the inherited
86
- // sandbox opencode.json is the one the TUI actually reads.
87
- envVars: { OPENCODE_CONFIG: '/home/devuser/.local/share/opencode/opencode.json' },
88
- hostLiveMounts: [
89
- {
90
- hostPath: hostJoin(home, '.local', 'share', 'opencode', 'auth.json'),
91
- containerSubpath: 'auth.json'
92
- }
93
- ]
94
- },
95
- 'gemini-cli': {
96
- id: 'gemini-cli',
97
- name: 'Gemini CLI',
98
- install: { type: 'npm', cmd: '@google/gemini-cli' },
99
- sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'gemini-cli'),
100
- containerMount: '/home/devuser/.gemini',
101
- versionCmd: 'gemini --version',
102
- setupHint: 'Run gemini inside the container to finish authentication.',
103
- hostLiveMounts: [
104
- { hostPath: hostJoin(home, '.gemini', 'oauth_creds.json'), containerSubpath: 'oauth_creds.json' }
105
- ],
106
- hostPreSeedFiles: [
107
- { hostPath: hostJoin(home, '.gemini', 'settings.json'), sandboxName: 'settings.json' },
108
- { hostPath: hostJoin(home, '.gemini', 'google_accounts.json'), sandboxName: 'google_accounts.json' }
109
- ]
110
- },
111
- 'agent-infra': {
112
- id: 'agent-infra',
113
- name: 'agent-infra CLI',
114
- install: { type: 'npm', cmd: '@fitlab-ai/agent-infra@latest' },
115
- sandboxBase: hostJoin(home, '.agent-infra', 'sandboxes', 'agent-infra'),
116
- containerMount: '/home/devuser/.agent-infra-cli',
117
- versionCmd: 'ai version --raw',
118
- setupHint: 'Provides the ai and agent-infra CLI commands inside the sandbox.'
119
- }
120
- };
121
- }
122
- export function builtinToolIds() {
123
- return Object.keys(createBuiltinTools('', ''));
124
- }
125
24
  function validateTool(tool) {
126
25
  if (!tool.id || !TOOL_ID_PATTERN.test(tool.id)) {
127
26
  throw new Error(`Invalid sandbox tool id: ${String(tool.id)}`);
@@ -308,30 +207,7 @@ export function parseCustomTools(value, options) {
308
207
  return value.map((entry, index) => parseCustomTool(entry, index, options));
309
208
  }
310
209
  export function resolveTools(config) {
311
- const builtins = createBuiltinTools(config.home, config.project);
312
- const customs = config.customTools ?? [];
313
- const seen = new Set();
314
- for (const tool of customs) {
315
- if (builtins[tool.id]) {
316
- throw new Error(`Custom sandbox tool id "${tool.id}" collides with a built-in tool`);
317
- }
318
- if (seen.has(tool.id)) {
319
- throw new Error(`Duplicate sandbox tool id "${tool.id}" in customTools`);
320
- }
321
- seen.add(tool.id);
322
- }
323
- const merged = { ...builtins };
324
- for (const tool of customs) {
325
- merged[tool.id] = tool;
326
- }
327
- return config.tools.map((id) => {
328
- const tool = merged[id];
329
- if (!tool) {
330
- throw new Error(`Unknown sandbox tool: ${id}`);
331
- }
332
- validateTool(tool);
333
- return tool;
334
- });
210
+ return [...createSandboxCapabilityPlan(config).tools];
335
211
  }
336
212
  export function toolConfigDir(tool, project, branch) {
337
213
  return hostJoin(tool.sandboxBase, project, sanitizeBranchName(branch));