@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
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Runs inside a worker thread spawned by lockfile-parse.ts: parses one
3
+ * lockfile with the ecosystem's own parser, off the proxy's event loop, and
4
+ * posts back a plain, structured-cloneable form of the result (entries,
5
+ * projects, and the edge table `resolve` answers from). `parentPort` is
6
+ * null when this file is merely imported, so nothing runs then.
7
+ */
8
+ import { parentPort } from "node:worker_threads";
9
+ import { REGISTRY_ADAPTERS } from "../../../coding/registry/adapters.js";
10
+ import { RegistryError } from "../../../coding/registry/types.js";
11
+ /** At most this many edges per entry are shipped (far above any real
12
+ * package's dependency count), so a crafted entry with a huge dependency
13
+ * map can't make the main thread deserialize millions of them. */
14
+ const MAX_EDGES_PER_ENTRY = 2000;
15
+ export function parseAndShip(request) {
16
+ const support = REGISTRY_ADAPTERS.get(request.ecosystem)?.lockfilePlan;
17
+ if (!support)
18
+ throw new RegistryError(404, "wardby_plan_unsupported", `no lockfile plans for ${request.ecosystem}`);
19
+ const parsed = support.parse(request.body, {
20
+ maxEntries: request.maxEntries,
21
+ proxyRegistryUrl: request.proxyRegistryUrl,
22
+ });
23
+ if (!parsed.dependencyKeys)
24
+ throw new Error("this lockfile format cannot be parsed off the event loop");
25
+ const index = new Map(parsed.entries.map((entry, at) => [entry, at]));
26
+ const edgesOf = (path, what) => {
27
+ let keys = parsed.dependencyKeys(path);
28
+ // A project's edges only label audited packages top-level (best effort):
29
+ // past the bound they are cut, never a reason to refuse the plan.
30
+ if (what === "project")
31
+ keys = keys.slice(0, MAX_EDGES_PER_ENTRY);
32
+ if (keys.length > MAX_EDGES_PER_ENTRY)
33
+ throw new RegistryError(413, "wardby_lockfile_too_large", `lockfile ${what} "${path}" lists ${keys.length} dependencies; at most ${MAX_EDGES_PER_ENTRY} are read`);
34
+ const out = [];
35
+ for (const key of keys) {
36
+ const target = parsed.resolve(path, key);
37
+ const at = target && index.get(target);
38
+ if (at !== undefined && at !== null)
39
+ out.push([key, at]);
40
+ }
41
+ return out;
42
+ };
43
+ return {
44
+ entries: parsed.entries,
45
+ projects: parsed.projects,
46
+ ...(parsed.integrity ? { integrity: parsed.integrity } : {}),
47
+ edges: parsed.entries.map((entry) => edgesOf(entry.path, "entry")),
48
+ projectEdges: parsed.projects.map((project) => edgesOf(project.path, "project")),
49
+ };
50
+ }
51
+ /** The answer for one parse: the lockfile, the parser's refusal, or (a bug,
52
+ * not a verdict about the lockfile) the unexpected error's message, which
53
+ * the main thread logs. */
54
+ export function responseFor(parse) {
55
+ try {
56
+ return { ok: true, lockfile: parse() };
57
+ }
58
+ catch (error) {
59
+ if (error instanceof RegistryError)
60
+ return { ok: false, status: error.status, code: error.code, message: error.message };
61
+ return { ok: false, unexpected: true, message: error instanceof Error ? String(error) : "unknown error" };
62
+ }
63
+ }
64
+ if (parentPort) {
65
+ const port = parentPort;
66
+ port.on("message", (request) => port.postMessage(responseFor(() => parseAndShip(request))));
67
+ }
@@ -0,0 +1,16 @@
1
+ import { type ParsedLockfile } from "../../../coding/registry/types.js";
2
+ /** Time one off-thread lockfile parse may take. Real lockfiles at the
3
+ * entry bound parse in well under a second. */
4
+ export declare const DEFAULT_LOCKFILE_PARSE_TIMEOUT_MS = 15000;
5
+ export declare function parseWorkerHeapMb(maxEntries: number): number;
6
+ export declare function parseLockfileOffThread(ecosystem: string, body: string, options: {
7
+ maxEntries: number;
8
+ proxyRegistryUrl: string;
9
+ }, limits?: {
10
+ timeoutMs?: number;
11
+ signal?: AbortSignal;
12
+ /** Heap bound for the worker (default parseWorkerHeapMb(maxEntries)). */
13
+ maxOldGenerationSizeMb?: number;
14
+ /** Tests only: the worker script to run. */
15
+ workerUrl?: URL;
16
+ }): Promise<ParsedLockfile>;
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Parses a lockfile in a disposable worker thread with a hard time bound,
3
+ * for the formats that are costly to read (pnpm's and yarn's: YAML, or
4
+ * yarn classic's own format). A lockfile is attacker-controlled up to the
5
+ * 20 MiB plan cap, and a synchronous parse can't be interrupted, so on the
6
+ * event loop a crafted one could stall every request the proxy serves (model
7
+ * traffic included) for seconds. A worker's terminate() can stop it; the
8
+ * event loop only deserializes the bounded result (entries and their edges).
9
+ *
10
+ * One worker per parse, never reused (the same choice as the sandbox's
11
+ * parser pool): plans already run at most `planConcurrency` at once.
12
+ */
13
+ import { existsSync } from "node:fs";
14
+ import { fileURLToPath } from "node:url";
15
+ import { Worker } from "node:worker_threads";
16
+ import { RegistryError } from "../../../coding/registry/types.js";
17
+ /** Time one off-thread lockfile parse may take. Real lockfiles at the
18
+ * entry bound parse in well under a second. */
19
+ export const DEFAULT_LOCKFILE_PARSE_TIMEOUT_MS = 15_000;
20
+ /** Heap one parse worker may use before it is stopped, per 5000 entries
21
+ * the plan may have (REGISTRY_PLAN_MAX_ENTRIES, at least the default's
22
+ * share). A realistic 5000-entry pnpm-lock.yaml (3.4 MiB: peers, engines,
23
+ * six dependencies a snapshot) needs 129 to 160 MiB of it, so 384 MiB is
24
+ * about 2.5 times that; a crafted lockfile that expands further is
25
+ * stopped there and refused as too large. */
26
+ const PARSE_WORKER_HEAP_MB_PER_5000_ENTRIES = 384;
27
+ export function parseWorkerHeapMb(maxEntries) {
28
+ return Math.ceil((PARSE_WORKER_HEAP_MB_PER_5000_ENTRIES * Math.max(maxEntries, 5000)) / 5000);
29
+ }
30
+ const compiledWorker = new URL("./lockfile-parse-worker.js", import.meta.url);
31
+ // Under tsx/vitest only the .ts source exists, and a worker gets no TS
32
+ // loader by inheritance (see src/sandbox/parser-worker/pool.ts).
33
+ const WORKER_URL = existsSync(fileURLToPath(compiledWorker))
34
+ ? compiledWorker
35
+ : new URL("./lockfile-parse-worker.ts", import.meta.url);
36
+ function incomplete(message) {
37
+ return new RegistryError(503, "wardby_plan_incomplete", message);
38
+ }
39
+ /** The ParsedLockfile a worker shipped back, with `resolve` answering from
40
+ * its edge table. */
41
+ function rebuild(shipped) {
42
+ const byPath = new Map();
43
+ shipped.entries.forEach((entry, at) => {
44
+ byPath.set(entry.path, new Map(shipped.edges[at].map(([key, target]) => [key, shipped.entries[target]])));
45
+ });
46
+ // A project's own dependencies (what package audit mode labels top-level);
47
+ // a path an entry also uses keeps the entry's, as the parser's resolve does.
48
+ shipped.projects.forEach((project, at) => {
49
+ if (byPath.has(project.path))
50
+ return;
51
+ byPath.set(project.path, new Map(shipped.projectEdges[at].map(([key, target]) => [key, shipped.entries[target]])));
52
+ });
53
+ return {
54
+ entries: shipped.entries,
55
+ projects: shipped.projects,
56
+ ...(shipped.integrity ? { integrity: shipped.integrity } : {}),
57
+ resolve: (fromPath, key) => byPath.get(fromPath)?.get(key),
58
+ dependencyKeys: (path) => [...(byPath.get(path)?.keys() ?? [])],
59
+ };
60
+ }
61
+ export async function parseLockfileOffThread(ecosystem, body, options, limits = {}) {
62
+ const timeoutMs = limits.timeoutMs ?? DEFAULT_LOCKFILE_PARSE_TIMEOUT_MS;
63
+ if (limits.signal?.aborted)
64
+ throw incomplete("the client went away before the lockfile was read");
65
+ const workerUrl = limits.workerUrl ?? WORKER_URL;
66
+ const worker = new Worker(workerUrl, {
67
+ resourceLimits: { maxOldGenerationSizeMb: limits.maxOldGenerationSizeMb ?? parseWorkerHeapMb(options.maxEntries) },
68
+ execArgv: workerUrl.pathname.endsWith(".ts") ? ["--import", "tsx/esm"] : [],
69
+ });
70
+ try {
71
+ const response = await new Promise((resolve, reject) => {
72
+ const timer = setTimeout(() => reject(incomplete(`the lockfile was not read within ${Math.round(timeoutMs / 1000)} s`)), timeoutMs);
73
+ const onAbort = () => reject(incomplete("the client went away before the lockfile was read"));
74
+ limits.signal?.addEventListener("abort", onAbort, { once: true });
75
+ const settle = () => {
76
+ clearTimeout(timer);
77
+ limits.signal?.removeEventListener("abort", onAbort);
78
+ };
79
+ worker.once("message", (message) => {
80
+ settle();
81
+ resolve(message);
82
+ });
83
+ worker.once("error", (error) => {
84
+ settle();
85
+ // Out of heap: the lockfile expanded too far, a verdict about it.
86
+ // Anything else (the worker failed to load, crashed) is the proxy's
87
+ // own failure: a plain error, which the plan route logs and answers
88
+ // as a failure to verify, never as a refusal of the lockfile.
89
+ reject(error.code === "ERR_WORKER_OUT_OF_MEMORY"
90
+ ? new RegistryError(413, "wardby_lockfile_too_large", "the lockfile needs too much memory to read")
91
+ : new Error(`the lockfile reader failed: ${error.message}`, { cause: error }));
92
+ });
93
+ worker.once("exit", (code) => {
94
+ settle();
95
+ reject(new Error(`the lockfile reader stopped (exit code ${code})`));
96
+ });
97
+ const request = { ecosystem, body, ...options };
98
+ worker.postMessage(request);
99
+ });
100
+ if (!response.ok) {
101
+ if ("unexpected" in response)
102
+ throw new Error(`the lockfile reader failed: ${response.message}`);
103
+ throw new RegistryError(response.status, response.code, response.message);
104
+ }
105
+ return rebuild(response.lockfile);
106
+ }
107
+ finally {
108
+ await worker.terminate().catch(() => undefined);
109
+ }
110
+ }
@@ -1,3 +1,4 @@
1
+ import type { PackageAuditMode } from "../../../coding/registry/audit-mode.js";
1
2
  import { type AllowlistEntry, type LockfilePlanSupport, type ParsedLockfile, type RegistryAdapter, type UpstreamFetch } from "../../../coding/registry/types.js";
2
3
  import { type AuditedVersion, type WithheldVersions } from "./audit.js";
3
4
  import type { ApprovedVersion, StoredVersionFact } from "./store.js";
@@ -28,6 +29,8 @@ export interface PlanRefusal {
28
29
  export interface PlanResult {
29
30
  approved: ApprovedVersion[];
30
31
  refused: PlanRefusal[];
32
+ /** Approvals carrying an audit verdict (package audit mode only; 0 otherwise). */
33
+ audited: number;
31
34
  }
32
35
  export interface PlanFacts {
33
36
  getVersionFacts(versions: readonly {
@@ -49,6 +52,12 @@ export interface PlanInput {
49
52
  /** Unbuffered fetch for streamed package documents. */
50
53
  streamUpstream: UpstreamFetch;
51
54
  audit(versions: readonly AuditedVersion[], signal: AbortSignal): Promise<WithheldVersions>;
55
+ /** The run's accepted advisory ids (packagePolicy.acceptedAdvisories):
56
+ * blocking advisories they waive do not refuse a version (see
57
+ * `isWaived`; malware is never waived). Absent or empty waives nothing. */
58
+ accepted?: ReadonlySet<string>;
59
+ /** The run's package audit mode (CodingRun.packageAuditMode); absent or null = normal enforcement. */
60
+ auditMode?: PackageAuditMode | null;
52
61
  hostAllowed(url: string): boolean;
53
62
  signal: AbortSignal;
54
63
  factConcurrency?: number;
@@ -4,24 +4,38 @@
4
4
  * lockfile claims is trusted:
5
5
  *
6
6
  * - every entry's integrity must equal the registry's own for that exact
7
- * version (a per-version fact read from the registry, stored forever);
7
+ * version (a per-version fact read from the registry, stored forever),
8
+ * unless the format records no npm integrity at all (yarn Berry): then
9
+ * the approval is the exact name@version, and the download is checked
10
+ * against the registry's integrity alone;
8
11
  * - edges come from the registry too: a parent version's declared
9
12
  * dependencies, resolved to lockfile entries by the client's own rule
10
13
  * (npm: nearest ancestor node_modules), and kept only when the entry is
11
14
  * the declared package at a version inside the declared range;
12
- * - reachability starts at the projects' dependencies that are allowlist
13
- * roots (at a version inside the root's range) and follows only verified
14
- * edges, through versions old enough and without a HIGH/CRITICAL advisory.
15
+ * - reachability starts at every lockfile entry that is an allowlist root
16
+ * (at a version inside the root's range), wherever it sits and whether or
17
+ * not a project declares it, and follows only verified
18
+ * edges, through versions old enough and without a HIGH/CRITICAL advisory
19
+ * (one the run's accepted set waives does not count; malware always does).
20
+ *
21
+ * PyPI dependency lines may be gated on extras: an entry is expanded with the
22
+ * extras it is installed with (a start's allowlist entry's, then those its
23
+ * parents' lines ask of it), and a plain install follows none.
15
24
  *
16
25
  * A refused entry refuses itself and whatever is reachable only through it;
17
- * everything else is approved, as exact name@version pairs. The work is
26
+ * everything else is approved, as exact name@version pairs (an ecosystem
27
+ * that `approves: "names"` turns them into name allowances). In package
28
+ * audit mode every registry entry is a start as well: one the allowlist does
29
+ * not reach is approved when it passes every other check, marked with its
30
+ * audit verdict (and, in `full`, a version kept only past non-malware
31
+ * blocking advisories is approved and marked too). The work is
18
32
  * bounded: facts are fetched only for entries reached (so a lockfile of
19
33
  * unrelated packages costs nothing upstream), with bounded concurrency and a
20
34
  * deadline.
21
35
  */
22
36
  import { matchRoot } from "../../../coding/registry/allowlist.js";
23
37
  import { RegistryError, } from "../../../coding/registry/types.js";
24
- import { versionKey } from "./audit.js";
38
+ import { auditAdvisoryOf, splitWaived, versionKey } from "./audit.js";
25
39
  export const DEFAULT_PLAN_MAX_ENTRIES = 5000;
26
40
  export const DEFAULT_PLAN_TIMEOUT_MS = 120_000;
27
41
  /** Per-version documents fetched at once (each is a few KB). */
@@ -61,21 +75,29 @@ export async function verifyLockfilePlan(input) {
61
75
  reason: entry.unsupportedReason ?? "cannot be verified against the registry",
62
76
  });
63
77
  }
64
- // Starting points: what the projects themselves declare, when it is an
65
- // allowlist root at a version inside the root's range.
78
+ // Starting points: every registry entry that is an allowlist root at a
79
+ // version inside the root's range, wherever it sits in the lockfile and
80
+ // whether or not a project declares it. An allowlisted name may be
81
+ // installed by name anyway (the download path allows the same name@version:
82
+ // `authorize` matches the name with `matchRoot`, and `keptVersions`
83
+ // applies the root's range, release age and advisories), so an `overrides`-forced version of it, which no parent's
84
+ // declared range reaches, is a start in its own right. Every start still
85
+ // passes the fact checks below (integrity, release age, advisories), and
86
+ // edges are still followed only inside declared ranges.
66
87
  const starts = new Set();
67
- for (const project of lockfile.projects) {
68
- for (const dependency of project.dependencies) {
69
- const entry = lockfile.resolve(project.path, dependency.key);
70
- if (!entry || entry.kind !== "registry" || entry.name !== dependency.name)
71
- continue;
72
- const root = matchRoot(input.roots, adapter.normalizeName(entry.name));
73
- if (!root)
74
- continue;
75
- if (root.range && !safeSatisfies(adapter, entry.version, root.range))
76
- continue;
77
- starts.add(entry);
78
- }
88
+ // The extras each reached entry is installed with (PyPI): a start's are its
89
+ // allowlist entry's, a child's those its parents' lines ask of it.
90
+ const extrasOf = new Map();
91
+ for (const entry of lockfile.entries) {
92
+ if (entry.kind !== "registry")
93
+ continue;
94
+ const root = matchRoot(input.roots, adapter.normalizeName(entry.name));
95
+ if (!root)
96
+ continue;
97
+ if (root.range && !safeSatisfies(adapter, entry.version, root.range))
98
+ continue;
99
+ starts.add(entry);
100
+ extrasOf.set(entry, new Set(root.extras ?? []));
79
101
  }
80
102
  // Breadth first over verified edges, fetching facts one layer at a time
81
103
  // (only for entries actually reached).
@@ -83,11 +105,28 @@ export async function verifyLockfilePlan(input) {
83
105
  const edges = new Map();
84
106
  const candidates = new Set();
85
107
  const visited = new Set(starts);
108
+ // Package audit mode: every other registry entry starts too, except an
109
+ // allowlisted name outside its entry's own range (an owner's explicit
110
+ // restriction, never widened by audit mode). Visited after the roots'
111
+ // graph, so facts load in the same bounded layers.
112
+ const auditStarts = input.auditMode
113
+ ? lockfile.entries.filter((entry) => entry.kind === "registry" &&
114
+ !starts.has(entry) &&
115
+ !outsideRootRange(input, adapter.normalizeName(entry.name), entry.version))
116
+ : [];
86
117
  let layer = [...starts];
87
- while (layer.length > 0) {
118
+ let auditSeeded = false;
119
+ while (layer.length > 0 || (!auditSeeded && auditStarts.length > 0)) {
120
+ if (layer.length === 0) {
121
+ auditSeeded = true;
122
+ layer = auditStarts.filter((entry) => !visited.has(entry));
123
+ for (const entry of layer)
124
+ visited.add(entry);
125
+ continue;
126
+ }
88
127
  signal.throwIfAborted();
89
128
  await loadFacts(input, layer, outcomes);
90
- const next = [];
129
+ const next = new Set();
91
130
  for (const entry of layer) {
92
131
  const outcome = outcomes.get(versionKey(entry.name, entry.version));
93
132
  if ("refusal" in outcome) {
@@ -95,7 +134,8 @@ export async function verifyLockfilePlan(input) {
95
134
  continue;
96
135
  }
97
136
  const { fact } = outcome;
98
- if (entry.integrity === null || entry.integrity.trim() !== fact.integrity) {
137
+ if (lockfile.integrity !== "registry" &&
138
+ (entry.integrity === null || entry.integrity.trim() !== fact.integrity)) {
99
139
  refusals.set(entry, {
100
140
  code: "wardby_lockfile_integrity_mismatch",
101
141
  reason: `the lockfile's integrity is not the registry's for ${entry.name}@${entry.version}`,
@@ -113,34 +153,63 @@ export async function verifyLockfilePlan(input) {
113
153
  }
114
154
  candidates.add(entry);
115
155
  const children = [];
116
- for (const dependency of fact.dependencies) {
117
- const child = lockfile.resolve(entry.path, dependency.key);
118
- if (!child || child.kind !== "registry" || child.name !== dependency.name)
119
- continue;
120
- if (!support.edgeSatisfies(child.version, dependency.range))
121
- continue;
122
- children.push(child);
123
- if (!visited.has(child)) {
124
- visited.add(child);
125
- next.push(child);
156
+ const dependencies = support.dependenciesOf
157
+ ? support.dependenciesOf(fact, [...(extrasOf.get(entry) ?? [])].sort())
158
+ : fact.dependencies;
159
+ for (const dependency of dependencies) {
160
+ const resolved = lockfile.resolveAll
161
+ ? lockfile.resolveAll(entry.path, dependency.key)
162
+ : [lockfile.resolve(entry.path, dependency.key)];
163
+ for (const child of resolved) {
164
+ if (!child || child.kind !== "registry" || child.name !== dependency.name)
165
+ continue;
166
+ if (!support.edgeSatisfies(child.version, dependency.range))
167
+ continue;
168
+ children.push(child);
169
+ const asked = dependency.extras ?? [];
170
+ if (!visited.has(child)) {
171
+ visited.add(child);
172
+ extrasOf.set(child, new Set(asked));
173
+ next.add(child);
174
+ continue;
175
+ }
176
+ // Reached again with extras it lacked: expanded again, now with them
177
+ // (extras only grow, so this ends).
178
+ const known = extrasOf.get(child) ?? new Set();
179
+ if (asked.some((extra) => !known.has(extra))) {
180
+ for (const extra of asked)
181
+ known.add(extra);
182
+ extrasOf.set(child, known);
183
+ next.add(child);
184
+ }
126
185
  }
127
186
  }
128
187
  edges.set(entry, children);
129
188
  }
130
- layer = next;
189
+ layer = [...next];
131
190
  }
132
191
  // One advisory lookup for every candidate version.
133
192
  const withheld = await input.audit([...new Map([...candidates].map((entry) => [versionKey(entry.name, entry.version), entry])).values()].map(({ name, version }) => ({ name, version })), signal);
193
+ // Advisories the run's accepted set waived, per approved name@version,
194
+ // and (full mode) the blocking ones served past.
195
+ const waivers = new Map();
196
+ const auditAdvisories = new Map();
134
197
  for (const entry of candidates) {
135
- const ids = withheld.get(versionKey(entry.name, entry.version));
136
- if (ids && ids.length > 0) {
198
+ const key = versionKey(entry.name, entry.version);
199
+ const split = splitWaived(withheld.get(key) ?? [], input.accepted, { auditAll: input.auditMode === "full" });
200
+ if (split.audited.length > 0)
201
+ auditAdvisories.set(key, split.audited.map(auditAdvisoryOf));
202
+ if (split.withheld.length > 0) {
137
203
  candidates.delete(entry);
138
204
  refusals.set(entry, {
139
205
  code: "wardby_version_filtered",
140
- reason: `withheld by advisory ${ids.join(", ")} (high severity or malware)`,
206
+ reason: `withheld by advisory ${split.withheld.map(({ id }) => id).join(", ")} (high severity or malware)`,
141
207
  intrinsic: true,
142
208
  });
143
209
  }
210
+ else if (split.waived.length > 0) {
211
+ waivers.set(key, split.waived.map(({ id }) => id));
212
+ }
144
213
  }
145
214
  // Reachability again, now only through entries that passed every check:
146
215
  // a refused entry refuses whatever is reachable only through it.
@@ -157,16 +226,38 @@ export async function verifyLockfilePlan(input) {
157
226
  queue.push(child);
158
227
  }
159
228
  }
229
+ // Package audit mode: every other entry that passed every check is
230
+ // approved too, marked with how it reached the lockfile.
231
+ const relations = input.auditMode
232
+ ? auditRelations(input, approvedEntries, candidates, edges)
233
+ : new Map();
160
234
  const approved = new Map();
161
- for (const entry of approvedEntries) {
162
- const outcome = outcomes.get(versionKey(entry.name, entry.version));
163
- if (outcome && "fact" in outcome)
164
- approved.set(versionKey(entry.name, entry.version), {
165
- name: entry.name,
166
- version: entry.version,
167
- integrity: outcome.fact.integrity,
168
- });
169
- }
235
+ const approve = (entry, relation) => {
236
+ const key = versionKey(entry.name, entry.version);
237
+ const outcome = outcomes.get(key);
238
+ if (!outcome || !("fact" in outcome) || approved.has(key))
239
+ return;
240
+ const acceptedAdvisories = waivers.get(key);
241
+ const advisories = auditAdvisories.get(key);
242
+ const audit = relation
243
+ ? { ...relation, ...(advisories ? { advisories } : {}) }
244
+ : advisories
245
+ ? { relation: "allowed", advisories }
246
+ : undefined;
247
+ approved.set(key, {
248
+ name: entry.name,
249
+ version: entry.version,
250
+ integrity: outcome.fact.integrity,
251
+ ...(acceptedAdvisories ? { acceptedAdvisories } : {}),
252
+ ...(audit ? { audit } : {}),
253
+ });
254
+ };
255
+ // Approvals through the allowlist first: an exact version approved both
256
+ // ways is not audited.
257
+ for (const entry of approvedEntries)
258
+ approve(entry, undefined);
259
+ for (const [entry, relation] of relations)
260
+ approve(entry, relation);
170
261
  const refused = new Map();
171
262
  for (const entry of lockfile.entries) {
172
263
  if (entry.kind === "bundled")
@@ -182,7 +273,59 @@ export async function verifyLockfilePlan(input) {
182
273
  }
183
274
  // An entry refused at one place in the tree but approved at another (the
184
275
  // same name@version) is approved: approvals are per exact version.
185
- return { approved: [...approved.values()], refused: [...refused.values()] };
276
+ const approvals = [...approved.values()];
277
+ return {
278
+ approved: approvals,
279
+ refused: [...refused.values()],
280
+ audited: approvals.filter((approval) => approval.audit).length,
281
+ };
282
+ }
283
+ /** Whether `name@version` is an allowlisted name at a version its entry's range excludes. */
284
+ function outsideRootRange(input, name, version) {
285
+ const root = matchRoot(input.roots, name);
286
+ return !!root?.range && !safeSatisfies(input.adapter, version, root.range);
287
+ }
288
+ /**
289
+ * How each candidate the allowlist does not reach came into the lockfile
290
+ * (package audit mode): `top-level` when a lockfile project declares it,
291
+ * `transitive` when an audited top-level entry reaches it over verified
292
+ * edges (`via` its nearest such ancestor, breadth first), otherwise
293
+ * `unknown`. Best effort: it decides only what a reviewer is shown.
294
+ */
295
+ function auditRelations(input, approvedEntries, candidates, edges) {
296
+ const { lockfile } = input;
297
+ // An allowlisted name outside its entry's range is never audited in,
298
+ // even when an audited package declares it.
299
+ const audited = new Set([...candidates].filter((entry) => !approvedEntries.has(entry) && !outsideRootRange(input, input.adapter.normalizeName(entry.name), entry.version)));
300
+ const relations = new Map();
301
+ const declared = new Set();
302
+ for (const project of lockfile.projects)
303
+ for (const dependency of project.dependencies) {
304
+ const entry = lockfile.resolve(project.path, dependency.key);
305
+ if (entry)
306
+ declared.add(entry);
307
+ }
308
+ const queue = [];
309
+ for (const entry of audited)
310
+ if (declared.has(entry)) {
311
+ relations.set(entry, { relation: "top-level" });
312
+ queue.push(entry);
313
+ }
314
+ const via = new Map(queue.map((entry) => [entry, entry.name]));
315
+ while (queue.length > 0) {
316
+ const entry = queue.shift();
317
+ for (const child of edges.get(entry) ?? []) {
318
+ if (via.has(child) || !audited.has(child))
319
+ continue;
320
+ via.set(child, via.get(entry));
321
+ relations.set(child, { relation: "transitive", via: via.get(entry) });
322
+ queue.push(child);
323
+ }
324
+ }
325
+ for (const entry of audited)
326
+ if (!relations.has(entry))
327
+ relations.set(entry, { relation: "unknown" });
328
+ return relations;
186
329
  }
187
330
  function safeSatisfies(adapter, version, range) {
188
331
  try {
@@ -213,6 +356,7 @@ async function loadFacts(input, layer, outcomes) {
213
356
  if (missing.length === 0)
214
357
  return;
215
358
  const fresh = new Map();
359
+ const complete = [];
216
360
  await eachLimited(missing, input.factConcurrency ?? DEFAULT_PLAN_FACT_CONCURRENCY, async ({ name, version }) => {
217
361
  const key = versionKey(name, version);
218
362
  try {
@@ -227,6 +371,13 @@ async function loadFacts(input, layer, outcomes) {
227
371
  });
228
372
  return;
229
373
  }
374
+ // A registry that gives the publish time with the version (PyPI) needs no second read.
375
+ if (fact.publishedAt) {
376
+ const stored = { ...fact, publishedAt: fact.publishedAt };
377
+ complete.push(stored);
378
+ outcomes.set(key, checkedFact(input, stored));
379
+ return;
380
+ }
230
381
  fresh.set(key, fact);
231
382
  }
232
383
  catch (error) {
@@ -239,7 +390,6 @@ async function loadFacts(input, layer, outcomes) {
239
390
  const byName = new Map();
240
391
  for (const fact of fresh.values())
241
392
  byName.set(fact.name, [...(byName.get(fact.name) ?? []), fact.version]);
242
- const complete = [];
243
393
  await eachLimited([...byName.entries()], input.timeConcurrency ?? DEFAULT_PLAN_TIME_CONCURRENCY, async ([name, versions]) => {
244
394
  let times;
245
395
  try {
@@ -300,5 +450,8 @@ function upstreamRefusal(error) {
300
450
  return { code: "wardby_metadata_too_large", reason: error.message };
301
451
  if (error instanceof RegistryError && error.code === "wardby_package_not_found")
302
452
  return { code: "wardby_package_not_found", reason: error.message };
453
+ // A release with nothing the registry may serve (PyPI: no wheel) stays so.
454
+ if (error instanceof RegistryError && error.code === "wardby_file_not_allowed")
455
+ return { code: "wardby_file_not_allowed", reason: error.message, intrinsic: true };
303
456
  return { code: "wardby_upstream_error", reason: "the registry could not be read for it; try again" };
304
457
  }
@@ -1,12 +1,17 @@
1
1
  import type { PrismaClient } from "#prisma";
2
2
  import type { FileDependency } from "../../../coding/registry/types.js";
3
- import type { ApprovedVersion, PlanRefusedVersion, RegistryFetchRecord, RegistryRunContext, RegistryStore, StoredVersionFact } from "./store.js";
3
+ import type { ApprovedDownload, ApprovedVersion, PlanRefusedVersion, RegistryAuditInsert, RegistryFetchRecord, RegistryRunContext, RegistryStore, StoredVersionFact } from "./store.js";
4
4
  export declare class PrismaRegistryStore implements RegistryStore {
5
5
  private readonly db;
6
6
  constructor(db: PrismaClient);
7
7
  findRunByRegistryTokenHash(hash: string, now: Date): Promise<RegistryRunContext | null>;
8
8
  isAllowedDependency(runId: string, ecosystem: string, name: string): Promise<boolean>;
9
+ findAllowance(runId: string, ecosystem: string, name: string): Promise<{
10
+ auditedVia: string | null;
11
+ } | null>;
9
12
  addAllowances(runId: string, ecosystem: string, names: readonly string[]): Promise<void>;
13
+ addAuditedAllowances(runId: string, ecosystem: string, names: readonly string[], auditedVia: string): Promise<void>;
14
+ recordAudit(record: RegistryAuditInsert): Promise<void>;
10
15
  allowedExtras(runId: string, ecosystem: string, name: string): Promise<string[]>;
11
16
  addExtraAllowances(runId: string, ecosystem: string, dependencies: readonly FileDependency[]): Promise<void>;
12
17
  recordFetch(record: RegistryFetchRecord): Promise<void>;
@@ -18,20 +23,21 @@ export declare class PrismaRegistryStore implements RegistryStore {
18
23
  listFetches(runId: string): Promise<(RegistryFetchRecord & {
19
24
  createdAt: Date;
20
25
  })[]>;
26
+ listRecentRefusals(runId: string, since: Date, limit: number): Promise<(RegistryFetchRecord & {
27
+ createdAt: Date;
28
+ })[]>;
21
29
  getVersionFacts(ecosystem: string, versions: readonly {
22
30
  name: string;
23
31
  version: string;
24
32
  }[]): Promise<StoredVersionFact[]>;
25
33
  putVersionFacts(ecosystem: string, facts: readonly StoredVersionFact[]): Promise<void>;
26
34
  approveVersions(runId: string, ecosystem: string, versions: readonly ApprovedVersion[]): Promise<void>;
27
- findApprovedVersion(runId: string, ecosystem: string, name: string, version: string): Promise<{
28
- integrity: string;
29
- downloadUrl: string;
30
- } | null>;
35
+ findApprovedVersion(runId: string, ecosystem: string, name: string, version: string): Promise<ApprovedDownload | null>;
31
36
  refusePlanVersions(runId: string, ecosystem: string, versions: readonly PlanRefusedVersion[]): Promise<void>;
32
37
  findPlanRefusal(runId: string, ecosystem: string, name: string, version: string): Promise<{
33
38
  code: string;
34
39
  reason: string;
35
40
  publishedAt: Date | null;
36
41
  } | null>;
42
+ recordDependencyCacheLockHash(runId: string, lockHash: string, max: number): Promise<void>;
37
43
  }