@wardby/cli 0.2.0 → 0.3.0

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 (491) hide show
  1. package/.env.example +34 -4
  2. package/README.md +183 -71
  3. package/dist/claude-coding-worker/driver.d.ts +7 -1
  4. package/dist/claude-coding-worker/driver.js +31 -2
  5. package/dist/claude-coding-worker/main.js +5 -2
  6. package/dist/claude-coding-worker/tool-socket.d.ts +9 -0
  7. package/dist/claude-coding-worker/tool-socket.js +26 -0
  8. package/dist/cli-help.d.ts +1 -1
  9. package/dist/cli-help.js +13 -4
  10. package/dist/cli.d.ts +1 -1
  11. package/dist/cli.js +219 -29
  12. package/dist/coding/collect-exclude.d.ts +25 -0
  13. package/dist/coding/collect-exclude.js +76 -0
  14. package/dist/coding/profile.d.ts +67 -22
  15. package/dist/coding/profile.js +58 -25
  16. package/dist/coding/protected-path-wording.d.ts +24 -0
  17. package/dist/coding/protected-path-wording.js +55 -0
  18. package/dist/coding/protected-paths.d.ts +31 -0
  19. package/dist/coding/protected-paths.js +48 -0
  20. package/dist/coding/protocol.d.ts +99 -3
  21. package/dist/coding/protocol.js +155 -4
  22. package/dist/coding/registry/adapters.d.ts +2 -0
  23. package/dist/coding/registry/adapters.js +9 -0
  24. package/dist/coding/registry/allowlist.d.ts +9 -0
  25. package/dist/coding/registry/allowlist.js +28 -0
  26. package/dist/coding/registry/json-scan.d.ts +55 -0
  27. package/dist/coding/registry/json-scan.js +191 -0
  28. package/dist/coding/registry/lockfiles.d.ts +6 -0
  29. package/dist/coding/registry/lockfiles.js +89 -0
  30. package/dist/coding/registry/npm-lockfile.d.ts +9 -0
  31. package/dist/coding/registry/npm-lockfile.js +73 -0
  32. package/dist/coding/registry/npm-plan.d.ts +29 -0
  33. package/dist/coding/registry/npm-plan.js +289 -0
  34. package/dist/coding/registry/npm.d.ts +7 -0
  35. package/dist/coding/registry/npm.js +252 -0
  36. package/dist/coding/registry/pypi.d.ts +4 -0
  37. package/dist/coding/registry/pypi.js +210 -0
  38. package/dist/coding/registry/report.d.ts +37 -0
  39. package/dist/coding/registry/report.js +31 -0
  40. package/dist/coding/registry/token.d.ts +4 -0
  41. package/dist/coding/registry/token.js +7 -0
  42. package/dist/coding/registry/types.d.ts +286 -0
  43. package/dist/coding/registry/types.js +19 -0
  44. package/dist/coding/registry/worker-config.d.ts +14 -0
  45. package/dist/coding/registry/worker-config.js +31 -0
  46. package/dist/coding/services/builtins.d.ts +22 -0
  47. package/dist/coding/services/builtins.js +85 -0
  48. package/dist/coding/services/catalog.d.ts +443 -0
  49. package/dist/coding/services/catalog.js +159 -0
  50. package/dist/coding/services/declaration.d.ts +11 -0
  51. package/dist/coding/services/declaration.js +114 -0
  52. package/dist/coding/services/note.d.ts +8 -0
  53. package/dist/coding/services/note.js +15 -0
  54. package/dist/coding/services/resolve.d.ts +25 -0
  55. package/dist/coding/services/resolve.js +50 -0
  56. package/dist/coding/services/wording.d.ts +25 -0
  57. package/dist/coding/services/wording.js +70 -0
  58. package/dist/coding-proxy/main.js +24 -4
  59. package/dist/coding-worker/artifact.d.ts +18 -0
  60. package/dist/coding-worker/artifact.js +48 -6
  61. package/dist/coding-worker/debug-trace.d.ts +37 -0
  62. package/dist/coding-worker/debug-trace.js +117 -0
  63. package/dist/coding-worker/driver.d.ts +14 -1
  64. package/dist/coding-worker/driver.js +106 -14
  65. package/dist/coding-worker/errors.d.ts +7 -0
  66. package/dist/coding-worker/errors.js +26 -0
  67. package/dist/coding-worker/main.js +13 -4
  68. package/dist/coding-worker/sdk.d.ts +2 -2
  69. package/dist/coding-worker/sdk.js +7 -2
  70. package/dist/coding-worker/types.d.ts +3 -0
  71. package/dist/config/providers.d.ts +41 -0
  72. package/dist/config/providers.js +66 -0
  73. package/dist/core/budget-groups.d.ts +109 -6
  74. package/dist/core/budget-groups.js +125 -19
  75. package/dist/core/budget-wording.d.ts +22 -0
  76. package/dist/core/budget-wording.js +55 -0
  77. package/dist/core/coding-queue.d.ts +1 -1
  78. package/dist/core/datastores.d.ts +1 -1
  79. package/dist/core/datastores.js +18 -2
  80. package/dist/core/db.d.ts +40 -2
  81. package/dist/core/db.js +75 -2
  82. package/dist/core/dispatch.d.ts +49 -7
  83. package/dist/core/dispatch.js +290 -45
  84. package/dist/core/engine-native.js +27 -10
  85. package/dist/core/grants.d.ts +86 -0
  86. package/dist/core/grants.js +126 -0
  87. package/dist/core/host-events.d.ts +49 -0
  88. package/dist/core/host-events.js +257 -0
  89. package/dist/core/host-identity-links.d.ts +54 -0
  90. package/dist/core/host-identity-links.js +189 -0
  91. package/dist/core/host-status.d.ts +61 -0
  92. package/dist/core/host-status.js +211 -0
  93. package/dist/core/http-runtime.js +3 -4
  94. package/dist/core/in-flight-runs.d.ts +8 -0
  95. package/dist/core/in-flight-runs.js +56 -0
  96. package/dist/core/lease.d.ts +1 -1
  97. package/dist/core/private-directory.d.ts +20 -0
  98. package/dist/core/private-directory.js +36 -0
  99. package/dist/core/provider-wording.d.ts +12 -0
  100. package/dist/core/provider-wording.js +44 -0
  101. package/dist/core/reconciler.d.ts +39 -3
  102. package/dist/core/reconciler.js +86 -2
  103. package/dist/core/repo-access.d.ts +99 -0
  104. package/dist/core/repo-access.js +136 -0
  105. package/dist/core/review-host-checks.d.ts +11 -0
  106. package/dist/core/review-host-checks.js +41 -0
  107. package/dist/core/review-host-tools.d.ts +47 -0
  108. package/dist/core/review-host-tools.js +347 -0
  109. package/dist/core/run-heartbeat.d.ts +27 -0
  110. package/dist/core/run-heartbeat.js +54 -0
  111. package/dist/core/runner.d.ts +31 -9
  112. package/dist/core/runner.js +237 -40
  113. package/dist/core/scheduler.d.ts +1 -1
  114. package/dist/core/secrets.d.ts +12 -3
  115. package/dist/core/secrets.js +25 -6
  116. package/dist/core/subagent-memory-tools.d.ts +2 -2
  117. package/dist/core/subagent-memory-tools.js +16 -3
  118. package/dist/core/tool-admin.d.ts +81 -0
  119. package/dist/core/tool-admin.js +129 -0
  120. package/dist/core/tool-names.d.ts +42 -0
  121. package/dist/core/tool-names.js +64 -0
  122. package/dist/core/untrusted-content.d.ts +32 -0
  123. package/dist/core/untrusted-content.js +72 -0
  124. package/dist/core/webhooks.d.ts +9 -2
  125. package/dist/core/webhooks.js +23 -2
  126. package/dist/generated/prisma/browser.d.ts +328 -0
  127. package/dist/generated/prisma/browser.js +17 -0
  128. package/dist/generated/prisma/client.d.ts +347 -0
  129. package/dist/generated/prisma/client.js +34 -0
  130. package/dist/generated/prisma/commonInputTypes.d.ts +839 -0
  131. package/dist/generated/prisma/commonInputTypes.js +10 -0
  132. package/dist/generated/prisma/enums.d.ts +59 -0
  133. package/dist/generated/prisma/enums.js +59 -0
  134. package/dist/generated/prisma/internal/class.d.ts +605 -0
  135. package/dist/generated/prisma/internal/class.js +49 -0
  136. package/dist/generated/prisma/internal/prismaNamespace.d.ts +4494 -0
  137. package/dist/generated/prisma/internal/prismaNamespace.js +628 -0
  138. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +635 -0
  139. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +599 -0
  140. package/dist/generated/prisma/models/Agent.d.ts +3560 -0
  141. package/dist/generated/prisma/models/Agent.js +1 -0
  142. package/dist/generated/prisma/models/AgentDatastore.d.ts +1210 -0
  143. package/dist/generated/prisma/models/AgentDatastore.js +1 -0
  144. package/dist/generated/prisma/models/AgentMemory.d.ts +986 -0
  145. package/dist/generated/prisma/models/AgentMemory.js +1 -0
  146. package/dist/generated/prisma/models/AgentRepository.d.ts +1425 -0
  147. package/dist/generated/prisma/models/AgentRepository.js +1 -0
  148. package/dist/generated/prisma/models/AgentSecret.d.ts +1212 -0
  149. package/dist/generated/prisma/models/AgentSecret.js +1 -0
  150. package/dist/generated/prisma/models/AgentSubAgent.d.ts +1217 -0
  151. package/dist/generated/prisma/models/AgentSubAgent.js +1 -0
  152. package/dist/generated/prisma/models/AgentTool.d.ts +1403 -0
  153. package/dist/generated/prisma/models/AgentTool.js +1 -0
  154. package/dist/generated/prisma/models/AuthFormChallenge.d.ts +1298 -0
  155. package/dist/generated/prisma/models/AuthFormChallenge.js +1 -0
  156. package/dist/generated/prisma/models/AuthLoginKey.d.ts +1245 -0
  157. package/dist/generated/prisma/models/AuthLoginKey.js +1 -0
  158. package/dist/generated/prisma/models/AuthRateLimit.d.ts +979 -0
  159. package/dist/generated/prisma/models/AuthRateLimit.js +1 -0
  160. package/dist/generated/prisma/models/AuthSession.d.ts +1376 -0
  161. package/dist/generated/prisma/models/AuthSession.js +1 -0
  162. package/dist/generated/prisma/models/AuthUser.d.ts +1700 -0
  163. package/dist/generated/prisma/models/AuthUser.js +1 -0
  164. package/dist/generated/prisma/models/BudgetGroup.d.ts +1548 -0
  165. package/dist/generated/prisma/models/BudgetGroup.js +1 -0
  166. package/dist/generated/prisma/models/CodingAgentProfile.d.ts +1681 -0
  167. package/dist/generated/prisma/models/CodingAgentProfile.js +1 -0
  168. package/dist/generated/prisma/models/CodingProxyRequest.d.ts +1749 -0
  169. package/dist/generated/prisma/models/CodingProxyRequest.js +1 -0
  170. package/dist/generated/prisma/models/CodingProxySession.d.ts +1626 -0
  171. package/dist/generated/prisma/models/CodingProxySession.js +1 -0
  172. package/dist/generated/prisma/models/CodingRun.d.ts +3583 -0
  173. package/dist/generated/prisma/models/CodingRun.js +1 -0
  174. package/dist/generated/prisma/models/CodingService.d.ts +1348 -0
  175. package/dist/generated/prisma/models/CodingService.js +1 -0
  176. package/dist/generated/prisma/models/Datastore.d.ts +1434 -0
  177. package/dist/generated/prisma/models/Datastore.js +1 -0
  178. package/dist/generated/prisma/models/DatastoreEntry.d.ts +1314 -0
  179. package/dist/generated/prisma/models/DatastoreEntry.js +1 -0
  180. package/dist/generated/prisma/models/HostEventDelivery.d.ts +946 -0
  181. package/dist/generated/prisma/models/HostEventDelivery.js +1 -0
  182. package/dist/generated/prisma/models/HostIdentity.d.ts +1232 -0
  183. package/dist/generated/prisma/models/HostIdentity.js +1 -0
  184. package/dist/generated/prisma/models/HostIdentityLinkRequest.d.ts +1473 -0
  185. package/dist/generated/prisma/models/HostIdentityLinkRequest.js +1 -0
  186. package/dist/generated/prisma/models/OAuthAuthorizationCode.d.ts +1526 -0
  187. package/dist/generated/prisma/models/OAuthAuthorizationCode.js +1 -0
  188. package/dist/generated/prisma/models/OAuthAuthorizationRequest.d.ts +1350 -0
  189. package/dist/generated/prisma/models/OAuthAuthorizationRequest.js +1 -0
  190. package/dist/generated/prisma/models/OAuthClient.d.ts +1289 -0
  191. package/dist/generated/prisma/models/OAuthClient.js +1 -0
  192. package/dist/generated/prisma/models/OAuthFamily.d.ts +1540 -0
  193. package/dist/generated/prisma/models/OAuthFamily.js +1 -0
  194. package/dist/generated/prisma/models/OAuthGrant.d.ts +1280 -0
  195. package/dist/generated/prisma/models/OAuthGrant.js +1 -0
  196. package/dist/generated/prisma/models/Principal.d.ts +2245 -0
  197. package/dist/generated/prisma/models/Principal.js +1 -0
  198. package/dist/generated/prisma/models/RegistryAllowance.d.ts +1148 -0
  199. package/dist/generated/prisma/models/RegistryAllowance.js +1 -0
  200. package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +1219 -0
  201. package/dist/generated/prisma/models/RegistryApprovedVersion.js +1 -0
  202. package/dist/generated/prisma/models/RegistryFetch.d.ts +1428 -0
  203. package/dist/generated/prisma/models/RegistryFetch.js +1 -0
  204. package/dist/generated/prisma/models/RegistryPlanRefusal.d.ts +1294 -0
  205. package/dist/generated/prisma/models/RegistryPlanRefusal.js +1 -0
  206. package/dist/generated/prisma/models/RegistryVersionFact.d.ts +1085 -0
  207. package/dist/generated/prisma/models/RegistryVersionFact.js +1 -0
  208. package/dist/generated/prisma/models/ResourceGrant.d.ts +1437 -0
  209. package/dist/generated/prisma/models/ResourceGrant.js +1 -0
  210. package/dist/generated/prisma/models/Run.d.ts +2669 -0
  211. package/dist/generated/prisma/models/Run.js +1 -0
  212. package/dist/generated/prisma/models/RunHostCheck.d.ts +1239 -0
  213. package/dist/generated/prisma/models/RunHostCheck.js +1 -0
  214. package/dist/generated/prisma/models/RunHostStatus.d.ts +1315 -0
  215. package/dist/generated/prisma/models/RunHostStatus.js +1 -0
  216. package/dist/generated/prisma/models/SchedulerLease.d.ts +970 -0
  217. package/dist/generated/prisma/models/SchedulerLease.js +1 -0
  218. package/dist/generated/prisma/models/Secret.d.ts +1399 -0
  219. package/dist/generated/prisma/models/Secret.js +1 -0
  220. package/dist/generated/prisma/models/SecretElicitationOutcome.d.ts +973 -0
  221. package/dist/generated/prisma/models/SecretElicitationOutcome.js +1 -0
  222. package/dist/generated/prisma/models/Task.d.ts +1176 -0
  223. package/dist/generated/prisma/models/Task.js +1 -0
  224. package/dist/generated/prisma/models/Tool.d.ts +1490 -0
  225. package/dist/generated/prisma/models/Tool.js +1 -0
  226. package/dist/generated/prisma/models/Webhook.d.ts +1386 -0
  227. package/dist/generated/prisma/models/Webhook.js +1 -0
  228. package/dist/generated/prisma/models.d.ts +45 -0
  229. package/dist/generated/prisma/models.js +1 -0
  230. package/dist/help/build.d.ts +1 -0
  231. package/dist/help/build.js +9 -0
  232. package/dist/help/catalog.d.ts +24 -0
  233. package/dist/help/catalog.js +160 -0
  234. package/dist/help/cli.d.ts +2 -0
  235. package/dist/help/cli.js +65 -0
  236. package/dist/help/runtime.d.ts +3 -0
  237. package/dist/help/runtime.js +44 -0
  238. package/dist/help/search.d.ts +9 -0
  239. package/dist/help/search.js +104 -0
  240. package/dist/help-index.json +660 -0
  241. package/dist/import/cli-args.js +3 -2
  242. package/dist/import/create.d.ts +6 -1
  243. package/dist/import/create.js +68 -14
  244. package/dist/import/index.d.ts +1 -1
  245. package/dist/import/index.js +19 -9
  246. package/dist/import/neutral-schema.d.ts +11 -11
  247. package/dist/mcp/auth/access.d.ts +70 -0
  248. package/dist/mcp/auth/access.js +58 -0
  249. package/dist/mcp/auth/grants-cli.d.ts +149 -0
  250. package/dist/mcp/auth/grants-cli.js +518 -0
  251. package/dist/mcp/auth/host-account-cli.d.ts +2 -0
  252. package/dist/mcp/auth/host-account-cli.js +47 -0
  253. package/dist/mcp/auth/ownership.d.ts +37 -63
  254. package/dist/mcp/auth/ownership.js +19 -14
  255. package/dist/mcp/auth/principal.d.ts +1 -1
  256. package/dist/mcp/auth/repo-authorization.d.ts +22 -0
  257. package/dist/mcp/auth/repo-authorization.js +51 -0
  258. package/dist/mcp/auth/resource-server.d.ts +32 -3
  259. package/dist/mcp/auth/resource-server.js +84 -3
  260. package/dist/mcp/auth/self-hosted/browser.js +22 -8
  261. package/dist/mcp/auth/self-hosted/cli.d.ts +1 -1
  262. package/dist/mcp/auth/self-hosted/cli.js +47 -13
  263. package/dist/mcp/auth/self-hosted/credentials.d.ts +21 -3
  264. package/dist/mcp/auth/self-hosted/credentials.js +62 -4
  265. package/dist/mcp/auth/self-hosted/rate-limit.d.ts +1 -1
  266. package/dist/mcp/auth/self-hosted/session.d.ts +3 -2
  267. package/dist/mcp/context.d.ts +28 -2
  268. package/dist/mcp/errors.d.ts +25 -6
  269. package/dist/mcp/errors.js +98 -0
  270. package/dist/mcp/host-events/github-ingress.d.ts +36 -0
  271. package/dist/mcp/host-events/github-ingress.js +92 -0
  272. package/dist/mcp/host-events/github-user-callback.d.ts +18 -0
  273. package/dist/mcp/host-events/github-user-callback.js +50 -0
  274. package/dist/mcp/index.d.ts +8 -1
  275. package/dist/mcp/index.js +106 -16
  276. package/dist/mcp/server.d.ts +1 -1
  277. package/dist/mcp/server.js +27 -9
  278. package/dist/mcp/shared/page-style.d.ts +16 -0
  279. package/dist/mcp/shared/page-style.js +129 -0
  280. package/dist/mcp/tasks/manager.d.ts +1 -1
  281. package/dist/mcp/tools/agents.js +447 -49
  282. package/dist/mcp/tools/budget-groups.js +3 -3
  283. package/dist/mcp/tools/datastore.js +23 -16
  284. package/dist/mcp/tools/grants.d.ts +2 -0
  285. package/dist/mcp/tools/grants.js +239 -0
  286. package/dist/mcp/tools/help.d.ts +5 -0
  287. package/dist/mcp/tools/help.js +67 -0
  288. package/dist/mcp/tools/host-accounts.d.ts +2 -0
  289. package/dist/mcp/tools/host-accounts.js +114 -0
  290. package/dist/mcp/tools/memory.d.ts +7 -1
  291. package/dist/mcp/tools/memory.js +5 -5
  292. package/dist/mcp/tools/repositories.d.ts +2 -0
  293. package/dist/mcp/tools/repositories.js +181 -0
  294. package/dist/mcp/tools/runs.d.ts +6 -0
  295. package/dist/mcp/tools/runs.js +60 -6
  296. package/dist/mcp/tools/scheduling.js +4 -7
  297. package/dist/mcp/tools/secret-elicitation-form.d.ts +1 -1
  298. package/dist/mcp/tools/secret-elicitation-form.js +10 -28
  299. package/dist/mcp/tools/secret-elicitation.d.ts +1 -1
  300. package/dist/mcp/tools/secrets.js +18 -6
  301. package/dist/mcp/tools/services.d.ts +2 -0
  302. package/dist/mcp/tools/services.js +222 -0
  303. package/dist/mcp/tools/subagents.js +53 -17
  304. package/dist/mcp/tools/tools.d.ts +41 -0
  305. package/dist/mcp/tools/tools.js +202 -41
  306. package/dist/mcp/tools/trigger.js +36 -6
  307. package/dist/mcp/tools/webhooks.js +15 -4
  308. package/dist/mcp/transport/streamable-http.d.ts +11 -1
  309. package/dist/mcp/transport/streamable-http.js +32 -2
  310. package/dist/mcp/unattended-schedules.d.ts +1 -1
  311. package/dist/mcp/webhooks/ingress.d.ts +1 -1
  312. package/dist/providers/auth/delegating.d.ts +19 -0
  313. package/dist/providers/auth/delegating.js +71 -0
  314. package/dist/providers/auth/index.d.ts +3 -1
  315. package/dist/providers/auth/index.js +11 -0
  316. package/dist/providers/auth/self-hosted.d.ts +4 -3
  317. package/dist/providers/auth/self-hosted.js +16 -4
  318. package/dist/providers/auth/types.d.ts +6 -0
  319. package/dist/providers/coding-proxy/memory-ledger.d.ts +3 -0
  320. package/dist/providers/coding-proxy/memory-ledger.js +21 -2
  321. package/dist/providers/coding-proxy/metering.d.ts +4 -0
  322. package/dist/providers/coding-proxy/metering.js +28 -2
  323. package/dist/providers/coding-proxy/prisma-ledger.d.ts +18 -1
  324. package/dist/providers/coding-proxy/prisma-ledger.js +50 -5
  325. package/dist/providers/coding-proxy/proxy.d.ts +13 -0
  326. package/dist/providers/coding-proxy/proxy.js +479 -18
  327. package/dist/providers/coding-proxy/registry/audit.d.ts +131 -0
  328. package/dist/providers/coding-proxy/registry/audit.js +380 -0
  329. package/dist/providers/coding-proxy/registry/bounded-fetch.d.ts +16 -0
  330. package/dist/providers/coding-proxy/registry/bounded-fetch.js +77 -0
  331. package/dist/providers/coding-proxy/registry/plan.d.ts +58 -0
  332. package/dist/providers/coding-proxy/registry/plan.js +304 -0
  333. package/dist/providers/coding-proxy/registry/prisma-store.d.ts +34 -0
  334. package/dist/providers/coding-proxy/registry/prisma-store.js +151 -0
  335. package/dist/providers/coding-proxy/registry/service.d.ts +213 -0
  336. package/dist/providers/coding-proxy/registry/service.js +1137 -0
  337. package/dist/providers/coding-proxy/registry/store.d.ts +127 -0
  338. package/dist/providers/coding-proxy/registry/store.js +70 -0
  339. package/dist/providers/coding-proxy/runtime.d.ts +18 -1
  340. package/dist/providers/coding-proxy/runtime.js +63 -0
  341. package/dist/providers/coding-proxy/secure-fetch.js +0 -1
  342. package/dist/providers/coding-proxy/server.d.ts +11 -0
  343. package/dist/providers/coding-proxy/server.js +163 -0
  344. package/dist/providers/coding-proxy/types.d.ts +15 -0
  345. package/dist/providers/datastore/postgres.d.ts +1 -1
  346. package/dist/providers/engine/types.d.ts +10 -0
  347. package/dist/providers/executor/build.d.ts +3 -3
  348. package/dist/providers/executor/composition.d.ts +4 -1
  349. package/dist/providers/executor/composition.js +19 -0
  350. package/dist/providers/executor/container.d.ts +106 -4
  351. package/dist/providers/executor/container.js +305 -22
  352. package/dist/providers/executor/dbos.d.ts +3 -4
  353. package/dist/providers/executor/in-process.d.ts +2 -3
  354. package/dist/providers/executor/routing.d.ts +7 -0
  355. package/dist/providers/executor/routing.js +13 -0
  356. package/dist/providers/executor/types.d.ts +22 -0
  357. package/dist/providers/jobs/claude-tool-setup.d.ts +23 -0
  358. package/dist/providers/jobs/claude-tool-setup.js +50 -0
  359. package/dist/providers/jobs/collect-prune.d.ts +7 -0
  360. package/dist/providers/jobs/collect-prune.js +27 -0
  361. package/dist/providers/jobs/docker-isolation.d.ts +48 -3
  362. package/dist/providers/jobs/docker-isolation.js +213 -22
  363. package/dist/providers/jobs/docker-services.d.ts +35 -0
  364. package/dist/providers/jobs/docker-services.js +191 -0
  365. package/dist/providers/jobs/docker.d.ts +60 -2
  366. package/dist/providers/jobs/docker.js +313 -50
  367. package/dist/providers/jobs/fake-kubernetes-api.d.ts +1 -0
  368. package/dist/providers/jobs/fake-kubernetes-api.js +12 -3
  369. package/dist/providers/jobs/kubernetes-isolation.d.ts +17 -2
  370. package/dist/providers/jobs/kubernetes-isolation.js +238 -57
  371. package/dist/providers/jobs/kubernetes-platform.d.ts +10 -4
  372. package/dist/providers/jobs/kubernetes-platform.js +11 -5
  373. package/dist/providers/jobs/kubernetes-preflight.js +3 -0
  374. package/dist/providers/jobs/kubernetes.d.ts +21 -2
  375. package/dist/providers/jobs/kubernetes.js +117 -31
  376. package/dist/providers/jobs/types.d.ts +20 -0
  377. package/dist/providers/llm/anthropic.js +6 -2
  378. package/dist/providers/llm/bedrock.js +2 -1
  379. package/dist/providers/llm/claude-messages.d.ts +5 -1
  380. package/dist/providers/llm/claude-messages.js +1 -0
  381. package/dist/providers/llm/claude-provider.d.ts +3 -1
  382. package/dist/providers/llm/claude-provider.js +5 -1
  383. package/dist/providers/llm/index.d.ts +1 -1
  384. package/dist/providers/llm/index.js +1 -1
  385. package/dist/providers/llm/pricing-anthropic.d.ts +10 -0
  386. package/dist/providers/llm/pricing-anthropic.js +32 -14
  387. package/dist/providers/llm/pricing-bedrock-claude.d.ts +8 -0
  388. package/dist/providers/llm/pricing-bedrock-claude.js +9 -0
  389. package/dist/providers/llm/routing.d.ts +10 -1
  390. package/dist/providers/llm/routing.js +18 -0
  391. package/dist/providers/llm/types.d.ts +12 -0
  392. package/dist/providers/llm/types.js +8 -1
  393. package/dist/providers/memory/postgres.d.ts +1 -1
  394. package/dist/providers/review-host/diff-lines.d.ts +16 -0
  395. package/dist/providers/review-host/diff-lines.js +59 -0
  396. package/dist/providers/review-host/github-events.d.ts +6 -0
  397. package/dist/providers/review-host/github-events.js +180 -0
  398. package/dist/providers/review-host/github-user-auth.d.ts +38 -0
  399. package/dist/providers/review-host/github-user-auth.js +128 -0
  400. package/dist/providers/review-host/github.d.ts +57 -0
  401. package/dist/providers/review-host/github.js +567 -0
  402. package/dist/providers/review-host/index.d.ts +12 -0
  403. package/dist/providers/review-host/index.js +30 -0
  404. package/dist/providers/review-host/review-format.d.ts +19 -0
  405. package/dist/providers/review-host/review-format.js +59 -0
  406. package/dist/providers/review-host/types.d.ts +284 -0
  407. package/dist/providers/review-host/types.js +24 -0
  408. package/dist/providers/vcs/git.d.ts +16 -6
  409. package/dist/providers/vcs/git.js +79 -14
  410. package/dist/providers/vcs/github.d.ts +74 -3
  411. package/dist/providers/vcs/github.js +195 -15
  412. package/dist/providers/vcs/types.d.ts +42 -4
  413. package/dist/quickstart/index.js +34 -32
  414. package/dist/quickstart/migrate.d.ts +23 -0
  415. package/dist/quickstart/migrate.js +125 -0
  416. package/dist/sandbox/fetch-policy.d.ts +20 -2
  417. package/dist/sandbox/fetch-policy.js +64 -3
  418. package/dist/sandbox/host-functions.d.ts +9 -1
  419. package/dist/sandbox/host-functions.js +12 -5
  420. package/dist/serve.js +1 -1
  421. package/dist/wardby-bin.js +6 -0
  422. package/docs/README.md +30 -0
  423. package/docs/architecture-runtime.md +90 -0
  424. package/docs/assets/brand/wardby-icon-512.png +0 -0
  425. package/docs/assets/brand/wardby-icon.svg +16 -0
  426. package/docs/assets/brand/wardby-mascot-profile-512.png +0 -0
  427. package/docs/assets/brand/wardby-mascot.png +0 -0
  428. package/docs/assets/brand/wardby-mascot.svg +5 -0
  429. package/docs/assets/wardby-workflow.svg +106 -0
  430. package/docs/code-review-agents.md +481 -0
  431. package/docs/coding-agent-setup.md +172 -0
  432. package/docs/coding-packages.md +455 -0
  433. package/docs/coding-services.md +300 -0
  434. package/docs/coding-worker-byo-images.md +98 -0
  435. package/docs/coding-worker-isolation.md +1061 -0
  436. package/docs/getting-started-gke.md +615 -0
  437. package/docs/getting-started-identity-provider.md +308 -0
  438. package/docs/getting-started.md +133 -0
  439. package/docs/observability.md +53 -0
  440. package/docs/release-verification.md +66 -0
  441. package/docs/security-deployment.md +657 -0
  442. package/help/code-review-agents.md +31 -0
  443. package/help/coding-packages.md +31 -0
  444. package/help/coding-services.md +71 -0
  445. package/help/creating-agents.md +68 -0
  446. package/help/deploy-gke.md +39 -0
  447. package/help/deployment-targets.md +39 -0
  448. package/help/errors/budget-group-exhausted.md +25 -0
  449. package/help/errors/docker-isolation-unsupported.md +26 -0
  450. package/help/errors/protected-path.md +45 -0
  451. package/help/errors/repo-access.md +27 -0
  452. package/help/errors/service-declaration-invalid.md +27 -0
  453. package/help/errors/service-declaration-unavailable.md +25 -0
  454. package/help/errors/service-launcher-unsupported.md +30 -0
  455. package/help/errors/service-not-allowed.md +27 -0
  456. package/help/errors/service-unknown.md +23 -0
  457. package/help/errors/service-unready.md +49 -0
  458. package/help/getting-started.md +31 -0
  459. package/help/github.md +30 -0
  460. package/help/identity-and-access.md +39 -0
  461. package/help/mcp.md +30 -0
  462. package/help/native-capabilities.md +30 -0
  463. package/help/observability.md +41 -0
  464. package/help/operating-agents.md +30 -0
  465. package/help/security.md +27 -0
  466. package/help/troubleshooting/budgets.md +26 -0
  467. package/help/troubleshooting/coding-workers.md +47 -0
  468. package/help/troubleshooting/repository-access.md +27 -0
  469. package/package.json +30 -11
  470. package/prisma/migrate.config.mjs +20 -0
  471. package/prisma/migrations/20260925010000_coding_collect_exclude/migration.sql +8 -0
  472. package/prisma/migrations/20260925015000_allowed_egress_default/migration.sql +8 -0
  473. package/prisma/migrations/20260925020000_coding_package_registry/migration.sql +55 -0
  474. package/prisma/migrations/20260925030000_tool_name_per_owner/migration.sql +11 -0
  475. package/prisma/migrations/20260926010000_run_trigger_host_event/migration.sql +7 -0
  476. package/prisma/migrations/20260926020000_code_review_hosts/migration.sql +53 -0
  477. package/prisma/migrations/20260926030000_auth_user_roles/migration.sql +9 -0
  478. package/prisma/migrations/20260926040000_agent_effort/migration.sql +6 -0
  479. package/prisma/migrations/20260926050000_registry_lockfile_plan/migration.sql +32 -0
  480. package/prisma/migrations/20260926050000_repo_access_authorization/migration.sql +82 -0
  481. package/prisma/migrations/20260926060000_registry_plan_refusal/migration.sql +19 -0
  482. package/prisma/migrations/20260926100000_registry_plan_refusal_published_at/migration.sql +5 -0
  483. package/prisma/migrations/20260926190000_run_host_status/migration.sql +18 -0
  484. package/prisma/migrations/20260926210000_run_host_status_at_dispatch/migration.sql +5 -0
  485. package/prisma/migrations/20260927010000_resource_grants/migration.sql +72 -0
  486. package/prisma/migrations/20260927020000_proxy_session_budget_exhausted/migration.sql +3 -0
  487. package/prisma/migrations/20260927030000_coding_debug_trace/migration.sql +4 -0
  488. package/prisma/migrations/20260927040000_proxy_session_upstream_failure/migration.sql +3 -0
  489. package/prisma/migrations/20260927050000_coding_run_services/migration.sql +74 -0
  490. package/prisma/migrations/20260928000000_coding_run_tool_image/migration.sql +5 -0
  491. package/prisma/schema.prisma +417 -23
