@oneuptime/common 14.0.9 → 14.0.10

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 (383) hide show
  1. package/Models/DatabaseModels/AutoRemediationSuggestion.ts +60 -0
  2. package/Models/DatabaseModels/CephCluster.ts +213 -0
  3. package/Models/DatabaseModels/DatabaseServer.ts +213 -0
  4. package/Models/DatabaseModels/DockerHost.ts +213 -0
  5. package/Models/DatabaseModels/DockerSwarmCluster.ts +213 -0
  6. package/Models/DatabaseModels/GlobalConfig.ts +1 -1
  7. package/Models/DatabaseModels/Host.ts +213 -0
  8. package/Models/DatabaseModels/Index.ts +2 -0
  9. package/Models/DatabaseModels/PodmanHost.ts +213 -0
  10. package/Models/DatabaseModels/ProxmoxCluster.ts +213 -0
  11. package/Models/DatabaseModels/ResourceAiAgent.ts +419 -0
  12. package/Models/DatabaseModels/RunnerJob.ts +144 -0
  13. package/Models/DatabaseModels/VMwareVCenter.ts +213 -0
  14. package/Server/API/AutoRemediationAPI.ts +392 -1
  15. package/Server/API/ResourceAiAccessAPI.ts +1669 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.ts +423 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  18. package/Server/Infrastructure/Semaphore.ts +22 -0
  19. package/Server/Middleware/TelemetryIngest.ts +15 -5
  20. package/Server/Services/AnalyticsDatabaseService.ts +45 -1
  21. package/Server/Services/AutoRemediationRuleEngineService.ts +940 -112
  22. package/Server/Services/CephClusterService.ts +109 -1
  23. package/Server/Services/DatabaseServerService.ts +116 -1
  24. package/Server/Services/DockerHostService.ts +109 -1
  25. package/Server/Services/DockerSwarmClusterService.ts +113 -1
  26. package/Server/Services/HostService.ts +126 -4
  27. package/Server/Services/MetricRecordingRuleService.ts +47 -0
  28. package/Server/Services/PodmanHostService.ts +109 -1
  29. package/Server/Services/ProxmoxClusterService.ts +110 -1
  30. package/Server/Services/ResourceAiAccessService.ts +1439 -0
  31. package/Server/Services/ResourceAiAgentJobService.ts +202 -0
  32. package/Server/Services/ResourceAiAgentService.ts +2432 -0
  33. package/Server/Services/RunnerJobService.ts +611 -4
  34. package/Server/Services/TelemetryUsageBillingService.ts +28 -0
  35. package/Server/Services/TraceRecordingRuleService.ts +33 -0
  36. package/Server/Services/VMwareVCenterService.ts +110 -1
  37. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1434 -32
  38. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +935 -21
  39. package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +25 -1
  40. package/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.ts +577 -0
  41. package/Server/Utils/AI/ResourceAccess/ResourceAccessContext.ts +271 -0
  42. package/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.ts +45 -0
  43. package/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.ts +964 -0
  44. package/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.ts +333 -0
  45. package/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.ts +873 -0
  46. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +72 -2
  47. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +72 -5
  48. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +72 -5
  49. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +396 -13
  50. package/Server/Utils/AutoRemediation/RemediationVerifier.ts +35 -2
  51. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +9 -1
  52. package/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.ts +920 -0
  53. package/Server/Utils/SessionReplay/SessionReplayUsage.ts +84 -6
  54. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +35 -15
  55. package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +33 -15
  56. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +21 -1
  57. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +298 -224
  58. package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +35 -15
  59. package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +389 -175
  60. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +366 -143
  61. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.ts +163 -0
  62. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.ts +396 -0
  63. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.ts +418 -0
  64. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.ts +152 -0
  65. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.ts +251 -0
  66. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.ts +214 -0
  67. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +93 -0
  68. package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +18 -6
  69. package/Tests/App/Dashboard/InvestigationInfrastructureTools.test.tsx +506 -0
  70. package/Tests/App/Dashboard/RemediationSuggestionCardDescription.test.tsx +209 -0
  71. package/Tests/App/Dashboard/RemediationSuggestionCardResource.test.tsx +317 -0
  72. package/Tests/App/Dashboard/ResourceAiAccessSettingsUtil.test.ts +921 -0
  73. package/Tests/App/Dashboard/ResourceAiAgentInstall.test.ts +860 -0
  74. package/Tests/App/Dashboard/ResourceAiAgentPage.test.tsx +1721 -0
  75. package/Tests/App/Dashboard/ResourceAiAgentStatus.test.ts +1002 -0
  76. package/Tests/App/Dashboard/ResourceAiInsightsPage.test.tsx +931 -0
  77. package/Tests/App/Dashboard/ResourceAiNavigation.test.tsx +575 -0
  78. package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +38 -7
  79. package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +33 -1
  80. package/Tests/Models/DatabaseModels/ResourceAiAccessColumns.test.ts +738 -0
  81. package/Tests/Models/DatabaseModels/ResourceAiAgentModel.test.ts +842 -0
  82. package/Tests/Server/API/AutoRemediationApproveResourceRound.test.ts +835 -0
  83. package/Tests/Server/API/ResourceAiAccessAPI.test.ts +2730 -0
  84. package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +60 -0
  85. package/Tests/Server/Infrastructure/Postgres/AddResourceAiAgentsMigration.test.ts +659 -0
  86. package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +31 -0
  87. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +63 -5
  88. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +103 -5
  89. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +19 -1
  90. package/Tests/Server/Services/AutoRemediationResourceRuleEngine.test.ts +1279 -0
  91. package/Tests/Server/Services/DatabaseServerService.test.ts +55 -0
  92. package/Tests/Server/Services/GroupTelemetryUsageExcludeNames.test.ts +299 -0
  93. package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +44 -0
  94. package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +45 -6
  95. package/Tests/Server/Services/RecordingRuleReservedMetricName.test.ts +171 -0
  96. package/Tests/Server/Services/ResourceAiAccessChangeAuthorization.test.ts +256 -0
  97. package/Tests/Server/Services/ResourceAiAccessService.test.ts +1373 -0
  98. package/Tests/Server/Services/ResourceAiAgentJobService.test.ts +375 -0
  99. package/Tests/Server/Services/ResourceAiAgentServiceHelpers.test.ts +1114 -0
  100. package/Tests/Server/Services/ResourceAiAgentServiceLifecycle.test.ts +1050 -0
  101. package/Tests/Server/Services/ResourceAiAgentServiceRegister.test.ts +2251 -0
  102. package/Tests/Server/Services/ResourceAiSettingsCreate.test.ts +373 -0
  103. package/Tests/Server/Services/ResourceAiSettingsPermission.test.ts +1042 -0
  104. package/Tests/Server/Services/ResourceServiceDeleteCleansUpAi.test.ts +319 -0
  105. package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +105 -0
  106. package/Tests/Server/Services/RunnerJobEnqueueResourceCommand.test.ts +986 -0
  107. package/Tests/Server/Services/RunnerJobResourceCommandLane.test.ts +211 -0
  108. package/Tests/Server/Services/RunnerJobResourceCommandTimeoutAndRedaction.test.ts +352 -0
  109. package/Tests/Server/Services/TelemetryUsageBillingSloExclusion.test.ts +218 -0
  110. package/Tests/Server/TestingUtils/Services/FakeRunnerJobCount.ts +145 -0
  111. package/Tests/Server/Utils/AI/InvestigationInfrastructureAccessWiring.test.ts +453 -0
  112. package/Tests/Server/Utils/AI/InvestigationInfrastructureReport.test.ts +721 -0
  113. package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +94 -1
  114. package/Tests/Server/Utils/AI/RemediationCommandToolsResource.test.ts +1530 -0
  115. package/Tests/Server/Utils/AI/RemediationExecutionRunnerResourceMode.test.ts +1249 -0
  116. package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +117 -0
  117. package/Tests/Server/Utils/AI/RemediationResourceCopyParity.test.ts +463 -0
  118. package/Tests/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.test.ts +722 -0
  119. package/Tests/Server/Utils/AI/ResourceAccess/ResourceAccessContext.test.ts +266 -0
  120. package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.test.ts +1220 -0
  121. package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.test.ts +569 -0
  122. package/Tests/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.test.ts +830 -0
  123. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorResource.test.ts +717 -0
  124. package/Tests/Server/Utils/AutoRemediation/RemediationVerifierResourceFollowUp.test.ts +338 -0
  125. package/Tests/Server/Utils/Monitor/Criteria/SessionReplayBudgetTemplateCriteria.test.ts +422 -0
  126. package/Tests/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.test.ts +1647 -0
  127. package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +143 -1
  128. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +33 -3
  129. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsAccountNotLinked.test.ts +1093 -0
  130. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.test.ts +1049 -0
  131. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.test.ts +1676 -0
  132. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCards.test.ts +2884 -0
  133. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.test.ts +2490 -0
  134. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmit.test.ts +2293 -0
  135. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmitServerTimezone.test.ts +386 -0
  136. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.test.ts +1477 -0
  137. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.test.ts +1967 -0
  138. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsStripHtmlTags.test.ts +92 -0
  139. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsSubmittedFormRemoval.test.ts +1819 -0
  140. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.test.ts +1033 -0
  141. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezoneServerZone.test.ts +610 -0
  142. package/Tests/Server/Utils/Workspace/MicrosoftTeamsBotMessageHandling.test.ts +2610 -0
  143. package/Tests/Server/Utils/Workspace/MicrosoftTeamsCreateCommandsEndToEnd.test.ts +4672 -0
  144. package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +190 -65
  145. package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +10 -4
  146. package/Tests/Types/AutoRemediation/AiRemediationCommandPlanResourceCommand.test.ts +305 -0
  147. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +277 -13
  148. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +113 -0
  149. package/Tests/Types/Monitor/RumAlertTemplates.test.ts +773 -2
  150. package/Tests/Types/ResourceAiAgent/AiResourceType.test.ts +419 -0
  151. package/Tests/Types/ResourceAiAgent/ResourceAiAccess.test.ts +517 -0
  152. package/Tests/Types/Runbook/RunbookStepType.test.ts +147 -8
  153. package/Tests/UI/Rum/RecordingHealthDashboard.test.tsx +769 -1
  154. package/Tests/Utils/AiRemediation/Resource/CephCommandPolicy.test.ts +1653 -0
  155. package/Tests/Utils/AiRemediation/Resource/CephOutputRedaction.test.ts +204 -0
  156. package/Tests/Utils/AiRemediation/Resource/DatabaseCommandPolicy.test.ts +1077 -0
  157. package/Tests/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.test.ts +558 -0
  158. package/Tests/Utils/AiRemediation/Resource/DatabaseQueryRedactor.test.ts +834 -0
  159. package/Tests/Utils/AiRemediation/Resource/DockerCliGrammar.test.ts +747 -0
  160. package/Tests/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.test.ts +1259 -0
  161. package/Tests/Utils/AiRemediation/Resource/DockerOutputRedaction.test.ts +386 -0
  162. package/Tests/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.test.ts +782 -0
  163. package/Tests/Utils/AiRemediation/Resource/GovcCommandPolicy.test.ts +2259 -0
  164. package/Tests/Utils/AiRemediation/Resource/HostCommandPolicy.test.ts +2019 -0
  165. package/Tests/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.test.ts +2497 -0
  166. package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicy.test.ts +1305 -0
  167. package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.test.ts +585 -0
  168. package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactor.test.ts +334 -0
  169. package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactorCopyParity.test.ts +98 -0
  170. package/Tests/Utils/AiRemediation/Resource/ResourcePolicyImportClosure.test.ts +391 -0
  171. package/Tests/Utils/AiRemediation/ResourceAiAgentPolicyCopyParity.test.ts +497 -0
  172. package/Tests/Utils/SessionReplay/SessionReplayBudgetMetricType.test.ts +383 -0
  173. package/Types/AI/ResourceAiAccessApi.ts +140 -0
  174. package/Types/AI/ResourceAiAccessPermissions.ts +126 -0
  175. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +183 -2
  176. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +55 -22
  177. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +35 -3
  178. package/Types/Monitor/RumAlertTemplates.ts +351 -4
  179. package/Types/ResourceAiAgent/AiResourceType.ts +310 -0
  180. package/Types/ResourceAiAgent/ResourceAiAccess.ts +574 -0
  181. package/Types/Rum/SessionReplayBudgetMetricType.ts +47 -0
  182. package/Types/Runbook/RunbookStepType.ts +29 -0
  183. package/Types/Telemetry/TelemetryIngestSurface.ts +14 -2
  184. package/Utils/AI/InvestigationReport.ts +121 -15
  185. package/Utils/AiRemediation/Resource/CephCommandPolicy.ts +1936 -0
  186. package/Utils/AiRemediation/Resource/DatabaseCommandPolicy.ts +166 -0
  187. package/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.ts +1532 -0
  188. package/Utils/AiRemediation/Resource/DatabaseQueryRedactor.ts +1394 -0
  189. package/Utils/AiRemediation/Resource/DockerCliGrammar.ts +1839 -0
  190. package/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.ts +490 -0
  191. package/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.ts +687 -0
  192. package/Utils/AiRemediation/Resource/GovcCommandPolicy.ts +2096 -0
  193. package/Utils/AiRemediation/Resource/HostCommandPolicy.ts +2982 -0
  194. package/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.ts +1679 -0
  195. package/Utils/AiRemediation/Resource/ResourceCommandPolicy.ts +851 -0
  196. package/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.ts +593 -0
  197. package/Utils/AiRemediation/Resource/ResourceOutputRedactor.ts +2812 -0
  198. package/Utils/SessionReplay/SessionReplayBudgetMetricType.ts +184 -0
  199. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +62 -0
  200. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
  201. package/build/dist/Models/DatabaseModels/CephCluster.js +219 -0
  202. package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
  203. package/build/dist/Models/DatabaseModels/DatabaseServer.js +219 -0
  204. package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -1
  205. package/build/dist/Models/DatabaseModels/DockerHost.js +219 -0
  206. package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
  207. package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js +219 -0
  208. package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js.map +1 -1
  209. package/build/dist/Models/DatabaseModels/GlobalConfig.js +1 -1
  210. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  211. package/build/dist/Models/DatabaseModels/Host.js +219 -0
  212. package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
  213. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  214. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  215. package/build/dist/Models/DatabaseModels/PodmanHost.js +219 -0
  216. package/build/dist/Models/DatabaseModels/PodmanHost.js.map +1 -1
  217. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +219 -0
  218. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
  219. package/build/dist/Models/DatabaseModels/ResourceAiAgent.js +439 -0
  220. package/build/dist/Models/DatabaseModels/ResourceAiAgent.js.map +1 -0
  221. package/build/dist/Models/DatabaseModels/RunnerJob.js +145 -0
  222. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  223. package/build/dist/Models/DatabaseModels/VMwareVCenter.js +219 -0
  224. package/build/dist/Models/DatabaseModels/VMwareVCenter.js.map +1 -1
  225. package/build/dist/Server/API/AutoRemediationAPI.js +231 -1
  226. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  227. package/build/dist/Server/API/ResourceAiAccessAPI.js +1119 -0
  228. package/build/dist/Server/API/ResourceAiAccessAPI.js.map +1 -0
  229. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js +168 -0
  230. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js.map +1 -0
  231. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  232. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  233. package/build/dist/Server/Infrastructure/Semaphore.js +6 -0
  234. package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
  235. package/build/dist/Server/Middleware/TelemetryIngest.js +12 -5
  236. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  237. package/build/dist/Server/Services/AnalyticsDatabaseService.js +26 -1
  238. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  239. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +601 -3
  240. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
  241. package/build/dist/Server/Services/CephClusterService.js +104 -0
  242. package/build/dist/Server/Services/CephClusterService.js.map +1 -1
  243. package/build/dist/Server/Services/DatabaseServerService.js +99 -0
  244. package/build/dist/Server/Services/DatabaseServerService.js.map +1 -1
  245. package/build/dist/Server/Services/DockerHostService.js +104 -0
  246. package/build/dist/Server/Services/DockerHostService.js.map +1 -1
  247. package/build/dist/Server/Services/DockerSwarmClusterService.js +104 -0
  248. package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
  249. package/build/dist/Server/Services/HostService.js +114 -2
  250. package/build/dist/Server/Services/HostService.js.map +1 -1
  251. package/build/dist/Server/Services/MetricRecordingRuleService.js +46 -0
  252. package/build/dist/Server/Services/MetricRecordingRuleService.js.map +1 -1
  253. package/build/dist/Server/Services/PodmanHostService.js +104 -0
  254. package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
  255. package/build/dist/Server/Services/ProxmoxClusterService.js +104 -0
  256. package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
  257. package/build/dist/Server/Services/ResourceAiAccessService.js +1028 -0
  258. package/build/dist/Server/Services/ResourceAiAccessService.js.map +1 -0
  259. package/build/dist/Server/Services/ResourceAiAgentJobService.js +173 -0
  260. package/build/dist/Server/Services/ResourceAiAgentJobService.js.map +1 -0
  261. package/build/dist/Server/Services/ResourceAiAgentService.js +1687 -0
  262. package/build/dist/Server/Services/ResourceAiAgentService.js.map +1 -0
  263. package/build/dist/Server/Services/RunnerJobService.js +344 -5
  264. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  265. package/build/dist/Server/Services/TelemetryUsageBillingService.js +26 -0
  266. package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
  267. package/build/dist/Server/Services/TraceRecordingRuleService.js +37 -0
  268. package/build/dist/Server/Services/TraceRecordingRuleService.js.map +1 -1
  269. package/build/dist/Server/Services/VMwareVCenterService.js +104 -0
  270. package/build/dist/Server/Services/VMwareVCenterService.js.map +1 -1
  271. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1029 -30
  272. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  273. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +633 -28
  274. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  275. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +21 -1
  276. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -1
  277. package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js +330 -0
  278. package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js.map +1 -0
  279. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js +160 -0
  280. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js.map +1 -0
  281. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js +33 -0
  282. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js.map +1 -0
  283. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js +640 -0
  284. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js.map +1 -0
  285. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js +226 -0
  286. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js.map +1 -0
  287. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js +600 -0
  288. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js.map +1 -0
  289. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +50 -4
  290. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  291. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +55 -4
  292. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  293. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +55 -4
  294. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  295. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +290 -13
  296. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
  297. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +29 -1
  298. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
  299. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +9 -1
  300. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  301. package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js +627 -0
  302. package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js.map +1 -0
  303. package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js +60 -5
  304. package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js.map +1 -1
  305. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +19 -15
  306. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
  307. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +19 -15
  308. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
  309. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +17 -1
  310. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
  311. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +183 -209
  312. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
  313. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +19 -15
  314. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
  315. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +233 -167
  316. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
  317. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +263 -110
  318. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  319. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js +129 -0
  320. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js.map +1 -0
  321. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js +266 -0
  322. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js.map +1 -0
  323. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js +258 -0
  324. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js.map +1 -0
  325. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js +104 -0
  326. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js.map +1 -0
  327. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js +183 -0
  328. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js.map +1 -0
  329. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js +123 -0
  330. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js.map +1 -0
  331. package/build/dist/Types/AI/ResourceAiAccessApi.js +30 -0
  332. package/build/dist/Types/AI/ResourceAiAccessApi.js.map +1 -0
  333. package/build/dist/Types/AI/ResourceAiAccessPermissions.js +98 -0
  334. package/build/dist/Types/AI/ResourceAiAccessPermissions.js.map +1 -0
  335. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +116 -29
  336. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
  337. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +47 -21
  338. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  339. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +5 -0
  340. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  341. package/build/dist/Types/Monitor/RumAlertTemplates.js +199 -3
  342. package/build/dist/Types/Monitor/RumAlertTemplates.js.map +1 -1
  343. package/build/dist/Types/ResourceAiAgent/AiResourceType.js +242 -0
  344. package/build/dist/Types/ResourceAiAgent/AiResourceType.js.map +1 -0
  345. package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js +275 -0
  346. package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js.map +1 -0
  347. package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js +45 -0
  348. package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js.map +1 -0
  349. package/build/dist/Types/Runbook/RunbookStepType.js +27 -0
  350. package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
  351. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +13 -2
  352. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
  353. package/build/dist/Utils/AI/InvestigationReport.js +71 -11
  354. package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
  355. package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js +1462 -0
  356. package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js.map +1 -0
  357. package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js +122 -0
  358. package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js.map +1 -0
  359. package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js +1139 -0
  360. package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js.map +1 -0
  361. package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js +1060 -0
  362. package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js.map +1 -0
  363. package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js +1301 -0
  364. package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js.map +1 -0
  365. package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js +370 -0
  366. package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js.map +1 -0
  367. package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js +498 -0
  368. package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js.map +1 -0
  369. package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js +1565 -0
  370. package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js.map +1 -0
  371. package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js +2206 -0
  372. package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js.map +1 -0
  373. package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js +1129 -0
  374. package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js.map +1 -0
  375. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js +565 -0
  376. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js.map +1 -0
  377. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js +408 -0
  378. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js.map +1 -0
  379. package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js +1910 -0
  380. package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js.map +1 -0
  381. package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js +160 -0
  382. package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js.map +1 -0
  383. package/package.json +1 -1
