@agent-native/core 0.98.0 → 0.98.2

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 (345) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +32 -0
  3. package/corpus/core/docs/content/locales/ar-SA/pr-visual-recap.mdx +25 -1
  4. package/corpus/core/docs/content/locales/ar-SA/template-plan.mdx +1 -1
  5. package/corpus/core/docs/content/locales/de-DE/pr-visual-recap.mdx +25 -1
  6. package/corpus/core/docs/content/locales/de-DE/template-plan.mdx +1 -1
  7. package/corpus/core/docs/content/locales/es-ES/pr-visual-recap.mdx +25 -1
  8. package/corpus/core/docs/content/locales/es-ES/template-plan.mdx +1 -1
  9. package/corpus/core/docs/content/locales/fr-FR/pr-visual-recap.mdx +25 -1
  10. package/corpus/core/docs/content/locales/fr-FR/template-plan.mdx +1 -1
  11. package/corpus/core/docs/content/locales/hi-IN/pr-visual-recap.mdx +25 -1
  12. package/corpus/core/docs/content/locales/hi-IN/template-plan.mdx +1 -1
  13. package/corpus/core/docs/content/locales/ja-JP/pr-visual-recap.mdx +25 -1
  14. package/corpus/core/docs/content/locales/ja-JP/template-plan.mdx +1 -1
  15. package/corpus/core/docs/content/locales/ko-KR/pr-visual-recap.mdx +25 -1
  16. package/corpus/core/docs/content/locales/ko-KR/template-plan.mdx +1 -1
  17. package/corpus/core/docs/content/locales/pt-BR/pr-visual-recap.mdx +25 -1
  18. package/corpus/core/docs/content/locales/pt-BR/template-plan.mdx +1 -1
  19. package/corpus/core/docs/content/locales/zh-CN/pr-visual-recap.mdx +25 -1
  20. package/corpus/core/docs/content/locales/zh-CN/template-plan.mdx +1 -1
  21. package/corpus/core/docs/content/locales/zh-TW/pr-visual-recap.mdx +25 -1
  22. package/corpus/core/docs/content/locales/zh-TW/template-plan.mdx +1 -1
  23. package/corpus/core/docs/content/pr-visual-recap.mdx +50 -1
  24. package/corpus/core/docs/content/template-plan.mdx +1 -1
  25. package/corpus/core/package.json +2 -2
  26. package/corpus/core/src/action-change-marker.ts +8 -1
  27. package/corpus/core/src/changelog/parse.ts +8 -3
  28. package/corpus/core/src/cli/plan-local.ts +43 -11
  29. package/corpus/core/src/cli/pr-visual-recap-workflow.ts +2 -9
  30. package/corpus/core/src/cli/recap.ts +356 -4
  31. package/corpus/core/src/cli/skills-content/local-files.ts +17 -12
  32. package/corpus/core/src/client/AgentPanel.tsx +2 -3
  33. package/corpus/core/src/client/AssistantChat.tsx +83 -39
  34. package/corpus/core/src/client/analytics.ts +125 -14
  35. package/corpus/core/src/client/blocks/library/wireframe-kit.tsx +16 -2
  36. package/corpus/core/src/client/chat-model-groups.ts +28 -0
  37. package/corpus/core/src/client/composer/RealtimeVoiceMode.tsx +442 -85
  38. package/corpus/core/src/client/composer/TiptapComposer.tsx +46 -29
  39. package/corpus/core/src/client/composer/VoiceButton.tsx +2 -1
  40. package/corpus/core/src/client/composer/index.ts +5 -0
  41. package/corpus/core/src/client/composer/useRealtimeVoiceMode.tsx +1206 -67
  42. package/corpus/core/src/client/index.ts +10 -0
  43. package/corpus/core/src/client/progress/RunsTray.tsx +13 -1
  44. package/corpus/core/src/client/session-replay.ts +10 -0
  45. package/corpus/core/src/client/settings/DemoModeSection.tsx +4 -28
  46. package/corpus/core/src/client/settings/SecretsSection.tsx +387 -266
  47. package/corpus/core/src/client/settings/SettingsPanel.tsx +117 -38
  48. package/corpus/core/src/client/url-scrub.ts +13 -0
  49. package/corpus/core/src/client/use-action.ts +12 -0
  50. package/corpus/core/src/client/use-agent-chat-home-handoff.ts +8 -1
  51. package/corpus/core/src/client/use-agent-engine-configured.ts +17 -3
  52. package/corpus/core/src/client/use-db-sync.ts +84 -34
  53. package/corpus/core/src/client/use-demo-mode-status.ts +44 -0
  54. package/corpus/core/src/client/use-pausing-interval.ts +14 -4
  55. package/corpus/core/src/integrations/scope-store.ts +1 -1
  56. package/corpus/core/src/integrations/usage-budget-store.ts +1 -1
  57. package/corpus/core/src/localization/default-messages.ts +44 -1
  58. package/corpus/core/src/mcp-client/workspace-servers.ts +4 -4
  59. package/corpus/core/src/secrets/crypto.ts +28 -8
  60. package/corpus/core/src/server/action-change.ts +5 -1
  61. package/corpus/core/src/server/action-routes.ts +8 -0
  62. package/corpus/core/src/server/poll.ts +1 -0
  63. package/corpus/core/src/server/realtime-voice.ts +299 -13
  64. package/corpus/core/src/styles/agent-native.css +2 -2
  65. package/corpus/core/src/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
  66. package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
  67. package/corpus/core/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
  68. package/corpus/core/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
  69. package/corpus/core/src/vite/client.ts +9 -1
  70. package/corpus/templates/analytics/.agents/skills/real-time-sync/SKILL.md +2 -2
  71. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +10 -5
  72. package/corpus/templates/analytics/AGENTS.md +17 -13
  73. package/corpus/templates/analytics/actions/navigate.ts +59 -5
  74. package/corpus/templates/analytics/actions/view-screen.ts +3 -6
  75. package/corpus/templates/analytics/app/components/ProviderCorpusJobNotifier.tsx +20 -3
  76. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +20 -5
  77. package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -1
  78. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +21 -6
  79. package/corpus/templates/analytics/app/global.css +19 -20
  80. package/corpus/templates/analytics/app/lib/demo-chart-trend.ts +156 -0
  81. package/corpus/templates/analytics/app/lib/provider-corpus-job-sync.ts +26 -0
  82. package/corpus/templates/analytics/app/pages/Settings.tsx +35 -341
  83. package/corpus/templates/analytics/app/pages/monitoring/ErrorsPanel.tsx +38 -7
  84. package/corpus/templates/analytics/app/pages/monitoring/MonitoringPage.tsx +1 -1
  85. package/corpus/templates/analytics/app/pages/monitoring/errors/i18n.ts +3 -0
  86. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +333 -272
  87. package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +72 -17
  88. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +297 -108
  89. package/corpus/templates/analytics/app/root.tsx +28 -8
  90. package/corpus/templates/analytics/changelog/2026-06-24-first-party-signup-and-pageview-panels-now-honor-dashboard-filters.md +2 -1
  91. package/corpus/templates/analytics/changelog/2026-06-25-referrer-dashboard-panels-show-external-domains.md +2 -1
  92. package/corpus/templates/analytics/changelog/2026-07-06-mobile-chat-header-is-more-compact-and-the-menu-button-opens-navigation.md +1 -0
  93. package/corpus/templates/analytics/changelog/2026-07-08-dashboard-history-and-chat-context.md +1 -0
  94. package/corpus/templates/analytics/changelog/2026-07-08-settings-are-cleaner-searchable-and-alerts-have-their-own-tab.md +1 -0
  95. package/corpus/templates/analytics/changelog/2026-07-08-sidebar-sections-stay-collapsed-until-opened.md +1 -0
  96. package/corpus/templates/analytics/changelog/2026-07-11-analytics-no-longer-repeatedly-restarts-background-requests-.md +6 -0
  97. package/corpus/templates/analytics/changelog/2026-07-11-analytics-only-asks-for-replay-storage-when-you-enable-sessi.md +6 -0
  98. package/corpus/templates/analytics/changelog/2026-07-11-charts-now-show-their-loading-skeleton-while-refreshing.md +6 -0
  99. package/corpus/templates/analytics/changelog/2026-07-11-realtime-voice-now-shows-a-clear-connection-indicator-comple.md +6 -0
  100. package/corpus/templates/analytics/changelog/2026-07-11-replay-event-counts-stay-hidden-until-recordings-finish-load.md +6 -0
  101. package/corpus/templates/analytics/changelog/2026-07-11-replay-storage-key-mismatches-show-actionable-setup-guidance.md +6 -0
  102. package/corpus/templates/analytics/changelog/2026-07-11-replay-timelines-group-continuous-scrolling-and-dev-tools-co.md +6 -0
  103. package/corpus/templates/analytics/changelog/2026-07-11-session-filters-now-stay-compact-in-a-settings-menu-at-mediu.md +6 -0
  104. package/corpus/templates/analytics/changelog/2026-07-11-session-replay-console-errors-now-link-to-matching-issues-or.md +6 -0
  105. package/corpus/templates/analytics/changelog/2026-07-11-session-replays-load-large-recordings-faster-and-surface-fai.md +6 -0
  106. package/corpus/templates/analytics/changelog/2026-07-11-session-replays-preserve-recorded-styles-and-viewport-layout.md +6 -0
  107. package/corpus/templates/analytics/changelog/2026-07-11-session-replays-stay-centered-instead-of-clipping-wide-recor.md +6 -0
  108. package/corpus/templates/analytics/changelog/2026-07-12-daily-dashboard-emails-reliably-include-the-complete-dashboa.md +6 -0
  109. package/corpus/templates/analytics/changelog/2026-07-12-demo-mode-charts-trend-upward.md +6 -0
  110. package/corpus/templates/analytics/package.json +1 -1
  111. package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +1 -303
  112. package/corpus/templates/analytics/server/handlers/session-replay.ts +57 -0
  113. package/corpus/templates/analytics/server/jobs/dashboard-report.ts +2 -0
  114. package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +1 -1
  115. package/corpus/templates/analytics/server/lib/dashboard-report.ts +17 -33
  116. package/corpus/templates/analytics/server/lib/demo-dashboards.ts +16 -3
  117. package/corpus/templates/analytics/server/lib/session-replay-agent-context.ts +12 -0
  118. package/corpus/templates/analytics/server/lib/session-replay.ts +208 -2
  119. package/corpus/templates/analytics/server/plugins/onboarding.ts +7 -79
  120. package/corpus/templates/analytics/server/routes/api/session-replay/recordings/[recordingId]/chunks.get.ts +1 -0
  121. package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +2 -2
  122. package/corpus/templates/assets/app/global.css +4 -4
  123. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +0 -1
  124. package/corpus/templates/assets/app/routes/library.tsx +0 -1
  125. package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +2 -2
  126. package/corpus/templates/brain/app/global.css +5 -5
  127. package/corpus/templates/brain/app/root.tsx +0 -3
  128. package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +2 -2
  129. package/corpus/templates/calendar/app/root.tsx +1 -0
  130. package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +2 -2
  131. package/corpus/templates/clips/app/components/player/video-player.tsx +5 -1
  132. package/corpus/templates/clips/app/hooks/use-auto-title.ts +74 -8
  133. package/corpus/templates/clips/app/hooks/use-library.ts +12 -25
  134. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +20 -22
  135. package/corpus/templates/clips/changelog/2026-07-11-clips-no-longer-stay-stuck-on-preparing-clip-when-they-are-r.md +6 -0
  136. package/corpus/templates/clips/changelog/2026-07-11-generated-titles-no-longer-cause-continuous-background-refreshes.md +6 -0
  137. package/corpus/templates/clips/changelog/2026-07-11-s3-storage-setup-stays-collapsed-until-you-choose-it-as-the-.md +6 -0
  138. package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +2 -2
  139. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +2 -2
  140. package/corpus/templates/content/app/hooks/use-comments.ts +0 -1
  141. package/corpus/templates/content/changelog/2026-07-11-comments-now-update-from-live-collaboration-events-without-c.md +6 -0
  142. package/corpus/templates/content/changelog/2026-07-12-the-left-sidebar-stays-fixed-horizontally-while-scrolling-on.md +6 -0
  143. package/corpus/templates/design/app/components/layout/Header.tsx +1 -1
  144. package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +2 -2
  145. package/corpus/templates/macros/app/components/layout/AppLayout.tsx +1 -2
  146. package/corpus/templates/macros/app/root.tsx +0 -3
  147. package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +2 -2
  148. package/corpus/templates/mail/app/hooks/use-compose-state.ts +1 -0
  149. package/corpus/templates/mail/app/root.tsx +1 -12
  150. package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +2 -2
  151. package/corpus/templates/plan/.agents/skills/visual-plan/references/local-files.md +17 -12
  152. package/corpus/templates/plan/.agents/skills/visual-recap/references/local-files.md +17 -12
  153. package/corpus/templates/plan/actions/validate-local-plan-source.ts +4 -4
  154. package/corpus/templates/plan/app/components/layout/Sidebar.tsx +2 -1
  155. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +5 -2
  156. package/corpus/templates/plan/app/hooks/use-plans.ts +6 -13
  157. package/corpus/templates/plan/app/lib/plan-local-bridge.ts +41 -0
  158. package/corpus/templates/plan/app/lib/plan-tracking.ts +3 -0
  159. package/corpus/templates/plan/app/pages/PlansPage.tsx +14 -36
  160. package/corpus/templates/plan/app/root.tsx +13 -3
  161. package/corpus/templates/plan/changelog/2026-07-11-plan-refreshes-only-when-content-changes.md +6 -0
  162. package/corpus/templates/plan/changelog/2026-07-12-local-plan-source-now-stays-on-device.md +6 -0
  163. package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +4 -1
  164. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +1 -1
  165. package/corpus/templates/slides/app/components/layout/Header.tsx +1 -1
  166. package/corpus/templates/slides/app/context/DeckContext.tsx +1 -0
  167. package/corpus/templates/slides/app/hooks/use-slide-comments.ts +7 -31
  168. package/corpus/templates/slides/changelog/2026-07-11-comments-now-update-from-live-collaboration-events-without-c.md +6 -0
  169. package/dist/action-change-marker.d.ts +1 -0
  170. package/dist/action-change-marker.d.ts.map +1 -1
  171. package/dist/action-change-marker.js +7 -1
  172. package/dist/action-change-marker.js.map +1 -1
  173. package/dist/changelog/parse.d.ts +4 -2
  174. package/dist/changelog/parse.d.ts.map +1 -1
  175. package/dist/changelog/parse.js +5 -3
  176. package/dist/changelog/parse.js.map +1 -1
  177. package/dist/cli/plan-local.d.ts +6 -6
  178. package/dist/cli/plan-local.d.ts.map +1 -1
  179. package/dist/cli/plan-local.js +36 -7
  180. package/dist/cli/plan-local.js.map +1 -1
  181. package/dist/cli/pr-visual-recap-workflow.d.ts +2 -8
  182. package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
  183. package/dist/cli/pr-visual-recap-workflow.js +2 -8
  184. package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
  185. package/dist/cli/recap.d.ts +22 -1
  186. package/dist/cli/recap.d.ts.map +1 -1
  187. package/dist/cli/recap.js +286 -2
  188. package/dist/cli/recap.js.map +1 -1
  189. package/dist/cli/skills-content/local-files.d.ts +1 -1
  190. package/dist/cli/skills-content/local-files.d.ts.map +1 -1
  191. package/dist/cli/skills-content/local-files.js +17 -12
  192. package/dist/cli/skills-content/local-files.js.map +1 -1
  193. package/dist/client/AgentPanel.d.ts.map +1 -1
  194. package/dist/client/AgentPanel.js +2 -3
  195. package/dist/client/AgentPanel.js.map +1 -1
  196. package/dist/client/AssistantChat.d.ts +7 -0
  197. package/dist/client/AssistantChat.d.ts.map +1 -1
  198. package/dist/client/AssistantChat.js +62 -40
  199. package/dist/client/AssistantChat.js.map +1 -1
  200. package/dist/client/analytics.d.ts +8 -0
  201. package/dist/client/analytics.d.ts.map +1 -1
  202. package/dist/client/analytics.js +103 -13
  203. package/dist/client/analytics.js.map +1 -1
  204. package/dist/client/blocks/library/wireframe-kit.d.ts +2 -0
  205. package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -1
  206. package/dist/client/blocks/library/wireframe-kit.js +9 -2
  207. package/dist/client/blocks/library/wireframe-kit.js.map +1 -1
  208. package/dist/client/chat-model-groups.d.ts.map +1 -1
  209. package/dist/client/chat-model-groups.js +24 -0
  210. package/dist/client/chat-model-groups.js.map +1 -1
  211. package/dist/client/composer/RealtimeVoiceMode.d.ts +40 -4
  212. package/dist/client/composer/RealtimeVoiceMode.d.ts.map +1 -1
  213. package/dist/client/composer/RealtimeVoiceMode.js +172 -42
  214. package/dist/client/composer/RealtimeVoiceMode.js.map +1 -1
  215. package/dist/client/composer/TiptapComposer.d.ts +4 -0
  216. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  217. package/dist/client/composer/TiptapComposer.js +47 -29
  218. package/dist/client/composer/TiptapComposer.js.map +1 -1
  219. package/dist/client/composer/VoiceButton.d.ts.map +1 -1
  220. package/dist/client/composer/VoiceButton.js +2 -2
  221. package/dist/client/composer/VoiceButton.js.map +1 -1
  222. package/dist/client/composer/index.d.ts +2 -2
  223. package/dist/client/composer/index.d.ts.map +1 -1
  224. package/dist/client/composer/index.js +1 -1
  225. package/dist/client/composer/index.js.map +1 -1
  226. package/dist/client/composer/useRealtimeVoiceMode.d.ts +100 -3
  227. package/dist/client/composer/useRealtimeVoiceMode.d.ts.map +1 -1
  228. package/dist/client/composer/useRealtimeVoiceMode.js +952 -51
  229. package/dist/client/composer/useRealtimeVoiceMode.js.map +1 -1
  230. package/dist/client/index.d.ts +3 -2
  231. package/dist/client/index.d.ts.map +1 -1
  232. package/dist/client/index.js +3 -2
  233. package/dist/client/index.js.map +1 -1
  234. package/dist/client/progress/RunsTray.d.ts.map +1 -1
  235. package/dist/client/progress/RunsTray.js +10 -1
  236. package/dist/client/progress/RunsTray.js.map +1 -1
  237. package/dist/client/session-replay.d.ts +2 -0
  238. package/dist/client/session-replay.d.ts.map +1 -1
  239. package/dist/client/session-replay.js +7 -0
  240. package/dist/client/session-replay.js.map +1 -1
  241. package/dist/client/settings/DemoModeSection.d.ts.map +1 -1
  242. package/dist/client/settings/DemoModeSection.js +4 -23
  243. package/dist/client/settings/DemoModeSection.js.map +1 -1
  244. package/dist/client/settings/SecretsSection.d.ts +2 -3
  245. package/dist/client/settings/SecretsSection.d.ts.map +1 -1
  246. package/dist/client/settings/SecretsSection.js +46 -22
  247. package/dist/client/settings/SecretsSection.js.map +1 -1
  248. package/dist/client/settings/SettingsPanel.d.ts +9 -0
  249. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  250. package/dist/client/settings/SettingsPanel.js +46 -12
  251. package/dist/client/settings/SettingsPanel.js.map +1 -1
  252. package/dist/client/url-scrub.d.ts.map +1 -1
  253. package/dist/client/url-scrub.js +14 -0
  254. package/dist/client/url-scrub.js.map +1 -1
  255. package/dist/client/use-action.d.ts +2 -0
  256. package/dist/client/use-action.d.ts.map +1 -1
  257. package/dist/client/use-action.js +10 -0
  258. package/dist/client/use-action.js.map +1 -1
  259. package/dist/client/use-agent-chat-home-handoff.d.ts +2 -0
  260. package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
  261. package/dist/client/use-agent-chat-home-handoff.js +7 -1
  262. package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
  263. package/dist/client/use-agent-engine-configured.d.ts.map +1 -1
  264. package/dist/client/use-agent-engine-configured.js +17 -3
  265. package/dist/client/use-agent-engine-configured.js.map +1 -1
  266. package/dist/client/use-db-sync.d.ts +8 -2
  267. package/dist/client/use-db-sync.d.ts.map +1 -1
  268. package/dist/client/use-db-sync.js +57 -32
  269. package/dist/client/use-db-sync.js.map +1 -1
  270. package/dist/client/use-demo-mode-status.d.ts +12 -0
  271. package/dist/client/use-demo-mode-status.d.ts.map +1 -0
  272. package/dist/client/use-demo-mode-status.js +30 -0
  273. package/dist/client/use-demo-mode-status.js.map +1 -0
  274. package/dist/client/use-pausing-interval.d.ts.map +1 -1
  275. package/dist/client/use-pausing-interval.js +16 -4
  276. package/dist/client/use-pausing-interval.js.map +1 -1
  277. package/dist/collab/struct-routes.d.ts +1 -1
  278. package/dist/integrations/scope-store.js +1 -1
  279. package/dist/integrations/scope-store.js.map +1 -1
  280. package/dist/integrations/usage-budget-store.js +1 -1
  281. package/dist/integrations/usage-budget-store.js.map +1 -1
  282. package/dist/localization/default-messages.d.ts +41 -1
  283. package/dist/localization/default-messages.d.ts.map +1 -1
  284. package/dist/localization/default-messages.js +41 -1
  285. package/dist/localization/default-messages.js.map +1 -1
  286. package/dist/mcp-client/workspace-servers.js +4 -4
  287. package/dist/mcp-client/workspace-servers.js.map +1 -1
  288. package/dist/notifications/routes.d.ts +3 -3
  289. package/dist/progress/routes.d.ts +1 -1
  290. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  291. package/dist/resources/handlers.d.ts +3 -3
  292. package/dist/secrets/crypto.d.ts +7 -6
  293. package/dist/secrets/crypto.d.ts.map +1 -1
  294. package/dist/secrets/crypto.js +30 -11
  295. package/dist/secrets/crypto.js.map +1 -1
  296. package/dist/server/action-change.d.ts +1 -0
  297. package/dist/server/action-change.d.ts.map +1 -1
  298. package/dist/server/action-change.js +4 -1
  299. package/dist/server/action-change.js.map +1 -1
  300. package/dist/server/action-routes.d.ts.map +1 -1
  301. package/dist/server/action-routes.js +5 -0
  302. package/dist/server/action-routes.js.map +1 -1
  303. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  304. package/dist/server/poll.d.ts.map +1 -1
  305. package/dist/server/poll.js +1 -0
  306. package/dist/server/poll.js.map +1 -1
  307. package/dist/server/realtime-voice.d.ts +7 -0
  308. package/dist/server/realtime-voice.d.ts.map +1 -1
  309. package/dist/server/realtime-voice.js +222 -12
  310. package/dist/server/realtime-voice.js.map +1 -1
  311. package/dist/styles/agent-native.css +2 -2
  312. package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
  313. package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
  314. package/dist/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
  315. package/dist/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
  316. package/dist/vite/client.d.ts.map +1 -1
  317. package/dist/vite/client.js +6 -1
  318. package/dist/vite/client.js.map +1 -1
  319. package/docs/content/locales/ar-SA/pr-visual-recap.mdx +25 -1
  320. package/docs/content/locales/ar-SA/template-plan.mdx +1 -1
  321. package/docs/content/locales/de-DE/pr-visual-recap.mdx +25 -1
  322. package/docs/content/locales/de-DE/template-plan.mdx +1 -1
  323. package/docs/content/locales/es-ES/pr-visual-recap.mdx +25 -1
  324. package/docs/content/locales/es-ES/template-plan.mdx +1 -1
  325. package/docs/content/locales/fr-FR/pr-visual-recap.mdx +25 -1
  326. package/docs/content/locales/fr-FR/template-plan.mdx +1 -1
  327. package/docs/content/locales/hi-IN/pr-visual-recap.mdx +25 -1
  328. package/docs/content/locales/hi-IN/template-plan.mdx +1 -1
  329. package/docs/content/locales/ja-JP/pr-visual-recap.mdx +25 -1
  330. package/docs/content/locales/ja-JP/template-plan.mdx +1 -1
  331. package/docs/content/locales/ko-KR/pr-visual-recap.mdx +25 -1
  332. package/docs/content/locales/ko-KR/template-plan.mdx +1 -1
  333. package/docs/content/locales/pt-BR/pr-visual-recap.mdx +25 -1
  334. package/docs/content/locales/pt-BR/template-plan.mdx +1 -1
  335. package/docs/content/locales/zh-CN/pr-visual-recap.mdx +25 -1
  336. package/docs/content/locales/zh-CN/template-plan.mdx +1 -1
  337. package/docs/content/locales/zh-TW/pr-visual-recap.mdx +25 -1
  338. package/docs/content/locales/zh-TW/template-plan.mdx +1 -1
  339. package/docs/content/pr-visual-recap.mdx +50 -1
  340. package/docs/content/template-plan.mdx +1 -1
  341. package/package.json +2 -2
  342. package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +2 -2
  343. package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +2 -2
  344. package/src/templates/workspace-core/.agents/skills/secrets/SKILL.md +7 -3
  345. package/src/templates/workspace-core/.agents/skills/voice-transcription/SKILL.md +39 -9
