@oneuptime/common 12.0.4 → 12.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 (374) hide show
  1. package/Models/DatabaseModels/AIRun.ts +38 -1
  2. package/Models/DatabaseModels/AlertFeed.ts +30 -0
  3. package/Models/DatabaseModels/Dashboard.ts +28 -0
  4. package/Models/DatabaseModels/Incident.ts +2 -0
  5. package/Models/DatabaseModels/IncidentEpisode.ts +1 -0
  6. package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +1 -0
  7. package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
  8. package/Models/DatabaseModels/IncidentFeed.ts +30 -0
  9. package/Models/DatabaseModels/IncidentPublicNote.ts +1 -0
  10. package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
  11. package/Models/DatabaseModels/LogSavedView.ts +33 -0
  12. package/Models/DatabaseModels/Project.ts +231 -9
  13. package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -0
  14. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +1 -0
  15. package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
  16. package/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser.ts +4 -4
  17. package/Models/DatabaseModels/StatusPage.ts +28 -0
  18. package/Models/DatabaseModels/StatusPageAnnouncement.ts +1 -0
  19. package/Server/API/AIAgentDataAPI.ts +44 -55
  20. package/Server/API/AIInvestigationAPI.ts +175 -17
  21. package/Server/API/DashboardAPI.ts +10 -10
  22. package/Server/API/StatusPageAPI.ts +38 -19
  23. package/Server/EnvironmentConfig.ts +16 -0
  24. package/Server/Infrastructure/ClickhouseConfig.ts +15 -1
  25. package/Server/Infrastructure/ClickhouseDatabase.ts +1 -1
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.ts +27 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.ts +89 -0
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.ts +27 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.ts +40 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.ts +26 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
  32. package/Server/Services/AIRunService.ts +78 -10
  33. package/Server/Services/AIService.ts +94 -22
  34. package/Server/Services/AlertFeedService.ts +5 -0
  35. package/Server/Services/AnalyticsDatabaseService.ts +29 -0
  36. package/Server/Services/DatabaseService.ts +43 -2
  37. package/Server/Services/IncidentFeedService.ts +5 -0
  38. package/Server/Services/LlmLogService.ts +46 -2
  39. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +6 -0
  40. package/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.ts +4 -0
  41. package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +8 -5
  42. package/Server/Utils/AI/CodeFix/FixRunBudget.ts +90 -16
  43. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +2 -0
  44. package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +2 -0
  45. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +223 -22
  46. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +25 -24
  47. package/Server/Utils/AI/SRE/ConfidenceSignal.ts +182 -71
  48. package/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.ts +252 -43
  49. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +25 -24
  50. package/Server/Utils/AI/SRE/InstrumentationTaskTrigger.ts +47 -13
  51. package/Server/Utils/AI/SRE/InvestigationGrader.ts +9 -5
  52. package/Server/Utils/AI/SRE/InvestigationQueue.ts +141 -24
  53. package/Server/Utils/AI/SRE/InvestigationSubjectLock.ts +66 -0
  54. package/Server/Utils/AI/SRE/PostedRootCause.ts +94 -3
  55. package/Server/Utils/AI/SRE/README.md +1 -1
  56. package/Server/Utils/AI/SRE/SubjectCodeFixRun.ts +132 -10
  57. package/Server/Utils/AnalyticsDatabase/ClusterConfig.ts +24 -0
  58. package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +29 -0
  59. package/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.ts +56 -0
  60. package/Server/Utils/Monitor/MonitorMetricUtil.ts +46 -0
  61. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +1 -0
  62. package/Server/Utils/PasswordHash.ts +28 -4
  63. package/Tests/App/Dashboard/AIInvestigationHeaderStatus.test.tsx +251 -0
  64. package/Tests/App/Dashboard/EventStatusPanel.test.tsx +747 -0
  65. package/Tests/App/Dashboard/InvestigationFeedRefresh.test.tsx +480 -0
  66. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +1184 -0
  67. package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +500 -0
  68. package/Tests/App/Dashboard/PortMonitorCriteriaFilter.test.ts +93 -0
  69. package/Tests/App/StatusPage/PublicStatusPageAPIErrorHandling.test.ts +117 -0
  70. package/Tests/App/StatusPage/StatusPageModelAPIPolymorphism.test.ts +223 -0
  71. package/Tests/Models/DatabaseModels/PermissionCatalogueCoverage.test.ts +243 -0
  72. package/Tests/Server/API/AIAgentDataFixFromIncidentContext.test.ts +266 -0
  73. package/Tests/Server/API/AIInvestigationAPI.test.ts +594 -0
  74. package/Tests/Server/API/AIInvestigationCreateFixTask.test.ts +80 -0
  75. package/Tests/Server/API/DashboardMasterPasswordAPI.test.ts +172 -1
  76. package/Tests/Server/API/StatusPageMasterPasswordAPI.test.ts +335 -0
  77. package/Tests/Server/Services/AIRunCodeFixClaim.test.ts +59 -5
  78. package/Tests/Server/Services/AIRunHumanVerdict.test.ts +127 -58
  79. package/Tests/Server/Services/AIServiceDailyBudget.test.ts +396 -20
  80. package/Tests/Server/Services/AddMasterPasswordSaltMigration.test.ts +283 -0
  81. package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +25 -1
  82. package/Tests/Server/Services/DatabaseServicePerUserPasswordSalt.test.ts +45 -35
  83. package/Tests/Server/Services/DatabaseServiceUpdateColumnsWithoutHooks.test.ts +54 -0
  84. package/Tests/Server/Services/FeedAIRunAssociation.test.ts +125 -0
  85. package/Tests/Server/Services/FixFromIncidentTaskTrigger.test.ts +418 -57
  86. package/Tests/Server/Services/InstrumentationTaskTrigger.test.ts +173 -14
  87. package/Tests/Server/Services/MasterPasswordScrypt.test.ts +567 -0
  88. package/Tests/Server/Services/SeparateIncidentAlertAiSettingsMigration.test.ts +194 -0
  89. package/Tests/Server/Utils/AI/AIAlertGating.test.ts +37 -4
  90. package/Tests/Server/Utils/AI/AIConfidenceSignal.test.ts +330 -29
  91. package/Tests/Server/Utils/AI/AIIncidentGating.test.ts +25 -0
  92. package/Tests/Server/Utils/AI/AIInvestigationQueue.test.ts +293 -5
  93. package/Tests/Server/Utils/AI/CodeFixAgentCompletion.test.ts +59 -1
  94. package/Tests/Server/Utils/AI/FixFromIncidentAutoTrigger.test.ts +342 -19
  95. package/Tests/Server/Utils/AI/FixRunBudget.test.ts +214 -30
  96. package/Tests/Server/Utils/AI/Insights/InvestigationQueueInsightSubject.test.ts +2 -0
  97. package/Tests/Server/Utils/AI/InvestigationGrader.test.ts +65 -17
  98. package/Tests/Server/Utils/AI/InvestigationQueueRemediationExecution.test.ts +3 -0
  99. package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +313 -20
  100. package/Tests/Server/Utils/AI/PostedRootCause.test.ts +254 -0
  101. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +4 -0
  102. package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +2 -0
  103. package/Tests/Server/Utils/AI/SubjectCodeFixRunDedupe.test.ts +44 -0
  104. package/Tests/Server/Utils/AnalyticsDatabase/ClusterAwareSchema.test.ts +30 -0
  105. package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaPortTimings.test.ts +212 -0
  106. package/Tests/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.test.ts +401 -0
  107. package/Tests/Server/Utils/Monitor/MonitorMetricUtilPortTimings.test.ts +243 -0
  108. package/Tests/Server/Utils/PasswordHash.test.ts +30 -0
  109. package/Tests/Types/Dashboard/DashboardTemplates.test.ts +111 -0
  110. package/Tests/Types/Monitor/CephMetricCatalog.test.ts +104 -0
  111. package/Tests/Types/Monitor/CriteriaFilter.test.ts +4 -0
  112. package/Tests/Types/Monitor/DockerMetricCatalog.test.ts +109 -0
  113. package/Tests/Types/Monitor/DockerSwarmMetricCatalog.test.ts +118 -0
  114. package/Tests/Types/Monitor/HostMetricCatalog.test.ts +104 -0
  115. package/Tests/Types/Monitor/IotMetricCatalog.test.ts +104 -0
  116. package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +118 -0
  117. package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +42 -0
  118. package/Tests/Types/Monitor/MonitorStep.test.ts +38 -0
  119. package/Tests/Types/Monitor/MonitorStepDomainMonitor.test.ts +75 -0
  120. package/Tests/Types/Monitor/PodmanMetricCatalog.test.ts +109 -0
  121. package/Tests/Types/Monitor/ProxmoxMetricCatalog.test.ts +107 -0
  122. package/Tests/Types/Monitor/SnmpMonitor/SnmpVendorTemplate.test.ts +151 -0
  123. package/Tests/Types/Permission.test.ts +137 -0
  124. package/Tests/Types/Rum/SessionReplayMaskingMode.test.ts +101 -0
  125. package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +5 -5
  126. package/Tests/UI/Components/CardModelDetailEdit.test.tsx +3 -5
  127. package/Tests/UI/Components/ErrorBoundary.test.tsx +158 -0
  128. package/Tests/UI/Components/FeedItemSafeMode.test.tsx +74 -0
  129. package/Tests/UI/Components/MoreMenu.test.tsx +756 -0
  130. package/Tests/UI/Components/StatusPage/ResourceGroupSection.test.tsx +62 -0
  131. package/Tests/UI/Monitor/PortMonitorView.test.tsx +237 -0
  132. package/Tests/UI/Telemetry/TelemetrySnapshotWindowAlert.test.tsx +91 -0
  133. package/Tests/UI/Utils/DownloadFile.test.ts +103 -0
  134. package/Tests/UI/Utils/ErrorSupportBundle.test.ts +582 -0
  135. package/Tests/UI/Utils/Project.test.ts +31 -0
  136. package/Tests/UI/Utils/StatusPageModelAPIInjection.test.ts +245 -0
  137. package/Tests/UI/Utils/UseDashboardGridDnd.test.tsx +820 -0
  138. package/Tests/Utils/Dashboard/DashboardViewConfig.test.ts +329 -0
  139. package/Tests/Utils/Dashboard/GridLayout.test.ts +951 -0
  140. package/Tests/Utils/Monitor/MonitorMetricType.test.ts +117 -0
  141. package/Tests/Utils/RecordingRuleExpression.test.ts +327 -0
  142. package/Tests/Utils/StatusPage/GroupNestingLayout.test.ts +186 -0
  143. package/Tests/Utils/StatusPage/GroupTree.test.ts +456 -0
  144. package/Tests/Utils/StatusPage/OverviewGroupHierarchyVisibility.test.ts +588 -0
  145. package/Tests/Utils/Telemetry/SavedViewTimeRange.test.ts +244 -0
  146. package/Tests/Utils/Telemetry/TelemetryQueryTimeRange.test.ts +825 -0
  147. package/Types/AI/AIRunCodeFixRecommendation.ts +17 -0
  148. package/Types/AI/CodeFixTaskContext.ts +63 -3
  149. package/Types/AI/CodeFixTaskType.ts +3 -3
  150. package/Types/HashedString.ts +4 -3
  151. package/Types/Monitor/CriteriaFilter.ts +4 -0
  152. package/Types/Monitor/DomainMonitor/DomainLookupMethod.ts +23 -0
  153. package/Types/Monitor/DomainMonitor/DomainMonitorResponse.ts +7 -0
  154. package/Types/Monitor/MonitorCriteriaInstance.ts +24 -7
  155. package/Types/Monitor/MonitorMetricType.ts +8 -0
  156. package/Types/Monitor/MonitorStep.ts +10 -1
  157. package/Types/Monitor/MonitorStepDomainMonitor.ts +21 -0
  158. package/Types/Monitor/PortMonitor/PortMonitorTimings.ts +10 -0
  159. package/Types/Permission.ts +23 -175
  160. package/Types/Probe/ProbeMonitorResponse.ts +6 -0
  161. package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +2 -3
  162. package/UI/Components/ErrorBoundary.tsx +138 -11
  163. package/UI/Components/Feed/FeedItem.tsx +9 -2
  164. package/UI/Components/ModelDetail/CardModelDetail.tsx +0 -12
  165. package/UI/Components/ModelTable/BaseModelTable.tsx +1 -1
  166. package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +12 -2
  167. package/UI/Components/MoreMenu/MoreMenu.tsx +285 -97
  168. package/UI/Components/MoreMenu/MoreMenuItem.tsx +8 -5
  169. package/UI/Utils/DownloadFile.ts +16 -8
  170. package/UI/Utils/ErrorSupportBundle.ts +805 -0
  171. package/UI/Utils/ModelAPI/ModelAPI.ts +19 -8
  172. package/UI/Utils/Project.ts +4 -0
  173. package/UI/Utils/StatusPage.ts +8 -2
  174. package/UI/Utils/UseDashboardGridDnd.ts +917 -0
  175. package/Utils/Dashboard/DashboardViewConfig.ts +80 -36
  176. package/Utils/Dashboard/GridLayout.ts +523 -0
  177. package/Utils/Metrics/RecordingRuleExpression.ts +19 -17
  178. package/Utils/Monitor/MonitorMetricType.ts +44 -2
  179. package/Utils/StatusPage/GroupNestingLayout.ts +69 -0
  180. package/Utils/StatusPage/GroupTree.ts +157 -66
  181. package/Utils/StatusPage/ResourceUptime.ts +37 -2
  182. package/Utils/Telemetry/SavedViewTimeRange.ts +114 -0
  183. package/Utils/Telemetry/TelemetryQueryTimeRange.ts +334 -0
  184. package/build/dist/Models/DatabaseModels/AIRun.js +39 -1
  185. package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
  186. package/build/dist/Models/DatabaseModels/AlertFeed.js +32 -0
  187. package/build/dist/Models/DatabaseModels/AlertFeed.js.map +1 -1
  188. package/build/dist/Models/DatabaseModels/Dashboard.js +29 -0
  189. package/build/dist/Models/DatabaseModels/Dashboard.js.map +1 -1
  190. package/build/dist/Models/DatabaseModels/Incident.js +2 -0
  191. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  192. package/build/dist/Models/DatabaseModels/IncidentEpisode.js +1 -0
  193. package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
  194. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +1 -0
  195. package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
  196. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
  197. package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
  198. package/build/dist/Models/DatabaseModels/IncidentFeed.js +32 -0
  199. package/build/dist/Models/DatabaseModels/IncidentFeed.js.map +1 -1
  200. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +1 -0
  201. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  202. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
  203. package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
  204. package/build/dist/Models/DatabaseModels/LogSavedView.js +33 -0
  205. package/build/dist/Models/DatabaseModels/LogSavedView.js.map +1 -1
  206. package/build/dist/Models/DatabaseModels/Project.js +241 -11
  207. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  208. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -0
  209. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  210. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +1 -0
  211. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  212. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
  213. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
  214. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplateOwnerUser.js +4 -4
  215. package/build/dist/Models/DatabaseModels/StatusPage.js +29 -0
  216. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  217. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +1 -0
  218. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
  219. package/build/dist/Server/API/AIAgentDataAPI.js +28 -46
  220. package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
  221. package/build/dist/Server/API/AIInvestigationAPI.js +111 -11
  222. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  223. package/build/dist/Server/API/DashboardAPI.js +8 -5
  224. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  225. package/build/dist/Server/API/StatusPageAPI.js +29 -11
  226. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  227. package/build/dist/Server/EnvironmentConfig.js +14 -0
  228. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  229. package/build/dist/Server/Infrastructure/ClickhouseConfig.js +12 -1
  230. package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
  231. package/build/dist/Server/Infrastructure/ClickhouseDatabase.js +1 -1
  232. package/build/dist/Server/Infrastructure/ClickhouseDatabase.js.map +1 -1
  233. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.js +22 -0
  234. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786096660558-AddTimeRangeToLogSavedView.js.map +1 -0
  235. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.js +36 -0
  236. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786101798351-MigrationName.js.map +1 -0
  237. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.js +18 -0
  238. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786105470826-MigrationName.js.map +1 -0
  239. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.js +19 -0
  240. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786400000000-AddMasterPasswordSalt.js.map +1 -0
  241. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.js +19 -0
  242. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786500000000-AddInvestigationCodeFixRecommendation.js.map +1 -0
  243. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
  244. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  245. package/build/dist/Server/Services/AIRunService.js +58 -12
  246. package/build/dist/Server/Services/AIRunService.js.map +1 -1
  247. package/build/dist/Server/Services/AIService.js +74 -21
  248. package/build/dist/Server/Services/AIService.js.map +1 -1
  249. package/build/dist/Server/Services/AlertFeedService.js +3 -0
  250. package/build/dist/Server/Services/AlertFeedService.js.map +1 -1
  251. package/build/dist/Server/Services/AnalyticsDatabaseService.js +29 -1
  252. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  253. package/build/dist/Server/Services/DatabaseService.js +21 -2
  254. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  255. package/build/dist/Server/Services/IncidentFeedService.js +3 -0
  256. package/build/dist/Server/Services/IncidentFeedService.js.map +1 -1
  257. package/build/dist/Server/Services/LlmLogService.js +22 -1
  258. package/build/dist/Server/Services/LlmLogService.js.map +1 -1
  259. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +3 -0
  260. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  261. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js +4 -0
  262. package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js.map +1 -1
  263. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +8 -5
  264. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -1
  265. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +63 -24
  266. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
  267. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +2 -0
  268. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  269. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +2 -0
  270. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -1
  271. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +156 -28
  272. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  273. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +21 -19
  274. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  275. package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js +128 -49
  276. package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js.map +1 -1
  277. package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js +170 -31
  278. package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js.map +1 -1
  279. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +21 -19
  280. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  281. package/build/dist/Server/Utils/AI/SRE/InstrumentationTaskTrigger.js +33 -9
  282. package/build/dist/Server/Utils/AI/SRE/InstrumentationTaskTrigger.js.map +1 -1
  283. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +9 -5
  284. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js.map +1 -1
  285. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js +121 -41
  286. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -1
  287. package/build/dist/Server/Utils/AI/SRE/InvestigationSubjectLock.js +46 -0
  288. package/build/dist/Server/Utils/AI/SRE/InvestigationSubjectLock.js.map +1 -0
  289. package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js +61 -14
  290. package/build/dist/Server/Utils/AI/SRE/PostedRootCause.js.map +1 -1
  291. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js +79 -1
  292. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js.map +1 -1
  293. package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js +21 -0
  294. package/build/dist/Server/Utils/AnalyticsDatabase/ClusterConfig.js.map +1 -1
  295. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +20 -3
  296. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
  297. package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js +43 -0
  298. package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js.map +1 -1
  299. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +33 -0
  300. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  301. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +1 -0
  302. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  303. package/build/dist/Server/Utils/PasswordHash.js +25 -4
  304. package/build/dist/Server/Utils/PasswordHash.js.map +1 -1
  305. package/build/dist/Types/AI/AIRunCodeFixRecommendation.js +18 -0
  306. package/build/dist/Types/AI/AIRunCodeFixRecommendation.js.map +1 -0
  307. package/build/dist/Types/AI/CodeFixTaskContext.js +23 -3
  308. package/build/dist/Types/AI/CodeFixTaskContext.js.map +1 -1
  309. package/build/dist/Types/AI/CodeFixTaskType.js +3 -3
  310. package/build/dist/Types/HashedString.js +4 -3
  311. package/build/dist/Types/HashedString.js.map +1 -1
  312. package/build/dist/Types/Monitor/CriteriaFilter.js +4 -0
  313. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  314. package/build/dist/Types/Monitor/DomainMonitor/DomainLookupMethod.js +24 -0
  315. package/build/dist/Types/Monitor/DomainMonitor/DomainLookupMethod.js.map +1 -0
  316. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +24 -7
  317. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  318. package/build/dist/Types/Monitor/MonitorMetricType.js +7 -0
  319. package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
  320. package/build/dist/Types/Monitor/MonitorStep.js +6 -1
  321. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  322. package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js +15 -0
  323. package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js.map +1 -1
  324. package/build/dist/Types/Monitor/PortMonitor/PortMonitorTimings.js +2 -0
  325. package/build/dist/Types/Monitor/PortMonitor/PortMonitorTimings.js.map +1 -0
  326. package/build/dist/Types/Permission.js +23 -155
  327. package/build/dist/Types/Permission.js.map +1 -1
  328. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +2 -5
  329. package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
  330. package/build/dist/UI/Components/ErrorBoundary.js +67 -8
  331. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  332. package/build/dist/UI/Components/Feed/FeedItem.js +2 -2
  333. package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
  334. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +0 -12
  335. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  336. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +1 -1
  337. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  338. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +12 -2
  339. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
  340. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +193 -65
  341. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  342. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +1 -1
  343. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
  344. package/build/dist/UI/Utils/DownloadFile.js +15 -8
  345. package/build/dist/UI/Utils/DownloadFile.js.map +1 -1
  346. package/build/dist/UI/Utils/ErrorSupportBundle.js +476 -0
  347. package/build/dist/UI/Utils/ErrorSupportBundle.js.map +1 -0
  348. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +18 -8
  349. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  350. package/build/dist/UI/Utils/Project.js +3 -0
  351. package/build/dist/UI/Utils/Project.js.map +1 -1
  352. package/build/dist/UI/Utils/StatusPage.js +4 -4
  353. package/build/dist/UI/Utils/StatusPage.js.map +1 -1
  354. package/build/dist/UI/Utils/UseDashboardGridDnd.js +551 -0
  355. package/build/dist/UI/Utils/UseDashboardGridDnd.js.map +1 -0
  356. package/build/dist/Utils/Dashboard/DashboardViewConfig.js +42 -26
  357. package/build/dist/Utils/Dashboard/DashboardViewConfig.js.map +1 -1
  358. package/build/dist/Utils/Dashboard/GridLayout.js +338 -0
  359. package/build/dist/Utils/Dashboard/GridLayout.js.map +1 -0
  360. package/build/dist/Utils/Metrics/RecordingRuleExpression.js +19 -17
  361. package/build/dist/Utils/Metrics/RecordingRuleExpression.js.map +1 -1
  362. package/build/dist/Utils/Monitor/MonitorMetricType.js +38 -6
  363. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  364. package/build/dist/Utils/StatusPage/GroupNestingLayout.js +46 -0
  365. package/build/dist/Utils/StatusPage/GroupNestingLayout.js.map +1 -1
  366. package/build/dist/Utils/StatusPage/GroupTree.js +79 -34
  367. package/build/dist/Utils/StatusPage/GroupTree.js.map +1 -1
  368. package/build/dist/Utils/StatusPage/ResourceUptime.js +22 -1
  369. package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
  370. package/build/dist/Utils/Telemetry/SavedViewTimeRange.js +79 -0
  371. package/build/dist/Utils/Telemetry/SavedViewTimeRange.js.map +1 -0
  372. package/build/dist/Utils/Telemetry/TelemetryQueryTimeRange.js +236 -0
  373. package/build/dist/Utils/Telemetry/TelemetryQueryTimeRange.js.map +1 -0
  374. package/package.json +1 -1
