@agent-native/core 0.189.0 → 0.190.0-nightly-20260925211446

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 (233) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/analysis-workspace/SKILL.md +10 -0
  3. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +1 -1
  4. package/corpus/templates/analytics/AGENTS.md +11 -22
  5. package/corpus/templates/analytics/actions/list-data-dictionary.ts +7 -9
  6. package/corpus/templates/analytics/actions/search-analytics-query-catalog.ts +1 -1
  7. package/corpus/templates/analytics/app/components/AnalyticsReviewArtifactPreview.tsx +201 -0
  8. package/corpus/templates/analytics/app/i18n/en-US.ts +2 -0
  9. package/corpus/templates/analytics/app/pages/Settings.tsx +50 -3
  10. package/corpus/templates/analytics/changelog/2026-09-25-first-run-onboarding-records-setup-choices-and-builder-conne.md +6 -0
  11. package/corpus/templates/analytics/changelog/2026-09-25-human-review-previews-analytics-dashboards-as-real-charts.md +6 -0
  12. package/corpus/templates/analytics/changelog/2026-09-25-org-admins-can-review-agent-runs-from-settings.md +6 -0
  13. package/corpus/templates/analytics/changelog/2026-09-25-proactive-query-context.md +6 -0
  14. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +98 -2
  15. package/corpus/templates/analytics/server/lib/analytics-agent-context.ts +557 -0
  16. package/corpus/templates/analytics/server/lib/analytics-query-catalog.ts +217 -46
  17. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +2 -2
  18. package/corpus/templates/analytics/server/lib/dashboards-store.ts +39 -5
  19. package/corpus/templates/analytics/server/lib/data-dictionary-context.ts +4 -0
  20. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +268 -2
  21. package/corpus/templates/analytics/server/plugins/agent-chat.ts +44 -9
  22. package/corpus/templates/calendar/AGENTS.md +5 -0
  23. package/corpus/templates/calendar/CHANGELOG.md +6 -0
  24. package/corpus/templates/calendar/actions/get-event-rules-status.ts +47 -0
  25. package/corpus/templates/calendar/actions/list-events.ts +50 -4
  26. package/corpus/templates/calendar/actions/undo-calendar-event-rule.ts +229 -0
  27. package/corpus/templates/calendar/actions/update-settings.ts +8 -0
  28. package/corpus/templates/calendar/app/i18n/en-US.ts +27 -0
  29. package/corpus/templates/calendar/app/pages/Settings.tsx +196 -1
  30. package/corpus/templates/calendar/changelog/2026-09-25-calendar-feeds-with-the-same-url-keep-their-own-names-and-co.md +6 -0
  31. package/corpus/templates/calendar/changelog/2026-09-25-calendar-invitation-rules-surface-expired-connections-and-un.md +6 -0
  32. package/corpus/templates/calendar/changelog/2026-09-25-restore-locally-hidden-events-from-activity-after-disconnect.md +6 -0
  33. package/corpus/templates/calendar/server/lib/calendar-settings.ts +44 -10
  34. package/corpus/templates/calendar/server/lib/google-api.ts +15 -1
  35. package/corpus/templates/calendar/server/lib/google-calendar.ts +1 -1
  36. package/corpus/templates/calendar/server/plugins/event-rules.ts +628 -0
  37. package/corpus/templates/calendar/shared/api.ts +13 -0
  38. package/corpus/templates/calendar/shared/settings.ts +71 -1
  39. package/corpus/templates/clips/actions/create-recording.ts +83 -2
  40. package/corpus/templates/clips/actions/finalize-recording.ts +610 -110
  41. package/corpus/templates/clips/actions/get-recording-player-data.ts +6 -0
  42. package/corpus/templates/clips/actions/import-loom-recording.ts +2 -0
  43. package/corpus/templates/clips/actions/lib/create-recording-schema.ts +7 -0
  44. package/corpus/templates/clips/actions/lib/loom-import-job.ts +15 -1
  45. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +126 -26
  46. package/corpus/templates/clips/app/hooks/use-drop-video-upload.ts +75 -15
  47. package/corpus/templates/clips/app/routes/_app.r.$recordingId.tsx +5 -1
  48. package/corpus/templates/clips/app/routes/record.tsx +134 -40
  49. package/corpus/templates/clips/chrome-extension/src/background.ts +3 -0
  50. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +73 -16
  51. package/corpus/templates/clips/desktop/src/lib/recorder.ts +188 -18
  52. package/corpus/templates/clips/desktop/src/lib/recording-request.ts +1 -0
  53. package/corpus/templates/clips/server/db/schema.ts +4 -0
  54. package/corpus/templates/clips/server/jobs/media-verification.ts +78 -4
  55. package/corpus/templates/clips/server/jobs/recording-failure-backfill.ts +142 -0
  56. package/corpus/templates/clips/server/lib/media-verification-state.ts +29 -0
  57. package/corpus/templates/clips/server/lib/post-finalize-dispatch.ts +2 -0
  58. package/corpus/templates/clips/server/lib/recording-failures.ts +111 -0
  59. package/corpus/templates/clips/server/lib/recordings.ts +6 -1
  60. package/corpus/templates/clips/server/lib/s3-upload-provider.ts +8 -3
  61. package/corpus/templates/clips/server/lib/upload-lease.ts +25 -6
  62. package/corpus/templates/clips/server/plugins/calendar-jobs.ts +2 -0
  63. package/corpus/templates/clips/server/plugins/db.ts +18 -0
  64. package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +48 -5
  65. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/abort.post.ts +322 -31
  66. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +223 -61
  67. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/interrupt.post.ts +62 -4
  68. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/reset-chunks.post.ts +74 -7
  69. package/corpus/templates/clips/shared/recording-core.ts +27 -0
  70. package/corpus/templates/design/actions/get-design.ts +35 -6
  71. package/corpus/templates/mail/.agents/skills/inbox-automations/SKILL.md +4 -0
  72. package/corpus/templates/mail/actions/consolidate-ai-filter-rules.ts +41 -0
  73. package/corpus/templates/mail/actions/get-ai-priority.ts +48 -8
  74. package/corpus/templates/mail/actions/record-ai-priority-feedback.ts +69 -0
  75. package/corpus/templates/mail/actions/update-mail-preferences.ts +2 -0
  76. package/corpus/templates/mail/app/components/email/EmailList.tsx +179 -32
  77. package/corpus/templates/mail/app/components/email/EmailListItem.tsx +97 -9
  78. package/corpus/templates/mail/app/components/email/EmailThread.tsx +55 -1
  79. package/corpus/templates/mail/app/components/email/ImportanceFeedbackMenu.tsx +52 -0
  80. package/corpus/templates/mail/app/components/email/compose-autocomplete.ts +2 -1
  81. package/corpus/templates/mail/app/components/layout/AppLayout.tsx +219 -188
  82. package/corpus/templates/mail/app/components/onboarding/AiInboxSetup.tsx +338 -0
  83. package/corpus/templates/mail/app/components/settings/AiFilterSection.tsx +597 -781
  84. package/corpus/templates/mail/app/components/settings/AiRulePromptField.tsx +37 -0
  85. package/corpus/templates/mail/app/global.css +42 -26
  86. package/corpus/templates/mail/app/hooks/use-ai-priority-feedback.ts +43 -0
  87. package/corpus/templates/mail/app/hooks/use-automations.ts +23 -0
  88. package/corpus/templates/mail/app/i18n/en-US.ts +58 -3
  89. package/corpus/templates/mail/app/pages/InboxPage.tsx +47 -10
  90. package/corpus/templates/mail/changelog/2026-09-25-editing-importance-rules-preserves-disabled-instructions-and.md +6 -0
  91. package/corpus/templates/mail/changelog/2026-09-25-handle-astral-unicode-letters-in-autocomplete-word-boundarie.md +6 -0
  92. package/corpus/templates/mail/changelog/2026-09-25-jev-priority-feedback-and-inbox-settings.md +6 -0
  93. package/corpus/templates/mail/changelog/2026-09-25-keep-existing-ai-filter-rules-intact-when-saving-a-prompt.md +6 -0
  94. package/corpus/templates/mail/server/lib/automation-engine.ts +21 -12
  95. package/corpus/templates/mail/server/lib/automations.ts +100 -1
  96. package/corpus/templates/mail/shared/types.ts +3 -0
  97. package/corpus/templates/plan/app/i18n/en-US.ts +6 -0
  98. package/corpus/templates/plan/app/lib/plan-invite-suggestion.ts +43 -0
  99. package/corpus/templates/plan/app/pages/PlansPage.tsx +249 -4
  100. package/corpus/templates/slides/actions/create-deck.ts +11 -1
  101. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +117 -1
  102. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +373 -330
  103. package/corpus/templates/slides/app/components/design-system/builder-index-response.ts +36 -5
  104. package/corpus/templates/slides/app/components/editor/BlockBubbleMenu.tsx +21 -53
  105. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +347 -671
  106. package/corpus/templates/slides/app/components/editor/bullet-editing.ts +55 -8
  107. package/corpus/templates/slides/app/components/editor/in-place-text-session.ts +2082 -0
  108. package/corpus/templates/slides/app/components/editor/list-editing.ts +174 -16
  109. package/corpus/templates/slides/app/components/editor/rich-text-selection.ts +497 -62
  110. package/corpus/templates/slides/app/components/editor/slide-text-targets.ts +102 -20
  111. package/corpus/templates/slides/app/context/DeckContext.tsx +9 -4
  112. package/corpus/templates/slides/app/global.css +7 -219
  113. package/corpus/templates/slides/app/i18n/en-US.ts +3 -0
  114. package/corpus/templates/slides/app/lib/sanitize-slide-html.ts +7 -0
  115. package/corpus/templates/slides/app/lib/slide-image-replacement.ts +203 -1
  116. package/corpus/templates/slides/app/lib/slide-source-map.ts +43 -0
  117. package/corpus/templates/slides/app/pages/DeckEditor.tsx +182 -4
  118. package/corpus/templates/slides/app/routes/settings.tsx +33 -2
  119. package/corpus/templates/slides/changelog/2026-09-25-improve-slides-design-system-setup-feedback.md +6 -0
  120. package/corpus/templates/slides/changelog/2026-09-25-org-admins-can-review-agent-runs-from-settings.md +6 -0
  121. package/corpus/templates/slides/changelog/2026-09-25-text-on-a-slide-is-now-edited-in-place-clicking-into-it-chan.md +6 -0
  122. package/corpus/templates/slides/package.json +0 -12
  123. package/corpus/templates/slides/scripts/edit-fidelity/README.md +4 -2
  124. package/corpus/templates/slides/scripts/edit-fidelity/run.ts +12 -3
  125. package/corpus/templates/slides/vite.config.ts +1 -10
  126. package/dist/agent/jev-tool-prefetch.d.ts +29 -3
  127. package/dist/agent/jev-tool-prefetch.js +161 -34
  128. package/dist/agent/production-agent.d.ts +11 -0
  129. package/dist/agent/production-agent.js +39 -8
  130. package/dist/app-config/runtime.d.ts +1 -0
  131. package/dist/app-config/runtime.js +4 -0
  132. package/dist/app-config/schema.d.ts +1 -0
  133. package/dist/client/AgentSidebar.d.ts +3 -1
  134. package/dist/client/AgentSidebar.js +8 -12
  135. package/dist/client/analytics/index.d.ts +1 -1
  136. package/dist/client/analytics/index.js +1 -1
  137. package/dist/client/analytics.d.ts +2 -0
  138. package/dist/client/analytics.js +9 -0
  139. package/dist/client/api-path.js +5 -2
  140. package/dist/client/chat/run-recovery.d.ts +0 -3
  141. package/dist/client/chat/run-recovery.js +13 -23
  142. package/dist/client/index.d.ts +1 -1
  143. package/dist/client/index.js +1 -1
  144. package/dist/client/observability/ObservabilityDashboard.d.ts +3 -0
  145. package/dist/client/observability/ObservabilityDashboard.js +366 -71
  146. package/dist/client/observability/ObservabilityReviewSummaryButton.d.ts +4 -1
  147. package/dist/client/observability/ObservabilityReviewSummaryButton.js +13 -9
  148. package/dist/client/observability/OutputPreview.d.ts +9 -4
  149. package/dist/client/observability/OutputPreview.js +230 -52
  150. package/dist/client/onboarding/FirstRunOnboarding.js +67 -4
  151. package/dist/client/onboarding/use-onboarding.js +2 -0
  152. package/dist/client/sharing/ShareButton.d.ts +2 -0
  153. package/dist/client/sharing/ShareButton.js +1 -0
  154. package/dist/client/sharing/useShareButtonController.d.ts +1 -0
  155. package/dist/client/sharing/useShareButtonController.js +29 -8
  156. package/dist/collab/awareness.d.ts +2 -2
  157. package/dist/collab/struct-routes.d.ts +1 -1
  158. package/dist/deploy/build.js +9 -1
  159. package/dist/embeddings/index.d.ts +3 -1
  160. package/dist/embeddings/index.js +20 -10
  161. package/dist/jobs/sweep-hooks.d.ts +7 -0
  162. package/dist/jobs/sweep-hooks.js +28 -0
  163. package/dist/localization/default-messages.d.ts +1 -0
  164. package/dist/localization/default-messages.js +1 -0
  165. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  166. package/dist/notifications/routes.d.ts +1 -1
  167. package/dist/observability/actions/authorization.d.ts +1 -0
  168. package/dist/observability/actions/authorization.js +22 -0
  169. package/dist/observability/actions/get-observability-review-detail.d.ts +5 -0
  170. package/dist/observability/actions/get-observability-review-detail.js +10 -2
  171. package/dist/observability/actions/get-observability-review-summary-source.d.ts +2 -0
  172. package/dist/observability/actions/get-observability-review-summary-source.js +3 -2
  173. package/dist/observability/actions/save-observability-instruction-update.js +2 -1
  174. package/dist/observability/actions/save-observability-review-summary.js +21 -10
  175. package/dist/observability/migrations.js +25 -0
  176. package/dist/observability/reviews.d.ts +3 -1
  177. package/dist/observability/reviews.js +410 -53
  178. package/dist/observability/routes.d.ts +6 -6
  179. package/dist/observability/store.d.ts +15 -0
  180. package/dist/observability/store.js +131 -11
  181. package/dist/observability/types.d.ts +18 -0
  182. package/dist/org/handlers.d.ts +1 -1
  183. package/dist/org/index.d.ts +1 -1
  184. package/dist/progress/routes.d.ts +1 -1
  185. package/dist/provider-api/actions/custom-provider-registration.d.ts +5 -5
  186. package/dist/provider-api/actions/provider-api.d.ts +8 -8
  187. package/dist/secrets/routes.d.ts +3 -3
  188. package/dist/server/agent-chat/plugin-options.d.ts +4 -20
  189. package/dist/server/agent-chat/prompt-resources.d.ts +16 -0
  190. package/dist/server/agent-chat/prompt-resources.js +385 -22
  191. package/dist/server/agent-chat-plugin.d.ts +3 -1
  192. package/dist/server/agent-chat-plugin.js +149 -27
  193. package/dist/server/agent-engine-ollama-models-route.d.ts +1 -1
  194. package/dist/server/index.d.ts +3 -0
  195. package/dist/server/index.js +3 -0
  196. package/dist/server/prompts/framework-core-compact.js +1 -1
  197. package/dist/server/prompts/framework-core.js +1 -1
  198. package/dist/server/realtime-token.d.ts +1 -1
  199. package/dist/server/request-context.d.ts +6 -3
  200. package/dist/server/transcribe-voice.d.ts +1 -1
  201. package/dist/settings/org-settings.d.ts +4 -2
  202. package/dist/settings/org-settings.js +3 -3
  203. package/dist/settings/store.d.ts +3 -1
  204. package/dist/settings/store.js +8 -3
  205. package/dist/styles/agent-native.css +5 -0
  206. package/dist/templates/default/app/i18n/ar-SA.ts +1 -0
  207. package/dist/templates/default/app/i18n/de-DE.ts +1 -0
  208. package/dist/templates/default/app/i18n/en-US.ts +1 -0
  209. package/dist/templates/default/app/i18n/es-ES.ts +1 -0
  210. package/dist/templates/default/app/i18n/fr-FR.ts +1 -0
  211. package/dist/templates/default/app/i18n/hi-IN.ts +1 -0
  212. package/dist/templates/default/app/i18n/ja-JP.ts +1 -0
  213. package/dist/templates/default/app/i18n/ko-KR.ts +1 -0
  214. package/dist/templates/default/app/i18n/pt-BR.ts +1 -0
  215. package/dist/templates/default/app/i18n/zh-CN.ts +1 -0
  216. package/dist/templates/default/app/i18n/zh-TW.ts +1 -0
  217. package/dist/tracking/registry.js +11 -4
  218. package/package.json +4 -3
  219. package/src/templates/default/app/i18n/ar-SA.ts +1 -0
  220. package/src/templates/default/app/i18n/de-DE.ts +1 -0
  221. package/src/templates/default/app/i18n/en-US.ts +1 -0
  222. package/src/templates/default/app/i18n/es-ES.ts +1 -0
  223. package/src/templates/default/app/i18n/fr-FR.ts +1 -0
  224. package/src/templates/default/app/i18n/hi-IN.ts +1 -0
  225. package/src/templates/default/app/i18n/ja-JP.ts +1 -0
  226. package/src/templates/default/app/i18n/ko-KR.ts +1 -0
  227. package/src/templates/default/app/i18n/pt-BR.ts +1 -0
  228. package/src/templates/default/app/i18n/zh-CN.ts +1 -0
  229. package/src/templates/default/app/i18n/zh-TW.ts +1 -0
  230. package/corpus/templates/slides/app/components/editor/SlideBubbleMenu.tsx +0 -488
  231. package/corpus/templates/slides/app/components/editor/SlideInlineEditor.tsx +0 -589
  232. package/corpus/templates/slides/app/components/editor/SlideRichTextEditor.tsx +0 -1347
  233. package/corpus/templates/slides/app/components/editor/SlideSlashMenu.tsx +0 -373
