@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,240 @@
1
+ import os from "os";
2
+ import { DataSource, DataSourceOptions } from "typeorm";
3
+ import logger from "../Logger";
4
+ import { AppVersion } from "../../EnvironmentConfig";
5
+
6
+ /*
7
+ * Records migrations that FAILED to apply into the "MigrationFailure" table so
8
+ * the admin health page can explain WHY a migration is pending (what error,
9
+ * when it ran) instead of only showing that it is behind. Every write here is
10
+ * best-effort: a diagnostic log must never itself crash a boot / migrate Job or
11
+ * mask the real migration error, so all failures inside this module are
12
+ * swallowed (logged, not thrown).
13
+ */
14
+
15
+ // Which runner a failed migration belongs to. Stored verbatim in the table.
16
+ export enum MigrationFailureType {
17
+ PostgresSchema = "PostgresSchema",
18
+ DataMigration = "DataMigration",
19
+ }
20
+
21
+ // Keep a single row bounded so a pathological error/stack can't bloat the table.
22
+ const MAX_ERROR_MESSAGE_LENGTH: number = 10000;
23
+ const MAX_ERROR_STACK_LENGTH: number = 20000;
24
+
25
+ /*
26
+ * Best-effort credential scrub for the free-form error text we persist. DB /
27
+ * migration errors seldom carry secrets, but a connection error can echo a
28
+ * `scheme://user:pass@host` DSN and a failing DDL can quote a literal default,
29
+ * so we mask the obvious vectors before the text is stored and later shown.
30
+ */
31
+ const CONNECTION_STRING_SECRET_PATTERN: RegExp =
32
+ /([a-z][a-z0-9+.-]*:\/\/[^:@\s/]+):[^\s/]+@/gi;
33
+ const KEYWORD_SECRET_PATTERN: RegExp =
34
+ /(password|secret|token|api[_-]?key|apikey|credential|private[_-]?key|auth[_-]?token|access[_-]?key)(\s*[:=]\s*)('(?:[^']|'')*'|"[^"]*"|[^\s,)]+)/gi;
35
+
36
+ export function scrubMigrationErrorText(text: string): string {
37
+ if (!text) {
38
+ return text;
39
+ }
40
+
41
+ return text
42
+ .replace(
43
+ CONNECTION_STRING_SECRET_PATTERN,
44
+ (_match: string, prefix: string): string => {
45
+ return `${prefix}:***REDACTED***@`;
46
+ },
47
+ )
48
+ .replace(
49
+ KEYWORD_SECRET_PATTERN,
50
+ (_match: string, keyword: string, separator: string): string => {
51
+ return `${keyword}${separator}***REDACTED***`;
52
+ },
53
+ );
54
+ }
55
+
56
+ function toErrorMessage(error: unknown): string {
57
+ if (error instanceof Error) {
58
+ return error.message || error.name || "Unknown error";
59
+ }
60
+ return String(error);
61
+ }
62
+
63
+ function toErrorStack(error: unknown): string | null {
64
+ return error instanceof Error && error.stack ? error.stack : null;
65
+ }
66
+
67
+ interface MigrationFailureInput {
68
+ migrationName: string;
69
+ migrationType: MigrationFailureType;
70
+ error: unknown;
71
+ }
72
+
73
+ /*
74
+ * Append one failed-attempt row using the provided (already-connected)
75
+ * DataSource. Raw SQL — not the Service layer — so it also works on a
76
+ * throwaway diagnostic connection and never depends on realtime/event wiring.
77
+ * Returns true if the row was written. If the "MigrationFailure" table does not
78
+ * exist yet (e.g. its own migration is still pending on a first rollout) the
79
+ * INSERT throws and is swallowed — the caller's real error is unaffected.
80
+ */
81
+ export async function recordMigrationFailure(
82
+ dataSource: DataSource,
83
+ input: MigrationFailureInput,
84
+ ): Promise<boolean> {
85
+ try {
86
+ const message: string = scrubMigrationErrorText(
87
+ toErrorMessage(input.error),
88
+ ).substring(0, MAX_ERROR_MESSAGE_LENGTH);
89
+
90
+ const rawStack: string | null = toErrorStack(input.error);
91
+ const stack: string | null = rawStack
92
+ ? scrubMigrationErrorText(rawStack).substring(0, MAX_ERROR_STACK_LENGTH)
93
+ : null;
94
+
95
+ await dataSource.query(
96
+ `INSERT INTO "MigrationFailure"
97
+ ("migrationName", "migrationType", "errorMessage", "errorStack", "attemptedAt", "hostName", "appVersion", "version", "createdAt", "updatedAt")
98
+ VALUES ($1, $2, $3, $4, $5, $6, $7, 1, now(), now())`,
99
+ [
100
+ input.migrationName,
101
+ input.migrationType,
102
+ message,
103
+ stack,
104
+ new Date(),
105
+ os.hostname(),
106
+ AppVersion,
107
+ ],
108
+ );
109
+
110
+ return true;
111
+ } catch (err) {
112
+ // Diagnostic write must never mask the real failure — log and move on.
113
+ logger.warn(
114
+ `Could not record migration failure for "${input.migrationName}" (${input.migrationType}); continuing.`,
115
+ );
116
+ logger.warn(err);
117
+ return false;
118
+ }
119
+ }
120
+
121
+ // Trailing epoch in a migration class name — how TypeORM orders/identifies them.
122
+ function parseMigrationTimestamp(name: string): number {
123
+ const match: RegExpMatchArray | null = name.match(/(\d{10,})$/);
124
+ return match ? Number(match[1]) : 0;
125
+ }
126
+
127
+ /*
128
+ * The TypeORM schema runner (migrationsRun) applies migrations in ascending
129
+ * timestamp order and halts at the first failure, so the FIRST defined
130
+ * migration not yet in the `migrations` table is exactly the one that failed.
131
+ * We recompute it here from a throwaway connection because the app's own
132
+ * connect() has already failed (its DataSource is not usable) at the point a
133
+ * schema migration throws.
134
+ */
135
+ export async function recordSchemaMigrationFailureBestEffort(
136
+ dataSourceOptions: DataSourceOptions,
137
+ originalError: unknown,
138
+ ): Promise<void> {
139
+ /*
140
+ * Only THIS process's own boot runs schema migrations (migrationsRun=true —
141
+ * the migrate Job and single-pod / dev boots). On runtime pods where a
142
+ * dedicated migrate Job owns migrations (migrationsRun=false), a connect()
143
+ * failure is a connectivity problem, never a failed migration — recording
144
+ * one here would misattribute an outage to a (legitimately) pending
145
+ * migration, so skip.
146
+ */
147
+ if (
148
+ (dataSourceOptions as { migrationsRun?: boolean }).migrationsRun !== true
149
+ ) {
150
+ return;
151
+ }
152
+
153
+ let diagnosticDataSource: DataSource | null = null;
154
+
155
+ try {
156
+ diagnosticDataSource = new DataSource({
157
+ ...dataSourceOptions,
158
+ // Never re-run migrations or sync schema on the diagnostic connection.
159
+ migrationsRun: false,
160
+ synchronize: false,
161
+ // Keep the probe short so a genuinely-down DB doesn't slow the failing boot.
162
+ extra: {
163
+ ...((dataSourceOptions as { extra?: Record<string, unknown> }).extra ||
164
+ {}),
165
+ connectionTimeoutMillis: 5000,
166
+ },
167
+ } as DataSourceOptions);
168
+
169
+ /*
170
+ * If this fails, the original error was a connectivity problem (not a bad
171
+ * migration) — there is nothing meaningful to attribute, so we skip.
172
+ */
173
+ await diagnosticDataSource.initialize();
174
+
175
+ const definedMigrationClasses: Array<new () => { name: string }> =
176
+ Array.isArray(dataSourceOptions.migrations)
177
+ ? (dataSourceOptions.migrations as unknown as Array<
178
+ new () => { name: string }
179
+ >)
180
+ : [];
181
+
182
+ const definedNames: Array<string> = Array.from(
183
+ new Set(
184
+ definedMigrationClasses.map(
185
+ (MigrationClass: new () => { name: string }): string => {
186
+ return new MigrationClass().name;
187
+ },
188
+ ),
189
+ ),
190
+ ).sort((a: string, b: string): number => {
191
+ return parseMigrationTimestamp(a) - parseMigrationTimestamp(b);
192
+ });
193
+
194
+ let appliedNames: Set<string> = new Set();
195
+ try {
196
+ const rows: Array<{ name: string }> = await diagnosticDataSource.query(
197
+ "SELECT name FROM migrations",
198
+ );
199
+ appliedNames = new Set(
200
+ rows.map((row: { name: string }): string => {
201
+ return row.name;
202
+ }),
203
+ );
204
+ } catch {
205
+ // migrations table may not exist on a brand-new DB — treat as none applied.
206
+ }
207
+
208
+ const firstPending: string | undefined = definedNames.find(
209
+ (name: string): boolean => {
210
+ return !appliedNames.has(name);
211
+ },
212
+ );
213
+
214
+ /*
215
+ * No pending schema migration means the connect() failure was not a pending
216
+ * migration failing (e.g. an entity-metadata error) — don't misattribute it.
217
+ */
218
+ if (!firstPending) {
219
+ return;
220
+ }
221
+
222
+ await recordMigrationFailure(diagnosticDataSource, {
223
+ migrationName: firstPending,
224
+ migrationType: MigrationFailureType.PostgresSchema,
225
+ error: originalError,
226
+ });
227
+ } catch (err) {
228
+ // Best-effort diagnostics only — swallow everything.
229
+ logger.warn("Could not record Postgres schema migration failure.");
230
+ logger.warn(err);
231
+ } finally {
232
+ try {
233
+ if (diagnosticDataSource && diagnosticDataSource.isInitialized) {
234
+ await diagnosticDataSource.destroy();
235
+ }
236
+ } catch (destroyErr) {
237
+ logger.warn(destroyErr);
238
+ }
239
+ }
240
+ }
@@ -0,0 +1,41 @@
1
+ import { AppApiHostname } from "../EnvironmentConfig";
2
+ import ClusterKeyAuthorization from "../Middleware/ClusterKeyAuthorization";
3
+ import logger from "./Logger";
4
+ import Protocol from "../../Types/API/Protocol";
5
+ import Route from "../../Types/API/Route";
6
+ import URL from "../../Types/API/URL";
7
+ import API from "../../Utils/API";
8
+ import ObjectID from "../../Types/ObjectID";
9
+
10
+ /*
11
+ * Releases a provisioned incoming-call number on the call provider by delegating
12
+ * to the notification app's internal endpoint (the Common layer cannot reach the
13
+ * provider directly). Best-effort: failures are logged but never thrown, so a
14
+ * provider/network issue during cleanup does not block the parent delete.
15
+ */
16
+ export default async function releaseIncomingCallPhoneNumber(data: {
17
+ projectCallSMSConfigId: ObjectID;
18
+ callProviderPhoneNumberId: string;
19
+ }): Promise<void> {
20
+ try {
21
+ await API.post({
22
+ url: new URL(
23
+ Protocol.HTTP,
24
+ AppApiHostname,
25
+ new Route("/api/notification/phone-number/internal/release"),
26
+ ),
27
+ data: {
28
+ projectCallSMSConfigId: data.projectCallSMSConfigId.toString(),
29
+ callProviderPhoneNumberId: data.callProviderPhoneNumberId,
30
+ },
31
+ headers: {
32
+ ...ClusterKeyAuthorization.getClusterKeyHeaders(),
33
+ },
34
+ });
35
+ } catch (err) {
36
+ logger.error(
37
+ "Failed to release incoming-call phone number on the provider during cleanup:",
38
+ );
39
+ logger.error(err);
40
+ }
41
+ }
@@ -49,6 +49,13 @@ export interface LLMUsage {
49
49
  promptTokens: number;
50
50
  completionTokens: number;
51
51
  totalTokens: number;
52
+ /*
53
+ * Prompt-caching breakdown, when the provider reports it. cachedInputTokens
54
+ * are input tokens served from cache (billed at a large discount);
55
+ * cacheCreationTokens are input tokens written to the cache on this call.
56
+ */
57
+ cachedInputTokens?: number | undefined;
58
+ cacheCreationTokens?: number | undefined;
52
59
  }
