@wardby/cli 0.2.1 → 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 (407) hide show
  1. package/.env.example +34 -4
  2. package/README.md +41 -4
  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 +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 +75 -4
  21. package/dist/coding/protocol.js +107 -7
  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 +6 -0
  60. package/dist/coding-worker/debug-trace.d.ts +37 -0
  61. package/dist/coding-worker/debug-trace.js +117 -0
  62. package/dist/coding-worker/driver.d.ts +13 -1
  63. package/dist/coding-worker/driver.js +102 -13
  64. package/dist/coding-worker/errors.js +8 -0
  65. package/dist/coding-worker/main.js +10 -2
  66. package/dist/coding-worker/sdk.d.ts +2 -2
  67. package/dist/coding-worker/sdk.js +7 -2
  68. package/dist/coding-worker/types.d.ts +3 -0
  69. package/dist/config/providers.d.ts +41 -0
  70. package/dist/config/providers.js +66 -0
  71. package/dist/core/budget-groups.d.ts +108 -5
  72. package/dist/core/budget-groups.js +125 -19
  73. package/dist/core/budget-wording.d.ts +22 -0
  74. package/dist/core/budget-wording.js +55 -0
  75. package/dist/core/datastores.js +18 -2
  76. package/dist/core/db.d.ts +5 -1
  77. package/dist/core/db.js +8 -2
  78. package/dist/core/dispatch.d.ts +33 -6
  79. package/dist/core/dispatch.js +260 -43
  80. package/dist/core/engine-native.js +27 -10
  81. package/dist/core/grants.d.ts +86 -0
  82. package/dist/core/grants.js +126 -0
  83. package/dist/core/host-events.d.ts +49 -0
  84. package/dist/core/host-events.js +257 -0
  85. package/dist/core/host-identity-links.d.ts +54 -0
  86. package/dist/core/host-identity-links.js +189 -0
  87. package/dist/core/host-status.d.ts +61 -0
  88. package/dist/core/host-status.js +211 -0
  89. package/dist/core/in-flight-runs.d.ts +8 -0
  90. package/dist/core/in-flight-runs.js +56 -0
  91. package/dist/core/provider-wording.d.ts +12 -0
  92. package/dist/core/provider-wording.js +44 -0
  93. package/dist/core/reconciler.d.ts +38 -2
  94. package/dist/core/reconciler.js +86 -2
  95. package/dist/core/repo-access.d.ts +99 -0
  96. package/dist/core/repo-access.js +136 -0
  97. package/dist/core/review-host-checks.d.ts +11 -0
  98. package/dist/core/review-host-checks.js +41 -0
  99. package/dist/core/review-host-tools.d.ts +47 -0
  100. package/dist/core/review-host-tools.js +347 -0
  101. package/dist/core/run-heartbeat.d.ts +27 -0
  102. package/dist/core/run-heartbeat.js +54 -0
  103. package/dist/core/runner.d.ts +30 -8
  104. package/dist/core/runner.js +237 -40
  105. package/dist/core/secrets.d.ts +11 -2
  106. package/dist/core/secrets.js +25 -6
  107. package/dist/core/subagent-memory-tools.d.ts +1 -1
  108. package/dist/core/subagent-memory-tools.js +16 -3
  109. package/dist/core/tool-admin.d.ts +81 -0
  110. package/dist/core/tool-admin.js +129 -0
  111. package/dist/core/tool-names.d.ts +42 -0
  112. package/dist/core/tool-names.js +64 -0
  113. package/dist/core/untrusted-content.d.ts +32 -0
  114. package/dist/core/untrusted-content.js +72 -0
  115. package/dist/core/webhooks.d.ts +8 -1
  116. package/dist/core/webhooks.js +23 -2
  117. package/dist/generated/prisma/browser.d.ts +100 -0
  118. package/dist/generated/prisma/client.d.ts +100 -0
  119. package/dist/generated/prisma/commonInputTypes.d.ts +30 -0
  120. package/dist/generated/prisma/enums.d.ts +6 -0
  121. package/dist/generated/prisma/enums.js +6 -1
  122. package/dist/generated/prisma/internal/class.d.ts +143 -0
  123. package/dist/generated/prisma/internal/class.js +4 -4
  124. package/dist/generated/prisma/internal/prismaNamespace.d.ts +1746 -577
  125. package/dist/generated/prisma/internal/prismaNamespace.js +179 -6
  126. package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +186 -0
  127. package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +179 -6
  128. package/dist/generated/prisma/models/Agent.d.ts +294 -1
  129. package/dist/generated/prisma/models/AgentRepository.d.ts +1425 -0
  130. package/dist/generated/prisma/models/AgentRepository.js +1 -0
  131. package/dist/generated/prisma/models/AgentTool.d.ts +95 -1
  132. package/dist/generated/prisma/models/AuthUser.d.ts +56 -1
  133. package/dist/generated/prisma/models/CodingAgentProfile.d.ts +258 -7
  134. package/dist/generated/prisma/models/CodingProxySession.d.ts +123 -2
  135. package/dist/generated/prisma/models/CodingRun.d.ts +1276 -95
  136. package/dist/generated/prisma/models/CodingService.d.ts +1348 -0
  137. package/dist/generated/prisma/models/CodingService.js +1 -0
  138. package/dist/generated/prisma/models/HostEventDelivery.d.ts +946 -0
  139. package/dist/generated/prisma/models/HostEventDelivery.js +1 -0
  140. package/dist/generated/prisma/models/HostIdentity.d.ts +1232 -0
  141. package/dist/generated/prisma/models/HostIdentity.js +1 -0
  142. package/dist/generated/prisma/models/HostIdentityLinkRequest.d.ts +1473 -0
  143. package/dist/generated/prisma/models/HostIdentityLinkRequest.js +1 -0
  144. package/dist/generated/prisma/models/Principal.d.ts +455 -0
  145. package/dist/generated/prisma/models/RegistryAllowance.d.ts +1148 -0
  146. package/dist/generated/prisma/models/RegistryAllowance.js +1 -0
  147. package/dist/generated/prisma/models/RegistryApprovedVersion.d.ts +1219 -0
  148. package/dist/generated/prisma/models/RegistryApprovedVersion.js +1 -0
  149. package/dist/generated/prisma/models/RegistryFetch.d.ts +1428 -0
  150. package/dist/generated/prisma/models/RegistryFetch.js +1 -0
  151. package/dist/generated/prisma/models/RegistryPlanRefusal.d.ts +1294 -0
  152. package/dist/generated/prisma/models/RegistryPlanRefusal.js +1 -0
  153. package/dist/generated/prisma/models/RegistryVersionFact.d.ts +1085 -0
  154. package/dist/generated/prisma/models/RegistryVersionFact.js +1 -0
  155. package/dist/generated/prisma/models/ResourceGrant.d.ts +1437 -0
  156. package/dist/generated/prisma/models/ResourceGrant.js +1 -0
  157. package/dist/generated/prisma/models/Run.d.ts +389 -1
  158. package/dist/generated/prisma/models/RunHostCheck.d.ts +1239 -0
  159. package/dist/generated/prisma/models/RunHostCheck.js +1 -0
  160. package/dist/generated/prisma/models/RunHostStatus.d.ts +1315 -0
  161. package/dist/generated/prisma/models/RunHostStatus.js +1 -0
  162. package/dist/generated/prisma/models/Tool.d.ts +15 -3
  163. package/dist/generated/prisma/models.d.ts +13 -0
  164. package/dist/help/build.d.ts +1 -0
  165. package/dist/help/build.js +9 -0
  166. package/dist/help/catalog.d.ts +24 -0
  167. package/dist/help/catalog.js +160 -0
  168. package/dist/help/cli.d.ts +2 -0
  169. package/dist/help/cli.js +65 -0
  170. package/dist/help/runtime.d.ts +3 -0
  171. package/dist/help/runtime.js +44 -0
  172. package/dist/help/search.d.ts +9 -0
  173. package/dist/help/search.js +104 -0
  174. package/dist/help-index.json +660 -0
  175. package/dist/import/cli-args.js +3 -2
  176. package/dist/import/create.d.ts +5 -0
  177. package/dist/import/create.js +67 -13
  178. package/dist/import/index.js +19 -9
  179. package/dist/import/neutral-schema.d.ts +11 -11
  180. package/dist/mcp/auth/access.d.ts +70 -0
  181. package/dist/mcp/auth/access.js +58 -0
  182. package/dist/mcp/auth/grants-cli.d.ts +149 -0
  183. package/dist/mcp/auth/grants-cli.js +518 -0
  184. package/dist/mcp/auth/host-account-cli.d.ts +2 -0
  185. package/dist/mcp/auth/host-account-cli.js +47 -0
  186. package/dist/mcp/auth/ownership.d.ts +26 -52
  187. package/dist/mcp/auth/ownership.js +19 -14
  188. package/dist/mcp/auth/repo-authorization.d.ts +22 -0
  189. package/dist/mcp/auth/repo-authorization.js +51 -0
  190. package/dist/mcp/auth/resource-server.d.ts +31 -2
  191. package/dist/mcp/auth/resource-server.js +84 -3
  192. package/dist/mcp/auth/self-hosted/browser.js +2 -2
  193. package/dist/mcp/auth/self-hosted/cli.js +47 -13
  194. package/dist/mcp/auth/self-hosted/credentials.d.ts +20 -2
  195. package/dist/mcp/auth/self-hosted/credentials.js +62 -4
  196. package/dist/mcp/auth/self-hosted/session.d.ts +2 -1
  197. package/dist/mcp/context.d.ts +27 -1
  198. package/dist/mcp/errors.d.ts +25 -6
  199. package/dist/mcp/errors.js +98 -0
  200. package/dist/mcp/host-events/github-ingress.d.ts +36 -0
  201. package/dist/mcp/host-events/github-ingress.js +92 -0
  202. package/dist/mcp/host-events/github-user-callback.d.ts +18 -0
  203. package/dist/mcp/host-events/github-user-callback.js +50 -0
  204. package/dist/mcp/index.d.ts +8 -1
  205. package/dist/mcp/index.js +106 -16
  206. package/dist/mcp/server.js +27 -9
  207. package/dist/mcp/tools/agents.js +446 -48
  208. package/dist/mcp/tools/budget-groups.js +3 -3
  209. package/dist/mcp/tools/datastore.js +22 -15
  210. package/dist/mcp/tools/grants.d.ts +2 -0
  211. package/dist/mcp/tools/grants.js +239 -0
  212. package/dist/mcp/tools/help.d.ts +5 -0
  213. package/dist/mcp/tools/help.js +67 -0
  214. package/dist/mcp/tools/host-accounts.d.ts +2 -0
  215. package/dist/mcp/tools/host-accounts.js +114 -0
  216. package/dist/mcp/tools/memory.d.ts +7 -1
  217. package/dist/mcp/tools/memory.js +5 -5
  218. package/dist/mcp/tools/repositories.d.ts +2 -0
  219. package/dist/mcp/tools/repositories.js +181 -0
  220. package/dist/mcp/tools/runs.d.ts +6 -0
  221. package/dist/mcp/tools/runs.js +60 -6
  222. package/dist/mcp/tools/scheduling.js +3 -6
  223. package/dist/mcp/tools/secrets.js +18 -6
  224. package/dist/mcp/tools/services.d.ts +2 -0
  225. package/dist/mcp/tools/services.js +222 -0
  226. package/dist/mcp/tools/subagents.js +52 -16
  227. package/dist/mcp/tools/tools.d.ts +41 -0
  228. package/dist/mcp/tools/tools.js +201 -40
  229. package/dist/mcp/tools/trigger.js +36 -6
  230. package/dist/mcp/tools/webhooks.js +15 -4
  231. package/dist/mcp/transport/streamable-http.d.ts +10 -0
  232. package/dist/mcp/transport/streamable-http.js +32 -2
  233. package/dist/providers/auth/delegating.d.ts +19 -0
  234. package/dist/providers/auth/delegating.js +71 -0
  235. package/dist/providers/auth/index.d.ts +2 -0
  236. package/dist/providers/auth/index.js +11 -0
  237. package/dist/providers/auth/self-hosted.d.ts +2 -1
  238. package/dist/providers/auth/self-hosted.js +16 -4
  239. package/dist/providers/auth/types.d.ts +6 -0
  240. package/dist/providers/coding-proxy/memory-ledger.d.ts +3 -0
  241. package/dist/providers/coding-proxy/memory-ledger.js +21 -2
  242. package/dist/providers/coding-proxy/metering.d.ts +4 -0
  243. package/dist/providers/coding-proxy/metering.js +28 -2
  244. package/dist/providers/coding-proxy/prisma-ledger.d.ts +17 -0
  245. package/dist/providers/coding-proxy/prisma-ledger.js +50 -5
  246. package/dist/providers/coding-proxy/proxy.d.ts +13 -0
  247. package/dist/providers/coding-proxy/proxy.js +479 -18
  248. package/dist/providers/coding-proxy/registry/audit.d.ts +131 -0
  249. package/dist/providers/coding-proxy/registry/audit.js +380 -0
  250. package/dist/providers/coding-proxy/registry/bounded-fetch.d.ts +16 -0
  251. package/dist/providers/coding-proxy/registry/bounded-fetch.js +77 -0
  252. package/dist/providers/coding-proxy/registry/plan.d.ts +58 -0
  253. package/dist/providers/coding-proxy/registry/plan.js +304 -0
  254. package/dist/providers/coding-proxy/registry/prisma-store.d.ts +34 -0
  255. package/dist/providers/coding-proxy/registry/prisma-store.js +151 -0
  256. package/dist/providers/coding-proxy/registry/service.d.ts +213 -0
  257. package/dist/providers/coding-proxy/registry/service.js +1137 -0
  258. package/dist/providers/coding-proxy/registry/store.d.ts +127 -0
  259. package/dist/providers/coding-proxy/registry/store.js +70 -0
  260. package/dist/providers/coding-proxy/runtime.d.ts +17 -0
  261. package/dist/providers/coding-proxy/runtime.js +63 -0
  262. package/dist/providers/coding-proxy/secure-fetch.js +0 -1
  263. package/dist/providers/coding-proxy/server.d.ts +11 -0
  264. package/dist/providers/coding-proxy/server.js +163 -0
  265. package/dist/providers/coding-proxy/types.d.ts +15 -0
  266. package/dist/providers/engine/types.d.ts +10 -0
  267. package/dist/providers/executor/build.d.ts +2 -2
  268. package/dist/providers/executor/composition.d.ts +3 -0
  269. package/dist/providers/executor/composition.js +19 -0
  270. package/dist/providers/executor/container.d.ts +105 -3
  271. package/dist/providers/executor/container.js +296 -20
  272. package/dist/providers/executor/dbos.d.ts +2 -3
  273. package/dist/providers/executor/in-process.d.ts +2 -3
  274. package/dist/providers/executor/routing.d.ts +7 -0
  275. package/dist/providers/executor/routing.js +13 -0
  276. package/dist/providers/executor/types.d.ts +22 -0
  277. package/dist/providers/jobs/claude-tool-setup.d.ts +23 -0
  278. package/dist/providers/jobs/claude-tool-setup.js +50 -0
  279. package/dist/providers/jobs/collect-prune.d.ts +7 -0
  280. package/dist/providers/jobs/collect-prune.js +27 -0
  281. package/dist/providers/jobs/docker-isolation.d.ts +48 -3
  282. package/dist/providers/jobs/docker-isolation.js +213 -22
  283. package/dist/providers/jobs/docker-services.d.ts +35 -0
  284. package/dist/providers/jobs/docker-services.js +191 -0
  285. package/dist/providers/jobs/docker.d.ts +49 -2
  286. package/dist/providers/jobs/docker.js +279 -32
  287. package/dist/providers/jobs/fake-kubernetes-api.d.ts +1 -0
  288. package/dist/providers/jobs/fake-kubernetes-api.js +12 -3
  289. package/dist/providers/jobs/kubernetes-isolation.d.ts +17 -2
  290. package/dist/providers/jobs/kubernetes-isolation.js +238 -57
  291. package/dist/providers/jobs/kubernetes-platform.d.ts +10 -4
  292. package/dist/providers/jobs/kubernetes-platform.js +11 -5
  293. package/dist/providers/jobs/kubernetes-preflight.js +3 -0
  294. package/dist/providers/jobs/kubernetes.d.ts +21 -2
  295. package/dist/providers/jobs/kubernetes.js +112 -21
  296. package/dist/providers/jobs/types.d.ts +18 -0
  297. package/dist/providers/llm/anthropic.js +6 -2
  298. package/dist/providers/llm/bedrock.js +2 -1
  299. package/dist/providers/llm/claude-messages.d.ts +5 -1
  300. package/dist/providers/llm/claude-messages.js +1 -0
  301. package/dist/providers/llm/claude-provider.d.ts +3 -1
  302. package/dist/providers/llm/claude-provider.js +5 -1
  303. package/dist/providers/llm/index.d.ts +1 -1
  304. package/dist/providers/llm/index.js +1 -1
  305. package/dist/providers/llm/pricing-anthropic.d.ts +10 -0
  306. package/dist/providers/llm/pricing-anthropic.js +32 -14
  307. package/dist/providers/llm/pricing-bedrock-claude.d.ts +8 -0
  308. package/dist/providers/llm/pricing-bedrock-claude.js +9 -0
  309. package/dist/providers/llm/routing.d.ts +10 -1
  310. package/dist/providers/llm/routing.js +18 -0
  311. package/dist/providers/llm/types.d.ts +12 -0
  312. package/dist/providers/llm/types.js +8 -1
  313. package/dist/providers/review-host/diff-lines.d.ts +16 -0
  314. package/dist/providers/review-host/diff-lines.js +59 -0
  315. package/dist/providers/review-host/github-events.d.ts +6 -0
  316. package/dist/providers/review-host/github-events.js +180 -0
  317. package/dist/providers/review-host/github-user-auth.d.ts +38 -0
  318. package/dist/providers/review-host/github-user-auth.js +128 -0
  319. package/dist/providers/review-host/github.d.ts +57 -0
  320. package/dist/providers/review-host/github.js +567 -0
  321. package/dist/providers/review-host/index.d.ts +12 -0
  322. package/dist/providers/review-host/index.js +30 -0
  323. package/dist/providers/review-host/review-format.d.ts +19 -0
  324. package/dist/providers/review-host/review-format.js +59 -0
  325. package/dist/providers/review-host/types.d.ts +284 -0
  326. package/dist/providers/review-host/types.js +24 -0
  327. package/dist/providers/vcs/git.d.ts +16 -6
  328. package/dist/providers/vcs/git.js +77 -13
  329. package/dist/providers/vcs/github.d.ts +74 -3
  330. package/dist/providers/vcs/github.js +195 -15
  331. package/dist/providers/vcs/types.d.ts +42 -4
  332. package/dist/quickstart/index.js +13 -2
  333. package/dist/sandbox/fetch-policy.d.ts +20 -2
  334. package/dist/sandbox/fetch-policy.js +64 -3
  335. package/dist/sandbox/host-functions.d.ts +9 -1
  336. package/dist/sandbox/host-functions.js +12 -5
  337. package/dist/serve.js +1 -1
  338. package/dist/wardby-bin.js +6 -0
  339. package/docs/README.md +30 -0
  340. package/docs/architecture-runtime.md +90 -0
  341. package/docs/assets/brand/wardby-icon-512.png +0 -0
  342. package/docs/assets/brand/wardby-icon.svg +16 -0
  343. package/docs/assets/brand/wardby-mascot-profile-512.png +0 -0
  344. package/docs/assets/brand/wardby-mascot.png +0 -0
  345. package/docs/assets/brand/wardby-mascot.svg +5 -0
  346. package/docs/assets/wardby-workflow.svg +106 -0
  347. package/docs/code-review-agents.md +481 -0
  348. package/docs/coding-agent-setup.md +172 -0
  349. package/docs/coding-packages.md +455 -0
  350. package/docs/coding-services.md +300 -0
  351. package/docs/coding-worker-byo-images.md +98 -0
  352. package/docs/coding-worker-isolation.md +1061 -0
  353. package/docs/getting-started-gke.md +615 -0
  354. package/docs/getting-started-identity-provider.md +308 -0
  355. package/docs/getting-started.md +133 -0
  356. package/docs/observability.md +53 -0
  357. package/docs/release-verification.md +66 -0
  358. package/docs/security-deployment.md +657 -0
  359. package/help/code-review-agents.md +31 -0
  360. package/help/coding-packages.md +31 -0
  361. package/help/coding-services.md +71 -0
  362. package/help/creating-agents.md +68 -0
  363. package/help/deploy-gke.md +39 -0
  364. package/help/deployment-targets.md +39 -0
  365. package/help/errors/budget-group-exhausted.md +25 -0
  366. package/help/errors/docker-isolation-unsupported.md +26 -0
  367. package/help/errors/protected-path.md +45 -0
  368. package/help/errors/repo-access.md +27 -0
  369. package/help/errors/service-declaration-invalid.md +27 -0
  370. package/help/errors/service-declaration-unavailable.md +25 -0
  371. package/help/errors/service-launcher-unsupported.md +30 -0
  372. package/help/errors/service-not-allowed.md +27 -0
  373. package/help/errors/service-unknown.md +23 -0
  374. package/help/errors/service-unready.md +49 -0
  375. package/help/getting-started.md +31 -0
  376. package/help/github.md +30 -0
  377. package/help/identity-and-access.md +39 -0
  378. package/help/mcp.md +30 -0
  379. package/help/native-capabilities.md +30 -0
  380. package/help/observability.md +41 -0
  381. package/help/operating-agents.md +30 -0
  382. package/help/security.md +27 -0
  383. package/help/troubleshooting/budgets.md +26 -0
  384. package/help/troubleshooting/coding-workers.md +47 -0
  385. package/help/troubleshooting/repository-access.md +27 -0
  386. package/package.json +12 -3
  387. package/prisma/migrations/20260925010000_coding_collect_exclude/migration.sql +8 -0
  388. package/prisma/migrations/20260925015000_allowed_egress_default/migration.sql +8 -0
  389. package/prisma/migrations/20260925020000_coding_package_registry/migration.sql +55 -0
  390. package/prisma/migrations/20260925030000_tool_name_per_owner/migration.sql +11 -0
  391. package/prisma/migrations/20260926010000_run_trigger_host_event/migration.sql +7 -0
  392. package/prisma/migrations/20260926020000_code_review_hosts/migration.sql +53 -0
  393. package/prisma/migrations/20260926030000_auth_user_roles/migration.sql +9 -0
  394. package/prisma/migrations/20260926040000_agent_effort/migration.sql +6 -0
  395. package/prisma/migrations/20260926050000_registry_lockfile_plan/migration.sql +32 -0
  396. package/prisma/migrations/20260926050000_repo_access_authorization/migration.sql +82 -0
  397. package/prisma/migrations/20260926060000_registry_plan_refusal/migration.sql +19 -0
  398. package/prisma/migrations/20260926100000_registry_plan_refusal_published_at/migration.sql +5 -0
  399. package/prisma/migrations/20260926190000_run_host_status/migration.sql +18 -0
  400. package/prisma/migrations/20260926210000_run_host_status_at_dispatch/migration.sql +5 -0
  401. package/prisma/migrations/20260927010000_resource_grants/migration.sql +72 -0
  402. package/prisma/migrations/20260927020000_proxy_session_budget_exhausted/migration.sql +3 -0
  403. package/prisma/migrations/20260927030000_coding_debug_trace/migration.sql +4 -0
  404. package/prisma/migrations/20260927040000_proxy_session_upstream_failure/migration.sql +3 -0
  405. package/prisma/migrations/20260927050000_coding_run_services/migration.sql +74 -0
  406. package/prisma/migrations/20260928000000_coding_run_tool_image/migration.sql +5 -0
  407. package/prisma/schema.prisma +407 -21
