@oneuptime/common 14.0.1 → 14.0.2
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.
- package/Models/DatabaseModels/AlertEpisodeMember.ts +8 -1
- package/Models/DatabaseModels/AutoRemediationSuggestion.ts +63 -0
- package/Models/DatabaseModels/IncidentEpisodeMember.ts +8 -1
- package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +9 -2
- package/Models/DatabaseModels/IncidentPublicNote.ts +9 -2
- package/Models/DatabaseModels/KubernetesCluster.ts +429 -0
- package/Models/DatabaseModels/Monitor.ts +48 -0
- package/Models/DatabaseModels/Runner.ts +5 -4
- package/Models/DatabaseModels/RunnerJob.ts +66 -0
- package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +10 -2
- package/Models/DatabaseModels/WorkflowVariable.ts +479 -2
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +79 -25
- package/Server/API/AIInvestigationAPI.ts +341 -0
- package/Server/API/AutoRemediationAPI.ts +184 -2
- package/Server/API/CommonAPI.ts +70 -0
- package/Server/API/KubernetesClusterAiAccessAPI.ts +793 -0
- package/Server/API/MicrosoftTeamsAPI.ts +770 -315
- package/Server/API/MonitorAPI.ts +140 -0
- package/Server/API/OnCallScheduleTimelineAPI.ts +5 -21
- package/Server/API/SlackAPI.ts +447 -99
- package/Server/API/StatusPageAPI.ts +146 -26
- package/Server/API/TelemetryAPI.ts +216 -5
- package/Server/API/UserTwoFactorBackupCodeAPI.ts +1 -0
- package/Server/API/WorkflowVariableAPI.ts +156 -0
- package/Server/Infrastructure/GlobalCache.ts +35 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.ts +97 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.ts +129 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.ts +20 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.ts +38 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
- package/Server/Middleware/MasterAdminAuthorization.ts +28 -0
- package/Server/Middleware/TelemetryIngest.ts +47 -2
- package/Server/Middleware/UserAuthorization.ts +56 -2
- package/Server/Services/AIAgentService.ts +1 -0
- package/Server/Services/AccessTokenService.ts +121 -48
- package/Server/Services/AlertEpisodeMemberService.ts +9 -0
- package/Server/Services/AlertEpisodeService.ts +14 -1
- package/Server/Services/AlertService.ts +134 -22
- package/Server/Services/AutoRemediationRuleEngineService.ts +968 -39
- package/Server/Services/AutoRemediationRuleService.ts +118 -0
- package/Server/Services/BillingService.ts +1 -0
- package/Server/Services/InboundEmail/Providers/SendGridInboundProvider.ts +16 -22
- package/Server/Services/IncidentEpisodeMemberService.ts +9 -0
- package/Server/Services/IncidentEpisodePublicNoteService.ts +66 -0
- package/Server/Services/IncidentEpisodeRoleMemberService.ts +27 -0
- package/Server/Services/IncidentEpisodeService.ts +14 -1
- package/Server/Services/IncidentGroupingEngineService.ts +11 -0
- package/Server/Services/IncidentMemberService.ts +21 -0
- package/Server/Services/IncidentPublicNoteService.ts +63 -0
- package/Server/Services/IncidentService.ts +120 -20
- package/Server/Services/IncidentStateTimelineService.ts +15 -0
- package/Server/Services/IncidentTemplateService.ts +83 -3
- package/Server/Services/KubernetesClusterAiAccessService.ts +1943 -0
- package/Server/Services/KubernetesClusterService.ts +1570 -1
- package/Server/Services/LogAggregationService.ts +14 -6
- package/Server/Services/MonitorService.ts +651 -52
- package/Server/Services/MonitorStatusTimelineService.ts +639 -2
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +2 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.ts +2 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleUserService.ts +2 -0
- package/Server/Services/OnCallDutyPolicyScheduleService.ts +3 -0
- package/Server/Services/OnCallNotificationAlertingService.ts +36 -12
- package/Server/Services/OnCallSetupReminderService.ts +1 -0
- package/Server/Services/ProbeService.ts +7 -1
- package/Server/Services/ProjectService.ts +11 -1
- package/Server/Services/RunbookCredentialService.ts +26 -1
- package/Server/Services/RunbookSecretService.ts +36 -0
- package/Server/Services/RunnerJobService.ts +941 -23
- package/Server/Services/RunnerService.ts +439 -6
- package/Server/Services/ScheduledMaintenancePublicNoteService.ts +66 -0
- package/Server/Services/ScheduledMaintenanceService.ts +124 -19
- package/Server/Services/ScheduledMaintenanceTemplateService.ts +82 -3
- package/Server/Services/ServiceLevelObjectiveService.ts +14 -1
- package/Server/Services/StatusPagePrivateUserService.ts +1 -0
- package/Server/Services/StatusPageService.ts +301 -74
- package/Server/Services/StatusPageSubscriberService.ts +4 -0
- package/Server/Services/TeamMemberService.ts +309 -7
- package/Server/Services/TraceAggregationService.ts +10 -6
- package/Server/Services/UserEmailService.ts +1 -0
- package/Server/Services/UserNotificationMethodAdminService.ts +1 -0
- package/Server/Services/UserNotificationRuleAdminService.ts +1 -0
- package/Server/Services/UserNotificationRuleService.ts +4 -0
- package/Server/Services/UserService.ts +122 -1
- package/Server/Services/WorkflowVariableService.ts +593 -8
- package/Server/Services/WorkspaceNotificationRuleService.ts +588 -8
- package/Server/Services/WorkspaceProjectAuthTokenService.ts +170 -1
- package/Server/Types/Database/QueryHelper.ts +22 -0
- package/Server/Types/Markdown.ts +502 -27
- package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +182 -0
- package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +525 -0
- package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +854 -0
- package/Server/Utils/AI/CodeFix/CodeFixAgentAuth.ts +22 -2
- package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +1916 -307
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +1601 -74
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +139 -7
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +58 -1
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +54 -1
- package/Server/Utils/AI/Toolbox/AIActionTools.ts +14 -6
- package/Server/Utils/AI/Toolbox/ResourceTools.ts +4 -2
- package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +1341 -150
- package/Server/Utils/AutoRemediation/RemediationVerifier.ts +375 -4
- package/Server/Utils/Database/AffectedResourceRelations.ts +127 -0
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +240 -1
- package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +1 -0
- package/Server/Utils/Monitor/MonitorAlert.ts +83 -9
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +5 -1
- package/Server/Utils/Monitor/MonitorIncident.ts +104 -9
- package/Server/Utils/Monitor/MonitorPayloadRedaction.ts +124 -7
- package/Server/Utils/Monitor/ProbeMonitorsNotification.ts +407 -0
- package/Server/Utils/OnCall/OnCallShiftReminderRunner.ts +1 -0
- package/Server/Utils/PushNotificationUtil.ts +45 -0
- package/Server/Utils/Rules/OwnerRuleAssignment.ts +40 -3
- package/Server/Utils/Runbook/RunbookExecutePermission.ts +56 -6
- package/Server/Utils/SessionReplay/SessionReplayReadService.ts +223 -33
- package/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.ts +559 -0
- package/Server/Utils/StartServer.ts +4 -3
- package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +528 -0
- package/Server/Utils/TwoFactorBackupCodeNotification.ts +1 -0
- package/Server/Utils/UserPermission/UserPermission.ts +16 -0
- package/Server/Utils/Workflow/OAuth2TokenClient.ts +704 -0
- package/Server/Utils/Workflow/WorkflowVariableOAuthToken.ts +318 -0
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +47 -7
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.ts +57 -16
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +193 -214
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.ts +57 -13
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.ts +54 -17
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +179 -161
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +281 -167
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.ts +114 -0
- package/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.ts +895 -0
- package/Server/Utils/Workspace/Slack/Actions/ActionTypes.ts +7 -9
- package/Server/Utils/Workspace/Slack/Actions/Alert.ts +96 -227
- package/Server/Utils/Workspace/Slack/Actions/AlertEpisode.ts +93 -214
- package/Server/Utils/Workspace/Slack/Actions/Auth.ts +30 -3
- package/Server/Utils/Workspace/Slack/Actions/Authorization.ts +99 -0
- package/Server/Utils/Workspace/Slack/Actions/Incident.ts +117 -283
- package/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.ts +101 -196
- package/Server/Utils/Workspace/Slack/Actions/ReactionNote.ts +329 -0
- package/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.ts +111 -287
- package/Server/Utils/Workspace/Slack/Slack.ts +86 -17
- package/Server/Utils/Workspace/Slack/app-manifest.json +2 -0
- package/Server/Utils/Workspace/Workspace.ts +171 -54
- package/Server/Utils/Workspace/WorkspaceActionAuthorization.ts +252 -0
- package/Server/Utils/Workspace/WorkspaceBase.ts +39 -0
- package/Server/Utils/Workspace/WorkspaceOAuthState.ts +280 -0
- package/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.ts +96 -0
- package/Server/Utils/Workspace/WorkspaceReactionNote.ts +620 -0
- package/Tests/App/Dashboard/AttributesJSONDashboardSurfaces.test.tsx +496 -0
- package/Tests/App/Dashboard/AutoRemediationRulesTableCommandRunners.test.tsx +152 -0
- package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +32 -0
- package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +794 -0
- package/Tests/App/Dashboard/CreateOAuthWorkflowVariableModal.test.tsx +840 -0
- package/Tests/App/Dashboard/CreateWorkflowModal.test.tsx +653 -0
- package/Tests/App/Dashboard/DatabaseMonitorStepForm.test.tsx +197 -0
- package/Tests/App/Dashboard/DatabaseMonitorView.test.tsx +376 -0
- package/Tests/App/Dashboard/DisabledWarningRobustness.test.tsx +262 -0
- package/Tests/App/Dashboard/EventNotes.test.tsx +2080 -0
- package/Tests/App/Dashboard/EventNotesPages.test.tsx +486 -0
- package/Tests/App/Dashboard/EventNotesUtil.test.ts +933 -0
- package/Tests/App/Dashboard/ExceptionMonitorEnvironmentFilter.test.tsx +218 -0
- package/Tests/App/Dashboard/IncidentChangeStateNotifyDefault.test.tsx +665 -0
- package/Tests/App/Dashboard/IncidentEpisodeFeedPublicNoteNotifyDefault.test.tsx +494 -0
- package/Tests/App/Dashboard/IncidentEpisodeOverviewNotifyDefault.test.tsx +706 -0
- package/Tests/App/Dashboard/IncidentFeedPublicNoteNotifyDefault.test.tsx +472 -0
- package/Tests/App/Dashboard/IncidentOverviewNotifyDefault.test.tsx +858 -0
- package/Tests/App/Dashboard/IncomingEmailAddressSettings.test.tsx +567 -0
- package/Tests/App/Dashboard/InvestigationClusterToolFormat.test.ts +241 -0
- package/Tests/App/Dashboard/InvestigationEvidenceKubectl.test.tsx +322 -0
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +12 -0
- package/Tests/App/Dashboard/InvestigationPanelClusterAccess.test.tsx +476 -0
- package/Tests/App/Dashboard/InvestigationRunDetailsKubectl.test.tsx +383 -0
- package/Tests/App/Dashboard/KubectlActivitySummary.test.tsx +366 -0
- package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +2886 -0
- package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +2688 -0
- package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +915 -0
- package/Tests/App/Dashboard/LayerDateTimeFieldTimezoneName.test.tsx +66 -0
- package/Tests/App/Dashboard/LogMonitorPreview.test.tsx +174 -0
- package/Tests/App/Dashboard/MonitorFeedRefresh.test.tsx +279 -0
- package/Tests/App/Dashboard/MonitorMetricQueryConfig.test.ts +244 -0
- package/Tests/App/Dashboard/MonitorOpenWorkCard.test.tsx +381 -0
- package/Tests/App/Dashboard/MonitorOpenWorkHook.test.tsx +784 -0
- package/Tests/App/Dashboard/MonitorOverviewData.test.tsx +2199 -0
- package/Tests/App/Dashboard/MonitorOverviewHero.test.tsx +1169 -0
- package/Tests/App/Dashboard/MonitorOverviewInput.test.ts +1127 -0
- package/Tests/App/Dashboard/MonitorOverviewPage.test.tsx +2136 -0
- package/Tests/App/Dashboard/MonitorOverviewSelectReadable.test.ts +368 -0
- package/Tests/App/Dashboard/MonitorOverviewStatBar.test.tsx +526 -0
- package/Tests/App/Dashboard/MonitorOverviewSupportingCards.test.tsx +542 -0
- package/Tests/App/Dashboard/MonitorOwnersCombine.test.ts +299 -0
- package/Tests/App/Dashboard/MonitorProbesCard.test.tsx +602 -0
- package/Tests/App/Dashboard/MonitorResponseTimeCard.test.tsx +386 -0
- package/Tests/App/Dashboard/MonitorSetupAndConnectionCards.test.tsx +612 -0
- package/Tests/App/Dashboard/MonitorStatusChangesCard.test.tsx +257 -0
- package/Tests/App/Dashboard/MonitorSummaryTestMonitorButton.test.tsx +360 -1
- package/Tests/App/Dashboard/MonitorUptimeHistoryCard.test.tsx +835 -0
- package/Tests/App/Dashboard/MonitorUptimeSummaryHook.test.tsx +971 -0
- package/Tests/App/Dashboard/MonitorViewLayout.test.tsx +580 -0
- package/Tests/App/Dashboard/OnCallLayerRestrictionTimesPreserved.test.tsx +441 -0
- package/Tests/App/Dashboard/OnCallNavbar.test.tsx +214 -0
- package/Tests/App/Dashboard/OnCallSchedulePreviewCalendarDisplay.test.tsx +1389 -0
- package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +205 -1
- package/Tests/App/Dashboard/OnCallScheduleTimeInputsSettingsZone.test.tsx +198 -0
- package/Tests/App/Dashboard/PublicNotePagesNotifyDefault.test.tsx +524 -0
- package/Tests/App/Dashboard/RemediationSuggestionCardApproval.test.tsx +702 -0
- package/Tests/App/Dashboard/RemoveUserFromProject.test.tsx +427 -0
- package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +4 -0
- package/Tests/App/Dashboard/RumSessionReplaySideMenu.test.tsx +3 -1
- package/Tests/App/Dashboard/RunbookStepTypeMaps.test.ts +166 -0
- package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +622 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceChangeStateNotifyDefault.test.tsx +1491 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceFeedPublicNoteNotifyDefault.test.tsx +489 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceOverviewNotifyDefault.test.tsx +909 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +14 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceWindowStatsTranslation.test.tsx +307 -0
- package/Tests/App/Dashboard/SecurityEventAttributeColumnPicker.test.tsx +542 -0
- package/Tests/App/Dashboard/SecurityEventConnectionFormModal.test.tsx +174 -0
- package/Tests/App/Dashboard/SecurityEventConnectionsEmptyState.test.tsx +973 -212
- package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +491 -52
- package/Tests/App/Dashboard/SecurityEventDetailPanel.test.tsx +301 -0
- package/Tests/App/Dashboard/SecurityEventListRow.test.tsx +158 -1
- package/Tests/App/Dashboard/SecurityEventProviderTile.test.tsx +869 -0
- package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +959 -94
- package/Tests/App/Dashboard/SecurityEventsNoResults.test.tsx +2 -0
- package/Tests/App/Dashboard/SecurityEventsViewer.test.tsx +274 -1
- package/Tests/App/Dashboard/TeamMembershipRemoval.test.ts +900 -0
- package/Tests/App/Dashboard/TeamMembershipRemovalPages.test.tsx +460 -0
- package/Tests/App/Dashboard/TimezoneElements.test.tsx +321 -0
- package/Tests/App/Dashboard/TimezoneSelectButton.test.tsx +634 -0
- package/Tests/App/Dashboard/TraceExplorer.test.tsx +260 -0
- package/Tests/App/Dashboard/TraceMonitorPreview.test.tsx +182 -0
- package/Tests/App/Dashboard/UserTimezoneInit.test.tsx +544 -0
- package/Tests/App/Dashboard/WorkflowVariableModals.test.tsx +1630 -0
- package/Tests/App/Dashboard/WorkflowVariableRoutes.test.tsx +603 -0
- package/Tests/App/Dashboard/WorkflowVariableTokenStatus.test.tsx +589 -0
- package/Tests/App/Dashboard/WorkflowVariableUtil.test.ts +1543 -0
- package/Tests/App/Dashboard/WorkflowVariableView.test.tsx +3227 -0
- package/Tests/App/Dashboard/WorkflowVariablesTable.test.tsx +1277 -395
- package/Tests/App/StatusPage/StatusPageOverviewMonitorGroupUptime.test.tsx +714 -0
- package/Tests/App/StatusPage/StatusPageOverviewUptimeBars.NewYork.test.tsx +1009 -0
- package/Tests/App/StatusPage/StatusPageOverviewUptimeBars.test.tsx +1092 -0
- package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +5 -0
- package/Tests/Models/DatabaseModels/KubernetesClusterAiAccessColumns.test.ts +520 -0
- package/Tests/Models/DatabaseModels/MonitorSecretKeyColumnAccessControl.test.ts +66 -2
- package/Tests/Models/DatabaseModels/WorkspaceProjectAuthTokenAccessControl.test.ts +236 -0
- package/Tests/Models/IncidentEpisodePublicNoteNotifyColumnContract.test.ts +103 -0
- package/Tests/Models/IncidentPublicNoteNotifyColumnContract.test.ts +96 -0
- package/Tests/Models/ScheduledMaintenancePublicNoteNotifyColumnContract.test.ts +111 -0
- package/Tests/Server/API/AIInvestigationAPI.test.ts +976 -0
- package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +836 -0
- package/Tests/Server/API/CommonAPIAssertCanCreateTable.test.ts +565 -0
- package/Tests/Server/API/CommonAPIAssertCanReadTable.test.ts +282 -0
- package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +1744 -0
- package/Tests/Server/API/LogHistogramAPI.test.ts +371 -0
- package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +125 -30
- package/Tests/Server/API/MicrosoftTeamsOAuthState.test.ts +1121 -0
- package/Tests/Server/API/MonitorUptimeSummaryAPI.test.ts +685 -0
- package/Tests/Server/API/SessionReplayAPI.test.ts +602 -1
- package/Tests/Server/API/SlackChannelCacheAPI.test.ts +358 -0
- package/Tests/Server/API/SlackOAuthState.test.ts +688 -0
- package/Tests/Server/API/StatusPageOverviewMonitorGroupUptime.test.ts +527 -0
- package/Tests/Server/API/StatusPageTestEmailReportAuth.test.ts +2 -0
- package/Tests/Server/API/StatusPageUptimeAPI.test.ts +427 -0
- package/Tests/Server/API/UserProjectsAPI.test.ts +4 -0
- package/Tests/Server/API/WorkflowVariableAPI.test.ts +404 -0
- package/Tests/Server/API/WorkspaceOAuthTestHelpers.ts +289 -0
- package/Tests/Server/API/WorkspaceSendTestNotificationRoutes.test.ts +2064 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +86 -0
- package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +589 -0
- package/Tests/Server/Infrastructure/Postgres/AddOAuth2WorkflowVariablesMigration.test.ts +254 -0
- package/Tests/Server/Middleware/BlockedUserMiddleware.test.ts +403 -0
- package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +13 -0
- package/Tests/Server/Middleware/PublicRouteUserMiddleware.test.ts +2 -0
- package/Tests/Server/Middleware/RemovedProjectMemberAuthorization.test.ts +469 -0
- package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +77 -1
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +211 -0
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +358 -0
- package/Tests/Server/Middleware/UserAuthorization.test.ts +97 -0
- package/Tests/Server/Middleware/UserAuthorizationEditionEnforcement.test.ts +2 -0
- package/Tests/Server/Services/AccessTokenProjectUserGrant.test.ts +14 -2
- package/Tests/Server/Services/AccessTokenRemovedMemberAccess.test.ts +1044 -0
- package/Tests/Server/Services/AddOwnersIdempotency.test.ts +5 -0
- package/Tests/Server/Services/AlertEpisodeMemberCreatePermission.test.ts +139 -0
- package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +1361 -0
- package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +375 -0
- package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +2384 -0
- package/Tests/Server/Services/FindOwnersProjectMembership.test.ts +267 -0
- package/Tests/Server/Services/InboundEmail/SendGridInboundProviderAddress.test.ts +90 -0
- package/Tests/Server/Services/IncidentEpisodeMemberCreatePermission.test.ts +143 -0
- package/Tests/Server/Services/IncidentEpisodePublicNoteNotifyDefault.test.ts +805 -0
- package/Tests/Server/Services/IncidentPublicNoteNotifyDefault.test.ts +724 -0
- package/Tests/Server/Services/IncidentRoleProjectMembership.test.ts +233 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessAgentRunnerAssignments.test.ts +372 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessBindingGuard.test.ts +1876 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +620 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +421 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +3662 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessSubjectClusters.test.ts +405 -0
- package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +975 -0
- package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +497 -0
- package/Tests/Server/Services/KubernetesClusterServiceDeleteSettlesRemediation.test.ts +328 -0
- package/Tests/Server/Services/LogAggregationService.test.ts +99 -0
- package/Tests/Server/Services/MonitorServiceIncomingEmailCustomAddress.test.ts +447 -0
- package/Tests/Server/Services/MonitorServiceProbeFlagSync.test.ts +1334 -0
- package/Tests/Server/Services/MonitorServiceProbeStatusGrouping.test.ts +1640 -0
- package/Tests/Server/Services/MonitorServiceRefreshCurrentStatus.test.ts +225 -0
- package/Tests/Server/Services/MonitorStatusBridgeProbePath.test.ts +8 -8
- package/Tests/Server/Services/MonitorStatusTimelineDailyAggregates.test.ts +264 -1
- package/Tests/Server/Services/MonitorStatusTimelineMergedDowntime.test.ts +284 -0
- package/Tests/Server/Services/MonitorUptimeSummaryService.test.ts +1043 -0
- package/Tests/Server/Services/NetworkDeviceRuleEngines.test.ts +5 -0
- package/Tests/Server/Services/OnCallUndeliverablePageOwnerMembership.test.ts +359 -0
- package/Tests/Server/Services/ProbeServiceDashboardLink.test.ts +191 -0
- package/Tests/Server/Services/ProjectOwnersProjectMembership.test.ts +221 -0
- package/Tests/Server/Services/RuleRun/IncidentAlertMaintenanceRuleEnginesRun.test.ts +5 -0
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupARun.test.ts +28 -0
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +9 -1
- package/Tests/Server/Services/RunnerJobEnqueueAiCommandAgentRunner.test.ts +230 -0
- package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +978 -0
- package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +866 -0
- package/Tests/Server/Services/RunnerJobSubmitResultRedaction.test.ts +322 -0
- package/Tests/Server/Services/RunnerJobSubmitResultUnstorableText.test.ts +357 -0
- package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +227 -0
- package/Tests/Server/Services/RunnerServiceAgentName.test.ts +581 -0
- package/Tests/Server/Services/ScheduledMaintenancePublicNoteNotifyDefault.test.ts +802 -0
- package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +53 -0
- package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleEngines.test.ts +5 -0
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +12 -0
- package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +1176 -1
- package/Tests/Server/Services/TeamMemberBillingSeatSync.test.ts +9 -0
- package/Tests/Server/Services/TeamMemberOnCallCleanup.test.ts +18 -0
- package/Tests/Server/Services/TeamMemberProjectMembership.test.ts +255 -0
- package/Tests/Server/Services/TeamMemberRemovalRevokesProjectAccess.test.ts +600 -0
- package/Tests/Server/Services/TeamMemberResourceCleanup.test.ts +731 -0
- package/Tests/Server/Services/TeamMemberWorkspaceAccountCleanup.test.ts +164 -0
- package/Tests/Server/Services/TraceAggregationService.test.ts +64 -0
- package/Tests/Server/Services/UserServiceBlockedUser.test.ts +293 -0
- package/Tests/Server/Services/WorkflowVariableOAuthHooks.test.ts +1016 -0
- package/Tests/Server/Services/WorkflowVariableOAuthPostgres.test.ts +543 -0
- package/Tests/Server/Services/WorkflowVariableRenameUniqueness.test.ts +16 -5
- package/Tests/Server/Services/WorkspaceNotificationRuleNoteDelivery.test.ts +473 -0
- package/Tests/Server/Services/WorkspaceNotificationRuleSendTestNotification.test.ts +3690 -0
- package/Tests/Server/Services/WorkspaceProjectAuthTokenMiscDataSecrets.test.ts +374 -0
- package/Tests/Server/Services/WorkspaceProjectAuthTokenSlackChannelCache.test.ts +143 -0
- package/Tests/Server/Types/Database/Permissions/WorkflowVariableOAuthColumnPermission.test.ts +250 -0
- package/Tests/Server/Types/Database/QueryHelperJsonContains.test.ts +96 -0
- package/Tests/Server/Types/Markdown.test.ts +1033 -0
- package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +285 -0
- package/Tests/Server/Utils/AI/CodeFix/CodeFixAgentAuth.test.ts +159 -1
- package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +752 -0
- package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +1840 -0
- package/Tests/Server/Utils/AI/KubectlJobRunner.test.ts +1067 -0
- package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +355 -0
- package/Tests/Server/Utils/AI/PostIncidentStatusUpdateTool.test.ts +496 -0
- package/Tests/Server/Utils/AI/RemediationCommandTools.test.ts +305 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +1365 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +639 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +1585 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +1065 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +2138 -1
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +1035 -0
- package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +517 -0
- package/Tests/Server/Utils/AI/ResourceTools.test.ts +27 -1
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +1371 -24
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorRollbackRace.test.ts +962 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +518 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +939 -2
- package/Tests/Server/Utils/Database/ProjectScopedRelationReferences.test.ts +371 -0
- package/Tests/Server/Utils/Monitor/AffectedResourceList.test.ts +64 -0
- package/Tests/Server/Utils/Monitor/DatabaseMetricObservationRemediation.test.ts +102 -0
- package/Tests/Server/Utils/Monitor/MonitorAlertCriteriaReferences.test.ts +362 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorBreachingSamples.test.ts +55 -0
- package/Tests/Server/Utils/Monitor/MonitorEmailAddressRedaction.test.ts +255 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentCriteriaReferences.test.ts +347 -0
- package/Tests/Server/Utils/Monitor/MonitorIncidentMemberRoleMembership.test.ts +203 -0
- package/Tests/Server/Utils/Monitor/ProbeMonitorsNotification.test.ts +1147 -0
- package/Tests/Server/Utils/PushNotificationUtilCreators.test.ts +79 -0
- package/Tests/Server/Utils/ResourceFeed/ResourceFeedUtil.test.ts +21 -1
- package/Tests/Server/Utils/Rules/OwnerRuleAssignment.test.ts +155 -1
- package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +288 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.test.ts +304 -0
- package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +40 -3
- package/Tests/Server/Utils/Workflow/OAuth2TokenClient.test.ts +702 -0
- package/Tests/Server/Utils/Workflow/OAuth2TokenClientHttpIntegration.test.ts +507 -0
- package/Tests/Server/Utils/Workflow/WorkflowVariableOAuthToken.test.ts +831 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +63 -1
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsAppAccessToken.test.ts +256 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +60 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChatActionAuthorization.test.ts +442 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +44 -2
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsDirectMessage.test.ts +55 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsReactionNoteSync.test.ts +1522 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsServiceUrl.test.ts +130 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +4 -4
- package/Tests/Server/Utils/Workspace/Slack/SlackActionAuthorization.test.ts +691 -0
- package/Tests/Server/Utils/Workspace/Slack/SlackMessageByTimestamp.test.ts +253 -0
- package/Tests/Server/Utils/Workspace/Slack/SlackReactionNote.test.ts +574 -0
- package/Tests/Server/Utils/Workspace/WorkspaceActionAuthorization.test.ts +488 -0
- package/Tests/Server/Utils/Workspace/WorkspaceCreateProjectReferences.test.ts +861 -0
- package/Tests/Server/Utils/Workspace/WorkspaceMessageAuthor.test.ts +635 -0
- package/Tests/Server/Utils/Workspace/WorkspaceOAuthState.test.ts +548 -0
- package/Tests/Server/Utils/Workspace/WorkspaceReactionNote.test.ts +839 -0
- package/Tests/Server/Utils/Workspace/WorkspaceSendErrorMessage.test.ts +725 -0
- package/Tests/Types/AutoRemediation/AiRemediationCommandPlan.test.ts +283 -9
- package/Tests/Types/DateBrowserLocalWallClock.test.ts +280 -0
- package/Tests/Types/DateLocalShortMonthNameFallback.test.ts +389 -0
- package/Tests/Types/DateUserTimezone.test.ts +177 -4
- package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +306 -0
- package/Tests/Types/Kubernetes/KubernetesClusterAiAccess.test.ts +283 -0
- package/Tests/Types/Kubernetes/KubernetesRunnerPostureWriteScope.test.ts +68 -0
- package/Tests/Types/Monitor/DatabaseMonitor/SqlServerPlatform.test.ts +144 -0
- package/Tests/Types/Monitor/MonitorStepExceptionMonitor.test.ts +160 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +53 -0
- package/Tests/Types/Runbook/RunbookStepType.test.ts +91 -16
- package/Tests/Types/Runner/RunnerLiveStatus.test.ts +14 -3
- package/Tests/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.test.ts +15 -0
- package/Tests/Types/StatusPage/PublicNoteSubscriberNotificationDefault.test.ts +1868 -0
- package/Tests/Types/Timezone.test.ts +5 -0
- package/Tests/Types/TimezoneAlias.test.ts +1034 -0
- package/Tests/Types/Workflow/JiraTemplateFixtures.ts +833 -0
- package/Tests/Types/Workflow/JiraTemplateScripts.test.ts +5414 -0
- package/Tests/Types/Workflow/JiraTemplateSimulation.test.ts +4803 -0
- package/Tests/Types/Workflow/JiraTemplates.test.ts +5429 -0
- package/Tests/Types/Workflow/Templates.test.ts +79 -4
- package/Tests/Types/Workflow/WorkflowVariableOAuth.test.ts +520 -0
- package/Tests/Types/Workspace/WorkspaceNoteReaction.test.ts +177 -0
- package/Tests/UI/Components/AttributesJSON/AttributesJSONView.test.tsx +390 -0
- package/Tests/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.test.tsx +566 -0
- package/Tests/UI/Components/AttributesJSON/JSONHighlight.test.ts +131 -0
- package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +276 -1
- package/Tests/UI/Components/Calendar/Calendar.test.tsx +1611 -0
- package/Tests/UI/Components/Calendar/CalendarDayLayout.test.ts +1486 -0
- package/Tests/UI/Components/Calendar/CalendarDayLayoutDst.test.ts +632 -0
- package/Tests/UI/Components/Calendar/CalendarDst.test.tsx +388 -0
- package/Tests/UI/Components/Charts/ChartGroupPlotFloor.test.tsx +170 -0
- package/Tests/UI/Components/Charts/HistogramSelection.test.ts +282 -0
- package/Tests/UI/Components/CustomTimeRangeModal.test.tsx +6 -0
- package/Tests/UI/Components/CustomTimeRangeModalTimezoneTranslation.test.tsx +285 -0
- package/Tests/UI/Components/Dropdown.test.tsx +999 -2
- package/Tests/UI/Components/Forms/FormFieldCheckboxTranslation.test.tsx +216 -0
- package/Tests/UI/Components/Forms/FormFieldTimezoneDescriptionTranslation.test.tsx +461 -0
- package/Tests/UI/Components/Forms/IncidentEpisodePublicNoteNotifyPayload.test.tsx +641 -0
- package/Tests/UI/Components/Forms/IncidentPublicNoteNotifyPayload.test.tsx +346 -0
- package/Tests/UI/Components/Forms/ScheduledMaintenancePublicNoteNotifyPayload.test.tsx +493 -0
- package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +911 -2
- package/Tests/UI/Components/Graphs/DayUptimeGraphTimezone.NewYork.test.tsx +730 -0
- package/Tests/UI/Components/Graphs/DayUptimeGraphTimezone.Tokyo.test.tsx +506 -0
- package/Tests/UI/Components/HistogramClickToZoom.test.tsx +619 -0
- package/Tests/UI/Components/LogDetailsPanelAttributesJSON.test.tsx +248 -0
- package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +47 -1
- package/Tests/UI/Components/LogsViewerSmallScreens.test.tsx +620 -0
- package/Tests/UI/Components/ModelPageModelChange.test.tsx +242 -3
- package/Tests/UI/Components/ModelTable/BaseModelTableDeleteConfirmation.test.tsx +521 -0
- package/Tests/UI/Components/MonitorGraphs/Uptime.test.tsx +406 -2
- package/Tests/UI/Components/MonitorGraphs/UptimeBarDayModal.test.tsx +169 -2
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +266 -0
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerSmallScreens.test.tsx +439 -0
- package/Tests/UI/Components/TimezoneDropdownLegacyValues.test.tsx +704 -0
- package/Tests/UI/Components/UseHistogramZoom.test.tsx +89 -1
- package/Tests/UI/Components/UseLogsHistogram.test.tsx +175 -0
- package/Tests/UI/Monitor/MonitorSummarySnapshotRender.test.tsx +5 -5
- package/Tests/UI/Telemetry/TelemetryDetailPanel.test.tsx +28 -0
- package/Tests/UI/Utils/ProjectUsersModelAPI.test.ts +51 -0
- package/Tests/UI/Utils/Timezone.test.ts +747 -33
- package/Tests/UI/Workspace/SendTestNotificationButton.test.tsx +1854 -0
- package/Tests/UI/Workspace/WorkspaceCardsSendTestNotification.test.tsx +1864 -0
- package/Tests/Utils/AI/InvestigationReport.test.ts +435 -7
- package/Tests/Utils/AiRemediation/KubectlAllowlistValidatorParity.test.ts +1084 -0
- package/Tests/Utils/AiRemediation/KubectlOutputRedactor.test.ts +2531 -0
- package/Tests/Utils/AiRemediation/KubectlPolicy.test.ts +1895 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyAllowlist.test.ts +428 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +828 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyCustomResourceScope.test.ts +573 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyDenials.test.ts +1525 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyExposeOverrides.test.ts +275 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyNodePatch.test.ts +428 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyObjectWords.test.ts +195 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyPatchParents.test.ts +637 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyPodSecurityBaseline.test.ts +322 -0
- package/Tests/Utils/AiRemediation/KubectlPolicySafeWriteScope.test.ts +1026 -0
- package/Tests/Utils/AiRemediation/KubectlWaitBudget.test.ts +267 -0
- package/Tests/Utils/AiRemediation/KubectlWriteScope.test.ts +390 -0
- package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +777 -0
- package/Tests/Utils/AiRemediation/KubectlWriteScopeClusterScoped.test.ts +613 -0
- package/Tests/Utils/AiRemediation/KubectlWriteScopeEntryPoint.test.ts +576 -0
- package/Tests/Utils/AiRemediation/KubectlWriteScopeKubectlSpellings.test.ts +752 -0
- package/Tests/Utils/AiRemediation/KubectlWriteScopeObjectWords.test.ts +358 -0
- package/Tests/Utils/AiRemediation/KubectlWriteScopeOverrides.test.ts +293 -0
- package/Tests/Utils/AiRemediation/KubectlWriteScopeParityCases.ts +598 -0
- package/Tests/Utils/AiRemediation/KubectlWriteScopePolicyParity.test.ts +403 -0
- package/Tests/Utils/Dashboard/DashboardClockComponent.test.ts +154 -5
- package/Tests/Utils/Monitor/IncomingEmailMonitorAddress.test.ts +478 -0
- package/Tests/Utils/Monitor/MonitorCheckScheduleUtil.test.ts +646 -0
- package/Tests/Utils/Monitor/MonitorOverviewCriteriaUtil.test.ts +292 -0
- package/Tests/Utils/Monitor/MonitorOverviewFamily.test.ts +274 -0
- package/Tests/Utils/Monitor/MonitorOverviewPresentationUtil.test.ts +3100 -0
- package/Tests/Utils/Monitor/MonitorOverviewProbeUtil.test.ts +1412 -0
- package/Tests/Utils/Monitor/MonitorOverviewTargetUtil.test.ts +374 -0
- package/Tests/Utils/Monitor/MonitorStatusHistoryUtil.test.ts +332 -0
- package/Tests/Utils/Monitor/MonitorUptimeSummaryUtil.test.ts +946 -0
- package/Tests/Utils/Rum/SessionIdentity.test.ts +94 -0
- package/Tests/Utils/Rum/SessionReplayWafCompatibility.test.ts +840 -0
- package/Tests/Utils/Rum/SessionReplayWireEncoding.test.ts +208 -0
- package/Tests/Utils/Rum/UserFlow.test.ts +1010 -0
- package/Tests/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.test.ts +217 -0
- package/Tests/Utils/StatusPage/ResourceUptime.test.ts +1272 -1
- package/Tests/Utils/StatusPage/UptimeDailyAggregateTotals.test.ts +298 -0
- package/Tests/Utils/StatusPage/UptimeDailyAggregateUtil.test.ts +649 -1
- package/Tests/Utils/StatusPage/UptimeDailyAggregateWindow.test.ts +314 -0
- package/Tests/Utils/Telemetry/AttributesJSON.test.ts +454 -0
- package/Tests/Utils/Uptime/DayUptimeGraphUtil.test.ts +418 -2
- package/Tests/Utils/Uptime/DayUptimeGraphUtilMidnightDst.test.ts +160 -0
- package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Auckland.test.ts +45 -0
- package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Kolkata.test.ts +40 -0
- package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.London.test.ts +147 -0
- package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.NewYork.test.ts +48 -0
- package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezone.Tokyo.test.ts +39 -0
- package/Tests/Utils/Uptime/DayUptimeGraphUtilTimezoneCases.ts +1015 -0
- package/Tests/Utils/Uptime/UptimeUtil.test.ts +75 -0
- package/Tests/Utils/Uptime/UptimeUtilChronologicalOrder.test.ts +1279 -0
- package/Types/AutoRemediation/AiRemediationCommandPlan.ts +203 -5
- package/Types/Date.ts +121 -7
- package/Types/Email/EmailMessage.ts +8 -0
- package/Types/Email/EmailTemplateType.ts +1 -0
- package/Types/Exception/ExceptionMessages.ts +1 -0
- package/Types/Kubernetes/KubernetesClusterAiAccess.ts +488 -0
- package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +54 -0
- package/Types/Kubernetes/KubernetesClusterAiAccessToolNames.ts +17 -0
- package/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.ts +9 -0
- package/Types/Monitor/DatabaseMonitor/SqlServerPlatform.ts +117 -0
- package/Types/Monitor/KubernetesAlertTemplates.ts +6 -5
- package/Types/Monitor/MonitorStepExceptionMonitor.ts +56 -0
- package/Types/Monitor/MonitorUptimeSummary.ts +113 -0
- package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +11 -0
- package/Types/Rum/SessionReplay.ts +55 -2
- package/Types/Rum/SessionReplayApi.ts +44 -1
- package/Types/Rum/UserFlow.ts +187 -0
- package/Types/Runbook/RunbookStepType.ts +10 -0
- package/Types/Runbook/RunnerJobOrigin.ts +15 -0
- package/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.ts +6 -2
- package/Types/StatusPage/MergedDowntimeTotals.ts +17 -0
- package/Types/StatusPage/PublicNoteSubscriberNotificationDefault.ts +148 -0
- package/Types/StatusPage/UptimeDailyAggregate.ts +15 -0
- package/Types/Telemetry/TelemetryIngestSurface.ts +9 -0
- package/Types/Timezone.ts +5 -0
- package/Types/TimezoneAlias.ts +385 -0
- package/Types/Workflow/Templates.ts +3041 -2
- package/Types/Workflow/WorkflowVariableOAuth.ts +476 -0
- package/Types/Workspace/WorkspaceNoteReaction.ts +132 -0
- package/Typings/Index.d.ts +24 -0
- package/UI/Components/AttributesJSON/AttributesJSONPreferences.ts +175 -0
- package/UI/Components/AttributesJSON/AttributesJSONView.tsx +138 -0
- package/UI/Components/AttributesJSON/AttributesViewToggle.tsx +79 -0
- package/UI/Components/AttributesJSON/BracesGlyph.tsx +30 -0
- package/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.tsx +491 -0
- package/UI/Components/AttributesJSON/JSONHighlight.ts +77 -0
- package/UI/Components/Calendar/Calendar.css +83 -0
- package/UI/Components/Calendar/Calendar.tsx +136 -7
- package/UI/Components/Calendar/CalendarDayLayout.ts +236 -0
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +23 -1
- package/UI/Components/Charts/Utils/HistogramSelection.ts +117 -0
- package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +269 -0
- package/UI/Components/Charts/Utils/useHistogramZoom.ts +26 -2
- package/UI/Components/Date/CustomTimeRangeModal.tsx +26 -3
- package/UI/Components/Dropdown/Dropdown.tsx +58 -9
- package/UI/Components/Forms/Fields/FormField.tsx +49 -14
- package/UI/Components/Graphs/DayUptimeGraph.tsx +166 -25
- package/UI/Components/Graphs/UptimeBarTooltip.tsx +10 -2
- package/UI/Components/Graphs/UptimeDaySummary.tsx +8 -0
- package/UI/Components/LogsViewer/LogsViewer.tsx +52 -5
- package/UI/Components/LogsViewer/components/ColumnSelector.tsx +13 -2
- package/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.tsx +13 -1
- package/UI/Components/LogsViewer/components/LogDetailsPanel.tsx +91 -76
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +15 -1
- package/UI/Components/LogsViewer/components/LogsHistogram.tsx +28 -106
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +54 -6
- package/UI/Components/LogsViewer/components/SavedViewsDropdown.tsx +8 -2
- package/UI/Components/LogsViewer/useLogsHistogram.ts +39 -6
- package/UI/Components/ModelTable/BaseModelTable.tsx +66 -14
- package/UI/Components/MonitorGraphs/Uptime.tsx +12 -0
- package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +10 -2
- package/UI/Components/Page/ModelPage.tsx +59 -3
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +69 -5
- package/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.tsx +12 -1
- package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +15 -1
- package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +36 -104
- package/UI/Components/TelemetryViewer/components/TelemetrySearchBar.tsx +9 -1
- package/UI/Utils/ModelAPI/ProjectUsersModelAPI.ts +19 -1
- package/UI/Utils/Timezone.ts +69 -28
- package/Utils/AI/InvestigationReport.ts +195 -25
- package/Utils/AiRemediation/KubectlOutputRedactor.ts +1724 -0
- package/Utils/AiRemediation/KubectlPolicy.ts +4136 -0
- package/Utils/AiRemediation/KubectlWaitBudget.ts +156 -0
- package/Utils/AiRemediation/KubectlWriteScope.ts +1964 -0
- package/Utils/Dashboard/Components/DashboardClockComponent.ts +7 -6
- package/Utils/Monitor/IncomingEmailMonitorAddress.ts +292 -0
- package/Utils/Monitor/MonitorCheckScheduleUtil.ts +654 -0
- package/Utils/Monitor/MonitorOverviewCriteriaUtil.ts +188 -0
- package/Utils/Monitor/MonitorOverviewFamily.ts +263 -0
- package/Utils/Monitor/MonitorOverviewPresentationUtil.ts +1958 -0
- package/Utils/Monitor/MonitorOverviewProbeUtil.ts +979 -0
- package/Utils/Monitor/MonitorOverviewTargetUtil.ts +299 -0
- package/Utils/Monitor/MonitorStatusHistoryUtil.ts +201 -0
- package/Utils/Monitor/MonitorUptimeSummaryUtil.ts +638 -0
- package/Utils/Rum/SessionIdentity.ts +44 -0
- package/Utils/Rum/SessionReplayWireEncoding.ts +137 -0
- package/Utils/Rum/UserFlow.ts +1527 -0
- package/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.ts +107 -0
- package/Utils/StatusPage/ResourceUptime.ts +99 -0
- package/Utils/StatusPage/UptimeDailyAggregateUtil.ts +197 -0
- package/Utils/Telemetry/AttributesJSON.ts +367 -0
- package/Utils/Uptime/DayUptimeGraphUtil.ts +165 -0
- package/Utils/Uptime/UptimeUtil.ts +122 -38
- package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js +8 -1
- package/build/dist/Models/DatabaseModels/AlertEpisodeMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js +62 -0
- package/build/dist/Models/DatabaseModels/AutoRemediationSuggestion.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js +8 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +10 -3
- package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +10 -3
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +434 -0
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +49 -0
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Runner.js +4 -4
- package/build/dist/Models/DatabaseModels/Runner.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunnerJob.js +66 -0
- package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +11 -3
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js +487 -3
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js +64 -16
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +205 -0
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/AutoRemediationAPI.js +115 -2
- package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +49 -0
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +564 -0
- package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -0
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +465 -176
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/MonitorAPI.js +107 -1
- package/build/dist/Server/API/MonitorAPI.js.map +1 -1
- package/build/dist/Server/API/OnCallScheduleTimelineAPI.js +5 -16
- package/build/dist/Server/API/OnCallScheduleTimelineAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +322 -78
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +123 -17
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +144 -5
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js +1 -0
- package/build/dist/Server/API/UserTwoFactorBackupCodeAPI.js.map +1 -1
- package/build/dist/Server/API/WorkflowVariableAPI.js +109 -0
- package/build/dist/Server/API/WorkflowVariableAPI.js.map +1 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js +29 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.js +38 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794300000000-AddOAuth2WorkflowVariables.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.js +54 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794400000000-AddKubernetesClusterAiAccess.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794500000000-AddIncomingEmailCustomAddressToMonitor.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794600000000-AddAuthTokenExpiresAtToWorkspaceProjectAuthToken.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.js +32 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794700000000-RemoveAppAccessTokenFromWorkspaceProjectAuthTokenMiscData.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js +21 -0
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +32 -2
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +39 -6
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/AIAgentService.js +1 -0
- package/build/dist/Server/Services/AIAgentService.js.map +1 -1
- package/build/dist/Server/Services/AccessTokenService.js +87 -42
- package/build/dist/Server/Services/AccessTokenService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeMemberService.js +4 -0
- package/build/dist/Server/Services/AlertEpisodeMemberService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeService.js +12 -1
- package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +110 -23
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js +716 -30
- package/build/dist/Server/Services/AutoRemediationRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleService.js +100 -0
- package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +1 -0
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/InboundEmail/Providers/SendGridInboundProvider.js +11 -16
- package/build/dist/Server/Services/InboundEmail/Providers/SendGridInboundProvider.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js +4 -0
- package/build/dist/Server/Services/IncidentEpisodeMemberService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +48 -0
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeRoleMemberService.js +21 -0
- package/build/dist/Server/Services/IncidentEpisodeRoleMemberService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +12 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentGroupingEngineService.js +8 -0
- package/build/dist/Server/Services/IncidentGroupingEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentMemberService.js +14 -0
- package/build/dist/Server/Services/IncidentMemberService.js.map +1 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js +48 -0
- package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +99 -21
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +12 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentTemplateService.js +68 -4
- package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +1470 -0
- package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesClusterService.js +1097 -0
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +14 -6
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +487 -48
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusTimelineService.js +540 -3
- package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +2 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js +2 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleTeamService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js +2 -0
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleUserService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +3 -0
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallNotificationAlertingService.js +29 -7
- package/build/dist/Server/Services/OnCallNotificationAlertingService.js.map +1 -1
- package/build/dist/Server/Services/OnCallSetupReminderService.js +1 -0
- package/build/dist/Server/Services/OnCallSetupReminderService.js.map +1 -1
- package/build/dist/Server/Services/ProbeService.js +7 -1
- package/build/dist/Server/Services/ProbeService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +11 -1
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/RunbookCredentialService.js +24 -0
- package/build/dist/Server/Services/RunbookCredentialService.js.map +1 -1
- package/build/dist/Server/Services/RunbookSecretService.js +44 -0
- package/build/dist/Server/Services/RunbookSecretService.js.map +1 -1
- package/build/dist/Server/Services/RunnerJobService.js +641 -21
- package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
- package/build/dist/Server/Services/RunnerService.js +344 -5
- package/build/dist/Server/Services/RunnerService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +48 -0
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +102 -20
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js +67 -4
- package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js +12 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserService.js +1 -0
- package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +230 -32
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +4 -0
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +270 -11
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/TraceAggregationService.js +10 -6
- package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
- package/build/dist/Server/Services/UserEmailService.js +1 -0
- package/build/dist/Server/Services/UserEmailService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js +1 -0
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js +1 -0
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +4 -0
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +107 -1
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/WorkflowVariableService.js +396 -7
- package/build/dist/Server/Services/WorkflowVariableService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +463 -7
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +144 -2
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +20 -0
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +394 -27
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +123 -0
- package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -0
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +344 -0
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -0
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +587 -0
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js +18 -2
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentAuth.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +1380 -239
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +1099 -68
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +88 -6
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +45 -11
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +41 -11
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js +11 -4
- package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +4 -2
- package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +997 -141
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
- package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js +299 -4
- package/build/dist/Server/Utils/AutoRemediation/RemediationVerifier.js.map +1 -1
- package/build/dist/Server/Utils/Database/AffectedResourceRelations.js +120 -0
- package/build/dist/Server/Utils/Database/AffectedResourceRelations.js.map +1 -0
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +160 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +1 -0
- package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +59 -12
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +5 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +84 -22
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js +70 -5
- package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/ProbeMonitorsNotification.js +249 -0
- package/build/dist/Server/Utils/Monitor/ProbeMonitorsNotification.js.map +1 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js +1 -0
- package/build/dist/Server/Utils/OnCall/OnCallShiftReminderRunner.js.map +1 -1
- package/build/dist/Server/Utils/PushNotificationUtil.js +27 -0
- package/build/dist/Server/Utils/PushNotificationUtil.js.map +1 -1
- package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js +26 -3
- package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js.map +1 -1
- package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js +38 -3
- package/build/dist/Server/Utils/Runbook/RunbookExecutePermission.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +143 -23
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.js +406 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.js.map +1 -0
- package/build/dist/Server/Utils/StartServer.js +4 -3
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +382 -0
- package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -0
- package/build/dist/Server/Utils/TwoFactorBackupCodeNotification.js +1 -0
- package/build/dist/Server/Utils/TwoFactorBackupCodeNotification.js.map +1 -1
- package/build/dist/Server/Utils/UserPermission/UserPermission.js +10 -0
- package/build/dist/Server/Utils/UserPermission/UserPermission.js.map +1 -1
- package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js +447 -0
- package/build/dist/Server/Utils/Workflow/OAuth2TokenClient.js.map +1 -0
- package/build/dist/Server/Utils/Workflow/WorkflowVariableOAuthToken.js +202 -0
- package/build/dist/Server/Utils/Workflow/WorkflowVariableOAuthToken.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +39 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js +44 -4
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/AlertEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +150 -191
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js +44 -4
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js +39 -17
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +151 -145
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +221 -127
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.js +91 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeamsServiceUrl.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.js +673 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/ReactionNoteSync.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js +3 -9
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ActionTypes.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js +65 -166
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js +63 -157
- package/build/dist/Server/Utils/Workspace/Slack/Actions/AlertEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js +23 -3
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Authorization.js +92 -0
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Authorization.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js +81 -210
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js +68 -138
- package/build/dist/Server/Utils/Workspace/Slack/Actions/IncidentEpisode.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ReactionNote.js +240 -0
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ReactionNote.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js +85 -212
- package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +60 -17
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/app-manifest.json +2 -0
- package/build/dist/Server/Utils/Workspace/Workspace.js +134 -41
- package/build/dist/Server/Utils/Workspace/Workspace.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceActionAuthorization.js +197 -0
- package/build/dist/Server/Utils/Workspace/WorkspaceActionAuthorization.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/WorkspaceBase.js +32 -0
- package/build/dist/Server/Utils/Workspace/WorkspaceBase.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceOAuthState.js +199 -0
- package/build/dist/Server/Utils/Workspace/WorkspaceOAuthState.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.js +91 -0
- package/build/dist/Server/Utils/Workspace/WorkspaceProjectReferenceValidator.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/WorkspaceReactionNote.js +450 -0
- package/build/dist/Server/Utils/Workspace/WorkspaceReactionNote.js.map +1 -0
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +103 -5
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
- package/build/dist/Types/Date.js +97 -7
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Email/EmailTemplateType.js +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/Exception/ExceptionMessages.js +1 -0
- package/build/dist/Types/Exception/ExceptionMessages.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +245 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +49 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessToolNames.js +16 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessToolNames.js.map +1 -0
- package/build/dist/Types/Monitor/DatabaseMonitor/SqlServerPlatform.js +104 -0
- package/build/dist/Types/Monitor/DatabaseMonitor/SqlServerPlatform.js.map +1 -0
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +6 -5
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js +32 -0
- package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorUptimeSummary.js +52 -0
- package/build/dist/Types/Monitor/MonitorUptimeSummary.js.map +1 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +9 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplay.js +52 -2
- package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplayApi.js +11 -0
- package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -1
- package/build/dist/Types/Rum/UserFlow.js +115 -0
- package/build/dist/Types/Rum/UserFlow.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookStepType.js +10 -0
- package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
- package/build/dist/Types/Runbook/RunnerJobOrigin.js +14 -0
- package/build/dist/Types/Runbook/RunnerJobOrigin.js.map +1 -1
- package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js +2 -2
- package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js.map +1 -1
- package/build/dist/Types/StatusPage/MergedDowntimeTotals.js +2 -0
- package/build/dist/Types/StatusPage/MergedDowntimeTotals.js.map +1 -0
- package/build/dist/Types/StatusPage/PublicNoteSubscriberNotificationDefault.js +65 -0
- package/build/dist/Types/StatusPage/PublicNoteSubscriberNotificationDefault.js.map +1 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +8 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
- package/build/dist/Types/Timezone.js +5 -0
- package/build/dist/Types/Timezone.js.map +1 -1
- package/build/dist/Types/TimezoneAlias.js +340 -0
- package/build/dist/Types/TimezoneAlias.js.map +1 -0
- package/build/dist/Types/Workflow/Templates.js +2694 -2
- package/build/dist/Types/Workflow/Templates.js.map +1 -1
- package/build/dist/Types/Workflow/WorkflowVariableOAuth.js +314 -0
- package/build/dist/Types/Workflow/WorkflowVariableOAuth.js.map +1 -0
- package/build/dist/Types/Workspace/WorkspaceNoteReaction.js +98 -0
- package/build/dist/Types/Workspace/WorkspaceNoteReaction.js.map +1 -0
- package/build/dist/UI/Components/AttributesJSON/AttributesJSONPreferences.js +95 -0
- package/build/dist/UI/Components/AttributesJSON/AttributesJSONPreferences.js.map +1 -0
- package/build/dist/UI/Components/AttributesJSON/AttributesJSONView.js +44 -0
- package/build/dist/UI/Components/AttributesJSON/AttributesJSONView.js.map +1 -0
- package/build/dist/UI/Components/AttributesJSON/AttributesViewToggle.js +27 -0
- package/build/dist/UI/Components/AttributesJSON/AttributesViewToggle.js.map +1 -0
- package/build/dist/UI/Components/AttributesJSON/BracesGlyph.js +9 -0
- package/build/dist/UI/Components/AttributesJSON/BracesGlyph.js.map +1 -0
- package/build/dist/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.js +260 -0
- package/build/dist/UI/Components/AttributesJSON/CopyAttributesAsJSONButton.js.map +1 -0
- package/build/dist/UI/Components/AttributesJSON/JSONHighlight.js +56 -0
- package/build/dist/UI/Components/AttributesJSON/JSONHighlight.js.map +1 -0
- package/build/dist/UI/Components/Calendar/Calendar.js +94 -7
- package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
- package/build/dist/UI/Components/Calendar/CalendarDayLayout.js +136 -0
- package/build/dist/UI/Components/Calendar/CalendarDayLayout.js.map +1 -0
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/HistogramSelection.js +72 -0
- package/build/dist/UI/Components/Charts/Utils/HistogramSelection.js.map +1 -0
- package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +178 -0
- package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -0
- package/build/dist/UI/Components/Charts/Utils/useHistogramZoom.js +13 -1
- package/build/dist/UI/Components/Charts/Utils/useHistogramZoom.js.map +1 -1
- package/build/dist/UI/Components/Date/CustomTimeRangeModal.js +20 -4
- package/build/dist/UI/Components/Date/CustomTimeRangeModal.js.map +1 -1
- package/build/dist/UI/Components/Dropdown/Dropdown.js +33 -9
- package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +36 -13
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +117 -11
- package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
- package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +5 -1
- package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -1
- package/build/dist/UI/Components/Graphs/UptimeDaySummary.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +36 -8
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js +10 -2
- package/build/dist/UI/Components/LogsViewer/components/ColumnSelector.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js +9 -1
- package/build/dist/UI/Components/LogsViewer/components/KeyboardShortcutsHelp.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js +16 -15
- package/build/dist/UI/Components/LogsViewer/components/LogDetailsPanel.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +14 -74
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +22 -5
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js +9 -2
- package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +16 -5
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +23 -8
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/MonitorGraphs/Uptime.js +7 -1
- package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
- package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +5 -1
- package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -1
- package/build/dist/UI/Components/Page/ModelPage.js +37 -3
- package/build/dist/UI/Components/Page/ModelPage.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +27 -6
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js +7 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryDetailPanel.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +17 -77
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js +10 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js +15 -1
- package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/Timezone.js +52 -17
- package/build/dist/UI/Utils/Timezone.js.map +1 -1
- package/build/dist/Utils/AI/InvestigationReport.js +116 -21
- package/build/dist/Utils/AI/InvestigationReport.js.map +1 -1
- package/build/dist/Utils/AiRemediation/KubectlOutputRedactor.js +1209 -0
- package/build/dist/Utils/AiRemediation/KubectlOutputRedactor.js.map +1 -0
- package/build/dist/Utils/AiRemediation/KubectlPolicy.js +2973 -0
- package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -0
- package/build/dist/Utils/AiRemediation/KubectlWaitBudget.js +99 -0
- package/build/dist/Utils/AiRemediation/KubectlWaitBudget.js.map +1 -0
- package/build/dist/Utils/AiRemediation/KubectlWriteScope.js +1429 -0
- package/build/dist/Utils/AiRemediation/KubectlWriteScope.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js +7 -6
- package/build/dist/Utils/Dashboard/Components/DashboardClockComponent.js.map +1 -1
- package/build/dist/Utils/Monitor/IncomingEmailMonitorAddress.js +214 -0
- package/build/dist/Utils/Monitor/IncomingEmailMonitorAddress.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorCheckScheduleUtil.js +471 -0
- package/build/dist/Utils/Monitor/MonitorCheckScheduleUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorOverviewCriteriaUtil.js +107 -0
- package/build/dist/Utils/Monitor/MonitorOverviewCriteriaUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorOverviewFamily.js +191 -0
- package/build/dist/Utils/Monitor/MonitorOverviewFamily.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorOverviewPresentationUtil.js +1259 -0
- package/build/dist/Utils/Monitor/MonitorOverviewPresentationUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorOverviewProbeUtil.js +593 -0
- package/build/dist/Utils/Monitor/MonitorOverviewProbeUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorOverviewTargetUtil.js +195 -0
- package/build/dist/Utils/Monitor/MonitorOverviewTargetUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorStatusHistoryUtil.js +127 -0
- package/build/dist/Utils/Monitor/MonitorStatusHistoryUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorUptimeSummaryUtil.js +464 -0
- package/build/dist/Utils/Monitor/MonitorUptimeSummaryUtil.js.map +1 -0
- package/build/dist/Utils/Rum/SessionIdentity.js +31 -1
- package/build/dist/Utils/Rum/SessionIdentity.js.map +1 -1
- package/build/dist/Utils/Rum/SessionReplayWireEncoding.js +128 -0
- package/build/dist/Utils/Rum/SessionReplayWireEncoding.js.map +1 -0
- package/build/dist/Utils/Rum/UserFlow.js +941 -0
- package/build/dist/Utils/Rum/UserFlow.js.map +1 -0
- package/build/dist/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.js +82 -0
- package/build/dist/Utils/StatusPage/MonitorGroupMergedDowntimeUtil.js.map +1 -0
- package/build/dist/Utils/StatusPage/ResourceUptime.js +46 -0
- package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
- package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js +148 -0
- package/build/dist/Utils/StatusPage/UptimeDailyAggregateUtil.js.map +1 -1
- package/build/dist/Utils/Telemetry/AttributesJSON.js +245 -0
- package/build/dist/Utils/Telemetry/AttributesJSON.js.map +1 -0
- package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js +103 -0
- package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js.map +1 -1
- package/build/dist/Utils/Uptime/UptimeUtil.js +91 -27
- package/build/dist/Utils/Uptime/UptimeUtil.js.map +1 -1
- package/jest.config.json +2 -1
- package/package.json +2 -1
- package/tsconfig.tests.json +36 -0
- package/Tests/UI/Components/EmptyState/EmptyStateGuideLinks.test.tsx +0 -337
- package/UI/Components/EmptyState/EmptyStateGuideLinks.tsx +0 -124
- package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js +0 -47
- package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js.map +0 -1
|
@@ -0,0 +1,1585 @@
|
|
|
1
|
+
import RemediationCommandToolkit, {
|
|
2
|
+
INLINE_COMMAND_STEP_ID_PREFIX,
|
|
3
|
+
RemediationCommandNeedingApproval,
|
|
4
|
+
RemediationCommandToolkitOptions,
|
|
5
|
+
} from "../../../../Server/Utils/AI/Remediation/RemediationCommandTools";
|
|
6
|
+
import { ObservabilityAssistantExtraTool } from "../../../../Server/Utils/AI/Chat/ObservabilityAssistant";
|
|
7
|
+
import { ToolCallOutcome } from "../../../../Server/Utils/AI/Toolbox/Index";
|
|
8
|
+
import AIRunService from "../../../../Server/Services/AIRunService";
|
|
9
|
+
import AutoRemediationSuggestionService from "../../../../Server/Services/AutoRemediationSuggestionService";
|
|
10
|
+
import KubernetesClusterAiAccessService from "../../../../Server/Services/KubernetesClusterAiAccessService";
|
|
11
|
+
import RunnerJobService from "../../../../Server/Services/RunnerJobService";
|
|
12
|
+
import RunnerService from "../../../../Server/Services/RunnerService";
|
|
13
|
+
import { MAX_AUTO_EXECUTIONS_PER_RULE_PER_HOUR } from "../../../../Server/Services/AutoRemediationRuleEngineService";
|
|
14
|
+
import Semaphore, {
|
|
15
|
+
SemaphoreMutex,
|
|
16
|
+
} from "../../../../Server/Infrastructure/Semaphore";
|
|
17
|
+
import logger from "../../../../Server/Utils/Logger";
|
|
18
|
+
import AutoRemediationSuggestion from "../../../../Models/DatabaseModels/AutoRemediationSuggestion";
|
|
19
|
+
import RunnerJob from "../../../../Models/DatabaseModels/RunnerJob";
|
|
20
|
+
import AutoRemediationExecutionMode from "../../../../Types/AutoRemediation/AutoRemediationExecutionMode";
|
|
21
|
+
import AutoRemediationSuggestionStatus from "../../../../Types/AutoRemediation/AutoRemediationSuggestionStatus";
|
|
22
|
+
import AutoRemediationVerificationStatus from "../../../../Types/AutoRemediation/AutoRemediationVerificationStatus";
|
|
23
|
+
import {
|
|
24
|
+
AiRemediationCommandPolicyVerdict,
|
|
25
|
+
MAX_PLAN_COMMANDS,
|
|
26
|
+
} from "../../../../Types/AutoRemediation/AiRemediationCommandPlan";
|
|
27
|
+
import {
|
|
28
|
+
KubectlCommandTier,
|
|
29
|
+
KubernetesAiRemediationMode,
|
|
30
|
+
KubernetesClusterAiAccessStatus,
|
|
31
|
+
} from "../../../../Types/Kubernetes/KubernetesClusterAiAccess";
|
|
32
|
+
import RunnerJobStatus from "../../../../Types/Runbook/RunnerJobStatus";
|
|
33
|
+
import { JSONObject } from "../../../../Types/JSON";
|
|
34
|
+
import ObjectID from "../../../../Types/ObjectID";
|
|
35
|
+
import PositiveNumber from "../../../../Types/PositiveNumber";
|
|
36
|
+
import { afterEach, beforeEach, describe, expect, it } from "@jest/globals";
|
|
37
|
+
|
|
38
|
+
/*
|
|
39
|
+
* Contract under test — what an unattended (FullAuto) run checks right
|
|
40
|
+
* before EACH inline kubectl change, beyond the policy tier:
|
|
41
|
+
*
|
|
42
|
+
* - the cluster's AI page is re-read LIVE for every command. A cluster
|
|
43
|
+
* whose remediation was turned off, lost readiness (project switches
|
|
44
|
+
* included), was re-bound to another Runner or credential, or cannot be
|
|
45
|
+
* read at all refuses the command — nothing is enqueued, nothing is
|
|
46
|
+
* recorded — and stays off-limits for the rest of the run. A cluster
|
|
47
|
+
* moved to "ask for approval", or from Bypass approval to Automatic, or
|
|
48
|
+
* whose allowlist shrank, is judged under its CURRENT mode and
|
|
49
|
+
* allowlist; what it refuses only for want of a human's click is kept
|
|
50
|
+
* for the round's proposal;
|
|
51
|
+
* - a kubectl READ sent through the execute tool is refused and pointed at
|
|
52
|
+
* run_kubectl: it would otherwise count as an executed fix;
|
|
53
|
+
* - the run's FIRST change on a cluster takes one of the cluster's hourly
|
|
54
|
+
* unattended slots under a per-cluster lock held until the job is
|
|
55
|
+
* enqueued. Three other runs already in the window refuse it; a lock or
|
|
56
|
+
* a count that cannot be had refuses it too; the run's later changes on
|
|
57
|
+
* the same cluster hold the slot already. Two runs racing at 2 of 3: the
|
|
58
|
+
* lock lets exactly one through;
|
|
59
|
+
* - on an Automatic cluster a rollback must be a safe change: `set image`
|
|
60
|
+
* back is refused, `rollout undo` is accepted — which is exactly what the
|
|
61
|
+
* persona recommends.
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
const PROJECT_ID: ObjectID = new ObjectID(
|
|
65
|
+
"22222222-2222-4222-8222-222222222222",
|
|
66
|
+
);
|
|
67
|
+
const RUN_ID: ObjectID = new ObjectID("88888888-8888-4888-8888-888888888888");
|
|
68
|
+
const SUGGESTION_ID: ObjectID = new ObjectID(
|
|
69
|
+
"77777777-7777-4777-8777-777777777777",
|
|
70
|
+
);
|
|
71
|
+
const OTHER_SUGGESTION_ID: ObjectID = new ObjectID(
|
|
72
|
+
"79797979-7979-4979-8979-797979797979",
|
|
73
|
+
);
|
|
74
|
+
const CLUSTER_ID: ObjectID = new ObjectID(
|
|
75
|
+
"33333333-3333-4333-8333-333333333333",
|
|
76
|
+
);
|
|
77
|
+
const RUNNER_ID: ObjectID = new ObjectID(
|
|
78
|
+
"44444444-4444-4444-8444-444444444444",
|
|
79
|
+
);
|
|
80
|
+
const OTHER_RUNNER_ID: ObjectID = new ObjectID(
|
|
81
|
+
"45454545-4545-4545-8545-454545454545",
|
|
82
|
+
);
|
|
83
|
+
const CREDENTIAL_ID: string = "55555555-5555-4555-8555-555555555555";
|
|
84
|
+
const JOB_ID: ObjectID = new ObjectID("66666666-6666-4666-8666-666666666666");
|
|
85
|
+
|
|
86
|
+
const SAFE_WRITE: string = "kubectl rollout restart deployment/web -n web";
|
|
87
|
+
const SAFE_UNDO: string = "kubectl rollout undo deployment/web -n web";
|
|
88
|
+
const RISKY_WRITE: string = "kubectl set image deployment/web web=img:2 -n web";
|
|
89
|
+
const RISKY_UNDO: string = "kubectl set image deployment/web web=img:1 -n web";
|
|
90
|
+
|
|
91
|
+
function cluster(
|
|
92
|
+
overrides: Partial<KubernetesClusterAiAccessStatus> = {},
|
|
93
|
+
): KubernetesClusterAiAccessStatus {
|
|
94
|
+
return {
|
|
95
|
+
clusterId: CLUSTER_ID.toString(),
|
|
96
|
+
clusterName: "prod-us",
|
|
97
|
+
clusterIdentifier: "prod-us",
|
|
98
|
+
runner: {
|
|
99
|
+
id: RUNNER_ID.toString(),
|
|
100
|
+
name: "kubernetes-agent/prod-us",
|
|
101
|
+
isOnline: true,
|
|
102
|
+
canRunAiCommands: true,
|
|
103
|
+
posture: { inCluster: true, allowWrites: true },
|
|
104
|
+
},
|
|
105
|
+
accessMethod: "in_cluster",
|
|
106
|
+
kubectlAllowlist: [],
|
|
107
|
+
isInvestigationEnabled: true,
|
|
108
|
+
isInvestigationReady: true,
|
|
109
|
+
remediationMode: KubernetesAiRemediationMode.Automatic,
|
|
110
|
+
isRemediationReady: true,
|
|
111
|
+
gaps: [],
|
|
112
|
+
evaluatedAt: new Date().toISOString(),
|
|
113
|
+
...overrides,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function buildToolkit(
|
|
118
|
+
overrides: Partial<RemediationCommandToolkitOptions> = {},
|
|
119
|
+
): RemediationCommandToolkit {
|
|
120
|
+
return new RemediationCommandToolkit({
|
|
121
|
+
projectId: PROJECT_ID,
|
|
122
|
+
aiRunId: RUN_ID,
|
|
123
|
+
suggestionId: SUGGESTION_ID,
|
|
124
|
+
mode: "FullAuto",
|
|
125
|
+
allowlistPatterns: [],
|
|
126
|
+
allowedRunnerIds: [],
|
|
127
|
+
clusterTargets: [cluster()],
|
|
128
|
+
...overrides,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function execute(
|
|
133
|
+
toolkit: RemediationCommandToolkit,
|
|
134
|
+
args: JSONObject,
|
|
135
|
+
): Promise<ToolCallOutcome> {
|
|
136
|
+
const tool: ObservabilityAssistantExtraTool | undefined = toolkit
|
|
137
|
+
.buildTools()
|
|
138
|
+
.find((candidate: ObservabilityAssistantExtraTool) => {
|
|
139
|
+
return candidate.definition.name === "execute_remediation_command";
|
|
140
|
+
});
|
|
141
|
+
if (!tool) {
|
|
142
|
+
throw new Error("execute_remediation_command is not offered.");
|
|
143
|
+
}
|
|
144
|
+
return tool.execute(args);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function listTargets(
|
|
148
|
+
toolkit: RemediationCommandToolkit,
|
|
149
|
+
): Promise<ToolCallOutcome> {
|
|
150
|
+
const tool: ObservabilityAssistantExtraTool | undefined = toolkit
|
|
151
|
+
.buildTools()
|
|
152
|
+
.find((candidate: ObservabilityAssistantExtraTool) => {
|
|
153
|
+
return candidate.definition.name === "list_command_targets";
|
|
154
|
+
});
|
|
155
|
+
return tool!.execute({});
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function kubectlArgs(
|
|
159
|
+
overrides: Partial<Record<string, unknown>> = {},
|
|
160
|
+
): JSONObject {
|
|
161
|
+
return {
|
|
162
|
+
stepType: "Kubectl",
|
|
163
|
+
kubernetesClusterId: CLUSTER_ID.toString(),
|
|
164
|
+
command: SAFE_WRITE,
|
|
165
|
+
rationale: "web pods are crash-looping after a config change",
|
|
166
|
+
expectedEffect: "fresh pods come up Running",
|
|
167
|
+
rollbackCommand: SAFE_UNDO,
|
|
168
|
+
...overrides,
|
|
169
|
+
} as JSONObject;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function fakeJob(overrides: Partial<Record<string, unknown>> = {}): RunnerJob {
|
|
173
|
+
return {
|
|
174
|
+
id: JOB_ID,
|
|
175
|
+
_id: JOB_ID.toString(),
|
|
176
|
+
status: RunnerJobStatus.Succeeded,
|
|
177
|
+
exitCode: 0,
|
|
178
|
+
output: "deployment.apps/web restarted",
|
|
179
|
+
payload: { displayCommand: SAFE_WRITE },
|
|
180
|
+
...overrides,
|
|
181
|
+
} as unknown as RunnerJob;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// An inline kubectl job row, as the breaker reads it, for another run.
|
|
185
|
+
function inlineJobOf(suggestionId: ObjectID): RunnerJob {
|
|
186
|
+
return {
|
|
187
|
+
id: ObjectID.generate(),
|
|
188
|
+
_id: ObjectID.generate().toString(),
|
|
189
|
+
autoRemediationSuggestionId: suggestionId,
|
|
190
|
+
} as unknown as RunnerJob;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let enqueueKubectl: jest.SpyInstance;
|
|
194
|
+
let persist: jest.SpyInstance;
|
|
195
|
+
let liveStatus: jest.SpyInstance;
|
|
196
|
+
let lock: jest.SpyInstance;
|
|
197
|
+
let release: jest.SpyInstance;
|
|
198
|
+
let jobFindBy: jest.SpyInstance;
|
|
199
|
+
|
|
200
|
+
function mockHappyPath(): void {
|
|
201
|
+
jest.spyOn(logger, "error").mockImplementation((): void => {
|
|
202
|
+
return undefined;
|
|
203
|
+
});
|
|
204
|
+
jest.spyOn(logger, "warn").mockImplementation((): void => {
|
|
205
|
+
return undefined;
|
|
206
|
+
});
|
|
207
|
+
jest
|
|
208
|
+
.spyOn(AutoRemediationSuggestionService, "findOneById")
|
|
209
|
+
.mockResolvedValue({
|
|
210
|
+
id: SUGGESTION_ID,
|
|
211
|
+
_id: SUGGESTION_ID.toString(),
|
|
212
|
+
status: AutoRemediationSuggestionStatus.Planning,
|
|
213
|
+
} as unknown as AutoRemediationSuggestion);
|
|
214
|
+
persist = jest
|
|
215
|
+
.spyOn(AutoRemediationSuggestionService, "updateOneById")
|
|
216
|
+
.mockResolvedValue(undefined as never);
|
|
217
|
+
jest
|
|
218
|
+
.spyOn(RunnerJobService, "countBy")
|
|
219
|
+
.mockResolvedValue(new PositiveNumber(0));
|
|
220
|
+
enqueueKubectl = jest
|
|
221
|
+
.spyOn(RunnerJobService, "enqueueAiKubectlCommand")
|
|
222
|
+
.mockResolvedValue(fakeJob({ status: RunnerJobStatus.Pending }));
|
|
223
|
+
jest
|
|
224
|
+
.spyOn(RunnerJobService, "pollUntilTerminal")
|
|
225
|
+
.mockResolvedValue(fakeJob());
|
|
226
|
+
jest.spyOn(AIRunService, "updateOneBy").mockResolvedValue(undefined as never);
|
|
227
|
+
jest
|
|
228
|
+
.spyOn(KubernetesClusterAiAccessService, "recordCommandOutcome")
|
|
229
|
+
.mockResolvedValue(undefined);
|
|
230
|
+
jest
|
|
231
|
+
.spyOn(RunnerService, "getOnlineAiCommandRunnersForProject")
|
|
232
|
+
.mockResolvedValue([]);
|
|
233
|
+
// Nothing changed on the cluster's AI page since the run started.
|
|
234
|
+
liveStatus = jest
|
|
235
|
+
.spyOn(KubernetesClusterAiAccessService, "getStatusForCluster")
|
|
236
|
+
.mockResolvedValue(cluster());
|
|
237
|
+
lock = jest
|
|
238
|
+
.spyOn(Semaphore, "lock")
|
|
239
|
+
.mockResolvedValue({ id: "cluster-lock" } as unknown as SemaphoreMutex);
|
|
240
|
+
release = jest.spyOn(Semaphore, "release").mockResolvedValue(undefined);
|
|
241
|
+
// An empty breaker window.
|
|
242
|
+
jest
|
|
243
|
+
.spyOn(AutoRemediationSuggestionService, "countBy")
|
|
244
|
+
.mockResolvedValue(new PositiveNumber(0));
|
|
245
|
+
jest.spyOn(AutoRemediationSuggestionService, "findBy").mockResolvedValue([]);
|
|
246
|
+
jobFindBy = jest.spyOn(RunnerJobService, "findBy").mockResolvedValue([]);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function expectNothingRanOrRecorded(
|
|
250
|
+
toolkit: RemediationCommandToolkit,
|
|
251
|
+
outcome: ToolCallOutcome,
|
|
252
|
+
): void {
|
|
253
|
+
expect(outcome.success).toBe(false);
|
|
254
|
+
expect(enqueueKubectl).not.toHaveBeenCalled();
|
|
255
|
+
// No Pending audit record: the retry/sweeper paths would read it as "ran".
|
|
256
|
+
expect(persist).not.toHaveBeenCalled();
|
|
257
|
+
expect(toolkit.getExecutedCommands()).toHaveLength(0);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
describe("RemediationCommandToolkit re-reads the cluster's AI page before every inline kubectl change", () => {
|
|
261
|
+
beforeEach(mockHappyPath);
|
|
262
|
+
afterEach(() => {
|
|
263
|
+
jest.restoreAllMocks();
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("executes when the live page still matches the run's snapshot, with the snapshot's Runner, credential and step id — one live read per command", async () => {
|
|
267
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
268
|
+
clusterTargets: [
|
|
269
|
+
cluster({ accessMethod: "credential", credentialId: CREDENTIAL_ID }),
|
|
270
|
+
],
|
|
271
|
+
});
|
|
272
|
+
liveStatus.mockResolvedValue(
|
|
273
|
+
cluster({ accessMethod: "credential", credentialId: CREDENTIAL_ID }),
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
const first: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
277
|
+
const second: ToolCallOutcome = await execute(
|
|
278
|
+
toolkit,
|
|
279
|
+
kubectlArgs({
|
|
280
|
+
command: "kubectl scale deployment/web --replicas=4 -n web",
|
|
281
|
+
rollbackCommand: "kubectl scale deployment/web --replicas=2 -n web",
|
|
282
|
+
}),
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
expect(first.success).toBe(true);
|
|
286
|
+
expect(second.success).toBe(true);
|
|
287
|
+
expect(liveStatus).toHaveBeenCalledTimes(2);
|
|
288
|
+
for (const call of liveStatus.mock.calls) {
|
|
289
|
+
const args: { clusterId: ObjectID; projectId: ObjectID } = call[0] as {
|
|
290
|
+
clusterId: ObjectID;
|
|
291
|
+
projectId: ObjectID;
|
|
292
|
+
};
|
|
293
|
+
expect(args.clusterId.toString()).toBe(CLUSTER_ID.toString());
|
|
294
|
+
expect(args.projectId.toString()).toBe(PROJECT_ID.toString());
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
expect(enqueueKubectl).toHaveBeenCalledTimes(2);
|
|
298
|
+
const enqueueArgs: Record<string, unknown> = enqueueKubectl.mock
|
|
299
|
+
.calls[0]![0] as Record<string, unknown>;
|
|
300
|
+
expect((enqueueArgs["targetAgentId"] as ObjectID).toString()).toBe(
|
|
301
|
+
RUNNER_ID.toString(),
|
|
302
|
+
);
|
|
303
|
+
expect(enqueueArgs["credentialId"]).toBe(CREDENTIAL_ID);
|
|
304
|
+
expect(enqueueArgs["stepId"]).toBe(`${INLINE_COMMAND_STEP_ID_PREFIX}1`);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it("refuses the next command once the operator turned remediation Off mid-run — one change landed, the second never does", async () => {
|
|
308
|
+
const toolkit: RemediationCommandToolkit = buildToolkit();
|
|
309
|
+
|
|
310
|
+
const first: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
311
|
+
expect(first.success).toBe(true);
|
|
312
|
+
expect(enqueueKubectl).toHaveBeenCalledTimes(1);
|
|
313
|
+
|
|
314
|
+
liveStatus.mockResolvedValue(
|
|
315
|
+
cluster({
|
|
316
|
+
remediationMode: KubernetesAiRemediationMode.Disabled,
|
|
317
|
+
isRemediationReady: false,
|
|
318
|
+
gaps: [
|
|
319
|
+
{
|
|
320
|
+
code: "remediation_disabled",
|
|
321
|
+
title: "AI remediation is turned off for this cluster",
|
|
322
|
+
description: "",
|
|
323
|
+
nextStep: "",
|
|
324
|
+
blocks: "remediation",
|
|
325
|
+
},
|
|
326
|
+
],
|
|
327
|
+
}),
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
const second: ToolCallOutcome = await execute(
|
|
331
|
+
toolkit,
|
|
332
|
+
kubectlArgs({ command: "kubectl rollout restart deployment/api -n web" }),
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
expect(second.success).toBe(false);
|
|
336
|
+
expect(second.textForLlm).toContain("no longer allows AI remediation");
|
|
337
|
+
expect(second.textForLlm).toContain(
|
|
338
|
+
"AI remediation is turned off for this cluster",
|
|
339
|
+
);
|
|
340
|
+
expect(second.textForLlm).toContain("Do NOT run any further command");
|
|
341
|
+
expect(enqueueKubectl).toHaveBeenCalledTimes(1);
|
|
342
|
+
expect(toolkit.getExecutedCommands()).toHaveLength(1);
|
|
343
|
+
// Not something a click could now allow — nothing is kept for a proposal.
|
|
344
|
+
expect(toolkit.getCommandsNeedingApproval()).toHaveLength(0);
|
|
345
|
+
|
|
346
|
+
// The cluster is off-limits for the rest of the run.
|
|
347
|
+
const listed: ToolCallOutcome = await listTargets(toolkit);
|
|
348
|
+
expect(listed.result?.rowCount).toBe(0);
|
|
349
|
+
const third: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
350
|
+
expect(third.success).toBe(false);
|
|
351
|
+
expect(enqueueKubectl).toHaveBeenCalledTimes(1);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it("refuses when the project's AI switches went off (the status folds them into readiness)", async () => {
|
|
355
|
+
liveStatus.mockResolvedValue(
|
|
356
|
+
cluster({
|
|
357
|
+
isRemediationReady: false,
|
|
358
|
+
gaps: [
|
|
359
|
+
{
|
|
360
|
+
code: "project_ai_command_execution_disabled",
|
|
361
|
+
title: "AI command execution is off for this project",
|
|
362
|
+
description: "",
|
|
363
|
+
nextStep: "",
|
|
364
|
+
blocks: "remediation",
|
|
365
|
+
},
|
|
366
|
+
],
|
|
367
|
+
}),
|
|
368
|
+
);
|
|
369
|
+
const toolkit: RemediationCommandToolkit = buildToolkit();
|
|
370
|
+
|
|
371
|
+
const outcome: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
372
|
+
|
|
373
|
+
expectNothingRanOrRecorded(toolkit, outcome);
|
|
374
|
+
expect(outcome.textForLlm).toContain(
|
|
375
|
+
"AI command execution is off for this project",
|
|
376
|
+
);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it("refuses — and keeps the change for a proposal — once the cluster was moved to Ask for approval mid-run", async () => {
|
|
380
|
+
liveStatus.mockResolvedValue(
|
|
381
|
+
cluster({ remediationMode: KubernetesAiRemediationMode.RequireApproval }),
|
|
382
|
+
);
|
|
383
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
384
|
+
proposesRefusedCommands: true,
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
const outcome: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
388
|
+
|
|
389
|
+
expectNothingRanOrRecorded(toolkit, outcome);
|
|
390
|
+
expect(outcome.textForLlm).toContain(
|
|
391
|
+
"was changed to ask for approval during this run",
|
|
392
|
+
);
|
|
393
|
+
expect(outcome.textForLlm).toContain("proposes it for one-click approval");
|
|
394
|
+
|
|
395
|
+
const kept: Array<RemediationCommandNeedingApproval> =
|
|
396
|
+
toolkit.getCommandsNeedingApproval();
|
|
397
|
+
expect(kept).toHaveLength(1);
|
|
398
|
+
expect(kept[0]!.command.command).toBe(SAFE_WRITE);
|
|
399
|
+
expect(kept[0]!.command.policyVerdict).toBe(
|
|
400
|
+
AiRemediationCommandPolicyVerdict.RequiresApproval,
|
|
401
|
+
);
|
|
402
|
+
expect(kept[0]!.command.wasAutoExecuted).toBe(false);
|
|
403
|
+
expect(kept[0]!.command.execution).toBeUndefined();
|
|
404
|
+
expect(kept[0]!.reason).toContain("changed to ask for approval");
|
|
405
|
+
|
|
406
|
+
// A second change on it is refused (and kept) the same way.
|
|
407
|
+
const again: ToolCallOutcome = await execute(
|
|
408
|
+
toolkit,
|
|
409
|
+
kubectlArgs({ command: "kubectl rollout restart deployment/api -n web" }),
|
|
410
|
+
);
|
|
411
|
+
expect(again.success).toBe(false);
|
|
412
|
+
expect(toolkit.getCommandsNeedingApproval()).toHaveLength(2);
|
|
413
|
+
expect(enqueueKubectl).not.toHaveBeenCalled();
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it("refuses when the cluster was re-bound to another Runner mid-run, and never enqueues on either Runner", async () => {
|
|
417
|
+
liveStatus.mockResolvedValue(
|
|
418
|
+
cluster({
|
|
419
|
+
runner: {
|
|
420
|
+
id: OTHER_RUNNER_ID.toString(),
|
|
421
|
+
name: "kubernetes-agent/prod-us-v2",
|
|
422
|
+
isOnline: true,
|
|
423
|
+
canRunAiCommands: true,
|
|
424
|
+
},
|
|
425
|
+
}),
|
|
426
|
+
);
|
|
427
|
+
const toolkit: RemediationCommandToolkit = buildToolkit();
|
|
428
|
+
|
|
429
|
+
const outcome: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
430
|
+
|
|
431
|
+
expectNothingRanOrRecorded(toolkit, outcome);
|
|
432
|
+
expect(outcome.textForLlm).toContain(
|
|
433
|
+
"re-bound to a different Runner or credential",
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
// Nor does the run pick the new binding up on its own afterwards.
|
|
437
|
+
const again: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
438
|
+
expect(again.success).toBe(false);
|
|
439
|
+
expect(enqueueKubectl).not.toHaveBeenCalled();
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it("refuses when the cluster's credential changed mid-run — in either direction", async () => {
|
|
443
|
+
liveStatus.mockResolvedValue(
|
|
444
|
+
cluster({ accessMethod: "credential", credentialId: CREDENTIAL_ID }),
|
|
445
|
+
);
|
|
446
|
+
const inCluster: RemediationCommandToolkit = buildToolkit();
|
|
447
|
+
const first: ToolCallOutcome = await execute(inCluster, kubectlArgs());
|
|
448
|
+
expectNothingRanOrRecorded(inCluster, first);
|
|
449
|
+
|
|
450
|
+
liveStatus.mockResolvedValue(cluster());
|
|
451
|
+
const credentialed: RemediationCommandToolkit = buildToolkit({
|
|
452
|
+
clusterTargets: [
|
|
453
|
+
cluster({ accessMethod: "credential", credentialId: CREDENTIAL_ID }),
|
|
454
|
+
],
|
|
455
|
+
});
|
|
456
|
+
const second: ToolCallOutcome = await execute(credentialed, kubectlArgs());
|
|
457
|
+
expect(second.success).toBe(false);
|
|
458
|
+
expect(second.textForLlm).toContain("re-bound");
|
|
459
|
+
expect(enqueueKubectl).not.toHaveBeenCalled();
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
it("fails CLOSED when the live page cannot be read", async () => {
|
|
463
|
+
liveStatus.mockRejectedValue(new Error("db down"));
|
|
464
|
+
const toolkit: RemediationCommandToolkit = buildToolkit();
|
|
465
|
+
|
|
466
|
+
const outcome: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
467
|
+
|
|
468
|
+
expectNothingRanOrRecorded(toolkit, outcome);
|
|
469
|
+
expect(outcome.textForLlm).toContain(
|
|
470
|
+
'Could not confirm that cluster "prod-us" still allows AI remediation',
|
|
471
|
+
);
|
|
472
|
+
expect(logger.error).toHaveBeenCalledWith(
|
|
473
|
+
expect.stringContaining("db down"),
|
|
474
|
+
);
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it("refuses when the cluster was deleted mid-run", async () => {
|
|
478
|
+
liveStatus.mockResolvedValue(null);
|
|
479
|
+
const toolkit: RemediationCommandToolkit = buildToolkit();
|
|
480
|
+
|
|
481
|
+
const outcome: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
482
|
+
|
|
483
|
+
expectNothingRanOrRecorded(toolkit, outcome);
|
|
484
|
+
expect(outcome.textForLlm).toContain("no longer exists in this project");
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it("judges a RiskyWrite under the LIVE mode: Bypass approval downgraded to Automatic mid-run refuses it (and keeps it for a proposal)", async () => {
|
|
488
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
489
|
+
clusterTargets: [
|
|
490
|
+
cluster({
|
|
491
|
+
remediationMode: KubernetesAiRemediationMode.BypassApproval,
|
|
492
|
+
}),
|
|
493
|
+
],
|
|
494
|
+
proposesRefusedCommands: true,
|
|
495
|
+
});
|
|
496
|
+
liveStatus.mockResolvedValue(
|
|
497
|
+
cluster({ remediationMode: KubernetesAiRemediationMode.Automatic }),
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
const outcome: ToolCallOutcome = await execute(
|
|
501
|
+
toolkit,
|
|
502
|
+
kubectlArgs({ command: RISKY_WRITE, rollbackCommand: undefined }),
|
|
503
|
+
);
|
|
504
|
+
|
|
505
|
+
expectNothingRanOrRecorded(toolkit, outcome);
|
|
506
|
+
expect(outcome.textForLlm).toContain("Requires human approval");
|
|
507
|
+
expect(toolkit.getCommandsNeedingApproval()).toHaveLength(1);
|
|
508
|
+
expect(toolkit.getCommandsNeedingApproval()[0]!.command.kubectlTier).toBe(
|
|
509
|
+
KubectlCommandTier.RiskyWrite,
|
|
510
|
+
);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it("refuses a RiskyWrite rollback accepted under Bypass approval once the cluster became Automatic mid-run", async () => {
|
|
514
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
515
|
+
clusterTargets: [
|
|
516
|
+
cluster({
|
|
517
|
+
remediationMode: KubernetesAiRemediationMode.BypassApproval,
|
|
518
|
+
}),
|
|
519
|
+
],
|
|
520
|
+
});
|
|
521
|
+
liveStatus.mockResolvedValue(
|
|
522
|
+
cluster({ remediationMode: KubernetesAiRemediationMode.Automatic }),
|
|
523
|
+
);
|
|
524
|
+
|
|
525
|
+
const outcome: ToolCallOutcome = await execute(
|
|
526
|
+
toolkit,
|
|
527
|
+
kubectlArgs({ command: SAFE_WRITE, rollbackCommand: RISKY_UNDO }),
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
expect(outcome.success).toBe(false);
|
|
531
|
+
expect(outcome.textForLlm).toContain(
|
|
532
|
+
"rollbackCommand does not qualify for automatic execution",
|
|
533
|
+
);
|
|
534
|
+
expect(enqueueKubectl).not.toHaveBeenCalled();
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it("negative control: the same rollback still runs when the Automatic cluster's allowlist names its shape", async () => {
|
|
538
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
539
|
+
clusterTargets: [
|
|
540
|
+
cluster({
|
|
541
|
+
remediationMode: KubernetesAiRemediationMode.BypassApproval,
|
|
542
|
+
}),
|
|
543
|
+
],
|
|
544
|
+
});
|
|
545
|
+
liveStatus.mockResolvedValue(
|
|
546
|
+
cluster({
|
|
547
|
+
remediationMode: KubernetesAiRemediationMode.Automatic,
|
|
548
|
+
kubectlAllowlist: ["kubectl set image deployment/web * -n web"],
|
|
549
|
+
}),
|
|
550
|
+
);
|
|
551
|
+
|
|
552
|
+
const outcome: ToolCallOutcome = await execute(
|
|
553
|
+
toolkit,
|
|
554
|
+
kubectlArgs({ command: SAFE_WRITE, rollbackCommand: RISKY_UNDO }),
|
|
555
|
+
);
|
|
556
|
+
|
|
557
|
+
expect(outcome.success).toBe(true);
|
|
558
|
+
expect(enqueueKubectl).toHaveBeenCalledTimes(1);
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
it("uses the LIVE allowlist: a shape the operator removed mid-run no longer runs", async () => {
|
|
562
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
563
|
+
clusterTargets: [
|
|
564
|
+
cluster({
|
|
565
|
+
kubectlAllowlist: ["kubectl set image deployment/web * -n web"],
|
|
566
|
+
}),
|
|
567
|
+
],
|
|
568
|
+
});
|
|
569
|
+
liveStatus.mockResolvedValue(cluster({ kubectlAllowlist: [] }));
|
|
570
|
+
|
|
571
|
+
const outcome: ToolCallOutcome = await execute(
|
|
572
|
+
toolkit,
|
|
573
|
+
kubectlArgs({ command: RISKY_WRITE, rollbackCommand: undefined }),
|
|
574
|
+
);
|
|
575
|
+
|
|
576
|
+
expectNothingRanOrRecorded(toolkit, outcome);
|
|
577
|
+
expect(outcome.textForLlm).toContain("Requires human approval");
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
it("negative control: the snapshot's allowlist still names the shape live — the RiskyWrite runs", async () => {
|
|
581
|
+
const allowlisted: KubernetesClusterAiAccessStatus = cluster({
|
|
582
|
+
kubectlAllowlist: ["kubectl set image deployment/web * -n web"],
|
|
583
|
+
});
|
|
584
|
+
liveStatus.mockResolvedValue(allowlisted);
|
|
585
|
+
|
|
586
|
+
const outcome: ToolCallOutcome = await execute(
|
|
587
|
+
buildToolkit({ clusterTargets: [allowlisted] }),
|
|
588
|
+
kubectlArgs({ command: RISKY_WRITE, rollbackCommand: SAFE_UNDO }),
|
|
589
|
+
);
|
|
590
|
+
|
|
591
|
+
expect(outcome.success).toBe(true);
|
|
592
|
+
expect(enqueueKubectl).toHaveBeenCalledTimes(1);
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
it("never reads the cluster page for a Bash command", async () => {
|
|
596
|
+
jest.spyOn(RunnerService, "findOneBy").mockResolvedValue({
|
|
597
|
+
id: OTHER_RUNNER_ID,
|
|
598
|
+
_id: OTHER_RUNNER_ID.toString(),
|
|
599
|
+
name: "ops-bastion",
|
|
600
|
+
} as never);
|
|
601
|
+
const bash: jest.SpyInstance = jest
|
|
602
|
+
.spyOn(RunnerJobService, "enqueueAiCommand")
|
|
603
|
+
.mockResolvedValue(fakeJob());
|
|
604
|
+
|
|
605
|
+
const outcome: ToolCallOutcome = await execute(
|
|
606
|
+
buildToolkit({
|
|
607
|
+
allowlistPatterns: ["systemctl restart *"],
|
|
608
|
+
allowedRunnerIds: [OTHER_RUNNER_ID.toString()],
|
|
609
|
+
clusterTargets: [],
|
|
610
|
+
}),
|
|
611
|
+
{
|
|
612
|
+
stepType: "Bash",
|
|
613
|
+
runnerId: OTHER_RUNNER_ID.toString(),
|
|
614
|
+
command: "systemctl restart nginx",
|
|
615
|
+
rationale: "nginx is wedged",
|
|
616
|
+
expectedEffect: "nginx serves again",
|
|
617
|
+
} as JSONObject,
|
|
618
|
+
);
|
|
619
|
+
|
|
620
|
+
expect(outcome.success).toBe(true);
|
|
621
|
+
expect(bash).toHaveBeenCalledTimes(1);
|
|
622
|
+
expect(liveStatus).not.toHaveBeenCalled();
|
|
623
|
+
// Bash never takes a cluster breaker slot either.
|
|
624
|
+
expect(lock).not.toHaveBeenCalled();
|
|
625
|
+
});
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
describe("RemediationCommandToolkit refuses kubectl reads on the execute tool", () => {
|
|
629
|
+
beforeEach(mockHappyPath);
|
|
630
|
+
afterEach(() => {
|
|
631
|
+
jest.restoreAllMocks();
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
it.each([
|
|
635
|
+
"kubectl rollout status deployment/web -n web",
|
|
636
|
+
"kubectl get pods -n web",
|
|
637
|
+
"kubectl describe pod web-1 -n web",
|
|
638
|
+
])(
|
|
639
|
+
"refuses %s on an Automatic cluster and points at run_kubectl — nothing enqueued or recorded",
|
|
640
|
+
async (command: string) => {
|
|
641
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
642
|
+
proposesRefusedCommands: true,
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
const outcome: ToolCallOutcome = await execute(
|
|
646
|
+
toolkit,
|
|
647
|
+
kubectlArgs({ command, rollbackCommand: undefined }),
|
|
648
|
+
);
|
|
649
|
+
|
|
650
|
+
expectNothingRanOrRecorded(toolkit, outcome);
|
|
651
|
+
expect(outcome.textForLlm).toContain("run_kubectl");
|
|
652
|
+
expect(outcome.textForLlm).toContain("read-only");
|
|
653
|
+
// A read is never proposed for approval either.
|
|
654
|
+
expect(toolkit.getCommandsNeedingApproval()).toHaveLength(0);
|
|
655
|
+
// Refused before the cluster page or the breaker is even consulted.
|
|
656
|
+
expect(liveStatus).not.toHaveBeenCalled();
|
|
657
|
+
expect(lock).not.toHaveBeenCalled();
|
|
658
|
+
},
|
|
659
|
+
);
|
|
660
|
+
|
|
661
|
+
it("refuses a read on a Bypass-approval cluster with a permissive allowlist too", async () => {
|
|
662
|
+
const bypass: KubernetesClusterAiAccessStatus = cluster({
|
|
663
|
+
remediationMode: KubernetesAiRemediationMode.BypassApproval,
|
|
664
|
+
kubectlAllowlist: ["kubectl *"],
|
|
665
|
+
});
|
|
666
|
+
liveStatus.mockResolvedValue(bypass);
|
|
667
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
668
|
+
clusterTargets: [bypass],
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
const outcome: ToolCallOutcome = await execute(
|
|
672
|
+
toolkit,
|
|
673
|
+
kubectlArgs({
|
|
674
|
+
command: "kubectl get pods -n web",
|
|
675
|
+
rollbackCommand: undefined,
|
|
676
|
+
}),
|
|
677
|
+
);
|
|
678
|
+
|
|
679
|
+
expectNothingRanOrRecorded(toolkit, outcome);
|
|
680
|
+
expect(outcome.textForLlm).toContain("run_kubectl");
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
it("negative control: a SafeWrite still executes and is recorded as before", async () => {
|
|
684
|
+
const toolkit: RemediationCommandToolkit = buildToolkit();
|
|
685
|
+
|
|
686
|
+
const outcome: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
687
|
+
|
|
688
|
+
expect(outcome.success).toBe(true);
|
|
689
|
+
expect(enqueueKubectl).toHaveBeenCalledTimes(1);
|
|
690
|
+
expect(toolkit.getExecutedCommands()).toHaveLength(1);
|
|
691
|
+
expect(toolkit.getExecutedCommands()[0]!.kubectlTier).toBe(
|
|
692
|
+
KubectlCommandTier.SafeWrite,
|
|
693
|
+
);
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
it("tells the model in the tool description that reads go through run_kubectl", () => {
|
|
697
|
+
const description: string =
|
|
698
|
+
buildToolkit()
|
|
699
|
+
.buildTools()
|
|
700
|
+
.find((tool: ObservabilityAssistantExtraTool) => {
|
|
701
|
+
return tool.definition.name === "execute_remediation_command";
|
|
702
|
+
})?.definition.description || "";
|
|
703
|
+
|
|
704
|
+
expect(description).toContain("read-only kubectl");
|
|
705
|
+
expect(description).toContain("run_kubectl");
|
|
706
|
+
expect(description).toContain("for CHANGES only");
|
|
707
|
+
});
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
describe("RemediationCommandToolkit takes a cluster's hourly unattended slot before its first inline change", () => {
|
|
711
|
+
beforeEach(mockHappyPath);
|
|
712
|
+
afterEach(() => {
|
|
713
|
+
jest.restoreAllMocks();
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
it("refuses the change when three other runs already changed the cluster this hour — nothing enqueued, lock released, kept for a proposal", async () => {
|
|
717
|
+
jobFindBy.mockResolvedValue([
|
|
718
|
+
inlineJobOf(ObjectID.generate()),
|
|
719
|
+
inlineJobOf(ObjectID.generate()),
|
|
720
|
+
inlineJobOf(ObjectID.generate()),
|
|
721
|
+
]);
|
|
722
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
723
|
+
proposesRefusedCommands: true,
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
const outcome: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
727
|
+
|
|
728
|
+
expectNothingRanOrRecorded(toolkit, outcome);
|
|
729
|
+
expect(outcome.textForLlm).toContain("hourly circuit breaker");
|
|
730
|
+
expect(outcome.textForLlm).toContain(
|
|
731
|
+
`already had ${MAX_AUTO_EXECUTIONS_PER_RULE_PER_HOUR} unattended AI fix(es)`,
|
|
732
|
+
);
|
|
733
|
+
expect(lock).toHaveBeenCalledTimes(1);
|
|
734
|
+
expect(release).toHaveBeenCalledTimes(1);
|
|
735
|
+
expect(toolkit.getCommandsNeedingApproval()).toHaveLength(1);
|
|
736
|
+
expect(toolkit.getCommandsNeedingApproval()[0]!.reason).toContain(
|
|
737
|
+
"hourly circuit breaker",
|
|
738
|
+
);
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
it("counts an unattended cluster round still in flight — created before this run — as a slot taken", async () => {
|
|
742
|
+
jobFindBy.mockResolvedValue([
|
|
743
|
+
inlineJobOf(ObjectID.generate()),
|
|
744
|
+
inlineJobOf(ObjectID.generate()),
|
|
745
|
+
]);
|
|
746
|
+
const createdAt: Date = new Date(Date.now() - 60 * 1000);
|
|
747
|
+
jest.spyOn(AutoRemediationSuggestionService, "findBy").mockResolvedValue([
|
|
748
|
+
{
|
|
749
|
+
id: OTHER_SUGGESTION_ID,
|
|
750
|
+
_id: OTHER_SUGGESTION_ID.toString(),
|
|
751
|
+
createdAt: new Date(createdAt.getTime() - 1000),
|
|
752
|
+
} as unknown as AutoRemediationSuggestion,
|
|
753
|
+
]);
|
|
754
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
755
|
+
suggestionCreatedAt: createdAt,
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
const outcome: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
759
|
+
|
|
760
|
+
expectNothingRanOrRecorded(toolkit, outcome);
|
|
761
|
+
expect(outcome.textForLlm).toContain("hourly circuit breaker");
|
|
762
|
+
|
|
763
|
+
// The in-flight read is scoped to unattended Planning rounds on this cluster.
|
|
764
|
+
const query: Record<string, unknown> = (
|
|
765
|
+
(AutoRemediationSuggestionService.findBy as unknown as jest.SpyInstance)
|
|
766
|
+
.mock.calls[0]![0] as { query: Record<string, unknown> }
|
|
767
|
+
).query;
|
|
768
|
+
expect(query["status"]).toBe(AutoRemediationSuggestionStatus.Planning);
|
|
769
|
+
expect(query["executionMode"]).toBe(AutoRemediationExecutionMode.FullAuto);
|
|
770
|
+
expect((query["kubernetesClusterId"] as ObjectID).toString()).toBe(
|
|
771
|
+
CLUSTER_ID.toString(),
|
|
772
|
+
);
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
it("negative control: an in-flight round created AFTER this run does not count against it", async () => {
|
|
776
|
+
jobFindBy.mockResolvedValue([
|
|
777
|
+
inlineJobOf(ObjectID.generate()),
|
|
778
|
+
inlineJobOf(ObjectID.generate()),
|
|
779
|
+
]);
|
|
780
|
+
const createdAt: Date = new Date(Date.now() - 60 * 1000);
|
|
781
|
+
jest.spyOn(AutoRemediationSuggestionService, "findBy").mockResolvedValue([
|
|
782
|
+
{
|
|
783
|
+
id: OTHER_SUGGESTION_ID,
|
|
784
|
+
_id: OTHER_SUGGESTION_ID.toString(),
|
|
785
|
+
createdAt: new Date(createdAt.getTime() + 1000),
|
|
786
|
+
} as unknown as AutoRemediationSuggestion,
|
|
787
|
+
]);
|
|
788
|
+
|
|
789
|
+
const outcome: ToolCallOutcome = await execute(
|
|
790
|
+
buildToolkit({ suggestionCreatedAt: createdAt }),
|
|
791
|
+
kubectlArgs(),
|
|
792
|
+
);
|
|
793
|
+
|
|
794
|
+
expect(outcome.success).toBe(true);
|
|
795
|
+
expect(enqueueKubectl).toHaveBeenCalledTimes(1);
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
it("negative control: two other runs in the window leave headroom — the change runs, and the lock is released right after the enqueue, before the wait", async () => {
|
|
799
|
+
jobFindBy.mockResolvedValue([
|
|
800
|
+
inlineJobOf(ObjectID.generate()),
|
|
801
|
+
inlineJobOf(ObjectID.generate()),
|
|
802
|
+
]);
|
|
803
|
+
const poll: jest.SpyInstance = jest
|
|
804
|
+
.spyOn(RunnerJobService, "pollUntilTerminal")
|
|
805
|
+
.mockResolvedValue(fakeJob());
|
|
806
|
+
|
|
807
|
+
const outcome: ToolCallOutcome = await execute(
|
|
808
|
+
buildToolkit(),
|
|
809
|
+
kubectlArgs(),
|
|
810
|
+
);
|
|
811
|
+
|
|
812
|
+
expect(outcome.success).toBe(true);
|
|
813
|
+
expect(enqueueKubectl).toHaveBeenCalledTimes(1);
|
|
814
|
+
expect(lock).toHaveBeenCalledWith(
|
|
815
|
+
expect.objectContaining({
|
|
816
|
+
key: CLUSTER_ID.toString(),
|
|
817
|
+
namespace: "AutoRemediationClusterBreaker",
|
|
818
|
+
}),
|
|
819
|
+
);
|
|
820
|
+
// lock < enqueue < release < wait: the job row IS the reservation.
|
|
821
|
+
expect(lock.mock.invocationCallOrder[0]).toBeLessThan(
|
|
822
|
+
enqueueKubectl.mock.invocationCallOrder[0]!,
|
|
823
|
+
);
|
|
824
|
+
expect(enqueueKubectl.mock.invocationCallOrder[0]).toBeLessThan(
|
|
825
|
+
release.mock.invocationCallOrder[0]!,
|
|
826
|
+
);
|
|
827
|
+
expect(release.mock.invocationCallOrder[0]).toBeLessThan(
|
|
828
|
+
poll.mock.invocationCallOrder[0]!,
|
|
829
|
+
);
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
it("negative control: this run's own jobs never count against it, and its later changes on the same cluster hold the slot already", async () => {
|
|
833
|
+
jobFindBy.mockResolvedValue([
|
|
834
|
+
inlineJobOf(ObjectID.generate()),
|
|
835
|
+
inlineJobOf(ObjectID.generate()),
|
|
836
|
+
inlineJobOf(SUGGESTION_ID),
|
|
837
|
+
]);
|
|
838
|
+
const toolkit: RemediationCommandToolkit = buildToolkit();
|
|
839
|
+
|
|
840
|
+
const first: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
841
|
+
expect(first.success).toBe(true);
|
|
842
|
+
|
|
843
|
+
// Even with the window now full, the run keeps changing "its" cluster.
|
|
844
|
+
jobFindBy.mockResolvedValue([
|
|
845
|
+
inlineJobOf(ObjectID.generate()),
|
|
846
|
+
inlineJobOf(ObjectID.generate()),
|
|
847
|
+
inlineJobOf(ObjectID.generate()),
|
|
848
|
+
inlineJobOf(SUGGESTION_ID),
|
|
849
|
+
]);
|
|
850
|
+
const second: ToolCallOutcome = await execute(
|
|
851
|
+
toolkit,
|
|
852
|
+
kubectlArgs({ command: "kubectl rollout restart deployment/api -n web" }),
|
|
853
|
+
);
|
|
854
|
+
|
|
855
|
+
expect(second.success).toBe(true);
|
|
856
|
+
expect(enqueueKubectl).toHaveBeenCalledTimes(2);
|
|
857
|
+
expect(lock).toHaveBeenCalledTimes(1);
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
it("fails CLOSED when the breaker lock cannot be taken", async () => {
|
|
861
|
+
lock.mockRejectedValue(new Error("Redis client is not connected"));
|
|
862
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
863
|
+
proposesRefusedCommands: true,
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
const outcome: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
867
|
+
|
|
868
|
+
expectNothingRanOrRecorded(toolkit, outcome);
|
|
869
|
+
expect(outcome.textForLlm).toContain("Could not check the hourly limit");
|
|
870
|
+
expect(toolkit.getCommandsNeedingApproval()).toHaveLength(1);
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
it("fails CLOSED when the breaker count cannot be read, and still releases the lock", async () => {
|
|
874
|
+
jobFindBy.mockRejectedValue(new Error("db down"));
|
|
875
|
+
const toolkit: RemediationCommandToolkit = buildToolkit();
|
|
876
|
+
|
|
877
|
+
const outcome: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
878
|
+
|
|
879
|
+
expectNothingRanOrRecorded(toolkit, outcome);
|
|
880
|
+
expect(outcome.textForLlm).toContain("Could not check the hourly limit");
|
|
881
|
+
expect(release).toHaveBeenCalledTimes(1);
|
|
882
|
+
});
|
|
883
|
+
|
|
884
|
+
it("releases the lock when the enqueue itself throws", async () => {
|
|
885
|
+
enqueueKubectl.mockRejectedValue(new Error("enqueue exploded"));
|
|
886
|
+
|
|
887
|
+
const outcome: ToolCallOutcome = await execute(
|
|
888
|
+
buildToolkit(),
|
|
889
|
+
kubectlArgs(),
|
|
890
|
+
);
|
|
891
|
+
|
|
892
|
+
/*
|
|
893
|
+
* The tool call resolves. Changed in the round-three review: an
|
|
894
|
+
* enqueue that throws wrote no job, so the command certainly never ran
|
|
895
|
+
* — a failed tool call off the record (KubectlJobRunner's NotRun), no
|
|
896
|
+
* longer an executed command that "FAILED before completion".
|
|
897
|
+
*/
|
|
898
|
+
expect(outcome.success).toBe(false);
|
|
899
|
+
expect(outcome.textForLlm).toContain("did NOT run on cluster");
|
|
900
|
+
expect(outcome.textForLlm).toContain("enqueue exploded");
|
|
901
|
+
expect(release).toHaveBeenCalledTimes(1);
|
|
902
|
+
});
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
describe("RemediationCommandToolkit serializes concurrent runs on one cluster's breaker", () => {
|
|
906
|
+
afterEach(() => {
|
|
907
|
+
jest.restoreAllMocks();
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
/*
|
|
911
|
+
* A shared cluster at 2 of 3: two runs of two different rounds try their
|
|
912
|
+
* first change at the same moment. The fake job store is what the breaker
|
|
913
|
+
* counts; an enqueue appends to it.
|
|
914
|
+
*/
|
|
915
|
+
async function raceTwoRuns(serializing: boolean): Promise<{
|
|
916
|
+
successes: number;
|
|
917
|
+
runs: Array<RemediationCommandToolkit>;
|
|
918
|
+
outcomes: Array<ToolCallOutcome>;
|
|
919
|
+
}> {
|
|
920
|
+
mockHappyPath();
|
|
921
|
+
|
|
922
|
+
const jobs: Array<RunnerJob> = [
|
|
923
|
+
inlineJobOf(ObjectID.generate()),
|
|
924
|
+
inlineJobOf(ObjectID.generate()),
|
|
925
|
+
];
|
|
926
|
+
jobFindBy.mockImplementation(async (): Promise<Array<RunnerJob>> => {
|
|
927
|
+
/*
|
|
928
|
+
* A read sees the rows as they were when it was issued, and takes a
|
|
929
|
+
* moment to come back — long enough for a race to interleave.
|
|
930
|
+
*/
|
|
931
|
+
const seen: Array<RunnerJob> = [...jobs];
|
|
932
|
+
await new Promise<void>((resolve: () => void) => {
|
|
933
|
+
setTimeout(resolve, 5);
|
|
934
|
+
});
|
|
935
|
+
return seen;
|
|
936
|
+
});
|
|
937
|
+
enqueueKubectl.mockImplementation(
|
|
938
|
+
async (args: {
|
|
939
|
+
autoRemediationSuggestionId: ObjectID;
|
|
940
|
+
}): Promise<RunnerJob> => {
|
|
941
|
+
jobs.push(inlineJobOf(args.autoRemediationSuggestionId));
|
|
942
|
+
return fakeJob({ status: RunnerJobStatus.Pending });
|
|
943
|
+
},
|
|
944
|
+
);
|
|
945
|
+
|
|
946
|
+
let tail: Promise<void> = Promise.resolve();
|
|
947
|
+
lock.mockImplementation(async (): Promise<SemaphoreMutex> => {
|
|
948
|
+
if (!serializing) {
|
|
949
|
+
return { id: "no-op" } as unknown as SemaphoreMutex;
|
|
950
|
+
}
|
|
951
|
+
let releaseThis: () => void = (): void => {
|
|
952
|
+
return undefined;
|
|
953
|
+
};
|
|
954
|
+
const previous: Promise<void> = tail;
|
|
955
|
+
tail = new Promise<void>((resolve: () => void) => {
|
|
956
|
+
releaseThis = resolve;
|
|
957
|
+
});
|
|
958
|
+
await previous;
|
|
959
|
+
return { release: releaseThis } as unknown as SemaphoreMutex;
|
|
960
|
+
});
|
|
961
|
+
release.mockImplementation(async (mutex: unknown): Promise<void> => {
|
|
962
|
+
const releaseFn: (() => void) | undefined = (
|
|
963
|
+
mutex as { release?: () => void }
|
|
964
|
+
).release;
|
|
965
|
+
if (releaseFn) {
|
|
966
|
+
releaseFn();
|
|
967
|
+
}
|
|
968
|
+
});
|
|
969
|
+
|
|
970
|
+
const runA: RemediationCommandToolkit = buildToolkit({
|
|
971
|
+
suggestionId: SUGGESTION_ID,
|
|
972
|
+
});
|
|
973
|
+
const runB: RemediationCommandToolkit = buildToolkit({
|
|
974
|
+
suggestionId: OTHER_SUGGESTION_ID,
|
|
975
|
+
});
|
|
976
|
+
|
|
977
|
+
const outcomes: Array<ToolCallOutcome> = await Promise.all([
|
|
978
|
+
execute(runA, kubectlArgs()),
|
|
979
|
+
execute(runB, kubectlArgs()),
|
|
980
|
+
]);
|
|
981
|
+
|
|
982
|
+
return {
|
|
983
|
+
successes: outcomes.filter((outcome: ToolCallOutcome) => {
|
|
984
|
+
return outcome.success;
|
|
985
|
+
}).length,
|
|
986
|
+
runs: [runA, runB],
|
|
987
|
+
outcomes,
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
it("lets exactly one of two racing runs take the last slot", async () => {
|
|
992
|
+
expect((await raceTwoRuns(true)).successes).toBe(1);
|
|
993
|
+
expect(enqueueKubectl).toHaveBeenCalledTimes(1);
|
|
994
|
+
});
|
|
995
|
+
|
|
996
|
+
it("the run that lost the race was refused by the breaker it read AFTER the winner's job landed, and keeps its change for a proposal", async () => {
|
|
997
|
+
const race: {
|
|
998
|
+
successes: number;
|
|
999
|
+
runs: Array<RemediationCommandToolkit>;
|
|
1000
|
+
outcomes: Array<ToolCallOutcome>;
|
|
1001
|
+
} = await raceTwoRuns(true);
|
|
1002
|
+
|
|
1003
|
+
const loser: number = race.outcomes.findIndex(
|
|
1004
|
+
(outcome: ToolCallOutcome) => {
|
|
1005
|
+
return !outcome.success;
|
|
1006
|
+
},
|
|
1007
|
+
);
|
|
1008
|
+
expect(loser).toBeGreaterThanOrEqual(0);
|
|
1009
|
+
expect(race.outcomes[loser]!.textForLlm).toContain(
|
|
1010
|
+
"hourly circuit breaker",
|
|
1011
|
+
);
|
|
1012
|
+
const kept: Array<RemediationCommandNeedingApproval> =
|
|
1013
|
+
race.runs[loser]!.getCommandsNeedingApproval();
|
|
1014
|
+
expect(kept).toHaveLength(1);
|
|
1015
|
+
expect(kept[0]!.reason).toContain("circuit breaker");
|
|
1016
|
+
expect(race.runs[1 - loser]!.getCommandsNeedingApproval()).toHaveLength(0);
|
|
1017
|
+
});
|
|
1018
|
+
|
|
1019
|
+
it("negative control: without a lock that serializes, both runs read 2 of 3 and both run — the lock is what holds the limit", async () => {
|
|
1020
|
+
expect((await raceTwoRuns(false)).successes).toBe(2);
|
|
1021
|
+
});
|
|
1022
|
+
});
|
|
1023
|
+
|
|
1024
|
+
describe("RemediationCommandToolkit keeps what a human's click would allow — and nothing else", () => {
|
|
1025
|
+
beforeEach(mockHappyPath);
|
|
1026
|
+
afterEach(() => {
|
|
1027
|
+
jest.restoreAllMocks();
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
it("keeps a refused RiskyWrite on an Automatic cluster once, with its reason, and tells a cluster round it will be proposed", async () => {
|
|
1031
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
1032
|
+
proposesRefusedCommands: true,
|
|
1033
|
+
});
|
|
1034
|
+
|
|
1035
|
+
const outcome: ToolCallOutcome = await execute(
|
|
1036
|
+
toolkit,
|
|
1037
|
+
kubectlArgs({ command: RISKY_WRITE, rollbackCommand: SAFE_UNDO }),
|
|
1038
|
+
);
|
|
1039
|
+
// The model retries the very same command: kept once.
|
|
1040
|
+
await execute(
|
|
1041
|
+
toolkit,
|
|
1042
|
+
kubectlArgs({ command: RISKY_WRITE, rollbackCommand: SAFE_UNDO }),
|
|
1043
|
+
);
|
|
1044
|
+
|
|
1045
|
+
expect(outcome.success).toBe(false);
|
|
1046
|
+
expect(outcome.textForLlm).toContain("Requires human approval");
|
|
1047
|
+
expect(outcome.textForLlm).toContain("proposes it for one-click approval");
|
|
1048
|
+
expect(outcome.textForLlm).toContain(
|
|
1049
|
+
"Do NOT hunt for a worse safe substitute",
|
|
1050
|
+
);
|
|
1051
|
+
expect(enqueueKubectl).not.toHaveBeenCalled();
|
|
1052
|
+
|
|
1053
|
+
const kept: Array<RemediationCommandNeedingApproval> =
|
|
1054
|
+
toolkit.getCommandsNeedingApproval();
|
|
1055
|
+
expect(kept).toHaveLength(1);
|
|
1056
|
+
expect(kept[0]!.command.command).toBe(RISKY_WRITE);
|
|
1057
|
+
expect(kept[0]!.command.rollbackCommand).toBe(SAFE_UNDO);
|
|
1058
|
+
expect(kept[0]!.command.kubernetesClusterId).toBe(CLUSTER_ID.toString());
|
|
1059
|
+
expect(kept[0]!.reason).toContain("riskier change");
|
|
1060
|
+
});
|
|
1061
|
+
|
|
1062
|
+
it("tells a rule-driven run (no proposal) to put the refused change in its recommendations instead", async () => {
|
|
1063
|
+
const outcome: ToolCallOutcome = await execute(
|
|
1064
|
+
buildToolkit(),
|
|
1065
|
+
kubectlArgs({ command: RISKY_WRITE, rollbackCommand: undefined }),
|
|
1066
|
+
);
|
|
1067
|
+
|
|
1068
|
+
expect(outcome.textForLlm).toContain("final recommendations for a human");
|
|
1069
|
+
expect(outcome.textForLlm).not.toContain("one-click approval");
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
it("never keeps a Denied command, a refused rollback, or a read", async () => {
|
|
1073
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
1074
|
+
proposesRefusedCommands: true,
|
|
1075
|
+
});
|
|
1076
|
+
|
|
1077
|
+
await execute(
|
|
1078
|
+
toolkit,
|
|
1079
|
+
kubectlArgs({
|
|
1080
|
+
command: "kubectl delete namespace web",
|
|
1081
|
+
rollbackCommand: undefined,
|
|
1082
|
+
}),
|
|
1083
|
+
);
|
|
1084
|
+
await execute(toolkit, kubectlArgs({ rollbackCommand: RISKY_UNDO }));
|
|
1085
|
+
await execute(
|
|
1086
|
+
toolkit,
|
|
1087
|
+
kubectlArgs({
|
|
1088
|
+
command: "kubectl get pods -n web",
|
|
1089
|
+
rollbackCommand: undefined,
|
|
1090
|
+
}),
|
|
1091
|
+
);
|
|
1092
|
+
|
|
1093
|
+
expect(toolkit.getCommandsNeedingApproval()).toHaveLength(0);
|
|
1094
|
+
expect(enqueueKubectl).not.toHaveBeenCalled();
|
|
1095
|
+
});
|
|
1096
|
+
|
|
1097
|
+
it(`keeps at most a plan's worth (${MAX_PLAN_COMMANDS})`, async () => {
|
|
1098
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
1099
|
+
proposesRefusedCommands: true,
|
|
1100
|
+
});
|
|
1101
|
+
|
|
1102
|
+
for (let i: number = 0; i < MAX_PLAN_COMMANDS + 2; i++) {
|
|
1103
|
+
await execute(
|
|
1104
|
+
toolkit,
|
|
1105
|
+
kubectlArgs({
|
|
1106
|
+
command: `kubectl set image deployment/web${i} web=img:2 -n web`,
|
|
1107
|
+
rollbackCommand: undefined,
|
|
1108
|
+
}),
|
|
1109
|
+
);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
expect(toolkit.getCommandsNeedingApproval()).toHaveLength(
|
|
1113
|
+
MAX_PLAN_COMMANDS,
|
|
1114
|
+
);
|
|
1115
|
+
});
|
|
1116
|
+
});
|
|
1117
|
+
|
|
1118
|
+
describe("RemediationCommandToolkit's rollback rule on an Automatic cluster — the form the persona recommends", () => {
|
|
1119
|
+
beforeEach(mockHappyPath);
|
|
1120
|
+
afterEach(() => {
|
|
1121
|
+
jest.restoreAllMocks();
|
|
1122
|
+
});
|
|
1123
|
+
|
|
1124
|
+
const allowlisted: () => KubernetesClusterAiAccessStatus =
|
|
1125
|
+
(): KubernetesClusterAiAccessStatus => {
|
|
1126
|
+
return cluster({
|
|
1127
|
+
kubectlAllowlist: ["kubectl set image deployment/web * -n web"],
|
|
1128
|
+
});
|
|
1129
|
+
};
|
|
1130
|
+
|
|
1131
|
+
it("refuses an allowlisted set image whose rollback is set image back: rollbacks run unattended", async () => {
|
|
1132
|
+
liveStatus.mockResolvedValue(allowlisted());
|
|
1133
|
+
|
|
1134
|
+
const outcome: ToolCallOutcome = await execute(
|
|
1135
|
+
buildToolkit({ clusterTargets: [allowlisted()] }),
|
|
1136
|
+
kubectlArgs({ command: RISKY_WRITE, rollbackCommand: RISKY_UNDO }),
|
|
1137
|
+
);
|
|
1138
|
+
|
|
1139
|
+
expect(outcome.success).toBe(false);
|
|
1140
|
+
expect(outcome.textForLlm).toContain("rollbacks run unattended");
|
|
1141
|
+
expect(outcome.textForLlm).toContain(
|
|
1142
|
+
"kubectl rollout undo deployment/<name>",
|
|
1143
|
+
);
|
|
1144
|
+
expect(enqueueKubectl).not.toHaveBeenCalled();
|
|
1145
|
+
});
|
|
1146
|
+
|
|
1147
|
+
it("runs the same allowlisted set image with rollout undo as its rollback", async () => {
|
|
1148
|
+
liveStatus.mockResolvedValue(allowlisted());
|
|
1149
|
+
|
|
1150
|
+
const outcome: ToolCallOutcome = await execute(
|
|
1151
|
+
buildToolkit({ clusterTargets: [allowlisted()] }),
|
|
1152
|
+
kubectlArgs({ command: RISKY_WRITE, rollbackCommand: SAFE_UNDO }),
|
|
1153
|
+
);
|
|
1154
|
+
|
|
1155
|
+
expect(outcome.success).toBe(true);
|
|
1156
|
+
expect(enqueueKubectl).toHaveBeenCalledTimes(1);
|
|
1157
|
+
});
|
|
1158
|
+
|
|
1159
|
+
it("negative control: an allowlist that does not name set image refuses the forward change — the allowlist is what authorizes it", async () => {
|
|
1160
|
+
const scaleOnly: KubernetesClusterAiAccessStatus = cluster({
|
|
1161
|
+
kubectlAllowlist: ["kubectl scale *"],
|
|
1162
|
+
});
|
|
1163
|
+
liveStatus.mockResolvedValue(scaleOnly);
|
|
1164
|
+
|
|
1165
|
+
const outcome: ToolCallOutcome = await execute(
|
|
1166
|
+
buildToolkit({ clusterTargets: [scaleOnly] }),
|
|
1167
|
+
kubectlArgs({ command: RISKY_WRITE, rollbackCommand: SAFE_UNDO }),
|
|
1168
|
+
);
|
|
1169
|
+
|
|
1170
|
+
expect(outcome.success).toBe(false);
|
|
1171
|
+
expect(outcome.textForLlm).toContain("Requires human approval");
|
|
1172
|
+
expect(enqueueKubectl).not.toHaveBeenCalled();
|
|
1173
|
+
});
|
|
1174
|
+
});
|
|
1175
|
+
|
|
1176
|
+
/*
|
|
1177
|
+
* What a round kept for a proposal is dropped once the run sees the cluster
|
|
1178
|
+
* stop allowing AI remediation (PR #3953 review, remediation-r2-03): turned
|
|
1179
|
+
* off, re-bound, or deleted mid-run. The operator just withdrew exactly
|
|
1180
|
+
* what the card would ask them to approve. Moving the cluster to "ask for
|
|
1181
|
+
* approval" is not a withdrawal: that keeps everything (see above).
|
|
1182
|
+
*/
|
|
1183
|
+
describe("RemediationCommandToolkit drops what it kept for a cluster the run saw stop allowing AI remediation", () => {
|
|
1184
|
+
beforeEach(mockHappyPath);
|
|
1185
|
+
afterEach(() => {
|
|
1186
|
+
jest.restoreAllMocks();
|
|
1187
|
+
});
|
|
1188
|
+
|
|
1189
|
+
const RISKY_WRITE_2: string =
|
|
1190
|
+
"kubectl set image deployment/api api=img:2 -n web";
|
|
1191
|
+
|
|
1192
|
+
it.each([
|
|
1193
|
+
[
|
|
1194
|
+
"remediation turned off",
|
|
1195
|
+
(): KubernetesClusterAiAccessStatus | null => {
|
|
1196
|
+
return cluster({
|
|
1197
|
+
remediationMode: KubernetesAiRemediationMode.Disabled,
|
|
1198
|
+
isRemediationReady: false,
|
|
1199
|
+
gaps: [
|
|
1200
|
+
{
|
|
1201
|
+
code: "remediation_disabled",
|
|
1202
|
+
title: "AI remediation is turned off for this cluster",
|
|
1203
|
+
description: "",
|
|
1204
|
+
nextStep: "",
|
|
1205
|
+
blocks: "remediation",
|
|
1206
|
+
},
|
|
1207
|
+
],
|
|
1208
|
+
});
|
|
1209
|
+
},
|
|
1210
|
+
"no longer allows AI remediation",
|
|
1211
|
+
],
|
|
1212
|
+
[
|
|
1213
|
+
"the cluster re-bound to another Runner",
|
|
1214
|
+
(): KubernetesClusterAiAccessStatus | null => {
|
|
1215
|
+
return cluster({
|
|
1216
|
+
runner: {
|
|
1217
|
+
id: OTHER_RUNNER_ID.toString(),
|
|
1218
|
+
name: "kubernetes-agent/prod-us-v2",
|
|
1219
|
+
isOnline: true,
|
|
1220
|
+
canRunAiCommands: true,
|
|
1221
|
+
},
|
|
1222
|
+
});
|
|
1223
|
+
},
|
|
1224
|
+
"re-bound",
|
|
1225
|
+
],
|
|
1226
|
+
[
|
|
1227
|
+
"the cluster deleted",
|
|
1228
|
+
(): KubernetesClusterAiAccessStatus | null => {
|
|
1229
|
+
return null;
|
|
1230
|
+
},
|
|
1231
|
+
"no longer exists",
|
|
1232
|
+
],
|
|
1233
|
+
])(
|
|
1234
|
+
"%s: the riskier change kept earlier is no longer proposed",
|
|
1235
|
+
async (
|
|
1236
|
+
_label: string,
|
|
1237
|
+
revoked: () => KubernetesClusterAiAccessStatus | null,
|
|
1238
|
+
refusal: string,
|
|
1239
|
+
) => {
|
|
1240
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
1241
|
+
proposesRefusedCommands: true,
|
|
1242
|
+
});
|
|
1243
|
+
|
|
1244
|
+
await execute(
|
|
1245
|
+
toolkit,
|
|
1246
|
+
kubectlArgs({ command: RISKY_WRITE, rollbackCommand: SAFE_UNDO }),
|
|
1247
|
+
);
|
|
1248
|
+
expect(toolkit.getCommandsNeedingApproval()).toHaveLength(1);
|
|
1249
|
+
|
|
1250
|
+
liveStatus.mockResolvedValue(revoked());
|
|
1251
|
+
|
|
1252
|
+
const next: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
1253
|
+
|
|
1254
|
+
expect(next.success).toBe(false);
|
|
1255
|
+
expect(next.textForLlm).toContain(refusal);
|
|
1256
|
+
expect(toolkit.getCommandsNeedingApproval()).toHaveLength(0);
|
|
1257
|
+
expect(enqueueKubectl).not.toHaveBeenCalled();
|
|
1258
|
+
},
|
|
1259
|
+
);
|
|
1260
|
+
|
|
1261
|
+
it("negative control: with the cluster unchanged, a second riskier change is kept alongside the first", async () => {
|
|
1262
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
1263
|
+
proposesRefusedCommands: true,
|
|
1264
|
+
});
|
|
1265
|
+
|
|
1266
|
+
await execute(
|
|
1267
|
+
toolkit,
|
|
1268
|
+
kubectlArgs({ command: RISKY_WRITE, rollbackCommand: SAFE_UNDO }),
|
|
1269
|
+
);
|
|
1270
|
+
await execute(
|
|
1271
|
+
toolkit,
|
|
1272
|
+
kubectlArgs({ command: RISKY_WRITE_2, rollbackCommand: undefined }),
|
|
1273
|
+
);
|
|
1274
|
+
|
|
1275
|
+
expect(
|
|
1276
|
+
toolkit
|
|
1277
|
+
.getCommandsNeedingApproval()
|
|
1278
|
+
.map((kept: RemediationCommandNeedingApproval) => {
|
|
1279
|
+
return kept.command.command;
|
|
1280
|
+
}),
|
|
1281
|
+
).toEqual([RISKY_WRITE, RISKY_WRITE_2]);
|
|
1282
|
+
});
|
|
1283
|
+
});
|
|
1284
|
+
|
|
1285
|
+
/*
|
|
1286
|
+
* The reason a kept change carries onto the approval card names what
|
|
1287
|
+
* actually holds it back (PR #3953 review, remediation-r2-05). A write in a
|
|
1288
|
+
* protected namespace — and a node drain, a node taint or a patch of a
|
|
1289
|
+
* Node — needs a human in EVERY
|
|
1290
|
+
* mode, so "a riskier change on a cluster that only runs safe changes" would
|
|
1291
|
+
* be false on a Bypass cluster, and for a one-object restart in kube-system.
|
|
1292
|
+
*/
|
|
1293
|
+
describe("RemediationCommandToolkit says why a kept change needs a human", () => {
|
|
1294
|
+
beforeEach(mockHappyPath);
|
|
1295
|
+
afterEach(() => {
|
|
1296
|
+
jest.restoreAllMocks();
|
|
1297
|
+
});
|
|
1298
|
+
|
|
1299
|
+
const KUBE_SYSTEM_RESTART: string =
|
|
1300
|
+
"kubectl rollout restart deployment/coredns -n kube-system";
|
|
1301
|
+
|
|
1302
|
+
async function keptReason(
|
|
1303
|
+
mode: KubernetesAiRemediationMode,
|
|
1304
|
+
command: string,
|
|
1305
|
+
): Promise<{ reason: string; text: string }> {
|
|
1306
|
+
const target: KubernetesClusterAiAccessStatus = cluster({
|
|
1307
|
+
remediationMode: mode,
|
|
1308
|
+
});
|
|
1309
|
+
liveStatus.mockResolvedValue(target);
|
|
1310
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
1311
|
+
clusterTargets: [target],
|
|
1312
|
+
proposesRefusedCommands: true,
|
|
1313
|
+
});
|
|
1314
|
+
|
|
1315
|
+
const outcome: ToolCallOutcome = await execute(
|
|
1316
|
+
toolkit,
|
|
1317
|
+
kubectlArgs({ command, rollbackCommand: undefined }),
|
|
1318
|
+
);
|
|
1319
|
+
|
|
1320
|
+
expect(outcome.success).toBe(false);
|
|
1321
|
+
expect(enqueueKubectl).not.toHaveBeenCalled();
|
|
1322
|
+
const kept: Array<RemediationCommandNeedingApproval> =
|
|
1323
|
+
toolkit.getCommandsNeedingApproval();
|
|
1324
|
+
expect(kept).toHaveLength(1);
|
|
1325
|
+
return { reason: kept[0]!.reason, text: outcome.textForLlm };
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
it("on a Bypass cluster, a restart in kube-system names the protected namespace — never 'only runs safe changes'", async () => {
|
|
1329
|
+
const kept: { reason: string; text: string } = await keptReason(
|
|
1330
|
+
KubernetesAiRemediationMode.BypassApproval,
|
|
1331
|
+
KUBE_SYSTEM_RESTART,
|
|
1332
|
+
);
|
|
1333
|
+
|
|
1334
|
+
expect(kept.reason).toContain("protected namespace kube-system");
|
|
1335
|
+
expect(kept.reason).toContain(
|
|
1336
|
+
"writes in kube-system always need a human, in every mode",
|
|
1337
|
+
);
|
|
1338
|
+
expect(kept.reason).not.toContain("safe changes");
|
|
1339
|
+
expect(kept.reason).not.toContain("riskier change");
|
|
1340
|
+
expect(kept.text).toContain(
|
|
1341
|
+
"writes in kube-system always need a human, in every mode",
|
|
1342
|
+
);
|
|
1343
|
+
});
|
|
1344
|
+
|
|
1345
|
+
it("on an Automatic cluster, the same SAFE restart in kube-system is not called a riskier change", async () => {
|
|
1346
|
+
const kept: { reason: string; text: string } = await keptReason(
|
|
1347
|
+
KubernetesAiRemediationMode.Automatic,
|
|
1348
|
+
KUBE_SYSTEM_RESTART,
|
|
1349
|
+
);
|
|
1350
|
+
|
|
1351
|
+
expect(kept.reason).toContain("protected namespace kube-system");
|
|
1352
|
+
expect(kept.reason).not.toContain("riskier change");
|
|
1353
|
+
});
|
|
1354
|
+
|
|
1355
|
+
it("a node drain names the drain — it needs a human in every mode", async () => {
|
|
1356
|
+
const kept: { reason: string; text: string } = await keptReason(
|
|
1357
|
+
KubernetesAiRemediationMode.Automatic,
|
|
1358
|
+
"kubectl drain n1 --ignore-daemonsets",
|
|
1359
|
+
);
|
|
1360
|
+
|
|
1361
|
+
expect(kept.reason).toContain("a node drain always needs a human");
|
|
1362
|
+
expect(kept.reason).not.toContain("riskier change");
|
|
1363
|
+
});
|
|
1364
|
+
|
|
1365
|
+
/*
|
|
1366
|
+
* Round four: a patch of a Node always needs a human too (it can write
|
|
1367
|
+
* the Node's taints). The toolkit reads that off the policy's verdict
|
|
1368
|
+
* (requiresHuman), not the verb: a Bypass cluster runs every other patch
|
|
1369
|
+
* on its own, so "does not allow it without a human" would be false.
|
|
1370
|
+
*/
|
|
1371
|
+
it.each([
|
|
1372
|
+
["an Automatic cluster", KubernetesAiRemediationMode.Automatic],
|
|
1373
|
+
["a Bypass cluster", KubernetesAiRemediationMode.BypassApproval],
|
|
1374
|
+
])(
|
|
1375
|
+
"on %s, a patch of a Node names the Node patch — it needs a human in every mode",
|
|
1376
|
+
async (_label: string, mode: KubernetesAiRemediationMode) => {
|
|
1377
|
+
const kept: { reason: string; text: string } = await keptReason(
|
|
1378
|
+
mode,
|
|
1379
|
+
`kubectl patch node n1 -p '{"spec":{"unschedulable":true}}'`,
|
|
1380
|
+
);
|
|
1381
|
+
|
|
1382
|
+
expect(kept.reason).toBe(
|
|
1383
|
+
"a patch of a Node always needs a human, in every mode",
|
|
1384
|
+
);
|
|
1385
|
+
expect(kept.reason).not.toContain("riskier change");
|
|
1386
|
+
expect(kept.reason).not.toContain("does not allow it");
|
|
1387
|
+
expect(kept.text).toContain(
|
|
1388
|
+
"kubectl patch of a Node can set, replace or clear its taints",
|
|
1389
|
+
);
|
|
1390
|
+
expect(kept.text).toContain(
|
|
1391
|
+
"a patch of a Node always needs a human, in every mode — Bypass approval and the cluster's allowlist included",
|
|
1392
|
+
);
|
|
1393
|
+
},
|
|
1394
|
+
);
|
|
1395
|
+
|
|
1396
|
+
it("a taint names the taint, not a patch of a Node", async () => {
|
|
1397
|
+
const kept: { reason: string; text: string } = await keptReason(
|
|
1398
|
+
KubernetesAiRemediationMode.BypassApproval,
|
|
1399
|
+
"kubectl taint nodes n1 dedicated=ai:NoSchedule",
|
|
1400
|
+
);
|
|
1401
|
+
|
|
1402
|
+
expect(kept.reason).toBe(
|
|
1403
|
+
"a node taint always needs a human, in every mode",
|
|
1404
|
+
);
|
|
1405
|
+
});
|
|
1406
|
+
|
|
1407
|
+
it("negative control: a patch of a Deployment on an Automatic cluster is still a riskier change", async () => {
|
|
1408
|
+
const kept: { reason: string; text: string } = await keptReason(
|
|
1409
|
+
KubernetesAiRemediationMode.Automatic,
|
|
1410
|
+
`kubectl patch deployment web -n web -p '{"spec":{"replicas":2}}'`,
|
|
1411
|
+
);
|
|
1412
|
+
|
|
1413
|
+
expect(kept.reason).toContain("riskier change");
|
|
1414
|
+
expect(kept.reason).not.toContain("always needs a human");
|
|
1415
|
+
});
|
|
1416
|
+
|
|
1417
|
+
it("negative control: an ordinary riskier change on an Automatic cluster is still a riskier change, and the allowlist is named", async () => {
|
|
1418
|
+
const kept: { reason: string; text: string } = await keptReason(
|
|
1419
|
+
KubernetesAiRemediationMode.Automatic,
|
|
1420
|
+
RISKY_WRITE,
|
|
1421
|
+
);
|
|
1422
|
+
|
|
1423
|
+
expect(kept.reason).toContain("riskier change");
|
|
1424
|
+
expect(kept.reason).toContain("kubectl allowlist");
|
|
1425
|
+
expect(kept.reason).not.toContain("protected namespace");
|
|
1426
|
+
});
|
|
1427
|
+
});
|
|
1428
|
+
|
|
1429
|
+
/*
|
|
1430
|
+
* The run's first change on a cluster re-checks, under the breaker lock,
|
|
1431
|
+
* that no other AI run holds the cluster (PR #3953 review,
|
|
1432
|
+
* remediation-r2-04): a rule-driven run that changed the cluster since this
|
|
1433
|
+
* run started is visible only now.
|
|
1434
|
+
*/
|
|
1435
|
+
describe("RemediationCommandToolkit checks for another AI run holding the cluster at its first change", () => {
|
|
1436
|
+
beforeEach(mockHappyPath);
|
|
1437
|
+
afterEach(() => {
|
|
1438
|
+
jest.restoreAllMocks();
|
|
1439
|
+
});
|
|
1440
|
+
|
|
1441
|
+
const INCIDENT_ID: ObjectID = new ObjectID(
|
|
1442
|
+
"cccccccc-cccc-4ccc-8ccc-cccccccccccc",
|
|
1443
|
+
);
|
|
1444
|
+
|
|
1445
|
+
// Another run's kubectl job on the cluster, as the hold check reads it.
|
|
1446
|
+
function kubectlJobOf(suggestionId: ObjectID, stepId: string): RunnerJob {
|
|
1447
|
+
return {
|
|
1448
|
+
id: ObjectID.generate(),
|
|
1449
|
+
_id: ObjectID.generate().toString(),
|
|
1450
|
+
autoRemediationSuggestionId: suggestionId,
|
|
1451
|
+
stepId,
|
|
1452
|
+
} as unknown as RunnerJob;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
function mockOtherRun(row: Record<string, unknown> | null): void {
|
|
1456
|
+
jobFindBy.mockResolvedValue(
|
|
1457
|
+
row ? [kubectlJobOf(OTHER_SUGGESTION_ID, "ai-command-1")] : [],
|
|
1458
|
+
);
|
|
1459
|
+
(
|
|
1460
|
+
AutoRemediationSuggestionService.findBy as unknown as jest.SpyInstance
|
|
1461
|
+
).mockImplementation(
|
|
1462
|
+
async (args: unknown): Promise<Array<AutoRemediationSuggestion>> => {
|
|
1463
|
+
const query: Record<string, unknown> =
|
|
1464
|
+
(args as { query?: Record<string, unknown> }).query || {};
|
|
1465
|
+
// Only the read of the runs behind those jobs names ids.
|
|
1466
|
+
return (query["_id"] && row
|
|
1467
|
+
? [row]
|
|
1468
|
+
: []) as unknown as Array<AutoRemediationSuggestion>;
|
|
1469
|
+
},
|
|
1470
|
+
);
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
function ruleRun(
|
|
1474
|
+
overrides: Record<string, unknown> = {},
|
|
1475
|
+
): Record<string, unknown> {
|
|
1476
|
+
return {
|
|
1477
|
+
id: OTHER_SUGGESTION_ID,
|
|
1478
|
+
_id: OTHER_SUGGESTION_ID.toString(),
|
|
1479
|
+
status: AutoRemediationSuggestionStatus.Planning,
|
|
1480
|
+
executionMode: AutoRemediationExecutionMode.FullAuto,
|
|
1481
|
+
incidentId: new ObjectID("dddddddd-dddd-4ddd-8ddd-dddddddddddd"),
|
|
1482
|
+
ruleNameSnapshot: "Restart web on 5xx",
|
|
1483
|
+
createdAt: new Date(Date.now() + 60 * 1000),
|
|
1484
|
+
...overrides,
|
|
1485
|
+
};
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
it("refuses the change while a rule-driven run is still changing the cluster — whatever the order — and keeps it for a proposal", async () => {
|
|
1489
|
+
mockOtherRun(ruleRun());
|
|
1490
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
1491
|
+
proposesRefusedCommands: true,
|
|
1492
|
+
suggestionCreatedAt: new Date(),
|
|
1493
|
+
clusterHold: { anyOrder: false },
|
|
1494
|
+
});
|
|
1495
|
+
|
|
1496
|
+
const outcome: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
1497
|
+
|
|
1498
|
+
expect(outcome.success).toBe(false);
|
|
1499
|
+
expect(outcome.textForLlm).toContain(
|
|
1500
|
+
'Another OneUptime AI run on cluster "prod-us" is still changing it',
|
|
1501
|
+
);
|
|
1502
|
+
expect(enqueueKubectl).not.toHaveBeenCalled();
|
|
1503
|
+
expect(release).toHaveBeenCalledTimes(1);
|
|
1504
|
+
const kept: Array<RemediationCommandNeedingApproval> =
|
|
1505
|
+
toolkit.getCommandsNeedingApproval();
|
|
1506
|
+
expect(kept).toHaveLength(1);
|
|
1507
|
+
expect(kept[0]!.reason).toContain("is still changing it");
|
|
1508
|
+
});
|
|
1509
|
+
|
|
1510
|
+
it("refuses the change while a rule-driven run's fix on the cluster is still being verified", async () => {
|
|
1511
|
+
mockOtherRun(
|
|
1512
|
+
ruleRun({
|
|
1513
|
+
status: AutoRemediationSuggestionStatus.AutoExecuted,
|
|
1514
|
+
verificationStatus: AutoRemediationVerificationStatus.Pending,
|
|
1515
|
+
verificationDeadlineAt: new Date(Date.now() + 10 * 60 * 1000),
|
|
1516
|
+
}),
|
|
1517
|
+
);
|
|
1518
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
1519
|
+
clusterHold: {
|
|
1520
|
+
anyOrder: true,
|
|
1521
|
+
subject: { incidentId: INCIDENT_ID },
|
|
1522
|
+
},
|
|
1523
|
+
});
|
|
1524
|
+
|
|
1525
|
+
const outcome: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
1526
|
+
|
|
1527
|
+
expect(outcome.success).toBe(false);
|
|
1528
|
+
expect(outcome.textForLlm).toContain("still being verified");
|
|
1529
|
+
expect(enqueueKubectl).not.toHaveBeenCalled();
|
|
1530
|
+
});
|
|
1531
|
+
|
|
1532
|
+
it("negative control: once that fix was verified the change runs", async () => {
|
|
1533
|
+
mockOtherRun(
|
|
1534
|
+
ruleRun({
|
|
1535
|
+
status: AutoRemediationSuggestionStatus.AutoExecuted,
|
|
1536
|
+
verificationStatus: AutoRemediationVerificationStatus.Verified,
|
|
1537
|
+
}),
|
|
1538
|
+
);
|
|
1539
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
1540
|
+
clusterHold: { anyOrder: false },
|
|
1541
|
+
});
|
|
1542
|
+
|
|
1543
|
+
const outcome: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
1544
|
+
|
|
1545
|
+
expect(outcome.success).toBe(true);
|
|
1546
|
+
expect(enqueueKubectl).toHaveBeenCalledTimes(1);
|
|
1547
|
+
});
|
|
1548
|
+
|
|
1549
|
+
it("negative control: nothing else on the cluster — the change runs", async () => {
|
|
1550
|
+
mockOtherRun(null);
|
|
1551
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
1552
|
+
clusterHold: { anyOrder: false },
|
|
1553
|
+
});
|
|
1554
|
+
|
|
1555
|
+
const outcome: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
1556
|
+
|
|
1557
|
+
expect(outcome.success).toBe(true);
|
|
1558
|
+
expect(enqueueKubectl).toHaveBeenCalledTimes(1);
|
|
1559
|
+
});
|
|
1560
|
+
|
|
1561
|
+
it("fails CLOSED when the hold check cannot be read: refused, lock released", async () => {
|
|
1562
|
+
mockOtherRun(null);
|
|
1563
|
+
(
|
|
1564
|
+
AutoRemediationSuggestionService.findBy as unknown as jest.SpyInstance
|
|
1565
|
+
).mockImplementation(async (args: unknown): Promise<never> => {
|
|
1566
|
+
const query: Record<string, unknown> =
|
|
1567
|
+
(args as { query?: Record<string, unknown> }).query || {};
|
|
1568
|
+
if (query["executionMode"]) {
|
|
1569
|
+
// The breaker's in-flight read works...
|
|
1570
|
+
return [] as never;
|
|
1571
|
+
}
|
|
1572
|
+
// ...the hold check's does not.
|
|
1573
|
+
throw new Error("db down");
|
|
1574
|
+
});
|
|
1575
|
+
const toolkit: RemediationCommandToolkit = buildToolkit({
|
|
1576
|
+
clusterHold: { anyOrder: false },
|
|
1577
|
+
});
|
|
1578
|
+
|
|
1579
|
+
const outcome: ToolCallOutcome = await execute(toolkit, kubectlArgs());
|
|
1580
|
+
|
|
1581
|
+
expect(outcome.success).toBe(false);
|
|
1582
|
+
expect(enqueueKubectl).not.toHaveBeenCalled();
|
|
1583
|
+
expect(release).toHaveBeenCalledTimes(1);
|
|
1584
|
+
});
|
|
1585
|
+
});
|