@wardby/cli 0.5.2 → 0.6.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 (411) hide show
  1. package/.env.example +33 -0
  2. package/README.md +25 -14
  3. package/dist/claude-coding-worker/context.d.ts +22 -0
  4. package/dist/claude-coding-worker/context.js +92 -0
  5. package/dist/claude-coding-worker/driver.d.ts +7 -1
  6. package/dist/claude-coding-worker/driver.js +38 -5
  7. package/dist/claude-coding-worker/sdk.js +13 -4
  8. package/dist/cli-help.d.ts +1 -1
  9. package/dist/cli-help.js +5 -2
  10. package/dist/cli-native-mode.d.ts +9 -0
  11. package/dist/cli-native-mode.js +20 -0
  12. package/dist/cli.d.ts +3 -1
  13. package/dist/cli.js +141 -5
  14. package/dist/coding/claude-context.d.ts +45 -0
  15. package/dist/coding/claude-context.js +434 -0
  16. package/dist/coding/local-git.js +9 -1
  17. package/dist/coding/profile.d.ts +12 -0
  18. package/dist/coding/profile.js +6 -0
  19. package/dist/coding/protocol.d.ts +104 -0
  20. package/dist/coding/protocol.js +87 -3
  21. package/dist/coding/registry/pypi.d.ts +25 -1
  22. package/dist/coding/registry/pypi.js +140 -16
  23. package/dist/coding/registry/types.d.ts +17 -3
  24. package/dist/coding-worker/artifact.d.ts +8 -0
  25. package/dist/coding-worker/artifact.js +2 -2
  26. package/dist/coding-worker/driver.js +2 -0
  27. package/dist/coding-worker/sdk.d.ts +6 -0
  28. package/dist/coding-worker/sdk.js +1 -0
  29. package/dist/coding-worker/skills.d.ts +23 -0
  30. package/dist/coding-worker/skills.js +117 -0
  31. package/dist/coding-worker/types.d.ts +2 -0
  32. package/dist/config/github-allowed-accounts.d.ts +15 -0
  33. package/dist/config/github-allowed-accounts.js +36 -0
  34. package/dist/config/low-power.d.ts +12 -0
  35. package/dist/config/low-power.js +18 -0
  36. package/dist/config/mobile.d.ts +12 -0
  37. package/dist/config/mobile.js +18 -0
  38. package/dist/config/providers.d.ts +56 -2
  39. package/dist/config/providers.js +92 -6
  40. package/dist/core/db.d.ts +16 -6
  41. package/dist/core/db.js +12 -10
  42. package/dist/core/dispatch.d.ts +11 -0
  43. package/dist/core/dispatch.js +36 -1
  44. package/dist/core/engine-native.js +9 -5
  45. package/dist/core/host-events.d.ts +49 -3
  46. package/dist/core/host-events.js +289 -15
  47. package/dist/core/http-runtime.js +13 -6
  48. package/dist/core/issue-bridge.js +42 -2
  49. package/dist/core/issue-events.js +12 -1
  50. package/dist/core/issue-status.js +14 -3
  51. package/dist/core/merge-order-check.d.ts +129 -0
  52. package/dist/core/merge-order-check.js +336 -0
  53. package/dist/core/notification-dispatcher.d.ts +70 -0
  54. package/dist/core/notification-dispatcher.js +368 -0
  55. package/dist/core/notification-templates.d.ts +25 -0
  56. package/dist/core/notification-templates.js +105 -0
  57. package/dist/core/notifications.d.ts +10 -0
  58. package/dist/core/notifications.js +38 -0
  59. package/dist/core/pg-auth.d.ts +22 -0
  60. package/dist/core/pg-auth.js +152 -0
  61. package/dist/core/power-composition.d.ts +16 -0
  62. package/dist/core/power-composition.js +37 -0
  63. package/dist/core/power-idle.d.ts +14 -0
  64. package/dist/core/power-idle.js +30 -0
  65. package/dist/core/power.d.ts +71 -0
  66. package/dist/core/power.js +202 -0
  67. package/dist/core/reconciler.d.ts +4 -2
  68. package/dist/core/reconciler.js +29 -5
  69. package/dist/core/related-pull-requests.d.ts +48 -4
  70. package/dist/core/related-pull-requests.js +113 -11
  71. package/dist/core/repo-access.d.ts +15 -0
  72. package/dist/core/repo-access.js +66 -0
  73. package/dist/core/review-fix.d.ts +37 -0
  74. package/dist/core/review-fix.js +207 -6
  75. package/dist/core/review-host-tools.d.ts +20 -0
  76. package/dist/core/review-host-tools.js +58 -2
  77. package/dist/core/runner.d.ts +336 -3
  78. package/dist/core/runner.js +894 -317
  79. package/dist/core/secret-broker-config.d.ts +103 -0
  80. package/dist/core/secret-broker-config.js +112 -0
  81. package/dist/core/secrets.d.ts +39 -4
  82. package/dist/core/secrets.js +148 -40
  83. package/dist/core/workflow-event-recorder.d.ts +14 -0
  84. package/dist/core/workflow-event-recorder.js +120 -0
  85. package/dist/core/workflow-events.d.ts +82 -0
  86. package/dist/core/workflow-events.js +44 -0
  87. package/dist/core/workflow-run-events.d.ts +16 -0
  88. package/dist/core/workflow-run-events.js +40 -0
  89. package/dist/generated/prisma/browser.d.ts +58 -0
  90. package/dist/generated/prisma/client.d.ts +58 -0
  91. package/dist/generated/prisma/commonInputTypes.d.ts +150 -0
  92. package/dist/generated/prisma/enums.d.ts +27 -0
  93. package/dist/generated/prisma/enums.js +23 -0
  94. package/dist/generated/prisma/internal/class.d.ts +88 -0
  95. package/dist/generated/prisma/internal/class.js +4 -4
  96. package/dist/generated/prisma/internal/prismaNamespace.d.ts +772 -1
  97. package/dist/generated/prisma/internal/prismaNamespace.js +135 -4
  98. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +139 -0
  99. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +135 -4
  100. package/dist/generated/prisma/models/Agent.d.ts +360 -1
  101. package/dist/generated/prisma/models/CodingAgentProfile.d.ts +73 -1
  102. package/dist/generated/prisma/models/CodingRun.d.ts +205 -1
  103. package/dist/generated/prisma/models/DeferredReview.d.ts +78 -1
  104. package/dist/generated/prisma/models/NativeGatewayCall.d.ts +1675 -0
  105. package/dist/generated/prisma/models/NativeGatewayCall.js +1 -0
  106. package/dist/generated/prisma/models/NativeGatewaySession.d.ts +1517 -0
  107. package/dist/generated/prisma/models/NativeGatewaySession.js +1 -0
  108. package/dist/generated/prisma/models/NativeWarmWorker.d.ts +1296 -0
  109. package/dist/generated/prisma/models/NativeWarmWorker.js +1 -0
  110. package/dist/generated/prisma/models/NotificationChannel.d.ts +1543 -0
  111. package/dist/generated/prisma/models/NotificationChannel.js +1 -0
  112. package/dist/generated/prisma/models/NotificationDelivery.d.ts +1502 -0
  113. package/dist/generated/prisma/models/NotificationDelivery.js +1 -0
  114. package/dist/generated/prisma/models/NotificationThread.d.ts +1090 -0
  115. package/dist/generated/prisma/models/NotificationThread.js +1 -0
  116. package/dist/generated/prisma/models/Run.d.ts +826 -1
  117. package/dist/generated/prisma/models/RunHostCheck.d.ts +36 -1
  118. package/dist/generated/prisma/models/Secret.d.ts +37 -1
  119. package/dist/generated/prisma/models/SecretBrokerChange.d.ts +1101 -0
  120. package/dist/generated/prisma/models/SecretBrokerChange.js +1 -0
  121. package/dist/generated/prisma/models/WorkflowEvent.d.ts +1422 -0
  122. package/dist/generated/prisma/models/WorkflowEvent.js +1 -0
  123. package/dist/generated/prisma/models.d.ts +8 -0
  124. package/dist/help/build.js +4 -2
  125. package/dist/help/catalog.d.ts +1 -1
  126. package/dist/help/catalog.js +31 -6
  127. package/dist/help-index.json +1492 -113
  128. package/dist/import/create.js +17 -0
  129. package/dist/import/neutral-schema.d.ts +6 -2
  130. package/dist/import/neutral-schema.js +2 -0
  131. package/dist/mcp/auth/access.d.ts +1 -0
  132. package/dist/mcp/auth/ownership.d.ts +1 -1
  133. package/dist/mcp/auth/repo-authorization.js +14 -0
  134. package/dist/mcp/auth/resource-server.d.ts +2 -1
  135. package/dist/mcp/auth/resource-server.js +7 -2
  136. package/dist/mcp/auth/self-hosted/browser.d.ts +1 -1
  137. package/dist/mcp/auth/self-hosted/browser.js +19 -6
  138. package/dist/mcp/context.d.ts +6 -0
  139. package/dist/mcp/host-events/github-ingress.d.ts +23 -1
  140. package/dist/mcp/host-events/github-ingress.js +68 -0
  141. package/dist/mcp/index.d.ts +13 -0
  142. package/dist/mcp/index.js +59 -10
  143. package/dist/mcp/tools/agents.js +83 -12
  144. package/dist/mcp/tools/notification-channels.d.ts +2 -0
  145. package/dist/mcp/tools/notification-channels.js +293 -0
  146. package/dist/mcp/tools/power.d.ts +2 -0
  147. package/dist/mcp/tools/power.js +77 -0
  148. package/dist/mcp/tools/runs.d.ts +0 -6
  149. package/dist/mcp/tools/runs.js +7 -2
  150. package/dist/mcp/tools/secret-broker-compat.d.ts +12 -0
  151. package/dist/mcp/tools/secret-broker-compat.js +23 -0
  152. package/dist/mcp/tools/secret-elicitation-form.d.ts +22 -2
  153. package/dist/mcp/tools/secret-elicitation-form.js +200 -10
  154. package/dist/mcp/tools/secret-elicitation-server.js +8 -1
  155. package/dist/mcp/tools/secret-elicitation.d.ts +38 -3
  156. package/dist/mcp/tools/secret-elicitation.js +110 -12
  157. package/dist/mcp/tools/secrets.js +184 -7
  158. package/dist/mcp/tools/tools.d.ts +1 -1
  159. package/dist/mcp/tools/tools.js +22 -2
  160. package/dist/native-worker/composition.d.ts +23 -0
  161. package/dist/native-worker/composition.js +77 -0
  162. package/dist/native-worker/docker-isolation.d.ts +65 -0
  163. package/dist/native-worker/docker-isolation.js +183 -0
  164. package/dist/native-worker/docker-launcher.d.ts +74 -0
  165. package/dist/native-worker/docker-launcher.js +237 -0
  166. package/dist/native-worker/gateway-server.d.ts +39 -0
  167. package/dist/native-worker/gateway-server.js +315 -0
  168. package/dist/native-worker/gateway.d.ts +44 -0
  169. package/dist/native-worker/gateway.js +197 -0
  170. package/dist/native-worker/http-server.d.ts +22 -0
  171. package/dist/native-worker/http-server.js +109 -0
  172. package/dist/native-worker/http-transport.d.ts +17 -0
  173. package/dist/native-worker/http-transport.js +88 -0
  174. package/dist/native-worker/kubernetes-isolation.d.ts +90 -0
  175. package/dist/native-worker/kubernetes-isolation.js +218 -0
  176. package/dist/native-worker/kubernetes-launcher.d.ts +102 -0
  177. package/dist/native-worker/kubernetes-launcher.js +335 -0
  178. package/dist/native-worker/launch.d.ts +45 -0
  179. package/dist/native-worker/launch.js +44 -0
  180. package/dist/native-worker/ledger.d.ts +109 -0
  181. package/dist/native-worker/ledger.js +192 -0
  182. package/dist/native-worker/loopback.d.ts +12 -0
  183. package/dist/native-worker/loopback.js +49 -0
  184. package/dist/native-worker/main.d.ts +7 -0
  185. package/dist/native-worker/main.js +74 -0
  186. package/dist/native-worker/protocol.d.ts +744 -0
  187. package/dist/native-worker/protocol.js +200 -0
  188. package/dist/native-worker/sandbox-executor.d.ts +68 -0
  189. package/dist/native-worker/sandbox-executor.js +210 -0
  190. package/dist/native-worker/stdio.d.ts +29 -0
  191. package/dist/native-worker/stdio.js +188 -0
  192. package/dist/native-worker/warm-delivery.d.ts +16 -0
  193. package/dist/native-worker/warm-delivery.js +33 -0
  194. package/dist/native-worker/warm-pool-ledger.d.ts +39 -0
  195. package/dist/native-worker/warm-pool-ledger.js +73 -0
  196. package/dist/native-worker/warm-pool.d.ts +107 -0
  197. package/dist/native-worker/warm-pool.js +283 -0
  198. package/dist/native-worker/worker.d.ts +17 -0
  199. package/dist/native-worker/worker.js +115 -0
  200. package/dist/providers/auth/authorization-server.d.ts +3 -0
  201. package/dist/providers/auth/index.js +2 -0
  202. package/dist/providers/auth/self-hosted.d.ts +13 -1
  203. package/dist/providers/auth/self-hosted.js +54 -4
  204. package/dist/providers/chat/fake.d.ts +46 -0
  205. package/dist/providers/chat/fake.js +39 -0
  206. package/dist/providers/chat/index.d.ts +4 -0
  207. package/dist/providers/chat/index.js +8 -0
  208. package/dist/providers/chat/slack.d.ts +37 -0
  209. package/dist/providers/chat/slack.js +105 -0
  210. package/dist/providers/chat/types.d.ts +42 -0
  211. package/dist/providers/chat/types.js +19 -0
  212. package/dist/providers/coding-proxy/mock-upstream.d.ts +33 -0
  213. package/dist/providers/coding-proxy/mock-upstream.js +116 -0
  214. package/dist/providers/coding-proxy/registry/prisma-store.d.ts +3 -0
  215. package/dist/providers/coding-proxy/registry/prisma-store.js +17 -0
  216. package/dist/providers/coding-proxy/registry/service.d.ts +4 -0
  217. package/dist/providers/coding-proxy/registry/service.js +20 -3
  218. package/dist/providers/coding-proxy/registry/store.d.ts +18 -1
  219. package/dist/providers/coding-proxy/registry/store.js +33 -0
  220. package/dist/providers/coding-proxy/runtime.js +12 -1
  221. package/dist/providers/coding-proxy/types.d.ts +2 -0
  222. package/dist/providers/engine/types.d.ts +19 -0
  223. package/dist/providers/engine/types.js +22 -0
  224. package/dist/providers/executor/composition.d.ts +24 -1
  225. package/dist/providers/executor/composition.js +39 -6
  226. package/dist/providers/executor/container.d.ts +24 -1
  227. package/dist/providers/executor/container.js +101 -11
  228. package/dist/providers/executor/dbos.d.ts +42 -0
  229. package/dist/providers/executor/dbos.js +118 -12
  230. package/dist/providers/executor/deferred.d.ts +56 -0
  231. package/dist/providers/executor/deferred.js +119 -0
  232. package/dist/providers/executor/routing.d.ts +37 -10
  233. package/dist/providers/executor/routing.js +73 -32
  234. package/dist/providers/executor/types.d.ts +12 -0
  235. package/dist/providers/jobs/docker.js +3 -3
  236. package/dist/providers/jobs/fake-kubernetes-api.d.ts +21 -2
  237. package/dist/providers/jobs/fake-kubernetes-api.js +42 -1
  238. package/dist/providers/jobs/kubernetes-api.d.ts +17 -1
  239. package/dist/providers/jobs/kubernetes-api.js +7 -0
  240. package/dist/providers/jobs/kubernetes-client.d.ts +10 -1
  241. package/dist/providers/jobs/kubernetes-client.js +52 -3
  242. package/dist/providers/jobs/kubernetes-dry-run-fixture.d.ts +6 -1
  243. package/dist/providers/jobs/kubernetes-dry-run-fixture.js +6 -3
  244. package/dist/providers/jobs/kubernetes-isolation.d.ts +16 -1
  245. package/dist/providers/jobs/kubernetes-isolation.js +58 -5
  246. package/dist/providers/jobs/kubernetes-platform.d.ts +4 -1
  247. package/dist/providers/jobs/kubernetes-platform.js +38 -1
  248. package/dist/providers/jobs/kubernetes-preflight.d.ts +2 -1
  249. package/dist/providers/jobs/kubernetes-preflight.js +4 -2
  250. package/dist/providers/jobs/kubernetes.d.ts +56 -7
  251. package/dist/providers/jobs/kubernetes.js +117 -36
  252. package/dist/providers/jobs/metadata-probes.d.ts +11 -0
  253. package/dist/providers/jobs/metadata-probes.js +13 -0
  254. package/dist/providers/jobs/types.d.ts +9 -0
  255. package/dist/providers/llm/openai.js +0 -1
  256. package/dist/providers/review-host/github.d.ts +11 -1
  257. package/dist/providers/review-host/github.js +50 -0
  258. package/dist/providers/review-host/types.d.ts +23 -0
  259. package/dist/providers/vcs/github.d.ts +32 -6
  260. package/dist/providers/vcs/github.js +74 -16
  261. package/dist/quickstart/coding-db.js +14 -2
  262. package/dist/quickstart/coding-doctor.d.ts +3 -0
  263. package/dist/quickstart/coding-doctor.js +3 -0
  264. package/dist/quickstart/coding-images.d.ts +9 -0
  265. package/dist/quickstart/coding-images.js +39 -0
  266. package/dist/quickstart/coding-seed.d.ts +20 -1
  267. package/dist/quickstart/coding-seed.js +96 -7
  268. package/dist/quickstart/coding.d.ts +2 -0
  269. package/dist/quickstart/coding.js +75 -3
  270. package/dist/quickstart/config.d.ts +2 -0
  271. package/dist/quickstart/config.js +4 -0
  272. package/dist/quickstart/images.d.ts +13 -0
  273. package/dist/quickstart/images.js +80 -12
  274. package/dist/quickstart/index.d.ts +0 -2
  275. package/dist/quickstart/index.js +10 -14
  276. package/dist/quickstart/manifests.d.ts +56 -0
  277. package/dist/quickstart/manifests.js +523 -0
  278. package/dist/quickstart/python-detect.d.ts +10 -0
  279. package/dist/quickstart/python-detect.js +32 -0
  280. package/dist/quickstart/repo-packages.d.ts +10 -0
  281. package/dist/quickstart/repo-packages.js +132 -0
  282. package/dist/quickstart/reviewer-prompt.d.ts +12 -0
  283. package/dist/quickstart/reviewer-prompt.js +84 -0
  284. package/dist/quickstart/scenarios.d.ts +19 -0
  285. package/dist/quickstart/scenarios.js +28 -0
  286. package/dist/quickstart/sync-reviewer-prompt.d.ts +1 -0
  287. package/dist/quickstart/sync-reviewer-prompt.js +10 -0
  288. package/dist/quickstart-images.json +1 -1
  289. package/dist/sandbox/brokered-fetch.d.ts +23 -0
  290. package/dist/sandbox/brokered-fetch.js +37 -0
  291. package/dist/sandbox/host-functions.d.ts +34 -0
  292. package/dist/sandbox/host-functions.js +144 -80
  293. package/dist/sandbox/prelude.js +7 -1
  294. package/dist/sandbox/run-in-sandbox.d.ts +19 -0
  295. package/dist/sandbox/run-in-sandbox.js +28 -10
  296. package/dist/sandbox/safe-fetch.d.ts +2 -0
  297. package/dist/sandbox/safe-fetch.js +3 -1
  298. package/dist/sandbox/secret-broker-sigv4.d.ts +2 -0
  299. package/dist/sandbox/secret-broker-sigv4.js +49 -0
  300. package/dist/sandbox/secret-broker.d.ts +34 -0
  301. package/dist/sandbox/secret-broker.js +162 -0
  302. package/dist/sandbox/user-tool.d.ts +14 -0
  303. package/dist/sandbox/user-tool.js +32 -0
  304. package/dist/serve.js +18 -0
  305. package/dist/viewer/api-schema.d.ts +214 -18
  306. package/dist/viewer/api-schema.js +22 -0
  307. package/dist/viewer/build-schemas.js +4 -1
  308. package/dist/viewer/event-bus.d.ts +6 -0
  309. package/dist/viewer/event-bus.js +16 -7
  310. package/dist/viewer/graph.d.ts +5 -2
  311. package/dist/viewer/graph.js +7 -2
  312. package/dist/viewer/http.d.ts +0 -1
  313. package/dist/viewer/http.js +2 -2
  314. package/dist/viewer/infra.js +25 -2
  315. package/dist/viewer/limits.d.ts +4 -0
  316. package/dist/viewer/limits.js +4 -0
  317. package/dist/viewer/openapi.d.ts +15 -0
  318. package/dist/viewer/openapi.js +172 -0
  319. package/docs/README.md +7 -0
  320. package/docs/agent-recipes.md +57 -8
  321. package/docs/architecture-runtime.md +16 -1
  322. package/docs/code-review-agents.md +139 -5
  323. package/docs/coding-agent-setup.md +127 -2
  324. package/docs/coding-packages.md +105 -5
  325. package/docs/coding-services.md +3 -1
  326. package/docs/coding-worker-byo-images.md +239 -2
  327. package/docs/coding-worker-isolation.md +88 -23
  328. package/docs/getting-started-gke.md +225 -9
  329. package/docs/getting-started-identity-provider.md +21 -10
  330. package/docs/getting-started.md +140 -11
  331. package/docs/jira-agents.md +5 -1
  332. package/docs/knowledge.md +11 -0
  333. package/docs/native-sandbox.md +635 -0
  334. package/docs/release-verification.md +9 -0
  335. package/docs/security-deployment.md +113 -12
  336. package/docs/slack-notifications.md +252 -0
  337. package/docs/tool-secrets.md +252 -0
  338. package/docs/viewer-api.md +46 -5
  339. package/docs/viewer-api.openapi.yml +1404 -0
  340. package/help/admin-viewer.md +6 -2
  341. package/help/agent-recipes.md +27 -0
  342. package/help/architecture-agent.md +136 -0
  343. package/help/brokered-secrets.md +79 -0
  344. package/help/build-claude-code-image.md +131 -0
  345. package/help/build-worker-image.md +195 -0
  346. package/help/code-review-agents.md +68 -6
  347. package/help/coding-packages.md +31 -2
  348. package/help/creating-agents.md +14 -0
  349. package/help/deploy-gke.md +37 -6
  350. package/help/deployment-targets.md +24 -13
  351. package/help/errors/coding-provider-not-configured.md +2 -2
  352. package/help/errors/database-auth-invalid.md +28 -0
  353. package/help/errors/github-account-not-allowed.md +25 -0
  354. package/help/errors/low-power-disabled.md +29 -0
  355. package/help/errors/low-power-scale-forbidden.md +35 -0
  356. package/help/errors/low-power-wake-timeout.md +30 -0
  357. package/help/errors/mobile-disabled.md +31 -0
  358. package/help/errors/native-sandbox-capacity.md +22 -0
  359. package/help/errors/native-sandbox-deadline-exceeded.md +19 -0
  360. package/help/errors/native-sandbox-docker-failed.md +20 -0
  361. package/help/errors/native-sandbox-gateway-unavailable.md +21 -0
  362. package/help/errors/native-sandbox-image-not-pinned.md +19 -0
  363. package/help/errors/native-sandbox-isolation-mismatch.md +20 -0
  364. package/help/errors/native-sandbox-network-unenforced.md +22 -0
  365. package/help/errors/native-sandbox-requires-catalog.md +31 -0
  366. package/help/errors/native-sandbox-unavailable.md +19 -0
  367. package/help/errors/native-sandbox-warm-delivery-failed.md +21 -0
  368. package/help/errors/native-sandbox-worker-exited.md +20 -0
  369. package/help/errors/native-sandbox-worker-lost.md +20 -0
  370. package/help/errors/native-sandbox-worker-unready.md +22 -0
  371. package/help/errors/secret-broker-body-invalid.md +26 -0
  372. package/help/errors/secret-broker-changed.md +20 -0
  373. package/help/errors/secret-broker-config-invalid.md +30 -0
  374. package/help/errors/secret-broker-conflict.md +27 -0
  375. package/help/errors/secret-broker-destination-denied.md +28 -0
  376. package/help/errors/secret-broker-value-invalid.md +28 -0
  377. package/help/errors/secret-brokered.md +24 -0
  378. package/help/errors/secret-not-brokered.md +25 -0
  379. package/help/errors/slack-auth-failed.md +48 -0
  380. package/help/errors/slack-channel-unreachable.md +39 -0
  381. package/help/errors/slack-not-configured.md +33 -0
  382. package/help/getting-started.md +18 -0
  383. package/help/github.md +18 -1
  384. package/help/identity-and-access.md +5 -1
  385. package/help/local-repositories.md +35 -5
  386. package/help/low-power-mode.md +125 -0
  387. package/help/merge-order-check.md +117 -0
  388. package/help/mobile-app.md +54 -0
  389. package/help/native-sandbox.md +136 -0
  390. package/help/observability.md +8 -0
  391. package/help/related-pull-requests.md +70 -9
  392. package/help/repo-instructions-and-skills.md +91 -0
  393. package/help/review-fix-rounds.md +10 -0
  394. package/help/slack-notifications.md +62 -0
  395. package/help/troubleshooting/coding-workers.md +21 -0
  396. package/package.json +13 -5
  397. package/prisma/migrations/20261009000003_native_execution_mode/migration.sql +12 -0
  398. package/prisma/migrations/20261009000004_native_gateway_ledger/migration.sql +65 -0
  399. package/prisma/migrations/20261009000005_native_gateway_call_child_agent/migration.sql +5 -0
  400. package/prisma/migrations/20261009000006_run_deferred_start_stop/migration.sql +14 -0
  401. package/prisma/migrations/20261009000007_native_gateway_network_ready/migration.sql +5 -0
  402. package/prisma/migrations/20261010000001_workflow_notifications/migration.sql +99 -0
  403. package/prisma/migrations/20261011000001_native_gateway_session_status_index/migration.sql +2 -0
  404. package/prisma/migrations/20261012000001_native_warm_worker/migration.sql +32 -0
  405. package/prisma/migrations/20261013000001_coding_run_merge_order/migration.sql +2 -0
  406. package/prisma/migrations/20261013000001_secret_broker/migration.sql +17 -0
  407. package/prisma/migrations/20261013000002_deferred_review_request/migration.sql +4 -0
  408. package/prisma/migrations/20261014000000_repo_skills/migration.sql +15 -0
  409. package/prisma/migrations/20261015000001_coding_run_merge_order_index/migration.sql +2 -0
  410. package/prisma/migrations/20261015000002_no_change_rereview/migration.sql +2 -0
  411. package/prisma/schema.prisma +362 -91
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Which GitHub accounts this server serves through its GitHub App: the App's
3
+ * owner, always, plus WARDBY_GITHUB_ALLOWED_ACCOUNTS. Fail closed — there is no
4
+ * wildcard. Events from other accounts are ignored, their installations
5
+ * removed, and their repositories refused.
6
+ */
7
+ export const GITHUB_ACCOUNT_NOT_ALLOWED = "github_account_not_allowed";
8
+ const LOGIN = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})$/;
9
+ export function isGitHubLogin(value) {
10
+ return typeof value === "string" && LOGIN.test(value);
11
+ }
12
+ export function parseGitHubAllowedAccounts(raw) {
13
+ const entries = (raw ?? "")
14
+ .split(",")
15
+ .map((entry) => entry.trim())
16
+ .filter((entry) => entry !== "");
17
+ for (const entry of entries) {
18
+ if (!LOGIN.test(entry))
19
+ throw new Error(`WARDBY_GITHUB_ALLOWED_ACCOUNTS: "${entry}" is not a GitHub login.`);
20
+ }
21
+ return new Set(entries.map((entry) => entry.toLowerCase()));
22
+ }
23
+ export function loadGitHubAllowedAccounts(env = process.env) {
24
+ return parseGitHubAllowedAccounts(env.WARDBY_GITHUB_ALLOWED_ACCOUNTS);
25
+ }
26
+ /** The App's owner plus the configured accounts, lower-cased. */
27
+ export function trustedAccounts(ownerLogin, extra) {
28
+ return new Set([ownerLogin.toLowerCase(), ...extra]);
29
+ }
30
+ /** The owner of an `owner/name` GitHub repository, lower-cased. */
31
+ export function githubAccountOf(repository) {
32
+ return repository.split("/", 1)[0].toLowerCase();
33
+ }
34
+ export function isGitHubAccountAllowed(trusted, login) {
35
+ return trusted.has(login.toLowerCase());
36
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Low-power mode from the environment.
3
+ *
4
+ * WARDBY_LOW_POWER=1 lets the control plane scale idle GKE deployments to zero;
5
+ * LOW_POWER_IDLE_MINUTES is how long they sit idle first. Unset/0 = off. See
6
+ * docs/getting-started-gke.md "Low-power mode".
7
+ */
8
+ export interface LowPowerConfig {
9
+ enabled: boolean;
10
+ idleMinutes: number;
11
+ }
12
+ export declare function loadLowPowerConfig(env?: NodeJS.ProcessEnv): LowPowerConfig;
@@ -0,0 +1,18 @@
1
+ const DEFAULT_IDLE_MINUTES = 30;
2
+ export function loadLowPowerConfig(env = process.env) {
3
+ const flag = env.WARDBY_LOW_POWER?.trim();
4
+ if (flag !== undefined && flag !== "" && flag !== "0" && flag !== "1") {
5
+ throw new Error("WARDBY_LOW_POWER must be 0 or 1.");
6
+ }
7
+ // LOW_POWER_IDLE_MINUTES only matters with the mode on; off, it is ignored entirely.
8
+ if (flag !== "1")
9
+ return { enabled: false, idleMinutes: DEFAULT_IDLE_MINUTES };
10
+ const raw = env.LOW_POWER_IDLE_MINUTES?.trim();
11
+ if (!raw)
12
+ return { enabled: true, idleMinutes: DEFAULT_IDLE_MINUTES };
13
+ const idleMinutes = /^\d+$/.test(raw) ? Number(raw) : NaN;
14
+ if (!(idleMinutes >= 5 && idleMinutes <= 1440)) {
15
+ throw new Error("LOW_POWER_IDLE_MINUTES must be an integer from 5 to 1440.");
16
+ }
17
+ return { enabled: true, idleMinutes };
18
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Mobile app access (WARDBY_MOBILE_ENABLED, off by default). S1: the app's
3
+ * private-use OAuth redirect (RFC 8252 §7.1); later: push notifications.
4
+ */
5
+ export declare const DEFAULT_MOBILE_REDIRECT_URI = "com.wardby.mobile:/oauth";
6
+ export declare const MOBILE_DISABLED = "mobile_disabled";
7
+ export interface MobileConfig {
8
+ enabled: boolean;
9
+ /** The one redirect URI mobile sign-in may use; matched exactly. */
10
+ redirectUri: string;
11
+ }
12
+ export declare function loadMobileConfig(env?: NodeJS.ProcessEnv): MobileConfig;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Mobile app access (WARDBY_MOBILE_ENABLED, off by default). S1: the app's
3
+ * private-use OAuth redirect (RFC 8252 §7.1); later: push notifications.
4
+ */
5
+ export const DEFAULT_MOBILE_REDIRECT_URI = "com.wardby.mobile:/oauth";
6
+ export const MOBILE_DISABLED = "mobile_disabled";
7
+ /** A reverse-domain scheme (at least one dot), ":/", then a path — no host, query, or fragment. */
8
+ const PRIVATE_USE_REDIRECT = /^[a-z][a-z0-9+-]*(\.[a-z0-9+-]+)+:\/(?!\/)[A-Za-z0-9/_-]*$/;
9
+ export function loadMobileConfig(env = process.env) {
10
+ const raw = env.WARDBY_MOBILE_ENABLED?.trim().toLowerCase() ?? "";
11
+ if (raw !== "" && raw !== "true" && raw !== "false")
12
+ throw new Error("WARDBY_MOBILE_ENABLED must be true or false.");
13
+ const redirectUri = env.WARDBY_MOBILE_REDIRECT_URI?.trim() || DEFAULT_MOBILE_REDIRECT_URI;
14
+ if (!PRIVATE_USE_REDIRECT.test(redirectUri)) {
15
+ throw new Error("WARDBY_MOBILE_REDIRECT_URI must be a private-use redirect such as com.example.app:/oauth.");
16
+ }
17
+ return { enabled: raw === "true", redirectUri };
18
+ }
@@ -7,6 +7,7 @@
7
7
  * adapters) is added once the adapters exist.
8
8
  */
9
9
  import { type KubernetesPlatform } from "../providers/jobs/kubernetes-platform.js";
10
+ import { type MobileConfig } from "./mobile.js";
10
11
  export type JobLauncherKind = "local" | "docker" | "kubernetes";
11
12
  export type EmailProviderKind = "smtp" | "ses";
12
13
  export type SecretCipherKind = "app-key" | "kms";
@@ -79,6 +80,13 @@ export interface JiraConfig {
79
80
  * are rejected.
80
81
  */
81
82
  export declare function loadJiraConfig(env?: NodeJS.ProcessEnv): JiraConfig | null;
83
+ export interface SlackConfig {
84
+ botToken: string;
85
+ apiBaseUrl: string;
86
+ customize: boolean;
87
+ }
88
+ /** Slack workflow notifications; null when WARDBY_SLACK_BOT_TOKEN is unset (and no other Slack variable is). */
89
+ export declare function loadSlackConfig(env?: NodeJS.ProcessEnv): SlackConfig | null;
82
90
  export interface ContainerExecutorConfig {
83
91
  workerImage?: string;
84
92
  claudeWorkerImage?: string;
@@ -122,6 +130,49 @@ export interface CodingConcurrencyConfig {
122
130
  * before closing the executor (SHUTDOWN_DRAIN_SECONDS, default 600; 0 = do
123
131
  * not wait). Keep the platform's termination grace period above this.
124
132
  */
133
+ /** The native sandbox (docs/native-sandbox.md): where sandbox-mode native runs execute. */
134
+ interface NativeSandboxCommonConfig {
135
+ workerImage: string;
136
+ /** What a worker dials; undefined = the launcher's default (Docker: the gateway alias; Kubernetes: the Service's ClusterIP). */
137
+ gatewayUrl?: string;
138
+ cpus: number;
139
+ memoryMb: number;
140
+ /** Docker only: Kubernetes has no per-pod process limit (it is a node-level kubelet setting). */
141
+ pids: number;
142
+ /** NATIVE_SANDBOX_MAX_CONCURRENT: at most this many sandbox runs at once (unset = no cap). */
143
+ maxConcurrent?: number;
144
+ /** NATIVE_SANDBOX_WARM_POOL_SIZE: idle, isolated workers kept ready for runs to claim (0 = no pool). */
145
+ warmPoolSize: number;
146
+ /** NATIVE_SANDBOX_WARM_MAX_AGE_MS: an idle worker older than this is replaced, never claimed. */
147
+ warmMaxAgeMs: number;
148
+ }
149
+ export interface DockerNativeSandboxConfig extends NativeSandboxCommonConfig {
150
+ launcher: "docker";
151
+ /** The container running `wardby native-gateway`, joined to each run's network. */
152
+ gatewayContainer: string;
153
+ }
154
+ export interface KubernetesNativeSandboxConfig extends NativeSandboxCommonConfig {
155
+ launcher: "kubernetes";
156
+ /** Where run pods (and the native gateway) live. */
157
+ namespace: string;
158
+ context?: string;
159
+ /** The native gateway's Service in that namespace. */
160
+ gatewayService: string;
161
+ runtimeClassName?: string;
162
+ /** KUBERNETES_PLATFORM: worker pods are conformed to its admission rules. */
163
+ platform: KubernetesPlatform;
164
+ /** NATIVE_SANDBOX_PRIORITY_CLASS, else KUBERNETES_RUN_PRIORITY_CLASS. */
165
+ priorityClassName?: string;
166
+ /** How long a worker pod may take to start running (NATIVE_SANDBOX_READY_TIMEOUT_MS). */
167
+ readyTimeoutMs?: number;
168
+ /** How long its isolation may take to prove from inside (NATIVE_SANDBOX_ENFORCEMENT_TIMEOUT_MS). */
169
+ enforcementTimeoutMs?: number;
170
+ }
171
+ /** The native gateway Service (and Deployment) name when NATIVE_GATEWAY_SERVICE is unset. */
172
+ export declare const DEFAULT_NATIVE_GATEWAY_SERVICE = "wardby-native-gateway";
173
+ export type NativeSandboxConfig = DockerNativeSandboxConfig | KubernetesNativeSandboxConfig;
174
+ /** Undefined when NATIVE_SANDBOX_LAUNCHER is unset (sandbox-mode runs then fail closed). Throws on a bad configuration. */
175
+ export declare function loadNativeSandboxConfig(env?: NodeJS.ProcessEnv): NativeSandboxConfig | undefined;
125
176
  export declare function loadShutdownDrainSeconds(env?: NodeJS.ProcessEnv): number;
126
177
  export declare function loadCodingConcurrencyConfig(env?: NodeJS.ProcessEnv): CodingConcurrencyConfig;
127
178
  /** JOB_LAUNCHER=kubernetes: where coding-run pods go, how they reach the in-cluster proxy, and which platform's admission rules apply. */
@@ -144,8 +195,9 @@ export interface KubernetesJobConfig {
144
195
  preflightTimeoutMs?: number;
145
196
  readyTimeoutMs?: number;
146
197
  /**
147
- * Bound for a single NetworkPolicy-enforcement probe exec (the keeper running two sequential
148
- * connects). Defaults to the launcher's own 10 s, which a resource-constrained keeper (e.g. a
198
+ * Time budget for a single NetworkPolicy-enforcement probe (the keeper running two sequential
199
+ * connects); a streak's probes share one exec whose timeout the launcher derives from this.
200
+ * Defaults to the launcher's own 10 s, which a resource-constrained keeper (e.g. a
149
201
  * laptop `kind` cluster's default 250m CPU / 128Mi limit) can exceed even though the probe
150
202
  * itself is healthy — observed live: raising this to 60_000 was enough on `kind`. GKE Autopilot
151
203
  * is unaffected by leaving this unset. The launcher derives its overall enforcement wall-clock
@@ -199,6 +251,8 @@ export interface AuthConfig {
199
251
  signingKey?: string;
200
252
  credentialHashKey?: string;
201
253
  maxClients?: number;
254
+ /** Self-hosted mode: mobile sign-in (WARDBY_MOBILE_ENABLED, WARDBY_MOBILE_REDIRECT_URI). */
255
+ mobile: MobileConfig;
202
256
  }
203
257
  /** Read auth-adapter config (issuer/JWKS/audience/signing key) from the environment. */
204
258
  export declare function loadAuthConfig(env?: NodeJS.ProcessEnv): AuthConfig;
@@ -6,7 +6,8 @@
6
6
  * code change. The `assembleProviders` factory (which constructs the concrete
7
7
  * adapters) is added once the adapters exist.
8
8
  */
9
- import { KUBERNETES_PLATFORMS } from "../providers/jobs/kubernetes-platform.js";
9
+ import { GVISOR_RUNTIME_CLASS, KUBERNETES_PLATFORMS, platformProfile, } from "../providers/jobs/kubernetes-platform.js";
10
+ import { loadMobileConfig } from "./mobile.js";
10
11
  /** Read provider selection from environment variables, defaulting to portable. */
11
12
  export function loadProviderConfig(env = process.env) {
12
13
  return {
@@ -137,6 +138,30 @@ export function loadJiraConfig(env = process.env) {
137
138
  ...(epicLinkField ? { epicLinkField } : {}),
138
139
  };
139
140
  }
141
+ /** Slack workflow notifications; null when WARDBY_SLACK_BOT_TOKEN is unset (and no other Slack variable is). */
142
+ export function loadSlackConfig(env = process.env) {
143
+ const token = env.WARDBY_SLACK_BOT_TOKEN?.trim();
144
+ const base = env.WARDBY_SLACK_API_BASE_URL?.trim();
145
+ const customizeRaw = env.WARDBY_SLACK_CUSTOMIZE?.trim().toLowerCase();
146
+ if (!token) {
147
+ if (base || customizeRaw)
148
+ throw new Error("WARDBY_SLACK_API_BASE_URL/WARDBY_SLACK_CUSTOMIZE need WARDBY_SLACK_BOT_TOKEN.");
149
+ return null;
150
+ }
151
+ if (!token.startsWith("xoxb-"))
152
+ throw new Error("WARDBY_SLACK_BOT_TOKEN must be a bot token (xoxb-…).");
153
+ let apiBaseUrl = "https://slack.com/api";
154
+ if (base) {
155
+ const url = new URL(base);
156
+ if (url.protocol !== "https:")
157
+ throw new Error("WARDBY_SLACK_API_BASE_URL must be an https URL.");
158
+ apiBaseUrl = base.replace(/\/+$/, "");
159
+ }
160
+ if (customizeRaw && customizeRaw !== "true" && customizeRaw !== "false") {
161
+ throw new Error("WARDBY_SLACK_CUSTOMIZE must be true or false.");
162
+ }
163
+ return { botToken: token, apiBaseUrl, customize: customizeRaw === "true" };
164
+ }
140
165
  function optionalPositiveNumber(value, name, fallback) {
141
166
  if (value === undefined)
142
167
  return fallback;
@@ -195,11 +220,67 @@ export function loadContainerExecutorConfig(env = process.env) {
195
220
  additionalWorkerImages,
196
221
  };
197
222
  }
198
- /**
199
- * How long a shutting-down instance waits for its in-flight native runs
200
- * before closing the executor (SHUTDOWN_DRAIN_SECONDS, default 600; 0 = do
201
- * not wait). Keep the platform's termination grace period above this.
202
- */
223
+ /** The native gateway Service (and Deployment) name when NATIVE_GATEWAY_SERVICE is unset. */
224
+ export const DEFAULT_NATIVE_GATEWAY_SERVICE = "wardby-native-gateway";
225
+ /** Undefined when NATIVE_SANDBOX_LAUNCHER is unset (sandbox-mode runs then fail closed). Throws on a bad configuration. */
226
+ export function loadNativeSandboxConfig(env = process.env) {
227
+ const launcher = env.NATIVE_SANDBOX_LAUNCHER?.trim();
228
+ if (!launcher)
229
+ return undefined;
230
+ if (launcher !== "docker" && launcher !== "kubernetes") {
231
+ throw new Error(`NATIVE_SANDBOX_LAUNCHER must be "docker" or "kubernetes" (got "${launcher}").`);
232
+ }
233
+ const workerImage = imageVariable(env.NATIVE_SANDBOX_WORKER_IMAGE);
234
+ if (!workerImage)
235
+ throw new Error(`NATIVE_SANDBOX_WORKER_IMAGE is required when NATIVE_SANDBOX_LAUNCHER=${launcher}.`);
236
+ const gatewayUrl = env.NATIVE_GATEWAY_URL?.trim();
237
+ if (gatewayUrl && !/^https?:\/\/[^\s]+$/.test(gatewayUrl)) {
238
+ throw new Error(`NATIVE_GATEWAY_URL must be an http(s) URL (got "${gatewayUrl}").`);
239
+ }
240
+ const common = {
241
+ workerImage,
242
+ ...(gatewayUrl ? { gatewayUrl } : {}),
243
+ cpus: optionalPositiveNumber(env.NATIVE_SANDBOX_CPUS, "NATIVE_SANDBOX_CPUS", 1),
244
+ memoryMb: optionalPositiveInteger(env.NATIVE_SANDBOX_MEMORY_MB, "NATIVE_SANDBOX_MEMORY_MB") ?? 512,
245
+ pids: optionalPositiveInteger(env.NATIVE_SANDBOX_PIDS, "NATIVE_SANDBOX_PIDS") ?? 128,
246
+ ...optionalField("maxConcurrent", optionalPositiveInteger(env.NATIVE_SANDBOX_MAX_CONCURRENT, "NATIVE_SANDBOX_MAX_CONCURRENT")),
247
+ warmPoolSize: optionalBoundedInteger(env.NATIVE_SANDBOX_WARM_POOL_SIZE?.trim() || undefined, "NATIVE_SANDBOX_WARM_POOL_SIZE", 0, 50) ?? 0,
248
+ warmMaxAgeMs: optionalBoundedInteger(env.NATIVE_SANDBOX_WARM_MAX_AGE_MS?.trim() || undefined, "NATIVE_SANDBOX_WARM_MAX_AGE_MS", 60_000, 21_600_000) ?? 1_800_000,
249
+ };
250
+ if (launcher === "kubernetes") {
251
+ // A cluster pulls by registry digest; a local image id means nothing to it.
252
+ if (!/@sha256:[0-9a-f]{64}$/.test(workerImage)) {
253
+ throw new Error("NATIVE_SANDBOX_WORKER_IMAGE must be a registry digest (repo@sha256:...) when NATIVE_SANDBOX_LAUNCHER=kubernetes.");
254
+ }
255
+ const namespace = env.NATIVE_SANDBOX_NAMESPACE?.trim() || env.KUBERNETES_NAMESPACE?.trim() || "wardby-coding";
256
+ const runtimeClassName = env.NATIVE_SANDBOX_RUNTIME_CLASS?.trim() || env.KUBERNETES_RUNTIME_CLASS?.trim() || undefined;
257
+ const context = env.KUBERNETES_CONTEXT?.trim() || undefined;
258
+ // The platform and run priority class are the coding launcher's settings, validated the same way.
259
+ const cluster = loadKubernetesJobConfig({
260
+ KUBERNETES_PLATFORM: env.KUBERNETES_PLATFORM,
261
+ KUBERNETES_RUN_PRIORITY_CLASS: env.NATIVE_SANDBOX_PRIORITY_CLASS?.trim() || env.KUBERNETES_RUN_PRIORITY_CLASS,
262
+ });
263
+ if (platformProfile(cluster.platform).requiresGvisor && runtimeClassName !== GVISOR_RUNTIME_CLASS) {
264
+ throw new Error(`KUBERNETES_PLATFORM=${cluster.platform} runs native workers only under gVisor: set NATIVE_SANDBOX_RUNTIME_CLASS (or KUBERNETES_RUNTIME_CLASS) to ${GVISOR_RUNTIME_CLASS} (found ${runtimeClassName ?? "unset"}).`);
265
+ }
266
+ return {
267
+ launcher,
268
+ ...common,
269
+ namespace,
270
+ gatewayService: env.NATIVE_GATEWAY_SERVICE?.trim() || DEFAULT_NATIVE_GATEWAY_SERVICE,
271
+ platform: cluster.platform,
272
+ ...(context ? { context } : {}),
273
+ ...(runtimeClassName ? { runtimeClassName } : {}),
274
+ ...optionalField("priorityClassName", cluster.priorityClassName),
275
+ ...optionalField("readyTimeoutMs", optionalBoundedInteger(env.NATIVE_SANDBOX_READY_TIMEOUT_MS, "NATIVE_SANDBOX_READY_TIMEOUT_MS", 1_000, 1_800_000)),
276
+ ...optionalField("enforcementTimeoutMs", optionalBoundedInteger(env.NATIVE_SANDBOX_ENFORCEMENT_TIMEOUT_MS, "NATIVE_SANDBOX_ENFORCEMENT_TIMEOUT_MS", 1_000, 600_000)),
277
+ };
278
+ }
279
+ const gatewayContainer = env.NATIVE_GATEWAY_CONTAINER?.trim();
280
+ if (!gatewayContainer)
281
+ throw new Error("NATIVE_GATEWAY_CONTAINER is required when NATIVE_SANDBOX_LAUNCHER=docker.");
282
+ return { launcher, ...common, gatewayContainer };
283
+ }
203
284
  export function loadShutdownDrainSeconds(env = process.env) {
204
285
  const value = env.SHUTDOWN_DRAIN_SECONDS?.trim();
205
286
  if (!value)
@@ -293,6 +374,7 @@ export function loadAuthConfig(env = process.env) {
293
374
  signingKey: env.AUTH_SIGNING_KEY,
294
375
  credentialHashKey: env.AUTH_CREDENTIAL_HASH_KEY,
295
376
  maxClients: env.AUTH_MAX_CLIENTS ? Number(env.AUTH_MAX_CLIENTS) : undefined,
377
+ mobile: loadMobileConfig(env),
296
378
  };
297
379
  }
298
380
  /** Read secret-cipher config (the app-key master key) from the environment. */
@@ -309,3 +391,7 @@ export function loadDbosConfig(env = process.env) {
309
391
  executorId: env.DBOS_EXECUTOR_ID ?? crypto.randomUUID(),
310
392
  };
311
393
  }
394
+ /** `{ [key]: value }` when value is set, else nothing: for spreading optional fields under exactOptionalPropertyTypes. */
395
+ function optionalField(key, value) {
396
+ return (value === undefined ? {} : { [key]: value });
397
+ }
package/dist/core/db.d.ts CHANGED
@@ -1,4 +1,6 @@
1
+ import type { PoolConfig } from "pg";
1
2
  import { PrismaClient } from "#prisma";
3
+ import { type DatabaseAuth, type PgAuthOptions } from "./pg-auth.js";
2
4
  /**
3
5
  * Connection-pool sizing for the node-postgres pool behind @prisma/adapter-pg.
4
6
  *
@@ -28,13 +30,21 @@ export interface PoolSettings {
28
30
  connectionTimeoutMillis: number;
29
31
  }
30
32
  export declare function poolSettings(connectionString: string): PoolSettings;
33
+ export interface PrismaClientOptions {
34
+ /** Overrides the pool size for a caller that sizes its own pools
35
+ * (the coding proxy keeps its ledger and its package registry apart). */
36
+ poolMax?: number;
37
+ /** Defaults to DATABASE_AUTH (see pg-auth.ts). */
38
+ auth?: DatabaseAuth;
39
+ /** Test seam for rds-iam (environment, token signer, CA file reader). */
40
+ pgAuth?: PgAuthOptions;
41
+ }
42
+ /** The pg pool config behind every Prisma client: auth (pg-auth.ts) plus pool sizing. */
43
+ export declare function prismaPgConfig(url: string, options?: PrismaClientOptions): PoolConfig;
31
44
  /**
32
45
  * The one way wardby builds a Prisma client. `url` defaults to DATABASE_URL
33
- * as read at call time.
46
+ * as read at call time. How it authenticates follows DATABASE_AUTH
47
+ * (password by default, or rds-iam; see pg-auth.ts).
34
48
  */
35
- /** `poolMax` overrides the pool size for a caller that sizes its own pools
36
- * (the coding proxy keeps its ledger and its package registry apart). */
37
- export declare function createPrismaClient(url?: string | undefined, options?: {
38
- poolMax?: number;
39
- }): PrismaClient;
49
+ export declare function createPrismaClient(url?: string | undefined, options?: PrismaClientOptions): PrismaClient;
40
50
  export declare const prisma: PrismaClient;
package/dist/core/db.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { availableParallelism } from "node:os";
2
2
  import { PrismaPg } from "@prisma/adapter-pg";
3
3
  import { PrismaClient } from "#prisma";
4
+ import { databaseAuthFromEnv, pgConnectionConfig } from "./pg-auth.js";
4
5
  /**
5
6
  * Connection-pool sizing for the node-postgres pool behind @prisma/adapter-pg.
6
7
  *
@@ -56,20 +57,21 @@ class MissingUrlAdapter extends PrismaPg {
56
57
  throw new Error("DATABASE_URL is not set: cannot connect to the database.");
57
58
  }
58
59
  }
60
+ /** The pg pool config behind every Prisma client: auth (pg-auth.ts) plus pool sizing. */
61
+ export function prismaPgConfig(url, options = {}) {
62
+ return {
63
+ ...pgConnectionConfig(url, options.auth ?? databaseAuthFromEnv(), options.pgAuth),
64
+ ...poolSettings(url),
65
+ ...(options.poolMax !== undefined ? { max: options.poolMax } : {}),
66
+ };
67
+ }
59
68
  /**
60
69
  * The one way wardby builds a Prisma client. `url` defaults to DATABASE_URL
61
- * as read at call time.
70
+ * as read at call time. How it authenticates follows DATABASE_AUTH
71
+ * (password by default, or rds-iam; see pg-auth.ts).
62
72
  */
63
- /** `poolMax` overrides the pool size for a caller that sizes its own pools
64
- * (the coding proxy keeps its ledger and its package registry apart). */
65
73
  export function createPrismaClient(url = process.env.DATABASE_URL, options = {}) {
66
- const adapter = url
67
- ? new PrismaPg({
68
- connectionString: url,
69
- ...poolSettings(url),
70
- ...(options.poolMax !== undefined ? { max: options.poolMax } : {}),
71
- })
72
- : new MissingUrlAdapter({ connectionString: "" });
74
+ const adapter = url ? new PrismaPg(prismaPgConfig(url, options)) : new MissingUrlAdapter({ connectionString: "" });
73
75
  return new PrismaClient({ adapter });
74
76
  }
75
77
  export const prisma = createPrismaClient();
@@ -28,6 +28,17 @@ export interface DispatchRunOptions {
28
28
  * exclusive with `codingBaseRef`. Coding agents only.
29
29
  */
30
30
  continuesCodingRunId?: string;
31
+ /**
32
+ * The step in the delegating agent's declared merge order for this coding
33
+ * run (1 = merge first; equal numbers mean no order between them). Integer
34
+ * 1-99, validated by the caller (the delegate_to_* tool's mergeOrder
35
+ * argument); throws `Error("invalid_merge_order")` otherwise. Coding agents
36
+ * only; ignored for a native agent (no CodingRun to store it on). A
37
+ * continuation (`continuesCodingRunId`) that omits this inherits the pull
38
+ * request's effective value: the newest non-null `mergeOrder` among the
39
+ * root coding run and its continuations.
40
+ */
41
+ mergeOrder?: number;
31
42
  now?: Date;
32
43
  lockAgent?: boolean;
33
44
  task?: {
@@ -227,8 +227,37 @@ ownerId) {
227
227
  headRef: root.headRef,
228
228
  continuationOf: { runId: root.runId },
229
229
  rootCodingRunId: root.runId,
230
+ mergeOrder: await effectiveMergeOrder(reader, root.runId),
230
231
  };
231
232
  }
233
+ /**
234
+ * The pull request's current merge step: the newest non-null mergeOrder
235
+ * (by run start) among the root coding run and every continuation of it
236
+ * (CodingRun.rootCodingRunId, indexed), or null when none set one. A
237
+ * continuation that omits mergeOrder inherits this, so an earlier
238
+ * continuation's override is kept rather than reverting to the root's.
239
+ */
240
+ async function effectiveMergeOrder(reader, rootRunId) {
241
+ const newest = await reader.codingRun.findFirst({
242
+ where: { OR: [{ runId: rootRunId }, { rootCodingRunId: rootRunId }], mergeOrder: { not: null } },
243
+ orderBy: [{ run: { startedAt: "desc" } }, { runId: "desc" }],
244
+ select: { mergeOrder: true },
245
+ });
246
+ return newest?.mergeOrder ?? null;
247
+ }
248
+ /**
249
+ * Resolves the CodingRun's mergeOrder: the caller's own value if given (an
250
+ * integer 1-99; anything else throws), else the inherited effective value
251
+ * (undefined for a fresh branch), else null (no order).
252
+ */
253
+ function resolveMergeOrder(options, inherited) {
254
+ if (options.mergeOrder === undefined)
255
+ return inherited ?? null;
256
+ if (!Number.isInteger(options.mergeOrder) || options.mergeOrder < 1 || options.mergeOrder > 99) {
257
+ throw new Error("invalid_merge_order");
258
+ }
259
+ return options.mergeOrder;
260
+ }
232
261
  /** Read failures that are the file's fault rather than the host's: reported as an invalid declaration. */
233
262
  const UNREADABLE_DECLARATION = {
234
263
  github_file_too_large: `it is larger than ${MAX_SERVICE_DECLARATION_BYTES} bytes`,
@@ -439,6 +468,8 @@ export async function dispatchRun(options) {
439
468
  agentId: agent.id,
440
469
  trigger: options.trigger ?? "manual",
441
470
  executionManaged: true,
471
+ // Fixed for the run's life: the executor routes on this, never on the agent's current setting.
472
+ nativeExecutionMode: agent.kind === "native" ? agent.nativeExecutionMode : null,
442
473
  parentRunId: options.parentRunId,
443
474
  grantedParentMemoryKeys: options.grantedParentMemoryKeys ?? [],
444
475
  taskOverride: options.taskOverride,
@@ -478,8 +509,9 @@ export async function dispatchRun(options) {
478
509
  const budgetUsd = codingBudget.budgetUsd;
479
510
  // Resolved once: before the transaction when the declaration was read from it.
480
511
  const branch = declarationRead?.branch ?? (await resolveCodingBranch(tx, options, agent.codingProfile, agent.ownerId));
481
- const { baseRef, continuationOf, rootCodingRunId } = branch;
512
+ const { baseRef, continuationOf, rootCodingRunId, mergeOrder: inheritedMergeOrder } = branch;
482
513
  const headRef = branch.headRef ?? `wardby/run-${run.id}`;
514
+ const mergeOrder = resolveMergeOrder(options, inheritedMergeOrder);
483
515
  const input = CodingTaskInputSchema.parse({
484
516
  schemaVersion: CODING_PROTOCOL_VERSION,
485
517
  runId: run.id,
@@ -523,8 +555,11 @@ export async function dispatchRun(options) {
523
555
  toolImage,
524
556
  budgetReservedUsd: budgetUsd,
525
557
  rootCodingRunId,
558
+ mergeOrder,
526
559
  workspaceDiskMb: agent.codingProfile.workspaceDiskMb,
527
560
  maxTurns: agent.codingProfile.maxTurns,
561
+ repoSkills: agent.codingProfile.repoSkills,
562
+ claudeBareMode: agent.codingProfile.claudeBareMode,
528
563
  // Fixed here so a run's tracing never changes mid-run.
529
564
  debugTrace: agent.codingProfile.debugTraceUntil != null &&
530
565
  now.getTime() < agent.codingProfile.debugTraceUntil.getTime(),
@@ -6,7 +6,7 @@
6
6
  * lives behind a seam (a future LangGraph engine swaps in here) and why
7
7
  * the budget guardrail must stay above whichever engine is running.
8
8
  */
9
- import { runStepInline } from "../providers/engine/types.js";
9
+ import { RunOwnershipLostError, runStepInline } from "../providers/engine/types.js";
10
10
  import { applyPreflightSafetyMargin, checkTokenCalibration, estimateInputCost, isOverBudget } from "./budget.js";
11
11
  import { logger } from "./logger.js";
12
12
  import { UNTRUSTED_CONTEXT_TAG, UNTRUSTED_TOOL_OUTPUT_TAG, wrapUntrusted } from "./untrusted-content.js";
@@ -197,11 +197,15 @@ export class NativeEngine {
197
197
  engineLog.info({ turn, toolCallIds: batch.map((i) => toolCalls[i].id) }, "running tool calls concurrently");
198
198
  const settled = await Promise.allSettled(batch.map((i) => runCall(i)));
199
199
  const rejections = settled.filter((outcome) => outcome.status === "rejected");
200
- for (const extra of rejections.slice(1)) {
201
- engineLog.warn({ turn, err: extra.reason }, "concurrent tool call also failed");
200
+ // An ownership loss wins over an ordinary failure: this attempt no longer owns the run, so it
201
+ // must stop without a terminal write rather than fail the run for the attempt that does.
202
+ const thrown = rejections.find((r) => r.reason instanceof RunOwnershipLostError) ?? rejections[0];
203
+ for (const extra of rejections) {
204
+ if (extra !== thrown)
205
+ engineLog.warn({ turn, err: extra.reason }, "concurrent tool call also failed");
202
206
  }
203
- if (rejections.length > 0)
204
- throw rejections[0].reason;
207
+ if (thrown)
208
+ throw thrown.reason;
205
209
  settled.forEach((outcome, position) => {
206
210
  results[batch[position]] = outcome.value;
207
211
  });
@@ -5,10 +5,10 @@
5
5
  * module decides which agents run. See
6
6
  * docs/private/2026-09-25-code-review-host-design.md §6.3.
7
7
  */
8
- import type { PrismaClient } from "#prisma";
8
+ import { type PrismaClient } from "#prisma";
9
9
  import type { Executor } from "../providers/executor/types.js";
10
10
  import type { IssueTrackerRegistry } from "../providers/issue-tracker/types.js";
11
- import type { HostEvent, ReviewHostRegistry } from "../providers/review-host/types.js";
11
+ import type { CodeReviewHost, HostEvent, ReviewHostRegistry } from "../providers/review-host/types.js";
12
12
  import { type OpenSibling } from "./related-pull-requests.js";
13
13
  import { type RepoAccessGate } from "./repo-access.js";
14
14
  import { type DriftScope } from "../knowledge/relevance.js";
@@ -84,12 +84,58 @@ export declare function siblingContinuationHints(siblings: readonly OpenSibling[
84
84
  */
85
85
  export declare function mentionTaskText(event: MentionEvent, siblings?: readonly OpenSibling[]): string;
86
86
  export declare function unknownPriorRunBody(priorRunId: string): string;
87
+ export interface ReviewTarget {
88
+ agentId: string;
89
+ checkName: string;
90
+ }
91
+ /** A review run's task: the head to review, plus any extra context its trigger adds. */
92
+ export declare function reviewTaskText(repository: string, prNumber: number, headSha: string, context?: {
93
+ task: string;
94
+ untrustedContext: string;
95
+ }): string;
96
+ /** Starts one review run per target on a pull request head; returns the started run ids. */
97
+ export declare function startReviews(deps: ReviewStartDeps, host: CodeReviewHost, repository: string, prNumber: number, headSha: string, targets: ReviewTarget[],
98
+ /** True for automatic triggers; an explicit re-run or `@wardby review` always runs. */
99
+ skipReviewedCommits?: boolean,
100
+ /**
101
+ * Extra context for this review: `task` (written by wardby, trusted) is appended to the
102
+ * review task; `untrustedContext` (anything a model or a person wrote) travels separately,
103
+ * delivered as data.
104
+ */
105
+ context?: {
106
+ task: string;
107
+ untrustedContext: string;
108
+ }): Promise<string[]>;
87
109
  /**
88
110
  * The reconciler's fallback for deferred reviews: drops rows older than
89
111
  * DEFERRED_REVIEW_MAX_AGE_MS, and starts (regardless of CI) those waiting
90
112
  * longer than DEFERRED_REVIEW_MAX_WAIT_MS, at most DEFERRED_REVIEW_BATCH per
91
113
  * pass, oldest first. Covers CI that never reported, ran very long, or whose
92
- * completion webhook was missed. Never throws; returns the runs started.
114
+ * completion webhook was missed. Rows waiting for a delegating run (reason
115
+ * "request") are released the same way once they are that old, but only when
116
+ * that run is terminal (or gone): a lead the reconciler marked lost never
117
+ * reaches its finalizer. Never throws; returns the runs started.
93
118
  */
94
119
  export declare function startDeferredReviews(deps: ReviewStartDeps, now?: Date): Promise<string[]>;
120
+ /**
121
+ * The run whose finish a review of this pull request waits for: the root of
122
+ * the run tree when the PR's opening coding run was delegated (it has a
123
+ * parent run) and that root is not terminal yet; otherwise null (a
124
+ * non-delegated coding PR, a finished request, or an unknown marker). A lead's
125
+ * delegate_to_* calls block until each child is terminal, so a terminal root
126
+ * means every sibling coding run of the request has finished and opened its
127
+ * pull request. A continuation's marker resolves to its root coding run, then
128
+ * up that run's parents. `repository`, when given, must be the coding run's.
129
+ */
130
+ export declare function requestLeadRunId(db: Pick<HostEventDb, "$queryRaw">, prMarkerRunId: string, repository?: string): Promise<string | null>;
131
+ /**
132
+ * The delegating run `leadRunId` is terminal (it succeeded, failed, or was
133
+ * cancelled): releases every review that waited for it. A reviewer linked
134
+ * with waitForCi whose head's CI is still pending is handed to CI (the row
135
+ * becomes reason "ci", its max wait counted from now); every other row is
136
+ * started (claimed by delete, so concurrent releases start each review once).
137
+ * Called from the lead's finalizer, after the related pull requests sections
138
+ * were rewritten. Never throws.
139
+ */
140
+ export declare function startDeferredForRequest(deps: ReviewStartDeps, leadRunId: string): Promise<void>;
95
141
  export declare function routeHostEvent(event: HostEvent, deps: RouteHostEventDeps): Promise<RouteResult>;