@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
@@ -0,0 +1,167 @@
1
+ import ObjectID from "../../../../Types/ObjectID";
2
+ import SortOrder from "../../../../Types/BaseDatabase/SortOrder";
3
+ import Incident from "../../../../Models/DatabaseModels/Incident";
4
+ import Monitor from "../../../../Models/DatabaseModels/Monitor";
5
+ import IncidentService from "../../../Services/IncidentService";
6
+ import CaptureSpan from "../../Telemetry/CaptureSpan";
7
+ import logger from "../../Logger";
8
+
9
+ /*
10
+ * Sentinel — episodic memory (Phase 4, "I've seen this before").
11
+ *
12
+ * The first, no-new-storage cut of the recurrence short-circuit: instead of a
13
+ * vector store, retrieve past RESOLVED incidents in the same project that share
14
+ * monitors/labels with the current signal and carry a recorded root cause, then
15
+ * feed a compact digest into the investigation so the agent can recognise a
16
+ * recurrence and reference the prior fix.
17
+ *
18
+ * The full episodic store (pgvector embeddings over postmortems/runbooks + a
19
+ * temporal knowledge graph) is the deferred, larger build — this delivers the
20
+ * user-visible "this matches #123" behaviour on data we already own.
21
+ */
22
+
23
+ // How many recent incidents to consider, and how many prior cases to surface.
24
+ const CANDIDATE_LIMIT: number = 30;
25
+ const MAX_CASES: number = 4;
26
+ const MAX_ROOT_CAUSE_CHARS: number = 300;
27
+
28
+ const SHARED_MONITOR_SCORE: number = 2;
29
+ const SHARED_LABEL_SCORE: number = 1;
30
+
31
+ export default class SentinelMemory {
32
+ /*
33
+ * Returns a markdown "past resolved incidents" section to append to the
34
+ * investigation context, or an empty string when there is nothing useful.
35
+ * Never throws — memory is a nice-to-have, not load-bearing.
36
+ */
37
+ @CaptureSpan()
38
+ public static async getPriorSimilarIncidentsContext(data: {
39
+ projectId: ObjectID;
40
+ currentIncidentId: ObjectID;
41
+ monitorNames: Array<string>;
42
+ labelNames: Array<string>;
43
+ }): Promise<string> {
44
+ try {
45
+ const candidates: Array<Incident> = await IncidentService.findBy({
46
+ query: {
47
+ projectId: data.projectId,
48
+ },
49
+ select: {
50
+ _id: true,
51
+ incidentNumber: true,
52
+ title: true,
53
+ rootCause: true,
54
+ createdAt: true,
55
+ currentIncidentState: {
56
+ isResolvedState: true,
57
+ },
58
+ monitors: {
59
+ name: true,
60
+ },
61
+ labels: {
62
+ name: true,
63
+ },
64
+ },
65
+ sort: {
66
+ createdAt: SortOrder.Descending,
67
+ },
68
+ limit: CANDIDATE_LIMIT,
69
+ skip: 0,
70
+ props: { isRoot: true },
71
+ });
72
+
73
+ const currentIdString: string = data.currentIncidentId.toString();
74
+ const monitorSet: Set<string> = new Set(
75
+ data.monitorNames.map((n: string) => {
76
+ return n.toLowerCase();
77
+ }),
78
+ );
79
+ const labelSet: Set<string> = new Set(
80
+ data.labelNames.map((n: string) => {
81
+ return n.toLowerCase();
82
+ }),
83
+ );
84
+
85
+ const scored: Array<{ incident: Incident; score: number }> = candidates
86
+ .filter((incident: Incident) => {
87
+ return (
88
+ incident.id?.toString() !== currentIdString &&
89
+ incident.currentIncidentState?.isResolvedState === true &&
90
+ Boolean(incident.rootCause && incident.rootCause.trim().length > 0)
91
+ );
92
+ })
93
+ .map((incident: Incident) => {
94
+ let score: number = 0;
95
+
96
+ for (const monitor of incident.monitors || []) {
97
+ if (monitor.name && monitorSet.has(monitor.name.toLowerCase())) {
98
+ score += SHARED_MONITOR_SCORE;
99
+ }
100
+ }
101
+
102
+ for (const label of incident.labels || []) {
103
+ if (label.name && labelSet.has(label.name.toLowerCase())) {
104
+ score += SHARED_LABEL_SCORE;
105
+ }
106
+ }
107
+
108
+ return { incident, score };
109
+ });
110
+
111
+ /*
112
+ * Prefer cases that actually share a monitor/label; otherwise fall back to
113
+ * the most recent resolved incidents (V8's sort is stable, keeping recency).
114
+ */
115
+ const relevant: Array<{ incident: Incident; score: number }> = scored
116
+ .filter((item: { incident: Incident; score: number }) => {
117
+ return item.score > 0;
118
+ })
119
+ .sort(
120
+ (
121
+ a: { incident: Incident; score: number },
122
+ b: { incident: Incident; score: number },
123
+ ) => {
124
+ return b.score - a.score;
125
+ },
126
+ );
127
+
128
+ const chosen: Array<{ incident: Incident; score: number }> = (
129
+ relevant.length > 0 ? relevant : scored
130
+ ).slice(0, MAX_CASES);
131
+
132
+ if (chosen.length === 0) {
133
+ return "";
134
+ }
135
+
136
+ let markdown: string =
137
+ "\n\n# Past resolved incidents in this project (recurrence context)\n" +
138
+ "If the current signal matches one of these, this may be a recurrence — say so explicitly and reference the incident number and how it was resolved.\n";
139
+
140
+ for (const { incident } of chosen) {
141
+ const rootCause: string = (incident.rootCause || "").trim();
142
+ const shortRootCause: string =
143
+ rootCause.length > MAX_ROOT_CAUSE_CHARS
144
+ ? `${rootCause.substring(0, MAX_ROOT_CAUSE_CHARS)}…`
145
+ : rootCause;
146
+
147
+ markdown += `\n- **#${incident.incidentNumber} — ${incident.title || "Untitled"}**`;
148
+ markdown += `\n Previously resolved. Root cause: ${shortRootCause}`;
149
+
150
+ const monitorNames: Array<string> = (incident.monitors || [])
151
+ .map((m: Monitor) => {
152
+ return m.name || "";
153
+ })
154
+ .filter(Boolean);
155
+
156
+ if (monitorNames.length > 0) {
157
+ markdown += `\n Monitors: ${monitorNames.join(", ")}`;
158
+ }
159
+ }
160
+
161
+ return markdown;
162
+ } catch (error) {
163
+ logger.error(`Sentinel: failed to load recurrence context: ${error}`);
164
+ return "";
165
+ }
166
+ }
167
+ }
@@ -35,7 +35,7 @@ const resolveReadPermissions: () => Array<Permission> =
35
35
  export const QueryIncidentsTool: ObservabilityTool = {
36
36
  name: "query_incidents",
37
37
  description:
38
- "Query incidents in this project. Returns the most recent incidents with their current state and severity. Pass incidentId to get full details of one incident.",
38
+ "Query incidents in this project. Returns the most recent incidents with their current state and severity. Pass incidentId to get full details of one incident — including its affected monitors, labels, root cause, remediation and postmortem notes, and the incident window (telemetryWindowStart) to pivot into logs/traces/metrics for the affected services.",
39
39
  inputSchema: {
40
40
  type: "object",
41
41
  properties: {
@@ -81,10 +81,44 @@ export const QueryIncidentsTool: ObservabilityTool = {
81
81
  incidentSeverity: {
82
82
  name: true,
83
83
  },
84
+ monitors: {
85
+ _id: true,
86
+ name: true,
87
+ },
88
+ labels: {
89
+ name: true,
90
+ },
91
+ rootCause: true,
92
+ remediationNotes: true,
93
+ postmortemNote: true,
84
94
  },
85
95
  props: ctx.props,
86
96
  });
87
97
 
98
+ /*
99
+ * Surface the incident's blast radius (affected monitors + labels) and
100
+ * its own window so the model can pivot from an incident into the
101
+ * logs/traces/metrics of the affected services over the right time
102
+ * range, instead of guessing which service or hour to look at.
103
+ */
104
+ const affectedMonitors: string = (incident?.monitors || [])
105
+ .map((monitor: { name?: string }) => {
106
+ return monitor.name || "";
107
+ })
108
+ .filter((value: string) => {
109
+ return value.length > 0;
110
+ })
111
+ .join(", ");
112
+
113
+ const incidentLabels: string = (incident?.labels || [])
114
+ .map((label: { name?: string }) => {
115
+ return label.name || "";
116
+ })
117
+ .filter((value: string) => {
118
+ return value.length > 0;
119
+ })
120
+ .join(", ");
121
+
88
122
  const rows: Array<JSONObject> = incident
89
123
  ? [
90
124
  {
@@ -95,6 +129,12 @@ export const QueryIncidentsTool: ObservabilityTool = {
95
129
  state: incident.currentIncidentState?.name,
96
130
  severity: incident.incidentSeverity?.name,
97
131
  createdAt: incident.createdAt,
132
+ affectedMonitors: affectedMonitors || undefined,
133
+ labels: incidentLabels || undefined,
134
+ rootCause: incident.rootCause,
135
+ remediationNotes: incident.remediationNotes,
136
+ postmortemNote: incident.postmortemNote,
137
+ telemetryWindowStart: incident.createdAt,
98
138
  },
99
139
  ]
100
140
  : [];
@@ -202,3 +242,145 @@ export const QueryIncidentsTool: ObservabilityTool = {
202
242
  };
203
243
  },
204
244
  };
