@elytracms/studio 0.0.52 → 0.0.54

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 (321) hide show
  1. package/dist/components/account/account-settings.js +4 -4
  2. package/dist/components/account/credential-panel.js +1 -1
  3. package/dist/components/account/self-account-view-source.js +3 -3
  4. package/dist/components/ai/ai-chat-panel-lazy.js +1 -1
  5. package/dist/components/ai/ai-chat-panel.js +4 -4
  6. package/dist/components/ai/ai-cms-panel.js +1 -1
  7. package/dist/components/ai/proposal-card.js +4 -4
  8. package/dist/components/ai/translate-document-action.js +1 -1
  9. package/dist/components/ai/translate-document-dialog.d.ts +1 -1
  10. package/dist/components/app-views/default-modules.d.ts +3 -3
  11. package/dist/components/app-views/default-modules.js +20 -20
  12. package/dist/components/app-views/sidebar.d.ts +1 -1
  13. package/dist/components/app-views/sidebar.js +2 -2
  14. package/dist/components/app-views/studio-config.d.ts +1 -1
  15. package/dist/components/assets/asset-detail.js +9 -9
  16. package/dist/components/assets/asset-grid.js +7 -7
  17. package/dist/components/assets/asset-thumb.d.ts +1 -1
  18. package/dist/components/assets/asset-thumb.js +2 -2
  19. package/dist/components/assets/assets-collection-view-source.js +3 -3
  20. package/dist/components/assets/assets-module.js +8 -8
  21. package/dist/components/assets/assets-store.d.ts +1 -1
  22. package/dist/components/assets/assets-store.js +1 -1
  23. package/dist/components/builder/blocks-model.js +1 -1
  24. package/dist/components/builder/blocks-panel.d.ts +1 -1
  25. package/dist/components/builder/blocks-panel.js +6 -6
  26. package/dist/components/builder/canvas.js +11 -11
  27. package/dist/components/builder/clipboard-actions.js +2 -2
  28. package/dist/components/builder/composition-field-editor-lazy.d.ts +1 -1
  29. package/dist/components/builder/composition-field-editor-lazy.js +1 -1
  30. package/dist/components/builder/composition-field-editor.d.ts +1 -1
  31. package/dist/components/builder/composition-field-editor.js +9 -9
  32. package/dist/components/builder/insert-context.d.ts +2 -2
  33. package/dist/components/builder/insert-context.js +2 -2
  34. package/dist/components/builder/insert-menu.d.ts +2 -2
  35. package/dist/components/builder/insert-menu.js +1 -1
  36. package/dist/components/builder/layout-inspector.js +1 -1
  37. package/dist/components/builder/page-tree.js +5 -5
  38. package/dist/components/builder/property-inspector.js +3 -3
  39. package/dist/components/builder/reuse-context.d.ts +1 -1
  40. package/dist/components/builder/reuse-context.js +3 -3
  41. package/dist/components/builder/spacing-handles.js +2 -2
  42. package/dist/components/cms/asset-picker.js +4 -4
  43. package/dist/components/cms/bulk-actions.js +1 -1
  44. package/dist/components/cms/bulk-bar.d.ts +1 -1
  45. package/dist/components/cms/bulk-bar.js +3 -3
  46. package/dist/components/cms/cell-edit.js +1 -1
  47. package/dist/components/cms/cell.js +2 -2
  48. package/dist/components/cms/cms-index.js +1 -1
  49. package/dist/components/cms/cms-store.d.ts +2 -2
  50. package/dist/components/cms/cms-store.js +6 -6
  51. package/dist/components/cms/collection-table.js +17 -17
  52. package/dist/components/cms/detail-editor.js +31 -31
  53. package/dist/components/cms/document-diff-view.js +4 -4
  54. package/dist/components/cms/editable-cell.d.ts +1 -1
  55. package/dist/components/cms/editable-cell.js +6 -6
  56. package/dist/components/cms/field-control.js +15 -15
  57. package/dist/components/cms/field-renderer.d.ts +2 -2
  58. package/dist/components/cms/filter-builder.js +3 -3
  59. package/dist/components/cms/import-export.js +1 -1
  60. package/dist/components/cms/issues-pane.js +1 -1
  61. package/dist/components/cms/prop-fields.js +3 -3
  62. package/dist/components/cms/reference-confirm.js +1 -1
  63. package/dist/components/cms/relation-picker.js +1 -1
  64. package/dist/components/cms/rich-text/block-commands.d.ts +1 -1
  65. package/dist/components/cms/rich-text/component-embed.js +2 -2
  66. package/dist/components/cms/rich-text/editor.js +8 -8
  67. package/dist/components/cms/rich-text/embed-inspector.d.ts +1 -1
  68. package/dist/components/cms/rich-text/embed-inspector.js +2 -2
  69. package/dist/components/cms/rich-text/embed-picker.js +1 -1
  70. package/dist/components/cms/rich-text/embed-props.js +1 -1
  71. package/dist/components/cms/rich-text/extensions.d.ts +1 -1
  72. package/dist/components/cms/rich-text/extensions.js +4 -4
  73. package/dist/components/cms/rich-text/index.d.ts +1 -1
  74. package/dist/components/cms/rich-text/index.js +1 -1
  75. package/dist/components/cms/rich-text/insert.d.ts +1 -1
  76. package/dist/components/cms/rich-text/lazy.d.ts +1 -1
  77. package/dist/components/cms/rich-text/lazy.js +1 -1
  78. package/dist/components/cms/rich-text/slash-command.js +1 -1
  79. package/dist/components/cms/rich-text/toolbar.d.ts +1 -1
  80. package/dist/components/cms/rich-text/toolbar.js +4 -4
  81. package/dist/components/cms/use-cell-edit.d.ts +1 -1
  82. package/dist/components/cms/use-cell-edit.js +2 -2
  83. package/dist/components/cms/use-compare-selection.d.ts +1 -1
  84. package/dist/components/cms/use-discard-controller.d.ts +1 -1
  85. package/dist/components/cms/use-discard-controller.js +2 -2
  86. package/dist/components/cms/use-document-versions.d.ts +1 -1
  87. package/dist/components/cms/use-document-versions.js +1 -1
  88. package/dist/components/cms/use-embed-inspector-host.d.ts +1 -1
  89. package/dist/components/cms/use-published-perspective.d.ts +2 -2
  90. package/dist/components/cms/use-published-perspective.js +2 -2
  91. package/dist/components/cms/use-restore-version.js +1 -1
  92. package/dist/components/cms/version-compare-lazy.js +1 -1
  93. package/dist/components/cms/version-compare.js +6 -6
  94. package/dist/components/cms/versions-model.d.ts +1 -1
  95. package/dist/components/cms/versions-pane.js +5 -5
  96. package/dist/components/cms/view-bar.js +2 -2
  97. package/dist/components/collab/collab-context.js +1 -1
  98. package/dist/components/collection-view/cms-collection-view-source.js +2 -2
  99. package/dist/components/components/component-preview.js +3 -3
  100. package/dist/components/components/components-index.js +2 -2
  101. package/dist/components/components/registry-browser.js +2 -2
  102. package/dist/components/components/transfer-dialog.js +5 -5
  103. package/dist/components/components/transfer.js +4 -4
  104. package/dist/components/current-user-menu.js +2 -2
  105. package/dist/components/delivery-health-banner.js +2 -2
  106. package/dist/components/operation-error.js +1 -1
  107. package/dist/components/project-list.js +5 -5
  108. package/dist/components/project-session.js +1 -1
  109. package/dist/components/project-switcher.js +3 -3
  110. package/dist/components/rail-store-status.js +1 -1
  111. package/dist/components/redirects/redirects-collection-view-source.js +3 -3
  112. package/dist/components/redirects/redirects-module.js +6 -6
  113. package/dist/components/section-nav.js +3 -3
  114. package/dist/components/section-surface.js +1 -1
  115. package/dist/components/studio-project-screen.js +7 -7
  116. package/dist/components/studio-shell.js +9 -9
  117. package/dist/components/use-hydration-phase.js +1 -1
  118. package/dist/components/users/users-collection-view-source.js +4 -4
  119. package/dist/components/users/users-module.js +9 -9
  120. package/dist/components/workspace-projects.js +1 -1
  121. package/dist/customization.d.ts +9 -9
  122. package/dist/customization.js +5 -5
  123. package/dist/lib/ai/anthropic-provider.d.ts +1 -1
  124. package/dist/lib/ai/apply-content.d.ts +1 -1
  125. package/dist/lib/ai/apply-content.js +3 -3
  126. package/dist/lib/ai/chat-state.d.ts +2 -2
  127. package/dist/lib/ai/config.d.ts +1 -1
  128. package/dist/lib/ai/convex-session-headers.d.ts +68 -0
  129. package/dist/lib/ai/convex-session-headers.js +79 -0
  130. package/dist/lib/ai/convex-session-headers.js.map +1 -0
  131. package/dist/lib/ai/convex-session.d.ts +82 -0
  132. package/dist/lib/ai/convex-session.js +92 -0
  133. package/dist/lib/ai/convex-session.js.map +1 -0
  134. package/dist/lib/ai/create-flow.d.ts +2 -2
  135. package/dist/lib/ai/create-flow.js +3 -3
  136. package/dist/lib/ai/fake-provider.d.ts +1 -1
  137. package/dist/lib/ai/index.d.ts +27 -16
  138. package/dist/lib/ai/index.js +27 -16
  139. package/dist/lib/ai/index.js.map +1 -1
  140. package/dist/lib/ai/knowledge.d.ts +1 -1
  141. package/dist/lib/ai/node-handler.d.ts +29 -0
  142. package/dist/lib/ai/node-handler.js +48 -0
  143. package/dist/lib/ai/node-handler.js.map +1 -0
  144. package/dist/lib/ai/provenance.d.ts +1 -1
  145. package/dist/lib/ai/route-provider.d.ts +1 -1
  146. package/dist/lib/ai/route.d.ts +1 -1
  147. package/dist/lib/ai/shape.d.ts +1 -1
  148. package/dist/lib/ai/translate-document.d.ts +1 -1
  149. package/dist/lib/ai/translate-document.js +3 -3
  150. package/dist/lib/ai/vite.d.ts +32 -0
  151. package/dist/lib/ai/vite.js +97 -0
  152. package/dist/lib/ai/vite.js.map +1 -0
  153. package/dist/lib/ai-panel.js +1 -1
  154. package/dist/lib/assets/index.d.ts +4 -4
  155. package/dist/lib/assets/index.js +4 -4
  156. package/dist/lib/assets/upload.d.ts +2 -2
  157. package/dist/lib/assets/upload.js +2 -2
  158. package/dist/lib/auth/convex-auth-adapter.d.ts +2 -2
  159. package/dist/lib/auth/convex-auth-adapter.js +1 -1
  160. package/dist/lib/auth/dev-auto-login-adapter.d.ts +1 -1
  161. package/dist/lib/auth/index.d.ts +9 -9
  162. package/dist/lib/auth/index.js +10 -10
  163. package/dist/lib/auth/local-auth-adapter.d.ts +1 -1
  164. package/dist/lib/auth/route-guard.d.ts +1 -1
  165. package/dist/lib/auth-errors.d.ts +1 -1
  166. package/dist/lib/clipboard/index.d.ts +3 -3
  167. package/dist/lib/clipboard/index.js +3 -3
  168. package/dist/lib/cms/published-documents.d.ts +1 -1
  169. package/dist/lib/cms-views/index.d.ts +2 -2
  170. package/dist/lib/cms-views/index.js +2 -2
  171. package/dist/lib/collection-view/collection-view-source.d.ts +4 -4
  172. package/dist/lib/data/collection-content.js +1 -1
  173. package/dist/lib/data/data-store.js +6 -6
  174. package/dist/lib/data/index.d.ts +4 -4
  175. package/dist/lib/data/index.js +4 -4
  176. package/dist/lib/data/sample-capture.js +4 -4
  177. package/dist/lib/data/sources-store.d.ts +1 -1
  178. package/dist/lib/diff-view-preference.js +1 -1
  179. package/dist/lib/dnd/gap-drag.d.ts +1 -1
  180. package/dist/lib/i18n/i18n.d.ts +1 -1
  181. package/dist/lib/i18n/i18n.js +1 -1
  182. package/dist/lib/i18n/index.d.ts +4 -4
  183. package/dist/lib/i18n/index.js +3 -3
  184. package/dist/lib/i18n/messages.en.d.ts +1 -1
  185. package/dist/lib/i18n/messages.en.js +1 -1
  186. package/dist/lib/locale/binding-fallback.d.ts +1 -1
  187. package/dist/lib/locale/binding-fallback.js +2 -2
  188. package/dist/lib/locale/index.d.ts +6 -6
  189. package/dist/lib/locale/index.js +6 -6
  190. package/dist/lib/locale/locale-switcher.js +3 -3
  191. package/dist/lib/locale/use-studio-locale.js +1 -1
  192. package/dist/lib/operations/asset-repository.d.ts +1 -1
  193. package/dist/lib/operations/cms-persistence.d.ts +1 -1
  194. package/dist/lib/operations/cms-persistence.js +1 -1
  195. package/dist/lib/operations/index.d.ts +6 -6
  196. package/dist/lib/operations/index.js +6 -6
  197. package/dist/lib/operations/session-hydration.d.ts +1 -1
  198. package/dist/lib/operations/session-hydration.js +1 -1
  199. package/dist/lib/operations/studio-operations.d.ts +1 -1
  200. package/dist/lib/operations/studio-operations.js +3 -3
  201. package/dist/lib/persistence/convex-adapter.js +2 -2
  202. package/dist/lib/persistence/convex-blob-storage.d.ts +1 -1
  203. package/dist/lib/persistence/convex-blob-storage.js +1 -1
  204. package/dist/lib/persistence/deployment-readiness.d.ts +2 -2
  205. package/dist/lib/persistence/index.d.ts +6 -6
  206. package/dist/lib/persistence/index.js +7 -7
  207. package/dist/lib/persona/labels.js +1 -1
  208. package/dist/lib/query/index.d.ts +2 -2
  209. package/dist/lib/query/index.js +1 -1
  210. package/dist/lib/rail.js +1 -1
  211. package/dist/lib/references/impact.d.ts +1 -1
  212. package/dist/lib/references/impact.js +1 -1
  213. package/dist/lib/references/index.d.ts +3 -3
  214. package/dist/lib/references/index.js +3 -3
  215. package/dist/lib/references/references-store.d.ts +2 -2
  216. package/dist/lib/references/references-store.js +3 -3
  217. package/dist/lib/registry/index.js +2 -2
  218. package/dist/lib/theme.js +1 -1
  219. package/dist/lib/webhooks/config.d.ts +1 -1
  220. package/dist/lib/webhooks/config.js +2 -2
  221. package/dist/lib/webhooks/dispatcher.js +2 -2
  222. package/dist/lib/webhooks/index.d.ts +6 -6
  223. package/dist/lib/webhooks/index.js +4 -4
  224. package/dist/lib/workspace/index.d.ts +7 -7
  225. package/dist/lib/workspace/index.js +3 -3
  226. package/dist/lib/workspace/switcher-source.d.ts +1 -1
  227. package/dist/router/index.d.ts +1 -1
  228. package/dist/router/index.js +1 -1
  229. package/dist/ui/avatar.js +1 -1
  230. package/dist/ui/badge.js +1 -1
  231. package/dist/ui/button.js +1 -1
  232. package/dist/ui/card.js +1 -1
  233. package/dist/ui/collapsible-section.js +2 -2
  234. package/dist/ui/dialog.js +1 -1
  235. package/dist/ui/dropdown-menu.js +1 -1
  236. package/dist/ui/field.js +1 -1
  237. package/dist/ui/index.d.ts +19 -19
  238. package/dist/ui/index.js +19 -19
  239. package/dist/ui/input.js +1 -1
  240. package/dist/ui/inspector.js +1 -1
  241. package/dist/ui/layout.js +1 -1
  242. package/dist/ui/panel.js +2 -2
  243. package/dist/ui/popover.js +1 -1
  244. package/dist/ui/row-item.js +2 -2
  245. package/dist/ui/segmented-control.js +1 -1
  246. package/dist/ui/separator.js +1 -1
  247. package/dist/ui/toolbar.js +2 -2
  248. package/dist/ui/typography.js +1 -1
  249. package/package.json +13 -5
  250. package/dist/components/assets/asset-library.d.ts +0 -14
  251. package/dist/components/assets/asset-library.js +0 -202
  252. package/dist/components/assets/asset-library.js.map +0 -1
  253. package/dist/components/backend-gate.d.ts +0 -19
  254. package/dist/components/backend-gate.js +0 -64
  255. package/dist/components/backend-gate.js.map +0 -1
  256. package/dist/components/builder/composition-scope.d.ts +0 -16
  257. package/dist/components/builder/composition-scope.js +0 -7
  258. package/dist/components/builder/composition-scope.js.map +0 -1
  259. package/dist/components/cms/alias-selector.d.ts +0 -20
  260. package/dist/components/cms/alias-selector.js +0 -34
  261. package/dist/components/cms/alias-selector.js.map +0 -1
  262. package/dist/components/cms/collections-sidebar.d.ts +0 -18
  263. package/dist/components/cms/collections-sidebar.js +0 -36
  264. package/dist/components/cms/collections-sidebar.js.map +0 -1
  265. package/dist/components/cms/new-page-dialog.d.ts +0 -22
  266. package/dist/components/cms/new-page-dialog.js +0 -42
  267. package/dist/components/cms/new-page-dialog.js.map +0 -1
  268. package/dist/components/cms/rich-text/embed-control.d.ts +0 -17
  269. package/dist/components/cms/rich-text/embed-control.js +0 -62
  270. package/dist/components/cms/rich-text/embed-control.js.map +0 -1
  271. package/dist/components/cms/rich-text/image-control.d.ts +0 -16
  272. package/dist/components/cms/rich-text/image-control.js +0 -36
  273. package/dist/components/cms/rich-text/image-control.js.map +0 -1
  274. package/dist/components/components/fixture-runner.d.ts +0 -10
  275. package/dist/components/components/fixture-runner.js +0 -27
  276. package/dist/components/components/fixture-runner.js.map +0 -1
  277. package/dist/components/references/builder-deep-link.d.ts +0 -12
  278. package/dist/components/references/builder-deep-link.js +0 -53
  279. package/dist/components/references/builder-deep-link.js.map +0 -1
  280. package/dist/components/routes/redirect-chain.d.ts +0 -9
  281. package/dist/components/routes/redirect-chain.js +0 -16
  282. package/dist/components/routes/redirect-chain.js.map +0 -1
  283. package/dist/components/routes/redirect-manager.d.ts +0 -13
  284. package/dist/components/routes/redirect-manager.js +0 -24
  285. package/dist/components/routes/redirect-manager.js.map +0 -1
  286. package/dist/components/routes/route-list.d.ts +0 -35
  287. package/dist/components/routes/route-list.js +0 -69
  288. package/dist/components/routes/route-list.js.map +0 -1
  289. package/dist/components/routes/routes-shell.d.ts +0 -14
  290. package/dist/components/routes/routes-shell.js +0 -68
  291. package/dist/components/routes/routes-shell.js.map +0 -1
  292. package/dist/components/sections.d.ts +0 -37
  293. package/dist/components/sections.js +0 -62
  294. package/dist/components/sections.js.map +0 -1
  295. package/dist/components/users/user-search.d.ts +0 -8
  296. package/dist/components/users/user-search.js +0 -13
  297. package/dist/components/users/user-search.js.map +0 -1
  298. package/dist/components/workspace-gate.d.ts +0 -12
  299. package/dist/components/workspace-gate.js +0 -28
  300. package/dist/components/workspace-gate.js.map +0 -1
  301. package/dist/lib/cms-views/views-store.d.ts +0 -70
  302. package/dist/lib/cms-views/views-store.js +0 -129
  303. package/dist/lib/cms-views/views-store.js.map +0 -1
  304. package/dist/lib/operations/graph-commands.d.ts +0 -136
  305. package/dist/lib/operations/graph-commands.js +0 -85
  306. package/dist/lib/operations/graph-commands.js.map +0 -1
  307. package/dist/lib/operations/graph-persistence.d.ts +0 -27
  308. package/dist/lib/operations/graph-persistence.js +0 -35
  309. package/dist/lib/operations/graph-persistence.js.map +0 -1
  310. package/dist/lib/routing/index.d.ts +0 -10
  311. package/dist/lib/routing/index.js +0 -11
  312. package/dist/lib/routing/index.js.map +0 -1
  313. package/dist/lib/routing/routing-analysis.d.ts +0 -26
  314. package/dist/lib/routing/routing-analysis.js +0 -37
  315. package/dist/lib/routing/routing-analysis.js.map +0 -1
  316. package/dist/lib/routing/routing-store.d.ts +0 -50
  317. package/dist/lib/routing/routing-store.js +0 -72
  318. package/dist/lib/routing/routing-store.js.map +0 -1
  319. package/dist/lib/working-graph.d.ts +0 -82
  320. package/dist/lib/working-graph.js +0 -113
  321. package/dist/lib/working-graph.js.map +0 -1
