@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
@@ -13,20 +13,44 @@ import {
13
13
  AiRemediationCommandExecutionStatus,
14
14
  AiRemediationCommandPlan,
15
15
  AiRemediationCommandPlanUtil,
16
+ AiRemediationCommandPolicyVerdict,
16
17
  AiRemediationPlanExecutionStatus,
17
18
  AiRemediationRollbackStatus,
19
+ getForwardCommandStepId,
20
+ getRollbackCommandStepId,
21
+ isSettledCommandExecutionStatus,
18
22
  } from "../../../Types/AutoRemediation/AiRemediationCommandPlan";
23
+ import SortOrder from "../../../Types/BaseDatabase/SortOrder";
19
24
  import CommandPolicy from "../../../Utils/AiRemediation/CommandPolicy";
25
+ import KubectlPolicy, {
26
+ KubectlAutoExecutionVerdict,
27
+ KubectlPolicyResult,
28
+ } from "../../../Utils/AiRemediation/KubectlPolicy";
29
+ import {
30
+ KubectlCommandTier,
31
+ KubernetesAiAccessGap,
32
+ KubernetesAiRemediationMode,
33
+ KubernetesClusterAiAccessStatus,
34
+ } from "../../../Types/Kubernetes/KubernetesClusterAiAccess";
35
+ import RunbookStepType from "../../../Types/Runbook/RunbookStepType";
36
+ import RunnerJobOrigin from "../../../Types/Runbook/RunnerJobOrigin";
20
37
  import { Green500, Red500 } from "../../../Types/BrandColors";
21
38
  import Color from "../../../Types/Color";
22
39
  import AlertFeedService from "../../Services/AlertFeedService";
23
40
  import AutoRemediationSuggestionService from "../../Services/AutoRemediationSuggestionService";
24
41
  import IncidentFeedService from "../../Services/IncidentFeedService";
42
+ import KubernetesClusterAiAccessService from "../../Services/KubernetesClusterAiAccessService";
25
43
  import RunnerJobService, {
26
44
  isTerminalAgentJobStatus,
27
45
  } from "../../Services/RunnerJobService";
28
46
  import RunnerJobStatus from "../../../Types/Runbook/RunnerJobStatus";
47
+ import Semaphore, { SemaphoreMutex } from "../../Infrastructure/Semaphore";
29
48
  import ToolResultSerializer from "../AI/Toolbox/Serializer";
49
+ import KubectlJobRunner, {
50
+ KubectlRunState,
51
+ KubectlTerminalJobFacts,
52
+ } from "../AI/ClusterAccess/KubectlJobRunner";
53
+ import RemediationCommandToolkit from "../AI/Remediation/RemediationCommandTools";
30
54
  import logger from "../Logger";
31
55
  import CaptureSpan from "../Telemetry/CaptureSpan";
32
56
 
@@ -45,11 +69,122 @@ import CaptureSpan from "../Telemetry/CaptureSpan";
45
69
  * executed command that carried a rollbackCommand is undone, in reverse
46
70
  * order. Rollback commands were visible on the approval card (or, for
47
71
  * FullAuto, passed the same policy the forward command did) and are
48
- * re-checked against the hard denylist here anyway.
72
+ * re-checked against the hard denylist here anyway. A command that may
73
+ * have run without saying so — its record still Pending, or a kubectl
74
+ * command recorded as failed whose job a Runner took with no result back
75
+ * (KubectlJobRunner's Unknown run state) — is never treated as never run:
76
+ * its undo is not run blind either, a human is told to check and undo it.
77
+ *
78
+ * Kubectl commands — forward and rollback — are additionally re-checked
79
+ * against the cluster's AI page AS IT IS NOW, right before they are
80
+ * enqueued: an approval can be clicked hours after the plan was composed,
81
+ * and a rollback fires at the end of the verification window. The
82
+ * operator's current choice (remediation off, a different Runner bound, a
83
+ * mode that no longer bypasses approvals) wins over the frozen plan.
84
+ *
85
+ * The two arms can overlap: verification fails a plan that overran its
86
+ * window while the executor is still waiting on a command. Three rules keep
87
+ * them from undoing each other:
88
+ * - the executor starts each command (its stand-down check, the enqueue,
89
+ * and the write that names the job) under a per-plan lock, and the
90
+ * rollback arm reads the plan under the same lock — so every forward
91
+ * job that will ever exist is either on the record the rollback arm
92
+ * reads, or its command stands down;
93
+ * - the rollback arm WAITS for a forward job still in flight and rolls it
94
+ * back once it succeeds, instead of skipping it;
95
+ * - both write the plan read-modify-write under that lock, each writing
96
+ * only its own fields (the executor: forward executions and the plan's
97
+ * execution lifecycle; the rollback arm: rollbacks and rollbackStatus),
98
+ * so neither erases the other's record.
99
+ *
100
+ * A Worker restart mid-rollback leaves the plan's rollbackStatus unset with
101
+ * a heartbeat that goes stale; the verifier's recovery sweep resumes it
102
+ * (executeRollback settles rollbacks already handed to a Runner from their
103
+ * jobs, and never re-runs one that ran). The resumed rollback's outcome is
104
+ * the WHOLE plan's: an undo the interrupted attempt already settled as
105
+ * failed, or left for a human, keeps the rollback Failed — the verifier
106
+ * forces the follow-up round to ask first on exactly that.
49
107
  */
50
108
 
51
109
  const AI_COMMAND_CLAIM_TIMEOUT_MS: number = 60_000;
52
110
  const MAX_STORED_OUTPUT_CHARS: number = 6000;
111
+ const MAX_FEED_REASON_CHARS: number = 400;
112
+
113
+ /*
114
+ * The per-plan lock covers database writes and one enqueue — never a wait
115
+ * on a Runner. It expires on its own if the pod dies holding it.
116
+ */
117
+ const PLAN_LOCK_NAMESPACE: string = "AutoRemediationCommandPlan";
118
+ const PLAN_LOCK_TIMEOUT_MS: number = 30_000;
119
+ const PLAN_LOCK_ACQUIRE_TIMEOUT_MS: number = 15_000;
120
+
121
+ // What the rollback arm needs from a RunnerJob to settle its command.
122
+ const RECONCILE_JOB_SELECT: {
123
+ _id: true;
124
+ status: true;
125
+ output: true;
126
+ exitCode: true;
127
+ errorMessage: true;
128
+ } = {
129
+ _id: true,
130
+ status: true,
131
+ output: true,
132
+ exitCode: true,
133
+ errorMessage: true,
134
+ };
135
+
136
+ /*
137
+ * What the rollback arm needs from a kubectl RunnerJob to tell whether
138
+ * kubectl ran (KubectlJobRunner.getRunStateOfJobRow): the outcome, and
139
+ * whether a Runner ever claimed or started the job.
140
+ */
141
+ const RUN_STATE_JOB_SELECT: {
142
+ _id: true;
143
+ status: true;
144
+ output: true;
145
+ exitCode: true;
146
+ errorMessage: true;
147
+ claimedAt: true;
148
+ startedAt: true;
149
+ assignedAgentId: true;
150
+ } = {
151
+ ...RECONCILE_JOB_SELECT,
152
+ claimedAt: true,
153
+ startedAt: true,
154
+ assignedAgentId: true,
155
+ };
156
+
157
+ // Who is writing the plan — each side owns different fields of it.
158
+ type PlanWriter = "executor" | "rollback";
159
+
160
+ /*
161
+ * What the rollback arm did, for the verifier: it gates the follow-up round
162
+ * on it and records it on the verification note. rollbackStatus is
163
+ * undefined when the arm could not settle the rollback (no plan, or the
164
+ * status could not be written).
165
+ */
166
+ export interface CommandPlanRollbackOutcome {
167
+ rollbackStatus: AiRemediationRollbackStatus | undefined;
168
+ // Rollbacks that ran and succeeded.
169
+ rolledBack: number;
170
+ // Rollbacks that ran and failed, or that the command policy refused.
171
+ failed: number;
172
+ // Rollbacks left for a human to run by hand (their outcome says why).
173
+ leftForHuman: number;
174
+ // One sentence for the verification note.
175
+ summary: string;
176
+ }
177
+
178
+ interface StartedCommand {
179
+ state: AiRemediationCommandExecutionState;
180
+ // Set when the command reached a Runner; null when it was refused first.
181
+ job: RunnerJob | null;
182
+ /*
183
+ * Whether the cluster's AI page may record this outcome at all (it then
184
+ * records a success, or a failure about the cluster's ACCESS only).
185
+ */
186
+ recordsClusterOutcome: boolean;
187
+ }
53
188
 
