@wardby/cli 0.5.3 → 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 (763) hide show
  1. package/.env.example +33 -0
  2. package/README.md +25 -14
  3. package/deploy/local/docker-compose.quickstart-coding.yml +13 -0
  4. package/dist/claude-coding-worker/context.d.ts +22 -0
  5. package/dist/claude-coding-worker/context.js +92 -0
  6. package/dist/claude-coding-worker/driver.d.ts +27 -3
  7. package/dist/claude-coding-worker/driver.js +161 -20
  8. package/dist/claude-coding-worker/main.js +34 -6
  9. package/dist/claude-coding-worker/sdk.js +25 -5
  10. package/dist/claude-coding-worker/tool-socket.d.ts +27 -0
  11. package/dist/claude-coding-worker/tool-socket.js +111 -0
  12. package/dist/claude-coding-worker/transcript.d.ts +18 -0
  13. package/dist/claude-coding-worker/transcript.js +77 -0
  14. package/dist/cli-help.d.ts +1 -1
  15. package/dist/cli-help.js +6 -2
  16. package/dist/cli-native-mode.d.ts +9 -0
  17. package/dist/cli-native-mode.js +20 -0
  18. package/dist/cli.d.ts +3 -1
  19. package/dist/cli.js +161 -11
  20. package/dist/coding/claude-context.d.ts +45 -0
  21. package/dist/coding/claude-context.js +434 -0
  22. package/dist/coding/continuation-wording.d.ts +35 -0
  23. package/dist/coding/continuation-wording.js +73 -0
  24. package/dist/coding/linked-issue-note.d.ts +23 -0
  25. package/dist/coding/linked-issue-note.js +47 -0
  26. package/dist/coding/merge-wording.d.ts +28 -0
  27. package/dist/coding/merge-wording.js +78 -0
  28. package/dist/coding/pr-description-note.d.ts +9 -0
  29. package/dist/coding/pr-description-note.js +32 -0
  30. package/dist/coding/profile.d.ts +50 -118
  31. package/dist/coding/profile.js +51 -4
  32. package/dist/coding/protected-path-wording.d.ts +8 -0
  33. package/dist/coding/protected-path-wording.js +1 -1
  34. package/dist/coding/protocol.d.ts +292 -270
  35. package/dist/coding/protocol.js +322 -15
  36. package/dist/coding/redact.d.ts +3 -0
  37. package/dist/coding/redact.js +12 -0
  38. package/dist/coding/registry/allowlist.d.ts +12 -0
  39. package/dist/coding/registry/allowlist.js +14 -1
  40. package/dist/coding/registry/audit-mode.d.ts +153 -0
  41. package/dist/coding/registry/audit-mode.js +304 -0
  42. package/dist/coding/registry/lockfile-shared.d.ts +36 -0
  43. package/dist/coding/registry/lockfile-shared.js +128 -0
  44. package/dist/coding/registry/lockfiles.js +1 -1
  45. package/dist/coding/registry/npm-lockfile-formats.d.ts +11 -0
  46. package/dist/coding/registry/npm-lockfile-formats.js +41 -0
  47. package/dist/coding/registry/npm-lockfile.d.ts +8 -4
  48. package/dist/coding/registry/npm-lockfile.js +4 -6
  49. package/dist/coding/registry/npm.js +78 -8
  50. package/dist/coding/registry/pnpm-lockfile.d.ts +17 -0
  51. package/dist/coding/registry/pnpm-lockfile.js +189 -0
  52. package/dist/coding/registry/pypi-plan.d.ts +50 -0
  53. package/dist/coding/registry/pypi-plan.js +406 -0
  54. package/dist/coding/registry/pypi-requirements.d.ts +58 -0
  55. package/dist/coding/registry/pypi-requirements.js +155 -0
  56. package/dist/coding/registry/pypi.d.ts +3 -27
  57. package/dist/coding/registry/pypi.js +40 -154
  58. package/dist/coding/registry/refusal-hints.d.ts +20 -0
  59. package/dist/coding/registry/refusal-hints.js +61 -0
  60. package/dist/coding/registry/refusal-wording.d.ts +11 -0
  61. package/dist/coding/registry/refusal-wording.js +56 -0
  62. package/dist/coding/registry/report.d.ts +70 -8
  63. package/dist/coding/registry/report.js +112 -5
  64. package/dist/coding/registry/types.d.ts +58 -8
  65. package/dist/coding/registry/yarn-lockfile.d.ts +30 -0
  66. package/dist/coding/registry/yarn-lockfile.js +367 -0
  67. package/dist/coding/services/catalog.d.ts +37 -351
  68. package/dist/coding/transcript/claude-code.d.ts +13 -0
  69. package/dist/coding/transcript/claude-code.js +273 -0
  70. package/dist/coding/transcript/download-protocol.d.ts +30 -0
  71. package/dist/coding/transcript/download-protocol.js +18 -0
  72. package/dist/coding/transcript/format.d.ts +20 -0
  73. package/dist/coding/transcript/format.js +32 -0
  74. package/dist/coding/transcript/recorder.d.ts +51 -0
  75. package/dist/coding/transcript/recorder.js +225 -0
  76. package/dist/coding/transcript/resume/checkout.d.ts +31 -0
  77. package/dist/coding/transcript/resume/checkout.js +203 -0
  78. package/dist/coding/transcript/resume/command.d.ts +17 -0
  79. package/dist/coding/transcript/resume/command.js +176 -0
  80. package/dist/coding/transcript/resume/download.d.ts +20 -0
  81. package/dist/coding/transcript/resume/download.js +153 -0
  82. package/dist/coding/transcript/resume/launch.d.ts +38 -0
  83. package/dist/coding/transcript/resume/launch.js +128 -0
  84. package/dist/coding/transcript/resume/session-file.d.ts +13 -0
  85. package/dist/coding/transcript/resume/session-file.js +79 -0
  86. package/dist/coding/transcript/resume/stand-in.d.ts +11 -0
  87. package/dist/coding/transcript/resume/stand-in.js +49 -0
  88. package/dist/coding-worker/artifact.d.ts +16 -3
  89. package/dist/coding-worker/artifact.js +5 -3
  90. package/dist/coding-worker/debug-trace.d.ts +6 -5
  91. package/dist/coding-worker/debug-trace.js +13 -18
  92. package/dist/coding-worker/dependency-cache.d.mts +229 -0
  93. package/dist/coding-worker/dependency-cache.mjs +2123 -0
  94. package/dist/coding-worker/driver.d.ts +4 -3
  95. package/dist/coding-worker/driver.js +118 -38
  96. package/dist/coding-worker/errors.d.ts +1 -0
  97. package/dist/coding-worker/errors.js +32 -0
  98. package/dist/coding-worker/sdk.d.ts +6 -0
  99. package/dist/coding-worker/sdk.js +1 -0
  100. package/dist/coding-worker/skills.d.ts +23 -0
  101. package/dist/coding-worker/skills.js +117 -0
  102. package/dist/coding-worker/task-note.d.ts +11 -0
  103. package/dist/coding-worker/task-note.js +13 -0
  104. package/dist/coding-worker/types.d.ts +13 -0
  105. package/dist/config/dependency-cache.d.ts +23 -0
  106. package/dist/config/dependency-cache.js +32 -0
  107. package/dist/config/github-allowed-accounts.d.ts +15 -0
  108. package/dist/config/github-allowed-accounts.js +36 -0
  109. package/dist/config/low-power.d.ts +12 -0
  110. package/dist/config/low-power.js +18 -0
  111. package/dist/config/mobile.d.ts +12 -0
  112. package/dist/config/mobile.js +18 -0
  113. package/dist/config/providers.d.ts +65 -3
  114. package/dist/config/providers.js +147 -11
  115. package/dist/config/test-simulators.d.ts +82 -0
  116. package/dist/config/test-simulators.js +305 -0
  117. package/dist/core/async-context.d.ts +2 -0
  118. package/dist/core/async-context.js +12 -0
  119. package/dist/core/budget-events.d.ts +108 -0
  120. package/dist/core/budget-events.js +346 -0
  121. package/dist/core/budget-groups.d.ts +50 -1
  122. package/dist/core/budget-groups.js +236 -27
  123. package/dist/core/budget-stopped-head.d.ts +46 -0
  124. package/dist/core/budget-stopped-head.js +81 -0
  125. package/dist/core/budget-topup.d.ts +51 -0
  126. package/dist/core/budget-topup.js +156 -0
  127. package/dist/core/budget-wording.d.ts +16 -2
  128. package/dist/core/budget-wording.js +40 -2
  129. package/dist/core/coding-queue.d.ts +2 -1
  130. package/dist/core/coding-queue.js +7 -0
  131. package/dist/core/cron.d.ts +19 -1
  132. package/dist/core/cron.js +17 -0
  133. package/dist/core/datastores.js +1 -1
  134. package/dist/core/db.d.ts +16 -6
  135. package/dist/core/db.js +12 -10
  136. package/dist/core/deferred-coding.d.ts +14 -0
  137. package/dist/core/deferred-coding.js +62 -0
  138. package/dist/core/delegation-child.d.ts +25 -0
  139. package/dist/core/delegation-child.js +37 -0
  140. package/dist/core/dispatch.d.ts +53 -0
  141. package/dist/core/dispatch.js +167 -57
  142. package/dist/core/engine-native.js +10 -6
  143. package/dist/core/error-code.d.ts +38 -0
  144. package/dist/core/error-code.js +168 -0
  145. package/dist/core/grants.d.ts +19 -6
  146. package/dist/core/grants.js +55 -6
  147. package/dist/core/host-events.d.ts +67 -5
  148. package/dist/core/host-events.js +571 -37
  149. package/dist/core/host-status.d.ts +85 -3
  150. package/dist/core/host-status.js +298 -20
  151. package/dist/core/issue-bridge.d.ts +50 -6
  152. package/dist/core/issue-bridge.js +151 -20
  153. package/dist/core/issue-events.d.ts +1 -1
  154. package/dist/core/issue-events.js +16 -11
  155. package/dist/core/issue-status.d.ts +1 -1
  156. package/dist/core/issue-status.js +68 -29
  157. package/dist/core/logger.d.ts +5 -0
  158. package/dist/core/logger.js +14 -1
  159. package/dist/core/lost-run-children.d.ts +6 -0
  160. package/dist/core/lost-run-children.js +32 -0
  161. package/dist/core/mark-ready.d.ts +34 -0
  162. package/dist/core/mark-ready.js +152 -0
  163. package/dist/core/markdown-code.d.ts +8 -0
  164. package/dist/core/markdown-code.js +107 -0
  165. package/dist/core/memory-tools.js +7 -0
  166. package/dist/core/merge-order-check.d.ts +130 -0
  167. package/dist/core/merge-order-check.js +343 -0
  168. package/dist/core/notification-dispatcher.d.ts +70 -0
  169. package/dist/core/notification-dispatcher.js +368 -0
  170. package/dist/core/notification-templates.d.ts +19 -0
  171. package/dist/core/notification-templates.js +139 -0
  172. package/dist/core/notifications.d.ts +10 -0
  173. package/dist/core/notifications.js +38 -0
  174. package/dist/core/pg-auth.d.ts +22 -0
  175. package/dist/core/pg-auth.js +152 -0
  176. package/dist/core/power-composition.d.ts +16 -0
  177. package/dist/core/power-composition.js +37 -0
  178. package/dist/core/power-idle.d.ts +14 -0
  179. package/dist/core/power-idle.js +30 -0
  180. package/dist/core/power.d.ts +71 -0
  181. package/dist/core/power.js +202 -0
  182. package/dist/core/pull-request-state-sync.js +2 -2
  183. package/dist/core/reconciler.d.ts +8 -5
  184. package/dist/core/reconciler.js +37 -6
  185. package/dist/core/related-pull-requests.d.ts +59 -7
  186. package/dist/core/related-pull-requests.js +140 -25
  187. package/dist/core/reply-parts.d.ts +21 -0
  188. package/dist/core/reply-parts.js +73 -0
  189. package/dist/core/repo-access.d.ts +15 -0
  190. package/dist/core/repo-access.js +66 -0
  191. package/dist/core/review-fix.d.ts +38 -1
  192. package/dist/core/review-fix.js +209 -7
  193. package/dist/core/review-host-checks.d.ts +1 -1
  194. package/dist/core/review-host-tools.d.ts +23 -0
  195. package/dist/core/review-host-tools.js +156 -14
  196. package/dist/core/review-reads.d.ts +55 -0
  197. package/dist/core/review-reads.js +233 -0
  198. package/dist/core/run-status.d.ts +12 -0
  199. package/dist/core/run-status.js +12 -0
  200. package/dist/core/runner.d.ts +384 -5
  201. package/dist/core/runner.js +1244 -372
  202. package/dist/core/schedule-start.d.ts +12 -0
  203. package/dist/core/schedule-start.js +14 -0
  204. package/dist/core/secret-broker-config.d.ts +39 -0
  205. package/dist/core/secret-broker-config.js +112 -0
  206. package/dist/core/secret-guard.d.ts +46 -0
  207. package/dist/core/secret-guard.js +104 -0
  208. package/dist/core/secret-guarded-stores.d.ts +12 -0
  209. package/dist/core/secret-guarded-stores.js +41 -0
  210. package/dist/core/secret-spans.d.ts +50 -0
  211. package/dist/core/secret-spans.js +94 -0
  212. package/dist/core/secrets.d.ts +39 -4
  213. package/dist/core/secrets.js +148 -40
  214. package/dist/core/stop-run.d.ts +46 -0
  215. package/dist/core/stop-run.js +61 -0
  216. package/dist/core/workflow-event-recorder.d.ts +14 -0
  217. package/dist/core/workflow-event-recorder.js +120 -0
  218. package/dist/core/workflow-events.d.ts +94 -0
  219. package/dist/core/workflow-events.js +48 -0
  220. package/dist/core/workflow-run-events.d.ts +18 -0
  221. package/dist/core/workflow-run-events.js +75 -0
  222. package/dist/core/zod-json-schema.d.ts +4 -0
  223. package/dist/core/zod-json-schema.js +39 -0
  224. package/dist/core/zod3-window.d.ts +35 -0
  225. package/dist/core/zod3-window.js +124 -0
  226. package/dist/generated/prisma/browser.d.ts +92 -3
  227. package/dist/generated/prisma/client.d.ts +92 -3
  228. package/dist/generated/prisma/commonInputTypes.d.ts +364 -54
  229. package/dist/generated/prisma/enums.d.ts +37 -0
  230. package/dist/generated/prisma/enums.js +31 -0
  231. package/dist/generated/prisma/internal/class.d.ts +132 -0
  232. package/dist/generated/prisma/internal/class.js +4 -4
  233. package/dist/generated/prisma/internal/prismaNamespace.d.ts +1332 -142
  234. package/dist/generated/prisma/internal/prismaNamespace.js +227 -5
  235. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +234 -0
  236. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +227 -5
  237. package/dist/generated/prisma/models/Agent.d.ts +464 -7
  238. package/dist/generated/prisma/models/AgentRepository.d.ts +42 -1
  239. package/dist/generated/prisma/models/CodingAgentProfile.d.ts +301 -1
  240. package/dist/generated/prisma/models/CodingProxyRequest.d.ts +207 -3
  241. package/dist/generated/prisma/models/CodingProxySession.d.ts +114 -1
  242. package/dist/generated/prisma/models/CodingRun.d.ts +1841 -1
  243. package/dist/generated/prisma/models/DeferredReview.d.ts +78 -1
  244. package/dist/generated/prisma/models/DependencySnapshot.d.ts +1668 -0
  245. package/dist/generated/prisma/models/DependencySnapshot.js +1 -0
  246. package/dist/generated/prisma/models/IssuePullRequest.d.ts +6 -2
  247. package/dist/generated/prisma/models/LocalReview.d.ts +40 -1
  248. package/dist/generated/prisma/models/NativeGatewayCall.d.ts +1713 -0
  249. package/dist/generated/prisma/models/NativeGatewayCall.js +1 -0
  250. package/dist/generated/prisma/models/NativeGatewaySession.d.ts +1551 -0
  251. package/dist/generated/prisma/models/NativeGatewaySession.js +1 -0
  252. package/dist/generated/prisma/models/NativeWarmWorker.d.ts +1296 -0
  253. package/dist/generated/prisma/models/NativeWarmWorker.js +1 -0
  254. package/dist/generated/prisma/models/NotificationChannel.d.ts +1543 -0
  255. package/dist/generated/prisma/models/NotificationChannel.js +1 -0
  256. package/dist/generated/prisma/models/NotificationDelivery.d.ts +1502 -0
  257. package/dist/generated/prisma/models/NotificationDelivery.js +1 -0
  258. package/dist/generated/prisma/models/NotificationThread.d.ts +1090 -0
  259. package/dist/generated/prisma/models/NotificationThread.js +1 -0
  260. package/dist/generated/prisma/models/RegistryAllowance.d.ts +41 -1
  261. package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +75 -1
  262. package/dist/generated/prisma/models/RegistryAuditRecord.d.ts +1364 -0
  263. package/dist/generated/prisma/models/RegistryAuditRecord.js +1 -0
  264. package/dist/generated/prisma/models/Run.d.ts +1619 -58
  265. package/dist/generated/prisma/models/RunBudgetEvent.d.ts +1211 -0
  266. package/dist/generated/prisma/models/RunBudgetEvent.js +1 -0
  267. package/dist/generated/prisma/models/RunHostCheck.d.ts +36 -1
  268. package/dist/generated/prisma/models/RunTranscript.d.ts +1339 -0
  269. package/dist/generated/prisma/models/RunTranscript.js +1 -0
  270. package/dist/generated/prisma/models/Secret.d.ts +37 -1
  271. package/dist/generated/prisma/models/SecretBrokerChange.d.ts +1101 -0
  272. package/dist/generated/prisma/models/SecretBrokerChange.js +1 -0
  273. package/dist/generated/prisma/models/WorkflowEvent.d.ts +1422 -0
  274. package/dist/generated/prisma/models/WorkflowEvent.js +1 -0
  275. package/dist/generated/prisma/models.d.ts +12 -0
  276. package/dist/help/build.js +4 -2
  277. package/dist/help/catalog.d.ts +1 -1
  278. package/dist/help/catalog.js +31 -6
  279. package/dist/help-index.json +2584 -146
  280. package/dist/import/create.js +49 -3
  281. package/dist/import/neutral-schema.d.ts +50 -277
  282. package/dist/import/neutral-schema.js +7 -4
  283. package/dist/knowledge/check.d.ts +1 -1
  284. package/dist/knowledge/cli.d.ts +11 -1
  285. package/dist/knowledge/cli.js +37 -4
  286. package/dist/knowledge/concept.d.ts +3 -19
  287. package/dist/knowledge/location.d.ts +31 -0
  288. package/dist/knowledge/location.js +78 -0
  289. package/dist/mcp/auth/access.d.ts +11 -2
  290. package/dist/mcp/auth/access.js +15 -1
  291. package/dist/mcp/auth/ownership.d.ts +7 -7
  292. package/dist/mcp/auth/repo-authorization.js +14 -0
  293. package/dist/mcp/auth/resource-server.d.ts +2 -1
  294. package/dist/mcp/auth/resource-server.js +7 -2
  295. package/dist/mcp/auth/self-hosted/browser.d.ts +1 -1
  296. package/dist/mcp/auth/self-hosted/browser.js +19 -6
  297. package/dist/mcp/auth/self-hosted/credentials.d.ts +3 -3
  298. package/dist/mcp/auth/self-hosted/session.d.ts +2 -2
  299. package/dist/mcp/context.d.ts +6 -0
  300. package/dist/mcp/host-events/github-ingress.d.ts +25 -1
  301. package/dist/mcp/host-events/github-ingress.js +70 -1
  302. package/dist/mcp/index.d.ts +19 -0
  303. package/dist/mcp/index.js +117 -20
  304. package/dist/mcp/server.d.ts +13 -0
  305. package/dist/mcp/server.js +33 -14
  306. package/dist/mcp/tools/agents.d.ts +4 -0
  307. package/dist/mcp/tools/agents.js +277 -24
  308. package/dist/mcp/tools/datastore.js +17 -8
  309. package/dist/mcp/tools/grants.js +9 -3
  310. package/dist/mcp/tools/memory.js +11 -1
  311. package/dist/mcp/tools/notification-channels.d.ts +2 -0
  312. package/dist/mcp/tools/notification-channels.js +293 -0
  313. package/dist/mcp/tools/packages.d.ts +2 -0
  314. package/dist/mcp/tools/packages.js +195 -0
  315. package/dist/mcp/tools/power.d.ts +2 -0
  316. package/dist/mcp/tools/power.js +77 -0
  317. package/dist/mcp/tools/repositories.js +18 -5
  318. package/dist/mcp/tools/run-access.d.ts +16 -0
  319. package/dist/mcp/tools/run-access.js +25 -0
  320. package/dist/mcp/tools/run-transcript-download.d.ts +30 -0
  321. package/dist/mcp/tools/run-transcript-download.js +129 -0
  322. package/dist/mcp/tools/run-transcript.d.ts +19 -0
  323. package/dist/mcp/tools/run-transcript.js +70 -0
  324. package/dist/mcp/tools/runs.d.ts +0 -6
  325. package/dist/mcp/tools/runs.js +41 -14
  326. package/dist/mcp/tools/scheduling.js +3 -1
  327. package/dist/mcp/tools/secret-broker-compat.d.ts +12 -0
  328. package/dist/mcp/tools/secret-broker-compat.js +23 -0
  329. package/dist/mcp/tools/secret-elicitation-form.d.ts +22 -2
  330. package/dist/mcp/tools/secret-elicitation-form.js +205 -10
  331. package/dist/mcp/tools/secret-elicitation-server.d.ts +6 -0
  332. package/dist/mcp/tools/secret-elicitation-server.js +10 -54
  333. package/dist/mcp/tools/secret-elicitation.d.ts +38 -3
  334. package/dist/mcp/tools/secret-elicitation.js +110 -12
  335. package/dist/mcp/tools/secrets.js +186 -9
  336. package/dist/mcp/tools/stop-run.d.ts +2 -0
  337. package/dist/mcp/tools/stop-run.js +82 -0
  338. package/dist/mcp/tools/subagents.js +2 -2
  339. package/dist/mcp/tools/tools.d.ts +2 -2
  340. package/dist/mcp/tools/tools.js +47 -11
  341. package/dist/mcp/transport/stdio-link-host.d.ts +20 -0
  342. package/dist/mcp/transport/stdio-link-host.js +67 -0
  343. package/dist/mcp/transport/streamable-http.d.ts +13 -0
  344. package/dist/mcp/transport/streamable-http.js +41 -1
  345. package/dist/native-worker/composition.d.ts +23 -0
  346. package/dist/native-worker/composition.js +77 -0
  347. package/dist/native-worker/docker-isolation.d.ts +65 -0
  348. package/dist/native-worker/docker-isolation.js +183 -0
  349. package/dist/native-worker/docker-launcher.d.ts +110 -0
  350. package/dist/native-worker/docker-launcher.js +297 -0
  351. package/dist/native-worker/gateway-server.d.ts +77 -0
  352. package/dist/native-worker/gateway-server.js +412 -0
  353. package/dist/native-worker/gateway.d.ts +70 -0
  354. package/dist/native-worker/gateway.js +263 -0
  355. package/dist/native-worker/http-server.d.ts +24 -0
  356. package/dist/native-worker/http-server.js +113 -0
  357. package/dist/native-worker/http-transport.d.ts +17 -0
  358. package/dist/native-worker/http-transport.js +88 -0
  359. package/dist/native-worker/kubernetes-isolation.d.ts +90 -0
  360. package/dist/native-worker/kubernetes-isolation.js +218 -0
  361. package/dist/native-worker/kubernetes-launcher.d.ts +106 -0
  362. package/dist/native-worker/kubernetes-launcher.js +375 -0
  363. package/dist/native-worker/launch.d.ts +62 -0
  364. package/dist/native-worker/launch.js +49 -0
  365. package/dist/native-worker/ledger.d.ts +125 -0
  366. package/dist/native-worker/ledger.js +245 -0
  367. package/dist/native-worker/loopback.d.ts +12 -0
  368. package/dist/native-worker/loopback.js +44 -0
  369. package/dist/native-worker/main.d.ts +8 -0
  370. package/dist/native-worker/main.js +85 -0
  371. package/dist/native-worker/protocol.d.ts +296 -0
  372. package/dist/native-worker/protocol.js +246 -0
  373. package/dist/native-worker/sandbox-executor.d.ts +83 -0
  374. package/dist/native-worker/sandbox-executor.js +284 -0
  375. package/dist/native-worker/stderr-redaction.d.ts +77 -0
  376. package/dist/native-worker/stderr-redaction.js +237 -0
  377. package/dist/native-worker/stdio.d.ts +29 -0
  378. package/dist/native-worker/stdio.js +189 -0
  379. package/dist/native-worker/warm-delivery.d.ts +16 -0
  380. package/dist/native-worker/warm-delivery.js +33 -0
  381. package/dist/native-worker/warm-pool-ledger.d.ts +39 -0
  382. package/dist/native-worker/warm-pool-ledger.js +73 -0
  383. package/dist/native-worker/warm-pool.d.ts +107 -0
  384. package/dist/native-worker/warm-pool.js +283 -0
  385. package/dist/native-worker/worker.d.ts +17 -0
  386. package/dist/native-worker/worker.js +141 -0
  387. package/dist/observability/metrics.js +6 -1
  388. package/dist/providers/auth/authorization-server.d.ts +3 -0
  389. package/dist/providers/auth/index.js +2 -0
  390. package/dist/providers/auth/self-hosted.d.ts +14 -2
  391. package/dist/providers/auth/self-hosted.js +54 -4
  392. package/dist/providers/chat/fake.d.ts +46 -0
  393. package/dist/providers/chat/fake.js +39 -0
  394. package/dist/providers/chat/index.d.ts +4 -0
  395. package/dist/providers/chat/index.js +8 -0
  396. package/dist/providers/chat/slack.d.ts +37 -0
  397. package/dist/providers/chat/slack.js +105 -0
  398. package/dist/providers/chat/types.d.ts +42 -0
  399. package/dist/providers/chat/types.js +19 -0
  400. package/dist/providers/coding-proxy/dependency-cache/eviction.d.ts +29 -0
  401. package/dist/providers/coding-proxy/dependency-cache/eviction.js +87 -0
  402. package/dist/providers/coding-proxy/dependency-cache/key.d.ts +33 -0
  403. package/dist/providers/coding-proxy/dependency-cache/key.js +86 -0
  404. package/dist/providers/coding-proxy/dependency-cache/model-request-seal.d.ts +18 -0
  405. package/dist/providers/coding-proxy/dependency-cache/model-request-seal.js +40 -0
  406. package/dist/providers/coding-proxy/dependency-cache/prisma-store.d.ts +25 -0
  407. package/dist/providers/coding-proxy/dependency-cache/prisma-store.js +131 -0
  408. package/dist/providers/coding-proxy/dependency-cache/service.d.ts +70 -0
  409. package/dist/providers/coding-proxy/dependency-cache/service.js +409 -0
  410. package/dist/providers/coding-proxy/dependency-cache/store.d.ts +85 -0
  411. package/dist/providers/coding-proxy/dependency-cache/store.js +95 -0
  412. package/dist/providers/coding-proxy/hold.d.ts +32 -0
  413. package/dist/providers/coding-proxy/hold.js +86 -0
  414. package/dist/providers/coding-proxy/interrupted.d.ts +70 -0
  415. package/dist/providers/coding-proxy/interrupted.js +104 -0
  416. package/dist/providers/coding-proxy/memory-ledger.d.ts +15 -2
  417. package/dist/providers/coding-proxy/memory-ledger.js +84 -8
  418. package/dist/providers/coding-proxy/metering.d.ts +16 -0
  419. package/dist/providers/coding-proxy/metering.js +27 -4
  420. package/dist/providers/coding-proxy/prisma-ledger.d.ts +15 -2
  421. package/dist/providers/coding-proxy/prisma-ledger.js +179 -67
  422. package/dist/providers/coding-proxy/proxy.d.ts +14 -1
  423. package/dist/providers/coding-proxy/proxy.js +166 -14
  424. package/dist/providers/coding-proxy/registry/audit.d.ts +53 -10
  425. package/dist/providers/coding-proxy/registry/audit.js +94 -17
  426. package/dist/providers/coding-proxy/registry/lockfile-parse-worker.d.ts +36 -0
  427. package/dist/providers/coding-proxy/registry/lockfile-parse-worker.js +67 -0
  428. package/dist/providers/coding-proxy/registry/lockfile-parse.d.ts +16 -0
  429. package/dist/providers/coding-proxy/registry/lockfile-parse.js +110 -0
  430. package/dist/providers/coding-proxy/registry/plan.d.ts +9 -0
  431. package/dist/providers/coding-proxy/registry/plan.js +201 -48
  432. package/dist/providers/coding-proxy/registry/prisma-store.d.ts +11 -5
  433. package/dist/providers/coding-proxy/registry/prisma-store.js +120 -17
  434. package/dist/providers/coding-proxy/registry/service.d.ts +97 -16
  435. package/dist/providers/coding-proxy/registry/service.js +579 -112
  436. package/dist/providers/coding-proxy/registry/store.d.ts +84 -9
  437. package/dist/providers/coding-proxy/registry/store.js +54 -1
  438. package/dist/providers/coding-proxy/runtime.d.ts +4 -0
  439. package/dist/providers/coding-proxy/runtime.js +78 -16
  440. package/dist/providers/coding-proxy/server.d.ts +6 -1
  441. package/dist/providers/coding-proxy/server.js +45 -1
  442. package/dist/providers/coding-proxy/types.d.ts +65 -1
  443. package/dist/providers/datastore/types.d.ts +5 -0
  444. package/dist/providers/engine/types.d.ts +36 -2
  445. package/dist/providers/engine/types.js +39 -0
  446. package/dist/providers/executor/composition.d.ts +31 -1
  447. package/dist/providers/executor/composition.js +81 -8
  448. package/dist/providers/executor/container.d.ts +385 -9
  449. package/dist/providers/executor/container.js +1234 -99
  450. package/dist/providers/executor/dbos-status.d.ts +31 -0
  451. package/dist/providers/executor/dbos-status.js +64 -0
  452. package/dist/providers/executor/dbos.d.ts +91 -0
  453. package/dist/providers/executor/dbos.js +319 -26
  454. package/dist/providers/executor/deferred.d.ts +44 -0
  455. package/dist/providers/executor/deferred.js +100 -0
  456. package/dist/providers/executor/in-process.d.ts +9 -1
  457. package/dist/providers/executor/in-process.js +14 -4
  458. package/dist/providers/executor/linked-issue-reader.d.ts +16 -0
  459. package/dist/providers/executor/linked-issue-reader.js +30 -0
  460. package/dist/providers/executor/routing.d.ts +39 -10
  461. package/dist/providers/executor/routing.js +77 -32
  462. package/dist/providers/executor/types.d.ts +25 -0
  463. package/dist/providers/index.d.ts +2 -1
  464. package/dist/providers/issue-tracker/types.d.ts +17 -0
  465. package/dist/providers/issue-tracker/types.js +39 -1
  466. package/dist/providers/jobs/claude-tool-setup.d.ts +4 -0
  467. package/dist/providers/jobs/claude-tool-setup.js +5 -2
  468. package/dist/providers/jobs/docker-isolation.d.ts +8 -0
  469. package/dist/providers/jobs/docker-isolation.js +17 -0
  470. package/dist/providers/jobs/docker.d.ts +1 -0
  471. package/dist/providers/jobs/docker.js +29 -4
  472. package/dist/providers/jobs/fake-kubernetes-api.d.ts +23 -2
  473. package/dist/providers/jobs/fake-kubernetes-api.js +45 -1
  474. package/dist/providers/jobs/kubernetes-api.d.ts +25 -1
  475. package/dist/providers/jobs/kubernetes-api.js +17 -0
  476. package/dist/providers/jobs/kubernetes-client.d.ts +10 -1
  477. package/dist/providers/jobs/kubernetes-client.js +61 -3
  478. package/dist/providers/jobs/kubernetes-dry-run-fixture.d.ts +6 -1
  479. package/dist/providers/jobs/kubernetes-dry-run-fixture.js +6 -3
  480. package/dist/providers/jobs/kubernetes-isolation.d.ts +5 -1
  481. package/dist/providers/jobs/kubernetes-isolation.js +23 -1
  482. package/dist/providers/jobs/kubernetes-platform.d.ts +4 -1
  483. package/dist/providers/jobs/kubernetes-platform.js +38 -1
  484. package/dist/providers/jobs/kubernetes-preflight.d.ts +2 -1
  485. package/dist/providers/jobs/kubernetes-preflight.js +4 -2
  486. package/dist/providers/jobs/kubernetes.d.ts +42 -2
  487. package/dist/providers/jobs/kubernetes.js +178 -19
  488. package/dist/providers/jobs/metadata-probes.d.ts +11 -0
  489. package/dist/providers/jobs/metadata-probes.js +13 -0
  490. package/dist/providers/jobs/types.d.ts +42 -0
  491. package/dist/providers/jobs/types.js +13 -1
  492. package/dist/providers/llm/anthropic.js +5 -1
  493. package/dist/providers/llm/bedrock.d.ts +0 -12
  494. package/dist/providers/llm/bedrock.js +4 -0
  495. package/dist/providers/llm/claude-messages.d.ts +3 -1
  496. package/dist/providers/llm/claude-messages.js +4 -1
  497. package/dist/providers/llm/claude-provider.d.ts +2 -2
  498. package/dist/providers/llm/claude-provider.js +2 -2
  499. package/dist/providers/llm/openai.d.ts +2 -2
  500. package/dist/providers/llm/openai.js +6 -2
  501. package/dist/providers/llm/routing.d.ts +2 -2
  502. package/dist/providers/llm/routing.js +2 -2
  503. package/dist/providers/llm/types.d.ts +12 -2
  504. package/dist/providers/review-host/github-events.d.ts +9 -1
  505. package/dist/providers/review-host/github-events.js +34 -12
  506. package/dist/providers/review-host/github.d.ts +39 -2
  507. package/dist/providers/review-host/github.js +274 -29
  508. package/dist/providers/review-host/local.d.ts +8 -4
  509. package/dist/providers/review-host/local.js +49 -31
  510. package/dist/providers/review-host/patch-pages.d.ts +41 -0
  511. package/dist/providers/review-host/patch-pages.js +176 -0
  512. package/dist/providers/review-host/review-format.d.ts +19 -1
  513. package/dist/providers/review-host/review-format.js +38 -7
  514. package/dist/providers/review-host/types.d.ts +157 -2
  515. package/dist/providers/review-host/types.js +8 -0
  516. package/dist/providers/storage/gcs.d.ts +63 -0
  517. package/dist/providers/storage/gcs.js +69 -0
  518. package/dist/providers/storage/index.d.ts +15 -0
  519. package/dist/providers/storage/index.js +14 -0
  520. package/dist/providers/storage/local.d.ts +24 -0
  521. package/dist/providers/storage/local.js +92 -0
  522. package/dist/providers/storage/meter.d.ts +14 -0
  523. package/dist/providers/storage/meter.js +34 -0
  524. package/dist/providers/storage/s3.d.ts +41 -0
  525. package/dist/providers/storage/s3.js +107 -0
  526. package/dist/providers/storage/types.d.ts +37 -3
  527. package/dist/providers/storage/types.js +21 -4
  528. package/dist/providers/vcs/git.d.ts +33 -2
  529. package/dist/providers/vcs/git.js +324 -22
  530. package/dist/providers/vcs/github-remote.d.ts +26 -3
  531. package/dist/providers/vcs/github-remote.js +124 -14
  532. package/dist/providers/vcs/github.d.ts +246 -12
  533. package/dist/providers/vcs/github.js +521 -61
  534. package/dist/providers/vcs/index.js +8 -1
  535. package/dist/providers/vcs/pr-description.d.ts +127 -0
  536. package/dist/providers/vcs/pr-description.js +270 -0
  537. package/dist/providers/vcs/remote.d.ts +33 -1
  538. package/dist/providers/vcs/routing.d.ts +4 -2
  539. package/dist/providers/vcs/routing.js +6 -0
  540. package/dist/providers/vcs/types.d.ts +80 -3
  541. package/dist/quickstart/coding-seed.d.ts +21 -3
  542. package/dist/quickstart/coding-seed.js +283 -3
  543. package/dist/quickstart/images.d.ts +2 -0
  544. package/dist/quickstart/images.js +7 -1
  545. package/dist/quickstart/reviewer-prompt.d.ts +1 -1
  546. package/dist/quickstart/reviewer-prompt.js +6 -6
  547. package/dist/quickstart-images.json +1 -1
  548. package/dist/sandbox/brokered-fetch.d.ts +23 -0
  549. package/dist/sandbox/brokered-fetch.js +37 -0
  550. package/dist/sandbox/generated/zod.bundle.js +62 -1
  551. package/dist/sandbox/generated/zod3.bundle.js +5 -0
  552. package/dist/sandbox/host-functions.d.ts +35 -0
  553. package/dist/sandbox/host-functions.js +150 -91
  554. package/dist/sandbox/prelude.js +7 -1
  555. package/dist/sandbox/run-in-sandbox.d.ts +19 -0
  556. package/dist/sandbox/run-in-sandbox.js +28 -10
  557. package/dist/sandbox/safe-fetch.d.ts +2 -0
  558. package/dist/sandbox/safe-fetch.js +3 -1
  559. package/dist/sandbox/secret-broker-sigv4.d.ts +2 -0
  560. package/dist/sandbox/secret-broker-sigv4.js +49 -0
  561. package/dist/sandbox/secret-broker.d.ts +34 -0
  562. package/dist/sandbox/secret-broker.js +161 -0
  563. package/dist/sandbox/tool-capabilities.d.ts +10 -30
  564. package/dist/sandbox/user-tool.d.ts +14 -0
  565. package/dist/sandbox/user-tool.js +32 -0
  566. package/dist/sandbox/zod-params.d.ts +37 -5
  567. package/dist/sandbox/zod-params.js +181 -26
  568. package/dist/serve.js +25 -0
  569. package/dist/viewer/api-schema.d.ts +372 -2454
  570. package/dist/viewer/api-schema.js +29 -0
  571. package/dist/viewer/build-schemas.js +10 -3
  572. package/dist/viewer/event-bus.d.ts +6 -0
  573. package/dist/viewer/event-bus.js +16 -7
  574. package/dist/viewer/graph.d.ts +5 -2
  575. package/dist/viewer/graph.js +7 -2
  576. package/dist/viewer/http.d.ts +0 -1
  577. package/dist/viewer/http.js +2 -2
  578. package/dist/viewer/infra.js +25 -2
  579. package/dist/viewer/limits.d.ts +4 -0
  580. package/dist/viewer/limits.js +4 -0
  581. package/dist/viewer/openapi.d.ts +15 -0
  582. package/dist/viewer/openapi.js +170 -0
  583. package/dist/wardby-bin.js +10 -0
  584. package/docs/README.md +7 -0
  585. package/docs/agent-recipes.md +114 -27
  586. package/docs/architecture-runtime.md +16 -1
  587. package/docs/code-review-agents.md +689 -45
  588. package/docs/coding-agent-setup.md +602 -12
  589. package/docs/coding-packages.md +1037 -92
  590. package/docs/coding-services.md +30 -13
  591. package/docs/coding-worker-byo-images.md +218 -6
  592. package/docs/coding-worker-isolation.md +173 -44
  593. package/docs/getting-started-gke.md +332 -129
  594. package/docs/getting-started-identity-provider.md +31 -11
  595. package/docs/getting-started.md +14 -4
  596. package/docs/jira-agents.md +22 -9
  597. package/docs/knowledge/blob-not-found-semantics.md +41 -0
  598. package/docs/knowledge/bounded-file-descriptor.md +52 -0
  599. package/docs/knowledge/delegation-admission-slots.md +52 -0
  600. package/docs/knowledge/http-dispatcher-bootstrap.md +61 -0
  601. package/docs/knowledge/import-rename-target.md +52 -0
  602. package/docs/knowledge/index.md +57 -0
  603. package/docs/knowledge/log.md +9 -0
  604. package/docs/knowledge/mcp-cancelled-task-precedence.md +41 -0
  605. package/docs/knowledge/mcp-http-prompt-discovery.md +41 -0
  606. package/docs/knowledge/mcp-schema-module-boundary.md +41 -0
  607. package/docs/knowledge/nullable-tool-uniqueness.md +52 -0
  608. package/docs/knowledge/overview-cli.md +74 -0
  609. package/docs/knowledge/overview-coding-proxy.md +72 -0
  610. package/docs/knowledge/overview-coding.md +72 -0
  611. package/docs/knowledge/overview-config.md +60 -0
  612. package/docs/knowledge/overview-core.md +72 -0
  613. package/docs/knowledge/overview-deploy.md +83 -0
  614. package/docs/knowledge/overview-mcp.md +61 -0
  615. package/docs/knowledge/overview-prisma.md +72 -0
  616. package/docs/knowledge/overview-providers.md +89 -0
  617. package/docs/knowledge/overview-sandbox.md +83 -0
  618. package/docs/knowledge/overview-surfaces.md +83 -0
  619. package/docs/knowledge/private-datastore-shared-boundary.md +54 -0
  620. package/docs/knowledge/proxy-settlement-projections.md +41 -0
  621. package/docs/knowledge/run-pricing-first-writer.md +43 -0
  622. package/docs/knowledge/schedule-claim-transaction.md +41 -0
  623. package/docs/knowledge/secret-entry-replica-contract.md +52 -0
  624. package/docs/knowledge/terminal-run-first-writer.md +41 -0
  625. package/docs/knowledge/transactional-agent-authorization.md +52 -0
  626. package/docs/knowledge/viewer-notify-write-isolation.md +30 -0
  627. package/docs/knowledge/warm-input-publication.md +30 -0
  628. package/docs/knowledge/workspace-staging-failure.md +54 -0
  629. package/docs/knowledge.md +54 -14
  630. package/docs/native-sandbox.md +731 -0
  631. package/docs/observability.md +4 -0
  632. package/docs/release-verification.md +9 -0
  633. package/docs/security-deployment.md +146 -14
  634. package/docs/slack-notifications.md +268 -0
  635. package/docs/tool-secrets.md +284 -0
  636. package/docs/viewer-api.md +46 -5
  637. package/docs/viewer-api.openapi.yml +1404 -0
  638. package/help/admin-viewer.md +29 -3
  639. package/help/agent-recipes.md +27 -0
  640. package/help/architecture-agent.md +11 -6
  641. package/help/brokered-secrets.md +79 -0
  642. package/help/build-claude-code-image.md +131 -0
  643. package/help/build-worker-image.md +6 -7
  644. package/help/builder-agent.md +19 -6
  645. package/help/code-review-agents.md +243 -10
  646. package/help/coding-packages.md +156 -6
  647. package/help/cost-attribution.md +9 -5
  648. package/help/creating-agents.md +14 -1
  649. package/help/dependency-cache.md +143 -0
  650. package/help/deploy-gke.md +23 -9
  651. package/help/deployment-targets.md +25 -14
  652. package/help/errors/budget-exhausted.md +187 -0
  653. package/help/errors/budget-group-exhausted.md +5 -1
  654. package/help/errors/coding-provider-not-configured.md +2 -2
  655. package/help/errors/coding-stream-quota-exceeded.md +33 -0
  656. package/help/errors/coding-workspace-disk-exceeds-limit.md +39 -0
  657. package/help/errors/continuation-closed.md +9 -12
  658. package/help/errors/database-auth-invalid.md +28 -0
  659. package/help/errors/delegation-conflict.md +24 -0
  660. package/help/errors/file-list-truncated.md +45 -0
  661. package/help/errors/full-read-required.md +49 -0
  662. package/help/errors/github-account-not-allowed.md +29 -0
  663. package/help/errors/graph-incomplete.md +43 -0
  664. package/help/errors/graph-limit.md +43 -0
  665. package/help/errors/low-power-disabled.md +29 -0
  666. package/help/errors/low-power-scale-forbidden.md +35 -0
  667. package/help/errors/low-power-wake-timeout.md +30 -0
  668. package/help/errors/merge-conflict.md +49 -0
  669. package/help/errors/mobile-disabled.md +31 -0
  670. package/help/errors/native-sandbox-capacity.md +22 -0
  671. package/help/errors/native-sandbox-deadline-exceeded.md +19 -0
  672. package/help/errors/native-sandbox-docker-failed.md +20 -0
  673. package/help/errors/native-sandbox-gateway-error.md +20 -0
  674. package/help/errors/native-sandbox-gateway-unavailable.md +21 -0
  675. package/help/errors/native-sandbox-image-not-pinned.md +19 -0
  676. package/help/errors/native-sandbox-isolation-mismatch.md +20 -0
  677. package/help/errors/native-sandbox-launch-abandoned.md +21 -0
  678. package/help/errors/native-sandbox-network-unenforced.md +22 -0
  679. package/help/errors/native-sandbox-requires-catalog.md +31 -0
  680. package/help/errors/native-sandbox-unavailable.md +19 -0
  681. package/help/errors/native-sandbox-warm-delivery-failed.md +21 -0
  682. package/help/errors/native-sandbox-worker-exited.md +21 -0
  683. package/help/errors/native-sandbox-worker-lost.md +20 -0
  684. package/help/errors/native-sandbox-worker-unready.md +22 -0
  685. package/help/errors/package-not-allowed-pending.md +52 -0
  686. package/help/errors/package-not-allowed.md +51 -0
  687. package/help/errors/package-refusals.md +43 -0
  688. package/help/errors/patch-cursor-malformed.md +31 -0
  689. package/help/errors/run-lost.md +39 -0
  690. package/help/errors/run-transcript-download.md +44 -0
  691. package/help/errors/secret-broker-body-invalid.md +26 -0
  692. package/help/errors/secret-broker-changed.md +20 -0
  693. package/help/errors/secret-broker-config-invalid.md +30 -0
  694. package/help/errors/secret-broker-conflict.md +27 -0
  695. package/help/errors/secret-broker-destination-denied.md +28 -0
  696. package/help/errors/secret-broker-value-invalid.md +28 -0
  697. package/help/errors/secret-brokered.md +24 -0
  698. package/help/errors/secret-in-value.md +69 -0
  699. package/help/errors/secret-not-brokered.md +25 -0
  700. package/help/errors/slack-auth-failed.md +48 -0
  701. package/help/errors/slack-channel-unreachable.md +39 -0
  702. package/help/errors/slack-not-configured.md +33 -0
  703. package/help/errors/target-merge.md +40 -0
  704. package/help/fix-merge-conflicts.md +73 -0
  705. package/help/getting-started.md +4 -5
  706. package/help/github-app-setup.md +132 -0
  707. package/help/github.md +40 -1
  708. package/help/identity-and-access.md +7 -1
  709. package/help/jira.md +23 -6
  710. package/help/knowledge.md +16 -7
  711. package/help/local-repositories.md +3 -4
  712. package/help/low-power-mode.md +131 -0
  713. package/help/merge-order-check.md +122 -0
  714. package/help/mobile-app.md +54 -0
  715. package/help/native-capabilities.md +29 -1
  716. package/help/native-sandbox.md +150 -0
  717. package/help/observability.md +8 -0
  718. package/help/operating-agents.md +5 -0
  719. package/help/package-audit-mode.md +91 -0
  720. package/help/related-pull-requests.md +80 -16
  721. package/help/repo-instructions-and-skills.md +91 -0
  722. package/help/resume-runs-locally.md +131 -0
  723. package/help/review-fix-rounds.md +43 -1
  724. package/help/sharing-agents.md +59 -0
  725. package/help/slack-notifications.md +76 -0
  726. package/help/status-comments.md +31 -0
  727. package/help/stop-run.md +58 -0
  728. package/help/troubleshooting/budgets.md +28 -0
  729. package/help/troubleshooting/coding-workers.md +99 -7
  730. package/package.json +27 -13
  731. package/prisma/migrations/20261009000003_native_execution_mode/migration.sql +12 -0
  732. package/prisma/migrations/20261009000004_native_gateway_ledger/migration.sql +65 -0
  733. package/prisma/migrations/20261009000005_native_gateway_call_child_agent/migration.sql +5 -0
  734. package/prisma/migrations/20261009000006_run_deferred_start_stop/migration.sql +14 -0
  735. package/prisma/migrations/20261009000007_native_gateway_network_ready/migration.sql +5 -0
  736. package/prisma/migrations/20261010000001_workflow_notifications/migration.sql +99 -0
  737. package/prisma/migrations/20261011000001_native_gateway_session_status_index/migration.sql +2 -0
  738. package/prisma/migrations/20261012000001_native_warm_worker/migration.sql +32 -0
  739. package/prisma/migrations/20261013000001_coding_run_merge_order/migration.sql +2 -0
  740. package/prisma/migrations/20261013000001_secret_broker/migration.sql +17 -0
  741. package/prisma/migrations/20261013000002_deferred_review_request/migration.sql +4 -0
  742. package/prisma/migrations/20261014000000_repo_skills/migration.sql +15 -0
  743. package/prisma/migrations/20261015000001_coding_run_merge_order_index/migration.sql +2 -0
  744. package/prisma/migrations/20261015000002_no_change_rereview/migration.sql +2 -0
  745. package/prisma/migrations/20261016000000_registry_approved_accepted_advisories/migration.sql +3 -0
  746. package/prisma/migrations/20261016000001_mark_ready_when_passing/migration.sql +3 -0
  747. package/prisma/migrations/20261017000000_debug_trace_max_mb/migration.sql +4 -0
  748. package/prisma/migrations/20261018000000_dependency_cache/migration.sql +53 -0
  749. package/prisma/migrations/20261019000000_claude_max_output_tokens/migration.sql +5 -0
  750. package/prisma/migrations/20261021000000_pr_description_hash/migration.sql +4 -0
  751. package/prisma/migrations/20261022000000_dependency_snapshot_validation_metadata/migration.sql +4 -0
  752. package/prisma/migrations/20261023000000_package_audit_mode/migration.sql +27 -0
  753. package/prisma/migrations/20261024000000_budget_events/migration.sql +26 -0
  754. package/prisma/migrations/20261025000000_cache_aware_holds/migration.sql +9 -0
  755. package/prisma/migrations/20261026000000_budget_overrun/migration.sql +4 -0
  756. package/prisma/migrations/20261027000000_coding_run_request/migration.sql +1 -0
  757. package/prisma/migrations/20261028000000_native_gateway_review_reads/migration.sql +6 -0
  758. package/prisma/migrations/20261029000000_lead_survives_deploy/migration.sql +6 -0
  759. package/prisma/migrations/20261030000000_local_review_covered_sha/migration.sql +2 -0
  760. package/prisma/migrations/20261031000000_coding_run_budget_limit/migration.sql +9 -0
  761. package/prisma/migrations/20261101000000_run_transcripts/migration.sql +22 -0
  762. package/prisma/schema.prisma +631 -146
  763. package/dist/sandbox/generated/zod-to-json-schema.bundle.js +0 -5
