@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,121 @@
1
+ /**
2
+ * The scheduler: a lease loop (only one process ticks at a time) and a tick
3
+ * loop (the holder claims due agents and hands them to the `Executor`).
4
+ * At-most-once firing is enforced by `claimDueRun`'s row lock + the
5
+ * `lastScheduledAt` advance happening in the same transaction as the `Run`
6
+ * create — never by the lease alone (the lease only prevents concurrent
7
+ * *ticking*, not a race within a single tick against a future one).
8
+ */
9
+ import { randomUUID } from "node:crypto";
10
+ import { dueWindow } from "./cron.js";
11
+ import { dispatchRun } from "./dispatch.js";
12
+ import { tryAcquireLease } from "./lease.js";
13
+ import { prisma as defaultDb } from "./db.js";
14
+ import { LEASE_RENEW_INTERVAL_MS, LEASE_TTL_MS, TICK_INTERVAL_MS } from "./timing.js";
15
+ import { logger } from "./logger.js";
16
+ const schedulerLog = logger.child({ module: "scheduler" });
17
+ /**
18
+ * Per spec: "Executor throws synchronously on start → the run is marked
19
+ * failed with the error." A guarded `updateMany` (only `pending`/`running`)
20
+ * rather than an unconditional `update`, so this can never clobber a run
21
+ * that already reached a terminal state through some other path.
22
+ */
23
+ export { markRunFailedFromExecutorError } from "./dispatch.js";
24
+ /** Cheap, lock-free pass: which enabled+scheduled agents look due right now. */
25
+ export async function findDueCandidates(db, now) {
26
+ const candidates = await db.agent.findMany({
27
+ where: { scheduleEnabled: true, schedule: { not: null } },
28
+ });
29
+ return candidates.filter((agent) => dueWindow({
30
+ schedule: agent.schedule,
31
+ timezone: agent.timezone,
32
+ lastScheduledAt: agent.lastScheduledAt,
33
+ now,
34
+ }) !== null);
35
+ }
36
+ /**
37
+ * Re-checks due-ness under `FOR UPDATE SKIP LOCKED` and, if still due,
38
+ * advances `lastScheduledAt` and creates the `Run` in the same transaction.
39
+ * Returns the new run's id, or null if the agent wasn't (or is no longer)
40
+ * due, or another tick already holds its row lock.
41
+ */
42
+ export async function claimDueRun(db, executor, agentId, now) {
43
+ const dispatched = await dispatchRun({
44
+ db,
45
+ executor,
46
+ agentId,
47
+ trigger: "scheduled",
48
+ now,
49
+ lockAgent: true,
50
+ beforePersist: async (tx, agent) => {
51
+ if (!agent.scheduleEnabled || !agent.schedule)
52
+ return false;
53
+ const window = dueWindow({
54
+ schedule: agent.schedule,
55
+ timezone: agent.timezone,
56
+ lastScheduledAt: agent.lastScheduledAt,
57
+ now,
58
+ });
59
+ if (!window)
60
+ return false;
61
+ await tx.agent.update({ where: { id: agentId }, data: { lastScheduledAt: window } });
62
+ return true;
63
+ },
64
+ });
65
+ return dispatched?.run.id ?? null;
66
+ }
67
+ export function startScheduler(options) {
68
+ const db = options.db ?? defaultDb;
69
+ const scope = options.scope ?? "default";
70
+ const instanceId = options.instanceId ?? randomUUID();
71
+ const now = options.now ?? (() => new Date());
72
+ const log = options.onLog ?? ((message) => schedulerLog.info(message));
73
+ let leader = false;
74
+ async function leaseTick() {
75
+ const acquired = await tryAcquireLease(db, scope, instanceId, LEASE_TTL_MS);
76
+ if (acquired && !leader) {
77
+ log(`acquired leadership for scope "${scope}" as ${instanceId}`);
78
+ }
79
+ else if (!acquired && leader) {
80
+ log(`lost leadership for scope "${scope}"`);
81
+ }
82
+ leader = acquired;
83
+ }
84
+ async function tick() {
85
+ if (!leader) {
86
+ return;
87
+ }
88
+ const due = await findDueCandidates(db, now());
89
+ for (const agent of due) {
90
+ try {
91
+ const runId = await claimDueRun(db, options.executor, agent.id, now());
92
+ if (runId) {
93
+ log(`firing agent "${agent.name}" -> run ${runId}`);
94
+ }
95
+ }
96
+ catch (err) {
97
+ schedulerLog.error({ err, agentName: agent.name }, "error claiming a due run");
98
+ }
99
+ }
100
+ if (options.onLeaderTick) {
101
+ await options.onLeaderTick().catch((err) => schedulerLog.error({ err }, "leader tick hook failed"));
102
+ }
103
+ }
104
+ const leaseTimer = setInterval(() => {
105
+ leaseTick().catch((err) => schedulerLog.error({ err }, "lease error"));
106
+ }, LEASE_RENEW_INTERVAL_MS);
107
+ const tickTimer = setInterval(() => {
108
+ tick().catch((err) => schedulerLog.error({ err }, "tick error"));
109
+ }, TICK_INTERVAL_MS);
110
+ // Acquire immediately rather than waiting a full renew interval to start.
111
+ leaseTick().catch((err) => schedulerLog.error({ err }, "lease error"));
112
+ return {
113
+ stop() {
114
+ clearInterval(leaseTimer);
115
+ clearInterval(tickTimer);
116
+ },
117
+ isLeader() {
118
+ return leader;
119
+ },
120
+ };
121
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Secrets: encrypted-at-rest values, attached to an agent by name, readable
3
+ * only inside the sandbox at point-of-use (never by any read/list tool).
4
+ * `Secret.name` is unique per owner (`@@unique([ownerId, name])`), so two
5
+ * owners can each have their own "API_KEY" without collision — attach/get
6
+ * always resolve a name within one owner's (or, at get-time, one agent's)
7
+ * scope, never globally.
8
+ */
9
+ import type { PrismaClient, Secret } from "@prisma/client";
10
+ import type { SecretCipher } from "../providers/secrets/types.js";
11
+ export type SecretMetadata = Pick<Secret, "id" | "name" | "keyId" | "ownerId" | "createdAt" | "updatedAt">;
12
+ export interface SecretsAccessor {
13
+ get(name: string): Promise<string | undefined>;
14
+ }
15
+ /**
16
+ * Encrypts and stores a secret value. Upserts on (ownerId, name) — calling
17
+ * this again for a name the owner already has rotates its value in place
18
+ * (same id) rather than failing on the unique constraint. The plaintext is
19
+ * never logged or returned.
20
+ */
21
+ export declare function createSecret(name: string, value: string, ownerId: string, cipher: SecretCipher, db: PrismaClient): Promise<Secret>;
22
+ /** Names/metadata only — never a value or ciphertext. */
23
+ export declare function listSecrets(ownerId: string, db: PrismaClient): Promise<SecretMetadata[]>;
24
+ /** Attaches one of the owner's own secrets (by canonical name) to an agent,
25
+ * under `boundName` — the point-of-use name the agent's tools resolve
26
+ * (`secrets.get(boundName)`). Defaults to the secret's own name. */
27
+ export declare function attachSecret(agentId: string, name: string, ownerId: string, db: PrismaClient, boundName?: string): Promise<void>;
28
+ /** Detaches by point-of-use name — the pair (agentId, boundName) uniquely
29
+ * identifies the edge; agent ownership is enforced by the caller. */
30
+ export declare function detachSecret(agentId: string, boundName: string, db: PrismaClient): Promise<void>;
31
+ export declare function deleteSecret(secretId: string, db: PrismaClient): Promise<void>;
32
+ /**
33
+ * Decrypt-on-demand: only the ONE secret actually requested by name is
34
+ * decrypted, never the agent's whole attached set eagerly — a lookup
35
+ * that returns nothing (unattached name) is indistinguishable from one
36
+ * that was never created, both `undefined`.
37
+ */
38
+ export declare function buildSecretsAccessor(agentId: string, cipher: SecretCipher, db: Pick<PrismaClient, "agentSecret"> & Partial<Pick<PrismaClient, "$queryRaw">>): SecretsAccessor;
39
+ /**
40
+ * Wraps a `SecretsAccessor` so `get()` only ever resolves a name the caller
41
+ * has declared this specific tool attachment may read — every other name
42
+ * behaves exactly like one that was never attached (`undefined`), never a
43
+ * throw, matching the existing "unattached name" convention. The underlying
44
+ * accessor is never even called for a disallowed name.
45
+ */
46
+ export declare function scopeSecretsAccessor(accessor: SecretsAccessor, allowedNames: readonly string[]): SecretsAccessor;
@@ -0,0 +1,100 @@
1
+ import { boundedString } from "../sandbox/bounded-json.js";
2
+ /**
3
+ * Encrypts and stores a secret value. Upserts on (ownerId, name) — calling
4
+ * this again for a name the owner already has rotates its value in place
5
+ * (same id) rather than failing on the unique constraint. The plaintext is
6
+ * never logged or returned.
7
+ */
8
+ export async function createSecret(name, value, ownerId, cipher, db) {
9
+ boundedString(name, 1024);
10
+ boundedString(value, 65_536);
11
+ const ciphertext = await cipher.encrypt(value);
12
+ const keyId = cipher.keyId();
13
+ return db.secret.upsert({
14
+ where: { ownerId_name: { ownerId, name } },
15
+ create: { name, ciphertext, keyId, ownerId },
16
+ update: { ciphertext, keyId },
17
+ });
18
+ }
19
+ /** Names/metadata only — never a value or ciphertext. */
20
+ export async function listSecrets(ownerId, db) {
21
+ const secrets = await db.secret.findMany({ where: { ownerId } });
22
+ return secrets.map(({ id, name, keyId, ownerId: owner, createdAt, updatedAt }) => ({
23
+ id,
24
+ name,
25
+ keyId,
26
+ ownerId: owner,
27
+ createdAt,
28
+ updatedAt,
29
+ }));
30
+ }
31
+ async function findOwnedSecretByName(db, ownerId, name) {
32
+ return db.secret.findUnique({ where: { ownerId_name: { ownerId, name } } });
33
+ }
34
+ /** Attaches one of the owner's own secrets (by canonical name) to an agent,
35
+ * under `boundName` — the point-of-use name the agent's tools resolve
36
+ * (`secrets.get(boundName)`). Defaults to the secret's own name. */
37
+ export async function attachSecret(agentId, name, ownerId, db, boundName = name) {
38
+ const secret = await findOwnedSecretByName(db, ownerId, name);
39
+ if (!secret)
40
+ throw new Error(`No secret named "${name}" owned by this caller.`);
41
+ await db.agentSecret.create({ data: { agentId, secretId: secret.id, boundName } });
42
+ }
43
+ /** Detaches by point-of-use name — the pair (agentId, boundName) uniquely
44
+ * identifies the edge; agent ownership is enforced by the caller. */
45
+ export async function detachSecret(agentId, boundName, db) {
46
+ await db.agentSecret.deleteMany({ where: { agentId, boundName } });
47
+ }
48
+ export async function deleteSecret(secretId, db) {
49
+ await db.secret.delete({ where: { id: secretId } });
50
+ }
51
+ /**
52
+ * Decrypt-on-demand: only the ONE secret actually requested by name is
53
+ * decrypted, never the agent's whole attached set eagerly — a lookup
54
+ * that returns nothing (unattached name) is indistinguishable from one
55
+ * that was never created, both `undefined`.
56
+ */
57
+ export function buildSecretsAccessor(agentId, cipher, db) {
58
+ return {
59
+ async get(name) {
60
+ boundedString(name, 1024);
61
+ if (db.$queryRaw) {
62
+ const rows = await db.$queryRaw `
63
+ SELECT CASE WHEN octet_length(s."ciphertext") <= 262144 THEN s."ciphertext" ELSE NULL END AS "ciphertext"
64
+ FROM "Secret" s JOIN "AgentSecret" a ON a."secretId" = s."id"
65
+ WHERE a."agentId" = ${agentId} AND a."boundName" = ${name} LIMIT 1`;
66
+ if (!rows.length)
67
+ return undefined;
68
+ if (rows[0].ciphertext === null)
69
+ throw new Error("secret_value_limit");
70
+ return cipher.decrypt(rows[0].ciphertext);
71
+ }
72
+ // Lightweight provider doubles use the same post-read bound; production Prisma filters in SQL.
73
+ const attachment = await db.agentSecret.findFirst({
74
+ where: { agentId, boundName: name },
75
+ include: { secret: true },
76
+ });
77
+ if (!attachment)
78
+ return undefined;
79
+ boundedString(attachment.secret.ciphertext, 256 * 1024);
80
+ return cipher.decrypt(attachment.secret.ciphertext);
81
+ },
82
+ };
83
+ }
84
+ /**
85
+ * Wraps a `SecretsAccessor` so `get()` only ever resolves a name the caller
86
+ * has declared this specific tool attachment may read — every other name
87
+ * behaves exactly like one that was never attached (`undefined`), never a
88
+ * throw, matching the existing "unattached name" convention. The underlying
89
+ * accessor is never even called for a disallowed name.
90
+ */
91
+ export function scopeSecretsAccessor(accessor, allowedNames) {
92
+ const allowed = new Set(allowedNames);
93
+ return {
94
+ async get(name) {
95
+ if (!allowed.has(name))
96
+ return undefined;
97
+ return accessor.get(name);
98
+ },
99
+ };
100
+ }
@@ -0,0 +1,18 @@
1
+ import type { PrismaClient } from "@prisma/client";
2
+ import type { LoadedTool } from "../providers/engine/types.js";
3
+ import type { AgentMemoryStore } from "../providers/memory/types.js";
4
+ export declare const SUBAGENT_MEMORY_TOOL_NAMES: ReadonlySet<string>;
5
+ export declare const SUBAGENT_MEMORY_GET_TOOL: LoadedTool;
6
+ export declare const PARENT_MEMORY_GET_TOOL: LoadedTool;
7
+ type SubAgentMemoryDb = Pick<PrismaClient, "agentSubAgent" | "run">;
8
+ /** `subagent_memory_get` — callable by a parent, gated by the AgentSubAgent edge itself. */
9
+ export declare function handleSubAgentMemoryGet(argsJson: string, callingAgentId: string, db: SubAgentMemoryDb, memory: AgentMemoryStore): Promise<string>;
10
+ /**
11
+ * `parent_memory_get` — callable by a dispatched child, gated by this exact
12
+ * run's ephemeral grant. `currentRunId` is the run making the call, not the
13
+ * parent — its own `parentRunId`/`grantedParentMemoryKeys` are looked up
14
+ * fresh rather than threaded through the caller, so this stays correct even
15
+ * if called from deep inside a multi-turn engine loop.
16
+ */
17
+ export declare function handleParentMemoryGet(argsJson: string, currentRunId: string, db: SubAgentMemoryDb, memory: AgentMemoryStore): Promise<string>;
18
+ export {};
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Cross-agent memory reads for sub-agent dispatch. Two deliberately
3
+ * asymmetric built-ins, recognized by name and never
4
+ * sandboxed, the same treatment memory-tools.ts gives the own-memory
5
+ * built-ins:
6
+ *
7
+ * - subagent_memory_get: a parent can always read a declared child's
8
+ * memory. The AgentSubAgent edge itself is the grant -- no reference
9
+ * needed each time.
10
+ * - parent_memory_get: a child can read a specific key of its dispatching
11
+ * parent's memory ONLY when that exact run was handed a grant for that
12
+ * key at dispatch time (Run.grantedParentMemoryKeys) -- never a standing
13
+ * allowlist, never the parent's memory in general.
14
+ *
15
+ * Both fail closed: a missing edge or an ungranted key is an error result,
16
+ * never a silent empty read (same discipline as scopeDatastore's shared
17
+ * methods).
18
+ */
19
+ import { z } from "zod";
20
+ export const SUBAGENT_MEMORY_TOOL_NAMES = new Set(["subagent_memory_get", "parent_memory_get"]);
21
+ export const SUBAGENT_MEMORY_GET_TOOL = {
22
+ name: "subagent_memory_get",
23
+ description: "Reads one persistent memory entry belonging to one of your declared sub-agents (by its bound name). Returns null if the key doesn't exist.",
24
+ jsonSchema: {
25
+ type: "object",
26
+ properties: { boundName: { type: "string" }, key: { type: "string" } },
27
+ required: ["boundName", "key"],
28
+ additionalProperties: false,
29
+ },
30
+ };
31
+ export const PARENT_MEMORY_GET_TOOL = {
32
+ name: "parent_memory_get",
33
+ description: "Reads one memory entry from the agent that dispatched you, but only for a key it explicitly granted you access to for this run. Returns an error for any other key.",
34
+ jsonSchema: {
35
+ type: "object",
36
+ properties: { key: { type: "string" } },
37
+ required: ["key"],
38
+ additionalProperties: false,
39
+ },
40
+ };
41
+ const SubAgentGetArgs = z.object({ boundName: z.string(), key: z.string() }).strict();
42
+ const ParentGetArgs = z.object({ key: z.string() }).strict();
43
+ function zodErrorResult(err) {
44
+ return JSON.stringify({
45
+ error: "validation_failed",
46
+ message: err.issues.map((issue) => `${issue.path.join(".") || "input"}: ${issue.message}`).join("; "),
47
+ });
48
+ }
49
+ /** `subagent_memory_get` — callable by a parent, gated by the AgentSubAgent edge itself. */
50
+ export async function handleSubAgentMemoryGet(argsJson, callingAgentId, db, memory) {
51
+ let parsed;
52
+ try {
53
+ parsed = JSON.parse(argsJson || "{}");
54
+ }
55
+ catch (err) {
56
+ return JSON.stringify({
57
+ error: "invalid_arguments_json",
58
+ message: err instanceof Error ? err.message : String(err),
59
+ });
60
+ }
61
+ let args;
62
+ try {
63
+ args = SubAgentGetArgs.parse(parsed);
64
+ }
65
+ catch (err) {
66
+ return zodErrorResult(err);
67
+ }
68
+ const edge = await db.agentSubAgent.findUnique({
69
+ where: { parentAgentId_boundName: { parentAgentId: callingAgentId, boundName: args.boundName } },
70
+ });
71
+ if (!edge) {
72
+ return JSON.stringify({
73
+ error: "no_such_subagent",
74
+ message: `No sub-agent is bound to name "${args.boundName}".`,
75
+ });
76
+ }
77
+ const content = await memory.get(edge.childAgentId, args.key);
78
+ return JSON.stringify({ content: content ?? null });
79
+ }
80
+ /**
81
+ * `parent_memory_get` — callable by a dispatched child, gated by this exact
82
+ * run's ephemeral grant. `currentRunId` is the run making the call, not the
83
+ * parent — its own `parentRunId`/`grantedParentMemoryKeys` are looked up
84
+ * fresh rather than threaded through the caller, so this stays correct even
85
+ * if called from deep inside a multi-turn engine loop.
86
+ */
87
+ export async function handleParentMemoryGet(argsJson, currentRunId, db, memory) {
88
+ let parsed;
89
+ try {
90
+ parsed = JSON.parse(argsJson || "{}");
91
+ }
92
+ catch (err) {
93
+ return JSON.stringify({
94
+ error: "invalid_arguments_json",
95
+ message: err instanceof Error ? err.message : String(err),
96
+ });
97
+ }
98
+ let args;
99
+ try {
100
+ args = ParentGetArgs.parse(parsed);
101
+ }
102
+ catch (err) {
103
+ return zodErrorResult(err);
104
+ }
105
+ const run = await db.run.findUniqueOrThrow({
106
+ where: { id: currentRunId },
107
+ select: { parentRunId: true, grantedParentMemoryKeys: true },
108
+ });
109
+ if (!run.parentRunId) {
110
+ return JSON.stringify({ error: "no_parent_run", message: "This run was not dispatched by a parent." });
111
+ }
112
+ if (!run.grantedParentMemoryKeys.includes(args.key)) {
113
+ return JSON.stringify({
114
+ error: "key_not_granted",
115
+ message: `Key "${args.key}" was not granted to this run by its parent.`,
116
+ });
117
+ }
118
+ const parentRun = await db.run.findUniqueOrThrow({ where: { id: run.parentRunId }, select: { agentId: true } });
119
+ const content = await memory.get(parentRun.agentId, args.key);
120
+ return JSON.stringify({ content: content ?? null });
121
+ }
@@ -0,0 +1,7 @@
1
+ /** Scheduler/executor/reconciler timing constants — one module, tunable. */
2
+ export declare const TICK_INTERVAL_MS = 10000;
3
+ export declare const LEASE_TTL_MS = 30000;
4
+ export declare const LEASE_RENEW_INTERVAL_MS = 10000;
5
+ export declare const HEARTBEAT_INTERVAL_MS = 10000;
6
+ export declare const HEARTBEAT_TIMEOUT_MS = 45000;
7
+ export declare const RECONCILE_INTERVAL_MS = 15000;
@@ -0,0 +1,7 @@
1
+ /** Scheduler/executor/reconciler timing constants — one module, tunable. */
2
+ export const TICK_INTERVAL_MS = 10_000;
3
+ export const LEASE_TTL_MS = 30_000;
4
+ export const LEASE_RENEW_INTERVAL_MS = 10_000;
5
+ export const HEARTBEAT_INTERVAL_MS = 10_000;
6
+ export const HEARTBEAT_TIMEOUT_MS = 45_000;
7
+ export const RECONCILE_INTERVAL_MS = 15_000;
@@ -0,0 +1,23 @@
1
+ import type { PrismaClient, Webhook } from "@prisma/client";
2
+ import type { Executor } from "../providers/executor/types.js";
3
+ export type WebhookMetadata = Pick<Webhook, "id" | "agentId" | "status" | "ownerId" | "createdAt" | "lastFiredAt">;
4
+ /** Creates a webhook and returns the raw secret ONCE — only its hash is ever stored. */
5
+ export declare function createWebhook(agentId: string, ownerId: string, db: PrismaClient): Promise<{
6
+ id: string;
7
+ secret: string;
8
+ }>;
9
+ export declare function listWebhooks(ownerId: string, db: PrismaClient): Promise<WebhookMetadata[]>;
10
+ export declare function deleteWebhook(id: string, db: PrismaClient): Promise<void>;
11
+ export type ResolveWebhookRunResult = {
12
+ ok: true;
13
+ runId: string;
14
+ } | {
15
+ ok: false;
16
+ reason: "not_found" | "invalid_secret" | "disabled";
17
+ };
18
+ /**
19
+ * Validates the presented secret and, if valid + enabled, enqueues a run via
20
+ * the exact same `createRun` path `trigger_agent`/the scheduler use — the
21
+ * budget guardrail applies identically regardless of what triggered the run.
22
+ */
23
+ export declare function resolveWebhookRun(id: string, presentedSecret: string, db: PrismaClient, executor: Executor, codingTask?: string): Promise<ResolveWebhookRunResult>;
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Webhooks: an inbound trigger for an agent, authenticated by a per-webhook
3
+ * secret (not OAuth — see ingress.ts). The secret is high-entropy
4
+ * (32 random bytes), so a plain SHA-256 hash is sufficient — unlike a
5
+ * user-chosen password, there's no low-entropy guessing surface a pepper/
6
+ * HMAC key would meaningfully defend against; this mirrors how GitHub/
7
+ * Stripe-style API tokens are stored (hash of a random token, no extra key).
8
+ * Comparison is constant-time (`timingSafeEqual` over the raw digest, which
9
+ * is always 32 bytes, so there's no length-mismatch case to special-case).
10
+ */
11
+ import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
12
+ import { dispatchRun } from "./dispatch.js";
13
+ function hashSecret(secret) {
14
+ return createHash("sha256").update(secret).digest("hex");
15
+ }
16
+ /**
17
+ * Whether a webhook is allowed to hand this agent per-run task text.
18
+ * Coding agents keep the existing explicit opt-in (allowWebhookTaskOverride)
19
+ * since the text can drive real repository changes. A native agent has no
20
+ * per-run input at all otherwise (its systemPrompt is fixed) and the
21
+ * resulting run is sandboxed, so accepting task text needs no separate
22
+ * opt-in — always allowed once the caller already holds the webhook secret.
23
+ */
24
+ function taskAllowed(kind, codingProfile) {
25
+ if (kind === "native")
26
+ return true;
27
+ return kind === "coding" && (codingProfile?.allowWebhookTaskOverride ?? false);
28
+ }
29
+ function secretMatches(presented, storedHash) {
30
+ const presentedHash = Buffer.from(hashSecret(presented), "hex");
31
+ const stored = Buffer.from(storedHash, "hex");
32
+ if (presentedHash.length !== stored.length)
33
+ return false;
34
+ return timingSafeEqual(presentedHash, stored);
35
+ }
36
+ /** Creates a webhook and returns the raw secret ONCE — only its hash is ever stored. */
37
+ export async function createWebhook(agentId, ownerId, db) {
38
+ const secret = randomBytes(32).toString("base64url");
39
+ const webhook = await db.webhook.create({ data: { agentId, ownerId, secretHash: hashSecret(secret) } });
40
+ return { id: webhook.id, secret };
41
+ }
42
+ export async function listWebhooks(ownerId, db) {
43
+ const webhooks = await db.webhook.findMany({ where: { ownerId } });
44
+ return webhooks.map(({ id, agentId, status, ownerId: owner, createdAt, lastFiredAt }) => ({
45
+ id,
46
+ agentId,
47
+ status,
48
+ ownerId: owner,
49
+ createdAt,
50
+ lastFiredAt,
51
+ }));
52
+ }
53
+ export async function deleteWebhook(id, db) {
54
+ await db.webhook.delete({ where: { id } });
55
+ }
56
+ /**
57
+ * Validates the presented secret and, if valid + enabled, enqueues a run via
58
+ * the exact same `createRun` path `trigger_agent`/the scheduler use — the
59
+ * budget guardrail applies identically regardless of what triggered the run.
60
+ */
61
+ export async function resolveWebhookRun(id, presentedSecret, db, executor, codingTask) {
62
+ const webhook = await db.webhook.findUnique({ where: { id } });
63
+ if (!webhook)
64
+ return { ok: false, reason: "not_found" };
65
+ if (!secretMatches(presentedSecret, webhook.secretHash))
66
+ return { ok: false, reason: "invalid_secret" };
67
+ if (webhook.status !== "enabled")
68
+ return { ok: false, reason: "disabled" };
69
+ const agent = await db.agent.findUnique({ where: { id: webhook.agentId }, include: { codingProfile: true } });
70
+ if (!agent)
71
+ return { ok: false, reason: "not_found" };
72
+ if (codingTask !== undefined && !taskAllowed(agent.kind, agent.codingProfile)) {
73
+ return { ok: false, reason: "disabled" };
74
+ }
75
+ let rejected = "disabled";
76
+ const dispatched = await dispatchRun({
77
+ db,
78
+ executor,
79
+ agentId: agent.id,
80
+ trigger: "webhook",
81
+ // Coding agents keep the existing per-agent opt-in (allowWebhookTaskOverride);
82
+ // a native agent's system prompt has no per-run input at all otherwise, so
83
+ // accepting task text over an authenticated webhook call needs no separate
84
+ // opt-in — it's a much lower-risk capability than a coding agent being told
85
+ // to modify a real repository.
86
+ codingTask: agent.kind === "coding" ? codingTask : undefined,
87
+ taskOverride: agent.kind === "native" ? codingTask : undefined,
88
+ beforePersist: async (tx, currentAgent) => {
89
+ const current = await tx.webhook.findUnique({ where: { id } });
90
+ if (!current) {
91
+ rejected = "not_found";
92
+ return false;
93
+ }
94
+ if (!secretMatches(presentedSecret, current.secretHash)) {
95
+ rejected = "invalid_secret";
96
+ return false;
97
+ }
98
+ if (current.status !== "enabled") {
99
+ rejected = "disabled";
100
+ return false;
101
+ }
102
+ if (codingTask !== undefined && !taskAllowed(currentAgent.kind, currentAgent.codingProfile)) {
103
+ rejected = "disabled";
104
+ return false;
105
+ }
106
+ await tx.webhook.update({ where: { id }, data: { lastFiredAt: new Date() } });
107
+ return true;
108
+ },
109
+ });
110
+ if (!dispatched)
111
+ return { ok: false, reason: rejected };
112
+ return { ok: true, runId: dispatched.run.id };
113
+ }
package/dist/env.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Single runtime load point for environment configuration. dotenv-flow uses
3
+ * its standard environment cascade; Vitest has a separate explicit cascade in
4
+ * vitest.setup.ts because dotenv-flow normally omits .env.local in test mode.
5
+ * Imported for its side effect only, before any module reads process.env.
6
+ *
7
+ * It is also where the process's HTTP runtime is installed (see
8
+ * core/http-runtime.ts): same contract — it has to happen before any other
9
+ * module does anything — and every entry point already imports this file first.
10
+ */
11
+ import "./core/http-runtime.js";
package/dist/env.js ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Single runtime load point for environment configuration. dotenv-flow uses
3
+ * its standard environment cascade; Vitest has a separate explicit cascade in
4
+ * vitest.setup.ts because dotenv-flow normally omits .env.local in test mode.
5
+ * Imported for its side effect only, before any module reads process.env.
6
+ *
7
+ * It is also where the process's HTTP runtime is installed (see
8
+ * core/http-runtime.ts): same contract — it has to happen before any other
9
+ * module does anything — and every entry point already imports this file first.
10
+ */
11
+ import "./core/http-runtime.js";
12
+ import dotenvFlow from "dotenv-flow";
13
+ dotenvFlow.config();
@@ -0,0 +1,16 @@
1
+ import type { NeutralBudget } from "./neutral-schema.js";
2
+ export interface BudgetGroupData {
3
+ name: string;
4
+ dailyBudgetUsd: string | null;
5
+ weeklyBudgetUsd: string | null;
6
+ monthlyBudgetUsd: string | null;
7
+ warnThresholdRatio: string;
8
+ }
9
+ export declare function mapBudget(b: NeutralBudget): {
10
+ ok: true;
11
+ group: BudgetGroupData;
12
+ agentNames: string[];
13
+ } | {
14
+ ok: false;
15
+ reason: string;
16
+ };
@@ -0,0 +1,21 @@
1
+ function warnRatio(alert, block) {
2
+ const a = Number(alert), b = Number(block);
3
+ if (!Number.isFinite(a) || !Number.isFinite(b) || b <= 0)
4
+ return "0.8";
5
+ const r = a / b;
6
+ if (!Number.isFinite(r) || r <= 0)
7
+ return "0.8";
8
+ return Math.min(r, 1).toFixed(2);
9
+ }
10
+ export function mapBudget(b) {
11
+ if (!b.enabled)
12
+ return { ok: false, reason: "disabled" };
13
+ const group = {
14
+ name: b.name,
15
+ dailyBudgetUsd: b.period === "daily" ? b.blockThreshold : null,
16
+ weeklyBudgetUsd: b.period === "weekly" ? b.blockThreshold : null,
17
+ monthlyBudgetUsd: b.period === "monthly" ? b.blockThreshold : null,
18
+ warnThresholdRatio: warnRatio(b.alertThreshold, b.blockThreshold),
19
+ };
20
+ return { ok: true, group, agentNames: b.attachedAgentNames };
21
+ }
@@ -0,0 +1,16 @@
1
+ import { type Manifest, type NeutralAgent, type NeutralTool, type NeutralAgentTool, type NeutralSecret, type NeutralAgentSecret, type NeutralSingleDatastore, type NeutralSharedDatastore, type NeutralWebhook, type NeutralBudget } from "./neutral-schema.js";
2
+ export declare class BundleError extends Error {
3
+ }
4
+ export interface Bundle {
5
+ manifest: Manifest;
6
+ readAgents(): NeutralAgent[];
7
+ readTools(): NeutralTool[];
8
+ readAgentTools(): NeutralAgentTool[];
9
+ readSecrets(): NeutralSecret[];
10
+ readAgentSecrets(): NeutralAgentSecret[];
11
+ readSingleDatastores(): NeutralSingleDatastore[];
12
+ readSharedDatastores(): NeutralSharedDatastore[];
13
+ readWebhooks(): NeutralWebhook[];
14
+ readBudgets(): NeutralBudget[];
15
+ }
16
+ export declare function openBundle(dir: string): Bundle;