@oneuptime/common 11.3.26 → 11.4.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 (298) hide show
  1. package/Models/DatabaseModels/AIRun.ts +50 -0
  2. package/Models/DatabaseModels/IncidentEpisodeRoleMember.ts +1 -1
  3. package/Models/DatabaseModels/IncidentMember.ts +1 -1
  4. package/Models/DatabaseModels/IncomingCallPolicy.ts +13 -0
  5. package/Models/DatabaseModels/Index.ts +3 -0
  6. package/Models/DatabaseModels/MigrationFailure.ts +170 -0
  7. package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +51 -2
  8. package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +44 -0
  9. package/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.ts +6 -0
  10. package/Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser.ts +6 -0
  11. package/Models/DatabaseModels/OnCallDutyPolicyTimeLog.ts +1 -1
  12. package/Models/DatabaseModels/OnCallDutyPolicyUserOverride.ts +2 -2
  13. package/Models/DatabaseModels/Project.ts +60 -0
  14. package/Server/API/AIInvestigationAPI.ts +148 -0
  15. package/Server/API/AlertAPI.ts +1 -1
  16. package/Server/API/IncidentAPI.ts +2 -2
  17. package/Server/API/IncidentEpisodeAPI.ts +1 -1
  18. package/Server/API/LlmProviderAPI.ts +1 -0
  19. package/Server/API/ScheduledMaintenanceAPI.ts +1 -1
  20. package/Server/Infrastructure/Postgres/SchemaMigrations/1783470000000-AddIncomingCallPolicyRoutingPhoneNumberUnique.ts +25 -0
  21. package/Server/Infrastructure/Postgres/SchemaMigrations/1783510935686-FixNotNullForeignKeysOnDelete.ts +81 -0
  22. package/Server/Infrastructure/Postgres/SchemaMigrations/1783515836148-MigrationName.ts +25 -0
  23. package/Server/Infrastructure/Postgres/SchemaMigrations/1783523076215-AddMigrationFailureTable.ts +35 -0
  24. package/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.ts +19 -0
  25. package/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.ts +33 -0
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.ts +19 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +14 -0
  28. package/Server/Infrastructure/PostgresDatabase.ts +11 -0
  29. package/Server/Services/AIService.ts +74 -2
  30. package/Server/Services/AlertService.ts +25 -0
  31. package/Server/Services/AnalyticsDatabaseService.ts +10 -1
  32. package/Server/Services/DatabaseService.ts +10 -1
  33. package/Server/Services/IncidentService.ts +26 -1
  34. package/Server/Services/IncidentStateTimelineService.ts +19 -0
  35. package/Server/Services/IncomingCallPolicyEscalationRuleService.ts +75 -2
  36. package/Server/Services/IncomingCallPolicyService.ts +42 -1
  37. package/Server/Services/Index.ts +2 -0
  38. package/Server/Services/MigrationFailureService.ts +9 -0
  39. package/Server/Services/MonitorProbeService.ts +33 -7
  40. package/Server/Services/MonitorService.ts +19 -7
  41. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +65 -0
  42. package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +185 -41
  43. package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +23 -15
  44. package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +39 -0
  45. package/Server/Services/OnCallDutyPolicyScheduleLayerService.ts +148 -0
  46. package/Server/Services/OnCallDutyPolicyScheduleLayerUserService.ts +11 -2
  47. package/Server/Services/OnCallDutyPolicyScheduleService.ts +502 -26
  48. package/Server/Services/OnCallDutyPolicyTimeLogService.ts +49 -7
  49. package/Server/Services/OnCallDutyPolicyUserOverrideService.ts +218 -4
  50. package/Server/Services/ProjectCallSMSConfigService.ts +82 -1
  51. package/Server/Services/TeamMemberService.ts +6 -0
  52. package/Server/Services/UserNotificationRuleService.ts +13 -42
  53. package/Server/Services/UserOnCallLogService.ts +90 -0
  54. package/Server/Services/UserOnCallLogTimelineService.ts +43 -1
  55. package/Server/Types/Database/QueryUtil.ts +57 -0
  56. package/Server/Utils/AI/AlertAIContextBuilder.ts +6 -1
  57. package/Server/Utils/AI/Chat/ChatAgentRunner.ts +14 -2
  58. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +119 -11
  59. package/Server/Utils/AI/IncidentAIContextBuilder.ts +18 -3
  60. package/Server/Utils/AI/IncidentEpisodeAIContextBuilder.ts +6 -1
  61. package/Server/Utils/AI/ScheduledMaintenanceAIContextBuilder.ts +12 -2
  62. package/Server/Utils/AI/Sentinel/AlertInvestigationRunner.ts +141 -0
  63. package/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.ts +178 -0
  64. package/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.ts +105 -0
  65. package/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.ts +405 -0
  66. package/Server/Utils/AI/Sentinel/SentinelMemory.ts +167 -0
  67. package/Server/Utils/AI/Toolbox/IncidentTools.ts +183 -1
  68. package/Server/Utils/AI/Toolbox/Index.ts +15 -1
  69. package/Server/Utils/AI/Toolbox/RecentChangesTools.ts +208 -0
  70. package/Server/Utils/AI/Toolbox/SentinelActionTools.ts +577 -0
  71. package/Server/Utils/AI/Toolbox/Serializer.ts +42 -0
  72. package/Server/Utils/Database/MigrationFailureLog.ts +240 -0
  73. package/Server/Utils/IncomingCallPhoneNumber.ts +41 -0
  74. package/Server/Utils/LLM/LLMService.ts +110 -11
  75. package/Server/Utils/Telemetry/IoTSnapshotScan.ts +12 -9
  76. package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +12 -9
  77. package/Tests/Server/API/BaseAPI.test.ts +1 -0
  78. package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +0 -8
  79. package/Tests/Server/Services/OnCallDutyPolicyScheduleLayerUserReorder.test.ts +233 -0
  80. package/Tests/Server/Services/OnCallDutyPolicyTimeLogServiceScoping.test.ts +242 -0
  81. package/Tests/Server/Services/OnCallDutyPolicyUserOverrideEdit.test.ts +198 -0
  82. package/Tests/Server/Services/UserOnCallLogClaimNotificationRule.test.ts +495 -0
  83. package/Tests/Server/Types/Database/QueryUtil.test.ts +74 -0
  84. package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +78 -0
  85. package/Tests/Server/Utils/AI/ToolResultSerializer.test.ts +68 -0
  86. package/Tests/Types/DateExhaustiveTimezone.test.ts +700 -0
  87. package/Tests/Types/DateTimezoneWallClock.test.ts +99 -0
  88. package/Tests/Types/Events/Recurring.test.ts +22 -9
  89. package/Tests/Types/OnCallDutyPolicy/LayerUtilAuditFixes.test.ts +291 -0
  90. package/Tests/Types/OnCallDutyPolicy/LayerUtilAuditFixesRound2.test.ts +398 -0
  91. package/Tests/Types/OnCallDutyPolicy/LayerUtilDSTWeekendGap.test.ts +114 -0
  92. package/Tests/Types/OnCallDutyPolicy/LayerUtilDSTWeeklyDayShift.test.ts +76 -0
  93. package/Tests/Types/OnCallDutyPolicy/LayerUtilDailyMutation.test.ts +128 -0
  94. package/Tests/Types/OnCallDutyPolicy/LayerUtilDailyProbe.test.ts +203 -0
  95. package/Tests/Types/OnCallDutyPolicy/LayerUtilDiffFuzz.test.ts +322 -0
  96. package/Tests/Types/OnCallDutyPolicy/LayerUtilEdgeCases.test.ts +280 -0
  97. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveCurrentUser.test.ts +863 -0
  98. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveDaily.test.ts +900 -0
  99. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveMultiLayer.test.ts +1104 -0
  100. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveRotation.test.ts +957 -0
  101. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveWeekly.test.ts +1095 -0
  102. package/Tests/Types/OnCallDutyPolicy/LayerUtilInvariants.test.ts +232 -0
  103. package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeAudit.test.ts +227 -0
  104. package/Tests/Types/OnCallDutyPolicy/LayerUtilMonthYearAudit.test.ts +255 -0
  105. package/Tests/Types/OnCallDutyPolicy/LayerUtilMultiLayerAudit.test.ts +388 -0
  106. package/Tests/Types/OnCallDutyPolicy/LayerUtilMultiLayerEdge.test.ts +300 -0
  107. package/Tests/Types/OnCallDutyPolicy/LayerUtilOvernightDSTAudit.test.ts +158 -0
  108. package/Tests/Types/OnCallDutyPolicy/LayerUtilRestrictedGapFix.test.ts +253 -0
  109. package/Tests/Types/OnCallDutyPolicy/LayerUtilRestrictedGapRepro.test.ts +157 -0
  110. package/Tests/Types/OnCallDutyPolicy/LayerUtilRotationFixes.test.ts +414 -0
  111. package/Tests/Types/OnCallDutyPolicy/LayerUtilTimezone.test.ts +243 -0
  112. package/Tests/Types/OnCallDutyPolicy/LayerUtilWeekendGapRepro.test.ts +112 -0
  113. package/Tests/Types/OnCallDutyPolicy/OverrideLensAudit.test.ts +117 -0
  114. package/Tests/Types/OnCallDutyPolicy/RestrictionTimes.test.ts +8 -6
  115. package/Tests/Types/OnCallDutyPolicy/RestrictionTimesDefaultDayAudit.test.ts +112 -0
  116. package/Tests/Types/OnCallDutyPolicy/RestrictionTimesExhaustive.test.ts +821 -0
  117. package/Tests/Types/OnCallDutyPolicy/RestrictionTimesMutationInvariant.test.ts +886 -0
  118. package/Tests/Types/OnCallDutyPolicy/ScheduleShiftUtil.test.ts +260 -0
  119. package/Tests/Types/OnCallDutyPolicy/UserOverrideUtil.test.ts +255 -0
  120. package/Tests/Types/OnCallDutyPolicy/UserOverrideUtilExhaustive.test.ts +1126 -0
  121. package/Types/Date.ts +246 -15
  122. package/Types/OnCallDutyPolicy/Layer.ts +790 -260
  123. package/Types/OnCallDutyPolicy/RestrictionTimes.ts +39 -13
  124. package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +257 -0
  125. package/Types/OnCallDutyPolicy/UserOverrideUtil.ts +21 -5
  126. package/UI/Components/Calendar/Calendar.tsx +8 -2
  127. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +5 -5
  128. package/UI/Components/Events/RecurringFieldElement.tsx +16 -0
  129. package/UI/Components/ModelFormModal/ModelFormModal.tsx +8 -0
  130. package/UI/Components/RadioButtons/BasicRadioButtons.tsx +1 -1
  131. package/build/dist/Models/DatabaseModels/AIRun.js +52 -0
  132. package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
  133. package/build/dist/Models/DatabaseModels/IncidentEpisodeRoleMember.js +1 -1
  134. package/build/dist/Models/DatabaseModels/IncidentEpisodeRoleMember.js.map +1 -1
  135. package/build/dist/Models/DatabaseModels/IncidentMember.js +1 -1
  136. package/build/dist/Models/DatabaseModels/IncidentMember.js.map +1 -1
  137. package/build/dist/Models/DatabaseModels/IncomingCallPolicy.js +10 -0
  138. package/build/dist/Models/DatabaseModels/IncomingCallPolicy.js.map +1 -1
  139. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  140. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  141. package/build/dist/Models/DatabaseModels/MigrationFailure.js +196 -0
  142. package/build/dist/Models/DatabaseModels/MigrationFailure.js.map +1 -0
  143. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +52 -2
  144. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
  145. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +45 -0
  146. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
  147. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js +6 -0
  148. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js.map +1 -1
  149. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser.js +6 -0
  150. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser.js.map +1 -1
  151. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyTimeLog.js +1 -1
  152. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyTimeLog.js.map +1 -1
  153. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyUserOverride.js +2 -2
  154. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyUserOverride.js.map +1 -1
  155. package/build/dist/Models/DatabaseModels/Project.js +62 -0
  156. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  157. package/build/dist/Server/API/AIInvestigationAPI.js +107 -0
  158. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -0
  159. package/build/dist/Server/API/AlertAPI.js +1 -1
  160. package/build/dist/Server/API/IncidentAPI.js +2 -2
  161. package/build/dist/Server/API/IncidentEpisodeAPI.js +1 -1
  162. package/build/dist/Server/API/LlmProviderAPI.js +1 -0
  163. package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
  164. package/build/dist/Server/API/ScheduledMaintenanceAPI.js +1 -1
  165. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783470000000-AddIncomingCallPolicyRoutingPhoneNumberUnique.js +18 -0
  166. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783470000000-AddIncomingCallPolicyRoutingPhoneNumberUnique.js.map +1 -0
  167. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783510935686-FixNotNullForeignKeysOnDelete.js +38 -0
  168. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783510935686-FixNotNullForeignKeysOnDelete.js.map +1 -0
  169. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783515836148-MigrationName.js +16 -0
  170. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783515836148-MigrationName.js.map +1 -0
  171. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783523076215-AddMigrationFailureTable.js +18 -0
  172. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783523076215-AddMigrationFailureTable.js.map +1 -0
  173. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.js +12 -0
  174. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.js.map +1 -0
  175. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.js +18 -0
  176. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.js.map +1 -0
  177. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.js +12 -0
  178. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.js.map +1 -0
  179. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +14 -0
  180. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  181. package/build/dist/Server/Infrastructure/PostgresDatabase.js +9 -0
  182. package/build/dist/Server/Infrastructure/PostgresDatabase.js.map +1 -1
  183. package/build/dist/Server/Services/AIService.js +53 -2
  184. package/build/dist/Server/Services/AIService.js.map +1 -1
  185. package/build/dist/Server/Services/AlertService.js +24 -0
  186. package/build/dist/Server/Services/AlertService.js.map +1 -1
  187. package/build/dist/Server/Services/AnalyticsDatabaseService.js +10 -1
  188. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  189. package/build/dist/Server/Services/DatabaseService.js +10 -1
  190. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  191. package/build/dist/Server/Services/IncidentService.js +25 -1
  192. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  193. package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
  194. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  195. package/build/dist/Server/Services/IncomingCallPolicyEscalationRuleService.js +55 -2
  196. package/build/dist/Server/Services/IncomingCallPolicyEscalationRuleService.js.map +1 -1
  197. package/build/dist/Server/Services/IncomingCallPolicyService.js +40 -0
  198. package/build/dist/Server/Services/IncomingCallPolicyService.js.map +1 -1
  199. package/build/dist/Server/Services/Index.js +2 -0
  200. package/build/dist/Server/Services/Index.js.map +1 -1
  201. package/build/dist/Server/Services/MigrationFailureService.js +9 -0
  202. package/build/dist/Server/Services/MigrationFailureService.js.map +1 -0
  203. package/build/dist/Server/Services/MonitorProbeService.js +33 -7
  204. package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
  205. package/build/dist/Server/Services/MonitorService.js +12 -5
  206. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  207. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +81 -22
  208. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  209. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +134 -31
  210. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
  211. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +21 -13
  212. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
  213. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +36 -0
  214. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
  215. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js +121 -0
  216. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js.map +1 -1
  217. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js +11 -2
  218. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js.map +1 -1
  219. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +409 -36
  220. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  221. package/build/dist/Server/Services/OnCallDutyPolicyTimeLogService.js +52 -8
  222. package/build/dist/Server/Services/OnCallDutyPolicyTimeLogService.js.map +1 -1
  223. package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js +170 -3
  224. package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js.map +1 -1
  225. package/build/dist/Server/Services/ProjectCallSMSConfigService.js +72 -0
  226. package/build/dist/Server/Services/ProjectCallSMSConfigService.js.map +1 -1
  227. package/build/dist/Server/Services/TeamMemberService.js +6 -0
  228. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  229. package/build/dist/Server/Services/UserNotificationRuleService.js +12 -30
  230. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  231. package/build/dist/Server/Services/UserOnCallLogService.js +75 -0
  232. package/build/dist/Server/Services/UserOnCallLogService.js.map +1 -1
  233. package/build/dist/Server/Services/UserOnCallLogTimelineService.js +31 -1
  234. package/build/dist/Server/Services/UserOnCallLogTimelineService.js.map +1 -1
  235. package/build/dist/Server/Types/Database/QueryUtil.js +45 -0
  236. package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
  237. package/build/dist/Server/Utils/AI/AlertAIContextBuilder.js +6 -1
  238. package/build/dist/Server/Utils/AI/AlertAIContextBuilder.js.map +1 -1
  239. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +13 -2
  240. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
  241. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +59 -11
  242. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  243. package/build/dist/Server/Utils/AI/IncidentAIContextBuilder.js +18 -3
  244. package/build/dist/Server/Utils/AI/IncidentAIContextBuilder.js.map +1 -1
  245. package/build/dist/Server/Utils/AI/IncidentEpisodeAIContextBuilder.js +6 -1
  246. package/build/dist/Server/Utils/AI/IncidentEpisodeAIContextBuilder.js.map +1 -1
  247. package/build/dist/Server/Utils/AI/ScheduledMaintenanceAIContextBuilder.js +12 -2
  248. package/build/dist/Server/Utils/AI/ScheduledMaintenanceAIContextBuilder.js.map +1 -1
  249. package/build/dist/Server/Utils/AI/Sentinel/AlertInvestigationRunner.js +119 -0
  250. package/build/dist/Server/Utils/AI/Sentinel/AlertInvestigationRunner.js.map +1 -0
  251. package/build/dist/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.js +147 -0
  252. package/build/dist/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.js.map +1 -0
  253. package/build/dist/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.js +93 -0
  254. package/build/dist/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.js.map +1 -0
  255. package/build/dist/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.js +311 -0
  256. package/build/dist/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.js.map +1 -0
  257. package/build/dist/Server/Utils/AI/Sentinel/SentinelMemory.js +143 -0
  258. package/build/dist/Server/Utils/AI/Sentinel/SentinelMemory.js.map +1 -0
  259. package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js +152 -1
  260. package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js.map +1 -1
  261. package/build/dist/Server/Utils/AI/Toolbox/Index.js +10 -1
  262. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
  263. package/build/dist/Server/Utils/AI/Toolbox/RecentChangesTools.js +165 -0
  264. package/build/dist/Server/Utils/AI/Toolbox/RecentChangesTools.js.map +1 -0
  265. package/build/dist/Server/Utils/AI/Toolbox/SentinelActionTools.js +451 -0
  266. package/build/dist/Server/Utils/AI/Toolbox/SentinelActionTools.js.map +1 -0
  267. package/build/dist/Server/Utils/AI/Toolbox/Serializer.js +40 -0
  268. package/build/dist/Server/Utils/AI/Toolbox/Serializer.js.map +1 -1
  269. package/build/dist/Server/Utils/Database/MigrationFailureLog.js +174 -0
  270. package/build/dist/Server/Utils/Database/MigrationFailureLog.js.map +1 -0
  271. package/build/dist/Server/Utils/IncomingCallPhoneNumber.js +30 -0
  272. package/build/dist/Server/Utils/IncomingCallPhoneNumber.js.map +1 -0
  273. package/build/dist/Server/Utils/LLM/LLMService.js +81 -5
  274. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  275. package/build/dist/Server/Utils/Telemetry/IoTSnapshotScan.js +11 -8
  276. package/build/dist/Server/Utils/Telemetry/IoTSnapshotScan.js.map +1 -1
  277. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +11 -8
  278. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
  279. package/build/dist/Types/Date.js +185 -15
  280. package/build/dist/Types/Date.js.map +1 -1
  281. package/build/dist/Types/OnCallDutyPolicy/Layer.js +602 -171
  282. package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
  283. package/build/dist/Types/OnCallDutyPolicy/RestrictionTimes.js +27 -13
  284. package/build/dist/Types/OnCallDutyPolicy/RestrictionTimes.js.map +1 -1
  285. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +159 -0
  286. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -0
  287. package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js +20 -5
  288. package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js.map +1 -1
  289. package/build/dist/UI/Components/Calendar/Calendar.js +2 -2
  290. package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
  291. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +2 -2
  292. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  293. package/build/dist/UI/Components/Events/RecurringFieldElement.js +13 -0
  294. package/build/dist/UI/Components/Events/RecurringFieldElement.js.map +1 -1
  295. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
  296. package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js +1 -1
  297. package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js.map +1 -1
  298. package/package.json +1 -1
