@wardby/cli 0.6.0 → 0.8.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 (740) hide show
  1. package/.env.example +5 -5
  2. package/README.md +6 -1
  3. package/deploy/local/docker-compose.quickstart-coding.yml +19 -0
  4. package/dist/claude-coding-worker/driver.d.ts +33 -3
  5. package/dist/claude-coding-worker/driver.js +151 -21
  6. package/dist/claude-coding-worker/main.js +38 -6
  7. package/dist/claude-coding-worker/sdk.js +29 -2
  8. package/dist/claude-coding-worker/tool-socket.d.ts +27 -0
  9. package/dist/claude-coding-worker/tool-socket.js +111 -0
  10. package/dist/claude-coding-worker/transcript.d.ts +18 -0
  11. package/dist/claude-coding-worker/transcript.js +77 -0
  12. package/dist/claude-plugin/command.d.ts +61 -0
  13. package/dist/claude-plugin/command.js +376 -0
  14. package/dist/cli-help.d.ts +1 -1
  15. package/dist/cli-help.js +3 -1
  16. package/dist/cli.js +71 -13
  17. package/dist/coding/code-host.d.ts +16 -0
  18. package/dist/coding/code-host.js +60 -0
  19. package/dist/coding/continuation-wording.d.ts +35 -0
  20. package/dist/coding/continuation-wording.js +73 -0
  21. package/dist/coding/linked-issue-note.d.ts +23 -0
  22. package/dist/coding/linked-issue-note.js +47 -0
  23. package/dist/coding/mcp-token.d.ts +12 -0
  24. package/dist/coding/mcp-token.js +26 -0
  25. package/dist/coding/merge-wording.d.ts +28 -0
  26. package/dist/coding/merge-wording.js +78 -0
  27. package/dist/coding/pr-description-note.d.ts +9 -0
  28. package/dist/coding/pr-description-note.js +32 -0
  29. package/dist/coding/profile.d.ts +46 -126
  30. package/dist/coding/profile.js +45 -4
  31. package/dist/coding/protected-path-wording.d.ts +8 -0
  32. package/dist/coding/protected-path-wording.js +1 -1
  33. package/dist/coding/protocol.d.ts +264 -346
  34. package/dist/coding/protocol.js +237 -12
  35. package/dist/coding/redact.d.ts +3 -0
  36. package/dist/coding/redact.js +17 -0
  37. package/dist/coding/registry/allowlist.d.ts +12 -0
  38. package/dist/coding/registry/allowlist.js +14 -1
  39. package/dist/coding/registry/audit-mode.d.ts +153 -0
  40. package/dist/coding/registry/audit-mode.js +304 -0
  41. package/dist/coding/registry/lockfile-shared.d.ts +36 -0
  42. package/dist/coding/registry/lockfile-shared.js +128 -0
  43. package/dist/coding/registry/lockfiles.js +1 -1
  44. package/dist/coding/registry/npm-lockfile-formats.d.ts +11 -0
  45. package/dist/coding/registry/npm-lockfile-formats.js +41 -0
  46. package/dist/coding/registry/npm-lockfile.d.ts +8 -4
  47. package/dist/coding/registry/npm-lockfile.js +4 -6
  48. package/dist/coding/registry/npm.js +78 -8
  49. package/dist/coding/registry/pnpm-lockfile.d.ts +17 -0
  50. package/dist/coding/registry/pnpm-lockfile.js +189 -0
  51. package/dist/coding/registry/pypi-plan.d.ts +50 -0
  52. package/dist/coding/registry/pypi-plan.js +406 -0
  53. package/dist/coding/registry/pypi-requirements.d.ts +58 -0
  54. package/dist/coding/registry/pypi-requirements.js +155 -0
  55. package/dist/coding/registry/pypi.d.ts +3 -27
  56. package/dist/coding/registry/pypi.js +40 -154
  57. package/dist/coding/registry/refusal-hints.d.ts +20 -0
  58. package/dist/coding/registry/refusal-hints.js +61 -0
  59. package/dist/coding/registry/refusal-wording.d.ts +11 -0
  60. package/dist/coding/registry/refusal-wording.js +56 -0
  61. package/dist/coding/registry/report.d.ts +70 -8
  62. package/dist/coding/registry/report.js +112 -5
  63. package/dist/coding/registry/types.d.ts +58 -8
  64. package/dist/coding/registry/yarn-lockfile.d.ts +30 -0
  65. package/dist/coding/registry/yarn-lockfile.js +367 -0
  66. package/dist/coding/services/catalog.d.ts +37 -351
  67. package/dist/coding/transcript/claude-code.d.ts +13 -0
  68. package/dist/coding/transcript/claude-code.js +273 -0
  69. package/dist/coding/transcript/download-protocol.d.ts +30 -0
  70. package/dist/coding/transcript/download-protocol.js +18 -0
  71. package/dist/coding/transcript/format.d.ts +20 -0
  72. package/dist/coding/transcript/format.js +32 -0
  73. package/dist/coding/transcript/recorder.d.ts +52 -0
  74. package/dist/coding/transcript/recorder.js +225 -0
  75. package/dist/coding/transcript/resume/checkout.d.ts +31 -0
  76. package/dist/coding/transcript/resume/checkout.js +203 -0
  77. package/dist/coding/transcript/resume/command.d.ts +17 -0
  78. package/dist/coding/transcript/resume/command.js +176 -0
  79. package/dist/coding/transcript/resume/download.d.ts +20 -0
  80. package/dist/coding/transcript/resume/download.js +153 -0
  81. package/dist/coding/transcript/resume/launch.d.ts +38 -0
  82. package/dist/coding/transcript/resume/launch.js +128 -0
  83. package/dist/coding/transcript/resume/session-file.d.ts +13 -0
  84. package/dist/coding/transcript/resume/session-file.js +79 -0
  85. package/dist/coding/transcript/resume/stand-in.d.ts +11 -0
  86. package/dist/coding/transcript/resume/stand-in.js +49 -0
  87. package/dist/coding-worker/artifact.d.ts +8 -3
  88. package/dist/coding-worker/artifact.js +3 -1
  89. package/dist/coding-worker/debug-trace.d.ts +9 -6
  90. package/dist/coding-worker/debug-trace.js +16 -19
  91. package/dist/coding-worker/dependency-cache.d.mts +229 -0
  92. package/dist/coding-worker/dependency-cache.mjs +2123 -0
  93. package/dist/coding-worker/driver.d.ts +4 -3
  94. package/dist/coding-worker/driver.js +124 -39
  95. package/dist/coding-worker/errors.d.ts +1 -0
  96. package/dist/coding-worker/errors.js +32 -0
  97. package/dist/coding-worker/main.js +5 -1
  98. package/dist/coding-worker/mcp-config.d.ts +27 -0
  99. package/dist/coding-worker/mcp-config.js +32 -0
  100. package/dist/coding-worker/sdk.d.ts +36 -0
  101. package/dist/coding-worker/sdk.js +28 -1
  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 +15 -0
  105. package/dist/config/dependency-cache.d.ts +23 -0
  106. package/dist/config/dependency-cache.js +32 -0
  107. package/dist/config/mcp-call-limit.d.ts +8 -0
  108. package/dist/config/mcp-call-limit.js +18 -0
  109. package/dist/config/mcp-servers.d.ts +8 -0
  110. package/dist/config/mcp-servers.js +12 -0
  111. package/dist/config/providers.d.ts +12 -3
  112. package/dist/config/providers.js +55 -5
  113. package/dist/config/test-simulators.d.ts +92 -0
  114. package/dist/config/test-simulators.js +333 -0
  115. package/dist/config/tool-timeout.d.ts +1 -0
  116. package/dist/config/tool-timeout.js +18 -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-mcp.d.ts +36 -0
  130. package/dist/core/coding-mcp.js +253 -0
  131. package/dist/core/coding-queue.d.ts +2 -1
  132. package/dist/core/coding-queue.js +7 -0
  133. package/dist/core/coding-repository.d.ts +46 -0
  134. package/dist/core/coding-repository.js +92 -0
  135. package/dist/core/cron.d.ts +19 -1
  136. package/dist/core/cron.js +17 -0
  137. package/dist/core/datastores.js +1 -1
  138. package/dist/core/deferred-coding.d.ts +14 -0
  139. package/dist/core/deferred-coding.js +62 -0
  140. package/dist/core/delegation-child.d.ts +25 -0
  141. package/dist/core/delegation-child.js +37 -0
  142. package/dist/core/delegation-slots.d.ts +27 -0
  143. package/dist/core/delegation-slots.js +44 -0
  144. package/dist/core/dispatch.d.ts +48 -2
  145. package/dist/core/dispatch.js +166 -66
  146. package/dist/core/engine-native.js +1 -1
  147. package/dist/core/error-code.d.ts +38 -0
  148. package/dist/core/error-code.js +169 -0
  149. package/dist/core/grants.d.ts +46 -8
  150. package/dist/core/grants.js +99 -8
  151. package/dist/core/host-events.d.ts +18 -2
  152. package/dist/core/host-events.js +291 -27
  153. package/dist/core/host-status.d.ts +85 -3
  154. package/dist/core/host-status.js +300 -22
  155. package/dist/core/issue-bridge.d.ts +50 -6
  156. package/dist/core/issue-bridge.js +124 -33
  157. package/dist/core/issue-events.d.ts +1 -1
  158. package/dist/core/issue-events.js +4 -10
  159. package/dist/core/issue-status.d.ts +1 -1
  160. package/dist/core/issue-status.js +58 -29
  161. package/dist/core/logger.d.ts +5 -0
  162. package/dist/core/logger.js +14 -1
  163. package/dist/core/lost-run-children.d.ts +6 -0
  164. package/dist/core/lost-run-children.js +32 -0
  165. package/dist/core/mark-ready.d.ts +34 -0
  166. package/dist/core/mark-ready.js +152 -0
  167. package/dist/core/markdown-code.d.ts +8 -0
  168. package/dist/core/markdown-code.js +107 -0
  169. package/dist/core/mcp-client/client.d.ts +47 -0
  170. package/dist/core/mcp-client/client.js +141 -0
  171. package/dist/core/mcp-client/jsonrpc.d.ts +15 -0
  172. package/dist/core/mcp-client/jsonrpc.js +54 -0
  173. package/dist/core/mcp-client/results.d.ts +13 -0
  174. package/dist/core/mcp-client/results.js +42 -0
  175. package/dist/core/mcp-servers.d.ts +90 -0
  176. package/dist/core/mcp-servers.js +230 -0
  177. package/dist/core/mcp-tools.d.ts +108 -0
  178. package/dist/core/mcp-tools.js +287 -0
  179. package/dist/core/memory-tools.js +7 -0
  180. package/dist/core/merge-order-check.d.ts +2 -1
  181. package/dist/core/merge-order-check.js +44 -18
  182. package/dist/core/notification-dispatcher.js +2 -1
  183. package/dist/core/notification-templates.d.ts +1 -7
  184. package/dist/core/notification-templates.js +36 -2
  185. package/dist/core/pull-request-state-sync.js +2 -2
  186. package/dist/core/reconciler.d.ts +4 -3
  187. package/dist/core/reconciler.js +8 -1
  188. package/dist/core/related-pull-requests.d.ts +11 -3
  189. package/dist/core/related-pull-requests.js +49 -20
  190. package/dist/core/reply-parts.d.ts +21 -0
  191. package/dist/core/reply-parts.js +73 -0
  192. package/dist/core/review-fix.d.ts +1 -1
  193. package/dist/core/review-fix.js +4 -3
  194. package/dist/core/review-host-checks.d.ts +1 -1
  195. package/dist/core/review-host-tools.d.ts +3 -0
  196. package/dist/core/review-host-tools.js +102 -14
  197. package/dist/core/review-reads.d.ts +55 -0
  198. package/dist/core/review-reads.js +233 -0
  199. package/dist/core/run-status.d.ts +12 -0
  200. package/dist/core/run-status.js +12 -0
  201. package/dist/core/runner.d.ts +145 -49
  202. package/dist/core/runner.js +719 -216
  203. package/dist/core/schedule-start.d.ts +12 -0
  204. package/dist/core/schedule-start.js +14 -0
  205. package/dist/core/secret-broker-config.d.ts +12 -74
  206. package/dist/core/secret-broker-config.js +1 -1
  207. package/dist/core/secret-guard.d.ts +49 -0
  208. package/dist/core/secret-guard.js +119 -0
  209. package/dist/core/secret-guarded-stores.d.ts +12 -0
  210. package/dist/core/secret-guarded-stores.js +41 -0
  211. package/dist/core/secret-spans.d.ts +50 -0
  212. package/dist/core/secret-spans.js +94 -0
  213. package/dist/core/secrets.d.ts +11 -1
  214. package/dist/core/secrets.js +27 -1
  215. package/dist/core/serializable-retry.d.ts +18 -0
  216. package/dist/core/serializable-retry.js +43 -0
  217. package/dist/core/stop-run.d.ts +46 -0
  218. package/dist/core/stop-run.js +61 -0
  219. package/dist/core/tool-admin.d.ts +5 -1
  220. package/dist/core/tool-admin.js +7 -2
  221. package/dist/core/tool-names.d.ts +13 -0
  222. package/dist/core/tool-names.js +20 -0
  223. package/dist/core/webhooks.d.ts +4 -0
  224. package/dist/core/webhooks.js +68 -49
  225. package/dist/core/workflow-events.d.ts +14 -2
  226. package/dist/core/workflow-events.js +4 -0
  227. package/dist/core/workflow-run-events.d.ts +5 -3
  228. package/dist/core/workflow-run-events.js +45 -9
  229. package/dist/core/zod-json-schema.d.ts +4 -0
  230. package/dist/core/zod-json-schema.js +39 -0
  231. package/dist/core/zod3-window.d.ts +35 -0
  232. package/dist/core/zod3-window.js +124 -0
  233. package/dist/generated/prisma/browser.d.ts +65 -3
  234. package/dist/generated/prisma/client.d.ts +65 -3
  235. package/dist/generated/prisma/commonInputTypes.d.ts +266 -106
  236. package/dist/generated/prisma/enums.d.ts +10 -0
  237. package/dist/generated/prisma/enums.js +8 -0
  238. package/dist/generated/prisma/internal/class.d.ts +110 -0
  239. package/dist/generated/prisma/internal/class.js +4 -4
  240. package/dist/generated/prisma/internal/prismaNamespace.d.ts +957 -9
  241. package/dist/generated/prisma/internal/prismaNamespace.js +166 -2
  242. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +174 -0
  243. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +166 -2
  244. package/dist/generated/prisma/models/Agent.d.ts +377 -8
  245. package/dist/generated/prisma/models/AgentMcpServer.d.ts +1412 -0
  246. package/dist/generated/prisma/models/AgentMcpServer.js +1 -0
  247. package/dist/generated/prisma/models/AgentRepository.d.ts +81 -1
  248. package/dist/generated/prisma/models/AuthUser.d.ts +1 -1
  249. package/dist/generated/prisma/models/CodingAgentProfile.d.ts +228 -7
  250. package/dist/generated/prisma/models/CodingProxyRequest.d.ts +207 -3
  251. package/dist/generated/prisma/models/CodingProxySession.d.ts +232 -2
  252. package/dist/generated/prisma/models/CodingRun.d.ts +1712 -1
  253. package/dist/generated/prisma/models/DependencySnapshot.d.ts +1668 -0
  254. package/dist/generated/prisma/models/DependencySnapshot.js +1 -0
  255. package/dist/generated/prisma/models/HostProvider.d.ts +2176 -0
  256. package/dist/generated/prisma/models/HostProvider.js +1 -0
  257. package/dist/generated/prisma/models/HostProviderCredential.d.ts +1285 -0
  258. package/dist/generated/prisma/models/HostProviderCredential.js +1 -0
  259. package/dist/generated/prisma/models/HostProviderOperation.d.ts +1470 -0
  260. package/dist/generated/prisma/models/HostProviderOperation.js +1 -0
  261. package/dist/generated/prisma/models/HostProviderTrustedAccount.d.ts +1145 -0
  262. package/dist/generated/prisma/models/HostProviderTrustedAccount.js +1 -0
  263. package/dist/generated/prisma/models/IssuePullRequest.d.ts +6 -2
  264. package/dist/generated/prisma/models/LocalReview.d.ts +40 -1
  265. package/dist/generated/prisma/models/McpServer.d.ts +1500 -0
  266. package/dist/generated/prisma/models/McpServer.js +1 -0
  267. package/dist/generated/prisma/models/NativeGatewayCall.d.ts +77 -1
  268. package/dist/generated/prisma/models/NativeGatewaySession.d.ts +35 -1
  269. package/dist/generated/prisma/models/Principal.d.ts +167 -0
  270. package/dist/generated/prisma/models/RegistryAllowance.d.ts +41 -1
  271. package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +75 -1
  272. package/dist/generated/prisma/models/RegistryAuditRecord.d.ts +1364 -0
  273. package/dist/generated/prisma/models/RegistryAuditRecord.js +1 -0
  274. package/dist/generated/prisma/models/ResourceGrant.d.ts +1 -1
  275. package/dist/generated/prisma/models/Run.d.ts +738 -2
  276. package/dist/generated/prisma/models/RunBudgetEvent.d.ts +1211 -0
  277. package/dist/generated/prisma/models/RunBudgetEvent.js +1 -0
  278. package/dist/generated/prisma/models/RunTranscript.d.ts +1339 -0
  279. package/dist/generated/prisma/models/RunTranscript.js +1 -0
  280. package/dist/generated/prisma/models/Secret.d.ts +387 -0
  281. package/dist/generated/prisma/models/Tool.d.ts +223 -1
  282. package/dist/generated/prisma/models.d.ts +10 -0
  283. package/dist/help/search.js +12 -3
  284. package/dist/help-index.json +2033 -110
  285. package/dist/import/create.js +48 -4
  286. package/dist/import/neutral-schema.d.ts +51 -281
  287. package/dist/import/neutral-schema.js +7 -4
  288. package/dist/knowledge/check.d.ts +1 -1
  289. package/dist/knowledge/cli.d.ts +11 -1
  290. package/dist/knowledge/cli.js +37 -4
  291. package/dist/knowledge/concept.d.ts +3 -19
  292. package/dist/knowledge/location.d.ts +31 -0
  293. package/dist/knowledge/location.js +78 -0
  294. package/dist/mcp/auth/access.d.ts +10 -2
  295. package/dist/mcp/auth/access.js +15 -1
  296. package/dist/mcp/auth/grants-cli.d.ts +6 -4
  297. package/dist/mcp/auth/grants-cli.js +42 -3
  298. package/dist/mcp/auth/ownership.d.ts +7 -7
  299. package/dist/mcp/auth/resource-server.d.ts +3 -1
  300. package/dist/mcp/auth/resource-server.js +17 -2
  301. package/dist/mcp/auth/self-hosted/credentials.d.ts +3 -3
  302. package/dist/mcp/auth/self-hosted/session.d.ts +2 -2
  303. package/dist/mcp/context.d.ts +3 -0
  304. package/dist/mcp/host-events/github-ingress.d.ts +2 -0
  305. package/dist/mcp/host-events/github-ingress.js +2 -1
  306. package/dist/mcp/index.d.ts +6 -0
  307. package/dist/mcp/index.js +69 -10
  308. package/dist/mcp/server.d.ts +13 -0
  309. package/dist/mcp/server.js +33 -14
  310. package/dist/mcp/tools/agents.d.ts +4 -0
  311. package/dist/mcp/tools/agents.js +295 -28
  312. package/dist/mcp/tools/datastore.js +17 -8
  313. package/dist/mcp/tools/grants.js +73 -29
  314. package/dist/mcp/tools/host-providers.d.ts +2 -0
  315. package/dist/mcp/tools/host-providers.js +215 -0
  316. package/dist/mcp/tools/mcp-servers.d.ts +26 -0
  317. package/dist/mcp/tools/mcp-servers.js +481 -0
  318. package/dist/mcp/tools/memory.js +11 -1
  319. package/dist/mcp/tools/notification-channels.js +2 -2
  320. package/dist/mcp/tools/packages.d.ts +2 -0
  321. package/dist/mcp/tools/packages.js +195 -0
  322. package/dist/mcp/tools/repositories.js +65 -12
  323. package/dist/mcp/tools/run-access.d.ts +16 -0
  324. package/dist/mcp/tools/run-access.js +25 -0
  325. package/dist/mcp/tools/run-transcript-download.d.ts +30 -0
  326. package/dist/mcp/tools/run-transcript-download.js +129 -0
  327. package/dist/mcp/tools/run-transcript.d.ts +19 -0
  328. package/dist/mcp/tools/run-transcript.js +70 -0
  329. package/dist/mcp/tools/runs.js +209 -27
  330. package/dist/mcp/tools/scheduling.js +3 -1
  331. package/dist/mcp/tools/secret-elicitation-form.js +5 -0
  332. package/dist/mcp/tools/secret-elicitation-server.d.ts +6 -0
  333. package/dist/mcp/tools/secret-elicitation-server.js +10 -61
  334. package/dist/mcp/tools/secret-elicitation.d.ts +2 -0
  335. package/dist/mcp/tools/secret-elicitation.js +25 -2
  336. package/dist/mcp/tools/secrets.js +40 -6
  337. package/dist/mcp/tools/stop-run.d.ts +2 -0
  338. package/dist/mcp/tools/stop-run.js +82 -0
  339. package/dist/mcp/tools/subagents.js +2 -2
  340. package/dist/mcp/tools/tools.d.ts +7 -5
  341. package/dist/mcp/tools/tools.js +64 -16
  342. package/dist/mcp/tools/trigger.js +28 -6
  343. package/dist/mcp/transport/http-limits.d.ts +2 -0
  344. package/dist/mcp/transport/http-limits.js +6 -2
  345. package/dist/mcp/transport/stdio-link-host.d.ts +20 -0
  346. package/dist/mcp/transport/stdio-link-host.js +67 -0
  347. package/dist/mcp/transport/streamable-http.d.ts +13 -0
  348. package/dist/mcp/transport/streamable-http.js +41 -1
  349. package/dist/mcp/webhooks/ingress.js +10 -0
  350. package/dist/native-worker/docker-launcher.d.ts +38 -2
  351. package/dist/native-worker/docker-launcher.js +65 -5
  352. package/dist/native-worker/gateway-server.d.ts +55 -3
  353. package/dist/native-worker/gateway-server.js +152 -34
  354. package/dist/native-worker/gateway.d.ts +29 -1
  355. package/dist/native-worker/gateway.js +74 -4
  356. package/dist/native-worker/http-server.d.ts +7 -1
  357. package/dist/native-worker/http-server.js +62 -7
  358. package/dist/native-worker/http-transport.js +1 -1
  359. package/dist/native-worker/kubernetes-launcher.d.ts +7 -3
  360. package/dist/native-worker/kubernetes-launcher.js +43 -3
  361. package/dist/native-worker/launch.d.ts +18 -1
  362. package/dist/native-worker/launch.js +6 -1
  363. package/dist/native-worker/ledger.d.ts +24 -4
  364. package/dist/native-worker/ledger.js +62 -8
  365. package/dist/native-worker/loopback.js +3 -8
  366. package/dist/native-worker/main.d.ts +2 -1
  367. package/dist/native-worker/main.js +14 -3
  368. package/dist/native-worker/protocol.d.ts +130 -577
  369. package/dist/native-worker/protocol.js +51 -2
  370. package/dist/native-worker/sandbox-executor.d.ts +17 -2
  371. package/dist/native-worker/sandbox-executor.js +78 -4
  372. package/dist/native-worker/stderr-redaction.d.ts +77 -0
  373. package/dist/native-worker/stderr-redaction.js +237 -0
  374. package/dist/native-worker/stdio.d.ts +1 -1
  375. package/dist/native-worker/stdio.js +6 -5
  376. package/dist/native-worker/warm-pool.d.ts +2 -2
  377. package/dist/native-worker/warm-pool.js +4 -4
  378. package/dist/native-worker/worker.js +28 -2
  379. package/dist/observability/metrics.js +6 -1
  380. package/dist/providers/auth/self-hosted.d.ts +1 -1
  381. package/dist/providers/coding-proxy/capability-hash.d.ts +2 -0
  382. package/dist/providers/coding-proxy/capability-hash.js +5 -0
  383. package/dist/providers/coding-proxy/dependency-cache/eviction.d.ts +29 -0
  384. package/dist/providers/coding-proxy/dependency-cache/eviction.js +87 -0
  385. package/dist/providers/coding-proxy/dependency-cache/key.d.ts +33 -0
  386. package/dist/providers/coding-proxy/dependency-cache/key.js +86 -0
  387. package/dist/providers/coding-proxy/dependency-cache/model-request-seal.d.ts +18 -0
  388. package/dist/providers/coding-proxy/dependency-cache/model-request-seal.js +40 -0
  389. package/dist/providers/coding-proxy/dependency-cache/prisma-store.d.ts +25 -0
  390. package/dist/providers/coding-proxy/dependency-cache/prisma-store.js +131 -0
  391. package/dist/providers/coding-proxy/dependency-cache/service.d.ts +70 -0
  392. package/dist/providers/coding-proxy/dependency-cache/service.js +409 -0
  393. package/dist/providers/coding-proxy/dependency-cache/store.d.ts +85 -0
  394. package/dist/providers/coding-proxy/dependency-cache/store.js +95 -0
  395. package/dist/providers/coding-proxy/hold.d.ts +32 -0
  396. package/dist/providers/coding-proxy/hold.js +86 -0
  397. package/dist/providers/coding-proxy/interrupted.d.ts +70 -0
  398. package/dist/providers/coding-proxy/interrupted.js +104 -0
  399. package/dist/providers/coding-proxy/mcp-route.d.ts +34 -0
  400. package/dist/providers/coding-proxy/mcp-route.js +166 -0
  401. package/dist/providers/coding-proxy/memory-ledger.d.ts +17 -2
  402. package/dist/providers/coding-proxy/memory-ledger.js +99 -8
  403. package/dist/providers/coding-proxy/metering.d.ts +16 -0
  404. package/dist/providers/coding-proxy/metering.js +27 -4
  405. package/dist/providers/coding-proxy/prisma-ledger.d.ts +16 -2
  406. package/dist/providers/coding-proxy/prisma-ledger.js +209 -69
  407. package/dist/providers/coding-proxy/proxy.d.ts +21 -2
  408. package/dist/providers/coding-proxy/proxy.js +300 -48
  409. package/dist/providers/coding-proxy/registry/audit.d.ts +53 -10
  410. package/dist/providers/coding-proxy/registry/audit.js +94 -17
  411. package/dist/providers/coding-proxy/registry/lockfile-parse-worker.d.ts +36 -0
  412. package/dist/providers/coding-proxy/registry/lockfile-parse-worker.js +67 -0
  413. package/dist/providers/coding-proxy/registry/lockfile-parse.d.ts +16 -0
  414. package/dist/providers/coding-proxy/registry/lockfile-parse.js +110 -0
  415. package/dist/providers/coding-proxy/registry/plan.d.ts +9 -0
  416. package/dist/providers/coding-proxy/registry/plan.js +201 -48
  417. package/dist/providers/coding-proxy/registry/prisma-store.d.ts +11 -5
  418. package/dist/providers/coding-proxy/registry/prisma-store.js +120 -17
  419. package/dist/providers/coding-proxy/registry/service.d.ts +97 -16
  420. package/dist/providers/coding-proxy/registry/service.js +579 -112
  421. package/dist/providers/coding-proxy/registry/store.d.ts +84 -9
  422. package/dist/providers/coding-proxy/registry/store.js +54 -1
  423. package/dist/providers/coding-proxy/runtime.d.ts +10 -0
  424. package/dist/providers/coding-proxy/runtime.js +109 -16
  425. package/dist/providers/coding-proxy/server.d.ts +9 -1
  426. package/dist/providers/coding-proxy/server.js +55 -1
  427. package/dist/providers/coding-proxy/types.d.ts +78 -1
  428. package/dist/providers/datastore/types.d.ts +5 -0
  429. package/dist/providers/engine/types.d.ts +17 -2
  430. package/dist/providers/engine/types.js +17 -0
  431. package/dist/providers/executor/composition.d.ts +10 -3
  432. package/dist/providers/executor/composition.js +52 -7
  433. package/dist/providers/executor/container.d.ts +394 -9
  434. package/dist/providers/executor/container.js +1210 -96
  435. package/dist/providers/executor/dbos-status.d.ts +31 -0
  436. package/dist/providers/executor/dbos-status.js +64 -0
  437. package/dist/providers/executor/dbos.d.ts +50 -1
  438. package/dist/providers/executor/dbos.js +207 -20
  439. package/dist/providers/executor/deferred.d.ts +12 -24
  440. package/dist/providers/executor/deferred.js +10 -29
  441. package/dist/providers/executor/in-process.d.ts +9 -1
  442. package/dist/providers/executor/in-process.js +14 -4
  443. package/dist/providers/executor/linked-issue-reader.d.ts +16 -0
  444. package/dist/providers/executor/linked-issue-reader.js +30 -0
  445. package/dist/providers/executor/routing.d.ts +2 -0
  446. package/dist/providers/executor/routing.js +4 -0
  447. package/dist/providers/executor/types.d.ts +13 -0
  448. package/dist/providers/host-tools/invoke.d.ts +37 -0
  449. package/dist/providers/host-tools/invoke.js +117 -0
  450. package/dist/providers/host-tools/operations.d.ts +65 -0
  451. package/dist/providers/host-tools/operations.js +149 -0
  452. package/dist/providers/host-tools/registry.d.ts +79 -0
  453. package/dist/providers/host-tools/registry.js +362 -0
  454. package/dist/providers/host-tools/request-guard.d.ts +17 -0
  455. package/dist/providers/host-tools/request-guard.js +99 -0
  456. package/dist/providers/host-tools/self-test.d.ts +64 -0
  457. package/dist/providers/host-tools/self-test.js +142 -0
  458. package/dist/providers/index.d.ts +2 -1
  459. package/dist/providers/issue-tracker/types.d.ts +17 -0
  460. package/dist/providers/issue-tracker/types.js +39 -1
  461. package/dist/providers/jobs/claude-tool-setup.d.ts +4 -0
  462. package/dist/providers/jobs/claude-tool-setup.js +5 -2
  463. package/dist/providers/jobs/docker-isolation.d.ts +10 -2
  464. package/dist/providers/jobs/docker-isolation.js +47 -8
  465. package/dist/providers/jobs/docker.d.ts +9 -0
  466. package/dist/providers/jobs/docker.js +60 -11
  467. package/dist/providers/jobs/fake-kubernetes-api.d.ts +2 -0
  468. package/dist/providers/jobs/fake-kubernetes-api.js +3 -0
  469. package/dist/providers/jobs/kubernetes-api.d.ts +8 -0
  470. package/dist/providers/jobs/kubernetes-api.js +10 -0
  471. package/dist/providers/jobs/kubernetes-client.js +10 -1
  472. package/dist/providers/jobs/kubernetes-isolation.d.ts +5 -1
  473. package/dist/providers/jobs/kubernetes-isolation.js +32 -3
  474. package/dist/providers/jobs/kubernetes.d.ts +11 -1
  475. package/dist/providers/jobs/kubernetes.js +111 -17
  476. package/dist/providers/jobs/types.d.ts +39 -0
  477. package/dist/providers/jobs/types.js +13 -1
  478. package/dist/providers/llm/anthropic.js +5 -1
  479. package/dist/providers/llm/bedrock.d.ts +0 -12
  480. package/dist/providers/llm/bedrock.js +4 -0
  481. package/dist/providers/llm/claude-messages.d.ts +3 -1
  482. package/dist/providers/llm/claude-messages.js +4 -1
  483. package/dist/providers/llm/claude-provider.d.ts +2 -2
  484. package/dist/providers/llm/claude-provider.js +2 -2
  485. package/dist/providers/llm/openai.d.ts +2 -2
  486. package/dist/providers/llm/openai.js +6 -2
  487. package/dist/providers/llm/routing.d.ts +2 -2
  488. package/dist/providers/llm/routing.js +2 -2
  489. package/dist/providers/llm/types.d.ts +12 -2
  490. package/dist/providers/review-host/github-events.d.ts +9 -1
  491. package/dist/providers/review-host/github-events.js +34 -12
  492. package/dist/providers/review-host/github.d.ts +29 -2
  493. package/dist/providers/review-host/github.js +224 -29
  494. package/dist/providers/review-host/local.d.ts +8 -4
  495. package/dist/providers/review-host/local.js +49 -31
  496. package/dist/providers/review-host/patch-pages.d.ts +41 -0
  497. package/dist/providers/review-host/patch-pages.js +176 -0
  498. package/dist/providers/review-host/review-format.d.ts +19 -1
  499. package/dist/providers/review-host/review-format.js +38 -7
  500. package/dist/providers/review-host/types.d.ts +140 -6
  501. package/dist/providers/review-host/types.js +9 -1
  502. package/dist/providers/storage/gcs.d.ts +63 -0
  503. package/dist/providers/storage/gcs.js +69 -0
  504. package/dist/providers/storage/index.d.ts +15 -0
  505. package/dist/providers/storage/index.js +14 -0
  506. package/dist/providers/storage/local.d.ts +24 -0
  507. package/dist/providers/storage/local.js +92 -0
  508. package/dist/providers/storage/meter.d.ts +14 -0
  509. package/dist/providers/storage/meter.js +34 -0
  510. package/dist/providers/storage/s3.d.ts +41 -0
  511. package/dist/providers/storage/s3.js +107 -0
  512. package/dist/providers/storage/types.d.ts +37 -3
  513. package/dist/providers/storage/types.js +21 -4
  514. package/dist/providers/vcs/git.d.ts +39 -4
  515. package/dist/providers/vcs/git.js +339 -26
  516. package/dist/providers/vcs/github-remote.d.ts +26 -3
  517. package/dist/providers/vcs/github-remote.js +124 -14
  518. package/dist/providers/vcs/github.d.ts +183 -44
  519. package/dist/providers/vcs/github.js +455 -56
  520. package/dist/providers/vcs/index.d.ts +1 -1
  521. package/dist/providers/vcs/index.js +9 -2
  522. package/dist/providers/vcs/pr-description.d.ts +127 -0
  523. package/dist/providers/vcs/pr-description.js +270 -0
  524. package/dist/providers/vcs/remote.d.ts +37 -1
  525. package/dist/providers/vcs/remote.js +7 -1
  526. package/dist/providers/vcs/routing.d.ts +16 -8
  527. package/dist/providers/vcs/routing.js +27 -6
  528. package/dist/providers/vcs/types.d.ts +150 -5
  529. package/dist/quickstart/coding-seed.d.ts +14 -3
  530. package/dist/quickstart/coding-seed.js +213 -4
  531. package/dist/quickstart/index.d.ts +5 -0
  532. package/dist/quickstart/index.js +21 -0
  533. package/dist/quickstart/reviewer-prompt.d.ts +1 -1
  534. package/dist/quickstart/reviewer-prompt.js +6 -6
  535. package/dist/quickstart-images.json +1 -1
  536. package/dist/sandbox/eval-core.d.ts +12 -2
  537. package/dist/sandbox/eval-core.js +20 -4
  538. package/dist/sandbox/generated/zod.bundle.js +62 -1
  539. package/dist/sandbox/generated/zod3.bundle.js +5 -0
  540. package/dist/sandbox/host-functions.d.ts +5 -0
  541. package/dist/sandbox/host-functions.js +13 -13
  542. package/dist/sandbox/limits.d.ts +4 -1
  543. package/dist/sandbox/limits.js +4 -1
  544. package/dist/sandbox/run-in-sandbox.d.ts +6 -0
  545. package/dist/sandbox/run-in-sandbox.js +14 -2
  546. package/dist/sandbox/safe-fetch.d.ts +2 -0
  547. package/dist/sandbox/safe-fetch.js +1 -1
  548. package/dist/sandbox/secret-broker.d.ts +1 -0
  549. package/dist/sandbox/secret-broker.js +4 -5
  550. package/dist/sandbox/tool-capabilities.d.ts +10 -30
  551. package/dist/sandbox/tool-timeout.d.ts +32 -0
  552. package/dist/sandbox/tool-timeout.js +52 -0
  553. package/dist/sandbox/user-tool.d.ts +8 -1
  554. package/dist/sandbox/user-tool.js +9 -3
  555. package/dist/sandbox/zod-params.d.ts +37 -5
  556. package/dist/sandbox/zod-params.js +181 -26
  557. package/dist/serve.js +8 -1
  558. package/dist/viewer/api-schema.d.ts +349 -2627
  559. package/dist/viewer/api-schema.js +7 -0
  560. package/dist/viewer/build-schemas.js +6 -2
  561. package/dist/viewer/graph.js +2 -1
  562. package/dist/viewer/openapi.js +2 -4
  563. package/dist/wardby-bin.js +15 -0
  564. package/docs/README.md +11 -1
  565. package/docs/agent-recipes.md +95 -24
  566. package/docs/architecture-runtime.md +1 -1
  567. package/docs/claude-code-plugin.md +164 -0
  568. package/docs/code-review-agents.md +557 -41
  569. package/docs/coding-agent-setup.md +566 -8
  570. package/docs/coding-packages.md +1033 -90
  571. package/docs/coding-services.md +27 -12
  572. package/docs/coding-worker-byo-images.md +24 -2
  573. package/docs/coding-worker-isolation.md +183 -37
  574. package/docs/debugging-agent-runs.md +242 -0
  575. package/docs/getting-started-gke.md +129 -141
  576. package/docs/getting-started-identity-provider.md +53 -38
  577. package/docs/getting-started.md +17 -0
  578. package/docs/host-providers.md +294 -0
  579. package/docs/jira-agents.md +17 -8
  580. package/docs/knowledge/blob-not-found-semantics.md +41 -0
  581. package/docs/knowledge/bounded-file-descriptor.md +52 -0
  582. package/docs/knowledge/delegation-admission-slots.md +52 -0
  583. package/docs/knowledge/http-dispatcher-bootstrap.md +61 -0
  584. package/docs/knowledge/import-rename-target.md +52 -0
  585. package/docs/knowledge/index.md +57 -0
  586. package/docs/knowledge/log.md +9 -0
  587. package/docs/knowledge/mcp-cancelled-task-precedence.md +41 -0
  588. package/docs/knowledge/mcp-http-prompt-discovery.md +41 -0
  589. package/docs/knowledge/mcp-schema-module-boundary.md +41 -0
  590. package/docs/knowledge/nullable-tool-uniqueness.md +52 -0
  591. package/docs/knowledge/overview-cli.md +74 -0
  592. package/docs/knowledge/overview-coding-proxy.md +72 -0
  593. package/docs/knowledge/overview-coding.md +72 -0
  594. package/docs/knowledge/overview-config.md +60 -0
  595. package/docs/knowledge/overview-core.md +72 -0
  596. package/docs/knowledge/overview-deploy.md +83 -0
  597. package/docs/knowledge/overview-mcp.md +61 -0
  598. package/docs/knowledge/overview-prisma.md +72 -0
  599. package/docs/knowledge/overview-providers.md +89 -0
  600. package/docs/knowledge/overview-sandbox.md +83 -0
  601. package/docs/knowledge/overview-surfaces.md +83 -0
  602. package/docs/knowledge/private-datastore-shared-boundary.md +54 -0
  603. package/docs/knowledge/proxy-settlement-projections.md +41 -0
  604. package/docs/knowledge/run-pricing-first-writer.md +43 -0
  605. package/docs/knowledge/schedule-claim-transaction.md +41 -0
  606. package/docs/knowledge/secret-entry-replica-contract.md +52 -0
  607. package/docs/knowledge/terminal-run-first-writer.md +41 -0
  608. package/docs/knowledge/transactional-agent-authorization.md +52 -0
  609. package/docs/knowledge/viewer-notify-write-isolation.md +30 -0
  610. package/docs/knowledge/warm-input-publication.md +30 -0
  611. package/docs/knowledge/workspace-staging-failure.md +54 -0
  612. package/docs/knowledge.md +54 -14
  613. package/docs/mcp-servers.md +254 -0
  614. package/docs/native-sandbox.md +143 -31
  615. package/docs/observability.md +4 -0
  616. package/docs/security-deployment.md +139 -29
  617. package/docs/slack-notifications.md +35 -19
  618. package/docs/tool-secrets.md +45 -10
  619. package/docs/viewer-api.md +6 -0
  620. package/docs/viewer-api.openapi.yml +1 -1
  621. package/help/admin-viewer.md +28 -2
  622. package/help/agent-recipes.md +33 -2
  623. package/help/architecture-agent.md +11 -6
  624. package/help/brokered-secrets.md +3 -0
  625. package/help/build-claude-code-image.md +1 -1
  626. package/help/build-worker-image.md +3 -1
  627. package/help/builder-agent.md +19 -6
  628. package/help/claude-code-run-pane.md +40 -0
  629. package/help/code-review-agents.md +177 -6
  630. package/help/coding-packages.md +156 -6
  631. package/help/cost-attribution.md +9 -5
  632. package/help/creating-agents.md +14 -1
  633. package/help/dependency-cache.md +143 -0
  634. package/help/deploy-gke.md +15 -8
  635. package/help/errors/budget-exhausted.md +187 -0
  636. package/help/errors/budget-group-exhausted.md +5 -1
  637. package/help/errors/coding-mcp-pins-unavailable.md +35 -0
  638. package/help/errors/coding-stream-quota-exceeded.md +33 -0
  639. package/help/errors/coding-workspace-disk-exceeds-limit.md +39 -0
  640. package/help/errors/continuation-closed.md +9 -12
  641. package/help/errors/database-auth-invalid.md +1 -1
  642. package/help/errors/delegation-conflict.md +24 -0
  643. package/help/errors/file-list-truncated.md +45 -0
  644. package/help/errors/full-read-required.md +49 -0
  645. package/help/errors/github-account-not-allowed.md +4 -0
  646. package/help/errors/graph-incomplete.md +43 -0
  647. package/help/errors/graph-limit.md +43 -0
  648. package/help/errors/host-provider-error.md +40 -0
  649. package/help/errors/host-tool-failed.md +36 -0
  650. package/help/errors/mcp-call-limit.md +22 -0
  651. package/help/errors/mcp-call-timeout.md +22 -0
  652. package/help/errors/mcp-server-auth-failed.md +22 -0
  653. package/help/errors/mcp-server-credentials-unavailable.md +45 -0
  654. package/help/errors/mcp-server-error.md +28 -0
  655. package/help/errors/mcp-server-unreachable.md +22 -0
  656. package/help/errors/mcp-servers-off.md +21 -0
  657. package/help/errors/mcp-tool-not-allowed.md +26 -0
  658. package/help/errors/mcp-tool-removed.md +22 -0
  659. package/help/errors/mcp-unavailable.md +25 -0
  660. package/help/errors/merge-conflict.md +49 -0
  661. package/help/errors/native-sandbox-capacity.md +1 -1
  662. package/help/errors/native-sandbox-deadline-exceeded.md +1 -1
  663. package/help/errors/native-sandbox-docker-failed.md +1 -1
  664. package/help/errors/native-sandbox-gateway-error.md +20 -0
  665. package/help/errors/native-sandbox-gateway-unavailable.md +1 -1
  666. package/help/errors/native-sandbox-image-not-pinned.md +1 -1
  667. package/help/errors/native-sandbox-isolation-mismatch.md +1 -1
  668. package/help/errors/native-sandbox-launch-abandoned.md +21 -0
  669. package/help/errors/native-sandbox-network-unenforced.md +1 -1
  670. package/help/errors/native-sandbox-requires-catalog.md +1 -1
  671. package/help/errors/native-sandbox-unavailable.md +1 -1
  672. package/help/errors/native-sandbox-warm-delivery-failed.md +1 -1
  673. package/help/errors/native-sandbox-worker-exited.md +4 -3
  674. package/help/errors/native-sandbox-worker-lost.md +1 -1
  675. package/help/errors/native-sandbox-worker-unready.md +1 -1
  676. package/help/errors/package-not-allowed-pending.md +52 -0
  677. package/help/errors/package-not-allowed.md +51 -0
  678. package/help/errors/package-refusals.md +43 -0
  679. package/help/errors/patch-cursor-malformed.md +31 -0
  680. package/help/errors/repository-not-listed.md +36 -0
  681. package/help/errors/repository-required.md +36 -0
  682. package/help/errors/run-lost.md +39 -0
  683. package/help/errors/run-transcript-download.md +44 -0
  684. package/help/errors/secret-in-value.md +69 -0
  685. package/help/errors/target-merge.md +40 -0
  686. package/help/errors/tool-timeout.md +38 -0
  687. package/help/fix-merge-conflicts.md +73 -0
  688. package/help/github-app-setup.md +132 -0
  689. package/help/github.md +22 -0
  690. package/help/host-providers.md +40 -0
  691. package/help/identity-and-access.md +6 -1
  692. package/help/jira.md +23 -6
  693. package/help/knowledge.md +16 -7
  694. package/help/local-repositories.md +1 -1
  695. package/help/low-power-mode.md +7 -1
  696. package/help/mcp-servers.md +73 -0
  697. package/help/mcp.md +7 -2
  698. package/help/merge-order-check.md +5 -0
  699. package/help/native-capabilities.md +55 -1
  700. package/help/native-sandbox.md +16 -2
  701. package/help/operating-agents.md +21 -0
  702. package/help/package-audit-mode.md +91 -0
  703. package/help/related-pull-requests.md +17 -10
  704. package/help/repo-instructions-and-skills.md +3 -1
  705. package/help/resume-runs-locally.md +134 -0
  706. package/help/review-fix-rounds.md +33 -1
  707. package/help/sharing-agents.md +59 -0
  708. package/help/slack-notifications.md +19 -5
  709. package/help/status-comments.md +31 -0
  710. package/help/stop-run.md +58 -0
  711. package/help/troubleshooting/budgets.md +28 -0
  712. package/help/troubleshooting/coding-workers.md +86 -7
  713. package/help/troubleshooting/debugging-agent-runs.md +29 -0
  714. package/package.json +18 -10
  715. package/prisma/migrations/20261016000000_registry_approved_accepted_advisories/migration.sql +3 -0
  716. package/prisma/migrations/20261016000001_mark_ready_when_passing/migration.sql +3 -0
  717. package/prisma/migrations/20261017000000_debug_trace_max_mb/migration.sql +4 -0
  718. package/prisma/migrations/20261018000000_dependency_cache/migration.sql +53 -0
  719. package/prisma/migrations/20261019000000_claude_max_output_tokens/migration.sql +5 -0
  720. package/prisma/migrations/20261021000000_pr_description_hash/migration.sql +4 -0
  721. package/prisma/migrations/20261022000000_dependency_snapshot_validation_metadata/migration.sql +4 -0
  722. package/prisma/migrations/20261023000000_package_audit_mode/migration.sql +27 -0
  723. package/prisma/migrations/20261024000000_budget_events/migration.sql +26 -0
  724. package/prisma/migrations/20261025000000_cache_aware_holds/migration.sql +9 -0
  725. package/prisma/migrations/20261026000000_budget_overrun/migration.sql +4 -0
  726. package/prisma/migrations/20261027000000_coding_run_request/migration.sql +1 -0
  727. package/prisma/migrations/20261028000000_native_gateway_review_reads/migration.sql +6 -0
  728. package/prisma/migrations/20261029000000_lead_survives_deploy/migration.sql +6 -0
  729. package/prisma/migrations/20261030000000_local_review_covered_sha/migration.sql +2 -0
  730. package/prisma/migrations/20261031000000_coding_run_budget_limit/migration.sql +9 -0
  731. package/prisma/migrations/20261101000000_run_transcripts/migration.sql +22 -0
  732. package/prisma/migrations/20261102000000_host_providers/migration.sql +56 -0
  733. package/prisma/migrations/20261104000000_coding_agent_repository_list/migration.sql +5 -0
  734. package/prisma/migrations/20261106000000_tool_timeout/migration.sql +4 -0
  735. package/prisma/migrations/20261107000000_mcp_servers/migration.sql +33 -0
  736. package/prisma/migrations/20261108000000_coding_mcp/migration.sql +5 -0
  737. package/prisma/migrations/20261109000000_task_run_index/migration.sql +3 -0
  738. package/prisma/schema.prisma +468 -109
  739. package/scripts/git-askpass.sh +1 -1
  740. package/dist/sandbox/generated/zod-to-json-schema.bundle.js +0 -5
