@oneuptime/common 11.4.0 → 11.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (473) hide show
  1. package/Models/DatabaseModels/AIRun.ts +50 -0
  2. package/Models/DatabaseModels/AlertSeverity.ts +6 -0
  3. package/Models/DatabaseModels/IncidentSeverity.ts +6 -0
  4. package/Models/DatabaseModels/Index.ts +16 -0
  5. package/Models/DatabaseModels/IoTDeviceCredential.ts +471 -0
  6. package/Models/DatabaseModels/LlmLog.ts +56 -0
  7. package/Models/DatabaseModels/LlmProvider.ts +33 -0
  8. package/Models/DatabaseModels/NetworkDevice.ts +1385 -0
  9. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +717 -0
  10. package/Models/DatabaseModels/NetworkDeviceLabelRule.ts +514 -0
  11. package/Models/DatabaseModels/NetworkDeviceOwnerRule.ts +596 -0
  12. package/Models/DatabaseModels/NetworkDeviceOwnerTeam.ts +487 -0
  13. package/Models/DatabaseModels/NetworkDeviceOwnerUser.ts +486 -0
  14. package/Models/DatabaseModels/NetworkInterface.ts +563 -0
  15. package/Models/DatabaseModels/Project.ts +145 -0
  16. package/Models/DatabaseModels/TelemetryEntityRelationship.ts +54 -0
  17. package/Server/API/AIAgentTaskAPI.ts +10 -24
  18. package/Server/API/AIInvestigationAPI.ts +131 -63
  19. package/Server/API/AlertAPI.ts +5 -0
  20. package/Server/API/IncidentAPI.ts +10 -0
  21. package/Server/API/IncidentEpisodeAPI.ts +5 -0
  22. package/Server/API/LlmProviderAPI.ts +4 -0
  23. package/Server/API/ScheduledMaintenanceAPI.ts +5 -0
  24. package/Server/API/TelemetryAPI.ts +4 -3
  25. package/Server/Infrastructure/Postgres/SchemaMigrations/1783650000000-AddAdditionalParamsToLlmProvider.ts +19 -0
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1783695782697-AddCacheTokenColumnsToLlmLog.ts +25 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/1783701585317-AddAlertInvestigationGating.ts +31 -0
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/1783702431535-AddAiDailyAutonomousTokenLimitToProject.ts +19 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1783720000000-AddNetworkDeviceTables.ts +105 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1783721121260-AddAttemptCountToAIRun.ts +15 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/1783730000000-AddNetworkDeviceOwnersAndRules.ts +192 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1783740000000-AddNetworkDeviceDiscoveryScan.ts +37 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1783750000000-AddLldpNeighborsToNetworkDevice.ts +19 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/1783760576655-AddInvestigationTuningToProject.ts +25 -0
  35. package/Server/Infrastructure/Postgres/SchemaMigrations/1783762505482-AddTelemetryEntityRelationshipMetrics.ts +31 -0
  36. package/Server/Infrastructure/Postgres/SchemaMigrations/1783780000000-AddIoTDeviceCredentialTable.ts +79 -0
  37. package/Server/Infrastructure/Postgres/SchemaMigrations/1783790000000-AddSnmpV3AuthColumnsToNetworkDevice.ts +49 -0
  38. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +24 -0
  39. package/Server/Services/AIAgentTaskService.ts +62 -0
  40. package/Server/Services/AIRunService.ts +58 -0
  41. package/Server/Services/AIService.ts +101 -0
  42. package/Server/Services/AnalyticsDatabaseService.ts +27 -0
  43. package/Server/Services/IncidentService.ts +14 -41
  44. package/Server/Services/Index.ts +14 -0
  45. package/Server/Services/IoTDeviceCredentialService.ts +351 -0
  46. package/Server/Services/IoTDeviceService.ts +64 -21
  47. package/Server/Services/LlmLogService.ts +26 -0
  48. package/Server/Services/LlmProviderService.ts +3 -0
  49. package/Server/Services/LogAggregationService.ts +29 -11
  50. package/Server/Services/MetricService.ts +603 -13
  51. package/Server/Services/MonitorService.ts +4 -1
  52. package/Server/Services/NetworkDeviceDiscoveryScanService.ts +10 -0
  53. package/Server/Services/NetworkDeviceLabelRuleEngineService.ts +204 -0
  54. package/Server/Services/NetworkDeviceLabelRuleService.ts +14 -0
  55. package/Server/Services/NetworkDeviceOwnerRuleEngineService.ts +222 -0
  56. package/Server/Services/NetworkDeviceOwnerRuleService.ts +14 -0
  57. package/Server/Services/NetworkDeviceOwnerTeamService.ts +10 -0
  58. package/Server/Services/NetworkDeviceOwnerUserService.ts +10 -0
  59. package/Server/Services/NetworkDeviceService.ts +50 -0
  60. package/Server/Services/NetworkInterfaceService.ts +10 -0
  61. package/Server/Services/TelemetryEntityRelationshipService.ts +23 -0
  62. package/Server/Services/TelemetryEntityService.ts +72 -2
  63. package/Server/Utils/AI/Sentinel/AlertInvestigationRunner.ts +244 -12
  64. package/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.ts +92 -19
  65. package/Server/Utils/AI/Sentinel/InvestigationQueue.ts +493 -0
  66. package/Server/Utils/AI/Sentinel/README.md +10 -0
  67. package/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.ts +88 -68
  68. package/Server/Utils/AI/Toolbox/Index.ts +2 -1
  69. package/Server/Utils/AI/Toolbox/MetricTools.ts +391 -0
  70. package/Server/Utils/LLM/LLMService.ts +21 -0
  71. package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +44 -0
  72. package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +170 -2
  73. package/Server/Utils/Monitor/IoTDeviceAbsenceSeries.ts +101 -0
  74. package/Server/Utils/Monitor/MonitorAlert.ts +15 -3
  75. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +1 -1
  76. package/Server/Utils/Monitor/MonitorIncident.ts +19 -3
  77. package/Server/Utils/Monitor/MonitorMetricUtil.ts +195 -2
  78. package/Server/Utils/Monitor/MonitorResource.ts +77 -21
  79. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +1 -1
  80. package/Server/Utils/Monitor/NetworkDeviceHydrationUtil.ts +201 -0
  81. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +215 -0
  82. package/Server/Utils/Monitor/SnmpInterfaceRateUtil.ts +169 -0
  83. package/Tests/Server/Services/AIAgentTaskServiceClaim.test.ts +146 -0
  84. package/Tests/Server/Services/AIServiceDailyBudget.test.ts +173 -0
  85. package/Tests/Server/Services/IncidentInternalNoteAnnouncement.test.ts +97 -0
  86. package/Tests/Server/Services/IoTDeviceService.test.ts +54 -0
  87. package/Tests/Server/Services/LogAggregationService.test.ts +22 -0
  88. package/Tests/Server/Services/MetricServiceAggregate.test.ts +194 -0
  89. package/Tests/Server/Utils/AI/BaselineAnomalyTool.test.ts +253 -0
  90. package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +44 -0
  91. package/Tests/Server/Utils/AI/SentinelAlertGating.test.ts +360 -0
  92. package/Tests/Server/Utils/AI/SentinelInvestigationQueue.test.ts +295 -0
  93. package/Tests/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.test.ts +59 -0
  94. package/Tests/Server/Utils/Monitor/IoTDeviceAbsenceSeries.test.ts +113 -0
  95. package/Tests/Server/Utils/Monitor/PerSeriesRecoveryResolution.test.ts +198 -0
  96. package/Tests/Types/AI/AIAgentTaskStatus.test.ts +86 -0
  97. package/Tests/Types/BaseDatabase/AggregationIntervalUtil.test.ts +127 -0
  98. package/Tests/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.test.ts +152 -0
  99. package/Tests/Types/Monitor/MonitorType.test.ts +1 -1
  100. package/Tests/Types/Trace/TraceRecordingRuleDefinition.test.ts +308 -0
  101. package/Tests/UI/Components/Dropdown.test.tsx +17 -0
  102. package/Tests/UI/Components/EntityDropdown.test.tsx +130 -0
  103. package/Tests/UI/Components/Modal.test.tsx +144 -2
  104. package/Tests/UI/Components/TelemetrySearchBar.test.tsx +90 -0
  105. package/Tests/UI/Utils/Theme.test.ts +161 -0
  106. package/Tests/Utils/Monitor/LatencyMatrixUtil.test.ts +111 -0
  107. package/Types/AI/AIRunStatus.ts +9 -0
  108. package/Types/BaseDatabase/AggregateBy.ts +12 -0
  109. package/Types/Dashboard/DashboardComponentType.ts +1 -0
  110. package/Types/Dashboard/DashboardComponents/DashboardLogChartComponent.ts +24 -0
  111. package/Types/Dashboard/DashboardTemplates.ts +9 -1
  112. package/Types/LlmLogStatus.ts +1 -0
  113. package/Types/Monitor/CriteriaFilter.ts +9 -0
  114. package/Types/Monitor/HttpPhaseTimings.ts +16 -0
  115. package/Types/Monitor/IotAlertTemplates.ts +22 -3
  116. package/Types/Monitor/LatencyMatrix.ts +27 -0
  117. package/Types/Monitor/MonitorCriteriaInstance.ts +2 -2
  118. package/Types/Monitor/MonitorMetricType.ts +27 -0
  119. package/Types/Monitor/MonitorStep.ts +36 -11
  120. package/Types/Monitor/MonitorStepNetworkDeviceMonitor.ts +55 -0
  121. package/Types/Monitor/MonitorStepSnmpMonitor.ts +8 -0
  122. package/Types/Monitor/MonitorType.ts +14 -9
  123. package/Types/Monitor/PingMonitor/PingMonitorResponse.ts +20 -0
  124. package/Types/Monitor/SnmpMonitor/LldpNeighbor.ts +12 -0
  125. package/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.ts +116 -0
  126. package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +29 -0
  127. package/Types/Monitor/SnmpMonitor/SnmpInterface.ts +31 -0
  128. package/Types/Monitor/SnmpMonitor/SnmpMonitorResponse.ts +24 -0
  129. package/Types/Monitor/SnmpMonitor/SnmpTrap.ts +20 -0
  130. package/Types/Monitor/SnmpMonitor/SnmpVendorTemplate.ts +173 -0
  131. package/Types/Permission.ts +314 -0
  132. package/Types/Probe/ProbeMonitorResponse.ts +22 -0
  133. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +8 -5
  134. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +4 -1
  135. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +6 -3
  136. package/UI/Components/CodeBlock/CodeBlock.tsx +2 -1
  137. package/UI/Components/CodeEditor/CodeEditor.tsx +3 -0
  138. package/UI/Components/Divider/Divider.tsx +1 -1
  139. package/UI/Components/Dropdown/Dropdown.tsx +13 -4
  140. package/UI/Components/EntityDropdown/EntityDropdown.tsx +110 -2
  141. package/UI/Components/ErrorBoundary.tsx +2 -1
  142. package/UI/Components/EventItem/EventItem.tsx +2 -2
  143. package/UI/Components/GanttChart/Bar/Index.tsx +1 -1
  144. package/UI/Components/Graphs/UptimeBarTooltip.tsx +30 -23
  145. package/UI/Components/Header/Header.tsx +7 -0
  146. package/UI/Components/Header/HeaderIconDropdownButton.tsx +2 -0
  147. package/UI/Components/Icon/Icon.tsx +8 -6
  148. package/UI/Components/Label/Label.tsx +2 -2
  149. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +22 -10
  150. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +9 -3
  151. package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +8 -8
  152. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +44 -20
  153. package/UI/Components/Modal/ConfirmModal.tsx +1 -1
  154. package/UI/Components/Modal/Modal.tsx +213 -121
  155. package/UI/Components/Modal/ModalFooter.tsx +33 -22
  156. package/UI/Components/ModelTable/TableView.tsx +1 -1
  157. package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +20 -12
  158. package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +2 -2
  159. package/UI/Components/Pill/Pill.tsx +21 -13
  160. package/UI/Components/StatusBubble/StatusBubble.tsx +3 -3
  161. package/UI/Components/Tabs/Tabs.tsx +10 -1
  162. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +7 -1
  163. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +9 -3
  164. package/UI/Components/TelemetryViewer/components/TelemetrySearchBar.tsx +62 -4
  165. package/UI/Components/Workflow/Component.tsx +53 -33
  166. package/UI/Components/Workflow/ComponentPortViewer.tsx +6 -6
  167. package/UI/Components/Workflow/ComponentReturnValueViewer.tsx +8 -8
  168. package/UI/Components/Workflow/ComponentsModal.tsx +16 -9
  169. package/UI/Components/Workflow/Workflow.tsx +13 -11
  170. package/UI/Utils/Navigation.ts +10 -1
  171. package/UI/Utils/Theme.ts +140 -0
  172. package/Utils/Dashboard/Components/DashboardLogChartComponent.ts +99 -0
  173. package/Utils/Dashboard/Components/Index.ts +7 -0
  174. package/Utils/Monitor/LatencyMatrixUtil.ts +162 -0
  175. package/Utils/Monitor/MonitorMetricType.ts +141 -3
  176. package/Utils/Monitor/NetworkTopologyUtil.ts +159 -0
  177. package/Utils/Telemetry/EntityRelationship.ts +11 -0
  178. package/build/dist/Models/DatabaseModels/AIRun.js +52 -0
  179. package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
  180. package/build/dist/Models/DatabaseModels/AlertSeverity.js +6 -0
  181. package/build/dist/Models/DatabaseModels/AlertSeverity.js.map +1 -1
  182. package/build/dist/Models/DatabaseModels/IncidentSeverity.js +6 -0
  183. package/build/dist/Models/DatabaseModels/IncidentSeverity.js.map +1 -1
  184. package/build/dist/Models/DatabaseModels/Index.js +16 -0
  185. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  186. package/build/dist/Models/DatabaseModels/IoTDeviceCredential.js +493 -0
  187. package/build/dist/Models/DatabaseModels/IoTDeviceCredential.js.map +1 -0
  188. package/build/dist/Models/DatabaseModels/LlmLog.js +58 -0
  189. package/build/dist/Models/DatabaseModels/LlmLog.js.map +1 -1
  190. package/build/dist/Models/DatabaseModels/LlmProvider.js +33 -0
  191. package/build/dist/Models/DatabaseModels/LlmProvider.js.map +1 -1
  192. package/build/dist/Models/DatabaseModels/NetworkDevice.js +1426 -0
  193. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -0
  194. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +738 -0
  195. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -0
  196. package/build/dist/Models/DatabaseModels/NetworkDeviceLabelRule.js +522 -0
  197. package/build/dist/Models/DatabaseModels/NetworkDeviceLabelRule.js.map +1 -0
  198. package/build/dist/Models/DatabaseModels/NetworkDeviceOwnerRule.js +603 -0
  199. package/build/dist/Models/DatabaseModels/NetworkDeviceOwnerRule.js.map +1 -0
  200. package/build/dist/Models/DatabaseModels/NetworkDeviceOwnerTeam.js +503 -0
  201. package/build/dist/Models/DatabaseModels/NetworkDeviceOwnerTeam.js.map +1 -0
  202. package/build/dist/Models/DatabaseModels/NetworkDeviceOwnerUser.js +502 -0
  203. package/build/dist/Models/DatabaseModels/NetworkDeviceOwnerUser.js.map +1 -0
  204. package/build/dist/Models/DatabaseModels/NetworkInterface.js +589 -0
  205. package/build/dist/Models/DatabaseModels/NetworkInterface.js.map +1 -0
  206. package/build/dist/Models/DatabaseModels/Project.js +147 -0
  207. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  208. package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js +57 -0
  209. package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js.map +1 -1
  210. package/build/dist/Server/API/AIAgentTaskAPI.js +8 -23
  211. package/build/dist/Server/API/AIAgentTaskAPI.js.map +1 -1
  212. package/build/dist/Server/API/AIInvestigationAPI.js +95 -55
  213. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  214. package/build/dist/Server/API/AlertAPI.js +5 -0
  215. package/build/dist/Server/API/AlertAPI.js.map +1 -1
  216. package/build/dist/Server/API/IncidentAPI.js +10 -0
  217. package/build/dist/Server/API/IncidentAPI.js.map +1 -1
  218. package/build/dist/Server/API/IncidentEpisodeAPI.js +5 -0
  219. package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
  220. package/build/dist/Server/API/LlmProviderAPI.js +5 -7
  221. package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
  222. package/build/dist/Server/API/ScheduledMaintenanceAPI.js +5 -0
  223. package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
  224. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  225. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783650000000-AddAdditionalParamsToLlmProvider.js +12 -0
  226. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783650000000-AddAdditionalParamsToLlmProvider.js.map +1 -0
  227. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783695782697-AddCacheTokenColumnsToLlmLog.js +14 -0
  228. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783695782697-AddCacheTokenColumnsToLlmLog.js.map +1 -0
  229. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783701585317-AddAlertInvestigationGating.js +18 -0
  230. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783701585317-AddAlertInvestigationGating.js.map +1 -0
  231. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783702431535-AddAiDailyAutonomousTokenLimitToProject.js +12 -0
  232. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783702431535-AddAiDailyAutonomousTokenLimitToProject.js.map +1 -0
  233. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783720000000-AddNetworkDeviceTables.js +51 -0
  234. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783720000000-AddNetworkDeviceTables.js.map +1 -0
  235. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783721121260-AddAttemptCountToAIRun.js +12 -0
  236. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783721121260-AddAttemptCountToAIRun.js.map +1 -0
  237. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783730000000-AddNetworkDeviceOwnersAndRules.js +101 -0
  238. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783730000000-AddNetworkDeviceOwnersAndRules.js.map +1 -0
  239. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783740000000-AddNetworkDeviceDiscoveryScan.js +18 -0
  240. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783740000000-AddNetworkDeviceDiscoveryScan.js.map +1 -0
  241. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783750000000-AddLldpNeighborsToNetworkDevice.js +12 -0
  242. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783750000000-AddLldpNeighborsToNetworkDevice.js.map +1 -0
  243. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783760576655-AddInvestigationTuningToProject.js +14 -0
  244. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783760576655-AddInvestigationTuningToProject.js.map +1 -0
  245. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783762505482-AddTelemetryEntityRelationshipMetrics.js +16 -0
  246. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783762505482-AddTelemetryEntityRelationshipMetrics.js.map +1 -0
  247. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783780000000-AddIoTDeviceCredentialTable.js +38 -0
  248. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783780000000-AddIoTDeviceCredentialTable.js.map +1 -0
  249. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783790000000-AddSnmpV3AuthColumnsToNetworkDevice.js +22 -0
  250. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783790000000-AddSnmpV3AuthColumnsToNetworkDevice.js.map +1 -0
  251. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +24 -0
  252. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  253. package/build/dist/Server/Services/AIAgentTaskService.js +59 -0
  254. package/build/dist/Server/Services/AIAgentTaskService.js.map +1 -1
  255. package/build/dist/Server/Services/AIRunService.js +33 -0
  256. package/build/dist/Server/Services/AIRunService.js.map +1 -1
  257. package/build/dist/Server/Services/AIService.js +82 -9
  258. package/build/dist/Server/Services/AIService.js.map +1 -1
  259. package/build/dist/Server/Services/AnalyticsDatabaseService.js +19 -0
  260. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  261. package/build/dist/Server/Services/IncidentService.js +14 -25
  262. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  263. package/build/dist/Server/Services/Index.js +14 -0
  264. package/build/dist/Server/Services/Index.js.map +1 -1
  265. package/build/dist/Server/Services/IoTDeviceCredentialService.js +321 -0
  266. package/build/dist/Server/Services/IoTDeviceCredentialService.js.map +1 -0
  267. package/build/dist/Server/Services/IoTDeviceService.js +49 -15
  268. package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
  269. package/build/dist/Server/Services/LlmLogService.js +29 -0
  270. package/build/dist/Server/Services/LlmLogService.js.map +1 -1
  271. package/build/dist/Server/Services/LlmProviderService.js +3 -0
  272. package/build/dist/Server/Services/LlmProviderService.js.map +1 -1
  273. package/build/dist/Server/Services/LogAggregationService.js +21 -7
  274. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  275. package/build/dist/Server/Services/MetricService.js +449 -7
  276. package/build/dist/Server/Services/MetricService.js.map +1 -1
  277. package/build/dist/Server/Services/MonitorService.js +2 -1
  278. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  279. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +9 -0
  280. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -0
  281. package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js +166 -0
  282. package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js.map +1 -0
  283. package/build/dist/Server/Services/NetworkDeviceLabelRuleService.js +13 -0
  284. package/build/dist/Server/Services/NetworkDeviceLabelRuleService.js.map +1 -0
  285. package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js +186 -0
  286. package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js.map +1 -0
  287. package/build/dist/Server/Services/NetworkDeviceOwnerRuleService.js +13 -0
  288. package/build/dist/Server/Services/NetworkDeviceOwnerRuleService.js.map +1 -0
  289. package/build/dist/Server/Services/NetworkDeviceOwnerTeamService.js +9 -0
  290. package/build/dist/Server/Services/NetworkDeviceOwnerTeamService.js.map +1 -0
  291. package/build/dist/Server/Services/NetworkDeviceOwnerUserService.js +9 -0
  292. package/build/dist/Server/Services/NetworkDeviceOwnerUserService.js.map +1 -0
  293. package/build/dist/Server/Services/NetworkDeviceService.js +52 -0
  294. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -0
  295. package/build/dist/Server/Services/NetworkInterfaceService.js +9 -0
  296. package/build/dist/Server/Services/NetworkInterfaceService.js.map +1 -0
  297. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +22 -0
  298. package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
  299. package/build/dist/Server/Services/TelemetryEntityService.js +64 -2
  300. package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
  301. package/build/dist/Server/Utils/AI/Sentinel/AlertInvestigationRunner.js +189 -9
  302. package/build/dist/Server/Utils/AI/Sentinel/AlertInvestigationRunner.js.map +1 -1
  303. package/build/dist/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.js +84 -17
  304. package/build/dist/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.js.map +1 -1
  305. package/build/dist/Server/Utils/AI/Sentinel/InvestigationQueue.js +428 -0
  306. package/build/dist/Server/Utils/AI/Sentinel/InvestigationQueue.js.map +1 -0
  307. package/build/dist/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.js +63 -47
  308. package/build/dist/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.js.map +1 -1
  309. package/build/dist/Server/Utils/AI/Toolbox/Index.js +2 -1
  310. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
  311. package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js +300 -0
  312. package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js.map +1 -1
  313. package/build/dist/Server/Utils/LLM/LLMService.js +16 -0
  314. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  315. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +29 -0
  316. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
  317. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +130 -2
  318. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -1
  319. package/build/dist/Server/Utils/Monitor/IoTDeviceAbsenceSeries.js +81 -0
  320. package/build/dist/Server/Utils/Monitor/IoTDeviceAbsenceSeries.js.map +1 -0
  321. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +18 -7
  322. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  323. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +1 -1
  324. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  325. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +25 -10
  326. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  327. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +155 -5
  328. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  329. package/build/dist/Server/Utils/Monitor/MonitorResource.js +76 -30
  330. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  331. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +1 -1
  332. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  333. package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js +147 -0
  334. package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js.map +1 -0
  335. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +167 -0
  336. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -0
  337. package/build/dist/Server/Utils/Monitor/SnmpInterfaceRateUtil.js +93 -0
  338. package/build/dist/Server/Utils/Monitor/SnmpInterfaceRateUtil.js.map +1 -0
  339. package/build/dist/Types/AI/AIRunStatus.js +9 -0
  340. package/build/dist/Types/AI/AIRunStatus.js.map +1 -1
  341. package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
  342. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  343. package/build/dist/Types/Dashboard/DashboardComponents/DashboardLogChartComponent.js +2 -0
  344. package/build/dist/Types/Dashboard/DashboardComponents/DashboardLogChartComponent.js.map +1 -0
  345. package/build/dist/Types/Dashboard/DashboardTemplates.js +9 -1
  346. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  347. package/build/dist/Types/LlmLogStatus.js +1 -0
  348. package/build/dist/Types/LlmLogStatus.js.map +1 -1
  349. package/build/dist/Types/Monitor/CriteriaFilter.js +9 -0
  350. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  351. package/build/dist/Types/Monitor/HttpPhaseTimings.js +2 -0
  352. package/build/dist/Types/Monitor/HttpPhaseTimings.js.map +1 -0
  353. package/build/dist/Types/Monitor/IotAlertTemplates.js +7 -7
  354. package/build/dist/Types/Monitor/IotAlertTemplates.js.map +1 -1
  355. package/build/dist/Types/Monitor/LatencyMatrix.js +2 -0
  356. package/build/dist/Types/Monitor/LatencyMatrix.js.map +1 -0
  357. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +2 -2
  358. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  359. package/build/dist/Types/Monitor/MonitorMetricType.js +24 -0
  360. package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
  361. package/build/dist/Types/Monitor/MonitorStep.js +22 -9
  362. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  363. package/build/dist/Types/Monitor/MonitorStepNetworkDeviceMonitor.js +36 -0
  364. package/build/dist/Types/Monitor/MonitorStepNetworkDeviceMonitor.js.map +1 -0
  365. package/build/dist/Types/Monitor/MonitorStepSnmpMonitor.js +3 -0
  366. package/build/dist/Types/Monitor/MonitorStepSnmpMonitor.js.map +1 -1
  367. package/build/dist/Types/Monitor/MonitorType.js +14 -9
  368. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  369. package/build/dist/Types/Monitor/PingMonitor/PingMonitorResponse.js +2 -0
  370. package/build/dist/Types/Monitor/PingMonitor/PingMonitorResponse.js.map +1 -0
  371. package/build/dist/Types/Monitor/SnmpMonitor/LldpNeighbor.js +2 -0
  372. package/build/dist/Types/Monitor/SnmpMonitor/LldpNeighbor.js.map +1 -0
  373. package/build/dist/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.js +89 -0
  374. package/build/dist/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.js.map +1 -0
  375. package/build/dist/Types/Monitor/SnmpMonitor/NetworkTopology.js +2 -0
  376. package/build/dist/Types/Monitor/SnmpMonitor/NetworkTopology.js.map +1 -0
  377. package/build/dist/Types/Monitor/SnmpMonitor/SnmpInterface.js +2 -0
  378. package/build/dist/Types/Monitor/SnmpMonitor/SnmpInterface.js.map +1 -0
  379. package/build/dist/Types/Monitor/SnmpMonitor/SnmpTrap.js +2 -0
  380. package/build/dist/Types/Monitor/SnmpMonitor/SnmpTrap.js.map +1 -0
  381. package/build/dist/Types/Monitor/SnmpMonitor/SnmpVendorTemplate.js +137 -0
  382. package/build/dist/Types/Monitor/SnmpMonitor/SnmpVendorTemplate.js.map +1 -0
  383. package/build/dist/Types/Permission.js +280 -0
  384. package/build/dist/Types/Permission.js.map +1 -1
  385. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +7 -4
  386. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  387. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +4 -1
  388. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  389. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +6 -3
  390. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  391. package/build/dist/UI/Components/CodeBlock/CodeBlock.js +1 -1
  392. package/build/dist/UI/Components/CodeBlock/CodeBlock.js.map +1 -1
  393. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +3 -1
  394. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  395. package/build/dist/UI/Components/Divider/Divider.js +1 -1
  396. package/build/dist/UI/Components/Divider/Divider.js.map +1 -1
  397. package/build/dist/UI/Components/Dropdown/Dropdown.js +10 -4
  398. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  399. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +76 -3
  400. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  401. package/build/dist/UI/Components/ErrorBoundary.js +2 -1
  402. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  403. package/build/dist/UI/Components/EventItem/EventItem.js +2 -2
  404. package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
  405. package/build/dist/UI/Components/GanttChart/Bar/Index.js +1 -1
  406. package/build/dist/UI/Components/GanttChart/Bar/Index.js.map +1 -1
  407. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +29 -23
  408. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -1
  409. package/build/dist/UI/Components/Header/Header.js +1 -0
  410. package/build/dist/UI/Components/Header/Header.js.map +1 -1
  411. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +1 -1
  412. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  413. package/build/dist/UI/Components/Icon/Icon.js +8 -6
  414. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  415. package/build/dist/UI/Components/Label/Label.js +2 -2
  416. package/build/dist/UI/Components/Label/Label.js.map +1 -1
  417. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +20 -8
  418. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  419. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +8 -2
  420. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  421. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +8 -8
  422. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +40 -20
  423. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  424. package/build/dist/UI/Components/Modal/ConfirmModal.js +1 -1
  425. package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
  426. package/build/dist/UI/Components/Modal/Modal.js +109 -51
  427. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  428. package/build/dist/UI/Components/Modal/ModalFooter.js +13 -10
  429. package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
  430. package/build/dist/UI/Components/ModelTable/TableView.js +1 -1
  431. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  432. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +17 -12
  433. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -1
  434. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +2 -2
  435. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
  436. package/build/dist/UI/Components/Pill/Pill.js +12 -13
  437. package/build/dist/UI/Components/Pill/Pill.js.map +1 -1
  438. package/build/dist/UI/Components/StatusBubble/StatusBubble.js +3 -3
  439. package/build/dist/UI/Components/StatusBubble/StatusBubble.js.map +1 -1
  440. package/build/dist/UI/Components/Tabs/Tabs.js +3 -1
  441. package/build/dist/UI/Components/Tabs/Tabs.js.map +1 -1
  442. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +1 -1
  443. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  444. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +8 -2
  445. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  446. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js +36 -6
  447. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js.map +1 -1
  448. package/build/dist/UI/Components/Workflow/Component.js +43 -33
  449. package/build/dist/UI/Components/Workflow/Component.js.map +1 -1
  450. package/build/dist/UI/Components/Workflow/ComponentPortViewer.js +6 -6
  451. package/build/dist/UI/Components/Workflow/ComponentPortViewer.js.map +1 -1
  452. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js +8 -8
  453. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js.map +1 -1
  454. package/build/dist/UI/Components/Workflow/ComponentsModal.js +15 -9
  455. package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
  456. package/build/dist/UI/Components/Workflow/Workflow.js +13 -11
  457. package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
  458. package/build/dist/UI/Utils/Navigation.js +11 -1
  459. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  460. package/build/dist/UI/Utils/Theme.js +106 -0
  461. package/build/dist/UI/Utils/Theme.js.map +1 -0
  462. package/build/dist/Utils/Dashboard/Components/DashboardLogChartComponent.js +89 -0
  463. package/build/dist/Utils/Dashboard/Components/DashboardLogChartComponent.js.map +1 -0
  464. package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
  465. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  466. package/build/dist/Utils/Monitor/LatencyMatrixUtil.js +89 -0
  467. package/build/dist/Utils/Monitor/LatencyMatrixUtil.js.map +1 -0
  468. package/build/dist/Utils/Monitor/MonitorMetricType.js +138 -4
  469. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  470. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +112 -0
  471. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -0
  472. package/build/dist/Utils/Telemetry/EntityRelationship.js.map +1 -1
  473. package/package.json +1 -1
