@agent-native/core 0.92.0 → 0.92.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 (363) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/core/CHANGELOG.md +14 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/application-state/store.ts +12 -1
  5. package/corpus/core/src/client/AssistantChat.tsx +27 -14
  6. package/corpus/core/src/client/PoweredByBadge.tsx +1 -8
  7. package/corpus/core/src/client/agent-chat-adapter.ts +37 -10
  8. package/corpus/core/src/client/chat/widgets/DataTableWidget.tsx +52 -1
  9. package/corpus/core/src/client/db-admin/TableEditor.tsx +49 -0
  10. package/corpus/core/src/client/db-admin/export-utils.ts +13 -4
  11. package/corpus/core/src/client/db-admin/useAgentSync.ts +72 -0
  12. package/corpus/core/src/client/error-capture.ts +69 -7
  13. package/corpus/core/src/client/extensions/EmbeddedExtension.tsx +5 -1
  14. package/corpus/core/src/client/extensions/ExtensionViewer.tsx +53 -8
  15. package/corpus/core/src/client/extensions/InlineExtensionFrame.tsx +5 -1
  16. package/corpus/core/src/client/extensions/portable-extension.ts +1 -0
  17. package/corpus/core/src/client/org/OrgSwitcher.tsx +12 -9
  18. package/corpus/core/src/client/org/TeamPage.tsx +77 -71
  19. package/corpus/core/src/client/settings/SettingsPanel.tsx +205 -52
  20. package/corpus/core/src/client/settings/SettingsSection.tsx +8 -1
  21. package/corpus/core/src/client/settings/SettingsTabsPage.tsx +25 -9
  22. package/corpus/core/src/db-admin/agent-tools.ts +8 -2
  23. package/corpus/core/src/db-admin/operations.ts +116 -4
  24. package/corpus/core/src/db-admin/routes.ts +1 -0
  25. package/corpus/core/src/db-admin/types.ts +4 -0
  26. package/corpus/core/src/extensions/html-shell.ts +5 -4
  27. package/corpus/core/src/extensions/routes.ts +10 -0
  28. package/corpus/core/src/navigation/index.ts +1 -1
  29. package/corpus/core/src/resources/handlers.ts +10 -8
  30. package/corpus/core/src/server/poll.ts +293 -8
  31. package/corpus/core/src/styles/agent-native.css +27 -0
  32. package/corpus/core/src/templates/default/.agents/skills/actions/SKILL.md +5 -0
  33. package/corpus/core/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +5 -0
  34. package/corpus/core/src/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
  35. package/corpus/core/src/templates/default/.agents/skills/security/SKILL.md +7 -0
  36. package/corpus/core/src/templates/default/.agents/skills/storing-data/SKILL.md +5 -1
  37. package/corpus/core/src/templates/default/AGENTS.md +4 -2
  38. package/corpus/core/src/templates/headless/AGENTS.md +4 -0
  39. package/corpus/core/src/templates/workspace-core/.agents/skills/actions/SKILL.md +5 -0
  40. package/corpus/core/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +5 -0
  41. package/corpus/core/src/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +4 -0
  42. package/corpus/core/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -0
  43. package/corpus/core/src/templates/workspace-core/.agents/skills/performance/SKILL.md +4 -0
  44. package/corpus/core/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
  45. package/corpus/core/src/templates/workspace-core/.agents/skills/security/SKILL.md +7 -0
  46. package/corpus/core/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +5 -1
  47. package/corpus/core/src/templates/workspace-core/AGENTS.md +7 -1
  48. package/corpus/core/src/templates/workspace-root/AGENTS.md +7 -1
  49. package/corpus/templates/analytics/.agents/skills/actions/SKILL.md +5 -0
  50. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +10 -0
  51. package/corpus/templates/analytics/.agents/skills/real-time-sync/SKILL.md +1 -1
  52. package/corpus/templates/analytics/.agents/skills/security/SKILL.md +7 -0
  53. package/corpus/templates/analytics/.agents/skills/storing-data/SKILL.md +5 -1
  54. package/corpus/templates/analytics/AGENTS.md +33 -14
  55. package/corpus/templates/analytics/actions/capture-test-error.ts +4 -0
  56. package/corpus/templates/analytics/actions/get-analytics-alert-rule-defaults.ts +23 -0
  57. package/corpus/templates/analytics/actions/get-error-issue.ts +1 -1
  58. package/corpus/templates/analytics/actions/list-analysis-revisions.ts +40 -0
  59. package/corpus/templates/analytics/actions/list-dashboard-revisions.ts +40 -0
  60. package/corpus/templates/analytics/actions/list-dashboard-usage-stats.ts +299 -0
  61. package/corpus/templates/analytics/actions/navigate.ts +4 -2
  62. package/corpus/templates/analytics/actions/restore-analysis-revision.ts +43 -0
  63. package/corpus/templates/analytics/actions/restore-dashboard-revision.ts +68 -0
  64. package/corpus/templates/analytics/actions/save-analytics-alert-rule.ts +14 -0
  65. package/corpus/templates/analytics/actions/save-monitor.ts +1 -1
  66. package/corpus/templates/analytics/actions/view-screen.ts +24 -0
  67. package/corpus/templates/analytics/app/components/analysis/AnalysisHistoryPanel.tsx +181 -0
  68. package/corpus/templates/analytics/app/components/dashboard/DashboardHistoryPanel.tsx +184 -0
  69. package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +22 -16
  70. package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -1
  71. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +27 -21
  72. package/corpus/templates/analytics/app/hooks/use-analysis-revisions.ts +43 -0
  73. package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +110 -0
  74. package/corpus/templates/analytics/app/hooks/use-dashboard-revisions.ts +43 -0
  75. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +8 -3
  76. package/corpus/templates/analytics/app/i18n/zh-TW.ts +64 -2
  77. package/corpus/templates/analytics/app/i18n-data.ts +783 -3
  78. package/corpus/templates/analytics/app/pages/Agents.tsx +359 -19
  79. package/corpus/templates/analytics/app/pages/Settings.tsx +3 -2
  80. package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +34 -1
  81. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +28 -0
  82. package/corpus/templates/analytics/app/pages/analyses/AnalysisDetail.tsx +18 -1
  83. package/corpus/templates/analytics/app/pages/monitoring/ErrorsPanel.tsx +7 -10
  84. package/corpus/templates/analytics/app/pages/monitoring/errors/IssueDetail.tsx +479 -23
  85. package/corpus/templates/analytics/app/pages/monitoring/errors/IssueList.tsx +0 -9
  86. package/corpus/templates/analytics/app/pages/monitoring/errors/i18n.ts +11 -0
  87. package/corpus/templates/analytics/app/pages/monitoring/errors/types.ts +9 -1
  88. package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorFormPage.tsx +6 -3
  89. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +26 -30
  90. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +9 -99
  91. package/corpus/templates/analytics/app/pages/settings/AlertRulesSettingsCard.tsx +262 -104
  92. package/corpus/templates/analytics/app/routes/team.tsx +1 -1
  93. package/corpus/templates/analytics/changelog/2026-07-08-admins-can-audit-dashboard-usage-ownership-and-cleanup-signa.md +6 -0
  94. package/corpus/templates/analytics/changelog/2026-07-08-agent-monitoring-stays-available-to-non-admins.md +6 -0
  95. package/corpus/templates/analytics/changelog/2026-07-08-alert-rules-are-easier-to-scan-expand-and-edit-from-settings.md +6 -0
  96. package/corpus/templates/analytics/changelog/2026-07-08-analytics-alert-forms-remember-last-email-recipients.md +6 -0
  97. package/corpus/templates/analytics/changelog/2026-07-08-analytics-alert-rules-can-now-use-per-rule-slack-and-webhook.md +6 -0
  98. package/corpus/templates/analytics/changelog/2026-07-08-dashboard-history-and-chat-context.md +5 -0
  99. package/corpus/templates/analytics/changelog/2026-07-08-dashboard-usage-now-counts-explorer-dashboard-traffic.md +6 -0
  100. package/corpus/templates/analytics/changelog/2026-07-08-demo-mode-session-emails-are-filtered-and-anonymized.md +6 -0
  101. package/corpus/templates/analytics/changelog/2026-07-08-error-issue-detail-adds-stack-traces-and-frequency-bars.md +6 -0
  102. package/corpus/templates/analytics/changelog/2026-07-08-error-issue-detail-now-shows-the-latest-occurrence-message-a.md +6 -0
  103. package/corpus/templates/analytics/changelog/2026-07-08-monitoring-errors-only-show-test-action-when-empty.md +6 -0
  104. package/corpus/templates/analytics/changelog/2026-07-08-production-session-replay-now-requires-private-blob-storage-.md +6 -0
  105. package/corpus/templates/analytics/changelog/2026-07-08-shared-extension-links-now-show-a-clear-message-when-you-do-.md +6 -0
  106. package/corpus/templates/analytics/changelog/2026-07-08-team-settings-show-access-controls-next-to-member-lists.md +6 -0
  107. package/corpus/templates/analytics/changelog/2026-07-08-uptime-monitors-now-use-a-10-second-request-timeout-by-defau.md +6 -0
  108. package/corpus/templates/analytics/server/db/schema-monitoring.ts +1 -1
  109. package/corpus/templates/analytics/server/db/schema.ts +56 -0
  110. package/corpus/templates/analytics/server/lib/analytics-alerts.ts +92 -0
  111. package/corpus/templates/analytics/server/lib/dashboards-store.ts +334 -5
  112. package/corpus/templates/analytics/server/lib/error-capture.ts +231 -36
  113. package/corpus/templates/analytics/server/lib/session-replay.ts +114 -8
  114. package/corpus/templates/analytics/server/lib/uptime-monitors.ts +5 -4
  115. package/corpus/templates/analytics/server/plugins/db.ts +112 -2
  116. package/corpus/templates/analytics/server/routes/api/dashboard-agent-context.json.get.ts +1 -0
  117. package/corpus/templates/assets/.agents/skills/actions/SKILL.md +5 -0
  118. package/corpus/templates/assets/.agents/skills/real-time-sync/SKILL.md +1 -1
  119. package/corpus/templates/assets/.agents/skills/security/SKILL.md +7 -0
  120. package/corpus/templates/assets/.agents/skills/storing-data/SKILL.md +5 -1
  121. package/corpus/templates/assets/AGENTS.md +4 -0
  122. package/corpus/templates/assets/app/routes/settings.tsx +1 -1
  123. package/corpus/templates/assets/app/routes/team.tsx +1 -1
  124. package/corpus/templates/brain/.agents/skills/actions/SKILL.md +5 -0
  125. package/corpus/templates/brain/.agents/skills/adding-a-feature/SKILL.md +5 -0
  126. package/corpus/templates/brain/.agents/skills/real-time-sync/SKILL.md +1 -1
  127. package/corpus/templates/brain/.agents/skills/security/SKILL.md +7 -0
  128. package/corpus/templates/brain/AGENTS.md +4 -0
  129. package/corpus/templates/brain/app/routes/team.tsx +1 -1
  130. package/corpus/templates/calendar/.agents/skills/actions/SKILL.md +5 -0
  131. package/corpus/templates/calendar/.agents/skills/real-time-sync/SKILL.md +1 -1
  132. package/corpus/templates/calendar/.agents/skills/security/SKILL.md +7 -0
  133. package/corpus/templates/calendar/.agents/skills/storing-data/SKILL.md +5 -1
  134. package/corpus/templates/calendar/AGENTS.md +4 -0
  135. package/corpus/templates/calendar/app/components/calendar/DayView.tsx +11 -2
  136. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +21 -8
  137. package/corpus/templates/calendar/app/components/calendar/MonthView.tsx +7 -1
  138. package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +11 -2
  139. package/corpus/templates/calendar/app/lib/popover-click-guard.ts +48 -4
  140. package/corpus/templates/calendar/app/pages/Settings.tsx +1 -1
  141. package/corpus/templates/calendar/app/routes/_app.team.tsx +1 -1
  142. package/corpus/templates/calendar/changelog/2026-07-08-clicking-empty-calendar-space-closes-an-open-event-popover-b.md +6 -0
  143. package/corpus/templates/chat/.agents/skills/actions/SKILL.md +5 -0
  144. package/corpus/templates/chat/.agents/skills/adding-a-feature/SKILL.md +5 -0
  145. package/corpus/templates/chat/.agents/skills/real-time-sync/SKILL.md +1 -1
  146. package/corpus/templates/chat/.agents/skills/security/SKILL.md +7 -0
  147. package/corpus/templates/chat/AGENTS.md +4 -0
  148. package/corpus/templates/chat/app/hooks/use-navigation-state.ts +1 -1
  149. package/corpus/templates/chat/app/routes/settings.tsx +2 -2
  150. package/corpus/templates/chat/app/routes/team.tsx +1 -1
  151. package/corpus/templates/clips/.agents/skills/actions/SKILL.md +5 -0
  152. package/corpus/templates/clips/.agents/skills/security/SKILL.md +7 -0
  153. package/corpus/templates/clips/AGENTS.md +4 -0
  154. package/corpus/templates/clips/actions/finalize-recording.ts +9 -3
  155. package/corpus/templates/clips/actions/set-thumbnail.ts +11 -2
  156. package/corpus/templates/clips/app/components/meetings/canvas-editor.tsx +1 -7
  157. package/corpus/templates/clips/app/components/player/player-controls.tsx +21 -13
  158. package/corpus/templates/clips/app/components/player/video-player.tsx +30 -75
  159. package/corpus/templates/clips/app/i18n/ar-SA.ts +6 -5
  160. package/corpus/templates/clips/app/i18n/de-DE.ts +6 -6
  161. package/corpus/templates/clips/app/i18n/en-US.ts +6 -5
  162. package/corpus/templates/clips/app/i18n/es-ES.ts +6 -5
  163. package/corpus/templates/clips/app/i18n/fr-FR.ts +6 -6
  164. package/corpus/templates/clips/app/i18n/hi-IN.ts +6 -5
  165. package/corpus/templates/clips/app/i18n/ja-JP.ts +6 -5
  166. package/corpus/templates/clips/app/i18n/ko-KR.ts +6 -5
  167. package/corpus/templates/clips/app/i18n/pt-BR.ts +6 -5
  168. package/corpus/templates/clips/app/i18n/zh-CN.ts +6 -5
  169. package/corpus/templates/clips/app/i18n/zh-TW.ts +6 -5
  170. package/corpus/templates/clips/app/routes/_app.meetings.$meetingId.tsx +51 -118
  171. package/corpus/templates/clips/changelog/2026-07-08-hosted-clip-uploads-now-clear-failed-buffered-chunks.md +6 -0
  172. package/corpus/templates/clips/changelog/2026-07-08-hosted-recording-uploads-now-avoid-sql-chunk-storage.md +6 -0
  173. package/corpus/templates/clips/changelog/2026-07-08-meeting-recaps-now-keep-summaries-action-items-and-transcrip.md +6 -0
  174. package/corpus/templates/clips/changelog/2026-07-08-video-playback-controls-now-match-loom-style-5-second-skips-.md +6 -0
  175. package/corpus/templates/clips/server/lib/recording-upload-state.ts +63 -0
  176. package/corpus/templates/clips/server/lib/video-storage.ts +4 -0
  177. package/corpus/templates/clips/server/routes/api/recordings/[recordingId]/thumbnail.post.ts +19 -13
  178. package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +40 -1
  179. package/corpus/templates/content/.agents/skills/actions/SKILL.md +5 -0
  180. package/corpus/templates/content/.agents/skills/real-time-sync/SKILL.md +1 -1
  181. package/corpus/templates/content/.agents/skills/security/SKILL.md +7 -0
  182. package/corpus/templates/content/.agents/skills/storing-data/SKILL.md +5 -1
  183. package/corpus/templates/content/AGENTS.md +4 -0
  184. package/corpus/templates/content/app/routes/_app.settings.tsx +2 -2
  185. package/corpus/templates/content/app/routes/_app.team.tsx +1 -1
  186. package/corpus/templates/design/.agents/skills/actions/SKILL.md +5 -0
  187. package/corpus/templates/design/.agents/skills/security/SKILL.md +7 -0
  188. package/corpus/templates/design/AGENTS.md +4 -0
  189. package/corpus/templates/design/actions/take-design-screenshot.ts +12 -9
  190. package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +57 -13
  191. package/corpus/templates/design/app/routes/team.tsx +1 -1
  192. package/corpus/templates/design/changelog/2026-07-08-local-images-added-to-designs-now-upload-to-file-storage-ins.md +6 -0
  193. package/corpus/templates/dispatch/.agents/skills/actions/SKILL.md +5 -0
  194. package/corpus/templates/dispatch/.agents/skills/security/SKILL.md +7 -0
  195. package/corpus/templates/dispatch/AGENTS.md +4 -0
  196. package/corpus/templates/dispatch/app/hooks/use-navigation-state.ts +1 -1
  197. package/corpus/templates/dispatch/app/routes/settings.tsx +2 -2
  198. package/corpus/templates/dispatch/app/routes/team.tsx +1 -1
  199. package/corpus/templates/forms/.agents/skills/actions/SKILL.md +5 -0
  200. package/corpus/templates/forms/.agents/skills/real-time-sync/SKILL.md +1 -1
  201. package/corpus/templates/forms/.agents/skills/security/SKILL.md +7 -0
  202. package/corpus/templates/forms/.agents/skills/storing-data/SKILL.md +5 -1
  203. package/corpus/templates/forms/AGENTS.md +4 -0
  204. package/corpus/templates/forms/app/routes/_app.settings.tsx +2 -2
  205. package/corpus/templates/forms/app/routes/_app.team.tsx +1 -1
  206. package/corpus/templates/forms/shared/navigation.ts +1 -1
  207. package/corpus/templates/macros/.agents/skills/security/SKILL.md +7 -0
  208. package/corpus/templates/macros/AGENTS.md +4 -0
  209. package/corpus/templates/macros/app/routes/settings.tsx +2 -2
  210. package/corpus/templates/macros/app/routes/team.tsx +1 -1
  211. package/corpus/templates/mail/.agents/skills/actions/SKILL.md +5 -0
  212. package/corpus/templates/mail/.agents/skills/email-drafts/SKILL.md +1 -1
  213. package/corpus/templates/mail/.agents/skills/real-time-sync/SKILL.md +1 -1
  214. package/corpus/templates/mail/.agents/skills/security/SKILL.md +7 -0
  215. package/corpus/templates/mail/.agents/skills/storing-data/SKILL.md +5 -1
  216. package/corpus/templates/mail/AGENTS.md +4 -0
  217. package/corpus/templates/mail/changelog/2026-07-08-mail-attachments-in-hosted-environments-now-require-file-sto.md +6 -0
  218. package/corpus/templates/mail/server/handlers/media.ts +26 -1
  219. package/corpus/templates/mail/server/lib/outgoing-email.ts +24 -1
  220. package/corpus/templates/mail/server/lib/upload-store.ts +2 -1
  221. package/corpus/templates/plan/.agents/skills/actions/SKILL.md +5 -0
  222. package/corpus/templates/plan/.agents/skills/adding-a-feature/SKILL.md +5 -0
  223. package/corpus/templates/plan/.agents/skills/real-time-sync/SKILL.md +1 -1
  224. package/corpus/templates/plan/.agents/skills/security/SKILL.md +7 -0
  225. package/corpus/templates/plan/AGENTS.md +4 -0
  226. package/corpus/templates/plan/app/hooks/use-navigation-state.ts +1 -1
  227. package/corpus/templates/plan/app/routes/settings.tsx +2 -2
  228. package/corpus/templates/plan/app/routes/team.tsx +1 -1
  229. package/corpus/templates/plan/changelog/2026-07-08-hosted-plan-images-now-require-connected-storage.md +6 -0
  230. package/corpus/templates/plan/server/lib/plan-assets.ts +33 -4
  231. package/corpus/templates/slides/.agents/skills/actions/SKILL.md +5 -0
  232. package/corpus/templates/slides/.agents/skills/security/SKILL.md +7 -0
  233. package/corpus/templates/slides/AGENTS.md +4 -0
  234. package/corpus/templates/slides/actions/generate-image-api.ts +15 -3
  235. package/corpus/templates/slides/app/routes/settings.tsx +2 -2
  236. package/corpus/templates/slides/app/routes/team.tsx +1 -1
  237. package/corpus/templates/slides/changelog/2026-07-08-generated-slide-images-now-return-hosted-file-storage-urls-i.md +6 -0
  238. package/corpus/templates/slides/shared/api.ts +1 -1
  239. package/dist/application-state/store.d.ts.map +1 -1
  240. package/dist/application-state/store.js +8 -2
  241. package/dist/application-state/store.js.map +1 -1
  242. package/dist/client/AssistantChat.d.ts.map +1 -1
  243. package/dist/client/AssistantChat.js +24 -13
  244. package/dist/client/AssistantChat.js.map +1 -1
  245. package/dist/client/PoweredByBadge.d.ts +1 -1
  246. package/dist/client/PoweredByBadge.d.ts.map +1 -1
  247. package/dist/client/PoweredByBadge.js +3 -9
  248. package/dist/client/PoweredByBadge.js.map +1 -1
  249. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  250. package/dist/client/agent-chat-adapter.js +28 -9
  251. package/dist/client/agent-chat-adapter.js.map +1 -1
  252. package/dist/client/chat/widgets/DataTableWidget.d.ts.map +1 -1
  253. package/dist/client/chat/widgets/DataTableWidget.js +19 -2
  254. package/dist/client/chat/widgets/DataTableWidget.js.map +1 -1
  255. package/dist/client/db-admin/TableEditor.d.ts.map +1 -1
  256. package/dist/client/db-admin/TableEditor.js +19 -2
  257. package/dist/client/db-admin/TableEditor.js.map +1 -1
  258. package/dist/client/db-admin/export-utils.d.ts +2 -0
  259. package/dist/client/db-admin/export-utils.d.ts.map +1 -1
  260. package/dist/client/db-admin/export-utils.js +11 -3
  261. package/dist/client/db-admin/export-utils.js.map +1 -1
  262. package/dist/client/db-admin/useAgentSync.d.ts.map +1 -1
  263. package/dist/client/db-admin/useAgentSync.js +63 -0
  264. package/dist/client/db-admin/useAgentSync.js.map +1 -1
  265. package/dist/client/error-capture.d.ts.map +1 -1
  266. package/dist/client/error-capture.js +41 -7
  267. package/dist/client/error-capture.js.map +1 -1
  268. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -1
  269. package/dist/client/extensions/EmbeddedExtension.js +3 -1
  270. package/dist/client/extensions/EmbeddedExtension.js.map +1 -1
  271. package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -1
  272. package/dist/client/extensions/ExtensionViewer.js +32 -5
  273. package/dist/client/extensions/ExtensionViewer.js.map +1 -1
  274. package/dist/client/extensions/InlineExtensionFrame.d.ts.map +1 -1
  275. package/dist/client/extensions/InlineExtensionFrame.js +3 -1
  276. package/dist/client/extensions/InlineExtensionFrame.js.map +1 -1
  277. package/dist/client/extensions/portable-extension.d.ts.map +1 -1
  278. package/dist/client/extensions/portable-extension.js +1 -0
  279. package/dist/client/extensions/portable-extension.js.map +1 -1
  280. package/dist/client/org/OrgSwitcher.d.ts +1 -1
  281. package/dist/client/org/OrgSwitcher.d.ts.map +1 -1
  282. package/dist/client/org/OrgSwitcher.js +11 -6
  283. package/dist/client/org/OrgSwitcher.js.map +1 -1
  284. package/dist/client/org/TeamPage.d.ts.map +1 -1
  285. package/dist/client/org/TeamPage.js +5 -5
  286. package/dist/client/org/TeamPage.js.map +1 -1
  287. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  288. package/dist/client/settings/SettingsPanel.js +37 -14
  289. package/dist/client/settings/SettingsPanel.js.map +1 -1
  290. package/dist/client/settings/SettingsSection.js +1 -1
  291. package/dist/client/settings/SettingsSection.js.map +1 -1
  292. package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
  293. package/dist/client/settings/SettingsTabsPage.js +21 -10
  294. package/dist/client/settings/SettingsTabsPage.js.map +1 -1
  295. package/dist/collab/routes.d.ts +1 -1
  296. package/dist/db-admin/agent-tools.d.ts.map +1 -1
  297. package/dist/db-admin/agent-tools.js +7 -2
  298. package/dist/db-admin/agent-tools.js.map +1 -1
  299. package/dist/db-admin/operations.d.ts.map +1 -1
  300. package/dist/db-admin/operations.js +90 -4
  301. package/dist/db-admin/operations.js.map +1 -1
  302. package/dist/db-admin/routes.d.ts.map +1 -1
  303. package/dist/db-admin/routes.js +1 -0
  304. package/dist/db-admin/routes.js.map +1 -1
  305. package/dist/db-admin/types.d.ts +4 -0
  306. package/dist/db-admin/types.d.ts.map +1 -1
  307. package/dist/db-admin/types.js.map +1 -1
  308. package/dist/extensions/html-shell.d.ts +5 -4
  309. package/dist/extensions/html-shell.d.ts.map +1 -1
  310. package/dist/extensions/html-shell.js.map +1 -1
  311. package/dist/extensions/routes.d.ts.map +1 -1
  312. package/dist/extensions/routes.js +8 -0
  313. package/dist/extensions/routes.js.map +1 -1
  314. package/dist/navigation/index.d.ts +1 -1
  315. package/dist/navigation/index.d.ts.map +1 -1
  316. package/dist/navigation/index.js +1 -1
  317. package/dist/navigation/index.js.map +1 -1
  318. package/dist/notifications/routes.d.ts +1 -1
  319. package/dist/observability/routes.d.ts +1 -1
  320. package/dist/resources/handlers.d.ts +6 -1
  321. package/dist/resources/handlers.d.ts.map +1 -1
  322. package/dist/resources/handlers.js +9 -8
  323. package/dist/resources/handlers.js.map +1 -1
  324. package/dist/server/poll.d.ts +12 -4
  325. package/dist/server/poll.d.ts.map +1 -1
  326. package/dist/server/poll.js +236 -9
  327. package/dist/server/poll.js.map +1 -1
  328. package/dist/styles/agent-native.css +27 -0
  329. package/dist/templates/default/.agents/skills/actions/SKILL.md +5 -0
  330. package/dist/templates/default/.agents/skills/adding-a-feature/SKILL.md +5 -0
  331. package/dist/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
  332. package/dist/templates/default/.agents/skills/security/SKILL.md +7 -0
  333. package/dist/templates/default/.agents/skills/storing-data/SKILL.md +5 -1
  334. package/dist/templates/default/AGENTS.md +4 -2
  335. package/dist/templates/headless/AGENTS.md +4 -0
  336. package/dist/templates/workspace-core/.agents/skills/actions/SKILL.md +5 -0
  337. package/dist/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +5 -0
  338. package/dist/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +4 -0
  339. package/dist/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -0
  340. package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +4 -0
  341. package/dist/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
  342. package/dist/templates/workspace-core/.agents/skills/security/SKILL.md +7 -0
  343. package/dist/templates/workspace-core/.agents/skills/storing-data/SKILL.md +5 -1
  344. package/dist/templates/workspace-core/AGENTS.md +7 -1
  345. package/dist/templates/workspace-root/AGENTS.md +7 -1
  346. package/package.json +1 -1
  347. package/src/templates/default/.agents/skills/actions/SKILL.md +5 -0
  348. package/src/templates/default/.agents/skills/adding-a-feature/SKILL.md +5 -0
  349. package/src/templates/default/.agents/skills/real-time-sync/SKILL.md +1 -1
  350. package/src/templates/default/.agents/skills/security/SKILL.md +7 -0
  351. package/src/templates/default/.agents/skills/storing-data/SKILL.md +5 -1
  352. package/src/templates/default/AGENTS.md +4 -2
  353. package/src/templates/headless/AGENTS.md +4 -0
  354. package/src/templates/workspace-core/.agents/skills/actions/SKILL.md +5 -0
  355. package/src/templates/workspace-core/.agents/skills/adding-a-feature/SKILL.md +5 -0
  356. package/src/templates/workspace-core/.agents/skills/context-awareness/SKILL.md +4 -0
  357. package/src/templates/workspace-core/.agents/skills/extensions/SKILL.md +6 -0
  358. package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +4 -0
  359. package/src/templates/workspace-core/.agents/skills/real-time-sync/SKILL.md +1 -1
  360. package/src/templates/workspace-core/.agents/skills/security/SKILL.md +7 -0
  361. package/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +5 -1
  362. package/src/templates/workspace-core/AGENTS.md +7 -1
  363. package/src/templates/workspace-root/AGENTS.md +7 -1
