@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,370 @@
|
|
|
1
|
+
import InsightStore, {
|
|
2
|
+
INSIGHT_FINGERPRINT_MAX_LENGTH,
|
|
3
|
+
INSIGHT_METRIC_NAME_MAX_LENGTH,
|
|
4
|
+
INSIGHT_SERVICE_NAME_MAX_LENGTH,
|
|
5
|
+
INSIGHT_TITLE_MAX_LENGTH,
|
|
6
|
+
INSIGHT_TRACE_ID_MAX_LENGTH,
|
|
7
|
+
MAX_NEW_INSIGHTS_PER_PROJECT_PER_SCAN,
|
|
8
|
+
UpsertCandidatesResult,
|
|
9
|
+
} from "../../../../../Server/Utils/AI/SRE/Insights/InsightStore";
|
|
10
|
+
import { InsightCandidate } from "../../../../../Server/Utils/AI/SRE/Insights/Types";
|
|
11
|
+
import AIInsightService from "../../../../../Server/Services/AIInsightService";
|
|
12
|
+
import CreateBy from "../../../../../Server/Types/Database/CreateBy";
|
|
13
|
+
import FindOneBy from "../../../../../Server/Types/Database/FindOneBy";
|
|
14
|
+
import AIInsight from "../../../../../Models/DatabaseModels/AIInsight";
|
|
15
|
+
import AIInsightStatus from "../../../../../Types/AI/AIInsightStatus";
|
|
16
|
+
import AIInsightType from "../../../../../Types/AI/AIInsightType";
|
|
17
|
+
import AIInsightSeverity from "../../../../../Types/AI/AIInsightSeverity";
|
|
18
|
+
import ColumnLength from "../../../../../Types/Database/ColumnLength";
|
|
19
|
+
import ObjectID from "../../../../../Types/ObjectID";
|
|
20
|
+
import { describe, expect, test, afterEach } from "@jest/globals";
|
|
21
|
+
|
|
22
|
+
/*
|
|
23
|
+
* Adversarial hardening of the insight store. The invariants these tests
|
|
24
|
+
* lock in:
|
|
25
|
+
* (a) column safety: detector-produced strings are clamped to the model's
|
|
26
|
+
* declared column lengths BEFORE hitting DatabaseService's length
|
|
27
|
+
* check (which throws on overflow), and the clamped fingerprint is the
|
|
28
|
+
* row's identity for the dedupe LOOKUP too — so an over-long
|
|
29
|
+
* fingerprint refreshes one row across ticks instead of failing or
|
|
30
|
+
* duplicating on every scan;
|
|
31
|
+
* (b) per-candidate isolation: one failing candidate never aborts the
|
|
32
|
+
* batch — siblings are still upserted and every successfully created
|
|
33
|
+
* insight is returned for routing (otherwise a mid-batch failure would
|
|
34
|
+
* strand already-created rows in Detected forever);
|
|
35
|
+
* (c) a failed create does not consume the per-scan cap;
|
|
36
|
+
* (d) same-fingerprint behavior is pinned: within one batch (DB-faithful
|
|
37
|
+
* lookup) the second candidate refreshes the row the first created;
|
|
38
|
+
* when the lookup misses for both (the concurrent-scan race —
|
|
39
|
+
* (projectId, fingerprint) is deliberately NOT unique and there is no
|
|
40
|
+
* DB-level guard) the store creates duplicates. The single-flight cron
|
|
41
|
+
* (10-min timeout < 15-min tick) is the real concurrency guard.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
45
|
+
const now: Date = new Date("2026-07-14T12:00:00.000Z");
|
|
46
|
+
|
|
47
|
+
function makeCandidate(
|
|
48
|
+
overrides?: Partial<InsightCandidate>,
|
|
49
|
+
): InsightCandidate {
|
|
50
|
+
return {
|
|
51
|
+
insightType: AIInsightType.TraceLatencyRegression,
|
|
52
|
+
fingerprint: "latency:svc-1:GET /checkout",
|
|
53
|
+
title: "Latency regression: p99 3.0x on GET /checkout",
|
|
54
|
+
detailMarkdown: "**p99** went from 800ms to 2400ms.",
|
|
55
|
+
severity: AIInsightSeverity.Medium,
|
|
56
|
+
evidence: {
|
|
57
|
+
latency: {
|
|
58
|
+
recentP99Ms: 2400,
|
|
59
|
+
baselineP99Ms: 800,
|
|
60
|
+
regressionMultiplier: 3,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
...overrides,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/*
|
|
68
|
+
* create echoes back the model it was handed (with an id), so assertions can
|
|
69
|
+
* inspect exactly what the store persisted.
|
|
70
|
+
*/
|
|
71
|
+
function mockCreate(): jest.SpyInstance {
|
|
72
|
+
return jest
|
|
73
|
+
.spyOn(AIInsightService, "create")
|
|
74
|
+
.mockImplementation((createBy: CreateBy<AIInsight>) => {
|
|
75
|
+
const model: AIInsight = createBy.data;
|
|
76
|
+
model.id = ObjectID.generate();
|
|
77
|
+
return Promise.resolve(model);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function mockFindOneBy(existing: AIInsight | null): jest.SpyInstance {
|
|
82
|
+
return jest.spyOn(AIInsightService, "findOneBy").mockResolvedValue(existing);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function mockUpdateOneById(): jest.SpyInstance {
|
|
86
|
+
return jest
|
|
87
|
+
.spyOn(AIInsightService, "updateOneById")
|
|
88
|
+
.mockResolvedValue(undefined);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
describe("InsightStore — column-length clamps", () => {
|
|
92
|
+
afterEach(() => {
|
|
93
|
+
jest.restoreAllMocks();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("the clamp caps mirror the AIInsight column definitions", () => {
|
|
97
|
+
expect(INSIGHT_FINGERPRINT_MAX_LENGTH).toBe(ColumnLength.LongText);
|
|
98
|
+
expect(INSIGHT_TITLE_MAX_LENGTH).toBe(ColumnLength.LongText);
|
|
99
|
+
expect(INSIGHT_SERVICE_NAME_MAX_LENGTH).toBe(ColumnLength.LongText);
|
|
100
|
+
expect(INSIGHT_METRIC_NAME_MAX_LENGTH).toBe(ColumnLength.LongText);
|
|
101
|
+
expect(INSIGHT_TRACE_ID_MAX_LENGTH).toBe(ColumnLength.ShortText);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("over-long detector strings are clamped to the column caps on create — and the dedupe lookup uses the CLAMPED fingerprint", async () => {
|
|
105
|
+
/*
|
|
106
|
+
* A span name can be an entire SQL statement — every string that ends up
|
|
107
|
+
* in a length-checked varchar column must be clamped or the create
|
|
108
|
+
* throws and the candidate (and, without isolation, the batch) is lost.
|
|
109
|
+
*/
|
|
110
|
+
const hugeOperation: string = `SELECT ${"column_name, ".repeat(100)}FROM orders`;
|
|
111
|
+
const findOneBy: jest.SpyInstance = mockFindOneBy(null);
|
|
112
|
+
const create: jest.SpyInstance = mockCreate();
|
|
113
|
+
|
|
114
|
+
const result: UpsertCandidatesResult = await InsightStore.upsertCandidates({
|
|
115
|
+
projectId,
|
|
116
|
+
candidates: [
|
|
117
|
+
makeCandidate({
|
|
118
|
+
fingerprint: `latency:svc-1:${hugeOperation}`,
|
|
119
|
+
title: `Latency regression: p99 3.0x on ${hugeOperation}`,
|
|
120
|
+
serviceName: "s".repeat(INSIGHT_SERVICE_NAME_MAX_LENGTH + 100),
|
|
121
|
+
metricName: "m".repeat(INSIGHT_METRIC_NAME_MAX_LENGTH + 100),
|
|
122
|
+
traceId: "t".repeat(INSIGHT_TRACE_ID_MAX_LENGTH + 50),
|
|
123
|
+
}),
|
|
124
|
+
],
|
|
125
|
+
now,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
expect(create).toHaveBeenCalledTimes(1);
|
|
129
|
+
expect(result.created).toHaveLength(1);
|
|
130
|
+
|
|
131
|
+
const persisted: AIInsight = result.created[0]!;
|
|
132
|
+
const expectedFingerprint: string = `latency:svc-1:${hugeOperation}`.slice(
|
|
133
|
+
0,
|
|
134
|
+
INSIGHT_FINGERPRINT_MAX_LENGTH,
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
expect(persisted.fingerprint).toBe(expectedFingerprint);
|
|
138
|
+
expect(persisted.title!.length).toBe(INSIGHT_TITLE_MAX_LENGTH);
|
|
139
|
+
expect(persisted.serviceName!.length).toBe(INSIGHT_SERVICE_NAME_MAX_LENGTH);
|
|
140
|
+
expect(persisted.metricName!.length).toBe(INSIGHT_METRIC_NAME_MAX_LENGTH);
|
|
141
|
+
expect(persisted.traceId!.length).toBe(INSIGHT_TRACE_ID_MAX_LENGTH);
|
|
142
|
+
|
|
143
|
+
// Identity consistency: the lookup asked for what the insert stored.
|
|
144
|
+
expect(findOneBy).toHaveBeenCalledWith(
|
|
145
|
+
expect.objectContaining({
|
|
146
|
+
query: expect.objectContaining({
|
|
147
|
+
fingerprint: expectedFingerprint,
|
|
148
|
+
}),
|
|
149
|
+
}),
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("a clamped fingerprint still dedupes on the next tick — refresh, not a duplicate row", async () => {
|
|
154
|
+
const hugeFingerprint: string = `latency:svc-1:${"x".repeat(1000)}`;
|
|
155
|
+
const clamped: string = hugeFingerprint.slice(
|
|
156
|
+
0,
|
|
157
|
+
INSIGHT_FINGERPRINT_MAX_LENGTH,
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
/*
|
|
161
|
+
* DB-faithful stateful lookup: rows created by this run are visible to
|
|
162
|
+
* later lookups keyed on their (clamped) stored fingerprint.
|
|
163
|
+
*/
|
|
164
|
+
const rowsByFingerprint: Map<string, AIInsight> = new Map();
|
|
165
|
+
|
|
166
|
+
jest
|
|
167
|
+
.spyOn(AIInsightService, "findOneBy")
|
|
168
|
+
.mockImplementation((findOneBy: FindOneBy<AIInsight>) => {
|
|
169
|
+
const fingerprint: string = (
|
|
170
|
+
findOneBy.query as { fingerprint?: string }
|
|
171
|
+
).fingerprint!;
|
|
172
|
+
return Promise.resolve(rowsByFingerprint.get(fingerprint) || null);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const create: jest.SpyInstance = jest
|
|
176
|
+
.spyOn(AIInsightService, "create")
|
|
177
|
+
.mockImplementation((createBy: CreateBy<AIInsight>) => {
|
|
178
|
+
const model: AIInsight = createBy.data;
|
|
179
|
+
model.id = ObjectID.generate();
|
|
180
|
+
model.status = AIInsightStatus.ActionRequired;
|
|
181
|
+
rowsByFingerprint.set(model.fingerprint!, model);
|
|
182
|
+
return Promise.resolve(model);
|
|
183
|
+
});
|
|
184
|
+
const update: jest.SpyInstance = mockUpdateOneById();
|
|
185
|
+
|
|
186
|
+
const firstTick: UpsertCandidatesResult =
|
|
187
|
+
await InsightStore.upsertCandidates({
|
|
188
|
+
projectId,
|
|
189
|
+
candidates: [makeCandidate({ fingerprint: hugeFingerprint })],
|
|
190
|
+
now,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const secondTick: UpsertCandidatesResult =
|
|
194
|
+
await InsightStore.upsertCandidates({
|
|
195
|
+
projectId,
|
|
196
|
+
candidates: [makeCandidate({ fingerprint: hugeFingerprint })],
|
|
197
|
+
now: new Date(now.getTime() + 15 * 60 * 1000),
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
expect(firstTick.created).toHaveLength(1);
|
|
201
|
+
expect(firstTick.created[0]?.fingerprint).toBe(clamped);
|
|
202
|
+
expect(secondTick.created).toHaveLength(0);
|
|
203
|
+
expect(secondTick.refreshed).toBe(1);
|
|
204
|
+
expect(create).toHaveBeenCalledTimes(1);
|
|
205
|
+
expect(update).toHaveBeenCalledTimes(1);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe("InsightStore — per-candidate failure isolation", () => {
|
|
210
|
+
afterEach(() => {
|
|
211
|
+
jest.restoreAllMocks();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("a create rejection on the middle candidate does not abort the batch — siblings are created and returned for routing", async () => {
|
|
215
|
+
mockFindOneBy(null);
|
|
216
|
+
jest
|
|
217
|
+
.spyOn(AIInsightService, "create")
|
|
218
|
+
.mockImplementation((createBy: CreateBy<AIInsight>) => {
|
|
219
|
+
const model: AIInsight = createBy.data;
|
|
220
|
+
if (model.fingerprint === "boom") {
|
|
221
|
+
return Promise.reject(new Error("title length cannot be more..."));
|
|
222
|
+
}
|
|
223
|
+
model.id = ObjectID.generate();
|
|
224
|
+
return Promise.resolve(model);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const result: UpsertCandidatesResult = await InsightStore.upsertCandidates({
|
|
228
|
+
projectId,
|
|
229
|
+
candidates: [
|
|
230
|
+
makeCandidate({ fingerprint: "first" }),
|
|
231
|
+
makeCandidate({ fingerprint: "boom" }),
|
|
232
|
+
makeCandidate({ fingerprint: "third" }),
|
|
233
|
+
],
|
|
234
|
+
now,
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
expect(result.created).toHaveLength(2);
|
|
238
|
+
expect(result.created[0]?.fingerprint).toBe("first");
|
|
239
|
+
expect(result.created[1]?.fingerprint).toBe("third");
|
|
240
|
+
expect(result.droppedByCap).toBe(0);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("a dedupe-lookup rejection is isolated too — the remaining candidates still upsert", async () => {
|
|
244
|
+
jest
|
|
245
|
+
.spyOn(AIInsightService, "findOneBy")
|
|
246
|
+
.mockImplementation((findOneBy: FindOneBy<AIInsight>) => {
|
|
247
|
+
const fingerprint: string = (
|
|
248
|
+
findOneBy.query as { fingerprint?: string }
|
|
249
|
+
).fingerprint!;
|
|
250
|
+
if (fingerprint === "first") {
|
|
251
|
+
return Promise.reject(new Error("connection reset"));
|
|
252
|
+
}
|
|
253
|
+
return Promise.resolve(null);
|
|
254
|
+
});
|
|
255
|
+
mockCreate();
|
|
256
|
+
|
|
257
|
+
const result: UpsertCandidatesResult = await InsightStore.upsertCandidates({
|
|
258
|
+
projectId,
|
|
259
|
+
candidates: [
|
|
260
|
+
makeCandidate({ fingerprint: "first" }),
|
|
261
|
+
makeCandidate({ fingerprint: "second" }),
|
|
262
|
+
],
|
|
263
|
+
now,
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
expect(result.created).toHaveLength(1);
|
|
267
|
+
expect(result.created[0]?.fingerprint).toBe("second");
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
test("a failed create does not consume the per-scan cap — later candidates still fill it", async () => {
|
|
271
|
+
mockFindOneBy(null);
|
|
272
|
+
jest
|
|
273
|
+
.spyOn(AIInsightService, "create")
|
|
274
|
+
.mockImplementation((createBy: CreateBy<AIInsight>) => {
|
|
275
|
+
const model: AIInsight = createBy.data;
|
|
276
|
+
if (model.fingerprint === "fp-2") {
|
|
277
|
+
return Promise.reject(new Error("transient"));
|
|
278
|
+
}
|
|
279
|
+
model.id = ObjectID.generate();
|
|
280
|
+
return Promise.resolve(model);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
/*
|
|
284
|
+
* Cap + 1 candidates with one failure: exactly cap creates succeed and
|
|
285
|
+
* nothing is counted as dropped — the failure freed a cap slot.
|
|
286
|
+
*/
|
|
287
|
+
const candidates: Array<InsightCandidate> = [];
|
|
288
|
+
for (
|
|
289
|
+
let i: number = 0;
|
|
290
|
+
i < MAX_NEW_INSIGHTS_PER_PROJECT_PER_SCAN + 1;
|
|
291
|
+
i++
|
|
292
|
+
) {
|
|
293
|
+
candidates.push(makeCandidate({ fingerprint: `fp-${i}` }));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const result: UpsertCandidatesResult = await InsightStore.upsertCandidates({
|
|
297
|
+
projectId,
|
|
298
|
+
candidates,
|
|
299
|
+
now,
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
expect(result.created).toHaveLength(MAX_NEW_INSIGHTS_PER_PROJECT_PER_SCAN);
|
|
303
|
+
expect(result.droppedByCap).toBe(0);
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
describe("InsightStore — same-fingerprint duplicates (batch and race)", () => {
|
|
308
|
+
afterEach(() => {
|
|
309
|
+
jest.restoreAllMocks();
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
test("two same-fingerprint candidates in ONE batch: the second refreshes the row the first created (DB-faithful lookup)", async () => {
|
|
313
|
+
const rowsByFingerprint: Map<string, AIInsight> = new Map();
|
|
314
|
+
|
|
315
|
+
jest
|
|
316
|
+
.spyOn(AIInsightService, "findOneBy")
|
|
317
|
+
.mockImplementation((findOneBy: FindOneBy<AIInsight>) => {
|
|
318
|
+
const fingerprint: string = (
|
|
319
|
+
findOneBy.query as { fingerprint?: string }
|
|
320
|
+
).fingerprint!;
|
|
321
|
+
return Promise.resolve(rowsByFingerprint.get(fingerprint) || null);
|
|
322
|
+
});
|
|
323
|
+
jest
|
|
324
|
+
.spyOn(AIInsightService, "create")
|
|
325
|
+
.mockImplementation((createBy: CreateBy<AIInsight>) => {
|
|
326
|
+
const model: AIInsight = createBy.data;
|
|
327
|
+
model.id = ObjectID.generate();
|
|
328
|
+
rowsByFingerprint.set(model.fingerprint!, model);
|
|
329
|
+
return Promise.resolve(model);
|
|
330
|
+
});
|
|
331
|
+
const update: jest.SpyInstance = mockUpdateOneById();
|
|
332
|
+
|
|
333
|
+
const result: UpsertCandidatesResult = await InsightStore.upsertCandidates({
|
|
334
|
+
projectId,
|
|
335
|
+
candidates: [
|
|
336
|
+
makeCandidate({ fingerprint: "shared" }),
|
|
337
|
+
makeCandidate({ fingerprint: "shared" }),
|
|
338
|
+
],
|
|
339
|
+
now,
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
expect(result.created).toHaveLength(1);
|
|
343
|
+
expect(result.refreshed).toBe(1);
|
|
344
|
+
expect(update).toHaveBeenCalledTimes(1);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test("PINNED: when the lookup misses for both (concurrent-scan race), the store creates duplicates — there is no DB-level uniqueness guard", async () => {
|
|
348
|
+
/*
|
|
349
|
+
* (projectId, fingerprint) is deliberately NOT unique (terminal rows
|
|
350
|
+
* accumulate as history), so nothing below the store prevents two racing
|
|
351
|
+
* scans from double-creating. The operational guard is the single-flight
|
|
352
|
+
* cron (10-minute job timeout < 15-minute tick). If multi-instance
|
|
353
|
+
* workers ever run this job, a real dedupe guard must be added.
|
|
354
|
+
*/
|
|
355
|
+
mockFindOneBy(null);
|
|
356
|
+
const create: jest.SpyInstance = mockCreate();
|
|
357
|
+
|
|
358
|
+
const result: UpsertCandidatesResult = await InsightStore.upsertCandidates({
|
|
359
|
+
projectId,
|
|
360
|
+
candidates: [
|
|
361
|
+
makeCandidate({ fingerprint: "raced" }),
|
|
362
|
+
makeCandidate({ fingerprint: "raced" }),
|
|
363
|
+
],
|
|
364
|
+
now,
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
expect(create).toHaveBeenCalledTimes(2);
|
|
368
|
+
expect(result.created).toHaveLength(2);
|
|
369
|
+
});
|
|
370
|
+
});
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import InsightTriage, {
|
|
2
|
+
InsightTriageResult,
|
|
3
|
+
} from "../../../../../Server/Utils/AI/SRE/Insights/Triage";
|
|
4
|
+
import AIInvestigationQueue from "../../../../../Server/Utils/AI/SRE/InvestigationQueue";
|
|
5
|
+
import ProjectService from "../../../../../Server/Services/ProjectService";
|
|
6
|
+
import LlmProviderService from "../../../../../Server/Services/LlmProviderService";
|
|
7
|
+
import AIRunService from "../../../../../Server/Services/AIRunService";
|
|
8
|
+
import AIInsightService from "../../../../../Server/Services/AIInsightService";
|
|
9
|
+
import AIInsight from "../../../../../Models/DatabaseModels/AIInsight";
|
|
10
|
+
import Project from "../../../../../Models/DatabaseModels/Project";
|
|
11
|
+
import LlmProvider from "../../../../../Models/DatabaseModels/LlmProvider";
|
|
12
|
+
import AIRunType from "../../../../../Types/AI/AIRunType";
|
|
13
|
+
import ObjectID from "../../../../../Types/ObjectID";
|
|
14
|
+
import PositiveNumber from "../../../../../Types/PositiveNumber";
|
|
15
|
+
import { describe, expect, test, afterEach } from "@jest/globals";
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
* Per-insight LLM triage enqueue. The invariants these tests lock in:
|
|
19
|
+
* (a) the gates mirror the engine's enablement posture: enableAi must not
|
|
20
|
+
* be false, Project.enableAiInsights must be EXACTLY true
|
|
21
|
+
* (strict opt-in, default false), and an LLM provider must exist —
|
|
22
|
+
* each miss is a quiet skip;
|
|
23
|
+
* (b) dedupe: at most one non-terminal triage run per insight (the
|
|
24
|
+
* scanner refreshes recurring insights every tick — refreshes must
|
|
25
|
+
* not fan out into duplicate runs);
|
|
26
|
+
* (c) the happy path enqueues with subjectAIInsightId (so the
|
|
27
|
+
* queue stamps triggeredByAiInsightId and dispatch routes to
|
|
28
|
+
* the triage runner) and persists triageAiRunId onto the insight;
|
|
29
|
+
* (d) a null enqueue result (the queue's budget quiet-skip) means no
|
|
30
|
+
* link is written and no id is returned;
|
|
31
|
+
* (e) enqueueInsightTriage NEVER throws.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
35
|
+
const insightId: ObjectID = ObjectID.generate();
|
|
36
|
+
const triageRunId: ObjectID = ObjectID.generate();
|
|
37
|
+
|
|
38
|
+
function makeInsight(): AIInsight {
|
|
39
|
+
return { id: insightId, projectId } as unknown as AIInsight;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function mockProject(
|
|
43
|
+
overrides: Record<string, unknown> = {},
|
|
44
|
+
): jest.SpyInstance {
|
|
45
|
+
return jest.spyOn(ProjectService, "findOneById").mockResolvedValue({
|
|
46
|
+
id: projectId,
|
|
47
|
+
enableAiInsights: true,
|
|
48
|
+
...overrides,
|
|
49
|
+
} as unknown as Project);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function mockProvider(exists: boolean): jest.SpyInstance {
|
|
53
|
+
return jest
|
|
54
|
+
.spyOn(LlmProviderService, "getLLMProviderForProject")
|
|
55
|
+
.mockResolvedValue(
|
|
56
|
+
exists ? ({ id: ObjectID.generate() } as unknown as LlmProvider) : null,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function mockNonTerminalTriageRunCount(count: number): jest.SpyInstance {
|
|
61
|
+
return jest
|
|
62
|
+
.spyOn(AIRunService, "countBy")
|
|
63
|
+
.mockResolvedValue(new PositiveNumber(count));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
describe("InsightTriage.enqueueInsightTriage", () => {
|
|
67
|
+
afterEach(() => {
|
|
68
|
+
jest.restoreAllMocks();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("skips when the project is not found", async () => {
|
|
72
|
+
jest.spyOn(ProjectService, "findOneById").mockResolvedValue(null);
|
|
73
|
+
const enqueue: jest.SpyInstance = jest.spyOn(
|
|
74
|
+
AIInvestigationQueue,
|
|
75
|
+
"enqueue",
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const result: InsightTriageResult =
|
|
79
|
+
await InsightTriage.enqueueInsightTriage({ insight: makeInsight() });
|
|
80
|
+
|
|
81
|
+
expect(result).toEqual({});
|
|
82
|
+
expect(enqueue).not.toHaveBeenCalled();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("skips when AI is disabled for the project", async () => {
|
|
86
|
+
mockProject({ enableAi: false });
|
|
87
|
+
const enqueue: jest.SpyInstance = jest.spyOn(
|
|
88
|
+
AIInvestigationQueue,
|
|
89
|
+
"enqueue",
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const result: InsightTriageResult =
|
|
93
|
+
await InsightTriage.enqueueInsightTriage({ insight: makeInsight() });
|
|
94
|
+
|
|
95
|
+
expect(result).toEqual({});
|
|
96
|
+
expect(enqueue).not.toHaveBeenCalled();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("skips unless enableAiInsights is EXACTLY true (strict opt-in)", async () => {
|
|
100
|
+
mockProject({ enableAiInsights: undefined });
|
|
101
|
+
const enqueue: jest.SpyInstance = jest.spyOn(
|
|
102
|
+
AIInvestigationQueue,
|
|
103
|
+
"enqueue",
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const result: InsightTriageResult =
|
|
107
|
+
await InsightTriage.enqueueInsightTriage({ insight: makeInsight() });
|
|
108
|
+
|
|
109
|
+
expect(result).toEqual({});
|
|
110
|
+
expect(enqueue).not.toHaveBeenCalled();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("skips quietly when no LLM provider is configured", async () => {
|
|
114
|
+
mockProject();
|
|
115
|
+
mockProvider(false);
|
|
116
|
+
const enqueue: jest.SpyInstance = jest.spyOn(
|
|
117
|
+
AIInvestigationQueue,
|
|
118
|
+
"enqueue",
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const result: InsightTriageResult =
|
|
122
|
+
await InsightTriage.enqueueInsightTriage({ insight: makeInsight() });
|
|
123
|
+
|
|
124
|
+
expect(result).toEqual({});
|
|
125
|
+
expect(enqueue).not.toHaveBeenCalled();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("dedupes: an existing non-terminal triage run for the insight blocks a second one", async () => {
|
|
129
|
+
mockProject();
|
|
130
|
+
mockProvider(true);
|
|
131
|
+
const countBy: jest.SpyInstance = mockNonTerminalTriageRunCount(1);
|
|
132
|
+
const enqueue: jest.SpyInstance = jest.spyOn(
|
|
133
|
+
AIInvestigationQueue,
|
|
134
|
+
"enqueue",
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
const result: InsightTriageResult =
|
|
138
|
+
await InsightTriage.enqueueInsightTriage({ insight: makeInsight() });
|
|
139
|
+
|
|
140
|
+
expect(result).toEqual({});
|
|
141
|
+
expect(enqueue).not.toHaveBeenCalled();
|
|
142
|
+
// The dedupe key is the provenance column, scoped to Investigation runs.
|
|
143
|
+
expect(countBy).toHaveBeenCalledWith(
|
|
144
|
+
expect.objectContaining({
|
|
145
|
+
query: expect.objectContaining({
|
|
146
|
+
runType: AIRunType.Investigation,
|
|
147
|
+
triggeredByAiInsightId: insightId,
|
|
148
|
+
}),
|
|
149
|
+
props: expect.objectContaining({ isRoot: true }),
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("happy path: enqueues with subjectAIInsightId and persists triageAiRunId onto the insight", async () => {
|
|
155
|
+
mockProject();
|
|
156
|
+
mockProvider(true);
|
|
157
|
+
mockNonTerminalTriageRunCount(0);
|
|
158
|
+
const enqueue: jest.SpyInstance = jest
|
|
159
|
+
.spyOn(AIInvestigationQueue, "enqueue")
|
|
160
|
+
.mockResolvedValue(triageRunId);
|
|
161
|
+
const persist: jest.SpyInstance = jest
|
|
162
|
+
.spyOn(AIInsightService, "updateOneById")
|
|
163
|
+
.mockResolvedValue(undefined);
|
|
164
|
+
|
|
165
|
+
const result: InsightTriageResult =
|
|
166
|
+
await InsightTriage.enqueueInsightTriage({ insight: makeInsight() });
|
|
167
|
+
|
|
168
|
+
expect(result).toEqual({ triageAiRunId: triageRunId });
|
|
169
|
+
expect(enqueue).toHaveBeenCalledWith(
|
|
170
|
+
expect.objectContaining({
|
|
171
|
+
projectId,
|
|
172
|
+
subjectAIInsightId: insightId,
|
|
173
|
+
}),
|
|
174
|
+
);
|
|
175
|
+
expect(persist).toHaveBeenCalledWith(
|
|
176
|
+
expect.objectContaining({
|
|
177
|
+
id: insightId,
|
|
178
|
+
data: expect.objectContaining({ triageAiRunId: triageRunId }),
|
|
179
|
+
props: expect.objectContaining({ isRoot: true }),
|
|
180
|
+
}),
|
|
181
|
+
);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("the queue's budget quiet-skip (null) means no link written and no id returned", async () => {
|
|
185
|
+
mockProject();
|
|
186
|
+
mockProvider(true);
|
|
187
|
+
mockNonTerminalTriageRunCount(0);
|
|
188
|
+
jest.spyOn(AIInvestigationQueue, "enqueue").mockResolvedValue(null);
|
|
189
|
+
const persist: jest.SpyInstance = jest.spyOn(
|
|
190
|
+
AIInsightService,
|
|
191
|
+
"updateOneById",
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
const result: InsightTriageResult =
|
|
195
|
+
await InsightTriage.enqueueInsightTriage({ insight: makeInsight() });
|
|
196
|
+
|
|
197
|
+
expect(result).toEqual({});
|
|
198
|
+
expect(persist).not.toHaveBeenCalled();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("NEVER throws: a gate failure resolves to an empty result", async () => {
|
|
202
|
+
jest
|
|
203
|
+
.spyOn(ProjectService, "findOneById")
|
|
204
|
+
.mockRejectedValue(new Error("db down"));
|
|
205
|
+
|
|
206
|
+
await expect(
|
|
207
|
+
InsightTriage.enqueueInsightTriage({ insight: makeInsight() }),
|
|
208
|
+
).resolves.toEqual({});
|
|
209
|
+
});
|
|
210
|
+
});
|