@wardby/cli 0.5.3 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (763) hide show
  1. package/.env.example +33 -0
  2. package/README.md +25 -14
  3. package/deploy/local/docker-compose.quickstart-coding.yml +13 -0
  4. package/dist/claude-coding-worker/context.d.ts +22 -0
  5. package/dist/claude-coding-worker/context.js +92 -0
  6. package/dist/claude-coding-worker/driver.d.ts +27 -3
  7. package/dist/claude-coding-worker/driver.js +161 -20
  8. package/dist/claude-coding-worker/main.js +34 -6
  9. package/dist/claude-coding-worker/sdk.js +25 -5
  10. package/dist/claude-coding-worker/tool-socket.d.ts +27 -0
  11. package/dist/claude-coding-worker/tool-socket.js +111 -0
  12. package/dist/claude-coding-worker/transcript.d.ts +18 -0
  13. package/dist/claude-coding-worker/transcript.js +77 -0
  14. package/dist/cli-help.d.ts +1 -1
  15. package/dist/cli-help.js +6 -2
  16. package/dist/cli-native-mode.d.ts +9 -0
  17. package/dist/cli-native-mode.js +20 -0
  18. package/dist/cli.d.ts +3 -1
  19. package/dist/cli.js +161 -11
  20. package/dist/coding/claude-context.d.ts +45 -0
  21. package/dist/coding/claude-context.js +434 -0
  22. package/dist/coding/continuation-wording.d.ts +35 -0
  23. package/dist/coding/continuation-wording.js +73 -0
  24. package/dist/coding/linked-issue-note.d.ts +23 -0
  25. package/dist/coding/linked-issue-note.js +47 -0
  26. package/dist/coding/merge-wording.d.ts +28 -0
  27. package/dist/coding/merge-wording.js +78 -0
  28. package/dist/coding/pr-description-note.d.ts +9 -0
  29. package/dist/coding/pr-description-note.js +32 -0
  30. package/dist/coding/profile.d.ts +50 -118
  31. package/dist/coding/profile.js +51 -4
  32. package/dist/coding/protected-path-wording.d.ts +8 -0
  33. package/dist/coding/protected-path-wording.js +1 -1
  34. package/dist/coding/protocol.d.ts +292 -270
  35. package/dist/coding/protocol.js +322 -15
  36. package/dist/coding/redact.d.ts +3 -0
  37. package/dist/coding/redact.js +12 -0
  38. package/dist/coding/registry/allowlist.d.ts +12 -0
  39. package/dist/coding/registry/allowlist.js +14 -1
  40. package/dist/coding/registry/audit-mode.d.ts +153 -0
  41. package/dist/coding/registry/audit-mode.js +304 -0
  42. package/dist/coding/registry/lockfile-shared.d.ts +36 -0
  43. package/dist/coding/registry/lockfile-shared.js +128 -0
  44. package/dist/coding/registry/lockfiles.js +1 -1
  45. package/dist/coding/registry/npm-lockfile-formats.d.ts +11 -0
  46. package/dist/coding/registry/npm-lockfile-formats.js +41 -0
  47. package/dist/coding/registry/npm-lockfile.d.ts +8 -4
  48. package/dist/coding/registry/npm-lockfile.js +4 -6
  49. package/dist/coding/registry/npm.js +78 -8
  50. package/dist/coding/registry/pnpm-lockfile.d.ts +17 -0
  51. package/dist/coding/registry/pnpm-lockfile.js +189 -0
  52. package/dist/coding/registry/pypi-plan.d.ts +50 -0
  53. package/dist/coding/registry/pypi-plan.js +406 -0
  54. package/dist/coding/registry/pypi-requirements.d.ts +58 -0
  55. package/dist/coding/registry/pypi-requirements.js +155 -0
  56. package/dist/coding/registry/pypi.d.ts +3 -27
  57. package/dist/coding/registry/pypi.js +40 -154
  58. package/dist/coding/registry/refusal-hints.d.ts +20 -0
  59. package/dist/coding/registry/refusal-hints.js +61 -0
  60. package/dist/coding/registry/refusal-wording.d.ts +11 -0
  61. package/dist/coding/registry/refusal-wording.js +56 -0
  62. package/dist/coding/registry/report.d.ts +70 -8
  63. package/dist/coding/registry/report.js +112 -5
  64. package/dist/coding/registry/types.d.ts +58 -8
  65. package/dist/coding/registry/yarn-lockfile.d.ts +30 -0
  66. package/dist/coding/registry/yarn-lockfile.js +367 -0
  67. package/dist/coding/services/catalog.d.ts +37 -351
  68. package/dist/coding/transcript/claude-code.d.ts +13 -0
  69. package/dist/coding/transcript/claude-code.js +273 -0
  70. package/dist/coding/transcript/download-protocol.d.ts +30 -0
  71. package/dist/coding/transcript/download-protocol.js +18 -0
  72. package/dist/coding/transcript/format.d.ts +20 -0
  73. package/dist/coding/transcript/format.js +32 -0
  74. package/dist/coding/transcript/recorder.d.ts +51 -0
  75. package/dist/coding/transcript/recorder.js +225 -0
  76. package/dist/coding/transcript/resume/checkout.d.ts +31 -0
  77. package/dist/coding/transcript/resume/checkout.js +203 -0
  78. package/dist/coding/transcript/resume/command.d.ts +17 -0
  79. package/dist/coding/transcript/resume/command.js +176 -0
  80. package/dist/coding/transcript/resume/download.d.ts +20 -0
  81. package/dist/coding/transcript/resume/download.js +153 -0
  82. package/dist/coding/transcript/resume/launch.d.ts +38 -0
  83. package/dist/coding/transcript/resume/launch.js +128 -0
  84. package/dist/coding/transcript/resume/session-file.d.ts +13 -0
  85. package/dist/coding/transcript/resume/session-file.js +79 -0
  86. package/dist/coding/transcript/resume/stand-in.d.ts +11 -0
  87. package/dist/coding/transcript/resume/stand-in.js +49 -0
  88. package/dist/coding-worker/artifact.d.ts +16 -3
  89. package/dist/coding-worker/artifact.js +5 -3
  90. package/dist/coding-worker/debug-trace.d.ts +6 -5
  91. package/dist/coding-worker/debug-trace.js +13 -18
  92. package/dist/coding-worker/dependency-cache.d.mts +229 -0
  93. package/dist/coding-worker/dependency-cache.mjs +2123 -0
  94. package/dist/coding-worker/driver.d.ts +4 -3
  95. package/dist/coding-worker/driver.js +118 -38
  96. package/dist/coding-worker/errors.d.ts +1 -0
  97. package/dist/coding-worker/errors.js +32 -0
  98. package/dist/coding-worker/sdk.d.ts +6 -0
  99. package/dist/coding-worker/sdk.js +1 -0
  100. package/dist/coding-worker/skills.d.ts +23 -0
  101. package/dist/coding-worker/skills.js +117 -0
  102. package/dist/coding-worker/task-note.d.ts +11 -0
  103. package/dist/coding-worker/task-note.js +13 -0
  104. package/dist/coding-worker/types.d.ts +13 -0
  105. package/dist/config/dependency-cache.d.ts +23 -0
  106. package/dist/config/dependency-cache.js +32 -0
  107. package/dist/config/github-allowed-accounts.d.ts +15 -0
  108. package/dist/config/github-allowed-accounts.js +36 -0
  109. package/dist/config/low-power.d.ts +12 -0
  110. package/dist/config/low-power.js +18 -0
  111. package/dist/config/mobile.d.ts +12 -0
  112. package/dist/config/mobile.js +18 -0
  113. package/dist/config/providers.d.ts +65 -3
  114. package/dist/config/providers.js +147 -11
  115. package/dist/config/test-simulators.d.ts +82 -0
  116. package/dist/config/test-simulators.js +305 -0
  117. package/dist/core/async-context.d.ts +2 -0
  118. package/dist/core/async-context.js +12 -0
  119. package/dist/core/budget-events.d.ts +108 -0
  120. package/dist/core/budget-events.js +346 -0
  121. package/dist/core/budget-groups.d.ts +50 -1
  122. package/dist/core/budget-groups.js +236 -27
  123. package/dist/core/budget-stopped-head.d.ts +46 -0
  124. package/dist/core/budget-stopped-head.js +81 -0
  125. package/dist/core/budget-topup.d.ts +51 -0
  126. package/dist/core/budget-topup.js +156 -0
  127. package/dist/core/budget-wording.d.ts +16 -2
  128. package/dist/core/budget-wording.js +40 -2
  129. package/dist/core/coding-queue.d.ts +2 -1
  130. package/dist/core/coding-queue.js +7 -0
  131. package/dist/core/cron.d.ts +19 -1
  132. package/dist/core/cron.js +17 -0
  133. package/dist/core/datastores.js +1 -1
  134. package/dist/core/db.d.ts +16 -6
  135. package/dist/core/db.js +12 -10
  136. package/dist/core/deferred-coding.d.ts +14 -0
  137. package/dist/core/deferred-coding.js +62 -0
  138. package/dist/core/delegation-child.d.ts +25 -0
  139. package/dist/core/delegation-child.js +37 -0
  140. package/dist/core/dispatch.d.ts +53 -0
  141. package/dist/core/dispatch.js +167 -57
  142. package/dist/core/engine-native.js +10 -6
  143. package/dist/core/error-code.d.ts +38 -0
  144. package/dist/core/error-code.js +168 -0
  145. package/dist/core/grants.d.ts +19 -6
  146. package/dist/core/grants.js +55 -6
  147. package/dist/core/host-events.d.ts +67 -5
  148. package/dist/core/host-events.js +571 -37
  149. package/dist/core/host-status.d.ts +85 -3
  150. package/dist/core/host-status.js +298 -20
  151. package/dist/core/issue-bridge.d.ts +50 -6
  152. package/dist/core/issue-bridge.js +151 -20
  153. package/dist/core/issue-events.d.ts +1 -1
  154. package/dist/core/issue-events.js +16 -11
  155. package/dist/core/issue-status.d.ts +1 -1
  156. package/dist/core/issue-status.js +68 -29
  157. package/dist/core/logger.d.ts +5 -0
  158. package/dist/core/logger.js +14 -1
  159. package/dist/core/lost-run-children.d.ts +6 -0
  160. package/dist/core/lost-run-children.js +32 -0
  161. package/dist/core/mark-ready.d.ts +34 -0
  162. package/dist/core/mark-ready.js +152 -0
  163. package/dist/core/markdown-code.d.ts +8 -0
  164. package/dist/core/markdown-code.js +107 -0
  165. package/dist/core/memory-tools.js +7 -0
  166. package/dist/core/merge-order-check.d.ts +130 -0
  167. package/dist/core/merge-order-check.js +343 -0
  168. package/dist/core/notification-dispatcher.d.ts +70 -0
  169. package/dist/core/notification-dispatcher.js +368 -0
  170. package/dist/core/notification-templates.d.ts +19 -0
  171. package/dist/core/notification-templates.js +139 -0
  172. package/dist/core/notifications.d.ts +10 -0
  173. package/dist/core/notifications.js +38 -0
  174. package/dist/core/pg-auth.d.ts +22 -0
  175. package/dist/core/pg-auth.js +152 -0
  176. package/dist/core/power-composition.d.ts +16 -0
  177. package/dist/core/power-composition.js +37 -0
  178. package/dist/core/power-idle.d.ts +14 -0
  179. package/dist/core/power-idle.js +30 -0
  180. package/dist/core/power.d.ts +71 -0
  181. package/dist/core/power.js +202 -0
  182. package/dist/core/pull-request-state-sync.js +2 -2
  183. package/dist/core/reconciler.d.ts +8 -5
  184. package/dist/core/reconciler.js +37 -6
  185. package/dist/core/related-pull-requests.d.ts +59 -7
  186. package/dist/core/related-pull-requests.js +140 -25
  187. package/dist/core/reply-parts.d.ts +21 -0
  188. package/dist/core/reply-parts.js +73 -0
  189. package/dist/core/repo-access.d.ts +15 -0
  190. package/dist/core/repo-access.js +66 -0
  191. package/dist/core/review-fix.d.ts +38 -1
  192. package/dist/core/review-fix.js +209 -7
  193. package/dist/core/review-host-checks.d.ts +1 -1
  194. package/dist/core/review-host-tools.d.ts +23 -0
  195. package/dist/core/review-host-tools.js +156 -14
  196. package/dist/core/review-reads.d.ts +55 -0
  197. package/dist/core/review-reads.js +233 -0
  198. package/dist/core/run-status.d.ts +12 -0
  199. package/dist/core/run-status.js +12 -0
  200. package/dist/core/runner.d.ts +384 -5
  201. package/dist/core/runner.js +1244 -372
  202. package/dist/core/schedule-start.d.ts +12 -0
  203. package/dist/core/schedule-start.js +14 -0
  204. package/dist/core/secret-broker-config.d.ts +39 -0
  205. package/dist/core/secret-broker-config.js +112 -0
  206. package/dist/core/secret-guard.d.ts +46 -0
  207. package/dist/core/secret-guard.js +104 -0
  208. package/dist/core/secret-guarded-stores.d.ts +12 -0
  209. package/dist/core/secret-guarded-stores.js +41 -0
  210. package/dist/core/secret-spans.d.ts +50 -0
  211. package/dist/core/secret-spans.js +94 -0
  212. package/dist/core/secrets.d.ts +39 -4
  213. package/dist/core/secrets.js +148 -40
  214. package/dist/core/stop-run.d.ts +46 -0
  215. package/dist/core/stop-run.js +61 -0
  216. package/dist/core/workflow-event-recorder.d.ts +14 -0
  217. package/dist/core/workflow-event-recorder.js +120 -0
  218. package/dist/core/workflow-events.d.ts +94 -0
  219. package/dist/core/workflow-events.js +48 -0
  220. package/dist/core/workflow-run-events.d.ts +18 -0
  221. package/dist/core/workflow-run-events.js +75 -0
  222. package/dist/core/zod-json-schema.d.ts +4 -0
  223. package/dist/core/zod-json-schema.js +39 -0
  224. package/dist/core/zod3-window.d.ts +35 -0
  225. package/dist/core/zod3-window.js +124 -0
  226. package/dist/generated/prisma/browser.d.ts +92 -3
  227. package/dist/generated/prisma/client.d.ts +92 -3
  228. package/dist/generated/prisma/commonInputTypes.d.ts +364 -54
  229. package/dist/generated/prisma/enums.d.ts +37 -0
  230. package/dist/generated/prisma/enums.js +31 -0
  231. package/dist/generated/prisma/internal/class.d.ts +132 -0
  232. package/dist/generated/prisma/internal/class.js +4 -4
  233. package/dist/generated/prisma/internal/prismaNamespace.d.ts +1332 -142
  234. package/dist/generated/prisma/internal/prismaNamespace.js +227 -5
  235. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +234 -0
  236. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +227 -5
  237. package/dist/generated/prisma/models/Agent.d.ts +464 -7
  238. package/dist/generated/prisma/models/AgentRepository.d.ts +42 -1
  239. package/dist/generated/prisma/models/CodingAgentProfile.d.ts +301 -1
  240. package/dist/generated/prisma/models/CodingProxyRequest.d.ts +207 -3
  241. package/dist/generated/prisma/models/CodingProxySession.d.ts +114 -1
  242. package/dist/generated/prisma/models/CodingRun.d.ts +1841 -1
  243. package/dist/generated/prisma/models/DeferredReview.d.ts +78 -1
  244. package/dist/generated/prisma/models/DependencySnapshot.d.ts +1668 -0
  245. package/dist/generated/prisma/models/DependencySnapshot.js +1 -0
  246. package/dist/generated/prisma/models/IssuePullRequest.d.ts +6 -2
  247. package/dist/generated/prisma/models/LocalReview.d.ts +40 -1
  248. package/dist/generated/prisma/models/NativeGatewayCall.d.ts +1713 -0
  249. package/dist/generated/prisma/models/NativeGatewayCall.js +1 -0
  250. package/dist/generated/prisma/models/NativeGatewaySession.d.ts +1551 -0
  251. package/dist/generated/prisma/models/NativeGatewaySession.js +1 -0
  252. package/dist/generated/prisma/models/NativeWarmWorker.d.ts +1296 -0
  253. package/dist/generated/prisma/models/NativeWarmWorker.js +1 -0
  254. package/dist/generated/prisma/models/NotificationChannel.d.ts +1543 -0
  255. package/dist/generated/prisma/models/NotificationChannel.js +1 -0
  256. package/dist/generated/prisma/models/NotificationDelivery.d.ts +1502 -0
  257. package/dist/generated/prisma/models/NotificationDelivery.js +1 -0
  258. package/dist/generated/prisma/models/NotificationThread.d.ts +1090 -0
  259. package/dist/generated/prisma/models/NotificationThread.js +1 -0
  260. package/dist/generated/prisma/models/RegistryAllowance.d.ts +41 -1
  261. package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +75 -1
  262. package/dist/generated/prisma/models/RegistryAuditRecord.d.ts +1364 -0
  263. package/dist/generated/prisma/models/RegistryAuditRecord.js +1 -0
  264. package/dist/generated/prisma/models/Run.d.ts +1619 -58
  265. package/dist/generated/prisma/models/RunBudgetEvent.d.ts +1211 -0
  266. package/dist/generated/prisma/models/RunBudgetEvent.js +1 -0
  267. package/dist/generated/prisma/models/RunHostCheck.d.ts +36 -1
  268. package/dist/generated/prisma/models/RunTranscript.d.ts +1339 -0
  269. package/dist/generated/prisma/models/RunTranscript.js +1 -0
  270. package/dist/generated/prisma/models/Secret.d.ts +37 -1
  271. package/dist/generated/prisma/models/SecretBrokerChange.d.ts +1101 -0
  272. package/dist/generated/prisma/models/SecretBrokerChange.js +1 -0
  273. package/dist/generated/prisma/models/WorkflowEvent.d.ts +1422 -0
  274. package/dist/generated/prisma/models/WorkflowEvent.js +1 -0
  275. package/dist/generated/prisma/models.d.ts +12 -0
  276. package/dist/help/build.js +4 -2
  277. package/dist/help/catalog.d.ts +1 -1
  278. package/dist/help/catalog.js +31 -6
  279. package/dist/help-index.json +2584 -146
  280. package/dist/import/create.js +49 -3
  281. package/dist/import/neutral-schema.d.ts +50 -277
  282. package/dist/import/neutral-schema.js +7 -4
  283. package/dist/knowledge/check.d.ts +1 -1
  284. package/dist/knowledge/cli.d.ts +11 -1
  285. package/dist/knowledge/cli.js +37 -4
  286. package/dist/knowledge/concept.d.ts +3 -19
  287. package/dist/knowledge/location.d.ts +31 -0
  288. package/dist/knowledge/location.js +78 -0
  289. package/dist/mcp/auth/access.d.ts +11 -2
  290. package/dist/mcp/auth/access.js +15 -1
  291. package/dist/mcp/auth/ownership.d.ts +7 -7
  292. package/dist/mcp/auth/repo-authorization.js +14 -0
  293. package/dist/mcp/auth/resource-server.d.ts +2 -1
  294. package/dist/mcp/auth/resource-server.js +7 -2
  295. package/dist/mcp/auth/self-hosted/browser.d.ts +1 -1
  296. package/dist/mcp/auth/self-hosted/browser.js +19 -6
  297. package/dist/mcp/auth/self-hosted/credentials.d.ts +3 -3
  298. package/dist/mcp/auth/self-hosted/session.d.ts +2 -2
  299. package/dist/mcp/context.d.ts +6 -0
  300. package/dist/mcp/host-events/github-ingress.d.ts +25 -1
  301. package/dist/mcp/host-events/github-ingress.js +70 -1
  302. package/dist/mcp/index.d.ts +19 -0
  303. package/dist/mcp/index.js +117 -20
  304. package/dist/mcp/server.d.ts +13 -0
  305. package/dist/mcp/server.js +33 -14
  306. package/dist/mcp/tools/agents.d.ts +4 -0
  307. package/dist/mcp/tools/agents.js +277 -24
  308. package/dist/mcp/tools/datastore.js +17 -8
  309. package/dist/mcp/tools/grants.js +9 -3
  310. package/dist/mcp/tools/memory.js +11 -1
  311. package/dist/mcp/tools/notification-channels.d.ts +2 -0
  312. package/dist/mcp/tools/notification-channels.js +293 -0
  313. package/dist/mcp/tools/packages.d.ts +2 -0
  314. package/dist/mcp/tools/packages.js +195 -0
  315. package/dist/mcp/tools/power.d.ts +2 -0
  316. package/dist/mcp/tools/power.js +77 -0
  317. package/dist/mcp/tools/repositories.js +18 -5
  318. package/dist/mcp/tools/run-access.d.ts +16 -0
  319. package/dist/mcp/tools/run-access.js +25 -0
  320. package/dist/mcp/tools/run-transcript-download.d.ts +30 -0
  321. package/dist/mcp/tools/run-transcript-download.js +129 -0
  322. package/dist/mcp/tools/run-transcript.d.ts +19 -0
  323. package/dist/mcp/tools/run-transcript.js +70 -0
  324. package/dist/mcp/tools/runs.d.ts +0 -6
  325. package/dist/mcp/tools/runs.js +41 -14
  326. package/dist/mcp/tools/scheduling.js +3 -1
  327. package/dist/mcp/tools/secret-broker-compat.d.ts +12 -0
  328. package/dist/mcp/tools/secret-broker-compat.js +23 -0
  329. package/dist/mcp/tools/secret-elicitation-form.d.ts +22 -2
  330. package/dist/mcp/tools/secret-elicitation-form.js +205 -10
  331. package/dist/mcp/tools/secret-elicitation-server.d.ts +6 -0
  332. package/dist/mcp/tools/secret-elicitation-server.js +10 -54
  333. package/dist/mcp/tools/secret-elicitation.d.ts +38 -3
  334. package/dist/mcp/tools/secret-elicitation.js +110 -12
  335. package/dist/mcp/tools/secrets.js +186 -9
  336. package/dist/mcp/tools/stop-run.d.ts +2 -0
  337. package/dist/mcp/tools/stop-run.js +82 -0
  338. package/dist/mcp/tools/subagents.js +2 -2
  339. package/dist/mcp/tools/tools.d.ts +2 -2
  340. package/dist/mcp/tools/tools.js +47 -11
  341. package/dist/mcp/transport/stdio-link-host.d.ts +20 -0
  342. package/dist/mcp/transport/stdio-link-host.js +67 -0
  343. package/dist/mcp/transport/streamable-http.d.ts +13 -0
  344. package/dist/mcp/transport/streamable-http.js +41 -1
  345. package/dist/native-worker/composition.d.ts +23 -0
  346. package/dist/native-worker/composition.js +77 -0
  347. package/dist/native-worker/docker-isolation.d.ts +65 -0
  348. package/dist/native-worker/docker-isolation.js +183 -0
  349. package/dist/native-worker/docker-launcher.d.ts +110 -0
  350. package/dist/native-worker/docker-launcher.js +297 -0
  351. package/dist/native-worker/gateway-server.d.ts +77 -0
  352. package/dist/native-worker/gateway-server.js +412 -0
  353. package/dist/native-worker/gateway.d.ts +70 -0
  354. package/dist/native-worker/gateway.js +263 -0
  355. package/dist/native-worker/http-server.d.ts +24 -0
  356. package/dist/native-worker/http-server.js +113 -0
  357. package/dist/native-worker/http-transport.d.ts +17 -0
  358. package/dist/native-worker/http-transport.js +88 -0
  359. package/dist/native-worker/kubernetes-isolation.d.ts +90 -0
  360. package/dist/native-worker/kubernetes-isolation.js +218 -0
  361. package/dist/native-worker/kubernetes-launcher.d.ts +106 -0
  362. package/dist/native-worker/kubernetes-launcher.js +375 -0
  363. package/dist/native-worker/launch.d.ts +62 -0
  364. package/dist/native-worker/launch.js +49 -0
  365. package/dist/native-worker/ledger.d.ts +125 -0
  366. package/dist/native-worker/ledger.js +245 -0
  367. package/dist/native-worker/loopback.d.ts +12 -0
  368. package/dist/native-worker/loopback.js +44 -0
  369. package/dist/native-worker/main.d.ts +8 -0
  370. package/dist/native-worker/main.js +85 -0
  371. package/dist/native-worker/protocol.d.ts +296 -0
  372. package/dist/native-worker/protocol.js +246 -0
  373. package/dist/native-worker/sandbox-executor.d.ts +83 -0
  374. package/dist/native-worker/sandbox-executor.js +284 -0
  375. package/dist/native-worker/stderr-redaction.d.ts +77 -0
  376. package/dist/native-worker/stderr-redaction.js +237 -0
  377. package/dist/native-worker/stdio.d.ts +29 -0
  378. package/dist/native-worker/stdio.js +189 -0
  379. package/dist/native-worker/warm-delivery.d.ts +16 -0
  380. package/dist/native-worker/warm-delivery.js +33 -0
  381. package/dist/native-worker/warm-pool-ledger.d.ts +39 -0
  382. package/dist/native-worker/warm-pool-ledger.js +73 -0
  383. package/dist/native-worker/warm-pool.d.ts +107 -0
  384. package/dist/native-worker/warm-pool.js +283 -0
  385. package/dist/native-worker/worker.d.ts +17 -0
  386. package/dist/native-worker/worker.js +141 -0
  387. package/dist/observability/metrics.js +6 -1
  388. package/dist/providers/auth/authorization-server.d.ts +3 -0
  389. package/dist/providers/auth/index.js +2 -0
  390. package/dist/providers/auth/self-hosted.d.ts +14 -2
  391. package/dist/providers/auth/self-hosted.js +54 -4
  392. package/dist/providers/chat/fake.d.ts +46 -0
  393. package/dist/providers/chat/fake.js +39 -0
  394. package/dist/providers/chat/index.d.ts +4 -0
  395. package/dist/providers/chat/index.js +8 -0
  396. package/dist/providers/chat/slack.d.ts +37 -0
  397. package/dist/providers/chat/slack.js +105 -0
  398. package/dist/providers/chat/types.d.ts +42 -0
  399. package/dist/providers/chat/types.js +19 -0
  400. package/dist/providers/coding-proxy/dependency-cache/eviction.d.ts +29 -0
  401. package/dist/providers/coding-proxy/dependency-cache/eviction.js +87 -0
  402. package/dist/providers/coding-proxy/dependency-cache/key.d.ts +33 -0
  403. package/dist/providers/coding-proxy/dependency-cache/key.js +86 -0
  404. package/dist/providers/coding-proxy/dependency-cache/model-request-seal.d.ts +18 -0
  405. package/dist/providers/coding-proxy/dependency-cache/model-request-seal.js +40 -0
  406. package/dist/providers/coding-proxy/dependency-cache/prisma-store.d.ts +25 -0
  407. package/dist/providers/coding-proxy/dependency-cache/prisma-store.js +131 -0
  408. package/dist/providers/coding-proxy/dependency-cache/service.d.ts +70 -0
  409. package/dist/providers/coding-proxy/dependency-cache/service.js +409 -0
  410. package/dist/providers/coding-proxy/dependency-cache/store.d.ts +85 -0
  411. package/dist/providers/coding-proxy/dependency-cache/store.js +95 -0
  412. package/dist/providers/coding-proxy/hold.d.ts +32 -0
  413. package/dist/providers/coding-proxy/hold.js +86 -0
  414. package/dist/providers/coding-proxy/interrupted.d.ts +70 -0
  415. package/dist/providers/coding-proxy/interrupted.js +104 -0
  416. package/dist/providers/coding-proxy/memory-ledger.d.ts +15 -2
  417. package/dist/providers/coding-proxy/memory-ledger.js +84 -8
  418. package/dist/providers/coding-proxy/metering.d.ts +16 -0
  419. package/dist/providers/coding-proxy/metering.js +27 -4
  420. package/dist/providers/coding-proxy/prisma-ledger.d.ts +15 -2
  421. package/dist/providers/coding-proxy/prisma-ledger.js +179 -67
  422. package/dist/providers/coding-proxy/proxy.d.ts +14 -1
  423. package/dist/providers/coding-proxy/proxy.js +166 -14
  424. package/dist/providers/coding-proxy/registry/audit.d.ts +53 -10
  425. package/dist/providers/coding-proxy/registry/audit.js +94 -17
  426. package/dist/providers/coding-proxy/registry/lockfile-parse-worker.d.ts +36 -0
  427. package/dist/providers/coding-proxy/registry/lockfile-parse-worker.js +67 -0
  428. package/dist/providers/coding-proxy/registry/lockfile-parse.d.ts +16 -0
  429. package/dist/providers/coding-proxy/registry/lockfile-parse.js +110 -0
  430. package/dist/providers/coding-proxy/registry/plan.d.ts +9 -0
  431. package/dist/providers/coding-proxy/registry/plan.js +201 -48
  432. package/dist/providers/coding-proxy/registry/prisma-store.d.ts +11 -5
  433. package/dist/providers/coding-proxy/registry/prisma-store.js +120 -17
  434. package/dist/providers/coding-proxy/registry/service.d.ts +97 -16
  435. package/dist/providers/coding-proxy/registry/service.js +579 -112
  436. package/dist/providers/coding-proxy/registry/store.d.ts +84 -9
  437. package/dist/providers/coding-proxy/registry/store.js +54 -1
  438. package/dist/providers/coding-proxy/runtime.d.ts +4 -0
  439. package/dist/providers/coding-proxy/runtime.js +78 -16
  440. package/dist/providers/coding-proxy/server.d.ts +6 -1
  441. package/dist/providers/coding-proxy/server.js +45 -1
  442. package/dist/providers/coding-proxy/types.d.ts +65 -1
  443. package/dist/providers/datastore/types.d.ts +5 -0
  444. package/dist/providers/engine/types.d.ts +36 -2
  445. package/dist/providers/engine/types.js +39 -0
  446. package/dist/providers/executor/composition.d.ts +31 -1
  447. package/dist/providers/executor/composition.js +81 -8
  448. package/dist/providers/executor/container.d.ts +385 -9
  449. package/dist/providers/executor/container.js +1234 -99
  450. package/dist/providers/executor/dbos-status.d.ts +31 -0
  451. package/dist/providers/executor/dbos-status.js +64 -0
  452. package/dist/providers/executor/dbos.d.ts +91 -0
  453. package/dist/providers/executor/dbos.js +319 -26
  454. package/dist/providers/executor/deferred.d.ts +44 -0
  455. package/dist/providers/executor/deferred.js +100 -0
  456. package/dist/providers/executor/in-process.d.ts +9 -1
  457. package/dist/providers/executor/in-process.js +14 -4
  458. package/dist/providers/executor/linked-issue-reader.d.ts +16 -0
  459. package/dist/providers/executor/linked-issue-reader.js +30 -0
  460. package/dist/providers/executor/routing.d.ts +39 -10
  461. package/dist/providers/executor/routing.js +77 -32
  462. package/dist/providers/executor/types.d.ts +25 -0
  463. package/dist/providers/index.d.ts +2 -1
  464. package/dist/providers/issue-tracker/types.d.ts +17 -0
  465. package/dist/providers/issue-tracker/types.js +39 -1
  466. package/dist/providers/jobs/claude-tool-setup.d.ts +4 -0
  467. package/dist/providers/jobs/claude-tool-setup.js +5 -2
  468. package/dist/providers/jobs/docker-isolation.d.ts +8 -0
  469. package/dist/providers/jobs/docker-isolation.js +17 -0
  470. package/dist/providers/jobs/docker.d.ts +1 -0
  471. package/dist/providers/jobs/docker.js +29 -4
  472. package/dist/providers/jobs/fake-kubernetes-api.d.ts +23 -2
  473. package/dist/providers/jobs/fake-kubernetes-api.js +45 -1
  474. package/dist/providers/jobs/kubernetes-api.d.ts +25 -1
  475. package/dist/providers/jobs/kubernetes-api.js +17 -0
  476. package/dist/providers/jobs/kubernetes-client.d.ts +10 -1
  477. package/dist/providers/jobs/kubernetes-client.js +61 -3
  478. package/dist/providers/jobs/kubernetes-dry-run-fixture.d.ts +6 -1
  479. package/dist/providers/jobs/kubernetes-dry-run-fixture.js +6 -3
  480. package/dist/providers/jobs/kubernetes-isolation.d.ts +5 -1
  481. package/dist/providers/jobs/kubernetes-isolation.js +23 -1
  482. package/dist/providers/jobs/kubernetes-platform.d.ts +4 -1
  483. package/dist/providers/jobs/kubernetes-platform.js +38 -1
  484. package/dist/providers/jobs/kubernetes-preflight.d.ts +2 -1
  485. package/dist/providers/jobs/kubernetes-preflight.js +4 -2
  486. package/dist/providers/jobs/kubernetes.d.ts +42 -2
  487. package/dist/providers/jobs/kubernetes.js +178 -19
  488. package/dist/providers/jobs/metadata-probes.d.ts +11 -0
  489. package/dist/providers/jobs/metadata-probes.js +13 -0
  490. package/dist/providers/jobs/types.d.ts +42 -0
  491. package/dist/providers/jobs/types.js +13 -1
  492. package/dist/providers/llm/anthropic.js +5 -1
  493. package/dist/providers/llm/bedrock.d.ts +0 -12
  494. package/dist/providers/llm/bedrock.js +4 -0
  495. package/dist/providers/llm/claude-messages.d.ts +3 -1
  496. package/dist/providers/llm/claude-messages.js +4 -1
  497. package/dist/providers/llm/claude-provider.d.ts +2 -2
  498. package/dist/providers/llm/claude-provider.js +2 -2
  499. package/dist/providers/llm/openai.d.ts +2 -2
  500. package/dist/providers/llm/openai.js +6 -2
  501. package/dist/providers/llm/routing.d.ts +2 -2
  502. package/dist/providers/llm/routing.js +2 -2
  503. package/dist/providers/llm/types.d.ts +12 -2
  504. package/dist/providers/review-host/github-events.d.ts +9 -1
  505. package/dist/providers/review-host/github-events.js +34 -12
  506. package/dist/providers/review-host/github.d.ts +39 -2
  507. package/dist/providers/review-host/github.js +274 -29
  508. package/dist/providers/review-host/local.d.ts +8 -4
  509. package/dist/providers/review-host/local.js +49 -31
  510. package/dist/providers/review-host/patch-pages.d.ts +41 -0
  511. package/dist/providers/review-host/patch-pages.js +176 -0
  512. package/dist/providers/review-host/review-format.d.ts +19 -1
  513. package/dist/providers/review-host/review-format.js +38 -7
  514. package/dist/providers/review-host/types.d.ts +157 -2
  515. package/dist/providers/review-host/types.js +8 -0
  516. package/dist/providers/storage/gcs.d.ts +63 -0
  517. package/dist/providers/storage/gcs.js +69 -0
  518. package/dist/providers/storage/index.d.ts +15 -0
  519. package/dist/providers/storage/index.js +14 -0
  520. package/dist/providers/storage/local.d.ts +24 -0
  521. package/dist/providers/storage/local.js +92 -0
  522. package/dist/providers/storage/meter.d.ts +14 -0
  523. package/dist/providers/storage/meter.js +34 -0
  524. package/dist/providers/storage/s3.d.ts +41 -0
  525. package/dist/providers/storage/s3.js +107 -0
  526. package/dist/providers/storage/types.d.ts +37 -3
  527. package/dist/providers/storage/types.js +21 -4
  528. package/dist/providers/vcs/git.d.ts +33 -2
  529. package/dist/providers/vcs/git.js +324 -22
  530. package/dist/providers/vcs/github-remote.d.ts +26 -3
  531. package/dist/providers/vcs/github-remote.js +124 -14
  532. package/dist/providers/vcs/github.d.ts +246 -12
  533. package/dist/providers/vcs/github.js +521 -61
  534. package/dist/providers/vcs/index.js +8 -1
  535. package/dist/providers/vcs/pr-description.d.ts +127 -0
  536. package/dist/providers/vcs/pr-description.js +270 -0
  537. package/dist/providers/vcs/remote.d.ts +33 -1
  538. package/dist/providers/vcs/routing.d.ts +4 -2
  539. package/dist/providers/vcs/routing.js +6 -0
  540. package/dist/providers/vcs/types.d.ts +80 -3
  541. package/dist/quickstart/coding-seed.d.ts +21 -3
  542. package/dist/quickstart/coding-seed.js +283 -3
  543. package/dist/quickstart/images.d.ts +2 -0
  544. package/dist/quickstart/images.js +7 -1
  545. package/dist/quickstart/reviewer-prompt.d.ts +1 -1
  546. package/dist/quickstart/reviewer-prompt.js +6 -6
  547. package/dist/quickstart-images.json +1 -1
  548. package/dist/sandbox/brokered-fetch.d.ts +23 -0
  549. package/dist/sandbox/brokered-fetch.js +37 -0
  550. package/dist/sandbox/generated/zod.bundle.js +62 -1
  551. package/dist/sandbox/generated/zod3.bundle.js +5 -0
  552. package/dist/sandbox/host-functions.d.ts +35 -0
  553. package/dist/sandbox/host-functions.js +150 -91
  554. package/dist/sandbox/prelude.js +7 -1
  555. package/dist/sandbox/run-in-sandbox.d.ts +19 -0
  556. package/dist/sandbox/run-in-sandbox.js +28 -10
  557. package/dist/sandbox/safe-fetch.d.ts +2 -0
  558. package/dist/sandbox/safe-fetch.js +3 -1
  559. package/dist/sandbox/secret-broker-sigv4.d.ts +2 -0
  560. package/dist/sandbox/secret-broker-sigv4.js +49 -0
  561. package/dist/sandbox/secret-broker.d.ts +34 -0
  562. package/dist/sandbox/secret-broker.js +161 -0
  563. package/dist/sandbox/tool-capabilities.d.ts +10 -30
  564. package/dist/sandbox/user-tool.d.ts +14 -0
  565. package/dist/sandbox/user-tool.js +32 -0
  566. package/dist/sandbox/zod-params.d.ts +37 -5
  567. package/dist/sandbox/zod-params.js +181 -26
  568. package/dist/serve.js +25 -0
  569. package/dist/viewer/api-schema.d.ts +372 -2454
  570. package/dist/viewer/api-schema.js +29 -0
  571. package/dist/viewer/build-schemas.js +10 -3
  572. package/dist/viewer/event-bus.d.ts +6 -0
  573. package/dist/viewer/event-bus.js +16 -7
  574. package/dist/viewer/graph.d.ts +5 -2
  575. package/dist/viewer/graph.js +7 -2
  576. package/dist/viewer/http.d.ts +0 -1
  577. package/dist/viewer/http.js +2 -2
  578. package/dist/viewer/infra.js +25 -2
  579. package/dist/viewer/limits.d.ts +4 -0
  580. package/dist/viewer/limits.js +4 -0
  581. package/dist/viewer/openapi.d.ts +15 -0
  582. package/dist/viewer/openapi.js +170 -0
  583. package/dist/wardby-bin.js +10 -0
  584. package/docs/README.md +7 -0
  585. package/docs/agent-recipes.md +114 -27
  586. package/docs/architecture-runtime.md +16 -1
  587. package/docs/code-review-agents.md +689 -45
  588. package/docs/coding-agent-setup.md +602 -12
  589. package/docs/coding-packages.md +1037 -92
  590. package/docs/coding-services.md +30 -13
  591. package/docs/coding-worker-byo-images.md +218 -6
  592. package/docs/coding-worker-isolation.md +173 -44
  593. package/docs/getting-started-gke.md +332 -129
  594. package/docs/getting-started-identity-provider.md +31 -11
  595. package/docs/getting-started.md +14 -4
  596. package/docs/jira-agents.md +22 -9
  597. package/docs/knowledge/blob-not-found-semantics.md +41 -0
  598. package/docs/knowledge/bounded-file-descriptor.md +52 -0
  599. package/docs/knowledge/delegation-admission-slots.md +52 -0
  600. package/docs/knowledge/http-dispatcher-bootstrap.md +61 -0
  601. package/docs/knowledge/import-rename-target.md +52 -0
  602. package/docs/knowledge/index.md +57 -0
  603. package/docs/knowledge/log.md +9 -0
  604. package/docs/knowledge/mcp-cancelled-task-precedence.md +41 -0
  605. package/docs/knowledge/mcp-http-prompt-discovery.md +41 -0
  606. package/docs/knowledge/mcp-schema-module-boundary.md +41 -0
  607. package/docs/knowledge/nullable-tool-uniqueness.md +52 -0
  608. package/docs/knowledge/overview-cli.md +74 -0
  609. package/docs/knowledge/overview-coding-proxy.md +72 -0
  610. package/docs/knowledge/overview-coding.md +72 -0
  611. package/docs/knowledge/overview-config.md +60 -0
  612. package/docs/knowledge/overview-core.md +72 -0
  613. package/docs/knowledge/overview-deploy.md +83 -0
  614. package/docs/knowledge/overview-mcp.md +61 -0
  615. package/docs/knowledge/overview-prisma.md +72 -0
  616. package/docs/knowledge/overview-providers.md +89 -0
  617. package/docs/knowledge/overview-sandbox.md +83 -0
  618. package/docs/knowledge/overview-surfaces.md +83 -0
  619. package/docs/knowledge/private-datastore-shared-boundary.md +54 -0
  620. package/docs/knowledge/proxy-settlement-projections.md +41 -0
  621. package/docs/knowledge/run-pricing-first-writer.md +43 -0
  622. package/docs/knowledge/schedule-claim-transaction.md +41 -0
  623. package/docs/knowledge/secret-entry-replica-contract.md +52 -0
  624. package/docs/knowledge/terminal-run-first-writer.md +41 -0
  625. package/docs/knowledge/transactional-agent-authorization.md +52 -0
  626. package/docs/knowledge/viewer-notify-write-isolation.md +30 -0
  627. package/docs/knowledge/warm-input-publication.md +30 -0
  628. package/docs/knowledge/workspace-staging-failure.md +54 -0
  629. package/docs/knowledge.md +54 -14
  630. package/docs/native-sandbox.md +731 -0
  631. package/docs/observability.md +4 -0
  632. package/docs/release-verification.md +9 -0
  633. package/docs/security-deployment.md +146 -14
  634. package/docs/slack-notifications.md +268 -0
  635. package/docs/tool-secrets.md +284 -0
  636. package/docs/viewer-api.md +46 -5
  637. package/docs/viewer-api.openapi.yml +1404 -0
  638. package/help/admin-viewer.md +29 -3
  639. package/help/agent-recipes.md +27 -0
  640. package/help/architecture-agent.md +11 -6
  641. package/help/brokered-secrets.md +79 -0
  642. package/help/build-claude-code-image.md +131 -0
  643. package/help/build-worker-image.md +6 -7
  644. package/help/builder-agent.md +19 -6
  645. package/help/code-review-agents.md +243 -10
  646. package/help/coding-packages.md +156 -6
  647. package/help/cost-attribution.md +9 -5
  648. package/help/creating-agents.md +14 -1
  649. package/help/dependency-cache.md +143 -0
  650. package/help/deploy-gke.md +23 -9
  651. package/help/deployment-targets.md +25 -14
  652. package/help/errors/budget-exhausted.md +187 -0
  653. package/help/errors/budget-group-exhausted.md +5 -1
  654. package/help/errors/coding-provider-not-configured.md +2 -2
  655. package/help/errors/coding-stream-quota-exceeded.md +33 -0
  656. package/help/errors/coding-workspace-disk-exceeds-limit.md +39 -0
  657. package/help/errors/continuation-closed.md +9 -12
  658. package/help/errors/database-auth-invalid.md +28 -0
  659. package/help/errors/delegation-conflict.md +24 -0
  660. package/help/errors/file-list-truncated.md +45 -0
  661. package/help/errors/full-read-required.md +49 -0
  662. package/help/errors/github-account-not-allowed.md +29 -0
  663. package/help/errors/graph-incomplete.md +43 -0
  664. package/help/errors/graph-limit.md +43 -0
  665. package/help/errors/low-power-disabled.md +29 -0
  666. package/help/errors/low-power-scale-forbidden.md +35 -0
  667. package/help/errors/low-power-wake-timeout.md +30 -0
  668. package/help/errors/merge-conflict.md +49 -0
  669. package/help/errors/mobile-disabled.md +31 -0
  670. package/help/errors/native-sandbox-capacity.md +22 -0
  671. package/help/errors/native-sandbox-deadline-exceeded.md +19 -0
  672. package/help/errors/native-sandbox-docker-failed.md +20 -0
  673. package/help/errors/native-sandbox-gateway-error.md +20 -0
  674. package/help/errors/native-sandbox-gateway-unavailable.md +21 -0
  675. package/help/errors/native-sandbox-image-not-pinned.md +19 -0
  676. package/help/errors/native-sandbox-isolation-mismatch.md +20 -0
  677. package/help/errors/native-sandbox-launch-abandoned.md +21 -0
  678. package/help/errors/native-sandbox-network-unenforced.md +22 -0
  679. package/help/errors/native-sandbox-requires-catalog.md +31 -0
  680. package/help/errors/native-sandbox-unavailable.md +19 -0
  681. package/help/errors/native-sandbox-warm-delivery-failed.md +21 -0
  682. package/help/errors/native-sandbox-worker-exited.md +21 -0
  683. package/help/errors/native-sandbox-worker-lost.md +20 -0
  684. package/help/errors/native-sandbox-worker-unready.md +22 -0
  685. package/help/errors/package-not-allowed-pending.md +52 -0
  686. package/help/errors/package-not-allowed.md +51 -0
  687. package/help/errors/package-refusals.md +43 -0
  688. package/help/errors/patch-cursor-malformed.md +31 -0
  689. package/help/errors/run-lost.md +39 -0
  690. package/help/errors/run-transcript-download.md +44 -0
  691. package/help/errors/secret-broker-body-invalid.md +26 -0
  692. package/help/errors/secret-broker-changed.md +20 -0
  693. package/help/errors/secret-broker-config-invalid.md +30 -0
  694. package/help/errors/secret-broker-conflict.md +27 -0
  695. package/help/errors/secret-broker-destination-denied.md +28 -0
  696. package/help/errors/secret-broker-value-invalid.md +28 -0
  697. package/help/errors/secret-brokered.md +24 -0
  698. package/help/errors/secret-in-value.md +69 -0
  699. package/help/errors/secret-not-brokered.md +25 -0
  700. package/help/errors/slack-auth-failed.md +48 -0
  701. package/help/errors/slack-channel-unreachable.md +39 -0
  702. package/help/errors/slack-not-configured.md +33 -0
  703. package/help/errors/target-merge.md +40 -0
  704. package/help/fix-merge-conflicts.md +73 -0
  705. package/help/getting-started.md +4 -5
  706. package/help/github-app-setup.md +132 -0
  707. package/help/github.md +40 -1
  708. package/help/identity-and-access.md +7 -1
  709. package/help/jira.md +23 -6
  710. package/help/knowledge.md +16 -7
  711. package/help/local-repositories.md +3 -4
  712. package/help/low-power-mode.md +131 -0
  713. package/help/merge-order-check.md +122 -0
  714. package/help/mobile-app.md +54 -0
  715. package/help/native-capabilities.md +29 -1
  716. package/help/native-sandbox.md +150 -0
  717. package/help/observability.md +8 -0
  718. package/help/operating-agents.md +5 -0
  719. package/help/package-audit-mode.md +91 -0
  720. package/help/related-pull-requests.md +80 -16
  721. package/help/repo-instructions-and-skills.md +91 -0
  722. package/help/resume-runs-locally.md +131 -0
  723. package/help/review-fix-rounds.md +43 -1
  724. package/help/sharing-agents.md +59 -0
  725. package/help/slack-notifications.md +76 -0
  726. package/help/status-comments.md +31 -0
  727. package/help/stop-run.md +58 -0
  728. package/help/troubleshooting/budgets.md +28 -0
  729. package/help/troubleshooting/coding-workers.md +99 -7
  730. package/package.json +27 -13
  731. package/prisma/migrations/20261009000003_native_execution_mode/migration.sql +12 -0
  732. package/prisma/migrations/20261009000004_native_gateway_ledger/migration.sql +65 -0
  733. package/prisma/migrations/20261009000005_native_gateway_call_child_agent/migration.sql +5 -0
  734. package/prisma/migrations/20261009000006_run_deferred_start_stop/migration.sql +14 -0
  735. package/prisma/migrations/20261009000007_native_gateway_network_ready/migration.sql +5 -0
  736. package/prisma/migrations/20261010000001_workflow_notifications/migration.sql +99 -0
  737. package/prisma/migrations/20261011000001_native_gateway_session_status_index/migration.sql +2 -0
  738. package/prisma/migrations/20261012000001_native_warm_worker/migration.sql +32 -0
  739. package/prisma/migrations/20261013000001_coding_run_merge_order/migration.sql +2 -0
  740. package/prisma/migrations/20261013000001_secret_broker/migration.sql +17 -0
  741. package/prisma/migrations/20261013000002_deferred_review_request/migration.sql +4 -0
  742. package/prisma/migrations/20261014000000_repo_skills/migration.sql +15 -0
  743. package/prisma/migrations/20261015000001_coding_run_merge_order_index/migration.sql +2 -0
  744. package/prisma/migrations/20261015000002_no_change_rereview/migration.sql +2 -0
  745. package/prisma/migrations/20261016000000_registry_approved_accepted_advisories/migration.sql +3 -0
  746. package/prisma/migrations/20261016000001_mark_ready_when_passing/migration.sql +3 -0
  747. package/prisma/migrations/20261017000000_debug_trace_max_mb/migration.sql +4 -0
  748. package/prisma/migrations/20261018000000_dependency_cache/migration.sql +53 -0
  749. package/prisma/migrations/20261019000000_claude_max_output_tokens/migration.sql +5 -0
  750. package/prisma/migrations/20261021000000_pr_description_hash/migration.sql +4 -0
  751. package/prisma/migrations/20261022000000_dependency_snapshot_validation_metadata/migration.sql +4 -0
  752. package/prisma/migrations/20261023000000_package_audit_mode/migration.sql +27 -0
  753. package/prisma/migrations/20261024000000_budget_events/migration.sql +26 -0
  754. package/prisma/migrations/20261025000000_cache_aware_holds/migration.sql +9 -0
  755. package/prisma/migrations/20261026000000_budget_overrun/migration.sql +4 -0
  756. package/prisma/migrations/20261027000000_coding_run_request/migration.sql +1 -0
  757. package/prisma/migrations/20261028000000_native_gateway_review_reads/migration.sql +6 -0
  758. package/prisma/migrations/20261029000000_lead_survives_deploy/migration.sql +6 -0
  759. package/prisma/migrations/20261030000000_local_review_covered_sha/migration.sql +2 -0
  760. package/prisma/migrations/20261031000000_coding_run_budget_limit/migration.sql +9 -0
  761. package/prisma/migrations/20261101000000_run_transcripts/migration.sql +22 -0
  762. package/prisma/schema.prisma +631 -146
  763. package/dist/sandbox/generated/zod-to-json-schema.bundle.js +0 -5
