@agent-native/core 0.79.27 → 0.80.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 (497) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +34 -0
  3. package/corpus/core/docs/content/locales/ar-SA/skills-guide.mdx +5 -0
  4. package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +55 -1
  5. package/corpus/core/docs/content/locales/de-DE/skills-guide.mdx +5 -0
  6. package/corpus/core/docs/content/locales/de-DE/template-design.mdx +66 -1
  7. package/corpus/core/docs/content/locales/es-ES/skills-guide.mdx +5 -0
  8. package/corpus/core/docs/content/locales/es-ES/template-design.mdx +65 -1
  9. package/corpus/core/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
  10. package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +64 -1
  11. package/corpus/core/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
  12. package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +59 -1
  13. package/corpus/core/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
  14. package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +62 -1
  15. package/corpus/core/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
  16. package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +58 -1
  17. package/corpus/core/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
  18. package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +62 -1
  19. package/corpus/core/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
  20. package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +50 -1
  21. package/corpus/core/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
  22. package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +52 -1
  23. package/corpus/core/docs/content/skills-guide.mdx +5 -0
  24. package/corpus/core/docs/content/template-design.mdx +59 -1
  25. package/corpus/core/package.json +2 -1
  26. package/corpus/core/src/agent/engine/index.ts +1 -0
  27. package/corpus/core/src/agent/engine/registry.ts +40 -0
  28. package/corpus/core/src/agent/production-agent.ts +6 -2
  29. package/corpus/core/src/application-state/handlers.ts +1 -1
  30. package/corpus/core/src/brand-kit/index.ts +1 -1
  31. package/corpus/core/src/cli/design-connect.ts +490 -0
  32. package/corpus/core/src/cli/index.ts +16 -0
  33. package/corpus/core/src/cli/skills.ts +144 -3
  34. package/corpus/core/src/client/AgentPanel.tsx +1 -1
  35. package/corpus/core/src/client/AssistantChat.tsx +43 -7
  36. package/corpus/core/src/client/MultiTabAssistantChat.tsx +1 -1
  37. package/corpus/core/src/client/agent-chat.ts +27 -3
  38. package/corpus/core/src/client/blocks/library/MermaidBlock.tsx +1 -1
  39. package/corpus/core/src/client/blocks/library/diagram.tsx +2 -2
  40. package/corpus/core/src/client/blocks/library/wireframe.tsx +1 -1
  41. package/corpus/core/src/client/chat/message-components.tsx +1 -1
  42. package/corpus/core/src/client/chat-model-groups.ts +1 -2
  43. package/corpus/core/src/client/components/LiveCursorOverlay.tsx +33 -66
  44. package/corpus/core/src/client/composer/TiptapComposer.tsx +99 -24
  45. package/corpus/core/src/client/composer/useVoiceDictation.ts +429 -260
  46. package/corpus/core/src/client/extensions/portable-extension.ts +1 -1
  47. package/corpus/core/src/client/frame-protocol.ts +17 -2
  48. package/corpus/core/src/client/rich-markdown-editor/RegistryBlockNode.tsx +2 -2
  49. package/corpus/core/src/client/sharing/ShareButton.tsx +20 -15
  50. package/corpus/core/src/client/use-agent-engine-configured.ts +80 -28
  51. package/corpus/core/src/code-agents/transcript-normalizer.ts +1 -1
  52. package/corpus/core/src/extensions/path.ts +1 -4
  53. package/corpus/core/src/integrations/a2a-continuation-processor.ts +1 -1
  54. package/corpus/core/src/mcp/build-server.ts +1 -1
  55. package/corpus/core/src/org/handlers.ts +5 -5
  56. package/corpus/core/src/org/plugin.ts +2 -2
  57. package/corpus/core/src/scripts/call-agent.ts +1 -1
  58. package/corpus/core/src/scripts/db/safety.ts +1 -1
  59. package/corpus/core/src/server/auth.ts +3 -0
  60. package/corpus/core/src/server/builder-design-systems.ts +308 -0
  61. package/corpus/core/src/server/core-routes-plugin.ts +29 -3
  62. package/corpus/core/src/server/index.ts +8 -0
  63. package/corpus/core/src/server/transcribe-voice.ts +57 -22
  64. package/corpus/core/src/shared/mcp-embed-headers.ts +23 -1
  65. package/corpus/core/src/styles/agent-native.css +60 -4
  66. package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +10 -0
  67. package/corpus/core/src/usage/store.ts +2 -2
  68. package/corpus/core/src/vite/client.ts +2 -0
  69. package/corpus/core/src/voice/index.ts +18 -0
  70. package/corpus/core/src/voice/voice-cleanup-prompt.ts +38 -0
  71. package/corpus/core/src/voice/voice-context.ts +217 -0
  72. package/corpus/core/src/voice/voice-replacements.ts +75 -0
  73. package/corpus/templates/analytics/AGENTS.md +4 -3
  74. package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +298 -15
  75. package/corpus/templates/analytics/actions/get-sql-dashboard.ts +9 -4
  76. package/corpus/templates/analytics/actions/list-session-recordings.ts +6 -1
  77. package/corpus/templates/analytics/actions/mutate-dashboard.ts +18 -2
  78. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +12 -2
  79. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +557 -400
  80. package/corpus/templates/analytics/app/global.css +0 -4
  81. package/corpus/templates/analytics/app/hooks/use-replay-storage-status.ts +66 -0
  82. package/corpus/templates/analytics/app/i18n/zh-TW.ts +39 -0
  83. package/corpus/templates/analytics/app/i18n-data.ts +424 -0
  84. package/corpus/templates/analytics/app/pages/Settings.tsx +333 -0
  85. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +2 -1
  86. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +121 -3
  87. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +82 -34
  88. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +7 -0
  89. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +144 -36
  90. package/corpus/templates/analytics/changelog/2026-06-28-ask-tab-now-matches-the-darker-app-background.md +6 -0
  91. package/corpus/templates/analytics/changelog/2026-06-28-dashboard-email-reports-now-use-cleaner-light-mode-snapshots.md +6 -0
  92. package/corpus/templates/analytics/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
  93. package/corpus/templates/analytics/changelog/2026-06-28-sessions-now-show-only-signed-in-recordings-with-playable-re.md +6 -0
  94. package/corpus/templates/analytics/changelog/2026-06-29-analytics-agents-can-now-place-new-charts-into-requested-das.md +6 -0
  95. package/corpus/templates/analytics/changelog/2026-06-29-analytics-queries-now-report-failed-source-query-errors-inst.md +6 -0
  96. package/corpus/templates/analytics/changelog/2026-06-29-charts-can-now-be-dropped-between-full-dashboard-rows-to-cre.md +6 -0
  97. package/corpus/templates/analytics/changelog/2026-06-29-dashboard-email-reports-include-the-full-dashboard-image-wit.md +6 -0
  98. package/corpus/templates/analytics/changelog/2026-06-29-main-surface-border-reaches-the-top.md +6 -0
  99. package/corpus/templates/analytics/changelog/2026-06-29-session-replay-now-records-playable-sessions-in-production-a.md +6 -0
  100. package/corpus/templates/analytics/changelog/2026-06-29-set-up-session-replay-storage-from-settings-connect-builder-.md +6 -0
  101. package/corpus/templates/analytics/scripts/cleanup-empty-session-recordings.ts +102 -0
  102. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +3 -3
  103. package/corpus/templates/analytics/server/lib/dashboard-report.ts +75 -30
  104. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +5 -2
  105. package/corpus/templates/analytics/server/lib/real-data-actions.ts +81 -20
  106. package/corpus/templates/analytics/server/lib/replay-storage.ts +79 -0
  107. package/corpus/templates/analytics/server/lib/s3-upload-provider.ts +255 -0
  108. package/corpus/templates/analytics/server/lib/session-replay.ts +53 -12
  109. package/corpus/templates/analytics/server/plugins/agent-chat.ts +11 -2
  110. package/corpus/templates/analytics/server/plugins/onboarding.ts +97 -0
  111. package/corpus/templates/assets/app/components/layout/Sidebar.tsx +4 -4
  112. package/corpus/templates/assets/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
  113. package/corpus/templates/brain/app/components/layout/Layout.tsx +29 -2
  114. package/corpus/templates/brain/app/components/layout/Sidebar.tsx +131 -42
  115. package/corpus/templates/brain/app/i18n/zh-TW.ts +2 -0
  116. package/corpus/templates/brain/app/i18n-data.ts +20 -0
  117. package/corpus/templates/brain/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
  118. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +29 -1
  119. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +417 -299
  120. package/corpus/templates/calendar/app/i18n/zh-TW.ts +2 -0
  121. package/corpus/templates/calendar/app/i18n-data.ts +20 -0
  122. package/corpus/templates/calendar/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
  123. package/corpus/templates/chat/app/components/layout/Sidebar.tsx +6 -19
  124. package/corpus/templates/chat/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
  125. package/corpus/templates/clips/AGENTS.md +4 -0
  126. package/corpus/templates/clips/actions/cleanup-transcript.ts +39 -9
  127. package/corpus/templates/clips/actions/finalize-meeting.ts +81 -12
  128. package/corpus/templates/clips/actions/lib/agents-md-context.ts +4 -1
  129. package/corpus/templates/clips/app/components/library/library-layout.tsx +3 -3
  130. package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +1 -1
  131. package/corpus/templates/clips/app/components/player/share-dialog.tsx +2 -2
  132. package/corpus/templates/clips/changelog/2026-06-28-left-sidebar-footer-controls-use-less-divider-chrome.md +6 -0
  133. package/corpus/templates/clips/desktop/src/lib/personal-vocabulary.ts +13 -8
  134. package/corpus/templates/clips/desktop/src/lib/recorder.ts +24 -0
  135. package/corpus/templates/clips/desktop/src/lib/transcription-capture.ts +105 -5
  136. package/corpus/templates/clips/desktop/src/lib/voice-dictation.ts +60 -1
  137. package/corpus/templates/content/actions/set-image-alt-text.ts +1 -1
  138. package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +1 -1
  139. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +1 -1
  140. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +5 -5
  141. package/corpus/templates/content/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
  142. package/corpus/templates/content/shared/document-export.ts +1 -1
  143. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +8 -18
  144. package/corpus/templates/design/.agents/skills/visual-edit/SKILL.md +154 -0
  145. package/corpus/templates/design/AGENTS.md +95 -5
  146. package/corpus/templates/design/DESIGN.md +82 -0
  147. package/corpus/templates/design/DEVELOPING.md +43 -0
  148. package/corpus/templates/design/actions/add-localhost-screens.ts +453 -0
  149. package/corpus/templates/design/actions/apply-shader.ts +277 -0
  150. package/corpus/templates/design/actions/apply-visual-edit.ts +303 -0
  151. package/corpus/templates/design/actions/connect-localhost.ts +183 -0
  152. package/corpus/templates/design/actions/edit-design.ts +195 -42
  153. package/corpus/templates/design/actions/generate-design.ts +71 -1
  154. package/corpus/templates/design/actions/generate-screens.ts +193 -0
  155. package/corpus/templates/design/actions/get-code-layer-projection.ts +191 -0
  156. package/corpus/templates/design/actions/get-shader.ts +119 -0
  157. package/corpus/templates/design/actions/index-design-system-with-builder.ts +259 -0
  158. package/corpus/templates/design/actions/list-localhost-connections.ts +95 -0
  159. package/corpus/templates/design/actions/navigate.ts +55 -2
  160. package/corpus/templates/design/actions/update-file.ts +9 -2
  161. package/corpus/templates/design/actions/view-screen.ts +68 -1
  162. package/corpus/templates/design/agent-native.app-skill.json +10 -0
  163. package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +719 -0
  164. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +2120 -103
  165. package/corpus/templates/design/app/components/design/DesignEditorSkeleton.tsx +74 -38
  166. package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +401 -0
  167. package/corpus/templates/design/app/components/design/EditPanel.tsx +3966 -440
  168. package/corpus/templates/design/app/components/design/LayersPanel.tsx +1473 -0
  169. package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +3940 -147
  170. package/corpus/templates/design/app/components/design/QuestionFlow.tsx +700 -13
  171. package/corpus/templates/design/app/components/design/TweaksPanel.tsx +105 -63
  172. package/corpus/templates/design/app/components/design/index.ts +0 -5
  173. package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +353 -0
  174. package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +1589 -0
  175. package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +511 -0
  176. package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +395 -0
  177. package/corpus/templates/design/app/components/design/inspector/FigmaColorPicker.tsx +2010 -0
  178. package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +601 -0
  179. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +242 -0
  180. package/corpus/templates/design/app/components/design/inspector/SHADER_INTEGRATION.md +420 -0
  181. package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +238 -0
  182. package/corpus/templates/design/app/components/design/inspector/ShaderControls.stories.tsx +71 -0
  183. package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +575 -0
  184. package/corpus/templates/design/app/components/design/inspector/ShaderEffectRow.tsx +106 -0
  185. package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +423 -0
  186. package/corpus/templates/design/app/components/design/inspector/figma-icons.tsx +741 -0
  187. package/corpus/templates/design/app/components/design/inspector/index.ts +87 -0
  188. package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +201 -0
  189. package/corpus/templates/design/app/components/design/types.ts +23 -0
  190. package/corpus/templates/design/app/components/layout/Layout.tsx +24 -13
  191. package/corpus/templates/design/app/components/layout/Sidebar.tsx +4 -4
  192. package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +474 -36
  193. package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +22 -5
  194. package/corpus/templates/design/app/components/visual-editor/SaveStatusIndicator.tsx +24 -11
  195. package/corpus/templates/design/app/global.css +149 -5
  196. package/corpus/templates/design/app/hooks/use-agent-generating.ts +4 -0
  197. package/corpus/templates/design/app/hooks/use-navigation-state.ts +130 -8
  198. package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +329 -0
  199. package/corpus/templates/design/app/i18n/zh-TW.ts +277 -0
  200. package/corpus/templates/design/app/i18n-data.ts +3028 -1
  201. package/corpus/templates/design/app/pages/DesignEditor.tsx +5831 -904
  202. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +57 -221
  203. package/corpus/templates/design/changelog/2026-06-28-annotate-mode-now-combines-drawing-and-comment-pins-in-one-c.md +6 -0
  204. package/corpus/templates/design/changelog/2026-06-28-design-editor-panel-borders-are-cleaner-so-the-canvas-edge-s.md +6 -0
  205. package/corpus/templates/design/changelog/2026-06-28-drawing-prompts-now-reliably-open-and-submit-to-the-design-a.md +6 -0
  206. package/corpus/templates/design/changelog/2026-06-28-editor-app-menus-now-use-an-outline-agent-native-mark-with-b.md +6 -0
  207. package/corpus/templates/design/changelog/2026-06-28-figma-style-code-design-mode.md +6 -0
  208. package/corpus/templates/design/changelog/2026-06-28-layer-selection-now-updates-chat-context-without-opening-the.md +6 -0
  209. package/corpus/templates/design/changelog/2026-06-28-layer-selections-now-preserve-hidden-and-locked-canvas-state.md +6 -0
  210. package/corpus/templates/design/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
  211. package/corpus/templates/design/changelog/2026-06-28-live-cursors-now-keep-names-beside-a-cleaner-pointer-marker.md +6 -0
  212. package/corpus/templates/design/changelog/2026-06-28-mode-switching-now-lives-in-a-compact-bottom-right-icon-dock.md +6 -0
  213. package/corpus/templates/design/changelog/2026-06-28-screen-overview-now-handles-zooming-drawing-opening-and-dele.md +6 -0
  214. package/corpus/templates/design/changelog/2026-06-28-screen-overview-now-selects-screens-on-click-and-opens-them-.md +6 -0
  215. package/corpus/templates/design/changelog/2026-06-28-screen-overview-zooms-into-the-active-screen-with-smoother-e.md +6 -0
  216. package/corpus/templates/design/changelog/2026-06-28-selected-element-chips-in-chat-are-shorter-and-easier-to-sca.md +6 -0
  217. package/corpus/templates/design/changelog/2026-06-28-the-design-editor-toolbar-is-more-compact-and-the-app-menu-m.md +6 -0
  218. package/corpus/templates/design/changelog/2026-06-28-tweaks-now-live-at-the-top-of-the-design-sidebar-instead-of-.md +6 -0
  219. package/corpus/templates/design/changelog/2026-06-28-tweaks-now-live-in-the-right-inspector-beside-the-design-tab.md +6 -0
  220. package/corpus/templates/design/changelog/2026-06-29-active-sidebar-rows-use-soft-highlights.md +6 -0
  221. package/corpus/templates/design/changelog/2026-06-29-agents-can-now-open-screen-overview-focus-screens-and-add.md +6 -0
  222. package/corpus/templates/design/changelog/2026-06-29-canvas-selection-no-longer-flashes-broken-prototype-html-whe.md +6 -0
  223. package/corpus/templates/design/changelog/2026-06-29-command-menu-selections-are-clearer-in-dark-mode.md +6 -0
  224. package/corpus/templates/design/changelog/2026-06-29-design-agents-now-edit-broad-copy-changes-more-reliably.md +6 -0
  225. package/corpus/templates/design/changelog/2026-06-29-design-editor-extensions-now-run-inline-beside-the-artboard.md +6 -0
  226. package/corpus/templates/design/changelog/2026-06-29-design-mode-top-chrome-now-groups-collaborators-preview-shar.md +6 -0
  227. package/corpus/templates/design/changelog/2026-06-29-element-selection-now-stays-on-the-intended-layer-after-sele.md +6 -0
  228. package/corpus/templates/design/changelog/2026-06-29-screen-overview-full-view-controls-now-sit-above-frames-inst.md +6 -0
  229. package/corpus/templates/design/changelog/2026-06-29-screen-overview-tools-can-draw-shapes-create-screens-and-kee.md +6 -0
  230. package/corpus/templates/design/changelog/2026-06-29-sidebars-use-darker-chrome-and-chat-warns-immediately.md +6 -0
  231. package/corpus/templates/design/changelog/2026-06-29-the-pen-tool-now-creates-anchor-based-bezier-vectors-with-ha.md +6 -0
  232. package/corpus/templates/design/changelog/2026-06-29-visual-edit-can-open-localhost-routes-as-url-backed-screens.md +6 -0
  233. package/corpus/templates/design/package.json +1 -0
  234. package/corpus/templates/design/server/db/schema.ts +26 -0
  235. package/corpus/templates/design/server/handlers/import-figma-system.ts +38 -85
  236. package/corpus/templates/design/server/lib/design-snapshot.ts +9 -1
  237. package/corpus/templates/design/server/plugins/agent-chat.ts +3 -0
  238. package/corpus/templates/design/shared/api.ts +27 -0
  239. package/corpus/templates/design/shared/canvas-frames.ts +103 -0
  240. package/corpus/templates/design/shared/canvas-math.ts +1058 -0
  241. package/corpus/templates/design/shared/code-layer.ts +2412 -0
  242. package/corpus/templates/design/shared/color-utils.ts +180 -0
  243. package/corpus/templates/design/shared/generation-session.ts +61 -0
  244. package/corpus/templates/design/shared/html-content.ts +21 -0
  245. package/corpus/templates/design/shared/pen-path.ts +207 -0
  246. package/corpus/templates/design/shared/shader-presets.ts +782 -0
  247. package/corpus/templates/design/shared/shader-safety.ts +137 -0
  248. package/corpus/templates/design/shared/source-mode.ts +237 -0
  249. package/corpus/templates/forms/app/components/layout/Sidebar.tsx +149 -6
  250. package/corpus/templates/forms/app/i18n/ar-SA.ts +2 -0
  251. package/corpus/templates/forms/app/i18n/de-DE.ts +2 -0
  252. package/corpus/templates/forms/app/i18n/en-US.ts +2 -0
  253. package/corpus/templates/forms/app/i18n/es-ES.ts +2 -0
  254. package/corpus/templates/forms/app/i18n/fr-FR.ts +2 -0
  255. package/corpus/templates/forms/app/i18n/hi-IN.ts +2 -0
  256. package/corpus/templates/forms/app/i18n/ja-JP.ts +2 -0
  257. package/corpus/templates/forms/app/i18n/ko-KR.ts +2 -0
  258. package/corpus/templates/forms/app/i18n/pt-BR.ts +2 -0
  259. package/corpus/templates/forms/app/i18n/zh-CN.ts +2 -0
  260. package/corpus/templates/forms/app/i18n/zh-TW.ts +2 -0
  261. package/corpus/templates/forms/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
  262. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +2 -2
  263. package/corpus/templates/macros/changelog/2026-06-28-left-sidebar-footer-controls-use-less-divider-chrome.md +6 -0
  264. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +408 -247
  265. package/corpus/templates/mail/app/i18n/ar-SA.ts +4 -0
  266. package/corpus/templates/mail/app/i18n/de-DE.ts +4 -0
  267. package/corpus/templates/mail/app/i18n/en-US.ts +4 -0
  268. package/corpus/templates/mail/app/i18n/es-ES.ts +4 -0
  269. package/corpus/templates/mail/app/i18n/fr-FR.ts +4 -0
  270. package/corpus/templates/mail/app/i18n/hi-IN.ts +4 -0
  271. package/corpus/templates/mail/app/i18n/ja-JP.ts +4 -0
  272. package/corpus/templates/mail/app/i18n/ko-KR.ts +4 -0
  273. package/corpus/templates/mail/app/i18n/pt-BR.ts +4 -0
  274. package/corpus/templates/mail/app/i18n/zh-CN.ts +4 -0
  275. package/corpus/templates/mail/app/i18n/zh-TW.ts +4 -0
  276. package/corpus/templates/mail/app/lib/utils.ts +2 -2
  277. package/corpus/templates/mail/changelog/2026-06-28-pinned-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
  278. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +6 -8
  279. package/corpus/templates/plan/app/components/plan/wireframe/Wireframe.tsx +1 -1
  280. package/corpus/templates/plan/app/global.css +61 -78
  281. package/corpus/templates/plan/app/pages/PlansPage.tsx +8 -0
  282. package/corpus/templates/plan/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
  283. package/corpus/templates/plan/changelog/2026-06-28-plans-use-the-shared-neutral-light-and-dark-theme.md +6 -0
  284. package/corpus/templates/plan/changelog/2026-06-28-sending-open-feedback-from-a-plan-now-opens-the-inline-plan-.md +6 -0
  285. package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +9 -10
  286. package/corpus/templates/slides/AGENTS.md +3 -2
  287. package/corpus/templates/slides/actions/import-file.ts +26 -17
  288. package/corpus/templates/slides/actions/index-design-system-with-builder.ts +259 -0
  289. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +35 -167
  290. package/corpus/templates/slides/app/components/design-system/fig-import-response.ts +9 -13
  291. package/corpus/templates/slides/app/components/layout/Sidebar.tsx +5 -5
  292. package/corpus/templates/slides/app/context/DeckContext.tsx +1 -1
  293. package/corpus/templates/slides/app/i18n/ar-SA.ts +9 -37
  294. package/corpus/templates/slides/app/i18n/de-DE.ts +10 -25
  295. package/corpus/templates/slides/app/i18n/en-US.ts +9 -26
  296. package/corpus/templates/slides/app/i18n/es-ES.ts +10 -28
  297. package/corpus/templates/slides/app/i18n/fr-FR.ts +10 -28
  298. package/corpus/templates/slides/app/i18n/hi-IN.ts +9 -26
  299. package/corpus/templates/slides/app/i18n/ja-JP.ts +10 -22
  300. package/corpus/templates/slides/app/i18n/ko-KR.ts +9 -23
  301. package/corpus/templates/slides/app/i18n/pt-BR.ts +10 -28
  302. package/corpus/templates/slides/app/i18n/zh-CN.ts +9 -22
  303. package/corpus/templates/slides/app/i18n/zh-TW.ts +9 -22
  304. package/corpus/templates/slides/changelog/2026-06-28-left-sidebar-collapse-motion-and-footer-chrome-are-quieter.md +6 -0
  305. package/corpus/templates/slides/server/handlers/import-figma-system.ts +44 -12
  306. package/corpus/templates/slides/server/lib/chat-attachments.ts +1 -1
  307. package/corpus/templates/videos/app/components/NewCompositionPopover.tsx +1 -1
  308. package/corpus/templates/videos/app/components/Sidebar.tsx +2 -2
  309. package/corpus/templates/videos/app/components/layout/Layout.tsx +29 -2
  310. package/corpus/templates/videos/app/components/layout/NavSidebar.tsx +113 -30
  311. package/corpus/templates/videos/app/i18n/ar-SA.ts +2 -0
  312. package/corpus/templates/videos/app/i18n/de-DE.ts +2 -0
  313. package/corpus/templates/videos/app/i18n/en-US.ts +2 -0
  314. package/corpus/templates/videos/app/i18n/es-ES.ts +2 -0
  315. package/corpus/templates/videos/app/i18n/fr-FR.ts +2 -0
  316. package/corpus/templates/videos/app/i18n/hi-IN.ts +2 -0
  317. package/corpus/templates/videos/app/i18n/ja-JP.ts +2 -0
  318. package/corpus/templates/videos/app/i18n/ko-KR.ts +2 -0
  319. package/corpus/templates/videos/app/i18n/pt-BR.ts +2 -0
  320. package/corpus/templates/videos/app/i18n/zh-CN.ts +2 -0
  321. package/corpus/templates/videos/app/i18n/zh-TW.ts +2 -0
  322. package/corpus/templates/videos/changelog/2026-06-28-left-sidebar-collapses-into-an-animated-icon-rail.md +6 -0
  323. package/dist/agent/engine/index.d.ts +1 -1
  324. package/dist/agent/engine/index.d.ts.map +1 -1
  325. package/dist/agent/engine/index.js +1 -1
  326. package/dist/agent/engine/index.js.map +1 -1
  327. package/dist/agent/engine/registry.d.ts +9 -0
  328. package/dist/agent/engine/registry.d.ts.map +1 -1
  329. package/dist/agent/engine/registry.js +37 -0
  330. package/dist/agent/engine/registry.js.map +1 -1
  331. package/dist/agent/production-agent.d.ts.map +1 -1
  332. package/dist/agent/production-agent.js +4 -3
  333. package/dist/agent/production-agent.js.map +1 -1
  334. package/dist/application-state/handlers.js +1 -1
  335. package/dist/application-state/handlers.js.map +1 -1
  336. package/dist/brand-kit/index.d.ts +1 -1
  337. package/dist/brand-kit/index.js +1 -1
  338. package/dist/brand-kit/index.js.map +1 -1
  339. package/dist/cli/design-connect.d.ts +52 -0
  340. package/dist/cli/design-connect.d.ts.map +1 -0
  341. package/dist/cli/design-connect.js +409 -0
  342. package/dist/cli/design-connect.js.map +1 -0
  343. package/dist/cli/index.js +15 -0
  344. package/dist/cli/index.js.map +1 -1
  345. package/dist/cli/skills.d.ts +3 -0
  346. package/dist/cli/skills.d.ts.map +1 -1
  347. package/dist/cli/skills.js +139 -3
  348. package/dist/cli/skills.js.map +1 -1
  349. package/dist/client/AgentPanel.js +1 -1
  350. package/dist/client/AgentPanel.js.map +1 -1
  351. package/dist/client/AssistantChat.d.ts.map +1 -1
  352. package/dist/client/AssistantChat.js +38 -8
  353. package/dist/client/AssistantChat.js.map +1 -1
  354. package/dist/client/MultiTabAssistantChat.js +1 -1
  355. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  356. package/dist/client/agent-chat.d.ts +6 -0
  357. package/dist/client/agent-chat.d.ts.map +1 -1
  358. package/dist/client/agent-chat.js +17 -3
  359. package/dist/client/agent-chat.js.map +1 -1
  360. package/dist/client/blocks/library/MermaidBlock.js +1 -1
  361. package/dist/client/blocks/library/MermaidBlock.js.map +1 -1
  362. package/dist/client/blocks/library/diagram.js +1 -1
  363. package/dist/client/blocks/library/diagram.js.map +1 -1
  364. package/dist/client/blocks/library/wireframe.js +1 -1
  365. package/dist/client/blocks/library/wireframe.js.map +1 -1
  366. package/dist/client/chat/message-components.js +1 -1
  367. package/dist/client/chat/message-components.js.map +1 -1
  368. package/dist/client/chat-model-groups.d.ts.map +1 -1
  369. package/dist/client/chat-model-groups.js +1 -2
  370. package/dist/client/chat-model-groups.js.map +1 -1
  371. package/dist/client/components/LiveCursorOverlay.d.ts +1 -2
  372. package/dist/client/components/LiveCursorOverlay.d.ts.map +1 -1
  373. package/dist/client/components/LiveCursorOverlay.js +19 -40
  374. package/dist/client/components/LiveCursorOverlay.js.map +1 -1
  375. package/dist/client/composer/TiptapComposer.d.ts +3 -1
  376. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  377. package/dist/client/composer/TiptapComposer.js +86 -24
  378. package/dist/client/composer/TiptapComposer.js.map +1 -1
  379. package/dist/client/composer/useVoiceDictation.d.ts +9 -0
  380. package/dist/client/composer/useVoiceDictation.d.ts.map +1 -1
  381. package/dist/client/composer/useVoiceDictation.js +154 -32
  382. package/dist/client/composer/useVoiceDictation.js.map +1 -1
  383. package/dist/client/extensions/portable-extension.d.ts +1 -1
  384. package/dist/client/extensions/portable-extension.d.ts.map +1 -1
  385. package/dist/client/extensions/portable-extension.js.map +1 -1
  386. package/dist/client/frame-protocol.d.ts +17 -2
  387. package/dist/client/frame-protocol.d.ts.map +1 -1
  388. package/dist/client/frame-protocol.js.map +1 -1
  389. package/dist/client/rich-markdown-editor/RegistryBlockNode.js +2 -2
  390. package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -1
  391. package/dist/client/sharing/ShareButton.d.ts +2 -0
  392. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  393. package/dist/client/sharing/ShareButton.js +7 -6
  394. package/dist/client/sharing/ShareButton.js.map +1 -1
  395. package/dist/client/use-agent-engine-configured.d.ts +5 -0
  396. package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
  397. package/dist/client/use-agent-engine-configured.js +51 -25
  398. package/dist/client/use-agent-engine-configured.js.map +1 -1
  399. package/dist/code-agents/transcript-normalizer.js +1 -1
  400. package/dist/code-agents/transcript-normalizer.js.map +1 -1
  401. package/dist/collab/routes.d.ts +1 -1
  402. package/dist/extensions/path.d.ts +1 -1
  403. package/dist/extensions/path.d.ts.map +1 -1
  404. package/dist/extensions/path.js.map +1 -1
  405. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  406. package/dist/integrations/a2a-continuation-processor.js +1 -1
  407. package/dist/integrations/a2a-continuation-processor.js.map +1 -1
  408. package/dist/mcp/build-server.d.ts +1 -1
  409. package/dist/mcp/build-server.d.ts.map +1 -1
  410. package/dist/mcp/build-server.js.map +1 -1
  411. package/dist/notifications/routes.d.ts +3 -3
  412. package/dist/observability/routes.d.ts +5 -5
  413. package/dist/org/handlers.js +5 -5
  414. package/dist/org/handlers.js.map +1 -1
  415. package/dist/org/plugin.js +2 -2
  416. package/dist/org/plugin.js.map +1 -1
  417. package/dist/scripts/call-agent.js +1 -1
  418. package/dist/scripts/call-agent.js.map +1 -1
  419. package/dist/scripts/db/safety.js +1 -1
  420. package/dist/scripts/db/safety.js.map +1 -1
  421. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  422. package/dist/server/auth.d.ts.map +1 -1
  423. package/dist/server/auth.js +3 -0
  424. package/dist/server/auth.js.map +1 -1
  425. package/dist/server/builder-design-systems.d.ts +28 -0
  426. package/dist/server/builder-design-systems.d.ts.map +1 -0
  427. package/dist/server/builder-design-systems.js +217 -0
  428. package/dist/server/builder-design-systems.js.map +1 -0
  429. package/dist/server/core-routes-plugin.d.ts +1 -0
  430. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  431. package/dist/server/core-routes-plugin.js +20 -3
  432. package/dist/server/core-routes-plugin.js.map +1 -1
  433. package/dist/server/index.d.ts +1 -0
  434. package/dist/server/index.d.ts.map +1 -1
  435. package/dist/server/index.js +1 -0
  436. package/dist/server/index.js.map +1 -1
  437. package/dist/server/transcribe-voice.d.ts +1 -1
  438. package/dist/server/transcribe-voice.d.ts.map +1 -1
  439. package/dist/server/transcribe-voice.js +40 -24
  440. package/dist/server/transcribe-voice.js.map +1 -1
  441. package/dist/shared/mcp-embed-headers.d.ts +1 -0
  442. package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
  443. package/dist/shared/mcp-embed-headers.js +20 -1
  444. package/dist/shared/mcp-embed-headers.js.map +1 -1
  445. package/dist/styles/agent-native.css +60 -4
  446. package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +10 -0
  447. package/dist/usage/store.js +2 -2
  448. package/dist/usage/store.js.map +1 -1
  449. package/dist/vite/client.d.ts.map +1 -1
  450. package/dist/vite/client.js +2 -0
  451. package/dist/vite/client.js.map +1 -1
  452. package/dist/voice/index.d.ts +5 -0
  453. package/dist/voice/index.d.ts.map +1 -0
  454. package/dist/voice/index.js +4 -0
  455. package/dist/voice/index.js.map +1 -0
  456. package/dist/voice/voice-cleanup-prompt.d.ts +6 -0
  457. package/dist/voice/voice-cleanup-prompt.d.ts.map +1 -0
  458. package/dist/voice/voice-cleanup-prompt.js +26 -0
  459. package/dist/voice/voice-cleanup-prompt.js.map +1 -0
  460. package/dist/voice/voice-context.d.ts +25 -0
  461. package/dist/voice/voice-context.d.ts.map +1 -0
  462. package/dist/voice/voice-context.js +156 -0
  463. package/dist/voice/voice-context.js.map +1 -0
  464. package/dist/voice/voice-replacements.d.ts +4 -0
  465. package/dist/voice/voice-replacements.d.ts.map +1 -0
  466. package/dist/voice/voice-replacements.js +52 -0
  467. package/dist/voice/voice-replacements.js.map +1 -0
  468. package/docs/content/locales/ar-SA/skills-guide.mdx +5 -0
  469. package/docs/content/locales/ar-SA/template-design.mdx +55 -1
  470. package/docs/content/locales/de-DE/skills-guide.mdx +5 -0
  471. package/docs/content/locales/de-DE/template-design.mdx +66 -1
  472. package/docs/content/locales/es-ES/skills-guide.mdx +5 -0
  473. package/docs/content/locales/es-ES/template-design.mdx +65 -1
  474. package/docs/content/locales/fr-FR/skills-guide.mdx +5 -0
  475. package/docs/content/locales/fr-FR/template-design.mdx +64 -1
  476. package/docs/content/locales/hi-IN/skills-guide.mdx +5 -0
  477. package/docs/content/locales/hi-IN/template-design.mdx +59 -1
  478. package/docs/content/locales/ja-JP/skills-guide.mdx +5 -0
  479. package/docs/content/locales/ja-JP/template-design.mdx +62 -1
  480. package/docs/content/locales/ko-KR/skills-guide.mdx +5 -0
  481. package/docs/content/locales/ko-KR/template-design.mdx +58 -1
  482. package/docs/content/locales/pt-BR/skills-guide.mdx +5 -0
  483. package/docs/content/locales/pt-BR/template-design.mdx +62 -1
  484. package/docs/content/locales/zh-CN/skills-guide.mdx +5 -0
  485. package/docs/content/locales/zh-CN/template-design.mdx +50 -1
  486. package/docs/content/locales/zh-TW/skills-guide.mdx +5 -0
  487. package/docs/content/locales/zh-TW/template-design.mdx +52 -1
  488. package/docs/content/skills-guide.mdx +5 -0
  489. package/docs/content/template-design.mdx +59 -1
  490. package/package.json +2 -1
  491. package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +10 -0
  492. package/corpus/templates/design/actions/import-figma-file.ts +0 -166
  493. package/corpus/templates/design/app/components/design/DesignToolbar.tsx +0 -339
  494. package/corpus/templates/design/app/components/design/PresentMode.tsx +0 -68
  495. package/corpus/templates/design/app/components/design/ViewportTabs.tsx +0 -149
  496. package/corpus/templates/design/app/components/design/ZoomControls.tsx +0 -157
  497. package/corpus/templates/slides/server/lib/fig-design-system.ts +0 -110
