@oneuptime/common 11.3.25 → 11.3.28

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 (397) hide show
  1. package/Models/DatabaseModels/AIConversation.ts +322 -0
  2. package/Models/DatabaseModels/AIConversationMessage.ts +495 -0
  3. package/Models/DatabaseModels/AIRun.ts +584 -0
  4. package/Models/DatabaseModels/AIRunEvent.ts +443 -0
  5. package/Models/DatabaseModels/IncidentEpisodeRoleMember.ts +1 -1
  6. package/Models/DatabaseModels/IncidentMember.ts +1 -1
  7. package/Models/DatabaseModels/IncomingCallPolicy.ts +13 -0
  8. package/Models/DatabaseModels/Index.ts +11 -0
  9. package/Models/DatabaseModels/LlmLog.ts +26 -0
  10. package/Models/DatabaseModels/LlmProvider.ts +2 -2
  11. package/Models/DatabaseModels/MigrationFailure.ts +170 -0
  12. package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +51 -2
  13. package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +44 -0
  14. package/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.ts +6 -0
  15. package/Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser.ts +6 -0
  16. package/Models/DatabaseModels/OnCallDutyPolicyTimeLog.ts +1 -1
  17. package/Models/DatabaseModels/OnCallDutyPolicyUserOverride.ts +2 -2
  18. package/Server/API/AIChatAPI.ts +693 -0
  19. package/Server/API/AlertAPI.ts +1 -1
  20. package/Server/API/IncidentAPI.ts +2 -2
  21. package/Server/API/IncidentEpisodeAPI.ts +1 -1
  22. package/Server/API/LlmProviderAPI.ts +170 -0
  23. package/Server/API/MicrosoftTeamsAPI.ts +5 -0
  24. package/Server/API/ScheduledMaintenanceAPI.ts +1 -1
  25. package/Server/API/SlackAPI.ts +663 -0
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1783363279075-AddAIChatModels.ts +245 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/1783443471795-AddLlmProviderToAIConversation.ts +36 -0
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/1783453297388-AddAIChatWriteActionsAndWidgets.ts +46 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1783461767405-MigrationName.ts +119 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1783470000000-AddIncomingCallPolicyRoutingPhoneNumberUnique.ts +25 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/1783510935686-FixNotNullForeignKeysOnDelete.ts +81 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/1783515836148-MigrationName.ts +25 -0
  33. package/Server/Infrastructure/Postgres/SchemaMigrations/1783523076215-AddMigrationFailureTable.ts +35 -0
  34. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +16 -0
  35. package/Server/Infrastructure/PostgresDatabase.ts +11 -0
  36. package/Server/Services/AIConversationMessageService.ts +39 -0
  37. package/Server/Services/AIConversationService.ts +63 -0
  38. package/Server/Services/AIRunEventService.ts +39 -0
  39. package/Server/Services/AIRunService.ts +39 -0
  40. package/Server/Services/AIService.ts +127 -33
  41. package/Server/Services/AnalyticsDatabaseService.ts +10 -1
  42. package/Server/Services/DatabaseService.ts +29 -1
  43. package/Server/Services/IncidentService.ts +1 -1
  44. package/Server/Services/IncomingCallPolicyEscalationRuleService.ts +75 -2
  45. package/Server/Services/IncomingCallPolicyService.ts +42 -1
  46. package/Server/Services/Index.ts +2 -0
  47. package/Server/Services/LlmProviderService.ts +110 -0
  48. package/Server/Services/MigrationFailureService.ts +9 -0
  49. package/Server/Services/MonitorProbeService.ts +33 -7
  50. package/Server/Services/MonitorService.ts +19 -7
  51. package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +65 -0
  52. package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +185 -41
  53. package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +23 -15
  54. package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +39 -0
  55. package/Server/Services/OnCallDutyPolicyScheduleLayerService.ts +148 -0
  56. package/Server/Services/OnCallDutyPolicyScheduleLayerUserService.ts +11 -2
  57. package/Server/Services/OnCallDutyPolicyScheduleService.ts +502 -26
  58. package/Server/Services/OnCallDutyPolicyTimeLogService.ts +49 -7
  59. package/Server/Services/OnCallDutyPolicyUserOverrideService.ts +218 -4
  60. package/Server/Services/ProjectCallSMSConfigService.ts +82 -1
  61. package/Server/Services/ProjectService.ts +24 -0
  62. package/Server/Services/TeamMemberService.ts +6 -0
  63. package/Server/Services/UserNotificationRuleService.ts +13 -42
  64. package/Server/Services/UserOnCallLogService.ts +90 -0
  65. package/Server/Services/UserOnCallLogTimelineService.ts +43 -1
  66. package/Server/Types/AnalyticsDatabase/ModelPermission.ts +74 -9
  67. package/Server/Types/Database/QueryUtil.ts +57 -0
  68. package/Server/Utils/AI/AIChatPrivacyFilter.ts +28 -0
  69. package/Server/Utils/AI/AlertAIContextBuilder.ts +6 -1
  70. package/Server/Utils/AI/Chat/ChatAgentRunner.ts +1066 -0
  71. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +239 -0
  72. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +51 -0
  73. package/Server/Utils/AI/IncidentAIContextBuilder.ts +18 -3
  74. package/Server/Utils/AI/IncidentEpisodeAIContextBuilder.ts +6 -1
  75. package/Server/Utils/AI/ScheduledMaintenanceAIContextBuilder.ts +12 -2
  76. package/Server/Utils/AI/Toolbox/AlertTools.ts +201 -0
  77. package/Server/Utils/AI/Toolbox/AlertWriteTools.ts +174 -0
  78. package/Server/Utils/AI/Toolbox/ContextTools.ts +189 -0
  79. package/Server/Utils/AI/Toolbox/ExceptionTools.ts +149 -0
  80. package/Server/Utils/AI/Toolbox/IncidentTools.ts +386 -0
  81. package/Server/Utils/AI/Toolbox/IncidentWriteTools.ts +350 -0
  82. package/Server/Utils/AI/Toolbox/Index.ts +231 -0
  83. package/Server/Utils/AI/Toolbox/LogTools.ts +339 -0
  84. package/Server/Utils/AI/Toolbox/MetricTools.ts +179 -0
  85. package/Server/Utils/AI/Toolbox/MonitorTools.ts +193 -0
  86. package/Server/Utils/AI/Toolbox/RecentChangesTools.ts +208 -0
  87. package/Server/Utils/AI/Toolbox/Serializer.ts +299 -0
  88. package/Server/Utils/AI/Toolbox/ToolTypes.ts +249 -0
  89. package/Server/Utils/AI/Toolbox/TraceTools.ts +402 -0
  90. package/Server/Utils/AI/Toolbox/WidgetBuilder.ts +189 -0
  91. package/Server/Utils/Database/MigrationFailureLog.ts +240 -0
  92. package/Server/Utils/IncomingCallPhoneNumber.ts +41 -0
  93. package/Server/Utils/LLM/LLMService.ts +605 -96
  94. package/Server/Utils/Telemetry/IoTSnapshotScan.ts +12 -9
  95. package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +12 -9
  96. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +512 -19
  97. package/Server/Utils/Workspace/Slack/Slack.ts +80 -0
  98. package/Server/Utils/Workspace/Slack/app-manifest.json +16 -1
  99. package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +0 -8
  100. package/Tests/Server/Services/OnCallDutyPolicyScheduleLayerUserReorder.test.ts +233 -0
  101. package/Tests/Server/Services/OnCallDutyPolicyTimeLogServiceScoping.test.ts +242 -0
  102. package/Tests/Server/Services/OnCallDutyPolicyUserOverrideEdit.test.ts +198 -0
  103. package/Tests/Server/Services/UserOnCallLogClaimNotificationRule.test.ts +495 -0
  104. package/Tests/Server/Types/Database/QueryUtil.test.ts +74 -0
  105. package/Tests/Server/Utils/AI/AIChatModelACL.test.ts +42 -0
  106. package/Tests/Server/Utils/AI/ChatAgentHelpers.test.ts +89 -0
  107. package/Tests/Server/Utils/AI/LLMServiceBaseUrl.test.ts +139 -0
  108. package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +386 -0
  109. package/Tests/Server/Utils/AI/ToolArgsGetTimeRange.test.ts +62 -0
  110. package/Tests/Server/Utils/AI/ToolArgsScopeServiceIds.test.ts +79 -0
  111. package/Tests/Server/Utils/AI/ToolResultSerializer.test.ts +223 -0
  112. package/Tests/Types/Billing/SubscriptionStatus.test.ts +127 -0
  113. package/Tests/Types/DateExhaustiveTimezone.test.ts +700 -0
  114. package/Tests/Types/DateTimezoneWallClock.test.ts +99 -0
  115. package/Tests/Types/Events/Recurring.test.ts +22 -9
  116. package/Tests/Types/Metrics/RecordingRuleDefinition.test.ts +213 -0
  117. package/Tests/Types/OnCallDutyPolicy/LayerUtilAuditFixes.test.ts +291 -0
  118. package/Tests/Types/OnCallDutyPolicy/LayerUtilAuditFixesRound2.test.ts +398 -0
  119. package/Tests/Types/OnCallDutyPolicy/LayerUtilDSTWeekendGap.test.ts +114 -0
  120. package/Tests/Types/OnCallDutyPolicy/LayerUtilDSTWeeklyDayShift.test.ts +76 -0
  121. package/Tests/Types/OnCallDutyPolicy/LayerUtilDailyMutation.test.ts +128 -0
  122. package/Tests/Types/OnCallDutyPolicy/LayerUtilDailyProbe.test.ts +203 -0
  123. package/Tests/Types/OnCallDutyPolicy/LayerUtilDiffFuzz.test.ts +322 -0
  124. package/Tests/Types/OnCallDutyPolicy/LayerUtilEdgeCases.test.ts +280 -0
  125. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveCurrentUser.test.ts +863 -0
  126. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveDaily.test.ts +900 -0
  127. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveMultiLayer.test.ts +1104 -0
  128. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveRotation.test.ts +957 -0
  129. package/Tests/Types/OnCallDutyPolicy/LayerUtilExhaustiveWeekly.test.ts +1095 -0
  130. package/Tests/Types/OnCallDutyPolicy/LayerUtilInvariants.test.ts +232 -0
  131. package/Tests/Types/OnCallDutyPolicy/LayerUtilMergeAudit.test.ts +227 -0
  132. package/Tests/Types/OnCallDutyPolicy/LayerUtilMonthYearAudit.test.ts +255 -0
  133. package/Tests/Types/OnCallDutyPolicy/LayerUtilMultiLayerAudit.test.ts +388 -0
  134. package/Tests/Types/OnCallDutyPolicy/LayerUtilMultiLayerEdge.test.ts +300 -0
  135. package/Tests/Types/OnCallDutyPolicy/LayerUtilOvernightDSTAudit.test.ts +158 -0
  136. package/Tests/Types/OnCallDutyPolicy/LayerUtilRestrictedGapFix.test.ts +253 -0
  137. package/Tests/Types/OnCallDutyPolicy/LayerUtilRestrictedGapRepro.test.ts +157 -0
  138. package/Tests/Types/OnCallDutyPolicy/LayerUtilRotationFixes.test.ts +414 -0
  139. package/Tests/Types/OnCallDutyPolicy/LayerUtilTimezone.test.ts +243 -0
  140. package/Tests/Types/OnCallDutyPolicy/LayerUtilWeekendGapRepro.test.ts +112 -0
  141. package/Tests/Types/OnCallDutyPolicy/OverrideLensAudit.test.ts +117 -0
  142. package/Tests/Types/OnCallDutyPolicy/RestrictionTimes.test.ts +8 -6
  143. package/Tests/Types/OnCallDutyPolicy/RestrictionTimesDefaultDayAudit.test.ts +112 -0
  144. package/Tests/Types/OnCallDutyPolicy/RestrictionTimesExhaustive.test.ts +821 -0
  145. package/Tests/Types/OnCallDutyPolicy/RestrictionTimesMutationInvariant.test.ts +886 -0
  146. package/Tests/Types/OnCallDutyPolicy/UserOverrideUtil.test.ts +255 -0
  147. package/Tests/Types/OnCallDutyPolicy/UserOverrideUtilExhaustive.test.ts +1126 -0
  148. package/Types/AI/AIChatMessageRole.ts +6 -0
  149. package/Types/AI/AIChatMessageStatus.ts +32 -0
  150. package/Types/AI/AIChatPermissionMode.ts +69 -0
  151. package/Types/AI/AIChatTypes.ts +231 -0
  152. package/Types/AI/AIRunEventType.ts +17 -0
  153. package/Types/AI/AIRunStatus.ts +28 -0
  154. package/Types/AI/AIRunType.ts +6 -0
  155. package/Types/Date.ts +200 -15
  156. package/Types/LLM/LlmType.ts +6 -0
  157. package/Types/OnCallDutyPolicy/Layer.ts +790 -260
  158. package/Types/OnCallDutyPolicy/RestrictionTimes.ts +39 -13
  159. package/Types/OnCallDutyPolicy/UserOverrideUtil.ts +21 -5
  160. package/UI/Components/Events/RecurringFieldElement.tsx +16 -0
  161. package/UI/Components/Header/HeaderIconDropdownButton.tsx +22 -12
  162. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +35 -3
  163. package/UI/Components/ModelFormModal/ModelFormModal.tsx +8 -0
  164. package/UI/Components/Page/Page.tsx +14 -0
  165. package/UI/Components/RadioButtons/BasicRadioButtons.tsx +1 -1
  166. package/UI/Utils/LlmTypeDropdownOptions.ts +40 -0
  167. package/UI/Utils/TestLLMProvider.ts +59 -0
  168. package/build/dist/Models/DatabaseModels/AIConversation.js +345 -0
  169. package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -0
  170. package/build/dist/Models/DatabaseModels/AIConversationMessage.js +521 -0
  171. package/build/dist/Models/DatabaseModels/AIConversationMessage.js.map +1 -0
  172. package/build/dist/Models/DatabaseModels/AIRun.js +619 -0
  173. package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -0
  174. package/build/dist/Models/DatabaseModels/AIRunEvent.js +469 -0
  175. package/build/dist/Models/DatabaseModels/AIRunEvent.js.map +1 -0
  176. package/build/dist/Models/DatabaseModels/IncidentEpisodeRoleMember.js +1 -1
  177. package/build/dist/Models/DatabaseModels/IncidentEpisodeRoleMember.js.map +1 -1
  178. package/build/dist/Models/DatabaseModels/IncidentMember.js +1 -1
  179. package/build/dist/Models/DatabaseModels/IncidentMember.js.map +1 -1
  180. package/build/dist/Models/DatabaseModels/IncomingCallPolicy.js +10 -0
  181. package/build/dist/Models/DatabaseModels/IncomingCallPolicy.js.map +1 -1
  182. package/build/dist/Models/DatabaseModels/Index.js +10 -0
  183. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  184. package/build/dist/Models/DatabaseModels/LlmLog.js +28 -0
  185. package/build/dist/Models/DatabaseModels/LlmLog.js.map +1 -1
  186. package/build/dist/Models/DatabaseModels/LlmProvider.js +2 -2
  187. package/build/dist/Models/DatabaseModels/LlmProvider.js.map +1 -1
  188. package/build/dist/Models/DatabaseModels/MigrationFailure.js +196 -0
  189. package/build/dist/Models/DatabaseModels/MigrationFailure.js.map +1 -0
  190. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +52 -2
  191. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
  192. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +45 -0
  193. package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
  194. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js +6 -0
  195. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js.map +1 -1
  196. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser.js +6 -0
  197. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayerUser.js.map +1 -1
  198. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyTimeLog.js +1 -1
  199. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyTimeLog.js.map +1 -1
  200. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyUserOverride.js +2 -2
  201. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyUserOverride.js.map +1 -1
  202. package/build/dist/Server/API/AIChatAPI.js +498 -0
  203. package/build/dist/Server/API/AIChatAPI.js.map +1 -0
  204. package/build/dist/Server/API/AlertAPI.js +1 -1
  205. package/build/dist/Server/API/IncidentAPI.js +2 -2
  206. package/build/dist/Server/API/IncidentEpisodeAPI.js +1 -1
  207. package/build/dist/Server/API/LlmProviderAPI.js +108 -1
  208. package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
  209. package/build/dist/Server/API/MicrosoftTeamsAPI.js +4 -0
  210. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  211. package/build/dist/Server/API/ScheduledMaintenanceAPI.js +1 -1
  212. package/build/dist/Server/API/SlackAPI.js +442 -0
  213. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  214. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783363279075-AddAIChatModels.js +96 -0
  215. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783363279075-AddAIChatModels.js.map +1 -0
  216. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783443471795-AddLlmProviderToAIConversation.js +25 -0
  217. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783443471795-AddLlmProviderToAIConversation.js.map +1 -0
  218. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783453297388-AddAIChatWriteActionsAndWidgets.js +31 -0
  219. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783453297388-AddAIChatWriteActionsAndWidgets.js.map +1 -0
  220. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783461767405-MigrationName.js +46 -0
  221. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783461767405-MigrationName.js.map +1 -0
  222. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783470000000-AddIncomingCallPolicyRoutingPhoneNumberUnique.js +18 -0
  223. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783470000000-AddIncomingCallPolicyRoutingPhoneNumberUnique.js.map +1 -0
  224. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783510935686-FixNotNullForeignKeysOnDelete.js +38 -0
  225. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783510935686-FixNotNullForeignKeysOnDelete.js.map +1 -0
  226. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783515836148-MigrationName.js +16 -0
  227. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783515836148-MigrationName.js.map +1 -0
  228. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783523076215-AddMigrationFailureTable.js +18 -0
  229. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783523076215-AddMigrationFailureTable.js.map +1 -0
  230. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +16 -0
  231. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  232. package/build/dist/Server/Infrastructure/PostgresDatabase.js +9 -0
  233. package/build/dist/Server/Infrastructure/PostgresDatabase.js.map +1 -1
  234. package/build/dist/Server/Services/AIConversationMessageService.js +34 -0
  235. package/build/dist/Server/Services/AIConversationMessageService.js.map +1 -0
  236. package/build/dist/Server/Services/AIConversationService.js +42 -0
  237. package/build/dist/Server/Services/AIConversationService.js.map +1 -0
  238. package/build/dist/Server/Services/AIRunEventService.js +34 -0
  239. package/build/dist/Server/Services/AIRunEventService.js.map +1 -0
  240. package/build/dist/Server/Services/AIRunService.js +34 -0
  241. package/build/dist/Server/Services/AIRunService.js.map +1 -0
  242. package/build/dist/Server/Services/AIService.js +84 -25
  243. package/build/dist/Server/Services/AIService.js.map +1 -1
  244. package/build/dist/Server/Services/AnalyticsDatabaseService.js +10 -1
  245. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  246. package/build/dist/Server/Services/DatabaseService.js +20 -1
  247. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  248. package/build/dist/Server/Services/IncidentService.js +1 -1
  249. package/build/dist/Server/Services/IncomingCallPolicyEscalationRuleService.js +55 -2
  250. package/build/dist/Server/Services/IncomingCallPolicyEscalationRuleService.js.map +1 -1
  251. package/build/dist/Server/Services/IncomingCallPolicyService.js +40 -0
  252. package/build/dist/Server/Services/IncomingCallPolicyService.js.map +1 -1
  253. package/build/dist/Server/Services/Index.js +2 -0
  254. package/build/dist/Server/Services/Index.js.map +1 -1
  255. package/build/dist/Server/Services/LlmProviderService.js +108 -0
  256. package/build/dist/Server/Services/LlmProviderService.js.map +1 -1
  257. package/build/dist/Server/Services/MigrationFailureService.js +9 -0
  258. package/build/dist/Server/Services/MigrationFailureService.js.map +1 -0
  259. package/build/dist/Server/Services/MonitorProbeService.js +33 -7
  260. package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
  261. package/build/dist/Server/Services/MonitorService.js +12 -5
  262. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  263. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +81 -22
  264. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
  265. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +134 -31
  266. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
  267. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +21 -13
  268. package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
  269. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +36 -0
  270. package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
  271. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js +121 -0
  272. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerService.js.map +1 -1
  273. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js +11 -2
  274. package/build/dist/Server/Services/OnCallDutyPolicyScheduleLayerUserService.js.map +1 -1
  275. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +409 -36
  276. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  277. package/build/dist/Server/Services/OnCallDutyPolicyTimeLogService.js +52 -8
  278. package/build/dist/Server/Services/OnCallDutyPolicyTimeLogService.js.map +1 -1
  279. package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js +170 -3
  280. package/build/dist/Server/Services/OnCallDutyPolicyUserOverrideService.js.map +1 -1
  281. package/build/dist/Server/Services/ProjectCallSMSConfigService.js +72 -0
  282. package/build/dist/Server/Services/ProjectCallSMSConfigService.js.map +1 -1
  283. package/build/dist/Server/Services/ProjectService.js +25 -0
  284. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  285. package/build/dist/Server/Services/TeamMemberService.js +6 -0
  286. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  287. package/build/dist/Server/Services/UserNotificationRuleService.js +12 -30
  288. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  289. package/build/dist/Server/Services/UserOnCallLogService.js +75 -0
  290. package/build/dist/Server/Services/UserOnCallLogService.js.map +1 -1
  291. package/build/dist/Server/Services/UserOnCallLogTimelineService.js +31 -1
  292. package/build/dist/Server/Services/UserOnCallLogTimelineService.js.map +1 -1
  293. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +50 -7
  294. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
  295. package/build/dist/Server/Types/Database/QueryUtil.js +45 -0
  296. package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
  297. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js +18 -0
  298. package/build/dist/Server/Utils/AI/AIChatPrivacyFilter.js.map +1 -0
  299. package/build/dist/Server/Utils/AI/AlertAIContextBuilder.js +6 -1
  300. package/build/dist/Server/Utils/AI/AlertAIContextBuilder.js.map +1 -1
  301. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +756 -0
  302. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -0
  303. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +165 -0
  304. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -0
  305. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +44 -0
  306. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -0
  307. package/build/dist/Server/Utils/AI/IncidentAIContextBuilder.js +18 -3
  308. package/build/dist/Server/Utils/AI/IncidentAIContextBuilder.js.map +1 -1
  309. package/build/dist/Server/Utils/AI/IncidentEpisodeAIContextBuilder.js +6 -1
  310. package/build/dist/Server/Utils/AI/IncidentEpisodeAIContextBuilder.js.map +1 -1
  311. package/build/dist/Server/Utils/AI/ScheduledMaintenanceAIContextBuilder.js +12 -2
  312. package/build/dist/Server/Utils/AI/ScheduledMaintenanceAIContextBuilder.js.map +1 -1
  313. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +167 -0
  314. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -0
  315. package/build/dist/Server/Utils/AI/Toolbox/AlertWriteTools.js +136 -0
  316. package/build/dist/Server/Utils/AI/Toolbox/AlertWriteTools.js.map +1 -0
  317. package/build/dist/Server/Utils/AI/Toolbox/ContextTools.js +141 -0
  318. package/build/dist/Server/Utils/AI/Toolbox/ContextTools.js.map +1 -0
  319. package/build/dist/Server/Utils/AI/Toolbox/ExceptionTools.js +117 -0
  320. package/build/dist/Server/Utils/AI/Toolbox/ExceptionTools.js.map +1 -0
  321. package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js +318 -0
  322. package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js.map +1 -0
  323. package/build/dist/Server/Utils/AI/Toolbox/IncidentWriteTools.js +280 -0
  324. package/build/dist/Server/Utils/AI/Toolbox/IncidentWriteTools.js.map +1 -0
  325. package/build/dist/Server/Utils/AI/Toolbox/Index.js +153 -0
  326. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -0
  327. package/build/dist/Server/Utils/AI/Toolbox/LogTools.js +246 -0
  328. package/build/dist/Server/Utils/AI/Toolbox/LogTools.js.map +1 -0
  329. package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js +120 -0
  330. package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js.map +1 -0
  331. package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js +158 -0
  332. package/build/dist/Server/Utils/AI/Toolbox/MonitorTools.js.map +1 -0
  333. package/build/dist/Server/Utils/AI/Toolbox/RecentChangesTools.js +165 -0
  334. package/build/dist/Server/Utils/AI/Toolbox/RecentChangesTools.js.map +1 -0
  335. package/build/dist/Server/Utils/AI/Toolbox/Serializer.js +228 -0
  336. package/build/dist/Server/Utils/AI/Toolbox/Serializer.js.map +1 -0
  337. package/build/dist/Server/Utils/AI/Toolbox/ToolTypes.js +142 -0
  338. package/build/dist/Server/Utils/AI/Toolbox/ToolTypes.js.map +1 -0
  339. package/build/dist/Server/Utils/AI/Toolbox/TraceTools.js +309 -0
  340. package/build/dist/Server/Utils/AI/Toolbox/TraceTools.js.map +1 -0
  341. package/build/dist/Server/Utils/AI/Toolbox/WidgetBuilder.js +120 -0
  342. package/build/dist/Server/Utils/AI/Toolbox/WidgetBuilder.js.map +1 -0
  343. package/build/dist/Server/Utils/Database/MigrationFailureLog.js +174 -0
  344. package/build/dist/Server/Utils/Database/MigrationFailureLog.js.map +1 -0
  345. package/build/dist/Server/Utils/IncomingCallPhoneNumber.js +30 -0
  346. package/build/dist/Server/Utils/IncomingCallPhoneNumber.js.map +1 -0
  347. package/build/dist/Server/Utils/LLM/LLMService.js +452 -92
  348. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  349. package/build/dist/Server/Utils/Telemetry/IoTSnapshotScan.js +11 -8
  350. package/build/dist/Server/Utils/Telemetry/IoTSnapshotScan.js.map +1 -1
  351. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +11 -8
  352. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
  353. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +378 -15
  354. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  355. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +59 -0
  356. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  357. package/build/dist/Server/Utils/Workspace/Slack/app-manifest.json +16 -1
  358. package/build/dist/Types/AI/AIChatMessageRole.js +7 -0
  359. package/build/dist/Types/AI/AIChatMessageRole.js.map +1 -0
  360. package/build/dist/Types/AI/AIChatMessageStatus.js +27 -0
  361. package/build/dist/Types/AI/AIChatMessageStatus.js.map +1 -0
  362. package/build/dist/Types/AI/AIChatPermissionMode.js +53 -0
  363. package/build/dist/Types/AI/AIChatPermissionMode.js.map +1 -0
  364. package/build/dist/Types/AI/AIChatTypes.js +74 -0
  365. package/build/dist/Types/AI/AIChatTypes.js.map +1 -0
  366. package/build/dist/Types/AI/AIRunEventType.js +18 -0
  367. package/build/dist/Types/AI/AIRunEventType.js.map +1 -0
  368. package/build/dist/Types/AI/AIRunStatus.js +26 -0
  369. package/build/dist/Types/AI/AIRunStatus.js.map +1 -0
  370. package/build/dist/Types/AI/AIRunType.js +7 -0
  371. package/build/dist/Types/AI/AIRunType.js.map +1 -0
  372. package/build/dist/Types/Date.js +153 -15
  373. package/build/dist/Types/Date.js.map +1 -1
  374. package/build/dist/Types/LLM/LlmType.js +6 -0
  375. package/build/dist/Types/LLM/LlmType.js.map +1 -1
  376. package/build/dist/Types/OnCallDutyPolicy/Layer.js +602 -171
  377. package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
  378. package/build/dist/Types/OnCallDutyPolicy/RestrictionTimes.js +27 -13
  379. package/build/dist/Types/OnCallDutyPolicy/RestrictionTimes.js.map +1 -1
  380. package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js +20 -5
  381. package/build/dist/Types/OnCallDutyPolicy/UserOverrideUtil.js.map +1 -1
  382. package/build/dist/UI/Components/Events/RecurringFieldElement.js +13 -0
  383. package/build/dist/UI/Components/Events/RecurringFieldElement.js.map +1 -1
  384. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +10 -5
  385. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  386. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +18 -4
  387. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  388. package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
  389. package/build/dist/UI/Components/Page/Page.js +3 -1
  390. package/build/dist/UI/Components/Page/Page.js.map +1 -1
  391. package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js +1 -1
  392. package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js.map +1 -1
  393. package/build/dist/UI/Utils/LlmTypeDropdownOptions.js +38 -0
  394. package/build/dist/UI/Utils/LlmTypeDropdownOptions.js.map +1 -0
  395. package/build/dist/UI/Utils/TestLLMProvider.js +37 -0
  396. package/build/dist/UI/Utils/TestLLMProvider.js.map +1 -0
  397. package/package.json +1 -1