@@ -0,0 +1,131 @@
1
+ import { Prisma } from "#prisma";
2
+ import { POLICY_ANY, } from "./store.js";
3
+ /** How long one eviction pass may hold the lock (its transaction) before Postgres releases it. */
4
+ const EVICTION_LOCK_MS = 15 * 60_000;
5
+ /**
6
+ * Only the scope's own fields (callers may pass a wider object, such as a
7
+ * derived key), plus the fixed POLICY_ANY every row is written and matched with.
8
+ */
9
+ const scopeOf = ({ ecosystem, repository, projectPath, workerImage }) => ({
10
+ ecosystem,
11
+ repository,
12
+ projectPath,
13
+ workerImage,
14
+ policyHash: POLICY_ANY,
15
+ });
16
+ const toSnapshot = ({ policyHash: _policyHash, ...row }) => ({
17
+ ...row,
18
+ sizeBytes: Number(row.sizeBytes),
19
+ unpackedBytes: row.unpackedBytes === null ? null : Number(row.unpackedBytes),
20
+ });
21
+ export class PrismaDependencyCacheStore {
22
+ db;
23
+ constructor(db) {
24
+ this.db = db;
25
+ }
26
+ async session(runId) {
27
+ const session = await this.db.codingProxySession.findUnique({
28
+ where: { runId },
29
+ select: { dependencyCacheLockHashes: true, dependencyCacheSealedAt: true, createdAt: true },
30
+ });
31
+ return session
32
+ ? {
33
+ lockHashes: session.dependencyCacheLockHashes,
34
+ sealedAt: session.dependencyCacheSealedAt,
35
+ createdAt: session.createdAt,
36
+ }
37
+ : null;
38
+ }
39
+ async seal(runId, now) {
40
+ await this.db.codingProxySession.updateMany({
41
+ where: { runId, dependencyCacheSealedAt: null },
42
+ data: { dependencyCacheSealedAt: now },
43
+ });
44
+ }
45
+ async sealForModelRequest(runId, now) {
46
+ await this.db.codingProxySession.updateMany({
47
+ where: { runId, dependencyCacheSealedAt: null, run: { dependencyCache: true } },
48
+ data: { dependencyCacheSealedAt: now },
49
+ });
50
+ }
51
+ async findByIdentity(identity) {
52
+ const row = await this.db.dependencySnapshot.findUnique({
53
+ where: {
54
+ ecosystem_repository_projectPath_lockHash_workerImage_policyHash: {
55
+ ...scopeOf(identity),
56
+ lockHash: identity.lockHash,
57
+ },
58
+ },
59
+ });
60
+ return row ? toSnapshot(row) : null;
61
+ }
62
+ async findNearest(scope, excludeLockHash) {
63
+ const row = await this.db.dependencySnapshot.findFirst({
64
+ where: { ...scopeOf(scope), lockHash: { not: excludeLockHash } },
65
+ orderBy: [{ createdAt: "desc" }, { id: "desc" }],
66
+ });
67
+ return row ? toSnapshot(row) : null;
68
+ }
69
+ async hasUploaded(runId, ecosystem, projectPath) {
70
+ const row = await this.db.dependencySnapshot.findFirst({
71
+ where: { createdByRunId: runId, projectPath, ecosystem },
72
+ select: { id: true },
73
+ });
74
+ return row !== null;
75
+ }
76
+ async insert(snapshot) {
77
+ try {
78
+ await this.db.dependencySnapshot.create({
79
+ data: {
80
+ ...scopeOf(snapshot),
81
+ lockHash: snapshot.lockHash,
82
+ blobKey: snapshot.blobKey,
83
+ sha256: snapshot.sha256,
84
+ sizeBytes: BigInt(snapshot.sizeBytes),
85
+ archiveScanVersion: snapshot.archiveScanVersion,
86
+ archiveEntries: snapshot.archiveEntries,
87
+ unpackedBytes: snapshot.unpackedBytes === null ? null : BigInt(snapshot.unpackedBytes),
88
+ createdByRunId: snapshot.createdByRunId,
89
+ },
90
+ });
91
+ return "inserted";
92
+ }
93
+ catch (error) {
94
+ if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002")
95
+ return "exists";
96
+ throw error;
97
+ }
98
+ }
99
+ async touch(id, now) {
100
+ await this.db.dependencySnapshot.updateMany({ where: { id }, data: { lastUsedAt: now } });
101
+ }
102
+ async delete(id) {
103
+ await this.db.dependencySnapshot.deleteMany({ where: { id } });
104
+ }
105
+ async deleteIfBlob(id, blobKey) {
106
+ const { count } = await this.db.dependencySnapshot.deleteMany({ where: { id, blobKey } });
107
+ return count > 0;
108
+ }
109
+ async listForEviction() {
110
+ const rows = await this.db.dependencySnapshot.findMany({
111
+ select: { id: true, repository: true, blobKey: true, sizeBytes: true, createdAt: true, lastUsedAt: true },
112
+ });
113
+ return rows.map((row) => ({ ...row, sizeBytes: Number(row.sizeBytes) }));
114
+ }
115
+ /**
116
+ * A transaction-scoped advisory lock: Postgres releases it when the
117
+ * transaction ends, so a crashed proxy never leaves it held. The work runs
118
+ * on the ordinary client (each delete commits at once); the transaction
119
+ * only holds the lock.
120
+ */
121
+ async withEvictionLock(work) {
122
+ return this.db.$transaction(async (tx) => {
123
+ const [row] = await tx.$queryRaw `
124
+ SELECT pg_try_advisory_xact_lock(hashtext('wardby.dependency-cache-eviction')) AS locked`;
125
+ if (!row?.locked)
126
+ return false;
127
+ await work();
128
+ return true;
129
+ }, { maxWait: 10_000, timeout: EVICTION_LOCK_MS });
130
+ }
131
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * The coding proxy's dependency cache: `/cache/npm/*`, authenticated by the
3
+ * run's registry-only token.
4
+ *
5
+ * Trust rules (the whole point of this file):
6
+ * - Every key is derived here, from the run's records and a lockfile hash the
7
+ * proxy computed while approving a plan for this session. A worker names
8
+ * only its project folder, which is validated, and which lockfile (one of
9
+ * the session's approved hashes) it is asking about.
10
+ * - Only trusted wardby code runs before the run's seal; the agent runs after
11
+ * it with the same token. The worker seals before it starts the agent, and
12
+ * the proxy seals on the run's first model request whatever the worker did
13
+ * (ModelRequestSeal). After the seal every route except the (idempotent)
14
+ * seal answers 409 `sealed`, and an upload still streaming when the seal
15
+ * lands is discarded.
16
+ * - A run uploads at most once per project, only within its upload window,
17
+ * and only for its derived key. Each upload writes its own blob object
18
+ * (a unique key), so no upload can replace a committed snapshot's bytes;
19
+ * an upload that does not end in its own row deletes its own object.
20
+ * - The sha256 is computed while the upload streams and stored in Postgres;
21
+ * downloads carry it so the worker verifies before extracting.
22
+ */
23
+ import type { IncomingMessage, ServerResponse } from "node:http";
24
+ import { type BlobStore } from "../../storage/types.js";
25
+ import type { RegistryStore } from "../registry/store.js";
26
+ import type { DependencyCacheStore } from "./store.js";
27
+ /** Uploads streaming at once across every run. */
28
+ export declare const DEFAULT_MAX_CONCURRENT_UPLOADS = 4;
29
+ export interface DependencyCacheServiceOptions {
30
+ registry: Pick<RegistryStore, "findRunByRegistryTokenHash">;
31
+ store: DependencyCacheStore;
32
+ blobs: BlobStore;
33
+ maxSnapshotBytes: number;
34
+ uploadMs: number;
35
+ now?: () => Date;
36
+ maxConcurrentUploads?: number;
37
+ }
38
+ export declare class DependencyCacheService {
39
+ private readonly options;
40
+ readonly uploadMs: number;
41
+ private readonly now;
42
+ /** `runId\0projectPath` and key ids of uploads streaming right now (per replica). */
43
+ private readonly uploading;
44
+ private activeUploads;
45
+ constructor(options: DependencyCacheServiceOptions);
46
+ /**
47
+ * Answers one `/cache/...` request. `token` is the registry token the
48
+ * request carried. `releaseBodyTimer` lifts the server's per-request body
49
+ * timer; it is called only for an authenticated, eligible upload that holds
50
+ * its upload slot (the upload window bounds it from then on).
51
+ */
52
+ handle(request: IncomingMessage, response: ServerResponse, token: string, releaseBodyTimer?: () => void): Promise<void>;
53
+ private route;
54
+ /** When the run's upload window closes (epoch ms): the one deadline lookups report and uploads enforce. */
55
+ private uploadDeadline;
56
+ private lookup;
57
+ /**
58
+ * Serves the run's derived key (by its keyId) or the snapshot a lookup
59
+ * offers as nearest for the same project and lockfile, recomputed now with
60
+ * the same query: nothing is remembered between requests, and no other id
61
+ * is ever served.
62
+ */
63
+ private download;
64
+ /** Removes a snapshot whose blob failed its row's checksum, so the key can be rebuilt. */
65
+ private dropCorrupt;
66
+ private upload;
67
+ private storeUpload;
68
+ /** Removes an upload's own blob object (unique to the upload, so no row refers to it). */
69
+ private discard;
70
+ }
@@ -0,0 +1,409 @@
1
+ import { createHash } from "node:crypto";
2
+ import { pipeline, Transform } from "node:stream";
3
+ import { logger } from "../../../core/logger.js";
4
+ import { BlobStoreError } from "../../storage/types.js";
5
+ import { capabilityHash } from "../proxy.js";
6
+ import { DEPENDENCY_CACHE_ECOSYSTEM, deriveSnapshotKey, isLockHash, newBlobKey, parseProjectPath, } from "./key.js";
7
+ const cacheLog = logger.child({ module: "dependency-cache" });
8
+ const ARCHIVE_SCAN_VERSION = 1;
9
+ const MAX_ARCHIVE_ENTRIES = 1_000_000;
10
+ /** Uploads streaming at once across every run. */
11
+ export const DEFAULT_MAX_CONCURRENT_UPLOADS = 4;
12
+ const SNAPSHOT_ID = /^[a-z0-9]{1,64}$/;
13
+ /** Matches a `/cache/...` path; a number is the refusal's status code. */
14
+ function matchRoute(method, pathname) {
15
+ const prefix = `/cache/${DEPENDENCY_CACHE_ECOSYSTEM}/`;
16
+ if (!pathname.startsWith(prefix))
17
+ return 404;
18
+ const rest = pathname.slice(prefix.length);
19
+ const expect = (wanted, route, id) => (method === wanted ? { route, id } : 405);
20
+ if (rest === "lookup")
21
+ return expect("GET", "lookup");
22
+ if (rest === "seal")
23
+ return expect("POST", "seal");
24
+ if (rest === "blob")
25
+ return expect("PUT", "upload");
26
+ if (rest.startsWith("blob/")) {
27
+ const id = rest.slice("blob/".length);
28
+ return SNAPSHOT_ID.test(id) ? expect("GET", "download", id) : 404;
29
+ }
30
+ return 404;
31
+ }
32
+ class CacheRefusal extends Error {
33
+ status;
34
+ code;
35
+ constructor(status, code) {
36
+ super(code);
37
+ this.status = status;
38
+ this.code = code;
39
+ }
40
+ }
41
+ function send(response, status, body, close = false) {
42
+ if (response.headersSent || response.destroyed)
43
+ return;
44
+ response.writeHead(status, {
45
+ "content-type": "application/json",
46
+ "cache-control": "no-store",
47
+ ...(close ? { connection: "close" } : {}),
48
+ });
49
+ response.end(JSON.stringify(body));
50
+ }
51
+ /**
52
+ * The archive scan the worker ran before uploading, recorded with the
53
+ * snapshot. Protocol: an upload carries `x-wardby-archive-scan-version`,
54
+ * `x-wardby-archive-entries` and `x-wardby-archive-unpacked-bytes`, all three
55
+ * or none (a partial or malformed set is refused). Only an upload accepted
56
+ * before the run's seal stores them, so they come from trusted pre-agent
57
+ * worker code, never from the agent. A lookup returns them as `manifest` (and
58
+ * `nearest.manifest`) with the sha256 this proxy computed and recorded for the
59
+ * row, so a worker trusts them only for bytes that verify against that digest.
60
+ * Optional so workers deployed before validation metadata remain compatible.
61
+ */
62
+ function verifiedArchiveHeaders(headers) {
63
+ const version = headers["x-wardby-archive-scan-version"];
64
+ const entries = headers["x-wardby-archive-entries"];
65
+ const unpackedBytes = headers["x-wardby-archive-unpacked-bytes"];
66
+ if (version === undefined && entries === undefined && unpackedBytes === undefined)
67
+ return null;
68
+ if (typeof version !== "string" || typeof entries !== "string" || typeof unpackedBytes !== "string")
69
+ throw new CacheRefusal(400, "bad_request");
70
+ const parsedVersion = Number(version);
71
+ const parsedEntries = Number(entries);
72
+ const parsedUnpackedBytes = Number(unpackedBytes);
73
+ if (parsedVersion !== ARCHIVE_SCAN_VERSION ||
74
+ !Number.isSafeInteger(parsedEntries) ||
75
+ parsedEntries < 1 ||
76
+ parsedEntries > MAX_ARCHIVE_ENTRIES ||
77
+ !Number.isSafeInteger(parsedUnpackedBytes) ||
78
+ parsedUnpackedBytes < 0)
79
+ throw new CacheRefusal(400, "bad_request");
80
+ return { archiveScanVersion: parsedVersion, archiveEntries: parsedEntries, unpackedBytes: parsedUnpackedBytes };
81
+ }
82
+ function lookupManifest(row) {
83
+ const { archiveScanVersion, archiveEntries, unpackedBytes } = row;
84
+ if (archiveScanVersion !== ARCHIVE_SCAN_VERSION ||
85
+ archiveEntries === null ||
86
+ unpackedBytes === null ||
87
+ !Number.isSafeInteger(archiveEntries) ||
88
+ archiveEntries < 1 ||
89
+ archiveEntries > MAX_ARCHIVE_ENTRIES ||
90
+ !Number.isSafeInteger(unpackedBytes) ||
91
+ unpackedBytes < 0)
92
+ return undefined;
93
+ return { scanVersion: ARCHIVE_SCAN_VERSION, sha256: row.sha256, entries: archiveEntries, unpackedBytes };
94
+ }
95
+ export class DependencyCacheService {
96
+ options;
97
+ uploadMs;
98
+ now;
99
+ /** `runId\0projectPath` and key ids of uploads streaming right now (per replica). */
100
+ uploading = new Set();
101
+ activeUploads = 0;
102
+ constructor(options) {
103
+ this.options = options;
104
+ this.uploadMs = options.uploadMs;
105
+ this.now = options.now ?? (() => new Date());
106
+ }
107
+ /**
108
+ * Answers one `/cache/...` request. `token` is the registry token the
109
+ * request carried. `releaseBodyTimer` lifts the server's per-request body
110
+ * timer; it is called only for an authenticated, eligible upload that holds
111
+ * its upload slot (the upload window bounds it from then on).
112
+ */
113
+ async handle(request, response, token, releaseBodyTimer = () => { }) {
114
+ const url = new URL(request.url ?? "/", "http://proxy");
115
+ const matched = matchRoute(request.method, url.pathname);
116
+ const isUpload = typeof matched === "object" && matched.route === "upload";
117
+ // Filled in once the token is known to belong to a run, for the refusal log.
118
+ const known = {};
119
+ try {
120
+ if (matched === 404)
121
+ throw new CacheRefusal(404, "not_found");
122
+ if (matched === 405)
123
+ throw new CacheRefusal(405, "method_not_allowed");
124
+ await this.route(matched.route, matched.id, url.searchParams, token, request, response, releaseBodyTimer, known);
125
+ }
126
+ catch (error) {
127
+ if (error instanceof CacheRefusal) {
128
+ // One line per refusal: a fixed route name and code, and the run when the token named one
129
+ // (never the token, the query or the URL).
130
+ cacheLog.info({
131
+ event: "dependency_cache.refused",
132
+ ...(known.runId ? { runId: known.runId } : {}),
133
+ route: typeof matched === "object" ? matched.route : "unknown",
134
+ status: error.status,
135
+ code: error.code,
136
+ }, "dependency cache request refused");
137
+ // An upload refused before its body was read: close rather than read it.
138
+ send(response, error.status, { error: error.code }, isUpload);
139
+ return;
140
+ }
141
+ cacheLog.error({ event: "dependency_cache.failed", err: error }, "dependency cache request failed");
142
+ if (!response.headersSent)
143
+ send(response, 500, { error: "internal_error" }, isUpload);
144
+ else if (!response.destroyed)
145
+ response.destroy();
146
+ }
147
+ }
148
+ async route(route, id, params, token, request, response, releaseBodyTimer, known) {
149
+ const context = await this.options.registry.findRunByRegistryTokenHash(capabilityHash(token), this.now());
150
+ if (!context)
151
+ throw new CacheRefusal(401, "invalid_capability");
152
+ known.runId = context.runId;
153
+ // The feature is off for this run: indistinguishable from a proxy without it.
154
+ if (!context.dependencyCache || !context.cacheImage)
155
+ throw new CacheRefusal(404, "not_found");
156
+ const session = await this.options.store.session(context.runId);
157
+ if (!session)
158
+ throw new CacheRefusal(401, "invalid_capability");
159
+ if (route === "seal") {
160
+ await this.options.store.seal(context.runId, this.now());
161
+ send(response, 200, { sealed: true });
162
+ return;
163
+ }
164
+ if (session.sealedAt)
165
+ throw new CacheRefusal(409, "sealed");
166
+ for (const name of new Set(params.keys()))
167
+ if (params.getAll(name).length > 1)
168
+ throw new CacheRefusal(400, "bad_request");
169
+ const projectPath = parseProjectPath(params.get("project"));
170
+ if (projectPath === null)
171
+ throw new CacheRefusal(400, "invalid_project");
172
+ const lockHash = params.get("lock") ?? "";
173
+ if (!isLockHash(lockHash) || !session.lockHashes.includes(lockHash))
174
+ throw new CacheRefusal(409, "plan_required");
175
+ const key = deriveSnapshotKey({
176
+ repository: context.repository,
177
+ projectPath,
178
+ lockHash,
179
+ workerImage: context.cacheImage,
180
+ });
181
+ if (route === "lookup")
182
+ await this.lookup(key, session.createdAt, response);
183
+ else if (route === "download")
184
+ await this.download(context.runId, key, id ?? "", response);
185
+ else
186
+ await this.upload(context, session.createdAt, key, request, response, releaseBodyTimer);
187
+ }
188
+ /** When the run's upload window closes (epoch ms): the one deadline lookups report and uploads enforce. */
189
+ uploadDeadline(sessionCreatedAt) {
190
+ return sessionCreatedAt.getTime() + this.uploadMs;
191
+ }
192
+ async lookup(key, sessionCreatedAt, response) {
193
+ const row = await this.options.store.findByIdentity(key);
194
+ const nearest = row ? null : await this.options.store.findNearest(key, key.lockHash);
195
+ const manifest = row ? lookupManifest(row) : undefined;
196
+ const nearestManifest = nearest ? lookupManifest(nearest) : undefined;
197
+ send(response, 200, {
198
+ key: key.keyId,
199
+ hit: row !== null,
200
+ ...(manifest ? { manifest } : {}),
201
+ ...(nearest ? { nearest: { id: nearest.id, ...(nearestManifest ? { manifest: nearestManifest } : {}) } } : {}),
202
+ maxBytes: this.options.maxSnapshotBytes,
203
+ uploadExpiresInMs: Math.max(0, Math.floor(this.uploadDeadline(sessionCreatedAt) - this.now().getTime())),
204
+ });
205
+ }
206
+ /**
207
+ * Serves the run's derived key (by its keyId) or the snapshot a lookup
208
+ * offers as nearest for the same project and lockfile, recomputed now with
209
+ * the same query: nothing is remembered between requests, and no other id
210
+ * is ever served.
211
+ */
212
+ async download(runId, key, id, response) {
213
+ let row = null;
214
+ if (id === key.keyId)
215
+ row = await this.options.store.findByIdentity(key);
216
+ else {
217
+ const nearest = await this.options.store.findNearest(key, key.lockHash);
218
+ if (nearest?.id === id)
219
+ row = nearest;
220
+ }
221
+ if (!row)
222
+ throw new CacheRefusal(404, "not_found");
223
+ let stream;
224
+ try {
225
+ stream = await this.options.blobs.getStream(row.blobKey);
226
+ }
227
+ catch (error) {
228
+ if (!(error instanceof BlobStoreError && error.code === "not_found"))
229
+ throw error;
230
+ // A row without its blob would answer hits forever: drop it.
231
+ await this.options.store.delete(row.id);
232
+ cacheLog.info({ event: "dependency_cache.blob_missing", runId, snapshotId: row.id }, "dependency cache snapshot had no blob and was removed");
233
+ throw new CacheRefusal(404, "not_found");
234
+ }
235
+ try {
236
+ await this.options.store.touch(row.id, this.now());
237
+ }
238
+ catch (error) {
239
+ cacheLog.warn({ event: "dependency_cache.touch_failed", err: error }, "dependency cache: lastUsedAt not updated");
240
+ }
241
+ response.writeHead(200, {
242
+ "content-type": "application/gzip",
243
+ "content-length": String(row.sizeBytes),
244
+ "x-wardby-sha256": row.sha256,
245
+ "cache-control": "no-store",
246
+ });
247
+ // Hash and count while streaming. The flush runs only once the blob was
248
+ // read to its end (a client abort destroys the pipeline before it), so only
249
+ // a complete read that disagrees with the row (or a blob longer than it)
250
+ // condemns the snapshot. The response then fails at once (the pipeline
251
+ // destroys it) instead of ending, or leaving the client waiting for the
252
+ // rest of its content-length.
253
+ const hash = createHash("sha256");
254
+ let bytes = 0;
255
+ const corrupt = () => {
256
+ void this.dropCorrupt(runId, row, bytes);
257
+ return new Error("snapshot_corrupt");
258
+ };
259
+ const verify = new Transform({
260
+ transform(chunk, _encoding, callback) {
261
+ bytes += chunk.length;
262
+ if (bytes > row.sizeBytes)
263
+ return callback(corrupt());
264
+ hash.update(chunk);
265
+ callback(null, chunk);
266
+ },
267
+ flush: (callback) => {
268
+ if (bytes === row.sizeBytes && hash.digest("hex") === row.sha256)
269
+ callback();
270
+ else
271
+ callback(corrupt());
272
+ },
273
+ });
274
+ pipeline(stream, verify, response, (error) => {
275
+ if (error && !response.destroyed)
276
+ response.destroy();
277
+ });
278
+ }
279
+ /** Removes a snapshot whose blob failed its row's checksum, so the key can be rebuilt. */
280
+ async dropCorrupt(runId, row, readBytes) {
281
+ try {
282
+ // Only while the row still names the blob that was streamed: a re-upload may have replaced it.
283
+ if (!(await this.options.store.deleteIfBlob(row.id, row.blobKey)))
284
+ return;
285
+ await this.discard(row.blobKey);
286
+ cacheLog.warn({
287
+ event: "snapshot_corrupt",
288
+ runId,
289
+ expectedBytes: row.sizeBytes,
290
+ readBytes,
291
+ }, "dependency cache snapshot failed its checksum and was removed");
292
+ }
293
+ catch (error) {
294
+ cacheLog.warn({ event: "dependency_cache.corrupt_cleanup_failed", err: error }, "dependency cache: cleanup failed");
295
+ }
296
+ }
297
+ async upload(context, sessionCreatedAt, key, request, response, releaseBodyTimer) {
298
+ const deadline = this.uploadDeadline(sessionCreatedAt);
299
+ if (this.now().getTime() >= deadline)
300
+ throw new CacheRefusal(409, "upload_expired");
301
+ const encoding = request.headers["content-encoding"];
302
+ if (encoding && encoding !== "identity")
303
+ throw new CacheRefusal(400, "bad_request");
304
+ const length = request.headers["content-length"];
305
+ if (length !== undefined && (!/^\d+$/.test(length) || Number(length) > this.options.maxSnapshotBytes))
306
+ throw new CacheRefusal(413, "too_large");
307
+ const verifiedArchive = verifiedArchiveHeaders(request.headers);
308
+ const projectSlot = `${context.runId}\0${key.projectPath}`;
309
+ if (this.uploading.has(projectSlot) || this.uploading.has(key.keyId))
310
+ throw new CacheRefusal(409, "upload_in_progress");
311
+ if (await this.options.store.hasUploaded(context.runId, key.ecosystem, key.projectPath))
312
+ throw new CacheRefusal(409, "already_uploaded");
313
+ if (await this.options.store.findByIdentity(key)) {
314
+ send(response, 200, { stored: false, reason: "exists" }, true);
315
+ return;
316
+ }
317
+ if (this.activeUploads >= (this.options.maxConcurrentUploads ?? DEFAULT_MAX_CONCURRENT_UPLOADS))
318
+ throw new CacheRefusal(503, "busy");
319
+ // Re-checked after the awaits above: the slot is taken synchronously.
320
+ if (this.uploading.has(projectSlot) || this.uploading.has(key.keyId))
321
+ throw new CacheRefusal(409, "upload_in_progress");
322
+ this.uploading.add(projectSlot);
323
+ this.uploading.add(key.keyId);
324
+ this.activeUploads += 1;
325
+ releaseBodyTimer();
326
+ try {
327
+ await this.storeUpload(context, deadline, key, request, response, verifiedArchive);
328
+ }
329
+ finally {
330
+ this.uploading.delete(projectSlot);
331
+ this.uploading.delete(key.keyId);
332
+ this.activeUploads -= 1;
333
+ }
334
+ }
335
+ async storeUpload(context, deadline, key, request, response, verifiedArchive) {
336
+ const blobKey = newBlobKey(key);
337
+ let expired = false;
338
+ const timer = setTimeout(() => {
339
+ expired = true;
340
+ request.destroy();
341
+ }, Math.max(0, deadline - this.now().getTime()));
342
+ let stored;
343
+ try {
344
+ stored = await this.options.blobs.putStream(blobKey, request, {
345
+ contentType: "application/gzip",
346
+ maxBytes: this.options.maxSnapshotBytes,
347
+ });
348
+ }
349
+ catch (error) {
350
+ if (error instanceof BlobStoreError && error.code === "blob_too_large")
351
+ throw new CacheRefusal(413, "too_large");
352
+ if (expired)
353
+ throw new CacheRefusal(409, "upload_expired");
354
+ // The client went away mid-upload: nothing to answer.
355
+ if (request.destroyed || response.destroyed)
356
+ return;
357
+ throw error;
358
+ }
359
+ finally {
360
+ clearTimeout(timer);
361
+ }
362
+ // Checked only now, after the last byte arrived: every byte of an
363
+ // accepted upload was sent before the seal (so before the agent ran).
364
+ const session = await this.options.store.session(context.runId);
365
+ const refusal = !session || session.sealedAt ? "sealed" : this.now().getTime() >= deadline ? "upload_expired" : null;
366
+ if (refusal) {
367
+ await this.discard(blobKey);
368
+ throw new CacheRefusal(409, refusal);
369
+ }
370
+ let outcome;
371
+ try {
372
+ outcome = await this.options.store.insert({
373
+ ecosystem: key.ecosystem,
374
+ repository: key.repository,
375
+ projectPath: key.projectPath,
376
+ lockHash: key.lockHash,
377
+ workerImage: key.workerImage,
378
+ blobKey,
379
+ sha256: stored.sha256,
380
+ sizeBytes: stored.sizeBytes,
381
+ archiveScanVersion: verifiedArchive?.archiveScanVersion ?? null,
382
+ archiveEntries: verifiedArchive?.archiveEntries ?? null,
383
+ unpackedBytes: verifiedArchive?.unpackedBytes ?? null,
384
+ createdByRunId: context.runId,
385
+ });
386
+ }
387
+ catch (error) {
388
+ await this.discard(blobKey);
389
+ throw error;
390
+ }
391
+ if (outcome === "exists") {
392
+ // Another upload of this key committed first; its object is untouched.
393
+ await this.discard(blobKey);
394
+ send(response, 200, { stored: false, reason: "exists" });
395
+ return;
396
+ }
397
+ cacheLog.info({ event: "dependency_cache.stored", runId: context.runId, sizeBytes: stored.sizeBytes }, "dependency cache snapshot stored");
398
+ send(response, 201, { stored: true, sha256: stored.sha256, sizeBytes: stored.sizeBytes });
399
+ }
400
+ /** Removes an upload's own blob object (unique to the upload, so no row refers to it). */
401
+ async discard(blobKey) {
402
+ try {
403
+ await this.options.blobs.delete(blobKey);
404
+ }
405
+ catch (error) {
406
+ cacheLog.warn({ event: "dependency_cache.discard_failed", err: error }, "dependency cache: blob not removed");
407
+ }
408
+ }
409
+ }