@oneuptime/common 12.0.4 → 12.0.6

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 (838) hide show
  1. package/Models/DatabaseModels/AIRun.ts +71 -1
  2. package/Models/DatabaseModels/AlertFeed.ts +30 -0
  3. package/Models/DatabaseModels/Dashboard.ts +28 -0
  4. package/Models/DatabaseModels/DataSource.ts +744 -0
  5. package/Models/DatabaseModels/Incident.ts +2 -0
  6. package/Models/DatabaseModels/IncidentEpisode.ts +1 -0
  7. package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +1 -0
  8. package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
  9. package/Models/DatabaseModels/IncidentFeed.ts +30 -0
  10. package/Models/DatabaseModels/IncidentPublicNote.ts +1 -0
  11. package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
  12. package/Models/DatabaseModels/Index.ts +3 -0
  13. package/Models/DatabaseModels/LogSavedView.ts +33 -0
  14. package/Models/DatabaseModels/Project.ts +231 -9
  15. package/Models/DatabaseModels/ProjectCallSMSConfig.ts +10 -2
  16. package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -0
  17. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +1 -0
  18. package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
  19. package/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser.ts +4 -4
  20. package/Models/DatabaseModels/StatusPage.ts +28 -0
  21. package/Models/DatabaseModels/StatusPageAnnouncement.ts +1 -0
  22. package/Models/DatabaseModels/User.ts +2 -0
  23. package/Models/DatabaseModels/UserOnCallLog.ts +2 -0
  24. package/Models/DatabaseModels/UserOnCallLogTimeline.ts +2 -0
  25. package/Models/DatabaseModels/UserSession.ts +2 -0
  26. package/Models/DatabaseModels/UserTotpAuth.ts +6 -4
  27. package/Models/DatabaseModels/UserWebAuthn.ts +5 -3
  28. package/Server/API/AIAgentDataAPI.ts +44 -55
  29. package/Server/API/AIInsightAPI.ts +47 -4
  30. package/Server/API/AIInvestigationAPI.ts +200 -17
  31. package/Server/API/DashboardAPI.ts +186 -502
  32. package/Server/API/DataSourceAPI.ts +268 -0
  33. package/Server/API/StatusPageAPI.ts +49 -20
  34. package/Server/API/UserAPI.ts +119 -0
  35. package/Server/EnvironmentConfig.ts +16 -0
  36. package/Server/Infrastructure/ClickhouseConfig.ts +74 -1
  37. package/Server/Infrastructure/ClickhouseDatabase.ts +1 -1
  38. package/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.ts +27 -0
  39. package/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.ts +89 -0
  40. package/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.ts +27 -0
  41. package/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.ts +39 -0
  42. package/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.ts +40 -0
  43. package/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.ts +26 -0
  44. package/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.ts +24 -0
  45. package/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.ts +76 -0
  46. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +16 -0
  47. package/Server/Infrastructure/Queue.ts +26 -0
  48. package/Server/Infrastructure/QueueWorker.ts +17 -0
  49. package/Server/Middleware/TelemetryIngest.ts +8 -3
  50. package/Server/Services/AIInsightService.ts +132 -12
  51. package/Server/Services/AIRunService.ts +112 -10
  52. package/Server/Services/AIService.ts +109 -22
  53. package/Server/Services/AlertFeedService.ts +5 -0
  54. package/Server/Services/AlertService.ts +38 -32
  55. package/Server/Services/AnalyticsDatabaseService.ts +37 -4
  56. package/Server/Services/DataSourceService.ts +200 -0
  57. package/Server/Services/DatabaseService.ts +43 -2
  58. package/Server/Services/HostService.ts +92 -0
  59. package/Server/Services/IncidentFeedService.ts +5 -0
  60. package/Server/Services/IncidentService.ts +45 -0
  61. package/Server/Services/Index.ts +2 -0
  62. package/Server/Services/LlmLogService.ts +46 -2
  63. package/Server/Services/MonitorService.ts +49 -0
  64. package/Server/Services/MonitorStatusService.ts +166 -0
  65. package/Server/Services/OpenTelemetryIngestService.ts +255 -184
  66. package/Server/Services/RumSessionPinService.ts +7 -21
  67. package/Server/Services/StatusPageSubscriberService.ts +13 -0
  68. package/Server/Services/UserService.ts +280 -9
  69. package/Server/Static/Vendor/README.md +62 -0
  70. package/Server/Static/Vendor/fonts/InterVariable.woff2 +0 -0
  71. package/Server/Static/Vendor/highlight/highlight.min.js +1244 -0
  72. package/Server/Static/Vendor/highlight/languages/apache.min.js +15 -0
  73. package/Server/Static/Vendor/highlight/languages/bash.min.js +21 -0
  74. package/Server/Static/Vendor/highlight/languages/c.min.js +41 -0
  75. package/Server/Static/Vendor/highlight/languages/cpp.min.js +47 -0
  76. package/Server/Static/Vendor/highlight/languages/csharp.min.js +49 -0
  77. package/Server/Static/Vendor/highlight/languages/css.min.js +31 -0
  78. package/Server/Static/Vendor/highlight/languages/diff.min.js +9 -0
  79. package/Server/Static/Vendor/highlight/languages/dns.min.js +11 -0
  80. package/Server/Static/Vendor/highlight/languages/dockerfile.min.js +8 -0
  81. package/Server/Static/Vendor/highlight/languages/go.min.js +20 -0
  82. package/Server/Static/Vendor/highlight/languages/graphql.min.js +12 -0
  83. package/Server/Static/Vendor/highlight/languages/http.min.js +14 -0
  84. package/Server/Static/Vendor/highlight/languages/ini.min.js +15 -0
  85. package/Server/Static/Vendor/highlight/languages/java.min.js +38 -0
  86. package/Server/Static/Vendor/highlight/languages/javascript.min.js +81 -0
  87. package/Server/Static/Vendor/highlight/languages/json.min.js +8 -0
  88. package/Server/Static/Vendor/highlight/languages/kotlin.min.js +46 -0
  89. package/Server/Static/Vendor/highlight/languages/lua.min.js +15 -0
  90. package/Server/Static/Vendor/highlight/languages/makefile.min.js +14 -0
  91. package/Server/Static/Vendor/highlight/languages/nginx.min.js +21 -0
  92. package/Server/Static/Vendor/highlight/languages/perl.min.js +41 -0
  93. package/Server/Static/Vendor/highlight/languages/php.min.js +59 -0
  94. package/Server/Static/Vendor/highlight/languages/powershell.min.js +39 -0
  95. package/Server/Static/Vendor/highlight/languages/python.min.js +42 -0
  96. package/Server/Static/Vendor/highlight/languages/r.min.js +26 -0
  97. package/Server/Static/Vendor/highlight/languages/ruby.min.js +54 -0
  98. package/Server/Static/Vendor/highlight/languages/rust.min.js +27 -0
  99. package/Server/Static/Vendor/highlight/languages/scala.min.js +28 -0
  100. package/Server/Static/Vendor/highlight/languages/sql.min.js +21 -0
  101. package/Server/Static/Vendor/highlight/languages/swift.min.js +68 -0
  102. package/Server/Static/Vendor/highlight/languages/typescript.min.js +99 -0
  103. package/Server/Static/Vendor/highlight/languages/xml.min.js +29 -0
  104. package/Server/Static/Vendor/highlight/languages/yaml.min.js +28 -0
  105. package/Server/Static/Vendor/highlight/styles/github-dark.min.css +10 -0
  106. package/Server/Static/Vendor/highlight/styles/vs2015.min.css +1 -0
  107. package/Server/Static/Vendor/tailwind/tailwind-3.4.5.js +21028 -0
  108. package/Server/Types/Database/Permissions/BasePermission.ts +7 -0
  109. package/Server/Types/Database/Permissions/DeletePermission.ts +2 -0
  110. package/Server/Types/Database/Permissions/ReadPermission.ts +3 -0
  111. package/Server/Types/Database/Permissions/TenantPermission.ts +101 -16
  112. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +6 -0
  113. package/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.ts +4 -0
  114. package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +8 -5
  115. package/Server/Utils/AI/CodeFix/FixRunBudget.ts +90 -16
  116. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +2 -0
  117. package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +2 -0
  118. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +311 -31
  119. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +26 -24
  120. package/Server/Utils/AI/SRE/ConfidenceSignal.ts +182 -71
  121. package/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.ts +252 -43
  122. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +26 -24
  123. package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.ts +125 -0
  124. package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +25 -15
  125. package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +256 -57
  126. package/Server/Utils/AI/SRE/Insights/InsightStore.ts +76 -18
  127. package/Server/Utils/AI/SRE/InstrumentationTaskTrigger.ts +47 -13
  128. package/Server/Utils/AI/SRE/InvestigationGrader.ts +9 -5
  129. package/Server/Utils/AI/SRE/InvestigationQueue.ts +141 -24
  130. package/Server/Utils/AI/SRE/InvestigationSubjectLock.ts +66 -0
  131. package/Server/Utils/AI/SRE/InvestigationTldr.ts +352 -0
  132. package/Server/Utils/AI/SRE/PostedRootCause.ts +94 -3
  133. package/Server/Utils/AI/SRE/README.md +1 -1
  134. package/Server/Utils/AI/SRE/SubjectCodeFixRun.ts +132 -10
  135. package/Server/Utils/AnalyticsDatabase/ClusterConfig.ts +24 -0
  136. package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +7 -2
  137. package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +1793 -0
  138. package/Server/Utils/Dashboard/PublicDashboardViewConfig.ts +65 -0
  139. package/Server/Utils/DataSource/ConnectorRegistry.ts +53 -0
  140. package/Server/Utils/DataSource/Connectors/ClickHouseConnector.ts +338 -0
  141. package/Server/Utils/DataSource/Connectors/ElasticsearchConnector.ts +717 -0
  142. package/Server/Utils/DataSource/Connectors/LokiConnector.ts +606 -0
  143. package/Server/Utils/DataSource/Connectors/MySqlConnector.ts +307 -0
  144. package/Server/Utils/DataSource/Connectors/PostgresConnector.ts +260 -0
  145. package/Server/Utils/DataSource/Connectors/PrometheusConnector.ts +555 -0
  146. package/Server/Utils/DataSource/Connectors/RestApiConnector.ts +597 -0
  147. package/Server/Utils/DataSource/Connectors/SqlServerConnector.ts +318 -0
  148. package/Server/Utils/DataSource/EgressGuard.ts +446 -0
  149. package/Server/Utils/DataSource/HttpFetch.ts +221 -0
  150. package/Server/Utils/DataSource/SqlQueryGuard.ts +383 -0
  151. package/Server/Utils/DataSource/SqlResultShaper.ts +372 -0
  152. package/Server/Utils/DataSource/Types.ts +93 -0
  153. package/Server/Utils/Database/PostgresErrorTranslator.ts +172 -17
  154. package/Server/Utils/InProcessMemo.ts +141 -0
  155. package/Server/Utils/InstanceHealth/TelemetryIngestion.ts +628 -0
  156. package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +29 -0
  157. package/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.ts +56 -0
  158. package/Server/Utils/Monitor/MonitorMetricUtil.ts +104 -0
  159. package/Server/Utils/Monitor/MonitorResource.ts +14 -0
  160. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +1 -0
  161. package/Server/Utils/PasswordHash.ts +28 -4
  162. package/Server/Utils/Response.ts +12 -1
  163. package/Server/Utils/SSRFProtection.ts +237 -0
  164. package/Server/Utils/StartServer.ts +8 -0
  165. package/Server/Utils/StatusPageSubscriberWebhook.ts +30 -0
  166. package/Server/Utils/Telemetry/CaptureSpan.ts +30 -18
  167. package/Server/Utils/Telemetry/IngestionTimestamp.ts +75 -0
  168. package/Server/Utils/Telemetry/ResourceHeartbeat.ts +99 -7
  169. package/Server/Utils/Telemetry/Telemetry.ts +112 -1
  170. package/Server/Utils/Telemetry.ts +104 -3
  171. package/Server/Utils/VendorAssets.ts +165 -0
  172. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +28 -5
  173. package/Server/Utils/Workspace/Slack/Slack.ts +5 -0
  174. package/Server/Views/Partials/Head.ejs +21 -7
  175. package/Tests/App/AdminDashboard/AdminProjectsIdFilter.test.tsx +207 -0
  176. package/Tests/App/AdminDashboard/AdminUsersIdFilter.test.tsx +216 -0
  177. package/Tests/App/Dashboard/AIInsightViewActions.test.tsx +422 -0
  178. package/Tests/App/Dashboard/AIInvestigationHeaderStatus.test.tsx +251 -0
  179. package/Tests/App/Dashboard/AddWidgetModal.test.tsx +10 -3
  180. package/Tests/App/Dashboard/AlertsSideMenu.test.tsx +387 -0
  181. package/Tests/App/Dashboard/ChatActivityFeed.test.tsx +199 -0
  182. package/Tests/App/Dashboard/DataSourceChartSyntheticConfig.test.tsx +736 -0
  183. package/Tests/App/Dashboard/DataSourceQueryEditor.test.tsx +868 -0
  184. package/Tests/App/Dashboard/DataSourceWidgetCatalog.test.ts +440 -0
  185. package/Tests/App/Dashboard/DataSourceWidgetFetching.test.tsx +2109 -0
  186. package/Tests/App/Dashboard/DataSourceWidgetViews.test.tsx +726 -0
  187. package/Tests/App/Dashboard/EventStatusPanel.test.tsx +747 -0
  188. package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +447 -0
  189. package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +480 -0
  190. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +1695 -0
  191. package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +500 -0
  192. package/Tests/App/Dashboard/PortMonitorCriteriaFilter.test.ts +93 -0
  193. package/Tests/App/Dashboard/ProductMenuBreadcrumbs.test.tsx +391 -0
  194. package/Tests/App/Dashboard/ScheduledMaintenanceSideMenu.test.tsx +326 -0
  195. package/Tests/App/Dashboard/SideMenuHarness.tsx +187 -0
  196. package/Tests/App/Dashboard/WidgetCatalog.test.ts +6 -1
  197. package/Tests/App/StatusPage/PublicStatusPageAPIErrorHandling.test.ts +117 -0
  198. package/Tests/App/StatusPage/StatusPageModelAPIPolymorphism.test.ts +223 -0
  199. package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +1858 -0
  200. package/Tests/Models/DatabaseModels/DataSourceModelSecrets.test.ts +70 -0
  201. package/Tests/Models/DatabaseModels/PermissionCatalogueCoverage.test.ts +243 -0
  202. package/Tests/Server/API/AIAgentDataFixFromIncidentContext.test.ts +266 -0
  203. package/Tests/Server/API/AIInvestigationAPI.test.ts +781 -0
  204. package/Tests/Server/API/AIInvestigationCreateFixTask.test.ts +80 -0
  205. package/Tests/Server/API/DashboardMasterPasswordAPI.test.ts +172 -1
  206. package/Tests/Server/API/DashboardPublicDataSourceStrip.test.ts +686 -0
  207. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +585 -823
  208. package/Tests/Server/API/DataSourceAPIRoutes.test.ts +486 -0
  209. package/Tests/Server/API/StatusPageMasterPasswordAPI.test.ts +335 -0
  210. package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +291 -0
  211. package/Tests/Server/API/UserAuthenticationAPI.test.ts +641 -0
  212. package/Tests/Server/Infrastructure/ClickhouseKeepAliveOptions.test.ts +441 -0
  213. package/Tests/Server/Middleware/TelemetryIngestTokenLog.test.ts +140 -0
  214. package/Tests/Server/Services/AIInsightService.test.ts +266 -11
  215. package/Tests/Server/Services/AIRunAnalysisTldrPersistence.test.ts +160 -0
  216. package/Tests/Server/Services/AIRunCodeFixClaim.test.ts +59 -5
  217. package/Tests/Server/Services/AIRunHumanVerdict.test.ts +127 -58
  218. package/Tests/Server/Services/AIServiceDailyBudget.test.ts +400 -20
  219. package/Tests/Server/Services/AddMasterPasswordSaltMigration.test.ts +283 -0
  220. package/Tests/Server/Services/AlertMetricResourceAttributes.test.ts +352 -0
  221. package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +25 -1
  222. package/Tests/Server/Services/DataSourceServiceSettings.test.ts +244 -0
  223. package/Tests/Server/Services/DatabaseServicePerUserPasswordSalt.test.ts +45 -35
  224. package/Tests/Server/Services/DatabaseServiceUpdateColumnsWithoutHooks.test.ts +54 -0
  225. package/Tests/Server/Services/FeedAIRunAssociation.test.ts +125 -0
  226. package/Tests/Server/Services/FixFromIncidentTaskTrigger.test.ts +418 -57
  227. package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +397 -0
  228. package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +41 -2
  229. package/Tests/Server/Services/IncidentMetricResourceAttributes.test.ts +408 -0
  230. package/Tests/Server/Services/InstrumentationTaskTrigger.test.ts +173 -14
  231. package/Tests/Server/Services/MasterPasswordScrypt.test.ts +567 -0
  232. package/Tests/Server/Services/MonitorStatusServiceDeleteReferenceCleanup.test.ts +351 -0
  233. package/Tests/Server/Services/MonitorStatusServiceValidation.test.ts +171 -0
  234. package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +398 -0
  235. package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +19 -0
  236. package/Tests/Server/Services/ProjectCallSMSConfigCredentialUniqueness.test.ts +305 -0
  237. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +36 -1
  238. package/Tests/Server/Services/RumSessionPinUniqueViolationRecovery.test.ts +163 -0
  239. package/Tests/Server/Services/SeparateIncidentAlertAiSettingsMigration.test.ts +194 -0
  240. package/Tests/Server/Services/UserAuthenticationService.test.ts +973 -0
  241. package/Tests/Server/Types/Database/ObjectIdColumnQuery.test.ts +262 -0
  242. package/Tests/Server/Types/Database/Permissions/TenantPermission.test.ts +358 -0
  243. package/Tests/Server/Utils/AI/AIAlertGating.test.ts +37 -4
  244. package/Tests/Server/Utils/AI/AIConfidenceSignal.test.ts +330 -29
  245. package/Tests/Server/Utils/AI/AIIncidentGating.test.ts +25 -0
  246. package/Tests/Server/Utils/AI/AIInvestigationQueue.test.ts +293 -5
  247. package/Tests/Server/Utils/AI/CodeFixAgentCompletion.test.ts +59 -1
  248. package/Tests/Server/Utils/AI/FixFromIncidentAutoTrigger.test.ts +342 -19
  249. package/Tests/Server/Utils/AI/FixRunBudget.test.ts +214 -30
  250. package/Tests/Server/Utils/AI/Insights/ExceptionSpikeDetector.test.ts +6 -1
  251. package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +121 -4
  252. package/Tests/Server/Utils/AI/Insights/InvestigationQueueInsightSubject.test.ts +2 -0
  253. package/Tests/Server/Utils/AI/Insights/NewExceptionDetector.test.ts +361 -10
  254. package/Tests/Server/Utils/AI/InvestigationGrader.test.ts +65 -17
  255. package/Tests/Server/Utils/AI/InvestigationQueueRemediationExecution.test.ts +3 -0
  256. package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +605 -20
  257. package/Tests/Server/Utils/AI/InvestigationTldr.test.ts +623 -0
  258. package/Tests/Server/Utils/AI/InvestigationTldrWiring.test.ts +98 -0
  259. package/Tests/Server/Utils/AI/PostedRootCause.test.ts +254 -0
  260. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +4 -0
  261. package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +2 -0
  262. package/Tests/Server/Utils/AI/SubjectCodeFixRunDedupe.test.ts +44 -0
  263. package/Tests/Server/Utils/AnalyticsDatabase/ClusterAwareSchema.test.ts +30 -0
  264. package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +857 -0
  265. package/Tests/Server/Utils/Dashboard/PublicDashboardViewConfig.test.ts +480 -0
  266. package/Tests/Server/Utils/DataSource/ClickHouseConnector.test.ts +759 -0
  267. package/Tests/Server/Utils/DataSource/ConnectorRegistry.test.ts +37 -0
  268. package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +645 -0
  269. package/Tests/Server/Utils/DataSource/ElasticsearchConnector.test.ts +1206 -0
  270. package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +616 -0
  271. package/Tests/Server/Utils/DataSource/LokiConnector.test.ts +970 -0
  272. package/Tests/Server/Utils/DataSource/MySqlConnector.test.ts +935 -0
  273. package/Tests/Server/Utils/DataSource/PostgresConnector.test.ts +854 -0
  274. package/Tests/Server/Utils/DataSource/PrometheusConnector.test.ts +881 -0
  275. package/Tests/Server/Utils/DataSource/RestApiConnector.test.ts +1020 -0
  276. package/Tests/Server/Utils/DataSource/SqlQueryGuard.test.ts +425 -0
  277. package/Tests/Server/Utils/DataSource/SqlResultShaper.test.ts +555 -0
  278. package/Tests/Server/Utils/DataSource/SqlServerConnector.test.ts +934 -0
  279. package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +228 -7
  280. package/Tests/Server/Utils/InProcessMemo.test.ts +292 -0
  281. package/Tests/Server/Utils/InstanceHealth/TelemetryIngestion.test.ts +960 -0
  282. package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +103 -0
  283. package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaPortTimings.test.ts +212 -0
  284. package/Tests/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.test.ts +401 -0
  285. package/Tests/Server/Utils/Monitor/MonitorMetricResourceAttributes.test.ts +336 -0
  286. package/Tests/Server/Utils/Monitor/MonitorMetricUtilPortTimings.test.ts +243 -0
  287. package/Tests/Server/Utils/OfflineAssetHygiene.test.ts +521 -0
  288. package/Tests/Server/Utils/PasswordHash.test.ts +30 -0
  289. package/Tests/Server/Utils/ResponseRenderAnalytics.test.ts +143 -0
  290. package/Tests/Server/Utils/SSRFProtection.test.ts +193 -0
  291. package/Tests/Server/Utils/StatusPageSubscriberWebhook.test.ts +101 -0
  292. package/Tests/Server/Utils/TailwindBuildCopy.test.ts +225 -0
  293. package/Tests/Server/Utils/Telemetry/ResourceHeartbeatL1Memo.test.ts +355 -0
  294. package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +128 -0
  295. package/Tests/Server/Utils/Telemetry/TelemetryUtilAttributeKeys.test.ts +381 -0
  296. package/Tests/Server/Utils/Telemetry.test.ts +87 -0
  297. package/Tests/Server/Utils/VendorAssetIntegrity.test.ts +343 -0
  298. package/Tests/Server/Utils/VendorAssets.test.ts +671 -0
  299. package/Tests/Server/Views/OfflineRender.test.ts +297 -0
  300. package/Tests/Types/Dashboard/DashboardTemplates.test.ts +111 -0
  301. package/Tests/Types/Dashboard/DataSourceComponentTypes.test.ts +618 -0
  302. package/Tests/Types/DateClickhouseFormat.test.ts +192 -0
  303. package/Tests/Types/Monitor/CephMetricCatalog.test.ts +104 -0
  304. package/Tests/Types/Monitor/CriteriaFilter.test.ts +4 -0
  305. package/Tests/Types/Monitor/DockerMetricCatalog.test.ts +109 -0
  306. package/Tests/Types/Monitor/DockerSwarmMetricCatalog.test.ts +118 -0
  307. package/Tests/Types/Monitor/HostMetricCatalog.test.ts +104 -0
  308. package/Tests/Types/Monitor/IotMetricCatalog.test.ts +104 -0
  309. package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +118 -0
  310. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +42 -0
  311. package/Tests/Types/Monitor/MonitorStep.test.ts +38 -0
  312. package/Tests/Types/Monitor/MonitorStepDomainMonitor.test.ts +75 -0
  313. package/Tests/Types/Monitor/PodmanMetricCatalog.test.ts +109 -0
  314. package/Tests/Types/Monitor/ProxmoxMetricCatalog.test.ts +107 -0
  315. package/Tests/Types/Monitor/SnmpMonitor/SnmpVendorTemplate.test.ts +151 -0
  316. package/Tests/Types/Password.test.ts +363 -0
  317. package/Tests/Types/Permission.test.ts +137 -0
  318. package/Tests/Types/Rum/SessionReplayMaskingMode.test.ts +101 -0
  319. package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +5 -5
  320. package/Tests/UI/Components/CardModelDetailEdit.test.tsx +3 -5
  321. package/Tests/UI/Components/ConfirmModal.test.tsx +7 -0
  322. package/Tests/UI/Components/ErrorBoundary.test.tsx +211 -0
  323. package/Tests/UI/Components/FeedItemSafeMode.test.tsx +74 -0
  324. package/Tests/UI/Components/Modal.test.tsx +281 -1
  325. package/Tests/UI/Components/MoreMenu.test.tsx +756 -0
  326. package/Tests/UI/Components/ObjectIdFilterPipeline.test.tsx +396 -0
  327. package/Tests/UI/Components/StatusPage/ResourceGroupNavigator.test.tsx +1147 -0
  328. package/Tests/UI/Components/StatusPage/ResourceGroupSection.test.tsx +62 -0
  329. package/Tests/UI/Monitor/PortMonitorView.test.tsx +237 -0
  330. package/Tests/UI/Telemetry/TelemetrySnapshotWindowAlert.test.tsx +91 -0
  331. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +116 -1
  332. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  333. package/Tests/UI/Utils/DownloadFile.test.ts +103 -0
  334. package/Tests/UI/Utils/ErrorSupportBundle.test.ts +582 -0
  335. package/Tests/UI/Utils/Project.test.ts +31 -0
  336. package/Tests/UI/Utils/StatusPageModelAPIInjection.test.ts +245 -0
  337. package/Tests/UI/Utils/UseDashboardGridDnd.test.tsx +820 -0
  338. package/Tests/Utils/Analytics.test.ts +23 -0
  339. package/Tests/Utils/Dashboard/Components/DashboardDataSourceComponents.test.ts +640 -0
  340. package/Tests/Utils/Dashboard/DashboardViewConfig.test.ts +329 -0
  341. package/Tests/Utils/Dashboard/GridLayout.test.ts +951 -0
  342. package/Tests/Utils/DataSource/DataSourceQueryText.test.ts +138 -0
  343. package/Tests/Utils/DataSource/DataSourceValueReducer.test.ts +526 -0
  344. package/Tests/Utils/Metrics/MetricResourceAttributeUtil.test.ts +691 -0
  345. package/Tests/Utils/Monitor/MonitorMetricType.test.ts +117 -0
  346. package/Tests/Utils/RecordingRuleExpression.test.ts +327 -0
  347. package/Tests/Utils/StatusPage/GroupHierarchyView.test.ts +1452 -0
  348. package/Tests/Utils/StatusPage/GroupNestingLayout.test.ts +186 -0
  349. package/Tests/Utils/StatusPage/GroupTree.test.ts +456 -0
  350. package/Tests/Utils/StatusPage/OverviewGroupHierarchyVisibility.test.ts +588 -0
  351. package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +931 -0
  352. package/Tests/Utils/Telemetry/CaptureSpanExportGating.test.ts +288 -0
  353. package/Tests/Utils/Telemetry/IngestionTimestamp.test.ts +170 -0
  354. package/Tests/Utils/Telemetry/SavedViewTimeRange.test.ts +244 -0
  355. package/Tests/Utils/Telemetry/TelemetryQueryTimeRange.test.ts +825 -0
  356. package/Types/AI/AIInsightEvidence.ts +6 -0
  357. package/Types/AI/AIRunCodeFixRecommendation.ts +17 -0
  358. package/Types/AI/CodeFixTaskContext.ts +63 -3
  359. package/Types/AI/CodeFixTaskType.ts +3 -3
  360. package/Types/Analytics/RevenueEvent.ts +31 -0
  361. package/Types/Dashboard/DashboardComponentType.ts +31 -0
  362. package/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.ts +27 -0
  363. package/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.ts +32 -0
  364. package/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.ts +30 -0
  365. package/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.ts +55 -0
  366. package/Types/DataSource/DataSourceLimits.ts +62 -0
  367. package/Types/DataSource/DataSourceQueryConfig.ts +41 -0
  368. package/Types/DataSource/DataSourceTableResult.ts +38 -0
  369. package/Types/DataSource/DataSourceType.ts +213 -0
  370. package/Types/Date.ts +43 -3
  371. package/Types/HashedString.ts +4 -3
  372. package/Types/Monitor/CriteriaFilter.ts +4 -0
  373. package/Types/Monitor/DomainMonitor/DomainLookupMethod.ts +23 -0
  374. package/Types/Monitor/DomainMonitor/DomainMonitorResponse.ts +7 -0
  375. package/Types/Monitor/MonitorCriteriaInstance.ts +24 -7
  376. package/Types/Monitor/MonitorMetricType.ts +8 -0
  377. package/Types/Monitor/MonitorStep.ts +10 -1
  378. package/Types/Monitor/MonitorStepDomainMonitor.ts +21 -0
  379. package/Types/Monitor/PortMonitor/PortMonitorTimings.ts +10 -0
  380. package/Types/Password.ts +64 -0
  381. package/Types/Permission.ts +69 -175
  382. package/Types/Probe/ProbeMonitorResponse.ts +6 -0
  383. package/Types/UserAuthenticationStatus.ts +34 -0
  384. package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +2 -3
  385. package/UI/Components/Button/Button.tsx +1 -1
  386. package/UI/Components/ErrorBoundary.tsx +549 -71
  387. package/UI/Components/Feed/FeedItem.tsx +9 -2
  388. package/UI/Components/Icon/Icon.tsx +10 -9
  389. package/UI/Components/Modal/ConfirmModal.tsx +6 -1
  390. package/UI/Components/Modal/Modal.tsx +267 -35
  391. package/UI/Components/Modal/ModalFooter.tsx +6 -1
  392. package/UI/Components/ModelDetail/CardModelDetail.tsx +0 -12
  393. package/UI/Components/ModelTable/BaseModelTable.tsx +1 -1
  394. package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +12 -2
  395. package/UI/Components/MoreMenu/MoreMenu.tsx +301 -98
  396. package/UI/Components/MoreMenu/MoreMenuItem.tsx +13 -5
  397. package/UI/Components/StatusPage/ResourceGrid.tsx +314 -0
  398. package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +808 -0
  399. package/UI/Components/StatusPage/ResourceList.tsx +570 -0
  400. package/UI/Styles/Theme.css +18 -0
  401. package/UI/Utils/API/RequestOptions.ts +7 -0
  402. package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +1 -0
  403. package/UI/Utils/DownloadFile.ts +16 -8
  404. package/UI/Utils/ErrorSupportBundle.ts +805 -0
  405. package/UI/Utils/ModelAPI/ModelAPI.ts +20 -8
  406. package/UI/Utils/Project.ts +4 -0
  407. package/UI/Utils/StatusPage.ts +8 -2
  408. package/UI/Utils/TestDataSource.ts +61 -0
  409. package/UI/Utils/UseDashboardGridDnd.ts +917 -0
  410. package/UI/esbuild-config.js +45 -0
  411. package/Utils/Analytics.ts +15 -0
  412. package/Utils/Dashboard/Components/DashboardDataSourceChartComponent.ts +88 -0
  413. package/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.ts +123 -0
  414. package/Utils/Dashboard/Components/DashboardDataSourceShared.ts +16 -0
  415. package/Utils/Dashboard/Components/DashboardDataSourceTableComponent.ts +72 -0
  416. package/Utils/Dashboard/Components/DashboardDataSourceValueComponent.ts +126 -0
  417. package/Utils/Dashboard/Components/Index.ts +28 -0
  418. package/Utils/Dashboard/DashboardViewConfig.ts +80 -36
  419. package/Utils/Dashboard/GridLayout.ts +523 -0
  420. package/Utils/DataSource/DataSourceQueryText.ts +94 -0
  421. package/Utils/DataSource/DataSourceValueReducer.ts +156 -0
  422. package/Utils/Metrics/MetricResourceAttributeUtil.ts +380 -0
  423. package/Utils/Metrics/RecordingRuleExpression.ts +19 -17
  424. package/Utils/Monitor/MonitorMetricType.ts +44 -2
  425. package/Utils/NetworkDiscovery/ScanTargetUtil.ts +8 -6
  426. package/Utils/StatusPage/GroupHierarchyView.ts +931 -0
  427. package/Utils/StatusPage/GroupNestingLayout.ts +69 -0
  428. package/Utils/StatusPage/GroupTree.ts +157 -66
  429. package/Utils/StatusPage/ResourceExplorer.ts +853 -0
  430. package/Utils/StatusPage/ResourceUptime.ts +37 -2
  431. package/Utils/Telemetry/SavedViewTimeRange.ts +114 -0
  432. package/Utils/Telemetry/TelemetryQueryTimeRange.ts +334 -0
  433. package/build/dist/Models/DatabaseModels/AIRun.js +73 -1
  434. package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
  435. package/build/dist/Models/DatabaseModels/AlertFeed.js +32 -0
  436. package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
  437. package/build/dist/Models/DatabaseModels/Dashboard.js +29 -0
  438. package/build/dist/Models/DatabaseModels/Dashboard.js.map +1 -1
  439. package/build/dist/Models/DatabaseModels/DataSource.js +774 -0
  440. package/build/dist/Models/DatabaseModels/DataSource.js.map +1 -0
  441. package/build/dist/Models/DatabaseModels/Incident.js +2 -0
  442. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  443. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +1 -0
  444. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  445. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +1 -0
  446. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
  447. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
  448. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
  449. package/build/dist/Models/DatabaseModels/IncidentFeed.js +32 -0
  450. package/build/dist/Models/DatabaseModels/IncidentFeed.js.map +1 -1
  451. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +1 -0
  452. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  453. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
  454. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
  455. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  456. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  457. package/build/dist/Models/DatabaseModels/LogSavedView.js +33 -0
  458. package/build/dist/Models/DatabaseModels/LogSavedView.js.map +1 -1
  459. package/build/dist/Models/DatabaseModels/Project.js +241 -11
  460. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  461. package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js +14 -4
  462. package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js.map +1 -1
  463. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -0
  464. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  465. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +1 -0
  466. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  467. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
  468. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
  469. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser.js +4 -4
  470. package/build/dist/Models/DatabaseModels/StatusPage.js +29 -0
  471. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  472. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +1 -0
  473. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
  474. package/build/dist/Models/DatabaseModels/User.js +2 -0
  475. package/build/dist/Models/DatabaseModels/User.js.map +1 -1
  476. package/build/dist/Models/DatabaseModels/UserOnCallLog.js +2 -0
  477. package/build/dist/Models/DatabaseModels/UserOnCallLog.js.map +1 -1
  478. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +2 -0
  479. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
  480. package/build/dist/Models/DatabaseModels/UserSession.js +2 -0
  481. package/build/dist/Models/DatabaseModels/UserSession.js.map +1 -1
  482. package/build/dist/Models/DatabaseModels/UserTotpAuth.js +6 -4
  483. package/build/dist/Models/DatabaseModels/UserTotpAuth.js.map +1 -1
  484. package/build/dist/Models/DatabaseModels/UserWebAuthn.js +5 -3
  485. package/build/dist/Models/DatabaseModels/UserWebAuthn.js.map +1 -1
  486. package/build/dist/Server/API/AIAgentDataAPI.js +28 -46
  487. package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
  488. package/build/dist/Server/API/AIInsightAPI.js +34 -4
  489. package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
  490. package/build/dist/Server/API/AIInvestigationAPI.js +134 -11
  491. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  492. package/build/dist/Server/API/DashboardAPI.js +127 -438
  493. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  494. package/build/dist/Server/API/DataSourceAPI.js +173 -0
  495. package/build/dist/Server/API/DataSourceAPI.js.map +1 -0
  496. package/build/dist/Server/API/StatusPageAPI.js +40 -12
  497. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  498. package/build/dist/Server/API/UserAPI.js +94 -2
  499. package/build/dist/Server/API/UserAPI.js.map +1 -1
  500. package/build/dist/Server/EnvironmentConfig.js +14 -0
  501. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  502. package/build/dist/Server/Infrastructure/ClickhouseConfig.js +61 -1
  503. package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
  504. package/build/dist/Server/Infrastructure/ClickhouseDatabase.js +1 -1
  505. package/build/dist/Server/Infrastructure/ClickhouseDatabase.js.map +1 -1
  506. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.js +22 -0
  507. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.js.map +1 -0
  508. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.js +36 -0
  509. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.js.map +1 -0
  510. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.js +18 -0
  511. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.js.map +1 -0
  512. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js +20 -0
  513. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js.map +1 -0
  514. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.js +19 -0
  515. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.js.map +1 -0
  516. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.js +19 -0
  517. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.js.map +1 -0
  518. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js +19 -0
  519. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js.map +1 -0
  520. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js +64 -0
  521. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js.map +1 -0
  522. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +16 -0
  523. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  524. package/build/dist/Server/Infrastructure/Queue.js +27 -0
  525. package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
  526. package/build/dist/Server/Infrastructure/QueueWorker.js +13 -0
  527. package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
  528. package/build/dist/Server/Middleware/TelemetryIngest.js +8 -3
  529. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  530. package/build/dist/Server/Services/AIInsightService.js +110 -10
  531. package/build/dist/Server/Services/AIInsightService.js.map +1 -1
  532. package/build/dist/Server/Services/AIRunService.js +92 -12
  533. package/build/dist/Server/Services/AIRunService.js.map +1 -1
  534. package/build/dist/Server/Services/AIService.js +88 -21
  535. package/build/dist/Server/Services/AIService.js.map +1 -1
  536. package/build/dist/Server/Services/AlertFeedService.js +3 -0
  537. package/build/dist/Server/Services/AlertFeedService.js.map +1 -1
  538. package/build/dist/Server/Services/AlertService.js +28 -35
  539. package/build/dist/Server/Services/AlertService.js.map +1 -1
  540. package/build/dist/Server/Services/AnalyticsDatabaseService.js +37 -5
  541. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  542. package/build/dist/Server/Services/DataSourceService.js +159 -0
  543. package/build/dist/Server/Services/DataSourceService.js.map +1 -0
  544. package/build/dist/Server/Services/DatabaseService.js +21 -2
  545. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  546. package/build/dist/Server/Services/HostService.js +74 -0
  547. package/build/dist/Server/Services/HostService.js.map +1 -1
  548. package/build/dist/Server/Services/IncidentFeedService.js +3 -0
  549. package/build/dist/Server/Services/IncidentFeedService.js.map +1 -1
  550. package/build/dist/Server/Services/IncidentService.js +35 -24
  551. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  552. package/build/dist/Server/Services/Index.js +2 -0
  553. package/build/dist/Server/Services/Index.js.map +1 -1
  554. package/build/dist/Server/Services/LlmLogService.js +22 -1
  555. package/build/dist/Server/Services/LlmLogService.js.map +1 -1
  556. package/build/dist/Server/Services/MonitorService.js +45 -0
  557. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  558. package/build/dist/Server/Services/MonitorStatusService.js +129 -0
  559. package/build/dist/Server/Services/MonitorStatusService.js.map +1 -1
  560. package/build/dist/Server/Services/OpenTelemetryIngestService.js +222 -159
  561. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  562. package/build/dist/Server/Services/RumSessionPinService.js +7 -15
  563. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  564. package/build/dist/Server/Services/StatusPageSubscriberService.js +10 -0
  565. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  566. package/build/dist/Server/Services/UserService.js +241 -10
  567. package/build/dist/Server/Services/UserService.js.map +1 -1
  568. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +6 -0
  569. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  570. package/build/dist/Server/Types/Database/Permissions/DeletePermission.js +1 -0
  571. package/build/dist/Server/Types/Database/Permissions/DeletePermission.js.map +1 -1
  572. package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +2 -0
  573. package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
  574. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +68 -12
  575. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
  576. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +3 -0
  577. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  578. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js +4 -0
  579. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js.map +1 -1
  580. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +8 -5
  581. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -1
  582. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +63 -24
  583. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
  584. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +2 -0
  585. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  586. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +2 -0
  587. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -1
  588. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +222 -34
  589. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  590. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +22 -19
  591. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  592. package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js +128 -49
  593. package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js.map +1 -1
  594. package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js +170 -31
  595. package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js.map +1 -1
  596. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +22 -19
  597. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  598. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js +97 -0
  599. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js.map +1 -0
  600. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +17 -15
  601. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
  602. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +178 -54
  603. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
  604. package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js +65 -16
  605. package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js.map +1 -1
  606. package/build/dist/Server/Utils/AI/SRE/InstrumentationTaskTrigger.js +33 -9
  607. package/build/dist/Server/Utils/AI/SRE/InstrumentationTaskTrigger.js.map +1 -1
  608. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +9 -5
  609. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js.map +1 -1
  610. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js +121 -41
  611. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -1
  612. package/build/dist/Server/Utils/AI/SRE/InvestigationSubjectLock.js +46 -0
  613. package/build/dist/Server/Utils/AI/SRE/InvestigationSubjectLock.js.map +1 -0
  614. package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js +303 -0
  615. package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js.map +1 -0
  616. package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js +61 -14
  617. package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js.map +1 -1
  618. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js +79 -1
  619. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js.map +1 -1
  620. package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js +21 -0
  621. package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js.map +1 -1
  622. package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -1
  623. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +1261 -0
  624. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -0
  625. package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js +47 -0
  626. package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js.map +1 -0
  627. package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js +42 -0
  628. package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js.map +1 -0
  629. package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js +187 -0
  630. package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js.map +1 -0
  631. package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js +493 -0
  632. package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js.map +1 -0
  633. package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js +399 -0
  634. package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js.map +1 -0
  635. package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js +195 -0
  636. package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js.map +1 -0
  637. package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js +149 -0
  638. package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js.map +1 -0
  639. package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js +362 -0
  640. package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js.map +1 -0
  641. package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js +342 -0
  642. package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js.map +1 -0
  643. package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js +186 -0
  644. package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js.map +1 -0
  645. package/build/dist/Server/Utils/DataSource/EgressGuard.js +305 -0
  646. package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -0
  647. package/build/dist/Server/Utils/DataSource/HttpFetch.js +134 -0
  648. package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -0
  649. package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js +331 -0
  650. package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js.map +1 -0
  651. package/build/dist/Server/Utils/DataSource/SqlResultShaper.js +280 -0
  652. package/build/dist/Server/Utils/DataSource/SqlResultShaper.js.map +1 -0
  653. package/build/dist/Server/Utils/DataSource/Types.js +2 -0
  654. package/build/dist/Server/Utils/DataSource/Types.js.map +1 -0
  655. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +103 -9
  656. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -1
  657. package/build/dist/Server/Utils/InProcessMemo.js +113 -0
  658. package/build/dist/Server/Utils/InProcessMemo.js.map +1 -0
  659. package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js +406 -0
  660. package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js.map +1 -0
  661. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +20 -3
  662. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
  663. package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js +43 -0
  664. package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js.map +1 -1
  665. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +67 -0
  666. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  667. package/build/dist/Server/Utils/Monitor/MonitorResource.js +14 -0
  668. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  669. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +1 -0
  670. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  671. package/build/dist/Server/Utils/PasswordHash.js +25 -4
  672. package/build/dist/Server/Utils/PasswordHash.js.map +1 -1
  673. package/build/dist/Server/Utils/Response.js +9 -1
  674. package/build/dist/Server/Utils/Response.js.map +1 -1
  675. package/build/dist/Server/Utils/SSRFProtection.js +192 -0
  676. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -0
  677. package/build/dist/Server/Utils/StartServer.js +7 -0
  678. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  679. package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js +24 -0
  680. package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js.map +1 -1
  681. package/build/dist/Server/Utils/Telemetry/CaptureSpan.js +25 -11
  682. package/build/dist/Server/Utils/Telemetry/CaptureSpan.js.map +1 -1
  683. package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js +32 -0
  684. package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js.map +1 -0
  685. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +92 -5
  686. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -1
  687. package/build/dist/Server/Utils/Telemetry/Telemetry.js +92 -6
  688. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  689. package/build/dist/Server/Utils/Telemetry.js +93 -6
  690. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  691. package/build/dist/Server/Utils/VendorAssets.js +121 -0
  692. package/build/dist/Server/Utils/VendorAssets.js.map +1 -0
  693. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -4
  694. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  695. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +5 -0
  696. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  697. package/build/dist/Types/AI/AIRunCodeFixRecommendation.js +18 -0
  698. package/build/dist/Types/AI/AIRunCodeFixRecommendation.js.map +1 -0
  699. package/build/dist/Types/AI/CodeFixTaskContext.js +23 -3
  700. package/build/dist/Types/AI/CodeFixTaskContext.js.map +1 -1
  701. package/build/dist/Types/AI/CodeFixTaskType.js +3 -3
  702. package/build/dist/Types/Analytics/RevenueEvent.js +26 -0
  703. package/build/dist/Types/Analytics/RevenueEvent.js.map +1 -0
  704. package/build/dist/Types/Dashboard/DashboardComponentType.js +27 -0
  705. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  706. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js +2 -0
  707. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js.map +1 -0
  708. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js +2 -0
  709. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js.map +1 -0
  710. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js +2 -0
  711. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js.map +1 -0
  712. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js +16 -0
  713. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js.map +1 -0
  714. package/build/dist/Types/DataSource/DataSourceLimits.js +47 -0
  715. package/build/dist/Types/DataSource/DataSourceLimits.js.map +1 -0
  716. package/build/dist/Types/DataSource/DataSourceQueryConfig.js +10 -0
  717. package/build/dist/Types/DataSource/DataSourceQueryConfig.js.map +1 -0
  718. package/build/dist/Types/DataSource/DataSourceTableResult.js +14 -0
  719. package/build/dist/Types/DataSource/DataSourceTableResult.js.map +1 -0
  720. package/build/dist/Types/DataSource/DataSourceType.js +183 -0
  721. package/build/dist/Types/DataSource/DataSourceType.js.map +1 -0
  722. package/build/dist/Types/Date.js +38 -3
  723. package/build/dist/Types/Date.js.map +1 -1
  724. package/build/dist/Types/HashedString.js +4 -3
  725. package/build/dist/Types/HashedString.js.map +1 -1
  726. package/build/dist/Types/Monitor/CriteriaFilter.js +4 -0
  727. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  728. package/build/dist/Types/Monitor/DomainMonitor/DomainLookupMethod.js +24 -0
  729. package/build/dist/Types/Monitor/DomainMonitor/DomainLookupMethod.js.map +1 -0
  730. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +24 -7
  731. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  732. package/build/dist/Types/Monitor/MonitorMetricType.js +7 -0
  733. package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
  734. package/build/dist/Types/Monitor/MonitorStep.js +6 -1
  735. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  736. package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js +15 -0
  737. package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js.map +1 -1
  738. package/build/dist/Types/Monitor/PortMonitor/PortMonitorTimings.js +2 -0
  739. package/build/dist/Types/Monitor/PortMonitor/PortMonitorTimings.js.map +1 -0
  740. package/build/dist/Types/Password.js +56 -0
  741. package/build/dist/Types/Password.js.map +1 -0
  742. package/build/dist/Types/Permission.js +65 -155
  743. package/build/dist/Types/Permission.js.map +1 -1
  744. package/build/dist/Types/UserAuthenticationStatus.js +2 -0
  745. package/build/dist/Types/UserAuthenticationStatus.js.map +1 -0
  746. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +2 -5
  747. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
  748. package/build/dist/UI/Components/ErrorBoundary.js +427 -52
  749. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  750. package/build/dist/UI/Components/Feed/FeedItem.js +2 -2
  751. package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
  752. package/build/dist/UI/Components/Icon/Icon.js +10 -4
  753. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  754. package/build/dist/UI/Components/Modal/ConfirmModal.js +1 -1
  755. package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
  756. package/build/dist/UI/Components/Modal/Modal.js +178 -19
  757. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  758. package/build/dist/UI/Components/Modal/ModalFooter.js +3 -1
  759. package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
  760. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +0 -12
  761. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  762. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +1 -1
  763. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  764. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +12 -2
  765. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
  766. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +197 -66
  767. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  768. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +7 -1
  769. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
  770. package/build/dist/UI/Components/StatusPage/ResourceGrid.js +119 -0
  771. package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -0
  772. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +354 -0
  773. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -0
  774. package/build/dist/UI/Components/StatusPage/ResourceList.js +203 -0
  775. package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -0
  776. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +1 -6
  777. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
  778. package/build/dist/UI/Utils/DownloadFile.js +15 -8
  779. package/build/dist/UI/Utils/DownloadFile.js.map +1 -1
  780. package/build/dist/UI/Utils/ErrorSupportBundle.js +476 -0
  781. package/build/dist/UI/Utils/ErrorSupportBundle.js.map +1 -0
  782. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +20 -15
  783. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  784. package/build/dist/UI/Utils/Project.js +3 -0
  785. package/build/dist/UI/Utils/Project.js.map +1 -1
  786. package/build/dist/UI/Utils/StatusPage.js +4 -4
  787. package/build/dist/UI/Utils/StatusPage.js.map +1 -1
  788. package/build/dist/UI/Utils/TestDataSource.js +39 -0
  789. package/build/dist/UI/Utils/TestDataSource.js.map +1 -0
  790. package/build/dist/UI/Utils/UseDashboardGridDnd.js +551 -0
  791. package/build/dist/UI/Utils/UseDashboardGridDnd.js.map +1 -0
  792. package/build/dist/Utils/Analytics.js +4 -0
  793. package/build/dist/Utils/Analytics.js.map +1 -1
  794. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js +79 -0
  795. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js.map +1 -0
  796. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js +111 -0
  797. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js.map +1 -0
  798. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js +15 -0
  799. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js.map +1 -0
  800. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js +62 -0
  801. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js.map +1 -0
  802. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js +112 -0
  803. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js.map +1 -0
  804. package/build/dist/Utils/Dashboard/Components/Index.js +16 -0
  805. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  806. package/build/dist/Utils/Dashboard/DashboardViewConfig.js +42 -26
  807. package/build/dist/Utils/Dashboard/DashboardViewConfig.js.map +1 -1
  808. package/build/dist/Utils/Dashboard/GridLayout.js +338 -0
  809. package/build/dist/Utils/Dashboard/GridLayout.js.map +1 -0
  810. package/build/dist/Utils/DataSource/DataSourceQueryText.js +64 -0
  811. package/build/dist/Utils/DataSource/DataSourceQueryText.js.map +1 -0
  812. package/build/dist/Utils/DataSource/DataSourceValueReducer.js +124 -0
  813. package/build/dist/Utils/DataSource/DataSourceValueReducer.js.map +1 -0
  814. package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js +281 -0
  815. package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js.map +1 -0
  816. package/build/dist/Utils/Metrics/RecordingRuleExpression.js +19 -17
  817. package/build/dist/Utils/Metrics/RecordingRuleExpression.js.map +1 -1
  818. package/build/dist/Utils/Monitor/MonitorMetricType.js +38 -6
  819. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  820. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +8 -6
  821. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
  822. package/build/dist/Utils/StatusPage/GroupHierarchyView.js +560 -0
  823. package/build/dist/Utils/StatusPage/GroupHierarchyView.js.map +1 -0
  824. package/build/dist/Utils/StatusPage/GroupNestingLayout.js +46 -0
  825. package/build/dist/Utils/StatusPage/GroupNestingLayout.js.map +1 -1
  826. package/build/dist/Utils/StatusPage/GroupTree.js +79 -34
  827. package/build/dist/Utils/StatusPage/GroupTree.js.map +1 -1
  828. package/build/dist/Utils/StatusPage/ResourceExplorer.js +519 -0
  829. package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -0
  830. package/build/dist/Utils/StatusPage/ResourceUptime.js +22 -1
  831. package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
  832. package/build/dist/Utils/Telemetry/SavedViewTimeRange.js +79 -0
  833. package/build/dist/Utils/Telemetry/SavedViewTimeRange.js.map +1 -0
  834. package/build/dist/Utils/Telemetry/TelemetryQueryTimeRange.js +236 -0
  835. package/build/dist/Utils/Telemetry/TelemetryQueryTimeRange.js.map +1 -0
  836. package/jest.config.json +1 -0
  837. package/package.json +5 -1
  838. package/tsconfig.json +12 -10
