@wardby/cli 0.2.1 → 0.4.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 (555) hide show
  1. package/.env.example +53 -4
  2. package/README.md +52 -13
  3. package/dist/claude-coding-worker/driver.d.ts +6 -1
  4. package/dist/claude-coding-worker/driver.js +27 -1
  5. package/dist/claude-coding-worker/main.js +2 -0
  6. package/dist/claude-coding-worker/tool-socket.d.ts +9 -0
  7. package/dist/claude-coding-worker/tool-socket.js +26 -0
  8. package/dist/cli-help.d.ts +1 -1
  9. package/dist/cli-help.js +14 -4
  10. package/dist/cli.d.ts +1 -1
  11. package/dist/cli.js +284 -36
  12. package/dist/coding/base-commit.d.ts +6 -0
  13. package/dist/coding/base-commit.js +12 -0
  14. package/dist/coding/collect-exclude.d.ts +25 -0
  15. package/dist/coding/collect-exclude.js +76 -0
  16. package/dist/coding/profile.d.ts +67 -22
  17. package/dist/coding/profile.js +58 -25
  18. package/dist/coding/protected-path-wording.d.ts +24 -0
  19. package/dist/coding/protected-path-wording.js +55 -0
  20. package/dist/coding/protected-paths.d.ts +31 -0
  21. package/dist/coding/protected-paths.js +48 -0
  22. package/dist/coding/protocol.d.ts +91 -4
  23. package/dist/coding/protocol.js +123 -12
  24. package/dist/coding/provider.d.ts +6 -1
  25. package/dist/coding/provider.js +16 -9
  26. package/dist/coding/registry/adapters.d.ts +2 -0
  27. package/dist/coding/registry/adapters.js +9 -0
  28. package/dist/coding/registry/allowlist.d.ts +9 -0
  29. package/dist/coding/registry/allowlist.js +28 -0
  30. package/dist/coding/registry/json-scan.d.ts +55 -0
  31. package/dist/coding/registry/json-scan.js +191 -0
  32. package/dist/coding/registry/lockfiles.d.ts +6 -0
  33. package/dist/coding/registry/lockfiles.js +89 -0
  34. package/dist/coding/registry/npm-lockfile.d.ts +9 -0
  35. package/dist/coding/registry/npm-lockfile.js +73 -0
  36. package/dist/coding/registry/npm-plan.d.ts +29 -0
  37. package/dist/coding/registry/npm-plan.js +289 -0
  38. package/dist/coding/registry/npm.d.ts +7 -0
  39. package/dist/coding/registry/npm.js +252 -0
  40. package/dist/coding/registry/pypi.d.ts +4 -0
  41. package/dist/coding/registry/pypi.js +210 -0
  42. package/dist/coding/registry/report.d.ts +37 -0
  43. package/dist/coding/registry/report.js +31 -0
  44. package/dist/coding/registry/token.d.ts +4 -0
  45. package/dist/coding/registry/token.js +7 -0
  46. package/dist/coding/registry/types.d.ts +286 -0
  47. package/dist/coding/registry/types.js +19 -0
  48. package/dist/coding/registry/worker-config.d.ts +14 -0
  49. package/dist/coding/registry/worker-config.js +31 -0
  50. package/dist/coding/services/builtins.d.ts +22 -0
  51. package/dist/coding/services/builtins.js +85 -0
  52. package/dist/coding/services/catalog.d.ts +443 -0
  53. package/dist/coding/services/catalog.js +159 -0
  54. package/dist/coding/services/declaration.d.ts +11 -0
  55. package/dist/coding/services/declaration.js +114 -0
  56. package/dist/coding/services/note.d.ts +8 -0
  57. package/dist/coding/services/note.js +15 -0
  58. package/dist/coding/services/resolve.d.ts +25 -0
  59. package/dist/coding/services/resolve.js +50 -0
  60. package/dist/coding/services/wording.d.ts +25 -0
  61. package/dist/coding/services/wording.js +70 -0
  62. package/dist/coding-proxy/main.js +24 -4
  63. package/dist/coding-worker/artifact.d.ts +6 -0
  64. package/dist/coding-worker/debug-trace.d.ts +37 -0
  65. package/dist/coding-worker/debug-trace.js +117 -0
  66. package/dist/coding-worker/driver.d.ts +13 -1
  67. package/dist/coding-worker/driver.js +102 -13
  68. package/dist/coding-worker/errors.js +8 -0
  69. package/dist/coding-worker/main.js +10 -2
  70. package/dist/coding-worker/sdk.d.ts +2 -2
  71. package/dist/coding-worker/sdk.js +7 -2
  72. package/dist/coding-worker/types.d.ts +3 -0
  73. package/dist/config/providers.d.ts +66 -0
  74. package/dist/config/providers.js +137 -0
  75. package/dist/core/attribution.d.ts +101 -0
  76. package/dist/core/attribution.js +208 -0
  77. package/dist/core/budget-groups.d.ts +120 -10
  78. package/dist/core/budget-groups.js +133 -24
  79. package/dist/core/budget-wording.d.ts +22 -0
  80. package/dist/core/budget-wording.js +55 -0
  81. package/dist/core/coding-queue.d.ts +3 -0
  82. package/dist/core/coding-queue.js +6 -2
  83. package/dist/core/coding-service-status.d.ts +11 -0
  84. package/dist/core/coding-service-status.js +17 -0
  85. package/dist/core/cost-report.d.ts +88 -0
  86. package/dist/core/cost-report.js +248 -0
  87. package/dist/core/datastores.js +18 -2
  88. package/dist/core/db.d.ts +5 -1
  89. package/dist/core/db.js +8 -2
  90. package/dist/core/dispatch.d.ts +74 -7
  91. package/dist/core/dispatch.js +382 -47
  92. package/dist/core/engine-native.js +44 -14
  93. package/dist/core/glob.d.ts +10 -0
  94. package/dist/core/glob.js +33 -0
  95. package/dist/core/grants.d.ts +86 -0
  96. package/dist/core/grants.js +126 -0
  97. package/dist/core/host-events.d.ts +72 -0
  98. package/dist/core/host-events.js +597 -0
  99. package/dist/core/host-identity-links.d.ts +54 -0
  100. package/dist/core/host-identity-links.js +189 -0
  101. package/dist/core/host-status.d.ts +78 -0
  102. package/dist/core/host-status.js +228 -0
  103. package/dist/core/in-flight-runs.d.ts +8 -0
  104. package/dist/core/in-flight-runs.js +56 -0
  105. package/dist/core/issue-bridge.d.ts +60 -0
  106. package/dist/core/issue-bridge.js +189 -0
  107. package/dist/core/issue-dedupe.d.ts +70 -0
  108. package/dist/core/issue-dedupe.js +255 -0
  109. package/dist/core/issue-events.d.ts +42 -0
  110. package/dist/core/issue-events.js +155 -0
  111. package/dist/core/issue-status.d.ts +29 -0
  112. package/dist/core/issue-status.js +241 -0
  113. package/dist/core/issue-tracker-tools.d.ts +64 -0
  114. package/dist/core/issue-tracker-tools.js +850 -0
  115. package/dist/core/model-usage.d.ts +10 -0
  116. package/dist/core/model-usage.js +24 -0
  117. package/dist/core/provider-wording.d.ts +12 -0
  118. package/dist/core/provider-wording.js +44 -0
  119. package/dist/core/reconciler.d.ts +42 -2
  120. package/dist/core/reconciler.js +97 -2
  121. package/dist/core/repo-access.d.ts +99 -0
  122. package/dist/core/repo-access.js +136 -0
  123. package/dist/core/review-host-checks.d.ts +11 -0
  124. package/dist/core/review-host-checks.js +41 -0
  125. package/dist/core/review-host-tools.d.ts +47 -0
  126. package/dist/core/review-host-tools.js +354 -0
  127. package/dist/core/run-heartbeat.d.ts +27 -0
  128. package/dist/core/run-heartbeat.js +54 -0
  129. package/dist/core/run-pricing.d.ts +61 -0
  130. package/dist/core/run-pricing.js +56 -0
  131. package/dist/core/runner.d.ts +33 -8
  132. package/dist/core/runner.js +410 -62
  133. package/dist/core/scheduler.d.ts +4 -1
  134. package/dist/core/scheduler.js +3 -2
  135. package/dist/core/secrets.d.ts +11 -2
  136. package/dist/core/secrets.js +25 -6
  137. package/dist/core/self-defects.d.ts +80 -0
  138. package/dist/core/self-defects.js +180 -0
  139. package/dist/core/subagent-memory-tools.d.ts +1 -1
  140. package/dist/core/subagent-memory-tools.js +16 -3
  141. package/dist/core/tool-admin.d.ts +81 -0
  142. package/dist/core/tool-admin.js +129 -0
  143. package/dist/core/tool-names.d.ts +42 -0
  144. package/dist/core/tool-names.js +67 -0
  145. package/dist/core/untrusted-content.d.ts +32 -0
  146. package/dist/core/untrusted-content.js +72 -0
  147. package/dist/core/webhooks.d.ts +17 -2
  148. package/dist/core/webhooks.js +42 -3
  149. package/dist/generated/prisma/browser.d.ts +166 -0
  150. package/dist/generated/prisma/client.d.ts +166 -0
  151. package/dist/generated/prisma/commonInputTypes.d.ts +152 -52
  152. package/dist/generated/prisma/enums.d.ts +13 -0
  153. package/dist/generated/prisma/enums.js +12 -1
  154. package/dist/generated/prisma/internal/class.d.ts +242 -0
  155. package/dist/generated/prisma/internal/class.js +4 -4
  156. package/dist/generated/prisma/internal/prismaNamespace.d.ts +2571 -577
  157. package/dist/generated/prisma/internal/prismaNamespace.js +312 -6
  158. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +328 -0
  159. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +312 -6
  160. package/dist/generated/prisma/models/Agent.d.ts +682 -1
  161. package/dist/generated/prisma/models/AgentIssueProject.d.ts +1838 -0
  162. package/dist/generated/prisma/models/AgentIssueProject.js +1 -0
  163. package/dist/generated/prisma/models/AgentRepository.d.ts +1425 -0
  164. package/dist/generated/prisma/models/AgentRepository.js +1 -0
  165. package/dist/generated/prisma/models/AgentTool.d.ts +95 -1
  166. package/dist/generated/prisma/models/AuthUser.d.ts +56 -1
  167. package/dist/generated/prisma/models/CodingAgentProfile.d.ts +258 -7
  168. package/dist/generated/prisma/models/CodingProxySession.d.ts +195 -2
  169. package/dist/generated/prisma/models/CodingRun.d.ts +1405 -95
  170. package/dist/generated/prisma/models/CodingRunServiceStatus.d.ts +1404 -0
  171. package/dist/generated/prisma/models/CodingRunServiceStatus.js +1 -0
  172. package/dist/generated/prisma/models/CodingService.d.ts +1348 -0
  173. package/dist/generated/prisma/models/CodingService.js +1 -0
  174. package/dist/generated/prisma/models/HostEventDelivery.d.ts +946 -0
  175. package/dist/generated/prisma/models/HostEventDelivery.js +1 -0
  176. package/dist/generated/prisma/models/HostIdentity.d.ts +1232 -0
  177. package/dist/generated/prisma/models/HostIdentity.js +1 -0
  178. package/dist/generated/prisma/models/HostIdentityLinkRequest.d.ts +1473 -0
  179. package/dist/generated/prisma/models/HostIdentityLinkRequest.js +1 -0
  180. package/dist/generated/prisma/models/IssueFingerprint.d.ts +1183 -0
  181. package/dist/generated/prisma/models/IssueFingerprint.js +1 -0
  182. package/dist/generated/prisma/models/IssuePullRequest.d.ts +1255 -0
  183. package/dist/generated/prisma/models/IssuePullRequest.js +1 -0
  184. package/dist/generated/prisma/models/ModelCatalogEntry.d.ts +1322 -0
  185. package/dist/generated/prisma/models/ModelCatalogEntry.js +1 -0
  186. package/dist/generated/prisma/models/Principal.d.ts +455 -0
  187. package/dist/generated/prisma/models/RegistryAllowance.d.ts +1148 -0
  188. package/dist/generated/prisma/models/RegistryAllowance.js +1 -0
  189. package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +1219 -0
  190. package/dist/generated/prisma/models/RegistryApprovedVersion.js +1 -0
  191. package/dist/generated/prisma/models/RegistryFetch.d.ts +1428 -0
  192. package/dist/generated/prisma/models/RegistryFetch.js +1 -0
  193. package/dist/generated/prisma/models/RegistryPlanRefusal.d.ts +1294 -0
  194. package/dist/generated/prisma/models/RegistryPlanRefusal.js +1 -0
  195. package/dist/generated/prisma/models/RegistryVersionFact.d.ts +1085 -0
  196. package/dist/generated/prisma/models/RegistryVersionFact.js +1 -0
  197. package/dist/generated/prisma/models/ResourceGrant.d.ts +1437 -0
  198. package/dist/generated/prisma/models/ResourceGrant.js +1 -0
  199. package/dist/generated/prisma/models/Run.d.ts +1402 -82
  200. package/dist/generated/prisma/models/RunAttribution.d.ts +1259 -0
  201. package/dist/generated/prisma/models/RunAttribution.js +1 -0
  202. package/dist/generated/prisma/models/RunHostCheck.d.ts +1239 -0
  203. package/dist/generated/prisma/models/RunHostCheck.js +1 -0
  204. package/dist/generated/prisma/models/RunHostStatus.d.ts +1315 -0
  205. package/dist/generated/prisma/models/RunHostStatus.js +1 -0
  206. package/dist/generated/prisma/models/RunIssueStatus.d.ts +1199 -0
  207. package/dist/generated/prisma/models/RunIssueStatus.js +1 -0
  208. package/dist/generated/prisma/models/RunModelUsage.d.ts +1316 -0
  209. package/dist/generated/prisma/models/RunModelUsage.js +1 -0
  210. package/dist/generated/prisma/models/Tool.d.ts +15 -3
  211. package/dist/generated/prisma/models/WorkItem.d.ts +1408 -0
  212. package/dist/generated/prisma/models/WorkItem.js +1 -0
  213. package/dist/generated/prisma/models.d.ts +22 -0
  214. package/dist/help/build.d.ts +1 -0
  215. package/dist/help/build.js +9 -0
  216. package/dist/help/catalog.d.ts +24 -0
  217. package/dist/help/catalog.js +160 -0
  218. package/dist/help/cli.d.ts +2 -0
  219. package/dist/help/cli.js +65 -0
  220. package/dist/help/runtime.d.ts +3 -0
  221. package/dist/help/runtime.js +44 -0
  222. package/dist/help/search.d.ts +9 -0
  223. package/dist/help/search.js +104 -0
  224. package/dist/help-index.json +999 -0
  225. package/dist/import/cli-args.js +3 -2
  226. package/dist/import/create.d.ts +5 -0
  227. package/dist/import/create.js +67 -13
  228. package/dist/import/index.js +19 -9
  229. package/dist/import/neutral-schema.d.ts +18 -18
  230. package/dist/knowledge/check.d.ts +13 -0
  231. package/dist/knowledge/check.js +69 -0
  232. package/dist/knowledge/cli.d.ts +14 -0
  233. package/dist/knowledge/cli.js +67 -0
  234. package/dist/knowledge/concept.d.ts +54 -0
  235. package/dist/knowledge/concept.js +78 -0
  236. package/dist/knowledge/note.d.ts +11 -0
  237. package/dist/knowledge/note.js +39 -0
  238. package/dist/knowledge/relevance.d.ts +11 -0
  239. package/dist/knowledge/relevance.js +14 -0
  240. package/dist/knowledge/span-hash.d.ts +3 -0
  241. package/dist/knowledge/span-hash.js +16 -0
  242. package/dist/mcp/auth/access.d.ts +72 -0
  243. package/dist/mcp/auth/access.js +58 -0
  244. package/dist/mcp/auth/grants-cli.d.ts +149 -0
  245. package/dist/mcp/auth/grants-cli.js +518 -0
  246. package/dist/mcp/auth/host-account-cli.d.ts +2 -0
  247. package/dist/mcp/auth/host-account-cli.js +47 -0
  248. package/dist/mcp/auth/ownership.d.ts +25 -51
  249. package/dist/mcp/auth/ownership.js +19 -14
  250. package/dist/mcp/auth/repo-authorization.d.ts +22 -0
  251. package/dist/mcp/auth/repo-authorization.js +51 -0
  252. package/dist/mcp/auth/resource-server.d.ts +33 -2
  253. package/dist/mcp/auth/resource-server.js +99 -3
  254. package/dist/mcp/auth/self-hosted/browser.js +2 -2
  255. package/dist/mcp/auth/self-hosted/cli.js +47 -13
  256. package/dist/mcp/auth/self-hosted/credentials.d.ts +23 -5
  257. package/dist/mcp/auth/self-hosted/credentials.js +62 -4
  258. package/dist/mcp/auth/self-hosted/session.d.ts +7 -6
  259. package/dist/mcp/context.d.ts +30 -1
  260. package/dist/mcp/errors.d.ts +25 -6
  261. package/dist/mcp/errors.js +98 -0
  262. package/dist/mcp/host-events/deliveries.d.ts +9 -0
  263. package/dist/mcp/host-events/deliveries.js +17 -0
  264. package/dist/mcp/host-events/github-ingress.d.ts +38 -0
  265. package/dist/mcp/host-events/github-ingress.js +83 -0
  266. package/dist/mcp/host-events/github-user-callback.d.ts +18 -0
  267. package/dist/mcp/host-events/github-user-callback.js +50 -0
  268. package/dist/mcp/host-events/jira-ingress.d.ts +29 -0
  269. package/dist/mcp/host-events/jira-ingress.js +92 -0
  270. package/dist/mcp/index.d.ts +10 -1
  271. package/dist/mcp/index.js +187 -19
  272. package/dist/mcp/server.js +32 -11
  273. package/dist/mcp/tools/agents.js +519 -50
  274. package/dist/mcp/tools/budget-groups.js +3 -3
  275. package/dist/mcp/tools/cost-report.d.ts +8 -0
  276. package/dist/mcp/tools/cost-report.js +60 -0
  277. package/dist/mcp/tools/datastore.js +22 -15
  278. package/dist/mcp/tools/grants.d.ts +2 -0
  279. package/dist/mcp/tools/grants.js +239 -0
  280. package/dist/mcp/tools/help.d.ts +5 -0
  281. package/dist/mcp/tools/help.js +67 -0
  282. package/dist/mcp/tools/host-accounts.d.ts +2 -0
  283. package/dist/mcp/tools/host-accounts.js +114 -0
  284. package/dist/mcp/tools/issue-projects.d.ts +2 -0
  285. package/dist/mcp/tools/issue-projects.js +238 -0
  286. package/dist/mcp/tools/memory.d.ts +7 -1
  287. package/dist/mcp/tools/memory.js +5 -5
  288. package/dist/mcp/tools/model-catalog.d.ts +22 -0
  289. package/dist/mcp/tools/model-catalog.js +423 -0
  290. package/dist/mcp/tools/repositories.d.ts +2 -0
  291. package/dist/mcp/tools/repositories.js +182 -0
  292. package/dist/mcp/tools/runs.d.ts +6 -0
  293. package/dist/mcp/tools/runs.js +60 -6
  294. package/dist/mcp/tools/scheduling.js +3 -6
  295. package/dist/mcp/tools/secrets.js +18 -6
  296. package/dist/mcp/tools/services.d.ts +2 -0
  297. package/dist/mcp/tools/services.js +222 -0
  298. package/dist/mcp/tools/subagents.js +52 -16
  299. package/dist/mcp/tools/tools.d.ts +41 -0
  300. package/dist/mcp/tools/tools.js +201 -40
  301. package/dist/mcp/tools/trigger.js +65 -7
  302. package/dist/mcp/tools/webhooks.js +15 -4
  303. package/dist/mcp/transport/streamable-http.d.ts +15 -0
  304. package/dist/mcp/transport/streamable-http.js +55 -3
  305. package/dist/mcp/webhooks/ingress.d.ts +2 -1
  306. package/dist/mcp/webhooks/ingress.js +9 -2
  307. package/dist/providers/auth/delegating.d.ts +19 -0
  308. package/dist/providers/auth/delegating.js +71 -0
  309. package/dist/providers/auth/index.d.ts +2 -0
  310. package/dist/providers/auth/index.js +11 -0
  311. package/dist/providers/auth/self-hosted.d.ts +10 -2
  312. package/dist/providers/auth/self-hosted.js +55 -6
  313. package/dist/providers/auth/types.d.ts +6 -0
  314. package/dist/providers/coding-proxy/memory-ledger.d.ts +4 -1
  315. package/dist/providers/coding-proxy/memory-ledger.js +31 -3
  316. package/dist/providers/coding-proxy/metering.d.ts +6 -1
  317. package/dist/providers/coding-proxy/metering.js +41 -4
  318. package/dist/providers/coding-proxy/prisma-ledger.d.ts +17 -0
  319. package/dist/providers/coding-proxy/prisma-ledger.js +106 -8
  320. package/dist/providers/coding-proxy/proxy.d.ts +25 -2
  321. package/dist/providers/coding-proxy/proxy.js +561 -38
  322. package/dist/providers/coding-proxy/registry/audit.d.ts +131 -0
  323. package/dist/providers/coding-proxy/registry/audit.js +380 -0
  324. package/dist/providers/coding-proxy/registry/bounded-fetch.d.ts +16 -0
  325. package/dist/providers/coding-proxy/registry/bounded-fetch.js +77 -0
  326. package/dist/providers/coding-proxy/registry/plan.d.ts +58 -0
  327. package/dist/providers/coding-proxy/registry/plan.js +304 -0
  328. package/dist/providers/coding-proxy/registry/prisma-store.d.ts +34 -0
  329. package/dist/providers/coding-proxy/registry/prisma-store.js +151 -0
  330. package/dist/providers/coding-proxy/registry/service.d.ts +213 -0
  331. package/dist/providers/coding-proxy/registry/service.js +1137 -0
  332. package/dist/providers/coding-proxy/registry/store.d.ts +127 -0
  333. package/dist/providers/coding-proxy/registry/store.js +70 -0
  334. package/dist/providers/coding-proxy/runtime.d.ts +17 -0
  335. package/dist/providers/coding-proxy/runtime.js +63 -0
  336. package/dist/providers/coding-proxy/secure-fetch.js +0 -1
  337. package/dist/providers/coding-proxy/server.d.ts +11 -0
  338. package/dist/providers/coding-proxy/server.js +163 -0
  339. package/dist/providers/coding-proxy/types.d.ts +33 -1
  340. package/dist/providers/coding-proxy/types.js +12 -1
  341. package/dist/providers/engine/types.d.ts +29 -0
  342. package/dist/providers/executor/build.d.ts +2 -2
  343. package/dist/providers/executor/composition.d.ts +3 -0
  344. package/dist/providers/executor/composition.js +28 -1
  345. package/dist/providers/executor/container.d.ts +134 -4
  346. package/dist/providers/executor/container.js +394 -37
  347. package/dist/providers/executor/dbos.d.ts +4 -3
  348. package/dist/providers/executor/dbos.js +7 -5
  349. package/dist/providers/executor/in-process.d.ts +2 -3
  350. package/dist/providers/executor/routing.d.ts +13 -0
  351. package/dist/providers/executor/routing.js +18 -0
  352. package/dist/providers/executor/types.d.ts +34 -0
  353. package/dist/providers/issue-tracker/adf.d.ts +31 -0
  354. package/dist/providers/issue-tracker/adf.js +181 -0
  355. package/dist/providers/issue-tracker/index.d.ts +5 -0
  356. package/dist/providers/issue-tracker/index.js +12 -0
  357. package/dist/providers/issue-tracker/jira-client.d.ts +41 -0
  358. package/dist/providers/issue-tracker/jira-client.js +151 -0
  359. package/dist/providers/issue-tracker/jira-events.d.ts +3 -0
  360. package/dist/providers/issue-tracker/jira-events.js +98 -0
  361. package/dist/providers/issue-tracker/jira.d.ts +116 -0
  362. package/dist/providers/issue-tracker/jira.js +502 -0
  363. package/dist/providers/issue-tracker/types.d.ts +269 -0
  364. package/dist/providers/issue-tracker/types.js +16 -0
  365. package/dist/providers/jobs/claude-tool-setup.d.ts +23 -0
  366. package/dist/providers/jobs/claude-tool-setup.js +50 -0
  367. package/dist/providers/jobs/collect-prune.d.ts +7 -0
  368. package/dist/providers/jobs/collect-prune.js +27 -0
  369. package/dist/providers/jobs/docker-isolation.d.ts +48 -3
  370. package/dist/providers/jobs/docker-isolation.js +213 -22
  371. package/dist/providers/jobs/docker-services.d.ts +35 -0
  372. package/dist/providers/jobs/docker-services.js +191 -0
  373. package/dist/providers/jobs/docker.d.ts +53 -2
  374. package/dist/providers/jobs/docker.js +307 -32
  375. package/dist/providers/jobs/fake-kubernetes-api.d.ts +1 -0
  376. package/dist/providers/jobs/fake-kubernetes-api.js +12 -3
  377. package/dist/providers/jobs/kubernetes-isolation.d.ts +17 -2
  378. package/dist/providers/jobs/kubernetes-isolation.js +238 -57
  379. package/dist/providers/jobs/kubernetes-platform.d.ts +10 -4
  380. package/dist/providers/jobs/kubernetes-platform.js +11 -5
  381. package/dist/providers/jobs/kubernetes-preflight.js +3 -0
  382. package/dist/providers/jobs/kubernetes.d.ts +24 -2
  383. package/dist/providers/jobs/kubernetes.js +153 -22
  384. package/dist/providers/jobs/service-state.d.ts +22 -0
  385. package/dist/providers/jobs/service-state.js +17 -0
  386. package/dist/providers/jobs/types.d.ts +18 -0
  387. package/dist/providers/llm/anthropic.d.ts +3 -3
  388. package/dist/providers/llm/anthropic.js +3 -5
  389. package/dist/providers/llm/bedrock.d.ts +3 -3
  390. package/dist/providers/llm/bedrock.js +3 -8
  391. package/dist/providers/llm/catalog-lookup.d.ts +10 -0
  392. package/dist/providers/llm/catalog-lookup.js +15 -0
  393. package/dist/providers/llm/catalog-shipped.d.ts +18 -0
  394. package/dist/providers/llm/catalog-shipped.js +197 -0
  395. package/dist/providers/llm/catalog-store.d.ts +58 -0
  396. package/dist/providers/llm/catalog-store.js +138 -0
  397. package/dist/providers/llm/catalog-types.d.ts +66 -0
  398. package/dist/providers/llm/catalog-types.js +64 -0
  399. package/dist/providers/llm/catalog.d.ts +61 -0
  400. package/dist/providers/llm/catalog.js +147 -0
  401. package/dist/providers/llm/claude-messages.d.ts +5 -1
  402. package/dist/providers/llm/claude-messages.js +1 -0
  403. package/dist/providers/llm/claude-provider.d.ts +10 -12
  404. package/dist/providers/llm/claude-provider.js +15 -6
  405. package/dist/providers/llm/index.d.ts +9 -6
  406. package/dist/providers/llm/index.js +8 -5
  407. package/dist/providers/llm/openai.d.ts +14 -5
  408. package/dist/providers/llm/openai.js +24 -14
  409. package/dist/providers/llm/pricing-core.d.ts +5 -3
  410. package/dist/providers/llm/registration.js +8 -12
  411. package/dist/providers/llm/routing.d.ts +21 -11
  412. package/dist/providers/llm/routing.js +47 -13
  413. package/dist/providers/llm/types.d.ts +12 -0
  414. package/dist/providers/llm/types.js +8 -1
  415. package/dist/providers/review-host/diff-lines.d.ts +16 -0
  416. package/dist/providers/review-host/diff-lines.js +59 -0
  417. package/dist/providers/review-host/github-events.d.ts +6 -0
  418. package/dist/providers/review-host/github-events.js +226 -0
  419. package/dist/providers/review-host/github-user-auth.d.ts +38 -0
  420. package/dist/providers/review-host/github-user-auth.js +128 -0
  421. package/dist/providers/review-host/github.d.ts +57 -0
  422. package/dist/providers/review-host/github.js +568 -0
  423. package/dist/providers/review-host/index.d.ts +12 -0
  424. package/dist/providers/review-host/index.js +30 -0
  425. package/dist/providers/review-host/review-format.d.ts +19 -0
  426. package/dist/providers/review-host/review-format.js +59 -0
  427. package/dist/providers/review-host/types.d.ts +309 -0
  428. package/dist/providers/review-host/types.js +24 -0
  429. package/dist/providers/vcs/git.d.ts +16 -6
  430. package/dist/providers/vcs/git.js +78 -34
  431. package/dist/providers/vcs/github.d.ts +94 -3
  432. package/dist/providers/vcs/github.js +223 -17
  433. package/dist/providers/vcs/types.d.ts +48 -4
  434. package/dist/quickstart/index.d.ts +2 -0
  435. package/dist/quickstart/index.js +36 -8
  436. package/dist/sandbox/fetch-policy.d.ts +20 -2
  437. package/dist/sandbox/fetch-policy.js +64 -3
  438. package/dist/sandbox/host-functions.d.ts +9 -1
  439. package/dist/sandbox/host-functions.js +12 -5
  440. package/dist/serve.js +8 -2
  441. package/dist/viewer/api-schema.d.ts +2757 -0
  442. package/dist/viewer/api-schema.js +165 -0
  443. package/dist/viewer/build-schemas.d.ts +2 -0
  444. package/dist/viewer/build-schemas.js +18 -0
  445. package/dist/viewer/event-bus.d.ts +38 -0
  446. package/dist/viewer/event-bus.js +232 -0
  447. package/dist/viewer/graph.d.ts +40 -0
  448. package/dist/viewer/graph.js +243 -0
  449. package/dist/viewer/http.d.ts +30 -0
  450. package/dist/viewer/http.js +133 -0
  451. package/dist/viewer/run-detail.d.ts +4 -0
  452. package/dist/viewer/run-detail.js +61 -0
  453. package/dist/wardby-bin.js +11 -0
  454. package/docs/README.md +40 -0
  455. package/docs/agent-recipes.md +383 -0
  456. package/docs/architecture-runtime.md +90 -0
  457. package/docs/assets/brand/wardby-icon-512.png +0 -0
  458. package/docs/assets/brand/wardby-icon.svg +16 -0
  459. package/docs/assets/brand/wardby-mascot-profile-512.png +0 -0
  460. package/docs/assets/brand/wardby-mascot.png +0 -0
  461. package/docs/assets/brand/wardby-mascot.svg +5 -0
  462. package/docs/assets/wardby-workflow.svg +106 -0
  463. package/docs/code-review-agents.md +508 -0
  464. package/docs/coding-agent-setup.md +175 -0
  465. package/docs/coding-packages.md +455 -0
  466. package/docs/coding-services.md +300 -0
  467. package/docs/coding-worker-byo-images.md +98 -0
  468. package/docs/coding-worker-isolation.md +1068 -0
  469. package/docs/getting-started-gke.md +632 -0
  470. package/docs/getting-started-identity-provider.md +319 -0
  471. package/docs/getting-started.md +147 -0
  472. package/docs/jira-agents.md +649 -0
  473. package/docs/knowledge.md +387 -0
  474. package/docs/models.md +221 -0
  475. package/docs/observability.md +53 -0
  476. package/docs/release-verification.md +66 -0
  477. package/docs/security-deployment.md +667 -0
  478. package/docs/viewer-api.md +142 -0
  479. package/help/admin-viewer.md +39 -0
  480. package/help/agent-recipes.md +173 -0
  481. package/help/architecture-agent.md +189 -0
  482. package/help/builder-agent.md +80 -0
  483. package/help/code-review-agents.md +37 -0
  484. package/help/coding-packages.md +31 -0
  485. package/help/coding-services.md +71 -0
  486. package/help/cost-attribution.md +67 -0
  487. package/help/creating-agents.md +90 -0
  488. package/help/deploy-gke.md +45 -0
  489. package/help/deployment-targets.md +39 -0
  490. package/help/errors/budget-group-exhausted.md +25 -0
  491. package/help/errors/docker-isolation-unsupported.md +26 -0
  492. package/help/errors/model-unavailable.md +63 -0
  493. package/help/errors/protected-path.md +45 -0
  494. package/help/errors/repo-access.md +27 -0
  495. package/help/errors/service-declaration-invalid.md +27 -0
  496. package/help/errors/service-declaration-unavailable.md +25 -0
  497. package/help/errors/service-launcher-unsupported.md +30 -0
  498. package/help/errors/service-not-allowed.md +27 -0
  499. package/help/errors/service-unknown.md +23 -0
  500. package/help/errors/service-unready.md +49 -0
  501. package/help/getting-started.md +32 -0
  502. package/help/github.md +48 -0
  503. package/help/identity-and-access.md +44 -0
  504. package/help/jira.md +135 -0
  505. package/help/knowledge.md +47 -0
  506. package/help/mcp.md +30 -0
  507. package/help/models.md +90 -0
  508. package/help/native-capabilities.md +30 -0
  509. package/help/observability.md +41 -0
  510. package/help/operating-agents.md +36 -0
  511. package/help/security.md +27 -0
  512. package/help/troubleshooting/budgets.md +32 -0
  513. package/help/troubleshooting/coding-workers.md +47 -0
  514. package/help/troubleshooting/repository-access.md +27 -0
  515. package/package.json +14 -3
  516. package/prisma/migrations/20260925010000_coding_collect_exclude/migration.sql +8 -0
  517. package/prisma/migrations/20260925015000_allowed_egress_default/migration.sql +8 -0
  518. package/prisma/migrations/20260925020000_coding_package_registry/migration.sql +55 -0
  519. package/prisma/migrations/20260925030000_tool_name_per_owner/migration.sql +11 -0
  520. package/prisma/migrations/20260926010000_run_trigger_host_event/migration.sql +7 -0
  521. package/prisma/migrations/20260926020000_code_review_hosts/migration.sql +53 -0
  522. package/prisma/migrations/20260926030000_auth_user_roles/migration.sql +9 -0
  523. package/prisma/migrations/20260926040000_agent_effort/migration.sql +6 -0
  524. package/prisma/migrations/20260926050000_registry_lockfile_plan/migration.sql +32 -0
  525. package/prisma/migrations/20260926050000_repo_access_authorization/migration.sql +82 -0
  526. package/prisma/migrations/20260926060000_registry_plan_refusal/migration.sql +19 -0
  527. package/prisma/migrations/20260926100000_registry_plan_refusal_published_at/migration.sql +5 -0
  528. package/prisma/migrations/20260926190000_run_host_status/migration.sql +18 -0
  529. package/prisma/migrations/20260926210000_run_host_status_at_dispatch/migration.sql +5 -0
  530. package/prisma/migrations/20260927010000_resource_grants/migration.sql +72 -0
  531. package/prisma/migrations/20260927020000_proxy_session_budget_exhausted/migration.sql +3 -0
  532. package/prisma/migrations/20260927030000_coding_debug_trace/migration.sql +4 -0
  533. package/prisma/migrations/20260927040000_proxy_session_upstream_failure/migration.sql +3 -0
  534. package/prisma/migrations/20260927050000_coding_run_services/migration.sql +74 -0
  535. package/prisma/migrations/20260928000000_coding_run_tool_image/migration.sql +5 -0
  536. package/prisma/migrations/20260930000000_jira_issue_projects/migration.sql +34 -0
  537. package/prisma/migrations/20261001000000_jira_phase2_allowlists/migration.sql +3 -0
  538. package/prisma/migrations/20261001010000_jira_link_types_allowlist/migration.sql +2 -0
  539. package/prisma/migrations/20261002000000_jira_coding_bridge/migration.sql +28 -0
  540. package/prisma/migrations/20261002010000_jira_issue_creation/migration.sql +25 -0
  541. package/prisma/migrations/20261003000000_issue_cost_attribution/migration.sql +56 -0
  542. package/prisma/migrations/20261003010000_coding_run_service_status/migration.sql +23 -0
  543. package/prisma/migrations/20261003020000_viewer_notify/migration.sql +54 -0
  544. package/prisma/migrations/20261003030000_viewer_notify_fixes/migration.sql +47 -0
  545. package/prisma/migrations/20261003040000_viewer_indexes/migration.sql +12 -0
  546. package/prisma/migrations/20261004000000_model_catalog/migration.sql +26 -0
  547. package/prisma/schema.prisma +663 -21
  548. package/dist/mcp/tools/models.d.ts +0 -8
  549. package/dist/mcp/tools/models.js +0 -15
  550. package/dist/providers/llm/pricing-anthropic.d.ts +0 -4
  551. package/dist/providers/llm/pricing-anthropic.js +0 -30
  552. package/dist/providers/llm/pricing-bedrock-claude.d.ts +0 -12
  553. package/dist/providers/llm/pricing-bedrock-claude.js +0 -37
  554. package/dist/providers/llm/pricing.d.ts +0 -30
  555. package/dist/providers/llm/pricing.js +0 -74
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Linking a wardby principal to its verified host identity (GitHub today).
3
+ *
4
+ * 1. startHostIdentityLink: a single-use OAuth state and a PKCE verifier are
5
+ * stored (the state only as a hash) for 10 minutes; the caller gets the
6
+ * host's authorize URL.
7
+ * 2. completeHostIdentityCallback: the host redirects the browser back with
8
+ * the state; it is consumed atomically, the code is exchanged (the user
9
+ * token is revoked at once by the authorizer), and a one-time
10
+ * confirmation code is shown to the person in the browser.
11
+ * 3. confirmHostIdentityLink: the SAME principal that started the link
12
+ * submits the code (at most 5 tries). Only then is the identity stored.
13
+ *
14
+ * Step 3 is what stops an attacker from sending a victim their own authorize
15
+ * URL: the host skips its consent screen for users who already authorized the
16
+ * App, so one click would otherwise bind the victim's identity to the
17
+ * attacker's principal. The code is shown only in the victim's browser.
18
+ * A host user belongs to at most one principal.
19
+ */
20
+ import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
21
+ import { Prisma } from "#prisma";
22
+ import { logger } from "./logger.js";
23
+ const log = logger.child({ module: "host-identity-links" });
24
+ export const HOST_LINK_TTL_MS = 10 * 60_000;
25
+ export const HOST_LINK_MAX_ATTEMPTS = 5;
26
+ export const HOST_LINK_MAX_PENDING = 5;
27
+ const BASE32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
28
+ const CODE_LENGTH = 8;
29
+ export class HostLinkError extends Error {
30
+ status;
31
+ constructor(status, message) {
32
+ super(message);
33
+ this.status = status;
34
+ this.name = "HostLinkError";
35
+ }
36
+ }
37
+ /** The browser callback a provider's link flow returns to (registered as the App's Callback URL). */
38
+ export function userCallbackPath(provider) {
39
+ return `/hosts/${provider}/user-callback`;
40
+ }
41
+ function sha256(value) {
42
+ return createHash("sha256").update(value, "utf8").digest("hex");
43
+ }
44
+ function confirmationCode() {
45
+ // 5 bits per character from a uniformly random byte's low bits (256 % 32 === 0: no bias).
46
+ return [...randomBytes(CODE_LENGTH)].map((b) => BASE32[b & 31]).join("");
47
+ }
48
+ export async function startHostIdentityLink(input) {
49
+ const now = input.now ?? new Date();
50
+ const provider = input.authorizer.provider;
51
+ await input.db.hostIdentityLinkRequest.deleteMany({ where: { expiresAt: { lte: now } } });
52
+ const pending = await input.db.hostIdentityLinkRequest.count({
53
+ where: { principalId: input.principalId, provider },
54
+ });
55
+ if (pending >= HOST_LINK_MAX_PENDING) {
56
+ throw new HostLinkError(429, "Too many account links in progress; finish one or wait 10 minutes.");
57
+ }
58
+ const state = randomBytes(32).toString("base64url");
59
+ const codeVerifier = randomBytes(32).toString("base64url");
60
+ const codeChallenge = createHash("sha256").update(codeVerifier).digest("base64url");
61
+ const expiresAt = new Date(now.getTime() + HOST_LINK_TTL_MS);
62
+ await input.db.hostIdentityLinkRequest.create({
63
+ data: { principalId: input.principalId, provider, stateHash: sha256(state), codeVerifier, expiresAt },
64
+ });
65
+ return {
66
+ authorizeUrl: input.authorizer.authorizeUrl({ state, codeChallenge, redirectUri: input.redirectUri }),
67
+ expiresAt,
68
+ };
69
+ }
70
+ export async function completeHostIdentityCallback(input) {
71
+ const now = input.now ?? new Date();
72
+ if (!input.state || !input.code || input.state.length > 256 || input.code.length > 256)
73
+ return { kind: "invalid" };
74
+ const stateHash = sha256(input.state);
75
+ // Single use: only the first callback for a live state wins.
76
+ const { count } = await input.db.hostIdentityLinkRequest.updateMany({
77
+ where: { stateHash, provider: input.authorizer.provider, callbackAt: null, expiresAt: { gt: now } },
78
+ data: { callbackAt: now },
79
+ });
80
+ if (count !== 1)
81
+ return { kind: "invalid" };
82
+ const request = await input.db.hostIdentityLinkRequest.findUnique({
83
+ where: { stateHash },
84
+ include: { principal: { select: { subject: true } } },
85
+ });
86
+ if (!request)
87
+ return { kind: "invalid" };
88
+ let user;
89
+ try {
90
+ user = await input.authorizer.complete({
91
+ code: input.code,
92
+ codeVerifier: request.codeVerifier,
93
+ redirectUri: input.redirectUri,
94
+ });
95
+ }
96
+ catch (err) {
97
+ log.warn({ err, provider: request.provider }, "host account link: the host refused the authorization");
98
+ await input.db.hostIdentityLinkRequest.delete({ where: { id: request.id } }).catch(() => undefined);
99
+ return { kind: "failed" };
100
+ }
101
+ const code = confirmationCode();
102
+ await input.db.hostIdentityLinkRequest.update({
103
+ where: { id: request.id },
104
+ // The verifier has done its job; keep nothing reusable.
105
+ data: { hostUserId: user.hostUserId, login: user.login, confirmHash: sha256(code), codeVerifier: "" },
106
+ });
107
+ return {
108
+ kind: "confirm",
109
+ code: `${code.slice(0, 4)}-${code.slice(4)}`,
110
+ login: user.login,
111
+ subject: request.principal.subject,
112
+ };
113
+ }
114
+ export async function confirmHostIdentityLink(input) {
115
+ const now = input.now ?? new Date();
116
+ const code = input.confirmationCode.toUpperCase().replace(/[\s-]/g, "");
117
+ if (!new RegExp(`^[${BASE32}]{${CODE_LENGTH}}$`).test(code)) {
118
+ throw new HostLinkError(400, "The confirmation code is 8 letters and digits, as shown on the page (XXXX-XXXX).");
119
+ }
120
+ const request = await input.db.hostIdentityLinkRequest.findFirst({
121
+ where: {
122
+ principalId: input.principalId,
123
+ provider: input.provider,
124
+ confirmHash: { not: null },
125
+ hostUserId: { not: null },
126
+ login: { not: null },
127
+ attempts: { lt: HOST_LINK_MAX_ATTEMPTS },
128
+ expiresAt: { gt: now },
129
+ },
130
+ orderBy: { callbackAt: "desc" },
131
+ });
132
+ if (!request?.confirmHash || !request.hostUserId || !request.login)
133
+ throw noPendingLink();
134
+ // Spend an attempt BEFORE comparing, atomically: concurrent guesses can't
135
+ // each get a comparison before the counter saturates.
136
+ const claimed = await input.db.hostIdentityLinkRequest.updateMany({
137
+ where: { id: request.id, attempts: { lt: HOST_LINK_MAX_ATTEMPTS }, expiresAt: { gt: now } },
138
+ data: { attempts: { increment: 1 } },
139
+ });
140
+ if (claimed.count !== 1)
141
+ throw noPendingLink();
142
+ const presented = Buffer.from(sha256(code), "hex");
143
+ const expected = Buffer.from(request.confirmHash, "hex");
144
+ if (presented.length !== expected.length || !timingSafeEqual(presented, expected)) {
145
+ const after = await input.db.hostIdentityLinkRequest.findUnique({ where: { id: request.id } });
146
+ const left = Math.max(0, HOST_LINK_MAX_ATTEMPTS - (after?.attempts ?? HOST_LINK_MAX_ATTEMPTS));
147
+ throw new HostLinkError(400, left > 0
148
+ ? `That confirmation code is not right; ${left} attempt${left === 1 ? "" : "s"} left.`
149
+ : "That confirmation code is not right, and this link attempt is used up. Start again with link_host_account.");
150
+ }
151
+ const hostUserId = request.hostUserId;
152
+ const login = request.login;
153
+ try {
154
+ await input.db.$transaction(async (tx) => {
155
+ // Consume the request first: a concurrent confirmation of the same code loses here.
156
+ // (Its attempt was already spent above, so attempts may now equal the limit.)
157
+ const consumed = await tx.hostIdentityLinkRequest.deleteMany({
158
+ where: { id: request.id, expiresAt: { gt: now } },
159
+ });
160
+ if (consumed.count !== 1)
161
+ throw new HostLinkError(404, "This link attempt is no longer valid; start again.");
162
+ const holder = await tx.hostIdentity.findUnique({
163
+ where: { provider_hostUserId: { provider: input.provider, hostUserId } },
164
+ });
165
+ if (holder && holder.principalId !== input.principalId)
166
+ throw alreadyLinked();
167
+ await tx.hostIdentity.upsert({
168
+ where: { principalId_provider: { principalId: input.principalId, provider: input.provider } },
169
+ create: { principalId: input.principalId, provider: input.provider, hostUserId, login },
170
+ update: { hostUserId, login, linkedAt: now },
171
+ });
172
+ }, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable });
173
+ }
174
+ catch (err) {
175
+ if (err instanceof HostLinkError)
176
+ throw err;
177
+ if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === "P2002")
178
+ throw alreadyLinked();
179
+ throw err;
180
+ }
181
+ return { hostUserId, login };
182
+ }
183
+ function noPendingLink() {
184
+ return new HostLinkError(404, "No account link is waiting for a confirmation code from you. Start again with link_host_account (no code).");
185
+ }
186
+ function alreadyLinked() {
187
+ return new HostLinkError(409, "That GitHub account is already linked to another wardby account. An operator can unlink it " +
188
+ "(`wardby auth host-account unlink --subject <subject>`) if it should move to you.");
189
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * The status comment for a run started by an @-mention. Where to comment is
3
+ * recorded with the run itself (mentionStatusRow, written in the dispatch
4
+ * transaction); the "working on it" comment is posted right after the webhook
5
+ * is answered, and edited once with the outcome — the pull requests its coding
6
+ * sub-runs opened or updated, the agent's reply when none came out, or why it
7
+ * did not succeed. A run that ends before its comment was posted (the instance
8
+ * died first, say) still gets one: the reconciler posts the outcome as a new
9
+ * comment. Best effort throughout: nothing here throws.
10
+ */
11
+ import type { Prisma, PrismaClient, Run } from "#prisma";
12
+ import type { CodeReviewHost, ReviewHostProvider, ReviewHostRegistry } from "../providers/review-host/types.js";
13
+ export type HostStatusDb = Pick<PrismaClient, "runHostStatus" | "run">;
14
+ export type FinishedRun = Pick<Run, "id" | "status" | "finalText">;
15
+ /** The parts of a CodingRun result this comment uses. */
16
+ export interface PullRequestOutcome {
17
+ outcome: "pull_request_opened" | "pull_request_updated";
18
+ repository: string;
19
+ pullRequestNumber: number;
20
+ /** The result's own pull request URL; absent from a malformed result. */
21
+ pullRequestUrl?: string;
22
+ /** The code host the repository is on. */
23
+ codeProvider: string;
24
+ /** The coding sub-run that produced it; set by collectRunOutcome. */
25
+ runId?: string;
26
+ }
27
+ /** A sub-run that ended without succeeding. */
28
+ export interface FailedChild {
29
+ id: string;
30
+ status: string;
31
+ /** The coding sub-run's failure category; `provider_<class>` names a model-provider refusal. */
32
+ failureCategory?: string | null;
33
+ /** The sub-run's Run.error; read only for a service refusal's host sentence (coding/services/wording.ts). */
34
+ error?: string | null;
35
+ /** The service names on the coding sub-run (CodingRun.services), for one that never became ready. */
36
+ services?: string[];
37
+ }
38
+ export declare const runLine: (runId: string) => string;
39
+ export declare function workingBody(runId: string): string;
40
+ /** The status row for a mention run, created in the same transaction as the run. */
41
+ export declare function mentionStatusRow(provider: ReviewHostProvider, event: {
42
+ repository: string;
43
+ number: number;
44
+ replyToReviewCommentId?: string;
45
+ }, runId: string): Prisma.RunHostStatusUncheckedCreateInput;
46
+ export declare function outcomeBody(run: FinishedRun, repository: string, pullRequests: PullRequestOutcome[], failedChildren?: FailedChild[], opts?: {
47
+ budgetSentence?: string;
48
+ noPullRequestText?: string;
49
+ /** The agent's reply IS the answer (no separate reply comment): shown as written, not quoted. */
50
+ replyAsAnswer?: boolean;
51
+ }): string;
52
+ /** What collectRunOutcome reads: a run's children and, when out of budget, its budget facts. */
53
+ export type RunOutcomeDb = Pick<PrismaClient, "run">;
54
+ /** The parts of a finished run's outcome a status comment reports; shared by GitHub and Jira status comments. */
55
+ export declare function collectRunOutcome(db: RunOutcomeDb, run: FinishedRun): Promise<{
56
+ pullRequests: PullRequestOutcome[];
57
+ failedChildren: FailedChild[];
58
+ budgetSentence?: string;
59
+ }>;
60
+ /**
61
+ * Writes a finished run's outcome to its status comment and marks it
62
+ * complete: edits the comment when it exists; when it does not yet, posts the
63
+ * outcome as a new comment only if `postIfMissing` (the reconciler, well after
64
+ * the run ended), and otherwise leaves the row for postMentionStatus, which
65
+ * is about to post it. A failed host call leaves the row open for the
66
+ * reconciler to retry.
67
+ */
68
+ export declare function completeHostStatus(db: HostStatusDb, run: FinishedRun, hosts: ReviewHostRegistry | undefined, opts?: {
69
+ postIfMissing?: boolean;
70
+ }): Promise<void>;
71
+ /**
72
+ * Posts the "working on it" comment for a mention run whose status row was
73
+ * written at dispatch, and records the comment on the row. When the run
74
+ * already ended (a fast failure can beat this follow-up), completes it at
75
+ * once. Does nothing when the row is gone, already has a comment, or is
76
+ * already complete. Never throws.
77
+ */
78
+ export declare function postMentionStatus(db: HostStatusDb, host: CodeReviewHost, runId: string, hosts: ReviewHostRegistry | undefined): Promise<void>;
@@ -0,0 +1,228 @@
1
+ import { CODING_CODE_PROVIDER } from "../coding/protocol.js";
2
+ import { storedServiceNames } from "../coding/services/catalog.js";
3
+ import { SERVICE_UNREADY_CATEGORY, serviceRefusalSentence, serviceUnreadySentence, } from "../coding/services/wording.js";
4
+ import { PROTECTED_PATH_CATEGORY, PROTECTED_PATH_HOST_LINE } from "../coding/protected-path-wording.js";
5
+ import { loadBudgetSentence } from "./budget-wording.js";
6
+ import { logger } from "./logger.js";
7
+ import { providerClassOfCategory, providerSentence } from "./provider-wording.js";
8
+ const log = logger.child({ module: "host-status" });
9
+ /** Keeps the edited comment readable; the full reply stays on the run. */
10
+ const MAX_REPLY_CHARS = 2000;
11
+ const TERMINAL = new Set(["succeeded", "failed", "refused", "lost", "budget_exhausted", "cancelled"]);
12
+ export const runLine = (runId) => `<sub>wardby run \`${runId}\`</sub>`;
13
+ export function workingBody(runId) {
14
+ return `👀 Working on it.\n\n${runLine(runId)}`;
15
+ }
16
+ /** The status row for a mention run, created in the same transaction as the run. */
17
+ export function mentionStatusRow(provider, event, runId) {
18
+ return {
19
+ runId,
20
+ provider,
21
+ repository: event.repository,
22
+ number: event.number,
23
+ commentKind: event.replyToReviewCommentId ? "inline" : "conversation",
24
+ replyToReviewCommentId: event.replyToReviewCommentId ?? null,
25
+ };
26
+ }
27
+ function pullRequestOutcome(result) {
28
+ if (!result || typeof result !== "object")
29
+ return null;
30
+ const r = result;
31
+ if (r.outcome !== "pull_request_opened" && r.outcome !== "pull_request_updated")
32
+ return null;
33
+ if (typeof r.repository !== "string" || typeof r.pullRequestNumber !== "number")
34
+ return null;
35
+ if (!Number.isInteger(r.pullRequestNumber) || r.pullRequestNumber <= 0)
36
+ return null;
37
+ return {
38
+ outcome: r.outcome,
39
+ repository: r.repository,
40
+ pullRequestNumber: r.pullRequestNumber,
41
+ codeProvider: CODING_CODE_PROVIDER,
42
+ ...(typeof r.pullRequestUrl === "string" ? { pullRequestUrl: r.pullRequestUrl } : {}),
43
+ };
44
+ }
45
+ /** The agent's reply as a quote, cut to MAX_REPLY_CHARS, with @-mentions defused so nobody is pinged. */
46
+ const cutReply = (text) => text.length > MAX_REPLY_CHARS ? `${text.slice(0, MAX_REPLY_CHARS)}…` : text;
47
+ function quoteReply(text) {
48
+ return cutReply(text)
49
+ .replace(/@(?=[\w-])/g, "@​")
50
+ .split("\n")
51
+ .map((line) => `> ${line}`)
52
+ .join("\n");
53
+ }
54
+ export function outcomeBody(run, repository, pullRequests, failedChildren = [], opts = {}) {
55
+ const links = pullRequests.map((pr) => {
56
+ const ref = pr.repository.toLowerCase() === repository.toLowerCase()
57
+ ? `#${pr.pullRequestNumber}`
58
+ : `${pr.repository}#${pr.pullRequestNumber}`;
59
+ return pr.outcome === "pull_request_opened" ? `Opened ${ref}` : `Pushed changes to ${ref}`;
60
+ });
61
+ const partial = links.length > 0 ? `\n\n${links.join(", ")}.` : "";
62
+ const footer = runLine(run.id);
63
+ if (run.status === "lost") {
64
+ return `❌ Interrupted before it finished (for example, wardby restarted). Repeat your request to retry.${partial}\n\n${footer}`;
65
+ }
66
+ if (run.status === "budget_exhausted" || run.status === "refused") {
67
+ // A refused run could not start for lack of budget; see budgetSentence.
68
+ return `❌ ${opts.budgetSentence ?? "Out of budget."}${partial}\n\n${footer}`;
69
+ }
70
+ if (run.status !== "succeeded") {
71
+ // Only the status: a run's error text can carry internal detail that does not belong on the host.
72
+ return `❌ Stopped: the run ended with status \`${run.status}\`.${partial}\n\n${footer}`;
73
+ }
74
+ const reply = run.finalText?.trim();
75
+ const quoted = reply ? `\n\n${quoteReply(reply)}` : "";
76
+ if (failedChildren.length > 0) {
77
+ // The agent itself finished, but the work it handed off did not.
78
+ const outOfBudget = failedChildren.filter((c) => c.status === "budget_exhausted");
79
+ const providerClassOf = (c) => c.status === "failed" ? providerClassOfCategory(c.failureCategory) : null;
80
+ // A coding sub-run its services stopped: refused at dispatch, or a sidecar never became ready.
81
+ const serviceSentenceOf = (c) => c.status === "refused"
82
+ ? serviceRefusalSentence(c.error)
83
+ : c.status === "failed" && c.failureCategory === SERVICE_UNREADY_CATEGORY
84
+ ? serviceUnreadySentence(c.services ?? [])
85
+ : null;
86
+ // No path reaches this comment (see coding/protected-path-wording.ts), only the category.
87
+ const protectedPathOf = (c) => c.status === "failed" && c.failureCategory === PROTECTED_PATH_CATEGORY;
88
+ const providerClasses = new Set(failedChildren.map(providerClassOf).filter((c) => c !== null));
89
+ const serviceSentences = new Set(failedChildren.map(serviceSentenceOf).filter((s) => s !== null));
90
+ const protectedPathFailed = failedChildren.some(protectedPathOf);
91
+ const other = failedChildren.filter((c) => c.status !== "budget_exhausted" &&
92
+ providerClassOf(c) === null &&
93
+ serviceSentenceOf(c) === null &&
94
+ !protectedPathOf(c));
95
+ const lines = [];
96
+ if (outOfBudget.length > 0) {
97
+ lines.push(`A sub-run ran out of budget: ${outOfBudget.map((c) => `\`${c.id}\``).join(", ")}.`);
98
+ }
99
+ // The class only, never the provider's code: the sentence says who has to act.
100
+ for (const providerClass of providerClasses) {
101
+ lines.push(`A sub-run could not reach the model: ${providerSentence(providerClass)}`);
102
+ }
103
+ // The fixed sentence only, never the Run.error code in front of it.
104
+ for (const sentence of serviceSentences) {
105
+ lines.push(`A sub-run could not start: ${sentence}`);
106
+ }
107
+ if (protectedPathFailed)
108
+ lines.push(PROTECTED_PATH_HOST_LINE);
109
+ if (other.length > 0) {
110
+ lines.push(`A sub-run did not succeed: ${other.map((c) => `\`${c.id}\` (\`${c.status}\`)`).join(", ")}.`);
111
+ }
112
+ return `❌ ${lines.join(" ")}${partial}${quoted}\n\n${footer}`;
113
+ }
114
+ if (links.length > 0)
115
+ return `✅ ${links.join(", ")}.\n\n${footer}`;
116
+ if (opts.replyAsAnswer && reply)
117
+ return `✅ ${cutReply(reply)}\n\n${footer}`;
118
+ return `✅ ${opts.noPullRequestText ?? "Finished without opening a pull request."}${quoted}\n\n${footer}`;
119
+ }
120
+ /** The parts of a finished run's outcome a status comment reports; shared by GitHub and Jira status comments. */
121
+ export async function collectRunOutcome(db, run) {
122
+ const children = await db.run.findMany({
123
+ where: { parentRunId: run.id },
124
+ select: {
125
+ id: true,
126
+ status: true,
127
+ error: true,
128
+ codingRun: { select: { result: true, failureCategory: true, services: true } },
129
+ },
130
+ orderBy: { startedAt: "asc" },
131
+ });
132
+ const pullRequests = children
133
+ .map((c) => {
134
+ const pr = pullRequestOutcome(c.codingRun?.result);
135
+ return pr ? { ...pr, runId: c.id } : null;
136
+ })
137
+ .filter((pr) => pr !== null);
138
+ const failedChildren = children
139
+ .filter((c) => TERMINAL.has(c.status) && c.status !== "succeeded")
140
+ .map((c) => ({
141
+ id: c.id,
142
+ status: c.status,
143
+ failureCategory: c.codingRun?.failureCategory ?? null,
144
+ error: c.error ?? null,
145
+ services: storedServiceNames(c.codingRun?.services),
146
+ }));
147
+ const budgetSentence = run.status === "budget_exhausted" || run.status === "refused"
148
+ ? await loadBudgetSentence(db, run.id, run.status)
149
+ : undefined;
150
+ return { pullRequests, failedChildren, ...(budgetSentence ? { budgetSentence } : {}) };
151
+ }
152
+ /**
153
+ * Writes a finished run's outcome to its status comment and marks it
154
+ * complete: edits the comment when it exists; when it does not yet, posts the
155
+ * outcome as a new comment only if `postIfMissing` (the reconciler, well after
156
+ * the run ended), and otherwise leaves the row for postMentionStatus, which
157
+ * is about to post it. A failed host call leaves the row open for the
158
+ * reconciler to retry.
159
+ */
160
+ export async function completeHostStatus(db, run, hosts, opts = {}) {
161
+ if (!hosts)
162
+ return;
163
+ try {
164
+ const status = await db.runHostStatus.findUnique({ where: { runId: run.id } });
165
+ if (!status || status.completedAt)
166
+ return;
167
+ if (!status.commentId && !opts.postIfMissing)
168
+ return;
169
+ const host = hosts[status.provider];
170
+ if (!host)
171
+ return;
172
+ const { pullRequests, failedChildren, budgetSentence } = await collectRunOutcome(db, run);
173
+ const body = outcomeBody(run, status.repository, pullRequests, failedChildren, { budgetSentence });
174
+ let commentId = status.commentId;
175
+ if (commentId) {
176
+ await host.editComment(status.repository, {
177
+ kind: status.commentKind === "inline" ? "inline" : "conversation",
178
+ id: commentId,
179
+ body,
180
+ });
181
+ }
182
+ else {
183
+ commentId = (await host.comment(status.repository, {
184
+ number: status.number,
185
+ body,
186
+ ...(status.replyToReviewCommentId ? { replyToReviewCommentId: status.replyToReviewCommentId } : {}),
187
+ })).id;
188
+ }
189
+ await db.runHostStatus.update({ where: { runId: run.id }, data: { commentId, completedAt: new Date() } });
190
+ }
191
+ catch (err) {
192
+ log.warn({ err, runId: run.id }, "could not complete the run's status comment");
193
+ }
194
+ }
195
+ /**
196
+ * Posts the "working on it" comment for a mention run whose status row was
197
+ * written at dispatch, and records the comment on the row. When the run
198
+ * already ended (a fast failure can beat this follow-up), completes it at
199
+ * once. Does nothing when the row is gone, already has a comment, or is
200
+ * already complete. Never throws.
201
+ */
202
+ export async function postMentionStatus(db, host, runId, hosts) {
203
+ try {
204
+ const status = await db.runHostStatus.findUnique({ where: { runId } });
205
+ if (!status || status.commentId || status.completedAt)
206
+ return;
207
+ const posted = await host.comment(status.repository, {
208
+ number: status.number,
209
+ body: workingBody(runId),
210
+ ...(status.replyToReviewCommentId ? { replyToReviewCommentId: status.replyToReviewCommentId } : {}),
211
+ });
212
+ const claimed = await db.runHostStatus.updateMany({
213
+ where: { runId, commentId: null, completedAt: null },
214
+ data: { commentId: posted.id },
215
+ });
216
+ if (claimed.count === 0) {
217
+ // The reconciler posted the outcome meanwhile; this comment is surplus.
218
+ log.warn({ runId }, "status comment posted after the outcome; leaving both");
219
+ return;
220
+ }
221
+ const run = await db.run.findUnique({ where: { id: runId }, select: { id: true, status: true, finalText: true } });
222
+ if (run && TERMINAL.has(run.status))
223
+ await completeHostStatus(db, run, hosts);
224
+ }
225
+ catch (err) {
226
+ log.warn({ err, runId }, "could not post the status comment");
227
+ }
228
+ }
@@ -0,0 +1,8 @@
1
+ /** Registers a run for the duration of `work`. */
2
+ export declare function trackRun<T>(runId: string, work: () => Promise<T>): Promise<T>;
3
+ export declare function inFlightRunIds(): string[];
4
+ /**
5
+ * Resolves once no run is executing, or after `timeoutMs`, whichever is
6
+ * first. Returns the ids still executing at that point (empty when drained).
7
+ */
8
+ export declare function waitForInFlightRuns(timeoutMs: number): Promise<string[]>;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Counts the native runs executing in this process, so a shutdown can wait
3
+ * for them instead of abandoning them. Every native run, however it was
4
+ * started (a direct start, a DBOS workflow, a workflow adopted from a dead
5
+ * instance), goes through executeRun, which registers here. Coding jobs are
6
+ * not counted: they run in their own pods and a new instance collects them.
7
+ */
8
+ import { logger } from "./logger.js";
9
+ const log = logger.child({ module: "in-flight-runs" });
10
+ const active = new Set();
11
+ let waiters = [];
12
+ /** Registers a run for the duration of `work`. */
13
+ export async function trackRun(runId, work) {
14
+ active.add(runId);
15
+ try {
16
+ return await work();
17
+ }
18
+ finally {
19
+ active.delete(runId);
20
+ if (active.size === 0) {
21
+ const done = waiters;
22
+ waiters = [];
23
+ for (const wake of done)
24
+ wake();
25
+ }
26
+ }
27
+ }
28
+ export function inFlightRunIds() {
29
+ return [...active];
30
+ }
31
+ /**
32
+ * Resolves once no run is executing, or after `timeoutMs`, whichever is
33
+ * first. Returns the ids still executing at that point (empty when drained).
34
+ */
35
+ export async function waitForInFlightRuns(timeoutMs) {
36
+ if (active.size === 0 || timeoutMs <= 0)
37
+ return inFlightRunIds();
38
+ log.info({ runIds: inFlightRunIds(), timeoutMs }, "waiting for in-flight runs before shutdown");
39
+ await new Promise((resolve) => {
40
+ const timer = setTimeout(() => {
41
+ waiters = waiters.filter((w) => w !== wake);
42
+ resolve();
43
+ }, timeoutMs);
44
+ const wake = () => {
45
+ clearTimeout(timer);
46
+ resolve();
47
+ };
48
+ waiters.push(wake);
49
+ });
50
+ const remaining = inFlightRunIds();
51
+ if (remaining.length > 0)
52
+ log.warn({ runIds: remaining }, "shutdown drain timed out; these runs are abandoned");
53
+ else
54
+ log.info("in-flight runs finished; shutting down");
55
+ return remaining;
56
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Links a run's pull requests to the issue that started it: records each
3
+ * issue/PR pair, puts a web link on the issue, and applies the link's
4
+ * configured status move. Provider-neutral: a code host is only ever
5
+ * { provider, repository, number }. The issue key comes from the run tree
6
+ * (RunIssueStatus), never from model output. The status move is a control-plane
7
+ * action (not gated by allowedTransitions). Nothing here throws, except
8
+ * handlePullRequestClosed's lookup before it has claimed anything (so the
9
+ * webhook delivery is rolled back and redelivered).
10
+ */
11
+ import { type PrismaClient } from "#prisma";
12
+ import { type IssueTracker, type IssueTrackerRegistry } from "../providers/issue-tracker/types.js";
13
+ export type IssueBridgeDb = Pick<PrismaClient, "issuePullRequest">;
14
+ export type PullRequestClosedDb = Pick<PrismaClient, "issuePullRequest" | "agentIssueProject">;
15
+ export interface BridgedPullRequest {
16
+ codeProvider: string;
17
+ repository: string;
18
+ number: number;
19
+ url: string;
20
+ openedByRunId: string;
21
+ outcome: "pull_request_opened" | "pull_request_updated";
22
+ }
23
+ export interface RecordPullRequestsInput {
24
+ issueKey: string;
25
+ issueProvider: string;
26
+ agentId: string;
27
+ /** The status the link moves the issue to when a pull request is opened, if configured. */
28
+ onPullRequestOpened?: string | null;
29
+ pullRequests: BridgedPullRequest[];
30
+ }
31
+ export declare const pullRequestGlobalId: (pr: Pick<BridgedPullRequest, "codeProvider" | "repository" | "number">) => string;
32
+ /**
33
+ * Returns sentences for the outcome comment (a refused status move); empty
34
+ * when there is nothing to say. Safe to repeat (a retried completion): each
35
+ * pair's side effects happen once — the status move only when this call newly
36
+ * recorded an opened pull request, and a merged/closed pair is never re-linked
37
+ * (re-posting its web link would un-resolve it). An unreadable store skips
38
+ * both rather than risk repeating them.
39
+ */
40
+ export declare function recordPullRequests(db: IssueBridgeDb, tracker: IssueTracker, input: RecordPullRequestsInput): Promise<{
41
+ notes: string[];
42
+ }>;
43
+ export interface ClosedPullRequest {
44
+ codeProvider: string;
45
+ repository: string;
46
+ number: number;
47
+ merged: boolean;
48
+ }
49
+ /**
50
+ * A pull request wardby recorded against an issue was merged or closed: marks
51
+ * each still-open pair, and, where the agent's link to the issue's project is
52
+ * still write, comments on the issue (and on merge resolves the web link and
53
+ * applies the link's onPullRequestMerged move). Only rows wardby wrote are
54
+ * touched, matched by the host's own identity for the PR — nothing from the
55
+ * PR's text. The row is claimed first (open → merged/closed), so a duplicate
56
+ * or concurrent delivery finds nothing to do. Throws only when the lookup of
57
+ * linked rows fails — before anything is claimed — so the caller's webhook
58
+ * delivery is rolled back and redelivered; never throws after that.
59
+ */
60
+ export declare function handlePullRequestClosed(db: PullRequestClosedDb, trackers: IssueTrackerRegistry | undefined, pr: ClosedPullRequest): Promise<void>;