@agent-native/core 0.141.5 → 0.141.7

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 (235) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +3 -14
  3. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +20 -8
  4. package/corpus/templates/analytics/.agents/skills/prometheus/SKILL.md +5 -5
  5. package/corpus/templates/analytics/AGENTS.md +2 -2
  6. package/corpus/templates/analytics/actions/compose-dashboard.ts +1 -1
  7. package/corpus/templates/analytics/actions/get-first-party-analytics-health.ts +15 -1
  8. package/corpus/templates/analytics/actions/migrate-first-party-analytics-to-bigquery.ts +135 -0
  9. package/corpus/templates/analytics/actions/navigate.ts +1 -1
  10. package/corpus/templates/analytics/actions/query-agent-native-analytics.ts +2 -2
  11. package/corpus/templates/analytics/actions/update-dashboard.ts +0 -1
  12. package/corpus/templates/analytics/actions/view-screen.ts +0 -20
  13. package/corpus/templates/analytics/app/components/layout/Header.tsx +0 -1
  14. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +5 -5
  15. package/corpus/templates/analytics/app/i18n-data.ts +330 -90
  16. package/corpus/templates/analytics/app/pages/Agents.tsx +0 -3
  17. package/corpus/templates/analytics/app/pages/DataSources.tsx +66 -30
  18. package/corpus/templates/analytics/app/pages/Settings.tsx +22 -43
  19. package/corpus/templates/analytics/app/pages/settings/settings-search.ts +23 -20
  20. package/corpus/templates/analytics/app/routes/extensions._index.tsx +1 -1
  21. package/corpus/templates/analytics/app/routes/settings.$.tsx +1 -0
  22. package/corpus/templates/analytics/app/routes/team.tsx +1 -1
  23. package/corpus/templates/analytics/changelog/2026-08-05-historical-analytics-rollups-now-repair-automatically-after-.md +6 -0
  24. package/corpus/templates/analytics/changelog/2026-08-05-settings-no-longer-includes-an-unnecessary-about-section.md +6 -0
  25. package/corpus/templates/analytics/docs/schemas/first-party-analytics.md +1 -1
  26. package/corpus/templates/analytics/netlify.toml +4 -0
  27. package/corpus/templates/analytics/scripts/emit-netlify-dashboard-report-cron.ts +109 -0
  28. package/corpus/templates/analytics/scripts/first-party-analytics-bigquery.sql +104 -0
  29. package/corpus/templates/analytics/server/jobs/analytics-rollup-backfill.ts +342 -0
  30. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +0 -2
  31. package/corpus/templates/analytics/server/lib/analytics-rollup-lock.ts +8 -0
  32. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +11 -29
  33. package/corpus/templates/analytics/server/lib/dashboards-store.ts +55 -0
  34. package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +1058 -0
  35. package/corpus/templates/analytics/server/lib/first-party-analytics-health.ts +151 -46
  36. package/corpus/templates/analytics/server/lib/first-party-analytics-rollups.ts +15 -9
  37. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +59 -17
  38. package/corpus/templates/analytics/server/lib/real-data-actions.ts +1 -3
  39. package/corpus/templates/analytics/server/plugins/agent-chat.ts +2 -4
  40. package/corpus/templates/analytics/server/plugins/analytics-rollup-backfill-jobs.ts +61 -0
  41. package/corpus/templates/analytics/server/plugins/db.ts +58 -187
  42. package/corpus/templates/analytics/server/routes/api/analytics-rollups/backfill.post.ts +53 -0
  43. package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +8 -6
  44. package/corpus/templates/assets/.agents/skills/library-management/SKILL.md +16 -1
  45. package/corpus/templates/assets/AGENTS.md +1 -0
  46. package/corpus/templates/assets/actions/_helpers.ts +12 -0
  47. package/corpus/templates/assets/actions/generate-image.ts +9 -3
  48. package/corpus/templates/assets/actions/import-style-from-url.ts +115 -0
  49. package/corpus/templates/assets/app/hooks/use-navigation-state.ts +3 -1
  50. package/corpus/templates/assets/app/routes/extensions._index.tsx +1 -1
  51. package/corpus/templates/assets/app/routes/settings.$.tsx +1 -0
  52. package/corpus/templates/assets/app/routes/settings.tsx +5 -0
  53. package/corpus/templates/assets/app/routes/team.tsx +1 -1
  54. package/corpus/templates/assets/changelog/2026-08-05-brand-kits-can-import-a-website-s-hydrated-visual-language-a.md +6 -0
  55. package/corpus/templates/assets/changelog/2026-08-05-generated-images-now-hand-off-embeddable-previews-to-other-w.md +6 -0
  56. package/corpus/templates/assets/server/lib/generation.ts +58 -0
  57. package/corpus/templates/assets/shared/api.ts +12 -0
  58. package/corpus/templates/brain/app/routes/extensions._index.tsx +1 -1
  59. package/corpus/templates/brain/app/routes/team.tsx +1 -1
  60. package/corpus/templates/brain/app/routes.ts +1 -1
  61. package/corpus/templates/calendar/app/components/layout/AppLayout.tsx +3 -1
  62. package/corpus/templates/calendar/app/routes/_app.extensions._index.tsx +1 -1
  63. package/corpus/templates/calendar/app/routes/_app.settings.$.tsx +1 -0
  64. package/corpus/templates/calendar/app/routes/_app.team.tsx +1 -1
  65. package/corpus/templates/chat/app/components/layout/Layout.tsx +1 -1
  66. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +1 -1
  67. package/corpus/templates/chat/app/root.tsx +1 -1
  68. package/corpus/templates/chat/app/routes/agent.tsx +7 -7
  69. package/corpus/templates/chat/app/routes/extensions._index.tsx +1 -1
  70. package/corpus/templates/chat/app/routes/settings.$.tsx +1 -0
  71. package/corpus/templates/chat/app/routes/team.tsx +1 -1
  72. package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -1
  73. package/corpus/templates/clips/app/components/player/transcript-panel.tsx +1 -1
  74. package/corpus/templates/clips/app/components/recorder/storage-setup-card.tsx +1 -1
  75. package/corpus/templates/clips/app/routes/_app.extensions._index.tsx +1 -1
  76. package/corpus/templates/clips/app/routes/_app.settings.$.tsx +1 -0
  77. package/corpus/templates/clips/changelog/2026-08-05-clips-overlays-now-use-clean-surfaces-without-clipped-shadow.md +6 -0
  78. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +2 -2
  79. package/corpus/templates/clips/desktop/src/styles.css +3 -54
  80. package/corpus/templates/clips/desktop/src-tauri/src/notifications.rs +3 -3
  81. package/corpus/templates/content/app/routes/_app.extensions._index.tsx +1 -1
  82. package/corpus/templates/content/app/routes/_app.settings.$.tsx +1 -0
  83. package/corpus/templates/content/app/routes/_app.team.tsx +1 -1
  84. package/corpus/templates/content/e2e/global-setup.ts +1 -1
  85. package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +6 -1
  86. package/corpus/templates/design/actions/analyze-brand-assets.ts +7 -7
  87. package/corpus/templates/design/actions/get-design-system.ts +3 -1
  88. package/corpus/templates/design/actions/import-from-url.ts +6 -5
  89. package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +2 -2
  90. package/corpus/templates/design/app/routes/extensions._index.tsx +1 -1
  91. package/corpus/templates/design/app/routes/settings.$.tsx +1 -0
  92. package/corpus/templates/design/app/routes/team.tsx +1 -1
  93. package/corpus/templates/design/changelog/2026-08-05-missing-design-systems-now-return-a-clear-not-found-response.md +6 -0
  94. package/corpus/templates/design/changelog/2026-08-05-website-url-imports-now-capture-hydrated-browser-computed-de.md +6 -0
  95. package/corpus/templates/design/e2e/global-setup.ts +1 -1
  96. package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +1 -1
  97. package/corpus/templates/dispatch/app/routes/settings.$.tsx +1 -0
  98. package/corpus/templates/dispatch/app/routes/team.tsx +1 -1
  99. package/corpus/templates/factory/app/hooks/use-navigation-state.ts +1 -1
  100. package/corpus/templates/factory/app/routes/extensions._index.tsx +1 -1
  101. package/corpus/templates/factory/app/routes/settings.$.tsx +1 -0
  102. package/corpus/templates/factory/app/routes/team.tsx +1 -1
  103. package/corpus/templates/forms/app/routes/_app.extensions._index.tsx +1 -1
  104. package/corpus/templates/forms/app/routes/_app.settings.$.tsx +1 -0
  105. package/corpus/templates/forms/app/routes/_app.team.tsx +1 -1
  106. package/corpus/templates/forms/shared/navigation.ts +1 -1
  107. package/corpus/templates/macros/app/routes/extensions._index.tsx +1 -1
  108. package/corpus/templates/macros/app/routes/settings.$.tsx +1 -0
  109. package/corpus/templates/macros/app/routes/team.tsx +1 -1
  110. package/corpus/templates/mail/app/routes/extensions._index.tsx +1 -1
  111. package/corpus/templates/mail/app/routes/settings.$.tsx +1 -0
  112. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +1 -1
  113. package/corpus/templates/plan/app/routes/extensions._index.tsx +1 -1
  114. package/corpus/templates/plan/app/routes/settings.$.tsx +1 -0
  115. package/corpus/templates/plan/app/routes/team.tsx +1 -1
  116. package/corpus/templates/slides/.agents/skills/design-systems/SKILL.md +3 -1
  117. package/corpus/templates/slides/actions/analyze-brand-assets.ts +7 -7
  118. package/corpus/templates/slides/actions/import-from-url.ts +6 -6
  119. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +2 -2
  120. package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +1 -1
  121. package/corpus/templates/slides/app/components/editor/PromptDialog.tsx +3 -0
  122. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +1 -1
  123. package/corpus/templates/slides/app/components/onboarding/FirstDeckOnboardingFlow.tsx +9 -3
  124. package/corpus/templates/slides/app/context/DeckContext.tsx +71 -20
  125. package/corpus/templates/slides/app/global.css +6 -0
  126. package/corpus/templates/slides/app/i18n/en-US.ts +5 -5
  127. package/corpus/templates/slides/app/lib/create-deck-generation.ts +9 -5
  128. package/corpus/templates/slides/app/pages/DeckEditor.tsx +17 -0
  129. package/corpus/templates/slides/app/pages/Index.tsx +16 -5
  130. package/corpus/templates/slides/app/routes/extensions._index.tsx +1 -1
  131. package/corpus/templates/slides/app/routes/settings.$.tsx +1 -0
  132. package/corpus/templates/slides/app/routes/team.tsx +1 -1
  133. package/corpus/templates/slides/changelog/2026-08-05-failed-deck-creation-and-reference-uploads-now-recover-clean.md +6 -0
  134. package/corpus/templates/slides/changelog/2026-08-05-new-presentation-prompts-now-clearly-describe-presentation-g.md +6 -0
  135. package/corpus/templates/slides/changelog/2026-08-05-slides-recover-completed-generation-results-after-a-sync-hic.md +6 -0
  136. package/corpus/templates/slides/changelog/2026-08-05-website-url-imports-now-capture-hydrated-browser-computed-st.md +6 -0
  137. package/corpus/templates/tasks/app/components/layout/Header.tsx +3 -1
  138. package/corpus/templates/tasks/app/routes/extensions._index.tsx +1 -1
  139. package/corpus/templates/tasks/app/routes/settings.$.tsx +1 -0
  140. package/dist/agent/engine/ai-sdk-engine.js +49 -3
  141. package/dist/agent/production-agent.d.ts +1 -0
  142. package/dist/agent/production-agent.js +8 -0
  143. package/dist/agent/types.d.ts +7 -0
  144. package/dist/client/AssistantChat.js +1 -1
  145. package/dist/client/agent-chat-adapter.d.ts +1 -1
  146. package/dist/client/agent-chat-adapter.js +30 -3
  147. package/dist/client/agent-page/AgentWorkspaceContent.d.ts +1 -1
  148. package/dist/client/agent-page/AgentWorkspaceContent.js +13 -13
  149. package/dist/client/chat/run-recovery.d.ts +2 -1
  150. package/dist/client/chat/run-recovery.js +14 -3
  151. package/dist/client/embed-auth.js +6 -2
  152. package/dist/client/error-format.d.ts +1 -0
  153. package/dist/client/error-format.js +16 -2
  154. package/dist/client/integrations/IntegrationsPanel.js +40 -6
  155. package/dist/client/org/OrgSwitcher.js +2 -2
  156. package/dist/client/org/TeamPage.js +92 -72
  157. package/dist/client/require-session.d.ts +1 -1
  158. package/dist/client/require-session.js +3 -3
  159. package/dist/client/resources/use-mcp-servers.d.ts +5 -0
  160. package/dist/client/resources/use-mcp-servers.js +36 -0
  161. package/dist/client/settings/AccountSettingsCard.js +40 -35
  162. package/dist/client/settings/AutomationsSection.js +1 -1
  163. package/dist/client/settings/DemoModeSection.d.ts +3 -1
  164. package/dist/client/settings/DemoModeSection.js +4 -1
  165. package/dist/client/settings/ExtensionsSettingsContent.js +1 -1
  166. package/dist/client/settings/SchedulingTimezoneField.d.ts +3 -1
  167. package/dist/client/settings/SchedulingTimezoneField.js +9 -5
  168. package/dist/client/settings/SettingsPanel.d.ts +4 -1
  169. package/dist/client/settings/SettingsPanel.js +21 -21
  170. package/dist/client/settings/SettingsTabsPage.d.ts +2 -2
  171. package/dist/client/settings/SettingsTabsPage.js +98 -45
  172. package/dist/client/settings/VoiceTranscriptionSection.js +6 -1
  173. package/dist/client/settings/useSettingsPanelController.js +47 -8
  174. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  175. package/dist/ingestion/website.d.ts +30 -0
  176. package/dist/localization/default-messages.d.ts +5 -0
  177. package/dist/localization/default-messages.js +5 -0
  178. package/dist/mcp-client/oauth-routes.js +1 -1
  179. package/dist/mcp-client/routes.d.ts +1 -0
  180. package/dist/mcp-client/routes.js +49 -0
  181. package/dist/navigation/index.js +16 -5
  182. package/dist/observability/routes.d.ts +3 -3
  183. package/dist/provider-api/actions/custom-provider-registration.d.ts +13 -13
  184. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  185. package/dist/scripts/call-agent.js +11 -0
  186. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  187. package/dist/server/auth.js +9 -7
  188. package/dist/server/identity-sso.js +2 -1
  189. package/dist/server/onboarding-html.js +57 -34
  190. package/dist/server/realtime-token.d.ts +1 -1
  191. package/dist/server/transcribe-voice.d.ts +1 -1
  192. package/dist/server/workspace-provider-oauth.js +1 -1
  193. package/dist/setup-connections/onboarding.js +1 -1
  194. package/dist/shared/index.d.ts +1 -1
  195. package/dist/shared/index.js +1 -1
  196. package/dist/shared/sign-in-journey.d.ts +5 -2
  197. package/dist/shared/sign-in-journey.js +10 -4
  198. package/dist/templates/chat/app/components/layout/Layout.tsx +1 -1
  199. package/dist/templates/chat/app/hooks/use-navigation-state.ts +1 -1
  200. package/dist/templates/chat/app/root.tsx +1 -1
  201. package/dist/templates/chat/app/routes/agent.tsx +7 -7
  202. package/dist/templates/chat/app/routes/extensions._index.tsx +1 -1
  203. package/dist/templates/chat/app/routes/settings.$.tsx +1 -0
  204. package/dist/templates/chat/app/routes/team.tsx +1 -1
  205. package/dist/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +37 -6
  206. package/dist/templates/workspace-core/.agents/skills/authentication/SKILL.md +4 -2
  207. package/docs/content/authentication.mdx +4 -5
  208. package/docs/content/template-analytics-developers.mdx +1 -1
  209. package/docs/content/template-calendar-developers.mdx +1 -1
  210. package/docs/content/template-chat-developers.mdx +2 -2
  211. package/docs/content/template-content-developers.mdx +1 -1
  212. package/docs/content/template-mail-developers.mdx +1 -1
  213. package/docs/content/template-plan-developers.mdx +1 -1
  214. package/docs/content/toolkit-command-navigation.mdx +6 -6
  215. package/docs/content/toolkit-org-team.mdx +1 -1
  216. package/docs/content/toolkit-settings.mdx +8 -8
  217. package/docs/content/toolkit-setup-connections.mdx +1 -1
  218. package/package.json +1 -1
  219. package/src/templates/chat/app/components/layout/Layout.tsx +1 -1
  220. package/src/templates/chat/app/hooks/use-navigation-state.ts +1 -1
  221. package/src/templates/chat/app/root.tsx +1 -1
  222. package/src/templates/chat/app/routes/agent.tsx +7 -7
  223. package/src/templates/chat/app/routes/extensions._index.tsx +1 -1
  224. package/src/templates/chat/app/routes/settings.$.tsx +1 -0
  225. package/src/templates/chat/app/routes/team.tsx +1 -1
  226. package/src/templates/workspace-core/.agents/skills/address-feedback/SKILL.md +37 -6
  227. package/src/templates/workspace-core/.agents/skills/authentication/SKILL.md +4 -2
  228. package/corpus/templates/analytics/.agents/skills/dashboard-management/references/template-catalog-and-demo.md +0 -62
  229. package/corpus/templates/analytics/actions/install-dashboard-template.ts +0 -369
  230. package/corpus/templates/analytics/actions/list-dashboard-templates.ts +0 -47
  231. package/corpus/templates/analytics/actions/list-native-v2-dashboard-templates.ts +0 -34
  232. package/corpus/templates/analytics/app/routes/catalog.tsx +0 -240
  233. package/corpus/templates/analytics/changelog/2026-08-05-historical-analytics-rollups-stay-complete-and-atomic.md +0 -6
  234. package/corpus/templates/analytics/changelog/2026-08-05-historical-rollups-stay-complete-while-live-events-arrive.md +0 -6
  235. package/src/templates/default/actions/navigate.spec.ts +0 -53
