@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
+ * 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 { Prisma } from "#prisma";
12
+ import { projectOf, } from "../providers/issue-tracker/types.js";
13
+ import { logger } from "./logger.js";
14
+ const log = logger.child({ module: "issue-bridge" });
15
+ export const pullRequestGlobalId = (pr) => `wardby:pr:${pr.codeProvider}:${pr.repository}#${pr.number}`;
16
+ /**
17
+ * Makes sure the issue/PR pair has a row, without ever overwriting one: a new
18
+ * row is "created"; an existing open one gets its url refreshed ("open"); a
19
+ * merged or closed one is left alone ("settled"). A lost create race (P2002)
20
+ * reads back the winner's row. "unknown" when the store could not be read.
21
+ */
22
+ async function ensurePair(db, input, pr) {
23
+ const where = {
24
+ codeProvider_repository_number_issueProvider_issueKey: {
25
+ codeProvider: pr.codeProvider,
26
+ repository: pr.repository,
27
+ number: pr.number,
28
+ issueProvider: input.issueProvider,
29
+ issueKey: input.issueKey,
30
+ },
31
+ };
32
+ try {
33
+ let existing = await db.issuePullRequest.findUnique({ where, select: { state: true } });
34
+ if (!existing) {
35
+ try {
36
+ await db.issuePullRequest.create({
37
+ data: {
38
+ issueProvider: input.issueProvider,
39
+ issueKey: input.issueKey,
40
+ codeProvider: pr.codeProvider,
41
+ repository: pr.repository,
42
+ number: pr.number,
43
+ url: pr.url,
44
+ agentId: input.agentId,
45
+ openedByRunId: pr.openedByRunId,
46
+ },
47
+ });
48
+ return "created";
49
+ }
50
+ catch (err) {
51
+ if (!(err instanceof Prisma.PrismaClientKnownRequestError && err.code === "P2002"))
52
+ throw err;
53
+ existing = await db.issuePullRequest.findUnique({ where, select: { state: true } });
54
+ if (!existing)
55
+ return "unknown";
56
+ }
57
+ }
58
+ if (existing.state !== "open")
59
+ return "settled";
60
+ await db.issuePullRequest.update({ where, data: { url: pr.url } });
61
+ return "open";
62
+ }
63
+ catch (err) {
64
+ log.warn({ err, issueKey: input.issueKey, repository: pr.repository, number: pr.number }, "could not record the issue/PR pair");
65
+ return "unknown";
66
+ }
67
+ }
68
+ /**
69
+ * Returns sentences for the outcome comment (a refused status move); empty
70
+ * when there is nothing to say. Safe to repeat (a retried completion): each
71
+ * pair's side effects happen once — the status move only when this call newly
72
+ * recorded an opened pull request, and a merged/closed pair is never re-linked
73
+ * (re-posting its web link would un-resolve it). An unreadable store skips
74
+ * both rather than risk repeating them.
75
+ */
76
+ export async function recordPullRequests(db, tracker, input) {
77
+ const notes = [];
78
+ try {
79
+ let newlyOpened = false;
80
+ for (const pr of input.pullRequests) {
81
+ const state = await ensurePair(db, input, pr);
82
+ if (state === "created" && pr.outcome === "pull_request_opened")
83
+ newlyOpened = true;
84
+ if (state !== "created" && state !== "open")
85
+ continue;
86
+ try {
87
+ await tracker.addRemoteLink(input.issueKey, {
88
+ globalId: pullRequestGlobalId(pr),
89
+ url: pr.url,
90
+ title: `${pr.repository}#${pr.number}`,
91
+ });
92
+ }
93
+ catch (err) {
94
+ log.warn({ err, issueKey: input.issueKey, repository: pr.repository, number: pr.number }, "could not link the pull request on the issue");
95
+ }
96
+ }
97
+ const target = input.onPullRequestOpened;
98
+ if (target && newlyOpened) {
99
+ try {
100
+ await tracker.transitionTo(input.issueKey, target);
101
+ }
102
+ catch (err) {
103
+ log.warn({ err, issueKey: input.issueKey, target }, "could not move the issue after a pull request opened");
104
+ notes.push(`Could not move ${input.issueKey} to "${target}"; it may need a manual move.`);
105
+ }
106
+ }
107
+ }
108
+ catch (err) {
109
+ log.warn({ err, issueKey: input.issueKey }, "could not bridge the pull requests to the issue");
110
+ }
111
+ return { notes };
112
+ }
113
+ /**
114
+ * A pull request wardby recorded against an issue was merged or closed: marks
115
+ * each still-open pair, and, where the agent's link to the issue's project is
116
+ * still write, comments on the issue (and on merge resolves the web link and
117
+ * applies the link's onPullRequestMerged move). Only rows wardby wrote are
118
+ * touched, matched by the host's own identity for the PR — nothing from the
119
+ * PR's text. The row is claimed first (open → merged/closed), so a duplicate
120
+ * or concurrent delivery finds nothing to do. Throws only when the lookup of
121
+ * linked rows fails — before anything is claimed — so the caller's webhook
122
+ * delivery is rolled back and redelivered; never throws after that.
123
+ */
124
+ export async function handlePullRequestClosed(db, trackers, pr) {
125
+ if (!trackers)
126
+ return;
127
+ const where = { codeProvider: pr.codeProvider, repository: pr.repository, number: pr.number };
128
+ // Not caught: nothing is claimed yet, and a throw rolls back the delivery so it is redelivered.
129
+ const rows = await db.issuePullRequest.findMany({ where: { ...where, state: "open" } });
130
+ for (const row of rows) {
131
+ try {
132
+ const claimed = await db.issuePullRequest.updateMany({
133
+ where: { id: row.id, state: "open" },
134
+ data: { state: pr.merged ? "merged" : "closed" },
135
+ });
136
+ if (claimed.count === 0)
137
+ continue;
138
+ const tracker = trackers[row.issueProvider];
139
+ if (!tracker)
140
+ continue;
141
+ const link = await db.agentIssueProject.findUnique({
142
+ where: {
143
+ agentId_provider_projectKey: {
144
+ agentId: row.agentId,
145
+ provider: row.issueProvider,
146
+ projectKey: projectOf(row.issueKey),
147
+ },
148
+ },
149
+ select: { access: true, commentVisibilityRole: true, onPullRequestMerged: true },
150
+ });
151
+ if (link?.access !== "write")
152
+ continue;
153
+ const ref = `[${row.repository}#${row.number}](${row.url})`;
154
+ let markdown;
155
+ if (pr.merged) {
156
+ await tracker
157
+ .addRemoteLink(row.issueKey, {
158
+ globalId: pullRequestGlobalId(row),
159
+ url: row.url,
160
+ title: `${row.repository}#${row.number}`,
161
+ status: { resolved: true },
162
+ })
163
+ .catch((err) => log.warn({ err, issueKey: row.issueKey, ...where }, "could not resolve the pull request's web link"));
164
+ markdown = `✅ Pull request ${ref} was merged.`;
165
+ const target = link.onPullRequestMerged;
166
+ if (target) {
167
+ try {
168
+ const moved = await tracker.transitionTo(row.issueKey, target);
169
+ markdown += ` Moved to ${moved.toStatus}.`;
170
+ }
171
+ catch (err) {
172
+ log.warn({ err, issueKey: row.issueKey, target }, "could not move the issue after its pull request merged");
173
+ markdown += ` Could not move ${row.issueKey} to "${target}"; it may need a manual move.`;
174
+ }
175
+ }
176
+ }
177
+ else {
178
+ markdown = `Pull request ${ref} was closed without merging.`;
179
+ }
180
+ await tracker.comment(row.issueKey, {
181
+ markdown,
182
+ ...(link.commentVisibilityRole ? { visibilityRole: link.commentVisibilityRole } : {}),
183
+ });
184
+ }
185
+ catch (err) {
186
+ log.warn({ err, issueKey: row.issueKey, ...where }, "could not bridge the closed pull request to the issue");
187
+ }
188
+ }
189
+ }
@@ -0,0 +1,70 @@
1
+ import type { PrismaClient } from "#prisma";
2
+ import { type CreateIssueInput, type IssueTracker, type TrackerCallOptions } from "../providers/issue-tracker/types.js";
3
+ export declare const FINGERPRINT_MAX_LENGTH = 200;
4
+ /**
5
+ * Each tracker call inside the lock: a whole-call cap, and a 429 fails at once rather than sleeping. Jira
6
+ * Cloud calls through the api.atlassian.com gateway routinely take several seconds each, and one tracker
7
+ * call can be a few requests (a create resolves the issue type and its fields first), so the cap is generous.
8
+ */
9
+ export declare const TRACKER_CALL_OPTIONS: Readonly<TrackerCallOptions>;
10
+ /** How long a sighting waits for another sighting of the same fingerprint before answering "busy". */
11
+ export declare const DEDUPE_LOCK_TIMEOUT_MS = 5000;
12
+ /**
13
+ * The critical section is the lock wait (5 s) plus at most two bounded tracker calls (getIssue, then
14
+ * createIssue or comment: 2 x 30 s) plus a few row reads/writes; 90 s leaves ample margin so a create is
15
+ * never in flight when the transaction (and with it the advisory lock) is torn down. The cost: each
16
+ * sighting in progress holds one pool connection for up to this long, and waiters hold one for up to
17
+ * the lock timeout (callers waiting for a free connection give up after maxWait).
18
+ */
19
+ export declare const DEDUPE_TRANSACTION_TIMEOUT_MS = 90000;
20
+ export type IssueDedupeDb = Pick<PrismaClient, "$transaction">;
21
+ export interface IssueDedupeLink {
22
+ provider: string;
23
+ projectKey: string;
24
+ access: string;
25
+ commentVisibilityRole?: string | null;
26
+ }
27
+ export interface FileIssueInput {
28
+ agentId: string;
29
+ runId?: string | null;
30
+ link: IssueDedupeLink;
31
+ tracker: IssueTracker;
32
+ fingerprint?: string | null;
33
+ /** Everything but the project, which comes from the link. */
34
+ create: Omit<CreateIssueInput, "projectKey">;
35
+ /** Appended after the description (and after any "Regression of" line), so it stays the last line. */
36
+ footerMarkdown?: string;
37
+ /** Posted on the existing issue when the fingerprint is seen again. */
38
+ seenAgainMarkdown: string;
39
+ /** false: only a seen-again update may happen; a create or regression returns issue_cap_reached. Default true. */
40
+ createAllowed?: boolean;
41
+ }
42
+ export type FileIssueOutcome = "created" | "seen_again" | "regression";
43
+ export type FileIssueSuccess = {
44
+ outcome: FileIssueOutcome;
45
+ issueKey: string;
46
+ url: string;
47
+ seenCount: number;
48
+ };
49
+ export type FileIssueResult = FileIssueSuccess | {
50
+ error: string;
51
+ message: string;
52
+ };
53
+ export declare const BUSY_RESULT: {
54
+ readonly error: "busy";
55
+ readonly message: "another sighting of this fingerprint is being filed; try again";
56
+ };
57
+ /**
58
+ * The part of a fingerprint that identifies it: letters and digits only, lower-cased (Unicode-normalised),
59
+ * so a model's small slips in case, punctuation or spacing ("checkout-api" vs "checkoutapi") still match.
60
+ */
61
+ export declare function normalizeFingerprint(fingerprint: string): string;
62
+ export declare function fingerprintHash(fingerprint: string): string;
63
+ /** A stable signed 64-bit advisory-lock key for one (provider, project, hash). */
64
+ export declare function dedupeLockKey(provider: string, projectKey: string, hash: string): bigint;
65
+ export declare function fileIssue(deps: {
66
+ db: IssueDedupeDb;
67
+ lockTimeoutMs?: number;
68
+ }, input: FileIssueInput): Promise<FileIssueResult>;
69
+ /** Postgres lock_not_available (55P03), as raised when lock_timeout expires, however the driver wraps it. */
70
+ export declare function isLockTimeout(err: unknown): boolean;
@@ -0,0 +1,255 @@
1
+ /**
2
+ * Fingerprint dedupe for issue creation, shared by jira_create_issue and
3
+ * self-defects. A fingerprint (opaque, 1-200 chars) is stored only as its
4
+ * sha256 hex in IssueFingerprint; the raw value never reaches the tracker.
5
+ *
6
+ * no fingerprint -> create
7
+ * no earlier issue (or it is gone,
8
+ * or it moved out of the project) -> create ("created")
9
+ * earlier issue still open -> comment on it ("seen_again")
10
+ * earlier issue Done -> create a new one, link it to the
11
+ * old one ("Relates"), old untouched
12
+ * ("regression")
13
+ *
14
+ * With createAllowed: false (a caller at its creation cap) only the
15
+ * seen-again update happens; where it would create (or file a regression)
16
+ * it returns { error: "issue_cap_reached" } and makes no tracker write.
17
+ *
18
+ * Concurrency: a Postgres advisory transaction lock on (provider, project,
19
+ * hash) serialises find -> decide -> tracker call -> row write, so concurrent
20
+ * calls with one fingerprint create at most one issue. Every tracker call in
21
+ * that critical section is bounded (TRACKER_CALL_OPTIONS) and the transaction
22
+ * timeout sits well above their sum, so the lock is never released (by a
23
+ * timed-out transaction) while a create is still in flight. The regression
24
+ * link is made after commit, outside the lock. Control-plane actions
25
+ * here (the regression link, the seen-again comment) are not gated by the
26
+ * link's allowlists but only ever act in a project the agent has a write link
27
+ * to. Never throws: failures come back as { error, message }.
28
+ */
29
+ import { createHash } from "node:crypto";
30
+ import { logger } from "./logger.js";
31
+ import { IssueTrackerError, } from "../providers/issue-tracker/types.js";
32
+ const log = logger.child({ module: "issue-dedupe" });
33
+ export const FINGERPRINT_MAX_LENGTH = 200;
34
+ /**
35
+ * Each tracker call inside the lock: a whole-call cap, and a 429 fails at once rather than sleeping. Jira
36
+ * Cloud calls through the api.atlassian.com gateway routinely take several seconds each, and one tracker
37
+ * call can be a few requests (a create resolves the issue type and its fields first), so the cap is generous.
38
+ */
39
+ export const TRACKER_CALL_OPTIONS = { timeoutMs: 30_000, retryOn429: false };
40
+ /** How long a sighting waits for another sighting of the same fingerprint before answering "busy". */
41
+ export const DEDUPE_LOCK_TIMEOUT_MS = 5_000;
42
+ /**
43
+ * The critical section is the lock wait (5 s) plus at most two bounded tracker calls (getIssue, then
44
+ * createIssue or comment: 2 x 30 s) plus a few row reads/writes; 90 s leaves ample margin so a create is
45
+ * never in flight when the transaction (and with it the advisory lock) is torn down. The cost: each
46
+ * sighting in progress holds one pool connection for up to this long, and waiters hold one for up to
47
+ * the lock timeout (callers waiting for a free connection give up after maxWait).
48
+ */
49
+ export const DEDUPE_TRANSACTION_TIMEOUT_MS = 90_000;
50
+ const DEDUPE_TRANSACTION_MAX_WAIT_MS = 10_000;
51
+ export const BUSY_RESULT = {
52
+ error: "busy",
53
+ message: "another sighting of this fingerprint is being filed; try again",
54
+ };
55
+ /** Thrown inside the transaction to abort a create the caller disallowed (nothing was written). */
56
+ class CreateNotAllowed extends Error {
57
+ }
58
+ /**
59
+ * The part of a fingerprint that identifies it: letters and digits only, lower-cased (Unicode-normalised),
60
+ * so a model's small slips in case, punctuation or spacing ("checkout-api" vs "checkoutapi") still match.
61
+ */
62
+ export function normalizeFingerprint(fingerprint) {
63
+ return fingerprint
64
+ .normalize("NFKC")
65
+ .toLowerCase()
66
+ .replace(/[^\p{L}\p{N}]+/gu, "");
67
+ }
68
+ export function fingerprintHash(fingerprint) {
69
+ return createHash("sha256").update(normalizeFingerprint(fingerprint), "utf8").digest("hex");
70
+ }
71
+ /** A stable signed 64-bit advisory-lock key for one (provider, project, hash). */
72
+ export function dedupeLockKey(provider, projectKey, hash) {
73
+ const digest = createHash("sha256").update(`wardby:issue-dedupe:${provider}:${projectKey}:${hash}`).digest();
74
+ return digest.readBigInt64BE(0);
75
+ }
76
+ const REGRESSION_LINK_TYPE = "relates";
77
+ export async function fileIssue(deps, input) {
78
+ const { link, tracker } = input;
79
+ // Set once the tracker side is done (issue created / comment posted), so a later row failure still reports it:
80
+ // answering with an error would make the caller retry and file a duplicate.
81
+ let completed;
82
+ let regressionOf;
83
+ // The tracker's own URL for the Done issue (never model input): the tracker renders it as a smart link.
84
+ let regressionOfUrl;
85
+ let hash;
86
+ try {
87
+ if (link.access !== "write") {
88
+ return { error: "permission_denied", message: `Creating issues in ${link.projectKey} needs a write link.` };
89
+ }
90
+ if (link.provider !== tracker.provider) {
91
+ return { error: "invalid_arguments", message: "The link and tracker providers differ." };
92
+ }
93
+ const withFooter = (markdown) => input.footerMarkdown ? `${markdown}\n\n${input.footerMarkdown}` : markdown;
94
+ const createInput = {
95
+ ...input.create,
96
+ projectKey: link.projectKey,
97
+ descriptionMarkdown: withFooter(input.create.descriptionMarkdown),
98
+ };
99
+ const fingerprint = input.fingerprint ?? undefined;
100
+ const createAllowed = input.createAllowed ?? true;
101
+ if (fingerprint === undefined) {
102
+ if (!createAllowed)
103
+ return capReached(link.projectKey);
104
+ const created = await tracker.createIssue(createInput);
105
+ return { outcome: "created", issueKey: created.key, url: created.url, seenCount: 1 };
106
+ }
107
+ if (fingerprint.length < 1 || fingerprint.length > FINGERPRINT_MAX_LENGTH) {
108
+ return {
109
+ error: "invalid_arguments",
110
+ message: `fingerprint must be 1-${FINGERPRINT_MAX_LENGTH} characters.`,
111
+ };
112
+ }
113
+ if (normalizeFingerprint(fingerprint) === "") {
114
+ return { error: "invalid_arguments", message: "fingerprint must contain letters or digits." };
115
+ }
116
+ const fpHash = fingerprintHash(fingerprint);
117
+ hash = fpHash;
118
+ const lockKey = dedupeLockKey(link.provider, link.projectKey, fpHash);
119
+ const lockTimeoutMs = Math.max(1, Math.trunc(deps.lockTimeoutMs ?? DEDUPE_LOCK_TIMEOUT_MS));
120
+ const result = await deps.db.$transaction(async (tx) => {
121
+ // Transaction-local: bounds the wait for the advisory lock below.
122
+ await tx.$executeRaw `SELECT set_config('lock_timeout', ${`${lockTimeoutMs}ms`}, true)`;
123
+ await tx.$executeRaw `SELECT pg_advisory_xact_lock(${lockKey})`;
124
+ const previous = await tx.issueFingerprint.findFirst({
125
+ where: { issueProvider: link.provider, projectKey: link.projectKey, fingerprintHash: fpHash },
126
+ orderBy: [{ firstSeenAt: "desc" }, { id: "desc" }],
127
+ });
128
+ if (previous) {
129
+ const view = await currentIssue(tracker, previous.issueKey);
130
+ // A moved issue (now in another project) is not reused. Known limitation: renaming the project's key
131
+ // makes every earlier issue look moved too (Jira answers with the new key while the link keeps the old
132
+ // one), so each sighting files a new issue until the link is re-pointed at the new key.
133
+ if (view && view.projectKey === link.projectKey) {
134
+ if (view.statusCategory !== "done") {
135
+ const seenCount = previous.seenCount + 1;
136
+ await tracker.comment(previous.issueKey, {
137
+ markdown: `${input.seenAgainMarkdown}\n\nSeen again (×${seenCount})`,
138
+ ...(link.commentVisibilityRole ? { visibilityRole: link.commentVisibilityRole } : {}),
139
+ }, TRACKER_CALL_OPTIONS);
140
+ completed = { outcome: "seen_again", issueKey: previous.issueKey, url: view.url, seenCount };
141
+ await tx.issueFingerprint.update({
142
+ where: { id: previous.id },
143
+ data: { seenCount, lastSeenAt: new Date() },
144
+ });
145
+ return completed;
146
+ }
147
+ regressionOf = previous.issueKey;
148
+ regressionOfUrl = view.url;
149
+ }
150
+ }
151
+ if (!createAllowed)
152
+ throw new CreateNotAllowed();
153
+ // Not idempotent: if the bounded POST is aborted after Jira already committed the issue, this transaction
154
+ // rolls back with no fingerprint row, and a retry files a second issue. Inherent to timing out a create
155
+ // (Jira has no idempotency key for it); the timeout keeps the advisory lock from being held indefinitely.
156
+ const created = await tracker.createIssue(regressionOf
157
+ ? {
158
+ ...createInput,
159
+ descriptionMarkdown: withFooter(`${input.create.descriptionMarkdown}\n\nRegression of ${regressionOfUrl?.startsWith("https://") ? regressionOfUrl : regressionOf}`),
160
+ }
161
+ : createInput, TRACKER_CALL_OPTIONS);
162
+ completed = {
163
+ outcome: regressionOf ? "regression" : "created",
164
+ issueKey: created.key,
165
+ url: created.url,
166
+ seenCount: 1,
167
+ };
168
+ await tx.issueFingerprint.create({
169
+ data: {
170
+ issueProvider: link.provider,
171
+ projectKey: link.projectKey,
172
+ fingerprintHash: fpHash,
173
+ issueKey: created.key,
174
+ agentId: input.agentId,
175
+ createdByRunId: input.runId ?? null,
176
+ },
177
+ });
178
+ return completed;
179
+ }, { timeout: DEDUPE_TRANSACTION_TIMEOUT_MS, maxWait: DEDUPE_TRANSACTION_MAX_WAIT_MS });
180
+ if (result.outcome === "regression" && regressionOf) {
181
+ await linkRegression(tracker, result.issueKey, regressionOf, input.agentId);
182
+ }
183
+ return result;
184
+ }
185
+ catch (err) {
186
+ if (completed) {
187
+ // The tracker side happened; only the bookkeeping did not. Report what was done (hash only, never the raw value).
188
+ log.warn({
189
+ err,
190
+ agentId: input.agentId,
191
+ issueKey: completed.issueKey,
192
+ fingerprintHash: hash,
193
+ outcome: completed.outcome,
194
+ }, "issue dedupe: tracker action done but its fingerprint row was not saved");
195
+ if (completed.outcome === "regression" && regressionOf) {
196
+ await linkRegression(tracker, completed.issueKey, regressionOf, input.agentId);
197
+ }
198
+ return completed;
199
+ }
200
+ if (err instanceof CreateNotAllowed)
201
+ return capReached(link.projectKey);
202
+ if (isLockTimeout(err))
203
+ return { ...BUSY_RESULT };
204
+ if (err instanceof IssueTrackerError) {
205
+ log.warn({ code: err.code, agentId: input.agentId, projectKey: link.projectKey }, "issue dedupe: tracker call failed");
206
+ return { error: err.code, message: err.message };
207
+ }
208
+ log.warn({ err, agentId: input.agentId, projectKey: link.projectKey }, "issue dedupe failed");
209
+ return { error: "internal_error", message: "Filing the issue failed." };
210
+ }
211
+ }
212
+ function capReached(projectKey) {
213
+ return {
214
+ error: "issue_cap_reached",
215
+ message: `This run has reached its limit of new issues in ${projectKey} (maxNewIssuesPerRun); nothing was created.`,
216
+ };
217
+ }
218
+ /** The issue as it is now, or null when it no longer exists. */
219
+ async function currentIssue(tracker, key) {
220
+ try {
221
+ return await tracker.getIssue(key, { maxComments: 0, agentMarker: "", ...TRACKER_CALL_OPTIONS });
222
+ }
223
+ catch (err) {
224
+ if (err instanceof IssueTrackerError && err.code === "tracker_not_found")
225
+ return null;
226
+ throw err;
227
+ }
228
+ }
229
+ /** Postgres lock_not_available (55P03), as raised when lock_timeout expires, however the driver wraps it. */
230
+ export function isLockTimeout(err) {
231
+ for (let e = err, depth = 0; e && typeof e === "object" && depth < 5; depth++) {
232
+ const o = e;
233
+ if (o.code === "55P03")
234
+ return true;
235
+ if (typeof o.message === "string" && /\b55P03\b|lock timeout/i.test(o.message))
236
+ return true;
237
+ const meta = o.meta;
238
+ if (meta?.code === "55P03" || meta?.driverAdapterError?.cause?.originalCode === "55P03")
239
+ return true;
240
+ e = o.cause;
241
+ }
242
+ return false;
243
+ }
244
+ /** New relates to old, after commit and outside the lock. Best effort: the description already names the old issue. */
245
+ async function linkRegression(tracker, newKey, oldKey, agentId) {
246
+ try {
247
+ const type = (await tracker.linkTypes(TRACKER_CALL_OPTIONS)).find((t) => t.name.toLowerCase() === REGRESSION_LINK_TYPE);
248
+ if (!type)
249
+ return;
250
+ await tracker.linkIssues({ type: type.name, inwardKey: oldKey, outwardKey: newKey }, TRACKER_CALL_OPTIONS);
251
+ }
252
+ catch (err) {
253
+ log.warn({ err, agentId, newKey, oldKey }, "regression link failed");
254
+ }
255
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Routes issue-tracker events (Jira webhooks) to the native agents linked to
3
+ * the issue's project. Adapters normalise payloads into IssueEvent
4
+ * (providers/issue-tracker/jira-events.ts); this module decides which agents
5
+ * run. The actor gate is accountType (in the adapter) plus, for mention and
6
+ * assignment, the link's trustedAccountIds — a live Jira permission check
7
+ * for another user needs Jira admin rights, which the bot must not hold.
8
+ * See docs/private/2026-09-30-jira-issue-tracker-design.md §5.
9
+ */
10
+ import type { PrismaClient } from "#prisma";
11
+ import type { Executor } from "../providers/executor/types.js";
12
+ import type { IssueEvent, IssueEventKind, IssueTrackerRegistry } from "../providers/issue-tracker/types.js";
13
+ import { type DispatchDb } from "./dispatch.js";
14
+ export type IssueEventDb = DispatchDb & Pick<PrismaClient, "agentIssueProject" | "runIssueStatus" | "issuePullRequest" | "workItem">;
15
+ export interface RouteIssueEventDeps {
16
+ db: IssueEventDb;
17
+ executor: Executor;
18
+ trackers: IssueTrackerRegistry;
19
+ }
20
+ export interface RouteResult {
21
+ runIds: string[];
22
+ followUps: Array<() => Promise<void>>;
23
+ }
24
+ export interface OpenIssuePr {
25
+ repository: string;
26
+ number: number;
27
+ url: string;
28
+ openedByRunId: string;
29
+ }
30
+ type TaskLink = {
31
+ triggerLabels: string[];
32
+ trustedAccountIds: string[];
33
+ };
34
+ /**
35
+ * The trusted task part carries only what the gate vouched for: key, kinds
36
+ * (with admin-configured labels), the actor's accountId, and, when the actor
37
+ * is in the link's trustedAccountIds, their display name. An unchecked actor's
38
+ * display name goes into the untrusted context with the summary/description.
39
+ */
40
+ export declare function issueTaskText(event: IssueEvent, matched: IssueEventKind[], issueUrl: string, link: TaskLink, openPrs?: OpenIssuePr[]): string;
41
+ export declare function routeIssueEvent(event: IssueEvent, deps: RouteIssueEventDeps): Promise<RouteResult>;
42
+ export {};