@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
@@ -18,19 +18,24 @@
18
18
  */
19
19
  import { createHash, randomBytes } from "node:crypto";
20
20
  import { z } from "zod";
21
- import { RunOwnershipLostError, runStepInline } from "../providers/engine/types.js";
21
+ import { Prisma } from "#prisma";
22
+ import { RunOwnershipLostError, STEPS_CHANGED_REASON, StepReplayMismatchError, runStepInline, } from "../providers/engine/types.js";
22
23
  import { isLlmEffort } from "../providers/llm/types.js";
23
24
  import { createPrivilegedHost } from "../sandbox/host-functions.js";
24
25
  import { consoleRedactionValues } from "../sandbox/secret-broker.js";
25
26
  import { runUserToolCall } from "../sandbox/user-tool.js";
26
- import { runSandboxedEngine, SandboxRunCancelledError, } from "../native-worker/gateway.js";
27
+ import { logUnexpectedError, runSandboxedEngine, SandboxRunCancelledError, } from "../native-worker/gateway.js";
27
28
  import { NATIVE_WORKER_PROTOCOL_VERSION } from "../native-worker/protocol.js";
28
29
  import { asStringArray, asPrefixMap } from "../sandbox/tool-capabilities.js";
29
30
  import { scopeDatastore } from "../providers/datastore/scoped.js";
30
31
  import { buildSecretsAccessor, scopeSecretsAccessor } from "./secrets.js";
32
+ import { loadAgentSecretValues, maskNamedSecrets, POSTING_TOOL_NAMES, SECRET_CHECK_UNAVAILABLE_MESSAGE, WITHHELD_TEXT_NOTICE, } from "./secret-guard.js";
31
33
  import { buildSharedDatastoreAccessor, scopeSharedDatastoreAccessor } from "./datastores.js";
32
34
  import { effectiveBudgetForRun } from "./budget-groups.js";
35
+ import { stopChildrenOfLostRun } from "./lost-run-children.js";
33
36
  import { createDelegationSiblings } from "./delegation-siblings.js";
37
+ import { findDelegationChild } from "./delegation-child.js";
38
+ import { DRIVABLE } from "./run-status.js";
34
39
  import { createSerialGate } from "./serial-gate.js";
35
40
  import { withRunHeartbeat } from "./run-heartbeat.js";
36
41
  import { ContinuationRefusedError, dispatchRun } from "./dispatch.js";
@@ -40,6 +45,7 @@ import { DELEGATE_TOOL_PREFIX, duplicateToolNames } from "./tool-names.js";
40
45
  import { SUBAGENT_MEMORY_GET_TOOL, PARENT_MEMORY_GET_TOOL, handleSubAgentMemoryGet, handleParentMemoryGet, } from "./subagent-memory-tools.js";
41
46
  import { prisma as defaultDb } from "./db.js";
42
47
  import { logger } from "./logger.js";
48
+ import { CHILD_RUN_FAILURE_CODES, declaredCodeOf } from "./error-code.js";
43
49
  import { loadCodingConcurrencyConfig } from "../config/providers.js";
44
50
  import { REVIEW_HOST_TOOL_DEFS, REVIEW_HOST_TOOL_NAMES, handleReviewHostTool, } from "./review-host-tools.js";
45
51
  import { closeOpenHostCheck } from "./review-host-checks.js";
@@ -55,14 +61,17 @@ import { emitRunFinishedEvents } from "./workflow-run-events.js";
55
61
  import { startReviewFixAfterReview } from "./review-fix.js";
56
62
  import { updateRelatedPullRequests } from "./related-pull-requests.js";
57
63
  import { syncMergeOrderChecksAfterRun } from "./merge-order-check.js";
64
+ import { markReadyAfterRun } from "./mark-ready.js";
58
65
  import { startDeferredForRequest } from "./host-events.js";
59
66
  import { recordNativeModelUsage } from "./model-usage.js";
60
67
  import { pinNativeRunPricing } from "./run-pricing.js";
61
68
  import { RoutingLlmProvider } from "../providers/llm/routing.js";
62
69
  import { ModelUnavailableError } from "../providers/llm/catalog-types.js";
63
70
  import { trackRun } from "./in-flight-runs.js";
71
+ import { ReviewReadLedger } from "./review-reads.js";
64
72
  import { createRepoAccessGate, requiredLevel } from "./repo-access.js";