@@ -1,13 +1,23 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { mkdir, realpath, rename, rm, writeFile } from "node:fs/promises";
3
3
  import { join, resolve, sep } from "node:path";
4
+ import { normalizeCollectExclusions } from "../../coding/collect-exclude.js";
4
5
  import { CodingProfileSchema } from "../../coding/profile.js";
6
+ import { parseStoredServices, storedServiceNames, workerServices } from "../../coding/services/catalog.js";
7
+ import { MAX_SERVICE_DECLARATION_BYTES, SERVICE_DECLARATION_PATH } from "../../coding/services/declaration.js";
8
+ import { SERVICE_UNREADY_CATEGORY, SERVICE_UNREADY_ERROR, serviceUnreadyName, serviceUnreadySentence, } from "../../coding/services/wording.js";
9
+ import { PROTECTED_PATH_CATEGORY, protectedPathFromError, protectedPathSentence, } from "../../coding/protected-path-wording.js";
10
+ import { budgetSentence } from "../../core/budget-wording.js";
11
+ import { classifyProviderFailure, providerClassOfCategory, providerSentence, } from "../../core/provider-wording.js";
5
12
  import { logger } from "../../core/logger.js";
13
+ import { requiredLevel } from "../../core/repo-access.js";
14
+ import { ensurePrivateDirectory } from "../../core/private-directory.js";
6
15
  import { codingRunObserver } from "../../coding/observability.js";