@@ -761,15 +761,19 @@
761
761
  "chartType": "table",
762
762
  "source": "first-party",
763
763
  "width": 2,
764
- "sql": "WITH signup_identity AS (\n SELECT NULLIF(anonymous_id, '') AS anonymous_id,\n MIN(NULLIF(user_id, '')) AS signup_user_id\n FROM analytics_events\n WHERE event_name = 'signup'\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD'))))\n 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'))\n AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}'))\n AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n AND NULLIF(anonymous_id, '') IS NOT NULL\n AND NULLIF(user_id, '') IS NOT NULL\n GROUP BY NULLIF(anonymous_id, '')\n), raw_funnel_events AS (\n SELECT e.*,\n COALESCE(si.signup_user_id, NULLIF(e.user_id, ''), NULLIF(e.anonymous_id, '')) AS funnel_user_key,\n COALESCE(si.signup_user_id, NULLIF(e.user_id, '')) AS funnel_user_email\n FROM analytics_events e\n LEFT JOIN signup_identity si ON si.anonymous_id = NULLIF(e.anonymous_id, '')\n WHERE (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD'))))\n AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}'))\n AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n), funnel_events AS (\n SELECT e.*\n FROM raw_funnel_events e\n WHERE (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n), signup_cohort AS (\n SELECT funnel_user_key, MIN(timestamp::timestamptz) AS signup_at\n FROM funnel_events\n WHERE event_name = 'signup'\n AND funnel_user_key IS NOT NULL\n AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n GROUP BY funnel_user_key\n), cohort_events AS (\n SELECT e.*, c.signup_at\n FROM funnel_events e\n JOIN signup_cohort c ON c.funnel_user_key = e.funnel_user_key\n), views AS (SELECT COALESCE(NULLIF(properties::jsonb ->> 'flow', ''), 'unknown') AS flow, COALESCE(NULLIF(properties::jsonb ->> 'step_id', ''), 'unknown') AS step_id, COALESCE(NULLIF(properties::jsonb ->> 'step_index', ''), '999') AS step_index, COUNT(DISTINCT funnel_user_key) AS users_reached FROM funnel_events WHERE event_name = 'onboarding_step_viewed' AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides') GROUP BY 1, 2, 3), completions AS (SELECT COALESCE(NULLIF(properties::jsonb ->> 'flow', ''), 'unknown') AS flow, COALESCE(NULLIF(properties::jsonb ->> 'step_id', ''), 'unknown') AS step_id, COUNT(DISTINCT funnel_user_key) AS users_completed FROM funnel_events WHERE event_name = 'onboarding_step_completed' AND (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD')))) AND ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io')) AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}')) AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides') GROUP BY 1, 2) SELECT views.flow, views.step_id, views.step_index, views.users_reached, COALESCE(completions.users_completed, 0) AS users_completed, COALESCE(completions.users_completed::float / NULLIF(views.users_reached, 0), 0) AS completion_rate FROM views LEFT JOIN completions ON completions.flow = views.flow AND completions.step_id = views.step_id ORDER BY views.step_index, views.flow, views.step_id",
764
+ "sql": "WITH auth_identity_bridge AS (\n SELECT linked_email, MIN(auth_user_id) AS auth_user_id\n FROM (\n SELECT lower(COALESCE(\n CASE WHEN NULLIF(e.user_key, '') LIKE '%@%.%' THEN e.user_key END,\n CASE WHEN NULLIF(e.user_id, '') LIKE '%@%.%' THEN e.user_id END\n )) AS linked_email,\n NULLIF(e.properties::jsonb ->> 'auth_user_id', '') AS auth_user_id\n FROM analytics_events e\n WHERE (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD'))))\n AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}'))\n AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n ) AS identities\n WHERE linked_email IS NOT NULL\n AND auth_user_id IS NOT NULL\n GROUP BY linked_email\n HAVING COUNT(DISTINCT auth_user_id) = 1\n), scoped_onboarding_events AS (\n SELECT e.*,\n COALESCE(\n NULLIF(e.properties::jsonb ->> 'auth_user_id', ''),\n auth_identity_bridge.auth_user_id,\n NULLIF(e.user_key, ''),\n NULLIF(e.user_id, ''),\n NULLIF(e.anonymous_id, '')\n ) AS funnel_user_key,\n COALESCE(\n CASE WHEN NULLIF(e.user_id, '') LIKE '%@%.%' THEN e.user_id END,\n CASE WHEN NULLIF(e.user_key, '') LIKE '%@%.%' THEN e.user_key END,\n CASE WHEN NULLIF(e.properties::jsonb ->> 'auth_user_id', '') LIKE '%@%.%' THEN e.properties::jsonb ->> 'auth_user_id' END\n ) AS funnel_user_email\n FROM analytics_events e\n LEFT JOIN auth_identity_bridge ON auth_identity_bridge.linked_email = lower(COALESCE(\n CASE WHEN NULLIF(e.user_key, '') LIKE '%@%.%' THEN e.user_key END,\n CASE WHEN NULLIF(e.user_id, '') LIKE '%@%.%' THEN e.user_id END\n ))\n WHERE (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD'))))\n AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}'))\n AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n), onboarding_events AS (\n SELECT * FROM scoped_onboarding_events\n WHERE ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io'))\n AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%+autoz%'\n), viewers AS (\n SELECT DISTINCT funnel_user_key,\n COALESCE(NULLIF(properties::jsonb ->> 'flow', ''), 'unknown') AS flow,\n COALESCE(NULLIF(properties::jsonb ->> 'step_id', ''), 'unknown') AS step_id,\n COALESCE(NULLIF(properties::jsonb ->> 'step_index', ''), '999') AS step_index\n FROM onboarding_events\n WHERE event_name = 'onboarding_step_viewed'\n AND funnel_user_key IS NOT NULL\n), views AS (\n SELECT flow, step_id, step_index, COUNT(DISTINCT funnel_user_key) AS users_reached\n FROM viewers\n GROUP BY flow, step_id, step_index\n), user_outcomes AS (\n SELECT viewers.funnel_user_key, viewers.flow, viewers.step_id, viewers.step_index,\n MAX(CASE WHEN events.event_name = 'onboarding_step_completed' THEN 1 ELSE 0 END) AS completed,\n MAX(CASE WHEN events.event_name = 'onboarding_step_skipped' THEN 1 ELSE 0 END) AS skipped\n FROM viewers\n LEFT JOIN onboarding_events AS events\n ON events.funnel_user_key = viewers.funnel_user_key\n AND events.event_name IN ('onboarding_step_completed', 'onboarding_step_skipped')\n AND COALESCE(NULLIF(events.properties::jsonb ->> 'flow', ''), 'unknown') = viewers.flow\n AND COALESCE(NULLIF(events.properties::jsonb ->> 'step_id', ''), 'unknown') = viewers.step_id\n GROUP BY viewers.funnel_user_key, viewers.flow, viewers.step_id, viewers.step_index\n), outcomes AS (\n SELECT flow, step_id, step_index,\n SUM(completed) AS users_completed,\n SUM(skipped) AS users_skipped,\n SUM(CASE WHEN completed = 1 OR skipped = 1 THEN 1 ELSE 0 END) AS users_with_outcome\n FROM user_outcomes\n GROUP BY flow, step_id, step_index\n)\nSELECT views.flow, views.step_id, views.step_index, views.users_reached,\n COALESCE(outcomes.users_completed, 0) AS users_completed,\n COALESCE(outcomes.users_skipped, 0) AS users_skipped,\n views.users_reached - COALESCE(outcomes.users_with_outcome, 0) AS users_no_recorded_outcome,\n COALESCE(outcomes.users_completed::float / NULLIF(views.users_reached, 0), 0) AS completion_rate\nFROM views\nLEFT JOIN outcomes ON outcomes.flow = views.flow AND outcomes.step_id = views.step_id AND outcomes.step_index = views.step_index\nORDER BY views.step_index, views.flow, views.step_id",
765
765
  "config": {
766
766
  "xKey": "step_id",
767
- "description": "Distinct visitors who reached and completed each first-run or checklist onboarding step, ordered by the emitted step index.",
767
+ "description": "Distinct people who viewed, completed, skipped, or had no recorded outcome for each first-run or checklist step. Completion and skip counts can overlap if a person retries; no outcome means neither event was recorded. This is per-step reach, not a sequential funnel. +autoz identities are excluded.",
768
768
  "columns": [
769
769
  {
770
770
  "key": "step_index",
771
771
  "label": "Order"
772
772
  },
773
+ {
774
+ "key": "flow",
775
+ "label": "Flow"
776
+ },
773
777
  {
774
778
  "key": "step_id",
775
779
  "label": "Step"
@@ -784,6 +788,16 @@
784
788
  "label": "Completed",
785
789
  "format": "number"
786
790
  },
791
+ {
792
+ "key": "users_skipped",
793
+ "label": "Skipped",
794
+ "format": "number"
795
+ },
796
+ {
797
+ "key": "users_no_recorded_outcome",
798
+ "label": "No outcome",
799
+ "format": "number"
800
+ },
787
801
  {
788
802
  "key": "completion_rate",
789
803
  "label": "Completion",
@@ -792,6 +806,88 @@
792
806
  ]
793
807
  }
794
808
  },
