@oneuptime/common 14.0.9 → 14.0.11

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 (1146) hide show
  1. package/Models/DatabaseModels/AIConversation.ts +127 -1
  2. package/Models/DatabaseModels/AutoRemediationSuggestion.ts +60 -0
  3. package/Models/DatabaseModels/CephCluster.ts +225 -0
  4. package/Models/DatabaseModels/CloudResource.ts +12 -0
  5. package/Models/DatabaseModels/DatabaseServer.ts +225 -0
  6. package/Models/DatabaseModels/DockerHost.ts +225 -0
  7. package/Models/DatabaseModels/DockerSwarmCluster.ts +225 -0
  8. package/Models/DatabaseModels/GlobalConfig.ts +1 -1
  9. package/Models/DatabaseModels/GlobalOidc.ts +0 -4
  10. package/Models/DatabaseModels/GlobalOidcProject.ts +0 -4
  11. package/Models/DatabaseModels/GlobalSso.ts +0 -4
  12. package/Models/DatabaseModels/GlobalSsoProject.ts +0 -4
  13. package/Models/DatabaseModels/Host.ts +225 -0
  14. package/Models/DatabaseModels/IncidentForm.ts +594 -0
  15. package/Models/DatabaseModels/IncidentFormSubmission.ts +283 -0
  16. package/Models/DatabaseModels/IncidentTemplate.ts +59 -0
  17. package/Models/DatabaseModels/Index.ts +16 -0
  18. package/Models/DatabaseModels/IoTFleet.ts +12 -0
  19. package/Models/DatabaseModels/KubernetesCluster.ts +12 -0
  20. package/Models/DatabaseModels/MessageQueue.ts +1102 -0
  21. package/Models/DatabaseModels/MessageQueueLabelRule.ts +553 -0
  22. package/Models/DatabaseModels/MessageQueueOwnerRule.ts +633 -0
  23. package/Models/DatabaseModels/MessageQueueOwnerTeam.ts +491 -0
  24. package/Models/DatabaseModels/MessageQueueOwnerUser.ts +490 -0
  25. package/Models/DatabaseModels/PodmanHost.ts +225 -0
  26. package/Models/DatabaseModels/Project.ts +59 -0
  27. package/Models/DatabaseModels/ProjectOidc.ts +0 -4
  28. package/Models/DatabaseModels/ProjectSso.ts +0 -4
  29. package/Models/DatabaseModels/ProxmoxCluster.ts +225 -0
  30. package/Models/DatabaseModels/ResourceAiAgent.ts +419 -0
  31. package/Models/DatabaseModels/RumApplication.ts +12 -0
  32. package/Models/DatabaseModels/RunnerJob.ts +144 -0
  33. package/Models/DatabaseModels/ServerlessFunction.ts +12 -0
  34. package/Models/DatabaseModels/Service.ts +12 -0
  35. package/Models/DatabaseModels/StatusPageOidc.ts +0 -4
  36. package/Models/DatabaseModels/StatusPageSso.ts +0 -4
  37. package/Models/DatabaseModels/TeamComplianceSetting.ts +47 -2
  38. package/Models/DatabaseModels/VMwareVCenter.ts +225 -0
  39. package/Server/API/AIChatAPI.ts +13 -114
  40. package/Server/API/AIInvestigationAPI.ts +3 -3
  41. package/Server/API/AIInvestigationConversationAPI.ts +266 -0
  42. package/Server/API/AutoRemediationAPI.ts +392 -1
  43. package/Server/API/IncidentFormAPI.ts +376 -0
  44. package/Server/API/ProjectOIDC.ts +16 -25
  45. package/Server/API/ProjectSSO.ts +16 -24
  46. package/Server/API/ResourceAiAccessAPI.ts +1669 -0
  47. package/Server/API/StatusPageAPI.ts +42 -77
  48. package/Server/Enterprise/EnterpriseEdition.ts +93 -28
  49. package/Server/Enterprise/EnterpriseFeature.ts +25 -3
  50. package/Server/Enterprise/EnterpriseLicenseSnapshot.ts +1 -1
  51. package/Server/Enterprise/EnterpriseServerModule.ts +4 -1
  52. package/Server/EnvironmentConfig.ts +1 -1
  53. package/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.ts +423 -0
  54. package/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.ts +129 -0
  55. package/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.ts +52 -0
  56. package/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.ts +75 -0
  57. package/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.ts +477 -0
  58. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
  59. package/Server/Infrastructure/Semaphore.ts +22 -0
  60. package/Server/Middleware/IncidentFormRateLimit.ts +847 -0
  61. package/Server/Middleware/TelemetryIngest.ts +15 -5
  62. package/Server/Middleware/UserAuthorization.ts +16 -38
  63. package/Server/Services/AIService.ts +10 -13
  64. package/Server/Services/AnalyticsDatabaseService.ts +45 -1
  65. package/Server/Services/AutoRemediationRuleEngineService.ts +940 -112
  66. package/Server/Services/CephClusterService.ts +109 -1
  67. package/Server/Services/DatabaseServerService.ts +116 -1
  68. package/Server/Services/DockerHostService.ts +109 -1
  69. package/Server/Services/DockerSwarmClusterService.ts +113 -1
  70. package/Server/Services/HostService.ts +126 -4
  71. package/Server/Services/IncidentCustomFieldService.ts +112 -2
  72. package/Server/Services/IncidentEpisodeMemberService.ts +24 -4
  73. package/Server/Services/IncidentEpisodeService.ts +10 -1
  74. package/Server/Services/IncidentFormService.ts +1566 -0
  75. package/Server/Services/IncidentFormSubmissionService.ts +97 -0
  76. package/Server/Services/IncidentGroupingEngineService.ts +45 -8
  77. package/Server/Services/IncidentService.ts +123 -5
  78. package/Server/Services/IncidentTemplateService.ts +22 -0
  79. package/Server/Services/Index.ts +14 -0
  80. package/Server/Services/MessageQueueLabelRuleEngineService.ts +321 -0
  81. package/Server/Services/MessageQueueLabelRuleService.ts +14 -0
  82. package/Server/Services/MessageQueueOwnerRuleEngineService.ts +412 -0
  83. package/Server/Services/MessageQueueOwnerRuleService.ts +14 -0
  84. package/Server/Services/MessageQueueOwnerTeamService.ts +62 -0
  85. package/Server/Services/MessageQueueOwnerUserService.ts +62 -0
  86. package/Server/Services/MessageQueueService.ts +1463 -0
  87. package/Server/Services/MetricRecordingRuleService.ts +47 -0
  88. package/Server/Services/OpenTelemetryIngestService.ts +37 -4
  89. package/Server/Services/PodmanHostService.ts +109 -1
  90. package/Server/Services/ProjectService.ts +0 -59
  91. package/Server/Services/ProxmoxClusterService.ts +110 -1
  92. package/Server/Services/ResourceAiAccessService.ts +1439 -0
  93. package/Server/Services/ResourceAiAgentJobService.ts +202 -0
  94. package/Server/Services/ResourceAiAgentService.ts +2432 -0
  95. package/Server/Services/RunnerJobService.ts +611 -4
  96. package/Server/Services/StatusPagePrivateUserService.ts +1 -4
  97. package/Server/Services/StatusPageService.ts +1 -51
  98. package/Server/Services/TeamComplianceSettingService.ts +163 -42
  99. package/Server/Services/TelemetryUsageBillingService.ts +28 -0
  100. package/Server/Services/TraceRecordingRuleService.ts +33 -0
  101. package/Server/Services/UserNotificationRuleService.ts +8 -1
  102. package/Server/Services/VMwareVCenterService.ts +110 -1
  103. package/Server/Services/WorkspaceNotificationSummaryService.ts +12 -3
  104. package/Server/Types/Database/Permissions/CreatePermission.ts +17 -3
  105. package/Server/Types/Database/Permissions/EditionPermission.ts +140 -38
  106. package/Server/Types/Database/Permissions/OwnerTableRegistry.ts +16 -0
  107. package/Server/Types/Database/Permissions/TablePermission.ts +3 -3
  108. package/Server/Types/Database/Permissions/UpdatePermission.ts +20 -6
  109. package/Server/Utils/AI/Chat/AgentContextCompactor.ts +200 -0
  110. package/Server/Utils/AI/Chat/ChatAgentRunner.ts +307 -48
  111. package/Server/Utils/AI/Chat/ChatRunCancellation.ts +156 -0
  112. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +78 -18
  113. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +1 -1
  114. package/Server/Utils/AI/Chat/ToolOutputPager.ts +322 -0
  115. package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +88 -30
  116. package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +13 -1
  117. package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +0 -32
  118. package/Server/Utils/AI/CodeFix/FixRunBudget.ts +27 -25
  119. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1442 -34
  120. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +968 -34
  121. package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +25 -1
  122. package/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.ts +622 -0
  123. package/Server/Utils/AI/ResourceAccess/ResourceAccessContext.ts +271 -0
  124. package/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.ts +45 -0
  125. package/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.ts +964 -0
  126. package/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.ts +333 -0
  127. package/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.ts +882 -0
  128. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +125 -12
  129. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +99 -15
  130. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +99 -15
  131. package/Server/Utils/AI/SRE/InvestigationQueue.ts +16 -19
  132. package/Server/Utils/AI/SRE/InvestigationThread.ts +646 -0
  133. package/Server/Utils/AI/SRE/InvestigationThreadService.ts +715 -0
  134. package/Server/Utils/AI/Toolbox/Serializer.ts +10 -3
  135. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +396 -13
  136. package/Server/Utils/AutoRemediation/RemediationVerifier.ts +35 -2
  137. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +9 -1
  138. package/Server/Utils/EditionEnforcement.ts +19 -202
  139. package/Server/Utils/LLM/LLMService.ts +259 -63
  140. package/Server/Utils/RelaxedScimTeamLocksReport.ts +245 -0
  141. package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +25 -0
  142. package/Server/Utils/SameOriginRequest.ts +203 -0
  143. package/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.ts +920 -0
  144. package/Server/Utils/SessionReplay/SessionReplayUsage.ts +84 -6
  145. package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +2 -0
  146. package/Server/Utils/Telemetry/EntityRegistry.ts +4 -1
  147. package/Server/Utils/Telemetry/MessageQueueDiscovery.ts +1402 -0
  148. package/Server/Utils/Telemetry/ResourceEntityFilter.ts +4 -1
  149. package/Server/Utils/VendorAssets.ts +3 -3
  150. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +35 -15
  151. package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +33 -15
  152. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +21 -1
  153. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +301 -225
  154. package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +38 -16
  155. package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +389 -175
  156. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +376 -144
  157. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.ts +163 -0
  158. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.ts +396 -0
  159. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.ts +418 -0
  160. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.ts +152 -0
  161. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.ts +251 -0
  162. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.ts +214 -0
  163. package/Tests/App/Accounts/IncidentFormPage.test.tsx +2036 -0
  164. package/Tests/App/Accounts/IncidentFormServerMessages.test.ts +119 -0
  165. package/Tests/App/Accounts/IncidentFormUtils.test.ts +1157 -0
  166. package/Tests/App/Accounts/LoginPasskey.test.tsx +6 -4
  167. package/Tests/App/Accounts/LoginSso.test.tsx +724 -0
  168. package/Tests/App/AdminDashboard/AdminAuthenticationRequireSso.test.tsx +338 -0
  169. package/Tests/App/AdminDashboard/AdminAuthenticationSsoCardLocales.test.tsx +331 -0
  170. package/Tests/App/AdminDashboard/AdminEnterprisePluginPage.test.tsx +47 -33
  171. package/Tests/App/AdminDashboard/GlobalSsoOidcPages.test.tsx +1021 -0
  172. package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +876 -78
  173. package/Tests/App/Dashboard/AffectedResourcesSlo.test.tsx +20 -3
  174. package/Tests/App/Dashboard/CephProxmoxVMwareDocumentationCards.test.tsx +359 -0
  175. package/Tests/App/Dashboard/CephSetupGuide.test.ts +961 -0
  176. package/Tests/App/Dashboard/CloudDocumentationCard.test.tsx +381 -95
  177. package/Tests/App/Dashboard/CloudSetupGuide.test.ts +1463 -0
  178. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +267 -12
  179. package/Tests/App/Dashboard/ContainerSetupGuideCards.test.tsx +345 -0
  180. package/Tests/App/Dashboard/CoreListEmptyStates.test.tsx +501 -0
  181. package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +0 -1
  182. package/Tests/App/Dashboard/DatabaseDocumentationCard.test.tsx +400 -0
  183. package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +23 -6
  184. package/Tests/App/Dashboard/DatabaseSetupGuide.test.ts +1869 -0
  185. package/Tests/App/Dashboard/DatabaseTelemetryScope.test.ts +44 -0
  186. package/Tests/App/Dashboard/DatabaseTelemetryTabs.test.tsx +50 -9
  187. package/Tests/App/Dashboard/DockerSetupGuide.test.ts +899 -0
  188. package/Tests/App/Dashboard/DockerSwarmSetupGuide.test.ts +964 -0
  189. package/Tests/App/Dashboard/EnterpriseFeatureUpgrade.test.tsx +141 -7
  190. package/Tests/App/Dashboard/EnterprisePluginPage.test.tsx +52 -39
  191. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +7 -1
  192. package/Tests/App/Dashboard/GettingStartedTaskRoutes.test.tsx +331 -0
  193. package/Tests/App/Dashboard/HomeOverviewStatsFirstRun.test.tsx +925 -0
  194. package/Tests/App/Dashboard/HostDocumentationCard.test.tsx +225 -0
  195. package/Tests/App/Dashboard/HostSetupGuide.test.ts +1183 -0
  196. package/Tests/App/Dashboard/IdentityEnterpriseShells.test.tsx +69 -45
  197. package/Tests/App/Dashboard/IncidentCreateTemplateCustomFieldSettings.test.tsx +745 -0
  198. package/Tests/App/Dashboard/IncidentCreateTemplateOwners.test.tsx +414 -0
  199. package/Tests/App/Dashboard/IncidentCustomFieldCreateSettingsForm.test.ts +1042 -0
  200. package/Tests/App/Dashboard/IncidentCustomFieldSettingsCard.test.tsx +1816 -0
  201. package/Tests/App/Dashboard/IncidentFormPlan.test.ts +89 -0
  202. package/Tests/App/Dashboard/IncidentFormShareLinkCard.test.tsx +846 -0
  203. package/Tests/App/Dashboard/IncidentFormsPages.test.tsx +1324 -0
  204. package/Tests/App/Dashboard/IncidentTemplateCustomFieldSettings.test.tsx +630 -0
  205. package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +13 -0
  206. package/Tests/App/Dashboard/IncomingEmailMonitorLinkVerifyHint.test.tsx +104 -0
  207. package/Tests/App/Dashboard/InvestigationConversation.test.tsx +1107 -0
  208. package/Tests/App/Dashboard/InvestigationConversationData.test.ts +546 -0
  209. package/Tests/App/Dashboard/InvestigationEvidenceList.test.tsx +170 -5
  210. package/Tests/App/Dashboard/InvestigationInfrastructureTools.test.tsx +506 -0
  211. package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +207 -2
  212. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +729 -34
  213. package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +9 -21
  214. package/Tests/App/Dashboard/InvestigationReportView.test.tsx +274 -37
  215. package/Tests/App/Dashboard/InvestigationStatusBadge.test.tsx +130 -0
  216. package/Tests/App/Dashboard/IoTDocumentationCard.test.tsx +218 -0
  217. package/Tests/App/Dashboard/IoTSetupGuide.test.ts +1124 -0
  218. package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +20 -6
  219. package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +107 -83
  220. package/Tests/App/Dashboard/KubernetesSetupGuide.test.ts +787 -0
  221. package/Tests/App/Dashboard/MessageQueueBreadcrumbs.test.tsx +228 -0
  222. package/Tests/App/Dashboard/MessageQueueBrokerHealth.test.tsx +1538 -0
  223. package/Tests/App/Dashboard/MessageQueueBrokerHealthMetricChart.test.tsx +969 -0
  224. package/Tests/App/Dashboard/MessageQueueBrokerHealthReads.test.ts +889 -0
  225. package/Tests/App/Dashboard/MessageQueueBrokerMetricsReach.test.ts +236 -0
  226. package/Tests/App/Dashboard/MessageQueueDocumentationCard.test.tsx +479 -0
  227. package/Tests/App/Dashboard/MessageQueueListPages.test.tsx +1232 -0
  228. package/Tests/App/Dashboard/MessageQueueMetricDescriptions.test.ts +244 -0
  229. package/Tests/App/Dashboard/MessageQueueMonitorLink.test.ts +952 -0
  230. package/Tests/App/Dashboard/MessageQueueMonitorLinkMonitorCreate.test.tsx +859 -0
  231. package/Tests/App/Dashboard/MessageQueueOverview.test.tsx +1773 -0
  232. package/Tests/App/Dashboard/MessageQueueOverviewPresentation.test.ts +196 -0
  233. package/Tests/App/Dashboard/MessageQueuePresentation.test.ts +799 -0
  234. package/Tests/App/Dashboard/MessageQueueProducersConsumers.test.tsx +680 -0
  235. package/Tests/App/Dashboard/MessageQueueRoutes.test.tsx +383 -0
  236. package/Tests/App/Dashboard/MessageQueueRulePages.test.tsx +278 -0
  237. package/Tests/App/Dashboard/MessageQueueSetupGuide.test.ts +2778 -0
  238. package/Tests/App/Dashboard/MessageQueueSideMenus.test.tsx +273 -0
  239. package/Tests/App/Dashboard/MessageQueueSpanPopulations.test.ts +930 -0
  240. package/Tests/App/Dashboard/MessageQueueSpanStore.ts +351 -0
  241. package/Tests/App/Dashboard/MessageQueueTelemetryScope.test.ts +375 -0
  242. package/Tests/App/Dashboard/MessageQueueTelemetryScopeTabs.test.tsx +668 -0
  243. package/Tests/App/Dashboard/MessageQueueViewPages.test.tsx +685 -0
  244. package/Tests/App/Dashboard/MonitorAndHomeNotOperationalMenus.test.tsx +342 -0
  245. package/Tests/App/Dashboard/MonitorCreateFirstRunDefaults.test.tsx +623 -0
  246. package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +442 -0
  247. package/Tests/App/Dashboard/MonitorLogsPageViewSummary.test.tsx +814 -0
  248. package/Tests/App/Dashboard/MonitorSetupCardBodies.test.tsx +246 -0
  249. package/Tests/App/Dashboard/NavigationSearchKeywords.test.tsx +8 -0
  250. package/Tests/App/Dashboard/OnboardingSsoEmptyState.test.tsx +27 -12
  251. package/Tests/App/Dashboard/PlanGatedPage.test.tsx +292 -0
  252. package/Tests/App/Dashboard/PodmanSetupGuide.test.ts +948 -0
  253. package/Tests/App/Dashboard/ProductOrientation.test.tsx +5 -4
  254. package/Tests/App/Dashboard/ProductSettingsSectionCollapsed.test.tsx +266 -0
  255. package/Tests/App/Dashboard/ProxmoxSetupGuide.test.ts +1318 -0
  256. package/Tests/App/Dashboard/RemediationSuggestionCardDescription.test.tsx +209 -0
  257. package/Tests/App/Dashboard/RemediationSuggestionCardResource.test.tsx +317 -0
  258. package/Tests/App/Dashboard/ResourceAiAccessSettingsUtil.test.ts +921 -0
  259. package/Tests/App/Dashboard/ResourceAiAgentAttentionServerParity.test.ts +555 -0
  260. package/Tests/App/Dashboard/ResourceAiAgentInstall.test.ts +860 -0
  261. package/Tests/App/Dashboard/ResourceAiAgentPage.test.tsx +2010 -0
  262. package/Tests/App/Dashboard/ResourceAiAgentStatus.test.ts +1455 -0
  263. package/Tests/App/Dashboard/ResourceAiInsightsPage.test.tsx +931 -0
  264. package/Tests/App/Dashboard/ResourceAiNavigation.test.tsx +575 -0
  265. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +16 -0
  266. package/Tests/App/Dashboard/ResourceConnectionGuideCard.test.tsx +444 -0
  267. package/Tests/App/Dashboard/ResourceConnectionGuideOverviewsInfra.test.tsx +798 -0
  268. package/Tests/App/Dashboard/ResourceConnectionGuideOverviewsPlatforms.test.tsx +717 -0
  269. package/Tests/App/Dashboard/ResourceConnectionGuides.test.ts +981 -0
  270. package/Tests/App/Dashboard/ResourceConnectionState.test.ts +132 -0
  271. package/Tests/App/Dashboard/RumDocumentationCard.test.tsx +293 -0
  272. package/Tests/App/Dashboard/RumSessionReplayRetentionSettings.test.tsx +467 -0
  273. package/Tests/App/Dashboard/RumSetupGuide.test.ts +758 -0
  274. package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +38 -7
  275. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +25 -0
  276. package/Tests/App/Dashboard/ServerlessDocumentationCard.test.tsx +254 -0
  277. package/Tests/App/Dashboard/ServerlessSetupGuide.test.ts +1074 -0
  278. package/Tests/App/Dashboard/SetupGuide.test.ts +246 -0
  279. package/Tests/App/Dashboard/SetupGuideCard.test.tsx +756 -0
  280. package/Tests/App/Dashboard/SloListSideMenu.test.tsx +120 -0
  281. package/Tests/App/Dashboard/SsoPages.test.tsx +833 -0
  282. package/Tests/App/Dashboard/TelemetryDocumentation.test.tsx +253 -0
  283. package/Tests/App/Dashboard/TelemetryRetentionShells.test.tsx +730 -0
  284. package/Tests/App/Dashboard/VMwareSetupGuide.test.ts +1033 -0
  285. package/Tests/App/StatusPage/StatusPageSsoEmptyState.test.tsx +87 -7
  286. package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +61 -10
  287. package/Tests/Models/DatabaseModels/IncidentFormModel.test.ts +1250 -0
  288. package/Tests/Models/DatabaseModels/MessageQueueModels.test.ts +1983 -0
  289. package/Tests/Models/DatabaseModels/ResourceAiAccessColumns.test.ts +738 -0
  290. package/Tests/Models/DatabaseModels/ResourceAiAgentModel.test.ts +842 -0
  291. package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
  292. package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +190 -14
  293. package/Tests/Models/IncidentTemplateCustomFieldSettingsColumn.test.ts +291 -0
  294. package/Tests/Server/API/AIInvestigationConversationAPI.test.ts +412 -0
  295. package/Tests/Server/API/AutoRemediationApproveResourceRound.test.ts +835 -0
  296. package/Tests/Server/API/GlobalConfigLicense.test.ts +38 -9
  297. package/Tests/Server/API/IncidentFormAPI.test.ts +942 -0
  298. package/Tests/Server/API/IncidentFormPublicRoutes.test.ts +1653 -0
  299. package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -1
  300. package/Tests/Server/API/ResourceAiAccessAPI.test.ts +2730 -0
  301. package/Tests/Server/API/{SsoProviderListingsEdition.test.ts → SsoProviderListings.test.ts} +164 -110
  302. package/Tests/Server/Enterprise/EnterpriseEdition.test.ts +76 -54
  303. package/Tests/Server/Enterprise/EnterpriseFeatureActive.test.ts +125 -42
  304. package/Tests/Server/Enterprise/EnterpriseLicenseSnapshot.test.ts +36 -10
  305. package/Tests/Server/Enterprise/EnterpriseModelFeatureMap.test.ts +51 -9
  306. package/Tests/Server/Enterprise/FakeEnterpriseModule.ts +13 -8
  307. package/Tests/Server/Enterprise/SsoInCommunityEdition.test.ts +540 -0
  308. package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
  309. package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +60 -0
  310. package/Tests/Server/Infrastructure/Postgres/AddIncidentFormsMigration.test.ts +438 -0
  311. package/Tests/Server/Infrastructure/Postgres/AddResourceAiAgentsMigration.test.ts +659 -0
  312. package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleNotificationChannelsMigration.test.ts +255 -0
  313. package/Tests/Server/Infrastructure/Postgres/MessageQueueTablesMigration.test.ts +637 -0
  314. package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +31 -0
  315. package/Tests/Server/Middleware/IncidentFormRateLimit.test.ts +1630 -0
  316. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +63 -5
  317. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +103 -5
  318. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +19 -1
  319. package/Tests/Server/Middleware/UserAuthorization.test.ts +16 -17
  320. package/Tests/Server/Middleware/UserAuthorizationSsoEnforcement.test.ts +544 -0
  321. package/Tests/Server/Services/AIServiceDailyBudget.test.ts +37 -8
  322. package/Tests/Server/Services/AutoRemediationResourceRuleEngine.test.ts +1279 -0
  323. package/Tests/Server/Services/DatabaseServerService.test.ts +55 -0
  324. package/Tests/Server/Services/GroupTelemetryUsageExcludeNames.test.ts +299 -0
  325. package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +44 -0
  326. package/Tests/Server/Services/IncidentCreatedFeedTitle.test.ts +212 -0
  327. package/Tests/Server/Services/IncidentCustomFieldDeleteFormQuestions.test.ts +297 -0
  328. package/Tests/Server/Services/IncidentFormOwnersCreatedNotification.test.ts +497 -0
  329. package/Tests/Server/Services/IncidentFormPostgres.test.ts +958 -0
  330. package/Tests/Server/Services/IncidentFormPublicForm.test.ts +1260 -0
  331. package/Tests/Server/Services/IncidentFormPublicSubmission.test.ts +2520 -0
  332. package/Tests/Server/Services/IncidentFormRegistration.test.ts +159 -0
  333. package/Tests/Server/Services/IncidentFormService.test.ts +663 -0
  334. package/Tests/Server/Services/IncidentFormSubmissionService.test.ts +430 -0
  335. package/Tests/Server/Services/IncidentFormSubmissionTemplateBranch.test.ts +473 -0
  336. package/Tests/Server/Services/IncidentGroupingTemplateSubstitution.test.ts +81 -0
  337. package/Tests/Server/Services/IncidentTemplateCustomFieldSettings.test.ts +262 -0
  338. package/Tests/Server/Services/IncidentTemplateOwnersOnDeclare.test.ts +566 -0
  339. package/Tests/Server/Services/IncidentTitleMarkdownSinks.test.ts +640 -0
  340. package/Tests/Server/Services/LlmLogServiceTokenAggregateParams.test.ts +3 -3
  341. package/Tests/Server/Services/MessageQueueOwnerServices.test.ts +440 -0
  342. package/Tests/Server/Services/MessageQueueRegistries.test.ts +577 -0
  343. package/Tests/Server/Services/MessageQueueRuleEngines.test.ts +900 -0
  344. package/Tests/Server/Services/MessageQueueService.test.ts +2210 -0
  345. package/Tests/Server/Services/MessageQueueSqlPostgres.test.ts +1271 -0
  346. package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +45 -6
  347. package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +28 -1
  348. package/Tests/Server/Services/OpenTelemetryRetentionOverrideEdition.test.ts +474 -0
  349. package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +21 -0
  350. package/Tests/Server/Services/OwnerRowUniqueness.test.ts +3 -0
  351. package/Tests/Server/Services/ProjectServiceAuditLogLicense.test.ts +0 -1
  352. package/Tests/Server/Services/RecordingRuleReservedMetricName.test.ts +171 -0
  353. package/Tests/Server/Services/ResourceAiAccessChangeAuthorization.test.ts +256 -0
  354. package/Tests/Server/Services/ResourceAiAccessService.test.ts +1373 -0
  355. package/Tests/Server/Services/ResourceAiAgentJobService.test.ts +375 -0
  356. package/Tests/Server/Services/ResourceAiAgentServiceHelpers.test.ts +1114 -0
  357. package/Tests/Server/Services/ResourceAiAgentServiceLifecycle.test.ts +1050 -0
  358. package/Tests/Server/Services/ResourceAiAgentServiceRegister.test.ts +2251 -0
  359. package/Tests/Server/Services/ResourceAiSettingsCreate.test.ts +373 -0
  360. package/Tests/Server/Services/ResourceAiSettingsPermission.test.ts +1042 -0
  361. package/Tests/Server/Services/ResourceServiceDeleteCleansUpAi.test.ts +319 -0
  362. package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +56 -6
  363. package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +30 -1
  364. package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +105 -0
  365. package/Tests/Server/Services/RunnerJobEnqueueResourceCommand.test.ts +986 -0
  366. package/Tests/Server/Services/RunnerJobResourceCommandLane.test.ts +211 -0
  367. package/Tests/Server/Services/RunnerJobResourceCommandTimeoutAndRedaction.test.ts +352 -0
  368. package/Tests/Server/Services/SsoRequirementReads.test.ts +454 -0
  369. package/Tests/Server/Services/SsoRequirementWrites.test.ts +529 -0
  370. package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +3 -3
  371. package/Tests/Server/Services/StatusPagePrivateUserInvitationPolicy.test.ts +107 -89
  372. package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +1068 -176
  373. package/Tests/Server/Services/TeamComplianceSettingService.test.ts +1962 -200
  374. package/Tests/Server/Services/TelemetryUsageBillingSloExclusion.test.ts +218 -0
  375. package/Tests/Server/TestingUtils/Services/FakeRunnerJobCount.ts +145 -0
  376. package/Tests/Server/Types/Database/Permissions/EditionPermission.test.ts +654 -297
  377. package/Tests/Server/Types/Database/Permissions/RetentionOverrideColumns.test.ts +784 -0
  378. package/Tests/Server/Types/Database/Permissions/SsoScalePlanGate.test.ts +418 -0
  379. package/Tests/Server/Utils/AI/AIAlertGating.test.ts +0 -1
  380. package/Tests/Server/Utils/AI/AIInvestigationEngineRunLimits.test.ts +136 -0
  381. package/Tests/Server/Utils/AI/AIInvestigationQueue.test.ts +15 -14
  382. package/Tests/Server/Utils/AI/AgentContextCompactor.test.ts +351 -0
  383. package/Tests/Server/Utils/AI/ChatAgentRunnerSharedThread.test.ts +842 -0
  384. package/Tests/Server/Utils/AI/ChatRunCancellation.test.ts +195 -0
  385. package/Tests/Server/Utils/AI/CodeFixReadiness.test.ts +10 -57
  386. package/Tests/Server/Utils/AI/FixRunBudget.test.ts +20 -24
  387. package/Tests/Server/Utils/AI/Insights/InvestigationQueueInsightSubject.test.ts +7 -30
  388. package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +225 -29
  389. package/Tests/Server/Utils/AI/InvestigationInfrastructureAccessWiring.test.ts +694 -0
  390. package/Tests/Server/Utils/AI/InvestigationInfrastructureReport.test.ts +721 -0
  391. package/Tests/Server/Utils/AI/InvestigationThread.test.ts +580 -0
  392. package/Tests/Server/Utils/AI/InvestigationThreadService.test.ts +801 -0
  393. package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +525 -43
  394. package/Tests/Server/Utils/AI/KubectlJobRunner.test.ts +4 -2
  395. package/Tests/Server/Utils/AI/LLMServiceUnreachableProvider.test.ts +435 -0
  396. package/Tests/Server/Utils/AI/ObservabilityAssistantLimits.test.ts +462 -0
  397. package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +94 -1
  398. package/Tests/Server/Utils/AI/RemediationCommandToolsResource.test.ts +1530 -0
  399. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +14 -25
  400. package/Tests/Server/Utils/AI/RemediationExecutionRunnerResourceMode.test.ts +1251 -0
  401. package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +117 -0
  402. package/Tests/Server/Utils/AI/RemediationResourceCopyParity.test.ts +463 -0
  403. package/Tests/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.test.ts +1004 -0
  404. package/Tests/Server/Utils/AI/ResourceAccess/ResourceAccessContext.test.ts +266 -0
  405. package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.test.ts +1220 -0
  406. package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.test.ts +569 -0
  407. package/Tests/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.test.ts +834 -0
  408. package/Tests/Server/Utils/AI/ToolOutputPager.test.ts +382 -0
  409. package/Tests/Server/Utils/AI/ToolResultSerializer.test.ts +23 -4
  410. package/Tests/Server/Utils/AI/Toolbox/LogTools.test.ts +25 -6
  411. package/Tests/Server/Utils/AI/Toolbox/Serializer.test.ts +14 -3
  412. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorResource.test.ts +717 -0
  413. package/Tests/Server/Utils/AutoRemediation/RemediationVerifierResourceFollowUp.test.ts +338 -0
  414. package/Tests/Server/Utils/EditionEnforcement.test.ts +107 -147
  415. package/Tests/Server/Utils/Monitor/Criteria/SessionReplayBudgetTemplateCriteria.test.ts +422 -0
  416. package/Tests/Server/Utils/{CommunityEditionSsoReport.test.ts → RelaxedScimTeamLocksReport.test.ts} +212 -208
  417. package/Tests/Server/Utils/SameOriginRequest.test.ts +372 -0
  418. package/Tests/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.test.ts +1647 -0
  419. package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +143 -1
  420. package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +20 -0
  421. package/Tests/Server/Utils/Telemetry/MessageQueueDiscovery.test.ts +2590 -0
  422. package/Tests/Server/Utils/Telemetry/MessageQueueSpanQueryTwin.ts +246 -0
  423. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +23 -0
  424. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +33 -3
  425. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsAccountNotLinked.test.ts +1093 -0
  426. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.test.ts +1049 -0
  427. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.test.ts +1676 -0
  428. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCards.test.ts +2884 -0
  429. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.test.ts +2490 -0
  430. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmit.test.ts +2293 -0
  431. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmitServerTimezone.test.ts +386 -0
  432. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.test.ts +1477 -0
  433. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.test.ts +1967 -0
  434. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsStripHtmlTags.test.ts +92 -0
  435. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsSubmittedFormRemoval.test.ts +1819 -0
  436. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.test.ts +1033 -0
  437. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezoneServerZone.test.ts +610 -0
  438. package/Tests/Server/Utils/Workspace/MicrosoftTeamsBotMessageHandling.test.ts +2610 -0
  439. package/Tests/Server/Utils/Workspace/MicrosoftTeamsCreateCommandsEndToEnd.test.ts +4672 -0
  440. package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +190 -65
  441. package/Tests/Types/AI/AIAgentRunLimits.test.ts +255 -0
  442. package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +10 -4
  443. package/Tests/Types/AutoRemediation/AiRemediationCommandPlanResourceCommand.test.ts +305 -0
  444. package/Tests/Types/Code/JsonSyntax.test.ts +1300 -0
  445. package/Tests/Types/CustomField/CustomFieldCreateSettings.test.ts +1113 -0
  446. package/Tests/Types/CustomField/CustomFieldMappingCatalog.test.ts +91 -0
  447. package/Tests/Types/CustomField/CustomFieldValueValidator.test.ts +30 -0
  448. package/Tests/Types/Incident/IncidentFormIpAllowlist.test.ts +227 -0
  449. package/Tests/Types/Incident/IncidentFormPublic.test.ts +1916 -0
  450. package/Tests/Types/MessageQueue/MessageQueueEntityKey.test.ts +916 -0
  451. package/Tests/Types/MessageQueue/MessageQueueIdentity.test.ts +567 -0
  452. package/Tests/Types/MessageQueue/MessageQueueManualIdentity.test.ts +686 -0
  453. package/Tests/Types/MessageQueue/MessageQueueMetricCatalog.test.ts +1726 -0
  454. package/Tests/Types/MessageQueue/MessageQueueMetricResolution.test.ts +1758 -0
  455. package/Tests/Types/MessageQueue/MessagingInstrumentationFixtures.test.ts +4686 -0
  456. package/Tests/Types/MessageQueue/MessagingSystem.test.ts +840 -0
  457. package/Tests/Types/MessageQueue/MessagingTelemetryFixtures.ts +3287 -0
  458. package/Tests/Types/MessageQueue/MessagingTelemetryResolver.test.ts +2966 -0
  459. package/Tests/Types/Monitor/MessageQueueAlertTemplates.test.ts +5401 -0
  460. package/Tests/Types/Monitor/MetricEngineFold.ts +154 -0
  461. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +277 -13
  462. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +113 -0
  463. package/Tests/Types/Monitor/RumAlertTemplates.test.ts +773 -2
  464. package/Tests/Types/ResourceAiAgent/AiResourceType.test.ts +419 -0
  465. package/Tests/Types/ResourceAiAgent/ResourceAiAccess.test.ts +519 -0
  466. package/Tests/Types/Runbook/RunbookStepType.test.ts +147 -8
  467. package/Tests/Types/Team/ComplianceRule.test.ts +588 -37
  468. package/Tests/Types/Team/TeamComplianceStatus.test.ts +64 -11
  469. package/Tests/Types/XML.test.ts +51 -0
  470. package/Tests/UI/Components/CodeEditor/CodeEditor.test.tsx +2362 -0
  471. package/Tests/UI/Components/CodeEditor/CodeEditorCommands.test.ts +810 -0
  472. package/Tests/UI/Components/CodeEditor/CodeEditorHighlight.test.ts +338 -0
  473. package/Tests/UI/Components/CodeEditor/CodeEditorLanguages.test.ts +222 -0
  474. package/Tests/UI/Components/CodeEditor/CodeEditorStyles.test.ts +807 -0
  475. package/Tests/UI/Components/CodeEditor/NoMonaco.test.ts +245 -0
  476. package/Tests/UI/Components/CodeEditor/YamlEditor.test.tsx +729 -0
  477. package/Tests/UI/Components/CopyTextButton.test.tsx +76 -0
  478. package/Tests/UI/Components/EditionLabelLapseCopy.ts +51 -14
  479. package/Tests/UI/Components/EditionLabelLicenseStatus.test.tsx +131 -23
  480. package/Tests/UI/Components/ErrorMessageAction.test.tsx +142 -0
  481. package/Tests/UI/Components/FacetSidebarInScope.test.tsx +459 -0
  482. package/Tests/UI/Components/FacetValuesInScope.test.ts +270 -0
  483. package/Tests/UI/Components/Forms/FormFieldCodeEditors.test.tsx +294 -0
  484. package/Tests/UI/Components/Forms/FormFieldMarkdown.test.tsx +126 -0
  485. package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +22 -75
  486. package/Tests/UI/Components/Forms/ModelFormOnBeforeCreateMiscData.test.tsx +254 -0
  487. package/Tests/UI/Components/Icon.test.tsx +32 -0
  488. package/Tests/UI/Components/Input.test.tsx +31 -0
  489. package/Tests/UI/Components/LockedFilterChipScope.test.tsx +501 -0
  490. package/Tests/UI/Components/MarkdownConverters.test.ts +253 -1
  491. package/Tests/UI/Components/MarkdownEditor.test.tsx +2785 -2
  492. package/Tests/UI/Components/MarkdownEditorHistory.test.ts +243 -0
  493. package/Tests/UI/Components/MarkdownListEditing.test.ts +1118 -0
  494. package/Tests/UI/Components/MarkdownPaste.test.ts +1180 -0
  495. package/Tests/UI/Components/MarkdownViewerCopy.test.tsx +466 -0
  496. package/Tests/UI/Components/MarkdownVisualEditing.test.ts +1089 -0
  497. package/Tests/UI/Components/ModelTable/BaseModelTableEmptyStateAction.test.tsx +623 -0
  498. package/Tests/UI/Components/ResetObjectID.test.tsx +282 -0
  499. package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
  500. package/Tests/UI/Components/Table/EmptyTableMessage.test.ts +439 -0
  501. package/Tests/UI/Components/Table/TableEmptyState.test.tsx +352 -0
  502. package/Tests/UI/Components/TableRowMobileTitles.test.tsx +78 -0
  503. package/Tests/UI/Components/TextArea.test.tsx +19 -0
  504. package/Tests/UI/Components/Workflow/ModelColumnEditor.render.test.tsx +12 -29
  505. package/Tests/UI/Components/fixtures/MarkdownPasteFixtures.ts +460 -0
  506. package/Tests/UI/EsbuildConfig.test.ts +115 -39
  507. package/Tests/UI/EsbuildEnterpriseAlias.test.ts +4 -4
  508. package/Tests/UI/Monitor/MonitorLogSummaryRender.test.tsx +377 -0
  509. package/Tests/UI/Rum/RecordingHealthDashboard.test.tsx +769 -1
  510. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +81 -1
  511. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +17 -0
  512. package/Tests/Utils/AiRemediation/Resource/CephCommandPolicy.test.ts +1653 -0
  513. package/Tests/Utils/AiRemediation/Resource/CephOutputRedaction.test.ts +204 -0
  514. package/Tests/Utils/AiRemediation/Resource/DatabaseCommandPolicy.test.ts +1077 -0
  515. package/Tests/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.test.ts +558 -0
  516. package/Tests/Utils/AiRemediation/Resource/DatabaseQueryRedactor.test.ts +834 -0
  517. package/Tests/Utils/AiRemediation/Resource/DockerCliGrammar.test.ts +747 -0
  518. package/Tests/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.test.ts +1259 -0
  519. package/Tests/Utils/AiRemediation/Resource/DockerOutputRedaction.test.ts +386 -0
  520. package/Tests/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.test.ts +782 -0
  521. package/Tests/Utils/AiRemediation/Resource/GovcCommandPolicy.test.ts +2259 -0
  522. package/Tests/Utils/AiRemediation/Resource/HostCommandPolicy.test.ts +2019 -0
  523. package/Tests/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.test.ts +2497 -0
  524. package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicy.test.ts +1305 -0
  525. package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.test.ts +585 -0
  526. package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactor.test.ts +334 -0
  527. package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactorCopyParity.test.ts +98 -0
  528. package/Tests/Utils/AiRemediation/Resource/ResourcePolicyImportClosure.test.ts +391 -0
  529. package/Tests/Utils/AiRemediation/ResourceAiAgentPolicyCopyParity.test.ts +497 -0
  530. package/Tests/Utils/LabelRuleImportExport.test.ts +5 -0
  531. package/Tests/Utils/Markdown/DashboardMarkdownRenderer.ts +81 -0
  532. package/Tests/Utils/Markdown/UntrustedMarkdown.test.ts +1339 -0
  533. package/Tests/Utils/Monitor/MonitorLogSummaryUtil.test.ts +862 -0
  534. package/Tests/Utils/SessionReplay/SessionReplayBudgetMetricType.test.ts +383 -0
  535. package/Types/AI/AIAgentRunLimits.ts +146 -0
  536. package/Types/AI/ResourceAiAccessApi.ts +140 -0
  537. package/Types/AI/ResourceAiAccessPermissions.ts +126 -0
  538. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +183 -2
  539. package/Types/Code/CodeType.ts +2 -0
  540. package/Types/Code/JsonSyntax.ts +923 -0
  541. package/Types/CustomField/CustomFieldCreateSettings.ts +549 -0
  542. package/Types/CustomField/CustomFieldMappingCatalog.ts +77 -0
  543. package/Types/CustomField/CustomFieldValueValidator.ts +10 -7
  544. package/Types/EnterpriseLicense/EnterpriseLicensePeriods.ts +1 -1
  545. package/Types/Icon/IconProp.ts +2 -0
  546. package/Types/Incident/IncidentFormIpAllowlist.ts +206 -0
  547. package/Types/Incident/IncidentFormPublic.ts +1145 -0
  548. package/Types/Kubernetes/KubernetesClusterAiAccess.ts +10 -3
  549. package/Types/MessageQueue/MessageQueueIdentity.ts +256 -0
  550. package/Types/MessageQueue/MessageQueueManualIdentity.ts +325 -0
  551. package/Types/MessageQueue/MessageQueueMetricCatalog.ts +1357 -0
  552. package/Types/MessageQueue/MessagingSystem.ts +518 -0
  553. package/Types/MessageQueue/MessagingTelemetryResolver.ts +2311 -0
  554. package/Types/Monitor/MessageQueueAlertTemplates.ts +2128 -0
  555. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +55 -22
  556. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +35 -3
  557. package/Types/Monitor/RumAlertTemplates.ts +351 -4
  558. package/Types/Permission.ts +310 -0
  559. package/Types/ResourceAiAgent/AiResourceType.ts +310 -0
  560. package/Types/ResourceAiAgent/ResourceAiAccess.ts +580 -0
  561. package/Types/Rules/RuleCriteriaFieldRegistry.ts +12 -0
  562. package/Types/Rules/RuleRun.ts +4 -0
  563. package/Types/Rum/SessionReplayBudgetMetricType.ts +47 -0
  564. package/Types/Runbook/RunbookStepType.ts +29 -0
  565. package/Types/Team/ComplianceNotificationChannel.ts +4 -3
  566. package/Types/Team/ComplianceRule.ts +71 -18
  567. package/Types/Team/ComplianceRuleType.ts +2 -2
  568. package/Types/Team/TeamComplianceStatus.ts +6 -2
  569. package/Types/Telemetry/EntityType.ts +16 -0
  570. package/Types/Telemetry/LockedFilterDetail.ts +22 -0
  571. package/Types/Telemetry/TelemetryIngestSurface.ts +14 -2
  572. package/Types/XML.ts +26 -0
  573. package/UI/Components/CodeEditor/CodeEditor.css +379 -0
  574. package/UI/Components/CodeEditor/CodeEditor.tsx +1128 -294
  575. package/UI/Components/CodeEditor/CodeEditorCommands.ts +774 -0
  576. package/UI/Components/CodeEditor/CodeEditorHighlight.ts +106 -0
  577. package/UI/Components/CodeEditor/CodeEditorLanguages.ts +164 -0
  578. package/UI/Components/CodeEditor/YamlEditor.tsx +28 -326
  579. package/UI/Components/CopyTextButton/CopyTextButton.tsx +5 -1
  580. package/UI/Components/CustomFields/CustomFieldModelFormFields.tsx +6 -25
  581. package/UI/Components/EditionLabel/EditionLabel.tsx +14 -13
  582. package/UI/Components/ErrorMessage/ErrorMessage.tsx +11 -1
  583. package/UI/Components/Forms/Fields/FormField.tsx +8 -0
  584. package/UI/Components/Forms/Types/Field.ts +8 -0
  585. package/UI/Components/Icon/Icon.tsx +18 -0
  586. package/UI/Components/Input/Input.tsx +11 -4
  587. package/UI/Components/LogsViewer/LogsViewer.tsx +4 -0
  588. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +21 -3
  589. package/UI/Components/Markdown.tsx/MarkdownConverters.ts +0 -0
  590. package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +877 -79
  591. package/UI/Components/Markdown.tsx/MarkdownEditorHistory.ts +284 -0
  592. package/UI/Components/Markdown.tsx/MarkdownListEditing.ts +1362 -0
  593. package/UI/Components/Markdown.tsx/MarkdownPaste.ts +1326 -0
  594. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +106 -10
  595. package/UI/Components/Markdown.tsx/MarkdownVisualEditing.ts +1032 -0
  596. package/UI/Components/ModelTable/BaseModelTable.tsx +85 -7
  597. package/UI/Components/ResetObjectID/ResetObjectID.tsx +35 -9
  598. package/UI/Components/Table/EmptyTableMessage.ts +81 -0
  599. package/UI/Components/Table/Table.tsx +50 -21
  600. package/UI/Components/Table/TableRow.tsx +9 -1
  601. package/UI/Components/TelemetryViewer/FacetVisibility.ts +97 -2
  602. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +13 -0
  603. package/UI/Components/TelemetryViewer/components/LockedFilterChip.tsx +214 -7
  604. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +21 -3
  605. package/UI/Components/TextArea/TextArea.tsx +9 -2
  606. package/UI/Components/Workflow/ModelFieldPicker.tsx +10 -12
  607. package/UI/Styles/Theme.css +8 -0
  608. package/UI/esbuild-config.js +2 -32
  609. package/Utils/AI/InvestigationReport.ts +121 -15
  610. package/Utils/AiRemediation/Resource/CephCommandPolicy.ts +1936 -0
  611. package/Utils/AiRemediation/Resource/DatabaseCommandPolicy.ts +166 -0
  612. package/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.ts +1532 -0
  613. package/Utils/AiRemediation/Resource/DatabaseQueryRedactor.ts +1394 -0
  614. package/Utils/AiRemediation/Resource/DockerCliGrammar.ts +1839 -0
  615. package/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.ts +490 -0
  616. package/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.ts +687 -0
  617. package/Utils/AiRemediation/Resource/GovcCommandPolicy.ts +2096 -0
  618. package/Utils/AiRemediation/Resource/HostCommandPolicy.ts +2982 -0
  619. package/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.ts +1679 -0
  620. package/Utils/AiRemediation/Resource/ResourceCommandPolicy.ts +851 -0
  621. package/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.ts +593 -0
  622. package/Utils/AiRemediation/Resource/ResourceOutputRedactor.ts +2812 -0
  623. package/Utils/LabelRuleImportExport.ts +2 -0
  624. package/Utils/Markdown/UntrustedMarkdown.ts +1513 -0
  625. package/Utils/Monitor/IncomingEmailMonitorRequestUtil.ts +41 -0
  626. package/Utils/Monitor/MonitorLogSummaryUtil.ts +212 -0
  627. package/Utils/SessionReplay/SessionReplayBudgetMetricType.ts +184 -0
  628. package/Utils/Telemetry/EntityKey.ts +48 -0
  629. package/build/dist/Models/DatabaseModels/AIConversation.js +125 -1
  630. package/build/dist/Models/DatabaseModels/AIConversation.js.map +1 -1
  631. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +62 -0
  632. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
  633. package/build/dist/Models/DatabaseModels/CephCluster.js +231 -0
  634. package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
  635. package/build/dist/Models/DatabaseModels/CloudResource.js +12 -0
  636. package/build/dist/Models/DatabaseModels/CloudResource.js.map +1 -1
  637. package/build/dist/Models/DatabaseModels/DatabaseServer.js +231 -0
  638. package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -1
  639. package/build/dist/Models/DatabaseModels/DockerHost.js +231 -0
  640. package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
  641. package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js +231 -0
  642. package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js.map +1 -1
  643. package/build/dist/Models/DatabaseModels/GlobalConfig.js +1 -1
  644. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  645. package/build/dist/Models/DatabaseModels/GlobalOidc.js +0 -4
  646. package/build/dist/Models/DatabaseModels/GlobalOidc.js.map +1 -1
  647. package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +0 -4
  648. package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
  649. package/build/dist/Models/DatabaseModels/GlobalSso.js +0 -4
  650. package/build/dist/Models/DatabaseModels/GlobalSso.js.map +1 -1
  651. package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +0 -4
  652. package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -1
  653. package/build/dist/Models/DatabaseModels/Host.js +231 -0
  654. package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
  655. package/build/dist/Models/DatabaseModels/IncidentForm.js +605 -0
  656. package/build/dist/Models/DatabaseModels/IncidentForm.js.map +1 -0
  657. package/build/dist/Models/DatabaseModels/IncidentFormSubmission.js +299 -0
  658. package/build/dist/Models/DatabaseModels/IncidentFormSubmission.js.map +1 -0
  659. package/build/dist/Models/DatabaseModels/IncidentTemplate.js +60 -0
  660. package/build/dist/Models/DatabaseModels/IncidentTemplate.js.map +1 -1
  661. package/build/dist/Models/DatabaseModels/Index.js +16 -0
  662. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  663. package/build/dist/Models/DatabaseModels/IoTFleet.js +12 -0
  664. package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
  665. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +12 -0
  666. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  667. package/build/dist/Models/DatabaseModels/MessageQueue.js +1106 -0
  668. package/build/dist/Models/DatabaseModels/MessageQueue.js.map +1 -0
  669. package/build/dist/Models/DatabaseModels/MessageQueueLabelRule.js +562 -0
  670. package/build/dist/Models/DatabaseModels/MessageQueueLabelRule.js.map +1 -0
  671. package/build/dist/Models/DatabaseModels/MessageQueueOwnerRule.js +643 -0
  672. package/build/dist/Models/DatabaseModels/MessageQueueOwnerRule.js.map +1 -0
  673. package/build/dist/Models/DatabaseModels/MessageQueueOwnerTeam.js +506 -0
  674. package/build/dist/Models/DatabaseModels/MessageQueueOwnerTeam.js.map +1 -0
  675. package/build/dist/Models/DatabaseModels/MessageQueueOwnerUser.js +505 -0
  676. package/build/dist/Models/DatabaseModels/MessageQueueOwnerUser.js.map +1 -0
  677. package/build/dist/Models/DatabaseModels/PodmanHost.js +231 -0
  678. package/build/dist/Models/DatabaseModels/PodmanHost.js.map +1 -1
  679. package/build/dist/Models/DatabaseModels/Project.js +61 -0
  680. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  681. package/build/dist/Models/DatabaseModels/ProjectOidc.js +0 -4
  682. package/build/dist/Models/DatabaseModels/ProjectOidc.js.map +1 -1
  683. package/build/dist/Models/DatabaseModels/ProjectSso.js +0 -4
  684. package/build/dist/Models/DatabaseModels/ProjectSso.js.map +1 -1
  685. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +231 -0
  686. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
  687. package/build/dist/Models/DatabaseModels/ResourceAiAgent.js +439 -0
  688. package/build/dist/Models/DatabaseModels/ResourceAiAgent.js.map +1 -0
  689. package/build/dist/Models/DatabaseModels/RumApplication.js +12 -0
  690. package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
  691. package/build/dist/Models/DatabaseModels/RunnerJob.js +145 -0
  692. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  693. package/build/dist/Models/DatabaseModels/ServerlessFunction.js +12 -0
  694. package/build/dist/Models/DatabaseModels/ServerlessFunction.js.map +1 -1
  695. package/build/dist/Models/DatabaseModels/Service.js +12 -0
  696. package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
  697. package/build/dist/Models/DatabaseModels/StatusPageOidc.js +0 -4
  698. package/build/dist/Models/DatabaseModels/StatusPageOidc.js.map +1 -1
  699. package/build/dist/Models/DatabaseModels/StatusPageSso.js +0 -4
  700. package/build/dist/Models/DatabaseModels/StatusPageSso.js.map +1 -1
  701. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +49 -3
  702. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
  703. package/build/dist/Models/DatabaseModels/VMwareVCenter.js +231 -0
  704. package/build/dist/Models/DatabaseModels/VMwareVCenter.js.map +1 -1
  705. package/build/dist/Server/API/AIChatAPI.js +9 -103
  706. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  707. package/build/dist/Server/API/AIInvestigationAPI.js +3 -3
  708. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  709. package/build/dist/Server/API/AIInvestigationConversationAPI.js +162 -0
  710. package/build/dist/Server/API/AIInvestigationConversationAPI.js.map +1 -0
  711. package/build/dist/Server/API/AutoRemediationAPI.js +231 -1
  712. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  713. package/build/dist/Server/API/IncidentFormAPI.js +245 -0
  714. package/build/dist/Server/API/IncidentFormAPI.js.map +1 -0
  715. package/build/dist/Server/API/ProjectOIDC.js +16 -24
  716. package/build/dist/Server/API/ProjectOIDC.js.map +1 -1
  717. package/build/dist/Server/API/ProjectSSO.js +16 -24
  718. package/build/dist/Server/API/ProjectSSO.js.map +1 -1
  719. package/build/dist/Server/API/ResourceAiAccessAPI.js +1119 -0
  720. package/build/dist/Server/API/ResourceAiAccessAPI.js.map +1 -0
  721. package/build/dist/Server/API/StatusPageAPI.js +42 -75
  722. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  723. package/build/dist/Server/Enterprise/EnterpriseEdition.js +79 -25
  724. package/build/dist/Server/Enterprise/EnterpriseEdition.js.map +1 -1
  725. package/build/dist/Server/Enterprise/EnterpriseFeature.js +24 -3
  726. package/build/dist/Server/Enterprise/EnterpriseFeature.js.map +1 -1
  727. package/build/dist/Server/Enterprise/EnterpriseLicenseSnapshot.js +1 -1
  728. package/build/dist/Server/Enterprise/EnterpriseServerModule.js.map +1 -1
  729. package/build/dist/Server/EnvironmentConfig.js +1 -1
  730. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js +168 -0
  731. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js.map +1 -0
  732. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.js +63 -0
  733. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796400000000-AddIncidentForms.js.map +1 -0
  734. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.js +41 -0
  735. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796500000000-AddTeamComplianceRuleNotificationChannels.js.map +1 -0
  736. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.js +40 -0
  737. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796600000000-AddAIInvestigationConversationAndTimeLimits.js.map +1 -0
  738. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.js +183 -0
  739. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796700000000-AddMessageQueueTables.js.map +1 -0
  740. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
  741. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  742. package/build/dist/Server/Infrastructure/Semaphore.js +6 -0
  743. package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
  744. package/build/dist/Server/Middleware/IncidentFormRateLimit.js +559 -0
  745. package/build/dist/Server/Middleware/IncidentFormRateLimit.js.map +1 -0
  746. package/build/dist/Server/Middleware/TelemetryIngest.js +12 -5
  747. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  748. package/build/dist/Server/Middleware/UserAuthorization.js +11 -30
  749. package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
  750. package/build/dist/Server/Services/AIService.js +10 -13
  751. package/build/dist/Server/Services/AIService.js.map +1 -1
  752. package/build/dist/Server/Services/AnalyticsDatabaseService.js +26 -1
  753. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  754. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +601 -3
  755. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
  756. package/build/dist/Server/Services/CephClusterService.js +104 -0
  757. package/build/dist/Server/Services/CephClusterService.js.map +1 -1
  758. package/build/dist/Server/Services/DatabaseServerService.js +99 -0
  759. package/build/dist/Server/Services/DatabaseServerService.js.map +1 -1
  760. package/build/dist/Server/Services/DockerHostService.js +104 -0
  761. package/build/dist/Server/Services/DockerHostService.js.map +1 -1
  762. package/build/dist/Server/Services/DockerSwarmClusterService.js +104 -0
  763. package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
  764. package/build/dist/Server/Services/HostService.js +114 -2
  765. package/build/dist/Server/Services/HostService.js.map +1 -1
  766. package/build/dist/Server/Services/IncidentCustomFieldService.js +83 -0
  767. package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
  768. package/build/dist/Server/Services/IncidentEpisodeMemberService.js +8 -4
  769. package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
  770. package/build/dist/Server/Services/IncidentEpisodeService.js +10 -1
  771. package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
  772. package/build/dist/Server/Services/IncidentFormService.js +1202 -0
  773. package/build/dist/Server/Services/IncidentFormService.js.map +1 -0
  774. package/build/dist/Server/Services/IncidentFormSubmissionService.js +91 -0
  775. package/build/dist/Server/Services/IncidentFormSubmissionService.js.map +1 -0
  776. package/build/dist/Server/Services/IncidentGroupingEngineService.js +20 -8
  777. package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
  778. package/build/dist/Server/Services/IncidentService.js +106 -4
  779. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  780. package/build/dist/Server/Services/IncidentTemplateService.js +18 -0
  781. package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
  782. package/build/dist/Server/Services/Index.js +14 -0
  783. package/build/dist/Server/Services/Index.js.map +1 -1
  784. package/build/dist/Server/Services/MessageQueueLabelRuleEngineService.js +262 -0
  785. package/build/dist/Server/Services/MessageQueueLabelRuleEngineService.js.map +1 -0
  786. package/build/dist/Server/Services/MessageQueueLabelRuleService.js +13 -0
  787. package/build/dist/Server/Services/MessageQueueLabelRuleService.js.map +1 -0
  788. package/build/dist/Server/Services/MessageQueueOwnerRuleEngineService.js +334 -0
  789. package/build/dist/Server/Services/MessageQueueOwnerRuleEngineService.js.map +1 -0
  790. package/build/dist/Server/Services/MessageQueueOwnerRuleService.js +13 -0
  791. package/build/dist/Server/Services/MessageQueueOwnerRuleService.js.map +1 -0
  792. package/build/dist/Server/Services/MessageQueueOwnerTeamService.js +62 -0
  793. package/build/dist/Server/Services/MessageQueueOwnerTeamService.js.map +1 -0
  794. package/build/dist/Server/Services/MessageQueueOwnerUserService.js +62 -0
  795. package/build/dist/Server/Services/MessageQueueOwnerUserService.js.map +1 -0
  796. package/build/dist/Server/Services/MessageQueueService.js +1140 -0
  797. package/build/dist/Server/Services/MessageQueueService.js.map +1 -0
  798. package/build/dist/Server/Services/MetricRecordingRuleService.js +46 -0
  799. package/build/dist/Server/Services/MetricRecordingRuleService.js.map +1 -1
  800. package/build/dist/Server/Services/OpenTelemetryIngestService.js +25 -4
  801. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  802. package/build/dist/Server/Services/PodmanHostService.js +104 -0
  803. package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
  804. package/build/dist/Server/Services/ProjectService.js +0 -54
  805. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  806. package/build/dist/Server/Services/ProxmoxClusterService.js +104 -0
  807. package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
  808. package/build/dist/Server/Services/ResourceAiAccessService.js +1028 -0
  809. package/build/dist/Server/Services/ResourceAiAccessService.js.map +1 -0
  810. package/build/dist/Server/Services/ResourceAiAgentJobService.js +173 -0
  811. package/build/dist/Server/Services/ResourceAiAgentJobService.js.map +1 -0
  812. package/build/dist/Server/Services/ResourceAiAgentService.js +1687 -0
  813. package/build/dist/Server/Services/ResourceAiAgentService.js.map +1 -0
  814. package/build/dist/Server/Services/RunnerJobService.js +344 -5
  815. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  816. package/build/dist/Server/Services/StatusPagePrivateUserService.js +1 -4
  817. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  818. package/build/dist/Server/Services/StatusPageService.js +0 -46
  819. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  820. package/build/dist/Server/Services/TeamComplianceSettingService.js +120 -31
  821. package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
  822. package/build/dist/Server/Services/TelemetryUsageBillingService.js +26 -0
  823. package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
  824. package/build/dist/Server/Services/TraceRecordingRuleService.js +37 -0
  825. package/build/dist/Server/Services/TraceRecordingRuleService.js.map +1 -1
  826. package/build/dist/Server/Services/UserNotificationRuleService.js +8 -1
  827. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  828. package/build/dist/Server/Services/VMwareVCenterService.js +104 -0
  829. package/build/dist/Server/Services/VMwareVCenterService.js.map +1 -1
  830. package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js +12 -3
  831. package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js.map +1 -1
  832. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js +6 -3
  833. package/build/dist/Server/Types/Database/Permissions/CreatePermission.js.map +1 -1
  834. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js +109 -37
  835. package/build/dist/Server/Types/Database/Permissions/EditionPermission.js.map +1 -1
  836. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js +16 -0
  837. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js.map +1 -1
  838. package/build/dist/Server/Types/Database/Permissions/TablePermission.js +3 -3
  839. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js +9 -6
  840. package/build/dist/Server/Types/Database/Permissions/UpdatePermission.js.map +1 -1
  841. package/build/dist/Server/Utils/AI/Chat/AgentContextCompactor.js +128 -0
  842. package/build/dist/Server/Utils/AI/Chat/AgentContextCompactor.js.map +1 -0
  843. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +192 -50
  844. package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
  845. package/build/dist/Server/Utils/AI/Chat/ChatRunCancellation.js +133 -0
  846. package/build/dist/Server/Utils/AI/Chat/ChatRunCancellation.js.map +1 -0
  847. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +53 -13
  848. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  849. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +1 -1
  850. package/build/dist/Server/Utils/AI/Chat/ToolOutputPager.js +206 -0
  851. package/build/dist/Server/Utils/AI/Chat/ToolOutputPager.js.map +1 -0
  852. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +53 -26
  853. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
  854. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +4 -2
  855. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
  856. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +1 -29
  857. package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -1
  858. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +24 -24
  859. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
  860. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1036 -32
  861. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  862. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +661 -41
  863. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  864. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +21 -1
  865. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -1
  866. package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js +354 -0
  867. package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js.map +1 -0
  868. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js +160 -0
  869. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js.map +1 -0
  870. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js +33 -0
  871. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js.map +1 -0
  872. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js +640 -0
  873. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js.map +1 -0
  874. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js +226 -0
  875. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js.map +1 -0
  876. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js +602 -0
  877. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js.map +1 -0
  878. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +90 -14
  879. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  880. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +73 -10
  881. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  882. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +73 -10
  883. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  884. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js +15 -18
  885. package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -1
  886. package/build/dist/Server/Utils/AI/SRE/InvestigationThread.js +412 -0
  887. package/build/dist/Server/Utils/AI/SRE/InvestigationThread.js.map +1 -0
  888. package/build/dist/Server/Utils/AI/SRE/InvestigationThreadService.js +495 -0
  889. package/build/dist/Server/Utils/AI/SRE/InvestigationThreadService.js.map +1 -0
  890. package/build/dist/Server/Utils/AI/Toolbox/Serializer.js +10 -3
  891. package/build/dist/Server/Utils/AI/Toolbox/Serializer.js.map +1 -1
  892. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +290 -13
  893. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
  894. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +29 -1
  895. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
  896. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +9 -1
  897. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  898. package/build/dist/Server/Utils/EditionEnforcement.js +20 -158
  899. package/build/dist/Server/Utils/EditionEnforcement.js.map +1 -1
  900. package/build/dist/Server/Utils/LLM/LLMService.js +193 -54
  901. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  902. package/build/dist/Server/Utils/RelaxedScimTeamLocksReport.js +160 -0
  903. package/build/dist/Server/Utils/RelaxedScimTeamLocksReport.js.map +1 -0
  904. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +25 -0
  905. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
  906. package/build/dist/Server/Utils/SameOriginRequest.js +151 -0
  907. package/build/dist/Server/Utils/SameOriginRequest.js.map +1 -0
  908. package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js +627 -0
  909. package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js.map +1 -0
  910. package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js +60 -5
  911. package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js.map +1 -1
  912. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +2 -0
  913. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -1
  914. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +4 -1
  915. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  916. package/build/dist/Server/Utils/Telemetry/MessageQueueDiscovery.js +1015 -0
  917. package/build/dist/Server/Utils/Telemetry/MessageQueueDiscovery.js.map +1 -0
  918. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +4 -1
  919. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
  920. package/build/dist/Server/Utils/VendorAssets.js +3 -3
  921. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +19 -15
  922. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
  923. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +19 -15
  924. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
  925. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +17 -1
  926. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
  927. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +186 -210
  928. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
  929. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +22 -16
  930. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
  931. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +233 -167
  932. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
  933. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +273 -111
  934. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  935. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js +129 -0
  936. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js.map +1 -0
  937. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js +266 -0
  938. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js.map +1 -0
  939. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js +258 -0
  940. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js.map +1 -0
  941. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js +104 -0
  942. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js.map +1 -0
  943. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js +183 -0
  944. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js.map +1 -0
  945. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js +123 -0
  946. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js.map +1 -0
  947. package/build/dist/Types/AI/AIAgentRunLimits.js +91 -0
  948. package/build/dist/Types/AI/AIAgentRunLimits.js.map +1 -0
  949. package/build/dist/Types/AI/ResourceAiAccessApi.js +30 -0
  950. package/build/dist/Types/AI/ResourceAiAccessApi.js.map +1 -0
  951. package/build/dist/Types/AI/ResourceAiAccessPermissions.js +98 -0
  952. package/build/dist/Types/AI/ResourceAiAccessPermissions.js.map +1 -0
  953. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +116 -29
  954. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
  955. package/build/dist/Types/Code/CodeType.js +2 -0
  956. package/build/dist/Types/Code/CodeType.js.map +1 -1
  957. package/build/dist/Types/Code/JsonSyntax.js +604 -0
  958. package/build/dist/Types/Code/JsonSyntax.js.map +1 -0
  959. package/build/dist/Types/CustomField/CustomFieldCreateSettings.js +310 -0
  960. package/build/dist/Types/CustomField/CustomFieldCreateSettings.js.map +1 -0
  961. package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js +38 -0
  962. package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js.map +1 -1
  963. package/build/dist/Types/CustomField/CustomFieldValueValidator.js +10 -7
  964. package/build/dist/Types/CustomField/CustomFieldValueValidator.js.map +1 -1
  965. package/build/dist/Types/EnterpriseLicense/EnterpriseLicensePeriods.js +1 -1
  966. package/build/dist/Types/Icon/IconProp.js +2 -0
  967. package/build/dist/Types/Icon/IconProp.js.map +1 -1
  968. package/build/dist/Types/Incident/IncidentFormIpAllowlist.js +119 -0
  969. package/build/dist/Types/Incident/IncidentFormIpAllowlist.js.map +1 -0
  970. package/build/dist/Types/Incident/IncidentFormPublic.js +678 -0
  971. package/build/dist/Types/Incident/IncidentFormPublic.js.map +1 -0
  972. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +10 -3
  973. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
  974. package/build/dist/Types/MessageQueue/MessageQueueIdentity.js +184 -0
  975. package/build/dist/Types/MessageQueue/MessageQueueIdentity.js.map +1 -0
  976. package/build/dist/Types/MessageQueue/MessageQueueManualIdentity.js +190 -0
  977. package/build/dist/Types/MessageQueue/MessageQueueManualIdentity.js.map +1 -0
  978. package/build/dist/Types/MessageQueue/MessageQueueMetricCatalog.js +1057 -0
  979. package/build/dist/Types/MessageQueue/MessageQueueMetricCatalog.js.map +1 -0
  980. package/build/dist/Types/MessageQueue/MessagingSystem.js +394 -0
  981. package/build/dist/Types/MessageQueue/MessagingSystem.js.map +1 -0
  982. package/build/dist/Types/MessageQueue/MessagingTelemetryResolver.js +1768 -0
  983. package/build/dist/Types/MessageQueue/MessagingTelemetryResolver.js.map +1 -0
  984. package/build/dist/Types/Monitor/MessageQueueAlertTemplates.js +1207 -0
  985. package/build/dist/Types/Monitor/MessageQueueAlertTemplates.js.map +1 -0
  986. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +47 -21
  987. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  988. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +5 -0
  989. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  990. package/build/dist/Types/Monitor/RumAlertTemplates.js +199 -3
  991. package/build/dist/Types/Monitor/RumAlertTemplates.js.map +1 -1
  992. package/build/dist/Types/Permission.js +274 -0
  993. package/build/dist/Types/Permission.js.map +1 -1
  994. package/build/dist/Types/ResourceAiAgent/AiResourceType.js +242 -0
  995. package/build/dist/Types/ResourceAiAgent/AiResourceType.js.map +1 -0
  996. package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js +281 -0
  997. package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js.map +1 -0
  998. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +12 -0
  999. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
  1000. package/build/dist/Types/Rules/RuleRun.js +4 -0
  1001. package/build/dist/Types/Rules/RuleRun.js.map +1 -1
  1002. package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js +45 -0
  1003. package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js.map +1 -0
  1004. package/build/dist/Types/Runbook/RunbookStepType.js +27 -0
  1005. package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
  1006. package/build/dist/Types/Team/ComplianceNotificationChannel.js +4 -3
  1007. package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -1
  1008. package/build/dist/Types/Team/ComplianceRule.js +48 -14
  1009. package/build/dist/Types/Team/ComplianceRule.js.map +1 -1
  1010. package/build/dist/Types/Team/ComplianceRuleType.js +2 -2
  1011. package/build/dist/Types/Telemetry/EntityType.js +16 -0
  1012. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  1013. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +13 -2
  1014. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
  1015. package/build/dist/Types/XML.js +23 -0
  1016. package/build/dist/Types/XML.js.map +1 -1
  1017. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +641 -266
  1018. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  1019. package/build/dist/UI/Components/CodeEditor/CodeEditorCommands.js +464 -0
  1020. package/build/dist/UI/Components/CodeEditor/CodeEditorCommands.js.map +1 -0
  1021. package/build/dist/UI/Components/CodeEditor/CodeEditorHighlight.js +69 -0
  1022. package/build/dist/UI/Components/CodeEditor/CodeEditorHighlight.js.map +1 -0
  1023. package/build/dist/UI/Components/CodeEditor/CodeEditorLanguages.js +108 -0
  1024. package/build/dist/UI/Components/CodeEditor/CodeEditorLanguages.js.map +1 -0
  1025. package/build/dist/UI/Components/CodeEditor/YamlEditor.js +14 -186
  1026. package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -1
  1027. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js +5 -1
  1028. package/build/dist/UI/Components/CopyTextButton/CopyTextButton.js.map +1 -1
  1029. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js +5 -14
  1030. package/build/dist/UI/Components/CustomFields/CustomFieldModelFormFields.js.map +1 -1
  1031. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +14 -13
  1032. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  1033. package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js +1 -1
  1034. package/build/dist/UI/Components/ErrorMessage/ErrorMessage.js.map +1 -1
  1035. package/build/dist/UI/Components/Forms/Fields/FormField.js +8 -3
  1036. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  1037. package/build/dist/UI/Components/Icon/Icon.js +8 -0
  1038. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  1039. package/build/dist/UI/Components/Input/Input.js +11 -3
  1040. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  1041. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +2 -1
  1042. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  1043. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +14 -4
  1044. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  1045. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js +0 -0
  1046. package/build/dist/UI/Components/Markdown.tsx/MarkdownConverters.js.map +1 -1
  1047. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +710 -65
  1048. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
  1049. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditorHistory.js +238 -0
  1050. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditorHistory.js.map +1 -0
  1051. package/build/dist/UI/Components/Markdown.tsx/MarkdownListEditing.js +1028 -0
  1052. package/build/dist/UI/Components/Markdown.tsx/MarkdownListEditing.js.map +1 -0
  1053. package/build/dist/UI/Components/Markdown.tsx/MarkdownPaste.js +1088 -0
  1054. package/build/dist/UI/Components/Markdown.tsx/MarkdownPaste.js.map +1 -0
  1055. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +70 -10
  1056. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  1057. package/build/dist/UI/Components/Markdown.tsx/MarkdownVisualEditing.js +887 -0
  1058. package/build/dist/UI/Components/Markdown.tsx/MarkdownVisualEditing.js.map +1 -0
  1059. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +57 -6
  1060. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  1061. package/build/dist/UI/Components/ResetObjectID/ResetObjectID.js +7 -5
  1062. package/build/dist/UI/Components/ResetObjectID/ResetObjectID.js.map +1 -1
  1063. package/build/dist/UI/Components/Table/EmptyTableMessage.js +51 -0
  1064. package/build/dist/UI/Components/Table/EmptyTableMessage.js.map +1 -0
  1065. package/build/dist/UI/Components/Table/Table.js +30 -11
  1066. package/build/dist/UI/Components/Table/Table.js.map +1 -1
  1067. package/build/dist/UI/Components/Table/TableRow.js +9 -2
  1068. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  1069. package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js +63 -1
  1070. package/build/dist/UI/Components/TelemetryViewer/FacetVisibility.js.map +1 -1
  1071. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +4 -2
  1072. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  1073. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js +91 -10
  1074. package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js.map +1 -1
  1075. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +14 -4
  1076. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  1077. package/build/dist/UI/Components/TextArea/TextArea.js +9 -3
  1078. package/build/dist/UI/Components/TextArea/TextArea.js.map +1 -1
  1079. package/build/dist/UI/Components/Workflow/ModelFieldPicker.js +1 -2
  1080. package/build/dist/UI/Components/Workflow/ModelFieldPicker.js.map +1 -1
  1081. package/build/dist/Utils/AI/InvestigationReport.js +71 -11
  1082. package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
  1083. package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js +1462 -0
  1084. package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js.map +1 -0
  1085. package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js +122 -0
  1086. package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js.map +1 -0
  1087. package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js +1139 -0
  1088. package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js.map +1 -0
  1089. package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js +1060 -0
  1090. package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js.map +1 -0
  1091. package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js +1301 -0
  1092. package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js.map +1 -0
  1093. package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js +370 -0
  1094. package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js.map +1 -0
  1095. package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js +498 -0
  1096. package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js.map +1 -0
  1097. package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js +1565 -0
  1098. package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js.map +1 -0
  1099. package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js +2206 -0
  1100. package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js.map +1 -0
  1101. package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js +1129 -0
  1102. package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js.map +1 -0
  1103. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js +565 -0
  1104. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js.map +1 -0
  1105. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js +408 -0
  1106. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js.map +1 -0
  1107. package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js +1910 -0
  1108. package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js.map +1 -0
  1109. package/build/dist/Utils/LabelRuleImportExport.js +2 -0
  1110. package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
  1111. package/build/dist/Utils/Markdown/UntrustedMarkdown.js +988 -0
  1112. package/build/dist/Utils/Markdown/UntrustedMarkdown.js.map +1 -0
  1113. package/build/dist/Utils/Monitor/IncomingEmailMonitorRequestUtil.js +34 -0
  1114. package/build/dist/Utils/Monitor/IncomingEmailMonitorRequestUtil.js.map +1 -0
  1115. package/build/dist/Utils/Monitor/MonitorLogSummaryUtil.js +148 -0
  1116. package/build/dist/Utils/Monitor/MonitorLogSummaryUtil.js.map +1 -0
  1117. package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js +160 -0
  1118. package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js.map +1 -0
  1119. package/build/dist/Utils/Telemetry/EntityKey.js +36 -0
  1120. package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
  1121. package/package.json +1 -3
  1122. package/Server/Utils/CommunityEditionSsoReport.ts +0 -361
  1123. package/Tests/App/Accounts/LoginSsoEdition.test.tsx +0 -616
  1124. package/Tests/App/AdminDashboard/AdminAuthenticationSsoEdition.test.tsx +0 -217
  1125. package/Tests/App/AdminDashboard/GlobalSSOEnterpriseShells.test.tsx +0 -271
  1126. package/Tests/App/Dashboard/CloudDocumentationMarkdown.test.ts +0 -215
  1127. package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +0 -784
  1128. package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +0 -518
  1129. package/Tests/Server/Services/SsoRequirementReadMasking.test.ts +0 -419
  1130. package/Tests/Server/Services/SsoRequirementWriteGuard.test.ts +0 -769
  1131. package/Tests/UI/Components/CodeEditor.test.tsx +0 -638
  1132. package/Tests/UI/Components/MonacoLoader.test.ts +0 -67
  1133. package/Tests/UI/Components/MonacoRuntime.test.ts +0 -121
  1134. package/Tests/UI/Components/Telemetry/TelemetryRetentionConfigForm.test.tsx +0 -329
  1135. package/Tests/UI/Components/YamlEditor.test.tsx +0 -847
  1136. package/UI/Components/CodeEditor/MonacoLoader.ts +0 -36
  1137. package/UI/Components/Telemetry/TelemetryRetentionConfigForm.tsx +0 -319
  1138. package/UI/Components/Telemetry/TelemetryRetentionConfigSummary.tsx +0 -223
  1139. package/build/dist/Server/Utils/CommunityEditionSsoReport.js +0 -223
  1140. package/build/dist/Server/Utils/CommunityEditionSsoReport.js.map +0 -1
  1141. package/build/dist/UI/Components/CodeEditor/MonacoLoader.js +0 -34
  1142. package/build/dist/UI/Components/CodeEditor/MonacoLoader.js.map +0 -1
  1143. package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js +0 -171
  1144. package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigForm.js.map +0 -1
  1145. package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigSummary.js +0 -119
  1146. package/build/dist/UI/Components/Telemetry/TelemetryRetentionConfigSummary.js.map +0 -1
