blume 2.0.0 → 2.0.2

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 (454) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +110 -0
  3. package/README.md +2 -2
  4. package/dist/cli/{chunk-f2972sbt.js → chunk-00gs3wqs.js} +1 -1
  5. package/dist/cli/{chunk-fa25z98p.js → chunk-273ygyr4.js} +4 -3
  6. package/dist/cli/chunk-273ygyr4.js.map +11 -0
  7. package/dist/cli/{chunk-pat2zzwc.js → chunk-3yce002v.js} +21 -8
  8. package/dist/cli/{chunk-pat2zzwc.js.map → chunk-3yce002v.js.map} +4 -4
  9. package/dist/cli/{chunk-d1tadaw7.js → chunk-4e9b9ra6.js} +17 -7
  10. package/dist/cli/chunk-4e9b9ra6.js.map +10 -0
  11. package/dist/cli/{chunk-zg2gtj10.js → chunk-5r8g91qn.js} +352 -676
  12. package/dist/cli/chunk-5r8g91qn.js.map +34 -0
  13. package/dist/cli/{chunk-11j0384y.js → chunk-6dtt0zfn.js} +14 -14
  14. package/dist/cli/{chunk-11j0384y.js.map → chunk-6dtt0zfn.js.map} +3 -3
  15. package/dist/cli/{chunk-bw22s759.js → chunk-6k4ftwze.js} +38 -19
  16. package/dist/cli/{chunk-bw22s759.js.map → chunk-6k4ftwze.js.map} +4 -4
  17. package/dist/cli/{chunk-sqn5t4q0.js → chunk-7mbqtmgb.js} +16 -7
  18. package/dist/cli/chunk-7mbqtmgb.js.map +10 -0
  19. package/dist/cli/{chunk-79njf86q.js → chunk-7vtckvaw.js} +21 -19
  20. package/dist/cli/chunk-7vtckvaw.js.map +11 -0
  21. package/dist/cli/{chunk-fh5hj5jt.js → chunk-8g8ytmgx.js} +17 -18
  22. package/dist/cli/{chunk-fh5hj5jt.js.map → chunk-8g8ytmgx.js.map} +2 -2
  23. package/dist/cli/{chunk-1w8dp3qb.js → chunk-91ws1n6j.js} +18 -15
  24. package/dist/cli/chunk-91ws1n6j.js.map +10 -0
  25. package/dist/cli/{chunk-7ez8ny0t.js → chunk-bbnwccaz.js} +2 -2
  26. package/dist/cli/{chunk-5a2z0198.js → chunk-bfwp9vp6.js} +18 -10
  27. package/dist/cli/chunk-bfwp9vp6.js.map +10 -0
  28. package/dist/cli/{chunk-ernrthtr.js → chunk-d1v5rhy0.js} +14 -15
  29. package/dist/cli/{chunk-ernrthtr.js.map → chunk-d1v5rhy0.js.map} +2 -2
  30. package/dist/cli/{chunk-a9kptbw5.js → chunk-ddndchfr.js} +21 -6
  31. package/dist/cli/chunk-ddndchfr.js.map +14 -0
  32. package/dist/cli/{chunk-zxccj738.js → chunk-esh98wmb.js} +1 -1
  33. package/dist/cli/{chunk-88cpgt6h.js → chunk-fsmrqk8a.js} +1 -1
  34. package/dist/cli/{chunk-b5aj94ah.js → chunk-g698a744.js} +5 -5
  35. package/dist/cli/{chunk-jts8mvcz.js → chunk-gs7r695n.js} +9 -3
  36. package/dist/cli/{chunk-jts8mvcz.js.map → chunk-gs7r695n.js.map} +3 -3
  37. package/dist/cli/{chunk-41za066z.js → chunk-h2ez8dzb.js} +4 -4
  38. package/dist/cli/{chunk-by2290sx.js → chunk-h7k3nq3v.js} +2 -2
  39. package/dist/cli/{chunk-j8mw0za6.js → chunk-hqp2ajnh.js} +252 -123
  40. package/dist/cli/chunk-hqp2ajnh.js.map +35 -0
  41. package/dist/cli/{chunk-mnqj32sj.js → chunk-hr8ne106.js} +119 -50
  42. package/dist/cli/chunk-hr8ne106.js.map +13 -0
  43. package/dist/cli/{chunk-mwt1k8n7.js → chunk-j85scx15.js} +74 -30
  44. package/dist/cli/chunk-j85scx15.js.map +10 -0
  45. package/dist/cli/{chunk-z01ze5c1.js → chunk-k7pj68a8.js} +63 -22
  46. package/dist/cli/chunk-k7pj68a8.js.map +11 -0
  47. package/dist/cli/{chunk-2q1dwty4.js → chunk-mqc662a6.js} +8 -8
  48. package/dist/cli/{chunk-2q1dwty4.js.map → chunk-mqc662a6.js.map} +4 -4
  49. package/dist/cli/{chunk-y3e45rc8.js → chunk-n1yg3tj3.js} +4 -4
  50. package/dist/cli/{chunk-bctazmbk.js → chunk-nfcyttvj.js} +17 -6
  51. package/dist/cli/chunk-nfcyttvj.js.map +10 -0
  52. package/dist/cli/{chunk-6k8vp3ta.js → chunk-pbg5a4s3.js} +60 -28
  53. package/dist/cli/chunk-pbg5a4s3.js.map +19 -0
  54. package/dist/cli/{chunk-d80hr03s.js → chunk-ppzjqwx2.js} +24 -17
  55. package/dist/cli/{chunk-d80hr03s.js.map → chunk-ppzjqwx2.js.map} +4 -4
  56. package/dist/cli/{chunk-beat36xx.js → chunk-qkb5a8sa.js} +25 -10
  57. package/dist/cli/chunk-qkb5a8sa.js.map +10 -0
  58. package/dist/cli/{chunk-bnbmcwfb.js → chunk-sqw4ekg1.js} +5 -5
  59. package/dist/cli/{chunk-bnbmcwfb.js.map → chunk-sqw4ekg1.js.map} +3 -3
  60. package/dist/cli/{chunk-xaz13gwg.js → chunk-v6ya5kcb.js} +3268 -1150
  61. package/dist/cli/chunk-v6ya5kcb.js.map +189 -0
  62. package/dist/cli/{chunk-tzne8qfq.js → chunk-w4bxdvsa.js} +18 -15
  63. package/dist/cli/chunk-w4bxdvsa.js.map +10 -0
  64. package/dist/cli/{chunk-f7t03s3g.js → chunk-wdrt2k2v.js} +2 -2
  65. package/dist/cli/{chunk-z1f5arsg.js → chunk-xh43dwgw.js} +170 -55
  66. package/dist/cli/chunk-xh43dwgw.js.map +36 -0
  67. package/dist/cli/{chunk-pnnvybbk.js → chunk-zp79m0ts.js} +5 -5
  68. package/dist/cli/{chunk-pnnvybbk.js.map → chunk-zp79m0ts.js.map} +2 -2
  69. package/dist/cli/index.js +160 -35
  70. package/dist/cli/index.js.map +4 -4
  71. package/dist/types/ai/agent-readability.d.ts +1 -1
  72. package/dist/types/ai/agent-surface.d.ts +32 -0
  73. package/dist/types/ai/api/paths.d.ts +1 -1
  74. package/dist/types/ai/api-catalog.d.ts +7 -1
  75. package/dist/types/ai/ask-context.d.ts +14 -7
  76. package/dist/types/ai/ask.d.ts +43 -43
  77. package/dist/types/ai/component-markdown.d.ts +4 -4
  78. package/dist/types/ai/index.d.ts +3 -3
  79. package/dist/types/ai/openapi-components.d.ts +1 -1
  80. package/dist/types/ai/relative-links.d.ts +9 -4
  81. package/dist/types/ai/serializers.d.ts +1 -1
  82. package/dist/types/ai/static-expression.d.ts +28 -0
  83. package/dist/types/ai/visibility.d.ts +1 -1
  84. package/dist/types/analytics/databuddy.d.ts +43 -0
  85. package/dist/types/analytics/index.d.ts +2 -0
  86. package/dist/types/analytics/schema.d.ts +14 -0
  87. package/dist/types/cli/env.d.ts +5 -0
  88. package/dist/types/cli/init/scaffold.d.ts +19 -3
  89. package/dist/types/cli/init/starter-spec.d.ts +11 -0
  90. package/dist/types/core/base-path.d.ts +9 -0
  91. package/dist/types/core/config-input.d.ts +21 -21
  92. package/dist/types/core/config.d.ts +5 -5
  93. package/dist/types/core/data.d.ts +3 -3
  94. package/dist/types/core/graph.d.ts +2 -0
  95. package/dist/types/core/i18n-ui.d.ts +37 -8
  96. package/dist/types/core/i18n.d.ts +7 -1
  97. package/dist/types/core/links.d.ts +3 -1
  98. package/dist/types/core/load-module.d.ts +10 -0
  99. package/dist/types/core/locale-links.d.ts +12 -2
  100. package/dist/types/core/meta.d.ts +5 -1
  101. package/dist/types/core/nav-diagnostics.d.ts +10 -0
  102. package/dist/types/core/navigation.d.ts +38 -0
  103. package/dist/types/core/ordering-prefix.d.ts +4 -0
  104. package/dist/types/core/safe-links.d.ts +3 -1
  105. package/dist/types/core/schema.d.ts +57 -18
  106. package/dist/types/core/sources/github-releases.d.ts +5 -0
  107. package/dist/types/core/sources/lower.d.ts +14 -2
  108. package/dist/types/core/sources/normalize.d.ts +10 -1
  109. package/dist/types/core/sources/remote.d.ts +11 -1
  110. package/dist/types/core/sources/resolve.d.ts +12 -0
  111. package/dist/types/core/sources/types.d.ts +31 -0
  112. package/dist/types/core/types.d.ts +9 -0
  113. package/dist/types/core/unrecognized-keys.d.ts +1 -1
  114. package/dist/types/deploy/adapters/node.d.ts +5 -2
  115. package/dist/types/deploy/adapters/types.d.ts +7 -0
  116. package/dist/types/deploy/cloudflare-negotiation.d.ts +3 -2
  117. package/dist/types/deploy/headers.d.ts +31 -7
  118. package/dist/types/deploy/node-headers.d.ts +43 -8
  119. package/dist/types/deploy/platforms/netlify.d.ts +27 -2
  120. package/dist/types/deploy/platforms/node.d.ts +6 -5
  121. package/dist/types/deploy/platforms/types.d.ts +7 -0
  122. package/dist/types/deploy/platforms/vercel.d.ts +3 -2
  123. package/dist/types/deploy/redirects.d.ts +7 -2
  124. package/dist/types/deploy/vercel-negotiation.d.ts +3 -2
  125. package/dist/types/openapi/model.d.ts +20 -6
  126. package/dist/types/search/documents.d.ts +1 -1
  127. package/dist/types/search/orama-index.d.ts +1 -1
  128. package/dist/types/search/sync/algolia.d.ts +3 -1
  129. package/docs/01-quickstart.mdx +3 -2
  130. package/docs/02-deployment.mdx +21 -12
  131. package/docs/03-upgrading.mdx +22 -9
  132. package/docs/04-migrating.mdx +4 -4
  133. package/docs/08-faq.mdx +12 -5
  134. package/docs/advanced/changelog.mdx +1 -1
  135. package/docs/advanced/custom-pages.mdx +5 -3
  136. package/docs/advanced/skills.mdx +1 -1
  137. package/docs/cli/audit.mdx +5 -5
  138. package/docs/cli/doctor.mdx +4 -4
  139. package/docs/cli/evals.mdx +9 -9
  140. package/docs/cli/index.mdx +6 -3
  141. package/docs/cli/translate.mdx +8 -8
  142. package/docs/configuration/analytics.mdx +23 -2
  143. package/docs/configuration/{ask-ai.mdx → assistant.mdx} +20 -20
  144. package/docs/configuration/customization.mdx +5 -4
  145. package/docs/configuration/index.mdx +7 -5
  146. package/docs/configuration/meta.ts +1 -1
  147. package/docs/configuration/search.mdx +2 -2
  148. package/docs/content/components.mdx +1 -1
  149. package/docs/content/frontmatter.mdx +5 -1
  150. package/docs/content/i18n.mdx +3 -3
  151. package/docs/content/index.mdx +4 -2
  152. package/docs/content/islands.mdx +1 -1
  153. package/docs/content/meta.mdx +5 -3
  154. package/docs/content/navigation.mdx +29 -4
  155. package/docs/content/sources.mdx +18 -12
  156. package/docs/content/versioning.mdx +1 -0
  157. package/docs/discoverability/agent-discovery.mdx +22 -8
  158. package/docs/discoverability/index.mdx +3 -3
  159. package/docs/discoverability/llms-txt.mdx +2 -5
  160. package/docs/discoverability/markdown.mdx +5 -3
  161. package/docs/discoverability/mcp.mdx +4 -0
  162. package/docs/discoverability/metadata.mdx +3 -2
  163. package/docs/discoverability/open-graph.mdx +6 -4
  164. package/docs/discoverability/rss.mdx +3 -1
  165. package/docs/index.mdx +2 -2
  166. package/docs/references/asyncapi.mdx +1 -1
  167. package/docs/references/graphql.mdx +2 -2
  168. package/docs/references/openapi.mdx +3 -3
  169. package/package.json +1 -1
  170. package/skills/blume/SKILL.md +5 -5
  171. package/skills/blume-migrate/SKILL.md +6 -6
  172. package/skills/blume-migrate/references/docusaurus.md +6 -6
  173. package/skills/blume-migrate/references/mintlify.md +3 -3
  174. package/skills/blume-migrate/references/monorepo.md +1 -1
  175. package/skills/blume-migrate/references/nextra.md +1 -1
  176. package/skills/blume-migrate/references/starlight.md +5 -5
  177. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +2 -2
  178. package/skills/blume-update-docs/references/audit-checklist.md +1 -1
  179. package/src/ai/agent-readability.ts +4 -4
  180. package/src/ai/agent-surface.ts +56 -0
  181. package/src/ai/api/paths.ts +1 -1
  182. package/src/ai/api/spec.ts +15 -2
  183. package/src/ai/api-catalog.ts +7 -1
  184. package/src/ai/ask-context.ts +21 -9
  185. package/src/ai/ask-data.ts +28 -14
  186. package/src/ai/ask.ts +84 -71
  187. package/src/ai/component-markdown.ts +43 -37
  188. package/src/ai/cors.ts +3 -3
  189. package/src/ai/index.ts +16 -16
  190. package/src/ai/link-headers.ts +8 -3
  191. package/src/ai/llms.ts +4 -3
  192. package/src/ai/markdown.ts +23 -11
  193. package/src/ai/mcp/server.ts +78 -7
  194. package/src/ai/openapi-components.ts +1 -1
  195. package/src/ai/relative-links.ts +78 -10
  196. package/src/ai/serializers.ts +1 -1
  197. package/src/ai/static-expression.ts +416 -0
  198. package/src/ai/visibility.ts +46 -15
  199. package/src/analytics/databuddy.ts +67 -0
  200. package/src/analytics/head.ts +4 -0
  201. package/src/analytics/index.ts +2 -0
  202. package/src/analytics/posthog.ts +21 -3
  203. package/src/analytics/schema.ts +2 -0
  204. package/src/astro/generate.ts +90 -45
  205. package/src/astro/module-types.ts +1 -1
  206. package/src/astro/runtime-deps.ts +6 -6
  207. package/src/astro/templates.ts +87 -49
  208. package/src/audit/catalog.ts +2 -2
  209. package/src/audit/checks/assets.ts +25 -5
  210. package/src/audit/checks/content.ts +20 -2
  211. package/src/audit/checks/i18n.ts +46 -8
  212. package/src/audit/checks/indexability.ts +39 -19
  213. package/src/audit/checks/links.ts +14 -0
  214. package/src/audit/checks/llms.ts +6 -3
  215. package/src/audit/checks/network.ts +3 -1
  216. package/src/audit/checks/og-image.ts +10 -0
  217. package/src/audit/checks/robots.ts +6 -1
  218. package/src/audit/checks/sitemap.ts +56 -31
  219. package/src/audit/checks/social.ts +21 -2
  220. package/src/audit/crawl.ts +88 -15
  221. package/src/audit/report.ts +54 -17
  222. package/src/audit/run.ts +1 -0
  223. package/src/audit/types.ts +18 -0
  224. package/src/audit/url.ts +37 -6
  225. package/src/blume-modules.d.ts +2 -2
  226. package/src/cli/build-failure.ts +50 -0
  227. package/src/cli/commands/audit.ts +8 -1
  228. package/src/cli/commands/build.ts +19 -5
  229. package/src/cli/commands/check.ts +2 -0
  230. package/src/cli/commands/doctor.ts +7 -5
  231. package/src/cli/commands/eval.ts +4 -10
  232. package/src/cli/commands/init.ts +15 -5
  233. package/src/cli/commands/migrate.ts +2 -2
  234. package/src/cli/commands/preview.ts +15 -0
  235. package/src/cli/commands/sync.ts +2 -0
  236. package/src/cli/commands/translate.ts +12 -9
  237. package/src/cli/commands/upgrade.ts +13 -2
  238. package/src/cli/eject-scripts.ts +32 -7
  239. package/src/cli/env.ts +12 -1
  240. package/src/cli/init/scaffold.ts +117 -11
  241. package/src/cli/init/starter-spec.ts +235 -0
  242. package/src/cli/required-secrets.ts +3 -3
  243. package/src/components/content/AccordionItem.astro +26 -22
  244. package/src/components/content/Badge.astro +2 -9
  245. package/src/components/content/Card.astro +2 -2
  246. package/src/components/content/Component.astro +9 -3
  247. package/src/components/content/Expandable.astro +5 -1
  248. package/src/components/content/Frame.astro +2 -7
  249. package/src/components/content/GithubInfo.astro +12 -2
  250. package/src/components/content/Prompt.astro +2 -7
  251. package/src/components/content/Tabs.astro +54 -5
  252. package/src/components/content/Tile.astro +1 -1
  253. package/src/components/content/Tooltip.astro +69 -7
  254. package/src/components/content/Tree.astro +7 -2
  255. package/src/components/content/TypeTable.astro +10 -5
  256. package/src/components/content/Update.astro +8 -2
  257. package/src/components/content/auto-type-table.ts +4 -1
  258. package/src/components/content/badge-color.ts +17 -0
  259. package/src/components/content/base-href.ts +18 -3
  260. package/src/components/content/inline-markdown.ts +27 -7
  261. package/src/components/copy-feedback.ts +36 -9
  262. package/src/components/islands/{AskAI.astro → Assistant.astro} +10 -10
  263. package/src/components/islands/{ask-ai.tsx → assistant.tsx} +39 -25
  264. package/src/components/islands/hooks.ts +21 -14
  265. package/src/components/islands/webmcp.ts +12 -8
  266. package/src/components/layout/Banner.astro +23 -4
  267. package/src/components/layout/Header.astro +32 -19
  268. package/src/components/layout/Logo.astro +5 -0
  269. package/src/components/layout/NavSelector.astro +6 -1
  270. package/src/components/layout/NavTabMenu.astro +133 -0
  271. package/src/components/layout/NavTree.astro +15 -6
  272. package/src/components/layout/NavTreeCache.astro +5 -2
  273. package/src/components/layout/NavTreeScript.astro +45 -5
  274. package/src/components/layout/PageLayout.astro +28 -14
  275. package/src/components/layout/Pagination.astro +7 -7
  276. package/src/components/layout/ReferenceLayout.astro +5 -2
  277. package/src/components/layout/RootLayout.astro +27 -14
  278. package/src/components/layout/Search.astro +18 -14
  279. package/src/components/layout/analytics-client.ts +3 -1
  280. package/src/components/layout/drawer-inert.ts +1 -1
  281. package/src/components/openapi/GraphqlType.astro +11 -3
  282. package/src/components/openapi/MessageComposer.astro +1 -1
  283. package/src/components/openapi/Operation.astro +21 -5
  284. package/src/components/openapi/PanelTabs.astro +4 -1
  285. package/src/components/openapi/Playground.astro +8 -2
  286. package/src/components/openapi/RequestPanel.astro +9 -5
  287. package/src/components/openapi/SchemaProperty.astro +11 -33
  288. package/src/components/openapi/SchemaTable.astro +27 -52
  289. package/src/components/openapi/description.ts +2 -2
  290. package/src/components/openapi/helpers.ts +90 -12
  291. package/src/components/openapi/message-composer.ts +8 -0
  292. package/src/components/openapi/message-model.ts +12 -2
  293. package/src/components/openapi/message.ts +4 -1
  294. package/src/components/openapi/operation-model.ts +40 -8
  295. package/src/components/openapi/panel.ts +29 -5
  296. package/src/components/openapi/playground-client.ts +37 -7
  297. package/src/components/openapi/playground-schema.ts +25 -6
  298. package/src/components/openapi/request.ts +2 -0
  299. package/src/components/openapi/schema-tree.ts +203 -0
  300. package/src/components/openapi/snippets.ts +30 -15
  301. package/src/components/openapi/validate-json.ts +1 -1
  302. package/src/core/base-path.ts +15 -0
  303. package/src/core/code-fences.ts +1 -1
  304. package/src/core/config-input.ts +23 -23
  305. package/src/core/config.ts +21 -7
  306. package/src/core/data.ts +3 -3
  307. package/src/core/diagnostics.ts +217 -30
  308. package/src/core/graph.ts +120 -11
  309. package/src/core/i18n-ui.ts +74 -11
  310. package/src/core/i18n.ts +13 -2
  311. package/src/core/links.ts +7 -4
  312. package/src/core/load-module.ts +20 -0
  313. package/src/core/locale-links.ts +17 -17
  314. package/src/core/manifest.ts +3 -2
  315. package/src/core/meta.ts +69 -6
  316. package/src/core/nav-diagnostics.ts +56 -1
  317. package/src/core/navigation.ts +245 -76
  318. package/src/core/ordering-prefix.ts +27 -0
  319. package/src/core/project-graph.ts +23 -1
  320. package/src/core/request-body.ts +1 -1
  321. package/src/core/safe-href.ts +53 -1
  322. package/src/core/safe-links.ts +11 -2
  323. package/src/core/schema.ts +95 -34
  324. package/src/core/server-features.ts +2 -2
  325. package/src/core/sources/assets.ts +83 -41
  326. package/src/core/sources/contentful-rich-text.ts +3 -2
  327. package/src/core/sources/contentful.ts +25 -12
  328. package/src/core/sources/filesystem.ts +5 -1
  329. package/src/core/sources/github-releases.ts +106 -5
  330. package/src/core/sources/lexical.ts +9 -4
  331. package/src/core/sources/lower.ts +79 -26
  332. package/src/core/sources/mdx-remote.ts +1 -0
  333. package/src/core/sources/normalize.ts +132 -30
  334. package/src/core/sources/notion.ts +26 -10
  335. package/src/core/sources/obsidian.ts +17 -3
  336. package/src/core/sources/payload.ts +1 -0
  337. package/src/core/sources/portable-text.ts +63 -44
  338. package/src/core/sources/remote.ts +18 -2
  339. package/src/core/sources/resolve.ts +52 -33
  340. package/src/core/sources/sanity.ts +1 -0
  341. package/src/core/sources/strapi-blocks.ts +9 -4
  342. package/src/core/sources/strapi.ts +1 -0
  343. package/src/core/sources/types.ts +31 -0
  344. package/src/core/types.ts +9 -0
  345. package/src/core/ui-packs/ar.ts +17 -5
  346. package/src/core/ui-packs/bg.ts +17 -5
  347. package/src/core/ui-packs/bn.ts +17 -5
  348. package/src/core/ui-packs/ca.ts +17 -5
  349. package/src/core/ui-packs/cs.ts +17 -5
  350. package/src/core/ui-packs/da.ts +17 -5
  351. package/src/core/ui-packs/de.ts +17 -5
  352. package/src/core/ui-packs/el.ts +17 -5
  353. package/src/core/ui-packs/es.ts +17 -5
  354. package/src/core/ui-packs/fa.ts +17 -5
  355. package/src/core/ui-packs/fi.ts +17 -5
  356. package/src/core/ui-packs/fr.ts +17 -5
  357. package/src/core/ui-packs/he.ts +17 -5
  358. package/src/core/ui-packs/hi.ts +17 -5
  359. package/src/core/ui-packs/hr.ts +17 -5
  360. package/src/core/ui-packs/hu.ts +17 -5
  361. package/src/core/ui-packs/id.ts +17 -5
  362. package/src/core/ui-packs/it.ts +17 -5
  363. package/src/core/ui-packs/ja.ts +17 -5
  364. package/src/core/ui-packs/ko.ts +17 -5
  365. package/src/core/ui-packs/nl.ts +17 -5
  366. package/src/core/ui-packs/no.ts +17 -5
  367. package/src/core/ui-packs/pl.ts +17 -5
  368. package/src/core/ui-packs/pt-br.ts +17 -5
  369. package/src/core/ui-packs/pt.ts +17 -5
  370. package/src/core/ui-packs/ro.ts +17 -5
  371. package/src/core/ui-packs/ru.ts +17 -5
  372. package/src/core/ui-packs/sk.ts +17 -5
  373. package/src/core/ui-packs/sr.ts +17 -5
  374. package/src/core/ui-packs/sv.ts +17 -5
  375. package/src/core/ui-packs/th.ts +17 -5
  376. package/src/core/ui-packs/tr.ts +17 -5
  377. package/src/core/ui-packs/uk.ts +17 -5
  378. package/src/core/ui-packs/vi.ts +17 -5
  379. package/src/core/ui-packs/zh-tw.ts +17 -5
  380. package/src/core/ui-packs/zh.ts +17 -5
  381. package/src/core/unrecognized-keys.ts +1 -1
  382. package/src/core/version-cut.ts +17 -2
  383. package/src/core/versions.ts +4 -1
  384. package/src/deploy/adapters/node.ts +5 -2
  385. package/src/deploy/adapters/registry.ts +2 -1
  386. package/src/deploy/adapters/types.ts +13 -1
  387. package/src/deploy/artifacts.ts +25 -5
  388. package/src/deploy/cloudflare-negotiation.ts +23 -4
  389. package/src/deploy/headers.ts +67 -51
  390. package/src/deploy/node-headers.ts +148 -27
  391. package/src/deploy/platforms/cloudflare.ts +1 -0
  392. package/src/deploy/platforms/netlify.ts +82 -5
  393. package/src/deploy/platforms/node.ts +7 -5
  394. package/src/deploy/platforms/static.ts +1 -0
  395. package/src/deploy/platforms/types.ts +7 -0
  396. package/src/deploy/platforms/vercel.ts +9 -3
  397. package/src/deploy/redirects.ts +14 -3
  398. package/src/deploy/vercel-negotiation.ts +35 -3
  399. package/src/eval/agents.ts +10 -2
  400. package/src/eval/run.ts +25 -0
  401. package/src/markdown/base-links.ts +55 -8
  402. package/src/markdown/index.ts +6 -3
  403. package/src/markdown/relative-links.ts +3 -23
  404. package/src/markdown/route-snapshot.ts +37 -0
  405. package/src/og/card.ts +20 -4
  406. package/src/og/derive.ts +145 -4
  407. package/src/openapi/graphql-build.ts +28 -2
  408. package/src/openapi/model.ts +77 -13
  409. package/src/openapi/render-mdx.ts +10 -1
  410. package/src/registry/eject.ts +135 -37
  411. package/src/search/documents.ts +24 -3
  412. package/src/search/orama-index.ts +1 -1
  413. package/src/search/sync/algolia.ts +36 -2
  414. package/src/sources/registry.ts +5 -0
  415. package/src/theme/entry.ts +11 -4
  416. package/src/translate/agents.ts +6 -1
  417. package/src/translate/ledger.ts +26 -3
  418. package/src/translate/meta.ts +11 -3
  419. package/src/translate/report.ts +1 -1
  420. package/src/translate/run.ts +11 -5
  421. package/src/translate/validate.ts +10 -1
  422. package/src/translate/work-list.ts +36 -3
  423. package/src/upgrade/upgrade.ts +37 -5
  424. package/dist/cli/chunk-1w8dp3qb.js.map +0 -10
  425. package/dist/cli/chunk-5a2z0198.js.map +0 -10
  426. package/dist/cli/chunk-6k8vp3ta.js.map +0 -19
  427. package/dist/cli/chunk-79njf86q.js.map +0 -11
  428. package/dist/cli/chunk-a9kptbw5.js.map +0 -14
  429. package/dist/cli/chunk-abh8yjkn.js +0 -31
  430. package/dist/cli/chunk-abh8yjkn.js.map +0 -10
  431. package/dist/cli/chunk-bctazmbk.js.map +0 -10
  432. package/dist/cli/chunk-beat36xx.js.map +0 -10
  433. package/dist/cli/chunk-d1tadaw7.js.map +0 -10
  434. package/dist/cli/chunk-fa25z98p.js.map +0 -11
  435. package/dist/cli/chunk-j8mw0za6.js.map +0 -35
  436. package/dist/cli/chunk-mnqj32sj.js.map +0 -12
  437. package/dist/cli/chunk-mwt1k8n7.js.map +0 -10
  438. package/dist/cli/chunk-nk3ts2xk.js +0 -51
  439. package/dist/cli/chunk-nk3ts2xk.js.map +0 -10
  440. package/dist/cli/chunk-sqn5t4q0.js.map +0 -10
  441. package/dist/cli/chunk-tzne8qfq.js.map +0 -10
  442. package/dist/cli/chunk-xaz13gwg.js.map +0 -182
  443. package/dist/cli/chunk-z01ze5c1.js.map +0 -10
  444. package/dist/cli/chunk-z1f5arsg.js.map +0 -36
  445. package/dist/cli/chunk-zg2gtj10.js.map +0 -35
  446. /package/dist/cli/{chunk-f2972sbt.js.map → chunk-00gs3wqs.js.map} +0 -0
  447. /package/dist/cli/{chunk-7ez8ny0t.js.map → chunk-bbnwccaz.js.map} +0 -0
  448. /package/dist/cli/{chunk-zxccj738.js.map → chunk-esh98wmb.js.map} +0 -0
  449. /package/dist/cli/{chunk-88cpgt6h.js.map → chunk-fsmrqk8a.js.map} +0 -0
  450. /package/dist/cli/{chunk-b5aj94ah.js.map → chunk-g698a744.js.map} +0 -0
  451. /package/dist/cli/{chunk-41za066z.js.map → chunk-h2ez8dzb.js.map} +0 -0
  452. /package/dist/cli/{chunk-by2290sx.js.map → chunk-h7k3nq3v.js.map} +0 -0
  453. /package/dist/cli/{chunk-y3e45rc8.js.map → chunk-n1yg3tj3.js.map} +0 -0
  454. /package/dist/cli/{chunk-f7t03s3g.js.map → chunk-wdrt2k2v.js.map} +0 -0
