@agent-native/core 0.91.2 → 0.92.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +17 -0
  3. package/corpus/core/docs/content/faq.mdx +17 -0
  4. package/corpus/core/docs/content/notifications.mdx +12 -6
  5. package/corpus/core/package.json +1 -1
  6. package/corpus/core/src/agent/production-agent.ts +140 -46
  7. package/corpus/core/src/agent/tool-search.ts +71 -4
  8. package/corpus/core/src/cli/index.ts +20 -0
  9. package/corpus/core/src/cli/upgrade.ts +798 -0
  10. package/corpus/core/src/client/AssistantChat.tsx +1 -1
  11. package/corpus/core/src/client/HighlightedCodeBlock.tsx +20 -3
  12. package/corpus/core/src/client/agent-chat-adapter.ts +31 -6
  13. package/corpus/core/src/client/chat/message-components.tsx +85 -8
  14. package/corpus/core/src/client/chat/tool-call-display.tsx +355 -144
  15. package/corpus/core/src/client/code-agent-chat-adapter.ts +2 -9
  16. package/corpus/core/src/client/session-replay.ts +3 -97
  17. package/corpus/core/src/client/settings/SettingsPanel.tsx +533 -389
  18. package/corpus/core/src/client/settings/SettingsSection.tsx +154 -17
  19. package/corpus/core/src/client/sse-event-processor.ts +29 -2
  20. package/corpus/core/src/extensions/actions.ts +113 -5
  21. package/corpus/core/src/notifications/channels.ts +88 -25
  22. package/corpus/core/src/notifications/registry.ts +17 -3
  23. package/corpus/core/src/notifications/types.ts +4 -1
  24. package/corpus/core/src/server/agent-chat-plugin.ts +7 -3
  25. package/corpus/core/src/server/request-context.ts +7 -0
  26. package/corpus/core/src/styles/agent-conversation.css +1 -1
  27. package/corpus/core/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +20 -2
  28. package/corpus/core/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  29. package/corpus/core/src/templates/default/AGENTS.md +10 -3
  30. package/corpus/core/src/templates/default/package.json +2 -1
  31. package/corpus/core/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  32. package/corpus/core/src/templates/headless/AGENTS.md +9 -3
  33. package/corpus/core/src/templates/headless/package.json +1 -0
  34. package/corpus/core/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +20 -2
  35. package/corpus/core/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  36. package/corpus/core/src/templates/workspace-root/AGENTS.md +10 -4
  37. package/corpus/core/src/templates/workspace-root/package.json +1 -0
  38. package/corpus/templates/analytics/.agents/skills/self-modifying-code/SKILL.md +20 -2
  39. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +5 -6
  40. package/corpus/templates/analytics/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  41. package/corpus/templates/analytics/AGENTS.md +7 -5
  42. package/corpus/templates/analytics/actions/save-monitor.ts +14 -0
  43. package/corpus/templates/analytics/app/components/dashboard/DataTable.tsx +41 -6
  44. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +122 -11
  45. package/corpus/templates/analytics/app/pages/monitoring/MonitoringPage.tsx +8 -2
  46. package/corpus/templates/analytics/app/pages/monitoring/uptime/MonitorFormPage.tsx +151 -32
  47. package/corpus/templates/analytics/app/pages/monitoring/uptime/i18n.ts +17 -2
  48. package/corpus/templates/analytics/app/pages/monitoring/uptime/types.ts +4 -0
  49. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +54 -190
  50. package/corpus/templates/analytics/changelog/2026-07-08-dashboard-tables-keep-their-size-while-loading.md +6 -0
  51. package/corpus/templates/analytics/changelog/2026-07-08-monitoring-uptime-and-errors-tabs-now-clearly-show-which-view-is-active.md +6 -0
  52. package/corpus/templates/analytics/changelog/2026-07-08-session-replay-uses-stock-rrweb-sizing-again-so-recorded.md +6 -0
  53. package/corpus/templates/analytics/changelog/2026-07-08-sidebar-dashboard-menus-now-open-above-the-dashboard-canvas.md +6 -0
  54. package/corpus/templates/analytics/changelog/2026-07-08-uptime-alerting-clarifies-in-app-inbox-vs-email-and-lets-you-paste-slack-or-webhook-urls-per-monitor.md +6 -0
  55. package/corpus/templates/analytics/docs/uptime-monitoring.md +13 -11
  56. package/corpus/templates/analytics/server/db/schema-monitoring.ts +4 -0
  57. package/corpus/templates/analytics/server/lib/uptime-monitors.ts +60 -12
  58. package/corpus/templates/analytics/server/plugins/db.ts +10 -0
  59. package/corpus/templates/assets/.agents/skills/self-modifying-code/SKILL.md +20 -2
  60. package/corpus/templates/assets/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  61. package/corpus/templates/brain/.agents/skills/self-modifying-code/SKILL.md +20 -2
  62. package/corpus/templates/brain/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  63. package/corpus/templates/calendar/.agents/skills/self-modifying-code/SKILL.md +20 -2
  64. package/corpus/templates/calendar/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  65. package/corpus/templates/chat/.agents/skills/self-modifying-code/SKILL.md +20 -2
  66. package/corpus/templates/chat/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  67. package/corpus/templates/clips/.agents/skills/self-modifying-code/SKILL.md +20 -2
  68. package/corpus/templates/clips/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  69. package/corpus/templates/clips/changelog/2026-07-08-desktop-meeting-notes-stay-active-from-tray-and-shortcut-controls.md +6 -0
  70. package/corpus/templates/clips/changelog/2026-07-08-desktop-meeting-popovers-can-start-notes-and-join-the-meetin.md +6 -0
  71. package/corpus/templates/clips/changelog/2026-07-08-meeting-notes-keep-their-recording-state-when-the-desktop.md +6 -0
  72. package/corpus/templates/clips/changelog/2026-07-08-meeting-reminder-dropdowns-now-expand-fully-instead-of-being.md +6 -0
  73. package/corpus/templates/clips/desktop/src/app.tsx +123 -3
  74. package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +15 -1
  75. package/corpus/templates/clips/desktop/src/overlays/meeting-notification.tsx +23 -0
  76. package/corpus/templates/clips/desktop/src/styles.css +23 -1
  77. package/corpus/templates/clips/desktop/src-tauri/src/adhoc_meetings_watcher.rs +1 -3
  78. package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +40 -6
  79. package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +4 -2
  80. package/corpus/templates/clips/desktop/src-tauri/src/meetings_watcher.rs +3 -5
  81. package/corpus/templates/clips/desktop/src-tauri/src/native_screen.rs +23 -19
  82. package/corpus/templates/clips/desktop/src-tauri/src/shortcuts.rs +3 -3
  83. package/corpus/templates/clips/desktop/src-tauri/src/silence_detector.rs +0 -1
  84. package/corpus/templates/clips/desktop/src-tauri/src/state.rs +5 -0
  85. package/corpus/templates/clips/desktop/src-tauri/src/tray.rs +18 -6
  86. package/corpus/templates/content/.agents/skills/self-modifying-code/SKILL.md +20 -2
  87. package/corpus/templates/content/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  88. package/corpus/templates/content/actions/_batch-utils.ts +8 -0
  89. package/corpus/templates/content/actions/_builder-cms-read-client.ts +7 -5
  90. package/corpus/templates/content/actions/_database-source-utils.ts +776 -319
  91. package/corpus/templates/content/actions/_database-utils.ts +6 -1
  92. package/corpus/templates/content/actions/_property-utils.ts +168 -0
  93. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +19 -12
  94. package/corpus/templates/content/actions/bind-content-database-source-field.ts +19 -12
  95. package/corpus/templates/content/actions/delete-document.ts +9 -13
  96. package/corpus/templates/content/actions/refresh-content-database-source.ts +5 -5
  97. package/corpus/templates/content/app/components/editor/body-hydration.ts +35 -11
  98. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +47 -5
  99. package/corpus/templates/content/app/hooks/use-documents.ts +8 -2
  100. package/corpus/templates/content/changelog/2026-07-08-builder-source-sync-now-completes-reliably-on-hosted-databases.md +6 -0
  101. package/corpus/templates/content/changelog/2026-07-08-fixed-builder-database-sync-so-large-sources-continue-past-t.md +6 -0
  102. package/corpus/templates/content/server/plugins/db.ts +19 -0
  103. package/corpus/templates/design/.agents/skills/self-modifying-code/SKILL.md +20 -2
  104. package/corpus/templates/design/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  105. package/corpus/templates/dispatch/.agents/skills/self-modifying-code/SKILL.md +20 -2
  106. package/corpus/templates/dispatch/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  107. package/corpus/templates/forms/.agents/skills/self-modifying-code/SKILL.md +20 -2
  108. package/corpus/templates/forms/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  109. package/corpus/templates/macros/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  110. package/corpus/templates/mail/.agents/skills/self-modifying-code/SKILL.md +20 -2
  111. package/corpus/templates/mail/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  112. package/corpus/templates/plan/.agents/skills/self-modifying-code/SKILL.md +20 -2
  113. package/corpus/templates/plan/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  114. package/corpus/templates/slides/.agents/skills/self-modifying-code/SKILL.md +20 -2
  115. package/corpus/templates/slides/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  116. package/corpus/templates/videos/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  117. package/corpus/templates/voice/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  118. package/dist/agent/production-agent.d.ts +5 -1
  119. package/dist/agent/production-agent.d.ts.map +1 -1
  120. package/dist/agent/production-agent.js +112 -42
  121. package/dist/agent/production-agent.js.map +1 -1
  122. package/dist/agent/tool-search.d.ts +2 -0
  123. package/dist/agent/tool-search.d.ts.map +1 -1
  124. package/dist/agent/tool-search.js +60 -4
  125. package/dist/agent/tool-search.js.map +1 -1
  126. package/dist/cli/index.js +19 -0
  127. package/dist/cli/index.js.map +1 -1
  128. package/dist/cli/upgrade.d.ts +100 -0
  129. package/dist/cli/upgrade.d.ts.map +1 -0
  130. package/dist/cli/upgrade.js +678 -0
  131. package/dist/cli/upgrade.js.map +1 -0
  132. package/dist/client/AssistantChat.js +1 -1
  133. package/dist/client/AssistantChat.js.map +1 -1
  134. package/dist/client/HighlightedCodeBlock.d.ts +2 -0
  135. package/dist/client/HighlightedCodeBlock.d.ts.map +1 -1
  136. package/dist/client/HighlightedCodeBlock.js +10 -1
  137. package/dist/client/HighlightedCodeBlock.js.map +1 -1
  138. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  139. package/dist/client/agent-chat-adapter.js +32 -4
  140. package/dist/client/agent-chat-adapter.js.map +1 -1
  141. package/dist/client/chat/message-components.d.ts.map +1 -1
  142. package/dist/client/chat/message-components.js +61 -8
  143. package/dist/client/chat/message-components.js.map +1 -1
  144. package/dist/client/chat/tool-call-display.d.ts +12 -0
  145. package/dist/client/chat/tool-call-display.d.ts.map +1 -1
  146. package/dist/client/chat/tool-call-display.js +136 -42
  147. package/dist/client/chat/tool-call-display.js.map +1 -1
  148. package/dist/client/code-agent-chat-adapter.js +2 -9
  149. package/dist/client/code-agent-chat-adapter.js.map +1 -1
  150. package/dist/client/session-replay.d.ts +0 -8
  151. package/dist/client/session-replay.d.ts.map +1 -1
  152. package/dist/client/session-replay.js +3 -80
  153. package/dist/client/session-replay.js.map +1 -1
  154. package/dist/client/settings/SettingsPanel.d.ts.map +1 -1
  155. package/dist/client/settings/SettingsPanel.js +52 -19
  156. package/dist/client/settings/SettingsPanel.js.map +1 -1
  157. package/dist/client/settings/SettingsSection.d.ts +19 -4
  158. package/dist/client/settings/SettingsSection.d.ts.map +1 -1
  159. package/dist/client/settings/SettingsSection.js +33 -4
  160. package/dist/client/settings/SettingsSection.js.map +1 -1
  161. package/dist/client/sse-event-processor.d.ts +9 -0
  162. package/dist/client/sse-event-processor.d.ts.map +1 -1
  163. package/dist/client/sse-event-processor.js +20 -2
  164. package/dist/client/sse-event-processor.js.map +1 -1
  165. package/dist/collab/routes.d.ts +1 -1
  166. package/dist/extensions/actions.d.ts.map +1 -1
  167. package/dist/extensions/actions.js +85 -5
  168. package/dist/extensions/actions.js.map +1 -1
  169. package/dist/notifications/channels.d.ts +8 -3
  170. package/dist/notifications/channels.d.ts.map +1 -1
  171. package/dist/notifications/channels.js +61 -22
  172. package/dist/notifications/channels.js.map +1 -1
  173. package/dist/notifications/registry.d.ts.map +1 -1
  174. package/dist/notifications/registry.js +14 -3
  175. package/dist/notifications/registry.js.map +1 -1
  176. package/dist/notifications/routes.d.ts +1 -1
  177. package/dist/notifications/types.d.ts +4 -1
  178. package/dist/notifications/types.d.ts.map +1 -1
  179. package/dist/notifications/types.js.map +1 -1
  180. package/dist/observability/routes.d.ts +1 -1
  181. package/dist/resources/handlers.d.ts +1 -1
  182. package/dist/server/agent-chat-plugin.d.ts +5 -1
  183. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  184. package/dist/server/agent-chat-plugin.js +2 -2
  185. package/dist/server/agent-chat-plugin.js.map +1 -1
  186. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  187. package/dist/server/request-context.d.ts +7 -0
  188. package/dist/server/request-context.d.ts.map +1 -1
  189. package/dist/server/request-context.js.map +1 -1
  190. package/dist/styles/agent-conversation.css +1 -1
  191. package/dist/templates/default/.agents/skills/self-modifying-code/SKILL.md +20 -2
  192. package/dist/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  193. package/dist/templates/default/AGENTS.md +10 -3
  194. package/dist/templates/default/package.json +2 -1
  195. package/dist/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  196. package/dist/templates/headless/AGENTS.md +9 -3
  197. package/dist/templates/headless/package.json +1 -0
  198. package/dist/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +20 -2
  199. package/dist/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  200. package/dist/templates/workspace-root/AGENTS.md +10 -4
  201. package/dist/templates/workspace-root/package.json +1 -0
  202. package/docs/content/faq.mdx +17 -0
  203. package/docs/content/notifications.mdx +12 -6
  204. package/package.json +2 -2
  205. package/src/templates/default/.agents/skills/self-modifying-code/SKILL.md +20 -2
  206. package/src/templates/default/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  207. package/src/templates/default/AGENTS.md +10 -3
  208. package/src/templates/default/package.json +2 -1
  209. package/src/templates/headless/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  210. package/src/templates/headless/AGENTS.md +9 -3
  211. package/src/templates/headless/package.json +1 -0
  212. package/src/templates/workspace-core/.agents/skills/self-modifying-code/SKILL.md +20 -2
  213. package/src/templates/workspace-core/.agents/skills/upgrade-agent-native/SKILL.md +87 -0
  214. package/src/templates/workspace-root/AGENTS.md +10 -4
  215. package/src/templates/workspace-root/package.json +1 -0
  216. package/corpus/templates/analytics/changelog/2026-07-08-session-replay-clamps-ultra-wide-viewports-while-keeping-the.md +0 -6
