@voltro/cli 0.33.0 → 0.34.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 (337) hide show
  1. package/CHANGELOG.md +1801 -0
  2. package/bin/nodeEnvironment.d.mts +30 -0
  3. package/bin/nodeEnvironment.mjs +158 -0
  4. package/bin/voltro.mjs +69 -5
  5. package/dist/addCommand-BNeoeSxe.js +124 -0
  6. package/dist/addCommand-aXSQveak.js +2 -0
  7. package/dist/agentsMd-BTchIZku.js +2 -0
  8. package/dist/agentsMd-mhQMF1bx.js +254 -0
  9. package/dist/apiBuild-BICVGnEC.js +2 -0
  10. package/dist/{apiBuild-h9VHtnlw.js → apiBuild-DZF_f0_I.js} +46 -46
  11. package/dist/appPort-B_HpJ_ck.js +48 -0
  12. package/dist/baselineCommand-C2ClWZN3.js +2 -0
  13. package/dist/baselineCommand-DIttzO8A.js +227 -0
  14. package/dist/bin.js +71 -28
  15. package/dist/build-CE7Ca9gm.js +711 -0
  16. package/dist/cacheCommand-DA4OH9xt.js +42 -0
  17. package/dist/capabilitiesCommand-nq_pz5xd.js +123 -0
  18. package/dist/checkCommand-Dv8r9tGO.js +231 -0
  19. package/dist/checkCommand-YZDmkAGd.js +2 -0
  20. package/dist/{cliArgs-qdZSElM3.js → cliArgs-D4p8n7EE.js} +12 -1
  21. package/dist/cliError-BmdYnghb.js +10 -0
  22. package/dist/cliOutput-D1tSBoRM.js +15 -0
  23. package/dist/{cliRuntime-Oh517vCV.js → cliRuntime-Dh7UDinH.js} +20 -20
  24. package/dist/cloudClient-DWL-Hw_T.js +67 -0
  25. package/dist/cloudCmd-Cvv5HGaZ.js +364 -0
  26. package/dist/clusterCmd-CNXKlcvD.js +54 -0
  27. package/dist/codegen-CYM3Zqrf.js +605 -0
  28. package/dist/codegen-ChBi_hVa.js +2 -0
  29. package/dist/codegenCommand-DC6w2tNZ.js +30 -0
  30. package/dist/codemodRunner-DRRqXR74.js +5243 -0
  31. package/dist/commandRunner-BLAEFLjp.js +47 -0
  32. package/dist/commands-gutsz-Ac.js +808 -0
  33. package/dist/connectionConfig-UFlIEiys.js +66 -0
  34. package/dist/dashboardCommand-3YG8p-UA.js +25 -0
  35. package/dist/dataCommand-qL0r7fPO.js +535 -0
  36. package/dist/dataProfile-dW-PsfLB.js +15 -0
  37. package/dist/{dbCommand-DTLKAfbA.js → dbCommand-B6X0FZbq.js} +551 -401
  38. package/dist/dbCommand-CpdKLeQq.js +2 -0
  39. package/dist/{dev-C_P8FLSx.js → dev-CerMd0mW.js} +3122 -3129
  40. package/dist/dev-CoG-ZPx8.js +3 -0
  41. package/dist/devActivity-Dx_3nnGv.js +100 -0
  42. package/dist/devActivity.js +1 -1
  43. package/dist/dialectDriver-CgXnDfec.js +39 -0
  44. package/dist/discover-C9XKJDco.js +25 -0
  45. package/dist/doctorCommand-BvqGBwNG.js +2 -0
  46. package/dist/{checkCommand-xGhRFFg2.js → doctorCommand-CnDXQxfa.js} +520 -1278
  47. package/dist/dormancyCommand-Dszo57d6.js +69 -0
  48. package/dist/e2eCmd-BRabZww-.js +147 -0
  49. package/dist/embeddingsCommand-C0sKVRo1.js +73 -0
  50. package/dist/envCommand-DPgdV1Bq.js +60 -0
  51. package/dist/evalCommand-6RUfPen4.js +118 -0
  52. package/dist/evolveCommand-DHpkgjgH.js +281 -0
  53. package/dist/fileTaxonomy-CJfgOllU.js +457 -0
  54. package/dist/frameworkTableAssembly-BGHmck-x.js +2 -0
  55. package/dist/{frameworkTableAssembly-BwIrO5nv.js → frameworkTableAssembly-DkkP6BgC.js} +184 -148
  56. package/dist/generateCommand-oibemh97.js +147 -0
  57. package/dist/index.d.ts +45 -0
  58. package/dist/index.js +4 -3
  59. package/dist/infoCommand-BJw9nLUR.js +60 -0
  60. package/dist/{inspect-BUUjt773.js → inspect-CBqFtAKA.js} +82 -40
  61. package/dist/inspect-C_T_WGvl.js +2 -0
  62. package/dist/inspectCmd-Bppy-GGw.js +224 -0
  63. package/dist/inspectFetch-Cm8_wVvp.js +151 -0
  64. package/dist/inspectMetrics-CfdKLh6t.js +72 -0
  65. package/dist/loadEnv-D9nEOClM.js +44 -0
  66. package/dist/logFileSink-C_D2wRN1.js +105 -0
  67. package/dist/logsCmd-CCca3KRZ.js +260 -0
  68. package/dist/manifestBuild-ChsKAhmn.js +2 -0
  69. package/dist/{manifestBuild-BLrVuSlM.js → manifestBuild-sxpwdKY1.js} +1 -1
  70. package/dist/metaCommands-7MJfZ5cf.js +196 -0
  71. package/dist/migrate-CBwOt_iV.js +83 -0
  72. package/dist/mssqlClusterPatch-_4cE_nun.js +44 -0
  73. package/dist/newCommand-COWOJ1_E.js +156 -0
  74. package/dist/nodeEnvironment-cGFAj1J8.js +28 -0
  75. package/dist/packageCommand-Cug_3Ogl.js +271 -0
  76. package/dist/pageConvention-cEiRxdab.js +5 -0
  77. package/dist/privacyCommand-C-Df56U_.js +146 -0
  78. package/dist/projectScaffold-DmzEKHib.js +2 -0
  79. package/dist/projectScaffold-LMMtaavR.js +814 -0
  80. package/dist/renderModeScan-D7J1B7Kw.js +105 -0
  81. package/dist/renderProfile-1OWWAAtx.js +81 -0
  82. package/dist/runtimeRegistry-DMeKfTHP.js +81 -0
  83. package/dist/runtimeTrace-CRxalXTs.js +91 -0
  84. package/dist/scheduleCmd--jksTrf6.js +69 -0
  85. package/dist/scheduleManifestCmd-D2x0CTTY.js +249 -0
  86. package/dist/schemaIr-UJybUUZW.js +103 -0
  87. package/dist/{sdkgen-C81QIkiL.js → sdkgen-CYJscZC7.js} +111 -209
  88. package/dist/seedRunner-TFHHiToI.js +329 -0
  89. package/dist/serveCommand-B_isw7q4.js +1647 -0
  90. package/dist/serveCommand-DOvbgRnQ.js +2 -0
  91. package/dist/serveEntry.js +5 -5
  92. package/dist/serverlessCommand-CfJZy6dS.js +482 -0
  93. package/dist/start-9LiUOfES.js +1087 -0
  94. package/dist/start-B-9Nsp-S.js +3 -0
  95. package/dist/startEntry.js +2 -2
  96. package/dist/staticCommand-Dr2M6tpU.js +304 -0
  97. package/dist/storageCommand-Co6NfLqN.js +42 -0
  98. package/dist/templates-De8IR5-c.js +102 -0
  99. package/dist/test-CI6iDsYc.js +115 -0
  100. package/dist/tracesCmd-CkEZQrtt.js +232 -0
  101. package/dist/tsconfigPaths-BWXBWgcl.js +107 -0
  102. package/dist/tsxLoader-EuXmSJ1K.js +51 -0
  103. package/dist/typecheckCommand-BlsWiCNq.js +61 -0
  104. package/dist/updateCommand-Bkptutss.js +585 -0
  105. package/dist/updateCommand-us1_hdIC.js +2 -0
  106. package/dist/{inspectMetrics-1xzTKAFx.js → webDev-CBYvPqQr.js} +975 -1559
  107. package/dist/webDev-Cg-fFiyd2.js +2 -0
  108. package/dist/webhookDiscovery-CrGAfhIG.js +2 -0
  109. package/dist/webhookDiscovery-D7VaeMlz.js +51 -0
  110. package/dist/webhooksCommand-CID96Rga.js +267 -0
  111. package/dist/workflowsCmd-D1VTmLMY.js +608 -0
  112. package/package.json +179 -17
  113. package/templates/AGENTS.core.md +58 -3
  114. package/templates/AGENTS.md +64 -7
  115. package/templates/agent-docs/_index.md +6 -4
  116. package/templates/agent-docs/_manifest.json +22 -5
  117. package/templates/agent-docs/ai.md +370 -0
  118. package/templates/agent-docs/authentication.md +265 -31
  119. package/templates/agent-docs/caching.md +6 -0
  120. package/templates/agent-docs/cli.md +794 -50
  121. package/templates/agent-docs/data.md +550 -11
  122. package/templates/agent-docs/database/migrations.md +174 -25
  123. package/templates/agent-docs/database/misc.md +156 -40
  124. package/templates/agent-docs/database/querying.md +19 -1
  125. package/templates/agent-docs/database/scaling.md +60 -0
  126. package/templates/agent-docs/database/schema.md +1 -0
  127. package/templates/agent-docs/database/seedsdialects.md +208 -19
  128. package/templates/agent-docs/database/transactions.md +68 -0
  129. package/templates/agent-docs/deployment.md +69 -3
  130. package/templates/agent-docs/introduction.md +87 -16
  131. package/templates/agent-docs/local-first-mobile.md +79 -4
  132. package/templates/agent-docs/multi-tenancy.md +95 -20
  133. package/templates/agent-docs/observability.md +58 -3
  134. package/templates/agent-docs/plugins/ai-flows.md +161 -2
  135. package/templates/agent-docs/plugins/analytics-postgres.md +1 -1
  136. package/templates/agent-docs/plugins/audit.md +37 -1
  137. package/templates/agent-docs/plugins/auth-social.md +143 -0
  138. package/templates/agent-docs/plugins/auth-workos.md +4 -2
  139. package/templates/agent-docs/plugins/auth.md +131 -6
  140. package/templates/agent-docs/plugins/billing.md +132 -15
  141. package/templates/agent-docs/plugins/cdc-out.md +46 -7
  142. package/templates/agent-docs/plugins/clickhouse.md +1 -1
  143. package/templates/agent-docs/plugins/duckdb.md +1 -1
  144. package/templates/agent-docs/plugins/flags.md +132 -0
  145. package/templates/agent-docs/plugins/governance.md +105 -7
  146. package/templates/agent-docs/plugins/multitenancy.md +9 -4
  147. package/templates/agent-docs/plugins/presence.md +13 -2
  148. package/templates/agent-docs/plugins/ratelimit.md +9 -0
  149. package/templates/agent-docs/plugins/search.md +157 -6
  150. package/templates/agent-docs/plugins/sso-saml.md +47 -8
  151. package/templates/agent-docs/plugins/webhooks.md +105 -0
  152. package/templates/agent-docs/plugins.md +150 -18
  153. package/templates/agent-docs/reference.md +60 -3
  154. package/templates/agent-docs/releases.md +1117 -0
  155. package/templates/agent-docs/routing.md +43 -25
  156. package/templates/agent-docs/scheduling.md +10 -0
  157. package/templates/agent-docs/schema-driven-ui.md +92 -12
  158. package/templates/agent-docs/security.md +426 -0
  159. package/templates/agent-docs/templates/apibackends.md +87 -18
  160. package/templates/agent-docs/templates/appshells.md +32 -14
  161. package/templates/agent-docs/templates/overview.md +13 -8
  162. package/templates/agent-docs/testing.md +211 -14
  163. package/templates/agent-docs/whats-new.md +1700 -104
  164. package/templates/agent-docs/workflows.md +130 -14
  165. package/templates/apps/api-ai/actions/summarize.action.ts +11 -0
  166. package/templates/apps/api-ai/package.json +8 -7
  167. package/templates/apps/api-auth/actions/me.action.ts +13 -0
  168. package/templates/apps/api-auth/package.json +9 -8
  169. package/templates/apps/api-backend/mutations/notes.create.mutation.ts +9 -0
  170. package/templates/apps/api-backend/package.json +12 -8
  171. package/templates/apps/api-backend/queries/notes.query.ts +30 -8
  172. package/templates/apps/api-backend-deactivation/actions/users.get.action.ts +14 -0
  173. package/templates/apps/api-backend-deactivation/mutations/users.create.mutation.ts +8 -0
  174. package/templates/apps/api-backend-deactivation/mutations/users.deactivate.mutation.ts +10 -0
  175. package/templates/apps/api-backend-deactivation/package.json +8 -7
  176. package/templates/apps/api-backend-mail/actions/sendWelcome.action.ts +17 -0
  177. package/templates/apps/api-backend-mail/mutations/notes.create.mutation.ts +9 -0
  178. package/templates/apps/api-backend-mail/package.json +9 -8
  179. package/templates/apps/api-backend-mail/queries/notes.query.ts +30 -8
  180. package/templates/apps/api-backend-mariadb/.env.example +14 -0
  181. package/templates/apps/api-backend-mariadb/mutations/notes.create.mutation.ts +9 -0
  182. package/templates/apps/api-backend-mariadb/package.json +10 -9
  183. package/templates/apps/api-backend-mariadb/queries/notes.query.ts +30 -8
  184. package/templates/apps/api-backend-sqlite/.env.example +14 -0
  185. package/templates/apps/api-backend-sqlite/mutations/notes.create.mutation.ts +9 -0
  186. package/templates/apps/api-backend-sqlite/package.json +9 -8
  187. package/templates/apps/api-backend-sqlite/queries/notes.query.ts +30 -8
  188. package/templates/apps/api-backend-storage/actions/uploadAvatar.action.ts +13 -0
  189. package/templates/apps/api-backend-storage/actions/uploadDocument.action.ts +12 -0
  190. package/templates/apps/api-backend-storage/mutations/notes.create.mutation.ts +9 -0
  191. package/templates/apps/api-backend-storage/package.json +9 -8
  192. package/templates/apps/api-backend-storage/queries/notes.query.ts +30 -8
  193. package/templates/apps/api-cms/actions/content.get.action.ts +7 -0
  194. package/templates/apps/api-cms/actions/content.types.action.ts +6 -0
  195. package/templates/apps/api-cms/actions/me.action.ts +13 -0
  196. package/templates/apps/api-cms/app.config.ts +19 -0
  197. package/templates/apps/api-cms/authz.ts +63 -0
  198. package/templates/apps/api-cms/mutations/content.publish.mutation.ts +10 -0
  199. package/templates/apps/api-cms/mutations/content.saveDraft.mutation.ts +10 -0
  200. package/templates/apps/api-cms/mutations/content.unpublish.mutation.ts +5 -0
  201. package/templates/apps/api-cms/package.json +11 -10
  202. package/templates/apps/api-cms/queries/content.list.query.ts +23 -7
  203. package/templates/apps/api-cms/tests/accessDecisions.test.ts +121 -0
  204. package/templates/apps/api-cms/tests/content.descriptors.test.ts +8 -4
  205. package/templates/apps/api-collab/mutations/documents.create.mutation.ts +8 -0
  206. package/templates/apps/api-collab/mutations/documents.setBody.mutation.ts +13 -0
  207. package/templates/apps/api-collab/package.json +9 -8
  208. package/templates/apps/api-collab/queries/documents.query.ts +19 -7
  209. package/templates/apps/api-data-advanced/package.json +9 -8
  210. package/templates/apps/api-data-advanced/queries/authors.withBooks.query.ts +12 -0
  211. package/templates/apps/api-data-advanced/queries/books.search.query.ts +9 -0
  212. package/templates/apps/api-durable/mutations/orders.approve.mutation.ts +17 -0
  213. package/templates/apps/api-durable/mutations/orders.place.mutation.ts +9 -0
  214. package/templates/apps/api-durable/package.json +9 -8
  215. package/templates/apps/api-feature-flags/actions/notes.export.action.ts +16 -0
  216. package/templates/apps/api-feature-flags/mutations/notes.create.mutation.ts +12 -0
  217. package/templates/apps/api-feature-flags/package.json +10 -9
  218. package/templates/apps/api-governance/README.md +30 -8
  219. package/templates/apps/api-governance/actions/profiles.get.action.server.ts +17 -1
  220. package/templates/apps/api-governance/actions/profiles.get.action.ts +29 -4
  221. package/templates/apps/api-governance/database/schema.ts +16 -4
  222. package/templates/apps/api-governance/mutations/profiles.create.mutation.ts +12 -0
  223. package/templates/apps/api-governance/package.json +9 -8
  224. package/templates/apps/api-kv/actions/sync.pull.action.ts +15 -0
  225. package/templates/apps/api-kv/actions/sync.reset.action.ts +13 -0
  226. package/templates/apps/api-kv/actions/sync.status.action.ts +7 -0
  227. package/templates/apps/api-kv/package.json +9 -8
  228. package/templates/apps/api-kv/queries/events.list.query.ts +19 -8
  229. package/templates/apps/api-moderation/mutations/comments.create.mutation.ts +11 -0
  230. package/templates/apps/api-moderation/mutations/posts.create.mutation.ts +13 -0
  231. package/templates/apps/api-moderation/package.json +9 -8
  232. package/templates/apps/api-observability/mutations/notes.create.mutation.ts +8 -0
  233. package/templates/apps/api-observability/package.json +9 -8
  234. package/templates/apps/api-observability/queries/notes.list.query.ts +13 -0
  235. package/templates/apps/api-ratelimit/mutations/notes.create.mutation.ts +14 -0
  236. package/templates/apps/api-ratelimit/package.json +9 -8
  237. package/templates/apps/api-rbac/package.json +9 -8
  238. package/templates/apps/api-rest/package.json +8 -7
  239. package/templates/apps/api-saas/mutations/projects.create.mutation.ts +13 -0
  240. package/templates/apps/api-saas/package.json +12 -11
  241. package/templates/apps/api-saas/queries/projects.list.query.ts +11 -0
  242. package/templates/apps/api-saas-starter/actions/me.action.ts +13 -0
  243. package/templates/apps/api-saas-starter/app.config.ts +19 -0
  244. package/templates/apps/api-saas-starter/authz.ts +75 -0
  245. package/templates/apps/api-saas-starter/mutations/invites.create.mutation.ts +10 -0
  246. package/templates/apps/api-saas-starter/mutations/projects.create.mutation.ts +9 -0
  247. package/templates/apps/api-saas-starter/package.json +15 -11
  248. package/templates/apps/api-saas-starter/queries/invites.list.query.ts +19 -8
  249. package/templates/apps/api-saas-starter/queries/projects.list.query.ts +18 -7
  250. package/templates/apps/api-saas-starter/tests/accessDecisions.test.ts +135 -0
  251. package/templates/apps/api-search/mutations/articles.create.mutation.ts +14 -0
  252. package/templates/apps/api-search/package.json +9 -8
  253. package/templates/apps/api-search/queries/articles.list.query.ts +21 -8
  254. package/templates/apps/api-status/README.md +10 -3
  255. package/templates/apps/api-status/app.config.ts +8 -3
  256. package/templates/apps/api-status/authz.ts +5 -3
  257. package/templates/apps/api-status/package.json +9 -8
  258. package/templates/apps/api-status/queries/components.list.query.ts +14 -6
  259. package/templates/apps/api-status/queries/incidents.live.query.ts +23 -10
  260. package/templates/apps/api-status/queries/updates.list.query.ts +16 -9
  261. package/templates/apps/api-status/tests/status.test.ts +9 -1
  262. package/templates/apps/api-versioning/actions/documents.asOf.action.ts +12 -0
  263. package/templates/apps/api-versioning/actions/documents.history.action.ts +13 -0
  264. package/templates/apps/api-versioning/mutations/documents.create.mutation.ts +9 -0
  265. package/templates/apps/api-versioning/mutations/documents.update.mutation.ts +12 -0
  266. package/templates/apps/api-versioning/package.json +9 -8
  267. package/templates/apps/api-webhooks/mutations/orders.fulfill.mutation.ts +16 -0
  268. package/templates/apps/api-webhooks/package.json +10 -9
  269. package/templates/apps/api-webhooks/queries/orders.list.query.ts +10 -0
  270. package/templates/apps/changelog/package.json +8 -6
  271. package/templates/apps/edge-functions/package.json +2 -2
  272. package/templates/apps/frontend-admin/package.json +10 -8
  273. package/templates/apps/frontend-admin/src/lib/admin.ts +20 -9
  274. package/templates/apps/frontend-admin/src/locales/de.ts +11 -1
  275. package/templates/apps/frontend-admin/src/locales/en.ts +13 -1
  276. package/templates/apps/frontend-admin/src/pages/admin/[entity]/page.tsx +65 -22
  277. package/templates/apps/frontend-admin/src/pages/admin/entity.test.tsx +130 -23
  278. package/templates/apps/frontend-admin/src/pages/admin/layout.tsx +3 -2
  279. package/templates/apps/frontend-admin/src/pages/admin/page.test.tsx +19 -2
  280. package/templates/apps/frontend-admin/src/pages/admin/page.tsx +9 -4
  281. package/templates/apps/frontend-app/app.config.ts +4 -3
  282. package/templates/apps/frontend-app/package.json +11 -8
  283. package/templates/apps/frontend-app/src/lib/api.ts +25 -0
  284. package/templates/apps/frontend-app/src/pages/page.test.tsx +130 -82
  285. package/templates/apps/frontend-app/src/pages/page.tsx +14 -18
  286. package/templates/apps/frontend-auth/package.json +10 -8
  287. package/templates/apps/frontend-blank/package.json +9 -7
  288. package/templates/apps/frontend-cms/package.json +11 -9
  289. package/templates/apps/frontend-collab/package.json +12 -9
  290. package/templates/apps/frontend-collab/src/pages/page.test.tsx +122 -78
  291. package/templates/apps/frontend-contact/package.json +9 -7
  292. package/templates/apps/frontend-dashboard/package.json +9 -7
  293. package/templates/apps/frontend-docs/package.json +9 -7
  294. package/templates/apps/frontend-i18n/package.json +8 -6
  295. package/templates/apps/frontend-landing/package.json +9 -7
  296. package/templates/apps/frontend-portal/package.json +10 -8
  297. package/templates/apps/frontend-saas/app.config.ts +10 -6
  298. package/templates/apps/frontend-saas/package.json +10 -8
  299. package/templates/apps/frontend-saas/src/lib/api.ts +27 -32
  300. package/templates/apps/frontend-saas/src/pages/dashboard/billing/page.tsx +7 -8
  301. package/templates/apps/frontend-saas/src/pages/dashboard/page.test.tsx +27 -3
  302. package/templates/apps/frontend-saas/src/pages/dashboard/page.tsx +4 -4
  303. package/templates/apps/frontend-saas/src/pages/dashboard/team/page.tsx +3 -4
  304. package/templates/apps/frontend-spa/package.json +9 -7
  305. package/templates/apps/frontend-ssr/package.json +9 -7
  306. package/templates/apps/frontend-ssr-api/package.json +10 -8
  307. package/templates/apps/frontend-static-blog/package.json +8 -6
  308. package/templates/apps/frontend-status/package.json +10 -8
  309. package/templates/apps/mobile-app/README.md +1 -0
  310. package/templates/apps/mobile-app/package.json +4 -2
  311. package/templates/apps/mobile-app/src/app/index.tsx +22 -12
  312. package/templates/apps/mobile-app/src/app/orders/[id].tsx +1 -1
  313. package/templates/apps/mobile-app/src/lib/api.ts +34 -0
  314. package/templates/apps/mobile-app/voltro.mobile.ts +4 -2
  315. package/templates/baselines/bare/.env.example +14 -0
  316. package/templates/baselines/bare/baseline.json +4 -4
  317. package/templates/baselines/compose/.env.example +14 -0
  318. package/templates/baselines/compose/README.md +1 -1
  319. package/templates/baselines/compose/baseline.json +5 -5
  320. package/templates/baselines/compose-mariadb/.env.example +14 -0
  321. package/templates/baselines/compose-mariadb/README.md +1 -1
  322. package/templates/baselines/compose-mariadb/baseline.json +5 -5
  323. package/templates/baselines/helm/.env.example +14 -0
  324. package/templates/baselines/helm/baseline.json +4 -4
  325. package/dist/apiBuild-C-x9YacA.js +0 -2
  326. package/dist/checkCommand-DRovTKza.js +0 -2
  327. package/dist/commands-CJfepbm4.js +0 -11541
  328. package/dist/dbCommand-b1gum4td.js +0 -2
  329. package/dist/dev-iiMtlkfs.js +0 -3
  330. package/dist/devActivity-BhIu6ncs.js +0 -159
  331. package/dist/frameworkTableAssembly-D-EebUQX.js +0 -2
  332. package/dist/inspect-mmBuRXmy.js +0 -2
  333. package/dist/manifestBuild-Dj8Jjoto.js +0 -2
  334. package/dist/seedRunner-Bqxgp7HZ.js +0 -230
  335. package/dist/serveCommand-DdaM4Hup.js +0 -1608
  336. package/dist/start-C0koT0UO.js +0 -1084
  337. /package/templates/apps/api-ai/actions/{summarize.action.server.tsx → summarize.action.server.ts} +0 -0