@@ -267,7 +267,7 @@
267
267
  "chartType": "metric",
268
268
  "source": "first-party",
269
269
  "width": 1,
270
- "sql": "SELECT COUNT(*) AS count FROM session_recordings WHERE chunk_count > 0 AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(user_id, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(user_id, '')) LIKE '%@builder.io'))",
270
+ "sql": "SELECT COUNT(*) AS count FROM session_recordings WHERE chunk_count > 0 AND event_count > 0 AND COALESCE(NULLIF(CASE WHEN lower(coalesce(user_id, '')) LIKE '%@%' THEN user_id ELSE '' END, ''), NULLIF(CASE WHEN lower(coalesce(user_key, '')) LIKE '%@%' THEN user_key ELSE '' END, '')) IS NOT NULL AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(COALESCE(NULLIF(CASE WHEN lower(coalesce(user_id, '')) LIKE '%@%' THEN user_id ELSE '' END, ''), NULLIF(CASE WHEN lower(coalesce(user_key, '')) LIKE '%@%' THEN user_key ELSE '' END, '')), '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(COALESCE(NULLIF(CASE WHEN lower(coalesce(user_id, '')) LIKE '%@%' THEN user_id ELSE '' END, ''), NULLIF(CASE WHEN lower(coalesce(user_key, '')) LIKE '%@%' THEN user_key ELSE '' END, '')), '')) LIKE '%@builder.io'))",
271
271
  "config": {
272
272
  "yKey": "count",
273
273
  "yFormatter": "number",
@@ -280,7 +280,7 @@
280
280
  "chartType": "area",
281
281
  "source": "first-party",
282
282
  "width": 1,
283
- "sql": "SELECT substr(started_at, 1, 10) AS date, SUM(chunk_count) AS count FROM session_recordings WHERE chunk_count > 0 AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(user_id, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(user_id, '')) LIKE '%@builder.io')) GROUP BY substr(started_at, 1, 10) ORDER BY date",
283
+ "sql": "SELECT substr(started_at, 1, 10) AS date, SUM(chunk_count) AS count FROM session_recordings WHERE chunk_count > 0 AND event_count > 0 AND COALESCE(NULLIF(CASE WHEN lower(coalesce(user_id, '')) LIKE '%@%' THEN user_id ELSE '' END, ''), NULLIF(CASE WHEN lower(coalesce(user_key, '')) LIKE '%@%' THEN user_key ELSE '' END, '')) IS NOT NULL AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(COALESCE(NULLIF(CASE WHEN lower(coalesce(user_id, '')) LIKE '%@%' THEN user_id ELSE '' END, ''), NULLIF(CASE WHEN lower(coalesce(user_key, '')) LIKE '%@%' THEN user_key ELSE '' END, '')), '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(COALESCE(NULLIF(CASE WHEN lower(coalesce(user_id, '')) LIKE '%@%' THEN user_id ELSE '' END, ''), NULLIF(CASE WHEN lower(coalesce(user_key, '')) LIKE '%@%' THEN user_key ELSE '' END, '')), '')) LIKE '%@builder.io')) GROUP BY substr(started_at, 1, 10) ORDER BY date",
284
284
  "config": {
285
285
  "xKey": "date",
286
286
  "yKey": "count",
@@ -295,7 +295,7 @@
295
295
  "chartType": "table",
296
296
  "source": "first-party",
297
297
  "width": 2,
298
- "sql": "SELECT id AS recording_id, session_id, COALESCE(NULLIF(app, ''), NULLIF(template, ''), 'unknown') AS app, COALESCE(NULLIF(user_id, ''), NULLIF(user_key, ''), NULLIF(anonymous_id, ''), 'anonymous') AS visitor, chunk_count AS chunks, event_count AS events, started_at, COALESCE(ended_at, last_ingested_at, started_at) AS last_seen, '/sessions/' || id AS href FROM session_recordings WHERE chunk_count > 0 AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(user_id, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(user_id, '')) LIKE '%@builder.io')) ORDER BY last_seen DESC LIMIT 25",
298
+ "sql": "SELECT id AS recording_id, session_id, COALESCE(NULLIF(app, ''), NULLIF(template, ''), 'unknown') AS app, COALESCE(NULLIF(CASE WHEN lower(coalesce(user_id, '')) LIKE '%@%' THEN user_id ELSE '' END, ''), NULLIF(CASE WHEN lower(coalesce(user_key, '')) LIKE '%@%' THEN user_key ELSE '' END, '')) AS visitor, chunk_count AS chunks, event_count AS events, started_at, COALESCE(ended_at, last_ingested_at, started_at) AS last_seen, '/sessions/' || id AS href FROM session_recordings WHERE chunk_count > 0 AND event_count > 0 AND COALESCE(NULLIF(CASE WHEN lower(coalesce(user_id, '')) LIKE '%@%' THEN user_id ELSE '' END, ''), NULLIF(CASE WHEN lower(coalesce(user_key, '')) LIKE '%@%' THEN user_key ELSE '' END, '')) IS NOT NULL AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND substr(started_at, 1, 10) >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD'))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(COALESCE(NULLIF(CASE WHEN lower(coalesce(user_id, '')) LIKE '%@%' THEN user_id ELSE '' END, ''), NULLIF(CASE WHEN lower(coalesce(user_key, '')) LIKE '%@%' THEN user_key ELSE '' END, '')), '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(COALESCE(NULLIF(CASE WHEN lower(coalesce(user_id, '')) LIKE '%@%' THEN user_id ELSE '' END, ''), NULLIF(CASE WHEN lower(coalesce(user_key, '')) LIKE '%@%' THEN user_key ELSE '' END, '')), '')) LIKE '%@builder.io')) ORDER BY last_seen DESC LIMIT 25",
299
299
  "config": {
300
300
  "description": "Recent first-party session recordings. Session links open the local replay viewer.",
301
301
  "sortable": true,
@@ -27,6 +27,7 @@ type ReportSnapshot = {
27
27
  description?: string;
28
28
  filters: Record<string, string>;
29
29
  dashboardUrl: string;
30
+ reportSettingsUrl: string;
30
31
  generatedAt: string;
31
32
  };
32
33
 
@@ -38,16 +39,17 @@ const DATE_FILTER_TYPES: ReadonlySet<FilterType> = new Set([
38
39
  const DEFAULT_SERVERLESS_CHROMIUM_PACK_URL =
39
40
  "https://github.com/Sparticuz/chromium/releases/download/v149.0.0/chromium-v149.0.0-pack.x64.tar";
40
41
  const DASHBOARD_REPORT_SCREENSHOT_PARAM = "reportScreenshot";
41
- const DASHBOARD_REPORT_PANEL_LIMIT_PARAM = "reportPanelLimit";
42
+ const DASHBOARD_REPORT_SETTINGS_PARAM = "reportSettings";
42
43
  const DASHBOARD_REPORT_CID = "dashboard-report-snapshot";
43
- const COMPACT_REPORT_PANEL_LIMIT = 12;
44
44
  const LOCAL_SCREENSHOT_TIMEOUT_MS = 90_000;
45
45
  const SERVERLESS_SCREENSHOT_TIMEOUT_MS = 25_000;
46
46
  const SERVERLESS_SECOND_READY_TIMEOUT_MS = 10_000;
47
+ const MAX_SCREENSHOT_VIEWPORT_HEIGHT = 30_000;
48
+ const SCREENSHOT_VIEWPORT_PADDING = 64;
47
49
 
48
50
  type DashboardScreenshotAttempt = {
49
- label: "full" | "compact";
50
- panelLimit?: number;
51
+ label: "full" | "full-lightweight";
52
+ viewport: { width: number; height: number };
51
53
  };
52
54
 
53
55
  function daysAgo(n: number): string {
@@ -114,7 +116,10 @@ function dashboardBaseUrl(): string {
114
116
  function buildDashboardPath(
115
117
  dashboardId: string,
116
118
  filters: Record<string, string>,
117
- options?: { reportScreenshot?: boolean; panelLimit?: number },
119
+ options?: {
120
+ reportScreenshot?: boolean;
121
+ reportSettings?: boolean;
122
+ },
118
123
  ): string {
119
124
  const url = new URL(
120
125
  `/dashboards/${encodeURIComponent(dashboardId)}`,
@@ -126,11 +131,8 @@ function buildDashboardPath(
126
131
  if (options?.reportScreenshot) {
127
132
  url.searchParams.set(DASHBOARD_REPORT_SCREENSHOT_PARAM, "1");
128
133
  }
129
- if (options?.panelLimit && options.panelLimit > 0) {
130
- url.searchParams.set(
131
- DASHBOARD_REPORT_PANEL_LIMIT_PARAM,
132
- String(Math.floor(options.panelLimit)),
133
- );
134
+ if (options?.reportSettings) {
135
+ url.searchParams.set(DASHBOARD_REPORT_SETTINGS_PARAM, "1");
134
136
  }
135
137
  return `${url.pathname}${url.search}`;
136
138
  }
@@ -138,7 +140,10 @@ function buildDashboardPath(
138
140
  function buildDashboardUrl(
139
141
  dashboardId: string,
140
142
  filters: Record<string, string>,
141
- options?: { reportScreenshot?: boolean; panelLimit?: number },
143
+ options?: {
144
+ reportScreenshot?: boolean;
145
+ reportSettings?: boolean;
146
+ },
142
147
  ): string {
143
148
  const path = buildDashboardPath(dashboardId, filters, options);
144
149
  const url = new URL(path, `${dashboardBaseUrl()}/`);
@@ -178,6 +183,9 @@ async function collectReportSnapshot(
178
183
  description: config.description,
179
184
  filters,
180
185
  dashboardUrl: buildDashboardUrl(sub.dashboardId, filters),
186
+ reportSettingsUrl: buildDashboardUrl(sub.dashboardId, filters, {
187
+ reportSettings: true,
188
+ }),
181
189
  generatedAt: new Date().toISOString(),
182
190
  };
183
191
  }
@@ -303,6 +311,44 @@ async function scrollDashboardForLazyRendering(page: any): Promise<void> {
303
311
  })()`);
304
312
  }
305
313
 
314
+ async function fitViewportToDashboardCapture(
315
+ page: any,
316
+ capture: any,
317
+ minViewport: { width: number; height: number },
318
+ ): Promise<void> {
319
+ const box = await capture.boundingBox();
320
+ if (!box) return;
321
+
322
+ const size = {
323
+ width: Math.max(
324
+ minViewport.width,
325
+ Math.min(1800, Math.ceil(box.width + SCREENSHOT_VIEWPORT_PADDING)),
326
+ ),
327
+ height: Math.max(
328
+ minViewport.height,
329
+ Math.min(
330
+ MAX_SCREENSHOT_VIEWPORT_HEIGHT,
331
+ Math.ceil(box.height + SCREENSHOT_VIEWPORT_PADDING),
332
+ ),
333
+ ),
334
+ };
335
+ await page.setViewportSize(size);
336
+ await page.waitForTimeout(250);
337
+
338
+ const resizedBox = await capture.boundingBox();
339
+ if (!resizedBox) return;
340
+ const resizedHeight = Math.ceil(
341
+ resizedBox.height + SCREENSHOT_VIEWPORT_PADDING,
342
+ );
343
+ if (resizedHeight > size.height) {
344
+ await page.setViewportSize({
345
+ width: size.width,
346
+ height: Math.min(MAX_SCREENSHOT_VIEWPORT_HEIGHT, resizedHeight),
347
+ });
348
+ await page.waitForTimeout(250);
349
+ }
350
+ }
351
+
306
352
  async function captureDashboardPng(
307
353
  sub: DashboardReportSubscription,
308
354
  snapshot: ReportSnapshot,
@@ -313,7 +359,6 @@ async function captureDashboardPng(
313
359
  snapshot.filters,
314
360
  {
315
361
  reportScreenshot: true,
316
- panelLimit: attempt.panelLimit,
317
362
  },
318
363
  );
319
364
  const token = signEmbedSessionToken({
@@ -331,11 +376,14 @@ async function captureDashboardPng(
331
376
  try {
332
377
  const timeout = screenshotTimeoutMs();
333
378
  const page = await browser.newPage({
334
- viewport: { width: 1440, height: 1800 },
379
+ viewport: attempt.viewport,
335
380
  deviceScaleFactor: 1,
336
381
  });
337
382
  page.setDefaultTimeout(timeout);
338
- await page.emulateMedia({ media: "screen" });
383
+ await page.emulateMedia({ media: "screen", colorScheme: "light" });
384
+ await page.addInitScript(() => {
385
+ window.localStorage.setItem("theme", "light");
386
+ });
339
387
  await page.goto(screenshotUrl.toString(), {
340
388
  waitUntil: "domcontentloaded",
341
389
  timeout,
@@ -352,13 +400,7 @@ async function captureDashboardPng(
352
400
  : timeout,
353
401
  );
354
402
 
355
- const box = await capture.boundingBox();
356
- if (box) {
357
- await page.setViewportSize({
358
- width: Math.max(1200, Math.min(1800, Math.ceil(box.width + 64))),
359
- height: Math.max(1000, Math.min(7000, Math.ceil(box.height + 64))),
360
- });
361
- }
403
+ await fitViewportToDashboardCapture(page, capture, attempt.viewport);
362
404
  await capture.scrollIntoViewIfNeeded();
363
405
  const image = await capture.screenshot({
364
406
  type: "png",
@@ -382,12 +424,12 @@ async function captureDashboardPngWithFallback(
382
424
  snapshot: ReportSnapshot,
383
425
  ): Promise<{
384
426
  png: Buffer | null;
385
- mode: "full" | "compact" | "none";
427
+ mode: "full" | "full-lightweight" | "none";
386
428
  error?: string;
387
429
  }> {
388
430
  const attempts: DashboardScreenshotAttempt[] = [
389
- { label: "full" },
390
- { label: "compact", panelLimit: COMPACT_REPORT_PANEL_LIMIT },
431
+ { label: "full", viewport: { width: 1440, height: 1800 } },
432
+ { label: "full-lightweight", viewport: { width: 1200, height: 1400 } },
391
433
  ];
392
434
  let lastError: string | undefined;
393
435
 
@@ -423,13 +465,11 @@ function renderReportEmailHtml(
423
465
  ): string {
424
466
  const title = escapeHtml(snapshot.title);
425
467
  const dashboardUrl = escapeHtml(snapshot.dashboardUrl);
468
+ const reportSettingsUrl = escapeHtml(snapshot.reportSettingsUrl);
426
469
  const date = escapeHtml(reportDate(snapshot));
427
- const description = snapshot.description
428
- ? `<p style="margin:8px 0 18px;color:#525866;font-size:14px;line-height:1.5;">${escapeHtml(snapshot.description)}</p>`
429
- : "";
430
470
  const screenshotBlock = options.screenshotAttached
431
471
  ? `<a href="${dashboardUrl}" style="display:block;text-decoration:none;">
432
- <img src="cid:${DASHBOARD_REPORT_CID}" alt="${title}" width="100%" style="display:block;width:100%;max-width:1280px;height:auto;border:1px solid #e5e7eb;border-radius:8px;" />
472
+ <img src="cid:${DASHBOARD_REPORT_CID}" alt="${title}" width="100%" style="display:block;width:100%;max-width:1280px;height:auto;border:0;outline:0;border-radius:0;" />
433
473
  </a>`
434
474
  : `<div style="margin:18px 0;padding:14px 16px;border:1px solid #e5e7eb;border-radius:8px;background:#f9fafb;color:#374151;font-size:14px;line-height:1.5;">
435
475
  The dashboard image was unavailable for this run. Open the live dashboard to view the latest report.
@@ -441,10 +481,14 @@ function renderReportEmailHtml(
441
481
  <h3 style="margin:0 0 8px;font-size:18px;line-height:1.35;font-weight:600;">
442
482
  Here's the report of <a href="${dashboardUrl}" style="color:#2563eb;text-decoration:none;">${title}</a> for ${date}.
443
483
  </h3>
444
- ${description}
445
484
  ${screenshotBlock}
446
485
  <p style="margin:18px 0 0;color:#525866;font-size:13px;line-height:1.45;">
447
486
  <a href="${dashboardUrl}" style="color:#2563eb;text-decoration:none;">Open dashboard</a>
487
+ <span style="color:#9ca3af;"> · </span>
488
+ <a href="${reportSettingsUrl}" style="color:#2563eb;text-decoration:none;">Edit subscription settings</a>
489
+ </p>
490
+ <p style="margin:6px 0 0;color:#6b7280;font-size:12px;line-height:1.45;">
491
+ Change recipients, delivery time, filters, or turn this report on/off.
448
492
  </p>
449
493
  </body>
450
494
  </html>`;
@@ -458,6 +502,7 @@ function renderReportText(
458
502
  `Daily dashboard report: ${snapshot.title}`,
459
503
  `Date: ${reportDate(snapshot)}`,
460
504
  `Open dashboard: ${snapshot.dashboardUrl}`,
505
+ `Edit subscription settings: ${snapshot.reportSettingsUrl}`,
461
506
  ];
462
507
  if (!options.screenshotAttached) {
463
508
  lines.push("Dashboard image unavailable for this run.");
@@ -480,7 +525,7 @@ export async function sendDashboardReportSubscription(
480
525
  dashboardUrl: string;
481
526
  recipientCount: number;
482
527
  screenshotAttached: boolean;
483
- screenshotMode: "full" | "compact" | "none";
528
+ screenshotMode: "full" | "full-lightweight" | "none";
484
529
  screenshotError?: string;
485
530
  }> {
486
531
  const snapshot = await collectReportSnapshot(sub);
@@ -166,10 +166,13 @@ const REPLAY_RECORDING_DATE_SQL = "substr(started_at, 1, 10)";
166
166
  const REPLAY_TIME_RANGE_FILTER = dashboardTimeRangeFilter(
167
167
  REPLAY_RECORDING_DATE_SQL,
168
168
  );
169
- const REPLAY_RECORDING_FILTER = `chunk_count > 0 AND event_count > 0 AND user_id LIKE '%@%' AND ${REPLAY_TIME_RANGE_FILTER} AND ${DASHBOARD_EMAIL_FILTER}`;
169
+ const REPLAY_VISITOR_EMAIL_SQL =
170
+ "COALESCE(NULLIF(CASE WHEN lower(coalesce(user_id, '')) LIKE '%@%' THEN user_id ELSE '' END, ''), NULLIF(CASE WHEN lower(coalesce(user_key, '')) LIKE '%@%' THEN user_key ELSE '' END, ''))";
171
+ const REPLAY_EMAIL_FILTER = `('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(${REPLAY_VISITOR_EMAIL_SQL}, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(${REPLAY_VISITOR_EMAIL_SQL}, '')) LIKE '%@builder.io'))`;
172
+ const REPLAY_RECORDING_FILTER = `chunk_count > 0 AND event_count > 0 AND ${REPLAY_VISITOR_EMAIL_SQL} IS NOT NULL AND ${REPLAY_TIME_RANGE_FILTER} AND ${REPLAY_EMAIL_FILTER}`;
170
173
  const REPLAY_SESSIONS_SQL = `SELECT COUNT(*) AS count FROM session_recordings WHERE ${REPLAY_RECORDING_FILTER}`;
171
174
  const REPLAY_CHUNKS_OVER_TIME_SQL = `SELECT ${REPLAY_RECORDING_DATE_SQL} AS date, SUM(chunk_count) AS count FROM session_recordings WHERE ${REPLAY_RECORDING_FILTER} GROUP BY ${REPLAY_RECORDING_DATE_SQL} ORDER BY date`;
172
- const RECENT_REPLAY_SESSIONS_SQL = `SELECT id AS recording_id, session_id, COALESCE(NULLIF(app, ''), NULLIF(template, ''), 'unknown') AS app, COALESCE(NULLIF(user_id, ''), NULLIF(user_key, ''), NULLIF(anonymous_id, ''), 'anonymous') AS visitor, chunk_count AS chunks, event_count AS events, started_at, COALESCE(ended_at, last_ingested_at, started_at) AS last_seen, '/sessions/' || id AS href FROM session_recordings WHERE ${REPLAY_RECORDING_FILTER} ORDER BY last_seen DESC LIMIT 25`;
175
+ const RECENT_REPLAY_SESSIONS_SQL = `SELECT id AS recording_id, session_id, COALESCE(NULLIF(app, ''), NULLIF(template, ''), 'unknown') AS app, ${REPLAY_VISITOR_EMAIL_SQL} AS visitor, chunk_count AS chunks, event_count AS events, started_at, COALESCE(ended_at, last_ingested_at, started_at) AS last_seen, '/sessions/' || id AS href FROM session_recordings WHERE ${REPLAY_RECORDING_FILTER} ORDER BY last_seen DESC LIMIT 25`;
173
176
  export const FIRST_PARTY_DASHBOARD_FILTERS: FirstPartyDashboardFilter[] = [
174
177
  {
175
178
  id: "timeRange",
@@ -69,9 +69,33 @@ const MCP_DATA_SOURCE_TOKENS = [
69
69
  "stripe",
70
70
  ];
71
71
 
72
+ function normalizeActionToolName(name: string): string {
73
+ return name
74
+ .trim()
75
+ .toLowerCase()
76
+ .replace(/[\s_]+/g, "-")
77
+ .replace(/-+/g, "-");
78
+ }
79
+
80
+ function isToolName(name: string, expected: string): boolean {
81
+ return normalizeActionToolName(name) === expected;
82
+ }
83
+
84
+ function isDataQueryActionName(name: string): boolean {
85
+ return DATA_QUERY_ACTIONS.has(normalizeActionToolName(name));
86
+ }
87
+
88
+ function isCorpusSourceActionName(name: string): boolean {
89
+ return CORPUS_SOURCE_ACTIONS.has(normalizeActionToolName(name));
90
+ }
91
+
92
+ function isCorpusReductionActionName(name: string): boolean {
93
+ return CORPUS_REDUCTION_ACTIONS.has(normalizeActionToolName(name));
94
+ }
95
+
72
96
  function isMcpDataSourceTool(name: string): boolean {
73
- if (!name.startsWith("mcp__")) return false;
74
97
  const normalized = name.toLowerCase();
98
+ if (!normalized.startsWith("mcp__")) return false;
75
99
  return MCP_DATA_SOURCE_TOKENS.some((token) => normalized.includes(token));
76
100
  }
77
101
 
@@ -94,13 +118,13 @@ function getRunCodeBridgeToolNames(content: string | undefined): string[] {
94
118
 
95
119
  function hasRunCodeDataQueryAttempt(content: string | undefined): boolean {
96
120
  return getRunCodeBridgeToolNames(content).some(
97
- (name) => DATA_QUERY_ACTIONS.has(name) || isMcpDataSourceTool(name),
121
+ (name) => isDataQueryActionName(name) || isMcpDataSourceTool(name),
98
122
  );
99
123
  }
100
124
 
101
125
  function hasRunCodeCorpusWorkflowAttempt(content: string | undefined): boolean {
102
126
  return getRunCodeBridgeToolNames(content).some(
103
- (name) => CORPUS_SOURCE_ACTIONS.has(name) || isCorpusCapableMcpTool(name),
127
+ (name) => isCorpusSourceActionName(name) || isCorpusCapableMcpTool(name),
104
128
  );
105
129
  }
106
130
 
@@ -208,7 +232,7 @@ const UNSUPPORTED_RESULT_CLAIM =
208
232
  /(?:\b\d[\d,.]*(?:\.\d+)?\s*(?:%|percent|users?|customers?|accounts?|sessions?|events?|deals?|tickets?|issues?|calls?|messages?|signups?|pageviews?)\b|\$\s*\d|\b(?:data|query|results?)\s+(?:shows?|showed|indicates?|returned|found)\b|\b(?:i found|the top|the bottom|highest|lowest|increased|decreased|grew|declined|converted|churned|retained|averaged|total(?:ed)?|count(?:ed)?)\b)/i;
209
233
 
210
234
  const SAFE_NO_DATA_RESPONSE =
211
- /\b(?:i can't|i cannot|can't retrieve|cannot retrieve|couldn't retrieve|unable to retrieve|don't have access|do not have access|not configured|missing credentials?|need (?:a|the)? ?data source|need to know which source|which source|which data source|clarify|can you|once (?:that'?s|it is) (?:connected|configured|available)|no data source|without a successful|before (?:i|we) can (?:calculate|report|answer|analyze)|i need to query)\b/i;
235
+ /\b(?:i can't|i cannot|can't retrieve|cannot retrieve|couldn't retrieve|unable to retrieve|don't have access|do not have access|not configured|missing credentials?|need (?:a|the)? ?data source|need to know which source|which source|which data source|clarify|can you|once (?:that'?s|it is) (?:connected|configured|available)|no data source|without a successful|query failed|source query failed|sql failed|error running|before (?:i|we) can (?:calculate|report|answer|analyze)|i need to query)\b/i;
212
236
 
213
237
  export function isSafeNoDataAnalyticsResponse(text: string): boolean {
214
238
  const trimmed = text.trim();
@@ -358,10 +382,10 @@ export function hasIncompleteDataEvidence(
358
382
  const name = String(result.name ?? "");
359
383
  if (
360
384
  name &&
361
- !DATA_QUERY_ACTIONS.has(name) &&
385
+ !isDataQueryActionName(name) &&
362
386
  !isMcpDataSourceTool(name) &&
363
- name !== "run-code" &&
364
- name !== "provider-api-request"
387
+ !isToolName(name, "run-code") &&
388
+ !isToolName(name, "provider-api-request")
365
389
  ) {
366
390
  return false;
367
391
  }
@@ -419,11 +443,50 @@ export function hasDataQueryAttempt(
419
443
  if (result.isError) return false;
420
444
  if (isProviderErrorOnlyContent(result.content)) return false;
421
445
  const name = String(result.name ?? "");
422
- if (name === "run-code") return hasRunCodeDataQueryAttempt(result.content);
423
- return DATA_QUERY_ACTIONS.has(name) || isMcpDataSourceTool(name);
446
+ if (isToolName(name, "run-code")) {
447
+ return hasRunCodeDataQueryAttempt(result.content);
448
+ }
449
+ return isDataQueryActionName(name) || isMcpDataSourceTool(name);
424
450
  });
425
451
  }
426
452
 
453
+ function isFailedDataQueryAttempt(result: {
454
+ name?: string;
455
+ isError?: boolean;
456
+ content?: string;
457
+ }): boolean {
458
+ const name = String(result.name ?? "");
459
+ const isDataQuery =
460
+ isDataQueryActionName(name) ||
461
+ isMcpDataSourceTool(name) ||
462
+ (isToolName(name, "run-code") &&
463
+ hasRunCodeDataQueryAttempt(result.content));
464
+ if (!isDataQuery) return false;
465
+ return result.isError === true || isProviderErrorOnlyContent(result.content);
466
+ }
467
+
468
+ function compactToolFailure(content: string | undefined): string {
469
+ const trimmed = String(content ?? "").trim();
470
+ if (!trimmed) return "the tool returned an error result without details";
471
+ const max = 900;
472
+ return trimmed.length > max ? `${trimmed.slice(0, max)}...` : trimmed;
473
+ }
474
+
475
+ export function failedDataQueryAttemptMessage(
476
+ toolResults:
477
+ | Array<{ name?: string; isError?: boolean; content?: string }>
478
+ | undefined,
479
+ ): string | null {
480
+ const failed = (toolResults ?? []).find(isFailedDataQueryAttempt);
481
+ if (!failed) return null;
482
+ const name = String(failed.name ?? "data-source query");
483
+ return (
484
+ `I did try \`${name}\`, but it did not return a successful data result: ` +
485
+ compactToolFailure(failed.content) +
486
+ "\n\nI need to fix and rerun that query, or report that exact source error instead of giving numbers."
487
+ );
488
+ }
489
+
427
490
  export function hasCorpusWorkflowAttempt(
428
491
  toolResults:
429
492
  | Array<{ name?: string; isError?: boolean; content?: string }>
@@ -433,8 +496,8 @@ export function hasCorpusWorkflowAttempt(
433
496
  if (result.isError) return false;
434
497
  if (isProviderErrorOnlyContent(result.content)) return false;
435
498
  const name = String(result.name ?? "");
436
- if (CORPUS_SOURCE_ACTIONS.has(name)) return true;
437
- if (name === "run-code") {
499
+ if (isCorpusSourceActionName(name)) return true;
500
+ if (isToolName(name, "run-code")) {
438
501
  return hasRunCodeCorpusWorkflowAttempt(result.content);
439
502
  }
440
503
 
@@ -453,10 +516,7 @@ export function hasFailedCorpusWorkflowEvidence(
453
516
  ): boolean {
454
517
  return (toolResults ?? []).some((result) => {
455
518
  const name = String(result.name ?? "");
456
- if (
457
- !CORPUS_SOURCE_ACTIONS.has(name) &&
458
- !CORPUS_REDUCTION_ACTIONS.has(name)
459
- ) {
519
+ if (!isCorpusSourceActionName(name) && !isCorpusReductionActionName(name)) {
460
520
  return false;
461
521
  }
462
522
  if (result.isError) return true;
@@ -576,19 +636,20 @@ function actionEvidenceTextForSourceRecords(result: {
576
636
  content?: string;
577
637
  }): string {
578
638
  const name = String(result.name ?? "");
639
+ const normalizedName = normalizeActionToolName(name);
579
640
  const content = String(result.content ?? "");
580
641
  const parsed = tryParseJsonContent(content);
581
642
 
582
- if (name === "provider-corpus-job") {
643
+ if (normalizedName === "provider-corpus-job") {
583
644
  return corpusJobSourceEvidenceText(parsed);
584
645
  }
585
- if (name === "provider-api-request") {
646
+ if (normalizedName === "provider-api-request") {
586
647
  return providerRequestEvidenceText(parsed);
587
648
  }
588
- if (name === "query-staged-dataset") {
649
+ if (normalizedName === "query-staged-dataset") {
589
650
  return queryStagedDatasetEvidenceText(parsed);
590
651
  }
591
- if (name === "gong-calls") {
652
+ if (normalizedName === "gong-calls") {
592
653
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
593
654
  return "";
594
655
  }
@@ -600,7 +661,7 @@ function actionEvidenceTextForSourceRecords(result: {
600
661
  transcripts: record.transcripts,
601
662
  });
602
663
  }
603
- if (name === "run-code") {
664
+ if (normalizedName === "run-code") {
604
665
  return content;
605
666
  }
606
667
  if (isCorpusCapableMcpTool(name)) {
@@ -0,0 +1,79 @@
1
+ import { listFileUploadProviders } from "@agent-native/core/file-upload";
2
+ import {
3
+ resolveHasBuilderPrivateKey,
4
+ runWithRequestContext,
5
+ } from "@agent-native/core/server";
6
+
7
+ /**
8
+ * Session replay chunk bytes are stored through the framework file-upload /
9
+ * private-blob path (Builder.io CDN by default, or an S3-compatible bucket).
10
+ * In production we require a configured provider — storing replay chunks inline
11
+ * in SQL is a local/dev-only fallback. This mirrors the Clips video-storage
12
+ * contract so replay capture fails loudly (and the UI can prompt setup) instead
13
+ * of silently recording empty, unplayable sessions.
14
+ */
15
+ export const REPLAY_STORAGE_SETUP_REQUIRED_REASON =
16
+ "Session replay storage is not connected yet. Connect Builder.io or configure S3-compatible storage to record replays.";
17
+
18
+ function appDatabaseUrl(): string {
19
+ const appName = process.env.APP_NAME?.toUpperCase().replace(/-/g, "_");
20
+ if (appName) {
21
+ const appUrl = process.env[`${appName}_DATABASE_URL`];
22
+ if (appUrl) return appUrl;
23
+ }
24
+ return process.env.DATABASE_URL || process.env.NETLIFY_DATABASE_URL || "";
25
+ }
26
+
27
+ function isLikelyLocalDatabase(): boolean {
28
+ const url = appDatabaseUrl();
29
+ return url === "" || url.startsWith("file:") || !url.includes("://");
30
+ }
31
+
32
+ export function requiresConfiguredReplayStorage(): boolean {
33
+ return process.env.NODE_ENV === "production" || !isLikelyLocalDatabase();
34
+ }
35
+
36
+ export interface ReplayStorageResolveContext {
37
+ userEmail?: string;
38
+ orgId?: string | null;
39
+ }
40
+
41
+ /**
42
+ * Whether a durable upload provider (non-inline) is configured for the given
43
+ * owner/org scope. Iterates registered providers (e.g. S3) first, then falls
44
+ * back to a resolvable Builder.io private key. When a scope is supplied the
45
+ * check runs inside that user/org request context so org-scoped `app_secrets`
46
+ * credentials resolve — exactly the scope the anonymous replay-ingest path
47
+ * lacks on its own.
48
+ */
49
+ export async function hasRequestReplayStorage(
50
+ context?: ReplayStorageResolveContext,
51
+ ): Promise<boolean> {
52
+ const resolve = async () => {
53
+ for (const provider of listFileUploadProviders()) {
54
+ if (provider.id === "builder") continue;
55
+ if (provider.isConfigured()) return true;
56
+ if (provider.isConfiguredForRequest) {
57
+ try {
58
+ if (await provider.isConfiguredForRequest()) return true;
59
+ } catch {
60
+ // Treat a failed scoped lookup as not configured.
61
+ }
62
+ }
63
+ }
64
+
65
+ try {
66
+ return await resolveHasBuilderPrivateKey();
67
+ } catch {
68
+ return false;
69
+ }
70
+ };
71
+
72
+ if (context?.userEmail) {
73
+ return runWithRequestContext(
74
+ { userEmail: context.userEmail, orgId: context.orgId ?? undefined },
75
+ resolve,
76
+ );
77
+ }
78
+ return resolve();
79
+ }