@@ -1,8 +1,8 @@
1
1
  # Installing packages in coding runs
2
2
 
3
3
  A coding agent's worker has no direct network access — it can reach only the
4
- coding proxy. Registry mode lets it run `npm install` and `pip install`
5
- anyway, by routing those requests through the proxy to a per-agent allowlist,
4
+ coding proxy. Registry mode lets it run `npm install` (or pnpm or yarn) and
5
+ `pip install` anyway, by routing those requests through the proxy to a per-agent allowlist,
6
6
  with supply-chain safeguards and a full record of what was fetched.
7
7
 
8
8
  **Registry mode works for both providers.** Claude Code runs every shell
@@ -25,10 +25,11 @@ one. The toolchain picks the tool-runner image: `node` uses
25
25
  pytest and ruff, the same packages as the Codex `node-python` worker). As with
26
26
  Codex, pip installs go into a virtual environment
27
27
  (`python -m venv --system-site-packages .venv`), and pip there reaches only the
28
- registry. The npm lockfile check
29
- (the shim on `PATH` that plans `npm ci`/`npm install` against the proxy
30
- before it runs; see "Lockfile installs: verified, then approved exactly"
31
- below) applies there exactly as it does for Codex. Before a Claude Code run's
28
+ registry. The lockfile check
29
+ (the shim on `PATH` that plans `npm ci`/`npm install`, and the pnpm and yarn
30
+ installs, against the proxy before they run; see "Lockfile installs:
31
+ verified, then approved exactly" below) applies there exactly as it does for
32
+ Codex. Before a Claude Code run's
32
33
  changes are committed, the control plane rewrites any collected lockfile's
33
34
  proxy URLs back to the ecosystem's real public registry URL (for example
34
35
  `https://registry.npmjs.org/...`), so a lockfile in the resulting pull