@@ -46,9 +46,11 @@ The implementation is the [Vercel AI SDK](https://sdk.vercel.ai) wrapped behind
46
46
  - [Providers](/docs/ai/providers) — Anthropic, OpenAI, the Vercel AI Gateway, mock; per-config keys (BYOK), switching at runtime, model-wrapping middleware
47
47
  - [Agents](/docs/ai/agents) — `*.agent.tsx` shape, system prompts, tool wiring
48
48
  - [Tools](/docs/ai/tools) — `*.tool.tsx` shape, validation, side effects
49
+ - [MCP clients](/docs/ai/mcp-clients) — mount an EXTERNAL MCP server's tools, through the same allow/deny policy, with the untrusted server bounded
49
50
  - [Streaming](/docs/ai/streaming) — tokens-over-WebSocket, client hooks, backpressure
50
51
  - [RAG](/docs/ai/rag) — pgvector, embedding mixin, hybrid search
51
52
  - [Cost tracking](/docs/ai/cost-tracking) — the token `usage` returned on every call
53
+ - [Prompt versioning](/docs/ai/prompt-versioning) — `definePrompt`, a content digest as the version, and which prompt version produced (and cost) a given run
52
54
 
53
55
  ## Why one surface
54
56
 
@@ -982,10 +984,12 @@ authorization path), by construction.
982
984
  // Opt a descriptor in (a description is REQUIRED — the model needs it):
