@oneuptime/common 13.0.3 → 13.0.5

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 (687) hide show
  1. package/Models/DatabaseModels/Index.ts +4 -0
  2. package/Models/DatabaseModels/RumApplication.ts +1 -1
  3. package/Models/DatabaseModels/SecurityEventConnection.ts +457 -0
  4. package/Models/DatabaseModels/SecurityEventConnectionRun.ts +263 -0
  5. package/Models/DatabaseModels/TelemetryIngestionKey.ts +2 -2
  6. package/Server/API/AIInvestigationAPI.ts +484 -24
  7. package/Server/API/GoogleSecOpsConnectionAPI.ts +249 -8
  8. package/Server/API/SecurityEventConnectionAPI.ts +250 -0
  9. package/Server/API/TelemetryAPI.ts +402 -265
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.ts +99 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  12. package/Server/Middleware/TelemetryIngest.ts +101 -6
  13. package/Server/Services/ExceptionAggregationService.ts +7 -16
  14. package/Server/Services/GoogleSecOpsConnectionService.ts +15 -0
  15. package/Server/Services/Index.ts +4 -0
  16. package/Server/Services/InventoryItemService.ts +28 -0
  17. package/Server/Services/LogAggregationService.ts +17 -23
  18. package/Server/Services/MetricAggregationService.ts +4 -14
  19. package/Server/Services/SecurityEventConnectionRunService.ts +10 -0
  20. package/Server/Services/SecurityEventConnectionService.ts +538 -0
  21. package/Server/Services/TraceAggregationService.ts +77 -28
  22. package/Server/Types/AnalyticsDatabase/ModelPermission.ts +9 -0
  23. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +13 -1
  24. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +15 -1
  25. package/Server/Utils/AI/SRE/InvestigationEvidence.ts +658 -0
  26. package/Server/Utils/AI/SRE/InvestigationReferences.ts +324 -0
  27. package/Server/Utils/AI/Toolbox/Index.ts +38 -0
  28. package/Server/Utils/AI/Toolbox/TimelineTools.ts +9 -2
  29. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +80 -0
  30. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +5 -3
  31. package/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.ts +1108 -0
  32. package/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.ts +732 -0
  33. package/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.ts +495 -0
  34. package/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.ts +689 -0
  35. package/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.ts +767 -0
  36. package/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.ts +571 -0
  37. package/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.ts +827 -0
  38. package/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.ts +610 -0
  39. package/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.ts +549 -0
  40. package/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.ts +830 -0
  41. package/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.ts +684 -0
  42. package/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.ts +487 -0
  43. package/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.ts +778 -0
  44. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.ts +1002 -0
  45. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.ts +556 -0
  46. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.ts +275 -0
  47. package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.ts +53 -0
  48. package/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.ts +709 -0
  49. package/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.ts +596 -0
  50. package/Server/Utils/SecurityEvent/Connectors/Types.ts +191 -0
  51. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +353 -65
  52. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.ts +777 -0
  53. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +747 -148
  54. package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.ts +48 -2
  55. package/Server/Utils/SecurityEvent/SecurityEventDedupe.ts +81 -0
  56. package/Server/Utils/SessionReplay/SessionReplayReadService.ts +741 -40
  57. package/Server/Utils/StartServer.ts +10 -0
  58. package/Server/Utils/Telemetry/EntityRegistry.ts +8 -0
  59. package/Server/Utils/Telemetry/KubernetesResourceAttributes.ts +108 -0
  60. package/Server/Utils/Telemetry/ResourceEntityFilter.ts +174 -93
  61. package/Server/Utils/Telemetry/ResourceFacetPlanner.ts +251 -0
  62. package/Server/Utils/Telemetry/ResourceFacetResolver.ts +248 -413
  63. package/Server/Utils/Telemetry/ServiceDependencyDiscovery.ts +668 -0
  64. package/Server/Utils/Telemetry/TraceContextPropagation.ts +348 -0
  65. package/Server/Utils/Telemetry.ts +18 -0
  66. package/Tests/App/Dashboard/AIInvestigationHeaderStatus.test.tsx +413 -0
  67. package/Tests/App/Dashboard/AIRootCauseFeedItem.test.tsx +285 -0
  68. package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +242 -0
  69. package/Tests/App/Dashboard/AlertEpisodeViewFields.test.tsx +601 -15
  70. package/Tests/App/Dashboard/ChangeAlertStateAIHeader.test.tsx +835 -0
  71. package/Tests/App/Dashboard/CloudResourceTelemetryScope.test.ts +41 -0
  72. package/Tests/App/Dashboard/ConnectionTestModal.test.tsx +604 -0
  73. package/Tests/App/Dashboard/ConnectorTestReportView.test.tsx +738 -0
  74. package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +153 -6
  75. package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +165 -0
  76. package/Tests/App/Dashboard/EpisodeChangeState.test.tsx +1069 -0
  77. package/Tests/App/Dashboard/EpisodeMembersCard.test.tsx +772 -0
  78. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +2354 -0
  79. package/Tests/App/Dashboard/EventOverviewSkeleton.test.tsx +210 -0
  80. package/Tests/App/Dashboard/EventStatBar.test.tsx +392 -0
  81. package/Tests/App/Dashboard/EventStatusPanel.test.tsx +247 -0
  82. package/Tests/App/Dashboard/ExceptionAIAssistance.test.tsx +606 -0
  83. package/Tests/App/Dashboard/ExceptionBreadcrumbTimeline.test.tsx +273 -0
  84. package/Tests/App/Dashboard/ExceptionDetailSideMenu.test.tsx +176 -0
  85. package/Tests/App/Dashboard/ExceptionOverviewCards.test.tsx +366 -0
  86. package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +371 -0
  87. package/Tests/App/Dashboard/ExceptionStackFrameViewer.test.tsx +440 -0
  88. package/Tests/App/Dashboard/ExceptionSummary.test.tsx +381 -0
  89. package/Tests/App/Dashboard/ExceptionTriage.test.tsx +306 -0
  90. package/Tests/App/Dashboard/ExceptionsResourceFacets.test.tsx +604 -0
  91. package/Tests/App/Dashboard/ExceptionsViewerEntityChips.test.tsx +706 -0
  92. package/Tests/App/Dashboard/FeedOrdering.test.ts +32 -0
  93. package/Tests/App/Dashboard/GoogleSecOpsConnectionForm.test.tsx +838 -0
  94. package/Tests/App/Dashboard/GoogleSecOpsConnectionsErrors.test.tsx +18 -0
  95. package/Tests/App/Dashboard/GoogleSecOpsDiagnostics.test.tsx +297 -20
  96. package/Tests/App/Dashboard/IncidentEpisodeViewFields.test.tsx +618 -13
  97. package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +254 -207
  98. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +303 -135
  99. package/Tests/App/Dashboard/InvestigationEvidenceList.test.tsx +1251 -0
  100. package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +303 -0
  101. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +876 -8
  102. package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +35 -0
  103. package/Tests/App/Dashboard/InvestigationReferenceLink.test.tsx +177 -0
  104. package/Tests/App/Dashboard/InvestigationReportView.test.tsx +1295 -0
  105. package/Tests/App/Dashboard/KubernetesImageReferenceView.test.tsx +114 -0
  106. package/Tests/App/Dashboard/KubernetesLogsTabChips.test.tsx +285 -0
  107. package/Tests/App/Dashboard/LogsEntityChipNames.test.tsx +316 -0
  108. package/Tests/App/Dashboard/LogsResourceFacetChips.test.tsx +361 -0
  109. package/Tests/App/Dashboard/ProfileTableEntityNames.test.tsx +432 -0
  110. package/Tests/App/Dashboard/ProfileViewEntityName.test.tsx +250 -0
  111. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +10 -0
  112. package/Tests/App/Dashboard/ResourceLogsTabsBareLayout.test.tsx +700 -0
  113. package/Tests/App/Dashboard/ResourcePageEntityNameProps.test.tsx +803 -0
  114. package/Tests/App/Dashboard/RumSessionReplaySideMenu.test.tsx +11 -1
  115. package/Tests/App/Dashboard/ScheduledMaintenanceChangeState.test.tsx +1026 -0
  116. package/Tests/App/Dashboard/ScheduledMaintenanceFeedRefresh.test.tsx +436 -0
  117. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +1195 -0
  118. package/Tests/App/Dashboard/SecurityEventConnectionFormModal.test.tsx +1022 -0
  119. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +905 -0
  120. package/Tests/App/Dashboard/SecurityEventsSideMenu.test.tsx +247 -0
  121. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +375 -274
  122. package/Tests/App/Dashboard/SessionReplayAuditTable.test.tsx +410 -0
  123. package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +774 -0
  124. package/Tests/App/Dashboard/TopErrorsPanelResourceNames.test.tsx +316 -0
  125. package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +30 -0
  126. package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +88 -24
  127. package/Tests/App/Dashboard/TraceExplorer.test.tsx +1590 -0
  128. package/Tests/App/Dashboard/TraceWaterfall.test.tsx +488 -0
  129. package/Tests/App/Dashboard/TracesAnalyticsSeriesLabels.test.tsx +367 -0
  130. package/Tests/App/Dashboard/TracesEntityNames.test.tsx +585 -0
  131. package/Tests/App/Dashboard/TracesResourceFacets.test.tsx +613 -0
  132. package/Tests/App/Dashboard/UseServiceNames.test.tsx +359 -0
  133. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +7 -0
  134. package/Tests/Models/InventoryItemNaming.test.ts +132 -16
  135. package/Tests/Models/SecurityDomainAccessControl.test.ts +61 -17
  136. package/Tests/Server/API/AIInvestigationAPI.test.ts +611 -2
  137. package/Tests/Server/API/AIInvestigationEvidenceAPI.test.ts +1098 -0
  138. package/Tests/Server/API/GoogleSecOpsConnectionAPI.test.ts +409 -0
  139. package/Tests/Server/API/SecurityEventConnectionAPI.test.ts +856 -0
  140. package/Tests/Server/API/SessionReplayAPI.test.ts +528 -1
  141. package/Tests/Server/API/TelemetryFacetsAPI.test.ts +810 -0
  142. package/Tests/Server/Infrastructure/E2EClickHouseFixtureAccess.test.ts +240 -0
  143. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +229 -0
  144. package/Tests/Server/Services/InventoryItemDisplayName.test.ts +66 -0
  145. package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +28 -1
  146. package/Tests/Server/Services/SecurityEventConnectionService.test.ts +1233 -0
  147. package/Tests/Server/Services/TelemetryIngestionKeyValidation.test.ts +53 -0
  148. package/Tests/Server/Services/TelemetryResourceFacetCounting.test.ts +218 -0
  149. package/Tests/Server/Services/TelemetryResourceFacetFilters.test.ts +155 -1
  150. package/Tests/Server/Services/TraceAggregationExceptionScope.test.ts +142 -0
  151. package/Tests/Server/Services/TraceAnalyticsResourceDisplayNames.test.ts +85 -24
  152. package/Tests/Server/Types/Database/Permissions/SecurityRoleAccess.test.ts +33 -0
  153. package/Tests/Server/Utils/AI/InvestigationEvidence.test.ts +1395 -0
  154. package/Tests/Server/Utils/AI/InvestigationReferences.test.ts +616 -0
  155. package/Tests/Server/Utils/AI/TimelineTools.test.ts +119 -0
  156. package/Tests/Server/Utils/AI/Toolbox/AIToolbox.test.ts +151 -0
  157. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +65 -0
  158. package/Tests/Server/Utils/AnalyticsDatabase/StatementGeneratorExceptionScope.test.ts +224 -0
  159. package/Tests/Server/Utils/LoggerCore.test.ts +597 -0
  160. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPerSeriesFanout.test.ts +55 -2
  161. package/Tests/Server/Utils/Monitor/MonitorTemplateUtil.test.ts +1109 -0
  162. package/Tests/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.test.ts +1240 -0
  163. package/Tests/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.test.ts +1853 -0
  164. package/Tests/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.test.ts +1129 -0
  165. package/Tests/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.test.ts +1066 -0
  166. package/Tests/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.test.ts +1568 -0
  167. package/Tests/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.test.ts +930 -0
  168. package/Tests/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.test.ts +1409 -0
  169. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.test.ts +763 -0
  170. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.test.ts +1296 -0
  171. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.test.ts +1102 -0
  172. package/Tests/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.test.ts +1174 -0
  173. package/Tests/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.test.ts +1032 -0
  174. package/Tests/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.test.ts +1558 -0
  175. package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.test.ts +3041 -0
  176. package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.test.ts +1168 -0
  177. package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.test.ts +906 -0
  178. package/Tests/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.test.ts +1068 -0
  179. package/Tests/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.test.ts +1004 -0
  180. package/Tests/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.test.ts +784 -0
  181. package/Tests/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.test.ts +113 -1
  182. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +5 -1
  183. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsDiagnostics.test.ts +93 -13
  184. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsHttpIntegration.test.ts +23 -1
  185. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +25 -3
  186. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +22 -8
  187. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +82 -16
  188. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsSearchDetections.test.ts +730 -0
  189. package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsThreePassPoller.test.ts +1577 -0
  190. package/Tests/Server/Utils/SessionReplay/SessionReplayOriginAllowlist.test.ts +41 -0
  191. package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +1453 -18
  192. package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +711 -0
  193. package/Tests/Server/Utils/Telemetry/KubernetesResourceAttributes.test.ts +162 -0
  194. package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +460 -13
  195. package/Tests/Server/Utils/Telemetry/ResourceFacetPlanner.test.ts +678 -0
  196. package/Tests/Server/Utils/Telemetry/ResourceFacetResolver.test.ts +868 -0
  197. package/Tests/Server/Utils/Telemetry/ServiceDependencyDiscovery.test.ts +525 -0
  198. package/Tests/Server/Utils/Telemetry/TraceContextPropagation.test.ts +420 -0
  199. package/Tests/Types/OnCallDutyPolicy/LayerUtilCorePaths.test.ts +1014 -0
  200. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveRotation.test.ts +9 -6
  201. package/Tests/Types/Rules/RuleCriteriaFieldRegistry.test.ts +276 -0
  202. package/Tests/Types/Rum/SessionReplayApiContracts.test.ts +73 -0
  203. package/Tests/Types/Rum/SessionReplayTransportContracts.test.ts +95 -0
  204. package/Tests/Types/SecurityEvent/Connectors/ConnectorDiagnostics.test.ts +82 -0
  205. package/Tests/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.test.ts +314 -0
  206. package/Tests/Types/Telemetry/ExceptionSpanScope.test.ts +89 -0
  207. package/Tests/Types/Telemetry/LlmConventions.test.ts +495 -0
  208. package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +96 -0
  209. package/Tests/Types/Telemetry/ResourceFacetCatalog.test.ts +304 -0
  210. package/Tests/UI/Components/ActiveFilterChipsLockedFilters.test.tsx +357 -0
  211. package/Tests/UI/Components/Card.test.tsx +312 -1
  212. package/Tests/UI/Components/CardModelDetailHeaderLayout.test.tsx +253 -0
  213. package/Tests/UI/Components/CardModelDetailRefresher.test.tsx +342 -0
  214. package/Tests/UI/Components/Checkbox.test.tsx +79 -0
  215. package/Tests/UI/Components/CopyTextButton.test.tsx +142 -0
  216. package/Tests/UI/Components/DetailCompactStyle.test.tsx +621 -0
  217. package/Tests/UI/Components/ErrorBoundary.test.tsx +60 -0
  218. package/Tests/UI/Components/FacetSections.test.tsx +581 -0
  219. package/Tests/UI/Components/FacetSidebarControls.test.tsx +812 -0
  220. package/Tests/UI/Components/FacetVisibility.test.ts +429 -0
  221. package/Tests/UI/Components/LockedFilterActions.test.tsx +396 -0
  222. package/Tests/UI/Components/LockedFilterChip.test.tsx +651 -0
  223. package/Tests/UI/Components/LogsEntityNames.test.ts +1769 -0
  224. package/Tests/UI/Components/LogsEntityNamesComponents.test.tsx +696 -0
  225. package/Tests/UI/Components/LogsFacetSidebarHiddenFacets.test.tsx +600 -0
  226. package/Tests/UI/Components/LogsViewerEntityNames.test.tsx +713 -0
  227. package/Tests/UI/Components/MarkdownInlineReferences.test.tsx +2079 -0
  228. package/Tests/UI/Components/MemberRoleAssignment.test.tsx +446 -0
  229. package/Tests/UI/Components/ModelDetailRefetch.test.tsx +397 -0
  230. package/Tests/UI/Components/ModelListRefetch.test.tsx +233 -0
  231. package/Tests/UI/Components/ModelPageModelChange.test.tsx +409 -0
  232. package/Tests/UI/Components/PaginationIntegration.test.tsx +25 -0
  233. package/Tests/UI/Components/ResourceFacetConfigs.test.ts +172 -0
  234. package/Tests/UI/Components/SavedViewsSidebarIntegration.test.tsx +32 -14
  235. package/Tests/UI/Components/TableSortAndMobileColumns.test.tsx +44 -1
  236. package/Tests/UI/Components/TelemetryActiveFilterChipsLockedFilters.test.tsx +304 -0
  237. package/Tests/UI/Components/TelemetryFacetSidebarHiddenFacets.test.tsx +718 -0
  238. package/Tests/UI/Monitor/EvaluationLogList.test.tsx +303 -0
  239. package/Tests/UI/Monitor/MonitorSummarySnapshotRender.test.tsx +55 -1
  240. package/Tests/UI/Rum/FidelityNotices.test.ts +22 -0
  241. package/Tests/UI/Rum/RecordingHealthCard.test.tsx +120 -136
  242. package/Tests/UI/Rum/RecordingHealthDashboard.test.tsx +988 -0
  243. package/Tests/UI/Rum/ReplayCard.test.tsx +32 -0
  244. package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +709 -12
  245. package/Tests/UI/Rum/ReplayHeader.test.tsx +50 -1
  246. package/Tests/UI/Rum/ReplayPinControl.test.tsx +67 -10
  247. package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +41 -12
  248. package/Tests/UI/Rum/ReplayStage.test.tsx +663 -1
  249. package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +173 -0
  250. package/Tests/UI/Rum/ReplayUi.test.tsx +189 -2
  251. package/Tests/UI/Rum/ReplayUserSessionsMenu.test.tsx +91 -0
  252. package/Tests/UI/Rum/SessionReplayEmptyState.test.tsx +1 -1
  253. package/Tests/UI/Rum/SessionReplaySettingsPolicyLoading.test.tsx +585 -0
  254. package/Tests/UI/Rum/SessionReplayTable.test.tsx +844 -51
  255. package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +29 -1
  256. package/Tests/UI/Telemetry/TelemetryDetailPanel.test.tsx +288 -0
  257. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +31 -1
  258. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +6 -0
  259. package/Tests/UI/Utils/Clipboard.test.ts +154 -0
  260. package/Tests/UI/Utils/Navigation.test.ts +24 -0
  261. package/Tests/UI/Utils/Telemetry/TelemetryEntityNames.test.ts +2205 -0
  262. package/Tests/UI/Utils/Telemetry/UseTelemetryEntityNames.test.tsx +844 -0
  263. package/Tests/Utils/AI/InvestigationReport.test.ts +2430 -0
  264. package/Tests/Utils/APIOutgoingRequestTracer.test.ts +137 -0
  265. package/Tests/Utils/Rum/SessionReplayHealth.test.ts +884 -0
  266. package/Tests/Utils/Rum/SessionReplayHealthDiagnosis.test.ts +40 -0
  267. package/Tests/Utils/Rum/SessionReplayRecordingEnded.test.ts +619 -0
  268. package/Tests/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.test.ts +631 -0
  269. package/Tests/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.test.ts +479 -0
  270. package/Tests/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.test.ts +507 -0
  271. package/Tests/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.test.ts +686 -0
  272. package/Tests/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.test.ts +474 -0
  273. package/Tests/Utils/SecurityEvent/Connectors/OktaNormalizer.test.ts +736 -0
  274. package/Tests/Utils/SecurityEvent/Connectors/SplunkNormalizer.test.ts +407 -0
  275. package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.test.ts +129 -0
  276. package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +57 -1
  277. package/Tests/Utils/Telemetry/EntityRelationship.test.ts +36 -0
  278. package/Tests/Utils/Telemetry/LockedFilterSearch.test.ts +600 -0
  279. package/Tests/Utils/Telemetry/NetworkHost.test.ts +48 -0
  280. package/Tests/Utils/Telemetry/OriginAllowList.test.ts +154 -0
  281. package/Tests/Utils/Traces/CriticalPath.test.ts +25 -0
  282. package/Types/AI/AIChatTypes.ts +8 -0
  283. package/Types/AI/InvestigationEvidence.ts +96 -0
  284. package/Types/Monitor/MonitorEvaluationSummary.ts +5 -0
  285. package/Types/OnCallDutyPolicy/Layer.ts +12 -0
  286. package/Types/Permission.ts +2 -2
  287. package/Types/Rules/RuleCriteriaFieldRegistry.ts +12 -0
  288. package/Types/Rum/SessionReplay.ts +109 -0
  289. package/Types/Rum/SessionReplayApi.ts +31 -0
  290. package/Types/SecurityEvent/Connectors/ConnectorDiagnostics.ts +111 -0
  291. package/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.ts +87 -0
  292. package/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.ts +492 -0
  293. package/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.ts +41 -0
  294. package/Types/SecurityEvent/GoogleSecOpsDiagnostics.ts +56 -1
  295. package/Types/SecurityEvent/GoogleSecOpsRegion.ts +34 -0
  296. package/Types/Telemetry/EntityType.ts +14 -0
  297. package/Types/Telemetry/ExceptionSpanScope.ts +83 -0
  298. package/Types/Telemetry/LockedFilterDetail.ts +46 -0
  299. package/Types/Telemetry/ResourceEntityFacet.ts +17 -10
  300. package/Types/Telemetry/ResourceFacetCatalog.ts +171 -0
  301. package/UI/Components/Card/Card.tsx +118 -83
  302. package/UI/Components/Checkbox/Checkbox.tsx +8 -5
  303. package/UI/Components/CopyTextButton/CopyTextButton.tsx +7 -1
  304. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +4 -1
  305. package/UI/Components/Detail/Detail.tsx +137 -22
  306. package/UI/Components/ErrorBoundary.tsx +38 -0
  307. package/UI/Components/LogsViewer/LogsEntityNames.ts +912 -0
  308. package/UI/Components/LogsViewer/LogsViewer.tsx +156 -77
  309. package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +28 -11
  310. package/UI/Components/LogsViewer/components/FacetSection.tsx +66 -79
  311. package/UI/Components/LogsViewer/components/FacetValueRow.tsx +17 -2
  312. package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +45 -5
  313. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +100 -19
  314. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +156 -39
  315. package/UI/Components/LogsViewer/components/LogsTable.tsx +30 -8
  316. package/UI/Components/LogsViewer/types.ts +6 -0
  317. package/UI/Components/Markdown.tsx/InlineReferences.tsx +628 -0
  318. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +385 -312
  319. package/UI/Components/MemberRoleAssignment/MemberRoleAssignment.tsx +50 -22
  320. package/UI/Components/ModelDetail/CardModelDetail.tsx +15 -0
  321. package/UI/Components/ModelDetail/ModelDetail.tsx +64 -19
  322. package/UI/Components/ModelList/ModelList.tsx +33 -8
  323. package/UI/Components/Page/ModelPage.tsx +119 -54
  324. package/UI/Components/Table/Table.tsx +8 -0
  325. package/UI/Components/Table/TableRow.tsx +13 -5
  326. package/UI/Components/TelemetryViewer/FacetVisibility.ts +229 -0
  327. package/UI/Components/TelemetryViewer/ResourceFacetConfigs.ts +46 -0
  328. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +11 -0
  329. package/UI/Components/TelemetryViewer/components/FacetSearchInput.tsx +79 -0
  330. package/UI/Components/TelemetryViewer/components/FacetSectionHeader.tsx +69 -0
  331. package/UI/Components/TelemetryViewer/components/FacetShowMoreButton.tsx +36 -0
  332. package/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.tsx +88 -0
  333. package/UI/Components/TelemetryViewer/components/LockedFilterActions.tsx +205 -0
  334. package/UI/Components/TelemetryViewer/components/LockedFilterChip.tsx +496 -0
  335. package/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.tsx +30 -10
  336. package/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.tsx +203 -10
  337. package/UI/Components/TelemetryViewer/components/TelemetryFacetSection.tsx +60 -73
  338. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +94 -7
  339. package/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.tsx +17 -2
  340. package/UI/Components/TelemetryViewer/types.ts +19 -0
  341. package/UI/Components/TelemetryViewer/useFacetSearchExemptions.ts +158 -0
  342. package/UI/Components/TelemetryViewer/useFacetSectionSearch.ts +102 -0
  343. package/UI/Utils/Clipboard.ts +68 -2
  344. package/UI/Utils/Navigation.ts +17 -2
  345. package/UI/Utils/Telemetry/TelemetryEntityNames.ts +620 -0
  346. package/UI/Utils/Telemetry/UseTelemetryEntityNames.ts +151 -0
  347. package/Utils/AI/InvestigationReport.ts +1789 -0
  348. package/Utils/API.ts +90 -1
  349. package/Utils/Rum/SessionReplayHealth.ts +11 -0
  350. package/Utils/Rum/SessionReplayRecordingEnded.ts +134 -0
  351. package/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.ts +427 -0
  352. package/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.ts +197 -0
  353. package/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.ts +464 -0
  354. package/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.ts +567 -0
  355. package/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.ts +299 -0
  356. package/Utils/SecurityEvent/Connectors/OktaNormalizer.ts +467 -0
  357. package/Utils/SecurityEvent/Connectors/SplunkNormalizer.ts +439 -0
  358. package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +124 -12
  359. package/Utils/Telemetry/CrossSignalScope.ts +22 -8
  360. package/Utils/Telemetry/EntityRelationship.ts +8 -0
  361. package/Utils/Telemetry/LockedFilterSearch.ts +412 -0
  362. package/Utils/Telemetry/NetworkHost.ts +43 -0
  363. package/Utils/Telemetry/OriginAllowList.ts +139 -4
  364. package/Utils/Traces/CriticalPath.ts +10 -4
  365. package/build/dist/Models/DatabaseModels/Index.js +4 -0
  366. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  367. package/build/dist/Models/DatabaseModels/RumApplication.js +1 -1
  368. package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
  369. package/build/dist/Models/DatabaseModels/SecurityEventConnection.js +490 -0
  370. package/build/dist/Models/DatabaseModels/SecurityEventConnection.js.map +1 -0
  371. package/build/dist/Models/DatabaseModels/SecurityEventConnectionRun.js +287 -0
  372. package/build/dist/Models/DatabaseModels/SecurityEventConnectionRun.js.map +1 -0
  373. package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js +2 -2
  374. package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js.map +1 -1
  375. package/build/dist/Server/API/AIInvestigationAPI.js +336 -27
  376. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  377. package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js +182 -7
  378. package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js.map +1 -1
  379. package/build/dist/Server/API/SecurityEventConnectionAPI.js +171 -0
  380. package/build/dist/Server/API/SecurityEventConnectionAPI.js.map +1 -0
  381. package/build/dist/Server/API/TelemetryAPI.js +248 -170
  382. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  383. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.js +40 -0
  384. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792700000000-AddSecurityEventConnections.js.map +1 -0
  385. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  386. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  387. package/build/dist/Server/Middleware/TelemetryIngest.js +57 -4
  388. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  389. package/build/dist/Server/Services/ExceptionAggregationService.js +7 -16
  390. package/build/dist/Server/Services/ExceptionAggregationService.js.map +1 -1
  391. package/build/dist/Server/Services/GoogleSecOpsConnectionService.js +8 -0
  392. package/build/dist/Server/Services/GoogleSecOpsConnectionService.js.map +1 -1
  393. package/build/dist/Server/Services/Index.js +4 -0
  394. package/build/dist/Server/Services/Index.js.map +1 -1
  395. package/build/dist/Server/Services/InventoryItemService.js +22 -0
  396. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  397. package/build/dist/Server/Services/LogAggregationService.js +8 -17
  398. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  399. package/build/dist/Server/Services/MetricAggregationService.js +4 -14
  400. package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
  401. package/build/dist/Server/Services/SecurityEventConnectionRunService.js +9 -0
  402. package/build/dist/Server/Services/SecurityEventConnectionRunService.js.map +1 -0
  403. package/build/dist/Server/Services/SecurityEventConnectionService.js +356 -0
  404. package/build/dist/Server/Services/SecurityEventConnectionService.js.map +1 -0
  405. package/build/dist/Server/Services/TraceAggregationService.js +51 -25
  406. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  407. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -0
  408. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
  409. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +3 -0
  410. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  411. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +14 -7
  412. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  413. package/build/dist/Server/Utils/AI/SRE/InvestigationEvidence.js +468 -0
  414. package/build/dist/Server/Utils/AI/SRE/InvestigationEvidence.js.map +1 -0
  415. package/build/dist/Server/Utils/AI/SRE/InvestigationReferences.js +230 -0
  416. package/build/dist/Server/Utils/AI/SRE/InvestigationReferences.js.map +1 -0
  417. package/build/dist/Server/Utils/AI/Toolbox/Index.js +32 -0
  418. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
  419. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js +6 -2
  420. package/build/dist/Server/Utils/AI/Toolbox/TimelineTools.js.map +1 -1
  421. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +59 -0
  422. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  423. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +5 -3
  424. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  425. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.js +752 -0
  426. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubClient.js.map +1 -0
  427. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.js +493 -0
  428. package/build/dist/Server/Utils/SecurityEvent/Connectors/AwsSecurityHub/AwsSecurityHubConnector.js.map +1 -0
  429. package/build/dist/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.js +352 -0
  430. package/build/dist/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.js.map +1 -0
  431. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.js +405 -0
  432. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconClient.js.map +1 -0
  433. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.js +466 -0
  434. package/build/dist/Server/Utils/SecurityEvent/Connectors/CrowdStrikeFalcon/CrowdStrikeFalconConnector.js.map +1 -0
  435. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.js +344 -0
  436. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityClient.js.map +1 -0
  437. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.js +511 -0
  438. package/build/dist/Server/Utils/SecurityEvent/Connectors/ElasticSecurity/ElasticSecurityConnector.js.map +1 -0
  439. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.js +406 -0
  440. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrClient.js.map +1 -0
  441. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.js +336 -0
  442. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdr/MicrosoftDefenderXdrConnector.js.map +1 -0
  443. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.js +474 -0
  444. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelClient.js.map +1 -0
  445. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.js +417 -0
  446. package/build/dist/Server/Utils/SecurityEvent/Connectors/MicrosoftSentinel/MicrosoftSentinelConnector.js.map +1 -0
  447. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.js +314 -0
  448. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaClient.js.map +1 -0
  449. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.js +494 -0
  450. package/build/dist/Server/Utils/SecurityEvent/Connectors/Okta/OktaConnector.js.map +1 -0
  451. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.js +719 -0
  452. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.js.map +1 -0
  453. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.js +404 -0
  454. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.js.map +1 -0
  455. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.js +209 -0
  456. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.js.map +1 -0
  457. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.js +40 -0
  458. package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.js.map +1 -0
  459. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.js +494 -0
  460. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkClient.js.map +1 -0
  461. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.js +379 -0
  462. package/build/dist/Server/Utils/SecurityEvent/Connectors/Splunk/SplunkConnector.js.map +1 -0
  463. package/build/dist/Server/Utils/SecurityEvent/Connectors/Types.js +27 -0
  464. package/build/dist/Server/Utils/SecurityEvent/Connectors/Types.js.map +1 -0
  465. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +234 -61
  466. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
  467. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.js +537 -0
  468. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.js.map +1 -0
  469. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +572 -115
  470. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
  471. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js +35 -1
  472. package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js.map +1 -1
  473. package/build/dist/Server/Utils/SecurityEvent/SecurityEventDedupe.js +52 -0
  474. package/build/dist/Server/Utils/SecurityEvent/SecurityEventDedupe.js.map +1 -0
  475. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +521 -32
  476. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
  477. package/build/dist/Server/Utils/StartServer.js +10 -0
  478. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  479. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +8 -0
  480. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  481. package/build/dist/Server/Utils/Telemetry/KubernetesResourceAttributes.js +80 -0
  482. package/build/dist/Server/Utils/Telemetry/KubernetesResourceAttributes.js.map +1 -0
  483. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +122 -67
  484. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
  485. package/build/dist/Server/Utils/Telemetry/ResourceFacetPlanner.js +146 -0
  486. package/build/dist/Server/Utils/Telemetry/ResourceFacetPlanner.js.map +1 -0
  487. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js +174 -248
  488. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js.map +1 -1
  489. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js +492 -0
  490. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js.map +1 -0
  491. package/build/dist/Server/Utils/Telemetry/TraceContextPropagation.js +259 -0
  492. package/build/dist/Server/Utils/Telemetry/TraceContextPropagation.js.map +1 -0
  493. package/build/dist/Server/Utils/Telemetry.js +11 -5
  494. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  495. package/build/dist/Types/AI/InvestigationEvidence.js +2 -0
  496. package/build/dist/Types/AI/InvestigationEvidence.js.map +1 -0
  497. package/build/dist/Types/OnCallDutyPolicy/Layer.js +11 -0
  498. package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
  499. package/build/dist/Types/Permission.js +2 -2
  500. package/build/dist/Types/Permission.js.map +1 -1
  501. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +9 -0
  502. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
  503. package/build/dist/Types/Rum/SessionReplay.js +95 -0
  504. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  505. package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -1
  506. package/build/dist/Types/SecurityEvent/Connectors/ConnectorDiagnostics.js +18 -0
  507. package/build/dist/Types/SecurityEvent/Connectors/ConnectorDiagnostics.js.map +1 -0
  508. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.js +2 -0
  509. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.js.map +1 -0
  510. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js +374 -0
  511. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js.map +1 -0
  512. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.js +35 -0
  513. package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.js.map +1 -0
  514. package/build/dist/Types/SecurityEvent/GoogleSecOpsRegion.js +35 -0
  515. package/build/dist/Types/SecurityEvent/GoogleSecOpsRegion.js.map +1 -0
  516. package/build/dist/Types/Telemetry/EntityType.js +14 -0
  517. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  518. package/build/dist/Types/Telemetry/ExceptionSpanScope.js +59 -0
  519. package/build/dist/Types/Telemetry/ExceptionSpanScope.js.map +1 -0
  520. package/build/dist/Types/Telemetry/LockedFilterDetail.js +16 -0
  521. package/build/dist/Types/Telemetry/LockedFilterDetail.js.map +1 -0
  522. package/build/dist/Types/Telemetry/ResourceEntityFacet.js +16 -10
  523. package/build/dist/Types/Telemetry/ResourceEntityFacet.js.map +1 -1
  524. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js +119 -0
  525. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js.map +1 -0
  526. package/build/dist/UI/Components/Card/Card.js +34 -17
  527. package/build/dist/UI/Components/Card/Card.js.map +1 -1
  528. package/build/dist/UI/Components/Checkbox/Checkbox.js +6 -4
  529. package/build/dist/UI/Components/Checkbox/Checkbox.js.map +1 -1
  530. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js +5 -1
  531. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js.map +1 -1
  532. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +1 -1
  533. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  534. package/build/dist/UI/Components/Detail/Detail.js +63 -13
  535. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  536. package/build/dist/UI/Components/ErrorBoundary.js +34 -0
  537. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  538. package/build/dist/UI/Components/LogsViewer/LogsEntityNames.js +535 -0
  539. package/build/dist/UI/Components/LogsViewer/LogsEntityNames.js.map +1 -0
  540. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +117 -57
  541. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  542. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +5 -7
  543. package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
  544. package/build/dist/UI/Components/LogsViewer/components/FacetSection.js +31 -44
  545. package/build/dist/UI/Components/LogsViewer/components/FacetSection.js.map +1 -1
  546. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js +17 -3
  547. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js.map +1 -1
  548. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +19 -3
  549. package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
  550. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +64 -18
  551. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  552. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +100 -41
  553. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  554. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js +13 -5
  555. package/build/dist/UI/Components/LogsViewer/components/LogsTable.js.map +1 -1
  556. package/build/dist/UI/Components/LogsViewer/types.js.map +1 -1
  557. package/build/dist/UI/Components/Markdown.tsx/InlineReferences.js +403 -0
  558. package/build/dist/UI/Components/Markdown.tsx/InlineReferences.js.map +1 -0
  559. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +197 -179
  560. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  561. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js +30 -21
  562. package/build/dist/UI/Components/MemberRoleAssignment/MemberRoleAssignment.js.map +1 -1
  563. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +12 -0
  564. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  565. package/build/dist/UI/Components/ModelDetail/ModelDetail.js +46 -12
  566. package/build/dist/UI/Components/ModelDetail/ModelDetail.js.map +1 -1
  567. package/build/dist/UI/Components/ModelList/ModelList.js +26 -7
  568. package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
  569. package/build/dist/UI/Components/Page/ModelPage.js +62 -34
  570. package/build/dist/UI/Components/Page/ModelPage.js.map +1 -1
  571. package/build/dist/UI/Components/Table/Table.js +3 -1
  572. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  573. package/build/dist/UI/Components/Table/TableRow.js +8 -1
  574. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  575. package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js +111 -0
  576. package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js.map +1 -0
  577. package/build/dist/UI/Components/TelemetryViewer/ResourceFacetConfigs.js +22 -0
  578. package/build/dist/UI/Components/TelemetryViewer/ResourceFacetConfigs.js.map +1 -0
  579. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +1 -1
  580. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  581. package/build/dist/UI/Components/TelemetryViewer/components/FacetSearchInput.js +36 -0
  582. package/build/dist/UI/Components/TelemetryViewer/components/FacetSearchInput.js.map +1 -0
  583. package/build/dist/UI/Components/TelemetryViewer/components/FacetSectionHeader.js +21 -0
  584. package/build/dist/UI/Components/TelemetryViewer/components/FacetSectionHeader.js.map +1 -0
  585. package/build/dist/UI/Components/TelemetryViewer/components/FacetShowMoreButton.js +14 -0
  586. package/build/dist/UI/Components/TelemetryViewer/components/FacetShowMoreButton.js.map +1 -0
  587. package/build/dist/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.js +27 -0
  588. package/build/dist/UI/Components/TelemetryViewer/components/HiddenFacetsFooter.js.map +1 -0
  589. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterActions.js +88 -0
  590. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterActions.js.map +1 -0
  591. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js +228 -0
  592. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js.map +1 -0
  593. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.js +6 -6
  594. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.js.map +1 -1
  595. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js +111 -12
  596. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js.map +1 -1
  597. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSection.js +27 -40
  598. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSection.js.map +1 -1
  599. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +70 -10
  600. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  601. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js +17 -3
  602. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js.map +1 -1
  603. package/build/dist/UI/Components/TelemetryViewer/types.js +0 -4
  604. package/build/dist/UI/Components/TelemetryViewer/types.js.map +1 -1
  605. package/build/dist/UI/Components/TelemetryViewer/useFacetSearchExemptions.js +70 -0
  606. package/build/dist/UI/Components/TelemetryViewer/useFacetSearchExemptions.js.map +1 -0
  607. package/build/dist/UI/Components/TelemetryViewer/useFacetSectionSearch.js +52 -0
  608. package/build/dist/UI/Components/TelemetryViewer/useFacetSectionSearch.js.map +1 -0
  609. package/build/dist/UI/Utils/Clipboard.js +59 -2
  610. package/build/dist/UI/Utils/Clipboard.js.map +1 -1
  611. package/build/dist/UI/Utils/Navigation.js +11 -2
  612. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  613. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js +471 -0
  614. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js.map +1 -0
  615. package/build/dist/UI/Utils/Telemetry/UseTelemetryEntityNames.js +113 -0
  616. package/build/dist/UI/Utils/Telemetry/UseTelemetryEntityNames.js.map +1 -0
  617. package/build/dist/Utils/AI/InvestigationReport.js +1241 -0
  618. package/build/dist/Utils/AI/InvestigationReport.js.map +1 -0
  619. package/build/dist/Utils/API.js +25 -0
  620. package/build/dist/Utils/API.js.map +1 -1
  621. package/build/dist/Utils/Rum/SessionReplayHealth.js +10 -0
  622. package/build/dist/Utils/Rum/SessionReplayHealth.js.map +1 -1
  623. package/build/dist/Utils/Rum/SessionReplayRecordingEnded.js +54 -0
  624. package/build/dist/Utils/Rum/SessionReplayRecordingEnded.js.map +1 -0
  625. package/build/dist/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.js +311 -0
  626. package/build/dist/Utils/SecurityEvent/Connectors/AwsSecurityHubNormalizer.js.map +1 -0
  627. package/build/dist/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.js +150 -0
  628. package/build/dist/Utils/SecurityEvent/Connectors/CrowdStrikeFalconNormalizer.js.map +1 -0
  629. package/build/dist/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.js +328 -0
  630. package/build/dist/Utils/SecurityEvent/Connectors/ElasticSecurityNormalizer.js.map +1 -0
  631. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.js +405 -0
  632. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftDefenderXdrNormalizer.js.map +1 -0
  633. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.js +212 -0
  634. package/build/dist/Utils/SecurityEvent/Connectors/MicrosoftSentinelNormalizer.js.map +1 -0
  635. package/build/dist/Utils/SecurityEvent/Connectors/OktaNormalizer.js +351 -0
  636. package/build/dist/Utils/SecurityEvent/Connectors/OktaNormalizer.js.map +1 -0
  637. package/build/dist/Utils/SecurityEvent/Connectors/SplunkNormalizer.js +348 -0
  638. package/build/dist/Utils/SecurityEvent/Connectors/SplunkNormalizer.js.map +1 -0
  639. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +103 -10
  640. package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -1
  641. package/build/dist/Utils/Telemetry/CrossSignalScope.js +16 -5
  642. package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
  643. package/build/dist/Utils/Telemetry/EntityRelationship.js +7 -0
  644. package/build/dist/Utils/Telemetry/EntityRelationship.js.map +1 -1
  645. package/build/dist/Utils/Telemetry/LockedFilterSearch.js +277 -0
  646. package/build/dist/Utils/Telemetry/LockedFilterSearch.js.map +1 -0
  647. package/build/dist/Utils/Telemetry/NetworkHost.js +39 -0
  648. package/build/dist/Utils/Telemetry/NetworkHost.js.map +1 -0
  649. package/build/dist/Utils/Telemetry/OriginAllowList.js +105 -4
  650. package/build/dist/Utils/Telemetry/OriginAllowList.js.map +1 -1
  651. package/build/dist/Utils/Traces/CriticalPath.js +9 -4
  652. package/build/dist/Utils/Traces/CriticalPath.js.map +1 -1
  653. package/package.json +1 -1
  654. package/Tests/UI/Rum/RecordingHealthStrip.test.tsx +0 -600
  655. package/UI/Components/GanttChart/Bar/BarLabel.tsx +0 -17
  656. package/UI/Components/GanttChart/Bar/Index.tsx +0 -149
  657. package/UI/Components/GanttChart/ChartContainer.tsx +0 -50
  658. package/UI/Components/GanttChart/Index.tsx +0 -90
  659. package/UI/Components/GanttChart/Row/Index.tsx +0 -22
  660. package/UI/Components/GanttChart/Row/Row.tsx +0 -231
  661. package/UI/Components/GanttChart/Row/RowLabel.tsx +0 -30
  662. package/UI/Components/GanttChart/Rows.tsx +0 -59
  663. package/UI/Components/GanttChart/Timeline/Index.tsx +0 -46
  664. package/UI/Components/GanttChart/Timeline/TimelineInterval.tsx +0 -33
  665. package/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.tsx +0 -43
  666. package/build/dist/UI/Components/GanttChart/Bar/BarLabel.js +0 -8
  667. package/build/dist/UI/Components/GanttChart/Bar/BarLabel.js.map +0 -1
  668. package/build/dist/UI/Components/GanttChart/Bar/Index.js +0 -77
  669. package/build/dist/UI/Components/GanttChart/Bar/Index.js.map +0 -1
  670. package/build/dist/UI/Components/GanttChart/ChartContainer.js +0 -33
  671. package/build/dist/UI/Components/GanttChart/ChartContainer.js.map +0 -1
  672. package/build/dist/UI/Components/GanttChart/Index.js +0 -39
  673. package/build/dist/UI/Components/GanttChart/Index.js.map +0 -1
  674. package/build/dist/UI/Components/GanttChart/Row/Index.js +0 -7
  675. package/build/dist/UI/Components/GanttChart/Row/Index.js.map +0 -1
  676. package/build/dist/UI/Components/GanttChart/Row/Row.js +0 -106
  677. package/build/dist/UI/Components/GanttChart/Row/Row.js.map +0 -1
  678. package/build/dist/UI/Components/GanttChart/Row/RowLabel.js +0 -10
  679. package/build/dist/UI/Components/GanttChart/Row/RowLabel.js.map +0 -1
  680. package/build/dist/UI/Components/GanttChart/Rows.js +0 -19
  681. package/build/dist/UI/Components/GanttChart/Rows.js.map +0 -1
  682. package/build/dist/UI/Components/GanttChart/Timeline/Index.js +0 -16
  683. package/build/dist/UI/Components/GanttChart/Timeline/Index.js.map +0 -1
  684. package/build/dist/UI/Components/GanttChart/Timeline/TimelineInterval.js +0 -13
  685. package/build/dist/UI/Components/GanttChart/Timeline/TimelineInterval.js.map +0 -1
  686. package/build/dist/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.js +0 -13
  687. package/build/dist/UI/Components/GanttChart/Timeline/TimelineIntervalMarks.js.map +0 -1