@@ -1254,6 +1254,61 @@ describe("DatabaseServerService.findOrCreateByEndpoint", () => {
1254
1254
  expect(feed.feedInfoInMarkdown).toContain("restored from the archive");
1255
1255
  });
1256
1256
 
1257
+ /*
1258
+ * A lookup that saw no traffic (a resource AI agent registering) returns
1259
+ * the owner as it is: an archived row stays archived, and neither the
1260
+ * endpoint's last match nor the row's engine is touched.
1261
+ */
1262
+ test("isSighting: false returns the owner without sighting it", async () => {
1263
+ const archived: DatabaseServer = databaseRow({
1264
+ discoverySource: DatabaseServerDiscoverySource.Collector,
1265
+ isArchived: true,
1266
+ autoArchivedAt: new Date("2026-09-01T00:00:00.000Z"),
1267
+ });
1268
+ const owner: any = { databaseServerId: archived.id };
1269
+ findOwner.mockResolvedValue(owner);
1270
+ findOneBy.mockResolvedValue(archived);
1271
+ rawQuery.mockResolvedValue([{ _id: archived.id!.toString() }]);
1272
+ const markMatched: jest.SpyInstance = getJestSpyOn(
1273
+ DatabaseServerEndpointService,
1274
+ "markEndpointMatched",
1275
+ ).mockResolvedValue(undefined);
1276
+ const evidence: jest.SpyInstance = getJestSpyOn(
1277
+ service,
1278
+ "applyDatabaseSystemEvidence",
1279
+ ).mockResolvedValue(undefined);
1280
+
1281
+ const result: DatabaseServer | null =
1282
+ await DatabaseServerService.findOrCreateByEndpoint({
1283
+ projectId: PROJECT_ID,
1284
+ dbSystem: "postgresql",
1285
+ endpoint: ORDERS_ENDPOINT,
1286
+ discoverySource: DatabaseServerDiscoverySource.Collector,
1287
+ allowCreate: true,
1288
+ isSighting: false,
1289
+ });
1290
+
1291
+ expect(result).toBe(archived);
1292
+ expect(result!.isArchived).toBe(true);
1293
+ expect(result!.autoArchivedAt).toBeDefined();
1294
+ expect(rawQuery).not.toHaveBeenCalled();
1295
+ expect(sideEffects.feed).not.toHaveBeenCalled();
1296
+ expect(markMatched).not.toHaveBeenCalled();
1297
+ expect(evidence).not.toHaveBeenCalled();
1298
+ expect(create).not.toHaveBeenCalled();
1299
+
1300
+ // The same lookup as a sighting restores it.
1301
+ await DatabaseServerService.findOrCreateByEndpoint({
1302
+ projectId: PROJECT_ID,
1303
+ dbSystem: "postgresql",
1304
+ endpoint: ORDERS_ENDPOINT,
1305
+ discoverySource: DatabaseServerDiscoverySource.Collector,
1306
+ allowCreate: true,
1307
+ });
1308
+ expect(markMatched).toHaveBeenCalledWith(owner);
1309
+ expect(rawQuery).toHaveBeenCalledTimes(1);
1310
+ });
1311
+
1257
1312
  test("a restore that lost to someone else writes no second feed item", async () => {
1258
1313
  const archived: DatabaseServer = databaseRow({
1259
1314
  discoverySource: DatabaseServerDiscoverySource.Collector,
@@ -0,0 +1,299 @@
1
+ import AnalyticsDatabaseService from "../../../Server/Services/AnalyticsDatabaseService";
2
+ import { LogService } from "../../../Server/Services/LogService";
3
+ import { MetricService } from "../../../Server/Services/MetricService";
4
+ import { SpanService } from "../../../Server/Services/SpanService";
5
+ import { Statement } from "../../../Server/Utils/AnalyticsDatabase/Statement";
6
+ import AnalyticsBaseModel from "../../../Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel";
7
+ import BadDataException from "../../../Types/Exception/BadDataException";
8
+ import { JSONObject } from "../../../Types/JSON";
9
+ import ObjectID from "../../../Types/ObjectID";
10
+ import ServiceType from "../../../Types/Telemetry/ServiceType";
11
+ import SessionReplayBudgetMetricTypeUtil from "../../../Utils/SessionReplay/SessionReplayBudgetMetricType";
12
+ import { describe, expect, test } from "@jest/globals";
13
+
14
+ /*
15
+ * The metrics billing scan is the only place OneUptime's own session replay
16
+ * budget rows can be told apart from a RUM application's web vitals: both are
17
+ * keyed to the application with primaryEntityType RealUserMonitor, so the
18
+ * (primaryEntityId, primaryEntityType) grouping merges them and only their
19
+ * names differ. These tests pin the SQL the scan generates - the exclusion
20
+ * sits inside the WHERE, binds the names as a parameter, and leaves the
21
+ * statement byte-for-byte unchanged when there is nothing to exclude - with no
22
+ * ClickHouse in the loop, the same way SessionReplayMetering.test.ts pins the
23
+ * replay scan.
24
+ */
25
+
26
+ const PROJECT_ID: ObjectID = new ObjectID("6512f3a9b7c4d2e108f5a3b9");
27
+ const START_OF_DAY: Date = new Date("2026-09-28T00:00:00.000Z");
28
+ const END_OF_DAY: Date = new Date("2026-09-28T23:59:59.999Z");
29
+
30
+ /*
31
+ * The statement as the scan generated it before excludeNames existed,
32
+ * captured from that code, up to the trailing SETTINGS clause (which belongs
33
+ * to QuerySettingsHelper and is pinned there).
34
+ */
35
+ const PRE_EXCLUSION_QUERY: string =
36
+ "SELECT primaryEntityId AS primaryEntityId, primaryEntityType AS primaryEntityType, count() AS rowCount, sum(byteSize(*)) AS estimatedBytes FROM {p0:Identifier}.{p1:Identifier} WHERE projectId = {p2:String} AND {p3:Identifier} >= {p4:DateTime64(9)} AND {p5:Identifier} <= {p6:DateTime64(9)} GROUP BY primaryEntityId, primaryEntityType";
37
+
38
+ const BUDGET_METRIC_NAMES: Array<string> =
39
+ SessionReplayBudgetMetricTypeUtil.getAll();
40
+
41
+ type UsageRow = {
42
+ primaryEntityId: string;
43
+ primaryEntityType: string | null;
44
+ rowCount: number;
45
+ estimatedBytes: number;
46
+ };
47
+
48
+ interface CapturedScan {
49
+ // Null when the scan refused before reaching ClickHouse.
50
+ query: string | null;
51
+ params: Record<string, unknown> | null;
52
+ result: Array<UsageRow>;
53
+ }
54
+
55
+ /*
56
+ * Runs the real groupTelemetryUsageByService with executeQuery replaced, so
57
+ * the generated statement is captured instead of sent. `excludeNames` is
58
+ * passed only when the key is present, so "absent" and "undefined" are
59
+ * separate cases.
60
+ */
61
+ async function captureUsageScan<TBaseModel extends AnalyticsBaseModel>(
62
+ service: AnalyticsDatabaseService<TBaseModel>,
63
+ options: {
64
+ excludeNames?: Array<string> | undefined;
65
+ responseRows?: Array<JSONObject> | undefined;
66
+ } = {},
67
+ ): Promise<CapturedScan> {
68
+ const captured: CapturedScan = { query: null, params: null, result: [] };
69
+
70
+ service.executeQuery = ((statement: Statement): Promise<unknown> => {
71
+ captured.query = statement.query;
72
+ captured.params = statement.query_params;
73
+
74
+ return Promise.resolve({
75
+ json: (): Promise<{ data: Array<JSONObject> }> => {
76
+ return Promise.resolve({ data: options.responseRows || [] });
77
+ },
78
+ });
79
+ }) as unknown as typeof service.executeQuery;
80
+
81
+ captured.result = await service.groupTelemetryUsageByService({
82
+ projectId: PROJECT_ID,
83
+ timestampColumnName: "time",
84
+ startDate: START_OF_DAY,
85
+ endDate: END_OF_DAY,
86
+ ...("excludeNames" in options
87
+ ? { excludeNames: options.excludeNames }
88
+ : {}),
89
+ });
90
+
91
+ return captured;
92
+ }
93
+
94
+ function queryBeforeSettings(query: string | null): string {
95
+ expect(query).not.toBeNull();
96
+
97
+ const settingsIndex: number = query!.indexOf(" SETTINGS ");
98
+ expect(settingsIndex).toBeGreaterThan(-1);
99
+
100
+ return query!.slice(0, settingsIndex);
101
+ }
102
+
103
+ describe("groupTelemetryUsageByService without excludeNames", () => {
104
+ test("generates exactly the statement it did before the option existed", async () => {
105
+ const scan: CapturedScan = await captureUsageScan(new MetricService());
106
+
107
+ expect(queryBeforeSettings(scan.query)).toBe(PRE_EXCLUSION_QUERY);
108
+ expect(scan.query).not.toContain("NOT IN");
109
+
110
+ // Same parameters too - p0 is the configured database name.
111
+ expect(Object.keys(scan.params!).sort()).toEqual(
112
+ ["p0", "p1", "p2", "p3", "p4", "p5", "p6"].sort(),
113
+ );
114
+ expect(scan.params!["p1"]).toBe("MetricItemV3");
115
+ expect(scan.params!["p2"]).toBe(PROJECT_ID.toString());
116
+ expect(scan.params!["p3"]).toBe("time");
117
+ expect(scan.params!["p5"]).toBe("time");
118
+ });
119
+
120
+ test.each([
121
+ ["an empty list", []],
122
+ ["an explicit undefined", undefined],
123
+ ])(
124
+ "treats %s as absent: the same statement, the same parameters",
125
+ async (_name: string, excludeNames: Array<string> | undefined) => {
126
+ const absent: CapturedScan = await captureUsageScan(new MetricService());
127
+ const given: CapturedScan = await captureUsageScan(new MetricService(), {
128
+ excludeNames,
129
+ });
130
+
131
+ expect(given.query).toBe(absent.query);
132
+ expect(given.params).toEqual(absent.params);
133
+ },
134
+ );
135
+
136
+ test("still scans a table with no name column when there is nothing to exclude", async () => {
137
+ const scan: CapturedScan = await captureUsageScan(new LogService(), {
138
+ excludeNames: [],
139
+ });
140
+
141
+ expect(scan.query).not.toBeNull();
142
+ expect(scan.query).not.toContain("NOT IN");
143
+ expect(scan.params!["p1"]).toBe("LogItemV3");
144
+ });
145
+ });
146
+
147
+ describe("groupTelemetryUsageByService with excludeNames", () => {
148
+ test("adds `AND name NOT IN` inside the WHERE: after the time window, before GROUP BY", async () => {
149
+ const scan: CapturedScan = await captureUsageScan(new MetricService(), {
150
+ excludeNames: BUDGET_METRIC_NAMES,
151
+ });
152
+
153
+ const query: string = scan.query!;
154
+
155
+ expect(query).toContain(
156
+ "AND {p5:Identifier} <= {p6:DateTime64(9)} AND name NOT IN {p7:Array(String)} GROUP BY primaryEntityId, primaryEntityType SETTINGS ",
157
+ );
158
+ expect(query.indexOf(" WHERE ")).toBeLessThan(query.indexOf("NOT IN"));
159
+ expect(query.indexOf("NOT IN")).toBeLessThan(query.indexOf(" GROUP BY "));
160
+ expect(query.indexOf(" GROUP BY ")).toBeLessThan(
161
+ query.indexOf(" SETTINGS "),
162
+ );
163
+
164
+ // Exactly one exclusion clause, and nothing else about the scan moved.
165
+ expect(query.split("NOT IN")).toHaveLength(2);
166
+ expect(
167
+ queryBeforeSettings(query).replace(
168
+ " AND name NOT IN {p7:Array(String)}",
169
+ "",
170
+ ),
171
+ ).toBe(PRE_EXCLUSION_QUERY);
172
+ });
173
+
174
+ test("binds every name, in order, as one Array(String) parameter", async () => {
175
+ const scan: CapturedScan = await captureUsageScan(new MetricService(), {
176
+ excludeNames: BUDGET_METRIC_NAMES,
177
+ });
178
+
179
+ expect(scan.params!["p7"]).toEqual(BUDGET_METRIC_NAMES);
180
+
181
+ // The names never reach the SQL text itself.
182
+ for (const name of BUDGET_METRIC_NAMES) {
183
+ expect(scan.query).not.toContain(name);
184
+ }
185
+ });
186
+
187
+ test("keeps a hostile name a parameter value, never SQL", async () => {
188
+ const hostileName: string = "x') OR 1=1 GROUP BY name --";
189
+
190
+ const scan: CapturedScan = await captureUsageScan(new MetricService(), {
191
+ excludeNames: [hostileName],
192
+ });
193
+
194
+ expect(scan.params!["p7"]).toEqual([hostileName]);
195
+ expect(scan.query).not.toContain("OR 1=1");
196
+ expect(scan.query).not.toContain("--");
197
+ expect(scan.query!.split("GROUP BY")).toHaveLength(2);
198
+ });
199
+
200
+ test("leaves the caller's list untouched", async () => {
201
+ const excludeNames: Array<string> = [...BUDGET_METRIC_NAMES];
202
+
203
+ await captureUsageScan(new MetricService(), { excludeNames });
204
+
205
+ expect(excludeNames).toEqual(BUDGET_METRIC_NAMES);
206
+ });
207
+
208
+ test("parses the grouped rows exactly as before", async () => {
209
+ const appId: string = ObjectID.generate().toString();
210
+
211
+ const scan: CapturedScan = await captureUsageScan(new MetricService(), {
212
+ excludeNames: BUDGET_METRIC_NAMES,
213
+ responseRows: [
214
+ {
215
+ primaryEntityId: appId,
216
+ primaryEntityType: ServiceType.RealUserMonitor,
217
+ rowCount: "12",
218
+ estimatedBytes: "3456",
219
+ },
220
+ // Unattributed rows are skipped, as they always were.
221
+ {
222
+ primaryEntityId: "",
223
+ primaryEntityType: ServiceType.OpenTelemetry,
224
+ rowCount: "1",
225
+ estimatedBytes: "1",
226
+ },
227
+ // A blank type reads as a legacy row.
228
+ {
229
+ primaryEntityId: PROJECT_ID.toString(),
230
+ primaryEntityType: " ",
231
+ rowCount: 2,
232
+ estimatedBytes: 0,
233
+ },
234
+ ],
235
+ });
236
+
237
+ expect(scan.result).toEqual([
238
+ {
239
+ primaryEntityId: appId,
240
+ primaryEntityType: ServiceType.RealUserMonitor,
241
+ rowCount: 12,
242
+ estimatedBytes: 3456,
243
+ },
244
+ {
245
+ primaryEntityId: PROJECT_ID.toString(),
246
+ primaryEntityType: null,
247
+ rowCount: 2,
248
+ estimatedBytes: 0,
249
+ },
250
+ ]);
251
+ });
252
+
253
+ test("refuses a table with no name column before querying it", async () => {
254
+ const service: LogService = new LogService();
255
+ let executed: boolean = false;
256
+ service.executeQuery = (async (): Promise<never> => {
257
+ executed = true;
258
+ throw new Error("must not run");
259
+ }) as unknown as typeof service.executeQuery;
260
+
261
+ await expect(
262
+ service.groupTelemetryUsageByService({
263
+ projectId: PROJECT_ID,
264
+ timestampColumnName: "time",
265
+ startDate: START_OF_DAY,
266
+ endDate: END_OF_DAY,
267
+ excludeNames: ["anything"],
268
+ }),
269
+ ).rejects.toThrow(BadDataException);
270
+ expect(executed).toBe(false);
271
+ });
272
+
273
+ test("refuses a Nullable name column, where NOT IN would silently drop every unnamed row", async () => {
274
+ /*
275
+ * Span.name is optional. ClickHouse does not treat `NULL NOT IN (...)`
276
+ * as true, so the scan would lose every span without a name from the
277
+ * bill - an undercount nobody would notice.
278
+ */
279
+ const service: SpanService = new SpanService();
280
+ let executed: boolean = false;
281
+ service.executeQuery = (async (): Promise<never> => {
282
+ executed = true;
283
+ throw new Error("must not run");
284
+ }) as unknown as typeof service.executeQuery;
285
+
286
+ await expect(
287
+ service.groupTelemetryUsageByService({
288
+ projectId: PROJECT_ID,
289
+ timestampColumnName: "startTime",
290
+ startDate: START_OF_DAY,
291
+ endDate: END_OF_DAY,
292
+ excludeNames: ["anything"],
293
+ }),
294
+ ).rejects.toThrow(
295
+ "excludeNames needs a required name column, and SpanItemV3 has none",
296
+ );
297
+ expect(executed).toBe(false);
298
+ });
299
+ });
@@ -228,6 +228,50 @@ describe("HostService.findOrCreateByHostIdentifier L1 memo", () => {
228
228
  expect(findOneBy).toHaveBeenCalledTimes(2);
229
229
  });
230
230
 
231
+ /*
232
+ * A host deleted within the TTL keeps resolving to its old id from the
233
+ * memo; a caller that found that id gone (a resource AI agent
234
+ * registering again right after the delete) asks past the memo, gets the
235
+ * host created anew, and the memo then holds the new id.
236
+ */
237
+ test("bypassMemo looks the host up again and refreshes the memo with what it found", async () => {
238
+ const projectId: ObjectID = ObjectID.generate();
239
+ const deleted: Host = buildHost();
240
+ const recreated: Host = buildHost();
241
+ findOneBy.mockResolvedValue(deleted);
242
+
243
+ await HostService.findOrCreateByHostIdentifier({
244
+ projectId,
245
+ hostIdentifier: "web-01",
246
+ });
247
+
248
+ // The host is deleted: nothing owns the identifier any more.
249
+ findOneBy.mockResolvedValue(null);
250
+ create.mockResolvedValue(recreated);
251
+
252
+ const memoed: Host = await HostService.findOrCreateByHostIdentifier({
253
+ projectId,
254
+ hostIdentifier: "web-01",
255
+ });
256
+ expect(memoed._id).toBe(deleted._id);
257
+
258
+ const fresh: Host = await HostService.findOrCreateByHostIdentifier({
259
+ projectId,
260
+ hostIdentifier: "web-01",
261
+ bypassMemo: true,
262
+ });
263
+ expect(fresh._id).toBe(recreated._id);
264
+ expect(findOneBy).toHaveBeenCalledTimes(2);
265
+ expect(create).toHaveBeenCalledTimes(1);
266
+
267
+ const afterwards: Host = await HostService.findOrCreateByHostIdentifier({
268
+ projectId,
269
+ hostIdentifier: "web-01",
270
+ });
271
+ expect(afterwards._id).toBe(recreated._id);
272
+ expect(findOneBy).toHaveBeenCalledTimes(2);
273
+ });
274
+
231
275
  test("memoizes a host it just created", async () => {
232
276
  const projectId: ObjectID = ObjectID.generate();
233
277
  const created: Host = buildHost({ hostIdentifier: "brand-new" });
@@ -73,6 +73,7 @@ function minutesBetween(from: Date, to: Date): number {
73
73
  describe("claimMonitorProbesForProbing computes the schedule the customer asked for", () => {
74
74
  afterEach(() => {
75
75
  jest.restoreAllMocks();
76
+ jest.useRealTimers();
76
77
  });
77
78
 
78
79
  describe("the production scenario", () => {
@@ -114,13 +115,51 @@ describe("claimMonitorProbesForProbing computes the schedule the customer asked
114
115
  },
115
116
  );
116
117
 
117
- test('a monitor storing "1 day" is scheduled for midnight, not a minute away', async () => {
118
- const { nextPingAt, now } = await claimAndReadNextPingAt("1 day");
118
+ /*
119
+ * "1 day" means the next midnight, and on the real clock "more than a
120
+ * minute away" cannot tell that from the old one-minute fallback in the
121
+ * last minute of the day: at 23:59:14 the right answer IS under a minute
122
+ * away (this failed CI at exactly that time), and "+1 minute" lands on
123
+ * 00:00 too. So the clock is pinned - only Date, the harness's promises
124
+ * run as usual - once mid-day and once in that last minute, and the
125
+ * schedule must be the next midnight to the millisecond, which "+1
126
+ * minute" never is: it keeps the seconds.
127
+ */
128
+ test.each([
129
+ ["mid-day", new Date(2026, 8, 29, 10, 17, 42, 123)],
130
+ ["in the last minute of the day", new Date(2026, 8, 29, 23, 59, 14, 500)],
131
+ ])(
132
+ 'a monitor storing "1 day" is scheduled for the next midnight, not a minute away (%s)',
133
+ async (_when: string, pinnedNow: Date) => {
134
+ jest.useFakeTimers({
135
+ now: pinnedNow,
136
+ doNotFake: [
137
+ "hrtime",
138
+ "nextTick",
139
+ "performance",
140
+ "queueMicrotask",
141
+ "requestAnimationFrame",
142
+ "cancelAnimationFrame",
143
+ "requestIdleCallback",
144
+ "cancelIdleCallback",
145
+ "setImmediate",
146
+ "clearImmediate",
147
+ "setInterval",
148
+ "clearInterval",
149
+ "setTimeout",
150
+ "clearTimeout",
151
+ ],
152
+ });
119
153
 
120
- expect(nextPingAt.getHours()).toBe(0);
121
- expect(nextPingAt.getMinutes()).toBe(0);
122
- expect(minutesBetween(now, nextPingAt)).toBeGreaterThan(1);
123
- });
154
+ const { nextPingAt, now } = await claimAndReadNextPingAt("1 day");
155
+
156
+ expect(now.getTime()).toBe(pinnedNow.getTime());
157
+ expect(nextPingAt.getTime()).toBe(
158
+ new Date(2026, 8, 30, 0, 0, 0, 0).getTime(),
159
+ );
160
+ expect(nextPingAt.getTime()).not.toBe(pinnedNow.getTime() + 60 * 1000);
161
+ },
162
+ );
124
163
  });
125
164
 
126
165
  describe("values that were always valid are unaffected", () => {
@@ -0,0 +1,171 @@
1
+ import DatabaseBaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
2
+ import MetricRecordingRule from "../../../Models/DatabaseModels/MetricRecordingRule";
3
+ import TraceRecordingRule from "../../../Models/DatabaseModels/TraceRecordingRule";
4
+ import MetricRecordingRuleService, {
5
+ Service as MetricRecordingRuleServiceClass,
6
+ } from "../../../Server/Services/MetricRecordingRuleService";
7
+ import TraceRecordingRuleService from "../../../Server/Services/TraceRecordingRuleService";
8
+ import CreateBy from "../../../Server/Types/Database/CreateBy";
9
+ import UpdateBy from "../../../Server/Types/Database/UpdateBy";
10
+ import { OnCreate, OnUpdate } from "../../../Server/Types/Database/Hooks";
11
+ import BadDataException from "../../../Types/Exception/BadDataException";
12
+ import ObjectID from "../../../Types/ObjectID";
13
+ import SessionReplayBudgetMetricType from "../../../Types/Rum/SessionReplayBudgetMetricType";
14
+ import { describe, expect, test } from "@jest/globals";
15
+
16
+ /*
17
+ * Recording rules write their output straight into the metric store, past
18
+ * OTLP ingest - which is where every other customer-chosen metric name is
19
+ * checked against the reserved session replay namespace. The budget series
20
+ * under it open incidents and are left out of telemetry billing by name, so
21
+ * a rule must not be able to write one either: both services refuse a
22
+ * reserved output name when a rule is created, and when an update sets it.
23
+ */
24
+
25
+ interface Hooks<TModel extends DatabaseBaseModel> {
26
+ onBeforeCreate: (createBy: CreateBy<TModel>) => Promise<OnCreate<TModel>>;
27
+ onBeforeUpdate: (updateBy: UpdateBy<TModel>) => Promise<OnUpdate<TModel>>;
28
+ }
29
+
30
+ const RESERVED_NAMES: Array<string> = [
31
+ ...Object.values(SessionReplayBudgetMetricType),
32
+ "oneuptime.rum.session.replay.anything.new",
33
+ "OneUptime.RUM.Session.Replay.Budget.Project.Daily.Used.Percent",
34
+ " oneuptime.rum.session.replay.budget.project.daily.used.bytes ",
35
+ ];
36
+
37
+ const ALLOWED_NAMES: Array<string> = [
38
+ "web_vital.lcp",
39
+ "checkout.latency.p99",
40
+ "oneuptime.llm.budget.percent.used",
41
+ "oneuptime.rum.application.id",
42
+ "oneuptime.rum.session.replayed",
43
+ ];
44
+
45
+ interface ServiceCase {
46
+ name: string;
47
+ hooks: Hooks<MetricRecordingRule | TraceRecordingRule>;
48
+ newRule: () => MetricRecordingRule | TraceRecordingRule;
49
+ }
50
+
51
+ const SERVICES: Array<ServiceCase> = [
52
+ {
53
+ name: "MetricRecordingRuleService",
54
+ hooks: MetricRecordingRuleService as unknown as Hooks<
55
+ MetricRecordingRule | TraceRecordingRule
56
+ >,
57
+ newRule: (): MetricRecordingRule => {
58
+ return new MetricRecordingRule();
59
+ },
60
+ },
61
+ {
62
+ name: "TraceRecordingRuleService",
63
+ hooks: TraceRecordingRuleService as unknown as Hooks<
64
+ MetricRecordingRule | TraceRecordingRule
65
+ >,
66
+ newRule: (): TraceRecordingRule => {
67
+ return new TraceRecordingRule();
68
+ },
69
+ },
70
+ ];
71
+
72
+ function createBy(
73
+ rule: MetricRecordingRule | TraceRecordingRule,
74
+ ): CreateBy<MetricRecordingRule | TraceRecordingRule> {
75
+ return {
76
+ data: rule,
77
+ props: { isRoot: true },
78
+ } as unknown as CreateBy<MetricRecordingRule | TraceRecordingRule>;
79
+ }
80
+
81
+ function updateBy(
82
+ data: Record<string, unknown>,
83
+ ): UpdateBy<MetricRecordingRule | TraceRecordingRule> {
84
+ return {
85
+ query: { _id: ObjectID.generate().toString() },
86
+ data: data,
87
+ props: { isRoot: true },
88
+ } as unknown as UpdateBy<MetricRecordingRule | TraceRecordingRule>;
89
+ }
90
+
91
+ describe("MetricRecordingRuleService.assertOutputMetricNameAllowed", () => {
92
+ test.each(RESERVED_NAMES)("refuses %p", (name: string) => {
93
+ expect(() => {
94
+ MetricRecordingRuleServiceClass.assertOutputMetricNameAllowed(name);
95
+ }).toThrow(BadDataException);
96
+ });
97
+
98
+ test("says which prefix is reserved and what to do", () => {
99
+ expect(() => {
100
+ MetricRecordingRuleServiceClass.assertOutputMetricNameAllowed(
101
+ SessionReplayBudgetMetricType.ProjectDailyUsedPercent,
102
+ );
103
+ }).toThrow(
104
+ 'Metric names that start with "oneuptime.rum.session.replay." are reserved for OneUptime\'s session replay budget metrics. Choose another output metric name.',
105
+ );
106
+ });
107
+
108
+ test.each(ALLOWED_NAMES)("allows %p", (name: string) => {
109
+ expect(() => {
110
+ MetricRecordingRuleServiceClass.assertOutputMetricNameAllowed(name);
111
+ }).not.toThrow();
112
+ });
113
+
114
+ test.each([undefined, null, "", 42])(
115
+ "leaves a missing or non-string name %p to the model's own validation",
116
+ (name: unknown) => {
117
+ expect(() => {
118
+ MetricRecordingRuleServiceClass.assertOutputMetricNameAllowed(name);
119
+ }).not.toThrow();
120
+ },
121
+ );
122
+ });
123
+
124
+ describe.each(SERVICES)("$name", ({ hooks, newRule }: ServiceCase) => {
125
+ test("refuses to create a rule whose output name is reserved", async () => {
126
+ const rule: MetricRecordingRule | TraceRecordingRule = newRule();
127
+ rule.outputMetricName =
128
+ SessionReplayBudgetMetricType.ApplicationMonthlyUsedPercent;
129
+
130
+ await expect(hooks.onBeforeCreate(createBy(rule))).rejects.toThrow(
131
+ BadDataException,
132
+ );
133
+ });
134
+
135
+ test("creates a rule with any other output name, untouched", async () => {
136
+ const rule: MetricRecordingRule | TraceRecordingRule = newRule();
137
+ rule.outputMetricName = "checkout.latency.p99";
138
+
139
+ const result: OnCreate<MetricRecordingRule | TraceRecordingRule> =
140
+ await hooks.onBeforeCreate(createBy(rule));
141
+
142
+ expect(result.createBy.data.outputMetricName).toBe("checkout.latency.p99");
143
+ expect(result.carryForward).toBeNull();
144
+ });
145
+
146
+ test("refuses an update that renames the output into the reserved namespace", async () => {
147
+ await expect(
148
+ hooks.onBeforeUpdate(
149
+ updateBy({
150
+ outputMetricName: "oneuptime.rum.session.replay.budget.mine",
151
+ }),
152
+ ),
153
+ ).rejects.toThrow(BadDataException);
154
+ });
155
+
156
+ test("lets through an update that does not touch the output name", async () => {
157
+ const result: OnUpdate<MetricRecordingRule | TraceRecordingRule> =
158
+ await hooks.onBeforeUpdate(updateBy({ isEnabled: false }));
159
+
160
+ expect(result.carryForward).toBeNull();
161
+ expect(
162
+ (result.updateBy.data as unknown as Record<string, unknown>)["isEnabled"],
163
+ ).toBe(false);
164
+ });
165
+
166
+ test("lets through an update to an allowed output name", async () => {
167
+ await expect(
168
+ hooks.onBeforeUpdate(updateBy({ outputMetricName: "web_vital.lcp.p75" })),
169
+ ).resolves.toBeDefined();
170
+ });
171
+ });