@@ -12,6 +12,7 @@ import BillingService from "./BillingService";
12
12
  import CallLogService from "./CallLogService";
13
13
  import CallService from "./CallService";
14
14
  import DataMigrationService from "./DataMigrationService";
15
+ import MigrationFailureService from "./MigrationFailureService";
15
16
  import DomainService from "./DomainService";
16
17
  import EmailLogService from "./EmailLogService";
17
18
  import EmailVerificationTokenService from "./EmailVerificationTokenService";
@@ -273,6 +274,7 @@ const services: Array<BaseService> = [
273
274
  CallService,
274
275
 
275
276
  DataMigrationService,
277
+ MigrationFailureService,
276
278
  DomainService,
277
279
 
278
280
  EmailLogService,
@@ -6,6 +6,7 @@ import ObjectID from "../../Types/ObjectID";
6
6
  import UpdateBy from "../Types/Database/UpdateBy";
7
7
  import QueryHelper from "../Types/Database/QueryHelper";
8
8
  import LIMIT_MAX from "../../Types/Database/LimitMax";
9
+ import SortOrder from "../../Types/BaseDatabase/SortOrder";
9
10
  import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
10
11
 
11
12
  export class Service extends DatabaseService<Model> {
@@ -153,6 +154,115 @@ export class Service extends DatabaseService<Model> {
153
154
 
154
155
  return null;
155
156
  }
157
+
158
+ /*
159
+ * Resolve the provider to use for a chat turn. When the user has explicitly
160
+ * chosen a provider (llmProviderId), use it — but only if it is actually
161
+ * usable by this project: either a global provider, or one owned by the
162
+ * project. Otherwise fall back to the project default / global provider.
163
+ * A stale id (e.g. the chosen provider was deleted) also falls back, so a
164
+ * conversation never breaks because its provider went away.
165
+ */
166
+ @CaptureSpan()
167
+ public async getProviderForChat(data: {
168
+ projectId: ObjectID;
169
+ llmProviderId?: ObjectID | undefined;
170
+ }): Promise<Model | null> {
171
+ if (data.llmProviderId) {
172
+ const provider: Model | null = await this.findOneBy({
173
+ query: {
174
+ _id: data.llmProviderId.toString(),
175
+ },
176
+ select: {
177
+ _id: true,
178
+ name: true,
179
+ llmType: true,
180
+ apiKey: true,
181
+ baseUrl: true,
182
+ modelName: true,
183
+ isGlobalLlm: true,
184
+ projectId: true,
185
+ costPerMillionTokensInUSDCents: true,
186
+ },
187
+ props: {
188
+ isRoot: true,
189
+ },
190
+ });
191
+
192
+ if (provider) {
193
+ const isGlobal: boolean = provider.isGlobalLlm === true;
194
+ const belongsToProject: boolean =
195
+ provider.projectId?.toString() === data.projectId.toString();
196
+
197
+ if (isGlobal || belongsToProject) {
198
+ return provider;
199
+ }
200
+ }
201
+ // Fall through to default resolution when the id is invalid/inaccessible.
202
+ }
203
+
204
+ return this.getLLMProviderForProject(data.projectId);
205
+ }
206
+
207
+ /*
208
+ * The providers a project member can pick from in the chat provider switcher:
209
+ * every provider configured for the project plus every global provider.
210
+ * Secrets (apiKey) are never selected here — this only feeds the picker UI.
211
+ */
212
+ @CaptureSpan()
213
+ public async getSelectableProvidersForProject(
214
+ projectId: ObjectID,
215
+ ): Promise<Array<Model>> {
216
+ const [projectProviders, globalProviders]: [Array<Model>, Array<Model>] =
217
+ await Promise.all([
218
+ this.findBy({
219
+ query: {
220
+ projectId: projectId,
221
+ },
222
+ select: {
223
+ _id: true,
224
+ name: true,
225
+ llmType: true,
226
+ modelName: true,
227
+ isDefault: true,
228
+ isGlobalLlm: true,
229
+ },
230
+ sort: {
231
+ isDefault: SortOrder.Descending,
232
+ name: SortOrder.Ascending,
233
+ },
234
+ skip: 0,
235
+ limit: LIMIT_MAX,
236
+ props: {
237
+ isRoot: true,
238
+ },
239
+ }),
240
+ this.findBy({
241
+ query: {
242
+ projectId: QueryHelper.isNull(),
243
+ isGlobalLlm: true,
244
+ },
245
+ select: {
246
+ _id: true,
247
+ name: true,
248
+ llmType: true,
249
+ modelName: true,
250
+ isDefault: true,
251
+ isGlobalLlm: true,
252
+ },
253
+ sort: {
254
+ name: SortOrder.Ascending,
255
+ },
256
+ skip: 0,
257
+ limit: LIMIT_MAX,
258
+ props: {
259
+ isRoot: true,
260
+ },
261
+ }),
262
+ ]);
263
+
264
+ return [...projectProviders, ...globalProviders];
265
+ }
156
266
  }
157
267
 
158
268
  export default new Service();
@@ -0,0 +1,9 @@
1
+ import DatabaseService from "./DatabaseService";
2
+ import Model from "../../Models/DatabaseModels/MigrationFailure";
3
+
4
+ export class Service extends DatabaseService<Model> {
5
+ public constructor() {
6
+ super(Model);
7
+ }
8
+ }
9
+ export default new Service();
@@ -310,8 +310,14 @@ export class Service extends DatabaseService<MonitorProbe> {
310
310
  _onCreate: OnCreate<MonitorProbe>,
311
311
  createdItem: MonitorProbe,
312
312
  ): Promise<MonitorProbe> {
313
- if (createdItem.probeId) {
314
- await MonitorService.refreshProbeStatus(createdItem.probeId);
313
+ /*
314
+ * Refresh only the monitor this probe was just added to. Do NOT call
315
+ * refreshProbeStatus(probeId) here: a global/shared probe can be attached
316
+ * to thousands of monitors, and refreshing every one of them on a single
317
+ * create storms the database and can 500 the request.
318
+ */
319
+ if (createdItem.monitorId) {
320
+ await this.refreshMonitorStatusSafely(createdItem.monitorId);
315
321
  }
316
322
 
317
323
  return Promise.resolve(createdItem);
@@ -321,7 +327,7 @@ export class Service extends DatabaseService<MonitorProbe> {
321
327
  onUpdate: OnUpdate<MonitorProbe>,
322
328
  updatedItemIds: ObjectID[],
323
329
  ): Promise<OnUpdate<MonitorProbe>> {
324
- // if isEnabled is updated, refresh the probe status
330
+ // if isEnabled is updated, refresh the status of the affected monitors.
325
331
  if (onUpdate.updateBy.data.isEnabled !== undefined) {
326
332
  const monitorProbes: Array<MonitorProbe> = await this.findBy({
327
333
  query: {
@@ -329,8 +335,6 @@ export class Service extends DatabaseService<MonitorProbe> {
329
335
  },
330
336
  select: {
331
337
  monitorId: true,
332
- probeId: true,
333
- nextPingAt: true,
334
338
  },
335
339
  limit: LIMIT_PER_PROJECT,
336
340
  skip: 0,
@@ -340,16 +344,38 @@ export class Service extends DatabaseService<MonitorProbe> {
340
344
  });
341
345
 
342
346
  for (const monitorProbe of monitorProbes) {
343
- if (!monitorProbe.probeId) {
347
+ if (!monitorProbe.monitorId) {
344
348
  continue;
345
349
  }
346
350
 
347
- await MonitorService.refreshProbeStatus(monitorProbe.probeId);
351
+ /*
352
+ * Refresh only the monitor whose probe row changed. Do NOT call
353
+ * refreshProbeStatus(probeId) here: a global/shared probe can be
354
+ * attached to thousands of monitors, and refreshing all of them on a
355
+ * single enable/disable toggle storms the database and 500s the
356
+ * request (the row itself is already saved by this point).
357
+ */
358
+ await this.refreshMonitorStatusSafely(monitorProbe.monitorId);
348
359
  }
349
360
  }
350
361
 
351
362
  return onUpdate;
352
363
  }
364
+
365
+ /*
366
+ * Refresh a single monitor's aggregate probe status without letting a
367
+ * failure bubble up. These refreshes run in post-commit hooks
368
+ * (onCreateSuccess / onUpdateSuccess) after the MonitorProbe row is already
369
+ * persisted, so a status-refresh error must not turn a successful save into
370
+ * a 500 for the user — log it instead.
371
+ */
372
+ private async refreshMonitorStatusSafely(monitorId: ObjectID): Promise<void> {
373
+ try {
374
+ await MonitorService.refreshMonitorProbeStatus(monitorId);
375
+ } catch (err) {
376
+ logger.error(err);
377
+ }
378
+ }
353
379
  }
354
380
 
355
381
  export default new Service();
@@ -1620,14 +1620,26 @@ ${createdItem.description?.trim() || "No description provided."}
1620
1620
 
1621
1621
  /*
1622
1622
  * Each monitor appears at most once for a given probeId (composite
1623
- * unique on MonitorProbe), so concurrent refreshes operate on
1624
- * disjoint rows and are safe to run in parallel.
1623
+ * unique on MonitorProbe), so concurrent refreshes operate on disjoint
1624
+ * rows and are safe to run in parallel. A global/shared probe can be
1625
+ * attached to thousands of monitors, though, so refresh in bounded
1626
+ * batches instead of firing every refresh at once — an unbounded
1627
+ * Promise.all here would exhaust the database connection pool.
1625
1628
  */
1626
- await Promise.all(
1627
- monitorProbes.map((monitorProbe: MonitorProbe) => {
1628
- return this.refreshMonitorProbeStatus(monitorProbe.monitorId!);
1629
- }),
1630
- );
1629
+ const refreshConcurrency: number = 50;
1630
+
1631
+ for (let i: number = 0; i < monitorProbes.length; i += refreshConcurrency) {
1632
+ const batch: Array<MonitorProbe> = monitorProbes.slice(
1633
+ i,
1634
+ i + refreshConcurrency,
1635
+ );
1636
+
1637
+ await Promise.all(
1638
+ batch.map((monitorProbe: MonitorProbe) => {
1639
+ return this.refreshMonitorProbeStatus(monitorProbe.monitorId!);
1640
+ }),
1641
+ );
1642
+ }
1631
1643
  }
1632
1644
 
1633
1645
  @CaptureSpan()
@@ -22,6 +22,9 @@ import PushNotificationMessage from "../../Types/PushNotification/PushNotificati
22
22
  import PushNotificationUtil from "../Utils/PushNotificationUtil";
23
23
  import { createWhatsAppMessageFromTemplate } from "../Utils/WhatsAppTemplateUtil";
24
24
  import { WhatsAppMessagePayload } from "../../Types/WhatsApp/WhatsAppMessage";
25
+ import OnCallDutyPolicyTimeLogService from "./OnCallDutyPolicyTimeLogService";
26
+ import OneUptimeDate from "../../Types/Date";
27
+ import logger, { LogAttributes } from "../Utils/Logger";
25
28
 
26
29
  export class Service extends DatabaseService<Model> {
27
30
  public constructor() {
@@ -86,6 +89,39 @@ export class Service extends DatabaseService<Model> {
86
89
  return createdItem;
87
90
  }
88
91
 
92
+ /*
93
+ * Open the roster user's on-call time log for this schedule/rule/policy.
94
+ * Mirrors onDeleteSuccess's endTimeLogForUser (detaching a schedule closes
95
+ * the log, so attaching must open it). Without this, attaching a schedule
96
+ * whose roster user is ALREADY established never opened a log — the roster
97
+ * refresh only opens one when the current user CHANGES — so the whole stint
98
+ * went unrecorded in on-call reporting and the later detach's
99
+ * endTimeLogForUser was a no-op (audit M1). startTimeLogForUser is idempotent
100
+ * (open-log dedup), so a later natural handoff re-opening the same log is a
101
+ * no-op.
102
+ */
103
+ if (
104
+ createdModel.onCallDutyPolicy?.id &&
105
+ createdModel.onCallDutyPolicyEscalationRule?.id &&
106
+ createdModel.projectId &&
107
+ createdModel.onCallDutyPolicyScheduleId
108
+ ) {
109
+ OnCallDutyPolicyTimeLogService.startTimeLogForUser({
110
+ projectId: createdModel.projectId,
111
+ onCallDutyPolicyId: createdModel.onCallDutyPolicy.id,
112
+ onCallDutyPolicyEscalationRuleId:
113
+ createdModel.onCallDutyPolicyEscalationRule.id,
114
+ userId: userOnSchedule,
115
+ onCallDutyPolicyScheduleId: createdModel.onCallDutyPolicyScheduleId,
116
+ startsAt: OneUptimeDate.getCurrentDate(),
117
+ }).catch((error: Error) => {
118
+ logger.error(error, {
119
+ projectId: createdModel.projectId?.toString(),
120
+ userId: userOnSchedule?.toString(),
121
+ } as LogAttributes);
122
+ });
123
+ }
124
+
89
125
  const scheduleName: string =
90
126
  createdModel.onCallDutyPolicySchedule?.name || "No name provided";
91
127
 
@@ -289,6 +325,35 @@ export class Service extends DatabaseService<Model> {
289
325
  continue;
290
326
  }
291
327
 
328
+ /*
329
+ * Close the roster user's open on-call time log for this schedule/rule/
330
+ * policy. Roster handoffs open a time log keyed by schedule + rule + policy
331
+ * (OnCallDutyPolicyScheduleService.startTimeLogForUser); the user-link and
332
+ * team-link delete handlers close theirs, but detaching a SCHEDULE from the
333
+ * rule left the log open forever, so the roster user kept showing as
334
+ * on-call in reporting (audit F18).
335
+ */
336
+ if (
337
+ deletedItem.onCallDutyPolicy?.id &&
338
+ deletedItem.onCallDutyPolicyEscalationRule?.id &&
339
+ deletedItem.projectId
340
+ ) {
341
+ OnCallDutyPolicyTimeLogService.endTimeLogForUser({
342
+ projectId: deletedItem.projectId,
343
+ onCallDutyPolicyId: deletedItem.onCallDutyPolicy.id,
344
+ onCallDutyPolicyEscalationRuleId:
345
+ deletedItem.onCallDutyPolicyEscalationRule.id,
346
+ userId: userOnSchedule,
347
+ onCallDutyPolicyScheduleId: deletedItem.onCallDutyPolicyScheduleId!,
348
+ endsAt: OneUptimeDate.getCurrentDate(),
349
+ }).catch((error: Error) => {
350
+ logger.error(error, {
351
+ projectId: deletedItem.projectId?.toString(),
352
+ userId: userOnSchedule?.toString(),
353
+ } as LogAttributes);
354
+ });
355
+ }
356
+
292
357
  const sendEmailToUserId: ObjectID | undefined | null = userOnSchedule;
293
358
 
294
359
  if (!sendEmailToUserId) {
@@ -10,6 +10,7 @@ import OnCallDutyPolicyEscalationRuleScheduleService from "./OnCallDutyPolicyEsc
10
10
  import OnCallDutyPolicyEscalationRuleTeamService from "./OnCallDutyPolicyEscalationRuleTeamService";
11
11
  import OnCallDutyPolicyEscalationRuleUserService from "./OnCallDutyPolicyEscalationRuleUserService";
12
12
  import OnCallDutyPolicyExecutionLogService from "./OnCallDutyPolicyExecutionLogService";
13
+ import OnCallDutyPolicyExecutionLog from "../../Models/DatabaseModels/OnCallDutyPolicyExecutionLog";
13
14
  import OnCallDutyPolicyExecutionLogTimelineService from "./OnCallDutyPolicyExecutionLogTimelineService";
14
15
  import OnCallDutyPolicyScheduleService from "./OnCallDutyPolicyScheduleService";
15
16
  import TeamMemberService from "./TeamMemberService";
@@ -315,31 +316,66 @@ export class Service extends DatabaseService<Model> {
315
316
  );
316
317
 
317
318
  type StartUserNotificationRuleExecutionFunction = (
318
- userId: ObjectID,
319
+ originalUserId: ObjectID,
320
+ alertSentToUserId: ObjectID,
319
321
  teamId: ObjectID | null,
320
322
  scheduleId: ObjectID | null,
321
323
  ) => Promise<void>;
322
324
 
323
- const startUserNotificationRuleExecution: StartUserNotificationRuleExecutionFunction =
324
- async (
325
- userId: ObjectID,
326
- teamId: ObjectID | null,
327
- scheduleId: ObjectID | null,
328
- ): Promise<void> => {
329
- // This is where user is notified.
330
-
331
- // get route alert to user id.
332
- let routeAlertToUserId: ObjectID | null = null;
325
+ /*
326
+ * Resolve the FINAL alert recipient for a target, applying a user override
327
+ * (getRouteAlertToUserId) exactly ONCE per paging decision.
328
+ *
329
+ * Schedule targets are already override-resolved: getCurrentUserIdInSchedule
330
+ * applies overrides via UserOverrideUtil. Applying getRouteAlertToUserId to
331
+ * them again re-substituted an already-substituted user (transitive), so the
332
+ * schedule paging path could page a different person than the roster,
333
+ * dashboard, handoff notification, and a rule that lists the same user
334
+ * directly all of which apply overrides only once (audit F5). So team and
335
+ * direct-user targets (the RAW roster user) get the single override hop here;
336
+ * schedule targets do not.
337
+ */
338
+ const resolveAlertRecipientUserId: (
339
+ userId: ObjectID,
340
+ isFromSchedule: boolean,
341
+ ) => Promise<ObjectID> = async (
342
+ userId: ObjectID,
343
+ isFromSchedule: boolean,
344
+ ): Promise<ObjectID> => {
345
+ if (isFromSchedule) {
346
+ return userId;
347
+ }
333
348
 
334
- if (options.onCallPolicyId) {
335
- routeAlertToUserId = await this.getRouteAlertToUserId({
349
+ if (options.onCallPolicyId) {
350
+ const routeAlertToUserId: ObjectID | null =
351
+ await this.getRouteAlertToUserId({
336
352
  userId,
337
353
  onCallDutyPolicyId: options.onCallPolicyId,
338
354
  projectId: options.projectId,
339
355
  });
356
+
357
+ if (routeAlertToUserId) {
358
+ return routeAlertToUserId;
340
359
  }
360
+ }
361
+
362
+ return userId;
363
+ };
364
+
365
+ const startUserNotificationRuleExecution: StartUserNotificationRuleExecutionFunction =
366
+ async (
367
+ originalUserId: ObjectID,
368
+ alertSentToUserId: ObjectID,
369
+ teamId: ObjectID | null,
370
+ scheduleId: ObjectID | null,
371
+ ): Promise<void> => {
372
+ /*
373
+ * This is where user is notified. alertSentToUserId is already resolved
374
+ * (override applied at most once by resolveAlertRecipientUserId).
375
+ */
341
376
 
342
- const alertSentToUserId: ObjectID = routeAlertToUserId || userId;
377
+ const wasOverridden: boolean =
378
+ alertSentToUserId.toString() !== originalUserId.toString();
343
379
 
344
380
  logger.debug(
345
381
  `Starting notification rule execution for userId: ${alertSentToUserId.toString()}`,
@@ -354,8 +390,8 @@ export class Service extends DatabaseService<Model> {
354
390
  log.status = OnCallDutyExecutionLogTimelineStatus.Executing;
355
391
  log.alertSentToUserId = alertSentToUserId;
356
392
 
357
- if (routeAlertToUserId) {
358
- log.overridedByUserId = userId;
393
+ if (wasOverridden) {
394
+ log.overridedByUserId = originalUserId;
359
395
  }
360
396
 
361
397
  if (teamId) {
@@ -390,12 +426,36 @@ export class Service extends DatabaseService<Model> {
390
426
  onCallDutyPolicyExecutionLogTimelineId: log.id!,
391
427
  projectId: options.projectId,
392
428
  onCallScheduleId: scheduleId || undefined,
393
- overridedByUserId: routeAlertToUserId ? userId : undefined,
429
+ overridedByUserId: wasOverridden ? originalUserId : undefined,
394
430
  },
395
431
  );
396
432
  };
397
433
 
434
+ /*
435
+ * Dedup on the RESOLVED recipient (post-override), not the pre-route id.
436
+ * Keying on the pre-route id let two different targets that both reroute to
437
+ * the same backup each fire a full notification chain for the same person in
438
+ * one rule (audit F6).
439
+ */
398
440
  const uniqueUserIds: Array<ObjectID> = [];
441
+ const alreadyNotified: (recipientId: ObjectID) => boolean = (
442
+ recipientId: ObjectID,
443
+ ): boolean => {
444
+ return Boolean(
445
+ uniqueUserIds.find((userId: ObjectID) => {
446
+ return recipientId.toString() === userId.toString();
447
+ }),
448
+ );
449
+ };
450
+
451
+ /*
452
+ * M-5: tracks whether any schedule target for this rule momentarily had no
453
+ * on-call user (a restriction gap / future start / empty layer). If a rule
454
+ * reaches NO ONE solely because of such a gap, we re-sample the same rule on
455
+ * the next cron ticks (bounded) instead of permanently skipping it, so a
456
+ * user coming on-call moments later still gets paged.
457
+ */
458
+ let hadScheduleGap: boolean = false;
399
459
 
400
460
  for (const teamInRule of teamsInRule) {
401
461
  const usersInTeam: Array<User> = await TeamMemberService.getUsersInTeam(
@@ -403,14 +463,16 @@ export class Service extends DatabaseService<Model> {
403
463
  );
404
464
 
405
465
  for (const user of usersInTeam) {
406
- if (
407
- !uniqueUserIds.find((userId: ObjectID) => {
408
- return user.id?.toString() === userId.toString();
409
- })
410
- ) {
411
- uniqueUserIds.push(user.id!);
466
+ const recipientUserId: ObjectID = await resolveAlertRecipientUserId(
467
+ user.id!,
468
+ false,
469
+ );
470
+
471
+ if (!alreadyNotified(recipientUserId)) {
472
+ uniqueUserIds.push(recipientUserId);
412
473
  await startUserNotificationRuleExecution(
413
474
  user.id!,
475
+ recipientUserId,
414
476
  teamInRule.teamId!,
415
477
  null,
416
478
  );
@@ -419,7 +481,7 @@ export class Service extends DatabaseService<Model> {
419
481
  log.statusMessage =
420
482
  "Skipped because notification sent to this user already.";
421
483
  log.status = OnCallDutyExecutionLogTimelineStatus.Skipped;
422
- log.alertSentToUserId = user.id!;
484
+ log.alertSentToUserId = recipientUserId;
423
485
  log.userBelongsToTeamId = teamInRule.teamId!;
424
486
 
425
487
  await OnCallDutyPolicyExecutionLogTimelineService.create({
@@ -433,19 +495,25 @@ export class Service extends DatabaseService<Model> {
433
495
  }
434
496
 
435
497
  for (const userRule of usersInRule) {
436
- if (
437
- !uniqueUserIds.find((userId: ObjectID) => {
438
- return userRule.userId?.toString() === userId.toString();
439
- })
440
- ) {
441
- uniqueUserIds.push(userRule.userId!);
442
- await startUserNotificationRuleExecution(userRule.userId!, null, null);
498
+ const recipientUserId: ObjectID = await resolveAlertRecipientUserId(
499
+ userRule.userId!,
500
+ false,
501
+ );
502
+
503
+ if (!alreadyNotified(recipientUserId)) {
504
+ uniqueUserIds.push(recipientUserId);
505
+ await startUserNotificationRuleExecution(
506
+ userRule.userId!,
507
+ recipientUserId,
508
+ null,
509
+ null,
510
+ );
443
511
  } else {
444
512
  const log: OnCallDutyPolicyExecutionLogTimeline = getNewLog();
445
513
  log.statusMessage =
446
514
  "Skipped because notification sent to this user already.";
447
515
  log.status = OnCallDutyExecutionLogTimelineStatus.Skipped;
448
- log.alertSentToUserId = userRule.userId!;
516
+ log.alertSentToUserId = recipientUserId;
449
517
 
450
518
  await OnCallDutyPolicyExecutionLogTimelineService.create({
451
519
  data: log,
@@ -464,6 +532,8 @@ export class Service extends DatabaseService<Model> {
464
532
  );
465
533
 
466
534
  if (!userIdInSchedule) {
535
+ hadScheduleGap = true;
536
+
467
537
  const log: OnCallDutyPolicyExecutionLogTimeline = getNewLog();
468
538
  log.statusMessage =
469
539
  "Skipped because no active users are found in this schedule.";
@@ -480,14 +550,17 @@ export class Service extends DatabaseService<Model> {
480
550
  continue;
481
551
  }
482
552
 
483
- if (
484
- !uniqueUserIds.find((userId: ObjectID) => {
485
- return userIdInSchedule?.toString() === userId.toString();
486
- })
487
- ) {
488
- uniqueUserIds.push(userIdInSchedule);
553
+ // Schedule users are already override-resolved; no second override hop (F5).
554
+ const recipientUserId: ObjectID = await resolveAlertRecipientUserId(
555
+ userIdInSchedule,
556
+ true,
557
+ );
558
+
559
+ if (!alreadyNotified(recipientUserId)) {
560
+ uniqueUserIds.push(recipientUserId);
489
561
  await startUserNotificationRuleExecution(
490
562
  userIdInSchedule,
563
+ recipientUserId,
491
564
  null,
492
565
  scheduleRule.onCallDutyPolicyScheduleId!,
493
566
  );
@@ -496,7 +569,7 @@ export class Service extends DatabaseService<Model> {
496
569
  log.statusMessage =
497
570
  "Skipped because notification sent to this user already.";
498
571
  log.status = OnCallDutyExecutionLogTimelineStatus.Skipped;
499
- log.alertSentToUserId = userIdInSchedule;
572
+ log.alertSentToUserId = recipientUserId;
500
573
  log.onCallDutyScheduleId = scheduleRule.onCallDutyPolicyScheduleId!;
501
574
 
502
575
  await OnCallDutyPolicyExecutionLogTimelineService.create({
@@ -521,6 +594,77 @@ export class Service extends DatabaseService<Model> {
521
594
  });
522
595
  }
523
596
 
597
+ /*
598
+ * M-5: if this rule reached NO ONE and the only reason was a schedule gap
599
+ * (a schedule target with no on-call user right now), re-sample the SAME
600
+ * rule on the next cron ticks — bounded — instead of permanently skipping.
601
+ * ExecutePendingExecutions selects the rule with order =
602
+ * lastExecutedEscalationRuleOrder + 1, so rewinding the order by one makes
603
+ * it re-run this rule. No notifications were sent (uniqueUserIds is empty),
604
+ * so a re-sample cannot double-page anyone.
605
+ */
606
+ const maxScheduleGapRetries: number = 5;
607
+ const scheduleGapRetryIntervalMinutes: number = 1;
608
+ const reachedNoOne: boolean = uniqueUserIds.length === 0;
609
+
610
+ if (reachedNoOne && hadScheduleGap && rule.order) {
611
+ const executionLog: OnCallDutyPolicyExecutionLog | null =
612
+ await OnCallDutyPolicyExecutionLogService.findOneById({
613
+ id: options.onCallPolicyExecutionLogId,
614
+ select: {
615
+ scheduleGapRetryCount: true,
616
+ },
617
+ props: {
618
+ isRoot: true,
619
+ },
620
+ });
621
+
622
+ const retryCount: number = executionLog?.scheduleGapRetryCount || 0;
623
+
624
+ if (retryCount < maxScheduleGapRetries) {
625
+ await OnCallDutyPolicyExecutionLogService.updateOneById({
626
+ id: options.onCallPolicyExecutionLogId,
627
+ data: {
628
+ lastExecutedEscalationRuleOrder: rule.order - 1,
629
+ executeNextEscalationRuleInMinutes: scheduleGapRetryIntervalMinutes,
630
+ scheduleGapRetryCount: retryCount + 1,
631
+ },
632
+ props: {
633
+ isRoot: true,
634
+ },
635
+ });
636
+
637
+ logger.debug(
638
+ `Rule ${ruleId.toString()} reached no one due to a schedule gap; re-sampling ${retryCount + 1}/${maxScheduleGapRetries}.`,
639
+ {
640
+ onCallDutyPolicyEscalationRuleId: ruleId.toString(),
641
+ } as LogAttributes,
642
+ );
643
+ } else {
644
+ // Retries exhausted — advance normally next tick and reset the counter.
645
+ await OnCallDutyPolicyExecutionLogService.updateOneById({
646
+ id: options.onCallPolicyExecutionLogId,
647
+ data: {
648
+ scheduleGapRetryCount: 0,
649
+ },
650
+ props: {
651
+ isRoot: true,
652
+ },
653
+ });
654
+ }
655
+ } else {
656
+ // Reached someone (or no schedule gap): clear retry state for the next rule.
657
+ await OnCallDutyPolicyExecutionLogService.updateOneById({
658
+ id: options.onCallPolicyExecutionLogId,
659
+ data: {
660
+ scheduleGapRetryCount: 0,
661
+ },
662
+ props: {
663
+ isRoot: true,
664
+ },
665
+ });
666
+ }
667
+
524
668
  logger.debug(`Completed rule execution for ruleId: ${ruleId.toString()}`, {
525
669
  projectId: options.projectId.toString(),
526
670
  onCallDutyPolicyEscalationRuleId: ruleId.toString(),
@@ -912,8 +1056,8 @@ export class Service extends DatabaseService<Model> {
912
1056
  // moving down.
913
1057
 
914
1058
  for (const resource of resources) {
915
- if (resource.order! <= newOrder) {
916
- // increment order.
1059
+ if (resource.order! > currentOrder && resource.order! <= newOrder) {
1060
+ // decrement order to fill the gap left by the moved rule.
917
1061
  await this.updateOneBy({
918
1062
  query: {
919
1063
  _id: resource._id!,