@@ -0,0 +1,2432 @@
1
+ import CephClusterFeedService from "./CephClusterFeedService";
2
+ import CephClusterService from "./CephClusterService";
3
+ import DatabaseServerFeedService from "./DatabaseServerFeedService";
4
+ import DatabaseServerService from "./DatabaseServerService";
5
+ import DatabaseService from "./DatabaseService";
6
+ import DockerHostFeedService from "./DockerHostFeedService";
7
+ import DockerHostService from "./DockerHostService";
8
+ import DockerSwarmClusterFeedService from "./DockerSwarmClusterFeedService";
9
+ import DockerSwarmClusterService from "./DockerSwarmClusterService";
10
+ import HostFeedService from "./HostFeedService";
11
+ import HostService from "./HostService";
12
+ import PodmanHostFeedService from "./PodmanHostFeedService";
13
+ import PodmanHostService from "./PodmanHostService";
14
+ import ProxmoxClusterFeedService from "./ProxmoxClusterFeedService";
15
+ import ProxmoxClusterService from "./ProxmoxClusterService";
16
+ import UserService from "./UserService";
17
+ import VMwareVCenterFeedService from "./VMwareVCenterFeedService";
18
+ import VMwareVCenterService from "./VMwareVCenterService";
19
+ import { CephClusterFeedEventType } from "../../Models/DatabaseModels/CephClusterFeed";
20
+ import DatabaseBaseModel from "../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
21
+ import DatabaseServer from "../../Models/DatabaseModels/DatabaseServer";
22
+ import { DatabaseServerFeedEventType } from "../../Models/DatabaseModels/DatabaseServerFeed";
23
+ import { DockerHostFeedEventType } from "../../Models/DatabaseModels/DockerHostFeed";
24
+ import { DockerSwarmClusterFeedEventType } from "../../Models/DatabaseModels/DockerSwarmClusterFeed";
25
+ import { HostFeedEventType } from "../../Models/DatabaseModels/HostFeed";
26
+ import { PodmanHostFeedEventType } from "../../Models/DatabaseModels/PodmanHostFeed";
27
+ import { ProxmoxClusterFeedEventType } from "../../Models/DatabaseModels/ProxmoxClusterFeed";
28
+ import Model from "../../Models/DatabaseModels/ResourceAiAgent";
29
+ import { VMwareVCenterFeedEventType } from "../../Models/DatabaseModels/VMwareVCenterFeed";
30
+ import QueryHelper from "../Types/Database/QueryHelper";
31
+ import Select from "../Types/Database/Select";
32
+ import logger from "../Utils/Logger";
33
+ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
34
+ import { Blue500, Green500 } from "../../Types/BrandColors";
35
+ import Color from "../../Types/Color";
36
+ import ColumnLength from "../../Types/Database/ColumnLength";
37
+ import LIMIT_MAX from "../../Types/Database/LimitMax";
38
+ import {
39
+ DatabaseEndpoint,
40
+ formatDatabaseEndpoint,
41
+ } from "../../Types/DatabaseServer/DatabaseEndpoint";
42
+ import DatabaseServerDiscoverySource from "../../Types/DatabaseServer/DatabaseServerDiscoverySource";
43
+ import {
44
+ DATABASE_AGENT_ATTRIBUTE,
45
+ resolveDatabaseFromResourceAttributes,
46
+ } from "../../Types/DatabaseServer/DatabaseTelemetryResolver";
47
+ import OneUptimeDate from "../../Types/Date";
48
+ import BadDataException from "../../Types/Exception/BadDataException";
49
+ import ForbiddenException from "../../Types/Exception/ForbiddenException";
50
+ import NotAuthenticatedException from "../../Types/Exception/NotAuthenticatedException";
51
+ import { JSONObject } from "../../Types/JSON";
52
+ import ObjectID from "../../Types/ObjectID";
53
+ import AiResourceType, {
54
+ AI_RESOURCE_TYPE_INFO,
55
+ ALL_AI_RESOURCE_TYPES,
56
+ AiResourceTypeInfo,
57
+ isAiResourceType,
58
+ parseAiResourceType,
59
+ } from "../../Types/ResourceAiAgent/AiResourceType";
60
+ import {
61
+ MAX_POSTURE_STRING_LENGTH,
62
+ RESOURCE_AI_AGENT_ALIVE_WINDOW_IN_MINUTES,
63
+ RESOURCE_AI_AGENT_RESOURCE_NAME_ENV,
64
+ RESOURCE_AI_AGENT_RESOURCE_TYPE_ENV,
65
+ RESOURCE_AI_ALLOW_WRITES_ENV,
66
+ ResourceAiAgentConnectionStatus,
67
+ ResourceAiAgentPosture,
68
+ ResourceAiAgentRegistrationRefusalReason,
69
+ ResourceAiAgentSummary,
70
+ ResourceAiRemediationMode,
71
+ parseResourceAiAgentPosture,
72
+ parseResourceAiRemediationMode,
73
+ } from "../../Types/ResourceAiAgent/ResourceAiAccess";
74
+ import crypto from "crypto";
75
+
76
+ /*
77
+ * Every column of a ResourceAiAgent row a reader may need — everything
78
+ * except keyHash. The key hash is read only by the agent authentication
79
+ * path (and registration), which select it explicitly; nothing that builds
80
+ * a status, a list or a response ever loads it, so it cannot leak into one
81
+ * by accident.
82
+ */
83
+ export const RESOURCE_AI_AGENT_SELECT_WITHOUT_KEY: Select<Model> = {
84
+ _id: true,
85
+ createdAt: true,
86
+ updatedAt: true,
87
+ projectId: true,
88
+ resourceType: true,
89
+ resourceId: true,
90
+ resourceIdentifier: true,
91
+ agentVersion: true,
92
+ posture: true,
93
+ lastAliveAt: true,
94
+ connectionStatus: true,
95
+ lastRegisteredAt: true,
96
+ registeredWithIngestionKeyId: true,
97
+ lastRefusedRegistrationAt: true,
98
+ lastRefusedRegistrationReason: true,
99
+ };
100
+
101
+ // 32 random bytes, hex: 64 characters, 256 bits.
102
+ const AGENT_KEY_BYTES: number = 32;
103
+
104
+ // What a stored key hash looks like: sha256, hex.
105
+ const SHA256_HEX_PATTERN: RegExp = /^[0-9a-f]{64}$/i;
106
+
107
+ const UUID_PATTERN: RegExp =
108
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
109
+
110
+ /*
111
+ * The subset of a row isOnline / getAgentSummary read. Dates may arrive as
112
+ * Date objects (a hydrated model) or ISO strings (a JSON payload).
113
+ */
114
+ export interface ResourceAiAgentPresenceRow {
115
+ connectionStatus?: string | null | undefined;
116
+ lastAliveAt?: Date | string | null | undefined;
117
+ }
118
+
119
+ type DateLike = Date | string | null | undefined;
120
+
121
+ function toDate(value: DateLike): Date | null {
122
+ if (!value) {
123
+ return null;
124
+ }
125
+
126
+ const date: Date = value instanceof Date ? value : new Date(value);
127
+
128
+ return Number.isNaN(date.getTime()) ? null : date;
129
+ }
130
+
131
+ function toIsoString(value: DateLike): string | null {
132
+ const date: Date | null = toDate(value);
133
+
134
+ return date ? OneUptimeDate.toString(date) : null;
135
+ }
136
+
137
+ /*
138
+ * Ceiling on agent rows one project may hold, and on how many new ones may
139
+ * be created per hour. Registration is authenticated by the project's
140
+ * telemetry ingestion key — a credential every collector and CI job holds —
141
+ * and every distinct resource identity creates a row that is handed
142
+ * commands, so the rows are bounded like anything else an ingestion key can
143
+ * create. The same numbers as the Kubernetes AI agent's: a fleet-wide
144
+ * install that trips the hourly one is only delayed, the agent keeps
145
+ * retrying.
146
+ */
147
+ export const MAX_RESOURCE_AI_AGENTS_PER_PROJECT: number = 250;
148
+ export const MAX_NEW_RESOURCE_AI_AGENTS_PER_PROJECT_PER_HOUR: number = 30;
149
+
150
+ /*
151
+ * An agent row not heard from in this long is removed when the project's
152
+ * cap would otherwise refuse a new agent. Unlike a Kubernetes cluster, a
153
+ * Docker, Podman or Linux host runs one agent per machine, and machines
154
+ * come and go (an autoscaled fleet names every instance anew) while their
155
+ * resources, and so their agent rows, stay: without this, 250 machines
156
+ * that ever registered would lock every later one out for good. The rows
157
+ * still never pass the cap, and a row holds only the agent's connection
158
+ * (the resource keeps its AI settings); an agent that comes back simply
159
+ * registers again.
160
+ */
161
+ export const RESOURCE_AI_AGENT_RECLAIM_AFTER_DAYS: number = 7;
162
+
163
+ /*
164
+ * When a transient refusal (previous_instance_online) tells the agent to
165
+ * try again. It clears within seconds when a container is replaced, and an
166
+ * agent that is refused for longer just keeps asking at this pace.
167
+ */
168
+ export const RESOURCE_AI_AGENT_REGISTRATION_RETRY_AFTER_SECONDS: number = 20;
169
+
170
+ /*
171
+ * The longest identity registration accepts for a resource found by its
172
+ * name or host identifier: those columns are ShortText, so a longer
173
+ * identity could never become a resource row.
174
+ */
175
+ export const MAX_RESOURCE_AI_AGENT_IDENTIFIER_LENGTH: number =
176
+ ColumnLength.ShortText;
177
+
178
+ /*
179
+ * A database registers by its endpoint ("<system>|<host>[:<port>]", a host
180
+ * name may be 253 characters) or its id, never by a ShortText name, so its
181
+ * limit is the posture's: the identity is stored in the posture too.
182
+ */
183
+ export const MAX_DATABASE_AI_AGENT_IDENTIFIER_LENGTH: number =
184
+ MAX_POSTURE_STRING_LENGTH;
185
+
186
+ /*
187
+ * How often, per agent and per server process, a heartbeat checks that the
188
+ * agent's resource still exists. The resource has no foreign key to the
189
+ * agent row (ResourceAiAgent.resourceId is polymorphic), so a deleted
190
+ * resource leaves its agent heartbeating a row nothing can reach — and a
191
+ * collector that re-creates the resource under a new id would show "no
192
+ * agent connected" forever. A gone resource retires the agent row, and the
193
+ * agent's next registration attaches it to whatever row its identity
194
+ * resolves to now.
195
+ */
196
+ export const RESOURCE_AI_AGENT_RESOURCE_CHECK_INTERVAL_MS: number =
197
+ 5 * 60 * 1000;
198
+ const MAX_RESOURCE_CHECKS_REMEMBERED: number = 10_000;
199
+
200
+ /*
201
+ * A 403 from POST /resource-ai-agent-ingest/register that says WHICH
202
+ * refusal it is, so the agent can tell a wait that clears on its own (the
203
+ * transient reason, which also carries retryAfterSeconds) from one that
204
+ * needs an operator. The ingress sends { message, reason,
205
+ * retryAfterSeconds? } and a Retry-After header for the transient one.
206
+ */
207
+ export class ResourceAiAgentRegistrationRefusedException extends ForbiddenException {
208
+ public readonly reason: ResourceAiAgentRegistrationRefusalReason;
209
+ // Set only for a refusal that clears on its own: when to try again.
210
+ public readonly retryAfterSeconds: number | undefined;
211
+
212
+ public constructor(data: {
213
+ message: string;
214
+ reason: ResourceAiAgentRegistrationRefusalReason;
215
+ retryAfterSeconds?: number | undefined;
216
+ }) {
217
+ super(data.message);
218
+ this.reason = data.reason;
219
+ this.retryAfterSeconds = data.retryAfterSeconds;
220
+ }
221
+ }
222
+
223
+ /*
224
+ * How a registration came to hold the resource's agent key.
225
+ *
226
+ * created: the resource had no agent row; this registration created it.
227
+ * continuity: it presented the row's current key — the same agent.
228
+ * reset: an admin reset the agent (no key is valid), so the first
229
+ * registration after that takes the row.
230
+ * signed_off: the previous instance signed off (/disconnect), as a
231
+ * container does on a clean shutdown.
232
+ * offline: the previous instance stopped heartbeating.
233
+ *
234
+ * Only "continuity" is proof. The rest are what a restarted container looks
235
+ * like — and also what anyone holding the ingestion key looks like, which
236
+ * is why none of them can take a row whose agent is online.
237
+ */
238
+ export type ResourceAiAgentRegistrationAdmission =
239
+ | "created"
240
+ | "continuity"
241
+ | "reset"
242
+ | "signed_off"
243
+ | "offline";
244
+
245
+ export interface ResourceAiAgentRegistrationResult {
246
+ agentId: ObjectID;
247
+ // The new agent key. Returned once, here; only its hash is stored.
248
+ agentKey: string;
249
+ resourceType: AiResourceType;
250
+ resourceId: ObjectID;
251
+ // The resource's name in OneUptime (its row's name, else its identity).
252
+ resourceName: string;
253
+ admission: ResourceAiAgentRegistrationAdmission;
254
+ }
255
+
256
+ /*
257
+ * What the server changed on the resource when it applied the agent's
258
+ * first-connection defaults. Empty when nothing needed changing.
259
+ */
260
+ export interface ResourceAiAgentDefaultsApplied {
261
+ turnedOnInvestigation: boolean;
262
+ remediationMode?: ResourceAiRemediationMode | undefined;
263
+ }
264
+
265
+ /*
266
+ * The resource row an agent serves, as registration, the heartbeat and the
267
+ * defaults read it: its identity and its AI settings, read as root from the
268
+ * table its type names.
269
+ */
270
+ export interface ResourceAiAgentResource {
271
+ resourceType: AiResourceType;
272
+ id: ObjectID;
273
+ projectId: ObjectID;
274
+ // The row's name, else the identity the agent registered with.
275
+ name: string;
276
+ isAiInvestigationEnabled?: boolean | undefined;
277
+ aiRemediationMode?: string | undefined;
278
+ aiAccessConfiguredAt?: Date | string | null | undefined;
279
+ }
280
+
281
+ // A feed item on the resource's own feed.
282
+ export interface ResourceAiAgentFeedItem {
283
+ resourceId: ObjectID;
284
+ projectId: ObjectID;
285
+ feedInfoInMarkdown: string;
286
+ moreInformationInMarkdown?: string | undefined;
287
+ displayColor: Color;
288
+ userId?: ObjectID | undefined;
289
+ }
290
+
291
+ /*
292
+ * How the server reaches one resource type: its model's service (reads and
293
+ * the first-connection defaults, always as root), how the collector's
294
+ * identity finds or creates its row, and its feed. Every service is looked
295
+ * up when used, never when this module loads: Common's services import
296
+ * each other in cycles, and an entry read too early would be undefined.
297
+ */
298
+ export interface ResourceAiAgentResourceBinding {
299
+ resourceType: AiResourceType;
300
+ getService: () => DatabaseService<DatabaseBaseModel>;
301
+ /*
302
+ * The row the collector's identity (a host identifier or a name) names,
303
+ * created when OneUptime has not seen it yet. Null for DatabaseServer,
304
+ * which is resolved by id or endpoint instead. `fresh` skips any
305
+ * in-process memo of the answer (HostService keeps one), for a caller
306
+ * that found the memoed row deleted.
307
+ */
308
+ findOrCreateByIdentity:
309
+ | ((data: {
310
+ projectId: ObjectID;
311
+ identifier: string;
312
+ fresh?: boolean | undefined;
313
+ }) => Promise<DatabaseBaseModel>)
314
+ | null;
315
+ // Never throws: every resource feed service swallows its own errors.
316
+ writeFeedItem: (item: ResourceAiAgentFeedItem) => Promise<void>;
317
+ }
318
+
319
+ // The AI columns every resource model carries, as a read row holds them.
320
+ interface ResourceAiColumns {
321
+ _id?: string | undefined;
322
+ projectId?: ObjectID | undefined;
323
+ name?: string | undefined;
324
+ isAiInvestigationEnabled?: boolean | undefined;
325
+ aiRemediationMode?: string | undefined;
326
+ aiAccessConfiguredAt?: Date | undefined;
327
+ }
328
+
329
+ // What registration, the heartbeat and the defaults read off a resource row.
330
+ export const RESOURCE_AI_AGENT_RESOURCE_SELECT: JSONObject = {
331
+ _id: true,
332
+ projectId: true,
333
+ name: true,
334
+ isAiInvestigationEnabled: true,
335
+ aiRemediationMode: true,
336
+ aiAccessConfiguredAt: true,
337
+ };
338
+
339
+ function asService<TModel extends DatabaseBaseModel>(
340
+ service: DatabaseService<TModel>,
341
+ ): DatabaseService<DatabaseBaseModel> {
342
+ return service as unknown as DatabaseService<DatabaseBaseModel>;
343
+ }
344
+
345
+ /*
346
+ * The binding of one resource type (see ResourceAiAgentResourceBinding).
347
+ * Total over AiResourceType: adding a type without a binding fails to
348
+ * compile.
349
+ */
350
+ export function getResourceAiAgentResourceBinding(
351
+ resourceType: AiResourceType,
352
+ ): ResourceAiAgentResourceBinding {
353
+ switch (resourceType) {
354
+ case AiResourceType.DockerHost:
355
+ return {
356
+ resourceType,
357
+ getService: (): DatabaseService<DatabaseBaseModel> => {
358
+ return asService(DockerHostService);
359
+ },
360
+ findOrCreateByIdentity: async (data: {
361
+ projectId: ObjectID;
362
+ identifier: string;
363
+ }): Promise<DatabaseBaseModel> => {
364
+ return await DockerHostService.findOrCreateByHostIdentifier({
365
+ projectId: data.projectId,
366
+ hostIdentifier: data.identifier,
367
+ });
368
+ },
369
+ writeFeedItem: async (item: ResourceAiAgentFeedItem): Promise<void> => {
370
+ await DockerHostFeedService.createDockerHostFeedItem({
371
+ dockerHostId: item.resourceId,
372
+ projectId: item.projectId,
373
+ dockerHostFeedEventType: DockerHostFeedEventType.DockerHostUpdated,
374
+ feedInfoInMarkdown: item.feedInfoInMarkdown,
375
+ moreInformationInMarkdown: item.moreInformationInMarkdown,
376
+ displayColor: item.displayColor,
377
+ userId: item.userId,
378
+ });
379
+ },
380
+ };
381
+ case AiResourceType.PodmanHost:
382
+ return {
383
+ resourceType,
384
+ getService: (): DatabaseService<DatabaseBaseModel> => {
385
+ return asService(PodmanHostService);
386
+ },
387
+ findOrCreateByIdentity: async (data: {
388
+ projectId: ObjectID;
389
+ identifier: string;
390
+ }): Promise<DatabaseBaseModel> => {
391
+ return await PodmanHostService.findOrCreateByHostIdentifier({
392
+ projectId: data.projectId,
393
+ hostIdentifier: data.identifier,
394
+ });
395
+ },
396
+ writeFeedItem: async (item: ResourceAiAgentFeedItem): Promise<void> => {
397
+ await PodmanHostFeedService.createPodmanHostFeedItem({
398
+ podmanHostId: item.resourceId,
399
+ projectId: item.projectId,
400
+ podmanHostFeedEventType: PodmanHostFeedEventType.PodmanHostUpdated,
401
+ feedInfoInMarkdown: item.feedInfoInMarkdown,
402
+ moreInformationInMarkdown: item.moreInformationInMarkdown,
403
+ displayColor: item.displayColor,
404
+ userId: item.userId,
405
+ });
406
+ },
407
+ };
408
+ case AiResourceType.DockerSwarmCluster:
409
+ return {
410
+ resourceType,
411
+ getService: (): DatabaseService<DatabaseBaseModel> => {
412
+ return asService(DockerSwarmClusterService);
413
+ },
414
+ findOrCreateByIdentity: async (data: {
415
+ projectId: ObjectID;
416
+ identifier: string;
417
+ }): Promise<DatabaseBaseModel> => {
418
+ return await DockerSwarmClusterService.findOrCreateByName({
419
+ projectId: data.projectId,
420
+ name: data.identifier,
421
+ });
422
+ },
423
+ writeFeedItem: async (item: ResourceAiAgentFeedItem): Promise<void> => {
424
+ await DockerSwarmClusterFeedService.createDockerSwarmClusterFeedItem({
425
+ dockerSwarmClusterId: item.resourceId,
426
+ projectId: item.projectId,
427
+ dockerSwarmClusterFeedEventType:
428
+ DockerSwarmClusterFeedEventType.DockerSwarmClusterUpdated,
429
+ feedInfoInMarkdown: item.feedInfoInMarkdown,
430
+ moreInformationInMarkdown: item.moreInformationInMarkdown,
431
+ displayColor: item.displayColor,
432
+ userId: item.userId,
433
+ });
434
+ },
435
+ };
436
+ case AiResourceType.ProxmoxCluster:
437
+ return {
438
+ resourceType,
439
+ getService: (): DatabaseService<DatabaseBaseModel> => {
440
+ return asService(ProxmoxClusterService);
441
+ },
442
+ findOrCreateByIdentity: async (data: {
443
+ projectId: ObjectID;
444
+ identifier: string;
445
+ }): Promise<DatabaseBaseModel> => {
446
+ return await ProxmoxClusterService.findOrCreateByName({
447
+ projectId: data.projectId,
448
+ name: data.identifier,
449
+ });
450
+ },
451
+ writeFeedItem: async (item: ResourceAiAgentFeedItem): Promise<void> => {
452
+ await ProxmoxClusterFeedService.createProxmoxClusterFeedItem({
453
+ proxmoxClusterId: item.resourceId,
454
+ projectId: item.projectId,
455
+ proxmoxClusterFeedEventType:
456
+ ProxmoxClusterFeedEventType.ProxmoxClusterUpdated,
457
+ feedInfoInMarkdown: item.feedInfoInMarkdown,
458
+ moreInformationInMarkdown: item.moreInformationInMarkdown,
459
+ displayColor: item.displayColor,
460
+ userId: item.userId,
461
+ });
462
+ },
463
+ };
464
+ case AiResourceType.VMwareVCenter:
465
+ return {
466
+ resourceType,
467
+ getService: (): DatabaseService<DatabaseBaseModel> => {
468
+ return asService(VMwareVCenterService);
469
+ },
470
+ findOrCreateByIdentity: async (data: {
471
+ projectId: ObjectID;
472
+ identifier: string;
473
+ }): Promise<DatabaseBaseModel> => {
474
+ return await VMwareVCenterService.findOrCreateByName({
475
+ projectId: data.projectId,
476
+ name: data.identifier,
477
+ });
478
+ },
479
+ writeFeedItem: async (item: ResourceAiAgentFeedItem): Promise<void> => {
480
+ await VMwareVCenterFeedService.createVMwareVCenterFeedItem({
481
+ vmwareVCenterId: item.resourceId,
482
+ projectId: item.projectId,
483
+ vmwareVCenterFeedEventType:
484
+ VMwareVCenterFeedEventType.VMwareVCenterUpdated,
485
+ feedInfoInMarkdown: item.feedInfoInMarkdown,
486
+ moreInformationInMarkdown: item.moreInformationInMarkdown,
487
+ displayColor: item.displayColor,
488
+ userId: item.userId,
489
+ });
490
+ },
491
+ };
492
+ case AiResourceType.CephCluster:
493
+ return {
494
+ resourceType,
495
+ getService: (): DatabaseService<DatabaseBaseModel> => {
496
+ return asService(CephClusterService);
497
+ },
498
+ findOrCreateByIdentity: async (data: {
499
+ projectId: ObjectID;
500
+ identifier: string;
501
+ }): Promise<DatabaseBaseModel> => {
502
+ return await CephClusterService.findOrCreateByName({
503
+ projectId: data.projectId,
504
+ name: data.identifier,
505
+ });
506
+ },
507
+ writeFeedItem: async (item: ResourceAiAgentFeedItem): Promise<void> => {
508
+ await CephClusterFeedService.createCephClusterFeedItem({
509
+ cephClusterId: item.resourceId,
510
+ projectId: item.projectId,
511
+ cephClusterFeedEventType:
512
+ CephClusterFeedEventType.CephClusterUpdated,
513
+ feedInfoInMarkdown: item.feedInfoInMarkdown,
514
+ moreInformationInMarkdown: item.moreInformationInMarkdown,
515
+ displayColor: item.displayColor,
516
+ userId: item.userId,
517
+ });
518
+ },
519
+ };
520
+ case AiResourceType.DatabaseServer:
521
+ return {
522
+ resourceType,
523
+ getService: (): DatabaseService<DatabaseBaseModel> => {
524
+ return asService(DatabaseServerService);
525
+ },
526
+ findOrCreateByIdentity: null,
527
+ writeFeedItem: async (item: ResourceAiAgentFeedItem): Promise<void> => {
528
+ await DatabaseServerFeedService.createDatabaseServerFeedItem({
529
+ databaseServerId: item.resourceId,
530
+ projectId: item.projectId,
531
+ databaseServerFeedEventType:
532
+ DatabaseServerFeedEventType.DatabaseServerUpdated,
533
+ feedInfoInMarkdown: item.feedInfoInMarkdown,
534
+ moreInformationInMarkdown: item.moreInformationInMarkdown,
535
+ displayColor: item.displayColor,
536
+ userId: item.userId,
537
+ });
538
+ },
539
+ };
540
+ case AiResourceType.Host:
541
+ return {
542
+ resourceType,
543
+ getService: (): DatabaseService<DatabaseBaseModel> => {
544
+ return asService(HostService);
545
+ },
546
+ findOrCreateByIdentity: async (data: {
547
+ projectId: ObjectID;
548
+ identifier: string;
549
+ fresh?: boolean | undefined;
550
+ }): Promise<DatabaseBaseModel> => {
551
+ return await HostService.findOrCreateByHostIdentifier({
552
+ projectId: data.projectId,
553
+ hostIdentifier: data.identifier,
554
+ ...(data.fresh ? { bypassMemo: true } : {}),
555
+ });
556
+ },
557
+ writeFeedItem: async (item: ResourceAiAgentFeedItem): Promise<void> => {
558
+ await HostFeedService.createHostFeedItem({
559
+ hostId: item.resourceId,
560
+ projectId: item.projectId,
561
+ hostFeedEventType: HostFeedEventType.HostUpdated,
562
+ feedInfoInMarkdown: item.feedInfoInMarkdown,
563
+ moreInformationInMarkdown: item.moreInformationInMarkdown,
564
+ displayColor: item.displayColor,
565
+ userId: item.userId,
566
+ });
567
+ },
568
+ };
569
+ }
570
+ }
571
+
572
+ /*
573
+ * How the resource is named mid-sentence: "Docker host", "Ceph cluster",
574
+ * but "host" and "database server" (common nouns, not product names).
575
+ */
576
+ const COMMON_NOUN_DISPLAY_NAME_PATTERN: RegExp = /^(Host|Database)\b/;
577
+
578
+ export function describeResourceInSentence(
579
+ resourceType: AiResourceType,
580
+ ): string {
581
+ const displayName: string = AI_RESOURCE_TYPE_INFO[resourceType].displayName;
582
+
583
+ return COMMON_NOUN_DISPLAY_NAME_PATTERN.test(displayName)
584
+ ? `${displayName.charAt(0).toLowerCase()}${displayName.slice(1)}`
585
+ : displayName;
586
+ }
587
+
588
+ // Where a database endpoint identity came from, for DB registration.
589
+ interface DatabaseEndpointIdentity {
590
+ system: string;
591
+ address: string;
592
+ port: number | null;
593
+ }
594
+
595
+ /*
596
+ * A key hash that no key can have (its preimage is unknown), compared
597
+ * against when the presented agent id matches no row, so an unknown id and
598
+ * a wrong key cost the same.
599
+ */
600
+ const UNMATCHABLE_KEY_HASH: string = "0".repeat(64);
601
+
602
+ /*
603
+ * The resource AI agent of one infrastructure resource (see the model): its
604
+ * identity, and everything the server does about it.
605
+ *
606
+ * - Shared helpers every caller needs: key minting and hashing, the one
607
+ * online rule, the summary the dashboard sees, and the reads the access
608
+ * status and the enqueue chokepoint use (findAgentForResource,
609
+ * findOnlineAgentForResource, findAgentsForResources).
610
+ * - The identity lifecycle behind /resource-ai-agent-ingest: register
611
+ * (with the project's telemetry ingestion key; hands out an agent key),
612
+ * authenticate (every later request, by agent id and key), heartbeat and
613
+ * sign-off — plus the admin reset on the resource's AI agent page.
614
+ *
615
+ * Registration never deletes or unbinds anything: the resource's settings
616
+ * are left as they are. The only resource setting it ever writes is the
617
+ * first-connection defaults, and only on a resource nobody configured.
618
+ */
619
+ export class Service extends DatabaseService<Model> {
620
+ // Agent id → when this process last checked the agent's resource (ms).
621
+ private resourceCheckedAt: Map<string, number> = new Map<string, number>();
622
+
623
+ public constructor() {
624
+ super(Model);
625
+ }
626
+
627
+ /*
628
+ * sha256 (hex) of an agent key: what ResourceAiAgent.keyHash stores. The
629
+ * key itself is never stored.
630
+ */
631
+ public static hashKey(key: string): string {
632
+ return crypto.createHash("sha256").update(key, "utf8").digest("hex");
633
+ }
634
+
635
+ // A fresh agent key: 32 cryptographically random bytes, hex encoded.
636
+ public static generateKey(): string {
637
+ return crypto.randomBytes(AGENT_KEY_BYTES).toString("hex");
638
+ }
639
+
640
+ /*
641
+ * Does this presented key match the stored hash? Timing-safe, and false —
642
+ * never a throw — for anything that is not a non-empty key and a stored
643
+ * sha256 hex hash (a reset row's hash is null, which nothing matches).
644
+ */
645
+ public static doesKeyMatchHash(key: unknown, keyHash: unknown): boolean {
646
+ if (typeof key !== "string" || key.length === 0) {
647
+ return false;
648
+ }
649
+
650
+ if (typeof keyHash !== "string" || !SHA256_HEX_PATTERN.test(keyHash)) {
651
+ return false;
652
+ }
653
+
654
+ const presented: Buffer = Buffer.from(Service.hashKey(key), "hex");
655
+ const stored: Buffer = Buffer.from(keyHash.toLowerCase(), "hex");
656
+
657
+ return (
658
+ presented.length === stored.length &&
659
+ crypto.timingSafeEqual(presented, stored)
660
+ );
661
+ }
662
+
663
+ // Instance forms of the static helpers, for callers holding the default export.
664
+ public hashKey(key: string): string {
665
+ return Service.hashKey(key);
666
+ }
667
+
668
+ public generateKey(): string {
669
+ return Service.generateKey();
670
+ }
671
+
672
+ public doesKeyMatchHash(key: unknown, keyHash: unknown): boolean {
673
+ return Service.doesKeyMatchHash(key, keyHash);
674
+ }
675
+
676
+ // The binding of a resource type (see getResourceAiAgentResourceBinding).
677
+ public static getResourceBinding(
678
+ resourceType: AiResourceType,
679
+ ): ResourceAiAgentResourceBinding {
680
+ return getResourceAiAgentResourceBinding(resourceType);
681
+ }
682
+
683
+ /*
684
+ * The one "is the agent online?" rule: it says it is connected (it
685
+ * registered or heartbeated and has not signed off or been reset) AND it
686
+ * was heard from within RESOURCE_AI_AGENT_ALIVE_WINDOW_IN_MINUTES. A
687
+ * heartbeat stamped slightly in the future (another server's clock) still
688
+ * counts as recent.
689
+ */
690
+ public isOnline(row: ResourceAiAgentPresenceRow, now?: Date): boolean {
691
+ if (row.connectionStatus !== "connected") {
692
+ return false;
693
+ }
694
+
695
+ const lastAliveAt: Date | null = toDate(row.lastAliveAt);
696
+
697
+ if (!lastAliveAt) {
698
+ return false;
699
+ }
700
+
701
+ const nowTime: number = (now || OneUptimeDate.getCurrentDate()).getTime();
702
+
703
+ return (
704
+ nowTime - lastAliveAt.getTime() <=
705
+ RESOURCE_AI_AGENT_ALIVE_WINDOW_IN_MINUTES * 60 * 1000
706
+ );
707
+ }
708
+
709
+ /*
710
+ * The row as the dashboard and the access status see it. Never carries
711
+ * the key hash; the posture is re-validated on the way out, so a stored
712
+ * value of the wrong shape is dropped (null) rather than trusted. Values
713
+ * that were never set are null.
714
+ */
715
+ public getAgentSummary(
716
+ row: Pick<
717
+ Model,
718
+ | "id"
719
+ | "agentVersion"
720
+ | "posture"
721
+ | "lastRegisteredAt"
722
+ | "lastRefusedRegistrationAt"
723
+ | "lastRefusedRegistrationReason"
724
+ > &
725
+ ResourceAiAgentPresenceRow,
726
+ now?: Date,
727
+ ): ResourceAiAgentSummary {
728
+ const connectionStatus: ResourceAiAgentConnectionStatus =
729
+ row.connectionStatus === "connected" ? "connected" : "disconnected";
730
+
731
+ return {
732
+ agentId: row.id ? row.id.toString() : "",
733
+ connectionStatus,
734
+ isOnline: this.isOnline(row, now),
735
+ agentVersion: row.agentVersion || null,
736
+ lastAliveAt: toIsoString(row.lastAliveAt),
737
+ lastRegisteredAt: toIsoString(row.lastRegisteredAt),
738
+ posture: parseResourceAiAgentPosture(row.posture),
739
+ lastRefusedRegistrationAt: toIsoString(row.lastRefusedRegistrationAt),
740
+ lastRefusedRegistrationReason: row.lastRefusedRegistrationReason || null,
741
+ };
742
+ }
743
+
744
+ // The resource's agent row (as root, without the key hash), or null.
745
+ @CaptureSpan()
746
+ public async findAgentForResource(data: {
747
+ projectId: ObjectID;
748
+ resourceType: AiResourceType;
749
+ resourceId: ObjectID;
750
+ }): Promise<Model | null> {
751
+ if (!isAiResourceType(data.resourceType)) {
752
+ return null;
753
+ }
754
+
755
+ return await this.findOneBy({
756
+ query: {
757
+ projectId: data.projectId,
758
+ resourceType: data.resourceType,
759
+ resourceId: data.resourceId,
760
+ },
761
+ select: RESOURCE_AI_AGENT_SELECT_WITHOUT_KEY,
762
+ props: {
763
+ isRoot: true,
764
+ },
765
+ });
766
+ }
767
+
768
+ /*
769
+ * The resource's agent row when it is online right now (isOnline), else
770
+ * null — what the enqueue chokepoint and the access status route a
771
+ * command to.
772
+ */
773
+ @CaptureSpan()
774
+ public async findOnlineAgentForResource(data: {
775
+ projectId: ObjectID;
776
+ resourceType: AiResourceType;
777
+ resourceId: ObjectID;
778
+ now?: Date | undefined;
779
+ }): Promise<Model | null> {
780
+ const agent: Model | null = await this.findAgentForResource(data);
781
+
782
+ if (!agent || !this.isOnline(agent, data.now)) {
783
+ return null;
784
+ }
785
+
786
+ return agent;
787
+ }
788
+
789
+ /*
790
+ * The agent rows of several resources of one type in one project, in one
791
+ * query, keyed by resource id (lowercase string). Resources without an
792
+ * agent are simply absent.
793
+ */
794
+ @CaptureSpan()
795
+ public async findAgentsForResources(data: {
796
+ projectId: ObjectID;
797
+ resourceType: AiResourceType;
798
+ resourceIds: Array<ObjectID>;
799
+ }): Promise<Map<string, Model>> {
800
+ const byResourceId: Map<string, Model> = new Map<string, Model>();
801
+
802
+ const resourceIds: Array<string> = Array.from(
803
+ new Set(
804
+ data.resourceIds.map((id: ObjectID): string => {
805
+ return id.toString().toLowerCase();
806
+ }),
807
+ ),
808
+ );
809
+
810
+ if (resourceIds.length === 0 || !isAiResourceType(data.resourceType)) {
811
+ return byResourceId;
812
+ }
813
+
814
+ const rows: Array<Model> = await this.findBy({
815
+ query: {
816
+ projectId: data.projectId,
817
+ resourceType: data.resourceType,
818
+ resourceId: QueryHelper.any(resourceIds),
819
+ },
820
+ select: RESOURCE_AI_AGENT_SELECT_WITHOUT_KEY,
821
+ limit: LIMIT_MAX,
822
+ skip: 0,
823
+ props: {
824
+ isRoot: true,
825
+ },
826
+ });
827
+
828
+ for (const row of rows) {
829
+ const resourceId: string | undefined = row.resourceId
830
+ ?.toString()
831
+ .toLowerCase();
832
+
833
+ if (
834
+ resourceId &&
835
+ resourceIds.includes(resourceId) &&
836
+ row.resourceType === data.resourceType
837
+ ) {
838
+ byResourceId.set(resourceId, row);
839
+ }
840
+ }
841
+
842
+ return byResourceId;
843
+ }
844
+
845
+ /*
846
+ * One resource row of a type, in its project, as root, with the columns
847
+ * registration and the defaults need; null when it does not exist.
848
+ */
849
+ @CaptureSpan()
850
+ public async findResource(data: {
851
+ projectId: ObjectID;
852
+ resourceType: AiResourceType;
853
+ resourceId: ObjectID;
854
+ }): Promise<ResourceAiAgentResource | null> {
855
+ if (!isAiResourceType(data.resourceType) || !data.resourceId) {
856
+ return null;
857
+ }
858
+
859
+ const row: DatabaseBaseModel | null = await Service.getResourceBinding(
860
+ data.resourceType,
861
+ )
862
+ .getService()
863
+ .findOneBy({
864
+ query: {
865
+ _id: data.resourceId.toString(),
866
+ projectId: data.projectId,
867
+ },
868
+ select: RESOURCE_AI_AGENT_RESOURCE_SELECT,
869
+ props: { isRoot: true },
870
+ } as never);
871
+
872
+ if (!row || !row.id) {
873
+ return null;
874
+ }
875
+
876
+ const columns: ResourceAiColumns = row as unknown as ResourceAiColumns;
877
+
878
+ return {
879
+ resourceType: data.resourceType,
880
+ id: row.id,
881
+ projectId: columns.projectId || data.projectId,
882
+ name: columns.name?.trim() || "",
883
+ isAiInvestigationEnabled: columns.isAiInvestigationEnabled,
884
+ aiRemediationMode: columns.aiRemediationMode,
885
+ aiAccessConfiguredAt: columns.aiAccessConfiguredAt,
886
+ };
887
+ }
888
+
889
+ /*
890
+ * ------------------------------------------------------------------
891
+ * Registration
892
+ * ------------------------------------------------------------------
893
+ */
894
+
895
+ /*
896
+ * The agent registering for its resource (POST
897
+ * /resource-ai-agent-ingest/register, authenticated by the project's
898
+ * telemetry ingestion key). Hands out a fresh agent key; only its hash is
899
+ * kept.
900
+ *
901
+ * Refused (ResourceAiAgentRegistrationRefusedException, a 403 with the
902
+ * reason) when:
903
+ * - the resource type is not one OneUptime knows (resource_type_invalid);
904
+ * - the identity is empty or too long (resource_identifier_invalid), or
905
+ * a database identity names no endpoint;
906
+ * - the resource cannot be resolved (resource_not_found): a
907
+ * DATABASE_SERVER_ID that is not this project's, or a database
908
+ * endpoint no row owns and none may be created for;
909
+ * - the resource's agent is online and the request did not present its
910
+ * current key (previous_instance_online) — recorded on the row so the
911
+ * AI agent page can say another agent tried;
912
+ * - a new row would pass the project's caps (agent_cap_reached).
913
+ *
914
+ * An existing row is re-keyed when the request proves continuity (its
915
+ * current key as previousAgentKey), or when the row was reset, or its
916
+ * agent signed off or went quiet: what a restarted container looks like.
917
+ *
918
+ * On a resource nobody configured it applies the first-connection
919
+ * defaults (applyFirstConnectionDefaults). It never deletes, unbinds or
920
+ * rebinds anything, and writes ONE feed item when a resource's agent
921
+ * first connects.
922
+ */
923
+ @CaptureSpan()
924
+ public async register(data: {
925
+ projectId: ObjectID;
926
+ resourceType: unknown;
927
+ resourceIdentifier: unknown;
928
+ // Set when the operator pinned the row (DATABASE_SERVER_ID).
929
+ resourceId?: unknown;
930
+ agentVersion?: string | undefined;
931
+ // The key the agent currently holds, when it still has one.
932
+ previousAgentKey?: string | undefined;
933
+ // The posture the body reports; parsed here, never trusted as is.
934
+ posture?: unknown;
935
+ // The TelemetryIngestionKey the request was admitted with, when known.
936
+ ingestionKeyId?: ObjectID | undefined;
937
+ }): Promise<ResourceAiAgentRegistrationResult> {
938
+ const resourceType: AiResourceType = this.getValidResourceType(
939
+ data.resourceType,
940
+ );
941
+
942
+ const identifier: string = this.getValidResourceIdentifier({
943
+ resourceType,
944
+ raw: data.resourceIdentifier,
945
+ });
946
+
947
+ const agentVersion: string | undefined = Service.normalizeAgentVersion(
948
+ data.agentVersion,
949
+ );
950
+
951
+ const posture: ResourceAiAgentPosture = this.buildStoredPosture({
952
+ reported: data.posture,
953
+ resourceType,
954
+ resourceIdentifier: identifier,
955
+ });
956
+
957
+ const resource: ResourceAiAgentResource = await this.resolveResource({
958
+ projectId: data.projectId,
959
+ resourceType,
960
+ identifier,
961
+ resourceId: data.resourceId,
962
+ posture,
963
+ });
964
+
965
+ const info: AiResourceTypeInfo = AI_RESOURCE_TYPE_INFO[resourceType];
966
+ const agentKey: string = Service.generateKey();
967
+ const now: Date = OneUptimeDate.getCurrentDate();
968
+
969
+ const existing: Model | null = await this.findOneBy({
970
+ query: {
971
+ projectId: data.projectId,
972
+ resourceType,
973
+ resourceId: resource.id,
974
+ },
975
+ select: { ...RESOURCE_AI_AGENT_SELECT_WITHOUT_KEY, keyHash: true },
976
+ props: { isRoot: true },
977
+ });
978
+
979
+ let agentId: ObjectID;
980
+ let admission: ResourceAiAgentRegistrationAdmission;
981
+
982
+ if (existing && existing.id) {
983
+ admission = await this.admitReRegistration({
984
+ agent: existing,
985
+ previousAgentKey: data.previousAgentKey,
986
+ resourceType,
987
+ identifier,
988
+ projectId: data.projectId,
989
+ });
990
+
991
+ await this.updateOneById({
992
+ id: existing.id,
993
+ data: {
994
+ keyHash: Service.hashKey(agentKey),
995
+ resourceIdentifier: identifier,
996
+ posture: posture as unknown as JSONObject,
997
+ lastAliveAt: now,
998
+ connectionStatus: "connected",
999
+ lastRegisteredAt: now,
1000
+ registeredWithIngestionKeyId: data.ingestionKeyId || null,
1001
+ ...(agentVersion ? { agentVersion } : {}),
1002
+ } as never,
1003
+ props: { isRoot: true },
1004
+ });
1005
+
1006
+ agentId = existing.id;
1007
+ } else {
1008
+ await this.assertAgentMayBeCreated({
1009
+ projectId: data.projectId,
1010
+ resourceType,
1011
+ identifier,
1012
+ });
1013
+
1014
+ const row: Model = new Model();
1015
+ row.projectId = data.projectId;
1016
+ row.resourceType = resourceType;
1017
+ row.resourceId = resource.id;
1018
+ row.resourceIdentifier = identifier;
1019
+ row.keyHash = Service.hashKey(agentKey);
1020
+ row.posture = posture as unknown as JSONObject;
1021
+ row.lastAliveAt = now;
1022
+ row.connectionStatus = "connected";
1023
+ row.lastRegisteredAt = now;
1024
+ if (agentVersion) {
1025
+ row.agentVersion = agentVersion;
1026
+ }
1027
+ if (data.ingestionKeyId) {
1028
+ row.registeredWithIngestionKeyId = data.ingestionKeyId;
1029
+ }
1030
+
1031
+ const created: Model = await this.createAgentRow({
1032
+ row,
1033
+ resourceType,
1034
+ identifier,
1035
+ });
1036
+
1037
+ agentId = created.id!;
1038
+ admission = "created";
1039
+ }
1040
+
1041
+ logger.info(
1042
+ `ResourceAiAgent: the ${info.agentDisplayName} of ${info.displayName} "${identifier}" (${resource.id.toString()}) in project ${data.projectId.toString()} registered (${admission}).`,
1043
+ );
1044
+
1045
+ /*
1046
+ * The new key's hash is stored: from here on nothing may fail the
1047
+ * registration, or the agent never receives a key the row now demands
1048
+ * (see applyFirstConnectionDefaultsSafely). The feed items are safe
1049
+ * already — the resource feed services never throw.
1050
+ */
1051
+ const defaultsApplied: ResourceAiAgentDefaultsApplied =
1052
+ await this.applyFirstConnectionDefaultsSafely({
1053
+ resource,
1054
+ allowWrites: posture.allowWrites === true,
1055
+ });
1056
+
1057
+ if (admission === "created") {
1058
+ await this.writeConnectedFeedItem({
1059
+ resource,
1060
+ agentId,
1061
+ posture,
1062
+ agentVersion,
1063
+ defaultsApplied,
1064
+ });
1065
+ } else if (Service.didApplyDefaults(defaultsApplied)) {
1066
+ await this.writeDefaultsFeedItem({ resource, posture, defaultsApplied });
1067
+ }
1068
+
1069
+ return {
1070
+ agentId,
1071
+ agentKey,
1072
+ resourceType,
1073
+ resourceId: resource.id,
1074
+ resourceName: resource.name || identifier,
1075
+ admission,
1076
+ };
1077
+ }
1078
+
1079
+ /*
1080
+ * How a registration for a resource that already has an agent row is
1081
+ * admitted, or null when it must be refused (the row's agent is online
1082
+ * and the request did not prove it is that agent). A reset row admits
1083
+ * anyone: no key is valid for it, and the admin reset is exactly the
1084
+ * "let the real agent back in" action.
1085
+ */
1086
+ public getReRegistrationAdmission(data: {
1087
+ agent: {
1088
+ keyHash?: string | null | undefined;
1089
+ } & ResourceAiAgentPresenceRow;
1090
+ previousAgentKey?: string | undefined;
1091
+ now?: Date | undefined;
1092
+ }): ResourceAiAgentRegistrationAdmission | null {
1093
+ if (!data.agent.keyHash) {
1094
+ return "reset";
1095
+ }
1096
+
1097
+ if (Service.doesKeyMatchHash(data.previousAgentKey, data.agent.keyHash)) {
1098
+ return "continuity";
1099
+ }
1100
+
1101
+ if (data.agent.connectionStatus !== "connected") {
1102
+ return "signed_off";
1103
+ }
1104
+
1105
+ if (!this.isOnline(data.agent, data.now)) {
1106
+ return "offline";
1107
+ }
1108
+
1109
+ return null;
1110
+ }
1111
+
1112
+ /*
1113
+ * The first-connection defaults, applied ONLY to a resource nobody
1114
+ * configured (aiAccessConfiguredAt is null — an operator's write of any
1115
+ * AI setting stamps it): investigating through the agent on, and, when
1116
+ * the agent allows writes (ONEUPTIME_AI_ALLOW_WRITES=true on the agent),
1117
+ * fixes "Ask for approval" instead of Off. Allowing writes on the agent is
1118
+ * the operator's consent for that; a human still approves every fix.
1119
+ * Never sets aiAccessConfiguredAt, so these stay defaults — and never
1120
+ * touches a mode an operator chose.
1121
+ *
1122
+ * Registration applies them every time; a heartbeat applies them when the
1123
+ * agent's write access appears (the agent was restarted with writes
1124
+ * allowed). The update is conditioned on the resource still being
1125
+ * unconfigured, so an operator's choice made a moment earlier is never
1126
+ * overwritten. It is a root write: the resource services neither gate nor
1127
+ * mark root writes of AI settings (they are the server's own).
1128
+ */
1129
+ @CaptureSpan()
1130
+ public async applyFirstConnectionDefaults(data: {
1131
+ resource: ResourceAiAgentResource;
1132
+ allowWrites: boolean;
1133
+ }): Promise<ResourceAiAgentDefaultsApplied> {
1134
+ const defaults: ResourceAiAgentDefaultsApplied =
1135
+ this.getFirstConnectionDefaults(data);
1136
+
1137
+ if (!Service.didApplyDefaults(defaults) || !data.resource.id) {
1138
+ return { turnedOnInvestigation: false };
1139
+ }
1140
+
1141
+ const updated: number = await Service.getResourceBinding(
1142
+ data.resource.resourceType,
1143
+ )
1144
+ .getService()
1145
+ .updateOneBy({
1146
+ query: {
1147
+ _id: data.resource.id.toString(),
1148
+ aiAccessConfiguredAt: QueryHelper.isNull(),
1149
+ ...(data.resource.projectId
1150
+ ? { projectId: data.resource.projectId }
1151
+ : {}),
1152
+ },
1153
+ data: {
1154
+ ...(defaults.turnedOnInvestigation
1155
+ ? { isAiInvestigationEnabled: true }
1156
+ : {}),
1157
+ ...(defaults.remediationMode
1158
+ ? { aiRemediationMode: defaults.remediationMode }
1159
+ : {}),
1160
+ },
1161
+ props: { isRoot: true },
1162
+ } as never);
1163
+
1164
+ if (updated === 0) {
1165
+ return { turnedOnInvestigation: false };
1166
+ }
1167
+
1168
+ logger.info(
1169
+ `ResourceAiAgent: applied the first-connection defaults to ${
1170
+ AI_RESOURCE_TYPE_INFO[data.resource.resourceType].displayName
1171
+ } ${data.resource.id.toString()} (${[
1172
+ defaults.turnedOnInvestigation ? "investigation on" : "",
1173
+ defaults.remediationMode
1174
+ ? `remediation ${defaults.remediationMode}`
1175
+ : "",
1176
+ ]
1177
+ .filter(Boolean)
1178
+ .join(", ")}).`,
1179
+ );
1180
+
1181
+ return defaults;
1182
+ }
1183
+
1184
+ /*
1185
+ * applyFirstConnectionDefaults for a request whose real work is already
1186
+ * written — registration stored the new key's hash and marked the row
1187
+ * connected, a heartbeat stored the liveness. A failure here must not
1188
+ * fail that request: a registration that answered 500 after storing the
1189
+ * hash would leave a row that reads online and holds a key nobody
1190
+ * received, so every retry would be refused as previous_instance_online
1191
+ * (and recorded as another agent's attempt) until the alive window
1192
+ * lapsed. Nothing is lost by skipping the defaults: the next
1193
+ * registration applies them again. Logged, and reported as "nothing
1194
+ * changed", so no feed item claims a change that did not happen.
1195
+ */
1196
+ private async applyFirstConnectionDefaultsSafely(data: {
1197
+ resource: ResourceAiAgentResource;
1198
+ allowWrites: boolean;
1199
+ }): Promise<ResourceAiAgentDefaultsApplied> {
1200
+ try {
1201
+ return await this.applyFirstConnectionDefaults(data);
1202
+ } catch (error) {
1203
+ logger.error(
1204
+ `ResourceAiAgent: could not apply the first-connection defaults to ${
1205
+ data.resource.resourceType
1206
+ } ${data.resource.id?.toString()}; the next registration tries again: ${error}`,
1207
+ );
1208
+ return { turnedOnInvestigation: false };
1209
+ }
1210
+ }
1211
+
1212
+ // What applyFirstConnectionDefaults would change, without writing it.
1213
+ public getFirstConnectionDefaults(data: {
1214
+ resource: Pick<
1215
+ ResourceAiAgentResource,
1216
+ "aiAccessConfiguredAt" | "isAiInvestigationEnabled" | "aiRemediationMode"
1217
+ >;
1218
+ allowWrites: boolean;
1219
+ }): ResourceAiAgentDefaultsApplied {
1220
+ if (data.resource.aiAccessConfiguredAt) {
1221
+ return { turnedOnInvestigation: false };
1222
+ }
1223
+
1224
+ const isRemediationOff: boolean =
1225
+ parseResourceAiRemediationMode(data.resource.aiRemediationMode) ===
1226
+ ResourceAiRemediationMode.Disabled;
1227
+
1228
+ return {
1229
+ turnedOnInvestigation: data.resource.isAiInvestigationEnabled !== true,
1230
+ ...(data.allowWrites && isRemediationOff
1231
+ ? { remediationMode: ResourceAiRemediationMode.RequireApproval }
1232
+ : {}),
1233
+ };
1234
+ }
1235
+
1236
+ public static didApplyDefaults(
1237
+ defaults: ResourceAiAgentDefaultsApplied,
1238
+ ): boolean {
1239
+ return defaults.turnedOnInvestigation || Boolean(defaults.remediationMode);
1240
+ }
1241
+
1242
+ /*
1243
+ * The posture a row stores: what the agent reported, validated field by
1244
+ * field (parseResourceAiAgentPosture: allowWrites only for a literal true,
1245
+ * malformed target lists force it off, strings capped), with the two
1246
+ * facts the server decides itself forced — WHICH kind of resource, and
1247
+ * WHICH identity (the one the agent registered with). A body can never
1248
+ * make the agent the executor of another resource.
1249
+ */
1250
+ public buildStoredPosture(data: {
1251
+ reported: unknown;
1252
+ resourceType: AiResourceType;
1253
+ resourceIdentifier: string;
1254
+ }): ResourceAiAgentPosture {
1255
+ const reported: Record<string, unknown> =
1256
+ data.reported &&
1257
+ typeof data.reported === "object" &&
1258
+ !Array.isArray(data.reported)
1259
+ ? (data.reported as Record<string, unknown>)
1260
+ : {};
1261
+
1262
+ const parsed: ResourceAiAgentPosture | null = parseResourceAiAgentPosture({
1263
+ ...reported,
1264
+ resourceType: data.resourceType,
1265
+ resourceIdentifier: data.resourceIdentifier,
1266
+ });
1267
+
1268
+ const posture: ResourceAiAgentPosture = parsed || {
1269
+ resourceType: data.resourceType,
1270
+ resourceIdentifier: data.resourceIdentifier.trim(),
1271
+ allowWrites: false,
1272
+ writeTargets: [],
1273
+ protectedTargets: [],
1274
+ reachable: false,
1275
+ };
1276
+
1277
+ // A JSON column: store only what is known, never `undefined` keys.
1278
+ for (const key of Object.keys(posture) as Array<
1279
+ keyof ResourceAiAgentPosture
1280
+ >) {
1281
+ if (posture[key] === undefined) {
1282
+ delete posture[key];
1283
+ }
1284
+ }
1285
+
1286
+ return posture;
1287
+ }
1288
+
1289
+ /*
1290
+ * The version string the agent reports, bounded to its ShortText column
1291
+ * (an over-long value would fail the whole write); undefined when absent.
1292
+ */
1293
+ public static normalizeAgentVersion(value: unknown): string | undefined {
1294
+ if (typeof value !== "string") {
1295
+ return undefined;
1296
+ }
1297
+
1298
+ const trimmed: string = value.trim();
1299
+
1300
+ return trimmed ? trimmed.substring(0, ColumnLength.ShortText) : undefined;
1301
+ }
1302
+
1303
+ // The longest identity registration accepts for a type.
1304
+ public static getMaxIdentifierLength(resourceType: AiResourceType): number {
1305
+ return resourceType === AiResourceType.DatabaseServer
1306
+ ? MAX_DATABASE_AI_AGENT_IDENTIFIER_LENGTH
1307
+ : MAX_RESOURCE_AI_AGENT_IDENTIFIER_LENGTH;
1308
+ }
1309
+
1310
+ private getValidResourceType(raw: unknown): AiResourceType {
1311
+ const resourceType: AiResourceType | null = parseAiResourceType(raw);
1312
+
1313
+ if (!resourceType) {
1314
+ const aliases: string = ALL_AI_RESOURCE_TYPES.map(
1315
+ (type: AiResourceType): string => {
1316
+ return AI_RESOURCE_TYPE_INFO[type].agentAlias;
1317
+ },
1318
+ ).join(", ");
1319
+
1320
+ throw new ResourceAiAgentRegistrationRefusedException({
1321
+ reason: "resource_type_invalid",
1322
+ message: `The agent did not say which kind of resource it serves${
1323
+ typeof raw === "string" && raw.trim()
1324
+ ? ` ("${raw.trim().substring(0, 64)}" is not one OneUptime knows)`
1325
+ : ""
1326
+ }. Set ${RESOURCE_AI_AGENT_RESOURCE_TYPE_ENV} on the agent to one of: ${aliases}.`,
1327
+ });
1328
+ }
1329
+
1330
+ return resourceType;
1331
+ }
1332
+
1333
+ private getValidResourceIdentifier(data: {
1334
+ resourceType: AiResourceType;
1335
+ raw: unknown;
1336
+ }): string {
1337
+ const info: AiResourceTypeInfo = AI_RESOURCE_TYPE_INFO[data.resourceType];
1338
+ const identifier: string =
1339
+ typeof data.raw === "string" ? data.raw.trim() : "";
1340
+ const variables: string = `${info.identityEnvVars.join(" / ")} (or ${RESOURCE_AI_AGENT_RESOURCE_NAME_ENV})`;
1341
+
1342
+ if (!identifier) {
1343
+ throw new ResourceAiAgentRegistrationRefusedException({
1344
+ reason: "resource_identifier_invalid",
1345
+ message: `The ${info.agentDisplayName} did not say which ${describeResourceInSentence(
1346
+ data.resourceType,
1347
+ )} it serves. Set ${variables} on the agent.`,
1348
+ });
1349
+ }
1350
+
1351
+ const limit: number = Service.getMaxIdentifierLength(data.resourceType);
1352
+
1353
+ if (identifier.length > limit) {
1354
+ throw new ResourceAiAgentRegistrationRefusedException({
1355
+ reason: "resource_identifier_invalid",
1356
+ message: `The ${describeResourceInSentence(
1357
+ data.resourceType,
1358
+ )} identity is ${identifier.length} characters long; the limit is ${limit}. Set a shorter ${variables} on the agent.`,
1359
+ });
1360
+ }
1361
+
1362
+ return identifier;
1363
+ }
1364
+
1365
+ /*
1366
+ * The resource the agent names, created when OneUptime has not seen it
1367
+ * yet (the agent may register before the collector's first telemetry),
1368
+ * then re-read for the AI columns registration needs.
1369
+ */
1370
+ private async resolveResource(data: {
1371
+ projectId: ObjectID;
1372
+ resourceType: AiResourceType;
1373
+ identifier: string;
1374
+ resourceId: unknown;
1375
+ posture: ResourceAiAgentPosture;
1376
+ }): Promise<ResourceAiAgentResource> {
1377
+ const info: AiResourceTypeInfo = AI_RESOURCE_TYPE_INFO[data.resourceType];
1378
+
1379
+ if (data.resourceType === AiResourceType.DatabaseServer) {
1380
+ const databaseServerId: ObjectID =
1381
+ await this.resolveDatabaseServerId(data);
1382
+
1383
+ const databaseServer: ResourceAiAgentResource | null =
1384
+ await this.findResource({
1385
+ projectId: data.projectId,
1386
+ resourceType: data.resourceType,
1387
+ resourceId: databaseServerId,
1388
+ });
1389
+
1390
+ if (!databaseServer) {
1391
+ throw new BadDataException(
1392
+ `The ${info.displayName} could not be resolved.`,
1393
+ );
1394
+ }
1395
+
1396
+ return databaseServer;
1397
+ }
1398
+
1399
+ const resourceId: ObjectID = await this.findOrCreateResourceId({
1400
+ ...data,
1401
+ fresh: false,
1402
+ });
1403
+
1404
+ let resource: ResourceAiAgentResource | null = await this.findResource({
1405
+ projectId: data.projectId,
1406
+ resourceType: data.resourceType,
1407
+ resourceId,
1408
+ });
1409
+
1410
+ /*
1411
+ * The id can come from an in-process memo (HostService keeps a host's
1412
+ * id for a minute, and a delete does not clear it) and name a resource
1413
+ * deleted since: an operator deleted it while its agent ran, and the
1414
+ * agent registers again within seconds. Resolve it once more past the
1415
+ * memo, which creates the resource anew, as a first registration does.
1416
+ */
1417
+ if (!resource) {
1418
+ const freshId: ObjectID = await this.findOrCreateResourceId({
1419
+ ...data,
1420
+ fresh: true,
1421
+ });
1422
+
1423
+ if (freshId.toString() !== resourceId.toString()) {
1424
+ resource = await this.findResource({
1425
+ projectId: data.projectId,
1426
+ resourceType: data.resourceType,
1427
+ resourceId: freshId,
1428
+ });
1429
+ }
1430
+ }
1431
+
1432
+ if (!resource) {
1433
+ throw new BadDataException(
1434
+ `The ${info.displayName} could not be resolved.`,
1435
+ );
1436
+ }
1437
+
1438
+ return resource;
1439
+ }
1440
+
1441
+ // The id of the row a named type's identity finds or creates.
1442
+ private async findOrCreateResourceId(data: {
1443
+ projectId: ObjectID;
1444
+ resourceType: AiResourceType;
1445
+ identifier: string;
1446
+ fresh: boolean;
1447
+ }): Promise<ObjectID> {
1448
+ const binding: ResourceAiAgentResourceBinding = Service.getResourceBinding(
1449
+ data.resourceType,
1450
+ );
1451
+
1452
+ const found: DatabaseBaseModel | null = binding.findOrCreateByIdentity
1453
+ ? await binding.findOrCreateByIdentity({
1454
+ projectId: data.projectId,
1455
+ identifier: data.identifier,
1456
+ ...(data.fresh ? { fresh: true } : {}),
1457
+ })
1458
+ : null;
1459
+
1460
+ if (!found || !found.id) {
1461
+ throw new BadDataException(
1462
+ `The ${
1463
+ AI_RESOURCE_TYPE_INFO[data.resourceType].displayName
1464
+ } could not be resolved.`,
1465
+ );
1466
+ }
1467
+
1468
+ return found.id;
1469
+ }
1470
+
1471
+ /*
1472
+ * The DatabaseServer a Database AI agent serves:
1473
+ *
1474
+ * 1. a pinned id (DATABASE_SERVER_ID, sent as resourceId — or an identity
1475
+ * that is itself a database id): that row, if it is in this project,
1476
+ * else resource_not_found. Never a fallback to the endpoint: the
1477
+ * operator pinned THAT database.
1478
+ * 2. otherwise the endpoint, read from "<system>|<address>[:<port>]"
1479
+ * (the identity the agent sends), else from the posture's details
1480
+ * (databaseSystem, serverAddress, serverPort), and canonicalized
1481
+ * exactly as the Database Agent collector's telemetry is (engine
1482
+ * family, default port, the same stability rules): the row that owns
1483
+ * it, or a new one when the endpoint may create one.
1484
+ */
1485
+ private async resolveDatabaseServerId(data: {
1486
+ projectId: ObjectID;
1487
+ identifier: string;
1488
+ resourceId: unknown;
1489
+ posture: ResourceAiAgentPosture;
1490
+ }): Promise<ObjectID> {
1491
+ const pinned: string | null = Service.readPinnedDatabaseServerId({
1492
+ resourceId: data.resourceId,
1493
+ identifier: data.identifier,
1494
+ });
1495
+
1496
+ if (pinned !== null) {
1497
+ const row: DatabaseServer | null = UUID_PATTERN.test(pinned)
1498
+ ? await DatabaseServerService.findByIdInProject(
1499
+ data.projectId,
1500
+ new ObjectID(pinned),
1501
+ )
1502
+ : null;
1503
+
1504
+ if (!row || !row.id) {
1505
+ throw new ResourceAiAgentRegistrationRefusedException({
1506
+ reason: "resource_not_found",
1507
+ message: `DATABASE_SERVER_ID "${pinned.substring(
1508
+ 0,
1509
+ 64,
1510
+ )}" is not a database in this project. Copy the id from the database's page in OneUptime, or unset DATABASE_SERVER_ID to register the database by its endpoint.`,
1511
+ });
1512
+ }
1513
+
1514
+ return row.id;
1515
+ }
1516
+
1517
+ const endpointIdentity: DatabaseEndpointIdentity | null =
1518
+ Service.parseDatabaseEndpointIdentity(data.identifier) ||
1519
+ Service.readDatabaseEndpointFromPosture(data.posture);
1520
+
1521
+ if (!endpointIdentity) {
1522
+ throw new ResourceAiAgentRegistrationRefusedException({
1523
+ reason: "resource_identifier_invalid",
1524
+ message: `The Database AI agent's identity "${data.identifier.substring(
1525
+ 0,
1526
+ 64,
1527
+ )}" names neither a database id nor an endpoint. Set DATABASE_SERVER_ID to the database's id in OneUptime, or DATABASE_SYSTEM, DATABASE_SERVER_ADDRESS and DATABASE_SERVER_PORT to its endpoint.`,
1528
+ });
1529
+ }
1530
+
1531
+ const attributes: Record<string, unknown> = {
1532
+ "db.system.name": endpointIdentity.system,
1533
+ "server.address": endpointIdentity.address,
1534
+ [DATABASE_AGENT_ATTRIBUTE]: "true",
1535
+ };
1536
+
1537
+ if (endpointIdentity.port !== null) {
1538
+ attributes["server.port"] = String(endpointIdentity.port);
1539
+ }
1540
+
1541
+ const resolved: ReturnType<typeof resolveDatabaseFromResourceAttributes> =
1542
+ resolveDatabaseFromResourceAttributes({ attributes });
1543
+
1544
+ const endpoint: DatabaseEndpoint | null = resolved?.endpoint || null;
1545
+
1546
+ if (!resolved || !endpoint) {
1547
+ throw new ResourceAiAgentRegistrationRefusedException({
1548
+ reason: "resource_not_found",
1549
+ message: `"${endpointIdentity.address.substring(
1550
+ 0,
1551
+ 128,
1552
+ )}" is not an address OneUptime can identify a database server by (a loopback or unreadable address). Set DATABASE_SERVER_ADDRESS to the host name your applications use for this database, or DATABASE_SERVER_ID to its id in OneUptime.`,
1553
+ });
1554
+ }
1555
+
1556
+ const row: DatabaseServer | null =
1557
+ await DatabaseServerService.findOrCreateByEndpoint({
1558
+ projectId: data.projectId,
1559
+ dbSystem: resolved.system,
1560
+ endpoint,
1561
+ discoverySource: DatabaseServerDiscoverySource.Collector,
1562
+ displayName: resolved.displayName || undefined,
1563
+ allowCreate: resolved.allowCreate,
1564
+ /*
1565
+ * An agent registering (every restart, and every refused retry of
1566
+ * a duplicate) is no sign the database is in use: it must not
1567
+ * restore a database discovery archived for going quiet, nor move
1568
+ * its endpoint's last match or weigh its engine.
1569
+ */
1570
+ isSighting: false,
1571
+ });
1572
+
1573
+ if (!row || !row.id) {
1574
+ throw new ResourceAiAgentRegistrationRefusedException({
1575
+ reason: "resource_not_found",
1576
+ message: `No database in this project owns ${formatDatabaseEndpoint(
1577
+ endpoint,
1578
+ )}, and OneUptime could not add one for it (a private or single-label address, an engine it does not add automatically, or the project's automatic-add budget). Add the database in OneUptime (or wait for its collector to report it), then set DATABASE_SERVER_ID to its id.`,
1579
+ });
1580
+ }
1581
+
1582
+ return row.id;
1583
+ }
1584
+
1585
+ /*
1586
+ * The database id an agent pinned: its resourceId, else an identity that
1587
+ * is itself an id (ONEUPTIME_AI_AGENT_RESOURCE_NAME set to one). Null
1588
+ * when neither pins a row; a pinned value that is not a UUID is returned
1589
+ * as is, so the caller refuses it rather than silently using the
1590
+ * endpoint.
1591
+ */
1592
+ public static readPinnedDatabaseServerId(data: {
1593
+ resourceId: unknown;
1594
+ identifier: string;
1595
+ }): string | null {
1596
+ if (typeof data.resourceId === "string" && data.resourceId.trim()) {
1597
+ return data.resourceId.trim().toLowerCase();
1598
+ }
1599
+
1600
+ if (UUID_PATTERN.test(data.identifier.trim())) {
1601
+ return data.identifier.trim().toLowerCase();
1602
+ }
1603
+
1604
+ return null;
1605
+ }
1606
+
1607
+ /*
1608
+ * "<system>|<address>[:<port>]" (the agent's endpoint identity, the form
1609
+ * DatabaseServer.databaseIdentifier takes) as its parts; the address
1610
+ * keeps its port (parsed with the rest when it is canonicalized). Null
1611
+ * when the value has no system or no address.
1612
+ */
1613
+ public static parseDatabaseEndpointIdentity(
1614
+ identifier: string,
1615
+ ): DatabaseEndpointIdentity | null {
1616
+ const separator: number = identifier.indexOf("|");
1617
+
1618
+ if (separator <= 0) {
1619
+ return null;
1620
+ }
1621
+
1622
+ const system: string = identifier.substring(0, separator).trim();
1623
+ const address: string = identifier.substring(separator + 1).trim();
1624
+
1625
+ if (!system || !address || address.includes("|")) {
1626
+ return null;
1627
+ }
1628
+
1629
+ return { system: system.toLowerCase(), address, port: null };
1630
+ }
1631
+
1632
+ // The endpoint the agent's posture details describe, or null.
1633
+ public static readDatabaseEndpointFromPosture(
1634
+ posture: ResourceAiAgentPosture | null | undefined,
1635
+ ): DatabaseEndpointIdentity | null {
1636
+ const details: Record<string, string | number | boolean | null> =
1637
+ posture?.details || {};
1638
+
1639
+ const system: unknown = details["databaseSystem"];
1640
+ const address: unknown = details["serverAddress"];
1641
+ const port: unknown = details["serverPort"];
1642
+
1643
+ if (
1644
+ typeof system !== "string" ||
1645
+ !system.trim() ||
1646
+ typeof address !== "string" ||
1647
+ !address.trim()
1648
+ ) {
1649
+ return null;
1650
+ }
1651
+
1652
+ return {
1653
+ system: system.trim().toLowerCase(),
1654
+ address: address.trim(),
1655
+ port:
1656
+ typeof port === "number" &&
1657
+ Number.isInteger(port) &&
1658
+ port >= 1 &&
1659
+ port <= 65535
1660
+ ? port
1661
+ : null,
1662
+ };
1663
+ }
1664
+
1665
+ /*
1666
+ * An existing row: admitted per getReRegistrationAdmission, or refused
1667
+ * with previous_instance_online — which is recorded on the row (when, and
1668
+ * why) so the AI agent page can warn that another agent tried to register
1669
+ * while this one was online. Nothing about the current key is disclosed.
1670
+ */
1671
+ private async admitReRegistration(data: {
1672
+ agent: Model;
1673
+ previousAgentKey?: string | undefined;
1674
+ resourceType: AiResourceType;
1675
+ identifier: string;
1676
+ projectId: ObjectID;
1677
+ }): Promise<ResourceAiAgentRegistrationAdmission> {
1678
+ const admission: ResourceAiAgentRegistrationAdmission | null =
1679
+ this.getReRegistrationAdmission({
1680
+ agent: data.agent,
1681
+ previousAgentKey: data.previousAgentKey,
1682
+ });
1683
+
1684
+ if (admission) {
1685
+ return admission;
1686
+ }
1687
+
1688
+ logger.warn(
1689
+ `ResourceAiAgent: refused a registration for ${
1690
+ AI_RESOURCE_TYPE_INFO[data.resourceType].displayName
1691
+ } "${data.identifier}" in project ${data.projectId.toString()}: its agent (${data.agent.id?.toString()}) is online and the request did not present its current key.`,
1692
+ );
1693
+
1694
+ await this.recordRefusedRegistration({
1695
+ agentId: data.agent.id!,
1696
+ reason: "previous_instance_online",
1697
+ });
1698
+
1699
+ throw this.getPreviousInstanceOnlineRefusal({
1700
+ resourceType: data.resourceType,
1701
+ identifier: data.identifier,
1702
+ });
1703
+ }
1704
+
1705
+ private getPreviousInstanceOnlineRefusal(data: {
1706
+ resourceType: AiResourceType;
1707
+ identifier: string;
1708
+ }): ResourceAiAgentRegistrationRefusedException {
1709
+ const info: AiResourceTypeInfo = AI_RESOURCE_TYPE_INFO[data.resourceType];
1710
+
1711
+ return new ResourceAiAgentRegistrationRefusedException({
1712
+ reason: "previous_instance_online",
1713
+ retryAfterSeconds: RESOURCE_AI_AGENT_REGISTRATION_RETRY_AFTER_SECONDS,
1714
+ message: `Another ${info.agentDisplayName} for ${describeResourceInSentence(
1715
+ data.resourceType,
1716
+ )} "${data.identifier}" is online. This is normal for a moment while its container restarts, and the agent retries on its own. If it lasts, make sure only one ${info.agentDisplayName} is installed for "${data.identifier}".`,
1717
+ });
1718
+ }
1719
+
1720
+ // Best-effort: recording a refusal must never turn it into a 500.
1721
+ private async recordRefusedRegistration(data: {
1722
+ agentId: ObjectID;
1723
+ reason: ResourceAiAgentRegistrationRefusalReason;
1724
+ }): Promise<void> {
1725
+ try {
1726
+ await this.updateColumnsByIdWithoutHooks({
1727
+ id: data.agentId,
1728
+ data: {
1729
+ lastRefusedRegistrationAt: OneUptimeDate.getCurrentDate(),
1730
+ lastRefusedRegistrationReason: data.reason,
1731
+ },
1732
+ });
1733
+ } catch (error) {
1734
+ logger.error(
1735
+ `ResourceAiAgent: could not record a refused registration on agent ${data.agentId.toString()}: ${error}`,
1736
+ );
1737
+ }
1738
+ }
1739
+
1740
+ /*
1741
+ * Bounds what an ingestion key can create (see the caps above). One
1742
+ * table serves every resource type, so the caps count every type's
1743
+ * agents together. At the cap, agents not heard from in
1744
+ * RESOURCE_AI_AGENT_RECLAIM_AFTER_DAYS make room first.
1745
+ */
1746
+ private async assertAgentMayBeCreated(data: {
1747
+ projectId: ObjectID;
1748
+ resourceType: AiResourceType;
1749
+ identifier: string;
1750
+ }): Promise<void> {
1751
+ const displayName: string =
1752
+ AI_RESOURCE_TYPE_INFO[data.resourceType].displayName;
1753
+
1754
+ let total: number = await this.countAgentsInProject(data.projectId);
1755
+
1756
+ if (
1757
+ total >= MAX_RESOURCE_AI_AGENTS_PER_PROJECT &&
1758
+ (await this.reclaimLongOfflineAgents(data.projectId)) > 0
1759
+ ) {
1760
+ total = await this.countAgentsInProject(data.projectId);
1761
+ }
1762
+
1763
+ if (total >= MAX_RESOURCE_AI_AGENTS_PER_PROJECT) {
1764
+ logger.warn(
1765
+ `ResourceAiAgent: refused to create an agent for ${displayName} "${data.identifier}" in project ${data.projectId.toString()}: the project already has ${total} (limit ${MAX_RESOURCE_AI_AGENTS_PER_PROJECT}).`,
1766
+ );
1767
+
1768
+ throw new ResourceAiAgentRegistrationRefusedException({
1769
+ reason: "agent_cap_reached",
1770
+ message: `This project already has ${total} resource AI agents, which is its limit (${MAX_RESOURCE_AI_AGENTS_PER_PROJECT}). An agent not heard from in ${RESOURCE_AI_AGENT_RECLAIM_AFTER_DAYS} days makes room for a new one; to make room now, delete the resources you no longer use in OneUptime. The agent connects on its next retry.`,
1771
+ });
1772
+ }
1773
+
1774
+ const createdInLastHour: number = (
1775
+ await this.countBy({
1776
+ query: {
1777
+ projectId: data.projectId,
1778
+ createdAt: QueryHelper.greaterThan(OneUptimeDate.getSomeHoursAgo(1)),
1779
+ },
1780
+ props: { isRoot: true },
1781
+ })
1782
+ ).toNumber();
1783
+
1784
+ if (createdInLastHour >= MAX_NEW_RESOURCE_AI_AGENTS_PER_PROJECT_PER_HOUR) {
1785
+ logger.warn(
1786
+ `ResourceAiAgent: refused to create an agent for ${displayName} "${data.identifier}" in project ${data.projectId.toString()}: ${createdInLastHour} were created in the last hour (limit ${MAX_NEW_RESOURCE_AI_AGENTS_PER_PROJECT_PER_HOUR}).`,
1787
+ );
1788
+
1789
+ throw new ResourceAiAgentRegistrationRefusedException({
1790
+ reason: "agent_cap_reached",
1791
+ message: `${createdInLastHour} resource AI agents connected to this project in the last hour, which is its limit (${MAX_NEW_RESOURCE_AI_AGENTS_PER_PROJECT_PER_HOUR}). The agent connects on a later retry.`,
1792
+ });
1793
+ }
1794
+ }
1795
+
1796
+ private async countAgentsInProject(projectId: ObjectID): Promise<number> {
1797
+ return (
1798
+ await this.countBy({
1799
+ query: { projectId },
1800
+ props: { isRoot: true },
1801
+ })
1802
+ ).toNumber();
1803
+ }
1804
+
1805
+ /*
1806
+ * Removes the project's agent rows not heard from in
1807
+ * RESOURCE_AI_AGENT_RECLAIM_AFTER_DAYS; how many. Best-effort: a failure
1808
+ * leaves the cap to refuse as before.
1809
+ */
1810
+ private async reclaimLongOfflineAgents(projectId: ObjectID): Promise<number> {
1811
+ try {
1812
+ const reclaimed: number = await this.deleteBy({
1813
+ query: {
1814
+ projectId,
1815
+ lastAliveAt: QueryHelper.lessThanOrNull(
1816
+ OneUptimeDate.getSomeDaysAgo(RESOURCE_AI_AGENT_RECLAIM_AFTER_DAYS),
1817
+ ),
1818
+ },
1819
+ limit: LIMIT_MAX,
1820
+ skip: 0,
1821
+ props: { isRoot: true },
1822
+ });
1823
+
1824
+ if (reclaimed > 0) {
1825
+ logger.info(
1826
+ `ResourceAiAgent: removed ${reclaimed} agent(s) of project ${projectId.toString()} not heard from in ${RESOURCE_AI_AGENT_RECLAIM_AFTER_DAYS} days, to make room for a new one.`,
1827
+ );
1828
+ }
1829
+
1830
+ return reclaimed;
1831
+ } catch (error) {
1832
+ logger.error(
1833
+ `ResourceAiAgent: could not remove the long-offline agents of project ${projectId.toString()}: ${error}`,
1834
+ );
1835
+ return 0;
1836
+ }
1837
+ }
1838
+
1839
+ /*
1840
+ * Creates the resource's agent row. The database allows one per live
1841
+ * resource, so when two agents register for a new resource at once one
1842
+ * create loses; that agent is told the other agent is online (it is — it
1843
+ * just registered), exactly as if it had arrived a moment later.
1844
+ */
1845
+ private async createAgentRow(data: {
1846
+ row: Model;
1847
+ resourceType: AiResourceType;
1848
+ identifier: string;
1849
+ }): Promise<Model> {
1850
+ try {
1851
+ return await this.create({
1852
+ data: data.row,
1853
+ props: { isRoot: true },
1854
+ });
1855
+ } catch (error) {
1856
+ const winner: Model | null = await this.findOneBy({
1857
+ query: {
1858
+ projectId: data.row.projectId!,
1859
+ resourceType: data.resourceType,
1860
+ resourceId: data.row.resourceId!,
1861
+ },
1862
+ select: { _id: true },
1863
+ props: { isRoot: true },
1864
+ });
1865
+
1866
+ if (!winner) {
1867
+ throw error;
1868
+ }
1869
+
1870
+ logger.info(
1871
+ `ResourceAiAgent: two agents registered for ${
1872
+ AI_RESOURCE_TYPE_INFO[data.resourceType].displayName
1873
+ } "${data.identifier}" at once; the other one was admitted.`,
1874
+ );
1875
+
1876
+ throw this.getPreviousInstanceOnlineRefusal({
1877
+ resourceType: data.resourceType,
1878
+ identifier: data.identifier,
1879
+ });
1880
+ }
1881
+ }
1882
+
1883
+ /*
1884
+ * ------------------------------------------------------------------
1885
+ * The registered agent
1886
+ * ------------------------------------------------------------------
1887
+ */
1888
+
1889
+ /*
1890
+ * The agent a request's id and key belong to, or null. Every request
1891
+ * after registration is authenticated here: the key's sha256 must match
1892
+ * the row's hash (timing-safe). A reset row (null hash) matches nothing,
1893
+ * and an unknown id costs the same comparison as a wrong key. The row is
1894
+ * returned without its key hash.
1895
+ */
1896
+ @CaptureSpan()
1897
+ public async authenticate(data: {
1898
+ agentId: ObjectID | string;
1899
+ agentKey: string;
1900
+ }): Promise<Model | null> {
1901
+ const agentId: string =
1902
+ typeof data.agentId === "string"
1903
+ ? data.agentId
1904
+ : data.agentId?.toString() || "";
1905
+
1906
+ if (
1907
+ !agentId ||
1908
+ !ObjectID.isValidUUID(agentId) ||
1909
+ typeof data.agentKey !== "string" ||
1910
+ !data.agentKey
1911
+ ) {
1912
+ return null;
1913
+ }
1914
+
1915
+ const agent: Model | null = await this.findOneBy({
1916
+ query: { _id: agentId },
1917
+ select: { ...RESOURCE_AI_AGENT_SELECT_WITHOUT_KEY, keyHash: true },
1918
+ props: { isRoot: true },
1919
+ });
1920
+
1921
+ const matches: boolean = Service.doesKeyMatchHash(
1922
+ data.agentKey,
1923
+ agent?.keyHash || UNMATCHABLE_KEY_HASH,
1924
+ );
1925
+
1926
+ if (!agent || !matches) {
1927
+ return null;
1928
+ }
1929
+
1930
+ delete agent.keyHash;
1931
+
1932
+ return agent;
1933
+ }
1934
+
1935
+ /*
1936
+ * A heartbeat from the authenticated agent: it is alive and connected,
1937
+ * and this is what it can do now. The posture is stored the way
1938
+ * registration stores it (validated, with the resource type and the
1939
+ * identity the agent registered with forced); a heartbeat without one
1940
+ * keeps the stored posture.
1941
+ *
1942
+ * When the agent's write access appears (it was restarted with
1943
+ * ONEUPTIME_AI_ALLOW_WRITES=true) on a resource nobody configured, the
1944
+ * first-connection defaults apply again, so fixes move from Off to "Ask
1945
+ * for approval" without anyone visiting the AI agent page.
1946
+ *
1947
+ * This runs every 30 seconds per resource, so a normal heartbeat is one
1948
+ * UPDATE without hooks and no read: the agent row holds the identity it
1949
+ * registered with. The resource row is read only every
1950
+ * RESOURCE_AI_AGENT_RESOURCE_CHECK_INTERVAL_MS (is it still there?) and
1951
+ * when write access just appeared. An agent whose resource is gone is
1952
+ * retired: its row is deleted and the heartbeat answered 401, so the
1953
+ * agent registers again and is attached to whatever row its identity
1954
+ * resolves to now.
1955
+ */
1956
+ @CaptureSpan()
1957
+ public async heartbeat(data: {
1958
+ // The row authenticate returned for this request.
1959
+ agent: Model;
1960
+ agentVersion?: string | undefined;
1961
+ posture?: unknown;
1962
+ now?: Date | undefined;
1963
+ }): Promise<void> {
1964
+ const agentId: ObjectID | null = data.agent.id;
1965
+
1966
+ if (!agentId) {
1967
+ throw new BadDataException("The agent is missing its id.");
1968
+ }
1969
+
1970
+ const now: Date = data.now || OneUptimeDate.getCurrentDate();
1971
+
1972
+ let resource: ResourceAiAgentResource | null | undefined = undefined;
1973
+
1974
+ if (this.isResourceCheckDue(agentId.toString(), now)) {
1975
+ resource = await this.findAgentResourceSafely(data.agent);
1976
+
1977
+ if (resource === null) {
1978
+ await this.retireAgentOfGoneResource(data.agent);
1979
+
1980
+ throw new NotAuthenticatedException(
1981
+ `This agent's ${
1982
+ isAiResourceType(data.agent.resourceType)
1983
+ ? describeResourceInSentence(data.agent.resourceType)
1984
+ : "resource"
1985
+ } no longer exists in OneUptime. Register again.`,
1986
+ );
1987
+ }
1988
+ }
1989
+
1990
+ const storedPosture: ResourceAiAgentPosture | null =
1991
+ parseResourceAiAgentPosture(data.agent.posture);
1992
+
1993
+ const isPostureReported: boolean = Boolean(
1994
+ data.posture &&
1995
+ typeof data.posture === "object" &&
1996
+ !Array.isArray(data.posture),
1997
+ );
1998
+
1999
+ const resourceType: AiResourceType | null = isAiResourceType(
2000
+ data.agent.resourceType,
2001
+ )
2002
+ ? data.agent.resourceType
2003
+ : null;
2004
+
2005
+ const identifier: string =
2006
+ data.agent.resourceIdentifier?.trim() ||
2007
+ storedPosture?.resourceIdentifier?.trim() ||
2008
+ "";
2009
+
2010
+ /*
2011
+ * The identity is the registered one, never the body's; a row that
2012
+ * somehow lacks it keeps its stored posture rather than trusting one.
2013
+ */
2014
+ const posture: ResourceAiAgentPosture | undefined =
2015
+ isPostureReported && resourceType && identifier
2016
+ ? this.buildStoredPosture({
2017
+ reported: data.posture,
2018
+ resourceType,
2019
+ resourceIdentifier: identifier,
2020
+ })
2021
+ : undefined;
2022
+
2023
+ const agentVersion: string | undefined = Service.normalizeAgentVersion(
2024
+ data.agentVersion,
2025
+ );
2026
+
2027
+ const update: JSONObject = {
2028
+ lastAliveAt: now,
2029
+ connectionStatus: "connected",
2030
+ };
2031
+
2032
+ if (posture) {
2033
+ update["posture"] = posture as unknown as JSONObject;
2034
+ }
2035
+
2036
+ if (agentVersion) {
2037
+ update["agentVersion"] = agentVersion;
2038
+ }
2039
+
2040
+ await this.updateColumnsByIdWithoutHooks({
2041
+ id: agentId,
2042
+ data: update as never,
2043
+ });
2044
+
2045
+ const writesAppeared: boolean =
2046
+ posture?.allowWrites === true && storedPosture?.allowWrites !== true;
2047
+
2048
+ if (!writesAppeared || !posture) {
2049
+ return;
2050
+ }
2051
+
2052
+ // The defaults read the resource's current AI settings.
2053
+ resource = resource || (await this.findAgentResource(data.agent));
2054
+
2055
+ if (!resource) {
2056
+ return;
2057
+ }
2058
+
2059
+ // The liveness is written; a failure here must not fail the heartbeat.
2060
+ const defaultsApplied: ResourceAiAgentDefaultsApplied =
2061
+ await this.applyFirstConnectionDefaultsSafely({
2062
+ resource,
2063
+ allowWrites: true,
2064
+ });
2065
+
2066
+ if (Service.didApplyDefaults(defaultsApplied)) {
2067
+ await this.writeDefaultsFeedItem({ resource, posture, defaultsApplied });
2068
+ }
2069
+ }
2070
+
2071
+ // The agent's own resource (in its project, as root), or null.
2072
+ public async findAgentResource(
2073
+ agent: Pick<Model, "resourceType" | "resourceId" | "projectId">,
2074
+ ): Promise<ResourceAiAgentResource | null> {
2075
+ if (
2076
+ !isAiResourceType(agent.resourceType) ||
2077
+ !agent.resourceId ||
2078
+ !agent.projectId
2079
+ ) {
2080
+ return null;
2081
+ }
2082
+
2083
+ return await this.findResource({
2084
+ projectId: agent.projectId,
2085
+ resourceType: agent.resourceType,
2086
+ resourceId: agent.resourceId,
2087
+ });
2088
+ }
2089
+
2090
+ /*
2091
+ * findAgentResource for the heartbeat's periodic check: a failed read is
2092
+ * "not known" (undefined), never "gone" — only a read that found nothing
2093
+ * retires an agent.
2094
+ */
2095
+ private async findAgentResourceSafely(
2096
+ agent: Model,
2097
+ ): Promise<ResourceAiAgentResource | null | undefined> {
2098
+ try {
2099
+ return await this.findAgentResource(agent);
2100
+ } catch (error) {
2101
+ logger.error(
2102
+ `ResourceAiAgent: could not check the resource of agent ${agent.id?.toString()}; the next check tries again: ${error}`,
2103
+ );
2104
+ return undefined;
2105
+ }
2106
+ }
2107
+
2108
+ /*
2109
+ * The resource of this agent is gone: the agent row goes too (the agent
2110
+ * registers again and gets a row for whatever its identity resolves to
2111
+ * now). Jobs still targeted at the row lose their target (the foreign key
2112
+ * sets it null) and time out unclaimed. Best-effort: a failed delete is
2113
+ * logged, and the next check tries again.
2114
+ */
2115
+ private async retireAgentOfGoneResource(agent: Model): Promise<void> {
2116
+ logger.info(
2117
+ `ResourceAiAgent: agent ${agent.id?.toString()} serves ${String(
2118
+ agent.resourceType,
2119
+ )} ${agent.resourceId?.toString()}, which no longer exists; removing the agent row so the agent registers again.`,
2120
+ );
2121
+
2122
+ try {
2123
+ await this.deleteOneBy({
2124
+ query: {
2125
+ _id: agent.id!.toString(),
2126
+ ...(agent.projectId ? { projectId: agent.projectId } : {}),
2127
+ },
2128
+ props: { isRoot: true },
2129
+ });
2130
+ } catch (error) {
2131
+ logger.error(
2132
+ `ResourceAiAgent: could not remove agent ${agent.id?.toString()} of a deleted resource: ${error}`,
2133
+ );
2134
+ }
2135
+ }
2136
+
2137
+ /*
2138
+ * At most one resource check per interval per agent in this process.
2139
+ * Stamped before the check runs, so heartbeats that overlap it do not
2140
+ * start a second one.
2141
+ */
2142
+ private isResourceCheckDue(agentId: string, now: Date): boolean {
2143
+ const lastCheckedAt: number | undefined =
2144
+ this.resourceCheckedAt.get(agentId);
2145
+
2146
+ if (
2147
+ lastCheckedAt !== undefined &&
2148
+ now.getTime() - lastCheckedAt <
2149
+ RESOURCE_AI_AGENT_RESOURCE_CHECK_INTERVAL_MS
2150
+ ) {
2151
+ return false;
2152
+ }
2153
+
2154
+ // Bounded: a long-lived process forgets old agents rather than growing.
2155
+ if (this.resourceCheckedAt.size >= MAX_RESOURCE_CHECKS_REMEMBERED) {
2156
+ this.resourceCheckedAt.clear();
2157
+ }
2158
+
2159
+ this.resourceCheckedAt.set(agentId, now.getTime());
2160
+
2161
+ return true;
2162
+ }
2163
+
2164
+ /*
2165
+ * The agent signing off on a clean shutdown. lastAliveAt is left alone
2166
+ * (it is when the agent was last heard from); the disconnected status is
2167
+ * what lets its replacement container register at once instead of
2168
+ * waiting for the alive window to lapse, and what makes the AI agent page
2169
+ * say "Offline" straight away. Its next heartbeat or registration flips
2170
+ * it back.
2171
+ */
2172
+ @CaptureSpan()
2173
+ public async markDisconnected(data: {
2174
+ resourceAiAgentId: ObjectID;
2175
+ }): Promise<void> {
2176
+ if (!data.resourceAiAgentId) {
2177
+ throw new BadDataException("resourceAiAgentId is required");
2178
+ }
2179
+
2180
+ await this.updateColumnsByIdWithoutHooks({
2181
+ id: data.resourceAiAgentId,
2182
+ data: { connectionStatus: "disconnected" },
2183
+ });
2184
+ }
2185
+
2186
+ /*
2187
+ * An admin's "Reset agent" on the resource's AI agent page: the agent's
2188
+ * key stops working at once (keyHash null matches nothing) and the row
2189
+ * reads as disconnected. The real agent fails authentication on its next
2190
+ * request, registers again and — the row being reset — is admitted, so it
2191
+ * comes back by itself within a few minutes. Whoever else held the key
2192
+ * (an agent that registered with a leaked ingestion key) is locked out
2193
+ * the same way; rotating that ingestion key keeps it out. The caller
2194
+ * checks the user may do this (see the resource AI access API).
2195
+ */
2196
+ @CaptureSpan()
2197
+ public async resetAgent(data: {
2198
+ projectId: ObjectID;
2199
+ resourceType: AiResourceType;
2200
+ resourceId: ObjectID;
2201
+ userId?: ObjectID | undefined;
2202
+ }): Promise<void> {
2203
+ const info: AiResourceTypeInfo | undefined = isAiResourceType(
2204
+ data.resourceType,
2205
+ )
2206
+ ? AI_RESOURCE_TYPE_INFO[data.resourceType]
2207
+ : undefined;
2208
+
2209
+ if (!info) {
2210
+ throw new BadDataException("This is not a resource type with AI agents.");
2211
+ }
2212
+
2213
+ const agent: Model | null = await this.findAgentForResource({
2214
+ projectId: data.projectId,
2215
+ resourceType: data.resourceType,
2216
+ resourceId: data.resourceId,
2217
+ });
2218
+
2219
+ if (!agent || !agent.id) {
2220
+ throw new BadDataException(
2221
+ `This ${describeResourceInSentence(data.resourceType)} has no ${
2222
+ info.agentDisplayName
2223
+ } to reset.`,
2224
+ );
2225
+ }
2226
+
2227
+ await this.updateOneById({
2228
+ id: agent.id,
2229
+ data: {
2230
+ keyHash: null,
2231
+ connectionStatus: "disconnected",
2232
+ } as never,
2233
+ props: { isRoot: true },
2234
+ });
2235
+
2236
+ logger.info(
2237
+ `ResourceAiAgent: the ${info.agentDisplayName} of ${info.displayName} ${data.resourceId.toString()} in project ${data.projectId.toString()} was reset${
2238
+ data.userId ? ` by user ${data.userId.toString()}` : ""
2239
+ }.`,
2240
+ );
2241
+
2242
+ const resetBy: string = data.userId
2243
+ ? await this.getUserMarkdownForFeed({
2244
+ userId: data.userId,
2245
+ projectId: data.projectId,
2246
+ })
2247
+ : "";
2248
+
2249
+ await Service.getResourceBinding(data.resourceType).writeFeedItem({
2250
+ resourceId: data.resourceId,
2251
+ projectId: data.projectId,
2252
+ displayColor: Blue500,
2253
+ feedInfoInMarkdown: `🔄 The ${info.agentDisplayName} was reset${
2254
+ resetBy ? ` by **${resetBy}**` : ""
2255
+ }. It reconnects on its own within a few minutes.`,
2256
+ moreInformationInMarkdown: `**Agent**: ${info.agentDisplayName} (${agent.id.toString()})`,
2257
+ ...(data.userId ? { userId: data.userId } : {}),
2258
+ });
2259
+ }
2260
+
2261
+ /*
2262
+ * The agent rows of a resource, removed — for a resource's delete path:
2263
+ * ResourceAiAgent.resourceId has no foreign key, so nothing else removes
2264
+ * them. Jobs still targeted at them lose their target (SET NULL). Returns
2265
+ * how many rows went.
2266
+ */
2267
+ @CaptureSpan()
2268
+ public async deleteAgentsForResources(data: {
2269
+ resourceType: AiResourceType;
2270
+ resourceIds: Array<ObjectID>;
2271
+ projectId?: ObjectID | undefined;
2272
+ }): Promise<number> {
2273
+ if (!isAiResourceType(data.resourceType) || data.resourceIds.length === 0) {
2274
+ return 0;
2275
+ }
2276
+
2277
+ return await this.deleteBy({
2278
+ query: {
2279
+ resourceType: data.resourceType,
2280
+ resourceId: QueryHelper.any(
2281
+ data.resourceIds.map((id: ObjectID): string => {
2282
+ return id.toString();
2283
+ }),
2284
+ ),
2285
+ ...(data.projectId ? { projectId: data.projectId } : {}),
2286
+ },
2287
+ limit: LIMIT_MAX,
2288
+ skip: 0,
2289
+ props: { isRoot: true },
2290
+ });
2291
+ }
2292
+
2293
+ /*
2294
+ * The user's name as a feed link, or "" when it cannot be had (no such
2295
+ * user, or the lookup failed). Only ever wording: the action it describes
2296
+ * is already done, so a failed lookup must not fail it.
2297
+ */
2298
+ private async getUserMarkdownForFeed(data: {
2299
+ userId: ObjectID;
2300
+ projectId: ObjectID;
2301
+ }): Promise<string> {
2302
+ try {
2303
+ return await UserService.getUserMarkdownString(data);
2304
+ } catch (error) {
2305
+ logger.error(
2306
+ `ResourceAiAgent: could not look up user ${data.userId.toString()} for a feed item: ${error}`,
2307
+ );
2308
+ return "";
2309
+ }
2310
+ }
2311
+
2312
+ /*
2313
+ * ------------------------------------------------------------------
2314
+ * Feed
2315
+ * ------------------------------------------------------------------
2316
+ */
2317
+
2318
+ // What the agent may change, in the feed's words.
2319
+ public static describeWriteAccess(
2320
+ posture: Pick<ResourceAiAgentPosture, "allowWrites" | "writeTargets">,
2321
+ ): string {
2322
+ if (posture.allowWrites !== true) {
2323
+ return "read-only";
2324
+ }
2325
+
2326
+ return `can make changes ${Service.describeWriteScope(posture)}`;
2327
+ }
2328
+
2329
+ // Which targets a writing agent may change: its globs, or any.
2330
+ public static describeWriteScope(
2331
+ posture: Pick<ResourceAiAgentPosture, "writeTargets">,
2332
+ ): string {
2333
+ const targets: Array<string> = posture.writeTargets || [];
2334
+
2335
+ return targets.length > 0
2336
+ ? `to ${targets.join(", ")}`
2337
+ : "to anything but its protected targets";
2338
+ }
2339
+
2340
+ // The first successful registration of a resource's agent. Best-effort.
2341
+ private async writeConnectedFeedItem(data: {
2342
+ resource: ResourceAiAgentResource;
2343
+ agentId: ObjectID;
2344
+ posture: ResourceAiAgentPosture;
2345
+ agentVersion?: string | undefined;
2346
+ defaultsApplied: ResourceAiAgentDefaultsApplied;
2347
+ }): Promise<void> {
2348
+ const info: AiResourceTypeInfo =
2349
+ AI_RESOURCE_TYPE_INFO[data.resource.resourceType];
2350
+ const noun: string = describeResourceInSentence(data.resource.resourceType);
2351
+
2352
+ const isInvestigationOn: boolean =
2353
+ data.resource.isAiInvestigationEnabled === true ||
2354
+ data.defaultsApplied.turnedOnInvestigation;
2355
+
2356
+ const sentences: Array<string> = [
2357
+ `🤖 The ${info.agentDisplayName} connected (${Service.describeWriteAccess(
2358
+ data.posture,
2359
+ )}).`,
2360
+ isInvestigationOn
2361
+ ? `AI can now use it to investigate this ${noun}.`
2362
+ : `Investigating through the agent is off for this ${noun}; turn it on on the ${noun}'s AI agent page.`,
2363
+ ];
2364
+
2365
+ if (data.defaultsApplied.remediationMode) {
2366
+ sentences.push(
2367
+ `Fixes are set to "Ask for approval", because the agent allows changes (${RESOURCE_AI_ALLOW_WRITES_ENV}=true).`,
2368
+ );
2369
+ }
2370
+
2371
+ const reachability: string = data.posture.reachable
2372
+ ? "yes"
2373
+ : `no${data.posture.reachError ? ` (${data.posture.reachError})` : ""}`;
2374
+
2375
+ await Service.getResourceBinding(data.resource.resourceType).writeFeedItem({
2376
+ resourceId: data.resource.id,
2377
+ projectId: data.resource.projectId,
2378
+ displayColor: Green500,
2379
+ feedInfoInMarkdown: sentences.join(" "),
2380
+ moreInformationInMarkdown: [
2381
+ `**Agent**: ${info.agentDisplayName} (${data.agentId.toString()})`,
2382
+ `**Identity**: \`${data.posture.resourceIdentifier}\``,
2383
+ `**Agent version**: ${data.agentVersion || "unknown"}`,
2384
+ `**Version**: ${data.posture.toolVersion || "not detected"}`,
2385
+ `**Reachable**: ${reachability}`,
2386
+ ].join("\n\n"),
2387
+ });
2388
+ }
2389
+
2390
+ /*
2391
+ * The first-connection defaults changed a setting after the first
2392
+ * connection (the agent re-registered with write access, or its write
2393
+ * access appeared on a heartbeat). A setting never changes silently.
2394
+ */
2395
+ private async writeDefaultsFeedItem(data: {
2396
+ resource: ResourceAiAgentResource;
2397
+ posture: ResourceAiAgentPosture;
2398
+ defaultsApplied: ResourceAiAgentDefaultsApplied;
2399
+ }): Promise<void> {
2400
+ const info: AiResourceTypeInfo =
2401
+ AI_RESOURCE_TYPE_INFO[data.resource.resourceType];
2402
+ const noun: string = describeResourceInSentence(data.resource.resourceType);
2403
+ const sentences: Array<string> = [];
2404
+
2405
+ if (data.defaultsApplied.turnedOnInvestigation) {
2406
+ sentences.push(
2407
+ `AI can now use the ${info.agentDisplayName} to investigate this ${noun}.`,
2408
+ );
2409
+ }
2410
+
2411
+ if (data.defaultsApplied.remediationMode) {
2412
+ sentences.push(
2413
+ `The ${info.agentDisplayName} can now make changes ${Service.describeWriteScope(
2414
+ data.posture,
2415
+ )}, so fixes are set to "Ask for approval".`,
2416
+ );
2417
+ }
2418
+
2419
+ sentences.push(
2420
+ `Nobody had chosen AI settings for this ${noun} yet; change them on the ${noun}'s AI agent page.`,
2421
+ );
2422
+
2423
+ await Service.getResourceBinding(data.resource.resourceType).writeFeedItem({
2424
+ resourceId: data.resource.id,
2425
+ projectId: data.resource.projectId,
2426
+ displayColor: Blue500,
2427
+ feedInfoInMarkdown: `🤖 ${sentences.join(" ")}`,
2428
+ });
2429
+ }
2430
+ }
2431
+
2432
+ export default new Service();