@agent-native/core 0.129.1 → 0.130.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (596) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +78 -0
  3. package/corpus/core/docs/content/cloneable-saas.mdx +114 -0
  4. package/corpus/core/docs/content/creating-templates.mdx +28 -2
  5. package/corpus/core/docs/content/syncing-template-changes.mdx +7 -0
  6. package/corpus/core/docs/content/template-clips-developers.mdx +12 -5
  7. package/corpus/core/docs/content/template-dispatch-developers.mdx +1 -1
  8. package/corpus/core/docs/content/template-dispatch.mdx +1 -1
  9. package/corpus/core/package.json +1 -1
  10. package/corpus/core/src/action-ui.ts +1 -0
  11. package/corpus/core/src/action.ts +5 -1
  12. package/corpus/core/src/agent/engine/ai-sdk-engine.ts +15 -5
  13. package/corpus/core/src/agent/harness/runner.ts +5 -0
  14. package/corpus/core/src/agent/production-agent.ts +33 -0
  15. package/corpus/core/src/agent/run-loop-with-resume.ts +5 -0
  16. package/corpus/core/src/agent/run-manager.ts +126 -1
  17. package/corpus/core/src/agent/thread-data-builder.ts +47 -2
  18. package/corpus/core/src/agent/types.ts +2 -0
  19. package/corpus/core/src/chat-threads/store.ts +4 -5
  20. package/corpus/core/src/cli/create.ts +1139 -65
  21. package/corpus/core/src/cli/design-connect.ts +3 -7
  22. package/corpus/core/src/cli/index.ts +5 -3
  23. package/corpus/core/src/cli/skills-content/design-visual-edit-skill.ts +83 -47
  24. package/corpus/core/src/client/AssistantChat.tsx +27 -16
  25. package/corpus/core/src/client/agent-chat-adapter.ts +24 -0
  26. package/corpus/core/src/client/blocks/library/wireframe-kit.tsx +17 -2
  27. package/corpus/core/src/client/chat/index.ts +1 -0
  28. package/corpus/core/src/client/chat/message-components.tsx +104 -1
  29. package/corpus/core/src/client/chat/run-recovery.tsx +15 -3
  30. package/corpus/core/src/client/chat/widgets/DataTableWidget.tsx +10 -29
  31. package/corpus/core/src/client/chat/widgets/WorkspaceFileWidget.tsx +71 -0
  32. package/corpus/core/src/client/chat/widgets/builtin-tool-renderers.tsx +22 -0
  33. package/corpus/core/src/client/chat/widgets/workspace-file-result.ts +49 -0
  34. package/corpus/core/src/client/embed-auth.ts +2 -0
  35. package/corpus/core/src/client/org/InvitationBanner.tsx +14 -1
  36. package/corpus/core/src/client/org/OrgSwitcher.tsx +23 -0
  37. package/corpus/core/src/client/org/TeamPage.tsx +130 -0
  38. package/corpus/core/src/client/org/WorkspaceNotice.tsx +85 -0
  39. package/corpus/core/src/client/org/hooks.ts +14 -0
  40. package/corpus/core/src/client/org/index.ts +2 -0
  41. package/corpus/core/src/client/resources/McpIntegrationDialog.tsx +27 -40
  42. package/corpus/core/src/client/resources/ResourcesPanel.tsx +134 -51
  43. package/corpus/core/src/client/resources/mcp-integration-catalog.ts +7 -0
  44. package/corpus/core/src/client/resources/use-resources.ts +6 -0
  45. package/corpus/core/src/client/use-action.ts +3 -1
  46. package/corpus/core/src/client/widgets/index.ts +1 -0
  47. package/corpus/core/src/coding-tools/run-code.ts +2 -1
  48. package/corpus/core/src/db/client.ts +28 -4
  49. package/corpus/core/src/deploy/build.ts +31 -3
  50. package/corpus/core/src/eject/remote-mcp-presets.ts +1 -1
  51. package/corpus/core/src/index.browser.ts +1 -0
  52. package/corpus/core/src/index.ts +1 -0
  53. package/corpus/core/src/integrations/google-docs-poller.ts +3 -0
  54. package/corpus/core/src/integrations/webhook-handler.ts +28 -11
  55. package/corpus/core/src/jobs/scheduler.ts +4 -0
  56. package/corpus/core/src/localization/default-messages.ts +3 -2
  57. package/corpus/core/src/mcp/build-server.ts +20 -2
  58. package/corpus/core/src/observability/traces.ts +48 -12
  59. package/corpus/core/src/org/handlers.ts +52 -1
  60. package/corpus/core/src/org/migrations.ts +4 -0
  61. package/corpus/core/src/org/plugin.ts +14 -0
  62. package/corpus/core/src/org/schema.ts +1 -0
  63. package/corpus/core/src/org/types.ts +8 -0
  64. package/corpus/core/src/org/workspace-url.ts +74 -0
  65. package/corpus/core/src/resources/handlers.ts +35 -5
  66. package/corpus/core/src/scripts/runner.ts +183 -5
  67. package/corpus/core/src/server/action-routes.ts +32 -2
  68. package/corpus/core/src/server/agent-chat-plugin.ts +29 -0
  69. package/corpus/core/src/server/agent-teams.ts +10 -1
  70. package/corpus/core/src/server/auth.ts +33 -7
  71. package/corpus/core/src/server/embed-route.ts +9 -0
  72. package/corpus/core/src/server/embed-session.ts +62 -3
  73. package/corpus/core/src/server/open-route.ts +20 -6
  74. package/corpus/core/src/server/prompts/framework-core-compact.ts +1 -1
  75. package/corpus/core/src/server/prompts/framework-core.ts +1 -0
  76. package/corpus/core/src/server/request-context.ts +36 -1
  77. package/corpus/core/src/shared/embed-auth.ts +1 -0
  78. package/corpus/core/src/shared/html-document.ts +115 -0
  79. package/corpus/core/src/shared/index.ts +1 -0
  80. package/corpus/core/src/sharing/access.ts +8 -1
  81. package/corpus/core/src/sharing/registry.ts +11 -2
  82. package/corpus/core/src/workspace-files/actions.ts +100 -0
  83. package/corpus/core/src/workspace-files/tool.ts +4 -0
  84. package/corpus/templates/analytics/.agents/skills/analysis-workspace/SKILL.md +4 -0
  85. package/corpus/templates/analytics/.agents/skills/hubspot/SKILL.md +9 -1
  86. package/corpus/templates/analytics/AGENTS.md +17 -24
  87. package/corpus/templates/analytics/actions/data-source-status.ts +11 -1
  88. package/corpus/templates/analytics/app/i18n-data.ts +34 -0
  89. package/corpus/templates/analytics/app/lib/data-source-status.ts +86 -0
  90. package/corpus/templates/analytics/app/lib/native-mcp-exclusions.ts +9 -0
  91. package/corpus/templates/analytics/app/pages/DataSources.tsx +184 -20
  92. package/corpus/templates/analytics/changelog/2026-07-28-native-analytics-data-sources-no-longer-show-duplicate-mc.md +6 -0
  93. package/corpus/templates/analytics/changelog/2026-07-29-exports-now-appear-in-chat-with-a-direct-download-instead-of.md +6 -0
  94. package/corpus/templates/analytics/changelog/2026-07-29-queued-chat-prompts-no-longer-resend-after-analytics-navigat.md +6 -0
  95. package/corpus/templates/analytics/changelog/2026-07-29-scheduled-dashboard-emails-load-every-panel-reliably-without.md +6 -0
  96. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +6 -3
  97. package/corpus/templates/analytics/server/lib/credential-keys.ts +1 -1
  98. package/corpus/templates/analytics/server/lib/hubspot.ts +1 -1
  99. package/corpus/templates/analytics/server/lib/provider-credentials.ts +55 -1
  100. package/corpus/templates/analytics/server/plugins/agent-chat.ts +123 -27
  101. package/corpus/templates/analytics/server/routes/[...page].get.ts +2 -7
  102. package/corpus/templates/analytics/vite.config.ts +7 -0
  103. package/corpus/templates/brain/actions/list-connection-providers.ts +100 -2
  104. package/corpus/templates/brain/app/lib/brain.ts +10 -0
  105. package/corpus/templates/brain/app/routes/sources.tsx +15 -4
  106. package/corpus/templates/brain/changelog/2026-07-29-brain-automatically-retries-failed-source-syncs-and-guides-y.md +6 -0
  107. package/corpus/templates/brain/jobs/process-ingest-queue.ts +13 -0
  108. package/corpus/templates/brain/server/jobs/sync-sources.ts +27 -22
  109. package/corpus/templates/brain/server/plugins/agent-chat.ts +2 -1
  110. package/corpus/templates/brain/server/routes/api/brain/queue/run.post.ts +7 -1
  111. package/corpus/templates/clips/AGENTS.md +1 -1
  112. package/corpus/templates/clips/actions/create-recording-agent-link.ts +11 -1
  113. package/corpus/templates/clips/actions/update-comment.ts +68 -0
  114. package/corpus/templates/clips/app/components/editor/editor-layout.tsx +0 -1
  115. package/corpus/templates/clips/app/components/player/comments-panel.tsx +221 -65
  116. package/corpus/templates/clips/app/components/player/video-player.tsx +101 -27
  117. package/corpus/templates/clips/app/i18n/en-US.ts +2 -1
  118. package/corpus/templates/clips/app/routes/bug-report.done.tsx +48 -27
  119. package/corpus/templates/clips/app/routes/bug-report.tsx +43 -4
  120. package/corpus/templates/clips/app/routes/record.tsx +2 -0
  121. package/corpus/templates/clips/changelog/2026-07-29-clip-playback-now-recovers-instead-of-getting-stuck-when-a-b.md +6 -0
  122. package/corpus/templates/clips/changelog/2026-07-29-comments-can-now-be-edited-after-posting.md +6 -0
  123. package/corpus/templates/clips/changelog/2026-07-29-download-actions-now-use-a-shorter-clearer-label.md +6 -0
  124. package/corpus/templates/clips/changelog/2026-07-29-embedded-bug-reports-now-return-a-temporary-agent-readable.md +6 -0
  125. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +1 -1
  126. package/corpus/templates/clips/desktop/src-tauri/src/system_audio.rs +21 -1
  127. package/corpus/templates/clips/shared/bug-report.ts +99 -0
  128. package/corpus/templates/content/actions/_content-database-personal-view.ts +7 -1
  129. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +5 -2
  130. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +20 -8
  131. package/corpus/templates/content/changelog/2026-07-29-missing-databases-no-longer-trigger-repeated-personal-view-e.md +6 -0
  132. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +83 -47
  133. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +474 -55
  134. package/corpus/templates/design/.generated/bridge/embedded-wheel.generated.ts +3 -0
  135. package/corpus/templates/design/.generated/bridge/hit-test.generated.ts +18 -1
  136. package/corpus/templates/design/.generated/bridge/source-location.generated.ts +348 -0
  137. package/corpus/templates/design/actions/add-localhost-screens.ts +2 -7
  138. package/corpus/templates/design/actions/apply-visual-edit.ts +7 -1
  139. package/corpus/templates/design/actions/connect-localhost.ts +5 -9
  140. package/corpus/templates/design/actions/create-component.ts +16 -13
  141. package/corpus/templates/design/actions/grant-localhost-write-consent.ts +2 -7
  142. package/corpus/templates/design/actions/list-design-source-capabilities.ts +4 -7
  143. package/corpus/templates/design/actions/list-local-files.ts +26 -50
  144. package/corpus/templates/design/actions/list-localhost-connections.ts +2 -7
  145. package/corpus/templates/design/actions/migrate-board-objects-to-file.ts +2 -2
  146. package/corpus/templates/design/actions/open-visual-edit.ts +234 -135
  147. package/corpus/templates/design/actions/read-local-file.ts +26 -50
  148. package/corpus/templates/design/actions/request-localhost-write-consent.ts +2 -7
  149. package/corpus/templates/design/actions/write-local-file.ts +31 -73
  150. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +307 -133
  151. package/corpus/templates/design/app/components/design/EditPanel.tsx +94 -22
  152. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +137 -60
  153. package/corpus/templates/design/app/components/design/ResponsiveInteractBar.tsx +75 -14
  154. package/corpus/templates/design/app/components/design/bridge/editor-chrome.bridge.ts +755 -97
  155. package/corpus/templates/design/app/components/design/bridge/embedded-wheel.bridge.ts +8 -0
  156. package/corpus/templates/design/app/components/design/bridge/hit-test.bridge.ts +24 -1
  157. package/corpus/templates/design/app/components/design/bridge/source-location.bridge.ts +530 -0
  158. package/corpus/templates/design/app/components/design/code-workbench/CodeWorkbenchLoader.tsx +278 -0
  159. package/corpus/templates/design/app/components/design/code-workbench/editor/MonacoHost.tsx +32 -12
  160. package/corpus/templates/design/app/components/design/code-workbench/editor/monaco-setup.ts +14 -3
  161. package/corpus/templates/design/app/components/design/code-workbench/explorer/ExplorerView.tsx +87 -24
  162. package/corpus/templates/design/app/components/design/code-workbench/explorer/FileTree.tsx +36 -0
  163. package/corpus/templates/design/app/components/design/code-workbench/store.tsx +2 -1
  164. package/corpus/templates/design/app/components/design/code-workbench/workspace/localhost-provider.ts +48 -8
  165. package/corpus/templates/design/app/components/design/design-canvas/content-size-report.ts +3 -13
  166. package/corpus/templates/design/app/components/design/design-canvas/embedded-frame.ts +4 -2
  167. package/corpus/templates/design/app/components/design/design-canvas/hit-test.ts +3 -13
  168. package/corpus/templates/design/app/components/design/edit-panel/element-classification.ts +3 -1
  169. package/corpus/templates/design/app/components/design/edit-panel/inspect-code-source.ts +93 -0
  170. package/corpus/templates/design/app/components/design/multi-screen/board-surface-html.ts +4 -2
  171. package/corpus/templates/design/app/components/design/multi-screen/cross-screen-drop.ts +35 -0
  172. package/corpus/templates/design/app/components/design/multi-screen/iframe-targeting.ts +24 -0
  173. package/corpus/templates/design/app/components/design/multi-screen/types.ts +9 -0
  174. package/corpus/templates/design/app/components/design/types.ts +29 -0
  175. package/corpus/templates/design/app/components/layout/Layout.tsx +7 -4
  176. package/corpus/templates/design/app/hooks/use-navigation-state.ts +3 -0
  177. package/corpus/templates/design/app/i18n-data.ts +153 -46
  178. package/corpus/templates/design/app/i18n-responsive-interact.ts +12 -12
  179. package/corpus/templates/design/app/lib/design-editor-route.ts +23 -0
  180. package/corpus/templates/design/app/pages/VisualEdit.tsx +19 -6
  181. package/corpus/templates/design/app/pages/design-editor/action-error.ts +19 -0
  182. package/corpus/templates/design/app/pages/design-editor/canvas-primitive-insert.ts +29 -0
  183. package/corpus/templates/design/app/pages/design-editor/clone-and-pen-edit.ts +6 -0
  184. package/corpus/templates/design/app/pages/design-editor/code-layer-state.ts +277 -15
  185. package/corpus/templates/design/app/pages/design-editor/editor-state.ts +75 -9
  186. package/corpus/templates/design/app/pages/design-editor/layer-state-scope.ts +28 -0
  187. package/corpus/templates/design/app/pages/design-editor/live-screen-layer-drop.ts +41 -0
  188. package/corpus/templates/design/app/pages/design-editor/pending-edits.ts +496 -41
  189. package/corpus/templates/design/app/pages/design-editor/pending-live-edit-unload-guard.ts +27 -0
  190. package/corpus/templates/design/app/pages/design-editor/pending-structure-verification.ts +10 -0
  191. package/corpus/templates/design/app/pages/design-editor/react-semantic-handoff.ts +168 -9
  192. package/corpus/templates/design/app/pages/design-editor/responsive-interact.ts +41 -3
  193. package/corpus/templates/design/app/pages/design-editor/screen-command-utils.ts +5 -0
  194. package/corpus/templates/design/app/pages/design-editor/selection-state.ts +42 -13
  195. package/corpus/templates/design/app/pages/design-editor/source-location.ts +180 -0
  196. package/corpus/templates/design/app/pages/design-editor/tool-state.ts +17 -0
  197. package/corpus/templates/design/app/public-routes.ts +1 -0
  198. package/corpus/templates/design/app/routes/visual-edit.$id.tsx +15 -0
  199. package/corpus/templates/design/changelog/2026-06-29-screen-overview-is-easier-to-find-and-named-screens-open-dir.md +1 -1
  200. package/corpus/templates/design/changelog/2026-07-28-deleting-a-selected-element-now-removes-it-from-the-live-pre.md +6 -0
  201. package/corpus/templates/design/changelog/2026-07-28-deleting-an-element-on-a-connected-localhost-screen-now-queu.md +6 -0
  202. package/corpus/templates/design/changelog/2026-07-28-interact-mode-now-shows-a-responsive-device-preview-with-pre.md +6 -0
  203. package/corpus/templates/design/changelog/2026-07-28-layer-hide-and-lock-now-apply-to-the-live-preview-instead-of.md +6 -0
  204. package/corpus/templates/design/changelog/2026-07-28-layers-panel-selections-on-a-connected-localhost-screen-can-.md +6 -0
  205. package/corpus/templates/design/changelog/2026-07-28-locked-layers-now-show-a-subtle-dashed-outline-in-the-live-p.md +6 -0
  206. package/corpus/templates/design/changelog/2026-07-28-the-apply-design-updates-button-now-counts-pending-live-text.md +6 -0
  207. package/corpus/templates/design/changelog/2026-07-28-the-design-canvas-no-longer-stalls-on-a-render-loop-while-a-.md +6 -0
  208. package/corpus/templates/design/changelog/2026-07-28-visual-edit-on-a-local-dev-server-no-longer-requires-signing.md +6 -0
  209. package/corpus/templates/design/changelog/2026-07-28-visual-edits-on-a-react-19-app-no-longer-report-a-source-lin.md +6 -0
  210. package/corpus/templates/design/changelog/2026-07-29-an-edit-that-cannot-be-written-to-source-now-names-the-reaso.md +6 -0
  211. package/corpus/templates/design/changelog/2026-07-29-applying-a-primitive-dropped-onto-a-connected-localhost-scre.md +6 -0
  212. package/corpus/templates/design/changelog/2026-07-29-changing-a-style-in-the-inspector-now-updates-a-connected-lo.md +6 -0
  213. package/corpus/templates/design/changelog/2026-07-29-choosing-edit-or-annotate-from-a-focused-screen-now-returns-.md +6 -0
  214. package/corpus/templates/design/changelog/2026-07-29-concurrent-source-edits-now-return-a-clear-conflict-response.md +6 -0
  215. package/corpus/templates/design/changelog/2026-07-29-creating-a-component-from-an-element-that-repeats-in-the-sou.md +6 -0
  216. package/corpus/templates/design/changelog/2026-07-29-deleting-an-element-you-just-dropped-onto-a-live-screen-no-l.md +6 -0
  217. package/corpus/templates/design/changelog/2026-07-29-dragging-a-layer-across-screens-no-longer-dies-when-the-poin.md +6 -0
  218. package/corpus/templates/design/changelog/2026-07-29-dragging-an-element-from-a-stored-screen-into-a-live-localho.md +6 -0
  219. package/corpus/templates/design/changelog/2026-07-29-drawing-a-box-on-the-canvas-and-dragging-it-into-a-connected.md +6 -0
  220. package/corpus/templates/design/changelog/2026-07-29-fixed-a-loading-screen-flicker-when-reopening-designs-with-a.md +6 -0
  221. package/corpus/templates/design/changelog/2026-07-29-interact-mode-has-edit-and-annotate-buttons-again-so-you-can.md +6 -0
  222. package/corpus/templates/design/changelog/2026-07-29-layer-groups-can-be-dropped-onto-live-localhost-screens.md +6 -0
  223. package/corpus/templates/design/changelog/2026-07-29-localhost-source-errors-now-say-what-actually-went-wrong-whi.md +6 -0
  224. package/corpus/templates/design/changelog/2026-07-29-opening-or-refreshing-a-live-localhost-screen-in-single-scre.md +6 -0
  225. package/corpus/templates/design/changelog/2026-07-29-redo-after-undoing-a-drop-onto-a-live-screen-puts-the-elemen.md +6 -0
  226. package/corpus/templates/design/changelog/2026-07-29-shapes-can-be-drawn-directly-onto-live-localhost-screens.md +6 -0
  227. package/corpus/templates/design/changelog/2026-07-29-shapes-drawn-on-the-canvas-board-now-appear-immediately-inst.md +6 -0
  228. package/corpus/templates/design/changelog/2026-07-29-style-edits-on-a-running-localhost-screen-now-preview-live-a.md +6 -0
  229. package/corpus/templates/design/changelog/2026-07-29-the-apply-design-updates-control-now-appears-as-a-clean-conn.md +6 -0
  230. package/corpus/templates/design/changelog/2026-07-29-visual-edits-on-a-mapped-component-instance-now-tell-the-age.md +6 -0
  231. package/corpus/templates/design/server/lib/design-data-access.ts +21 -7
  232. package/corpus/templates/design/server/lib/localhost-connection.ts +240 -0
  233. package/corpus/templates/design/server/routes/[...page].get.ts +2 -5
  234. package/corpus/templates/design/server/source-workspace.ts +2 -0
  235. package/corpus/templates/design/shared/code-layer.ts +43 -0
  236. package/corpus/templates/design/shared/html-content.ts +26 -0
  237. package/corpus/templates/design/shared/html-integrity.ts +21 -1
  238. package/corpus/templates/design/shared/local-jsx-visual-edit.ts +15 -0
  239. package/corpus/templates/design/shared/source-mode.ts +76 -0
  240. package/corpus/templates/dispatch/changelog/2026-07-29-workspace-members-without-admin-access-now-see-a-clear-permi.md +6 -0
  241. package/corpus/templates/dispatch/server/lib/usage-metrics.ts +2 -1
  242. package/corpus/templates/plan/app/lib/plan-annotation-runtime.ts +3 -4
  243. package/corpus/templates/plan/server/routes/[...page].get.ts +2 -5
  244. package/corpus/templates/slides/actions/patch-deck.ts +29 -17
  245. package/corpus/toolkit/CHANGELOG.md +6 -0
  246. package/corpus/toolkit/package.json +1 -1
  247. package/corpus/toolkit/src/composer/ComposerPlusMenu.tsx +3 -2
  248. package/dist/action-ui.d.ts +1 -0
  249. package/dist/action-ui.d.ts.map +1 -1
  250. package/dist/action-ui.js +1 -0
  251. package/dist/action-ui.js.map +1 -1
  252. package/dist/action.d.ts +5 -1
  253. package/dist/action.d.ts.map +1 -1
  254. package/dist/action.js.map +1 -1
  255. package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
  256. package/dist/agent/engine/ai-sdk-engine.js +13 -5
  257. package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
  258. package/dist/agent/harness/runner.d.ts.map +1 -1
  259. package/dist/agent/harness/runner.js +5 -0
  260. package/dist/agent/harness/runner.js.map +1 -1
  261. package/dist/agent/production-agent.d.ts +16 -0
  262. package/dist/agent/production-agent.d.ts.map +1 -1
  263. package/dist/agent/production-agent.js +18 -1
  264. package/dist/agent/production-agent.js.map +1 -1
  265. package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
  266. package/dist/agent/run-loop-with-resume.js +6 -0
  267. package/dist/agent/run-loop-with-resume.js.map +1 -1
  268. package/dist/agent/run-manager.d.ts +14 -0
  269. package/dist/agent/run-manager.d.ts.map +1 -1
  270. package/dist/agent/run-manager.js +91 -1
  271. package/dist/agent/run-manager.js.map +1 -1
  272. package/dist/agent/thread-data-builder.d.ts +3 -0
  273. package/dist/agent/thread-data-builder.d.ts.map +1 -1
  274. package/dist/agent/thread-data-builder.js +35 -3
  275. package/dist/agent/thread-data-builder.js.map +1 -1
  276. package/dist/agent/types.d.ts +2 -0
  277. package/dist/agent/types.d.ts.map +1 -1
  278. package/dist/agent/types.js.map +1 -1
  279. package/dist/chat-threads/store.d.ts.map +1 -1
  280. package/dist/chat-threads/store.js +4 -6
  281. package/dist/chat-threads/store.js.map +1 -1
  282. package/dist/cli/create.d.ts +66 -6
  283. package/dist/cli/create.d.ts.map +1 -1
  284. package/dist/cli/create.js +765 -66
  285. package/dist/cli/create.js.map +1 -1
  286. package/dist/cli/design-connect.d.ts.map +1 -1
  287. package/dist/cli/design-connect.js +2 -7
  288. package/dist/cli/design-connect.js.map +1 -1
  289. package/dist/cli/index.js +5 -3
  290. package/dist/cli/index.js.map +1 -1
  291. package/dist/cli/skills-content/design-visual-edit-skill.d.ts +1 -1
  292. package/dist/cli/skills-content/design-visual-edit-skill.d.ts.map +1 -1
  293. package/dist/cli/skills-content/design-visual-edit-skill.js +83 -47
  294. package/dist/cli/skills-content/design-visual-edit-skill.js.map +1 -1
  295. package/dist/client/AssistantChat.d.ts.map +1 -1
  296. package/dist/client/AssistantChat.js +16 -9
  297. package/dist/client/AssistantChat.js.map +1 -1
  298. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  299. package/dist/client/agent-chat-adapter.js +21 -0
  300. package/dist/client/agent-chat-adapter.js.map +1 -1
  301. package/dist/client/blocks/library/wireframe-kit.d.ts +2 -0
  302. package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -1
  303. package/dist/client/blocks/library/wireframe-kit.js +8 -2
  304. package/dist/client/blocks/library/wireframe-kit.js.map +1 -1
  305. package/dist/client/chat/index.d.ts +1 -1
  306. package/dist/client/chat/index.d.ts.map +1 -1
  307. package/dist/client/chat/index.js +1 -1
  308. package/dist/client/chat/index.js.map +1 -1
  309. package/dist/client/chat/message-components.d.ts +17 -0
  310. package/dist/client/chat/message-components.d.ts.map +1 -1
  311. package/dist/client/chat/message-components.js +24 -2
  312. package/dist/client/chat/message-components.js.map +1 -1
  313. package/dist/client/chat/run-recovery.d.ts +6 -0
  314. package/dist/client/chat/run-recovery.d.ts.map +1 -1
  315. package/dist/client/chat/run-recovery.js +13 -3
  316. package/dist/client/chat/run-recovery.js.map +1 -1
  317. package/dist/client/chat/widgets/DataTableWidget.d.ts.map +1 -1
  318. package/dist/client/chat/widgets/DataTableWidget.js +2 -3
  319. package/dist/client/chat/widgets/DataTableWidget.js.map +1 -1
  320. package/dist/client/chat/widgets/WorkspaceFileWidget.d.ts +5 -0
  321. package/dist/client/chat/widgets/WorkspaceFileWidget.d.ts.map +1 -0
  322. package/dist/client/chat/widgets/WorkspaceFileWidget.js +34 -0
  323. package/dist/client/chat/widgets/WorkspaceFileWidget.js.map +1 -0
  324. package/dist/client/chat/widgets/builtin-tool-renderers.d.ts.map +1 -1
  325. package/dist/client/chat/widgets/builtin-tool-renderers.js +13 -1
  326. package/dist/client/chat/widgets/builtin-tool-renderers.js.map +1 -1
  327. package/dist/client/chat/widgets/workspace-file-result.d.ts +12 -0
  328. package/dist/client/chat/widgets/workspace-file-result.d.ts.map +1 -0
  329. package/dist/client/chat/widgets/workspace-file-result.js +34 -0
  330. package/dist/client/chat/widgets/workspace-file-result.js.map +1 -0
  331. package/dist/client/embed-auth.d.ts.map +1 -1
  332. package/dist/client/embed-auth.js +2 -1
  333. package/dist/client/embed-auth.js.map +1 -1
  334. package/dist/client/org/InvitationBanner.d.ts +5 -2
  335. package/dist/client/org/InvitationBanner.d.ts.map +1 -1
  336. package/dist/client/org/InvitationBanner.js +9 -2
  337. package/dist/client/org/InvitationBanner.js.map +1 -1
  338. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  339. package/dist/client/org/OrgSwitcher.js +3 -2
  340. package/dist/client/org/OrgSwitcher.js.map +1 -1
  341. package/dist/client/org/TeamPage.d.ts.map +1 -1
  342. package/dist/client/org/TeamPage.js +27 -3
  343. package/dist/client/org/TeamPage.js.map +1 -1
  344. package/dist/client/org/WorkspaceNotice.d.ts +18 -0
  345. package/dist/client/org/WorkspaceNotice.d.ts.map +1 -0
  346. package/dist/client/org/WorkspaceNotice.js +56 -0
  347. package/dist/client/org/WorkspaceNotice.js.map +1 -0
  348. package/dist/client/org/hooks.d.ts +1 -0
  349. package/dist/client/org/hooks.d.ts.map +1 -1
  350. package/dist/client/org/hooks.js +12 -0
  351. package/dist/client/org/hooks.js.map +1 -1
  352. package/dist/client/org/index.d.ts +2 -1
  353. package/dist/client/org/index.d.ts.map +1 -1
  354. package/dist/client/org/index.js +2 -1
  355. package/dist/client/org/index.js.map +1 -1
  356. package/dist/client/resources/McpIntegrationDialog.d.ts.map +1 -1
  357. package/dist/client/resources/McpIntegrationDialog.js +8 -12
  358. package/dist/client/resources/McpIntegrationDialog.js.map +1 -1
  359. package/dist/client/resources/ResourcesPanel.d.ts +3 -0
  360. package/dist/client/resources/ResourcesPanel.d.ts.map +1 -1
  361. package/dist/client/resources/ResourcesPanel.js +49 -20
  362. package/dist/client/resources/ResourcesPanel.js.map +1 -1
  363. package/dist/client/resources/mcp-integration-catalog.d.ts +1 -0
  364. package/dist/client/resources/mcp-integration-catalog.d.ts.map +1 -1
  365. package/dist/client/resources/mcp-integration-catalog.js +3 -0
  366. package/dist/client/resources/mcp-integration-catalog.js.map +1 -1
  367. package/dist/client/resources/use-resources.d.ts +1 -0
  368. package/dist/client/resources/use-resources.d.ts.map +1 -1
  369. package/dist/client/resources/use-resources.js +3 -0
  370. package/dist/client/resources/use-resources.js.map +1 -1
  371. package/dist/client/use-action.d.ts +3 -1
  372. package/dist/client/use-action.d.ts.map +1 -1
  373. package/dist/client/use-action.js +3 -1
  374. package/dist/client/use-action.js.map +1 -1
  375. package/dist/client/widgets/index.d.ts +1 -1
  376. package/dist/client/widgets/index.d.ts.map +1 -1
  377. package/dist/client/widgets/index.js +1 -1
  378. package/dist/client/widgets/index.js.map +1 -1
  379. package/dist/coding-tools/run-code.js +2 -1
  380. package/dist/coding-tools/run-code.js.map +1 -1
  381. package/dist/collab/awareness.d.ts +2 -2
  382. package/dist/collab/awareness.d.ts.map +1 -1
  383. package/dist/collab/routes.d.ts +1 -1
  384. package/dist/db/client.d.ts +1 -0
  385. package/dist/db/client.d.ts.map +1 -1
  386. package/dist/db/client.js +24 -3
  387. package/dist/db/client.js.map +1 -1
  388. package/dist/deploy/build.d.ts.map +1 -1
  389. package/dist/deploy/build.js +25 -3
  390. package/dist/deploy/build.js.map +1 -1
  391. package/dist/eject/remote-mcp-presets.js +1 -1
  392. package/dist/eject/remote-mcp-presets.js.map +1 -1
  393. package/dist/index.browser.d.ts +1 -1
  394. package/dist/index.browser.d.ts.map +1 -1
  395. package/dist/index.browser.js +1 -1
  396. package/dist/index.browser.js.map +1 -1
  397. package/dist/index.d.ts +1 -1
  398. package/dist/index.d.ts.map +1 -1
  399. package/dist/index.js +1 -1
  400. package/dist/index.js.map +1 -1
  401. package/dist/integrations/google-docs-poller.d.ts.map +1 -1
  402. package/dist/integrations/google-docs-poller.js +4 -1
  403. package/dist/integrations/google-docs-poller.js.map +1 -1
  404. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  405. package/dist/integrations/webhook-handler.js +23 -11
  406. package/dist/integrations/webhook-handler.js.map +1 -1
  407. package/dist/jobs/scheduler.js +4 -0
  408. package/dist/jobs/scheduler.js.map +1 -1
  409. package/dist/localization/default-messages.d.ts +3 -2
  410. package/dist/localization/default-messages.d.ts.map +1 -1
  411. package/dist/localization/default-messages.js +3 -2
  412. package/dist/localization/default-messages.js.map +1 -1
  413. package/dist/mcp/build-server.d.ts.map +1 -1
  414. package/dist/mcp/build-server.js +10 -1
  415. package/dist/mcp/build-server.js.map +1 -1
  416. package/dist/notifications/routes.d.ts +1 -1
  417. package/dist/observability/traces.d.ts.map +1 -1
  418. package/dist/observability/traces.js +27 -7
  419. package/dist/observability/traces.js.map +1 -1
  420. package/dist/org/handlers.d.ts +15 -0
  421. package/dist/org/handlers.d.ts.map +1 -1
  422. package/dist/org/handlers.js +45 -1
  423. package/dist/org/handlers.js.map +1 -1
  424. package/dist/org/migrations.d.ts.map +1 -1
  425. package/dist/org/migrations.js +4 -0
  426. package/dist/org/migrations.js.map +1 -1
  427. package/dist/org/plugin.d.ts +1 -0
  428. package/dist/org/plugin.d.ts.map +1 -1
  429. package/dist/org/plugin.js +10 -1
  430. package/dist/org/plugin.js.map +1 -1
  431. package/dist/org/schema.d.ts +19 -0
  432. package/dist/org/schema.d.ts.map +1 -1
  433. package/dist/org/schema.js +1 -0
  434. package/dist/org/schema.js.map +1 -1
  435. package/dist/org/types.d.ts +8 -0
  436. package/dist/org/types.d.ts.map +1 -1
  437. package/dist/org/types.js.map +1 -1
  438. package/dist/org/workspace-url.d.ts +32 -0
  439. package/dist/org/workspace-url.d.ts.map +1 -0
  440. package/dist/org/workspace-url.js +63 -0
  441. package/dist/org/workspace-url.js.map +1 -0
  442. package/dist/progress/routes.d.ts +1 -1
  443. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  444. package/dist/provider-api/actions/provider-api.d.ts +7 -7
  445. package/dist/resources/handlers.d.ts +2 -3
  446. package/dist/resources/handlers.d.ts.map +1 -1
  447. package/dist/resources/handlers.js +25 -5
  448. package/dist/resources/handlers.js.map +1 -1
  449. package/dist/scripts/runner.d.ts +17 -0
  450. package/dist/scripts/runner.d.ts.map +1 -1
  451. package/dist/scripts/runner.js +130 -5
  452. package/dist/scripts/runner.js.map +1 -1
  453. package/dist/secrets/routes.d.ts +9 -9
  454. package/dist/server/action-routes.d.ts.map +1 -1
  455. package/dist/server/action-routes.js +24 -2
  456. package/dist/server/action-routes.js.map +1 -1
  457. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  458. package/dist/server/agent-chat-plugin.js +26 -1
  459. package/dist/server/agent-chat-plugin.js.map +1 -1
  460. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  461. package/dist/server/agent-teams.d.ts.map +1 -1
  462. package/dist/server/agent-teams.js +10 -1
  463. package/dist/server/agent-teams.js.map +1 -1
  464. package/dist/server/auth.d.ts +22 -0
  465. package/dist/server/auth.d.ts.map +1 -1
  466. package/dist/server/auth.js +33 -7
  467. package/dist/server/auth.js.map +1 -1
  468. package/dist/server/embed-route.d.ts.map +1 -1
  469. package/dist/server/embed-route.js +6 -1
  470. package/dist/server/embed-route.js.map +1 -1
  471. package/dist/server/embed-session.d.ts +7 -0
  472. package/dist/server/embed-session.d.ts.map +1 -1
  473. package/dist/server/embed-session.js +46 -4
  474. package/dist/server/embed-session.js.map +1 -1
  475. package/dist/server/open-route.d.ts.map +1 -1
  476. package/dist/server/open-route.js +17 -7
  477. package/dist/server/open-route.js.map +1 -1
  478. package/dist/server/prompts/framework-core-compact.js +1 -1
  479. package/dist/server/prompts/framework-core-compact.js.map +1 -1
  480. package/dist/server/prompts/framework-core.d.ts.map +1 -1
  481. package/dist/server/prompts/framework-core.js +1 -0
  482. package/dist/server/prompts/framework-core.js.map +1 -1
  483. package/dist/server/request-context.d.ts +26 -0
  484. package/dist/server/request-context.d.ts.map +1 -1
  485. package/dist/server/request-context.js +18 -1
  486. package/dist/server/request-context.js.map +1 -1
  487. package/dist/shared/embed-auth.d.ts +1 -0
  488. package/dist/shared/embed-auth.d.ts.map +1 -1
  489. package/dist/shared/embed-auth.js +1 -0
  490. package/dist/shared/embed-auth.js.map +1 -1
  491. package/dist/shared/html-document.d.ts +12 -0
  492. package/dist/shared/html-document.d.ts.map +1 -0
  493. package/dist/shared/html-document.js +92 -0
  494. package/dist/shared/html-document.js.map +1 -0
  495. package/dist/shared/index.d.ts +1 -0
  496. package/dist/shared/index.d.ts.map +1 -1
  497. package/dist/shared/index.js +1 -0
  498. package/dist/shared/index.js.map +1 -1
  499. package/dist/sharing/access.d.ts +1 -0
  500. package/dist/sharing/access.d.ts.map +1 -1
  501. package/dist/sharing/access.js +8 -2
  502. package/dist/sharing/access.js.map +1 -1
  503. package/dist/sharing/registry.d.ts +3 -0
  504. package/dist/sharing/registry.d.ts.map +1 -1
  505. package/dist/sharing/registry.js.map +1 -1
  506. package/dist/workspace-files/actions.d.ts +28 -0
  507. package/dist/workspace-files/actions.d.ts.map +1 -0
  508. package/dist/workspace-files/actions.js +75 -0
  509. package/dist/workspace-files/actions.js.map +1 -0
  510. package/dist/workspace-files/tool.d.ts.map +1 -1
  511. package/dist/workspace-files/tool.js +4 -0
  512. package/dist/workspace-files/tool.js.map +1 -1
  513. package/docs/content/cloneable-saas.mdx +114 -0
  514. package/docs/content/creating-templates.mdx +28 -2
  515. package/docs/content/syncing-template-changes.mdx +7 -0
  516. package/docs/content/template-clips-developers.mdx +12 -5
  517. package/docs/content/template-dispatch-developers.mdx +1 -1
  518. package/docs/content/template-dispatch.mdx +1 -1
  519. package/package.json +2 -2
  520. package/src/action-ui.ts +1 -0
  521. package/src/action.ts +5 -1
  522. package/src/agent/engine/ai-sdk-engine.ts +15 -5
  523. package/src/agent/harness/runner.ts +5 -0
  524. package/src/agent/production-agent.ts +33 -0
  525. package/src/agent/run-loop-with-resume.ts +5 -0
  526. package/src/agent/run-manager.ts +126 -1
  527. package/src/agent/thread-data-builder.ts +47 -2
  528. package/src/agent/types.ts +2 -0
  529. package/src/chat-threads/store.ts +4 -5
  530. package/src/cli/create.ts +1139 -65
  531. package/src/cli/design-connect.ts +3 -7
  532. package/src/cli/index.ts +5 -3
  533. package/src/cli/skills-content/design-visual-edit-skill.ts +83 -47
  534. package/src/client/AssistantChat.tsx +27 -16
  535. package/src/client/agent-chat-adapter.ts +24 -0
  536. package/src/client/blocks/library/wireframe-kit.tsx +17 -2
  537. package/src/client/chat/index.ts +1 -0
  538. package/src/client/chat/message-components.tsx +104 -1
  539. package/src/client/chat/run-recovery.tsx +15 -3
  540. package/src/client/chat/widgets/DataTableWidget.tsx +10 -29
  541. package/src/client/chat/widgets/WorkspaceFileWidget.tsx +71 -0
  542. package/src/client/chat/widgets/builtin-tool-renderers.tsx +22 -0
  543. package/src/client/chat/widgets/workspace-file-result.ts +49 -0
  544. package/src/client/embed-auth.ts +2 -0
  545. package/src/client/org/InvitationBanner.tsx +14 -1
  546. package/src/client/org/OrgSwitcher.tsx +23 -0
  547. package/src/client/org/TeamPage.tsx +130 -0
  548. package/src/client/org/WorkspaceNotice.tsx +85 -0
  549. package/src/client/org/hooks.ts +14 -0
  550. package/src/client/org/index.ts +2 -0
  551. package/src/client/resources/McpIntegrationDialog.tsx +27 -40
  552. package/src/client/resources/ResourcesPanel.tsx +134 -51
  553. package/src/client/resources/mcp-integration-catalog.ts +7 -0
  554. package/src/client/resources/use-resources.ts +6 -0
  555. package/src/client/use-action.ts +3 -1
  556. package/src/client/widgets/index.ts +1 -0
  557. package/src/coding-tools/run-code.ts +2 -1
  558. package/src/db/client.ts +28 -4
  559. package/src/deploy/build.ts +31 -3
  560. package/src/eject/remote-mcp-presets.ts +1 -1
  561. package/src/index.browser.ts +1 -0
  562. package/src/index.ts +1 -0
  563. package/src/integrations/google-docs-poller.ts +3 -0
  564. package/src/integrations/webhook-handler.ts +28 -11
  565. package/src/jobs/scheduler.ts +4 -0
  566. package/src/localization/default-messages.ts +3 -2
  567. package/src/mcp/build-server.ts +20 -2
  568. package/src/observability/traces.ts +48 -12
  569. package/src/org/handlers.ts +52 -1
  570. package/src/org/migrations.ts +4 -0
  571. package/src/org/plugin.ts +14 -0
  572. package/src/org/schema.ts +1 -0
  573. package/src/org/types.ts +8 -0
  574. package/src/org/workspace-url.ts +74 -0
  575. package/src/resources/handlers.ts +35 -5
  576. package/src/scripts/runner.ts +183 -5
  577. package/src/server/__snapshots__/agent-chat-plugin.surface.spec.ts.snap +2 -1
  578. package/src/server/action-routes.ts +32 -2
  579. package/src/server/agent-chat-plugin.ts +29 -0
  580. package/src/server/agent-teams.ts +10 -1
  581. package/src/server/auth.ts +33 -7
  582. package/src/server/embed-route.ts +9 -0
  583. package/src/server/embed-session.ts +62 -3
  584. package/src/server/open-route.ts +20 -6
  585. package/src/server/prompts/framework-core-compact.ts +1 -1
  586. package/src/server/prompts/framework-core.ts +1 -0
  587. package/src/server/request-context.ts +36 -1
  588. package/src/shared/embed-auth.ts +1 -0
  589. package/src/shared/html-document.ts +115 -0
  590. package/src/shared/index.ts +1 -0
  591. package/src/sharing/access.ts +8 -1
  592. package/src/sharing/registry.ts +11 -2
  593. package/src/workspace-files/actions.ts +100 -0
  594. package/src/workspace-files/tool.ts +4 -0
  595. package/corpus/templates/design/changelog/2026-07-28-interact-mode-can-preview-designs-in-responsive-device-sizes.md +0 -6
  596. package/corpus/templates/design/changelog/2026-07-28-interact-mode-now-opens-a-full-screen-responsive-preview-wit.md +0 -6