@@ -52,8 +53,9 @@ request never names the internal proxy.
52
53
  packages), and the registry for the project-level npm/PyPI packages your
53
54
  agent adds during the run.
54
55
 
55
- npm and pip in every coding run are always pointed at the proxy — this is not
56
- optional per run. If an agent has no allowlist entries for an ecosystem (the
56
+ npm, pnpm, yarn, pip and uv in every coding run are always pointed at the proxy —
57
+ this is not optional per run (see [pnpm and yarn](#pnpm-and-yarn) for how
58
+ each client is configured). If an agent has no allowlist entries for an ecosystem (the
57
59
  default for a new agent), an install in that ecosystem gets a clear refusal
58
60
  (`403 wardby_package_not_allowed`) rather than silently failing or reaching
59
61
  the real registry.
@@ -104,7 +106,11 @@ default). Only _top-level_ entries need to be on the allowlist — once a
104
106
  version is approved, the proxy reads its dependency graph from registry
105
107
  metadata and grows the run's allowance automatically as npm or pip requests
106
108
  each dependency's metadata, so you don't have to enumerate transitive
107
- dependencies yourself.
109
+ dependencies yourself. A package the project adds as a new _top-level_
110
+ dependency, though, must be on the allowlist itself (or already in the
111
+ lockfile the run verified), even if some allowlisted package's dependency
112
+ graph contains it: see
113
+ [Metadata requests are answered at once](#metadata-requests-are-answered-at-once).
108
114
 
109
115
  ### PyPI extras
110
116
 
@@ -179,6 +185,54 @@ entries first.
179
185
  Every safeguard below (release age, advisories, wheels only, the record of
180
186
  what was fetched) applies to packages reached through an extra.
181
187
 
188
+ ### Accepting advisories
189
+
190
+ A version withheld only because of a **high** or **critical** advisory can be
191
+ served for one agent once an approver accepts that advisory. List the advisory
192
+ ids in the coding profile's `packagePolicy.acceptedAdvisories` with
193
+ `create_agent` or `update_agent` (up to 32 unique ids; needs `packages:approve`
194
+ or `agents:admin`, like any other package-policy change):
195
+
196
+ ```json
197
+ {
198
+ "codingProfile": {
199
+ "packagePolicy": {
200
+ "minReleaseAgeDays": 3,
201
+ "acceptedAdvisories": ["GHSA-xxxx-xxxx-xxxx"]
202
+ }
203
+ }
204
+ }
205
+ ```
206
+
207
+ `update_agent` replaces `packagePolicy` as a whole, so always send the full
208
+ object: leaving out `minReleaseAgeDays` resets it to the default of 3. To
209
+ remove a waiver, send the whole object again without that id.
210
+
211
+ Upgrade the coding proxy together with the control plane before setting
212
+ `acceptedAdvisories`: an older proxy rejects the unknown policy key and fails
213
+ package requests for that run (it fails closed).
214
+
215
+ - Ids are matched exactly as OSV prints them (case-sensitive, for example
216
+ `GHSA-…`, `CVE-…`, `PYSEC-…`) against an advisory's id and its aliases, so
217
+ accepting a `CVE-…` id also waives the `GHSA-…` advisory that aliases it.
218
+ - The registry serves a version only when **every** advisory blocking it is
219
+ accepted. Otherwise it is still refused with `wardby_version_filtered`, and
220
+ the refusal names only the advisories still blocking.
221
+ - **Malware is never waivable.** `MAL-…` ids are rejected when you set the
222
+ policy, and an advisory that is malware (a `MAL-…` id or alias, or CWE-506)
223
+ is still withheld even if its id is listed.
224
+ - Each served version that needed a waiver is recorded with the reason
225
+ `accepted advisories: <ids>`. `get_run` shows `acceptedAdvisories` per
226
+ package, and the pull request body gets an **Accepted advisories** section
227
+ listing `name@version: ids` (only when a waiver was used).
228
+ - The policy is fixed when a run starts; a change applies to the next run.
229
+
230
+ To find the id, take the package named in the refusal and look it up at
231
+ `https://osv.dev/list?q=<package>`, or run `npm audit`. Accept an advisory only
232
+ after reading it and judging whether the vulnerable code is actually exposed
233
+ (for example, a dependency used only at build time). Remove the entry once a
234
+ fixed version ships, so the waiver does not outlive its reason.
235
+
182
236
  ### The quickstart offers a repository's declared packages
183
237
 
184
238
  The quickstart's coding step (see
@@ -213,9 +267,16 @@ the driver image puts a small shim first on the agent's `PATH`
213
267
  proxy's `POST /registry/npm/-/plan` (with the run's registry token), prints a
214
268
  one-line summary and any refusals to stderr, and then runs the real npm with
215
269
  the same arguments. Anything else runs npm directly, and a plan that fails
216
- still runs npm: the install then goes through the usual checks below. An npm
217
- started by another npm (a lifecycle script's) doesn't plan again; its
218
- installs also go through the usual checks.
270
+ still runs npm: the install then goes through the usual checks below, and
271
+ for the rest of the run metadata requests the proxy can't otherwise answer
272
+ fall back to the slower dependency-graph walk (see
273
+ [Metadata requests are answered at once](#metadata-requests-are-answered-at-once)).
274
+ A lockfileVersion 1 lockfile can't be planned (`400
275
+ wardby_lockfile_unsupported`), so regenerate it with a current npm before the
276
+ run to avoid that fallback. An npm started by another npm (a lifecycle
277
+ script's) doesn't plan again; its installs also go through the usual
278
+ checks. Python lockfiles are verified the same way, with differences listed
279
+ under [Python installers](#python-installers-pip-uv-and-poetry).
219
280
 
220
281
  The proxy trusts nothing the lockfile claims. For lockfileVersion 2 or 3
221
282
  (version 1 is refused with `400 wardby_lockfile_unsupported`; regenerate it
@@ -231,11 +292,20 @@ with npm 7 or later):
231
292
  nearest enclosing package's `node_modules/<name>`), and counts only when
232
293
  that entry is the declared package at a version inside the declared range
233
294
  (strict semver, as the walk matches).
234
- - **Reachability.** It starts from the project's own dependencies (and
235
- workspaces') that are allowlist entries, at a version inside the
236
- allowlisted range; a scope wildcard such as `@heroui/*` counts here, since
237
- the lockfile names the exact package. From there it follows only verified
238
- edges. An entry nothing reaches is refused with `wardby_package_not_allowed`.
295
+ - **Reachability.** It starts from every lockfile entry that is an allowlist
296
+ entry, at a version inside the allowlisted range, wherever it sits in the
297
+ lockfile and whether or not a project declares it; a scope wildcard such as
298
+ `@heroui/*` counts here, since the lockfile names the exact package. From
299
+ there it follows only verified edges, each inside the declared range. An
300
+ entry nothing reaches is refused with `wardby_package_not_allowed`.
301
+ - **Overrides.** An npm `overrides` entry can install a version that its parent's
302
+ declared range doesn't allow, so the plan can't reach it through that
303
+ parent. It's approved, with its own dependencies, when the overridden
304
+ package's name is on the allowlist (and the version is inside the
305
+ allowlisted range); otherwise it's refused with
306
+ `wardby_package_not_allowed`. Allowlist the target of each override your
307
+ repository uses. Release age, advisories and integrity are checked as for
308
+ any other entry.
239
309
  - **Release age and advisories.** Each version's publish time (read once by
240
310
  streaming the package's full document for its `time` field, never holding
241
311
  it) must be older than `minReleaseAgeDays`, and one OSV `querybatch`
@@ -250,9 +320,13 @@ with npm 7 or later):
250
320
  its parent's tarball and needs nothing.
251
321
 
252
322
  A refused entry refuses only itself and whatever is reachable only through
253
- it; everything else is approved, as exact `name@version` pairs for the run.
323
+ it; everything else is approved, as exact `name@version` pairs for the run,
324
+ and each approved package name is allowed for the run, so npm can read its
325
+ metadata again (as `npm install <new-package>` does for the packages already in
326
+ the lockfile).
254
327
  The response is `{ "approved": <count>, "refused": [{ "name", "version",
255
- "code", "reason" }] }`, and each refusal is recorded like any other (within
328
+ "code", "reason" }] }` (plus `"audited": <count>` in a
329
+ [package audit mode](#package-audit-mode) run), and each refusal is recorded like any other (within
256
330
  the 500-per-run cap). A tarball request for an approved `name@version` is then served directly,
257
331
  checked against the approved integrity, with no graph walk.
258
332
 
@@ -271,8 +345,10 @@ isn't used to answer downloads: the same version reached another way (an
271
345
  a hostile lockfile entry can't block a package by claiming its name. Neither
272
346
  does a version the registry couldn't be read for, nor one the plan never
273
347
  mentioned (a package added with `npm install <new-package>`, an install
274
- without a lockfile): they go through the allowlist and the dependency-graph
275
- walk below.
348
+ without a lockfile): their metadata requests go through the allowlist and the
349
+ run's allowances (see
350
+ [Metadata requests are answered at once](#metadata-requests-are-answered-at-once)),
351
+ and their downloads also through the dependency-graph walk below.
276
352
 
277
353
  The per-version facts (integrity, publish time, dependencies, download URL)
278
354
  never change, so they are stored in the database and reused by every later
@@ -285,22 +361,164 @@ one of two plan slots before it reads the lockfile at all, so an
285
361
  unauthenticated or queued plan holds none of it; a run may run one plan at a
286
362
  time and make `REGISTRY_PLAN_MAX_PER_RUN` in all (`429 wardby_plan_limit`).
287
363
  It only reads the registry for entries it reaches, so a lockfile full of
288
- unrelated packages costs nothing upstream. Measured
289
- against real npm and OSV with a 0-day release age, the knock-knock `web/`
290
- lockfile (216 entries: React 19, HeroUI 3, Vite 8, Vitest 5, jsdom) was
291
- verified in 9.5 s cold and 0.7 s with stored facts, and its whole `npm ci`
292
- took 12 s cold and 3 s warm, with the proxy at 183 MiB peak RSS (30 MiB
293
- heap). At the default 3 days the same plan
294
- took 8 s and refused 53 entries: vite 8.3.1 and vitest 5.0.2 were days old,
295
- and those two are answered from the plan, but the 51 entries refused only as
296
- unreachable through them are lockfile-dependent verdicts, so npm's requests
297
- for them went to the graph walk, and the failing `npm ci` took 547 s with the
298
- proxy at 1604 MiB peak RSS.
364
+ unrelated packages costs nothing upstream. As a rough guide, a web-app
365
+ lockfile of about 200 entries verifies in seconds the first time and in under a
366
+ second once its package facts are stored. When the release-age policy refuses
367
+ some entries, packages that are refused only because they're reachable through
368
+ those entries can't be answered from the plan, so npm's requests for them fall
369
+ back to the dependency-graph walk below (after a plan with any refusal, this
370
+ applies to metadata requests as well as downloads). That can make a failing install take
371
+ minutes and use much more proxy memory; fix the refusals (wait out the release
372
+ age or adjust the agent's policy) rather than retrying.
373
+
374
+ ### pnpm and yarn
375
+
376
+ pnpm and yarn install from the same npm allowlist and the same registry
377
+ route as npm, with the same safeguards. The worker configures each of them
378
+ to reach only the proxy, with the run's registry token:
379
+
380
+ | Client | How the worker points it at the proxy |
381
+ | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
382
+ | npm | `npm_config_registry`, and an npmrc (`npm_config_userconfig`) holding the token. Install scripts, the update notifier, audit and fund are off. |
383
+ | pnpm | npm's settings above (pnpm reads the same ones), so install scripts and the update check are off too. The shim puts its store and cache under the workspace's `.cache` (`npm_config_store_dir` and `npm_config_cache_dir`, set for pnpm alone: npm warns about settings it doesn't know). |
384
+ | yarn classic (1.x) | npm's registry and npmrc. yarn classic sends the token only with `always-auth`, which npm warns about, so the shim sets `npm_config_always_auth` for yarn alone. Install scripts are off with `YARN_IGNORE_SCRIPTS` (yarn classic ignores `npm_config_ignore_scripts`); the shim puts its cache under `.cache` (`npm_config_cache_folder`, for yarn alone). |
385
+ | yarn Berry (2 or later) | Berry reads none of npm's settings: `YARN_NPM_REGISTRY_SERVER`, `YARN_NPM_AUTH_TOKEN` with `YARN_NPM_ALWAYS_AUTH`, and the proxy's host in `YARN_UNSAFE_HTTP_WHITELIST` when the proxy is plain HTTP (Berry refuses an `http:` registry otherwise). Telemetry and scripts are off (`YARN_ENABLE_TELEMETRY=0`, `YARN_ENABLE_SCRIPTS=false`); its global folder is under `.cache`. |
386
+ | corepack | `COREPACK_NPM_REGISTRY` and `COREPACK_NPM_TOKEN`, so it downloads a pinned pnpm or yarn release from the proxy; no download prompt, no `packageManager` pinning, no lookup of the latest release. |
387
+
388
+ Berry's per-project `cacheFolder` is left alone, so a repository that
389
+ commits its cache (zero-install) keeps using it. A service's `testEnv` can't
390
+ set `YARN_*`, `PNPM_*` or `COREPACK_*` variables.
391
+
392
+ **Getting pnpm or yarn Berry into the run.** The stock worker images ship
393
+ npm, yarn classic and corepack, not pnpm or yarn Berry. The shim runs
394
+ `pnpm`, and `yarn` in a project whose `package.json` (or, for a workspace
395
+ package, the workspace root's) pins yarn 2 or later
396
+ (`"packageManager": "yarn@4.x"`), through corepack, which downloads that
397
+ release through the registry like any other package: allowlist `pnpm` for
398
+ pnpm, `@yarnpkg/cli-dist` for yarn 2 or later, or `yarn` for a pinned yarn
399
+ 1.x other than the image's. The release-age policy applies to it too, so a
400
+ release pinned the day it shipped is refused until it is old enough.
401
+ corepack reads one version's document at
402
+ `/registry/npm/<name>/<version>`, which the proxy serves with the same checks
403
+ as the package's whole document. A repository that commits its yarn release
404
+ (`yarnPath` in `.yarnrc.yml`) needs none of this. Alternatively, bake the
405
+ tool into a custom worker image.
406
+
407
+ **Lockfile plans.** The shim acts as `pnpm` and `yarn` too: before
408
+ `pnpm install`/`i`/`add` it sends `pnpm-lock.yaml`, and before a bare
409
+ `yarn`, `yarn install`, `add`, `remove`, `upgrade`, `upgrade-interactive`
410
+ or `up` (also as `yarn workspace <name> <command>`) it sends `yarn.lock` (the nearest one
411
+ from the project up, so a workspace root's lockfile counts), to the same
412
+ `POST /registry/npm/-/plan`, which tells the formats apart by content.
413
+ Supported:
414
+
415
+ | Lockfile | Versions | Integrity checked against the registry | How a dependency is matched to its entry |
416
+ | ---------------------------- | ------------------------------------------ | -------------------------------------- | --------------------------------------------------------------------------------- |
417
+ | `package-lock.json` | lockfileVersion 2 and 3 | `integrity` | npm's: the nearest enclosing `node_modules/<name>` |
418
+ | `pnpm-lock.yaml` | lockfileVersion 6 (pnpm 8) and 9 (pnpm 9+) | `resolution.integrity` | the reference the parent's entry records (`ms: 2.1.3`, an alias's `name@version`) |
419
+ | `yarn.lock`, yarn classic | `# yarn lockfile v1` | `integrity` | the parent's `name@range` descriptor |
420
+ | `yarn.lock`, yarn 2 or later | `__metadata.version` 4 or later | none: see below | the parent's `name@range` descriptor |
421
+
422
+ Every other check is the one described above: reachability from allowlist
423
+ entries over dependencies the registry declares, inside their declared
424
+ ranges, release age, advisories, the plan's refusal records and allowances,
425
+ and the fallback to the walk after a failed or partial plan. Workspace
426
+ packages and `link:`, `portal:`, `file:`, `exec:` and `patch:` entries are
427
+ local and skipped (a yarn patch applies to an npm entry the lockfile lists
428
+ on its own); git and URL entries are refused with
429
+ `wardby_lockfile_entry_unsupported`. pnpm's older `lockfileVersion` 5.x
430
+ (pnpm 7) is refused with `400 wardby_lockfile_unsupported`: regenerate it
431
+ with pnpm 8 or later. The YAML lockfiles are read as plain strings, and one
432
+ using YAML anchors or aliases (which pnpm and yarn never write) is refused.
433
+ The proxy reads a `pnpm-lock.yaml` or `yarn.lock` in a separate worker
434
+ thread, never on the thread that serves every run's registry and model
435
+ traffic. The read is bounded to 15 seconds and to 384 MiB of memory for
436
+ every 5000 entries `REGISTRY_PLAN_MAX_ENTRIES` allows (384 MiB at the
437
+ default). A lockfile not read in time is answered with
438
+ `503 wardby_plan_incomplete`, and one that needs more memory with
439
+ `413 wardby_lockfile_too_large`; a retry of the same lockfile fails the same
440
+ way, and the install falls back to the walk, as for any failed plan. A
441
+ reader that fails for any other reason is the proxy's own failure: it is
442
+ logged (`registry.failed`) and answered with `502 wardby_upstream_error`.
443
+ A realistic lockfile at the default entry bound (a 3.4 MiB
444
+ `pnpm-lock.yaml`) reads in under a second using about 160 MiB.
445
+
446
+ **Proxy memory.** Each lockfile plan being read can use that bound on top of
447
+ the proxy's own memory, and two plans are read at once, so allow about
448
+ 800 MiB of headroom for lockfile reads at the default settings (more if you
449
+ raise `REGISTRY_PLAN_MAX_ENTRIES`).
450
+
451
+ **yarn Berry's `checksum` is not npm's integrity.** It is yarn's own hash
452
+ of its cache archive, so the plan can't compare it with the registry's.
453
+ A Berry plan approves exact `name@version` pairs, and each download is then
454
+ checked against the registry's own integrity for that version, as every
455
+ download is. A tampered tarball is still refused (`wardby_integrity_mismatch`);
456
+ what a Berry plan can't catch is a lockfile entry whose checksum was edited,
457
+ which yarn itself rejects at install.
458
+
459
+ **yarn Berry adds `node-gyp`.** Berry gives every package with an install
460
+ script (such as `fsevents`) an implicit dependency on `node-gyp`, which no
461
+ registry metadata declares. Allowlist `node-gyp` for a Berry project whose
462
+ lockfile has it, or the install is refused at `node-gyp`.
463
+
464
+ **yarn classic downloads from the URLs in yarn.lock**, never from its
465
+ configured registry, so a frozen install can't reach the proxy on its own.
466
+ For the install, the shim points the lockfile's `registry.yarnpkg.com` and
467
+ `registry.npmjs.org` URLs at the proxy, and afterwards puts the file back
468
+ byte for byte; if yarn rewrote the lockfile (`yarn add`), each entry's
469
+ proxy URL goes back to the host that entry had (a new entry gets the host
470
+ most of the lockfile uses). The shim does this for every command above. A yarn classic install that
471
+ bypasses the shim (the real `yarn` called by its full path) gets neither this
472
+ nor the token, and fails.
473
+
474
+ **What a run commits.** The proxy serves each download at the registry's
475
+ usual path (`<proxy>/registry/npm/<name>/-/<name>-<version>.tgz`), and pnpm
476
+ and yarn 2 or later record a download URL only when it isn't the usual one,
477
+ so their lockfiles, yarn's `.pnp.cjs` and install state, and the names in
478
+ `.yarn/cache` name nothing of the proxy: a zero-install repository can
479
+ commit them as they are. npm and yarn classic record every download URL, so
480
+ before a run's changes are collected Wardby rewrites their `resolved` URLs
481
+ to public registry URLs (yarn classic's on `registry.yarnpkg.com`). It
482
+ also drops a pnpm `tarball` or a yarn 2+ `__archiveUrl` that still names the
483
+ proxy (from an older Wardby). A committed lockfile never names the proxy or
484
+ the token.
485
+
486
+ ### Metadata requests are answered at once
487
+
488
+ When a package manager resolves a package, it reads the package's metadata
489
+ (npm's packument) first. The proxy serves it if the name is allowlisted, was
490
+ declared as a dependency (regular, optional or peer) by a package whose metadata
491
+ was already served in this run, or was approved by the run's lockfile plan.
492
+ Package managers resolve top-down, reading a parent's metadata before asking for
493
+ its dependencies, so every genuine dependency is already allowed when it is
494
+ requested. Any other name is refused at once with
495
+ `403 wardby_package_not_allowed`, with no dependency-graph walk.
496
+
497
+ The exception is a run whose lockfile plan failed (any error answer, such as
498
+ an unsupported lockfileVersion 1, `wardby_lockfile_too_large`,
499
+ `wardby_plan_incomplete`, `wardby_plan_limit` or `wardby_plan_in_progress`)
500
+ or refused an entry. Then some of the lockfile's packages may have no
501
+ allowance, and npm may read their metadata in no particular order (for
502
+ example, when it upgrades a lockfileVersion 1 lockfile or re-reads the
503
+ lockfile's packages on `npm install <new-package>`). For the rest of that run
504
+ and ecosystem, a metadata request for an unknown name goes through the
505
+ dependency-graph walk below, exactly as a download does, even if a later plan
506
+ succeeds. The proxy keeps this per run in memory, like the walk itself.
507
+
508
+ As a consequence, a **new top-level package must be allowlisted** (or already
509
+ be in the lockfile the run's plan approved), even if some allowlisted
510
+ package's dependency graph contains it. For example, if `framework` is
511
+ allowlisted and depends on `helper`, `npm install framework` installs `helper`
512
+ as its dependency, but `npm install helper` (adding it as a top-level
513
+ dependency) is refused until `helper` is allowlisted too.
299
514
 
300
515
  ### Lockfile installs without a plan: the dependency-graph walk
301
516
 
302
- When a tarball request names a package the run hasn't approved or seen yet,
303
- the proxy resolves the approved dependency graph on demand. The graph is
517
+ When a tarball request names a package the run hasn't approved or seen yet
518
+ (a lockfile install requests tarballs without first reading each parent's
519
+ metadata), the proxy resolves the approved dependency graph on demand. A
520
+ metadata request starts this walk only after a lockfile plan of the run failed
521
+ or refused an entry (see above). The graph is
304
522
  range-aware:
305
523
 
306
524
  - It starts from the allowlisted packages, at their kept versions: those
@@ -353,14 +571,45 @@ try again" error (`502 wardby_upstream_error`, or
353
571
  `503 wardby_audit_unavailable` if it was the audit), not with
354
572
  `wardby_package_not_allowed`.
355
573
 
356
- Likewise, if the walk is cut short before it reaches the requested package,
357
- the package isn't proven absent, so the answer is never
358
- `wardby_package_not_allowed`. After `REGISTRY_GRAPH_TIMEOUT_MS` the answer is
359
- `503 wardby_graph_incomplete`: the next request resumes the walk where it
360
- stopped (npm retries a 5xx on its own), so a retry can succeed. After
361
- `REGISTRY_MAX_GRAPH_PACKAGES` trips, the bound is permanent for the run, so
362
- the answer is a definitive `403 wardby_graph_limit` that clients don't retry:
363
- allowlist the package directly or raise the bound.
574
+ The walk expands the requested name first, so a request is answered as soon as
575
+ the walk reaches it. It groups all of a package's ranges together, so each
576
+ package is expanded once however many dependents ask for it, and it reuses
577
+ already expanded packages through a bounded memo (entries are per run, kept for 15
578
+ minutes; its 32 MiB budget is shared by the whole proxy process). Because it follows every kept version inside each range, plus
579
+ optional and peer dependencies, its graph is usually several times larger than
580
+ the graph of one install. The walk runs only while a request waits for it; its
581
+ state is per run, so it resumes within the run and starts again in a new run.
582
+ Each time a walk ends, the proxy logs `registry.graph_walk` with how far it
583
+ got. (A run in [package audit mode](#package-audit-mode) never walks; it logs
584
+ `registry.audited` for each package it installs outside the allowlist.)
585
+
586
+ A request the walk could not settle gets one of four answers (a metadata
587
+ request gets the last three only after a failed or partial lockfile plan,
588
+ since otherwise it never walks):
589
+
590
+ - `403 wardby_package_not_allowed`: final. The walk finished and the package is
591
+ not on the allowlist, or is only reachable through a refused package. The
592
+ agent's owner must allowlist it.
593
+ - `403 wardby_package_not_allowed_pending`: the walk stopped (at
594
+ `REGISTRY_GRAPH_TIMEOUT_MS`) before reaching the name, so it may still be
595
+ reachable. The message shows how many packages were walked and how many were
596
+ queued. It is not transient: the agent should stop, because retrying rarely
597
+ helps, and the owner should allowlist the package directly.
598
+ - `403 wardby_graph_limit`: this run's walk reached `REGISTRY_MAX_GRAPH_PACKAGES`
599
+ (counting package names) before it reached the name, so the name may not be in
600
+ the graph at all. The bound is permanent for the run: the agent's owner can
601
+ allowlist the package directly, or the operator can raise the limit.
602
+ - `503 wardby_graph_incomplete`: only for a name that is still being checked.
603
+ The walk reached the name (an expanded version declares it) but timed out
604
+ (`REGISTRY_GRAPH_TIMEOUT_MS`) before the name's own versions were checked,
605
+ usually a slow or failing upstream for that package, or another request's walk
606
+ running out of time. Retry shortly (npm retries a 5xx on its own). When a
607
+ completed walk has failed nodes, the answer is `wardby_upstream_error` or
608
+ `wardby_audit_unavailable` instead.
609
+
610
+ Large frameworks (for example Expo) can exceed the graph limit, because the
611
+ walk follows every in-range version plus optional and peer dependencies.
612
+ Allowlist the packages the project needs directly.
364
613
 
365
614
  The walk reads full npm packuments (the largest are tens of MB) but keeps
366
615
  only what it needs from each: per version, its dependency specs, publish
@@ -374,6 +623,533 @@ why the GKE overlay gives the proxy 3Gi. A lockfile the shim verified
374
623
  avoids the walk for every version the plan approved, and for every version it
375
624
  refused for a reason about the version itself (too new, an advisory, ...).
376
625
 
626
+ ## Package audit mode
627
+
628
+ From Wardby 0.7.0, an agent's owner can switch its coding runs' registry into
629
+ package audit mode for a limited time. A package outside the allowlist is then
630
+ installed instead of refused, and recorded so it can be approved afterwards.
631
+ Use it to find out which packages a task actually needs (a new framework, a
632
+ large toolchain) without a round of refusals for each one, then approve them
633
+ and let the mode lapse.
634
+
635
+ ### What each mode does
636
+
637
+ "Outside the allowlist" means a name that is neither an allowlist entry nor
638
+ allowed for the run as a dependency of one.
639
+
640
+ | Situation | Off (default) | `audit` | `full` |
641
+ | ------------------------------------------------------------ | ---------------------------------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
642
+ | Metadata for a package outside the allowlist | `403 wardby_package_not_allowed` | served; the package is recorded as `top-level` (`unknown` after a failed or partial lockfile plan) | same as `audit` |
643
+ | Download of a package outside the allowlist | lockfile plan, graph walk, refusal | served with no walk; recorded as `unknown` unless its metadata or a plan said more | same as `audit` |
644
+ | A dependency an audited package declares | not applicable | allowed for the run; recorded as `transitive`, with the top-level audited package that brought it (`via`) | same as `audit` |
645
+ | A version outside an allowlist entry's own range (`foo@^1`) | `wardby_version_filtered` | unchanged: an entry's range is the owner's explicit restriction | unchanged |
646
+ | A high or critical advisory, not accepted | withheld | withheld | served, and every blocking advisory recorded (also for an allowlisted package) |
647
+ | An advisory in `packagePolicy.acceptedAdvisories` | waived, recorded on the served row | same | same |
648
+ | Malware (`MAL-…`, an alias of one, CWE-506) | withheld | withheld | withheld |
649
+ | Release age (`minReleaseAgeDays`, per file) | enforced | enforced | enforced |
650
+ | OSV unreachable | `503 wardby_audit_unavailable` | same | same |
651
+ | Source distributions, size and file limits, integrity, hosts | enforced | enforced | enforced |
652
+
653
+ No graph walk runs in an audit-mode run, so `wardby_graph_limit`,
654
+ `wardby_graph_incomplete` and `wardby_package_not_allowed_pending` can't
655
+ occur there.
656
+
657
+ ### Turning it on
658
+
659
+ The agent's owner sets it with `update_agent`. It needs `packages:approve` or
660
+ `agents:admin` (the gate for `packageAllowlist`), and `create_agent` doesn't
661
+ accept it:
662
+
663
+ ```json
664
+ { "id": "<agent id>", "codingProfile": { "packageAuditMode": "audit", "packageAuditMinutes": 1440 } }
665
+ ```
666
+
667
+ | Input | Effect |
668
+ | --------------------------------------------- | -------------------------------------------------------------- |
669
+ | `packageAuditMode: "audit"` or `"full"` | on for 24 hours |
670
+ | …with `packageAuditMinutes: <m>` (1 to 10080) | on for `m` minutes (at most 7 days) |
671
+ | `packageAuditMinutes: <m>` alone | re-arms the stored mode for `m` minutes (an error without one) |
672
+ | `packageAuditMode: null` | off |
673
+
674
+ Each change is logged as `coding.package_audit.set` with the agent, mode,
675
+ end time and who set it. `get_agent` and `update_agent` return
676
+ `codingProfile.packageAuditMode`, `packageAuditUntil` and
677
+ `packageAuditActive`, plus a warning while the window is open. An expired
678
+ window keeps the stored mode but is inactive.
679
+
680
+ A coding run takes the mode when it is dispatched and keeps it for its whole
681
+ life, queued or long-running. A run dispatched just before the window ends
682
+ runs with the mode for up to the agent's `timeoutSec`. Continuations and
683
+ review fix rounds are new dispatches and read the window again.
684
+
685
+ ### What is recorded, and where
686
+
687
+ Each package version served only because of the mode (or, in `full`, served
688
+ despite a blocking advisory) is recorded once per run, when its first file
689
+ finishes downloading. A HEAD request, a metadata request or a refusal records
690
+ nothing. If the record can't be written, the download fails instead of being
691
+ installed unrecorded. The record shows up in:
692
+
693
+ - `get_run`: `packageAuditMode` and `packageAudit` (an empty list when the mode
694
+ was on and nothing was recorded; both absent when it was off).
695
+ - The pull request body: a `[!CAUTION]` banner above the agent's summary,
696
+ **Packages outside the allowlist** (the `top-level` names to approve, each
697
+ with a count of its dependencies), a separate **Relation unknown** list (see
698
+ below), and in `full` mode **Served despite advisories**. When
699
+ the mode was on and nothing was recorded, the banner says so in one line. A
700
+ later run on the same pull request merges the lists across its runs, and the
701
+ banner is kept when the **Packages** section is dropped for size.
702
+ - The run's GitHub or Jira status comment: a short block above the run line,
703
+ with `unknown` names in their own labelled sentence.
704
+ - Slack: a `packages_audited` event for the top-level run (see
705
+ [Slack notifications](slack-notifications.md)).
706
+ - The proxy log: `registry.audited`, one line per version, with the run,
707
+ ecosystem, name, version, size, relation, `via`, mode and advisory ids.
708
+
709
+ ```json
710
+ {
711
+ "packageAuditMode": "full",
712
+ "packageAudit": [
713
+ { "ecosystem": "npm", "name": "vite-plugin-pwa", "version": "1.0.3", "size": 41210, "relation": "top-level" },
714
+ {
715
+ "ecosystem": "npm",
716
+ "name": "workbox-build",
717
+ "version": "7.3.0",
718
+ "size": 90231,
719
+ "relation": "transitive",
720
+ "via": "vite-plugin-pwa"
721
+ },
722
+ {
723
+ "ecosystem": "npm",
724
+ "name": "lodash",
725
+ "version": "4.17.20",
726
+ "size": 1200,
727
+ "relation": "allowed",
728
+ "advisories": [{ "id": "GHSA-35jh-r3h4-6jhm", "severity": "HIGH", "summary": "Command injection in lodash" }]
729
+ }
730
+ ]
731
+ }
732
+ ```
733
+
734
+ `relation` is best effort. `unknown` means the registry couldn't tell how the
735
+ package reached the run (a direct tarball download, or a metadata request
736
+ after a lockfile plan fell back). A dependency that an audited package
737
+ declared first stays recorded as audited even if an allowlisted package
738
+ declares it later. Both errors over-report, never under-report. Because an
739
+ `unknown` package may really be a dependency of an allowlisted package that
740
+ was installed without its parent's metadata, every surface lists `unknown`
741
+ names apart, labelled "Relation unknown — may be a dependency; approve only
742
+ if needed directly", and they aren't approved unless you name them.
743
+
744
+ ### Lockfile plans and the dependency cache
745
+
746
+ In audit mode a lockfile plan (`POST /registry/<ecosystem>/-/plan`) also
747
+ approves the lockfile entries the allowlist doesn't reach, when they pass
748
+ every other check. An entry a lockfile project declares is `top-level`, one
749
+ an audited top-level entry reaches is `transitive`, and anything else is
750
+ `unknown`. The projects are the root and workspaces a `package-lock.json`,
751
+ `pnpm-lock.yaml` or yarn 2+ `yarn.lock` lists. A yarn classic `yarn.lock`
752
+ lists none, so there a package is `top-level` when no other entry depends on
753
+ it at the same range; one that another package also requires at that range
754
+ shows as `transitive` (or `unknown` when nothing audited reaches it). An allowlisted name at a version outside its entry's range is never
755
+ approved this way. The plan's response adds `audited`, the number of
756
+ approvals carrying an audit record, and each download of such an approval is
757
+ recorded like any other audited package.
758
+
759
+ A plan that audited anything never records its lockfile for the
760
+ [dependency cache](#dependency-cache). A restore installs packages without
761
+ downloading them, so they would go unrecorded. The run installs normally
762
+ instead, and no snapshot of that lockfile is saved from it.
763
+
764
+ ### Promoting audited packages
765
+
766
+ Add what a run recorded to the allowlist with `approve_audited_packages`
767
+ (`{ "runId": "<run id>" }`, optionally `"names": [...]`). Without `names` it
768
+ adds the `top-level` names as bare entries to the agent's `packageAllowlist`;
769
+ their dependencies then follow through the allowlist graph. It adds an
770
+ `unknown` name only when `names` lists it, and otherwise returns those names
771
+ as `unknown` for you to judge. For a parent run, each coding sub-run's names go to that sub-run's own
772
+ agent. It needs the same access as `packageAllowlist` (`packages:approve` or
773
+ `agents:admin`, owner only), skips names already allowed, keeps the 256-entry
774
+ limit per ecosystem, and is logged as `coding.package_audit.promoted`. It
775
+ checks every agent before writing any, so a refusal (another owner's agent, a
776
+ full allowlist) changes nothing. It
777
+ doesn't turn the mode off. You can also edit `packageAllowlist` with
778
+ `update_agent` yourself, for example to pin a range.
779
+
780
+ ### Risks
781
+
782
+ Package audit mode weakens the supply-chain guard while it is on:
783
+
784
+ - `audit` keeps the malware, advisory and release-age checks. `full` keeps
785
+ only malware and release age. A confused or prompt-injected agent can
786
+ install a lookalike package that has no advisory yet and is older than the
787
+ release age.
788
+ - npm runs with `--ignore-scripts` and pip installs only wheels, so nothing
789
+ runs at install time, but the run's tests and builds still execute what was
790
+ installed.
791
+ - A scheduled agent can run many times inside one window. Keep windows short,
792
+ turn the mode off once you've approved what you need, and read the caution
793
+ banner on every affected pull request before merging.
794
+
795
+ The mode is never on by default, needs an approver's scope and the agent's
796
+ owner, lasts at most 7 days, is logged when set, and is reported on every
797
+ run it affected.
798
+
799
+ ### Upgrading
800
+
801
+ The coding proxy reads each run's mode from `CodingRun.packageAuditMode` and
802
+ writes `RegistryAuditRecord` rows. The GKE `up.sh` applies these grants from
803
+ `deploy/gke/database-grants.sql` after its migrations. If you manage the proxy
804
+ database role yourself, grant it `SELECT` on `CodingRun.packageAuditMode` and
805
+ `SELECT, INSERT` on `RegistryAuditRecord` before upgrading the proxy.
806
+ Without them, every registry request fails with `502 wardby_upstream_error`
807
+ and the proxy logs `registry.failed` with a permission error.
808
+
809
+ ## Dependency cache
810
+
811
+ Installing a large `node_modules` on every run is slow. With the dependency
812
+ cache on, Wardby prepares an npm project's `node_modules` **before the agent
813
+ starts**, from a snapshot kept in a blob store, and tells the agent not to
814
+ reinstall it. It is off by default and is turned on per agent.
815
+
816
+ ### What it does
817
+
818
+ For each eligible npm project the worker prepares `node_modules` in one of
819
+ these ways, reported per project in [`get_run`](#what-get_run-reports):
820
+
821
+ - **`hit`**: a snapshot for exactly this lockfile exists. It is downloaded,
822
+ verified, and put in place. Nothing is installed. A snapshot that fails to
823
+ download or verify is not used: the project is installed `cold` instead.
824
+ - **`nearest`**: no exact snapshot, but one exists for the same repository,
825
+ folder and worker image with a different lockfile. It is
826
+ restored, `npm install --no-save` brings it to the committed lockfile
827
+ (usually a small change), the committed `package.json` and
828
+ `package-lock.json` are put back byte for byte, and a new snapshot is
829
+ stored.
830
+ - **`cold`**: no usable snapshot. `npm ci` runs and its result is stored.
831
+ - **`skipped`** or **`refused`**: nothing was prepared and the agent installs
832
+ for itself as it always did. A skip never fails the run.
833
+
834
+ Snapshots are keyed by repository, project folder, lockfile hash and worker
835
+ image (the Claude Code tool-runner image for Claude Code runs), all derived on
836
+ the server. Changing the image starts a new key; changing the lockfile falls
837
+ back to `nearest`. The agent's package policy is not part of the key: a
838
+ snapshot holds exactly the lockfile's integrity-checked packages, so agents on
839
+ the same repository share snapshots, and each restore is still gated by the
840
+ restoring agent's own current policy (see
841
+ [A restore needs the plan first](#the-trust-model)).
842
+
843
+ ### The trust model
844
+
845
+ A snapshot holds only what Wardby's own code installed, from packages the
846
+ uploading run's policy approved, before the run's agent existed; and a run restores
847
+ one only after the proxy re-approves the committed lockfile for that run. The
848
+ packages themselves are whatever your allowlist and policy approve: the cache
849
+ doesn't vet their contents beyond what a normal install through the registry
850
+ does. A `nearest` base may come from another agent on the same repository and
851
+ contain packages outside this agent's policy; the delta install prunes them,
852
+ and the consistency check verifies that the tree matches the approved lockfile
853
+ before the agent starts.
854
+
855
+ - **Only Wardby builds snapshots, and only before the agent exists.** The
856
+ worker uploads a snapshot as part of its own preparation step, then seals the
857
+ run's upload window with the proxy. The proxy also seals the window itself on
858
+ the run's first model request: the worker sends none before its preparation
859
+ step is over, so the window is closed before the agent can act even when the
860
+ worker never sealed it. After the seal the proxy refuses every lookup,
861
+ download and upload for the run, and bytes of an upload that finish after the
862
+ seal are discarded. A proxy-side deadline
863
+ (`WARDBY_DEPENDENCY_CACHE_UPLOAD_SECONDS` after the run's proxy session
864
+ starts) is the last backstop. A run uploads at most once per project.
865
+ - **A restore needs the plan first.** The worker sends the committed lockfile
866
+ to the proxy's lockfile plan (see
867
+ [Lockfile installs: verified, then approved exactly](#lockfile-installs-verified-then-approved-exactly)).
868
+ The proxy records the lockfile's hash only if the plan approved every package
869
+ under the agent's _current_ allowlist, release-age rule and advisory policy,
870
+ and serves a snapshot only for a recorded hash. If the policy now refuses
871
+ something, the project is `refused` and no snapshot is used.
872
+ - **Keys are server-derived.** The worker supplies only the project folder and
873
+ the lockfile hash. The repository and image come from the run's own
874
+ record, and an id that is neither the run's own key nor the recomputed
875
+ nearest snapshot for the same scope is answered 404.
876
+ - **Integrity is checked on download.** The proxy stores each snapshot's
877
+ SHA-256 and size and sends them with the download; the worker checks both
878
+ while streaming. A blob whose bytes no longer match its record is deleted by
879
+ the proxy when it is served, so the next run rebuilds it. The worker then
880
+ scans the archive before writing anything (regular files, folders and
881
+ relative links inside `node_modules` only; no absolute or `..` paths, hard
882
+ links, devices, duplicate paths, including by case or Unicode form, or links
883
+ that leave `node_modules`), extracts it into a private temporary folder,
884
+ re-verifies the tree on disk, compares the recorded package metadata (npm's
885
+ hidden lockfile and each package's `package.json` version) with the committed
886
+ lockfile, and only then renames it into place. The integrity of the bytes
887
+ comes from the checksum the proxy holds, not from this comparison. Any
888
+ failure removes the temporary tree, and the project is installed `cold`
889
+ instead (except when the run's time is up or the tree would not fit the
890
+ run's disk). The worker records that scan's result with each snapshot it
891
+ uploads; a restore whose downloaded bytes match the recorded digest skips
892
+ the separate scan, and extraction still enforces every archive rule above
893
+ and the disk budget.
894
+ - **Install scripts are not cached.** Every preparation step runs npm with
895
+ `--ignore-scripts`. After the upload window is sealed, and only for a project
896
+ whose committed lockfile lists a package with an install script, the worker
897
+ runs `npm rebuild` so native add-ons (esbuild, sharp and the like) work. It
898
+ runs with a minimal environment (`PATH`, `HOME`, `LANG`, `TMPDIR` and the
899
+ registry settings, never the run's credentials), starts only after the proxy
900
+ confirmed the seal, and nothing it writes can be uploaded.
901
+
902
+ ### When it applies
903
+
904
+ - npm only: a `package.json` and a `package-lock.json` with `lockfileVersion`
905
+ 2 or 3, at the repository root and/or in other folders up to **3 levels
906
+ deep**, at most **4 projects** in all (the root first, then the others in
907
+ path order; any further ones are skipped `too_many_projects`).
908
+ - Folders named `node_modules`, folders whose name starts with a dot or has a
909
+ space or other characters outside letters, digits and `_@+-.`, and symlinked
910
+ folders or lockfiles are not searched or used.
911
+ - npm workspaces monorepos are not supported. A project that declares
912
+ `workspaces`, and any project covered by another project's `workspaces`
913
+ globs, is skipped, and so is a folder with an `npm-shrinkwrap.json`.
914
+ - A folder that already has a `node_modules` is left alone.
915
+ - The agent needs a package allowlist (registry mode): the restore is approved
916
+ by the lockfile plan.
917
+ - Codex and Claude Code runs on the `node` and `node-python` toolchains, with
918
+ worker images from a Wardby release that includes the cache. See
919
+ [Worker images](#worker-images) before you turn it on.
920
+
921
+ ### Worker images
922
+
923
+ Use the worker images from the same release as the control plane before you
924
+ turn `dependencyCache` on for any agent: the Codex worker images
925
+ (`CODING_WORKER_IMAGE` and its toolchain variants such as
926
+ `CODING_WORKER_IMAGE_NODE_PYTHON_3_12`), the Claude Code worker image
927
+ (`CODING_CLAUDE_WORKER_IMAGE`) and the Claude Code tool runner images
928
+ (`CODING_CLAUDE_TOOL_RUNNER_IMAGE` and its variants). An older Codex or
929
+ Claude Code worker image validates its input strictly and rejects a run whose
930
+ agent has the cache on: the run fails with `worker_input_failed`. A
931
+ `workerImageRef` image must also be rebuilt for this release: `FROM` this
932
+ release's driver image for a Codex agent, or `FROM` this release's tool runner
933
+ image for a Claude Code agent. `create_agent` and `update_agent` return a
934
+ warning when an agent has both `dependencyCache` and a `workerImageRef`;
935
+ Wardby can't tell how old a custom image is. A Claude Code tool runner without
936
+ the cache doesn't fail the run: its projects report `tool_unavailable`. See
937
+ [Bring-Your-Own Coding-Worker Images](coding-worker-byo-images.md#the-dependency-cache-needs-a-current-driver).
938
+
939
+ ### Enable it
940
+
941
+ 1. **Deployment.** On the **coding proxy**, which is the only component that
942
+ touches the store, set `WARDBY_DEPENDENCY_CACHE=true` and a blob store
943
+ (`BLOB_STORE` plus its per-store variables: `BLOB_STORE=gcs` with
944
+ `BLOB_STORE_GCS_BUCKET`, `BLOB_STORE=s3` with `BLOB_STORE_S3_BUCKET` and
945
+ `BLOB_STORE_S3_REGION`, or `BLOB_STORE=local` with `BLOB_STORE_LOCAL_DIR`).
946
+ With the switch on but no blob store the cache stays off and the proxy logs
947
+ a warning. Per target:
948
+ - **GKE**: set `dependency_cache_enabled = true` and
949
+ `dependency_cache_bucket_name` in `terraform.tfvars`, then run
950
+ `deploy/gke/up.sh` with `WARDBY_DEPENDENCY_CACHE=true`. See
951
+ [`deploy/gke/README.md`](../deploy/gke/README.md#run-dependency-cache).
952
+ - **AWS (EKS)**: set the Terraform variables of the same names, and give the
953
+ proxy container `BLOB_STORE=s3`, `BLOB_STORE_S3_BUCKET` (the
954
+ `dependency_cache_bucket` output), `BLOB_STORE_S3_REGION` and
955
+ `WARDBY_DEPENDENCY_CACHE=true`. See
956
+ [`deploy/aws/README.md`](../deploy/aws/README.md#run-dependency-cache).
957
+ - **kind harness**:
958
+ `WARDBY_DEPENDENCY_CACHE=true bash deploy/kind-coding/up.sh`. The store is
959
+ the proxy pod's `emptyDir`, so snapshots are lost when the pod restarts.
960
+ - **Docker quickstart**: add `WARDBY_DEPENDENCY_CACHE=true` to
961
+ `.wardby/.env` and restart the stack. Snapshots live on a named volume
962
+ that `docker compose down -v` removes. This requires wardby 0.7.0 or
963
+ later (earlier runtime images can't write to the volume).
964
+ 2. **Database grants.** Where the coding proxy has its own restricted database
965
+ role (GKE), it needs access to `DependencySnapshot` and a few `CodingRun`
966
+ columns. `deploy/gke/up.sh` applies them before the rollout; see
967
+ [Upgrading an existing deployment](getting-started-gke.md#upgrading-an-existing-deployment).
968
+ 3. **Per agent.** Once the [worker images](#worker-images) are current, set
969
+ `codingProfile.dependencyCache` to `true` with `create_agent` or
970
+ `update_agent`. Like the package allowlist, it needs
971
+ `packages:approve` or `agents:admin`. The setting is read when a run
972
+ starts.
973
+
974
+ ### Dependency cache limits
975
+
976
+ These variables are read by the coding proxy, and only when
977
+ `WARDBY_DEPENDENCY_CACHE=true`. Each limit must be a positive integer; the
978
+ proxy refuses to start otherwise.
979
+
980
+ | Variable | Default | Meaning |
981
+ | ----------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------ |
982
+ | `WARDBY_DEPENDENCY_CACHE` | `false` | Master switch (`true` or `false`). Also needs `BLOB_STORE`. |
983
+ | `WARDBY_DEPENDENCY_CACHE_MAX_SNAPSHOT_MB` | 1024 | Largest snapshot accepted. A bigger one is not stored. |
984
+ | `WARDBY_DEPENDENCY_CACHE_REPO_CAP_MB` | 4096 | Snapshot bytes kept per repository. Least recently used go first. |
985
+ | `WARDBY_DEPENDENCY_CACHE_TOTAL_CAP_MB` | 20480 | Snapshot bytes kept in all. Least recently used go first. |
986
+ | `WARDBY_DEPENDENCY_CACHE_MAX_AGE_DAYS` | 14 | A snapshot's lifetime, counted from creation rather than last use, so even a constantly used snapshot is rebuilt periodically. |
987
+ | `WARDBY_DEPENDENCY_CACHE_UPLOAD_SECONDS` | 900 | How long after a run's proxy session starts the proxy still accepts an upload. |
988
+
989
+ The proxy evicts hourly: snapshots past the maximum age, then least recently
990
+ used ones over the per-repository cap, then over the total cap. It deletes the
991
+ stored object before its database record. On GKE and AWS the bucket also
992
+ deletes objects older than `dependency_cache_max_age_days + 7` days, which
993
+ removes objects that were never recorded; keep that Terraform variable equal
994
+ to `WARDBY_DEPENDENCY_CACHE_MAX_AGE_DAYS` by hand. The kind harness and
995
+ Docker quickstart stores are local and have no such rule.
996
+
997
+ Other bounds:
998
+
999
+ - The cache works within a disk budget: the run's workspace disk
1000
+ (`workspaceDiskMb`) less what the workspace already uses and a safety
1001
+ margin. If that can't be measured, or leaves under 64 MiB, the cache is
1002
+ skipped (`budget_unknown`, `budget_too_small`). Restores and the snapshot
1003
+ archive are held to the budget while they are written; an install is only
1004
+ checked before it starts, since npm's own writes can't be bounded while it
1005
+ runs.
1006
+ - A restore must fit: the downloaded archive plus its unpacked tree. A
1007
+ `nearest` snapshot is restored only when one and a half times its
1008
+ unpacked size fits, leaving room for the update and the new archive.
1009
+ - An install (`cold`) starts only when at least 1 GiB is left, or twice
1010
+ the project's tree size when a snapshot of it was already seen. Otherwise
1011
+ the project is skipped `budget_too_small` and the workspace is left as it
1012
+ was, so the agent installs for itself. A tree much larger than that can
1013
+ still fill the disk during the install, as it would for the agent.
1014
+ - After its own install, the worker deletes npm's download cache (the
1015
+ `_cacache` folder under the npm cache folder in the workspace's `.cache`;
1016
+ the registry settings beside it stay). It is about as large as
1017
+ `node_modules` again, and the agent's npm downloads what it needs.
1018
+ - Before archiving a tree for upload, the worker measures the workspace
1019
+ again. It doesn't write the archive when the estimated size (the tree
1020
+ at its last snapshot's compression, or a third of the tree) doesn't fit
1021
+ what is left, and stops and deletes an archive that outgrows it while
1022
+ being written. The installed tree stays and the project is still
1023
+ `cold` or `nearest`; only the upload is skipped. The worker log records
1024
+ this as `snapshot_skipped` with reason `budget` (it isn't a `get_run`
1025
+ reason).
1026
+ - The worker gives the lockfile plan up to 10 minutes (a proxy with a cold
1027
+ package cache checks every package of a large lockfile first), and each
1028
+ other proxy call up to 2 minutes. When the proxy stops at its own
1029
+ `REGISTRY_PLAN_TIMEOUT_MS` (`503 wardby_plan_incomplete`), the worker sends
1030
+ the plan again after 2 seconds and the proxy resumes from what it already
1031
+ verified, until the 10 minutes or 5 attempts are up (`timeout`). Each
1032
+ attempt counts toward `REGISTRY_PLAN_MAX_PER_RUN`; the cache sends at most
1033
+ 8 plans per run, leaving the rest for the agent's own installs (a project
1034
+ past that is `plan_failed`). Raising `REGISTRY_PLAN_TIMEOUT_MS` is optional
1035
+ tuning: it means fewer, longer attempts.
1036
+ - The whole preparation, measuring the disk included, is bounded to 20
1037
+ minutes or half the time left before the run's deadline, whichever is
1038
+ sooner, so a short run keeps most of its time for the agent. A rebuild is
1039
+ bounded to 5 minutes, and the worker stops uploading once the proxy's upload
1040
+ window is nearly over.
1041
+ - Packing a snapshot (`tar` and gzip) gets 5 minutes plus 0.5 seconds per MiB
1042
+ of the installed tree (a 1 GiB tree gets about 13.5 minutes), and never runs
1043
+ past the upload window: a snapshot that couldn't be uploaded isn't packed.
1044
+ When a step runs out of time, the worker log records `snapshot_skipped` with
1045
+ reason `timeout`, the `stage` (`archive`, `scan` or `upload`),
1046
+ `writtenBytes`, `estimateBytes`, `elapsedMs`, and the `limit` that fired
1047
+ with its `limitMs`. No snapshot is stored, so every run on that lockfile
1048
+ installs again. What to raise depends on `limit`:
1049
+ - `pack`: the pod is short on CPU for gzip; raise `CODING_CPUS`.
1050
+ - `upload_window`: the install took most of the window; raise
1051
+ `WARDBY_DEPENDENCY_CACHE_UPLOAD_SECONDS`, or `CODING_CPUS` to install
1052
+ faster.
1053
+ - `upload`: the blob store is slow to accept the archive; check the proxy's
1054
+ path to it.
1055
+ - `deadline`: the whole preparation ran out (20 minutes, or half the time
1056
+ left before the run's deadline); raise `CODING_CPUS` or the agent's
1057
+ `timeoutSec`.
1058
+ - A large tree also needs memory: an npm install of about 1 GiB and 400,000
1059
+ files can need more than the default 2048 MiB under gVisor, and a pod that
1060
+ runs out is killed before anything is cached. Raise `CODING_MEMORY_MB` (6144
1061
+ is enough for a tree that size).
1062
+ - On Docker the workspace can be a tmpfs, whose inode limit can bind before
1063
+ its byte limit on a very large restore. The cache is then skipped
1064
+ (`internal_error`) and the run continues.
1065
+
1066
+ ### What the agent sees
1067
+
1068
+ When at least one project was restored or installed, the agent's task carries a
1069
+ note naming the folders: "Dependencies are pre-installed from the committed
1070
+ lockfile in: the repo root, web. Don't run `npm ci` there; add packages with a
1071
+ single install command." `npm ci` deletes `node_modules` and installs
1072
+ everything again, which throws away the prepared tree and the time saved,
1073
+ whereas `npm install <package>` works on top of it. A pull request Wardby opens
1074
+ gets a line, "Dependency cache: restored `node_modules` for the repo root,
1075
+ `web`.", naming the folders restored from a snapshot (`hit` or `nearest`).
1076
+
1077
+ ### What `get_run` reports
1078
+
1079
+ `get_run` returns `dependencyCache` for a run that used the cache:
1080
+
1081
+ ```json
1082
+ {
1083
+ "dependencyCache": {
1084
+ "projects": [
1085
+ { "path": ".", "result": "hit", "key": "9f2c…", "sizeBytes": 148000000, "restoreMs": 4100, "rebuild": "ok" },
1086
+ { "path": "web", "result": "cold", "installMs": 61000, "sizeBytes": 91000000 },
1087
+ { "path": "tools", "result": "skipped", "reason": "workspaces" }
1088
+ ]
1089
+ }
1090
+ }
1091
+ ```
1092
+
1093
+ - `result` is `hit`, `nearest`, `cold`, `skipped` or `refused`.
1094
+ - `key`, `sizeBytes`, `restoreMs` and `installMs` appear where they apply.
1095
+ - `reason` explains a `skipped` or `refused` project, or a `cold` project whose
1096
+ tree was kept but not stored (`inconsistent`).
1097
+ - `rebuild` is `ok`, `failed`, `timeout`, `skipped` (no install scripts) or
1098
+ `unsealed` (the proxy did not confirm the seal, so no script ran).
1099
+ - A top-level `reason` with an empty `projects` list means the whole step
1100
+ failed or was skipped. `omitted` counts projects that couldn't be listed.
1101
+
1102
+ The field is absent for a run that didn't use the cache, and for one that
1103
+ failed before its result was collected.
1104
+
1105
+ ### Cache reason codes
1106
+
1107
+ A skipped project never fails the run. Look the `reason` up here. A snapshot
1108
+ that a `hit` or `nearest` restore rejects (`download_failed`, `size_mismatch`,
1109
+ `sha_mismatch`, `too_large`, `unsafe_archive`, `inconsistent`) is not reported
1110
+ as the project's reason: the project installs `cold` instead, and a traced
1111
+ Codex run's worker log records the code as `hit_rejected` or
1112
+ `nearest_rejected`. The same log times each restore's stages as
1113
+ `restore_phase` events (`stage` is `download`, `scan`, `extract`,
1114
+ `verify_links` or `check_consistency`, with `ms`), for finding a slow restore.
1115
+
1116
+ | Reason | Meaning and what to do |
1117
+ | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1118
+ | `no_lockfile`, `lockfile_unreadable`, `no_package_json` | Nothing eligible in that folder. Expected for repositories without npm. |
1119
+ | `lockfile_version` | The lockfile is below `lockfileVersion` 2. Regenerate it with npm 7 or later. |
1120
+ | `workspaces`, `workspace_member`, `shrinkwrap_present` | An npm workspaces root, a folder inside one, or an `npm-shrinkwrap.json`: not supported. The agent installs for itself. |
1121
+ | `node_modules_present` | The checkout already has a `node_modules` there. It is left alone. |
1122
+ | `no_dependencies` | The lockfile installs nothing. |
1123
+ | `too_many_projects` | More than 4 projects were found. The rest are skipped. |
1124
+ | `cache_dir_unsafe` | `.cache` in the checkout isn't a plain folder (for example a committed symlink). Remove it from the repository. |
1125
+ | `no_registry` | The worker got no registry settings: the agent has no package allowlist. Enable registry mode first. |
1126
+ | `plan_failed` | The lockfile plan didn't answer properly. See [Error codes](#error-codes); retry. |
1127
+ | `plan_refused` (result `refused`) | The plan refused at least one package under the agent's current policy (not allowlisted, too new, advisory). Fix the allowlist or policy; no snapshot is used. |
1128
+ | `cache_unavailable` | The proxy has no cache for this run: the switch is off, there is no blob store, or the run's flag or image is missing. Check `WARDBY_DEPENDENCY_CACHE` and `BLOB_STORE` on the proxy. |
1129
+ | `plan_required`, `sealed` | The proxy refused a lookup because the lockfile wasn't approved by a plan, or the window was already sealed. Usually the preparation was cut short. |
1130
+ | `lookup_failed`, `download_failed` | The proxy or the blob store answered unexpectedly. Check the proxy log and the store's permissions. |
1131
+ | `size_mismatch`, `sha_mismatch` | The download didn't match its recorded size or digest. It is not used, and the proxy deletes the damaged snapshot. Repeats point at the blob store. |
1132
+ | `too_large` | A snapshot is larger than the maximum the proxy reported. Raise `WARDBY_DEPENDENCY_CACHE_MAX_SNAPSHOT_MB` if that is intended. |
1133
+ | `unsafe_archive` | The archive failed a safety rule or its extracted tree didn't verify. Runs install `cold` instead until it ages out and is rebuilt. A package shipping two files that differ only in case is not cached. |
1134
+ | `over_budget` | The unpacked snapshot is larger than the disk budget. Raise the agent's `workspaceDiskMb`. |
1135
+ | `inconsistent` | The tree doesn't match the committed lockfile. A restored one is discarded and the project installs `cold`; a `cold` one is kept for the agent but not stored. |
1136
+ | `install_failed` | `npm ci`, or the `npm install` that updates a nearest snapshot, exited non-zero. The agent installs for itself and sees the real error. |
1137
+ | `budget_unknown`, `budget_too_small` | The run's workspace disk couldn't be measured, under 64 MiB would remain, or too little is left for an install (see "Other bounds" above). The workspace is unchanged. Raise `workspaceDiskMb`. |
1138
+ | `timeout`, `cancelled` | A step or the preparation's bound (20 minutes, or half the time left before the run's deadline) ran out, or the run was cancelled. |
1139
+ | `internal_error`, `worker_error` | Something unexpected in the worker, including a full disk or inode table. The run continues without the cache; check the worker's log. |
1140
+ | `tool_unavailable` | Claude Code only: the tool-runner image predates the cache or is a custom image without it. Rebuild it `FROM` the current tool-runner image. The proxy still seals the window on the run's first model request. |
1141
+ | `report_invalid` | The worker's report was malformed and was replaced by this code. |
1142
+ | `already_started` | A command ran before preparation finished. Not expected in normal operation. |
1143
+ | `invalid_deadline` | Claude Code only: the worker passed the tool runner a deadline it couldn't read. Not expected; the worker and tool runner images are from different releases. |
1144
+ | `worker_input_failed` (the run's error, not a project reason) | The run failed before the agent started: a Codex or Claude Code worker image older than the cache, or a `workerImageRef` image not rebuilt from a release that includes it. See [Worker images](#worker-images). |
1145
+
1146
+ For `rebuild`, `failed`, `timeout` and `unsealed` mean native add-ons may be
1147
+ unbuilt; the agent can run `npm rebuild` itself.
1148
+
1149
+ The proxy answers its `/cache/npm/` routes with `{"error": "<code>"}` and a
1150
+ `404 not_found` when the cache is off; those codes show up as the reasons
1151
+ above, never to the agent.
1152
+
377
1153
  ## What the agent can then run
378
1154
 
379
1155
  Inside the run, `npm install <package>` and `pip install <package>` (into
@@ -388,6 +1164,120 @@ can otherwise run out of space on a large package; that directory counts
388
1164
  toward the run's `workspaceDiskMb` and, like the rest of `.cache`, is never
389
1165
  collected either.
390
1166
 
1167
+ ### Python installers: pip, uv and Poetry
1168
+
1169
+ The `node-python` worker images ship pip (inside a virtual environment) and
1170
+ uv. The worker points both at the registry for every run:
1171
+
1172
+ | Setting | Client | What it does |
1173
+ | ------------------------------- | ------ | ----------------------------------------------------------------------------- |
1174
+ | `PIP_INDEX_URL` | pip | The registry, with the run's registry-only token, as the only index |
1175
+ | `PIP_ONLY_BINARY=:all:` | pip | Wheels only; source distributions are never installed |
1176
+ | `UV_DEFAULT_INDEX` | uv | The same registry URL as `PIP_INDEX_URL` (uv reads none of pip's settings) |
1177
+ | `UV_NO_BUILD=1` | uv | Wheels only from the index; the project itself still builds |
1178
+ | `PIP_CACHE_DIR`, `UV_CACHE_DIR` | both | Caches under the workspace's `.cache`, never collected |
1179
+ | `UV_PYTHON_DOWNLOADS=never` | uv | uv never tries to download a Python interpreter (the sandbox can't reach one) |
1180
+
1181
+ These are set by the worker, not by an operator, and an agent's or
1182
+ service's own environment can't override `PIP_*` or `UV_*` names.
1183
+
1184
+ What works, and what doesn't yet:
1185
+
1186
+ - **`pip install <package>`, `pip install -r requirements.txt` with
1187
+ top-level requirements, and `uv pip install …`** resolve top-down, so
1188
+ every dependency is allowed by the time it is requested.
1189
+ - **A fully pinned requirements file** (`pip-compile`, `uv pip compile` or
1190
+ `pip freeze` output, with or without `--hash`) lists transitive packages
1191
+ as top-level lines, which pip and uv request before reading any parent's
1192
+ metadata. Verify it first, as npm lockfiles are: `uv pip install -r` and
1193
+ `uv pip sync` do this themselves (see below); for pip, run
1194
+ `wardby-packages plan requirements.txt` before `pip install -r
1195
+ requirements.txt` (pip has no hook, and in the worker it only exists
1196
+ inside a virtual environment). The worker tells the agent so.
1197
+ - **`uv sync`, `uv run`, `uv add`, `uv remove` and `uv lock` with a
1198
+ committed `uv.lock`** work: the worker's `uv` is a small shim
1199
+ (`/opt/wardby/bin/uv`) that verifies the lock first. uv installs a locked
1200
+ project from the file URLs the lock records, never from its index, so for
1201
+ the command the shim also points the lock's PyPI URLs at the registry
1202
+ (without credentials; uv sends the index's), and afterwards puts the file
1203
+ back: exactly as it was when uv left it alone, otherwise with PyPI's URLs
1204
+ (a file the lock already had keeps its URL; a new one gets PyPI's
1205
+ redirecting `files.pythonhosted.org/packages/<tag>/<letter>/<name>/<file>`
1206
+ path). Before the workspace is collected, any registry URL left in a
1207
+ `uv.lock` is rewritten the same way, so a committed lock never names the
1208
+ registry.
1209
+ - **`pip install .`, `uv sync`, `uv run` and other project installs** build
1210
+ the project, which installs its build backend (`[build-system] requires`:
1211
+ `hatchling`, `setuptools`, `poetry-core`, …) and whatever the backend asks
1212
+ for while building (hatchling asks for `editables` for an editable install,
1213
+ which `uv sync` and `uv run` do). A lockfile doesn't record these, so
1214
+ allowlist them like any other package.
1215
+ - **Poetry** is not supported in the sandbox: it can't be pointed at the
1216
+ registry by environment alone (it needs a primary source in
1217
+ `pyproject.toml` plus credentials), and a `poetry.lock` (or a
1218
+ `pylock.toml`) is refused by the plan with `wardby_lockfile_unsupported`.
1219
+ Export a pinned requirements file, or use uv.
1220
+
1221
+ A Python lockfile plan (`POST /registry/pypi/-/plan`) trusts nothing the
1222
+ file claims, like an npm one, with these differences:
1223
+
1224
+ - **Entries.** A `uv.lock` (`version = 1`): every package from PyPI (or the
1225
+ registry). Its own projects (editable, virtual and directory sources) are
1226
+ where the lock starts; a package from git, a URL, a path or another index
1227
+ is refused with `wardby_lockfile_entry_unsupported`. A requirements file:
1228
+ every exact `name==version` line (options, `-r`/`-c` includes, local
1229
+ projects and unpinned lines are not entries; pip resolves those the usual
1230
+ way, so plan an included file separately); a `name @ url` line is refused.
1231
+ - **Facts.** Each pinned release is read from PyPI's JSON API
1232
+ (`pypi.org/pypi/<name>/<version>/json`, stored for reuse): its dependency
1233
+ lines (`requires_dist`) and its wheels. A release with no wheel is refused
1234
+ with `wardby_file_not_allowed`; its publish time, for the release-age
1235
+ check, is its earliest wheel's. Hashes a lockfile records are not compared
1236
+ (pip and uv check them on install).
1237
+ - **Edges and extras.** A dependency line counts when the locked version is
1238
+ inside its specifier (a pinned pre-release included). Extras follow the
1239
+ same rule as the dependency walk: a line gated on an extra (`extra ==
1240
+ "test"`) is followed only for an install with that extra, and extras are
1241
+ followed only along a chain that starts at an allowlist entry naming them
1242
+ (`psycopg[binary]`).
1243
+ - **Approvals are names.** An approved package is allowed by name for the
1244
+ run; its downloads still go through every usual check (wheels only, each
1245
+ file's own release age, the index's sha256, advisories). A pin refused as
1246
+ too new or with no wheel is named in the plan's output with its reason.
1247
+
1248
+ **Why an install was refused.** npm prints the registry's refusal body, so
1249
+ the agent sees the code and the hint. pip doesn't (a refused package shows
1250
+ as `from versions: none`), and uv shows only `403 Forbidden`. So the worker
1251
+ tells the agent to run `wardby-packages` after an install that failed
1252
+ without saying why: it lists the packages the registry refused for the run
1253
+ in the last 15 minutes, each with its code and what to do. For a Claude Code
1254
+ agent, the tool runner also adds those lines to any failed command's output
1255
+ by itself. The command reads the run's registry-only token from the worker's
1256
+ environment and sends it only to the registry.
1257
+
1258
+ The registry also records what it hides from a Python index, which pip and
1259
+ uv report only as "no versions": a package whose allowed versions publish
1260
+ only source distributions (`wardby_file_not_allowed`), or whose every wheel
1261
+ is newer than the release-age limit (`wardby_version_filtered`). One case
1262
+ stays unrecorded: a single pinned version withheld while others are listed
1263
+ (`jinja2==2.10` with a blocking advisory), since the registry never sees
1264
+ the client's pin.
1265
+
1266
+ An installer often reads an index it doesn't need (a resolver weighing
1267
+ candidates, a dependency behind a platform marker), so a refusal recorded
1268
+ only from an index weighs less than a refused download. Once any version of
1269
+ that name is served later in the same coding run, it is dropped like any
1270
+ other refusal the run got past. If nothing of that name was served,
1271
+ `wardby-packages` and `get_run` list it (`get_run` marks it
1272
+ `"listingOnly": true`), and the status shows it according to the outcome:
1273
+
1274
+ - a coding sub-run failed: it counts toward ⛔ blocked, like any refusal;
1275
+ - the run made no changes: it is a ⚠️ dependency install line, not ⛔ (for
1276
+ example, a package whose only release has nothing but too-new wheels);
1277
+ - a pull request was opened or updated: it adds no **Dependency install
1278
+ incomplete** warning, no ⚠️ line, and no entry in the pull request's
1279
+ package section.
1280
+
391
1281
  ## Safeguards
392
1282
 
393
1283
  - **Dependency graph only.** Only packages on the allowlist, or reachable
@@ -416,16 +1306,26 @@ collected either.
416
1306
  parse counts as affected. If OSV can't be reached, installs in that request fail closed
417
1307
  (`503 wardby_audit_unavailable`) rather than skipping the check — an
418
1308
  operator can opt out of fail-closed with `REGISTRY_AUDIT_FAIL_OPEN=true`.
1309
+ An approver can accept specific advisories for one agent with
1310
+ `packagePolicy.acceptedAdvisories` (see
1311
+ [Accepting advisories](#accepting-advisories)); malware is never waivable,
1312
+ and every waiver is reported in the run's packages and in the pull request.
419
1313
  - **Wheels only for Python.** PyPI source distributions (sdists), which can
420
1314
  run arbitrary code at install/build time, are never served — only wheels.
1315
+ The index still lists them, so a `uv.lock` uv writes in a run keeps them
1316
+ as one written against PyPI does, but a download of one is refused with
1317
+ `wardby_file_not_allowed`.
421
1318
  - **npm install scripts are off, but only by configuration.** The proxy sets
422
1319
  `npm_config_ignore_scripts=true` in the agent's environment, which disables
423
- npm's install-time script hooks. This is a configuration default, not
1320
+ npm's (and pnpm's) install-time script hooks; `YARN_IGNORE_SCRIPTS` and
1321
+ `YARN_ENABLE_SCRIPTS=false` do the same for yarn classic and yarn Berry. This is a configuration default, not
424
1322
  something the proxy can enforce on the downloaded tarball itself: the
425
1323
  proxy cannot strip scripts from a package, so an agent that deliberately
426
1324
  overrode this setting could still run one. A script that ran would still be
427
1325
  confined to the same sandbox — no network, no credentials, only the
428
1326
  workspace writable — but this is a documented limit, not a guarantee.
1327
+ The one exception is the [dependency cache](#dependency-cache)'s
1328
+ `npm rebuild`, which Wardby runs itself before the agent starts.
429
1329
 
430
1330
  ## Operator limits
431
1331
 
@@ -436,22 +1336,28 @@ as what's already been recorded, so parallel downloads (for example npm's
436
1336
  default concurrent connections) can't add up to more than the limit before any
437
1337
  one of them finishes:
438
1338
 
439
- | Setting | Default | Meaning |
440
- | ------------------------------ | ------- | ------------------------------------------------------------- |
441
- | `REGISTRY_MAX_FILE_MB` | 200 | Largest single downloaded file. |
442
- | `REGISTRY_MAX_TOTAL_MB` | 2048 | Total bytes downloaded in one run. |
443
- | `REGISTRY_MAX_FILES` | 5000 | Total files served in one run. |
444
- | `REGISTRY_IDLE_TIMEOUT_MS` | 120000 | Idle time allowed on one download. |
445
- | `REGISTRY_AUDIT_FAIL_OPEN` | `false` | Allow-and-report instead of refusing when OSV is unreachable. |
446
- | `REGISTRY_METADATA_TIMEOUT_MS` | 30000 | Time allowed for one metadata or OSV request, body included. |
447
- | `REGISTRY_MAX_METADATA_MB` | 64 | Largest metadata or OSV response the proxy reads. |
448
- | `REGISTRY_MAX_GRAPH_PACKAGES` | 3000 | Packages the on-demand graph walk may expand in one run. |
449
- | `REGISTRY_GRAPH_TIMEOUT_MS` | 180000 | Time allowed for one on-demand graph walk. |
450
- | `REGISTRY_DB_POOL_MAX` | 3 | Database connections for the registry's own pool. |
451
- | `CODING_PROXY_DB_POOL_MAX` | 5 | Database connections for the budget ledger (model requests). |
452
- | `REGISTRY_PLAN_MAX_ENTRIES` | 5000 | Entries a lockfile plan (`POST /-/plan`) may have. |
453
- | `REGISTRY_PLAN_TIMEOUT_MS` | 120000 | Time allowed for one lockfile plan. |
454
- | `REGISTRY_PLAN_MAX_PER_RUN` | 20 | Lockfile plans one run may make. |
1339
+ | Setting | Default | Meaning |
1340
+ | ------------------------------ | ------- | -------------------------------------------------------------- |
1341
+ | `REGISTRY_MAX_FILE_MB` | 200 | Largest single downloaded file. |
1342
+ | `REGISTRY_MAX_TOTAL_MB` | 2048 | Total bytes downloaded in one run. |
1343
+ | `REGISTRY_MAX_FILES` | 5000 | Total files served in one run. |
1344
+ | `REGISTRY_IDLE_TIMEOUT_MS` | 120000 | Idle time allowed on one download. |
1345
+ | `REGISTRY_AUDIT_FAIL_OPEN` | `false` | Allow-and-report instead of refusing when OSV is unreachable. |
1346
+ | `REGISTRY_METADATA_TIMEOUT_MS` | 30000 | Time allowed for one metadata or OSV request, body included. |
1347
+ | `REGISTRY_MAX_METADATA_MB` | 64 | Largest metadata or OSV response the proxy reads. |
1348
+ | `REGISTRY_MAX_GRAPH_PACKAGES` | 3000 | Package names the graph walk may expand per run and ecosystem. |
1349
+ | `REGISTRY_GRAPH_TIMEOUT_MS` | 180000 | Time one download's graph walk may take. |
1350
+ | `REGISTRY_DB_POOL_MAX` | 3 | Database connections for the registry's own pool. |
1351
+ | `CODING_PROXY_DB_POOL_MAX` | 5 | Database connections for the budget ledger (model requests). |
1352
+ | `REGISTRY_PLAN_MAX_ENTRIES` | 5000 | Entries a lockfile plan (`POST /-/plan`) may have. |
1353
+ | `REGISTRY_PLAN_TIMEOUT_MS` | 120000 | Time allowed for one lockfile plan. |
1354
+ | `REGISTRY_PLAN_MAX_PER_RUN` | 20 | Lockfile plans one run may make. |
1355
+
1356
+ A large allowlist can reach the package bound: the walk follows every kept
1357
+ version in each range plus optional and peer dependencies, so a graph is
1358
+ usually several times larger than one install. Allowlist the packages a
1359
+ project needs directly, or raise `REGISTRY_MAX_GRAPH_PACKAGES` (which costs
1360
+ proxy memory and walk time).
455
1361
 
456
1362
  Metadata is cached for five minutes in a bounded cache (500 packages and
457
1363
  64 MiB of trimmed metadata, least recently used evicted first), and
@@ -485,14 +1391,34 @@ one package):
485
1391
  { "ecosystem": "npm", "name": "@heroui/react", "version": "3.2.6", "size": 482113 },
486
1392
  { "ecosystem": "pypi", "name": "flask", "version": "3.0.0", "size": 101817 }
487
1393
  ],
488
- "packageRefusals": [{ "ecosystem": "npm", "name": "left-pad", "reason": "wardby_package_not_allowed" }]
1394
+ "packageRefusals": [
1395
+ {
1396
+ "ecosystem": "npm",
1397
+ "name": "left-pad",
1398
+ "reason": "wardby_package_not_allowed",
1399
+ "hint": "The package is not on the agent's allowlist and no allowlisted package the run resolved depends on it. The agent's owner must allowlist the package directly; retrying will not help."
1400
+ }
1401
+ ]
489
1402
  }
490
1403
  ```
491
1404
 
1405
+ `hint` says what the refusal code means and what to do about it; it is
1406
+ present for the allowlist codes below and omitted for codes without guidance.
492
1407
  `size` is the number of bytes served, or `null` if none was recorded.
1408
+ A refusal the run got past is left out of `packageRefusals`, and so out of
1409
+ the pull request warning, the status comments and Slack: the registry later
1410
+ served the same package to the same coding run (for a refusal of one
1411
+ version, that version), for example after npm retried a temporary
1412
+ `wardby_upstream_error`. A package served to a different coding sub-run
1413
+ does not clear it, and a refusal recorded after the package was served
1414
+ still counts.
493
1415
  `get_run` also returns `packagePlan: { "approved": <n>, "refused": <n> }`:
494
1416
  the exact versions lockfile plans approved for the run, and the distinct
495
- entries they refused (each refusal is also in `packageRefusals`).
1417
+ entries they refused (each refusal is also in `packageRefusals`, unless that
1418
+ version was served later). A run with
1419
+ [package audit mode](#package-audit-mode) on also returns `packageAuditMode`
1420
+ and `packageAudit`, and its pull request opens with a caution banner listing
1421
+ the packages installed outside the allowlist.
496
1422
 
497
1423
  The pull request finalization also appends a collapsed **Packages installed
498
1424
  during this run** section listing the same information, so a reviewer
@@ -501,10 +1427,17 @@ and 100 refusals, followed by "…and N more — see get_run for the full list";
501
1427
  `get_run` always has the complete list. If the report can't be loaded or
502
1428
  rendered, the section is left out and the pull request is still opened.
503
1429
 
1430
+ A later run on the same pull request (a review fix round, a mention
1431
+ follow-up, a continuation) rewrites the section as **Packages installed by
1432
+ wardby's runs on this pull request**: every package installed by the runs
1433
+ that pushed to the pull request plus the latest run's, each package and
1434
+ version once. Refusals, and the warning below, cover the latest run only. See
1435
+ [How the pull request description is kept current](code-review-agents.md#how-the-pull-request-description-is-kept-current).
1436
+
504
1437
  When the registry refused anything during the run, the pull request also
505
1438
  opens with a **Dependency install incomplete** warning, above the agent's
506
1439
  summary. It names the refused packages grouped by refusal code, says what
507
- each code means, and notes that failed checks under **Tests** may come from
1440
+ each code means (in the same terms as `hint`), and notes that failed checks under **Tests** may come from
508
1441
  the sandbox rather than the change. If the run changed a lock file
509
1442
  (`package-lock.json`, `npm-shrinkwrap.json`, `yarn.lock`, `pnpm-lock.yaml`,
510
1443
  `poetry.lock`, `uv.lock` or `Pipfile.lock`), the warning names it: the lock
@@ -517,6 +1450,12 @@ Review agents see the pull request's CI results through `repo_pr_read`
517
1450
  failure; see
518
1451
  [code-review-agents.md](code-review-agents.md#the-repo_-tools).
519
1452
 
1453
+ GitHub and Jira status comments show a package-registry blocked message only
1454
+ when no pull request resulted and a coding child reported no changes or failed.
1455
+ Otherwise, recorded refusals add a dependency install warning while preserving
1456
+ the agent's reply or answer, or the pull request link. A recorded refusal alone
1457
+ does not mean the final install or the run failed.
1458
+
520
1459
  A package that is only reachable through a refused one is refused too, as
521
1460
  `wardby_package_not_allowed`. When a dependency deep in a toolchain has a
522
1461
  high-severity advisory and no fixed version, every run that installs that
@@ -524,34 +1463,38 @@ tree is refused at the same place; approving more packages won't help.
524
1463
 
525
1464
  ## Error codes
526
1465
 
527
- npm and pip print the proxy's error body verbatim, so these are what you'll
528
- see on a failed install:
529
-
530
- | Code | Status | Meaning / what to do |
531
- | ------------------------------------ | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
532
- | `wardby_package_not_allowed` | 403 | The package isn't on the allowlist and isn't reachable from an allowlisted package's dependency graph. Add it (or its top-level dependent) to `packageAllowlist`. For a PyPI package an extra pulls in (`psycopg-binary` for `psycopg[binary]`), name the extra on the entry instead. |
533
- | `wardby_file_not_allowed` | 403 | The specific file type is never served for this ecosystem (for example a PyPI sdist). Nothing to configure; use a wheel. |
534
- | `wardby_version_filtered` | 404 | Every matching version is too new (younger than `minReleaseAgeDays`) or withheld by the vulnerability audit. Wait for it to age past the threshold, or lower `minReleaseAgeDays` if you understand the risk. |
535
- | `wardby_package_not_found` | 404 | The upstream registry has no such package name. Check the spelling (npm names are case-sensitive). |
536
- | `wardby_package_too_large` | 413 | The file exceeds `REGISTRY_MAX_FILE_MB`. Ask the operator to raise it if the file is legitimately larger. |
537
- | `wardby_package_limit` | 429 | The run hit `REGISTRY_MAX_FILES` or `REGISTRY_MAX_TOTAL_MB`. Trim what the run installs, or ask the operator to raise the limit. |
538
- | `wardby_audit_unavailable` | 503 | OSV couldn't be reached and `REGISTRY_AUDIT_FAIL_OPEN` isn't set. Retry, or have the operator set that flag if the outage is expected to be long. Also returned, as "could not be checked against this agent's approved dependency graph… try again", when the audit failed while resolving a lockfile install's dependency graph. |
539
- | `wardby_graph_incomplete` | 503 | A lockfile install requested a package the on-demand dependency-graph walk hadn't reached when it was cut short by `REGISTRY_GRAPH_TIMEOUT_MS`. Retry: the walk resumes where it stopped (npm retries 5xx on its own). Never means the package is outside the graph. |
540
- | `wardby_graph_limit` | 403 | The walk hit `REGISTRY_MAX_GRAPH_PACKAGES` for this run before reaching the package. Permanent for the run, so retrying won't help: allowlist the package directly, or ask the operator to raise the limit. Never means the package is outside the graph. |
541
- | `wardby_lockfile_unsupported` | 400 | The lockfile sent to `POST /-/plan` isn't lockfileVersion 2 or 3 (or has no `packages`). Regenerate it with npm 7 or later. The install still runs, through the walk. |
542
- | `wardby_lockfile_too_large` | 413 | The lockfile is over 20 MiB or has more than `REGISTRY_PLAN_MAX_ENTRIES` entries. The install still runs, through the walk. |
543
- | `wardby_lockfile_integrity_mismatch` | — | A plan refusal: the lockfile's `integrity` for this entry isn't the registry's own (or the registry publishes none). Regenerate the lockfile entry; never edit integrity by hand. |
544
- | `wardby_lockfile_entry_unsupported` | — | A plan refusal: the entry is installed from git, a URL or a path, or isn't an exact registry version. Only registry packages can be approved. |
545
- | `wardby_plan_limit` | 429 | The run has made `REGISTRY_PLAN_MAX_PER_RUN` lockfile plans. The install still runs, through the usual checks. |
546
- | `wardby_registry_integrity_missing` | — | A plan refusal: npm publishes no integrity for this version, so a download of it couldn't be checked. |
547
- | `wardby_plan_in_progress` | 429 | A lockfile plan is already running for this run (one at a time). Wait for it; the install still runs, through the walk. |
548
- | `wardby_plan_incomplete` | 503 | The plan didn't finish within `REGISTRY_PLAN_TIMEOUT_MS` (or the client went away). What was verified is kept, so a retry resumes; the install still runs, through the walk. |
549
- | `wardby_bad_request` | 400 | The request path is malformed (bad percent-encoding, or not a valid package name). A client or agent bug, not a package choice. |
550
- | `wardby_upstream_error` | 502 | The upstream registry answered with an error, or the download failed partway. Retry. Also returned, as "could not be checked against this agent's approved dependency graph… try again", when the registry failed while resolving a lockfile install's dependency graph. |
551
- | `wardby_upstream_unavailable` | 504 | The upstream registry didn't answer a metadata request within `REGISTRY_METADATA_TIMEOUT_MS`. Retry. |
552
- | `wardby_metadata_too_large` | 502 | The package's metadata document is larger than `REGISTRY_MAX_METADATA_MB`. Ask the operator to raise it. |
553
- | `wardby_upstream_host_not_allowed` | 502 | The file's download URL points outside that ecosystem's own upstream hosts, so the proxy won't fetch it. |
554
- | `invalid_capability` | 401 | The run's registry token doesn't match a live session (the run has ended or the token is malformed). Not something a package choice can fix. |
1466
+ npm prints the proxy's error body verbatim, so these are what you'll see on
1467
+ a failed npm install. pip and uv don't print it: `wardby-packages` lists the
1468
+ same codes for the run (see
1469
+ [Python installers](#python-installers-pip-uv-and-poetry)), and so do
1470
+ `get_run` and the pull request.
1471
+
1472
+ | Code | Status | Meaning / what to do |
1473
+ | ------------------------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1474
+ | `wardby_package_not_allowed` | 403 | The package isn't on the allowlist and wasn't declared by a package already resolved in the run (a new top-level package must be allowlisted itself), or is only reachable through a refused package. Final: the agent's owner must allowlist it. For a package the project adds directly, allowlist the package itself; allowlisting a dependent only helps for a package something already in the project depends on. For a PyPI package an extra pulls in (`psycopg-binary` for `psycopg[binary]`), name the extra on the entry instead. |
1475
+ | `wardby_package_not_allowed_pending` | 403 | Only for a download the run hasn't resolved (a lockfile install), or a metadata request after a lockfile plan of the run failed or refused an entry: the dependency walk stopped before reaching the package, so it may still be reachable. Not transient and retrying rarely helps: the owner should allowlist the package directly. |
1476
+ | `wardby_file_not_allowed` | 403 | The specific file type is never served for this ecosystem (for example a PyPI sdist). Nothing to configure; use a wheel. |
1477
+ | `wardby_version_filtered` | 404 | Every matching version is too new (younger than `minReleaseAgeDays`) or withheld by the vulnerability audit. Wait for it to age past the threshold, or lower `minReleaseAgeDays` if you understand the risk. |
1478
+ | `wardby_package_not_found` | 404 | The upstream registry has no such package name. Check the spelling (npm names are case-sensitive). |
1479
+ | `wardby_package_too_large` | 413 | The file exceeds `REGISTRY_MAX_FILE_MB`. Ask the operator to raise it if the file is legitimately larger. |
1480
+ | `wardby_package_limit` | 429 | The run hit `REGISTRY_MAX_FILES` or `REGISTRY_MAX_TOTAL_MB`. Trim what the run installs, or ask the operator to raise the limit. |
1481
+ | `wardby_audit_unavailable` | 503 | OSV couldn't be reached and `REGISTRY_AUDIT_FAIL_OPEN` isn't set. Retry, or have the operator set that flag if the outage is expected to be long. Also returned, as "could not be checked against this agent's approved dependency graph… try again", when the audit failed while resolving a lockfile install's dependency graph. |
1482
+ | `wardby_graph_incomplete` | 503 | Only for a download the run hasn't resolved (a lockfile install), or a metadata request after a lockfile plan of the run failed or refused an entry: a dependency the walk reached but timed out (`REGISTRY_GRAPH_TIMEOUT_MS`) before checking its versions, usually a slow or failing upstream for that package. Retry shortly; npm retries 5xx on its own. Never means the package is outside the graph. |
1483
+ | `wardby_graph_limit` | 403 | Only for a download the run hasn't resolved (a lockfile install), or a metadata request after a lockfile plan of the run failed or refused an entry: the dependency graph is over `REGISTRY_MAX_GRAPH_PACKAGES` for this run. This run's walk reached the limit before the package, so it may not be in the graph. Permanent for the run, so retrying won't help: the owner can allowlist the package directly, or the operator can raise the limit. |
1484
+ | `wardby_lockfile_unsupported` | 400 | The lockfile sent to `POST /-/plan` isn't one the plan reads: a `package-lock.json` with lockfileVersion 2 or 3 (and a `packages` section), a `pnpm-lock.yaml` with lockfileVersion 6 or 9, or a yarn classic or yarn 2+ `yarn.lock`; for PyPI, a `uv.lock` (version 1) or a pinned requirements file (a Poetry `poetry.lock` or a `pylock.toml` is refused). Regenerate it with a current client. The install still runs, but downloads and, for the rest of the run, metadata requests fall back to the slower walk. |
1485
+ | `wardby_lockfile_too_large` | 413 | The lockfile is over 20 MiB or has more than `REGISTRY_PLAN_MAX_ENTRIES` entries. The install still runs, but downloads and, for the rest of the run, metadata requests fall back to the walk. |
1486
+ | `wardby_lockfile_integrity_mismatch` | — | A plan refusal: the lockfile's `integrity` for this entry isn't the registry's own (or the registry publishes none, or the lockfile records none; a yarn 2+ `yarn.lock` is never checked this way, see [pnpm and yarn](#pnpm-and-yarn)). Regenerate the lockfile entry; never edit integrity by hand. |
1487
+ | `wardby_lockfile_entry_unsupported` | — | A plan refusal: the entry is installed from git, a URL or a path, or isn't an exact registry version. Only registry packages can be approved. |
1488
+ | `wardby_plan_limit` | 429 | The run has made `REGISTRY_PLAN_MAX_PER_RUN` lockfile plans. The install still runs, but downloads and, for the rest of the run, metadata requests fall back to the walk. |
1489
+ | `wardby_registry_integrity_missing` | — | A plan refusal: npm publishes no integrity for this version, so a download of it couldn't be checked. |
1490
+ | `wardby_plan_in_progress` | 429 | A lockfile plan is already running for this run (one at a time). Wait for it; the install still runs, but downloads and, for the rest of the run, metadata requests fall back to the walk. |
1491
+ | `wardby_plan_incomplete` | 503 | The plan didn't finish within `REGISTRY_PLAN_TIMEOUT_MS` (or the client went away), or a `pnpm-lock.yaml` or `yarn.lock` wasn't read within 15 seconds (see [pnpm and yarn](#pnpm-and-yarn); a retry of that lockfile won't help). What was verified is kept, so a retry resumes; the install still runs, but downloads and, for the rest of the run, metadata requests fall back to the walk. |
1492
+ | `wardby_bad_request` | 400 | The request path is malformed (bad percent-encoding, or not a valid package name). A client or agent bug, not a package choice. |
1493
+ | `wardby_upstream_error` | 502 | The upstream registry answered with an error, or the download failed partway. Retry. Also returned, as "could not be checked against this agent's approved dependency graph… try again", when the registry failed while resolving a lockfile install's dependency graph. |
1494
+ | `wardby_upstream_unavailable` | 504 | The upstream registry didn't answer a metadata request within `REGISTRY_METADATA_TIMEOUT_MS`. Retry. |
1495
+ | `wardby_metadata_too_large` | 502 | The package's metadata document is larger than `REGISTRY_MAX_METADATA_MB`. Ask the operator to raise it. |
1496
+ | `wardby_upstream_host_not_allowed` | 502 | The file's download URL points outside that ecosystem's own upstream hosts, so the proxy won't fetch it. |
1497
+ | `invalid_capability` | 401 | The run's registry token doesn't match a live session (the run has ended or the token is malformed). Not something a package choice can fix. |
555
1498
 
556
1499
  ## Adding an ecosystem
557
1500