245
+
246
+ export const SearchIncidentsTool: ObservabilityTool = {
247
+ name: "search_incidents",
248
+ description:
249
+ "Search PAST incidents by free text across their title, description, root cause, remediation notes and postmortem. Use this to ground root-cause analysis in history — e.g. 'have we seen this error before, and how was it resolved?'. Returns matching incidents (most recent first) with their root cause and postmortem so you can reuse prior resolutions.",
250
+ inputSchema: {
251
+ type: "object",
252
+ properties: {
253
+ searchText: {
254
+ type: "string",
255
+ description:
256
+ "Text to match (case-insensitive substring) across incident title, description, root cause, remediation notes and postmortem.",
257
+ },
258
+ createdWithinHours: {
259
+ type: "number",
260
+ description:
261
+ "Only incidents created within this many hours (default 2160 = 90 days, max 8760 = 1 year).",
262
+ },
263
+ limit: {
264
+ type: "number",
265
+ description: "Maximum incidents to return (default 5, max 15).",
266
+ },
267
+ },
268
+ required: ["searchText"],
269
+ },
270
+ get requiredPermissions(): Array<Permission> {
271
+ return resolveReadPermissions();
272
+ },
273
+ execute: async (
274
+ args: JSONObject,
275
+ ctx: ToolContext,
276
+ ): Promise<ToolExecutionResult> => {
277
+ const searchText: string | undefined = ToolArgs.getString(
278
+ args,
279
+ "searchText",
280
+ );
281
+
282
+ if (!searchText) {
283
+ return {
284
+ dataForLlm:
285
+ "Error: searchText is required. Provide the text to search past incidents for.",
286
+ rowCount: 0,
287
+ citationLabel: "Incident search (no query)",
288
+ redactionCount: 0,
289
+ isTruncated: false,
290
+ };
291
+ }
292
+
293
+ const createdWithinHours: number = ToolArgs.getNumber(
294
+ args,
295
+ "createdWithinHours",
296
+ { defaultValue: 2160, min: 1, max: 8760 },
297
+ );
298
+ const limit: number = ToolArgs.getNumber(args, "limit", {
299
+ defaultValue: 5,
300
+ min: 1,
301
+ max: 15,
302
+ });
303
+
304
+ const endTime: Date = OneUptimeDate.getCurrentDate();
305
+ const startTime: Date = OneUptimeDate.addRemoveHours(
306
+ endTime,
307
+ -1 * createdWithinHours,
308
+ );
309
+
310
+ const incidents: Array<Incident> = await IncidentService.findBy({
311
+ query: {
312
+ createdAt: QueryHelper.inBetween(startTime, endTime),
313
+ /*
314
+ * The OR-joined ILIKE Raw references the listed columns, not the
315
+ * attached property — this is the established multiSearch attachment
316
+ * pattern (see QueryUtil).
317
+ */
318
+ title: QueryHelper.multiSearch(
319
+ [
320
+ "title",
321
+ "description",
322
+ "rootCause",
323
+ "remediationNotes",
324
+ "postmortemNote",
325
+ ],
326
+ searchText,
327
+ ),
328
+ },
329
+ select: {
330
+ _id: true,
331
+ title: true,
332
+ incidentNumber: true,
333
+ createdAt: true,
334
+ currentIncidentState: {
335
+ name: true,
336
+ },
337
+ incidentSeverity: {
338
+ name: true,
339
+ },
340
+ rootCause: true,
341
+ postmortemNote: true,
342
+ },
343
+ sort: {
344
+ createdAt: SortOrder.Descending,
345
+ },
346
+ limit: limit,
347
+ skip: 0,
348
+ props: ctx.props,
349
+ });
350
+
351
+ const rows: Array<JSONObject> = incidents.map((incident: Incident) => {
352
+ return {
353
+ id: incident.id?.toString(),
354
+ incidentNumber: incident.incidentNumber,
355
+ title: incident.title,
356
+ state: incident.currentIncidentState?.name,
357
+ severity: incident.incidentSeverity?.name,
358
+ createdAt: incident.createdAt,
359
+ rootCause: incident.rootCause,
360
+ postmortemNote: incident.postmortemNote,
361
+ };
362
+ });
363
+
364
+ const serialized: SerializedResult =
365
+ ToolResultSerializer.serializeRows(rows);
366
+
367
+ return {
368
+ dataForLlm: serialized.text,
369
+ rowCount: serialized.rowCount,
370
+ citationLabel: `Incident search "${searchText}" (${serialized.rowCount} found)`,
371
+ citationTarget: {
372
+ type: AIChatCitationTargetType.Incidents,
373
+ },
374
+ redactionCount: serialized.redactionCount,
375
+ isTruncated: serialized.isTruncated,
376
+ widget:
377
+ rows.length > 0
378
+ ? WidgetBuilder.incidentList({
379
+ title: `Incidents matching "${searchText}" (${rows.length})`,
380
+ items: rows,
381
+ link: { type: AIChatCitationTargetType.Incidents },
382
+ })
383
+ : undefined,
384
+ };
385
+ },
386
+ };
@@ -13,11 +13,12 @@ import {
13
13
  ToolContext,
14
14
  ToolExecutionResult,
15
15
  } from "./ToolTypes";