@@ -12,15 +12,17 @@ import {
12
12
  IconClock,
13
13
  IconArrowLeft,
14
14
  IconDatabase,
15
+ IconHistory,
15
16
  IconLock,
16
17
  IconUsersGroup,
17
18
  IconWorld,
18
19
  } from "@tabler/icons-react";
19
20
  import { useQuery, useQueryClient } from "@tanstack/react-query";
20
- import { useEffect } from "react";
21
+ import { useEffect, useState } from "react";
21
22
  import { useParams } from "react-router";
22
23
  import { Link, useNavigate } from "react-router";
23
24
 
25
+ import { AnalysisHistoryPanel } from "@/components/analysis/AnalysisHistoryPanel";
24
26
  import {
25
27
  useSetPageTitle,
26
28
  useSetHeaderActions,
@@ -104,6 +106,7 @@ export default function AnalysisDetail() {
104
106
  const navigate = useNavigate();
105
107
  const queryClient = useQueryClient();
106
108
  const { send, isGenerating, codeRequiredDialog } = useSendToAgentChat();
109
+ const [historyOpen, setHistoryOpen] = useState(false);
107
110
 
108
111
  const analysesSync = useChangeVersions(["analyses", "action"]);
109
112
  const { data: analysis, isLoading } = useQuery({
@@ -182,6 +185,14 @@ export default function AnalysisDetail() {
182
185
  resourceTitle={analysis.name}
183
186
  variant="compact"
184
187
  />
188
+ <Button
189
+ variant="outline"
190
+ size="sm"
191
+ onClick={() => setHistoryOpen(true)}
192
+ >
193
+ <IconHistory className="h-4 w-4" />
194
+ {t("analyses.historyTitle")}
195
+ </Button>
185
196
  {canEdit ? (
186
197
  <Tooltip>
187
198
  <TooltipTrigger asChild>
@@ -254,6 +265,12 @@ export default function AnalysisDetail() {
254
265
  return (
255
266
  <>
256
267
  {codeRequiredDialog}
268
+ <AnalysisHistoryPanel
269
+ analysisId={analysis.id}
270
+ open={historyOpen}
271
+ onOpenChange={setHistoryOpen}
272
+ canRestore={canEdit}
273
+ />
257
274
  <div
258
275
  className={cn(
259
276
  "space-y-6",
@@ -59,18 +59,15 @@ export function ErrorsPanel() {
59
59
  [status, debouncedSearch],
60
60
  );
61
61
 
62
- const { data, isLoading, isFetching, error, refetch } = useActionQuery<
63
- ErrorIssueSummary[]
64
- >("list-error-issues", queryArgs, { staleTime: 10_000 });
65
-
66
- const resolveIssue = useActionMutation<
67
- { id: string; status: IssueStatus; assignee: string | null },
68
- { id: string; status: IssueStatus }
69
- >("resolve-error-issue");
70
- const sendTest = useActionMutation<{ issueId: string }, { message?: string }>(
71
- "capture-test-error",
62
+ const { data, isLoading, isFetching, error, refetch } = useActionQuery(
63
+ "list-error-issues",
64
+ queryArgs,
65
+ { staleTime: 10_000 },
72
66
  );
73
67
 
68
+ const resolveIssue = useActionMutation("resolve-error-issue");
69
+ const sendTest = useActionMutation("capture-test-error");
70
+
74
71
  const issues = useMemo(() => (Array.isArray(data) ? data : []), [data]);
75
72
 
76
73
  // Refresh list + detail when a capture or agent edit records an
@@ -15,8 +15,8 @@ import { cn } from "@/lib/utils";
15
15
 
16
16
  import { fmt, useErrorsT } from "./i18n";
17
17
  import type {
18
+ ErrorBreadcrumb,
18
19
  ErrorEventDetail,
19
- ErrorIssueDetail,
20
20
  ErrorIssueSummary,
21
21
  IssueStatus,
22
22
  ParsedStackFrame,
@@ -31,6 +31,8 @@ import {
31
31
  useStatusLabel,
32
32
  } from "./utils";
33
33
 
34
+ const DEFAULT_FREQUENCY_DAYS = 14;
35
+
34
36
  export function IssueDetail({
35
37
  issueId,
36
38
  fallback,
@@ -47,7 +49,7 @@ export function IssueDetail({
47
49
  const t = useErrorsT();
48
50
  const statusLabel = useStatusLabel();
49
51
 
50
- const { data, isLoading, error } = useActionQuery<ErrorIssueDetail>(
52
+ const { data, isLoading, error } = useActionQuery(
51
53
  "get-error-issue",
52
54
  { id: issueId },
53
55
  { staleTime: 10_000 },
@@ -158,6 +160,7 @@ export function IssueDetail({
158
160
  </div>
159
161
 
160
162
  {issue ? <OverviewGrid issue={issue} latest={latest} /> : null}
163
+ {issue ? <FrequencyCard issue={issue} /> : null}
161
164
 
162
165
  {isLoading && !latest ? (
163
166
  <Card>
@@ -169,6 +172,7 @@ export function IssueDetail({
169
172
  ) : (
170
173
  <>
171
174
  <StackTraceCard event={latest} />
175
+ {latest ? <LatestOccurrenceCard event={latest} /> : null}
172
176
  <BreadcrumbsCard event={latest} />
173
177
  <OccurrencesCard events={events} />
174
178
  </>
@@ -177,6 +181,79 @@ export function IssueDetail({
177
181
  );
178
182
  }
179
183
 
184
+ function hasEntries(value: Record<string, unknown>): boolean {
185
+ return Object.keys(value).length > 0;
186
+ }
187
+
188
+ function JsonBlock({ value }: { value: Record<string, unknown> }) {
189
+ return (
190
+ <pre className="mt-2 max-h-52 overflow-auto rounded-md bg-muted/50 p-3 text-xs leading-relaxed text-foreground">
191
+ {JSON.stringify(value, null, 2)}
192
+ </pre>
193
+ );
194
+ }
195
+
196
+ function LatestOccurrenceCard({ event }: { event: ErrorEventDetail }) {
197
+ const t = useErrorsT();
198
+ const details: Array<{ label: string; value: string }> = [
199
+ {
200
+ label: t.occurrenceTime,
201
+ value: formatDateTime(event.occurredAt),
202
+ },
203
+ {
204
+ label: t.handled,
205
+ value: event.handled ? t.handled : t.unhandled,
206
+ },
207
+ ];
208
+ if (event.url) details.push({ label: t.url, value: event.url });
209
+
210
+ return (
211
+ <Card>
212
+ <CardContent className="space-y-4 p-4">
213
+ <div className="text-sm font-medium">{t.latestOccurrence}</div>
214
+ {event.message ? (
215
+ <div>
216
+ <div className="text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
217
+ {t.message}
218
+ </div>
219
+ <p className="mt-1 whitespace-pre-wrap rounded-md bg-muted/40 p-3 font-mono text-xs text-foreground">
220
+ {event.message}
221
+ </p>
222
+ </div>
223
+ ) : null}
224
+ <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
225
+ {details.map((item) => (
226
+ <div key={item.label} className="min-w-0">
227
+ <div className="text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
228
+ {item.label}
229
+ </div>
230
+ <div className="mt-0.5 truncate text-sm text-foreground">
231
+ {item.value}
232
+ </div>
233
+ </div>
234
+ ))}
235
+ </div>
236
+ {hasEntries(event.tags) ? (
237
+ <div>
238
+ <div className="text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
239
+ {t.tags}
240
+ </div>
241
+ <JsonBlock value={event.tags} />
242
+ </div>
243
+ ) : null}
244
+ {hasEntries(event.extra) ? (
245
+ <div>
246
+ <div className="text-[11px] font-semibold uppercase tracking-wide text-muted-foreground">
247
+ {t.additionalData}
248
+ </div>
249
+ <JsonBlock value={event.extra} />
250
+ </div>
251
+ ) : null}
252
+ </CardContent>
253
+ </Card>
254
+ );
255
+ }
256
+
180
257
  function OverviewGrid({
181
258
  issue,
182
259
  latest,
@@ -214,55 +291,207 @@ function OverviewGrid({
214
291
  );
215
292
  }
216
293
 
294
+ function FrequencyCard({ issue }: { issue: ErrorIssueSummary }) {
295
+ const t = useErrorsT();
296
+ const values = (
297
+ issue.sparkline?.length
298
+ ? issue.sparkline
299
+ : new Array(DEFAULT_FREQUENCY_DAYS).fill(0)
300
+ ).map((value) => (Number.isFinite(value) ? Math.max(0, value) : 0));
301
+ const max = Math.max(1, ...values);
302
+ const total = values.reduce((sum, value) => sum + value, 0);
303
+ const dayLabels = buildFrequencyDayLabels(values.length);
304
+ const barClass = frequencyBarClass(issue.level);
305
+
306
+ return (
307
+ <Card>
308
+ <CardContent className="space-y-4 p-4">
309
+ <div className="flex flex-wrap items-start justify-between gap-3">
310
+ <div>
311
+ <div className="text-sm font-medium">{t.frequency}</div>
312
+ <div className="text-xs text-muted-foreground">
313
+ {fmt(t.frequencyWindow, { days: values.length })}
314
+ </div>
315
+ </div>
316
+ <div className="text-end">
317
+ <div className="text-sm font-semibold text-foreground">
318
+ {formatNumber(total)}
319
+ </div>
320
+ <div className="text-[11px] uppercase tracking-wide text-muted-foreground">
321
+ {t.recentOccurrences}
322
+ </div>
323
+ </div>
324
+ </div>
325
+
326
+ <div className="flex h-28 items-end gap-1 border-b border-border/60 pb-2">
327
+ {values.map((count, index) => {
328
+ const height = count === 0 ? 4 : Math.max(12, (count / max) * 100);
329
+ const label = fmt(t.frequencyBarLabel, {
330
+ count,
331
+ date: dayLabels[index]?.long ?? "",
332
+ });
333
+ return (
334
+ <div
335
+ key={`${dayLabels[index]?.iso ?? index}-${index}`}
336
+ className="flex h-full flex-1 items-end"
337
+ title={label}
338
+ aria-label={label}
339
+ >
340
+ <div
341
+ className={cn(
342
+ "w-full rounded-t-sm transition-colors",
343
+ count > 0 ? barClass : "bg-muted-foreground/15",
344
+ )}
345
+ style={{ height: `${height}%` }}
346
+ />
347
+ </div>
348
+ );
349
+ })}
350
+ </div>
351
+
352
+ <div className="flex items-center justify-between text-[11px] text-muted-foreground">
353
+ <span>{dayLabels[0]?.short}</span>
354
+ {total === 0 ? <span>{t.noRecentVolume}</span> : null}
355
+ <span>{dayLabels[dayLabels.length - 1]?.short}</span>
356
+ </div>
357
+ </CardContent>
358
+ </Card>
359
+ );
360
+ }
361
+
362
+ function frequencyBarClass(level: ErrorIssueSummary["level"]): string {
363
+ switch (level) {
364
+ case "fatal":
365
+ return "bg-red-500/85 hover:bg-red-400";
366
+ case "error":
367
+ return "bg-rose-500/85 hover:bg-rose-400";
368
+ case "warning":
369
+ return "bg-amber-400/85 hover:bg-amber-300";
370
+ case "info":
371
+ return "bg-sky-400/85 hover:bg-sky-300";
372
+ case "debug":
373
+ default:
374
+ return "bg-violet-400/75 hover:bg-violet-300";
375
+ }
376
+ }
377
+
378
+ function buildFrequencyDayLabels(days: number): Array<{
379
+ iso: string;
380
+ short: string;
381
+ long: string;
382
+ }> {
383
+ const formatter = new Intl.DateTimeFormat(undefined, {
384
+ month: "short",
385
+ day: "numeric",
386
+ });
387
+ return Array.from({ length: Math.max(1, days) }, (_, index) => {
388
+ const date = new Date();
389
+ date.setHours(0, 0, 0, 0);
390
+ date.setDate(date.getDate() - (days - 1 - index));
391
+ return {
392
+ iso: date.toISOString().slice(0, 10),
393
+ short: formatter.format(date),
394
+ long: formatter.format(date),
395
+ };
396
+ });
397
+ }
398
+
217
399
  function StackTraceCard({ event }: { event?: ErrorEventDetail }) {
218
400
  const t = useErrorsT();
219
401
  const frames = event?.stack ?? [];
402
+ const rawStack = event?.rawStack?.trim() ?? "";
403
+ const headline =
404
+ rawStack.split("\n")[0]?.trim() ||
405
+ (event ? `${event.type}: ${event.message}` : "");
220
406
 
221
407
  return (
222
408
  <Card>
223
409
  <CardContent className="p-0">
224
- <div className="border-b px-4 py-3 text-sm font-medium">
225
- {t.stackTrace}
410
+ <div className="flex flex-wrap items-center justify-between gap-2 border-b px-4 py-3">
411
+ <div>
412
+ <div className="text-sm font-medium">{t.stackTrace}</div>
413
+ {headline ? (
414
+ <div className="mt-0.5 truncate font-mono text-xs text-muted-foreground">
415
+ {headline}
416
+ </div>
417
+ ) : null}
418
+ </div>
419
+ {frames.length > 0 ? (
420
+ <span className="rounded-full border bg-muted/30 px-2 py-0.5 text-[11px] font-medium text-muted-foreground">
421
+ {fmt(t.stackFrameCount, { count: frames.length })}
422
+ </span>
423
+ ) : null}
226
424
  </div>
227
425
  {frames.length === 0 ? (
228
426
  <p className="p-4 text-sm text-muted-foreground">{t.noStack}</p>
229
427
  ) : (
230
- <ol className="divide-y divide-border/40 font-mono text-xs">
428
+ <ol className="divide-y divide-border/40">
231
429
  {frames.map((frame, index) => (
232
- <StackFrameRow key={index} frame={frame} />
430
+ <StackFrameRow key={index} frame={frame} index={index} />
233
431
  ))}
234
432
  </ol>
235
433
  )}
434
+ {rawStack ? (
435
+ <details className="border-t px-4 py-3">
436
+ <summary className="cursor-pointer text-xs font-medium text-muted-foreground transition-colors hover:text-foreground">
437
+ {t.rawStack}
438
+ </summary>
439
+ <pre className="mt-3 max-h-72 overflow-auto rounded-md bg-muted/40 p-3 font-mono text-xs leading-relaxed text-foreground">
440
+ {rawStack}
441
+ </pre>
442
+ </details>
443
+ ) : null}
236
444
  </CardContent>
237
445
  </Card>
238
446
  );
239
447
  }
240
448
 
241
- function StackFrameRow({ frame }: { frame: ParsedStackFrame }) {
449
+ function StackFrameRow({
450
+ frame,
451
+ index,
452
+ }: {
453
+ frame: ParsedStackFrame;
454
+ index: number;
455
+ }) {
242
456
  const t = useErrorsT();
457
+ const location = stackFrameLocation(frame);
458
+
243
459
  return (
244
460
  <li
245
461
  className={cn(
246
- "flex flex-wrap items-baseline gap-x-2 px-4 py-2",
247
- frame.inApp ? "bg-transparent" : "bg-muted/20 text-muted-foreground",
462
+ "grid gap-3 px-4 py-3 sm:grid-cols-[auto_1fr_auto]",
463
+ frame.inApp ? "bg-background" : "bg-muted/20 text-muted-foreground",
248
464
  )}
249
465
  >
250
- <span
251
- className={cn(
252
- "font-semibold",
253
- frame.inApp ? "text-foreground" : "text-muted-foreground",
254
- )}
255
- >
256
- {frame.function || "<anonymous>"}
257
- </span>
258
- <span className="text-muted-foreground">
259
- {shortFrameFile(frame.file)}
260
- {frame.lineno != null ? `:${frame.lineno}` : ""}
261
- {frame.colno != null ? `:${frame.colno}` : ""}
466
+ <span className="flex size-6 shrink-0 items-center justify-center rounded-md bg-muted/35 font-mono text-[11px] text-muted-foreground">
467
+ {index + 1}
262
468
  </span>
469
+ <div className="min-w-0">
470
+ <div className="flex flex-wrap items-baseline gap-x-2 gap-y-1 font-mono text-xs">
471
+ <span
472
+ className={cn(
473
+ "font-semibold",
474
+ frame.inApp ? "text-foreground" : "text-muted-foreground",
475
+ )}
476
+ >
477
+ {frame.function || "<anonymous>"}
478
+ </span>
479
+ <span className="min-w-0 truncate text-muted-foreground">
480
+ {location}
481
+ </span>
482
+ </div>
483
+ {frame.raw ? (
484
+ <pre className="mt-2 overflow-x-auto rounded-md bg-muted/25 px-3 py-2 font-mono text-[11px] leading-relaxed text-muted-foreground">
485
+ {frame.raw}
486
+ </pre>
487
+ ) : null}
488
+ {frame.sourceContext?.length ? (
489
+ <SourceContextBlock lines={frame.sourceContext} />
490
+ ) : null}
491
+ </div>
263
492
  <span
264
493
  className={cn(
265
- "ms-auto rounded px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide",
494
+ "h-fit rounded px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide sm:justify-self-end",
266
495
  frame.inApp
267
496
  ? "bg-primary/10 text-primary"
268
497
  : "bg-muted text-muted-foreground",
@@ -274,9 +503,236 @@ function StackFrameRow({ frame }: { frame: ParsedStackFrame }) {
274
503
  );
275
504
  }
276
505
 
506
+ function SourceContextBlock({
507
+ lines,
508
+ }: {
509
+ lines: NonNullable<ParsedStackFrame["sourceContext"]>;
510
+ }) {
511
+ return (
512
+ <div className="mt-2 overflow-hidden rounded-md bg-muted/20">
513
+ <div className="overflow-x-auto font-mono text-[11px] leading-relaxed">
514
+ {lines.map((line) => (
515
+ <div
516
+ key={line.line}
517
+ className={cn(
518
+ "grid min-w-max grid-cols-[3rem_1fr]",
519
+ line.highlight
520
+ ? "bg-rose-500/10 text-foreground"
521
+ : "text-muted-foreground",
522
+ )}
523
+ >
524
+ <span
525
+ className={cn(
526
+ "select-none px-2 py-0.5 text-end",
527
+ line.highlight ? "text-rose-300" : "text-muted-foreground/70",
528
+ )}
529
+ >
530
+ {line.line}
531
+ </span>
532
+ <code className="whitespace-pre px-3 py-0.5">
533
+ <HighlightedSourceLine text={line.text || " "} />
534
+ </code>
535
+ </div>
536
+ ))}
537
+ </div>
538
+ </div>
539
+ );
540
+ }
541
+
542
+ type SourceTokenKind =
543
+ | "keyword"
544
+ | "string"
545
+ | "number"
546
+ | "comment"
547
+ | "literal"
548
+ | "function"
549
+ | "operator"
550
+ | "plain";
551
+
552
+ const JS_KEYWORDS = new Set([
553
+ "as",
554
+ "async",
555
+ "await",
556
+ "break",
557
+ "case",
558
+ "catch",
559
+ "class",
560
+ "const",
561
+ "continue",
562
+ "default",
563
+ "do",
564
+ "else",
565
+ "export",
566
+ "extends",
567
+ "finally",
568
+ "for",
569
+ "from",
570
+ "function",
571
+ "if",
572
+ "import",
573
+ "in",
574
+ "interface",
575
+ "let",
576
+ "new",
577
+ "of",
578
+ "return",
579
+ "switch",
580
+ "throw",
581
+ "try",
582
+ "type",
583
+ "typeof",
584
+ "var",
585
+ "while",
586
+ ]);
587
+
588
+ const JS_LITERALS = new Set(["false", "null", "true", "undefined"]);
589
+
590
+ function HighlightedSourceLine({ text }: { text: string }) {
591
+ return (
592
+ <>
593
+ {tokenizeSourceLine(text).map((token, index) => (
594
+ <span
595
+ key={`${index}-${token.text}`}
596
+ className={sourceTokenClass(token.kind)}
597
+ >
598
+ {token.text}
599
+ </span>
600
+ ))}
601
+ </>
602
+ );
603
+ }
604
+
605
+ function tokenizeSourceLine(
606
+ text: string,
607
+ ): Array<{ text: string; kind: SourceTokenKind }> {
608
+ const tokens: Array<{ text: string; kind: SourceTokenKind }> = [];
609
+ let index = 0;
610
+
611
+ const push = (value: string, kind: SourceTokenKind) => {
612
+ if (value) tokens.push({ text: value, kind });
613
+ };
614
+
615
+ while (index < text.length) {
616
+ const rest = text.slice(index);
617
+
618
+ if (rest.startsWith("//")) {
619
+ push(rest, "comment");
620
+ break;
621
+ }
622
+
623
+ const quote = text[index];
624
+ if (quote === '"' || quote === "'" || quote === "`") {
625
+ let end = index + 1;
626
+ let escaped = false;
627
+ while (end < text.length) {
628
+ const char = text[end];
629
+ if (escaped) {
630
+ escaped = false;
631
+ } else if (char === "\\") {
632
+ escaped = true;
633
+ } else if (char === quote) {
634
+ end += 1;
635
+ break;
636
+ }
637
+ end += 1;
638
+ }
639
+ push(text.slice(index, end), "string");
640
+ index = end;
641
+ continue;
642
+ }
643
+
644
+ const number = rest.match(/^\b\d+(?:\.\d+)?\b/);
645
+ if (number) {
646
+ push(number[0], "number");
647
+ index += number[0].length;
648
+ continue;
649
+ }
650
+
651
+ const word = rest.match(/^[A-Za-z_$][\w$]*/);
652
+ if (word) {
653
+ const value = word[0];
654
+ const after = text.slice(index + value.length);
655
+ const kind: SourceTokenKind = JS_KEYWORDS.has(value)
656
+ ? "keyword"
657
+ : JS_LITERALS.has(value)
658
+ ? "literal"
659
+ : /^\s*\(/.test(after)
660
+ ? "function"
661
+ : "plain";
662
+ push(value, kind);
663
+ index += value.length;
664
+ continue;
665
+ }
666
+
667
+ const operator = rest.match(/^[{}()[\].,;:?=+\-*/!<>|&%]+/);
668
+ if (operator) {
669
+ push(operator[0], "operator");
670
+ index += operator[0].length;
671
+ continue;
672
+ }
673
+
674
+ push(text[index], "plain");
675
+ index += 1;
676
+ }
677
+
678
+ return tokens.length ? tokens : [{ text: " ", kind: "plain" }];
679
+ }
680
+
681
+ function sourceTokenClass(kind: SourceTokenKind): string {
682
+ switch (kind) {
683
+ case "keyword":
684
+ return "text-fuchsia-300";
685
+ case "string":
686
+ return "text-emerald-300";
687
+ case "number":
688
+ return "text-amber-300";
689
+ case "comment":
690
+ return "text-muted-foreground/65 italic";
691
+ case "literal":
692
+ return "text-orange-300";
693
+ case "function":
694
+ return "text-sky-300";
695
+ case "operator":
696
+ return "text-muted-foreground";
697
+ case "plain":
698
+ default:
699
+ return "";
700
+ }
701
+ }
702
+
703
+ function stackFrameLocation(frame: ParsedStackFrame): string {
704
+ return [
705
+ shortFrameFile(frame.file),
706
+ frame.lineno != null ? frame.lineno : null,
707
+ frame.colno != null ? frame.colno : null,
708
+ ]
709
+ .filter((part) => part !== null && part !== "")
710
+ .join(":");
711
+ }
712
+
713
+ function normalizeBreadcrumb(value: unknown): ErrorBreadcrumb | null {
714
+ if (typeof value === "string" && value.trim()) {
715
+ return { message: value.trim() };
716
+ }
717
+ if (!value || typeof value !== "object" || Array.isArray(value)) return null;
718
+
719
+ const record = value as Record<string, unknown>;
720
+ const timestamp =
721
+ typeof record.timestamp === "string" ? record.timestamp : undefined;
722
+ const category =
723
+ typeof record.category === "string" ? record.category : undefined;
724
+ const message =
725
+ typeof record.message === "string" ? record.message : undefined;
726
+
727
+ if (!timestamp && !category && !message) return null;
728
+ return { timestamp, category, message };
729
+ }
730
+
277
731
  function BreadcrumbsCard({ event }: { event?: ErrorEventDetail }) {
278
732
  const t = useErrorsT();
279
- const crumbs = event?.breadcrumbs ?? [];
733
+ const crumbs = (event?.breadcrumbs ?? [])
734
+ .map(normalizeBreadcrumb)
735
+ .filter((crumb): crumb is ErrorBreadcrumb => Boolean(crumb));
280
736
 
281
737
  return (
282
738
  <Card>
@@ -145,15 +145,6 @@ export function IssueList({
145
145
  className={cn("size-3.5", isFetching && "animate-spin")}
146
146
  />
147
147
  </Button>
148
- <Button
149
- variant="outline"
150
- size="sm"
151
- onClick={onSendTestError}
152
- disabled={sendingTest}
153
- >
154
- <IconBug className="size-3.5" />
155
- {sendingTest ? t.sending : t.sendTestError}
156
- </Button>
157
148
  </div>
158
149
  </div>
159
150
 
@@ -57,7 +57,18 @@ const MESSAGES = {
57
57
  metaUsers: "Users affected",
58
58
  metaEnvironment: "Environment",
59
59
  metaRelease: "Release",
60
+ frequency: "Error frequency",
61
+ frequencyWindow: "Last {days} days",
62
+ frequencyBarLabel: "{count} occurrences on {date}",
63
+ recentOccurrences: "recent",
64
+ noRecentVolume: "No recent volume",
65
+ latestOccurrence: "Latest occurrence",
66
+ message: "Message",
67
+ url: "URL",
68
+ occurrenceTime: "Occurred",
60
69
  stackTrace: "Stack trace",
70
+ stackFrameCount: "{count} frames",
71
+ rawStack: "Raw stack",
61
72
  noStack: "No stack trace was captured for this error.",
62
73
  inApp: "In app",
63
74
  vendor: "Vendor",