65
- const runnerLog = logger.child({ module: "runner" });
73
+ import { loadBudgetEvent, ownerBudgetSentence } from "./budget-events.js";
74
+ export const runnerLog = logger.child({ module: "runner" });
66
75
  /**
67
76
  * Sub-agent dispatch uses one synthetic tool per declared child, named by the
68
77
  * boundName it's attached
@@ -145,7 +154,7 @@ const DelegateArgs = z
145
154
  grantParentMemoryKeys: z.array(z.string()).optional(),
146
155
  continuePriorRun: z.string().optional(),
147
156
  mergeOrder: z
148
- .number({ invalid_type_error: MERGE_ORDER_RANGE_MESSAGE })
157
+ .number({ error: MERGE_ORDER_RANGE_MESSAGE })
149
158
  .int(MERGE_ORDER_RANGE_MESSAGE)
150
159
  .min(1, MERGE_ORDER_RANGE_MESSAGE)
151
160
  .max(99, MERGE_ORDER_RANGE_MESSAGE)
@@ -325,18 +334,7 @@ function toIssueProjectLink(row) {
325
334
  maxNewIssuesPerRun: row.maxNewIssuesPerRun ?? null,
326
335
  };
327
336
  }
328
- /**
329
- * The two states a Run can still be driven out of. Every write `executeRun`
330
- * makes is filtered on these: a durable executor can have two attempts of the
331
- * same run in flight at once (a still-live original parked in a slow LLM step
332
- * and an adopted attempt the reconciler resumed elsewhere), and whichever
333
- * reaches a terminal state first must win. A conditional `updateMany` makes
334
- * that a database-level CAS rather than a race — the loser's WHERE matches
335
- * zero rows. It also stops a *later* attempt resurrecting a run the
336
- * reconciler already reaped: a `lost` row stays `lost`. A step-level
337
- * ownership loss writes nothing at all (RunOwnershipLostError).
338
- */
339
- const DRIVABLE = ["pending", "running"];
337
+ // DRIVABLE (the two states a Run can still be driven out of) lives in run-status.ts.
340
338
  /**
341
339
  * Raised when a run's step boundary observed a cancellation (DbosExecutor's
342
340
  * `stop`). `executeRun`'s backstop persists these as `cancelled` with the
@@ -370,6 +368,37 @@ async function finishRunClaimed(db, runId, data) {
370
368
  const updated = await db.run.updateMany({ where: { id: runId, status: { in: [...DRIVABLE] } }, data });
371
369
  return { run: await db.run.findUniqueOrThrow({ where: { id: runId } }), claimed: updated.count > 0 };
372
370
  }
371
+ /**
372
+ * A step runner made to notice a stop. Before each step this reads the run's own row: once anything
373
+ * else has made it terminal (stop_run, an MCP task's cancel, a parent run's stop, the reconciler),
374
+ * the run stops at this boundary with a RunCancelledError. The terminal row is never rewritten
375
+ * (finishRunClaimed matches zero rows), so the stop keeps its reason. Inline runs need it because
376
+ * nothing else interrupts their steps. DbosExecutor wraps its own step runner in it too: DBOS stops
377
+ * only a workflow it was told to cancel, and stop() can name only the workflow the run records.
378
+ */
379
+ export function stopAwareStep(db, runId, inner = runStepInline, options = {}) {
380
+ const check = async (name, own) => {
381
+ const read = db.run.findUnique({ where: { id: runId }, select: { status: true, executionWorkflowId: true } });
382
+ const row = options.failOpen ? await read.catch(() => null) : await read;
383
+ if (row?.executionWorkflowId && own && options.ownership?.forkedFrom(row.executionWorkflowId, own)) {
384
+ throw new RunOwnershipLostError(name);
385
+ }
386
+ if (row && !DRIVABLE.includes(row.status)) {
387
+ throw new RunCancelledError(`Run ${row.status}: stopped before step "${name}".`);
388
+ }
389
+ };
390
+ // Each check waits for the one before it, so steps reach `inner` in the order they were called,
391
+ // as they did without a read in front: a durable runner numbers a step when it is called (DBOS
392
+ // assigns its function id then), and a replay of concurrent delegations matches steps by number.
393
+ let previous = Promise.resolve();
394
+ return async (name, fn) => {
395
+ const own = options.ownership?.workflowId();
396
+ const checked = previous.then(() => check(name, own));
397
+ previous = checked.catch(() => undefined);
398
+ await checked;
399
+ return inner(name, fn);
400
+ };
401
+ }
373
402
  /**
374
403
  * Why a sandbox-mode native run is not executed here. The runner only ever
375
404
  * executes native runs in the control plane; a run whose snapshot says
@@ -399,12 +428,16 @@ export const CODING_CHILD_WAIT_GRACE_SEC = 60;
399
428
  * a parent left waiting on a child that never finishes.
400
429
  */
401
430
  export const SANDBOX_CHILD_WAIT_SEC = 60 * 60;
431
+ /** The leading snake_case code of an error message (`code: sentence`); messages can carry internals. */
402
432
  /**
403
433
  * What a coding sub-agent's run tells the parent run's model. A run refused
404
434
  * over its services carries the host sentence (never the raw Run.error), so
405
- * the parent can pass it on — for a mention, that is the requester.
435
+ * the parent can pass it on — for a mention, that is the requester. A failed
436
+ * run carries only its error code, so the parent can say why without seeing internals.
406
437
  */
