@oneuptime/common 14.0.8 → 14.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (976) hide show
  1. package/Models/DatabaseModels/AutoRemediationSuggestion.ts +60 -0
  2. package/Models/DatabaseModels/CephCluster.ts +213 -0
  3. package/Models/DatabaseModels/DatabaseServer.ts +213 -0
  4. package/Models/DatabaseModels/DockerHost.ts +213 -0
  5. package/Models/DatabaseModels/DockerSwarmCluster.ts +213 -0
  6. package/Models/DatabaseModels/GlobalConfig.ts +1 -1
  7. package/Models/DatabaseModels/Host.ts +213 -0
  8. package/Models/DatabaseModels/Index.ts +4 -0
  9. package/Models/DatabaseModels/KubernetesAiAgent.ts +364 -0
  10. package/Models/DatabaseModels/KubernetesCluster.ts +3 -3
  11. package/Models/DatabaseModels/PodmanHost.ts +213 -0
  12. package/Models/DatabaseModels/Project.ts +55 -5
  13. package/Models/DatabaseModels/ProxmoxCluster.ts +213 -0
  14. package/Models/DatabaseModels/ProxmoxResource.ts +49 -1
  15. package/Models/DatabaseModels/ResourceAiAgent.ts +419 -0
  16. package/Models/DatabaseModels/RunnerJob.ts +219 -0
  17. package/Models/DatabaseModels/TeamComplianceSetting.ts +143 -2
  18. package/Models/DatabaseModels/VMwareVCenter.ts +213 -0
  19. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +17 -0
  20. package/Server/API/AIChatAPI.ts +6 -3
  21. package/Server/API/AIInvestigationAPI.ts +17 -10
  22. package/Server/API/AutoRemediationAPI.ts +598 -25
  23. package/Server/API/KubernetesClusterAiAccessAPI.ts +623 -21
  24. package/Server/API/MicrosoftTeamsAPI.ts +93 -14
  25. package/Server/API/ProxmoxResourceAPI.ts +244 -14
  26. package/Server/API/ResourceAiAccessAPI.ts +1669 -0
  27. package/Server/Infrastructure/GlobalCache.ts +32 -0
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.ts +99 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.ts +137 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.ts +42 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.ts +423 -0
  32. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  33. package/Server/Infrastructure/Semaphore.ts +22 -0
  34. package/Server/Middleware/TelemetryIngest.ts +51 -18
  35. package/Server/Services/AIService.ts +70 -1
  36. package/Server/Services/AlertService.ts +352 -11
  37. package/Server/Services/AlertSeverityService.ts +65 -15
  38. package/Server/Services/AnalyticsDatabaseService.ts +45 -1
  39. package/Server/Services/AutoRemediationRuleEngineService.ts +940 -112
  40. package/Server/Services/AutoRemediationRuleService.ts +1 -1
  41. package/Server/Services/CephClusterService.ts +109 -1
  42. package/Server/Services/DatabaseServerService.ts +116 -1
  43. package/Server/Services/DockerHostService.ts +109 -1
  44. package/Server/Services/DockerSwarmClusterService.ts +113 -1
  45. package/Server/Services/HostService.ts +126 -4
  46. package/Server/Services/IncidentService.ts +280 -104
  47. package/Server/Services/IncidentSeverityService.ts +66 -15
  48. package/Server/Services/KubernetesAiAgentJobService.ts +185 -0
  49. package/Server/Services/KubernetesAiAgentService.ts +1634 -0
  50. package/Server/Services/KubernetesClusterAiAccessService.ts +1118 -451
  51. package/Server/Services/KubernetesClusterService.ts +129 -21
  52. package/Server/Services/MetricRecordingRuleService.ts +47 -0
  53. package/Server/Services/PodmanHostService.ts +109 -1
  54. package/Server/Services/ProjectService.ts +32 -0
  55. package/Server/Services/ProxmoxClusterService.ts +110 -1
  56. package/Server/Services/ProxmoxResourceService.ts +322 -5
  57. package/Server/Services/ResourceAiAccessService.ts +1439 -0
  58. package/Server/Services/ResourceAiAgentJobService.ts +202 -0
  59. package/Server/Services/ResourceAiAgentService.ts +2432 -0
  60. package/Server/Services/RunnerJobService.ts +852 -108
  61. package/Server/Services/RunnerService.ts +1 -1
  62. package/Server/Services/ScheduledMaintenanceService.ts +737 -1
  63. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +414 -56
  64. package/Server/Services/TeamComplianceSettingService.ts +1125 -9
  65. package/Server/Services/TelemetryUsageBillingService.ts +28 -0
  66. package/Server/Services/TraceRecordingRuleService.ts +33 -0
  67. package/Server/Services/VMwareVCenterService.ts +110 -1
  68. package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +67 -9
  69. package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +39 -10
  70. package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +14 -3
  71. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1475 -51
  72. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +1000 -43
  73. package/Server/Utils/AI/Remediation/RemediationPlanRunner.ts +25 -1
  74. package/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.ts +577 -0
  75. package/Server/Utils/AI/ResourceAccess/ResourceAccessContext.ts +271 -0
  76. package/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.ts +45 -0
  77. package/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.ts +964 -0
  78. package/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.ts +333 -0
  79. package/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.ts +873 -0
  80. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +120 -7
  81. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +72 -5
  82. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +72 -5
  83. package/Server/Utils/AI/SRE/IncidentPostmortemRunner.ts +40 -8
  84. package/Server/Utils/AI/SRE/InvestigationEligibility.ts +13 -2
  85. package/Server/Utils/AI/SRE/InvestigationGrader.ts +1 -1
  86. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +402 -14
  87. package/Server/Utils/AutoRemediation/RemediationVerifier.ts +35 -2
  88. package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +9 -1
  89. package/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.ts +920 -0
  90. package/Server/Utils/SessionReplay/SessionReplayUsage.ts +84 -6
  91. package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +133 -7
  92. package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +58 -4
  93. package/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.ts +447 -0
  94. package/Server/Utils/Telemetry/ProxmoxNativePush.ts +903 -0
  95. package/Server/Utils/Telemetry/TelemetryEntity.ts +87 -27
  96. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +35 -15
  97. package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +33 -15
  98. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +21 -1
  99. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +298 -224
  100. package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +35 -15
  101. package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +389 -175
  102. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +1073 -213
  103. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.ts +163 -0
  104. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.ts +396 -0
  105. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.ts +418 -0
  106. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.ts +152 -0
  107. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.ts +251 -0
  108. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.ts +214 -0
  109. package/Tests/App/AdminDashboard/RowActionPlacementOverrides.test.tsx +278 -0
  110. package/Tests/App/Dashboard/AIChatChartWidgetZoom.test.tsx +738 -0
  111. package/Tests/App/Dashboard/AIFeaturesSettingsPointers.test.tsx +147 -0
  112. package/Tests/App/Dashboard/AIInsightsKubernetesClusterPage.test.tsx +1523 -0
  113. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +463 -3
  114. package/Tests/App/Dashboard/AffectedResourcesPickerDropdownPlacement.test.tsx +958 -0
  115. package/Tests/App/Dashboard/AffectedResourcesPickerFocusLeave.test.tsx +595 -0
  116. package/Tests/App/Dashboard/AskAiAvailability.test.ts +20 -3
  117. package/Tests/App/Dashboard/AskAiDisabled.test.tsx +8 -3
  118. package/Tests/App/Dashboard/CephRateChartReload.test.tsx +1456 -0
  119. package/Tests/App/Dashboard/CephTimeRangeZoom.test.tsx +1229 -0
  120. package/Tests/App/Dashboard/ChartCardTimeRangeZoom.test.tsx +151 -0
  121. package/Tests/App/Dashboard/ChartCardZoomAffordances.test.tsx +257 -0
  122. package/Tests/App/Dashboard/ChartRefetchFrame.test.tsx +141 -0
  123. package/Tests/App/Dashboard/ChartZoomHarness.tsx +249 -0
  124. package/Tests/App/Dashboard/ChartZoomStandIn.tsx +131 -0
  125. package/Tests/App/Dashboard/CloudServerlessSlowLoads.test.tsx +628 -0
  126. package/Tests/App/Dashboard/CloudServerlessTimeRangeZoom.test.tsx +607 -0
  127. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +184 -19
  128. package/Tests/App/Dashboard/CompanionMetricsTabZoom.test.tsx +632 -0
  129. package/Tests/App/Dashboard/ContainerChartZoomStandIn.tsx +195 -0
  130. package/Tests/App/Dashboard/ContainerHostOverviewSlowLoads.test.tsx +542 -0
  131. package/Tests/App/Dashboard/ContainerHostOverviewTimeRangeZoom.test.tsx +848 -0
  132. package/Tests/App/Dashboard/ContainerMetricsTimeRangeZoom.test.tsx +561 -0
  133. package/Tests/App/Dashboard/DashboardBoardWideZoomShell.test.tsx +1147 -0
  134. package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +3 -2
  135. package/Tests/App/Dashboard/DashboardHistogramWidgetDragCursor.test.tsx +399 -0
  136. package/Tests/App/Dashboard/DashboardHistogramWidgetZoom.test.tsx +985 -0
  137. package/Tests/App/Dashboard/DashboardHistogramWidgetZoomHint.test.tsx +803 -0
  138. package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +1 -0
  139. package/Tests/App/Dashboard/DashboardSettingsPreviewZoom.test.tsx +679 -0
  140. package/Tests/App/Dashboard/DashboardSloAndDataSourceChartZoom.test.tsx +881 -0
  141. package/Tests/App/Dashboard/DashboardTraceStatusWidgets.test.tsx +878 -0
  142. package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +18 -6
  143. package/Tests/App/Dashboard/DatabaseMetricChartModalTimeRangeZoom.test.tsx +442 -0
  144. package/Tests/App/Dashboard/DatabaseOverviewTimeRangeZoom.test.tsx +831 -0
  145. package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +43 -0
  146. package/Tests/App/Dashboard/DockerSwarmInsightsTimeRangeZoom.test.tsx +524 -0
  147. package/Tests/App/Dashboard/DockerSwarmRateChartRefresh.test.tsx +259 -0
  148. package/Tests/App/Dashboard/EdgeDetailPanelHistory.test.tsx +763 -0
  149. package/Tests/App/Dashboard/EdgeDetailPanelTimeRangeZoom.test.tsx +547 -0
  150. package/Tests/App/Dashboard/EdgeHistoryBucketsCases.tsx +565 -0
  151. package/Tests/App/Dashboard/EdgeHistoryBucketsKathmandu.test.tsx +73 -0
  152. package/Tests/App/Dashboard/EdgeHistoryBucketsKolkata.test.tsx +74 -0
  153. package/Tests/App/Dashboard/EdgeHistoryBucketsNewYork.test.tsx +72 -0
  154. package/Tests/App/Dashboard/EdgeHistoryBucketsUtc.test.tsx +72 -0
  155. package/Tests/App/Dashboard/EmbeddedMetricCardRefresh.test.tsx +310 -0
  156. package/Tests/App/Dashboard/EmbeddedMetricCardTimeRangeZoom.test.tsx +536 -0
  157. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +3 -1
  158. package/Tests/App/Dashboard/EscalationRuleRowActions.test.tsx +660 -0
  159. package/Tests/App/Dashboard/EventNotes.test.tsx +2 -2
  160. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +587 -13
  161. package/Tests/App/Dashboard/EventOverviewSnapshotExplorerZoom.test.tsx +1139 -0
  162. package/Tests/App/Dashboard/EventOverviewSnapshotZoomAcrossTabs.test.tsx +1057 -0
  163. package/Tests/App/Dashboard/EventOverviewTelemetrySnapshotZoom.test.tsx +757 -0
  164. package/Tests/App/Dashboard/ExceptionOccurrenceTrendZoom.test.tsx +675 -0
  165. package/Tests/App/Dashboard/ExceptionOccurrencesZoomSurvivesViewSwitch.test.tsx +405 -0
  166. package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +67 -3
  167. package/Tests/App/Dashboard/ExceptionTablesSpanStatus.test.tsx +515 -0
  168. package/Tests/App/Dashboard/ExplorerOwnZoomRegression.test.tsx +739 -0
  169. package/Tests/App/Dashboard/FlowBandwidthAxisCases.tsx +461 -0
  170. package/Tests/App/Dashboard/FlowBandwidthAxisKathmandu.test.tsx +25 -0
  171. package/Tests/App/Dashboard/FlowBandwidthAxisKolkata.test.tsx +25 -0
  172. package/Tests/App/Dashboard/FlowBandwidthAxisNewYork.test.tsx +25 -0
  173. package/Tests/App/Dashboard/FlowBandwidthAxisUtc.test.tsx +24 -0
  174. package/Tests/App/Dashboard/FlowTopTalkersZoomRefetch.test.tsx +722 -0
  175. package/Tests/App/Dashboard/FlowViewportGuard.test.tsx +2526 -0
  176. package/Tests/App/Dashboard/HostPagesSlowLoads.test.tsx +872 -0
  177. package/Tests/App/Dashboard/HostPagesTimeRangeZoom.test.tsx +1208 -0
  178. package/Tests/App/Dashboard/IncidentRootCauseTimeRangeZoom.test.tsx +358 -0
  179. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +100 -9
  180. package/Tests/App/Dashboard/InfrastructureMetricsTabTimeRangeZoom.test.tsx +485 -0
  181. package/Tests/App/Dashboard/InvestigationDrawerCompanionZoom.test.tsx +655 -0
  182. package/Tests/App/Dashboard/InvestigationDrawerZoom.test.tsx +830 -0
  183. package/Tests/App/Dashboard/InvestigationInfrastructureTools.test.tsx +506 -0
  184. package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +338 -2
  185. package/Tests/App/Dashboard/InvoicesPayInvoice.test.tsx +454 -16
  186. package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +799 -819
  187. package/Tests/App/Dashboard/KubernetesClusterAiAgentOverviewCard.test.tsx +367 -0
  188. package/Tests/App/Dashboard/KubernetesClusterAiAgentStatus.test.ts +1202 -0
  189. package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +106 -0
  190. package/Tests/App/Dashboard/KubernetesClusterAiNavigation.test.tsx +354 -0
  191. package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +1884 -2054
  192. package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +478 -179
  193. package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +50 -0
  194. package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +2 -1
  195. package/Tests/App/Dashboard/KubernetesClusterOverviewZoom.test.tsx +1217 -0
  196. package/Tests/App/Dashboard/KubernetesClusterPagesZoom.test.tsx +731 -0
  197. package/Tests/App/Dashboard/KubernetesCostsLoadRecovery.test.tsx +869 -0
  198. package/Tests/App/Dashboard/KubernetesCostsZoom.test.tsx +1214 -0
  199. package/Tests/App/Dashboard/KubernetesCostsZoomExactHours.test.tsx +692 -0
  200. package/Tests/App/Dashboard/KubernetesNetworkThroughputReload.test.tsx +902 -0
  201. package/Tests/App/Dashboard/KubernetesNetworkZoomBucketEdges.test.tsx +571 -0
  202. package/Tests/App/Dashboard/KubernetesOverviewZoomBucketEdges.test.tsx +697 -0
  203. package/Tests/App/Dashboard/KubernetesResourceMetricsTabZoom.test.tsx +633 -0
  204. package/Tests/App/Dashboard/LogsExplorerTimeRangeZoom.test.tsx +470 -0
  205. package/Tests/App/Dashboard/LogsInsightsTimeRangeZoom.test.tsx +948 -0
  206. package/Tests/App/Dashboard/LogsViewerPinnedWindowZoom.test.tsx +486 -0
  207. package/Tests/App/Dashboard/MetricChartsBucketInspectorDoubleClick.test.tsx +1001 -0
  208. package/Tests/App/Dashboard/MetricExplorerInvestigationAutoRefresh.test.tsx +514 -0
  209. package/Tests/App/Dashboard/MetricExplorerSavedViewZoomRoundTrip.test.tsx +581 -0
  210. package/Tests/App/Dashboard/MetricExplorerTimeRangeZoom.test.tsx +1162 -0
  211. package/Tests/App/Dashboard/MetricMonitorPreviewZoomLabel.test.ts +72 -0
  212. package/Tests/App/Dashboard/MetricRowSparklineTimeRangeZoom.test.tsx +204 -0
  213. package/Tests/App/Dashboard/MetricViewFirstResultLands.test.tsx +408 -0
  214. package/Tests/App/Dashboard/MetricViewOwnZoomResetPlacement.test.tsx +952 -0
  215. package/Tests/App/Dashboard/MetricViewTimeRange.test.ts +139 -0
  216. package/Tests/App/Dashboard/MetricViewTimeRangeZoom.test.tsx +556 -0
  217. package/Tests/App/Dashboard/MonitorMetricsChartWrappersZoom.test.tsx +552 -0
  218. package/Tests/App/Dashboard/MonitorMetricsTabsTimeRangeZoom.test.tsx +740 -0
  219. package/Tests/App/Dashboard/MonitorOverviewChartsTimeRangeZoom.test.tsx +735 -0
  220. package/Tests/App/Dashboard/MonitorStepPreviewsTimeRangeZoom.test.tsx +835 -0
  221. package/Tests/App/Dashboard/NetworkDeviceTimeRangeZoom.test.tsx +1115 -0
  222. package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +186 -17
  223. package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +155 -46
  224. package/Tests/App/Dashboard/PasskeySettings.test.tsx +87 -2
  225. package/Tests/App/Dashboard/ProjectAISettingsPages.test.tsx +884 -0
  226. package/Tests/App/Dashboard/ProxmoxNodeRemove.test.tsx +621 -0
  227. package/Tests/App/Dashboard/ProxmoxOverviewSlowLoads.test.tsx +584 -0
  228. package/Tests/App/Dashboard/ProxmoxPagesTimeRangeZoom.test.tsx +823 -0
  229. package/Tests/App/Dashboard/ProxmoxRateChartReload.test.tsx +1253 -0
  230. package/Tests/App/Dashboard/PublicDashboardBoardWideZoom.test.tsx +605 -0
  231. package/Tests/App/Dashboard/RealRowsLineChart.tsx +250 -0
  232. package/Tests/App/Dashboard/RechartsHostsPlotCursor.test.tsx +513 -0
  233. package/Tests/App/Dashboard/RemediationSuggestionCardDescription.test.tsx +209 -0
  234. package/Tests/App/Dashboard/RemediationSuggestionCardResource.test.tsx +317 -0
  235. package/Tests/App/Dashboard/ResourceAiAccessSettingsUtil.test.ts +921 -0
  236. package/Tests/App/Dashboard/ResourceAiAgentInstall.test.ts +860 -0
  237. package/Tests/App/Dashboard/ResourceAiAgentPage.test.tsx +1721 -0
  238. package/Tests/App/Dashboard/ResourceAiAgentStatus.test.ts +1002 -0
  239. package/Tests/App/Dashboard/ResourceAiInsightsPage.test.tsx +931 -0
  240. package/Tests/App/Dashboard/ResourceAiNavigation.test.tsx +575 -0
  241. package/Tests/App/Dashboard/RowActionPlacementOverrides.test.tsx +865 -0
  242. package/Tests/App/Dashboard/RumOverviewTimeRangeZoom.test.tsx +877 -0
  243. package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +38 -7
  244. package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +20 -4
  245. package/Tests/App/Dashboard/RunnerSupersededByAiAgentNote.test.tsx +296 -0
  246. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +146 -36
  247. package/Tests/App/Dashboard/ServiceDatabaseSlowLoads.test.tsx +1166 -0
  248. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +1243 -104
  249. package/Tests/App/Dashboard/ServiceOverviewTimeRangeZoom.test.tsx +792 -0
  250. package/Tests/App/Dashboard/SloBudgetBurnDownZoom.test.tsx +771 -0
  251. package/Tests/App/Dashboard/SloBudgetBurnDownZoomAffordances.test.tsx +380 -0
  252. package/Tests/App/Dashboard/SloEventMetricsBarChartZoom.test.tsx +369 -0
  253. package/Tests/App/Dashboard/SloHistoryChartsTimeRangeZoom.test.tsx +694 -0
  254. package/Tests/App/Dashboard/SloHistoryChartsZoomAffordances.test.tsx +497 -0
  255. package/Tests/App/Dashboard/SloMetricsTimeRangeZoom.test.tsx +541 -0
  256. package/Tests/App/Dashboard/SlowLoadHarness.ts +123 -0
  257. package/Tests/App/Dashboard/SnapshotExplorerPrimaryZoom.test.tsx +1194 -0
  258. package/Tests/App/Dashboard/SnapshotViewerPinnedWindow.test.tsx +620 -0
  259. package/Tests/App/Dashboard/TelemetrySnapshotPanelTimeRangeZoom.test.tsx +765 -0
  260. package/Tests/App/Dashboard/TelemetrySnapshotZoom.test.tsx +516 -0
  261. package/Tests/App/Dashboard/TelemetrySnapshotZoomAcrossTabs.test.tsx +827 -0
  262. package/Tests/App/Dashboard/TimeRangeZoomPageHarness.tsx +463 -0
  263. package/Tests/App/Dashboard/TraceChartEditorStatusSplit.test.tsx +543 -0
  264. package/Tests/App/Dashboard/TraceStatusPresentation.test.tsx +967 -0
  265. package/Tests/App/Dashboard/TraceTimelineMinimapZoom.test.tsx +315 -0
  266. package/Tests/App/Dashboard/TracesAnalyticsStatusSplit.test.tsx +778 -0
  267. package/Tests/App/Dashboard/TracesAnalyticsViewZoom.test.tsx +349 -0
  268. package/Tests/App/Dashboard/TracesExplorerTimeRangeZoom.test.tsx +551 -0
  269. package/Tests/App/Dashboard/TracesStatusChartAndFacet.test.tsx +687 -0
  270. package/Tests/App/Dashboard/TracesViewerStaleResponses.test.tsx +458 -0
  271. package/Tests/App/Dashboard/UptimeStripsExcludedFromZoom.test.tsx +406 -0
  272. package/Tests/App/Dashboard/VMwareOverviewSlowLoads.test.tsx +535 -0
  273. package/Tests/App/Dashboard/VMwarePagesTimeRangeZoom.test.tsx +606 -0
  274. package/Tests/App/Dashboard/ValueWidgetSparklineStaleHover.test.tsx +1292 -0
  275. package/Tests/App/Dashboard/ValueWidgetSparklineZoom.test.tsx +765 -0
  276. package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +98 -0
  277. package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +33 -1
  278. package/Tests/Models/DatabaseModels/KubernetesAiAgentModel.test.ts +295 -0
  279. package/Tests/Models/DatabaseModels/ResourceAiAccessColumns.test.ts +738 -0
  280. package/Tests/Models/DatabaseModels/ResourceAiAgentModel.test.ts +842 -0
  281. package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +347 -0
  282. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +58 -0
  283. package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +1 -1
  284. package/Tests/Server/API/AIInvestigationAPI.test.ts +123 -45
  285. package/Tests/Server/API/AutoRemediationApproveClusterRound.test.ts +1329 -0
  286. package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +25 -2
  287. package/Tests/Server/API/AutoRemediationApproveResourceRound.test.ts +835 -0
  288. package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -0
  289. package/Tests/Server/API/KubernetesClusterAiAccessAPIAgent.test.ts +903 -0
  290. package/Tests/Server/API/MicrosoftTeamsChatsRefreshRoute.test.ts +667 -0
  291. package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +294 -6
  292. package/Tests/Server/API/ProxmoxResourceAPIRemoveNode.test.ts +1932 -0
  293. package/Tests/Server/API/ResourceAiAccessAPI.test.ts +2730 -0
  294. package/Tests/Server/Infrastructure/GlobalCache.test.ts +195 -0
  295. package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +60 -0
  296. package/Tests/Server/Infrastructure/Postgres/AddKubernetesAiAgentAndAiDefaultsMigration.test.ts +623 -0
  297. package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +87 -10
  298. package/Tests/Server/Infrastructure/Postgres/AddResourceAiAgentsMigration.test.ts +659 -0
  299. package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleScopeMigration.test.ts +622 -0
  300. package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +31 -0
  301. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +119 -4
  302. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +196 -2
  303. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +123 -2
  304. package/Tests/Server/Services/AIServiceBalanceBlocker.test.ts +282 -0
  305. package/Tests/Server/Services/AlertMonitorClearWritesNull.test.ts +409 -0
  306. package/Tests/Server/Services/AlertMonitorEditFeed.test.ts +474 -0
  307. package/Tests/Server/Services/AlertMonitorEditGuard.test.ts +610 -0
  308. package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +2 -0
  309. package/Tests/Server/Services/AutoRemediationResourceRuleEngine.test.ts +1279 -0
  310. package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +2 -1
  311. package/Tests/Server/Services/DatabaseServerService.test.ts +55 -0
  312. package/Tests/Server/Services/GroupTelemetryUsageExcludeNames.test.ts +299 -0
  313. package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +44 -0
  314. package/Tests/Server/Services/IncidentAffectedMonitorsEdit.test.ts +1360 -0
  315. package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +3 -1
  316. package/Tests/Server/Services/IncidentSeverityDeletePausesComplianceRules.test.ts +488 -0
  317. package/Tests/Server/Services/KubernetesAiAgentJobService.test.ts +329 -0
  318. package/Tests/Server/Services/KubernetesAiAgentServiceHelpers.test.ts +618 -0
  319. package/Tests/Server/Services/KubernetesAiAgentServiceLifecycle.test.ts +1444 -0
  320. package/Tests/Server/Services/KubernetesAiAgentServiceRegister.test.ts +1666 -0
  321. package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +140 -111
  322. package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +267 -105
  323. package/Tests/Server/Services/KubernetesClusterAiAccessLegacySuperseded.test.ts +440 -0
  324. package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +567 -411
  325. package/Tests/Server/Services/KubernetesClusterAiAccessTarget.test.ts +1168 -0
  326. package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +410 -12
  327. package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +55 -0
  328. package/Tests/Server/Services/MonitorProbeServiceIntervalScheduling.test.ts +45 -6
  329. package/Tests/Server/Services/ProjectServiceNewProjectAiDefaults.test.ts +288 -0
  330. package/Tests/Server/Services/ProxmoxResourcePostgres.test.ts +5633 -0
  331. package/Tests/Server/Services/ProxmoxResourceService.test.ts +6458 -5
  332. package/Tests/Server/Services/RecordingRuleReservedMetricName.test.ts +171 -0
  333. package/Tests/Server/Services/ResourceAiAccessChangeAuthorization.test.ts +256 -0
  334. package/Tests/Server/Services/ResourceAiAccessService.test.ts +1373 -0
  335. package/Tests/Server/Services/ResourceAiAgentJobService.test.ts +375 -0
  336. package/Tests/Server/Services/ResourceAiAgentServiceHelpers.test.ts +1114 -0
  337. package/Tests/Server/Services/ResourceAiAgentServiceLifecycle.test.ts +1050 -0
  338. package/Tests/Server/Services/ResourceAiAgentServiceRegister.test.ts +2251 -0
  339. package/Tests/Server/Services/ResourceAiSettingsCreate.test.ts +373 -0
  340. package/Tests/Server/Services/ResourceAiSettingsPermission.test.ts +1042 -0
  341. package/Tests/Server/Services/ResourceServiceDeleteCleansUpAi.test.ts +319 -0
  342. package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +153 -15
  343. package/Tests/Server/Services/RunnerJobEnqueueKubectlAiAgent.test.ts +620 -0
  344. package/Tests/Server/Services/RunnerJobEnqueueResourceCommand.test.ts +986 -0
  345. package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +170 -3
  346. package/Tests/Server/Services/RunnerJobResourceCommandLane.test.ts +211 -0
  347. package/Tests/Server/Services/RunnerJobResourceCommandTimeoutAndRedaction.test.ts +352 -0
  348. package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +72 -0
  349. package/Tests/Server/Services/RunnerServiceAgentName.test.ts +6 -4
  350. package/Tests/Server/Services/ScheduledMaintenanceAttachmentEdit.test.ts +2113 -0
  351. package/Tests/Server/Services/ScheduledMaintenanceMonitorRelease.test.ts +972 -0
  352. package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +2237 -0
  353. package/Tests/Server/Services/TeamComplianceSettingService.test.ts +3988 -138
  354. package/Tests/Server/Services/TelemetryUsageBillingSloExclusion.test.ts +218 -0
  355. package/Tests/Server/TestingUtils/Services/FakeRunnerJobCount.ts +145 -0
  356. package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +107 -1
  357. package/Tests/Server/Utils/AI/IncidentPostmortemRunner.test.ts +351 -0
  358. package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +2 -0
  359. package/Tests/Server/Utils/AI/InvestigationEligibility.test.ts +310 -0
  360. package/Tests/Server/Utils/AI/InvestigationInfrastructureAccessWiring.test.ts +453 -0
  361. package/Tests/Server/Utils/AI/InvestigationInfrastructureReport.test.ts +721 -0
  362. package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +70 -0
  363. package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +58 -0
  364. package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +94 -1
  365. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +2 -0
  366. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +2 -0
  367. package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +2 -0
  368. package/Tests/Server/Utils/AI/RemediationCommandToolsResource.test.ts +1530 -0
  369. package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +125 -1
  370. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +252 -3
  371. package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +2 -0
  372. package/Tests/Server/Utils/AI/RemediationExecutionRunnerProjectGates.test.ts +240 -0
  373. package/Tests/Server/Utils/AI/RemediationExecutionRunnerResourceMode.test.ts +1249 -0
  374. package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +2 -0
  375. package/Tests/Server/Utils/AI/RemediationPlanRunner.test.ts +117 -0
  376. package/Tests/Server/Utils/AI/RemediationResourceCopyParity.test.ts +463 -0
  377. package/Tests/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.test.ts +722 -0
  378. package/Tests/Server/Utils/AI/ResourceAccess/ResourceAccessContext.test.ts +266 -0
  379. package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.test.ts +1220 -0
  380. package/Tests/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.test.ts +569 -0
  381. package/Tests/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.test.ts +830 -0
  382. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +2 -0
  383. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorResource.test.ts +717 -0
  384. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +2 -0
  385. package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +2 -0
  386. package/Tests/Server/Utils/AutoRemediation/RemediationVerifierResourceFollowUp.test.ts +338 -0
  387. package/Tests/Server/Utils/Monitor/Criteria/SessionReplayBudgetTemplateCriteria.test.ts +422 -0
  388. package/Tests/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.test.ts +1647 -0
  389. package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +143 -1
  390. package/Tests/Server/Utils/Telemetry/HostAssetAttributesFromOtlp.test.ts +68 -0
  391. package/Tests/Server/Utils/Telemetry/InventoryMirrorPostgres.test.ts +370 -0
  392. package/Tests/Server/Utils/Telemetry/NetworkDeviceInventoryAttributes.test.ts +522 -0
  393. package/Tests/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.test.ts +155 -0
  394. package/Tests/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.test.ts +8631 -0
  395. package/Tests/Server/Utils/Telemetry/ProxmoxNativePush.test.ts +2852 -0
  396. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +367 -1
  397. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +75 -3
  398. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsAccountNotLinked.test.ts +1093 -0
  399. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.test.ts +1049 -0
  400. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.test.ts +1676 -0
  401. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCards.test.ts +2884 -0
  402. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.test.ts +2490 -0
  403. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmit.test.ts +2293 -0
  404. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateSubmitServerTimezone.test.ts +386 -0
  405. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.test.ts +1477 -0
  406. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.test.ts +1967 -0
  407. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsStripHtmlTags.test.ts +92 -0
  408. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsSubmittedFormRemoval.test.ts +1819 -0
  409. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.test.ts +1033 -0
  410. package/Tests/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezoneServerZone.test.ts +610 -0
  411. package/Tests/Server/Utils/Workspace/MicrosoftTeamsBotMessageHandling.test.ts +2610 -0
  412. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +25 -0
  413. package/Tests/Server/Utils/Workspace/MicrosoftTeamsCreateCommandsEndToEnd.test.ts +4672 -0
  414. package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNames.test.ts +3119 -0
  415. package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNamesPostgres.test.ts +1721 -0
  416. package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +190 -65
  417. package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +10 -4
  418. package/Tests/Types/AutoRemediation/AiRemediationCommandPlanResourceCommand.test.ts +305 -0
  419. package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +581 -113
  420. package/Tests/Types/Kubernetes/KubernetesAiAgentAccessContract.test.ts +510 -0
  421. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +277 -13
  422. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +113 -0
  423. package/Tests/Types/Monitor/RumAlertTemplates.test.ts +773 -2
  424. package/Tests/Types/ResourceAiAgent/AiResourceType.test.ts +419 -0
  425. package/Tests/Types/ResourceAiAgent/ResourceAiAccess.test.ts +517 -0
  426. package/Tests/Types/Runbook/RunbookStepType.test.ts +147 -8
  427. package/Tests/Types/Team/ComplianceRule.test.ts +1029 -0
  428. package/Tests/Types/Team/TeamComplianceStatus.test.ts +176 -0
  429. package/Tests/UI/Components/ActionButton/RowActions.test.tsx +641 -0
  430. package/Tests/UI/Components/ActionButton/RowActionsAccessibility.test.tsx +414 -0
  431. package/Tests/UI/Components/ActionButton/SplitActionButtons.test.ts +565 -0
  432. package/Tests/UI/Components/Charts/BarChartDragToSelect.test.tsx +386 -0
  433. package/Tests/UI/Components/Charts/ChartDoubleClickResetWithoutDblclick.test.tsx +1025 -0
  434. package/Tests/UI/Components/Charts/ChartGroupPageZoomHint.test.tsx +196 -0
  435. package/Tests/UI/Components/Charts/ChartPlaceholdersTakeDoubleClick.test.tsx +1861 -0
  436. package/Tests/UI/Components/Charts/ChartPlotClicksWhileResetArmed.test.tsx +382 -0
  437. package/Tests/UI/Components/Charts/ChartRangeSelection.test.tsx +704 -0
  438. package/Tests/UI/Components/Charts/ChartRangeSelectionDoubleClickReset.test.tsx +1481 -0
  439. package/Tests/UI/Components/Charts/ChartRangeSelectionRealRecharts.test.tsx +918 -0
  440. package/Tests/UI/Components/Charts/ChartRowBucketStart.test.ts +350 -0
  441. package/Tests/UI/Components/Charts/DoubleClickReset.test.tsx +996 -0
  442. package/Tests/UI/Components/Charts/HistogramDoubleClickResetRealRecharts.test.tsx +965 -0
  443. package/Tests/UI/Components/Charts/HistogramDriftThenRefetch.test.tsx +277 -0
  444. package/Tests/UI/Components/Charts/HistogramRangeSelectionDataChange.test.tsx +1757 -0
  445. package/Tests/UI/Components/Charts/HistogramRangeSelectionRealRecharts.test.tsx +560 -0
  446. package/Tests/UI/Components/Charts/TimeRangeZoom/ChartPageTimeRangeZoom.test.tsx +513 -0
  447. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.test.tsx +845 -0
  448. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.test.tsx +119 -0
  449. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHintTailwind.test.tsx +274 -0
  450. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.test.ts +433 -0
  451. package/Tests/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.test.tsx +863 -0
  452. package/Tests/UI/Components/Charts/TimeRangeZoom/ZoomDocsPromises.test.tsx +1147 -0
  453. package/Tests/UI/Components/DisabledButtonTooltip.test.tsx +14 -3
  454. package/Tests/UI/Components/EntityDropdownEscape.test.tsx +454 -0
  455. package/Tests/UI/Components/EntityDropdownFocusLeave.test.tsx +420 -0
  456. package/Tests/UI/Components/Forms/FormFieldDropdownDisabled.test.tsx +233 -0
  457. package/Tests/UI/Components/Input.test.tsx +80 -1
  458. package/Tests/UI/Components/LogsAnalyticsViewZoom.test.tsx +947 -0
  459. package/Tests/UI/Components/LogsHistogramDragTooltip.test.tsx +17 -5
  460. package/Tests/UI/Components/LogsViewerAnalyticsZoom.test.tsx +501 -0
  461. package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +1 -1
  462. package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +6 -4
  463. package/Tests/UI/Components/ModelTable/BaseModelTablePermissionGating.test.tsx +60 -8
  464. package/Tests/UI/Components/ModelTable/BaseModelTableRowActions.test.tsx +476 -0
  465. package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +7 -5
  466. package/Tests/UI/Components/MoreMenuPortal.test.tsx +623 -0
  467. package/Tests/UI/Components/StatusPage/ResourceGridActions.test.tsx +538 -0
  468. package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +14 -4
  469. package/Tests/UI/Components/TableCellWrapping.test.tsx +3 -3
  470. package/Tests/UI/Components/TableRowActions.test.tsx +472 -0
  471. package/Tests/UI/Components/TelemetryHistogramDragTooltip.test.tsx +1 -1
  472. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +1 -1
  473. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerZoomScope.test.tsx +470 -0
  474. package/Tests/UI/Components/TelemetryViewer/UseViewerTimeRangeZoom.test.tsx +571 -0
  475. package/Tests/UI/Components/TelemetryViewer/ViewerDoubleClickResetUnderDataChange.test.tsx +928 -0
  476. package/Tests/UI/Components/TelemetryViewer/ViewerFollowsEnclosingZoom.test.tsx +1569 -0
  477. package/Tests/UI/Components/Toggle.test.tsx +66 -0
  478. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardRefreshNames.test.tsx +1023 -0
  479. package/Tests/UI/Rum/ChunkLoader.test.ts +201 -0
  480. package/Tests/UI/Rum/FidelityNotices.test.ts +54 -0
  481. package/Tests/UI/Rum/RecordingHealthDashboard.test.tsx +769 -1
  482. package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +72 -0
  483. package/Tests/UI/Rum/ReplayFrameCapture.test.ts +1009 -9
  484. package/Tests/UI/Rum/ReplayRecordedAssets.test.ts +1069 -0
  485. package/Tests/UI/Rum/ReplayStage.test.tsx +913 -2
  486. package/Tests/UI/Rum/SessionReplayTable.test.tsx +443 -8
  487. package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +439 -9
  488. package/Tests/UI/Rum/UserFlowTablesRowActions.test.tsx +501 -0
  489. package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +47 -19
  490. package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +2 -0
  491. package/Tests/Utils/AiRemediation/KubernetesAiAgentPolicyCopyParity.test.ts +396 -0
  492. package/Tests/Utils/AiRemediation/Resource/CephCommandPolicy.test.ts +1653 -0
  493. package/Tests/Utils/AiRemediation/Resource/CephOutputRedaction.test.ts +204 -0
  494. package/Tests/Utils/AiRemediation/Resource/DatabaseCommandPolicy.test.ts +1077 -0
  495. package/Tests/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.test.ts +558 -0
  496. package/Tests/Utils/AiRemediation/Resource/DatabaseQueryRedactor.test.ts +834 -0
  497. package/Tests/Utils/AiRemediation/Resource/DockerCliGrammar.test.ts +747 -0
  498. package/Tests/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.test.ts +1259 -0
  499. package/Tests/Utils/AiRemediation/Resource/DockerOutputRedaction.test.ts +386 -0
  500. package/Tests/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.test.ts +782 -0
  501. package/Tests/Utils/AiRemediation/Resource/GovcCommandPolicy.test.ts +2259 -0
  502. package/Tests/Utils/AiRemediation/Resource/HostCommandPolicy.test.ts +2019 -0
  503. package/Tests/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.test.ts +2497 -0
  504. package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicy.test.ts +1305 -0
  505. package/Tests/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.test.ts +585 -0
  506. package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactor.test.ts +334 -0
  507. package/Tests/Utils/AiRemediation/Resource/ResourceOutputRedactorCopyParity.test.ts +98 -0
  508. package/Tests/Utils/AiRemediation/Resource/ResourcePolicyImportClosure.test.ts +391 -0
  509. package/Tests/Utils/AiRemediation/ResourceAiAgentPolicyCopyParity.test.ts +497 -0
  510. package/Tests/Utils/Dashboard/DashboardTimeRangeZoom.test.ts +104 -1
  511. package/Tests/Utils/SessionReplay/SessionReplayBudgetMetricType.test.ts +383 -0
  512. package/Types/AI/AIRunType.ts +9 -4
  513. package/Types/AI/InvestigationNotStartedReason.ts +6 -0
  514. package/Types/AI/ResourceAiAccessApi.ts +140 -0
  515. package/Types/AI/ResourceAiAccessPermissions.ts +126 -0
  516. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +197 -18
  517. package/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.ts +28 -0
  518. package/Types/Dashboard/DashboardComponents/DashboardTraceChartComponent.ts +4 -2
  519. package/Types/Kubernetes/KubernetesClusterAiAccess.ts +263 -32
  520. package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +20 -16
  521. package/Types/Kubernetes/KubernetesClusterAiInsights.ts +81 -0
  522. package/Types/Monitor/ProxmoxAlertTemplates.ts +4 -4
  523. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +55 -22
  524. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +35 -3
  525. package/Types/Monitor/RumAlertTemplates.ts +351 -4
  526. package/Types/ResourceAiAgent/AiResourceType.ts +310 -0
  527. package/Types/ResourceAiAgent/ResourceAiAccess.ts +574 -0
  528. package/Types/Rum/SessionReplayBudgetMetricType.ts +47 -0
  529. package/Types/Runbook/RunbookStepType.ts +29 -0
  530. package/Types/Team/ComplianceNotificationChannel.ts +22 -0
  531. package/Types/Team/ComplianceRule.ts +434 -0
  532. package/Types/Team/ComplianceRuleType.ts +21 -0
  533. package/Types/Team/TeamComplianceStatus.ts +81 -0
  534. package/Types/Telemetry/TelemetryIngestSurface.ts +40 -0
  535. package/UI/Components/ActionButton/ActionButtonSchema.ts +21 -0
  536. package/UI/Components/ActionButton/RowActions.tsx +238 -0
  537. package/UI/Components/ActionButton/SplitActionButtons.ts +173 -0
  538. package/UI/Components/Charts/Area/AreaChart.tsx +32 -2
  539. package/UI/Components/Charts/Bar/BarChart.tsx +40 -3
  540. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +37 -11
  541. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +80 -243
  542. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +122 -31
  543. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +95 -244
  544. package/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.ts +214 -0
  545. package/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.ts +32 -0
  546. package/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.ts +599 -0
  547. package/UI/Components/Charts/Line/LineChart.tsx +32 -2
  548. package/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.tsx +70 -0
  549. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.tsx +226 -0
  550. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.tsx +73 -0
  551. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.ts +163 -0
  552. package/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.ts +215 -0
  553. package/UI/Components/Charts/Utils/DataPoint.ts +20 -8
  554. package/UI/Components/Charts/Utils/XAxis.ts +75 -0
  555. package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +493 -59
  556. package/UI/Components/EntityDropdown/EntityDropdown.tsx +107 -11
  557. package/UI/Components/Forms/Fields/FormField.tsx +10 -0
  558. package/UI/Components/Input/Input.tsx +90 -57
  559. package/UI/Components/List/ListRow.tsx +8 -74
  560. package/UI/Components/LogsViewer/LogsViewer.tsx +40 -11
  561. package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +22 -6
  562. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +209 -6
  563. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +127 -84
  564. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +3 -0
  565. package/UI/Components/ModelTable/BaseModelTable.tsx +12 -1
  566. package/UI/Components/MoreMenu/MoreMenu.tsx +365 -30
  567. package/UI/Components/MoreMenu/MoreMenuItem.tsx +70 -17
  568. package/UI/Components/OrderedStatesList/Item.tsx +7 -72
  569. package/UI/Components/StatusPage/ResourceGrid.tsx +126 -77
  570. package/UI/Components/Table/TableBody.tsx +8 -2
  571. package/UI/Components/Table/TableRow.tsx +20 -140
  572. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +41 -10
  573. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +47 -9
  574. package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +25 -6
  575. package/UI/Components/TelemetryViewer/types.ts +2 -0
  576. package/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.ts +448 -0
  577. package/UI/Components/Toggle/Toggle.tsx +26 -0
  578. package/UI/Components/Tooltip/Tooltip.tsx +8 -1
  579. package/UI/Styles/Theme.css +14 -2
  580. package/Utils/AI/InvestigationReport.ts +121 -15
  581. package/Utils/AiRemediation/CommandPolicy.ts +1 -1
  582. package/Utils/AiRemediation/KubectlPolicy.ts +1 -1
  583. package/Utils/AiRemediation/Resource/CephCommandPolicy.ts +1936 -0
  584. package/Utils/AiRemediation/Resource/DatabaseCommandPolicy.ts +166 -0
  585. package/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.ts +1532 -0
  586. package/Utils/AiRemediation/Resource/DatabaseQueryRedactor.ts +1394 -0
  587. package/Utils/AiRemediation/Resource/DockerCliGrammar.ts +1839 -0
  588. package/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.ts +490 -0
  589. package/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.ts +687 -0
  590. package/Utils/AiRemediation/Resource/GovcCommandPolicy.ts +2096 -0
  591. package/Utils/AiRemediation/Resource/HostCommandPolicy.ts +2982 -0
  592. package/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.ts +1679 -0
  593. package/Utils/AiRemediation/Resource/ResourceCommandPolicy.ts +851 -0
  594. package/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.ts +593 -0
  595. package/Utils/AiRemediation/Resource/ResourceOutputRedactor.ts +2812 -0
  596. package/Utils/Dashboard/DashboardTimeRangeZoom.ts +46 -2
  597. package/Utils/SessionReplay/SessionReplayBudgetMetricType.ts +184 -0
  598. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +62 -0
  599. package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
  600. package/build/dist/Models/DatabaseModels/CephCluster.js +219 -0
  601. package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
  602. package/build/dist/Models/DatabaseModels/DatabaseServer.js +219 -0
  603. package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -1
  604. package/build/dist/Models/DatabaseModels/DockerHost.js +219 -0
  605. package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
  606. package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js +219 -0
  607. package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js.map +1 -1
  608. package/build/dist/Models/DatabaseModels/GlobalConfig.js +1 -1
  609. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  610. package/build/dist/Models/DatabaseModels/Host.js +219 -0
  611. package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
  612. package/build/dist/Models/DatabaseModels/Index.js +4 -0
  613. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  614. package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js +385 -0
  615. package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js.map +1 -0
  616. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +3 -3
  617. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  618. package/build/dist/Models/DatabaseModels/PodmanHost.js +219 -0
  619. package/build/dist/Models/DatabaseModels/PodmanHost.js.map +1 -1
  620. package/build/dist/Models/DatabaseModels/Project.js +56 -5
  621. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  622. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +219 -0
  623. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
  624. package/build/dist/Models/DatabaseModels/ProxmoxResource.js +51 -1
  625. package/build/dist/Models/DatabaseModels/ProxmoxResource.js.map +1 -1
  626. package/build/dist/Models/DatabaseModels/ResourceAiAgent.js +439 -0
  627. package/build/dist/Models/DatabaseModels/ResourceAiAgent.js.map +1 -0
  628. package/build/dist/Models/DatabaseModels/RunnerJob.js +219 -0
  629. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  630. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +134 -3
  631. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
  632. package/build/dist/Models/DatabaseModels/VMwareVCenter.js +219 -0
  633. package/build/dist/Models/DatabaseModels/VMwareVCenter.js.map +1 -1
  634. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  635. package/build/dist/Server/API/AIChatAPI.js +6 -1
  636. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  637. package/build/dist/Server/API/AIInvestigationAPI.js +17 -10
  638. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  639. package/build/dist/Server/API/AutoRemediationAPI.js +374 -24
  640. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  641. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +436 -22
  642. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -1
  643. package/build/dist/Server/API/MicrosoftTeamsAPI.js +69 -13
  644. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  645. package/build/dist/Server/API/ProxmoxResourceAPI.js +187 -14
  646. package/build/dist/Server/API/ProxmoxResourceAPI.js.map +1 -1
  647. package/build/dist/Server/API/ResourceAiAccessAPI.js +1119 -0
  648. package/build/dist/Server/API/ResourceAiAccessAPI.js.map +1 -0
  649. package/build/dist/Server/Infrastructure/GlobalCache.js +27 -0
  650. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  651. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js +50 -0
  652. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js.map +1 -0
  653. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js +84 -0
  654. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js.map +1 -0
  655. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js +31 -0
  656. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js.map +1 -0
  657. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js +168 -0
  658. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796300000000-AddResourceAiAgents.js.map +1 -0
  659. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  660. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  661. package/build/dist/Server/Infrastructure/Semaphore.js +6 -0
  662. package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
  663. package/build/dist/Server/Middleware/TelemetryIngest.js +41 -19
  664. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  665. package/build/dist/Server/Services/AIService.js +59 -1
  666. package/build/dist/Server/Services/AIService.js.map +1 -1
  667. package/build/dist/Server/Services/AlertService.js +248 -13
  668. package/build/dist/Server/Services/AlertService.js.map +1 -1
  669. package/build/dist/Server/Services/AlertSeverityService.js +51 -15
  670. package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
  671. package/build/dist/Server/Services/AnalyticsDatabaseService.js +26 -1
  672. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  673. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +601 -3
  674. package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
  675. package/build/dist/Server/Services/AutoRemediationRuleService.js +1 -1
  676. package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
  677. package/build/dist/Server/Services/CephClusterService.js +104 -0
  678. package/build/dist/Server/Services/CephClusterService.js.map +1 -1
  679. package/build/dist/Server/Services/DatabaseServerService.js +99 -0
  680. package/build/dist/Server/Services/DatabaseServerService.js.map +1 -1
  681. package/build/dist/Server/Services/DockerHostService.js +104 -0
  682. package/build/dist/Server/Services/DockerHostService.js.map +1 -1
  683. package/build/dist/Server/Services/DockerSwarmClusterService.js +104 -0
  684. package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
  685. package/build/dist/Server/Services/HostService.js +114 -2
  686. package/build/dist/Server/Services/HostService.js.map +1 -1
  687. package/build/dist/Server/Services/IncidentService.js +199 -72
  688. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  689. package/build/dist/Server/Services/IncidentSeverityService.js +51 -15
  690. package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
  691. package/build/dist/Server/Services/KubernetesAiAgentJobService.js +158 -0
  692. package/build/dist/Server/Services/KubernetesAiAgentJobService.js.map +1 -0
  693. package/build/dist/Server/Services/KubernetesAiAgentService.js +1145 -0
  694. package/build/dist/Server/Services/KubernetesAiAgentService.js.map +1 -0
  695. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +842 -371
  696. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -1
  697. package/build/dist/Server/Services/KubernetesClusterService.js +86 -17
  698. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  699. package/build/dist/Server/Services/MetricRecordingRuleService.js +46 -0
  700. package/build/dist/Server/Services/MetricRecordingRuleService.js.map +1 -1
  701. package/build/dist/Server/Services/PodmanHostService.js +104 -0
  702. package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
  703. package/build/dist/Server/Services/ProjectService.js +22 -0
  704. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  705. package/build/dist/Server/Services/ProxmoxClusterService.js +104 -0
  706. package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
  707. package/build/dist/Server/Services/ProxmoxResourceService.js +258 -3
  708. package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
  709. package/build/dist/Server/Services/ResourceAiAccessService.js +1028 -0
  710. package/build/dist/Server/Services/ResourceAiAccessService.js.map +1 -0
  711. package/build/dist/Server/Services/ResourceAiAgentJobService.js +173 -0
  712. package/build/dist/Server/Services/ResourceAiAgentJobService.js.map +1 -0
  713. package/build/dist/Server/Services/ResourceAiAgentService.js +1687 -0
  714. package/build/dist/Server/Services/ResourceAiAgentService.js.map +1 -0
  715. package/build/dist/Server/Services/RunnerJobService.js +522 -97
  716. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  717. package/build/dist/Server/Services/RunnerService.js +1 -1
  718. package/build/dist/Server/Services/RunnerService.js.map +1 -1
  719. package/build/dist/Server/Services/ScheduledMaintenanceService.js +535 -3
  720. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  721. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +316 -46
  722. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  723. package/build/dist/Server/Services/TeamComplianceSettingService.js +833 -7
  724. package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
  725. package/build/dist/Server/Services/TelemetryUsageBillingService.js +26 -0
  726. package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
  727. package/build/dist/Server/Services/TraceRecordingRuleService.js +37 -0
  728. package/build/dist/Server/Services/TraceRecordingRuleService.js.map +1 -1
  729. package/build/dist/Server/Services/VMwareVCenterService.js +104 -0
  730. package/build/dist/Server/Services/VMwareVCenterService.js.map +1 -1
  731. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +52 -9
  732. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -1
  733. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +13 -9
  734. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
  735. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +7 -3
  736. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
  737. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1056 -43
  738. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  739. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +683 -42
  740. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  741. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js +21 -1
  742. package/build/dist/Server/Utils/AI/Remediation/RemediationPlanRunner.js.map +1 -1
  743. package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js +330 -0
  744. package/build/dist/Server/Utils/AI/ResourceAccess/InfrastructureInvestigationToolkit.js.map +1 -0
  745. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js +160 -0
  746. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessContext.js.map +1 -0
  747. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js +33 -0
  748. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAccessToolNames.js.map +1 -0
  749. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js +640 -0
  750. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiAccessSettings.js.map +1 -0
  751. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js +226 -0
  752. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceAiDeleteCleanup.js.map +1 -0
  753. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js +600 -0
  754. package/build/dist/Server/Utils/AI/ResourceAccess/ResourceCommandJobRunner.js.map +1 -0
  755. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +89 -9
  756. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  757. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +55 -4
  758. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  759. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +55 -4
  760. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  761. package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js +30 -3
  762. package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js.map +1 -1
  763. package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js +13 -2
  764. package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js.map +1 -1
  765. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +1 -1
  766. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +294 -15
  767. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
  768. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +29 -1
  769. package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
  770. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +9 -1
  771. package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
  772. package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js +627 -0
  773. package/build/dist/Server/Utils/SessionReplay/SessionReplayBudgetMetrics.js.map +1 -0
  774. package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js +60 -5
  775. package/build/dist/Server/Utils/SessionReplay/SessionReplayUsage.js.map +1 -1
  776. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +111 -6
  777. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
  778. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +40 -4
  779. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
  780. package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js +296 -0
  781. package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js.map +1 -0
  782. package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js +699 -0
  783. package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js.map +1 -0
  784. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +77 -19
  785. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  786. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +19 -15
  787. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
  788. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +19 -15
  789. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
  790. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +17 -1
  791. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
  792. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +183 -209
  793. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
  794. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +19 -15
  795. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
  796. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +233 -167
  797. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
  798. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +768 -152
  799. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  800. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js +129 -0
  801. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsActivityDeduplicator.js.map +1 -0
  802. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js +266 -0
  803. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCardChoices.js.map +1 -0
  804. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js +258 -0
  805. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsCreateCommands.js.map +1 -0
  806. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js +104 -0
  807. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsMessageSize.js.map +1 -0
  808. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js +183 -0
  809. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsReplies.js.map +1 -0
  810. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js +123 -0
  811. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsTimezone.js.map +1 -0
  812. package/build/dist/Types/AI/AIRunType.js +9 -4
  813. package/build/dist/Types/AI/AIRunType.js.map +1 -1
  814. package/build/dist/Types/AI/ResourceAiAccessApi.js +30 -0
  815. package/build/dist/Types/AI/ResourceAiAccessApi.js.map +1 -0
  816. package/build/dist/Types/AI/ResourceAiAccessPermissions.js +98 -0
  817. package/build/dist/Types/AI/ResourceAiAccessPermissions.js.map +1 -0
  818. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +126 -45
  819. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
  820. package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js +28 -0
  821. package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js.map +1 -0
  822. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +72 -16
  823. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
  824. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +20 -16
  825. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -1
  826. package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js +17 -0
  827. package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js.map +1 -0
  828. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +4 -4
  829. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -1
  830. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +47 -21
  831. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  832. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +5 -0
  833. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  834. package/build/dist/Types/Monitor/RumAlertTemplates.js +199 -3
  835. package/build/dist/Types/Monitor/RumAlertTemplates.js.map +1 -1
  836. package/build/dist/Types/ResourceAiAgent/AiResourceType.js +242 -0
  837. package/build/dist/Types/ResourceAiAgent/AiResourceType.js.map +1 -0
  838. package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js +275 -0
  839. package/build/dist/Types/ResourceAiAgent/ResourceAiAccess.js.map +1 -0
  840. package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js +45 -0
  841. package/build/dist/Types/Rum/SessionReplayBudgetMetricType.js.map +1 -0
  842. package/build/dist/Types/Runbook/RunbookStepType.js +27 -0
  843. package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
  844. package/build/dist/Types/Team/ComplianceNotificationChannel.js +23 -0
  845. package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -0
  846. package/build/dist/Types/Team/ComplianceRule.js +304 -0
  847. package/build/dist/Types/Team/ComplianceRule.js.map +1 -0
  848. package/build/dist/Types/Team/ComplianceRuleType.js +21 -0
  849. package/build/dist/Types/Team/ComplianceRuleType.js.map +1 -1
  850. package/build/dist/Types/Team/TeamComplianceStatus.js +2 -0
  851. package/build/dist/Types/Team/TeamComplianceStatus.js.map +1 -0
  852. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +36 -0
  853. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
  854. package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js +20 -1
  855. package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js.map +1 -1
  856. package/build/dist/UI/Components/ActionButton/RowActions.js +113 -0
  857. package/build/dist/UI/Components/ActionButton/RowActions.js.map +1 -0
  858. package/build/dist/UI/Components/ActionButton/SplitActionButtons.js +71 -0
  859. package/build/dist/UI/Components/ActionButton/SplitActionButtons.js.map +1 -0
  860. package/build/dist/UI/Components/Charts/Area/AreaChart.js +16 -1
  861. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  862. package/build/dist/UI/Components/Charts/Bar/BarChart.js +16 -1
  863. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  864. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +27 -5
  865. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  866. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +61 -194
  867. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  868. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +84 -26
  869. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  870. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +75 -196
  871. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  872. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js +133 -0
  873. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js.map +1 -1
  874. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js +26 -0
  875. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js.map +1 -0
  876. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js +371 -0
  877. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js.map +1 -0
  878. package/build/dist/UI/Components/Charts/Line/LineChart.js +16 -1
  879. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  880. package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js +35 -0
  881. package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js.map +1 -0
  882. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js +107 -0
  883. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js.map +1 -0
  884. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js +41 -0
  885. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js.map +1 -0
  886. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js +117 -0
  887. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js.map +1 -0
  888. package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js +121 -0
  889. package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js.map +1 -0
  890. package/build/dist/UI/Components/Charts/Utils/DataPoint.js +15 -7
  891. package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
  892. package/build/dist/UI/Components/Charts/Utils/XAxis.js +70 -0
  893. package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
  894. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +305 -38
  895. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -1
  896. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +89 -14
  897. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  898. package/build/dist/UI/Components/Forms/Fields/FormField.js +11 -2
  899. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  900. package/build/dist/UI/Components/Input/Input.js +69 -54
  901. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  902. package/build/dist/UI/Components/List/ListRow.js +4 -39
  903. package/build/dist/UI/Components/List/ListRow.js.map +1 -1
  904. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +31 -8
  905. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  906. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +10 -1
  907. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
  908. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +139 -21
  909. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  910. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +30 -8
  911. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  912. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
  913. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  914. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +10 -0
  915. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  916. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +253 -14
  917. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  918. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +43 -13
  919. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
  920. package/build/dist/UI/Components/OrderedStatesList/Item.js +2 -38
  921. package/build/dist/UI/Components/OrderedStatesList/Item.js.map +1 -1
  922. package/build/dist/UI/Components/StatusPage/ResourceGrid.js +53 -15
  923. package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
  924. package/build/dist/UI/Components/Table/TableBody.js +6 -2
  925. package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
  926. package/build/dist/UI/Components/Table/TableRow.js +12 -75
  927. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  928. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +32 -7
  929. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  930. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +27 -9
  931. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  932. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +11 -1
  933. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
  934. package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js +272 -0
  935. package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js.map +1 -0
  936. package/build/dist/UI/Components/Toggle/Toggle.js +14 -1
  937. package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
  938. package/build/dist/UI/Components/Tooltip/Tooltip.js +1 -1
  939. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  940. package/build/dist/Utils/AI/InvestigationReport.js +71 -11
  941. package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
  942. package/build/dist/Utils/AiRemediation/CommandPolicy.js +1 -1
  943. package/build/dist/Utils/AiRemediation/CommandPolicy.js.map +1 -1
  944. package/build/dist/Utils/AiRemediation/KubectlPolicy.js +1 -1
  945. package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -1
  946. package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js +1462 -0
  947. package/build/dist/Utils/AiRemediation/Resource/CephCommandPolicy.js.map +1 -0
  948. package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js +122 -0
  949. package/build/dist/Utils/AiRemediation/Resource/DatabaseCommandPolicy.js.map +1 -0
  950. package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js +1139 -0
  951. package/build/dist/Utils/AiRemediation/Resource/DatabaseDiagnosticCatalog.js.map +1 -0
  952. package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js +1060 -0
  953. package/build/dist/Utils/AiRemediation/Resource/DatabaseQueryRedactor.js.map +1 -0
  954. package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js +1301 -0
  955. package/build/dist/Utils/AiRemediation/Resource/DockerCliGrammar.js.map +1 -0
  956. package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js +370 -0
  957. package/build/dist/Utils/AiRemediation/Resource/DockerEngineCommandPolicy.js.map +1 -0
  958. package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js +498 -0
  959. package/build/dist/Utils/AiRemediation/Resource/DockerSwarmCommandPolicy.js.map +1 -0
  960. package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js +1565 -0
  961. package/build/dist/Utils/AiRemediation/Resource/GovcCommandPolicy.js.map +1 -0
  962. package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js +2206 -0
  963. package/build/dist/Utils/AiRemediation/Resource/HostCommandPolicy.js.map +1 -0
  964. package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js +1129 -0
  965. package/build/dist/Utils/AiRemediation/Resource/ProxmoxCommandPolicy.js.map +1 -0
  966. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js +565 -0
  967. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicy.js.map +1 -0
  968. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js +408 -0
  969. package/build/dist/Utils/AiRemediation/Resource/ResourceCommandPolicyCore.js.map +1 -0
  970. package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js +1910 -0
  971. package/build/dist/Utils/AiRemediation/Resource/ResourceOutputRedactor.js.map +1 -0
  972. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js +27 -1
  973. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js.map +1 -1
  974. package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js +160 -0
  975. package/build/dist/Utils/SessionReplay/SessionReplayBudgetMetricType.js.map +1 -0
  976. package/package.json +1 -1
