@agent-native/core 0.98.0 → 0.98.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +24 -0
  3. package/corpus/core/package.json +2 -2
  4. package/corpus/core/src/action-change-marker.ts +8 -1
  5. package/corpus/core/src/changelog/parse.ts +8 -3
  6. package/corpus/core/src/client/AgentPanel.tsx +2 -3
  7. package/corpus/core/src/client/AssistantChat.tsx +83 -39
  8. package/corpus/core/src/client/analytics.ts +29 -0
  9. package/corpus/core/src/client/blocks/library/wireframe-kit.tsx +16 -2
  10. package/corpus/core/src/client/chat-model-groups.ts +28 -0
  11. package/corpus/core/src/client/composer/RealtimeVoiceMode.tsx +442 -85
  12. package/corpus/core/src/client/composer/TiptapComposer.tsx +46 -29
  13. package/corpus/core/src/client/composer/VoiceButton.tsx +2 -1
  14. package/corpus/core/src/client/composer/index.ts +5 -0
  15. package/corpus/core/src/client/composer/useRealtimeVoiceMode.tsx +1206 -67
  16. package/corpus/core/src/client/index.ts +5 -0
  17. package/corpus/core/src/client/progress/RunsTray.tsx +13 -1
  18. package/corpus/core/src/client/settings/SecretsSection.tsx +387 -266
  19. package/corpus/core/src/client/settings/SettingsPanel.tsx +117 -38
  20. package/corpus/core/src/client/use-action.ts +12 -0
  21. package/corpus/core/src/client/use-agent-chat-home-handoff.ts +8 -1
  22. package/corpus/core/src/client/use-agent-engine-configured.ts +17 -3
  23. package/corpus/core/src/client/use-db-sync.ts +84 -34
  24. package/corpus/core/src/client/use-pausing-interval.ts +14 -4
  25. package/corpus/core/src/integrations/scope-store.ts +1 -1
  26. package/corpus/core/src/integrations/usage-budget-store.ts +1 -1
  27. package/corpus/core/src/localization/default-messages.ts +44 -1
  28. package/corpus/core/src/mcp-client/workspace-servers.ts +4 -4
  29. package/corpus/core/src/secrets/crypto.ts +28 -8
  30. package/corpus/core/src/server/action-change.ts +5 -1
  31. package/corpus/core/src/server/action-routes.ts +8 -0
  32. package/corpus/core/src/server/poll.ts +1 -0
  33. package/corpus/core/src/server/realtime-voice.ts +299 -13
  34. package/corpus/core/src/styles/agent-native.css +2 -2
  35. package/corpus/core/src/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
  36. package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
  37. package/corpus/core/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
  38. package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
  39. package/corpus/core/src/vite/client.ts +9 -1
  40. package/corpus/templates/analytics/.agents/skills/real-time-sync/SKILL.md +2 -2
  41. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +10 -5
  42. package/corpus/templates/analytics/AGENTS.md +9 -13
  43. package/corpus/templates/analytics/actions/navigate.ts +59 -5
  44. package/corpus/templates/analytics/actions/view-screen.ts +3 -6
  45. package/corpus/templates/analytics/app/components/ProviderCorpusJobNotifier.tsx +20 -3
  46. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +2 -1
  47. package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -1
  48. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +21 -6
  49. package/corpus/templates/analytics/app/global.css +19 -20
  50. package/corpus/templates/analytics/app/lib/provider-corpus-job-sync.ts +26 -0
  51. package/corpus/templates/analytics/app/pages/Settings.tsx +35 -341
  52. package/corpus/templates/analytics/app/pages/monitoring/ErrorsPanel.tsx +38 -7
  53. package/corpus/templates/analytics/app/pages/monitoring/MonitoringPage.tsx +1 -1
  54. package/corpus/templates/analytics/app/pages/monitoring/errors/i18n.ts +3 -0
  55. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +333 -272
  56. package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +72 -17
  57. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +297 -108
  58. package/corpus/templates/analytics/app/root.tsx +28 -8
  59. package/corpus/templates/analytics/changelog/2026-06-24-first-party-signup-and-pageview-panels-now-honor-dashboard-filters.md +2 -1
  60. package/corpus/templates/analytics/changelog/2026-06-25-referrer-dashboard-panels-show-external-domains.md +2 -1
  61. package/corpus/templates/analytics/changelog/2026-07-06-mobile-chat-header-is-more-compact-and-the-menu-button-opens-navigation.md +1 -0
  62. package/corpus/templates/analytics/changelog/2026-07-08-dashboard-history-and-chat-context.md +1 -0
  63. package/corpus/templates/analytics/changelog/2026-07-08-settings-are-cleaner-searchable-and-alerts-have-their-own-tab.md +1 -0
  64. package/corpus/templates/analytics/changelog/2026-07-08-sidebar-sections-stay-collapsed-until-opened.md +1 -0
  65. package/corpus/templates/analytics/changelog/2026-07-11-analytics-no-longer-repeatedly-restarts-background-requests-.md +6 -0
  66. package/corpus/templates/analytics/changelog/2026-07-11-analytics-only-asks-for-replay-storage-when-you-enable-sessi.md +6 -0
  67. package/corpus/templates/analytics/changelog/2026-07-11-charts-now-show-their-loading-skeleton-while-refreshing.md +6 -0
  68. package/corpus/templates/analytics/changelog/2026-07-11-realtime-voice-now-shows-a-clear-connection-indicator-comple.md +6 -0
  69. package/corpus/templates/analytics/changelog/2026-07-11-replay-event-counts-stay-hidden-until-recordings-finish-load.md +6 -0
  70. package/corpus/templates/analytics/changelog/2026-07-11-replay-storage-key-mismatches-show-actionable-setup-guidance.md +6 -0
  71. package/corpus/templates/analytics/changelog/2026-07-11-replay-timelines-group-continuous-scrolling-and-dev-tools-co.md +6 -0
  72. package/corpus/templates/analytics/changelog/2026-07-11-session-filters-now-stay-compact-in-a-settings-menu-at-mediu.md +6 -0
  73. package/corpus/templates/analytics/changelog/2026-07-11-session-replay-console-errors-now-link-to-matching-issues-or.md +6 -0
  74. package/corpus/templates/analytics/changelog/2026-07-11-session-replays-load-large-recordings-faster-and-surface-fai.md +6 -0
  75. package/corpus/templates/analytics/changelog/2026-07-11-session-replays-preserve-recorded-styles-and-viewport-layout.md +6 -0
  76. package/corpus/templates/analytics/changelog/2026-07-11-session-replays-stay-centered-instead-of-clipping-wide-recor.md +6 -0
  77. package/corpus/templates/analytics/package.json +1 -1
  78. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +1 -303
  79. package/corpus/templates/analytics/server/handlers/session-replay.ts +57 -0
  80. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +1 -1
  81. package/corpus/templates/analytics/server/lib/demo-dashboards.ts +16 -3
  82. package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +12 -0
  83. package/corpus/templates/analytics/server/lib/session-replay.ts +208 -2
  84. package/corpus/templates/analytics/server/plugins/onboarding.ts +7 -79
  85. package/corpus/templates/analytics/server/routes/api/session-replay/recordings/[recordingId]/chunks.get.ts +1 -0
  86. package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +2 -2
  87. package/corpus/templates/assets/app/global.css +4 -4
  88. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +0 -1
  89. package/corpus/templates/assets/app/routes/library.tsx +0 -1
  90. package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +2 -2
  91. package/corpus/templates/brain/app/global.css +5 -5
  92. package/corpus/templates/brain/app/root.tsx +0 -3
  93. package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +2 -2
  94. package/corpus/templates/calendar/app/root.tsx +1 -0
  95. package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +2 -2
  96. package/corpus/templates/clips/app/hooks/use-auto-title.ts +74 -8
  97. package/corpus/templates/clips/app/hooks/use-library.ts +12 -25
  98. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +20 -22
  99. package/corpus/templates/clips/changelog/2026-07-11-generated-titles-no-longer-cause-continuous-background-refreshes.md +6 -0
  100. package/corpus/templates/clips/changelog/2026-07-11-s3-storage-setup-stays-collapsed-until-you-choose-it-as-the-.md +6 -0
  101. package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +2 -2
  102. package/corpus/templates/content/app/hooks/use-comments.ts +0 -1
  103. package/corpus/templates/content/changelog/2026-07-11-comments-now-update-from-live-collaboration-events-without-c.md +6 -0
  104. package/corpus/templates/design/app/components/layout/Header.tsx +1 -1
  105. package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +2 -2
  106. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +1 -2
  107. package/corpus/templates/macros/app/root.tsx +0 -3
  108. package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +2 -2
  109. package/corpus/templates/mail/app/hooks/use-compose-state.ts +1 -0
  110. package/corpus/templates/mail/app/root.tsx +1 -12
  111. package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +2 -2
  112. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +5 -2
  113. package/corpus/templates/plan/app/hooks/use-plans.ts +6 -13
  114. package/corpus/templates/plan/app/pages/PlansPage.tsx +5 -27
  115. package/corpus/templates/plan/changelog/2026-07-11-plan-refreshes-only-when-content-changes.md +6 -0
  116. package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +4 -1
  117. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +1 -1
  118. package/corpus/templates/slides/app/components/layout/Header.tsx +1 -1
  119. package/corpus/templates/slides/app/context/DeckContext.tsx +1 -0
  120. package/corpus/templates/slides/app/hooks/use-slide-comments.ts +7 -31
  121. package/corpus/templates/slides/changelog/2026-07-11-comments-now-update-from-live-collaboration-events-without-c.md +6 -0
  122. package/dist/action-change-marker.d.ts +1 -0
  123. package/dist/action-change-marker.d.ts.map +1 -1
  124. package/dist/action-change-marker.js +7 -1
  125. package/dist/action-change-marker.js.map +1 -1
  126. package/dist/changelog/parse.d.ts +4 -2
  127. package/dist/changelog/parse.d.ts.map +1 -1
  128. package/dist/changelog/parse.js +5 -3
  129. package/dist/changelog/parse.js.map +1 -1
  130. package/dist/client/AgentPanel.d.ts.map +1 -1
  131. package/dist/client/AgentPanel.js +2 -3
  132. package/dist/client/AgentPanel.js.map +1 -1
  133. package/dist/client/AssistantChat.d.ts +7 -0
  134. package/dist/client/AssistantChat.d.ts.map +1 -1
  135. package/dist/client/AssistantChat.js +62 -40
  136. package/dist/client/AssistantChat.js.map +1 -1
  137. package/dist/client/analytics.d.ts.map +1 -1
  138. package/dist/client/analytics.js +26 -0
  139. package/dist/client/analytics.js.map +1 -1
  140. package/dist/client/blocks/library/wireframe-kit.d.ts +2 -0
  141. package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -1
  142. package/dist/client/blocks/library/wireframe-kit.js +9 -2
  143. package/dist/client/blocks/library/wireframe-kit.js.map +1 -1
  144. package/dist/client/chat-model-groups.d.ts.map +1 -1
  145. package/dist/client/chat-model-groups.js +24 -0
  146. package/dist/client/chat-model-groups.js.map +1 -1
  147. package/dist/client/composer/RealtimeVoiceMode.d.ts +40 -4
  148. package/dist/client/composer/RealtimeVoiceMode.d.ts.map +1 -1
  149. package/dist/client/composer/RealtimeVoiceMode.js +172 -42
  150. package/dist/client/composer/RealtimeVoiceMode.js.map +1 -1
  151. package/dist/client/composer/TiptapComposer.d.ts +4 -0
  152. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  153. package/dist/client/composer/TiptapComposer.js +47 -29
  154. package/dist/client/composer/TiptapComposer.js.map +1 -1
  155. package/dist/client/composer/VoiceButton.d.ts.map +1 -1
  156. package/dist/client/composer/VoiceButton.js +2 -2
  157. package/dist/client/composer/VoiceButton.js.map +1 -1
  158. package/dist/client/composer/index.d.ts +2 -2
  159. package/dist/client/composer/index.d.ts.map +1 -1
  160. package/dist/client/composer/index.js +1 -1
  161. package/dist/client/composer/index.js.map +1 -1
  162. package/dist/client/composer/useRealtimeVoiceMode.d.ts +100 -3
  163. package/dist/client/composer/useRealtimeVoiceMode.d.ts.map +1 -1
  164. package/dist/client/composer/useRealtimeVoiceMode.js +952 -51
  165. package/dist/client/composer/useRealtimeVoiceMode.js.map +1 -1
  166. package/dist/client/index.d.ts +1 -1
  167. package/dist/client/index.d.ts.map +1 -1
  168. package/dist/client/index.js +1 -1
  169. package/dist/client/index.js.map +1 -1
  170. package/dist/client/progress/RunsTray.d.ts.map +1 -1
  171. package/dist/client/progress/RunsTray.js +10 -1
  172. package/dist/client/progress/RunsTray.js.map +1 -1
  173. package/dist/client/settings/SecretsSection.d.ts +2 -3
  174. package/dist/client/settings/SecretsSection.d.ts.map +1 -1
  175. package/dist/client/settings/SecretsSection.js +46 -22
  176. package/dist/client/settings/SecretsSection.js.map +1 -1
  177. package/dist/client/settings/SettingsPanel.d.ts +9 -0
  178. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  179. package/dist/client/settings/SettingsPanel.js +46 -12
  180. package/dist/client/settings/SettingsPanel.js.map +1 -1
  181. package/dist/client/use-action.d.ts +2 -0
  182. package/dist/client/use-action.d.ts.map +1 -1
  183. package/dist/client/use-action.js +10 -0
  184. package/dist/client/use-action.js.map +1 -1
  185. package/dist/client/use-agent-chat-home-handoff.d.ts +2 -0
  186. package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
  187. package/dist/client/use-agent-chat-home-handoff.js +7 -1
  188. package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
  189. package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
  190. package/dist/client/use-agent-engine-configured.js +17 -3
  191. package/dist/client/use-agent-engine-configured.js.map +1 -1
  192. package/dist/client/use-db-sync.d.ts +8 -2
  193. package/dist/client/use-db-sync.d.ts.map +1 -1
  194. package/dist/client/use-db-sync.js +57 -32
  195. package/dist/client/use-db-sync.js.map +1 -1
  196. package/dist/client/use-pausing-interval.d.ts.map +1 -1
  197. package/dist/client/use-pausing-interval.js +16 -4
  198. package/dist/client/use-pausing-interval.js.map +1 -1
  199. package/dist/integrations/scope-store.js +1 -1
  200. package/dist/integrations/scope-store.js.map +1 -1
  201. package/dist/integrations/usage-budget-store.js +1 -1
  202. package/dist/integrations/usage-budget-store.js.map +1 -1
  203. package/dist/localization/default-messages.d.ts +41 -1
  204. package/dist/localization/default-messages.d.ts.map +1 -1
  205. package/dist/localization/default-messages.js +41 -1
  206. package/dist/localization/default-messages.js.map +1 -1
  207. package/dist/mcp-client/workspace-servers.js +4 -4
  208. package/dist/mcp-client/workspace-servers.js.map +1 -1
  209. package/dist/observability/routes.d.ts +5 -5
  210. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  211. package/dist/secrets/crypto.d.ts +7 -6
  212. package/dist/secrets/crypto.d.ts.map +1 -1
  213. package/dist/secrets/crypto.js +30 -11
  214. package/dist/secrets/crypto.js.map +1 -1
  215. package/dist/secrets/routes.d.ts +9 -9
  216. package/dist/server/action-change.d.ts +1 -0
  217. package/dist/server/action-change.d.ts.map +1 -1
  218. package/dist/server/action-change.js +4 -1
  219. package/dist/server/action-change.js.map +1 -1
  220. package/dist/server/action-routes.d.ts.map +1 -1
  221. package/dist/server/action-routes.js +5 -0
  222. package/dist/server/action-routes.js.map +1 -1
  223. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  224. package/dist/server/poll.d.ts.map +1 -1
  225. package/dist/server/poll.js +1 -0
  226. package/dist/server/poll.js.map +1 -1
  227. package/dist/server/realtime-voice.d.ts +7 -0
  228. package/dist/server/realtime-voice.d.ts.map +1 -1
  229. package/dist/server/realtime-voice.js +222 -12
  230. package/dist/server/realtime-voice.js.map +1 -1
  231. package/dist/styles/agent-native.css +2 -2
  232. package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
  233. package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
  234. package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
  235. package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
  236. package/dist/vite/client.d.ts.map +1 -1
  237. package/dist/vite/client.js +6 -1
  238. package/dist/vite/client.js.map +1 -1
  239. package/package.json +2 -2
  240. package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
  241. package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
  242. package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
  243. package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