16
- import { QueryIncidentsTool } from "./IncidentTools";
16
+ import { QueryIncidentsTool, SearchIncidentsTool } from "./IncidentTools";
17
17
  import { QueryAlertsTool } from "./AlertTools";
18
18
  import { QueryMonitorsTool } from "./MonitorTools";
19
19
  import { TopExceptionsTool } from "./ExceptionTools";
20
20
  import { LogHistogramTool, SearchLogsTool } from "./LogTools";
21
+ import { RecentChangesTool } from "./RecentChangesTools";
21
22
  import { QueryMetricsTool } from "./MetricTools";
22
23
  import { GetTraceTool, QueryTracesTool } from "./TraceTools";
23
24
  import { LookupContextTool } from "./ContextTools";
@@ -27,6 +28,12 @@ import {
27
28
  ResolveIncidentTool,
28
29
  } from "./IncidentWriteTools";
29
30
  import { AcknowledgeAlertTool, ResolveAlertTool } from "./AlertWriteTools";
31
+ import {
32
+ PageOnCallPolicyTool,
33
+ RunRunbookTool,
34
+ PostIncidentStatusUpdateTool,
35
+ ChangeIncidentSeverityTool,
36
+ } from "./SentinelActionTools";
30
37
  import AIChatPermissionMode from "../../../../Types/AI/AIChatPermissionMode";