@@ -489,9 +489,6 @@ export default function AgentsPage() {
489
489
  </p>
490
490
  </div>
491
491
  <div className="flex shrink-0 items-center gap-2">
492
- <Button variant="outline" size="sm" asChild>
493
- <Link to="/catalog">{t("agents.openCatalog")}</Link>
494
- </Button>
495
492
  {canManageOrg ? (
496
493
  <DropdownMenu>
497
494
  <DropdownMenuTrigger asChild>
@@ -120,11 +120,19 @@ interface GitHubOAuthStatus {
120
120
 
121
121
  interface FirstPartyAnalyticsHealthResponse {
122
122
  status: "healthy" | "monitor" | "recommend_bigquery" | "unavailable";
123
+ externalBackendRecommendation?: "none" | "connect" | "use" | "unknown";
123
124
  metrics: {
124
125
  eventCount: number;
125
126
  slowQueryCount24h: number;
126
127
  maxQueryDurationMs24h: number;
127
128
  };
129
+ externalBackends?: Array<{
130
+ id: "bigquery" | "amplitude";
131
+ label: string;
132
+ role: "warehouse" | "product-analytics";
133
+ configured: boolean | null;
134
+ setupLink: string;
135
+ }>;
128
136
  bigQuery: {
129
137
  configured: boolean | null;
130
138
  };
@@ -1615,23 +1623,25 @@ function FirstPartyAnalyticsCard() {
1615
1623
  });
1616
1624
  const health = rawHealth as FirstPartyAnalyticsHealthResponse | undefined;
1617
1625
  const healthStatus = isHealthError ? "unavailable" : health?.status;
1618
- const bigQueryConfigured = health?.bigQuery.configured === true;
1619
- const healthTitleKey =
1620
- healthStatus === "recommend_bigquery"
1621
- ? bigQueryConfigured
1622
- ? "dataSources.bigQueryConnectedTitle"
1623
- : "dataSources.bigQueryRecommendationTitle"
1624
- : null;
1625
- const healthDescriptionKey =
1626
- healthStatus === "recommend_bigquery"
1627
- ? bigQueryConfigured
1628
- ? "dataSources.bigQueryConnectedDescription"
1629
- : "dataSources.bigQueryRecommendationDescription"
1630
- : healthStatus === "monitor"
1631
- ? "dataSources.bigQueryMonitorDescription"
1632
- : healthStatus === "healthy"
1633
- ? "dataSources.bigQueryHealthyDescription"
1634
- : "dataSources.bigQueryHealthUnavailable";
1626
+ const externalBackends = health?.externalBackends ?? [];
1627
+ const externalBackendConfigured = externalBackends.some(
1628
+ (backend) => backend.configured === true,
1629
+ );
1630
+ const recommendsExternalBackend = healthStatus === "recommend_bigquery";
1631
+ const healthTitleKey = recommendsExternalBackend
1632
+ ? externalBackendConfigured
1633
+ ? "analyticsBackend.connectedTitle"
1634
+ : "analyticsBackend.recommendationTitle"
1635
+ : null;
1636
+ const healthDescriptionKey = recommendsExternalBackend
1637
+ ? externalBackendConfigured
1638
+ ? "analyticsBackend.connectedDescription"
1639
+ : "analyticsBackend.recommendationDescription"
1640
+ : healthStatus === "monitor"
1641
+ ? "analyticsBackend.monitorDescription"
1642
+ : healthStatus === "healthy"
1643
+ ? "analyticsBackend.healthyDescription"
1644
+ : "analyticsBackend.unavailableDescription";
1635
1645
 
1636
1646
  const createKey = useActionMutation("create-analytics-public-key", {
1637
1647
  onSuccess: (result: any) => {
@@ -1681,7 +1691,7 @@ function FirstPartyAnalyticsCard() {
1681
1691
  <div className="flex shrink-0 items-center gap-2">
1682
1692
  {isLoading ? (
1683
1693
  <Skeleton className="h-4 w-20 rounded-full" />
1684
- ) : healthStatus === "recommend_bigquery" ? (
1694
+ ) : recommendsExternalBackend ? (
1685
1695
  <span
1686
1696
  className="flex min-w-0 items-center gap-1.5 text-xs font-medium text-amber-500"
1687
1697
  title={healthTitleKey ? t(healthTitleKey) : undefined}
@@ -1690,13 +1700,13 @@ function FirstPartyAnalyticsCard() {
1690
1700
  <span className="max-w-[12rem] truncate">
1691
1701
  {healthTitleKey
1692
1702
  ? t(healthTitleKey)
1693
- : t("dataSources.bigQueryRecommendationTitle")}
1703
+ : t("analyticsBackend.recommendationTitle")}
1694
1704
  </span>
1695
1705
  </span>
1696
1706
  ) : connected ? (
1697
1707
  <span className="flex items-center gap-1.5 text-xs text-emerald-500 font-medium whitespace-nowrap">
1698
1708
  <IconCheck className="h-3.5 w-3.5" />
1699
- {t("dataSources.configured")}
1709
+ {t("analyticsBackend.configured")}
1700
1710
  </span>
1701
1711
  ) : (
1702
1712
  <span className="flex items-center gap-1.5 text-xs text-muted-foreground whitespace-nowrap">
@@ -1722,7 +1732,7 @@ function FirstPartyAnalyticsCard() {
1722
1732
  ) : (
1723
1733
  <div
1724
1734
  className={`rounded-md border p-3 text-xs ${
1725
- healthStatus === "recommend_bigquery"
1735
+ recommendsExternalBackend
1726
1736
  ? "border-amber-500/30 bg-amber-500/10"
1727
1737
  : "border-border/50 bg-muted/20"
1728
1738
  }`}
@@ -1731,7 +1741,7 @@ function FirstPartyAnalyticsCard() {
1731
1741
  <div className="flex min-w-0 items-start gap-2">
1732
1742
  <IconAlertCircle
1733
1743
  className={`mt-px h-3.5 w-3.5 shrink-0 ${
1734
- healthStatus === "recommend_bigquery"
1744
+ recommendsExternalBackend
1735
1745
  ? "text-amber-500"
1736
1746
  : "text-muted-foreground"
1737
1747
  }`}
@@ -1747,15 +1757,41 @@ function FirstPartyAnalyticsCard() {
1747
1757
  </p>
1748
1758
  </div>
1749
1759
  </div>
1750
- {healthStatus === "recommend_bigquery" &&
1751
- !bigQueryConfigured ? (
1752
- <Button asChild size="sm" className="shrink-0 text-xs">
1753
- <Link to="/data-sources?source=bigquery">
1754
- {t("dataSources.bigQuerySetup")}
1755
- </Link>
1756
- </Button>
1757
- ) : null}
1758
1760
  </div>
1761
+ {externalBackends.length > 0 && (
1762
+ <div className="mt-3 border-t border-border/30 pt-3">
1763
+ <div className="mb-2 text-muted-foreground">
1764
+ {t("analyticsBackend.options")}
1765
+ </div>
1766
+ <div className="flex flex-wrap gap-2">
1767
+ {externalBackends.map((backend) => {
1768
+ const statusLabel =
1769
+ backend.configured === true
1770
+ ? t("analyticsBackend.configured")
1771
+ : backend.configured === false
1772
+ ? t("analyticsBackend.setUp")
1773
+ : t("dataSources.statusUnknown");
1774
+ return (
1775
+ <Button
1776
+ asChild
1777
+ key={backend.id}
1778
+ size="sm"
1779
+ variant={
1780
+ backend.configured === true
1781
+ ? "outline"
1782
+ : "default"
1783
+ }
1784
+ className="text-xs"
1785
+ >
1786
+ <Link to={backend.setupLink}>
1787
+ {backend.label} · {statusLabel}
1788
+ </Link>
1789
+ </Button>
1790
+ );
1791
+ })}
1792
+ </div>
1793
+ </div>
1794
+ )}
1759
1795
  {health && healthStatus !== "unavailable" && (
1760
1796
  <div className="mt-3 grid grid-cols-3 gap-2 border-t border-border/30 pt-3">
1761
1797
  <div>
@@ -41,7 +41,6 @@ import { buildAnalyticsGeneralSettingsSearchEntries } from "./settings/settings-
41
41
 
42
42
  export default function Settings() {
43
43
  const t = useT();
44
- const agentSettingsTabs = useAgentSettingsTabs();
45
44
  const replayStorageStatus = useReplayStorageStatus();
46
45
  const { data: analyticsPrefs, isLoading: analyticsPrefsLoading } =
47
46
  useActionQuery<AnalyticsUserPrefs>("get-user-pref", {
@@ -122,6 +121,23 @@ export default function Settings() {
122
121
  });
123
122
  };
124
123
 
124
+ const agentAdditionalContent = (
125
+ <SettingsRow
126
+ id="bell-sound"
127
+ label={t("settings.bellSound")}
128
+ description={t("settings.bellSoundDescription")}
129
+ control={
130
+ <Switch
131
+ aria-label={t("settings.bellSound")}
132
+ checked={bellSoundEnabled}
133
+ disabled={analyticsPrefsLoading || saveAnalyticsPrefs.isPending}
134
+ onCheckedChange={saveBellSoundPreference}
135
+ />
136
+ }
137
+ />
138
+ );
139
+ const agentSettingsTabs = useAgentSettingsTabs({ agentAdditionalContent });
140
+
125
141
  const extraTabs = useMemo<SettingsTabItem[]>(
126
142
  () => [
127
143
  {
@@ -130,7 +146,7 @@ export default function Settings() {
130
146
  icon: IconBell,
131
147
  keywords: "alerts rules notifications thresholds triggers monitoring",
132
148
  content: (
133
- <div className="mx-auto w-full max-w-5xl">
149
+ <div className="w-full">
134
150
  <AlertRulesSettingsCard />
135
151
  </div>
136
152
  ),
@@ -157,7 +173,7 @@ export default function Settings() {
157
173
  extraTabs={extraTabs}
158
174
  generalSearchEntries={generalSearchEntries}
159
175
  general={
160
- <div className="mx-auto w-full max-w-2xl space-y-6">
176
+ <div className="w-full space-y-6">
161
177
  <SettingsGroup className="bg-card border-border/50">
162
178
  <SettingsRow
163
179
  id="credentials"
@@ -171,18 +187,6 @@ export default function Settings() {
171
187
  </Button>
172
188
  }
173
189
  />
174
- <SettingsRow
175
- id="dashboard-templates"
176
- label={t("settings.dashboardTemplates")}
177
- description={t("settings.dashboardTemplatesDescription")}
178
- control={
179
- <Button variant="outline" size="sm" asChild>
180
- <Link to="/catalog">
181
- {t("settings.openDashboardTemplates")}
182
- </Link>
183
- </Button>
184
- }
185
- />
186
190
  <SettingsRow
187
191
  id="language"
188
192
  label={t("settings.languageTitle")}
@@ -207,21 +211,6 @@ export default function Settings() {
207
211
  />
208
212
  }
209
213
  />
210
- <SettingsRow
211
- id="bell-sound"
212
- label={t("settings.bellSound")}
213
- description={t("settings.bellSoundDescription")}
214
- control={
215
- <Switch
216
- aria-label={t("settings.bellSound")}
217
- checked={bellSoundEnabled}
218
- disabled={
219
- analyticsPrefsLoading || saveAnalyticsPrefs.isPending
220
- }
221
- onCheckedChange={saveBellSoundPreference}
222
- />
223
- }
224
- />
225
214
  </SettingsGroup>
226
215
 
227
216
  {replayStorageStatus.data?.configured ? (
@@ -242,29 +231,19 @@ export default function Settings() {
242
231
  </CardContent>
243
232
  </Card>
244
233
  ) : null}
245
-
246
- <Card id="about" className="bg-card border-border/50 scroll-mt-16">
247
- <CardHeader>
248
- <CardTitle className="text-base">{t("settings.about")}</CardTitle>
249
- </CardHeader>
250
- <CardContent className="space-y-2 text-sm text-muted-foreground">
251
- <p>{t("settings.aboutDescription")}</p>
252
- <p>{t("settings.aboutUsage")}</p>
253
- </CardContent>
254
- </Card>
255
234
  </div>
256
235
  }
257
236
  team={
258
- <div className="mx-auto w-full max-w-5xl">
237
+ <div className="w-full">
259
238
  <TeamPage
260
239
  showTitle={false}
261
240
  createOrgDescription="Set up a team to share dashboards and data sources with your colleagues."
262
- className="max-w-5xl"
241
+ className="w-full"
263
242
  />
264
243
  </div>
265
244
  }
266
245
  whatsNew={
267
- <div className="mx-auto w-full max-w-2xl">
246
+ <div className="w-full">
268
247
  <ChangelogSettingsCard markdown={changelog} />
269
248
  </div>
270
249
  }
@@ -2,6 +2,7 @@ import {
2
2
  getAgentSettingsSearchTabs,
3
3
  type SettingsSearchEntry,
4
4
  } from "@agent-native/core/client/settings";
5
+ import { buildSettingsRoute } from "@agent-native/core/navigation";
5
6
 
6
7
  interface SettingsCommandItem {
7
8
  id: string;
@@ -12,6 +13,20 @@ interface SettingsCommandItem {
12
13
 
13
14
  type Translate = (key: string) => string;
14
15
 
16
+ function buildSettingsEntryRoute(tabId: string, section?: string): string {
17
+ const normalizedSection = section?.replace(/^#/, "").trim();
18
+ if (!normalizedSection || normalizedSection === tabId) {
19
+ return buildSettingsRoute(tabId);
20
+ }
21
+ if (normalizedSection.startsWith("agent:")) {
22
+ return buildSettingsRoute(normalizedSection);
23
+ }
24
+ if (normalizedSection.startsWith(`${tabId}:`)) {
25
+ return buildSettingsRoute(normalizedSection);
26
+ }
27
+ return buildSettingsRoute(`${tabId}:${normalizedSection}`);
28
+ }
29
+
15
30
  export function buildAnalyticsGeneralSettingsSearchEntries(
16
31
  t: Translate,
17
32
  replayStorageConfigured: boolean,
@@ -21,6 +36,7 @@ export function buildAnalyticsGeneralSettingsSearchEntries(
21
36
  id: "analytics-account",
22
37
  label: t("settings.account"),
23
38
  keywords: "profile photo avatar email signed in identity",
39
+ tabId: "account",
24
40
  hash: "account",
25
41
  },
26
42
  {
@@ -29,12 +45,6 @@ export function buildAnalyticsGeneralSettingsSearchEntries(
29
45
  keywords: "data sources api keys manage credentials",
30
46
  hash: "credentials",
31
47
  },
32
- {
33
- id: "analytics-dashboard-templates",
34
- label: t("settings.dashboardTemplates"),
35
- keywords: "templates catalog dashboards",
36
- hash: "dashboard-templates",
37
- },
38
48
  ...(replayStorageConfigured
39
49
  ? [
40
50
  {
@@ -57,18 +67,6 @@ export function buildAnalyticsGeneralSettingsSearchEntries(
57
67
  keywords: "email notifications errors alerts javascript monitoring",
58
68
  hash: "error-email-notifications",
59
69
  },
60
- {
61
- id: "analytics-bell-sound",
62
- label: t("settings.bellSound"),
63
- keywords: "sound audio ding agent completion notification",
64
- hash: "bell-sound",
65
- },
66
- {
67
- id: "analytics-about",
68
- label: t("settings.about"),
69
- keywords: "about version info usage",
70
- hash: "about",
71
- },
72
70
  ];
73
71
  }
74
72
 
@@ -118,18 +116,23 @@ export function buildAnalyticsSettingsCommandItems(
118
116
  };
119
117
 
120
118
  for (const tab of tabs) {
119
+ const entryHref = (entry: SettingsSearchEntry, tabId: string) => {
120
+ const hash = entry.hash?.replace(/^#/, "");
121
+ return buildSettingsEntryRoute(tabId, hash);
122
+ };
121
123
  add({
122
124
  id: `tab:${tab.id}`,
123
125
  label: tab.label,
124
126
  keywords: `${tab.keywords} settings`,
125
- href: `/settings#${tab.id}`,
127
+ href: buildSettingsRoute(tab.id),
126
128
  });
127
129
  for (const entry of tab.searchEntries ?? []) {
130
+ const tabId = entry.tabId ?? tab.id;
128
131
  add({
129
132
  id: entry.id,
130
133
  label: entry.label,
131
134
  keywords: `${entry.keywords ?? ""} ${entry.description ?? ""} ${tab.label} settings`,
132
- href: `/settings#${entry.hash ?? entry.tabId ?? tab.id}`,
135
+ href: entryHref(entry, tabId),
133
136
  });
134
137
  }
135
138
  }
@@ -7,5 +7,5 @@ export function meta() {
7
7
  }
8
8
 
9
9
  export default function ExtensionsRoute() {
10
- return <Navigate to="/settings#extensions" replace />;
10
+ return <Navigate to="/settings/extensions" replace />;
11
11
  }
@@ -0,0 +1 @@
1
+ export { default, meta } from "./settings.js";
@@ -7,5 +7,5 @@ export function meta() {
7
7
  }
8
8
 
9
9
  export default function TeamRoute() {
10
- return <Navigate to="/settings#organization" replace />;
10
+ return <Navigate to="/settings/organization" replace />;
11
11
  }
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-08-05
4
+ ---
5
+
6
+ Historical Analytics rollups now repair automatically after an upgrade
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: removed
3
+ date: 2026-08-05
4
+ ---
5
+
6
+ Settings no longer includes an unnecessary About section.
@@ -173,7 +173,7 @@ Agent Native observability panels belong in the canonical Agent Native dashboard
173
173
  (`agent-native-templates-first-party`), including generation metrics, explicit
174
174
  feedback sentiment, optional inferred message sentiment, and separate
175
175
  by-main-model breakdowns. Do not publish or install a separate observability
176
- dashboard template.
176
+ dashboard.
177
177
 
178
178
  Example dashboard panel:
179
179
 
@@ -34,3 +34,7 @@ timeout = 120
34
34
 
35
35
  [functions.uptime-monitor-sweep-background]
36
36
  timeout = 120
37
+
38
+ [functions.analytics-rollup-backfill-background]
39
+ timeout = 300
40
+ memory = "2gb"
@@ -22,6 +22,10 @@ const UPTIME_SCHEDULED_NAME = "uptime-monitor-cron";
22
22
  const UPTIME_WORKER_NAME = "uptime-monitor-sweep-background";
23
23
  const UPTIME_ROUTE_PATH = "/api/uptime-monitors/run";
24
24
  const UPTIME_SCHEDULE = "* * * * *";
25
+ const ROLLUP_SCHEDULED_NAME = "analytics-rollup-backfill-cron";
26
+ const ROLLUP_WORKER_NAME = "analytics-rollup-backfill-background";
27
+ const ROLLUP_ROUTE_PATH = "/api/analytics-rollups/backfill";
28
+ const ROLLUP_SCHEDULE = "*/15 * * * *";
25
29
 
26
30
  function ensureDir(dir: string) {
27
31
  mkdirSync(dir, { recursive: true });
@@ -388,6 +392,105 @@ export const config = {
388
392
  writeFileSync(path.join(dest, `${UPTIME_WORKER_NAME}.mjs`), source);
389
393
  }
390
394
 
395
+ function emitRollupScheduledTrigger(token: string) {
396
+ const dest = path.join(FUNCTIONS_DIR, ROLLUP_SCHEDULED_NAME);
397
+ rmSync(dest, { recursive: true, force: true });
398
+ ensureDir(dest);
399
+
400
+ const source = `const WORKER_PATH = "/.netlify/functions/${ROLLUP_WORKER_NAME}";
401
+ const CRON_TOKEN = ${JSON.stringify(token)};
402
+
403
+ function siteOrigin(request) {
404
+ const configured = process.env.URL || process.env.DEPLOY_URL;
405
+ if (configured) return configured;
406
+ const url = new URL(request.url);
407
+ return url.origin;
408
+ }
409
+
410
+ export default async function handler(request) {
411
+ const url = new URL(WORKER_PATH, siteOrigin(request));
412
+ const response = await fetch(url.toString(), {
413
+ method: "POST",
414
+ headers: {
415
+ "content-type": "application/json",
416
+ "x-agent-native-analytics-rollup-cron": CRON_TOKEN,
417
+ },
418
+ body: JSON.stringify({ scheduled: true }),
419
+ });
420
+
421
+ if (!response.ok && response.status !== 202) {
422
+ console.error(
423
+ "[analytics-rollup-backfill-cron] Background backfill trigger failed:",
424
+ response.status,
425
+ );
426
+ }
427
+ }
428
+
429
+ export const config = {
430
+ name: "analytics rollup backfill cron trigger",
431
+ generator: "agent-native analytics build",
432
+ schedule: ${JSON.stringify(ROLLUP_SCHEDULE)},
433
+ };
434
+ `;
435
+
436
+ writeFileSync(path.join(dest, `${ROLLUP_SCHEDULED_NAME}.mjs`), source);
437
+ }
438
+
439
+ function emitRollupBackgroundWorker(token: string) {
440
+ const dest = path.join(FUNCTIONS_DIR, ROLLUP_WORKER_NAME);
441
+ rmSync(dest, { recursive: true, force: true });
442
+ cloneServerBundleForFunction(SERVER_DIR, dest);
443
+ rmSync(path.join(dest, "server.mjs"), { force: true });
444
+
445
+ const source = `globalThis.__AGENT_NATIVE_BACKGROUND_RUNTIME__ = true;
446
+ globalThis.__AGENT_NATIVE_ANALYTICS_ROLLUP_BACKFILL_SCHEDULED_RUNTIME__ = true;
447
+
448
+ const CRON_TOKEN = ${JSON.stringify(token)};
449
+ const ROUTE_PATH = ${JSON.stringify(ROLLUP_ROUTE_PATH)};
450
+ let cachedHandler;
451
+
452
+ function timingSafeEquals(a, b) {
453
+ if (a.length !== b.length) return false;
454
+ let diff = 0;
455
+ for (let i = 0; i < a.length; i += 1) {
456
+ diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
457
+ }
458
+ return diff === 0;
459
+ }
460
+
461
+ export default async function handler(request, context) {
462
+ const token = request.headers.get("x-agent-native-analytics-rollup-cron") || "";
463
+ if (!timingSafeEquals(token, CRON_TOKEN)) {
464
+ return new Response("Unauthorized", { status: 401 });
465
+ }
466
+
467
+ cachedHandler ??= (await import("./main.mjs")).default;
468
+ const url = new URL(request.url);
469
+ url.pathname = ROUTE_PATH;
470
+ url.search = "";
471
+
472
+ const rewritten = new Request(url.toString(), {
473
+ method: "POST",
474
+ headers: new Headers({ "content-type": "application/json" }),
475
+ body: JSON.stringify({ scheduled: true }),
476
+ });
477
+
478
+ return await cachedHandler(rewritten, context);
479
+ }
480
+
481
+ export const config = {
482
+ name: "analytics rollup backfill background sweep",
483
+ generator: "agent-native analytics build",
484
+ background: true,
485
+ nodeBundler: "none",
486
+ includedFiles: ["**"],
487
+ preferStatic: false,
488
+ };
489
+ `;
490
+
491
+ writeFileSync(path.join(dest, `${ROLLUP_WORKER_NAME}.mjs`), source);
492
+ }
493
+
391
494
  function isDirectRun(): boolean {
392
495
  const entrypoint = process.argv[1];
393
496
  return Boolean(
@@ -407,12 +510,15 @@ function main(): void {
407
510
  const token = randomBytes(32).toString("hex");
408
511
  const alertToken = randomBytes(32).toString("hex");
409
512
  const uptimeToken = randomBytes(32).toString("hex");
513
+ const rollupToken = randomBytes(32).toString("hex");
410
514
  emitScheduledTrigger(token);
411
515
  emitBackgroundWorker(token);
412
516
  emitAlertScheduledTrigger(alertToken);
413
517
  emitAlertBackgroundWorker(alertToken);
414
518
  emitUptimeScheduledTrigger(uptimeToken);
415
519
  emitUptimeBackgroundWorker(uptimeToken);
520
+ emitRollupScheduledTrigger(rollupToken);
521
+ emitRollupBackgroundWorker(rollupToken);
416
522
  console.log(
417
523
  `[dashboard-report-cron] Emitted Netlify scheduled trigger "${SCHEDULED_NAME}" (${SCHEDULE}) and background worker "${WORKER_NAME}".`,
418
524
  );
@@ -422,6 +528,9 @@ function main(): void {
422
528
  console.log(
423
529
  `[uptime-monitor-cron] Emitted Netlify scheduled trigger "${UPTIME_SCHEDULED_NAME}" (${UPTIME_SCHEDULE}) and background worker "${UPTIME_WORKER_NAME}".`,
424
530
  );
531
+ console.log(
532
+ `[analytics-rollup-backfill-cron] Emitted Netlify scheduled trigger "${ROLLUP_SCHEDULED_NAME}" (${ROLLUP_SCHEDULE}) and background worker "${ROLLUP_WORKER_NAME}".`,
533
+ );
425
534
  }
426
535
 
427
536
  if (isDirectRun()) {
@@ -0,0 +1,104 @@
1
+ -- Builder.io production first-party Analytics warehouse.
2
+ -- Run this once with the authenticated Builder Google Cloud account:
3
+ -- bq query --use_legacy_sql=false < templates/analytics/scripts/first-party-analytics-bigquery.sql
4
+ --
5
+ -- The raw table is append-only. The query view removes retry duplicates by the
6
+ -- stable Postgres event id, and the two aggregate views preserve the existing
7
+ -- dashboard SQL names without writing rollups back to Neon.
8
+
9
+ CREATE SCHEMA IF NOT EXISTS `builder-3b0a2.analytics`
10
+ OPTIONS (location = "US");
11
+
12
+ CREATE TABLE IF NOT EXISTS `builder-3b0a2.analytics.first_party_analytics_events_raw` (
13
+ id STRING NOT NULL,
14
+ public_key_id STRING,
15
+ event_name STRING NOT NULL,
16
+ user_id STRING,
17
+ anonymous_id STRING,
18
+ user_key STRING,
19
+ session_id STRING,
20
+ timestamp TIMESTAMP NOT NULL,
21
+ event_date DATE,
22
+ received_at TIMESTAMP NOT NULL,
23
+ url STRING,
24
+ path STRING,
25
+ hostname STRING,
26
+ referrer STRING,
27
+ app STRING,
28
+ template STRING,
29
+ signed_in STRING,
30
+ properties STRING NOT NULL,
31
+ context STRING NOT NULL,
32
+ owner_email STRING NOT NULL,
33
+ org_id STRING
34
+ )
35
+ PARTITION BY event_date
36
+ CLUSTER BY owner_email, org_id, event_name, app;
37
+
38
+ CREATE OR REPLACE VIEW `builder-3b0a2.analytics.first_party_analytics_events_raw_query` AS
39
+ SELECT * EXCEPT (_row_number)
40
+ FROM (
41
+ SELECT
42
+ raw.*,
43
+ ROW_NUMBER() OVER (PARTITION BY id ORDER BY received_at DESC) AS _row_number
44
+ FROM `builder-3b0a2.analytics.first_party_analytics_events_raw` AS raw
45
+ )
46
+ WHERE _row_number = 1;
47
+
48
+ CREATE OR REPLACE VIEW `builder-3b0a2.analytics.first_party_analytics_events_raw_daily_rollups` AS
49
+ WITH tenant_events AS (
50
+ SELECT
51
+ CASE
52
+ WHEN org_id IS NOT NULL AND org_id <> '' THEN CONCAT('org:', org_id)
53
+ ELSE CONCAT('user:', owner_email)
54
+ END AS tenant_key,
55
+ owner_email,
56
+ org_id,
57
+ event_date,
58
+ event_name,
59
+ COALESCE(app, '') AS app,
60
+ COALESCE(template, '') AS template
61
+ FROM `builder-3b0a2.analytics.first_party_analytics_events_raw_query`
62
+ WHERE event_date IS NOT NULL
63
+ )
64
+ SELECT
65
+ TO_HEX(SHA256(CONCAT(
66
+ tenant_key, '|', CAST(event_date AS STRING), '|', event_name, '|', app,
67
+ '|', template
68
+ ))) AS id,
69
+ tenant_key,
70
+ ANY_VALUE(owner_email) AS owner_email,
71
+ ANY_VALUE(org_id) AS org_id,
72
+ event_date,
73
+ event_name,
74
+ app,
75
+ template,
76
+ COUNT(*) AS event_count
77
+ FROM tenant_events
78
+ GROUP BY tenant_key, event_date, event_name, app, template;
79
+
80
+ CREATE OR REPLACE VIEW `builder-3b0a2.analytics.first_party_analytics_events_raw_user_days` AS
81
+ WITH tenant_user_days AS (
82
+ SELECT
83
+ CASE
84
+ WHEN org_id IS NOT NULL AND org_id <> '' THEN CONCAT('org:', org_id)
85
+ ELSE CONCAT('user:', owner_email)
86
+ END AS tenant_key,
87
+ owner_email,
88
+ org_id,
89
+ event_date,
90
+ user_key
91
+ FROM `builder-3b0a2.analytics.first_party_analytics_events_raw_query`
92
+ WHERE event_date IS NOT NULL AND user_key IS NOT NULL AND user_key <> ''
93
+ )
94
+ SELECT
95
+ TO_HEX(SHA256(CONCAT(
96
+ tenant_key, '|', CAST(event_date AS STRING), '|', user_key
97
+ ))) AS id,
98
+ tenant_key,
99
+ ANY_VALUE(owner_email) AS owner_email,
100
+ ANY_VALUE(org_id) AS org_id,
101
+ event_date,
102
+ user_key
103
+ FROM tenant_user_days
104
+ GROUP BY tenant_key, event_date, user_key;