@wardby/cli 0.6.0 → 0.7.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 (576) hide show
  1. package/deploy/local/docker-compose.quickstart-coding.yml +13 -0
  2. package/dist/claude-coding-worker/driver.d.ts +21 -3
  3. package/dist/claude-coding-worker/driver.js +125 -17
  4. package/dist/claude-coding-worker/main.js +34 -6
  5. package/dist/claude-coding-worker/sdk.js +12 -1
  6. package/dist/claude-coding-worker/tool-socket.d.ts +27 -0
  7. package/dist/claude-coding-worker/tool-socket.js +111 -0
  8. package/dist/claude-coding-worker/transcript.d.ts +18 -0
  9. package/dist/claude-coding-worker/transcript.js +77 -0
  10. package/dist/cli-help.d.ts +1 -1
  11. package/dist/cli-help.js +1 -0
  12. package/dist/cli.js +24 -10
  13. package/dist/coding/continuation-wording.d.ts +35 -0
  14. package/dist/coding/continuation-wording.js +73 -0
  15. package/dist/coding/linked-issue-note.d.ts +23 -0
  16. package/dist/coding/linked-issue-note.js +47 -0
  17. package/dist/coding/merge-wording.d.ts +28 -0
  18. package/dist/coding/merge-wording.js +78 -0
  19. package/dist/coding/pr-description-note.d.ts +9 -0
  20. package/dist/coding/pr-description-note.js +32 -0
  21. package/dist/coding/profile.d.ts +46 -126
  22. package/dist/coding/profile.js +45 -4
  23. package/dist/coding/protected-path-wording.d.ts +8 -0
  24. package/dist/coding/protected-path-wording.js +1 -1
  25. package/dist/coding/protocol.d.ts +264 -346
  26. package/dist/coding/protocol.js +235 -12
  27. package/dist/coding/redact.d.ts +3 -0
  28. package/dist/coding/redact.js +12 -0
  29. package/dist/coding/registry/allowlist.d.ts +12 -0
  30. package/dist/coding/registry/allowlist.js +14 -1
  31. package/dist/coding/registry/audit-mode.d.ts +153 -0
  32. package/dist/coding/registry/audit-mode.js +304 -0
  33. package/dist/coding/registry/lockfile-shared.d.ts +36 -0
  34. package/dist/coding/registry/lockfile-shared.js +128 -0
  35. package/dist/coding/registry/lockfiles.js +1 -1
  36. package/dist/coding/registry/npm-lockfile-formats.d.ts +11 -0
  37. package/dist/coding/registry/npm-lockfile-formats.js +41 -0
  38. package/dist/coding/registry/npm-lockfile.d.ts +8 -4
  39. package/dist/coding/registry/npm-lockfile.js +4 -6
  40. package/dist/coding/registry/npm.js +78 -8
  41. package/dist/coding/registry/pnpm-lockfile.d.ts +17 -0
  42. package/dist/coding/registry/pnpm-lockfile.js +189 -0
  43. package/dist/coding/registry/pypi-plan.d.ts +50 -0
  44. package/dist/coding/registry/pypi-plan.js +406 -0
  45. package/dist/coding/registry/pypi-requirements.d.ts +58 -0
  46. package/dist/coding/registry/pypi-requirements.js +155 -0
  47. package/dist/coding/registry/pypi.d.ts +3 -27
  48. package/dist/coding/registry/pypi.js +40 -154
  49. package/dist/coding/registry/refusal-hints.d.ts +20 -0
  50. package/dist/coding/registry/refusal-hints.js +61 -0
  51. package/dist/coding/registry/refusal-wording.d.ts +11 -0
  52. package/dist/coding/registry/refusal-wording.js +56 -0
  53. package/dist/coding/registry/report.d.ts +70 -8
  54. package/dist/coding/registry/report.js +112 -5
  55. package/dist/coding/registry/types.d.ts +58 -8
  56. package/dist/coding/registry/yarn-lockfile.d.ts +30 -0
  57. package/dist/coding/registry/yarn-lockfile.js +367 -0
  58. package/dist/coding/services/catalog.d.ts +37 -351
  59. package/dist/coding/transcript/claude-code.d.ts +13 -0
  60. package/dist/coding/transcript/claude-code.js +273 -0
  61. package/dist/coding/transcript/download-protocol.d.ts +30 -0
  62. package/dist/coding/transcript/download-protocol.js +18 -0
  63. package/dist/coding/transcript/format.d.ts +20 -0
  64. package/dist/coding/transcript/format.js +32 -0
  65. package/dist/coding/transcript/recorder.d.ts +51 -0
  66. package/dist/coding/transcript/recorder.js +225 -0
  67. package/dist/coding/transcript/resume/checkout.d.ts +31 -0
  68. package/dist/coding/transcript/resume/checkout.js +203 -0
  69. package/dist/coding/transcript/resume/command.d.ts +17 -0
  70. package/dist/coding/transcript/resume/command.js +176 -0
  71. package/dist/coding/transcript/resume/download.d.ts +20 -0
  72. package/dist/coding/transcript/resume/download.js +153 -0
  73. package/dist/coding/transcript/resume/launch.d.ts +38 -0
  74. package/dist/coding/transcript/resume/launch.js +128 -0
  75. package/dist/coding/transcript/resume/session-file.d.ts +13 -0
  76. package/dist/coding/transcript/resume/session-file.js +79 -0
  77. package/dist/coding/transcript/resume/stand-in.d.ts +11 -0
  78. package/dist/coding/transcript/resume/stand-in.js +49 -0
  79. package/dist/coding-worker/artifact.d.ts +8 -3
  80. package/dist/coding-worker/artifact.js +3 -1
  81. package/dist/coding-worker/debug-trace.d.ts +6 -5
  82. package/dist/coding-worker/debug-trace.js +13 -18
  83. package/dist/coding-worker/dependency-cache.d.mts +229 -0
  84. package/dist/coding-worker/dependency-cache.mjs +2123 -0
  85. package/dist/coding-worker/driver.d.ts +4 -3
  86. package/dist/coding-worker/driver.js +116 -38
  87. package/dist/coding-worker/errors.d.ts +1 -0
  88. package/dist/coding-worker/errors.js +32 -0
  89. package/dist/coding-worker/task-note.d.ts +11 -0
  90. package/dist/coding-worker/task-note.js +13 -0
  91. package/dist/coding-worker/types.d.ts +11 -0
  92. package/dist/config/dependency-cache.d.ts +23 -0
  93. package/dist/config/dependency-cache.js +32 -0
  94. package/dist/config/providers.d.ts +12 -3
  95. package/dist/config/providers.js +55 -5
  96. package/dist/config/test-simulators.d.ts +82 -0
  97. package/dist/config/test-simulators.js +305 -0
  98. package/dist/core/async-context.d.ts +2 -0
  99. package/dist/core/async-context.js +12 -0
  100. package/dist/core/budget-events.d.ts +108 -0
  101. package/dist/core/budget-events.js +346 -0
  102. package/dist/core/budget-groups.d.ts +50 -1
  103. package/dist/core/budget-groups.js +236 -27
  104. package/dist/core/budget-stopped-head.d.ts +46 -0
  105. package/dist/core/budget-stopped-head.js +81 -0
  106. package/dist/core/budget-topup.d.ts +51 -0
  107. package/dist/core/budget-topup.js +156 -0
  108. package/dist/core/budget-wording.d.ts +16 -2
  109. package/dist/core/budget-wording.js +40 -2
  110. package/dist/core/coding-queue.d.ts +2 -1
  111. package/dist/core/coding-queue.js +7 -0
  112. package/dist/core/cron.d.ts +19 -1
  113. package/dist/core/cron.js +17 -0
  114. package/dist/core/datastores.js +1 -1
  115. package/dist/core/deferred-coding.d.ts +14 -0
  116. package/dist/core/deferred-coding.js +62 -0
  117. package/dist/core/delegation-child.d.ts +25 -0
  118. package/dist/core/delegation-child.js +37 -0
  119. package/dist/core/dispatch.d.ts +42 -0
  120. package/dist/core/dispatch.js +131 -56
  121. package/dist/core/engine-native.js +1 -1
  122. package/dist/core/error-code.d.ts +38 -0
  123. package/dist/core/error-code.js +168 -0
  124. package/dist/core/grants.d.ts +19 -6
  125. package/dist/core/grants.js +55 -6
  126. package/dist/core/host-events.d.ts +18 -2
  127. package/dist/core/host-events.js +284 -24
  128. package/dist/core/host-status.d.ts +85 -3
  129. package/dist/core/host-status.js +298 -20
  130. package/dist/core/issue-bridge.d.ts +50 -6
  131. package/dist/core/issue-bridge.js +124 -33
  132. package/dist/core/issue-events.d.ts +1 -1
  133. package/dist/core/issue-events.js +4 -10
  134. package/dist/core/issue-status.d.ts +1 -1
  135. package/dist/core/issue-status.js +54 -26
  136. package/dist/core/logger.d.ts +5 -0
  137. package/dist/core/logger.js +14 -1
  138. package/dist/core/lost-run-children.d.ts +6 -0
  139. package/dist/core/lost-run-children.js +32 -0
  140. package/dist/core/mark-ready.d.ts +34 -0
  141. package/dist/core/mark-ready.js +152 -0
  142. package/dist/core/markdown-code.d.ts +8 -0
  143. package/dist/core/markdown-code.js +107 -0
  144. package/dist/core/memory-tools.js +7 -0
  145. package/dist/core/merge-order-check.d.ts +2 -1
  146. package/dist/core/merge-order-check.js +8 -1
  147. package/dist/core/notification-templates.d.ts +1 -7
  148. package/dist/core/notification-templates.js +36 -2
  149. package/dist/core/pull-request-state-sync.js +2 -2
  150. package/dist/core/reconciler.d.ts +4 -3
  151. package/dist/core/reconciler.js +8 -1
  152. package/dist/core/related-pull-requests.d.ts +11 -3
  153. package/dist/core/related-pull-requests.js +27 -14
  154. package/dist/core/reply-parts.d.ts +21 -0
  155. package/dist/core/reply-parts.js +73 -0
  156. package/dist/core/review-fix.d.ts +1 -1
  157. package/dist/core/review-fix.js +2 -1
  158. package/dist/core/review-host-checks.d.ts +1 -1
  159. package/dist/core/review-host-tools.d.ts +3 -0
  160. package/dist/core/review-host-tools.js +99 -13
  161. package/dist/core/review-reads.d.ts +55 -0
  162. package/dist/core/review-reads.js +233 -0
  163. package/dist/core/run-status.d.ts +12 -0
  164. package/dist/core/run-status.js +12 -0
  165. package/dist/core/runner.d.ts +80 -34
  166. package/dist/core/runner.js +439 -144
  167. package/dist/core/schedule-start.d.ts +12 -0
  168. package/dist/core/schedule-start.js +14 -0
  169. package/dist/core/secret-broker-config.d.ts +10 -74
  170. package/dist/core/secret-guard.d.ts +46 -0
  171. package/dist/core/secret-guard.js +104 -0
  172. package/dist/core/secret-guarded-stores.d.ts +12 -0
  173. package/dist/core/secret-guarded-stores.js +41 -0
  174. package/dist/core/secret-spans.d.ts +50 -0
  175. package/dist/core/secret-spans.js +94 -0
  176. package/dist/core/stop-run.d.ts +46 -0
  177. package/dist/core/stop-run.js +61 -0
  178. package/dist/core/workflow-events.d.ts +14 -2
  179. package/dist/core/workflow-events.js +4 -0
  180. package/dist/core/workflow-run-events.d.ts +5 -3
  181. package/dist/core/workflow-run-events.js +43 -8
  182. package/dist/core/zod-json-schema.d.ts +4 -0
  183. package/dist/core/zod-json-schema.js +39 -0
  184. package/dist/core/zod3-window.d.ts +35 -0
  185. package/dist/core/zod3-window.js +124 -0
  186. package/dist/generated/prisma/browser.d.ts +34 -3
  187. package/dist/generated/prisma/client.d.ts +34 -3
  188. package/dist/generated/prisma/commonInputTypes.d.ts +220 -60
  189. package/dist/generated/prisma/enums.d.ts +10 -0
  190. package/dist/generated/prisma/enums.js +8 -0
  191. package/dist/generated/prisma/internal/class.d.ts +44 -0
  192. package/dist/generated/prisma/internal/class.js +4 -4
  193. package/dist/generated/prisma/internal/prismaNamespace.d.ts +428 -9
  194. package/dist/generated/prisma/internal/prismaNamespace.js +92 -1
  195. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +95 -0
  196. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +92 -1
  197. package/dist/generated/prisma/models/Agent.d.ts +106 -8
  198. package/dist/generated/prisma/models/AgentRepository.d.ts +42 -1
  199. package/dist/generated/prisma/models/CodingAgentProfile.d.ts +229 -1
  200. package/dist/generated/prisma/models/CodingProxyRequest.d.ts +207 -3
  201. package/dist/generated/prisma/models/CodingProxySession.d.ts +114 -1
  202. package/dist/generated/prisma/models/CodingRun.d.ts +1637 -1
  203. package/dist/generated/prisma/models/DependencySnapshot.d.ts +1668 -0
  204. package/dist/generated/prisma/models/DependencySnapshot.js +1 -0
  205. package/dist/generated/prisma/models/IssuePullRequest.d.ts +6 -2
  206. package/dist/generated/prisma/models/LocalReview.d.ts +40 -1
  207. package/dist/generated/prisma/models/NativeGatewayCall.d.ts +39 -1
  208. package/dist/generated/prisma/models/NativeGatewaySession.d.ts +35 -1
  209. package/dist/generated/prisma/models/RegistryAllowance.d.ts +41 -1
  210. package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +75 -1
  211. package/dist/generated/prisma/models/RegistryAuditRecord.d.ts +1364 -0
  212. package/dist/generated/prisma/models/RegistryAuditRecord.js +1 -0
  213. package/dist/generated/prisma/models/Run.d.ts +738 -2
  214. package/dist/generated/prisma/models/RunBudgetEvent.d.ts +1211 -0
  215. package/dist/generated/prisma/models/RunBudgetEvent.js +1 -0
  216. package/dist/generated/prisma/models/RunTranscript.d.ts +1339 -0
  217. package/dist/generated/prisma/models/RunTranscript.js +1 -0
  218. package/dist/generated/prisma/models.d.ts +4 -0
  219. package/dist/help-index.json +1320 -69
  220. package/dist/import/create.js +32 -3
  221. package/dist/import/neutral-schema.d.ts +50 -281
  222. package/dist/import/neutral-schema.js +5 -4
  223. package/dist/knowledge/check.d.ts +1 -1
  224. package/dist/knowledge/cli.d.ts +11 -1
  225. package/dist/knowledge/cli.js +37 -4
  226. package/dist/knowledge/concept.d.ts +3 -19
  227. package/dist/knowledge/location.d.ts +31 -0
  228. package/dist/knowledge/location.js +78 -0
  229. package/dist/mcp/auth/access.d.ts +10 -2
  230. package/dist/mcp/auth/access.js +15 -1
  231. package/dist/mcp/auth/ownership.d.ts +7 -7
  232. package/dist/mcp/auth/self-hosted/credentials.d.ts +3 -3
  233. package/dist/mcp/auth/self-hosted/session.d.ts +2 -2
  234. package/dist/mcp/host-events/github-ingress.d.ts +2 -0
  235. package/dist/mcp/host-events/github-ingress.js +2 -1
  236. package/dist/mcp/index.d.ts +6 -0
  237. package/dist/mcp/index.js +58 -10
  238. package/dist/mcp/server.d.ts +13 -0
  239. package/dist/mcp/server.js +33 -14
  240. package/dist/mcp/tools/agents.d.ts +4 -0
  241. package/dist/mcp/tools/agents.js +198 -16
  242. package/dist/mcp/tools/datastore.js +17 -8
  243. package/dist/mcp/tools/grants.js +9 -3
  244. package/dist/mcp/tools/memory.js +11 -1
  245. package/dist/mcp/tools/notification-channels.js +2 -2
  246. package/dist/mcp/tools/packages.d.ts +2 -0
  247. package/dist/mcp/tools/packages.js +195 -0
  248. package/dist/mcp/tools/repositories.js +18 -5
  249. package/dist/mcp/tools/run-access.d.ts +16 -0
  250. package/dist/mcp/tools/run-access.js +25 -0
  251. package/dist/mcp/tools/run-transcript-download.d.ts +30 -0
  252. package/dist/mcp/tools/run-transcript-download.js +129 -0
  253. package/dist/mcp/tools/run-transcript.d.ts +19 -0
  254. package/dist/mcp/tools/run-transcript.js +70 -0
  255. package/dist/mcp/tools/runs.js +34 -12
  256. package/dist/mcp/tools/scheduling.js +3 -1
  257. package/dist/mcp/tools/secret-elicitation-form.js +5 -0
  258. package/dist/mcp/tools/secret-elicitation-server.d.ts +6 -0
  259. package/dist/mcp/tools/secret-elicitation-server.js +10 -61
  260. package/dist/mcp/tools/secrets.js +2 -2
  261. package/dist/mcp/tools/stop-run.d.ts +2 -0
  262. package/dist/mcp/tools/stop-run.js +82 -0
  263. package/dist/mcp/tools/subagents.js +2 -2
  264. package/dist/mcp/tools/tools.d.ts +1 -1
  265. package/dist/mcp/tools/tools.js +25 -9
  266. package/dist/mcp/transport/stdio-link-host.d.ts +20 -0
  267. package/dist/mcp/transport/stdio-link-host.js +67 -0
  268. package/dist/mcp/transport/streamable-http.d.ts +13 -0
  269. package/dist/mcp/transport/streamable-http.js +41 -1
  270. package/dist/native-worker/docker-launcher.d.ts +38 -2
  271. package/dist/native-worker/docker-launcher.js +65 -5
  272. package/dist/native-worker/gateway-server.d.ts +41 -3
  273. package/dist/native-worker/gateway-server.js +124 -27
  274. package/dist/native-worker/gateway.d.ts +27 -1
  275. package/dist/native-worker/gateway.js +70 -4
  276. package/dist/native-worker/http-server.d.ts +3 -1
  277. package/dist/native-worker/http-server.js +11 -7
  278. package/dist/native-worker/http-transport.js +1 -1
  279. package/dist/native-worker/kubernetes-launcher.d.ts +7 -3
  280. package/dist/native-worker/kubernetes-launcher.js +43 -3
  281. package/dist/native-worker/launch.d.ts +18 -1
  282. package/dist/native-worker/launch.js +6 -1
  283. package/dist/native-worker/ledger.d.ts +18 -2
  284. package/dist/native-worker/ledger.js +57 -4
  285. package/dist/native-worker/loopback.js +3 -8
  286. package/dist/native-worker/main.d.ts +2 -1
  287. package/dist/native-worker/main.js +14 -3
  288. package/dist/native-worker/protocol.d.ts +129 -577
  289. package/dist/native-worker/protocol.js +47 -1
  290. package/dist/native-worker/sandbox-executor.d.ts +17 -2
  291. package/dist/native-worker/sandbox-executor.js +78 -4
  292. package/dist/native-worker/stderr-redaction.d.ts +77 -0
  293. package/dist/native-worker/stderr-redaction.js +237 -0
  294. package/dist/native-worker/stdio.d.ts +1 -1
  295. package/dist/native-worker/stdio.js +6 -5
  296. package/dist/native-worker/warm-pool.d.ts +2 -2
  297. package/dist/native-worker/warm-pool.js +4 -4
  298. package/dist/native-worker/worker.js +28 -2
  299. package/dist/observability/metrics.js +6 -1
  300. package/dist/providers/auth/self-hosted.d.ts +1 -1
  301. package/dist/providers/coding-proxy/dependency-cache/eviction.d.ts +29 -0
  302. package/dist/providers/coding-proxy/dependency-cache/eviction.js +87 -0
  303. package/dist/providers/coding-proxy/dependency-cache/key.d.ts +33 -0
  304. package/dist/providers/coding-proxy/dependency-cache/key.js +86 -0
  305. package/dist/providers/coding-proxy/dependency-cache/model-request-seal.d.ts +18 -0
  306. package/dist/providers/coding-proxy/dependency-cache/model-request-seal.js +40 -0
  307. package/dist/providers/coding-proxy/dependency-cache/prisma-store.d.ts +25 -0
  308. package/dist/providers/coding-proxy/dependency-cache/prisma-store.js +131 -0
  309. package/dist/providers/coding-proxy/dependency-cache/service.d.ts +70 -0
  310. package/dist/providers/coding-proxy/dependency-cache/service.js +409 -0
  311. package/dist/providers/coding-proxy/dependency-cache/store.d.ts +85 -0
  312. package/dist/providers/coding-proxy/dependency-cache/store.js +95 -0
  313. package/dist/providers/coding-proxy/hold.d.ts +32 -0
  314. package/dist/providers/coding-proxy/hold.js +86 -0
  315. package/dist/providers/coding-proxy/interrupted.d.ts +70 -0
  316. package/dist/providers/coding-proxy/interrupted.js +104 -0
  317. package/dist/providers/coding-proxy/memory-ledger.d.ts +15 -2
  318. package/dist/providers/coding-proxy/memory-ledger.js +84 -8
  319. package/dist/providers/coding-proxy/metering.d.ts +16 -0
  320. package/dist/providers/coding-proxy/metering.js +27 -4
  321. package/dist/providers/coding-proxy/prisma-ledger.d.ts +15 -2
  322. package/dist/providers/coding-proxy/prisma-ledger.js +179 -67
  323. package/dist/providers/coding-proxy/proxy.d.ts +14 -1
  324. package/dist/providers/coding-proxy/proxy.js +166 -14
  325. package/dist/providers/coding-proxy/registry/audit.d.ts +53 -10
  326. package/dist/providers/coding-proxy/registry/audit.js +94 -17
  327. package/dist/providers/coding-proxy/registry/lockfile-parse-worker.d.ts +36 -0
  328. package/dist/providers/coding-proxy/registry/lockfile-parse-worker.js +67 -0
  329. package/dist/providers/coding-proxy/registry/lockfile-parse.d.ts +16 -0
  330. package/dist/providers/coding-proxy/registry/lockfile-parse.js +110 -0
  331. package/dist/providers/coding-proxy/registry/plan.d.ts +9 -0
  332. package/dist/providers/coding-proxy/registry/plan.js +201 -48
  333. package/dist/providers/coding-proxy/registry/prisma-store.d.ts +11 -5
  334. package/dist/providers/coding-proxy/registry/prisma-store.js +120 -17
  335. package/dist/providers/coding-proxy/registry/service.d.ts +97 -16
  336. package/dist/providers/coding-proxy/registry/service.js +579 -112
  337. package/dist/providers/coding-proxy/registry/store.d.ts +84 -9
  338. package/dist/providers/coding-proxy/registry/store.js +54 -1
  339. package/dist/providers/coding-proxy/runtime.d.ts +4 -0
  340. package/dist/providers/coding-proxy/runtime.js +78 -16
  341. package/dist/providers/coding-proxy/server.d.ts +6 -1
  342. package/dist/providers/coding-proxy/server.js +45 -1
  343. package/dist/providers/coding-proxy/types.d.ts +65 -1
  344. package/dist/providers/datastore/types.d.ts +5 -0
  345. package/dist/providers/engine/types.d.ts +17 -2
  346. package/dist/providers/engine/types.js +17 -0
  347. package/dist/providers/executor/composition.d.ts +10 -3
  348. package/dist/providers/executor/composition.js +47 -7
  349. package/dist/providers/executor/container.d.ts +361 -8
  350. package/dist/providers/executor/container.js +1136 -91
  351. package/dist/providers/executor/dbos-status.d.ts +31 -0
  352. package/dist/providers/executor/dbos-status.js +64 -0
  353. package/dist/providers/executor/dbos.d.ts +50 -1
  354. package/dist/providers/executor/dbos.js +207 -20
  355. package/dist/providers/executor/deferred.d.ts +12 -24
  356. package/dist/providers/executor/deferred.js +10 -29
  357. package/dist/providers/executor/in-process.d.ts +9 -1
  358. package/dist/providers/executor/in-process.js +14 -4
  359. package/dist/providers/executor/linked-issue-reader.d.ts +16 -0
  360. package/dist/providers/executor/linked-issue-reader.js +30 -0
  361. package/dist/providers/executor/routing.d.ts +2 -0
  362. package/dist/providers/executor/routing.js +4 -0
  363. package/dist/providers/executor/types.d.ts +13 -0
  364. package/dist/providers/index.d.ts +2 -1
  365. package/dist/providers/issue-tracker/types.d.ts +17 -0
  366. package/dist/providers/issue-tracker/types.js +39 -1
  367. package/dist/providers/jobs/claude-tool-setup.d.ts +4 -0
  368. package/dist/providers/jobs/claude-tool-setup.js +5 -2
  369. package/dist/providers/jobs/docker-isolation.d.ts +8 -0
  370. package/dist/providers/jobs/docker-isolation.js +17 -0
  371. package/dist/providers/jobs/docker.d.ts +1 -0
  372. package/dist/providers/jobs/docker.js +27 -2
  373. package/dist/providers/jobs/fake-kubernetes-api.d.ts +2 -0
  374. package/dist/providers/jobs/fake-kubernetes-api.js +3 -0
  375. package/dist/providers/jobs/kubernetes-api.d.ts +8 -0
  376. package/dist/providers/jobs/kubernetes-api.js +10 -0
  377. package/dist/providers/jobs/kubernetes-client.js +10 -1
  378. package/dist/providers/jobs/kubernetes-isolation.d.ts +2 -0
  379. package/dist/providers/jobs/kubernetes-isolation.js +17 -1
  380. package/dist/providers/jobs/kubernetes.d.ts +6 -1
  381. package/dist/providers/jobs/kubernetes.js +105 -16
  382. package/dist/providers/jobs/types.d.ts +33 -0
  383. package/dist/providers/jobs/types.js +13 -1
  384. package/dist/providers/llm/anthropic.js +5 -1
  385. package/dist/providers/llm/bedrock.d.ts +0 -12
  386. package/dist/providers/llm/bedrock.js +4 -0
  387. package/dist/providers/llm/claude-messages.d.ts +3 -1
  388. package/dist/providers/llm/claude-messages.js +4 -1
  389. package/dist/providers/llm/claude-provider.d.ts +2 -2
  390. package/dist/providers/llm/claude-provider.js +2 -2
  391. package/dist/providers/llm/openai.d.ts +2 -2
  392. package/dist/providers/llm/openai.js +6 -2
  393. package/dist/providers/llm/routing.d.ts +2 -2
  394. package/dist/providers/llm/routing.js +2 -2
  395. package/dist/providers/llm/types.d.ts +12 -2
  396. package/dist/providers/review-host/github-events.d.ts +9 -1
  397. package/dist/providers/review-host/github-events.js +34 -12
  398. package/dist/providers/review-host/github.d.ts +29 -2
  399. package/dist/providers/review-host/github.js +224 -29
  400. package/dist/providers/review-host/local.d.ts +8 -4
  401. package/dist/providers/review-host/local.js +49 -31
  402. package/dist/providers/review-host/patch-pages.d.ts +41 -0
  403. package/dist/providers/review-host/patch-pages.js +176 -0
  404. package/dist/providers/review-host/review-format.d.ts +19 -1
  405. package/dist/providers/review-host/review-format.js +38 -7
  406. package/dist/providers/review-host/types.d.ts +134 -2
  407. package/dist/providers/review-host/types.js +8 -0
  408. package/dist/providers/storage/gcs.d.ts +63 -0
  409. package/dist/providers/storage/gcs.js +69 -0
  410. package/dist/providers/storage/index.d.ts +15 -0
  411. package/dist/providers/storage/index.js +14 -0
  412. package/dist/providers/storage/local.d.ts +24 -0
  413. package/dist/providers/storage/local.js +92 -0
  414. package/dist/providers/storage/meter.d.ts +14 -0
  415. package/dist/providers/storage/meter.js +34 -0
  416. package/dist/providers/storage/s3.d.ts +41 -0
  417. package/dist/providers/storage/s3.js +107 -0
  418. package/dist/providers/storage/types.d.ts +37 -3
  419. package/dist/providers/storage/types.js +21 -4
  420. package/dist/providers/vcs/git.d.ts +33 -2
  421. package/dist/providers/vcs/git.js +324 -22
  422. package/dist/providers/vcs/github-remote.d.ts +26 -3
  423. package/dist/providers/vcs/github-remote.js +124 -14
  424. package/dist/providers/vcs/github.d.ts +214 -6
  425. package/dist/providers/vcs/github.js +447 -45
  426. package/dist/providers/vcs/index.js +8 -1
  427. package/dist/providers/vcs/pr-description.d.ts +127 -0
  428. package/dist/providers/vcs/pr-description.js +270 -0
  429. package/dist/providers/vcs/remote.d.ts +33 -1
  430. package/dist/providers/vcs/routing.d.ts +4 -2
  431. package/dist/providers/vcs/routing.js +6 -0
  432. package/dist/providers/vcs/types.d.ts +80 -3
  433. package/dist/quickstart/coding-seed.d.ts +14 -3
  434. package/dist/quickstart/coding-seed.js +213 -4
  435. package/dist/quickstart/reviewer-prompt.d.ts +1 -1
  436. package/dist/quickstart/reviewer-prompt.js +6 -6
  437. package/dist/quickstart-images.json +1 -1
  438. package/dist/sandbox/generated/zod.bundle.js +62 -1
  439. package/dist/sandbox/generated/zod3.bundle.js +5 -0
  440. package/dist/sandbox/host-functions.d.ts +1 -0
  441. package/dist/sandbox/host-functions.js +6 -11
  442. package/dist/sandbox/secret-broker.js +3 -4
  443. package/dist/sandbox/tool-capabilities.d.ts +10 -30
  444. package/dist/sandbox/zod-params.d.ts +37 -5
  445. package/dist/sandbox/zod-params.js +181 -26
  446. package/dist/serve.js +8 -1
  447. package/dist/viewer/api-schema.d.ts +349 -2627
  448. package/dist/viewer/api-schema.js +7 -0
  449. package/dist/viewer/build-schemas.js +6 -2
  450. package/dist/viewer/openapi.js +2 -4
  451. package/dist/wardby-bin.js +10 -0
  452. package/docs/agent-recipes.md +58 -20
  453. package/docs/code-review-agents.md +551 -41
  454. package/docs/coding-agent-setup.md +489 -7
  455. package/docs/coding-packages.md +1033 -90
  456. package/docs/coding-services.md +27 -12
  457. package/docs/coding-worker-byo-images.md +20 -0
  458. package/docs/coding-worker-isolation.md +105 -30
  459. package/docs/getting-started-gke.md +124 -137
  460. package/docs/getting-started-identity-provider.md +10 -1
  461. package/docs/getting-started.md +9 -0
  462. package/docs/jira-agents.md +17 -8
  463. package/docs/knowledge/blob-not-found-semantics.md +41 -0
  464. package/docs/knowledge/bounded-file-descriptor.md +52 -0
  465. package/docs/knowledge/delegation-admission-slots.md +52 -0
  466. package/docs/knowledge/http-dispatcher-bootstrap.md +61 -0
  467. package/docs/knowledge/import-rename-target.md +52 -0
  468. package/docs/knowledge/index.md +57 -0
  469. package/docs/knowledge/log.md +9 -0
  470. package/docs/knowledge/mcp-cancelled-task-precedence.md +41 -0
  471. package/docs/knowledge/mcp-http-prompt-discovery.md +41 -0
  472. package/docs/knowledge/mcp-schema-module-boundary.md +41 -0
  473. package/docs/knowledge/nullable-tool-uniqueness.md +52 -0
  474. package/docs/knowledge/overview-cli.md +74 -0
  475. package/docs/knowledge/overview-coding-proxy.md +72 -0
  476. package/docs/knowledge/overview-coding.md +72 -0
  477. package/docs/knowledge/overview-config.md +60 -0
  478. package/docs/knowledge/overview-core.md +72 -0
  479. package/docs/knowledge/overview-deploy.md +83 -0
  480. package/docs/knowledge/overview-mcp.md +61 -0
  481. package/docs/knowledge/overview-prisma.md +72 -0
  482. package/docs/knowledge/overview-providers.md +89 -0
  483. package/docs/knowledge/overview-sandbox.md +83 -0
  484. package/docs/knowledge/overview-surfaces.md +83 -0
  485. package/docs/knowledge/private-datastore-shared-boundary.md +54 -0
  486. package/docs/knowledge/proxy-settlement-projections.md +41 -0
  487. package/docs/knowledge/run-pricing-first-writer.md +43 -0
  488. package/docs/knowledge/schedule-claim-transaction.md +41 -0
  489. package/docs/knowledge/secret-entry-replica-contract.md +52 -0
  490. package/docs/knowledge/terminal-run-first-writer.md +41 -0
  491. package/docs/knowledge/transactional-agent-authorization.md +52 -0
  492. package/docs/knowledge/viewer-notify-write-isolation.md +30 -0
  493. package/docs/knowledge/warm-input-publication.md +30 -0
  494. package/docs/knowledge/workspace-staging-failure.md +54 -0
  495. package/docs/knowledge.md +54 -14
  496. package/docs/native-sandbox.md +119 -23
  497. package/docs/observability.md +4 -0
  498. package/docs/security-deployment.md +33 -2
  499. package/docs/slack-notifications.md +35 -19
  500. package/docs/tool-secrets.md +42 -10
  501. package/docs/viewer-api.openapi.yml +1 -1
  502. package/help/admin-viewer.md +24 -2
  503. package/help/architecture-agent.md +11 -6
  504. package/help/build-worker-image.md +3 -1
  505. package/help/builder-agent.md +19 -6
  506. package/help/code-review-agents.md +177 -6
  507. package/help/coding-packages.md +156 -6
  508. package/help/cost-attribution.md +9 -5
  509. package/help/creating-agents.md +14 -1
  510. package/help/dependency-cache.md +143 -0
  511. package/help/deploy-gke.md +15 -8
  512. package/help/errors/budget-exhausted.md +187 -0
  513. package/help/errors/budget-group-exhausted.md +5 -1
  514. package/help/errors/coding-stream-quota-exceeded.md +33 -0
  515. package/help/errors/coding-workspace-disk-exceeds-limit.md +39 -0
  516. package/help/errors/continuation-closed.md +9 -12
  517. package/help/errors/delegation-conflict.md +24 -0
  518. package/help/errors/file-list-truncated.md +45 -0
  519. package/help/errors/full-read-required.md +49 -0
  520. package/help/errors/github-account-not-allowed.md +4 -0
  521. package/help/errors/graph-incomplete.md +43 -0
  522. package/help/errors/graph-limit.md +43 -0
  523. package/help/errors/merge-conflict.md +49 -0
  524. package/help/errors/native-sandbox-gateway-error.md +20 -0
  525. package/help/errors/native-sandbox-launch-abandoned.md +21 -0
  526. package/help/errors/native-sandbox-worker-exited.md +3 -2
  527. package/help/errors/package-not-allowed-pending.md +52 -0
  528. package/help/errors/package-not-allowed.md +51 -0
  529. package/help/errors/package-refusals.md +43 -0
  530. package/help/errors/patch-cursor-malformed.md +31 -0
  531. package/help/errors/run-lost.md +39 -0
  532. package/help/errors/run-transcript-download.md +44 -0
  533. package/help/errors/secret-in-value.md +69 -0
  534. package/help/errors/target-merge.md +40 -0
  535. package/help/fix-merge-conflicts.md +73 -0
  536. package/help/github-app-setup.md +132 -0
  537. package/help/github.md +22 -0
  538. package/help/identity-and-access.md +2 -0
  539. package/help/jira.md +23 -6
  540. package/help/knowledge.md +16 -7
  541. package/help/local-repositories.md +1 -1
  542. package/help/low-power-mode.md +7 -1
  543. package/help/merge-order-check.md +5 -0
  544. package/help/native-capabilities.md +29 -1
  545. package/help/native-sandbox.md +15 -1
  546. package/help/operating-agents.md +5 -0
  547. package/help/package-audit-mode.md +91 -0
  548. package/help/related-pull-requests.md +10 -7
  549. package/help/resume-runs-locally.md +131 -0
  550. package/help/review-fix-rounds.md +33 -1
  551. package/help/sharing-agents.md +59 -0
  552. package/help/slack-notifications.md +19 -5
  553. package/help/status-comments.md +31 -0
  554. package/help/stop-run.md +58 -0
  555. package/help/troubleshooting/budgets.md +28 -0
  556. package/help/troubleshooting/coding-workers.md +78 -7
  557. package/package.json +17 -10
  558. package/prisma/migrations/20261016000000_registry_approved_accepted_advisories/migration.sql +3 -0
  559. package/prisma/migrations/20261016000001_mark_ready_when_passing/migration.sql +3 -0
  560. package/prisma/migrations/20261017000000_debug_trace_max_mb/migration.sql +4 -0
  561. package/prisma/migrations/20261018000000_dependency_cache/migration.sql +53 -0
  562. package/prisma/migrations/20261019000000_claude_max_output_tokens/migration.sql +5 -0
  563. package/prisma/migrations/20261021000000_pr_description_hash/migration.sql +4 -0
  564. package/prisma/migrations/20261022000000_dependency_snapshot_validation_metadata/migration.sql +4 -0
  565. package/prisma/migrations/20261023000000_package_audit_mode/migration.sql +27 -0
  566. package/prisma/migrations/20261024000000_budget_events/migration.sql +26 -0
  567. package/prisma/migrations/20261025000000_cache_aware_holds/migration.sql +9 -0
  568. package/prisma/migrations/20261026000000_budget_overrun/migration.sql +4 -0
  569. package/prisma/migrations/20261027000000_coding_run_request/migration.sql +1 -0
  570. package/prisma/migrations/20261028000000_native_gateway_review_reads/migration.sql +6 -0
  571. package/prisma/migrations/20261029000000_lead_survives_deploy/migration.sql +6 -0
  572. package/prisma/migrations/20261030000000_local_review_covered_sha/migration.sql +2 -0
  573. package/prisma/migrations/20261031000000_coding_run_budget_limit/migration.sql +9 -0
  574. package/prisma/migrations/20261101000000_run_transcripts/migration.sql +22 -0
  575. package/prisma/schema.prisma +311 -97
  576. package/dist/sandbox/generated/zod-to-json-schema.bundle.js +0 -5