@@ -117,7 +117,7 @@ const MESSAGES = {
117
117
  sectionResponseChecksHint:
118
118
  "Choose which responses count as healthy and add body, header, or latency assertions.",
119
119
  sectionAlerting: "Alerting",
120
- sectionAlertingHint: "Pick the severity and where alerts are delivered.",
120
+ sectionAlertingHint: "Choose severity and where alerts go.",
121
121
  sectionAdvanced: "Advanced",
122
122
  sectionAdvancedHint:
123
123
  "Request method, timeout, cooldown, redirects, and pause.",
@@ -165,12 +165,24 @@ const MESSAGES = {
165
165
  severityWarning: "Warning",
166
166
  severityCritical: "Critical",
167
167
  fieldChannels: "Notify via",
168
- channelInbox: "Inbox",
168
+ channelInbox: "In-app inbox",
169
+ channelInboxHint: "Bell notifications inside Analytics.",
169
170
  channelEmail: "Email",
171
+ channelEmailHint: "Send to specific addresses.",
170
172
  channelSlack: "Slack",
173
+ channelSlackHint: "Post to a Slack incoming webhook.",
171
174
  channelWebhook: "Webhook",
175
+ channelWebhookHint: "POST JSON to any HTTPS endpoint.",
172
176
  fieldEmailRecipients: "Email recipients",
173
177
  fieldEmailRecipientsPlaceholder: "alerts@example.com, oncall@example.com",
178
+ fieldSlackWebhookUrl: "Slack webhook URL",
179
+ fieldSlackWebhookUrlPlaceholder: "https://hooks.slack.com/services/T…/B…/…",
180
+ fieldSlackWebhookUrlHint:
181
+ "Paste a Slack Incoming Webhook URL. Leave blank to use the workspace default, if configured.",
182
+ fieldWebhookUrl: "Webhook URL",
183
+ fieldWebhookUrlPlaceholder: "https://example.com/hooks/uptime",
184
+ fieldWebhookUrlHint:
185
+ "Receives a JSON POST with severity, title, and body. Leave blank to use the workspace default, if configured.",
174
186
  fieldCooldown: "Alert cooldown (minutes)",
175
187
  fieldEnabled: "Enabled",
176
188
  fieldEnabledHint: "Run this monitor on its schedule.",
@@ -188,6 +200,9 @@ const MESSAGES = {
188
200
  urlRequired: "Enter an absolute http(s) URL.",
189
201
  urlInvalid: "Enter a valid http(s) URL (e.g. https://example.com).",
190
202
  channelRequired: "Select at least one alert channel.",
203
+ emailRecipientsRequired: "Add at least one email recipient.",
204
+ slackWebhookUrlInvalid: "Enter a valid Slack webhook http(s) URL.",
205
+ webhookUrlInvalid: "Enter a valid webhook http(s) URL.",
191
206
  classRequired: "Select at least one status class.",
192
207
  codesRequired: "Enter at least one status code.",
193
208
  saved: "Monitor saved.",
@@ -61,6 +61,8 @@ export interface MonitorSummary {
61
61
  severity: MonitorSeverity;
62
62
  channels: string[];
63
63
  emailRecipients: string[];
64
+ slackWebhookUrl: string | null;
65
+ webhookUrl: string | null;
64
66
  cooldownMinutes: number;
65
67
  enabled: boolean;
66
68
  lastStatus: MonitorStatus | null;
@@ -156,6 +158,8 @@ export interface SaveMonitorInput {
156
158
  severity?: MonitorSeverity;
157
159
  channels?: string[];
158
160
  emailRecipients?: string[];
161
+ slackWebhookUrl?: string | null;
162
+ webhookUrl?: string | null;
159
163
  cooldownMinutes?: number;
160
164
  enabled?: boolean;
161
165
  }
@@ -156,6 +156,8 @@ type ReplayViewportDimensions = {
156
156
 
157
157
  const DEFAULT_PLAYER_WIDTH = 1024;
158
158
  const DEFAULT_PLAYER_HEIGHT = 640;
159
+ const MIN_REPLAY_DISPLAY_ASPECT_RATIO = 0.45;
160
+ const MAX_REPLAY_DISPLAY_ASPECT_RATIO = 3;
159
161
  const DEFAULT_SPEED = 2;
160
162
  const SPEED_OPTIONS = [0.5, 1, 2, 4, 8];
161
163
  const SKIP_STEP_MS = 5000;
@@ -166,15 +168,12 @@ const REPLAY_CHUNK_UNAVAILABLE_MESSAGE = "Session replay chunk is unavailable";
166
168
  const DEFAULT_DEVTOOLS_HEIGHT = 220;
167
169
  const MIN_STAGE_HEIGHT_PX = 240;
168
170
  const SCRUBBER_MARKER_LIMIT = 500;
169
- /** Reject Meta/resize frames that produce the ultra-wide ribbon letterbox.
170
- * True 21:9 ultrawide is ~2.33; multi-monitor spans and corrupt frames are
171
- * usually well above that and collapse the stage after fit-to-scale. */
172
- const MIN_REPLAY_VIEWPORT_WIDTH = 320;
173
- const MIN_REPLAY_VIEWPORT_HEIGHT = 240;
174
- const MAX_REPLAY_ASPECT_RATIO = 2.45;
175
- const MIN_REPLAY_ASPECT_RATIO = 0.5;
176
171
  const TIMELINE_MARKER_LIMIT = 300;
177
172
  const TIMELINE_FOLLOW_PAUSE_MS = 4000;
173
+ /** Toast/snackbar noise only — keep insertStyleRules minimal like builder-internal. */
174
+ const SUPPRESS_OVERLAYS_CSS = [
175
+ "[data-radix-popper-content-wrapper], .Toastify, [class*='toast'], [class*='Toast'], [class*='Snackbar'] { display: none !important; }",
176
+ ];
178
177
  const SESSION_REPLAY_CONSOLE_EVENT_TAG = "agent-native.console";
179
178
  const SESSION_REPLAY_NETWORK_EVENT_TAG = "agent-native.network";
180
179
 
@@ -506,10 +505,9 @@ function ReplayPlayer({
506
505
  const scrubbingRef = useRef(false);
507
506
  const scrubResumePlayingRef = useRef(false);
508
507
 
509
- const playerWidth =
510
- streamedDims?.width ?? initialDims?.width ?? DEFAULT_PLAYER_WIDTH;
511
- const playerHeight =
512
- streamedDims?.height ?? initialDims?.height ?? DEFAULT_PLAYER_HEIGHT;
508
+ const displayDims = clampReplayDisplayDimensions(streamedDims ?? initialDims);
509
+ const playerWidth = displayDims?.width ?? DEFAULT_PLAYER_WIDTH;
510
+ const playerHeight = displayDims?.height ?? DEFAULT_PLAYER_HEIGHT;
513
511
  const skipRanges = useMemo(() => buildIdleSkipRanges(events), [events]);
514
512
  const skipRangesRef = useLiveRef(skipRanges);
515
513
  const skipInactiveRef = useLiveRef(skipInactive);
@@ -695,33 +693,21 @@ function ReplayPlayer({
695
693
  if (cancelled || !stageRootRef.current) return;
696
694
 
697
695
  stageRootRef.current.innerHTML = "";
698
- const frameDims = replayViewportDimensions(replayEvents);
699
- const playbackEvents = sanitizeReplayViewportEvents(
700
- replayEvents,
701
- frameDims ?? {
702
- width: DEFAULT_PLAYER_WIDTH,
703
- height: DEFAULT_PLAYER_HEIGHT,
704
- },
705
- );
706
- setStreamedDims(frameDims);
707
- localReplayer = new Replayer(playbackEvents as any[], {
696
+ // Match builder-internal: pass events through untouched and let rrweb own
697
+ // iframe sizing via Meta / ViewportResize. Only use dimensions for CSS
698
+ // fit-to-stage scaling of the outer wrapper — never rewrite Meta or force
699
+ // iframe width/height (that mismatches the FullSnapshot DOM and blanks
700
+ // the stage).
701
+ setStreamedDims(replayViewportDimensions(replayEvents));
702
+ localReplayer = new Replayer(replayEvents as any[], {
708
703
  root: stageRootRef.current,
709
704
  speed: speedRef.current,
710
705
  skipInactive: false,
711
706
  showWarning: false,
712
707
  showDebug: false,
713
708
  triggerFocus: false,
714
- // Subtle FullStory-style trail behind the visitor cursor. The canvas
715
- // is resized alongside the iframe in applyReplayFrameDimensions.
716
- mouseTail: {
717
- strokeStyle: "rgba(59, 130, 246, 0.35)",
718
- lineWidth: 2,
719
- duration: 600,
720
- lineCap: "round",
721
- },
722
- insertStyleRules: [
723
- "[data-radix-popper-content-wrapper], .Toastify, [class*='toast'], [class*='Toast'] { display: none !important; }",
724
- ],
709
+ mouseTail: false,
710
+ insertStyleRules: SUPPRESS_OVERLAYS_CSS,
725
711
  });
726
712
  replayerRef.current = localReplayer;
727
713
  const meta = localReplayer.getMetaData?.();
@@ -732,7 +718,6 @@ function ReplayPlayer({
732
718
  0,
733
719
  Number.isFinite(total) ? total : 0,
734
720
  );
735
- applyReplayFrameDimensions(localReplayer, frameDims);
736
721
  localReplayer.on?.("finish", () => {
737
722
  setPlaying(false);
738
723
  const finalTime = Number(localReplayer.getCurrentTime?.() ?? total);
@@ -740,21 +725,20 @@ function ReplayPlayer({
740
725
  });
741
726
  localReplayer.on?.("resize", (payload: unknown) => {
742
727
  const dims = payload as { width?: unknown; height?: unknown };
743
- const nextDims = normalizeReplayDimensions(dims.width, dims.height);
744
- if (nextDims) {
745
- applyReplayFrameDimensions(localReplayer, nextDims);
746
- revealReplayFrame(localReplayer);
747
- setStreamedDims(nextDims);
728
+ if (
729
+ typeof dims.width === "number" &&
730
+ typeof dims.height === "number" &&
731
+ Number.isFinite(dims.width) &&
732
+ Number.isFinite(dims.height) &&
733
+ dims.width > 0 &&
734
+ dims.height > 0
735
+ ) {
736
+ setStreamedDims({
737
+ width: Math.round(dims.width),
738
+ height: Math.round(dims.height),
739
+ });
748
740
  }
749
741
  });
750
- localReplayer.on?.("fullsnapshot-rebuilded", () => {
751
- applyReplayFrameDimensions(
752
- localReplayer,
753
- replayViewportAt(eventsRef.current, currentTimeRef.current) ??
754
- frameDims,
755
- );
756
- revealReplayFrame(localReplayer);
757
- });
758
742
  updateTime(startAt);
759
743
  setStatus("ready");
760
744
  try {
@@ -769,14 +753,6 @@ function ReplayPlayer({
769
753
  }
770
754
  setPlaying(false);
771
755
  }
772
- window.requestAnimationFrame(() => {
773
- applyReplayFrameDimensions(
774
- localReplayer,
775
- replayViewportAt(eventsRef.current, currentTimeRef.current) ??
776
- frameDims,
777
- );
778
- revealReplayFrame(localReplayer);
779
- });
780
756
  }
781
757
 
782
758
  void loadReplay().catch((loadError: any) => {
@@ -2177,9 +2153,8 @@ function hasPlayableReplayEvents(events: unknown[]): boolean {
2177
2153
  export function replayViewportDimensions(
2178
2154
  events: AnyReplayEvent[],
2179
2155
  ): ReplayViewportDimensions | null {
2180
- // Prefer the most recent sane Meta/ViewportResize frame. Early Meta events
2181
- // (or corrupt resize payloads) can report absurd aspect ratios that collapse
2182
- // the stage into an ultra-wide ribbon after fit-to-stage scaling.
2156
+ // Latest Meta / ViewportResize for CSS fit-to-stage only. Never rewrite these
2157
+ // into the event stream rrweb must keep Meta in sync with the FullSnapshot.
2183
2158
  let best: ReplayViewportDimensions | null = null;
2184
2159
  for (const event of events) {
2185
2160
  const dims = dimensionsFromReplayEvent(event);
@@ -2188,22 +2163,6 @@ export function replayViewportDimensions(
2188
2163
  return best;
2189
2164
  }
2190
2165
 
2191
- export function replayViewportAt(
2192
- events: AnyReplayEvent[],
2193
- currentTime: number,
2194
- ): ReplayViewportDimensions | null {
2195
- const startedAt = replayStartedAt(events);
2196
- let current: ReplayViewportDimensions | null = null;
2197
- for (const event of events) {
2198
- const dims = dimensionsFromReplayEvent(event);
2199
- if (!dims) continue;
2200
- const offset = Number(event.timestamp ?? 0) - startedAt;
2201
- if (offset <= currentTime + 50) current = dims;
2202
- else break;
2203
- }
2204
- return current ?? replayViewportDimensions(events);
2205
- }
2206
-
2207
2166
  function dimensionsFromReplayEvent(
2208
2167
  event: AnyReplayEvent,
2209
2168
  ): ReplayViewportDimensions | null {
@@ -2219,12 +2178,7 @@ function dimensionsFromReplayEvent(
2219
2178
  return null;
2220
2179
  }
2221
2180
 
2222
- /**
2223
- * Prefer a sane viewport for the stage. Returns null only when width/height
2224
- * are missing or non-positive. Out-of-range aspect ratios are clamped rather
2225
- * than rejected so genuine wide windows keep their height (and most of their
2226
- * width) instead of collapsing to the default 1024×640 fallback.
2227
- */
2181
+ /** Read raw positive finite dimensions; no aspect clamping. */
2228
2182
  export function normalizeReplayDimensions(
2229
2183
  width: unknown,
2230
2184
  height: unknown,
@@ -2239,28 +2193,30 @@ export function normalizeReplayDimensions(
2239
2193
  ) {
2240
2194
  return null;
2241
2195
  }
2242
- return clampReplayViewport(width, height);
2196
+ return {
2197
+ width: Math.round(width),
2198
+ height: Math.round(height),
2199
+ };
2243
2200
  }
2244
2201
 
2245
- /**
2246
- * Clamp Meta / ViewportResize dimensions into a displayable aspect range.
2247
- * Multi-monitor spans and corrupt frames often report absurd widths; keep the
2248
- * captured height and shrink width (or vice versa) so the DOM still roughly
2249
- * matches the recorded layout.
2250
- */
2251
- export function clampReplayViewport(
2252
- width: number,
2253
- height: number,
2254
- ): ReplayViewportDimensions {
2255
- let nextWidth = Math.max(MIN_REPLAY_VIEWPORT_WIDTH, Math.round(width));
2256
- let nextHeight = Math.max(MIN_REPLAY_VIEWPORT_HEIGHT, Math.round(height));
2257
- const aspect = nextWidth / nextHeight;
2258
- if (aspect > MAX_REPLAY_ASPECT_RATIO) {
2259
- nextWidth = Math.round(nextHeight * MAX_REPLAY_ASPECT_RATIO);
2260
- } else if (aspect < MIN_REPLAY_ASPECT_RATIO) {
2261
- nextHeight = Math.round(nextWidth / MIN_REPLAY_ASPECT_RATIO);
2202
+ export function clampReplayDisplayDimensions(
2203
+ dims: ReplayViewportDimensions | null,
2204
+ ): ReplayViewportDimensions | null {
2205
+ if (!dims) return null;
2206
+ const aspect = dims.width / dims.height;
2207
+ if (aspect > MAX_REPLAY_DISPLAY_ASPECT_RATIO) {
2208
+ return {
2209
+ width: Math.round(dims.height * MAX_REPLAY_DISPLAY_ASPECT_RATIO),
2210
+ height: dims.height,
2211
+ };
2212
+ }
2213
+ if (aspect < MIN_REPLAY_DISPLAY_ASPECT_RATIO) {
2214
+ return {
2215
+ width: dims.width,
2216
+ height: Math.round(dims.width / MIN_REPLAY_DISPLAY_ASPECT_RATIO),
2217
+ };
2262
2218
  }
2263
- return { width: nextWidth, height: nextHeight };
2219
+ return dims;
2264
2220
  }
2265
2221
 
2266
2222
  export function filterReplayMarkers(
@@ -2284,98 +2240,6 @@ export function filterReplayMarkers(
2284
2240
  });
2285
2241
  }
2286
2242
 
2287
- /**
2288
- * Rewrite Meta / ViewportResize frames so rrweb never applies absurd aspect
2289
- * ratios that collapse the stage into an ultra-wide ribbon. Prefer clamping
2290
- * the captured dimensions (preserve height, shrink width) over replacing them
2291
- * with the default fallback, which would misalign the recorded DOM.
2292
- */
2293
- export function sanitizeReplayViewportEvents(
2294
- events: AnyReplayEvent[],
2295
- fallback: ReplayViewportDimensions,
2296
- ): AnyReplayEvent[] {
2297
- return events.map((event) => {
2298
- if (event.type === RRWEB_EVENT_TYPE.Meta && isRecord(event.data)) {
2299
- return rewriteViewportEventData(event, event.data, fallback);
2300
- }
2301
- if (
2302
- event.type === RRWEB_EVENT_TYPE.IncrementalSnapshot &&
2303
- event.data?.source === INCREMENTAL_SOURCE.ViewportResize &&
2304
- isRecord(event.data)
2305
- ) {
2306
- return rewriteViewportEventData(event, event.data, fallback);
2307
- }
2308
- return event;
2309
- });
2310
- }
2311
-
2312
- function rewriteViewportEventData(
2313
- event: AnyReplayEvent,
2314
- data: AnyRecord,
2315
- fallback: ReplayViewportDimensions,
2316
- ): AnyReplayEvent {
2317
- const width = data.width;
2318
- const height = data.height;
2319
- if (
2320
- typeof width === "number" &&
2321
- typeof height === "number" &&
2322
- Number.isFinite(width) &&
2323
- Number.isFinite(height) &&
2324
- width > 0 &&
2325
- height > 0
2326
- ) {
2327
- const clamped = clampReplayViewport(width, height);
2328
- if (
2329
- clamped.width === Math.round(width) &&
2330
- clamped.height === Math.round(height)
2331
- ) {
2332
- return event;
2333
- }
2334
- return {
2335
- ...event,
2336
- data: {
2337
- ...data,
2338
- width: clamped.width,
2339
- height: clamped.height,
2340
- },
2341
- };
2342
- }
2343
- return {
2344
- ...event,
2345
- data: {
2346
- ...data,
2347
- width: fallback.width,
2348
- height: fallback.height,
2349
- },
2350
- };
2351
- }
2352
-
2353
- function applyReplayFrameDimensions(
2354
- replayer: any,
2355
- dims: ReplayViewportDimensions | null,
2356
- ) {
2357
- const width = dims?.width ?? DEFAULT_PLAYER_WIDTH;
2358
- const height = dims?.height ?? DEFAULT_PLAYER_HEIGHT;
2359
- const wrapper = replayer?.wrapper as HTMLElement | undefined;
2360
- const iframe = replayer?.iframe as HTMLIFrameElement | undefined;
2361
- const mouseTail = replayer?.mouseTail as HTMLCanvasElement | undefined;
2362
- for (const element of [wrapper, iframe]) {
2363
- if (!element) continue;
2364
- element.style.width = `${width}px`;
2365
- element.style.height = `${height}px`;
2366
- }
2367
- for (const element of [iframe, mouseTail]) {
2368
- if (!element) continue;
2369
- element.setAttribute("width", String(width));
2370
- element.setAttribute("height", String(height));
2371
- }
2372
- }
2373
-
2374
- function revealReplayFrame(replayer: any) {
2375
- const iframe = replayer?.iframe as HTMLIFrameElement | undefined;
2376
- if (iframe) iframe.style.display = "inherit";
2377
- }
2378
-
2379
2243
  function replayStartedAt(events: AnyReplayEvent[]): number {
2380
2244
  const first = events.find((event) =>
2381
2245
  Number.isFinite(Number(event.timestamp)),
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-08
4
+ ---
5
+
6
+ Dashboard charts and tables keep their layout steady while loading.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-08
4
+ ---
5
+
6
+ Monitoring Uptime and Errors tabs now clearly show which view is active.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-08
4
+ ---
5
+
6
+ Session replay uses stock rrweb sizing again so recorded pages reassemble correctly instead of showing a blank stage
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-07-08
4
+ ---
5
+
6
+ Sidebar dashboard menus now open above the dashboard canvas
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-07-08
4
+ ---
5
+
6
+ Uptime alerting clarifies in-app inbox vs email and lets you paste Slack or webhook URLs per monitor.
@@ -60,10 +60,12 @@ Each check runs on the server (`server/lib/uptime-monitors.ts`):
60
60
 
61
61
  - On a transition **into failure**, it opens a `monitor_incidents` row (one per
62
62
  continuous failure streak) and calls `notifyWithDelivery` with a clear title
63
- and body plus metadata
64
- `{ kind: "uptime_monitor", monitorId, url, statusCode, latencyMs, failedAssertions }`.
65
- The `inbox` channel is always included; `email`/`slack`/`webhook` are added
66
- when configured, and email uses the monitor's `emailRecipients`.
63
+ and body plus safe inbox metadata
64
+ `{ kind: "uptime_monitor", monitorId, url, statusCode, latencyMs, failedAssertions, emailRecipients }`.
65
+ The `inbox` channel is the in-app bell; `email` uses the monitor's
66
+ `emailRecipients`; `slack` / `webhook` use the monitor's optional
67
+ delivery-only `slackWebhookUrl` / `webhookUrl` (falling back to workspace
68
+ `NOTIFICATIONS_SLACK_WEBHOOK_URL` / `NOTIFICATIONS_WEBHOOK_URL` when unset).
67
69
  - While an incident is open it will not re-alert. After an incident resolves,
68
70
  `cooldownMinutes` suppresses a fresh "down" alert for that window to prevent
69
71
  flapping.
@@ -119,13 +121,13 @@ have their schema.
119
121
  Every operation is an owner-scoped action, so the agent can create and triage
120
122
  monitors from chat exactly like the UI:
121
123
 
122
- | action | method | what it does |
123
- | ------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
124
- | `save-monitor` | POST | create or update a monitor (name, url, method, interval, timeout, expected status, assertions, redirects, severity, channels, email recipients, cooldown, enabled). |
125
- | `list-monitors` | GET | list monitors with latest status, latency, and 24h/7d uptime %. |
126
- | `get-monitor` | GET | one monitor plus its recent check results and incidents. |
127
- | `delete-monitor` | POST | delete a monitor and its results + incidents. |
128
- | `run-monitor-check` | POST | run one check now, record it, and open/resolve incidents. |
124
+ | action | method | what it does |
125
+ | ------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
126
+ | `save-monitor` | POST | create or update a monitor (name, url, method, interval, timeout, expected status, assertions, redirects, severity, channels, email recipients, optional Slack/webhook URLs, cooldown, enabled). |
127
+ | `list-monitors` | GET | list monitors with latest status, latency, and 24h/7d uptime %. |
128
+ | `get-monitor` | GET | one monitor plus its recent check results and incidents. |
129
+ | `delete-monitor` | POST | delete a monitor and its results + incidents. |
130
+ | `run-monitor-check` | POST | run one check now, record it, and open/resolve incidents. |
129
131
 
130
132
  Example prompts:
131
133
 
@@ -70,6 +70,10 @@ export const monitors = table("monitors", {
70
70
  channels: text("channels").notNull().default('["inbox"]'),
71
71
  /** Email recipients as JSON array of addresses. */
72
72
  emailRecipients: text("email_recipients").notNull().default("[]"),
73
+ /** Optional per-monitor Slack incoming webhook URL (overrides workspace env). */
74
+ slackWebhookUrl: text("slack_webhook_url"),
75
+ /** Optional per-monitor generic webhook URL (overrides workspace env). */
76
+ webhookUrl: text("webhook_url"),
73
77
  /** Minutes to suppress repeat alerts while an incident is ongoing. */
74
78
  cooldownMinutes: integer("cooldown_minutes").notNull().default(15),
75
79
  enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
@@ -101,6 +101,8 @@ export interface MonitorInput {
101
101
  severity?: MonitorSeverity;
102
102
  channels?: string[];
103
103
  emailRecipients?: string[];
104
+ slackWebhookUrl?: string | null;
105
+ webhookUrl?: string | null;
104
106
  cooldownMinutes?: number;
105
107
  enabled?: boolean;
106
108
  }
@@ -120,6 +122,8 @@ export interface Monitor {
120
122
  severity: MonitorSeverity;
121
123
  channels: string[];
122
124
  emailRecipients: string[];
125
+ slackWebhookUrl: string | null;
126
+ webhookUrl: string | null;
123
127
  cooldownMinutes: number;
124
128
  enabled: boolean;
125
129
  lastStatus: MonitorStatus | null;
@@ -462,11 +466,50 @@ function normalizeEmailRecipients(recipients: string[] | undefined): string[] {
462
466
  return normalized;
463
467
  }
464
468
 
469
+ function normalizeOptionalHttpUrl(
470
+ value: string | null | undefined,
471
+ label: string,
472
+ ): string | null {
473
+ const trimmed = typeof value === "string" ? value.trim() : "";
474
+ if (!trimmed) return null;
475
+ let parsed: URL;
476
+ try {
477
+ parsed = new URL(trimmed);
478
+ } catch {
479
+ throw badRequest(`${label} must be an absolute http(s) URL`);
480
+ }
481
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
482
+ throw badRequest(`${label} must be an absolute http(s) URL`);
483
+ }
484
+ return trimmed;
485
+ }
486
+
465
487
  function ensureInboxChannel(channels: string[]): string[] {
466
488
  const normalized = channels.map((c) => c.trim()).filter(Boolean);
467
489
  return normalized.includes("inbox") ? normalized : ["inbox", ...normalized];
468
490
  }
469
491
 
492
+ function monitorNotifyMetadata(monitor: Monitor): Record<string, unknown> {
493
+ return {
494
+ kind: "uptime_monitor",
495
+ monitorId: monitor.id,
496
+ monitorName: monitor.name,
497
+ url: monitor.url,
498
+ emailRecipients: monitor.emailRecipients,
499
+ requestedChannels: monitor.channels,
500
+ };
501
+ }
502
+
503
+ function monitorNotifyDeliveryMetadata(
504
+ monitor: Monitor,
505
+ ): Record<string, string> | undefined {
506
+ const delivery: Record<string, string> = {};
507
+ if (monitor.slackWebhookUrl)
508
+ delivery.slackWebhookUrl = monitor.slackWebhookUrl;
509
+ if (monitor.webhookUrl) delivery.webhookUrl = monitor.webhookUrl;
510
+ return Object.keys(delivery).length > 0 ? delivery : undefined;
511
+ }
512
+
470
513
  // ---------------------------------------------------------------------------
471
514
  // Pure evaluation core (unit-tested)
472
515
  // ---------------------------------------------------------------------------
@@ -891,6 +934,8 @@ function rowToMonitor(row: any): Monitor {
891
934
  severity: row.severity === "warning" ? "warning" : "critical",
892
935
  channels: safeJsonParse<string[]>(row.channels, ["inbox"]),
893
936
  emailRecipients: safeJsonParse<string[]>(row.emailRecipients, []),
937
+ slackWebhookUrl: row.slackWebhookUrl?.trim() || null,
938
+ webhookUrl: row.webhookUrl?.trim() || null,
894
939
  cooldownMinutes: Number(row.cooldownMinutes ?? 15),
895
940
  enabled: row.enabled === true || row.enabled === 1,
896
941
  lastStatus: (row.lastStatus ?? null) as MonitorStatus | null,
@@ -1121,6 +1166,11 @@ export async function saveMonitor(
1121
1166
  const severity = input.severity === "warning" ? "warning" : "critical";
1122
1167
  const emailRecipients = normalizeEmailRecipients(input.emailRecipients);
1123
1168
  const channels = normalizeChannels(input.channels, emailRecipients);
1169
+ const slackWebhookUrl = normalizeOptionalHttpUrl(
1170
+ input.slackWebhookUrl,
1171
+ "Slack webhook URL",
1172
+ );
1173
+ const webhookUrl = normalizeOptionalHttpUrl(input.webhookUrl, "Webhook URL");
1124
1174
  const cooldownMinutes = clampInt(input.cooldownMinutes ?? 15, 0, 24 * 60, 15);
1125
1175
  const enabled = input.enabled ?? true;
1126
1176
 
@@ -1138,6 +1188,8 @@ export async function saveMonitor(
1138
1188
  severity,
1139
1189
  channels: JSON.stringify(channels),
1140
1190
  emailRecipients: JSON.stringify(emailRecipients),
1191
+ slackWebhookUrl,
1192
+ webhookUrl,
1141
1193
  cooldownMinutes,
1142
1194
  enabled,
1143
1195
  };
@@ -1440,16 +1492,14 @@ async function notifyMonitorDown(monitor: Monitor, outcome: CheckOutcome) {
1440
1492
  body: `${host} is ${label}${detail}.${latency}`,
1441
1493
  channels: ensureInboxChannel(monitor.channels),
1442
1494
  metadata: {
1443
- kind: "uptime_monitor",
1444
- monitorId: monitor.id,
1445
- monitorName: monitor.name,
1446
- url: monitor.url,
1495
+ ...monitorNotifyMetadata(monitor),
1496
+ ...(monitorNotifyDeliveryMetadata(monitor)
1497
+ ? { delivery: monitorNotifyDeliveryMetadata(monitor) }
1498
+ : {}),
1447
1499
  status: outcome.status,
1448
1500
  statusCode: outcome.statusCode,
1449
1501
  latencyMs: outcome.latencyMs,
1450
1502
  failedAssertions: outcome.failedAssertions,
1451
- emailRecipients: monitor.emailRecipients,
1452
- requestedChannels: monitor.channels,
1453
1503
  },
1454
1504
  },
1455
1505
  { owner: monitor.ownerEmail },
@@ -1475,16 +1525,14 @@ async function notifyMonitorRecovered(
1475
1525
  body: `${host} is back up${downFor ? ` after ${downFor} of downtime` : ""}.${latency}`,
1476
1526
  channels: ensureInboxChannel(monitor.channels),
1477
1527
  metadata: {
1478
- kind: "uptime_monitor",
1479
- monitorId: monitor.id,
1480
- monitorName: monitor.name,
1481
- url: monitor.url,
1528
+ ...monitorNotifyMetadata(monitor),
1529
+ ...(monitorNotifyDeliveryMetadata(monitor)
1530
+ ? { delivery: monitorNotifyDeliveryMetadata(monitor) }
1531
+ : {}),
1482
1532
  status: "up",
1483
1533
  statusCode: outcome.statusCode,
1484
1534
  latencyMs: outcome.latencyMs,
1485
1535
  incidentId: incident.id,
1486
- emailRecipients: monitor.emailRecipients,
1487
- requestedChannels: monitor.channels,
1488
1536
  },
1489
1537
  },
1490
1538
  { owner: monitor.ownerEmail },
@@ -1114,6 +1114,16 @@ const runAnalyticsMigrations = runMigrations(
1114
1114
  name: "status-pages-scope-updated-idx",
1115
1115
  sql: `CREATE INDEX IF NOT EXISTS status_pages_scope_updated_idx ON status_pages (owner_email, org_id, updated_at)`,
1116
1116
  },
1117
+ {
1118
+ version: 106,
1119
+ name: "uptime-monitors-slack-webhook-url",
1120
+ sql: `ALTER TABLE monitors ADD COLUMN IF NOT EXISTS slack_webhook_url TEXT`,
1121
+ },
1122
+ {
1123
+ version: 107,
1124
+ name: "uptime-monitors-webhook-url",
1125
+ sql: `ALTER TABLE monitors ADD COLUMN IF NOT EXISTS webhook_url TEXT`,
1126
+ },
1117
1127
  ],
1118
1128
  { table: "analytics_migrations" },
1119
1129
  );