53
60
 
54
61
  export interface LLMCompletionResponse {
@@ -237,6 +244,17 @@ export default class LLMService {
237
244
  const toolCalls: Array<LLMToolCall> | undefined =
238
245
  this.parseOpenAIToolCalls(message);
239
246
 
247
+ /*
248
+ * OpenAI (and Azure OpenAI) automatically cache a stable prompt prefix
249
+ * once it is long enough and report the cache hit under
250
+ * prompt_tokens_details.cached_tokens — surface it for cost visibility.
251
+ */
252
+ const cachedTokens: number | undefined = usage
253
+ ? ((usage["prompt_tokens_details"] as JSONObject | undefined)?.[
254
+ "cached_tokens"
255
+ ] as number | undefined)
256
+ : undefined;
257
+
240
258
  return {
241
259
  content: (message["content"] as string) || "",
242
260
  toolCalls: toolCalls,
@@ -246,6 +264,7 @@ export default class LLMService {
246
264
  promptTokens: usage["prompt_tokens"] as number,
247
265
  completionTokens: usage["completion_tokens"] as number,
248
266
  totalTokens: usage["total_tokens"] as number,
267
+ cachedInputTokens: cachedTokens || undefined,
249
268
  }
250
269
  : undefined,
251
270
  };
@@ -565,6 +584,32 @@ export default class LLMService {
565
584
  continue;
566
585
  }
567
586
 
587
+ /*
588
+ * Anthropic requires strictly alternating user/assistant turns and
589
+ * returns a 400 on two consecutive same-role messages. The agent loop
590
+ * can legitimately emit back-to-back user turns (e.g. a tool_result
591
+ * user message immediately followed by the "budget exhausted, answer
592
+ * now" nudge), so coalesce a run of same-role messages into one instead
593
+ * of failing the whole request.
594
+ */
595
+ const previousMessage: JSONObject | undefined =
596
+ anthropicMessages[anthropicMessages.length - 1];
597
+
598
+ if (previousMessage && previousMessage["role"] === msg.role) {
599
+ if (typeof previousMessage["content"] === "string") {
600
+ previousMessage["content"] =
601
+ `${previousMessage["content"] as string}\n\n${msg.content}`;
602
+ continue;
603
+ }
604
+ if (Array.isArray(previousMessage["content"])) {
605
+ (previousMessage["content"] as Array<JSONObject>).push({
606
+ type: "text",
607
+ text: msg.content,
608
+ });
609
+ continue;
610
+ }
611
+ }
612
+
568
613
  anthropicMessages.push({
569
614
  role: msg.role,
570
615
  content: msg.content,
@@ -602,18 +647,43 @@ export default class LLMService {
602
647
  max_tokens: request.maxTokens || LLMService.ANTHROPIC_DEFAULT_MAX_TOKENS,
603
648
  };
604
649
 
650
+ /*
651
+ * Prompt caching. The system prompt and tool definitions are the large,
652
+ * stable prefix re-sent on every turn of the agent loop, so caching them is
653
+ * the biggest single cost + latency lever (cached input is billed at ~10%).
654
+ * An ephemeral cache_control breakpoint on the system block and on the LAST
655
+ * tool caches the whole system + tools prefix. cache_control is GA under
656
+ * anthropic-version 2023-06-01, so no beta header is required.
657
+ */
605
658
  if (systemMessage) {
606
- requestData["system"] = systemMessage;
659
+ requestData["system"] = [
660
+ {
661
+ type: "text",
662
+ text: systemMessage,
663
+ cache_control: { type: "ephemeral" },
664
+ },
665
+ ];
607
666
  }
608
667
 
609
668
  if (request.tools && request.tools.length > 0) {
610
- requestData["tools"] = request.tools.map((tool: LLMToolDefinition) => {
611
- return {
612
- name: tool.name,
613
- description: tool.description,
614
- input_schema: tool.inputSchema,
615
- };
616
- });
669
+ const anthropicTools: Array<JSONObject> = request.tools.map(
670
+ (tool: LLMToolDefinition) => {
671
+ return {
672
+ name: tool.name,
673
+ description: tool.description,
674
+ input_schema: tool.inputSchema,
675
+ };
676
+ },
677
+ );
678
+
679
+ // A breakpoint on the last tool caches the entire tools block before it.
680
+ const lastTool: JSONObject | undefined =
681
+ anthropicTools[anthropicTools.length - 1];
682
+ if (lastTool) {
683
+ lastTool["cache_control"] = { type: "ephemeral" };
684
+ }
685
+
686
+ requestData["tools"] = anthropicTools;
617
687
  }
618
688
 
619
689
  const response: HTTPErrorResponse | HTTPResponse<JSONObject> =
@@ -685,10 +755,21 @@ export default class LLMService {
685
755
  stopReason: jsonData["stop_reason"] === "tool_use" ? "tool_use" : "stop",
686
756
  usage: usage
687
757
  ? {
688
- promptTokens: usage["input_tokens"] as number,
689
- completionTokens: usage["output_tokens"] as number,
758
+ promptTokens: (usage["input_tokens"] as number) || 0,
759
+ completionTokens: (usage["output_tokens"] as number) || 0,
760
+ /*
761
+ * Anthropic reports cache-read and cache-creation input tokens
762
+ * separately from input_tokens. Surface them and fold them into
763
+ * totalTokens so the logged total reflects the full billable input.
764
+ */
765
+ cachedInputTokens:
766
+ (usage["cache_read_input_tokens"] as number) || undefined,
767
+ cacheCreationTokens:
768
+ (usage["cache_creation_input_tokens"] as number) || undefined,
690
769
  totalTokens:
691
770
  ((usage["input_tokens"] as number) || 0) +
771
+ ((usage["cache_read_input_tokens"] as number) || 0) +
772
+ ((usage["cache_creation_input_tokens"] as number) || 0) +
692
773
  ((usage["output_tokens"] as number) || 0),
693
774
  }
694
775
  : undefined,
@@ -812,11 +893,29 @@ export default class LLMService {
812
893
  });
813
894
  }
814
895
 
896
+ /*
897
+ * Ollama reports token counts on the final /api/chat response as
898
+ * prompt_eval_count (input) and eval_count (output). Populate usage from
899
+ * them so LlmLog, the AI dashboards and (costed self-hosted Ollama)
900
+ * billing are not silently blind to token spend.
901
+ */
902
+ const ollamaPromptTokens: number =
903
+ (jsonData["prompt_eval_count"] as number) || 0;
904
+ const ollamaCompletionTokens: number =
905
+ (jsonData["eval_count"] as number) || 0;
906
+
815
907
  return {
816
908
  content: (message["content"] as string) || "",
817
909
  toolCalls: toolCalls,
818
910
  stopReason: toolCalls && toolCalls.length > 0 ? "tool_use" : "stop",
819
- usage: undefined, // Ollama doesn't provide token usage in the same way
911
+ usage:
912
+ ollamaPromptTokens || ollamaCompletionTokens
913
+ ? {
914
+ promptTokens: ollamaPromptTokens,
915
+ completionTokens: ollamaCompletionTokens,
916
+ totalTokens: ollamaPromptTokens + ollamaCompletionTokens,
917
+ }
918
+ : undefined,
820
919
  };
821
920
  }
822
921
  }
@@ -141,31 +141,34 @@ function parseUnixNanoToDate(
141
141
  value: string | number | undefined,
142
142
  context: string,
143
143
  ): Date {
144
- let numericValue: number = OneUptimeDate.getCurrentDateAsUnixNano();
145
-
146
144
  if (value !== undefined && value !== null) {
147
145
  try {
148
146
  if (typeof value === "string") {
149
- const parsed: number = Number.parseFloat(value);
150
- if (isNaN(parsed)) {
147
+ const trimmed: string = value.trim();
148
+ if (trimmed === "" || isNaN(Number(trimmed))) {
151
149
  throw new Error(`Invalid timestamp string: ${value}`);
152
150
  }
153
- numericValue = parsed;
154
- } else if (typeof value === "number") {
151
+ /*
152
+ * Hand the raw nanosecond string to fromUnixNano so it can divide
153
+ * with BigInt — nanosecond epochs exceed Number.MAX_SAFE_INTEGER, so
154
+ * parsing to a float here would drop up to a full millisecond.
155
+ */
156
+ return OneUptimeDate.fromUnixNano(trimmed);
157
+ }
158
+ if (typeof value === "number") {
155
159
  if (!Number.isFinite(value)) {
156
160
  throw new Error(`Invalid timestamp number: ${value}`);
157
161
  }
158
- numericValue = value;
162
+ return OneUptimeDate.fromUnixNano(value);
159
163
  }
160
164
  } catch (error) {
161
165
  logger.warn(
162
166
  `Error processing ${context}: ${error instanceof Error ? error.message : String(error)}, using current time`,
163
167
  );
164
- numericValue = OneUptimeDate.getCurrentDateAsUnixNano();
165
168
  }
166
169
  }
167
170
 
168
- return OneUptimeDate.fromUnixNano(numericValue);
171
+ return OneUptimeDate.getCurrentDate();
169
172
  }
170
173
 
171
174
  // Same trim-or-null read contract as OtelIngestBaseService.getStringAttribute.
@@ -242,31 +242,34 @@ function parseUnixNanoToDate(
242
242
  value: string | number | undefined,
243
243
  context: string,
244
244
  ): Date {
245
- let numericValue: number = OneUptimeDate.getCurrentDateAsUnixNano();
246
-
247
245
  if (value !== undefined && value !== null) {
248
246
  try {
249
247
  if (typeof value === "string") {
250
- const parsed: number = Number.parseFloat(value);
251
- if (isNaN(parsed)) {
248
+ const trimmed: string = value.trim();
249
+ if (trimmed === "" || isNaN(Number(trimmed))) {
252
250
  throw new Error(`Invalid timestamp string: ${value}`);
253
251
  }
254
- numericValue = parsed;
255
- } else if (typeof value === "number") {
252
+ /*
253
+ * Hand the raw nanosecond string to fromUnixNano so it can divide
254
+ * with BigInt — nanosecond epochs exceed Number.MAX_SAFE_INTEGER, so
255
+ * parsing to a float here would drop up to a full millisecond.
256
+ */
257
+ return OneUptimeDate.fromUnixNano(trimmed);
258
+ }
259
+ if (typeof value === "number") {
256
260
  if (!Number.isFinite(value)) {
257
261
  throw new Error(`Invalid timestamp number: ${value}`);
258
262
  }
259
- numericValue = value;
263
+ return OneUptimeDate.fromUnixNano(value);
260
264
  }
261
265
  } catch (error) {
262
266
  logger.warn(
263
267
  `Error processing ${context}: ${error instanceof Error ? error.message : String(error)}, using current time`,
264
268
  );
265
- numericValue = OneUptimeDate.getCurrentDateAsUnixNano();
266
269
  }
267
270
  }
268
271
 
269
- return OneUptimeDate.fromUnixNano(numericValue);
272
+ return OneUptimeDate.getCurrentDate();
270
273
  }
271
274
 
272
275
  // Same trim-or-null read contract as OtelIngestBaseService.getStringAttribute.
@@ -103,6 +103,7 @@ jest.mock("../../../Server/Services/ProjectService", () => {
103
103
 
104
104
  jest.mock("../../../Server/EnvironmentConfig", () => {
105
105
  return {
106
+ ...jest.requireActual("../../../Server/EnvironmentConfig"),
106
107
  IsBillingEnabled: true,
107
108
  LogLevel: "INFO", // Use string literal instead of ConfigLogLevel.INFO
108
109
  DisableTelemetry: true,
@@ -234,14 +234,6 @@ describe("AnalyticsDatabaseService", () => {
234
234
  jest.spyOn(logger, "error").mockImplementation(() => {
235
235
  return undefined!;
236
236
  });
237
-
238
- /*
239
- * The base getException method is async and throws without await in the
240
- * catch block, creating unhandled rejections. Override to throw synchronously.
241
- */
242
- (service as any).getException = (error: Error): never => {
243
- throw error;
244
- };
245
237
  });
246
238
 
247
239
  afterEach(() => {