@@ -0,0 +1,1721 @@
1
+ import "@testing-library/jest-dom";
2
+ import {
3
+ afterEach,
4
+ beforeEach,
5
+ describe,
6
+ expect,
7
+ jest,
8
+ test,
9
+ } from "@jest/globals";
10
+ import {
11
+ act,
12
+ cleanup,
13
+ fireEvent,
14
+ render,
15
+ screen,
16
+ waitFor,
17
+ within,
18
+ } from "@testing-library/react";
19
+ import React from "react";
20
+ import { MemoryRouter, Route as PageRoute, Routes } from "react-router-dom";
21
+ import ResourceAiAgentPage from "../../../../App/FeatureSet/Dashboard/src/Components/ResourceAiAgent/ResourceAiAgentPage";
22
+ import {
23
+ ResourceAiAgentDescriptor,
24
+ getResourceAiAgentDescriptor,
25
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/ResourceAiAgent/ResourceAiAgentDescriptors";
26
+ import {
27
+ RESOURCE_AI_AGENT_STATUS_POLL_INTERVAL_MS,
28
+ getResourceAiAgentNotInstalledText,
29
+ getResourceAiAgentPageSubtitle,
30
+ getResourceAiAgentReadyText,
31
+ getResourceAiAgentSignedOffText,
32
+ getResourceAiAgentSilentText,
33
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/ResourceAiAgent/ResourceAiAgentStatus";
34
+ import {
35
+ RESOURCE_REMEDIATION_MODE_LABELS,
36
+ RESOURCE_REMEDIATION_MODE_SUMMARIES,
37
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/ResourceAiAgent/ResourceAiAccessSettingsUtil";
38
+ import {
39
+ getResourceAiAgentComposeSnippet,
40
+ getResourceAiAgentLogsCommand,
41
+ getResourceAiAgentServiceName,
42
+ getResourceAiAgentWriteAccessCommands,
43
+ getResourceAiAgentWriteDisclosure,
44
+ } from "../../../../App/FeatureSet/Dashboard/src/Components/ResourceAiAgent/ResourceAiAgentInstall";
45
+ import RouteMap from "../../../../App/FeatureSet/Dashboard/src/Utils/RouteMap";
46
+ import DockerHost from "../../../Models/DatabaseModels/DockerHost";
47
+ import Project from "../../../Models/DatabaseModels/Project";
48
+ import HTTPErrorResponse from "../../../Types/API/HTTPErrorResponse";
49
+ import HTTPResponse from "../../../Types/API/HTTPResponse";
50
+ import Route from "../../../Types/API/Route";
51
+ import ExceptionCode from "../../../Types/Exception/ExceptionCode";
52
+ import { JSONObject } from "../../../Types/JSON";
53
+ import ObjectID from "../../../Types/ObjectID";
54
+ import Permission from "../../../Types/Permission";
55
+ import AiResourceType, {
56
+ ALL_AI_RESOURCE_TYPES,
57
+ } from "../../../Types/ResourceAiAgent/AiResourceType";
58
+ import {
59
+ ResourceAiAccessGap,
60
+ ResourceAiAccessGapCode,
61
+ ResourceAiAccessStatus,
62
+ ResourceAiAgentPosture,
63
+ ResourceAiAgentSummary,
64
+ ResourceAiRemediationMode,
65
+ } from "../../../Types/ResourceAiAgent/ResourceAiAccess";
66
+ import API from "../../../UI/Utils/API/API";
67
+ import ModelAPI from "../../../UI/Utils/ModelAPI/ModelAPI";
68
+ import PermissionUtil from "../../../UI/Utils/Permission";
69
+ import User from "../../../UI/Utils/User";
70
+ import { goTo, PROJECT_ID } from "./SideMenuHarness";
71
+
72
+ jest.mock("react-i18next", () => {
73
+ return {
74
+ useTranslation: () => {
75
+ return {
76
+ t: (value: string): string => {
77
+ return value;
78
+ },
79
+ };
80
+ },
81
+ };
82
+ });
83
+
84
+ /*
85
+ * A resource's AI agent page (AI → AI agent) renders for real on its real
86
+ * route, with the status, test and reset-agent routes, the model API and
87
+ * the permission snapshot stubbed. One generic page serves every resource
88
+ * type through its descriptor; most cases run on a Docker host, and a
89
+ * table runs the page on each of the eight. Three cards at most: the
90
+ * agent's card in each of its states (connected, offline, unreachable, not
91
+ * installed — with the install instructions), "Needs attention" only when
92
+ * the server has gaps, and "What AI may do" with its Change modal — whose
93
+ * loosening rules, allowlist checks and confirmations mirror the server's.
94
+ */
95
+
96
+ // Real components fetch; give the waits room on a loaded CI box.
97
+ const WAIT_TIMEOUT: number = 20000;
98
+
99
+ const RESOURCE_ID: string = "44444444-0000-4000-8000-000000000004";
100
+ const AGENT_ID: string = "99999999-0000-4000-8000-000000000009";
101
+
102
+ const DOCKER: ResourceAiAgentDescriptor = getResourceAiAgentDescriptor(
103
+ AiResourceType.DockerHost,
104
+ );
105
+
106
+ const STATUS_ROUTE: string = "/resource-ai-access/status";
107
+ const TEST_ROUTE: string = "/resource-ai-access/test";
108
+ const RESET_ROUTE: string = "/resource-ai-access/reset-agent";
109
+
110
+ const SETTINGS_CARD_TITLE: string = "What AI may do";
111
+ const GAPS_CARD_TITLE: string = "Needs attention";
112
+
113
+ const BASE_PERMISSIONS: Array<Permission> = [
114
+ Permission.Public,
115
+ Permission.User,
116
+ Permission.CurrentUser,
117
+ ];
118
+
119
+ // May edit the resource, but not loosen AI access.
120
+ const MEMBER_PERMISSIONS: Array<Permission> = [
121
+ ...BASE_PERMISSIONS,
122
+ Permission.ProjectMember,
123
+ ];
124
+
125
+ // May loosen AI access, reset the agent and change project AI settings.
126
+ const ADMIN_PERMISSIONS: Array<Permission> = [
127
+ ...BASE_PERMISSIONS,
128
+ Permission.ProjectAdmin,
129
+ ];
130
+
131
+ const READER_PERMISSIONS: Array<Permission> = [
132
+ ...BASE_PERMISSIONS,
133
+ Permission.ReadDockerHost,
134
+ ];
135
+
136
+ function minutesAgo(minutes: number): string {
137
+ return new Date(Date.now() - minutes * 60 * 1000).toISOString();
138
+ }
139
+
140
+ function makePosture(
141
+ overrides: Partial<ResourceAiAgentPosture> = {},
142
+ ): ResourceAiAgentPosture {
143
+ return {
144
+ resourceType: AiResourceType.DockerHost,
145
+ resourceIdentifier: "prod-docker-01",
146
+ agentVersion: "14.1.0",
147
+ allowWrites: false,
148
+ writeTargets: [],
149
+ protectedTargets: [],
150
+ toolVersion: "27.3.1",
151
+ reachable: true,
152
+ details: {},
153
+ ...overrides,
154
+ };
155
+ }
156
+
157
+ function makeAgent(
158
+ overrides: Partial<ResourceAiAgentSummary> = {},
159
+ ): ResourceAiAgentSummary {
160
+ return {
161
+ agentId: AGENT_ID,
162
+ connectionStatus: "connected",
163
+ isOnline: true,
164
+ agentVersion: "14.1.0",
165
+ lastAliveAt: new Date().toISOString(),
166
+ lastRegisteredAt: minutesAgo(60),
167
+ posture: makePosture(),
168
+ ...overrides,
169
+ };
170
+ }
171
+
172
+ function gap(code: ResourceAiAccessGapCode): ResourceAiAccessGap {
173
+ return {
174
+ code,
175
+ title: `Title of ${code}`,
176
+ nextStep: `Next step for ${code}`,
177
+ blocksInvestigation: true,
178
+ blocksRemediation: true,
179
+ };
180
+ }
181
+
182
+ function makeStatus(
183
+ overrides: Partial<ResourceAiAccessStatus> = {},
184
+ ): ResourceAiAccessStatus {
185
+ return {
186
+ resourceType: AiResourceType.DockerHost,
187
+ resourceId: RESOURCE_ID,
188
+ resourceName: "prod-docker-01",
189
+ isAiInvestigationEnabled: true,
190
+ aiRemediationMode: ResourceAiRemediationMode.Disabled,
191
+ aiCommandAllowlist: [],
192
+ agent: makeAgent(),
193
+ gaps: [gap("remediation_disabled")],
194
+ isInvestigationReady: true,
195
+ isRemediationReady: false,
196
+ ...overrides,
197
+ };
198
+ }
199
+
200
+ function notInstalledStatus(): ResourceAiAccessStatus {
201
+ return makeStatus({
202
+ agent: null,
203
+ isInvestigationReady: false,
204
+ gaps: [gap("ai_agent_not_connected")],
205
+ });
206
+ }
207
+
208
+ function statusResponse(
209
+ status: ResourceAiAccessStatus,
210
+ ): HTTPResponse<JSONObject> {
211
+ return new HTTPResponse<JSONObject>(200, status as unknown as JSONObject, {});
212
+ }
213
+
214
+ let postSpy: ReturnType<typeof jest.spyOn>;
215
+ let getItemSpy: ReturnType<typeof jest.spyOn>;
216
+ let updateByIdSpy: ReturnType<typeof jest.spyOn>;
217
+ let savedModel: JSONObject = {};
218
+
219
+ type RouteAnswer = () => Promise<HTTPResponse<JSONObject> | HTTPErrorResponse>;
220
+
221
+ /*
222
+ * Answers the status route with `status`, and the test and reset routes
223
+ * with their handlers (a plain 200 by default).
224
+ */
225
+ function serve(
226
+ status: ResourceAiAccessStatus,
227
+ handlers: { test?: RouteAnswer; reset?: RouteAnswer } = {},
228
+ ): void {
229
+ postSpy.mockImplementation(
230
+ async (
231
+ request: unknown,
232
+ ): Promise<HTTPResponse<JSONObject> | HTTPErrorResponse> => {
233
+ const url: string = String((request as JSONObject)["url"]);
234
+ if (url.endsWith(TEST_ROUTE) && handlers.test) {
235
+ return await handlers.test();
236
+ }
237
+ if (url.endsWith(RESET_ROUTE)) {
238
+ return handlers.reset
239
+ ? await handlers.reset()
240
+ : new HTTPResponse<JSONObject>(200, { ok: true }, {});
241
+ }
242
+ return statusResponse(status);
243
+ },
244
+ );
245
+ }
246
+
247
+ async function failStatusRequest(): Promise<HTTPResponse<JSONObject>> {
248
+ throw new Error("Network Error");
249
+ }
250
+
251
+ // The resource row the modal (and the install instructions) read.
252
+ function serveModel(overrides: JSONObject = {}): void {
253
+ savedModel = {
254
+ _id: RESOURCE_ID,
255
+ hostIdentifier: "prod-docker-01",
256
+ name: "prod-docker-01",
257
+ isAiInvestigationEnabled: true,
258
+ aiRemediationMode: ResourceAiRemediationMode.RequireApproval,
259
+ aiCommandAllowlist: [],
260
+ ...overrides,
261
+ };
262
+ }
263
+
264
+ function postsTo(route: string): Array<JSONObject> {
265
+ return postSpy.mock.calls
266
+ .map((call: Array<unknown>): JSONObject => {
267
+ return (call[0] || {}) as JSONObject;
268
+ })
269
+ .filter((request: JSONObject): boolean => {
270
+ return String(request["url"]).endsWith(route);
271
+ });
272
+ }
273
+
274
+ function updateRequests(): Array<JSONObject> {
275
+ return updateByIdSpy.mock.calls.map((call: Array<unknown>): JSONObject => {
276
+ return (call[0] || {}) as JSONObject;
277
+ });
278
+ }
279
+
280
+ function grant(permissions: Array<Permission>): void {
281
+ jest.spyOn(User, "isMasterAdmin").mockReturnValue(false);
282
+ jest.spyOn(PermissionUtil, "getAllPermissions").mockReturnValue(permissions);
283
+ jest.spyOn(PermissionUtil, "getGlobalPermissions").mockReturnValue(null);
284
+ jest.spyOn(PermissionUtil, "getProjectPermissions").mockReturnValue({
285
+ projectId: new ObjectID(PROJECT_ID),
286
+ userId: ObjectID.generate(),
287
+ permissions: permissions.map((permission: Permission) => {
288
+ return {
289
+ permission: permission,
290
+ labelIds: [],
291
+ _type: "UserPermission",
292
+ };
293
+ }),
294
+ _type: "UserTenantAccessPermission",
295
+ } as unknown as ReturnType<typeof PermissionUtil.getProjectPermissions>);
296
+ }
297
+
298
+ function pagePath(descriptor: ResourceAiAgentDescriptor): string {
299
+ return RouteMap[descriptor.agentPage]!.toString()
300
+ .replace(":projectId", PROJECT_ID)
301
+ .replace(":id", RESOURCE_ID);
302
+ }
303
+
304
+ function openAgentPage(descriptor: ResourceAiAgentDescriptor = DOCKER): void {
305
+ const path: string = pagePath(descriptor);
306
+ goTo(path);
307
+
308
+ render(
309
+ <MemoryRouter initialEntries={[path]}>
310
+ <Routes>
311
+ <PageRoute
312
+ path={String(RouteMap[descriptor.agentPage])}
313
+ element={
314
+ <ResourceAiAgentPage
315
+ descriptor={descriptor}
316
+ pageRoute={RouteMap[descriptor.agentPage] as Route}
317
+ currentProject={Object.assign(new Project(), { name: "Acme" })}
318
+ hasPaymentMethod={true}
319
+ />
320
+ }
321
+ />
322
+ </Routes>
323
+ </MemoryRouter>,
324
+ );
325
+ }
326
+
327
+ async function findText(text: string | RegExp): Promise<HTMLElement> {
328
+ return await screen.findByText(text, {}, { timeout: WAIT_TIMEOUT });
329
+ }
330
+
331
+ async function findTestId(testId: string): Promise<HTMLElement> {
332
+ return await screen.findByTestId(testId, {}, { timeout: WAIT_TIMEOUT });
333
+ }
334
+
335
+ // The command a CodeBlock inside the element shows, exactly.
336
+ function codeIn(element: HTMLElement): string {
337
+ return element.querySelector("code")?.textContent || "";
338
+ }
339
+
340
+ async function openChangeModal(
341
+ descriptor: ResourceAiAgentDescriptor = DOCKER,
342
+ ): Promise<HTMLElement> {
343
+ fireEvent.click(await findTestId("ai-access-change-button"));
344
+ const dialog: HTMLElement = await screen.findByRole(
345
+ "dialog",
346
+ {},
347
+ { timeout: WAIT_TIMEOUT },
348
+ );
349
+ // The form is on screen once its first field label is.
350
+ await within(dialog).findByText(
351
+ descriptor.investigateTitle,
352
+ {},
353
+ { timeout: WAIT_TIMEOUT },
354
+ );
355
+ return dialog;
356
+ }
357
+
358
+ // The open react-select menu's options (not native <option>s).
359
+ function menuOptions(): Array<HTMLElement> {
360
+ return screen
361
+ .queryAllByRole("option")
362
+ .filter((option: HTMLElement): boolean => {
363
+ return option.tagName !== "OPTION";
364
+ });
365
+ }
366
+
367
+ async function openDropdown(
368
+ dialog: HTMLElement,
369
+ name: RegExp,
370
+ ): Promise<Array<string>> {
371
+ const combobox: HTMLElement = within(dialog).getByRole("combobox", {
372
+ name,
373
+ });
374
+ fireEvent.keyDown(combobox, { key: "ArrowDown", code: "ArrowDown" });
375
+ await waitFor(
376
+ () => {
377
+ expect(menuOptions().length).toBeGreaterThan(0);
378
+ },
379
+ { timeout: WAIT_TIMEOUT },
380
+ );
381
+ return menuOptions().map((option: HTMLElement): string => {
382
+ return option.textContent || "";
383
+ });
384
+ }
385
+
386
+ async function pickOption(
387
+ dialog: HTMLElement,
388
+ name: RegExp,
389
+ optionText: string,
390
+ ): Promise<void> {
391
+ await openDropdown(dialog, name);
392
+ const option: HTMLElement | undefined = menuOptions().find(
393
+ (candidate: HTMLElement): boolean => {
394
+ return candidate.textContent === optionText;
395
+ },
396
+ );
397
+ if (!option) {
398
+ throw new Error(`No option "${optionText}" in the ${name} dropdown.`);
399
+ }
400
+ fireEvent.click(option);
401
+ }
402
+
403
+ async function toggleSwitch(
404
+ dialog: HTMLElement,
405
+ testId: string,
406
+ from: boolean,
407
+ ): Promise<void> {
408
+ const toggle: HTMLElement = within(dialog).getByTestId(testId);
409
+ await waitFor(
410
+ () => {
411
+ expect(toggle).toHaveAttribute("aria-checked", String(from));
412
+ },
413
+ { timeout: WAIT_TIMEOUT },
414
+ );
415
+ fireEvent.click(toggle);
416
+ expect(toggle).toHaveAttribute("aria-checked", String(!from));
417
+ }
418
+
419
+ async function setAllowlistText(
420
+ dialog: HTMLElement,
421
+ value: string,
422
+ ): Promise<void> {
423
+ const field: HTMLElement = await within(dialog).findByTestId(
424
+ "ai-command-allowlist-field",
425
+ {},
426
+ { timeout: WAIT_TIMEOUT },
427
+ );
428
+ fireEvent.change(field, { target: { value } });
429
+ }
430
+
431
+ async function waitForOneUpdate(): Promise<JSONObject> {
432
+ await waitFor(
433
+ () => {
434
+ expect(updateByIdSpy).toHaveBeenCalledTimes(1);
435
+ },
436
+ { timeout: WAIT_TIMEOUT },
437
+ );
438
+ return updateRequests()[0]!["data"] as JSONObject;
439
+ }
440
+
441
+ function saveChangeModal(dialog: HTMLElement): void {
442
+ fireEvent.click(within(dialog).getByTestId("modal-footer-submit-button"));
443
+ }
444
+
445
+ async function findDialogTitled(title: string): Promise<HTMLElement> {
446
+ const heading: HTMLElement = await findText(title);
447
+ const dialog: HTMLElement | null = heading.closest('[role="dialog"]');
448
+ if (!dialog) {
449
+ throw new Error(`"${title}" is not inside a dialog.`);
450
+ }
451
+ return dialog;
452
+ }
453
+
454
+ beforeEach(() => {
455
+ window.localStorage.clear();
456
+ window.sessionStorage.clear();
457
+ grant(MEMBER_PERMISSIONS);
458
+
459
+ postSpy = jest.spyOn(API, "post");
460
+ serve(makeStatus());
461
+
462
+ serveModel();
463
+ getItemSpy = jest.spyOn(ModelAPI, "getItem");
464
+ getItemSpy.mockImplementation(async (): Promise<unknown> => {
465
+ return Object.assign(new DockerHost(), savedModel);
466
+ });
467
+
468
+ updateByIdSpy = jest.spyOn(ModelAPI, "updateById");
469
+ updateByIdSpy.mockImplementation(
470
+ async (): Promise<HTTPResponse<JSONObject>> => {
471
+ return new HTTPResponse<JSONObject>(200, {}, {});
472
+ },
473
+ );
474
+ jest.spyOn(ModelAPI, "getCommonHeaders").mockReturnValue({});
475
+ });
476
+
477
+ afterEach(() => {
478
+ cleanup();
479
+ jest.useRealTimers();
480
+ jest.restoreAllMocks();
481
+ });
482
+
483
+ describe("the agent card", () => {
484
+ test("a connected agent: pill, sentence, meta line and the ready line", async () => {
485
+ openAgentPage();
486
+
487
+ expect(await findText("Docker AI agent")).toBeInTheDocument();
488
+ expect(await findTestId("ai-agent-status")).toHaveTextContent("Connected");
489
+ expect(screen.getByTestId("ai-agent-sentence")).toHaveTextContent(
490
+ "The Docker AI agent is running next to this Docker host.",
491
+ );
492
+ expect(screen.getByTestId("ai-agent-meta")).toHaveTextContent(
493
+ "agent v14.1.0 · Docker 27.3.1 · Read-only",
494
+ );
495
+ expect(screen.getByTestId("ai-agent-meta")).toHaveTextContent(
496
+ /^last seen /,
497
+ );
498
+ expect(screen.getByTestId("ai-agent-ready")).toHaveTextContent(
499
+ getResourceAiAgentReadyText(DOCKER),
500
+ );
501
+ expect(screen.queryByTestId("ai-agent-install")).not.toBeInTheDocument();
502
+ expect(
503
+ screen.queryByTestId("ai-agent-logs-command"),
504
+ ).not.toBeInTheDocument();
505
+ });
506
+
507
+ test("not installed: the service to add, pinned to this host's name, and nothing to test", async () => {
508
+ serve(notInstalledStatus());
509
+ openAgentPage();
510
+
511
+ expect(await findTestId("ai-agent-status")).toHaveTextContent(
512
+ "Not installed",
513
+ );
514
+ expect(screen.getByTestId("ai-agent-sentence")).toHaveTextContent(
515
+ getResourceAiAgentNotInstalledText(DOCKER),
516
+ );
517
+
518
+ // The name the collector reports, read from the host's row.
519
+ await waitFor(
520
+ () => {
521
+ expect(codeIn(screen.getByTestId("ai-agent-install-command"))).toBe(
522
+ getResourceAiAgentComposeSnippet({
523
+ resourceType: AiResourceType.DockerHost,
524
+ resourceId: RESOURCE_ID,
525
+ identity: "prod-docker-01",
526
+ }),
527
+ );
528
+ },
529
+ { timeout: WAIT_TIMEOUT },
530
+ );
531
+ const identityRequest: JSONObject = getItemSpy.mock
532
+ .calls[0]![0] as JSONObject;
533
+ expect(identityRequest["modelType"]).toBe(DockerHost);
534
+ expect(String(identityRequest["id"])).toBe(RESOURCE_ID);
535
+ expect(identityRequest["select"]).toEqual({
536
+ _id: true,
537
+ hostIdentifier: true,
538
+ });
539
+
540
+ // No installer creates a Docker agent directory: never `cd` into one.
541
+ expect(codeIn(screen.getByTestId("ai-agent-install-start"))).toBe(
542
+ "# In the directory of your docker-compose.yml:\ndocker compose up -d oneuptime-docker-ai-agent",
543
+ );
544
+ // install.sh, the usual way, comes first.
545
+ expect(screen.getByTestId("ai-agent-install-where")).toHaveTextContent(
546
+ /^Installed the Docker agent with install\.sh\? Run it again/,
547
+ );
548
+ expect(
549
+ screen.getByTestId("ai-agent-install-variable-DOCKER_HOST_NAME"),
550
+ ).toHaveTextContent("prod-docker-01");
551
+ expect(
552
+ screen.getByTestId("ai-agent-install-variable-ONEUPTIME_AI_ALLOW_WRITES"),
553
+ ).toHaveTextContent("false");
554
+ expect(
555
+ screen.getByTestId("ai-agent-install-prerequisites"),
556
+ ).toHaveTextContent("docker run");
557
+ expect(
558
+ screen.queryByTestId("ai-agent-test-button"),
559
+ ).not.toBeInTheDocument();
560
+ expect(screen.queryByTestId("ai-agent-ready")).not.toBeInTheDocument();
561
+ expect(screen.queryByTestId("ai-agent-meta")).not.toBeInTheDocument();
562
+ });
563
+
564
+ test("not installed, and the host's name cannot be read: the collector's own variable", async () => {
565
+ serve(notInstalledStatus());
566
+ getItemSpy.mockImplementation(async (): Promise<never> => {
567
+ throw new Error("no access");
568
+ });
569
+ openAgentPage();
570
+
571
+ expect(codeIn(await findTestId("ai-agent-install-command"))).toContain(
572
+ "DOCKER_HOST_NAME=${DOCKER_HOST_NAME:-docker-host}",
573
+ );
574
+ expect(
575
+ screen.getByTestId("ai-agent-install-variable-DOCKER_HOST_NAME"),
576
+ ).toHaveTextContent("from .env");
577
+ });
578
+
579
+ test("offline: the logs command for the agent's container", async () => {
580
+ serve(
581
+ makeStatus({
582
+ agent: makeAgent({ isOnline: false, lastAliveAt: minutesAgo(12) }),
583
+ }),
584
+ );
585
+ openAgentPage();
586
+
587
+ expect(await findTestId("ai-agent-status")).toHaveTextContent("Offline");
588
+ expect(screen.getByTestId("ai-agent-sentence")).toHaveTextContent(
589
+ getResourceAiAgentSilentText(DOCKER),
590
+ );
591
+ expect(codeIn(screen.getByTestId("ai-agent-logs-command"))).toBe(
592
+ getResourceAiAgentLogsCommand(AiResourceType.DockerHost),
593
+ );
594
+ expect(codeIn(screen.getByTestId("ai-agent-logs-command"))).toBe(
595
+ "docker logs --tail 100 oneuptime-docker-ai-agent",
596
+ );
597
+ });
598
+
599
+ test("offline right after a sign-off: says it reconnects", async () => {
600
+ serve(
601
+ makeStatus({
602
+ agent: makeAgent({
603
+ isOnline: false,
604
+ connectionStatus: "disconnected",
605
+ lastAliveAt: new Date().toISOString(),
606
+ }),
607
+ }),
608
+ );
609
+ openAgentPage();
610
+
611
+ expect(await findTestId("ai-agent-sentence")).toHaveTextContent(
612
+ getResourceAiAgentSignedOffText(DOCKER),
613
+ );
614
+ expect(screen.getByTestId("ai-agent-sentence")).not.toHaveTextContent(
615
+ "has not checked in",
616
+ );
617
+ });
618
+
619
+ test("online but unable to reach the host: why, the logs, and a test from the gap", async () => {
620
+ serve(
621
+ makeStatus({
622
+ agent: makeAgent({
623
+ posture: makePosture({
624
+ reachable: false,
625
+ reachError: "Cannot connect to the Docker daemon",
626
+ }),
627
+ }),
628
+ isInvestigationReady: false,
629
+ gaps: [gap("ai_agent_unreachable_resource")],
630
+ }),
631
+ {
632
+ test: async (): Promise<HTTPResponse<JSONObject>> => {
633
+ return new HTTPResponse<JSONObject>(
634
+ 200,
635
+ { ok: true, message: "It works.", results: [] },
636
+ {},
637
+ );
638
+ },
639
+ },
640
+ );
641
+ grant(ADMIN_PERMISSIONS);
642
+ openAgentPage();
643
+
644
+ expect(await findTestId("ai-agent-status")).toHaveTextContent("Connected");
645
+ expect(screen.getByTestId("ai-agent-sentence")).toHaveTextContent(
646
+ "could not reach this Docker host at its last check: Cannot connect to the Docker daemon",
647
+ );
648
+ expect(screen.getByTestId("ai-agent-logs-command")).toBeInTheDocument();
649
+
650
+ const row: HTMLElement = await findTestId(
651
+ "ai-agent-gap-ai_agent_unreachable_resource",
652
+ );
653
+ fireEvent.click(within(row).getByText("Test connection"));
654
+ expect(await findText("The connection works")).toBeInTheDocument();
655
+ });
656
+
657
+ test("warns when another agent tried to register for the host", async () => {
658
+ serve(
659
+ makeStatus({
660
+ agent: makeAgent({
661
+ lastRefusedRegistrationAt: new Date().toISOString(),
662
+ lastRefusedRegistrationReason: "previous_instance_online",
663
+ }),
664
+ }),
665
+ );
666
+ openAgentPage();
667
+
668
+ const warning: HTMLElement = await findTestId(
669
+ "ai-agent-refused-registration",
670
+ );
671
+ expect(warning).toHaveTextContent(
672
+ "Another agent tried to register for this Docker host at",
673
+ );
674
+ expect(warning).toHaveTextContent("DOCKER_HOST_NAME");
675
+ });
676
+
677
+ test("shows the last command error the server recorded", async () => {
678
+ serve(
679
+ makeStatus({
680
+ aiAccessLastError: "docker: permission denied",
681
+ aiAccessLastVerifiedAt: minutesAgo(90),
682
+ }),
683
+ );
684
+ openAgentPage();
685
+
686
+ const line: HTMLElement = await findTestId("ai-agent-last-error");
687
+ expect(line).toHaveTextContent("Last command error (last worked");
688
+ expect(line).toHaveTextContent("docker: permission denied");
689
+ });
690
+ });
691
+
692
+ describe("the page heading", () => {
693
+ test("names the page and the resource", async () => {
694
+ openAgentPage();
695
+
696
+ const heading: HTMLElement = await screen.findByRole(
697
+ "heading",
698
+ { level: 2, name: "AI agent" },
699
+ { timeout: WAIT_TIMEOUT },
700
+ );
701
+ expect(heading).toBeInTheDocument();
702
+ expect(screen.getByTestId("ai-agent-page-heading")).toHaveTextContent(
703
+ getResourceAiAgentPageSubtitle(DOCKER),
704
+ );
705
+ });
706
+
707
+ test("is shown while the status loads, and above the error when it fails", async () => {
708
+ let answer: (value: HTTPResponse<JSONObject>) => void = () => {
709
+ return undefined;
710
+ };
711
+ postSpy.mockImplementation((): Promise<HTTPResponse<JSONObject>> => {
712
+ return new Promise<HTTPResponse<JSONObject>>(
713
+ (resolve: (value: HTTPResponse<JSONObject>) => void) => {
714
+ answer = resolve;
715
+ },
716
+ );
717
+ });
718
+ openAgentPage();
719
+
720
+ expect(screen.getByTestId("ai-agent-page-heading")).toHaveTextContent(
721
+ "AI agent",
722
+ );
723
+ expect(screen.queryByText("Docker AI agent")).not.toBeInTheDocument();
724
+
725
+ await act(async () => {
726
+ answer(statusResponse(makeStatus()));
727
+ });
728
+ expect(await findText("Docker AI agent")).toBeInTheDocument();
729
+ cleanup();
730
+
731
+ postSpy.mockImplementation(failStatusRequest);
732
+ openAgentPage();
733
+ expect(await findText("Network Error")).toBeInTheDocument();
734
+ expect(screen.getByTestId("ai-agent-page-heading")).toBeInTheDocument();
735
+ expect(screen.queryByText("Docker AI agent")).not.toBeInTheDocument();
736
+ });
737
+ });
738
+
739
+ describe("status polling", () => {
740
+ test("asks about this resource once on load, by type and id", async () => {
741
+ openAgentPage();
742
+
743
+ expect(await findText(SETTINGS_CARD_TITLE)).toBeInTheDocument();
744
+ expect(postsTo(STATUS_ROUTE)).toHaveLength(1);
745
+ expect(postsTo(STATUS_ROUTE)[0]!["data"]).toEqual({
746
+ resourceType: AiResourceType.DockerHost,
747
+ resourceId: RESOURCE_ID,
748
+ });
749
+ });
750
+
751
+ test("keeps the last good status and warns inline when a background poll fails", async () => {
752
+ jest.useFakeTimers();
753
+ postSpy
754
+ .mockResolvedValueOnce(statusResponse(makeStatus()))
755
+ .mockImplementationOnce(failStatusRequest)
756
+ .mockResolvedValue(statusResponse(makeStatus()));
757
+
758
+ openAgentPage();
759
+ expect(await findText("Docker AI agent")).toBeInTheDocument();
760
+
761
+ await act(async () => {
762
+ jest.advanceTimersByTime(RESOURCE_AI_AGENT_STATUS_POLL_INTERVAL_MS);
763
+ });
764
+
765
+ const warning: HTMLElement = await findTestId("ai-access-refresh-warning");
766
+ expect(warning).toHaveTextContent("Could not refresh the AI agent status");
767
+ expect(warning).toHaveTextContent("Network Error");
768
+ expect(screen.getByTestId("ai-agent-status")).toHaveTextContent(
769
+ "Connected",
770
+ );
771
+
772
+ await act(async () => {
773
+ jest.advanceTimersByTime(RESOURCE_AI_AGENT_STATUS_POLL_INTERVAL_MS);
774
+ });
775
+
776
+ await waitFor(
777
+ () => {
778
+ expect(
779
+ screen.queryByTestId("ai-access-refresh-warning"),
780
+ ).not.toBeInTheDocument();
781
+ },
782
+ { timeout: WAIT_TIMEOUT },
783
+ );
784
+ });
785
+
786
+ test("picks up an agent that connects while the page is open", async () => {
787
+ jest.useFakeTimers();
788
+ postSpy
789
+ .mockResolvedValueOnce(statusResponse(notInstalledStatus()))
790
+ .mockResolvedValue(statusResponse(makeStatus()));
791
+
792
+ openAgentPage();
793
+ expect(await findTestId("ai-agent-status")).toHaveTextContent(
794
+ "Not installed",
795
+ );
796
+
797
+ await act(async () => {
798
+ jest.advanceTimersByTime(RESOURCE_AI_AGENT_STATUS_POLL_INTERVAL_MS);
799
+ });
800
+
801
+ await waitFor(
802
+ () => {
803
+ expect(screen.getByTestId("ai-agent-status")).toHaveTextContent(
804
+ "Connected",
805
+ );
806
+ },
807
+ { timeout: WAIT_TIMEOUT },
808
+ );
809
+ expect(screen.queryByTestId("ai-agent-install")).not.toBeInTheDocument();
810
+ });
811
+
812
+ test("keeps the page when a poll returns a status it cannot read", async () => {
813
+ jest.useFakeTimers();
814
+ postSpy
815
+ .mockResolvedValueOnce(statusResponse(makeStatus()))
816
+ .mockResolvedValue(
817
+ new HTTPResponse<JSONObject>(200, { unexpected: true }, {}),
818
+ );
819
+
820
+ openAgentPage();
821
+ expect(await findText("Docker AI agent")).toBeInTheDocument();
822
+
823
+ await act(async () => {
824
+ jest.advanceTimersByTime(RESOURCE_AI_AGENT_STATUS_POLL_INTERVAL_MS);
825
+ });
826
+
827
+ expect(await findTestId("ai-access-refresh-warning")).toHaveTextContent(
828
+ "cannot read",
829
+ );
830
+ expect(screen.getByText("Docker AI agent")).toBeInTheDocument();
831
+ });
832
+ });
833
+
834
+ describe("Needs attention", () => {
835
+ test("is not shown when the only gap is the fixes-off choice", async () => {
836
+ openAgentPage();
837
+
838
+ expect(await findText(SETTINGS_CARD_TITLE)).toBeInTheDocument();
839
+ expect(screen.queryByText(GAPS_CARD_TITLE)).not.toBeInTheDocument();
840
+ });
841
+
842
+ test("lists one row per server gap with its title and next step", async () => {
843
+ serve(
844
+ makeStatus({
845
+ isInvestigationReady: false,
846
+ gaps: [
847
+ gap("ai_balance_insufficient"),
848
+ gap("remediation_disabled"),
849
+ gap("llm_provider_missing"),
850
+ ],
851
+ }),
852
+ );
853
+ openAgentPage();
854
+
855
+ const list: HTMLElement = await findTestId("ai-agent-gaps");
856
+ const rows: Array<HTMLElement> = within(list).getAllByRole("listitem");
857
+ expect(rows).toHaveLength(2);
858
+ expect(rows[0]).toHaveTextContent("Title of ai_balance_insufficient");
859
+ expect(rows[0]).toHaveTextContent("Next step for ai_balance_insufficient");
860
+ expect(rows[1]).toHaveTextContent("Title of llm_provider_missing");
861
+ expect(screen.getByText(GAPS_CARD_TITLE)).toBeInTheDocument();
862
+ expect(screen.queryByTestId("ai-agent-ready")).not.toBeInTheDocument();
863
+ });
864
+
865
+ test("an editor turns investigation on from its row", async () => {
866
+ serve(
867
+ makeStatus({
868
+ isAiInvestigationEnabled: false,
869
+ isInvestigationReady: false,
870
+ gaps: [gap("investigation_disabled")],
871
+ }),
872
+ );
873
+ openAgentPage();
874
+
875
+ fireEvent.click(await findTestId("ai-agent-gap-turn-on-investigation"));
876
+
877
+ expect(await waitForOneUpdate()).toEqual({
878
+ isAiInvestigationEnabled: true,
879
+ });
880
+ expect(String(updateRequests()[0]!["id"])).toBe(RESOURCE_ID);
881
+ expect(updateRequests()[0]!["modelType"]).toBe(DockerHost);
882
+ expect(await findTestId("ai-agent-action-notice")).toHaveTextContent(
883
+ "AI may now investigate this Docker host with read-only docker commands.",
884
+ );
885
+ await waitFor(
886
+ () => {
887
+ expect(postsTo(STATUS_ROUTE).length).toBeGreaterThanOrEqual(2);
888
+ },
889
+ { timeout: WAIT_TIMEOUT },
890
+ );
891
+ });
892
+
893
+ test("a failed one-click fix is shown on the page", async () => {
894
+ serve(
895
+ makeStatus({
896
+ isAiInvestigationEnabled: false,
897
+ isInvestigationReady: false,
898
+ gaps: [gap("investigation_disabled")],
899
+ }),
900
+ );
901
+ updateByIdSpy.mockImplementation(async (): Promise<never> => {
902
+ throw new HTTPErrorResponse(422, { error: "Not allowed." }, {});
903
+ });
904
+ openAgentPage();
905
+
906
+ fireEvent.click(await findTestId("ai-agent-gap-turn-on-investigation"));
907
+
908
+ expect(await findTestId("ai-agent-action-error")).toHaveTextContent(
909
+ "Not allowed.",
910
+ );
911
+ });
912
+
913
+ test("a reader is told who to ask instead of getting a button", async () => {
914
+ grant(READER_PERMISSIONS);
915
+ serve(
916
+ makeStatus({
917
+ isAiInvestigationEnabled: false,
918
+ isInvestigationReady: false,
919
+ gaps: [gap("investigation_disabled")],
920
+ }),
921
+ );
922
+ openAgentPage();
923
+
924
+ const row: HTMLElement = await findTestId(
925
+ "ai-agent-gap-investigation_disabled",
926
+ );
927
+ expect(within(row).getByTestId("ai-agent-gap-ask")).toHaveTextContent(
928
+ "Ask a project owner or admin.",
929
+ );
930
+ expect(
931
+ screen.queryByTestId("ai-agent-gap-turn-on-investigation"),
932
+ ).not.toBeInTheDocument();
933
+ });
934
+
935
+ test("project-level gaps link an admin to the page that fixes them", async () => {
936
+ grant(ADMIN_PERMISSIONS);
937
+ serve(
938
+ makeStatus({
939
+ isInvestigationReady: false,
940
+ gaps: [
941
+ gap("ai_disabled_for_project"),
942
+ gap("llm_provider_missing"),
943
+ gap("ai_balance_insufficient"),
944
+ ],
945
+ }),
946
+ );
947
+ openAgentPage();
948
+
949
+ const expected: Array<[string, string, string]> = [
950
+ ["ai_disabled_for_project", "Open AI Features", "settings/ai-features"],
951
+ ["llm_provider_missing", "Open LLM Providers", "settings/llm-providers"],
952
+ ["ai_balance_insufficient", "Open AI Credits", "settings/ai-credits"],
953
+ ];
954
+ for (const [code, linkText, path] of expected) {
955
+ const row: HTMLElement = await findTestId(`ai-agent-gap-${code}`);
956
+ expect(
957
+ within(row).getByText(linkText).closest("a")?.getAttribute("href"),
958
+ ).toBe(`/dashboard/${PROJECT_ID}/${path}`);
959
+ }
960
+ });
961
+
962
+ test("project-level gaps tell a member who to ask", async () => {
963
+ serve(
964
+ makeStatus({
965
+ isInvestigationReady: false,
966
+ gaps: [gap("auto_remediation_disabled_for_project")],
967
+ }),
968
+ );
969
+ openAgentPage();
970
+
971
+ const row: HTMLElement = await findTestId(
972
+ "ai-agent-gap-auto_remediation_disabled_for_project",
973
+ );
974
+ expect(row).toHaveTextContent("Ask a project owner or admin.");
975
+ expect(within(row).queryByText("Open AI Features")).not.toBeInTheDocument();
976
+ });
977
+
978
+ test("the not-installed gap has no extra action: the instructions are on the card", async () => {
979
+ serve(notInstalledStatus());
980
+ openAgentPage();
981
+
982
+ const row: HTMLElement = await findTestId(
983
+ "ai-agent-gap-ai_agent_not_connected",
984
+ );
985
+ expect(within(row).queryByRole("button")).not.toBeInTheDocument();
986
+ expect(within(row).queryByRole("link")).not.toBeInTheDocument();
987
+ });
988
+ });
989
+
990
+ describe("What AI may do", () => {
991
+ test("shows investigation and fixes in plain words, with the fixes-off hint", async () => {
992
+ openAgentPage();
993
+
994
+ expect(await findTestId("ai-access-investigation-value")).toHaveTextContent(
995
+ "Yes — read-only (ps, inspect, logs, stats, events)",
996
+ );
997
+ expect(screen.getByText(DOCKER.investigateTitle)).toBeInTheDocument();
998
+ expect(screen.getByTestId("ai-access-fixes-value")).toHaveTextContent(
999
+ `Off — ${RESOURCE_REMEDIATION_MODE_SUMMARIES[ResourceAiRemediationMode.Disabled]}`,
1000
+ );
1001
+ expect(screen.getByTestId("ai-access-fixes-off-hint")).toHaveTextContent(
1002
+ "Want AI to propose fixes? Choose Ask for approval.",
1003
+ );
1004
+ expect(
1005
+ screen.queryByTestId("ai-access-write-commands"),
1006
+ ).not.toBeInTheDocument();
1007
+ expect(
1008
+ screen.queryByTestId("ai-command-allowlist-in-effect"),
1009
+ ).not.toBeInTheDocument();
1010
+ });
1011
+
1012
+ test("investigation off says so", async () => {
1013
+ serve(
1014
+ makeStatus({
1015
+ isAiInvestigationEnabled: false,
1016
+ isInvestigationReady: false,
1017
+ }),
1018
+ );
1019
+ openAgentPage();
1020
+
1021
+ expect(await findTestId("ai-access-investigation-value")).toHaveTextContent(
1022
+ "No — AI investigates with OneUptime data only",
1023
+ );
1024
+ });
1025
+
1026
+ test("fixes on with a read-only agent: the write switch, scoped first, and the disclosure", async () => {
1027
+ serve(
1028
+ makeStatus({
1029
+ aiRemediationMode: ResourceAiRemediationMode.RequireApproval,
1030
+ gaps: [gap("remediation_write_access_missing")],
1031
+ }),
1032
+ );
1033
+ openAgentPage();
1034
+
1035
+ const section: HTMLElement = await findTestId("ai-access-write-commands");
1036
+ const commands: ReturnType<typeof getResourceAiAgentWriteAccessCommands> =
1037
+ getResourceAiAgentWriteAccessCommands(AiResourceType.DockerHost);
1038
+ expect(section).toHaveTextContent("Give the agent write access");
1039
+ expect(codeIn(screen.getByTestId("ai-access-write-scoped-env"))).toBe(
1040
+ commands.scopedEnv,
1041
+ );
1042
+ expect(codeIn(screen.getByTestId("ai-access-write-scoped-env"))).toBe(
1043
+ "ONEUPTIME_AI_ALLOW_WRITES=true\nONEUPTIME_AI_WRITE_TARGETS=web-*,api-*",
1044
+ );
1045
+ expect(codeIn(screen.getByTestId("ai-access-write-all-env"))).toBe(
1046
+ "ONEUPTIME_AI_ALLOW_WRITES=true",
1047
+ );
1048
+ expect(codeIn(screen.getByTestId("ai-access-write-restart-command"))).toBe(
1049
+ commands.restartCommand,
1050
+ );
1051
+ expect(
1052
+ codeIn(screen.getByTestId("ai-access-write-restart-command")),
1053
+ ).not.toContain("cd /opt/");
1054
+ const installerNote: HTMLElement = screen.getByTestId(
1055
+ "ai-access-write-installer-note",
1056
+ );
1057
+ const envIntro: HTMLElement = screen.getByTestId(
1058
+ "ai-access-write-env-intro",
1059
+ );
1060
+ expect(installerNote).toHaveTextContent(commands.installerNote!);
1061
+ expect(envIntro).toHaveTextContent(commands.envIntro);
1062
+ // install.sh, the usual way, before the Compose way.
1063
+ expect(
1064
+ installerNote.compareDocumentPosition(envIntro) &
1065
+ Node.DOCUMENT_POSITION_FOLLOWING,
1066
+ ).toBeTruthy();
1067
+ expect(
1068
+ installerNote.compareDocumentPosition(
1069
+ screen.getByTestId("ai-access-write-scoped-env"),
1070
+ ) & Node.DOCUMENT_POSITION_FOLLOWING,
1071
+ ).toBeTruthy();
1072
+ expect(screen.getByTestId("ai-access-write-disclosure")).toHaveTextContent(
1073
+ getResourceAiAgentWriteDisclosure(AiResourceType.DockerHost),
1074
+ );
1075
+ expect(
1076
+ screen.queryByTestId("ai-access-fixes-off-hint"),
1077
+ ).not.toBeInTheDocument();
1078
+ });
1079
+
1080
+ test("a database server has no targets to scope: just the switch", async () => {
1081
+ const database: ResourceAiAgentDescriptor = getResourceAiAgentDescriptor(
1082
+ AiResourceType.DatabaseServer,
1083
+ );
1084
+ serve(
1085
+ makeStatus({
1086
+ resourceType: AiResourceType.DatabaseServer,
1087
+ aiRemediationMode: ResourceAiRemediationMode.RequireApproval,
1088
+ agent: makeAgent({
1089
+ posture: makePosture({ resourceType: AiResourceType.DatabaseServer }),
1090
+ }),
1091
+ }),
1092
+ );
1093
+ openAgentPage(database);
1094
+
1095
+ await findTestId("ai-access-write-commands");
1096
+ expect(
1097
+ screen.queryByTestId("ai-access-write-scoped-env"),
1098
+ ).not.toBeInTheDocument();
1099
+ expect(codeIn(screen.getByTestId("ai-access-write-all-env"))).toBe(
1100
+ "ONEUPTIME_AI_ALLOW_WRITES=true",
1101
+ );
1102
+ expect(
1103
+ screen.queryByTestId("ai-access-write-installer-note"),
1104
+ ).not.toBeInTheDocument();
1105
+ });
1106
+
1107
+ test("an agent that already writes gets no instructions, and says where it may change", async () => {
1108
+ serve(
1109
+ makeStatus({
1110
+ aiRemediationMode: ResourceAiRemediationMode.RequireApproval,
1111
+ agent: makeAgent({
1112
+ posture: makePosture({
1113
+ allowWrites: true,
1114
+ writeTargets: ["web-*", "api"],
1115
+ }),
1116
+ }),
1117
+ }),
1118
+ );
1119
+ openAgentPage();
1120
+
1121
+ expect(await findTestId("ai-agent-meta")).toHaveTextContent(
1122
+ "Can change: web-*, api",
1123
+ );
1124
+ expect(
1125
+ screen.queryByTestId("ai-access-write-commands"),
1126
+ ).not.toBeInTheDocument();
1127
+ });
1128
+
1129
+ test("the allowlist in effect is listed in Automatic mode only", async () => {
1130
+ serve(
1131
+ makeStatus({
1132
+ aiRemediationMode: ResourceAiRemediationMode.Automatic,
1133
+ aiCommandAllowlist: ["docker stop web"],
1134
+ }),
1135
+ );
1136
+ openAgentPage();
1137
+
1138
+ expect(
1139
+ await findTestId("ai-command-allowlist-in-effect"),
1140
+ ).toHaveTextContent("docker stop web");
1141
+ cleanup();
1142
+
1143
+ serve(
1144
+ makeStatus({
1145
+ aiRemediationMode: ResourceAiRemediationMode.BypassApproval,
1146
+ aiCommandAllowlist: ["docker stop web"],
1147
+ }),
1148
+ );
1149
+ openAgentPage();
1150
+ expect(await findTestId("ai-access-fixes-value")).toHaveTextContent(
1151
+ "Bypass approval",
1152
+ );
1153
+ expect(
1154
+ screen.queryByTestId("ai-command-allowlist-in-effect"),
1155
+ ).not.toBeInTheDocument();
1156
+ });
1157
+
1158
+ test("an empty allowlist in Automatic mode reads None", async () => {
1159
+ serve(
1160
+ makeStatus({ aiRemediationMode: ResourceAiRemediationMode.Automatic }),
1161
+ );
1162
+ openAgentPage();
1163
+
1164
+ expect(
1165
+ await findTestId("ai-command-allowlist-in-effect"),
1166
+ ).toHaveTextContent("None");
1167
+ });
1168
+
1169
+ test("the Change button is locked, with the reason, for a reader", async () => {
1170
+ grant(READER_PERMISSIONS);
1171
+ openAgentPage();
1172
+
1173
+ const button: HTMLElement = await findTestId("ai-access-change-button");
1174
+ expect(button).toBeDisabled();
1175
+ fireEvent.click(button);
1176
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
1177
+ });
1178
+ });
1179
+
1180
+ describe("the Change modal: who may loosen", () => {
1181
+ test("reads the saved settings of this resource, and nothing else", async () => {
1182
+ openAgentPage();
1183
+ await openChangeModal();
1184
+
1185
+ const request: JSONObject = getItemSpy.mock.calls[0]![0] as JSONObject;
1186
+ expect(request["modelType"]).toBe(DockerHost);
1187
+ expect(String(request["id"])).toBe(RESOURCE_ID);
1188
+ expect(request["select"]).toEqual({
1189
+ _id: true,
1190
+ isAiInvestigationEnabled: true,
1191
+ aiRemediationMode: true,
1192
+ aiCommandAllowlist: true,
1193
+ });
1194
+ });
1195
+
1196
+ test("a member on an Ask-for-approval host is offered only Off and the current mode, and told why", async () => {
1197
+ openAgentPage();
1198
+ const dialog: HTMLElement = await openChangeModal();
1199
+
1200
+ expect(
1201
+ within(dialog).getByTestId("resource-ai-access-admin-note"),
1202
+ ).toHaveTextContent("Edit Auto Remediation Rule");
1203
+ // Nothing saved to remove: no allowlist field.
1204
+ expect(
1205
+ within(dialog).queryByTestId("ai-command-allowlist-field"),
1206
+ ).not.toBeInTheDocument();
1207
+
1208
+ expect(await openDropdown(dialog, /^Fixes/)).toEqual([
1209
+ RESOURCE_REMEDIATION_MODE_LABELS[ResourceAiRemediationMode.Disabled],
1210
+ `${RESOURCE_REMEDIATION_MODE_LABELS[ResourceAiRemediationMode.RequireApproval]} (current)`,
1211
+ ]);
1212
+ });
1213
+
1214
+ test("a member on an Off host cannot turn fixes on", async () => {
1215
+ serveModel({ aiRemediationMode: ResourceAiRemediationMode.Disabled });
1216
+ openAgentPage();
1217
+ const dialog: HTMLElement = await openChangeModal();
1218
+
1219
+ expect(await openDropdown(dialog, /^Fixes/)).toEqual([
1220
+ RESOURCE_REMEDIATION_MODE_LABELS[ResourceAiRemediationMode.Disabled],
1221
+ ]);
1222
+ });
1223
+
1224
+ test("an admin is offered every mode, and turns fixes on from Off", async () => {
1225
+ grant(ADMIN_PERMISSIONS);
1226
+ serveModel({ aiRemediationMode: ResourceAiRemediationMode.Disabled });
1227
+ openAgentPage();
1228
+ const dialog: HTMLElement = await openChangeModal();
1229
+
1230
+ expect(
1231
+ within(dialog).queryByTestId("resource-ai-access-admin-note"),
1232
+ ).not.toBeInTheDocument();
1233
+ expect(await openDropdown(dialog, /^Fixes/)).toEqual(
1234
+ Object.values(ResourceAiRemediationMode).map(
1235
+ (mode: ResourceAiRemediationMode): string => {
1236
+ return RESOURCE_REMEDIATION_MODE_LABELS[mode];
1237
+ },
1238
+ ),
1239
+ );
1240
+ fireEvent.click(
1241
+ menuOptions().find((option: HTMLElement): boolean => {
1242
+ return (
1243
+ option.textContent ===
1244
+ RESOURCE_REMEDIATION_MODE_LABELS[
1245
+ ResourceAiRemediationMode.RequireApproval
1246
+ ]
1247
+ );
1248
+ })!,
1249
+ );
1250
+ saveChangeModal(dialog);
1251
+
1252
+ expect(await waitForOneUpdate()).toEqual({
1253
+ aiRemediationMode: ResourceAiRemediationMode.RequireApproval,
1254
+ });
1255
+ expect(updateRequests()[0]!["modelType"]).toBe(DockerHost);
1256
+ });
1257
+
1258
+ test("a member turns investigation off, and only that is sent", async () => {
1259
+ serveModel({ aiRemediationMode: ResourceAiRemediationMode.Automatic });
1260
+ openAgentPage();
1261
+ const dialog: HTMLElement = await openChangeModal();
1262
+
1263
+ await toggleSwitch(dialog, "ai-investigation-field", true);
1264
+ saveChangeModal(dialog);
1265
+
1266
+ expect(await waitForOneUpdate()).toEqual({
1267
+ isAiInvestigationEnabled: false,
1268
+ });
1269
+ });
1270
+
1271
+ test("a member on a Bypass-approval host may step down to Automatic, unconfirmed", async () => {
1272
+ serveModel({ aiRemediationMode: ResourceAiRemediationMode.BypassApproval });
1273
+ openAgentPage();
1274
+ const dialog: HTMLElement = await openChangeModal();
1275
+
1276
+ await pickOption(
1277
+ dialog,
1278
+ /^Fixes/,
1279
+ RESOURCE_REMEDIATION_MODE_LABELS[ResourceAiRemediationMode.Automatic],
1280
+ );
1281
+ saveChangeModal(dialog);
1282
+
1283
+ expect(await waitForOneUpdate()).toEqual({
1284
+ aiRemediationMode: ResourceAiRemediationMode.Automatic,
1285
+ });
1286
+ expect(
1287
+ screen.queryByText("Let riskier changes run without approval?"),
1288
+ ).not.toBeInTheDocument();
1289
+ });
1290
+
1291
+ test("a member may remove an allowlist entry", async () => {
1292
+ serveModel({
1293
+ aiRemediationMode: ResourceAiRemediationMode.Automatic,
1294
+ aiCommandAllowlist: ["docker stop web", "docker stop api"],
1295
+ });
1296
+ openAgentPage();
1297
+ const dialog: HTMLElement = await openChangeModal();
1298
+
1299
+ expect(
1300
+ within(dialog).getByText(/You can remove entries or clear the list/),
1301
+ ).toBeInTheDocument();
1302
+ await setAllowlistText(dialog, "docker stop web");
1303
+ saveChangeModal(dialog);
1304
+
1305
+ expect(await waitForOneUpdate()).toEqual({
1306
+ aiCommandAllowlist: ["docker stop web"],
1307
+ });
1308
+ });
1309
+
1310
+ test("a member who adds an entry is told it needs the admin set, and nothing is sent", async () => {
1311
+ serveModel({
1312
+ aiRemediationMode: ResourceAiRemediationMode.Automatic,
1313
+ aiCommandAllowlist: ["docker stop web"],
1314
+ });
1315
+ openAgentPage();
1316
+ const dialog: HTMLElement = await openChangeModal();
1317
+
1318
+ await setAllowlistText(dialog, "docker stop web\ndocker stop api");
1319
+ saveChangeModal(dialog);
1320
+
1321
+ expect(
1322
+ await within(dialog).findByText(
1323
+ /is not in the saved allowlist/,
1324
+ {},
1325
+ { timeout: WAIT_TIMEOUT },
1326
+ ),
1327
+ ).toHaveTextContent("Edit Auto Remediation Rule");
1328
+ expect(updateByIdSpy).not.toHaveBeenCalled();
1329
+ });
1330
+
1331
+ test("the allowlist field is shown only while Automatic is chosen", async () => {
1332
+ grant(ADMIN_PERMISSIONS);
1333
+ serveModel({ aiRemediationMode: ResourceAiRemediationMode.Automatic });
1334
+ openAgentPage();
1335
+ const dialog: HTMLElement = await openChangeModal();
1336
+
1337
+ expect(
1338
+ await within(dialog).findByTestId(
1339
+ "ai-command-allowlist-field",
1340
+ {},
1341
+ { timeout: WAIT_TIMEOUT },
1342
+ ),
1343
+ ).toHaveAttribute("placeholder", DOCKER.allowlistPlaceholder);
1344
+
1345
+ await pickOption(
1346
+ dialog,
1347
+ /^Fixes/,
1348
+ RESOURCE_REMEDIATION_MODE_LABELS[
1349
+ ResourceAiRemediationMode.RequireApproval
1350
+ ],
1351
+ );
1352
+ await waitFor(
1353
+ () => {
1354
+ expect(
1355
+ within(dialog).queryByTestId("ai-command-allowlist-field"),
1356
+ ).not.toBeInTheDocument();
1357
+ },
1358
+ { timeout: WAIT_TIMEOUT },
1359
+ );
1360
+ });
1361
+
1362
+ test("an allowlist entry is checked with this resource type's policy", async () => {
1363
+ grant(ADMIN_PERMISSIONS);
1364
+ serveModel({ aiRemediationMode: ResourceAiRemediationMode.Automatic });
1365
+ openAgentPage();
1366
+ const dialog: HTMLElement = await openChangeModal();
1367
+
1368
+ // A host command means nothing on a Docker host.
1369
+ await setAllowlistText(dialog, "docker stop web\nsystemctl stop nginx");
1370
+ saveChangeModal(dialog);
1371
+
1372
+ expect(
1373
+ await within(dialog).findByText(
1374
+ /Entry 2: "systemctl stop nginx" does not start with a program the Docker AI agent runs/,
1375
+ {},
1376
+ { timeout: WAIT_TIMEOUT },
1377
+ ),
1378
+ ).toBeInTheDocument();
1379
+ expect(updateByIdSpy).not.toHaveBeenCalled();
1380
+ });
1381
+
1382
+ test("a failed save stays in the modal with the server's reason", async () => {
1383
+ grant(ADMIN_PERMISSIONS);
1384
+ serveModel({ aiRemediationMode: ResourceAiRemediationMode.Disabled });
1385
+ updateByIdSpy.mockImplementation(async (): Promise<never> => {
1386
+ throw new HTTPErrorResponse(
1387
+ 403,
1388
+ { error: "You need one of these permissions." },
1389
+ {},
1390
+ );
1391
+ });
1392
+ openAgentPage();
1393
+ const dialog: HTMLElement = await openChangeModal();
1394
+
1395
+ await pickOption(
1396
+ dialog,
1397
+ /^Fixes/,
1398
+ RESOURCE_REMEDIATION_MODE_LABELS[
1399
+ ResourceAiRemediationMode.RequireApproval
1400
+ ],
1401
+ );
1402
+ saveChangeModal(dialog);
1403
+
1404
+ expect(await findTestId("ai-access-save-error")).toHaveTextContent(
1405
+ "You need one of these permissions.",
1406
+ );
1407
+ expect(screen.getByRole("dialog")).toBe(dialog);
1408
+ });
1409
+
1410
+ test("the modal's read failing is said in the modal", async () => {
1411
+ getItemSpy.mockImplementation(async (): Promise<null> => {
1412
+ return null;
1413
+ });
1414
+ openAgentPage();
1415
+ fireEvent.click(await findTestId("ai-access-change-button"));
1416
+
1417
+ expect(
1418
+ await findText(/Could not read this Docker host's AI settings/),
1419
+ ).toBeInTheDocument();
1420
+ });
1421
+ });
1422
+
1423
+ describe("the Change modal: confirmations", () => {
1424
+ test("Bypass approval is confirmed before it is saved", async () => {
1425
+ grant(ADMIN_PERMISSIONS);
1426
+ openAgentPage();
1427
+ const dialog: HTMLElement = await openChangeModal();
1428
+
1429
+ await pickOption(
1430
+ dialog,
1431
+ /^Fixes/,
1432
+ RESOURCE_REMEDIATION_MODE_LABELS[
1433
+ ResourceAiRemediationMode.BypassApproval
1434
+ ],
1435
+ );
1436
+ saveChangeModal(dialog);
1437
+
1438
+ const confirm: HTMLElement = await findDialogTitled(
1439
+ "Turn on Bypass approval?",
1440
+ );
1441
+ expect(confirm).toHaveTextContent("on this Docker host on its own");
1442
+ expect(updateByIdSpy).not.toHaveBeenCalled();
1443
+
1444
+ fireEvent.click(within(confirm).getByText("Confirm and save"));
1445
+
1446
+ expect(await waitForOneUpdate()).toEqual({
1447
+ aiRemediationMode: ResourceAiRemediationMode.BypassApproval,
1448
+ });
1449
+ });
1450
+
1451
+ test("a broad entry is confirmed, and backing out sends nothing", async () => {
1452
+ grant(ADMIN_PERMISSIONS);
1453
+ serveModel({ aiRemediationMode: ResourceAiRemediationMode.Automatic });
1454
+ openAgentPage();
1455
+ const dialog: HTMLElement = await openChangeModal();
1456
+
1457
+ await setAllowlistText(dialog, "docker stop *");
1458
+ saveChangeModal(dialog);
1459
+
1460
+ const confirm: HTMLElement = await findDialogTitled(
1461
+ "Let riskier changes run without approval?",
1462
+ );
1463
+ expect(confirm).toHaveTextContent(
1464
+ 'The allowlist entry "docker stop *" uses a * for the object a change touches',
1465
+ );
1466
+
1467
+ fireEvent.click(within(confirm).getByText("Cancel"));
1468
+ await waitFor(
1469
+ () => {
1470
+ expect(
1471
+ screen.queryByText("Let riskier changes run without approval?"),
1472
+ ).not.toBeInTheDocument();
1473
+ },
1474
+ { timeout: WAIT_TIMEOUT },
1475
+ );
1476
+ expect(updateByIdSpy).not.toHaveBeenCalled();
1477
+ });
1478
+
1479
+ test("an entry that names its container saves without a question", async () => {
1480
+ grant(ADMIN_PERMISSIONS);
1481
+ serveModel({ aiRemediationMode: ResourceAiRemediationMode.Automatic });
1482
+ openAgentPage();
1483
+ const dialog: HTMLElement = await openChangeModal();
1484
+
1485
+ await setAllowlistText(dialog, "docker stop web");
1486
+ saveChangeModal(dialog);
1487
+
1488
+ expect(await waitForOneUpdate()).toEqual({
1489
+ aiCommandAllowlist: ["docker stop web"],
1490
+ });
1491
+ });
1492
+
1493
+ test("nothing changed: the modal closes without a write", async () => {
1494
+ openAgentPage();
1495
+ const dialog: HTMLElement = await openChangeModal();
1496
+
1497
+ saveChangeModal(dialog);
1498
+
1499
+ await waitFor(
1500
+ () => {
1501
+ expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
1502
+ },
1503
+ { timeout: WAIT_TIMEOUT },
1504
+ );
1505
+ expect(updateByIdSpy).not.toHaveBeenCalled();
1506
+ });
1507
+ });
1508
+
1509
+ describe("Test connection", () => {
1510
+ test("runs the test for this resource and shows each command's result", async () => {
1511
+ serve(makeStatus(), {
1512
+ test: async (): Promise<HTTPResponse<JSONObject>> => {
1513
+ return new HTTPResponse<JSONObject>(
1514
+ 200,
1515
+ {
1516
+ ok: false,
1517
+ message: "docker info failed.",
1518
+ results: [
1519
+ {
1520
+ command: "docker version",
1521
+ succeeded: true,
1522
+ exitCode: 0,
1523
+ output: "Server: 27.3.1",
1524
+ errorMessage: null,
1525
+ },
1526
+ {
1527
+ command: "docker info",
1528
+ succeeded: false,
1529
+ exitCode: 1,
1530
+ output: "",
1531
+ errorMessage: "permission denied",
1532
+ },
1533
+ ],
1534
+ status: makeStatus({
1535
+ agent: makeAgent({ agentVersion: "14.2.0" }),
1536
+ }) as unknown as JSONObject,
1537
+ },
1538
+ {},
1539
+ );
1540
+ },
1541
+ });
1542
+ openAgentPage();
1543
+
1544
+ fireEvent.click(await findTestId("ai-agent-test-button"));
1545
+
1546
+ const results: HTMLElement = await findTestId("ai-agent-test-results");
1547
+ expect(results).toHaveTextContent("The connection is not working yet");
1548
+ expect(results).toHaveTextContent("docker info failed.");
1549
+ const rows: Array<HTMLElement> = within(results).getAllByTestId(
1550
+ "ai-agent-test-result-row",
1551
+ );
1552
+ expect(rows).toHaveLength(2);
1553
+ expect(rows[0]).toHaveTextContent("docker version");
1554
+ expect(rows[0]).toHaveTextContent("succeeded");
1555
+ expect(rows[0]).toHaveTextContent("Server: 27.3.1");
1556
+ expect(rows[1]).toHaveTextContent("failed (exit 1)");
1557
+ expect(rows[1]).toHaveTextContent("permission denied");
1558
+ expect(postsTo(TEST_ROUTE)[0]!["data"]).toEqual({
1559
+ resourceType: AiResourceType.DockerHost,
1560
+ resourceId: RESOURCE_ID,
1561
+ });
1562
+ // The status the test returned replaces the one on screen.
1563
+ await waitFor(
1564
+ () => {
1565
+ expect(screen.getByTestId("ai-agent-meta")).toHaveTextContent(
1566
+ "agent v14.2.0",
1567
+ );
1568
+ },
1569
+ { timeout: WAIT_TIMEOUT },
1570
+ );
1571
+ });
1572
+
1573
+ test("a refused test says what it needs, not what the server says about settings", async () => {
1574
+ serve(makeStatus(), {
1575
+ test: async (): Promise<HTTPErrorResponse> => {
1576
+ // The server's NotAuthorizedException, which OneUptime sends as 422.
1577
+ return new HTTPErrorResponse(
1578
+ ExceptionCode.NotAuthorizedException,
1579
+ { error: "You cannot change this resource's AI access." },
1580
+ {},
1581
+ );
1582
+ },
1583
+ });
1584
+ openAgentPage();
1585
+
1586
+ fireEvent.click(await findTestId("ai-agent-test-button"));
1587
+
1588
+ const error: HTMLElement = await findTestId("ai-agent-test-error");
1589
+ expect(error).toHaveTextContent(
1590
+ "Testing the connection needs permission to edit this Docker host",
1591
+ );
1592
+ expect(error).toHaveTextContent(
1593
+ "Nothing on the Docker host or in its AI settings was changed.",
1594
+ );
1595
+ });
1596
+
1597
+ test("another failure is shown as the server said it", async () => {
1598
+ serve(makeStatus(), {
1599
+ test: async (): Promise<HTTPErrorResponse> => {
1600
+ return new HTTPErrorResponse(
1601
+ 429,
1602
+ { error: "An AI access test is already running." },
1603
+ {},
1604
+ );
1605
+ },
1606
+ });
1607
+ openAgentPage();
1608
+
1609
+ fireEvent.click(await findTestId("ai-agent-test-button"));
1610
+
1611
+ expect(await findTestId("ai-agent-test-error")).toHaveTextContent(
1612
+ "An AI access test is already running.",
1613
+ );
1614
+ });
1615
+
1616
+ test("is locked for a reader, with the reason", async () => {
1617
+ grant(READER_PERMISSIONS);
1618
+ openAgentPage();
1619
+
1620
+ expect(await findTestId("ai-agent-test-button")).toBeDisabled();
1621
+ expect(
1622
+ screen.getByTestId("ai-agent-test-permission-note"),
1623
+ ).toHaveTextContent("Testing the connection needs permission to edit");
1624
+ });
1625
+ });
1626
+
1627
+ describe("Reset agent", () => {
1628
+ test("is only offered to the admin set", async () => {
1629
+ openAgentPage();
1630
+
1631
+ expect(await findText("Docker AI agent")).toBeInTheDocument();
1632
+ expect(
1633
+ screen.queryByTestId("ai-agent-reset-button"),
1634
+ ).not.toBeInTheDocument();
1635
+ });
1636
+
1637
+ test("an admin resets the agent after a confirmation", async () => {
1638
+ grant(ADMIN_PERMISSIONS);
1639
+ openAgentPage();
1640
+
1641
+ fireEvent.click(await findTestId("ai-agent-reset-button"));
1642
+ const confirm: HTMLElement = await findDialogTitled("Reset the AI agent?");
1643
+ expect(confirm).toHaveTextContent(
1644
+ "This revokes the Docker AI agent's key.",
1645
+ );
1646
+ expect(postsTo(RESET_ROUTE)).toHaveLength(0);
1647
+
1648
+ fireEvent.click(within(confirm).getByText("Reset agent"));
1649
+
1650
+ expect(await findTestId("ai-agent-action-notice")).toHaveTextContent(
1651
+ "The Docker AI agent was reset. It reconnects on its own within a few minutes.",
1652
+ );
1653
+ expect(postsTo(RESET_ROUTE)).toHaveLength(1);
1654
+ expect(postsTo(RESET_ROUTE)[0]!["data"]).toEqual({
1655
+ resourceType: AiResourceType.DockerHost,
1656
+ resourceId: RESOURCE_ID,
1657
+ });
1658
+ });
1659
+
1660
+ test("a refused reset stays in its dialog with the reason", async () => {
1661
+ grant(ADMIN_PERMISSIONS);
1662
+ serve(makeStatus(), {
1663
+ reset: async (): Promise<HTTPErrorResponse> => {
1664
+ return new HTTPErrorResponse(403, { error: "Not yours to reset." }, {});
1665
+ },
1666
+ });
1667
+ openAgentPage();
1668
+
1669
+ fireEvent.click(await findTestId("ai-agent-reset-button"));
1670
+ const confirm: HTMLElement = await findDialogTitled("Reset the AI agent?");
1671
+ fireEvent.click(within(confirm).getByText("Reset agent"));
1672
+
1673
+ expect(
1674
+ await within(confirm).findByText(
1675
+ "Not yours to reset.",
1676
+ {},
1677
+ { timeout: WAIT_TIMEOUT },
1678
+ ),
1679
+ ).toBeInTheDocument();
1680
+ });
1681
+
1682
+ test("nothing to reset before an agent registered", async () => {
1683
+ grant(ADMIN_PERMISSIONS);
1684
+ serve(notInstalledStatus());
1685
+ openAgentPage();
1686
+
1687
+ expect(await findTestId("ai-agent-status")).toHaveTextContent(
1688
+ "Not installed",
1689
+ );
1690
+ expect(
1691
+ screen.queryByTestId("ai-agent-reset-button"),
1692
+ ).not.toBeInTheDocument();
1693
+ });
1694
+ });
1695
+
1696
+ describe("every resource type", () => {
1697
+ test.each(ALL_AI_RESOURCE_TYPES)(
1698
+ "%s: the page asks about its own type and names its own agent",
1699
+ async (type: AiResourceType) => {
1700
+ const descriptor: ResourceAiAgentDescriptor =
1701
+ getResourceAiAgentDescriptor(type);
1702
+ serve({
1703
+ ...notInstalledStatus(),
1704
+ resourceType: type,
1705
+ });
1706
+ openAgentPage(descriptor);
1707
+
1708
+ expect(await findText(descriptor.agentName)).toBeInTheDocument();
1709
+ expect(screen.getByTestId("ai-agent-page-heading")).toHaveTextContent(
1710
+ getResourceAiAgentPageSubtitle(descriptor),
1711
+ );
1712
+ expect(postsTo(STATUS_ROUTE)[0]!["data"]).toEqual({
1713
+ resourceType: type,
1714
+ resourceId: RESOURCE_ID,
1715
+ });
1716
+ expect(codeIn(await findTestId("ai-agent-install-command"))).toContain(
1717
+ `${getResourceAiAgentServiceName(type)}:`,
1718
+ );
1719
+ },
1720
+ );
1721
+ });