@oneuptime/common 14.0.1 → 14.0.2

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