@@ -1,6 +1,13 @@
1
1
  import { createPrivateKey } from "node:crypto";
2
2
  import { importPKCS8, SignJWT } from "jose";
3
- import { normalizeGitHubRepository, normalizeGitRef } from "../../coding/protocol.js";
3
+ import { isGitHubLogin } from "../../config/github-allowed-accounts.js";
4
+ import { testSimulatorBase } from "../../config/test-simulators.js";
5
+ import { ACCEPTED_ADVISORY_ID } from "../../coding/registry/allowlist.js";
6
+ import { packageAuditRows, packageAuditWording, summarizePackageAudit, } from "../../coding/registry/audit-mode.js";
7
+ import { refusalMeaning } from "../../coding/registry/refusal-hints.js";
8
+ import { DEPENDENCY_CACHE_PATH, normalizeGitHubRepository, normalizeGitRef, truncateText, } from "../../coding/protocol.js";
9
+ import { LATEST_RUN_MARKER_PREFIX, RELATED_SECTION_END, RELATED_SECTION_START, assessFinalMessage, descriptionAppliedComment, descriptionFallbackComment, descriptionSha256, extractRelatedBlock, findDescriptionComments, hasClosingKeyword, isUneditedDescription, supersededDescriptionComment, } from "./pr-description.js";
10
+ export { RELATED_SECTION_END, RELATED_SECTION_START };
4
11
  const DEFAULT_API_BASE_URL = "https://api.github.com";