@@ -0,0 +1,92 @@
1
+ const RAW_TEXT_TAG_NAMES = new Set(["script", "style", "textarea", "title"]);
2
+ function tagEndIndex(html, start) {
3
+ let quote = null;
4
+ for (let index = start; index < html.length; index += 1) {
5
+ const character = html[index];
6
+ if (quote) {
7
+ if (character === quote)
8
+ quote = null;
9
+ continue;
10
+ }
11
+ if (character === '"' || character === "'") {
12
+ quote = character;
13
+ continue;
14
+ }
15
+ if (character === ">")
16
+ return index;
17
+ }
18
+ return -1;
19
+ }
20
+ function tagNameAt(html, start) {
21
+ const match = /^[A-Za-z][\w:-]*/.exec(html.slice(start));
22
+ return match?.[0]?.toLowerCase() ?? null;
23
+ }
24
+ function rawTextCloseIndex(html, tag, start) {
25
+ const pattern = new RegExp(`<\\/${tag}\\s*>`, "gi");
26
+ pattern.lastIndex = start;
27
+ return pattern.exec(html)?.index ?? -1;
28
+ }
29
+ function closingTagIndex(html, tag, last) {
30
+ let index = -1;
31
+ let cursor = 0;
32
+ while (cursor < html.length) {
33
+ const tagStart = html.indexOf("<", cursor);
34
+ if (tagStart === -1)
35
+ break;
36
+ if (html.startsWith("<!--", tagStart)) {
37
+ const commentEnd = html.indexOf("-->", tagStart + 4);
38
+ cursor = commentEnd === -1 ? html.length : commentEnd + 3;
39
+ continue;
40
+ }
41
+ const closing = html[tagStart + 1] === "/";
42
+ const nameStart = tagStart + (closing ? 2 : 1);
43
+ const tagName = tagNameAt(html, nameStart);
44
+ if (!tagName) {
45
+ cursor = tagStart + 1;
46
+ continue;
47
+ }
48
+ const tagEnd = tagEndIndex(html, nameStart + tagName.length);
49
+ if (tagEnd === -1)
50
+ break;
51
+ if (closing &&
52
+ tagName === tag &&
53
+ !/\S/.test(html.slice(nameStart + tagName.length, tagEnd))) {
54
+ index = tagStart;
55
+ if (!last)
56
+ return index;
57
+ }
58
+ if (!closing && RAW_TEXT_TAG_NAMES.has(tagName)) {
59
+ const rawTextEnd = rawTextCloseIndex(html, tagName, tagEnd + 1);
60
+ cursor =
61
+ rawTextEnd === -1 ? html.length : rawTextEnd + tagName.length + 3;
62
+ continue;
63
+ }
64
+ cursor = tagEnd + 1;
65
+ }
66
+ return index;
67
+ }
68
+ /**
69
+ * Inserts markup at a document boundary without using String.replace's
70
+ * replacement-string semantics. It ignores raw text, attributes, and comments
71
+ * when locating a real closing tag. Body and html closers use their last
72
+ * marker so literal close tags cannot capture the injection.
73
+ */
74
+ export function injectDocumentMarkup(html, markup, options = {}) {
75
+ if (!markup)
76
+ return html;
77
+ const target = options.target ?? "body";
78
+ const targetIndex = closingTagIndex(html, target, target === "body");
79
+ if (targetIndex !== -1) {
80
+ return html.slice(0, targetIndex) + markup + html.slice(targetIndex);
81
+ }
82
+ const bodyIndex = closingTagIndex(html, "body", true);
83
+ if (bodyIndex !== -1) {
84
+ return html.slice(0, bodyIndex) + markup + html.slice(bodyIndex);
85
+ }
86
+ const htmlIndex = closingTagIndex(html, "html", true);
87
+ if (htmlIndex !== -1) {
88
+ return html.slice(0, htmlIndex) + markup + html.slice(htmlIndex);
89
+ }
90
+ return html + markup;
91
+ }
92
+ //# sourceMappingURL=html-document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-document.js","sourceRoot":"","sources":["../../src/shared/html-document.ts"],"names":[],"mappings":"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","sourcesContent":["type HtmlInjectionTarget = \"head\" | \"body\";\nconst RAW_TEXT_TAG_NAMES = new Set([\"script\", \"style\", \"textarea\", \"title\"]);\n\nfunction tagEndIndex(html: string, start: number): number {\n let quote: '\"' | \"'\" | null = null;\n\n for (let index = start; index < html.length; index += 1) {\n const character = html[index];\n if (quote) {\n if (character === quote) quote = null;\n continue;\n }\n if (character === '\"' || character === \"'\") {\n quote = character;\n continue;\n }\n if (character === \">\") return index;\n }\n\n return -1;\n}\n\nfunction tagNameAt(html: string, start: number): string | null {\n const match = /^[A-Za-z][\\w:-]*/.exec(html.slice(start));\n return match?.[0]?.toLowerCase() ?? null;\n}\n\nfunction rawTextCloseIndex(html: string, tag: string, start: number): number {\n const pattern = new RegExp(`<\\\\/${tag}\\\\s*>`, \"gi\");\n pattern.lastIndex = start;\n return pattern.exec(html)?.index ?? -1;\n}\n\nfunction closingTagIndex(\n html: string,\n tag: HtmlInjectionTarget | \"html\",\n last: boolean,\n): number {\n let index = -1;\n let cursor = 0;\n\n while (cursor < html.length) {\n const tagStart = html.indexOf(\"<\", cursor);\n if (tagStart === -1) break;\n\n if (html.startsWith(\"<!--\", tagStart)) {\n const commentEnd = html.indexOf(\"-->\", tagStart + 4);\n cursor = commentEnd === -1 ? html.length : commentEnd + 3;\n continue;\n }\n\n const closing = html[tagStart + 1] === \"/\";\n const nameStart = tagStart + (closing ? 2 : 1);\n const tagName = tagNameAt(html, nameStart);\n if (!tagName) {\n cursor = tagStart + 1;\n continue;\n }\n\n const tagEnd = tagEndIndex(html, nameStart + tagName.length);\n if (tagEnd === -1) break;\n\n if (\n closing &&\n tagName === tag &&\n !/\\S/.test(html.slice(nameStart + tagName.length, tagEnd))\n ) {\n index = tagStart;\n if (!last) return index;\n }\n\n if (!closing && RAW_TEXT_TAG_NAMES.has(tagName)) {\n const rawTextEnd = rawTextCloseIndex(html, tagName, tagEnd + 1);\n cursor =\n rawTextEnd === -1 ? html.length : rawTextEnd + tagName.length + 3;\n continue;\n }\n\n cursor = tagEnd + 1;\n }\n\n return index;\n}\n\n/**\n * Inserts markup at a document boundary without using String.replace's\n * replacement-string semantics. It ignores raw text, attributes, and comments\n * when locating a real closing tag. Body and html closers use their last\n * marker so literal close tags cannot capture the injection.\n */\nexport function injectDocumentMarkup(\n html: string,\n markup: string,\n options: { target?: HtmlInjectionTarget } = {},\n): string {\n if (!markup) return html;\n\n const target = options.target ?? \"body\";\n const targetIndex = closingTagIndex(html, target, target === \"body\");\n if (targetIndex !== -1) {\n return html.slice(0, targetIndex) + markup + html.slice(targetIndex);\n }\n\n const bodyIndex = closingTagIndex(html, \"body\", true);\n if (bodyIndex !== -1) {\n return html.slice(0, bodyIndex) + markup + html.slice(bodyIndex);\n }\n\n const htmlIndex = closingTagIndex(html, \"html\", true);\n if (htmlIndex !== -1) {\n return html.slice(0, htmlIndex) + markup + html.slice(htmlIndex);\n }\n\n return html + markup;\n}\n"]}
@@ -3,6 +3,7 @@ export { agentEnv, type EnvVar } from "./agent-env.js";
3
3
  export { extractOAuthStateAppId } from "./oauth-state.js";
