@wardby/cli 0.1.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 (412) hide show
  1. package/.env.example +131 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +2 -0
  4. package/README.md +282 -0
  5. package/bin/wardby.js +3 -0
  6. package/deploy/local/docker-compose.yml +19 -0
  7. package/dist/claude-coding-worker/driver.d.ts +70 -0
  8. package/dist/claude-coding-worker/driver.js +128 -0
  9. package/dist/claude-coding-worker/main.d.ts +2 -0
  10. package/dist/claude-coding-worker/main.js +41 -0
  11. package/dist/claude-coding-worker/sdk.d.ts +4 -0
  12. package/dist/claude-coding-worker/sdk.js +43 -0
  13. package/dist/claude-coding-worker/tool-relay.d.ts +1 -0
  14. package/dist/claude-coding-worker/tool-relay.js +9 -0
  15. package/dist/cli-help.d.ts +1 -0
  16. package/dist/cli-help.js +20 -0
  17. package/dist/cli.d.ts +18 -0
  18. package/dist/cli.js +629 -0
  19. package/dist/coding/observability.d.ts +59 -0
  20. package/dist/coding/observability.js +77 -0
  21. package/dist/coding/profile.d.ts +88 -0
  22. package/dist/coding/profile.js +137 -0
  23. package/dist/coding/protocol.d.ts +345 -0
  24. package/dist/coding/protocol.js +445 -0
  25. package/dist/coding/provider.d.ts +4 -0
  26. package/dist/coding/provider.js +18 -0
  27. package/dist/coding-proxy/main.d.ts +1 -0
  28. package/dist/coding-proxy/main.js +51 -0
  29. package/dist/coding-worker/artifact.d.ts +16 -0
  30. package/dist/coding-worker/artifact.js +37 -0
  31. package/dist/coding-worker/driver.d.ts +46 -0
  32. package/dist/coding-worker/driver.js +158 -0
  33. package/dist/coding-worker/errors.d.ts +1 -0
  34. package/dist/coding-worker/errors.js +23 -0
  35. package/dist/coding-worker/isolation-probe.d.ts +1 -0
  36. package/dist/coding-worker/isolation-probe.js +146 -0
  37. package/dist/coding-worker/keeper.d.ts +4 -0
  38. package/dist/coding-worker/keeper.js +29 -0
  39. package/dist/coding-worker/main.d.ts +2 -0
  40. package/dist/coding-worker/main.js +43 -0
  41. package/dist/coding-worker/sdk.d.ts +26 -0
  42. package/dist/coding-worker/sdk.js +28 -0
  43. package/dist/coding-worker/types.d.ts +64 -0
  44. package/dist/coding-worker/types.js +1 -0
  45. package/dist/config/providers.d.ts +158 -0
  46. package/dist/config/providers.js +162 -0
  47. package/dist/core/budget-groups.d.ts +72 -0
  48. package/dist/core/budget-groups.js +149 -0
  49. package/dist/core/budget.d.ts +63 -0
  50. package/dist/core/budget.js +69 -0
  51. package/dist/core/coding-queue.d.ts +32 -0
  52. package/dist/core/coding-queue.js +54 -0
  53. package/dist/core/cron.d.ts +31 -0
  54. package/dist/core/cron.js +43 -0
  55. package/dist/core/datastores.d.ts +41 -0
  56. package/dist/core/datastores.js +105 -0
  57. package/dist/core/db.d.ts +2 -0
  58. package/dist/core/db.js +2 -0
  59. package/dist/core/dispatch.d.ts +81 -0
  60. package/dist/core/dispatch.js +184 -0
  61. package/dist/core/engine-native.d.ts +29 -0
  62. package/dist/core/engine-native.js +259 -0
  63. package/dist/core/http-runtime.d.ts +7 -0
  64. package/dist/core/http-runtime.js +101 -0
  65. package/dist/core/lease.d.ts +13 -0
  66. package/dist/core/lease.js +22 -0
  67. package/dist/core/logger.d.ts +13 -0
  68. package/dist/core/logger.js +45 -0
  69. package/dist/core/memory-tools.d.ts +11 -0
  70. package/dist/core/memory-tools.js +122 -0
  71. package/dist/core/reconciler.d.ts +45 -0
  72. package/dist/core/reconciler.js +152 -0
  73. package/dist/core/runner.d.ts +87 -0
  74. package/dist/core/runner.js +529 -0
  75. package/dist/core/scheduler.d.ts +47 -0
  76. package/dist/core/scheduler.js +121 -0
  77. package/dist/core/secrets.d.ts +46 -0
  78. package/dist/core/secrets.js +100 -0
  79. package/dist/core/subagent-memory-tools.d.ts +18 -0
  80. package/dist/core/subagent-memory-tools.js +121 -0
  81. package/dist/core/timing.d.ts +7 -0
  82. package/dist/core/timing.js +7 -0
  83. package/dist/core/webhooks.d.ts +23 -0
  84. package/dist/core/webhooks.js +113 -0
  85. package/dist/env.d.ts +11 -0
  86. package/dist/env.js +13 -0
  87. package/dist/import/budgets.d.ts +16 -0
  88. package/dist/import/budgets.js +21 -0
  89. package/dist/import/bundle.d.ts +16 -0
  90. package/dist/import/bundle.js +61 -0
  91. package/dist/import/cli-args.d.ts +2 -0
  92. package/dist/import/cli-args.js +44 -0
  93. package/dist/import/create.d.ts +34 -0
  94. package/dist/import/create.js +363 -0
  95. package/dist/import/index.d.ts +25 -0
  96. package/dist/import/index.js +130 -0
  97. package/dist/import/model-gate.d.ts +1 -0
  98. package/dist/import/model-gate.js +3 -0
  99. package/dist/import/neutral-schema.d.ts +369 -0
  100. package/dist/import/neutral-schema.js +109 -0
  101. package/dist/import/preflight.d.ts +41 -0
  102. package/dist/import/preflight.js +71 -0
  103. package/dist/import/report.d.ts +5 -0
  104. package/dist/import/report.js +98 -0
  105. package/dist/import/tool-scan.d.ts +8 -0
  106. package/dist/import/tool-scan.js +20 -0
  107. package/dist/mcp/auth/ownership.d.ts +113 -0
  108. package/dist/mcp/auth/ownership.js +87 -0
  109. package/dist/mcp/auth/principal.d.ts +9 -0
  110. package/dist/mcp/auth/principal.js +9 -0
  111. package/dist/mcp/auth/resource-server.d.ts +47 -0
  112. package/dist/mcp/auth/resource-server.js +82 -0
  113. package/dist/mcp/auth/self-hosted/browser.d.ts +3 -0
  114. package/dist/mcp/auth/self-hosted/browser.js +273 -0
  115. package/dist/mcp/auth/self-hosted/cli.d.ts +2 -0
  116. package/dist/mcp/auth/self-hosted/cli.js +39 -0
  117. package/dist/mcp/auth/self-hosted/credentials.d.ts +58 -0
  118. package/dist/mcp/auth/self-hosted/credentials.js +106 -0
  119. package/dist/mcp/auth/self-hosted/rate-limit.d.ts +13 -0
  120. package/dist/mcp/auth/self-hosted/rate-limit.js +20 -0
  121. package/dist/mcp/auth/self-hosted/session.d.ts +36 -0
  122. package/dist/mcp/auth/self-hosted/session.js +110 -0
  123. package/dist/mcp/capabilities.d.ts +11 -0
  124. package/dist/mcp/capabilities.js +10 -0
  125. package/dist/mcp/context.d.ts +46 -0
  126. package/dist/mcp/context.js +1 -0
  127. package/dist/mcp/errors.d.ts +13 -0
  128. package/dist/mcp/errors.js +22 -0
  129. package/dist/mcp/index.d.ts +32 -0
  130. package/dist/mcp/index.js +187 -0
  131. package/dist/mcp/jsonrpc.d.ts +8 -0
  132. package/dist/mcp/jsonrpc.js +1 -0
  133. package/dist/mcp/server.d.ts +107 -0
  134. package/dist/mcp/server.js +203 -0
  135. package/dist/mcp/tasks/manager.d.ts +63 -0
  136. package/dist/mcp/tasks/manager.js +103 -0
  137. package/dist/mcp/tools/agents.d.ts +2 -0
  138. package/dist/mcp/tools/agents.js +375 -0
  139. package/dist/mcp/tools/budget-groups.d.ts +2 -0
  140. package/dist/mcp/tools/budget-groups.js +136 -0
  141. package/dist/mcp/tools/datastore.d.ts +2 -0
  142. package/dist/mcp/tools/datastore.js +217 -0
  143. package/dist/mcp/tools/memory.d.ts +3 -0
  144. package/dist/mcp/tools/memory.js +60 -0
  145. package/dist/mcp/tools/models.d.ts +8 -0
  146. package/dist/mcp/tools/models.js +15 -0
  147. package/dist/mcp/tools/runs.d.ts +2 -0
  148. package/dist/mcp/tools/runs.js +61 -0
  149. package/dist/mcp/tools/scheduling.d.ts +2 -0
  150. package/dist/mcp/tools/scheduling.js +48 -0
  151. package/dist/mcp/tools/secret-elicitation-form.d.ts +22 -0
  152. package/dist/mcp/tools/secret-elicitation-form.js +93 -0
  153. package/dist/mcp/tools/secret-elicitation-server.d.ts +5 -0
  154. package/dist/mcp/tools/secret-elicitation-server.js +57 -0
  155. package/dist/mcp/tools/secret-elicitation.d.ts +38 -0
  156. package/dist/mcp/tools/secret-elicitation.js +52 -0
  157. package/dist/mcp/tools/secrets.d.ts +13 -0
  158. package/dist/mcp/tools/secrets.js +124 -0
  159. package/dist/mcp/tools/subagents.d.ts +2 -0
  160. package/dist/mcp/tools/subagents.js +133 -0
  161. package/dist/mcp/tools/text-result.d.ts +6 -0
  162. package/dist/mcp/tools/text-result.js +3 -0
  163. package/dist/mcp/tools/tools.d.ts +2 -0
  164. package/dist/mcp/tools/tools.js +211 -0
  165. package/dist/mcp/tools/trigger.d.ts +2 -0
  166. package/dist/mcp/tools/trigger.js +120 -0
  167. package/dist/mcp/tools/webhooks.d.ts +2 -0
  168. package/dist/mcp/tools/webhooks.js +34 -0
  169. package/dist/mcp/transport/http-limits.d.ts +17 -0
  170. package/dist/mcp/transport/http-limits.js +92 -0
  171. package/dist/mcp/transport/stdio.d.ts +23 -0
  172. package/dist/mcp/transport/stdio.js +6 -0
  173. package/dist/mcp/transport/streamable-http.d.ts +34 -0
  174. package/dist/mcp/transport/streamable-http.js +158 -0
  175. package/dist/mcp/unattended-schedules.d.ts +15 -0
  176. package/dist/mcp/unattended-schedules.js +21 -0
  177. package/dist/mcp/webhooks/ingress.d.ts +17 -0
  178. package/dist/mcp/webhooks/ingress.js +33 -0
  179. package/dist/observability/config.d.ts +6 -0
  180. package/dist/observability/config.js +20 -0
  181. package/dist/observability/metrics-server.d.ts +11 -0
  182. package/dist/observability/metrics-server.js +47 -0
  183. package/dist/observability/metrics.d.ts +36 -0
  184. package/dist/observability/metrics.js +158 -0
  185. package/dist/providers/auth/authorization-server.d.ts +41 -0
  186. package/dist/providers/auth/authorization-server.js +1 -0
  187. package/dist/providers/auth/delegating.d.ts +44 -0
  188. package/dist/providers/auth/delegating.js +105 -0
  189. package/dist/providers/auth/index.d.ts +11 -0
  190. package/dist/providers/auth/index.js +20 -0
  191. package/dist/providers/auth/release-gate.d.ts +1 -0
  192. package/dist/providers/auth/release-gate.js +4 -0
  193. package/dist/providers/auth/self-hosted.d.ts +78 -0
  194. package/dist/providers/auth/self-hosted.js +377 -0
  195. package/dist/providers/auth/subject.d.ts +1 -0
  196. package/dist/providers/auth/subject.js +6 -0
  197. package/dist/providers/auth/types.d.ts +63 -0
  198. package/dist/providers/auth/types.js +22 -0
  199. package/dist/providers/coding-proxy/deny-port.d.ts +5 -0
  200. package/dist/providers/coding-proxy/deny-port.js +45 -0
  201. package/dist/providers/coding-proxy/environment-credentials.d.ts +7 -0
  202. package/dist/providers/coding-proxy/environment-credentials.js +16 -0
  203. package/dist/providers/coding-proxy/index.d.ts +7 -0
  204. package/dist/providers/coding-proxy/index.js +7 -0
  205. package/dist/providers/coding-proxy/memory-ledger.d.ts +17 -0
  206. package/dist/providers/coding-proxy/memory-ledger.js +103 -0
  207. package/dist/providers/coding-proxy/metering.d.ts +20 -0
  208. package/dist/providers/coding-proxy/metering.js +161 -0
  209. package/dist/providers/coding-proxy/prisma-ledger.d.ts +15 -0
  210. package/dist/providers/coding-proxy/prisma-ledger.js +213 -0
  211. package/dist/providers/coding-proxy/proxy.d.ts +75 -0
  212. package/dist/providers/coding-proxy/proxy.js +721 -0
  213. package/dist/providers/coding-proxy/runtime.d.ts +18 -0
  214. package/dist/providers/coding-proxy/runtime.js +39 -0
  215. package/dist/providers/coding-proxy/secure-fetch.d.ts +9 -0
  216. package/dist/providers/coding-proxy/secure-fetch.js +72 -0
  217. package/dist/providers/coding-proxy/server.d.ts +17 -0
  218. package/dist/providers/coding-proxy/server.js +139 -0
  219. package/dist/providers/coding-proxy/types.d.ts +99 -0
  220. package/dist/providers/coding-proxy/types.js +1 -0
  221. package/dist/providers/datastore/index.d.ts +2 -0
  222. package/dist/providers/datastore/index.js +2 -0
  223. package/dist/providers/datastore/postgres.d.ts +20 -0
  224. package/dist/providers/datastore/postgres.js +130 -0
  225. package/dist/providers/datastore/scoped.d.ts +10 -0
  226. package/dist/providers/datastore/scoped.js +46 -0
  227. package/dist/providers/datastore/types.d.ts +31 -0
  228. package/dist/providers/datastore/types.js +7 -0
  229. package/dist/providers/email/types.d.ts +46 -0
  230. package/dist/providers/email/types.js +8 -0
  231. package/dist/providers/engine/index.d.ts +1 -0
  232. package/dist/providers/engine/index.js +1 -0
  233. package/dist/providers/engine/types.d.ts +67 -0
  234. package/dist/providers/engine/types.js +16 -0
  235. package/dist/providers/executor/build.d.ts +6 -0
  236. package/dist/providers/executor/build.js +15 -0
  237. package/dist/providers/executor/composition.d.ts +14 -0
  238. package/dist/providers/executor/composition.js +123 -0
  239. package/dist/providers/executor/container.d.ts +184 -0
  240. package/dist/providers/executor/container.js +936 -0
  241. package/dist/providers/executor/dbos-status.d.ts +50 -0
  242. package/dist/providers/executor/dbos-status.js +64 -0
  243. package/dist/providers/executor/dbos.d.ts +53 -0
  244. package/dist/providers/executor/dbos.js +269 -0
  245. package/dist/providers/executor/in-process.d.ts +18 -0
  246. package/dist/providers/executor/in-process.js +38 -0
  247. package/dist/providers/executor/index.d.ts +8 -0
  248. package/dist/providers/executor/index.js +8 -0
  249. package/dist/providers/executor/routing.d.ts +48 -0
  250. package/dist/providers/executor/routing.js +70 -0
  251. package/dist/providers/executor/types.d.ts +55 -0
  252. package/dist/providers/executor/types.js +1 -0
  253. package/dist/providers/index.d.ts +43 -0
  254. package/dist/providers/index.js +18 -0
  255. package/dist/providers/jobs/docker-isolation.d.ts +166 -0
  256. package/dist/providers/jobs/docker-isolation.js +724 -0
  257. package/dist/providers/jobs/docker.d.ts +127 -0
  258. package/dist/providers/jobs/docker.js +1027 -0
  259. package/dist/providers/jobs/fake-kubernetes-api.d.ts +98 -0
  260. package/dist/providers/jobs/fake-kubernetes-api.js +142 -0
  261. package/dist/providers/jobs/fake.d.ts +23 -0
  262. package/dist/providers/jobs/fake.js +132 -0
  263. package/dist/providers/jobs/kubernetes-api.d.ts +60 -0
  264. package/dist/providers/jobs/kubernetes-api.js +20 -0
  265. package/dist/providers/jobs/kubernetes-client.d.ts +36 -0
  266. package/dist/providers/jobs/kubernetes-client.js +171 -0
  267. package/dist/providers/jobs/kubernetes-dry-run-fixture.d.ts +49 -0
  268. package/dist/providers/jobs/kubernetes-dry-run-fixture.js +141 -0
  269. package/dist/providers/jobs/kubernetes-isolation.d.ts +147 -0
  270. package/dist/providers/jobs/kubernetes-isolation.js +607 -0
  271. package/dist/providers/jobs/kubernetes-platform.d.ts +177 -0
  272. package/dist/providers/jobs/kubernetes-platform.js +280 -0
  273. package/dist/providers/jobs/kubernetes-preflight.d.ts +59 -0
  274. package/dist/providers/jobs/kubernetes-preflight.js +339 -0
  275. package/dist/providers/jobs/kubernetes-witness.d.ts +10 -0
  276. package/dist/providers/jobs/kubernetes-witness.js +193 -0
  277. package/dist/providers/jobs/kubernetes.d.ts +127 -0
  278. package/dist/providers/jobs/kubernetes.js +874 -0
  279. package/dist/providers/jobs/safe-extract.d.ts +9 -0
  280. package/dist/providers/jobs/safe-extract.js +325 -0
  281. package/dist/providers/jobs/types.d.ts +78 -0
  282. package/dist/providers/jobs/types.js +6 -0
  283. package/dist/providers/jobs/workspace-swap.d.ts +2 -0
  284. package/dist/providers/jobs/workspace-swap.js +47 -0
  285. package/dist/providers/llm/anthropic.d.ts +13 -0
  286. package/dist/providers/llm/anthropic.js +21 -0
  287. package/dist/providers/llm/bedrock.d.ts +19 -0
  288. package/dist/providers/llm/bedrock.js +33 -0
  289. package/dist/providers/llm/claude-messages.d.ts +106 -0
  290. package/dist/providers/llm/claude-messages.js +157 -0
  291. package/dist/providers/llm/claude-provider.d.ts +40 -0
  292. package/dist/providers/llm/claude-provider.js +31 -0
  293. package/dist/providers/llm/index.d.ts +10 -0
  294. package/dist/providers/llm/index.js +10 -0
  295. package/dist/providers/llm/openai.d.ts +23 -0
  296. package/dist/providers/llm/openai.js +156 -0
  297. package/dist/providers/llm/pricing-anthropic.d.ts +4 -0
  298. package/dist/providers/llm/pricing-anthropic.js +30 -0
  299. package/dist/providers/llm/pricing-bedrock-claude.d.ts +12 -0
  300. package/dist/providers/llm/pricing-bedrock-claude.js +37 -0
  301. package/dist/providers/llm/pricing-core.d.ts +26 -0
  302. package/dist/providers/llm/pricing-core.js +17 -0
  303. package/dist/providers/llm/pricing.d.ts +30 -0
  304. package/dist/providers/llm/pricing.js +74 -0
  305. package/dist/providers/llm/registration.d.ts +8 -0
  306. package/dist/providers/llm/registration.js +39 -0
  307. package/dist/providers/llm/routing.d.ts +25 -0
  308. package/dist/providers/llm/routing.js +32 -0
  309. package/dist/providers/llm/types.d.ts +85 -0
  310. package/dist/providers/llm/types.js +12 -0
  311. package/dist/providers/memory/index.d.ts +2 -0
  312. package/dist/providers/memory/index.js +2 -0
  313. package/dist/providers/memory/postgres.d.ts +20 -0
  314. package/dist/providers/memory/postgres.js +72 -0
  315. package/dist/providers/memory/types.d.ts +32 -0
  316. package/dist/providers/memory/types.js +17 -0
  317. package/dist/providers/secrets/app-key.d.ts +8 -0
  318. package/dist/providers/secrets/app-key.js +50 -0
  319. package/dist/providers/secrets/index.d.ts +10 -0
  320. package/dist/providers/secrets/index.js +13 -0
  321. package/dist/providers/secrets/transfer-envelope.d.ts +12 -0
  322. package/dist/providers/secrets/transfer-envelope.js +35 -0
  323. package/dist/providers/secrets/types.d.ts +18 -0
  324. package/dist/providers/secrets/types.js +12 -0
  325. package/dist/providers/storage/types.d.ts +18 -0
  326. package/dist/providers/storage/types.js +7 -0
  327. package/dist/providers/vcs/git.d.ts +105 -0
  328. package/dist/providers/vcs/git.js +710 -0
  329. package/dist/providers/vcs/github.d.ts +105 -0
  330. package/dist/providers/vcs/github.js +394 -0
  331. package/dist/providers/vcs/index.d.ts +6 -0
  332. package/dist/providers/vcs/index.js +26 -0
  333. package/dist/providers/vcs/types.d.ts +118 -0
  334. package/dist/providers/vcs/types.js +1 -0
  335. package/dist/sandbox/bounded-json.d.ts +2 -0
  336. package/dist/sandbox/bounded-json.js +36 -0
  337. package/dist/sandbox/bridge.d.ts +15 -0
  338. package/dist/sandbox/bridge.js +78 -0
  339. package/dist/sandbox/eval-core.d.ts +37 -0
  340. package/dist/sandbox/eval-core.js +159 -0
  341. package/dist/sandbox/fetch-policy.d.ts +24 -0
  342. package/dist/sandbox/fetch-policy.js +96 -0
  343. package/dist/sandbox/generated/zod-to-json-schema.bundle.js +5 -0
  344. package/dist/sandbox/generated/zod.bundle.js +1 -0
  345. package/dist/sandbox/host-functions.d.ts +29 -0
  346. package/dist/sandbox/host-functions.js +168 -0
  347. package/dist/sandbox/limits.d.ts +39 -0
  348. package/dist/sandbox/limits.js +39 -0
  349. package/dist/sandbox/parser-worker/__fixtures__/crash.d.ts +1 -0
  350. package/dist/sandbox/parser-worker/__fixtures__/crash.js +6 -0
  351. package/dist/sandbox/parser-worker/__fixtures__/echo.d.ts +1 -0
  352. package/dist/sandbox/parser-worker/__fixtures__/echo.js +5 -0
  353. package/dist/sandbox/parser-worker/__fixtures__/reject.d.ts +1 -0
  354. package/dist/sandbox/parser-worker/__fixtures__/reject.js +5 -0
  355. package/dist/sandbox/parser-worker/__fixtures__/spin-forever.d.ts +1 -0
  356. package/dist/sandbox/parser-worker/__fixtures__/spin-forever.js +11 -0
  357. package/dist/sandbox/parser-worker/pool.d.ts +13 -0
  358. package/dist/sandbox/parser-worker/pool.js +111 -0
  359. package/dist/sandbox/parser-worker/worker.d.ts +14 -0
  360. package/dist/sandbox/parser-worker/worker.js +63 -0
  361. package/dist/sandbox/pii-redaction.d.ts +1 -0
  362. package/dist/sandbox/pii-redaction.js +29 -0
  363. package/dist/sandbox/prelude.d.ts +26 -0
  364. package/dist/sandbox/prelude.js +408 -0
  365. package/dist/sandbox/quickjs-module.d.ts +13 -0
  366. package/dist/sandbox/quickjs-module.js +19 -0
  367. package/dist/sandbox/run-in-sandbox.d.ts +34 -0
  368. package/dist/sandbox/run-in-sandbox.js +53 -0
  369. package/dist/sandbox/safe-fetch.d.ts +26 -0
  370. package/dist/sandbox/safe-fetch.js +140 -0
  371. package/dist/sandbox/tool-capabilities.d.ts +66 -0
  372. package/dist/sandbox/tool-capabilities.js +103 -0
  373. package/dist/sandbox/zod-params.d.ts +22 -0
  374. package/dist/sandbox/zod-params.js +60 -0
  375. package/dist/serve.d.ts +14 -0
  376. package/dist/serve.js +54 -0
  377. package/dist/wardby-bin.d.ts +2 -0
  378. package/dist/wardby-bin.js +20 -0
  379. package/package.json +123 -0
  380. package/prisma/migrations/20260905000000_init/migration.sql +34 -0
  381. package/prisma/migrations/20260905010000_scheduler_durability/migration.sql +31 -0
  382. package/prisma/migrations/20260905020000_tools_multiturn/migration.sql +47 -0
  383. package/prisma/migrations/20260905030000_cache_tool_json_schema/migration.sql +13 -0
  384. package/prisma/migrations/20260906000000_mcp_phase4/migration.sql +115 -0
  385. package/prisma/migrations/20260906010000_run_final_text/migration.sql +7 -0
  386. package/prisma/migrations/20260906020000_run_turns/migration.sql +6 -0
  387. package/prisma/migrations/20260906030000_task_principal_id/migration.sql +5 -0
  388. package/prisma/migrations/20260906040000_secure_self_hosted_oauth/migration.sql +67 -0
  389. package/prisma/migrations/20260906050000_phase5_coding_agents/migration.sql +52 -0
  390. package/prisma/migrations/20260906060000_tool_capability_scoping/migration.sql +22 -0
  391. package/prisma/migrations/20260906070000_datastore_pii_flag/migration.sql +6 -0
  392. package/prisma/migrations/20260906080000_secret_elicitation_outcomes/migration.sql +13 -0
  393. package/prisma/migrations/20260907010000_coding_proxy_ledger/migration.sql +57 -0
  394. package/prisma/migrations/20260907020000_budget_groups/migration.sql +24 -0
  395. package/prisma/migrations/20260907030000_coding_webhook_task_override/migration.sql +4 -0
  396. package/prisma/migrations/20260908010000_run_execution_backend/migration.sql +2 -0
  397. package/prisma/migrations/20260908020000_coding_run_audit_metadata/migration.sql +7 -0
  398. package/prisma/migrations/20260910010000_agent_secret_bound_name/migration.sql +16 -0
  399. package/prisma/migrations/20260911010000_coding_worker_toolchains/migration.sql +5 -0
  400. package/prisma/migrations/20260912010000_coding_provider_contract/migration.sql +24 -0
  401. package/prisma/migrations/20260912020000_coding_proxy_protocol/migration.sql +10 -0
  402. package/prisma/migrations/20260912030000_agent_memory/migration.sql +18 -0
  403. package/prisma/migrations/20260912040000_named_shared_datastores/migration.sql +68 -0
  404. package/prisma/migrations/20260913000000_run_trigger_webhook/migration.sql +2 -0
  405. package/prisma/migrations/20260913010000_agent_sub_agent/migration.sql +32 -0
  406. package/prisma/migrations/20260913020000_run_task_override/migration.sql +5 -0
  407. package/prisma/migrations/20260913030000_coding_run_root/migration.sql +11 -0
  408. package/prisma/migrations/20260922010000_coding_run_queued_at/migration.sql +8 -0
  409. package/prisma/migrations/20260922020000_coding_workspace_disk/migration.sql +8 -0
  410. package/prisma/migrations/migration_lock.toml +3 -0
  411. package/prisma/schema.prisma +723 -0
  412. package/scripts/git-askpass.sh +11 -0