@@ -0,0 +1,1295 @@
1
+ import "@testing-library/jest-dom";
2
+ import {
3
+ afterEach,
4
+ beforeEach,
5
+ describe,
6
+ expect,
7
+ jest,
8
+ test,
9
+ } from "@jest/globals";
10
+ import {
11
+ act,
12
+ cleanup,
13
+ fireEvent,
14
+ render,
15
+ screen,
16
+ within,
17
+ } from "@testing-library/react";
18
+ import * as React from "react";
19
+ import getJestMockFunction, { MockFunction } from "../../MockType";
20
+
21
+ /*
22
+ * The completed investigation, laid out for a responder: a Summary section,
23
+ * the report split into its own sections, and "Evidence checked". The report
24
+ * is untrusted model output, so beyond layout these tests pin that every
25
+ * fragment still goes through the safe markdown renderer and that the only
26
+ * interactive things inside the prose are chips for recorded citations and
27
+ * links to incidents/alerts the server resolved.
28
+ *
29
+ * react-markdown is mocked in Common jest, so the viewer mock below stands in
30
+ * for the real inline-reference pipeline: it splits "[C#]" and "#123" tokens
31
+ * out of the text and asks the panel's renderers for each one.
32
+ */
33
+
34
+ const postMock: MockFunction = getJestMockFunction();
35
+ const getCommonHeadersMock: MockFunction = getJestMockFunction();
36
+ const markdownViewerMock: MockFunction = getJestMockFunction();
37
+ const copyToClipboardMock: MockFunction = getJestMockFunction();
38
+
39
+ jest.mock("../../../UI/Utils/API/API", () => {
40
+ return {
41
+ __esModule: true,
42
+ default: {
43
+ post: (...args: Array<unknown>) => {
44
+ return postMock(...args);
45
+ },
46
+ getFriendlyMessage: () => {
47
+ return "Request failed";
48
+ },
49
+ },
50
+ };
51
+ });
52
+
53
+ jest.mock("../../../UI/Utils/ModelAPI/ModelAPI", () => {
54
+ return {
55
+ __esModule: true,
56
+ default: {
57
+ getCommonHeaders: (...args: Array<unknown>) => {
58
+ return getCommonHeadersMock(...args);
59
+ },
60
+ },
61
+ };
62
+ });
63
+
64
+ jest.mock("../../../UI/Utils/Clipboard", () => {
65
+ return {
66
+ __esModule: true,
67
+ default: {
68
+ copyToClipboard: (...args: Array<unknown>) => {
69
+ copyToClipboardMock(...args);
70
+ return Promise.resolve(true);
71
+ },
72
+ },
73
+ };
74
+ });
75
+
76
+ jest.mock(
77
+ "../../../../App/FeatureSet/Dashboard/src/Components/AIChat/Widgets/WidgetRenderer",
78
+ () => {
79
+ return {
80
+ __esModule: true,
81
+ default: (): React.ReactElement => {
82
+ return React.createElement("div", { "data-testid": "evidence-widget" });
83
+ },
84
+ };
85
+ },
86
+ );
87
+
88
+ jest.mock("../../../UI/Components/Markdown.tsx/LazyMarkdownViewer", () => {
89
+ return {
90
+ __esModule: true,
91
+ default: (props: MarkdownViewerProps): React.ReactElement => {
92
+ markdownViewerMock(props);
93
+ const parts: Array<string> = props.text.split(
94
+ /(\[C\d{1,3}\]|#\d{1,9}\b)/g,
95
+ );
96
+
97
+ return React.createElement(
98
+ "div",
99
+ { "data-testid": "report-markdown" },
100
+ parts.map((part: string, index: number): React.ReactNode => {
101
+ const citationPattern: RegExp = new RegExp("^\\[(C\\d{1,3})\\]$");
102
+ const citation: RegExpExecArray | null = citationPattern.exec(part);
103
+
104
+ if (citation && props.inlineReferences?.renderCitation) {
105
+ const element: React.ReactElement | null =
106
+ props.inlineReferences.renderCitation(citation[1]!);
107
+
108
+ if (element) {
109
+ return React.cloneElement(element, { key: index });
110
+ }
111
+ }
112
+
113
+ const referencePattern: RegExp = new RegExp("^#(\\d{1,9})$");
114
+ const reference: RegExpExecArray | null = referencePattern.exec(part);
115
+
116
+ if (reference && props.inlineReferences?.renderEventReference) {
117
+ const element: React.ReactElement | null =
118
+ props.inlineReferences.renderEventReference({
119
+ kind: null,
120
+ number: parseInt(reference[1]!, 10),
121
+ text: part,
122
+ });
123
+
124
+ if (element) {
125
+ return React.cloneElement(element, { key: index });
126
+ }
127
+ }
128
+
129
+ return part;
130
+ }),
131
+ );
132
+ },
133
+ };
134
+ });
135
+
136
+ import InvestigationReportView, {
137
+ ComponentProps as ReportViewProps,
138
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/AI/InvestigationReport/InvestigationReportView";
139
+ import {
140
+ MAX_EVIDENCE_ITEMS,
141
+ MAX_REPORT_SUMMARY_LENGTH,
142
+ clipText,
143
+ getInvestigationReportSummaryText,
144
+ markdownToPlainText,
145
+ parseInvestigationEvidence,
146
+ parseInvestigationEvidenceRows,
147
+ parseInvestigationReferences,
148
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/AI/InvestigationReport/InvestigationReportData";
149
+ import {
150
+ AIChatCitationTargetType,
151
+ AIChatWidgetType,
152
+ } from "../../../Types/AI/AIChatTypes";
153
+ import {
154
+ InvestigationEventReference,
155
+ InvestigationEvidenceItem,
156
+ } from "../../../Types/AI/InvestigationEvidence";
157
+ import type { MarkdownInlineReferenceRenderers } from "../../../UI/Components/Markdown.tsx/InlineReferences";
158
+ import {
159
+ InvestigationReportSectionKind,
160
+ parseInvestigationReport,
161
+ } from "../../../Utils/AI/InvestigationReport";
162
+ import { formatEvidenceLabel } from "../../../../App/FeatureSet/Dashboard/src/Utils/InvestigationEvidenceFormat";
163
+
164
+ interface MarkdownViewerProps {
165
+ text: string;
166
+ safeMode?: boolean | undefined;
167
+ inlineReferences?: MarkdownInlineReferenceRenderers | undefined;
168
+ }
169
+
170
+ const RUN_ID: string = "11111111-1111-4111-8111-111111111111";
171
+ const INCIDENT_ID: string = "33333333-3333-4333-8333-333333333333";
172
+ const PRIOR_INCIDENT_ID: string = "66666666-6666-4666-8666-666666666666";
173
+ const ALERT_ID: string = "44444444-4444-4444-8444-444444444444";
174
+ const TLDR: string =
175
+ "Checkout is returning 500s because the database connection pool is exhausted.";
176
+
177
+ const SERVER_REPORT: string = [
178
+ "## \u{1F9E0} AI — Automated Root Cause Analysis",
179
+ "",
180
+ "**Summary** — the connection pool ran dry after the 18:00 deploy [C1].",
181
+ "",
182
+ "**Most likely root cause** — the deploy halved the pool size; this matches prior #6954 [C2].",
183
+ "",
184
+ "**Evidence**",
185
+ "- 7 active incidents on checkout [C1]",
186
+ "- pool timeouts in the API logs [C2]",
187
+ "",
188
+ "**Suggested next steps**",
189
+ "- Roll back the deploy",
190
+ "- Raise the pool size",
191
+ "",
192
+ "**Evidence checked**",
193
+ "- **[C1]** Active incidents (7 total) — 7 row(s)",
194
+ "- **[C2]** Logs 17:20 – 18:20 (1 shown) — 1 row(s)",
195
+ "",
196
+ "---",
197
+ "*Investigated automatically by OneUptime AI — read-only, 2 queries run across your own telemetry using gpt-5. This is an AI-generated first pass; verify before acting.*",
198
+ ].join("\n");
199
+
200
+ const evidence: Array<InvestigationEvidenceItem> = [
201
+ {
202
+ citationId: "C1",
203
+ toolName: "query_incidents",
204
+ label: "Active incidents (7 total)",
205
+ rowCount: 7,
206
+ queryArguments: { state: "active" },
207
+ target: { type: AIChatCitationTargetType.Incidents },
208
+ executedAt: "2026-09-14T18:01:00.000Z",
209
+ canLoadRows: true,
210
+ },
211
+ {
212
+ citationId: "C2",
213
+ toolName: "search_logs",
214
+ label: "Logs 17:20 – 18:20 (1 shown)",
215
+ rowCount: 1,
216
+ queryArguments: { bodySearchText: "pool timeout" },
217
+ target: { type: AIChatCitationTargetType.Logs },
218
+ executedAt: "2026-09-14T18:02:00.000Z",
219
+ canLoadRows: true,
220
+ },
221
+ ];
222
+
223
+ const priorIncident: InvestigationEventReference = {
224
+ kind: "incident",
225
+ number: 6954,
226
+ id: PRIOR_INCIDENT_ID,
227
+ displayNumber: "INC-6954",
228
+ title: "Checkout pool exhausted",
229
+ stateName: "Resolved",
230
+ };
231
+
232
+ function viewProps(overrides: Partial<ReportViewProps> = {}): ReportViewProps {
233
+ const analysisMarkdown: string = overrides.analysisMarkdown ?? SERVER_REPORT;
234
+
235
+ return {
236
+ analysisMarkdown,
237
+ report: parseInvestigationReport(analysisMarkdown),
238
+ analysisTldr: null,
239
+ evidence,
240
+ references: [priorIncident],
241
+ subjectType: "incident",
242
+ subjectId: INCIDENT_ID,
243
+ runId: RUN_ID,
244
+ ...overrides,
245
+ };
246
+ }
247
+
248
+ function renderView(
249
+ overrides: Partial<ReportViewProps> = {},
250
+ ): ReturnType<typeof render> {
251
+ return render(<InvestigationReportView {...viewProps(overrides)} />);
252
+ }
253
+
254
+ function viewerCalls(): Array<MarkdownViewerProps> {
255
+ return (
256
+ markdownViewerMock.mock.calls as Array<Array<MarkdownViewerProps>>
257
+ ).map((call: Array<MarkdownViewerProps>): MarkdownViewerProps => {
258
+ return call[0]!;
259
+ });
260
+ }
261
+
262
+ function viewerTexts(): Array<string> {
263
+ return viewerCalls().map((call: MarkdownViewerProps): string => {
264
+ return call.text;
265
+ });
266
+ }
267
+
268
+ function lastRenderers(): MarkdownInlineReferenceRenderers {
269
+ const calls: Array<MarkdownViewerProps> = viewerCalls();
270
+ return calls[calls.length - 1]!.inlineReferences!;
271
+ }
272
+
273
+ function reportSectionHeadings(): Array<string> {
274
+ const report: HTMLElement = screen.getByLabelText("Investigation report");
275
+ return within(report)
276
+ .queryAllByRole("heading", { level: 4 })
277
+ .map((heading: HTMLElement): string => {
278
+ return heading.textContent || "";
279
+ });
280
+ }
281
+
282
+ beforeEach(() => {
283
+ getCommonHeadersMock.mockReturnValue({});
284
+ Object.defineProperty(Element.prototype, "scrollIntoView", {
285
+ configurable: true,
286
+ writable: true,
287
+ value: getJestMockFunction(),
288
+ });
289
+ });
290
+
291
+ afterEach(() => {
292
+ cleanup();
293
+ jest.useRealTimers();
294
+ postMock.mockReset();
295
+ getCommonHeadersMock.mockReset();
296
+ markdownViewerMock.mockReset();
297
+ copyToClipboardMock.mockReset();
298
+ delete (Element.prototype as unknown as { scrollIntoView?: unknown })
299
+ .scrollIntoView;
300
+ });
301
+
302
+ describe("InvestigationReportView summary section", () => {
303
+ test("leads with the TL;DR as plain text, then the report's Summary", () => {
304
+ renderView({ analysisTldr: TLDR });
305
+
306
+ const summary: HTMLElement = screen.getByRole("region", {
307
+ name: "Investigation summary",
308
+ });
309
+ expect(
310
+ within(summary).getByRole("heading", { name: "Summary" }),
311
+ ).toBeInTheDocument();
312
+ expect(within(summary).getByText("TL;DR")).toBeInTheDocument();
313
+
314
+ const tldr: HTMLElement = within(summary).getByText(TLDR);
315
+ expect(tldr.tagName).toBe("P");
316
+ expect(tldr).toHaveClass("text-base", "font-semibold", "text-gray-900");
317
+
318
+ const summaryMarkdown: HTMLElement =
319
+ within(summary).getByTestId("report-markdown");
320
+ expect(summaryMarkdown).toHaveTextContent(
321
+ "the connection pool ran dry after the 18:00 deploy",
322
+ );
323
+ expect(
324
+ tldr.compareDocumentPosition(summaryMarkdown) &
325
+ Node.DOCUMENT_POSITION_FOLLOWING,
326
+ ).toBeTruthy();
327
+
328
+ // The TL;DR is never routed through markdown.
329
+ expect(viewerTexts()).not.toContain(TLDR);
330
+ expect(
331
+ viewerTexts().some((text: string): boolean => {
332
+ return text.includes("Checkout is returning 500s");
333
+ }),
334
+ ).toBe(false);
335
+ });
336
+
337
+ test("uses the Summary as the lead text when there is no TL;DR", () => {
338
+ renderView();
339
+
340
+ const summary: HTMLElement = screen.getByRole("region", {
341
+ name: "Investigation summary",
342
+ });
343
+ expect(within(summary).queryByText("TL;DR")).toBeNull();
344
+ expect(within(summary).getByTestId("report-markdown")).toHaveTextContent(
345
+ "the connection pool ran dry after the 18:00 deploy",
346
+ );
347
+ });
348
+
349
+ test("shows only the TL;DR when the report has no Summary", () => {
350
+ renderView({
351
+ analysisMarkdown: "## Root cause\n\nThe pool was exhausted.",
352
+ analysisTldr: TLDR,
353
+ });
354
+
355
+ const summary: HTMLElement = screen.getByRole("region", {
356
+ name: "Investigation summary",
357
+ });
358
+ expect(summary).toHaveTextContent(TLDR);
359
+ expect(within(summary).queryByTestId("report-markdown")).toBeNull();
360
+ });
361
+
362
+ test("has no summary section with neither a TL;DR nor a Summary", () => {
363
+ renderView({
364
+ analysisMarkdown: "## Root cause\n\nThe pool was exhausted.",
365
+ analysisTldr: " ",
366
+ });
367
+
368
+ expect(
369
+ screen.queryByRole("region", { name: "Investigation summary" }),
370
+ ).toBeNull();
371
+ expect(screen.queryByText("TL;DR")).toBeNull();
372
+ });
373
+
374
+ test("sits above the report, which sits above the evidence", () => {
375
+ renderView({ analysisTldr: TLDR });
376
+
377
+ const summary: HTMLElement = screen.getByLabelText("Investigation summary");
378
+ const report: HTMLElement = screen.getByLabelText("Investigation report");
379
+ const evidenceSection: HTMLElement =
380
+ screen.getByLabelText("Evidence checked");
381
+
382
+ expect(
383
+ summary.compareDocumentPosition(report) &
384
+ Node.DOCUMENT_POSITION_FOLLOWING,
385
+ ).toBeTruthy();
386
+ expect(
387
+ report.compareDocumentPosition(evidenceSection) &
388
+ Node.DOCUMENT_POSITION_FOLLOWING,
389
+ ).toBeTruthy();
390
+ });
391
+
392
+ test("renders a hostile TL;DR inertly", () => {
393
+ const hostile: string =
394
+ '<img src=x onerror="window.__reportPwned = true"> **bold** [link](https://evil.example)';
395
+ renderView({ analysisTldr: hostile });
396
+
397
+ const summary: HTMLElement = screen.getByLabelText("Investigation summary");
398
+ expect(summary).toHaveTextContent(hostile);
399
+ expect(summary.querySelector("img")).toBeNull();
400
+ expect(summary.querySelector("a")).toBeNull();
401
+ expect(
402
+ (window as unknown as { __reportPwned?: boolean }).__reportPwned,
403
+ ).toBeUndefined();
404
+ });
405
+ });
406
+
407
+ describe("InvestigationReportView report section", () => {
408
+ test("lays out the report's own sections in order, without the summary", () => {
409
+ renderView();
410
+
411
+ expect(reportSectionHeadings()).toEqual([
412
+ "Most likely root cause",
413
+ "Evidence",
414
+ "Suggested next steps",
415
+ ]);
416
+
417
+ const report: HTMLElement = screen.getByLabelText("Investigation report");
418
+ expect(report).not.toHaveTextContent("the connection pool ran dry");
419
+ });
420
+
421
+ test("labels each sub-section by its heading", () => {
422
+ renderView();
423
+
424
+ const report: HTMLElement = screen.getByLabelText("Investigation report");
425
+ const subSections: Array<Element> = Array.from(
426
+ report.querySelectorAll("section[aria-labelledby]"),
427
+ );
428
+
429
+ expect(subSections).toHaveLength(3);
430
+
431
+ for (const subSection of subSections) {
432
+ const headingId: string = subSection.getAttribute("aria-labelledby")!;
433
+ const heading: HTMLElement | null = document.getElementById(headingId);
434
+ expect(heading).not.toBeNull();
435
+ expect(heading!.tagName).toBe("H4");
436
+ expect(subSection).toContainElement(heading);
437
+ }
438
+
439
+ expect(
440
+ screen.getByRole("region", { name: "Suggested next steps" }),
441
+ ).toHaveTextContent("Roll back the deploy");
442
+ });
443
+
444
+ test("calls out the most likely root cause", () => {
445
+ renderView();
446
+
447
+ const rootCause: HTMLElement = screen.getByRole("region", {
448
+ name: "Most likely root cause",
449
+ });
450
+ expect(rootCause).toHaveClass("border-amber-200", "bg-amber-50/60");
451
+ expect(
452
+ within(rootCause).getByRole("heading", {
453
+ name: "Most likely root cause",
454
+ }),
455
+ ).toHaveClass("uppercase", "text-amber-700");
456
+ expect(rootCause).toHaveTextContent("the deploy halved the pool size");
457
+ });
458
+
459
+ /*
460
+ * Models often open the root cause with its own sub-heading. That heading
461
+ * belongs to the root cause, so the callout (and its title) must survive
462
+ * rather than the body turning into an unrelated section.
463
+ */
464
+ test("keeps a root cause that opens with a sub-heading inside its callout", () => {
465
+ const markdown: string = [
466
+ "## Summary",
467
+ "Pool exhausted.",
468
+ "",
469
+ "## Most likely root cause",
470
+ "### Connection pool exhaustion",
471
+ "The pool hit 100% after the deploy [C1].",
472
+ "",
473
+ "## Evidence",
474
+ "- pool timeouts [C2]",
475
+ ].join("\n");
476
+ renderView({ analysisMarkdown: markdown });
477
+
478
+ const rootCause: HTMLElement = screen.getByRole("region", {
479
+ name: "Most likely root cause",
480
+ });
481
+ expect(rootCause).toHaveAttribute(
482
+ "data-section-kind",
483
+ InvestigationReportSectionKind.RootCause,
484
+ );
485
+ expect(rootCause).toHaveClass("border-amber-200", "bg-amber-50/60");
486
+ expect(rootCause).toHaveTextContent("Connection pool exhaustion");
487
+ expect(rootCause).toHaveTextContent("The pool hit 100% after the deploy");
488
+ // Its citation still becomes a chip inside the callout.
489
+ expect(
490
+ within(rootCause).getByRole("button", {
491
+ name: "Citation C1: Active incidents (7 total)",
492
+ }),
493
+ ).toBeInTheDocument();
494
+
495
+ // The sub-heading is part of the callout, not a section of its own.
496
+ expect(reportSectionHeadings()).toEqual([
497
+ "Most likely root cause",
498
+ "Evidence",
499
+ ]);
500
+ expect(
501
+ screen.queryByRole("region", { name: "Connection pool exhaustion" }),
502
+ ).toBeNull();
503
+ expect(
504
+ screen
505
+ .getByLabelText("Investigation report")
506
+ .querySelector(
507
+ `[data-section-kind="${InvestigationReportSectionKind.Other}"]`,
508
+ ),
509
+ ).toBeNull();
510
+ });
511
+
512
+ test("keeps a root cause that is only a sub-heading inside its callout", () => {
513
+ const markdown: string = [
514
+ "## Summary",
515
+ "Pool exhausted.",
516
+ "",
517
+ "## Most likely root cause",
518
+ "### Inconclusive",
519
+ "",
520
+ "## Evidence",
521
+ "- pool timeouts [C2]",
522
+ ].join("\n");
523
+ renderView({ analysisMarkdown: markdown });
524
+
525
+ const rootCause: HTMLElement = screen.getByRole("region", {
526
+ name: "Most likely root cause",
527
+ });
528
+ expect(rootCause).toHaveClass("border-amber-200");
529
+ expect(rootCause).toHaveTextContent("Inconclusive");
530
+ expect(reportSectionHeadings()).toEqual([
531
+ "Most likely root cause",
532
+ "Evidence",
533
+ ]);
534
+ });
535
+
536
+ test("keeps the brand heading, evidence list and footer out of the rendered text", () => {
537
+ renderView();
538
+
539
+ for (const text of viewerTexts()) {
540
+ expect(text).not.toContain("Automated Root Cause Analysis");
541
+ expect(text).not.toContain("Evidence checked");
542
+ expect(text).not.toContain("Investigated automatically");
543
+ }
544
+ });
545
+
546
+ test("renders every fragment through the safe viewer with inline references", () => {
547
+ renderView({ analysisTldr: TLDR });
548
+
549
+ expect(viewerCalls().length).toBeGreaterThanOrEqual(4);
550
+
551
+ for (const call of viewerCalls()) {
552
+ expect(call.safeMode).toBe(true);
553
+ expect(typeof call.inlineReferences?.renderCitation).toBe("function");
554
+ expect(typeof call.inlineReferences?.renderEventReference).toBe(
555
+ "function",
556
+ );
557
+ }
558
+ });
559
+
560
+ test("keeps the report's preamble before its sections", () => {
561
+ const markdown: string = [
562
+ "Paged at 18:02 for checkout errors.",
563
+ "",
564
+ "## Summary",
565
+ "Pool exhausted.",
566
+ "",
567
+ "## Root cause",
568
+ "A deploy.",
569
+ ].join("\n");
570
+ renderView({ analysisMarkdown: markdown });
571
+
572
+ const report: HTMLElement = screen.getByLabelText("Investigation report");
573
+ const fragments: Array<HTMLElement> =
574
+ within(report).getAllByTestId("report-markdown");
575
+ expect(fragments[0]).toHaveTextContent(
576
+ "Paged at 18:02 for checkout errors.",
577
+ );
578
+ expect(fragments[1]).toHaveTextContent("A deploy.");
579
+ expect(reportSectionHeadings()).toEqual(["Root cause"]);
580
+ });
581
+
582
+ test("keeps unknown sections under their own title", () => {
583
+ const markdown: string = [
584
+ "**Summary** — Pool exhausted.",
585
+ "",
586
+ "**Most likely root cause** — A deploy.",
587
+ "",
588
+ "**Blast radius**",
589
+ "Checkout and cart.",
590
+ ].join("\n");
591
+ renderView({ analysisMarkdown: markdown });
592
+
593
+ expect(reportSectionHeadings()).toEqual([
594
+ "Most likely root cause",
595
+ "Blast radius",
596
+ ]);
597
+ expect(
598
+ screen.getByRole("region", { name: "Blast radius" }),
599
+ ).toHaveTextContent("Checkout and cart.");
600
+ });
601
+
602
+ test("keeps a second Summary in the report", () => {
603
+ const markdown: string = [
604
+ "**Summary** — First.",
605
+ "",
606
+ "**Most likely root cause** — A deploy.",
607
+ "",
608
+ "## Summary",
609
+ "Second summary.",
610
+ ].join("\n");
611
+ renderView({ analysisMarkdown: markdown });
612
+
613
+ expect(screen.getByLabelText("Investigation summary")).toHaveTextContent(
614
+ "First.",
615
+ );
616
+ expect(reportSectionHeadings()).toEqual([
617
+ "Most likely root cause",
618
+ "Summary",
619
+ ]);
620
+ expect(screen.getByLabelText("Investigation report")).toHaveTextContent(
621
+ "Second summary.",
622
+ );
623
+ });
624
+
625
+ test("falls back to one viewer for an unstructured report", () => {
626
+ const markdown: string =
627
+ "## Alert root cause\n\nThe upstream dependency rejected requests.";
628
+ renderView({ analysisMarkdown: markdown });
629
+
630
+ expect(viewerTexts()).toEqual([markdown]);
631
+ expect(reportSectionHeadings()).toEqual([]);
632
+ expect(
633
+ screen.queryByRole("region", { name: "Investigation summary" }),
634
+ ).toBeNull();
635
+ expect(screen.getByTestId("report-markdown")).toHaveTextContent(
636
+ "The upstream dependency rejected requests.",
637
+ );
638
+ });
639
+
640
+ test("an unstructured branded report drops only its chrome", () => {
641
+ const markdown: string = [
642
+ "## \u{1F9E0} AI — Automated Root Cause Analysis",
643
+ "",
644
+ "Nothing conclusive was found.",
645
+ "",
646
+ "---",
647
+ "*Investigated automatically by OneUptime AI — read-only, 1 query run across your own telemetry.*",
648
+ ].join("\n");
649
+ renderView({ analysisMarkdown: markdown, evidence: [] });
650
+
651
+ expect(viewerTexts()).toEqual(["Nothing conclusive was found."]);
652
+ });
653
+
654
+ test("says so when a structured report has nothing beyond its summary", () => {
655
+ renderView({ analysisMarkdown: "**Summary** — Pool exhausted." });
656
+
657
+ expect(screen.getByLabelText("Investigation report")).toHaveTextContent(
658
+ "The report's findings are in the summary above.",
659
+ );
660
+ });
661
+
662
+ test("offers AI generated and Copy report that copies the original markdown", async () => {
663
+ renderView();
664
+
665
+ const report: HTMLElement = screen.getByLabelText("Investigation report");
666
+ expect(report).toHaveTextContent("AI generated");
667
+ expect(
668
+ within(report).getByRole("heading", { name: "Investigation report" }),
669
+ ).toBeInTheDocument();
670
+
671
+ const copyButton: HTMLElement = within(report).getByRole("button", {
672
+ name: "Copy report",
673
+ });
674
+ await act(async (): Promise<void> => {
675
+ fireEvent.click(copyButton);
676
+ await Promise.resolve();
677
+ });
678
+
679
+ expect(copyToClipboardMock).toHaveBeenCalledTimes(1);
680
+ expect(copyToClipboardMock).toHaveBeenCalledWith(SERVER_REPORT);
681
+ });
682
+ });
683
+
684
+ describe("InvestigationReportView inline references", () => {
685
+ test("turns a recorded citation into a chip", () => {
686
+ renderView();
687
+
688
+ const chips: Array<HTMLElement> = screen.getAllByRole("button", {
689
+ name: "Citation C1: Active incidents (7 total)",
690
+ });
691
+ expect(chips.length).toBeGreaterThan(0);
692
+ expect(chips[0]).toHaveAttribute("type", "button");
693
+ expect(chips[0]).toHaveAttribute("title", "Active incidents (7 total)");
694
+ expect(chips[0]).toHaveTextContent("C1");
695
+ expect(chips[0]).toHaveClass("tabular-nums", "ring-inset");
696
+ });
697
+
698
+ /*
699
+ * The formatted label depends on the viewer's timezone and clock, so the
700
+ * expected name is derived with the same formatter the evidence row uses.
701
+ */
702
+ test("reads a timestamped citation the way its evidence row shows it", () => {
703
+ const rawLabel: string =
704
+ "Logs 2026-09-14T17:20:00Z – 2026-09-14T18:20:00Z (1 shown)";
705
+ const displayLabel: string = formatEvidenceLabel(rawLabel);
706
+ expect(displayLabel).not.toBe(rawLabel);
707
+
708
+ renderView({
709
+ analysisMarkdown:
710
+ "**Summary** — Pool timeouts [C2].\n\n**Most likely root cause** — A deploy [C2].",
711
+ evidence: [evidence[0]!, { ...evidence[1]!, label: rawLabel }],
712
+ });
713
+
714
+ const chips: Array<HTMLElement> = screen.getAllByRole("button", {
715
+ name: `Citation C2: ${displayLabel}`,
716
+ });
717
+ expect(chips).toHaveLength(2);
718
+
719
+ for (const chip of chips) {
720
+ expect(chip.getAttribute("aria-label")).not.toContain("2026-09-14T");
721
+ expect(chip.getAttribute("aria-label")).not.toContain("00Z");
722
+ // The tooltip keeps the raw label, like the evidence row's.
723
+ expect(chip).toHaveAttribute("title", rawLabel);
724
+ expect(chip).toHaveTextContent("C2");
725
+ }
726
+
727
+ // Chip and evidence row announce the same label.
728
+ const evidenceSection: HTMLElement =
729
+ screen.getByLabelText("Evidence checked");
730
+ expect(
731
+ within(evidenceSection).getByRole("button", {
732
+ name: (accessibleName: string): boolean => {
733
+ return accessibleName.includes(displayLabel);
734
+ },
735
+ }),
736
+ ).toBeInTheDocument();
737
+ });
738
+
739
+ test("reads a timestamped legacy citation in local time too", () => {
740
+ const rawLabel: string =
741
+ "Changes 2026-09-13T18:02:00.000Z → 2026-09-14T18:02:00.000Z (3 events)";
742
+ const markdown: string = [
743
+ "**Summary** — A deploy changed the pool [C5].",
744
+ "",
745
+ "**Evidence checked**",
746
+ `- **[C5]** ${rawLabel} — 3 row(s)`,
747
+ "",
748
+ "---",
749
+ "*Investigated automatically by OneUptime AI — read-only, 1 query run across your own telemetry using gpt-5. This is an AI-generated first pass; verify before acting.*",
750
+ ].join("\n");
751
+ const report: ReturnType<typeof parseInvestigationReport> =
752
+ parseInvestigationReport(markdown);
753
+ // The fixture only proves something if the legacy label kept its timestamps.
754
+ expect(report.evidenceChecked).toEqual([
755
+ expect.objectContaining({ citationId: "C5", label: rawLabel }),
756
+ ]);
757
+
758
+ renderView({ analysisMarkdown: markdown, evidence: [] });
759
+
760
+ const chip: HTMLElement = screen.getByRole("button", {
761
+ name: `Citation C5: ${formatEvidenceLabel(rawLabel)}`,
762
+ });
763
+ expect(chip.getAttribute("aria-label")).not.toContain("2026-09-13T");
764
+ expect(chip).toHaveAttribute("title", rawLabel);
765
+ });
766
+
767
+ test("leaves a citation that is not in the evidence list as plain text", () => {
768
+ renderView();
769
+
770
+ const renderers: MarkdownInlineReferenceRenderers = lastRenderers();
771
+ expect(renderers.renderCitation!("C9")).toBeNull();
772
+ expect(renderers.renderCitation!("C1")).not.toBeNull();
773
+ });
774
+
775
+ test("clicking a chip expands, scrolls to and highlights its evidence row", async () => {
776
+ postMock.mockReturnValue(new Promise(() => {}) as never);
777
+ renderView();
778
+
779
+ const evidenceSection: HTMLElement =
780
+ screen.getByLabelText("Evidence checked");
781
+ const toggle: HTMLElement = within(evidenceSection).getByRole("button", {
782
+ name: /Logs 17:20/,
783
+ });
784
+ expect(toggle).toHaveAttribute("aria-expanded", "false");
785
+
786
+ await act(async (): Promise<void> => {
787
+ fireEvent.click(
788
+ screen.getAllByRole("button", {
789
+ name: "Citation C2: Logs 17:20 – 18:20 (1 shown)",
790
+ })[0]!,
791
+ );
792
+ await Promise.resolve();
793
+ });
794
+
795
+ expect(toggle).toHaveAttribute("aria-expanded", "true");
796
+ expect(toggle.closest("li")).toHaveAttribute("data-highlighted", "true");
797
+ expect(Element.prototype.scrollIntoView).toHaveBeenCalledWith({
798
+ block: "nearest",
799
+ behavior: "smooth",
800
+ });
801
+ expect(postMock).toHaveBeenCalledTimes(1);
802
+ expect(
803
+ (postMock.mock.calls[0]![0] as { data: Record<string, unknown> }).data,
804
+ ).toEqual({
805
+ subjectType: "incident",
806
+ subjectId: INCIDENT_ID,
807
+ investigationRunId: RUN_ID,
808
+ citationId: "C2",
809
+ });
810
+ });
811
+
812
+ test("a chip click starts one highlight timer that clears itself", async () => {
813
+ jest.useFakeTimers();
814
+ postMock.mockReturnValue(new Promise(() => {}) as never);
815
+ renderView();
816
+ expect(jest.getTimerCount()).toBe(0);
817
+
818
+ await act(async (): Promise<void> => {
819
+ fireEvent.click(
820
+ screen.getAllByRole("button", {
821
+ name: "Citation C1: Active incidents (7 total)",
822
+ })[0]!,
823
+ );
824
+ await Promise.resolve();
825
+ });
826
+ expect(jest.getTimerCount()).toBe(1);
827
+
828
+ await act(async (): Promise<void> => {
829
+ jest.advanceTimersByTime(2000);
830
+ });
831
+ expect(jest.getTimerCount()).toBe(0);
832
+ expect(document.querySelector('[data-highlighted="true"]')).toBeNull();
833
+ });
834
+
835
+ test("links a resolved incident number to its page", () => {
836
+ renderView();
837
+
838
+ const link: HTMLElement = screen.getByRole("link", {
839
+ name: "INC-6954 · Checkout pool exhausted · Resolved",
840
+ });
841
+ expect(link).toHaveTextContent("#6954");
842
+ expect(link.getAttribute("href")).toMatch(
843
+ new RegExp(`/incidents/${PRIOR_INCIDENT_ID}$`),
844
+ );
845
+ expect(link).toHaveAttribute(
846
+ "title",
847
+ "INC-6954 · Checkout pool exhausted · Resolved",
848
+ );
849
+ });
850
+
851
+ test("leaves an unresolved number as plain text", () => {
852
+ renderView({ references: [] });
853
+
854
+ expect(screen.queryByRole("link")).toBeNull();
855
+ expect(screen.getByLabelText("Investigation report")).toHaveTextContent(
856
+ "prior #6954",
857
+ );
858
+ expect(
859
+ lastRenderers().renderEventReference!({
860
+ kind: "incident",
861
+ number: 6954,
862
+ text: "#6954",
863
+ }),
864
+ ).toBeNull();
865
+ });
866
+
867
+ test("an unqualified number resolves against the subject type", () => {
868
+ const alertReference: InvestigationEventReference = {
869
+ kind: "alert",
870
+ number: 6954,
871
+ id: ALERT_ID,
872
+ displayNumber: "#6954",
873
+ title: "Payment webhook failures",
874
+ };
875
+
876
+ renderView({
877
+ subjectType: "alert",
878
+ references: [priorIncident, alertReference],
879
+ });
880
+
881
+ const link: HTMLElement = screen.getByRole("link", {
882
+ name: "#6954 · Payment webhook failures",
883
+ });
884
+ expect(link.getAttribute("href")).toMatch(
885
+ new RegExp(`/alerts/${ALERT_ID}$`),
886
+ );
887
+ });
888
+
889
+ test("an explicit qualifier wins over the subject type", () => {
890
+ renderView({ subjectType: "alert" });
891
+
892
+ const element: React.ReactElement | null = lastRenderers()
893
+ .renderEventReference!({
894
+ kind: "incident",
895
+ number: 6954,
896
+ text: "#6954",
897
+ });
898
+ expect(element).not.toBeNull();
899
+
900
+ cleanup();
901
+ render(element!);
902
+
903
+ expect(screen.getByRole("link").getAttribute("href")).toMatch(
904
+ new RegExp(`/incidents/${PRIOR_INCIDENT_ID}$`),
905
+ );
906
+ });
907
+
908
+ test("hostile report markdown never produces a link or image outside resolved references", () => {
909
+ const hostile: string = [
910
+ "**Summary** — <img src=x onerror=alert(1)> [click](https://evil.example) ![p](https://evil.example/p.png)",
911
+ "",
912
+ "**Most likely root cause** — [C1]: https://evil.example and #999999",
913
+ ].join("\n");
914
+ const { container } = renderView({
915
+ analysisMarkdown: hostile,
916
+ references: [],
917
+ });
918
+
919
+ expect(container.querySelector("img")).toBeNull();
920
+ expect(container.querySelector("a")).toBeNull();
921
+
922
+ for (const call of viewerCalls()) {
923
+ expect(call.safeMode).toBe(true);
924
+ }
925
+ });
926
+ });
927
+
928
+ describe("InvestigationReportView evidence section", () => {
929
+ test("lists the structured evidence", () => {
930
+ renderView();
931
+
932
+ const section: HTMLElement = screen.getByLabelText("Evidence checked");
933
+ expect(within(section).getByText("2 queries")).toBeInTheDocument();
934
+ expect(
935
+ within(section).getByRole("button", { name: /Active incidents/ }),
936
+ ).toHaveAttribute("aria-expanded", "false");
937
+ });
938
+
939
+ test("falls back to the report's own evidence list for older runs", () => {
940
+ renderView({ evidence: [] });
941
+
942
+ const section: HTMLElement = screen.getByLabelText("Evidence checked");
943
+ expect(within(section).queryAllByRole("button")).toHaveLength(0);
944
+ expect(section).toHaveTextContent("Active incidents (7 total)");
945
+ expect(section).toHaveTextContent("Logs 17:20 – 18:20 (1 shown)");
946
+
947
+ // Legacy citations still become chips that jump to their entry.
948
+ expect(
949
+ screen.getAllByRole("button", {
950
+ name: "Citation C1: Active incidents (7 total)",
951
+ }).length,
952
+ ).toBeGreaterThan(0);
953
+ });
954
+
955
+ test("has no evidence section when nothing was checked", () => {
956
+ renderView({
957
+ analysisMarkdown:
958
+ "**Summary** — Pool exhausted.\n\n**Root cause** — A deploy.",
959
+ evidence: [],
960
+ });
961
+
962
+ expect(screen.queryByLabelText("Evidence checked")).toBeNull();
963
+ });
964
+ });
965
+
966
+ describe("InvestigationReportData payload parsing", () => {
967
+ test("reads well-formed evidence and sorts it by citation number", () => {
968
+ const parsed: Array<InvestigationEvidenceItem> = parseInvestigationEvidence(
969
+ [
970
+ {
971
+ citationId: "C10",
972
+ toolName: "query_metrics",
973
+ label: "P95(latency)",
974
+ rowCount: 40,
975
+ queryArguments: { metricName: "latency" },
976
+ canLoadRows: true,
977
+ },
978
+ {
979
+ citationId: "C2",
980
+ toolName: "search_logs",
981
+ label: "Logs",
982
+ rowCount: 3,
983
+ durationInMs: 120,
984
+ queryArguments: { bodySearchText: "timeout" },
985
+ target: { type: "Logs" },
986
+ executedAt: "2026-09-14T18:01:00.000Z",
987
+ canLoadRows: true,
988
+ },
989
+ ],
990
+ );
991
+
992
+ expect(
993
+ parsed.map((item: InvestigationEvidenceItem): string => {
994
+ return item.citationId;
995
+ }),
996
+ ).toEqual(["C2", "C10"]);
997
+ expect(parsed[0]).toEqual({
998
+ citationId: "C2",
999
+ toolName: "search_logs",
1000
+ label: "Logs",
1001
+ rowCount: 3,
1002
+ durationInMs: 120,
1003
+ queryArguments: { bodySearchText: "timeout" },
1004
+ target: { type: AIChatCitationTargetType.Logs },
1005
+ executedAt: "2026-09-14T18:01:00.000Z",
1006
+ canLoadRows: true,
1007
+ });
1008
+ });
1009
+
1010
+ test("treats a missing or malformed evidence field as empty", () => {
1011
+ expect(parseInvestigationEvidence(undefined)).toEqual([]);
1012
+ expect(parseInvestigationEvidence(null)).toEqual([]);
1013
+ expect(parseInvestigationEvidence({})).toEqual([]);
1014
+ expect(parseInvestigationEvidence("C1")).toEqual([]);
1015
+ });
1016
+
1017
+ test("drops malformed items and duplicate citations", () => {
1018
+ const parsed: Array<InvestigationEvidenceItem> = parseInvestigationEvidence(
1019
+ [
1020
+ null,
1021
+ "C1",
1022
+ { citationId: "1", toolName: "search_logs" },
1023
+ { citationId: "C1234", toolName: "search_logs" },
1024
+ { citationId: "c1", toolName: "search_logs" },
1025
+ { citationId: "C1" },
1026
+ { citationId: "C1", toolName: " " },
1027
+ { citationId: "C1", toolName: "search_logs", label: "first" },
1028
+ { citationId: "C1", toolName: "search_logs", label: "second" },
1029
+ ],
1030
+ );
1031
+
1032
+ expect(parsed).toHaveLength(1);
1033
+ expect(parsed[0]!.label).toBe("first");
1034
+ });
1035
+
1036
+ test("normalises item fields defensively", () => {
1037
+ const [item] = parseInvestigationEvidence([
1038
+ {
1039
+ citationId: "C1",
1040
+ toolName: "query_incidents",
1041
+ rowCount: -4,
1042
+ durationInMs: "fast",
1043
+ queryArguments: {
1044
+ state: "active",
1045
+ limit: 20,
1046
+ problemsOnly: true,
1047
+ nested: { a: 1 },
1048
+ list: ["a", 1, false, { b: 2 }, null],
1049
+ infinite: Number.POSITIVE_INFINITY,
1050
+ },
1051
+ target: { type: "NotARealTarget" },
1052
+ executedAt: "not a date",
1053
+ canLoadRows: "yes",
1054
+ },
1055
+ ]);
1056
+
1057
+ expect(item).toEqual({
1058
+ citationId: "C1",
1059
+ toolName: "query_incidents",
1060
+ label: "query_incidents",
1061
+ rowCount: 0,
1062
+ queryArguments: {
1063
+ state: "active",
1064
+ limit: 20,
1065
+ problemsOnly: true,
1066
+ list: ["a", 1, false],
1067
+ },
1068
+ canLoadRows: false,
1069
+ });
1070
+ });
1071
+
1072
+ test("keeps only id-shaped target params", () => {
1073
+ const [item] = parseInvestigationEvidence([
1074
+ {
1075
+ citationId: "C1",
1076
+ toolName: "get_trace",
1077
+ target: {
1078
+ type: "TraceView",
1079
+ params: {
1080
+ traceId: "4bf92f3577b34da6a3ce929d0e0e4736",
1081
+ evil: "../../settings?x=1",
1082
+ other: 7,
1083
+ },
1084
+ },
1085
+ },
1086
+ {
1087
+ citationId: "C2",
1088
+ toolName: "get_trace",
1089
+ target: { type: "TraceView", params: { evil: "a/b" } },
1090
+ },
1091
+ ]);
1092
+
1093
+ expect(item!.target).toEqual({
1094
+ type: AIChatCitationTargetType.TraceView,
1095
+ params: { traceId: "4bf92f3577b34da6a3ce929d0e0e4736" },
1096
+ });
1097
+ expect(
1098
+ parseInvestigationEvidence([
1099
+ {
1100
+ citationId: "C2",
1101
+ toolName: "get_trace",
1102
+ target: { type: "TraceView", params: { evil: "a/b" } },
1103
+ },
1104
+ ])[0]!.target,
1105
+ ).toEqual({ type: AIChatCitationTargetType.TraceView });
1106
+ });
1107
+
1108
+ test("caps the number of evidence items", () => {
1109
+ const many: Array<Record<string, unknown>> = [];
1110
+
1111
+ for (let index: number = 1; index <= 150; index++) {
1112
+ many.push({ citationId: `C${index}`, toolName: "search_logs" });
1113
+ }
1114
+
1115
+ expect(parseInvestigationEvidence(many)).toHaveLength(MAX_EVIDENCE_ITEMS);
1116
+ });
1117
+
1118
+ test("reads resolved references and rejects anything that is not one", () => {
1119
+ const parsed: Array<InvestigationEventReference> =
1120
+ parseInvestigationReferences([
1121
+ {
1122
+ kind: "incident",
1123
+ number: 6954,
1124
+ id: PRIOR_INCIDENT_ID,
1125
+ displayNumber: "INC-6954",
1126
+ title: "Checkout pool exhausted",
1127
+ stateName: "Resolved",
1128
+ stateColor: "#10b981",
1129
+ },
1130
+ { kind: "alert", number: 7, id: ALERT_ID, title: "Webhook" },
1131
+ { kind: "monitor", number: 1, id: ALERT_ID },
1132
+ { kind: "incident", number: 1.5, id: ALERT_ID },
1133
+ { kind: "incident", number: -1, id: ALERT_ID },
1134
+ { kind: "incident", number: 2, id: "javascript:alert(1)" },
1135
+ { kind: "incident", number: 6954, id: ALERT_ID, title: "duplicate" },
1136
+ null,
1137
+ ]);
1138
+
1139
+ expect(parsed).toEqual([
1140
+ {
1141
+ kind: "incident",
1142
+ number: 6954,
1143
+ id: PRIOR_INCIDENT_ID,
1144
+ displayNumber: "INC-6954",
1145
+ title: "Checkout pool exhausted",
1146
+ stateName: "Resolved",
1147
+ stateColor: "#10b981",
1148
+ },
1149
+ {
1150
+ kind: "alert",
1151
+ number: 7,
1152
+ id: ALERT_ID,
1153
+ displayNumber: "#7",
1154
+ title: "Webhook",
1155
+ },
1156
+ ]);
1157
+ });
1158
+
1159
+ test("drops a state colour that is not a hex colour", () => {
1160
+ const [reference] = parseInvestigationReferences([
1161
+ {
1162
+ kind: "incident",
1163
+ number: 1,
1164
+ id: PRIOR_INCIDENT_ID,
1165
+ stateColor: "red; background: url(https://evil)",
1166
+ },
1167
+ ]);
1168
+
1169
+ expect(reference!.stateColor).toBeUndefined();
1170
+ expect(parseInvestigationReferences(undefined)).toEqual([]);
1171
+ });
1172
+
1173
+ test("reads an evidence rows response", () => {
1174
+ expect(
1175
+ parseInvestigationEvidenceRows({
1176
+ citationId: "C1",
1177
+ toolName: "query_incidents",
1178
+ label: "Active incidents (7 total)",
1179
+ rowCount: 7,
1180
+ isTruncated: true,
1181
+ executedAt: "2026-09-15T09:00:00.000Z",
1182
+ isPinnedToInvestigationTime: true,
1183
+ investigatedAt: "2026-09-14T18:01:00.000Z",
1184
+ widget: {
1185
+ id: "W1",
1186
+ type: AIChatWidgetType.IncidentList,
1187
+ title: "Active incidents",
1188
+ data: { items: [] },
1189
+ },
1190
+ text: "rows",
1191
+ }),
1192
+ ).toEqual({
1193
+ citationId: "C1",
1194
+ toolName: "query_incidents",
1195
+ label: "Active incidents (7 total)",
1196
+ rowCount: 7,
1197
+ isTruncated: true,
1198
+ executedAt: "2026-09-15T09:00:00.000Z",
1199
+ isPinnedToInvestigationTime: true,
1200
+ investigatedAt: "2026-09-14T18:01:00.000Z",
1201
+ widget: {
1202
+ id: "W1",
1203
+ type: AIChatWidgetType.IncidentList,
1204
+ title: "Active incidents",
1205
+ data: { items: [] },
1206
+ },
1207
+ text: "rows",
1208
+ });
1209
+ });
1210
+
1211
+ test("rejects a rows response without a citation or row count and drops unknown widgets", () => {
1212
+ expect(parseInvestigationEvidenceRows(null)).toBeNull();
1213
+ expect(parseInvestigationEvidenceRows({ rowCount: 1 })).toBeNull();
1214
+ expect(
1215
+ parseInvestigationEvidenceRows({ citationId: "C1", rowCount: "7" }),
1216
+ ).toBeNull();
1217
+
1218
+ const parsed: ReturnType<typeof parseInvestigationEvidenceRows> =
1219
+ parseInvestigationEvidenceRows({
1220
+ citationId: "C1",
1221
+ rowCount: 2,
1222
+ widget: { type: "Iframe", data: {} },
1223
+ text: "x".repeat(30_000),
1224
+ });
1225
+ expect(parsed!.widget).toBeUndefined();
1226
+ expect(parsed!.text).toHaveLength(20_000);
1227
+ expect(parsed!.isPinnedToInvestigationTime).toBe(false);
1228
+ });
1229
+ });
1230
+
1231
+ describe("InvestigationReportData summary text", () => {
1232
+ test("strips markdown down to one line of words", () => {
1233
+ expect(
1234
+ markdownToPlainText(
1235
+ "**The pool** ran _dry_ after `deploy-42` [C1][C2].\n\n- see [the logs](https://x.example) ![chart](https://x/c.png)\n> quoted\n## Heading",
1236
+ ),
1237
+ ).toBe(
1238
+ "The pool ran dry after deploy-42. see the logs chart quoted Heading",
1239
+ );
1240
+ });
1241
+
1242
+ test("keeps snake_case words and multiplication intact", () => {
1243
+ expect(markdownToPlainText("check pool_size_max and 2 * 3 * 4")).toBe(
1244
+ "check pool_size_max and 2 * 3 * 4",
1245
+ );
1246
+ });
1247
+
1248
+ test("clips at a word boundary within the limit", () => {
1249
+ const text: string =
1250
+ "The database connection pool was exhausted after the deploy halved its size";
1251
+ const clipped: string = clipText(text, 40);
1252
+
1253
+ expect(clipped.length).toBeLessThanOrEqual(40);
1254
+ expect(clipped.endsWith("…")).toBe(true);
1255
+ expect(text.startsWith(clipped.slice(0, -1))).toBe(true);
1256
+ expect(clipText("short", 40)).toBe("short");
1257
+ });
1258
+
1259
+ test("clips a single long word hard", () => {
1260
+ const clipped: string = clipText("x".repeat(100), 10);
1261
+ expect(clipped).toBe(`${"x".repeat(9)}…`);
1262
+ });
1263
+
1264
+ test("prefers the TL;DR, then the parsed Summary, else null", () => {
1265
+ const report: ReturnType<typeof parseInvestigationReport> =
1266
+ parseInvestigationReport(SERVER_REPORT);
1267
+
1268
+ expect(
1269
+ getInvestigationReportSummaryText({ analysisTldr: ` ${TLDR}\n`, report }),
1270
+ ).toBe(TLDR);
1271
+ expect(
1272
+ getInvestigationReportSummaryText({ analysisTldr: null, report }),
1273
+ ).toBe("the connection pool ran dry after the 18:00 deploy.");
1274
+ expect(
1275
+ getInvestigationReportSummaryText({
1276
+ analysisTldr: " ",
1277
+ report: parseInvestigationReport("## Root cause\n\nA deploy."),
1278
+ }),
1279
+ ).toBeNull();
1280
+ expect(
1281
+ getInvestigationReportSummaryText({ analysisTldr: null, report: null }),
1282
+ ).toBeNull();
1283
+ });
1284
+
1285
+ test("bounds the summary text", () => {
1286
+ const longSummary: string = `**Summary** — ${"word ".repeat(200)}`;
1287
+ const summary: string | null = getInvestigationReportSummaryText({
1288
+ analysisTldr: null,
1289
+ report: parseInvestigationReport(longSummary),
1290
+ });
1291
+
1292
+ expect(summary!.length).toBeLessThanOrEqual(MAX_REPORT_SUMMARY_LENGTH);
1293
+ expect(summary!.endsWith("…")).toBe(true);
1294
+ });
1295
+ });