983
985
  export const listOrders = defineQuery({
984
986
  name: 'orders.list', input: ListInput, output: Schema.Array(Order), source: 'orders',
987
+ guards: [{ scope: 'orders:read' }], // the tool inherits exactly this check
985
988
  exposeAsTool: { description: "List the current tenant's orders." },
986
989
  })
987
990
  export const createOrder = defineMutation({
988
991
  name: 'orders.create', input: CreateInput, output: Order, target: { table: 'orders', op: 'insert' },
992
+ guards: [{ scope: 'orders:write' }],
989
993
  exposeAsTool: { description: 'Create an order.', confirm: true }, // writes confirm by default
990
994
  })
991
995
  ```
@@ -999,6 +1003,14 @@ const tools = appTools(entries, { allow: ['orders.*'], includeWrites: true })
999
1003
  const { object } = yield* generateObjectWithTools({ prompt, tools, schema: Result })
1000
1004
  ```
1001
1005
 
1006
+ **The guards are load-bearing, not incidental.** "The agent can do nothing the
1007
+ subject couldn't" is a claim about the descriptor's own access decision — the
1008
+ synthesized tool runs the real handler under the calling subject and re-checks
1009
+ the same `guards:`. Exposing a procedure whose decision is `openAccess:` gives
1010
+ the model an unchecked endpoint, which is the right call for a public price
1011
+ lookup and the wrong one for anything reading rows. (A descriptor with *no*
1012
+ decision cannot reach this page: the app would not have booted.)
1013
+
1002
1014
  Safety defaults (don't override blindly): **reads are included, writes are
1003
1015
  opt-in** (`includeWrites: true`) and **confirm by default**. Put destructive
1004
1016
  tags on `deny`. `exposeAsTool: true` alone does NOT expose — a tool with no
@@ -1006,6 +1018,60 @@ description is unusable; always use the object form. The annotations also
1006
1018
  surface in the capability manifest, so a coding agent discovers what's
1007
1019
  tool-exposable.
1008
1020
 
1021
+ ### `confirm` is enforced, not reported
1022
+
1023
+ A `confirm` tool is admitted only when its descriptor also declares
1024
+ [`requiresApproval`](/docs/data/approvals). Otherwise it is REFUSED — with the
1025
+ fix in the reason — because the alternative is to mount it and hope the caller
1026
+ asks, and the caller is a model.
1027
+
1028
+ ```ts
1029
+ export const refundOrder = defineMutation({
1030
+ name: 'orders.refund',
1031
+ // …
1032
+ guards: [{ scope: 'orders:refund' }],
1033
+ exposeAsTool: { description: 'Refund an order.' }, // confirm: true by default
1034
+ requiresApproval: { approvers: [{ scope: 'orders:approve' }] },
1035
+ })
1036
+ ```
1037
+
1038
+ With that pair, the agent's call parks in your app's own approval queue and comes
1039
+ back as a typed `ApprovalRequired` carrying an id; a human decides in your UI;
1040
+ the identical call then succeeds exactly once. The human step is enforced by the
1041
+ handler, so nothing depends on the agent harness honouring a flag.
1042
+
1043
+ The inventory reports both `confirm` and `approvalBacked`, because "why is this
1044
+ tool not executable" is answered only by the pair. For an **external** MCP
1045
+ server's tools `approvalBacked` is always false: their handler is behind an HTTP
1046
+ boundary we do not own, so there is no point at which we could hold the call —
1047
+ the human decision for an external write is the declaration-time one (`allow` is
1048
+ required, `includeWrites` is opt-in).
1049
+
1050
+ ### The same toolset, to an EXTERNAL agent
1051
+
1052
+ `appTools` is the in-process form: your own agent loop, in your own handler. The
1053
+ same descriptors — through the same admission decision (`appToolDecision`, which
1054
+ `appTools` itself filters on) — can also be handed to an external MCP peer, so
1055
+ Claude Code or Cursor calls your procedures directly:
1056
+
1057
+ ```ts
1058
+ // app.config.ts
1059
+ export default {
1060
+ agents: {
1061
+ tools: { allow: ['orders.*'], includeWrites: true }, // the SAME AppToolPolicy
1062
+ mcp: true, // off by default
1063
+ },
1064
+ }
1065
+ ```
1066
+
1067
+ Five gates stand in front of it, the app credential the agent acts as is a
1068
+ separate header from the operator's inspect token, and an **unbacked** `confirm`
1069
+ tool is not mounted there at all (there is no human in that process). A
1070
+ `requiresApproval`-backed one IS mounted: the human is not in the transport, they
1071
+ are in your approval queue, so nothing there has to trust the client.
1072
+ The full list, and what it does not defend against, is on the
1073
+ [MCP server](/docs/cli/mcp) page.
1074
+
1009
1075
  The end-user-facing counterpart is **`<AppAgent>`** (from `@voltro/web`) — a
1010
1076
  "do it for me" chat whose ceiling is the logged-in subject's own permissions.
1011
1077
  See [Schema-driven UI → Reactive components](/docs/ui/reactive-components).
@@ -1019,6 +1085,127 @@ See [Schema-driven UI → Reactive components](/docs/ui/reactive-components).
1019
1085
 
1020
1086
 
1021
1087
 
1088
+ ---
1089
+
1090
+ <!-- source: en/ai/mcp-clients.md -->
1091
+ ## MCP clients
1092
+
1093
+ _Mount an external MCP server's tools onto a Voltro agent — through the same allow/deny policy your own `exposeAsTool` descriptors go through, with the untrusted server bounded._
1094
+
1095
+ `@voltro/mcp` points **outward**: it exposes your app to a coding agent as an MCP server. `mcpToolset` points **inward** — it connects to somebody else's MCP server (GitHub, Slack, Sentry, a Postgres bridge) and mounts its tools onto a Voltro agent.
1096
+
1097
+ The two halves speak the same protocol; only the direction differs.
1098
+
1099
+ ## Mounting a server
1100
+
1101
+ ```ts
1102
+ import { generateObjectWithTools, httpMcpTransport, mcpToolset } from '@voltro/ai'
1103
+
1104
+ const github = yield* mcpToolset(
1105
+ {
1106
+ namespace: 'github',
1107
+ transport: httpMcpTransport({
1108
+ server: 'github',
1109
+ url: process.env.GITHUB_MCP_URL!,
1110
+ headers: { authorization: `Bearer ${process.env.GITHUB_MCP_TOKEN!}` },
1111
+ }),
1112
+ },
1113
+ {
1114
+ allow: ['github.search_*', 'github.get_*'],
1115
+ readOnly: ['github.search_*', 'github.get_*'],
1116
+ },
1117
+ )
1118
+
1119
+ const { object } = yield* generateObjectWithTools({
1120
+ prompt,
1121
+ tools: github.tools,
1122
+ schema: Result,
1123
+ })
1124
+ ```
1125
+
1126
+ `github.tools` is a `Record<string, AnyTool>` keyed by the namespaced tag (`github.get_issue`) — the same shape [`appTools`](/docs/ai/tools) produces, so one agent can mix its own tools and an external server's.
1127
+
1128
+ For a locally spawned server, use the stdio transport instead:
1129
+
1130
+ ```ts
1131
+ import { stdioMcpTransport } from '@voltro/ai'
1132
+
1133
+ const files = yield* mcpToolset(
1134
+ {
1135
+ namespace: 'files',
1136
+ transport: stdioMcpTransport({
1137
+ server: 'files',
1138
+ command: 'npx',
1139
+ args: ['-y', 'some-mcp-server'],
1140
+ // The child does NOT inherit your process environment. Pass only what it needs.
1141
+ env: { HOME: process.env.HOME! },
1142
+ }),
1143
+ },
1144
+ { allow: ['files.read_*'], readOnly: ['files.read_*'] },
1145
+ )
1146
+ ```
1147
+
1148
+ Credentials always come from your environment or config. The framework ships no default token for any server.
1149
+
1150
+ ## The policy is the ceiling
1151
+
1152
+ An `exposeAsTool` descriptor executes **your** handler under **the caller's** subject, so an agent's ceiling is that subject's permissions by construction. An external MCP server has no such property — it runs elsewhere, with whatever credentials you gave it. So for external tools the policy *is* the ceiling, and it is deliberately stricter:
1153
+
1154
+ | | App tools (`appTools`) | External tools (`mcpToolset`) |
1155
+ |---|---|---|
1156
+ | Default exposure | Only descriptors annotated `exposeAsTool` | **None** — `allow` is required |
1157
+ | Allow / deny | `passesPolicy`, deny beats allow | The same function, same globs |
1158
+ | Read vs write | The descriptor's `kind` | **The app's `readOnly` list**, not the server's hint |
1159
+ | Writes | `includeWrites: true` | `includeWrites: true` |
1160
+ | Confirm | On for writes by default | On for writes by default |
1161
+
1162
+ Three consequences worth stating outright:
1163
+
1164
+ - **Omitting `allow` is refused, not defaulted.** A descriptor got a per-tool decision when somebody wrote `exposeAsTool` on it. Nobody in your repository wrote anything about a server's 94 tools, so naming what you allow is that decision.
1165
+ - **A server's `readOnlyHint` does not classify a tool.** The server is the untrusted party and can change the hint between two `tools/list` calls, so believing it would be a way to talk past `includeWrites: false`. Set `trustToolHints: true` if you want to delegate that judgement — explicitly, in one place a reviewer can find.
1166
+ - **The gate runs again inside every tool body**, so a tool spliced into the record after mount still cannot reach the server.
1167
+
1168
+ `toolset.specs` is a `SynthesizedTool[]` — the same inventory type app tools produce — so one confirm-UI covers both kinds. `toolset.dropped` lists everything the server advertised that did not mount, and why.
1169
+
1170
+ ## Bounding an untrusted server
1171
+
1172
+ A server's tool names, descriptions, schemas and results all reach your model's context. Every one of them is bounded, and every bound is an option with a default and an environment override:
1173
+
1174
+ | Bound | Default | Env |
1175
+ |---|---|---|
1176
+ | `maxTools` | 64 | `VOLTRO_MCP_MAX_TOOLS` |
1177
+ | `maxDescriptionChars` | 1024 | `VOLTRO_MCP_MAX_DESCRIPTION_CHARS` |
1178
+ | `maxSchemaBytes` | 32 KiB | `VOLTRO_MCP_MAX_SCHEMA_BYTES` |
1179
+ | `maxResultBytes` | 256 KiB | `VOLTRO_MCP_MAX_RESULT_BYTES` |
1180
+ | `maxResponseBytes` | 4 MiB | `VOLTRO_MCP_MAX_RESPONSE_BYTES` |
1181
+ | `requestTimeoutMs` | 30 000 | `VOLTRO_MCP_TIMEOUT_MS` |
1182
+
1183
+ ```ts
1184
+ yield* mcpToolset(server, {
1185
+ allow: ['github.get_*'],
1186
+ bounds: { maxTools: 12, maxResultBytes: 32 * 1024 },
1187
+ })
1188
+ ```
1189
+
1190
+ Alongside the numbers:
1191
+
1192
+ - **Tool names must be `[A-Za-z0-9_-]`.** Anything else is dropped rather than sanitized — a truncated name would not be the name you allowed.
1193
+ - **Descriptions and schemas are stripped of invisible characters** (zero-width spaces, bidi overrides, the Unicode tags block) before they reach the model. Those are how an injection hides from the human reviewing the same string.
1194
+ - **Every description carries a provenance prefix** telling the model the text is third-party, not an instruction from your app.
1195
+ - **Non-text results are described, not inlined** — a 6 MB base64 image does not buy a context window.
1196
+ - **The tool set is snapshotted at mount.** Nothing re-reads `tools/list` on its own; a server that renames or re-describes its tools between calls changes nothing until you call `refreshMcpToolset`.
1197
+
1198
+ ## What this does not defend against
1199
+
1200
+ Stated plainly, because a bound you assume is worse than one you know you lack:
1201
+
1202
+ - **Instructions inside a description or a result.** They are bounded, sanitized and labelled, but a model may still choose to obey them. What actually contains the damage is the allowlist above: an injected "now call `admin_delete_all`" reaches a tool that was never mounted.
1203
+ - **A server that lies about a tool's effect**, or does something destructive inside a tool you allowed. The ceiling there is the credentials you gave the server — scope them.
1204
+ - **Argument exfiltration.** The allowlist bounds *which* tools run, not what the model puts in their arguments. A mounted external tool is a channel out of your process; do not mount one alongside tools that read secrets and expect the two not to meet.
1205
+ - **The transport target.** `url` / `command` are treated as app configuration. There is no SSRF guard or binary allowlist, because a legitimate deployment mounts an MCP server on a private address. If either can be influenced by user or model input in your app, gate it there.
1206
+
1207
+
1208
+
1022
1209
  ---
1023
1210
 
1024
1211
  <!-- source: en/ai/streaming.md -->
@@ -1046,6 +1233,10 @@ import { Schema } from 'effect'
1046
1233
 
1047
1234
  export const supportRun = defineStream({
1048
1235
  name: 'support.run',
1236
+ // Every call spends provider tokens, so this is metered work — guard it
1237
+ // rather than declaring it open, and rate-limit it before it faces the
1238
+ // internet (`@voltro/plugin-ratelimit`).
1239
+ guards: [{ scope: 'support:chat' }],
1049
1240
  input: Schema.Struct({ message: Schema.String }),
1050
1241
  element: AgentEvent,
1051
1242
  })
@@ -1877,6 +2068,78 @@ Nothing about this needs a third party to operate an inference tier. It needs so
1877
2068
 
1878
2069
  The Flow tab shows the queue: what is waiting and for how long, which calls have been waiting more than two minutes, which claims have a lease their dispatcher will never release, and the dispatcher's own last tick. A run parked on an offloaded call reads `suspended` in the run list with no step row yet, so this is the only view of the wait while it is happening.
1879
2070
 
2071
+ ## Budget SUSPEND — stop spending without destroying the run
2072
+
2073
+ `requireAiBudget` above fails ONE call. That does stop the spend, and it does it
2074
+ by killing a durable run that may be nine steps in — the work is lost, and lost
2075
+ again on every retry until somebody raises the limit. A ceiling whose only
2076
+ expression is destruction gets set high, or turned off. `defineCostBudget` sits
2077
+ at the other extreme: it is an observability-grade signal over work that already
2078
+ happened, and never blocks anything.
2079
+
2080
+ The third answer is the one the workflow engine already knows how to do for a
2081
+ human: **suspend**. `aiStep` / `aiObjectStep` take a `budget`:
2082
+
2083
+ ```ts
2084
+ import { aiStep } from '@voltro/ai/workflow'
2085
+
2086
+ const summary = yield* aiStep({
2087
+ name: 'summarise-thread',
2088
+ prompt: `Summarise:\n${thread}`,
2089
+ store: ctx.store,
2090
+ tenantId,
2091
+ budget: {
2092
+ limitUsd: 50,
2093
+ estimateUsd: 0.25,
2094
+ onExceeded: 'suspend',
2095
+ },
2096
+ })
2097
+ ```
2098
+
2099
+ Over the cap, the run parks on a durable `_voltro_budget_holds` row, frees its
2100
+ worker, and resumes when the budget has headroom — then continues from where it
2101
+ stopped. Nothing is spent while it is held, and nothing is lost.
2102
+
2103
+ **The ordering is the feature.** The gate reads the *reservation counter* before
2104
+ the journaled step and before an offloaded call is enqueued — not a sum over
2105
+ `_voltro_ai_usage`, which by definition only knows about money already gone. On
2106
+ the suspend path no provider is contacted and no queue row exists for a
2107
+ dispatcher to pick up. Under the cap, `estimateUsd` is RESERVED atomically before
2108
+ the call, which is the difference between a ceiling and a speed bump.
2109
+
2110
+ **A release wakes a run; it does not authorise a spend.** Every wake re-reads the
2111
+ budget and parks again if it is still over, so an operator lifting the wrong
2112
+ hold — or a window rolling over for a tenant that immediately spends again —
2113
+ cannot spend through the ceiling. Three things can wake a hold:
2114
+
2115
+ - its own durable recheck clock (15 minutes by default, `recheckEveryMs`), so a
2116
+ tumbling window that rolls over on a clock nothing notifies us about is still
2117
+ noticed;
2118
+ - `releaseBudgetHolds({ store, budget, tenantId? })`, called from your own code
2119
+ — an admin mutation, or a subscriber on `defineCostBudget`'s `recovered`
2120
+ signal. The framework does not subscribe for you: whether a compute budget
2121
+ recovering should wake AI holds is an app decision, and the recheck clock
2122
+ already guarantees the run is not stranded either way;
2123
+ - its total timeout (`holdTimeoutMs`, 7 days), after which the run fails having
2124
+ spent nothing.
2125
+
2126
+ `onExceeded: 'fail'` is the default, so an existing `budget` behaves exactly like
2127
+ `requireAiBudget`. A compute budget opts in the same way:
2128
+
2129
+ ```ts
2130
+ import { defineCostBudget } from '@voltro/runtime'
2131
+
2132
+ export default defineCostBudget({
2133
+ name: 'tenant-recompute-hourly',
2134
+ unit: 'recompute',
2135
+ limit: 100_000,
2136
+ window: '1h',
2137
+ onExceeded: 'suspend', // default 'observe' — signal only
2138
+ })
2139
+ ```
2140
+
2141
+ Held runs are visible via `pendingBudgetHolds(store)`.
2142
+
1880
2143
  ## Deliberately your call
1881
2144
 
1882
2145
  The toolkit prices + records + gates; a few things stay explicit by design:
@@ -1896,6 +2159,110 @@ For quota tied to billing TIERS (not a raw USD cap), see
1896
2159
 
1897
2160
 
1898
2161
 
2162
+ ---
2163
+
2164
+ <!-- source: en/ai/prompt-versioning.md -->
2165
+ ## Prompt versioning
2166
+
2167
+ _`definePrompt` makes a prompt an identified, versioned artefact — and stamps that identity onto the step row, the spend ledger and a `_voltro_prompts` table, so "which prompt produced this run" is a lookup._
2168
+
2169
+ Your schema is versioned. Your rows carry provenance. Your prompts — the part of an AI feature that changes weekly and is edited by whoever is nearest — were code with no identity at all.
2170
+
2171
+ `definePrompt` closes that asymmetry. It reuses the digest `aiStep` already recorded on step rows rather than inventing a second identity scheme.
2172
+
2173
+ ## Declaring a prompt
2174
+
2175
+ ```ts
2176
+ import { definePrompt } from '@voltro/ai'
2177
+
2178
+ export const triage = definePrompt({
2179
+ id: 'support.triage',
2180
+ system: 'You triage support tickets. Answer only with the category.',
2181
+ template: 'Ticket:\n{{body}}\n\nCategories: {{categories}}',
2182
+ label: 'v3-shorter-system',
2183
+ })
2184
+ ```
2185
+
2186
+ - **`id`** is the stable identity across revisions, dotted like a descriptor tag.
2187
+ - **`template`** is a string with `{{name}}` placeholders — not a function. A function has no stable content to hash, so a function-built prompt could only be versioned by hashing its *output*, which would make every distinct customer message a new "version".
2188
+ - **`digest`** is computed from the template + system at definition time. It is the version, and it exists before any database does — a test, an eval, or a CLI can identify a prompt version with no connection.
2189
+ - **`label`** is metadata and deliberately **not** part of the digest, so renaming a revision does not fork it.
2190
+
2191
+ Render it to get the text plus a stamp:
2192
+
2193
+ ```ts
2194
+ const rendered = yield* triage.render({ body: ticket.body, categories: 'hardware, software, billing' })
2195
+ // { promptId: 'support.triage', digest: 'sha256:…', prompt: 'Ticket:\n…', system: '…' }
2196
+ ```
2197
+
2198
+ A missing variable **fails** rather than sending the literal `{{body}}` to a model.
2199
+
2200
+ ## Provenance you get by using the primitive
2201
+
2202
+ Pass the rendered prompt straight to `aiStep` — the string form still works, it just carries no stamp:
2203
+
2204
+ ```ts
2205
+ const result = yield* aiStep({
2206
+ name: 'triage-ticket',
2207
+ prompt: rendered,
2208
+ store: ctx.store,
2209
+ offload: true,
2210
+ })
2211
+ ```
2212
+
2213
+ That one call now writes the same `promptId` + digest to three places:
2214
+
2215
+ | Where | What it answers |
2216
+ |---|---|
2217
+ | `_voltro_workflow_run_steps.input` | Which prompt version produced **this run** |
2218
+ | `_voltro_ai_usage` | What each prompt version **cost** |
2219
+ | `_voltro_prompts` | What the template **was**, at that version |
2220
+
2221
+ Nothing has to be remembered at call time, and offloaded calls are covered too — the stamp rides across the suspend on the queue row, so the dispatcher attributes the spend to the same version.
2222
+
2223
+ `recordPrompt: 'none'` still records the provenance. That is deliberate: the reason to record nothing about a prompt is that its *text* is sensitive, and an id plus a content digest is neither the text nor derivable from it. Suppressing the identity along with the content would mean the most privacy-conscious setting is also the one where you cannot tell which prompt version ran.
2224
+
2225
+ ## Reading it back
2226
+
2227
+ ```ts
2228
+ import { aiSpendUsd, promptVersionByDigest, promptVersionsFor } from '@voltro/ai'
2229
+
2230
+ // From a run's step row → the artefact.
2231
+ const version = yield* promptVersionByDigest(ctx.store, stepInput.promptVersion)
2232
+ version?.template // the template, as it was
2233
+
2234
+ // The history of one prompt, newest revision first.
2235
+ const history = yield* promptVersionsFor(ctx.store, 'support.triage')
2236
+
2237
+ // Did revision 4 cost more than revision 3?
2238
+ const spend = yield* aiSpendUsd(ctx.store, { promptDigest: version.digest })
2239
+ ```
2240
+
2241
+ `recordPromptVersion` assigns the human-facing `revision` (1, 2, 3 …) on first sight of a `(promptId, digest)` pair and is idempotent afterwards. Under a two-replica race two different versions can land on the same revision **number** — accepted deliberately: the digest is the exact identity, the revision is a label for humans, and order by `firstSeenAt` when you need the true sequence.
2242
+
2243
+ ## Retention
2244
+
2245
+ `_voltro_prompts` is bounded by the standard retention sweep on `lastUsedAt`, defaulting to **365 days** and tunable with `VOLTRO_AI_PROMPTS_TTL_HOURS`. The table is self-healing under it: a version that ages out is one nothing has run in a year, and the next run re-registers it.
2246
+
2247
+ The registration is `framework`-precedence, so an app that registers its own window for the table wins without having to know the framework's exists.
2248
+
2249
+ ## The table
2250
+
2251
+ ```text
2252
+ _voltro_prompts
2253
+ promptId stable identity across revisions
2254
+ digest content digest of template + system — the version
2255
+ revision human-facing counter within a promptId
2256
+ label the author's name for this revision
2257
+ template the TEMPLATE (code) — never a rendered prompt (data)
2258
+ system
2259
+ firstSeenAt / lastUsedAt
2260
+ ```
2261
+
2262
+ Storing the template is safe precisely because it is code — it is in your repository already. The rendered prompt, which may contain a customer's message, is never written here.
2263
+
2264
+
2265
+
1899
2266
  ---
1900
2267
 
1901
2268
  <!-- source: en/ai/data-copilot.md -->
@@ -1929,6 +2296,9 @@ import { Schema } from 'effect'
1929
2296
 
1930
2297
  export const ask = defineAction({
1931
2298
  name: 'copilot.ask',
2299
+ // A model-proposed read, executed as the caller. The grammar bounds WHAT can
2300
+ // be asked; this bounds WHO may ask.
2301
+ guards: [{ scope: 'copilot:ask' }],
1932
2302
  input: Schema.Struct({ question: Schema.String }),
1933
2303
  output: Schema.Union(
1934
2304
  Schema.Struct({ ok: Schema.Literal(true), rows: Schema.Array(Schema.Record({ key: Schema.String, value: Schema.Unknown })) }),