@agent-native/core 0.69.0 → 0.70.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +48 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/changelog/parse.ts +245 -0
  5. package/corpus/core/src/cli/changelog.ts +199 -0
  6. package/corpus/core/src/cli/index.ts +19 -0
  7. package/corpus/core/src/cli/recap.ts +24 -1
  8. package/corpus/core/src/cli/skills.ts +72 -45
  9. package/corpus/core/src/client/CommandMenu.tsx +183 -75
  10. package/corpus/core/src/client/agent-chat-adapter.ts +44 -7
  11. package/corpus/core/src/client/analytics.ts +181 -0
  12. package/corpus/core/src/client/changelog/Changelog.tsx +321 -0
  13. package/corpus/core/src/client/index.ts +11 -0
  14. package/corpus/core/src/client/mcp-apps/McpAppRenderer.tsx +54 -0
  15. package/corpus/core/src/client/settings/useBuilderStatus.ts +82 -20
  16. package/corpus/core/src/mcp/build-server.ts +1 -1
  17. package/corpus/core/src/mcp/embed-app.ts +112 -6
  18. package/corpus/core/src/server/attribution.ts +211 -0
  19. package/corpus/core/src/server/auth.ts +6 -0
  20. package/corpus/core/src/server/better-auth-instance.ts +49 -5
  21. package/corpus/core/src/server/builder-browser.ts +27 -1
  22. package/corpus/core/src/server/core-routes-plugin.ts +58 -0
  23. package/corpus/core/src/server/google-oauth.ts +19 -6
  24. package/corpus/core/src/shared/mcp-embed-headers.ts +1 -1
  25. package/corpus/core/src/styles/agent-conversation.css +21 -0
  26. package/corpus/core/src/templates/workspace-core/.agents/skills/changelog/SKILL.md +111 -0
  27. package/corpus/core/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +46 -14
  28. package/corpus/templates/analytics/CHANGELOG.md +10 -0
  29. package/corpus/templates/analytics/actions/install-dashboard-template.ts +2 -2
  30. package/corpus/templates/analytics/actions/open-traffic-dashboard.ts +1 -1
  31. package/corpus/templates/analytics/actions/update-dashboard.ts +5 -3
  32. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +180 -144
  33. package/corpus/templates/analytics/app/components/layout/Header.tsx +2 -2
  34. package/corpus/templates/analytics/app/components/layout/Layout.tsx +7 -5
  35. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +221 -213
  36. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +6 -3
  37. package/corpus/templates/analytics/app/lib/demo-dashboard-path.ts +1 -1
  38. package/corpus/templates/analytics/app/lib/last-opened.ts +2 -1
  39. package/corpus/templates/analytics/app/pages/Settings.tsx +4 -0
  40. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +3 -3
  41. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +2 -2
  42. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +101 -99
  43. package/corpus/templates/analytics/app/pages/overview/OverviewPage.tsx +1 -1
  44. package/corpus/templates/analytics/app/routes/adhoc.$id.tsx +18 -5
  45. package/corpus/templates/analytics/app/routes/catalog.tsx +1 -1
  46. package/corpus/templates/analytics/app/routes/dashboards.$id.tsx +9 -0
  47. package/corpus/templates/analytics/app/routes/traffic.tsx +1 -1
  48. package/corpus/templates/analytics/changelog/2026-06-23-the-dashboards-and-analyses-sidebar-settings-buttons-now-sta.md +6 -0
  49. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +145 -0
  50. package/corpus/templates/analytics/server/db/index.ts +1 -1
  51. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +9 -2
  52. package/corpus/templates/analytics/server/lib/demo-dashboards.ts +1 -1
  53. package/corpus/templates/analytics/server/plugins/agent-chat.ts +2 -2
  54. package/corpus/templates/analytics/server/plugins/core-routes.ts +6 -6
  55. package/corpus/templates/assets/CHANGELOG.md +10 -0
  56. package/corpus/templates/assets/app/root.tsx +7 -1
  57. package/corpus/templates/assets/app/routes/settings.tsx +6 -0
  58. package/corpus/templates/brain/CHANGELOG.md +10 -0
  59. package/corpus/templates/brain/app/root.tsx +7 -1
  60. package/corpus/templates/brain/app/routes/settings.tsx +8 -1
  61. package/corpus/templates/calendar/CHANGELOG.md +10 -0
  62. package/corpus/templates/calendar/app/pages/Settings.tsx +4 -0
  63. package/corpus/templates/calendar/app/root.tsx +7 -1
  64. package/corpus/templates/chat/CHANGELOG.md +10 -0
  65. package/corpus/templates/chat/app/root.tsx +7 -1
  66. package/corpus/templates/clips/CHANGELOG.md +10 -0
  67. package/corpus/templates/clips/app/components/player/share-dialog.tsx +33 -7
  68. package/corpus/templates/clips/app/components/player/sign-in-prompt-dialog.tsx +8 -0
  69. package/corpus/templates/clips/app/components/player/video-player.tsx +150 -20
  70. package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +7 -11
  71. package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +31 -0
  72. package/corpus/templates/clips/app/components/sharing/slack-share-hint.tsx +71 -0
  73. package/corpus/templates/clips/app/root.tsx +7 -1
  74. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +15 -1
  75. package/corpus/templates/clips/app/routes/record.tsx +78 -5
  76. package/corpus/templates/clips/app/routes/share.$shareId.tsx +86 -5
  77. package/corpus/templates/clips/changelog/2026-06-23-clips-shared-in-slack-no-longer-show-a-could-not-start-playb.md +6 -0
  78. package/corpus/templates/clips/changelog/2026-06-23-made-the-play-button-on-shared-and-embedded-clips-scale-with.md +6 -0
  79. package/corpus/templates/clips/changelog/2026-06-23-public-clip-share-links-now-show-whether-they-ll-play-inline.md +6 -0
  80. package/corpus/templates/clips/changelog/2026-06-23-screen-camera-recordings-now-keep-the-presenter-s-face-in-th.md +6 -0
  81. package/corpus/templates/clips/desktop/src/lib/recorder.ts +20 -0
  82. package/corpus/templates/clips/desktop/src/main.tsx +3 -0
  83. package/corpus/templates/clips/desktop/src/overlays/region-record-border.tsx +76 -0
  84. package/corpus/templates/clips/desktop/src/styles.css +49 -0
  85. package/corpus/templates/clips/desktop/src-tauri/capabilities/default.json +2 -1
  86. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +67 -1
  87. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +2 -0
  88. package/corpus/templates/clips/desktop/src-tauri/src/util.rs +4 -1
  89. package/corpus/templates/clips/server/routes/api/video/[recordingId].get.ts +51 -1
  90. package/corpus/templates/clips/shared/share-attribution.ts +79 -0
  91. package/corpus/templates/content/CHANGELOG.md +10 -0
  92. package/corpus/templates/content/app/root.tsx +7 -1
  93. package/corpus/templates/design/CHANGELOG.md +10 -0
  94. package/corpus/templates/design/actions/generate-design.ts +4 -0
  95. package/corpus/templates/design/app/root.tsx +7 -1
  96. package/corpus/templates/design/app/routes/settings.tsx +10 -2
  97. package/corpus/templates/dispatch/CHANGELOG.md +10 -0
  98. package/corpus/templates/dispatch/app/root.tsx +7 -1
  99. package/corpus/templates/forms/CHANGELOG.md +10 -0
  100. package/corpus/templates/forms/app/root.tsx +7 -1
  101. package/corpus/templates/macros/CHANGELOG.md +10 -0
  102. package/corpus/templates/macros/app/root.tsx +7 -1
  103. package/corpus/templates/mail/CHANGELOG.md +10 -0
  104. package/corpus/templates/mail/app/components/layout/CommandPalette.tsx +3 -0
  105. package/corpus/templates/mail/app/pages/SettingsPage.tsx +27 -0
  106. package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +31 -17
  107. package/corpus/templates/plan/.agents/skills/visual-plan/references/wireframe.md +7 -4
  108. package/corpus/templates/plan/.agents/skills/visual-recap/SKILL.md +34 -24
  109. package/corpus/templates/plan/.agents/skills/visual-recap/references/wireframe.md +7 -4
  110. package/corpus/templates/plan/AGENTS.md +4 -0
  111. package/corpus/templates/plan/CHANGELOG.md +10 -0
  112. package/corpus/templates/plan/actions/view-screen.ts +5 -0
  113. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +98 -1
  114. package/corpus/templates/plan/app/pages/PlansPage.tsx +107 -7
  115. package/corpus/templates/plan/app/root.tsx +7 -1
  116. package/corpus/templates/plan/shared/share-attribution.ts +72 -0
  117. package/corpus/templates/slides/CHANGELOG.md +10 -0
  118. package/corpus/templates/slides/app/root.tsx +7 -1
  119. package/corpus/templates/videos/CHANGELOG.md +4 -151
  120. package/corpus/templates/videos/app/root.tsx +7 -1
  121. package/dist/changelog/parse.d.ts +70 -0
  122. package/dist/changelog/parse.d.ts.map +1 -0
  123. package/dist/changelog/parse.js +200 -0
  124. package/dist/changelog/parse.js.map +1 -0
  125. package/dist/cli/changelog.d.ts +4 -0
  126. package/dist/cli/changelog.d.ts.map +1 -0
  127. package/dist/cli/changelog.js +166 -0
  128. package/dist/cli/changelog.js.map +1 -0
  129. package/dist/cli/index.js +18 -0
  130. package/dist/cli/index.js.map +1 -1
  131. package/dist/cli/recap.d.ts.map +1 -1
  132. package/dist/cli/recap.js +22 -1
  133. package/dist/cli/recap.js.map +1 -1
  134. package/dist/cli/skills.d.ts +3 -3
  135. package/dist/cli/skills.d.ts.map +1 -1
  136. package/dist/cli/skills.js +72 -45
  137. package/dist/cli/skills.js.map +1 -1
  138. package/dist/client/CommandMenu.d.ts +17 -1
  139. package/dist/client/CommandMenu.d.ts.map +1 -1
  140. package/dist/client/CommandMenu.js +49 -13
  141. package/dist/client/CommandMenu.js.map +1 -1
  142. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  143. package/dist/client/agent-chat-adapter.js +43 -6
  144. package/dist/client/agent-chat-adapter.js.map +1 -1
  145. package/dist/client/analytics.d.ts +18 -0
  146. package/dist/client/analytics.d.ts.map +1 -1
  147. package/dist/client/analytics.js +177 -0
  148. package/dist/client/analytics.js.map +1 -1
  149. package/dist/client/changelog/Changelog.d.ts +47 -0
  150. package/dist/client/changelog/Changelog.d.ts.map +1 -0
  151. package/dist/client/changelog/Changelog.js +138 -0
  152. package/dist/client/changelog/Changelog.js.map +1 -0
  153. package/dist/client/index.d.ts +2 -1
  154. package/dist/client/index.d.ts.map +1 -1
  155. package/dist/client/index.js +2 -1
  156. package/dist/client/index.js.map +1 -1
  157. package/dist/client/mcp-apps/McpAppRenderer.d.ts +1 -0
  158. package/dist/client/mcp-apps/McpAppRenderer.d.ts.map +1 -1
  159. package/dist/client/mcp-apps/McpAppRenderer.js +42 -1
  160. package/dist/client/mcp-apps/McpAppRenderer.js.map +1 -1
  161. package/dist/client/settings/useBuilderStatus.d.ts +2 -0
  162. package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
  163. package/dist/client/settings/useBuilderStatus.js +58 -10
  164. package/dist/client/settings/useBuilderStatus.js.map +1 -1
  165. package/dist/mcp/build-server.js +1 -1
  166. package/dist/mcp/build-server.js.map +1 -1
  167. package/dist/mcp/embed-app.d.ts.map +1 -1
  168. package/dist/mcp/embed-app.js +112 -6
  169. package/dist/mcp/embed-app.js.map +1 -1
  170. package/dist/server/attribution.d.ts +83 -0
  171. package/dist/server/attribution.d.ts.map +1 -0
  172. package/dist/server/attribution.js +178 -0
  173. package/dist/server/attribution.js.map +1 -0
  174. package/dist/server/auth.d.ts.map +1 -1
  175. package/dist/server/auth.js +7 -0
  176. package/dist/server/auth.js.map +1 -1
  177. package/dist/server/better-auth-instance.d.ts +9 -1
  178. package/dist/server/better-auth-instance.d.ts.map +1 -1
  179. package/dist/server/better-auth-instance.js +32 -2
  180. package/dist/server/better-auth-instance.js.map +1 -1
  181. package/dist/server/builder-browser.d.ts +4 -0
  182. package/dist/server/builder-browser.d.ts.map +1 -1
  183. package/dist/server/builder-browser.js +20 -1
  184. package/dist/server/builder-browser.js.map +1 -1
  185. package/dist/server/core-routes-plugin.d.ts +25 -0
  186. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  187. package/dist/server/core-routes-plugin.js +38 -0
  188. package/dist/server/core-routes-plugin.js.map +1 -1
  189. package/dist/server/google-oauth.d.ts.map +1 -1
  190. package/dist/server/google-oauth.js +16 -2
  191. package/dist/server/google-oauth.js.map +1 -1
  192. package/dist/shared/mcp-embed-headers.js +1 -1
  193. package/dist/shared/mcp-embed-headers.js.map +1 -1
  194. package/dist/styles/agent-conversation.css +21 -0
  195. package/dist/templates/workspace-core/.agents/skills/changelog/SKILL.md +111 -0
  196. package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +46 -14
  197. package/package.json +1 -1
  198. package/src/templates/workspace-core/.agents/skills/changelog/SKILL.md +111 -0
  199. package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +46 -14
  200. /package/corpus/templates/analytics/app/routes/{dashboards.tsx → dashboards._index.tsx} +0 -0