package/src/ai/ask.ts CHANGED
@@ -9,7 +9,7 @@ import { unrecognizedKeysMessage } from "../core/unrecognized-keys.ts";
9
9
  * `reasoning` values minus `provider-default`, which is what omitting the
10
10
  * option means.
11
11
  */
12
- export const askReasoningLevels = [
12
+ export const assistantReasoningLevels = [
13
13
  "none",
14
14
  "minimal",
15
15
  "low",
@@ -19,14 +19,17 @@ export const askReasoningLevels = [
19
19
  ] as const;
20
20
 
21
21
  /** How much the model reasons before answering (an adapter's `reasoning`). */
22
- export type AskReasoning = (typeof askReasoningLevels)[number];
22
+ export type AssistantReasoning = (typeof assistantReasoningLevels)[number];
23
23
 
24
24
  /**
25
25
  * The AI SDK's `providerOptions` shape, forwarded to `streamText` verbatim:
26
26
  * `{ [provider]: { [option]: value } }`. The escape hatch for model controls
27
27
  * Blume doesn't name, so a new provider knob never needs a Blume field.
28
28
  */
29
- export type AskProviderOptions = Record<string, Record<string, JsonValue>>;
29
+ export type AssistantProviderOptions = Record<
30
+ string,
31
+ Record<string, JsonValue>
32
+ >;
30
33
 
31
34
  /** The AI SDK provider package the OpenAI-compatible adapters install. */
32
35
  const OPENAI_COMPATIBLE_DEP = "@ai-sdk/openai-compatible";
@@ -35,8 +38,8 @@ const OPENAI_COMPATIBLE_DEP = "@ai-sdk/openai-compatible";
35
38
  // Shared options
36
39
  // ---------------------------------------------------------------------------
37
40
 
38
- /** The options every Ask AI adapter accepts. */
39
- export interface AskAdapterOptions {
41
+ /** The options every assistant adapter accepts. */
42
+ export interface AssistantAdapterOptions {
40
43
  /**
41
44
  * Name of the env var holding the provider's API key. Each adapter has its
42
45
  * own default; set this only to point at a different variable.
@@ -53,7 +56,7 @@ export interface AskAdapterOptions {
53
56
  * Options passed to `streamText` as its `providerOptions`, untouched, in the
54
57
  * AI SDK's own shape (`{ openai: { textVerbosity: "low" } }`, say).
55
58
  */
56
- providerOptions?: AskProviderOptions;
59
+ providerOptions?: AssistantProviderOptions;
57
60
  }
58
61
 
59
62
  /**
@@ -72,7 +75,7 @@ const sharedOptions = (apiKeyEnv: string) => ({
72
75
  providerOptions: providerOptionsSchema.optional(),
73
76
  });
74
77
 
75
- const reasoningOption = z.enum(askReasoningLevels).optional();
78
+ const reasoningOption = z.enum(assistantReasoningLevels).optional();
76
79
 
77
80
  // ---------------------------------------------------------------------------
78
81
  // gateway()
@@ -83,7 +86,7 @@ const GATEWAY_API_KEY_ENV = "AI_GATEWAY_API_KEY";
83
86
  const DEFAULT_GATEWAY_MODEL = "openai/gpt-5.5";
84
87
 
85
88
  /** Options for {@link gateway}. */
86
- export interface AskGatewayOptions extends AskAdapterOptions {
89
+ export interface AssistantGatewayOptions extends AssistantAdapterOptions {
87
90
  /** A `provider/model` id routed by the gateway. Defaults to `openai/gpt-5.5`. */
88
91
  model?: string;
89
92
  /**
@@ -92,7 +95,7 @@ export interface AskGatewayOptions extends AskAdapterOptions {
92
95
  * control (OpenAI's `reasoning_effort`, for example); the model has to
93
96
  * offer the level you pick. Omitted keeps the model's default.
94
97
  */
95
- reasoning?: AskReasoning;
98
+ reasoning?: AssistantReasoning;
96
99
  }
97
100
 
98
101
  const gatewayOptionsSchema = z.strictObject({
@@ -101,7 +104,10 @@ const gatewayOptionsSchema = z.strictObject({
101
104
  reasoning: reasoningOption,
102
105
  });
103
106
 
104
- export type AskGatewayAdapter = AdapterDescriptor<"gateway", AskGatewayOptions>;
107
+ export type AssistantGatewayAdapter = AdapterDescriptor<
108
+ "gateway",
109
+ AssistantGatewayOptions
110
+ >;
105
111
 
106
112
  export const gatewayAdapterSchema = adapterDescriptorSchema(
107
113
  "gateway",
@@ -109,14 +115,14 @@ export const gatewayAdapterSchema = adapterDescriptorSchema(
109
115
  );
110
116
 
111
117
  /**
112
- * Route Ask AI through the Vercel AI Gateway (the default). `model` is a
118
+ * Route the assistant through the Vercel AI Gateway (the default). `model` is a
113
119
  * `provider/model` string; the key is `AI_GATEWAY_API_KEY`, or Vercel's OIDC
114
120
  * token when deployed there. Needs no provider SDK beyond the `ai` package
115
121
  * Blume ships.
116
122
  */
117
123
  export const gateway = (
118
- options: AskGatewayOptions = {}
119
- ): AskGatewayAdapter => ({
124
+ options: AssistantGatewayOptions = {}
125
+ ): AssistantGatewayAdapter => ({
120
126
  kind: "gateway",
121
127
  options,
122
128
  requiredSecrets: [options.apiKeyEnv ?? GATEWAY_API_KEY_ENV],
@@ -130,7 +136,7 @@ export const gateway = (
130
136
  const OPENROUTER_API_KEY_ENV = "OPENROUTER_API_KEY";
131
137
 
132
138
  /** Options for {@link openrouter}. */
133
- export interface AskOpenRouterOptions extends AskAdapterOptions {
139
+ export interface AssistantOpenRouterOptions extends AssistantAdapterOptions {
134
140
  /** The OpenRouter model id (`anthropic/claude-sonnet-4-5`). */
135
141
  model: string;
136
142
  /**
@@ -138,7 +144,7 @@ export interface AskOpenRouterOptions extends AskAdapterOptions {
138
144
  * OpenRouter's `reasoning.effort`, because its provider ignores the AI
139
145
  * SDK's call-level option. Omitted keeps the model's default.
140
146
  */
141
- reasoning?: AskReasoning;
147
+ reasoning?: AssistantReasoning;
142
148
  }
143
149
 
144
150
  const openrouterOptionsSchema = z.strictObject({
@@ -147,9 +153,9 @@ const openrouterOptionsSchema = z.strictObject({
147
153
  reasoning: reasoningOption,
148
154
  });
149
155
 
150
- export type AskOpenRouterAdapter = AdapterDescriptor<
156
+ export type AssistantOpenRouterAdapter = AdapterDescriptor<
151
157
  "openrouter",
152
- AskOpenRouterOptions
158
+ AssistantOpenRouterOptions
153
159
  >;
154
160
 
155
161
  export const openrouterAdapterSchema = adapterDescriptorSchema(
@@ -158,12 +164,12 @@ export const openrouterAdapterSchema = adapterDescriptorSchema(
158
164
  );
159
165
 
160
166
  /**
161
- * Route Ask AI through OpenRouter. Reads `OPENROUTER_API_KEY` and needs
167
+ * Route the assistant through OpenRouter. Reads `OPENROUTER_API_KEY` and needs
162
168
  * `@openrouter/ai-sdk-provider` installed in the project.
163
169
  */
164
170
  export const openrouter = (
165
- options: AskOpenRouterOptions
166
- ): AskOpenRouterAdapter => ({
171
+ options: AssistantOpenRouterOptions
172
+ ): AssistantOpenRouterAdapter => ({
167
173
  kind: "openrouter",
168
174
  options,
169
175
  requiredSecrets: [options.apiKeyEnv ?? OPENROUTER_API_KEY_ENV],
@@ -177,7 +183,7 @@ export const openrouter = (
177
183
  const LLMGATEWAY_API_KEY_ENV = "LLMGATEWAY_API_KEY";
178
184
 
179
185
  /** Options for {@link llmgateway}. */
180
- export interface AskLlmGatewayOptions extends AskAdapterOptions {
186
+ export interface AssistantLlmGatewayOptions extends AssistantAdapterOptions {
181
187
  /** Overrides the preset endpoint (`https://api.llmgateway.io/v1`). */
182
188
  baseUrl?: string;
183
189
  /** The model id LLMGateway serves. */
@@ -186,7 +192,7 @@ export interface AskLlmGatewayOptions extends AskAdapterOptions {
186
192
  * How much the model reasons before answering, sent in the request as
187
193
  * `reasoning_effort`. Omitted keeps the model's default.
188
194
  */
189
- reasoning?: AskReasoning;
195
+ reasoning?: AssistantReasoning;
190
196
  }
191
197
 
192
198
  const llmgatewayOptionsSchema = z.strictObject({
@@ -196,9 +202,9 @@ const llmgatewayOptionsSchema = z.strictObject({
196
202
  reasoning: reasoningOption,
197
203
  });
198
204
 
199
- export type AskLlmGatewayAdapter = AdapterDescriptor<
205
+ export type AssistantLlmGatewayAdapter = AdapterDescriptor<
200
206
  "llmgateway",
201
- AskLlmGatewayOptions
207
+ AssistantLlmGatewayOptions
202
208
  >;
203
209
 
204
210
  export const llmgatewayAdapterSchema = adapterDescriptorSchema(
@@ -207,12 +213,12 @@ export const llmgatewayAdapterSchema = adapterDescriptorSchema(
207
213
  );
208
214
 
209
215
  /**
210
- * Route Ask AI through LLMGateway's OpenAI-compatible endpoint. Reads
216
+ * Route the assistant through LLMGateway's OpenAI-compatible endpoint. Reads
211
217
  * `LLMGATEWAY_API_KEY` and needs `@ai-sdk/openai-compatible` installed.
212
218
  */
213
219
  export const llmgateway = (
214
- options: AskLlmGatewayOptions
215
- ): AskLlmGatewayAdapter => ({
220
+ options: AssistantLlmGatewayOptions
221
+ ): AssistantLlmGatewayAdapter => ({
216
222
  kind: "llmgateway",
217
223
  options,
218
224
  requiredSecrets: [options.apiKeyEnv ?? LLMGATEWAY_API_KEY_ENV],
@@ -229,7 +235,7 @@ const INKEEP_API_KEY_ENV = "INKEEP_API_KEY";
229
235
  * Options for {@link inkeep}. No `reasoning`: Inkeep runs its own QA pipeline
230
236
  * behind an OpenAI-compatible endpoint with no reasoning control.
231
237
  */
232
- export interface AskInkeepOptions extends AskAdapterOptions {
238
+ export interface AssistantInkeepOptions extends AssistantAdapterOptions {
233
239
  /** Overrides the preset endpoint (`https://api.inkeep.com/v1`). */
234
240
  baseUrl?: string;
235
241
  /** The Inkeep QA model id. */
@@ -242,7 +248,10 @@ const inkeepOptionsSchema = z.strictObject({
242
248
  model: z.string().min(1),
243
249
  });
244
250
 
245
- export type AskInkeepAdapter = AdapterDescriptor<"inkeep", AskInkeepOptions>;
251
+ export type AssistantInkeepAdapter = AdapterDescriptor<
252
+ "inkeep",
253
+ AssistantInkeepOptions
254
+ >;
246
255
 
247
256
  export const inkeepAdapterSchema = adapterDescriptorSchema(
248
257
  "inkeep",
@@ -254,7 +263,9 @@ export const inkeepAdapterSchema = adapterDescriptorSchema(
254
263
  * dashboard, so Blume leaves it ungrounded and it takes no `reasoning`.
255
264
  * Reads `INKEEP_API_KEY` and needs `@ai-sdk/openai-compatible` installed.
256
265
  */
257
- export const inkeep = (options: AskInkeepOptions): AskInkeepAdapter => ({
266
+ export const inkeep = (
267
+ options: AssistantInkeepOptions
268
+ ): AssistantInkeepAdapter => ({
258
269
  kind: "inkeep",
259
270
  options,
260
271
  requiredSecrets: [options.apiKeyEnv ?? INKEEP_API_KEY_ENV],
@@ -266,7 +277,7 @@ export const inkeep = (options: AskInkeepOptions): AskInkeepAdapter => ({
266
277
  // ---------------------------------------------------------------------------
267
278
 
268
279
  /** Options for {@link openaiCompatible}. */
269
- export interface AskOpenAICompatibleOptions extends AskAdapterOptions {
280
+ export interface AssistantOpenAICompatibleOptions extends AssistantAdapterOptions {
270
281
  /** Name of the env var holding the endpoint's API key. */
271
282
  apiKeyEnv: string;
272
283
  /** The endpoint's base URL (`https://my-gateway.example.com/v1`). */
@@ -280,7 +291,7 @@ export interface AskOpenAICompatibleOptions extends AskAdapterOptions {
280
291
  * `reasoning_effort`, so the endpoint has to accept that parameter.
281
292
  * Omitted keeps the model's default.
282
293
  */
283
- reasoning?: AskReasoning;
294
+ reasoning?: AssistantReasoning;
284
295
  }
285
296
 
286
297
  const openaiCompatibleOptionsSchema = z.strictObject({
@@ -293,9 +304,9 @@ const openaiCompatibleOptionsSchema = z.strictObject({
293
304
  reasoning: reasoningOption,
294
305
  });
295
306
 
296
- export type AskOpenAICompatibleAdapter = AdapterDescriptor<
307
+ export type AssistantOpenAICompatibleAdapter = AdapterDescriptor<
297
308
  "openai-compatible",
298
- AskOpenAICompatibleOptions
309
+ AssistantOpenAICompatibleOptions
299
310
  >;
300
311
 
301
312
  export const openaiCompatibleAdapterSchema = adapterDescriptorSchema(
@@ -304,13 +315,13 @@ export const openaiCompatibleAdapterSchema = adapterDescriptorSchema(
304
315
  );
305
316
 
306
317
  /**
307
- * Route Ask AI through any OpenAI-compatible endpoint: supply its `baseUrl`,
318
+ * Route the assistant through any OpenAI-compatible endpoint: supply its `baseUrl`,
308
319
  * the `model` it serves, and the env var holding its key. Needs
309
320
  * `@ai-sdk/openai-compatible` installed.
310
321
  */
311
322
  export const openaiCompatible = (
312
- options: AskOpenAICompatibleOptions
313
- ): AskOpenAICompatibleAdapter => ({
323
+ options: AssistantOpenAICompatibleOptions
324
+ ): AssistantOpenAICompatibleAdapter => ({
314
325
  kind: "openai-compatible",
315
326
  options,
316
327
  requiredSecrets: [options.apiKeyEnv],
@@ -318,16 +329,16 @@ export const openaiCompatible = (
318
329
  });
319
330
 
320
331
  // ---------------------------------------------------------------------------
321
- // The `ai.ask.provider` schema
332
+ // The `ai.assistant.provider` schema
322
333
  // ---------------------------------------------------------------------------
323
334
 
324
- /** Which backend answers Ask AI: the value of `gateway()`, `openrouter()`, … */
325
- export type AskAdapter =
326
- | AskGatewayAdapter
327
- | AskOpenRouterAdapter
328
- | AskLlmGatewayAdapter
329
- | AskInkeepAdapter
330
- | AskOpenAICompatibleAdapter;
335
+ /** Which backend answers the assistant: the value of `gateway()`, `openrouter()`, … */
336
+ export type AssistantAdapter =
337
+ | AssistantGatewayAdapter
338
+ | AssistantOpenRouterAdapter
339
+ | AssistantLlmGatewayAdapter
340
+ | AssistantInkeepAdapter
341
+ | AssistantOpenAICompatibleAdapter;
331
342
 
332
343
  // ---------------------------------------------------------------------------
333
344
  // Blume 1 hints
@@ -354,29 +365,29 @@ const MOVED_TO_PROVIDER: ReadonlySet<string> = new Set([
354
365
  "reasoning",
355
366
  ]);
356
367
 
357
- /** The provider an `ai.ask` object names: a 1.x string or a descriptor's `kind`. */
358
- const askProviderNameProbe = z.looseObject({
368
+ /** The provider an `ai.assistant` object names: a 1.x string or a descriptor's `kind`. */
369
+ const assistantProviderNameProbe = z.looseObject({
359
370
  provider: z.union([
360
371
  z.string(),
361
372
  z.looseObject({ kind: z.string() }).transform(({ kind }) => kind),
362
373
  ]),
363
374
  });
364
375
 
365
- /** The factory a failing `ai.ask` object's provider names, or the gateway. */
376
+ /** The factory a failing `ai.assistant` object's provider names, or the gateway. */
366
377
  const providerFactory = (issue: z.core.$ZodRawIssue): string => {
367
- const probe = askProviderNameProbe.safeParse(issue.input);
378
+ const probe = assistantProviderNameProbe.safeParse(issue.input);
368
379
  return (
369
380
  (probe.success && FACTORY_BY_PROVIDER.get(probe.data.provider)) || "gateway"
370
381
  );
371
382
  };
372
383
 
373
384
  /**
374
- * Error params for `ai.ask`: a 1.x flat provider field (`model`, `apiKeyEnv`,
385
+ * Error params for `ai.assistant`: a 1.x flat provider field (`model`, `apiKeyEnv`,
375
386
  * `baseUrl`, `headers`, `reasoning`) names the adapter call it moves into —
376
387
  * the one the object's `provider` picks, 1.x string or descriptor — instead
377
388
  * of Zod's bare "Unrecognized key"; any other unknown key keeps its wording.
378
389
  */
379
- export const askMovedFieldsHint = {
390
+ export const assistantMovedFieldsHint = {
380
391
  error: (issue: z.core.$ZodRawIssue): string | undefined => {
381
392
  if (issue.code !== "unrecognized_keys") {
382
393
  return;
@@ -385,7 +396,7 @@ export const askMovedFieldsHint = {
385
396
  if (moved.length === 0) {
386
397
  return;
387
398
  }
388
- const fields = moved.map((key) => `ai.ask.${key}`).join(", ");
399
+ const fields = moved.map((key) => `ai.assistant.${key}`).join(", ");
389
400
  const hint = `${fields} moved into the provider adapter: \`provider: ${providerFactory(issue)}({ ${moved.join(", ")} })\`, imported from "blume/ai".`;
390
401
  const others = issue.keys.filter((key) => !MOVED_TO_PROVIDER.has(key));
391
402
  return others.length > 0
@@ -394,27 +405,27 @@ export const askMovedFieldsHint = {
394
405
  },
395
406
  };
396
407
 
397
- /** A value that is a 1.x provider name, for the `ai.ask.provider` hint. */
408
+ /** A value that is a 1.x provider name, for the `ai.assistant.provider` hint. */
398
409
  const providerNameProbe = z.string();
399
410
 
400
411
  /**
401
- * The message for an `ai.ask.provider` that isn't a descriptor: a 1.x
412
+ * The message for an `ai.assistant.provider` that isn't a descriptor: a 1.x
402
413
  * provider name names the factory that replaced it; anything else lists them.
403
414
  */
404
415
  const providerNotAdapterMessage = (issue: z.core.$ZodRawIssue): string => {
405
416
  const name = providerNameProbe.safeParse(issue.input);
406
417
  const factory = name.success ? FACTORY_BY_PROVIDER.get(name.data) : undefined;
407
418
  return factory
408
- ? `ai.ask.provider takes an adapter from "blume/ai", not a provider name: \`provider: ${factory}({ model })\`. The 1.x model, apiKeyEnv, baseUrl, headers, and reasoning fields move into the call.`
409
- : 'ai.ask.provider takes an adapter from "blume/ai": gateway(), openrouter(), llmgateway(), inkeep(), or openaiCompatible().';
419
+ ? `ai.assistant.provider takes an adapter from "blume/ai", not a provider name: \`provider: ${factory}({ model })\`. The 1.x model, apiKeyEnv, baseUrl, headers, and reasoning fields move into the call.`
420
+ : 'ai.assistant.provider takes an adapter from "blume/ai": gateway(), openrouter(), llmgateway(), inkeep(), or openaiCompatible().';
410
421
  };
411
422
 
412
423
  /**
413
- * `ai.ask.provider`: the descriptor an adapter factory returned, validated
424
+ * `ai.assistant.provider`: the descriptor an adapter factory returned, validated
414
425
  * against that adapter's own option schema. A value that isn't a descriptor
415
426
  * at all (a 1.x provider name) names the factory that replaced it.
416
427
  */
417
- export const askAdapterSchema = z.discriminatedUnion(
428
+ export const assistantAdapterSchema = z.discriminatedUnion(
418
429
  "kind",
419
430
  [
420
431
  gatewayAdapterSchema,
@@ -434,12 +445,12 @@ export const askAdapterSchema = z.discriminatedUnion(
434
445
  }
435
446
  );
436
447
 
437
- /** A resolved (post-defaults) `ai.ask.provider` descriptor. */
438
- export type AskAdapterConfig = z.output<typeof askAdapterSchema>;
439
- export type AskAdapterKind = AskAdapterConfig["kind"];
448
+ /** A resolved (post-defaults) `ai.assistant.provider` descriptor. */
449
+ export type AssistantAdapterConfig = z.output<typeof assistantAdapterSchema>;
450
+ export type AssistantAdapterKind = AssistantAdapterConfig["kind"];
440
451
 
441
- /** The provider when `ai.ask.provider` is unset: the gateway with its defaults. */
442
- export const DEFAULT_ASK_PROVIDER: AskAdapter = gateway();
452
+ /** The provider when `ai.assistant.provider` is unset: the gateway with its defaults. */
453
+ export const DEFAULT_ASSISTANT_PROVIDER: AssistantAdapter = gateway();
443
454
 
444
455
  // ---------------------------------------------------------------------------
445
456
  // Resolved backend
@@ -474,7 +485,7 @@ export interface AskBackendTemplate {
474
485
  export interface AskBackend {
475
486
  /** Whether Blume grounds answers in the docs (Inkeep retrieves itself). */
476
487
  grounded: boolean;
477
- kind: AskAdapterKind;
488
+ kind: AssistantAdapterKind;
478
489
  /** Human-readable adapter name for diagnostics ("AI Gateway"). */
479
490
  label: string;
480
491
  /** Appended to the missing-secret warning (an alternative credential, say). */
@@ -499,7 +510,7 @@ const headersLine = (headers?: Record<string, string>): string =>
499
510
  /** Refuse a request up front when the adapter's key env var is unset. */
500
511
  const keyCheck = (env: string): string => ` if (!${secretExpr(env)}) {
501
512
  return new Response(
502
- ${JSON.stringify(`Ask AI is not configured: set ${env}.`)},
513
+ ${JSON.stringify(`The assistant is not configured: set ${env}.`)},
503
514
  { status: 503 }
504
515
  );
505
516
  }`;
@@ -510,7 +521,7 @@ const keyCheck = (env: string): string => ` if (!${secretExpr(env)}) {
510
521
  */
511
522
  const callFields = (options: {
512
523
  providerOptions?: z.output<typeof providerOptionsSchema>;
513
- reasoning?: AskReasoning;
524
+ reasoning?: AssistantReasoning;
514
525
  }): string[] => {
515
526
  const fields: string[] = [];
516
527
  if (options.reasoning) {
@@ -542,7 +553,7 @@ const gatewayBackend = (
542
553
  keyCheck: ` // The AI Gateway authenticates with an API key or Vercel's OIDC token.
543
554
  if (!(${secretExpr(options.apiKeyEnv)} || getSecret("VERCEL_OIDC_TOKEN"))) {
544
555
  return new Response(
545
- ${JSON.stringify(`Ask AI is not configured: set ${options.apiKeyEnv} (or deploy on Vercel with OIDC).`)},
556
+ ${JSON.stringify(`The assistant is not configured: set ${options.apiKeyEnv} (or deploy on Vercel with OIDC).`)},
546
557
  { status: 503 }
547
558
  );
548
559
  }`,
@@ -587,7 +598,7 @@ const openrouterBackend = (
587
598
  * option as `reasoning_effort`.
588
599
  */
589
600
  const openaiCompatibleBackend = (
590
- kind: AskAdapterKind,
601
+ kind: AssistantAdapterKind,
591
602
  label: string,
592
603
  grounded: boolean,
593
604
  options: {
@@ -597,7 +608,7 @@ const openaiCompatibleBackend = (
597
608
  model: string;
598
609
  name: string;
599
610
  providerOptions?: z.output<typeof providerOptionsSchema>;
600
- reasoning?: AskReasoning;
611
+ reasoning?: AssistantReasoning;
601
612
  }
602
613
  ): AskBackend => ({
603
614
  grounded,
@@ -621,9 +632,11 @@ const openaiCompatibleBackend = (
621
632
  },
622
633
  });
623
634
 
624
- /** Resolve a parsed `ai.ask.provider` descriptor into its backend. */
635
+ /** Resolve a parsed `ai.assistant.provider` descriptor into its backend. */
625
636
  export const resolveAskBackend = (
626
- provider: AskAdapterConfig = askAdapterSchema.parse(DEFAULT_ASK_PROVIDER)
637
+ provider: AssistantAdapterConfig = assistantAdapterSchema.parse(
638
+ DEFAULT_ASSISTANT_PROVIDER
639
+ )
627
640
  ): AskBackend => {
628
641
  switch (provider.kind) {
629
642
  case "gateway": {
@@ -4,6 +4,7 @@ import { mdxToMdast } from "satteri";
4
4
  import { parseYouTubeId } from "../components/content/youtube.ts";
5
5
  import type { ExampleLookup } from "../core/types.ts";
6
6
  import { MDX_FEATURES } from "../markdown/features.ts";
7
+ import { readStaticExpression } from "./static-expression.ts";
7
8
 
8
9
  /**
9
10
  * Downlevel Blume's MDX components to plain Markdown for agent-facing output
@@ -44,11 +45,19 @@ interface MdxAttribute {
44
45
 
45
46
  /** A single source replacement: `[start, end)` byte range → `text`. */
46
47
  interface Splice {
48
+ /**
49
+ * The range is a flow (block-level) element, so the line after it must
50
+ * start a block of its own.
51
+ */
52
+ block: boolean;
47
53
  end: number;
48
54
  start: number;
49
55
  text: string;
50
56
  }
51
57
 
58
+ /** Text that continues on the very next line, with no blank line between. */
59
+ const NEXT_LINE_TEXT = /^[\t ]*\n[\t ]*\S/u;
60
+
52
61
  /**
53
62
  * A statically-recovered data value. Parsed front matter and evaluated
54
63
  * attribute literals are both plain data — scalars, dates, arrays, and
@@ -135,33 +144,13 @@ export type ComponentMarkdown = (
135
144
  ) => string | null;
136
145
 
137
146
  /**
138
- * Statically evaluate an MDX attribute expression (`prop={...}`). Component
139
- * data props are object/array/number literals in practice; evaluation runs at
140
- * build time over the author's own content — the same trust level as the MDX
141
- * itself, which Astro compiles and executes. The page's `frontmatter` is in
142
- * scope, mirroring what Astro provides an MDX body at render time, so
143
- * `prop={frontmatter.status}` resolves; expressions that reference imports or
144
- * other scope throw and report as not evaluable.
147
+ * Read an element's attributes into a plain props object. An expression
148
+ * attribute (`prop={...}`) is read as literal data and never executed (see
149
+ * `static-expression.ts`): the downleveler also runs over plain `.md` pages
150
+ * and remote content, which nothing else executes. `prop={frontmatter.status}`
151
+ * resolves against the page's front matter, as it does when Astro renders
152
+ * the page; anything else — a call, an import — marks the props lossy.
145
153
  */
146
- const evaluateExpression = (
147
- raw: string,
148
- frontmatter: Record<string, EvaluatedValue> | undefined
149
- ) => {
150
- try {
151
- // Build-time eval of the author's own attribute literals; a throw falls
152
- // back to leaving the JSX verbatim.
153
- // oxlint-disable-next-line no-new-func
154
- const value: EvaluatedValue = new Function(
155
- "frontmatter",
156
- `"use strict"; return (${raw});`
157
- )(frontmatter);
158
- return { ok: true, value };
159
- } catch {
160
- return { ok: false, value: undefined };
161
- }
162
- };
163
-
164
- /** Evaluate an element's attributes into a plain props object. */
165
154
  const readProps = (
166
155
  node: MdastNode,
167
156
  frontmatter: Record<string, EvaluatedValue> | undefined
@@ -180,7 +169,7 @@ const readProps = (
180
169
  } else if (isString(attribute.value)) {
181
170
  props[attribute.name] = attribute.value;
182
171
  } else {
183
- const result = evaluateExpression(attribute.value.value, frontmatter);
172
+ const result = readStaticExpression(attribute.value.value, frontmatter);
184
173
  if (result.ok) {
185
174
  props[attribute.name] = result.value;
186
175
  } else {
@@ -208,14 +197,24 @@ const applySplices = (text: string, splices: Splice[]): string => {
208
197
  const lineStart = result.lastIndexOf("\n", splice.start - 1) + 1;
209
198
  const prefix = result.slice(lineStart, splice.start);
210
199
  const indent = /^[\t ]+$/u.test(prefix) ? prefix : "";
200
+ // The JSX's closing tag ended its block; the Markdown standing in for it
201
+ // doesn't. Text on the very next line would read as a lazy continuation
202
+ // of a blockquote or list item (`> Body` then `Next.` joins the quote),
203
+ // so a blank line keeps it out.
204
+ const spliced =
205
+ splice.block &&
206
+ splice.text !== "" &&
207
+ NEXT_LINE_TEXT.test(result.slice(splice.end))
208
+ ? `${splice.text}\n`
209
+ : splice.text;
211
210
  const replacement = indent
212
- ? splice.text
211
+ ? spliced
213
212
  .split("\n")
214
213
  .map((line, index) =>
215
214
  index === 0 || line === "" ? line : `${indent}${line}`
216
215
  )
217
216
  .join("\n")
218
- : splice.text;
217
+ : spliced;
219
218
  result =
220
219
  result.slice(0, splice.start) + replacement + result.slice(splice.end);
221
220
  }
@@ -888,15 +887,21 @@ const renderSlice = (
888
887
  const splices: Splice[] = [];
889
888
  // oxlint-disable-next-line no-use-before-define
890
889
  collectSplices(walk, nodes, splices);
890
+ // Start the slice at its line's indent, so a component that opens the
891
+ // slice is indented like the lines after it once replaced (see
892
+ // `applySplices`) and dedents with them; the final trim drops that indent
893
+ // from the first line again.
894
+ const indent = indentAt(walk.source, start);
895
+ const from = start - (indent ?? 0);
891
896
  const spliced = applySplices(
892
- walk.source.slice(start, end),
897
+ walk.source.slice(from, end),
893
898
  splices.map((splice) => ({
894
899
  ...splice,
895
- end: splice.end - start,
896
- start: splice.start - start,
900
+ end: splice.end - from,
901
+ start: splice.start - from,
897
902
  }))
898
903
  );
899
- return dedent(spliced, indentAt(walk.source, start)).trim();
904
+ return dedent(spliced, indent).trim();
900
905
  };
901
906
 
902
907
  /** The element's body as Markdown: the slice covering all of its children. */
@@ -996,6 +1001,7 @@ const collectSplices = (
996
1001
  const text = hasOffsets(node) ? downlevelComponentNode(node, walk) : null;
997
1002
  if (text !== null && hasOffsets(node)) {
998
1003
  out.push({
1004
+ block: node.type === "mdxJsxFlowElement",
999
1005
  end: node.position.end.offset,
1000
1006
  start: node.position.start.offset,
1001
1007
  text,
@@ -1015,10 +1021,10 @@ const collectSplices = (
1015
1021
  * over the built-ins: a same-name entry replaces the built-in serializer, and
1016
1022
  * one that always returns `null` effectively opts that component out.
1017
1023
  *
1018
- * `frontmatter` is the page's parsed front-matter data. It is put in scope
1019
- * when evaluating attribute expressions — so `prop={frontmatter.status}`
1020
- * resolves the way it does when Astro renders the page — and handed to
1021
- * serializers on their context.
1024
+ * `frontmatter` is the page's parsed front-matter data. Attribute
1025
+ * expressions that reference it — `prop={frontmatter.status}` — resolve the
1026
+ * way they do when Astro renders the page, and it is handed to serializers on
1027
+ * their context. Expressions are read as literal data, never executed.
1022
1028
  */
1023
1029
  export const downlevelComponents = (
1024
1030
  source: string,
package/src/ai/cors.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * CORS for the generated Ask AI route (`ai.ask.cors`).
2
+ * CORS for the assistant's generated route (`ai.assistant.cors`).
3
3
  *
4
4
  * A browser only lets a page on another origin read a response that names
5
5
  * that origin, and a JSON `POST` preflights first. `preflightResponse` answers
@@ -8,12 +8,12 @@
8
8
  * rather than stamping each `return`, keeps the next return site added to the
9
9
  * handler from shipping an opaque failure for that one status.
10
10
  *
11
- * `allowed` is the `ai.ask.cors` list: origins already reduced to their
11
+ * `allowed` is the `ai.assistant.cors` list: origins already reduced to their
12
12
  * `scheme://host[:port]` form by the config schema, or the single entry `"*"`
13
13
  * to admit every origin.
14
14
  */
15
15
 
16
- /** The `ai.ask.cors` entry that admits every origin. */
16
+ /** The `ai.assistant.cors` entry that admits every origin. */
17
17
  export const ANY_ORIGIN = "*";
18
18
 
19
19
  /** The headers a response carries for a cross-origin caller. */