@oneuptime/common 14.0.0 → 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/Index.ts +4 -0
- 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/ServiceLevelObjectiveFeed.ts +2 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.ts +522 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.ts +38 -4
- package/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.ts +603 -0
- 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/1794100000000-AddSloLabelAndOwnerRules.ts +248 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.ts +19 -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 +14 -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/DatabaseService.ts +10 -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/Index.ts +4 -0
- 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 +654 -54
- 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/ServiceLevelObjectiveLabelRuleEngineService.ts +385 -0
- package/Server/Services/ServiceLevelObjectiveLabelRuleService.ts +51 -0
- package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +92 -16
- package/Server/Services/ServiceLevelObjectiveMonitorRuleService.ts +36 -1
- package/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.ts +521 -0
- package/Server/Services/ServiceLevelObjectiveOwnerRuleService.ts +51 -0
- package/Server/Services/ServiceLevelObjectiveService.ts +42 -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/Types/Workflow/Components/Telegram/SendMessageToChat.ts +27 -1
- 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/AI/Toolbox/SloTools.ts +1 -0
- 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/AffectedResourceList.ts +31 -73
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +1 -1
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +1 -1
- package/Server/Utils/Monitor/MonitorAlert.ts +83 -9
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +181 -136
- 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/Monitor/RootCauseList.ts +139 -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/Rules/RuleRun/RuleRunRegistry.ts +28 -0
- 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/Slo/SloRulePatternValidator.ts +42 -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/FeedFilterSortIntegration.test.tsx +1632 -0
- package/Tests/App/Dashboard/FeedOptionsLocales.test.ts +250 -0
- package/Tests/App/Dashboard/FeedOrdering.test.ts +279 -9
- 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/InvestigationFeedRefresh.test.tsx +60 -1
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +606 -3
- 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/ProductOrientation.test.tsx +646 -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/ResourceFeed.test.tsx +612 -2
- package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +30 -0
- package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +1202 -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/ScheduledMaintenanceFeedRefresh.test.tsx +52 -1
- 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/SloFeed.test.tsx +106 -1
- package/Tests/App/Dashboard/SloMonitorRuleCriteria.test.tsx +566 -0
- package/Tests/App/Dashboard/SloNavigationWiring.test.tsx +6 -4
- package/Tests/App/Dashboard/SloNoticeBanner.test.tsx +186 -0
- package/Tests/App/Dashboard/SloOverviewKpiStrip.test.tsx +388 -44
- package/Tests/App/Dashboard/SloOverviewLayout.test.tsx +613 -0
- 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/ResourceFeedModels.test.ts +0 -8
- package/Tests/Models/DatabaseModels/SloLabelAndOwnerRuleModels.test.ts +404 -0
- package/Tests/Models/DatabaseModels/SloOverhaulModels.test.ts +17 -1
- package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -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/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
- package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +589 -0
- package/Tests/Server/Infrastructure/Postgres/AddOAuth2WorkflowVariablesMigration.test.ts +254 -0
- package/Tests/Server/Infrastructure/Postgres/AddSloLabelAndOwnerRulesMigration.test.ts +407 -0
- package/Tests/Server/Infrastructure/Postgres/AddSloMonitorRuleMonitorTypeMigration.test.ts +171 -0
- package/Tests/Server/Infrastructure/Postgres/SloProductOverhaulMigration.test.ts +88 -4
- 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/DatabaseServiceRuleCriteriaValidation.test.ts +104 -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/LabelRuleEnginesGroupBRun.test.ts +48 -1
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupARun.test.ts +28 -0
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +39 -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 +1175 -0
- package/Tests/Server/Services/ServiceLevelObjectiveLabelAndOwnerRuleServices.test.ts +172 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +323 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleService.test.ts +115 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleTriggers.test.ts +21 -2
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorTypePostgres.test.ts +480 -0
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +174 -0
- package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +45 -6
- 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/FeedEventTypeFilterQuery.test.ts +745 -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 +1035 -1
- package/Tests/Server/Types/Workflow/Components/ApiComponentSsrf.test.ts +49 -0
- package/Tests/Server/Types/Workflow/Components/TelegramSendMessageToChat.test.ts +331 -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/AI/SloTools.test.ts +10 -0
- 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/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +1 -1
- 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/MonitorCriteriaEvaluator.test.ts +405 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorBreachingSamples.test.ts +1156 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +106 -80
- 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/Monitor/RootCauseList.test.ts +252 -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/Rules/RuleRun/RuleRunPermission.test.ts +154 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +111 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +288 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayUserFlowReadService.test.ts +304 -0
- package/Tests/Server/Utils/Slo/SloRulePatternValidator.test.ts +67 -0
- package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +40 -3
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +213 -0
- 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/API/Route.test.ts +56 -0
- package/Tests/Types/API/URLSchemeLikePath.test.ts +304 -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/FeedOptions.test.ts +1404 -0
- package/Tests/UI/Components/FeedOptionsButton.test.tsx +2603 -0
- 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/Icon.test.tsx +26 -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/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
- 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/Components/useFeedItemsViewKey.test.tsx +1017 -0
- package/Tests/UI/Components/useFeedOptions.test.tsx +1107 -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/LabelRuleImportExport.test.ts +102 -0
- 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/Slo/SloDuration.test.ts +115 -24
- package/Tests/Utils/Slo/SloHealth.test.ts +93 -10
- package/Tests/Utils/Slo/SloMonitorRuleCriteria.test.ts +109 -0
- package/Tests/Utils/Slo/SloOverviewText.test.ts +64 -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/API/Route.ts +19 -2
- package/Types/API/URL.ts +26 -2
- 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/MetricMonitor/MetricCriteriaContext.ts +6 -5
- package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +9 -0
- package/Types/Monitor/MonitorStepExceptionMonitor.ts +56 -0
- package/Types/Monitor/MonitorUptimeSummary.ts +113 -0
- package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +11 -0
- package/Types/Permission.ts +90 -0
- package/Types/Rules/RuleCriteriaFieldRegistry.ts +11 -0
- package/Types/Rules/RuleRun.ts +4 -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/Feed/FeedOptions.ts +427 -0
- package/UI/Components/Feed/FeedOptionsButton.tsx +692 -0
- package/UI/Components/Feed/useFeedItems.ts +28 -9
- package/UI/Components/Feed/useFeedOptions.ts +225 -0
- 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/Icon/Icon.tsx +1 -1
- 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/LabelRuleImportExport.ts +27 -9
- package/Utils/Monitor/IncomingEmailMonitorAddress.ts +292 -0
- package/Utils/Monitor/MetricValueFormatter.ts +2 -2
- 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/Rules/MonitorTypeRuleCriteria.ts +31 -0
- package/Utils/Rum/SessionIdentity.ts +44 -0
- package/Utils/Rum/SessionReplayWireEncoding.ts +137 -0
- package/Utils/Rum/UserFlow.ts +1527 -0
- package/Utils/Slo/SloDuration.ts +22 -6
- package/Utils/Slo/SloHealth.ts +8 -7
- package/Utils/Slo/SloMonitorRuleCriteria.ts +33 -1
- package/Utils/Slo/SloOverviewText.ts +33 -0
- package/Utils/Slo/SloProjection.ts +4 -3
- 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/Index.js +4 -0
- package/build/dist/Models/DatabaseModels/Index.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/ServiceLevelObjectiveFeed.js +2 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js +530 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveLabelRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js +39 -4
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js +612 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerRule.js.map +1 -0
- 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/1794100000000-AddSloLabelAndOwnerRules.js +93 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794100000000-AddSloLabelAndOwnerRules.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1794200000000-AddSloMonitorRuleMonitorType.js.map +1 -0
- 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 +14 -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/DatabaseService.js +7 -0
- package/build/dist/Server/Services/DatabaseService.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/Index.js +4 -0
- package/build/dist/Server/Services/Index.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 +490 -50
- 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/ServiceLevelObjectiveLabelRuleEngineService.js +295 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js +56 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveLabelRuleService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +59 -14
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js +22 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js +400 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js +56 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerRuleService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js +33 -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/Types/Workflow/Components/Telegram/SendMessageToChat.js +20 -1
- package/build/dist/Server/Types/Workflow/Components/Telegram/SendMessageToChat.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/AI/Toolbox/SloTools.js +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.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/AffectedResourceList.js +22 -51
- package/build/dist/Server/Utils/Monitor/AffectedResourceList.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +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/MonitorCriteriaEvaluator.js +139 -109
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.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/Monitor/RootCauseList.js +101 -0
- package/build/dist/Server/Utils/Monitor/RootCauseList.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/Rules/RuleRun/RuleRunRegistry.js +28 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.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/Slo/SloRulePatternValidator.js +28 -0
- package/build/dist/Server/Utils/Slo/SloRulePatternValidator.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/API/Route.js +18 -2
- package/build/dist/Types/API/Route.js.map +1 -1
- package/build/dist/Types/API/URL.js +23 -2
- package/build/dist/Types/API/URL.js.map +1 -1
- 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/Permission.js +84 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +11 -0
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
- package/build/dist/Types/Rules/RuleRun.js +4 -0
- package/build/dist/Types/Rules/RuleRun.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/Feed/FeedOptions.js +235 -0
- package/build/dist/UI/Components/Feed/FeedOptions.js.map +1 -0
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js +346 -0
- package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -0
- package/build/dist/UI/Components/Feed/useFeedItems.js +17 -7
- package/build/dist/UI/Components/Feed/useFeedItems.js.map +1 -1
- package/build/dist/UI/Components/Feed/useFeedOptions.js +130 -0
- package/build/dist/UI/Components/Feed/useFeedOptions.js.map +1 -0
- 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/Icon/Icon.js +1 -1
- package/build/dist/UI/Components/Icon/Icon.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/LabelRuleImportExport.js +22 -6
- package/build/dist/Utils/LabelRuleImportExport.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/MetricValueFormatter.js +2 -2
- 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/Rules/MonitorTypeRuleCriteria.js +18 -0
- package/build/dist/Utils/Rules/MonitorTypeRuleCriteria.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/Slo/SloDuration.js +13 -5
- package/build/dist/Utils/Slo/SloDuration.js.map +1 -1
- package/build/dist/Utils/Slo/SloHealth.js +8 -7
- package/build/dist/Utils/Slo/SloHealth.js.map +1 -1
- package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js +16 -1
- package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js.map +1 -1
- package/build/dist/Utils/Slo/SloOverviewText.js +16 -0
- package/build/dist/Utils/Slo/SloOverviewText.js.map +1 -1
- package/build/dist/Utils/Slo/SloProjection.js.map +1 -1
- 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,1840 @@
|
|
|
1
|
+
import KubectlInvestigationToolkit, {
|
|
2
|
+
INVESTIGATION_MAX_WALL_CLOCK_MS,
|
|
3
|
+
KUBECTL_RESULT_UNKNOWN_EVENT_PREFIX,
|
|
4
|
+
LIST_CLUSTER_ACCESS_TOOL_NAME,
|
|
5
|
+
RUN_KUBECTL_TOOL_NAME,
|
|
6
|
+
} from "../../../../Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit";
|
|
7
|
+
import { isKubectlResultUnknownMessage } from "../../../../../App/FeatureSet/Dashboard/src/Components/AI/ClusterToolFormat";
|
|
8
|
+
import { KUBECTL_RESULT_UNKNOWN_EVENT_PREFIX as SHARED_KUBECTL_RESULT_UNKNOWN_PREFIX } from "../../../../Types/Kubernetes/KubernetesClusterAiAccessToolNames";
|
|
9
|
+
import KubectlJobRunner, {
|
|
10
|
+
KUBECTL_CLAIM_TIMEOUT_MS,
|
|
11
|
+
KUBECTL_OUTPUT_TRUNCATED_SUFFIX,
|
|
12
|
+
RedactedKubectlOutput,
|
|
13
|
+
} from "../../../../Server/Utils/AI/ClusterAccess/KubectlJobRunner";
|
|
14
|
+
import { MAX_WALL_CLOCK_MS as ENGINE_MAX_WALL_CLOCK_MS } from "../../../../Server/Utils/AI/SRE/AIInvestigationEngine";
|
|
15
|
+
import { ObservabilityAssistantExtraTool } from "../../../../Server/Utils/AI/Chat/ObservabilityAssistant";
|
|
16
|
+
import { ToolCallOutcome } from "../../../../Server/Utils/AI/Toolbox/Index";
|
|
17
|
+
import AIRunService from "../../../../Server/Services/AIRunService";
|
|
18
|
+
import KubernetesClusterAiAccessService from "../../../../Server/Services/KubernetesClusterAiAccessService";
|
|
19
|
+
import RunnerJobService from "../../../../Server/Services/RunnerJobService";
|
|
20
|
+
import RunnerJob from "../../../../Models/DatabaseModels/RunnerJob";
|
|
21
|
+
import RunnerJobOrigin from "../../../../Types/Runbook/RunnerJobOrigin";
|
|
22
|
+
import RunnerJobStatus from "../../../../Types/Runbook/RunnerJobStatus";
|
|
23
|
+
import AIRunStatus from "../../../../Types/AI/AIRunStatus";
|
|
24
|
+
import {
|
|
25
|
+
DEFAULT_KUBECTL_TIMEOUT_MS,
|
|
26
|
+
KubernetesAiRemediationMode,
|
|
27
|
+
KubernetesClusterAiAccessStatus,
|
|
28
|
+
MAX_KUBECTL_COMMANDS_PER_INVESTIGATION,
|
|
29
|
+
MAX_KUBECTL_OUTPUT_CHARS_FOR_LLM,
|
|
30
|
+
MAX_KUBECTL_TIMEOUT_MS,
|
|
31
|
+
} from "../../../../Types/Kubernetes/KubernetesClusterAiAccess";
|
|
32
|
+
import { KUBECTL_REDACTED_MARKER } from "../../../../Utils/AiRemediation/KubectlOutputRedactor";
|
|
33
|
+
import KubectlWaitBudget, {
|
|
34
|
+
KUBECTL_WAIT_OVERHEAD_MS,
|
|
35
|
+
MIN_KUBECTL_CLAIM_TIMEOUT_MS,
|
|
36
|
+
} from "../../../../Utils/AiRemediation/KubectlWaitBudget";
|
|
37
|
+
import ObjectID from "../../../../Types/ObjectID";
|
|
38
|
+
import { JSONObject } from "../../../../Types/JSON";
|
|
39
|
+
import { afterEach, beforeEach, describe, expect, it } from "@jest/globals";
|
|
40
|
+
|
|
41
|
+
/*
|
|
42
|
+
* Contract under test — the run-scoped, READ-ONLY kubectl toolkit an
|
|
43
|
+
* investigation gets:
|
|
44
|
+
*
|
|
45
|
+
* - no investigation-ready cluster, no tools at all (the model is told in
|
|
46
|
+
* its context why, and a tool that always fails only burns budget);
|
|
47
|
+
* - run_kubectl only accepts a cluster from the ready set, only Read-tier
|
|
48
|
+
* commands (never a Secret read), and at most
|
|
49
|
+
* MAX_KUBECTL_COMMANDS_PER_INVESTIGATION of them;
|
|
50
|
+
* - a command runs as an AiInvestigation-origin job through the shared
|
|
51
|
+
* KubectlJobRunner (which records the outcome on the cluster) and its
|
|
52
|
+
* output reaches the model framed as untrusted cluster data, with Secret
|
|
53
|
+
* data, credential-like env values, tokens and kubeconfig material
|
|
54
|
+
* masked before the model sees any of it;
|
|
55
|
+
* - every command's claim window and execution timeout are planned so the
|
|
56
|
+
* wait ends before the run's wall-clock deadline, and a command the
|
|
57
|
+
* budget can no longer hold is refused before anything is enqueued;
|
|
58
|
+
* - a job failure becomes a tool failure the model can continue from,
|
|
59
|
+
* never a thrown error that kills the investigation;
|
|
60
|
+
* - only a command that reached kubectl is evidence (cited, counted as
|
|
61
|
+
* run); one that never ran is a failed call, one a Runner took whose
|
|
62
|
+
* result never came back is a failed call marked "result unknown"
|
|
63
|
+
* (never "did not run"), and one no Runner claimed makes its Runner —
|
|
64
|
+
* and every cluster it serves — unreachable for the rest of the run.
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
const PROJECT_ID: ObjectID = new ObjectID(
|
|
68
|
+
"22222222-2222-4222-8222-222222222222",
|
|
69
|
+
);
|
|
70
|
+
const RUN_ID: ObjectID = new ObjectID("88888888-8888-4888-8888-888888888888");
|
|
71
|
+
const CLUSTER_ID: ObjectID = new ObjectID(
|
|
72
|
+
"33333333-3333-4333-8333-333333333333",
|
|
73
|
+
);
|
|
74
|
+
const RUNNER_ID: ObjectID = new ObjectID(
|
|
75
|
+
"44444444-4444-4444-8444-444444444444",
|
|
76
|
+
);
|
|
77
|
+
const JOB_ID: ObjectID = new ObjectID("66666666-6666-4666-8666-666666666666");
|
|
78
|
+
|
|
79
|
+
function readyCluster(
|
|
80
|
+
overrides: Partial<KubernetesClusterAiAccessStatus> = {},
|
|
81
|
+
): KubernetesClusterAiAccessStatus {
|
|
82
|
+
return {
|
|
83
|
+
clusterId: CLUSTER_ID.toString(),
|
|
84
|
+
clusterName: "prod-us",
|
|
85
|
+
clusterIdentifier: "prod-us",
|
|
86
|
+
runner: {
|
|
87
|
+
id: RUNNER_ID.toString(),
|
|
88
|
+
name: "kubernetes-agent/prod-us",
|
|
89
|
+
isOnline: true,
|
|
90
|
+
canRunAiCommands: true,
|
|
91
|
+
posture: { inCluster: true, allowWrites: false },
|
|
92
|
+
},
|
|
93
|
+
accessMethod: "in_cluster",
|
|
94
|
+
kubectlAllowlist: [],
|
|
95
|
+
isInvestigationEnabled: true,
|
|
96
|
+
isInvestigationReady: true,
|
|
97
|
+
remediationMode: KubernetesAiRemediationMode.Disabled,
|
|
98
|
+
isRemediationReady: false,
|
|
99
|
+
gaps: [],
|
|
100
|
+
evaluatedAt: new Date().toISOString(),
|
|
101
|
+
...overrides,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function getTool(
|
|
106
|
+
toolkit: KubectlInvestigationToolkit,
|
|
107
|
+
name: string,
|
|
108
|
+
): ObservabilityAssistantExtraTool {
|
|
109
|
+
const tool: ObservabilityAssistantExtraTool | undefined = toolkit
|
|
110
|
+
.buildTools()
|
|
111
|
+
.find((candidate: ObservabilityAssistantExtraTool) => {
|
|
112
|
+
return candidate.definition.name === name;
|
|
113
|
+
});
|
|
114
|
+
if (!tool) {
|
|
115
|
+
throw new Error(`Tool ${name} not offered.`);
|
|
116
|
+
}
|
|
117
|
+
return tool;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function fakeJob(overrides: Partial<Record<string, unknown>> = {}): RunnerJob {
|
|
121
|
+
return {
|
|
122
|
+
id: JOB_ID,
|
|
123
|
+
_id: JOB_ID.toString(),
|
|
124
|
+
status: RunnerJobStatus.Succeeded,
|
|
125
|
+
exitCode: 0,
|
|
126
|
+
output: "NAME READY STATUS RESTARTS\nweb-1 0/1 Pending 0",
|
|
127
|
+
payload: { displayCommand: "kubectl get pods -n web" },
|
|
128
|
+
...overrides,
|
|
129
|
+
} as unknown as RunnerJob;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
describe("KubectlInvestigationToolkit.buildTools", () => {
|
|
133
|
+
it("offers nothing when no cluster is investigation-ready", () => {
|
|
134
|
+
const toolkit: KubectlInvestigationToolkit =
|
|
135
|
+
new KubectlInvestigationToolkit({
|
|
136
|
+
projectId: PROJECT_ID,
|
|
137
|
+
aiRunId: RUN_ID,
|
|
138
|
+
clusters: [
|
|
139
|
+
readyCluster({ isInvestigationReady: false }),
|
|
140
|
+
readyCluster({ runner: null }),
|
|
141
|
+
],
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
expect(toolkit.buildTools()).toEqual([]);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("offers list_cluster_access and run_kubectl for a ready cluster", () => {
|
|
148
|
+
const toolkit: KubectlInvestigationToolkit =
|
|
149
|
+
new KubectlInvestigationToolkit({
|
|
150
|
+
projectId: PROJECT_ID,
|
|
151
|
+
aiRunId: RUN_ID,
|
|
152
|
+
clusters: [readyCluster()],
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
expect(
|
|
156
|
+
toolkit.buildTools().map((tool: ObservabilityAssistantExtraTool) => {
|
|
157
|
+
return tool.definition.name;
|
|
158
|
+
}),
|
|
159
|
+
).toEqual([LIST_CLUSTER_ACCESS_TOOL_NAME, RUN_KUBECTL_TOOL_NAME]);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("admits a remediation-ready cluster when asked to check remediation readiness", () => {
|
|
163
|
+
const cluster: KubernetesClusterAiAccessStatus = readyCluster({
|
|
164
|
+
isInvestigationReady: false,
|
|
165
|
+
isRemediationReady: true,
|
|
166
|
+
remediationMode: KubernetesAiRemediationMode.RequireApproval,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
expect(
|
|
170
|
+
new KubectlInvestigationToolkit({
|
|
171
|
+
projectId: PROJECT_ID,
|
|
172
|
+
aiRunId: RUN_ID,
|
|
173
|
+
clusters: [cluster],
|
|
174
|
+
}).buildTools(),
|
|
175
|
+
).toEqual([]);
|
|
176
|
+
|
|
177
|
+
expect(
|
|
178
|
+
new KubectlInvestigationToolkit({
|
|
179
|
+
projectId: PROJECT_ID,
|
|
180
|
+
aiRunId: RUN_ID,
|
|
181
|
+
clusters: [cluster],
|
|
182
|
+
readinessCheck: "remediation",
|
|
183
|
+
}).buildTools(),
|
|
184
|
+
).toHaveLength(2);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
describe("KubectlInvestigationToolkit run_kubectl", () => {
|
|
189
|
+
let recordOutcome: jest.SpyInstance;
|
|
190
|
+
|
|
191
|
+
beforeEach(() => {
|
|
192
|
+
jest
|
|
193
|
+
.spyOn(AIRunService, "updateOneBy")
|
|
194
|
+
.mockResolvedValue(undefined as never);
|
|
195
|
+
recordOutcome = jest
|
|
196
|
+
.spyOn(KubernetesClusterAiAccessService, "recordCommandOutcome")
|
|
197
|
+
.mockResolvedValue(undefined);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
afterEach(() => {
|
|
201
|
+
jest.restoreAllMocks();
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("lists the ready clusters with their ids", async () => {
|
|
205
|
+
const outcome: ToolCallOutcome = await getTool(
|
|
206
|
+
new KubectlInvestigationToolkit({
|
|
207
|
+
projectId: PROJECT_ID,
|
|
208
|
+
aiRunId: RUN_ID,
|
|
209
|
+
clusters: [readyCluster()],
|
|
210
|
+
}),
|
|
211
|
+
LIST_CLUSTER_ACCESS_TOOL_NAME,
|
|
212
|
+
).execute({});
|
|
213
|
+
|
|
214
|
+
expect(outcome.success).toBe(true);
|
|
215
|
+
expect(outcome.result?.rowCount).toBe(1);
|
|
216
|
+
expect(outcome.textForLlm).toContain(CLUSTER_ID.toString());
|
|
217
|
+
expect(outcome.textForLlm).toContain("prod-us");
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it("refuses a cluster that is not in the ready set", async () => {
|
|
221
|
+
const enqueue: jest.SpyInstance = jest.spyOn(
|
|
222
|
+
RunnerJobService,
|
|
223
|
+
"enqueueAiKubectlCommand",
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
const outcome: ToolCallOutcome = await getTool(
|
|
227
|
+
new KubectlInvestigationToolkit({
|
|
228
|
+
projectId: PROJECT_ID,
|
|
229
|
+
aiRunId: RUN_ID,
|
|
230
|
+
clusters: [readyCluster()],
|
|
231
|
+
}),
|
|
232
|
+
RUN_KUBECTL_TOOL_NAME,
|
|
233
|
+
).execute({
|
|
234
|
+
clusterId: "99999999-9999-4999-8999-999999999999",
|
|
235
|
+
command: "kubectl get pods",
|
|
236
|
+
rationale: "look",
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
expect(outcome.success).toBe(false);
|
|
240
|
+
expect(outcome.textForLlm).toContain("not one of the clusters");
|
|
241
|
+
expect(enqueue).not.toHaveBeenCalled();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("refuses a write and a denied command without enqueueing anything", async () => {
|
|
245
|
+
const enqueue: jest.SpyInstance = jest.spyOn(
|
|
246
|
+
RunnerJobService,
|
|
247
|
+
"enqueueAiKubectlCommand",
|
|
248
|
+
);
|
|
249
|
+
const tool: ObservabilityAssistantExtraTool = getTool(
|
|
250
|
+
new KubectlInvestigationToolkit({
|
|
251
|
+
projectId: PROJECT_ID,
|
|
252
|
+
aiRunId: RUN_ID,
|
|
253
|
+
clusters: [readyCluster()],
|
|
254
|
+
}),
|
|
255
|
+
RUN_KUBECTL_TOOL_NAME,
|
|
256
|
+
);
|
|
257
|
+
|
|
258
|
+
const write: ToolCallOutcome = await tool.execute({
|
|
259
|
+
clusterId: CLUSTER_ID.toString(),
|
|
260
|
+
command: "kubectl rollout restart deployment/web -n web",
|
|
261
|
+
rationale: "restart it",
|
|
262
|
+
});
|
|
263
|
+
expect(write.success).toBe(false);
|
|
264
|
+
expect(write.textForLlm).toContain("would change the cluster");
|
|
265
|
+
expect(write.textForLlm).toContain("SafeWrite");
|
|
266
|
+
|
|
267
|
+
const denied: ToolCallOutcome = await tool.execute({
|
|
268
|
+
clusterId: CLUSTER_ID.toString(),
|
|
269
|
+
command: "kubectl exec -it web -- sh",
|
|
270
|
+
rationale: "poke",
|
|
271
|
+
});
|
|
272
|
+
expect(denied.success).toBe(false);
|
|
273
|
+
expect(denied.textForLlm).toContain("Refused by the kubectl policy");
|
|
274
|
+
|
|
275
|
+
expect(enqueue).not.toHaveBeenCalled();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("runs a read command as an AiInvestigation job and returns the output as untrusted data", async () => {
|
|
279
|
+
const enqueue: jest.SpyInstance = jest
|
|
280
|
+
.spyOn(RunnerJobService, "enqueueAiKubectlCommand")
|
|
281
|
+
.mockResolvedValue(fakeJob());
|
|
282
|
+
jest
|
|
283
|
+
.spyOn(RunnerJobService, "pollUntilTerminal")
|
|
284
|
+
.mockResolvedValue(fakeJob());
|
|
285
|
+
|
|
286
|
+
const toolkit: KubectlInvestigationToolkit =
|
|
287
|
+
new KubectlInvestigationToolkit({
|
|
288
|
+
projectId: PROJECT_ID,
|
|
289
|
+
aiRunId: RUN_ID,
|
|
290
|
+
clusters: [readyCluster()],
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
const outcome: ToolCallOutcome = await getTool(
|
|
294
|
+
toolkit,
|
|
295
|
+
RUN_KUBECTL_TOOL_NAME,
|
|
296
|
+
).execute({
|
|
297
|
+
clusterId: CLUSTER_ID.toString(),
|
|
298
|
+
command: "kubectl get pods -n web",
|
|
299
|
+
rationale: "see pod phases",
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
expect(outcome.success).toBe(true);
|
|
303
|
+
expect(toolkit.getCommandsRun()).toBe(1);
|
|
304
|
+
|
|
305
|
+
const enqueueArgs: Record<string, unknown> = enqueue.mock
|
|
306
|
+
.calls[0]![0] as Record<string, unknown>;
|
|
307
|
+
expect(enqueueArgs["origin"]).toBe(RunnerJobOrigin.AiInvestigation);
|
|
308
|
+
expect((enqueueArgs["kubernetesClusterId"] as ObjectID).toString()).toBe(
|
|
309
|
+
CLUSTER_ID.toString(),
|
|
310
|
+
);
|
|
311
|
+
expect((enqueueArgs["targetAgentId"] as ObjectID).toString()).toBe(
|
|
312
|
+
RUNNER_ID.toString(),
|
|
313
|
+
);
|
|
314
|
+
expect(enqueueArgs["command"]).toBe("kubectl get pods -n web");
|
|
315
|
+
expect(enqueueArgs["credentialId"]).toBeUndefined();
|
|
316
|
+
expect(enqueueArgs["stepId"]).toBe("ai-investigation-kubectl-1");
|
|
317
|
+
// No deadline given: the normal windows, untouched.
|
|
318
|
+
expect(enqueueArgs["timeoutInMs"]).toBe(DEFAULT_KUBECTL_TIMEOUT_MS);
|
|
319
|
+
expect(enqueueArgs["claimTimeoutInMs"]).toBe(KUBECTL_CLAIM_TIMEOUT_MS);
|
|
320
|
+
|
|
321
|
+
expect(outcome.textForLlm).toContain("SUCCEEDED");
|
|
322
|
+
expect(outcome.textForLlm).toContain(
|
|
323
|
+
'<tool_result source="untrusted_cluster_output">',
|
|
324
|
+
);
|
|
325
|
+
expect(outcome.textForLlm).toContain("Pending");
|
|
326
|
+
expect(outcome.result?.citationLabel).toBe(
|
|
327
|
+
'kubectl get pods -n web on cluster "prod-us"',
|
|
328
|
+
);
|
|
329
|
+
expect(recordOutcome).toHaveBeenCalledWith({
|
|
330
|
+
clusterId: expect.any(ObjectID),
|
|
331
|
+
succeeded: true,
|
|
332
|
+
errorMessage: undefined,
|
|
333
|
+
});
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
it("passes the cluster's credential id for a Runner outside the cluster", async () => {
|
|
337
|
+
const enqueue: jest.SpyInstance = jest
|
|
338
|
+
.spyOn(RunnerJobService, "enqueueAiKubectlCommand")
|
|
339
|
+
.mockResolvedValue(fakeJob());
|
|
340
|
+
jest
|
|
341
|
+
.spyOn(RunnerJobService, "pollUntilTerminal")
|
|
342
|
+
.mockResolvedValue(fakeJob());
|
|
343
|
+
|
|
344
|
+
await getTool(
|
|
345
|
+
new KubectlInvestigationToolkit({
|
|
346
|
+
projectId: PROJECT_ID,
|
|
347
|
+
aiRunId: RUN_ID,
|
|
348
|
+
clusters: [
|
|
349
|
+
readyCluster({
|
|
350
|
+
accessMethod: "credential",
|
|
351
|
+
credentialId: "55555555-5555-4555-8555-555555555555",
|
|
352
|
+
}),
|
|
353
|
+
],
|
|
354
|
+
}),
|
|
355
|
+
RUN_KUBECTL_TOOL_NAME,
|
|
356
|
+
).execute({
|
|
357
|
+
clusterId: CLUSTER_ID.toString(),
|
|
358
|
+
command: "kubectl get nodes",
|
|
359
|
+
rationale: "capacity",
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
expect(
|
|
363
|
+
(enqueue.mock.calls[0]![0] as Record<string, unknown>)["credentialId"],
|
|
364
|
+
).toBe("55555555-5555-4555-8555-555555555555");
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
/*
|
|
368
|
+
* A kubectl that ran and exited non-zero is still evidence ("NotFound",
|
|
369
|
+
* "Forbidden" are findings): a completed, cited tool call with rowCount 0
|
|
370
|
+
* the model can continue from — never a thrown error.
|
|
371
|
+
*/
|
|
372
|
+
it("keeps a command that ran and failed as cited evidence with rowCount 0", async () => {
|
|
373
|
+
jest.spyOn(RunnerJobService, "enqueueAiKubectlCommand").mockResolvedValue(
|
|
374
|
+
fakeJob({
|
|
375
|
+
payload: { displayCommand: "kubectl describe pod web-7d9f-abc -n web" },
|
|
376
|
+
}),
|
|
377
|
+
);
|
|
378
|
+
jest.spyOn(RunnerJobService, "pollUntilTerminal").mockResolvedValue(
|
|
379
|
+
fakeJob({
|
|
380
|
+
status: RunnerJobStatus.Failed,
|
|
381
|
+
exitCode: 1,
|
|
382
|
+
errorMessage: "Exit code 1",
|
|
383
|
+
output:
|
|
384
|
+
'[stderr]\nError from server (NotFound): pods "web-7d9f-abc" not found',
|
|
385
|
+
}),
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
const outcome: ToolCallOutcome = await getTool(
|
|
389
|
+
new KubectlInvestigationToolkit({
|
|
390
|
+
projectId: PROJECT_ID,
|
|
391
|
+
aiRunId: RUN_ID,
|
|
392
|
+
clusters: [readyCluster()],
|
|
393
|
+
}),
|
|
394
|
+
RUN_KUBECTL_TOOL_NAME,
|
|
395
|
+
).execute({
|
|
396
|
+
clusterId: CLUSTER_ID.toString(),
|
|
397
|
+
command: "kubectl describe pod web-7d9f-abc -n web",
|
|
398
|
+
rationale: "see why it is pending",
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
expect(outcome.success).toBe(true);
|
|
402
|
+
expect(outcome.result?.rowCount).toBe(0);
|
|
403
|
+
expect(outcome.result?.citationLabel).toBe(
|
|
404
|
+
'kubectl describe pod web-7d9f-abc -n web on cluster "prod-us"',
|
|
405
|
+
);
|
|
406
|
+
expect(outcome.textForLlm).toContain("FAILED (exit code: 1");
|
|
407
|
+
expect(outcome.textForLlm).toContain("NotFound");
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
/*
|
|
411
|
+
* The contract the investigation panel counts from: a command that ran
|
|
412
|
+
* is a completed call whose rowCount says whether kubectl succeeded.
|
|
413
|
+
*/
|
|
414
|
+
it("reports rowCount 1 for a command that succeeded", async () => {
|
|
415
|
+
jest
|
|
416
|
+
.spyOn(RunnerJobService, "enqueueAiKubectlCommand")
|
|
417
|
+
.mockResolvedValue(fakeJob());
|
|
418
|
+
jest
|
|
419
|
+
.spyOn(RunnerJobService, "pollUntilTerminal")
|
|
420
|
+
.mockResolvedValue(fakeJob());
|
|
421
|
+
|
|
422
|
+
const outcome: ToolCallOutcome = await getTool(
|
|
423
|
+
new KubectlInvestigationToolkit({
|
|
424
|
+
projectId: PROJECT_ID,
|
|
425
|
+
aiRunId: RUN_ID,
|
|
426
|
+
clusters: [readyCluster()],
|
|
427
|
+
}),
|
|
428
|
+
RUN_KUBECTL_TOOL_NAME,
|
|
429
|
+
).execute({
|
|
430
|
+
clusterId: CLUSTER_ID.toString(),
|
|
431
|
+
command: "kubectl get pods -n web",
|
|
432
|
+
rationale: "see pod phases",
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
expect(outcome.success).toBe(true);
|
|
436
|
+
expect(outcome.result?.rowCount).toBe(1);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it("reports an enqueue error as a tool failure instead of throwing", async () => {
|
|
440
|
+
jest
|
|
441
|
+
.spyOn(RunnerJobService, "enqueueAiKubectlCommand")
|
|
442
|
+
.mockRejectedValue(new Error("hourly limit"));
|
|
443
|
+
|
|
444
|
+
const outcome: ToolCallOutcome = await getTool(
|
|
445
|
+
new KubectlInvestigationToolkit({
|
|
446
|
+
projectId: PROJECT_ID,
|
|
447
|
+
aiRunId: RUN_ID,
|
|
448
|
+
clusters: [readyCluster()],
|
|
449
|
+
}),
|
|
450
|
+
RUN_KUBECTL_TOOL_NAME,
|
|
451
|
+
).execute({
|
|
452
|
+
clusterId: CLUSTER_ID.toString(),
|
|
453
|
+
command: "kubectl get pods -n web",
|
|
454
|
+
rationale: "see pod phases",
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
expect(outcome.success).toBe(false);
|
|
458
|
+
expect(outcome.textForLlm).toContain("hourly limit");
|
|
459
|
+
expect(outcome.textForLlm).toContain("Continue with OneUptime telemetry");
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
/*
|
|
463
|
+
* C5: reading a Secret is Denied by the policy, so the toolkit refuses it
|
|
464
|
+
* before anything is enqueued — the Runner and the enqueue chokepoint
|
|
465
|
+
* refuse the same, and the tool description says so up front.
|
|
466
|
+
*/
|
|
467
|
+
it.each([
|
|
468
|
+
"kubectl get secret app-db -n prod -o yaml",
|
|
469
|
+
"kubectl get secrets -A -o json",
|
|
470
|
+
"kubectl get secret sa-token -n kube-system -o jsonpath='{.data.token}'",
|
|
471
|
+
"kubectl describe secret app-db -n prod",
|
|
472
|
+
"kubectl get secrets.v1. -n prod",
|
|
473
|
+
"kubectl get secret/app-db -n prod",
|
|
474
|
+
])(
|
|
475
|
+
"refuses to read a Secret (%s) before anything is enqueued",
|
|
476
|
+
async (command: string) => {
|
|
477
|
+
const enqueue: jest.SpyInstance = jest.spyOn(
|
|
478
|
+
RunnerJobService,
|
|
479
|
+
"enqueueAiKubectlCommand",
|
|
480
|
+
);
|
|
481
|
+
const toolkit: KubectlInvestigationToolkit =
|
|
482
|
+
new KubectlInvestigationToolkit({
|
|
483
|
+
projectId: PROJECT_ID,
|
|
484
|
+
aiRunId: RUN_ID,
|
|
485
|
+
clusters: [readyCluster()],
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
const outcome: ToolCallOutcome = await getTool(
|
|
489
|
+
toolkit,
|
|
490
|
+
RUN_KUBECTL_TOOL_NAME,
|
|
491
|
+
).execute({
|
|
492
|
+
clusterId: CLUSTER_ID.toString(),
|
|
493
|
+
command,
|
|
494
|
+
rationale: "read the db password",
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
expect(outcome.success).toBe(false);
|
|
498
|
+
expect(outcome.textForLlm).toContain("Refused by the kubectl policy");
|
|
499
|
+
expect(outcome.textForLlm).toContain("Nothing was run");
|
|
500
|
+
expect(enqueue).not.toHaveBeenCalled();
|
|
501
|
+
expect(toolkit.getCommandsRun()).toBe(0);
|
|
502
|
+
},
|
|
503
|
+
);
|
|
504
|
+
|
|
505
|
+
it("tells the model Secrets are refused and output is redacted", () => {
|
|
506
|
+
const tool: ObservabilityAssistantExtraTool = getTool(
|
|
507
|
+
new KubectlInvestigationToolkit({
|
|
508
|
+
projectId: PROJECT_ID,
|
|
509
|
+
aiRunId: RUN_ID,
|
|
510
|
+
clusters: [readyCluster()],
|
|
511
|
+
}),
|
|
512
|
+
RUN_KUBECTL_TOOL_NAME,
|
|
513
|
+
);
|
|
514
|
+
|
|
515
|
+
expect(tool.definition.description).toContain("Reading Secrets is refused");
|
|
516
|
+
expect(tool.definition.description).toContain("redacted from every output");
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
/*
|
|
520
|
+
* C5: what a Read command can still return — a pod spec with plaintext
|
|
521
|
+
* env values, a ConfigMap with a connection string, a ServiceAccount
|
|
522
|
+
* token, a Secret the credential's RBAC happened to allow — reaches the
|
|
523
|
+
* model with every credential-looking value masked.
|
|
524
|
+
*/
|
|
525
|
+
it("masks Secret data, credential-like env values and tokens before the model sees the output", async () => {
|
|
526
|
+
const PASSWORD_B64: string = "cGFzc3dvcmQ=";
|
|
527
|
+
const JWT: string = `eyJhbGciOiJSUzI1NiIsImtpZCI6IjEyMyJ9.${"a".repeat(24)}.${"b".repeat(16)}`;
|
|
528
|
+
const rawOutput: string = [
|
|
529
|
+
"apiVersion: v1",
|
|
530
|
+
"items:",
|
|
531
|
+
"- apiVersion: v1",
|
|
532
|
+
" data:",
|
|
533
|
+
` DB_PASSWORD: ${PASSWORD_B64}`,
|
|
534
|
+
" tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQo=",
|
|
535
|
+
" kind: Secret",
|
|
536
|
+
" metadata:",
|
|
537
|
+
" name: app-db",
|
|
538
|
+
" namespace: prod",
|
|
539
|
+
"- apiVersion: v1",
|
|
540
|
+
" kind: Pod",
|
|
541
|
+
" metadata:",
|
|
542
|
+
" name: web-7d9f4c8b5d-x2k9q",
|
|
543
|
+
" spec:",
|
|
544
|
+
" containers:",
|
|
545
|
+
" - name: web",
|
|
546
|
+
" image: registry.example.com/auth:1.2.3",
|
|
547
|
+
" env:",
|
|
548
|
+
" - name: DB_HOST",
|
|
549
|
+
" value: postgres",
|
|
550
|
+
" - name: DB_PASSWORD",
|
|
551
|
+
" value: s3cr3t-value",
|
|
552
|
+
" - name: STRIPE_KEY",
|
|
553
|
+
" value: sk-live-abc",
|
|
554
|
+
"- apiVersion: v1",
|
|
555
|
+
" data:",
|
|
556
|
+
" DATABASE_URL: postgres://app:hunter2@db.prod.svc:5432/app",
|
|
557
|
+
" LOG_LEVEL: debug",
|
|
558
|
+
" kind: ConfigMap",
|
|
559
|
+
" metadata:",
|
|
560
|
+
" name: settings",
|
|
561
|
+
"kind: List",
|
|
562
|
+
`token: ${JWT}`,
|
|
563
|
+
"curl -H 'Authorization: Bearer abcdefghijklmnop1234567890'",
|
|
564
|
+
].join("\n");
|
|
565
|
+
|
|
566
|
+
jest
|
|
567
|
+
.spyOn(RunnerJobService, "enqueueAiKubectlCommand")
|
|
568
|
+
.mockResolvedValue(fakeJob());
|
|
569
|
+
jest
|
|
570
|
+
.spyOn(RunnerJobService, "pollUntilTerminal")
|
|
571
|
+
.mockResolvedValue(fakeJob({ output: rawOutput }));
|
|
572
|
+
|
|
573
|
+
const outcome: ToolCallOutcome = await getTool(
|
|
574
|
+
new KubectlInvestigationToolkit({
|
|
575
|
+
projectId: PROJECT_ID,
|
|
576
|
+
aiRunId: RUN_ID,
|
|
577
|
+
clusters: [readyCluster()],
|
|
578
|
+
}),
|
|
579
|
+
RUN_KUBECTL_TOOL_NAME,
|
|
580
|
+
).execute({
|
|
581
|
+
clusterId: CLUSTER_ID.toString(),
|
|
582
|
+
command: "kubectl get pods -n web -o yaml",
|
|
583
|
+
rationale: "see the pod spec",
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
expect(outcome.success).toBe(true);
|
|
587
|
+
const text: string = outcome.textForLlm;
|
|
588
|
+
|
|
589
|
+
// Nothing credential-looking survives, in the text or the cited data.
|
|
590
|
+
for (const leaked of [
|
|
591
|
+
PASSWORD_B64,
|
|
592
|
+
"LS0tLS1CRUdJTi",
|
|
593
|
+
"s3cr3t-value",
|
|
594
|
+
"sk-live-abc",
|
|
595
|
+
"hunter2",
|
|
596
|
+
JWT,
|
|
597
|
+
"abcdefghijklmnop1234567890",
|
|
598
|
+
]) {
|
|
599
|
+
expect(text).not.toContain(leaked);
|
|
600
|
+
expect(outcome.result?.dataForLlm).not.toContain(leaked);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// The structure the model needs to reason stays readable.
|
|
604
|
+
expect(text).toContain(` DB_PASSWORD: ${KUBECTL_REDACTED_MARKER}`);
|
|
605
|
+
expect(text).toContain(` tls.key: ${KUBECTL_REDACTED_MARKER}`);
|
|
606
|
+
expect(text).toContain(" kind: Secret");
|
|
607
|
+
expect(text).toContain(" name: app-db");
|
|
608
|
+
expect(text).toContain(" value: postgres");
|
|
609
|
+
expect(text).toContain(` value: ${KUBECTL_REDACTED_MARKER}`);
|
|
610
|
+
expect(text).toContain(" image: registry.example.com/auth:1.2.3");
|
|
611
|
+
expect(text).toContain(
|
|
612
|
+
` DATABASE_URL: postgres://app:${KUBECTL_REDACTED_MARKER}@db.prod.svc:5432/app`,
|
|
613
|
+
);
|
|
614
|
+
expect(text).toContain(" LOG_LEVEL: debug");
|
|
615
|
+
expect(text).toContain('<tool_result source="untrusted_cluster_output">');
|
|
616
|
+
|
|
617
|
+
expect(outcome.result?.redactionCount).toBeGreaterThanOrEqual(7);
|
|
618
|
+
expect(outcome.result?.isTruncated).toBe(false);
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
it("reports the shared cap's truncation on the result", async () => {
|
|
622
|
+
jest
|
|
623
|
+
.spyOn(RunnerJobService, "enqueueAiKubectlCommand")
|
|
624
|
+
.mockResolvedValue(fakeJob());
|
|
625
|
+
jest.spyOn(RunnerJobService, "pollUntilTerminal").mockResolvedValue(
|
|
626
|
+
fakeJob({
|
|
627
|
+
output: `NAME READY\n${"web-1 1/1\n".repeat(
|
|
628
|
+
MAX_KUBECTL_OUTPUT_CHARS_FOR_LLM,
|
|
629
|
+
)}`,
|
|
630
|
+
}),
|
|
631
|
+
);
|
|
632
|
+
|
|
633
|
+
const outcome: ToolCallOutcome = await getTool(
|
|
634
|
+
new KubectlInvestigationToolkit({
|
|
635
|
+
projectId: PROJECT_ID,
|
|
636
|
+
aiRunId: RUN_ID,
|
|
637
|
+
clusters: [readyCluster()],
|
|
638
|
+
}),
|
|
639
|
+
RUN_KUBECTL_TOOL_NAME,
|
|
640
|
+
).execute({
|
|
641
|
+
clusterId: CLUSTER_ID.toString(),
|
|
642
|
+
command: "kubectl get pods -A",
|
|
643
|
+
rationale: "everything",
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
expect(outcome.result?.isTruncated).toBe(true);
|
|
647
|
+
expect(outcome.textForLlm).toContain("[output truncated]");
|
|
648
|
+
expect(outcome.result?.redactionCount).toBe(0);
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
it("redacts a Runner error message that echoes credential material", async () => {
|
|
652
|
+
const JWT: string = `eyJhbGciOiJSUzI1NiIsImtpZCI6IjEyMyJ9.${"c".repeat(24)}.${"d".repeat(16)}`;
|
|
653
|
+
jest
|
|
654
|
+
.spyOn(RunnerJobService, "enqueueAiKubectlCommand")
|
|
655
|
+
.mockResolvedValue(fakeJob());
|
|
656
|
+
jest.spyOn(RunnerJobService, "pollUntilTerminal").mockResolvedValue(
|
|
657
|
+
fakeJob({
|
|
658
|
+
status: RunnerJobStatus.Failed,
|
|
659
|
+
exitCode: 1,
|
|
660
|
+
output: "",
|
|
661
|
+
errorMessage: `error: the server rejected token=${JWT} (Unauthorized)`,
|
|
662
|
+
}),
|
|
663
|
+
);
|
|
664
|
+
|
|
665
|
+
const outcome: ToolCallOutcome = await getTool(
|
|
666
|
+
new KubectlInvestigationToolkit({
|
|
667
|
+
projectId: PROJECT_ID,
|
|
668
|
+
aiRunId: RUN_ID,
|
|
669
|
+
clusters: [readyCluster()],
|
|
670
|
+
}),
|
|
671
|
+
RUN_KUBECTL_TOOL_NAME,
|
|
672
|
+
).execute({
|
|
673
|
+
clusterId: CLUSTER_ID.toString(),
|
|
674
|
+
command: "kubectl get pods -n web",
|
|
675
|
+
rationale: "see pod phases",
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
expect(outcome.success).toBe(true);
|
|
679
|
+
expect(outcome.textForLlm).toContain("FAILED");
|
|
680
|
+
expect(outcome.textForLlm).toContain("Unauthorized");
|
|
681
|
+
expect(outcome.textForLlm).not.toContain(JWT);
|
|
682
|
+
expect(recordOutcome).toHaveBeenCalledWith(
|
|
683
|
+
expect.objectContaining({
|
|
684
|
+
succeeded: false,
|
|
685
|
+
errorMessage: expect.not.stringContaining(JWT),
|
|
686
|
+
}),
|
|
687
|
+
);
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
it("spends the per-investigation budget and then refuses", async () => {
|
|
691
|
+
jest
|
|
692
|
+
.spyOn(RunnerJobService, "enqueueAiKubectlCommand")
|
|
693
|
+
.mockResolvedValue(fakeJob());
|
|
694
|
+
jest
|
|
695
|
+
.spyOn(RunnerJobService, "pollUntilTerminal")
|
|
696
|
+
.mockResolvedValue(fakeJob());
|
|
697
|
+
|
|
698
|
+
const toolkit: KubectlInvestigationToolkit =
|
|
699
|
+
new KubectlInvestigationToolkit({
|
|
700
|
+
projectId: PROJECT_ID,
|
|
701
|
+
aiRunId: RUN_ID,
|
|
702
|
+
clusters: [readyCluster()],
|
|
703
|
+
maxCommands: 2,
|
|
704
|
+
});
|
|
705
|
+
const tool: ObservabilityAssistantExtraTool = getTool(
|
|
706
|
+
toolkit,
|
|
707
|
+
RUN_KUBECTL_TOOL_NAME,
|
|
708
|
+
);
|
|
709
|
+
const call: JSONObject = {
|
|
710
|
+
clusterId: CLUSTER_ID.toString(),
|
|
711
|
+
command: "kubectl get pods -n web",
|
|
712
|
+
rationale: "again",
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
expect((await tool.execute(call)).success).toBe(true);
|
|
716
|
+
expect((await tool.execute(call)).success).toBe(true);
|
|
717
|
+
|
|
718
|
+
const third: ToolCallOutcome = await tool.execute(call);
|
|
719
|
+
expect(third.success).toBe(false);
|
|
720
|
+
expect(third.textForLlm).toContain("budget (2 commands) is spent");
|
|
721
|
+
expect(toolkit.getCommandsRun()).toBe(2);
|
|
722
|
+
expect(MAX_KUBECTL_COMMANDS_PER_INVESTIGATION).toBeGreaterThan(0);
|
|
723
|
+
});
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
/*
|
|
727
|
+
* C27: the agent loop checks its wall clock only between tool calls, so
|
|
728
|
+
* the toolkit plans each command's claim window and execution timeout
|
|
729
|
+
* against the run's deadline and refuses what no longer fits.
|
|
730
|
+
*/
|
|
731
|
+
describe("KubectlInvestigationToolkit run_kubectl and the run's wall clock", () => {
|
|
732
|
+
const NOW_MS: number = 1_700_000_000_000;
|
|
733
|
+
|
|
734
|
+
let enqueue: jest.SpyInstance;
|
|
735
|
+
let poll: jest.SpyInstance;
|
|
736
|
+
|
|
737
|
+
beforeEach(() => {
|
|
738
|
+
jest.spyOn(Date, "now").mockReturnValue(NOW_MS);
|
|
739
|
+
jest
|
|
740
|
+
.spyOn(AIRunService, "updateOneBy")
|
|
741
|
+
.mockResolvedValue(undefined as never);
|
|
742
|
+
jest
|
|
743
|
+
.spyOn(KubernetesClusterAiAccessService, "recordCommandOutcome")
|
|
744
|
+
.mockResolvedValue(undefined);
|
|
745
|
+
enqueue = jest
|
|
746
|
+
.spyOn(RunnerJobService, "enqueueAiKubectlCommand")
|
|
747
|
+
.mockResolvedValue(fakeJob());
|
|
748
|
+
poll = jest
|
|
749
|
+
.spyOn(RunnerJobService, "pollUntilTerminal")
|
|
750
|
+
.mockResolvedValue(fakeJob());
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
afterEach(() => {
|
|
754
|
+
jest.restoreAllMocks();
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
function toolkitWithDeadline(
|
|
758
|
+
runDeadlineAtMs: number | undefined,
|
|
759
|
+
): KubectlInvestigationToolkit {
|
|
760
|
+
return new KubectlInvestigationToolkit({
|
|
761
|
+
projectId: PROJECT_ID,
|
|
762
|
+
aiRunId: RUN_ID,
|
|
763
|
+
clusters: [readyCluster()],
|
|
764
|
+
runDeadlineAtMs,
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
async function run(
|
|
769
|
+
toolkit: KubectlInvestigationToolkit,
|
|
770
|
+
timeoutInMs?: number,
|
|
771
|
+
): Promise<ToolCallOutcome> {
|
|
772
|
+
return getTool(toolkit, RUN_KUBECTL_TOOL_NAME).execute({
|
|
773
|
+
clusterId: CLUSTER_ID.toString(),
|
|
774
|
+
command: "kubectl get pods -n web",
|
|
775
|
+
rationale: "see pod phases",
|
|
776
|
+
...(timeoutInMs !== undefined ? { timeoutInMs } : {}),
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
function enqueuedWindows(): {
|
|
781
|
+
timeoutInMs: number;
|
|
782
|
+
claimTimeoutInMs: number;
|
|
783
|
+
} {
|
|
784
|
+
const args: Record<string, unknown> = enqueue.mock.calls[0]![0] as Record<
|
|
785
|
+
string,
|
|
786
|
+
unknown
|
|
787
|
+
>;
|
|
788
|
+
return {
|
|
789
|
+
timeoutInMs: args["timeoutInMs"] as number,
|
|
790
|
+
claimTimeoutInMs: args["claimTimeoutInMs"] as number,
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
function polledWindows(): { timeoutInMs: number; claimTimeoutInMs: number } {
|
|
795
|
+
const args: Record<string, unknown> = poll.mock.calls[0]![0] as Record<
|
|
796
|
+
string,
|
|
797
|
+
unknown
|
|
798
|
+
>;
|
|
799
|
+
return {
|
|
800
|
+
timeoutInMs: args["executionTimeoutInMs"] as number,
|
|
801
|
+
claimTimeoutInMs: args["claimTimeoutInMs"] as number,
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
it("passes the requested windows through while the deadline is far away", async () => {
|
|
806
|
+
const outcome: ToolCallOutcome = await run(
|
|
807
|
+
toolkitWithDeadline(NOW_MS + 200_000),
|
|
808
|
+
MAX_KUBECTL_TIMEOUT_MS,
|
|
809
|
+
);
|
|
810
|
+
|
|
811
|
+
expect(outcome.success).toBe(true);
|
|
812
|
+
expect(enqueuedWindows()).toEqual({
|
|
813
|
+
timeoutInMs: MAX_KUBECTL_TIMEOUT_MS,
|
|
814
|
+
claimTimeoutInMs: KUBECTL_CLAIM_TIMEOUT_MS,
|
|
815
|
+
});
|
|
816
|
+
expect(polledWindows()).toEqual(enqueuedWindows());
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
it("shortens the claim window first and keeps the requested execution time", async () => {
|
|
820
|
+
await run(toolkitWithDeadline(NOW_MS + 80_000));
|
|
821
|
+
|
|
822
|
+
expect(enqueuedWindows()).toEqual({
|
|
823
|
+
timeoutInMs: DEFAULT_KUBECTL_TIMEOUT_MS,
|
|
824
|
+
claimTimeoutInMs:
|
|
825
|
+
80_000 - KUBECTL_WAIT_OVERHEAD_MS - DEFAULT_KUBECTL_TIMEOUT_MS,
|
|
826
|
+
});
|
|
827
|
+
expect(polledWindows()).toEqual(enqueuedWindows());
|
|
828
|
+
});
|
|
829
|
+
|
|
830
|
+
it("clamps a maximal timeout to what the remaining budget can hold", async () => {
|
|
831
|
+
// The finding's scenario: 120 s asked for with 40 s of the run left.
|
|
832
|
+
await run(toolkitWithDeadline(NOW_MS + 40_000), MAX_KUBECTL_TIMEOUT_MS);
|
|
833
|
+
|
|
834
|
+
const windows: { timeoutInMs: number; claimTimeoutInMs: number } =
|
|
835
|
+
enqueuedWindows();
|
|
836
|
+
expect(windows).toEqual({
|
|
837
|
+
timeoutInMs:
|
|
838
|
+
40_000 - KUBECTL_WAIT_OVERHEAD_MS - MIN_KUBECTL_CLAIM_TIMEOUT_MS,
|
|
839
|
+
claimTimeoutInMs: MIN_KUBECTL_CLAIM_TIMEOUT_MS,
|
|
840
|
+
});
|
|
841
|
+
expect(KubectlWaitBudget.getWorstCaseWaitMs(windows)).toBeLessThanOrEqual(
|
|
842
|
+
40_000,
|
|
843
|
+
);
|
|
844
|
+
expect(polledWindows()).toEqual(windows);
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
it("refuses, enqueuing nothing and spending no command, when the budget cannot hold another command", async () => {
|
|
848
|
+
const toolkit: KubectlInvestigationToolkit = toolkitWithDeadline(
|
|
849
|
+
NOW_MS + 10_000,
|
|
850
|
+
);
|
|
851
|
+
|
|
852
|
+
const outcome: ToolCallOutcome = await run(toolkit, MAX_KUBECTL_TIMEOUT_MS);
|
|
853
|
+
|
|
854
|
+
expect(outcome.success).toBe(false);
|
|
855
|
+
expect(outcome.textForLlm).toContain("Not enough time is left");
|
|
856
|
+
expect(outcome.textForLlm).toContain("about 10s remain");
|
|
857
|
+
expect(outcome.textForLlm).toContain(
|
|
858
|
+
`at least ${Math.round(KubectlWaitBudget.getMinimumBudgetMs() / 1000)}s`,
|
|
859
|
+
);
|
|
860
|
+
expect(outcome.textForLlm).toContain("Nothing was run");
|
|
861
|
+
expect(enqueue).not.toHaveBeenCalled();
|
|
862
|
+
expect(poll).not.toHaveBeenCalled();
|
|
863
|
+
expect(toolkit.getCommandsRun()).toBe(0);
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
it("refuses once the deadline has passed", async () => {
|
|
867
|
+
const outcome: ToolCallOutcome = await run(toolkitWithDeadline(NOW_MS - 1));
|
|
868
|
+
|
|
869
|
+
expect(outcome.success).toBe(false);
|
|
870
|
+
expect(outcome.textForLlm).toContain("about 0s remain");
|
|
871
|
+
expect(enqueue).not.toHaveBeenCalled();
|
|
872
|
+
});
|
|
873
|
+
|
|
874
|
+
it("still refuses a Secret read and a write before it looks at the budget", async () => {
|
|
875
|
+
const toolkit: KubectlInvestigationToolkit = toolkitWithDeadline(
|
|
876
|
+
NOW_MS + 200_000,
|
|
877
|
+
);
|
|
878
|
+
const tool: ObservabilityAssistantExtraTool = getTool(
|
|
879
|
+
toolkit,
|
|
880
|
+
RUN_KUBECTL_TOOL_NAME,
|
|
881
|
+
);
|
|
882
|
+
|
|
883
|
+
const secret: ToolCallOutcome = await tool.execute({
|
|
884
|
+
clusterId: CLUSTER_ID.toString(),
|
|
885
|
+
command: "kubectl get secret app-db -n prod -o yaml",
|
|
886
|
+
rationale: "read it",
|
|
887
|
+
});
|
|
888
|
+
expect(secret.textForLlm).toContain("Refused by the kubectl policy");
|
|
889
|
+
|
|
890
|
+
const write: ToolCallOutcome = await tool.execute({
|
|
891
|
+
clusterId: CLUSTER_ID.toString(),
|
|
892
|
+
command: "kubectl delete pod web-1 -n web",
|
|
893
|
+
rationale: "bounce it",
|
|
894
|
+
});
|
|
895
|
+
expect(write.textForLlm).toContain("would change the cluster");
|
|
896
|
+
|
|
897
|
+
expect(enqueue).not.toHaveBeenCalled();
|
|
898
|
+
expect(toolkit.getCommandsRun()).toBe(0);
|
|
899
|
+
});
|
|
900
|
+
|
|
901
|
+
it("never lets a planned wait exceed the remaining budget, whatever is asked for", async () => {
|
|
902
|
+
const remainings: Array<number> = [
|
|
903
|
+
KubectlWaitBudget.getMinimumBudgetMs(),
|
|
904
|
+
30_000,
|
|
905
|
+
60_000,
|
|
906
|
+
96_000,
|
|
907
|
+
INVESTIGATION_MAX_WALL_CLOCK_MS,
|
|
908
|
+
];
|
|
909
|
+
const requests: Array<number> = [
|
|
910
|
+
1_000,
|
|
911
|
+
DEFAULT_KUBECTL_TIMEOUT_MS,
|
|
912
|
+
MAX_KUBECTL_TIMEOUT_MS,
|
|
913
|
+
];
|
|
914
|
+
|
|
915
|
+
for (const remaining of remainings) {
|
|
916
|
+
for (const requested of requests) {
|
|
917
|
+
enqueue.mockClear();
|
|
918
|
+
poll.mockClear();
|
|
919
|
+
|
|
920
|
+
const outcome: ToolCallOutcome = await run(
|
|
921
|
+
toolkitWithDeadline(NOW_MS + remaining),
|
|
922
|
+
requested,
|
|
923
|
+
);
|
|
924
|
+
|
|
925
|
+
expect(outcome.success).toBe(true);
|
|
926
|
+
const windows: { timeoutInMs: number; claimTimeoutInMs: number } =
|
|
927
|
+
enqueuedWindows();
|
|
928
|
+
expect(
|
|
929
|
+
KubectlWaitBudget.getWorstCaseWaitMs(windows),
|
|
930
|
+
).toBeLessThanOrEqual(remaining);
|
|
931
|
+
expect(windows.timeoutInMs).toBeLessThanOrEqual(requested);
|
|
932
|
+
expect(windows.claimTimeoutInMs).toBeLessThanOrEqual(
|
|
933
|
+
KUBECTL_CLAIM_TIMEOUT_MS,
|
|
934
|
+
);
|
|
935
|
+
expect(polledWindows()).toEqual(windows);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
it("uses the normal windows without a deadline", async () => {
|
|
941
|
+
await run(toolkitWithDeadline(undefined), MAX_KUBECTL_TIMEOUT_MS);
|
|
942
|
+
|
|
943
|
+
expect(enqueuedWindows()).toEqual({
|
|
944
|
+
timeoutInMs: MAX_KUBECTL_TIMEOUT_MS,
|
|
945
|
+
claimTimeoutInMs: KUBECTL_CLAIM_TIMEOUT_MS,
|
|
946
|
+
});
|
|
947
|
+
});
|
|
948
|
+
|
|
949
|
+
it("states the engine's own default wall clock, not a copy of it", () => {
|
|
950
|
+
/*
|
|
951
|
+
* The runners hand this value to the engine (maxWallClockMs) and to the
|
|
952
|
+
* toolkit (as a deadline); a second definition is how the two drift.
|
|
953
|
+
*/
|
|
954
|
+
expect(INVESTIGATION_MAX_WALL_CLOCK_MS).toBe(ENGINE_MAX_WALL_CLOCK_MS);
|
|
955
|
+
expect(typeof INVESTIGATION_MAX_WALL_CLOCK_MS).toBe("number");
|
|
956
|
+
expect(INVESTIGATION_MAX_WALL_CLOCK_MS).toBeGreaterThan(0);
|
|
957
|
+
});
|
|
958
|
+
|
|
959
|
+
it("states one wall clock that a run_kubectl wait can always fit inside", () => {
|
|
960
|
+
// Full windows must fit a fresh budget, or the tool would never run.
|
|
961
|
+
expect(
|
|
962
|
+
KubectlWaitBudget.getWorstCaseWaitMs({
|
|
963
|
+
timeoutInMs: DEFAULT_KUBECTL_TIMEOUT_MS,
|
|
964
|
+
claimTimeoutInMs: KUBECTL_CLAIM_TIMEOUT_MS,
|
|
965
|
+
}),
|
|
966
|
+
).toBeLessThan(INVESTIGATION_MAX_WALL_CLOCK_MS);
|
|
967
|
+
});
|
|
968
|
+
});
|
|
969
|
+
|
|
970
|
+
describe("KubectlJobRunner.redactAndCap", () => {
|
|
971
|
+
const PASSWORD_B64: string = "cGFzc3dvcmQ=";
|
|
972
|
+
|
|
973
|
+
it("masks Secret data structurally and then applies the generic tool-result rules", () => {
|
|
974
|
+
const result: RedactedKubectlOutput = KubectlJobRunner.redactAndCap(
|
|
975
|
+
[
|
|
976
|
+
"apiVersion: v1",
|
|
977
|
+
"data:",
|
|
978
|
+
` DB_PASSWORD: ${PASSWORD_B64}`,
|
|
979
|
+
"kind: Secret",
|
|
980
|
+
"metadata:",
|
|
981
|
+
" annotations:",
|
|
982
|
+
" owner: oncall@example.com",
|
|
983
|
+
" name: app-db",
|
|
984
|
+
].join("\n"),
|
|
985
|
+
);
|
|
986
|
+
|
|
987
|
+
expect(result.text).not.toContain(PASSWORD_B64);
|
|
988
|
+
expect(result.text).toContain(` DB_PASSWORD: ${KUBECTL_REDACTED_MARKER}`);
|
|
989
|
+
expect(result.text).toContain("kind: Secret");
|
|
990
|
+
expect(result.text).toContain(" name: app-db");
|
|
991
|
+
// The generic rules still run (this one is the serializer's).
|
|
992
|
+
expect(result.text).not.toContain("oncall@example.com");
|
|
993
|
+
expect(result.text).toContain("[redacted-email]");
|
|
994
|
+
expect(result.redactionCount).toBeGreaterThanOrEqual(2);
|
|
995
|
+
expect(result.isTruncated).toBe(false);
|
|
996
|
+
});
|
|
997
|
+
|
|
998
|
+
it("redacts before it caps, so a cut never exposes what the cap removed a mask from", () => {
|
|
999
|
+
const filler: string = `${"pod-line 1/1 Running\n".repeat(
|
|
1000
|
+
Math.ceil(MAX_KUBECTL_OUTPUT_CHARS_FOR_LLM / 21),
|
|
1001
|
+
)}`;
|
|
1002
|
+
const result: RedactedKubectlOutput = KubectlJobRunner.redactAndCap(
|
|
1003
|
+
`password: hunter2\n${filler}password: hunter2-after-the-cap\n`,
|
|
1004
|
+
);
|
|
1005
|
+
|
|
1006
|
+
expect(result.isTruncated).toBe(true);
|
|
1007
|
+
expect(result.text.endsWith(KUBECTL_OUTPUT_TRUNCATED_SUFFIX)).toBe(true);
|
|
1008
|
+
expect(result.text.length).toBeLessThanOrEqual(
|
|
1009
|
+
MAX_KUBECTL_OUTPUT_CHARS_FOR_LLM + KUBECTL_OUTPUT_TRUNCATED_SUFFIX.length,
|
|
1010
|
+
);
|
|
1011
|
+
expect(result.text).not.toContain("hunter2");
|
|
1012
|
+
expect(result.text).toContain(`password: ${KUBECTL_REDACTED_MARKER}`);
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
it("honours a caller's smaller cap and leaves short clean output untouched", () => {
|
|
1016
|
+
const small: RedactedKubectlOutput = KubectlJobRunner.redactAndCap(
|
|
1017
|
+
"NAME READY STATUS\nweb-1 1/1 Running",
|
|
1018
|
+
10,
|
|
1019
|
+
);
|
|
1020
|
+
expect(small.isTruncated).toBe(true);
|
|
1021
|
+
expect(small.text).toBe(`NAME REA${KUBECTL_OUTPUT_TRUNCATED_SUFFIX}`);
|
|
1022
|
+
|
|
1023
|
+
const clean: RedactedKubectlOutput = KubectlJobRunner.redactAndCap(
|
|
1024
|
+
"NAME READY STATUS\nweb-1 1/1 Running",
|
|
1025
|
+
);
|
|
1026
|
+
expect(clean).toEqual({
|
|
1027
|
+
text: "NAME READY STATUS\nweb-1 1/1 Running",
|
|
1028
|
+
redactionCount: 0,
|
|
1029
|
+
isTruncated: false,
|
|
1030
|
+
});
|
|
1031
|
+
});
|
|
1032
|
+
|
|
1033
|
+
it("treats missing output as empty", () => {
|
|
1034
|
+
expect(KubectlJobRunner.redactAndCap("")).toEqual({
|
|
1035
|
+
text: "",
|
|
1036
|
+
redactionCount: 0,
|
|
1037
|
+
isTruncated: false,
|
|
1038
|
+
});
|
|
1039
|
+
});
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
describe("KubectlJobRunner.describeForLlm", () => {
|
|
1043
|
+
it("frames output as untrusted cluster data with the outcome up front", () => {
|
|
1044
|
+
const text: string = KubectlJobRunner.describeForLlm({
|
|
1045
|
+
jobId: JOB_ID.toString(),
|
|
1046
|
+
succeeded: false,
|
|
1047
|
+
exitCode: 1,
|
|
1048
|
+
output: "Error from server (Forbidden): pods is forbidden",
|
|
1049
|
+
errorMessage: "Exit code 1",
|
|
1050
|
+
displayCommand: "kubectl get pods -n web",
|
|
1051
|
+
});
|
|
1052
|
+
|
|
1053
|
+
expect(text.split("\n")[0]).toBe("kubectl get pods -n web");
|
|
1054
|
+
expect(text).toContain("FAILED (exit code: 1, error: Exit code 1)");
|
|
1055
|
+
expect(text).toContain("Forbidden");
|
|
1056
|
+
expect(text).toContain("never instructions");
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1059
|
+
/*
|
|
1060
|
+
* The remediation toolkit assembles its own outcome from its own wait;
|
|
1061
|
+
* whatever it hands over, the framing is the last gate before the model.
|
|
1062
|
+
*/
|
|
1063
|
+
it("masks an outcome assembled outside run() before framing it", () => {
|
|
1064
|
+
const PASSWORD_B64: string = "cGFzc3dvcmQ=";
|
|
1065
|
+
const text: string = KubectlJobRunner.describeForLlm({
|
|
1066
|
+
jobId: JOB_ID.toString(),
|
|
1067
|
+
succeeded: false,
|
|
1068
|
+
exitCode: 1,
|
|
1069
|
+
output: [
|
|
1070
|
+
"apiVersion: v1",
|
|
1071
|
+
"data:",
|
|
1072
|
+
` password: ${PASSWORD_B64}`,
|
|
1073
|
+
"kind: Secret",
|
|
1074
|
+
"spec:",
|
|
1075
|
+
" containers:",
|
|
1076
|
+
" - env:",
|
|
1077
|
+
" - name: API_TOKEN",
|
|
1078
|
+
" value: abc-123-def",
|
|
1079
|
+
].join("\n"),
|
|
1080
|
+
errorMessage:
|
|
1081
|
+
"kubectl failed with --token=bootstrap-abcdef.0123456789abcdef",
|
|
1082
|
+
displayCommand: "kubectl get secret app-db -n prod -o yaml",
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1085
|
+
expect(text).not.toContain(PASSWORD_B64);
|
|
1086
|
+
expect(text).not.toContain("abc-123-def");
|
|
1087
|
+
expect(text).not.toContain("bootstrap-abcdef");
|
|
1088
|
+
expect(text).toContain(` password: ${KUBECTL_REDACTED_MARKER}`);
|
|
1089
|
+
expect(text).toContain(` value: ${KUBECTL_REDACTED_MARKER}`);
|
|
1090
|
+
expect(text).toContain(`--token=${KUBECTL_REDACTED_MARKER}`);
|
|
1091
|
+
expect(text).toContain("kind: Secret");
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
it("leaves an outcome that run() already redacted unchanged", () => {
|
|
1095
|
+
const redacted: RedactedKubectlOutput = KubectlJobRunner.redactAndCap(
|
|
1096
|
+
["data:", " password: cGFzc3dvcmQ=", "kind: Secret"].join("\n"),
|
|
1097
|
+
);
|
|
1098
|
+
const text: string = KubectlJobRunner.describeForLlm({
|
|
1099
|
+
jobId: JOB_ID.toString(),
|
|
1100
|
+
succeeded: true,
|
|
1101
|
+
exitCode: 0,
|
|
1102
|
+
output: redacted.text,
|
|
1103
|
+
redactionCount: redacted.redactionCount,
|
|
1104
|
+
isTruncated: redacted.isTruncated,
|
|
1105
|
+
displayCommand: "kubectl get secret app-db -n prod -o yaml",
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1108
|
+
expect(text).toContain(redacted.text);
|
|
1109
|
+
});
|
|
1110
|
+
});
|
|
1111
|
+
|
|
1112
|
+
/*
|
|
1113
|
+
* A command that never reached kubectl — no Runner claimed it, or the
|
|
1114
|
+
* server or the Runner refused it before spawning kubectl — produced no
|
|
1115
|
+
* evidence. It is a FAILED tool call (no citation, so the agent loop emits
|
|
1116
|
+
* tool_failed and the panel never counts it as run), and an unclaimed one
|
|
1117
|
+
* trips a per-cluster breaker so later commands for that cluster fail at
|
|
1118
|
+
* once instead of each waiting out another claim window.
|
|
1119
|
+
*/
|
|
1120
|
+
describe("KubectlInvestigationToolkit run_kubectl when kubectl never ran", () => {
|
|
1121
|
+
const OTHER_CLUSTER_ID: ObjectID = new ObjectID(
|
|
1122
|
+
"77777777-7777-4777-8777-777777777777",
|
|
1123
|
+
);
|
|
1124
|
+
const OTHER_RUNNER_ID: ObjectID = new ObjectID(
|
|
1125
|
+
"99999999-9999-4999-8999-999999999999",
|
|
1126
|
+
);
|
|
1127
|
+
const UNCLAIMED_REASON: string =
|
|
1128
|
+
"No runbook agent picked up this step before the wait window expired. The agent may be offline — check that it is running and reachable, then try again.";
|
|
1129
|
+
|
|
1130
|
+
let enqueue: jest.SpyInstance;
|
|
1131
|
+
let poll: jest.SpyInstance;
|
|
1132
|
+
let claimRead: jest.SpyInstance;
|
|
1133
|
+
let recordOutcome: jest.SpyInstance;
|
|
1134
|
+
|
|
1135
|
+
// What RunnerJobService.timeoutJob hands back: no claimedAt selected.
|
|
1136
|
+
function timedOutJob(): RunnerJob {
|
|
1137
|
+
return fakeJob({
|
|
1138
|
+
status: RunnerJobStatus.TimedOut,
|
|
1139
|
+
exitCode: undefined,
|
|
1140
|
+
output: "",
|
|
1141
|
+
errorMessage: UNCLAIMED_REASON,
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
// The claim columns of the row, as KubectlJobRunner re-reads them.
|
|
1146
|
+
function claimRow(overrides: Partial<Record<string, unknown>>): RunnerJob {
|
|
1147
|
+
return { _id: JOB_ID.toString(), ...overrides } as unknown as RunnerJob;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
function singleClusterToolkit(): KubectlInvestigationToolkit {
|
|
1151
|
+
return new KubectlInvestigationToolkit({
|
|
1152
|
+
projectId: PROJECT_ID,
|
|
1153
|
+
aiRunId: RUN_ID,
|
|
1154
|
+
clusters: [readyCluster()],
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
function twoClusterToolkit(): KubectlInvestigationToolkit {
|
|
1159
|
+
return new KubectlInvestigationToolkit({
|
|
1160
|
+
projectId: PROJECT_ID,
|
|
1161
|
+
aiRunId: RUN_ID,
|
|
1162
|
+
clusters: [
|
|
1163
|
+
readyCluster(),
|
|
1164
|
+
readyCluster({
|
|
1165
|
+
clusterId: OTHER_CLUSTER_ID.toString(),
|
|
1166
|
+
clusterName: "prod-eu",
|
|
1167
|
+
clusterIdentifier: "prod-eu",
|
|
1168
|
+
runner: {
|
|
1169
|
+
id: OTHER_RUNNER_ID.toString(),
|
|
1170
|
+
name: "kubernetes-agent/prod-eu",
|
|
1171
|
+
isOnline: true,
|
|
1172
|
+
canRunAiCommands: true,
|
|
1173
|
+
posture: { inCluster: true, allowWrites: false },
|
|
1174
|
+
},
|
|
1175
|
+
}),
|
|
1176
|
+
],
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
async function runOn(
|
|
1181
|
+
toolkit: KubectlInvestigationToolkit,
|
|
1182
|
+
clusterId: ObjectID = CLUSTER_ID,
|
|
1183
|
+
command: string = "kubectl get pods -n web",
|
|
1184
|
+
): Promise<ToolCallOutcome> {
|
|
1185
|
+
return getTool(toolkit, RUN_KUBECTL_TOOL_NAME).execute({
|
|
1186
|
+
clusterId: clusterId.toString(),
|
|
1187
|
+
command,
|
|
1188
|
+
rationale: "see pod phases",
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
beforeEach(() => {
|
|
1193
|
+
jest
|
|
1194
|
+
.spyOn(AIRunService, "updateOneBy")
|
|
1195
|
+
.mockResolvedValue(undefined as never);
|
|
1196
|
+
recordOutcome = jest
|
|
1197
|
+
.spyOn(KubernetesClusterAiAccessService, "recordCommandOutcome")
|
|
1198
|
+
.mockResolvedValue(undefined);
|
|
1199
|
+
enqueue = jest
|
|
1200
|
+
.spyOn(RunnerJobService, "enqueueAiKubectlCommand")
|
|
1201
|
+
.mockResolvedValue(fakeJob());
|
|
1202
|
+
poll = jest
|
|
1203
|
+
.spyOn(RunnerJobService, "pollUntilTerminal")
|
|
1204
|
+
.mockResolvedValue(timedOutJob());
|
|
1205
|
+
// Never claimed: no claimedAt, no assignedAgentId, no startedAt.
|
|
1206
|
+
claimRead = jest
|
|
1207
|
+
.spyOn(RunnerJobService, "findOneById")
|
|
1208
|
+
.mockResolvedValue(claimRow({}));
|
|
1209
|
+
});
|
|
1210
|
+
|
|
1211
|
+
afterEach(() => {
|
|
1212
|
+
jest.restoreAllMocks();
|
|
1213
|
+
});
|
|
1214
|
+
|
|
1215
|
+
it("reports an unclaimed job as a failed call with no citation, never as run", async () => {
|
|
1216
|
+
const toolkit: KubectlInvestigationToolkit = singleClusterToolkit();
|
|
1217
|
+
|
|
1218
|
+
const outcome: ToolCallOutcome = await runOn(toolkit);
|
|
1219
|
+
|
|
1220
|
+
expect(outcome.success).toBe(false);
|
|
1221
|
+
expect(outcome.result).toBeUndefined();
|
|
1222
|
+
expect(outcome.textForLlm).toContain(
|
|
1223
|
+
'kubectl was NOT run on cluster "prod-us"',
|
|
1224
|
+
);
|
|
1225
|
+
expect(outcome.textForLlm).toContain("did not pick up");
|
|
1226
|
+
expect(outcome.textForLlm).toContain("Continue with OneUptime telemetry");
|
|
1227
|
+
// The runbook wording invites a retry; the model must be told not to.
|
|
1228
|
+
expect(outcome.textForLlm).not.toContain("try again");
|
|
1229
|
+
expect(outcome.textForLlm).not.toContain("runbook");
|
|
1230
|
+
expect(outcome.textForLlm).toContain("do not call run_kubectl on it again");
|
|
1231
|
+
// The persisted event names the cluster and nothing about its Runner.
|
|
1232
|
+
expect(outcome.errorMessage).toBe(
|
|
1233
|
+
'kubectl was not run on cluster "prod-us": its Runner did not pick up the command in time.',
|
|
1234
|
+
);
|
|
1235
|
+
// The claim state was read from the row, not guessed from the reason.
|
|
1236
|
+
expect(claimRead).toHaveBeenCalledTimes(1);
|
|
1237
|
+
expect(
|
|
1238
|
+
(claimRead.mock.calls[0]![0] as { select: Record<string, boolean> })
|
|
1239
|
+
.select,
|
|
1240
|
+
).toEqual(
|
|
1241
|
+
expect.objectContaining({
|
|
1242
|
+
claimedAt: true,
|
|
1243
|
+
startedAt: true,
|
|
1244
|
+
assignedAgentId: true,
|
|
1245
|
+
}),
|
|
1246
|
+
);
|
|
1247
|
+
// A job was enqueued, so it counts against the per-run cap.
|
|
1248
|
+
expect(toolkit.getCommandsRun()).toBe(1);
|
|
1249
|
+
});
|
|
1250
|
+
|
|
1251
|
+
it("trips a per-cluster breaker: the next command fails at once, enqueues nothing and spends nothing", async () => {
|
|
1252
|
+
const toolkit: KubectlInvestigationToolkit = singleClusterToolkit();
|
|
1253
|
+
|
|
1254
|
+
await runOn(toolkit);
|
|
1255
|
+
expect(toolkit.isClusterUnreachable(CLUSTER_ID.toString())).toBe(true);
|
|
1256
|
+
|
|
1257
|
+
const plan: jest.SpyInstance = jest.spyOn(KubectlWaitBudget, "plan");
|
|
1258
|
+
const second: ToolCallOutcome = await runOn(
|
|
1259
|
+
toolkit,
|
|
1260
|
+
CLUSTER_ID,
|
|
1261
|
+
"kubectl get events -n web",
|
|
1262
|
+
);
|
|
1263
|
+
|
|
1264
|
+
expect(second.success).toBe(false);
|
|
1265
|
+
expect(second.result).toBeUndefined();
|
|
1266
|
+
expect(second.textForLlm).toContain(
|
|
1267
|
+
"treated as unreachable for the rest of this investigation",
|
|
1268
|
+
);
|
|
1269
|
+
expect(second.textForLlm).toContain(
|
|
1270
|
+
`within ${Math.round(KUBECTL_CLAIM_TIMEOUT_MS / 1000)}s`,
|
|
1271
|
+
);
|
|
1272
|
+
expect(second.textForLlm).toContain("Nothing was run");
|
|
1273
|
+
expect(second.textForLlm).toContain("continue with OneUptime telemetry");
|
|
1274
|
+
expect(second.errorMessage).toContain(
|
|
1275
|
+
'kubectl was not run on cluster "prod-us"',
|
|
1276
|
+
);
|
|
1277
|
+
expect(enqueue).toHaveBeenCalledTimes(1);
|
|
1278
|
+
expect(poll).toHaveBeenCalledTimes(1);
|
|
1279
|
+
expect(plan).not.toHaveBeenCalled();
|
|
1280
|
+
expect(toolkit.getCommandsRun()).toBe(1);
|
|
1281
|
+
});
|
|
1282
|
+
|
|
1283
|
+
it("marks the unreachable cluster in list_cluster_access", async () => {
|
|
1284
|
+
const toolkit: KubectlInvestigationToolkit = twoClusterToolkit();
|
|
1285
|
+
|
|
1286
|
+
await runOn(toolkit);
|
|
1287
|
+
|
|
1288
|
+
const listing: ToolCallOutcome = await getTool(
|
|
1289
|
+
toolkit,
|
|
1290
|
+
LIST_CLUSTER_ACCESS_TOOL_NAME,
|
|
1291
|
+
).execute({});
|
|
1292
|
+
|
|
1293
|
+
const lines: Array<string> = listing.textForLlm.split("\n");
|
|
1294
|
+
expect(lines[0]).toContain('"prod-us"');
|
|
1295
|
+
expect(lines[0]).toContain(
|
|
1296
|
+
"UNREACHABLE for the rest of this investigation",
|
|
1297
|
+
);
|
|
1298
|
+
expect(lines[1]).toContain('"prod-eu"');
|
|
1299
|
+
expect(lines[1]).toContain(
|
|
1300
|
+
'read-only kubectl via Runner "kubernetes-agent/prod-eu"',
|
|
1301
|
+
);
|
|
1302
|
+
});
|
|
1303
|
+
|
|
1304
|
+
// Negative control for the per-Runner breaker: another Runner is not tripped.
|
|
1305
|
+
it("keeps the breaker per Runner: a cluster on another Runner still runs", async () => {
|
|
1306
|
+
const toolkit: KubectlInvestigationToolkit = twoClusterToolkit();
|
|
1307
|
+
|
|
1308
|
+
await runOn(toolkit, CLUSTER_ID);
|
|
1309
|
+
poll.mockResolvedValue(fakeJob());
|
|
1310
|
+
|
|
1311
|
+
const other: ToolCallOutcome = await runOn(toolkit, OTHER_CLUSTER_ID);
|
|
1312
|
+
|
|
1313
|
+
expect(other.success).toBe(true);
|
|
1314
|
+
expect(other.result?.rowCount).toBe(1);
|
|
1315
|
+
expect(enqueue).toHaveBeenCalledTimes(2);
|
|
1316
|
+
expect(
|
|
1317
|
+
(
|
|
1318
|
+
(enqueue.mock.calls[1]![0] as Record<string, unknown>)[
|
|
1319
|
+
"kubernetesClusterId"
|
|
1320
|
+
] as ObjectID
|
|
1321
|
+
).toString(),
|
|
1322
|
+
).toBe(OTHER_CLUSTER_ID.toString());
|
|
1323
|
+
expect(toolkit.isClusterUnreachable(OTHER_CLUSTER_ID.toString())).toBe(
|
|
1324
|
+
false,
|
|
1325
|
+
);
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1328
|
+
/*
|
|
1329
|
+
* IP-4: two clusters bound to the same external Runner, each with its
|
|
1330
|
+
* own credential. Jobs are claimed by Runner, never by cluster, so an
|
|
1331
|
+
* unclaimed command on one says the other's commands will not be
|
|
1332
|
+
* claimed either.
|
|
1333
|
+
*/
|
|
1334
|
+
function sharedRunnerToolkit(): KubectlInvestigationToolkit {
|
|
1335
|
+
const sharedRunner: KubernetesClusterAiAccessStatus["runner"] = {
|
|
1336
|
+
id: RUNNER_ID.toString(),
|
|
1337
|
+
name: "ops-runner",
|
|
1338
|
+
isOnline: true,
|
|
1339
|
+
canRunAiCommands: true,
|
|
1340
|
+
};
|
|
1341
|
+
|
|
1342
|
+
return new KubectlInvestigationToolkit({
|
|
1343
|
+
projectId: PROJECT_ID,
|
|
1344
|
+
aiRunId: RUN_ID,
|
|
1345
|
+
clusters: [
|
|
1346
|
+
readyCluster({
|
|
1347
|
+
runner: sharedRunner,
|
|
1348
|
+
accessMethod: "credential",
|
|
1349
|
+
credentialId: "11111111-1111-4111-8111-111111111111",
|
|
1350
|
+
}),
|
|
1351
|
+
readyCluster({
|
|
1352
|
+
clusterId: OTHER_CLUSTER_ID.toString(),
|
|
1353
|
+
clusterName: "prod-eu",
|
|
1354
|
+
clusterIdentifier: "prod-eu",
|
|
1355
|
+
runner: sharedRunner,
|
|
1356
|
+
accessMethod: "credential",
|
|
1357
|
+
credentialId: "55555555-5555-4555-8555-555555555555",
|
|
1358
|
+
}),
|
|
1359
|
+
],
|
|
1360
|
+
});
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
it("trips the breaker for every cluster on the Runner that left a command unclaimed", async () => {
|
|
1364
|
+
const toolkit: KubectlInvestigationToolkit = sharedRunnerToolkit();
|
|
1365
|
+
|
|
1366
|
+
const first: ToolCallOutcome = await runOn(toolkit, CLUSTER_ID);
|
|
1367
|
+
|
|
1368
|
+
// The trip message tells the model the other cluster is gone too.
|
|
1369
|
+
expect(first.textForLlm).toContain(
|
|
1370
|
+
'The same Runner "ops-runner" serves "prod-eu", so that cluster is unreachable too.',
|
|
1371
|
+
);
|
|
1372
|
+
|
|
1373
|
+
const plan: jest.SpyInstance = jest.spyOn(KubectlWaitBudget, "plan");
|
|
1374
|
+
const second: ToolCallOutcome = await runOn(toolkit, OTHER_CLUSTER_ID);
|
|
1375
|
+
|
|
1376
|
+
expect(second.success).toBe(false);
|
|
1377
|
+
expect(second.result).toBeUndefined();
|
|
1378
|
+
expect(second.textForLlm).toContain(
|
|
1379
|
+
"treated as unreachable for the rest of this investigation",
|
|
1380
|
+
);
|
|
1381
|
+
expect(second.textForLlm).toContain('Runner "ops-runner"');
|
|
1382
|
+
expect(second.textForLlm).toContain('serves cluster "prod-eu"');
|
|
1383
|
+
expect(second.textForLlm).toContain('(for cluster "prod-us")');
|
|
1384
|
+
expect(second.textForLlm).toContain("Nothing was run");
|
|
1385
|
+
expect(second.errorMessage).toBe(
|
|
1386
|
+
'kubectl was not run on cluster "prod-eu": its Runner did not pick up an earlier command, so the cluster was unreachable for the rest of this investigation.',
|
|
1387
|
+
);
|
|
1388
|
+
// No second claim window was waited out.
|
|
1389
|
+
expect(enqueue).toHaveBeenCalledTimes(1);
|
|
1390
|
+
expect(plan).not.toHaveBeenCalled();
|
|
1391
|
+
expect(toolkit.getCommandsRun()).toBe(1);
|
|
1392
|
+
expect(toolkit.isClusterUnreachable(CLUSTER_ID.toString())).toBe(true);
|
|
1393
|
+
expect(toolkit.isClusterUnreachable(OTHER_CLUSTER_ID.toString())).toBe(
|
|
1394
|
+
true,
|
|
1395
|
+
);
|
|
1396
|
+
|
|
1397
|
+
const listing: ToolCallOutcome = await getTool(
|
|
1398
|
+
toolkit,
|
|
1399
|
+
LIST_CLUSTER_ACCESS_TOOL_NAME,
|
|
1400
|
+
).execute({});
|
|
1401
|
+
const lines: Array<string> = listing.textForLlm.split("\n");
|
|
1402
|
+
|
|
1403
|
+
expect(lines).toHaveLength(2);
|
|
1404
|
+
for (const line of lines) {
|
|
1405
|
+
expect(line).toContain("UNREACHABLE for the rest of this investigation");
|
|
1406
|
+
expect(line).toContain('its Runner "ops-runner"');
|
|
1407
|
+
}
|
|
1408
|
+
});
|
|
1409
|
+
|
|
1410
|
+
/*
|
|
1411
|
+
* Negative controls: on a shared Runner, only an UNCLAIMED command trips
|
|
1412
|
+
* the breaker. A command that was claimed and went silent, ran and
|
|
1413
|
+
* exited non-zero, or was refused by the Runner leaves the other
|
|
1414
|
+
* cluster's commands running.
|
|
1415
|
+
*/
|
|
1416
|
+
it.each<[string, () => void]>([
|
|
1417
|
+
[
|
|
1418
|
+
"was claimed and then went silent",
|
|
1419
|
+
(): void => {
|
|
1420
|
+
claimRead.mockResolvedValue(
|
|
1421
|
+
claimRow({
|
|
1422
|
+
claimedAt: new Date("2026-09-22T10:00:00.000Z"),
|
|
1423
|
+
assignedAgentId: RUNNER_ID,
|
|
1424
|
+
}),
|
|
1425
|
+
);
|
|
1426
|
+
},
|
|
1427
|
+
],
|
|
1428
|
+
[
|
|
1429
|
+
"ran and exited non-zero",
|
|
1430
|
+
(): void => {
|
|
1431
|
+
poll.mockResolvedValueOnce(
|
|
1432
|
+
fakeJob({
|
|
1433
|
+
status: RunnerJobStatus.Failed,
|
|
1434
|
+
exitCode: 1,
|
|
1435
|
+
errorMessage: "Exit code 1",
|
|
1436
|
+
output:
|
|
1437
|
+
'[stderr]\nError from server (NotFound): pods "x" not found',
|
|
1438
|
+
}),
|
|
1439
|
+
);
|
|
1440
|
+
},
|
|
1441
|
+
],
|
|
1442
|
+
[
|
|
1443
|
+
"was refused by the Runner",
|
|
1444
|
+
(): void => {
|
|
1445
|
+
poll.mockResolvedValueOnce(
|
|
1446
|
+
fakeJob({
|
|
1447
|
+
status: RunnerJobStatus.Failed,
|
|
1448
|
+
exitCode: undefined,
|
|
1449
|
+
output: "",
|
|
1450
|
+
errorMessage: "Refused by the Runner: --kubeconfig is not allowed.",
|
|
1451
|
+
}),
|
|
1452
|
+
);
|
|
1453
|
+
},
|
|
1454
|
+
],
|
|
1455
|
+
])(
|
|
1456
|
+
"does not trip the shared Runner's breaker for a command that %s",
|
|
1457
|
+
async (_label: string, arrange: () => void) => {
|
|
1458
|
+
arrange();
|
|
1459
|
+
const toolkit: KubectlInvestigationToolkit = sharedRunnerToolkit();
|
|
1460
|
+
|
|
1461
|
+
await runOn(toolkit, CLUSTER_ID);
|
|
1462
|
+
poll.mockResolvedValue(fakeJob());
|
|
1463
|
+
const other: ToolCallOutcome = await runOn(toolkit, OTHER_CLUSTER_ID);
|
|
1464
|
+
|
|
1465
|
+
expect(other.success).toBe(true);
|
|
1466
|
+
expect(enqueue).toHaveBeenCalledTimes(2);
|
|
1467
|
+
expect(toolkit.isClusterUnreachable(OTHER_CLUSTER_ID.toString())).toBe(
|
|
1468
|
+
false,
|
|
1469
|
+
);
|
|
1470
|
+
},
|
|
1471
|
+
);
|
|
1472
|
+
|
|
1473
|
+
it("reports the claim window actually planned for the command", async () => {
|
|
1474
|
+
const NOW_MS: number = 1_700_000_000_000;
|
|
1475
|
+
jest.spyOn(Date, "now").mockReturnValue(NOW_MS);
|
|
1476
|
+
const toolkit: KubectlInvestigationToolkit =
|
|
1477
|
+
new KubectlInvestigationToolkit({
|
|
1478
|
+
projectId: PROJECT_ID,
|
|
1479
|
+
aiRunId: RUN_ID,
|
|
1480
|
+
clusters: [readyCluster()],
|
|
1481
|
+
runDeadlineAtMs: NOW_MS + 40_000,
|
|
1482
|
+
});
|
|
1483
|
+
|
|
1484
|
+
const outcome: ToolCallOutcome = await runOn(toolkit);
|
|
1485
|
+
|
|
1486
|
+
expect(outcome.textForLlm).toContain(
|
|
1487
|
+
`within ${Math.round(MIN_KUBECTL_CLAIM_TIMEOUT_MS / 1000)}s`,
|
|
1488
|
+
);
|
|
1489
|
+
});
|
|
1490
|
+
|
|
1491
|
+
/*
|
|
1492
|
+
* Negative controls: only a job no Runner ever took trips the breaker. A
|
|
1493
|
+
* kubectl that ran and failed, or a Runner that took the job and went
|
|
1494
|
+
* silent, leaves the cluster reachable.
|
|
1495
|
+
*/
|
|
1496
|
+
it("does not trip the breaker for a command that ran and exited non-zero", async () => {
|
|
1497
|
+
poll.mockResolvedValue(
|
|
1498
|
+
fakeJob({
|
|
1499
|
+
status: RunnerJobStatus.Failed,
|
|
1500
|
+
exitCode: 1,
|
|
1501
|
+
errorMessage: "Exit code 1",
|
|
1502
|
+
output: '[stderr]\nError from server (NotFound): pods "x" not found',
|
|
1503
|
+
}),
|
|
1504
|
+
);
|
|
1505
|
+
const toolkit: KubectlInvestigationToolkit = singleClusterToolkit();
|
|
1506
|
+
|
|
1507
|
+
expect((await runOn(toolkit)).success).toBe(true);
|
|
1508
|
+
expect((await runOn(toolkit)).success).toBe(true);
|
|
1509
|
+
|
|
1510
|
+
expect(enqueue).toHaveBeenCalledTimes(2);
|
|
1511
|
+
expect(toolkit.isClusterUnreachable(CLUSTER_ID.toString())).toBe(false);
|
|
1512
|
+
// The Failed row is read as it is; only a TimedOut row is re-read.
|
|
1513
|
+
expect(claimRead).not.toHaveBeenCalled();
|
|
1514
|
+
});
|
|
1515
|
+
|
|
1516
|
+
/*
|
|
1517
|
+
* IP-1: a Runner took the job and no result came back. The row may have
|
|
1518
|
+
* claimedAt and assignedAgentId but no startedAt — the Runner announces a
|
|
1519
|
+
* step with one best-effort heartbeat (capped at 5 s) right before its
|
|
1520
|
+
* executor gets it, and runs it anyway when that gets no answer (an
|
|
1521
|
+
* older Runner's first heartbeat came 10 s in) — or startedAt too, which
|
|
1522
|
+
* does not prove kubectl ran either. It may have run: no citation (there
|
|
1523
|
+
* is no output), no breaker (the Runner is alive enough to claim), and
|
|
1524
|
+
* never "refused" or "did not run".
|
|
1525
|
+
*/
|
|
1526
|
+
it.each<[string, Partial<Record<string, unknown>>]>([
|
|
1527
|
+
[
|
|
1528
|
+
"never heartbeated (its start heartbeat and its result both lost)",
|
|
1529
|
+
{
|
|
1530
|
+
claimedAt: new Date("2026-09-22T10:00:00.000Z"),
|
|
1531
|
+
assignedAgentId: RUNNER_ID,
|
|
1532
|
+
startedAt: null,
|
|
1533
|
+
},
|
|
1534
|
+
],
|
|
1535
|
+
[
|
|
1536
|
+
"heartbeated and then went silent",
|
|
1537
|
+
{
|
|
1538
|
+
claimedAt: new Date("2026-09-22T10:00:00.000Z"),
|
|
1539
|
+
assignedAgentId: RUNNER_ID,
|
|
1540
|
+
startedAt: new Date("2026-09-22T10:00:10.000Z"),
|
|
1541
|
+
},
|
|
1542
|
+
],
|
|
1543
|
+
])(
|
|
1544
|
+
"reports a job a Runner claimed and %s as result unknown, not as run or not run",
|
|
1545
|
+
async (_label: string, row: Partial<Record<string, unknown>>) => {
|
|
1546
|
+
claimRead.mockResolvedValue(claimRow(row));
|
|
1547
|
+
const toolkit: KubectlInvestigationToolkit = singleClusterToolkit();
|
|
1548
|
+
|
|
1549
|
+
const first: ToolCallOutcome = await runOn(toolkit);
|
|
1550
|
+
|
|
1551
|
+
// Nothing came back, so there is nothing to cite...
|
|
1552
|
+
expect(first.success).toBe(false);
|
|
1553
|
+
expect(first.result).toBeUndefined();
|
|
1554
|
+
expect(first.textForLlm).toContain("did not report a result");
|
|
1555
|
+
expect(first.textForLlm).toContain("Whether it ran");
|
|
1556
|
+
expect(first.textForLlm).not.toContain("runbook");
|
|
1557
|
+
expect(first.textForLlm).not.toContain("try again");
|
|
1558
|
+
// ...and the persisted event says so, in a form the panel can tell apart.
|
|
1559
|
+
expect(first.errorMessage).toBe(
|
|
1560
|
+
`${KUBECTL_RESULT_UNKNOWN_EVENT_PREFIX} the Runner of cluster "prod-us" took the command, but no result came back, so whether it ran is unknown.`,
|
|
1561
|
+
);
|
|
1562
|
+
expect(first.errorMessage).not.toContain("refused");
|
|
1563
|
+
expect(first.errorMessage).not.toContain("did not run");
|
|
1564
|
+
expect(first.errorMessage).not.toContain("not run");
|
|
1565
|
+
expect(isKubectlResultUnknownMessage(first.errorMessage)).toBe(true);
|
|
1566
|
+
|
|
1567
|
+
// The Runner claimed it, so the breaker stays open.
|
|
1568
|
+
await runOn(toolkit);
|
|
1569
|
+
expect(enqueue).toHaveBeenCalledTimes(2);
|
|
1570
|
+
expect(toolkit.isClusterUnreachable(CLUSTER_ID.toString())).toBe(false);
|
|
1571
|
+
},
|
|
1572
|
+
);
|
|
1573
|
+
|
|
1574
|
+
it("does not trip the breaker when the claim state cannot be read", async () => {
|
|
1575
|
+
claimRead.mockRejectedValue(new Error("database unavailable"));
|
|
1576
|
+
const toolkit: KubectlInvestigationToolkit = singleClusterToolkit();
|
|
1577
|
+
|
|
1578
|
+
const outcome: ToolCallOutcome = await runOn(toolkit);
|
|
1579
|
+
|
|
1580
|
+
// Nothing is known to have run, so nothing is cited...
|
|
1581
|
+
expect(outcome.success).toBe(false);
|
|
1582
|
+
expect(outcome.result).toBeUndefined();
|
|
1583
|
+
// ...nor is it said not to have run...
|
|
1584
|
+
expect(isKubectlResultUnknownMessage(outcome.errorMessage)).toBe(true);
|
|
1585
|
+
// ...and nothing is known to be unreachable either.
|
|
1586
|
+
expect(toolkit.isClusterUnreachable(CLUSTER_ID.toString())).toBe(false);
|
|
1587
|
+
await runOn(toolkit);
|
|
1588
|
+
expect(enqueue).toHaveBeenCalledTimes(2);
|
|
1589
|
+
});
|
|
1590
|
+
|
|
1591
|
+
// Negative control: an unclaimed job still says the Runner did not pick it up.
|
|
1592
|
+
it("keeps the never-ran wording for a job no Runner claimed", async () => {
|
|
1593
|
+
const outcome: ToolCallOutcome = await runOn(singleClusterToolkit());
|
|
1594
|
+
|
|
1595
|
+
expect(outcome.errorMessage).toContain("did not pick up");
|
|
1596
|
+
expect(isKubectlResultUnknownMessage(outcome.errorMessage)).toBe(false);
|
|
1597
|
+
});
|
|
1598
|
+
|
|
1599
|
+
/*
|
|
1600
|
+
* One definition of the marker (next to the shared tool names): the
|
|
1601
|
+
* event the toolkit persists for a lost result starts with it, and the
|
|
1602
|
+
* panel's reader recognises that event.
|
|
1603
|
+
*/
|
|
1604
|
+
it("persists a lost result under the shared result-unknown marker, which the panel reads as unknown", async () => {
|
|
1605
|
+
claimRead.mockResolvedValue(
|
|
1606
|
+
claimRow({
|
|
1607
|
+
claimedAt: new Date("2026-09-22T10:00:00.000Z"),
|
|
1608
|
+
assignedAgentId: RUNNER_ID,
|
|
1609
|
+
}),
|
|
1610
|
+
);
|
|
1611
|
+
|
|
1612
|
+
const outcome: ToolCallOutcome = await runOn(singleClusterToolkit());
|
|
1613
|
+
|
|
1614
|
+
expect(
|
|
1615
|
+
outcome.errorMessage?.startsWith(
|
|
1616
|
+
`${SHARED_KUBECTL_RESULT_UNKNOWN_PREFIX} `,
|
|
1617
|
+
),
|
|
1618
|
+
).toBe(true);
|
|
1619
|
+
expect(isKubectlResultUnknownMessage(outcome.errorMessage)).toBe(true);
|
|
1620
|
+
// What the server module exports under that name is the shared marker.
|
|
1621
|
+
expect(KUBECTL_RESULT_UNKNOWN_EVENT_PREFIX).toBe(
|
|
1622
|
+
SHARED_KUBECTL_RESULT_UNKNOWN_PREFIX,
|
|
1623
|
+
);
|
|
1624
|
+
});
|
|
1625
|
+
|
|
1626
|
+
it.each([
|
|
1627
|
+
[
|
|
1628
|
+
"the claim-time credential refusal",
|
|
1629
|
+
"The credential this step references is not available to this Runner. Check that it exists and that this Runner is assigned to it.",
|
|
1630
|
+
],
|
|
1631
|
+
[
|
|
1632
|
+
"the Runner's cluster refusal",
|
|
1633
|
+
'Refused by the Runner: this kubectl command targets cluster "prod-us" but this Runner is the Kubernetes agent of cluster "staging" and only runs credential-less kubectl for its own cluster.',
|
|
1634
|
+
],
|
|
1635
|
+
[
|
|
1636
|
+
"a missing kubectl",
|
|
1637
|
+
"kubectl is not installed on this Runner. Use the oneuptime/runner image (which bundles kubectl) or install kubectl on the host.",
|
|
1638
|
+
],
|
|
1639
|
+
])(
|
|
1640
|
+
"reports %s as a failed call with no citation",
|
|
1641
|
+
async (_label: string, refusal: string) => {
|
|
1642
|
+
poll.mockResolvedValue(
|
|
1643
|
+
fakeJob({
|
|
1644
|
+
status: RunnerJobStatus.Failed,
|
|
1645
|
+
exitCode: undefined,
|
|
1646
|
+
output: "",
|
|
1647
|
+
errorMessage: refusal,
|
|
1648
|
+
claimedAt: new Date("2026-09-22T10:00:00.000Z"),
|
|
1649
|
+
}),
|
|
1650
|
+
);
|
|
1651
|
+
const toolkit: KubectlInvestigationToolkit = singleClusterToolkit();
|
|
1652
|
+
|
|
1653
|
+
const outcome: ToolCallOutcome = await runOn(toolkit);
|
|
1654
|
+
|
|
1655
|
+
expect(outcome.success).toBe(false);
|
|
1656
|
+
expect(outcome.result).toBeUndefined();
|
|
1657
|
+
expect(outcome.textForLlm).toContain(
|
|
1658
|
+
'No kubectl result came back from cluster "prod-us"',
|
|
1659
|
+
);
|
|
1660
|
+
// The model reads why; the persisted event carries only the category.
|
|
1661
|
+
expect(outcome.textForLlm).toContain(refusal.slice(0, 40));
|
|
1662
|
+
expect(outcome.errorMessage).toBe(
|
|
1663
|
+
'No kubectl result came back from cluster "prod-us": the command was refused, or the Runner did not run it.',
|
|
1664
|
+
);
|
|
1665
|
+
expect(isKubectlResultUnknownMessage(outcome.errorMessage)).toBe(false);
|
|
1666
|
+
// A refusal is fast, not a dead Runner: the breaker stays open.
|
|
1667
|
+
expect(toolkit.isClusterUnreachable(CLUSTER_ID.toString())).toBe(false);
|
|
1668
|
+
// A refusal is an access problem, so the cluster's page shows it.
|
|
1669
|
+
expect(recordOutcome).toHaveBeenCalledWith(
|
|
1670
|
+
expect.objectContaining({ succeeded: false }),
|
|
1671
|
+
);
|
|
1672
|
+
},
|
|
1673
|
+
);
|
|
1674
|
+
|
|
1675
|
+
it("keeps a kubectl the Runner killed at its timeout as a command that ran", async () => {
|
|
1676
|
+
poll.mockResolvedValue(
|
|
1677
|
+
fakeJob({
|
|
1678
|
+
status: RunnerJobStatus.Failed,
|
|
1679
|
+
exitCode: undefined,
|
|
1680
|
+
output: "",
|
|
1681
|
+
errorMessage: "Killed (timeout 30000ms)",
|
|
1682
|
+
}),
|
|
1683
|
+
);
|
|
1684
|
+
|
|
1685
|
+
const outcome: ToolCallOutcome = await runOn(singleClusterToolkit());
|
|
1686
|
+
|
|
1687
|
+
expect(outcome.success).toBe(true);
|
|
1688
|
+
expect(outcome.result?.rowCount).toBe(0);
|
|
1689
|
+
expect(outcome.textForLlm).toContain("Killed (timeout 30000ms)");
|
|
1690
|
+
});
|
|
1691
|
+
});
|
|
1692
|
+
|
|
1693
|
+
/*
|
|
1694
|
+
* The AIRun heartbeat: a kubectl wait can outlast the stale-run sweeper's
|
|
1695
|
+
* window, so the run is touched while the command is in flight — and never
|
|
1696
|
+
* after, whether the wait resolved or threw.
|
|
1697
|
+
*/
|
|
1698
|
+
describe("KubectlJobRunner keeps the AI run alive while it waits", () => {
|
|
1699
|
+
let touch: jest.SpyInstance;
|
|
1700
|
+
|
|
1701
|
+
beforeEach(() => {
|
|
1702
|
+
jest.useFakeTimers();
|
|
1703
|
+
touch = jest
|
|
1704
|
+
.spyOn(AIRunService, "updateOneBy")
|
|
1705
|
+
.mockResolvedValue(undefined as never);
|
|
1706
|
+
jest
|
|
1707
|
+
.spyOn(KubernetesClusterAiAccessService, "recordCommandOutcome")
|
|
1708
|
+
.mockResolvedValue(undefined);
|
|
1709
|
+
jest
|
|
1710
|
+
.spyOn(RunnerJobService, "enqueueAiKubectlCommand")
|
|
1711
|
+
.mockResolvedValue(fakeJob());
|
|
1712
|
+
});
|
|
1713
|
+
|
|
1714
|
+
afterEach(() => {
|
|
1715
|
+
jest.useRealTimers();
|
|
1716
|
+
jest.restoreAllMocks();
|
|
1717
|
+
});
|
|
1718
|
+
|
|
1719
|
+
function runOnce(): Promise<unknown> {
|
|
1720
|
+
return KubectlJobRunner.run({
|
|
1721
|
+
projectId: PROJECT_ID,
|
|
1722
|
+
aiRunId: RUN_ID,
|
|
1723
|
+
origin: RunnerJobOrigin.AiInvestigation,
|
|
1724
|
+
kubernetesClusterId: CLUSTER_ID,
|
|
1725
|
+
targetRunnerId: RUNNER_ID,
|
|
1726
|
+
command: "kubectl get pods -n web",
|
|
1727
|
+
stepId: "ai-investigation-kubectl-1",
|
|
1728
|
+
timeoutInMs: DEFAULT_KUBECTL_TIMEOUT_MS,
|
|
1729
|
+
});
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
/*
|
|
1733
|
+
* pollUntilTerminal held open until the test settles it; `polled`
|
|
1734
|
+
* resolves once the wait has started (the heartbeat timer is armed just
|
|
1735
|
+
* before it), so the fake clock only moves while a command is in flight.
|
|
1736
|
+
*/
|
|
1737
|
+
function holdPoll(): {
|
|
1738
|
+
polled: Promise<void>;
|
|
1739
|
+
settle: (job: RunnerJob) => void;
|
|
1740
|
+
fail: (error: Error) => void;
|
|
1741
|
+
} {
|
|
1742
|
+
let markPolled: () => void = (): void => {};
|
|
1743
|
+
const polled: Promise<void> = new Promise<void>(
|
|
1744
|
+
(resolve: () => void): void => {
|
|
1745
|
+
markPolled = resolve;
|
|
1746
|
+
},
|
|
1747
|
+
);
|
|
1748
|
+
const handles: {
|
|
1749
|
+
settle: (job: RunnerJob) => void;
|
|
1750
|
+
fail: (error: Error) => void;
|
|
1751
|
+
} = { settle: (): void => {}, fail: (): void => {} };
|
|
1752
|
+
|
|
1753
|
+
jest
|
|
1754
|
+
.spyOn(RunnerJobService, "pollUntilTerminal")
|
|
1755
|
+
.mockImplementation((): Promise<RunnerJob> => {
|
|
1756
|
+
markPolled();
|
|
1757
|
+
return new Promise<RunnerJob>(
|
|
1758
|
+
(
|
|
1759
|
+
resolve: (job: RunnerJob) => void,
|
|
1760
|
+
reject: (error: Error) => void,
|
|
1761
|
+
): void => {
|
|
1762
|
+
handles.settle = resolve;
|
|
1763
|
+
handles.fail = reject;
|
|
1764
|
+
},
|
|
1765
|
+
);
|
|
1766
|
+
});
|
|
1767
|
+
|
|
1768
|
+
return {
|
|
1769
|
+
polled,
|
|
1770
|
+
settle: (job: RunnerJob): void => {
|
|
1771
|
+
handles.settle(job);
|
|
1772
|
+
},
|
|
1773
|
+
fail: (error: Error): void => {
|
|
1774
|
+
handles.fail(error);
|
|
1775
|
+
},
|
|
1776
|
+
};
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
it("touches the run about every 15 s while the job is in flight, and stops after it settles", async () => {
|
|
1780
|
+
const poll: ReturnType<typeof holdPoll> = holdPoll();
|
|
1781
|
+
|
|
1782
|
+
const running: Promise<unknown> = runOnce();
|
|
1783
|
+
await poll.polled;
|
|
1784
|
+
jest.advanceTimersByTime(40_000);
|
|
1785
|
+
|
|
1786
|
+
expect(touch).toHaveBeenCalledTimes(2);
|
|
1787
|
+
expect(touch.mock.calls[0]![0]).toEqual(
|
|
1788
|
+
expect.objectContaining({
|
|
1789
|
+
query: { _id: RUN_ID.toString(), status: AIRunStatus.Running },
|
|
1790
|
+
}),
|
|
1791
|
+
);
|
|
1792
|
+
|
|
1793
|
+
poll.settle(fakeJob());
|
|
1794
|
+
await running;
|
|
1795
|
+
jest.advanceTimersByTime(60_000);
|
|
1796
|
+
|
|
1797
|
+
expect(touch).toHaveBeenCalledTimes(2);
|
|
1798
|
+
});
|
|
1799
|
+
|
|
1800
|
+
it("stops touching the run when the wait throws", async () => {
|
|
1801
|
+
const poll: ReturnType<typeof holdPoll> = holdPoll();
|
|
1802
|
+
|
|
1803
|
+
const running: Promise<unknown> = runOnce();
|
|
1804
|
+
const settled: Promise<unknown> = running.catch((error: unknown) => {
|
|
1805
|
+
return error;
|
|
1806
|
+
});
|
|
1807
|
+
await poll.polled;
|
|
1808
|
+
jest.advanceTimersByTime(20_000);
|
|
1809
|
+
expect(touch).toHaveBeenCalledTimes(1);
|
|
1810
|
+
|
|
1811
|
+
poll.fail(new Error("RunnerJob disappeared while waiting."));
|
|
1812
|
+
expect(await settled).toBeInstanceOf(Error);
|
|
1813
|
+
jest.advanceTimersByTime(60_000);
|
|
1814
|
+
|
|
1815
|
+
expect(touch).toHaveBeenCalledTimes(1);
|
|
1816
|
+
});
|
|
1817
|
+
});
|
|
1818
|
+
|
|
1819
|
+
/*
|
|
1820
|
+
* The server writes the "result unknown" marker into the run's persisted
|
|
1821
|
+
* event; the dashboard reads it to count such a command apart from one
|
|
1822
|
+
* that never ran. The marker has one definition (next to the shared tool
|
|
1823
|
+
* names); the toolkit's behaviour against the panel's reader is pinned in
|
|
1824
|
+
* "when kubectl never ran" above.
|
|
1825
|
+
*/
|
|
1826
|
+
describe("the kubectl result-unknown event marker", () => {
|
|
1827
|
+
it("does not match the never-ran wording", () => {
|
|
1828
|
+
expect(
|
|
1829
|
+
isKubectlResultUnknownMessage(
|
|
1830
|
+
'No kubectl result came back from cluster "prod-us": the command was refused, or the Runner did not run it.',
|
|
1831
|
+
),
|
|
1832
|
+
).toBe(false);
|
|
1833
|
+
expect(
|
|
1834
|
+
isKubectlResultUnknownMessage(
|
|
1835
|
+
'kubectl was not run on cluster "prod-us": its Runner did not pick up the command in time.',
|
|
1836
|
+
),
|
|
1837
|
+
).toBe(false);
|
|
1838
|
+
expect(isKubectlResultUnknownMessage(undefined)).toBe(false);
|
|
1839
|
+
});
|
|
1840
|
+
});
|