@@ -9,7 +9,7 @@ import {
9
9
  } from "@agent-native/core/client";
10
10
  import { configureTracking } from "@agent-native/core/client";
11
11
  import { useQueryClient } from "@tanstack/react-query";
12
- import { useCallback, useEffect, useState } from "react";
12
+ import { useEffect, useState } from "react";
13
13
  import {
14
14
  Links,
15
15
  Meta,
@@ -25,6 +25,7 @@ import { ProviderCorpusJobNotifier } from "@/components/ProviderCorpusJobNotifie
25
25
  import { Toaster as Sonner } from "@/components/ui/sonner";
26
26
  import { Toaster } from "@/components/ui/toaster";
27
27
  import { AppToolkitProvider } from "@/components/ui/toolkit-provider";
28
+ import { notifyProviderCorpusJobSyncEvent } from "@/lib/provider-corpus-job-sync";
28
29
  import { TAB_ID } from "@/lib/tab-id";
29
30
 
30
31
  import { CommandPalette } from "./components/layout/CommandPalette";
@@ -92,20 +93,39 @@ function DbSyncBridge() {
92
93
  // refresh/filter semantics instead of joining the broad action fallback.
93
94
  // Screen-refresh is handled automatically inside AgentSidebar.
94
95
  const queryClient = useQueryClient();
95
- const shouldInvalidateForAction = useCallback(
96
- (query: { queryKey: readonly unknown[] }) => {
97
- return query.queryKey[0] !== "sql-chart";
98
- },
99
- [],
100
- );
101
96
  useDbSync({
102
97
  queryClient,
103
98
  ignoreSource: TAB_ID,
104
- actionInvalidatePredicate: shouldInvalidateForAction,
99
+ onEvent: notifyProviderCorpusJobSyncEvent,
100
+ actionInvalidatePredicate: shouldInvalidateAnalyticsQueryForAction,
101
+ // These boot-time maintenance calls update their own local state and do
102
+ // not imply that every mounted Analytics query needs to restart.
103
+ suppressActionInvalidationFor: [
104
+ "ensure-demo-dashboards",
105
+ "manage-agent-engine",
106
+ ],
105
107
  });
106
108
  return null;
107
109
  }
108
110
 
111
+ export function shouldInvalidateAnalyticsQueryForAction(query: {
112
+ queryKey: readonly unknown[];
113
+ }): boolean {
114
+ const [scope, name] = query.queryKey;
115
+ if (
116
+ scope === "sql-chart" ||
117
+ scope === "sql-dashboards-sidebar" ||
118
+ scope === "analyses-sidebar" ||
119
+ scope === "extensions"
120
+ ) {
121
+ return false;
122
+ }
123
+ // The notifier refreshes for corpus-job events and only polls while a job is
124
+ // actively running. Unrelated actions must not restart its idle query.
125
+ if (scope === "action" && name === "provider-corpus-jobs") return false;
126
+ return true;
127
+ }
128
+
109
129
  function DemoDashboardInstaller() {
110
130
  useEffect(() => {
111
131
  void callAction("ensure-demo-dashboards", {}).catch((err) => {
@@ -1,5 +1,6 @@
1
1
  ---
2
- "analytics": fixed
2
+ type: fixed
3
+ date: 2026-06-24
3
4
  ---
4
5
 
5
6
  First-party signup and pageview dashboard panels now use the selected time range and email filters consistently.
@@ -1,5 +1,6 @@
1
1
  ---
2
- "analytics": fixed
2
+ type: fixed
3
+ date: 2026-06-25
3
4
  ---
4
5
 
5
6
  Referrer dashboard panels now show external domains without SQL parameter placeholder errors.
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  type: fixed
3
+ date: 2026-07-06
3
4
  ---
4
5
 
5
6
  Mobile chat now has a tighter matching header, stable Safari viewport sizing, working navigation menu, and New chat action in the top bar.
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  type: improved
3
+ date: 2026-07-08
3
4
  ---
4
5
 
5
6
  Dashboards and saved analyses now keep restorable history, and the active dashboard or database table appears as context in chat.
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  type: improved
3
+ date: 2026-07-08
3
4
  ---
4
5
 
5
6
  Settings are cleaner and searchable, and alert rules now live in their own dedicated Alerts tab.
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  type: improved
3
+ date: 2026-07-08
3
4
  ---
4
5
 
5
6
  Sidebar sections stay quieter by default, while Ask, dashboards, and analyses can each be expanded or collapsed manually.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-11
4
+ ---
5
+
6
+ Analytics no longer repeatedly restarts background requests when the app initializes or syncs changes.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-11
4
+ ---
5
+
6
+ Analytics only asks for replay storage when you enable session replay, with Builder.io as the primary setup path.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-11
4
+ ---
5
+
6
+ Charts now show their loading skeleton while refreshing.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-11
4
+ ---
5
+
6
+ Realtime voice now shows a clear connection indicator, completes its greeting, and can navigate the Analytics app.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-11
4
+ ---
5
+
6
+ Replay event counts stay hidden until recordings finish loading
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-11
4
+ ---
5
+
6
+ Session replay storage key mismatches now show actionable setup guidance instead of a low-level encryption error.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-11
4
+ ---
5
+
6
+ Replay timelines group continuous scrolling, and Dev Tools counts and expanded details stay accurate and compact.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-11
4
+ ---
5
+
6
+ Session filters now stay compact in a settings menu at medium widths.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-11
4
+ ---
5
+
6
+ Session replay console errors now link to matching issues or a filtered Monitoring search.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-11
4
+ ---
5
+
6
+ Session replays load large recordings faster and surface failed network and scroll activity in the timeline.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-11
4
+ ---
5
+
6
+ Session replays preserve recorded styles and use one consistent viewport so previews match the original browser more closely.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-11
4
+ ---
5
+
6
+ Session replays stay centered instead of clipping wide recordings
@@ -21,7 +21,7 @@
21
21
  "@dnd-kit/utilities": "^3.2.2",
22
22
  "@fontsource-variable/inter": "^5.2.8",
23
23
  "@libsql/client": "^0.15.8",
24
- "@rrweb/replay": "2.0.1",
24
+ "@rrweb/replay": "2.1.0",
25
25
  "@sparticuz/chromium-min": "^149.0.0",
26
26
  "@tabler/icons-react": "catalog:",
27
27
  "drizzle-orm": "^0.45.2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "Agent Native Templates (First-party)",
3
- "description": "Template engagement and per-app activity recorded through analytics.agent-native.com/track. Source: first-party analytics_events. Includes LLM generation volume, latency, errors, token usage, cost, explicit user feedback, and optional inferred message sentiment from first-party tracking events.",
3
+ "description": "Template engagement and per-app activity recorded through analytics.agent-native.com/track. Source: first-party analytics_events. Includes explicit feedback sentiment by model and optional inferred message sentiment.",
4
4
  "filters": [
5
5
  {
6
6
  "id": "timeRange",
@@ -756,175 +756,6 @@
756
756
  "description": "Distinct users who drove at least one referred signup in the window (distinct referrer_user on signups in the window)."
757
757
  }
758
758
  },
759
- {
760
- "id": "llm-generations-30d",
761
- "title": "LLM Generations (30d)",
762
- "chartType": "metric",
763
- "source": "first-party",
764
- "width": 1,
765
- "sql": "SELECT COUNT(*) AS count FROM analytics_events WHERE event_name = '$ai_generation' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')",
766
- "config": {
767
- "yKey": "count",
768
- "yFormatter": "number",
769
- "description": "Agent LLM generation events emitted by core observability tracking over the last 30 days."
770
- }
771
- },
772
- {
773
- "id": "llm-cost-30d",
774
- "title": "LLM Cost (30d)",
775
- "chartType": "metric",
776
- "source": "first-party",
777
- "width": 1,
778
- "sql": "SELECT COALESCE(SUM(COALESCE(NULLIF(properties::jsonb ->> '$ai_total_cost_usd', '')::float, NULLIF(properties::jsonb ->> 'cost_usd', '')::float, COALESCE(NULLIF(properties::jsonb ->> 'cost_cents_x100', '')::float, 0) / 10000.0)), 0) AS cost_usd FROM analytics_events WHERE event_name = '$ai_generation' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')",
779
- "config": {
780
- "yKey": "cost_usd",
781
- "yFormatter": "currency",
782
- "description": "Estimated provider spend from tracked LLM generation events."
783
- }
784
- },
785
- {
786
- "id": "llm-avg-latency-30d",
787
- "title": "Avg LLM Latency (30d)",
788
- "chartType": "metric",
789
- "source": "first-party",
790
- "width": 1,
791
- "sql": "SELECT COALESCE(AVG(COALESCE(NULLIF(properties::jsonb ->> 'duration_ms', '')::float, NULLIF(properties::jsonb ->> '$ai_latency', '')::float * 1000.0)), 0) AS latency_ms FROM analytics_events WHERE event_name = '$ai_generation' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')",
792
- "config": {
793
- "yKey": "latency_ms",
794
- "yFormatter": "duration",
795
- "description": "Average agent loop latency for tracked LLM generations."
796
- }
797
- },
798
- {
799
- "id": "llm-error-rate-30d",
800
- "title": "LLM Error Rate (30d)",
801
- "chartType": "metric",
802
- "source": "first-party",
803
- "width": 1,
804
- "sql": "SELECT CASE WHEN COUNT(*) = 0 THEN 0 ELSE COUNT(*) FILTER (WHERE lower(COALESCE(properties::jsonb ->> '$ai_is_error', properties::jsonb ->> 'status', '')) IN ('true', 'error'))::float / COUNT(*) END AS rate FROM analytics_events WHERE event_name = '$ai_generation' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')",
805
- "config": {
806
- "yKey": "rate",
807
- "yFormatter": "percent",
808
- "description": "Share of tracked LLM generations that ended in an error status."
809
- }
810
- },
811
- {
812
- "id": "llm-cost-over-time",
813
- "title": "LLM Cost Over Time by App",
814
- "chartType": "area",
815
- "source": "first-party",
816
- "width": 2,
817
- "sql": "SELECT event_date AS date, COALESCE(NULLIF(app, ''), NULLIF(template, ''), 'unknown') AS app, COALESCE(SUM(COALESCE(NULLIF(properties::jsonb ->> '$ai_total_cost_usd', '')::float, NULLIF(properties::jsonb ->> 'cost_usd', '')::float, COALESCE(NULLIF(properties::jsonb ->> 'cost_cents_x100', '')::float, 0) / 10000.0)), 0) AS cost_usd FROM analytics_events WHERE event_name = '$ai_generation' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD') GROUP BY event_date, COALESCE(NULLIF(app, ''), NULLIF(template, ''), 'unknown') ORDER BY date, app",
818
- "config": {
819
- "xKey": "date",
820
- "yKey": "cost_usd",
821
- "yFormatter": "currency",
822
- "pivot": {
823
- "xKey": "date",
824
- "seriesKey": "app",
825
- "valueKey": "cost_usd"
826
- },
827
- "stacked": true,
828
- "description": "Daily estimated LLM cost stacked by emitting app/template."
829
- }
830
- },
831
- {
832
- "id": "llm-cost-by-model",
833
- "title": "LLM Cost by Model",
834
- "chartType": "bar",
835
- "source": "first-party",
836
- "width": 1,
837
- "sql": "SELECT COALESCE(NULLIF(properties::jsonb ->> '$ai_model', ''), NULLIF(properties::jsonb ->> 'model', ''), 'unknown') AS model, COALESCE(SUM(COALESCE(NULLIF(properties::jsonb ->> '$ai_total_cost_usd', '')::float, NULLIF(properties::jsonb ->> 'cost_usd', '')::float, COALESCE(NULLIF(properties::jsonb ->> 'cost_cents_x100', '')::float, 0) / 10000.0)), 0) AS cost_usd FROM analytics_events WHERE event_name = '$ai_generation' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD') GROUP BY COALESCE(NULLIF(properties::jsonb ->> '$ai_model', ''), NULLIF(properties::jsonb ->> 'model', ''), 'unknown') ORDER BY cost_usd DESC LIMIT 20",
838
- "config": {
839
- "xKey": "model",
840
- "yKey": "cost_usd",
841
- "yFormatter": "currency",
842
- "color": "var(--brand-purple)",
843
- "description": "Estimated LLM cost grouped by model over the last 30 days."
844
- }
845
- },
846
- {
847
- "id": "llm-tokens-over-time",
848
- "title": "LLM Tokens Over Time by Model",
849
- "chartType": "area",
850
- "source": "first-party",
851
- "width": 2,
852
- "sql": "SELECT event_date AS date, COALESCE(NULLIF(properties::jsonb ->> '$ai_model', ''), NULLIF(properties::jsonb ->> 'model', ''), 'unknown') AS model, SUM(COALESCE(NULLIF(properties::jsonb ->> '$ai_input_tokens', '')::float, NULLIF(properties::jsonb ->> 'input_tokens', '')::float, 0) + COALESCE(NULLIF(properties::jsonb ->> '$ai_output_tokens', '')::float, NULLIF(properties::jsonb ->> 'output_tokens', '')::float, 0)) AS tokens FROM analytics_events WHERE event_name = '$ai_generation' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD') GROUP BY event_date, COALESCE(NULLIF(properties::jsonb ->> '$ai_model', ''), NULLIF(properties::jsonb ->> 'model', ''), 'unknown') ORDER BY date, model",
853
- "config": {
854
- "xKey": "date",
855
- "yKey": "tokens",
856
- "yFormatter": "number",
857
- "pivot": {
858
- "xKey": "date",
859
- "seriesKey": "model",
860
- "valueKey": "tokens"
861
- },
862
- "stacked": true,
863
- "description": "Input plus output tokens per day, stacked by model."
864
- }
865
- },
866
- {
867
- "id": "llm-latency-by-model",
868
- "title": "LLM Latency by Model",
869
- "chartType": "bar",
870
- "source": "first-party",
871
- "width": 1,
872
- "sql": "SELECT COALESCE(NULLIF(properties::jsonb ->> '$ai_model', ''), NULLIF(properties::jsonb ->> 'model', ''), 'unknown') AS model, COALESCE(AVG(COALESCE(NULLIF(properties::jsonb ->> 'duration_ms', '')::float, NULLIF(properties::jsonb ->> '$ai_latency', '')::float * 1000.0)), 0) AS latency_ms FROM analytics_events WHERE event_name = '$ai_generation' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD') GROUP BY COALESCE(NULLIF(properties::jsonb ->> '$ai_model', ''), NULLIF(properties::jsonb ->> 'model', ''), 'unknown') ORDER BY latency_ms DESC LIMIT 20",
873
- "config": {
874
- "xKey": "model",
875
- "yKey": "latency_ms",
876
- "yFormatter": "duration",
877
- "color": "var(--brand-teal)",
878
- "description": "Average tracked LLM generation latency grouped by model."
879
- }
880
- },
881
- {
882
- "id": "llm-feedback-responses-30d",
883
- "title": "LLM Feedback Responses (30d)",
884
- "chartType": "metric",
885
- "source": "first-party",
886
- "width": 1,
887
- "sql": "SELECT COUNT(*) AS responses FROM analytics_events WHERE event_name = '$ai_feedback' AND lower(COALESCE(properties::jsonb ->> 'sentiment', '')) IN ('positive', 'negative') AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')",
888
- "config": {
889
- "yKey": "responses",
890
- "yFormatter": "number",
891
- "description": "Explicit thumbs-up and thumbs-down feedback submitted for agent responses over the last 30 days."
892
- }
893
- },
894
- {
895
- "id": "llm-positive-feedback-rate-30d",
896
- "title": "Positive LLM Feedback (30d)",
897
- "chartType": "metric",
898
- "source": "first-party",
899
- "width": 1,
900
- "sql": "SELECT CASE WHEN COUNT(*) = 0 THEN 0 ELSE COUNT(*) FILTER (WHERE lower(properties::jsonb ->> 'sentiment') = 'positive')::float / COUNT(*) END AS rate FROM analytics_events WHERE event_name = '$ai_feedback' AND lower(COALESCE(properties::jsonb ->> 'sentiment', '')) IN ('positive', 'negative') AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')",
901
- "config": {
902
- "yKey": "rate",
903
- "yFormatter": "percent",
904
- "description": "Share of explicit feedback that was thumbs-up over the last 30 days. This is user-provided feedback, not inferred sentiment."
905
- }
906
- },
907
- {
908
- "id": "llm-feedback-over-time",
909
- "title": "LLM Feedback Sentiment Over Time",
910
- "chartType": "area",
911
- "source": "first-party",
912
- "width": 2,
913
- "sql": "SELECT event_date AS date, lower(properties::jsonb ->> 'sentiment') AS sentiment, COUNT(*) AS responses FROM analytics_events WHERE event_name = '$ai_feedback' AND lower(COALESCE(properties::jsonb ->> 'sentiment', '')) IN ('positive', 'negative') AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD') GROUP BY event_date, lower(properties::jsonb ->> 'sentiment') ORDER BY date, CASE WHEN lower(properties::jsonb ->> 'sentiment') = 'positive' THEN 0 ELSE 1 END",
914
- "config": {
915
- "xKey": "date",
916
- "yKey": "responses",
917
- "yFormatter": "number",
918
- "pivot": {
919
- "xKey": "date",
920
- "seriesKey": "sentiment",
921
- "valueKey": "responses"
922
- },
923
- "stacked": true,
924
- "colors": ["#10b981", "#ef4444"],
925
- "description": "Daily explicit thumbs-up and thumbs-down responses over the last 30 days."
926
- }
927
- },
928
759
  {
929
760
  "id": "llm-feedback-by-model",
930
761
  "title": "LLM Feedback Sentiment by Model",
@@ -960,139 +791,6 @@
960
791
  "colors": ["#10b981", "#94a3b8", "#ef4444"],
961
792
  "description": "Optional LLM-inferred positive, neutral, and negative message classifications over the last 30 days. This is separate from explicit thumbs feedback."
962
793
  }
963
- },
964
- {
965
- "id": "llm-inferred-sentiment-over-time",
966
- "title": "Inferred Message Sentiment Over Time",
967
- "chartType": "area",
968
- "source": "first-party",
969
- "width": 2,
970
- "sql": "SELECT event_date AS date, lower(properties::jsonb ->> 'sentiment') AS sentiment, COUNT(*) AS messages FROM analytics_events WHERE event_name = '$ai_sentiment' AND lower(COALESCE(properties::jsonb ->> 'method', '')) = 'llm' AND lower(COALESCE(properties::jsonb ->> 'sentiment', '')) IN ('positive', 'neutral', 'negative') AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD') GROUP BY event_date, lower(properties::jsonb ->> 'sentiment') ORDER BY date, CASE lower(properties::jsonb ->> 'sentiment') WHEN 'positive' THEN 0 WHEN 'neutral' THEN 1 ELSE 2 END",
971
- "config": {
972
- "xKey": "date",
973
- "yKey": "messages",
974
- "yFormatter": "number",
975
- "pivot": {
976
- "xKey": "date",
977
- "seriesKey": "sentiment",
978
- "valueKey": "messages"
979
- },
980
- "stacked": true,
981
- "colors": ["#10b981", "#94a3b8", "#ef4444"],
982
- "description": "Daily optional LLM-inferred message sentiment over the last 30 days, kept separate from explicit thumbs feedback."
983
- }
984
- },
985
- {
986
- "id": "llm-inferred-sentiment-by-model",
987
- "title": "Inferred Message Sentiment by Main Model",
988
- "chartType": "bar",
989
- "source": "first-party",
990
- "width": 3,
991
- "sql": "WITH inferred AS (SELECT COALESCE(NULLIF(properties::jsonb ->> '$ai_model', ''), NULLIF(properties::jsonb ->> 'model', ''), 'unknown') AS model, lower(properties::jsonb ->> 'sentiment') AS sentiment FROM analytics_events WHERE event_name = '$ai_sentiment' AND lower(COALESCE(properties::jsonb ->> 'method', '')) = 'llm' AND lower(COALESCE(properties::jsonb ->> 'sentiment', '')) IN ('positive', 'neutral', 'negative') AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD')), top_models AS (SELECT model, COUNT(*) AS total_messages FROM inferred GROUP BY model ORDER BY total_messages DESC LIMIT 20) SELECT inferred.model, inferred.sentiment, COUNT(*) AS messages FROM inferred JOIN top_models ON top_models.model = inferred.model GROUP BY inferred.model, inferred.sentiment, top_models.total_messages ORDER BY top_models.total_messages DESC, inferred.model, CASE inferred.sentiment WHEN 'positive' THEN 0 WHEN 'neutral' THEN 1 ELSE 2 END",
992
- "config": {
993
- "xKey": "model",
994
- "yKey": "messages",
995
- "yFormatter": "number",
996
- "pivot": {
997
- "xKey": "model",
998
- "seriesKey": "sentiment",
999
- "valueKey": "messages"
1000
- },
1001
- "stacked": true,
1002
- "colors": ["#10b981", "#94a3b8", "#ef4444"],
1003
- "description": "Optional inferred message sentiment grouped by the main model whose response preceded the classified user message, not by the classifier model."
1004
- }
1005
- },
1006
- {
1007
- "id": "top-expensive-llm-runs",
1008
- "title": "Top Expensive LLM Runs",
1009
- "chartType": "table",
1010
- "source": "first-party",
1011
- "width": 3,
1012
- "sql": "SELECT timestamp, COALESCE(NULLIF(app, ''), NULLIF(template, ''), 'unknown') AS app, COALESCE(NULLIF(properties::jsonb ->> '$ai_model', ''), NULLIF(properties::jsonb ->> 'model', ''), 'unknown') AS model, COALESCE(NULLIF(user_id, ''), NULLIF(anonymous_id, ''), 'anonymous') AS user_key, COALESCE(NULLIF(properties::jsonb ->> 'run_id', ''), NULLIF(properties::jsonb ->> '$ai_trace_id', '')) AS run_id, ROUND((COALESCE(NULLIF(properties::jsonb ->> '$ai_total_cost_usd', '')::float, NULLIF(properties::jsonb ->> 'cost_usd', '')::float, COALESCE(NULLIF(properties::jsonb ->> 'cost_cents_x100', '')::float, 0) / 10000.0))::numeric, 4) AS cost_usd, ROUND((COALESCE(NULLIF(properties::jsonb ->> 'duration_ms', '')::float, NULLIF(properties::jsonb ->> '$ai_latency', '')::float * 1000.0, 0))::numeric) AS duration_ms, COALESCE(NULLIF(properties::jsonb ->> 'tool_calls', '')::int, 0) AS tool_calls FROM analytics_events WHERE event_name = '$ai_generation' AND event_date >= to_char(CURRENT_DATE - INTERVAL '30 days', 'YYYY-MM-DD') ORDER BY cost_usd DESC LIMIT 25",
1013
- "config": {
1014
- "description": "Highest estimated-cost agent runs in the last 30 days.",
1015
- "columns": [
1016
- {
1017
- "key": "timestamp",
1018
- "label": "Time",
1019
- "format": "date"
1020
- },
1021
- {
1022
- "key": "app",
1023
- "label": "App"
1024
- },
1025
- {
1026
- "key": "model",
1027
- "label": "Model"
1028
- },
1029
- {
1030
- "key": "user_key",
1031
- "label": "User"
1032
- },
1033
- {
1034
- "key": "run_id",
1035
- "label": "Run ID"
1036
- },
1037
- {
1038
- "key": "cost_usd",
1039
- "label": "Cost",
1040
- "format": "currency"
1041
- },
1042
- {
1043
- "key": "duration_ms",
1044
- "label": "Duration",
1045
- "format": "duration"
1046
- },
1047
- {
1048
- "key": "tool_calls",
1049
- "label": "Tools",
1050
- "format": "number"
1051
- }
1052
- ],
1053
- "sortable": true,
1054
- "limit": 25
1055
- }
1056
- },
1057
- {
1058
- "id": "recent-llm-errors",
1059
- "title": "Recent LLM Errors",
1060
- "chartType": "table",
1061
- "source": "first-party",
1062
- "width": 3,
1063
- "sql": "SELECT timestamp, COALESCE(NULLIF(app, ''), NULLIF(template, ''), 'unknown') AS app, COALESCE(NULLIF(properties::jsonb ->> '$ai_model', ''), NULLIF(properties::jsonb ->> 'model', ''), 'unknown') AS model, COALESCE(NULLIF(user_id, ''), NULLIF(anonymous_id, ''), 'anonymous') AS user_key, COALESCE(NULLIF(properties::jsonb ->> 'run_id', ''), NULLIF(properties::jsonb ->> '$ai_trace_id', '')) AS run_id, COALESCE(NULLIF(properties::jsonb ->> 'error_message', ''), NULLIF(properties::jsonb ->> '$ai_error', ''), 'error') AS error FROM analytics_events WHERE event_name = '$ai_generation' AND lower(COALESCE(properties::jsonb ->> '$ai_is_error', properties::jsonb ->> 'status', '')) IN ('true', 'error') ORDER BY timestamp DESC LIMIT 25",
1064
- "config": {
1065
- "description": "Most recent tracked LLM generation errors.",
1066
- "columns": [
1067
- {
1068
- "key": "timestamp",
1069
- "label": "Time",
1070
- "format": "date"
1071
- },
1072
- {
1073
- "key": "app",
1074
- "label": "App"
1075
- },
1076
- {
1077
- "key": "model",
1078
- "label": "Model"
1079
- },
1080
- {
1081
- "key": "user_key",
1082
- "label": "User"
1083
- },
1084
- {
1085
- "key": "run_id",
1086
- "label": "Run ID"
1087
- },
1088
- {
1089
- "key": "error",
1090
- "label": "Error"
1091
- }
1092
- ],
1093
- "sortable": true,
1094
- "limit": 25
1095
- }
1096
794
  }
1097
795
  ]
1098
796
  }
@@ -23,7 +23,9 @@ import {
23
23
  listSessionRecordings,
24
24
  parseSessionReplayIngestPayload,
25
25
  recordSessionReplayChunks,
26
+ readSessionReplayChunkBatch,
26
27
  readSessionReplayChunkBytes,
28
+ readSessionReplayTokenizedChunkBatch,
27
29
  readSessionReplayTokenizedChunkBytes,
28
30
  type SessionReplayListFilters,
29
31
  } from "../lib/session-replay.js";
@@ -221,6 +223,16 @@ function readAgentReplayAccessToken(event: any): string | undefined {
221
223
  return asString(getQuery(event)[SESSION_REPLAY_AGENT_ACCESS_PARAM]);
222
224
  }
223
225
 
226
+ function replayChunkSeqsFromQuery(query: Record<string, unknown>): number[] {
227
+ const raw = asString(query.seqs);
228
+ if (!raw) return [];
229
+ return raw.split(",").map((value) => {
230
+ const normalized = value.trim();
231
+ if (!/^\d+$/.test(normalized)) return Number.NaN;
232
+ return Number(normalized);
233
+ });
234
+ }
235
+
224
236
  function verifyAgentReplayAccessToken(
225
237
  event: any,
226
238
  recordingId: string,
@@ -449,3 +461,48 @@ export const handleSessionReplayChunkBytes = defineEventHandler(
449
461
  });
450
462
  },
451
463
  );
464
+
465
+ export const handleSessionReplayChunkBatch = defineEventHandler(
466
+ async (event) => {
467
+ const recordingId = getRouterParam(event, "recordingId");
468
+ if (!recordingId) {
469
+ setResponseStatus(event, 400);
470
+ return { error: "Missing recordingId" };
471
+ }
472
+ const seqs = replayChunkSeqsFromQuery(getQuery(event));
473
+
474
+ const agentAccessToken = verifyAgentReplayAccessToken(event, recordingId);
475
+ if (agentAccessToken === "") {
476
+ return { error: "Invalid or expired agent access" };
477
+ }
478
+ if (agentAccessToken) {
479
+ try {
480
+ const result = await readSessionReplayTokenizedChunkBatch(
481
+ recordingId,
482
+ seqs,
483
+ );
484
+ applyAgentReplayReadHeaders(event);
485
+ setResponseHeader(event, "Content-Type", "application/json");
486
+ return result;
487
+ } catch (error: any) {
488
+ setResponseStatus(event, statusFromError(error));
489
+ return { error: messageFromError(error) };
490
+ }
491
+ }
492
+
493
+ return runApiHandlerWithContext(event, async (ctx) => {
494
+ try {
495
+ const result = await readSessionReplayChunkBatch(recordingId, seqs, {
496
+ userEmail: ctx.userEmail,
497
+ orgId: ctx.orgId ?? null,
498
+ });
499
+ setResponseHeader(event, "Content-Type", "application/json");
500
+ setResponseHeader(event, "Cache-Control", "no-store");
501
+ return result;
502
+ } catch (error: any) {
503
+ setResponseStatus(event, statusFromError(error));
504
+ return { error: messageFromError(error) };
505
+ }
506
+ });
507
+ },
508
+ );
@@ -938,7 +938,7 @@ export const dashboardCatalogEntries: DashboardCatalogEntry[] = [
938
938
  "referrals",
939
939
  "virality",
940
940
  ],
941
- panelCount: 51,
941
+ panelCount: 36,
942
942
  version: CATALOG_VERSION,
943
943
  recommended: true,
944
944
  visibleInCatalog: false,