@@ -0,0 +1,1695 @@
1
+ import {
2
+ afterEach,
3
+ beforeEach,
4
+ describe,
5
+ expect,
6
+ jest,
7
+ test,
8
+ } from "@jest/globals";
9
+ import "@testing-library/jest-dom";
10
+ import {
11
+ act,
12
+ cleanup,
13
+ fireEvent,
14
+ render,
15
+ screen,
16
+ } from "@testing-library/react";
17
+ import * as React from "react";
18
+ import getJestMockFunction, { MockFunction } from "../../MockType";
19
+
20
+ /*
21
+ * InvestigationPanel joins three independently changing pieces of state: the
22
+ * live AIRun, the report posted just after completion, and the persisted
23
+ * recommendation that authorizes a code-fix action. These tests drive real
24
+ * React effects and timers so completion, navigation, and request races stay
25
+ * covered rather than testing only static snapshots.
26
+ */
27
+
28
+ const postMock: MockFunction = getJestMockFunction();
29
+ const getFriendlyMessageMock: MockFunction = getJestMockFunction();
30
+ const getCommonHeadersMock: MockFunction = getJestMockFunction();
31
+ const markdownViewerMock: MockFunction = getJestMockFunction();
32
+ const activityFeedMock: MockFunction = getJestMockFunction();
33
+
34
+ jest.mock("../../../UI/Utils/API/API", () => {
35
+ return {
36
+ __esModule: true,
37
+ default: {
38
+ post: (...args: Array<unknown>) => {
39
+ return postMock(...args);
40
+ },
41
+ getFriendlyMessage: (...args: Array<unknown>) => {
42
+ return getFriendlyMessageMock(...args);
43
+ },
44
+ },
45
+ };
46
+ });
47
+
48
+ jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
49
+ return {
50
+ __esModule: true,
51
+ default: {
52
+ getCommonHeaders: (...args: Array<unknown>) => {
53
+ return getCommonHeadersMock(...args);
54
+ },
55
+ },
56
+ };
57
+ });
58
+
59
+ /*
60
+ * Record MarkdownViewer's props so safeMode remains an asserted part of the
61
+ * contract even when the Common Jest config replaces its markdown renderer.
62
+ */
63
+ jest.mock("../../../UI/Components/Markdown.tsx/MarkdownViewer", () => {
64
+ return {
65
+ __esModule: true,
66
+ default: (props: MarkdownViewerProps): React.ReactElement => {
67
+ markdownViewerMock(props);
68
+ return React.createElement(
69
+ "div",
70
+ { "data-testid": "investigation-markdown" },
71
+ props.text,
72
+ );
73
+ },
74
+ };
75
+ });
76
+
77
+ /*
78
+ * The feed owns the "will this draw any steps?" predicate, because several
79
+ * event types only close a step an earlier event opened. The panel asks it
80
+ * instead of counting raw events, so the mock has to answer too — and
81
+ * hasRenderableActivityMock lets a test make the two disagree, which is the
82
+ * case that used to render an empty framed box.
83
+ */
84
+ const hasRenderableActivityMock: MockFunction = getJestMockFunction();
85
+
86
+ jest.mock(
87
+ "../../../../App/FeatureSet/Dashboard/src/Components/AIChat/ChatActivityFeed",
88
+ () => {
89
+ return {
90
+ __esModule: true,
91
+ default: (props: ActivityFeedProps): React.ReactElement => {
92
+ activityFeedMock(props);
93
+ return React.createElement("div", {
94
+ "data-testid": "investigation-activity",
95
+ });
96
+ },
97
+ hasRenderableActivity: (events: Array<AIRunEvent>): boolean => {
98
+ return hasRenderableActivityMock(events) as boolean;
99
+ },
100
+ };
101
+ },
102
+ );
103
+
104
+ import InvestigationPanel, {
105
+ InvestigationSubjectType,
106
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/AI/InvestigationPanel";
107
+ import AIRunEvent from "../../../Models/DatabaseModels/AIRunEvent";
108
+ import HTTPErrorResponse from "../../../Types/API/HTTPErrorResponse";
109
+ import AIRunCodeFixRecommendation from "../../../Types/AI/AIRunCodeFixRecommendation";
110
+ import AIRunEventType from "../../../Types/AI/AIRunEventType";
111
+ import AIRunStatus from "../../../Types/AI/AIRunStatus";
112
+ import { JSONArray, JSONObject } from "../../../Types/JSON";
113
+ import ObjectID from "../../../Types/ObjectID";
114
+
115
+ interface MarkdownViewerProps {
116
+ text: string;
117
+ safeMode?: boolean | undefined;
118
+ }
119
+
120
+ interface ActivityFeedProps {
121
+ events: Array<AIRunEvent>;
122
+ title?: string | undefined;
123
+ showLiveIndicator?: boolean | undefined;
124
+ maxVisibleSteps?: number | undefined;
125
+ hideChrome?: boolean | undefined;
126
+ }
127
+
128
+ interface InvestigationPayloadOptions {
129
+ status: AIRunStatus;
130
+ runId?: string | undefined;
131
+ events?: JSONArray | undefined;
132
+ analysisMarkdown?: string | null | undefined;
133
+ analysisTldr?: string | null | undefined;
134
+ isAnalysisPending?: boolean | undefined;
135
+ errorMessage?: string | null | undefined;
136
+ toolCallCount?: number | undefined;
137
+ totalTokens?: number | undefined;
138
+ humanVerdict?: string | null | undefined;
139
+ codeFixRecommendation?: AIRunCodeFixRecommendation | undefined;
140
+ completedAt?: string | undefined;
141
+ }
142
+
143
+ interface ApiResponse {
144
+ data: JSONObject;
145
+ }
146
+
147
+ interface PostRequest {
148
+ url: { toString: () => string };
149
+ data: JSONObject;
150
+ }
151
+
152
+ interface Deferred<T> {
153
+ promise: Promise<T>;
154
+ resolve: (value: T) => void;
155
+ }
156
+
157
+ const POLL_INTERVAL_MS: number = 2500;
158
+ const SETTLED_POLL_INTERVAL_MS: number = 30_000;
159
+ const RECOMMENDATION_SETTLEMENT_MAX_AGE_MS: number = 3 * 60 * 1000;
160
+ const MAX_RECOMMENDATION_POLL_RESPONSES: number = Math.ceil(
161
+ RECOMMENDATION_SETTLEMENT_MAX_AGE_MS / POLL_INTERVAL_MS,
162
+ );
163
+ const COMPLETED_AT: string = "2026-08-07T12:00:00.000Z";
164
+ const OLD_COMPLETED_AT: string = "2026-08-07T11:56:59.000Z";
165
+ const RUN_ID: string = "11111111-1111-4111-8111-111111111111";
166
+ const FIX_RUN_ID: string = "22222222-2222-4222-8222-222222222222";
167
+ const NEXT_RUN_ID: string = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa";
168
+ const INCIDENT_ID: ObjectID = new ObjectID(
169
+ "33333333-3333-4333-8333-333333333333",
170
+ );
171
+ const ALERT_ID: ObjectID = new ObjectID("44444444-4444-4444-8444-444444444444");
172
+ const EVENT_ID: string = "55555555-5555-4555-8555-555555555555";
173
+ const ANALYSIS: string =
174
+ "## Root cause\n\nThe database connection pool was exhausted.";
175
+ const TLDR: string =
176
+ "Checkout is returning 500s because the database connection pool is exhausted.";
177
+
178
+ const activityEvent: JSONObject = {
179
+ _id: EVENT_ID,
180
+ sequence: 1,
181
+ eventType: AIRunEventType.ToolCallStarted,
182
+ toolName: "search_logs",
183
+ createdAt: new Date("2026-08-07T10:00:00.000Z"),
184
+ };
185
+
186
+ function investigationPayload(
187
+ options: InvestigationPayloadOptions,
188
+ ): JSONObject {
189
+ const run: JSONObject = {
190
+ _id: options.runId || RUN_ID,
191
+ status: options.status,
192
+ errorMessage: options.errorMessage ?? null,
193
+ toolCallCount: options.toolCallCount ?? 0,
194
+ totalTokens: options.totalTokens ?? 0,
195
+ humanVerdict: options.humanVerdict ?? null,
196
+ };
197
+
198
+ if (options.codeFixRecommendation !== undefined) {
199
+ run["codeFixRecommendation"] = options.codeFixRecommendation;
200
+ }
201
+
202
+ if (options.completedAt !== undefined) {
203
+ run["completedAt"] = options.completedAt;
204
+ }
205
+
206
+ return {
207
+ run,
208
+ events: options.events || [],
209
+ analysisMarkdown: options.analysisMarkdown ?? null,
210
+ analysisTldr: options.analysisTldr ?? null,
211
+ isAnalysisPending: options.isAnalysisPending === true,
212
+ };
213
+ }
214
+
215
+ function successfulResponse(payload: JSONObject): ApiResponse {
216
+ return { data: payload };
217
+ }
218
+
219
+ function noInvestigationResponse(): ApiResponse {
220
+ return {
221
+ data: {
222
+ run: null,
223
+ events: [],
224
+ analysisMarkdown: null,
225
+ analysisTldr: null,
226
+ isAnalysisPending: false,
227
+ },
228
+ };
229
+ }
230
+
231
+ function completedResponse(
232
+ overrides: Partial<InvestigationPayloadOptions> = {},
233
+ ): ApiResponse {
234
+ return successfulResponse(
235
+ investigationPayload({
236
+ status: AIRunStatus.Completed,
237
+ analysisMarkdown: ANALYSIS,
238
+ events: [activityEvent],
239
+ toolCallCount: 2,
240
+ totalTokens: 1234,
241
+ codeFixRecommendation: AIRunCodeFixRecommendation.Recommended,
242
+ completedAt: COMPLETED_AT,
243
+ ...overrides,
244
+ }),
245
+ );
246
+ }
247
+
248
+ function createDeferred<T>(): Deferred<T> {
249
+ let resolvePromise: ((value: T) => void) | undefined;
250
+ const promise: Promise<T> = new Promise<T>((resolve: (value: T) => void) => {
251
+ resolvePromise = resolve;
252
+ });
253
+
254
+ return {
255
+ promise,
256
+ resolve: (value: T): void => {
257
+ resolvePromise!(value);
258
+ },
259
+ };
260
+ }
261
+
262
+ function renderPanel(data?: {
263
+ subjectType?: InvestigationSubjectType | undefined;
264
+ subjectId?: ObjectID | undefined;
265
+ onAnalysisAvailable?: (() => void) | undefined;
266
+ onStatusChange?: ((status: AIRunStatus | null) => void) | undefined;
267
+ }): ReturnType<typeof render> {
268
+ return render(
269
+ <InvestigationPanel
270
+ subjectType={data?.subjectType || "incident"}
271
+ subjectId={data?.subjectId || INCIDENT_ID}
272
+ onAnalysisAvailable={data?.onAnalysisAvailable}
273
+ onStatusChange={data?.onStatusChange}
274
+ />,
275
+ );
276
+ }
277
+
278
+ /* Let the awaits inside fetchData and the following React effects settle. */
279
+ async function flush(): Promise<void> {
280
+ await act(async (): Promise<void> => {
281
+ await Promise.resolve();
282
+ await Promise.resolve();
283
+ await Promise.resolve();
284
+ await Promise.resolve();
285
+ });
286
+ }
287
+
288
+ async function tick(milliseconds: number): Promise<void> {
289
+ await act(async (): Promise<void> => {
290
+ jest.advanceTimersByTime(milliseconds);
291
+ await Promise.resolve();
292
+ await Promise.resolve();
293
+ await Promise.resolve();
294
+ await Promise.resolve();
295
+ });
296
+ }
297
+
298
+ async function advanceFastPolls(count: number = 1): Promise<void> {
299
+ for (let index: number = 0; index < count; index++) {
300
+ await tick(POLL_INTERVAL_MS);
301
+ }
302
+ }
303
+
304
+ async function resolveDeferred<T>(
305
+ deferred: Deferred<T>,
306
+ value: T,
307
+ ): Promise<void> {
308
+ await act(async (): Promise<void> => {
309
+ deferred.resolve(value);
310
+ await Promise.resolve();
311
+ await Promise.resolve();
312
+ await Promise.resolve();
313
+ await Promise.resolve();
314
+ });
315
+ }
316
+
317
+ function postRequestAt(index: number): PostRequest {
318
+ return postMock.mock.calls[index]![0] as PostRequest;
319
+ }
320
+
321
+ function requestPath(index: number): string {
322
+ return postRequestAt(index).url.toString();
323
+ }
324
+
325
+ function fixButton(): HTMLElement | null {
326
+ return screen.queryByRole("button", {
327
+ name: "Open Fix PR from this analysis",
328
+ });
329
+ }
330
+
331
+ function lastActivityProps(): ActivityFeedProps {
332
+ const calls: Array<Array<ActivityFeedProps>> = activityFeedMock.mock
333
+ .calls as Array<Array<ActivityFeedProps>>;
334
+ return calls[calls.length - 1]![0]!;
335
+ }
336
+
337
+ beforeEach(() => {
338
+ jest.useFakeTimers();
339
+ jest.setSystemTime(new Date(COMPLETED_AT));
340
+ getCommonHeadersMock.mockReturnValue({});
341
+ /*
342
+ * Default to the real component's behaviour for the ordinary event shapes
343
+ * these tests use: every step-opening event draws a step.
344
+ */
345
+ hasRenderableActivityMock.mockImplementation(
346
+ (events: Array<AIRunEvent>): boolean => {
347
+ return events.length > 0;
348
+ },
349
+ );
350
+ getFriendlyMessageMock.mockImplementation((error: unknown): string => {
351
+ if (
352
+ typeof error === "object" &&
353
+ error !== null &&
354
+ "message" in error &&
355
+ typeof (error as { message?: unknown }).message === "string"
356
+ ) {
357
+ return (error as { message: string }).message;
358
+ }
359
+
360
+ return "Request failed";
361
+ });
362
+ });
363
+
364
+ afterEach(() => {
365
+ cleanup();
366
+ jest.clearAllTimers();
367
+ jest.useRealTimers();
368
+ postMock.mockReset();
369
+ getFriendlyMessageMock.mockReset();
370
+ getCommonHeadersMock.mockReset();
371
+ markdownViewerMock.mockReset();
372
+ activityFeedMock.mockReset();
373
+ hasRenderableActivityMock.mockReset();
374
+ });
375
+
376
+ describe("InvestigationPanel report lifecycle", () => {
377
+ test("renders nothing and briefly discovers a run when none exists yet", async () => {
378
+ postMock.mockResolvedValue(noInvestigationResponse() as never);
379
+
380
+ const { container } = renderPanel();
381
+ await flush();
382
+
383
+ expect(container).toBeEmptyDOMElement();
384
+ expect(jest.getTimerCount()).toBe(1);
385
+
386
+ await advanceFastPolls(4);
387
+ expect(postMock).toHaveBeenCalledTimes(5);
388
+ expect(jest.getTimerCount()).toBe(1);
389
+ });
390
+
391
+ test("shows live activity and requests the incident endpoint", async () => {
392
+ postMock.mockResolvedValue(
393
+ successfulResponse(
394
+ investigationPayload({
395
+ status: AIRunStatus.Running,
396
+ events: [activityEvent],
397
+ }),
398
+ ) as never,
399
+ );
400
+
401
+ renderPanel();
402
+ await flush();
403
+
404
+ expect(screen.getByText("Investigating…")).toBeInTheDocument();
405
+ expect(screen.getByTestId("investigation-activity")).toBeInTheDocument();
406
+ expect(lastActivityProps().events).toHaveLength(1);
407
+ /*
408
+ * The panel frames the feed itself, so the feed drops its own bubble and
409
+ * heading — the "Investigating…" wording must appear exactly once.
410
+ */
411
+ expect(lastActivityProps().hideChrome).toBe(true);
412
+ expect(screen.getAllByText("Investigating…")).toHaveLength(1);
413
+ expect(
414
+ document.querySelector('[class~="motion-safe:animate-ping"]'),
415
+ ).not.toBeNull();
416
+
417
+ const request: PostRequest = postRequestAt(0);
418
+ expect(request.url.toString()).toContain("/ai-investigation/incident");
419
+ expect(request.data).toEqual({ incidentId: INCIDENT_ID.toString() });
420
+ expect(getCommonHeadersMock).toHaveBeenCalledTimes(1);
421
+ });
422
+
423
+ test("renders the completed report safely and demotes activity to a disclosure", async () => {
424
+ const onAnalysisAvailable: MockFunction = getJestMockFunction();
425
+ postMock.mockResolvedValue(completedResponse() as never);
426
+
427
+ renderPanel({ onAnalysisAvailable });
428
+ await flush();
429
+
430
+ expect(screen.getByText("Investigation complete")).toBeInTheDocument();
431
+ expect(screen.getByLabelText("Investigation report")).toBeInTheDocument();
432
+ expect(screen.getByTestId("investigation-markdown")).toHaveTextContent(
433
+ "The database connection pool was exhausted.",
434
+ );
435
+ expect(markdownViewerMock).toHaveBeenCalledWith({
436
+ text: ANALYSIS,
437
+ safeMode: true,
438
+ });
439
+ expect(screen.getByText("Investigation activity")).toBeInTheDocument();
440
+ expect(lastActivityProps()).toEqual(
441
+ expect.objectContaining({
442
+ hideChrome: true,
443
+ showLiveIndicator: false,
444
+ maxVisibleSteps: 10,
445
+ }),
446
+ );
447
+
448
+ const usage: HTMLElement = screen.getByLabelText("Investigation usage");
449
+ expect(usage).toHaveTextContent("2 telemetry queries");
450
+ expect(usage).toHaveTextContent("1,234 tokens");
451
+ expect(usage).toHaveTextContent(
452
+ "Read-only — nothing in your systems was changed",
453
+ );
454
+ expect(fixButton()).toBeEnabled();
455
+ expect(screen.getByRole("button", { name: "Confirmed" })).toBeEnabled();
456
+ expect(screen.getByRole("button", { name: "Rejected" })).toBeEnabled();
457
+ expect(onAnalysisAvailable).toHaveBeenCalledTimes(1);
458
+ expect(jest.getTimerCount()).toBe(1);
459
+ });
460
+
461
+ test("keeps polling across Completed until the same run report appears", async () => {
462
+ const onAnalysisAvailable: MockFunction = getJestMockFunction();
463
+ postMock
464
+ .mockResolvedValueOnce(
465
+ completedResponse({
466
+ analysisMarkdown: null,
467
+ isAnalysisPending: true,
468
+ }) as never,
469
+ )
470
+ .mockResolvedValueOnce(
471
+ completedResponse({
472
+ analysisMarkdown: ANALYSIS,
473
+ isAnalysisPending: false,
474
+ }) as never,
475
+ );
476
+
477
+ renderPanel({ onAnalysisAvailable });
478
+ await flush();
479
+
480
+ expect(screen.getByText("Preparing investigation report…")).toBeVisible();
481
+ expect(screen.getByText("Preparing the final report")).toBeVisible();
482
+ expect(screen.queryByTestId("investigation-markdown")).toBeNull();
483
+ expect(onAnalysisAvailable).not.toHaveBeenCalled();
484
+
485
+ /* Status, run id, events and recommendation remain identical. */
486
+ await tick(POLL_INTERVAL_MS);
487
+
488
+ expect(screen.getByText("Investigation complete")).toBeVisible();
489
+ expect(screen.getByTestId("investigation-markdown")).toHaveTextContent(
490
+ "The database connection pool was exhausted.",
491
+ );
492
+ expect(postMock).toHaveBeenCalledTimes(2);
493
+ expect(onAnalysisAvailable).toHaveBeenCalledTimes(1);
494
+
495
+ await tick(POLL_INTERVAL_MS * 4);
496
+ expect(postMock).toHaveBeenCalledTimes(2);
497
+ });
498
+
499
+ test("does not overlap a slow active-run poll", async () => {
500
+ const slowPoll: Deferred<ApiResponse> = createDeferred<ApiResponse>();
501
+ postMock
502
+ .mockResolvedValueOnce(
503
+ successfulResponse(
504
+ investigationPayload({ status: AIRunStatus.Running }),
505
+ ) as never,
506
+ )
507
+ .mockReturnValueOnce(slowPoll.promise as never)
508
+ .mockResolvedValue(completedResponse() as never);
509
+
510
+ const view: ReturnType<typeof render> = renderPanel();
511
+ await flush();
512
+ await tick(POLL_INTERVAL_MS);
513
+
514
+ expect(postMock).toHaveBeenCalledTimes(2);
515
+ expect(screen.getByText("Investigating…")).toBeVisible();
516
+
517
+ view.rerender(
518
+ <InvestigationPanel
519
+ subjectType="incident"
520
+ subjectId={new ObjectID(INCIDENT_ID.toString())}
521
+ />,
522
+ );
523
+ await flush();
524
+ await tick(POLL_INTERVAL_MS * 3);
525
+ expect(postMock).toHaveBeenCalledTimes(2);
526
+
527
+ await resolveDeferred(slowPoll, completedResponse());
528
+ expect(screen.getByText("Investigation complete")).toBeVisible();
529
+ expect(postMock).toHaveBeenCalledTimes(2);
530
+ });
531
+
532
+ test("ignores a previous subject response after route navigation", async () => {
533
+ const previousSubject: Deferred<ApiResponse> =
534
+ createDeferred<ApiResponse>();
535
+ const statuses: Array<AIRunStatus | null> = [];
536
+ postMock
537
+ .mockReturnValueOnce(previousSubject.promise as never)
538
+ .mockResolvedValueOnce(completedResponse() as never);
539
+
540
+ const view: ReturnType<typeof render> = renderPanel({
541
+ onStatusChange: (status: AIRunStatus | null): void => {
542
+ statuses.push(status);
543
+ },
544
+ });
545
+ await flush();
546
+
547
+ view.rerender(
548
+ <InvestigationPanel
549
+ subjectType="alert"
550
+ subjectId={ALERT_ID}
551
+ onStatusChange={(status: AIRunStatus | null): void => {
552
+ statuses.push(status);
553
+ }}
554
+ />,
555
+ );
556
+ await flush();
557
+
558
+ expect(screen.getByText("Investigation complete")).toBeVisible();
559
+ expect(postRequestAt(1).data).toEqual({ alertId: ALERT_ID.toString() });
560
+
561
+ await resolveDeferred(
562
+ previousSubject,
563
+ successfulResponse(investigationPayload({ status: AIRunStatus.Running })),
564
+ );
565
+
566
+ expect(screen.getByText("Investigation complete")).toBeVisible();
567
+ expect(screen.queryByText("Investigating…")).toBeNull();
568
+ expect(statuses[statuses.length - 1]).toBe(AIRunStatus.Completed);
569
+ });
570
+
571
+ test("does not notify a new subject with the previous report", async () => {
572
+ const nextSubject: Deferred<ApiResponse> = createDeferred<ApiResponse>();
573
+ const onAnalysisAvailable: MockFunction = getJestMockFunction();
574
+ const nextAnalysis: string =
575
+ "## Alert root cause\n\nThe upstream dependency rejected requests.";
576
+ postMock
577
+ .mockResolvedValueOnce(completedResponse() as never)
578
+ .mockReturnValueOnce(nextSubject.promise as never);
579
+
580
+ const view: ReturnType<typeof render> = renderPanel({
581
+ onAnalysisAvailable,
582
+ });
583
+ await flush();
584
+ expect(onAnalysisAvailable).toHaveBeenCalledTimes(1);
585
+ onAnalysisAvailable.mockClear();
586
+
587
+ view.rerender(
588
+ <InvestigationPanel
589
+ subjectType="alert"
590
+ subjectId={ALERT_ID}
591
+ onAnalysisAvailable={onAnalysisAvailable}
592
+ />,
593
+ );
594
+ await flush();
595
+
596
+ expect(view.container).toBeEmptyDOMElement();
597
+ expect(onAnalysisAvailable).not.toHaveBeenCalled();
598
+
599
+ await resolveDeferred(
600
+ nextSubject,
601
+ completedResponse({ runId: NEXT_RUN_ID, analysisMarkdown: nextAnalysis }),
602
+ );
603
+
604
+ expect(screen.getByTestId("investigation-markdown")).toHaveTextContent(
605
+ "The upstream dependency rejected requests.",
606
+ );
607
+ expect(onAnalysisAvailable).toHaveBeenCalledTimes(1);
608
+ });
609
+
610
+ test("shows the no-report outcome and disables analysis-only actions", async () => {
611
+ postMock.mockResolvedValue(
612
+ completedResponse({
613
+ analysisMarkdown: null,
614
+ isAnalysisPending: false,
615
+ events: [],
616
+ toolCallCount: 0,
617
+ totalTokens: 0,
618
+ }) as never,
619
+ );
620
+
621
+ renderPanel();
622
+ await flush();
623
+
624
+ expect(
625
+ screen.getByText("Investigation completed without a report"),
626
+ ).toBeVisible();
627
+ expect(
628
+ screen.getByText("No investigation report was published."),
629
+ ).toBeVisible();
630
+ expect(fixButton()).toBeDisabled();
631
+ expect(screen.getByRole("button", { name: "Confirmed" })).toBeDisabled();
632
+ expect(screen.getByRole("button", { name: "Rejected" })).toBeDisabled();
633
+ });
634
+
635
+ test("shows a terminal failure without completed actions", async () => {
636
+ postMock.mockResolvedValue(
637
+ successfulResponse(
638
+ investigationPayload({
639
+ status: AIRunStatus.Error,
640
+ errorMessage: "The model provider timed out.",
641
+ events: [activityEvent],
642
+ }),
643
+ ) as never,
644
+ );
645
+
646
+ renderPanel();
647
+ await flush();
648
+
649
+ expect(screen.getByText("Investigation did not finish")).toBeVisible();
650
+ expect(screen.getByText("The model provider timed out.")).toBeVisible();
651
+ expect(
652
+ screen.getByText("The investigation stopped before it could report."),
653
+ ).toBeVisible();
654
+ expect(
655
+ screen.getByText("What the investigation got through"),
656
+ ).toBeVisible();
657
+ expect(lastActivityProps()).toEqual(
658
+ expect.objectContaining({
659
+ hideChrome: true,
660
+ showLiveIndicator: false,
661
+ }),
662
+ );
663
+ expect(fixButton()).toBeNull();
664
+ expect(screen.queryByText("Rate this investigation")).toBeNull();
665
+ });
666
+
667
+ test("uses the alert endpoint and alert id", async () => {
668
+ postMock.mockResolvedValue(
669
+ successfulResponse(
670
+ investigationPayload({ status: AIRunStatus.Running }),
671
+ ) as never,
672
+ );
673
+
674
+ renderPanel({ subjectType: "alert", subjectId: ALERT_ID });
675
+ await flush();
676
+
677
+ expect(requestPath(0)).toContain("/ai-investigation/alert");
678
+ expect(postRequestAt(0).data).toEqual({ alertId: ALERT_ID.toString() });
679
+ });
680
+ });
681
+
682
+ /*
683
+ * Every state of the card has to stand on its own: a responder can land on it
684
+ * while the run is queued, live, failed, or finished. These tests pin the
685
+ * framing and the plain-language explanation each state owes the reader.
686
+ */
687
+ describe("InvestigationPanel card states", () => {
688
+ test("frames the live run and says what it is doing and that it cannot change anything", async () => {
689
+ postMock.mockResolvedValue(
690
+ successfulResponse(
691
+ investigationPayload({
692
+ status: AIRunStatus.Running,
693
+ events: [activityEvent],
694
+ }),
695
+ ) as never,
696
+ );
697
+
698
+ renderPanel();
699
+ await flush();
700
+
701
+ const live: HTMLElement = screen.getByLabelText("Live investigation");
702
+ expect(live).toHaveTextContent("OneUptime AI is investigating");
703
+ expect(live).toHaveTextContent("Read-only — nothing is changed");
704
+ expect(live).toContainElement(screen.getByTestId("investigation-activity"));
705
+ });
706
+
707
+ test("explains the wait while the run is only queued", async () => {
708
+ postMock.mockResolvedValue(
709
+ successfulResponse(
710
+ investigationPayload({ status: AIRunStatus.Queued }),
711
+ ) as never,
712
+ );
713
+
714
+ renderPanel();
715
+ await flush();
716
+
717
+ expect(screen.getByLabelText("Live investigation")).toHaveTextContent(
718
+ "Waiting for a worker to pick this up",
719
+ );
720
+ expect(screen.getByText("Starting investigation…")).toBeVisible();
721
+ });
722
+
723
+ test("puts a copy control on the report so it can be pasted elsewhere", async () => {
724
+ postMock.mockResolvedValue(completedResponse() as never);
725
+
726
+ renderPanel();
727
+ await flush();
728
+
729
+ const report: HTMLElement = screen.getByLabelText("Investigation report");
730
+ expect(report).toHaveTextContent("AI generated");
731
+ expect(report).toContainElement(
732
+ screen.getByRole("button", { name: "Copy report" }),
733
+ );
734
+ });
735
+
736
+ /*
737
+ * A queued run carries a stats object whose counts are all zero, so an
738
+ * ungated strip would tell the reader the AI ran zero queries and changed
739
+ * nothing — a past-tense report on work that has not started, directly
740
+ * under "waiting for a worker".
741
+ */
742
+ test.each([AIRunStatus.Running, AIRunStatus.Queued])(
743
+ "shows no usage strip while a %s run has not produced anything yet",
744
+ async (status: AIRunStatus) => {
745
+ postMock.mockResolvedValue(
746
+ successfulResponse(
747
+ investigationPayload({
748
+ status,
749
+ toolCallCount: 0,
750
+ totalTokens: 0,
751
+ }),
752
+ ) as never,
753
+ );
754
+
755
+ renderPanel();
756
+ await flush();
757
+
758
+ expect(screen.queryByLabelText("Investigation usage")).toBeNull();
759
+ expect(
760
+ screen.queryByText(/nothing in your systems was changed/),
761
+ ).toBeNull();
762
+ },
763
+ );
764
+
765
+ /*
766
+ * Events and rendered steps are not the same thing: RunFailed and the
767
+ * completion halves of tool/LLM calls only close a step an earlier event
768
+ * opened. A run whose RunStarted failed to persist and then failed outright
769
+ * carries one event and draws nothing, and counting raw events would frame
770
+ * an empty panel instead of saying what happened.
771
+ */
772
+ test("explains an empty trail instead of framing a blank panel", async () => {
773
+ hasRenderableActivityMock.mockReturnValue(false);
774
+ postMock.mockResolvedValue(
775
+ successfulResponse(
776
+ investigationPayload({
777
+ status: AIRunStatus.Error,
778
+ errorMessage: "The provider timed out.",
779
+ events: [activityEvent],
780
+ }),
781
+ ) as never,
782
+ );
783
+
784
+ renderPanel();
785
+ await flush();
786
+
787
+ expect(screen.queryByTestId("investigation-activity")).toBeNull();
788
+ expect(
789
+ screen.getByText("No investigation steps were recorded."),
790
+ ).toBeVisible();
791
+ });
792
+
793
+ test("hides the completed activity disclosure when no step would render", async () => {
794
+ hasRenderableActivityMock.mockReturnValue(false);
795
+ postMock.mockResolvedValue(completedResponse() as never);
796
+
797
+ renderPanel();
798
+ await flush();
799
+
800
+ expect(screen.getByLabelText("Investigation report")).toBeInTheDocument();
801
+ expect(screen.queryByText("Investigation activity")).toBeNull();
802
+ });
803
+
804
+ test("reports a single query without pluralising it", async () => {
805
+ postMock.mockResolvedValue(
806
+ completedResponse({ toolCallCount: 1, totalTokens: 0 }) as never,
807
+ );
808
+
809
+ renderPanel();
810
+ await flush();
811
+
812
+ const usage: HTMLElement = screen.getByLabelText("Investigation usage");
813
+ expect(usage).toHaveTextContent("1 telemetry query");
814
+ expect(usage).not.toHaveTextContent("tokens");
815
+ });
816
+ });
817
+
818
+ /*
819
+ * The TL;DR is the first thing on the card, and it is AI prose about the
820
+ * report below it. So it must never appear without that report, must never be
821
+ * rendered as markdown, and must be dropped the instant the panel changes
822
+ * subject — a summary of the previous incident is worse than no summary.
823
+ */
824
+ describe("InvestigationPanel TL;DR", () => {
825
+ function markdownTexts(): Array<string> {
826
+ return (
827
+ markdownViewerMock.mock.calls as Array<Array<MarkdownViewerProps>>
828
+ ).map((call: Array<MarkdownViewerProps>): string => {
829
+ return call[0]!.text;
830
+ });
831
+ }
832
+
833
+ test("leads the completed card with the AI summary, as plain text above the report", async () => {
834
+ postMock.mockResolvedValue(
835
+ completedResponse({ analysisTldr: TLDR }) as never,
836
+ );
837
+
838
+ renderPanel();
839
+ await flush();
840
+
841
+ const summary: HTMLElement = screen.getByLabelText("Investigation summary");
842
+ expect(summary).toHaveTextContent("TL;DR");
843
+ expect(summary).toHaveTextContent(TLDR);
844
+
845
+ // The summary is never routed through the markdown renderer.
846
+ expect(markdownTexts().length).toBeGreaterThan(0);
847
+ expect(
848
+ markdownTexts().every((text: string): boolean => {
849
+ return text === ANALYSIS;
850
+ }),
851
+ ).toBe(true);
852
+
853
+ // It sits above the report it summarizes.
854
+ const report: HTMLElement = screen.getByLabelText("Investigation report");
855
+ expect(
856
+ summary.compareDocumentPosition(report) &
857
+ Node.DOCUMENT_POSITION_FOLLOWING,
858
+ ).toBeTruthy();
859
+ });
860
+
861
+ test("omits the block for a run that has no stored summary", async () => {
862
+ postMock.mockResolvedValue(completedResponse() as never);
863
+
864
+ renderPanel();
865
+ await flush();
866
+
867
+ expect(screen.getByLabelText("Investigation report")).toBeInTheDocument();
868
+ expect(screen.queryByLabelText("Investigation summary")).toBeNull();
869
+ expect(screen.queryByText("TL;DR")).toBeNull();
870
+ });
871
+
872
+ test("omits a blank summary rather than rendering an empty banner", async () => {
873
+ postMock.mockResolvedValue(
874
+ completedResponse({ analysisTldr: " \n " }) as never,
875
+ );
876
+
877
+ renderPanel();
878
+ await flush();
879
+
880
+ expect(screen.queryByLabelText("Investigation summary")).toBeNull();
881
+ });
882
+
883
+ test("never shows a summary before the report it describes is published", async () => {
884
+ postMock.mockResolvedValue(
885
+ completedResponse({
886
+ analysisMarkdown: null,
887
+ analysisTldr: TLDR,
888
+ isAnalysisPending: true,
889
+ }) as never,
890
+ );
891
+
892
+ renderPanel();
893
+ await flush();
894
+
895
+ expect(screen.queryByLabelText("Investigation summary")).toBeNull();
896
+ expect(screen.queryByText(TLDR)).toBeNull();
897
+ expect(screen.getByText("Preparing the final report")).toBeVisible();
898
+ });
899
+
900
+ test("appears on the poll that publishes the report", async () => {
901
+ postMock
902
+ .mockResolvedValueOnce(
903
+ completedResponse({
904
+ analysisMarkdown: null,
905
+ analysisTldr: null,
906
+ isAnalysisPending: true,
907
+ }) as never,
908
+ )
909
+ .mockResolvedValue(completedResponse({ analysisTldr: TLDR }) as never);
910
+
911
+ renderPanel();
912
+ await flush();
913
+ expect(screen.queryByLabelText("Investigation summary")).toBeNull();
914
+
915
+ await advanceFastPolls();
916
+
917
+ expect(screen.getByLabelText("Investigation summary")).toHaveTextContent(
918
+ TLDR,
919
+ );
920
+ });
921
+
922
+ test("is not shown for a run that never completed", async () => {
923
+ postMock.mockResolvedValue(
924
+ successfulResponse(
925
+ investigationPayload({
926
+ status: AIRunStatus.Error,
927
+ analysisTldr: TLDR,
928
+ errorMessage: "The provider timed out.",
929
+ }),
930
+ ) as never,
931
+ );
932
+
933
+ renderPanel();
934
+ await flush();
935
+
936
+ expect(screen.queryByLabelText("Investigation summary")).toBeNull();
937
+ expect(screen.getByText("The provider timed out.")).toBeVisible();
938
+ });
939
+
940
+ /*
941
+ * The report is recomputed from the feed on every poll, so it can vanish
942
+ * (a deleted feed item) while the run stays Completed. The summary must go
943
+ * with it — a summary of a report the reader cannot see is worse than none.
944
+ * Cadence matters: a settled Recommended run polls at the SETTLED interval,
945
+ * so advancing by the fast interval would make this pass vacuously.
946
+ */
947
+ test("drops the summary if the report it describes disappears on a later poll", async () => {
948
+ postMock
949
+ .mockResolvedValueOnce(completedResponse({ analysisTldr: TLDR }) as never)
950
+ .mockResolvedValue(
951
+ completedResponse({
952
+ analysisMarkdown: null,
953
+ analysisTldr: TLDR,
954
+ }) as never,
955
+ );
956
+
957
+ renderPanel();
958
+ await flush();
959
+ expect(screen.getByLabelText("Investigation summary")).toHaveTextContent(
960
+ TLDR,
961
+ );
962
+
963
+ await tick(SETTLED_POLL_INTERVAL_MS);
964
+
965
+ expect(postMock).toHaveBeenCalledTimes(2);
966
+ expect(screen.queryByLabelText("Investigation summary")).toBeNull();
967
+ expect(screen.queryByText(TLDR)).toBeNull();
968
+ });
969
+
970
+ /*
971
+ * "Never attributed to the wrong run" — the same subject can be
972
+ * re-investigated, and the previous run's summary must not survive onto the
973
+ * new run's report.
974
+ */
975
+ test("does not carry a summary over to a new run on the same subject", async () => {
976
+ const nextAnalysis: string = "## Root cause\n\nA second, different cause.";
977
+ postMock
978
+ .mockResolvedValueOnce(completedResponse({ analysisTldr: TLDR }) as never)
979
+ .mockResolvedValue(
980
+ completedResponse({
981
+ runId: NEXT_RUN_ID,
982
+ analysisMarkdown: nextAnalysis,
983
+ analysisTldr: null,
984
+ }) as never,
985
+ );
986
+
987
+ renderPanel();
988
+ await flush();
989
+ expect(screen.getByLabelText("Investigation summary")).toBeInTheDocument();
990
+
991
+ await tick(SETTLED_POLL_INTERVAL_MS);
992
+
993
+ expect(screen.queryByLabelText("Investigation summary")).toBeNull();
994
+ expect(screen.queryByText(TLDR)).toBeNull();
995
+ expect(screen.getByTestId("investigation-markdown")).toHaveTextContent(
996
+ "A second, different cause.",
997
+ );
998
+ });
999
+
1000
+ /*
1001
+ * The module's threat model is "the worst a prompt injection achieves is a
1002
+ * misleading sentence". That holds only while the summary is rendered as
1003
+ * inert text — never as markdown, never as HTML.
1004
+ */
1005
+ test("renders hostile summary content inertly, as text", async () => {
1006
+ const hostile: string =
1007
+ '<img src=x onerror="window.__pwned = true"> **bold** [link](https://evil.example)';
1008
+ postMock.mockResolvedValue(
1009
+ completedResponse({ analysisTldr: hostile }) as never,
1010
+ );
1011
+
1012
+ renderPanel();
1013
+ await flush();
1014
+
1015
+ const summary: HTMLElement = screen.getByLabelText("Investigation summary");
1016
+ expect(summary).toHaveTextContent(hostile);
1017
+ expect(summary.querySelector("img")).toBeNull();
1018
+ expect(summary.querySelector("a")).toBeNull();
1019
+ expect(
1020
+ (window as unknown as { __pwned?: boolean }).__pwned,
1021
+ ).toBeUndefined();
1022
+ // The summary never reaches the markdown renderer.
1023
+ expect(
1024
+ markdownTexts().every((text: string): boolean => {
1025
+ return text === ANALYSIS;
1026
+ }),
1027
+ ).toBe(true);
1028
+ });
1029
+
1030
+ test("drops the previous subject's summary the moment the subject changes", async () => {
1031
+ const nextSubject: Deferred<ApiResponse> = createDeferred<ApiResponse>();
1032
+ postMock
1033
+ .mockResolvedValueOnce(completedResponse({ analysisTldr: TLDR }) as never)
1034
+ .mockReturnValueOnce(nextSubject.promise as never);
1035
+
1036
+ const view: ReturnType<typeof render> = renderPanel();
1037
+ await flush();
1038
+ expect(screen.getByLabelText("Investigation summary")).toBeInTheDocument();
1039
+
1040
+ view.rerender(
1041
+ <InvestigationPanel subjectType="alert" subjectId={ALERT_ID} />,
1042
+ );
1043
+ await flush();
1044
+
1045
+ expect(screen.queryByLabelText("Investigation summary")).toBeNull();
1046
+
1047
+ await resolveDeferred(
1048
+ nextSubject,
1049
+ completedResponse({
1050
+ runId: NEXT_RUN_ID,
1051
+ analysisTldr: "The alert cleared on its own after the pod restarted.",
1052
+ }),
1053
+ );
1054
+
1055
+ expect(screen.getByLabelText("Investigation summary")).toHaveTextContent(
1056
+ "The alert cleared on its own after the pod restarted.",
1057
+ );
1058
+ });
1059
+ });
1060
+
1061
+ /*
1062
+ * The verdict prompt is a single question with two answers, so both choices
1063
+ * live in one labelled group; once answered it collapses to the recorded
1064
+ * verdict plus a way back. These tests pin that shape — the earlier suites
1065
+ * cover the request/optimistic-state contract behind it.
1066
+ */
1067
+ describe("InvestigationPanel verdict control", () => {
1068
+ function verdictGroup(): HTMLElement | null {
1069
+ return screen.queryByRole("group", { name: "Rate this investigation" });
1070
+ }
1071
+
1072
+ test("offers both verdicts inside one labelled group", async () => {
1073
+ postMock.mockResolvedValue(completedResponse() as never);
1074
+
1075
+ renderPanel();
1076
+ await flush();
1077
+
1078
+ const group: HTMLElement = verdictGroup()!;
1079
+ expect(group).toBeInTheDocument();
1080
+ expect(
1081
+ screen.getByRole("button", { name: "Confirmed" }),
1082
+ ).toBeInTheDocument();
1083
+ expect(
1084
+ screen.getByRole("button", { name: "Rejected" }),
1085
+ ).toBeInTheDocument();
1086
+ expect(group).toContainElement(
1087
+ screen.getByRole("button", { name: "Confirmed" }),
1088
+ );
1089
+ expect(group).toContainElement(
1090
+ screen.getByRole("button", { name: "Rejected" }),
1091
+ );
1092
+ });
1093
+
1094
+ test("collapses to the recorded verdict and back again through Change", async () => {
1095
+ postMock
1096
+ .mockResolvedValueOnce(completedResponse() as never)
1097
+ .mockResolvedValueOnce(successfulResponse({}) as never);
1098
+
1099
+ renderPanel();
1100
+ await flush();
1101
+ fireEvent.click(screen.getByRole("button", { name: "Rejected" }));
1102
+ await flush();
1103
+
1104
+ expect(verdictGroup()).toBeNull();
1105
+ expect(screen.getByText(/You rejected this analysis/)).toBeVisible();
1106
+
1107
+ fireEvent.click(screen.getByRole("button", { name: "Change" }));
1108
+
1109
+ expect(verdictGroup()).toBeInTheDocument();
1110
+ expect(screen.getByRole("button", { name: "Confirmed" })).toBeEnabled();
1111
+ expect(screen.getByRole("button", { name: "Rejected" })).toBeEnabled();
1112
+ // Reopening the prompt must not itself save anything.
1113
+ expect(postMock).toHaveBeenCalledTimes(2);
1114
+ });
1115
+
1116
+ test("locks both choices while a verdict is saving", async () => {
1117
+ const save: Deferred<ApiResponse> = createDeferred<ApiResponse>();
1118
+ postMock
1119
+ .mockResolvedValueOnce(completedResponse() as never)
1120
+ .mockReturnValueOnce(save.promise as never);
1121
+
1122
+ renderPanel();
1123
+ await flush();
1124
+ fireEvent.click(screen.getByRole("button", { name: "Confirmed" }));
1125
+ await flush();
1126
+
1127
+ // Optimistic: the pill replaces the prompt immediately.
1128
+ expect(screen.getByText(/You confirmed this analysis/)).toBeVisible();
1129
+
1130
+ fireEvent.click(screen.getByRole("button", { name: "Change" }));
1131
+ expect(screen.getByRole("button", { name: "Confirmed" })).toBeDisabled();
1132
+ expect(screen.getByRole("button", { name: "Rejected" })).toBeDisabled();
1133
+
1134
+ await resolveDeferred(save, successfulResponse({}));
1135
+
1136
+ expect(screen.getByRole("button", { name: "Confirmed" })).toBeEnabled();
1137
+ });
1138
+
1139
+ test("shows the run status as a badge in the card header", async () => {
1140
+ postMock.mockResolvedValue(completedResponse() as never);
1141
+
1142
+ renderPanel();
1143
+ await flush();
1144
+
1145
+ expect(screen.getByLabelText("Investigation status")).toHaveTextContent(
1146
+ "Investigation complete",
1147
+ );
1148
+ });
1149
+ });
1150
+
1151
+ describe("InvestigationPanel code-fix recommendation", () => {
1152
+ test("shows the fix action only for an explicit Recommended decision", async () => {
1153
+ postMock.mockResolvedValue(completedResponse() as never);
1154
+
1155
+ renderPanel();
1156
+ await flush();
1157
+
1158
+ expect(fixButton()).toBeEnabled();
1159
+ expect(screen.getByText("Act on this investigation")).toBeVisible();
1160
+ expect(screen.getByText("Rate this investigation")).toBeVisible();
1161
+ });
1162
+
1163
+ test("hides every fix-task element for NotRecommended but retains verdict controls", async () => {
1164
+ postMock.mockResolvedValue(
1165
+ completedResponse({
1166
+ codeFixRecommendation: AIRunCodeFixRecommendation.NotRecommended,
1167
+ }) as never,
1168
+ );
1169
+
1170
+ renderPanel();
1171
+ await flush();
1172
+
1173
+ expect(fixButton()).toBeNull();
1174
+ expect(screen.queryByText("Act on this investigation")).toBeNull();
1175
+ expect(screen.queryByText(/Fix task created/)).toBeNull();
1176
+ expect(screen.getByText("Rate this investigation")).toBeVisible();
1177
+ expect(screen.getByRole("button", { name: "Confirmed" })).toBeEnabled();
1178
+ expect(screen.getByRole("button", { name: "Rejected" })).toBeEnabled();
1179
+ });
1180
+
1181
+ test("fails closed for a legacy row with no recommendation or completion time", async () => {
1182
+ postMock.mockResolvedValue(
1183
+ completedResponse({
1184
+ codeFixRecommendation: undefined,
1185
+ completedAt: undefined,
1186
+ }) as never,
1187
+ );
1188
+
1189
+ renderPanel();
1190
+ await flush();
1191
+
1192
+ expect(fixButton()).toBeNull();
1193
+ expect(screen.getByText("Rate this investigation")).toBeVisible();
1194
+ expect(jest.getTimerCount()).toBe(1);
1195
+
1196
+ await advanceFastPolls(4);
1197
+ expect(postMock).toHaveBeenCalledTimes(1);
1198
+ });
1199
+
1200
+ test("fails closed at fast cadence for an old row with no recommendation", async () => {
1201
+ postMock.mockResolvedValue(
1202
+ completedResponse({
1203
+ codeFixRecommendation: undefined,
1204
+ completedAt: OLD_COMPLETED_AT,
1205
+ }) as never,
1206
+ );
1207
+
1208
+ renderPanel();
1209
+ await flush();
1210
+
1211
+ expect(fixButton()).toBeNull();
1212
+ expect(jest.getTimerCount()).toBe(1);
1213
+
1214
+ await advanceFastPolls(4);
1215
+ expect(postMock).toHaveBeenCalledTimes(1);
1216
+ });
1217
+
1218
+ test("polls a recent missing decision and reveals the action when it settles", async () => {
1219
+ postMock
1220
+ .mockResolvedValueOnce(
1221
+ completedResponse({ codeFixRecommendation: undefined }) as never,
1222
+ )
1223
+ .mockResolvedValueOnce(completedResponse() as never);
1224
+
1225
+ renderPanel();
1226
+ await flush();
1227
+
1228
+ expect(fixButton()).toBeNull();
1229
+ expect(screen.getByText("Rate this investigation")).toBeVisible();
1230
+ expect(jest.getTimerCount()).toBe(2);
1231
+
1232
+ await advanceFastPolls();
1233
+
1234
+ expect(postMock).toHaveBeenCalledTimes(2);
1235
+ expect(fixButton()).toBeEnabled();
1236
+ expect(jest.getTimerCount()).toBe(1);
1237
+ });
1238
+
1239
+ test("applies Pending to Recommended when no other signature field changes", async () => {
1240
+ postMock
1241
+ .mockResolvedValueOnce(
1242
+ completedResponse({
1243
+ codeFixRecommendation: AIRunCodeFixRecommendation.Pending,
1244
+ }) as never,
1245
+ )
1246
+ .mockResolvedValueOnce(completedResponse() as never);
1247
+
1248
+ renderPanel();
1249
+ await flush();
1250
+
1251
+ expect(screen.getByText("Investigation complete")).toBeVisible();
1252
+ expect(fixButton()).toBeNull();
1253
+ expect(postMock).toHaveBeenCalledTimes(1);
1254
+ expect(jest.getTimerCount()).toBe(2);
1255
+
1256
+ await advanceFastPolls();
1257
+
1258
+ expect(postMock).toHaveBeenCalledTimes(2);
1259
+ expect(fixButton()).toBeEnabled();
1260
+ expect(jest.getTimerCount()).toBe(1);
1261
+ });
1262
+
1263
+ test("does not present explicit Pending as an active investigation", async () => {
1264
+ postMock.mockResolvedValue(
1265
+ completedResponse({
1266
+ codeFixRecommendation: AIRunCodeFixRecommendation.Pending,
1267
+ }) as never,
1268
+ );
1269
+
1270
+ renderPanel();
1271
+ await flush();
1272
+
1273
+ expect(screen.getByText("Investigation complete")).toBeVisible();
1274
+ expect(screen.queryByText("Investigating…")).toBeNull();
1275
+ expect(
1276
+ document.querySelector('[class~="motion-safe:animate-ping"]'),
1277
+ ).toBeNull();
1278
+ expect(fixButton()).toBeNull();
1279
+ });
1280
+
1281
+ test("tolerates a browser clock behind the server while Pending settles", async () => {
1282
+ jest.setSystemTime(new Date("2026-08-07T11:59:55.000Z"));
1283
+ postMock
1284
+ .mockResolvedValueOnce(
1285
+ completedResponse({
1286
+ codeFixRecommendation: AIRunCodeFixRecommendation.Pending,
1287
+ }) as never,
1288
+ )
1289
+ .mockResolvedValueOnce(completedResponse() as never);
1290
+
1291
+ renderPanel();
1292
+ await flush();
1293
+ expect(fixButton()).toBeNull();
1294
+
1295
+ await advanceFastPolls();
1296
+ expect(fixButton()).toBeEnabled();
1297
+ });
1298
+
1299
+ test("tolerates a browser clock ahead of the server for explicit Pending", async () => {
1300
+ jest.setSystemTime(new Date("2026-08-07T12:05:00.000Z"));
1301
+ postMock
1302
+ .mockResolvedValueOnce(
1303
+ completedResponse({
1304
+ codeFixRecommendation: AIRunCodeFixRecommendation.Pending,
1305
+ }) as never,
1306
+ )
1307
+ .mockResolvedValueOnce(completedResponse() as never);
1308
+
1309
+ renderPanel();
1310
+ await flush();
1311
+ expect(fixButton()).toBeNull();
1312
+
1313
+ await advanceFastPolls();
1314
+ expect(fixButton()).toBeEnabled();
1315
+ });
1316
+
1317
+ test("bounds a permanently Pending decision by time and response count", async () => {
1318
+ postMock.mockResolvedValue(
1319
+ completedResponse({
1320
+ codeFixRecommendation: AIRunCodeFixRecommendation.Pending,
1321
+ }) as never,
1322
+ );
1323
+
1324
+ renderPanel();
1325
+ await flush();
1326
+
1327
+ expect(fixButton()).toBeNull();
1328
+ expect(jest.getTimerCount()).toBe(2);
1329
+
1330
+ await advanceFastPolls(MAX_RECOMMENDATION_POLL_RESPONSES);
1331
+
1332
+ const callsAtSettlement: number = postMock.mock.calls.length;
1333
+ expect(callsAtSettlement).toBeGreaterThan(1);
1334
+ expect(callsAtSettlement).toBeLessThanOrEqual(
1335
+ MAX_RECOMMENDATION_POLL_RESPONSES + 1,
1336
+ );
1337
+ expect(fixButton()).toBeNull();
1338
+ expect(screen.queryByRole("alert")).toBeNull();
1339
+ expect(jest.getTimerCount()).toBe(1);
1340
+
1341
+ await advanceFastPolls(4);
1342
+ expect(postMock).toHaveBeenCalledTimes(callsAtSettlement);
1343
+ });
1344
+
1345
+ test("keeps Pending polls sequential when a response is slow", async () => {
1346
+ const slowPending: Deferred<ApiResponse> = createDeferred<ApiResponse>();
1347
+ postMock
1348
+ .mockResolvedValueOnce(
1349
+ completedResponse({
1350
+ codeFixRecommendation: AIRunCodeFixRecommendation.Pending,
1351
+ }) as never,
1352
+ )
1353
+ .mockReturnValueOnce(slowPending.promise as never)
1354
+ .mockResolvedValueOnce(completedResponse() as never);
1355
+
1356
+ renderPanel();
1357
+ await flush();
1358
+ await advanceFastPolls();
1359
+ expect(postMock).toHaveBeenCalledTimes(2);
1360
+
1361
+ await advanceFastPolls(5);
1362
+ expect(postMock).toHaveBeenCalledTimes(2);
1363
+
1364
+ await resolveDeferred(
1365
+ slowPending,
1366
+ completedResponse({
1367
+ codeFixRecommendation: AIRunCodeFixRecommendation.Pending,
1368
+ }),
1369
+ );
1370
+ await advanceFastPolls();
1371
+
1372
+ expect(postMock).toHaveBeenCalledTimes(3);
1373
+ expect(fixButton()).toBeEnabled();
1374
+ });
1375
+
1376
+ test("stops failed Pending polls at the independent deadline", async () => {
1377
+ const failedPoll: HTTPErrorResponse = new HTTPErrorResponse(
1378
+ 503,
1379
+ { message: "temporarily unavailable" },
1380
+ {},
1381
+ );
1382
+ postMock
1383
+ .mockResolvedValueOnce(
1384
+ completedResponse({
1385
+ codeFixRecommendation: AIRunCodeFixRecommendation.Pending,
1386
+ }) as never,
1387
+ )
1388
+ .mockResolvedValue(failedPoll as never);
1389
+
1390
+ renderPanel();
1391
+ await flush();
1392
+ await advanceFastPolls(MAX_RECOMMENDATION_POLL_RESPONSES);
1393
+
1394
+ const callsAtDeadline: number = postMock.mock.calls.length;
1395
+ expect(callsAtDeadline).toBeGreaterThan(1);
1396
+ expect(fixButton()).toBeNull();
1397
+ expect(screen.queryByRole("alert")).toBeNull();
1398
+
1399
+ await advanceFastPolls(4);
1400
+ expect(postMock).toHaveBeenCalledTimes(callsAtDeadline);
1401
+ });
1402
+
1403
+ test("a hung Pending request never overlaps before or after the deadline", async () => {
1404
+ const hungPoll: Deferred<ApiResponse> = createDeferred<ApiResponse>();
1405
+ postMock
1406
+ .mockResolvedValueOnce(
1407
+ completedResponse({
1408
+ codeFixRecommendation: AIRunCodeFixRecommendation.Pending,
1409
+ }) as never,
1410
+ )
1411
+ .mockReturnValueOnce(hungPoll.promise as never);
1412
+
1413
+ renderPanel();
1414
+ await flush();
1415
+ await advanceFastPolls();
1416
+ expect(postMock).toHaveBeenCalledTimes(2);
1417
+
1418
+ await tick(RECOMMENDATION_SETTLEMENT_MAX_AGE_MS);
1419
+ expect(fixButton()).toBeNull();
1420
+ expect(postMock).toHaveBeenCalledTimes(2);
1421
+
1422
+ /* Settled cadence must reuse the still-hung request, not overlap it. */
1423
+ await tick(SETTLED_POLL_INTERVAL_MS);
1424
+ expect(postMock).toHaveBeenCalledTimes(2);
1425
+ });
1426
+ });
1427
+
1428
+ describe("InvestigationPanel completed actions", () => {
1429
+ test("creates a run-bound fix task and replaces the action with success", async () => {
1430
+ postMock
1431
+ .mockResolvedValueOnce(completedResponse() as never)
1432
+ .mockResolvedValueOnce(
1433
+ successfulResponse({ aiRunId: FIX_RUN_ID }) as never,
1434
+ );
1435
+
1436
+ renderPanel();
1437
+ await flush();
1438
+ fireEvent.click(fixButton()!);
1439
+ await flush();
1440
+
1441
+ expect(screen.getByRole("alert")).toHaveTextContent("Fix task created");
1442
+ expect(fixButton()).toBeNull();
1443
+ expect(requestPath(1)).toContain("/ai-investigation/create-fix-task");
1444
+ expect(postRequestAt(1).data).toEqual({
1445
+ subjectType: "incident",
1446
+ subjectId: INCIDENT_ID.toString(),
1447
+ investigationRunId: RUN_ID,
1448
+ });
1449
+ expect(screen.getByText("View task progress")).toBeVisible();
1450
+ });
1451
+
1452
+ test("keeps the Recommended action and shows a friendly task error", async () => {
1453
+ postMock
1454
+ .mockResolvedValueOnce(completedResponse() as never)
1455
+ .mockResolvedValueOnce(
1456
+ new HTTPErrorResponse(
1457
+ 400,
1458
+ { message: "No connected repository exists." },
1459
+ {},
1460
+ ) as never,
1461
+ );
1462
+
1463
+ renderPanel();
1464
+ await flush();
1465
+ fireEvent.click(fixButton()!);
1466
+ await flush();
1467
+
1468
+ expect(screen.getByRole("alert")).toHaveTextContent(
1469
+ "Could not create the fix task",
1470
+ );
1471
+ expect(screen.getByRole("alert")).toHaveTextContent(
1472
+ "No connected repository exists",
1473
+ );
1474
+ expect(fixButton()).toBeEnabled();
1475
+ });
1476
+
1477
+ test("clears settled fix-task state after subject navigation", async () => {
1478
+ const nextAnalysis: string =
1479
+ "## Alert root cause\n\nA deployment removed the required credential.";
1480
+ postMock
1481
+ .mockResolvedValueOnce(completedResponse() as never)
1482
+ .mockResolvedValueOnce(
1483
+ successfulResponse({ aiRunId: FIX_RUN_ID }) as never,
1484
+ )
1485
+ .mockResolvedValueOnce(
1486
+ completedResponse({
1487
+ runId: NEXT_RUN_ID,
1488
+ analysisMarkdown: nextAnalysis,
1489
+ }) as never,
1490
+ );
1491
+
1492
+ const view: ReturnType<typeof render> = renderPanel();
1493
+ await flush();
1494
+ fireEvent.click(fixButton()!);
1495
+ await flush();
1496
+ expect(screen.getByRole("alert")).toHaveTextContent("Fix task created");
1497
+
1498
+ view.rerender(
1499
+ <InvestigationPanel subjectType="alert" subjectId={ALERT_ID} />,
1500
+ );
1501
+ await flush();
1502
+
1503
+ expect(screen.queryByText(/Fix task created/)).toBeNull();
1504
+ expect(screen.getByTestId("investigation-markdown")).toHaveTextContent(
1505
+ "A deployment removed the required credential.",
1506
+ );
1507
+ expect(fixButton()).toBeEnabled();
1508
+ });
1509
+
1510
+ test("ignores a fix-task result that resolves on another subject", async () => {
1511
+ const staleTask: Deferred<ApiResponse> = createDeferred<ApiResponse>();
1512
+ postMock
1513
+ .mockResolvedValueOnce(completedResponse() as never)
1514
+ .mockReturnValueOnce(staleTask.promise as never)
1515
+ .mockResolvedValueOnce(
1516
+ completedResponse({ runId: NEXT_RUN_ID }) as never,
1517
+ );
1518
+
1519
+ const view: ReturnType<typeof render> = renderPanel();
1520
+ await flush();
1521
+ fireEvent.click(fixButton()!);
1522
+ await flush();
1523
+
1524
+ view.rerender(
1525
+ <InvestigationPanel subjectType="alert" subjectId={ALERT_ID} />,
1526
+ );
1527
+ await flush();
1528
+ await resolveDeferred(
1529
+ staleTask,
1530
+ successfulResponse({ aiRunId: FIX_RUN_ID }),
1531
+ );
1532
+
1533
+ expect(screen.queryByRole("alert")).toBeNull();
1534
+ expect(fixButton()).toBeEnabled();
1535
+ expect(screen.getByText("Investigation complete")).toBeVisible();
1536
+ });
1537
+
1538
+ test("resets fix-task and verdict state when the same subject gets a new run", async () => {
1539
+ const nextAnalysis: string =
1540
+ "## New root cause\n\nA later investigation found a certificate rollover.";
1541
+ postMock
1542
+ .mockResolvedValueOnce(completedResponse() as never)
1543
+ .mockResolvedValueOnce(
1544
+ successfulResponse({ aiRunId: FIX_RUN_ID }) as never,
1545
+ )
1546
+ .mockResolvedValueOnce(successfulResponse({}) as never)
1547
+ .mockResolvedValueOnce(
1548
+ completedResponse({
1549
+ runId: NEXT_RUN_ID,
1550
+ analysisMarkdown: nextAnalysis,
1551
+ }) as never,
1552
+ );
1553
+
1554
+ renderPanel();
1555
+ await flush();
1556
+ fireEvent.click(fixButton()!);
1557
+ await flush();
1558
+ fireEvent.click(screen.getByRole("button", { name: "Confirmed" }));
1559
+ await flush();
1560
+
1561
+ expect(screen.getByRole("alert")).toHaveTextContent("Fix task created");
1562
+ expect(screen.getByText(/You confirmed this analysis/)).toBeVisible();
1563
+
1564
+ await tick(SETTLED_POLL_INTERVAL_MS);
1565
+
1566
+ expect(screen.getByTestId("investigation-markdown")).toHaveTextContent(
1567
+ "A later investigation found a certificate rollover.",
1568
+ );
1569
+ expect(screen.queryByText(/Fix task created/)).toBeNull();
1570
+ expect(screen.queryByText(/You confirmed this analysis/)).toBeNull();
1571
+ expect(fixButton()).toBeEnabled();
1572
+ expect(screen.getByRole("button", { name: "Confirmed" })).toBeEnabled();
1573
+ });
1574
+
1575
+ test("binds a verdict to the displayed run even without a fix recommendation", async () => {
1576
+ postMock
1577
+ .mockResolvedValueOnce(
1578
+ completedResponse({
1579
+ codeFixRecommendation: AIRunCodeFixRecommendation.NotRecommended,
1580
+ }) as never,
1581
+ )
1582
+ .mockResolvedValueOnce(successfulResponse({}) as never);
1583
+
1584
+ renderPanel();
1585
+ await flush();
1586
+ fireEvent.click(screen.getByRole("button", { name: "Confirmed" }));
1587
+ await flush();
1588
+
1589
+ expect(requestPath(1)).toContain("/ai-investigation/verdict");
1590
+ expect(postRequestAt(1).data).toEqual({
1591
+ subjectType: "incident",
1592
+ subjectId: INCIDENT_ID.toString(),
1593
+ investigationRunId: RUN_ID,
1594
+ verdict: "Confirmed",
1595
+ });
1596
+ expect(screen.getByText(/You confirmed this analysis/)).toBeVisible();
1597
+ });
1598
+
1599
+ test("retains an existing verdict when a fix is not recommended", async () => {
1600
+ postMock.mockResolvedValue(
1601
+ completedResponse({
1602
+ codeFixRecommendation: AIRunCodeFixRecommendation.NotRecommended,
1603
+ humanVerdict: "Confirmed",
1604
+ }) as never,
1605
+ );
1606
+
1607
+ renderPanel();
1608
+ await flush();
1609
+
1610
+ expect(fixButton()).toBeNull();
1611
+ expect(screen.getByText(/You confirmed this analysis/)).toBeVisible();
1612
+ expect(screen.getByText("Change")).toBeVisible();
1613
+ expect(screen.getByText("Rate this investigation")).toBeVisible();
1614
+ });
1615
+
1616
+ test("a GET started before verdict save cannot overwrite the saved verdict", async () => {
1617
+ const stalePoll: Deferred<ApiResponse> = createDeferred<ApiResponse>();
1618
+ postMock
1619
+ .mockResolvedValueOnce(completedResponse() as never)
1620
+ .mockReturnValueOnce(stalePoll.promise as never)
1621
+ .mockResolvedValueOnce(successfulResponse({}) as never);
1622
+
1623
+ renderPanel();
1624
+ await flush();
1625
+ await tick(SETTLED_POLL_INTERVAL_MS);
1626
+ expect(postMock).toHaveBeenCalledTimes(2);
1627
+
1628
+ fireEvent.click(screen.getByRole("button", { name: "Confirmed" }));
1629
+ await flush();
1630
+ expect(screen.getByText(/You confirmed this analysis/)).toBeVisible();
1631
+
1632
+ await resolveDeferred(stalePoll, completedResponse({ humanVerdict: null }));
1633
+
1634
+ expect(screen.getByText(/You confirmed this analysis/)).toBeVisible();
1635
+ expect(screen.queryByRole("button", { name: "Confirmed" })).toBeNull();
1636
+ });
1637
+
1638
+ test("rolls back an optimistic verdict when save fails", async () => {
1639
+ postMock
1640
+ .mockResolvedValueOnce(completedResponse() as never)
1641
+ .mockResolvedValueOnce(
1642
+ new HTTPErrorResponse(
1643
+ 500,
1644
+ { message: "Verdict storage is unavailable." },
1645
+ {},
1646
+ ) as never,
1647
+ );
1648
+
1649
+ renderPanel();
1650
+ await flush();
1651
+ fireEvent.click(screen.getByRole("button", { name: "Rejected" }));
1652
+ await flush();
1653
+
1654
+ expect(screen.getByRole("alert")).toHaveTextContent(
1655
+ "Could not save your verdict",
1656
+ );
1657
+ expect(screen.getByRole("alert")).toHaveTextContent(
1658
+ "Verdict storage is unavailable.",
1659
+ );
1660
+ expect(screen.queryByText(/You rejected this analysis/)).toBeNull();
1661
+ expect(screen.getByRole("button", { name: "Rejected" })).toBeEnabled();
1662
+ });
1663
+
1664
+ test("ignores a verdict result that resolves after navigation", async () => {
1665
+ const staleVerdict: Deferred<ApiResponse> = createDeferred<ApiResponse>();
1666
+ postMock
1667
+ .mockResolvedValueOnce(
1668
+ completedResponse({
1669
+ codeFixRecommendation: AIRunCodeFixRecommendation.NotRecommended,
1670
+ }) as never,
1671
+ )
1672
+ .mockReturnValueOnce(staleVerdict.promise as never)
1673
+ .mockResolvedValueOnce(
1674
+ completedResponse({
1675
+ runId: NEXT_RUN_ID,
1676
+ codeFixRecommendation: AIRunCodeFixRecommendation.NotRecommended,
1677
+ }) as never,
1678
+ );
1679
+
1680
+ const view: ReturnType<typeof render> = renderPanel();
1681
+ await flush();
1682
+ fireEvent.click(screen.getByRole("button", { name: "Confirmed" }));
1683
+ await flush();
1684
+
1685
+ view.rerender(
1686
+ <InvestigationPanel subjectType="alert" subjectId={ALERT_ID} />,
1687
+ );
1688
+ await flush();
1689
+ await resolveDeferred(staleVerdict, successfulResponse({}));
1690
+
1691
+ expect(screen.queryByText(/You confirmed this analysis/)).toBeNull();
1692
+ expect(screen.queryByRole("alert")).toBeNull();
1693
+ expect(screen.getByRole("button", { name: "Confirmed" })).toBeEnabled();
1694
+ });
1695
+ });