809
+ {
810
+ "id": "onboarding-setup-choice",
811
+ "title": "First-run Setup Choices",
812
+ "chartType": "table",
813
+ "source": "first-party",
814
+ "width": 3,
815
+ "sql": "WITH auth_identity_bridge AS (\n SELECT linked_email, MIN(auth_user_id) AS auth_user_id\n FROM (\n SELECT lower(COALESCE(\n CASE WHEN NULLIF(e.user_key, '') LIKE '%@%.%' THEN e.user_key END,\n CASE WHEN NULLIF(e.user_id, '') LIKE '%@%.%' THEN e.user_id END\n )) AS linked_email,\n NULLIF(e.properties::jsonb ->> 'auth_user_id', '') AS auth_user_id\n FROM analytics_events e\n WHERE (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD'))))\n AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}'))\n AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n ) AS identities\n WHERE linked_email IS NOT NULL\n AND auth_user_id IS NOT NULL\n GROUP BY linked_email\n HAVING COUNT(DISTINCT auth_user_id) = 1\n), scoped_onboarding_events AS (\n SELECT e.*,\n COALESCE(\n NULLIF(e.properties::jsonb ->> 'auth_user_id', ''),\n auth_identity_bridge.auth_user_id,\n NULLIF(e.user_key, ''),\n NULLIF(e.user_id, ''),\n NULLIF(e.anonymous_id, '')\n ) AS funnel_user_key,\n COALESCE(\n CASE WHEN NULLIF(e.user_id, '') LIKE '%@%.%' THEN e.user_id END,\n CASE WHEN NULLIF(e.user_key, '') LIKE '%@%.%' THEN e.user_key END,\n CASE WHEN NULLIF(e.properties::jsonb ->> 'auth_user_id', '') LIKE '%@%.%' THEN e.properties::jsonb ->> 'auth_user_id' END\n ) AS funnel_user_email\n FROM analytics_events e\n LEFT JOIN auth_identity_bridge ON auth_identity_bridge.linked_email = lower(COALESCE(\n CASE WHEN NULLIF(e.user_key, '') LIKE '%@%.%' THEN e.user_key END,\n CASE WHEN NULLIF(e.user_id, '') LIKE '%@%.%' THEN e.user_id END\n ))\n WHERE (event_date <= to_char(CURRENT_DATE, 'YYYY-MM-DD') AND ('{{timeRange}}' IN ('', 'all') OR ('{{timeRange}}' = '7d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '7 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '30d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '90d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '90 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '180d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '180 days', 'YYYY-MM-DD')) OR ('{{timeRange}}' = '365d' AND event_date >= to_char(CURRENT_DATE - INTERVAL '365 days', 'YYYY-MM-DD'))))\n AND ('{{appFilter}}' IN ('', 'all') OR lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) = lower('{{appFilter}}'))\n AND lower(COALESCE(NULLIF(template, ''), NULLIF(properties::jsonb ->> 'templateId', ''), NULLIF(properties::jsonb ->> 'agent_native_template', ''), NULLIF(properties::jsonb ->> 'agentNativeTemplate', ''), NULLIF(app, ''), NULLIF(properties::jsonb ->> 'agent_native_app', ''), NULLIF(properties::jsonb ->> 'agentNativeApp', ''), 'unknown')) IN ('analytics', 'assets', 'brain', 'calendar', 'chat', 'clips', 'content', 'design', 'dispatch', 'forms', 'mail', 'plan', 'slides')\n), onboarding_events AS (\n SELECT * FROM scoped_onboarding_events\n WHERE ('{{emailFilter}}' IN ('', 'all') OR ('{{emailFilter}}' = 'exclude_builder' AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%@builder.io') OR ('{{emailFilter}}' = 'only_builder' AND lower(coalesce(funnel_user_email, '')) LIKE '%@builder.io'))\n AND lower(coalesce(funnel_user_email, '')) NOT LIKE '%+autoz%'\n), choice_viewers AS (\n SELECT DISTINCT funnel_user_key\n FROM onboarding_events\n WHERE event_name = 'onboarding_step_viewed'\n AND COALESCE(NULLIF(properties::jsonb ->> 'flow', ''), '') = 'first_run'\n AND COALESCE(NULLIF(properties::jsonb ->> 'step_id', ''), '') = 'choice'\n AND funnel_user_key IS NOT NULL\n), choices AS (\n SELECT funnel_user_key,\n COALESCE(NULLIF(properties::jsonb ->> 'method_id', ''), 'unknown') AS method_id,\n NULLIF(properties::jsonb ->> 'onboarding_attempt_id', '') AS attempt_id,\n timestamp::timestamptz AS clicked_at,\n id\n FROM onboarding_events\n WHERE event_name = 'onboarding_method_clicked'\n AND COALESCE(NULLIF(properties::jsonb ->> 'flow', ''), '') = 'first_run'\n AND COALESCE(NULLIF(properties::jsonb ->> 'step_id', ''), '') = 'choice'\n AND funnel_user_key IS NOT NULL\n), ranked_choices AS (\n SELECT choices.*,\n ROW_NUMBER() OVER (PARTITION BY funnel_user_key ORDER BY clicked_at, id) AS choice_number\n FROM choices\n JOIN choice_viewers USING (funnel_user_key)\n), first_choice_summary AS (\n SELECT method_id, COUNT(DISTINCT funnel_user_key) AS first_choice_users\n FROM ranked_choices\n WHERE choice_number = 1\n GROUP BY method_id\n), selection_summary AS (\n SELECT method_id,\n COUNT(DISTINCT funnel_user_key) AS selected_users,\n COUNT(*) AS method_clicks,\n COUNT(DISTINCT attempt_id) AS selection_attempts\n FROM choices\n GROUP BY method_id\n), starts AS (\n SELECT funnel_user_key,\n COALESCE(NULLIF(properties::jsonb ->> 'method_id', ''), 'unknown') AS method_id,\n NULLIF(properties::jsonb ->> 'onboarding_attempt_id', '') AS attempt_id\n FROM onboarding_events\n WHERE event_name = 'onboarding_method_started'\n AND COALESCE(NULLIF(properties::jsonb ->> 'flow', ''), '') = 'first_run'\n AND COALESCE(NULLIF(properties::jsonb ->> 'step_id', ''), '') = 'choice'\n AND funnel_user_key IS NOT NULL\n), method_outcomes AS (\n SELECT funnel_user_key,\n COALESCE(NULLIF(properties::jsonb ->> 'method_id', ''), 'unknown') AS method_id,\n NULLIF(properties::jsonb ->> 'onboarding_attempt_id', '') AS attempt_id,\n COALESCE(NULLIF(properties::jsonb ->> 'outcome', ''), 'unknown') AS outcome\n FROM onboarding_events\n WHERE event_name = 'onboarding_method_outcome'\n AND COALESCE(NULLIF(properties::jsonb ->> 'flow', ''), '') = 'first_run'\n AND COALESCE(NULLIF(properties::jsonb ->> 'step_id', ''), '') = 'choice'\n AND funnel_user_key IS NOT NULL\n), attempts AS (\n SELECT starts.funnel_user_key, starts.method_id, starts.attempt_id,\n MAX(CASE WHEN method_outcomes.outcome IN ('connected', 'already_connected') THEN 1 ELSE 0 END) AS connected,\n MAX(CASE WHEN method_outcomes.outcome = 'failed' THEN 1 ELSE 0 END) AS failed,\n MAX(CASE WHEN method_outcomes.outcome = 'handoff_failed' THEN 1 ELSE 0 END) AS handoff_failed,\n MAX(CASE WHEN method_outcomes.outcome = 'settings_opened' THEN 1 ELSE 0 END) AS settings_opened,\n MAX(CASE WHEN method_outcomes.outcome IS NOT NULL THEN 1 ELSE 0 END) AS has_outcome\n FROM starts\n LEFT JOIN method_outcomes\n ON method_outcomes.funnel_user_key = starts.funnel_user_key\n AND method_outcomes.method_id = starts.method_id\n AND method_outcomes.attempt_id = starts.attempt_id\n GROUP BY starts.funnel_user_key, starts.method_id, starts.attempt_id\n), attempt_summary AS (\n SELECT method_id,\n COUNT(DISTINCT CASE WHEN connected = 1 THEN attempt_id END) AS connection_success_attempts,\n COUNT(DISTINCT CASE WHEN failed = 1 THEN attempt_id END) AS connection_failure_attempts,\n COUNT(DISTINCT CASE WHEN handoff_failed = 1 THEN attempt_id END) AS handoff_failure_attempts,\n COUNT(DISTINCT CASE WHEN connected = 1 THEN funnel_user_key END) AS connection_success_users,\n COUNT(DISTINCT CASE WHEN failed = 1 THEN funnel_user_key END) AS connection_failure_users,\n COUNT(DISTINCT CASE WHEN connected = 0 AND failed = 0 AND has_outcome = 0 THEN attempt_id END) AS connection_no_outcome_attempts,\n COUNT(DISTINCT CASE WHEN settings_opened = 1 THEN attempt_id END) AS settings_handoff_attempts\n FROM attempts\n GROUP BY method_id\n), method_list AS (\n SELECT 'builder_create_account' AS method_id, 'Create Builder.io account' AS method_label\n UNION ALL SELECT 'builder_sign_in', 'Sign in with Builder.io account'\n UNION ALL SELECT 'custom_keys', 'Configure custom keys'\n)\nSELECT method_list.method_id, method_list.method_label,\n (SELECT COUNT(DISTINCT funnel_user_key) FROM choice_viewers) AS choice_screen_viewers,\n COALESCE(first_choice_summary.first_choice_users, 0) AS first_choice_users,\n COALESCE(first_choice_summary.first_choice_users::float / NULLIF((SELECT COUNT(DISTINCT funnel_user_key) FROM choice_viewers), 0), 0) AS first_choice_rate,\n COALESCE(selection_summary.selected_users, 0) AS selected_users,\n COALESCE(selection_summary.method_clicks, 0) AS method_clicks,\n COALESCE(selection_summary.selection_attempts, 0) AS selection_attempts,\n COALESCE(attempt_summary.connection_success_attempts, 0) AS connection_success_attempts,\n COALESCE(attempt_summary.connection_failure_attempts, 0) AS connection_failure_attempts,\n COALESCE(attempt_summary.handoff_failure_attempts, 0) AS handoff_failure_attempts,\n COALESCE(attempt_summary.connection_success_users, 0) AS connection_success_users,\n COALESCE(attempt_summary.connection_failure_users, 0) AS connection_failure_users,\n COALESCE(attempt_summary.connection_no_outcome_attempts, 0) AS connection_no_outcome_attempts,\n COALESCE(attempt_summary.settings_handoff_attempts, 0) AS settings_handoff_attempts,\n attempt_summary.connection_success_attempts::float / NULLIF(attempt_summary.connection_success_attempts + attempt_summary.connection_failure_attempts, 0) AS connection_success_rate\nFROM method_list\nLEFT JOIN first_choice_summary ON first_choice_summary.method_id = method_list.method_id\nLEFT JOIN selection_summary ON selection_summary.method_id = method_list.method_id\nLEFT JOIN attempt_summary ON attempt_summary.method_id = method_list.method_id\nORDER BY method_list.method_id",
816
+ "config": {
817
+ "description": "Choice-screen viewers, first selected setup path, repeat selections, and linked Builder connection outcomes. Rates use choice-screen viewers or resolved Builder outcomes as their denominator; started attempts with no outcome are unknown or abandoned, not assumed failures. Handoff failures are reported separately from Builder connection failures. A successful Builder outcome confirms credentials connected, not that a new external account was created. Account-exists is counted as a failed create-account attempt. +autoz identities are excluded.",
818
+ "columns": [
819
+ {
820
+ "key": "method_label",
821
+ "label": "Setup choice"
822
+ },
823
+ {
824
+ "key": "choice_screen_viewers",
825
+ "label": "Choice viewers",
826
+ "format": "number"
827
+ },
828
+ {
829
+ "key": "first_choice_users",
830
+ "label": "First choice users",
831
+ "format": "number"
832
+ },
833
+ {
834
+ "key": "first_choice_rate",
835
+ "label": "First choice rate",
836
+ "format": "percent"
837
+ },
838
+ {
839
+ "key": "selected_users",
840
+ "label": "Ever selected",
841
+ "format": "number"
842
+ },
843
+ {
844
+ "key": "method_clicks",
845
+ "label": "Selections",
846
+ "format": "number"
847
+ },
848
+ {
849
+ "key": "connection_success_users",
850
+ "label": "Connected users",
851
+ "format": "number"
852
+ },
853
+ {
854
+ "key": "connection_failure_users",
855
+ "label": "Failed users",
856
+ "format": "number"
857
+ },
858
+ {
859
+ "key": "connection_success_attempts",
860
+ "label": "Connected attempts",
861
+ "format": "number"
862
+ },
863
+ {
864
+ "key": "connection_failure_attempts",
865
+ "label": "Failed attempts",
866
+ "format": "number"
867
+ },
868
+ {
869
+ "key": "handoff_failure_attempts",
870
+ "label": "Handoff failures",
871
+ "format": "number"
872
+ },
873
+ {
874
+ "key": "connection_no_outcome_attempts",
875
+ "label": "No outcome",
876
+ "format": "number"
877
+ },
878
+ {
879
+ "key": "connection_success_rate",
880
+ "label": "Resolved success rate",
881
+ "format": "percent"
882
+ },
883
+ {
884
+ "key": "settings_handoff_attempts",
885
+ "label": "Settings handoffs",
886
+ "format": "number"
887
+ }
888
+ ]
889
+ }
890
+ },
795
891
  {
796
892
  "id": "sharing-actions-by-app",
797
893
  "title": "Sharing Actions by App",