@agent-native/core 0.90.11 → 0.91.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 (322) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +34 -0
  3. package/corpus/core/docs/content/toolkit-settings.mdx +56 -3
  4. package/corpus/core/package.json +2 -1
  5. package/corpus/core/src/agent/run-ownership.ts +29 -1
  6. package/corpus/core/src/changelog/parse.ts +78 -0
  7. package/corpus/core/src/chat-threads/schema.ts +43 -0
  8. package/corpus/core/src/chat-threads/store.ts +144 -12
  9. package/corpus/core/src/cli/design-connect.ts +53 -9
  10. package/corpus/core/src/cli/workspace-dev.ts +25 -5
  11. package/corpus/core/src/client/AgentPanel.tsx +79 -14
  12. package/corpus/core/src/client/AssistantChat.tsx +359 -425
  13. package/corpus/core/src/client/analytics-session.ts +70 -0
  14. package/corpus/core/src/client/analytics.ts +175 -49
  15. package/corpus/core/src/client/chat/message-components.tsx +22 -2
  16. package/corpus/core/src/client/components/ui/message-scroller.tsx +131 -0
  17. package/corpus/core/src/client/conversation/AgentConversation.tsx +63 -41
  18. package/corpus/core/src/client/error-capture.ts +616 -0
  19. package/corpus/core/src/client/index.ts +12 -0
  20. package/corpus/core/src/client/session-replay.ts +57 -7
  21. package/corpus/core/src/client/settings/SettingsPanel.tsx +123 -1
  22. package/corpus/core/src/client/settings/SettingsTabsPage.tsx +292 -45
  23. package/corpus/core/src/client/settings/index.ts +1 -0
  24. package/corpus/core/src/client/sharing/ShareButton.tsx +14 -11
  25. package/corpus/core/src/client/sharing/ShareDialog.tsx +13 -10
  26. package/corpus/core/src/mcp/builtin-tools.ts +29 -2
  27. package/corpus/core/src/server/agent-chat-plugin.ts +174 -79
  28. package/corpus/core/src/server/onboarding-html.ts +28 -16
  29. package/corpus/core/src/sharing/actions/list-resource-shares.ts +45 -1
  30. package/corpus/core/src/sharing/actions/share-resource.ts +9 -0
  31. package/corpus/core/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -2
  32. package/corpus/core/src/templates/workspace-core/.agents/skills/changelog/SKILL.md +9 -6
  33. package/corpus/core/src/vite/client.ts +217 -35
  34. package/corpus/templates/analytics/.agents/skills/adhoc-analysis/SKILL.md +8 -7
  35. package/corpus/templates/analytics/.agents/skills/analysis-workspace/SKILL.md +14 -10
  36. package/corpus/templates/analytics/.agents/skills/bigquery/SKILL.md +15 -15
  37. package/corpus/templates/analytics/.agents/skills/cross-source-analysis/SKILL.md +1 -0
  38. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +38 -22
  39. package/corpus/templates/analytics/.agents/skills/data-programs/SKILL.md +114 -61
  40. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +1 -0
  41. package/corpus/templates/analytics/.agents/skills/gong/SKILL.md +7 -7
  42. package/corpus/templates/analytics/.agents/skills/hubspot/SKILL.md +1 -0
  43. package/corpus/templates/analytics/.agents/skills/prometheus/SKILL.md +1 -1
  44. package/corpus/templates/analytics/.builder/skills/bigquery/SKILL.md +17 -17
  45. package/corpus/templates/analytics/.builder/skills/gcloud/SKILL.md +5 -5
  46. package/corpus/templates/analytics/.builder/skills/github/SKILL.md +7 -7
  47. package/corpus/templates/analytics/.builder/skills/gong/SKILL.md +12 -12
  48. package/corpus/templates/analytics/.builder/skills/grafana/SKILL.md +7 -7
  49. package/corpus/templates/analytics/.builder/skills/sentry/SKILL.md +7 -7
  50. package/corpus/templates/analytics/.builder/skills/slack/SKILL.md +6 -6
  51. package/corpus/templates/analytics/.builder/skills/stripe/SKILL.md +7 -7
  52. package/corpus/templates/analytics/AGENTS.md +47 -2
  53. package/corpus/templates/analytics/actions/add-status-page-monitor.ts +36 -0
  54. package/corpus/templates/analytics/actions/capture-test-error.ts +33 -0
  55. package/corpus/templates/analytics/actions/delete-monitor.ts +24 -0
  56. package/corpus/templates/analytics/actions/delete-status-page.ts +23 -0
  57. package/corpus/templates/analytics/actions/get-error-issue.ts +36 -0
  58. package/corpus/templates/analytics/actions/get-monitor-stats.ts +55 -0
  59. package/corpus/templates/analytics/actions/get-monitor.ts +27 -0
  60. package/corpus/templates/analytics/actions/get-public-status-page.ts +41 -0
  61. package/corpus/templates/analytics/actions/get-status-page.ts +29 -0
  62. package/corpus/templates/analytics/actions/list-error-issues.ts +40 -0
  63. package/corpus/templates/analytics/actions/list-monitors.ts +21 -0
  64. package/corpus/templates/analytics/actions/list-status-pages.ts +21 -0
  65. package/corpus/templates/analytics/actions/match-error-issues.ts +51 -0
  66. package/corpus/templates/analytics/actions/navigate.ts +58 -4
  67. package/corpus/templates/analytics/actions/remove-status-page-monitor.ts +22 -0
  68. package/corpus/templates/analytics/actions/reorder-status-page-monitors.ts +25 -0
  69. package/corpus/templates/analytics/actions/resolve-error-issue.ts +38 -0
  70. package/corpus/templates/analytics/actions/run-monitor-check.ts +23 -0
  71. package/corpus/templates/analytics/actions/save-analysis.ts +1 -0
  72. package/corpus/templates/analytics/actions/save-monitor.ts +145 -0
  73. package/corpus/templates/analytics/actions/save-status-page.ts +47 -0
  74. package/corpus/templates/analytics/actions/view-screen.ts +212 -0
  75. package/corpus/templates/analytics/app/components/layout/Header.tsx +1 -0
  76. package/corpus/templates/analytics/app/components/layout/Layout.tsx +10 -1
  77. package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +134 -39
  78. package/corpus/templates/analytics/app/components/monitoring/PublicStatusView.tsx +351 -0
  79. package/corpus/templates/analytics/app/components/monitoring/ResponseTimeChart.tsx +174 -0
  80. package/corpus/templates/analytics/app/components/monitoring/UptimeStatCards.tsx +85 -0
  81. package/corpus/templates/analytics/app/components/monitoring/UptimeTimelineBars.tsx +106 -0
  82. package/corpus/templates/analytics/app/components/monitoring/chart-utils.ts +101 -0
  83. package/corpus/templates/analytics/app/components/monitoring/index.ts +28 -0
  84. package/corpus/templates/analytics/app/components/monitoring/types.ts +42 -0
  85. package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +34 -0
  86. package/corpus/templates/analytics/app/i18n/zh-TW.ts +6 -0
  87. package/corpus/templates/analytics/app/i18n-data.ts +33 -0
  88. package/corpus/templates/analytics/app/pages/DataSources.tsx +28 -0
  89. package/corpus/templates/analytics/app/pages/Settings.tsx +77 -9
  90. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/SqlChartCard.tsx +3 -3
  91. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/dashboard-layout.ts +7 -0
  92. package/corpus/templates/analytics/app/pages/monitoring/ErrorsPanel.tsx +205 -0
  93. package/corpus/templates/analytics/app/pages/monitoring/MonitoringPage.tsx +100 -0
  94. package/corpus/templates/analytics/app/pages/monitoring/UptimePanel.tsx +475 -0
  95. package/corpus/templates/analytics/app/pages/monitoring/errors/IssueDetail.tsx +372 -0
  96. package/corpus/templates/analytics/app/pages/monitoring/errors/IssueList.tsx +360 -0
  97. package/corpus/templates/analytics/app/pages/monitoring/errors/Sparkline.tsx +55 -0
  98. package/corpus/templates/analytics/app/pages/monitoring/errors/i18n.ts +106 -0
  99. package/corpus/templates/analytics/app/pages/monitoring/errors/types.ts +75 -0
  100. package/corpus/templates/analytics/app/pages/monitoring/errors/utils.ts +132 -0
  101. package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorDetail.tsx +535 -0
  102. package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorFormPage.tsx +1026 -0
  103. package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorList.tsx +311 -0
  104. package/corpus/templates/analytics/app/pages/monitoring/uptime/i18n.ts +235 -0
  105. package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/StatusPageEditor.tsx +661 -0
  106. package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/StatusPagesView.tsx +355 -0
  107. package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/i18n.ts +106 -0
  108. package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/slug.ts +24 -0
  109. package/corpus/templates/analytics/app/pages/monitoring/uptime/status-pages/types.ts +20 -0
  110. package/corpus/templates/analytics/app/pages/monitoring/uptime/status-summary.ts +87 -0
  111. package/corpus/templates/analytics/app/pages/monitoring/uptime/types.ts +161 -0
  112. package/corpus/templates/analytics/app/pages/monitoring/uptime/utils.ts +210 -0
  113. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +76 -1
  114. package/corpus/templates/analytics/app/pages/sessions/SessionDevToolsPanel.tsx +74 -9
  115. package/corpus/templates/analytics/app/pages/sessions/SessionsPage.tsx +197 -20
  116. package/corpus/templates/analytics/app/root.tsx +33 -1
  117. package/corpus/templates/analytics/app/routes/monitoring._index.tsx +10 -0
  118. package/corpus/templates/analytics/app/routes/status.$slug.tsx +118 -0
  119. package/corpus/templates/analytics/changelog/2026-07-08-add-uptime-monitors-that-ping-your-urls-and-alert-you-when-t.md +6 -0
  120. package/corpus/templates/analytics/changelog/2026-07-08-analytics-chats-keep-large-data-dictionaries-compact-so-prov.md +6 -0
  121. package/corpus/templates/analytics/changelog/2026-07-08-create-public-status-pages-that-share-the-live-health-of-cho.md +6 -0
  122. package/corpus/templates/analytics/changelog/2026-07-08-dashboard-extension-panels-use-clearer-open-copy.md +6 -0
  123. package/corpus/templates/analytics/changelog/2026-07-08-error-capture.md +6 -0
  124. package/corpus/templates/analytics/changelog/2026-07-08-fixed-single-panel-dashboard-drags-so-they-no-longer-rewrite.md +6 -0
  125. package/corpus/templates/analytics/changelog/2026-07-08-jump-straight-from-an-error-in-a-session-recording-to-its-fu.md +6 -0
  126. package/corpus/templates/analytics/changelog/2026-07-08-monitor-detail-and-list-now-show-colorful-uptime-timelines-r.md +6 -0
  127. package/corpus/templates/analytics/changelog/2026-07-08-pinned-ask-chats-now-show-a-pin-icon-in-the-sidebar.md +6 -0
  128. package/corpus/templates/analytics/changelog/2026-07-08-publish-a-public-uptime-status-page-with-colorful-uptime-tim.md +6 -0
  129. package/corpus/templates/analytics/changelog/2026-07-08-sessions-now-hide-visitor-emails-in-demo-mode-and-show-clean.md +6 -0
  130. package/corpus/templates/analytics/changelog/2026-07-08-settings-are-cleaner-searchable-and-alerts-have-their-own-tab.md +5 -0
  131. package/corpus/templates/analytics/changelog/2026-07-08-sidebar-sections-stay-collapsed-until-opened.md +5 -0
  132. package/corpus/templates/analytics/changelog/2026-07-08-the-uptime-monitor-list-now-has-a-current-status-overview-wi.md +6 -0
  133. package/corpus/templates/analytics/docs/error-capture.md +172 -0
  134. package/corpus/templates/analytics/docs/uptime-monitoring.md +150 -0
  135. package/corpus/templates/analytics/server/db/schema-errors.ts +150 -0
  136. package/corpus/templates/analytics/server/db/schema-monitoring.ts +177 -0
  137. package/corpus/templates/analytics/server/db/schema.ts +6 -0
  138. package/corpus/templates/analytics/server/jobs/uptime-monitors.ts +123 -0
  139. package/corpus/templates/analytics/server/lib/agent-chat-plan-mode.ts +41 -0
  140. package/corpus/templates/analytics/server/lib/data-dictionary-context.ts +55 -12
  141. package/corpus/templates/analytics/server/lib/error-capture.ts +1331 -0
  142. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +47 -1
  143. package/corpus/templates/analytics/server/lib/monitor-stats.ts +494 -0
  144. package/corpus/templates/analytics/server/lib/status-pages.ts +798 -0
  145. package/corpus/templates/analytics/server/lib/uptime-monitors.ts +1665 -0
  146. package/corpus/templates/analytics/server/plugins/agent-chat.ts +7 -37
  147. package/corpus/templates/analytics/server/plugins/auth.ts +11 -1
  148. package/corpus/templates/analytics/server/plugins/db.ts +408 -0
  149. package/corpus/templates/analytics/server/plugins/uptime-monitor-jobs.ts +63 -0
  150. package/corpus/templates/assets/app/routes/_index.tsx +1 -1
  151. package/corpus/templates/assets/app/routes/settings.tsx +22 -1
  152. package/corpus/templates/assets/changelog/2026-07-08-recent-drafts-keep-comfortable-side-padding.md +6 -0
  153. package/corpus/templates/assets/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  154. package/corpus/templates/brain/app/routes/settings.tsx +43 -5
  155. package/corpus/templates/brain/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  156. package/corpus/templates/calendar/app/pages/Settings.tsx +44 -6
  157. package/corpus/templates/calendar/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  158. package/corpus/templates/chat/app/routes/settings.tsx +16 -1
  159. package/corpus/templates/chat/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  160. package/corpus/templates/clips/actions/lib/audio-only-transcription.ts +79 -37
  161. package/corpus/templates/clips/app/components/player/player-controls.tsx +35 -0
  162. package/corpus/templates/clips/app/components/player/video-player.tsx +162 -18
  163. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +63 -6
  164. package/corpus/templates/clips/app/routes/record.tsx +18 -1
  165. package/corpus/templates/clips/changelog/2026-07-07-shared-clips-start-playback-reliably-from-the-first-play-click.md +6 -0
  166. package/corpus/templates/clips/changelog/2026-07-08-chrome-extension-camera-and-microphone-menus-now-show-which-.md +6 -0
  167. package/corpus/templates/clips/changelog/2026-07-08-chrome-extension-copies-new-clip-links-after-saving.md +6 -0
  168. package/corpus/templates/clips/changelog/2026-07-08-chrome-extension-recordings-now-continue-after-granting-came.md +6 -0
  169. package/corpus/templates/clips/changelog/2026-07-08-mobile-shared-clips-now-have-loom-style-tap-and-skip-controls.md +6 -0
  170. package/corpus/templates/clips/changelog/2026-07-08-recordings-and-uploads-copy-links-after-saving.md +6 -0
  171. package/corpus/templates/clips/changelog/2026-07-08-settings-are-cleaner-searchable-and-collapse-s3-with-builder.md +5 -0
  172. package/corpus/templates/clips/changelog/2026-07-08-transcripts-are-more-reliable-for-longer-desktop-recordings-.md +6 -0
  173. package/corpus/templates/clips/chrome-extension/public/manifest.json +1 -0
  174. package/corpus/templates/clips/chrome-extension/src/background.ts +162 -13
  175. package/corpus/templates/clips/chrome-extension/src/content-script.ts +14 -0
  176. package/corpus/templates/clips/chrome-extension/src/offscreen.ts +36 -0
  177. package/corpus/templates/clips/chrome-extension/src/overlay.ts +99 -25
  178. package/corpus/templates/clips/chrome-extension/src/permission.ts +63 -14
  179. package/corpus/templates/clips/chrome-extension/src/popup.ts +123 -23
  180. package/corpus/templates/content/actions/_builder-cms-read-client.ts +47 -0
  181. package/corpus/templates/content/actions/_database-source-utils.ts +33 -4
  182. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +214 -29
  183. package/corpus/templates/content/actions/attach-content-database-source.ts +3 -0
  184. package/corpus/templates/content/actions/change-content-database-source-role.ts +1 -0
  185. package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +42 -32
  186. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +352 -81
  187. package/corpus/templates/content/app/components/editor/database/settings.tsx +49 -24
  188. package/corpus/templates/content/app/routes/_app.settings.tsx +19 -1
  189. package/corpus/templates/content/changelog/2026-07-07-builder-source-tag-fields-now-import-as-multi-select.md +6 -0
  190. package/corpus/templates/content/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  191. package/corpus/templates/content/shared/api.ts +3 -0
  192. package/corpus/templates/content/shared/properties.ts +42 -0
  193. package/corpus/templates/design/AGENTS.md +9 -0
  194. package/corpus/templates/design/actions/generate-design.ts +3 -0
  195. package/corpus/templates/design/actions/get-design-system.ts +147 -1
  196. package/corpus/templates/design/actions/request-localhost-write-consent.ts +141 -0
  197. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +13 -19
  198. package/corpus/templates/design/app/routes/settings.tsx +16 -1
  199. package/corpus/templates/design/changelog/2026-07-08-design-generation-now-follows-hydrated-builder-design-system.md +6 -0
  200. package/corpus/templates/design/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  201. package/corpus/templates/design/server/lib/verify-write-grant.ts +4 -2
  202. package/corpus/templates/dispatch/app/routes/settings.tsx +23 -2
  203. package/corpus/templates/dispatch/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  204. package/corpus/templates/forms/app/routes/_app.settings.tsx +16 -1
  205. package/corpus/templates/forms/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  206. package/corpus/templates/macros/app/routes/settings.tsx +16 -1
  207. package/corpus/templates/macros/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  208. package/corpus/templates/mail/app/components/settings/GmailFiltersSection.tsx +1 -1
  209. package/corpus/templates/mail/app/components/settings/SnippetsSection.tsx +1 -1
  210. package/corpus/templates/mail/app/pages/SettingsPage.tsx +123 -105
  211. package/corpus/templates/mail/changelog/2026-07-08-settings-redesigned-with-a-consistent-searchable-layout.md +5 -0
  212. package/corpus/templates/plan/app/routes/settings.tsx +23 -2
  213. package/corpus/templates/plan/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  214. package/corpus/templates/slides/actions/add-slide.ts +1 -0
  215. package/corpus/templates/slides/actions/get-design-system.ts +147 -1
  216. package/corpus/templates/slides/app/pages/Index.tsx +46 -1
  217. package/corpus/templates/slides/app/routes/settings.tsx +16 -1
  218. package/corpus/templates/slides/changelog/2026-07-08-deck-generation-now-follows-hydrated-builder-design-system-g.md +6 -0
  219. package/corpus/templates/slides/changelog/2026-07-08-settings-are-cleaner-and-searchable.md +5 -0
  220. package/dist/agent/run-ownership.d.ts +6 -0
  221. package/dist/agent/run-ownership.d.ts.map +1 -1
  222. package/dist/agent/run-ownership.js +11 -1
  223. package/dist/agent/run-ownership.js.map +1 -1
  224. package/dist/changelog/parse.d.ts +7 -0
  225. package/dist/changelog/parse.d.ts.map +1 -1
  226. package/dist/changelog/parse.js +64 -0
  227. package/dist/changelog/parse.js.map +1 -1
  228. package/dist/chat-threads/schema.d.ts +445 -0
  229. package/dist/chat-threads/schema.d.ts.map +1 -0
  230. package/dist/chat-threads/schema.js +39 -0
  231. package/dist/chat-threads/schema.js.map +1 -0
  232. package/dist/chat-threads/store.d.ts +12 -0
  233. package/dist/chat-threads/store.d.ts.map +1 -1
  234. package/dist/chat-threads/store.js +99 -10
  235. package/dist/chat-threads/store.js.map +1 -1
  236. package/dist/cli/design-connect.d.ts.map +1 -1
  237. package/dist/cli/design-connect.js +40 -10
  238. package/dist/cli/design-connect.js.map +1 -1
  239. package/dist/cli/workspace-dev.d.ts.map +1 -1
  240. package/dist/cli/workspace-dev.js +20 -5
  241. package/dist/cli/workspace-dev.js.map +1 -1
  242. package/dist/client/AgentPanel.d.ts.map +1 -1
  243. package/dist/client/AgentPanel.js +35 -7
  244. package/dist/client/AgentPanel.js.map +1 -1
  245. package/dist/client/AssistantChat.d.ts.map +1 -1
  246. package/dist/client/AssistantChat.js +105 -183
  247. package/dist/client/AssistantChat.js.map +1 -1
  248. package/dist/client/analytics-session.d.ts +3 -0
  249. package/dist/client/analytics-session.d.ts.map +1 -0
  250. package/dist/client/analytics-session.js +66 -0
  251. package/dist/client/analytics-session.js.map +1 -0
  252. package/dist/client/analytics.d.ts +32 -0
  253. package/dist/client/analytics.d.ts.map +1 -1
  254. package/dist/client/analytics.js +116 -46
  255. package/dist/client/analytics.js.map +1 -1
  256. package/dist/client/chat/message-components.d.ts +2 -1
  257. package/dist/client/chat/message-components.d.ts.map +1 -1
  258. package/dist/client/chat/message-components.js +16 -6
  259. package/dist/client/chat/message-components.js.map +1 -1
  260. package/dist/client/components/ui/message-scroller.d.ts +16 -0
  261. package/dist/client/components/ui/message-scroller.d.ts.map +1 -0
  262. package/dist/client/components/ui/message-scroller.js +25 -0
  263. package/dist/client/components/ui/message-scroller.js.map +1 -0
  264. package/dist/client/conversation/AgentConversation.d.ts +1 -1
  265. package/dist/client/conversation/AgentConversation.d.ts.map +1 -1
  266. package/dist/client/conversation/AgentConversation.js +3 -8
  267. package/dist/client/conversation/AgentConversation.js.map +1 -1
  268. package/dist/client/error-capture.d.ts +99 -0
  269. package/dist/client/error-capture.d.ts.map +1 -0
  270. package/dist/client/error-capture.js +437 -0
  271. package/dist/client/error-capture.js.map +1 -0
  272. package/dist/client/index.d.ts +2 -2
  273. package/dist/client/index.d.ts.map +1 -1
  274. package/dist/client/index.js +3 -1
  275. package/dist/client/index.js.map +1 -1
  276. package/dist/client/session-replay.d.ts +22 -0
  277. package/dist/client/session-replay.d.ts.map +1 -1
  278. package/dist/client/session-replay.js +45 -4
  279. package/dist/client/session-replay.js.map +1 -1
  280. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  281. package/dist/client/settings/SettingsPanel.js +100 -5
  282. package/dist/client/settings/SettingsPanel.js.map +1 -1
  283. package/dist/client/settings/SettingsTabsPage.d.ts +49 -1
  284. package/dist/client/settings/SettingsTabsPage.d.ts.map +1 -1
  285. package/dist/client/settings/SettingsTabsPage.js +131 -19
  286. package/dist/client/settings/SettingsTabsPage.js.map +1 -1
  287. package/dist/client/settings/index.d.ts +1 -1
  288. package/dist/client/settings/index.d.ts.map +1 -1
  289. package/dist/client/settings/index.js.map +1 -1
  290. package/dist/client/sharing/ShareButton.d.ts.map +1 -1
  291. package/dist/client/sharing/ShareButton.js +13 -8
  292. package/dist/client/sharing/ShareButton.js.map +1 -1
  293. package/dist/client/sharing/ShareDialog.d.ts.map +1 -1
  294. package/dist/client/sharing/ShareDialog.js +12 -7
  295. package/dist/client/sharing/ShareDialog.js.map +1 -1
  296. package/dist/collab/routes.d.ts +1 -1
  297. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  298. package/dist/mcp/builtin-tools.d.ts.map +1 -1
  299. package/dist/mcp/builtin-tools.js +21 -2
  300. package/dist/mcp/builtin-tools.js.map +1 -1
  301. package/dist/observability/routes.d.ts +1 -1
  302. package/dist/org/schema.d.ts +15 -15
  303. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  304. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  305. package/dist/server/agent-chat-plugin.js +109 -73
  306. package/dist/server/agent-chat-plugin.js.map +1 -1
  307. package/dist/server/onboarding-html.d.ts.map +1 -1
  308. package/dist/server/onboarding-html.js +28 -16
  309. package/dist/server/onboarding-html.js.map +1 -1
  310. package/dist/sharing/actions/list-resource-shares.js +30 -1
  311. package/dist/sharing/actions/list-resource-shares.js.map +1 -1
  312. package/dist/sharing/actions/share-resource.js +6 -0
  313. package/dist/sharing/actions/share-resource.js.map +1 -1
  314. package/dist/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -2
  315. package/dist/templates/workspace-core/.agents/skills/changelog/SKILL.md +9 -6
  316. package/dist/vite/client.d.ts.map +1 -1
  317. package/dist/vite/client.js +192 -30
  318. package/dist/vite/client.js.map +1 -1
  319. package/docs/content/toolkit-settings.mdx +56 -3
  320. package/package.json +2 -1
  321. package/src/templates/workspace-core/.agents/skills/agent-native-toolkit/SKILL.md +4 -2
  322. package/src/templates/workspace-core/.agents/skills/changelog/SKILL.md +9 -6