@@ -38,15 +38,15 @@
38
38
  * createStudio(config, studioConfig)
39
39
  * ```
40
40
  */
41
- export { defineStudio } from './components/app-views/studio-config';
42
- export { nav } from './components/app-views/default-modules';
43
- export { createStudio } from './lib/workspace';
41
+ export { defineStudio } from './components/app-views/studio-config.js';
42
+ export { nav } from './components/app-views/default-modules.js';
43
+ export { createStudio } from './lib/workspace/index.js';
44
44
  /** A ready-made example renderer; a consuming repo may use it or author its own. */
45
- export { colorFieldRenderer } from './components/cms/field-renderer';
45
+ export { colorFieldRenderer } from './components/cms/field-renderer.js';
46
46
  /**
47
47
  * Studio-UI localization (EC-241): author a translation catalog for `defineStudio({
48
48
  * i18n })`. `msg` is only needed to build an in-repo *source* catalog with translator
49
49
  * hints; a plain `MessageCatalog` (flat key → string) is all a consumer supplies.
50
50
  */
51
- export { msg } from './lib/i18n';
51
+ export { msg } from './lib/i18n/index.js';
52
52
  //# sourceMappingURL=customization.js.map
@@ -10,7 +10,7 @@
10
10
  * dependency, never in `packages/core`. Key custody stays with the host: the key is
11
11
  * read from host env by the mount and passed in (or falls back to `ANTHROPIC_API_KEY`).
12
12
  */
13
- import type { AiProvider } from './types';
13
+ import type { AiProvider } from './types.js';
14
14
  export interface AnthropicProviderOptions {
15
15
  /** Provider key (host-env secret). Falls back to the SDK's `ANTHROPIC_API_KEY` lookup. */
16
16
  apiKey?: string;
@@ -1,4 +1,4 @@
1
- import type { ApplyContentInput, ApplyContentResult } from './types';
1
+ import type { ApplyContentInput, ApplyContentResult } from './types.js';
2
2
  /** Default repair attempts after the first generation. */
3
3
  export declare const DEFAULT_MAX_REPAIR_ATTEMPTS = 2;
4
4
  /**
@@ -19,9 +19,9 @@
19
19
  * whole loop without a key. No data access, no service credentials here.
20
20
  */
21
21
  import { assetNode, referenceNode, resolveLabel, validateDocument } from '@elytracms/core/cms-core';
22
- import { assembleKnowledgeBlock } from './knowledge';
23
- import { aiProvenance } from './provenance';
24
- import { buildTargetShape } from './shape';
22
+ import { assembleKnowledgeBlock } from './knowledge.js';
23
+ import { aiProvenance } from './provenance.js';
24
+ import { buildTargetShape } from './shape.js';
25
25
  /** Default repair attempts after the first generation. */
26
26
  export const DEFAULT_MAX_REPAIR_ATTEMPTS = 2;
27
27
  const SYSTEM_PROMPT = [
@@ -9,8 +9,8 @@
9
9
  * every run (determinism principle).
10
10
  */
11
11
  import type { FieldValues } from '@elytracms/core/cms-core';
12
- import type { AiProvenance, ContentPatch } from './types';
13
- import type { CreateFlowResult, CreateFlowStep, ProposalOrigin } from './create-flow';
12
+ import type { AiProvenance, ContentPatch } from './types.js';
13
+ import type { CreateFlowResult, CreateFlowStep, ProposalOrigin } from './create-flow.js';
14
14
  /** A proposal the editor reviews as a diff and then applies (as a draft) or rejects. */
15
15
  export interface ProposalReview {
16
16
  id: string;
@@ -13,7 +13,7 @@
13
13
  * never pulls the AI SDK. When `enabled: false`, the gate is skipped (nothing loads).
14
14
  */
15
15
  import { z } from 'zod';
16
- import type { AiLimits, AiProvider, KnowledgeSource, StudioAiConfig } from './types';
16
+ import type { AiLimits, AiProvider, KnowledgeSource, StudioAiConfig } from './types.js';
17
17
  export declare const AI_CONFIG_ISSUE_CODES: readonly ["invalid-config-shape", "missing-provider", "provider-missing-capabilities", "provider-not-callable"];
18
18
  export type AiConfigIssueCode = (typeof AI_CONFIG_ISSUE_CODES)[number];
19
19
  /** A structured, path-located `ai`-config issue (never thrown — collected). */
@@ -0,0 +1,68 @@
1
+ /**
2
+ * `createConvexSessionHeaders` — the CLIENT half of the engine-default AI session
3
+ * gate (EC-371). It supplies the `headers` thunk `createRouteProvider` re-evaluates
4
+ * per request, attaching the signed-in user's Convex Auth JWT as
5
+ * `authorization: Bearer <token>` so the route's {@link createConvexJwtSessionResolver}
6
+ * can verify it against the deployment's JWKS.
7
+ *
8
+ * Two storage conventions, owned HERE and nowhere else:
9
+ *
10
+ * 1. **The engine's own session store (primary).** The studio's `ConvexAuthAdapter`
11
+ * (`lib/auth`) persists the JWT in `sessionStorage` under
12
+ * `elytra.convex-auth.jwt` / `elytra.convex-auth.jwt::<deployment-url>`
13
+ * (per-backend sessions, EC-179). This covers every engine studio sign-in,
14
+ * including the dev-login path. The prefix is pinned against `tokenStorageKey()`
15
+ * by a drift test in `convex-session-headers.test.ts` — if `lib/auth` ever
16
+ * renames it, that test fails loudly.
17
+ * 2. **The `@convex-dev/auth` react-client store (fallback).** Hosts that mount the
18
+ * official `ConvexAuthProvider` get the JWT persisted in `localStorage` under a
19
+ * key starting with `__convexAuthJWT` — an INTERNAL convention of that package,
20
+ * not a public API. If a future upgrade renames it, the token is simply not
21
+ * found and the assistant fails CLOSED (401) — never open.
22
+ *
23
+ * A playground/local studio has no Convex session, so the thunk returns `undefined`
24
+ * (no auth header); the route then rejects unless the loud dev-only opt-in on
25
+ * `createAiDevPlugin` is active. Browser-only reads; SSR yields no header.
26
+ *
27
+ * Note on multiple per-backend sessions: the scan returns the first token found;
28
+ * a token minted by a DIFFERENT deployment fails the route's issuer/JWKS check and
29
+ * yields 401 (fail-closed), never a cross-deployment pass.
30
+ */
31
+ /**
32
+ * The engine `ConvexAuthAdapter` sessionStorage key prefix (`lib/auth`,
33
+ * `tokenStorageKey()`), duplicated here so `lib/ai` carries no runtime edge into
34
+ * the auth adapter (which pulls the Convex client). Drift-pinned by test.
35
+ */
36
+ export declare const ELYTRA_CONVEX_JWT_STORAGE_PREFIX = "elytra.convex-auth.jwt";
37
+ /** The `@convex-dev/auth`-internal localStorage key prefix carrying the fresh JWT. */
38
+ export declare const CONVEX_AUTH_JWT_STORAGE_PREFIX = "__convexAuthJWT";
39
+ /** The read-only subset of DOM `Storage` the token scan needs (test seam). */
40
+ export interface TokenStorageLike {
41
+ readonly length: number;
42
+ key(index: number): string | null;
43
+ getItem(key: string): string | null;
44
+ }
45
+ /** First non-empty value under a key starting with `prefix`; `null` when absent. */
46
+ export declare function readTokenByPrefix(storage: TokenStorageLike | null | undefined, prefix: string): string | null;
47
+ export interface ConvexSessionStorages {
48
+ /** The engine adapter's store (default: `window.sessionStorage`). */
49
+ sessionStorage?: TokenStorageLike | null;
50
+ /** The @convex-dev/auth react-client store (default: `window.localStorage`). */
51
+ localStorage?: TokenStorageLike | null;
52
+ }
53
+ /**
54
+ * Read the current Convex Auth JWT: the engine's sessionStorage convention first,
55
+ * then the `@convex-dev/auth` localStorage convention. Pure over the injected
56
+ * storages; `null` when signed out (or outside a browser).
57
+ */
58
+ export declare function readConvexAuthToken(storages: ConvexSessionStorages): string | null;
59
+ export interface ConvexSessionHeadersOptions {
60
+ /** Storage accessor override (default: the browser's session/local storages). */
61
+ storages?: () => ConvexSessionStorages;
62
+ }
63
+ /**
64
+ * Build the `headers` thunk for `createRouteProvider({ headers })`. Re-evaluated on
65
+ * every request, so a refreshed token is picked up automatically; `undefined` (no
66
+ * header) when no session token exists.
67
+ */
68
+ export declare function createConvexSessionHeaders(options?: ConvexSessionHeadersOptions): () => HeadersInit | undefined;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * `createConvexSessionHeaders` — the CLIENT half of the engine-default AI session
3
+ * gate (EC-371). It supplies the `headers` thunk `createRouteProvider` re-evaluates
4
+ * per request, attaching the signed-in user's Convex Auth JWT as
5
+ * `authorization: Bearer <token>` so the route's {@link createConvexJwtSessionResolver}
6
+ * can verify it against the deployment's JWKS.
7
+ *
8
+ * Two storage conventions, owned HERE and nowhere else:
9
+ *
10
+ * 1. **The engine's own session store (primary).** The studio's `ConvexAuthAdapter`
11
+ * (`lib/auth`) persists the JWT in `sessionStorage` under
12
+ * `elytra.convex-auth.jwt` / `elytra.convex-auth.jwt::<deployment-url>`
13
+ * (per-backend sessions, EC-179). This covers every engine studio sign-in,
14
+ * including the dev-login path. The prefix is pinned against `tokenStorageKey()`
15
+ * by a drift test in `convex-session-headers.test.ts` — if `lib/auth` ever
16
+ * renames it, that test fails loudly.
17
+ * 2. **The `@convex-dev/auth` react-client store (fallback).** Hosts that mount the
18
+ * official `ConvexAuthProvider` get the JWT persisted in `localStorage` under a
19
+ * key starting with `__convexAuthJWT` — an INTERNAL convention of that package,
20
+ * not a public API. If a future upgrade renames it, the token is simply not
21
+ * found and the assistant fails CLOSED (401) — never open.
22
+ *
23
+ * A playground/local studio has no Convex session, so the thunk returns `undefined`
24
+ * (no auth header); the route then rejects unless the loud dev-only opt-in on
25
+ * `createAiDevPlugin` is active. Browser-only reads; SSR yields no header.
26
+ *
27
+ * Note on multiple per-backend sessions: the scan returns the first token found;
28
+ * a token minted by a DIFFERENT deployment fails the route's issuer/JWKS check and
29
+ * yields 401 (fail-closed), never a cross-deployment pass.
30
+ */
31
+ /**
32
+ * The engine `ConvexAuthAdapter` sessionStorage key prefix (`lib/auth`,
33
+ * `tokenStorageKey()`), duplicated here so `lib/ai` carries no runtime edge into
34
+ * the auth adapter (which pulls the Convex client). Drift-pinned by test.
35
+ */
36
+ export const ELYTRA_CONVEX_JWT_STORAGE_PREFIX = 'elytra.convex-auth.jwt';
37
+ /** The `@convex-dev/auth`-internal localStorage key prefix carrying the fresh JWT. */
38
+ export const CONVEX_AUTH_JWT_STORAGE_PREFIX = '__convexAuthJWT';
39
+ /** First non-empty value under a key starting with `prefix`; `null` when absent. */
40
+ export function readTokenByPrefix(storage, prefix) {
41
+ if (!storage)
42
+ return null;
43
+ for (let i = 0; i < storage.length; i += 1) {
44
+ const key = storage.key(i);
45
+ if (!key?.startsWith(prefix))
46
+ continue;
47
+ const token = storage.getItem(key);
48
+ if (token)
49
+ return token;
50
+ }
51
+ return null;
52
+ }
53
+ /**
54
+ * Read the current Convex Auth JWT: the engine's sessionStorage convention first,
55
+ * then the `@convex-dev/auth` localStorage convention. Pure over the injected
56
+ * storages; `null` when signed out (or outside a browser).
57
+ */
58
+ export function readConvexAuthToken(storages) {
59
+ return (readTokenByPrefix(storages.sessionStorage, ELYTRA_CONVEX_JWT_STORAGE_PREFIX) ??
60
+ readTokenByPrefix(storages.localStorage, CONVEX_AUTH_JWT_STORAGE_PREFIX));
61
+ }
62
+ function browserStorages() {
63
+ if (typeof window === 'undefined')
64
+ return {};
65
+ return { sessionStorage: window.sessionStorage, localStorage: window.localStorage };
66
+ }
67
+ /**
68
+ * Build the `headers` thunk for `createRouteProvider({ headers })`. Re-evaluated on
69
+ * every request, so a refreshed token is picked up automatically; `undefined` (no
70
+ * header) when no session token exists.
71
+ */
72
+ export function createConvexSessionHeaders(options = {}) {
73
+ const storages = options.storages ?? browserStorages;
74
+ return () => {
75
+ const token = readConvexAuthToken(storages());
76
+ return token ? { authorization: `Bearer ${token}` } : undefined;
77
+ };
78
+ }
79
+ //# sourceMappingURL=convex-session-headers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convex-session-headers.js","sourceRoot":"","sources":["../../../src/lib/ai/convex-session-headers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,wBAAwB,CAAA;AAExE,sFAAsF;AACtF,MAAM,CAAC,MAAM,8BAA8B,GAAG,iBAAiB,CAAA;AAS/D,oFAAoF;AACpF,MAAM,UAAU,iBAAiB,CAC/B,OAA4C,EAC5C,MAAc;IAEd,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAA;IACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA;QAC1B,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC,MAAM,CAAC;YAAE,SAAQ;QACtC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAClC,IAAI,KAAK;YAAE,OAAO,KAAK,CAAA;IACzB,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AASD;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAA+B;IACjE,OAAO,CACL,iBAAiB,CAAC,QAAQ,CAAC,cAAc,EAAE,gCAAgC,CAAC;QAC5E,iBAAiB,CAAC,QAAQ,CAAC,YAAY,EAAE,8BAA8B,CAAC,CACzE,CAAA;AACH,CAAC;AAOD,SAAS,eAAe;IACtB,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,EAAE,CAAA;IAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,CAAC,cAAc,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAA;AACrF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CACxC,UAAuC,EAAE;IAEzC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,eAAe,CAAA;IACpD,OAAO,GAAG,EAAE;QACV,MAAM,KAAK,GAAG,mBAAmB,CAAC,QAAQ,EAAE,CAAC,CAAA;QAC7C,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;IACjE,CAAC,CAAA;AACH,CAAC","sourcesContent":["/**\n * `createConvexSessionHeaders` — the CLIENT half of the engine-default AI session\n * gate (EC-371). It supplies the `headers` thunk `createRouteProvider` re-evaluates\n * per request, attaching the signed-in user's Convex Auth JWT as\n * `authorization: Bearer <token>` so the route's {@link createConvexJwtSessionResolver}\n * can verify it against the deployment's JWKS.\n *\n * Two storage conventions, owned HERE and nowhere else:\n *\n * 1. **The engine's own session store (primary).** The studio's `ConvexAuthAdapter`\n * (`lib/auth`) persists the JWT in `sessionStorage` under\n * `elytra.convex-auth.jwt` / `elytra.convex-auth.jwt::<deployment-url>`\n * (per-backend sessions, EC-179). This covers every engine studio sign-in,\n * including the dev-login path. The prefix is pinned against `tokenStorageKey()`\n * by a drift test in `convex-session-headers.test.ts` — if `lib/auth` ever\n * renames it, that test fails loudly.\n * 2. **The `@convex-dev/auth` react-client store (fallback).** Hosts that mount the\n * official `ConvexAuthProvider` get the JWT persisted in `localStorage` under a\n * key starting with `__convexAuthJWT` — an INTERNAL convention of that package,\n * not a public API. If a future upgrade renames it, the token is simply not\n * found and the assistant fails CLOSED (401) — never open.\n *\n * A playground/local studio has no Convex session, so the thunk returns `undefined`\n * (no auth header); the route then rejects unless the loud dev-only opt-in on\n * `createAiDevPlugin` is active. Browser-only reads; SSR yields no header.\n *\n * Note on multiple per-backend sessions: the scan returns the first token found;\n * a token minted by a DIFFERENT deployment fails the route's issuer/JWKS check and\n * yields 401 (fail-closed), never a cross-deployment pass.\n */\n\n/**\n * The engine `ConvexAuthAdapter` sessionStorage key prefix (`lib/auth`,\n * `tokenStorageKey()`), duplicated here so `lib/ai` carries no runtime edge into\n * the auth adapter (which pulls the Convex client). Drift-pinned by test.\n */\nexport const ELYTRA_CONVEX_JWT_STORAGE_PREFIX = 'elytra.convex-auth.jwt'\n\n/** The `@convex-dev/auth`-internal localStorage key prefix carrying the fresh JWT. */\nexport const CONVEX_AUTH_JWT_STORAGE_PREFIX = '__convexAuthJWT'\n\n/** The read-only subset of DOM `Storage` the token scan needs (test seam). */\nexport interface TokenStorageLike {\n readonly length: number\n key(index: number): string | null\n getItem(key: string): string | null\n}\n\n/** First non-empty value under a key starting with `prefix`; `null` when absent. */\nexport function readTokenByPrefix(\n storage: TokenStorageLike | null | undefined,\n prefix: string,\n): string | null {\n if (!storage) return null\n for (let i = 0; i < storage.length; i += 1) {\n const key = storage.key(i)\n if (!key?.startsWith(prefix)) continue\n const token = storage.getItem(key)\n if (token) return token\n }\n return null\n}\n\nexport interface ConvexSessionStorages {\n /** The engine adapter's store (default: `window.sessionStorage`). */\n sessionStorage?: TokenStorageLike | null\n /** The @convex-dev/auth react-client store (default: `window.localStorage`). */\n localStorage?: TokenStorageLike | null\n}\n\n/**\n * Read the current Convex Auth JWT: the engine's sessionStorage convention first,\n * then the `@convex-dev/auth` localStorage convention. Pure over the injected\n * storages; `null` when signed out (or outside a browser).\n */\nexport function readConvexAuthToken(storages: ConvexSessionStorages): string | null {\n return (\n readTokenByPrefix(storages.sessionStorage, ELYTRA_CONVEX_JWT_STORAGE_PREFIX) ??\n readTokenByPrefix(storages.localStorage, CONVEX_AUTH_JWT_STORAGE_PREFIX)\n )\n}\n\nexport interface ConvexSessionHeadersOptions {\n /** Storage accessor override (default: the browser's session/local storages). */\n storages?: () => ConvexSessionStorages\n}\n\nfunction browserStorages(): ConvexSessionStorages {\n if (typeof window === 'undefined') return {}\n return { sessionStorage: window.sessionStorage, localStorage: window.localStorage }\n}\n\n/**\n * Build the `headers` thunk for `createRouteProvider({ headers })`. Re-evaluated on\n * every request, so a refreshed token is picked up automatically; `undefined` (no\n * header) when no session token exists.\n */\nexport function createConvexSessionHeaders(\n options: ConvexSessionHeadersOptions = {},\n): () => HeadersInit | undefined {\n const storages = options.storages ?? browserStorages\n return () => {\n const token = readConvexAuthToken(storages())\n return token ? { authorization: `Bearer ${token}` } : undefined\n }\n}\n"]}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * `createConvexJwtSessionResolver` — the engine-default `getSession` for
3
+ * {@link createAiRouteHandler} (EC-371; closes EC-369 finding #1, where the template
4
+ * shipped an auth-shaped no-op that accepted any caller with a non-empty marker
5
+ * header).
6
+ *
7
+ * REAL verification, fail-closed: the studio client attaches the signed-in user's
8
+ * Convex Auth JWT (`authorization: Bearer …`, see `createConvexSessionHeaders`), and
9
+ * this resolver verifies signature / issuer / audience / expiry against the Convex
10
+ * deployment's public JWKS (`<site-url>/.well-known/jwks.json`). Only a signed-in
11
+ * studio user of THIS deployment can spend the route's provider key. No token,
12
+ * an invalid token, or missing site-URL env → `null` → the route answers 401.
13
+ *
14
+ * The deployment's site URL (JWT issuer + JWKS host) resolves from, in order:
15
+ * the explicit `siteUrl` option, `VITE_CONVEX_SITE_URL`, or `VITE_CONVEX_URL`
16
+ * (`….convex.cloud` mapped to `….convex.site`). In a Vite-dev mount these are
17
+ * surfaced to `process.env` by {@link createAiDevPlugin}'s `loadEnv` bridge.
18
+ *
19
+ * Dev-only anonymous escape hatch (playground DX): a playground/local studio has no
20
+ * Convex session at all, so the assistant panel would 401 there. Anonymous requests
21
+ * (NO credential presented) may pass ONLY when the loud
22
+ * {@link INSECURE_ANONYMOUS_SESSION_ENV_VAR} handshake is active — set exclusively by
23
+ * `createAiDevPlugin`'s explicit `insecureAllowAnonymousSessions` opt-in under
24
+ * `vite dev` (the plugin is `apply: 'serve'`, so no production build ever carries it)
25
+ * AND additionally gated on `NODE_ENV !== 'production'`. A PRESENTED token must
26
+ * always verify for real — the bypass never masks an invalid credential.
27
+ *
28
+ * `jose` is loaded via dynamic `import()` so this module can sit in the barrel
29
+ * without putting the JWT library on the studio client's critical path (the same
30
+ * discipline `anthropic-provider` applies to the AI SDK; pinned in `bundle.test.ts`).
31
+ */
32
+ import type { JWTVerifyGetKey } from 'jose';
33
+ import type { AiSession } from './types.js';
34
+ /** A `getSession` implementation for {@link AiRouteHandlerOptions.getSession}. */
35
+ export type AiSessionResolver = (request: Request) => Promise<AiSession | null>;
36
+ /**
37
+ * The dev-bypass handshake env var. Set ONLY by `createAiDevPlugin` (vite dev) when
38
+ * the host explicitly opts in via `insecureAllowAnonymousSessions`. Never set this
39
+ * on a deployment: the name says exactly what it does.
40
+ */
41
+ export declare const INSECURE_ANONYMOUS_SESSION_ENV_VAR = "ELYTRA_AI_INSECURE_ALLOW_ANONYMOUS";
42
+ /** The exact value the handshake requires (anything else is ignored, fail-closed). */
43
+ export declare const INSECURE_ANONYMOUS_SESSION_ENV_VALUE = "dev-only";
44
+ /** The synthetic principal id an insecure dev-anonymous session carries. */
45
+ export declare const INSECURE_ANONYMOUS_USER_ID = "insecure-anonymous-dev";
46
+ /** An env bag (defaults to `process.env`, read per-request). */
47
+ export type EnvSource = Record<string, string | undefined>;
48
+ export interface ConvexJwtSessionResolverOptions {
49
+ /**
50
+ * The Convex deployment's SITE url — the JWT issuer and JWKS host (e.g.
51
+ * `https://your-deployment-123.convex.site`). Default: resolved from env via
52
+ * {@link resolveConvexSiteUrl}. Missing everywhere ⇒ fail closed (nobody passes).
53
+ */
54
+ siteUrl?: string;
55
+ /** Expected JWT audience (default `convex` — what Convex Auth mints). */
56
+ audience?: string;
57
+ /** Env source override (default `process.env`, read per-request). */
58
+ env?: EnvSource;
59
+ /**
60
+ * Key source override — primarily a test seam (a `createLocalJWKSet` over a
61
+ * locally generated pair). Default: the deployment's remote JWKS.
62
+ */
63
+ getKey?: JWTVerifyGetKey;
64
+ }
65
+ /**
66
+ * Resolve the Convex SITE url (JWT issuer + JWKS host) from env:
67
+ * `VITE_CONVEX_SITE_URL`, else derived from `VITE_CONVEX_URL`
68
+ * (`….convex.cloud` → `….convex.site`), else `null` (fail closed).
69
+ */
70
+ export declare function resolveConvexSiteUrl(env: EnvSource): string | null;
71
+ /**
72
+ * True when the dev-only anonymous handshake is active: the loud env var carries its
73
+ * exact value AND the process is not a production one. Both conditions are required —
74
+ * setting the var on a production deployment does nothing.
75
+ */
76
+ export declare function insecureAnonymousSessionActive(env: EnvSource): boolean;
77
+ /**
78
+ * Build the engine-default `getSession`: verify the caller's Convex Auth Bearer JWT
79
+ * against the deployment's public JWKS; every failure resolves `null` (→ 401), never
80
+ * throws. See the module doc for the full contract.
81
+ */
82
+ export declare function createConvexJwtSessionResolver(options?: ConvexJwtSessionResolverOptions): AiSessionResolver;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * The dev-bypass handshake env var. Set ONLY by `createAiDevPlugin` (vite dev) when
3
+ * the host explicitly opts in via `insecureAllowAnonymousSessions`. Never set this
4
+ * on a deployment: the name says exactly what it does.
5
+ */
6
+ export const INSECURE_ANONYMOUS_SESSION_ENV_VAR = 'ELYTRA_AI_INSECURE_ALLOW_ANONYMOUS';
7
+ /** The exact value the handshake requires (anything else is ignored, fail-closed). */
8
+ export const INSECURE_ANONYMOUS_SESSION_ENV_VALUE = 'dev-only';
9
+ /** The synthetic principal id an insecure dev-anonymous session carries. */
10
+ export const INSECURE_ANONYMOUS_USER_ID = 'insecure-anonymous-dev';
11
+ /**
12
+ * Resolve the Convex SITE url (JWT issuer + JWKS host) from env:
13
+ * `VITE_CONVEX_SITE_URL`, else derived from `VITE_CONVEX_URL`
14
+ * (`….convex.cloud` → `….convex.site`), else `null` (fail closed).
15
+ */
16
+ export function resolveConvexSiteUrl(env) {
17
+ const site = env.VITE_CONVEX_SITE_URL?.trim();
18
+ if (site)
19
+ return site.replace(/\/$/, '');
20
+ const cloud = env.VITE_CONVEX_URL?.trim();
21
+ if (cloud)
22
+ return cloud.replace(/\/$/, '').replace('.convex.cloud', '.convex.site');
23
+ return null;
24
+ }
25
+ /**
26
+ * True when the dev-only anonymous handshake is active: the loud env var carries its
27
+ * exact value AND the process is not a production one. Both conditions are required —
28
+ * setting the var on a production deployment does nothing.
29
+ */
30
+ export function insecureAnonymousSessionActive(env) {
31
+ return (env[INSECURE_ANONYMOUS_SESSION_ENV_VAR] === INSECURE_ANONYMOUS_SESSION_ENV_VALUE &&
32
+ env.NODE_ENV !== 'production');
33
+ }
34
+ function processEnv() {
35
+ return typeof process !== 'undefined' ? process.env : {};
36
+ }
37
+ let josePromise = null;
38
+ function loadJose() {
39
+ return (josePromise ??= import('jose'));
40
+ }
41
+ let warnedInsecureAnonymous = false;
42
+ function insecureAnonymousSession() {
43
+ if (!warnedInsecureAnonymous) {
44
+ warnedInsecureAnonymous = true;
45
+ console.warn(`[elytra-ai] INSECURE anonymous AI session bypass is ACTIVE (${INSECURE_ANONYMOUS_SESSION_ENV_VAR}). ` +
46
+ 'This is a dev-only playground convenience — if you ever see this on a deployment, remove the env var immediately.');
47
+ }
48
+ return { userId: INSECURE_ANONYMOUS_USER_ID };
49
+ }
50
+ /**
51
+ * Build the engine-default `getSession`: verify the caller's Convex Auth Bearer JWT
52
+ * against the deployment's public JWKS; every failure resolves `null` (→ 401), never
53
+ * throws. See the module doc for the full contract.
54
+ */
55
+ export function createConvexJwtSessionResolver(options = {}) {
56
+ const audience = options.audience ?? 'convex';
57
+ // Remote JWKS getters are cached per site URL (jose caches the fetched keys inside).
58
+ const remoteKeys = new Map();
59
+ return async (request) => {
60
+ const env = options.env ?? processEnv();
61
+ const authorization = request.headers.get('authorization');
62
+ const token = authorization?.startsWith('Bearer ')
63
+ ? authorization.slice('Bearer '.length)
64
+ : null;
65
+ if (!token) {
66
+ // No credential presented. The ONLY way this passes is the loud, dev-only
67
+ // anonymous opt-in (set by `createAiDevPlugin` under `vite dev`).
68
+ return insecureAnonymousSessionActive(env) ? insecureAnonymousSession() : null;
69
+ }
70
+ // A presented token must ALWAYS verify for real — the dev bypass never masks
71
+ // an invalid credential.
72
+ const siteUrl = options.siteUrl?.replace(/\/$/, '') ?? resolveConvexSiteUrl(env);
73
+ if (!siteUrl)
74
+ return null; // fail closed: gate env missing → nobody passes
75
+ try {
76
+ const jose = await loadJose();
77
+ let getKey = options.getKey ?? remoteKeys.get(siteUrl);
78
+ if (!getKey) {
79
+ getKey = jose.createRemoteJWKSet(new URL('/.well-known/jwks.json', siteUrl));
80
+ remoteKeys.set(siteUrl, getKey);
81
+ }
82
+ const { payload } = await jose.jwtVerify(token, getKey, { issuer: siteUrl, audience });
83
+ return typeof payload.sub === 'string' && payload.sub.length > 0
84
+ ? { userId: payload.sub }
85
+ : null;
86
+ }
87
+ catch {
88
+ return null; // expired / bad signature / wrong issuer, audience, or deployment → 401
89
+ }
90
+ };
91
+ }
92
+ //# sourceMappingURL=convex-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convex-session.js","sourceRoot":"","sources":["../../../src/lib/ai/convex-session.ts"],"names":[],"mappings":"AAqCA;;;;GAIG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAAG,oCAAoC,CAAA;AAEtF,sFAAsF;AACtF,MAAM,CAAC,MAAM,oCAAoC,GAAG,UAAU,CAAA;AAE9D,4EAA4E;AAC5E,MAAM,CAAC,MAAM,0BAA0B,GAAG,wBAAwB,CAAA;AAuBlE;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAc;IACjD,MAAM,IAAI,GAAG,GAAG,CAAC,oBAAoB,EAAE,IAAI,EAAE,CAAA;IAC7C,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IACxC,MAAM,KAAK,GAAG,GAAG,CAAC,eAAe,EAAE,IAAI,EAAE,CAAA;IACzC,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,cAAc,CAAC,CAAA;IACnF,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,8BAA8B,CAAC,GAAc;IAC3D,OAAO,CACL,GAAG,CAAC,kCAAkC,CAAC,KAAK,oCAAoC;QAChF,GAAG,CAAC,QAAQ,KAAK,YAAY,CAC9B,CAAA;AACH,CAAC;AAED,SAAS,UAAU;IACjB,OAAO,OAAO,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;AAC1D,CAAC;AAID,IAAI,WAAW,GAA+B,IAAI,CAAA;AAClD,SAAS,QAAQ;IACf,OAAO,CAAC,WAAW,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;AACzC,CAAC;AAED,IAAI,uBAAuB,GAAG,KAAK,CAAA;AACnC,SAAS,wBAAwB;IAC/B,IAAI,CAAC,uBAAuB,EAAE,CAAC;QAC7B,uBAAuB,GAAG,IAAI,CAAA;QAC9B,OAAO,CAAC,IAAI,CACV,+DAA+D,kCAAkC,KAAK;YACpG,mHAAmH,CACtH,CAAA;IACH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,0BAA0B,EAAE,CAAA;AAC/C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,8BAA8B,CAC5C,UAA2C,EAAE;IAE7C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,QAAQ,CAAA;IAC7C,qFAAqF;IACrF,MAAM,UAAU,GAAG,IAAI,GAAG,EAA2B,CAAA;IAErD,OAAO,KAAK,EAAE,OAAgB,EAA6B,EAAE;QAC3D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,UAAU,EAAE,CAAA;QACvC,MAAM,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;QAC1D,MAAM,KAAK,GAAG,aAAa,EAAE,UAAU,CAAC,SAAS,CAAC;YAChD,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC;YACvC,CAAC,CAAC,IAAI,CAAA;QAER,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,0EAA0E;YAC1E,kEAAkE;YAClE,OAAO,8BAA8B,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,wBAAwB,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;QAChF,CAAC;QAED,6EAA6E;QAC7E,yBAAyB;QACzB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,oBAAoB,CAAC,GAAG,CAAC,CAAA;QAChF,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAA,CAAC,gDAAgD;QAC1E,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,QAAQ,EAAE,CAAA;YAC7B,IAAI,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;YACtD,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,GAAG,CAAC,wBAAwB,EAAE,OAAO,CAAC,CAAC,CAAA;gBAC5E,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;YACjC,CAAC;YACD,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAA;YACtF,OAAO,OAAO,OAAO,CAAC,GAAG,KAAK,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC;gBAC9D,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE;gBACzB,CAAC,CAAC,IAAI,CAAA;QACV,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAA,CAAC,wEAAwE;QACtF,CAAC;IACH,CAAC,CAAA;AACH,CAAC","sourcesContent":["/**\n * `createConvexJwtSessionResolver` — the engine-default `getSession` for\n * {@link createAiRouteHandler} (EC-371; closes EC-369 finding #1, where the template\n * shipped an auth-shaped no-op that accepted any caller with a non-empty marker\n * header).\n *\n * REAL verification, fail-closed: the studio client attaches the signed-in user's\n * Convex Auth JWT (`authorization: Bearer …`, see `createConvexSessionHeaders`), and\n * this resolver verifies signature / issuer / audience / expiry against the Convex\n * deployment's public JWKS (`<site-url>/.well-known/jwks.json`). Only a signed-in\n * studio user of THIS deployment can spend the route's provider key. No token,\n * an invalid token, or missing site-URL env → `null` → the route answers 401.\n *\n * The deployment's site URL (JWT issuer + JWKS host) resolves from, in order:\n * the explicit `siteUrl` option, `VITE_CONVEX_SITE_URL`, or `VITE_CONVEX_URL`\n * (`….convex.cloud` mapped to `….convex.site`). In a Vite-dev mount these are\n * surfaced to `process.env` by {@link createAiDevPlugin}'s `loadEnv` bridge.\n *\n * Dev-only anonymous escape hatch (playground DX): a playground/local studio has no\n * Convex session at all, so the assistant panel would 401 there. Anonymous requests\n * (NO credential presented) may pass ONLY when the loud\n * {@link INSECURE_ANONYMOUS_SESSION_ENV_VAR} handshake is active — set exclusively by\n * `createAiDevPlugin`'s explicit `insecureAllowAnonymousSessions` opt-in under\n * `vite dev` (the plugin is `apply: 'serve'`, so no production build ever carries it)\n * AND additionally gated on `NODE_ENV !== 'production'`. A PRESENTED token must\n * always verify for real — the bypass never masks an invalid credential.\n *\n * `jose` is loaded via dynamic `import()` so this module can sit in the barrel\n * without putting the JWT library on the studio client's critical path (the same\n * discipline `anthropic-provider` applies to the AI SDK; pinned in `bundle.test.ts`).\n */\nimport type { JWTVerifyGetKey } from 'jose'\nimport type { AiSession } from './types'\n\n/** A `getSession` implementation for {@link AiRouteHandlerOptions.getSession}. */\nexport type AiSessionResolver = (request: Request) => Promise<AiSession | null>\n\n/**\n * The dev-bypass handshake env var. Set ONLY by `createAiDevPlugin` (vite dev) when\n * the host explicitly opts in via `insecureAllowAnonymousSessions`. Never set this\n * on a deployment: the name says exactly what it does.\n */\nexport const INSECURE_ANONYMOUS_SESSION_ENV_VAR = 'ELYTRA_AI_INSECURE_ALLOW_ANONYMOUS'\n\n/** The exact value the handshake requires (anything else is ignored, fail-closed). */\nexport const INSECURE_ANONYMOUS_SESSION_ENV_VALUE = 'dev-only'\n\n/** The synthetic principal id an insecure dev-anonymous session carries. */\nexport const INSECURE_ANONYMOUS_USER_ID = 'insecure-anonymous-dev'\n\n/** An env bag (defaults to `process.env`, read per-request). */\nexport type EnvSource = Record<string, string | undefined>\n\nexport interface ConvexJwtSessionResolverOptions {\n /**\n * The Convex deployment's SITE url — the JWT issuer and JWKS host (e.g.\n * `https://your-deployment-123.convex.site`). Default: resolved from env via\n * {@link resolveConvexSiteUrl}. Missing everywhere ⇒ fail closed (nobody passes).\n */\n siteUrl?: string\n /** Expected JWT audience (default `convex` — what Convex Auth mints). */\n audience?: string\n /** Env source override (default `process.env`, read per-request). */\n env?: EnvSource\n /**\n * Key source override — primarily a test seam (a `createLocalJWKSet` over a\n * locally generated pair). Default: the deployment's remote JWKS.\n */\n getKey?: JWTVerifyGetKey\n}\n\n/**\n * Resolve the Convex SITE url (JWT issuer + JWKS host) from env:\n * `VITE_CONVEX_SITE_URL`, else derived from `VITE_CONVEX_URL`\n * (`….convex.cloud` → `….convex.site`), else `null` (fail closed).\n */\nexport function resolveConvexSiteUrl(env: EnvSource): string | null {\n const site = env.VITE_CONVEX_SITE_URL?.trim()\n if (site) return site.replace(/\\/$/, '')\n const cloud = env.VITE_CONVEX_URL?.trim()\n if (cloud) return cloud.replace(/\\/$/, '').replace('.convex.cloud', '.convex.site')\n return null\n}\n\n/**\n * True when the dev-only anonymous handshake is active: the loud env var carries its\n * exact value AND the process is not a production one. Both conditions are required —\n * setting the var on a production deployment does nothing.\n */\nexport function insecureAnonymousSessionActive(env: EnvSource): boolean {\n return (\n env[INSECURE_ANONYMOUS_SESSION_ENV_VAR] === INSECURE_ANONYMOUS_SESSION_ENV_VALUE &&\n env.NODE_ENV !== 'production'\n )\n}\n\nfunction processEnv(): EnvSource {\n return typeof process !== 'undefined' ? process.env : {}\n}\n\n// jose stays off the studio client's critical path: loaded on first verification only.\ntype JoseModule = typeof import('jose')\nlet josePromise: Promise<JoseModule> | null = null\nfunction loadJose(): Promise<JoseModule> {\n return (josePromise ??= import('jose'))\n}\n\nlet warnedInsecureAnonymous = false\nfunction insecureAnonymousSession(): AiSession {\n if (!warnedInsecureAnonymous) {\n warnedInsecureAnonymous = true\n console.warn(\n `[elytra-ai] INSECURE anonymous AI session bypass is ACTIVE (${INSECURE_ANONYMOUS_SESSION_ENV_VAR}). ` +\n 'This is a dev-only playground convenience — if you ever see this on a deployment, remove the env var immediately.',\n )\n }\n return { userId: INSECURE_ANONYMOUS_USER_ID }\n}\n\n/**\n * Build the engine-default `getSession`: verify the caller's Convex Auth Bearer JWT\n * against the deployment's public JWKS; every failure resolves `null` (→ 401), never\n * throws. See the module doc for the full contract.\n */\nexport function createConvexJwtSessionResolver(\n options: ConvexJwtSessionResolverOptions = {},\n): AiSessionResolver {\n const audience = options.audience ?? 'convex'\n // Remote JWKS getters are cached per site URL (jose caches the fetched keys inside).\n const remoteKeys = new Map<string, JWTVerifyGetKey>()\n\n return async (request: Request): Promise<AiSession | null> => {\n const env = options.env ?? processEnv()\n const authorization = request.headers.get('authorization')\n const token = authorization?.startsWith('Bearer ')\n ? authorization.slice('Bearer '.length)\n : null\n\n if (!token) {\n // No credential presented. The ONLY way this passes is the loud, dev-only\n // anonymous opt-in (set by `createAiDevPlugin` under `vite dev`).\n return insecureAnonymousSessionActive(env) ? insecureAnonymousSession() : null\n }\n\n // A presented token must ALWAYS verify for real — the dev bypass never masks\n // an invalid credential.\n const siteUrl = options.siteUrl?.replace(/\\/$/, '') ?? resolveConvexSiteUrl(env)\n if (!siteUrl) return null // fail closed: gate env missing → nobody passes\n try {\n const jose = await loadJose()\n let getKey = options.getKey ?? remoteKeys.get(siteUrl)\n if (!getKey) {\n getKey = jose.createRemoteJWKSet(new URL('/.well-known/jwks.json', siteUrl))\n remoteKeys.set(siteUrl, getKey)\n }\n const { payload } = await jose.jwtVerify(token, getKey, { issuer: siteUrl, audience })\n return typeof payload.sub === 'string' && payload.sub.length > 0\n ? { userId: payload.sub }\n : null\n } catch {\n return null // expired / bad signature / wrong issuer, audience, or deployment → 401\n }\n }\n}\n"]}
@@ -21,8 +21,8 @@
21
21
  */