7
16
  import { CODING_PROTOCOL_VERSION, CodingRunResultSchema, CodingTaskInputSchema, parseCodingAgentOutputJson, redactAndTruncate, } from "../../coding/protocol.js";
8
17
  import { getModelPricing } from "../llm/pricing.js";
9
18
  import { getAnthropicPricing } from "../llm/pricing-anthropic.js";
10
- import { isImmutableDockerImage } from "../jobs/docker-isolation.js";
19
+ import { CODING_PROXY_ALIAS, CODING_PROXY_PORT, isImmutableDockerImage } from "../jobs/docker-isolation.js";
20
+ import { normalizeRegistryLockfiles } from "../../coding/registry/lockfiles.js";
11
21
  import { HEARTBEAT_TIMEOUT_MS } from "../../core/timing.js";
12
22
  const containerLog = logger.child({ module: "container-executor" });
13
23
  const TERMINAL_STATUSES = new Set(["succeeded", "failed", "refused", "lost", "budget_exhausted", "cancelled"]);
@@ -32,6 +42,19 @@ function proxyProtocol(provider) {
32
42
  return "anthropic-messages";
33
43
  throw new Error("coding_provider_unsupported");
34
44
  }
45
+ /**
46
+ * Claude Code's commands run in its tool runner, which never reports back through the agent, so the
47
+ * host rewrites registry-proxy download URLs in collected lockfiles itself (the Codex driver does it
48
+ * in the worker). Best effort and symlink-safe (lockfiles.ts); a Codex workspace is left alone.
49
+ */
50
+ export async function normalizeCollectedLockfiles(provider, workspacePath) {
51
+ if (provider !== "claude-code")
52
+ return [];
53
+ return normalizeRegistryLockfiles({
54
+ workspace: workspacePath,
55
+ proxyBaseUrl: `http://${CODING_PROXY_ALIAS}:${CODING_PROXY_PORT}`,
56
+ });
57
+ }
35
58
  export class PrismaContainerExecutionStore {
36
59
  db;
37
60
  options;
@@ -42,7 +65,10 @@ export class PrismaContainerExecutionStore {
42
65
  async load(runId) {
43
66
  const row = await this.db.run.findUnique({
44
67
  where: { id: runId },
45
- include: { agent: true, codingRun: { include: { proxySession: true } } },
68
+ include: {
69
+ agent: { include: { codingProfile: true, budgetGroup: { select: { name: true } } } },
70
+ codingRun: { include: { proxySession: true } },
71
+ },
46
72
  });
47
73
  if (!row?.codingRun)
48
74
  return null;
@@ -59,10 +85,12 @@ export class PrismaContainerExecutionStore {
59
85
  provider: row.codingRun.provider,
60
86
  model: row.codingRun.model,
61
87
  timeoutSec: row.codingRun.timeoutSec,
62
- allowedEgress: row.codingRun.allowedEgress,
63
88
  protectedPaths: row.codingRun.protectedPaths,
89
+ collectExclude: row.codingRun.collectExclude,
64
90
  rootCodingRunId: row.codingRun.rootCodingRunId,
65
91
  budgetUsd: Number(row.codingRun.budgetReservedUsd),
92
+ agentBudgetUsd: Number(row.agent.budgetUsd),
93
+ budgetGroupName: row.agent.budgetGroup?.name ?? null,
66
94
  tokensIn: row.tokensIn,
67
95
  tokensOut: row.tokensOut,
68
96
  costUsd: Number(row.costUsd),
@@ -73,7 +101,13 @@ export class PrismaContainerExecutionStore {
73
101
  proxySessionId: row.codingRun.proxySession?.id ?? null,
74
102
  result: row.codingRun.result,
75
103
  workerImage: row.codingRun.workerImage,
104
+ toolImage: row.codingRun.toolImage,
76
105
  workspaceDiskMb: row.codingRun.workspaceDiskMb,
106
+ debugTrace: row.codingRun.debugTrace,
107
+ services: row.codingRun.services,
108
+ failureCategory: row.codingRun.failureCategory,
109
+ profileRepository: row.agent.codingProfile?.repository ?? null,
110
+ repositoryAuthorizedVia: row.agent.codingProfile?.repositoryAuthorizedVia ?? null,
77
111
  };
78
112
  }
79
113
  async claimProvisioning(runId, claimId) {
@@ -246,6 +280,9 @@ export class RunCapabilityVault {
246
280
  }
247
281
  class PreflightError extends Error {
248
282
  }
283
+ /** A refusal by repository authorization; its message is the failure category's prefix (repo_access_*). */
284
+ class RepoAccessError extends PreflightError {
285
+ }
249
286
  export class ContainerExecutor {
250
287
  options;
251
288
  artifactRoot;
@@ -272,6 +309,12 @@ export class ContainerExecutor {
272
309
  if (image !== undefined && !isImmutableDockerImage(image))
273
310
  throw new Error("coding_worker_image_invalid");
274
311
  }
312
+ for (const versions of Object.values(options.claudeToolRunnerImages ?? {})) {
313
+ for (const image of Object.values(versions)) {
314
+ if (!isImmutableDockerImage(image))
315
+ throw new Error("coding_worker_image_invalid");
316
+ }
317
+ }
275
318
  if (!/^[A-Za-z][A-Za-z0-9_.:-]{0,199}$/.test(options.credentialRef)) {
276
319
  throw new Error("coding_credential_ref_invalid");
277
320
  }
@@ -396,6 +439,9 @@ export class ContainerExecutor {
396
439
  if ((await this.options.store.claimProvisioning(runId, claimId)) !== "claimed")
397
440
  return;
398
441
  }
442
+ // A launched run (recovered after a restart) is in flight: a transient
443
+ // host error is retried once before it is stopped.
444
+ await this.authorizeRepository(run, { inFlight: Boolean(handle) });
399
445
  try {
400
446
  workspace = await this.options.vcs.recoverWorkspace(prepared);
401
447
  if (!workspace && handle)
@@ -473,8 +519,13 @@ export class ContainerExecutor {
473
519
  await this.options.sessions.cancelSession(sessionId).catch(() => undefined);
474
520
  if (handle)
475
521
  await this.options.jobs.stop(handle, "executor_failure").catch(() => undefined);
476
- const status = !spendEnabled && error instanceof PreflightError ? "refused" : "failed";
477
- const failure = this.failure(error);
522
+ const outOfBudget = spendEnabled && (await this.outOfBudget(sessionId));
523
+ const status = outOfBudget
524
+ ? "budget_exhausted"
525
+ : !spendEnabled && error instanceof PreflightError
526
+ ? "refused"
527
+ : "failed";
528
+ const failure = outOfBudget ? this.budgetFailure(error) : this.failure(error);
478
529
  this.emit({
479
530
  stage: "stopping",
480
531
  runId,
@@ -486,9 +537,17 @@ export class ContainerExecutor {
486
537
  this.terminal(run, status, failure.audit);
487
538
  if (handle)
488
539
  await this.options.jobs.remove(handle).catch(() => undefined);
540
+ // A service sidecar that never became ready (kubernetes.ts waitForKeeper) is named on the host.
541
+ const unreadyService = outOfBudget ? null : serviceUnreadyName(error);
542
+ const protectedPath = outOfBudget ? null : protectedPathFromError(error);
489
543
  if (workspace) {
490
544
  await this.options.vcs.cleanup(workspace).catch(() => undefined);
491
- await this.options.vcs.notifyContinuationFinished?.(workspace, "failed", { agentName: run.agentName });
545
+ await this.options.vcs.notifyContinuationFinished?.(workspace, outOfBudget ? "budget_exhausted" : "failed", {
546
+ agentName: run.agentName,
547
+ ...(outOfBudget ? { budgetSentence: this.budgetSentence(run) } : {}),
548
+ ...(unreadyService ? { serviceSentence: serviceUnreadySentence([unreadyService]) } : {}),
549
+ ...(protectedPath ? { protectedPathSentence: protectedPathSentence(protectedPath) } : {}),
550
+ });
492
551
  }
493
552
  this.emit({ stage: "cleanup", runId, jobId: handle?.id, cleanupSucceeded: true });
494
553
  }
@@ -530,6 +589,26 @@ export class ContainerExecutor {
530
589
  clearInterval(timer);
531
590
  }
532
591
  }
592
+ /**
593
+ * The run may use its repository only if the agent's current owner still
594
+ * has write on it, or the profile's approval (admin or grandfathered)
595
+ * covers exactly this repository. A profile changed since dispatch no
596
+ * longer covers the run's repository, so that falls back to the owner's
597
+ * access. Throws RepoAccessError (refused before anything is cloned).
598
+ */
599
+ async authorizeRepository(run, opts) {
600
+ const sameRepository = run.profileRepository !== null && run.profileRepository === run.repository;
601
+ const decision = await this.options.repoAccess.authorizeUse({
602
+ ownerId: run.ownerId,
603
+ provider: "github",
604
+ repository: run.repository,
605
+ required: requiredLevel("coding"),
606
+ authorizedVia: sameRepository ? run.repositoryAuthorizedVia : "host_permission",
607
+ retryTransient: opts.inFlight,
608
+ });
609
+ if (!decision.ok)
610
+ throw new RepoAccessError(`repo_access_${decision.reason}`);
611
+ }
533
612
  preflight(run) {
534
613
  try {
535
614
  if (run.status !== "pending" && run.status !== "running")
@@ -551,7 +630,6 @@ export class ContainerExecutor {
551
630
  baseRef: run.baseRef,
552
631
  defaultTask: null,
553
632
  timeoutSec: run.timeoutSec,
554
- allowedEgress: run.allowedEgress,
555
633
  protectedPaths: run.protectedPaths,
556
634
  });
557
635
  const expectedHeadRunId = run.rootCodingRunId ?? run.runId;
@@ -576,6 +654,7 @@ export class ContainerExecutor {
576
654
  baseRef: profile.baseRef,
577
655
  headRef: run.headRef,
578
656
  protectedPaths: profile.protectedPaths,
657
+ collectExclude: [...normalizeCollectExclusions(run.collectExclude).paths],
579
658
  continuation: run.rootCodingRunId ? { rootRunId: run.rootCodingRunId } : undefined,
580
659
  };
581
660
  }
@@ -588,16 +667,34 @@ export class ContainerExecutor {
588
667
  if (sessionId)
589
668
  await this.options.sessions.cancelSession(sessionId).catch(() => undefined);
590
669
  // For notifyContinuationFinished in the `finally` below -- defaults to
591
- // "failed" and is only flipped right before an actual success return.
670
+ // "failed" and is only flipped right before a success or out-of-budget return.
592
671
  let outcome = "failed";
593
672
  let finishedSummary;
673
+ // Set when a failed job's session relayed a model-provider failure; names it on the host.
674
+ // Only a failed job: a transient provider error the worker retried past did not end the run.
675
+ let providerClass;
676
+ // Set when finalizeChanges refused the collected diff for touching a protected path; names it on the host.
677
+ let protectedPath;
594
678
  try {
595
679
  if (jobState !== "succeeded") {
596
680
  const collected = await this.options.jobs.collect(handle).catch(() => null);
597
681
  const reason = collected?.diagnostic ?? collected?.reason ?? jobState;
598
682
  this.emit({ stage: "collected", runId: run.runId, jobId: handle.id });
683
+ // The worker usually fails outright when the proxy refuses a model
684
+ // request for budget; the session records that refusal.
685
+ if (await this.outOfBudget(sessionId)) {
686
+ const failure = this.budgetFailure(`job_${reason}`, { issues: collected?.diagnosticIssues });
687
+ await this.options.store.terminate(run.runId, "budget_exhausted", failure.error, failure.audit);
688
+ this.terminal(run, "budget_exhausted", failure.audit);
689
+ outcome = "budget_exhausted";
690
+ return;
691
+ }
599
692
  const status = jobState === "lost" ? "lost" : "failed";
600
- const failure = this.failure(`job_${reason}`);
693
+ const provider = status === "failed" ? await this.providerFailure(sessionId) : null;
694
+ const failure = provider
695
+ ? this.providerFailureOf(run, provider, `job_${reason}`, { issues: collected?.diagnosticIssues })
696
+ : this.failure(`job_${reason}`, { issues: collected?.diagnosticIssues });
697
+ providerClass = provider?.class;
601
698
  await this.options.store.terminate(run.runId, status, failure.error, failure.audit);
602
699
  this.terminal(run, status, failure.audit);
603
700
  return;
@@ -614,6 +711,7 @@ export class ContainerExecutor {
614
711
  this.emit({ stage: "budget_cutoff", runId: run.runId, jobId: handle.id });
615
712
  await this.options.store.complete(run.runId, "budget_exhausted", this.resultFor(output, current, "budget_exhausted"));
616
713
  this.terminal(current, "budget_exhausted");
714
+ outcome = "budget_exhausted";
617
715
  return;
618
716
  }
619
717
  if (output.outcome === "no_changes") {
@@ -628,17 +726,24 @@ export class ContainerExecutor {
628
726
  if (!workspace)
629
727
  throw new Error("coding_workspace_lost");
630
728
  await this.options.jobs.materializeWorkspace(handle, workspace.workspacePath);
729
+ await normalizeCollectedLockfiles(current.provider, workspace.workspacePath);
631
730
  current = await this.requireCurrent(run.runId);
632
731
  if (current.costUsd > current.budgetUsd) {
633
732
  this.emit({ stage: "budget_cutoff", runId: run.runId, jobId: handle.id });
634
733
  await this.options.store.complete(run.runId, "budget_exhausted", this.resultFor(output, current, "budget_exhausted"));
635
734
  this.terminal(current, "budget_exhausted");
735
+ outcome = "budget_exhausted";
636
736
  return;
637
737
  }
738
+ // One more (usually cached) check right before anything is pushed: access
739
+ // revoked while the run was working stops the push.
740
+ await this.authorizeRepository(current, { inFlight: true });
741
+ const report = await this.loadRegistryReport(run.runId);
638
742
  const finalized = await this.options.vcs.finalizeChanges(workspace, {
639
743
  summary: output.summary,
640
744
  tests: output.tests,
641
745
  tag: output.tag,
746
+ ...report,
642
747
  });
643
748
  const result = this.resultFor(output, current, finalized.outcome, finalized);
644
749
  await this.options.store.complete(run.runId, "succeeded", result);
@@ -650,9 +755,18 @@ export class ContainerExecutor {
650
755
  finishedSummary = output.summary;
651
756
  }
652
757
  catch (error) {
653
- const failure = this.failure(error);
654
- await this.options.store.terminate(run.runId, "failed", failure.error, failure.audit);
655
- this.terminal(run, "failed", failure.audit);
758
+ if (await this.outOfBudget(sessionId)) {
759
+ const failure = this.budgetFailure(error);
760
+ await this.options.store.terminate(run.runId, "budget_exhausted", failure.error, failure.audit);
761
+ this.terminal(run, "budget_exhausted", failure.audit);
762
+ outcome = "budget_exhausted";
763
+ }
764
+ else {
765
+ const failure = this.failure(error);
766
+ await this.options.store.terminate(run.runId, "failed", failure.error, failure.audit);
767
+ this.terminal(run, "failed", failure.audit);
768
+ protectedPath = protectedPathFromError(error) ?? undefined;
769
+ }
656
770
  }
657
771
  finally {
658
772
  await this.options.jobs.remove(handle).catch(() => undefined);
@@ -663,12 +777,34 @@ export class ContainerExecutor {
663
777
  await this.options.vcs.notifyContinuationFinished?.(workspace, outcome, {
664
778
  summary: finishedSummary,
665
779
  agentName: run.agentName,
780
+ ...(outcome === "budget_exhausted" ? { budgetSentence: this.budgetSentence(run) } : {}),
781
+ ...(outcome === "failed" && providerClass ? { providerSentence: providerSentence(providerClass) } : {}),
782
+ ...(outcome === "failed" && protectedPath
783
+ ? { protectedPathSentence: protectedPathSentence(protectedPath) }
784
+ : {}),
666
785
  });
667
786
  }
668
787
  await rm(this.artifactPath(run.runId), { recursive: true, force: true }).catch(() => undefined);
669
788
  this.emit({ stage: "cleanup", runId: run.runId, jobId: handle.id, cleanupSucceeded: true });
670
789
  }
671
790
  }
791
+ /** The package report is informational: any failure loading it (a
792
+ * rejection, a synchronous throw, or a malformed result) yields no
793
+ * package section rather than failing finalization. */
794
+ async loadRegistryReport(runId) {
795
+ try {
796
+ const report = await this.options.registryReport?.(runId);
797
+ const packages = Array.isArray(report?.packages) ? report.packages : [];
798
+ const packageRefusals = Array.isArray(report?.packageRefusals) ? report.packageRefusals : [];
799
+ return {
800
+ ...(packages.length > 0 ? { packages } : {}),
801
+ ...(packageRefusals.length > 0 ? { packageRefusals } : {}),
802
+ };
803
+ }
804
+ catch {
805
+ return {};
806
+ }
807
+ }
672
808
  resultFor(output, run, forcedOutcome, finalized) {
673
809
  const outcome = forcedOutcome ?? (output.outcome === "budget_exhausted" ? "budget_exhausted" : "no_changes");
674
810
  return CodingRunResultSchema.parse({
@@ -693,15 +829,12 @@ export class ContainerExecutor {
693
829
  }
694
830
  resolveCodingWorkerImage(selector) {
695
831
  if (selector.provider === "claude-code") {
696
- if (selector.toolchain !== "node" || selector.toolchainVersion !== null) {
697
- throw new Error("coding_toolchain_unsupported:claude-code");
698
- }
832
+ this.claudeToolImage(selector);
699
833
  const image = selector.workerImageRef ?? this.options.claudeWorkerImage;
700
- if (!image || !this.options.claudeToolRunnerImage)
834
+ if (!image)
701
835
  throw new Error("coding_provider_not_configured:claude-code");
702
- if (!isImmutableDockerImage(image) || !isImmutableDockerImage(this.options.claudeToolRunnerImage)) {
836
+ if (!isImmutableDockerImage(image))
703
837
  throw new Error("coding_worker_image_invalid");
704
- }
705
838
  return image;
706
839
  }
707
840
  if (selector.provider !== "codex") {
@@ -721,6 +854,40 @@ export class ContainerExecutor {
721
854
  }
722
855
  return image;
723
856
  }
857
+ /** Claude's tool-runner image for the agent's toolchain, at dispatch (Executor.resolveCodingToolImage); null for Codex. */
858
+ resolveCodingToolImage(selector) {
859
+ return selector.provider === "claude-code" ? this.claudeToolImage(selector) : null;
860
+ }
861
+ /** The tool runner is what runs a Claude agent's commands, so it is what the toolchain selects. */
862
+ claudeToolImage(selector) {
863
+ if (!this.options.claudeToolRunnerImage)
864
+ throw new Error("coding_provider_not_configured:claude-code");
865
+ const image = selector.toolchain === "node" && selector.toolchainVersion === null
866
+ ? this.options.claudeToolRunnerImage
867
+ : selector.toolchainVersion !== null
868
+ ? this.options.claudeToolRunnerImages?.[selector.toolchain]?.[selector.toolchainVersion]
869
+ : undefined;
870
+ if (!image)
871
+ throw new Error("coding_toolchain_unsupported:claude-code");
872
+ if (!isImmutableDockerImage(image))
873
+ throw new Error("coding_worker_image_invalid");
874
+ return image;
875
+ }
876
+ /** Coding-run services: the repository's declaration at its base ref (Executor.readCodingServiceDeclaration). */
877
+ async readCodingServiceDeclaration(input) {
878
+ if (!this.options.vcs.readRepositoryFile)
879
+ return null;
880
+ return this.options.vcs.readRepositoryFile({
881
+ repository: input.repository,
882
+ ref: input.baseRef,
883
+ path: SERVICE_DECLARATION_PATH,
884
+ maxBytes: MAX_SERVICE_DECLARATION_BYTES,
885
+ });
886
+ }
887
+ /** The job launcher decides (Kubernetes and Docker start services for both providers). */
888
+ supportsCodingServices(provider) {
889
+ return this.options.jobs.supportsServicesFor?.(provider) === true;
890
+ }
724
891
  async requireCurrent(runId) {
725
892
  const current = await this.options.store.load(runId);
726
893
  if (!current)
@@ -735,16 +902,24 @@ export class ContainerExecutor {
735
902
  if (run.workspaceDiskMb && run.workspaceDiskMb > this.options.maxDiskMb) {
736
903
  throw new Error("coding_workspace_disk_exceeds_limit");
737
904
  }
905
+ const services = parseStoredServices(run.services);
906
+ // Dispatch refuses services this deployment can't start; this is the backstop.
907
+ if (services.length > 0 && !this.supportsCodingServices(provider)) {
908
+ throw new Error("coding_services_unsupported_launcher");
909
+ }
738
910
  return {
739
911
  kind: "coding-agent",
740
912
  runId: run.runId,
741
913
  provider,
742
914
  image: run.workerImage ?? this.options.workerImage,
743
- ...(provider === "claude-code" ? { toolImage: this.options.claudeToolRunnerImage } : {}),
915
+ // A run keeps the tool image it was dispatched with; rows from before CodingRun.toolImage use the default.
916
+ ...(provider === "claude-code" ? { toolImage: run.toolImage ?? this.options.claudeToolRunnerImage } : {}),
744
917
  inputArtifact,
745
918
  timeoutSec: run.timeoutSec,
746
919
  limits: { ...this.options.limits, ...(run.workspaceDiskMb ? { diskMb: run.workspaceDiskMb } : {}) },
747
920
  labels: {},
921
+ collectExclude: normalizeCollectExclusions(run.collectExclude),
922
+ ...(services.length > 0 ? { services } : {}),
748
923
  };
749
924
  }
750
925
  credentialRef(provider) {
@@ -755,7 +930,7 @@ export class ContainerExecutor {
755
930
  throw new Error("coding_provider_not_configured:claude-code");
756
931
  }
757
932
  async writeInput(run, deadlineAt) {
758
- await mkdir(this.artifactRoot, { recursive: true, mode: 0o700 });
933
+ await ensurePrivateDirectory(this.artifactRoot);
759
934
  const rootReal = await realpath(this.artifactRoot);
760
935
  const directory = resolve(rootReal, run.runId);
761
936
  if (!directory.startsWith(`${rootReal}${sep}`))
@@ -765,6 +940,7 @@ export class ContainerExecutor {
765
940
  throw new Error("coding_artifact_path_invalid");
766
941
  const destination = join(directory, "input.json");
767
942
  const temporary = join(directory, `.input-${randomUUID()}.tmp`);
943
+ const services = parseStoredServices(run.services);
768
944
  const input = CodingTaskInputSchema.parse({
769
945
  schemaVersion: CODING_PROTOCOL_VERSION,
770
946
  runId: run.runId,
@@ -776,6 +952,10 @@ export class ContainerExecutor {
776
952
  budgetUsd: run.budgetUsd,
777
953
  deadlineAt: deadlineAt.toISOString(),
778
954
  continuationOf: run.rootCodingRunId ? { runId: run.rootCodingRunId } : undefined,
955
+ // Only when true: an untraced run's input stays exactly what older workers expect.
956
+ ...(run.debugTrace ? { debugTrace: true } : {}),
957
+ // Only when there are some: every other run's input stays exactly what older workers expect.
958
+ ...(services.length > 0 ? { services: workerServices(services) } : {}),
779
959
  });
780
960
  // The run directory is 0700; world-readable mode only crosses Docker's UID boundary.
781
961
  await writeFile(temporary, JSON.stringify(input), { flag: "wx", mode: 0o444 });
@@ -788,12 +968,20 @@ export class ContainerExecutor {
788
968
  preflightForCleanup(run) {
789
969
  if (!Array.isArray(run.protectedPaths) || !run.protectedPaths.every((path) => typeof path === "string"))
790
970
  return null;
971
+ let collectExclude;
972
+ try {
973
+ collectExclude = [...normalizeCollectExclusions(run.collectExclude).paths];
974
+ }
975
+ catch {
976
+ return null;
977
+ }
791
978
  return {
792
979
  runId: run.runId,
793
980
  repository: run.repository,
794
981
  baseRef: run.baseRef,
795
982
  headRef: run.headRef,
796
983
  protectedPaths: run.protectedPaths,
984
+ collectExclude,
797
985
  continuation: run.rootCodingRunId ? { rootRunId: run.rootCodingRunId } : undefined,
798
986
  };
799
987
  }
@@ -837,11 +1025,71 @@ export class ContainerExecutor {
837
1025
  // notification actually firing) -- safe to call again because
838
1026
  // notifyContinuationFinished is find-and-update-or-no-op, never
839
1027
  // find-or-create.
840
- await this.options.vcs.notifyContinuationFinished?.(workspace, run.status === "succeeded" ? "succeeded" : "failed", { agentName: run.agentName });
1028
+ const outcome = run.status === "succeeded" ? "succeeded" : run.status === "budget_exhausted" ? "budget_exhausted" : "failed";
1029
+ const providerClass = run.status === "failed" ? providerClassOfCategory(run.failureCategory) : null;
1030
+ const unreadyServices = run.status === "failed" && run.failureCategory === SERVICE_UNREADY_CATEGORY
1031
+ ? storedServiceNames(run.services)
1032
+ : null;
1033
+ // No path survives to the stored run (only the category does -- see
1034
+ // protected-path-wording.ts), so this can only ever use the pathless sentence.
1035
+ const protectedPathFailed = run.status === "failed" && run.failureCategory === PROTECTED_PATH_CATEGORY;
1036
+ await this.options.vcs.notifyContinuationFinished?.(workspace, outcome, {
1037
+ agentName: run.agentName,
1038
+ ...(outcome === "budget_exhausted" ? { budgetSentence: this.budgetSentence(run) } : {}),
1039
+ ...(providerClass ? { providerSentence: providerSentence(providerClass) } : {}),
1040
+ ...(unreadyServices ? { serviceSentence: serviceUnreadySentence(unreadyServices) } : {}),
1041
+ ...(protectedPathFailed ? { protectedPathSentence: protectedPathSentence(null) } : {}),
1042
+ });
841
1043
  }
842
1044
  await rm(this.artifactPath(run.runId), { recursive: true, force: true }).catch(() => undefined);
843
1045
  this.emit({ stage: "cleanup", runId: run.runId, jobId: run.jobHandle?.id, cleanupSucceeded: true });
844
1046
  }
1047
+ /** Whether the proxy refused this run's session for budget. Never throws. */
1048
+ async outOfBudget(sessionId) {
1049
+ if (!sessionId || !this.options.sessions.budgetExhausted)
1050
+ return false;
1051
+ return this.options.sessions.budgetExhausted(sessionId).catch(() => false);
1052
+ }
1053
+ /** The model-provider failure the proxy relayed for this run's session, classified. Never throws. */
1054
+ async providerFailure(sessionId) {
1055
+ if (!sessionId || !this.options.sessions.upstreamFailure)
1056
+ return null;
1057
+ const code = await this.options.sessions.upstreamFailure(sessionId).catch(() => null);
1058
+ return code ? { class: classifyProviderFailure(code), code } : null;
1059
+ }
1060
+ /**
1061
+ * A failure the model provider's refusal caused. The persisted error names
1062
+ * the class only; the raw upstream code (a fixed identifier, never a message)
1063
+ * goes to one operator line an alert can key on, and the failure itself is
1064
+ * still logged under its diagnostic id.
1065
+ */
1066
+ providerFailureOf(run, provider, error, options = {}) {
1067
+ const logged = this.failure(error, options);
1068
+ containerLog.warn({ event: "coding.provider_failure", runId: run.runId, class: provider.class, upstreamCode: provider.code }, "the model provider refused this coding run's requests");
1069
+ return {
1070
+ error: `coding_provider_${provider.class}`,
1071
+ audit: { failureCategory: `provider_${provider.class}`, diagnosticId: logged.audit.diagnosticId },
1072
+ };
1073
+ }
1074
+ /** The host-safe sentence for a run that ran out of budget. */
1075
+ budgetSentence(run) {
1076
+ return budgetSentence({
1077
+ runBudgetUsd: run.budgetUsd,
1078
+ agentBudgetUsd: run.agentBudgetUsd ?? run.budgetUsd,
1079
+ budgetGroupName: run.budgetGroupName,
1080
+ });
1081
+ }
1082
+ /**
1083
+ * A failure the proxy's budget refusal caused. The failure itself is still
1084
+ * logged under its diagnostic id, so the operator keeps the real reason.
1085
+ */
1086
+ budgetFailure(error, options = {}) {
1087
+ const logged = this.failure(error, options);
1088
+ return {
1089
+ error: "coding_budget_exhausted",
1090
+ audit: { failureCategory: "budget", diagnosticId: logged.audit.diagnosticId },
1091
+ };
1092
+ }
845
1093
  emit(event) {
846
1094
  try {
847
1095
  this.observer.emit(event);
@@ -881,7 +1129,13 @@ export class ContainerExecutor {
881
1129
  // keyed by the same diagnostic id. Token-shaped values are redacted, and a
882
1130
  // cause chain is kept because the outer message is often just a wrapper.
883
1131
  // A user asking to stop is not a failure, so it gets the same id at info.
884
- const line = { diagnosticId, category, reason: describeFailure(error) };
1132
+ // `issues` names only which output-schema fields failed (see SAFE_CODING_OUTPUT_ISSUE).
1133
+ const line = {
1134
+ diagnosticId,
1135
+ category,
1136
+ reason: describeFailure(error),
1137
+ ...(options.issues ? { issues: options.issues } : {}),
1138
+ };
885
1139
  if (options.cancelled === true)
886
1140
  containerLog.info(line, "coding run cancelled; the persisted error is its id only");
887
1141
  else
@@ -906,7 +1160,36 @@ function safeError(error) {
906
1160
  const message = error instanceof Error ? error.message : "unknown";
907
1161
  return `coding_executor:${redactAndTruncate(message, 200).replace(/[^A-Za-z0-9_.:-]/g, "_")}`;
908
1162
  }
1163
+ /**
1164
+ * Error-code prefixes that name their category outright, checked before the
1165
+ * substring heuristics below. Those heuristics filed every `github_*` error
1166
+ * (a failed PR lookup or create after the push, a token mint) under
1167
+ * "workspace" because "github" contains "git".
1168
+ */
1169
+ const CATEGORY_BY_PREFIX = [
1170
+ // A service sidecar never became ready (src/providers/jobs/kubernetes.ts).
1171
+ [SERVICE_UNREADY_ERROR, SERVICE_UNREADY_CATEGORY],
1172
+ // Services on a launcher that can't start them: refused at dispatch, failed here as a backstop.
1173
+ ["coding_services_unsupported", "preflight"],
1174
+ // Claude Code's tool runner failed to start or never became ready: the same category on both launchers.
1175
+ ["docker_tool_runner_", "job"],
1176
+ ["kubernetes_tool_runner_", "job"],
1177
+ // Checked first: the host could not be asked (after one retry), not a lost permission.
1178
+ ["repo_access_check_unavailable", "repo_access_unavailable"],
1179
+ ["repo_access_", "repo_access"],
1180
+ ["github_", "github"],
1181
+ // Checked before the generic "vcs_" fallback: the collected diff touched a
1182
+ // path this agent may not edit, not a generic workspace/git failure.
1183
+ ["vcs_protected_path:", PROTECTED_PATH_CATEGORY],
1184
+ ["vcs_", "workspace"],
1185
+ ["git_", "workspace"],
1186
+ ];
909
1187
  function failureCategory(error) {
1188
+ const raw = error instanceof Error ? error.message : typeof error === "string" ? error : "";
1189
+ for (const [prefix, category] of CATEGORY_BY_PREFIX) {
1190
+ if (raw.startsWith(prefix))
1191
+ return category;
1192
+ }
910
1193
  const message = safeError(error);
911
1194
  if (message.includes("preflight") || message.includes("ownership") || message.includes("image"))
912
1195
  return "preflight";
@@ -12,13 +12,12 @@
12
12
  * stale-heartbeat detection is unchanged; the reconciler consults
13
13
  * `recover()` (Task 6) before declaring a DBOS run lost.
14
14
  */
15
- import type { PrismaClient } from "@prisma/client";
16
- import type { ProviderRegistry } from "../index.js";
15
+ import type { PrismaClient } from "#prisma";
17
16
  import type { DbosConfig } from "../../config/providers.js";
18
- import { type RunnerDb } from "../../core/runner.js";
17
+ import { type NativeRunProviders, type RunnerDb } from "../../core/runner.js";
19
18
  import type { Executor, ExecutionRecoveryResult, PersistedExecutionHandle } from "./types.js";
20
19
  export declare const DBOS_BACKEND = "dbos";
21
- type Providers = Pick<ProviderRegistry, "llm" | "engine" | "datastore" | "secrets" | "memory">;
20
+ type Providers = NativeRunProviders;
22
21
  type Db = RunnerDb & Pick<PrismaClient, "run">;
23
22
  export declare class DbosExecutor implements Executor {
24
23
  private readonly providers;
@@ -5,14 +5,13 @@
5
5
  * heartbeat and recovers the run to `lost` rather than leaving it stuck
6
6
  * `running` forever.
7
7
  */
8
- import type { ProviderRegistry } from "../index.js";
9
- import { type RunnerDb } from "../../core/runner.js";
8
+ import { type NativeRunProviders, type RunnerDb } from "../../core/runner.js";
10
9
  import type { Executor } from "./types.js";
11
10
  export declare class InProcessExecutor implements Executor {
12
11
  private readonly providers;
13
12
  private readonly db;
14
13
  private readonly heartbeatIntervalMs;
15
- constructor(providers: Pick<ProviderRegistry, "llm" | "engine" | "datastore" | "secrets" | "memory">, db?: RunnerDb, heartbeatIntervalMs?: number);
14
+ constructor(providers: NativeRunProviders, db?: RunnerDb, heartbeatIntervalMs?: number);
16
15
  start(runId: string): Promise<void>;
17
16
  stop(_runId: string, _reason?: string): Promise<void>;
18
17
  }
@@ -1,4 +1,5 @@
1
1
  import type { CodingImageSelector, Executor, ExecutionRecoveryResult, PersistedExecutionHandle } from "./types.js";
2
+ import type { CodingProvider } from "../../coding/provider.js";
2
3
  export interface ExecutionKindResolver {
3
4
  kindForRun(runId: string): Promise<"native" | "coding" | null>;
4
5
  }
@@ -21,6 +22,12 @@ export declare class RoutingExecutor implements Executor {
21
22
  launch(): Promise<void>;
22
23
  close(): Promise<void>;
23
24
  resolveCodingWorkerImage(selector: CodingImageSelector): string;
25
+ resolveCodingToolImage(selector: CodingImageSelector): string | null;
26
+ readCodingServiceDeclaration(input: {
27
+ repository: string;
28
+ baseRef: string;
29
+ }): Promise<string | null>;
30
+ supportsCodingServices(provider: CodingProvider): boolean;
24
31
  }
25
32
  export declare class PrismaExecutionKindResolver implements ExecutionKindResolver {
26
33
  private readonly db;
@@ -54,6 +54,19 @@ export class RoutingExecutor {
54
54
  throw new Error("coding_execution_not_configured");
55
55
  return this.coding.resolveCodingWorkerImage(selector);
56
56
  }
57
+ resolveCodingToolImage(selector) {
58
+ if (!this.coding.resolveCodingToolImage)
59
+ throw new Error("coding_execution_not_configured");
60
+ return this.coding.resolveCodingToolImage(selector);
61
+ }
62
+ async readCodingServiceDeclaration(input) {
63
+ if (!this.coding.readCodingServiceDeclaration)
64
+ return null;
65
+ return this.coding.readCodingServiceDeclaration(input);
66
+ }
67
+ supportsCodingServices(provider) {
68
+ return this.coding.supportsCodingServices?.(provider) === true;
69
+ }
57
70
  }
58
71
  export class PrismaExecutionKindResolver {
59
72
  db;