@@ -146,6 +146,7 @@ export default class InvestigationGrader {
146
146
  select: {
147
147
  _id: true,
148
148
  autoGrade: true,
149
+ completedAt: true,
149
150
  },
150
151
  sort: { createdAt: SortOrder.Descending },
151
152
  props: { isRoot: true },
@@ -175,13 +176,16 @@ export default class InvestigationGrader {
175
176
  }
176
177
 
177
178
  /*
178
- * The posted analysis: the latest RootCause feed item the AI's
179
- * postAnalysis is its only writer, and it is the only persisted copy
180
- * of the analysis (the same source the fix recipes and the remediation
181
- * planner read, through this one helper).
179
+ * The posted analysis owned by this exact run. A newer investigation or
180
+ * a human RootCause item may exist by resolution time; neither may be
181
+ * graded as though it came from the selected investigation.
182
182
  */
183
183
  const analysisMarkdown: string | null =
184
- await PostedRootCause.getForIncident(incidentId);
184
+ await PostedRootCause.getForInvestigation({
185
+ incidentId,
186
+ aiRunId: run.id!,
187
+ runCompletedAt: run.completedAt,
188
+ });
185
189
 
186
190
  if (!analysisMarkdown) {
187
191
  logger.debug(
@@ -1,7 +1,9 @@
1
1
  import ObjectID from "../../../../Types/ObjectID";
2
2
  import OneUptimeDate from "../../../../Types/Date";
3
+ import Query from "../../../../Types/BaseDatabase/Query";
3
4
  import AIRunType from "../../../../Types/AI/AIRunType";
4
5
  import AIRunStatus from "../../../../Types/AI/AIRunStatus";
6
+ import AIRunCodeFixRecommendation from "../../../../Types/AI/AIRunCodeFixRecommendation";
5
7
  import AIRun from "../../../../Models/DatabaseModels/AIRun";
6
8
  import Project from "../../../../Models/DatabaseModels/Project";
7
9
  import AIRunService from "../../../Services/AIRunService";
@@ -11,6 +13,7 @@ import SortOrder from "../../../../Types/BaseDatabase/SortOrder";
11
13
  import QueryHelper from "../../../Types/Database/QueryHelper";
12
14
  import logger from "../../Logger";
13
15
  import CaptureSpan from "../../Telemetry/CaptureSpan";
16
+ import InvestigationSubjectLock from "./InvestigationSubjectLock";
14
17
 
15
18
  /*
16
19
  * AI SRE — the durable investigation queue (Phase 2's first item; Q1
@@ -47,11 +50,11 @@ import CaptureSpan from "../../Telemetry/CaptureSpan";
47
50
  export const MAX_INVESTIGATION_ATTEMPTS: number = 2;
48
51
 
49
52
  /*
50
- * G4 cost guardrail: at most this many investigations may be Running per
51
- * project at once (per-project override in
52
- * Project.aiMaxConcurrentInvestigations). Enforced at CLAIM time, so a storm
53
- * queues (bounded by dedupe + severity gates + this TTL) and drains at cap
54
- * rate instead of being dropped.
53
+ * G4 cost guardrail: at most this many investigations may be Running in an
54
+ * incident, alert or subjectless lane at once. Each lane has its own project
55
+ * override; subjectless work keeps Project.aiMaxConcurrentInvestigations.
56
+ * Enforced at CLAIM time, so a storm queues (bounded by dedupe + severity
57
+ * gates + this TTL) and drains at cap rate instead of being dropped.
55
58
  */
56
59
  export const DEFAULT_MAX_CONCURRENT_INVESTIGATIONS: number = 3;
57
60
  /*
@@ -62,7 +65,7 @@ const MIN_CONCURRENT_INVESTIGATIONS: number = 1;
62
65
  const MAX_CONCURRENT_INVESTIGATIONS: number = 25;
63
66
 
64
67
  /*
65
- * Lane priority. Two kinds of run share this queue and the per-project
68
+ * Lane priority. Two kinds of run share this queue and each subject lane's
66
69
  * concurrency cap above:
67
70
  * - the INTERACTIVE lane (incident/alert RCA) — a human is waiting;
68
71
  * - the PREVENTIVE lane (AI-insight triage, identified by
@@ -143,6 +146,13 @@ export default class AIInvestigationQueue {
143
146
  }): Promise<ObjectID | null> {
144
147
  const { projectId } = data;
145
148
 
149
+ if (data.subjectIncidentId && data.subjectAlertId) {
150
+ logger.error(
151
+ `AI: not enqueueing investigation for project ${projectId.toString()} — a run cannot belong to both an incident and an alert.`,
152
+ );
153
+ return null;
154
+ }
155
+
146
156
  /*
147
157
  * Budget quiet-skip at enqueue: when the daily budget is already
148
158
  * exhausted there is no point recording intent that the TTL would
@@ -150,7 +160,10 @@ export default class AIInvestigationQueue {
150
160
  */
151
161
  try {
152
162
  const budget: AutonomousBudgetStatus =
153
- await AIService.getAutonomousDailyBudgetStatus(projectId);
163
+ await AIService.getAutonomousDailyBudgetStatus(projectId, {
164
+ incidentId: data.subjectIncidentId,
165
+ alertId: data.subjectAlertId,
166
+ });
154
167
 
155
168
  if (budget.exhausted) {
156
169
  logger.debug(
@@ -173,6 +186,13 @@ export default class AIInvestigationQueue {
173
186
  run.projectId = projectId;
174
187
  run.runType = runType;
175
188
  run.status = AIRunStatus.Queued;
189
+ /*
190
+ * Queued and Running rows must never advertise an unresolved code-fix
191
+ * decision. The engine atomically moves eligible incident/alert runs to
192
+ * Pending only when it wins the Running -> Completed transition; until
193
+ * then the fail-closed default is NotRecommended.
194
+ */
195
+ run.codeFixRecommendation = AIRunCodeFixRecommendation.NotRecommended;
176
196
 
177
197
  if (data.subjectIncidentId) {
178
198
  run.triggeredByIncidentId = data.subjectIncidentId;
@@ -193,10 +213,32 @@ export default class AIInvestigationQueue {
193
213
 
194
214
  let createdRun: AIRun;
195
215
  try {
196
- createdRun = await AIRunService.create({
197
- data: run,
198
- props: { isRoot: true },
199
- });
216
+ const createRun: () => Promise<AIRun> = async (): Promise<AIRun> => {
217
+ return AIRunService.create({
218
+ data: run,
219
+ props: { isRoot: true },
220
+ });
221
+ };
222
+
223
+ /*
224
+ * Linearize subject Investigation creation with FixFromIncident's
225
+ * final latest-source validation and INSERT. Whichever writer gets the
226
+ * shared lock first becomes authoritative: a fix inserted first is
227
+ * valid at that instant; an Investigation inserted first is observed by
228
+ * the fix gate and makes the stale request fail closed.
229
+ */
230
+ createdRun =
231
+ runType === AIRunType.Investigation &&
232
+ (data.subjectIncidentId || data.subjectAlertId)
233
+ ? await InvestigationSubjectLock.runExclusive(
234
+ {
235
+ projectId,
236
+ incidentId: data.subjectIncidentId,
237
+ alertId: data.subjectAlertId,
238
+ },
239
+ createRun,
240
+ )
241
+ : await createRun();
200
242
  } catch (error) {
201
243
  logger.error(`AI: failed to enqueue investigation run: ${error}`);
202
244
  return null;
@@ -291,6 +333,7 @@ export default class AIInvestigationQueue {
291
333
  fromStatus: AIRunStatus.Queued,
292
334
  set: {
293
335
  status: AIRunStatus.Cancelled,
336
+ codeFixRecommendation: AIRunCodeFixRecommendation.NotRecommended,
294
337
  completedAt: OneUptimeDate.getCurrentDate(),
295
338
  errorMessage: `Expired in the investigation queue after ${QUEUE_TTL_MINUTES} minutes — a first-pass analysis this late would no longer be useful. The project may have been at its concurrency cap or daily token budget.`,
296
339
  },
@@ -404,8 +447,10 @@ export default class AIInvestigationQueue {
404
447
  const requeued: number = await AIRunService.attemptStatusTransition({
405
448
  aiRunId: data.aiRunId,
406
449
  fromStatus: AIRunStatus.Running,
450
+ expectedAttemptCount: data.attemptCount,
407
451
  set: {
408
452
  status: AIRunStatus.Queued,
453
+ codeFixRecommendation: AIRunCodeFixRecommendation.NotRecommended,
409
454
  errorMessage: `Attempt ${data.attemptCount} failed and the run was requeued: ${truncatedMessage}`,
410
455
  },
411
456
  });
@@ -422,8 +467,10 @@ export default class AIInvestigationQueue {
422
467
  const finalized: number = await AIRunService.attemptStatusTransition({
423
468
  aiRunId: data.aiRunId,
424
469
  fromStatus: AIRunStatus.Running,
470
+ expectedAttemptCount: data.attemptCount,
425
471
  set: {
426
472
  status: AIRunStatus.Error,
473
+ codeFixRecommendation: AIRunCodeFixRecommendation.NotRecommended,
427
474
  completedAt: OneUptimeDate.getCurrentDate(),
428
475
  errorMessage: truncatedMessage,
429
476
  },
@@ -448,17 +495,21 @@ export default class AIInvestigationQueue {
448
495
  public static async requeueOrMarkStale(run: {
449
496
  id: ObjectID;
450
497
  attemptCount: number;
498
+ lastHeartbeatAt?: Date | undefined;
451
499
  runType?: AIRunType | undefined;
452
500
  projectId?: ObjectID | undefined;
453
501
  triggeredByIncidentId?: ObjectID | undefined;
454
502
  triggeredByAlertId?: ObjectID | undefined;
455
- }): Promise<"requeued" | "stale"> {
503
+ }): Promise<"requeued" | "stale" | "noop"> {
456
504
  if ((run.attemptCount || 0) < MAX_INVESTIGATION_ATTEMPTS) {
457
505
  const requeued: number = await AIRunService.attemptStatusTransition({
458
506
  aiRunId: run.id,
459
507
  fromStatus: AIRunStatus.Running,
508
+ expectedAttemptCount: run.attemptCount,
509
+ expectedLastHeartbeatAt: run.lastHeartbeatAt,
460
510
  set: {
461
511
  status: AIRunStatus.Queued,
512
+ codeFixRecommendation: AIRunCodeFixRecommendation.NotRecommended,
462
513
  errorMessage: `Attempt ${run.attemptCount} stopped reporting progress (the server processing it may have restarted) and the run was requeued.`,
463
514
  },
464
515
  });
@@ -471,8 +522,11 @@ export default class AIInvestigationQueue {
471
522
  const staled: number = await AIRunService.attemptStatusTransition({
472
523
  aiRunId: run.id,
473
524
  fromStatus: AIRunStatus.Running,
525
+ expectedAttemptCount: run.attemptCount,
526
+ expectedLastHeartbeatAt: run.lastHeartbeatAt,
474
527
  set: {
475
528
  status: AIRunStatus.Stale,
529
+ codeFixRecommendation: AIRunCodeFixRecommendation.NotRecommended,
476
530
  completedAt: OneUptimeDate.getCurrentDate(),
477
531
  errorMessage:
478
532
  "The run stopped reporting progress and was marked as stale after exhausting its retry attempts. The server processing it may have restarted.",
@@ -485,9 +539,10 @@ export default class AIInvestigationQueue {
485
539
  */
486
540
  if (staled > 0) {
487
541
  await this.handOffSettledInvestigationToRemediation(run);
542
+ return "stale";
488
543
  }
489
544
 
490
- return "stale";
545
+ return "noop";
491
546
  }
492
547
 
493
548
  /*
@@ -544,26 +599,50 @@ export default class AIInvestigationQueue {
544
599
  * errors.
545
600
  */
546
601
  private static async passesClaimGates(run: QueuedRunRef): Promise<boolean> {
547
- // Per-project cap override, defaulting to 3 and clamped to [1, 25].
602
+ if (run.triggeredByIncidentId && run.triggeredByAlertId) {
603
+ logger.error(
604
+ `AI: leaving run ${run.id.toString()} queued — a run cannot belong to both an incident and an alert.`,
605
+ );
606
+ return false;
607
+ }
608
+
609
+ /*
610
+ * Each subject lane owns its own concurrency pool. Insight triage and any
611
+ * future subjectless queue work retain the legacy project-wide fallback.
612
+ */
548
613
  const project: Project | null = await ProjectService.findOneById({
549
614
  id: run.projectId,
550
- select: { aiMaxConcurrentInvestigations: true },
615
+ select: {
616
+ aiMaxConcurrentInvestigations: true,
617
+ incidentAiMaxConcurrentInvestigations: true,
618
+ alertAiMaxConcurrentInvestigations: true,
619
+ },
551
620
  props: { isRoot: true },
552
621
  });
553
622
 
623
+ let configuredConcurrencyCap: number | undefined =
624
+ project?.aiMaxConcurrentInvestigations;
625
+
626
+ if (run.triggeredByIncidentId) {
627
+ configuredConcurrencyCap = project?.incidentAiMaxConcurrentInvestigations;
628
+ } else if (run.triggeredByAlertId) {
629
+ configuredConcurrencyCap = project?.alertAiMaxConcurrentInvestigations;
630
+ }
631
+
554
632
  const concurrencyCap: number = Math.min(
555
633
  MAX_CONCURRENT_INVESTIGATIONS,
556
634
  Math.max(
557
635
  MIN_CONCURRENT_INVESTIGATIONS,
558
- project?.aiMaxConcurrentInvestigations ??
559
- DEFAULT_MAX_CONCURRENT_INVESTIGATIONS,
636
+ configuredConcurrencyCap ?? DEFAULT_MAX_CONCURRENT_INVESTIGATIONS,
560
637
  ),
561
638
  );
562
639
 
640
+ const subjectLaneQuery: Query<AIRun> = this.getSubjectLaneQuery(run);
641
+
563
642
  /*
564
643
  * Investigations, remediation plans and remediation executions share
565
- * the same per-project cap all are short and storm-shaped on
566
- * incident/alert creation, so one pool keeps the cost model simple.
644
+ * the cap inside their incident, alert or subjectless lane. Work in one
645
+ * lane never consumes another lane's concurrency slots.
567
646
  */
568
647
  const runningCount: number = (
569
648
  await AIRunService.countBy({
@@ -575,6 +654,7 @@ export default class AIInvestigationQueue {
575
654
  AIRunType.RemediationExecution,
576
655
  ]),
577
656
  status: AIRunStatus.Running,
657
+ ...subjectLaneQuery,
578
658
  },
579
659
  props: { isRoot: true },
580
660
  })
@@ -595,10 +675,10 @@ export default class AIInvestigationQueue {
595
675
  * kinds share ONE combined sub-cap, so together they always leave the
596
676
  * reserved slot reachable. Floored at 1 so a project running at the
597
677
  * minimum cap of 1 still drains its background work (slower, one at a
598
- * time) instead of deadlocking the lane entirely — the interactive lane
599
- * keeps its priority through the global cap check above, which a single
600
- * running background run cannot outlast for long (both kinds are
601
- * read-only and short).
678
+ * time) instead of deadlocking the lane entirely — foreground work keeps
679
+ * its priority through the lane cap check above, which a single running
680
+ * background run cannot outlast for long (both kinds are read-only and
681
+ * short).
602
682
  */
603
683
  const isBackgroundLane: boolean =
604
684
  Boolean(run.triggeredByAiInsightId) ||
@@ -622,6 +702,7 @@ export default class AIInvestigationQueue {
622
702
  runType: AIRunType.Investigation,
623
703
  status: AIRunStatus.Running,
624
704
  triggeredByAiInsightId: QueryHelper.notNull(),
705
+ ...subjectLaneQuery,
625
706
  },
626
707
  props: { isRoot: true },
627
708
  })
@@ -636,6 +717,7 @@ export default class AIInvestigationQueue {
636
717
  AIRunType.RemediationExecution,
637
718
  ]),
638
719
  status: AIRunStatus.Running,
720
+ ...subjectLaneQuery,
639
721
  },
640
722
  props: { isRoot: true },
641
723
  })
@@ -653,7 +735,10 @@ export default class AIInvestigationQueue {
653
735
  }
654
736
 
655
737
  const budget: AutonomousBudgetStatus =
656
- await AIService.getAutonomousDailyBudgetStatus(run.projectId);
738
+ await AIService.getAutonomousDailyBudgetStatus(run.projectId, {
739
+ incidentId: run.triggeredByIncidentId,
740
+ alertId: run.triggeredByAlertId,
741
+ });
657
742
 
658
743
  if (budget.exhausted) {
659
744
  logger.debug(
@@ -665,6 +750,35 @@ export default class AIInvestigationQueue {
665
750
  return true;
666
751
  }
667
752
 
753
+ /*
754
+ * Scope queue counts to one independent lane. The concrete subject id is
755
+ * deliberately not compared: all incidents share the incident cap, all
756
+ * alerts share the alert cap, and subjectless work must have neither link.
757
+ */
758
+ private static getSubjectLaneQuery(run: {
759
+ triggeredByIncidentId?: ObjectID | undefined;
760
+ triggeredByAlertId?: ObjectID | undefined;
761
+ }): Query<AIRun> {
762
+ if (run.triggeredByIncidentId) {
763
+ return {
764
+ triggeredByIncidentId: QueryHelper.notNull(),
765
+ triggeredByAlertId: QueryHelper.isNull(),
766
+ };
767
+ }
768
+
769
+ if (run.triggeredByAlertId) {
770
+ return {
771
+ triggeredByIncidentId: QueryHelper.isNull(),
772
+ triggeredByAlertId: QueryHelper.notNull(),
773
+ };
774
+ }
775
+
776
+ return {
777
+ triggeredByIncidentId: QueryHelper.isNull(),
778
+ triggeredByAlertId: QueryHelper.isNull(),
779
+ };
780
+ }
781
+
668
782
  /*
669
783
  * The atomic claim: Queued -> Running, guarded on the attemptCount the
670
784
  * caller observed, so a stale queue snapshot can neither double-claim
@@ -709,6 +823,7 @@ export default class AIInvestigationQueue {
709
823
  await AIRunService.attemptStatusTransition({
710
824
  aiRunId: run.id,
711
825
  fromStatus: AIRunStatus.Running,
826
+ expectedAttemptCount: attempt,
712
827
  set: {
713
828
  status: AIRunStatus.Error,
714
829
  completedAt: OneUptimeDate.getCurrentDate(),
@@ -737,6 +852,7 @@ export default class AIInvestigationQueue {
737
852
  await AIRunService.attemptStatusTransition({
738
853
  aiRunId: run.id,
739
854
  fromStatus: AIRunStatus.Running,
855
+ expectedAttemptCount: attempt,
740
856
  set: {
741
857
  status: AIRunStatus.Error,
742
858
  completedAt: OneUptimeDate.getCurrentDate(),
@@ -817,6 +933,7 @@ export default class AIInvestigationQueue {
817
933
  await AIRunService.attemptStatusTransition({
818
934
  aiRunId: run.id,
819
935
  fromStatus: AIRunStatus.Running,
936
+ expectedAttemptCount: attempt,
820
937
  set: {
821
938
  status: AIRunStatus.Error,
822
939
  completedAt: OneUptimeDate.getCurrentDate(),
@@ -0,0 +1,66 @@
1
+ import ObjectID from "../../../../Types/ObjectID";
2
+ import BadDataException from "../../../../Types/Exception/BadDataException";
3
+ import Semaphore, { SemaphoreMutex } from "../../../Infrastructure/Semaphore";
4
+ import logger from "../../Logger";
5
+
6
+ /*
7
+ * Investigation creation and FixFromIncident authorization must share one
8
+ * linearization point. Without it, a new Investigation can be inserted after
9
+ * a fix trigger's "latest run" read but before the CodeFix INSERT, allowing a
10
+ * task grounded in an analysis that is no longer authoritative.
11
+ */
12
+ export const INVESTIGATION_SUBJECT_LOCK_NAMESPACE: string =
13
+ "InvestigationSubjectLifecycle";
14
+
15
+ export interface InvestigationSubjectLockData {
16
+ projectId: ObjectID;
17
+ incidentId?: ObjectID | undefined;
18
+ alertId?: ObjectID | undefined;
19
+ }
20
+
21
+ export function getInvestigationSubjectLockKey(
22
+ data: InvestigationSubjectLockData,
23
+ ): string {
24
+ if (Boolean(data.incidentId) === Boolean(data.alertId)) {
25
+ throw new BadDataException(
26
+ "Exactly one incident or alert subject is required.",
27
+ );
28
+ }
29
+
30
+ const subjectKey: string = data.incidentId
31
+ ? `incident-${data.incidentId.toString()}`
32
+ : `alert-${data.alertId!.toString()}`;
33
+
34
+ return `${data.projectId.toString()}-${subjectKey}`;
35
+ }
36
+
37
+ export default class InvestigationSubjectLock {
38
+ public static async runExclusive<T>(
39
+ data: InvestigationSubjectLockData,
40
+ operation: () => Promise<T>,
41
+ ): Promise<T> {
42
+ const key: string = getInvestigationSubjectLockKey(data);
43
+ const mutex: SemaphoreMutex = await Semaphore.lock({
44
+ key,
45
+ namespace: INVESTIGATION_SUBJECT_LOCK_NAMESPACE,
46
+ lockTimeout: 30 * 1000,
47
+ acquireTimeout: 10 * 1000,
48
+ });
49
+
50
+ try {
51
+ return await operation();
52
+ } finally {
53
+ try {
54
+ await Semaphore.release(mutex);
55
+ } catch (error) {
56
+ /*
57
+ * The Redis mutex expires automatically. Do not hide the operation's
58
+ * result (or its more useful error) when best-effort release fails.
59
+ */
60
+ logger.error(
61
+ `AI: failed to release the investigation subject lock ${key}; it will expire automatically: ${error}`,
62
+ );
63
+ }
64
+ }
65
+ }
66
+ }
@@ -8,8 +8,26 @@ import AlertFeed, {
8
8
  } from "../../../../Models/DatabaseModels/AlertFeed";
9
9
  import ObjectID from "../../../../Types/ObjectID";
10
10
  import SortOrder from "../../../../Types/BaseDatabase/SortOrder";
11
+ import QueryHelper from "../../../Types/Database/QueryHelper";
11
12
  import CaptureSpan from "../../Telemetry/CaptureSpan";
12
13
 
14
+ export const AI_ROOT_CAUSE_HEADING: string =
15
+ "AI — Automated Root Cause Analysis";
16
+
17
+ export interface PostedRootCauseQueryOptions {
18
+ /* Exact ownership for newly posted AI investigation reports. */
19
+ aiRunId?: ObjectID | undefined;
20
+ /* Upgrade-only lookup for RootCause rows written before aiRunId existed. */
21
+ withoutAIRunId?: boolean | undefined;
22
+ /*
23
+ * A subject can be investigated more than once. When a caller is resolving
24
+ * the analysis for one particular run, it must not accidentally return a
25
+ * RootCause item posted by an older run while the new one is still being
26
+ * finalized.
27
+ */
28
+ createdAtOrAfter?: Date | undefined;
29
+ }
30
+
13
31
  /*
14
32
  * The analysis an AI investigation posted for a subject.
15
33
  *
@@ -20,14 +38,64 @@ import CaptureSpan from "../../Telemetry/CaptureSpan";
20
38
  * where the analysis lives moves one place.
21
39
  */
22
40
  export default class PostedRootCause {
41
+ /*
42
+ * Resolve one run's report exactly. A narrow compatibility path keeps
43
+ * reports written before aiRunId was deployed visible. Deployment time is
44
+ * not the migration file's build timestamp, so compatibility cannot use a
45
+ * wall-clock cutover. Instead, only null-associated rows posted after the
46
+ * run completed and carrying the server's branded AI heading qualify.
47
+ */
48
+ @CaptureSpan()
49
+ public static async getForInvestigation(data: {
50
+ incidentId?: ObjectID | undefined;
51
+ alertId?: ObjectID | undefined;
52
+ aiRunId: ObjectID;
53
+ runCompletedAt?: Date | undefined;
54
+ }): Promise<string | null> {
55
+ const exactAnalysis: string | null = await this.getForSubject({
56
+ incidentId: data.incidentId,
57
+ alertId: data.alertId,
58
+ aiRunId: data.aiRunId,
59
+ });
60
+
61
+ if (exactAnalysis) {
62
+ return exactAnalysis;
63
+ }
64
+
65
+ if (!data.runCompletedAt) {
66
+ return null;
67
+ }
68
+
69
+ const legacyAnalysis: string | null = await this.getForSubject({
70
+ incidentId: data.incidentId,
71
+ alertId: data.alertId,
72
+ withoutAIRunId: true,
73
+ createdAtOrAfter: data.runCompletedAt,
74
+ });
75
+
76
+ return legacyAnalysis?.includes(AI_ROOT_CAUSE_HEADING)
77
+ ? legacyAnalysis
78
+ : null;
79
+ }
80
+
23
81
  @CaptureSpan()
24
82
  public static async getForIncident(
25
83
  incidentId: ObjectID,
84
+ options?: PostedRootCauseQueryOptions | undefined,
26
85
  ): Promise<string | null> {
27
86
  const feedItem: IncidentFeed | null = await IncidentFeedService.findOneBy({
28
87
  query: {
29
88
  incidentId: incidentId,
30
89
  incidentFeedEventType: IncidentFeedEventType.RootCause,
90
+ ...(options?.aiRunId ? { aiRunId: options.aiRunId } : {}),
91
+ ...(options?.withoutAIRunId ? { aiRunId: QueryHelper.isNull() } : {}),
92
+ ...(options?.createdAtOrAfter
93
+ ? {
94
+ createdAt: QueryHelper.greaterThanEqualTo(
95
+ options.createdAtOrAfter,
96
+ ),
97
+ }
98
+ : {}),
31
99
  },
32
100
  select: {
33
101
  feedInfoInMarkdown: true,
@@ -44,11 +112,23 @@ export default class PostedRootCause {
44
112
  }
45
113
 
46
114
  @CaptureSpan()
47
- public static async getForAlert(alertId: ObjectID): Promise<string | null> {
115
+ public static async getForAlert(
116
+ alertId: ObjectID,
117
+ options?: PostedRootCauseQueryOptions | undefined,
118
+ ): Promise<string | null> {
48
119
  const feedItem: AlertFeed | null = await AlertFeedService.findOneBy({
49
120
  query: {
50
121
  alertId: alertId,
51
122
  alertFeedEventType: AlertFeedEventType.RootCause,
123
+ ...(options?.aiRunId ? { aiRunId: options.aiRunId } : {}),
124
+ ...(options?.withoutAIRunId ? { aiRunId: QueryHelper.isNull() } : {}),
125
+ ...(options?.createdAtOrAfter
126
+ ? {
127
+ createdAt: QueryHelper.greaterThanEqualTo(
128
+ options.createdAtOrAfter,
129
+ ),
130
+ }
131
+ : {}),
52
132
  },
53
133
  select: {
54
134
  feedInfoInMarkdown: true,
@@ -68,13 +148,24 @@ export default class PostedRootCause {
68
148
  public static async getForSubject(data: {
69
149
  incidentId?: ObjectID | undefined;
70
150
  alertId?: ObjectID | undefined;
151
+ aiRunId?: ObjectID | undefined;
152
+ withoutAIRunId?: boolean | undefined;
153
+ createdAtOrAfter?: Date | undefined;
71
154
  }): Promise<string | null> {
72
155
  if (data.incidentId) {
73
- return PostedRootCause.getForIncident(data.incidentId);
156
+ return PostedRootCause.getForIncident(data.incidentId, {
157
+ aiRunId: data.aiRunId,
158
+ withoutAIRunId: data.withoutAIRunId,
159
+ createdAtOrAfter: data.createdAtOrAfter,
160
+ });
74
161
  }
75
162
 
76
163
  if (data.alertId) {
77
- return PostedRootCause.getForAlert(data.alertId);
164
+ return PostedRootCause.getForAlert(data.alertId, {
165
+ aiRunId: data.aiRunId,
166
+ withoutAIRunId: data.withoutAIRunId,
167
+ createdAtOrAfter: data.createdAtOrAfter,
168
+ });
78
169
  }
79
170
 
80
171
  return null;
@@ -8,4 +8,4 @@ Wake-on-signal AI SRE: when an incident or alert is created (and the project has
8
8
  - `AIMemory.ts` — recurrence context from past resolved incidents (retrieval-only v1)
9
9
  - `RemediationHandoff.ts` — the investigation → auto-remediation hand-off (RCA-first ordering). The incident/alert create hooks defer `AutoRemediationRuleEngineService` whenever an investigation was enqueued (the runners' `investigateNew*` return "enqueued"); this module releases the subject to the rule engine when that investigation **settles** — any terminal outcome. Settle-once discipline is inherited from the run-status CAS transitions (the same discipline that keeps `postAnalysis` single-shot): only the actor that WON a terminal transition calls the hand-off. The settle sites: Completed and finalized-Error via `InvestigationRequest.onSettled` / the runners' `failOrRequeue` outcome, queue-TTL expiry and terminal-stale via `InvestigationQueue`'s private hand-off. A failed investigation still hands off — remediation degrades to its pre-RCA behavior, never silently disappears; with no investigation enqueued, the create hook applies rules immediately and this module is never involved.
10
10
  - `ConfidenceSignal.ts` — the structured, server-verified confidence signal (deterministic evidence floor + one constrained classification call), with per-consumer fail directions
11
- - `FixFromIncidentTaskTrigger.ts` — the FixFromIncident recipe's two trigger forms: the human "Open Fix PR from this analysis" button (no opt-in flag the click is the gate), and the automatic form for projects opted in via `Project.enableAutomaticCodeFixes` (default false). The automatic form fires from the runners' `postAnalysis`, only on `AIConfidenceSignal.shouldAutoEnqueueCodeFixTask` a POSITIVE confident classification; the evidence floor and a failed classification both fail toward doing nothing. Never throws; pre-checks the daily fix-run budget as a quiet skip; same repo + per-subject dedupe gates as the button; the enqueued run is system-authored (no userId).
11
+ - `FixFromIncidentTaskTrigger.ts` — the FixFromIncident recipe's two trigger forms: the human "Open Fix PR from this analysis" button (shown only when the investigation's structured classification recommends a repository code change), and the automatic form for projects opted in through the independent incident or alert automatic-code-fix setting (default false). Both use the same fail-closed code-fix recommendation: operational, infrastructure, external, user-error, inconclusive and failed classifications never offer or open a code-fix PR. The automatic form fires only after the exact posted analysis and its Recommended decision are durably stored; it never throws, pre-checks the subject lane's daily fix-run budget as a quiet skip, and shares the same repo + serialized per-subject dedupe gates as the button. The enqueued automatic run is system-authored (no userId).