22
22
  import type { CmsDocument, CmsValidationIssue, CollectionDef, CollectionRegistry, FieldValues, LocaleConfig, ValidateDocumentOptions } from '@elytracms/core/cms-core';
23
23
  import type { ComponentManifest } from '@elytracms/core/component-registry';
24
- import type { AiProvenance, AiProvider, ContentPatch, ResolvedKnowledge } from './types';
25
- import type { CreateFlowTools } from './tools';
24
+ import type { AiProvenance, AiProvider, ContentPatch, ResolvedKnowledge } from './types.js';
25
+ import type { CreateFlowTools } from './tools.js';
26
26
  /** A progress event the UI renders as a tool-status affordance while the loop runs. */
27
27
  export type CreateFlowStep = {
28
28
  kind: 'search';
@@ -1,6 +1,6 @@
1
- import { applyContentWithShape } from './apply-content';
2
- import { translateDocument } from './translate-document';
3
- import { PLANNER_SCHEMA_NAME, parsePlannerAction, plannerSchema, } from './tools';
1
+ import { applyContentWithShape } from './apply-content.js';
2
+ import { translateDocument } from './translate-document.js';
3
+ import { PLANNER_SCHEMA_NAME, parsePlannerAction, plannerSchema, } from './tools.js';
4
4
  export const DEFAULT_MAX_STEPS = 6;
5
5
  export const DEFAULT_SEARCH_LIMIT = 20;
6
6
  /**
@@ -9,7 +9,7 @@
9
9
  * them in call order (the last is reused once the script is exhausted). Or pass
10
10
  * `respond` to compute the object from the request.
11
11
  */
12
- import type { AiProvider, AiProviderCapabilities, GenerateStructuredRequest, StreamChatRequest } from './types';
12
+ import type { AiProvider, AiProviderCapabilities, GenerateStructuredRequest, StreamChatRequest } from './types.js';
13
13
  export interface FakeProviderOptions {
14
14
  /** Provider id (default `fake`). */
15
15
  id?: string;
@@ -8,21 +8,32 @@
8
8
  * Bundle discipline: nothing exported here statically imports `ai` / `@ai-sdk/*` —
9
9
  * only `anthropic-provider` touches them, via dynamic `import()`. So importing this
10
10
  * barrel never eagerly loads the AI SDK, and a studio without an `ai` config block
11
- * (which only references `StudioAiConfig` as a type) reaches none of it.
11
+ * (which only references `StudioAiConfig` as a type) reaches none of it. The same
12
+ * discipline keeps `jose` (the session resolver's JWT library) behind a dynamic
13
+ * `import()` in `convex-session`.
14
+ *
15
+ * EC-371 adds the engine-default session gate + mounting kit: the Convex JWT
16
+ * session resolver (server), the matching client header provider, and the Node
17
+ * `(req, res)` bridge. The Vite DEV mount lives in its own entry —
18
+ * `@elytracms/studio/lib/ai/vite` (`createAiDevPlugin`) — because it imports `vite`,
19
+ * a dev-only tool that must stay out of this barrel's dependency edge.
12
20
  */
13
21
  export declare const PACKAGE = "@elytracms/studio/lib/ai";
14
- export * from './types';
15
- export * from './config';
16
- export * from './provenance';
17
- export * from './knowledge';
18
- export * from './shape';
19
- export * from './apply-content';
20
- export * from './anthropic-provider';
21
- export * from './fake-provider';
22
- export * from './route';
23
- export * from './route-provider';
24
- export * from './tools';
25
- export * from './create-flow';
26
- export * from './chat-state';
27
- export * from './rich-text-translate';
28
- export * from './translate-document';
22
+ export * from './types.js';
23
+ export * from './config.js';
24
+ export * from './provenance.js';
25
+ export * from './knowledge.js';
26
+ export * from './shape.js';
27
+ export * from './apply-content.js';
28
+ export * from './anthropic-provider.js';
29
+ export * from './fake-provider.js';
30
+ export * from './route.js';
31
+ export * from './route-provider.js';
32
+ export * from './convex-session.js';
33
+ export * from './convex-session-headers.js';
34
+ export * from './node-handler.js';
35
+ export * from './tools.js';
36
+ export * from './create-flow.js';
37
+ export * from './chat-state.js';
38
+ export * from './rich-text-translate.js';
39
+ export * from './translate-document.js';
@@ -8,22 +8,33 @@
8
8
  * Bundle discipline: nothing exported here statically imports `ai` / `@ai-sdk/*` —
9
9
  * only `anthropic-provider` touches them, via dynamic `import()`. So importing this
10
10
  * barrel never eagerly loads the AI SDK, and a studio without an `ai` config block
11
- * (which only references `StudioAiConfig` as a type) reaches none of it.
11
+ * (which only references `StudioAiConfig` as a type) reaches none of it. The same
12
+ * discipline keeps `jose` (the session resolver's JWT library) behind a dynamic
13
+ * `import()` in `convex-session`.
14
+ *
15
+ * EC-371 adds the engine-default session gate + mounting kit: the Convex JWT
16
+ * session resolver (server), the matching client header provider, and the Node
17
+ * `(req, res)` bridge. The Vite DEV mount lives in its own entry —
18
+ * `@elytracms/studio/lib/ai/vite` (`createAiDevPlugin`) — because it imports `vite`,
19
+ * a dev-only tool that must stay out of this barrel's dependency edge.
12
20
  */
13
21
  export const PACKAGE = '@elytracms/studio/lib/ai';
14
- export * from './types';
15
- export * from './config';
16
- export * from './provenance';
17
- export * from './knowledge';
18
- export * from './shape';
19
- export * from './apply-content';
20
- export * from './anthropic-provider';
21
- export * from './fake-provider';
22
- export * from './route';
23
- export * from './route-provider';
24
- export * from './tools';
25
- export * from './create-flow';
26
- export * from './chat-state';
27
- export * from './rich-text-translate';
28
- export * from './translate-document';
22
+ export * from './types.js';
23
+ export * from './config.js';
24
+ export * from './provenance.js';
25
+ export * from './knowledge.js';
26
+ export * from './shape.js';
27
+ export * from './apply-content.js';
28
+ export * from './anthropic-provider.js';
29
+ export * from './fake-provider.js';
30
+ export * from './route.js';
31
+ export * from './route-provider.js';
32
+ export * from './convex-session.js';
33
+ export * from './convex-session-headers.js';
34
+ export * from './node-handler.js';
35
+ export * from './tools.js';
36
+ export * from './create-flow.js';
37
+ export * from './chat-state.js';
38
+ export * from './rich-text-translate.js';
39
+ export * from './translate-document.js';
29
40
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/ai/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,0BAA0B,CAAA;AAEjD,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,SAAS,CAAA;AACvB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,sBAAsB,CAAA;AACpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,SAAS,CAAA;AACvB,cAAc,kBAAkB,CAAA;AAChC,cAAc,SAAS,CAAA;AACvB,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,uBAAuB,CAAA;AACrC,cAAc,sBAAsB,CAAA","sourcesContent":["/**\n * `@elytracms/studio/lib/ai` — the headless AI platform seam (EC-331, phase 1).\n *\n * Public surface: the config gate, the provider adapters (Anthropic + Fake), the\n * knowledge mechanism, the target-shape derivation, the `applyContentWithShape`\n * engine, the server-route factory, and the provenance seam. No chat UI here.\n *\n * Bundle discipline: nothing exported here statically imports `ai` / `@ai-sdk/*` —\n * only `anthropic-provider` touches them, via dynamic `import()`. So importing this\n * barrel never eagerly loads the AI SDK, and a studio without an `ai` config block\n * (which only references `StudioAiConfig` as a type) reaches none of it.\n */\nexport const PACKAGE = '@elytracms/studio/lib/ai'\n\nexport * from './types'\nexport * from './config'\nexport * from './provenance'\nexport * from './knowledge'\nexport * from './shape'\nexport * from './apply-content'\nexport * from './anthropic-provider'\nexport * from './fake-provider'\nexport * from './route'\nexport * from './route-provider'\nexport * from './tools'\nexport * from './create-flow'\nexport * from './chat-state'\nexport * from './rich-text-translate'\nexport * from './translate-document'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/ai/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,0BAA0B,CAAA;AAEjD,cAAc,SAAS,CAAA;AACvB,cAAc,UAAU,CAAA;AACxB,cAAc,cAAc,CAAA;AAC5B,cAAc,aAAa,CAAA;AAC3B,cAAc,SAAS,CAAA;AACvB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,sBAAsB,CAAA;AACpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,SAAS,CAAA;AACvB,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,0BAA0B,CAAA;AACxC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,SAAS,CAAA;AACvB,cAAc,eAAe,CAAA;AAC7B,cAAc,cAAc,CAAA;AAC5B,cAAc,uBAAuB,CAAA;AACrC,cAAc,sBAAsB,CAAA","sourcesContent":["/**\n * `@elytracms/studio/lib/ai` — the headless AI platform seam (EC-331, phase 1).\n *\n * Public surface: the config gate, the provider adapters (Anthropic + Fake), the\n * knowledge mechanism, the target-shape derivation, the `applyContentWithShape`\n * engine, the server-route factory, and the provenance seam. No chat UI here.\n *\n * Bundle discipline: nothing exported here statically imports `ai` / `@ai-sdk/*` —\n * only `anthropic-provider` touches them, via dynamic `import()`. So importing this\n * barrel never eagerly loads the AI SDK, and a studio without an `ai` config block\n * (which only references `StudioAiConfig` as a type) reaches none of it. The same\n * discipline keeps `jose` (the session resolver's JWT library) behind a dynamic\n * `import()` in `convex-session`.\n *\n * EC-371 adds the engine-default session gate + mounting kit: the Convex JWT\n * session resolver (server), the matching client header provider, and the Node\n * `(req, res)` bridge. The Vite DEV mount lives in its own entry —\n * `@elytracms/studio/lib/ai/vite` (`createAiDevPlugin`) — because it imports `vite`,\n * a dev-only tool that must stay out of this barrel's dependency edge.\n */\nexport const PACKAGE = '@elytracms/studio/lib/ai'\n\nexport * from './types'\nexport * from './config'\nexport * from './provenance'\nexport * from './knowledge'\nexport * from './shape'\nexport * from './apply-content'\nexport * from './anthropic-provider'\nexport * from './fake-provider'\nexport * from './route'\nexport * from './route-provider'\nexport * from './convex-session'\nexport * from './convex-session-headers'\nexport * from './node-handler'\nexport * from './tools'\nexport * from './create-flow'\nexport * from './chat-state'\nexport * from './rich-text-translate'\nexport * from './translate-document'\n"]}
@@ -7,7 +7,7 @@
7
7
  * deterministic item list; `assembleKnowledgeBlock` renders the context block the
8
8
  * engine injects. Knowledge is DATA, never instructions (see the engine's stance).
9
9
  */
10
- import type { KnowledgeCollectionReader, KnowledgeCollectionSource, KnowledgeFn, KnowledgeItem, KnowledgeSource, ResolvedKnowledge } from './types';
10
+ import type { KnowledgeCollectionReader, KnowledgeCollectionSource, KnowledgeFn, KnowledgeItem, KnowledgeSource, ResolvedKnowledge } from './types.js';
11
11
  /** `fromCollection('faq')` — sugar for a studio-editable knowledge source. */
12
12
  export declare function fromCollection(collectionId: string, options?: {
13
13
  field?: string;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * `toNodeHandler` — bridge the framework-agnostic Web `Request → Response`
3
+ * {@link AiRouteHandler} to Node's `(req, res)` signature (EC-371; closes EC-369
4
+ * finding #2, where the identical ~25-line bridge was hand-copied four times across
5
+ * consumers). This is the PROD half of the mounting kit: a Vercel Node function (or
6
+ * any Node server) mounts the route as
7
+ *
8
+ * ```ts
9
+ * // api/ai.ts (Vercel filesystem routing beats the SPA catch-all rewrite)
10
+ * import { toNodeHandler } from '@elytracms/studio/lib/ai'
11
+ * import { aiRouteHandler } from '../src/server/ai-handler.js'
12
+ * export default toNodeHandler(aiRouteHandler)
13
+ * ```
14
+ *
15
+ * The DEV half (`createAiDevPlugin`, Vite middleware + `ssrLoadModule`) reuses this
16
+ * same bridge — one implementation, both mounts. No platform dependency: the classic
17
+ * `(IncomingMessage, ServerResponse)` signature works on every @vercel/node version
18
+ * and on plain `node:http`. Note it buffers the response body (including the chat
19
+ * stream) — the proven Konditorei behavior; native streaming is EC-334's scope.
20
+ */
21
+ import type { IncomingMessage, ServerResponse } from 'node:http';
22
+ import type { AiRouteHandler } from './types.js';
23
+ /** A Node-mountable request handler (Vercel Node function / `node:http` compatible). */
24
+ export type NodeAiRouteHandler = (req: IncomingMessage, res: ServerResponse) => Promise<void>;
25
+ /**
26
+ * Wrap an {@link AiRouteHandler} as a Node `(req, res)` handler: buffer the body,
27
+ * bridge headers both directions, copy status + payload back.
28
+ */
29
+ export declare function toNodeHandler(handler: AiRouteHandler): NodeAiRouteHandler;