54
189
  export default class CommandPlanExecutor {
55
190
  /*
@@ -110,9 +245,13 @@ export default class CommandPlanExecutor {
110
245
 
111
246
  plan.executionStatus = AiRemediationPlanExecutionStatus.Running;
112
247
  plan.executionStartedAt = OneUptimeDate.getCurrentDate().toISOString();
113
- await this.persistPlan(suggestion, plan);
248
+ await this.persistPlan(suggestion, plan, "executor");
114
249
 
115
250
  let failed: boolean = false;
251
+ // What stopped the plan, for the feed: the first non-Succeeded outcome.
252
+ let firstFailure: string | null = null;
253
+ // Verification settled the remediation while the plan was executing.
254
+ let settledMidPlan: boolean = false;
116
255
 
117
256
  for (const command of plan.commands) {
118
257
  // FullAuto-executed entries never appear in approved Suggest plans.
@@ -129,43 +268,76 @@ export default class CommandPlanExecutor {
129
268
  }
130
269
 
131
270
  /*
132
- * Abort check between commands. Verification runs on its own sweep
133
- * and can fail this remediation while the plan is still executing
134
- * (a slow Runner, a long command); once it does, it rolls the
135
- * executed commands back. Continuing to push NEW forward commands
136
- * into a rollback that is already under way is how a plan and its
137
- * undo end up fighting each other, so the executor stops the moment
138
- * verification is no longer Pending or a human moved the
139
- * suggestion off Approved.
271
+ * Start the command under the plan lock: the stand-down check, the
272
+ * Pending marker, the enqueue and the write that names the job. The
273
+ * rollback arm reads the plan under the same lock, so it either sees
274
+ * this command's job (and waits for it) or this command sees the
275
+ * settled verification and never starts.
140
276
  */
141
- if (!(await this.isStillExecutable(suggestion.id!))) {
142
- command.execution = {
143
- status: AiRemediationCommandExecutionStatus.Skipped,
144
- errorMessage:
145
- "Skipped: this remediation was settled (verified, failed, or actioned by a human) before the command ran.",
146
- };
147
- failed = true;
148
- continue;
277
+ const mutex: SemaphoreMutex | null = await this.lockPlan(
278
+ suggestion.id!,
279
+ );
280
+
281
+ let started: StartedCommand | null = null;
282
+
283
+ try {
284
+ /*
285
+ * Abort check between commands. Verification runs on its own
286
+ * sweep and can fail this remediation while the plan is still
287
+ * executing (a slow Runner, a long command); once it does, it
288
+ * rolls the executed commands back. Continuing to push NEW
289
+ * forward commands into a rollback that is already under way is
290
+ * how a plan and its undo end up fighting each other, so the
291
+ * executor stops the moment verification is no longer Pending or
292
+ * a human moved the suggestion off Approved.
293
+ */
294
+ if (!(await this.isStillExecutable(suggestion.id!))) {
295
+ command.execution = {
296
+ status: AiRemediationCommandExecutionStatus.Skipped,
297
+ errorMessage:
298
+ "Skipped: this remediation was settled (verified, failed, or actioned by a human) before the command ran.",
299
+ };
300
+ failed = true;
301
+ settledMidPlan = true;
302
+ } else {
303
+ /*
304
+ * Persist the Pending marker BEFORE the side effect: a pod death
305
+ * mid-command must leave a record that this command may have
306
+ * run.
307
+ */
308
+ command.execution = {
309
+ status: AiRemediationCommandExecutionStatus.Pending,
310
+ startedAt: OneUptimeDate.getCurrentDate().toISOString(),
311
+ };
312
+ await this.persistPlan(suggestion, plan, "executor", {
313
+ planLocked: true,
314
+ });
315
+
316
+ started = await this.startOneCommand({ suggestion, plan, command });
317
+ }
318
+ } finally {
319
+ await this.unlockPlan(mutex);
149
320
  }
150
321
 
151
- /*
152
- * Persist the Pending marker BEFORE the side effect: a pod death
153
- * mid-command must leave a record that this command may have run.
154
- */
155
- command.execution = {
156
- status: AiRemediationCommandExecutionStatus.Pending,
157
- startedAt: OneUptimeDate.getCurrentDate().toISOString(),
158
- };
159
- await this.persistPlan(suggestion, plan);
322
+ if (!started) {
323
+ continue;
324
+ }
160
325
 
161
326
  const state: AiRemediationCommandExecutionState =
162
- await this.executeOneCommand({ suggestion, command });
327
+ await this.finishOneCommand({ command, started });
163
328
 
164
329
  command.execution = state;
165
- await this.persistPlan(suggestion, plan);
330
+ await this.persistPlan(suggestion, plan, "executor");
166
331
 
167
332
  if (state.status !== AiRemediationCommandExecutionStatus.Succeeded) {
168
333
  failed = true;
334
+ firstFailure =
335
+ firstFailure ||
336
+ `command ${command.sequence} ${
337
+ state.errorMessage
338
+ ? `failed: ${this.capForFeed(state.errorMessage)}`
339
+ : "failed"
340
+ }`;
169
341
  }
170
342
  }
171
343
 
@@ -173,15 +345,31 @@ export default class CommandPlanExecutor {
173
345
  ? AiRemediationPlanExecutionStatus.Failed
174
346
  : AiRemediationPlanExecutionStatus.Completed;
175
347
  plan.executionCompletedAt = OneUptimeDate.getCurrentDate().toISOString();
176
- await this.persistPlan(suggestion, plan);
348
+ await this.persistPlan(suggestion, plan, "executor");
349
+
350
+ /*
351
+ * The last command can still be running when verification concludes
352
+ * (a plan that overran its window). "Verification is watching" would
353
+ * then contradict the verification and rollback notes already on the
354
+ * feed.
355
+ */
356
+ if (!settledMidPlan && !(await this.isStillExecutable(suggestion.id!))) {
357
+ settledMidPlan = true;
358
+ }
177
359
 
178
360
  await this.postFeedItem({
179
361
  suggestion,
180
- markdown: failed
181
- ? `⚠️ **Approved AI command plan did not complete** — a command failed; the remaining commands were skipped. Review the per-command output on the suggestion. Verification will judge (and roll back) what ran.`
182
- : `⚡ **Approved AI command plan executed** (${plan.commands.length} command(s)). Verification is watching the monitors for recovery.`,
183
- pingWorkspace: failed,
184
- displayColor: failed ? Red500 : Green500,
362
+ markdown: settledMidPlan
363
+ ? `⚠️ **Approved AI command plan finished after its verification had already concluded**${
364
+ firstFailure ? ` — ${firstFailure}` : ""
365
+ }. Commands that had not started yet were skipped; the verification and rollback notes on this suggestion say what was judged and what was undone. Review the per-command record.`
366
+ : failed
367
+ ? `⚠️ **Approved AI command plan did not complete** — ${
368
+ firstFailure || "a command failed"
369
+ }; the remaining commands were skipped. Review the per-command output on the suggestion. Verification will judge (and roll back) what ran.`
370
+ : `⚡ **Approved AI command plan executed** (${plan.commands.length} command(s)). Verification is watching the monitors for recovery.`,
371
+ pingWorkspace: failed || settledMidPlan,
372
+ displayColor: failed || settledMidPlan ? Red500 : Green500,
185
373
  });
186
374
  } catch (error) {
187
375
  logger.error(
@@ -193,38 +381,145 @@ export default class CommandPlanExecutor {
193
381
  /*
194
382
  * Undo a failed remediation: run the rollbackCommand of every command
195
383
  * that executed successfully, in REVERSE order. Called by the verifier
196
- * after it wins the Failed verification transition — single caller by
197
- * construction. Never throws.
384
+ * after it wins the Failed verification transition, and by its recovery
385
+ * sweep for a rollback a Worker restart interrupted. Never throws.
198
386
  */
199
387
  @CaptureSpan()
200
388
  public static async executeRollback(data: {
201
389
  suggestion: AutoRemediationSuggestion;
202
- }): Promise<void> {
390
+ }): Promise<CommandPlanRollbackOutcome> {
203
391
  const { suggestion } = data;
204
392
 
393
+ let plan: AiRemediationCommandPlan | null = null;
394
+
205
395
  try {
206
- const plan: AiRemediationCommandPlan | null =
207
- AiRemediationCommandPlanUtil.parse(suggestion.commandPlan);
396
+ /*
397
+ * Read the plan as stored NOW, under the plan lock — not the copy the
398
+ * verifier read before it won the verification transition. An
399
+ * approved plan's executor may have enqueued a command since; the
400
+ * lock guarantees its job is on this read, or that the command stands
401
+ * down and never starts.
402
+ */
403
+ plan = await this.readPlanForRollback(suggestion);
208
404
 
209
405
  if (!plan) {
210
- return;
406
+ return this.describeRollbackOutcome(null);
211
407
  }
212
408
 
213
409
  if (
214
410
  plan.rollbackStatus &&
215
411
  plan.rollbackStatus !== AiRemediationRollbackStatus.NotAttempted
216
412
  ) {
217
- return; // Already rolled back (or judged not applicable).
413
+ // Already rolled back (or judged not applicable).
414
+ return this.describeRollbackOutcome(plan);
218
415
  }
219
416
 
417
+ plan.rollbackHeartbeatAt = OneUptimeDate.getCurrentDate().toISOString();
418
+ await this.persistPlan(suggestion, plan, "rollback");
419
+
220
420
  /*
221
421
  * A command whose record still says Pending may nonetheless have run:
222
422
  * the executor persists Pending, enqueues, and can die before writing
223
- * the outcome. The RunnerJob row is the authority on what actually
224
- * happened, so resolve those before deciding what to undo — otherwise a
225
- * pod death silently exempts a real state change from rollback.
423
+ * the outcome — or it is simply still waiting on the job. The
424
+ * RunnerJob row is the authority on what actually happened, so
425
+ * resolve those first, WAITING for a job still in flight: skipping it
426
+ * would leave a change that is about to land exempt from its undo.
226
427
  */
227
- await this.reconcilePendingExecutions(suggestion, plan);
428
+ await this.reconcilePendingExecutions(suggestion, plan, {
429
+ waitForInFlight: true,
430
+ });
431
+
432
+ /*
433
+ * A resumed rollback: undos an interrupted attempt already handed to
434
+ * a Runner are settled from their jobs, never run twice.
435
+ */
436
+ await this.reconcilePendingRollbacks(suggestion, plan);
437
+
438
+ let anyFailed: boolean = false;
439
+ // Rollbacks nobody may run unattended now — a human undoes them.
440
+ const leftForHuman: Array<AiRemediationCommand> = [];
441
+
442
+ /*
443
+ * A resumed rollback reports the WHOLE plan, not just what this
444
+ * attempt does: an undo the interrupted attempt already settled as
445
+ * Failed — or that reconcilePendingRollbacks just settled Failed from
446
+ * its job — keeps the rollback Failed, and one it left for a human is
447
+ * named again on the feed. Otherwise a resumed rollback whose earlier
448
+ * undo failed would report "completed" (or "nothing to roll back"),
449
+ * and the follow-up round would run unattended on top of a change
450
+ * that is still applied.
451
+ */
452
+ for (const command of plan.commands) {
453
+ const earlier: AiRemediationCommandExecutionState | undefined =
454
+ command.rollbackExecution;
455
+
456
+ if (earlier?.status === AiRemediationCommandExecutionStatus.Failed) {
457
+ anyFailed = true;
458
+ } else if (
459
+ earlier?.status === AiRemediationCommandExecutionStatus.Skipped
460
+ ) {
461
+ anyFailed = true;
462
+ leftForHuman.push(command);
463
+ }
464
+ }
465
+
466
+ /*
467
+ * A forward command whose job could not be resolved even after
468
+ * waiting may or may not have run. Its undo is not run blind; a human
469
+ * is told to check.
470
+ */
471
+ for (const command of plan.commands) {
472
+ const execution: AiRemediationCommandExecutionState | undefined =
473
+ command.execution;
474
+
475
+ if (
476
+ execution &&
477
+ !isSettledCommandExecutionStatus(execution.status) &&
478
+ execution.runnerJobId &&
479
+ command.rollbackCommand &&
480
+ !command.rollbackExecution
481
+ ) {
482
+ command.rollbackExecution = {
483
+ status: AiRemediationCommandExecutionStatus.Skipped,
484
+ errorMessage: `Rollback not run: whether the command ran could not be confirmed from its job. If it ran, undo it manually: ${command.rollbackCommand}`,
485
+ };
486
+ leftForHuman.push(command);
487
+ anyFailed = true;
488
+ }
489
+ }
490
+
491
+ /*
492
+ * A kubectl command recorded as failed may still have changed the
493
+ * cluster: a Runner took it and no result came back, the wait for it
494
+ * broke while the job ran on, or kubectl was stopped before it
495
+ * finished. Its job is read again: one that succeeded after all is
496
+ * rolled back like any other, and one that may have changed the
497
+ * cluster gets the same answer as an unresolved one above — its undo
498
+ * is not run blind, a human is told to check.
499
+ */
500
+ for (const command of await this.reconcileFailedKubectlExecutions(
501
+ suggestion,
502
+ plan,
503
+ )) {
504
+ command.rollbackExecution = {
505
+ status: AiRemediationCommandExecutionStatus.Skipped,
506
+ errorMessage: `Rollback not run: the command reached the cluster's Runner but kubectl did not report a finished result, so whether it changed the cluster is unknown. If it did, undo it manually: ${command.rollbackCommand}`,
507
+ };
508
+ leftForHuman.push(command);
509
+ anyFailed = true;
510
+ }
511
+
512
+ if (
513
+ plan.commands.some((command: AiRemediationCommand) => {
514
+ return (
515
+ command.rollbackExecution !== undefined &&
516
+ !isSettledCommandExecutionStatus(command.rollbackExecution.status)
517
+ );
518
+ })
519
+ ) {
520
+ // A rollback of an interrupted attempt whose outcome is unknown.
521
+ anyFailed = true;
522
+ }
228
523
 
229
524
  const rollbackTargets: Array<AiRemediationCommand> = plan.commands
230
525
  .filter((command: AiRemediationCommand) => {
@@ -237,19 +532,35 @@ export default class CommandPlanExecutor {
237
532
  })
238
533
  .reverse();
239
534
 
240
- if (rollbackTargets.length === 0) {
241
- plan.rollbackStatus = AiRemediationRollbackStatus.NotApplicable;
242
- await this.persistPlan(suggestion, plan);
243
- return;
535
+ /*
536
+ * Nothing to run and nothing that went wrong, now or in an
537
+ * interrupted attempt: Completed when some undo ran, NotApplicable
538
+ * when none was ever needed. Anything else goes through the end of
539
+ * this method, which settles the status from the whole plan and says
540
+ * so on the feed.
541
+ */
542
+ if (rollbackTargets.length === 0 && !anyFailed) {
543
+ plan.rollbackStatus = plan.commands.some(
544
+ (command: AiRemediationCommand) => {
545
+ return (
546
+ command.rollbackExecution?.status ===
547
+ AiRemediationCommandExecutionStatus.Succeeded
548
+ );
549
+ },
550
+ )
551
+ ? AiRemediationRollbackStatus.Completed
552
+ : AiRemediationRollbackStatus.NotApplicable;
553
+ await this.persistPlan(suggestion, plan, "rollback");
554
+ return this.describeRollbackOutcome(plan);
244
555
  }
245
556
 
246
- let anyFailed: boolean = false;
247
-
248
557
  for (const command of rollbackTargets) {
249
558
  const rollbackCommand: string = command.rollbackCommand as string;
250
559
 
251
- const denyReason: string | null =
252
- CommandPolicy.getDenyReason(rollbackCommand);
560
+ const denyReason: string | null = this.getDenyReason({
561
+ stepType: command.stepType,
562
+ command: rollbackCommand,
563
+ });
253
564
 
254
565
  if (denyReason) {
255
566
  command.rollbackExecution = {
@@ -257,7 +568,34 @@ export default class CommandPlanExecutor {
257
568
  errorMessage: `Rollback refused by the command policy: ${denyReason}.`,
258
569
  };
259
570
  anyFailed = true;
260
- await this.persistPlan(suggestion, plan);
571
+ await this.persistPlan(suggestion, plan, "rollback");
572
+ continue;
573
+ }
574
+
575
+ /*
576
+ * A rollback runs with nobody watching, so it has to be allowed
577
+ * unattended by the cluster as it is configured NOW — not as it was
578
+ * when the plan accepted the rollback. A RiskyWrite undo accepted
579
+ * while the operator bypassed approvals must not run once they
580
+ * stopped bypassing them; a cluster whose remediation was turned
581
+ * off must not be changed at all. Left for a human, and said so.
582
+ */
583
+ const clusterRefusal: string | null =
584
+ await this.getClusterExecutionRefusal({
585
+ suggestion,
586
+ command,
587
+ commandText: rollbackCommand,
588
+ purpose: "rollback",
589
+ });
590
+
591
+ if (clusterRefusal) {
592
+ command.rollbackExecution = {
593
+ status: AiRemediationCommandExecutionStatus.Skipped,
594
+ errorMessage: `Rollback not run: ${clusterRefusal}. Undo it manually: ${rollbackCommand}`,
595
+ };
596
+ leftForHuman.push(command);
597
+ anyFailed = true;
598
+ await this.persistPlan(suggestion, plan, "rollback");
261
599
  continue;
262
600
  }
263
601
 
@@ -265,23 +603,20 @@ export default class CommandPlanExecutor {
265
603
  status: AiRemediationCommandExecutionStatus.Pending,
266
604
  startedAt: OneUptimeDate.getCurrentDate().toISOString(),
267
605
  };
268
- await this.persistPlan(suggestion, plan);
606
+ plan.rollbackHeartbeatAt = OneUptimeDate.getCurrentDate().toISOString();
607
+ await this.persistPlan(suggestion, plan, "rollback");
269
608
 
270
609
  try {
271
- const job: RunnerJob = await RunnerJobService.enqueueAiCommand({
272
- projectId: suggestion.projectId!,
273
- aiRunId: suggestion.aiRunId!,
274
- autoRemediationSuggestionId: suggestion.id!,
275
- stepId: `ai-rollback-${command.sequence}`,
276
- stepType: command.stepType,
277
- targetAgentId: new ObjectID(command.runnerId),
278
- command: rollbackCommand,
279
- credentialId: command.credentialId,
280
- timeoutInMs: command.timeoutInMs,
281
- claimTimeoutInMs: AI_COMMAND_CLAIM_TIMEOUT_MS,
610
+ const job: RunnerJob = await this.enqueue({
611
+ suggestion,
612
+ command,
613
+ commandText: rollbackCommand,
614
+ stepId: getRollbackCommandStepId(command),
282
615
  });
283
616
 
617
+ // The job id lands before the wait — see startOneCommand.
284
618
  command.rollbackExecution.runnerJobId = job.id?.toString();
619
+ await this.persistPlan(suggestion, plan, "rollback");
285
620
 
286
621
  const terminalJob: RunnerJob =
287
622
  await RunnerJobService.pollUntilTerminal({
@@ -290,22 +625,15 @@ export default class CommandPlanExecutor {
290
625
  executionTimeoutInMs: command.timeoutInMs,
291
626
  });
292
627
 
293
- const succeeded: boolean =
294
- terminalJob.status === RunnerJobStatus.Succeeded;
295
-
296
- command.rollbackExecution.status = succeeded
297
- ? AiRemediationCommandExecutionStatus.Succeeded
298
- : AiRemediationCommandExecutionStatus.Failed;
299
- command.rollbackExecution.completedAt =
300
- OneUptimeDate.getCurrentDate().toISOString();
301
- command.rollbackExecution.exitCode = terminalJob.exitCode;
302
- command.rollbackExecution.output = this.capOutput(
303
- terminalJob.output || "",
628
+ this.settleFromJob(
629
+ command.rollbackExecution,
630
+ terminalJob,
631
+ "Rollback",
304
632
  );
305
- if (!succeeded) {
306
- command.rollbackExecution.errorMessage =
307
- terminalJob.errorMessage ||
308
- `Rollback ended with status ${terminalJob.status}.`;
633
+ if (
634
+ command.rollbackExecution.status !==
635
+ AiRemediationCommandExecutionStatus.Succeeded
636
+ ) {
309
637
  anyFailed = true;
310
638
  }
311
639
  } catch (error) {
@@ -316,49 +644,127 @@ export default class CommandPlanExecutor {
316
644
  anyFailed = true;
317
645
  }
318
646
 
319
- await this.persistPlan(suggestion, plan);
647
+ await this.persistPlan(suggestion, plan, "rollback");
320
648
  }
321
649
 
322
650
  plan.rollbackStatus = anyFailed
323
651
  ? AiRemediationRollbackStatus.Failed
324
652
  : AiRemediationRollbackStatus.Completed;
325
- await this.persistPlan(suggestion, plan);
653
+ await this.persistPlan(suggestion, plan, "rollback");
654
+
655
+ const failedUndos: number = plan.commands.filter(
656
+ (command: AiRemediationCommand) => {
657
+ return (
658
+ command.rollbackExecution?.status ===
659
+ AiRemediationCommandExecutionStatus.Failed
660
+ );
661
+ },
662
+ ).length;
326
663
 
327
664
  await this.postFeedItem({
328
665
  suggestion,
329
- markdown: anyFailed
330
- ? `⚠️ **Auto-remediation rollback did not fully complete** — at least one rollback command failed. Review the suggestion's per-command record; manual intervention may be needed.`
331
- : `↩️ **Auto-remediation rolled back** — the executed commands' rollback commands ran after verification failed. Escalation continues as normal.`,
666
+ markdown:
667
+ leftForHuman.length > 0
668
+ ? `⚠️ **Auto-remediation rollback was not run for ${leftForHuman.length} command(s)** — the cluster no longer allows them unattended, or whether they ran could not be confirmed, so a human has to undo them. ${leftForHuman
669
+ .map((command: AiRemediationCommand) => {
670
+ return this.capForFeed(
671
+ command.rollbackExecution?.errorMessage || "",
672
+ );
673
+ })
674
+ .join(" ")}${
675
+ failedUndos > 0
676
+ ? ` ${failedUndos} other rollback command(s) failed.`
677
+ : ""
678
+ } Review the suggestion's per-command record; manual intervention is needed.`
679
+ : anyFailed
680
+ ? `⚠️ **Auto-remediation rollback did not fully complete** — at least one rollback command failed, or its outcome is not known. Review the suggestion's per-command record; manual intervention may be needed.`
681
+ : `↩️ **Auto-remediation rolled back** — the executed commands' rollback commands ran after verification failed. Escalation continues as normal.`,
332
682
  pingWorkspace: true,
333
683
  displayColor: anyFailed ? Red500 : Green500,
334
684
  });
685
+
686
+ return this.describeRollbackOutcome(plan);
335
687
  } catch (error) {
336
688
  logger.error(
337
689
  `CommandPlanExecutor: rollback failed for suggestion ${suggestion.id?.toString()}: ${error}`,
338
690
  );
691
+
692
+ /*
693
+ * Settle it as Failed so nobody assumes it was undone — and so the
694
+ * recovery sweep does not retry a rollback that throws every time.
695
+ */
696
+ if (plan) {
697
+ plan.rollbackStatus = AiRemediationRollbackStatus.Failed;
698
+ await this.persistPlan(suggestion, plan, "rollback");
699
+ await this.postFeedItem({
700
+ suggestion,
701
+ markdown: `⚠️ **Auto-remediation rollback could not complete** — an unexpected error stopped it. Review the suggestion's per-command record; manual intervention may be needed.`,
702
+ pingWorkspace: true,
703
+ displayColor: Red500,
704
+ });
705
+ }
706
+
707
+ return this.describeRollbackOutcome(plan);
339
708
  }
340
709
  }
341
710
 
342
711
  // ------------------------------------------------------------------
343
712
 
344
- private static async executeOneCommand(data: {
713
+ /*
714
+ * Everything up to and including the enqueue: the execution-time policy
715
+ * floor, the cluster re-check, the enqueue, and the write that names the
716
+ * job. Runs under the plan lock (see executeApprovedPlan).
717
+ */
718
+ private static async startOneCommand(data: {
345
719
  suggestion: AutoRemediationSuggestion;
720
+ plan: AiRemediationCommandPlan;
346
721
  command: AiRemediationCommand;
347
- }): Promise<AiRemediationCommandExecutionState> {
348
- const { suggestion, command } = data;
722
+ }): Promise<StartedCommand> {
723
+ const { suggestion, plan, command } = data;
349
724
 
350
725
  /*
351
726
  * Execution-time denylist re-check: the plan was validated when it was
352
727
  * composed, but the policy may have been tightened since.
353
728
  */
354
- const denyReason: string | null = CommandPolicy.getDenyReason(
355
- command.command,
356
- );
729
+ const denyReason: string | null = this.getDenyReason({
730
+ stepType: command.stepType,
731
+ command: command.command,
732
+ });
357
733
 
358
734
  if (denyReason) {
359
735
  return {
360
- status: AiRemediationCommandExecutionStatus.Failed,
361
- errorMessage: `Refused by the remediation command policy at execution time: ${denyReason}.`,
736
+ state: {
737
+ status: AiRemediationCommandExecutionStatus.Failed,
738
+ errorMessage: `Refused by the remediation command policy at execution time: ${denyReason}.`,
739
+ },
740
+ job: null,
741
+ recordsClusterOutcome: false,
742
+ };
743
+ }
744
+
745
+ /*
746
+ * Execution-time cluster re-check: the approve API re-checks the
747
+ * project switches, this re-checks the cluster's own AI page. An
748
+ * operator who turned remediation off, or bound a different Runner,
749
+ * after the plan was composed expects that to stop the plan too.
750
+ */
751
+ const clusterRefusal: string | null = await this.getClusterExecutionRefusal(
752
+ {
753
+ suggestion,
754
+ command,
755
+ commandText: command.command,
756
+ purpose: "command",
757
+ },
758
+ );
759
+
760
+ if (clusterRefusal) {
761
+ return {
762
+ state: {
763
+ status: AiRemediationCommandExecutionStatus.Failed,
764
+ errorMessage: `Refused at execution time: ${clusterRefusal}.`,
765
+ },
766
+ job: null,
767
+ recordsClusterOutcome: false,
362
768
  };
363
769
  }
364
770
 
@@ -370,104 +776,451 @@ export default class CommandPlanExecutor {
370
776
  };
371
777
 
372
778
  try {
373
- const job: RunnerJob = await RunnerJobService.enqueueAiCommand({
779
+ const job: RunnerJob = await this.enqueue({
780
+ suggestion,
781
+ command,
782
+ commandText: command.command,
783
+ // Approved plans never hold inline-executed commands (skipped above).
784
+ stepId: getForwardCommandStepId(command),
785
+ });
786
+
787
+ /*
788
+ * The job id lands on the record BEFORE the wait: a pod death while
789
+ * polling must leave a Pending command that names its RunnerJob, so
790
+ * the rollback arm can resolve what really happened instead of
791
+ * treating the command as one that never reached the Runner.
792
+ */
793
+ state.runnerJobId = job.id?.toString();
794
+ command.execution = state;
795
+ await this.persistPlan(suggestion, plan, "executor", {
796
+ planLocked: true,
797
+ });
798
+
799
+ return { state, job, recordsClusterOutcome: true };
800
+ } catch (error) {
801
+ state.status = AiRemediationCommandExecutionStatus.Failed;
802
+ state.completedAt = OneUptimeDate.getCurrentDate().toISOString();
803
+ state.errorMessage =
804
+ error instanceof Error ? error.message : String(error);
805
+
806
+ return { state, job: null, recordsClusterOutcome: true };
807
+ }
808
+ }
809
+
810
+ // The wait (outside the plan lock) and the outcome.
811
+ private static async finishOneCommand(data: {
812
+ command: AiRemediationCommand;
813
+ started: StartedCommand;
814
+ }): Promise<AiRemediationCommandExecutionState> {
815
+ const { command, started } = data;
816
+ const state: AiRemediationCommandExecutionState = started.state;
817
+
818
+ /*
819
+ * What the job's end says about the cluster's ACCESS. A job that never
820
+ * reached a Runner (the enqueue refused it) is an access failure; a
821
+ * wait that threw says nothing about the cluster.
822
+ */
823
+ let facts: KubectlTerminalJobFacts | null = started.job
824
+ ? null
825
+ : { status: RunnerJobStatus.Failed, errorMessage: state.errorMessage };
826
+
827
+ if (started.job) {
828
+ try {
829
+ const terminalJob: RunnerJob = await RunnerJobService.pollUntilTerminal(
830
+ {
831
+ jobId: started.job.id!,
832
+ claimTimeoutInMs: AI_COMMAND_CLAIM_TIMEOUT_MS,
833
+ executionTimeoutInMs: command.timeoutInMs,
834
+ },
835
+ );
836
+
837
+ if (!isTerminalAgentJobStatus(terminalJob.status)) {
838
+ throw new Error("Command job did not reach a terminal state.");
839
+ }
840
+
841
+ this.settleFromJob(state, terminalJob, "Command");
842
+ facts = {
843
+ status: terminalJob.status,
844
+ exitCode: terminalJob.exitCode,
845
+ output: terminalJob.output,
846
+ errorMessage: terminalJob.errorMessage,
847
+ };
848
+ } catch (error) {
849
+ state.status = AiRemediationCommandExecutionStatus.Failed;
850
+ state.completedAt = OneUptimeDate.getCurrentDate().toISOString();
851
+ state.errorMessage =
852
+ error instanceof Error ? error.message : String(error);
853
+ }
854
+ }
855
+
856
+ /*
857
+ * The cluster's "Last error" is about its AI ACCESS: a success proves
858
+ * the access works, and a failure is recorded only when it is about
859
+ * the access (never claimed, refused, timed out, unauthorized,
860
+ * unreachable). A deployment the plan named wrong must neither become
861
+ * the cluster's last error nor clear a real one.
862
+ */
863
+ const succeeded: boolean =
864
+ state.status === AiRemediationCommandExecutionStatus.Succeeded;
865
+ const recordsOutcome: boolean =
866
+ succeeded || (facts !== null && KubectlJobRunner.isAccessFailure(facts));
867
+
868
+ if (
869
+ started.recordsClusterOutcome &&
870
+ recordsOutcome &&
871
+ command.stepType === RunbookStepType.Kubectl &&
872
+ command.kubernetesClusterId &&
873
+ ObjectID.isValidUUID(command.kubernetesClusterId)
874
+ ) {
875
+ await KubernetesClusterAiAccessService.recordCommandOutcome({
876
+ clusterId: new ObjectID(command.kubernetesClusterId),
877
+ succeeded:
878
+ state.status === AiRemediationCommandExecutionStatus.Succeeded,
879
+ errorMessage: state.errorMessage,
880
+ });
881
+ }
882
+
883
+ return state;
884
+ }
885
+
886
+ // Copy a terminal job's outcome onto an execution record.
887
+ private static settleFromJob(
888
+ state: AiRemediationCommandExecutionState,
889
+ job: RunnerJob,
890
+ label: "Command" | "Rollback",
891
+ ): void {
892
+ const succeeded: boolean = job.status === RunnerJobStatus.Succeeded;
893
+
894
+ state.status = succeeded
895
+ ? AiRemediationCommandExecutionStatus.Succeeded
896
+ : AiRemediationCommandExecutionStatus.Failed;
897
+ state.completedAt = OneUptimeDate.getCurrentDate().toISOString();
898
+ state.exitCode = job.exitCode;
899
+ state.output = this.capOutput(job.output || "");
900
+ state.errorMessage = succeeded
901
+ ? undefined
902
+ : job.errorMessage || `${label} ended with status ${job.status}.`;
903
+ }
904
+
905
+ /*
906
+ * One enqueue for both lanes. Bash/SSH go through enqueueAiCommand
907
+ * (script or SSH payload); Kubectl goes through enqueueAiKubectlCommand
908
+ * (argv + cluster + optional credential), whose policy is re-run there.
909
+ */
910
+ private static async enqueue(data: {
911
+ suggestion: AutoRemediationSuggestion;
912
+ command: AiRemediationCommand;
913
+ commandText: string;
914
+ stepId: string;
915
+ }): Promise<RunnerJob> {
916
+ const { suggestion, command } = data;
917
+
918
+ if (command.stepType === RunbookStepType.Kubectl) {
919
+ if (
920
+ !command.kubernetesClusterId ||
921
+ !ObjectID.isValidUUID(command.kubernetesClusterId)
922
+ ) {
923
+ throw new Error("The kubectl command names no cluster.");
924
+ }
925
+
926
+ return RunnerJobService.enqueueAiKubectlCommand({
374
927
  projectId: suggestion.projectId!,
375
928
  aiRunId: suggestion.aiRunId!,
929
+ origin: RunnerJobOrigin.AiRemediation,
376
930
  autoRemediationSuggestionId: suggestion.id!,
377
- stepId: `ai-approved-${command.sequence}`,
378
- stepType: command.stepType,
931
+ kubernetesClusterId: new ObjectID(command.kubernetesClusterId),
932
+ stepId: data.stepId,
379
933
  targetAgentId: new ObjectID(command.runnerId),
380
- command: command.command,
381
934
  credentialId: command.credentialId,
935
+ command: data.commandText,
382
936
  timeoutInMs: command.timeoutInMs,
383
937
  claimTimeoutInMs: AI_COMMAND_CLAIM_TIMEOUT_MS,
384
938
  });
939
+ }
385
940
 
386
- state.runnerJobId = job.id?.toString();
941
+ return RunnerJobService.enqueueAiCommand({
942
+ projectId: suggestion.projectId!,
943
+ aiRunId: suggestion.aiRunId!,
944
+ autoRemediationSuggestionId: suggestion.id!,
945
+ stepId: data.stepId,
946
+ stepType: command.stepType,
947
+ targetAgentId: new ObjectID(command.runnerId),
948
+ command: data.commandText,
949
+ credentialId: command.credentialId,
950
+ timeoutInMs: command.timeoutInMs,
951
+ claimTimeoutInMs: AI_COMMAND_CLAIM_TIMEOUT_MS,
952
+ });
953
+ }
387
954
 
388
- const terminalJob: RunnerJob = await RunnerJobService.pollUntilTerminal({
389
- jobId: job.id!,
390
- claimTimeoutInMs: AI_COMMAND_CLAIM_TIMEOUT_MS,
391
- executionTimeoutInMs: command.timeoutInMs,
955
+ /*
956
+ * Why a kubectl command may NOT run on its cluster right now, or null.
957
+ *
958
+ * The plan froze the cluster, the Runner and the credential the cluster's
959
+ * AI page bound when the plan was composed. Before anything is enqueued
960
+ * the page is read again: remediation must still be enabled and ready
961
+ * (Disabled means "AI never runs a change on this cluster", whatever was
962
+ * approved earlier), and the bound Runner and credential must still be
963
+ * the ones on the plan — a re-bound cluster never receives a job meant
964
+ * for its previous access path. A rollback must ALSO be allowed
965
+ * unattended under the cluster's CURRENT mode and allowlist: nothing
966
+ * shows a rollback to a human when it fires.
967
+ *
968
+ * Fails closed: a status that cannot be read is a refusal. Bash/SSH
969
+ * commands have no cluster and pass through.
970
+ */
971
+ private static async getClusterExecutionRefusal(data: {
972
+ suggestion: AutoRemediationSuggestion;
973
+ command: AiRemediationCommand;
974
+ commandText: string;
975
+ purpose: "command" | "rollback";
976
+ }): Promise<string | null> {
977
+ const { suggestion, command } = data;
978
+
979
+ if (command.stepType !== RunbookStepType.Kubectl) {
980
+ return null;
981
+ }
982
+
983
+ const clusterLabel: string =
984
+ command.kubernetesClusterNameSnapshot ||
985
+ command.kubernetesClusterId ||
986
+ "(unknown)";
987
+
988
+ if (
989
+ !command.kubernetesClusterId ||
990
+ !ObjectID.isValidUUID(command.kubernetesClusterId) ||
991
+ !suggestion.projectId
992
+ ) {
993
+ return `the kubectl command names no valid cluster`;
994
+ }
995
+
996
+ let status: KubernetesClusterAiAccessStatus | null;
997
+
998
+ try {
999
+ status = await KubernetesClusterAiAccessService.getStatusForCluster({
1000
+ clusterId: new ObjectID(command.kubernetesClusterId),
1001
+ projectId: suggestion.projectId,
392
1002
  });
1003
+ } catch (error) {
1004
+ logger.error(
1005
+ `CommandPlanExecutor: could not read the AI access status of cluster ${command.kubernetesClusterId} before a ${data.purpose}: ${error}`,
1006
+ );
1007
+ return `could not confirm that cluster "${clusterLabel}" still allows AI remediation`;
1008
+ }
393
1009
 
394
- if (!isTerminalAgentJobStatus(terminalJob.status)) {
395
- throw new Error("Command job did not reach a terminal state.");
396
- }
1010
+ if (!status) {
1011
+ return `cluster "${clusterLabel}" no longer exists in this project`;
1012
+ }
397
1013
 
398
- const succeeded: boolean =
399
- terminalJob.status === RunnerJobStatus.Succeeded;
1014
+ if (!status.isRemediationReady) {
1015
+ const gap: KubernetesAiAccessGap | undefined = status.gaps.find(
1016
+ (candidate: KubernetesAiAccessGap) => {
1017
+ return candidate.blocks !== "investigation";
1018
+ },
1019
+ );
1020
+ return `cluster "${status.clusterName}" no longer allows AI remediation${
1021
+ gap ? ` (${gap.title})` : ""
1022
+ }`;
1023
+ }
400
1024
 
401
- state.status = succeeded
402
- ? AiRemediationCommandExecutionStatus.Succeeded
403
- : AiRemediationCommandExecutionStatus.Failed;
404
- state.completedAt = OneUptimeDate.getCurrentDate().toISOString();
405
- state.exitCode = terminalJob.exitCode;
406
- state.output = this.capOutput(terminalJob.output || "");
407
- if (!succeeded) {
408
- state.errorMessage =
409
- terminalJob.errorMessage ||
410
- `Command ended with status ${terminalJob.status}.`;
1025
+ if (!status.runner || status.runner.id !== command.runnerId) {
1026
+ return `cluster "${status.clusterName}" is no longer reached through Runner "${command.runnerNameSnapshot}", which this plan was composed for`;
1027
+ }
1028
+
1029
+ if (
1030
+ (status.credentialId || undefined) !== (command.credentialId || undefined)
1031
+ ) {
1032
+ return `cluster "${status.clusterName}" is no longer reached with the credential this plan was composed for`;
1033
+ }
1034
+
1035
+ /*
1036
+ * The Runner's write scope as it reports it now: a write it would
1037
+ * refuse is not enqueued (or, for a rollback, is left for a human with
1038
+ * the reason) — never sent only to be refused.
1039
+ */
1040
+ const scopeRefusal: string | null =
1041
+ RemediationCommandToolkit.getRunnerScopeRefusal({
1042
+ cluster: status,
1043
+ command: data.commandText,
1044
+ });
1045
+
1046
+ if (scopeRefusal) {
1047
+ // The callers end the sentence themselves.
1048
+ return `the cluster's Runner would refuse it: ${
1049
+ scopeRefusal.endsWith(".") ? scopeRefusal.slice(0, -1) : scopeRefusal
1050
+ }`;
1051
+ }
1052
+
1053
+ if (data.purpose === "rollback") {
1054
+ const verdict: KubectlAutoExecutionVerdict =
1055
+ KubectlPolicy.evaluateForAutoExecution({
1056
+ command: data.commandText,
1057
+ allowlistPatterns: status.kubectlAllowlist,
1058
+ bypassApproval:
1059
+ status.remediationMode ===
1060
+ KubernetesAiRemediationMode.BypassApproval,
1061
+ });
1062
+
1063
+ if (verdict.verdict !== AiRemediationCommandPolicyVerdict.AutoApproved) {
1064
+ return `cluster "${status.clusterName}" no longer allows this change unattended (its AI remediation mode is now ${status.remediationMode}): ${verdict.reason}`;
411
1065
  }
412
- } catch (error) {
413
- state.status = AiRemediationCommandExecutionStatus.Failed;
414
- state.completedAt = OneUptimeDate.getCurrentDate().toISOString();
415
- state.errorMessage =
416
- error instanceof Error ? error.message : String(error);
417
1066
  }
418
1067
 
419
- return state;
1068
+ return null;
1069
+ }
1070
+
1071
+ /*
1072
+ * The job a command's step ran under, when its record never got the job
1073
+ * id. The enqueue and the write that names the job are two steps; a pod
1074
+ * death or a failed write between them leaves a Pending record for a job
1075
+ * the Runner was nonetheless handed. (suggestion, stepId) is unique per
1076
+ * forward command — a plan executes at most once and a retried run never
1077
+ * re-executes — and per rollback; the newest row wins should that ever
1078
+ * change.
1079
+ */
1080
+ private static async findJobByStepId(data: {
1081
+ suggestion: AutoRemediationSuggestion;
1082
+ stepId: string;
1083
+ }): Promise<RunnerJob | null> {
1084
+ if (!data.suggestion.id) {
1085
+ return null;
1086
+ }
1087
+
1088
+ const jobs: Array<RunnerJob> = await RunnerJobService.findBy({
1089
+ query: {
1090
+ ...(data.suggestion.projectId
1091
+ ? { projectId: data.suggestion.projectId }
1092
+ : {}),
1093
+ autoRemediationSuggestionId: data.suggestion.id,
1094
+ origin: RunnerJobOrigin.AiRemediation,
1095
+ stepId: data.stepId,
1096
+ },
1097
+ select: RECONCILE_JOB_SELECT,
1098
+ sort: { createdAt: SortOrder.Descending },
1099
+ limit: 1,
1100
+ skip: 0,
1101
+ props: { isRoot: true },
1102
+ });
1103
+
1104
+ return jobs[0] || null;
1105
+ }
1106
+
1107
+ private static capForFeed(text: string): string {
1108
+ const collapsed: string = text.trim().replace(/\s+/g, " ");
1109
+ return collapsed.length > MAX_FEED_REASON_CHARS
1110
+ ? `${collapsed.slice(0, MAX_FEED_REASON_CHARS)}…`
1111
+ : collapsed;
1112
+ }
1113
+
1114
+ /*
1115
+ * Execution-time policy floor per lane: the bash denylist for Bash/SSH,
1116
+ * the Denied tier for Kubectl. Null when the command may run.
1117
+ */
1118
+ private static getDenyReason(data: {
1119
+ stepType: RunbookStepType;
1120
+ command: string;
1121
+ }): string | null {
1122
+ if (data.stepType === RunbookStepType.Kubectl) {
1123
+ const policy: KubectlPolicyResult = KubectlPolicy.evaluateCommand(
1124
+ data.command,
1125
+ );
1126
+ return policy.tier === KubectlCommandTier.Denied ? policy.reason : null;
1127
+ }
1128
+
1129
+ return CommandPolicy.getDenyReason(data.command);
420
1130
  }
421
1131
 
422
1132
  /*
423
1133
  * Settle commands left in Pending/Running by reading their RunnerJob. A
424
1134
  * job that reached Succeeded really ran, so the command becomes Succeeded
425
- * (and therefore rollback-eligible); any other terminal state, or a
426
- * missing job, becomes Failed. Jobs still in flight are left alone.
1135
+ * (and therefore rollback-eligible); any other terminal state becomes
1136
+ * Failed. A job still in flight is WAITED for when `waitForInFlight` is
1137
+ * set (the rollback arm: the change is about to land, and its undo must
1138
+ * not be skipped); otherwise it is left alone.
1139
+ *
1140
+ * The job is found by the id on the record, or — when the record never
1141
+ * got one, because the pod died or the write failed between the enqueue
1142
+ * and the persist that names the job — by the (suggestion, stepId) pair
1143
+ * the lane enqueued it under. A command with neither never reached the
1144
+ * Runner and is left alone.
427
1145
  */
428
1146
  private static async reconcilePendingExecutions(
429
1147
  suggestion: AutoRemediationSuggestion,
430
1148
  plan: AiRemediationCommandPlan,
1149
+ options: { waitForInFlight: boolean },
431
1150
  ): Promise<void> {
432
1151
  let changed: boolean = false;
433
1152
 
434
1153
  for (const command of plan.commands) {
435
- const status: AiRemediationCommandExecutionStatus | undefined =
436
- command.execution?.status;
1154
+ const execution: AiRemediationCommandExecutionState | undefined =
1155
+ command.execution;
1156
+
1157
+ if (!execution) {
1158
+ continue;
1159
+ }
437
1160
 
438
1161
  const isUnsettled: boolean =
439
- status === AiRemediationCommandExecutionStatus.Pending ||
440
- status === AiRemediationCommandExecutionStatus.Running;
1162
+ execution.status === AiRemediationCommandExecutionStatus.Pending ||
1163
+ execution.status === AiRemediationCommandExecutionStatus.Running;
441
1164
 
442
- if (!isUnsettled || !command.execution?.runnerJobId) {
1165
+ if (!isUnsettled) {
443
1166
  continue;
444
1167
  }
445
1168
 
446
1169
  try {
447
- const job: RunnerJob | null = await RunnerJobService.findOneById({
448
- id: new ObjectID(command.execution.runnerJobId),
449
- select: {
450
- _id: true,
451
- status: true,
452
- output: true,
453
- exitCode: true,
454
- errorMessage: true,
455
- },
456
- props: { isRoot: true },
457
- });
1170
+ let job: RunnerJob | null = execution.runnerJobId
1171
+ ? await RunnerJobService.findOneById({
1172
+ id: new ObjectID(execution.runnerJobId),
1173
+ select: RECONCILE_JOB_SELECT,
1174
+ props: { isRoot: true },
1175
+ })
1176
+ : await this.findJobByStepId({
1177
+ suggestion,
1178
+ stepId: getForwardCommandStepId(command),
1179
+ });
458
1180
 
459
- if (!job || !isTerminalAgentJobStatus(job.status)) {
1181
+ if (!job) {
460
1182
  continue;
461
1183
  }
462
1184
 
1185
+ // Recovered by stepId: name the job so the record stays resolvable.
1186
+ if (!execution.runnerJobId && job.id) {
1187
+ execution.runnerJobId = job.id.toString();
1188
+ changed = true;
1189
+ }
1190
+
1191
+ if (!isTerminalAgentJobStatus(job.status)) {
1192
+ if (!options.waitForInFlight || !job.id) {
1193
+ continue;
1194
+ }
1195
+
1196
+ // Say where the rollback stands before a wait that can take minutes.
1197
+ if (changed) {
1198
+ await this.persistPlan(suggestion, plan, "rollback");
1199
+ changed = false;
1200
+ }
1201
+ plan.rollbackHeartbeatAt =
1202
+ OneUptimeDate.getCurrentDate().toISOString();
1203
+ await this.persistPlan(suggestion, plan, "rollback");
1204
+
1205
+ job = await RunnerJobService.pollUntilTerminal({
1206
+ jobId: job.id,
1207
+ claimTimeoutInMs: AI_COMMAND_CLAIM_TIMEOUT_MS,
1208
+ executionTimeoutInMs: command.timeoutInMs,
1209
+ });
1210
+
1211
+ if (!isTerminalAgentJobStatus(job.status)) {
1212
+ continue;
1213
+ }
1214
+ }
1215
+
463
1216
  const succeeded: boolean = job.status === RunnerJobStatus.Succeeded;
464
1217
 
465
- command.execution.status = succeeded
1218
+ execution.status = succeeded
466
1219
  ? AiRemediationCommandExecutionStatus.Succeeded
467
1220
  : AiRemediationCommandExecutionStatus.Failed;
468
- command.execution.exitCode = job.exitCode;
469
- command.execution.output = this.capOutput(job.output || "");
470
- command.execution.errorMessage = succeeded
1221
+ execution.exitCode = job.exitCode;
1222
+ execution.output = this.capOutput(job.output || "");
1223
+ execution.errorMessage = succeeded
471
1224
  ? undefined
472
1225
  : job.errorMessage ||
473
1226
  `Command ended with status ${job.status} (recovered from the job record).`;
@@ -480,10 +1233,231 @@ export default class CommandPlanExecutor {
480
1233
  }
481
1234
 
482
1235
  if (changed) {
483
- await this.persistPlan(suggestion, plan);
1236
+ await this.persistPlan(suggestion, plan, "rollback");
1237
+ }
1238
+ }
1239
+
1240
+ /*
1241
+ * A rollback resumed after a Worker restart: settle the undos the
1242
+ * interrupted attempt had already started. One with a job — on the record
1243
+ * or found under its rollback step id — is settled from that job
1244
+ * (waiting for it when it is still running): it is never run a second
1245
+ * time, because running an undo twice (a second `rollout undo`) can put
1246
+ * the bad change right back. One that never reached a Runner is cleared,
1247
+ * so it runs now. One whose job cannot be looked up stays unsettled, and
1248
+ * the rollback is not reported complete.
1249
+ */
1250
+ private static async reconcilePendingRollbacks(
1251
+ suggestion: AutoRemediationSuggestion,
1252
+ plan: AiRemediationCommandPlan,
1253
+ ): Promise<void> {
1254
+ let changed: boolean = false;
1255
+
1256
+ for (const command of plan.commands) {
1257
+ const rollback: AiRemediationCommandExecutionState | undefined =
1258
+ command.rollbackExecution;
1259
+
1260
+ if (!rollback || isSettledCommandExecutionStatus(rollback.status)) {
1261
+ continue;
1262
+ }
1263
+
1264
+ try {
1265
+ let job: RunnerJob | null = rollback.runnerJobId
1266
+ ? await RunnerJobService.findOneById({
1267
+ id: new ObjectID(rollback.runnerJobId),
1268
+ select: RECONCILE_JOB_SELECT,
1269
+ props: { isRoot: true },
1270
+ })
1271
+ : await this.findJobByStepId({
1272
+ suggestion,
1273
+ stepId: getRollbackCommandStepId(command),
1274
+ });
1275
+
1276
+ if (!job) {
1277
+ // Never reached a Runner: run it now.
1278
+ command.rollbackExecution = undefined;
1279
+ changed = true;
1280
+ continue;
1281
+ }
1282
+
1283
+ if (!rollback.runnerJobId && job.id) {
1284
+ rollback.runnerJobId = job.id.toString();
1285
+ changed = true;
1286
+ }
1287
+
1288
+ if (!isTerminalAgentJobStatus(job.status) && job.id) {
1289
+ plan.rollbackHeartbeatAt =
1290
+ OneUptimeDate.getCurrentDate().toISOString();
1291
+ await this.persistPlan(suggestion, plan, "rollback");
1292
+
1293
+ job = await RunnerJobService.pollUntilTerminal({
1294
+ jobId: job.id,
1295
+ claimTimeoutInMs: AI_COMMAND_CLAIM_TIMEOUT_MS,
1296
+ executionTimeoutInMs: command.timeoutInMs,
1297
+ });
1298
+ }
1299
+
1300
+ if (isTerminalAgentJobStatus(job.status)) {
1301
+ this.settleFromJob(rollback, job, "Rollback");
1302
+ changed = true;
1303
+ }
1304
+ } catch (error) {
1305
+ logger.error(
1306
+ `CommandPlanExecutor: could not reconcile the rollback of command ${command.sequence} of suggestion ${suggestion.id?.toString()}: ${error}`,
1307
+ );
1308
+ }
1309
+ }
1310
+
1311
+ if (changed) {
1312
+ await this.persistPlan(suggestion, plan, "rollback");
484
1313
  }
485
1314
  }
486
1315
 
1316
+ /*
1317
+ * The kubectl commands recorded as Failed that may nonetheless have
1318
+ * changed the cluster, read from their jobs the way KubectlJobRunner
1319
+ * reads every kubectl job (KubectlJobRunner.getRunStateOfJobRow). Only a
1320
+ * command that carries a rollbackCommand and has no rollback yet is read
1321
+ * — nothing else has an undo to owe.
1322
+ *
1323
+ * - A job that Succeeded after all (the wait for it broke, and the
1324
+ * record said Failed) is settled from the job: it becomes Succeeded,
1325
+ * and so a rollback target like any other.
1326
+ * - A job whose run is Unknown — a Runner took it and no result came
1327
+ * back, it is still in flight, or its row cannot be read — is
1328
+ * returned: whether it changed the cluster is not known. So is one
1329
+ * where kubectl ran but never finished (the Runner killed it at its
1330
+ * timeout, or it left output but no exit code): it may have applied
1331
+ * its change before it was stopped.
1332
+ * - A job that certainly never ran, or whose kubectl finished and
1333
+ * reported a failure (an exit code), owes no undo, as before.
1334
+ */
1335
+ private static async reconcileFailedKubectlExecutions(
1336
+ suggestion: AutoRemediationSuggestion,
1337
+ plan: AiRemediationCommandPlan,
1338
+ ): Promise<Array<AiRemediationCommand>> {
1339
+ const mayHaveRun: Array<AiRemediationCommand> = [];
1340
+ let changed: boolean = false;
1341
+
1342
+ for (const command of plan.commands) {
1343
+ const execution: AiRemediationCommandExecutionState | undefined =
1344
+ command.execution;
1345
+
1346
+ if (
1347
+ command.stepType !== RunbookStepType.Kubectl ||
1348
+ !execution ||
1349
+ execution.status !== AiRemediationCommandExecutionStatus.Failed ||
1350
+ !execution.runnerJobId ||
1351
+ !command.rollbackCommand ||
1352
+ command.rollbackExecution
1353
+ ) {
1354
+ continue;
1355
+ }
1356
+
1357
+ let runState: KubectlRunState = KubectlRunState.Unknown;
1358
+ let job: RunnerJob | null = null;
1359
+
1360
+ try {
1361
+ job = ObjectID.isValidUUID(execution.runnerJobId)
1362
+ ? await RunnerJobService.findOneById({
1363
+ id: new ObjectID(execution.runnerJobId),
1364
+ select: RUN_STATE_JOB_SELECT,
1365
+ props: { isRoot: true },
1366
+ })
1367
+ : null;
1368
+
1369
+ if (job) {
1370
+ runState = KubectlJobRunner.getRunStateOfJobRow(job);
1371
+ }
1372
+ } catch (error) {
1373
+ logger.error(
1374
+ `CommandPlanExecutor: could not read whether command ${command.sequence} of suggestion ${suggestion.id?.toString()} ran: ${error}`,
1375
+ );
1376
+ }
1377
+
1378
+ if (job && job.status === RunnerJobStatus.Succeeded) {
1379
+ this.settleFromJob(execution, job, "Command");
1380
+ changed = true;
1381
+ continue;
1382
+ }
1383
+
1384
+ const kubectlFinished: boolean =
1385
+ runState === KubectlRunState.Ran && typeof job?.exitCode === "number";
1386
+
1387
+ if (runState !== KubectlRunState.NotRun && !kubectlFinished) {
1388
+ mayHaveRun.push(command);
1389
+ }
1390
+ }
1391
+
1392
+ if (changed) {
1393
+ await this.persistPlan(suggestion, plan, "rollback");
1394
+ }
1395
+
1396
+ return mayHaveRun;
1397
+ }
1398
+
1399
+ private static describeRollbackOutcome(
1400
+ plan: AiRemediationCommandPlan | null,
1401
+ ): CommandPlanRollbackOutcome {
1402
+ let rolledBack: number = 0;
1403
+ let failed: number = 0;
1404
+ let leftForHuman: number = 0;
1405
+
1406
+ for (const command of plan?.commands || []) {
1407
+ switch (command.rollbackExecution?.status) {
1408
+ case AiRemediationCommandExecutionStatus.Succeeded:
1409
+ rolledBack += 1;
1410
+ break;
1411
+ case AiRemediationCommandExecutionStatus.Failed:
1412
+ failed += 1;
1413
+ break;
1414
+ case AiRemediationCommandExecutionStatus.Skipped:
1415
+ leftForHuman += 1;
1416
+ break;
1417
+ default:
1418
+ break;
1419
+ }
1420
+ }
1421
+
1422
+ const storedStatus: AiRemediationRollbackStatus | undefined =
1423
+ plan?.rollbackStatus &&
1424
+ plan.rollbackStatus !== AiRemediationRollbackStatus.NotAttempted
1425
+ ? plan.rollbackStatus
1426
+ : undefined;
1427
+
1428
+ /*
1429
+ * A status and its counts never contradict each other: an undo that
1430
+ * failed or was left for a human makes a settled rollback Failed, even
1431
+ * one recorded "completed" before resumed rollbacks carried earlier
1432
+ * failures forward.
1433
+ */
1434
+ const rollbackStatus: AiRemediationRollbackStatus | undefined =
1435
+ storedStatus !== undefined && failed + leftForHuman > 0
1436
+ ? AiRemediationRollbackStatus.Failed
1437
+ : storedStatus;
1438
+
1439
+ let summary: string;
1440
+
1441
+ switch (rollbackStatus) {
1442
+ case AiRemediationRollbackStatus.Completed:
1443
+ summary = `Rollback completed: ${rolledBack} command(s) undone.`;
1444
+ break;
1445
+ case AiRemediationRollbackStatus.NotApplicable:
1446
+ summary =
1447
+ "Nothing was rolled back: no executed command carried a rollback command.";
1448
+ break;
1449
+ case AiRemediationRollbackStatus.Failed:
1450
+ summary = `Rollback did NOT fully complete: ${rolledBack} command(s) undone, ${failed} rollback(s) failed, ${leftForHuman} left for a human to undo — the change may still be applied.`;
1451
+ break;
1452
+ default:
1453
+ summary =
1454
+ "The rollback did not finish; whatever it did not undo may still be applied.";
1455
+ break;
1456
+ }
1457
+
1458
+ return { rollbackStatus, rolledBack, failed, leftForHuman, summary };
1459
+ }
1460
+
487
1461
  /*
488
1462
  * Whether the suggestion is still an Approved remediation whose
489
1463
  * verification window is open. False means someone (the verifier, or a
@@ -540,15 +1514,90 @@ export default class CommandPlanExecutor {
540
1514
  }
541
1515
  }
542
1516
 
1517
+ /*
1518
+ * The plan the rollback arm works from: the stored one, read under the
1519
+ * plan lock. Falls back to the copy the caller holds when the stored plan
1520
+ * cannot be read.
1521
+ */
1522
+ private static async readPlanForRollback(
1523
+ suggestion: AutoRemediationSuggestion,
1524
+ ): Promise<AiRemediationCommandPlan | null> {
1525
+ const mutex: SemaphoreMutex | null = suggestion.id
1526
+ ? await this.lockPlan(suggestion.id)
1527
+ : null;
1528
+
1529
+ try {
1530
+ const stored: AiRemediationCommandPlan | null = suggestion.id
1531
+ ? await this.readStoredPlan(suggestion.id)
1532
+ : null;
1533
+
1534
+ return (
1535
+ stored || AiRemediationCommandPlanUtil.parse(suggestion.commandPlan)
1536
+ );
1537
+ } finally {
1538
+ await this.unlockPlan(mutex);
1539
+ }
1540
+ }
1541
+
1542
+ // The plan as stored right now, or null when it cannot be read.
1543
+ private static async readStoredPlan(
1544
+ suggestionId: ObjectID,
1545
+ ): Promise<AiRemediationCommandPlan | null> {
1546
+ try {
1547
+ /*
1548
+ * findOneBy, not findOneById: this is the column read for a merge,
1549
+ * not a status re-check, and it must not be mistaken for one.
1550
+ */
1551
+ const stored: AutoRemediationSuggestion | null =
1552
+ await AutoRemediationSuggestionService.findOneBy({
1553
+ query: { _id: suggestionId.toString() },
1554
+ select: { _id: true, commandPlan: true },
1555
+ props: { isRoot: true },
1556
+ });
1557
+
1558
+ return AiRemediationCommandPlanUtil.parse(stored?.commandPlan);
1559
+ } catch (error) {
1560
+ logger.warn(
1561
+ `CommandPlanExecutor: could not read the stored plan of suggestion ${suggestionId.toString()} to merge into; writing this writer's copy as is: ${error}`,
1562
+ );
1563
+ return null;
1564
+ }
1565
+ }
1566
+
1567
+ /*
1568
+ * Persist the plan without erasing what the OTHER arm wrote. The stored
1569
+ * plan is re-read (under the plan lock) and only this writer's fields are
1570
+ * taken from its copy: the executor owns forward executions and the
1571
+ * plan's execution lifecycle; the rollback arm owns rollbacks,
1572
+ * rollbackStatus and the rollback heartbeat. A forward execution the
1573
+ * rollback arm settled from its job is kept over a stale unsettled copy
1574
+ * either way. When the stored plan cannot be read, this writer's copy is
1575
+ * written as is — the record must still land.
1576
+ */
543
1577
  private static async persistPlan(
544
1578
  suggestion: AutoRemediationSuggestion,
545
1579
  plan: AiRemediationCommandPlan,
1580
+ writer: PlanWriter,
1581
+ options?: { planLocked?: boolean | undefined },
546
1582
  ): Promise<void> {
1583
+ const mutex: SemaphoreMutex | null =
1584
+ options?.planLocked || !suggestion.id
1585
+ ? null
1586
+ : await this.lockPlan(suggestion.id);
1587
+
547
1588
  try {
1589
+ const stored: AiRemediationCommandPlan | null = suggestion.id
1590
+ ? await this.readStoredPlan(suggestion.id)
1591
+ : null;
1592
+
1593
+ const toWrite: AiRemediationCommandPlan = stored
1594
+ ? this.mergePlans({ mine: plan, stored, writer })
1595
+ : plan;
1596
+
548
1597
  await AutoRemediationSuggestionService.updateOneById({
549
1598
  id: suggestion.id!,
550
1599
  data: {
551
- commandPlan: AiRemediationCommandPlanUtil.toJSON(plan),
1600
+ commandPlan: AiRemediationCommandPlanUtil.toJSON(toWrite),
552
1601
  } as never,
553
1602
  props: { isRoot: true },
554
1603
  });
@@ -556,6 +1605,148 @@ export default class CommandPlanExecutor {
556
1605
  logger.error(
557
1606
  `CommandPlanExecutor: failed to persist plan progress for suggestion ${suggestion.id?.toString()}: ${error}`,
558
1607
  );
1608
+ } finally {
1609
+ await this.unlockPlan(mutex);
1610
+ }
1611
+ }
1612
+
1613
+ private static mergePlans(data: {
1614
+ mine: AiRemediationCommandPlan;
1615
+ stored: AiRemediationCommandPlan;
1616
+ writer: PlanWriter;
1617
+ }): AiRemediationCommandPlan {
1618
+ const { mine, stored } = data;
1619
+
1620
+ if (data.writer === "executor") {
1621
+ const storedBySequence: Map<number, AiRemediationCommand> = new Map<
1622
+ number,
1623
+ AiRemediationCommand
1624
+ >(
1625
+ stored.commands.map((command: AiRemediationCommand) => {
1626
+ return [command.sequence, command];
1627
+ }),
1628
+ );
1629
+
1630
+ return {
1631
+ ...mine,
1632
+ commands: mine.commands.map((command: AiRemediationCommand) => {
1633
+ const storedCommand: AiRemediationCommand | undefined =
1634
+ storedBySequence.get(command.sequence);
1635
+
1636
+ return {
1637
+ ...command,
1638
+ execution: this.pickExecution(
1639
+ command.execution,
1640
+ storedCommand?.execution,
1641
+ ),
1642
+ rollbackExecution: storedCommand?.rollbackExecution,
1643
+ };
1644
+ }),
1645
+ rollbackStatus: stored.rollbackStatus,
1646
+ rollbackHeartbeatAt: stored.rollbackHeartbeatAt,
1647
+ };
1648
+ }
1649
+
1650
+ const mineBySequence: Map<number, AiRemediationCommand> = new Map<
1651
+ number,
1652
+ AiRemediationCommand
1653
+ >(
1654
+ mine.commands.map((command: AiRemediationCommand) => {
1655
+ return [command.sequence, command];
1656
+ }),
1657
+ );
1658
+
1659
+ return {
1660
+ ...stored,
1661
+ commands: stored.commands.map((storedCommand: AiRemediationCommand) => {
1662
+ const command: AiRemediationCommand | undefined = mineBySequence.get(
1663
+ storedCommand.sequence,
1664
+ );
1665
+
1666
+ if (!command) {
1667
+ return storedCommand;
1668
+ }
1669
+
1670
+ return {
1671
+ ...storedCommand,
1672
+ execution: this.pickExecution(
1673
+ command.execution,
1674
+ storedCommand.execution,
1675
+ ),
1676
+ rollbackExecution: command.rollbackExecution,
1677
+ };
1678
+ }),
1679
+ rollbackStatus: mine.rollbackStatus ?? stored.rollbackStatus,
1680
+ rollbackHeartbeatAt:
1681
+ mine.rollbackHeartbeatAt ?? stored.rollbackHeartbeatAt,
1682
+ };
1683
+ }
1684
+
1685
+ /*
1686
+ * The more settled of two records of one command's forward execution: a
1687
+ * record with an outcome beats one still waiting for it; with both
1688
+ * settled (or both not) the writer's own wins, keeping any job id either
1689
+ * of them knows.
1690
+ */
1691
+ private static pickExecution(
1692
+ mine: AiRemediationCommandExecutionState | undefined,
1693
+ stored: AiRemediationCommandExecutionState | undefined,
1694
+ ): AiRemediationCommandExecutionState | undefined {
1695
+ if (!mine) {
1696
+ return stored;
1697
+ }
1698
+
1699
+ if (!stored) {
1700
+ return mine;
1701
+ }
1702
+
1703
+ if (isSettledCommandExecutionStatus(mine.status)) {
1704
+ return mine;
1705
+ }
1706
+
1707
+ if (isSettledCommandExecutionStatus(stored.status)) {
1708
+ return stored;
1709
+ }
1710
+
1711
+ return mine.runnerJobId || !stored.runnerJobId
1712
+ ? mine
1713
+ : { ...mine, runnerJobId: stored.runnerJobId };
1714
+ }
1715
+
1716
+ /*
1717
+ * The per-plan lock, or null when it cannot be had (Redis unavailable):
1718
+ * the plan's writes then proceed unserialized rather than not at all — a
1719
+ * remediation must never be stranded half-recorded for want of a lock.
1720
+ */
1721
+ private static async lockPlan(
1722
+ suggestionId: ObjectID,
1723
+ ): Promise<SemaphoreMutex | null> {
1724
+ try {
1725
+ return await Semaphore.lock({
1726
+ key: suggestionId.toString(),
1727
+ namespace: PLAN_LOCK_NAMESPACE,
1728
+ lockTimeout: PLAN_LOCK_TIMEOUT_MS,
1729
+ acquireTimeout: PLAN_LOCK_ACQUIRE_TIMEOUT_MS,
1730
+ });
1731
+ } catch (error) {
1732
+ logger.warn(
1733
+ `CommandPlanExecutor: could not take the plan lock of suggestion ${suggestionId.toString()}; continuing unserialized: ${error}`,
1734
+ );
1735
+ return null;
1736
+ }
1737
+ }
1738
+
1739
+ private static async unlockPlan(mutex: SemaphoreMutex | null): Promise<void> {
1740
+ if (!mutex) {
1741
+ return;
1742
+ }
1743
+
1744
+ try {
1745
+ await Semaphore.release(mutex);
1746
+ } catch (error) {
1747
+ logger.error(
1748
+ `CommandPlanExecutor: failed to release the plan lock: ${error}`,
1749
+ );
559
1750
  }
560
1751
  }
561
1752