@@ -48,6 +48,7 @@ import {
48
48
  DropdownMenu,
49
49
  DropdownMenuContent,
50
50
  DropdownMenuItem,
51
+ DropdownMenuLabel,
51
52
  DropdownMenuSeparator,
52
53
  DropdownMenuTrigger,
53
54
  } from "@/components/ui/dropdown-menu";
@@ -995,53 +996,107 @@ export default function SqlDashboardPage() {
995
996
  </Button>
996
997
  </AddPanelPopover>
997
998
  ) : null}
998
- {canEdit || canManage ? (
999
- <DropdownMenu>
1000
- <Tooltip>
1001
- <TooltipTrigger asChild>
1002
- <DropdownMenuTrigger asChild>
1003
- <Button
1004
- size="sm"
1005
- variant="ghost"
1006
- className="text-muted-foreground hover:text-foreground"
1007
- aria-label="Dashboard actions"
1008
- >
1009
- <IconDots className="h-4 w-4" />
1010
- </Button>
1011
- </DropdownMenuTrigger>
1012
- </TooltipTrigger>
1013
- <TooltipContent>More actions</TooltipContent>
1014
- </Tooltip>
1015
- <DropdownMenuContent align="end" className="w-44">
1016
- {canEdit && !archivedAt ? (
1017
- <DropdownMenuItem
1018
- onSelect={(event) => {
1019
- event.preventDefault();
1020
- void handleArchive();
1021
- }}
1022
- >
1023
- <IconArchive className="mr-2 h-3.5 w-3.5" />
1024
- Archive
1025
- </DropdownMenuItem>
1026
- ) : null}
1027
- {canEdit && !archivedAt && canManage ? (
1028
- <DropdownMenuSeparator />
1029
- ) : null}
1030
- {canManage ? (
1031
- <DropdownMenuItem
1032
- onSelect={(event) => {
1033
- event.preventDefault();
1034
- setConfirmDeleteOpen(true);
1035
- }}
1036
- className="text-destructive focus:text-destructive"
999
+ <DropdownMenu>
1000
+ <Tooltip>
1001
+ <TooltipTrigger asChild>
1002
+ <DropdownMenuTrigger asChild>
1003
+ <Button
1004
+ size="sm"
1005
+ variant="ghost"
1006
+ className="text-muted-foreground hover:text-foreground"
1007
+ aria-label="Dashboard details and actions"
1037
1008
  >
1038
- <IconTrash className="mr-2 h-3.5 w-3.5" />
1039
- Delete permanently
1040
- </DropdownMenuItem>
1041
- ) : null}
1042
- </DropdownMenuContent>
1043
- </DropdownMenu>
1044
- ) : null}
1009
+ <IconDots className="h-4 w-4" />
1010
+ </Button>
1011
+ </DropdownMenuTrigger>
1012
+ </TooltipTrigger>
1013
+ <TooltipContent>Details</TooltipContent>
1014
+ </Tooltip>
1015
+ <DropdownMenuContent align="end" className="w-56">
1016
+ <DropdownMenuLabel className="font-normal">
1017
+ <div className="flex flex-col gap-1.5 text-xs text-muted-foreground">
1018
+ {dashboardUpdatedAt && (
1019
+ <span className="flex items-center gap-1.5">
1020
+ <IconClock className="h-3 w-3" />
1021
+ Updated{" "}
1022
+ {new Date(dashboardUpdatedAt).toLocaleDateString("en-US", {
1023
+ year: "numeric",
1024
+ month: "short",
1025
+ day: "numeric",
1026
+ })}
1027
+ </span>
1028
+ )}
1029
+ {dashboardOwner && (
1030
+ <span className="flex items-center gap-1.5">
1031
+ <IconUser className="h-3 w-3" />
1032
+ {dashboardOwner.split("@")[0]}
1033
+ </span>
1034
+ )}
1035
+ {dashboardVisibility ? (
1036
+ <span
1037
+ className={`flex items-center gap-1.5 font-medium ${
1038
+ dashboardVisibility === "public"
1039
+ ? "text-green-600"
1040
+ : dashboardVisibility === "org"
1041
+ ? "text-blue-600"
1042
+ : "text-yellow-600"
1043
+ }`}
1044
+ >
1045
+ <span
1046
+ className={`h-1.5 w-1.5 rounded-full ${
1047
+ dashboardVisibility === "public"
1048
+ ? "bg-green-500"
1049
+ : dashboardVisibility === "org"
1050
+ ? "bg-blue-500"
1051
+ : "bg-yellow-500"
1052
+ }`}
1053
+ />
1054
+ {dashboardVisibility === "public"
1055
+ ? "Public"
1056
+ : dashboardVisibility === "org"
1057
+ ? "Shared with org"
1058
+ : "Private"}
1059
+ </span>
1060
+ ) : null}
1061
+ {hiddenAt && (
1062
+ <span className="flex items-center gap-1.5 font-medium text-amber-600 dark:text-amber-400">
1063
+ <IconEyeOff className="h-3 w-3" />
1064
+ Hidden
1065
+ </span>
1066
+ )}
1067
+ </div>
1068
+ </DropdownMenuLabel>
1069
+ {(canEdit && !archivedAt) || canManage ? (
1070
+ <DropdownMenuSeparator />
1071
+ ) : null}
1072
+ {canEdit && !archivedAt ? (
1073
+ <DropdownMenuItem
1074
+ onSelect={(event) => {
1075
+ event.preventDefault();
1076
+ void handleArchive();
1077
+ }}
1078
+ >
1079
+ <IconArchive className="mr-2 h-3.5 w-3.5" />
1080
+ Archive
1081
+ </DropdownMenuItem>
1082
+ ) : null}
1083
+ {canEdit && !archivedAt && canManage ? (
1084
+ <DropdownMenuSeparator />
1085
+ ) : null}
1086
+ {canManage ? (
1087
+ <DropdownMenuItem
1088
+ onSelect={(event) => {
1089
+ event.preventDefault();
1090
+ setConfirmDeleteOpen(true);
1091
+ }}
1092
+ className="text-destructive focus:text-destructive"
1093
+ >
1094
+ <IconTrash className="mr-2 h-3.5 w-3.5" />
1095
+ Delete permanently
1096
+ </DropdownMenuItem>
1097
+ ) : null}
1098
+ </DropdownMenuContent>
1099
+ </DropdownMenu>
1045
1100
  {canManage ? (
1046
1101
  <AlertDialog
1047
1102
  open={confirmDeleteOpen}
@@ -1152,59 +1207,6 @@ export default function SqlDashboardPage() {
1152
1207
  </Button>
1153
1208
  </Alert>
1154
1209
  ) : null}
1155
- {/* Author, last updated, and visibility metadata */}
1156
- <div className="flex flex-wrap items-center gap-3 text-xs text-muted-foreground">
1157
- {hiddenAt && (
1158
- <span className="flex items-center gap-1.5 font-medium text-amber-600 dark:text-amber-400">
1159
- <IconEyeOff className="h-3 w-3" />
1160
- Hidden
1161
- </span>
1162
- )}
1163
- {dashboardUpdatedAt && (
1164
- <span className="flex items-center gap-1">
1165
- <IconClock className="h-3 w-3" />
1166
- Updated{" "}
1167
- {new Date(dashboardUpdatedAt).toLocaleDateString("en-US", {
1168
- year: "numeric",
1169
- month: "short",
1170
- day: "numeric",
1171
- })}
1172
- </span>
1173
- )}
1174
- {dashboardOwner && (
1175
- <span className="flex items-center gap-1">
1176
- <IconUser className="h-3 w-3" />
1177
- {dashboardOwner.split("@")[0]}
1178
- </span>
1179
- )}
1180
- {dashboardVisibility ? (
1181
- <span
1182
- className={`flex items-center gap-1.5 font-medium ${
1183
- dashboardVisibility === "public"
1184
- ? "text-green-600"
1185
- : dashboardVisibility === "org"
1186
- ? "text-blue-600"
1187
- : "text-yellow-600"
1188
- }`}
1189
- >
1190
- <span
1191
- className={`h-1.5 w-1.5 rounded-full ${
1192
- dashboardVisibility === "public"
1193
- ? "bg-green-500"
1194
- : dashboardVisibility === "org"
1195
- ? "bg-blue-500"
1196
- : "bg-yellow-500"
1197
- }`}
1198
- />
1199
- {dashboardVisibility === "public"
1200
- ? "Public"
1201
- : dashboardVisibility === "org"
1202
- ? "Shared with org"
1203
- : "Private"}
1204
- </span>
1205
- ) : null}
1206
- </div>
1207
-
1208
1210
  {/* Description (click to edit) */}
1209
1211
  {editingDescription && canEdit ? (
1210
1212
  <Textarea
@@ -134,7 +134,7 @@ export default function OverviewPage() {
134
134
  key={`${item.type}:${item.id}`}
135
135
  to={
136
136
  item.type === "dashboard"
137
- ? `/adhoc/${item.id}`
137
+ ? `/dashboards/${item.id}`
138
138
  : `/analyses/${item.id}`
139
139
  }
140
140
  className="block"
@@ -1,9 +1,22 @@
1
- import AdhocRouter from "@/pages/adhoc/AdhocRouter";
1
+ import { redirect, type LoaderFunctionArgs } from "react-router";
2
2
 
3
- export function meta() {
4
- return [{ title: "Dashboard Analytics" }];
3
+ // `/adhoc/:id` is the legacy dashboard URL. The canonical, user-facing URL is
4
+ // now `/dashboards/:id`. Forward old links (bookmarks, deep links, query
5
+ // params like `?id=` and `?config=`) to the canonical path so nothing breaks.
6
+ function target({ request, params }: LoaderFunctionArgs): string {
7
+ const url = new URL(request.url);
8
+ const id = params.id ?? "";
9
+ return `/dashboards/${encodeURIComponent(id)}${url.search}${url.hash}`;
5
10
  }
6
11
 
7
- export default function AdhocRoute() {
8
- return <AdhocRouter />;
12
+ export function loader(args: LoaderFunctionArgs) {
13
+ throw redirect(target(args));
14
+ }
15
+
16
+ export function clientLoader(args: LoaderFunctionArgs) {
17
+ throw redirect(target(args));
18
+ }
19
+
20
+ export default function AdhocRedirectRoute() {
21
+ return null;
9
22
  }
@@ -76,7 +76,7 @@ function dashboardPathForTemplate(
76
76
  if (template.id === "demo-node-exporter") {
77
77
  return demoNodeExporterDashboardPath(dashboardId, options);
78
78
  }
79
- return `/adhoc/${dashboardId}`;
79
+ return `/dashboards/${dashboardId}`;
80
80
  }
81
81
 
82
82
  function sourceLabel(source: string): string {
@@ -0,0 +1,9 @@
1
+ import AdhocRouter from "@/pages/adhoc/AdhocRouter";
2
+
3
+ export function meta() {
4
+ return [{ title: "Dashboard — Analytics" }];
5
+ }
6
+
7
+ export default function DashboardRoute() {
8
+ return <AdhocRouter />;
9
+ }
@@ -1,6 +1,6 @@
1
1
  import { redirect, type LoaderFunctionArgs } from "react-router";
2
2
 
3
- const TRAFFIC_DASHBOARD_PATH = "/adhoc/agent-native-templates-first-party";
3
+ const TRAFFIC_DASHBOARD_PATH = "/dashboards/agent-native-templates-first-party";
4
4
 
5
5
  function target(request: Request): string {
6
6
  const url = new URL(request.url);
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-06-23
4
+ ---
5
+
6
+ The Dashboards and Analyses sidebar settings buttons now stay hidden until you hover the section, for a cleaner sidebar
@@ -187,6 +187,151 @@
187
187
  "yKey": "count",
188
188
  "color": "#10b981"
189
189
  }
190
+ },
191
+ {
192
+ "id": "virality-referrals-section",
193
+ "title": "Virality & Referrals",
194
+ "chartType": "section",
195
+ "width": 1,
196
+ "columns": 3,
197
+ "config": {
198
+ "description": "Signups attributed to shared clips and plans, who is driving referrals, and the share view to click to signup funnel. referral_* and utm_* fields are read from the signup event's properties JSON; share_view and share_cta_click track shared-surface engagement."
199
+ }
200
+ },
201
+ {
202
+ "id": "referred-signups-30d",
203
+ "title": "Referred Signups (30d)",
204
+ "chartType": "metric",
205
+ "source": "first-party",
206
+ "width": 1,
207
+ "sql": "SELECT COUNT(*) AS count FROM analytics_events WHERE event_name = 'signup' AND timestamp::timestamptz >= now() - interval '30 days' AND properties::jsonb ->> 'referral_source' IS NOT NULL AND properties::jsonb ->> 'referral_source' <> '' AND properties::jsonb ->> 'referral_source' <> 'direct'",
208
+ "config": {
209
+ "yKey": "count",
210
+ "yFormatter": "number",
211
+ "description": "Signups in the last 30 days with a non-direct referral_source (clip_share, plan_share, external)."
212
+ }
213
+ },
214
+ {
215
+ "id": "viral-signup-share-30d",
216
+ "title": "Viral Signup Share (30d)",
217
+ "chartType": "metric",
218
+ "source": "first-party",
219
+ "width": 1,
220
+ "sql": "SELECT CASE WHEN COUNT(*) = 0 THEN 0 ELSE 1.0 * COUNT(*) FILTER (WHERE properties::jsonb ->> 'referral_source' IS NOT NULL AND properties::jsonb ->> 'referral_source' <> '' AND properties::jsonb ->> 'referral_source' <> 'direct') / COUNT(*) END AS rate FROM analytics_events WHERE event_name = 'signup' AND timestamp::timestamptz >= now() - interval '30 days'",
221
+ "config": {
222
+ "yKey": "rate",
223
+ "yFormatter": "percent",
224
+ "description": "Headline virality number: referred signups divided by all signups over the last 30 days."
225
+ }
226
+ },
227
+ {
228
+ "id": "clip-share-signups-30d",
229
+ "title": "Clip-Share Signups (30d)",
230
+ "chartType": "metric",
231
+ "source": "first-party",
232
+ "width": 1,
233
+ "sql": "SELECT COUNT(*) AS count FROM analytics_events WHERE event_name = 'signup' AND timestamp::timestamptz >= now() - interval '30 days' AND properties::jsonb ->> 'referral_source' = 'clip_share'",
234
+ "config": {
235
+ "yKey": "count",
236
+ "yFormatter": "number",
237
+ "description": "Signups in the last 30 days attributed to a shared clip."
238
+ }
239
+ },
240
+ {
241
+ "id": "signups-by-referral-source",
242
+ "title": "Signups by Referral Source (90d)",
243
+ "chartType": "bar",
244
+ "source": "first-party",
245
+ "width": 1,
246
+ "sql": "SELECT COALESCE(NULLIF(properties::jsonb ->> 'referral_source', ''), 'direct') AS referral_source, COUNT(*) AS count FROM analytics_events WHERE event_name = 'signup' AND timestamp::timestamptz >= now() - interval '90 days' GROUP BY COALESCE(NULLIF(properties::jsonb ->> 'referral_source', ''), 'direct') ORDER BY count DESC LIMIT 20",
247
+ "config": {
248
+ "xKey": "referral_source",
249
+ "yKey": "count",
250
+ "color": "var(--brand-purple)",
251
+ "description": "Signups grouped by referral_source over the last 90 days. Null/empty sources are bucketed as direct."
252
+ }
253
+ },
254
+ {
255
+ "id": "referred-signups-over-time",
256
+ "title": "Referred Signups Over Time (90d)",
257
+ "chartType": "area",
258
+ "source": "first-party",
259
+ "width": 2,
260
+ "sql": "SELECT substr(timestamp, 1, 10) AS date, COUNT(*) AS count FROM analytics_events WHERE event_name = 'signup' AND timestamp::timestamptz >= now() - interval '90 days' AND properties::jsonb ->> 'referral_source' IS NOT NULL AND properties::jsonb ->> 'referral_source' <> '' AND properties::jsonb ->> 'referral_source' <> 'direct' GROUP BY substr(timestamp, 1, 10) ORDER BY date",
261
+ "config": {
262
+ "xKey": "date",
263
+ "yKey": "count",
264
+ "color": "var(--brand-purple)",
265
+ "description": "Daily referred (non-direct) signups over the last 90 days."
266
+ }
267
+ },
268
+ {
269
+ "id": "top-referrers",
270
+ "title": "Top Referrers (90d)",
271
+ "chartType": "table",
272
+ "source": "first-party",
273
+ "width": 1,
274
+ "sql": "SELECT properties::jsonb ->> 'referrer_user' AS referrer_user, COUNT(*) AS signups FROM analytics_events WHERE event_name = 'signup' AND timestamp::timestamptz >= now() - interval '90 days' AND properties::jsonb ->> 'referrer_user' IS NOT NULL AND properties::jsonb ->> 'referrer_user' <> '' GROUP BY properties::jsonb ->> 'referrer_user' ORDER BY signups DESC LIMIT 20",
275
+ "config": {
276
+ "description": "Users (by id) driving the most referred signups over the last 90 days.",
277
+ "columns": [
278
+ { "key": "referrer_user", "label": "Referrer (user id)" },
279
+ { "key": "signups", "label": "Signups", "format": "number" }
280
+ ]
281
+ }
282
+ },
283
+ {
284
+ "id": "share-funnel-30d",
285
+ "title": "Share Funnel (30d)",
286
+ "chartType": "bar",
287
+ "source": "first-party",
288
+ "width": 2,
289
+ "sql": "SELECT 'Share views' AS stage, COUNT(*) AS count FROM analytics_events WHERE event_name = 'share_view' AND timestamp::timestamptz >= now() - interval '30 days' UNION ALL SELECT 'CTA clicks' AS stage, COUNT(*) AS count FROM analytics_events WHERE event_name = 'share_cta_click' AND timestamp::timestamptz >= now() - interval '30 days' UNION ALL SELECT 'Clip-share signups' AS stage, COUNT(*) AS count FROM analytics_events WHERE event_name = 'signup' AND timestamp::timestamptz >= now() - interval '30 days' AND properties::jsonb ->> 'referral_source' = 'clip_share'",
290
+ "config": {
291
+ "xKey": "stage",
292
+ "yKey": "count",
293
+ "color": "var(--brand-teal)",
294
+ "description": "View to click to signup funnel for shared surfaces over the last 30 days: share_view, share_cta_click, then clip_share signups."
295
+ }
296
+ },
297
+ {
298
+ "id": "viral-participation-rate-90d",
299
+ "title": "Viral Participation Rate (90d)",
300
+ "chartType": "metric",
301
+ "source": "first-party",
302
+ "width": 1,
303
+ "sql": "SELECT COALESCE(COUNT(*) FILTER (WHERE recent.auth_user_id IN (SELECT properties::jsonb ->> 'referrer_user' FROM analytics_events WHERE event_name = 'signup' AND properties::jsonb ->> 'referrer_user' IS NOT NULL AND properties::jsonb ->> 'referrer_user' <> ''))::float / NULLIF(COUNT(*), 0), 0) AS rate FROM (SELECT DISTINCT properties::jsonb ->> 'auth_user_id' AS auth_user_id FROM analytics_events WHERE event_name = 'signup' AND timestamp::timestamptz >= now() - interval '90 days' AND properties::jsonb ->> 'auth_user_id' IS NOT NULL AND properties::jsonb ->> 'auth_user_id' <> '') AS recent",
304
+ "config": {
305
+ "yKey": "rate",
306
+ "yFormatter": "percent",
307
+ "description": "Share of users who signed up in the last 90d who have since referred at least one new signup (referrers counted across all time). New cohorts under-count: recent signups haven't had time to refer yet. Matches auth_user_id to referrer_user (both better-auth ids)."
308
+ }
309
+ },
310
+ {
311
+ "id": "viral-coefficient-90d",
312
+ "title": "Viral Coefficient K (90d)",
313
+ "chartType": "metric",
314
+ "source": "first-party",
315
+ "width": 1,
316
+ "sql": "SELECT COALESCE(COUNT(*) FILTER (WHERE properties::jsonb ->> 'referrer_user' IS NOT NULL AND properties::jsonb ->> 'referrer_user' <> '')::float / NULLIF(COUNT(DISTINCT properties::jsonb ->> 'auth_user_id'), 0), 0) AS k FROM analytics_events WHERE event_name = 'signup' AND timestamp::timestamptz >= now() - interval '90 days'",
317
+ "config": {
318
+ "yKey": "k",
319
+ "yFormatter": "number",
320
+ "description": "Viral coefficient (K): referred signups divided by new users over the last 90d. K >= 1 means each user brings on at least one more, i.e. self-sustaining growth."
321
+ }
322
+ },
323
+ {
324
+ "id": "activated-referrers-90d",
325
+ "title": "Activated Referrers (90d)",
326
+ "chartType": "metric",
327
+ "source": "first-party",
328
+ "width": 1,
329
+ "sql": "SELECT COUNT(DISTINCT properties::jsonb ->> 'referrer_user') AS count FROM analytics_events WHERE event_name = 'signup' AND timestamp::timestamptz >= now() - interval '90 days' AND properties::jsonb ->> 'referrer_user' IS NOT NULL AND properties::jsonb ->> 'referrer_user' <> ''",
330
+ "config": {
331
+ "yKey": "count",
332
+ "yFormatter": "number",
333
+ "description": "Distinct users who drove at least one referred signup in the last 90d (distinct referrer_user on signups in the window)."
334
+ }
190
335
  }
191
336
  ]
192
337
  }
@@ -11,7 +11,7 @@ registerShareableResource({
11
11
  sharesTable: schema.dashboardShares,
12
12
  displayName: "Dashboard",
13
13
  titleColumn: "title",
14
- getResourcePath: (dashboard) => `/adhoc/${dashboard.id}`,
14
+ getResourcePath: (dashboard) => `/dashboards/${dashboard.id}`,
15
15
  getDb,
16
16
  });
17
17
 
@@ -924,8 +924,15 @@ export const dashboardCatalogEntries: DashboardCatalogEntry[] = [
924
924
  category: "Product",
925
925
  defaultDashboardId: "agent-native-templates-first-party",
926
926
  dataSources: ["first-party"],
927
- tags: ["templates", "traffic", "signups", "sessions"],
928
- panelCount: 14,
927
+ tags: [
928
+ "templates",
929
+ "traffic",
930
+ "signups",
931
+ "sessions",
932
+ "referrals",
933
+ "virality",
934
+ ],
935
+ panelCount: 25,
929
936
  version: CATALOG_VERSION,
930
937
  recommended: true,
931
938
  buildConfig: () => seedConfig("agent-native-templates-first-party"),
@@ -60,7 +60,7 @@ function demoDashboardPath(dashboardId: string): string {
60
60
  const params = new URLSearchParams({
61
61
  tab: DEMO_NODE_EXPORTER_DEFAULT_TAB,
62
62
  });
63
- return `/adhoc/${dashboardId}?${params.toString()}`;
63
+ return `/dashboards/${dashboardId}?${params.toString()}`;
64
64
  }
65
65
 
66
66
  function nowIso(): string {
@@ -240,11 +240,11 @@ export default createAgentChatPlugin({
240
240
  return filtered.slice(0, 20).map((d) => ({
241
241
  id: `dashboard:${d.id}`,
242
242
  label: d.name || "Untitled dashboard",
243
- description: `/adhoc/${d.id}`,
243
+ description: `/dashboards/${d.id}`,
244
244
  icon: "deck",
245
245
  refType: "dashboard",
246
246
  refId: d.id,
247
- refPath: `/adhoc/${d.id}`,
247
+ refPath: `/dashboards/${d.id}`,
248
248
  }));
249
249
  } catch (err) {
250
250
  console.error("[analytics] Dashboard mention provider failed:", err);
@@ -3,17 +3,17 @@ import { createCoreRoutesPlugin } from "@agent-native/core/server";
3
3
  // Land external-agent deep links straight on the real SPA route. Without
4
4
  // this, `/_agent-native/open?app=analytics&view=adhoc&dashboardId=…` falls
5
5
  // back to `/<view>` = `/adhoc`, which has no matching route (the dashboard
6
- // route is `adhoc.$id.tsx` → `/adhoc/:id`) and 404s — so an "Open in
7
- // Analytics" link produced by `update-dashboard` / `save-analysis` for a
6
+ // route is `dashboards.$id.tsx` → `/dashboards/:id`) and 404s — so an "Open
7
+ // in Analytics" link produced by `update-dashboard` / `save-analysis` for a
8
8
  // connected Claude Code / Codex / Cowork never opened the record.
9
9
  export default createCoreRoutesPlugin({
10
10
  resolveOpenPath: ({ view, params }) => {
11
- if (params.dashboardId) return `/adhoc/${params.dashboardId}`;
11
+ if (params.dashboardId) return `/dashboards/${params.dashboardId}`;
12
12
  if (params.analysisId) return `/analyses/${params.analysisId}`;
13
13
  if (view === "analyses") return "/analyses";
14
- // `adhoc`/unknown with no id: there is no bare `/adhoc` route — send to
15
- // the app root rather than 404 (the polled `navigate` command still
16
- // applies any record focus once the SPA is loaded).
14
+ // `adhoc`/unknown with no id: there is no bare `/dashboards` record route —
15
+ // send to the app root rather than 404 (the polled `navigate` command
16
+ // still applies any record focus once the SPA is loaded).
17
17
  if (view === "adhoc") return "/";
18
18
  return null;
19
19
  },
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ All notable user-facing changes to Assets are documented here. Open it any
4
+ time from the command menu (Cmd+K → "What's new") or from Settings.
5
+
6
+ ## 2026-06-23
7
+
8
+ ### Added
9
+
10
+ - See what's new right inside Assets — a changelog now lives in the command menu (Cmd+K) and in Settings.
@@ -16,6 +16,7 @@ import {
16
16
  import { Toaster } from "@/components/ui/sonner";
17
17
  import { IconSun, IconMoon } from "@tabler/icons-react";
18
18
  import { Layout as AppLayout } from "@/components/layout/Layout";
19
+ import changelog from "../CHANGELOG.md?raw";
19
20
  import type { LinksFunction } from "react-router";
20
21
  import stylesheet from "./global.css?url";
21
22
 
@@ -106,7 +107,12 @@ export default function Root() {
106
107
  <AppProviders queryClient={queryClient}>
107
108
  <DbSyncSetup />
108
109
  <Toaster richColors position="bottom-left" />
109
- <CommandMenu open={cmdkOpen} onOpenChange={setCmdkOpen}>
110
+ <CommandMenu
111
+ open={cmdkOpen}
112
+ onOpenChange={setCmdkOpen}
113
+ changelog={changelog}
114
+ changelogKey="assets"
115
+ >
110
116
  <CommandMenu.Group heading="Actions">
111
117
  <CommandMenu.Item onSelect={() => {}}>Search</CommandMenu.Item>
112
118
  </CommandMenu.Group>
@@ -1,9 +1,11 @@
1
1
  import {
2
+ ChangelogSettingsCard,
2
3
  agentNativePath,
3
4
  useActionQuery,
4
5
  useBuilderConnectFlow,
5
6
  useBuilderStatus,
6
7
  } from "@agent-native/core/client";
8
+ import changelog from "../../CHANGELOG.md?raw";
7
9
  import {
8
10
  useOnboarding,
9
11
  type OnboardingMethod,
@@ -75,6 +77,10 @@ export default function SettingsPage() {
75
77
  description="Asset generation, storage, and brand kit access."
76
78
  className="max-w-4xl space-y-6"
77
79
  >
80
+ <div className="max-w-2xl">
81
+ <ChangelogSettingsCard markdown={changelog} />
82
+ </div>
83
+
78
84
  <div className="max-w-2xl">
79
85
  <h2 className="text-lg font-semibold tracking-tight">Connections</h2>
80
86
  <p className="mt-1 text-sm leading-6 text-muted-foreground">
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ All notable user-facing changes to Agent-Native Brain are documented here. Open it any
4
+ time from the command menu (Cmd+K → "What's new") or from Settings.
5
+
6
+ ## 2026-06-23
7
+
8
+ ### Added
9
+
10
+ - See what's new right inside Agent-Native Brain — a changelog now lives in the command menu (Cmd+K) and in Settings.
@@ -24,6 +24,7 @@ import { Layout as AppLayout } from "@/components/layout/Layout";
24
24
  import { useDistillationBridge } from "@/hooks/use-distillation-bridge";
25
25
  import { useNavigationState } from "@/hooks/use-navigation-state";
26
26
  import { TAB_ID } from "@/lib/tab-id";
27
+ import changelog from "../CHANGELOG.md?raw";
27
28
  import type { LinksFunction } from "react-router";
28
29
  import stylesheet from "./global.css?url";
29
30
 
@@ -137,7 +138,12 @@ function AppContent() {
137
138
  useCommandMenuShortcut(useCallback(() => setCmdkOpen(true), []));
138
139
  return (
139
140
  <>
140
- <CommandMenu open={cmdkOpen} onOpenChange={setCmdkOpen}>
141
+ <CommandMenu
142
+ open={cmdkOpen}
143
+ onOpenChange={setCmdkOpen}
144
+ changelog={changelog}
145
+ changelogKey="brain"
146
+ >
141
147
  <CommandMenu.Group heading="Navigate">
142
148
  <CommandMenu.Item onSelect={() => navigate("/")}>
143
149
  Ask Brain
@@ -1,5 +1,10 @@
1
1
  import { useEffect, useMemo, useState } from "react";
2
- import { useActionMutation, useActionQuery } from "@agent-native/core/client";
2
+ import {
3
+ ChangelogSettingsCard,
4
+ useActionMutation,
5
+ useActionQuery,
6
+ } from "@agent-native/core/client";
7
+ import changelog from "../../CHANGELOG.md?raw";
3
8
  import {
4
9
  IconAdjustments,
5
10
  IconBuilding,
@@ -146,6 +151,8 @@ export default function SettingsRoute() {
146
151
 
147
152
  <div className="grid gap-5 p-4 sm:p-5 lg:grid-cols-[minmax(0,1fr)_360px] lg:p-7">
148
153
  <main className="grid gap-5">
154
+ <ChangelogSettingsCard markdown={changelog} />
155
+
149
156
  <Card>
150
157
  <CardHeader>
151
158
  <CardTitle className="flex items-center gap-2 text-base">