@wardby/cli 0.1.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 (412) hide show
  1. package/.env.example +131 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +2 -0
  4. package/README.md +282 -0
  5. package/bin/wardby.js +3 -0
  6. package/deploy/local/docker-compose.yml +19 -0
  7. package/dist/claude-coding-worker/driver.d.ts +70 -0
  8. package/dist/claude-coding-worker/driver.js +128 -0
  9. package/dist/claude-coding-worker/main.d.ts +2 -0
  10. package/dist/claude-coding-worker/main.js +41 -0
  11. package/dist/claude-coding-worker/sdk.d.ts +4 -0
  12. package/dist/claude-coding-worker/sdk.js +43 -0
  13. package/dist/claude-coding-worker/tool-relay.d.ts +1 -0
  14. package/dist/claude-coding-worker/tool-relay.js +9 -0
  15. package/dist/cli-help.d.ts +1 -0
  16. package/dist/cli-help.js +20 -0
  17. package/dist/cli.d.ts +18 -0
  18. package/dist/cli.js +629 -0
  19. package/dist/coding/observability.d.ts +59 -0
  20. package/dist/coding/observability.js +77 -0
  21. package/dist/coding/profile.d.ts +88 -0
  22. package/dist/coding/profile.js +137 -0
  23. package/dist/coding/protocol.d.ts +345 -0
  24. package/dist/coding/protocol.js +445 -0
  25. package/dist/coding/provider.d.ts +4 -0
  26. package/dist/coding/provider.js +18 -0
  27. package/dist/coding-proxy/main.d.ts +1 -0
  28. package/dist/coding-proxy/main.js +51 -0
  29. package/dist/coding-worker/artifact.d.ts +16 -0
  30. package/dist/coding-worker/artifact.js +37 -0
  31. package/dist/coding-worker/driver.d.ts +46 -0
  32. package/dist/coding-worker/driver.js +158 -0
  33. package/dist/coding-worker/errors.d.ts +1 -0
  34. package/dist/coding-worker/errors.js +23 -0
  35. package/dist/coding-worker/isolation-probe.d.ts +1 -0
  36. package/dist/coding-worker/isolation-probe.js +146 -0
  37. package/dist/coding-worker/keeper.d.ts +4 -0
  38. package/dist/coding-worker/keeper.js +29 -0
  39. package/dist/coding-worker/main.d.ts +2 -0
  40. package/dist/coding-worker/main.js +43 -0
  41. package/dist/coding-worker/sdk.d.ts +26 -0
  42. package/dist/coding-worker/sdk.js +28 -0
  43. package/dist/coding-worker/types.d.ts +64 -0
  44. package/dist/coding-worker/types.js +1 -0
  45. package/dist/config/providers.d.ts +158 -0
  46. package/dist/config/providers.js +162 -0
  47. package/dist/core/budget-groups.d.ts +72 -0
  48. package/dist/core/budget-groups.js +149 -0
  49. package/dist/core/budget.d.ts +63 -0
  50. package/dist/core/budget.js +69 -0
  51. package/dist/core/coding-queue.d.ts +32 -0
  52. package/dist/core/coding-queue.js +54 -0
  53. package/dist/core/cron.d.ts +31 -0
  54. package/dist/core/cron.js +43 -0
  55. package/dist/core/datastores.d.ts +41 -0
  56. package/dist/core/datastores.js +105 -0
  57. package/dist/core/db.d.ts +2 -0
  58. package/dist/core/db.js +2 -0
  59. package/dist/core/dispatch.d.ts +81 -0
  60. package/dist/core/dispatch.js +184 -0
  61. package/dist/core/engine-native.d.ts +29 -0
  62. package/dist/core/engine-native.js +259 -0
  63. package/dist/core/http-runtime.d.ts +7 -0
  64. package/dist/core/http-runtime.js +101 -0
  65. package/dist/core/lease.d.ts +13 -0
  66. package/dist/core/lease.js +22 -0
  67. package/dist/core/logger.d.ts +13 -0
  68. package/dist/core/logger.js +45 -0
  69. package/dist/core/memory-tools.d.ts +11 -0
  70. package/dist/core/memory-tools.js +122 -0
  71. package/dist/core/reconciler.d.ts +45 -0
  72. package/dist/core/reconciler.js +152 -0
  73. package/dist/core/runner.d.ts +87 -0
  74. package/dist/core/runner.js +529 -0
  75. package/dist/core/scheduler.d.ts +47 -0
  76. package/dist/core/scheduler.js +121 -0
  77. package/dist/core/secrets.d.ts +46 -0
  78. package/dist/core/secrets.js +100 -0
  79. package/dist/core/subagent-memory-tools.d.ts +18 -0
  80. package/dist/core/subagent-memory-tools.js +121 -0
  81. package/dist/core/timing.d.ts +7 -0
  82. package/dist/core/timing.js +7 -0
  83. package/dist/core/webhooks.d.ts +23 -0
  84. package/dist/core/webhooks.js +113 -0
  85. package/dist/env.d.ts +11 -0
  86. package/dist/env.js +13 -0
  87. package/dist/import/budgets.d.ts +16 -0
  88. package/dist/import/budgets.js +21 -0
  89. package/dist/import/bundle.d.ts +16 -0
  90. package/dist/import/bundle.js +61 -0
  91. package/dist/import/cli-args.d.ts +2 -0
  92. package/dist/import/cli-args.js +44 -0
  93. package/dist/import/create.d.ts +34 -0
  94. package/dist/import/create.js +363 -0
  95. package/dist/import/index.d.ts +25 -0
  96. package/dist/import/index.js +130 -0
  97. package/dist/import/model-gate.d.ts +1 -0
  98. package/dist/import/model-gate.js +3 -0
  99. package/dist/import/neutral-schema.d.ts +369 -0
  100. package/dist/import/neutral-schema.js +109 -0
  101. package/dist/import/preflight.d.ts +41 -0
  102. package/dist/import/preflight.js +71 -0
  103. package/dist/import/report.d.ts +5 -0
  104. package/dist/import/report.js +98 -0
  105. package/dist/import/tool-scan.d.ts +8 -0
  106. package/dist/import/tool-scan.js +20 -0
  107. package/dist/mcp/auth/ownership.d.ts +113 -0
  108. package/dist/mcp/auth/ownership.js +87 -0
  109. package/dist/mcp/auth/principal.d.ts +9 -0
  110. package/dist/mcp/auth/principal.js +9 -0
  111. package/dist/mcp/auth/resource-server.d.ts +47 -0
  112. package/dist/mcp/auth/resource-server.js +82 -0
  113. package/dist/mcp/auth/self-hosted/browser.d.ts +3 -0
  114. package/dist/mcp/auth/self-hosted/browser.js +273 -0
  115. package/dist/mcp/auth/self-hosted/cli.d.ts +2 -0
  116. package/dist/mcp/auth/self-hosted/cli.js +39 -0
  117. package/dist/mcp/auth/self-hosted/credentials.d.ts +58 -0
  118. package/dist/mcp/auth/self-hosted/credentials.js +106 -0
  119. package/dist/mcp/auth/self-hosted/rate-limit.d.ts +13 -0
  120. package/dist/mcp/auth/self-hosted/rate-limit.js +20 -0
  121. package/dist/mcp/auth/self-hosted/session.d.ts +36 -0
  122. package/dist/mcp/auth/self-hosted/session.js +110 -0
  123. package/dist/mcp/capabilities.d.ts +11 -0
  124. package/dist/mcp/capabilities.js +10 -0
  125. package/dist/mcp/context.d.ts +46 -0
  126. package/dist/mcp/context.js +1 -0
  127. package/dist/mcp/errors.d.ts +13 -0
  128. package/dist/mcp/errors.js +22 -0
  129. package/dist/mcp/index.d.ts +32 -0
  130. package/dist/mcp/index.js +187 -0
  131. package/dist/mcp/jsonrpc.d.ts +8 -0
  132. package/dist/mcp/jsonrpc.js +1 -0
  133. package/dist/mcp/server.d.ts +107 -0
  134. package/dist/mcp/server.js +203 -0
  135. package/dist/mcp/tasks/manager.d.ts +63 -0
  136. package/dist/mcp/tasks/manager.js +103 -0
  137. package/dist/mcp/tools/agents.d.ts +2 -0
  138. package/dist/mcp/tools/agents.js +375 -0
  139. package/dist/mcp/tools/budget-groups.d.ts +2 -0
  140. package/dist/mcp/tools/budget-groups.js +136 -0
  141. package/dist/mcp/tools/datastore.d.ts +2 -0
  142. package/dist/mcp/tools/datastore.js +217 -0
  143. package/dist/mcp/tools/memory.d.ts +3 -0
  144. package/dist/mcp/tools/memory.js +60 -0
  145. package/dist/mcp/tools/models.d.ts +8 -0
  146. package/dist/mcp/tools/models.js +15 -0
  147. package/dist/mcp/tools/runs.d.ts +2 -0
  148. package/dist/mcp/tools/runs.js +61 -0
  149. package/dist/mcp/tools/scheduling.d.ts +2 -0
  150. package/dist/mcp/tools/scheduling.js +48 -0
  151. package/dist/mcp/tools/secret-elicitation-form.d.ts +22 -0
  152. package/dist/mcp/tools/secret-elicitation-form.js +93 -0
  153. package/dist/mcp/tools/secret-elicitation-server.d.ts +5 -0
  154. package/dist/mcp/tools/secret-elicitation-server.js +57 -0
  155. package/dist/mcp/tools/secret-elicitation.d.ts +38 -0
  156. package/dist/mcp/tools/secret-elicitation.js +52 -0
  157. package/dist/mcp/tools/secrets.d.ts +13 -0
  158. package/dist/mcp/tools/secrets.js +124 -0
  159. package/dist/mcp/tools/subagents.d.ts +2 -0
  160. package/dist/mcp/tools/subagents.js +133 -0
  161. package/dist/mcp/tools/text-result.d.ts +6 -0
  162. package/dist/mcp/tools/text-result.js +3 -0
  163. package/dist/mcp/tools/tools.d.ts +2 -0
  164. package/dist/mcp/tools/tools.js +211 -0
  165. package/dist/mcp/tools/trigger.d.ts +2 -0
  166. package/dist/mcp/tools/trigger.js +120 -0
  167. package/dist/mcp/tools/webhooks.d.ts +2 -0
  168. package/dist/mcp/tools/webhooks.js +34 -0
  169. package/dist/mcp/transport/http-limits.d.ts +17 -0
  170. package/dist/mcp/transport/http-limits.js +92 -0
  171. package/dist/mcp/transport/stdio.d.ts +23 -0
  172. package/dist/mcp/transport/stdio.js +6 -0
  173. package/dist/mcp/transport/streamable-http.d.ts +34 -0
  174. package/dist/mcp/transport/streamable-http.js +158 -0
  175. package/dist/mcp/unattended-schedules.d.ts +15 -0
  176. package/dist/mcp/unattended-schedules.js +21 -0
  177. package/dist/mcp/webhooks/ingress.d.ts +17 -0
  178. package/dist/mcp/webhooks/ingress.js +33 -0
  179. package/dist/observability/config.d.ts +6 -0
  180. package/dist/observability/config.js +20 -0
  181. package/dist/observability/metrics-server.d.ts +11 -0
  182. package/dist/observability/metrics-server.js +47 -0
  183. package/dist/observability/metrics.d.ts +36 -0
  184. package/dist/observability/metrics.js +158 -0
  185. package/dist/providers/auth/authorization-server.d.ts +41 -0
  186. package/dist/providers/auth/authorization-server.js +1 -0
  187. package/dist/providers/auth/delegating.d.ts +44 -0
  188. package/dist/providers/auth/delegating.js +105 -0
  189. package/dist/providers/auth/index.d.ts +11 -0
  190. package/dist/providers/auth/index.js +20 -0
  191. package/dist/providers/auth/release-gate.d.ts +1 -0
  192. package/dist/providers/auth/release-gate.js +4 -0
  193. package/dist/providers/auth/self-hosted.d.ts +78 -0
  194. package/dist/providers/auth/self-hosted.js +377 -0
  195. package/dist/providers/auth/subject.d.ts +1 -0
  196. package/dist/providers/auth/subject.js +6 -0
  197. package/dist/providers/auth/types.d.ts +63 -0
  198. package/dist/providers/auth/types.js +22 -0
  199. package/dist/providers/coding-proxy/deny-port.d.ts +5 -0
  200. package/dist/providers/coding-proxy/deny-port.js +45 -0
  201. package/dist/providers/coding-proxy/environment-credentials.d.ts +7 -0
  202. package/dist/providers/coding-proxy/environment-credentials.js +16 -0
  203. package/dist/providers/coding-proxy/index.d.ts +7 -0
  204. package/dist/providers/coding-proxy/index.js +7 -0
  205. package/dist/providers/coding-proxy/memory-ledger.d.ts +17 -0
  206. package/dist/providers/coding-proxy/memory-ledger.js +103 -0
  207. package/dist/providers/coding-proxy/metering.d.ts +20 -0
  208. package/dist/providers/coding-proxy/metering.js +161 -0
  209. package/dist/providers/coding-proxy/prisma-ledger.d.ts +15 -0
  210. package/dist/providers/coding-proxy/prisma-ledger.js +213 -0
  211. package/dist/providers/coding-proxy/proxy.d.ts +75 -0
  212. package/dist/providers/coding-proxy/proxy.js +721 -0
  213. package/dist/providers/coding-proxy/runtime.d.ts +18 -0
  214. package/dist/providers/coding-proxy/runtime.js +39 -0
  215. package/dist/providers/coding-proxy/secure-fetch.d.ts +9 -0
  216. package/dist/providers/coding-proxy/secure-fetch.js +72 -0
  217. package/dist/providers/coding-proxy/server.d.ts +17 -0
  218. package/dist/providers/coding-proxy/server.js +139 -0
  219. package/dist/providers/coding-proxy/types.d.ts +99 -0
  220. package/dist/providers/coding-proxy/types.js +1 -0
  221. package/dist/providers/datastore/index.d.ts +2 -0
  222. package/dist/providers/datastore/index.js +2 -0
  223. package/dist/providers/datastore/postgres.d.ts +20 -0
  224. package/dist/providers/datastore/postgres.js +130 -0
  225. package/dist/providers/datastore/scoped.d.ts +10 -0
  226. package/dist/providers/datastore/scoped.js +46 -0
  227. package/dist/providers/datastore/types.d.ts +31 -0
  228. package/dist/providers/datastore/types.js +7 -0
  229. package/dist/providers/email/types.d.ts +46 -0
  230. package/dist/providers/email/types.js +8 -0
  231. package/dist/providers/engine/index.d.ts +1 -0
  232. package/dist/providers/engine/index.js +1 -0
  233. package/dist/providers/engine/types.d.ts +67 -0
  234. package/dist/providers/engine/types.js +16 -0
  235. package/dist/providers/executor/build.d.ts +6 -0
  236. package/dist/providers/executor/build.js +15 -0
  237. package/dist/providers/executor/composition.d.ts +14 -0
  238. package/dist/providers/executor/composition.js +123 -0
  239. package/dist/providers/executor/container.d.ts +184 -0
  240. package/dist/providers/executor/container.js +936 -0
  241. package/dist/providers/executor/dbos-status.d.ts +50 -0
  242. package/dist/providers/executor/dbos-status.js +64 -0
  243. package/dist/providers/executor/dbos.d.ts +53 -0
  244. package/dist/providers/executor/dbos.js +269 -0
  245. package/dist/providers/executor/in-process.d.ts +18 -0
  246. package/dist/providers/executor/in-process.js +38 -0
  247. package/dist/providers/executor/index.d.ts +8 -0
  248. package/dist/providers/executor/index.js +8 -0
  249. package/dist/providers/executor/routing.d.ts +48 -0
  250. package/dist/providers/executor/routing.js +70 -0
  251. package/dist/providers/executor/types.d.ts +55 -0
  252. package/dist/providers/executor/types.js +1 -0
  253. package/dist/providers/index.d.ts +43 -0
  254. package/dist/providers/index.js +18 -0
  255. package/dist/providers/jobs/docker-isolation.d.ts +166 -0
  256. package/dist/providers/jobs/docker-isolation.js +724 -0
  257. package/dist/providers/jobs/docker.d.ts +127 -0
  258. package/dist/providers/jobs/docker.js +1027 -0
  259. package/dist/providers/jobs/fake-kubernetes-api.d.ts +98 -0
  260. package/dist/providers/jobs/fake-kubernetes-api.js +142 -0
  261. package/dist/providers/jobs/fake.d.ts +23 -0
  262. package/dist/providers/jobs/fake.js +132 -0
  263. package/dist/providers/jobs/kubernetes-api.d.ts +60 -0
  264. package/dist/providers/jobs/kubernetes-api.js +20 -0
  265. package/dist/providers/jobs/kubernetes-client.d.ts +36 -0
  266. package/dist/providers/jobs/kubernetes-client.js +171 -0
  267. package/dist/providers/jobs/kubernetes-dry-run-fixture.d.ts +49 -0
  268. package/dist/providers/jobs/kubernetes-dry-run-fixture.js +141 -0
  269. package/dist/providers/jobs/kubernetes-isolation.d.ts +147 -0
  270. package/dist/providers/jobs/kubernetes-isolation.js +607 -0
  271. package/dist/providers/jobs/kubernetes-platform.d.ts +177 -0
  272. package/dist/providers/jobs/kubernetes-platform.js +280 -0
  273. package/dist/providers/jobs/kubernetes-preflight.d.ts +59 -0
  274. package/dist/providers/jobs/kubernetes-preflight.js +339 -0
  275. package/dist/providers/jobs/kubernetes-witness.d.ts +10 -0
  276. package/dist/providers/jobs/kubernetes-witness.js +193 -0
  277. package/dist/providers/jobs/kubernetes.d.ts +127 -0
  278. package/dist/providers/jobs/kubernetes.js +874 -0
  279. package/dist/providers/jobs/safe-extract.d.ts +9 -0
  280. package/dist/providers/jobs/safe-extract.js +325 -0
  281. package/dist/providers/jobs/types.d.ts +78 -0
  282. package/dist/providers/jobs/types.js +6 -0
  283. package/dist/providers/jobs/workspace-swap.d.ts +2 -0
  284. package/dist/providers/jobs/workspace-swap.js +47 -0
  285. package/dist/providers/llm/anthropic.d.ts +13 -0
  286. package/dist/providers/llm/anthropic.js +21 -0
  287. package/dist/providers/llm/bedrock.d.ts +19 -0
  288. package/dist/providers/llm/bedrock.js +33 -0
  289. package/dist/providers/llm/claude-messages.d.ts +106 -0
  290. package/dist/providers/llm/claude-messages.js +157 -0
  291. package/dist/providers/llm/claude-provider.d.ts +40 -0
  292. package/dist/providers/llm/claude-provider.js +31 -0
  293. package/dist/providers/llm/index.d.ts +10 -0
  294. package/dist/providers/llm/index.js +10 -0
  295. package/dist/providers/llm/openai.d.ts +23 -0
  296. package/dist/providers/llm/openai.js +156 -0
  297. package/dist/providers/llm/pricing-anthropic.d.ts +4 -0
  298. package/dist/providers/llm/pricing-anthropic.js +30 -0
  299. package/dist/providers/llm/pricing-bedrock-claude.d.ts +12 -0
  300. package/dist/providers/llm/pricing-bedrock-claude.js +37 -0
  301. package/dist/providers/llm/pricing-core.d.ts +26 -0
  302. package/dist/providers/llm/pricing-core.js +17 -0
  303. package/dist/providers/llm/pricing.d.ts +30 -0
  304. package/dist/providers/llm/pricing.js +74 -0
  305. package/dist/providers/llm/registration.d.ts +8 -0
  306. package/dist/providers/llm/registration.js +39 -0
  307. package/dist/providers/llm/routing.d.ts +25 -0
  308. package/dist/providers/llm/routing.js +32 -0
  309. package/dist/providers/llm/types.d.ts +85 -0
  310. package/dist/providers/llm/types.js +12 -0
  311. package/dist/providers/memory/index.d.ts +2 -0
  312. package/dist/providers/memory/index.js +2 -0
  313. package/dist/providers/memory/postgres.d.ts +20 -0
  314. package/dist/providers/memory/postgres.js +72 -0
  315. package/dist/providers/memory/types.d.ts +32 -0
  316. package/dist/providers/memory/types.js +17 -0
  317. package/dist/providers/secrets/app-key.d.ts +8 -0
  318. package/dist/providers/secrets/app-key.js +50 -0
  319. package/dist/providers/secrets/index.d.ts +10 -0
  320. package/dist/providers/secrets/index.js +13 -0
  321. package/dist/providers/secrets/transfer-envelope.d.ts +12 -0
  322. package/dist/providers/secrets/transfer-envelope.js +35 -0
  323. package/dist/providers/secrets/types.d.ts +18 -0
  324. package/dist/providers/secrets/types.js +12 -0
  325. package/dist/providers/storage/types.d.ts +18 -0
  326. package/dist/providers/storage/types.js +7 -0
  327. package/dist/providers/vcs/git.d.ts +105 -0
  328. package/dist/providers/vcs/git.js +710 -0
  329. package/dist/providers/vcs/github.d.ts +105 -0
  330. package/dist/providers/vcs/github.js +394 -0
  331. package/dist/providers/vcs/index.d.ts +6 -0
  332. package/dist/providers/vcs/index.js +26 -0
  333. package/dist/providers/vcs/types.d.ts +118 -0
  334. package/dist/providers/vcs/types.js +1 -0
  335. package/dist/sandbox/bounded-json.d.ts +2 -0
  336. package/dist/sandbox/bounded-json.js +36 -0
  337. package/dist/sandbox/bridge.d.ts +15 -0
  338. package/dist/sandbox/bridge.js +78 -0
  339. package/dist/sandbox/eval-core.d.ts +37 -0
  340. package/dist/sandbox/eval-core.js +159 -0
  341. package/dist/sandbox/fetch-policy.d.ts +24 -0
  342. package/dist/sandbox/fetch-policy.js +96 -0
  343. package/dist/sandbox/generated/zod-to-json-schema.bundle.js +5 -0
  344. package/dist/sandbox/generated/zod.bundle.js +1 -0
  345. package/dist/sandbox/host-functions.d.ts +29 -0
  346. package/dist/sandbox/host-functions.js +168 -0
  347. package/dist/sandbox/limits.d.ts +39 -0
  348. package/dist/sandbox/limits.js +39 -0
  349. package/dist/sandbox/parser-worker/__fixtures__/crash.d.ts +1 -0
  350. package/dist/sandbox/parser-worker/__fixtures__/crash.js +6 -0
  351. package/dist/sandbox/parser-worker/__fixtures__/echo.d.ts +1 -0
  352. package/dist/sandbox/parser-worker/__fixtures__/echo.js +5 -0
  353. package/dist/sandbox/parser-worker/__fixtures__/reject.d.ts +1 -0
  354. package/dist/sandbox/parser-worker/__fixtures__/reject.js +5 -0
  355. package/dist/sandbox/parser-worker/__fixtures__/spin-forever.d.ts +1 -0
  356. package/dist/sandbox/parser-worker/__fixtures__/spin-forever.js +11 -0
  357. package/dist/sandbox/parser-worker/pool.d.ts +13 -0
  358. package/dist/sandbox/parser-worker/pool.js +111 -0
  359. package/dist/sandbox/parser-worker/worker.d.ts +14 -0
  360. package/dist/sandbox/parser-worker/worker.js +63 -0
  361. package/dist/sandbox/pii-redaction.d.ts +1 -0
  362. package/dist/sandbox/pii-redaction.js +29 -0
  363. package/dist/sandbox/prelude.d.ts +26 -0
  364. package/dist/sandbox/prelude.js +408 -0
  365. package/dist/sandbox/quickjs-module.d.ts +13 -0
  366. package/dist/sandbox/quickjs-module.js +19 -0
  367. package/dist/sandbox/run-in-sandbox.d.ts +34 -0
  368. package/dist/sandbox/run-in-sandbox.js +53 -0
  369. package/dist/sandbox/safe-fetch.d.ts +26 -0
  370. package/dist/sandbox/safe-fetch.js +140 -0
  371. package/dist/sandbox/tool-capabilities.d.ts +66 -0
  372. package/dist/sandbox/tool-capabilities.js +103 -0
  373. package/dist/sandbox/zod-params.d.ts +22 -0
  374. package/dist/sandbox/zod-params.js +60 -0
  375. package/dist/serve.d.ts +14 -0
  376. package/dist/serve.js +54 -0
  377. package/dist/wardby-bin.d.ts +2 -0
  378. package/dist/wardby-bin.js +20 -0
  379. package/package.json +123 -0
  380. package/prisma/migrations/20260905000000_init/migration.sql +34 -0
  381. package/prisma/migrations/20260905010000_scheduler_durability/migration.sql +31 -0
  382. package/prisma/migrations/20260905020000_tools_multiturn/migration.sql +47 -0
  383. package/prisma/migrations/20260905030000_cache_tool_json_schema/migration.sql +13 -0
  384. package/prisma/migrations/20260906000000_mcp_phase4/migration.sql +115 -0
  385. package/prisma/migrations/20260906010000_run_final_text/migration.sql +7 -0
  386. package/prisma/migrations/20260906020000_run_turns/migration.sql +6 -0
  387. package/prisma/migrations/20260906030000_task_principal_id/migration.sql +5 -0
  388. package/prisma/migrations/20260906040000_secure_self_hosted_oauth/migration.sql +67 -0
  389. package/prisma/migrations/20260906050000_phase5_coding_agents/migration.sql +52 -0
  390. package/prisma/migrations/20260906060000_tool_capability_scoping/migration.sql +22 -0
  391. package/prisma/migrations/20260906070000_datastore_pii_flag/migration.sql +6 -0
  392. package/prisma/migrations/20260906080000_secret_elicitation_outcomes/migration.sql +13 -0
  393. package/prisma/migrations/20260907010000_coding_proxy_ledger/migration.sql +57 -0
  394. package/prisma/migrations/20260907020000_budget_groups/migration.sql +24 -0
  395. package/prisma/migrations/20260907030000_coding_webhook_task_override/migration.sql +4 -0
  396. package/prisma/migrations/20260908010000_run_execution_backend/migration.sql +2 -0
  397. package/prisma/migrations/20260908020000_coding_run_audit_metadata/migration.sql +7 -0
  398. package/prisma/migrations/20260910010000_agent_secret_bound_name/migration.sql +16 -0
  399. package/prisma/migrations/20260911010000_coding_worker_toolchains/migration.sql +5 -0
  400. package/prisma/migrations/20260912010000_coding_provider_contract/migration.sql +24 -0
  401. package/prisma/migrations/20260912020000_coding_proxy_protocol/migration.sql +10 -0
  402. package/prisma/migrations/20260912030000_agent_memory/migration.sql +18 -0
  403. package/prisma/migrations/20260912040000_named_shared_datastores/migration.sql +68 -0
  404. package/prisma/migrations/20260913000000_run_trigger_webhook/migration.sql +2 -0
  405. package/prisma/migrations/20260913010000_agent_sub_agent/migration.sql +32 -0
  406. package/prisma/migrations/20260913020000_run_task_override/migration.sql +5 -0
  407. package/prisma/migrations/20260913030000_coding_run_root/migration.sql +11 -0
  408. package/prisma/migrations/20260922010000_coding_run_queued_at/migration.sql +8 -0
  409. package/prisma/migrations/20260922020000_coding_workspace_disk/migration.sql +8 -0
  410. package/prisma/migrations/migration_lock.toml +3 -0
  411. package/prisma/schema.prisma +723 -0
  412. package/scripts/git-askpass.sh +11 -0
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The coding concurrency queue's driver. claimProvisioning (container.ts)
3
+ * refuses a slot and sets CodingRun.queuedAt when CODING_MAX_CONCURRENT
4
+ * coding runs already hold one; this module later:
5
+ *
6
+ * 1. fails queued runs that waited longer than CODING_QUEUE_TIMEOUT_SEC
7
+ * (coding_queue_timeout), with a conditional update so concurrent
8
+ * drains never double-fail a run;
9
+ * 2. starts the oldest queued runs, at most as many as there are free
10
+ * slots, so order stays FIFO in practice. Starting is fire-and-forget,
11
+ * like dispatchRun: Executor.start resolves only when the run ends.
12
+ * A run that loses a slot race is simply re-queued by claimProvisioning.
13
+ *
14
+ * Called on the scheduler leader's tick and immediately in any process
15
+ * whose coding run just finished (ContainerExecutor onSlotReleased).
16
+ */
17
+ import type { PrismaClient } from "@prisma/client";
18
+ import type { Executor } from "../providers/executor/types.js";
19
+ export declare const CODING_QUEUE_TIMEOUT_ERROR = "coding_queue_timeout";
20
+ export type CodingQueueDb = Pick<PrismaClient, "run" | "codingRun" | "$transaction">;
21
+ export interface DrainCodingQueueOptions {
22
+ db: CodingQueueDb;
23
+ executor: Executor;
24
+ maxConcurrent: number;
25
+ queueTimeoutSec: number;
26
+ now?: () => Date;
27
+ }
28
+ export interface DrainCodingQueueResult {
29
+ timedOut: number;
30
+ started: string[];
31
+ }
32
+ export declare function drainCodingQueue(options: DrainCodingQueueOptions): Promise<DrainCodingQueueResult>;
@@ -0,0 +1,54 @@
1
+ import { markRunFailedFromExecutorError } from "./dispatch.js";
2
+ import { logger } from "./logger.js";
3
+ const queueLog = logger.child({ module: "coding-queue" });
4
+ export const CODING_QUEUE_TIMEOUT_ERROR = "coding_queue_timeout";
5
+ export async function drainCodingQueue(options) {
6
+ const { db, executor } = options;
7
+ const now = options.now?.() ?? new Date();
8
+ const cutoff = new Date(now.getTime() - options.queueTimeoutSec * 1000);
9
+ let timedOut = 0;
10
+ const expired = await db.codingRun.findMany({
11
+ where: { queuedAt: { lt: cutoff }, run: { status: "pending" } },
12
+ select: { runId: true },
13
+ });
14
+ for (const { runId } of expired) {
15
+ // Both writes commit together: a run is only ever observed with
16
+ // status=failed/error=coding_queue_timeout and no matching
17
+ // failureCategory, never one without the other.
18
+ const didTimeOut = await db.$transaction(async (tx) => {
19
+ const failed = await tx.run.updateMany({
20
+ where: { id: runId, status: "pending" },
21
+ data: { status: "failed", error: CODING_QUEUE_TIMEOUT_ERROR, finishedAt: now },
22
+ });
23
+ if (failed.count !== 1)
24
+ return false;
25
+ await tx.codingRun.update({ where: { runId }, data: { failureCategory: CODING_QUEUE_TIMEOUT_ERROR } });
26
+ return true;
27
+ });
28
+ if (didTimeOut)
29
+ timedOut += 1;
30
+ }
31
+ const active = await db.codingRun.count({
32
+ where: { jobBackend: { not: null }, run: { status: { in: ["pending", "running"] } } },
33
+ });
34
+ const free = options.maxConcurrent - active;
35
+ if (free <= 0)
36
+ return { timedOut, started: [] };
37
+ const next = await db.codingRun.findMany({
38
+ where: { queuedAt: { not: null }, jobBackend: null, run: { status: "pending" } },
39
+ // runId breaks queuedAt ties, matching claimProvisioning's queue order.
40
+ orderBy: [{ queuedAt: "asc" }, { runId: "asc" }],
41
+ take: free,
42
+ select: { runId: true },
43
+ });
44
+ for (const { runId } of next) {
45
+ // Promise.resolve().then(...) defers executor.start's call itself into
46
+ // the microtask queue, so even a synchronous throw from a misbehaving
47
+ // Executor implementation lands in this .catch instead of escaping
48
+ // drainCodingQueue's own call stack.
49
+ void Promise.resolve()
50
+ .then(() => executor.start(runId))
51
+ .catch((err) => markRunFailedFromExecutorError(db, runId, err).catch((err2) => queueLog.error({ err: err2, runId }, "failed to persist queued-run start failure")));
52
+ }
53
+ return { timedOut, started: next.map((row) => row.runId) };
54
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Cron window computation for the scheduler — pure wrapper around `croner`
3
+ * so the due-window / DST logic is unit-testable without a live clock or DB.
4
+ */
5
+ export interface DueCheckInput {
6
+ schedule: string;
7
+ timezone: string;
8
+ /** Start of the most recent window this agent already fired for, or null if never. */
9
+ lastScheduledAt: Date | null;
10
+ now: Date;
11
+ }
12
+ /**
13
+ * The start of the most recent due window at/before `now` that hasn't
14
+ * already been fired for, or `null` if not due.
15
+ *
16
+ * Only ever returns the single latest window — this is what implements
17
+ * "skip, don't backfill": if several windows elapsed since
18
+ * `lastScheduledAt` (e.g. the scheduler was down), the caller advances
19
+ * `lastScheduledAt` straight to this value, abandoning the earlier ones.
20
+ *
21
+ * Boundary note: croner's `previousRuns` treats its reference instant as
22
+ * excluded (strictly-before), so a `now` that lands on the exact
23
+ * millisecond of a scheduled tick reports that tick as not-yet-due until a
24
+ * subsequent check. Irrelevant in practice at a multi-second tick
25
+ * interval, but worth knowing if this is ever called with a synthetic
26
+ * `now` that lands exactly on a boundary.
27
+ */
28
+ export declare function dueWindow(input: DueCheckInput): Date | null;
29
+ export declare function isDue(input: DueCheckInput): boolean;
30
+ /** Throws a descriptive error for an invalid cron expression or timezone. */
31
+ export declare function validateCronExpression(schedule: string, timezone: string): void;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Cron window computation for the scheduler — pure wrapper around `croner`
3
+ * so the due-window / DST logic is unit-testable without a live clock or DB.
4
+ */
5
+ import { Cron } from "croner";
6
+ /**
7
+ * The start of the most recent due window at/before `now` that hasn't
8
+ * already been fired for, or `null` if not due.
9
+ *
10
+ * Only ever returns the single latest window — this is what implements
11
+ * "skip, don't backfill": if several windows elapsed since
12
+ * `lastScheduledAt` (e.g. the scheduler was down), the caller advances
13
+ * `lastScheduledAt` straight to this value, abandoning the earlier ones.
14
+ *
15
+ * Boundary note: croner's `previousRuns` treats its reference instant as
16
+ * excluded (strictly-before), so a `now` that lands on the exact
17
+ * millisecond of a scheduled tick reports that tick as not-yet-due until a
18
+ * subsequent check. Irrelevant in practice at a multi-second tick
19
+ * interval, but worth knowing if this is ever called with a synthetic
20
+ * `now` that lands exactly on a boundary.
21
+ */
22
+ export function dueWindow(input) {
23
+ const { schedule, timezone, lastScheduledAt, now } = input;
24
+ const job = new Cron(schedule, { timezone });
25
+ const [mostRecent] = job.previousRuns(1, now);
26
+ if (!mostRecent) {
27
+ return null;
28
+ }
29
+ if (lastScheduledAt && mostRecent.getTime() <= lastScheduledAt.getTime()) {
30
+ return null;
31
+ }
32
+ return mostRecent;
33
+ }
34
+ export function isDue(input) {
35
+ return dueWindow(input) !== null;
36
+ }
37
+ /** Throws a descriptive error for an invalid cron expression or timezone. */
38
+ export function validateCronExpression(schedule, timezone) {
39
+ // The Cron constructor validates the pattern synchronously, but an
40
+ // invalid timezone only throws once a run is actually computed.
41
+ const job = new Cron(schedule, { timezone });
42
+ job.nextRun();
43
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Named datastores shared across agents: an owned resource (like Secret)
3
+ * attached to agents via AgentDatastore, addressed at sandbox point-of-use
4
+ * by a per-attachment `boundName` (sharedDatastore.get(boundName, key)) —
5
+ * same boundName convention as AgentSecret.
6
+ */
7
+ import type { PrismaClient, Datastore as DatastoreRow } from "@prisma/client";
8
+ import type { Datastore, DatastoreSetOptions, DatastoreValue } from "../providers/datastore/types.js";
9
+ export type DatastoreMetadata = Pick<DatastoreRow, "id" | "name" | "ownerId" | "createdAt" | "updatedAt">;
10
+ export interface SharedDatastoreAccessor {
11
+ get(boundName: string, key: string): Promise<DatastoreValue | undefined>;
12
+ set(boundName: string, key: string, value: DatastoreValue, opts?: DatastoreSetOptions): Promise<void>;
13
+ delete(boundName: string, key: string): Promise<void>;
14
+ list(boundName: string, prefix?: string): Promise<string[]>;
15
+ }
16
+ /** Creates a named datastore owned by `ownerId`. */
17
+ export declare function createDatastore(name: string, ownerId: string, db: PrismaClient): Promise<DatastoreRow>;
18
+ export declare function listDatastores(ownerId: string, db: PrismaClient): Promise<DatastoreMetadata[]>;
19
+ export declare function deleteDatastore(datastoreId: string, db: PrismaClient): Promise<void>;
20
+ /** Attaches a datastore (by id — the caller already resolved/verified ownership) to an agent under `boundName`. */
21
+ export declare function attachDatastore(agentId: string, datastoreId: string, db: PrismaClient, boundName: string): Promise<void>;
22
+ /** Detaches by point-of-use name — (agentId, boundName) uniquely identifies the edge. */
23
+ export declare function detachDatastore(agentId: string, boundName: string, db: PrismaClient): Promise<void>;
24
+ /**
25
+ * Resolves `boundName` to the Datastore attached to this agent under that
26
+ * name, then delegates to its *Shared methods. A `boundName` with no
27
+ * attachment behaves like a cache-miss for get/list (undefined/[]), and
28
+ * throws for set (a write must never silently no-op) — delete is a no-op,
29
+ * matching scopeDatastore's existing "disallowed delete is a no-op"
30
+ * convention for the private per-agent store.
31
+ */
32
+ export declare function buildSharedDatastoreAccessor(agentId: string, datastore: Datastore, db: Pick<PrismaClient, "agentDatastore">): SharedDatastoreAccessor;
33
+ /**
34
+ * Wraps a `SharedDatastoreAccessor` so every operation is confined to a
35
+ * bound name explicitly granted a prefix list, and within it, keys under one
36
+ * of those prefixes — mirrors scopeDatastore's OWASP LLM08 fix, per bound
37
+ * name instead of one flat prefix list. A boundName absent from the map
38
+ * denies everything, the same default-deny convention as an empty
39
+ * allowedDatastorePrefixes.
40
+ */
41
+ export declare function scopeSharedDatastoreAccessor(accessor: SharedDatastoreAccessor, allowedPrefixes: Readonly<Record<string, readonly string[]>>): SharedDatastoreAccessor;
@@ -0,0 +1,105 @@
1
+ import { boundedString } from "../sandbox/bounded-json.js";
2
+ /** Creates a named datastore owned by `ownerId`. */
3
+ export async function createDatastore(name, ownerId, db) {
4
+ boundedString(name, 1024);
5
+ return db.datastore.create({ data: { name, ownerId } });
6
+ }
7
+ export async function listDatastores(ownerId, db) {
8
+ const rows = await db.datastore.findMany({ where: { ownerId } });
9
+ return rows.map(({ id, name, ownerId: owner, createdAt, updatedAt }) => ({
10
+ id,
11
+ name,
12
+ ownerId: owner,
13
+ createdAt,
14
+ updatedAt,
15
+ }));
16
+ }
17
+ export async function deleteDatastore(datastoreId, db) {
18
+ await db.datastore.delete({ where: { id: datastoreId } });
19
+ }
20
+ /** Attaches a datastore (by id — the caller already resolved/verified ownership) to an agent under `boundName`. */
21
+ export async function attachDatastore(agentId, datastoreId, db, boundName) {
22
+ await db.agentDatastore.create({ data: { agentId, datastoreId, boundName } });
23
+ }
24
+ /** Detaches by point-of-use name — (agentId, boundName) uniquely identifies the edge. */
25
+ export async function detachDatastore(agentId, boundName, db) {
26
+ await db.agentDatastore.deleteMany({ where: { agentId, boundName } });
27
+ }
28
+ async function resolveDatastoreId(agentId, boundName, db) {
29
+ const attachment = await db.agentDatastore.findFirst({ where: { agentId, boundName } });
30
+ return attachment?.datastoreId;
31
+ }
32
+ /**
33
+ * Resolves `boundName` to the Datastore attached to this agent under that
34
+ * name, then delegates to its *Shared methods. A `boundName` with no
35
+ * attachment behaves like a cache-miss for get/list (undefined/[]), and
36
+ * throws for set (a write must never silently no-op) — delete is a no-op,
37
+ * matching scopeDatastore's existing "disallowed delete is a no-op"
38
+ * convention for the private per-agent store.
39
+ */
40
+ export function buildSharedDatastoreAccessor(agentId, datastore, db) {
41
+ return {
42
+ async get(boundName, key) {
43
+ const datastoreId = await resolveDatastoreId(agentId, boundName, db);
44
+ if (!datastoreId)
45
+ return undefined;
46
+ return datastore.getShared(datastoreId, key);
47
+ },
48
+ async set(boundName, key, value, opts) {
49
+ const datastoreId = await resolveDatastoreId(agentId, boundName, db);
50
+ if (!datastoreId)
51
+ throw new Error("datastore_not_bound");
52
+ await datastore.setShared(datastoreId, key, value, opts);
53
+ },
54
+ async delete(boundName, key) {
55
+ const datastoreId = await resolveDatastoreId(agentId, boundName, db);
56
+ if (!datastoreId)
57
+ return;
58
+ await datastore.deleteShared(datastoreId, key);
59
+ },
60
+ async list(boundName, prefix) {
61
+ const datastoreId = await resolveDatastoreId(agentId, boundName, db);
62
+ if (!datastoreId)
63
+ return [];
64
+ return datastore.listShared(datastoreId, prefix);
65
+ },
66
+ };
67
+ }
68
+ /**
69
+ * Wraps a `SharedDatastoreAccessor` so every operation is confined to a
70
+ * bound name explicitly granted a prefix list, and within it, keys under one
71
+ * of those prefixes — mirrors scopeDatastore's OWASP LLM08 fix, per bound
72
+ * name instead of one flat prefix list. A boundName absent from the map
73
+ * denies everything, the same default-deny convention as an empty
74
+ * allowedDatastorePrefixes.
75
+ */
76
+ export function scopeSharedDatastoreAccessor(accessor, allowedPrefixes) {
77
+ function isAllowed(boundName, key) {
78
+ const prefixes = allowedPrefixes[boundName];
79
+ return prefixes !== undefined && prefixes.some((prefix) => key.startsWith(prefix));
80
+ }
81
+ return {
82
+ async get(boundName, key) {
83
+ if (!isAllowed(boundName, key))
84
+ return undefined;
85
+ return accessor.get(boundName, key);
86
+ },
87
+ async set(boundName, key, value, opts) {
88
+ if (!isAllowed(boundName, key))
89
+ throw new Error("datastore_prefix_not_allowed");
90
+ await accessor.set(boundName, key, value, opts);
91
+ },
92
+ async delete(boundName, key) {
93
+ if (!isAllowed(boundName, key))
94
+ return;
95
+ await accessor.delete(boundName, key);
96
+ },
97
+ async list(boundName, prefix) {
98
+ const prefixes = allowedPrefixes[boundName];
99
+ if (!prefixes)
100
+ return [];
101
+ const keys = await accessor.list(boundName, prefix);
102
+ return keys.filter((key) => prefixes.some((p) => key.startsWith(p)));
103
+ },
104
+ };
105
+ }
@@ -0,0 +1,2 @@
1
+ import { PrismaClient } from "@prisma/client";
2
+ export declare const prisma: PrismaClient<import("@prisma/client").Prisma.PrismaClientOptions, never, import("@prisma/client/runtime/library").DefaultArgs>;
@@ -0,0 +1,2 @@
1
+ import { PrismaClient } from "@prisma/client";
2
+ export const prisma = new PrismaClient();
@@ -0,0 +1,81 @@
1
+ import type { Prisma, PrismaClient, Run, RunTrigger, Task } from "@prisma/client";
2
+ import type { Executor } from "../providers/executor/types.js";
3
+ export type DispatchDb = Pick<PrismaClient, "agent" | "run" | "codingRun" | "task" | "webhook" | "$transaction" | "$queryRaw">;
4
+ type DispatchTx = Pick<Prisma.TransactionClient, "agent" | "run" | "codingRun" | "task" | "webhook" | "$queryRaw">;
5
+ type DispatchAgent = Prisma.AgentGetPayload<{
6
+ include: {
7
+ codingProfile: true;
8
+ };
9
+ }>;
10
+ export interface DispatchRunOptions {
11
+ db: DispatchDb;
12
+ executor: Executor;
13
+ agentId: string;
14
+ trigger?: RunTrigger;
15
+ codingTask?: string;
16
+ codingBaseRef?: string;
17
+ /**
18
+ * Revision-in-place: the id of a prior CodingRun whose branch/PR this
19
+ * dispatch should push a new
20
+ * commit onto instead of opening a fresh branch. Resolved and verified
21
+ * here against the database (same repository, and it actually reached a
22
+ * PR-opening outcome) -- never trusted as a raw branch name, and
23
+ * deliberately not restricted to the same agent (cross-role continuation,
24
+ * e.g. plan -> implement on one PR, is allowed by design). Mutually
25
+ * exclusive with `codingBaseRef`. Coding agents only.
26
+ */
27
+ continuesCodingRunId?: string;
28
+ now?: Date;
29
+ lockAgent?: boolean;
30
+ task?: {
31
+ principalId: string;
32
+ ttlMs: number;
33
+ };
34
+ beforePersist?: (tx: DispatchTx, agent: DispatchAgent) => Promise<boolean>;
35
+ /** Sub-agent dispatch (see AgentSubAgent): links this run into a run tree. */
36
+ parentRunId?: string;
37
+ grantedParentMemoryKeys?: string[];
38
+ /**
39
+ * Per-run task text for a NATIVE agent (appended to its systemPrompt at
40
+ * load time — see runner.ts). The native equivalent of `codingTask`;
41
+ * mutually exclusive with it, since a native run has no CodingRun.
42
+ */
43
+ taskOverride?: string;
44
+ /**
45
+ * Overrides `agent.budgetUsd` for this run's coding budget reservation —
46
+ * used by sub-agent dispatch to apply the run-tree's tightened effective
47
+ * budget (see src/core/budget-groups.ts) instead of the agent's raw
48
+ * per-run ceiling, the same composability already applied to native runs.
49
+ * Ignored for native agents, which compute their own effective budget
50
+ * inside executeRun's load step.
51
+ */
52
+ budgetUsdOverride?: number;
53
+ /**
54
+ * Default (false/undefined): fire-and-forget, matching every existing
55
+ * caller (webhook ingress, trigger_agent, the scheduler) — none of them
56
+ * want to block on a potentially long-running coding container. Set true
57
+ * only when the caller genuinely wants to await the run's terminal state,
58
+ * e.g. a sub-agent dispatch tool blocking the parent's own turn.
59
+ */
60
+ awaitExecution?: boolean;
61
+ }
62
+ export interface DispatchRunResult {
63
+ run: Run;
64
+ task?: Task;
65
+ }
66
+ /**
67
+ * Record an executor-level failure on a run that never reached a terminal
68
+ * state itself. Deliberately idempotent: the same failure can arrive twice —
69
+ * DbosExecutor.start() calls this when the workflow handle rejects, and
70
+ * dispatchRun's `.catch` around that same `start()` call calls it again — and
71
+ * the conditional `updateMany` makes the second call match zero rows rather
72
+ * than overwrite whatever landed in between.
73
+ */
74
+ export declare function markRunFailedFromExecutorError(db: Pick<DispatchDb, "run">, runId: string, err: unknown): Promise<void>;
75
+ /**
76
+ * Persists every detached run input in one transaction, then invokes the
77
+ * executor only after commit. A null result means the caller's transactional
78
+ * claim was no longer valid (for example, a schedule was already claimed).
79
+ */
80
+ export declare function dispatchRun(options: DispatchRunOptions): Promise<DispatchRunResult | null>;
81
+ export {};
@@ -0,0 +1,184 @@
1
+ import { CODING_PROTOCOL_VERSION, CodingTaskInputSchema, normalizeGitHubRepository, publicCodingRunResult, } from "../coding/protocol.js";
2
+ import { assertCodingProviderModel } from "../coding/provider.js";
3
+ import { logger } from "./logger.js";
4
+ const dispatchLog = logger.child({ module: "dispatch" });
5
+ function isSerializationConflict(err) {
6
+ if (!err || typeof err !== "object")
7
+ return false;
8
+ const candidate = err;
9
+ return candidate.code === "P2034" || (candidate.code === "P2010" && candidate.meta?.code === "40001");
10
+ }
11
+ /**
12
+ * Record an executor-level failure on a run that never reached a terminal
13
+ * state itself. Deliberately idempotent: the same failure can arrive twice —
14
+ * DbosExecutor.start() calls this when the workflow handle rejects, and
15
+ * dispatchRun's `.catch` around that same `start()` call calls it again — and
16
+ * the conditional `updateMany` makes the second call match zero rows rather
17
+ * than overwrite whatever landed in between.
18
+ */
19
+ export async function markRunFailedFromExecutorError(db, runId, err) {
20
+ await db.run.updateMany({
21
+ where: { id: runId, status: { in: ["pending", "running"] } },
22
+ data: {
23
+ status: "failed",
24
+ error: err instanceof Error ? err.message : String(err),
25
+ finishedAt: new Date(),
26
+ },
27
+ });
28
+ }
29
+ /**
30
+ * Persists every detached run input in one transaction, then invokes the
31
+ * executor only after commit. A null result means the caller's transactional
32
+ * claim was no longer valid (for example, a schedule was already claimed).
33
+ */
34
+ export async function dispatchRun(options) {
35
+ const now = options.now ?? new Date();
36
+ const persistOnce = () => options.db.$transaction(async (tx) => {
37
+ if (options.lockAgent) {
38
+ const locked = await tx.$queryRaw `
39
+ SELECT "id" FROM "Agent" WHERE "id" = ${options.agentId} FOR UPDATE SKIP LOCKED
40
+ `;
41
+ if (locked.length === 0)
42
+ return null;
43
+ }
44
+ const agent = await tx.agent.findUnique({
45
+ where: { id: options.agentId },
46
+ include: { codingProfile: true },
47
+ });
48
+ if (!agent)
49
+ throw new Error(`Unknown agent "${options.agentId}".`);
50
+ if (options.beforePersist && !(await options.beforePersist(tx, agent)))
51
+ return null;
52
+ if (options.taskOverride !== undefined && agent.kind !== "native") {
53
+ throw new Error("taskOverride can only be supplied for a native agent.");
54
+ }
55
+ const run = await tx.run.create({
56
+ data: {
57
+ agentId: agent.id,
58
+ trigger: options.trigger ?? "manual",
59
+ executionManaged: true,
60
+ parentRunId: options.parentRunId,
61
+ grantedParentMemoryKeys: options.grantedParentMemoryKeys ?? [],
62
+ taskOverride: options.taskOverride,
63
+ },
64
+ });
65
+ if (agent.kind === "coding") {
66
+ if (!agent.codingProfile)
67
+ throw new Error(`Coding agent "${agent.id}" has no coding profile.`);
68
+ assertCodingProviderModel(agent.codingProfile.provider, agent.model);
69
+ const task = options.codingTask ?? agent.codingProfile.defaultTask;
70
+ if (!task)
71
+ throw new Error(`Coding agent "${agent.id}" requires a task.`);
72
+ const budgetUsd = options.budgetUsdOverride ?? Number(agent.budgetUsd);
73
+ let baseRef = options.codingBaseRef ?? agent.codingProfile.baseRef;
74
+ let headRef = `wardby/run-${run.id}`;
75
+ let continuationOf;
76
+ let rootCodingRunId;
77
+ if (options.continuesCodingRunId !== undefined) {
78
+ if (options.codingBaseRef !== undefined) {
79
+ throw new Error("continuesCodingRunId cannot be combined with codingBaseRef.");
80
+ }
81
+ const candidate = await tx.codingRun.findUnique({ where: { runId: options.continuesCodingRunId } });
82
+ if (!candidate)
83
+ throw new Error("Cannot continue an unknown coding run.");
84
+ const root = candidate.rootCodingRunId
85
+ ? await tx.codingRun.findUnique({ where: { runId: candidate.rootCodingRunId } })
86
+ : candidate;
87
+ if (!root)
88
+ throw new Error("Cannot continue an unknown coding run.");
89
+ if (normalizeGitHubRepository(root.repository) !== normalizeGitHubRepository(agent.codingProfile.repository)) {
90
+ throw new Error("Cannot continue a coding run from a different repository.");
91
+ }
92
+ const rootResult = publicCodingRunResult(root.result);
93
+ if (rootResult?.outcome !== "pull_request_opened" && rootResult?.outcome !== "pull_request_updated") {
94
+ throw new Error("Cannot continue a coding run that never opened a pull request.");
95
+ }
96
+ baseRef = root.baseRef;
97
+ headRef = root.headRef;
98
+ continuationOf = { runId: root.runId };
99
+ rootCodingRunId = root.runId;
100
+ }
101
+ const input = CodingTaskInputSchema.parse({
102
+ schemaVersion: CODING_PROTOCOL_VERSION,
103
+ runId: run.id,
104
+ repository: agent.codingProfile.repository,
105
+ baseRef,
106
+ headRef,
107
+ task,
108
+ model: agent.model,
109
+ budgetUsd,
110
+ deadlineAt: new Date(now.getTime() + agent.codingProfile.timeoutSec * 1000).toISOString(),
111
+ continuationOf,
112
+ });
113
+ const workerImage = options.executor.resolveCodingWorkerImage?.({
114
+ provider: agent.codingProfile.provider,
115
+ toolchain: agent.codingProfile.toolchain,
116
+ toolchainVersion: agent.codingProfile.toolchainVersion,
117
+ workerImageRef: agent.codingProfile.workerImageRef,
118
+ });
119
+ await tx.codingRun.create({
120
+ data: {
121
+ runId: run.id,
122
+ task: input.task,
123
+ repository: input.repository,
124
+ baseRef: input.baseRef,
125
+ headRef: input.headRef,
126
+ provider: agent.codingProfile.provider,
127
+ model: input.model,
128
+ timeoutSec: agent.codingProfile.timeoutSec,
129
+ allowedEgress: agent.codingProfile.allowedEgress,
130
+ protectedPaths: agent.codingProfile.protectedPaths,
131
+ workerImage,
132
+ budgetReservedUsd: budgetUsd,
133
+ rootCodingRunId,
134
+ workspaceDiskMb: agent.codingProfile.workspaceDiskMb,
135
+ },
136
+ });
137
+ }
138
+ else if (options.codingTask !== undefined ||
139
+ options.codingBaseRef !== undefined ||
140
+ options.continuesCodingRunId !== undefined) {
141
+ throw new Error("Coding overrides cannot be supplied for a native agent.");
142
+ }
143
+ const task = options.task
144
+ ? await tx.task.create({
145
+ data: {
146
+ kind: "run",
147
+ runId: run.id,
148
+ principalId: options.task.principalId,
149
+ status: "working",
150
+ ttlAt: new Date(now.getTime() + options.task.ttlMs),
151
+ },
152
+ })
153
+ : undefined;
154
+ return { run, task };
155
+ }, { isolationLevel: "Serializable" });
156
+ let persisted = null;
157
+ for (let attempt = 0; attempt < 3; attempt += 1) {
158
+ try {
159
+ persisted = await persistOnce();
160
+ break;
161
+ }
162
+ catch (err) {
163
+ if (!isSerializationConflict(err) || attempt === 2)
164
+ throw err;
165
+ }
166
+ }
167
+ if (!persisted)
168
+ return null;
169
+ const runStart = async () => {
170
+ try {
171
+ await options.executor.start(persisted.run.id);
172
+ }
173
+ catch (err) {
174
+ await markRunFailedFromExecutorError(options.db, persisted.run.id, err).catch((err2) => dispatchLog.error({ err: err2, runId: persisted.run.id }, "failed to persist executor start failure"));
175
+ }
176
+ };
177
+ if (options.awaitExecution) {
178
+ await runStart();
179
+ }
180
+ else {
181
+ void runStart();
182
+ }
183
+ return persisted;
184
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * The native multi-turn engine — the default (and, in Phase 3, only)
3
+ * `Engine` implementation. Owns the whole turn loop and the whole budget
4
+ * lifecycle: pre-flight refuse, cumulative pre-turn gate, mid-stream
5
+ * cutoff, graceful wind-down. See providers/engine/types.ts for why this
6
+ * lives behind a seam (a future LangGraph engine swaps in here) and why
7
+ * the budget guardrail must stay above whichever engine is running.
8
+ */
9
+ import type { Engine, EngineResult, EngineRunContext } from "../providers/engine/types.js";
10
+ export declare class NativeEngine implements Engine {
11
+ run(ctx: EngineRunContext): Promise<EngineResult>;
12
+ /**
13
+ * One final turn with tools disabled, checked against *remaining*
14
+ * budget and mid-stream-capped so it cannot overshoot. If remaining
15
+ * budget can't even afford this turn's estimated input, hard-stop
16
+ * without it — either way the terminal status is `budget_exhausted`.
17
+ */
18
+ private windDown;
19
+ private finish;
20
+ /**
21
+ * Streams one model turn, applying the mid-stream cutoff against the
22
+ * *cumulative* running total (this extends Phase 1's single-turn
23
+ * AbortController logic across the whole loop). Per-delta output-token
24
+ * counting mirrors runner.ts's O(delta)-per-event approach — see its
25
+ * comment for why summing per-delta counts beats re-tokenizing the
26
+ * whole accumulated output on every event.
27
+ */
28
+ private runOneTurn;
29
+ }