4
4
  export { SIGN_IN_CONTINUATION_MAX_LENGTH, SIGN_IN_CONTINUATION_PARAM, SIGN_IN_ENTRY_PATH, SIGN_IN_LEGACY_RETURN_PARAM, decodeContinuation, encodeContinuation, normalizeAppPath, signInJourney, signInJourneyInlineScript, type SignInJourney, type SignInJourneyInput, } from "./sign-in-journey.js";
5
5
  export { truncate } from "./truncate.js";
6
+ export { injectDocumentMarkup } from "./html-document.js";
6
7
  export { withBuilderUtmTrackingParams } from "./builder-link-tracking.js";
7
8
  export { llmConnectionTrackingProperties, normalizeLlmConnection, type LlmConnectionStatus, } from "./llm-connection.js";
8
9
  export { DISPATCH_WORKSPACE_ROOT_REDIRECTS, RESERVED_WORKSPACE_APP_IDS, assertValidWorkspaceAppId, getWorkspaceAppIdValidationError, isValidWorkspaceAppIdFormat, } from "./workspace-app-id.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shared/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EACL,+BAA+B,EAC/B,0BAA0B,EAC1B,kBAAkB,EAClB,2BAA2B,EAC3B,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,yBAAyB,EACzB,KAAK,aAAa,EAClB,KAAK,kBAAkB,GACxB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EACL,+BAA+B,EAC/B,sBAAsB,EACtB,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,iCAAiC,EACjC,0BAA0B,EAC1B,yBAAyB,EACzB,gCAAgC,EAChC,2BAA2B,GAC5B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,8BAA8B,EAC9B,uBAAuB,EACvB,6BAA6B,EAC7B,6BAA6B,EAC7B,2BAA2B,EAC3B,mCAAmC,EACnC,8BAA8B,EAC9B,sCAAsC,EACtC,KAAK,uBAAuB,EAC5B,KAAK,iCAAiC,EACtC,KAAK,oBAAoB,GAC1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,gCAAgC,EAChC,yBAAyB,EACzB,8BAA8B,GAC/B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,iCAAiC,EACjC,sCAAsC,EACtC,6BAA6B,EAC7B,gCAAgC,EAChC,8BAA8B,EAC9B,8BAA8B,EAC9B,+BAA+B,EAC/B,sBAAsB,EACtB,qCAAqC,EACrC,sBAAsB,EACtB,KAAK,oBAAoB,GAC1B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,kBAAkB,EAClB,gCAAgC,EAChC,sBAAsB,EACtB,sBAAsB,EACtB,mBAAmB,EACnB,4BAA4B,EAC5B,0BAA0B,EAC1B,2BAA2B,EAC3B,gBAAgB,EAChB,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,GAC3B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,oCAAoC,EACpC,mCAAmC,EACnC,mCAAmC,EACnC,0CAA0C,EAC1C,eAAe,EACf,KAAK,8BAA8B,EACnC,KAAK,0CAA0C,GAChD,MAAM,8BAA8B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shared/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAE,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EACL,+BAA+B,EAC/B,0BAA0B,EAC1B,kBAAkB,EAClB,2BAA2B,EAC3B,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,yBAAyB,EACzB,KAAK,aAAa,EAClB,KAAK,kBAAkB,GACxB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EACL,+BAA+B,EAC/B,sBAAsB,EACtB,KAAK,mBAAmB,GACzB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,iCAAiC,EACjC,0BAA0B,EAC1B,yBAAyB,EACzB,gCAAgC,EAChC,2BAA2B,GAC5B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,8BAA8B,EAC9B,uBAAuB,EACvB,6BAA6B,EAC7B,6BAA6B,EAC7B,2BAA2B,EAC3B,mCAAmC,EACnC,8BAA8B,EAC9B,sCAAsC,EACtC,KAAK,uBAAuB,EAC5B,KAAK,iCAAiC,EACtC,KAAK,oBAAoB,GAC1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,gCAAgC,EAChC,yBAAyB,EACzB,8BAA8B,GAC/B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,iCAAiC,EACjC,sCAAsC,EACtC,6BAA6B,EAC7B,gCAAgC,EAChC,8BAA8B,EAC9B,8BAA8B,EAC9B,+BAA+B,EAC/B,sBAAsB,EACtB,qCAAqC,EACrC,sBAAsB,EACtB,KAAK,oBAAoB,GAC1B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,kBAAkB,EAClB,gCAAgC,EAChC,sBAAsB,EACtB,sBAAsB,EACtB,mBAAmB,EACnB,4BAA4B,EAC5B,0BAA0B,EAC1B,2BAA2B,EAC3B,gBAAgB,EAChB,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,GAC3B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,oCAAoC,EACpC,mCAAmC,EACnC,mCAAmC,EACnC,0CAA0C,EAC1C,eAAe,EACf,KAAK,8BAA8B,EACnC,KAAK,0CAA0C,GAChD,MAAM,8BAA8B,CAAC"}