@@ -5,10 +5,8 @@ import {
5
5
  AIChatCitation,
6
6
  AIRunEventResultSummary,
7
7
  } from "../../../../Types/AI/AIChatTypes";
8
- import AIRunType from "../../../../Types/AI/AIRunType";
9
8
  import AIRunStatus from "../../../../Types/AI/AIRunStatus";
10
9
  import AIRunEventType from "../../../../Types/AI/AIRunEventType";
11
- import AIRun from "../../../../Models/DatabaseModels/AIRun";
12
10
  import AIRunEvent from "../../../../Models/DatabaseModels/AIRunEvent";
13
11
  import Project from "../../../../Models/DatabaseModels/Project";
14
12
  import LlmProvider from "../../../../Models/DatabaseModels/LlmProvider";
@@ -16,6 +14,7 @@ import AIRunService from "../../../Services/AIRunService";
16
14
  import AIRunEventService from "../../../Services/AIRunEventService";
17
15
  import ProjectService from "../../../Services/ProjectService";
18
16
  import LlmProviderService from "../../../Services/LlmProviderService";
17
+ import SentinelInvestigationQueue from "./InvestigationQueue";
19
18
  import ObservabilityAssistant, {
20
19
  ObservabilityAssistantResult,
21
20
  ObservabilityAssistantStep,
@@ -53,6 +52,14 @@ const MAX_OUTPUT_TOKENS: number = 2000;
53
52
  */
54
53
  const INCONCLUSIVE_RE: RegExp = /inconclusive[^.\n]*insufficient signal/i;
55
54
 
55
+ /*
56
+ * Failures a retry cannot fix within the run's usefulness window: missing/
57
+ * broken provider configuration and budget exhaustion (both messages minted
58
+ * by our own gating in AIService/LLMService, so they are stable to match).
59
+ */
60
+ const PERMANENT_FAILURE_RE: RegExp =
61
+ /no llm provider configured|llm provider type is not configured|token budget exhausted/i;
62
+
56
63
  // Maps a live agent step to the AIRunEvent type persisted for the glass-box trail.
57
64
  const STEP_EVENT_TYPE: Record<ObservabilityAssistantStepType, AIRunEventType> =
58
65
  {
@@ -67,7 +74,7 @@ const INVESTIGATION_PERSONA: string = `You are "Sentinel", OneUptime's autonomou
67
74
 
68
75
  Investigate like a senior on-call engineer:
69
76
  - Start from the affected monitors/services named in the signal.
70
- - Use your read tools to inspect the telemetry AROUND the signal's creation time: recent exceptions and their trends, error/latency metrics versus their normal range, failing traces, relevant logs, and recent changes / deploys.
77
+ - Use your read tools to inspect the telemetry AROUND the signal's creation time: recent exceptions and their trends, error/latency metrics versus their normal range (use baseline_anomaly to judge a metric against its learned hour-of-week baseline quantitatively instead of eyeballing), failing traces, relevant logs, and recent changes / deploys.
71
78
  - Form the single most likely root-cause hypothesis. If the evidence is inconclusive, say so plainly and list what you checked — do NOT guess a cause the data does not support.
72
79
  - If the context lists past resolved incidents, check whether this is a RECURRENCE. If the current signal matches one, say so explicitly, reference that incident number, and note how it was resolved before — but still verify against the current telemetry.
73
80
 
@@ -83,17 +90,10 @@ Keep it tight and skimmable. You are read-only: never claim to have changed anyt
83
90
  export type SentinelSubjectType = "Incident" | "Alert";
84
91
 
85
92
  export interface InvestigationRequest {
86
- projectId: ObjectID;
87
93
  // Label recorded on LlmLog, e.g. "Sentinel Incident Investigation".
88
94
  feature: string;
89
95
  // A compact markdown summary of the subject that seeds the investigation.
90
96
  contextSummary: string;
91
- /*
92
- * The subject that triggered this run — links the AIRun so the live panel can
93
- * find "the investigation for this incident/alert". Exactly one is set.
94
- */
95
- subjectIncidentId?: ObjectID | undefined;
96
- subjectAlertId?: ObjectID | undefined;
97
97
  /*
98
98
  * Called with the finished, branded, cited analysis so the caller can post it
99
99
  * to the subject's timeline. `isConfident` is false when Sentinel reported it
@@ -160,44 +160,38 @@ export default class SentinelInvestigationEngine {
160
160
  }
161
161
 
162
162
  /*
163
- * Run one investigation end-to-end. Never throws — failures are logged and
164
- * recorded on the AIRun.
163
+ * Execute an already-CLAIMED (Running) investigation run end-to-end.
164
+ * Called by SentinelInvestigationQueue after a successful CAS claim —
165
+ * cap/budget gating and run creation live in the queue now. Never throws;
166
+ * failures are handed to the queue's retry policy (failOrRequeue).
165
167
  */
166
168
  @CaptureSpan()
167
- public static async investigate(
168
- request: InvestigationRequest,
169
- ): Promise<void> {
170
- const { projectId } = request;
171
-
172
- // Record the run up front so it is auditable even if it fails.
173
- const run: AIRun = new AIRun();
174
- run.projectId = projectId;
175
- run.runType = AIRunType.Investigation;
176
- run.status = AIRunStatus.Running;
177
- run.startedAt = OneUptimeDate.getCurrentDate();
178
- run.lastHeartbeatAt = OneUptimeDate.getCurrentDate();
179
-
180
- if (request.subjectIncidentId) {
181
- run.triggeredByIncidentId = request.subjectIncidentId;
182
- }
183
- if (request.subjectAlertId) {
184
- run.triggeredByAlertId = request.subjectAlertId;
185
- }
169
+ public static async executeRun(data: {
170
+ aiRunId: ObjectID;
171
+ projectId: ObjectID;
172
+ attemptCount: number;
173
+ request: InvestigationRequest;
174
+ }): Promise<void> {
175
+ const { aiRunId, projectId, request } = data;
186
176
 
187
- let createdRun: AIRun;
177
+ /*
178
+ * Retried runs already have events from earlier attempts; continue the
179
+ * sequence so the glass-box trail stays ordered and shows every attempt.
180
+ */
181
+ let sequence: number = 0;
188
182
  try {
189
- createdRun = await AIRunService.create({
190
- data: run,
191
- props: { isRoot: true },
192
- });
183
+ sequence = (
184
+ await AIRunEventService.countBy({
185
+ query: { aiRunId },
186
+ props: { isRoot: true },
187
+ })
188
+ ).toNumber();
193
189
  } catch (error) {
194
- logger.error(`Sentinel: failed to create investigation run: ${error}`);
195
- return;
190
+ logger.error(
191
+ `Sentinel: failed to read event count for run ${aiRunId.toString()}; starting sequence at 0: ${error}`,
192
+ );
196
193
  }
197
194
 
198
- const aiRunId: ObjectID = createdRun.id!;
199
- let sequence: number = 0;
200
-
201
195
  await this.emitEvent({
202
196
  projectId,
203
197
  aiRunId,
@@ -234,10 +228,12 @@ export default class SentinelInvestigationEngine {
234
228
  citationId: step.citationId,
235
229
  });
236
230
 
237
- // Keep the run visibly alive for the stale-run sweeper + live UI.
238
- if (step.type === "llm_started") {
239
- await this.touchHeartbeat(aiRunId);
240
- }
231
+ /*
232
+ * Keep the run visibly alive for the stale-run sweeper + live UI on
233
+ * EVERY step — a slow self-hosted LLM call can approach the sweeper's
234
+ * timeout, so the heartbeat must be as frequent as we can make it.
235
+ */
236
+ await this.touchHeartbeat(aiRunId);
241
237
  };
242
238
 
243
239
  try {
@@ -256,18 +252,35 @@ export default class SentinelInvestigationEngine {
256
252
  onStep,
257
253
  });
258
254
 
259
- await AIRunService.updateOneBy({
260
- query: { _id: aiRunId.toString(), status: AIRunStatus.Running },
261
- data: {
262
- status: AIRunStatus.Completed,
263
- completedAt: OneUptimeDate.getCurrentDate(),
264
- lastHeartbeatAt: OneUptimeDate.getCurrentDate(),
265
- llmCallCount: result.llmCallCount,
266
- toolCallCount: result.toolCallCount,
267
- totalTokens: result.totalTokens,
268
- } as never,
269
- props: { isRoot: true },
270
- });
255
+ /*
256
+ * Atomic Running -> Completed. If we did NOT win this transition,
257
+ * another actor moved the run while we executed — most likely the
258
+ * stale sweeper falsely requeued it (slow LLM call outlasting the
259
+ * heartbeat window) and a second attempt is or will be running. In
260
+ * that case DO NOT post the analysis: the winning attempt will, and
261
+ * posting here would duplicate the RCA in the feed and workspace.
262
+ */
263
+ const completedCount: number = await AIRunService.attemptStatusTransition(
264
+ {
265
+ aiRunId,
266
+ fromStatus: AIRunStatus.Running,
267
+ set: {
268
+ status: AIRunStatus.Completed,
269
+ completedAt: OneUptimeDate.getCurrentDate(),
270
+ lastHeartbeatAt: OneUptimeDate.getCurrentDate(),
271
+ llmCallCount: result.llmCallCount,
272
+ toolCallCount: result.toolCallCount,
273
+ totalTokens: result.totalTokens,
274
+ },
275
+ },
276
+ );
277
+
278
+ if (completedCount === 0) {
279
+ logger.warn(
280
+ `Sentinel: run ${aiRunId.toString()} finished but was no longer Running (likely requeued as stale mid-flight); skipping postAnalysis to avoid a duplicate RCA.`,
281
+ );
282
+ return;
283
+ }
271
284
 
272
285
  await this.emitEvent({
273
286
  projectId,
@@ -300,16 +313,6 @@ export default class SentinelInvestigationEngine {
300
313
  const message: string =
301
314
  error instanceof Error ? error.message : String(error);
302
315
 
303
- await AIRunService.updateOneBy({
304
- query: { _id: aiRunId.toString(), status: AIRunStatus.Running },
305
- data: {
306
- status: AIRunStatus.Error,
307
- completedAt: OneUptimeDate.getCurrentDate(),
308
- errorMessage: message.substring(0, 480),
309
- } as never,
310
- props: { isRoot: true },
311
- });
312
-
313
316
  await this.emitEvent({
314
317
  projectId,
315
318
  aiRunId,
@@ -317,8 +320,25 @@ export default class SentinelInvestigationEngine {
317
320
  eventType: AIRunEventType.RunFailed,
318
321
  });
319
322
 
323
+ /*
324
+ * Hand the failure to the queue's retry policy: transient errors
325
+ * requeue while attempts remain; permanent ones finalize as Error
326
+ * since retrying cannot help. Classification is by message, NOT by
327
+ * exception type: LLMService wraps transient provider failures (429s,
328
+ * 5xx, timeouts) in BadDataException too, so type-based classification
329
+ * would wrongly make the exact failures retries exist for permanent.
330
+ * Only configuration/budget gating — which a retry cannot change
331
+ * within the run's usefulness window — counts as permanent.
332
+ */
333
+ await SentinelInvestigationQueue.failOrRequeue({
334
+ aiRunId,
335
+ attemptCount: data.attemptCount,
336
+ errorMessage: message,
337
+ isPermanent: PERMANENT_FAILURE_RE.test(message),
338
+ });
339
+
320
340
  logger.error(
321
- `Sentinel: investigation failed (run ${aiRunId.toString()}): ${message}`,
341
+ `Sentinel: investigation attempt ${data.attemptCount} failed (run ${aiRunId.toString()}): ${message}`,
322
342
  );
323
343
  }
324
344
  }
@@ -19,7 +19,7 @@ import { QueryMonitorsTool } from "./MonitorTools";
19
19
  import { TopExceptionsTool } from "./ExceptionTools";
20
20
  import { LogHistogramTool, SearchLogsTool } from "./LogTools";
21
21
  import { RecentChangesTool } from "./RecentChangesTools";
22
- import { QueryMetricsTool } from "./MetricTools";
22
+ import { BaselineAnomalyTool, QueryMetricsTool } from "./MetricTools";
23
23
  import { GetTraceTool, QueryTracesTool } from "./TraceTools";
24
24
  import { LookupContextTool } from "./ContextTools";
25
25
  import {
@@ -67,6 +67,7 @@ export default class AIToolbox {
67
67
  SearchLogsTool,
68
68
  LogHistogramTool,
69
69
  QueryMetricsTool,
70
+ BaselineAnomalyTool,
70
71
  QueryTracesTool,
71
72
  GetTraceTool,
72
73
  RecentChangesTool,
@@ -1,5 +1,7 @@
1
1
  import Metric from "../../../../Models/AnalyticsModels/Metric";
2
2
  import AggregationType from "../../../../Types/BaseDatabase/AggregationType";
3
+ import DatabaseRequestType from "../../../Types/BaseDatabase/DatabaseRequestType";
4
+ import ModelPermission from "../../../Types/AnalyticsDatabase/ModelPermission";
3
5
  import InBetween from "../../../../Types/BaseDatabase/InBetween";
4
6
  import { JSONObject } from "../../../../Types/JSON";
5
7
  import ObjectID from "../../../../Types/ObjectID";
@@ -11,6 +13,11 @@ import {
11
13
  } from "../../../../Types/AI/AIChatTypes";
12
14
  import AggregateBy from "../../../Types/AnalyticsDatabase/AggregateBy";
13
15
  import MetricService from "../../../Services/MetricService";
16
+ import MetricBaselineService, {
17
+ BandPoint,
18
+ BaselineSummary,
19
+ MetricBaselineService as MetricBaselineServiceClass,
20
+ } from "../../../Services/MetricBaselineService";
14
21
  import AggregatedResult from "../../../../Types/BaseDatabase/AggregatedResult";
15
22
  import AggregatedModel from "../../../../Types/BaseDatabase/AggregatedModel";
16
23
  import ToolResultSerializer, { SerializedResult } from "./Serializer";
@@ -177,3 +184,387 @@ export const QueryMetricsTool: ObservabilityTool = {
177
184
  };
178
185
  },
179
186
  };
187
+
188
+ // The recent window whose average is judged against the baseline band.
189
+ const BASELINE_CURRENT_WINDOW_MINUTES: number = 15;
190
+
191
+ // Lookback rendered on the expected-range band chart.
192
+ const BASELINE_CHART_HOURS: number = 6;
193
+
194
+ export const BaselineAnomalyTool: ObservabilityTool = {
195
+ name: "baseline_anomaly",
196
+ description:
197
+ "Judge whether a metric is currently anomalous versus its learned hour-of-week baseline (rolling-window mean ± sigma band, the same math the anomaly monitors use). Answers 'is this value NORMAL for this metric at this time of week?' quantitatively — use it instead of eyeballing query_metrics output. Requires the exact metric name — discover names via lookup_context.",
198
+ inputSchema: {
199
+ type: "object",
200
+ properties: {
201
+ metricName: {
202
+ type: "string",
203
+ description: "Exact metric name (required).",
204
+ },
205
+ entityId: {
206
+ type: "string",
207
+ description:
208
+ "Only data points from this service/host/monitor (its OneUptime ID). When omitted, the baseline spans every entity that ingests the metric.",
209
+ },
210
+ atTime: {
211
+ type: "string",
212
+ description:
213
+ "Evaluate at this ISO 8601 timestamp (e.g. the signal's creation time). Defaults to now.",
214
+ },
215
+ sensitivity: {
216
+ type: "string",
217
+ enum: ["Low", "Medium", "High"],
218
+ description:
219
+ "Band width: Low = 4 sigma (fewest anomalies), Medium = 3 sigma (default), High = 2 sigma.",
220
+ },
221
+ windowDays: {
222
+ type: "number",
223
+ enum: [...MetricBaselineServiceClass.WINDOW_DAYS_OPTIONS],
224
+ description: "Rolling baseline window in days (default 14).",
225
+ },
226
+ },
227
+ required: ["metricName"],
228
+ },
229
+ requiredPermissions: METRIC_READ_PERMISSIONS,
230
+ execute: async (
231
+ args: JSONObject,
232
+ ctx: ToolContext,
233
+ ): Promise<ToolExecutionResult> => {
234
+ const metricName: string | undefined = ToolArgs.getString(
235
+ args,
236
+ "metricName",
237
+ );
238
+
239
+ if (!metricName) {
240
+ throw new BadDataException(
241
+ "metricName is required. Use lookup_context with type 'metricNames' to discover metric names.",
242
+ );
243
+ }
244
+
245
+ const entityId: ObjectID | undefined = ToolArgs.getObjectID(
246
+ args,
247
+ "entityId",
248
+ );
249
+
250
+ let atTime: Date = OneUptimeDate.getCurrentDate();
251
+ const atTimeString: string | undefined = ToolArgs.getString(args, "atTime");
252
+ if (atTimeString) {
253
+ const parsed: Date = new Date(atTimeString);
254
+ if (isNaN(parsed.getTime())) {
255
+ throw new BadDataException(
256
+ `atTime "${atTimeString}" is not a valid ISO 8601 timestamp (e.g. 2024-01-31T14:00:00Z).`,
257
+ );
258
+ }
259
+ atTime = parsed;
260
+ }
261
+
262
+ const sensitivityString: string =
263
+ ToolArgs.getString(args, "sensitivity") || "Medium";
264
+ if (!["Low", "Medium", "High"].includes(sensitivityString)) {
265
+ throw new BadDataException(
266
+ `Invalid sensitivity: ${sensitivityString}. Must be Low, Medium or High.`,
267
+ );
268
+ }
269
+ const sensitivity: "Low" | "Medium" | "High" = sensitivityString as
270
+ | "Low"
271
+ | "Medium"
272
+ | "High";
273
+
274
+ const requestedWindowDays: number = ToolArgs.getNumber(args, "windowDays", {
275
+ defaultValue: MetricBaselineServiceClass.DEFAULT_WINDOW_DAYS,
276
+ min: MetricBaselineServiceClass.DEFAULT_WINDOW_DAYS,
277
+ max: MetricBaselineServiceClass.MAX_WINDOW_DAYS,
278
+ });
279
+ // Snap to the supported window options (they match the table's TTL).
280
+ const windowDays: number =
281
+ [...MetricBaselineServiceClass.WINDOW_DAYS_OPTIONS]
282
+ .reverse()
283
+ .find((option: number) => {
284
+ return option <= requestedWindowDays;
285
+ }) || MetricBaselineServiceClass.DEFAULT_WINDOW_DAYS;
286
+
287
+ /*
288
+ * The baseline queries are raw ClickHouse SQL scoped only by projectId —
289
+ * they skip the model layer's owned-scope filter. A label/owned-restricted
290
+ * user must therefore be pinned to one of THEIR services: project-wide
291
+ * baselines (no entityId) and other services' baselines are refused, the
292
+ * same posture as the other raw-SQL tools (see LogTools/TraceTools).
293
+ */
294
+ const accessibleServiceIds: Array<ObjectID> | null =
295
+ await ModelPermission.getAccessibleServiceIdsForAnalyticsModel(
296
+ Metric,
297
+ ctx.props,
298
+ DatabaseRequestType.Read,
299
+ );
300
+
301
+ if (accessibleServiceIds !== null) {
302
+ const isEntityAccessible: boolean = Boolean(
303
+ entityId &&
304
+ accessibleServiceIds.some((id: ObjectID) => {
305
+ return id.toString() === entityId.toString();
306
+ }),
307
+ );
308
+
309
+ if (!isEntityAccessible) {
310
+ throw new BadDataException(
311
+ "Your telemetry access is scoped to specific services, so a project-wide baseline is not available. Pass entityId set to the OneUptime ID of a service you can read (discover them via lookup_context).",
312
+ );
313
+ }
314
+ }
315
+
316
+ const hourOfWeek: number =
317
+ MetricBaselineServiceClass.computeHourOfWeek(atTime);
318
+
319
+ const baseline: BaselineSummary | null =
320
+ await MetricBaselineService.getBaseline({
321
+ projectId: ctx.projectId,
322
+ metricName,
323
+ primaryEntityId: entityId,
324
+ hourOfWeek,
325
+ windowDays,
326
+ });
327
+
328
+ const citationLabel: string = `baseline_anomaly(${metricName}${
329
+ entityId ? ` · ${entityId.toString()}` : ""
330
+ }) @ ${atTime.toISOString()}`;
331
+
332
+ /*
333
+ * Cold start: no baseline (or too few samples) for this hour-of-week cell.
334
+ * Report it explicitly — a missing baseline must read as "cannot judge",
335
+ * never as "not anomalous".
336
+ */
337
+ if (!baseline || !baseline.isReliable) {
338
+ const coverage: { totalSamples: number; oldestDay: Date | null } =
339
+ await MetricBaselineService.getCoverage({
340
+ projectId: ctx.projectId,
341
+ metricName,
342
+ primaryEntityId: entityId,
343
+ });
344
+
345
+ const verdictRow: JSONObject = {
346
+ verdict: "insufficient baseline data",
347
+ detail: `The metric has ${
348
+ baseline
349
+ ? `only ${baseline.sampleCount} sample(s)`
350
+ : "no baseline data"
351
+ } for this hour of week (needs at least ${MetricBaselineServiceClass.DEFAULT_MIN_SAMPLES}). Baseline coverage overall: ${coverage.totalSamples} total sample(s)${
352
+ coverage.oldestDay
353
+ ? `, oldest day ${OneUptimeDate.getDateAsFormattedString(coverage.oldestDay)}`
354
+ : ""
355
+ }. Do NOT treat this as "not anomalous" — the baseline simply cannot judge yet.`,
356
+ metricName,
357
+ entityId: entityId?.toString(),
358
+ evaluatedAt: atTime.toISOString(),
359
+ hourOfWeek,
360
+ windowDays,
361
+ };
362
+
363
+ const serializedColdStart: SerializedResult =
364
+ ToolResultSerializer.serializeRows([verdictRow]);
365
+
366
+ return {
367
+ dataForLlm: serializedColdStart.text,
368
+ rowCount: serializedColdStart.rowCount,
369
+ citationLabel,
370
+ citationTarget: { type: AIChatCitationTargetType.Metrics },
371
+ redactionCount: serializedColdStart.redactionCount,
372
+ isTruncated: serializedColdStart.isTruncated,
373
+ };
374
+ }
375
+
376
+ // Current value: average over the recent window ending at atTime.
377
+ const currentWindowStart: Date = OneUptimeDate.addRemoveMinutes(
378
+ atTime,
379
+ -1 * BASELINE_CURRENT_WINDOW_MINUTES,
380
+ );
381
+
382
+ const currentQuery: JSONObject = {
383
+ name: metricName,
384
+ time: new InBetween(currentWindowStart, atTime),
385
+ };
386
+ if (entityId) {
387
+ currentQuery["primaryEntityId"] = entityId;
388
+ }
389
+
390
+ const currentResult: AggregatedResult = await MetricService.aggregateBy({
391
+ aggregateColumnName: "value",
392
+ aggregationType: AggregationType.Avg,
393
+ aggregationTimestampColumnName: "time",
394
+ startTimestamp: currentWindowStart,
395
+ endTimestamp: atTime,
396
+ query: currentQuery as never,
397
+ limit: 200,
398
+ skip: 0,
399
+ props: ctx.props,
400
+ } as AggregateBy<Metric>);
401
+
402
+ const currentValues: Array<number> = currentResult.data
403
+ .map((item: AggregatedModel) => {
404
+ return item.value;
405
+ })
406
+ .filter((value: unknown): value is number => {
407
+ return typeof value === "number" && Number.isFinite(value);
408
+ });
409
+
410
+ const sigma: number =
411
+ MetricBaselineServiceClass.sigmaForSensitivity(sensitivity);
412
+ const expectedLow: number = baseline.mean - sigma * baseline.stddev;
413
+ const expectedHigh: number = baseline.mean + sigma * baseline.stddev;
414
+
415
+ let verdict: string;
416
+ let currentValue: number | null = null;
417
+ let zScore: number | null = null;
418
+
419
+ if (currentValues.length === 0) {
420
+ verdict = `no data points in the last ${BASELINE_CURRENT_WINDOW_MINUTES} minutes — the metric may have STOPPED reporting, which can itself be the anomaly`;
421
+ } else {
422
+ currentValue =
423
+ currentValues.reduce((sum: number, value: number) => {
424
+ return sum + value;
425
+ }, 0) / currentValues.length;
426
+
427
+ if (baseline.stddev <= 0) {
428
+ /*
429
+ * Zero-variance baseline: the band collapses to a point and
430
+ * floating-point noise alone would flag every sample. The anomaly
431
+ * monitors refuse to classify here (MetricMonitorCriteria) — match
432
+ * them rather than emitting guaranteed-false ANOMALOUS verdicts.
433
+ */
434
+ verdict = `cannot judge — the baseline has zero variance (the metric has been constant at ${baseline.mean}), so any band would flag every sample; compare the current value to the baseline mean directly instead`;
435
+ } else {
436
+ zScore = (currentValue - baseline.mean) / baseline.stddev;
437
+
438
+ if (currentValue > expectedHigh) {
439
+ verdict = `ANOMALOUS — above the expected range for this hour of week`;
440
+ } else if (currentValue < expectedLow) {
441
+ verdict = `ANOMALOUS — below the expected range for this hour of week`;
442
+ } else {
443
+ verdict = `normal — within the expected range for this hour of week`;
444
+ }
445
+ }
446
+ }
447
+
448
+ const row: JSONObject = {
449
+ verdict,
450
+ metricName,
451
+ entityId: entityId?.toString(),
452
+ evaluatedAt: atTime.toISOString(),
453
+ currentValue:
454
+ currentValue !== null
455
+ ? `${currentValue} (avg over the last ${BASELINE_CURRENT_WINDOW_MINUTES} min)`
456
+ : null,
457
+ expectedRange: `${expectedLow} to ${expectedHigh} (mean ${baseline.mean} ± ${sigma}σ, σ=${baseline.stddev})`,
458
+ zScore,
459
+ baselineMedian: baseline.median,
460
+ baselineP95: baseline.p95,
461
+ baselineSampleCount: baseline.sampleCount,
462
+ baselineWindowDays: baseline.windowDays,
463
+ hourOfWeek,
464
+ sensitivity,
465
+ };
466
+
467
+ const serialized: SerializedResult = ToolResultSerializer.serializeRows([
468
+ row,
469
+ ]);
470
+
471
+ // Expected-range band + actual values over the recent past, for the panel.
472
+ const chartStart: Date = OneUptimeDate.addRemoveHours(
473
+ atTime,
474
+ -1 * BASELINE_CHART_HOURS,
475
+ );
476
+
477
+ let bandSeries: Array<BandPoint> = [];
478
+ let actualPoints: Array<AIChatWidgetPoint> = [];
479
+ try {
480
+ bandSeries = await MetricBaselineService.getBandSeries({
481
+ projectId: ctx.projectId,
482
+ metricName,
483
+ primaryEntityId: entityId,
484
+ startTime: chartStart,
485
+ endTime: atTime,
486
+ intervalMinutes: 15,
487
+ sigmaCount: sigma,
488
+ windowDays,
489
+ });
490
+
491
+ const chartQuery: JSONObject = {
492
+ name: metricName,
493
+ time: new InBetween(chartStart, atTime),
494
+ };
495
+ if (entityId) {
496
+ chartQuery["primaryEntityId"] = entityId;
497
+ }
498
+
499
+ const chartActualResult: AggregatedResult =
500
+ await MetricService.aggregateBy({
501
+ aggregateColumnName: "value",
502
+ aggregationType: AggregationType.Avg,
503
+ aggregationTimestampColumnName: "time",
504
+ startTimestamp: chartStart,
505
+ endTimestamp: atTime,
506
+ query: chartQuery as never,
507
+ limit: 200,
508
+ skip: 0,
509
+ props: ctx.props,
510
+ } as AggregateBy<Metric>);
511
+
512
+ actualPoints = chartActualResult.data.map((item: AggregatedModel) => {
513
+ return {
514
+ x: OneUptimeDate.toString(OneUptimeDate.fromString(item.timestamp)),
515
+ y: typeof item.value === "number" ? item.value : null,
516
+ };
517
+ });
518
+ } catch {
519
+ // The band chart is decorative — never fail the verdict for it.
520
+ }
521
+
522
+ const toPoints: (
523
+ pick: (point: BandPoint) => number,
524
+ ) => Array<AIChatWidgetPoint> = (
525
+ pick: (point: BandPoint) => number,
526
+ ): Array<AIChatWidgetPoint> => {
527
+ return bandSeries.map((point: BandPoint) => {
528
+ return {
529
+ x: OneUptimeDate.toString(point.time),
530
+ y: pick(point),
531
+ };
532
+ });
533
+ };
534
+
535
+ return {
536
+ dataForLlm: serialized.text,
537
+ rowCount: serialized.rowCount,
538
+ citationLabel,
539
+ citationTarget: { type: AIChatCitationTargetType.Metrics },
540
+ redactionCount: serialized.redactionCount,
541
+ isTruncated: serialized.isTruncated,
542
+ widget:
543
+ bandSeries.length > 0
544
+ ? WidgetBuilder.timeSeries({
545
+ title: `${metricName} vs expected range`,
546
+ description: `Hour-of-week baseline (±${sigma}σ, ${windowDays}-day window), ${chartStart.toISOString()} – ${atTime.toISOString()}`,
547
+ series: [
548
+ {
549
+ name: "expected high",
550
+ points: toPoints((point: BandPoint) => {
551
+ return point.expectedHigh;
552
+ }),
553
+ },
554
+ {
555
+ name: "expected low",
556
+ points: toPoints((point: BandPoint) => {
557
+ return point.expectedLow;
558
+ }),
559
+ },
560
+ ...(actualPoints.length > 0
561
+ ? [{ name: "actual", points: actualPoints }]
562
+ : []),
563
+ ],
564
+ valueLabel: metricName,
565
+ link: { type: AIChatCitationTargetType.Metrics },
566
+ })
567
+ : undefined,
568
+ };
569
+ },
570
+ };