407
- export function codingChildResult(run, codingResult) {
438
+ export function codingChildResult(run, codingResult,
439
+ /** The child's recorded budget refusal (loadBudgetEvent), if any. */
440
+ budgetEvent) {
408
441
  // Checked before the "refused" branch: a continuation whose PR closed
409
442
  // before the run ever spent anything is stored as "refused" (a preflight
410
443
  // refusal, container.ts), not "failed" -- but it is the same
@@ -415,6 +448,10 @@ export function codingChildResult(run, codingResult) {
415
448
  : run.status === "refused"
416
449
  ? serviceRefusalSentence(run.error)
417
450
  : null;
451
+ const errorCode = run.status === "failed" ? declaredCodeOf(run.error, CHILD_RUN_FAILURE_CODES) : undefined;
452
+ const budget = budgetEvent && (run.status === "budget_exhausted" || run.status === "refused")
453
+ ? ownerBudgetSentence(budgetEvent.event, budgetEvent.agentName)
454
+ : undefined;
418
455
  return JSON.stringify({
419
456
  status: run.status,
420
457
  finalText: run.finalText,
@@ -422,6 +459,8 @@ export function codingChildResult(run, codingResult) {
422
459
  tokensIn: run.tokensIn,
423
460
  tokensOut: run.tokensOut,
424
461
  ...(refusal ? { refusal } : {}),
462
+ ...(errorCode ? { errorCode } : {}),
463
+ ...(budget ? { budget } : {}),
425
464
  ...pullRequestOf(codingResult),
426
465
  });
427
466
  }
@@ -668,6 +707,18 @@ export async function loadNativeRun(options) {
668
707
  throw err;
669
708
  });
670
709
  }
710
+ /**
711
+ * Every value of the secrets `names` (readable or brokered; a SigV4 secret's key and token alone
712
+ * too, the same set the in-process host redacts), as a redaction list. Rejects when ANY read
713
+ * fails: a list missing one secret would let that secret through, so a caller treats a rejection
714
+ * as "no list" and fails closed. A secret with no value contributes nothing.
715
+ */
716
+ export async function readRedactionValues(accessor, names) {
717
+ const values = await Promise.all(names.map((secret) => accessor.resolve
718
+ ? accessor.resolve(secret).then((e) => (e ? consoleRedactionValues(e) : []))
719
+ : accessor.get(secret).then((v) => (v === undefined ? [] : [v]))));
720
+ return values.flat().filter((value) => typeof value === "string");
721
+ }
671
722
  /**
672
723
  * One attempt's tool surface for a native run. `builtinHandler(name)` is the
673
724
  * single source of truth for which names are trusted built-ins (memory,
@@ -683,6 +734,8 @@ export function createNativeRunTools(options) {
683
734
  // jira_create_issue's per-run cap counter: shared by every tool call of this attempt (a resumed attempt
684
735
  // starts a fresh one, floored by the run's recorded fingerprint creates).
685
736
  const issueCreationCounters = new Map();
737
+ // What this attempt's review tools have read, for repo_publish_review's unread_files gate.
738
+ const reviewReads = options.reviewReads ?? new ReviewReadLedger();
686
739
  // One per attempt: admits this run's delegations one at a time (see the delegate branch).
687
740
  const delegationGate = createSerialGate();
688
741
  // The children this attempt's delegations have running, and the sub-agents whose delegation is
@@ -690,7 +743,125 @@ export function createNativeRunTools(options) {
690
743
  // place in the same-child and limit checks while the gate is open to the others.
691
744
  const delegationSiblings = createDelegationSiblings();
692
745
  const waitingDelegations = new Set();
693
- const builtinHandler = (name) => {
746
+ /**
747
+ * Waits on a dispatched coding or sandbox-mode native child until it is terminal, its wait
748
+ * bound passes (it is then stopped), or this run is cancelled, and returns what the model sees.
749
+ * The bound is measured from the child's creation, so a re-executed delegate step (a resume, or
750
+ * a takeover under a new version) never extends how long this run waits for it.
751
+ */
752
+ const awaitDelegatedChild = async (wait) => {
753
+ const { executor, childRunId, kind, boundName } = wait;
754
+ const startedAt = Date.now();
755
+ let boundMs;
756
+ if (kind === "coding") {
757
+ const codingRun = await db.codingRun.findUnique({
758
+ where: { runId: childRunId },
759
+ select: { timeoutSec: true },
760
+ });
761
+ const { queueTimeoutSec } = loadCodingConcurrencyConfig();
762
+ boundMs = (queueTimeoutSec + (codingRun?.timeoutSec ?? 0) + CODING_CHILD_WAIT_GRACE_SEC) * 1000;
763
+ }
764
+ else {
765
+ boundMs = SANDBOX_CHILD_WAIT_SEC * 1000;
766
+ }
767
+ const elapsedMs = Math.max(0, Date.now() - wait.createdAtMs);
768
+ // Executor.start may resolve before the child is terminal, so wait on the row.
769
+ const waited = await waitForCodingChild({
770
+ db,
771
+ executor,
772
+ childRunId,
773
+ parentRunId: runId,
774
+ boundMs: Math.max(0, boundMs - elapsedMs),
775
+ });
776
+ wait.onSettled();
777
+ runnerLog.info({
778
+ runId,
779
+ childRunId,
780
+ boundName,
781
+ outcome: waited.kind === "terminal" ? waited.run.status : waited.kind,
782
+ durationMs: Date.now() - startedAt,
783
+ }, "delegation finished");
784
+ if (waited.kind === "timed_out") {
785
+ return JSON.stringify({
786
+ error: "subagent_wait_timed_out",
787
+ runId: childRunId,
788
+ message: kind === "coding"
789
+ ? "The coding sub-agent did not finish within its queue timeout plus run timeout; it was stopped and produced no result."
790
+ : "The sandbox sub-agent did not finish in time; it was stopped and produced no result.",
791
+ });
792
+ }
793
+ if (waited.kind === "parent_cancelled") {
794
+ return JSON.stringify({
795
+ error: "parent_cancelled",
796
+ runId: childRunId,
797
+ message: kind === "coding"
798
+ ? "This run was cancelled while waiting for the coding sub-agent; the sub-agent was stopped."
799
+ : "This run was cancelled while waiting for the sandbox sub-agent; the sub-agent was stopped.",
800
+ });
801
+ }
802
+ if (kind === "coding") {
803
+ const stored = await db.codingRun
804
+ .findUnique({ where: { runId: childRunId }, select: { result: true } })
805
+ .catch((err) => {
806
+ runnerLog.warn({ err, childRunId }, "could not read the coding sub-run's result");
807
+ return null;
808
+ });
809
+ return codingChildResult(waited.run, stored?.result, await loadBudgetEvent(db, childRunId));
810
+ }
811
+ return nativeChildResult(waited.run, wait.mergeOrderNote);
812
+ };
813
+ /**
814
+ * A re-executed delegate call whose child already exists (findDelegationChild): wait on it
815
+ * again rather than refuse it as a duplicate. A control-plane native child ran inline in the
816
+ * attempt that died, so nothing drives it any more: it is stopped (or its result returned, if
817
+ * it had finished) and no new child is started.
818
+ */
819
+ const reattachDelegatedChild = async (child, boundName, argsJson) => {
820
+ const row = await db.run.findUniqueOrThrow({ where: { id: child.id } });
821
+ const childAgentRow = await db.agent.findUnique({ where: { id: row.agentId }, select: { kind: true } });
822
+ const parsedArgs = parseDelegateArgs(argsJson);
823
+ const mergeOrderNote = "args" in parsedArgs && parsedArgs.args.mergeOrder !== undefined ? MERGE_ORDER_IGNORED_NOTE : undefined;
824
+ const coding = childAgentRow?.kind === "coding";
825
+ if (!coding && row.nativeExecutionMode !== "sandbox") {
826
+ if (!DRIVABLE.includes(row.status))
827
+ return nativeChildResult(row, mergeOrderNote);
828
+ await providers.executor?.stop(child.id, "parent resumed without it").catch((err) => {
829
+ runnerLog.warn({ err, childRunId: child.id, runId }, "failed to stop an interrupted sub-agent run");
830
+ });
831
+ return JSON.stringify({
832
+ error: "subagent_interrupted",
833
+ message: "The sub-agent was interrupted by a restart and cannot be resumed; no new sub-agent run was started.",
834
+ });
835
+ }
836
+ if (!providers.executor) {
837
+ return coding
838
+ ? JSON.stringify({
839
+ error: "coding_dispatch_unavailable",
840
+ message: "This execution context has no Executor wired in, so a coding-kind sub-agent cannot be dispatched.",
841
+ })
842
+ : JSON.stringify({
843
+ error: "sandbox_dispatch_unavailable",
844
+ message: "This execution context has no Executor wired in, so a sandbox-mode sub-agent cannot be dispatched.",
845
+ });
846
+ }
847
+ // Counted as a running sibling while it is waited on, as a fresh dispatch is.
848
+ const finishSibling = delegationSiblings.start();
849
+ try {
850
+ return await awaitDelegatedChild({
851
+ executor: providers.executor,
852
+ childRunId: child.id,
853
+ createdAtMs: child.createdAt.getTime(),
854
+ kind: coding ? "coding" : "native-sandbox",
855
+ boundName,
856
+ mergeOrderNote: coding ? undefined : mergeOrderNote,
857
+ onSettled: finishSibling,
858
+ });
859
+ }
860
+ finally {
861
+ finishSibling();
862
+ }
863
+ };
864
+ const unguardedBuiltinHandler = (name) => {
694
865
  if (loaded.memoryEnabled && MEMORY_TOOL_NAMES.has(name)) {
695
866
  return async (argsJson) => {
696
867
  return handleMemoryTool(name, argsJson, loaded.agentId, providers.memory);
@@ -703,6 +874,7 @@ export function createNativeRunTools(options) {
703
874
  agentId: loaded.agentId,
704
875
  links: loaded.repositoryLinks,
705
876
  hosts: reviewHosts,
877
+ reads: reviewReads,
706
878
  db,
707
879
  runCheck: check && !check.completedAt
708
880
  ? {
@@ -791,7 +963,9 @@ export function createNativeRunTools(options) {
791
963
  };
792
964
  }
793
965
  if (name.startsWith(DELEGATE_TOOL_PREFIX)) {
794
- return async (argsJson) => {
966
+ return async (argsJson, toolCallId) => {
967
+ // An empty id (an adapter that had none) is no key: every such call would share it.
968
+ const callId = toolCallId || undefined;
795
969
  const boundName = name.slice(DELEGATE_TOOL_PREFIX.length);
796
970
  const edge = loaded.subAgentEdges.find((e) => e.boundName === boundName);
797
971
  if (!edge) {
@@ -800,8 +974,36 @@ export function createNativeRunTools(options) {
800
974
  message: `No sub-agent is bound to name "${boundName}".`,
801
975
  });
802
976
  }
977
+ // A re-executed delegate step (a resume, or a takeover under a new version) finds the
978
+ // child this very call created and waits on it again, instead of refusing it as a duplicate.
979
+ const existingChild = await findDelegationChild(db, runId, edge.childAgentId, callId);
980
+ if (existingChild) {
981
+ runnerLog.info({ runId, childRunId: existingChild.id, boundName }, "delegation re-attached");
982
+ return reattachDelegatedChild(existingChild, boundName, argsJson);
983
+ }
803
984
  let release = await delegationGate.acquire();
804
985
  let finishSibling = () => { };
986
+ /**
987
+ * A child create that lost the (parentRunId, parentCallId) unique key to another execution of
988
+ * this same delegate step (a stalled owner overlapping an adopted attempt): wait on the child
989
+ * that execution created, as a re-executed step does. A key held by no child of this agent
990
+ * (an id colliding with another sub-agent's call) is the model's to see, never a run failure.
991
+ */
992
+ const afterDelegationKeyConflict = async (err) => {
993
+ if (!(err instanceof Prisma.PrismaClientKnownRequestError && err.code === "P2002"))
994
+ throw err;
995
+ release();
996
+ const raced = await findDelegationChild(db, runId, edge.childAgentId, callId);
997
+ if (raced) {
998
+ runnerLog.info({ runId, childRunId: raced.id, boundName }, "delegation re-attached after a concurrent create");
999
+ return reattachDelegatedChild(raced, boundName, argsJson);
1000
+ }
1001
+ runnerLog.warn({ err, runId, boundName }, "delegation child create hit a unique key held by no child of this agent");
1002
+ return JSON.stringify({
1003
+ error: "delegation_conflict",
1004
+ message: "The sub-agent run could not be created: its call id is already used by another sub-agent run.",
1005
+ });
1006
+ };
805
1007
  try {
806
1008
  // At most maxDelegationsPerRun dispatches per run (default one: a
807
1009
  // classifier deciding plan-vs-implement commits to exactly one child),
@@ -839,6 +1041,7 @@ export function createNativeRunTools(options) {
839
1041
  while (delegationSiblings.inFlight > 0) {
840
1042
  // Taken before the budget read, so a sibling that finishes during it still wakes this wait.
841
1043
  const finishes = delegationSiblings.finishes;
1044
+ // What dispatch will reserve: a coding child's base too, its overrun is never reserved.
842
1045
  const { exhaustedBy } = await effectiveBudgetForRun(db, childAgent, new Date(), runId);
843
1046
  const remainingMs = deadline - Date.now();
844
1047
  if (!exhaustedBy || remainingMs <= 0)
@@ -898,6 +1101,7 @@ export function createNativeRunTools(options) {
898
1101
  continuesCodingRunId: args.continuePriorRun,
899
1102
  mergeOrder: args.mergeOrder,
900
1103
  parentRunId: runId,
1104
+ parentCallId: callId,
901
1105
  // The child's result flows back into this run, which the
902
1106
  // triggerer sees, so the child is visible to them too.
903
1107
  triggeredById: existingRun.triggeredById,
@@ -917,7 +1121,7 @@ export function createNativeRunTools(options) {
917
1121
  // a pull request another deployment opened, say) is the model's to
918
1122
  // report, not a reason to fail the whole run.
919
1123
  if (!(err instanceof ContinuationRefusedError))
920
- throw err;
1124
+ return await afterDelegationKeyConflict(err);
921
1125
  return JSON.stringify({
922
1126
  error: "continuation_refused",
923
1127
  message: `${err.message} No sub-agent run was started. Do not open a new pull request in its place: ` +
@@ -928,49 +1132,16 @@ export function createNativeRunTools(options) {
928
1132
  return JSON.stringify({ error: "dispatch_failed", message: "The sub-agent run could not be created." });
929
1133
  }
930
1134
  const childRunId = dispatched.run.id;
931
- const startedAt = Date.now();
932
1135
  runnerLog.info({ runId, childRunId, boundName, kind: "coding" }, "delegation started");
933
- const codingRun = await db.codingRun.findUnique({
934
- where: { runId: childRunId },
935
- select: { timeoutSec: true },
936
- });
937
- const { queueTimeoutSec } = loadCodingConcurrencyConfig();
938
- const waited = await waitForCodingChild({
939
- db,
1136
+ return await awaitDelegatedChild({
940
1137
  executor: providers.executor,
941
1138
  childRunId,
942
- parentRunId: runId,
943
- boundMs: (queueTimeoutSec + (codingRun?.timeoutSec ?? 0) + CODING_CHILD_WAIT_GRACE_SEC) * 1000,
944
- });
945
- finishSibling();
946
- runnerLog.info({
947
- runId,
948
- childRunId,
1139
+ createdAtMs: dispatched.run.startedAt.getTime(),
1140
+ kind: "coding",
949
1141
  boundName,
950
- outcome: waited.kind === "terminal" ? waited.run.status : waited.kind,
951
- durationMs: Date.now() - startedAt,
952
- }, "delegation finished");
953
- if (waited.kind === "timed_out") {
954
- return JSON.stringify({
955
- error: "subagent_wait_timed_out",
956
- runId: childRunId,
957
- message: "The coding sub-agent did not finish within its queue timeout plus run timeout; it was stopped and produced no result.",
958
- });
959
- }
960
- if (waited.kind === "parent_cancelled") {
961
- return JSON.stringify({
962
- error: "parent_cancelled",
963
- runId: childRunId,
964
- message: "This run was cancelled while waiting for the coding sub-agent; the sub-agent was stopped.",
965
- });
966
- }
967
- const stored = await db.codingRun
968
- .findUnique({ where: { runId: childRunId }, select: { result: true } })
969
- .catch((err) => {
970
- runnerLog.warn({ err, childRunId }, "could not read the coding sub-run's result");
971
- return null;
1142
+ mergeOrderNote: undefined,
1143
+ onSettled: finishSibling,
972
1144
  });
973
- return codingChildResult(waited.run, stored?.result);
974
1145
  }
975
1146
  const taskOverride = delegationTaskOverride(args);
976
1147
  // mergeOrder is a coding-only concept (it orders CodingRuns within a merge); a native
@@ -988,73 +1159,64 @@ export function createNativeRunTools(options) {
988
1159
  message: "This execution context has no Executor wired in, so a sandbox-mode sub-agent cannot be dispatched.",
989
1160
  });
990
1161
  }
991
- const dispatched = await dispatchRun({
992
- db,
993
- executor: providers.executor,
994
- selfDefects: { db, issueTrackers },
995
- agentId: edge.childAgentId,
996
- trigger: "subagent",
997
- taskOverride,
998
- grantedParentMemoryKeys: args.grantParentMemoryKeys ?? [],
999
- parentRunId: runId,
1000
- triggeredById: existingRun.triggeredById,
1001
- awaitExecution: true,
1002
- onPersisted: (persistedRun) => {
1003
- if (persistedRun.status !== "refused" && persistedRun.status !== "failed") {
1004
- finishSibling = delegationSiblings.start();
1005
- }
1006
- release();
1007
- },
1008
- });
1162
+ let dispatched;
1163
+ try {
1164
+ dispatched = await dispatchRun({
1165
+ db,
1166
+ executor: providers.executor,
1167
+ selfDefects: { db, issueTrackers },
1168
+ agentId: edge.childAgentId,
1169
+ trigger: "subagent",
1170
+ taskOverride,
1171
+ grantedParentMemoryKeys: args.grantParentMemoryKeys ?? [],
1172
+ parentRunId: runId,
1173
+ parentCallId: callId,
1174
+ triggeredById: existingRun.triggeredById,
1175
+ awaitExecution: true,
1176
+ onPersisted: (persistedRun) => {
1177
+ if (persistedRun.status !== "refused" && persistedRun.status !== "failed") {
1178
+ finishSibling = delegationSiblings.start();
1179
+ }
1180
+ release();
1181
+ },
1182
+ });
1183
+ }
1184
+ catch (err) {
1185
+ return await afterDelegationKeyConflict(err);
1186
+ }
1009
1187
  if (!dispatched) {
1010
1188
  return JSON.stringify({ error: "dispatch_failed", message: "The sub-agent run could not be created." });
1011
1189
  }
1012
1190
  const childRunId = dispatched.run.id;
1013
- const startedAt = Date.now();
1014
1191
  runnerLog.info({ runId, childRunId, boundName, kind: "native-sandbox" }, "delegation started");
1015
- // Executor.start may resolve before the child is terminal, so wait on the row like a coding child.
1016
- const waited = await waitForCodingChild({
1017
- db,
1192
+ return await awaitDelegatedChild({
1018
1193
  executor: providers.executor,
1019
1194
  childRunId,
1020
- parentRunId: runId,
1021
- boundMs: SANDBOX_CHILD_WAIT_SEC * 1000,
1022
- });
1023
- finishSibling();
1024
- runnerLog.info({
1025
- runId,
1026
- childRunId,
1195
+ createdAtMs: dispatched.run.startedAt.getTime(),
1196
+ kind: "native-sandbox",
1027
1197
  boundName,
1028
- outcome: waited.kind === "terminal" ? waited.run.status : waited.kind,
1029
- durationMs: Date.now() - startedAt,
1030
- }, "delegation finished");
1031
- if (waited.kind === "timed_out") {
1032
- return JSON.stringify({
1033
- error: "subagent_wait_timed_out",
1034
- runId: childRunId,
1035
- message: "The sandbox sub-agent did not finish in time; it was stopped and produced no result.",
1036
- });
1037
- }
1038
- if (waited.kind === "parent_cancelled") {
1039
- return JSON.stringify({
1040
- error: "parent_cancelled",
1041
- runId: childRunId,
1042
- message: "This run was cancelled while waiting for the sandbox sub-agent; the sub-agent was stopped.",
1043
- });
1044
- }
1045
- return nativeChildResult(waited.run, mergeOrderNote);
1198
+ mergeOrderNote,
1199
+ onSettled: finishSibling,
1200
+ });
1201
+ }
1202
+ let childRun;
1203
+ try {
1204
+ childRun = await db.run.create({
1205
+ data: {
1206
+ agentId: edge.childAgentId,
1207
+ trigger: "subagent",
1208
+ parentRunId: runId,
1209
+ parentCallId: callId,
1210
+ grantedParentMemoryKeys: args.grantParentMemoryKeys ?? [],
1211
+ taskOverride,
1212
+ triggeredById: existingRun.triggeredById,
1213
+ nativeExecutionMode: childAgent.nativeExecutionMode,
1214
+ },
1215
+ });
1216
+ }
1217
+ catch (err) {
1218
+ return await afterDelegationKeyConflict(err);
1046
1219
  }
1047
- const childRun = await db.run.create({
1048
- data: {
1049
- agentId: edge.childAgentId,
1050
- trigger: "subagent",
1051
- parentRunId: runId,
1052
- grantedParentMemoryKeys: args.grantParentMemoryKeys ?? [],
1053
- taskOverride,
1054
- triggeredById: existingRun.triggeredById,
1055
- nativeExecutionMode: childAgent.nativeExecutionMode,
1056
- },
1057
- });
1058
1220
  finishSibling = delegationSiblings.start();
1059
1221
  // The child row exists: admit the next delegation while this child runs.
1060
1222
  release();
@@ -1081,6 +1243,26 @@ export function createNativeRunTools(options) {
1081
1243
  }
1082
1244
  return undefined;
1083
1245
  };
1246
+ /**
1247
+ * The only route to a built-in tool, in both execution modes (the in-process loop and the native
1248
+ * gateway's builtin.call). A tool that posts model-written text to a host, and a delegate_to_*
1249
+ * call (its task text becomes the child run's stored task), has every attached secret's value
1250
+ * masked in its arguments first; when the secrets cannot be read the call is refused and
1251
+ * neither the host nor a child run is reached. (The native gateway serves delegate_to_* through
1252
+ * durableDelegate, which masks the same way.)
1253
+ */
1254
+ const builtinHandler = (name) => {
1255
+ const handler = unguardedBuiltinHandler(name);
1256
+ if (!handler || !(POSTING_TOOL_NAMES.has(name) || name.startsWith(DELEGATE_TOOL_PREFIX)))
1257
+ return handler;
1258
+ return async (argsJson, toolCallId) => {
1259
+ const masked = await maskModelWrittenArgs({ db, secrets: providers.secrets, agentId: loaded.agentId, runId }, name, argsJson);
1260
+ if (masked === undefined) {
1261
+ return JSON.stringify({ error: "secret_check_unavailable", message: SECRET_CHECK_UNAVAILABLE_MESSAGE });
1262
+ }
1263
+ return handler(masked, toolCallId);
1264
+ };
1265
+ };
1084
1266
  /**
1085
1267
  * The privileged host for one call of the user tool `name`, scoped to that attachment's
1086
1268
  * consented capabilities. Undefined for a name that is not an attached user tool. In-process it
@@ -1101,17 +1283,15 @@ export function createNativeRunTools(options) {
1101
1283
  const tool = toolsByName.get(name);
1102
1284
  return tool ? scopedHost(name, tool, signal, redactSecretValues) : undefined;
1103
1285
  };
1104
- /** Every secret value the user tool `name` may use (readable or brokered), for console redaction by a stateless gateway. */
1286
+ /**
1287
+ * Every secret value the user tool `name` may use (readable or brokered), for console redaction
1288
+ * by a stateless gateway. Rejects when any read fails (readRedactionValues).
1289
+ */
1105
1290
  const readableSecretValues = async (name) => {
1106
1291
  const tool = toolsByName.get(name);
1107
1292
  if (!tool)
1108
1293
  return [];
1109
- const scoped = scopeSecretsAccessor(secretsAccessor, tool.allowedSecrets);
1110
- const values = await Promise.all(tool.allowedSecrets.map((secret) => (scoped.resolve
1111
- ? scoped.resolve(secret).then((e) => (e ? consoleRedactionValues(e) : []))
1112
- : scoped.get(secret).then((v) => (v === undefined ? [] : [v]))).catch(() => [])));
1113
- // A SigV4 secret's key and token alone, too: the same set the in-process host redacts.
1114
- return values.flat().filter((value) => typeof value === "string");
1294
+ return readRedactionValues(scopeSecretsAccessor(secretsAccessor, tool.allowedSecrets), tool.allowedSecrets);
1115
1295
  };
1116
1296
  const runUserTool = async (name, argsJson) => {
1117
1297
  const tool = toolsByName.get(name);
@@ -1123,9 +1303,9 @@ export function createNativeRunTools(options) {
1123
1303
  }
1124
1304
  return runUserToolCall(tool, argsJson, (signal) => scopedHost(name, tool, signal));
1125
1305
  };
1126
- const runSandboxTool = async (name, argsJson) => {
1306
+ const runSandboxTool = async (name, argsJson, callId) => {
1127
1307
  const builtin = builtinHandler(name);
1128
- return builtin ? builtin(argsJson) : runUserTool(name, argsJson);
1308
+ return builtin ? builtin(argsJson, callId) : runUserTool(name, argsJson);
1129
1309
  };
1130
1310
  return { toolsByName, builtinHandler, privilegedHostFor, readableSecretValues, runUserTool, runSandboxTool };
1131
1311
  }
@@ -1135,11 +1315,13 @@ export function createNativeRunTools(options) {
1135
1315
  * tree's shared budget (computeRunTreeSpend) then counts a running parent's spend so far, as it
1136
1316
  * already does for coding runs, whose proxy ledger writes their totals live. Best-effort: a
1137
1317
  * failed write only delays what observers see, and finishRun writes the final totals anyway.
1318
+ * A cancelled run takes them too: a stop lands mid-step, and that step's spend is real, while the
1319
+ * cancelled run's terminal write (failNativeRun) records no totals of its own.
1138
1320
  */
1139
1321
  export async function recordNativeRunProgress(db, runId, progress) {
1140
1322
  try {
1141
1323
  await db.run.updateMany({
1142
- where: { id: runId, status: "running" },
1324
+ where: { id: runId, status: { in: ["running", "cancelled"] } },
1143
1325
  data: {
1144
1326
  turns: progress.turns,
1145
1327
  tokensIn: progress.usage.tokensIn,
@@ -1153,6 +1335,35 @@ export async function recordNativeRunProgress(db, runId, progress) {
1153
1335
  runnerLog.warn({ err, runId }, "failed to record run progress");
1154
1336
  }
1155
1337
  }
1338
+ /**
1339
+ * `texts` with every value of every secret attached to the run's agent masked, for a terminal
1340
+ * write: the stored row is what get_run, the viewer, host and issue status comments and run
1341
+ * notifications all read. Fails closed: when the secrets cannot be read, every non-null text is
1342
+ * replaced by WITHHELD_TEXT_NOTICE (the log line carries no text and no error message). A pure
1343
+ * function of the stored secrets, so a durable replay writes the same row.
1344
+ */
1345
+ async function maskRunTexts(ctx, texts) {
1346
+ const entries = Object.entries(texts);
1347
+ if (!entries.some(([, text]) => typeof text === "string" && text.length > 0))
1348
+ return texts;
1349
+ let mask;
1350
+ try {
1351
+ const row = await ctx.db.run.findUnique({ where: { id: ctx.runId }, select: { agentId: true } });
1352
+ if (!row)
1353
+ throw new Error("run not found");
1354
+ const named = await loadAgentSecretValues(ctx.db, ctx.providers.secrets, row.agentId);
1355
+ mask = (text) => maskNamedSecrets(text, named);
1356
+ }
1357
+ catch (err) {
1358
+ runnerLog.warn({
1359
+ runId: ctx.runId,
1360
+ event: "run_text_secret_check_failed",
1361
+ errorName: err instanceof Error ? err.name : typeof err,
1362
+ }, "could not read the agent's secrets to check the run's final text and error; storing the withheld notice");
1363
+ mask = () => WITHHELD_TEXT_NOTICE;
1364
+ }
1365
+ return Object.fromEntries(entries.map(([key, text]) => [key, typeof text === "string" && text.length > 0 ? mask(text) : text]));
1366
+ }
1156
1367
  /** Everything after the terminal write, shared by the normal and backstop paths. */
1157
1368
  async function settleFinishedNativeRun(ctx, finished, claimed) {
1158
1369
  const { runId, db, providers, reviewHosts, repoAccess, issueTrackers } = ctx;
@@ -1192,17 +1403,22 @@ async function settleFinishedNativeRun(ctx, finished, claimed) {
1192
1403
  // host reads). Only for a tree root that delegated. Not gated on `claimed`: the sync is
1193
1404
  // idempotent (an unchanged check writes nothing). Never throws.
1194
1405
  await syncMergeOrderChecksAfterRun({ db, hosts: reviewHosts }, finished);
1406
+ // A reviewer run's end may be the last thing a pull request waited for: mark it ready for review
1407
+ // when its builder link opted in (#287). Last, so nothing above waits on its host reads; idempotent
1408
+ // (claimed once per pull request); no host reads unless a link opted in. Never throws.
1409
+ await markReadyAfterRun({ db, hosts: reviewHosts, log: runnerLog }, runId);
1195
1410
  return finished;
1196
1411
  }
1197
1412
  /** The terminal write for an engine result, then its usage, host/issue status, and follow-ups. */
1198
1413
  export async function finishNativeRun(ctx, model, result) {
1414
+ const texts = await maskRunTexts(ctx, { error: result.error ?? null, finalText: result.finalText || null });
1199
1415
  const { run: finished, claimed } = await finishRunClaimed(ctx.db, ctx.runId, {
1200
1416
  status: result.status,
1201
1417
  tokensIn: result.usage.tokensIn,
1202
1418
  tokensOut: result.usage.tokensOut,
1203
1419
  costUsd: result.usage.costUsd,
1204
- error: result.error ?? null,
1205
- finalText: result.finalText || null,
1420
+ error: texts.error,
1421
+ finalText: texts.finalText,
1206
1422
  turns: result.turns,
1207
1423
  finishedAt: new Date(),
1208
1424
  });
@@ -1212,11 +1428,24 @@ export async function finishNativeRun(ctx, model, result) {
1212
1428
  }
1213
1429
  /** The backstop terminal write for a run that threw. A cancellation is not a failure: it carries the operator's own reason. */
1214
1430
  export async function failNativeRun(ctx, err) {
1431
+ const { error } = await maskRunTexts(ctx, { error: err instanceof Error ? err.message : String(err) });
1215
1432
  const { run: finished, claimed } = await finishRunClaimed(ctx.db, ctx.runId, {
1216
1433
  status: err instanceof RunCancelledError || err instanceof SandboxRunCancelledError ? "cancelled" : "failed",
1217
- error: err instanceof Error ? err.message : String(err),
1434
+ error,
1435
+ finishedAt: new Date(),
1436
+ });
1437
+ return settleFinishedNativeRun(ctx, finished, claimed);
1438
+ }
1439
+ /** The terminal write for a run that cannot continue (it ends `lost` with `reason`), then the same settle path. */
1440
+ export async function loseNativeRun(ctx, reason) {
1441
+ const { error } = await maskRunTexts(ctx, { error: reason });
1442
+ const { run: finished, claimed } = await finishRunClaimed(ctx.db, ctx.runId, {
1443
+ status: "lost",
1444
+ error,
1218
1445
  finishedAt: new Date(),
1219
1446
  });
1447
+ if (claimed)
1448
+ await stopChildrenOfLostRun(ctx.db, ctx.providers.executor, ctx.runId);
1220
1449
  return settleFinishedNativeRun(ctx, finished, claimed);
1221
1450
  }
1222
1451
  /** Where a run stands for the native gateway: still drivable, cancelled (its task), or otherwise ended. */
@@ -1250,6 +1479,25 @@ export function sandboxWorkerInput(runId, loaded, tools) {
1250
1479
  pricing: { ...loaded.pricing.entry, efforts: [...loaded.pricing.entry.efforts] },
1251
1480
  };
1252
1481
  }
1482
+ /**
1483
+ * A tool call's model-written arguments with every secret value attached to the agent masked, or
1484
+ * undefined (logged by error name only) when the secrets cannot be read and the call must be refused.
1485
+ */
1486
+ async function maskModelWrittenArgs(ctx, tool, argsJson) {
1487
+ try {
1488
+ const named = await loadAgentSecretValues(ctx.db, ctx.secrets, ctx.agentId);
1489
+ return maskNamedSecrets(argsJson, named);
1490
+ }
1491
+ catch (err) {
1492
+ runnerLog.warn({
1493
+ runId: ctx.runId,
1494
+ tool,
1495
+ event: "post_secret_check_failed",
1496
+ errorName: err instanceof Error ? err.name : typeof err,
1497
+ }, "could not read the agent's secrets to check a posted text; refusing the call");
1498
+ return undefined;
1499
+ }
1500
+ }
1253
1501
  /** Long-poll window: well under common proxy and load-balancer idle timeouts. */
1254
1502
  export const DELEGATION_POLL_WINDOW_MS = 20_000;
1255
1503
  /**
@@ -1308,13 +1556,24 @@ export async function durableDelegate(ctx, callId, name, argsJson) {
1308
1556
  if (!childRunId && state?.childAgentId === edge.childAgentId) {
1309
1557
  // A replica that died between dispatching the child and recording it left the child row:
1310
1558
  // adopt it rather than refusing this very delegation as a duplicate.
1311
- const orphan = await db.run.findFirst({
1312
- where: { parentRunId: runId, agentId: edge.childAgentId },
1313
- select: { id: true },
1314
- });
1315
- childRunId = orphan?.id ?? null;
1559
+ childRunId = (await findDelegationChild(db, runId, edge.childAgentId, callId))?.id ?? null;
1560
+ // Recorded, so later calls wait on it by id.
1561
+ if (childRunId) {
1562
+ await ledger.setDelegation(sessionId, callId, { status: "pending", childAgentId: edge.childAgentId, childRunId });
1563
+ }
1316
1564
  }
1317
1565
  if (!childRunId) {
1566
+ // About to create a child: its task is model-written and stored, so mask the parent's secret
1567
+ // values first. Only here, never on a poll of an existing child (a transient secret-read
1568
+ // failure must not orphan a running child). Recorded as the call's result, which also completes a waiting row; the model's retry comes with a new callId.
1569
+ const maskedArgsJson = await maskModelWrittenArgs({ db, secrets: providers.secrets, agentId: loaded.agentId, runId }, name, argsJson);
1570
+ if (maskedArgsJson === undefined) {
1571
+ return finish(JSON.stringify({ error: "secret_check_unavailable", message: SECRET_CHECK_UNAVAILABLE_MESSAGE }));
1572
+ }
1573
+ const maskedArgs = parseDelegateArgs(maskedArgsJson);
1574
+ if ("refusal" in maskedArgs)
1575
+ return finish(maskedArgs.refusal);
1576
+ const dispatchArgs = maskedArgs.args;
1318
1577
  const limit = loaded.maxDelegationsPerRun ?? 1;
1319
1578
  const refusalNow = async (reader) => {
1320
1579
  const prior = await reader.run.findMany({ where: { parentRunId: runId }, select: { agentId: true } });
@@ -1356,9 +1615,17 @@ export async function durableDelegate(ctx, callId, name, argsJson) {
1356
1615
  agentId: edge.childAgentId,
1357
1616
  trigger: "subagent",
1358
1617
  ...(coding
1359
- ? { codingTask: args.task, continuesCodingRunId: args.continuePriorRun, mergeOrder: args.mergeOrder }
1360
- : { taskOverride: delegationTaskOverride(args), grantedParentMemoryKeys: args.grantParentMemoryKeys ?? [] }),
1618
+ ? {
1619
+ codingTask: dispatchArgs.task,
1620
+ continuesCodingRunId: dispatchArgs.continuePriorRun,
1621
+ mergeOrder: dispatchArgs.mergeOrder,
1622
+ }
1623
+ : {
1624
+ taskOverride: delegationTaskOverride(dispatchArgs),
1625
+ grantedParentMemoryKeys: dispatchArgs.grantParentMemoryKeys ?? [],
1626
+ }),
1361
1627
  parentRunId: runId,
1628
+ parentCallId: callId,
1362
1629
  // The child's result flows back into this run, which the triggerer sees, so the child is visible to them too.
1363
1630
  triggeredById: existingRun.triggeredById,
1364
1631
  beforePersist: async (tx) => {
@@ -1370,12 +1637,21 @@ export async function durableDelegate(ctx, callId, name, argsJson) {
1370
1637
  });
1371
1638
  }
1372
1639
  catch (err) {
1373
- if (!(err instanceof ContinuationRefusedError))
1374
- throw err;
1640
+ if (err instanceof ContinuationRefusedError) {
1641
+ return finish(JSON.stringify({
1642
+ error: "continuation_refused",
1643
+ message: `${err.message} No sub-agent run was started. Do not open a new pull request in its place: ` +
1644
+ "tell the requester that this wardby deployment cannot continue that pull request's branch.",
1645
+ }));
1646
+ }
1647
+ // Recorded as the call's result, so the model can report it and a retried callId never
1648
+ // dispatches twice. Only the leading code is exposed: messages can carry internals.
1649
+ const code = declaredCodeOf(err);
1650
+ const values = await ctx.secretValues?.().catch(() => undefined);
1651
+ logUnexpectedError(runnerLog, err, values, "delegation dispatch failed", { runId, boundName });
1375
1652
  return finish(JSON.stringify({
1376
- error: "continuation_refused",
1377
- message: `${err.message} No sub-agent run was started. Do not open a new pull request in its place: ` +
1378
- "tell the requester that this wardby deployment cannot continue that pull request's branch.",
1653
+ error: "dispatch_failed",
1654
+ message: `The sub-agent run could not be created${code ? ` (${code})` : ""}.`,
1379
1655
  }));
1380
1656
  }
1381
1657
  if (!dispatched) {
@@ -1403,7 +1679,7 @@ export async function durableDelegate(ctx, callId, name, argsJson) {
1403
1679
  const stored = await db.codingRun
1404
1680
  .findUnique({ where: { runId: childRunId }, select: { result: true } })
1405
1681
  .catch(() => null);
1406
- return finish(codingChildResult(child, stored?.result));
1682
+ return finish(codingChildResult(child, stored?.result, await loadBudgetEvent(db, childRunId)));
1407
1683
  }
1408
1684
  if (now() - startedAt >= boundMs) {
1409
1685
  await executor.stop(childRunId, "sub-agent wait timed out").catch((err) => {
@@ -1505,8 +1781,17 @@ export async function createSandboxSession(options) {
1505
1781
  sessionId: session.id,
1506
1782
  capability,
1507
1783
  input: { ...input, gateway: { url: options.gatewayUrl, capability } },
1784
+ secretValues: () => userToolSecretValues(tools, Object.keys(input.userTools)),
1508
1785
  };
1509
1786
  }
1787
+ /**
1788
+ * Every secret value the sandbox run's user tools `names` may read (readable or brokered): what its
1789
+ * worker can be handed, so what the gateway checks its cause codes against and a launcher masks
1790
+ * from the worker's output tail. Rejects when any read fails (readRedactionValues).
1791
+ */
1792
+ export async function userToolSecretValues(tools, names) {
1793
+ return (await Promise.all(names.map((name) => tools.readableSecretValues(name)))).flat();
1794
+ }
1510
1795
  /** Run.executionBackend for a sandbox-mode run whose worker is (or was) launched. */
1511
1796
  export const NATIVE_SANDBOX_BACKEND = "native-sandbox";
1512
1797
  /** The stored form of a sandbox capability: never the token itself. */
@@ -1539,13 +1824,18 @@ async function executeTrackedRun(runId, providers, db, onText, step) {
1539
1824
  ? (providers.repoAccess ?? createRepoAccessGate({ db, hosts: reviewHosts }))
1540
1825
  : undefined;
1541
1826
  const issueTrackers = configuredIssueTrackers(providers.issueTrackers);
1827
+ // Inline execution checks for a stop before every step (stopAwareStep); a durable executor passes
1828
+ // its own step runner, which does its own check (DbosExecutor wraps it in stopAwareStep too).
1829
+ if (step === runStepInline)
1830
+ step = stopAwareStep(db, runId);
1542
1831
  // Fails a run that ends before its engine starts, closing what its trigger opened (the review
1543
1832
  // check, the host and issue status comments) exactly as the normal and catch paths below do.
1544
1833
  // Deliberately no self-defect: every caller is a configuration state, not this run's defect (an
1545
1834
  // admin disabled or removed the model, or this deployment has no coding or native sandbox
1546
1835
  // executor), and filing would open one defect per affected agent rather than describe a failure
1547
1836
  // of that agent.
1548
- const finishEarly = async (error) => {
1837
+ const finishEarly = async (reason) => {
1838
+ const { error } = await maskRunTexts({ runId, db, providers }, { error: reason });
1549
1839
  const finished = await finishRun(db, runId, { status: "failed", error, finishedAt: new Date() });
1550
1840
  await closeOpenHostCheck(db, finished, reviewHosts);
1551
1841
  await completeHostStatus(db, finished, reviewHosts);
@@ -1601,6 +1891,7 @@ async function executeTrackedRun(runId, providers, db, onText, step) {
1601
1891
  ctx: { providers: { llm }, onText, onProgress },
1602
1892
  builtinHandler: tools.builtinHandler,
1603
1893
  privilegedHostFor: tools.privilegedHostFor,
1894
+ readableSecretValues: tools.readableSecretValues,
1604
1895
  drivability: () => runDrivability(db, runId),
1605
1896
  launcher: providers.nativeSandbox,
1606
1897
  });
@@ -1634,6 +1925,10 @@ async function executeTrackedRun(runId, providers, db, onText, step) {
1634
1925
  logRunOwnershipLost(runId, err);
1635
1926
  throw err;
1636
1927
  }
1928
+ // A replay that no longer matches its record (a run recorded by a release whose steps differ)
1929
+ // cannot continue: it ends lost, not failed, through the same settle path.
1930
+ if (err instanceof StepReplayMismatchError)
1931
+ return loseNativeRun(finishContext, STEPS_CHANGED_REASON);
1637
1932
  return failNativeRun(finishContext, err);
1638
1933
  }
1639
1934
  }