@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,635 @@
1
+ # Native sandbox
2
+
3
+ > **Requires Wardby 0.5.4 or later.**
4
+
5
+ By default a native agent runs inside the Wardby server process: its turn loop
6
+ and any user tool code you attach execute next to the database connection, the
7
+ LLM credentials, and every other agent's runs. **Sandbox mode** moves the whole
8
+ turn loop and the agent's user tools into a single-use worker that holds no
9
+ credentials, no database access, no container-runtime access, and no network
10
+ except one path to a separate **native gateway**. The worker is a Docker
11
+ container or, on a Kubernetes cluster, a pod; you choose with
12
+ `NATIVE_SANDBOX_LAUNCHER`.
13
+
14
+ Use it when a native agent runs tool code or prompts you do not fully trust, for
15
+ example tools written by people other than the operators, or agents that fetch
16
+ and process untrusted content. Leave agents in the default `control-plane` mode
17
+ when you trust the tools and want the simplest deployment.
18
+
19
+ Sandbox mode applies to native agents only. Coding agents have their own
20
+ isolation model; see [Coding-worker isolation](coding-worker-isolation.md).
21
+
22
+ ## What is isolated and what is not
23
+
24
+ | Component | Trusted? | Where it runs |
25
+ | ------------------------- | -------- | ---------------------------------------- |
26
+ | Wardby server / scheduler | Yes | Your host, VM, or cluster |
27
+ | Native gateway | Yes | A separate container or Deployment |
28
+ | Postgres, LLM provider | Yes | Reachable only from server/gateway |
29
+ | Agent turn loop (worker) | No | Single-use container or pod, one per run |
30
+ | User tool code (worker) | No | The same worker |
31
+
32
+ The Docker worker container:
33
+
34
+ - has no LLM keys, secrets, database URL, or Docker socket, and receives only a
35
+ run-scoped capability on its standard input (never in its environment,
36
+ arguments, labels, or `docker inspect` output);
37
+ - runs as non-root (uid 10001) on a read-only root filesystem with a
38
+ size-capped `/tmp` (no exec), all Linux capabilities dropped,
39
+ `no-new-privileges`, and CPU, memory, and PID limits;
40
+ - sits on an internal Docker network created for that run alone, shared only
41
+ with the gateway container, with no route to the internet, the host, or any
42
+ other run.
43
+
44
+ On Kubernetes the worker is a pod with the same posture; see
45
+ [Set up with Kubernetes](#set-up-with-kubernetes) for what the pod and its
46
+ NetworkPolicy contain and how the isolation is proven before the run starts.
47
+
48
+ Everything the worker needs from the outside world (model calls, the built-in
49
+ tools, and tool access to datastores, secrets, and outbound fetch) goes through
50
+ the gateway, which enforces each tool attachment's grants. A worker can only
51
+ ask for what the agent's attachments already allow.
52
+
53
+ Containers are defense in depth, not a virtual-machine boundary. On Kubernetes,
54
+ set a sandboxing runtime class such as gVisor where you have one. Run the Docker
55
+ host on a machine that holds no production credentials beyond what the server
56
+ and gateway need.
57
+
58
+ ## Topology (Docker)
59
+
60
+ ```
61
+ Docker host
62
+ +---------------------------------------------------------+
63
+ | wardby serve (server + scheduler) |
64
+ | | docker run / network create / network connect |
65
+ | v |
66
+ | per-run internal network (no external route) |
67
+ | +--------------------+ +--------------------+ |
68
+ | | native worker | HTTP | native gateway | |
69
+ | | (no credentials) +------->| wardby-native- |--+--> LLM provider
70
+ | | single-use | | gateway :8790 |--+--> Postgres
71
+ | +--------------------+ | (credentials, DB) | |
72
+ | +--------------------+ |
73
+ +---------------------------------------------------------+
74
+ ```
75
+
76
+ For each run the server creates a new internal network, connects the gateway
77
+ container to it under the alias `wardby-native-gateway`, and starts the worker
78
+ on it. When the run ends the worker, the network, and the gateway's membership
79
+ of it are removed.
80
+
81
+ The gateway is stateless: all state is in Postgres. You can restart it while
82
+ runs are in flight; workers retry their calls. It must not be given the Docker
83
+ socket, because workers can reach it.
84
+
85
+ ## Requirements
86
+
87
+ - **Docker launcher:** Docker on the host that runs `wardby serve` (or
88
+ `wardby scheduler`). The server launches workers with the `docker` CLI, so its
89
+ user must be able to use the Docker daemon. A running native gateway container
90
+ (below) on the same Docker host, with access to the same Postgres database as
91
+ the server.
92
+ - **Kubernetes launcher:** a cluster whose CNI enforces `NetworkPolicy`, a
93
+ namespace holding the native gateway, and a server identity with the
94
+ permissions in [Server permissions](#server-permissions).
95
+ - The `wardby-native-worker` image, pinned by digest (Docker also accepts a
96
+ local image id; Kubernetes requires a registry digest).
97
+ - A running `wardby serve` or `wardby scheduler`. Sub-agents of a sandboxed run
98
+ are started by the scheduler leader (see [Sub-agents](#sub-agents)).
99
+
100
+ ## Set up with Docker
101
+
102
+ ### 1. Choose the worker image
103
+
104
+ Releases publish the image to the GitHub Container Registry as
105
+ `ghcr.io/<org>/wardby-native-worker`. The exact digest-pinned reference for the
106
+ installed version is listed as `nativeWorker` in `dist/quickstart-images.json`
107
+ inside the npm package. Use that reference as-is:
108
+
109
+ ```dotenv
110
+ NATIVE_SANDBOX_WORKER_IMAGE=ghcr.io/your-org/wardby-native-worker@sha256:replace-with-digest
111
+ ```
112
+
113
+ The image must be immutable: a `repo@sha256:...` digest or a local image id.
114
+ A mutable tag such as `:latest` is refused with
115
+ [`native_sandbox_image_not_pinned`](../help/errors/native-sandbox-image-not-pinned.md).
116
+
117
+ To build it yourself from a repository checkout:
118
+
119
+ ```bash
120
+ npm run native-worker:image:local
121
+ docker image inspect --format '{{.Id}}' wardby-native-worker:local
122
+ ```
123
+
124
+ Use the printed `sha256:...` image id as `NATIVE_SANDBOX_WORKER_IMAGE`. A local
125
+ id must already exist on the Docker host; a digest reference is pulled once if
126
+ missing.
127
+
128
+ ### 2. Run the native gateway
129
+
130
+ The gateway is the same Wardby build as the server, started with the
131
+ `native-gateway` command. It needs the server's database, LLM, secrets, and
132
+ integration settings, and **no Docker access**.
133
+
134
+ | Gateway variable | Purpose |
135
+ | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
136
+ | `NATIVE_GATEWAY_LISTEN` | `host:port` to listen on. Default `0.0.0.0:8790`. |
137
+ | `DATABASE_URL` | The same database the server uses. With `DATABASE_AUTH=rds-iam` the gateway also needs `DATABASE_AUTH`, `AWS_REGION` (or `DATABASE_AWS_REGION`) and the CA bundle file named by `sslrootcert` ([details](security-deployment.md#database-authentication)). |
138
+ | LLM provider keys | The same provider settings as the server (for example `OPENAI_API_KEY`). |
139
+ | `SECRET_APP_KEY` | Same value as the server, so tools can read their bound secrets or send brokered ones ([details](tool-secrets.md)). |
140
+ | Integration settings | The same repository-host and issue-tracker settings as the server. |
141
+
142
+ If sandboxed agents delegate to coding agents, give the gateway the same
143
+ coding image settings as the server (for example `CODING_WORKER_IMAGE`); the
144
+ gateway only resolves them, it never starts coding workers.
145
+
146
+ For local development, the repository ships a compose overlay
147
+ (`deploy/local/docker-compose.native-sandbox.yml`) that runs the gateway next to
148
+ the local Postgres from `deploy/local/docker-compose.yml`:
149
+
150
+ ```bash
151
+ docker compose -f deploy/local/docker-compose.yml -f deploy/local/docker-compose.native-sandbox.yml --env-file .env.local up -d --build native-gateway
152
+ ```
153
+
154
+ The overlay names the container `wardby-native-gateway`, passes the database
155
+ URL for the compose network and your LLM keys from the environment file. It
156
+ runs with a read-only root filesystem and all capabilities dropped, and mounts
157
+ no Docker socket.
158
+ It defines its own health check against `/healthz`.
159
+
160
+ For other deployments, run `wardby native-gateway` in a container built from
161
+ the same image as the server, on the same Docker host, with any container
162
+ name you choose, and give that name to the server in the next step. Do not
163
+ publish its port to the internet; only the per-run networks and your own
164
+ health checks need to reach it.
165
+
166
+ ### 3. Configure the server
167
+
168
+ Add these to the server's environment (`wardby serve` or `wardby scheduler`),
169
+ then restart it:
170
+
171
+ ```dotenv
172
+ NATIVE_SANDBOX_LAUNCHER=docker
173
+ NATIVE_SANDBOX_WORKER_IMAGE=ghcr.io/your-org/wardby-native-worker@sha256:replace-with-digest
174
+ NATIVE_GATEWAY_CONTAINER=wardby-native-gateway
175
+ ```
176
+
177
+ See [Configuration reference](#configuration-reference) for every variable,
178
+ including the Kubernetes ones. A missing required variable, or a launcher other
179
+ than `docker` or `kubernetes`, stops the server at startup with a message naming the variable. When
180
+ `NATIVE_SANDBOX_LAUNCHER` is unset, sandbox mode is off and any sandbox-mode
181
+ run fails closed (see [Failure modes](#failure-modes)).
182
+
183
+ `JOB_LAUNCHER` (coding agents) is a separate setting; you can enable either,
184
+ both, or neither.
185
+
186
+ ### 4. Set an agent's mode
187
+
188
+ Each native agent has a `nativeExecutionMode` setting:
189
+
190
+ - `control-plane` (default): the run executes in the server process.
191
+ - `sandbox`: the run executes in a worker container as described above.
192
+
193
+ Set it with the MCP tools `create_agent` or `update_agent` (field
194
+ `nativeExecutionMode`, native agents only), or in an import bundle. Setting
195
+ `sandbox` is refused while the server has no native sandbox configured.
196
+
197
+ From the command line, with the same environment as the server:
198
+
199
+ ```bash
200
+ wardby agent create --name <n> --model <m> --prompt <p> --budget <usd> --native-execution-mode sandbox
201
+ ```
202
+
203
+ ```bash
204
+ wardby agent mode <name> sandbox
205
+ ```
206
+
207
+ The CLI refuses `sandbox` with `native_sandbox_unavailable` when its own
208
+ environment has no `NATIVE_SANDBOX_LAUNCHER`, so run it where the server's
209
+ sandbox variables are set. `wardby agent list` marks sandbox-mode agents.
210
+ `get_run` and `list_runs` report each run's mode as `nativeExecutionMode`
211
+ (`control-plane`, `sandbox`, or `null` for a coding run).
212
+
213
+ Each run keeps the mode it started with. Changing an agent affects only the
214
+ runs that start afterwards.
215
+
216
+ ## Set up with Kubernetes
217
+
218
+ With `NATIVE_SANDBOX_LAUNCHER=kubernetes` the server starts one pod per
219
+ sandbox run in a namespace you choose and deletes it when the run ends. The
220
+ native gateway runs in the same namespace as an ordinary Deployment.
221
+
222
+ ```
223
+ cluster namespace
224
+ +---------------------------------------------------------------+
225
+ | wardby serve --create pod / Secret / NetworkPolicy--> |
226
+ | |
227
+ | native-run pod (no credentials) native gateway pods |
228
+ | +---------------------------+ 8790 +-------------------+ |
229
+ | | worker, single-use +------->| wardby-native- |--+--> LLM provider
230
+ | | egress: gateway:8790 only | x | gateway Service |--+--> Postgres
231
+ | +---------------------------+ 8791 +-------------------+ |
232
+ +---------------------------------------------------------------+
233
+ ```
234
+
235
+ ### 1. Choose the worker image
236
+
237
+ Use the digest-pinned `nativeWorker` reference from
238
+ `dist/quickstart-images.json`, or push your own build to a registry your
239
+ cluster can pull from. The reference must be a registry digest,
240
+ `repo@sha256:...`. A local image id means nothing to a cluster, and a
241
+ mutable tag is refused; both stop the server at startup or fail the run with
242
+ [`native_sandbox_image_not_pinned`](../help/errors/native-sandbox-image-not-pinned.md).
243
+
244
+ ### 2. Deploy the native gateway
245
+
246
+ Run `wardby native-gateway` as a Deployment in the run namespace, from the
247
+ same Wardby image as the server. Requirements:
248
+
249
+ - **Environment:** the server's `DATABASE_URL`, `SECRET_APP_KEY` (so tools can
250
+ read their bound secrets or send brokered ones; see
251
+ [tool-secrets.md](tool-secrets.md)), the LLM provider keys, and any integration or
252
+ coding image settings the server has (see the gateway table in the Docker
253
+ setup above). Also `NATIVE_GATEWAY_LISTEN` (default `0.0.0.0:8790`) and
254
+ `NATIVE_GATEWAY_DENY_PORT` (default `8791`). Keep these in a Secret.
255
+ - **No Kubernetes access:** `automountServiceAccountToken: false` and no Role
256
+ or RoleBinding. Workers can reach the gateway, so it must not be able to
257
+ touch the cluster. A read-only root filesystem with a small `/tmp`,
258
+ all capabilities dropped, and a non-root user all apply.
259
+ - **Pod label:** `app.kubernetes.io/name: wardby-native-gateway`. The per-run
260
+ NetworkPolicy selects the gateway pods by this label.
261
+ - **A ClusterIP Service** named `wardby-native-gateway` (or set
262
+ `NATIVE_GATEWAY_SERVICE`) exposing **both** port 8790 and the deny port 8791. Nothing is served on 8791; it exists only so isolation can be proven.
263
+ - **A NetworkPolicy on the gateway pods** that admits pods labeled
264
+ `wardby.io/component: native-run` on **both 8790 and 8791**, with egress to
265
+ cluster DNS, the model providers (HTTPS), and the database. Do not drop the
266
+ 8791 rule: the check that a worker cannot reach 8791 only means something if
267
+ 8791 is open at the destination, so that the worker's own policy is the only
268
+ thing that can block it. Without it, a worker whose policy was never
269
+ programmed would read the port as blocked and its isolation could be
270
+ wrongly proven.
271
+ - Replicas are fine; the gateway is stateless.
272
+
273
+ The gateway ships in every target's manifests: the Deployment, Service, and
274
+ NetworkPolicy are in `deploy/kind-coding/manifests/base/native-gateway.yaml`,
275
+ and each overlay (`kind`, `gke-autopilot`) supplies its own database egress and
276
+ the `wardby-native-gateway-env` Secret. On a cluster you manage yourself, copy
277
+ that file and adapt it. On GKE Autopilot the gateway also gets its own Workload
278
+ Identity and Cloud SQL database user; see
279
+ [Getting started on GKE](getting-started-gke.md#native-sandbox).
280
+
281
+ ### 3. Server permissions
282
+
283
+ The server (`wardby serve` or `wardby scheduler`) needs a Role in the run
284
+ namespace that allows:
285
+
286
+ | Resource | Verbs |
287
+ | ----------------------------------- | --------------------------------- |
288
+ | `pods` | `create`, `get`, `delete`, `list` |
289
+ | `pods/exec` | `create`, `get` |
290
+ | `secrets` | `create`, `delete` |
291
+ | `networking.k8s.io/networkpolicies` | `create`, `get`, `delete` |
292
+ | `services` | `get` |
293
+
294
+ `list` on pods lets the startup sweep find leftover worker pods by label;
295
+ `pods/exec` is how the server proves isolation from inside the pod; `services`
296
+ is read once to find the gateway's ClusterIP. Coding runs on Kubernetes use a
297
+ largely overlapping Role (`deploy/kind-coding/manifests/base/launcher-role.yaml`).
298
+
299
+ ### 4. Configure the server
300
+
301
+ ```dotenv
302
+ NATIVE_SANDBOX_LAUNCHER=kubernetes
303
+ NATIVE_SANDBOX_WORKER_IMAGE=registry.example.com/wardby-native-worker@sha256:replace-with-digest
304
+ NATIVE_SANDBOX_NAMESPACE=wardby
305
+ ```
306
+
307
+ Optional: `KUBERNETES_CONTEXT` (the kubeconfig context; unset uses the
308
+ in-cluster identity or the current context), `NATIVE_SANDBOX_RUNTIME_CLASS`
309
+ (for example a gVisor class), `NATIVE_GATEWAY_SERVICE`, the CPU and memory
310
+ limits, `NATIVE_SANDBOX_MAX_CONCURRENT`, `NATIVE_SANDBOX_PRIORITY_CLASS`, and
311
+ the two start-up time limits. See [Configuration reference](#configuration-reference). Then set
312
+ agents to `sandbox` mode as described in
313
+ [Set an agent's mode](#4-set-an-agents-mode).
314
+
315
+ ### What each run gets
316
+
317
+ For every run the server creates, then deletes when the run ends:
318
+
319
+ - A **Secret** holding the run's input and capability, immutable and mounted
320
+ read-only into the pod. The capability is never in the pod's environment,
321
+ arguments, or labels.
322
+ - A **NetworkPolicy** selecting only that pod: no ingress, and egress only to
323
+ the gateway pods on port 8790.
324
+ - The **pod**, with no service-account token, non-root (uid and fsGroup
325
+ 10001), a read-only root filesystem with a 64 MiB `/tmp`, all capabilities
326
+ dropped, no privilege escalation, the runtime-default seccomp profile, CPU
327
+ and memory requests equal to limits, an optional priority class
328
+ (`NATIVE_SANDBOX_PRIORITY_CLASS`), and `activeDeadlineSeconds` set to the
329
+ run's deadline. Kubernetes has no per-pod process limit, so
330
+ `NATIVE_SANDBOX_PIDS` does not apply.
331
+
332
+ Workers dial the gateway Service's ClusterIP, so they need no DNS egress. Set
333
+ `NATIVE_GATEWAY_URL` only to override that.
334
+
335
+ ### How isolation is proven
336
+
337
+ A NetworkPolicy takes effect a few seconds after its pod starts, so the run
338
+ does not begin until the server has proven the isolation. A new sandbox run
339
+ starts **not ready**: the gateway refuses every call from it with a
340
+ `not_ready` response, and the worker keeps retrying. Meanwhile the server:
341
+
342
+ 1. Reads back the pod and NetworkPolicy it created and compares them with what
343
+ it built. If an admission controller or mutating webhook changed the
344
+ security-relevant parts (service-account token, host networking, runtime
345
+ class, priority class, image, container resources and security context,
346
+ extra containers, or the policy),
347
+ the run fails with
348
+ [`native_sandbox_isolation_mismatch`](../help/errors/native-sandbox-isolation-mismatch.md).
349
+ 2. Waits for the pod to run, then executes a short probe inside it. The probe
350
+ must reach the gateway on 8790, must fail to reach the gateway's deny port
351
+ 8791 at the same address, and must fail to reach an outside address or any
352
+ cloud metadata endpoint: `169.254.169.254:80`, the EKS Pod Identity agent
353
+ (`169.254.170.23:80` and its IPv6 address `[fd00:ec2::23]:80`) and EC2's IPv6
354
+ metadata address `[fd00:ec2::254]:80`.
355
+ 3. Only when the probe passes does it mark the run ready. The gateway then
356
+ accepts the worker's calls.
357
+
358
+ If the probe cannot pass within its time limit the run fails with
359
+ [`native_sandbox_network_unenforced`](../help/errors/native-sandbox-network-unenforced.md)
360
+ and the pod is removed. What operators see while this happens: a run that
361
+ stays in its starting state for a few seconds (longer on a cold image pull),
362
+ and nothing in the model or tool logs until it is ready. A cluster whose CNI
363
+ does not enforce NetworkPolicy fails every sandbox run this way, by design.
364
+
365
+ ### Managed Kubernetes platforms
366
+
367
+ Set `KUBERNETES_PLATFORM=eks` on Amazon EKS clusters that have a gVisor node
368
+ group. It requires the same gVisor runtime class as `gke-autopilot` (the server
369
+ refuses to start unless `NATIVE_SANDBOX_RUNTIME_CLASS` or
370
+ `KUBERNETES_RUNTIME_CLASS` is `gvisor`) and expects a RuntimeClass `gvisor`
371
+ (handler `runsc`) whose scheduling block selects nodes labelled
372
+ `wardby.dev/sandbox=gvisor` and tolerates their `sandbox=gvisor:NoSchedule`
373
+ taint.
374
+
375
+ Set `KUBERNETES_PLATFORM=gke-autopilot` on GKE Autopilot. It applies to native
376
+ worker pods as well as coding pods: pod resources are conformed to Autopilot's
377
+ rules (CPU in 250m steps, 1 to 6.5 GiB of memory per vCPU) so admission changes
378
+ nothing, and the attestation above then passes. Autopilot also requires a
379
+ sandboxing runtime: with this platform the server refuses to start unless
380
+ `NATIVE_SANDBOX_RUNTIME_CLASS` (or `KUBERNETES_RUNTIME_CLASS`) is `gvisor`
381
+ ("runs native workers only under gVisor"). A cold gVisor node can take about
382
+ two minutes to come up, so raise `NATIVE_SANDBOX_READY_TIMEOUT_MS` (for example
383
+ to `600000`).
384
+
385
+ ### Capacity
386
+
387
+ `NATIVE_SANDBOX_MAX_CONCURRENT` caps how many sandbox runs may be active at
388
+ once across all server replicas (it counts active gateway sessions). A start
389
+ past the cap fails immediately with
390
+ [`native_sandbox_capacity`](../help/errors/native-sandbox-capacity.md); it does
391
+ not queue. The same code is returned when the namespace's ResourceQuota has no
392
+ room for another worker pod. Raise the ResourceQuota together with
393
+ `NATIVE_SANDBOX_MAX_CONCURRENT` (and `CODING_MAX_CONCURRENT`, which shares the
394
+ namespace): each worker pod needs its CPU and memory limits from the quota.
395
+
396
+ ## Warm pool
397
+
398
+ By default every sandbox run starts its own worker, so a run waits for the
399
+ worker to be created and, on Kubernetes, for pod scheduling, the image pull, and
400
+ the in-pod isolation proof. The warm pool keeps a few idle, already-isolated
401
+ workers ready so a run can take one instead. It works with both
402
+ `NATIVE_SANDBOX_LAUNCHER=docker` and `kubernetes`.
403
+
404
+ ### Enable it
405
+
406
+ Set `NATIVE_SANDBOX_WARM_POOL_SIZE` on the server to the number of idle workers
407
+ to keep (0 to 50; the default `0` means no pool and every run cold-starts as
408
+ before). `NATIVE_SANDBOX_WARM_MAX_AGE_MS` (60000 to 21600000, default `1800000`,
409
+ 30 minutes) is how long an idle worker may wait: an older one is replaced and
410
+ never claimed. A pool worker that nobody claims also exits by itself five
411
+ minutes after its max age.
412
+
413
+ The worker image must support pool workers: rebuild or update
414
+ `wardby-native-worker` with the release that adds the pool. An older image still
415
+ runs cold runs but cannot be a pool worker. The release adds a database
416
+ migration (the `NativeWarmWorker` table); apply it with `prisma migrate deploy`
417
+ as with any upgrade.
418
+
419
+ ### Sizing and idle cost
420
+
421
+ `NATIVE_SANDBOX_MAX_CONCURRENT` still counts only active runs. Idle pool workers
422
+ are extra running containers or pods on top of it. On Kubernetes the namespace
423
+ ResourceQuota must have room for `NATIVE_SANDBOX_MAX_CONCURRENT` plus
424
+ `NATIVE_SANDBOX_WARM_POOL_SIZE` worker pods, plus the gateway and any coding
425
+ runs. A pool worker that does not fit is skipped and logged; it never fails a
426
+ run.
427
+
428
+ Each idle worker reserves its CPU and memory continuously. On GKE Autopilot you
429
+ pay for each idle pod's requests (500m and 512Mi in the reference overlay). In
430
+ return, a claimed run skips pod scheduling, the image pull, node boot, and the
431
+ in-pod isolation probe; on Autopilot a cold gVisor node can take about two
432
+ minutes to come up. Start with a small size and raise it if runs still
433
+ cold-start under load.
434
+
435
+ ### How claims keep the isolation guarantees
436
+
437
+ - A pool worker is started and its isolation proven before any run exists (on
438
+ Kubernetes it is attested and probed from inside the pod, the same as a cold
439
+ pod). It holds no run data and no gateway capability.
440
+ - A run claims an idle worker atomically through the database, so claims are
441
+ safe across server replicas and processes. A worker is never given to two
442
+ runs.
443
+ - The run's input, including its one-time gateway capability, is delivered by
444
+ exec over stdin only after the claim. It never travels in the environment,
445
+ the command line, labels, or a Kubernetes Secret.
446
+ - At claim the worker is re-checked: still running, and the pod and
447
+ NetworkPolicy still as built (Docker: attached only to its own network). It is
448
+ not probed again; the max age bounds how old the isolation proof can be.
449
+ - The worker is single-use and destroyed after the run, exactly like a cold one.
450
+
451
+ ### Fallback
452
+
453
+ - No idle worker, or the claimed one fails the re-check: the run cold-launches
454
+ as if there were no pool.
455
+ - Delivery fails: the worker is removed and, once it is confirmed gone, the run
456
+ cold-launches. If it cannot be confirmed gone within 30 seconds, the run fails
457
+ with
458
+ [`native_sandbox_warm_delivery_failed`](../help/errors/native-sandbox-warm-delivery-failed.md)
459
+ rather than risk a second worker holding the same capability.
460
+
461
+ ### Upkeep
462
+
463
+ Every `wardby serve`, `wardby scheduler`, and `wardby mcp` process with the
464
+ sandbox configured keeps the pool up every 15 seconds. They coordinate through
465
+ the database, so running several never overfills it. Each pass starts workers up
466
+ to the size, retires idle workers that are older than the max age, gone, or over
467
+ the size (oldest first), and removes leftovers. Changing the worker image, CPU or
468
+ memory, runtime class, priority class, platform, namespace, gateway, or max age
469
+ retires all idle workers of the old configuration automatically, about a
470
+ minute after they started (a grace that keeps two replicas from retiring each
471
+ other's workers mid rolling update); they are never claimed meanwhile. Lowering the
472
+ size to `0` retires leftover pool workers within a pass, including any that a
473
+ replica still running the old size refills during a rolling update. A short-lived
474
+ command such as `wardby run` claims workers but does not warm new ones.
475
+
476
+ ### Naming, labels, and logs
477
+
478
+ Pool workers are named `wardby-nwarm-<token>`. Kubernetes pods carry
479
+ `wardby.io/pool=warm` and `wardby.io/warm-worker=<token>`, and still carry
480
+ `wardby.io/component=native-run`, so the gateway NetworkPolicy admits them.
481
+ Docker containers carry `io.wardby.pool=warm`. No new RBAC is needed: the
482
+ permissions in [Server permissions](#3-server-permissions) already cover it.
483
+
484
+ List idle workers with
485
+ `kubectl get pods -n <namespace> -l wardby.io/pool=warm` or
486
+ `docker ps --filter label=io.wardby.pool=warm`.
487
+
488
+ Pool logs use the module `native-warm-pool`, with an `event` field:
489
+
490
+ | `event` | Meaning |
491
+ | ---------- | ------------------------------------------------------------------------------ |
492
+ | `warmed` | A pool worker became ready (with the time it took, in ms). |
493
+ | `claimed` | A run took an idle worker. |
494
+ | `miss` | No idle worker was available; the run cold-launched. |
495
+ | `fallback` | A claimed worker failed the re-check or delivery; see the run's outcome above. |
496
+ | `retired` | An idle worker was replaced (with the reason: `stale`, `gone`, or `excess`). |
497
+ | `reaped` | An abandoned or orphaned pool worker was removed. |
498
+
499
+ If runs keep logging `miss`, the pool is smaller than demand, workers cannot be
500
+ started (look for "starting a warm worker failed", and check the ResourceQuota),
501
+ or the server running upkeep is not up.
502
+
503
+ ## Configuration reference
504
+
505
+ | Variable | Launcher | Required | Default | Purpose |
506
+ | --------------------------------------- | ---------- | -------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
507
+ | `NATIVE_SANDBOX_LAUNCHER` | both | Yes | unset (sandbox off) | `docker` or `kubernetes`. Independent of `JOB_LAUNCHER`. |
508
+ | `NATIVE_SANDBOX_WORKER_IMAGE` | both | Yes | | Pinned worker image. Docker: `repo@sha256:...` or a local image id. Kubernetes: a registry digest `repo@sha256:...` only. |
509
+ | `NATIVE_GATEWAY_CONTAINER` | Docker | Yes | | Name of the running gateway container the server connects to each run's network. |
510
+ | `NATIVE_SANDBOX_NAMESPACE` | Kubernetes | No | `KUBERNETES_NAMESPACE`, else `wardby-coding` | Namespace for run pods and the gateway. |
511
+ | `KUBERNETES_CONTEXT` | Kubernetes | No | in-cluster or current context | Kubeconfig context the server uses. |
512
+ | `NATIVE_SANDBOX_RUNTIME_CLASS` | Kubernetes | No | `KUBERNETES_RUNTIME_CLASS`, else none | RuntimeClass for run pods (for example gVisor). |
513
+ | `NATIVE_GATEWAY_SERVICE` | Kubernetes | No | `wardby-native-gateway` | Name of the gateway Service in the run namespace. |
514
+ | `NATIVE_GATEWAY_URL` | both | No | Docker: `http://wardby-native-gateway:8790/native-gateway/v1/call`; Kubernetes: the Service's ClusterIP on 8790 | What workers dial. Change only if the gateway is reachable another way. Must be an `http(s)` URL. |
515
+ | `NATIVE_SANDBOX_CPUS` | both | No | `1` | CPU limit per worker (Kubernetes: request and limit). |
516
+ | `NATIVE_SANDBOX_MEMORY_MB` | both | No | `512` | Memory limit per worker (Docker: swap disabled; Kubernetes: request and limit). |
517
+ | `NATIVE_SANDBOX_PIDS` | Docker | No | `128` | Process limit per worker. Docker only: Kubernetes has no per-pod process limit. |
518
+ | `NATIVE_SANDBOX_MAX_CONCURRENT` | both | No | unset (no cap) | At most this many sandbox runs at once across all replicas (counts active gateway sessions). A start past it fails with `native_sandbox_capacity`. |
519
+ | `NATIVE_SANDBOX_WARM_POOL_SIZE` | both | No | `0` (no pool; range 0-50) | Idle, already-isolated workers kept ready for runs to claim. See [Warm pool](#warm-pool). On Kubernetes, size the ResourceQuota for this many extra pods. Must be `0` when `WARDBY_LOW_POWER=1`. |
520
+ | `NATIVE_SANDBOX_WARM_MAX_AGE_MS` | both | No | `1800000` (range 60000-21600000) | An idle pool worker older than this is replaced and never claimed. |
521
+ | `NATIVE_SANDBOX_READY_TIMEOUT_MS` | Kubernetes | No | `120000` (range 1000-1800000) | How long a worker pod may take to start running. Use about `600000` on GKE Autopilot, where a cold gVisor node takes about two minutes. |
522
+ | `NATIVE_SANDBOX_ENFORCEMENT_TIMEOUT_MS` | Kubernetes | No | `30000` (range 1000-600000) | How long the in-pod isolation proof may take. |
523
+ | `NATIVE_SANDBOX_PRIORITY_CLASS` | Kubernetes | No | `KUBERNETES_RUN_PRIORITY_CLASS`, else none | PriorityClass for worker pods. Must not be a `system-` class. |
524
+ | `KUBERNETES_PLATFORM` | Kubernetes | No | none | `gke-autopilot` conforms worker pod resources to Autopilot's rules and requires the gVisor runtime class; `eks` targets EKS with a gVisor node group and also requires it. Shared with coding runs. |
525
+
526
+ Gateway-side variables (`NATIVE_GATEWAY_LISTEN`, `NATIVE_GATEWAY_DENY_PORT`) are
527
+ set on the gateway, not the server.
528
+
529
+ ## Limits and deadline
530
+
531
+ - Each worker is limited by `NATIVE_SANDBOX_CPUS`, `NATIVE_SANDBOX_MEMORY_MB`,
532
+ and (Docker only) `NATIVE_SANDBOX_PIDS`, with a 64 MiB `/tmp`. Raise the
533
+ limits if tools run out of memory or processes.
534
+ - A sandbox run has a maximum lifetime of 60 minutes. Past it the worker is
535
+ stopped and the run fails with `native_sandbox_deadline_exceeded`.
536
+ - The agent's own turn and budget limits still apply as for any native agent.
537
+
538
+ ## Sub-agents
539
+
540
+ A sandboxed run can delegate to sub-agents, including several in parallel.
541
+ Sub-agents of a sandboxed run are always **managed runs**: the gateway records
542
+ the delegation, and the server's scheduler leader starts the child on its next
543
+ tick (about every 10 seconds). Each delegation therefore adds up to about ten
544
+ seconds of start-up latency, and `wardby serve` or `wardby scheduler` must be
545
+ running or the child never starts.
546
+
547
+ A child runs in its own configured mode (a `control-plane` child runs in the
548
+ server, a `sandbox` child in its own worker). Cancelling a parent run stops its
549
+ open children.
550
+
551
+ ## Budget behavior
552
+
553
+ The gateway is the budget authority for sandboxed runs. Before each model call
554
+ it reserves that call's worst-case cost against the run's budget (and any
555
+ budget group), calls the provider, then settles at the actual usage. A call the
556
+ remaining budget cannot cover is refused and the run ends `budget_exhausted`
557
+ instead of overspending. The worker cannot raise or bypass its budget because it
558
+ never talks to the provider directly.
559
+
560
+ ## Failure modes
561
+
562
+ | Error code | Meaning | What to do |
563
+ | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
564
+ | [`native_sandbox_unavailable`](../help/errors/native-sandbox-unavailable.md) | Agent is in `sandbox` mode but the server has no native sandbox. Fails before any spend. | Configure the sandbox (steps above) or set the agent back to `control-plane`. |
565
+ | [`native_sandbox_worker_exited`](../help/errors/native-sandbox-worker-exited.md) | The worker ended without producing a result. | Check the tool code and the worker's memory and PID limits; run again. |
566
+ | [`native_sandbox_deadline_exceeded`](../help/errors/native-sandbox-deadline-exceeded.md) | The run passed its 60-minute maximum; the worker was stopped. | Split the work into smaller runs or sub-agents. |
567
+ | [`native_sandbox_worker_lost`](../help/errors/native-sandbox-worker-lost.md) | The worker container or pod disappeared (for example its host or node restarted). It is never relaunched. | Trigger the run again; check the Docker host or node stability. |
568
+ | [`native_sandbox_image_not_pinned`](../help/errors/native-sandbox-image-not-pinned.md) | `NATIVE_SANDBOX_WORKER_IMAGE` is a mutable tag. | Use a `repo@sha256:...` digest or a local image id. |
569
+ | [`native_sandbox_docker_failed`](../help/errors/native-sandbox-docker-failed.md) | A Docker command (pull, network create, connect, run) failed. | Read the message, then check the Docker daemon, image availability, and gateway name. |
570
+ | [`native_sandbox_network_unenforced`](../help/errors/native-sandbox-network-unenforced.md) | Kubernetes: the pod's egress isolation could not be proven in time; the pod was removed. | Check that the CNI enforces NetworkPolicy and the gateway Service exposes 8790 and 8791. |
571
+ | [`native_sandbox_isolation_mismatch`](../help/errors/native-sandbox-isolation-mismatch.md) | Kubernetes: the stored pod or NetworkPolicy differs from what Wardby built (an admission change). | Exempt the run namespace's native-run pods from mutating policies. |
572
+ | [`native_sandbox_gateway_unavailable`](../help/errors/native-sandbox-gateway-unavailable.md) | Kubernetes: the gateway Service is missing or has no ClusterIP. | Create the Service (ClusterIP, not headless) or fix `NATIVE_GATEWAY_SERVICE`. |
573
+ | [`native_sandbox_worker_unready`](../help/errors/native-sandbox-worker-unready.md) | Kubernetes: the worker pod did not reach Running in time. | Check image pull, quotas, and scheduling with `kubectl describe pod`. |
574
+ | [`native_sandbox_capacity`](../help/errors/native-sandbox-capacity.md) | `NATIVE_SANDBOX_MAX_CONCURRENT` runs are already active, or the namespace ResourceQuota is full. Not queued. | Wait and retry, or raise the cap together with the ResourceQuota. |
575
+ | [`native_sandbox_warm_delivery_failed`](../help/errors/native-sandbox-warm-delivery-failed.md) | A claimed warm worker could not receive the run's input and could not be confirmed stopped within 30 seconds. | Trigger the run again; check the node or Docker host and pod exec access. |
576
+ | [`native_sandbox_requires_catalog`](../help/errors/native-sandbox-requires-catalog.md) | No model catalog entry was recorded for the run: the process running it does not use the catalog-backed model router. | Run it on a standard `wardby serve`/`scheduler`/`mcp` process, or keep the agent on `control-plane`. |
577
+
578
+ A run's error appears in its `error` field in `get_run` and `list_runs`.
579
+
580
+ ## Roll back
581
+
582
+ Set the agent back to `control-plane` with `update_agent` or
583
+ `wardby agent mode <name> control-plane`. Only new runs are
584
+ affected: runs already started in sandbox mode finish there. To switch sandbox mode off for the whole
585
+ deployment, unset `NATIVE_SANDBOX_LAUNCHER`; sandbox-mode agents then fail
586
+ closed with `native_sandbox_unavailable` rather than running unisolated, so move
587
+ them back to `control-plane` first if they should keep running.
588
+
589
+ ## Acceptance tests
590
+
591
+ Three opt-in checks exercise the real isolation against a real runtime. Each is
592
+ skipped unless its script sets its flag:
593
+
594
+ - `npm run test:native-docker`: the Docker launcher on the local Docker host.
595
+ - `npm run test:native-kind`: the Kubernetes launcher on a kind cluster
596
+ (`NATIVE_TEST_KIND_WORKER_IMAGE`).
597
+ - `npm run test:native-cluster`: launcher-only isolation checks against the
598
+ deployed gateway of any cluster. It needs no database or model. Set
599
+ `NATIVE_TEST_WORKER_IMAGE` (required, a registry digest) and, as needed,
600
+ `NATIVE_TEST_CONTEXT`, `NATIVE_TEST_NAMESPACE`, `NATIVE_TEST_PLATFORM`,
601
+ `NATIVE_TEST_RUNTIME_CLASS`, `NATIVE_TEST_PRIORITY_CLASS`, and
602
+ `NATIVE_TEST_FORBIDDEN`, a comma-separated list of `host:port` addresses that
603
+ must be unreachable from a worker (for example the database's private IP on
604
+ port 3307).
605
+
606
+ ## Troubleshooting
607
+
608
+ - **Gateway health.** `GET /healthz` on the gateway returns `{"ok":true}` when
609
+ its database answers and 503 otherwise. From the Docker host:
610
+ `docker exec wardby-native-gateway node -e "fetch('http://127.0.0.1:8790/healthz').then(r => r.text()).then(console.log)"`.
611
+ - **List worker containers.** Workers and networks carry the label
612
+ `io.wardby.component=native-worker`:
613
+ `docker ps --all --filter label=io.wardby.component=native-worker`.
614
+ The server removes finished workers and, at startup, sweeps leftovers whose
615
+ runs have ended or passed their deadline.
616
+ - **Kubernetes: list worker pods.** Run pods carry the label
617
+ `wardby.io/component=native-run`:
618
+ `kubectl get pods -n <namespace> -l wardby.io/component=native-run`.
619
+ The server removes finished pods (with their Secret and NetworkPolicy) and,
620
+ at startup, sweeps leftovers whose runs have ended or passed their deadline.
621
+ - **Kubernetes: gateway health.** `kubectl get deploy,svc,endpoints -n <namespace> wardby-native-gateway`;
622
+ the Service must list ports 8790 and 8791 and have ready endpoints.
623
+ - **Check a run.** Call `get_run` for the run's status and error code, then use
624
+ the matching error article from `search_help` or `get_help_article`.
625
+ - **Workers cannot reach the gateway.** Confirm `NATIVE_GATEWAY_CONTAINER`
626
+ matches the running container's name exactly and that the gateway listens on
627
+ the port in `NATIVE_GATEWAY_URL` (default 8790).
628
+ - **Sub-agents never start.** Confirm `wardby serve` or `wardby scheduler` is
629
+ running; the scheduler leader starts them.
630
+ - **Tool code fails inside the sandbox.** The worker has no network except the
631
+ gateway and a read-only filesystem apart from `/tmp`; tools must use the
632
+ provided fetch, datastore, and secret functions.
633
+
634
+ See also [Security deployment](security-deployment.md) and the
635
+ [runtime architecture](architecture-runtime.md).
@@ -40,6 +40,15 @@ repository-scoped GitHub App installation, and a deliberately small budget.
40
40
  Verify the requested diff, terminal outcome, cost, resource cleanup, and branch
41
41
  cleanup. See [local coding-agent setup](coding-agent-setup.md).
42
42
 
43
+ The bundled help catalog is built for the version in `package.json`.
44
+ `appliesTo` on each help article must name its minimum release; later articles
45
+ stay in source but are omitted from that release's CLI and MCP help. The build
46
+ fails when an included article links to an omitted one. When adding a newer
47
+ feature to an older article, move its guidance into a suitably versioned
48
+ article or update the older article so it remains accurate for its stated
49
+ minimum release. Mark newer features in the public `docs/` guides with their
50
+ minimum release as well.
51
+
43
52
  Before production deployment, review [runtime architecture](architecture-runtime.md),
44
53
  [coding-worker isolation](coding-worker-isolation.md), and the
45
54
  [security deployment guide](security-deployment.md). A self-hosted operator is