@@ -3,6 +3,7 @@ export { agentEnv } from "./agent-env.js";
3
3
  export { extractOAuthStateAppId } from "./oauth-state.js";
4
4
  export { SIGN_IN_CONTINUATION_MAX_LENGTH, SIGN_IN_CONTINUATION_PARAM, SIGN_IN_ENTRY_PATH, SIGN_IN_LEGACY_RETURN_PARAM, decodeContinuation, encodeContinuation, normalizeAppPath, signInJourney, signInJourneyInlineScript, } from "./sign-in-journey.js";
5
5
  export { truncate } from "./truncate.js";
6
+ export { injectDocumentMarkup } from "./html-document.js";
6
7
  export { withBuilderUtmTrackingParams } from "./builder-link-tracking.js";
7
8
  export { llmConnectionTrackingProperties, normalizeLlmConnection, } from "./llm-connection.js";
8
9
  export { DISPATCH_WORKSPACE_ROOT_REDIRECTS, RESERVED_WORKSPACE_APP_IDS, assertValidWorkspaceAppId, getWorkspaceAppIdValidationError, isValidWorkspaceAppIdFormat, } from "./workspace-app-id.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/shared/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,GAIV,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAe,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EACL,+BAA+B,EAC/B,0BAA0B,EAC1B,kBAAkB,EAClB,2BAA2B,EAC3B,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,yBAAyB,GAG1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EACL,+BAA+B,EAC/B,sBAAsB,GAEvB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,iCAAiC,EACjC,0BAA0B,EAC1B,yBAAyB,EACzB,gCAAgC,EAChC,2BAA2B,GAC5B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,8BAA8B,EAC9B,uBAAuB,EACvB,6BAA6B,EAC7B,6BAA6B,EAC7B,2BAA2B,EAC3B,mCAAmC,EACnC,8BAA8B,EAC9B,sCAAsC,GAIvC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,gCAAgC,EAChC,yBAAyB,EACzB,8BAA8B,GAC/B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,iCAAiC,EACjC,sCAAsC,EACtC,6BAA6B,EAC7B,gCAAgC,EAChC,8BAA8B,EAC9B,8BAA8B,EAC9B,+BAA+B,EAC/B,sBAAsB,EACtB,qCAAqC,EACrC,sBAAsB,GAEvB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,kBAAkB,EAClB,gCAAgC,EAChC,sBAAsB,EACtB,sBAAsB,EACtB,mBAAmB,EACnB,4BAA4B,EAC5B,0BAA0B,EAC1B,2BAA2B,EAC3B,gBAAgB,GAIjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,oCAAoC,EACpC,mCAAmC,EACnC,mCAAmC,EACnC,0CAA0C,EAC1C,eAAe,GAGhB,MAAM,8BAA8B,CAAC","sourcesContent":["export {\n agentChat,\n type AgentChatMessage,\n type AgentChatCallOptions,\n type AgentChatResponse,\n} from \"./agent-chat.js\";\nexport { agentEnv, type EnvVar } from \"./agent-env.js\";\nexport { extractOAuthStateAppId } from \"./oauth-state.js\";\nexport {\n SIGN_IN_CONTINUATION_MAX_LENGTH,\n SIGN_IN_CONTINUATION_PARAM,\n SIGN_IN_ENTRY_PATH,\n SIGN_IN_LEGACY_RETURN_PARAM,\n decodeContinuation,\n encodeContinuation,\n normalizeAppPath,\n signInJourney,\n signInJourneyInlineScript,\n type SignInJourney,\n type SignInJourneyInput,\n} from \"./sign-in-journey.js\";\nexport { truncate } from \"./truncate.js\";\nexport { withBuilderUtmTrackingParams } from \"./builder-link-tracking.js\";\nexport {\n llmConnectionTrackingProperties,\n normalizeLlmConnection,\n type LlmConnectionStatus,\n} from \"./llm-connection.js\";\nexport {\n DISPATCH_WORKSPACE_ROOT_REDIRECTS,\n RESERVED_WORKSPACE_APP_IDS,\n assertValidWorkspaceAppId,\n getWorkspaceAppIdValidationError,\n isValidWorkspaceAppIdFormat,\n} from \"./workspace-app-id.js\";\nexport {\n DEFAULT_WORKSPACE_APP_AUDIENCE,\n WORKSPACE_APP_AUDIENCES,\n normalizeWorkspaceAppAudience,\n normalizeWorkspaceAppPathList,\n workspaceAppAudienceFromEnv,\n workspaceAppAudienceFromPackageJson,\n workspaceAppRouteAccessFromEnv,\n workspaceAppRouteAccessFromPackageJson,\n type WorkspaceAppRouteAccess,\n type WorkspaceAppRouteAccessFromConfig,\n type WorkspaceAppAudience,\n} from \"./workspace-app-audience.js\";\nexport {\n AGENT_NATIVE_OPEN_PATH,\n AGENT_SIDEBAR_QUERY_PARAM,\n AGENT_SIDEBAR_QUERY_VALUE_CLOSED,\n isAgentNativeOpenDeepLink,\n withCollapsedAgentSidebarParam,\n} from \"./agent-sidebar-url.js\";\nexport {\n AGENT_NATIVE_DEFAULT_SOCIAL_IMAGE,\n AGENT_NATIVE_SOCIAL_IMAGE_CACHE_BUSTER,\n AGENT_NATIVE_SOCIAL_IMAGE_ALT,\n AGENT_NATIVE_SOCIAL_IMAGE_HEIGHT,\n AGENT_NATIVE_SOCIAL_IMAGE_PATH,\n AGENT_NATIVE_SOCIAL_IMAGE_TYPE,\n AGENT_NATIVE_SOCIAL_IMAGE_WIDTH,\n defaultSocialImageMeta,\n withAgentNativeSocialImageCacheBuster,\n withDefaultSocialImage,\n type SocialMetaDescriptor,\n} from \"./social-meta.js\";\nexport {\n EMBED_MODE_QUERY_PARAM,\n EMBED_SESSION_COOKIE,\n EMBED_START_PATH,\n EMBED_TOKEN_QUERY_PARAM,\n} from \"./embed-auth.js\";\nexport {\n AGENT_ACCESS_PARAM,\n DEFAULT_AGENT_ACCESS_TTL_SECONDS,\n appendAgentAccessParam,\n buildAgentAccessApiUrl,\n buildAgentAccessUrl,\n normalizeAgentAccessBasePath,\n normalizeAgentAccessOrigin,\n scopedAgentAccessResourceId,\n toAgentAccessUrl,\n type AgentAccessApiUrlOptions,\n type AgentAccessResourceScope,\n type AgentAccessUrlOptions,\n} from \"./agent-access.js\";\nexport {\n AGENT_READABLE_RESOURCE_PAYLOAD_TYPE,\n AGENT_READABLE_RESOURCE_SCRIPT_TYPE,\n buildAgentReadableResourceDiscovery,\n renderAgentReadableResourceDiscoveryScript,\n safeJsonForHtml,\n type AgentReadableResourceDiscovery,\n type BuildAgentReadableResourceDiscoveryOptions,\n} from \"./agent-readable-resource.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/shared/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,GAIV,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAe,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EACL,+BAA+B,EAC/B,0BAA0B,EAC1B,kBAAkB,EAClB,2BAA2B,EAC3B,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,EACb,yBAAyB,GAG1B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EACL,+BAA+B,EAC/B,sBAAsB,GAEvB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,iCAAiC,EACjC,0BAA0B,EAC1B,yBAAyB,EACzB,gCAAgC,EAChC,2BAA2B,GAC5B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,8BAA8B,EAC9B,uBAAuB,EACvB,6BAA6B,EAC7B,6BAA6B,EAC7B,2BAA2B,EAC3B,mCAAmC,EACnC,8BAA8B,EAC9B,sCAAsC,GAIvC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,sBAAsB,EACtB,yBAAyB,EACzB,gCAAgC,EAChC,yBAAyB,EACzB,8BAA8B,GAC/B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,iCAAiC,EACjC,sCAAsC,EACtC,6BAA6B,EAC7B,gCAAgC,EAChC,8BAA8B,EAC9B,8BAA8B,EAC9B,+BAA+B,EAC/B,sBAAsB,EACtB,qCAAqC,EACrC,sBAAsB,GAEvB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,gBAAgB,EAChB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,kBAAkB,EAClB,gCAAgC,EAChC,sBAAsB,EACtB,sBAAsB,EACtB,mBAAmB,EACnB,4BAA4B,EAC5B,0BAA0B,EAC1B,2BAA2B,EAC3B,gBAAgB,GAIjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,oCAAoC,EACpC,mCAAmC,EACnC,mCAAmC,EACnC,0CAA0C,EAC1C,eAAe,GAGhB,MAAM,8BAA8B,CAAC","sourcesContent":["export {\n agentChat,\n type AgentChatMessage,\n type AgentChatCallOptions,\n type AgentChatResponse,\n} from \"./agent-chat.js\";\nexport { agentEnv, type EnvVar } from \"./agent-env.js\";\nexport { extractOAuthStateAppId } from \"./oauth-state.js\";\nexport {\n SIGN_IN_CONTINUATION_MAX_LENGTH,\n SIGN_IN_CONTINUATION_PARAM,\n SIGN_IN_ENTRY_PATH,\n SIGN_IN_LEGACY_RETURN_PARAM,\n decodeContinuation,\n encodeContinuation,\n normalizeAppPath,\n signInJourney,\n signInJourneyInlineScript,\n type SignInJourney,\n type SignInJourneyInput,\n} from \"./sign-in-journey.js\";\nexport { truncate } from \"./truncate.js\";\nexport { injectDocumentMarkup } from \"./html-document.js\";\nexport { withBuilderUtmTrackingParams } from \"./builder-link-tracking.js\";\nexport {\n llmConnectionTrackingProperties,\n normalizeLlmConnection,\n type LlmConnectionStatus,\n} from \"./llm-connection.js\";\nexport {\n DISPATCH_WORKSPACE_ROOT_REDIRECTS,\n RESERVED_WORKSPACE_APP_IDS,\n assertValidWorkspaceAppId,\n getWorkspaceAppIdValidationError,\n isValidWorkspaceAppIdFormat,\n} from \"./workspace-app-id.js\";\nexport {\n DEFAULT_WORKSPACE_APP_AUDIENCE,\n WORKSPACE_APP_AUDIENCES,\n normalizeWorkspaceAppAudience,\n normalizeWorkspaceAppPathList,\n workspaceAppAudienceFromEnv,\n workspaceAppAudienceFromPackageJson,\n workspaceAppRouteAccessFromEnv,\n workspaceAppRouteAccessFromPackageJson,\n type WorkspaceAppRouteAccess,\n type WorkspaceAppRouteAccessFromConfig,\n type WorkspaceAppAudience,\n} from \"./workspace-app-audience.js\";\nexport {\n AGENT_NATIVE_OPEN_PATH,\n AGENT_SIDEBAR_QUERY_PARAM,\n AGENT_SIDEBAR_QUERY_VALUE_CLOSED,\n isAgentNativeOpenDeepLink,\n withCollapsedAgentSidebarParam,\n} from \"./agent-sidebar-url.js\";\nexport {\n AGENT_NATIVE_DEFAULT_SOCIAL_IMAGE,\n AGENT_NATIVE_SOCIAL_IMAGE_CACHE_BUSTER,\n AGENT_NATIVE_SOCIAL_IMAGE_ALT,\n AGENT_NATIVE_SOCIAL_IMAGE_HEIGHT,\n AGENT_NATIVE_SOCIAL_IMAGE_PATH,\n AGENT_NATIVE_SOCIAL_IMAGE_TYPE,\n AGENT_NATIVE_SOCIAL_IMAGE_WIDTH,\n defaultSocialImageMeta,\n withAgentNativeSocialImageCacheBuster,\n withDefaultSocialImage,\n type SocialMetaDescriptor,\n} from \"./social-meta.js\";\nexport {\n EMBED_MODE_QUERY_PARAM,\n EMBED_SESSION_COOKIE,\n EMBED_START_PATH,\n EMBED_TOKEN_QUERY_PARAM,\n} from \"./embed-auth.js\";\nexport {\n AGENT_ACCESS_PARAM,\n DEFAULT_AGENT_ACCESS_TTL_SECONDS,\n appendAgentAccessParam,\n buildAgentAccessApiUrl,\n buildAgentAccessUrl,\n normalizeAgentAccessBasePath,\n normalizeAgentAccessOrigin,\n scopedAgentAccessResourceId,\n toAgentAccessUrl,\n type AgentAccessApiUrlOptions,\n type AgentAccessResourceScope,\n type AgentAccessUrlOptions,\n} from \"./agent-access.js\";\nexport {\n AGENT_READABLE_RESOURCE_PAYLOAD_TYPE,\n AGENT_READABLE_RESOURCE_SCRIPT_TYPE,\n buildAgentReadableResourceDiscovery,\n renderAgentReadableResourceDiscoveryScript,\n safeJsonForHtml,\n type AgentReadableResourceDiscovery,\n type BuildAgentReadableResourceDiscoveryOptions,\n} from \"./agent-readable-resource.js\";\n"]}
@@ -22,6 +22,7 @@ export declare class ForbiddenError extends Error {
22
22
  export interface AccessContext {
23
23
  userEmail?: string;
24
24
  orgId?: string;
25
+ authCapability?: string;
25
26
  }
26
27
  /** Current request's access context. Pulls from request-context ALS. */
27
28
  export declare function currentAccess(): AccessContext;
@@ -1 +1 @@
1
- {"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../../src/sharing/access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAoB,KAAK,GAAG,EAAE,MAAM,aAAa,CAAC;AAMzD,OAAO,EAGL,KAAK,6BAA6B,EACnC,MAAM,eAAe,CAAC;AACvB,OAAO,EAAa,KAAK,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAmBzE,qBAAa,cAAe,SAAQ,KAAK;IACvC,UAAU,SAAO;IACjB,YAAY,OAAO,SAAc,EAGhC;CACF;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wEAAwE;AACxE,wBAAgB,aAAa,IAAI,aAAa,CAK7C;AAED,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,6BAA6B,GAAG,SAAS,EAC9C,GAAG,EAAE,aAAa,GACjB,aAAa,CAEf;AAWD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAC1B,aAAa,EAAE,GAAG,EAClB,WAAW,EAAE,GAAG,EAChB,MAAM,GAAE,aAA+B,EACvC,OAAO,GAAE,SAAoB,EAC7B,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC,GAAG,CA0DL;AAgED,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1B,yCAAyC;IACzC,QAAQ,EAAE,GAAG,CAAC;CACf;AAED;;;;;;GAMG;AACH,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,UAAU,CAAC;CACxB;AAED,MAAM,WAAW,uBAAuB;IACtC,yEAAyE;IACzE,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1B,oEAAoE;IACpE,QAAQ,EAAE,uBAAuB,CAAC;CACnC;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;;;;;;;;;OAiBG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAyID;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE;IAAE,gBAAgB,CAAC,EAAE,KAAK,CAAA;CAAE,GACrC,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;AAClC,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,aAAa,GAAG,SAAS,EACjC,OAAO,EAAE;IAAE,gBAAgB,EAAE,IAAI,CAAA;CAAE,GAClC,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAAC;AA0G3C;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,SAAS,GAAG,OAAO,EAC7B,GAAG,CAAC,EAAE,aAAa,EACnB,OAAO,CAAC,EAAE;IAAE,gBAAgB,CAAC,EAAE,KAAK,CAAA;CAAE,GACrC,OAAO,CAAC,cAAc,CAAC,CAAC;AAC3B,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,SAAS,GAAG,OAAO,GAAG,SAAS,EACxC,GAAG,EAAE,aAAa,GAAG,SAAS,EAC9B,OAAO,EAAE;IAAE,gBAAgB,EAAE,IAAI,CAAA;CAAE,GAClC,OAAO,CAAC,uBAAuB,CAAC,CAAC"}
1
+ {"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../../src/sharing/access.ts"],"names":[],"mappings":"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"}
@@ -13,7 +13,7 @@
13
13
  * callers who lack the required role.
14
14
  */
15
15
  import { and, eq, or, sql } from "drizzle-orm";
16
- import { getRequestUserEmail, getRequestOrgId, } from "../server/request-context.js";
16
+ import { getRequestAuthCapability, getRequestUserEmail, getRequestOrgId, } from "../server/request-context.js";
17
17
  import { listShareableResources, requireShareableResource, } from "./registry.js";
18
18
  import { ROLE_RANK } from "./schema.js";