@@ -107,13 +107,13 @@ export function registerBudgetGroupTools(mcp) {
107
107
  handler: async (args, ctx) => {
108
108
  const group = await ctx.db.budgetGroup.findUnique({
109
109
  where: { id: args.id },
110
- include: { agents: { select: { id: true, name: true } } },
110
+ include: { agents: { select: { id: true, name: true, budgetUsd: true } } },
111
111
  });
112
112
  if (!group || !canRead(group.ownerId, ctx.principal.id)) {
113
113
  throw new McpError(404, `Budget group "${args.id}" not found.`);
114
114
  }
115
- const spend = await computeGroupSpend(ctx.db, group, group.agents.map((a) => a.id), new Date());
116
- return textResult({ ...group, spend });
115
+ const spend = await computeGroupSpend(ctx.db, group, group.agents, new Date());
116
+ return textResult({ ...group, agents: group.agents.map(({ id, name }) => ({ id, name })), spend });
117
117
  },
118
118
  });
119
119
  mcp.registerTool({
@@ -1,17 +1,15 @@
1
1
  import { Prisma } from "#prisma";
2
- import { requireOwnedAgent, requireReadableAgent, requireOwnedDatastore, canRead, assertCanMutate, } from "../auth/ownership.js";
2
+ import { requireOwnedDatastore, canRead, assertCanMutate } from "../auth/ownership.js";
3
+ import { requireAgentAccess, requireBindingOwner } from "../auth/access.js";
3
4
  import { createDatastore, listDatastores, deleteDatastore, attachDatastore, detachDatastore, buildSharedDatastoreAccessor, } from "../../core/datastores.js";
4
5
  import { McpError } from "../errors.js";
5
6
  import { textResult } from "./text-result.js";
6
7
  /**
7
8
  * A `boundName` names an *attachment* (agentId, boundName) -> Datastore.
8
- * requireOwnedAgent/requireReadableAgent only check the agent's own
9
- * ownership — for a public (null-owner) agent that's readable/mutable by
10
- * anyone, so gating solely on the agent would let a principal who attaches
11
- * their own private Datastore to a public agent inadvertently expose that
12
- * Datastore's full read/write access to every other principal, with no
13
- * sandbox mediation. So every boundName branch additionally checks the
14
- * underlying Datastore's own ownership. When no attachment exists under
9
+ * Agent data (the private store and bound stores) is owner-only (resource-
10
+ * sharing grants spec §3.4.6), and a binding only resolves while the
11
+ * datastore's owner is the agent's owner (core/datastores.ts). These checks
12
+ * on the underlying Datastore's own ownership stay as defence in depth. When no attachment exists under
15
13
  * that boundName, this resolves to `undefined` and callers fall through to
16
14
  * today's unchanged behavior (miss for reads, throw for writes) — this
17
15
  * check only ever narrows access when an attachment IS found, it never
@@ -97,8 +95,15 @@ export function registerDatastoreTools(mcp) {
97
95
  required: ["agentId", "datastoreId"],
98
96
  },
99
97
  handler: async (args, ctx) => {
100
- await requireOwnedAgent(ctx.db, args.agentId, ctx.principal.id);
98
+ // A binding hands the datastore to every tool the agent's owner lets
99
+ // read it: strictly the agent owner's own datastore on the owner's
100
+ // own agent (A2/S2-2). Not even the stdio operator crosses owners.
101
+ const { agent } = await requireAgentAccess(ctx, args.agentId, "read");
102
+ requireBindingOwner(ctx, agent);
101
103
  const datastore = await requireOwnedDatastore(ctx.db, args.datastoreId, ctx.principal.id);
104
+ if (datastore.ownerId !== agent.ownerId) {
105
+ throw new McpError(403, "Only a datastore owned by the agent's owner can be attached to it.");
106
+ }
102
107
  const boundName = args.boundName ?? datastore.name;
103
108
  try {
104
109
  await attachDatastore(args.agentId, args.datastoreId, ctx.db, boundName);
@@ -121,8 +126,10 @@ export function registerDatastoreTools(mcp) {
121
126
  required: ["agentId", "boundName"],
122
127
  },
123
128
  handler: async (args, ctx) => {
124
- await requireOwnedAgent(ctx.db, args.agentId, ctx.principal.id);
125
- await requireMutableSharedDatastore(ctx.db, args.agentId, args.boundName, ctx.principal.id);
129
+ // The agent's owner may remove any binding from it, including one to
130
+ // another owner's datastore left behind by a transfer.
131
+ const { agent } = await requireAgentAccess(ctx, args.agentId, "read");
132
+ requireBindingOwner(ctx, agent);
126
133
  await detachDatastore(args.agentId, args.boundName, ctx.db);
127
134
  return textResult({ detached: true });
128
135
  },
@@ -136,7 +143,7 @@ export function registerDatastoreTools(mcp) {
136
143
  required: ["agentId", "key"],
137
144
  },
138
145
  handler: async (args, ctx) => {
139
- await requireReadableAgent(ctx.db, args.agentId, ctx.principal.id);
146
+ await requireAgentAccess(ctx, args.agentId, "owner");
140
147
  if (args.boundName) {
141
148
  await requireReadableSharedDatastore(ctx.db, args.agentId, args.boundName, ctx.principal.id);
142
149
  const accessor = buildSharedDatastoreAccessor(args.agentId, ctx.providers.datastore, ctx.db);
@@ -156,7 +163,7 @@ export function registerDatastoreTools(mcp) {
156
163
  required: ["agentId"],
157
164
  },
158
165
  handler: async (args, ctx) => {
159
- await requireReadableAgent(ctx.db, args.agentId, ctx.principal.id);
166
+ await requireAgentAccess(ctx, args.agentId, "owner");
160
167
  if (args.boundName) {
161
168
  await requireReadableSharedDatastore(ctx.db, args.agentId, args.boundName, ctx.principal.id);
162
169
  const accessor = buildSharedDatastoreAccessor(args.agentId, ctx.providers.datastore, ctx.db);
@@ -183,7 +190,7 @@ export function registerDatastoreTools(mcp) {
183
190
  required: ["agentId", "key", "value"],
184
191
  },
185
192
  handler: async (args, ctx) => {
186
- await requireOwnedAgent(ctx.db, args.agentId, ctx.principal.id);
193
+ await requireAgentAccess(ctx, args.agentId, "owner");
187
194
  if (args.boundName) {
188
195
  await requireMutableSharedDatastore(ctx.db, args.agentId, args.boundName, ctx.principal.id);
189
196
  const accessor = buildSharedDatastoreAccessor(args.agentId, ctx.providers.datastore, ctx.db);
@@ -203,7 +210,7 @@ export function registerDatastoreTools(mcp) {
203
210
  required: ["agentId", "key"],
204
211
  },
205
212
  handler: async (args, ctx) => {
206
- await requireOwnedAgent(ctx.db, args.agentId, ctx.principal.id);
213
+ await requireAgentAccess(ctx, args.agentId, "owner");
207
214
  if (args.boundName) {
208
215
  await requireMutableSharedDatastore(ctx.db, args.agentId, args.boundName, ctx.principal.id);
209
216
  const accessor = buildSharedDatastoreAccessor(args.agentId, ctx.providers.datastore, ctx.db);
@@ -0,0 +1,2 @@
1
+ import type { WardbyMcpServer } from "../server.js";
2
+ export declare function registerGrantTools(mcp: WardbyMcpServer): void;
@@ -0,0 +1,239 @@
1
+ /**
2
+ * grant_access / revoke_access / list_access: the owner's sharing controls
3
+ * (resource-sharing grants spec §3.11). Phase 1 shares agents only; other
4
+ * resource types are refused as "not shareable yet".
5
+ *
6
+ * - Only the resource's owner (or the stdio operator) grants and revokes.
7
+ * Admins get no implicit access: they may only list_access, for incident
8
+ * response ("who can reach this?"), and reassign with make_owner.
9
+ * - One grant per resource and grantee, so granting again replaces the
10
+ * level (also downgrades). Everyone grants are capped below write.
11
+ * - Takes effect on the next check; runs already in flight keep going.
12
+ */
13
+ import { z } from "zod";
14
+ import { Prisma } from "#prisma";
15
+ import { EVERYONE_KEY, EVERYONE_MAX, LEVELS, SHAREABLE_TYPES, accessRank, atLeast, isLevel, isResourceType, principalGranteeKey, } from "../../core/grants.js";
16
+ import { agentNotFound, assertAgentAccess, agentAccess } from "../auth/access.js";
17
+ import { permissionsOf } from "../auth/resource-server.js";
18
+ import { McpError } from "../errors.js";
19
+ import { textResult } from "./text-result.js";
20
+ const GranteeSchema = z.union([
21
+ z.literal("everyone"),
22
+ z.object({ subject: z.string().min(1).max(512) }).strict(),
23
+ z.object({ principalId: z.string().min(1).max(128) }).strict(),
24
+ ]);
25
+ const TargetSchema = z.object({
26
+ resourceType: z.string().min(1).max(64),
27
+ resourceId: z.string().min(1).max(128),
28
+ });
29
+ const GrantSchema = TargetSchema.extend({ grantee: GranteeSchema, level: z.string().min(1).max(64) }).strict();
30
+ const RevokeSchema = TargetSchema.extend({ grantee: GranteeSchema }).strict();
31
+ const ListSchema = TargetSchema.strict();
32
+ function parse(schema, label, args) {
33
+ const result = schema.safeParse(args);
34
+ if (result.success)
35
+ return result.data;
36
+ const details = result.error.issues.map((i) => `${i.path.join(".") || "input"}: ${i.message}`).join("; ");
37
+ throw new McpError(400, `Invalid ${label} arguments: ${details}`);
38
+ }
39
+ function shareableType(value) {
40
+ if (!isResourceType(value) || !SHAREABLE_TYPES.includes(value)) {
41
+ throw new McpError(400, `Resource type "${value}" is not shareable yet (shareable: ${SHAREABLE_TYPES.join(", ")}).`);
42
+ }
43
+ return value;
44
+ }
45
+ /** The grantee's principal (must have signed in once: never created here), or null for everyone. */
46
+ async function resolveGrantee(db, grantee) {
47
+ if (grantee === "everyone")
48
+ return null;
49
+ const principal = "subject" in grantee
50
+ ? await db.principal.findUnique({ where: { subject: grantee.subject } })
51
+ : await db.principal.findUnique({ where: { id: grantee.principalId } });
52
+ if (!principal) {
53
+ throw new McpError(404, "Grantee not found: they must have signed in to wardby at least once.");
54
+ }
55
+ return principal;
56
+ }
57
+ /** Grant management is the owner's (or the stdio operator's). Phase 1: agents. */
58
+ async function requireOwnedResource(ctx, db, type, id) {
59
+ // Only "agent" reaches here in Phase 1 (shareableType).
60
+ void type;
61
+ const row = await db.agent.findUnique({ where: { id } });
62
+ return (await assertAgentAccess(ctx, row, id, "owner", db)).agent;
63
+ }
64
+ /**
65
+ * The ownership check and the write in one Serializable transaction (review
66
+ * M3): a concurrent make_owner, which resets grants in its own transaction,
67
+ * conflicts instead of letting the old owner's grant land on the new
68
+ * owner's agent.
69
+ */
70
+ function inSerializable(ctx, fn) {
71
+ return ctx.db.$transaction((tx) => fn(tx), { isolationLevel: Prisma.TransactionIsolationLevel.Serializable });
72
+ }
73
+ function isAdmin(ctx) {
74
+ return ctx.scopes.has("agents:admin") && permissionsOf(ctx.roles).has("agents:admin");
75
+ }
76
+ const TYPE_PROPERTIES = {
77
+ // No enum: another type gets a clear "not shareable yet" from the handler.
78
+ resourceType: { type: "string", description: 'Only "agent" is shareable today.' },
79
+ resourceId: { type: "string" },
80
+ };
81
+ const GRANTEE_PROPERTY = {
82
+ description: 'One principal, as {"subject": "<IdP subject>"} or {"principalId": "<id>"}, or "everyone".',
83
+ anyOf: [
84
+ { type: "string", enum: ["everyone"] },
85
+ {
86
+ type: "object",
87
+ additionalProperties: false,
88
+ properties: { subject: { type: "string" } },
89
+ required: ["subject"],
90
+ },
91
+ {
92
+ type: "object",
93
+ additionalProperties: false,
94
+ properties: { principalId: { type: "string" } },
95
+ required: ["principalId"],
96
+ },
97
+ ],
98
+ };
99
+ export function registerGrantTools(mcp) {
100
+ mcp.registerTool({
101
+ name: "grant_access",
102
+ scope: "agents:write",
103
+ description: "Shares an agent you own with one principal or with everyone, at a level: " +
104
+ "read (see its config, never secret values or other owners' tool code); " +
105
+ "execute (also trigger runs and see the runs they triggered); " +
106
+ "write (also change its name, prompt, model, budget amount, max turns, effort, memory on/off and schedule, attach tools they can use and detach tools, and create webhooks). " +
107
+ "Owner-only whatever the level: the coding profile (task, base ref, protected paths, task-override opt-in, image, packages, limits), kind, budget group, sub-agents, secret/datastore/repository bindings, tool capabilities, memory and datastore contents, grants, delete. " +
108
+ "Consequences: execute lets them run your agent with your tools, secrets, datastores and repository, and every run shares the agent's memory; they supply no task text unless it's a coding agent with allowWebhookTaskOverride. " +
109
+ "Write lets them rewrite what those runs do: the prompt can make a run use everything the agent can already reach (the capabilities you granted, its memory, its linked repositories), and on a coding agent the prompt is part of every coding task, so write directs work done with your GitHub access. " +
110
+ "Granting again replaces the level. Everyone is capped at execute. The grantee must have signed in once.",
111
+ inputSchema: {
112
+ type: "object",
113
+ additionalProperties: false,
114
+ properties: {
115
+ ...TYPE_PROPERTIES,
116
+ grantee: GRANTEE_PROPERTY,
117
+ level: { type: "string", description: "Agents: read < execute < write." },
118
+ },
119
+ required: ["resourceType", "resourceId", "grantee", "level"],
120
+ },
121
+ handler: async (rawArgs, ctx) => {
122
+ const args = parse(GrantSchema, "grant_access", rawArgs);
123
+ const type = shareableType(args.resourceType);
124
+ if (!isLevel(type, args.level)) {
125
+ throw new McpError(400, `Level "${args.level}" is not valid for ${type} (${LEVELS[type].join(" < ")}).`);
126
+ }
127
+ const { resource, principal, grant } = await inSerializable(ctx, async (tx) => {
128
+ const resource = await requireOwnedResource(ctx, tx, type, args.resourceId);
129
+ const principal = await resolveGrantee(tx, args.grantee);
130
+ if (principal && (principal.id === resource.ownerId || principal.id === ctx.principal.id)) {
131
+ throw new McpError(400, "The owner already has full access; a grant to yourself or the owner changes nothing.");
132
+ }
133
+ if (!principal && accessRank(type, args.level) > accessRank(type, EVERYONE_MAX[type])) {
134
+ throw new McpError(400, `An everyone grant can be at most ${EVERYONE_MAX[type]}: write for everyone would let anyone rewrite an agent that holds your tools and secrets.`);
135
+ }
136
+ const granteeKey = principal ? principalGranteeKey(principal.id) : EVERYONE_KEY;
137
+ const grant = await tx.resourceGrant.upsert({
138
+ where: { resourceType_resourceId_granteeKey: { resourceType: type, resourceId: resource.id, granteeKey } },
139
+ create: {
140
+ resourceType: type,
141
+ resourceId: resource.id,
142
+ granteeKind: principal ? "principal" : "everyone",
143
+ granteePrincipalId: principal?.id ?? null,
144
+ granteeKey,
145
+ level: args.level,
146
+ source: "owner",
147
+ grantedById: ctx.principal.id,
148
+ },
149
+ update: { level: args.level, source: "owner", grantedById: ctx.principal.id },
150
+ });
151
+ return { resource, principal, grant };
152
+ });
153
+ return textResult({
154
+ granted: true,
155
+ resourceType: type,
156
+ resourceId: resource.id,
157
+ // Echo only what the caller supplied: a principalId never reveals a subject (review M4).
158
+ grantee: !principal
159
+ ? "everyone"
160
+ : args.grantee !== "everyone" && "subject" in args.grantee
161
+ ? { principalId: principal.id, subject: principal.subject }
162
+ : { principalId: principal.id },
163
+ level: grant.level,
164
+ });
165
+ },
166
+ });
167
+ mcp.registerTool({
168
+ name: "revoke_access",
169
+ scope: "agents:write",
170
+ description: "Removes a grant on an agent you own. Idempotent. Takes effect on the next check (trigger, delegation, webhook fire); runs already in flight keep going.",
171
+ inputSchema: {
172
+ type: "object",
173
+ additionalProperties: false,
174
+ properties: { ...TYPE_PROPERTIES, grantee: GRANTEE_PROPERTY },
175
+ required: ["resourceType", "resourceId", "grantee"],
176
+ },
177
+ handler: async (rawArgs, ctx) => {
178
+ const args = parse(RevokeSchema, "revoke_access", rawArgs);
179
+ const type = shareableType(args.resourceType);
180
+ const { count } = await inSerializable(ctx, async (tx) => {
181
+ const resource = await requireOwnedResource(ctx, tx, type, args.resourceId);
182
+ const granteeKey = args.grantee === "everyone"
183
+ ? EVERYONE_KEY
184
+ : principalGranteeKey((await resolveGrantee(tx, args.grantee)).id);
185
+ return tx.resourceGrant.deleteMany({ where: { resourceType: type, resourceId: resource.id, granteeKey } });
186
+ });
187
+ return textResult({ revoked: count > 0 });
188
+ },
189
+ });
190
+ mcp.registerTool({
191
+ name: "list_access",
192
+ scope: "agents:read",
193
+ description: "Lists who an agent is shared with (grant metadata only). For its owner, the stdio operator, and admins (incident response).",
194
+ inputSchema: {
195
+ type: "object",
196
+ additionalProperties: false,
197
+ properties: TYPE_PROPERTIES,
198
+ required: ["resourceType", "resourceId"],
199
+ },
200
+ handler: async (rawArgs, ctx) => {
201
+ const args = parse(ListSchema, "list_access", rawArgs);
202
+ const type = shareableType(args.resourceType);
203
+ const resource = await ctx.db.agent.findUnique({ where: { id: args.resourceId } });
204
+ if (!resource)
205
+ throw agentNotFound(args.resourceId);
206
+ if (!isAdmin(ctx)) {
207
+ const access = await agentAccess(ctx, resource);
208
+ if (!atLeast("agent", access, "read"))
209
+ throw agentNotFound(args.resourceId);
210
+ if (access !== "owner") {
211
+ throw new McpError(403, `Agent "${resource.id}": only its owner (or an admin) can list who it is shared with.`);
212
+ }
213
+ }
214
+ const grants = await ctx.db.resourceGrant.findMany({
215
+ where: { resourceType: type, resourceId: resource.id },
216
+ orderBy: { createdAt: "asc" },
217
+ });
218
+ const principalIds = grants.flatMap((g) => (g.granteePrincipalId ? [g.granteePrincipalId] : []));
219
+ const principals = principalIds.length
220
+ ? await ctx.db.principal.findMany({ where: { id: { in: principalIds } } })
221
+ : [];
222
+ const subjectOf = new Map(principals.map((p) => [p.id, p.subject]));
223
+ return textResult({
224
+ resourceType: type,
225
+ resourceId: resource.id,
226
+ ownerId: resource.ownerId,
227
+ grants: grants.map((g) => ({
228
+ grantee: g.granteePrincipalId
229
+ ? { principalId: g.granteePrincipalId, subject: subjectOf.get(g.granteePrincipalId) ?? null }
230
+ : "everyone",
231
+ level: g.level,
232
+ source: g.source,
233
+ grantedById: g.grantedById,
234
+ updatedAt: g.updatedAt,
235
+ })),
236
+ });
237
+ },
238
+ });
239
+ }
@@ -0,0 +1,5 @@
1
+ import type { HelpCatalog } from "../../help/catalog.js";
2
+ import type { WardbyMcpServer } from "../server.js";
3
+ export type HelpCatalogLoader = () => Promise<HelpCatalog>;
4
+ /** Registers release-bundled, read-only help without exposing any instance data or credentials. */
5
+ export declare function registerHelpTools(mcp: WardbyMcpServer, loadCatalog?: HelpCatalogLoader): void;
@@ -0,0 +1,67 @@
1
+ import { loadBundledHelpCatalog } from "../../help/runtime.js";
2
+ import { searchHelp } from "../../help/search.js";
3
+ import { McpError } from "../errors.js";
4
+ import { textResult } from "./text-result.js";
5
+ function publicArticle(page) {
6
+ return {
7
+ id: page.id,
8
+ title: page.title,
9
+ summary: page.summary,
10
+ audience: page.audience,
11
+ tags: page.tags,
12
+ appliesTo: page.appliesTo,
13
+ headings: page.headings,
14
+ markdown: page.markdown,
15
+ };
16
+ }
17
+ /** Registers release-bundled, read-only help without exposing any instance data or credentials. */
18
+ export function registerHelpTools(mcp, loadCatalog = loadBundledHelpCatalog) {
19
+ mcp.registerTool({
20
+ name: "search_help",
21
+ description: "Search Wardby's bundled self-hosted documentation with offline fuzzy matching.",
22
+ scope: "agents:read",
23
+ inputSchema: {
24
+ type: "object",
25
+ properties: { query: { type: "string", minLength: 1, maxLength: 200 } },
26
+ required: ["query"],
27
+ additionalProperties: false,
28
+ },
29
+ handler: async (args) => {
30
+ const query = args.query.trim();
31
+ if (!query)
32
+ throw new McpError(400, "search_help requires a non-empty query.");
33
+ const results = searchHelp(await loadCatalog(), query).slice(0, 10);
34
+ return textResult({
35
+ results: results.map((result) => ({
36
+ id: result.page.id,
37
+ title: result.page.title,
38
+ summary: result.page.summary,
39
+ audience: result.page.audience,
40
+ tags: result.page.tags,
41
+ ...(result.matchedHeading ? { matchedHeading: result.matchedHeading.text } : {}),
42
+ excerpt: result.excerpt,
43
+ })),
44
+ });
45
+ },
46
+ });
47
+ mcp.registerTool({
48
+ name: "get_help_article",
49
+ description: "Read one complete Wardby self-hosted help article by its id.",
50
+ scope: "agents:read",
51
+ inputSchema: {
52
+ type: "object",
53
+ properties: { id: { type: "string", minLength: 1, maxLength: 120 } },
54
+ required: ["id"],
55
+ additionalProperties: false,
56
+ },
57
+ handler: async (args) => {
58
+ const id = args.id.trim();
59
+ if (!id)
60
+ throw new McpError(400, "get_help_article requires a non-empty id.");
61
+ const page = (await loadCatalog()).pages.find((candidate) => candidate.id === id);
62
+ if (!page)
63
+ throw new McpError(404, `Help article "${id}" not found.`);
64
+ return textResult(publicArticle(page));
65
+ },
66
+ });
67
+ }
@@ -0,0 +1,2 @@
1
+ import type { WardbyMcpServer } from "../server.js";
2
+ export declare function registerHostAccountTools(mcp: WardbyMcpServer): void;
@@ -0,0 +1,114 @@
1
+ /**
2
+ * A principal's own code-review host identity (GitHub today). Repository
3
+ * authorization checks the agent owner's access through this identity (see
4
+ * core/repo-access.ts), so linking it is the first step before linking a
5
+ * repository or setting a coding agent's repository. Each tool acts only on
6
+ * the caller; an operator can list and unlink anyone's with
7
+ * `wardby auth host-account`.
8
+ */
9
+ import { REVIEW_HOST_PROVIDERS } from "../../providers/review-host/types.js";
10
+ import { confirmHostIdentityLink, HostLinkError, startHostIdentityLink, userCallbackPath, } from "../../core/host-identity-links.js";
11
+ import { McpError } from "../errors.js";
12
+ import { canonicalUrl } from "../transport/http-limits.js";
13
+ import { textResult } from "./text-result.js";
14
+ const PROVIDER_PROP = { type: "string", enum: [...REVIEW_HOST_PROVIDERS] };
15
+ function asMcpError(err) {
16
+ return err instanceof HostLinkError ? new McpError(err.status, err.message) : err;
17
+ }
18
+ /** The HTTP origin the host's browser callback must reach, or null over stdio (no canonical URI). */
19
+ function callbackOrigin(canonicalUri) {
20
+ try {
21
+ return canonicalUrl(canonicalUri).origin;
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ }
27
+ export function registerHostAccountTools(mcp) {
28
+ mcp.registerTool({
29
+ name: "link_host_account",
30
+ scope: "agents:write",
31
+ description: "Links your GitHub account to your wardby identity, so agents you own can be given repositories your GitHub account can access. " +
32
+ "Call it with no confirmationCode to get an authorizeUrl; open that in a browser signed in to GitHub. The page you land on shows a " +
33
+ "one-time code: call link_host_account again with confirmationCode set to it (within 10 minutes).",
34
+ inputSchema: {
35
+ type: "object",
36
+ additionalProperties: false,
37
+ properties: {
38
+ provider: PROVIDER_PROP,
39
+ confirmationCode: { type: "string", minLength: 1, maxLength: 32 },
40
+ },
41
+ },
42
+ handler: async (args, ctx) => {
43
+ const provider = args.provider ?? "github";
44
+ try {
45
+ if (args.confirmationCode !== undefined) {
46
+ const linked = await confirmHostIdentityLink({
47
+ db: ctx.db,
48
+ principalId: ctx.principal.id,
49
+ provider,
50
+ confirmationCode: args.confirmationCode,
51
+ });
52
+ return textResult({ linked: true, provider, login: linked.login });
53
+ }
54
+ const authorizer = ctx.providers.hostUserAuthorizers?.[provider];
55
+ if (!authorizer) {
56
+ throw new McpError(400, "GitHub account linking is not configured on this deployment: an operator must set GITHUB_APP_CLIENT_ID and " +
57
+ "GITHUB_APP_CLIENT_SECRET (see docs/code-review-agents.md).");
58
+ }
59
+ const origin = callbackOrigin(ctx.canonicalUri);
60
+ if (!origin) {
61
+ throw new McpError(400, "Linking needs wardby's HTTP transport (the GitHub callback is served at MCP_CANONICAL_URI); " +
62
+ "use an MCP client connected over HTTP, or ask an operator.");
63
+ }
64
+ const started = await startHostIdentityLink({
65
+ db: ctx.db,
66
+ authorizer,
67
+ principalId: ctx.principal.id,
68
+ redirectUri: `${origin}${userCallbackPath(provider)}`,
69
+ });
70
+ return textResult({
71
+ authorizeUrl: started.authorizeUrl,
72
+ expiresAt: started.expiresAt.toISOString(),
73
+ next: "Open authorizeUrl in a browser signed in to the GitHub account to link. Then call link_host_account " +
74
+ "with confirmationCode set to the code the page shows.",
75
+ });
76
+ }
77
+ catch (err) {
78
+ throw asMcpError(err);
79
+ }
80
+ },
81
+ });
82
+ mcp.registerTool({
83
+ name: "get_host_account",
84
+ scope: "agents:read",
85
+ description: "Shows the GitHub account linked to your wardby identity, if any.",
86
+ inputSchema: { type: "object", additionalProperties: false, properties: {} },
87
+ handler: async (_args, ctx) => {
88
+ const identities = await ctx.db.hostIdentity.findMany({
89
+ where: { principalId: ctx.principal.id },
90
+ orderBy: { provider: "asc" },
91
+ });
92
+ return textResult({
93
+ accounts: identities.map((i) => ({
94
+ provider: i.provider,
95
+ login: i.login,
96
+ hostUserId: i.hostUserId,
97
+ linkedAt: i.linkedAt,
98
+ })),
99
+ });
100
+ },
101
+ });
102
+ mcp.registerTool({
103
+ name: "unlink_host_account",
104
+ scope: "agents:write",
105
+ description: "Unlinks your GitHub account. Repositories your agents were given through its access stop working until you link again.",
106
+ inputSchema: { type: "object", additionalProperties: false, properties: { provider: PROVIDER_PROP } },
107
+ handler: async (args, ctx) => {
108
+ const provider = args.provider ?? "github";
109
+ const { count } = await ctx.db.hostIdentity.deleteMany({ where: { principalId: ctx.principal.id, provider } });
110
+ await ctx.db.hostIdentityLinkRequest.deleteMany({ where: { principalId: ctx.principal.id, provider } });
111
+ return textResult({ unlinked: count > 0 });
112
+ },
113
+ });
114
+ }
@@ -1,3 +1,9 @@
1
- /** Operator inspection of agent memory, mirroring the Datastore MCP tools (`./datastore.ts`). */
1
+ /**
2
+ * Operator inspection of agent memory, mirroring the Datastore MCP tools
3
+ * (`./datastore.ts`). Memory is agent *data*, written by every triggerer's
4
+ * runs, so every tool here is owner-only whatever the grants (resource-
5
+ * sharing grants spec §3.4.6): showing it at read or execute would leak one
6
+ * triggerer's run output to another (A6 through memory).
7
+ */
2
8
  import type { WardbyMcpServer } from "../server.js";
3
9
  export declare function registerMemoryTools(mcp: WardbyMcpServer): void;
@@ -1,4 +1,4 @@
1
- import { requireOwnedAgent, requireReadableAgent } from "../auth/ownership.js";
1
+ import { requireAgentAccess } from "../auth/access.js";
2
2
  import { textResult } from "./text-result.js";
3
3
  export function registerMemoryTools(mcp) {
4
4
  mcp.registerTool({
@@ -10,7 +10,7 @@ export function registerMemoryTools(mcp) {
10
10
  required: ["agentId", "key"],
11
11
  },
12
12
  handler: async (args, ctx) => {
13
- await requireReadableAgent(ctx.db, args.agentId, ctx.principal.id);
13
+ await requireAgentAccess(ctx, args.agentId, "owner");
14
14
  const content = await ctx.providers.memory.get(args.agentId, args.key);
15
15
  return textResult({ content: content ?? null });
16
16
  },
@@ -24,7 +24,7 @@ export function registerMemoryTools(mcp) {
24
24
  required: ["agentId"],
25
25
  },
26
26
  handler: async (args, ctx) => {
27
- await requireReadableAgent(ctx.db, args.agentId, ctx.principal.id);
27
+ await requireAgentAccess(ctx, args.agentId, "owner");
28
28
  const keys = await ctx.providers.memory.list(args.agentId);
29
29
  return textResult(keys);
30
30
  },
@@ -38,7 +38,7 @@ export function registerMemoryTools(mcp) {
38
38
  required: ["agentId", "key", "content"],
39
39
  },
40
40
  handler: async (args, ctx) => {
41
- await requireOwnedAgent(ctx.db, args.agentId, ctx.principal.id);
41
+ await requireAgentAccess(ctx, args.agentId, "owner");
42
42
  await ctx.providers.memory.set(args.agentId, args.key, args.content);
43
43
  return textResult({ ok: true });
44
44
  },
@@ -52,7 +52,7 @@ export function registerMemoryTools(mcp) {
52
52
  required: ["agentId", "key"],
53
53
  },
54
54
  handler: async (args, ctx) => {
55
- await requireOwnedAgent(ctx.db, args.agentId, ctx.principal.id);
55
+ await requireAgentAccess(ctx, args.agentId, "owner");
56
56
  await ctx.providers.memory.delete(args.agentId, args.key);
57
57
  return textResult({ ok: true });
58
58
  },
@@ -0,0 +1,2 @@
1
+ import type { WardbyMcpServer } from "../server.js";
2
+ export declare function registerRepositoryTools(mcp: WardbyMcpServer): void;