@@ -2,6 +2,11 @@ import { getDbExec } from "@agent-native/core/db";
2
2
  import { and, eq, isNull, or } from "drizzle-orm";
3
3
 
4
4
  import { getDb, schema } from "../db/index.js";
5
+ import {
6
+ EXCEPTION_EVENT_NAME,
7
+ ingestAnalyticsExceptionEvents,
8
+ type DerivedExceptionFields,
9
+ } from "./error-capture.js";
5
10
 
6
11
  export interface AnalyticsScope {
7
12
  userEmail: string;
@@ -351,6 +356,10 @@ export async function recordAnalyticsEvents(
351
356
  }
352
357
 
353
358
  const receivedAt = nowIso();
359
+ const exceptionSources: Array<{
360
+ properties: Record<string, unknown>;
361
+ derived: DerivedExceptionFields;
362
+ }> = [];
354
363
  const rows = events.map((event) => {
355
364
  const properties = event.properties ?? {};
356
365
  const context = event.context ?? {};
@@ -380,6 +389,24 @@ export async function recordAnalyticsEvents(
380
389
  asString((properties as any).distinctId);
381
390
  const userKey = userId || anonymousId;
382
391
  const timestamp = normalizeAnalyticsTimestamp(event.timestamp, receivedAt);
392
+ const sessionId =
393
+ event.sessionId ?? asString((properties as any).sessionId);
394
+
395
+ if (event.event === EXCEPTION_EVENT_NAME) {
396
+ exceptionSources.push({
397
+ properties,
398
+ derived: {
399
+ app,
400
+ template,
401
+ url: parts.url,
402
+ userId,
403
+ anonymousId,
404
+ userKey,
405
+ sessionId,
406
+ timestamp,
407
+ },
408
+ });
409
+ }
383
410
 
384
411
  return {
385
412
  id: id("evt"),
@@ -388,7 +415,7 @@ export async function recordAnalyticsEvents(
388
415
  userId,
389
416
  anonymousId,
390
417
  userKey,
391
- sessionId: event.sessionId ?? asString((properties as any).sessionId),
418
+ sessionId,
392
419
  timestamp,
393
420
  eventDate: eventDateFromTimestamp(timestamp),
394
421
  receivedAt,
@@ -415,6 +442,25 @@ export async function recordAnalyticsEvents(
415
442
  .where(eq(schema.analyticsPublicKeys.id, key.id));
416
443
  }
417
444
 
445
+ // Fork captured exceptions into the dedicated error-capture tables. This is
446
+ // best-effort: a malformed `$exception` payload must never reject the whole
447
+ // analytics ingest (the event is still recorded in analytics_events above,
448
+ // which keeps alerting working).
449
+ if (exceptionSources.length) {
450
+ try {
451
+ await ingestAnalyticsExceptionEvents(
452
+ {
453
+ ownerEmail: key.ownerEmail,
454
+ orgId: key.orgId ?? null,
455
+ publicKeyId: key.id,
456
+ },
457
+ exceptionSources,
458
+ );
459
+ } catch (error) {
460
+ console.warn("[first-party-analytics] Exception ingest failed:", error);
461
+ }
462
+ }
463
+
418
464
  return { accepted: rows.length, keyId: key.id };
419
465
  }
420
466
 
@@ -0,0 +1,494 @@
1
+ /**
2
+ * Aggregated uptime statistics for the monitoring feature.
3
+ *
4
+ * This module turns the raw `monitor_check_results` / `monitor_incidents`
5
+ * history into the numbers a status dashboard needs: current status, uptime %
6
+ * over 24h / 7d / 30d / 90d, a bucketed uptime timeline (green/amber/red/no
7
+ * data), a downsampled response-time series, incident count, and MTBF.
8
+ *
9
+ * Design notes (see the `performance` skill):
10
+ * - Reads are done as a small fixed set of GROUPED, column-projected queries
11
+ * (one per shape), never one query per monitor — no N+1. All aggregation
12
+ * (uptime %, daily buckets, hourly response averages) happens IN SQL so we
13
+ * never pull the full result history across the wire.
14
+ * - Every query is scoped by owner_email + org_id, mirroring
15
+ * server/lib/uptime-monitors.ts. The in-app stats action passes the request
16
+ * user; the public status page passes the PAGE OWNER's scope.
17
+ * - The math (`computeUptimePercents`, `assembleDailyTimeline`, `computeMtbf`,
18
+ * `bucketStatusFromCounts`) is pure and unit-tested (monitor-stats.spec.ts).
19
+ */
20
+ import { and, eq, gte, inArray, isNull, sql } from "drizzle-orm";
21
+
22
+ import { getDb, schema } from "../db/index.js";
23
+ import type { AccessCtx, MonitorStatus } from "./uptime-monitors.js";
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // Types
27
+ // ---------------------------------------------------------------------------
28
+
29
+ /** Coarse health of a single timeline bucket. */
30
+ export type BucketStatus = "up" | "down" | "degraded" | "no-data";
31
+
32
+ export interface UptimeWindows {
33
+ uptime24h: number | null;
34
+ uptime7d: number | null;
35
+ uptime30d: number | null;
36
+ uptime90d: number | null;
37
+ }
38
+
39
+ export interface UptimeBucket {
40
+ /** ISO timestamp for the inclusive start of the bucket. */
41
+ start: string;
42
+ /** ISO timestamp for the exclusive end of the bucket. */
43
+ end: string;
44
+ status: BucketStatus;
45
+ /** Uptime percentage in the bucket (null when there were no checks). */
46
+ uptimePct: number | null;
47
+ total: number;
48
+ downCount: number;
49
+ degradedCount: number;
50
+ }
51
+
52
+ export interface ResponseTimePoint {
53
+ /** ISO timestamp for the start of the bucket. */
54
+ bucketStart: string;
55
+ avg: number | null;
56
+ min: number | null;
57
+ max: number | null;
58
+ count: number;
59
+ }
60
+
61
+ export interface MonitorStats {
62
+ monitorId: string;
63
+ /** Current status from the monitor row (up/down/degraded/…), null if never run. */
64
+ status: MonitorStatus | null;
65
+ lastCheckedAt: string | null;
66
+ lastLatencyMs: number | null;
67
+ windows: UptimeWindows;
68
+ /** Bucketed uptime timeline, oldest → newest (default: 90 daily buckets). */
69
+ timeline: UptimeBucket[];
70
+ /** Downsampled recent response-time series, oldest → newest. */
71
+ responseSeries: ResponseTimePoint[];
72
+ /** Count-weighted average response time across `responseSeries`. */
73
+ avgResponseMs: number | null;
74
+ incidentCount: number;
75
+ /** Mean time between failures over the 90d window, in ms (null if <1 failure). */
76
+ mtbfMs: number | null;
77
+ }
78
+
79
+ export interface MonitorStatsOptions {
80
+ now?: Date;
81
+ /** How many trailing daily buckets to include in `timeline`. Default 90. */
82
+ timelineDays?: number;
83
+ /** Trailing window (hours) for the hourly `responseSeries`. Default 24. */
84
+ responseWindowHours?: number;
85
+ }
86
+
87
+ // ---------------------------------------------------------------------------
88
+ // Constants
89
+ // ---------------------------------------------------------------------------
90
+
91
+ const DAY_MS = 24 * 60 * 60 * 1000;
92
+ const HOUR_MS = 60 * 60 * 1000;
93
+ const DEFAULT_TIMELINE_DAYS = 90;
94
+ const DEFAULT_RESPONSE_WINDOW_HOURS = 24;
95
+ const MAX_TIMELINE_DAYS = 365;
96
+ const MAX_RESPONSE_WINDOW_HOURS = 24 * 90;
97
+
98
+ // ---------------------------------------------------------------------------
99
+ // Pure math (unit-tested)
100
+ // ---------------------------------------------------------------------------
101
+
102
+ export interface UptimeWindowAggregate {
103
+ total24h: number;
104
+ ok24h: number;
105
+ total7d: number;
106
+ ok7d: number;
107
+ total30d: number;
108
+ ok30d: number;
109
+ total90d: number;
110
+ ok90d: number;
111
+ }
112
+
113
+ function pct(ok: number, total: number): number | null {
114
+ return total > 0 ? (ok / total) * 100 : null;
115
+ }
116
+
117
+ export function computeUptimePercents(
118
+ agg: UptimeWindowAggregate,
119
+ ): UptimeWindows {
120
+ return {
121
+ uptime24h: pct(agg.ok24h, agg.total24h),
122
+ uptime7d: pct(agg.ok7d, agg.total7d),
123
+ uptime30d: pct(agg.ok30d, agg.total30d),
124
+ uptime90d: pct(agg.ok90d, agg.total90d),
125
+ };
126
+ }
127
+
128
+ /** Daily aggregate for a single monitor+day, as produced by the DB reader. */
129
+ export interface DailyBucketRow {
130
+ /** UTC calendar day key, `YYYY-MM-DD`. */
131
+ day: string;
132
+ total: number;
133
+ ok: number;
134
+ down: number;
135
+ degraded: number;
136
+ }
137
+
138
+ export function bucketStatusFromCounts(
139
+ total: number,
140
+ down: number,
141
+ degraded: number,
142
+ ): BucketStatus {
143
+ if (total <= 0) return "no-data";
144
+ if (down > 0) return "down";
145
+ if (degraded > 0) return "degraded";
146
+ return "up";
147
+ }
148
+
149
+ function utcDayKey(ms: number): string {
150
+ return new Date(ms).toISOString().slice(0, 10);
151
+ }
152
+
153
+ /**
154
+ * Expand per-day aggregates into a dense, gap-filled trailing timeline of
155
+ * `days` calendar-day buckets (oldest → newest). Days with no checks become
156
+ * `no-data` buckets so the rendered strip stays a fixed width.
157
+ */
158
+ export function assembleDailyTimeline(
159
+ rows: DailyBucketRow[],
160
+ opts: { now?: Date; days?: number } = {},
161
+ ): UptimeBucket[] {
162
+ const now = opts.now ?? new Date();
163
+ const days = Math.max(
164
+ 1,
165
+ Math.min(opts.days ?? DEFAULT_TIMELINE_DAYS, MAX_TIMELINE_DAYS),
166
+ );
167
+ const byDay = new Map<string, DailyBucketRow>();
168
+ for (const row of rows) byDay.set(row.day, row);
169
+
170
+ const nowMs = now.getTime();
171
+ const buckets: UptimeBucket[] = [];
172
+ for (let i = days - 1; i >= 0; i--) {
173
+ const dayMs = nowMs - i * DAY_MS;
174
+ const dayKey = utcDayKey(dayMs);
175
+ const startMs = Date.parse(`${dayKey}T00:00:00.000Z`);
176
+ const nextDayStartMs = startMs + DAY_MS;
177
+ // The most recent bucket ends "now", not at the end of the calendar day.
178
+ const endMs = i === 0 ? Math.max(nowMs, startMs) : nextDayStartMs;
179
+ const row = byDay.get(dayKey);
180
+ const total = row?.total ?? 0;
181
+ const down = row?.down ?? 0;
182
+ const degraded = row?.degraded ?? 0;
183
+ const ok = row?.ok ?? 0;
184
+ buckets.push({
185
+ start: new Date(startMs).toISOString(),
186
+ end: new Date(endMs).toISOString(),
187
+ status: bucketStatusFromCounts(total, down, degraded),
188
+ uptimePct: pct(ok, total),
189
+ total,
190
+ downCount: down,
191
+ degradedCount: degraded,
192
+ });
193
+ }
194
+ return buckets;
195
+ }
196
+
197
+ export interface IncidentWindowRow {
198
+ startedAt: string;
199
+ resolvedAt: string | null;
200
+ }
201
+
202
+ /**
203
+ * Mean time between failures over a window, in ms. Defined as operational time
204
+ * (window length minus clamped downtime) divided by the number of failures.
205
+ * Returns null when there were no failures in the window.
206
+ */
207
+ export function computeMtbf(
208
+ incidents: IncidentWindowRow[],
209
+ opts: { windowStartMs: number; nowMs: number },
210
+ ): number | null {
211
+ const failures = incidents.length;
212
+ if (failures === 0) return null;
213
+ const { windowStartMs, nowMs } = opts;
214
+ const windowMs = Math.max(0, nowMs - windowStartMs);
215
+ let downtime = 0;
216
+ for (const incident of incidents) {
217
+ const startedMs = Date.parse(incident.startedAt);
218
+ if (!Number.isFinite(startedMs)) continue;
219
+ const resolvedMs = incident.resolvedAt
220
+ ? Date.parse(incident.resolvedAt)
221
+ : nowMs;
222
+ const start = Math.max(startedMs, windowStartMs);
223
+ const end = Math.min(
224
+ Number.isFinite(resolvedMs) ? resolvedMs : nowMs,
225
+ nowMs,
226
+ );
227
+ if (end > start) downtime += end - start;
228
+ }
229
+ const operational = Math.max(0, windowMs - downtime);
230
+ return operational / failures;
231
+ }
232
+
233
+ export function averageResponse(series: ResponseTimePoint[]): number | null {
234
+ let weighted = 0;
235
+ let count = 0;
236
+ for (const point of series) {
237
+ if (point.avg == null || point.count <= 0) continue;
238
+ weighted += point.avg * point.count;
239
+ count += point.count;
240
+ }
241
+ return count > 0 ? weighted / count : null;
242
+ }
243
+
244
+ // ---------------------------------------------------------------------------
245
+ // Owner scoping (mirrors uptime-monitors.ts)
246
+ // ---------------------------------------------------------------------------
247
+
248
+ function ownerScope(table: { ownerEmail: any; orgId: any }, ctx: AccessCtx) {
249
+ return and(
250
+ sql`lower(${table.ownerEmail}) = ${ctx.email.toLowerCase()}`,
251
+ ctx.orgId ? eq(table.orgId, ctx.orgId) : isNull(table.orgId),
252
+ );
253
+ }
254
+
255
+ /** All monitor ids owned by `ctx` (used when a caller wants stats for "all"). */
256
+ export async function listOwnedMonitorIds(ctx: AccessCtx): Promise<string[]> {
257
+ const db = getDb() as any;
258
+ const rows = await db
259
+ .select({ id: schema.monitors.id })
260
+ .from(schema.monitors)
261
+ .where(ownerScope(schema.monitors, ctx));
262
+ return rows.map((row: any) => row.id as string);
263
+ }
264
+
265
+ // ---------------------------------------------------------------------------
266
+ // DB reader
267
+ // ---------------------------------------------------------------------------
268
+
269
+ /**
270
+ * Compute aggregated stats for the given monitor ids, scoped to `ctx`. Returns
271
+ * a Map keyed by monitor id. Ids the caller doesn't own are simply absent.
272
+ */
273
+ export async function getMonitorStats(
274
+ ctx: AccessCtx,
275
+ monitorIds: string[],
276
+ opts: MonitorStatsOptions = {},
277
+ ): Promise<Map<string, MonitorStats>> {
278
+ const result = new Map<string, MonitorStats>();
279
+ const ids = Array.from(new Set(monitorIds.filter(Boolean)));
280
+ if (ids.length === 0) return result;
281
+
282
+ const db = getDb() as any;
283
+ const now = opts.now ?? new Date();
284
+ const nowMs = now.getTime();
285
+ const timelineDays = Math.max(
286
+ 1,
287
+ Math.min(opts.timelineDays ?? DEFAULT_TIMELINE_DAYS, MAX_TIMELINE_DAYS),
288
+ );
289
+ const responseWindowHours = Math.max(
290
+ 1,
291
+ Math.min(
292
+ opts.responseWindowHours ?? DEFAULT_RESPONSE_WINDOW_HOURS,
293
+ MAX_RESPONSE_WINDOW_HOURS,
294
+ ),
295
+ );
296
+
297
+ const since24h = new Date(nowMs - 24 * HOUR_MS).toISOString();
298
+ const since7d = new Date(nowMs - 7 * DAY_MS).toISOString();
299
+ const since30d = new Date(nowMs - 30 * DAY_MS).toISOString();
300
+ const since90d = new Date(nowMs - 90 * DAY_MS).toISOString();
301
+ const sinceTimeline = new Date(
302
+ nowMs - (timelineDays - 1) * DAY_MS - DAY_MS,
303
+ ).toISOString();
304
+ const sinceResponse = new Date(
305
+ nowMs - responseWindowHours * HOUR_MS,
306
+ ).toISOString();
307
+
308
+ const monitorsTable = schema.monitors;
309
+ const resultsTable = schema.monitorCheckResults;
310
+ const incidentsTable = schema.monitorIncidents;
311
+
312
+ const [monitorRows, windowRows, dailyRows, responseRows, incidentRows] =
313
+ await Promise.all([
314
+ db
315
+ .select({
316
+ id: monitorsTable.id,
317
+ lastStatus: monitorsTable.lastStatus,
318
+ lastCheckedAt: monitorsTable.lastCheckedAt,
319
+ lastLatencyMs: monitorsTable.lastLatencyMs,
320
+ })
321
+ .from(monitorsTable)
322
+ .where(
323
+ and(ownerScope(monitorsTable, ctx), inArray(monitorsTable.id, ids)),
324
+ ),
325
+ db
326
+ .select({
327
+ monitorId: resultsTable.monitorId,
328
+ total24h: sql<number>`sum(case when ${resultsTable.checkedAt} >= ${since24h} then 1 else 0 end)`,
329
+ ok24h: sql<number>`sum(case when ${resultsTable.checkedAt} >= ${since24h} and ${resultsTable.ok} then 1 else 0 end)`,
330
+ total7d: sql<number>`sum(case when ${resultsTable.checkedAt} >= ${since7d} then 1 else 0 end)`,
331
+ ok7d: sql<number>`sum(case when ${resultsTable.checkedAt} >= ${since7d} and ${resultsTable.ok} then 1 else 0 end)`,
332
+ total30d: sql<number>`sum(case when ${resultsTable.checkedAt} >= ${since30d} then 1 else 0 end)`,
333
+ ok30d: sql<number>`sum(case when ${resultsTable.checkedAt} >= ${since30d} and ${resultsTable.ok} then 1 else 0 end)`,
334
+ total90d: sql<number>`sum(case when ${resultsTable.checkedAt} >= ${since90d} then 1 else 0 end)`,
335
+ ok90d: sql<number>`sum(case when ${resultsTable.checkedAt} >= ${since90d} and ${resultsTable.ok} then 1 else 0 end)`,
336
+ })
337
+ .from(resultsTable)
338
+ .where(
339
+ and(
340
+ ownerScope(resultsTable, ctx),
341
+ inArray(resultsTable.monitorId, ids),
342
+ gte(resultsTable.checkedAt, since90d),
343
+ ),
344
+ )
345
+ .groupBy(resultsTable.monitorId),
346
+ db
347
+ .select({
348
+ monitorId: resultsTable.monitorId,
349
+ day: sql<string>`substr(${resultsTable.checkedAt}, 1, 10)`,
350
+ total: sql<number>`count(*)`,
351
+ ok: sql<number>`sum(case when ${resultsTable.ok} then 1 else 0 end)`,
352
+ down: sql<number>`sum(case when ${resultsTable.status} in ('down','error') then 1 else 0 end)`,
353
+ degraded: sql<number>`sum(case when ${resultsTable.status} = 'degraded' then 1 else 0 end)`,
354
+ })
355
+ .from(resultsTable)
356
+ .where(
357
+ and(
358
+ ownerScope(resultsTable, ctx),
359
+ inArray(resultsTable.monitorId, ids),
360
+ gte(resultsTable.checkedAt, sinceTimeline),
361
+ ),
362
+ )
363
+ .groupBy(
364
+ resultsTable.monitorId,
365
+ sql`substr(${resultsTable.checkedAt}, 1, 10)`,
366
+ ),
367
+ db
368
+ .select({
369
+ monitorId: resultsTable.monitorId,
370
+ hour: sql<string>`substr(${resultsTable.checkedAt}, 1, 13)`,
371
+ avg: sql<number>`avg(${resultsTable.latencyMs})`,
372
+ min: sql<number>`min(${resultsTable.latencyMs})`,
373
+ max: sql<number>`max(${resultsTable.latencyMs})`,
374
+ count: sql<number>`count(${resultsTable.latencyMs})`,
375
+ })
376
+ .from(resultsTable)
377
+ .where(
378
+ and(
379
+ ownerScope(resultsTable, ctx),
380
+ inArray(resultsTable.monitorId, ids),
381
+ gte(resultsTable.checkedAt, sinceResponse),
382
+ sql`${resultsTable.latencyMs} is not null`,
383
+ ),
384
+ )
385
+ .groupBy(
386
+ resultsTable.monitorId,
387
+ sql`substr(${resultsTable.checkedAt}, 1, 13)`,
388
+ ),
389
+ db
390
+ .select({
391
+ monitorId: incidentsTable.monitorId,
392
+ startedAt: incidentsTable.startedAt,
393
+ resolvedAt: incidentsTable.resolvedAt,
394
+ })
395
+ .from(incidentsTable)
396
+ .where(
397
+ and(
398
+ ownerScope(incidentsTable, ctx),
399
+ inArray(incidentsTable.monitorId, ids),
400
+ gte(incidentsTable.startedAt, since90d),
401
+ ),
402
+ ),
403
+ ]);
404
+
405
+ const windowById = new Map<string, UptimeWindowAggregate>();
406
+ for (const row of windowRows) {
407
+ windowById.set(row.monitorId, {
408
+ total24h: Number(row.total24h ?? 0),
409
+ ok24h: Number(row.ok24h ?? 0),
410
+ total7d: Number(row.total7d ?? 0),
411
+ ok7d: Number(row.ok7d ?? 0),
412
+ total30d: Number(row.total30d ?? 0),
413
+ ok30d: Number(row.ok30d ?? 0),
414
+ total90d: Number(row.total90d ?? 0),
415
+ ok90d: Number(row.ok90d ?? 0),
416
+ });
417
+ }
418
+
419
+ const dailyById = new Map<string, DailyBucketRow[]>();
420
+ for (const row of dailyRows) {
421
+ const list = dailyById.get(row.monitorId) ?? [];
422
+ list.push({
423
+ day: String(row.day),
424
+ total: Number(row.total ?? 0),
425
+ ok: Number(row.ok ?? 0),
426
+ down: Number(row.down ?? 0),
427
+ degraded: Number(row.degraded ?? 0),
428
+ });
429
+ dailyById.set(row.monitorId, list);
430
+ }
431
+
432
+ const responseById = new Map<string, ResponseTimePoint[]>();
433
+ for (const row of responseRows) {
434
+ const list = responseById.get(row.monitorId) ?? [];
435
+ const hourKey = String(row.hour);
436
+ list.push({
437
+ bucketStart: new Date(`${hourKey}:00:00.000Z`).toISOString(),
438
+ avg: row.avg == null ? null : Number(row.avg),
439
+ min: row.min == null ? null : Number(row.min),
440
+ max: row.max == null ? null : Number(row.max),
441
+ count: Number(row.count ?? 0),
442
+ });
443
+ responseById.set(row.monitorId, list);
444
+ }
445
+
446
+ const incidentsById = new Map<string, IncidentWindowRow[]>();
447
+ for (const row of incidentRows) {
448
+ const list = incidentsById.get(row.monitorId) ?? [];
449
+ list.push({ startedAt: row.startedAt, resolvedAt: row.resolvedAt ?? null });
450
+ incidentsById.set(row.monitorId, list);
451
+ }
452
+
453
+ const since90dMs = nowMs - 90 * DAY_MS;
454
+
455
+ for (const monitor of monitorRows) {
456
+ const id = monitor.id as string;
457
+ const windowAgg = windowById.get(id) ?? {
458
+ total24h: 0,
459
+ ok24h: 0,
460
+ total7d: 0,
461
+ ok7d: 0,
462
+ total30d: 0,
463
+ ok30d: 0,
464
+ total90d: 0,
465
+ ok90d: 0,
466
+ };
467
+ const timeline = assembleDailyTimeline(dailyById.get(id) ?? [], {
468
+ now,
469
+ days: timelineDays,
470
+ });
471
+ const responseSeries = (responseById.get(id) ?? []).sort((a, b) =>
472
+ a.bucketStart < b.bucketStart
473
+ ? -1
474
+ : a.bucketStart > b.bucketStart
475
+ ? 1
476
+ : 0,
477
+ );
478
+ const incidents = incidentsById.get(id) ?? [];
479
+ result.set(id, {
480
+ monitorId: id,
481
+ status: (monitor.lastStatus ?? null) as MonitorStatus | null,
482
+ lastCheckedAt: monitor.lastCheckedAt ?? null,
483
+ lastLatencyMs: monitor.lastLatencyMs ?? null,
484
+ windows: computeUptimePercents(windowAgg),
485
+ timeline,
486
+ responseSeries,
487
+ avgResponseMs: averageResponse(responseSeries),
488
+ incidentCount: incidents.length,
489
+ mtbfMs: computeMtbf(incidents, { windowStartMs: since90dMs, nowMs }),
490
+ });
491
+ }
492
+
493
+ return result;
494
+ }