19
19
  /**
@@ -43,10 +43,16 @@ export function currentAccess() {
43
43
  return {
44
44
  userEmail: getRequestUserEmail(),
45
45
  orgId: getRequestOrgId(),
46
+ authCapability: getRequestAuthCapability(),
46
47
  };
47
48
  }
48
49
  export function resolveRegisteredAccessContext(reg, ctx) {
49
- return reg?.resolveAccessContext ? reg.resolveAccessContext(ctx) : ctx;
50
+ if (!reg?.resolveAccessContext)
51
+ return ctx;
52
+ const resolved = reg.resolveAccessContext(ctx);
53
+ return ctx.authCapability
54
+ ? { ...resolved, authCapability: ctx.authCapability }
55
+ : resolved;
50
56
  }
51
57
  function normalizeEmailForAccess(email) {
52
58
  const normalized = email?.trim().toLowerCase();
@@ -1 +1 @@
1
- {"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/sharing/access.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Access-control helpers for shareable resources.\n *\n * The access model combines:\n * 1. Direct ownership — `owner_email = currentUser`.\n * 2. Visibility — `'private' | 'org' | 'public'`. `org` grants read to anyone\n * in the same org; `public` grants read to any authenticated user.\n * 3. Share rows — per-user or per-org grants in the `{type}_shares` table\n * with a role (`viewer | editor | admin`).\n *\n * Use `applyAccessFilter()` on list/read queries to filter rows the current\n * user can see. Use `assertAccess()` at the top of write actions to reject\n * callers who lack the required role.\n */\n\nimport { and, eq, or, sql, type SQL } from \"drizzle-orm\";\n\nimport {\n getRequestUserEmail,\n getRequestOrgId,\n} from \"../server/request-context.js\";\nimport {\n listShareableResources,\n requireShareableResource,\n type ShareableResourceRegistration,\n} from \"./registry.js\";\nimport { ROLE_RANK, type ShareRole, type Visibility } from \"./schema.js\";\n\n/**\n * Find a registration by its drizzle table identity. Used to look up\n * per-resource policy flags (e.g. `allowPublic`) inside `accessFilter`,\n * which receives only the table — not the resource-type name.\n *\n * Identity is stable within a single bundle (Vite dedupes module instances);\n * the SSR/server side is the only caller, so per-bundle identity is fine.\n */\nfunction findRegistrationByTable(\n resourceTable: any,\n): ShareableResourceRegistration | undefined {\n for (const reg of listShareableResources()) {\n if (reg.resourceTable === resourceTable) return reg;\n }\n return undefined;\n}\n\nexport class ForbiddenError extends Error {\n statusCode = 403;\n constructor(message = \"Forbidden\") {\n super(message);\n this.name = \"ForbiddenError\";\n }\n}\n\nexport interface AccessContext {\n userEmail?: string;\n orgId?: string;\n}\n\n/** Current request's access context. Pulls from request-context ALS. */\nexport function currentAccess(): AccessContext {\n return {\n userEmail: getRequestUserEmail(),\n orgId: getRequestOrgId(),\n };\n}\n\nexport function resolveRegisteredAccessContext(\n reg: ShareableResourceRegistration | undefined,\n ctx: AccessContext,\n): AccessContext {\n return reg?.resolveAccessContext ? reg.resolveAccessContext(ctx) : ctx;\n}\n\nfunction normalizeEmailForAccess(email: string | undefined): string | null {\n const normalized = email?.trim().toLowerCase();\n return normalized || null;\n}\n\nfunction emailColumnMatches(column: any, email: string): SQL {\n return sql`lower(${column}) = ${email}`;\n}\n\n/**\n * Build a Drizzle `WHERE` clause that admits rows the current user can see.\n * Pass the ownable resource table and its shares table; optional min role\n * (defaults to 'viewer') gates which share rows count.\n *\n * `visibility = 'public'` is intentionally NOT admitted by default. Public\n * means \"anyone with the link can view\" (still honoured by `resolveAccess`\n * for read-by-id), not \"appears in every signed-in user's list/sidebar.\"\n * Pass `{ includePublic: true }` for the rare list endpoint that wants\n * cross-user public discovery (a public template gallery, for example).\n *\n * Example:\n *\n * const rows = await db\n * .select()\n * .from(schema.documents)\n * .where(accessFilter(schema.documents, schema.documentShares));\n */\nexport function accessFilter(\n resourceTable: any,\n sharesTable: any,\n rawCtx: AccessContext = currentAccess(),\n minRole: ShareRole = \"viewer\",\n options: { includePublic?: boolean } = {},\n): SQL {\n // Defense in depth — resources registered with `allowPublic: false` must\n // never participate in cross-user \"public\" discovery, even if a caller\n // accidentally passes `includePublic: true` or if a stale public row sits\n // in the DB.\n const reg = findRegistrationByTable(resourceTable);\n const ctx = resolveRegisteredAccessContext(reg, rawCtx);\n const { userEmail, orgId } = ctx;\n const normalizedUserEmail = normalizeEmailForAccess(userEmail);\n const publicAllowed = reg?.allowPublic !== false;\n const includePublic = (options.includePublic ?? false) && publicAllowed;\n const clauses: SQL[] = [];\n\n if (normalizedUserEmail) {\n clauses.push(\n and(\n emailColumnMatches(resourceTable.ownerEmail, normalizedUserEmail),\n ownerScopeFilter(reg, resourceTable, ctx),\n )!,\n );\n }\n if (minRole === \"viewer\") {\n if (includePublic) {\n clauses.push(eq(resourceTable.visibility, \"public\"));\n }\n if (orgId) {\n clauses.push(\n and(\n eq(resourceTable.visibility, \"org\"),\n eq(resourceTable.orgId, orgId),\n )!,\n );\n }\n }\n if (normalizedUserEmail) {\n const shareScope = restrictedShareScopeSql(reg, resourceTable, ctx);\n clauses.push(\n sql`exists (select 1 from ${sharesTable}\n where ${sharesTable.resourceId} = ${resourceTable.id}\n and ${sharesTable.principalType} = 'user'\n and lower(${sharesTable.principalId}) = ${normalizedUserEmail}\n and ${shareScope}\n and ${minRoleSql(minRole)})`,\n );\n }\n if (orgId) {\n const shareScope = restrictedShareScopeSql(reg, resourceTable, ctx);\n clauses.push(\n sql`exists (select 1 from ${sharesTable}\n where ${sharesTable.resourceId} = ${resourceTable.id}\n and ${sharesTable.principalType} = 'org'\n and ${sharesTable.principalId} = ${orgId}\n and ${shareScope}\n and ${minRoleSql(minRole)})`,\n );\n }\n\n return or(...clauses) ?? sql`1=0`;\n}\n\nfunction ownerScopeFilter(\n reg: ShareableResourceRegistration | undefined,\n resourceTable: any,\n ctx: AccessContext,\n): SQL {\n if (reg?.ownerAccessIgnoresOrg === true) return sql`1=1`;\n if (ctx.orgId) {\n // Rows created before org-scoping, or in solo mode, have no org_id. Keep\n // them manageable by their owner after the owner joins or switches into an\n // organization, while still keeping rows from other orgs out of scope.\n return or(\n eq(resourceTable.orgId, ctx.orgId),\n sql`${resourceTable.orgId} IS NULL`,\n )!;\n }\n return sql`${resourceTable.orgId} IS NULL`;\n}\n\nfunction ownerMatchesActiveScope(\n reg: ShareableResourceRegistration | undefined,\n resource: any,\n ctx: AccessContext,\n): boolean {\n if (reg?.ownerAccessIgnoresOrg === true) return true;\n const resourceOrgId = resource?.orgId ?? null;\n if (!resourceOrgId) return true;\n return ctx.orgId === resourceOrgId;\n}\n\nfunction minRoleSql(minRole: ShareRole): SQL {\n if (minRole === \"viewer\") {\n // any role satisfies viewer\n return sql`1=1`;\n }\n if (minRole === \"editor\") {\n return sql`role in ('editor','admin')`;\n }\n return sql`role = 'admin'`;\n}\n\nfunction restrictedShareScopeSql(\n reg: ShareableResourceRegistration | undefined,\n resourceTable: any,\n ctx: AccessContext,\n): SQL {\n // Restricted resources (extensions) must stay inside their resource org even\n // if stale cross-org share rows already exist from older code or bad data.\n if (reg?.requireOrgMemberForUserShares !== true) return sql`1=1`;\n if (!ctx.orgId) return sql`1=0`;\n return eq(resourceTable.orgId, ctx.orgId);\n}\n\nfunction explicitSharesAllowedForResource(\n reg: ShareableResourceRegistration,\n resource: any,\n ctx: AccessContext,\n): boolean {\n if (reg.requireOrgMemberForUserShares !== true) return true;\n const resourceOrgId = resource?.orgId ?? null;\n return !!resourceOrgId && !!ctx.orgId && resourceOrgId === ctx.orgId;\n}\n\nexport interface ResolvedAccess {\n /** Effective role: 'owner' for the resource owner, or the share role. */\n role: \"owner\" | ShareRole;\n /** The resource row (already loaded). */\n resource: any;\n}\n\n/**\n * Minimal resource shape returned when a caller opts into a projected access\n * load via `{ skipResourceBody: true }`. Contains exactly the columns the\n * access-decision logic itself reads — identity, ownership, org scope, and\n * visibility — never a resource type's heavy body columns (`data`,\n * `content`, and similar blobs).\n */\nexport interface AccessProjectedResource {\n id: string;\n ownerEmail: string;\n orgId: string | null;\n visibility: Visibility;\n}\n\nexport interface ResolvedAccessProjected {\n /** Effective role: 'owner' for the resource owner, or the share role. */\n role: \"owner\" | ShareRole;\n /** Only the access-decision columns — not the full resource row. */\n resource: AccessProjectedResource;\n}\n\nexport interface ResolveAccessOptions {\n /**\n * When true, load only the columns the access decision itself needs\n * (`id`, `ownerEmail`, `orgId`, `visibility`) instead of the full resource\n * row. Use this when the caller only needs the access decision — not the\n * resource body — and wants to skip fetching heavy type-specific columns\n * (e.g. `data`/`content` blobs) on every `assertAccess`/`resolveAccess`\n * call.\n *\n * Silently ignored (falls back to a full row load) for any resource type\n * registered with a `publicAccessRole` *function* resolver, since that\n * callback can read arbitrary resource fields the projection would have\n * omitted — see `hasDynamicPublicAccessRoleResolver` below.\n *\n * Default: `false` — the full row is loaded, matching historical behavior.\n * Callers that need resource body fields (most call sites today — for\n * many resource types `resolveAccess().resource` doubles as the action's\n * primary data read) must NOT pass this.\n */\n skipResourceBody?: boolean;\n}\n\nasync function publicAccessRoleForResource(\n reg: ShareableResourceRegistration,\n resource: any,\n ctx: AccessContext,\n): Promise<ShareRole> {\n const roleResolver = reg.publicAccessRole;\n if (!roleResolver) return \"viewer\";\n return typeof roleResolver === \"function\"\n ? await roleResolver(resource, ctx)\n : roleResolver;\n}\n\nfunction higherShareRole(a: ShareRole, b: ShareRole | null): ShareRole {\n if (!b) return a;\n return ROLE_RANK[b] > ROLE_RANK[a] ? b : a;\n}\n\nfunction columnName(column: unknown): string | null {\n const candidate = column as\n | {\n name?: unknown;\n config?: { name?: unknown };\n _: { name?: unknown };\n }\n | undefined;\n const name = candidate?.name ?? candidate?.config?.name ?? candidate?._?.name;\n return typeof name === \"string\" && name ? name : null;\n}\n\nfunction missingColumnName(err: unknown): string | null {\n const error = err as { code?: string; message?: string } | undefined;\n const message = error?.message ?? \"\";\n if (\n error?.code !== \"42703\" &&\n !/no such column|does not exist/i.test(message)\n ) {\n return null;\n }\n const quoted = message.match(/column\\s+\"([^\"]+)\"\\s+does not exist/i)?.[1];\n if (quoted) return quoted;\n const sqlite = message.match(/no such column:\\s+[\"`]?([\\w.]+)[\"`]?/i)?.[1];\n if (sqlite) return sqlite.split(\".\").pop() ?? sqlite;\n return null;\n}\n\nfunction selectExistingColumns(\n columns: Record<string, unknown>,\n omittedColumnNames: Set<string>,\n): Record<string, unknown> {\n const selection: Record<string, unknown> = {};\n for (const [key, column] of Object.entries(columns)) {\n const name = columnName(column);\n if (!name || omittedColumnNames.has(name)) continue;\n selection[key] = column;\n }\n return selection;\n}\n\n/**\n * The fixed column set the access-decision logic in `resolveAccess` itself\n * reads: identity (`id`), ownership (`ownerEmail`, `orgId`), and the coarse\n * `visibility` flag. This is intentionally NOT caller-configurable — an\n * arbitrary caller-supplied column list could omit a column the access\n * checks below depend on and silently break authorization. Every ownable\n * resource table has these columns (`ownableColumns()` + a primary key\n * named `id`), so this projection is safe for any registration that\n * doesn't also read the row through a dynamic resolver (see below).\n */\nfunction projectedAccessColumns(resourceTable: any): Record<string, unknown> {\n return {\n id: resourceTable.id,\n ownerEmail: resourceTable.ownerEmail,\n orgId: resourceTable.orgId,\n visibility: resourceTable.visibility,\n };\n}\n\n/**\n * True when this registration's `publicAccessRole` is a callback rather than\n * a fixed role string. Callbacks receive the loaded `resource` and may read\n * arbitrary fields on it (see e.g. `templates/design`'s\n * `publicDesignAccessRole`, which inspects design-specific data). The\n * projected access load must never be used for these registrations — always\n * fall back to a full row load so the resolver sees the complete resource.\n */\nfunction hasDynamicPublicAccessRoleResolver(\n reg: ShareableResourceRegistration,\n): boolean {\n return typeof reg.publicAccessRole === \"function\";\n}\n\nasync function loadResourceForAccess(\n reg: ShareableResourceRegistration,\n resourceId: string,\n options: ResolveAccessOptions = {},\n): Promise<any | null> {\n const db = reg.getDb() as any;\n const useProjection =\n options.skipResourceBody === true &&\n !hasDynamicPublicAccessRoleResolver(reg);\n const projectedColumns = useProjection\n ? projectedAccessColumns(reg.resourceTable)\n : null;\n const omittedColumnNames = new Set<string>();\n\n for (let attempt = 0; attempt < 12; attempt++) {\n try {\n const query =\n !projectedColumns && omittedColumnNames.size === 0\n ? db.select()\n : db.select(\n selectExistingColumns(\n projectedColumns ?? reg.resourceTable,\n omittedColumnNames,\n ),\n );\n const [resource] = await query\n .from(reg.resourceTable)\n .where(eq(reg.resourceTable.id, resourceId));\n return resource ?? null;\n } catch (err) {\n const missing = missingColumnName(err);\n if (!missing || omittedColumnNames.has(missing)) throw err;\n omittedColumnNames.add(missing);\n console.warn(\n `[sharing] ${reg.type} access lookup omitted missing column ${missing}`,\n );\n }\n }\n\n throw new Error(\n `Could not load ${reg.type} ${resourceId}: too many missing resource columns`,\n );\n}\n\n/**\n * Return the effective role the current user has on a specific resource, or\n * null if they have no access. Loads the resource and relevant share rows.\n *\n * By default the full resource row is loaded (unchanged historical\n * behavior — for most resource types `.resource` here doubles as the\n * action's primary data read). Pass `{ skipResourceBody: true }` when the\n * caller only needs the access decision to load just the access-decision\n * columns instead — see `ResolveAccessOptions`.\n */\nexport async function resolveAccess(\n resourceType: string,\n resourceId: string,\n rawCtx?: AccessContext,\n options?: { skipResourceBody?: false },\n): Promise<ResolvedAccess | null>;\nexport async function resolveAccess(\n resourceType: string,\n resourceId: string,\n rawCtx: AccessContext | undefined,\n options: { skipResourceBody: true },\n): Promise<ResolvedAccessProjected | null>;\nexport async function resolveAccess(\n resourceType: string,\n resourceId: string,\n rawCtx: AccessContext = currentAccess(),\n options: ResolveAccessOptions = {},\n): Promise<ResolvedAccess | ResolvedAccessProjected | null> {\n return resolveAccessImpl(resourceType, resourceId, rawCtx, options);\n}\n\n/**\n * Un-overloaded implementation shared by the `resolveAccess` overloads and\n * called directly by `assertAccess` below. Kept separate from the exported\n * `resolveAccess` so internal callers passing a widened\n * `ResolveAccessOptions` (rather than a `{ skipResourceBody: true }` /\n * `{ skipResourceBody?: false }` literal) don't have to satisfy TypeScript's\n * overload resolution, which only matches against the declared overload\n * signatures, not the implementation signature.\n */\nasync function resolveAccessImpl(\n resourceType: string,\n resourceId: string,\n rawCtx: AccessContext = currentAccess(),\n options: ResolveAccessOptions = {},\n): Promise<ResolvedAccess | ResolvedAccessProjected | null> {\n const reg = requireShareableResource(resourceType);\n const ctx = resolveRegisteredAccessContext(reg, rawCtx);\n\n const resource = await loadResourceForAccess(reg, resourceId, options);\n if (!resource) return null;\n\n const { userEmail, orgId } = ctx;\n const normalizedUserEmail = normalizeEmailForAccess(userEmail);\n\n if (\n normalizedUserEmail &&\n normalizeEmailForAccess(resource.ownerEmail) === normalizedUserEmail &&\n ownerMatchesActiveScope(reg, resource, ctx)\n ) {\n return { role: \"owner\", resource };\n }\n if (resource.visibility === \"public\" && reg.allowPublic !== false) {\n // No share row needed; default viewer unless the resource registration\n // deliberately grants a stronger public-by-link role or explicit shares\n // upgrade the current principal.\n const publicRole = await publicAccessRoleForResource(reg, resource, ctx);\n const role = await highestShareRole(reg, resourceId, ctx, resource);\n return { role: higherShareRole(publicRole, role), resource };\n }\n // `visibility === \"public\"` on an `allowPublic: false` resource is treated\n // as private: only owner + explicit shares grant access. Falls through to\n // the explicit-share lookup below.\n if (resource.visibility === \"org\" && orgId && resource.orgId === orgId) {\n const role = await highestShareRole(reg, resourceId, ctx, resource);\n return { role: role ?? \"viewer\", resource };\n }\n const role = await highestShareRole(reg, resourceId, ctx, resource);\n if (role) return { role, resource };\n return null;\n}\n\nasync function highestShareRole(\n reg: ShareableResourceRegistration,\n resourceId: string,\n ctx: AccessContext,\n resource: any,\n): Promise<ShareRole | null> {\n const { userEmail, orgId } = ctx;\n const normalizedUserEmail = normalizeEmailForAccess(userEmail);\n if (!normalizedUserEmail && !orgId) return null;\n if (!explicitSharesAllowedForResource(reg, resource, ctx)) return null;\n const db = reg.getDb() as any;\n\n const principalClauses: ReturnType<typeof and>[] = [];\n if (normalizedUserEmail) {\n principalClauses.push(\n and(\n eq(reg.sharesTable.principalType, \"user\"),\n emailColumnMatches(reg.sharesTable.principalId, normalizedUserEmail),\n ),\n );\n }\n if (orgId) {\n principalClauses.push(\n and(\n eq(reg.sharesTable.principalType, \"org\"),\n eq(reg.sharesTable.principalId, orgId),\n ),\n );\n }\n\n const rows = await db\n .select({ role: reg.sharesTable.role })\n .from(reg.sharesTable)\n .where(\n and(eq(reg.sharesTable.resourceId, resourceId), or(...principalClauses)),\n )\n .limit(10);\n\n let best: ShareRole | null = null;\n for (const r of rows as Array<{ role: ShareRole }>) {\n if (!best || ROLE_RANK[r.role] > ROLE_RANK[best]) best = r.role;\n }\n return best;\n}\n\n/**\n * Throw ForbiddenError if the current user can't act on this resource with at\n * least the given role. Used at the top of update/delete actions.\n *\n * By default the full resource row is loaded (unchanged historical\n * behavior). Pass `{ skipResourceBody: true }` as the fifth argument when\n * the caller only needs the access decision — see `ResolveAccessOptions`.\n */\nexport async function assertAccess(\n resourceType: string,\n resourceId: string,\n minRole?: ShareRole | \"owner\",\n ctx?: AccessContext,\n options?: { skipResourceBody?: false },\n): Promise<ResolvedAccess>;\nexport async function assertAccess(\n resourceType: string,\n resourceId: string,\n minRole: ShareRole | \"owner\" | undefined,\n ctx: AccessContext | undefined,\n options: { skipResourceBody: true },\n): Promise<ResolvedAccessProjected>;\nexport async function assertAccess(\n resourceType: string,\n resourceId: string,\n minRole: ShareRole | \"owner\" = \"viewer\",\n ctx: AccessContext = currentAccess(),\n options: ResolveAccessOptions = {},\n): Promise<ResolvedAccess | ResolvedAccessProjected> {\n const access = await resolveAccessImpl(\n resourceType,\n resourceId,\n ctx,\n options,\n );\n if (!access) {\n throw new ForbiddenError(`No access to ${resourceType} ${resourceId}`);\n }\n if (ROLE_RANK[access.role] < ROLE_RANK[minRole]) {\n throw new ForbiddenError(\n `Requires ${minRole} role on ${resourceType} ${resourceId} (have ${access.role})`,\n );\n }\n return access;\n}\n"]}
1
+ {"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/sharing/access.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Access-control helpers for shareable resources.\n *\n * The access model combines:\n * 1. Direct ownership — `owner_email = currentUser`.\n * 2. Visibility — `'private' | 'org' | 'public'`. `org` grants read to anyone\n * in the same org; `public` grants read to any authenticated user.\n * 3. Share rows — per-user or per-org grants in the `{type}_shares` table\n * with a role (`viewer | editor | admin`).\n *\n * Use `applyAccessFilter()` on list/read queries to filter rows the current\n * user can see. Use `assertAccess()` at the top of write actions to reject\n * callers who lack the required role.\n */\n\nimport { and, eq, or, sql, type SQL } from \"drizzle-orm\";\n\nimport {\n getRequestAuthCapability,\n getRequestUserEmail,\n getRequestOrgId,\n} from \"../server/request-context.js\";\nimport {\n listShareableResources,\n requireShareableResource,\n type ShareableResourceRegistration,\n} from \"./registry.js\";\nimport { ROLE_RANK, type ShareRole, type Visibility } from \"./schema.js\";\n\n/**\n * Find a registration by its drizzle table identity. Used to look up\n * per-resource policy flags (e.g. `allowPublic`) inside `accessFilter`,\n * which receives only the table — not the resource-type name.\n *\n * Identity is stable within a single bundle (Vite dedupes module instances);\n * the SSR/server side is the only caller, so per-bundle identity is fine.\n */\nfunction findRegistrationByTable(\n resourceTable: any,\n): ShareableResourceRegistration | undefined {\n for (const reg of listShareableResources()) {\n if (reg.resourceTable === resourceTable) return reg;\n }\n return undefined;\n}\n\nexport class ForbiddenError extends Error {\n statusCode = 403;\n constructor(message = \"Forbidden\") {\n super(message);\n this.name = \"ForbiddenError\";\n }\n}\n\nexport interface AccessContext {\n userEmail?: string;\n orgId?: string;\n authCapability?: string;\n}\n\n/** Current request's access context. Pulls from request-context ALS. */\nexport function currentAccess(): AccessContext {\n return {\n userEmail: getRequestUserEmail(),\n orgId: getRequestOrgId(),\n authCapability: getRequestAuthCapability(),\n };\n}\n\nexport function resolveRegisteredAccessContext(\n reg: ShareableResourceRegistration | undefined,\n ctx: AccessContext,\n): AccessContext {\n if (!reg?.resolveAccessContext) return ctx;\n const resolved = reg.resolveAccessContext(ctx);\n return ctx.authCapability\n ? { ...resolved, authCapability: ctx.authCapability }\n : resolved;\n}\n\nfunction normalizeEmailForAccess(email: string | undefined): string | null {\n const normalized = email?.trim().toLowerCase();\n return normalized || null;\n}\n\nfunction emailColumnMatches(column: any, email: string): SQL {\n return sql`lower(${column}) = ${email}`;\n}\n\n/**\n * Build a Drizzle `WHERE` clause that admits rows the current user can see.\n * Pass the ownable resource table and its shares table; optional min role\n * (defaults to 'viewer') gates which share rows count.\n *\n * `visibility = 'public'` is intentionally NOT admitted by default. Public\n * means \"anyone with the link can view\" (still honoured by `resolveAccess`\n * for read-by-id), not \"appears in every signed-in user's list/sidebar.\"\n * Pass `{ includePublic: true }` for the rare list endpoint that wants\n * cross-user public discovery (a public template gallery, for example).\n *\n * Example:\n *\n * const rows = await db\n * .select()\n * .from(schema.documents)\n * .where(accessFilter(schema.documents, schema.documentShares));\n */\nexport function accessFilter(\n resourceTable: any,\n sharesTable: any,\n rawCtx: AccessContext = currentAccess(),\n minRole: ShareRole = \"viewer\",\n options: { includePublic?: boolean } = {},\n): SQL {\n // Defense in depth — resources registered with `allowPublic: false` must\n // never participate in cross-user \"public\" discovery, even if a caller\n // accidentally passes `includePublic: true` or if a stale public row sits\n // in the DB.\n const reg = findRegistrationByTable(resourceTable);\n const ctx = resolveRegisteredAccessContext(reg, rawCtx);\n const { userEmail, orgId } = ctx;\n const normalizedUserEmail = normalizeEmailForAccess(userEmail);\n const publicAllowed = reg?.allowPublic !== false;\n const includePublic = (options.includePublic ?? false) && publicAllowed;\n const clauses: SQL[] = [];\n\n if (normalizedUserEmail) {\n clauses.push(\n and(\n emailColumnMatches(resourceTable.ownerEmail, normalizedUserEmail),\n ownerScopeFilter(reg, resourceTable, ctx),\n )!,\n );\n }\n if (minRole === \"viewer\") {\n if (includePublic) {\n clauses.push(eq(resourceTable.visibility, \"public\"));\n }\n if (orgId) {\n clauses.push(\n and(\n eq(resourceTable.visibility, \"org\"),\n eq(resourceTable.orgId, orgId),\n )!,\n );\n }\n }\n if (normalizedUserEmail) {\n const shareScope = restrictedShareScopeSql(reg, resourceTable, ctx);\n clauses.push(\n sql`exists (select 1 from ${sharesTable}\n where ${sharesTable.resourceId} = ${resourceTable.id}\n and ${sharesTable.principalType} = 'user'\n and lower(${sharesTable.principalId}) = ${normalizedUserEmail}\n and ${shareScope}\n and ${minRoleSql(minRole)})`,\n );\n }\n if (orgId) {\n const shareScope = restrictedShareScopeSql(reg, resourceTable, ctx);\n clauses.push(\n sql`exists (select 1 from ${sharesTable}\n where ${sharesTable.resourceId} = ${resourceTable.id}\n and ${sharesTable.principalType} = 'org'\n and ${sharesTable.principalId} = ${orgId}\n and ${shareScope}\n and ${minRoleSql(minRole)})`,\n );\n }\n\n return or(...clauses) ?? sql`1=0`;\n}\n\nfunction ownerScopeFilter(\n reg: ShareableResourceRegistration | undefined,\n resourceTable: any,\n ctx: AccessContext,\n): SQL {\n if (reg?.ownerAccessIgnoresOrg === true) return sql`1=1`;\n if (ctx.orgId) {\n // Rows created before org-scoping, or in solo mode, have no org_id. Keep\n // them manageable by their owner after the owner joins or switches into an\n // organization, while still keeping rows from other orgs out of scope.\n return or(\n eq(resourceTable.orgId, ctx.orgId),\n sql`${resourceTable.orgId} IS NULL`,\n )!;\n }\n return sql`${resourceTable.orgId} IS NULL`;\n}\n\nfunction ownerMatchesActiveScope(\n reg: ShareableResourceRegistration | undefined,\n resource: any,\n ctx: AccessContext,\n): boolean {\n if (reg?.ownerAccessIgnoresOrg === true) return true;\n const resourceOrgId = resource?.orgId ?? null;\n if (!resourceOrgId) return true;\n return ctx.orgId === resourceOrgId;\n}\n\nfunction minRoleSql(minRole: ShareRole): SQL {\n if (minRole === \"viewer\") {\n // any role satisfies viewer\n return sql`1=1`;\n }\n if (minRole === \"editor\") {\n return sql`role in ('editor','admin')`;\n }\n return sql`role = 'admin'`;\n}\n\nfunction restrictedShareScopeSql(\n reg: ShareableResourceRegistration | undefined,\n resourceTable: any,\n ctx: AccessContext,\n): SQL {\n // Restricted resources (extensions) must stay inside their resource org even\n // if stale cross-org share rows already exist from older code or bad data.\n if (reg?.requireOrgMemberForUserShares !== true) return sql`1=1`;\n if (!ctx.orgId) return sql`1=0`;\n return eq(resourceTable.orgId, ctx.orgId);\n}\n\nfunction explicitSharesAllowedForResource(\n reg: ShareableResourceRegistration,\n resource: any,\n ctx: AccessContext,\n): boolean {\n if (reg.requireOrgMemberForUserShares !== true) return true;\n const resourceOrgId = resource?.orgId ?? null;\n return !!resourceOrgId && !!ctx.orgId && resourceOrgId === ctx.orgId;\n}\n\nexport interface ResolvedAccess {\n /** Effective role: 'owner' for the resource owner, or the share role. */\n role: \"owner\" | ShareRole;\n /** The resource row (already loaded). */\n resource: any;\n}\n\n/**\n * Minimal resource shape returned when a caller opts into a projected access\n * load via `{ skipResourceBody: true }`. Contains exactly the columns the\n * access-decision logic itself reads — identity, ownership, org scope, and\n * visibility — never a resource type's heavy body columns (`data`,\n * `content`, and similar blobs).\n */\nexport interface AccessProjectedResource {\n id: string;\n ownerEmail: string;\n orgId: string | null;\n visibility: Visibility;\n}\n\nexport interface ResolvedAccessProjected {\n /** Effective role: 'owner' for the resource owner, or the share role. */\n role: \"owner\" | ShareRole;\n /** Only the access-decision columns — not the full resource row. */\n resource: AccessProjectedResource;\n}\n\nexport interface ResolveAccessOptions {\n /**\n * When true, load only the columns the access decision itself needs\n * (`id`, `ownerEmail`, `orgId`, `visibility`) instead of the full resource\n * row. Use this when the caller only needs the access decision — not the\n * resource body — and wants to skip fetching heavy type-specific columns\n * (e.g. `data`/`content` blobs) on every `assertAccess`/`resolveAccess`\n * call.\n *\n * Silently ignored (falls back to a full row load) for any resource type\n * registered with a `publicAccessRole` *function* resolver, since that\n * callback can read arbitrary resource fields the projection would have\n * omitted — see `hasDynamicPublicAccessRoleResolver` below.\n *\n * Default: `false` — the full row is loaded, matching historical behavior.\n * Callers that need resource body fields (most call sites today — for\n * many resource types `resolveAccess().resource` doubles as the action's\n * primary data read) must NOT pass this.\n */\n skipResourceBody?: boolean;\n}\n\nasync function publicAccessRoleForResource(\n reg: ShareableResourceRegistration,\n resource: any,\n ctx: AccessContext,\n): Promise<ShareRole> {\n const roleResolver = reg.publicAccessRole;\n if (!roleResolver) return \"viewer\";\n return typeof roleResolver === \"function\"\n ? await roleResolver(resource, ctx)\n : roleResolver;\n}\n\nfunction higherShareRole(a: ShareRole, b: ShareRole | null): ShareRole {\n if (!b) return a;\n return ROLE_RANK[b] > ROLE_RANK[a] ? b : a;\n}\n\nfunction columnName(column: unknown): string | null {\n const candidate = column as\n | {\n name?: unknown;\n config?: { name?: unknown };\n _: { name?: unknown };\n }\n | undefined;\n const name = candidate?.name ?? candidate?.config?.name ?? candidate?._?.name;\n return typeof name === \"string\" && name ? name : null;\n}\n\nfunction missingColumnName(err: unknown): string | null {\n const error = err as { code?: string; message?: string } | undefined;\n const message = error?.message ?? \"\";\n if (\n error?.code !== \"42703\" &&\n !/no such column|does not exist/i.test(message)\n ) {\n return null;\n }\n const quoted = message.match(/column\\s+\"([^\"]+)\"\\s+does not exist/i)?.[1];\n if (quoted) return quoted;\n const sqlite = message.match(/no such column:\\s+[\"`]?([\\w.]+)[\"`]?/i)?.[1];\n if (sqlite) return sqlite.split(\".\").pop() ?? sqlite;\n return null;\n}\n\nfunction selectExistingColumns(\n columns: Record<string, unknown>,\n omittedColumnNames: Set<string>,\n): Record<string, unknown> {\n const selection: Record<string, unknown> = {};\n for (const [key, column] of Object.entries(columns)) {\n const name = columnName(column);\n if (!name || omittedColumnNames.has(name)) continue;\n selection[key] = column;\n }\n return selection;\n}\n\n/**\n * The fixed column set the access-decision logic in `resolveAccess` itself\n * reads: identity (`id`), ownership (`ownerEmail`, `orgId`), and the coarse\n * `visibility` flag. This is intentionally NOT caller-configurable — an\n * arbitrary caller-supplied column list could omit a column the access\n * checks below depend on and silently break authorization. Every ownable\n * resource table has these columns (`ownableColumns()` + a primary key\n * named `id`), so this projection is safe for any registration that\n * doesn't also read the row through a dynamic resolver (see below).\n */\nfunction projectedAccessColumns(resourceTable: any): Record<string, unknown> {\n return {\n id: resourceTable.id,\n ownerEmail: resourceTable.ownerEmail,\n orgId: resourceTable.orgId,\n visibility: resourceTable.visibility,\n };\n}\n\n/**\n * True when this registration's `publicAccessRole` is a callback rather than\n * a fixed role string. Callbacks receive the loaded `resource` and may read\n * arbitrary fields on it (see e.g. `templates/design`'s\n * `publicDesignAccessRole`, which inspects design-specific data). The\n * projected access load must never be used for these registrations — always\n * fall back to a full row load so the resolver sees the complete resource.\n */\nfunction hasDynamicPublicAccessRoleResolver(\n reg: ShareableResourceRegistration,\n): boolean {\n return typeof reg.publicAccessRole === \"function\";\n}\n\nasync function loadResourceForAccess(\n reg: ShareableResourceRegistration,\n resourceId: string,\n options: ResolveAccessOptions = {},\n): Promise<any | null> {\n const db = reg.getDb() as any;\n const useProjection =\n options.skipResourceBody === true &&\n !hasDynamicPublicAccessRoleResolver(reg);\n const projectedColumns = useProjection\n ? projectedAccessColumns(reg.resourceTable)\n : null;\n const omittedColumnNames = new Set<string>();\n\n for (let attempt = 0; attempt < 12; attempt++) {\n try {\n const query =\n !projectedColumns && omittedColumnNames.size === 0\n ? db.select()\n : db.select(\n selectExistingColumns(\n projectedColumns ?? reg.resourceTable,\n omittedColumnNames,\n ),\n );\n const [resource] = await query\n .from(reg.resourceTable)\n .where(eq(reg.resourceTable.id, resourceId));\n return resource ?? null;\n } catch (err) {\n const missing = missingColumnName(err);\n if (!missing || omittedColumnNames.has(missing)) throw err;\n omittedColumnNames.add(missing);\n console.warn(\n `[sharing] ${reg.type} access lookup omitted missing column ${missing}`,\n );\n }\n }\n\n throw new Error(\n `Could not load ${reg.type} ${resourceId}: too many missing resource columns`,\n );\n}\n\n/**\n * Return the effective role the current user has on a specific resource, or\n * null if they have no access. Loads the resource and relevant share rows.\n *\n * By default the full resource row is loaded (unchanged historical\n * behavior — for most resource types `.resource` here doubles as the\n * action's primary data read). Pass `{ skipResourceBody: true }` when the\n * caller only needs the access decision to load just the access-decision\n * columns instead — see `ResolveAccessOptions`.\n */\nexport async function resolveAccess(\n resourceType: string,\n resourceId: string,\n rawCtx?: AccessContext,\n options?: { skipResourceBody?: false },\n): Promise<ResolvedAccess | null>;\nexport async function resolveAccess(\n resourceType: string,\n resourceId: string,\n rawCtx: AccessContext | undefined,\n options: { skipResourceBody: true },\n): Promise<ResolvedAccessProjected | null>;\nexport async function resolveAccess(\n resourceType: string,\n resourceId: string,\n rawCtx: AccessContext = currentAccess(),\n options: ResolveAccessOptions = {},\n): Promise<ResolvedAccess | ResolvedAccessProjected | null> {\n return resolveAccessImpl(resourceType, resourceId, rawCtx, options);\n}\n\n/**\n * Un-overloaded implementation shared by the `resolveAccess` overloads and\n * called directly by `assertAccess` below. Kept separate from the exported\n * `resolveAccess` so internal callers passing a widened\n * `ResolveAccessOptions` (rather than a `{ skipResourceBody: true }` /\n * `{ skipResourceBody?: false }` literal) don't have to satisfy TypeScript's\n * overload resolution, which only matches against the declared overload\n * signatures, not the implementation signature.\n */\nasync function resolveAccessImpl(\n resourceType: string,\n resourceId: string,\n rawCtx: AccessContext = currentAccess(),\n options: ResolveAccessOptions = {},\n): Promise<ResolvedAccess | ResolvedAccessProjected | null> {\n const reg = requireShareableResource(resourceType);\n const ctx = resolveRegisteredAccessContext(reg, rawCtx);\n\n const resource = await loadResourceForAccess(reg, resourceId, options);\n if (!resource) return null;\n\n const { userEmail, orgId } = ctx;\n const normalizedUserEmail = normalizeEmailForAccess(userEmail);\n\n if (\n normalizedUserEmail &&\n normalizeEmailForAccess(resource.ownerEmail) === normalizedUserEmail &&\n ownerMatchesActiveScope(reg, resource, ctx)\n ) {\n return { role: \"owner\", resource };\n }\n if (resource.visibility === \"public\" && reg.allowPublic !== false) {\n // No share row needed; default viewer unless the resource registration\n // deliberately grants a stronger public-by-link role or explicit shares\n // upgrade the current principal.\n const publicRole = await publicAccessRoleForResource(reg, resource, ctx);\n const role = await highestShareRole(reg, resourceId, ctx, resource);\n return { role: higherShareRole(publicRole, role), resource };\n }\n // `visibility === \"public\"` on an `allowPublic: false` resource is treated\n // as private: only owner + explicit shares grant access. Falls through to\n // the explicit-share lookup below.\n if (resource.visibility === \"org\" && orgId && resource.orgId === orgId) {\n const role = await highestShareRole(reg, resourceId, ctx, resource);\n return { role: role ?? \"viewer\", resource };\n }\n const role = await highestShareRole(reg, resourceId, ctx, resource);\n if (role) return { role, resource };\n return null;\n}\n\nasync function highestShareRole(\n reg: ShareableResourceRegistration,\n resourceId: string,\n ctx: AccessContext,\n resource: any,\n): Promise<ShareRole | null> {\n const { userEmail, orgId } = ctx;\n const normalizedUserEmail = normalizeEmailForAccess(userEmail);\n if (!normalizedUserEmail && !orgId) return null;\n if (!explicitSharesAllowedForResource(reg, resource, ctx)) return null;\n const db = reg.getDb() as any;\n\n const principalClauses: ReturnType<typeof and>[] = [];\n if (normalizedUserEmail) {\n principalClauses.push(\n and(\n eq(reg.sharesTable.principalType, \"user\"),\n emailColumnMatches(reg.sharesTable.principalId, normalizedUserEmail),\n ),\n );\n }\n if (orgId) {\n principalClauses.push(\n and(\n eq(reg.sharesTable.principalType, \"org\"),\n eq(reg.sharesTable.principalId, orgId),\n ),\n );\n }\n\n const rows = await db\n .select({ role: reg.sharesTable.role })\n .from(reg.sharesTable)\n .where(\n and(eq(reg.sharesTable.resourceId, resourceId), or(...principalClauses)),\n )\n .limit(10);\n\n let best: ShareRole | null = null;\n for (const r of rows as Array<{ role: ShareRole }>) {\n if (!best || ROLE_RANK[r.role] > ROLE_RANK[best]) best = r.role;\n }\n return best;\n}\n\n/**\n * Throw ForbiddenError if the current user can't act on this resource with at\n * least the given role. Used at the top of update/delete actions.\n *\n * By default the full resource row is loaded (unchanged historical\n * behavior). Pass `{ skipResourceBody: true }` as the fifth argument when\n * the caller only needs the access decision — see `ResolveAccessOptions`.\n */\nexport async function assertAccess(\n resourceType: string,\n resourceId: string,\n minRole?: ShareRole | \"owner\",\n ctx?: AccessContext,\n options?: { skipResourceBody?: false },\n): Promise<ResolvedAccess>;\nexport async function assertAccess(\n resourceType: string,\n resourceId: string,\n minRole: ShareRole | \"owner\" | undefined,\n ctx: AccessContext | undefined,\n options: { skipResourceBody: true },\n): Promise<ResolvedAccessProjected>;\nexport async function assertAccess(\n resourceType: string,\n resourceId: string,\n minRole: ShareRole | \"owner\" = \"viewer\",\n ctx: AccessContext = currentAccess(),\n options: ResolveAccessOptions = {},\n): Promise<ResolvedAccess | ResolvedAccessProjected> {\n const access = await resolveAccessImpl(\n resourceType,\n resourceId,\n ctx,\n options,\n );\n if (!access) {\n throw new ForbiddenError(`No access to ${resourceType} ${resourceId}`);\n }\n if (ROLE_RANK[access.role] < ROLE_RANK[minRole]) {\n throw new ForbiddenError(\n `Requires ${minRole} role on ${resourceType} ${resourceId} (have ${access.role})`,\n );\n }\n return access;\n}\n"]}
@@ -117,6 +117,7 @@ export interface ShareableResourceRegistration {
117
117
  publicAccessRole?: "viewer" | "editor" | "admin" | ((resource: any, ctx: {
118
118
  userEmail?: string;
119
119
  orgId?: string;
120
+ authCapability?: string;
120
121
  }) => "viewer" | "editor" | "admin" | Promise<"viewer" | "editor" | "admin">);
121
122
  /**
122
123
  * When `true`, individual user shares (`principalType: "user"`) must target
@@ -139,9 +140,11 @@ export interface ShareableResourceRegistration {
139
140
  resolveAccessContext?: (ctx: {
140
141
  userEmail?: string;
141
142
  orgId?: string;
143
+ authCapability?: string;
142
144
  }) => {
143
145
  userEmail?: string;
144
146
  orgId?: string;
147
+ authCapability?: string;
145
148
  };
146
149
  /**
147
150
  * When true, direct ownership is recognized by owner_email regardless of the
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/sharing/registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/sharing/registry.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/sharing/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAuKH,0EAA0E;AAC1E,6EAA6E;AAC7E,8EAA8E;AAC9E,4EAA4E;AAC5E,2EAA2E;AAC3E,0EAA0E;AAC1E,8EAA8E;AAC9E,+EAA+E;AAC/E,MAAM,YAAY,GAAG,mCAAmC,CAAC;AAEzD,MAAM,cAAc,GAClB,UAAiB,CAAC;AAEpB,SAAS,aAAa;IACpB,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM;QAC/B,OAAO,CAAC,GAAG,CAAC,MAAM,KAAK,MAAM;QAC7B,OAAO,CAAC,GAAG,CAAC,MAAM,KAAK,GAAG,CAC3B,CAAC;AACJ,CAAC;AAED,SAAS,4BAA4B;IACnC,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;IACtC,OAAO,oEAAoE,CAAC,IAAI,CAC9E,KAAK,CACN,CAAC;AACJ,CAAC;AAED,SAAS,WAAW;IAClB,IAAI,CAAC,GAAG,cAAc,CAAC,YAAY,CAAC,CAAC;IACrC,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,CAAC,GAAG,IAAI,GAAG,EAAyC,CAAC;QACrD,cAAc,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,KAAoC;IAEpC,IAAI,CAAC,aAAa,EAAE,IAAI,4BAA4B,EAAE;QAAE,OAAO;IAC/D,WAAW,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,IAAY;IAEZ,OAAO,WAAW,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,IAAY;IAEZ,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,qCAAqC,IAAI,gDAAgD,CAC1F,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,OAAO,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;AAC5C,CAAC","sourcesContent":["/**\n * Registry of shareable resources.\n *\n * Each template registers its ownable resource(s) once on module load so the\n * framework-level share actions (`share-resource`, `list-resource-shares`,\n * etc.) can dispatch to the correct tables.\n *\n * import { registerShareableResource } from \"@agent-native/core/sharing\";\n * import * as schema from \"./schema.js\";\n *\n * registerShareableResource({\n * type: \"document\",\n * resourceTable: schema.documents,\n * sharesTable: schema.documentShares,\n * displayName: \"Document\",\n * titleColumn: \"title\",\n * });\n */\n\nimport type { UserProfile } from \"../user-profile/shared.js\";\n\nexport interface ShareableResourceRegistration {\n /** Stable identifier used across actions, UI, and analytics. e.g. \"document\". */\n type: string;\n /** Drizzle table for the parent resource (must have ownableColumns()). */\n resourceTable: any;\n /** Drizzle table produced by createSharesTable(). */\n sharesTable: any;\n /** Human-readable singular label shown in the share dialog. */\n displayName: string;\n /**\n * Column on the resource table that holds a human-readable title for\n * display in the share UI. Default: \"title\".\n */\n titleColumn?: string;\n /**\n * Optional app-relative path to this resource. Used by share notifications\n * when the caller does not pass a more specific resourceUrl.\n */\n getResourcePath?: (resource: any) => string | undefined;\n /**\n * Optional resolver for the share-notification email's brand logo. Return an\n * absolute `https://` URL to override the default embedded Agent Native logo\n * (e.g. the sharing org's own logo), or undefined to fall back. Receives the\n * resource row being shared so the template can scope the logo to its org.\n */\n getLogoUrl?: (\n resource: any,\n ) => string | undefined | Promise<string | undefined>;\n /**\n * Optional resolver for the brand name shown beside the logo in the\n * share-notification email header. Return a display name (e.g. the sharing\n * org's name) to override the app name, or undefined to keep the default.\n * Receives the resource row being shared.\n */\n getBrandName?: (\n resource: any,\n ) => string | undefined | Promise<string | undefined>;\n /**\n * Optional resolver for who the share-notification email appears to come\n * from. `fromName` changes only the display name and keeps the configured,\n * domain-verified sending address (e.g. \"Alice via Clips\"); `replyTo` routes\n * replies to the sharer. `ctx.sender` is the account doing the sharing, so the\n * template decides how to present them (name, email, avatar).\n *\n * Do not put a user's address in the sending address itself — SPF/DKIM sign\n * the app's domain, so recipients would bounce or spam-filter it.\n */\n getSender?: (\n resource: any,\n ctx: { sender: UserProfile },\n ) =>\n | { fromName?: string; replyTo?: string }\n | undefined\n | Promise<{ fromName?: string; replyTo?: string } | undefined>;\n /**\n * Optional resolver for a preview block shown above the CTA in the\n * share-notification email — e.g. a recording thumbnail with a play badge.\n * Return trusted HTML (built by template code, dynamic values escaped) that\n * is injected verbatim, or undefined to omit it. `ctx.href` is the resolved\n * link to the resource; `ctx.alt` is its title. The template owns the entire\n * preview markup so the generic share action stays app-agnostic.\n */\n getHeroHtml?: (\n resource: any,\n ctx: { href: string; alt?: string },\n ) => string | undefined | Promise<string | undefined>;\n /**\n * Drizzle DB accessor from the template's server/db/index.ts. Required —\n * the framework-level share actions and access helpers call this to reach\n * the right DB instance (schema is template-specific).\n */\n getDb: () => any;\n /**\n * When `false`, `visibility: \"public\"` is rejected by `set-resource-visibility`,\n * and `accessFilter` / `resolveAccess` treat any stored public row as private\n * (defense in depth — only owner + explicit shares grant access).\n *\n * Use this for resources that execute code or expose privileged data and must\n * never be reachable by a random authenticated user. Extensions set this:\n * extension HTML runs inside an iframe that calls actions / DB / proxied APIs\n * as the *viewer*, so a public extension would be arbitrary code with the\n * viewer's credentials.\n *\n * Default: `true` (matches the historical behavior — most resources can be public).\n */\n allowPublic?: boolean;\n /**\n * Optional role granted by a public-by-link read. Most resources should omit\n * this so public visibility remains viewer-only. Use narrowly for local or\n * otherwise constrained resources where the resource owner intentionally wants\n * unauthenticated link holders to do more than view.\n *\n * When this is a function, it may read arbitrary fields on the resource row\n * (not just the ownership/visibility columns). Because of that,\n * `resolveAccess`/`assertAccess`'s opt-in `{ skipResourceBody: true }`\n * projected load (see `access.ts`) is automatically ignored for this\n * registration and always loads the full row instead — a fixed role string\n * has no such requirement and is compatible with the projection.\n */\n publicAccessRole?:\n | \"viewer\"\n | \"editor\"\n | \"admin\"\n | ((\n resource: any,\n ctx: { userEmail?: string; orgId?: string },\n ) =>\n | \"viewer\"\n | \"editor\"\n | \"admin\"\n | Promise<\"viewer\" | \"editor\" | \"admin\">);\n /**\n * When `true`, individual user shares (`principalType: \"user\"`) must target\n * an email that is already a member of the same org as the resource, OR has\n * a pending invitation to that org. Cross-org user shares are rejected.\n *\n * Pair with `allowPublic: false` for resources that need a hard \"this org\n * only\" trust boundary. Extensions set this so a malicious caller can't\n * widen reach by sharing a code-executing extension to an outsider email.\n *\n * Default: `false` (matches the historical behavior — any email can be granted).\n */\n requireOrgMemberForUserShares?: boolean;\n /**\n * Optional per-resource access-context adapter. Most resources should use the\n * request user/org unchanged. Templates with an intentional alternate local\n * identity can normalize here so the generic framework sharing actions and\n * access helpers stay in sync with template-owned actions.\n */\n resolveAccessContext?: (ctx: { userEmail?: string; orgId?: string }) => {\n userEmail?: string;\n orgId?: string;\n };\n /**\n * When true, direct ownership is recognized by owner_email regardless of the\n * caller's active org. Use this only for resource types where the template's\n * own actions already treat owner_email as the cross-org authority and list\n * views add their own org filters.\n *\n * Default: `false`.\n */\n ownerAccessIgnoresOrg?: boolean;\n /**\n * Optional external-agent read handoff. When set, the framework-level\n * `create-agent-resource-link` action can mint a short-lived, read-only\n * `agent_access` URL for this resource. The context endpoint is owned by the\n * template so it can expose the same intentionally shareable shape as the\n * public page, not a generic raw database row.\n */\n agentReadable?:\n | false\n | {\n /** Token scope. Include the app name to avoid cross-app collisions. */\n resourceKind: string;\n /** App-relative JSON endpoint that accepts `id` + `agent_access`. */\n getContextPath: (resource: any) => string | undefined;\n /** Optional override for the page URL. Defaults to getResourcePath. */\n getPagePath?: (resource: any) => string | undefined;\n /** Optional override for the default two-hour token lifetime. */\n ttlSeconds?: number;\n };\n}\n\n// Stash the registry on globalThis so it survives SSR bundle duplication.\n// Vite SSR's `noExternal: /^(?!node:)/` policy means @agent-native/core gets\n// inlined into every server bundle that imports it — and each bundle gets its\n// own module-level state. A plain `new Map()` here would create one Map per\n// bundle, so the template's `registerShareableResource()` (called from the\n// Nitro plugin graph) wouldn't be visible to the framework's auto-mounted\n// share-resource action (loaded via `import(\"../sharing/actions/...js\")` in a\n// different module instance). Using globalThis collapses them back to one Map.\nconst REGISTRY_KEY = \"__agentNativeShareableResources__\";\ntype RegistryStore = Map<string, ShareableResourceRegistration>;\nconst globalRegistry: { [K in typeof REGISTRY_KEY]?: RegistryStore } =\n globalThis as any;\n\nfunction isTestRuntime(): boolean {\n return (\n process.env.NODE_ENV === \"test\" ||\n process.env.VITEST === \"true\" ||\n process.env.VITEST === \"1\"\n );\n}\n\nfunction registrationCameFromTestFile(): boolean {\n const stack = new Error().stack ?? \"\";\n return /[./\\\\](?:[^/\\\\]+[.-])(?:test|spec)\\.[cm]?[jt]sx?(?::\\d+)?(?::\\d+)?/.test(\n stack,\n );\n}\n\nfunction getRegistry(): RegistryStore {\n let r = globalRegistry[REGISTRY_KEY];\n if (!r) {\n r = new Map<string, ShareableResourceRegistration>();\n globalRegistry[REGISTRY_KEY] = r;\n }\n return r;\n}\n\nexport function registerShareableResource(\n entry: ShareableResourceRegistration,\n): void {\n if (!isTestRuntime() && registrationCameFromTestFile()) return;\n getRegistry().set(entry.type, entry);\n}\n\nexport function getShareableResource(\n type: string,\n): ShareableResourceRegistration | undefined {\n return getRegistry().get(type);\n}\n\nexport function requireShareableResource(\n type: string,\n): ShareableResourceRegistration {\n const reg = getRegistry();\n const entry = reg.get(type);\n if (!entry) {\n throw new Error(\n `Unknown shareable resource type: \"${type}\". Did you forget registerShareableResource()?`,\n );\n }\n return entry;\n}\n\nexport function listShareableResources(): ShareableResourceRegistration[] {\n return Array.from(getRegistry().values());\n}\n"]}
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/sharing/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAgLH,0EAA0E;AAC1E,6EAA6E;AAC7E,8EAA8E;AAC9E,4EAA4E;AAC5E,2EAA2E;AAC3E,0EAA0E;AAC1E,8EAA8E;AAC9E,+EAA+E;AAC/E,MAAM,YAAY,GAAG,mCAAmC,CAAC;AAEzD,MAAM,cAAc,GAClB,UAAiB,CAAC;AAEpB,SAAS,aAAa;IACpB,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM;QAC/B,OAAO,CAAC,GAAG,CAAC,MAAM,KAAK,MAAM;QAC7B,OAAO,CAAC,GAAG,CAAC,MAAM,KAAK,GAAG,CAC3B,CAAC;AACJ,CAAC;AAED,SAAS,4BAA4B;IACnC,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;IACtC,OAAO,oEAAoE,CAAC,IAAI,CAC9E,KAAK,CACN,CAAC;AACJ,CAAC;AAED,SAAS,WAAW;IAClB,IAAI,CAAC,GAAG,cAAc,CAAC,YAAY,CAAC,CAAC;IACrC,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,CAAC,GAAG,IAAI,GAAG,EAAyC,CAAC;QACrD,cAAc,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,KAAoC;IAEpC,IAAI,CAAC,aAAa,EAAE,IAAI,4BAA4B,EAAE;QAAE,OAAO;IAC/D,WAAW,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,IAAY;IAEZ,OAAO,WAAW,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,IAAY;IAEZ,MAAM,GAAG,GAAG,WAAW,EAAE,CAAC;IAC1B,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,qCAAqC,IAAI,gDAAgD,CAC1F,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,OAAO,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;AAC5C,CAAC","sourcesContent":["/**\n * Registry of shareable resources.\n *\n * Each template registers its ownable resource(s) once on module load so the\n * framework-level share actions (`share-resource`, `list-resource-shares`,\n * etc.) can dispatch to the correct tables.\n *\n * import { registerShareableResource } from \"@agent-native/core/sharing\";\n * import * as schema from \"./schema.js\";\n *\n * registerShareableResource({\n * type: \"document\",\n * resourceTable: schema.documents,\n * sharesTable: schema.documentShares,\n * displayName: \"Document\",\n * titleColumn: \"title\",\n * });\n */\n\nimport type { UserProfile } from \"../user-profile/shared.js\";\n\nexport interface ShareableResourceRegistration {\n /** Stable identifier used across actions, UI, and analytics. e.g. \"document\". */\n type: string;\n /** Drizzle table for the parent resource (must have ownableColumns()). */\n resourceTable: any;\n /** Drizzle table produced by createSharesTable(). */\n sharesTable: any;\n /** Human-readable singular label shown in the share dialog. */\n displayName: string;\n /**\n * Column on the resource table that holds a human-readable title for\n * display in the share UI. Default: \"title\".\n */\n titleColumn?: string;\n /**\n * Optional app-relative path to this resource. Used by share notifications\n * when the caller does not pass a more specific resourceUrl.\n */\n getResourcePath?: (resource: any) => string | undefined;\n /**\n * Optional resolver for the share-notification email's brand logo. Return an\n * absolute `https://` URL to override the default embedded Agent Native logo\n * (e.g. the sharing org's own logo), or undefined to fall back. Receives the\n * resource row being shared so the template can scope the logo to its org.\n */\n getLogoUrl?: (\n resource: any,\n ) => string | undefined | Promise<string | undefined>;\n /**\n * Optional resolver for the brand name shown beside the logo in the\n * share-notification email header. Return a display name (e.g. the sharing\n * org's name) to override the app name, or undefined to keep the default.\n * Receives the resource row being shared.\n */\n getBrandName?: (\n resource: any,\n ) => string | undefined | Promise<string | undefined>;\n /**\n * Optional resolver for who the share-notification email appears to come\n * from. `fromName` changes only the display name and keeps the configured,\n * domain-verified sending address (e.g. \"Alice via Clips\"); `replyTo` routes\n * replies to the sharer. `ctx.sender` is the account doing the sharing, so the\n * template decides how to present them (name, email, avatar).\n *\n * Do not put a user's address in the sending address itself — SPF/DKIM sign\n * the app's domain, so recipients would bounce or spam-filter it.\n */\n getSender?: (\n resource: any,\n ctx: { sender: UserProfile },\n ) =>\n | { fromName?: string; replyTo?: string }\n | undefined\n | Promise<{ fromName?: string; replyTo?: string } | undefined>;\n /**\n * Optional resolver for a preview block shown above the CTA in the\n * share-notification email — e.g. a recording thumbnail with a play badge.\n * Return trusted HTML (built by template code, dynamic values escaped) that\n * is injected verbatim, or undefined to omit it. `ctx.href` is the resolved\n * link to the resource; `ctx.alt` is its title. The template owns the entire\n * preview markup so the generic share action stays app-agnostic.\n */\n getHeroHtml?: (\n resource: any,\n ctx: { href: string; alt?: string },\n ) => string | undefined | Promise<string | undefined>;\n /**\n * Drizzle DB accessor from the template's server/db/index.ts. Required —\n * the framework-level share actions and access helpers call this to reach\n * the right DB instance (schema is template-specific).\n */\n getDb: () => any;\n /**\n * When `false`, `visibility: \"public\"` is rejected by `set-resource-visibility`,\n * and `accessFilter` / `resolveAccess` treat any stored public row as private\n * (defense in depth — only owner + explicit shares grant access).\n *\n * Use this for resources that execute code or expose privileged data and must\n * never be reachable by a random authenticated user. Extensions set this:\n * extension HTML runs inside an iframe that calls actions / DB / proxied APIs\n * as the *viewer*, so a public extension would be arbitrary code with the\n * viewer's credentials.\n *\n * Default: `true` (matches the historical behavior — most resources can be public).\n */\n allowPublic?: boolean;\n /**\n * Optional role granted by a public-by-link read. Most resources should omit\n * this so public visibility remains viewer-only. Use narrowly for local or\n * otherwise constrained resources where the resource owner intentionally wants\n * unauthenticated link holders to do more than view.\n *\n * When this is a function, it may read arbitrary fields on the resource row\n * (not just the ownership/visibility columns). Because of that,\n * `resolveAccess`/`assertAccess`'s opt-in `{ skipResourceBody: true }`\n * projected load (see `access.ts`) is automatically ignored for this\n * registration and always loads the full row instead — a fixed role string\n * has no such requirement and is compatible with the projection.\n */\n publicAccessRole?:\n | \"viewer\"\n | \"editor\"\n | \"admin\"\n | ((\n resource: any,\n ctx: {\n userEmail?: string;\n orgId?: string;\n authCapability?: string;\n },\n ) =>\n | \"viewer\"\n | \"editor\"\n | \"admin\"\n | Promise<\"viewer\" | \"editor\" | \"admin\">);\n /**\n * When `true`, individual user shares (`principalType: \"user\"`) must target\n * an email that is already a member of the same org as the resource, OR has\n * a pending invitation to that org. Cross-org user shares are rejected.\n *\n * Pair with `allowPublic: false` for resources that need a hard \"this org\n * only\" trust boundary. Extensions set this so a malicious caller can't\n * widen reach by sharing a code-executing extension to an outsider email.\n *\n * Default: `false` (matches the historical behavior — any email can be granted).\n */\n requireOrgMemberForUserShares?: boolean;\n /**\n * Optional per-resource access-context adapter. Most resources should use the\n * request user/org unchanged. Templates with an intentional alternate local\n * identity can normalize here so the generic framework sharing actions and\n * access helpers stay in sync with template-owned actions.\n */\n resolveAccessContext?: (ctx: {\n userEmail?: string;\n orgId?: string;\n authCapability?: string;\n }) => {\n userEmail?: string;\n orgId?: string;\n authCapability?: string;\n };\n /**\n * When true, direct ownership is recognized by owner_email regardless of the\n * caller's active org. Use this only for resource types where the template's\n * own actions already treat owner_email as the cross-org authority and list\n * views add their own org filters.\n *\n * Default: `false`.\n */\n ownerAccessIgnoresOrg?: boolean;\n /**\n * Optional external-agent read handoff. When set, the framework-level\n * `create-agent-resource-link` action can mint a short-lived, read-only\n * `agent_access` URL for this resource. The context endpoint is owned by the\n * template so it can expose the same intentionally shareable shape as the\n * public page, not a generic raw database row.\n */\n agentReadable?:\n | false\n | {\n /** Token scope. Include the app name to avoid cross-app collisions. */\n resourceKind: string;\n /** App-relative JSON endpoint that accepts `id` + `agent_access`. */\n getContextPath: (resource: any) => string | undefined;\n /** Optional override for the page URL. Defaults to getResourcePath. */\n getPagePath?: (resource: any) => string | undefined;\n /** Optional override for the default two-hour token lifetime. */\n ttlSeconds?: number;\n };\n}\n\n// Stash the registry on globalThis so it survives SSR bundle duplication.\n// Vite SSR's `noExternal: /^(?!node:)/` policy means @agent-native/core gets\n// inlined into every server bundle that imports it — and each bundle gets its\n// own module-level state. A plain `new Map()` here would create one Map per\n// bundle, so the template's `registerShareableResource()` (called from the\n// Nitro plugin graph) wouldn't be visible to the framework's auto-mounted\n// share-resource action (loaded via `import(\"../sharing/actions/...js\")` in a\n// different module instance). Using globalThis collapses them back to one Map.\nconst REGISTRY_KEY = \"__agentNativeShareableResources__\";\ntype RegistryStore = Map<string, ShareableResourceRegistration>;\nconst globalRegistry: { [K in typeof REGISTRY_KEY]?: RegistryStore } =\n globalThis as any;\n\nfunction isTestRuntime(): boolean {\n return (\n process.env.NODE_ENV === \"test\" ||\n process.env.VITEST === \"true\" ||\n process.env.VITEST === \"1\"\n );\n}\n\nfunction registrationCameFromTestFile(): boolean {\n const stack = new Error().stack ?? \"\";\n return /[./\\\\](?:[^/\\\\]+[.-])(?:test|spec)\\.[cm]?[jt]sx?(?::\\d+)?(?::\\d+)?/.test(\n stack,\n );\n}\n\nfunction getRegistry(): RegistryStore {\n let r = globalRegistry[REGISTRY_KEY];\n if (!r) {\n r = new Map<string, ShareableResourceRegistration>();\n globalRegistry[REGISTRY_KEY] = r;\n }\n return r;\n}\n\nexport function registerShareableResource(\n entry: ShareableResourceRegistration,\n): void {\n if (!isTestRuntime() && registrationCameFromTestFile()) return;\n getRegistry().set(entry.type, entry);\n}\n\nexport function getShareableResource(\n type: string,\n): ShareableResourceRegistration | undefined {\n return getRegistry().get(type);\n}\n\nexport function requireShareableResource(\n type: string,\n): ShareableResourceRegistration {\n const reg = getRegistry();\n const entry = reg.get(type);\n if (!entry) {\n throw new Error(\n `Unknown shareable resource type: \"${type}\". Did you forget registerShareableResource()?`,\n );\n }\n return entry;\n}\n\nexport function listShareableResources(): ShareableResourceRegistration[] {\n return Array.from(getRegistry().values());\n}\n"]}
@@ -0,0 +1,28 @@
1
+ import { z } from "zod";
2
+ import type { ActionEntry } from "../agent/production-agent.js";
3
+ declare const workspaceFileResultSchema: z.ZodObject<{
4
+ file: z.ZodObject<{
5
+ resourceId: z.ZodString;
6
+ path: z.ZodString;
7
+ name: z.ZodString;
8
+ contentType: z.ZodString;
9
+ sizeBytes: z.ZodNumber;
10
+ updatedAt: z.ZodString;
11
+ }, z.core.$strip>;
12
+ }, z.core.$strip>;
13
+ export type WorkspaceFileActionResult = z.infer<typeof workspaceFileResultSchema>;
14
+ export declare const showWorkspaceFileAction: import("../action.js").ActionDefinition<{
15
+ path: string;
16
+ }, {
17
+ file: {
18
+ resourceId: string;
19
+ path: string;
20
+ name: string;
21
+ contentType: string;
22
+ sizeBytes: number;
23
+ updatedAt: string;
24
+ };
25
+ }>;
26
+ export declare function createWorkspaceFileActionEntries(): Record<string, ActionEntry>;
27
+ export {};
28
+ //# sourceMappingURL=actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/workspace-files/actions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAOhE,QAAA,MAAM,yBAAyB;;;;;;;;;iBAS7B,CAAC;AAEH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAC7C,OAAO,yBAAyB,CACjC,CAAC;AAuBF,eAAO,MAAM,uBAAuB;;;;QAkC5B,UAAU;QACV,IAAI;QACJ,IAAI;QACJ,WAAW;QACX,SAAS;QACT,SAAS;;EAIf,CAAC;AAEH,wBAAgB,gCAAgC,IAAI,MAAM,CACxD,MAAM,EACN,WAAW,CACZ,CAIA"}
@@ -0,0 +1,75 @@
1
+ import { z } from "zod";
2
+ import { ACTION_CHAT_UI_WORKSPACE_FILE_RENDERER } from "../action-ui.js";
3
+ import { defineAction } from "../action.js";
4
+ import { getRequestOrgId, getRequestUserEmail, } from "../server/request-context.js";
5
+ import { getWorkspaceFileMeta } from "./store.js";
6
+ const workspaceFileResultSchema = z.object({
7
+ file: z.object({
8
+ resourceId: z.string().min(1),
9
+ path: z.string().min(1),
10
+ name: z.string().min(1),
11
+ contentType: z.string().min(1),
12
+ sizeBytes: z.number().int().nonnegative(),
13
+ updatedAt: z.string().min(1),
14
+ }),
15
+ });
16
+ function currentWorkspaceScope() {
17
+ const orgId = getRequestOrgId();
18
+ if (orgId)
19
+ return { scope: "org", scopeId: orgId };
20
+ const userEmail = getRequestUserEmail();
21
+ if (userEmail)
22
+ return { scope: "user", scopeId: userEmail };
23
+ throw new Error("show-workspace-file requires an authenticated request context.");
24
+ }
25
+ function fileName(path) {
26
+ return path.split("/").at(-1) || path;
27
+ }
28
+ function isTextWorkspaceFile(contentType) {
29
+ const mimeType = contentType.split(";")[0].trim().toLowerCase();
30
+ return mimeType.startsWith("text/") || mimeType === "application/json";
31
+ }
32
+ export const showWorkspaceFileAction = defineAction({
33
+ description: "Render a workspace file as a downloadable card in chat. Call this after writing a user-requested export or other durable file with workspaceWrite; a file export is not complete until this action succeeds and the download card appears. The path must exactly match an existing file in the current organization or personal workspace.",
34
+ schema: z.object({
35
+ path: z
36
+ .string()
37
+ .trim()
38
+ .min(1)
39
+ .describe('Exact workspace-relative path returned by workspaceWrite, for example "exports/report.csv".'),
40
+ }),
41
+ outputSchema: workspaceFileResultSchema,
42
+ outputErrorStrategy: "strict",
43
+ http: false,
44
+ readOnly: true,
45
+ chatUI: {
46
+ renderer: ACTION_CHAT_UI_WORKSPACE_FILE_RENDERER,
47
+ title: "Workspace file",
48
+ description: "A private, downloadable workspace file.",
49
+ },
50
+ run: async ({ path }) => {
51
+ const file = await getWorkspaceFileMeta(currentWorkspaceScope(), path);
52
+ if (!file) {
53
+ throw new Error(`Workspace file not found: "${path}"`);
54
+ }
55
+ if (!isTextWorkspaceFile(file.contentType)) {
56
+ throw new Error(`Workspace file "${path}" has unsupported content type "${file.contentType}". show-workspace-file currently supports text files and application/json.`);
57
+ }
58
+ return {
59
+ file: {
60
+ resourceId: file.id,
61
+ path: file.path,
62
+ name: fileName(file.path),
63
+ contentType: file.contentType,
64
+ sizeBytes: file.sizeBytes,
65
+ updatedAt: file.updatedAt,
66
+ },
67
+ };
68
+ },
69
+ });
70
+ export function createWorkspaceFileActionEntries() {
71
+ return {
72
+ "show-workspace-file": showWorkspaceFileAction,
73
+ };
74
+ }
75
+ //# sourceMappingURL=actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.js","sourceRoot":"","sources":["../../src/workspace-files/actions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,sCAAsC,EAAE,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,EACL,eAAe,EACf,mBAAmB,GACpB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,oBAAoB,EAA4B,MAAM,YAAY,CAAC;AAE5E,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;QACb,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACvB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACvB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;QACzC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;KAC7B,CAAC;CACH,CAAC,CAAC;AAMH,SAAS,qBAAqB;IAC5B,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;IAChC,IAAI,KAAK;QAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAEnD,MAAM,SAAS,GAAG,mBAAmB,EAAE,CAAC;IACxC,IAAI,SAAS;QAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;IAE5D,MAAM,IAAI,KAAK,CACb,gEAAgE,CACjE,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AACxC,CAAC;AAED,SAAS,mBAAmB,CAAC,WAAmB;IAC9C,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAChE,OAAO,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,QAAQ,KAAK,kBAAkB,CAAC;AACzE,CAAC;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAG,YAAY,CAAC;IAClD,WAAW,EACT,4UAA4U;IAC9U,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,IAAI,EAAE,CAAC;aACJ,MAAM,EAAE;aACR,IAAI,EAAE;aACN,GAAG,CAAC,CAAC,CAAC;aACN,QAAQ,CACP,6FAA6F,CAC9F;KACJ,CAAC;IACF,YAAY,EAAE,yBAAyB;IACvC,mBAAmB,EAAE,QAAQ;IAC7B,IAAI,EAAE,KAAK;IACX,QAAQ,EAAE,IAAI;IACd,MAAM,EAAE;QACN,QAAQ,EAAE,sCAAsC;QAChD,KAAK,EAAE,gBAAgB;QACvB,WAAW,EAAE,yCAAyC;KACvD;IACD,GAAG,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACtB,MAAM,IAAI,GAAG,MAAM,oBAAoB,CAAC,qBAAqB,EAAE,EAAE,IAAI,CAAC,CAAC;QACvE,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,8BAA8B,IAAI,GAAG,CAAC,CAAC;QACzD,CAAC;QACD,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;YAC3C,MAAM,IAAI,KAAK,CACb,mBAAmB,IAAI,mCAAmC,IAAI,CAAC,WAAW,4EAA4E,CACvJ,CAAC;QACJ,CAAC;QAED,OAAO;YACL,IAAI,EAAE;gBACJ,UAAU,EAAE,IAAI,CAAC,EAAE;gBACnB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;gBACzB,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,SAAS,EAAE,IAAI,CAAC,SAAS;aAC1B;SACF,CAAC;IACJ,CAAC;CACF,CAAC,CAAC;AAEH,MAAM,UAAU,gCAAgC;IAI9C,OAAO;QACL,qBAAqB,EAAE,uBAAuB;KAC/C,CAAC;AACJ,CAAC","sourcesContent":["import { z } from \"zod\";\n\nimport { ACTION_CHAT_UI_WORKSPACE_FILE_RENDERER } from \"../action-ui.js\";\nimport { defineAction } from \"../action.js\";\nimport type { ActionEntry } from \"../agent/production-agent.js\";\nimport {\n getRequestOrgId,\n getRequestUserEmail,\n} from \"../server/request-context.js\";\nimport { getWorkspaceFileMeta, type WorkspaceFilesScope } from \"./store.js\";\n\nconst workspaceFileResultSchema = z.object({\n file: z.object({\n resourceId: z.string().min(1),\n path: z.string().min(1),\n name: z.string().min(1),\n contentType: z.string().min(1),\n sizeBytes: z.number().int().nonnegative(),\n updatedAt: z.string().min(1),\n }),\n});\n\nexport type WorkspaceFileActionResult = z.infer<\n typeof workspaceFileResultSchema\n>;\n\nfunction currentWorkspaceScope(): WorkspaceFilesScope {\n const orgId = getRequestOrgId();\n if (orgId) return { scope: \"org\", scopeId: orgId };\n\n const userEmail = getRequestUserEmail();\n if (userEmail) return { scope: \"user\", scopeId: userEmail };\n\n throw new Error(\n \"show-workspace-file requires an authenticated request context.\",\n );\n}\n\nfunction fileName(path: string): string {\n return path.split(\"/\").at(-1) || path;\n}\n\nfunction isTextWorkspaceFile(contentType: string): boolean {\n const mimeType = contentType.split(\";\")[0].trim().toLowerCase();\n return mimeType.startsWith(\"text/\") || mimeType === \"application/json\";\n}\n\nexport const showWorkspaceFileAction = defineAction({\n description:\n \"Render a workspace file as a downloadable card in chat. Call this after writing a user-requested export or other durable file with workspaceWrite; a file export is not complete until this action succeeds and the download card appears. The path must exactly match an existing file in the current organization or personal workspace.\",\n schema: z.object({\n path: z\n .string()\n .trim()\n .min(1)\n .describe(\n 'Exact workspace-relative path returned by workspaceWrite, for example \"exports/report.csv\".',\n ),\n }),\n outputSchema: workspaceFileResultSchema,\n outputErrorStrategy: \"strict\",\n http: false,\n readOnly: true,\n chatUI: {\n renderer: ACTION_CHAT_UI_WORKSPACE_FILE_RENDERER,\n title: \"Workspace file\",\n description: \"A private, downloadable workspace file.\",\n },\n run: async ({ path }) => {\n const file = await getWorkspaceFileMeta(currentWorkspaceScope(), path);\n if (!file) {\n throw new Error(`Workspace file not found: \"${path}\"`);\n }\n if (!isTextWorkspaceFile(file.contentType)) {\n throw new Error(\n `Workspace file \"${path}\" has unsupported content type \"${file.contentType}\". show-workspace-file currently supports text files and application/json.`,\n );\n }\n\n return {\n file: {\n resourceId: file.id,\n path: file.path,\n name: fileName(file.path),\n contentType: file.contentType,\n sizeBytes: file.sizeBytes,\n updatedAt: file.updatedAt,\n },\n };\n },\n});\n\nexport function createWorkspaceFileActionEntries(): Record<\n string,\n ActionEntry\n> {\n return {\n \"show-workspace-file\": showWorkspaceFileAction,\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/workspace-files/tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AA2BhE,wBAAgB,wBAAwB,IAAI,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAkOtE"}
1
+ {"version":3,"file":"tool.d.ts","sourceRoot":"","sources":["../../src/workspace-files/tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AA2BhE,wBAAgB,wBAAwB,IAAI,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAsOtE"}