@wardby/cli 0.2.0 → 0.3.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 (491) hide show
  1. package/.env.example +34 -4
  2. package/README.md +183 -71
  3. package/dist/claude-coding-worker/driver.d.ts +7 -1
  4. package/dist/claude-coding-worker/driver.js +31 -2
  5. package/dist/claude-coding-worker/main.js +5 -2
  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 +13 -4
  10. package/dist/cli.d.ts +1 -1
  11. package/dist/cli.js +219 -29
  12. package/dist/coding/collect-exclude.d.ts +25 -0
  13. package/dist/coding/collect-exclude.js +76 -0
  14. package/dist/coding/profile.d.ts +67 -22
  15. package/dist/coding/profile.js +58 -25
  16. package/dist/coding/protected-path-wording.d.ts +24 -0
  17. package/dist/coding/protected-path-wording.js +55 -0
  18. package/dist/coding/protected-paths.d.ts +31 -0
  19. package/dist/coding/protected-paths.js +48 -0
  20. package/dist/coding/protocol.d.ts +99 -3
  21. package/dist/coding/protocol.js +155 -4
  22. package/dist/coding/registry/adapters.d.ts +2 -0
  23. package/dist/coding/registry/adapters.js +9 -0
  24. package/dist/coding/registry/allowlist.d.ts +9 -0
  25. package/dist/coding/registry/allowlist.js +28 -0
  26. package/dist/coding/registry/json-scan.d.ts +55 -0
  27. package/dist/coding/registry/json-scan.js +191 -0
  28. package/dist/coding/registry/lockfiles.d.ts +6 -0
  29. package/dist/coding/registry/lockfiles.js +89 -0
  30. package/dist/coding/registry/npm-lockfile.d.ts +9 -0
  31. package/dist/coding/registry/npm-lockfile.js +73 -0
  32. package/dist/coding/registry/npm-plan.d.ts +29 -0
  33. package/dist/coding/registry/npm-plan.js +289 -0
  34. package/dist/coding/registry/npm.d.ts +7 -0
  35. package/dist/coding/registry/npm.js +252 -0
  36. package/dist/coding/registry/pypi.d.ts +4 -0
  37. package/dist/coding/registry/pypi.js +210 -0
  38. package/dist/coding/registry/report.d.ts +37 -0
  39. package/dist/coding/registry/report.js +31 -0
  40. package/dist/coding/registry/token.d.ts +4 -0
  41. package/dist/coding/registry/token.js +7 -0
  42. package/dist/coding/registry/types.d.ts +286 -0
  43. package/dist/coding/registry/types.js +19 -0
  44. package/dist/coding/registry/worker-config.d.ts +14 -0
  45. package/dist/coding/registry/worker-config.js +31 -0
  46. package/dist/coding/services/builtins.d.ts +22 -0
  47. package/dist/coding/services/builtins.js +85 -0
  48. package/dist/coding/services/catalog.d.ts +443 -0
  49. package/dist/coding/services/catalog.js +159 -0
  50. package/dist/coding/services/declaration.d.ts +11 -0
  51. package/dist/coding/services/declaration.js +114 -0
  52. package/dist/coding/services/note.d.ts +8 -0
  53. package/dist/coding/services/note.js +15 -0
  54. package/dist/coding/services/resolve.d.ts +25 -0
  55. package/dist/coding/services/resolve.js +50 -0
  56. package/dist/coding/services/wording.d.ts +25 -0
  57. package/dist/coding/services/wording.js +70 -0
  58. package/dist/coding-proxy/main.js +24 -4
  59. package/dist/coding-worker/artifact.d.ts +18 -0
  60. package/dist/coding-worker/artifact.js +48 -6
  61. package/dist/coding-worker/debug-trace.d.ts +37 -0
  62. package/dist/coding-worker/debug-trace.js +117 -0
  63. package/dist/coding-worker/driver.d.ts +14 -1
  64. package/dist/coding-worker/driver.js +106 -14
  65. package/dist/coding-worker/errors.d.ts +7 -0
  66. package/dist/coding-worker/errors.js +26 -0
  67. package/dist/coding-worker/main.js +13 -4
  68. package/dist/coding-worker/sdk.d.ts +2 -2
  69. package/dist/coding-worker/sdk.js +7 -2
  70. package/dist/coding-worker/types.d.ts +3 -0
  71. package/dist/config/providers.d.ts +41 -0
  72. package/dist/config/providers.js +66 -0
  73. package/dist/core/budget-groups.d.ts +109 -6
  74. package/dist/core/budget-groups.js +125 -19
  75. package/dist/core/budget-wording.d.ts +22 -0
  76. package/dist/core/budget-wording.js +55 -0
  77. package/dist/core/coding-queue.d.ts +1 -1
  78. package/dist/core/datastores.d.ts +1 -1
  79. package/dist/core/datastores.js +18 -2
  80. package/dist/core/db.d.ts +40 -2
  81. package/dist/core/db.js +75 -2
  82. package/dist/core/dispatch.d.ts +49 -7
  83. package/dist/core/dispatch.js +290 -45
  84. package/dist/core/engine-native.js +27 -10
  85. package/dist/core/grants.d.ts +86 -0
  86. package/dist/core/grants.js +126 -0
  87. package/dist/core/host-events.d.ts +49 -0
  88. package/dist/core/host-events.js +257 -0
  89. package/dist/core/host-identity-links.d.ts +54 -0
  90. package/dist/core/host-identity-links.js +189 -0
  91. package/dist/core/host-status.d.ts +61 -0
  92. package/dist/core/host-status.js +211 -0
  93. package/dist/core/http-runtime.js +3 -4
  94. package/dist/core/in-flight-runs.d.ts +8 -0
  95. package/dist/core/in-flight-runs.js +56 -0
  96. package/dist/core/lease.d.ts +1 -1
  97. package/dist/core/private-directory.d.ts +20 -0
  98. package/dist/core/private-directory.js +36 -0
  99. package/dist/core/provider-wording.d.ts +12 -0
  100. package/dist/core/provider-wording.js +44 -0
  101. package/dist/core/reconciler.d.ts +39 -3
  102. package/dist/core/reconciler.js +86 -2
  103. package/dist/core/repo-access.d.ts +99 -0
  104. package/dist/core/repo-access.js +136 -0
  105. package/dist/core/review-host-checks.d.ts +11 -0
  106. package/dist/core/review-host-checks.js +41 -0
  107. package/dist/core/review-host-tools.d.ts +47 -0
  108. package/dist/core/review-host-tools.js +347 -0
  109. package/dist/core/run-heartbeat.d.ts +27 -0
  110. package/dist/core/run-heartbeat.js +54 -0
  111. package/dist/core/runner.d.ts +31 -9
  112. package/dist/core/runner.js +237 -40
  113. package/dist/core/scheduler.d.ts +1 -1
  114. package/dist/core/secrets.d.ts +12 -3
  115. package/dist/core/secrets.js +25 -6
  116. package/dist/core/subagent-memory-tools.d.ts +2 -2
  117. package/dist/core/subagent-memory-tools.js +16 -3
  118. package/dist/core/tool-admin.d.ts +81 -0
  119. package/dist/core/tool-admin.js +129 -0
  120. package/dist/core/tool-names.d.ts +42 -0
  121. package/dist/core/tool-names.js +64 -0
  122. package/dist/core/untrusted-content.d.ts +32 -0
  123. package/dist/core/untrusted-content.js +72 -0
  124. package/dist/core/webhooks.d.ts +9 -2
  125. package/dist/core/webhooks.js +23 -2
  126. package/dist/generated/prisma/browser.d.ts +328 -0
  127. package/dist/generated/prisma/browser.js +17 -0
  128. package/dist/generated/prisma/client.d.ts +347 -0
  129. package/dist/generated/prisma/client.js +34 -0
  130. package/dist/generated/prisma/commonInputTypes.d.ts +839 -0
  131. package/dist/generated/prisma/commonInputTypes.js +10 -0
  132. package/dist/generated/prisma/enums.d.ts +59 -0
  133. package/dist/generated/prisma/enums.js +59 -0
  134. package/dist/generated/prisma/internal/class.d.ts +605 -0
  135. package/dist/generated/prisma/internal/class.js +49 -0
  136. package/dist/generated/prisma/internal/prismaNamespace.d.ts +4494 -0
  137. package/dist/generated/prisma/internal/prismaNamespace.js +628 -0
  138. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +635 -0
  139. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +599 -0
  140. package/dist/generated/prisma/models/Agent.d.ts +3560 -0
  141. package/dist/generated/prisma/models/Agent.js +1 -0
  142. package/dist/generated/prisma/models/AgentDatastore.d.ts +1210 -0
  143. package/dist/generated/prisma/models/AgentDatastore.js +1 -0
  144. package/dist/generated/prisma/models/AgentMemory.d.ts +986 -0
  145. package/dist/generated/prisma/models/AgentMemory.js +1 -0
  146. package/dist/generated/prisma/models/AgentRepository.d.ts +1425 -0
  147. package/dist/generated/prisma/models/AgentRepository.js +1 -0
  148. package/dist/generated/prisma/models/AgentSecret.d.ts +1212 -0
  149. package/dist/generated/prisma/models/AgentSecret.js +1 -0
  150. package/dist/generated/prisma/models/AgentSubAgent.d.ts +1217 -0
  151. package/dist/generated/prisma/models/AgentSubAgent.js +1 -0
  152. package/dist/generated/prisma/models/AgentTool.d.ts +1403 -0
  153. package/dist/generated/prisma/models/AgentTool.js +1 -0
  154. package/dist/generated/prisma/models/AuthFormChallenge.d.ts +1298 -0
  155. package/dist/generated/prisma/models/AuthFormChallenge.js +1 -0
  156. package/dist/generated/prisma/models/AuthLoginKey.d.ts +1245 -0
  157. package/dist/generated/prisma/models/AuthLoginKey.js +1 -0
  158. package/dist/generated/prisma/models/AuthRateLimit.d.ts +979 -0
  159. package/dist/generated/prisma/models/AuthRateLimit.js +1 -0
  160. package/dist/generated/prisma/models/AuthSession.d.ts +1376 -0
  161. package/dist/generated/prisma/models/AuthSession.js +1 -0
  162. package/dist/generated/prisma/models/AuthUser.d.ts +1700 -0
  163. package/dist/generated/prisma/models/AuthUser.js +1 -0
  164. package/dist/generated/prisma/models/BudgetGroup.d.ts +1548 -0
  165. package/dist/generated/prisma/models/BudgetGroup.js +1 -0
  166. package/dist/generated/prisma/models/CodingAgentProfile.d.ts +1681 -0
  167. package/dist/generated/prisma/models/CodingAgentProfile.js +1 -0
  168. package/dist/generated/prisma/models/CodingProxyRequest.d.ts +1749 -0
  169. package/dist/generated/prisma/models/CodingProxyRequest.js +1 -0
  170. package/dist/generated/prisma/models/CodingProxySession.d.ts +1626 -0
  171. package/dist/generated/prisma/models/CodingProxySession.js +1 -0
  172. package/dist/generated/prisma/models/CodingRun.d.ts +3583 -0
  173. package/dist/generated/prisma/models/CodingRun.js +1 -0
  174. package/dist/generated/prisma/models/CodingService.d.ts +1348 -0
  175. package/dist/generated/prisma/models/CodingService.js +1 -0
  176. package/dist/generated/prisma/models/Datastore.d.ts +1434 -0
  177. package/dist/generated/prisma/models/Datastore.js +1 -0
  178. package/dist/generated/prisma/models/DatastoreEntry.d.ts +1314 -0
  179. package/dist/generated/prisma/models/DatastoreEntry.js +1 -0
  180. package/dist/generated/prisma/models/HostEventDelivery.d.ts +946 -0
  181. package/dist/generated/prisma/models/HostEventDelivery.js +1 -0
  182. package/dist/generated/prisma/models/HostIdentity.d.ts +1232 -0
  183. package/dist/generated/prisma/models/HostIdentity.js +1 -0
  184. package/dist/generated/prisma/models/HostIdentityLinkRequest.d.ts +1473 -0
  185. package/dist/generated/prisma/models/HostIdentityLinkRequest.js +1 -0
  186. package/dist/generated/prisma/models/OAuthAuthorizationCode.d.ts +1526 -0
  187. package/dist/generated/prisma/models/OAuthAuthorizationCode.js +1 -0
  188. package/dist/generated/prisma/models/OAuthAuthorizationRequest.d.ts +1350 -0
  189. package/dist/generated/prisma/models/OAuthAuthorizationRequest.js +1 -0
  190. package/dist/generated/prisma/models/OAuthClient.d.ts +1289 -0
  191. package/dist/generated/prisma/models/OAuthClient.js +1 -0
  192. package/dist/generated/prisma/models/OAuthFamily.d.ts +1540 -0
  193. package/dist/generated/prisma/models/OAuthFamily.js +1 -0
  194. package/dist/generated/prisma/models/OAuthGrant.d.ts +1280 -0
  195. package/dist/generated/prisma/models/OAuthGrant.js +1 -0
  196. package/dist/generated/prisma/models/Principal.d.ts +2245 -0
  197. package/dist/generated/prisma/models/Principal.js +1 -0
  198. package/dist/generated/prisma/models/RegistryAllowance.d.ts +1148 -0
  199. package/dist/generated/prisma/models/RegistryAllowance.js +1 -0
  200. package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +1219 -0
  201. package/dist/generated/prisma/models/RegistryApprovedVersion.js +1 -0
  202. package/dist/generated/prisma/models/RegistryFetch.d.ts +1428 -0
  203. package/dist/generated/prisma/models/RegistryFetch.js +1 -0
  204. package/dist/generated/prisma/models/RegistryPlanRefusal.d.ts +1294 -0
  205. package/dist/generated/prisma/models/RegistryPlanRefusal.js +1 -0
  206. package/dist/generated/prisma/models/RegistryVersionFact.d.ts +1085 -0
  207. package/dist/generated/prisma/models/RegistryVersionFact.js +1 -0
  208. package/dist/generated/prisma/models/ResourceGrant.d.ts +1437 -0
  209. package/dist/generated/prisma/models/ResourceGrant.js +1 -0
  210. package/dist/generated/prisma/models/Run.d.ts +2669 -0
  211. package/dist/generated/prisma/models/Run.js +1 -0
  212. package/dist/generated/prisma/models/RunHostCheck.d.ts +1239 -0
  213. package/dist/generated/prisma/models/RunHostCheck.js +1 -0
  214. package/dist/generated/prisma/models/RunHostStatus.d.ts +1315 -0
  215. package/dist/generated/prisma/models/RunHostStatus.js +1 -0
  216. package/dist/generated/prisma/models/SchedulerLease.d.ts +970 -0
  217. package/dist/generated/prisma/models/SchedulerLease.js +1 -0
  218. package/dist/generated/prisma/models/Secret.d.ts +1399 -0
  219. package/dist/generated/prisma/models/Secret.js +1 -0
  220. package/dist/generated/prisma/models/SecretElicitationOutcome.d.ts +973 -0
  221. package/dist/generated/prisma/models/SecretElicitationOutcome.js +1 -0
  222. package/dist/generated/prisma/models/Task.d.ts +1176 -0
  223. package/dist/generated/prisma/models/Task.js +1 -0
  224. package/dist/generated/prisma/models/Tool.d.ts +1490 -0
  225. package/dist/generated/prisma/models/Tool.js +1 -0
  226. package/dist/generated/prisma/models/Webhook.d.ts +1386 -0
  227. package/dist/generated/prisma/models/Webhook.js +1 -0
  228. package/dist/generated/prisma/models.d.ts +45 -0
  229. package/dist/generated/prisma/models.js +1 -0
  230. package/dist/help/build.d.ts +1 -0
  231. package/dist/help/build.js +9 -0
  232. package/dist/help/catalog.d.ts +24 -0
  233. package/dist/help/catalog.js +160 -0
  234. package/dist/help/cli.d.ts +2 -0
  235. package/dist/help/cli.js +65 -0
  236. package/dist/help/runtime.d.ts +3 -0
  237. package/dist/help/runtime.js +44 -0
  238. package/dist/help/search.d.ts +9 -0
  239. package/dist/help/search.js +104 -0
  240. package/dist/help-index.json +660 -0
  241. package/dist/import/cli-args.js +3 -2
  242. package/dist/import/create.d.ts +6 -1
  243. package/dist/import/create.js +68 -14
  244. package/dist/import/index.d.ts +1 -1
  245. package/dist/import/index.js +19 -9
  246. package/dist/import/neutral-schema.d.ts +11 -11
  247. package/dist/mcp/auth/access.d.ts +70 -0
  248. package/dist/mcp/auth/access.js +58 -0
  249. package/dist/mcp/auth/grants-cli.d.ts +149 -0
  250. package/dist/mcp/auth/grants-cli.js +518 -0
  251. package/dist/mcp/auth/host-account-cli.d.ts +2 -0
  252. package/dist/mcp/auth/host-account-cli.js +47 -0
  253. package/dist/mcp/auth/ownership.d.ts +37 -63
  254. package/dist/mcp/auth/ownership.js +19 -14
  255. package/dist/mcp/auth/principal.d.ts +1 -1
  256. package/dist/mcp/auth/repo-authorization.d.ts +22 -0
  257. package/dist/mcp/auth/repo-authorization.js +51 -0
  258. package/dist/mcp/auth/resource-server.d.ts +32 -3
  259. package/dist/mcp/auth/resource-server.js +84 -3
  260. package/dist/mcp/auth/self-hosted/browser.js +22 -8
  261. package/dist/mcp/auth/self-hosted/cli.d.ts +1 -1
  262. package/dist/mcp/auth/self-hosted/cli.js +47 -13
  263. package/dist/mcp/auth/self-hosted/credentials.d.ts +21 -3
  264. package/dist/mcp/auth/self-hosted/credentials.js +62 -4
  265. package/dist/mcp/auth/self-hosted/rate-limit.d.ts +1 -1
  266. package/dist/mcp/auth/self-hosted/session.d.ts +3 -2
  267. package/dist/mcp/context.d.ts +28 -2
  268. package/dist/mcp/errors.d.ts +25 -6
  269. package/dist/mcp/errors.js +98 -0
  270. package/dist/mcp/host-events/github-ingress.d.ts +36 -0
  271. package/dist/mcp/host-events/github-ingress.js +92 -0
  272. package/dist/mcp/host-events/github-user-callback.d.ts +18 -0
  273. package/dist/mcp/host-events/github-user-callback.js +50 -0
  274. package/dist/mcp/index.d.ts +8 -1
  275. package/dist/mcp/index.js +106 -16
  276. package/dist/mcp/server.d.ts +1 -1
  277. package/dist/mcp/server.js +27 -9
  278. package/dist/mcp/shared/page-style.d.ts +16 -0
  279. package/dist/mcp/shared/page-style.js +129 -0
  280. package/dist/mcp/tasks/manager.d.ts +1 -1
  281. package/dist/mcp/tools/agents.js +447 -49
  282. package/dist/mcp/tools/budget-groups.js +3 -3
  283. package/dist/mcp/tools/datastore.js +23 -16
  284. package/dist/mcp/tools/grants.d.ts +2 -0
  285. package/dist/mcp/tools/grants.js +239 -0
  286. package/dist/mcp/tools/help.d.ts +5 -0
  287. package/dist/mcp/tools/help.js +67 -0
  288. package/dist/mcp/tools/host-accounts.d.ts +2 -0
  289. package/dist/mcp/tools/host-accounts.js +114 -0
  290. package/dist/mcp/tools/memory.d.ts +7 -1
  291. package/dist/mcp/tools/memory.js +5 -5
  292. package/dist/mcp/tools/repositories.d.ts +2 -0
  293. package/dist/mcp/tools/repositories.js +181 -0
  294. package/dist/mcp/tools/runs.d.ts +6 -0
  295. package/dist/mcp/tools/runs.js +60 -6
  296. package/dist/mcp/tools/scheduling.js +4 -7
  297. package/dist/mcp/tools/secret-elicitation-form.d.ts +1 -1
  298. package/dist/mcp/tools/secret-elicitation-form.js +10 -28
  299. package/dist/mcp/tools/secret-elicitation.d.ts +1 -1
  300. package/dist/mcp/tools/secrets.js +18 -6
  301. package/dist/mcp/tools/services.d.ts +2 -0
  302. package/dist/mcp/tools/services.js +222 -0
  303. package/dist/mcp/tools/subagents.js +53 -17
  304. package/dist/mcp/tools/tools.d.ts +41 -0
  305. package/dist/mcp/tools/tools.js +202 -41
  306. package/dist/mcp/tools/trigger.js +36 -6
  307. package/dist/mcp/tools/webhooks.js +15 -4
  308. package/dist/mcp/transport/streamable-http.d.ts +11 -1
  309. package/dist/mcp/transport/streamable-http.js +32 -2
  310. package/dist/mcp/unattended-schedules.d.ts +1 -1
  311. package/dist/mcp/webhooks/ingress.d.ts +1 -1
  312. package/dist/providers/auth/delegating.d.ts +19 -0
  313. package/dist/providers/auth/delegating.js +71 -0
  314. package/dist/providers/auth/index.d.ts +3 -1
  315. package/dist/providers/auth/index.js +11 -0
  316. package/dist/providers/auth/self-hosted.d.ts +4 -3
  317. package/dist/providers/auth/self-hosted.js +16 -4
  318. package/dist/providers/auth/types.d.ts +6 -0
  319. package/dist/providers/coding-proxy/memory-ledger.d.ts +3 -0
  320. package/dist/providers/coding-proxy/memory-ledger.js +21 -2
  321. package/dist/providers/coding-proxy/metering.d.ts +4 -0
  322. package/dist/providers/coding-proxy/metering.js +28 -2
  323. package/dist/providers/coding-proxy/prisma-ledger.d.ts +18 -1
  324. package/dist/providers/coding-proxy/prisma-ledger.js +50 -5
  325. package/dist/providers/coding-proxy/proxy.d.ts +13 -0
  326. package/dist/providers/coding-proxy/proxy.js +479 -18
  327. package/dist/providers/coding-proxy/registry/audit.d.ts +131 -0
  328. package/dist/providers/coding-proxy/registry/audit.js +380 -0
  329. package/dist/providers/coding-proxy/registry/bounded-fetch.d.ts +16 -0
  330. package/dist/providers/coding-proxy/registry/bounded-fetch.js +77 -0
  331. package/dist/providers/coding-proxy/registry/plan.d.ts +58 -0
  332. package/dist/providers/coding-proxy/registry/plan.js +304 -0
  333. package/dist/providers/coding-proxy/registry/prisma-store.d.ts +34 -0
  334. package/dist/providers/coding-proxy/registry/prisma-store.js +151 -0
  335. package/dist/providers/coding-proxy/registry/service.d.ts +213 -0
  336. package/dist/providers/coding-proxy/registry/service.js +1137 -0
  337. package/dist/providers/coding-proxy/registry/store.d.ts +127 -0
  338. package/dist/providers/coding-proxy/registry/store.js +70 -0
  339. package/dist/providers/coding-proxy/runtime.d.ts +18 -1
  340. package/dist/providers/coding-proxy/runtime.js +63 -0
  341. package/dist/providers/coding-proxy/secure-fetch.js +0 -1
  342. package/dist/providers/coding-proxy/server.d.ts +11 -0
  343. package/dist/providers/coding-proxy/server.js +163 -0
  344. package/dist/providers/coding-proxy/types.d.ts +15 -0
  345. package/dist/providers/datastore/postgres.d.ts +1 -1
  346. package/dist/providers/engine/types.d.ts +10 -0
  347. package/dist/providers/executor/build.d.ts +3 -3
  348. package/dist/providers/executor/composition.d.ts +4 -1
  349. package/dist/providers/executor/composition.js +19 -0
  350. package/dist/providers/executor/container.d.ts +106 -4
  351. package/dist/providers/executor/container.js +305 -22
  352. package/dist/providers/executor/dbos.d.ts +3 -4
  353. package/dist/providers/executor/in-process.d.ts +2 -3
  354. package/dist/providers/executor/routing.d.ts +7 -0
  355. package/dist/providers/executor/routing.js +13 -0
  356. package/dist/providers/executor/types.d.ts +22 -0
  357. package/dist/providers/jobs/claude-tool-setup.d.ts +23 -0
  358. package/dist/providers/jobs/claude-tool-setup.js +50 -0
  359. package/dist/providers/jobs/collect-prune.d.ts +7 -0
  360. package/dist/providers/jobs/collect-prune.js +27 -0
  361. package/dist/providers/jobs/docker-isolation.d.ts +48 -3
  362. package/dist/providers/jobs/docker-isolation.js +213 -22
  363. package/dist/providers/jobs/docker-services.d.ts +35 -0
  364. package/dist/providers/jobs/docker-services.js +191 -0
  365. package/dist/providers/jobs/docker.d.ts +60 -2
  366. package/dist/providers/jobs/docker.js +313 -50
  367. package/dist/providers/jobs/fake-kubernetes-api.d.ts +1 -0
  368. package/dist/providers/jobs/fake-kubernetes-api.js +12 -3
  369. package/dist/providers/jobs/kubernetes-isolation.d.ts +17 -2
  370. package/dist/providers/jobs/kubernetes-isolation.js +238 -57
  371. package/dist/providers/jobs/kubernetes-platform.d.ts +10 -4
  372. package/dist/providers/jobs/kubernetes-platform.js +11 -5
  373. package/dist/providers/jobs/kubernetes-preflight.js +3 -0
  374. package/dist/providers/jobs/kubernetes.d.ts +21 -2
  375. package/dist/providers/jobs/kubernetes.js +117 -31
  376. package/dist/providers/jobs/types.d.ts +20 -0
  377. package/dist/providers/llm/anthropic.js +6 -2
  378. package/dist/providers/llm/bedrock.js +2 -1
  379. package/dist/providers/llm/claude-messages.d.ts +5 -1
  380. package/dist/providers/llm/claude-messages.js +1 -0
  381. package/dist/providers/llm/claude-provider.d.ts +3 -1
  382. package/dist/providers/llm/claude-provider.js +5 -1
  383. package/dist/providers/llm/index.d.ts +1 -1
  384. package/dist/providers/llm/index.js +1 -1
  385. package/dist/providers/llm/pricing-anthropic.d.ts +10 -0
  386. package/dist/providers/llm/pricing-anthropic.js +32 -14
  387. package/dist/providers/llm/pricing-bedrock-claude.d.ts +8 -0
  388. package/dist/providers/llm/pricing-bedrock-claude.js +9 -0
  389. package/dist/providers/llm/routing.d.ts +10 -1
  390. package/dist/providers/llm/routing.js +18 -0
  391. package/dist/providers/llm/types.d.ts +12 -0
  392. package/dist/providers/llm/types.js +8 -1
  393. package/dist/providers/memory/postgres.d.ts +1 -1
  394. package/dist/providers/review-host/diff-lines.d.ts +16 -0
  395. package/dist/providers/review-host/diff-lines.js +59 -0
  396. package/dist/providers/review-host/github-events.d.ts +6 -0
  397. package/dist/providers/review-host/github-events.js +180 -0
  398. package/dist/providers/review-host/github-user-auth.d.ts +38 -0
  399. package/dist/providers/review-host/github-user-auth.js +128 -0
  400. package/dist/providers/review-host/github.d.ts +57 -0
  401. package/dist/providers/review-host/github.js +567 -0
  402. package/dist/providers/review-host/index.d.ts +12 -0
  403. package/dist/providers/review-host/index.js +30 -0
  404. package/dist/providers/review-host/review-format.d.ts +19 -0
  405. package/dist/providers/review-host/review-format.js +59 -0
  406. package/dist/providers/review-host/types.d.ts +284 -0
  407. package/dist/providers/review-host/types.js +24 -0
  408. package/dist/providers/vcs/git.d.ts +16 -6
  409. package/dist/providers/vcs/git.js +79 -14
  410. package/dist/providers/vcs/github.d.ts +74 -3
  411. package/dist/providers/vcs/github.js +195 -15
  412. package/dist/providers/vcs/types.d.ts +42 -4
  413. package/dist/quickstart/index.js +34 -32
  414. package/dist/quickstart/migrate.d.ts +23 -0
  415. package/dist/quickstart/migrate.js +125 -0
  416. package/dist/sandbox/fetch-policy.d.ts +20 -2
  417. package/dist/sandbox/fetch-policy.js +64 -3
  418. package/dist/sandbox/host-functions.d.ts +9 -1
  419. package/dist/sandbox/host-functions.js +12 -5
  420. package/dist/serve.js +1 -1
  421. package/dist/wardby-bin.js +6 -0
  422. package/docs/README.md +30 -0
  423. package/docs/architecture-runtime.md +90 -0
  424. package/docs/assets/brand/wardby-icon-512.png +0 -0
  425. package/docs/assets/brand/wardby-icon.svg +16 -0
  426. package/docs/assets/brand/wardby-mascot-profile-512.png +0 -0
  427. package/docs/assets/brand/wardby-mascot.png +0 -0
  428. package/docs/assets/brand/wardby-mascot.svg +5 -0
  429. package/docs/assets/wardby-workflow.svg +106 -0
  430. package/docs/code-review-agents.md +481 -0
  431. package/docs/coding-agent-setup.md +172 -0
  432. package/docs/coding-packages.md +455 -0
  433. package/docs/coding-services.md +300 -0
  434. package/docs/coding-worker-byo-images.md +98 -0
  435. package/docs/coding-worker-isolation.md +1061 -0
  436. package/docs/getting-started-gke.md +615 -0
  437. package/docs/getting-started-identity-provider.md +308 -0
  438. package/docs/getting-started.md +133 -0
  439. package/docs/observability.md +53 -0
  440. package/docs/release-verification.md +66 -0
  441. package/docs/security-deployment.md +657 -0
  442. package/help/code-review-agents.md +31 -0
  443. package/help/coding-packages.md +31 -0
  444. package/help/coding-services.md +71 -0
  445. package/help/creating-agents.md +68 -0
  446. package/help/deploy-gke.md +39 -0
  447. package/help/deployment-targets.md +39 -0
  448. package/help/errors/budget-group-exhausted.md +25 -0
  449. package/help/errors/docker-isolation-unsupported.md +26 -0
  450. package/help/errors/protected-path.md +45 -0
  451. package/help/errors/repo-access.md +27 -0
  452. package/help/errors/service-declaration-invalid.md +27 -0
  453. package/help/errors/service-declaration-unavailable.md +25 -0
  454. package/help/errors/service-launcher-unsupported.md +30 -0
  455. package/help/errors/service-not-allowed.md +27 -0
  456. package/help/errors/service-unknown.md +23 -0
  457. package/help/errors/service-unready.md +49 -0
  458. package/help/getting-started.md +31 -0
  459. package/help/github.md +30 -0
  460. package/help/identity-and-access.md +39 -0
  461. package/help/mcp.md +30 -0
  462. package/help/native-capabilities.md +30 -0
  463. package/help/observability.md +41 -0
  464. package/help/operating-agents.md +30 -0
  465. package/help/security.md +27 -0
  466. package/help/troubleshooting/budgets.md +26 -0
  467. package/help/troubleshooting/coding-workers.md +47 -0
  468. package/help/troubleshooting/repository-access.md +27 -0
  469. package/package.json +30 -11
  470. package/prisma/migrate.config.mjs +20 -0
  471. package/prisma/migrations/20260925010000_coding_collect_exclude/migration.sql +8 -0
  472. package/prisma/migrations/20260925015000_allowed_egress_default/migration.sql +8 -0
  473. package/prisma/migrations/20260925020000_coding_package_registry/migration.sql +55 -0
  474. package/prisma/migrations/20260925030000_tool_name_per_owner/migration.sql +11 -0
  475. package/prisma/migrations/20260926010000_run_trigger_host_event/migration.sql +7 -0
  476. package/prisma/migrations/20260926020000_code_review_hosts/migration.sql +53 -0
  477. package/prisma/migrations/20260926030000_auth_user_roles/migration.sql +9 -0
  478. package/prisma/migrations/20260926040000_agent_effort/migration.sql +6 -0
  479. package/prisma/migrations/20260926050000_registry_lockfile_plan/migration.sql +32 -0
  480. package/prisma/migrations/20260926050000_repo_access_authorization/migration.sql +82 -0
  481. package/prisma/migrations/20260926060000_registry_plan_refusal/migration.sql +19 -0
  482. package/prisma/migrations/20260926100000_registry_plan_refusal_published_at/migration.sql +5 -0
  483. package/prisma/migrations/20260926190000_run_host_status/migration.sql +18 -0
  484. package/prisma/migrations/20260926210000_run_host_status_at_dispatch/migration.sql +5 -0
  485. package/prisma/migrations/20260927010000_resource_grants/migration.sql +72 -0
  486. package/prisma/migrations/20260927020000_proxy_session_budget_exhausted/migration.sql +3 -0
  487. package/prisma/migrations/20260927030000_coding_debug_trace/migration.sql +4 -0
  488. package/prisma/migrations/20260927040000_proxy_session_upstream_failure/migration.sql +3 -0
  489. package/prisma/migrations/20260927050000_coding_run_services/migration.sql +74 -0
  490. package/prisma/migrations/20260928000000_coding_run_tool_image/migration.sql +5 -0
  491. package/prisma/schema.prisma +417 -23