@@ -2,6 +2,7 @@
2
2
  * Pure decision table for reconciling a stale `Run` that a DBOS workflow
3
3
  * owns. Kept free of DBOS imports so it is unit-testable without Postgres.
4
4
  */
5
+ import { STEPS_CHANGED_REASON } from "../engine/types.js";
5
6
  export interface WorkflowStatusLike {
6
7
  status: string;
7
8
  executorId?: string;
@@ -48,3 +49,33 @@ export type RecoveryDecision = {
48
49
  reason: string;
49
50
  };
50
51
  export declare function decideRecovery(status: WorkflowStatusLike | null, runIsTerminal: boolean, selfExecutorId: string): RecoveryDecision;
52
+ /** One recorded step of a workflow, as `DBOS.listWorkflowSteps` reports it. */
53
+ export interface TakeoverStep {
54
+ functionID: number;
55
+ name: string;
56
+ output: unknown;
57
+ }
58
+ /** A still-drivable child of the lead being taken over. */
59
+ export interface TakeoverChild {
60
+ status: string;
61
+ kind: "coding" | "native";
62
+ nativeExecutionMode: string | null;
63
+ }
64
+ export type TakeoverDecision = {
65
+ action: "fork";
66
+ startStep: number;
67
+ } | {
68
+ action: "lost";
69
+ reason: string;
70
+ };
71
+ /** Why a forked run ends lost when its replay meets a step the new code no longer takes (defined with StepReplayMismatchError). */
72
+ export { STEPS_CHANGED_REASON };
73
+ /**
74
+ * Whether a workflow recorded under another application version can be forked under this one
75
+ * (design §1): only when it is waiting inside a `delegate_to_*` call of its last recorded turn and
76
+ * every child it may be waiting on is durable on its own (a coding run, or a sandbox-mode native
77
+ * run). The fork starts at the first function id with no record, so every recorded step (the
78
+ * turn's model reply above all) replays from its record and the delegate step re-attaches to its
79
+ * child. Anything else is `lost` with the mismatch reason, as before.
80
+ */
81
+ export declare function decideTakeover(steps: TakeoverStep[], children: TakeoverChild[], mismatchReason: string): TakeoverDecision;
@@ -2,6 +2,8 @@
2
2
  * Pure decision table for reconciling a stale `Run` that a DBOS workflow
3
3
  * owns. Kept free of DBOS imports so it is unit-testable without Postgres.
4
4
  */
5
+ import { DELEGATE_TOOL_PREFIX } from "../../core/tool-names.js";
6
+ import { STEPS_CHANGED_REASON } from "../engine/types.js";
5
7
  /**
6
8
  * How many times the reconciler will try to adopt (resume) one orphaned
7
9
  * workflow before declaring the run lost.
@@ -62,3 +64,65 @@ export function decideRecovery(status, runIsTerminal, selfExecutorId) {
62
64
  }
63
65
  return { action: "lost", reason: `Durable workflow is in unrecognised status "${status.status}".` };
64
66
  }
67
+ /** Why a forked run ends lost when its replay meets a step the new code no longer takes (defined with StepReplayMismatchError). */
68
+ export { STEPS_CHANGED_REASON };
69
+ const LLM_STEP = /^turn:(\d+):llm$/;
70
+ /**
71
+ * The tool-call names of a recorded `turn:N:llm` step. Its output is the engine's TurnResult
72
+ * (engine-native.ts) as DBOS deserialized it: `{ usage, text, toolCalls: [{ id, name, argsJson }],
73
+ * budgetExceededMidStream, error? }`. Anything else yields undefined (the shape changed: lost).
74
+ */
75
+ function recordedToolCallNames(output) {
76
+ if (typeof output !== "object" || output === null || !("toolCalls" in output))
77
+ return undefined;
78
+ const calls = output.toolCalls;
79
+ if (!Array.isArray(calls))
80
+ return undefined;
81
+ const names = [];
82
+ for (const call of calls) {
83
+ if (typeof call !== "object" || call === null || !("name" in call) || typeof call.name !== "string") {
84
+ return undefined;
85
+ }
86
+ names.push(call.name);
87
+ }
88
+ return names;
89
+ }
90
+ /**
91
+ * Whether a workflow recorded under another application version can be forked under this one
92
+ * (design §1): only when it is waiting inside a `delegate_to_*` call of its last recorded turn and
93
+ * every child it may be waiting on is durable on its own (a coding run, or a sandbox-mode native
94
+ * run). The fork starts at the first function id with no record, so every recorded step (the
95
+ * turn's model reply above all) replays from its record and the delegate step re-attaches to its
96
+ * child. Anything else is `lost` with the mismatch reason, as before.
97
+ */
98
+ export function decideTakeover(steps, children, mismatchReason) {
99
+ const lost = { action: "lost", reason: mismatchReason };
100
+ const sorted = [...steps].sort((a, b) => a.functionID - b.functionID);
101
+ const llmStep = sorted.filter((s) => LLM_STEP.test(s.name)).at(-1);
102
+ if (!llmStep)
103
+ return lost;
104
+ const turn = LLM_STEP.exec(llmStep.name)[1];
105
+ const names = recordedToolCallNames(llmStep.output);
106
+ if (!names)
107
+ return lost;
108
+ const recorded = new Set(sorted.map((s) => s.name));
109
+ const firstPending = names.findIndex((_, i) => !recorded.has(`turn:${turn}:tool:${i}`));
110
+ // Not waiting in a tool call at all, or waiting in one that is not a delegation (re-running it
111
+ // under new code would repeat its effect).
112
+ if (firstPending < 0 || !names[firstPending].startsWith(DELEGATE_TOOL_PREFIX))
113
+ return lost;
114
+ const durable = (child) => child.kind === "coding" || child.nativeExecutionMode === "sandbox";
115
+ if (!children.every(durable))
116
+ return lost;
117
+ let startStep = 0;
118
+ for (const step of sorted) {
119
+ if (step.functionID > startStep)
120
+ break;
121
+ if (step.functionID === startStep)
122
+ startStep += 1;
123
+ }
124
+ // A gap at or before the recorded reply would re-run the model turn: never fork into that.
125
+ if (startStep <= llmStep.functionID)
126
+ return lost;
127
+ return { action: "fork", startStep };
128
+ }
@@ -15,7 +15,10 @@
15
15
  * A stalled-but-alive owner can overlap an adopted attempt of the same run:
16
16
  * whichever records a step second loses ownership, stops at that step
17
17
  * without writing the run, and hands the SDK's conflict back so DBOS parks
18
- * it as a duplicate execution (see dbosStep and runInsideWorkflow).
18
+ * it as a duplicate execution (see dbosStep and runInsideWorkflow). After a
19
+ * takeover the fork has its own workflow id, so the two never share a step:
20
+ * the original stops the same way when it meets the takeover's cancel
21
+ * (dbosStepFor), and its start() handle writes nothing.
19
22
  */
20
23
  import pg from "pg";
21
24
  import type { PrismaClient } from "#prisma";
@@ -44,6 +47,20 @@ export declare function dbosLaunchConfig(config: DbosConfig, executorId: string,
44
47
  logLevel: "warn";
45
48
  systemDatabasePool?: pg.Pool;
46
49
  };
50
+ /**
51
+ * The id a takeover gives the fork of workflow `from` under application `version`. Deterministic, so
52
+ * a takeover that forked but never recorded the fork finishes it rather than forking again; and
53
+ * descendants share their ancestor's id as a prefix, which isForkOf relies on.
54
+ */
55
+ export declare function forkWorkflowId(from: string, version: string): string;
56
+ /** Whether workflow `id` was forked from `from`, directly or through further forks (forkWorkflowId). */
57
+ export declare function isForkOf(id: string, from: string): boolean;
58
+ /**
59
+ * The DBOS workflow id that drives a run: `Run.executionWorkflowId` once the
60
+ * run's workflow was forked under a new application version, otherwise the
61
+ * run id (a run's first workflow is started with its run id).
62
+ */
63
+ export declare function workflowIdOf(db: Pick<PrismaClient, "run">, runId: string): Promise<string>;
47
64
  /**
48
65
  * Bind the engine's step boundary to a DBOS checkpointed step. Never
49
66
  * retried: a retry would re-spend.
@@ -59,8 +76,34 @@ export declare function dbosLaunchConfig(config: DbosConfig, executorId: string,
59
76
  * discarded step's usage, if any, for the log) so executeRun's backstop stops
60
77
  * this attempt without writing the run; runInsideWorkflow turns it back into
61
78
  * the SDK error.
79
+ *
80
+ * DBOS signals that a replay reached a step whose recorded name differs (the
81
+ * run was recorded by a release whose steps differ, e.g. after a takeover
82
+ * fork) by throwing `DBOSUnexpectedStepError`. Translated into a
83
+ * `StepReplayMismatchError`, which executeRun's backstop settles as `lost`
84
+ * (runInsideWorkflow does it for a mismatch at the load step, which is outside
85
+ * that backstop).
62
86
  */
63
87
  export declare const dbosStep: StepRunner;
88
+ /**
89
+ * dbosStep for one run's workflow body. A cancellation of a workflow the run has moved off (its
90
+ * `executionWorkflowId` names a fork of it) is not a stop: recover() took the run over (forked it) and cancelled this,
91
+ * the forked-from workflow, while its owner was still alive. That attempt stops like one that lost
92
+ * a step's checkpoint (RunOwnershipLostError: no terminal write), so the fork, not a reasonless
93
+ * "cancelled" from the original, settles the run. takeOver records the fork's id before it
94
+ * cancels, so the original always reads the new id by the time it sees the cancel.
95
+ */
96
+ export declare function dbosStepFor(db: Pick<PrismaClient, "run">, runId: string): StepRunner;
97
+ /**
98
+ * dbosStepFor behind the same per-step row read as inline runs (stopAwareStep), failing open. DBOS
99
+ * stops a workflow it was told to cancel, but stop() can cancel only the workflow the run names, and
100
+ * a takeover's fork is named only once recorded: a fork already running before that (or whose
101
+ * record failed) would otherwise keep paying for turns under a run stop() already made terminal.
102
+ * The same read fences out a taken-over original as soon as the run names its fork, before the
103
+ * takeover's cancel lands, and even when that cancel failed: it stops as an ownership loss, without
104
+ * a terminal write or the settle side effects (host checks, status comments) the fork owns.
105
+ */
106
+ export declare function stopAwareDbosStep(db: Pick<PrismaClient, "run">, runId: string): StepRunner;
64
107
  export declare class DbosExecutor implements Executor {
65
108
  private readonly providers;
66
109
  private readonly config;
@@ -90,6 +133,12 @@ export declare class DbosExecutor implements Executor {
90
133
  * the same thing launch() does for this executor's own workflows.
91
134
  */
92
135
  recover(handle: PersistedExecutionHandle): Promise<ExecutionRecoveryResult>;
136
+ /**
137
+ * A workflow recorded under another application version: fork it under this one when it is only
138
+ * waiting on durable children (decideTakeover), otherwise answer lost with the mismatch. The
139
+ * fork counts as an adoption attempt, so repeated failed takeovers of one run end lost too.
140
+ */
141
+ private takeOver;
93
142
  /** Workflow body. Public only so the module-level registration can reach it. */
94
143
  runInsideWorkflow(runId: string): Promise<void>;
95
144
  }
@@ -15,18 +15,22 @@
15
15
  * A stalled-but-alive owner can overlap an adopted attempt of the same run:
16
16
  * whichever records a step second loses ownership, stops at that step
17
17
  * without writing the run, and hands the SDK's conflict back so DBOS parks
18
- * it as a duplicate execution (see dbosStep and runInsideWorkflow).
18
+ * it as a duplicate execution (see dbosStep and runInsideWorkflow). After a
19
+ * takeover the fork has its own workflow id, so the two never share a step:
20
+ * the original stops the same way when it meets the takeover's cancel
21
+ * (dbosStepFor), and its start() handle writes nothing.
19
22
  */
20
23
  import { DBOS, Error as DbosErrors } from "@dbos-inc/dbos-sdk";
21
24
  import pg from "pg";
22
- import { RunOwnershipLostError } from "../engine/types.js";
23
- import { executeRun, RunCancelledError } from "../../core/runner.js";
25
+ import { RunOwnershipLostError, StepReplayMismatchError } from "../engine/types.js";
26
+ import { executeRun, failNativeRun, loseNativeRun, nativeRunIntegrations, RunCancelledError, stopAwareStep, } from "../../core/runner.js";
24
27
  import { markRunFailedFromExecutorError } from "../../core/dispatch.js";
25
28
  import { prisma as defaultDb } from "../../core/db.js";
26
29
  import { HEARTBEAT_INTERVAL_MS } from "../../core/timing.js";
30
+ import { DRIVABLE } from "../../core/run-status.js";
27
31
  import { logger } from "../../core/logger.js";
28
32
  import { databaseAuthFromEnv, pgConnectionConfig } from "../../core/pg-auth.js";
29
- import { ADOPTION_EXHAUSTED_REASON, decideRecovery, shouldGiveUpAdoption, versionMismatchReason, } from "./dbos-status.js";
33
+ import { ADOPTION_EXHAUSTED_REASON, STEPS_CHANGED_REASON, decideRecovery, decideTakeover, shouldGiveUpAdoption, versionMismatchReason, } from "./dbos-status.js";
30
34
  export const DBOS_BACKEND = "dbos";
31
35
  const dbosLog = logger.child({ module: "dbos-executor" });
32
36
  /** DBOS's own system pool size (DEFAULT_POOL_SIZE in the SDK's system_database.js); kept for ours. */
@@ -87,6 +91,27 @@ function setActiveExecutor(executor) {
87
91
  * `runInsideWorkflow`'s finally.
88
92
  */
89
93
  const cancellationReasons = new Map();
94
+ /**
95
+ * The id a takeover gives the fork of workflow `from` under application `version`. Deterministic, so
96
+ * a takeover that forked but never recorded the fork finishes it rather than forking again; and
97
+ * descendants share their ancestor's id as a prefix, which isForkOf relies on.
98
+ */
99
+ export function forkWorkflowId(from, version) {
100
+ return `${from}@${version}`;
101
+ }
102
+ /** Whether workflow `id` was forked from `from`, directly or through further forks (forkWorkflowId). */
103
+ export function isForkOf(id, from) {
104
+ return id.startsWith(`${from}@`);
105
+ }
106
+ /**
107
+ * The DBOS workflow id that drives a run: `Run.executionWorkflowId` once the
108
+ * run's workflow was forked under a new application version, otherwise the
109
+ * run id (a run's first workflow is started with its run id).
110
+ */
111
+ export async function workflowIdOf(db, runId) {
112
+ const row = await db.run.findUnique({ where: { id: runId }, select: { executionWorkflowId: true } });
113
+ return row?.executionWorkflowId ?? runId;
114
+ }
90
115
  /**
91
116
  * Bind the engine's step boundary to a DBOS checkpointed step. Never
92
117
  * retried: a retry would re-spend.
@@ -102,8 +127,47 @@ const cancellationReasons = new Map();
102
127
  * discarded step's usage, if any, for the log) so executeRun's backstop stops
103
128
  * this attempt without writing the run; runInsideWorkflow turns it back into
104
129
  * the SDK error.
130
+ *
131
+ * DBOS signals that a replay reached a step whose recorded name differs (the
132
+ * run was recorded by a release whose steps differ, e.g. after a takeover
133
+ * fork) by throwing `DBOSUnexpectedStepError`. Translated into a
134
+ * `StepReplayMismatchError`, which executeRun's backstop settles as `lost`
135
+ * (runInsideWorkflow does it for a mismatch at the load step, which is outside
136
+ * that backstop).
137
+ */
138
+ export const dbosStep = (name, fn) => runDbosStep(name, fn);
139
+ /**
140
+ * dbosStep for one run's workflow body. A cancellation of a workflow the run has moved off (its
141
+ * `executionWorkflowId` names a fork of it) is not a stop: recover() took the run over (forked it) and cancelled this,
142
+ * the forked-from workflow, while its owner was still alive. That attempt stops like one that lost
143
+ * a step's checkpoint (RunOwnershipLostError: no terminal write), so the fork, not a reasonless
144
+ * "cancelled" from the original, settles the run. takeOver records the fork's id before it
145
+ * cancels, so the original always reads the new id by the time it sees the cancel.
146
+ */
147
+ export function dbosStepFor(db, runId) {
148
+ const supersedes = async (cancelledWorkflowId) => {
149
+ // A failed read falls back to an ordinary cancellation, as before this check existed.
150
+ const current = await workflowIdOf(db, runId).catch(() => cancelledWorkflowId);
151
+ return isForkOf(current, cancelledWorkflowId);
152
+ };
153
+ return (name, fn) => runDbosStep(name, fn, supersedes);
154
+ }
155
+ /**
156
+ * dbosStepFor behind the same per-step row read as inline runs (stopAwareStep), failing open. DBOS
157
+ * stops a workflow it was told to cancel, but stop() can cancel only the workflow the run names, and
158
+ * a takeover's fork is named only once recorded: a fork already running before that (or whose
159
+ * record failed) would otherwise keep paying for turns under a run stop() already made terminal.
160
+ * The same read fences out a taken-over original as soon as the run names its fork, before the
161
+ * takeover's cancel lands, and even when that cancel failed: it stops as an ownership loss, without
162
+ * a terminal write or the settle side effects (host checks, status comments) the fork owns.
105
163
  */
106
- export const dbosStep = async (name, fn) => {
164
+ export function stopAwareDbosStep(db, runId) {
165
+ return stopAwareStep(db, runId, dbosStepFor(db, runId), {
166
+ failOpen: true,
167
+ ownership: { workflowId: () => DBOS.workflowID, forkedFrom: isForkOf },
168
+ });
169
+ }
170
+ async function runDbosStep(name, fn, supersededBy) {
107
171
  let ran;
108
172
  try {
109
173
  return await DBOS.runStep(async () => {
@@ -114,6 +178,8 @@ export const dbosStep = async (name, fn) => {
114
178
  }
115
179
  catch (err) {
116
180
  if (err instanceof DbosErrors.DBOSWorkflowCancelledError) {
181
+ if (await supersededBy?.(err.workflowID))
182
+ throw new RunOwnershipLostError(name, { cause: err });
117
183
  const reason = cancellationReasons.get(err.workflowID);
118
184
  throw new RunCancelledError(`Run cancelled: ${reason ?? "no reason given"}`);
119
185
  }
@@ -121,9 +187,11 @@ export const dbosStep = async (name, fn) => {
121
187
  const discardedUsage = usageOf(ran?.value);
122
188
  throw new RunOwnershipLostError(name, { cause: err, ...(discardedUsage ? { discardedUsage } : {}) });
123
189
  }
190
+ if (err instanceof DbosErrors.DBOSUnexpectedStepError)
191
+ throw new StepReplayMismatchError(name, { cause: err });
124
192
  throw err;
125
193
  }
126
- };
194
+ }
127
195
  /**
128
196
  * The numeric `usage` fields of a step result, if it has any (an LLM turn's
129
197
  * token counts and cost). Tool steps return a string and yield nothing.
@@ -141,6 +209,19 @@ function usageOf(value) {
141
209
  * Registered once at module load — DBOS requires registration before
142
210
  * launch(). The body must be deterministic between steps; everything
143
211
  * non-deterministic in executeRun/engine is inside a runStep.
212
+ *
213
+ * Replay constraints. A run's workflow is replayed both by a same-version
214
+ * resume and, when recover() takes a waiting lead over after a deploy, by a
215
+ * fork that runs this body under the *new* release against steps the *old*
216
+ * release recorded (see decideTakeover). So:
217
+ * - Code between steps must stay deterministic and safe to repeat: it runs
218
+ * again on every replay, now possibly under newer code.
219
+ * - Step names and their order must only change in ways a replay can still
220
+ * follow; a replay that meets a different recorded name ends the run lost
221
+ * with STEPS_CHANGED_REASON (executeRun's backstop, or runInsideWorkflow).
222
+ * - New code must accept step outputs recorded by the previous release (the
223
+ * `turn:N:llm` TurnResult, tool results). A release that changes a recorded
224
+ * shape keeps reading the old one, or a takeover fails at that step (lost).
144
225
  */
145
226
  const runWorkflow = DBOS.registerWorkflow(async (runId) => {
146
227
  const self = activeExecutor;
@@ -218,12 +299,16 @@ export class DbosExecutor {
218
299
  if (!this.launched)
219
300
  return;
220
301
  this.launched = false;
221
- if (activeExecutor === this)
222
- setActiveExecutor(undefined);
223
302
  try {
224
303
  await DBOS.shutdown({ workflowCompletionTimeoutMS: 5_000 });
225
304
  }
226
305
  finally {
306
+ // Only now: shutdown first lets the queue runner finish the dispatch it is in, so a workflow
307
+ // the queue claimed just before (a takeover's fork, an adoption) still starts. Without its
308
+ // executor its body would throw, and DBOS could record that as the workflow's outcome: the
309
+ // next process would then mark a run failed that was only waiting to be taken over.
310
+ if (activeExecutor === this)
311
+ setActiveExecutor(undefined);
227
312
  await endSystemPool();
228
313
  }
229
314
  }
@@ -263,6 +348,15 @@ export class DbosExecutor {
263
348
  // the same failure — dispatchRun's own `.catch` around `start()` calls
264
349
  // it too — which is harmless precisely because it is conditional and
265
350
  // idempotent: the second call matches zero rows.
351
+ //
352
+ // A workflow that recover() took over (forked, then cancelled) no longer drives the run: the
353
+ // fork does, and settles it. Its rejection is the takeover's cancel, not the run's failure.
354
+ if ((await workflowIdOf(this.db, runId).catch(() => runId)) !== runId) {
355
+ // The error's name only: this path adds no message text to any output.
356
+ const error = err instanceof Error ? err.name : typeof err;
357
+ dbosLog.info({ runId, error }, "the run's original workflow ended after a takeover; its fork drives the run");
358
+ return;
359
+ }
266
360
  await markRunFailedFromExecutorError(this.db, runId, err, this.selfDefects);
267
361
  throw err;
268
362
  }
@@ -274,11 +368,43 @@ export class DbosExecutor {
274
368
  if (!this.launched)
275
369
  await this.launch();
276
370
  dbosLog.info({ runId, reason }, "cancelling durable run");
371
+ // The terminal write comes first and is conditional (the first writer wins), so the run is
372
+ // cancelled with the caller's reason even when its workflow runs in another process (where the
373
+ // reason map below is empty) or has no workflow at all yet (cancelWorkflow then matches
374
+ // nothing). The workflow's next step then throws, and its own terminal write matches no row.
375
+ // The cancel is still attempted when that settle throws (a host or issue update failing after
376
+ // the write) or the database is unreachable: a cancelled row whose workflow kept going would
377
+ // keep spending. The settle's error, if any, is the one rethrown.
378
+ let settleFailed;
379
+ try {
380
+ const row = await this.db.run.findUnique({ where: { id: runId }, select: { status: true } });
381
+ if (row && (row.status === "pending" || row.status === "running")) {
382
+ await failNativeRun({ runId, db: this.db, providers: this.providers, ...nativeRunIntegrations(this.providers, this.db) }, new RunCancelledError(reason ?? "The run was cancelled."));
383
+ }
384
+ }
385
+ catch (err) {
386
+ settleFailed = { err };
387
+ }
277
388
  // Recorded before the cancel so the workflow's next step boundary can
278
389
  // find it: DBOS only reports "cancelled", never why.
279
- if (reason)
390
+ // Keyed by both ids: dbosStep looks it up by the cancelled workflow's id, which is the forked
391
+ // id for a forked run. A failed read targets the run id, the workflow of any run never forked.
392
+ const workflowId = await workflowIdOf(this.db, runId).catch(() => runId);
393
+ if (reason) {
280
394
  cancellationReasons.set(runId, reason);
281
- await DBOS.cancelWorkflow(runId);
395
+ cancellationReasons.set(workflowId, reason);
396
+ }
397
+ try {
398
+ await DBOS.cancelWorkflow(workflowId);
399
+ }
400
+ catch (err) {
401
+ if (!settleFailed)
402
+ throw err;
403
+ // The error's name only: this path adds no message text to any output.
404
+ dbosLog.warn({ runId, error: err instanceof Error ? err.name : typeof err }, "could not cancel the workflow");
405
+ }
406
+ if (settleFailed)
407
+ throw settleFailed.err;
282
408
  }
283
409
  /**
284
410
  * Answer the reconciler for a stale run. Never relaunches: `resume`
@@ -307,13 +433,12 @@ export class DbosExecutor {
307
433
  // DBOS gates recovery and dequeue on application_version. A workflow
308
434
  // recorded under a different version is never dequeued here, so
309
435
  // resuming it would be an unbounded loop that holds the run
310
- // `running` forever: give up at once and let the reconciler reap it.
436
+ // `running` forever. Instead it is forked under this version when it
437
+ // is only waiting on durable children, and otherwise given up at once
438
+ // so the reconciler reaps it (takeOver).
311
439
  const mismatch = versionMismatchReason(status?.applicationVersion, DBOS.applicationVersion);
312
- if (mismatch) {
313
- this.resumeAttempts.delete(handle.runId);
314
- dbosLog.warn({ runId: handle.runId, workflowVersion: status?.applicationVersion }, mismatch);
315
- return { state: "lost", reason: mismatch };
316
- }
440
+ if (mismatch)
441
+ return this.takeOver(handle, mismatch, status?.applicationVersion);
317
442
  // Belt and braces for every other reason a resume can fail to take
318
443
  // (an id we can't dequeue for, a queue that never drains): bound the
319
444
  // number of adoptions of one run so the reconciler stops re-driving
@@ -347,6 +472,56 @@ export class DbosExecutor {
347
472
  return { state: "lost", reason: decision.reason };
348
473
  }
349
474
  }
475
+ /**
476
+ * A workflow recorded under another application version: fork it under this one when it is only
477
+ * waiting on durable children (decideTakeover), otherwise answer lost with the mismatch. The
478
+ * fork counts as an adoption attempt, so repeated failed takeovers of one run end lost too.
479
+ */
480
+ async takeOver(handle, mismatch, workflowVersion) {
481
+ const steps = (await DBOS.listWorkflowSteps(handle.id)) ?? [];
482
+ const children = await this.db.run.findMany({
483
+ where: { parentRunId: handle.runId, status: { in: [...DRIVABLE] } },
484
+ select: { status: true, nativeExecutionMode: true, agent: { select: { kind: true } } },
485
+ });
486
+ const decision = decideTakeover(steps.map((s) => ({ functionID: s.functionID, name: s.name, output: s.output })), children.map((c) => ({ status: c.status, kind: c.agent.kind, nativeExecutionMode: c.nativeExecutionMode })), mismatch);
487
+ if (decision.action === "lost") {
488
+ this.resumeAttempts.delete(handle.runId);
489
+ dbosLog.warn({ runId: handle.runId, workflowVersion }, mismatch);
490
+ return { state: "lost", reason: decision.reason };
491
+ }
492
+ const attempts = this.resumeAttempts.get(handle.runId) ?? 0;
493
+ if (shouldGiveUpAdoption(attempts)) {
494
+ this.resumeAttempts.delete(handle.runId);
495
+ dbosLog.warn({ runId: handle.runId, attempts }, "giving up on taking over durable run");
496
+ return { state: "lost", reason: ADOPTION_EXHAUSTED_REASON };
497
+ }
498
+ this.resumeAttempts.set(handle.runId, attempts + 1);
499
+ // Deterministic, so a takeover that forked but never recorded the fork (the run write below
500
+ // failed, or the process died) is finished by the next pass rather than forked a second time:
501
+ // two workflows driving one run would each spend on every turn. DBOS inserts a fork's row with
502
+ // a plain INSERT (bulkForkWorkflows in the SDK's system_database.js), so a repeated id would
503
+ // fail on the key: look first, and treat a lost race the same way.
504
+ const forkedId = forkWorkflowId(handle.id, DBOS.applicationVersion);
505
+ if (!(await DBOS.getWorkflowStatus(forkedId))) {
506
+ try {
507
+ await DBOS.forkWorkflow(handle.id, decision.startStep, {
508
+ newWorkflowID: forkedId,
509
+ applicationVersion: DBOS.applicationVersion,
510
+ });
511
+ }
512
+ catch (err) {
513
+ if (!(await DBOS.getWorkflowStatus(forkedId)))
514
+ throw err;
515
+ }
516
+ }
517
+ const forked = DBOS.retrieveWorkflow(forkedId);
518
+ await this.db.run.update({ where: { id: handle.runId }, data: { executionWorkflowId: forkedId } });
519
+ // So nothing can dequeue the original again (it stays recorded as forked from).
520
+ await DBOS.cancelWorkflow(handle.id).catch((err) => dbosLog.warn({ err, runId: handle.runId }, "could not cancel the forked-from workflow"));
521
+ dbosLog.info({ runId: handle.runId, from: handle.id, to: forkedId, startStep: decision.startStep, workflowVersion }, "run taken over under this application version");
522
+ void forked.getResult().catch((err) => dbosLog.error({ err, runId: handle.runId }, "taken-over run failed"));
523
+ return { state: "active" };
524
+ }
350
525
  /** Workflow body. Public only so the module-level registration can reach it. */
351
526
  async runInsideWorkflow(runId) {
352
527
  const beat = () => this.db.run.update({ where: { id: runId }, data: { heartbeatAt: new Date() } }).catch(() => {
@@ -355,23 +530,35 @@ export class DbosExecutor {
355
530
  await beat();
356
531
  const timer = setInterval(() => void beat(), this.heartbeatIntervalMs);
357
532
  try {
358
- await executeRun(runId, this.providers, this.db, undefined, dbosStep);
533
+ await executeRun(runId, this.providers, this.db, undefined, stopAwareDbosStep(this.db, runId));
359
534
  }
360
535
  catch (err) {
361
536
  // An ownership loss leaves the workflow body as the SDK's own conflict error, so DBOS takes
362
537
  // its duplicate-execution path: it records nothing and parks this attempt until the winner
363
538
  // records the outcome. Any other error escaping here would be recorded as the workflow's
364
- // outcome while the winner still runs. The runner already logged the loss.
539
+ // outcome while the winner still runs. The runner already logged the loss. A forked-from
540
+ // workflow's own cancellation (dbosStepFor) goes back as itself: DBOS parks that too.
365
541
  if (err instanceof RunOwnershipLostError) {
366
- throw err.cause instanceof DbosErrors.DBOSWorkflowConflictError
542
+ throw err.cause instanceof DbosErrors.DBOSWorkflowConflictError ||
543
+ err.cause instanceof DbosErrors.DBOSWorkflowCancelledError
367
544
  ? err.cause
368
- : new DbosErrors.DBOSWorkflowConflictError(runId);
545
+ : new DbosErrors.DBOSWorkflowConflictError(DBOS.workflowID ?? runId);
546
+ }
547
+ // A replay met a step the recorded run does not have at the load step, ahead of executeRun's
548
+ // backstop (which settles a mismatch in any later step itself).
549
+ if (err instanceof StepReplayMismatchError) {
550
+ dbosLog.warn({ err, runId }, "replay no longer matches the recorded run");
551
+ // The same settle as executeRun's backstop: lost (first writer wins), its children stopped.
552
+ await loseNativeRun({ runId, db: this.db, providers: this.providers, ...nativeRunIntegrations(this.providers, this.db) }, STEPS_CHANGED_REASON);
553
+ return;
369
554
  }
370
555
  throw err;
371
556
  }
372
557
  finally {
373
558
  clearInterval(timer);
374
559
  cancellationReasons.delete(runId);
560
+ if (DBOS.workflowID)
561
+ cancellationReasons.delete(DBOS.workflowID);
375
562
  }
376
563
  }
377
564
  }
@@ -6,43 +6,31 @@
6
6
  * stop it needs only requested (Run.stopRequestedAt). The scheduler leader
7
7
  * carries both out through the server's own executor (`drainDeferredRuns`).
8
8
  *
9
- * Coding dispatch still needs the worker image and the repository's service
10
- * declaration and knowledge files, resolved at dispatch: those come from a
11
- * resolve-only view of the configured coding executor, whose start/stop are
12
- * never called here.
9
+ * Coding dispatch is recorded unresolved (`defersCodingResolution`): the worker image,
10
+ * service declaration and knowledge files are resolved by the process that starts the run
11
+ * (the drain's `beforeStart` hook), not here.
13
12
  */
14
13
  import type { PrismaClient } from "#prisma";
15
- import type { CodingImageSelector, Executor } from "./types.js";
16
- import type { CodingProvider } from "../../coding/provider.js";
14
+ import type { Executor } from "./types.js";
17
15
  export type DeferredExecutorDb = Pick<PrismaClient, "run">;
18
16
  export declare class DeferredExecutor implements Executor {
19
17
  private readonly db;
20
- /** Coding resolution only (image, service declaration, repository files); never started or stopped. */
21
- private readonly resolver?;
22
- constructor(db: DeferredExecutorDb,
23
- /** Coding resolution only (image, service declaration, repository files); never started or stopped. */
24
- resolver?: Executor | undefined);
18
+ readonly defersCodingResolution = true;
19
+ constructor(db: DeferredExecutorDb);
25
20
  start(runId: string): Promise<void>;
26
21
  stop(runId: string, reason?: string): Promise<void>;
27
- resolveCodingWorkerImage(selector: CodingImageSelector): string;
28
- resolveCodingToolImage(selector: CodingImageSelector): string | null;
29
- readCodingServiceDeclaration(input: {
30
- repository: string;
31
- baseRef: string;
32
- }): Promise<string | null>;
33
- readCodingRepositoryFile(input: {
34
- repository: string;
35
- baseRef: string;
36
- path: string;
37
- maxBytes: number;
38
- }): Promise<string | null>;
39
- supportsCodingServices(provider: CodingProvider): boolean;
40
22
  }
41
23
  export interface DrainDeferredRunsOptions {
42
24
  db: Pick<PrismaClient, "run">;
43
25
  executor: Executor;
44
26
  /** Where a start failure is recorded; defaults to marking the run failed with the error. */
45
27
  onStartFailed?: (runId: string, err: unknown) => Promise<void>;
28
+ /**
29
+ * Runs after a row is claimed and before `executor.start`. Resolves inputs the deferring process
30
+ * could not; false means the hook ended the run itself so it must not start. A throw is handled
31
+ * like a start failure.
32
+ */
33
+ beforeStart?: (runId: string) => Promise<boolean>;
46
34
  limit?: number;
47
35
  }
48
36
  /**
@@ -6,21 +6,17 @@
6
6
  * stop it needs only requested (Run.stopRequestedAt). The scheduler leader
7
7
  * carries both out through the server's own executor (`drainDeferredRuns`).
8
8
  *
9
- * Coding dispatch still needs the worker image and the repository's service
10
- * declaration and knowledge files, resolved at dispatch: those come from a
11
- * resolve-only view of the configured coding executor, whose start/stop are
12
- * never called here.
9
+ * Coding dispatch is recorded unresolved (`defersCodingResolution`): the worker image,
10
+ * service declaration and knowledge files are resolved by the process that starts the run
11
+ * (the drain's `beforeStart` hook), not here.
13
12
  */
14
13
  import { logger } from "../../core/logger.js";
15
14
  const deferredLog = logger.child({ module: "deferred-executor" });
16
15
  export class DeferredExecutor {
17
16
  db;
18
- resolver;
19
- constructor(db,
20
- /** Coding resolution only (image, service declaration, repository files); never started or stopped. */
21
- resolver) {
17
+ defersCodingResolution = true;
18
+ constructor(db) {
22
19
  this.db = db;
23
- this.resolver = resolver;
24
20
  }
25
21
  async start(runId) {
26
22
  await this.db.run.updateMany({ where: { id: runId, status: "pending" }, data: { startDeferredAt: new Date() } });
@@ -43,25 +39,6 @@ export class DeferredExecutor {
43
39
  data: { stopRequestedAt: new Date(), stopRequestReason: reason ?? null },
44
40
  });
45
41
  }
46
- resolveCodingWorkerImage(selector) {
47
- if (!this.resolver?.resolveCodingWorkerImage)
48
- throw new Error("coding_execution_not_configured");
49
- return this.resolver.resolveCodingWorkerImage(selector);
50
- }
51
- resolveCodingToolImage(selector) {
52
- if (!this.resolver?.resolveCodingToolImage)
53
- throw new Error("coding_execution_not_configured");
54
- return this.resolver.resolveCodingToolImage(selector);
55
- }
56
- async readCodingServiceDeclaration(input) {
57
- return this.resolver?.readCodingServiceDeclaration?.(input) ?? null;
58
- }
59
- async readCodingRepositoryFile(input) {
60
- return this.resolver?.readCodingRepositoryFile?.(input) ?? null;
61
- }
62
- supportsCodingServices(provider) {
63
- return this.resolver?.supportsCodingServices?.(provider) === true;
64
- }
65
42
  }
66
43
  /**
67
44
  * Starts runs a deferred executor dispatched, and stops runs it asked to stop, through the
@@ -88,7 +65,11 @@ export async function drainDeferredRuns(options) {
88
65
  continue;
89
66
  started += 1;
90
67
  // Not awaited: an in-process executor's start runs the whole run.
91
- void executor.start(id).catch(async (err) => {
68
+ void (async () => {
69
+ if (options.beforeStart && !(await options.beforeStart(id)))
70
+ return;
71
+ await executor.start(id);
72
+ })().catch(async (err) => {
92
73
  deferredLog.error({ err, runId: id }, "deferred run failed to start");
93
74
  await (options.onStartFailed?.(id, err) ??
94
75
  db.run.updateMany({