@oneuptime/common 11.5.4 → 11.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/AIAgentTaskPullRequest.ts +56 -45
- package/Models/DatabaseModels/AIInsight.ts +748 -0
- package/Models/DatabaseModels/AIRun.ts +262 -0
- package/Models/DatabaseModels/CodeRepository.ts +42 -0
- package/Models/DatabaseModels/GlobalConfig.ts +105 -0
- package/Models/DatabaseModels/Index.ts +4 -8
- package/Models/DatabaseModels/InstanceHealthLog.ts +277 -0
- package/Models/DatabaseModels/LlmLog.ts +33 -0
- package/Models/DatabaseModels/Project.ts +123 -6
- package/Models/DatabaseModels/Runbook.ts +1 -1
- package/Server/API/AIAgentDataAPI.ts +802 -140
- package/Server/API/AIAgentTaskAPI.ts +132 -69
- package/Server/API/AIAgentTaskLogAPI.ts +65 -41
- package/Server/API/AIAgentTaskPullRequestAPI.ts +41 -0
- package/Server/API/AIInsightAPI.ts +220 -0
- package/Server/API/AIInvestigationAPI.ts +363 -1
- package/Server/API/CodeFixRunAPI.ts +251 -0
- package/Server/API/GitHubAPI.ts +246 -3
- package/Server/API/TelemetryExceptionAPI.ts +193 -48
- package/Server/EnvironmentConfig.ts +1 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783943300000-DropServiceCodeRepository.ts +26 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783947444597-CodeFixRunsOnAIRun.ts +73 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783950962813-AddCodeFixTaskType.ts +29 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783954040984-AddInstrumentationFixTasksFlag.ts +31 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783957160597-MigrationName.ts +51 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783958542237-AddTaskContextToAIRun.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783965945957-AddInvestigationVerdictAndGrade.ts +47 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783970619301-AddFixGuardrailColumns.ts +53 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783970619302-DropLegacyAIAgentTaskTables.ts +31 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783977781677-AddCiStatusToFixPullRequests.ts +37 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783990000000-AddCompletionTokensToLlmLog.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784010274993-AddSentinelInsight.ts +75 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784010274994-AddSentinelInsightFlags.ts +37 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784030612266-RenameSentinelToAI.ts +203 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784033837629-MigrationName.ts +163 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +30 -0
- package/Server/Infrastructure/Queue.ts +82 -4
- package/Server/Infrastructure/Semaphore.ts +62 -1
- package/Server/Services/AIAgentService.ts +32 -0
- package/Server/Services/AIAgentTaskPullRequestService.ts +117 -0
- package/Server/Services/AIInsightService.ts +174 -0
- package/Server/Services/AIRunEventService.ts +63 -0
- package/Server/Services/AIRunService.ts +259 -1
- package/Server/Services/AIService.ts +249 -7
- package/Server/Services/AlertService.ts +4 -4
- package/Server/Services/CodeRepositoryService.ts +209 -0
- package/Server/Services/GlobalConfigService.ts +148 -0
- package/Server/Services/IncidentService.ts +183 -38
- package/Server/Services/IncidentStateTimelineService.ts +24 -4
- package/Server/Services/Index.ts +4 -4
- package/Server/Services/{AIAgentTaskLogService.ts → InstanceHealthLogService.ts} +1 -1
- package/Server/Services/LlmLogService.ts +27 -0
- package/Server/Services/LlmProviderService.ts +108 -0
- package/Server/Services/MetricBaselineService.ts +123 -0
- package/Server/Services/MonitorService.ts +15 -0
- package/Server/Services/TelemetryExceptionService.ts +323 -116
- package/Server/Services/TraceAggregationService.ts +176 -0
- package/Server/Types/Workflow/ComponentCode.ts +6 -0
- package/Server/Types/Workflow/Components/AI/GenerateText.ts +326 -0
- package/Server/Types/Workflow/Components/Index.ts +2 -0
- package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +1 -1
- package/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.ts +71 -0
- package/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.ts +243 -0
- package/Server/Utils/AI/CodeFix/CodeFixRunQueue.ts +131 -0
- package/Server/Utils/AI/CodeFix/FixRunBudget.ts +148 -0
- package/Server/Utils/AI/CodeFix/OpenPullRequestCap.ts +117 -0
- package/Server/Utils/AI/Eval/EvalCorpus.ts +369 -0
- package/Server/Utils/AI/Eval/EvalScores.ts +172 -0
- package/Server/Utils/AI/Eval/ReplayInvestigation.ts +87 -0
- package/Server/Utils/AI/PerfEvidence/SpanTreeAnalyzer.ts +676 -0
- package/Server/Utils/AI/{Sentinel/SentinelInvestigationEngine.ts → SRE/AIInvestigationEngine.ts} +58 -37
- package/Server/Utils/AI/{Sentinel/SentinelMemory.ts → SRE/AIMemory.ts} +3 -3
- package/Server/Utils/AI/{Sentinel → SRE}/AlertInvestigationRunner.ts +50 -20
- package/Server/Utils/AI/SRE/ConfidenceSignal.ts +293 -0
- package/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.ts +115 -0
- package/Server/Utils/AI/SRE/FixPerformanceTaskTrigger.ts +223 -0
- package/Server/Utils/AI/{Sentinel → SRE}/IncidentInvestigationRunner.ts +71 -21
- package/Server/Utils/AI/{Sentinel → SRE}/IncidentPostmortemRunner.ts +8 -8
- package/Server/Utils/AI/SRE/Insights/Detectors/ErrorLogSpikeDetector.ts +364 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +317 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/Index.ts +27 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/MetricDriftDetector.ts +221 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +238 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/TraceLatencyRegressionDetector.ts +521 -0
- package/Server/Utils/AI/SRE/Insights/FixRouting.ts +242 -0
- package/Server/Utils/AI/SRE/Insights/InsightScanner.ts +290 -0
- package/Server/Utils/AI/SRE/Insights/InsightStore.ts +283 -0
- package/Server/Utils/AI/SRE/Insights/InsightTriageRunner.ts +242 -0
- package/Server/Utils/AI/SRE/Insights/Triage.ts +162 -0
- package/Server/Utils/AI/SRE/Insights/Types.ts +49 -0
- package/Server/Utils/AI/SRE/InstrumentationTaskTrigger.ts +212 -0
- package/Server/Utils/AI/SRE/InvestigationGrader.ts +282 -0
- package/Server/Utils/AI/{Sentinel → SRE}/InvestigationQueue.ts +114 -19
- package/Server/Utils/AI/{Sentinel → SRE}/README.md +4 -4
- package/Server/Utils/AI/SRE/SubjectCodeFixRun.ts +172 -0
- package/Server/Utils/AI/Toolbox/{SentinelActionTools.ts → AIActionTools.ts} +22 -1
- package/Server/Utils/AI/Toolbox/Index.ts +2 -2
- package/Server/Utils/AnalyticsDatabase/ClickhouseCapacity.ts +526 -0
- package/Server/Utils/CodeRepository/GitHub/GitHub.ts +319 -1
- package/Server/Utils/CodeRepository/ServiceRepoLinkSuggester.ts +221 -0
- package/Server/Utils/CodeRepository/StackTraceRepoResolver.ts +571 -0
- package/Server/Utils/Execute.ts +2 -1
- package/Server/Utils/LLM/LLMService.ts +160 -30
- package/Server/Utils/Monitor/Criteria/SqlMonitorCriteria.ts +152 -0
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +29 -0
- package/Tests/Server/Infrastructure/Queue.test.ts +227 -0
- package/Tests/Server/Infrastructure/SemaphorePermit.test.ts +159 -0
- package/Tests/Server/Services/AIAgentTaskPullRequestOutcomeStats.test.ts +154 -0
- package/Tests/Server/Services/AIInsightService.test.ts +324 -0
- package/Tests/Server/Services/AIRunCodeFixClaim.test.ts +401 -0
- package/Tests/Server/Services/AIRunHumanVerdict.test.ts +161 -0
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +95 -5
- package/Tests/Server/Services/AIServiceProjectAccess.test.ts +259 -0
- package/Tests/Server/Services/FixFromIncidentTaskTrigger.test.ts +249 -0
- package/Tests/Server/Services/FixPerformanceTaskTrigger.test.ts +274 -0
- package/Tests/Server/Services/GlobalConfigService.test.ts +80 -0
- package/Tests/Server/Services/IncidentInternalNoteAnnouncement.test.ts +1 -1
- package/Tests/Server/Services/InstrumentationTaskTrigger.test.ts +313 -0
- package/Tests/Server/Services/LlmProviderProjectOwned.test.ts +132 -0
- package/Tests/Server/Services/TelemetryExceptionAIFixReadiness.test.ts +405 -0
- package/Tests/Server/Services/TelemetryExceptionCodeFixRun.test.ts +322 -0
- package/Tests/Server/Services/TraceServiceLatencyProfile.test.ts +278 -0
- package/Tests/Server/Types/Workflow/Components/AIGenerateText.test.ts +722 -0
- package/Tests/Server/Utils/AI/{SentinelAlertGating.test.ts → AIAlertGating.test.ts} +19 -19
- package/Tests/Server/Utils/AI/AIConfidenceSignal.test.ts +362 -0
- package/Tests/Server/Utils/AI/{SentinelInvestigationQueue.test.ts → AIInvestigationQueue.test.ts} +19 -19
- package/Tests/Server/Utils/AI/CodeAgentWorkspaceGuard.test.ts +138 -0
- package/Tests/Server/Utils/AI/CodeFixAgentCompletion.test.ts +335 -0
- package/Tests/Server/Utils/AI/CodeFixRunQueue.test.ts +134 -0
- package/Tests/Server/Utils/AI/EvalCorpus.test.ts +209 -0
- package/Tests/Server/Utils/AI/EvalScores.test.ts +343 -0
- package/Tests/Server/Utils/AI/FixRunBudget.test.ts +218 -0
- package/Tests/Server/Utils/AI/Insights/ErrorLogSpikeDetector.test.ts +437 -0
- package/Tests/Server/Utils/AI/Insights/ExceptionSpikeDetector.test.ts +287 -0
- package/Tests/Server/Utils/AI/Insights/InsightDetectorsIndex.test.ts +34 -0
- package/Tests/Server/Utils/AI/Insights/InsightFixRouting.test.ts +572 -0
- package/Tests/Server/Utils/AI/Insights/InsightNeverThrowsHardening.test.ts +344 -0
- package/Tests/Server/Utils/AI/Insights/InsightScanner.test.ts +720 -0
- package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +467 -0
- package/Tests/Server/Utils/AI/Insights/InsightStoreHardening.test.ts +370 -0
- package/Tests/Server/Utils/AI/Insights/InsightTriage.test.ts +210 -0
- package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +221 -0
- package/Tests/Server/Utils/AI/Insights/InvestigationQueueInsightSubject.test.ts +328 -0
- package/Tests/Server/Utils/AI/Insights/MetricBaselineDrift.test.ts +182 -0
- package/Tests/Server/Utils/AI/Insights/MetricDriftDetector.test.ts +280 -0
- package/Tests/Server/Utils/AI/Insights/NewExceptionDetector.test.ts +259 -0
- package/Tests/Server/Utils/AI/Insights/TraceLatencyRegressionDetector.test.ts +770 -0
- package/Tests/Server/Utils/AI/InvestigationGrader.test.ts +343 -0
- package/Tests/Server/Utils/AI/LLMServiceRequestPolicy.test.ts +307 -0
- package/Tests/Server/Utils/AI/OpenPullRequestCap.test.ts +170 -0
- package/Tests/Server/Utils/AI/SpanTreeAnalyzer.test.ts +559 -0
- package/Tests/Server/Utils/AnalyticsDatabase/ClickhouseCapacity.test.ts +158 -0
- package/Tests/Server/Utils/GitHubCheckRunsConclusion.test.ts +100 -0
- package/Tests/Server/Utils/GitHubPullRequestStateMapping.test.ts +50 -0
- package/Tests/Server/Utils/Monitor/Criteria/SqlMonitorCriteria.test.ts +266 -0
- package/Tests/Server/Utils/ServiceRepoLinkSuggester.test.ts +167 -0
- package/Tests/Server/Utils/StackTraceRepoResolver.test.ts +527 -0
- package/Tests/Types/AI/AIChatPermissionMode.test.ts +112 -0
- package/Tests/Types/AI/CodeFixTaskType.test.ts +152 -0
- package/Tests/Types/AI/FixPullRequestCiStatus.test.ts +181 -0
- package/Tests/Types/Monitor/MonitorStepSqlMonitor.test.ts +110 -0
- package/Tests/Types/Monitor/SqlDatabaseType.test.ts +55 -0
- package/Tests/Types/Workflow/AIGenerateTextMetadata.test.ts +180 -0
- package/Tests/UI/Components/TableBulkCsvExport.test.tsx +168 -0
- package/Tests/UI/Utils/TableColumnsToCsv.test.ts +523 -0
- package/Types/AI/AIChatTypes.ts +8 -0
- package/Types/AI/AIInsightEvidence.ts +77 -0
- package/Types/AI/AIInsightHumanVerdict.ts +15 -0
- package/Types/AI/AIInsightSeverity.ts +13 -0
- package/Types/AI/AIInsightStatus.ts +38 -0
- package/Types/AI/AIInsightType.ts +41 -0
- package/Types/AI/AIRunAutoGrade.ts +18 -0
- package/Types/AI/AIRunEventType.ts +5 -0
- package/Types/AI/AIRunHumanVerdict.ts +17 -0
- package/Types/AI/AIRunType.ts +6 -0
- package/Types/AI/CodeFixTaskContext.ts +88 -0
- package/Types/AI/CodeFixTaskType.ts +132 -0
- package/Types/AI/FixPullRequestCiStatus.ts +132 -0
- package/Types/BaseDatabase/AggregationIntervalUtil.ts +67 -0
- package/Types/Incident/IncidentMetricType.ts +7 -0
- package/Types/Monitor/CriteriaFilter.ts +8 -0
- package/Types/Monitor/MonitorCriteriaInstance.ts +67 -0
- package/Types/Monitor/MonitorStep.ts +38 -0
- package/Types/Monitor/MonitorStepSqlMonitor.ts +141 -0
- package/Types/Monitor/MonitorType.ts +17 -0
- package/Types/Monitor/SqlDatabaseType.ts +49 -0
- package/Types/Monitor/SqlMonitor/SqlMonitorResponse.ts +28 -0
- package/Types/Permission.ts +11 -95
- package/Types/Probe/ProbeMonitorResponse.ts +2 -0
- package/Types/Runbook/RunbookStep.ts +26 -2
- package/Types/Runbook/RunbookStepType.ts +1 -0
- package/Types/Workflow/Component.ts +13 -0
- package/Types/Workflow/ComponentID.ts +1 -0
- package/Types/Workflow/Components/AI.ts +148 -0
- package/Types/Workflow/Components.ts +8 -0
- package/UI/Components/AI/GenerateFromAIModal.tsx +1 -1
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +11 -0
- package/UI/Components/MoreMenu/MoreMenu.tsx +1 -1
- package/UI/Components/Table/Table.tsx +38 -2
- package/UI/Utils/TableColumnsToCsv.ts +385 -0
- package/Utils/Monitor/MonitorMetricType.ts +1 -0
- package/build/dist/Models/DatabaseModels/AIAgentTaskPullRequest.js +59 -45
- package/build/dist/Models/DatabaseModels/AIAgentTaskPullRequest.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AIInsight.js +795 -0
- package/build/dist/Models/DatabaseModels/AIInsight.js.map +1 -0
- package/build/dist/Models/DatabaseModels/AIRun.js +274 -0
- package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CodeRepository.js +43 -0
- package/build/dist/Models/DatabaseModels/CodeRepository.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +110 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +4 -8
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/InstanceHealthLog.js +304 -0
- package/build/dist/Models/DatabaseModels/InstanceHealthLog.js.map +1 -0
- package/build/dist/Models/DatabaseModels/LlmLog.js +35 -0
- package/build/dist/Models/DatabaseModels/LlmLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +127 -6
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Runbook.js +1 -1
- package/build/dist/Models/DatabaseModels/Runbook.js.map +1 -1
- package/build/dist/Server/API/AIAgentDataAPI.js +568 -85
- package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
- package/build/dist/Server/API/AIAgentTaskAPI.js +100 -44
- package/build/dist/Server/API/AIAgentTaskAPI.js.map +1 -1
- package/build/dist/Server/API/AIAgentTaskLogAPI.js +49 -25
- package/build/dist/Server/API/AIAgentTaskLogAPI.js.map +1 -1
- package/build/dist/Server/API/AIAgentTaskPullRequestAPI.js +27 -0
- package/build/dist/Server/API/AIAgentTaskPullRequestAPI.js.map +1 -1
- package/build/dist/Server/API/AIInsightAPI.js +140 -0
- package/build/dist/Server/API/AIInsightAPI.js.map +1 -0
- package/build/dist/Server/API/AIInvestigationAPI.js +257 -1
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/CodeFixRunAPI.js +181 -0
- package/build/dist/Server/API/CodeFixRunAPI.js.map +1 -0
- package/build/dist/Server/API/GitHubAPI.js +164 -2
- package/build/dist/Server/API/GitHubAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryExceptionAPI.js +148 -43
- package/build/dist/Server/API/TelemetryExceptionAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +1 -1
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783943300000-DropServiceCodeRepository.js +23 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783943300000-DropServiceCodeRepository.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783947444597-CodeFixRunsOnAIRun.js +32 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783947444597-CodeFixRunsOnAIRun.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783950962813-AddCodeFixTaskType.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783950962813-AddCodeFixTaskType.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783954040984-AddInstrumentationFixTasksFlag.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783954040984-AddInstrumentationFixTasksFlag.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783957160597-MigrationName.js +24 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783957160597-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783958542237-AddTaskContextToAIRun.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783958542237-AddTaskContextToAIRun.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783965945957-AddInvestigationVerdictAndGrade.js +24 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783965945957-AddInvestigationVerdictAndGrade.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783970619301-AddFixGuardrailColumns.js +24 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783970619301-AddFixGuardrailColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783970619302-DropLegacyAIAgentTaskTables.js +24 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783970619302-DropLegacyAIAgentTaskTables.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783977781677-AddCiStatusToFixPullRequests.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783977781677-AddCiStatusToFixPullRequests.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783990000000-AddCompletionTokensToLlmLog.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783990000000-AddCompletionTokensToLlmLog.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784010274993-AddSentinelInsight.js +32 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784010274993-AddSentinelInsight.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784010274994-AddSentinelInsightFlags.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784010274994-AddSentinelInsightFlags.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784030612266-RenameSentinelToAI.js +125 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784030612266-RenameSentinelToAI.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784033837629-MigrationName.js +62 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784033837629-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +30 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +73 -3
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Infrastructure/Semaphore.js +44 -1
- package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
- package/build/dist/Server/Services/AIAgentService.js +29 -0
- package/build/dist/Server/Services/AIAgentService.js.map +1 -1
- package/build/dist/Server/Services/AIAgentTaskPullRequestService.js +78 -0
- package/build/dist/Server/Services/AIAgentTaskPullRequestService.js.map +1 -1
- package/build/dist/Server/Services/AIInsightService.js +164 -0
- package/build/dist/Server/Services/AIInsightService.js.map +1 -0
- package/build/dist/Server/Services/AIRunEventService.js +49 -0
- package/build/dist/Server/Services/AIRunEventService.js.map +1 -1
- package/build/dist/Server/Services/AIRunService.js +221 -0
- package/build/dist/Server/Services/AIRunService.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +198 -11
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +4 -4
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/CodeRepositoryService.js +180 -0
- package/build/dist/Server/Services/CodeRepositoryService.js.map +1 -1
- package/build/dist/Server/Services/GlobalConfigService.js +84 -0
- package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +149 -37
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +24 -4
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +4 -4
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/{AIAgentTaskLogService.js → InstanceHealthLogService.js} +2 -2
- package/build/dist/Server/Services/InstanceHealthLogService.js.map +1 -0
- package/build/dist/Server/Services/LlmLogService.js +23 -0
- package/build/dist/Server/Services/LlmLogService.js.map +1 -1
- package/build/dist/Server/Services/LlmProviderService.js +96 -0
- package/build/dist/Server/Services/LlmProviderService.js.map +1 -1
- package/build/dist/Server/Services/MetricBaselineService.js +65 -0
- package/build/dist/Server/Services/MetricBaselineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +9 -1
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryExceptionService.js +243 -82
- package/build/dist/Server/Services/TelemetryExceptionService.js.map +1 -1
- package/build/dist/Server/Services/TraceAggregationService.js +124 -0
- package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
- package/build/dist/Server/Types/Workflow/ComponentCode.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/AI/GenerateText.js +240 -0
- package/build/dist/Server/Types/Workflow/Components/AI/GenerateText.js.map +1 -0
- package/build/dist/Server/Types/Workflow/Components/Index.js +2 -0
- package/build/dist/Server/Types/Workflow/Components/Index.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.js +48 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeAgentWorkspaceGuard.js.map +1 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js +174 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js.map +1 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixRunQueue.js +126 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixRunQueue.js.map +1 -0
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +131 -0
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -0
- package/build/dist/Server/Utils/AI/CodeFix/OpenPullRequestCap.js +99 -0
- package/build/dist/Server/Utils/AI/CodeFix/OpenPullRequestCap.js.map +1 -0
- package/build/dist/Server/Utils/AI/Eval/EvalCorpus.js +245 -0
- package/build/dist/Server/Utils/AI/Eval/EvalCorpus.js.map +1 -0
- package/build/dist/Server/Utils/AI/Eval/EvalScores.js +83 -0
- package/build/dist/Server/Utils/AI/Eval/EvalScores.js.map +1 -0
- package/build/dist/Server/Utils/AI/Eval/ReplayInvestigation.js +12 -0
- package/build/dist/Server/Utils/AI/Eval/ReplayInvestigation.js.map +1 -0
- package/build/dist/Server/Utils/AI/PerfEvidence/SpanTreeAnalyzer.js +439 -0
- package/build/dist/Server/Utils/AI/PerfEvidence/SpanTreeAnalyzer.js.map +1 -0
- package/build/dist/Server/Utils/AI/{Sentinel/SentinelInvestigationEngine.js → SRE/AIInvestigationEngine.js} +47 -31
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -0
- package/build/dist/Server/Utils/AI/{Sentinel/SentinelMemory.js → SRE/AIMemory.js} +5 -5
- package/build/dist/Server/Utils/AI/SRE/AIMemory.js.map +1 -0
- package/build/dist/Server/Utils/AI/{Sentinel → SRE}/AlertInvestigationRunner.js +42 -19
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js +226 -0
- package/build/dist/Server/Utils/AI/SRE/ConfidenceSignal.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js +100 -0
- package/build/dist/Server/Utils/AI/SRE/FixFromIncidentTaskTrigger.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/FixPerformanceTaskTrigger.js +174 -0
- package/build/dist/Server/Utils/AI/SRE/FixPerformanceTaskTrigger.js.map +1 -0
- package/build/dist/Server/Utils/AI/{Sentinel → SRE}/IncidentInvestigationRunner.js +60 -22
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/{Sentinel → SRE}/IncidentPostmortemRunner.js +9 -9
- package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ErrorLogSpikeDetector.js +268 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ErrorLogSpikeDetector.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +226 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/Index.js +26 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/Index.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/MetricDriftDetector.js +159 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/MetricDriftDetector.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +177 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/TraceLatencyRegressionDetector.js +384 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/TraceLatencyRegressionDetector.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/FixRouting.js +180 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/FixRouting.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightScanner.js +269 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightScanner.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js +231 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js +206 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Triage.js +121 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Triage.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Types.js +2 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Types.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/InstrumentationTaskTrigger.js +148 -0
- package/build/dist/Server/Utils/AI/SRE/InstrumentationTaskTrigger.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +231 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js.map +1 -0
- package/build/dist/Server/Utils/AI/{Sentinel → SRE}/InvestigationQueue.js +102 -23
- package/build/dist/Server/Utils/AI/SRE/InvestigationQueue.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js +167 -0
- package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/{SentinelActionTools.js → AIActionTools.js} +18 -2
- package/build/dist/Server/Utils/AI/Toolbox/AIActionTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/Index.js +2 -2
- package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/ClickhouseCapacity.js +301 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/ClickhouseCapacity.js.map +1 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +240 -2
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/ServiceRepoLinkSuggester.js +127 -0
- package/build/dist/Server/Utils/CodeRepository/ServiceRepoLinkSuggester.js.map +1 -0
- package/build/dist/Server/Utils/CodeRepository/StackTraceRepoResolver.js +336 -0
- package/build/dist/Server/Utils/CodeRepository/StackTraceRepoResolver.js.map +1 -0
- package/build/dist/Server/Utils/Execute.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +125 -25
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/SqlMonitorCriteria.js +120 -0
- package/build/dist/Server/Utils/Monitor/Criteria/SqlMonitorCriteria.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +21 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Types/AI/AIInsightEvidence.js +2 -0
- package/build/dist/Types/AI/AIInsightEvidence.js.map +1 -0
- package/build/dist/Types/AI/AIInsightHumanVerdict.js +16 -0
- package/build/dist/Types/AI/AIInsightHumanVerdict.js.map +1 -0
- package/build/dist/Types/AI/AIInsightSeverity.js +14 -0
- package/build/dist/Types/AI/AIInsightSeverity.js.map +1 -0
- package/build/dist/Types/AI/AIInsightStatus.js +36 -0
- package/build/dist/Types/AI/AIInsightStatus.js.map +1 -0
- package/build/dist/Types/AI/AIInsightType.js +42 -0
- package/build/dist/Types/AI/AIInsightType.js.map +1 -0
- package/build/dist/Types/AI/AIRunAutoGrade.js +19 -0
- package/build/dist/Types/AI/AIRunAutoGrade.js.map +1 -0
- package/build/dist/Types/AI/AIRunEventType.js +5 -0
- package/build/dist/Types/AI/AIRunEventType.js.map +1 -1
- package/build/dist/Types/AI/AIRunHumanVerdict.js +18 -0
- package/build/dist/Types/AI/AIRunHumanVerdict.js.map +1 -0
- package/build/dist/Types/AI/AIRunType.js +6 -0
- package/build/dist/Types/AI/AIRunType.js.map +1 -1
- package/build/dist/Types/AI/CodeFixTaskContext.js +29 -0
- package/build/dist/Types/AI/CodeFixTaskContext.js.map +1 -0
- package/build/dist/Types/AI/CodeFixTaskType.js +122 -0
- package/build/dist/Types/AI/CodeFixTaskType.js.map +1 -0
- package/build/dist/Types/AI/FixPullRequestCiStatus.js +102 -0
- package/build/dist/Types/AI/FixPullRequestCiStatus.js.map +1 -0
- package/build/dist/Types/BaseDatabase/AggregationIntervalUtil.js +55 -0
- package/build/dist/Types/BaseDatabase/AggregationIntervalUtil.js.map +1 -1
- package/build/dist/Types/Incident/IncidentMetricType.js +7 -0
- package/build/dist/Types/Incident/IncidentMetricType.js.map +1 -1
- package/build/dist/Types/Monitor/CriteriaFilter.js +7 -0
- package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +62 -0
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +28 -0
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepSqlMonitor.js +93 -0
- package/build/dist/Types/Monitor/MonitorStepSqlMonitor.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorType.js +15 -0
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/Monitor/SqlDatabaseType.js +47 -0
- package/build/dist/Types/Monitor/SqlDatabaseType.js.map +1 -0
- package/build/dist/Types/Monitor/SqlMonitor/SqlMonitorResponse.js +2 -0
- package/build/dist/Types/Monitor/SqlMonitor/SqlMonitorResponse.js.map +1 -0
- package/build/dist/Types/Permission.js +10 -84
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Runbook/RunbookStepType.js +1 -0
- package/build/dist/Types/Runbook/RunbookStepType.js.map +1 -1
- package/build/dist/Types/Workflow/ComponentID.js +1 -0
- package/build/dist/Types/Workflow/ComponentID.js.map +1 -1
- package/build/dist/Types/Workflow/Components/AI.js +135 -0
- package/build/dist/Types/Workflow/Components/AI.js.map +1 -0
- package/build/dist/Types/Workflow/Components.js +7 -0
- package/build/dist/Types/Workflow/Components.js.map +1 -1
- package/build/dist/UI/Components/AI/GenerateFromAIModal.js +1 -1
- package/build/dist/UI/Components/AI/GenerateFromAIModal.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +9 -0
- package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +1 -1
- package/build/dist/UI/Components/Table/Table.js +28 -2
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Utils/TableColumnsToCsv.js +291 -0
- package/build/dist/UI/Utils/TableColumnsToCsv.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorMetricType.js +1 -0
- package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
- package/package.json +1 -1
- package/Models/DatabaseModels/AIAgentTask.ts +0 -660
- package/Models/DatabaseModels/AIAgentTaskLog.ts +0 -426
- package/Models/DatabaseModels/AIAgentTaskTelemetryException.ts +0 -399
- package/Models/DatabaseModels/ServiceCodeRepository.ts +0 -647
- package/Server/Services/AIAgentTaskService.ts +0 -240
- package/Server/Services/AIAgentTaskTelemetryExceptionService.ts +0 -39
- package/Server/Services/ServiceCodeRepositoryService.ts +0 -55
- package/Tests/Server/Services/AIAgentTaskServiceClaim.test.ts +0 -146
- package/Types/AI/AIAgentTaskMetadata.ts +0 -25
- package/Types/AI/AIAgentTaskType.ts +0 -40
- package/build/dist/Models/DatabaseModels/AIAgentTask.js +0 -691
- package/build/dist/Models/DatabaseModels/AIAgentTask.js.map +0 -1
- package/build/dist/Models/DatabaseModels/AIAgentTaskLog.js +0 -447
- package/build/dist/Models/DatabaseModels/AIAgentTaskLog.js.map +0 -1
- package/build/dist/Models/DatabaseModels/AIAgentTaskTelemetryException.js +0 -415
- package/build/dist/Models/DatabaseModels/AIAgentTaskTelemetryException.js.map +0 -1
- package/build/dist/Models/DatabaseModels/ServiceCodeRepository.js +0 -665
- package/build/dist/Models/DatabaseModels/ServiceCodeRepository.js.map +0 -1
- package/build/dist/Server/Services/AIAgentTaskLogService.js.map +0 -1
- package/build/dist/Server/Services/AIAgentTaskService.js +0 -217
- package/build/dist/Server/Services/AIAgentTaskService.js.map +0 -1
- package/build/dist/Server/Services/AIAgentTaskTelemetryExceptionService.js +0 -36
- package/build/dist/Server/Services/AIAgentTaskTelemetryExceptionService.js.map +0 -1
- package/build/dist/Server/Services/ServiceCodeRepositoryService.js +0 -54
- package/build/dist/Server/Services/ServiceCodeRepositoryService.js.map +0 -1
- package/build/dist/Server/Utils/AI/Sentinel/AlertInvestigationRunner.js.map +0 -1
- package/build/dist/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.js.map +0 -1
- package/build/dist/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.js.map +0 -1
- package/build/dist/Server/Utils/AI/Sentinel/InvestigationQueue.js.map +0 -1
- package/build/dist/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.js.map +0 -1
- package/build/dist/Server/Utils/AI/Sentinel/SentinelMemory.js.map +0 -1
- package/build/dist/Server/Utils/AI/Toolbox/SentinelActionTools.js.map +0 -1
- package/build/dist/Types/AI/AIAgentTaskMetadata.js +0 -6
- package/build/dist/Types/AI/AIAgentTaskMetadata.js.map +0 -1
- package/build/dist/Types/AI/AIAgentTaskType.js +0 -29
- package/build/dist/Types/AI/AIAgentTaskType.js.map +0 -1
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import FixRunBudget, {
|
|
2
|
+
DEFAULT_DAILY_FIX_RUN_LIMIT,
|
|
3
|
+
FixRunBudgetDecision,
|
|
4
|
+
} from "../../../../Server/Utils/AI/CodeFix/FixRunBudget";
|
|
5
|
+
import ProjectService from "../../../../Server/Services/ProjectService";
|
|
6
|
+
import AIRunService from "../../../../Server/Services/AIRunService";
|
|
7
|
+
import Project from "../../../../Models/DatabaseModels/Project";
|
|
8
|
+
import AIRunType from "../../../../Types/AI/AIRunType";
|
|
9
|
+
import BadDataException from "../../../../Types/Exception/BadDataException";
|
|
10
|
+
import ObjectID from "../../../../Types/ObjectID";
|
|
11
|
+
import PositiveNumber from "../../../../Types/PositiveNumber";
|
|
12
|
+
import { describe, expect, test, afterEach } from "@jest/globals";
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* The per-project daily fix-run budget (G11 guardrail): every CodeFix
|
|
16
|
+
* AIRun counts against Project.aiDailyFixTaskLimit per UTC day. Unlike the
|
|
17
|
+
* token budget, an UNSET limit is NOT unlimited — fix runs open pull
|
|
18
|
+
* requests on customer repositories, so the default cap applies. 0 pauses
|
|
19
|
+
* fix tasks entirely (the same kill-switch semantics as the token budget).
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
23
|
+
|
|
24
|
+
describe("FixRunBudget.evaluate (pure decision)", () => {
|
|
25
|
+
test("unset limit uses the default cap — under it is allowed", () => {
|
|
26
|
+
const decision: FixRunBudgetDecision = FixRunBudget.evaluate({
|
|
27
|
+
configuredLimit: null,
|
|
28
|
+
runsToday: DEFAULT_DAILY_FIX_RUN_LIMIT - 1,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
expect(decision.allowed).toBe(true);
|
|
32
|
+
expect(decision.limit).toBe(DEFAULT_DAILY_FIX_RUN_LIMIT);
|
|
33
|
+
expect(decision.paused).toBe(false);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("unset limit is NOT unlimited: at the default cap is rejected", () => {
|
|
37
|
+
const decision: FixRunBudgetDecision = FixRunBudget.evaluate({
|
|
38
|
+
configuredLimit: undefined,
|
|
39
|
+
runsToday: DEFAULT_DAILY_FIX_RUN_LIMIT,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
expect(decision.allowed).toBe(false);
|
|
43
|
+
expect(decision.paused).toBe(false);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("a custom limit overrides the default in both directions", () => {
|
|
47
|
+
expect(
|
|
48
|
+
FixRunBudget.evaluate({ configuredLimit: 2, runsToday: 1 }).allowed,
|
|
49
|
+
).toBe(true);
|
|
50
|
+
expect(
|
|
51
|
+
FixRunBudget.evaluate({ configuredLimit: 2, runsToday: 2 }).allowed,
|
|
52
|
+
).toBe(false);
|
|
53
|
+
// A limit above the default allows more than the default would.
|
|
54
|
+
expect(
|
|
55
|
+
FixRunBudget.evaluate({
|
|
56
|
+
configuredLimit: 100,
|
|
57
|
+
runsToday: DEFAULT_DAILY_FIX_RUN_LIMIT,
|
|
58
|
+
}).allowed,
|
|
59
|
+
).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("0 pauses fix tasks outright — even with zero runs today", () => {
|
|
63
|
+
const decision: FixRunBudgetDecision = FixRunBudget.evaluate({
|
|
64
|
+
configuredLimit: 0,
|
|
65
|
+
runsToday: 0,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
expect(decision.allowed).toBe(false);
|
|
69
|
+
expect(decision.paused).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("a negative limit reads as paused, never as unlimited", () => {
|
|
73
|
+
const decision: FixRunBudgetDecision = FixRunBudget.evaluate({
|
|
74
|
+
configuredLimit: -5,
|
|
75
|
+
runsToday: 0,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
expect(decision.allowed).toBe(false);
|
|
79
|
+
expect(decision.paused).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("over the limit is rejected and reports the counts", () => {
|
|
83
|
+
const decision: FixRunBudgetDecision = FixRunBudget.evaluate({
|
|
84
|
+
configuredLimit: 3,
|
|
85
|
+
runsToday: 7,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(decision.allowed).toBe(false);
|
|
89
|
+
expect(decision.runsToday).toBe(7);
|
|
90
|
+
expect(decision.limit).toBe(3);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe("FixRunBudget.describeRejection", () => {
|
|
95
|
+
test("paused rejection names the setting and the 0 value", () => {
|
|
96
|
+
const message: string = FixRunBudget.describeRejection(
|
|
97
|
+
FixRunBudget.evaluate({ configuredLimit: 0, runsToday: 0 }),
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
expect(message).toMatch(/Daily AI Fix Task Limit/);
|
|
101
|
+
expect(message).toMatch(/0/);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("over-budget rejection names the counts, the setting and the default", () => {
|
|
105
|
+
const message: string = FixRunBudget.describeRejection(
|
|
106
|
+
FixRunBudget.evaluate({ configuredLimit: 10, runsToday: 10 }),
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
expect(message).toMatch(/10 of 10/);
|
|
110
|
+
expect(message).toMatch(/Daily AI Fix Task Limit/);
|
|
111
|
+
expect(message).toMatch(new RegExp(String(DEFAULT_DAILY_FIX_RUN_LIMIT)));
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
describe("FixRunBudget.getBudgetStatus (IO wiring)", () => {
|
|
116
|
+
afterEach(() => {
|
|
117
|
+
jest.restoreAllMocks();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("counts the project's CodeFix runs created since UTC midnight", async () => {
|
|
121
|
+
jest.spyOn(ProjectService, "findOneById").mockResolvedValue({
|
|
122
|
+
id: projectId,
|
|
123
|
+
aiDailyFixTaskLimit: 10,
|
|
124
|
+
} as unknown as Project);
|
|
125
|
+
|
|
126
|
+
const countBy: jest.SpyInstance = jest
|
|
127
|
+
.spyOn(AIRunService, "countBy")
|
|
128
|
+
.mockResolvedValue(new PositiveNumber(4));
|
|
129
|
+
|
|
130
|
+
const decision: FixRunBudgetDecision =
|
|
131
|
+
await FixRunBudget.getBudgetStatus(projectId);
|
|
132
|
+
|
|
133
|
+
expect(decision).toEqual({
|
|
134
|
+
allowed: true,
|
|
135
|
+
limit: 10,
|
|
136
|
+
paused: false,
|
|
137
|
+
runsToday: 4,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
expect(countBy).toHaveBeenCalledWith(
|
|
141
|
+
expect.objectContaining({
|
|
142
|
+
query: expect.objectContaining({
|
|
143
|
+
projectId,
|
|
144
|
+
runType: AIRunType.CodeFix,
|
|
145
|
+
// createdAt >= UTC midnight rides in a QueryHelper find operator.
|
|
146
|
+
createdAt: expect.anything(),
|
|
147
|
+
}),
|
|
148
|
+
props: expect.objectContaining({ isRoot: true }),
|
|
149
|
+
}),
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("a paused project (limit 0) short-circuits without the count query", async () => {
|
|
154
|
+
jest.spyOn(ProjectService, "findOneById").mockResolvedValue({
|
|
155
|
+
id: projectId,
|
|
156
|
+
aiDailyFixTaskLimit: 0,
|
|
157
|
+
} as unknown as Project);
|
|
158
|
+
|
|
159
|
+
const countBy: jest.SpyInstance = jest.spyOn(AIRunService, "countBy");
|
|
160
|
+
|
|
161
|
+
const decision: FixRunBudgetDecision =
|
|
162
|
+
await FixRunBudget.getBudgetStatus(projectId);
|
|
163
|
+
|
|
164
|
+
expect(decision.allowed).toBe(false);
|
|
165
|
+
expect(decision.paused).toBe(true);
|
|
166
|
+
expect(countBy).not.toHaveBeenCalled();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("a missing project row falls back to the default cap (fail-safe, not fail-open)", async () => {
|
|
170
|
+
jest.spyOn(ProjectService, "findOneById").mockResolvedValue(null);
|
|
171
|
+
jest
|
|
172
|
+
.spyOn(AIRunService, "countBy")
|
|
173
|
+
.mockResolvedValue(new PositiveNumber(DEFAULT_DAILY_FIX_RUN_LIMIT));
|
|
174
|
+
|
|
175
|
+
const decision: FixRunBudgetDecision =
|
|
176
|
+
await FixRunBudget.getBudgetStatus(projectId);
|
|
177
|
+
|
|
178
|
+
expect(decision.limit).toBe(DEFAULT_DAILY_FIX_RUN_LIMIT);
|
|
179
|
+
expect(decision.allowed).toBe(false);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe("FixRunBudget.assertWithinBudget", () => {
|
|
184
|
+
afterEach(() => {
|
|
185
|
+
jest.restoreAllMocks();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("under budget resolves silently", async () => {
|
|
189
|
+
jest.spyOn(ProjectService, "findOneById").mockResolvedValue({
|
|
190
|
+
id: projectId,
|
|
191
|
+
aiDailyFixTaskLimit: 5,
|
|
192
|
+
} as unknown as Project);
|
|
193
|
+
jest
|
|
194
|
+
.spyOn(AIRunService, "countBy")
|
|
195
|
+
.mockResolvedValue(new PositiveNumber(1));
|
|
196
|
+
|
|
197
|
+
await expect(
|
|
198
|
+
FixRunBudget.assertWithinBudget(projectId),
|
|
199
|
+
).resolves.toBeUndefined();
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("over budget throws a BadDataException naming the setting", async () => {
|
|
203
|
+
jest.spyOn(ProjectService, "findOneById").mockResolvedValue({
|
|
204
|
+
id: projectId,
|
|
205
|
+
aiDailyFixTaskLimit: 5,
|
|
206
|
+
} as unknown as Project);
|
|
207
|
+
jest
|
|
208
|
+
.spyOn(AIRunService, "countBy")
|
|
209
|
+
.mockResolvedValue(new PositiveNumber(5));
|
|
210
|
+
|
|
211
|
+
await expect(FixRunBudget.assertWithinBudget(projectId)).rejects.toThrow(
|
|
212
|
+
BadDataException,
|
|
213
|
+
);
|
|
214
|
+
await expect(FixRunBudget.assertWithinBudget(projectId)).rejects.toThrow(
|
|
215
|
+
/Daily AI Fix Task Limit/,
|
|
216
|
+
);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
/** @timezone Asia/Kolkata */
|
|
2
|
+
|
|
3
|
+
import { afterEach, describe, expect, test } from "@jest/globals";
|
|
4
|
+
import ErrorLogSpikeDetector, {
|
|
5
|
+
ERROR_LOG_SEVERITIES,
|
|
6
|
+
ERROR_LOG_SPIKE_HIGH_SEVERITY_MULTIPLIER,
|
|
7
|
+
ERROR_LOG_SPIKE_HISTOGRAM_BUCKET_MINUTES,
|
|
8
|
+
ERROR_LOG_SPIKE_MAX_ATTRIBUTED_SERVICES,
|
|
9
|
+
ERROR_LOG_SPIKE_MIN_MULTIPLIER,
|
|
10
|
+
ERROR_LOG_SPIKE_MIN_RECENT_COUNT,
|
|
11
|
+
ErrorLogSpikeDecision,
|
|
12
|
+
} from "../../../../../Server/Utils/AI/SRE/Insights/Detectors/ErrorLogSpikeDetector";
|
|
13
|
+
import { InsightCandidate } from "../../../../../Server/Utils/AI/SRE/Insights/Types";
|
|
14
|
+
import LogAggregationService, {
|
|
15
|
+
HistogramBucket,
|
|
16
|
+
} from "../../../../../Server/Services/LogAggregationService";
|
|
17
|
+
import ServiceService from "../../../../../Server/Services/ServiceService";
|
|
18
|
+
import Service from "../../../../../Models/DatabaseModels/Service";
|
|
19
|
+
import AIInsightSeverity from "../../../../../Types/AI/AIInsightSeverity";
|
|
20
|
+
import AIInsightType from "../../../../../Types/AI/AIInsightType";
|
|
21
|
+
import ObjectID from "../../../../../Types/ObjectID";
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
* Invariant under test: the ErrorLogSpike detector fires exactly when the
|
|
25
|
+
* project's Error/Fatal log count in the last hour is >= 100 AND >= 3x its
|
|
26
|
+
* average hourly rate over the prior 24h (baseline floored at 1/hour),
|
|
27
|
+
* escalating to High at 10x — stage 1 is ONE projection-shaped histogram
|
|
28
|
+
* (no service filter), and the scan-costly service attribution runs ONLY
|
|
29
|
+
* after a spike is found, bounded to the spike hour and capped at 3
|
|
30
|
+
* services. No LLM is ever involved.
|
|
31
|
+
*
|
|
32
|
+
* The suite is PINNED to a non-UTC process timezone (docblock pragma above,
|
|
33
|
+
* honored by Tests/Utils/TimezoneEnvironment.js). Asia/Kolkata is chosen for
|
|
34
|
+
* two properties: it is a POSITIVE offset, the direction that silently
|
|
35
|
+
* SUPPRESSES spikes (bad buckets fall out of the recent window and inflate
|
|
36
|
+
* the baseline they are compared against — a detector that reports nothing
|
|
37
|
+
* looks healthy), and it is a :30 offset, which a whole-hours-only fix would
|
|
38
|
+
* still get wrong. Every bucket fixture below therefore uses the shape
|
|
39
|
+
* ClickHouse actually emits — a zone-LESS "YYYY-MM-DD hh:mm:ss" wall clock —
|
|
40
|
+
* and every expectation is an absolute UTC instant, so the suite holds only
|
|
41
|
+
* if bucket times are parsed as UTC rather than as the worker's local time.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
45
|
+
const now: Date = new Date("2026-07-14T12:00:00.000Z");
|
|
46
|
+
const serviceIdA: string = ObjectID.generate().toString();
|
|
47
|
+
const serviceIdB: string = ObjectID.generate().toString();
|
|
48
|
+
|
|
49
|
+
// The real ClickHouse DateTime rendering: no zone designator, space-separated.
|
|
50
|
+
function bucket(time: string, count: number): HistogramBucket {
|
|
51
|
+
return { time, severity: "Error", count };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
describe("test environment (guards the fixtures below from being vacuous)", () => {
|
|
55
|
+
test("the process runs in a non-UTC zone that skews zone-less parsing", () => {
|
|
56
|
+
expect(process.env["TZ"]).toBe("Asia/Kolkata");
|
|
57
|
+
|
|
58
|
+
/*
|
|
59
|
+
* Proof that this suite is adversarial rather than accidentally green: a
|
|
60
|
+
* bare `new Date(clickhouseWallClock)` here does NOT land on the UTC
|
|
61
|
+
* instant of the same wall clock, it lands 5h30m earlier. Every zone-less
|
|
62
|
+
* fixture below is therefore a live trap for local-time parsing.
|
|
63
|
+
*/
|
|
64
|
+
const naive: number = new Date("2026-07-14 11:05:00").getTime();
|
|
65
|
+
expect(naive).toBe(Date.UTC(2026, 6, 14, 11, 5, 0) - 5.5 * 60 * 60 * 1000);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe("ErrorLogSpikeDetector.parseBucketTime (pure)", () => {
|
|
70
|
+
test("a zone-less ClickHouse wall clock is read as UTC, not as local time", () => {
|
|
71
|
+
expect(
|
|
72
|
+
ErrorLogSpikeDetector.parseBucketTime("2026-07-14 11:05:00").getTime(),
|
|
73
|
+
).toBe(Date.UTC(2026, 6, 14, 11, 5, 0));
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("a zone-less T-separated clock is read as UTC too", () => {
|
|
77
|
+
expect(
|
|
78
|
+
ErrorLogSpikeDetector.parseBucketTime("2026-07-14T11:05:00").getTime(),
|
|
79
|
+
).toBe(Date.UTC(2026, 6, 14, 11, 5, 0));
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("fractional seconds (DateTime64) survive the UTC normalization", () => {
|
|
83
|
+
expect(
|
|
84
|
+
ErrorLogSpikeDetector.parseBucketTime(
|
|
85
|
+
"2026-07-14 11:05:00.123",
|
|
86
|
+
).getTime(),
|
|
87
|
+
).toBe(Date.UTC(2026, 6, 14, 11, 5, 0) + 123);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("an ISO-Z string passes through untouched (no double-Z Invalid Date)", () => {
|
|
91
|
+
const parsed: Date = ErrorLogSpikeDetector.parseBucketTime(
|
|
92
|
+
"2026-07-14T11:05:00.000Z",
|
|
93
|
+
);
|
|
94
|
+
expect(isNaN(parsed.getTime())).toBe(false);
|
|
95
|
+
expect(parsed.getTime()).toBe(Date.UTC(2026, 6, 14, 11, 5, 0));
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("an explicit +/- offset passes through and keeps its own zone", () => {
|
|
99
|
+
// 11:05 at +05:30 is 05:35 UTC — the offset must be honored, not appended to.
|
|
100
|
+
const positive: Date = ErrorLogSpikeDetector.parseBucketTime(
|
|
101
|
+
"2026-07-14 11:05:00+05:30",
|
|
102
|
+
);
|
|
103
|
+
expect(isNaN(positive.getTime())).toBe(false);
|
|
104
|
+
expect(positive.getTime()).toBe(Date.UTC(2026, 6, 14, 5, 35, 0));
|
|
105
|
+
|
|
106
|
+
// 11:05 at -08:00 is 19:05 UTC.
|
|
107
|
+
const negative: Date = ErrorLogSpikeDetector.parseBucketTime(
|
|
108
|
+
"2026-07-14T11:05:00-08:00",
|
|
109
|
+
);
|
|
110
|
+
expect(isNaN(negative.getTime())).toBe(false);
|
|
111
|
+
expect(negative.getTime()).toBe(Date.UTC(2026, 6, 14, 19, 5, 0));
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("garbage stays an Invalid Date so the caller's isNaN guard still skips it", () => {
|
|
115
|
+
expect(
|
|
116
|
+
isNaN(ErrorLogSpikeDetector.parseBucketTime("not-a-date").getTime()),
|
|
117
|
+
).toBe(true);
|
|
118
|
+
expect(isNaN(ErrorLogSpikeDetector.parseBucketTime("").getTime())).toBe(
|
|
119
|
+
true,
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe("ErrorLogSpikeDetector.splitHistogram (pure)", () => {
|
|
125
|
+
const boundary: Date = new Date("2026-07-14T11:00:00.000Z");
|
|
126
|
+
|
|
127
|
+
test("buckets at or after the boundary are recent; earlier ones are prior", () => {
|
|
128
|
+
const split: { recentCount: number; priorCount: number } =
|
|
129
|
+
ErrorLogSpikeDetector.splitHistogram(
|
|
130
|
+
[
|
|
131
|
+
bucket("2026-07-14 10:55:00", 7),
|
|
132
|
+
bucket("2026-07-14 11:00:00", 11),
|
|
133
|
+
bucket("2026-07-14 11:30:00", 13),
|
|
134
|
+
bucket("2026-07-13 12:00:00", 5),
|
|
135
|
+
],
|
|
136
|
+
boundary,
|
|
137
|
+
);
|
|
138
|
+
expect(split.recentCount).toBe(24);
|
|
139
|
+
expect(split.priorCount).toBe(12);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("the zone-less ClickHouse shape splits identically to its ISO-Z equivalent", () => {
|
|
143
|
+
/*
|
|
144
|
+
* Regression lock for the local-time parsing bug: these two fixtures are
|
|
145
|
+
* the same four instants written two ways. Under the bug the zone-less
|
|
146
|
+
* form drifted by the worker's UTC offset (here it lost the entire recent
|
|
147
|
+
* window into the baseline) while the ISO-Z form did not — which is
|
|
148
|
+
* exactly why the original ISO-Z-only fixture could not see the defect.
|
|
149
|
+
*/
|
|
150
|
+
const clickhouseSplit: { recentCount: number; priorCount: number } =
|
|
151
|
+
ErrorLogSpikeDetector.splitHistogram(
|
|
152
|
+
[
|
|
153
|
+
bucket("2026-07-14 11:05:00", 500),
|
|
154
|
+
bucket("2026-07-14 11:55:00", 250),
|
|
155
|
+
bucket("2026-07-14 10:59:00", 40),
|
|
156
|
+
bucket("2026-07-14 09:00:00", 200),
|
|
157
|
+
],
|
|
158
|
+
boundary,
|
|
159
|
+
);
|
|
160
|
+
const isoSplit: { recentCount: number; priorCount: number } =
|
|
161
|
+
ErrorLogSpikeDetector.splitHistogram(
|
|
162
|
+
[
|
|
163
|
+
bucket("2026-07-14T11:05:00.000Z", 500),
|
|
164
|
+
bucket("2026-07-14T11:55:00.000Z", 250),
|
|
165
|
+
bucket("2026-07-14T10:59:00.000Z", 40),
|
|
166
|
+
bucket("2026-07-14T09:00:00.000Z", 200),
|
|
167
|
+
],
|
|
168
|
+
boundary,
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
expect(clickhouseSplit).toEqual({ recentCount: 750, priorCount: 240 });
|
|
172
|
+
expect(clickhouseSplit).toEqual(isoSplit);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("a bucket straddling the boundary counts as prior (conservative bias)", () => {
|
|
176
|
+
const split: { recentCount: number; priorCount: number } =
|
|
177
|
+
ErrorLogSpikeDetector.splitHistogram(
|
|
178
|
+
[bucket("2026-07-14 10:59:00", 100)],
|
|
179
|
+
boundary,
|
|
180
|
+
);
|
|
181
|
+
expect(split.recentCount).toBe(0);
|
|
182
|
+
expect(split.priorCount).toBe(100);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test("unparseable bucket times are skipped, empty input splits to zeros", () => {
|
|
186
|
+
const split: { recentCount: number; priorCount: number } =
|
|
187
|
+
ErrorLogSpikeDetector.splitHistogram(
|
|
188
|
+
[bucket("not-a-date", 999)],
|
|
189
|
+
boundary,
|
|
190
|
+
);
|
|
191
|
+
expect(split).toEqual({ recentCount: 0, priorCount: 0 });
|
|
192
|
+
expect(ErrorLogSpikeDetector.splitHistogram([], boundary)).toEqual({
|
|
193
|
+
recentCount: 0,
|
|
194
|
+
priorCount: 0,
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe("ErrorLogSpikeDetector.evaluateSpike (pure decision matrix)", () => {
|
|
200
|
+
test("exactly the recent floor on a zero baseline spikes at High", () => {
|
|
201
|
+
const decision: ErrorLogSpikeDecision = ErrorLogSpikeDetector.evaluateSpike(
|
|
202
|
+
ERROR_LOG_SPIKE_MIN_RECENT_COUNT,
|
|
203
|
+
0,
|
|
204
|
+
);
|
|
205
|
+
expect(decision.isSpike).toBe(true);
|
|
206
|
+
expect(decision.multiplier).toBe(ERROR_LOG_SPIKE_MIN_RECENT_COUNT);
|
|
207
|
+
expect(decision.severity).toBe(AIInsightSeverity.High);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("one below the recent floor never spikes", () => {
|
|
211
|
+
const decision: ErrorLogSpikeDecision = ErrorLogSpikeDetector.evaluateSpike(
|
|
212
|
+
ERROR_LOG_SPIKE_MIN_RECENT_COUNT - 1,
|
|
213
|
+
0,
|
|
214
|
+
);
|
|
215
|
+
expect(decision.isSpike).toBe(false);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("exactly the multiplier threshold spikes (300 vs 100/hour baseline)", () => {
|
|
219
|
+
const decision: ErrorLogSpikeDecision = ErrorLogSpikeDetector.evaluateSpike(
|
|
220
|
+
300,
|
|
221
|
+
2400,
|
|
222
|
+
);
|
|
223
|
+
expect(decision.multiplier).toBe(ERROR_LOG_SPIKE_MIN_MULTIPLIER);
|
|
224
|
+
expect(decision.isSpike).toBe(true);
|
|
225
|
+
expect(decision.severity).toBe(AIInsightSeverity.Medium);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("just under the multiplier threshold does not spike", () => {
|
|
229
|
+
const decision: ErrorLogSpikeDecision = ErrorLogSpikeDetector.evaluateSpike(
|
|
230
|
+
299,
|
|
231
|
+
2400,
|
|
232
|
+
);
|
|
233
|
+
expect(decision.isSpike).toBe(false);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("chronically chatty projects do not spike on their normal volume", () => {
|
|
237
|
+
const decision: ErrorLogSpikeDecision = ErrorLogSpikeDetector.evaluateSpike(
|
|
238
|
+
5000,
|
|
239
|
+
5000 * 24,
|
|
240
|
+
);
|
|
241
|
+
expect(decision.isSpike).toBe(false);
|
|
242
|
+
expect(decision.multiplier).toBe(1);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test("severity boundary: exactly 10x is High", () => {
|
|
246
|
+
const decision: ErrorLogSpikeDecision = ErrorLogSpikeDetector.evaluateSpike(
|
|
247
|
+
1000,
|
|
248
|
+
2400,
|
|
249
|
+
);
|
|
250
|
+
expect(decision.multiplier).toBe(ERROR_LOG_SPIKE_HIGH_SEVERITY_MULTIPLIER);
|
|
251
|
+
expect(decision.severity).toBe(AIInsightSeverity.High);
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
describe("ErrorLogSpikeDetector.buildFingerprint (pure)", () => {
|
|
256
|
+
test("per-service and project-wide wire-contract formats", () => {
|
|
257
|
+
expect(ErrorLogSpikeDetector.buildFingerprint("svc1")).toBe(
|
|
258
|
+
"error-log-spike:svc1",
|
|
259
|
+
);
|
|
260
|
+
expect(ErrorLogSpikeDetector.buildFingerprint(undefined)).toBe(
|
|
261
|
+
"error-log-spike:project",
|
|
262
|
+
);
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
describe("ErrorLogSpikeDetector.detect (IO wiring)", () => {
|
|
267
|
+
afterEach(() => {
|
|
268
|
+
jest.restoreAllMocks();
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test("spike → one insight per attributed service, sharing the project-wide evidence", async () => {
|
|
272
|
+
const getHistogramSpy: jest.SpyInstance = jest
|
|
273
|
+
.spyOn(LogAggregationService, "getHistogram")
|
|
274
|
+
.mockResolvedValue([
|
|
275
|
+
bucket("2026-07-14 11:05:00", 500),
|
|
276
|
+
bucket("2026-07-14 09:00:00", 240),
|
|
277
|
+
]);
|
|
278
|
+
const topListSpy: jest.SpyInstance = jest
|
|
279
|
+
.spyOn(LogAggregationService, "getAnalyticsTopList")
|
|
280
|
+
.mockResolvedValue([
|
|
281
|
+
{ value: serviceIdA, count: 300 },
|
|
282
|
+
{ value: serviceIdB, count: 100 },
|
|
283
|
+
]);
|
|
284
|
+
jest
|
|
285
|
+
.spyOn(ServiceService, "findOneById")
|
|
286
|
+
.mockResolvedValueOnce({ name: "api" } as unknown as Service)
|
|
287
|
+
.mockResolvedValueOnce(null);
|
|
288
|
+
|
|
289
|
+
const detector: ErrorLogSpikeDetector = new ErrorLogSpikeDetector();
|
|
290
|
+
const candidates: Array<InsightCandidate> = await detector.detect({
|
|
291
|
+
projectId,
|
|
292
|
+
now,
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Stage 1: one projection-shaped project-wide histogram over 25h.
|
|
296
|
+
expect(getHistogramSpy).toHaveBeenCalledWith(
|
|
297
|
+
expect.objectContaining({
|
|
298
|
+
projectId: projectId,
|
|
299
|
+
startTime: new Date("2026-07-13T11:00:00.000Z"),
|
|
300
|
+
endTime: now,
|
|
301
|
+
bucketSizeInMinutes: ERROR_LOG_SPIKE_HISTOGRAM_BUCKET_MINUTES,
|
|
302
|
+
severityTexts: ERROR_LOG_SEVERITIES,
|
|
303
|
+
}),
|
|
304
|
+
);
|
|
305
|
+
// No service filter on the projection path.
|
|
306
|
+
expect(
|
|
307
|
+
(getHistogramSpy.mock.calls[0]![0] as unknown as Record<string, unknown>)[
|
|
308
|
+
"serviceIds"
|
|
309
|
+
],
|
|
310
|
+
).toBeUndefined();
|
|
311
|
+
|
|
312
|
+
// Stage 2: attribution bounded to the spike hour, capped at 3.
|
|
313
|
+
expect(topListSpy).toHaveBeenCalledWith(
|
|
314
|
+
expect.objectContaining({
|
|
315
|
+
projectId: projectId,
|
|
316
|
+
startTime: new Date("2026-07-14T11:00:00.000Z"),
|
|
317
|
+
endTime: now,
|
|
318
|
+
groupBy: ["primaryEntityId"],
|
|
319
|
+
aggregation: "count",
|
|
320
|
+
severityTexts: ERROR_LOG_SEVERITIES,
|
|
321
|
+
limit: ERROR_LOG_SPIKE_MAX_ATTRIBUTED_SERVICES,
|
|
322
|
+
}),
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
/*
|
|
326
|
+
* 500 recent vs 10/hour baseline → 50x, High, two service insights. The
|
|
327
|
+
* stage-2 window above is bound as a real Date (true UTC hour), so under
|
|
328
|
+
* the local-time parsing bug a non-UTC worker would not merely miss the
|
|
329
|
+
* spike — its stage-1 counts would not even reconcile with the hour it
|
|
330
|
+
* attributes. This assertion holds only when both stages agree on UTC.
|
|
331
|
+
*/
|
|
332
|
+
expect(candidates).toHaveLength(2);
|
|
333
|
+
const first: InsightCandidate = candidates[0]!;
|
|
334
|
+
expect(first.insightType).toBe(AIInsightType.ErrorLogSpike);
|
|
335
|
+
expect(first.fingerprint).toBe(`error-log-spike:${serviceIdA}`);
|
|
336
|
+
expect(first.title).toBe("Error-log spike: 50.0x normal volume in api");
|
|
337
|
+
expect(first.severity).toBe(AIInsightSeverity.High);
|
|
338
|
+
expect(first.serviceName).toBe("api");
|
|
339
|
+
expect(first.telemetryServiceId?.toString()).toBe(serviceIdA);
|
|
340
|
+
expect(first.evidence.logSpike).toEqual(
|
|
341
|
+
expect.objectContaining({
|
|
342
|
+
recentErrorCount: 500,
|
|
343
|
+
baselineHourlyAverage: 10,
|
|
344
|
+
spikeMultiplier: 50,
|
|
345
|
+
windowMinutes: 60,
|
|
346
|
+
}),
|
|
347
|
+
);
|
|
348
|
+
expect(first.evidence.logSpike!.topServices).toEqual([
|
|
349
|
+
{ serviceName: "api", count: 300 },
|
|
350
|
+
{ serviceName: serviceIdB, count: 100 },
|
|
351
|
+
]);
|
|
352
|
+
|
|
353
|
+
// Unresolvable entity keeps its raw id and no telemetryServiceId.
|
|
354
|
+
const second: InsightCandidate = candidates[1]!;
|
|
355
|
+
expect(second.fingerprint).toBe(`error-log-spike:${serviceIdB}`);
|
|
356
|
+
expect(second.serviceName).toBe(serviceIdB);
|
|
357
|
+
expect(second.telemetryServiceId).toBeUndefined();
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
test("no spike → no insights and NO stage-2 attribution query", async () => {
|
|
361
|
+
jest
|
|
362
|
+
.spyOn(LogAggregationService, "getHistogram")
|
|
363
|
+
.mockResolvedValue([bucket("2026-07-14 11:05:00", 50)]);
|
|
364
|
+
const topListSpy: jest.SpyInstance = jest.spyOn(
|
|
365
|
+
LogAggregationService,
|
|
366
|
+
"getAnalyticsTopList",
|
|
367
|
+
);
|
|
368
|
+
|
|
369
|
+
const detector: ErrorLogSpikeDetector = new ErrorLogSpikeDetector();
|
|
370
|
+
const candidates: Array<InsightCandidate> = await detector.detect({
|
|
371
|
+
projectId,
|
|
372
|
+
now,
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
expect(candidates).toEqual([]);
|
|
376
|
+
expect(topListSpy).not.toHaveBeenCalled();
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
test("spike with empty attribution → single project-wide insight", async () => {
|
|
380
|
+
jest
|
|
381
|
+
.spyOn(LogAggregationService, "getHistogram")
|
|
382
|
+
.mockResolvedValue([bucket("2026-07-14 11:05:00", 500)]);
|
|
383
|
+
jest
|
|
384
|
+
.spyOn(LogAggregationService, "getAnalyticsTopList")
|
|
385
|
+
.mockResolvedValue([]);
|
|
386
|
+
|
|
387
|
+
const detector: ErrorLogSpikeDetector = new ErrorLogSpikeDetector();
|
|
388
|
+
const candidates: Array<InsightCandidate> = await detector.detect({
|
|
389
|
+
projectId,
|
|
390
|
+
now,
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
expect(candidates).toHaveLength(1);
|
|
394
|
+
expect(candidates[0]!.fingerprint).toBe("error-log-spike:project");
|
|
395
|
+
expect(candidates[0]!.title).toContain("across the project");
|
|
396
|
+
expect(candidates[0]!.serviceName).toBeUndefined();
|
|
397
|
+
expect(candidates[0]!.evidence.logSpike!.topServices).toEqual([]);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
test("a zone-carrying histogram still spikes (no double-Z zeroing)", async () => {
|
|
401
|
+
/*
|
|
402
|
+
* Belt-and-braces on the passthrough branch: if a ClickHouse setting ever
|
|
403
|
+
* starts emitting a zone designator, the detector must keep counting those
|
|
404
|
+
* buckets rather than parsing them to Invalid Date and reporting silence.
|
|
405
|
+
*/
|
|
406
|
+
jest
|
|
407
|
+
.spyOn(LogAggregationService, "getHistogram")
|
|
408
|
+
.mockResolvedValue([
|
|
409
|
+
bucket("2026-07-14T11:05:00.000Z", 500),
|
|
410
|
+
bucket("2026-07-14T09:00:00.000Z", 240),
|
|
411
|
+
]);
|
|
412
|
+
jest
|
|
413
|
+
.spyOn(LogAggregationService, "getAnalyticsTopList")
|
|
414
|
+
.mockResolvedValue([]);
|
|
415
|
+
|
|
416
|
+
const detector: ErrorLogSpikeDetector = new ErrorLogSpikeDetector();
|
|
417
|
+
const candidates: Array<InsightCandidate> = await detector.detect({
|
|
418
|
+
projectId,
|
|
419
|
+
now,
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
expect(candidates).toHaveLength(1);
|
|
423
|
+
expect(candidates[0]!.evidence.logSpike!.recentErrorCount).toBe(500);
|
|
424
|
+
expect(candidates[0]!.evidence.logSpike!.spikeMultiplier).toBe(50);
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
test("detect propagates histogram errors — the scanner isolates detectors", async () => {
|
|
428
|
+
jest
|
|
429
|
+
.spyOn(LogAggregationService, "getHistogram")
|
|
430
|
+
.mockRejectedValue(new Error("clickhouse down"));
|
|
431
|
+
|
|
432
|
+
const detector: ErrorLogSpikeDetector = new ErrorLogSpikeDetector();
|
|
433
|
+
await expect(detector.detect({ projectId, now })).rejects.toThrow(
|
|
434
|
+
"clickhouse down",
|
|
435
|
+
);
|
|
436
|
+
});
|
|
437
|
+
});
|