@agent-native/core 0.121.2 → 0.122.1

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 (588) hide show
  1. package/README.md +4 -12
  2. package/corpus/README.md +2 -2
  3. package/corpus/core/CHANGELOG.md +148 -0
  4. package/corpus/core/docs/content/actions.mdx +2 -2
  5. package/corpus/core/docs/content/agent-surfaces.mdx +1 -1
  6. package/corpus/core/docs/content/authentication.mdx +2 -0
  7. package/corpus/core/docs/content/deployment.mdx +36 -0
  8. package/corpus/core/docs/content/http-api.mdx +266 -0
  9. package/corpus/core/docs/content/syncing-template-changes.mdx +159 -0
  10. package/corpus/core/package.json +2 -1
  11. package/corpus/core/src/a2a/activity.ts +38 -27
  12. package/corpus/core/src/a2a/client.ts +57 -4
  13. package/corpus/core/src/agent/engine/builder-engine.ts +35 -4
  14. package/corpus/core/src/agent/run-manager.ts +20 -1
  15. package/corpus/core/src/agent/run-store.ts +4 -1
  16. package/corpus/core/src/agent/types.ts +28 -8
  17. package/corpus/core/src/chat-threads/store.ts +45 -0
  18. package/corpus/core/src/checkpoints/index.ts +2 -0
  19. package/corpus/core/src/checkpoints/route-match.ts +13 -0
  20. package/corpus/core/src/cli/create.ts +105 -17
  21. package/corpus/core/src/cli/index.ts +20 -0
  22. package/corpus/core/src/cli/template-baseline.ts +410 -0
  23. package/corpus/core/src/cli/template-sync.ts +1004 -0
  24. package/corpus/core/src/cli/templates-meta.ts +1 -0
  25. package/corpus/core/src/client/AssistantChat.tsx +38 -12
  26. package/corpus/core/src/client/NewWorkspaceAppFlow.tsx +75 -10
  27. package/corpus/core/src/client/chat/message-components.tsx +127 -66
  28. package/corpus/core/src/client/chat/tool-call-display.tsx +29 -5
  29. package/corpus/core/src/client/error-format.ts +18 -0
  30. package/corpus/core/src/client/org/OrgSwitcher.tsx +30 -8
  31. package/corpus/core/src/client/org/TeamPage.tsx +7 -6
  32. package/corpus/core/src/client/settings/SettingsPanel.tsx +9 -9
  33. package/corpus/core/src/client/settings/SettingsTabsPage.tsx +12 -2
  34. package/corpus/core/src/client/sse-event-processor.ts +3 -0
  35. package/corpus/core/src/data-widgets/index.ts +41 -0
  36. package/corpus/core/src/deploy/build.ts +11 -9
  37. package/corpus/core/src/extensions/url-safety.ts +67 -4
  38. package/corpus/core/src/integrations/webhook-handler.ts +63 -9
  39. package/corpus/core/src/org/auto-join-domain.ts +4 -3
  40. package/corpus/core/src/org/context.ts +108 -39
  41. package/corpus/core/src/org/index.ts +5 -0
  42. package/corpus/core/src/org/service-identity.ts +60 -0
  43. package/corpus/core/src/provider-api/index.ts +22 -1
  44. package/corpus/core/src/scripts/docs/search.ts +12 -1
  45. package/corpus/core/src/server/agent-chat/context-tools.ts +9 -4
  46. package/corpus/core/src/server/agent-chat/framework-prompts.ts +38 -66
  47. package/corpus/core/src/server/agent-chat-plugin.ts +17 -10
  48. package/corpus/core/src/server/agents-bundle.ts +84 -5
  49. package/corpus/core/src/server/auth.ts +4 -3
  50. package/corpus/core/src/server/builder-browser.ts +47 -19
  51. package/corpus/core/src/server/core-routes-plugin.ts +2 -2
  52. package/corpus/core/src/server/credential-provider.ts +209 -68
  53. package/corpus/core/src/server/index.ts +3 -0
  54. package/corpus/core/src/server/prompts/framework-core-compact.ts +6 -13
  55. package/corpus/core/src/server/prompts/framework-core.ts +9 -21
  56. package/corpus/core/src/server/prompts/index.ts +0 -1
  57. package/corpus/core/src/server/prompts/shared-rules.ts +19 -11
  58. package/corpus/core/src/server/ssr-handler.ts +13 -2
  59. package/corpus/core/src/settings/user-settings.ts +13 -1
  60. package/corpus/core/src/shared/cache-control.ts +141 -0
  61. package/corpus/core/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  62. package/corpus/core/src/templates/chat/_gitignore +1 -0
  63. package/corpus/core/src/templates/chat/app/components/layout/Sidebar.tsx +0 -7
  64. package/corpus/core/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  65. package/corpus/core/src/templates/default/app/root.tsx +1 -1
  66. package/corpus/core/src/templates/default/public/manifest.json +2 -2
  67. package/corpus/core/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  68. package/corpus/core/src/templates/workspace-core/.agents/skills/performance/SKILL.md +19 -0
  69. package/corpus/core/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  70. package/corpus/core/src/templates/workspace-core/.agents/skills/visual-answer/SKILL.md +1 -0
  71. package/corpus/core/src/vite/agents-bundle-plugin.ts +7 -6
  72. package/corpus/core/src/vite/client.ts +69 -3
  73. package/corpus/templates/analytics/.agents/skills/adhoc-analysis/SKILL.md +4 -5
  74. package/corpus/templates/analytics/.agents/skills/admin-surfaces/SKILL.md +55 -0
  75. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +10 -0
  76. package/corpus/templates/analytics/.agents/skills/dashboard-management/references/template-catalog-and-demo.md +62 -0
  77. package/corpus/templates/analytics/.agents/skills/dashboard-ops/SKILL.md +85 -0
  78. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +27 -2
  79. package/corpus/templates/analytics/.agents/skills/data-querying/references/inline-chart-embeds.md +139 -0
  80. package/corpus/templates/analytics/.agents/skills/monitoring/SKILL.md +77 -0
  81. package/corpus/templates/analytics/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  82. package/corpus/templates/analytics/AGENTS.md +91 -563
  83. package/corpus/templates/analytics/_gitignore +1 -0
  84. package/corpus/templates/analytics/actions/bigquery.ts +12 -4
  85. package/corpus/templates/analytics/actions/generate-chart.ts +5 -5
  86. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +30 -1
  87. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -23
  88. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +3 -0
  89. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +1 -1
  90. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +1 -0
  91. package/corpus/templates/analytics/app/routes/_index.tsx +1 -1
  92. package/corpus/templates/analytics/changelog/2026-07-24-analytics-finds-saved-dashboards-and-queries-far-more-reliab.md +6 -0
  93. package/corpus/templates/analytics/changelog/2026-07-24-notion-access-errors-now-explain-that-a-page-needs-sharing-w.md +6 -0
  94. package/corpus/templates/analytics/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
  95. package/corpus/templates/analytics/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
  96. package/corpus/templates/analytics/package.json +1 -1
  97. package/corpus/templates/analytics/public/manifest.json +1 -1
  98. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +7 -0
  99. package/corpus/templates/analytics/server/lib/analytics-query-catalog.ts +191 -30
  100. package/corpus/templates/analytics/server/lib/bigquery.ts +11 -1
  101. package/corpus/templates/analytics/server/lib/notion.ts +15 -4
  102. package/corpus/templates/analytics/server/plugins/agent-chat.ts +10 -0
  103. package/corpus/templates/analytics/server/plugins/auth.ts +1 -1
  104. package/corpus/templates/assets/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  105. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +0 -2
  106. package/corpus/templates/assets/app/routes/_index.tsx +1 -1
  107. package/corpus/templates/assets/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
  108. package/corpus/templates/assets/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
  109. package/corpus/templates/assets/server/plugins/auth.ts +1 -1
  110. package/corpus/templates/brain/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  111. package/corpus/templates/brain/_gitignore +1 -0
  112. package/corpus/templates/brain/app/lib/brain.ts +0 -7
  113. package/corpus/templates/brain/app/routes/_index.tsx +1 -2
  114. package/corpus/templates/brain/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
  115. package/corpus/templates/brain/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
  116. package/corpus/templates/brain/package.json +1 -1
  117. package/corpus/templates/brain/public/manifest.json +1 -1
  118. package/corpus/templates/brain/server/plugins/auth.ts +1 -1
  119. package/corpus/templates/calendar/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  120. package/corpus/templates/calendar/_gitignore +1 -0
  121. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +0 -6
  122. package/corpus/templates/calendar/app/routes/_app._index.tsx +1 -1
  123. package/corpus/templates/calendar/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
  124. package/corpus/templates/calendar/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
  125. package/corpus/templates/calendar/package.json +1 -1
  126. package/corpus/templates/calendar/public/manifest.json +1 -1
  127. package/corpus/templates/calendar/server/plugins/auth.ts +1 -1
  128. package/corpus/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  129. package/corpus/templates/chat/_gitignore +1 -0
  130. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +0 -7
  131. package/corpus/templates/chat/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
  132. package/corpus/templates/clips/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  133. package/corpus/templates/clips/.agents/skills/video-sharing/SKILL.md +34 -0
  134. package/corpus/templates/clips/AGENTS.md +5 -4
  135. package/corpus/templates/clips/_gitignore +1 -0
  136. package/corpus/templates/clips/actions/cleanup-transcript.ts +5 -0
  137. package/corpus/templates/clips/actions/get-recording-insights.ts +44 -8
  138. package/corpus/templates/clips/actions/get-recording-player-data.ts +39 -70
  139. package/corpus/templates/clips/actions/import-loom-recording.ts +53 -98
  140. package/corpus/templates/clips/actions/lib/loom-import-job.ts +260 -0
  141. package/corpus/templates/clips/actions/list-clip-views.ts +2 -4
  142. package/corpus/templates/clips/actions/list-recordings.ts +69 -19
  143. package/corpus/templates/clips/actions/list-viewers.ts +5 -2
  144. package/corpus/templates/clips/actions/regenerate-summary.ts +5 -1
  145. package/corpus/templates/clips/actions/request-transcript.ts +28 -1
  146. package/corpus/templates/clips/app/components/editor/stitch-manager.tsx +21 -1
  147. package/corpus/templates/clips/app/components/library/library-layout.tsx +0 -7
  148. package/corpus/templates/clips/app/components/meetings/quick-ask-sidebar.tsx +14 -1
  149. package/corpus/templates/clips/app/components/player/insights-panel.tsx +8 -20
  150. package/corpus/templates/clips/app/components/player/recording-views-badge.tsx +335 -0
  151. package/corpus/templates/clips/app/components/player/video-player.tsx +13 -8
  152. package/corpus/templates/clips/app/components/sharing/share-ui.tsx +2 -1
  153. package/corpus/templates/clips/app/i18n/en-US.ts +13 -2
  154. package/corpus/templates/clips/app/lib/capture-permissions.ts +110 -0
  155. package/corpus/templates/clips/app/lib/recording-link.ts +32 -0
  156. package/corpus/templates/clips/app/routes/_app.library._index.tsx +1 -1
  157. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +27 -0
  158. package/corpus/templates/clips/app/routes/_index.tsx +1 -1
  159. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +52 -15
  160. package/corpus/templates/clips/app/routes/record.tsx +82 -31
  161. package/corpus/templates/clips/app/routes/share.$shareId.tsx +42 -31
  162. package/corpus/templates/clips/changelog/2026-07-24-clip-pages-now-show-a-view-count-with-viewer-avatars-next-to.md +6 -0
  163. package/corpus/templates/clips/changelog/2026-07-24-clip-summaries-read-like-a-description-you-d-write-yourself-.md +6 -0
  164. package/corpus/templates/clips/changelog/2026-07-24-clip-view-counts-now-separate-human-views-from-agent-views-s.md +6 -0
  165. package/corpus/templates/clips/changelog/2026-07-24-clips-actions-can-now-be-called-by-org-service-tokens-so-ext.md +6 -0
  166. package/corpus/templates/clips/changelog/2026-07-24-clips-no-longer-briefly-shows-a-dock-icon-while-launching.md +6 -0
  167. package/corpus/templates/clips/changelog/2026-07-24-fixed-owner-and-editor-share-links-redirecting-to-the-record.md +6 -0
  168. package/corpus/templates/clips/changelog/2026-07-24-meeting-and-note-recordings-now-start-as-a-compact-pill-on-t.md +6 -0
  169. package/corpus/templates/clips/changelog/2026-07-24-new-clips-now-copy-a-shareable-link-instead-of-the-owner-onl.md +6 -0
  170. package/corpus/templates/clips/changelog/2026-07-24-opening-a-share-link-as-the-clip-s-owner-now-goes-straight-t.md +6 -0
  171. package/corpus/templates/clips/changelog/2026-07-24-permissions-and-rewind-controls-now-use-compact-consistent-h.md +6 -0
  172. package/corpus/templates/clips/changelog/2026-07-24-quitting-the-desktop-app-no-longer-shows-a-false-macos-crash.md +6 -0
  173. package/corpus/templates/clips/changelog/2026-07-24-screen-recording-permission-errors-now-name-the-exact-app-to.md +6 -0
  174. package/corpus/templates/clips/changelog/2026-07-24-the-desktop-app-now-copies-a-new-clip-s-share-link-to-your-c.md +6 -0
  175. package/corpus/templates/clips/changelog/2026-07-24-the-recording-toolbar-now-stays-above-other-apps-including-o.md +6 -0
  176. package/corpus/templates/clips/changelog/2026-07-24-transcript-captions-now-use-real-speech-timings-instead-of-a.md +6 -0
  177. package/corpus/templates/clips/changelog/2026-07-24-video-playback-no-longer-stutters-and-replays-the-last-momen.md +6 -0
  178. package/corpus/templates/clips/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
  179. package/corpus/templates/clips/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
  180. package/corpus/templates/clips/chrome-extension/src/background.ts +17 -1
  181. package/corpus/templates/clips/desktop/package.json +2 -0
  182. package/corpus/templates/clips/desktop/src/app.tsx +89 -0
  183. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +9 -3
  184. package/corpus/templates/clips/desktop/src/lib/audio-meter.ts +51 -0
  185. package/corpus/templates/clips/desktop/src/lib/native-notification.ts +44 -0
  186. package/corpus/templates/clips/desktop/src/lib/recording-link.ts +67 -0
  187. package/corpus/templates/clips/desktop/src/overlays/live-audio-bars.tsx +35 -35
  188. package/corpus/templates/clips/desktop/src/overlays/live-transcript.tsx +3 -2
  189. package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +47 -46
  190. package/corpus/templates/clips/desktop/src/styles.css +127 -78
  191. package/corpus/templates/clips/desktop/src-tauri/Cargo.toml +7 -0
  192. package/corpus/templates/clips/desktop/src-tauri/Info.plist +6 -0
  193. package/corpus/templates/clips/desktop/src-tauri/capabilities/default.json +1 -0
  194. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +7 -3
  195. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +4 -1
  196. package/corpus/templates/clips/desktop/src-tauri/src/main.rs +8 -0
  197. package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +21 -10
  198. package/corpus/templates/clips/desktop/src-tauri/src/util.rs +40 -0
  199. package/corpus/templates/clips/server/db/schema.ts +27 -0
  200. package/corpus/templates/clips/server/lib/agent-views.ts +145 -0
  201. package/corpus/templates/clips/server/lib/post-finalize-dispatch.ts +2 -1
  202. package/corpus/templates/clips/server/lib/public-agent-context.ts +7 -0
  203. package/corpus/templates/clips/server/lib/recording-share-grant.ts +79 -0
  204. package/corpus/templates/clips/server/lib/recordings.ts +51 -3
  205. package/corpus/templates/clips/server/plugins/auth.ts +1 -1
  206. package/corpus/templates/clips/server/plugins/db.ts +31 -0
  207. package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +56 -3
  208. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +49 -17
  209. package/corpus/templates/clips/shared/recording-link.ts +43 -0
  210. package/corpus/templates/clips/shared/share-attribution.ts +4 -0
  211. package/corpus/templates/clips/shared/share-dashboard-redirect.ts +42 -0
  212. package/corpus/templates/clips/shared/view-analytics.ts +26 -0
  213. package/corpus/templates/content/.agents/skills/content/SKILL.md +5 -0
  214. package/corpus/templates/content/.agents/skills/content/references/local-file-mode.md +135 -0
  215. package/corpus/templates/content/.agents/skills/document-editing/SKILL.md +12 -0
  216. package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +238 -0
  217. package/corpus/templates/content/.agents/skills/document-editing/references/document-behavior.md +91 -0
  218. package/corpus/templates/content/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  219. package/corpus/templates/content/AGENTS.md +62 -583
  220. package/corpus/templates/content/_gitignore +1 -0
  221. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +1 -39
  222. package/corpus/templates/content/app/routes/_app._index.tsx +1 -2
  223. package/corpus/templates/content/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
  224. package/corpus/templates/content/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
  225. package/corpus/templates/content/package.json +1 -1
  226. package/corpus/templates/content/public/manifest.json +1 -1
  227. package/corpus/templates/content/server/plugins/auth.ts +1 -1
  228. package/corpus/templates/design/.agents/skills/creative-context/SKILL.md +11 -0
  229. package/corpus/templates/design/.agents/skills/design-generation/SKILL.md +50 -8
  230. package/corpus/templates/design/.agents/skills/design-generation/references/code-layers.md +81 -0
  231. package/corpus/templates/design/.agents/skills/design-generation/references/code-workspace.md +42 -0
  232. package/corpus/templates/design/.agents/skills/design-generation/references/editor-extensions.md +34 -0
  233. package/corpus/templates/design/.agents/skills/responsive-breakpoints/SKILL.md +4 -1
  234. package/corpus/templates/design/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  235. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +17 -0
  236. package/corpus/templates/design/.generated/bridge/editor-chrome.generated.ts +8196 -0
  237. package/corpus/templates/design/.generated/bridge/embedded-wheel.generated.ts +265 -0
  238. package/corpus/templates/design/.generated/bridge/hit-test.generated.ts +642 -0
  239. package/corpus/templates/design/.generated/bridge/motion-preview.generated.ts +571 -0
  240. package/corpus/templates/design/.generated/bridge/nav.generated.ts +91 -0
  241. package/corpus/templates/design/.generated/bridge/sample.generated.ts +23 -0
  242. package/corpus/templates/design/.generated/bridge/shader-fill-preview.generated.ts +263 -0
  243. package/corpus/templates/design/.generated/bridge/shader-runtime.generated.ts +518 -0
  244. package/corpus/templates/design/.generated/bridge/tweak.generated.ts +20 -0
  245. package/corpus/templates/design/.generated/bridge/zoom.generated.ts +32 -0
  246. package/corpus/templates/design/AGENTS.md +88 -503
  247. package/corpus/templates/design/_gitignore +1 -0
  248. package/corpus/templates/design/app/components/layout/Sidebar.tsx +0 -2
  249. package/corpus/templates/design/app/routes/_index.tsx +1 -1
  250. package/corpus/templates/design/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
  251. package/corpus/templates/design/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
  252. package/corpus/templates/design/server/plugins/auth.ts +1 -1
  253. package/corpus/templates/dispatch/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  254. package/corpus/templates/dispatch/_gitignore +1 -0
  255. package/corpus/templates/dispatch/app/dispatch-extensions.tsx +1 -10
  256. package/corpus/templates/dispatch/app/routes/overview.tsx +1 -1
  257. package/corpus/templates/dispatch/changelog/2026-07-24-signing-in-now-puts-you-in-your-companys-workspace.md +6 -0
  258. package/corpus/templates/dispatch/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
  259. package/corpus/templates/dispatch/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
  260. package/corpus/templates/dispatch/package.json +1 -1
  261. package/corpus/templates/dispatch/public/manifest.json +1 -1
  262. package/corpus/templates/forms/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  263. package/corpus/templates/forms/_gitignore +1 -0
  264. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +0 -35
  265. package/corpus/templates/forms/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
  266. package/corpus/templates/forms/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
  267. package/corpus/templates/forms/package.json +1 -1
  268. package/corpus/templates/forms/public/manifest.json +1 -1
  269. package/corpus/templates/forms/server/lib/public-form-ssr.ts +2 -2
  270. package/corpus/templates/forms/server/plugins/auth.ts +1 -1
  271. package/corpus/templates/macros/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  272. package/corpus/templates/macros/_gitignore +1 -0
  273. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +0 -2
  274. package/corpus/templates/macros/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
  275. package/corpus/templates/macros/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
  276. package/corpus/templates/macros/package.json +1 -1
  277. package/corpus/templates/macros/server/plugins/auth.ts +2 -2
  278. package/corpus/templates/mail/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  279. package/corpus/templates/mail/_gitignore +1 -0
  280. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +0 -39
  281. package/corpus/templates/mail/app/routes/$view.$threadId.tsx +1 -1
  282. package/corpus/templates/mail/app/routes/$view.tsx +1 -1
  283. package/corpus/templates/mail/app/routes/_index.tsx +1 -1
  284. package/corpus/templates/mail/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
  285. package/corpus/templates/mail/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
  286. package/corpus/templates/mail/package.json +1 -1
  287. package/corpus/templates/mail/public/manifest.json +1 -1
  288. package/corpus/templates/mail/server/plugins/auth.ts +1 -1
  289. package/corpus/templates/plan/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  290. package/corpus/templates/plan/_gitignore +1 -0
  291. package/corpus/templates/plan/agent-native.app-skill.json +1 -1
  292. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +4 -9
  293. package/corpus/templates/plan/app/lib/app-config.ts +1 -1
  294. package/corpus/templates/plan/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
  295. package/corpus/templates/plan/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
  296. package/corpus/templates/plan/package.json +1 -1
  297. package/corpus/templates/plan/public/manifest.json +1 -1
  298. package/corpus/templates/plan/server/plugins/auth.ts +1 -1
  299. package/corpus/templates/slides/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  300. package/corpus/templates/slides/_gitignore +1 -0
  301. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +0 -2
  302. package/corpus/templates/slides/app/routes/_index.tsx +1 -1
  303. package/corpus/templates/slides/changelog/2026-07-25-app-branding-now-uses-the-product-name-without-the-agent-nat.md +6 -0
  304. package/corpus/templates/slides/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
  305. package/corpus/templates/slides/package.json +1 -1
  306. package/corpus/templates/slides/public/manifest.json +1 -1
  307. package/corpus/templates/slides/server/plugins/auth.ts +1 -1
  308. package/corpus/templates/slides/server/plugins/db.ts +4 -0
  309. package/corpus/templates/tasks/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  310. package/corpus/templates/tasks/AGENTS.md +1 -1
  311. package/corpus/templates/tasks/_gitignore +1 -0
  312. package/dist/a2a/activity.d.ts +4 -1
  313. package/dist/a2a/activity.d.ts.map +1 -1
  314. package/dist/a2a/activity.js +27 -16
  315. package/dist/a2a/activity.js.map +1 -1
  316. package/dist/a2a/client.d.ts.map +1 -1
  317. package/dist/a2a/client.js +58 -4
  318. package/dist/a2a/client.js.map +1 -1
  319. package/dist/agent/engine/builder-engine.d.ts +1 -0
  320. package/dist/agent/engine/builder-engine.d.ts.map +1 -1
  321. package/dist/agent/engine/builder-engine.js +31 -4
  322. package/dist/agent/engine/builder-engine.js.map +1 -1
  323. package/dist/agent/run-manager.d.ts.map +1 -1
  324. package/dist/agent/run-manager.js +18 -1
  325. package/dist/agent/run-manager.js.map +1 -1
  326. package/dist/agent/run-store.d.ts +1 -0
  327. package/dist/agent/run-store.d.ts.map +1 -1
  328. package/dist/agent/run-store.js +2 -1
  329. package/dist/agent/run-store.js.map +1 -1
  330. package/dist/agent/types.d.ts +12 -1
  331. package/dist/agent/types.d.ts.map +1 -1
  332. package/dist/agent/types.js +21 -1
  333. package/dist/agent/types.js.map +1 -1
  334. package/dist/chat-threads/store.d.ts +10 -0
  335. package/dist/chat-threads/store.d.ts.map +1 -1
  336. package/dist/chat-threads/store.js +41 -0
  337. package/dist/chat-threads/store.js.map +1 -1
  338. package/dist/checkpoints/index.d.ts +1 -0
  339. package/dist/checkpoints/index.d.ts.map +1 -1
  340. package/dist/checkpoints/index.js +1 -0
  341. package/dist/checkpoints/index.js.map +1 -1
  342. package/dist/checkpoints/route-match.d.ts +8 -0
  343. package/dist/checkpoints/route-match.d.ts.map +1 -0
  344. package/dist/checkpoints/route-match.js +13 -0
  345. package/dist/checkpoints/route-match.js.map +1 -0
  346. package/dist/cli/create.d.ts +35 -4
  347. package/dist/cli/create.d.ts.map +1 -1
  348. package/dist/cli/create.js +69 -19
  349. package/dist/cli/create.js.map +1 -1
  350. package/dist/cli/index.js +19 -0
  351. package/dist/cli/index.js.map +1 -1
  352. package/dist/cli/template-baseline.d.ts +54 -0
  353. package/dist/cli/template-baseline.d.ts.map +1 -0
  354. package/dist/cli/template-baseline.js +334 -0
  355. package/dist/cli/template-baseline.js.map +1 -0
  356. package/dist/cli/template-sync.d.ts +57 -0
  357. package/dist/cli/template-sync.d.ts.map +1 -0
  358. package/dist/cli/template-sync.js +787 -0
  359. package/dist/cli/template-sync.js.map +1 -0
  360. package/dist/cli/templates-meta.d.ts.map +1 -1
  361. package/dist/cli/templates-meta.js +1 -0
  362. package/dist/cli/templates-meta.js.map +1 -1
  363. package/dist/client/AssistantChat.d.ts +2 -1
  364. package/dist/client/AssistantChat.d.ts.map +1 -1
  365. package/dist/client/AssistantChat.js +38 -10
  366. package/dist/client/AssistantChat.js.map +1 -1
  367. package/dist/client/NewWorkspaceAppFlow.d.ts.map +1 -1
  368. package/dist/client/NewWorkspaceAppFlow.js +32 -2
  369. package/dist/client/NewWorkspaceAppFlow.js.map +1 -1
  370. package/dist/client/chat/message-components.d.ts +20 -0
  371. package/dist/client/chat/message-components.d.ts.map +1 -1
  372. package/dist/client/chat/message-components.js +81 -46
  373. package/dist/client/chat/message-components.js.map +1 -1
  374. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  375. package/dist/client/chat/tool-call-display.js +14 -6
  376. package/dist/client/chat/tool-call-display.js.map +1 -1
  377. package/dist/client/error-format.d.ts.map +1 -1
  378. package/dist/client/error-format.js +13 -0
  379. package/dist/client/error-format.js.map +1 -1
  380. package/dist/client/org/OrgSwitcher.d.ts +8 -1
  381. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  382. package/dist/client/org/OrgSwitcher.js +3 -2
  383. package/dist/client/org/OrgSwitcher.js.map +1 -1
  384. package/dist/client/org/TeamPage.d.ts.map +1 -1
  385. package/dist/client/org/TeamPage.js +1 -1
  386. package/dist/client/org/TeamPage.js.map +1 -1
  387. package/dist/client/settings/SettingsPanel.js +9 -9
  388. package/dist/client/settings/SettingsPanel.js.map +1 -1
  389. package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
  390. package/dist/client/settings/SettingsTabsPage.js +7 -4
  391. package/dist/client/settings/SettingsTabsPage.js.map +1 -1
  392. package/dist/client/sse-event-processor.d.ts.map +1 -1
  393. package/dist/client/sse-event-processor.js +3 -0
  394. package/dist/client/sse-event-processor.js.map +1 -1
  395. package/dist/collab/awareness.d.ts +2 -2
  396. package/dist/collab/awareness.d.ts.map +1 -1
  397. package/dist/collab/struct-routes.d.ts +1 -1
  398. package/dist/data-widgets/index.d.ts +3 -0
  399. package/dist/data-widgets/index.d.ts.map +1 -1
  400. package/dist/data-widgets/index.js +33 -0
  401. package/dist/data-widgets/index.js.map +1 -1
  402. package/dist/deploy/build.d.ts.map +1 -1
  403. package/dist/deploy/build.js +11 -7
  404. package/dist/deploy/build.js.map +1 -1
  405. package/dist/eject/provider-api-definitions.d.ts +1 -0
  406. package/dist/eject/provider-api-definitions.d.ts.map +1 -1
  407. package/dist/extensions/url-safety.d.ts +9 -16
  408. package/dist/extensions/url-safety.d.ts.map +1 -1
  409. package/dist/extensions/url-safety.js +48 -4
  410. package/dist/extensions/url-safety.js.map +1 -1
  411. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  412. package/dist/integrations/webhook-handler.d.ts.map +1 -1
  413. package/dist/integrations/webhook-handler.js +46 -9
  414. package/dist/integrations/webhook-handler.js.map +1 -1
  415. package/dist/notifications/routes.d.ts +2 -2
  416. package/dist/observability/routes.d.ts +3 -3
  417. package/dist/org/auto-join-domain.d.ts +3 -2
  418. package/dist/org/auto-join-domain.d.ts.map +1 -1
  419. package/dist/org/auto-join-domain.js +1 -1
  420. package/dist/org/auto-join-domain.js.map +1 -1
  421. package/dist/org/context.d.ts.map +1 -1
  422. package/dist/org/context.js +84 -28
  423. package/dist/org/context.js.map +1 -1
  424. package/dist/org/index.d.ts +1 -0
  425. package/dist/org/index.d.ts.map +1 -1
  426. package/dist/org/index.js +1 -0
  427. package/dist/org/index.js.map +1 -1
  428. package/dist/org/service-identity.d.ts +43 -0
  429. package/dist/org/service-identity.d.ts.map +1 -0
  430. package/dist/org/service-identity.js +34 -0
  431. package/dist/org/service-identity.js.map +1 -0
  432. package/dist/provider-api/actions/custom-provider-registration.d.ts +14 -14
  433. package/dist/provider-api/actions/provider-api.d.ts +13 -11
  434. package/dist/provider-api/actions/provider-api.d.ts.map +1 -1
  435. package/dist/provider-api/index.d.ts +4 -0
  436. package/dist/provider-api/index.d.ts.map +1 -1
  437. package/dist/provider-api/index.js +17 -1
  438. package/dist/provider-api/index.js.map +1 -1
  439. package/dist/scripts/docs/search.d.ts.map +1 -1
  440. package/dist/scripts/docs/search.js +12 -1
  441. package/dist/scripts/docs/search.js.map +1 -1
  442. package/dist/server/agent-chat/context-tools.d.ts.map +1 -1
  443. package/dist/server/agent-chat/context-tools.js +3 -3
  444. package/dist/server/agent-chat/context-tools.js.map +1 -1
  445. package/dist/server/agent-chat/framework-prompts.d.ts +8 -3
  446. package/dist/server/agent-chat/framework-prompts.d.ts.map +1 -1
  447. package/dist/server/agent-chat/framework-prompts.js +33 -62
  448. package/dist/server/agent-chat/framework-prompts.js.map +1 -1
  449. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  450. package/dist/server/agent-chat-plugin.js +16 -10
  451. package/dist/server/agent-chat-plugin.js.map +1 -1
  452. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  453. package/dist/server/agents-bundle.d.ts +18 -0
  454. package/dist/server/agents-bundle.d.ts.map +1 -1
  455. package/dist/server/agents-bundle.js +70 -4
  456. package/dist/server/agents-bundle.js.map +1 -1
  457. package/dist/server/auth.d.ts.map +1 -1
  458. package/dist/server/auth.js +4 -3
  459. package/dist/server/auth.js.map +1 -1
  460. package/dist/server/builder-browser.d.ts.map +1 -1
  461. package/dist/server/builder-browser.js +43 -19
  462. package/dist/server/builder-browser.js.map +1 -1
  463. package/dist/server/core-routes-plugin.js +2 -2
  464. package/dist/server/core-routes-plugin.js.map +1 -1
  465. package/dist/server/credential-provider.d.ts +38 -2
  466. package/dist/server/credential-provider.d.ts.map +1 -1
  467. package/dist/server/credential-provider.js +142 -64
  468. package/dist/server/credential-provider.js.map +1 -1
  469. package/dist/server/index.d.ts +1 -1
  470. package/dist/server/index.d.ts.map +1 -1
  471. package/dist/server/index.js +1 -1
  472. package/dist/server/index.js.map +1 -1
  473. package/dist/server/prompts/framework-core-compact.d.ts +1 -1
  474. package/dist/server/prompts/framework-core-compact.d.ts.map +1 -1
  475. package/dist/server/prompts/framework-core-compact.js +7 -13
  476. package/dist/server/prompts/framework-core-compact.js.map +1 -1
  477. package/dist/server/prompts/framework-core.d.ts +1 -1
  478. package/dist/server/prompts/framework-core.d.ts.map +1 -1
  479. package/dist/server/prompts/framework-core.js +10 -21
  480. package/dist/server/prompts/framework-core.js.map +1 -1
  481. package/dist/server/prompts/index.d.ts +1 -1
  482. package/dist/server/prompts/index.d.ts.map +1 -1
  483. package/dist/server/prompts/index.js +1 -1
  484. package/dist/server/prompts/index.js.map +1 -1
  485. package/dist/server/prompts/shared-rules.d.ts +16 -10
  486. package/dist/server/prompts/shared-rules.d.ts.map +1 -1
  487. package/dist/server/prompts/shared-rules.js +19 -10
  488. package/dist/server/prompts/shared-rules.js.map +1 -1
  489. package/dist/server/ssr-handler.d.ts +1 -1
  490. package/dist/server/ssr-handler.d.ts.map +1 -1
  491. package/dist/server/ssr-handler.js +10 -3
  492. package/dist/server/ssr-handler.js.map +1 -1
  493. package/dist/server/transcribe-voice.d.ts +1 -1
  494. package/dist/settings/user-settings.d.ts.map +1 -1
  495. package/dist/settings/user-settings.js +13 -1
  496. package/dist/settings/user-settings.js.map +1 -1
  497. package/dist/shared/cache-control.d.ts +52 -0
  498. package/dist/shared/cache-control.d.ts.map +1 -1
  499. package/dist/shared/cache-control.js +107 -0
  500. package/dist/shared/cache-control.js.map +1 -1
  501. package/dist/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  502. package/dist/templates/chat/_gitignore +1 -0
  503. package/dist/templates/chat/app/components/layout/Sidebar.tsx +0 -7
  504. package/dist/templates/chat/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
  505. package/dist/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  506. package/dist/templates/default/app/root.tsx +1 -1
  507. package/dist/templates/default/public/manifest.json +2 -2
  508. package/dist/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  509. package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +19 -0
  510. package/dist/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  511. package/dist/templates/workspace-core/.agents/skills/visual-answer/SKILL.md +1 -0
  512. package/dist/vite/agents-bundle-plugin.d.ts.map +1 -1
  513. package/dist/vite/agents-bundle-plugin.js +8 -7
  514. package/dist/vite/agents-bundle-plugin.js.map +1 -1
  515. package/dist/vite/client.d.ts.map +1 -1
  516. package/dist/vite/client.js +66 -1
  517. package/dist/vite/client.js.map +1 -1
  518. package/docs/content/actions.mdx +2 -2
  519. package/docs/content/agent-surfaces.mdx +1 -1
  520. package/docs/content/authentication.mdx +2 -0
  521. package/docs/content/deployment.mdx +36 -0
  522. package/docs/content/http-api.mdx +266 -0
  523. package/docs/content/syncing-template-changes.mdx +159 -0
  524. package/package.json +4 -3
  525. package/src/a2a/activity.ts +38 -27
  526. package/src/a2a/client.ts +57 -4
  527. package/src/agent/engine/builder-engine.ts +35 -4
  528. package/src/agent/run-manager.ts +20 -1
  529. package/src/agent/run-store.ts +4 -1
  530. package/src/agent/types.ts +28 -8
  531. package/src/chat-threads/store.ts +45 -0
  532. package/src/checkpoints/index.ts +2 -0
  533. package/src/checkpoints/route-match.ts +13 -0
  534. package/src/cli/create.ts +105 -17
  535. package/src/cli/index.ts +20 -0
  536. package/src/cli/template-baseline.ts +410 -0
  537. package/src/cli/template-sync.ts +1004 -0
  538. package/src/cli/templates-meta.ts +1 -0
  539. package/src/client/AssistantChat.tsx +38 -12
  540. package/src/client/NewWorkspaceAppFlow.tsx +75 -10
  541. package/src/client/chat/message-components.tsx +127 -66
  542. package/src/client/chat/tool-call-display.tsx +29 -5
  543. package/src/client/error-format.ts +18 -0
  544. package/src/client/org/OrgSwitcher.tsx +30 -8
  545. package/src/client/org/TeamPage.tsx +7 -6
  546. package/src/client/settings/SettingsPanel.tsx +9 -9
  547. package/src/client/settings/SettingsTabsPage.tsx +12 -2
  548. package/src/client/sse-event-processor.ts +3 -0
  549. package/src/data-widgets/index.ts +41 -0
  550. package/src/deploy/build.ts +11 -9
  551. package/src/extensions/url-safety.ts +67 -4
  552. package/src/integrations/webhook-handler.ts +63 -9
  553. package/src/org/auto-join-domain.ts +4 -3
  554. package/src/org/context.ts +108 -39
  555. package/src/org/index.ts +5 -0
  556. package/src/org/service-identity.ts +60 -0
  557. package/src/provider-api/index.ts +22 -1
  558. package/src/scripts/docs/search.ts +12 -1
  559. package/src/server/__snapshots__/agent-chat-plugin.surface.spec.ts.snap +25 -36
  560. package/src/server/agent-chat/context-tools.ts +9 -4
  561. package/src/server/agent-chat/framework-prompts.ts +38 -66
  562. package/src/server/agent-chat-plugin.ts +17 -10
  563. package/src/server/agents-bundle.ts +84 -5
  564. package/src/server/auth.ts +4 -3
  565. package/src/server/builder-browser.ts +47 -19
  566. package/src/server/core-routes-plugin.ts +2 -2
  567. package/src/server/credential-provider.ts +209 -68
  568. package/src/server/index.ts +3 -0
  569. package/src/server/prompts/framework-core-compact.ts +6 -13
  570. package/src/server/prompts/framework-core.ts +9 -21
  571. package/src/server/prompts/index.ts +0 -1
  572. package/src/server/prompts/shared-rules.ts +19 -11
  573. package/src/server/ssr-handler.ts +13 -2
  574. package/src/settings/user-settings.ts +13 -1
  575. package/src/shared/cache-control.ts +141 -0
  576. package/src/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  577. package/src/templates/chat/_gitignore +1 -0
  578. package/src/templates/chat/app/components/layout/Sidebar.tsx +0 -7
  579. package/src/templates/chat/changelog/2026-07-25-settings-navigation-now-keeps-manage-agent-as-a-dedicated-li.md +6 -0
  580. package/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  581. package/src/templates/default/app/root.tsx +1 -1
  582. package/src/templates/default/public/manifest.json +2 -2
  583. package/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  584. package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +19 -0
  585. package/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +24 -2
  586. package/src/templates/workspace-core/.agents/skills/visual-answer/SKILL.md +1 -0
  587. package/src/vite/agents-bundle-plugin.ts +7 -6
  588. package/src/vite/client.ts +69 -3