@@ -0,0 +1,52 @@
1
+ import { createSecret } from "../../core/secrets.js";
2
+ const ELICITATION_TTL_MS = 600_000;
3
+ /** Sweeps every expired entry, not just the one being looked up, so a set-and-never-polled-again entry doesn't linger forever. */
4
+ async function pruneExpired(db, now) {
5
+ await db.secretElicitationOutcome.deleteMany({ where: { expiresAt: { lte: now } } });
6
+ }
7
+ export async function getSecretElicitationOutcome(ownerId, secretName, db) {
8
+ const now = new Date();
9
+ await pruneExpired(db, now);
10
+ const row = await db.secretElicitationOutcome.findUnique({ where: { ownerId_secretName: { ownerId, secretName } } });
11
+ return row?.outcome;
12
+ }
13
+ /**
14
+ * Called by the browser form's POST handler. Idempotent — a resubmit for
15
+ * the same (ownerId, secretName) that's already fulfilled returns the
16
+ * recorded outcome rather than writing again.
17
+ */
18
+ export async function fulfillSecretElicitation(payload, value, cipher, db) {
19
+ const now = new Date();
20
+ await pruneExpired(db, now);
21
+ const { ownerId, secretName } = payload;
22
+ const existing = await db.secretElicitationOutcome.findUnique({
23
+ where: { ownerId_secretName: { ownerId, secretName } },
24
+ });
25
+ if (existing)
26
+ return existing.outcome;
27
+ let outcome;
28
+ try {
29
+ const secret = await createSecret(secretName, value, ownerId, cipher, db);
30
+ outcome = {
31
+ ok: true,
32
+ secret: {
33
+ id: secret.id,
34
+ name: secret.name,
35
+ keyId: secret.keyId,
36
+ ownerId: secret.ownerId,
37
+ createdAt: secret.createdAt,
38
+ updatedAt: secret.updatedAt,
39
+ },
40
+ };
41
+ }
42
+ catch (err) {
43
+ outcome = { ok: false, error: err instanceof Error ? err.message : String(err) };
44
+ }
45
+ const expiresAt = new Date(now.getTime() + ELICITATION_TTL_MS);
46
+ await db.secretElicitationOutcome.upsert({
47
+ where: { ownerId_secretName: { ownerId, secretName } },
48
+ create: { ownerId, secretName, outcome, expiresAt },
49
+ update: { outcome, expiresAt },
50
+ });
51
+ return outcome;
52
+ }
@@ -0,0 +1,13 @@
1
+ import type { WardbyMcpServer } from "../server.js";
2
+ /** Builds the browser-form URL for a minted token — stdio's ephemeral loopback server or the HTTP transport's mounted route (see mcp/index.ts and streamable-http.ts). */
3
+ export type SecretElicitationUrlBuilder = (token: string) => Promise<string>;
4
+ export interface SecretsToolsOptions {
5
+ buildElicitationUrl: SecretElicitationUrlBuilder;
6
+ /**
7
+ * Use the MCP protocol's own URL-mode elicitation (InputRequiredResult)
8
+ * instead of the plain-text "here's a link, call me again" fallback.
9
+ * See McpConfig.secretElicitationProtocol for why this defaults off.
10
+ */
11
+ protocolElicitation: boolean;
12
+ }
13
+ export declare function registerSecretsTools(mcp: WardbyMcpServer, opts: SecretsToolsOptions): void;
@@ -0,0 +1,124 @@
1
+ /**
2
+ * MCP secrets tools — thin wrappers over core/secrets.ts. The write-only /
3
+ * never-returned invariants are enforced there (listSecrets already omits
4
+ * value/ciphertext at the source); this file adds ownership on top.
5
+ */
6
+ import { inputRequired, inputResponse } from "@modelcontextprotocol/server";
7
+ import { createSecret, listSecrets, attachSecret, detachSecret, deleteSecret } from "../../core/secrets.js";
8
+ import { McpError } from "../errors.js";
9
+ import { requireOwnedAgent, requireOwnedSecret } from "../auth/ownership.js";
10
+ import { getSecretElicitationOutcome } from "./secret-elicitation.js";
11
+ import { textResult } from "./text-result.js";
12
+ export function registerSecretsTools(mcp, opts) {
13
+ mcp.registerTool({
14
+ name: "create_secret",
15
+ scope: "secrets:write",
16
+ description: 'Creates a secret. Omit "value" to enter it securely via a one-time browser link instead of passing it as a plaintext argument.',
17
+ inputSchema: {
18
+ type: "object",
19
+ properties: { name: { type: "string" }, value: { type: "string" } },
20
+ required: ["name"],
21
+ },
22
+ handler: async (args, ctx) => {
23
+ if (args.value !== undefined) {
24
+ const secret = await createSecret(args.name, args.value, ctx.principal.id, ctx.providers.secrets, ctx.db);
25
+ return textResult({ id: secret.id, name: secret.name, keyId: secret.keyId, createdAt: secret.createdAt });
26
+ }
27
+ if (opts.protocolElicitation) {
28
+ const state = ctx.mcpReq.requestState();
29
+ if (state) {
30
+ const view = inputResponse(ctx.mcpReq.inputResponses, "secretValue");
31
+ if (view.kind === "elicit" && view.action !== "accept") {
32
+ throw new McpError(400, `Secret entry was ${view.action}d.`);
33
+ }
34
+ // Not yet submitted in the browser falls through to the shared
35
+ // outcome check below, then re-mints so the client's retry/cancel
36
+ // controls apply to the fresh elicitation.
37
+ }
38
+ }
39
+ const existingOutcome = await getSecretElicitationOutcome(ctx.principal.id, args.name, ctx.db);
40
+ if (existingOutcome) {
41
+ if (!existingOutcome.ok)
42
+ throw new McpError(400, existingOutcome.error);
43
+ return textResult(existingOutcome.secret);
44
+ }
45
+ const payload = { ownerId: ctx.principal.id, secretName: args.name };
46
+ const token = await mcp.mintRequestState(payload);
47
+ const url = await opts.buildElicitationUrl(token);
48
+ if (opts.protocolElicitation) {
49
+ return inputRequired({
50
+ requestState: token,
51
+ inputRequests: {
52
+ secretValue: inputRequired.elicitUrl({
53
+ url,
54
+ message: `Enter the value for secret "${args.name}" in your browser.`,
55
+ }),
56
+ },
57
+ });
58
+ }
59
+ // Polling fallback: no MCP protocol feature required, so it works
60
+ // regardless of what the connecting client declares support for.
61
+ return textResult({
62
+ status: "pending",
63
+ message: `Open this link in your browser to enter the value for secret "${args.name}", then call create_secret again with the same name to finish.`,
64
+ url,
65
+ });
66
+ },
67
+ });
68
+ mcp.registerTool({
69
+ name: "list_secrets",
70
+ scope: "secrets:write",
71
+ inputSchema: { type: "object", properties: {} },
72
+ handler: async (_args, ctx) => {
73
+ const secrets = await listSecrets(ctx.principal.id, ctx.db);
74
+ return textResult(secrets);
75
+ },
76
+ });
77
+ mcp.registerTool({
78
+ name: "attach_secret",
79
+ scope: "secrets:write",
80
+ inputSchema: {
81
+ type: "object",
82
+ properties: {
83
+ agentId: { type: "string" },
84
+ name: { type: "string" },
85
+ alias: { type: "string" },
86
+ },
87
+ required: ["agentId", "name"],
88
+ },
89
+ handler: async (args, ctx) => {
90
+ await requireOwnedAgent(ctx.db, args.agentId, ctx.principal.id);
91
+ try {
92
+ await attachSecret(args.agentId, args.name, ctx.principal.id, ctx.db, args.alias ?? args.name);
93
+ }
94
+ catch (err) {
95
+ throw new McpError(404, err instanceof Error ? err.message : String(err));
96
+ }
97
+ return textResult({ attached: true });
98
+ },
99
+ });
100
+ mcp.registerTool({
101
+ name: "detach_secret",
102
+ scope: "secrets:write",
103
+ inputSchema: {
104
+ type: "object",
105
+ properties: { agentId: { type: "string" }, name: { type: "string" } },
106
+ required: ["agentId", "name"],
107
+ },
108
+ handler: async (args, ctx) => {
109
+ await requireOwnedAgent(ctx.db, args.agentId, ctx.principal.id);
110
+ await detachSecret(args.agentId, args.name, ctx.db);
111
+ return textResult({ detached: true });
112
+ },
113
+ });
114
+ mcp.registerTool({
115
+ name: "delete_secret",
116
+ scope: "secrets:write",
117
+ inputSchema: { type: "object", properties: { id: { type: "string" } }, required: ["id"] },
118
+ handler: async (args, ctx) => {
119
+ await requireOwnedSecret(ctx.db, args.id, ctx.principal.id);
120
+ await deleteSecret(args.id, ctx.db);
121
+ return textResult({ deleted: args.id });
122
+ },
123
+ });
124
+ }
@@ -0,0 +1,2 @@
1
+ import type { WardbyMcpServer } from "../server.js";
2
+ export declare function registerSubAgentTools(mcp: WardbyMcpServer): void;
@@ -0,0 +1,133 @@
1
+ /**
2
+ * AgentSubAgent CRUD — agent-composition edges. Attach/detach require
3
+ * ownership of both the parent and child agent (same convention as
4
+ * attach_tool requiring ownership of both the agent and the tool), and
5
+ * attach rejects any edge that would create a cycle in the parent/child
6
+ * graph (§7 of the design doc) — cycles are not something Postgres can
7
+ * enforce natively.
8
+ */
9
+ import { Prisma } from "@prisma/client";
10
+ import { requireOwnedAgent } from "../auth/ownership.js";
11
+ import { McpError } from "../errors.js";
12
+ import { textResult } from "./text-result.js";
13
+ /**
14
+ * True if `fromAgentId` can already reach `toAgentId` by following existing
15
+ * parent -> child edges. Attaching a new edge parentAgentId -> childAgentId
16
+ * is only safe when childAgentId cannot already reach parentAgentId --
17
+ * otherwise the new edge would close a cycle. Loads the whole edge set and
18
+ * walks it in JS rather than a recursive SQL query: the edge count is
19
+ * expected to stay small (agent-composition graphs, not a general graph
20
+ * store), and this keeps the check readable and easy to unit test.
21
+ */
22
+ async function canReach(db, fromAgentId, toAgentId) {
23
+ const edges = await db.agentSubAgent.findMany({ select: { parentAgentId: true, childAgentId: true } });
24
+ const childrenOf = new Map();
25
+ for (const edge of edges) {
26
+ const list = childrenOf.get(edge.parentAgentId) ?? [];
27
+ list.push(edge.childAgentId);
28
+ childrenOf.set(edge.parentAgentId, list);
29
+ }
30
+ const seen = new Set([fromAgentId]);
31
+ const stack = [fromAgentId];
32
+ while (stack.length > 0) {
33
+ const current = stack.pop();
34
+ if (current === toAgentId)
35
+ return true;
36
+ for (const next of childrenOf.get(current) ?? []) {
37
+ if (!seen.has(next)) {
38
+ seen.add(next);
39
+ stack.push(next);
40
+ }
41
+ }
42
+ }
43
+ return false;
44
+ }
45
+ export function registerSubAgentTools(mcp) {
46
+ mcp.registerTool({
47
+ name: "attach_subagent",
48
+ scope: "agents:write",
49
+ inputSchema: {
50
+ type: "object",
51
+ additionalProperties: false,
52
+ properties: {
53
+ parentAgentId: { type: "string" },
54
+ childAgentId: { type: "string" },
55
+ boundName: { type: "string", minLength: 1 },
56
+ },
57
+ required: ["parentAgentId", "childAgentId"],
58
+ },
59
+ handler: async (args, ctx) => {
60
+ if (args.parentAgentId === args.childAgentId) {
61
+ throw new McpError(400, "An agent cannot be its own sub-agent.");
62
+ }
63
+ const parent = await requireOwnedAgent(ctx.db, args.parentAgentId, ctx.principal.id);
64
+ const child = await requireOwnedAgent(ctx.db, args.childAgentId, ctx.principal.id);
65
+ if (await canReach(ctx.db, args.childAgentId, args.parentAgentId)) {
66
+ throw new McpError(400, `Attaching "${child.name}" as a sub-agent of "${parent.name}" would create a cycle.`);
67
+ }
68
+ const boundName = args.boundName ?? child.name;
69
+ try {
70
+ await ctx.db.agentSubAgent.upsert({
71
+ where: { parentAgentId_childAgentId: { parentAgentId: args.parentAgentId, childAgentId: args.childAgentId } },
72
+ create: { parentAgentId: args.parentAgentId, childAgentId: args.childAgentId, boundName },
73
+ update: { boundName },
74
+ });
75
+ }
76
+ catch (err) {
77
+ if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === "P2002") {
78
+ throw new McpError(409, `"${parent.name}" already has a different sub-agent bound to name "${boundName}".`);
79
+ }
80
+ throw err;
81
+ }
82
+ return textResult({ attached: true, boundName });
83
+ },
84
+ });
85
+ mcp.registerTool({
86
+ name: "detach_subagent",
87
+ scope: "agents:write",
88
+ inputSchema: {
89
+ type: "object",
90
+ additionalProperties: false,
91
+ properties: {
92
+ parentAgentId: { type: "string" },
93
+ childAgentId: { type: "string" },
94
+ },
95
+ required: ["parentAgentId", "childAgentId"],
96
+ },
97
+ handler: async (args, ctx) => {
98
+ await requireOwnedAgent(ctx.db, args.parentAgentId, ctx.principal.id);
99
+ await requireOwnedAgent(ctx.db, args.childAgentId, ctx.principal.id);
100
+ await ctx.db.agentSubAgent.deleteMany({
101
+ where: { parentAgentId: args.parentAgentId, childAgentId: args.childAgentId },
102
+ });
103
+ return textResult({ detached: true });
104
+ },
105
+ });
106
+ mcp.registerTool({
107
+ name: "list_subagents",
108
+ scope: "agents:read",
109
+ inputSchema: {
110
+ type: "object",
111
+ additionalProperties: false,
112
+ properties: { agentId: { type: "string" } },
113
+ required: ["agentId"],
114
+ },
115
+ handler: async (args, ctx) => {
116
+ await requireOwnedAgent(ctx.db, args.agentId, ctx.principal.id);
117
+ const [asParent, asChild] = await Promise.all([
118
+ ctx.db.agentSubAgent.findMany({
119
+ where: { parentAgentId: args.agentId },
120
+ include: { child: { select: { id: true, name: true } } },
121
+ }),
122
+ ctx.db.agentSubAgent.findMany({
123
+ where: { childAgentId: args.agentId },
124
+ include: { parent: { select: { id: true, name: true } } },
125
+ }),
126
+ ]);
127
+ return textResult({
128
+ children: asParent.map((e) => ({ boundName: e.boundName, agentId: e.child.id, agentName: e.child.name })),
129
+ parents: asChild.map((e) => ({ boundName: e.boundName, agentId: e.parent.id, agentName: e.parent.name })),
130
+ });
131
+ },
132
+ });
133
+ }
@@ -0,0 +1,6 @@
1
+ export declare function textResult(value: unknown): {
2
+ content: {
3
+ type: "text";
4
+ text: string;
5
+ }[];
6
+ };
@@ -0,0 +1,3 @@
1
+ export function textResult(value) {
2
+ return { content: [{ type: "text", text: JSON.stringify(value) }] };
3
+ }
@@ -0,0 +1,2 @@
1
+ import type { WardbyMcpServer } from "../server.js";
2
+ export declare function registerToolAuthoringTools(mcp: WardbyMcpServer): void;
@@ -0,0 +1,211 @@
1
+ /**
2
+ * Tool authoring — raw path only (Amendment A: the guided Elicitation/Tasks
3
+ * flow is deferred; this ships create_tool + dry_run_tool as the
4
+ * test-before-commit loop instead). A compile/validation failure is a
5
+ * NORMAL tool result carrying `{ok:false, errorKind, errorMessage}` — not
6
+ * an isError — since the call itself succeeded (compilation was attempted
7
+ * and correctly reported a problem the caller can fix and retry), matching
8
+ * runInSandbox's own SandboxResult convention. Ownership/scope failures
9
+ * (attach/detach on an agent or tool you don't own) DO throw, same
10
+ * convention as every other tool in this codebase.
11
+ */
12
+ import { Prisma } from "@prisma/client";
13
+ import { deriveJsonSchema, validateParams } from "../../sandbox/zod-params.js";
14
+ import { runInSandbox } from "../../sandbox/run-in-sandbox.js";
15
+ import { ToolCapabilitiesPatchSchema } from "../../sandbox/tool-capabilities.js";
16
+ import { buildSharedDatastoreAccessor } from "../../core/datastores.js";
17
+ import { MEMORY_TOOL_NAMES } from "../../core/memory-tools.js";
18
+ import { McpError } from "../errors.js";
19
+ import { assertCanMutate, requireOwnedAgent, requireOwnedTool, requireReadableAgent, visibleToPrincipal, canRead, } from "../auth/ownership.js";
20
+ import { textResult } from "./text-result.js";
21
+ export function registerToolAuthoringTools(mcp) {
22
+ mcp.registerTool({
23
+ name: "create_tool",
24
+ scope: "tools:write",
25
+ inputSchema: {
26
+ type: "object",
27
+ properties: {
28
+ name: { type: "string" },
29
+ description: { type: "string" },
30
+ paramsZod: { type: "string" },
31
+ code: { type: "string" },
32
+ },
33
+ required: ["name", "description", "paramsZod", "code"],
34
+ },
35
+ 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
+ }
39
+ const schemaResult = await deriveJsonSchema(args.paramsZod);
40
+ if (!schemaResult.ok) {
41
+ return textResult({ ok: false, errorKind: schemaResult.errorKind, errorMessage: schemaResult.errorMessage });
42
+ }
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);
54
+ },
55
+ });
56
+ mcp.registerTool({
57
+ name: "dry_run_tool",
58
+ scope: "tools:write",
59
+ description: "Test-runs tool code against sample args without persisting it. Fetch is denied by default, same as a freshly attached tool — pass allowedHosts to test code that calls fetch().",
60
+ inputSchema: {
61
+ type: "object",
62
+ properties: {
63
+ paramsZod: { type: "string" },
64
+ code: { type: "string" },
65
+ sampleArgs: { type: "object" },
66
+ allowedHosts: { type: "array", items: { type: "string" } },
67
+ },
68
+ required: ["paramsZod", "code", "sampleArgs"],
69
+ },
70
+ handler: async (args, ctx) => {
71
+ const hosts = ToolCapabilitiesPatchSchema.pick({ allowedHosts: true }).safeParse({
72
+ allowedHosts: args.allowedHosts,
73
+ });
74
+ if (!hosts.success) {
75
+ throw new McpError(400, `Invalid allowedHosts: ${hosts.error.issues.map((i) => i.message).join("; ")}`);
76
+ }
77
+ const schemaResult = await deriveJsonSchema(args.paramsZod);
78
+ if (!schemaResult.ok) {
79
+ return textResult({ ok: false, errorKind: schemaResult.errorKind, errorMessage: schemaResult.errorMessage });
80
+ }
81
+ const jsonSchema = schemaResult.value;
82
+ const validated = await validateParams(args.paramsZod, args.sampleArgs);
83
+ if (!validated.ok) {
84
+ return textResult({
85
+ jsonSchema,
86
+ result: { ok: false, errorKind: validated.errorKind, errorMessage: validated.errorMessage },
87
+ });
88
+ }
89
+ // No real agent exists for a dry run — the caller's own principal
90
+ // scopes the sandbox's datastore access, keeping dry-run reads/writes
91
+ // isolated per-caller rather than colliding across authors. Fetch is
92
+ // deny-by-default here too — same posture as a freshly attached tool
93
+ // (OWASP LLM08) — rather than the unconditional wildcard this used to
94
+ // pass; a caller testing fetch-dependent code must declare the hosts
95
+ // it needs, same as at attach_tool time. sharedDatastore is likewise
96
+ // unscoped here: there's no tool attachment to carry
97
+ // allowedSharedDatastorePrefixes for a dry run, so it resolves exactly
98
+ // like a real agent with no shared-datastore attachments at all
99
+ // (empty reads, throwing writes) — never a capability-scoping bypass.
100
+ const sandboxResult = await runInSandbox({
101
+ code: args.code,
102
+ params: validated.value,
103
+ agentId: ctx.principal.id,
104
+ datastore: ctx.providers.datastore,
105
+ sharedDatastore: buildSharedDatastoreAccessor(ctx.principal.id, ctx.providers.datastore, ctx.db),
106
+ toolName: "dry_run_tool",
107
+ allowedFetchHosts: hosts.data.allowedHosts ?? [],
108
+ });
109
+ return textResult({ jsonSchema, result: sandboxResult });
110
+ },
111
+ });
112
+ mcp.registerTool({
113
+ name: "attach_tool",
114
+ scope: "tools:write",
115
+ inputSchema: {
116
+ type: "object",
117
+ properties: {
118
+ agentId: { type: "string" },
119
+ toolId: { type: "string" },
120
+ allowedSecrets: { type: "array", items: { type: "string" } },
121
+ allowedDatastorePrefixes: { type: "array", items: { type: "string" } },
122
+ allowedHosts: { type: "array", items: { type: "string" } },
123
+ // { [boundName]: allowedKeyPrefixes[] } — per shared-datastore
124
+ // attachment, the same default-deny grant as allowedDatastorePrefixes
125
+ // is for the agent's private store. A boundName absent from this map
126
+ // denies that shared store entirely.
127
+ allowedSharedDatastorePrefixes: {
128
+ type: "object",
129
+ additionalProperties: { type: "array", items: { type: "string" } },
130
+ },
131
+ },
132
+ required: ["agentId", "toolId"],
133
+ },
134
+ handler: async (args, ctx) => {
135
+ const patch = ToolCapabilitiesPatchSchema.safeParse({
136
+ allowedSecrets: args.allowedSecrets,
137
+ allowedDatastorePrefixes: args.allowedDatastorePrefixes,
138
+ allowedHosts: args.allowedHosts,
139
+ allowedSharedDatastorePrefixes: args.allowedSharedDatastorePrefixes,
140
+ });
141
+ if (!patch.success) {
142
+ throw new McpError(400, `Invalid tool capabilities: ${patch.error.issues.map((i) => i.message).join("; ")}`);
143
+ }
144
+ 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.`);
149
+ if (agent.kind === "coding") {
150
+ throw new McpError(400, "Native sandbox tools cannot be attached to coding agents.");
151
+ }
152
+ await requireOwnedTool(tx, args.toolId, ctx.principal.id);
153
+ await tx.agentTool.upsert({
154
+ where: { agentId_toolId: { agentId: args.agentId, toolId: args.toolId } },
155
+ create: {
156
+ agentId: args.agentId,
157
+ 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
+ : {}),
172
+ },
173
+ });
174
+ }, { isolationLevel: Prisma.TransactionIsolationLevel.Serializable });
175
+ return textResult({ attached: true });
176
+ },
177
+ });
178
+ mcp.registerTool({
179
+ name: "detach_tool",
180
+ scope: "tools:write",
181
+ inputSchema: {
182
+ type: "object",
183
+ properties: { agentId: { type: "string" }, toolId: { type: "string" } },
184
+ required: ["agentId", "toolId"],
185
+ },
186
+ handler: async (args, ctx) => {
187
+ await requireOwnedAgent(ctx.db, args.agentId, ctx.principal.id);
188
+ await requireOwnedTool(ctx.db, args.toolId, ctx.principal.id);
189
+ await ctx.db.agentTool.deleteMany({ where: { agentId: args.agentId, toolId: args.toolId } });
190
+ return textResult({ detached: true });
191
+ },
192
+ });
193
+ mcp.registerTool({
194
+ name: "list_tools",
195
+ scope: "tools:write",
196
+ inputSchema: { type: "object", properties: { agentId: { type: "string" } } },
197
+ handler: async (args, ctx) => {
198
+ const project = (tool) => tool.ownerId === ctx.principal.id ? tool : { id: tool.id, name: tool.name, description: tool.description };
199
+ if (args.agentId) {
200
+ await requireReadableAgent(ctx.db, args.agentId, ctx.principal.id);
201
+ const rows = await ctx.db.agentTool.findMany({ where: { agentId: args.agentId }, include: { tool: true } });
202
+ return textResult(rows
203
+ .map((r) => r.tool)
204
+ .filter((tool) => canRead(tool.ownerId, ctx.principal.id))
205
+ .map(project));
206
+ }
207
+ const tools = await ctx.db.tool.findMany({ where: visibleToPrincipal(ctx.principal.id) });
208
+ return textResult(tools.filter((tool) => canRead(tool.ownerId, ctx.principal.id)).map(project));
209
+ },
210
+ });
211
+ }
@@ -0,0 +1,2 @@
1
+ import type { WardbyMcpServer } from "../server.js";
2
+ export declare function registerTriggerTool(mcp: WardbyMcpServer): void;