@@ -45,10 +45,20 @@ import {
45
45
  export type SessionIssueMatch = { issueId: string; status: string };
46
46
 
47
47
  /** Deep-link from a session error to the Monitoring → Errors issue detail. */
48
- function issueDetailPath(issueId: string): string {
48
+ export function issueDetailPath(issueId: string): string {
49
49
  return `/monitoring?view=errors&issue=${encodeURIComponent(issueId)}`;
50
50
  }
51
51
 
52
+ /** Search Monitoring for all captured issues resembling an unmatched line. */
53
+ export function issueSearchPath(message: string): string {
54
+ const params = new URLSearchParams({
55
+ view: "errors",
56
+ status: "all",
57
+ q: message,
58
+ });
59
+ return `/monitoring?${params.toString()}`;
60
+ }
61
+
52
62
  /** Pause row auto-follow for a while after the user scrolls the list. */
53
63
  const MANUAL_SCROLL_FOLLOW_PAUSE_MS = 4000;
54
64
  const DEVTOOLS_ROW_HEIGHT = 34;
@@ -65,14 +75,13 @@ const DEVTOOLS_MAX_HEIGHT = 620;
65
75
  export function buildDevToolsRowOffsets(
66
76
  entryCount: number,
67
77
  expandedIndex: number,
78
+ expandedHeight = DEVTOOLS_EXPANDED_ESTIMATE,
68
79
  ): number[] {
69
80
  const offsets = new Array<number>(entryCount + 1);
70
81
  offsets[0] = 0;
71
82
  for (let index = 0; index < entryCount; index += 1) {
72
83
  const height =
73
- index === expandedIndex
74
- ? DEVTOOLS_EXPANDED_ESTIMATE
75
- : DEVTOOLS_ROW_HEIGHT;
84
+ index === expandedIndex ? expandedHeight : DEVTOOLS_ROW_HEIGHT;
76
85
  offsets[index + 1] = offsets[index] + height;
77
86
  }
78
87
  return offsets;
@@ -86,6 +95,7 @@ export function SessionDevToolsPanel({
86
95
  onHeightChange,
87
96
  onSeek,
88
97
  issueMatches,
98
+ issueMatching = false,
89
99
  }: {
90
100
  diagnostics: ReplayDevToolsDiagnostics;
91
101
  currentTime: number;
@@ -95,6 +105,8 @@ export function SessionDevToolsPanel({
95
105
  onSeek: (ms: number) => void;
96
106
  /** Resolved error issues by console entry id, for cross-linking to Errors. */
97
107
  issueMatches?: ReadonlyMap<string, SessionIssueMatch>;
108
+ /** Prevent an unmatched fallback from flashing while issue lookup is active. */
109
+ issueMatching?: boolean;
98
110
  }) {
99
111
  const t = useT();
100
112
  const [tab, setTab] = useState<"console" | "network">("console");
@@ -265,6 +277,7 @@ export function SessionDevToolsPanel({
265
277
  active={entry.id === activeConsoleId}
266
278
  selected={expanded}
267
279
  issueMatch={issueMatches?.get(entry.id) ?? null}
280
+ issueMatching={issueMatching}
268
281
  onSelect={() =>
269
282
  setSelectedConsoleId((current) =>
270
283
  current === entry.id ? null : entry.id,
@@ -414,6 +427,12 @@ function VirtualizedDevToolsList<T extends { id: string }>({
414
427
  const lastManualScrollAtRef = useRef(0);
415
428
  const [scrollTop, setScrollTop] = useState(0);
416
429
  const [viewportHeight, setViewportHeight] = useState(0);
430
+ const [expandedRowHeight, setExpandedRowHeight] = useState(
431
+ DEVTOOLS_EXPANDED_ESTIMATE,
432
+ );
433
+ const [expandedElement, setExpandedElement] = useState<HTMLDivElement | null>(
434
+ null,
435
+ );
417
436
 
418
437
  const activeIndex = activeEntryId
419
438
  ? entries.findIndex((entry) => entry.id === activeEntryId)
@@ -423,12 +442,31 @@ function VirtualizedDevToolsList<T extends { id: string }>({
423
442
  : -1;
424
443
 
425
444
  const rowOffsets = useMemo(
426
- () => buildDevToolsRowOffsets(entries.length, expandedIndex),
427
- [entries.length, expandedIndex],
445
+ () =>
446
+ buildDevToolsRowOffsets(entries.length, expandedIndex, expandedRowHeight),
447
+ [entries.length, expandedIndex, expandedRowHeight],
428
448
  );
429
449
 
430
450
  const totalHeight = rowOffsets[entries.length] ?? 0;
431
451
 
452
+ useEffect(() => {
453
+ setExpandedRowHeight(DEVTOOLS_EXPANDED_ESTIMATE);
454
+ }, [expandedEntryId]);
455
+
456
+ useEffect(() => {
457
+ if (!expandedElement) return;
458
+ const update = () => {
459
+ const measured = Math.ceil(
460
+ expandedElement.getBoundingClientRect().height,
461
+ );
462
+ if (measured > 0) setExpandedRowHeight(measured);
463
+ };
464
+ update();
465
+ const observer = new ResizeObserver(update);
466
+ observer.observe(expandedElement);
467
+ return () => observer.disconnect();
468
+ }, [expandedElement]);
469
+
432
470
  useEffect(() => {
433
471
  const el = containerRef.current;
434
472
  if (!el) return;
@@ -510,18 +548,11 @@ function VirtualizedDevToolsList<T extends { id: string }>({
510
548
  const index = startIndex + offset;
511
549
  const expanded = entry.id === expandedEntryId;
512
550
  const top = rowOffsets[index] ?? 0;
513
- const height =
514
- (rowOffsets[index + 1] ?? top + DEVTOOLS_ROW_HEIGHT) - top;
515
551
  return (
516
- <div
517
- key={entry.id}
518
- className="absolute inset-x-0"
519
- style={{
520
- minHeight: height,
521
- top,
522
- }}
523
- >
524
- {renderRow(entry, expanded)}
552
+ <div key={entry.id} className="absolute inset-x-0" style={{ top }}>
553
+ <div ref={expanded ? setExpandedElement : undefined}>
554
+ {renderRow(entry, expanded)}
555
+ </div>
525
556
  </div>
526
557
  );
527
558
  })}
@@ -647,11 +678,27 @@ function ViewIssueLink({
647
678
  );
648
679
  }
649
680
 
681
+ function SearchIssuesLink({ message }: { message: string }) {
682
+ const et = useErrorsT();
683
+ return (
684
+ <Link
685
+ to={issueSearchPath(message)}
686
+ title={et.searchIssuesTooltip}
687
+ onClick={(event) => event.stopPropagation()}
688
+ className="inline-flex shrink-0 items-center gap-1 rounded border px-1.5 py-0.5 text-[11px] font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
689
+ >
690
+ <IconSearch className="h-3 w-3" />
691
+ {et.searchIssues}
692
+ </Link>
693
+ );
694
+ }
695
+
650
696
  function ConsoleRow({
651
697
  entry,
652
698
  active,
653
699
  selected,
654
700
  issueMatch,
701
+ issueMatching,
655
702
  onSelect,
656
703
  onSeek,
657
704
  }: {
@@ -659,6 +706,7 @@ function ConsoleRow({
659
706
  active: boolean;
660
707
  selected: boolean;
661
708
  issueMatch: SessionIssueMatch | null;
709
+ issueMatching: boolean;
662
710
  onSelect: () => void;
663
711
  onSeek: (ms: number) => void;
664
712
  }) {
@@ -723,6 +771,9 @@ function ConsoleRow({
723
771
  />
724
772
  </button>
725
773
  {issueMatch ? <ViewIssueLink issueId={issueMatch.issueId} /> : null}
774
+ {!issueMatch && !issueMatching && bucket === "error" ? (
775
+ <SearchIssuesLink message={entry.message} />
776
+ ) : null}
726
777
  <JumpToButton offsetMs={entry.offsetMs} onSeek={onSeek} />
727
778
  </div>
728
779
  {selected ? (
@@ -757,6 +808,10 @@ function ConsoleRow({
757
808
  <div className="pt-1">
758
809
  <ViewIssueLink issueId={issueMatch.issueId} />
759
810
  </div>
811
+ ) : !issueMatching && bucket === "error" ? (
812
+ <div className="pt-1">
813
+ <SearchIssuesLink message={entry.message} />
814
+ </div>
760
815
  ) : null}
761
816
  </div>
762
817
  ) : null}
@@ -1,13 +1,14 @@
1
1
  import { CodeSurface } from "@agent-native/core/blocks";
2
2
  import {
3
+ agentNativePath,
3
4
  useActionQuery,
4
5
  useBuilderConnectFlow,
5
6
  useBuilderStatus,
6
7
  useT,
7
8
  } from "@agent-native/core/client";
8
9
  import {
9
- IconChevronDown,
10
10
  IconCheck,
11
+ IconChevronDown,
11
12
  IconCloud,
12
13
  IconCode,
13
14
  IconExternalLink,
@@ -17,14 +18,28 @@ import {
17
18
  IconRefresh,
18
19
  IconSearch,
19
20
  IconServer,
21
+ IconSettings,
20
22
  } from "@tabler/icons-react";
21
23
  import { useQuery } from "@tanstack/react-query";
22
24
  import { useMemo, useState } from "react";
23
25
  import { Link, useNavigate, useSearchParams } from "react-router";
26
+ import { toast } from "sonner";
24
27
 
28
+ import { Badge } from "@/components/ui/badge";
25
29
  import { Button } from "@/components/ui/button";
26
30
  import { Card, CardContent } from "@/components/ui/card";
31
+ import {
32
+ Collapsible,
33
+ CollapsibleContent,
34
+ CollapsibleTrigger,
35
+ } from "@/components/ui/collapsible";
27
36
  import { Input } from "@/components/ui/input";
37
+ import { Label } from "@/components/ui/label";
38
+ import {
39
+ Popover,
40
+ PopoverContent,
41
+ PopoverTrigger,
42
+ } from "@/components/ui/popover";
28
43
  import {
29
44
  Select,
30
45
  SelectContent,
@@ -33,6 +48,11 @@ import {
33
48
  SelectValue,
34
49
  } from "@/components/ui/select";
35
50
  import { Skeleton } from "@/components/ui/skeleton";
51
+ import {
52
+ Tooltip,
53
+ TooltipContent,
54
+ TooltipTrigger,
55
+ } from "@/components/ui/tooltip";
36
56
  import { useReplayStorageStatus } from "@/hooks/use-replay-storage-status";
37
57
  import { cn } from "@/lib/utils";
38
58
 
@@ -41,6 +61,73 @@ type ReplayRange = "24h" | "7d" | "30d" | "90d" | "all";
41
61
  const SESSION_REPLAY_DOCS_URL =
42
62
  "https://www.agent-native.com/docs/tracking#session-replay";
43
63
 
64
+ const S3_STORAGE_FIELDS = [
65
+ {
66
+ key: "S3_ENDPOINT",
67
+ labelKey: "settings.s3EndpointLabel",
68
+ placeholder: "https://s3.us-east-1.amazonaws.com",
69
+ required: true,
70
+ },
71
+ {
72
+ key: "S3_BUCKET",
73
+ labelKey: "settings.s3BucketLabel",
74
+ placeholder: "my-replays-bucket",
75
+ required: true,
76
+ },
77
+ {
78
+ key: "S3_ACCESS_KEY_ID",
79
+ labelKey: "settings.s3AccessKeyLabel",
80
+ placeholder: "AKIA...",
81
+ required: true,
82
+ },
83
+ {
84
+ key: "S3_SECRET_ACCESS_KEY",
85
+ labelKey: "settings.s3SecretAccessKeyLabel",
86
+ placeholder: "••••••••",
87
+ required: true,
88
+ secret: true,
89
+ },
90
+ {
91
+ key: "S3_REGION",
92
+ labelKey: "settings.s3RegionLabel",
93
+ placeholder: "us-east-1",
94
+ },
95
+ {
96
+ key: "S3_PUBLIC_BASE_URL",
97
+ labelKey: "settings.s3PublicBaseUrlLabel",
98
+ placeholder: "https://cdn.example.com",
99
+ },
100
+ ] as const;
101
+
102
+ async function saveS3StorageSettings(
103
+ values: Record<string, string>,
104
+ ): Promise<void> {
105
+ const vars = S3_STORAGE_FIELDS.map((field) => ({
106
+ key: field.key,
107
+ value: (values[field.key] ?? "").trim(),
108
+ })).filter((entry) => entry.value.length > 0);
109
+
110
+ for (const { key, value } of vars) {
111
+ const res = await fetch(agentNativePath("/_agent-native/secrets/adhoc"), {
112
+ method: "POST",
113
+ headers: { "Content-Type": "application/json" },
114
+ body: JSON.stringify({
115
+ name: key,
116
+ value,
117
+ scope: "workspace",
118
+ description: "Analytics S3-compatible replay storage", // i18n-ignore -- secret metadata description, not visible UI
119
+ }),
120
+ });
121
+
122
+ if (!res.ok) {
123
+ const body = (await res.json().catch(() => null)) as {
124
+ error?: string;
125
+ } | null;
126
+ throw new Error(body?.error ?? `Save failed (${res.status})`);
127
+ }
128
+ }
129
+ }
130
+
44
131
  type SessionRecordingSummary = {
45
132
  id: string;
46
133
  clientRecordingId: string;
@@ -85,7 +172,6 @@ export default function SessionsPage() {
85
172
  const t = useT();
86
173
  const navigate = useNavigate();
87
174
  const [searchParams, setSearchParams] = useSearchParams();
88
- const [filtersOpen, setFiltersOpen] = useState(false);
89
175
  const range = readRange(searchParams.get("range"));
90
176
  const app = searchParams.get("app") ?? "";
91
177
  const query = searchParams.get("q") ?? "";
@@ -119,12 +205,12 @@ export default function SessionsPage() {
119
205
  <div className="analytics-sessions-page mx-auto flex w-full max-w-7xl flex-col gap-4 px-4 py-5">
120
206
  <Card>
121
207
  <CardContent className="p-3">
122
- <div className="analytics-sessions-filter-grid grid gap-2">
208
+ <div className="analytics-sessions-filter-bar flex flex-wrap items-center gap-2">
123
209
  <div className="analytics-sessions-filter-label flex items-center gap-2 px-1 text-sm font-medium">
124
210
  <IconFilter className="h-4 w-4 text-muted-foreground" />
125
- {t("sessions.segmentFilters")}
211
+ {t("sessions.filters")}
126
212
  </div>
127
- <div className="relative">
213
+ <div className="analytics-sessions-filter-search relative min-w-0">
128
214
  <IconSearch className="pointer-events-none absolute start-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
129
215
  <Input
130
216
  value={query}
@@ -133,37 +219,75 @@ export default function SessionsPage() {
133
219
  className="h-9 ps-9"
134
220
  />
135
221
  </div>
136
- <Select
137
- value={range}
138
- onValueChange={(value) => updateFilter("range", value)}
139
- >
140
- <SelectTrigger className="h-9" aria-label={t("sessions.range")}>
141
- <SelectValue />
142
- </SelectTrigger>
143
- <SelectContent>
144
- {RANGE_OPTIONS.map((value) => (
145
- <SelectItem key={value} value={value}>
146
- {rangeLabel(value, t)}
147
- </SelectItem>
148
- ))}
149
- </SelectContent>
150
- </Select>
151
- <Button
152
- type="button"
153
- variant="outline"
154
- size="sm"
155
- className="h-9 justify-between gap-2"
156
- onClick={() => setFiltersOpen((value) => !value)}
157
- aria-expanded={filtersOpen}
158
- >
159
- {t("sessions.filters")}
160
- <IconChevronDown
161
- className={cn(
162
- "h-4 w-4 transition-transform",
163
- filtersOpen && "rotate-180",
164
- )}
165
- />
166
- </Button>
222
+ <Popover>
223
+ <Tooltip>
224
+ <TooltipTrigger asChild>
225
+ <PopoverTrigger asChild>
226
+ <Button
227
+ type="button"
228
+ variant="outline"
229
+ size="icon"
230
+ className="h-9 w-9 shrink-0"
231
+ aria-label={t("sessions.filters")}
232
+ >
233
+ <IconSettings className="h-4 w-4" />
234
+ </Button>
235
+ </PopoverTrigger>
236
+ </TooltipTrigger>
237
+ <TooltipContent>{t("sessions.filters")}</TooltipContent>
238
+ </Tooltip>
239
+ <PopoverContent align="end" className="w-72 p-3">
240
+ <div className="grid gap-3">
241
+ <div className="text-sm font-medium">
242
+ {t("sessions.filters")}
243
+ </div>
244
+ <div className="grid gap-1.5">
245
+ <div className="text-xs font-medium text-muted-foreground">
246
+ {t("sessions.range")}
247
+ </div>
248
+ <Select
249
+ value={range}
250
+ onValueChange={(value) => updateFilter("range", value)}
251
+ >
252
+ <SelectTrigger
253
+ className="h-9"
254
+ aria-label={t("sessions.range")}
255
+ >
256
+ <SelectValue />
257
+ </SelectTrigger>
258
+ <SelectContent>
259
+ {RANGE_OPTIONS.map((value) => (
260
+ <SelectItem key={value} value={value}>
261
+ {rangeLabel(value, t)}
262
+ </SelectItem>
263
+ ))}
264
+ </SelectContent>
265
+ </Select>
266
+ </div>
267
+ <div className="grid gap-1.5 border-t pt-3">
268
+ <div className="text-xs font-medium text-muted-foreground">
269
+ {t("sessions.userFilters")}
270
+ </div>
271
+ <Input
272
+ value={app}
273
+ onChange={(event) =>
274
+ updateFilter("app", event.target.value)
275
+ }
276
+ placeholder={t("sessions.appPlaceholder")}
277
+ className="h-9"
278
+ />
279
+ </div>
280
+ <div className="grid gap-1.5">
281
+ <div className="text-xs font-medium text-muted-foreground">
282
+ {t("sessions.eventFilters")}
283
+ </div>
284
+ <div className="flex h-9 items-center rounded-md border bg-muted/20 px-3 text-sm text-muted-foreground">
285
+ {t("sessions.anyActivity")}
286
+ </div>
287
+ </div>
288
+ </div>
289
+ </PopoverContent>
290
+ </Popover>
167
291
  <Button
168
292
  type="button"
169
293
  variant="ghost"
@@ -178,29 +302,6 @@ export default function SessionsPage() {
178
302
  />
179
303
  </Button>
180
304
  </div>
181
- {filtersOpen ? (
182
- <div className="analytics-sessions-filter-extra mt-3 grid gap-3 border-t pt-3">
183
- <div className="rounded-md border bg-muted/20 p-3">
184
- <div className="mb-2 text-[11px] font-semibold uppercase text-muted-foreground">
185
- {t("sessions.userFilters")}
186
- </div>
187
- <Input
188
- value={app}
189
- onChange={(event) => updateFilter("app", event.target.value)}
190
- placeholder={t("sessions.appPlaceholder")}
191
- className="h-9"
192
- />
193
- </div>
194
- <div className="rounded-md border bg-muted/20 p-3">
195
- <div className="mb-2 text-[11px] font-semibold uppercase text-muted-foreground">
196
- {t("sessions.eventFilters")}
197
- </div>
198
- <div className="flex h-9 items-center rounded-md border bg-background px-3 text-sm text-muted-foreground">
199
- {t("sessions.anyActivity")}
200
- </div>
201
- </div>
202
- </div>
203
- ) : null}
204
305
  </CardContent>
205
306
  </Card>
206
307
 
@@ -363,7 +464,7 @@ function EmptySessionsState() {
363
464
  );
364
465
  }
365
466
 
366
- function ReplayStorageHint() {
467
+ export function ReplayStorageHint() {
367
468
  const t = useT();
368
469
  const storageStatus = useReplayStorageStatus();
369
470
  const builderStatus = useBuilderStatus();
@@ -386,59 +487,147 @@ function ReplayStorageHint() {
386
487
  storageStatus.isLoading ||
387
488
  builderStatus.loading ||
388
489
  !builderConnect.hasFetchedStatus;
490
+ const [s3Expanded, setS3Expanded] = useState(false);
491
+ const [s3Values, setS3Values] = useState<Record<string, string>>({});
492
+ const [savingStorage, setSavingStorage] = useState(false);
493
+
494
+ async function handleSaveS3Storage() {
495
+ const missing = S3_STORAGE_FIELDS.filter(
496
+ (field) =>
497
+ "required" in field &&
498
+ field.required &&
499
+ !(s3Values[field.key] ?? "").trim(),
500
+ );
501
+ if (missing.length > 0) {
502
+ toast.error(t("settings.storageRequired"));
503
+ return;
504
+ }
505
+
506
+ setSavingStorage(true);
507
+ try {
508
+ await saveS3StorageSettings(s3Values);
509
+ setS3Values((current) => ({
510
+ ...current,
511
+ S3_SECRET_ACCESS_KEY: "",
512
+ }));
513
+ await storageStatus.refetch();
514
+ toast.success(t("settings.storageSaved"));
515
+ } catch (err) {
516
+ toast.error(
517
+ err instanceof Error ? err.message : t("settings.storageSaveFailed"),
518
+ );
519
+ } finally {
520
+ setSavingStorage(false);
521
+ }
522
+ }
389
523
 
390
524
  return (
391
- <div className="mb-6 flex flex-col gap-4 rounded-md border border-primary/30 bg-primary/5 p-4 sm:flex-row sm:items-center sm:justify-between">
392
- <div className="flex min-w-0 items-start gap-3">
393
- <div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-background text-primary">
394
- <IconCloud className="h-5 w-5" />
395
- </div>
396
- <div className="min-w-0">
397
- <div className="text-sm font-medium">
398
- {t("sessions.storageSetupTitle")}
525
+ <Collapsible open={s3Expanded} onOpenChange={setS3Expanded}>
526
+ <div className="mb-6 rounded-md border border-primary/30 bg-primary/5 p-4">
527
+ <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
528
+ <div className="flex min-w-0 items-start gap-3">
529
+ <div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-md bg-background text-primary">
530
+ <IconCloud className="h-5 w-5" />
531
+ </div>
532
+ <div className="min-w-0">
533
+ <div className="text-sm font-medium">
534
+ {t("sessions.storageSetupTitle")}
535
+ </div>
536
+ <p className="mt-0.5 text-xs text-muted-foreground">
537
+ {t("sessions.storageSetupDescription")}
538
+ </p>
539
+ </div>
540
+ </div>
541
+ <div className="flex shrink-0 flex-wrap items-center gap-3">
542
+ <Button
543
+ type="button"
544
+ size="sm"
545
+ className="shrink-0"
546
+ onClick={() =>
547
+ builderConnect.start({
548
+ trackingSource: "analytics_sessions_storage_hint",
549
+ trackingFlow: "replay_storage",
550
+ })
551
+ }
552
+ disabled={
553
+ builderConnect.connecting ||
554
+ builderStatusLoading ||
555
+ builderConnected
556
+ }
557
+ >
558
+ {builderConnect.connecting ? (
559
+ <IconLoader2 className="h-4 w-4 animate-spin" />
560
+ ) : builderConnected ? (
561
+ <IconCheck className="h-4 w-4" />
562
+ ) : (
563
+ <IconExternalLink className="h-4 w-4" />
564
+ )}
565
+ {builderConnected
566
+ ? t("sessions.storageConnected")
567
+ : t("sessions.connectBuilder")}
568
+ </Button>
569
+ <CollapsibleTrigger asChild>
570
+ <Button type="button" variant="ghost" size="sm">
571
+ <IconServer className="h-3.5 w-3.5" />
572
+ {t("sessions.configureS3")}
573
+ <Badge variant="outline" className="text-[10px]">
574
+ {t("settings.secondary")}
575
+ </Badge>
576
+ <IconChevronDown
577
+ className={cn(
578
+ "h-4 w-4 transition-transform",
579
+ s3Expanded && "rotate-180",
580
+ )}
581
+ />
582
+ </Button>
583
+ </CollapsibleTrigger>
399
584
  </div>
400
- <p className="mt-0.5 text-xs text-muted-foreground">
401
- {t("sessions.storageSetupDescription")}
402
- </p>
403
585
  </div>
586
+ <CollapsibleContent>
587
+ <div className="mt-4 border-t border-primary/20 pt-4">
588
+ <p className="mb-4 text-xs text-muted-foreground">
589
+ {t("settings.s3OwnBucketDescription")}
590
+ </p>
591
+ <div className="grid gap-4 sm:grid-cols-2">
592
+ {S3_STORAGE_FIELDS.map((field) => (
593
+ <div key={field.key} className="space-y-1.5">
594
+ <Label htmlFor={`replay-${field.key}`}>
595
+ {t(field.labelKey)}
596
+ </Label>
597
+ <Input
598
+ id={`replay-${field.key}`}
599
+ type={
600
+ "secret" in field && field.secret ? "password" : "text"
601
+ }
602
+ value={s3Values[field.key] ?? ""}
603
+ onChange={(event) =>
604
+ setS3Values((current) => ({
605
+ ...current,
606
+ [field.key]: event.target.value,
607
+ }))
608
+ }
609
+ placeholder={field.placeholder}
610
+ autoComplete="off"
611
+ disabled={savingStorage}
612
+ />
613
+ </div>
614
+ ))}
615
+ </div>
616
+ <div className="mt-4 flex justify-end">
617
+ <Button
618
+ onClick={handleSaveS3Storage}
619
+ disabled={savingStorage || storageStatus.isLoading}
620
+ >
621
+ {savingStorage ? (
622
+ <IconLoader2 className="h-4 w-4 animate-spin" />
623
+ ) : null}
624
+ {t("settings.saveStorage")}
625
+ </Button>
626
+ </div>
627
+ </div>
628
+ </CollapsibleContent>
404
629
  </div>
405
- <div className="flex shrink-0 flex-wrap items-center gap-3">
406
- <Button
407
- type="button"
408
- size="sm"
409
- className="shrink-0"
410
- onClick={() =>
411
- builderConnect.start({
412
- trackingSource: "analytics_sessions_storage_hint",
413
- trackingFlow: "replay_storage",
414
- })
415
- }
416
- disabled={
417
- builderConnect.connecting ||
418
- builderStatusLoading ||
419
- builderConnected
420
- }
421
- >
422
- {builderConnect.connecting ? (
423
- <IconLoader2 className="h-4 w-4 animate-spin" />
424
- ) : builderConnected ? (
425
- <IconCheck className="h-4 w-4" />
426
- ) : (
427
- <IconExternalLink className="h-4 w-4" />
428
- )}
429
- {builderConnected
430
- ? t("sessions.storageConnected")
431
- : t("sessions.connectBuilder")}
432
- </Button>
433
- <Link
434
- to="/settings#replay-storage"
435
- className="inline-flex items-center gap-1.5 text-xs font-medium text-muted-foreground underline underline-offset-2 hover:text-foreground"
436
- >
437
- <IconServer className="h-3.5 w-3.5" />
438
- {t("sessions.configureS3")}
439
- </Link>
440
- </div>
441
- </div>
630
+ </Collapsible>
442
631
  );
443
632
  }
444
633