@@ -15,6 +15,8 @@ import UserNotificationExecutionStatus from "../../Types/UserNotification/UserNo
15
15
  import User from "../../Models/DatabaseModels/User";
16
16
  import Model from "../../Models/DatabaseModels/UserOnCallLogTimeline";
17
17
  import AlertService from "./AlertService";
18
+ import AlertEpisodeService from "./AlertEpisodeService";
19
+ import IncidentEpisodeService from "./IncidentEpisodeService";
18
20
 
19
21
  export class Service extends DatabaseService<Model> {
20
22
  public constructor() {
@@ -40,6 +42,8 @@ export class Service extends DatabaseService<Model> {
40
42
  onCallDutyPolicyExecutionLogId: true,
41
43
  triggeredByIncidentId: true,
42
44
  triggeredByAlertId: true,
45
+ triggeredByAlertEpisodeId: true,
46
+ triggeredByIncidentEpisodeId: true,
43
47
  onCallDutyPolicyExecutionLogTimelineId: true,
44
48
  },
45
49
  skip: 0,
@@ -52,6 +56,19 @@ export class Service extends DatabaseService<Model> {
52
56
  for (const item of items) {
53
57
  const isIncident: boolean = Boolean(item.triggeredByIncidentId);
54
58
  const isAlert: boolean = Boolean(item.triggeredByAlertId);
59
+ /*
60
+ * Alert-episode and incident-episode pages were never handled here, so
61
+ * acknowledging one via call/SMS/email/push marked only this user's log
62
+ * done but never acknowledged the EPISODE. ExecutePendingExecutions gates
63
+ * every co-notified user's continuation solely on isEpisodeAcknowledged,
64
+ * so the other responders kept escalating and the episode stayed
65
+ * unacknowledged in its own state (audit F15). Handle both episode kinds
66
+ * the same way incident/alert are handled below.
67
+ */
68
+ const isAlertEpisode: boolean = Boolean(item.triggeredByAlertEpisodeId);
69
+ const isIncidentEpisode: boolean = Boolean(
70
+ item.triggeredByIncidentEpisodeId,
71
+ );
55
72
 
56
73
  // this incident is acknowledged.
57
74
 
@@ -73,7 +90,16 @@ export class Service extends DatabaseService<Model> {
73
90
  throw new BadDataException("User not found.");
74
91
  }
75
92
 
76
- const statusMessage: string = `${isIncident ? "Incident" : "Alert"} acknowledged by ${user.name} (${user.email})`;
93
+ const entityLabel: string =
94
+ isIncident || isIncidentEpisode
95
+ ? isIncidentEpisode
96
+ ? "Incident Episode"
97
+ : "Incident"
98
+ : isAlertEpisode
99
+ ? "Alert Episode"
100
+ : "Alert";
101
+
102
+ const statusMessage: string = `${entityLabel} acknowledged by ${user.name} (${user.email})`;
77
103
 
78
104
  await UserOnCallLogService.updateOneById({
79
105
  id: item.userNotificationLogId!,
@@ -133,6 +159,22 @@ export class Service extends DatabaseService<Model> {
133
159
  item.userId!,
134
160
  );
135
161
  }
162
+
163
+ if (isAlertEpisode) {
164
+ // alert episode — stops co-notified responders from escalating.
165
+ await AlertEpisodeService.acknowledgeEpisode(
166
+ item.triggeredByAlertEpisodeId!,
167
+ item.userId!,
168
+ );
169
+ }
170
+
171
+ if (isIncidentEpisode) {
172
+ // incident episode — stops co-notified responders from escalating.
173
+ await IncidentEpisodeService.acknowledgeEpisode(
174
+ item.triggeredByIncidentEpisodeId!,
175
+ item.userId!,
176
+ );
177
+ }
136
178
  }
137
179
  }
138
180
 
@@ -549,6 +549,63 @@ export default class QueryUtil {
549
549
  _id: QueryHelper.any(query[key] as Array<string>),
550
550
  };
551
551
  }
552
+
553
+ /*
554
+ * Text-search operator nested under a single-Entity relation, e.g.
555
+ * { user: { name: new Search("foo") } }. serializeQuery otherwise only
556
+ * visits top-level keys, so the nested operator reaches TypeORM as a raw
557
+ * class instance and silently degrades to an exact equality
558
+ * (`user.name = 'foo'` instead of `user.name ILIKE '%foo%'`) — which is
559
+ * why the Owner / user picker "search" appeared to do nothing once a
560
+ * project had more than a page of members.
561
+ *
562
+ * We deliberately touch ONLY the ILIKE-family leaf operators here and
563
+ * leave every other nested value (arrays, ids, plain equality, other
564
+ * operators) exactly as it was, so existing nested relation filters (the
565
+ * access-control label filters built by @CanAccessIfCanReadOn, etc.)
566
+ * serialize unchanged.
567
+ */
568
+ if (
569
+ tableColumnMetadata &&
570
+ tableColumnMetadata.modelType &&
571
+ tableColumnMetadata.type === TableColumnType.Entity &&
572
+ query[key] &&
573
+ typeof query[key] === Typeof.Object &&
574
+ !Array.isArray(query[key]) &&
575
+ (Object.getPrototypeOf(query[key]) === Object.prototype ||
576
+ Object.getPrototypeOf(query[key]) === null)
577
+ ) {
578
+ const relationModel: BaseModel = new tableColumnMetadata.modelType();
579
+ const relationQuery: Record<string, any> = query[key] as Record<
580
+ string,
581
+ any
582
+ >;
583
+ for (const relationKey in relationQuery) {
584
+ const relationColumnMetadata: TableColumnMetadata =
585
+ relationModel.getTableColumnMetadata(relationKey);
586
+ if (!relationColumnMetadata) {
587
+ continue;
588
+ }
589
+ const nestedValue: any = relationQuery[relationKey];
590
+ if (nestedValue instanceof Search) {
591
+ relationQuery[relationKey] = QueryHelper.search(
592
+ (nestedValue as Search<string>).toString(),
593
+ ) as any;
594
+ } else if (nestedValue instanceof StartsWith) {
595
+ relationQuery[relationKey] = QueryHelper.startsWith(
596
+ (nestedValue as StartsWith<string>).toString(),
597
+ ) as any;
598
+ } else if (nestedValue instanceof EndsWith) {
599
+ relationQuery[relationKey] = QueryHelper.endsWith(
600
+ (nestedValue as EndsWith<string>).toString(),
601
+ ) as any;
602
+ } else if (nestedValue instanceof NotContains) {
603
+ relationQuery[relationKey] = QueryHelper.notContains(
604
+ (nestedValue as NotContains<string>).toString(),
605
+ ) as any;
606
+ }
607
+ }
608
+ }
552
609
  }
553
610
 
554
611
  return query;
@@ -235,7 +235,12 @@ The note should:
235
235
  4. Be detailed enough to help team members understand the situation
236
236
  5. Use technical language appropriate for the engineering team
237
237
 
238
- Write in markdown format for better readability. Be thorough and technical.`;
238
+ Write in markdown format for better readability. Be thorough and technical.
239
+
240
+ Grounding rules (important):
241
+ - Use ONLY facts that appear in the provided alert data. Do NOT invent causes, metrics, impact, or actions the data does not support.
242
+ - If something a section needs is not in the data, write "Not available in the alert record" instead of guessing.
243
+ - Never fabricate numbers or state confidence levels.`;
239
244
  }