31
38
 
32
39
  export interface ToolCallOutcome {
@@ -53,6 +60,7 @@ export default class AIToolbox {
53
60
  private static readonly tools: Array<ObservabilityTool> = [
54
61
  LookupContextTool,
55
62
  QueryIncidentsTool,
63
+ SearchIncidentsTool,
56
64
  QueryAlertsTool,
57
65
  QueryMonitorsTool,
58
66
  TopExceptionsTool,
@@ -61,12 +69,18 @@ export default class AIToolbox {
61
69
  QueryMetricsTool,
62
70
  QueryTracesTool,
63
71
  GetTraceTool,
72
+ RecentChangesTool,
64
73
  // Write tools (mutations). Gated by conversation permission mode.
65
74
  CreateIncidentTool,
66
75
  AcknowledgeIncidentTool,
67
76
  ResolveIncidentTool,
68
77
  AcknowledgeAlertTool,
69
78
  ResolveAlertTool,
79
+ // Sentinel action belt (Phase 0) — operate the platform, not just answer.
80
+ PageOnCallPolicyTool,
81
+ RunRunbookTool,
82
+ PostIncidentStatusUpdateTool,
83
+ ChangeIncidentSeverityTool,
70
84
  ];
71
85
 
72
86
  public static getTools(): Array<ObservabilityTool> {
@@ -0,0 +1,208 @@
1
+ import Monitor from "../../../../Models/DatabaseModels/Monitor";
2
+ import MonitorStatusTimeline from "../../../../Models/DatabaseModels/MonitorStatusTimeline";
3
+ import ScheduledMaintenance from "../../../../Models/DatabaseModels/ScheduledMaintenance";
4
+ import TelemetryException from "../../../../Models/DatabaseModels/TelemetryException";
5
+ import { JSONObject } from "../../../../Types/JSON";
6
+ import Permission from "../../../../Types/Permission";
7
+ import SortOrder from "../../../../Types/BaseDatabase/SortOrder";
8
+ import MonitorStatusTimelineService from "../../../Services/MonitorStatusTimelineService";
9
+ import ScheduledMaintenanceService from "../../../Services/ScheduledMaintenanceService";
10
+ import TelemetryExceptionService from "../../../Services/TelemetryExceptionService";
11
+ import QueryHelper from "../../../Types/Database/QueryHelper";
12
+ import logger from "../../Logger";
13
+ import ToolResultSerializer, { SerializedResult } from "./Serializer";
14
+ import {
15
+ ObservabilityTool,
16
+ ToolArgs,
17
+ ToolContext,
18
+ ToolExecutionResult,
19
+ TimeRangeSchemaProperties,
20
+ } from "./ToolTypes";
21
+
22
+ /*
23
+ * Gate on Monitor read permissions. This is defense-in-depth only: every
24
+ * source query below runs under the requesting user's props, so a user who
25
+ * cannot see monitors / exceptions / maintenance simply gets no rows from that
26
+ * source — never another user's data. Resolved lazily to avoid the
27
+ * import-time circular-dependency TypeError other tools document.
28
+ */
29
+ let cachedReadPermissions: Array<Permission> | null = null;
30
+ const resolveReadPermissions: () => Array<Permission> =
31
+ (): Array<Permission> => {
32
+ if (!cachedReadPermissions) {
33
+ cachedReadPermissions = new Monitor().getReadPermissions();
34
+ }
35
+ return cachedReadPermissions;
36
+ };
37
+
38
+ // One normalized entry in the merged "what changed" feed.
39
+ interface ChangeEvent {
40
+ at: Date;
41
+ change: string;
42
+ detail: string;
43
+ }
44
+
45
+ export const RecentChangesTool: ObservabilityTool = {
46
+ name: "recent_changes",
47
+ description:
48
+ "Get a single chronological feed of what changed in a time window: newly-seen exceptions, monitor status changes (up/down), and scheduled maintenance overlapping the window. This is the highest-yield way to answer 'what changed right before this started?' during root-cause analysis — call it with the window around when a problem began.",
49
+ inputSchema: {
50
+ type: "object",
51
+ properties: {
52
+ ...TimeRangeSchemaProperties,
53
+ limitPerSource: {
54
+ type: "number",
55
+ description:
56
+ "Maximum items to pull from each source (exceptions, monitor changes, maintenance) before merging. Default 15, max 30.",
57
+ },
58
+ },
59
+ },
60
+ get requiredPermissions(): Array<Permission> {
61
+ return resolveReadPermissions();
62
+ },
63
+ execute: async (
64
+ args: JSONObject,
65
+ ctx: ToolContext,
66
+ ): Promise<ToolExecutionResult> => {
67
+ const timeRange: { startTime: Date; endTime: Date } = ToolArgs.getTimeRange(
68
+ args,
69
+ { defaultHours: 24, maxDays: 30 },
70
+ );
71
+ const startTime: Date = timeRange.startTime;
72
+ const endTime: Date = timeRange.endTime;
73
+
74
+ const limitPerSource: number = ToolArgs.getNumber(args, "limitPerSource", {
75
+ defaultValue: 15,
76
+ min: 1,
77
+ max: 30,
78
+ });
79
+
80
+ const events: Array<ChangeEvent> = [];
81
+
82
+ // 1) Newly-seen exceptions — their first occurrence falls inside the window.
83
+ try {
84
+ const exceptions: Array<TelemetryException> =
85
+ await TelemetryExceptionService.findBy({
86
+ query: {
87
+ firstSeenAt: QueryHelper.inBetween(startTime, endTime),
88
+ } as never,
89
+ select: {
90
+ message: true,
91
+ exceptionType: true,
92
+ firstSeenAt: true,
93
+ occuranceCount: true,
94
+ },
95
+ sort: { firstSeenAt: SortOrder.Descending },
96
+ limit: limitPerSource,
97
+ skip: 0,
98
+ props: ctx.props,
99
+ });
100
+
101
+ for (const exception of exceptions) {
102
+ if (!exception.firstSeenAt) {
103
+ continue;
104
+ }
105
+ events.push({
106
+ at: exception.firstSeenAt,
107
+ change: "new_exception",
108
+ detail: `${exception.exceptionType || "Exception"}: ${exception.message || ""} (x${exception.occuranceCount ?? 0})`,
109
+ });
110
+ }
111
+ } catch (error) {
112
+ logger.debug(`recent_changes: exceptions source skipped: ${error}`);
113
+ }
114
+
115
+ // 2) Monitor status changes (up/down) inside the window.
116
+ try {
117
+ const statusChanges: Array<MonitorStatusTimeline> =
118
+ await MonitorStatusTimelineService.findBy({
119
+ query: {
120
+ createdAt: QueryHelper.inBetween(startTime, endTime),
121
+ },
122
+ select: {
123
+ createdAt: true,
124
+ monitor: { name: true },
125
+ monitorStatus: { name: true },
126
+ },
127
+ sort: { createdAt: SortOrder.Descending },
128
+ limit: limitPerSource,
129
+ skip: 0,
130
+ props: ctx.props,
131
+ });
132
+
133
+ for (const statusChange of statusChanges) {
134
+ if (!statusChange.createdAt) {
135
+ continue;
136
+ }
137
+ events.push({
138
+ at: statusChange.createdAt,
139
+ change: "monitor_status_change",
140
+ detail: `${statusChange.monitor?.name || "Monitor"} → ${statusChange.monitorStatus?.name || "unknown"}`,
141
+ });
142
+ }
143
+ } catch (error) {
144
+ logger.debug(`recent_changes: monitor source skipped: ${error}`);
145
+ }
146
+
147
+ /*
148
+ * 3) Scheduled maintenance overlapping the window (started before it ended,
149
+ * ends after it started).
150
+ */
151
+ try {
152
+ const maintenanceEvents: Array<ScheduledMaintenance> =
153
+ await ScheduledMaintenanceService.findBy({
154
+ query: {
155
+ startsAt: QueryHelper.lessThanEqualTo(endTime),
156
+ endsAt: QueryHelper.greaterThanEqualTo(startTime),
157
+ },
158
+ select: {
159
+ title: true,
160
+ startsAt: true,
161
+ endsAt: true,
162
+ currentScheduledMaintenanceState: { name: true },
163
+ },
164
+ sort: { startsAt: SortOrder.Descending },
165
+ limit: limitPerSource,
166
+ skip: 0,
167
+ props: ctx.props,
168
+ });
169
+
170
+ for (const maintenance of maintenanceEvents) {
171
+ if (!maintenance.startsAt) {
172
+ continue;
173
+ }
174
+ events.push({
175
+ at: maintenance.startsAt,
176
+ change: "scheduled_maintenance",
177
+ detail: `${maintenance.title || "Maintenance"} (${maintenance.currentScheduledMaintenanceState?.name || "scheduled"})`,
178
+ });
179
+ }
180
+ } catch (error) {
181
+ logger.debug(`recent_changes: maintenance source skipped: ${error}`);
182
+ }
183
+
184
+ // Merge into one chronological feed, most recent first.
185
+ events.sort((a: ChangeEvent, b: ChangeEvent) => {
186
+ return b.at.getTime() - a.at.getTime();
187
+ });
188
+
189
+ const rows: Array<JSONObject> = events.map((event: ChangeEvent) => {
190
+ return {
191
+ at: event.at,
192
+ change: event.change,
193
+ detail: event.detail,
194
+ };
195
+ });
196
+
197
+ const serialized: SerializedResult =
198
+ ToolResultSerializer.serializeRows(rows);
199
+
200
+ return {
201
+ dataForLlm: serialized.text,
202
+ rowCount: serialized.rowCount,
203
+ citationLabel: `Changes ${startTime.toISOString()} → ${endTime.toISOString()} (${serialized.rowCount} events)`,
204
+ redactionCount: serialized.redactionCount,
205
+ isTruncated: serialized.isTruncated,
206
+ };
207
+ },
208
+ };