5
12
  const DEFAULT_API_VERSION = "2026-03-10";
6
13
  const RUN_MARKER_PREFIX = "<!-- wardby:";
@@ -39,6 +46,8 @@ export function isSafeGitHubInstallationToken(value) {
39
46
  * executor validates with the shared one.
40
47
  */
41
48
  export const PR_ISSUE_KEY = /^[A-Z][A-Z0-9_]{0,254}-[1-9]\d{0,9}$/;
49
+ /** Same shape as GITHUB_ISSUE_KEY (issue-tracker/types.ts), duplicated likewise. Only the Related section shows one. */
50
+ export const PR_GITHUB_ISSUE_KEY = /^[a-z0-9](?:[a-z0-9-]{0,37}[a-z0-9])?\/[a-z0-9._-]{1,100}#[1-9]\d{0,9}$/;
42
51
  /** The issue key when it is well-formed; a malformed one is dropped everywhere it would be rendered. */
43
52
  function validIssueKey(input) {
44
53
  const key = input.issue?.key;
@@ -49,22 +58,29 @@ export function pullRequestTitle(input) {
49
58
  const key = validIssueKey(input);
50
59
  return key ? `[${key}] ${base}` : base;
51
60
  }
52
- /** Validated issue parts; the tracker prefix is "" or "<Name> ". */
53
- function issueParts(issue) {
61
+ /**
62
+ * Validated issue parts; the tracker prefix is "" or "<Name> ". A GitHub issue
63
+ * key is accepted only with `orGitHub` (the Related section): the title and
64
+ * the body's issue line stay tracker-only.
65
+ */
66
+ function issueParts(issue, orGitHub = false) {
54
67
  const key = issue?.key;
55
- if (typeof key !== "string" || !PR_ISSUE_KEY.test(key))
68
+ if (typeof key !== "string" || !(PR_ISSUE_KEY.test(key) || (orGitHub && PR_GITHUB_ISSUE_KEY.test(key)))) {
56
69
  return undefined;
70
+ }
57
71
  const url = issue?.url;
58
72
  const safeUrl = typeof url === "string" && url.startsWith("https://") && !/[\s<>()]/.test(url) ? url : undefined;
59
73
  const name = issue?.trackerName;
60
74
  const tracker = typeof name === "string" && /^[A-Za-z][A-Za-z0-9 .-]{0,39}$/.test(name) ? `${name.trim()} ` : "";
61
75
  return { key, ...(safeUrl ? { url: safeUrl } : {}), tracker };
62
76
  }
77
+ /** The body's issue line: a tracker issue's link, else the same-repository issue the run came from. */
63
78
  function issueLine(input) {
64
79
  const parts = issueParts(input.issue);
65
- if (!parts?.url)
66
- return undefined;
67
- return `Resolves ${parts.tracker}issue [${parts.key}](${parts.url})`;
80
+ if (parts?.url)
81
+ return `Resolves ${parts.tracker}issue [${parts.key}](${parts.url})`;
82
+ const number = input.issueNumber;
83
+ return typeof number === "number" && Number.isSafeInteger(number) && number > 0 ? `From issue #${number}` : undefined;
68
84
  }
69
85
  /**
70
86
  * Package names/versions/reasons come from registry metadata, not from
@@ -77,8 +93,17 @@ function issueLine(input) {
77
93
  function isSafeMarkdownFragment(value) {
78
94
  return !value.includes("`") && !value.includes("\n") && !value.includes("\r");
79
95
  }
80
- export const RELATED_SECTION_START = "<!-- wardby-related:start -->";
81
- export const RELATED_SECTION_END = "<!-- wardby-related:end -->";
96
+ /** Whether a comment was written by this GitHub App (its bot user, through this App's id). */
97
+ function isThisAppsComment(comment, appId) {
98
+ const user = comment.user;
99
+ const app = comment.performed_via_github_app;
100
+ return (!!user &&
101
+ typeof user === "object" &&
102
+ user.type === "Bot" &&
103
+ !!app &&
104
+ typeof app === "object" &&
105
+ app.id === appId);
106
+ }
82
107
  /** At most this many pull requests are listed; the rest are counted. */
83
108
  export const MAX_RELATED_PULL_REQUESTS = 20;
84
109
  /** GitHub rejects a pull request body longer than this. */
@@ -110,43 +135,82 @@ const isDone = (entry) => entry.state === "merged" || entry.state === "closed";
110
135
  /**
111
136
  * The "Related pull requests" block, markers included, built only from
112
137
  * stored rows (never agent text). Open (and draft, and not-yet-known) pull
113
- * requests form the numbered suggested merge order; merged and closed ones
114
- * follow as context. Undefined when no other valid pull request is left after
115
- * validation: a set of one is not a set.
138
+ * requests form the merge order; merged and closed ones follow as context.
139
+ * Undefined when no other valid pull request is left after validation: a
140
+ * set of one is not a set.
141
+ *
142
+ * When any valid entry (open, merged or closed) carries a `mergeOrder` (set
143
+ * by the delegating agent), steps are counted over all of them, so a
144
+ * label never changes as pull requests merge: the open entries that have
145
+ * one are numbered under "Merge order" and labelled with their step (equal
146
+ * `mergeOrder` values share a step label), any open entry without one is
147
+ * listed after under "Not ordered:", and a merged or closed entry that has
148
+ * one carries its step label in the context list too. Otherwise every open
149
+ * entry is numbered in the unordered "Suggested merge order" list.
116
150
  */
117
151
  export function renderRelatedSection(input) {
118
152
  const valid = input.entries.filter((entry) => relatedLine(entry, "-") !== undefined);
119
153
  if (!valid.some((entry) => !entry.self))
120
154
  return undefined;
121
- const issue = issueParts(input.issue);
155
+ const issue = issueParts(input.issue, true);
122
156
  const issueText = issue ? ` for ${issue.tracker}issue ${issue.url ? `[${issue.key}](${issue.url})` : issue.key}` : "";
123
157
  const open = valid.filter((entry) => !isDone(entry)).slice(0, MAX_RELATED_PULL_REQUESTS);
124
158
  const done = valid.filter(isDone).slice(0, MAX_RELATED_PULL_REQUESTS - open.length);
125
159
  const more = valid.length - open.length - done.length;
160
+ const isNumber = (value) => Number.isSafeInteger(value);
161
+ // Over every valid entry, done ones included, so "step 2 of 3" stays the
162
+ // same label after step 1 merges.
163
+ const steps = [...new Set(valid.map((entry) => entry.mergeOrder).filter(isNumber))].sort((a, b) => a - b);
164
+ const stepLabel = (entry) => isNumber(entry.mergeOrder) ? ` — step ${steps.indexOf(entry.mergeOrder) + 1} of ${steps.length}` : "";
165
+ // Stable-sorted by mergeOrder so numbering and step labels are correct
166
+ // regardless of the caller's input order; unordered entries keep input order.
167
+ const ordered = steps.length > 0
168
+ ? open.filter((entry) => isNumber(entry.mergeOrder)).sort((a, b) => a.mergeOrder - b.mergeOrder)
169
+ : [];
170
+ const unordered = steps.length > 0 ? open.filter((entry) => !isNumber(entry.mergeOrder)) : [];
126
171
  return [
127
172
  RELATED_SECTION_START,
128
173
  "**Related pull requests**",
129
174
  "",
130
175
  `Wardby opened these pull requests for the same request${issueText}.`,
131
- ...(open.length > 0
176
+ ...(steps.length > 0
177
+ ? [
178
+ ...(ordered.length > 0
179
+ ? [
180
+ "",
181
+ "Merge order (set by the delegating agent; equal steps can merge in either order):",
182
+ "",
183
+ ...ordered.map((entry, index) => `${relatedLine(entry, `${index + 1}.`)}${stepLabel(entry)}`),
184
+ ]
185
+ : []),
186
+ ...(unordered.length > 0
187
+ ? ["", "Not ordered:", "", ...unordered.map((entry) => relatedLine(entry, "-"))]
188
+ : []),
189
+ ]
190
+ : open.length > 0
191
+ ? [
192
+ "",
193
+ "Suggested merge order (the order Wardby's agent opened them in; not a guarantee, so check dependencies before merging):",
194
+ "",
195
+ ...open.map((entry, index) => relatedLine(entry, `${index + 1}.`)),
196
+ ]
197
+ : []),
198
+ ...(done.length > 0
132
199
  ? [
133
200
  "",
134
- "Suggested merge order (the order Wardby's agent opened them in; not a guarantee, so check dependencies before merging):",
201
+ "Already merged or closed (context only):",
135
202
  "",
136
- ...open.map((entry, index) => relatedLine(entry, `${index + 1}.`)),
203
+ ...done.map((entry) => `${relatedLine(entry, "-")}${stepLabel(entry)}`),
137
204
  ]
138
205
  : []),
139
- ...(done.length > 0
140
- ? ["", "Already merged or closed (context only):", "", ...done.map((entry) => relatedLine(entry, "-"))]
141
- : []),
142
206
  ...(more > 0 ? ["", `…and ${more} more`] : []),
143
207
  "",
144
208
  "<sub>Written by Wardby from its run records; this section is replaced when the request's runs finish.</sub>",
145
209
  RELATED_SECTION_END,
146
210
  ].join("\n");
147
211
  }
148
- /** The run marker, then optionally blank lines and the "Resolves …" line: the section goes right after. */
149
- const BODY_HEAD = /^[^\S\n]*<!-- (?:wardby|reevo-run):\S+ -->[^\S\n]*(?:\n|$)(?:[^\S\n]*\n)*(?:Resolves [^\n]*(?:\n|$))?/;
212
+ /** The run marker, then optionally blank lines and the issue line ("Resolves … issue …" or "From issue #N"): the section goes right after. */
213
+ const BODY_HEAD = /^[^\S\n]*<!-- (?:wardby|reevo-run):\S+ -->[^\S\n]*(?:\n|$)(?:[^\S\n]*\n)*(?:(?:Resolves |From issue #\d)[^\n]*(?:\n|$))?/;
150
214
  /**
151
215
  * The body with `block` in place of the existing marked section, or inserted
152
216
  * after the run marker (and the issue line) when there is none. Null — do not
@@ -183,13 +247,16 @@ export const MAX_PR_PACKAGE_LINES = 100;
183
247
  /** A hard ceiling on each list's rendered size, whatever the entries
184
248
  * contain, so the section can never push the body past GitHub's limit. */
185
249
  const MAX_PR_PACKAGE_LIST_CHARS = 16 * 1024;
186
- /** Renders at most MAX_PR_PACKAGE_LINES lines (and at most
187
- * MAX_PR_PACKAGE_LIST_CHARS characters) of `lines`, then a count of the rest. */
188
- function cappedList(lines) {
250
+ /** The accepted-advisories list's ceiling: smaller, so adding it keeps the
251
+ * whole section's worst case well inside the body limit. */
252
+ const MAX_PR_WAIVER_LIST_CHARS = 8 * 1024;
253
+ /** Renders at most MAX_PR_PACKAGE_LINES lines (and at most `maxChars`
254
+ * characters) of `lines`, then a count of the rest. */
255
+ function cappedList(lines, maxChars = MAX_PR_PACKAGE_LIST_CHARS) {
189
256
  const shown = [];
190
257
  let chars = 0;
191
258
  for (const line of lines.slice(0, MAX_PR_PACKAGE_LINES)) {
192
- if (chars + line.length + 1 > MAX_PR_PACKAGE_LIST_CHARS)
259
+ if (chars + line.length + 1 > maxChars)
193
260
  break;
194
261
  shown.push(line);
195
262
  chars += line.length + 1;
@@ -197,6 +264,14 @@ function cappedList(lines) {
197
264
  const more = lines.length - shown.length;
198
265
  return more > 0 ? [...shown, `- …and ${more} more — see get_run for the full list`] : shown;
199
266
  }
267
+ const MAX_DEPENDENCY_CACHE_FOLDERS = 4;
268
+ function dependencyCacheLine(folders) {
269
+ const shown = [...new Set(folders ?? [])]
270
+ .filter((folder) => DEPENDENCY_CACHE_PATH.test(folder))
271
+ .slice(0, MAX_DEPENDENCY_CACHE_FOLDERS)
272
+ .map((folder) => (folder === "." ? "the repo root" : `\`${folder}\``));
273
+ return shown.length > 0 ? `Dependency cache: restored \`node_modules\` for ${shown.join(", ")}.` : undefined;
274
+ }
200
275
  function packagesSection(input) {
201
276
  const packages = [
202
277
  ...new Map((input.packages ?? [])
@@ -212,11 +287,23 @@ function packagesSection(input) {
212
287
  isSafeMarkdownFragment(refusal.reason))
213
288
  .map((refusal) => [`${refusal.ecosystem}\0${refusal.name}\0${refusal.reason}`, refusal])).values(),
214
289
  ];
290
+ const restored = dependencyCacheLine(input.restoredDependencies);
215
291
  if (packages.length === 0 && refusals.length === 0)
216
- return undefined;
292
+ return restored;
293
+ // Only ids safe in a Markdown line; a package whose ids are all unsafe is
294
+ // not listed here (it is still listed as installed).
295
+ const waivers = packages.flatMap((pkg) => {
296
+ // Ids come from OSV (a waiver can match via alias), so keep only the
297
+ // policy id shape and render each in a code span.
298
+ const ids = (pkg.acceptedAdvisories ?? []).filter((id) => ACCEPTED_ADVISORY_ID.test(id));
299
+ return ids.length > 0 ? [`- \`${pkg.name}@${pkg.version}\`: ${ids.map((id) => `\`${id}\``).join(", ")}`] : [];
300
+ });
217
301
  return [
302
+ ...(restored ? [restored, ""] : []),
218
303
  "<details>",
219
- `<summary>Packages installed during this run (${packages.length})</summary>`,
304
+ input.packagesScope === "pull_request"
305
+ ? `<summary>Packages installed by wardby's runs on this pull request (${packages.length})</summary>`
306
+ : `<summary>Packages installed during this run (${packages.length})</summary>`,
220
307
  "",
221
308
  ...cappedList(packages.map((pkg) => `- ${pkg.ecosystem} \`${pkg.name}@${pkg.version}\``)),
222
309
  ...(refusals.length > 0
@@ -226,6 +313,13 @@ function packagesSection(input) {
226
313
  ...cappedList(refusals.map((refusal) => `- ${refusal.ecosystem} \`${refusal.name}\`: ${refusal.reason}`)),
227
314
  ]
228
315
  : []),
316
+ ...(waivers.length > 0
317
+ ? [
318
+ "",
319
+ "**Accepted advisories** (waived by this agent's package policy):",
320
+ ...cappedList(waivers, MAX_PR_WAIVER_LIST_CHARS),
321
+ ]
322
+ : []),
229
323
  "</details>",
230
324
  ].join("\n");
231
325
  }
@@ -242,15 +336,6 @@ const LOCKFILE_NAMES = new Set([
242
336
  export function isLockfilePath(path) {
243
337
  return LOCKFILE_NAMES.has(path.slice(path.lastIndexOf("/") + 1));
244
338
  }
245
- /** What each refusal code means to someone reading the PR, without the registry's internals. */
246
- const REFUSAL_MEANING = {
247
- wardby_package_not_allowed: "not on the allowlist, or only reachable through a refused package",
248
- wardby_version_filtered: "withheld: newer than the release-age limit, or a high-severity advisory",
249
- wardby_graph_incomplete: "its dependencies could not be confirmed (usually transient)",
250
- wardby_lockfile_integrity_mismatch: "the lock file's integrity does not match the registry's",
251
- wardby_upstream_error: "the upstream registry failed (transient)",
252
- wardby_audit_unavailable: "the advisory check was unavailable (transient)",
253
- };
254
339
  /** At most this many refused packages are named in the warning; the full list stays in the details. */
255
340
  const MAX_WARNING_NAMES = 8;
256
341
  /**
@@ -280,7 +365,7 @@ function refusalWarning(input) {
280
365
  const listed = [...names];
281
366
  const shown = listed.slice(0, MAX_WARNING_NAMES).join(", ");
282
367
  const more = listed.length > MAX_WARNING_NAMES ? ` and ${listed.length - MAX_WARNING_NAMES} more` : "";
283
- const meaning = REFUSAL_MEANING[reason];
368
+ const meaning = refusalMeaning(reason);
284
369
  lines.push(`> - ${shown}${more}: \`${reason}\`${meaning ? ` (${meaning})` : ""}`);
285
370
  }
286
371
  const lockfiles = (input.changedLockfiles ?? []).filter(isSafeMarkdownFragment);
@@ -289,8 +374,51 @@ function refusalWarning(input) {
289
374
  }
290
375
  return lines.join("\n");
291
376
  }
377
+ /** The banner when the audit report can't be rendered: fixed text, no record data. */
378
+ const PACKAGE_AUDIT_FALLBACK_BANNER = "> [!CAUTION]\n> **Package audit mode was on for this run.** Wardby's registry may have installed packages outside this agent's allowlist; see `get_run` (`packageAudit`) before merging.";
379
+ /**
380
+ * The package audit block: built only from registry records (names and
381
+ * versions through codeSpan, advisory ids by their policy shape), so neither
382
+ * the model nor a package's metadata can remove or soften it.
383
+ */
384
+ function packageAuditSection(input) {
385
+ if (!input.packageAudit)
386
+ return undefined;
387
+ const summary = summarizePackageAudit(packageAuditRows(input.packageAudit.entries));
388
+ return packageAuditWording(input.packageAudit.mode, summary, { format: "pr" });
389
+ }
390
+ /**
391
+ * The pull request body, within GitHub's MAX_PULL_REQUEST_BODY_CHARS. When it
392
+ * would be longer, the informational Packages section goes first; if that is
393
+ * not enough, the agent's summary is truncated to fit (fitSummary keeps its
394
+ * closing-keyword line). The other sections are bounded and stay whole.
395
+ */
396
+ export function pullRequestBody(input, options = {}) {
397
+ const body = buildPullRequestBody(input, options);
398
+ if (body.length <= MAX_PULL_REQUEST_BODY_CHARS)
399
+ return body;
400
+ const withoutPackages = options.omitPackages ? body : buildPullRequestBody(input, { omitPackages: true });
401
+ if (withoutPackages.length <= MAX_PULL_REQUEST_BODY_CHARS || !input.summary)
402
+ return withoutPackages;
403
+ const room = input.summary.length - (withoutPackages.length - MAX_PULL_REQUEST_BODY_CHARS);
404
+ return buildPullRequestBody({ ...input, summary: fitSummary(input.summary, room) }, { omitPackages: true });
405
+ }
406
+ /**
407
+ * `summary` cut to `max` characters. A last line with a closing keyword
408
+ * (`Resolves #12`) is kept after the cut, so merging still closes the issue.
409
+ */
410
+ function fitSummary(summary, max) {
411
+ const measure = (value) => value.length;
412
+ const lines = summary.trimEnd().split("\n");
413
+ const last = lines[lines.length - 1];
414
+ if (lines.length > 1 && last.length <= 200 && hasClosingKeyword(last)) {
415
+ const kept = truncateText(lines.slice(0, -1).join("\n"), max - last.length - 2, measure);
416
+ return `${kept}\n\n${last}`;
417
+ }
418
+ return truncateText(summary, max, measure);
419
+ }
292
420
  /** The hidden run marker stays first and unconditional: createOrFindDraftPullRequest's idempotent lookup depends on it. */
293
- export function pullRequestBody(input) {
421
+ function buildPullRequestBody(input, options) {
294
422
  const sections = [`${RUN_MARKER_PREFIX}${input.runId} -->`];
295
423
  // Hidden marker stays first; the issue link is the first visible line.
296
424
  const issue = issueLine(input);
@@ -312,6 +440,17 @@ export function pullRequestBody(input) {
312
440
  catch {
313
441
  // omitted, like the packages section
314
442
  }
443
+ try {
444
+ const audit = packageAuditSection(input);
445
+ if (audit)
446
+ sections.push(audit);
447
+ }
448
+ catch {
449
+ // Unlike the packages section, the audit banner fails closed: a report
450
+ // that can't be rendered still says the mode was on.
451
+ if (input.packageAudit)
452
+ sections.push(PACKAGE_AUDIT_FALLBACK_BANNER);
453
+ }
315
454
  if (input.summary)
316
455
  sections.push(input.summary);
317
456
  if (input.tests?.length) {
@@ -320,13 +459,16 @@ export function pullRequestBody(input) {
320
459
  // The packages section is informational: a failure rendering it (e.g. a
321
460
  // malformed report) omits the section and never fails finalization.
322
461
  try {
323
- const packages = packagesSection(input);
462
+ const packages = options.omitPackages ? undefined : packagesSection(input);
324
463
  if (packages)
325
464
  sections.push(packages);
326
465
  }
327
466
  catch {
328
467
  // omitted
329
468
  }
469
+ if (input.latestRunId && SAFE_RUN_ID.test(input.latestRunId)) {
470
+ sections.push(`${LATEST_RUN_MARKER_PREFIX}${input.latestRunId} -->`);
471
+ }
330
472
  return sections.join("\n\n");
331
473
  }
332
474
  /** The hidden marker stays first and unconditional: findStatusComment's lookup depends on it. */
@@ -364,7 +506,9 @@ export class GitHubAppClient {
364
506
  throw new Error("github_app_id_invalid");
365
507
  if (!config.privateKey.trim())
366
508
  throw new Error("github_app_private_key_invalid");
367
- this.apiBaseUrl = normalizeApiBaseUrl(config.apiBaseUrl ?? DEFAULT_API_BASE_URL);
509
+ // WARDBY_TEST_SIMULATORS (test only, refused with production configuration): every call
510
+ // goes to the simulator instead, all or nothing.
511
+ this.apiBaseUrl = testSimulatorBase("github-api") ?? normalizeApiBaseUrl(config.apiBaseUrl ?? DEFAULT_API_BASE_URL);
368
512
  this.apiVersion = config.apiVersion ?? DEFAULT_API_VERSION;
369
513
  if (!/^\d{4}-\d{2}-\d{2}$/.test(this.apiVersion))
370
514
  throw new Error("github_api_version_invalid");
@@ -463,20 +607,38 @@ export class GitHubAppClient {
463
607
  });
464
608
  }
465
609
  identity;
466
- /** The App's id and slug (its @-mention handle), read once with the App JWT. */
467
- appIdentity() {
610
+ /**
611
+ * The App's id, slug (its @-mention handle), and owner account, read once
612
+ * with the App JWT and memoized. `fresh` re-reads GET /app and replaces the
613
+ * memo (used before a destructive action, since the owner can change).
614
+ * A rejection is never cached.
615
+ */
616
+ appIdentity(opts) {
617
+ if (opts?.fresh)
618
+ this.identity = undefined;
468
619
  this.identity ??= (async () => {
469
620
  const response = await this.requestJson("/app", await this.appJwt());
470
621
  const payload = record(await response.json());
471
622
  if (typeof payload.slug !== "string" || !/^[a-z0-9-]{1,64}$/.test(payload.slug)) {
472
623
  throw new Error("github_api_invalid_response");
473
624
  }
474
- return { id: positiveInteger(payload.id), slug: payload.slug };
475
- })().catch((err) => {
476
- this.identity = undefined;
477
- throw err;
625
+ const ownerLogin = record(payload.owner).login;
626
+ if (!isGitHubLogin(ownerLogin))
627
+ throw new Error("github_api_invalid_response");
628
+ return { id: positiveInteger(payload.id), slug: payload.slug, ownerLogin };
629
+ })();
630
+ const pending = this.identity;
631
+ void pending.catch(() => {
632
+ // Only clear our own attempt: a newer (fresh) read may have replaced it.
633
+ if (this.identity === pending)
634
+ this.identity = undefined;
478
635
  });
479
- return this.identity;
636
+ return pending;
637
+ }
638
+ /** Removes this App from an installation (an account outside the trusted set). */
639
+ async deleteInstallation(installationId) {
640
+ const id = positiveInteger(installationId);
641
+ await this.requestJson(`/app/installations/${id}`, await this.appJwt(), { method: "DELETE" }, [204]);
480
642
  }
481
643
  async upsertContinuationStatusComment(input) {
482
644
  const repository = normalizeGitHubRepository(input.repository);
@@ -553,11 +715,14 @@ export class GitHubAppClient {
553
715
  }
554
716
  async completeContinuationCheckRun(input) {
555
717
  const repository = normalizeGitHubRepository(input.repository);
556
- if (!SAFE_RUN_ID.test(input.runId) || !SAFE_COMMIT_SHA.test(input.headSha)) {
718
+ if (!SAFE_RUN_ID.test(input.runId) || (input.headSha !== undefined && !SAFE_COMMIT_SHA.test(input.headSha))) {
557
719
  throw new Error("github_check_run_input_invalid");
558
720
  }
721
+ const ref = input.headSha ?? (input.headRef === undefined ? undefined : normalizeGitRef(input.headRef));
722
+ if (!ref)
723
+ throw new Error("github_check_run_input_invalid");
559
724
  await this.withChecksToken(repository, async (token) => {
560
- const existing = await this.findCheckRun(token, repository, input.headSha, input.runId);
725
+ const existing = await this.findCheckRun(token, repository, ref, input.runId);
561
726
  if (!existing)
562
727
  return;
563
728
  const [owner, name] = repository.split("/");
@@ -595,8 +760,14 @@ export class GitHubAppClient {
595
760
  draft: true,
596
761
  }),
597
762
  }, [201, 422]);
598
- if (response.status === 201)
599
- return this.parsePullRequest(await response.json(), repository);
763
+ if (response.status === 201) {
764
+ const payload = record(await response.json());
765
+ const created = this.parsePullRequest(payload, repository);
766
+ // Hash what GitHub stored, not what was sent.
767
+ return typeof payload.body === "string"
768
+ ? { ...created, descriptionSha256: descriptionSha256(payload.body) }
769
+ : created;
770
+ }
600
771
  const raced = await this.findPullRequest(token, { ...input, repository, baseRef, headRef });
601
772
  if (raced)
602
773
  return raced;
@@ -617,6 +788,270 @@ export class GitHubAppClient {
617
788
  throw new Error("github_pull_request_input_invalid");
618
789
  return this.withRepositoryToken(repository, (token) => this.findPullRequest(token, { runId: input.runId, repository, baseRef, headRef, acceptReadyForReview: true }));
619
790
  }
791
+ /** See GitHubRepositoryAccess.openPullRequestDraft: GitHub's own `draft` flag on the marked open PR. */
792
+ async openPullRequestDraft(input) {
793
+ const repository = normalizeGitHubRepository(input.repository);
794
+ const baseRef = normalizeGitRef(input.baseRef);
795
+ const headRef = normalizeGitRef(input.headRef);
796
+ if (!SAFE_RUN_ID.test(input.runId))
797
+ throw new Error("github_pull_request_input_invalid");
798
+ return this.withRepositoryToken(repository, async (token) => {
799
+ const found = await this.findMarkedPullRequest(token, {
800
+ runId: input.runId,
801
+ repository,
802
+ baseRef,
803
+ headRef,
804
+ acceptReadyForReview: true,
805
+ });
806
+ return found ? found.draft : null;
807
+ });
808
+ }
809
+ /** See GitHubRepositoryAccess.openPullRequestBase: the marked open PR's current base, same repository only. */
810
+ async openPullRequestBase(input) {
811
+ const repository = normalizeGitHubRepository(input.repository);
812
+ const baseRef = normalizeGitRef(input.baseRef);
813
+ const headRef = normalizeGitRef(input.headRef);
814
+ if (!SAFE_RUN_ID.test(input.runId))
815
+ throw new Error("github_pull_request_input_invalid");
816
+ return this.withRepositoryToken(repository, async (token) => {
817
+ const found = await this.findMarkedPullRequest(token, {
818
+ runId: input.runId,
819
+ repository,
820
+ baseRef,
821
+ headRef,
822
+ acceptReadyForReview: true,
823
+ });
824
+ if (!found)
825
+ return null;
826
+ const base = record(found.raw.base);
827
+ const head = record(found.raw.head);
828
+ const sameRepository = (side) => {
829
+ const name = side.repo && typeof side.repo === "object" ? side.repo.full_name : null;
830
+ return typeof name === "string" && name.toLowerCase() === repository.toLowerCase();
831
+ };
832
+ // A fork's branch is never fetched: wardby's own heads live in the PR's repository.
833
+ if (!sameRepository(base) || !sameRepository(head))
834
+ return null;
835
+ if (typeof base.ref !== "string")
836
+ throw new Error("github_api_invalid_response");
837
+ try {
838
+ return { number: found.pr.number, baseRef: normalizeGitRef(base.ref) };
839
+ }
840
+ catch (error) {
841
+ throw new Error("github_api_invalid_response", { cause: error });
842
+ }
843
+ });
844
+ }
845
+ /**
846
+ * A fix round's description update (see GitHubRepositoryAccess). One
847
+ * repository token; every failure becomes `skipped:error`.
848
+ */
849
+ async updatePullRequestDescription(input) {
850
+ try {
851
+ const target = this.descriptionTarget(input);
852
+ return await this.withRepositoryToken(target.repository, async (token) => {
853
+ const state = await this.descriptionState(token, input, target);
854
+ if (!state)
855
+ return { kind: "skipped", reason: "not_open" };
856
+ const { pr, verdict, full, proposed } = state;
857
+ const { repository } = target;
858
+ if (!state.writable) {
859
+ // The comment truncates a body too long for it, so the full one goes in.
860
+ const commentPosted = await this.upsertDescriptionComment(token, repository, pr.number, input, full, state)
861
+ .then(() => true)
862
+ .catch(() => false);
863
+ return { kind: "edited_by_hand", commentPosted };
864
+ }
865
+ if (proposed === undefined)
866
+ return { kind: "skipped", reason: "too_long" };
867
+ const agentTextRunId = verdict.ok ? input.runId : input.baseline.agentTextRunId;
868
+ const outcome = verdict.ok
869
+ ? { kind: "replaced" }
870
+ : { kind: "refreshed", reason: verdict.reason };
871
+ const proposedSha256 = descriptionSha256(proposed);
872
+ if (proposedSha256 === input.baseline.recordedSha256) {
873
+ return { kind: "unchanged", sha256: proposedSha256, agentTextRunId: input.baseline.agentTextRunId };
874
+ }
875
+ const [owner, name] = repository.split("/");
876
+ // Known, accepted race: a person editing between our read and this PATCH is overwritten (GitHub has no If-Match); the store's compare-and-set only covers wardby runs racing each other.
877
+ const response = await this.requestJson(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/pulls/${pr.number}`, token, { method: "PATCH", body: JSON.stringify({ body: proposed }) }, [200]);
878
+ const written = record(await response.json()).body;
879
+ if (typeof written !== "string")
880
+ throw new Error("github_api_invalid_response");
881
+ // A proposal left from an earlier round is stale now; a failure here never undoes the write.
882
+ await this.markDescriptionCommentApplied(token, repository, pr.number, input.runId, state).catch(() => undefined);
883
+ return { ...outcome, sha256: descriptionSha256(written), agentTextRunId };
884
+ });
885
+ }
886
+ catch {
887
+ return { kind: "skipped", reason: "error" };
888
+ }
889
+ }
890
+ /** See GitHubRepositoryAccess.proposeDescriptionComment. */
891
+ async proposeDescriptionComment(input) {
892
+ try {
893
+ if (!input.headSha || !/^[0-9a-f]{40}$/.test(input.headSha))
894
+ return false;
895
+ const target = this.descriptionTarget(input);
896
+ return await this.withRepositoryToken(target.repository, async (token) => {
897
+ const state = await this.descriptionState(token, input, target);
898
+ if (!state || state.writable)
899
+ return false;
900
+ await this.upsertDescriptionComment(token, target.repository, state.pr.number, input, state.full, state);
901
+ return true;
902
+ });
903
+ }
904
+ catch {
905
+ return false;
906
+ }
907
+ }
908
+ /** The validated repository and refs; throws unless this is wardby's own pull request branch. */
909
+ descriptionTarget(input) {
910
+ const repository = normalizeGitHubRepository(input.repository);
911
+ const baseRef = normalizeGitRef(input.baseRef);
912
+ const headRef = normalizeGitRef(input.headRef);
913
+ if (!SAFE_RUN_ID.test(input.runId) ||
914
+ !SAFE_RUN_ID.test(input.rootRunId) ||
915
+ !SAFE_RUN_ID.test(input.baseline.agentTextRunId) ||
916
+ (input.headSha !== undefined && !/^[0-9a-f]{40}$/.test(input.headSha)) ||
917
+ // Only wardby's own pull request: its branch is the root run's.
918
+ headRef !== `wardby/run-${input.rootRunId}`) {
919
+ throw new Error("github_pull_request_description_input_invalid");
920
+ }
921
+ return { repository, baseRef, headRef };
922
+ }
923
+ /**
924
+ * The open pull request, the description this run proposes, and whether
925
+ * wardby may write it directly: it is unedited (recorded or rebuilt hash),
926
+ * or a person copied wardby's latest proposal in (taken back). Null when
927
+ * the pull request isn't open. `comments` is set when it was read.
928
+ */
929
+ async descriptionState(token, input, target) {
930
+ const { repository, baseRef, headRef } = target;
931
+ const found = await this.findMarkedPullRequest(token, {
932
+ runId: input.rootRunId,
933
+ repository,
934
+ baseRef,
935
+ headRef,
936
+ acceptReadyForReview: true,
937
+ });
938
+ if (!found)
939
+ return null;
940
+ const { pr, body: currentBody } = found;
941
+ const verdict = assessFinalMessage(input.summary, input.baseline.previousAgentText);
942
+ const summary = verdict.ok ? input.summary : input.baseline.previousAgentText;
943
+ const { full, proposed } = this.proposedDescription(input, repository, baseRef, headRef, summary, currentBody);
944
+ const unedited = isUneditedDescription({
945
+ currentBody,
946
+ rootRunId: input.rootRunId,
947
+ recordedSha256: input.baseline.recordedSha256,
948
+ ...(input.baseline.legacySha256 ? { legacySha256: input.baseline.legacySha256 } : {}),
949
+ previousAgentText: input.baseline.previousAgentText,
950
+ });
951
+ if (unedited)
952
+ return { pr, currentBody, verdict, full, proposed, writable: true };
953
+ const comments = await this.findDescriptionComments(token, repository, pr.number);
954
+ const latest = comments.newest?.parsed.proposed;
955
+ // The author copied wardby's latest proposal in: the description is wardby's again.
956
+ const takenBack = latest !== undefined &&
957
+ isUneditedDescription({
958
+ currentBody,
959
+ rootRunId: input.rootRunId,
960
+ recordedSha256: descriptionSha256(latest),
961
+ });
962
+ return { pr, currentBody, verdict, full, proposed, writable: takenBack, comments };
963
+ }
964
+ /**
965
+ * The rebuilt description: the generated sections from this run with
966
+ * `summary` as the agent text, the latest-run marker last, and the current
967
+ * body's Related block re-inserted verbatim. `proposed` drops the Packages
968
+ * section when `full` exceeds GitHub's limit, and is undefined when it still
969
+ * would.
970
+ */
971
+ proposedDescription(input, repository, baseRef, headRef, summary, currentBody) {
972
+ const related = extractRelatedBlock(currentBody.replace(/\r\n/g, "\n"));
973
+ const build = (omitPackages) => {
974
+ const body = pullRequestBody({
975
+ repository,
976
+ baseRef,
977
+ headRef,
978
+ runId: input.rootRunId,
979
+ latestRunId: input.runId,
980
+ summary,
981
+ tests: input.tests,
982
+ tag: input.tag,
983
+ packages: input.packages,
984
+ packagesScope: "pull_request",
985
+ packageRefusals: input.packageRefusals,
986
+ packageAudit: input.packageAudit,
987
+ restoredDependencies: input.restoredDependencies,
988
+ changedLockfiles: input.changedLockfiles,
989
+ issue: input.issue,
990
+ issueNumber: input.issueNumber,
991
+ }, { omitPackages });
992
+ return related ? (upsertRelatedSection(body, related) ?? body) : body;
993
+ };
994
+ const full = build(false);
995
+ if (full.length <= MAX_PULL_REQUEST_BODY_CHARS)
996
+ return { full, proposed: full };
997
+ const trimmed = build(true);
998
+ return { full, proposed: trimmed.length <= MAX_PULL_REQUEST_BODY_CHARS ? trimmed : undefined };
999
+ }
1000
+ /**
1001
+ * Creates the pull request's one description comment, or edits the newest
1002
+ * one this App wrote (unchanged text is not re-sent); older duplicates are
1003
+ * rewritten to point at it. A comment anyone else wrote is never edited.
1004
+ */
1005
+ async upsertDescriptionComment(token, repository, pullRequestNumber, input, proposedBody, state) {
1006
+ const body = descriptionFallbackComment(input.runId, proposedBody, input.headSha);
1007
+ const { newest, older } = state.comments ?? (await this.findDescriptionComments(token, repository, pullRequestNumber));
1008
+ const [owner, name] = repository.split("/");
1009
+ const commentPath = (id) => `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/issues/comments/${id}`;
1010
+ let url = newest?.url;
1011
+ if (newest) {
1012
+ if (newest.body.replace(/\r\n/g, "\n") !== body) {
1013
+ await this.requestJson(commentPath(newest.id), token, { method: "PATCH", body: JSON.stringify({ body }) }, [200]);
1014
+ }
1015
+ }
1016
+ else {
1017
+ const response = await this.requestJson(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/issues/${pullRequestNumber}/comments`, token, { method: "POST", body: JSON.stringify({ body }) }, [201]);
1018
+ const created = record(await response.json()).html_url;
1019
+ url = typeof created === "string" ? created : undefined;
1020
+ }
1021
+ await this.supersedeDescriptionComments(token, commentPath, older, url);
1022
+ }
1023
+ /** Once a run applied its description, a pending proposal in the description comment is marked applied. */
1024
+ async markDescriptionCommentApplied(token, repository, pullRequestNumber, runId, state) {
1025
+ const { newest, older } = state.comments ?? (await this.findDescriptionComments(token, repository, pullRequestNumber));
1026
+ if (!newest)
1027
+ return;
1028
+ const [owner, name] = repository.split("/");
1029
+ const commentPath = (id) => `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/issues/comments/${id}`;
1030
+ if (!newest.parsed.applied) {
1031
+ await this.requestJson(commentPath(newest.id), token, { method: "PATCH", body: JSON.stringify({ body: descriptionAppliedComment(runId) }) }, [200]);
1032
+ }
1033
+ await this.supersedeDescriptionComments(token, commentPath, older, newest.url);
1034
+ }
1035
+ /** Rewrites duplicate description comments to a pointer at the one in use. */
1036
+ async supersedeDescriptionComments(token, commentPath, older, url) {
1037
+ for (const duplicate of older) {
1038
+ await this.requestJson(commentPath(duplicate.id), token, { method: "PATCH", body: JSON.stringify({ body: supersededDescriptionComment(url) }) }, [200]);
1039
+ }
1040
+ }
1041
+ /** The description comments this App wrote (see findDescriptionComments; the reviewer reads them the same way). */
1042
+ async findDescriptionComments(token, repository, pullRequestNumber) {
1043
+ const { id: appId } = await this.appIdentity();
1044
+ const [owner, name] = repository.split("/");
1045
+ return findDescriptionComments(async (page) => {
1046
+ const response = await this.requestJson(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/issues/${pullRequestNumber}/comments?per_page=100&page=${page}`, token);
1047
+ const payload = await response.json();
1048
+ if (!Array.isArray(payload))
1049
+ throw new Error("github_api_invalid_response");
1050
+ return payload;
1051
+ },
1052
+ // Only this App's own comments: never a person's, nor another App's or bot's.
1053
+ (comment) => isThisAppsComment(comment, appId));
1054
+ }
620
1055
  async appJwt() {
621
1056
  let keyObject;
622
1057
  try {
@@ -682,11 +1117,19 @@ export class GitHubAppClient {
682
1117
  await this.requestJson("/installation/token", token, { method: "DELETE" }, [204]);
683
1118
  }
684
1119
  async findPullRequest(token, input) {
1120
+ return (await this.findMarkedPullRequest(token, input))?.pr ?? null;
1121
+ }
1122
+ /**
1123
+ * findPullRequest, plus the matched pull request's raw body and payload. Not
1124
+ * filtered by base: a person may retarget the PR, and the head branch plus the
1125
+ * run marker already identify it. When several open PRs match, the one on
1126
+ * `baseRef` wins.
1127
+ */
1128
+ async findMarkedPullRequest(token, input) {
685
1129
  const [owner, name] = input.repository.split("/");
686
1130
  const query = new URLSearchParams({
687
1131
  state: "open",
688
1132
  head: `${owner}:${input.headRef}`,
689
- base: input.baseRef,
690
1133
  per_page: "100",
691
1134
  });
692
1135
  const response = await this.requestJson(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/pulls?${query.toString()}`, token);
@@ -694,13 +1137,26 @@ export class GitHubAppClient {
694
1137
  if (!Array.isArray(payload))
695
1138
  throw new Error("github_api_invalid_response");
696
1139
  const marker = `${RUN_MARKER_PREFIX}${input.runId} -->`;
1140
+ let first;
697
1141
  for (const item of payload) {
698
1142
  const candidate = record(item);
699
- if (typeof candidate.body === "string" && candidate.body.includes(marker)) {
700
- return this.parsePullRequest(candidate, input.repository, input.acceptReadyForReview === true);
1143
+ if (typeof candidate.body !== "string" || !candidate.body.includes(marker))
1144
+ continue;
1145
+ const base = candidate.base && typeof candidate.base === "object" ? candidate.base : {};
1146
+ if (base.ref === input.baseRef) {
1147
+ first = candidate;
1148
+ break;
701
1149
  }
1150
+ first ??= candidate;
702
1151
  }
703
- return null;
1152
+ if (!first)
1153
+ return null;
1154
+ return {
1155
+ pr: this.parsePullRequest(first, input.repository, input.acceptReadyForReview === true),
1156
+ body: first.body,
1157
+ draft: first.draft === true,
1158
+ raw: first,
1159
+ };
704
1160
  }
705
1161
  parsePullRequest(value, repository, acceptReadyForReview = false) {
706
1162
  const payload = record(value);
@@ -730,23 +1186,27 @@ export class GitHubAppClient {
730
1186
  return { number, url: expectedUrl };
731
1187
  }
732
1188
  async findStatusComment(token, repository, pullRequestNumber, runId) {
1189
+ const marker = `${STATUS_COMMENT_MARKER_PREFIX}${runId} -->`;
1190
+ return this.findMarkedComment(token, repository, pullRequestNumber, (body) => body.includes(marker));
1191
+ }
1192
+ /** The first comment on the pull request (first page) whose body `matches`. */
1193
+ async findMarkedComment(token, repository, pullRequestNumber, matches) {
733
1194
  const [owner, name] = repository.split("/");
734
1195
  const response = await this.requestJson(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/issues/${pullRequestNumber}/comments?per_page=100`, token);
735
1196
  const payload = await response.json();
736
1197
  if (!Array.isArray(payload))
737
1198
  throw new Error("github_api_invalid_response");
738
- const marker = `${STATUS_COMMENT_MARKER_PREFIX}${runId} -->`;
739
1199
  for (const item of payload) {
740
1200
  const candidate = record(item);
741
- if (typeof candidate.body === "string" && candidate.body.includes(marker)) {
742
- return { id: positiveInteger(candidate.id) };
743
- }
1201
+ if (typeof candidate.body !== "string" || !matches(candidate.body))
1202
+ continue;
1203
+ return { id: positiveInteger(candidate.id), body: candidate.body };
744
1204
  }
745
1205
  return null;
746
1206
  }
747
- async findCheckRun(token, repository, headSha, runId) {
1207
+ async findCheckRun(token, repository, ref, runId) {
748
1208
  const [owner, name] = repository.split("/");
749
- const response = await this.requestJson(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/commits/${headSha}/check-runs?per_page=100`, token);
1209
+ const response = await this.requestJson(`/repos/${encodeURIComponent(owner)}/${encodeURIComponent(name)}/commits/${encodeURIComponent(ref)}/check-runs?per_page=100`, token);
750
1210
  const payload = record(await response.json());
751
1211
  const checkRuns = Array.isArray(payload.check_runs) ? payload.check_runs.map(record) : [];
752
1212
  for (const candidate of checkRuns) {