240
245
 
241
246
  // Build user message
@@ -908,9 +908,21 @@ export default class ChatAgentRunner {
908
908
  continue;
909
909
  }
910
910
 
911
+ const isUserMessage: boolean = message.role === AIChatMessageRole.User;
912
+
911
913
  messages.push({
912
- role: message.role === AIChatMessageRole.User ? "user" : "assistant",
913
- content: message.contentInMarkdown,
914
+ role: isUserMessage ? "user" : "assistant",
915
+ /*
916
+ * A prior assistant answer carries [C#] citation markers that pointed
917
+ * at that turn's tool results — citations that no longer exist in this
918
+ * turn. Left in the replayed history the model echoes and renumbers
919
+ * them, and this turn's stripFabricatedCitationMarkers then deletes the
920
+ * unmatched ones, leaving claims that look uncited. Strip the markers
921
+ * from replayed answers so only freshly minted citations ever appear.
922
+ */
923
+ content: isUserMessage
924
+ ? message.contentInMarkdown
925
+ : message.contentInMarkdown.replace(/\s?\[C\d+\]/g, ""),
914
926
  });
915
927
  }
916
928
 
@@ -1,6 +1,7 @@
1
1
  import DatabaseCommonInteractionProps from "../../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
2
2
  import OneUptimeDate from "../../../../Types/Date";