@@ -9,15 +9,51 @@
9
9
  * (attach/detach on an agent or tool you don't own) DO throw, same
10
10
  * convention as every other tool in this codebase.
11
11
  */
12
- import { Prisma } from "@prisma/client";
12
+ import { Prisma } from "#prisma";
13
13
  import { deriveJsonSchema, validateParams } from "../../sandbox/zod-params.js";
14
14
  import { runInSandbox } from "../../sandbox/run-in-sandbox.js";
15
15
  import { ToolCapabilitiesPatchSchema } from "../../sandbox/tool-capabilities.js";
16
16
  import { buildSharedDatastoreAccessor } from "../../core/datastores.js";
17
- import { MEMORY_TOOL_NAMES } from "../../core/memory-tools.js";
17
+ import { findSameNamedAttachedTool, reservedToolNameReason } from "../../core/tool-names.js";
18
+ import { ToolAttachedError, deleteToolGuarded, hasToolChanges, prepareToolUpdate, updateToolGuarded, } from "../../core/tool-admin.js";
18
19
  import { McpError } from "../errors.js";
19
- import { assertCanMutate, requireOwnedAgent, requireOwnedTool, requireReadableAgent, visibleToPrincipal, canRead, } from "../auth/ownership.js";
20
+ import { requireOwnedTool, requireStrictlyOwnedTool, assertStrictlyOwnedTool, visibleToPrincipal, canRead, } from "../auth/ownership.js";
21
+ import { agentAccessResolver, assertAgentAccess, requireAgentAccess } from "../auth/access.js";
22
+ import { atLeast } from "../../core/grants.js";
20
23
  import { textResult } from "./text-result.js";
24
+ /**
25
+ * Names the agents the caller can read (its own and ones granted to it)
26
+ * and only counts the rest, so a refusal never reveals another principal's
27
+ * agent names or ids.
28
+ */
29
+ async function describeAgents(agents, ctx) {
30
+ const accessOf = await agentAccessResolver(ctx);
31
+ const readable = agents.filter((agent) => atLeast("agent", accessOf(agent), "read"));
32
+ const hidden = agents.length - readable.length;
33
+ const parts = readable.map((agent) => `"${agent.name}" (${agent.id})`);
34
+ if (hidden > 0)
35
+ parts.push(`${hidden} agent(s) owned by other principals`);
36
+ return parts.join(", ");
37
+ }
38
+ /**
39
+ * A tool row as a caller who doesn't own it may see it: name and
40
+ * description, never code or paramsZod (A7). `public` tells an owner-less
41
+ * tool apart from the caller's own same-named one (names are unique per
42
+ * owner, not globally). Shared by list_tools and get_agent.
43
+ */
44
+ export function projectTool(tool, principalId) {
45
+ if (tool.ownerId !== null && tool.ownerId === principalId)
46
+ return tool;
47
+ return tool.ownerId === null
48
+ ? { id: tool.id, name: tool.name, description: tool.description, public: true }
49
+ : { id: tool.id, name: tool.name, description: tool.description, public: false, ownerIsCaller: false };
50
+ }
51
+ /** Refuses a name one of the runner's built-ins would shadow (see core/tool-names.ts). */
52
+ function assertToolNameAllowed(name) {
53
+ const reason = reservedToolNameReason(name);
54
+ if (reason)
55
+ throw new McpError(400, reason);
56
+ }
21
57
  export function registerToolAuthoringTools(mcp) {
22
58
  mcp.registerTool({
23
59
  name: "create_tool",
@@ -33,24 +69,108 @@ export function registerToolAuthoringTools(mcp) {
33
69
  required: ["name", "description", "paramsZod", "code"],
34
70
  },
35
71
  handler: async (args, ctx) => {
36
- if (MEMORY_TOOL_NAMES.has(args.name)) {
37
- throw new McpError(400, `Tool name "${args.name}" is reserved for the built-in agent-memory tools.`);
38
- }
72
+ assertToolNameAllowed(args.name);
39
73
  const schemaResult = await deriveJsonSchema(args.paramsZod);
40
74
  if (!schemaResult.ok) {
41
75
  return textResult({ ok: false, errorKind: schemaResult.errorKind, errorMessage: schemaResult.errorMessage });
42
76
  }
43
- const tool = await ctx.db.tool.create({
44
- data: {
45
- name: args.name,
46
- description: args.description,
47
- paramsZod: args.paramsZod,
48
- code: args.code,
49
- jsonSchema: schemaResult.value,
50
- ownerId: ctx.principal.id,
51
- },
52
- });
53
- return textResult(tool);
77
+ try {
78
+ const tool = await ctx.db.tool.create({
79
+ data: {
80
+ name: args.name,
81
+ description: args.description,
82
+ paramsZod: args.paramsZod,
83
+ code: args.code,
84
+ jsonSchema: schemaResult.value,
85
+ ownerId: ctx.principal.id,
86
+ },
87
+ });
88
+ return textResult(tool);
89
+ }
90
+ catch (err) {
91
+ if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === "P2002") {
92
+ throw new McpError(409, `A tool named "${args.name}" already exists for your principal.`);
93
+ }
94
+ throw err;
95
+ }
96
+ },
97
+ });
98
+ mcp.registerTool({
99
+ name: "update_tool",
100
+ scope: "tools:write",
101
+ description: "Replaces a tool's description, paramsZod and/or code in place (its name can't change: agents' prompts call it by name). Owner-only: public tools can't be changed over MCP. Refused while the tool is attached to any agent you don't own, including owner-less ones and agents shared with you, since the change would reach another owner's agent; detach it there first or create a new tool. Runs already started keep the version they loaded for their whole lifetime; the next run picks up the new one.",
102
+ inputSchema: {
103
+ type: "object",
104
+ additionalProperties: false,
105
+ properties: {
106
+ toolId: { type: "string" },
107
+ description: { type: "string" },
108
+ paramsZod: { type: "string" },
109
+ code: { type: "string" },
110
+ },
111
+ required: ["toolId"],
112
+ },
113
+ handler: async (args, ctx) => {
114
+ const changes = { description: args.description, paramsZod: args.paramsZod, code: args.code };
115
+ if (!hasToolChanges(changes)) {
116
+ throw new McpError(400, "update_tool needs at least one of description, paramsZod or code.");
117
+ }
118
+ // Checked once up front so a non-owner never gets as far as a sandbox
119
+ // compile, and again (with the attachments) inside the transaction.
120
+ await requireStrictlyOwnedTool(ctx.db, args.toolId, ctx.principal.id);
121
+ const prepared = await prepareToolUpdate(changes);
122
+ if (!prepared.ok) {
123
+ return textResult({ ok: false, errorKind: prepared.errorKind, errorMessage: prepared.errorMessage });
124
+ }
125
+ // Any attachment to an agent the caller doesn't own (an owner-less one
126
+ // included) blocks every field; see core/tool-admin.ts for why, and
127
+ // for the Serializable transaction that keeps a concurrent attach from
128
+ // landing between the check and the write.
129
+ try {
130
+ const tool = await updateToolGuarded(ctx.db, args.toolId, prepared.data, (row) => assertStrictlyOwnedTool(row, args.toolId, ctx.principal.id));
131
+ return textResult(tool);
132
+ }
133
+ catch (err) {
134
+ if (err instanceof ToolAttachedError) {
135
+ throw new McpError(409, `Tool "${args.toolId}" is attached to agents you don't own: ${await describeAgents(err.agents, ctx)}. Detach it from those agents first (or ask their owners to), or create a new tool instead.`);
136
+ }
137
+ throw err;
138
+ }
139
+ },
140
+ });
141
+ mcp.registerTool({
142
+ name: "delete_tool",
143
+ scope: "tools:write",
144
+ description: "Deletes a tool you own. Refused while it is attached to any agent; pass detach: true to detach it from your own agents first. It is never detached from an agent you don't own -- use detach_tool where you hold write on the agent, or ask its owner. Past runs are unaffected, and a run already under way keeps the version it loaded.",
145
+ inputSchema: {
146
+ type: "object",
147
+ additionalProperties: false,
148
+ properties: { toolId: { type: "string" }, detach: { type: "boolean" } },
149
+ required: ["toolId"],
150
+ },
151
+ handler: async (args, ctx) => {
152
+ try {
153
+ const detachedFrom = await deleteToolGuarded(ctx.db, args.toolId, {
154
+ detach: args.detach === true,
155
+ authorize: (row) => assertStrictlyOwnedTool(row, args.toolId, ctx.principal.id),
156
+ });
157
+ return textResult({ deleted: args.toolId, detachedFrom });
158
+ }
159
+ catch (err) {
160
+ if (!(err instanceof ToolAttachedError))
161
+ throw err;
162
+ const agents = await describeAgents(err.agents, ctx);
163
+ switch (err.reason) {
164
+ // Public agents are included: pulling a tool from a shared agent
165
+ // should be an explicit detach_tool.
166
+ case "other_owners":
167
+ throw new McpError(409, `Tool "${args.toolId}" is still attached to ${agents}. Only your own agents can be detached here; use detach_tool where you hold write on the agent, or ask the other owners to.`);
168
+ case "needs_detach":
169
+ throw new McpError(409, `Tool "${args.toolId}" is still attached to ${agents}. Pass detach: true to detach it from these agents and delete it.`);
170
+ case "race":
171
+ throw new McpError(409, `Tool "${args.toolId}" is still attached to an agent; nothing was deleted.`);
172
+ }
173
+ }
54
174
  },
55
175
  });
56
176
  mcp.registerTool({
@@ -112,6 +232,7 @@ export function registerToolAuthoringTools(mcp) {
112
232
  mcp.registerTool({
113
233
  name: "attach_tool",
114
234
  scope: "tools:write",
235
+ description: "Attaches a tool to an agent (needs write on the agent). The four capability fields (allowedSecrets, allowedDatastorePrefixes, allowedHosts, allowedSharedDatastorePrefixes) are the agent owner's to grant: anyone else passing one gets 403, and their attachment runs with none until the owner re-runs attach_tool with the capabilities. When the owner grants an attachment it had not granted before (someone else attached it, or the agent changed owner), every capability not passed is reset to empty, so state each one you want. Granting capabilities to a tool someone else owns vouches for code you can't read (only its owner sees it); the tool's owner can't change it while it's attached to your agent, and detaching drops your grant.",
115
236
  inputSchema: {
116
237
  type: "object",
117
238
  properties: {
@@ -141,35 +262,72 @@ export function registerToolAuthoringTools(mcp) {
141
262
  if (!patch.success) {
142
263
  throw new McpError(400, `Invalid tool capabilities: ${patch.error.issues.map((i) => i.message).join("; ")}`);
143
264
  }
265
+ const capabilitiesPassed = Object.values(patch.data).some((value) => value !== undefined);
144
266
  await ctx.db.$transaction(async (tx) => {
145
- const agent = await tx.agent.findUnique({ where: { id: args.agentId } });
146
- if (!agent)
147
- throw new McpError(404, `Agent "${args.agentId}" not found.`);
148
- assertCanMutate(agent.ownerId, ctx.principal.id, `Agent "${args.agentId}" is not owned by the caller.`);
267
+ const { agent } = await assertAgentAccess(ctx, await tx.agent.findUnique({ where: { id: args.agentId } }), args.agentId, "write", tx);
149
268
  if (agent.kind === "coding") {
150
269
  throw new McpError(400, "Native sandbox tools cannot be attached to coding agents.");
151
270
  }
152
- await requireOwnedTool(tx, args.toolId, ctx.principal.id);
271
+ // Capabilities carry the owner's consent (resource-sharing grants
272
+ // spec §3.4.2). Strict ownership: neither a write-grantee nor the
273
+ // stdio operator may hand the owner's secrets, datastores or
274
+ // network to code on the agent.
275
+ const isOwner = agent.ownerId !== null && agent.ownerId === ctx.principal.id;
276
+ if (capabilitiesPassed && !isOwner) {
277
+ throw new McpError(403, "Only the agent's owner can grant capabilities to an attachment; ask them to re-run attach_tool with the capabilities.");
278
+ }
279
+ const existing = await tx.agentTool.findUnique({
280
+ where: { agentId_toolId: { agentId: args.agentId, toolId: args.toolId } },
281
+ });
282
+ // Creating an attachment needs a tool the caller may attach (Phase
283
+ // 1: its own or an owner-less one). Re-attaching an existing one
284
+ // only changes its capabilities: that is how the agent's owner
285
+ // grants them to a write-grantee's tool, or re-grants them after a
286
+ // make_owner transfer, so the tool check does not apply.
287
+ const tool = existing
288
+ ? await tx.tool.findUniqueOrThrow({ where: { id: args.toolId } })
289
+ : await requireOwnedTool(tx, args.toolId, ctx.principal.id);
290
+ // The runtime dispatches by name, and names are only unique per
291
+ // owner: a second same-named tool on one agent would be a
292
+ // duplicate tool name to the model and silently shadow one of them.
293
+ const clash = await findSameNamedAttachedTool(tx, args.agentId, tool);
294
+ if (clash) {
295
+ // The clashing tool may be another principal's private one (a
296
+ // write-grantee's, or one left from before a transfer): name its
297
+ // id only when the caller could read it anyway.
298
+ const which = canRead(clash.ownerId, ctx.principal.id)
299
+ ? ` (${clash.id})`
300
+ : " (a tool owned by another principal)";
301
+ throw new McpError(409, `Agent "${args.agentId}" already has a different tool named "${tool.name}" attached${which}; detach it first.`);
302
+ }
303
+ // Re-granting an attachment this owner never vouched for: what is
304
+ // not restated is reset, never silently adopted.
305
+ const keepUnstated = existing?.capabilitiesGrantedById === agent.ownerId;
306
+ const field = (key, empty) => patch.data[key] !== undefined ? { [key]: patch.data[key] } : keepUnstated ? {} : { [key]: empty };
307
+ const ownerGrant = isOwner
308
+ ? {
309
+ ...field("allowedSecrets", []),
310
+ ...field("allowedDatastorePrefixes", []),
311
+ ...field("allowedHosts", []),
312
+ ...field("allowedSharedDatastorePrefixes", {}),
313
+ capabilitiesGrantedById: agent.ownerId,
314
+ }
315
+ : {};
153
316
  await tx.agentTool.upsert({
154
317
  where: { agentId_toolId: { agentId: args.agentId, toolId: args.toolId } },
155
318
  create: {
156
319
  agentId: args.agentId,
157
320
  toolId: args.toolId,
158
- allowedSecrets: patch.data.allowedSecrets ?? [],
159
- allowedDatastorePrefixes: patch.data.allowedDatastorePrefixes ?? [],
160
- allowedHosts: patch.data.allowedHosts ?? [],
161
- allowedSharedDatastorePrefixes: patch.data.allowedSharedDatastorePrefixes ?? {},
162
- },
163
- update: {
164
- ...(patch.data.allowedSecrets !== undefined ? { allowedSecrets: patch.data.allowedSecrets } : {}),
165
- ...(patch.data.allowedDatastorePrefixes !== undefined
166
- ? { allowedDatastorePrefixes: patch.data.allowedDatastorePrefixes }
167
- : {}),
168
- ...(patch.data.allowedHosts !== undefined ? { allowedHosts: patch.data.allowedHosts } : {}),
169
- ...(patch.data.allowedSharedDatastorePrefixes !== undefined
170
- ? { allowedSharedDatastorePrefixes: patch.data.allowedSharedDatastorePrefixes }
171
- : {}),
321
+ allowedSecrets: (isOwner && patch.data.allowedSecrets) || [],
322
+ allowedDatastorePrefixes: (isOwner && patch.data.allowedDatastorePrefixes) || [],
323
+ allowedHosts: (isOwner && patch.data.allowedHosts) || [],
324
+ allowedSharedDatastorePrefixes: (isOwner && patch.data.allowedSharedDatastorePrefixes) || {},
325
+ attachedById: ctx.principal.id,
326
+ capabilitiesGrantedById: isOwner ? agent.ownerId : null,
172
327
  },
328
+ // A non-owner's re-attach leaves an existing attachment's
329
+ // capabilities and consent stamp untouched.
330
+ update: ownerGrant,
173
331
  });
174
332
  }, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable });
175
333
  return textResult({ attached: true });
@@ -184,8 +342,9 @@ export function registerToolAuthoringTools(mcp) {
184
342
  required: ["agentId", "toolId"],
185
343
  },
186
344
  handler: async (args, ctx) => {
187
- await requireOwnedAgent(ctx.db, args.agentId, ctx.principal.id);
188
- await requireOwnedTool(ctx.db, args.toolId, ctx.principal.id);
345
+ // Write on the agent is enough, whoever owns the tool: an agent's
346
+ // owner (or write-grantee) can always pull a tool off it.
347
+ await requireAgentAccess(ctx, args.agentId, "write");
189
348
  await ctx.db.agentTool.deleteMany({ where: { agentId: args.agentId, toolId: args.toolId } });
190
349
  return textResult({ detached: true });
191
350
  },
@@ -195,13 +354,15 @@ export function registerToolAuthoringTools(mcp) {
195
354
  scope: "tools:write",
196
355
  inputSchema: { type: "object", properties: { agentId: { type: "string" } } },
197
356
  handler: async (args, ctx) => {
198
- const project = (tool) => tool.ownerId === ctx.principal.id ? tool : { id: tool.id, name: tool.name, description: tool.description };
357
+ const project = (tool) => projectTool(tool, ctx.principal.id);
199
358
  if (args.agentId) {
200
- await requireReadableAgent(ctx.db, args.agentId, ctx.principal.id);
359
+ const { access } = await requireAgentAccess(ctx, args.agentId, "read");
201
360
  const rows = await ctx.db.agentTool.findMany({ where: { agentId: args.agentId }, include: { tool: true } });
361
+ // The agent's owner sees every tool on it (projected: code only for
362
+ // the tool's own owner); anyone else only tools they could see anyway.
202
363
  return textResult(rows
203
364
  .map((r) => r.tool)
204
- .filter((tool) => canRead(tool.ownerId, ctx.principal.id))
365
+ .filter((tool) => access === "owner" || canRead(tool.ownerId, ctx.principal.id))
205
366
  .map(project));
206
367
  }
207
368
  const tools = await ctx.db.tool.findMany({ where: visibleToPrincipal(ctx.principal.id) });
@@ -33,7 +33,8 @@ import { CodingBaseRefSchema, CodingTaskOverrideSchema } from "../../coding/prot
33
33
  import { z } from "zod";
34
34
  import { McpError } from "../errors.js";
35
35
  import { createTaskResult, getTask, cancelTask } from "../tasks/manager.js";
36
- import { canMutate, requireOwnedAgent, requireOwnedTask } from "../auth/ownership.js";
36
+ import { requireOwnedTask } from "../auth/ownership.js";
37
+ import { assertAgentAccess } from "../auth/access.js";
37
38
  import { textResult } from "./text-result.js";
38
39
  const DEFAULT_TASK_TTL_MS = 24 * 60 * 60 * 1000;
39
40
  const TriggerAgentSchema = z
@@ -50,6 +51,29 @@ function parseTriggerArgs(args) {
50
51
  const details = parsed.error.issues.map((issue) => `${issue.path.join(".") || "input"}: ${issue.message}`).join("; ");
51
52
  throw new McpError(400, `Invalid trigger_agent arguments: ${details}`);
52
53
  }
54
+ /**
55
+ * A non-owner's coding task or baseRef would steer what the owner's
56
+ * repository authority does, so it is allowed only when the owner opted in
57
+ * to non-owner task text (codingProfile.allowWebhookTaskOverride, the same
58
+ * opt-in webhooks use). Otherwise a non-owner run uses the owner's
59
+ * defaultTask. Resource-sharing grants spec §3.4.5.
60
+ */
61
+ function assertOverrideAllowed(principalId, agent, args) {
62
+ // Strictly the owner: the stdio operator's owner-level access doesn't make
63
+ // it the owner whose repository authority the task steers (review M8).
64
+ const isOwner = agent.ownerId !== null && agent.ownerId === principalId;
65
+ if (isOwner || (args.task === undefined && args.baseRef === undefined))
66
+ return;
67
+ if (!agent.codingProfile?.allowWebhookTaskOverride) {
68
+ throw new McpError(403, `Agent "${agent.id}": only its owner can pass a task or baseRef, unless the owner allows it (codingProfile.allowWebhookTaskOverride).`);
69
+ }
70
+ }
71
+ /** execute on the agent, plus the override rule; `db` is the transaction when re-checking. */
72
+ async function requireTriggerable(ctx, row, id, args, db = ctx.db) {
73
+ const { agent } = await assertAgentAccess(ctx, row, id, "execute", db);
74
+ assertOverrideAllowed(ctx.principal.id, agent, args);
75
+ return agent;
76
+ }
53
77
  export function registerTriggerTool(mcp) {
54
78
  mcp.registerTool({
55
79
  name: "trigger_agent",
@@ -62,7 +86,7 @@ export function registerTriggerTool(mcp) {
62
86
  },
63
87
  handler: async (rawArgs, ctx) => {
64
88
  const args = parseTriggerArgs(rawArgs);
65
- const agent = await requireOwnedAgent(ctx.db, args.agentId, ctx.principal.id);
89
+ const agent = await requireTriggerable(ctx, await ctx.db.agent.findUnique({ where: { id: args.agentId }, include: { codingProfile: true } }), args.agentId, args);
66
90
  if (agent.kind !== "coding" && (args.task !== undefined || args.baseRef !== undefined)) {
67
91
  throw new McpError(400, "Task and baseRef overrides are only valid for coding agents.");
68
92
  }
@@ -74,10 +98,11 @@ export function registerTriggerTool(mcp) {
74
98
  codingTask: args.task,
75
99
  codingBaseRef: args.baseRef,
76
100
  task: ctx.clientSupportsTasks ? { principalId: ctx.principal.id, ttlMs: DEFAULT_TASK_TTL_MS } : undefined,
77
- beforePersist: async (_tx, current) => {
78
- if (!canMutate(current.ownerId, ctx.principal.id)) {
79
- throw new McpError(403, `Agent "${agent.id}" is not owned by the caller.`);
80
- }
101
+ triggeredById: ctx.principal.id,
102
+ // Re-checked through the transaction: a concurrent revoke or
103
+ // make_owner conflicts instead of racing the run in.
104
+ beforePersist: async (tx, current) => {
105
+ await requireTriggerable(ctx, current, agent.id, args, tx);
81
106
  return true;
82
107
  },
83
108
  });
@@ -88,6 +113,11 @@ export function registerTriggerTool(mcp) {
88
113
  throw new Error("Run task was not persisted.");
89
114
  return textResult(createTaskResult(dispatched.task, DEFAULT_TASK_TTL_MS));
90
115
  }
116
+ // A run refused at dispatch (its budget group or run tree is spent) is
117
+ // already terminal: say so now rather than only through get_run.
118
+ if (dispatched.run.status === "refused") {
119
+ return textResult({ runId: dispatched.run.id, status: "refused", error: dispatched.run.error });
120
+ }
91
121
  return textResult({ runId: dispatched.run.id });
92
122
  },
93
123
  });
@@ -1,13 +1,17 @@
1
1
  import { createWebhook, listWebhooks, deleteWebhook } from "../../core/webhooks.js";
2
- import { requireOwnedAgent, requireOwnedWebhook } from "../auth/ownership.js";
2
+ import { McpError } from "../errors.js";
3
+ import { requireAgentAccess } from "../auth/access.js";
3
4
  import { textResult } from "./text-result.js";
4
5
  export function registerWebhookTools(mcp) {
5
6
  mcp.registerTool({
6
7
  name: "create_webhook",
7
8
  scope: "webhooks:write",
9
+ description: "Creates a standing trigger for an agent (needs write on it) and returns its secret once. It fires only while you still own the agent or hold execute on it.",
8
10
  inputSchema: { type: "object", properties: { agentId: { type: "string" } }, required: ["agentId"] },
9
11
  handler: async (args, ctx) => {
10
- await requireOwnedAgent(ctx.db, args.agentId, ctx.principal.id);
12
+ // A standing trigger that can also carry task text for a native agent:
13
+ // write, not just execute (resource-sharing grants spec §3.10).
14
+ await requireAgentAccess(ctx, args.agentId, "write");
11
15
  const created = await createWebhook(args.agentId, ctx.principal.id, ctx.db);
12
16
  return textResult(created);
13
17
  },
@@ -15,18 +19,25 @@ export function registerWebhookTools(mcp) {
15
19
  mcp.registerTool({
16
20
  name: "list_webhooks",
17
21
  scope: "webhooks:write",
22
+ description: "Lists webhooks you created, and every webhook on an agent you own.",
18
23
  inputSchema: { type: "object", properties: {} },
19
24
  handler: async (_args, ctx) => {
20
- const webhooks = await listWebhooks(ctx.principal.id, ctx.db);
25
+ const webhooks = await listWebhooks(ctx.principal.id, ctx.db, { all: ctx.operator === true });
21
26
  return textResult(webhooks);
22
27
  },
23
28
  });
24
29
  mcp.registerTool({
25
30
  name: "delete_webhook",
26
31
  scope: "webhooks:write",
32
+ description: "Deletes a webhook you created, or any webhook on an agent you own.",
27
33
  inputSchema: { type: "object", properties: { id: { type: "string" } }, required: ["id"] },
28
34
  handler: async (args, ctx) => {
29
- await requireOwnedWebhook(ctx.db, args.id, ctx.principal.id);
35
+ const webhook = await ctx.db.webhook.findUnique({ where: { id: args.id }, include: { agent: true } });
36
+ const principalId = ctx.principal.id;
37
+ const allowed = webhook !== null &&
38
+ (ctx.operator === true || webhook.ownerId === principalId || webhook.agent.ownerId === principalId);
39
+ if (!allowed)
40
+ throw new McpError(403, `Webhook "${args.id}" is not owned by the caller.`);
30
41
  await deleteWebhook(args.id, ctx.db);
31
42
  return textResult({ deleted: args.id });
32
43
  },
@@ -1,10 +1,12 @@
1
1
  import { type ServerResponse } from "node:http";
2
2
  import { type PerRequestResponseMode } from "@modelcontextprotocol/server";
3
- import type { PrismaClient } from "@prisma/client";
3
+ import type { PrismaClient } from "#prisma";
4
4
  import type { AuthProvider } from "../../providers/auth/types.js";
5
5
  import type { SelfHostedAuthProvider } from "../../providers/auth/self-hosted.js";
6
6
  import type { WardbyMcpServer } from "../server.js";
7
7
  import type { McpProviders } from "../context.js";
8
+ import { type GitHubIngressDeps } from "../host-events/github-ingress.js";
9
+ import { type HostUserCallbackDeps } from "../host-events/github-user-callback.js";
8
10
  export interface HttpServerConfig {
9
11
  canonicalUri: string;
10
12
  httpBind: {
@@ -25,6 +27,14 @@ export interface StartHttpServerOptions {
25
27
  providers: McpProviders;
26
28
  };
27
29
  selfHosted?: SelfHostedAuthProvider;
30
+ /** Code-review host webhooks; absent = the endpoints answer 404. */
31
+ hostEvents?: {
32
+ github?: GitHubIngressDeps;
33
+ };
34
+ /** The host identity-link browser callback (link_host_account); absent = 404. */
35
+ hostUserAuth?: {
36
+ github?: HostUserCallbackDeps;
37
+ };
28
38
  }
29
39
  export interface HttpServerHandle {
30
40
  port: number;
@@ -5,6 +5,8 @@ import { toNodeHandler, hostHeaderValidation } from "@modelcontextprotocol/node"
5
5
  import { authenticate, protectedResourceMetadata } from "../auth/resource-server.js";
6
6
  import { McpError } from "../errors.js";
7
7
  import { handleWebhookIngress } from "../webhooks/ingress.js";
8
+ import { handleGitHubEventIngress } from "../host-events/github-ingress.js";
9
+ import { handleHostUserCallback } from "../host-events/github-user-callback.js";
8
10
  import { canonicalUrl, HTTP_LIMITS, HttpBoundaryError, parseBody, readBody } from "./http-limits.js";
9
11
  import { browserHandler } from "../auth/self-hosted/browser.js";
10
12
  import { handleSecretElicitationForm, SECRET_ELICITATION_PATH } from "../tools/secret-elicitation-form.js";
@@ -73,6 +75,7 @@ export async function startHttpServer(opts) {
73
75
  const controller = new AbortController();
74
76
  const timer = setTimeout(() => controller.abort(), HTTP_LIMITS.requestMs);
75
77
  let body;
78
+ let rawBody;
76
79
  try {
77
80
  const hasBody = req.method === "POST" ||
78
81
  req.method === "PUT" ||
@@ -80,8 +83,11 @@ export async function startHttpServer(opts) {
80
83
  req.headers["content-length"] ||
81
84
  req.headers["transfer-encoding"];
82
85
  if (hasBody) {
83
- const limit = url.pathname === "/mcp" || url.pathname.startsWith("/webhooks/") ? HTTP_LIMITS.json : HTTP_LIMITS.auth;
86
+ const limit = url.pathname === "/mcp" || url.pathname.startsWith("/webhooks/") || url.pathname.startsWith("/hosts/")
87
+ ? HTTP_LIMITS.json
88
+ : HTTP_LIMITS.auth;
84
89
  const raw = await readBody(req, limit, controller.signal);
90
+ rawBody = raw;
85
91
  body = parseBody(raw, req.headers["content-type"]);
86
92
  }
87
93
  }
@@ -117,6 +123,30 @@ export async function startHttpServer(opts) {
117
123
  sendJson(res, result.status, result.body);
118
124
  return;
119
125
  }
126
+ if (url.pathname === "/hosts/github/events" && req.method === "POST") {
127
+ if (!opts.hostEvents?.github) {
128
+ sendJson(res, 404, { error: "not_found" });
129
+ return;
130
+ }
131
+ const headers = Object.fromEntries(Object.entries(req.headers).map(([k, v]) => [k, Array.isArray(v) ? v[0] : v]));
132
+ const result = await handleGitHubEventIngress({ headers, rawBody: rawBody ?? "" }, opts.hostEvents.github);
133
+ sendJson(res, result.status, result.body);
134
+ if (result.afterResponse) {
135
+ void result.afterResponse().catch((err) => httpLog.warn({ err }, "host event follow-up failed"));
136
+ }
137
+ return;
138
+ }
139
+ if (url.pathname === "/hosts/github/user-callback" && req.method === "GET") {
140
+ // Unauthenticated by design (a browser redirect from GitHub): the
141
+ // single-use, hashed OAuth state is what binds it to a link request,
142
+ // and the canonical-Host check above has already run.
143
+ if (!opts.hostUserAuth?.github) {
144
+ sendJson(res, 404, { error: "not_found" });
145
+ return;
146
+ }
147
+ await handleHostUserCallback(url, res, opts.hostUserAuth.github);
148
+ return;
149
+ }
120
150
  if (url.pathname === "/mcp") {
121
151
  if (req.method === "POST" && (!body || body instanceof URLSearchParams))
122
152
  throw new HttpBoundaryError(415, "expected_json");
@@ -125,7 +155,7 @@ export async function startHttpServer(opts) {
125
155
  token: "",
126
156
  clientId: ctx.principal.subject,
127
157
  scopes: [...ctx.scopes],
128
- extra: { principal: ctx.principal },
158
+ extra: { principal: ctx.principal, roles: ctx.roles },
129
159
  };
130
160
  await nodeHandler(req, res, body);
131
161
  return;
@@ -1,4 +1,4 @@
1
- import type { PrismaClient } from "@prisma/client";
1
+ import type { PrismaClient } from "#prisma";
2
2
  /**
3
3
  * `wardby mcp` serves the MCP surface and launches the executor, but the
4
4
  * scheduler and reconciler live in `wardby scheduler` (or in `wardby serve`,
@@ -4,7 +4,7 @@
4
4
  * route in streamable-http.ts, matching the design's "route is
5
5
  * unauthenticated by OAuth" invariant.
6
6
  */
7
- import type { PrismaClient } from "@prisma/client";
7
+ import type { PrismaClient } from "#prisma";
8
8
  import type { Executor } from "../../providers/executor/types.js";
9
9
  export interface WebhookIngressRequest {
10
10
  headers: Record<string, string | undefined>;
@@ -13,7 +13,25 @@ export interface DelegatingAuthConfig {
13
13
  issuer?: string;
14
14
  audience?: string;
15
15
  jwksUri?: string;
16
+ /**
17
+ * AUTH_ROLE_CLAIM: the access-token claim carrying the IdP's roles/groups —
18
+ * an exact top-level claim name first (e.g. Auth0's namespaced
19
+ * `https://wardby.example/roles`, Okta `groups`, FusionAuth `roles`), else
20
+ * a dotted path into nested objects (Keycloak `realm_access.roles`).
21
+ */
22
+ roleClaim?: string;
23
+ /**
24
+ * AUTH_ROLE_MAP: comma-separated `idpValue=wardbyRole` pairs, e.g.
25
+ * `wardby-admin=admin,wardby-packages=package-approver,wardby-services=service-manager`.
26
+ */
27
+ roleMap?: string;
16
28
  }
29
+ /**
30
+ * Parses AUTH_ROLE_MAP. Whitespace around entries and around `=` is ignored;
31
+ * an unknown wardby role, or a malformed or empty entry, is a startup error.
32
+ * The LAST `=` splits an entry (wardby role names never contain one).
33
+ */
34
+ export declare function parseRoleMap(value: string): Map<string, string[]>;
17
35
  /**
18
36
  * Every spelling of the configured audience a token may legitimately carry.
19
37
  *
@@ -35,6 +53,7 @@ export declare class DelegatingAuthProvider implements AuthProvider {
35
53
  private readonly config;
36
54
  private readonly audiences;
37
55
  private readonly jwks;
56
+ private readonly roleMap?;
38
57
  constructor(config: DelegatingAuthConfig, jwks?: JWTVerifyGetKey);
39
58
  verifyBearer(token: string): Promise<VerifiedToken>;
40
59
  profile(idToken: string): Promise<AuthProfile>;