@@ -166,6 +166,8 @@ export const recordings = table("recordings", {
166
166
  .default("uploading"),
167
167
  uploadProgress: integer("upload_progress").notNull().default(0),
168
168
  failureReason: text("failure_reason"),
169
+ loomImportClaimId: text("loom_import_claim_id"),
170
+ loomImportClaimedAt: text("loom_import_claimed_at"),
169
171
 
170
172
  // Non-destructive edits: JSON `{ trims: [{startMs,endMs,excluded}], blurs: [...], speed: [...] }`
171
173
  editsJson: text("edits_json").notNull().default("{}"),
@@ -382,6 +384,31 @@ export const recordingViews = table("recording_views", {
382
384
  viewedAt: text("viewed_at").notNull().default(now()),
383
385
  });
384
386
 
387
+ // Agent views — one row per (clip, agent, time bucket). Deliberately separate
388
+ // from `recording_viewers` / `recording_views` so no human-view count can ever
389
+ // pick agents up by forgetting a filter: the human tables stay agent-free.
390
+ export const recordingAgentViews = table(
391
+ "recording_agent_views",
392
+ {
393
+ id: text("id").primaryKey(),
394
+ recordingId: text("recording_id").notNull(),
395
+ // sha256 of user-agent + request IP. Never stores the raw IP.
396
+ agentKey: text("agent_key").notNull(),
397
+ agentLabel: text("agent_label"),
398
+ // Time bucket that collapses one agent's burst of context/transcript/frame
399
+ // polls into a single view.
400
+ viewSessionId: text("view_session_id").notNull(),
401
+ firstSeenAt: text("first_seen_at").notNull().default(now()),
402
+ lastSeenAt: text("last_seen_at").notNull().default(now()),
403
+ requestCount: integer("request_count").notNull().default(1),
404
+ },
405
+ (view) => ({
406
+ recordingAgentViewSessionUnique: uniqueIndex(
407
+ "recording_agent_views_session_unique_idx",
408
+ ).on(view.recordingId, view.agentKey, view.viewSessionId),
409
+ }),
410
+ );
411
+
385
412
  // -----------------------------------------------------------------------------
386
413
  // Meetings (Granola-style) — recording + transcript + AI notes anchored to
387
414
  // a calendar event or an ad-hoc meeting block. Composes with the existing
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Agent views — counting when an *outside agent* reads a clip through the
3
+ * public agent APIs (`/api/agent-context.json`, `/api/agent-transcript.json`,
4
+ * `/api/agent-frame.jpg`), as opposed to a human opening the player.
5
+ *
6
+ * Those routes are agent-only surfaces: a human watching a clip never hits
7
+ * them, so a request on one is the signal. Counts live in their own table so
8
+ * every existing human-view query stays agent-free by construction.
9
+ */
10
+
11
+ import { createHash } from "node:crypto";
12
+
13
+ import { count, desc, eq, sql } from "drizzle-orm";
14
+ import { getRequestHeader, getRequestIP, type H3Event } from "h3";
15
+
16
+ import { getDb, schema } from "../db/index.js";
17
+ import { nanoid } from "./recordings.js";
18
+
19
+ /**
20
+ * One agent's burst of context + transcript + frame polls is one view.
21
+ * Fixed windows mean a burst straddling a boundary counts twice; switch to
22
+ * last-seen-gap dedup if that ever shows up in the numbers.
23
+ */
24
+ export const AGENT_VIEW_SESSION_MS = 30 * 60 * 1000;
25
+
26
+ const AGENT_LABELS: [RegExp, string][] = [
27
+ [/claude|anthropic/i, "Claude"],
28
+ [/chatgpt|gptbot|oai-searchbot|openai/i, "ChatGPT"],
29
+ [/perplexity/i, "Perplexity"],
30
+ [/gemini|google-extended|googleother/i, "Gemini"],
31
+ [/copilot/i, "Copilot"],
32
+ [/cursor/i, "Cursor"],
33
+ [/bingbot|bingpreview/i, "Bing"],
34
+ [/applebot/i, "Apple Intelligence"],
35
+ [/meta-external|facebookbot/i, "Meta AI"],
36
+ [/bytespider|amazonbot|ccbot|diffbot|youbot/i, "Crawler"],
37
+ ];
38
+
39
+ export const UNKNOWN_AGENT_LABEL = "Agent";
40
+
41
+ export function agentLabelFromUserAgent(userAgent: string): string {
42
+ for (const [pattern, label] of AGENT_LABELS) {
43
+ if (pattern.test(userAgent)) return label;
44
+ }
45
+ return UNKNOWN_AGENT_LABEL;
46
+ }
47
+
48
+ export function agentViewSessionId(
49
+ now: number,
50
+ bucketMs = AGENT_VIEW_SESSION_MS,
51
+ ): string {
52
+ return String(Math.floor(now / bucketMs));
53
+ }
54
+
55
+ /** Hashed so an agent is distinguishable across polls without storing its IP. */
56
+ export function agentKeyFor(userAgent: string, ip: string): string {
57
+ return createHash("sha256")
58
+ .update(`${userAgent}|${ip}`)
59
+ .digest("hex")
60
+ .slice(0, 32);
61
+ }
62
+
63
+ /**
64
+ * Best-effort: never let view accounting fail an agent's read of a clip.
65
+ */
66
+ export async function recordAgentView(
67
+ event: H3Event,
68
+ recordingId: string,
69
+ nowMs: number = Date.now(),
70
+ ): Promise<void> {
71
+ try {
72
+ const userAgent = (getRequestHeader(event, "user-agent") ?? "").slice(
73
+ 0,
74
+ 512,
75
+ );
76
+ const ip = getRequestIP(event) || "unknown";
77
+ const now = new Date(nowMs).toISOString();
78
+
79
+ await getDb()
80
+ .insert(schema.recordingAgentViews)
81
+ .values({
82
+ id: nanoid(),
83
+ recordingId,
84
+ agentKey: agentKeyFor(userAgent, ip),
85
+ agentLabel: agentLabelFromUserAgent(userAgent),
86
+ viewSessionId: agentViewSessionId(nowMs),
87
+ firstSeenAt: now,
88
+ lastSeenAt: now,
89
+ requestCount: 1,
90
+ })
91
+ .onConflictDoUpdate({
92
+ target: [
93
+ schema.recordingAgentViews.recordingId,
94
+ schema.recordingAgentViews.agentKey,
95
+ schema.recordingAgentViews.viewSessionId,
96
+ ],
97
+ set: {
98
+ lastSeenAt: now,
99
+ requestCount: sql`${schema.recordingAgentViews.requestCount} + 1`,
100
+ },
101
+ });
102
+ } catch (err) {
103
+ console.warn("[agent-views] failed to record agent view:", err);
104
+ }
105
+ }
106
+
107
+ export async function countRecordingAgentViews(
108
+ recordingId: string,
109
+ ): Promise<number> {
110
+ const [row] = await getDb()
111
+ .select({ value: count() })
112
+ .from(schema.recordingAgentViews)
113
+ .where(eq(schema.recordingAgentViews.recordingId, recordingId));
114
+ return Number(row?.value ?? 0);
115
+ }
116
+
117
+ export interface AgentViewerSummary {
118
+ agentLabel: string;
119
+ views: number;
120
+ lastSeenAt: string;
121
+ }
122
+
123
+ /** Agents that read this clip, most recent first, grouped by product label. */
124
+ export async function listRecordingAgentViewers(
125
+ recordingId: string,
126
+ limit = 8,
127
+ ): Promise<AgentViewerSummary[]> {
128
+ const rows = await getDb()
129
+ .select({
130
+ agentLabel: schema.recordingAgentViews.agentLabel,
131
+ views: count(),
132
+ lastSeenAt: sql<string>`MAX(${schema.recordingAgentViews.lastSeenAt})`,
133
+ })
134
+ .from(schema.recordingAgentViews)
135
+ .where(eq(schema.recordingAgentViews.recordingId, recordingId))
136
+ .groupBy(schema.recordingAgentViews.agentLabel)
137
+ .orderBy(desc(sql`MAX(${schema.recordingAgentViews.lastSeenAt})`))
138
+ .limit(limit);
139
+
140
+ return rows.map((r) => ({
141
+ agentLabel: r.agentLabel || UNKNOWN_AGENT_LABEL,
142
+ views: Number(r.views ?? 0),
143
+ lastSeenAt: r.lastSeenAt,
144
+ }));
145
+ }
@@ -11,7 +11,8 @@ export type PostFinalizeJobKind =
11
11
  | "media-ready"
12
12
  | "seekable"
13
13
  | "transcript"
14
- | "brain-export";
14
+ | "brain-export"
15
+ | "loom-import";
15
16
 
16
17
  export const POST_FINALIZE_JOB_TOKEN_KIND = "clips-post-finalize-job";
17
18
 
@@ -31,6 +31,7 @@ import {
31
31
  } from "../../shared/transcript-segments.js";
32
32
  import { resolveTranscriptPresentation } from "../../shared/transcript-status.js";
33
33
  import { getDb, schema } from "../db/index.js";
34
+ import { recordAgentView } from "./agent-views.js";
34
35
  import { verifySharePassword } from "./share-password.js";
35
36
 
36
37
  export type PublicAgentRecording = typeof schema.recordings._.inferSelect;
@@ -290,6 +291,12 @@ export async function loadPublicAgentAccess(
290
291
  }
291
292
  }
292
293
 
294
+ // Every agent API route funnels through here, so this is the one place that
295
+ // sees an outside agent read a clip. Owner requests are previews, not views.
296
+ if (!viewerIsOwner) {
297
+ await recordAgentView(event, recording.id);
298
+ }
299
+
293
300
  return {
294
301
  ok: true,
295
302
  access: {
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Explicit share-grant lookup for the direct `/r/:id` recording page.
3
+ *
4
+ * Lives beside `recording-page-access.ts` rather than inside it so that module
5
+ * stays free of database imports and their registration side effects. Every
6
+ * surface that decides whether a viewer may open the authenticated recording
7
+ * page — the player action and the public share endpoint that auto-redirects
8
+ * into it — must use this helper, or the share page and the page it redirects
9
+ * to will disagree and ping-pong.
10
+ */
11
+
12
+ import {
13
+ getRequestOrgId,
14
+ getRequestUserEmail,
15
+ } from "@agent-native/core/server/request-context";
16
+ import { and, eq, or, sql } from "drizzle-orm";
17
+
18
+ import { getDb, schema } from "../db/index.js";
19
+ import type { RecordingPageAccessRole } from "./recording-page-access.js";
20
+ import type { RecordingVisibility } from "./recordings.js";
21
+
22
+ export interface RecordingShareGrantInput {
23
+ recordingId: string;
24
+ role: RecordingPageAccessRole;
25
+ visibility: RecordingVisibility;
26
+ hasPassword: boolean;
27
+ /** Agent/MCP/A2A callers, which may open password-less public clips. */
28
+ isAgentCaller?: boolean;
29
+ /** Omit to read the ambient request context. */
30
+ userEmail?: string | null;
31
+ /** Omit to read the ambient request context. */
32
+ orgId?: string | null;
33
+ }
34
+
35
+ export async function hasExplicitRecordingShare(
36
+ input: RecordingShareGrantInput,
37
+ ): Promise<boolean> {
38
+ if (input.role === "owner") return true;
39
+ if (input.visibility !== "public") return false;
40
+ if (!input.hasPassword && input.isAgentCaller) return true;
41
+
42
+ const userEmail = (
43
+ input.userEmail === undefined ? getRequestUserEmail() : input.userEmail
44
+ )
45
+ ?.trim()
46
+ .toLowerCase();
47
+ const orgId = input.orgId === undefined ? getRequestOrgId() : input.orgId;
48
+
49
+ const principals = [];
50
+ if (userEmail) {
51
+ principals.push(
52
+ and(
53
+ eq(schema.recordingShares.principalType, "user"),
54
+ sql`lower(${schema.recordingShares.principalId}) = ${userEmail}`,
55
+ ),
56
+ );
57
+ }
58
+ if (orgId) {
59
+ principals.push(
60
+ and(
61
+ eq(schema.recordingShares.principalType, "org"),
62
+ eq(schema.recordingShares.principalId, orgId),
63
+ ),
64
+ );
65
+ }
66
+ if (principals.length === 0) return false;
67
+
68
+ const [share] = await getDb()
69
+ .select({ id: schema.recordingShares.id })
70
+ .from(schema.recordingShares)
71
+ .where(
72
+ and(
73
+ eq(schema.recordingShares.resourceId, input.recordingId),
74
+ or(...principals),
75
+ ),
76
+ )
77
+ .limit(1);
78
+ return Boolean(share);
79
+ }
@@ -1,11 +1,11 @@
1
1
  import { readAppState } from "@agent-native/core/application-state";
2
- import { orgMembers } from "@agent-native/core/org";
2
+ import { implicitServiceOrgRole, orgMembers } from "@agent-native/core/org";
3
3
  import { getSession } from "@agent-native/core/server";
4
4
  import {
5
5
  getRequestUserEmail,
6
6
  getRequestOrgId,
7
7
  } from "@agent-native/core/server/request-context";
8
- import { and, desc, eq, sql } from "drizzle-orm";
8
+ import { and, count, desc, eq, sql } from "drizzle-orm";
9
9
  import { HTTPError, type H3Event } from "h3";
10
10
 
11
11
  import { getDb, schema } from "../db/index.js";
@@ -142,7 +142,11 @@ export async function getOrganizationRoleForEmail(
142
142
  // org_members table may not exist yet on first boot before migrations finish.
143
143
  }
144
144
 
145
- return null;
145
+ return implicitServiceOrgRole({
146
+ email,
147
+ orgId: organizationId,
148
+ requestOrgId: getRequestOrgId(),
149
+ });
146
150
  }
147
151
 
148
152
  export async function requireOrganizationAccess(
@@ -362,3 +366,47 @@ export function shouldCountView(
362
366
  ): boolean {
363
367
  return totalWatchMs >= 5000 || completedPct >= 75 || scrubbedToEnd;
364
368
  }
369
+
370
+ /**
371
+ * The single definition of a counted *viewer*: one `recording_viewers` row
372
+ * whose `countedView` flag is set. That is one row per person, so it answers
373
+ * "how many distinct viewers", not "how many views" — use
374
+ * `countRecordingViews` for the total. The in-memory twin is
375
+ * `isCountedViewerRow` in `shared/view-analytics.ts`.
376
+ */
377
+ export function countedViewCondition() {
378
+ return eq(schema.recordingViewers.countedView, true);
379
+ }
380
+
381
+ /**
382
+ * Total views for a recording: one per counted view *session*, so a returning
383
+ * viewer's second visit counts again. Every surface that reports a view count
384
+ * (library list, insights, player, public share page) goes through this.
385
+ */
386
+ export async function countRecordingViews(
387
+ recordingId: string,
388
+ ): Promise<number> {
389
+ const db = getDb();
390
+ const [viewerRow] = await db
391
+ .select({ value: count() })
392
+ .from(schema.recordingViewers)
393
+ .where(
394
+ and(
395
+ eq(schema.recordingViewers.recordingId, recordingId),
396
+ countedViewCondition(),
397
+ ),
398
+ );
399
+ const [viewLogRow] = await db
400
+ .select({ value: count() })
401
+ .from(schema.recordingViews)
402
+ .where(eq(schema.recordingViews.recordingId, recordingId));
403
+
404
+ // `recording_views` only exists from migration v46, so clips recorded before
405
+ // it have zero log rows. Floor the total at the counted-viewer count so those
406
+ // clips keep reporting a real number instead of dropping to 0, and so the
407
+ // total can never read below the unique-viewer count beside it.
408
+ return Math.max(
409
+ Number(viewLogRow?.value ?? 0),
410
+ Number(viewerRow?.value ?? 0),
411
+ );
412
+ }
@@ -7,7 +7,7 @@ export default createAuthPlugin({
7
7
  // callback can handle both normal sign-in and the Calendar connect flow.
8
8
  mountGoogleOAuthRoutes: false,
9
9
  marketing: {
10
- appName: "Agent-Native Clips",
10
+ appName: "Clips",
11
11
  tagline:
12
12
  "Your AI agent transcribes, summarizes, and searches everything you record alongside you.",
13
13
  features: [
@@ -850,6 +850,37 @@ const migrations = runMigrations(
850
850
  `UPDATE organization_settings SET default_visibility = 'public' WHERE default_visibility = 'private' AND updated_at = created_at`,
851
851
  ].join("; "),
852
852
  },
853
+ // -------------------------------------------------------------------------
854
+ // Agent views — external agents polling a public clip's agent context,
855
+ // transcript, or frame APIs. Kept in its own table so human view counts
856
+ // cannot accidentally include agents.
857
+ // -------------------------------------------------------------------------
858
+ {
859
+ version: 51,
860
+ name: "recording-agent-views",
861
+ sql: [
862
+ `CREATE TABLE IF NOT EXISTS recording_agent_views (
863
+ id TEXT PRIMARY KEY,
864
+ recording_id TEXT NOT NULL,
865
+ agent_key TEXT NOT NULL,
866
+ agent_label TEXT,
867
+ view_session_id TEXT NOT NULL,
868
+ first_seen_at TEXT NOT NULL DEFAULT (datetime('now')),
869
+ last_seen_at TEXT NOT NULL DEFAULT (datetime('now')),
870
+ request_count INTEGER NOT NULL DEFAULT 1
871
+ )`,
872
+ `CREATE UNIQUE INDEX IF NOT EXISTS recording_agent_views_session_unique_idx ON recording_agent_views (recording_id, agent_key, view_session_id)`,
873
+ `CREATE INDEX IF NOT EXISTS recording_agent_views_recording_idx ON recording_agent_views (recording_id, last_seen_at)`,
874
+ ].join("; "),
875
+ },
876
+ {
877
+ version: 52,
878
+ name: "recording-loom-import-claim-lease",
879
+ sql: [
880
+ `ALTER TABLE recordings ADD COLUMN IF NOT EXISTS loom_import_claim_id TEXT`,
881
+ `ALTER TABLE recordings ADD COLUMN IF NOT EXISTS loom_import_claimed_at TEXT`,
882
+ ].join("; "),
883
+ },
853
884
  ],
854
885
  { table: "clips_migrations" },
855
886
  );
@@ -1,8 +1,10 @@
1
+ import { randomUUID } from "node:crypto";
2
+
1
3
  import {
2
4
  runWithRequestContext,
3
5
  verifyScopedAgentAccessToken,
4
6
  } from "@agent-native/core/server";
5
- import { eq } from "drizzle-orm";
7
+ import { and, eq, isNull, lt, or } from "drizzle-orm";
6
8
  import {
7
9
  defineEventHandler,
8
10
  readBody,
@@ -14,6 +16,7 @@ import { z } from "zod";
14
16
  import exportToBrain from "../../../../actions/export-to-brain.js";
15
17
  import finalizeRecording from "../../../../actions/finalize-recording.js";
16
18
  import { ensureRecordingSeekable } from "../../../../actions/lib/ensure-seekable-video.js";
19
+ import { runLoomImportJob } from "../../../../actions/lib/loom-import-job.js";
17
20
  import requestTranscript from "../../../../actions/request-transcript.js";
18
21
  import { getDb, schema } from "../../../db/index.js";
19
22
  import {
@@ -24,13 +27,21 @@ import {
24
27
 
25
28
  const bodySchema = z.object({
26
29
  recordingId: z.string().min(1).max(200),
27
- kind: z.enum(["media-ready", "seekable", "transcript", "brain-export"]),
30
+ kind: z.enum([
31
+ "media-ready",
32
+ "seekable",
33
+ "transcript",
34
+ "brain-export",
35
+ "loom-import",
36
+ ]),
28
37
  token: z.string().min(1),
29
38
  delayMs: z.number().int().min(0).max(30_000).optional(),
30
39
  retryAttempt: z.number().int().min(1).max(10).optional(),
31
40
  regenerate: z.boolean().optional(),
32
41
  });
33
42
 
43
+ const LOOM_IMPORT_LEASE_MS = 30 * 60 * 1000;
44
+
34
45
  export default defineEventHandler(async (event: H3Event) => {
35
46
  const parsed = bodySchema.safeParse(await readBody(event).catch(() => null));
36
47
  if (!parsed.success) {
@@ -63,7 +74,8 @@ export default defineEventHandler(async (event: H3Event) => {
63
74
  setResponseStatus(event, 404);
64
75
  return { ok: false, error: "Recording not found" };
65
76
  }
66
- const requiredStatus = kind === "media-ready" ? "processing" : "ready";
77
+ const requiredStatus =
78
+ kind === "media-ready" || kind === "loom-import" ? "processing" : "ready";
67
79
  if (recording.status !== requiredStatus) {
68
80
  return {
69
81
  ok: true,
@@ -112,6 +124,47 @@ export default defineEventHandler(async (event: H3Event) => {
112
124
  return { ok: true, kind, result };
113
125
  }
114
126
 
127
+ if (kind === "loom-import") {
128
+ const claimId = randomUUID();
129
+ const claimStartedAt = new Date().toISOString();
130
+ const claimExpiredBefore = new Date(
131
+ Date.now() - LOOM_IMPORT_LEASE_MS,
132
+ ).toISOString();
133
+ const [claimed] = await getDb()
134
+ .update(schema.recordings)
135
+ .set({
136
+ loomImportClaimId: claimId,
137
+ loomImportClaimedAt: claimStartedAt,
138
+ })
139
+ .where(
140
+ and(
141
+ eq(schema.recordings.id, recordingId),
142
+ eq(schema.recordings.status, "processing"),
143
+ or(
144
+ isNull(schema.recordings.loomImportClaimId),
145
+ isNull(schema.recordings.loomImportClaimedAt),
146
+ lt(schema.recordings.loomImportClaimedAt, claimExpiredBefore),
147
+ ),
148
+ ),
149
+ )
150
+ .returning({ id: schema.recordings.id });
151
+ if (!claimed) {
152
+ return {
153
+ ok: true,
154
+ recordingId,
155
+ kind,
156
+ skipped: true,
157
+ reason: "loom-import-already-running",
158
+ };
159
+ }
160
+ const result = await runLoomImportJob({
161
+ recordingId,
162
+ ownerEmail: recording.ownerEmail,
163
+ claimId,
164
+ });
165
+ return { ok: true, kind, result };
166
+ }
167
+
115
168
  if (kind === "brain-export") {
116
169
  const result = await exportToBrain.run({
117
170
  recordingId,
@@ -46,8 +46,16 @@ import { isMediaVerificationPending } from "../../lib/media-verification-state.j
46
46
  import { resolvePlayerThumbnailUrl } from "../../lib/player-thumbnail-url.js";
47
47
  import { resolvePlayerVideoUrl } from "../../lib/player-video-url.js";
48
48
  import {
49
+ canOpenDirectRecordingPage,
50
+ isRecordingExpired,
51
+ type RecordingPageAccessRole,
52
+ } from "../../lib/recording-page-access.js";
53
+ import { hasExplicitRecordingShare } from "../../lib/recording-share-grant.js";
54
+ import {
55
+ countRecordingViews,
49
56
  getOrganizationRoleForEmail,
50
57
  parseSpaceIds,
58
+ type RecordingVisibility,
51
59
  } from "../../lib/recordings.js";
52
60
  import { verifySharePassword } from "../../lib/share-password.js";
53
61
 
@@ -258,12 +266,10 @@ export default defineEventHandler(async (event) => {
258
266
  }
259
267
 
260
268
  // Expiry check
261
- if (rec.expiresAt) {
262
- const expires = new Date(rec.expiresAt).getTime();
263
- if (isFinite(expires) && expires < Date.now()) {
264
- setResponseStatus(event, 410);
265
- return { error: "Recording has expired", expired: true };
266
- }
269
+ const recordingExpired = isRecordingExpired(rec.expiresAt);
270
+ if (recordingExpired) {
271
+ setResponseStatus(event, 410);
272
+ return { error: "Recording has expired", expired: true };
267
273
  }
268
274
 
269
275
  // Password check
@@ -405,6 +411,32 @@ export default defineEventHandler(async (event) => {
405
411
  recordingStatus: rec.status,
406
412
  });
407
413
 
414
+ const viewCount = await countRecordingViews(recordingId);
415
+
416
+ const viewerRole =
417
+ viewerAccess?.role ?? (viewerIsOrgMember ? "viewer" : null);
418
+ // Mirrors the gate in `get-recording-player-data` exactly: the share page
419
+ // auto-redirects on this flag, so a false positive bounces the viewer
420
+ // between /share/:id and /r/:id forever. Only a resolved access role can
421
+ // open the direct page — the org-member fallback above is a display role,
422
+ // not access the player action would grant.
423
+ const canOpenDashboard =
424
+ Boolean(session?.email) && viewerAccess && !recordingExpired
425
+ ? canOpenDirectRecordingPage({
426
+ role: viewerAccess.role as RecordingPageAccessRole,
427
+ visibility: rec.visibility as RecordingVisibility,
428
+ hasPassword: Boolean(rec.password),
429
+ hasExplicitShare: await hasExplicitRecordingShare({
430
+ recordingId,
431
+ role: viewerAccess.role as RecordingPageAccessRole,
432
+ visibility: rec.visibility as RecordingVisibility,
433
+ hasPassword: Boolean(rec.password),
434
+ userEmail: session?.email ?? null,
435
+ orgId: session?.orgId ?? null,
436
+ }),
437
+ })
438
+ : false;
439
+
408
440
  return {
409
441
  recording: {
410
442
  id: rec.id,
@@ -439,6 +471,8 @@ export default defineEventHandler(async (event) => {
439
471
  updatedAt: rec.updatedAt,
440
472
  },
441
473
  agentContextUrl,
474
+ // Aggregate count only — never viewer identities on this public payload.
475
+ viewCount,
442
476
  transcript: transcript
443
477
  ? {
444
478
  status: transcriptPresentation.status,
@@ -479,17 +513,15 @@ export default defineEventHandler(async (event) => {
479
513
  placement: c.placement,
480
514
  })),
481
515
  viewer: session?.email
482
- ? (() => {
483
- const role =
484
- viewerAccess?.role ?? (viewerIsOrgMember ? "viewer" : null);
485
- const canEdit =
486
- role === "owner" || role === "admin" || role === "editor";
487
- return {
488
- canEdit,
489
- isOwner: role === "owner",
490
- role: role ?? "viewer",
491
- };
492
- })()
516
+ ? {
517
+ canEdit:
518
+ viewerRole === "owner" ||
519
+ viewerRole === "admin" ||
520
+ viewerRole === "editor",
521
+ isOwner: viewerRole === "owner",
522
+ role: viewerRole ?? "viewer",
523
+ canOpenDashboard,
524
+ }
493
525
  : null,
494
526
  };
495
527
  });
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Canonical share URL for a recording, shared by every surface that hands a
3
+ * user a link to paste: the web recorder, the desktop app, and the Chrome
4
+ * extension.
5
+ *
6
+ * `/share/<id>` is the public viewer page — SSR-rendered, password/expiry
7
+ * aware, and the shape Slack unfurls. `/r/<id>` is the owner dashboard: it
8
+ * renders client-side only and shows a sign-in prompt to a recipient. Copying
9
+ * `/r/<id>` produces a link that works for the author and looks broken to
10
+ * everyone they send it to, so share flows must build URLs from here rather
11
+ * than hand-rolling a path.
12
+ */
13
+ import { withShareAttribution } from "./share-attribution";
14
+
15
+ /** Public share path for a recording, relative to the app base path. */
16
+ export function recordingSharePath(recordingId: string): string {
17
+ return `/share/${encodeURIComponent(recordingId)}`;
18
+ }
19
+
20
+ export interface RecordingShareUrlParams {
21
+ recordingId: string;
22
+ /** Absolute origin, e.g. `https://clips.example.com` or a desktop serverUrl. */
23
+ origin: string;
24
+ /** App base path when the app is mounted under a subpath. */
25
+ basePath?: string;
26
+ /** Non-PII owner id for viral attribution. Omitted when unknown. */
27
+ ownerId?: string | null;
28
+ }
29
+
30
+ /**
31
+ * Absolute, ready-to-paste share URL for a recording, carrying the same
32
+ * attribution params the Share dialog mints so auto-copied links measure the
33
+ * signup funnel identically.
34
+ */
35
+ export function buildRecordingShareUrl(
36
+ params: RecordingShareUrlParams,
37
+ ): string {
38
+ const { recordingId, origin, basePath = "", ownerId } = params;
39
+ const trimmedOrigin = origin.trim().replace(/\/+$/, "");
40
+ const trimmedBase = basePath.trim().replace(/\/+$/, "");
41
+ const url = `${trimmedOrigin}${trimmedBase}${recordingSharePath(recordingId)}`;
42
+ return withShareAttribution(url, ownerId);
43
+ }
@@ -21,6 +21,10 @@ export const CLIP_SHARE_REF = "clip_share";
21
21
  export const REF_PARAM = "ref";
22
22
  export const VIA_PARAM = "via";
23
23
 
24
+ /** Marker used only when `/r/:id` falls back to the public share route. */
25
+ export const DASHBOARD_REDIRECT_PARAM = "dashboard_redirect";
26
+ export const DASHBOARD_REDIRECT_VALUE = "1";
27
+
24
28
  /**
25
29
  * Append `ref=clip_share` (and `via=<ownerId>` when a non-PII owner id is
26
30
  * known) to an absolute share/embed URL, preserving any existing query params.