3
3
  import ObjectID from "../../../../Types/ObjectID";
4
+ import { JSONObject } from "../../../../Types/JSON";
4
5
  import { AIChatCitation } from "../../../../Types/AI/AIChatTypes";
5
6
  import AIService, { AILogResponse } from "../../../Services/AIService";
6
7
  import logger from "../../Logger";
@@ -32,6 +33,29 @@ export interface ObservabilityAssistantPriorTurn {
32
33
  content: string;
33
34
  }
34
35
 
36
+ /*
37
+ * A single "thinking step" emitted live as the agent loop runs, so callers can
38
+ * narrate the investigation in real time ("Reading trace ✓ 0.4s"). This is the
39
+ * step-level narration the OneUptime UI renders by polling AIRunEvent rows.
40
+ */
41
+ export type ObservabilityAssistantStepType =
42
+ | "llm_started"
43
+ | "llm_completed"
44
+ | "tool_started"
45
+ | "tool_completed"
46
+ | "tool_failed";
47
+
48
+ export interface ObservabilityAssistantStep {
49
+ type: ObservabilityAssistantStepType;
50
+ toolName?: string | undefined;
51
+ toolArguments?: JSONObject | undefined;
52
+ rowCount?: number | undefined;
53
+ durationMs?: number | undefined;
54
+ citationId?: string | undefined;
55
+ totalTokens?: number | undefined;
56
+ errorMessage?: string | undefined;
57
+ }
58
+
35
59
  export interface ObservabilityAssistantRequest {
36
60
  projectId: ObjectID;
37
61
  userId?: ObjectID | undefined;
@@ -44,6 +68,29 @@ export interface ObservabilityAssistantRequest {
44
68
  llmProviderId?: ObjectID | undefined;
45
69
  // Label recorded on LlmLog, e.g. "Slack ChatOps".
46
70
  feature: string;
71
+ /*
72
+ * Extra instructions appended to the base observability system prompt — used
73
+ * to give an autonomous run (e.g. Sentinel incident investigation) a distinct
74
+ * persona and task framing while keeping the same hard rules (citations, no
75
+ * fabrication, read-only). The base prompt's grounding rules always win.
76
+ */
77
+ systemInstructions?: string | undefined;
78
+ /*
79
+ * Optional budget overrides. Autonomous investigations get more room than an
80
+ * interactive chat-ops answer, which must return in a single quick message.
81
+ * Omitted values fall back to the interactive defaults.
82
+ */
83
+ maxLlmCalls?: number | undefined;
84
+ maxToolCalls?: number | undefined;
85
+ maxWallClockMs?: number | undefined;
86
+ maxOutputTokens?: number | undefined;
87
+ /*
88
+ * Optional live-narration hook, fired for each LLM/tool step as the loop runs.
89
+ * Used by autonomous runs to persist a per-step AIRunEvent trail so the UI can
90
+ * "watch it think". Best-effort — implementations must not throw; a slow or
91
+ * failing handler should not break the run.
92
+ */
93
+ onStep?: ((step: ObservabilityAssistantStep) => Promise<void>) | undefined;
47
94
  }
48
95
 
49
96
  export interface ObservabilityAssistantResult {
@@ -70,19 +117,50 @@ export default class ObservabilityAssistant {
70
117
  ): Promise<ObservabilityAssistantResult> {
71
118
  const startedAtMs: number = Date.now();
72
119
 
120
+ const maxLlmCalls: number = request.maxLlmCalls ?? MAX_LLM_CALLS;
121
+ const maxToolCalls: number = request.maxToolCalls ?? MAX_TOOL_CALLS;
122
+ const maxWallClockMs: number = request.maxWallClockMs ?? MAX_WALL_CLOCK_MS;
123
+ const maxOutputTokens: number =
124
+ request.maxOutputTokens ?? MAX_OUTPUT_TOKENS;
125
+
126
+ // Best-effort live-narration emitter — never throws into the loop.
127
+ const emitStep: (
128
+ step: ObservabilityAssistantStep,
129
+ ) => Promise<void> = async (
130
+ step: ObservabilityAssistantStep,
131
+ ): Promise<void> => {
132
+ if (!request.onStep) {
133
+ return;
134
+ }
135
+ try {
136
+ await request.onStep(step);
137
+ } catch (err) {
138
+ logger.error(`ObservabilityAssistant onStep failed: ${err}`);
139
+ }
140
+ };
141
+
73
142
  const toolContext: ToolContext = {
74
143
  projectId: request.projectId,
75
144
  props: request.props,
76
145
  };
77
146
 
147
+ let systemPromptContent: string = buildObservabilityChatSystemPrompt({
148
+ currentTime: OneUptimeDate.getCurrentDate(),
149
+ /*
150
+ * Slack/Teams and autonomous investigations have no approval UI, so this
151
+ * surface stays strictly read-only.
152
+ */
153
+ permissionMode: AIChatPermissionMode.ReadOnly,
154
+ });
155
+
156
+ if (request.systemInstructions) {
157
+ systemPromptContent += `\n\n${request.systemInstructions}`;
158
+ }
159
+
78
160
  const messages: Array<LLMMessage> = [
79
161
  {
80
162
  role: "system",
81
- content: buildObservabilityChatSystemPrompt({
82
- currentTime: OneUptimeDate.getCurrentDate(),
83
- // Slack/Teams have no approval UI, so this surface stays read-only.
84
- permissionMode: AIChatPermissionMode.ReadOnly,
85
- }),
163
+ content: systemPromptContent,
86
164
  },
87
165
  ];
88
166
 
@@ -104,9 +182,9 @@ export default class ObservabilityAssistant {
104
182
 
105
183
  while (true) {
106
184
  const budgetExhausted: boolean =
107
- llmCallCount >= MAX_LLM_CALLS - 1 ||
108
- toolCallCount >= MAX_TOOL_CALLS ||
109
- Date.now() - startedAtMs >= MAX_WALL_CLOCK_MS;
185
+ llmCallCount >= maxLlmCalls - 1 ||
186
+ toolCallCount >= maxToolCalls ||
187
+ Date.now() - startedAtMs >= maxWallClockMs;
110
188
 
111
189
  if (budgetExhausted) {
112
190
  messages.push({
@@ -116,6 +194,8 @@ export default class ObservabilityAssistant {
116
194
  });
117
195
  }
118
196
 
197
+ await emitStep({ type: "llm_started" });
198
+
119
199
  const response: AILogResponse = await AIService.executeWithLogging({
120
200
  projectId: request.projectId,
121
201
  userId: request.userId,
@@ -125,7 +205,7 @@ export default class ObservabilityAssistant {
125
205
  tools: budgetExhausted
126
206
  ? undefined
127
207
  : AIToolbox.getLlmToolDefinitions(AIChatPermissionMode.ReadOnly),
128
- maxTokens: MAX_OUTPUT_TOKENS,
208
+ maxTokens: maxOutputTokens,
129
209
  temperature: TEMPERATURE,
130
210
  // Chat-ops content is per-user — do not persist previews to LlmLog.
131
211
  storeContentPreviews: false,
@@ -134,6 +214,11 @@ export default class ObservabilityAssistant {
134
214
  llmCallCount++;
135
215
  totalTokens += response.llmLog.totalTokens || 0;
136
216
 
217
+ await emitStep({
218
+ type: "llm_completed",
219
+ totalTokens: response.llmLog.totalTokens || 0,
220
+ });
221
+
137
222
  if (!providerName) {
138
223
  providerName = response.llmLog.llmProviderName;
139
224
  modelName = response.llmLog.modelName;
@@ -152,8 +237,8 @@ export default class ObservabilityAssistant {
152
237
 
153
238
  for (const toolCall of response.toolCalls) {
154
239
  const overBudget: boolean =
155
- toolCallCount >= MAX_TOOL_CALLS ||
156
- Date.now() - startedAtMs >= MAX_WALL_CLOCK_MS;
240
+ toolCallCount >= maxToolCalls ||
241
+ Date.now() - startedAtMs >= maxWallClockMs;
157
242
 
158
243
  if (overBudget) {
159
244
  messages.push({
@@ -176,6 +261,14 @@ export default class ObservabilityAssistant {
176
261
  continue;
177
262
  }
178
263
 
264
+ const toolStartedAtMs: number = Date.now();
265
+
266
+ await emitStep({
267
+ type: "tool_started",
268
+ toolName: toolCall.name,
269
+ toolArguments: toolCall.arguments,
270
+ });
271
+
179
272
  const outcome: ToolCallOutcome = await AIToolbox.executeTool({
180
273
  name: toolCall.name,
181
274
  args: toolCall.arguments,
@@ -183,6 +276,13 @@ export default class ObservabilityAssistant {
183
276
  });
184
277
 
185
278
  if (!outcome.success || !outcome.result) {
279
+ await emitStep({
280
+ type: "tool_failed",
281
+ toolName: toolCall.name,
282
+ durationMs: Date.now() - toolStartedAtMs,
283
+ errorMessage: outcome.errorMessage || outcome.textForLlm,
284
+ });
285
+
186
286
  messages.push({
187
287
  role: "tool",
188
288
  toolCallId: toolCall.id,
@@ -202,6 +302,14 @@ export default class ObservabilityAssistant {
202
302
  target: outcome.result.citationTarget,
203
303
  });
204
304
 
305
+ await emitStep({
306
+ type: "tool_completed",
307
+ toolName: toolCall.name,
308
+ durationMs: Date.now() - toolStartedAtMs,
309
+ rowCount: outcome.result.rowCount,
310
+ citationId: citationId,
311
+ });
312
+
205
313
  const escapedText: string = escapeToolResultContent(
206
314
  outcome.textForLlm,
207
315
  );
@@ -365,7 +365,12 @@ The postmortem should:
365
365
 
366
366
  Use a standard incident postmortem format with sections for: Executive Summary, Timeline, Root Cause Analysis, Impact, Action Items, and Lessons Learned.
367
367
 
368
- Write in a professional, clear, and concise manner. Use markdown formatting for better readability.`;
368
+ Write in a professional, clear, and concise manner. Use markdown formatting for better readability.
369
+
370
+ Grounding rules (important):
371
+ - Use ONLY facts that appear in the provided incident data. Do NOT invent root causes, timelines, metrics, customer impact, or actions the data does not support.
372
+ - If a section needs information the data does not contain, write "Not available in the incident record" instead of guessing.
373
+ - Never fabricate numbers or state confidence levels.`;
369
374
  }
370
375
 
371
376
  // Build user message based on whether template is provided
@@ -549,7 +554,12 @@ DO NOT include:
549
554
  - Confidential information
550
555
  - Excessive jargon
551
556
 
552
- Write in markdown format for better readability.`;
557
+ Write in markdown format for better readability.
558
+
559
+ Grounding rules (important):
560
+ - Use ONLY facts that appear in the provided incident data; never invent impact, causes, timing, or status.
561
+ - If something is not known from the data, say it is still being investigated rather than guessing.
562
+ - Never fabricate numbers or specific timings the data does not support.`;
553
563
  }
554
564
  } else if (template) {
555
565
  // Internal note with template
@@ -577,7 +587,12 @@ The note should:
577
587
  4. Be detailed enough to help team members understand the situation
578
588
  5. Use technical language appropriate for the engineering team
579
589
 
580
- Write in markdown format for better readability. Be thorough and technical.`;
590
+ Write in markdown format for better readability. Be thorough and technical.
591
+
592
+ Grounding rules (important):
593
+ - Use ONLY facts that appear in the provided incident data. Do NOT invent root causes, metrics, impact, or actions the data does not support.
594
+ - If something a section needs is not in the data, write "Not available in the incident record" instead of guessing.
595
+ - Never fabricate numbers or state confidence levels.`;
581
596
  }
582
597
 
583
598
  // Build user message
@@ -389,7 +389,12 @@ The postmortem should:
389
389
 
390
390
  Use a standard incident postmortem format with sections for: Executive Summary, Timeline, Root Cause Analysis, Impact, Action Items, and Lessons Learned.
391
391
 
392
- Write in a professional, clear, and concise manner. Use markdown formatting for better readability.`;
392
+ Write in a professional, clear, and concise manner. Use markdown formatting for better readability.
393
+
394
+ Grounding rules (important):
395
+ - Use ONLY facts that appear in the provided episode data. Do NOT invent root causes, timelines, metrics, impact, or actions the data does not support.
396
+ - If a section needs information the data does not contain, write "Not available in the incident record" instead of guessing.
397
+ - Never fabricate numbers or state confidence levels.`;
393
398
  }
394
399
 
395
400
  // Build user message based on whether template is provided
@@ -288,7 +288,12 @@ DO NOT include:
288
288
  - Confidential information
289
289
  - Excessive jargon
290
290
 
291
- Write in markdown format for better readability.`;
291
+ Write in markdown format for better readability.
292
+
293
+ Grounding rules (important):
294
+ - Use ONLY facts that appear in the provided maintenance data; never invent scope, impact, timing, or status.
295
+ - If something is not known from the data, say it will be confirmed rather than guessing.
296
+ - Never fabricate numbers or specific timings the data does not support.`;
292
297
  }
293
298
  } else if (template) {
294
299
  // Internal note with template
@@ -316,7 +321,12 @@ The note should:
316
321
  4. Be detailed enough to help team members understand the current status
317
322
  5. Use technical language appropriate for the engineering team
318
323
 
319
- Write in markdown format for better readability. Be thorough and technical.`;
324
+ Write in markdown format for better readability. Be thorough and technical.
325
+
326
+ Grounding rules (important):
327
+ - Use ONLY facts that appear in the provided maintenance data. Do NOT invent scope, metrics, impact, or actions the data does not support.
328
+ - If something a section needs is not in the data, write "Not available in the maintenance record" instead of guessing.
329
+ - Never fabricate numbers or state confidence levels.`;
320
330
  }
321
331
 
322
332
  // Build user message
@@ -0,0 +1,141 @@
1
+ import ObjectID from "../../../../Types/ObjectID";
2
+ import OneUptimeDate from "../../../../Types/Date";
3
+ import { Blue500 } from "../../../../Types/BrandColors";
4
+ import { AlertFeedEventType } from "../../../../Models/DatabaseModels/AlertFeed";
5
+ import AlertFeedService from "../../../Services/AlertFeedService";
6
+ import AlertAIContextBuilder, {
7
+ AlertContextData,
8
+ } from "../AlertAIContextBuilder";
9
+ import SentinelInvestigationEngine from "./SentinelInvestigationEngine";
10
+ import { ObservabilityAssistantResult } from "../Chat/ObservabilityAssistant";
11
+ import logger from "../../Logger";
12
+ import CaptureSpan from "../../Telemetry/CaptureSpan";
13
+
14
+ /*
15
+ * Sentinel — alert investigation.
16
+ *
17
+ * Alerts are the earlier, "left of boom" signal. When a new alert is declared,
18
+ * Sentinel investigates it (read-only) and posts a cited root-cause analysis to
19
+ * the alert timeline + Slack/Teams. Symmetric to incident investigation and
20
+ * built on the same shared SentinelInvestigationEngine.
21
+ *
22
+ * Gated by the same per-project opt-in as incidents. Alert volume can be higher
23
+ * than incidents, so severity / rate gating is a deliberate follow-up.
24
+ */
25
+ export default class SentinelAlertInvestigationRunner {
26
+ /*
27
+ * Entry point called (fire-and-forget) from AlertService.onCreateSuccess.
28
+ * Never throws — all failures are logged and recorded on the AIRun.
29
+ */
30
+ @CaptureSpan()
31
+ public static async investigateNewAlert(data: {
32
+ alertId: ObjectID;
33
+ projectId: ObjectID;
34
+ }): Promise<void> {
35
+ const { alertId, projectId } = data;
36
+
37
+ try {
38
+ if (
39
+ !(await SentinelInvestigationEngine.isEnabledForProject(
40
+ projectId,
41
+ "Alert",
42
+ ))
43
+ ) {
44
+ return;
45
+ }
46
+
47
+ const contextData: AlertContextData =
48
+ await AlertAIContextBuilder.buildAlertContext({
49
+ alertId,
50
+ });
51
+
52
+ await SentinelInvestigationEngine.investigate({
53
+ projectId,
54
+ feature: "Sentinel Alert Investigation",
55
+ subjectAlertId: alertId,
56
+ contextSummary: this.buildAlertSummary(contextData),
57
+ postAnalysis: async (postData: {
58
+ analysisMarkdown: string;
59
+ isConfident: boolean;
60
+ result: ObservabilityAssistantResult;
61
+ }): Promise<void> => {
62
+ await AlertFeedService.createAlertFeedItem({
63
+ alertId,
64
+ projectId,
65
+ alertFeedEventType: AlertFeedEventType.RootCause,
66
+ displayColor: Blue500,
67
+ feedInfoInMarkdown: postData.analysisMarkdown,
68
+ /*
69
+ * Quiet mode: an inconclusive investigation posts to the timeline
70
+ * but does NOT loudly ping the workspace / on-call.
71
+ */
72
+ workspaceNotification: {
73
+ sendWorkspaceNotification: postData.isConfident,
74
+ },
75
+ });
76
+ },
77
+ });
78
+ } catch (error) {
79
+ logger.error(
80
+ `Sentinel: unexpected error investigating alert ${alertId.toString()}: ${error}`,
81
+ );
82
+ }
83
+ }
84
+
85
+ // Build a compact alert record to seed the investigation.
86
+ private static buildAlertSummary(contextData: AlertContextData): string {
87
+ const { alert, internalNotes } = contextData;
88
+
89
+ const lines: Array<string> = [];
90
+ lines.push("# Alert");
91
+ lines.push(`Title: ${alert.title || "N/A"}`);
92
+
93
+ if (alert.description) {
94
+ lines.push(`Description: ${alert.description}`);
95
+ }
96
+
97
+ lines.push(`Severity: ${alert.alertSeverity?.name || "N/A"}`);
98
+ lines.push(`Current state: ${alert.currentAlertState?.name || "N/A"}`);
99
+ lines.push(
100
+ `Declared at: ${
101
+ alert.createdAt
102
+ ? OneUptimeDate.getDateAsFormattedString(alert.createdAt)
103
+ : "N/A"
104
+ }`,
105
+ );
106
+
107
+ if (alert.monitor?.name) {
108
+ lines.push(`Affected monitor: ${alert.monitor.name}`);
109
+ }
110
+
111
+ if (alert.labels && alert.labels.length > 0) {
112
+ lines.push(
113
+ `Labels: ${alert.labels
114
+ .map((l: { name?: string }) => {
115
+ return l.name;
116
+ })
117
+ .filter(Boolean)
118
+ .join(", ")}`,
119
+ );
120
+ }
121
+
122
+ if (alert.rootCause) {
123
+ lines.push(`Root cause (as recorded so far): ${alert.rootCause}`);
124
+ }
125
+
126
+ const recentNotes: Array<string> = (internalNotes || [])
127
+ .slice(-5)
128
+ .map((note: { note?: string }) => {
129
+ return note.note ? `- ${note.note}` : "";
130
+ })
131
+ .filter(Boolean);
132
+
133
+ if (recentNotes.length > 0) {
134
+ lines.push("");
135
+ lines.push("Recent internal notes:");
136
+ lines.push(...recentNotes);
137
+ }
138
+
139
+ return lines.join("\n");
140
+ }
141
+ }