@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,74 @@
1
+ /**
2
+ * Launches, watches, stops, and removes native sandbox workers on the local
3
+ * Docker engine (docs/native-sandbox.md), from the isolation plan in
4
+ * docker-isolation.ts. Every operation is idempotent and keyed by the run id,
5
+ * so the executor can retry, a second launch attaches instead of starting
6
+ * twice, and a restarted server re-finds a running worker by name.
7
+ */
8
+ import { type NativeWorkerLimits } from "./docker-isolation.js";
9
+ import type { DetachedWorkerLauncher, WorkerHandle } from "./launch.js";
10
+ import type { WorkerInput } from "./protocol.js";
11
+ import { type WarmWorkerLauncher } from "./warm-pool.js";
12
+ export interface DockerResult {
13
+ code: number | null;
14
+ stdout: string;
15
+ stderr: string;
16
+ }
17
+ /** The Docker CLI as this launcher uses it; tests replace it. */
18
+ export interface DockerCli {
19
+ run(args: string[], options?: {
20
+ input?: string;
21
+ timeoutMs?: number;
22
+ }): Promise<DockerResult>;
23
+ }
24
+ export declare function processDockerCli(binary?: string): DockerCli;
25
+ export type NativeWorkerState = {
26
+ state: "running";
27
+ } | {
28
+ state: "exited";
29
+ exitCode: number;
30
+ } | {
31
+ state: "missing";
32
+ };
33
+ export interface DockerNativeWorkerLauncherOptions {
34
+ image: string;
35
+ /** The container running `wardby native-gateway`, joined to each run's network. */
36
+ gatewayContainer: string;
37
+ limits: NativeWorkerLimits;
38
+ docker?: DockerCli;
39
+ }
40
+ export declare class DockerNativeWorkerLauncher implements DetachedWorkerLauncher, WarmWorkerLauncher {
41
+ private readonly options;
42
+ private readonly docker;
43
+ constructor(options: DockerNativeWorkerLauncherOptions);
44
+ private must;
45
+ /** The image must be present and pinned; a digest is pulled once, a local id must already exist. */
46
+ private ensureImage;
47
+ launch(input: WorkerInput): Promise<WorkerHandle>;
48
+ /** Waits for (and reports) the worker's exit; usable after a server restart. */
49
+ handle(runId: string): WorkerHandle;
50
+ private handleByName;
51
+ inspect(runId: string): Promise<NativeWorkerState>;
52
+ private inspectByName;
53
+ kill(runId: string): Promise<void>;
54
+ /** Removes the worker, its network, and the gateway's membership of it. Idempotent. */
55
+ remove(runId: string): Promise<void>;
56
+ /** Removes a worker found by the janitor, by its container name (its network shares the name's token). */
57
+ removeByWorkerName(workerName: string): Promise<void>;
58
+ /** Every native worker container this engine holds, with its run-hash label (for the janitor). */
59
+ listWorkers(): Promise<{
60
+ name: string;
61
+ runHash: string;
62
+ }[]>;
63
+ warmSpecHash(waitMs: number): string;
64
+ /** Its network is internal from creation, so a running container is already isolated: claimable. */
65
+ startWarm(token: string, waitMs: number): Promise<void>;
66
+ /** Still running, and attached to its own network and nothing else. */
67
+ reattestWarm(token: string): Promise<boolean>;
68
+ deliver(token: string, input: WorkerInput): Promise<void>;
69
+ warmHandle(token: string): WorkerHandle;
70
+ inspectWarm(token: string): Promise<NativeWorkerState>;
71
+ killWarm(token: string): Promise<void>;
72
+ removeWarm(token: string): Promise<void>;
73
+ listWarm(): Promise<string[]>;
74
+ }
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Launches, watches, stops, and removes native sandbox workers on the local
3
+ * Docker engine (docs/native-sandbox.md), from the isolation plan in
4
+ * docker-isolation.ts. Every operation is idempotent and keyed by the run id,
5
+ * so the executor can retry, a second launch attaches instead of starting
6
+ * twice, and a restarted server re-finds a running worker by name.
7
+ */
8
+ import { spawn } from "node:child_process";
9
+ import { createHash } from "node:crypto";
10
+ import { logger } from "../core/logger.js";
11
+ import { buildGatewayConnectArgs, buildGatewayDisconnectArgs, buildNativeNetworkCreateArgs, buildNativeWarmNetworkCreateArgs, buildNativeWarmWorkerRunArgs, buildNativeWorkerRunArgs, gatewayConnectArgs, gatewayDisconnectArgs, nativeIsolationNames, nativeWarmIsolationNames, NATIVE_WARM_LABEL_FILTER, NATIVE_WARM_TOKEN_LABEL, NATIVE_WORKER_LABEL_FILTER, } from "./docker-isolation.js";
12
+ import { warmDeliveryCommand } from "./warm-delivery.js";
13
+ import { NATIVE_SANDBOX_WARM_DELIVERY_FAILED } from "./warm-pool.js";
14
+ const launcherLog = logger.child({ module: "native-docker-launcher" });
15
+ export function processDockerCli(binary = "docker") {
16
+ return {
17
+ run(args, options = {}) {
18
+ return new Promise((resolve) => {
19
+ const child = spawn(binary, args, { stdio: ["pipe", "pipe", "pipe"] });
20
+ let stdout = "";
21
+ let stderr = "";
22
+ const timer = options.timeoutMs ? setTimeout(() => child.kill("SIGKILL"), options.timeoutMs) : undefined;
23
+ child.stdout.on("data", (chunk) => (stdout += chunk.toString()));
24
+ child.stderr.on("data", (chunk) => (stderr += chunk.toString()));
25
+ child.on("error", (err) => resolve({ code: null, stdout, stderr: err.message }));
26
+ child.on("close", (code) => {
27
+ if (timer)
28
+ clearTimeout(timer);
29
+ resolve({ code, stdout, stderr });
30
+ });
31
+ child.stdin.on("error", () => { });
32
+ child.stdin.end(options.input ?? "");
33
+ });
34
+ },
35
+ };
36
+ }
37
+ export class DockerNativeWorkerLauncher {
38
+ options;
39
+ docker;
40
+ constructor(options) {
41
+ this.options = options;
42
+ this.docker = options.docker ?? processDockerCli();
43
+ }
44
+ async must(args, what, tolerate) {
45
+ const result = await this.docker.run(args);
46
+ if (result.code !== 0 && !(tolerate && tolerate.test(result.stderr))) {
47
+ throw new Error(`native_sandbox_docker_failed: ${what}: ${result.stderr.trim().slice(0, 500)}`);
48
+ }
49
+ return result;
50
+ }
51
+ /** The image must be present and pinned; a digest is pulled once, a local id must already exist. */
52
+ async ensureImage() {
53
+ const present = await this.docker.run(["image", "inspect", "--format", "{{.Id}}", this.options.image]);
54
+ if (present.code === 0)
55
+ return;
56
+ await this.must(["image", "pull", "--quiet", this.options.image], "pull the worker image");
57
+ }
58
+ async launch(input) {
59
+ const runId = input.runId;
60
+ const names = nativeIsolationNames(runId);
61
+ const existing = await this.inspect(runId);
62
+ if (existing.state === "missing") {
63
+ await this.ensureImage();
64
+ await this.must(buildNativeNetworkCreateArgs(runId), "create the run network", /already exists/);
65
+ await this.must(buildGatewayConnectArgs(runId, this.options.gatewayContainer), "join the gateway to the run network", /already exists|is already connected/);
66
+ const args = buildNativeWorkerRunArgs({ runId, image: this.options.image, limits: this.options.limits });
67
+ // Attached so the input (capability included) goes in on stdin; the exit is read with
68
+ // `docker wait`, which a restarted server can call again.
69
+ void this.docker.run(args, { input: `${JSON.stringify(input)}\n` }).then((result) => {
70
+ if (result.code !== 0 && result.code !== null && !/Conflict/.test(result.stderr)) {
71
+ launcherLog.warn({ container: names.worker, code: result.code }, "native worker exited non-zero");
72
+ }
73
+ });
74
+ }
75
+ else {
76
+ launcherLog.info({ container: names.worker }, "native worker already exists: attaching, not relaunching");
77
+ }
78
+ return this.handle(runId);
79
+ }
80
+ /** Waits for (and reports) the worker's exit; usable after a server restart. */
81
+ handle(runId) {
82
+ return this.handleByName(nativeIsolationNames(runId).worker);
83
+ }
84
+ handleByName(name) {
85
+ const exited = (async () => {
86
+ // `docker run` may not have created the container yet: wait for it to exist (bounded).
87
+ for (let attempt = 0; attempt < 50; attempt += 1) {
88
+ const waited = await this.docker.run(["container", "wait", name]);
89
+ if (waited.code === 0) {
90
+ const code = Number(waited.stdout.trim());
91
+ return Number.isInteger(code) ? code : null;
92
+ }
93
+ if (!/No such container/i.test(waited.stderr))
94
+ return null;
95
+ await new Promise((resolve) => setTimeout(resolve, 200));
96
+ }
97
+ return null;
98
+ })();
99
+ return { exited, kill: () => void this.docker.run(["container", "kill", name]) };
100
+ }
101
+ async inspect(runId) {
102
+ return this.inspectByName(nativeIsolationNames(runId).worker);
103
+ }
104
+ async inspectByName(name) {
105
+ const result = await this.docker.run([
106
+ "container",
107
+ "inspect",
108
+ "--format",
109
+ "{{.State.Status}} {{.State.ExitCode}}",
110
+ name,
111
+ ]);
112
+ if (result.code !== 0)
113
+ return { state: "missing" };
114
+ const [status, exitCode] = result.stdout.trim().split(" ");
115
+ if (status === "running" || status === "created" || status === "restarting")
116
+ return { state: "running" };
117
+ return { state: "exited", exitCode: Number(exitCode) };
118
+ }
119
+ async kill(runId) {
120
+ await this.docker.run(["container", "kill", nativeIsolationNames(runId).worker]);
121
+ }
122
+ /** Removes the worker, its network, and the gateway's membership of it. Idempotent. */
123
+ async remove(runId) {
124
+ const names = nativeIsolationNames(runId);
125
+ await this.docker.run(["container", "rm", "--force", names.worker]);
126
+ await this.docker.run(buildGatewayDisconnectArgs(runId, this.options.gatewayContainer));
127
+ await this.docker.run(["network", "rm", names.network]);
128
+ }
129
+ /** Removes a worker found by the janitor, by its container name (its network shares the name's token). */
130
+ async removeByWorkerName(workerName) {
131
+ const token = /^wardby-native-([0-9a-f]{20})$/.exec(workerName)?.[1];
132
+ if (!token)
133
+ return;
134
+ await this.docker.run(["container", "rm", "--force", workerName]);
135
+ await this.docker.run(["network", "disconnect", "--force", `wardby-nnet-${token}`, this.options.gatewayContainer]);
136
+ await this.docker.run(["network", "rm", `wardby-nnet-${token}`]);
137
+ }
138
+ /** Every native worker container this engine holds, with its run-hash label (for the janitor). */
139
+ async listWorkers() {
140
+ const result = await this.docker.run([
141
+ "container",
142
+ "ls",
143
+ "--all",
144
+ "--filter",
145
+ `label=${NATIVE_WORKER_LABEL_FILTER}`,
146
+ "--format",
147
+ '{{.Names}} {{.Label "io.wardby.run-sha256"}}',
148
+ ]);
149
+ if (result.code !== 0)
150
+ return [];
151
+ return result.stdout
152
+ .split("\n")
153
+ .map((line) => line.trim().split(" "))
154
+ .filter(([name, runHash]) => name && runHash)
155
+ .map(([name, runHash]) => ({ name, runHash }));
156
+ }
157
+ // --- Warm pool workers (native sandbox phase 6) ---
158
+ warmSpecHash(waitMs) {
159
+ const { image, limits, gatewayContainer } = this.options;
160
+ return createHash("sha256")
161
+ .update(JSON.stringify({ launcher: "docker", image, limits, gatewayContainer, waitMs }))
162
+ .digest("hex");
163
+ }
164
+ /** Its network is internal from creation, so a running container is already isolated: claimable. */
165
+ async startWarm(token, waitMs) {
166
+ const names = nativeWarmIsolationNames(token);
167
+ try {
168
+ await this.ensureImage();
169
+ await this.must(buildNativeWarmNetworkCreateArgs(token), "create the warm worker network", /already exists/);
170
+ await this.must(gatewayConnectArgs(names.network, this.options.gatewayContainer), "join the gateway to the warm worker network", /already exists|is already connected/);
171
+ await this.must(buildNativeWarmWorkerRunArgs({ token, image: this.options.image, limits: this.options.limits, waitMs }), "start the warm worker");
172
+ }
173
+ catch (err) {
174
+ await this.removeWarm(token).catch(() => { });
175
+ throw err;
176
+ }
177
+ }
178
+ /** Still running, and attached to its own network and nothing else. */
179
+ async reattestWarm(token) {
180
+ const names = nativeWarmIsolationNames(token);
181
+ const result = await this.docker.run([
182
+ "container",
183
+ "inspect",
184
+ "--format",
185
+ "{{.State.Status}} {{json .NetworkSettings.Networks}}",
186
+ names.worker,
187
+ ]);
188
+ if (result.code !== 0)
189
+ return false;
190
+ const [status, ...rest] = result.stdout.trim().split(" ");
191
+ try {
192
+ const networks = Object.keys(JSON.parse(rest.join(" ")));
193
+ return status === "running" && networks.length === 1 && networks[0] === names.network;
194
+ }
195
+ catch {
196
+ return false;
197
+ }
198
+ }
199
+ async deliver(token, input) {
200
+ const result = await this.docker.run(["container", "exec", "-i", nativeWarmIsolationNames(token).worker, ...warmDeliveryCommand()], { input: `${JSON.stringify(input)}\n`, timeoutMs: 20_000 });
201
+ if (result.code !== 0) {
202
+ throw new Error(`${NATIVE_SANDBOX_WARM_DELIVERY_FAILED}: delivering the run's input exited ${result.code ?? "?"}.`);
203
+ }
204
+ }
205
+ warmHandle(token) {
206
+ return this.handleByName(nativeWarmIsolationNames(token).worker);
207
+ }
208
+ inspectWarm(token) {
209
+ return this.inspectByName(nativeWarmIsolationNames(token).worker);
210
+ }
211
+ async killWarm(token) {
212
+ await this.docker.run(["container", "kill", nativeWarmIsolationNames(token).worker]);
213
+ }
214
+ async removeWarm(token) {
215
+ const names = nativeWarmIsolationNames(token);
216
+ await this.docker.run(["container", "rm", "--force", names.worker]);
217
+ await this.docker.run(gatewayDisconnectArgs(names.network, this.options.gatewayContainer));
218
+ await this.docker.run(["network", "rm", names.network]);
219
+ }
220
+ async listWarm() {
221
+ const result = await this.docker.run([
222
+ "container",
223
+ "ls",
224
+ "--all",
225
+ "--filter",
226
+ `label=${NATIVE_WARM_LABEL_FILTER}`,
227
+ "--format",
228
+ `{{.Label "${NATIVE_WARM_TOKEN_LABEL}"}}`,
229
+ ]);
230
+ if (result.code !== 0)
231
+ return [];
232
+ return result.stdout
233
+ .split("\n")
234
+ .map((line) => line.trim())
235
+ .filter((token) => /^[0-9a-f]{20}$/.test(token));
236
+ }
237
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * The native sandbox gateway over HTTP (native sandbox phase 3): stateless per
3
+ * request. Each request is authenticated by the hash of its bearer capability,
4
+ * rebuilt from its run's gateway session (the snapshot pinned at launch), the
5
+ * run row, and the call ledger, and served by the same trusted code the
6
+ * in-process path uses — so any control-plane replica can serve any call, and a
7
+ * replica restart is only a retry for the worker.
8
+ *
9
+ * Authority stays here: a model call reserves its worst case against the
10
+ * session budget before the provider is called and settles at the provider's
11
+ * reported usage; the worker's own usage numbers are never read.
12
+ */
13
+ import { type NativeRunProviders, type RunnerDb } from "../core/runner.js";
14
+ import type { LlmStreamEvent } from "../providers/llm/types.js";
15
+ import { PrismaGatewayLedger } from "./ledger.js";
16
+ /** Output tokens reserved for a model call that names no maxTokens (as the coding proxy does). */
17
+ export declare const GATEWAY_DEFAULT_RESERVE_OUTPUT_TOKENS = 4096;
18
+ /** The most output tokens one call may reserve for. */
19
+ export declare const GATEWAY_MAX_RESERVE_OUTPUT_TOKENS = 128000;
20
+ /** A replayable result is kept only up to this size; a larger one cannot be replayed on retry. */
21
+ export declare const GATEWAY_MAX_REPLAY_BYTES: number;
22
+ export interface GatewayServerDeps {
23
+ db: RunnerDb & ConstructorParameters<typeof PrismaGatewayLedger>[0];
24
+ providers: NativeRunProviders;
25
+ /** Delegation long-poll window; tests shorten it. */
26
+ pollWindowMs?: number;
27
+ }
28
+ /** What a request gets back: one JSON value, or a stream of model events. */
29
+ export type GatewayResponse = {
30
+ kind: "json";
31
+ status: number;
32
+ body: unknown;
33
+ } | {
34
+ kind: "stream";
35
+ status: 200;
36
+ events: AsyncIterable<LlmStreamEvent>;
37
+ };
38
+ /** Serves one gateway request. Never throws: every failure is a response. */
39
+ export declare function serveGatewayRequest(deps: GatewayServerDeps, authorization: string | undefined, body: unknown): Promise<GatewayResponse>;
@@ -0,0 +1,315 @@
1
+ /**
2
+ * The native sandbox gateway over HTTP (native sandbox phase 3): stateless per
3
+ * request. Each request is authenticated by the hash of its bearer capability,
4
+ * rebuilt from its run's gateway session (the snapshot pinned at launch), the
5
+ * run row, and the call ledger, and served by the same trusted code the
6
+ * in-process path uses — so any control-plane replica can serve any call, and a
7
+ * replica restart is only a retry for the worker.
8
+ *
9
+ * Authority stays here: a model call reserves its worst case against the
10
+ * session budget before the provider is called and settles at the provider's
11
+ * reported usage; the worker's own usage numbers are never read.
12
+ */
13
+ import { logger } from "../core/logger.js";
14
+ import { createNativeRunTools, durableDelegate, failNativeRun, finishNativeRun, nativeRunIntegrations, recordNativeRunProgress, runDrivability, sandboxCapabilityHash, } from "../core/runner.js";
15
+ import { DELEGATE_TOOL_PREFIX } from "../core/tool-names.js";
16
+ import { RoutingLlmProvider } from "../providers/llm/routing.js";
17
+ import { isPrivilegedBridgeName } from "../sandbox/host-functions.js";
18
+ import { PrismaGatewayLedger } from "./ledger.js";
19
+ import { GatewayError, GatewayRequestSchema, parseParams, } from "./protocol.js";
20
+ const serverLog = logger.child({ module: "native-gateway-server" });
21
+ /** Output tokens reserved for a model call that names no maxTokens (as the coding proxy does). */
22
+ export const GATEWAY_DEFAULT_RESERVE_OUTPUT_TOKENS = 4_096;
23
+ /** The most output tokens one call may reserve for. */
24
+ export const GATEWAY_MAX_RESERVE_OUTPUT_TOKENS = 128_000;
25
+ /** A replayable result is kept only up to this size; a larger one cannot be replayed on retry. */
26
+ export const GATEWAY_MAX_REPLAY_BYTES = 256 * 1024;
27
+ const ok = (result) => ({
28
+ kind: "json",
29
+ status: 200,
30
+ body: { ok: true, result: result ?? null },
31
+ });
32
+ const fail = (status, code, message) => ({
33
+ kind: "json",
34
+ status,
35
+ body: { ok: false, error: { code, message } },
36
+ });
37
+ const STATUS_FOR = {
38
+ invalid_request: 400,
39
+ not_allowed: 403,
40
+ run_not_drivable: 409,
41
+ duplicate_call: 409,
42
+ budget_exhausted: 402,
43
+ not_ready: 503,
44
+ bridge_error: 200,
45
+ internal: 500,
46
+ };
47
+ /** Serves one gateway request. Never throws: every failure is a response. */
48
+ export async function serveGatewayRequest(deps, authorization, body) {
49
+ try {
50
+ return await handle(deps, authorization, body);
51
+ }
52
+ catch (err) {
53
+ if (err instanceof GatewayError)
54
+ return fail(STATUS_FOR[err.code] ?? 500, err.code, err.message);
55
+ serverLog.error({ err }, "native gateway request failed");
56
+ return fail(500, "internal", "The gateway could not serve this call.");
57
+ }
58
+ }
59
+ async function handle(deps, authorization, body) {
60
+ const { db } = deps;
61
+ const ledger = new PrismaGatewayLedger(db);
62
+ const token = /^Bearer (\S+)$/.exec(authorization ?? "")?.[1];
63
+ if (!token)
64
+ return fail(401, "unauthorized", "A gateway capability is required.");
65
+ const session = await ledger.findSessionByCapabilityHash(sandboxCapabilityHash(token));
66
+ if (!session)
67
+ return fail(401, "unauthorized", "Unknown gateway capability.");
68
+ const parsed = GatewayRequestSchema.safeParse(body);
69
+ if (!parsed.success)
70
+ throw new GatewayError("invalid_request", parsed.error.issues[0]?.message ?? "invalid request");
71
+ const request = parsed.data;
72
+ if (request.runId !== session.runId)
73
+ throw new GatewayError("not_allowed", "request for another run");
74
+ let params;
75
+ try {
76
+ params = parseParams(request.method, request.params);
77
+ }
78
+ catch (err) {
79
+ throw new GatewayError("invalid_request", err instanceof Error ? err.message : String(err));
80
+ }
81
+ if (session.status !== "active" || Date.now() >= session.deadlineAt.getTime()) {
82
+ throw new GatewayError("run_not_drivable", "This run's gateway session has ended.");
83
+ }
84
+ // Nothing is served before the worker's network isolation is proven: until then its egress may
85
+ // not yet be limited to this gateway, so it must not receive tool code results, secrets, or data.
86
+ if (!session.networkReadyAt) {
87
+ throw new GatewayError("not_ready", "This run's network isolation is not proven yet; retry shortly.");
88
+ }
89
+ if ((await runDrivability(db, session.runId)) !== "drivable") {
90
+ throw new GatewayError("run_not_drivable", "This run is no longer running.");
91
+ }
92
+ const ctx = await rebuild(deps, session);
93
+ switch (request.method) {
94
+ case "llm.stream":
95
+ return {
96
+ kind: "stream",
97
+ status: 200,
98
+ events: streamModel(ctx, ledger, request.callId, params),
99
+ };
100
+ case "llm.countTokens": {
101
+ const p = params;
102
+ return ok(await ctx.llm.countTokens(p.model, p.messages, p.tools));
103
+ }
104
+ case "builtin.call":
105
+ return ok(await builtin(ctx, ledger, request.callId, params, deps.pollWindowMs));
106
+ case "host.call":
107
+ return ok(await hostCall(ctx, ledger, request.callId, params));
108
+ case "progress": {
109
+ const p = params;
110
+ const totals = await ledger.totals(session.id);
111
+ await recordNativeRunProgress(db, session.runId, {
112
+ turns: p.turns,
113
+ usage: { tokensIn: totals.tokensIn, tokensOut: totals.tokensOut, costUsd: totals.costUsd },
114
+ });
115
+ return ok(null);
116
+ }
117
+ case "text":
118
+ // Live text is for an attached observer; a background sandbox run has none.
119
+ return ok(null);
120
+ case "finish":
121
+ return ok(await finish(ctx, ledger, request.callId, params));
122
+ }
123
+ }
124
+ async function rebuild(deps, session) {
125
+ const { db, providers } = deps;
126
+ const snapshot = session.snapshot;
127
+ const existingRun = await db.run.findUniqueOrThrow({ where: { id: session.runId } });
128
+ const { reviewHosts, repoAccess, issueTrackers } = nativeRunIntegrations(providers, db);
129
+ const loaded = snapshot.loaded;
130
+ const llm = loaded.pricing && providers.llm instanceof RoutingLlmProvider
131
+ ? providers.llm.forRun(loaded.pricing.entry)
132
+ : providers.llm;
133
+ return {
134
+ session,
135
+ snapshot,
136
+ llm,
137
+ tools: createNativeRunTools({
138
+ runId: session.runId,
139
+ existingRun,
140
+ loaded,
141
+ providers,
142
+ db,
143
+ reviewHosts,
144
+ repoAccess,
145
+ issueTrackers,
146
+ }),
147
+ finishContext: { runId: session.runId, db, providers, reviewHosts, repoAccess, issueTrackers },
148
+ delegation: {
149
+ runId: session.runId,
150
+ existingRun,
151
+ loaded,
152
+ providers,
153
+ db,
154
+ issueTrackers,
155
+ pollWindowMs: deps.pollWindowMs,
156
+ },
157
+ };
158
+ }
159
+ async function* streamModel(ctx, ledger, callId, params) {
160
+ const req = params.request;
161
+ if (req.model !== ctx.snapshot.input.agent.model)
162
+ throw new GatewayError("not_allowed", "A run may only call its own model.");
163
+ const inputTokens = await ctx.llm.countTokens(req.model, req.messages, req.tools);
164
+ const outputTokens = Math.min(req.maxTokens ?? GATEWAY_DEFAULT_RESERVE_OUTPUT_TOKENS, GATEWAY_MAX_RESERVE_OUTPUT_TOKENS);
165
+ const reservationUsd = ctx.llm.priceUsd(req.model, { inputTokens, outputTokens });
166
+ const reserved = await ledger.reserve({ sessionId: ctx.session.id, callId, reservationUsd, now: new Date() });
167
+ if (reserved.outcome === "duplicate")
168
+ throw new GatewayError("duplicate_call", `callId "${callId}" was already used.`);
169
+ if (reserved.outcome === "inactive")
170
+ throw new GatewayError("run_not_drivable", "This run's gateway session has ended.");
171
+ if (reserved.outcome === "budget_exhausted") {
172
+ throw new GatewayError("budget_exhausted", "This run's budget cannot cover another model call.");
173
+ }
174
+ let settled = false;
175
+ let sawEvent = false;
176
+ let streamedText = "";
177
+ try {
178
+ for await (const event of ctx.llm.stream(req)) {
179
+ sawEvent = true;
180
+ if (event.type === "text")
181
+ streamedText += event.delta;
182
+ if (event.type === "done") {
183
+ settled = true;
184
+ await ledger.complete(ctx.session.id, callId, {
185
+ inputTokens: event.usage.inputTokens,
186
+ outputTokens: event.usage.outputTokens,
187
+ cachedInputTokens: event.usage.cachedInputTokens ?? 0,
188
+ cacheWriteTokens: event.usage.cacheWriteTokens ?? 0,
189
+ costUsd: event.usage.costUsd,
190
+ });
191
+ }
192
+ yield event;
193
+ }
194
+ }
195
+ catch (err) {
196
+ if (!settled) {
197
+ // Refused before anything streamed: the provider billed nothing. Mid-stream: unknown, keep the hold.
198
+ if (sawEvent)
199
+ await ledger.markUncertain(ctx.session.id, callId);
200
+ else
201
+ await ledger.release(ctx.session.id, callId);
202
+ settled = true;
203
+ }
204
+ throw err;
205
+ }
206
+ finally {
207
+ if (!settled) {
208
+ // The worker stopped reading (its own budget cutoff) before usage arrived: bill an estimate.
209
+ const output = streamedText
210
+ ? await ctx.llm.countTokens(req.model, [{ role: "assistant", content: streamedText }]).catch(() => 0)
211
+ : 0;
212
+ await ledger.complete(ctx.session.id, callId, {
213
+ inputTokens,
214
+ outputTokens: output,
215
+ cachedInputTokens: 0,
216
+ cacheWriteTokens: 0,
217
+ costUsd: ctx.llm.priceUsd(req.model, { inputTokens, outputTokens: output }),
218
+ });
219
+ }
220
+ }
221
+ }
222
+ async function builtin(ctx, ledger, callId, params, pollWindowMs) {
223
+ if (!ctx.snapshot.input.builtinTools.includes(params.name)) {
224
+ throw new GatewayError("not_allowed", `"${params.name}" is not a built-in tool of this run.`);
225
+ }
226
+ if (params.name.startsWith(DELEGATE_TOOL_PREFIX)) {
227
+ const outcome = await durableDelegate({ ...ctx.delegation, pollWindowMs, ledger, sessionId: ctx.session.id }, callId, params.name, params.argsJson);
228
+ return "pending" in outcome ? { pending: true } : outcome.result;
229
+ }
230
+ const handler = ctx.tools.builtinHandler(params.name);
231
+ if (!handler)
232
+ throw new GatewayError("not_allowed", `"${params.name}" is not a built-in tool of this run.`);
233
+ return replayable(ledger, ctx.session.id, callId, "builtin.call", () => handler(params.argsJson));
234
+ }
235
+ async function hostCall(ctx, ledger, callId, params) {
236
+ if (!isPrivilegedBridgeName(params.bridge))
237
+ throw new GatewayError("not_allowed", "unknown bridge");
238
+ if (!ctx.snapshot.input.userTools[params.tool]) {
239
+ throw new GatewayError("not_allowed", `"${params.tool}" is not a user tool of this run.`);
240
+ }
241
+ const redact = params.bridge === "__bridge_console" ? await ctx.tools.readableSecretValues(params.tool) : undefined;
242
+ const host = ctx.tools.privilegedHostFor(params.tool, AbortSignal.timeout(30_000), redact);
243
+ if (!host)
244
+ throw new GatewayError("not_allowed", `"${params.tool}" is not a user tool of this run.`);
245
+ const bridge = host[params.bridge];
246
+ const run = async () => {
247
+ try {
248
+ return await bridge(params.argsJson);
249
+ }
250
+ catch (err) {
251
+ // What the tool sees, exactly as in-process (bridge.ts truncates the same way).
252
+ throw new GatewayError("bridge_error", err instanceof Error ? err.message.slice(0, 1024) : "Host function failed.");
253
+ }
254
+ };
255
+ // Console lines are not worth a ledger row, and a secret value must never be stored in one
256
+ // (a secrets.get is a side-effect-free read, so a retried callId simply reads it again).
257
+ if (params.bridge === "__bridge_console" || params.bridge === "__bridge_secretsGet")
258
+ return run();
259
+ return replayable(ledger, ctx.session.id, callId, "host.call", run);
260
+ }
261
+ /** Runs a one-shot call once per callId; a retry replays its recorded result. */
262
+ async function replayable(ledger, sessionId, callId, method, run) {
263
+ const claimed = await ledger.claim(sessionId, callId, method);
264
+ if (claimed.outcome === "done") {
265
+ const stored = claimed.result;
266
+ if (stored.tooLarge)
267
+ throw new GatewayError("internal", "This call's result was too large to replay.");
268
+ if (stored.error)
269
+ throw new GatewayError(stored.error.code, stored.error.message);
270
+ return stored.value;
271
+ }
272
+ if (claimed.outcome === "in_flight")
273
+ throw new GatewayError("duplicate_call", `callId "${callId}" is still in flight.`);
274
+ try {
275
+ const value = await run();
276
+ const record = { value: value ?? null };
277
+ await ledger.recordResult(sessionId, callId, JSON.stringify(record).length <= GATEWAY_MAX_REPLAY_BYTES ? record : { tooLarge: true });
278
+ return value;
279
+ }
280
+ catch (err) {
281
+ if (err instanceof GatewayError && err.code === "bridge_error") {
282
+ await ledger.recordResult(sessionId, callId, { error: { code: err.code, message: err.message } });
283
+ }
284
+ throw err;
285
+ }
286
+ }
287
+ async function finish(ctx, ledger, callId, params) {
288
+ const claimed = await ledger.claim(ctx.session.id, callId, "finish");
289
+ if (claimed.outcome !== "claimed")
290
+ return null;
291
+ const totals = await ledger.totals(ctx.session.id);
292
+ try {
293
+ await finishNativeRun(ctx.finishContext, ctx.snapshot.input.agent.model, {
294
+ status: params.status,
295
+ finalText: params.finalText,
296
+ turns: params.turns,
297
+ ...(params.error !== undefined ? { error: params.error } : {}),
298
+ usage: {
299
+ tokensIn: totals.tokensIn,
300
+ tokensOut: totals.tokensOut,
301
+ costUsd: totals.costUsd,
302
+ cachedInputTokens: totals.cachedInputTokens,
303
+ cacheWriteTokens: totals.cacheWriteTokens,
304
+ },
305
+ });
306
+ }
307
+ catch (err) {
308
+ await failNativeRun(ctx.finishContext, err);
309
+ }
310
+ finally {
311
+ await ledger.recordResult(ctx.session.id, callId, { value: null });
312
+ await ledger.endSession(ctx.session.id, "finished");
313
+ }
314
+ return null;
315
+ }