@oneuptime/common 12.0.5 → 12.0.6
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/AIRun.ts +33 -0
- package/Models/DatabaseModels/DataSource.ts +744 -0
- package/Models/DatabaseModels/Index.ts +3 -0
- package/Models/DatabaseModels/ProjectCallSMSConfig.ts +10 -2
- package/Models/DatabaseModels/User.ts +2 -0
- package/Models/DatabaseModels/UserOnCallLog.ts +2 -0
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +2 -0
- package/Models/DatabaseModels/UserSession.ts +2 -0
- package/Models/DatabaseModels/UserTotpAuth.ts +6 -4
- package/Models/DatabaseModels/UserWebAuthn.ts +5 -3
- package/Server/API/AIInsightAPI.ts +47 -4
- package/Server/API/AIInvestigationAPI.ts +25 -0
- package/Server/API/DashboardAPI.ts +176 -492
- package/Server/API/DataSourceAPI.ts +268 -0
- package/Server/API/StatusPageAPI.ts +11 -1
- package/Server/API/UserAPI.ts +119 -0
- package/Server/Infrastructure/ClickhouseConfig.ts +59 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.ts +39 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.ts +24 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.ts +76 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Infrastructure/Queue.ts +26 -0
- package/Server/Infrastructure/QueueWorker.ts +17 -0
- package/Server/Middleware/TelemetryIngest.ts +8 -3
- package/Server/Services/AIInsightService.ts +132 -12
- package/Server/Services/AIRunService.ts +34 -0
- package/Server/Services/AIService.ts +17 -2
- package/Server/Services/AlertService.ts +38 -32
- package/Server/Services/AnalyticsDatabaseService.ts +8 -4
- package/Server/Services/DataSourceService.ts +200 -0
- package/Server/Services/HostService.ts +92 -0
- package/Server/Services/IncidentService.ts +45 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/MonitorService.ts +49 -0
- package/Server/Services/MonitorStatusService.ts +166 -0
- package/Server/Services/OpenTelemetryIngestService.ts +255 -184
- package/Server/Services/RumSessionPinService.ts +7 -21
- package/Server/Services/StatusPageSubscriberService.ts +13 -0
- package/Server/Services/UserService.ts +280 -9
- package/Server/Static/Vendor/README.md +62 -0
- package/Server/Static/Vendor/fonts/InterVariable.woff2 +0 -0
- package/Server/Static/Vendor/highlight/highlight.min.js +1244 -0
- package/Server/Static/Vendor/highlight/languages/apache.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/bash.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/c.min.js +41 -0
- package/Server/Static/Vendor/highlight/languages/cpp.min.js +47 -0
- package/Server/Static/Vendor/highlight/languages/csharp.min.js +49 -0
- package/Server/Static/Vendor/highlight/languages/css.min.js +31 -0
- package/Server/Static/Vendor/highlight/languages/diff.min.js +9 -0
- package/Server/Static/Vendor/highlight/languages/dns.min.js +11 -0
- package/Server/Static/Vendor/highlight/languages/dockerfile.min.js +8 -0
- package/Server/Static/Vendor/highlight/languages/go.min.js +20 -0
- package/Server/Static/Vendor/highlight/languages/graphql.min.js +12 -0
- package/Server/Static/Vendor/highlight/languages/http.min.js +14 -0
- package/Server/Static/Vendor/highlight/languages/ini.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/java.min.js +38 -0
- package/Server/Static/Vendor/highlight/languages/javascript.min.js +81 -0
- package/Server/Static/Vendor/highlight/languages/json.min.js +8 -0
- package/Server/Static/Vendor/highlight/languages/kotlin.min.js +46 -0
- package/Server/Static/Vendor/highlight/languages/lua.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/makefile.min.js +14 -0
- package/Server/Static/Vendor/highlight/languages/nginx.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/perl.min.js +41 -0
- package/Server/Static/Vendor/highlight/languages/php.min.js +59 -0
- package/Server/Static/Vendor/highlight/languages/powershell.min.js +39 -0
- package/Server/Static/Vendor/highlight/languages/python.min.js +42 -0
- package/Server/Static/Vendor/highlight/languages/r.min.js +26 -0
- package/Server/Static/Vendor/highlight/languages/ruby.min.js +54 -0
- package/Server/Static/Vendor/highlight/languages/rust.min.js +27 -0
- package/Server/Static/Vendor/highlight/languages/scala.min.js +28 -0
- package/Server/Static/Vendor/highlight/languages/sql.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/swift.min.js +68 -0
- package/Server/Static/Vendor/highlight/languages/typescript.min.js +99 -0
- package/Server/Static/Vendor/highlight/languages/xml.min.js +29 -0
- package/Server/Static/Vendor/highlight/languages/yaml.min.js +28 -0
- package/Server/Static/Vendor/highlight/styles/github-dark.min.css +10 -0
- package/Server/Static/Vendor/highlight/styles/vs2015.min.css +1 -0
- package/Server/Static/Vendor/tailwind/tailwind-3.4.5.js +21028 -0
- package/Server/Types/Database/Permissions/BasePermission.ts +7 -0
- package/Server/Types/Database/Permissions/DeletePermission.ts +2 -0
- package/Server/Types/Database/Permissions/ReadPermission.ts +3 -0
- package/Server/Types/Database/Permissions/TenantPermission.ts +101 -16
- package/Server/Utils/AI/CodeFix/FixRunBudget.ts +2 -2
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +90 -11
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +1 -0
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +1 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.ts +125 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +25 -15
- package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +256 -57
- package/Server/Utils/AI/SRE/Insights/InsightStore.ts +76 -18
- package/Server/Utils/AI/SRE/InvestigationTldr.ts +352 -0
- package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +7 -2
- package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +1793 -0
- package/Server/Utils/Dashboard/PublicDashboardViewConfig.ts +65 -0
- package/Server/Utils/DataSource/ConnectorRegistry.ts +53 -0
- package/Server/Utils/DataSource/Connectors/ClickHouseConnector.ts +338 -0
- package/Server/Utils/DataSource/Connectors/ElasticsearchConnector.ts +717 -0
- package/Server/Utils/DataSource/Connectors/LokiConnector.ts +606 -0
- package/Server/Utils/DataSource/Connectors/MySqlConnector.ts +307 -0
- package/Server/Utils/DataSource/Connectors/PostgresConnector.ts +260 -0
- package/Server/Utils/DataSource/Connectors/PrometheusConnector.ts +555 -0
- package/Server/Utils/DataSource/Connectors/RestApiConnector.ts +597 -0
- package/Server/Utils/DataSource/Connectors/SqlServerConnector.ts +318 -0
- package/Server/Utils/DataSource/EgressGuard.ts +446 -0
- package/Server/Utils/DataSource/HttpFetch.ts +221 -0
- package/Server/Utils/DataSource/SqlQueryGuard.ts +383 -0
- package/Server/Utils/DataSource/SqlResultShaper.ts +372 -0
- package/Server/Utils/DataSource/Types.ts +93 -0
- package/Server/Utils/Database/PostgresErrorTranslator.ts +172 -17
- package/Server/Utils/InProcessMemo.ts +141 -0
- package/Server/Utils/InstanceHealth/TelemetryIngestion.ts +628 -0
- package/Server/Utils/Monitor/MonitorMetricUtil.ts +58 -0
- package/Server/Utils/Monitor/MonitorResource.ts +14 -0
- package/Server/Utils/Response.ts +12 -1
- package/Server/Utils/SSRFProtection.ts +237 -0
- package/Server/Utils/StartServer.ts +8 -0
- package/Server/Utils/StatusPageSubscriberWebhook.ts +30 -0
- package/Server/Utils/Telemetry/CaptureSpan.ts +30 -18
- package/Server/Utils/Telemetry/IngestionTimestamp.ts +75 -0
- package/Server/Utils/Telemetry/ResourceHeartbeat.ts +99 -7
- package/Server/Utils/Telemetry/Telemetry.ts +112 -1
- package/Server/Utils/Telemetry.ts +104 -3
- package/Server/Utils/VendorAssets.ts +165 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +28 -5
- package/Server/Utils/Workspace/Slack/Slack.ts +5 -0
- package/Server/Views/Partials/Head.ejs +21 -7
- package/Tests/App/AdminDashboard/AdminProjectsIdFilter.test.tsx +207 -0
- package/Tests/App/AdminDashboard/AdminUsersIdFilter.test.tsx +216 -0
- package/Tests/App/Dashboard/AIInsightViewActions.test.tsx +422 -0
- package/Tests/App/Dashboard/AddWidgetModal.test.tsx +10 -3
- package/Tests/App/Dashboard/AlertsSideMenu.test.tsx +387 -0
- package/Tests/App/Dashboard/ChatActivityFeed.test.tsx +199 -0
- package/Tests/App/Dashboard/DataSourceChartSyntheticConfig.test.tsx +736 -0
- package/Tests/App/Dashboard/DataSourceQueryEditor.test.tsx +868 -0
- package/Tests/App/Dashboard/DataSourceWidgetCatalog.test.ts +440 -0
- package/Tests/App/Dashboard/DataSourceWidgetFetching.test.tsx +2109 -0
- package/Tests/App/Dashboard/DataSourceWidgetViews.test.tsx +726 -0
- package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +447 -0
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +514 -3
- package/Tests/App/Dashboard/ProductMenuBreadcrumbs.test.tsx +391 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceSideMenu.test.tsx +326 -0
- package/Tests/App/Dashboard/SideMenuHarness.tsx +187 -0
- package/Tests/App/Dashboard/WidgetCatalog.test.ts +6 -1
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +1858 -0
- package/Tests/Models/DatabaseModels/DataSourceModelSecrets.test.ts +70 -0
- package/Tests/Server/API/AIInvestigationAPI.test.ts +187 -0
- package/Tests/Server/API/DashboardPublicDataSourceStrip.test.ts +686 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +585 -823
- package/Tests/Server/API/DataSourceAPIRoutes.test.ts +486 -0
- package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +291 -0
- package/Tests/Server/API/UserAuthenticationAPI.test.ts +641 -0
- package/Tests/Server/Infrastructure/ClickhouseKeepAliveOptions.test.ts +441 -0
- package/Tests/Server/Middleware/TelemetryIngestTokenLog.test.ts +140 -0
- package/Tests/Server/Services/AIInsightService.test.ts +266 -11
- package/Tests/Server/Services/AIRunAnalysisTldrPersistence.test.ts +160 -0
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +4 -0
- package/Tests/Server/Services/AlertMetricResourceAttributes.test.ts +352 -0
- package/Tests/Server/Services/DataSourceServiceSettings.test.ts +244 -0
- package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +397 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +41 -2
- package/Tests/Server/Services/IncidentMetricResourceAttributes.test.ts +408 -0
- package/Tests/Server/Services/MonitorStatusServiceDeleteReferenceCleanup.test.ts +351 -0
- package/Tests/Server/Services/MonitorStatusServiceValidation.test.ts +171 -0
- package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +398 -0
- package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +19 -0
- package/Tests/Server/Services/ProjectCallSMSConfigCredentialUniqueness.test.ts +305 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +36 -1
- package/Tests/Server/Services/RumSessionPinUniqueViolationRecovery.test.ts +163 -0
- package/Tests/Server/Services/UserAuthenticationService.test.ts +973 -0
- package/Tests/Server/Types/Database/ObjectIdColumnQuery.test.ts +262 -0
- package/Tests/Server/Types/Database/Permissions/TenantPermission.test.ts +358 -0
- package/Tests/Server/Utils/AI/FixRunBudget.test.ts +2 -2
- package/Tests/Server/Utils/AI/Insights/ExceptionSpikeDetector.test.ts +6 -1
- package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +121 -4
- package/Tests/Server/Utils/AI/Insights/NewExceptionDetector.test.ts +361 -10
- package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +292 -0
- package/Tests/Server/Utils/AI/InvestigationTldr.test.ts +623 -0
- package/Tests/Server/Utils/AI/InvestigationTldrWiring.test.ts +98 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +857 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardViewConfig.test.ts +480 -0
- package/Tests/Server/Utils/DataSource/ClickHouseConnector.test.ts +759 -0
- package/Tests/Server/Utils/DataSource/ConnectorRegistry.test.ts +37 -0
- package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +645 -0
- package/Tests/Server/Utils/DataSource/ElasticsearchConnector.test.ts +1206 -0
- package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +616 -0
- package/Tests/Server/Utils/DataSource/LokiConnector.test.ts +970 -0
- package/Tests/Server/Utils/DataSource/MySqlConnector.test.ts +935 -0
- package/Tests/Server/Utils/DataSource/PostgresConnector.test.ts +854 -0
- package/Tests/Server/Utils/DataSource/PrometheusConnector.test.ts +881 -0
- package/Tests/Server/Utils/DataSource/RestApiConnector.test.ts +1020 -0
- package/Tests/Server/Utils/DataSource/SqlQueryGuard.test.ts +425 -0
- package/Tests/Server/Utils/DataSource/SqlResultShaper.test.ts +555 -0
- package/Tests/Server/Utils/DataSource/SqlServerConnector.test.ts +934 -0
- package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +228 -7
- package/Tests/Server/Utils/InProcessMemo.test.ts +292 -0
- package/Tests/Server/Utils/InstanceHealth/TelemetryIngestion.test.ts +960 -0
- package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +103 -0
- package/Tests/Server/Utils/Monitor/MonitorMetricResourceAttributes.test.ts +336 -0
- package/Tests/Server/Utils/OfflineAssetHygiene.test.ts +521 -0
- package/Tests/Server/Utils/ResponseRenderAnalytics.test.ts +143 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +193 -0
- package/Tests/Server/Utils/StatusPageSubscriberWebhook.test.ts +101 -0
- package/Tests/Server/Utils/TailwindBuildCopy.test.ts +225 -0
- package/Tests/Server/Utils/Telemetry/ResourceHeartbeatL1Memo.test.ts +355 -0
- package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +128 -0
- package/Tests/Server/Utils/Telemetry/TelemetryUtilAttributeKeys.test.ts +381 -0
- package/Tests/Server/Utils/Telemetry.test.ts +87 -0
- package/Tests/Server/Utils/VendorAssetIntegrity.test.ts +343 -0
- package/Tests/Server/Utils/VendorAssets.test.ts +671 -0
- package/Tests/Server/Views/OfflineRender.test.ts +297 -0
- package/Tests/Types/Dashboard/DataSourceComponentTypes.test.ts +618 -0
- package/Tests/Types/DateClickhouseFormat.test.ts +192 -0
- package/Tests/Types/Password.test.ts +363 -0
- package/Tests/UI/Components/ConfirmModal.test.tsx +7 -0
- package/Tests/UI/Components/ErrorBoundary.test.tsx +53 -0
- package/Tests/UI/Components/Modal.test.tsx +281 -1
- package/Tests/UI/Components/ObjectIdFilterPipeline.test.tsx +396 -0
- package/Tests/UI/Components/StatusPage/ResourceGroupNavigator.test.tsx +1147 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +116 -1
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
- package/Tests/Utils/Analytics.test.ts +23 -0
- package/Tests/Utils/Dashboard/Components/DashboardDataSourceComponents.test.ts +640 -0
- package/Tests/Utils/DataSource/DataSourceQueryText.test.ts +138 -0
- package/Tests/Utils/DataSource/DataSourceValueReducer.test.ts +526 -0
- package/Tests/Utils/Metrics/MetricResourceAttributeUtil.test.ts +691 -0
- package/Tests/Utils/StatusPage/GroupHierarchyView.test.ts +1452 -0
- package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +931 -0
- package/Tests/Utils/Telemetry/CaptureSpanExportGating.test.ts +288 -0
- package/Tests/Utils/Telemetry/IngestionTimestamp.test.ts +170 -0
- package/Types/AI/AIInsightEvidence.ts +6 -0
- package/Types/Analytics/RevenueEvent.ts +31 -0
- package/Types/Dashboard/DashboardComponentType.ts +31 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.ts +27 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.ts +32 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.ts +30 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.ts +55 -0
- package/Types/DataSource/DataSourceLimits.ts +62 -0
- package/Types/DataSource/DataSourceQueryConfig.ts +41 -0
- package/Types/DataSource/DataSourceTableResult.ts +38 -0
- package/Types/DataSource/DataSourceType.ts +213 -0
- package/Types/Date.ts +43 -3
- package/Types/Password.ts +64 -0
- package/Types/Permission.ts +46 -0
- package/Types/UserAuthenticationStatus.ts +34 -0
- package/UI/Components/Button/Button.tsx +1 -1
- package/UI/Components/ErrorBoundary.tsx +459 -108
- package/UI/Components/Icon/Icon.tsx +10 -9
- package/UI/Components/Modal/ConfirmModal.tsx +6 -1
- package/UI/Components/Modal/Modal.tsx +267 -35
- package/UI/Components/Modal/ModalFooter.tsx +6 -1
- package/UI/Components/MoreMenu/MoreMenu.tsx +19 -4
- package/UI/Components/MoreMenu/MoreMenuItem.tsx +6 -1
- package/UI/Components/StatusPage/ResourceGrid.tsx +314 -0
- package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +808 -0
- package/UI/Components/StatusPage/ResourceList.tsx +570 -0
- package/UI/Styles/Theme.css +18 -0
- package/UI/Utils/API/RequestOptions.ts +7 -0
- package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +1 -0
- package/UI/Utils/ModelAPI/ModelAPI.ts +1 -0
- package/UI/Utils/TestDataSource.ts +61 -0
- package/UI/esbuild-config.js +45 -0
- package/Utils/Analytics.ts +15 -0
- package/Utils/Dashboard/Components/DashboardDataSourceChartComponent.ts +88 -0
- package/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.ts +123 -0
- package/Utils/Dashboard/Components/DashboardDataSourceShared.ts +16 -0
- package/Utils/Dashboard/Components/DashboardDataSourceTableComponent.ts +72 -0
- package/Utils/Dashboard/Components/DashboardDataSourceValueComponent.ts +126 -0
- package/Utils/Dashboard/Components/Index.ts +28 -0
- package/Utils/DataSource/DataSourceQueryText.ts +94 -0
- package/Utils/DataSource/DataSourceValueReducer.ts +156 -0
- package/Utils/Metrics/MetricResourceAttributeUtil.ts +380 -0
- package/Utils/NetworkDiscovery/ScanTargetUtil.ts +8 -6
- package/Utils/StatusPage/GroupHierarchyView.ts +931 -0
- package/Utils/StatusPage/ResourceExplorer.ts +853 -0
- package/build/dist/Models/DatabaseModels/AIRun.js +34 -0
- package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DataSource.js +774 -0
- package/build/dist/Models/DatabaseModels/DataSource.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js +14 -4
- package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +2 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js +2 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +2 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSession.js +2 -0
- package/build/dist/Models/DatabaseModels/UserSession.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserTotpAuth.js +6 -4
- package/build/dist/Models/DatabaseModels/UserTotpAuth.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWebAuthn.js +5 -3
- package/build/dist/Models/DatabaseModels/UserWebAuthn.js.map +1 -1
- package/build/dist/Server/API/AIInsightAPI.js +34 -4
- package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +23 -0
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +119 -433
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/DataSourceAPI.js +173 -0
- package/build/dist/Server/API/DataSourceAPI.js.map +1 -0
- package/build/dist/Server/API/StatusPageAPI.js +11 -1
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +94 -2
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js +49 -0
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js +19 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js +64 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +27 -0
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Infrastructure/QueueWorker.js +13 -0
- package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +8 -3
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AIInsightService.js +110 -10
- package/build/dist/Server/Services/AIInsightService.js.map +1 -1
- package/build/dist/Server/Services/AIRunService.js +34 -0
- package/build/dist/Server/Services/AIRunService.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +16 -2
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +28 -35
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +8 -4
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DataSourceService.js +159 -0
- package/build/dist/Server/Services/DataSourceService.js.map +1 -0
- package/build/dist/Server/Services/HostService.js +74 -0
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +35 -24
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +45 -0
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusService.js +129 -0
- package/build/dist/Server/Services/MonitorStatusService.js.map +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +222 -159
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/RumSessionPinService.js +7 -15
- package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +10 -0
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +241 -10
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js +6 -0
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/DeletePermission.js +1 -0
- package/build/dist/Server/Types/Database/Permissions/DeletePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +2 -0
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +68 -12
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +2 -2
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +68 -8
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +1 -0
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +1 -0
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js +97 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +17 -15
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +178 -54
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js +65 -16
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js +303 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js.map +1 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -1
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +1261 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js +47 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js +42 -0
- package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js +187 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js +493 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js +399 -0
- package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js +195 -0
- package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js +149 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js +362 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js +342 -0
- package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js +186 -0
- package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js +305 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/HttpFetch.js +134 -0
- package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js +331 -0
- package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/SqlResultShaper.js +280 -0
- package/build/dist/Server/Utils/DataSource/SqlResultShaper.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Types.js +2 -0
- package/build/dist/Server/Utils/DataSource/Types.js.map +1 -0
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +103 -9
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -1
- package/build/dist/Server/Utils/InProcessMemo.js +113 -0
- package/build/dist/Server/Utils/InProcessMemo.js.map +1 -0
- package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js +406 -0
- package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +34 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +14 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Response.js +9 -1
- package/build/dist/Server/Utils/Response.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +192 -0
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -0
- package/build/dist/Server/Utils/StartServer.js +7 -0
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js +24 -0
- package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/CaptureSpan.js +25 -11
- package/build/dist/Server/Utils/Telemetry/CaptureSpan.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js +32 -0
- package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +92 -5
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +92 -6
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry.js +93 -6
- package/build/dist/Server/Utils/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/VendorAssets.js +121 -0
- package/build/dist/Server/Utils/VendorAssets.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -4
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +5 -0
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Types/Analytics/RevenueEvent.js +26 -0
- package/build/dist/Types/Analytics/RevenueEvent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js +27 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js +16 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceLimits.js +47 -0
- package/build/dist/Types/DataSource/DataSourceLimits.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceQueryConfig.js +10 -0
- package/build/dist/Types/DataSource/DataSourceQueryConfig.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceTableResult.js +14 -0
- package/build/dist/Types/DataSource/DataSourceTableResult.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceType.js +183 -0
- package/build/dist/Types/DataSource/DataSourceType.js.map +1 -0
- package/build/dist/Types/Date.js +38 -3
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Password.js +56 -0
- package/build/dist/Types/Password.js.map +1 -0
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/UserAuthenticationStatus.js +2 -0
- package/build/dist/Types/UserAuthenticationStatus.js.map +1 -0
- package/build/dist/UI/Components/ErrorBoundary.js +388 -72
- package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +10 -4
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js +1 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +178 -19
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js +3 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +9 -6
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +7 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +119 -0
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +354 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -0
- package/build/dist/UI/Components/StatusPage/ResourceList.js +203 -0
- package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -0
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +1 -6
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +3 -8
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/TestDataSource.js +39 -0
- package/build/dist/UI/Utils/TestDataSource.js.map +1 -0
- package/build/dist/Utils/Analytics.js +4 -0
- package/build/dist/Utils/Analytics.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js +79 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js +111 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js +15 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js +62 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js +112 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +16 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/build/dist/Utils/DataSource/DataSourceQueryText.js +64 -0
- package/build/dist/Utils/DataSource/DataSourceQueryText.js.map +1 -0
- package/build/dist/Utils/DataSource/DataSourceValueReducer.js +124 -0
- package/build/dist/Utils/DataSource/DataSourceValueReducer.js.map +1 -0
- package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js +281 -0
- package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +8 -6
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
- package/build/dist/Utils/StatusPage/GroupHierarchyView.js +560 -0
- package/build/dist/Utils/StatusPage/GroupHierarchyView.js.map +1 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js +519 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -0
- package/jest.config.json +1 -0
- package/package.json +5 -1
- package/tsconfig.json +12 -10
|
@@ -9,7 +9,13 @@ import Service from "../../../../../../Models/DatabaseModels/Service";
|
|
|
9
9
|
import TelemetryExceptionService from "../../../../../Services/TelemetryExceptionService";
|
|
10
10
|
import ServiceService from "../../../../../Services/ServiceService";
|
|
11
11
|
import QueryHelper from "../../../../../Types/Database/QueryHelper";
|
|
12
|
+
import logger from "../../../../Logger";
|
|
12
13
|
import { sanitizeExceptionMessage } from "../../../../Telemetry/ExceptionSanitizer";
|
|
14
|
+
import {
|
|
15
|
+
EXCEPTION_LABEL_MESSAGE_MAX_LENGTH,
|
|
16
|
+
buildExceptionFailureModeKey,
|
|
17
|
+
buildExceptionLabel,
|
|
18
|
+
} from "./ExceptionIdentity";
|
|
13
19
|
import {
|
|
14
20
|
InsightCandidate,
|
|
15
21
|
InsightDetector,
|
|
@@ -23,6 +29,18 @@ import {
|
|
|
23
29
|
* the exception groups table carries firstSeenAt and the lifetime
|
|
24
30
|
* occurrence count, so no ClickHouse scan is needed at all. Deterministic —
|
|
25
31
|
* no LLM anywhere in this detector.
|
|
32
|
+
*
|
|
33
|
+
* ONE INSIGHT PER FAILURE MODE, NOT PER EXCEPTION GROUP. Group identity at
|
|
34
|
+
* ingest hashes the normalized STACK TRACE alongside the type and message, so
|
|
35
|
+
* one logical failure raised from several call sites is several groups. This
|
|
36
|
+
* detector used to file one insight per group, which filed the same finding
|
|
37
|
+
* over and over — and because the title was the exceptionType alone, the rows
|
|
38
|
+
* were not merely duplicated but indistinguishable (a service whose
|
|
39
|
+
* instrumentation reports `exception.type = "401"` produced a wall of
|
|
40
|
+
* identical "New exception: 401 in api" rows). Candidates are therefore
|
|
41
|
+
* bucketed on the (entity, type, normalized message) failure mode, counts are
|
|
42
|
+
* summed across the member groups, and the insight is fingerprinted on that
|
|
43
|
+
* bucket — see ExceptionIdentity.
|
|
26
44
|
*/
|
|
27
45
|
|
|
28
46
|
/*
|
|
@@ -36,38 +54,72 @@ export const NEW_EXCEPTION_LOOKBACK_HOURS: number = 24;
|
|
|
36
54
|
/*
|
|
37
55
|
* A single occurrence is noise (a deploy hiccup, a one-off bad request).
|
|
38
56
|
* Three occurrences means the failure path is being re-hit — worth a
|
|
39
|
-
* human's attention.
|
|
57
|
+
* human's attention. Applied per exception GROUP, before bucketing: a
|
|
58
|
+
* failure mode must have at least one group that is genuinely recurring,
|
|
59
|
+
* rather than qualifying by adding up single-occurrence stack variants.
|
|
40
60
|
*/
|
|
41
61
|
export const NEW_EXCEPTION_MIN_OCCURRENCE_COUNT: number = 3;
|
|
42
62
|
|
|
43
63
|
/*
|
|
44
64
|
* At 50+ occurrences inside its first day the exception is hammering a hot
|
|
45
|
-
* path — escalate to High so it sorts above the long tail.
|
|
65
|
+
* path — escalate to High so it sorts above the long tail. Measured on the
|
|
66
|
+
* failure mode's summed count, which is what the insight reports.
|
|
46
67
|
*/
|
|
47
68
|
export const NEW_EXCEPTION_HIGH_SEVERITY_OCCURRENCE_COUNT: number = 50;
|
|
48
69
|
|
|
49
70
|
/*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
71
|
+
* How many exception GROUPS one tick reads. Higher than the candidate cap
|
|
72
|
+
* because groups collapse into failure modes: a single noisy throw site can
|
|
73
|
+
* easily own dozens of stack-trace variants, and reading only 25 of them
|
|
74
|
+
* would let one loud failure mode crowd every other one out of the tick. It
|
|
75
|
+
* is one indexed Postgres query either way.
|
|
76
|
+
*/
|
|
77
|
+
export const NEW_EXCEPTION_GROUP_SCAN_LIMIT: number = 100;
|
|
78
|
+
|
|
79
|
+
/*
|
|
80
|
+
* Upper bound on candidates per scan tick, applied AFTER bucketing — a
|
|
81
|
+
* project melting down does not need 500 insights, and the scanner
|
|
52
82
|
* additionally caps new insights per tick.
|
|
53
83
|
*/
|
|
54
84
|
export const NEW_EXCEPTION_CANDIDATE_LIMIT: number = 25;
|
|
55
85
|
|
|
56
86
|
// Titles must stay scannable in a list — truncate long exception messages.
|
|
57
|
-
export const NEW_EXCEPTION_TITLE_MESSAGE_MAX_LENGTH: number =
|
|
87
|
+
export const NEW_EXCEPTION_TITLE_MESSAGE_MAX_LENGTH: number =
|
|
88
|
+
EXCEPTION_LABEL_MESSAGE_MAX_LENGTH;
|
|
58
89
|
|
|
59
90
|
export interface NewExceptionDecision {
|
|
60
91
|
qualifies: boolean;
|
|
61
92
|
severity: AIInsightSeverity;
|
|
62
93
|
}
|
|
63
94
|
|
|
95
|
+
/*
|
|
96
|
+
* One failure mode: the exception groups that share an (entity, type,
|
|
97
|
+
* normalized message) identity, rolled up into the numbers the insight
|
|
98
|
+
* reports.
|
|
99
|
+
*/
|
|
100
|
+
export interface NewExceptionFailureMode {
|
|
101
|
+
failureModeKey: string;
|
|
102
|
+
// The loudest member group — what the insight links to and labels itself by.
|
|
103
|
+
representative: TelemetryException;
|
|
104
|
+
// The representative's id, narrowed once at bucket time.
|
|
105
|
+
representativeExceptionId: ObjectID;
|
|
106
|
+
// Summed lifetime occurrences across every member group.
|
|
107
|
+
totalOccurrenceCount: number;
|
|
108
|
+
// How many distinct exception groups (stack variants) rolled up here.
|
|
109
|
+
groupCount: number;
|
|
110
|
+
// Earliest firstSeenAt across the member groups.
|
|
111
|
+
firstSeenAt: Date | undefined;
|
|
112
|
+
// The representative's message, sanitized once.
|
|
113
|
+
safeMessage: string;
|
|
114
|
+
}
|
|
115
|
+
|
|
64
116
|
export default class NewExceptionDetector implements InsightDetector {
|
|
65
117
|
public insightType: AIInsightType = AIInsightType.NewException;
|
|
66
118
|
|
|
67
119
|
/*
|
|
68
|
-
* Pure decision: does
|
|
69
|
-
*
|
|
70
|
-
*
|
|
120
|
+
* Pure decision: does an occurrence count qualify, and how urgent is it.
|
|
121
|
+
* The query already filters on the minimum count — this re-check keeps the
|
|
122
|
+
* rule in one unit-testable place.
|
|
71
123
|
*/
|
|
72
124
|
public static evaluateNewException(
|
|
73
125
|
occurrenceCount: number,
|
|
@@ -82,33 +134,28 @@ export default class NewExceptionDetector implements InsightDetector {
|
|
|
82
134
|
}
|
|
83
135
|
|
|
84
136
|
/*
|
|
85
|
-
* Human label for the exception: the type
|
|
86
|
-
*
|
|
137
|
+
* Human label for the exception: the type AND the message, because either
|
|
138
|
+
* one alone collides across findings (see ExceptionIdentity).
|
|
87
139
|
*/
|
|
88
140
|
public static buildExceptionLabel(
|
|
89
141
|
exceptionType: string | undefined,
|
|
90
142
|
message: string | undefined,
|
|
91
143
|
): string {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return "Unknown exception";
|
|
98
|
-
}
|
|
99
|
-
if (trimmedMessage.length <= NEW_EXCEPTION_TITLE_MESSAGE_MAX_LENGTH) {
|
|
100
|
-
return trimmedMessage;
|
|
101
|
-
}
|
|
102
|
-
return `${trimmedMessage.slice(0, NEW_EXCEPTION_TITLE_MESSAGE_MAX_LENGTH)}…`;
|
|
144
|
+
return buildExceptionLabel(
|
|
145
|
+
exceptionType,
|
|
146
|
+
message,
|
|
147
|
+
NEW_EXCEPTION_TITLE_MESSAGE_MAX_LENGTH,
|
|
148
|
+
);
|
|
103
149
|
}
|
|
104
150
|
|
|
105
151
|
/*
|
|
106
|
-
* Stable dedupe key. Keyed on the
|
|
107
|
-
*
|
|
108
|
-
*
|
|
152
|
+
* Stable dedupe key. Keyed on the FAILURE MODE (entity + exception type +
|
|
153
|
+
* normalized message), never on the exception group id: group identity
|
|
154
|
+
* includes the stack trace, so one failure raised from several call sites
|
|
155
|
+
* would otherwise file one insight per call site.
|
|
109
156
|
*/
|
|
110
|
-
public static buildFingerprint(
|
|
111
|
-
return `new-exception:${
|
|
157
|
+
public static buildFingerprint(failureModeKey: string): string {
|
|
158
|
+
return `new-exception:${failureModeKey}`;
|
|
112
159
|
}
|
|
113
160
|
|
|
114
161
|
public async detect(
|
|
@@ -132,6 +179,13 @@ export default class NewExceptionDetector implements InsightDetector {
|
|
|
132
179
|
/*
|
|
133
180
|
* Root read with an explicit projectId in the query — the scanner is a
|
|
134
181
|
* server-side worker (monitor-worker precedent), never a user ACL.
|
|
182
|
+
*
|
|
183
|
+
* firstSeenAt is a deterministic tie-break on the LIMIT boundary:
|
|
184
|
+
* occuranceCount is incremented on every ingested event, so the long
|
|
185
|
+
* tail of a noisy endpoint sits densely tied right at the cut and an
|
|
186
|
+
* untie-broken ORDER BY hands back an arbitrary, shifting subset each
|
|
187
|
+
* tick. Bucketing already keeps that churn from filing duplicates; the
|
|
188
|
+
* tie-break keeps the tick itself reproducible.
|
|
135
189
|
*/
|
|
136
190
|
const exceptions: Array<TelemetryException> =
|
|
137
191
|
await TelemetryExceptionService.findBy({
|
|
@@ -146,44 +200,43 @@ export default class NewExceptionDetector implements InsightDetector {
|
|
|
146
200
|
},
|
|
147
201
|
sort: {
|
|
148
202
|
occuranceCount: SortOrder.Descending,
|
|
203
|
+
firstSeenAt: SortOrder.Ascending,
|
|
149
204
|
},
|
|
150
|
-
limit:
|
|
205
|
+
limit: NEW_EXCEPTION_GROUP_SCAN_LIMIT,
|
|
151
206
|
skip: 0,
|
|
152
207
|
props: { isRoot: true },
|
|
153
208
|
});
|
|
154
209
|
|
|
210
|
+
const failureModes: Array<NewExceptionFailureMode> =
|
|
211
|
+
NewExceptionDetector.bucketIntoFailureModes(exceptions);
|
|
212
|
+
|
|
155
213
|
const candidates: Array<InsightCandidate> = [];
|
|
156
214
|
|
|
157
|
-
|
|
158
|
-
|
|
215
|
+
// Service names are resolved once per entity, not once per group.
|
|
216
|
+
const serviceNameCache: Map<string, string | null> = new Map();
|
|
217
|
+
|
|
218
|
+
for (const failureMode of failureModes) {
|
|
159
219
|
const decision: NewExceptionDecision =
|
|
160
|
-
NewExceptionDetector.evaluateNewException(
|
|
220
|
+
NewExceptionDetector.evaluateNewException(
|
|
221
|
+
failureMode.totalOccurrenceCount,
|
|
222
|
+
);
|
|
161
223
|
|
|
162
|
-
if (!decision.qualifies
|
|
224
|
+
if (!decision.qualifies) {
|
|
163
225
|
continue;
|
|
164
226
|
}
|
|
165
227
|
|
|
166
228
|
const serviceName: string | null = await this.resolveServiceName(
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
/*
|
|
171
|
-
* The message flows into the insight title/detail/evidence, and from
|
|
172
|
-
* there into the triage LLM prompt — sanitize it once here so no
|
|
173
|
-
* interpolated user data (IDs, emails, domains) rides along. The raw
|
|
174
|
-
* message stays on the exception row for the dashboard.
|
|
175
|
-
*/
|
|
176
|
-
const safeMessage: string = sanitizeExceptionMessage(
|
|
177
|
-
exception.message || "",
|
|
229
|
+
failureMode.representative.primaryEntityId,
|
|
230
|
+
serviceNameCache,
|
|
178
231
|
);
|
|
179
232
|
|
|
180
233
|
const label: string = NewExceptionDetector.buildExceptionLabel(
|
|
181
|
-
|
|
182
|
-
safeMessage,
|
|
234
|
+
failureMode.representative.exceptionType,
|
|
235
|
+
failureMode.safeMessage,
|
|
183
236
|
);
|
|
184
237
|
|
|
185
|
-
const firstSeenAtIso: string | undefined =
|
|
186
|
-
?
|
|
238
|
+
const firstSeenAtIso: string | undefined = failureMode.firstSeenAt
|
|
239
|
+
? failureMode.firstSeenAt.toISOString()
|
|
187
240
|
: undefined;
|
|
188
241
|
|
|
189
242
|
const detailLines: Array<string> = [
|
|
@@ -191,59 +244,205 @@ export default class NewExceptionDetector implements InsightDetector {
|
|
|
191
244
|
"",
|
|
192
245
|
`- Exception: \`${label}\``,
|
|
193
246
|
];
|
|
194
|
-
if (safeMessage) {
|
|
195
|
-
detailLines.push(`- Message: ${safeMessage}`);
|
|
247
|
+
if (failureMode.safeMessage) {
|
|
248
|
+
detailLines.push(`- Message: ${failureMode.safeMessage}`);
|
|
196
249
|
}
|
|
197
250
|
if (serviceName) {
|
|
198
251
|
detailLines.push(`- Service: ${serviceName}`);
|
|
199
252
|
}
|
|
200
253
|
detailLines.push(
|
|
201
254
|
`- First seen: ${firstSeenAtIso || "unknown"} (within the last ${NEW_EXCEPTION_LOOKBACK_HOURS}h)`,
|
|
202
|
-
`- Occurrences so far: ${
|
|
255
|
+
`- Occurrences so far: ${failureMode.totalOccurrenceCount}`,
|
|
203
256
|
);
|
|
257
|
+
if (failureMode.groupCount > 1) {
|
|
258
|
+
detailLines.push(
|
|
259
|
+
`- Raised from ${failureMode.groupCount} distinct stack traces (same exception type and message — counted together as one failure mode)`,
|
|
260
|
+
);
|
|
261
|
+
}
|
|
204
262
|
|
|
205
263
|
candidates.push({
|
|
206
264
|
insightType: AIInsightType.NewException,
|
|
207
265
|
fingerprint: NewExceptionDetector.buildFingerprint(
|
|
208
|
-
|
|
266
|
+
failureMode.failureModeKey,
|
|
209
267
|
),
|
|
210
268
|
title: `New exception: ${label}${serviceName ? ` in ${serviceName}` : ""}`,
|
|
211
269
|
detailMarkdown: detailLines.join("\n"),
|
|
212
270
|
severity: decision.severity,
|
|
213
271
|
serviceName: serviceName || undefined,
|
|
214
|
-
telemetryServiceId: serviceName
|
|
215
|
-
|
|
272
|
+
telemetryServiceId: serviceName
|
|
273
|
+
? failureMode.representative.primaryEntityId
|
|
274
|
+
: undefined,
|
|
275
|
+
telemetryExceptionId: failureMode.representativeExceptionId,
|
|
216
276
|
evidence: {
|
|
217
277
|
exception: {
|
|
218
|
-
exceptionMessage: safeMessage,
|
|
219
|
-
exceptionType:
|
|
220
|
-
totalOccurrenceCount:
|
|
278
|
+
exceptionMessage: failureMode.safeMessage,
|
|
279
|
+
exceptionType: failureMode.representative.exceptionType,
|
|
280
|
+
totalOccurrenceCount: failureMode.totalOccurrenceCount,
|
|
281
|
+
distinctExceptionGroupCount: failureMode.groupCount,
|
|
221
282
|
firstSeenAt: firstSeenAtIso,
|
|
222
283
|
},
|
|
223
284
|
},
|
|
224
285
|
});
|
|
286
|
+
|
|
287
|
+
if (candidates.length >= NEW_EXCEPTION_CANDIDATE_LIMIT) {
|
|
288
|
+
// No silent caps — say what this tick left on the floor.
|
|
289
|
+
if (failureModes.length > candidates.length) {
|
|
290
|
+
logger.debug(
|
|
291
|
+
`AI Insights: NewException capped at ${NEW_EXCEPTION_CANDIDATE_LIMIT} candidates for project ${context.projectId.toString()} — ${
|
|
292
|
+
failureModes.length - candidates.length
|
|
293
|
+
} further failure mode(s) this tick were not emitted. They re-surface on later ticks while their exceptions stay inside the ${NEW_EXCEPTION_LOOKBACK_HOURS}h window.`,
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
225
298
|
}
|
|
226
299
|
|
|
227
300
|
return candidates;
|
|
228
301
|
}
|
|
229
302
|
|
|
303
|
+
/*
|
|
304
|
+
* Roll the tick's exception groups up into failure modes, loudest first.
|
|
305
|
+
*
|
|
306
|
+
* The representative is the group with the highest lifetime occurrence
|
|
307
|
+
* count (ties broken by the earliest firstSeenAt, then by id, so the choice
|
|
308
|
+
* is deterministic rather than dependent on row order). It supplies the
|
|
309
|
+
* label and the exception the insight links to — but NOT the fingerprint,
|
|
310
|
+
* which is the bucket key, so a representative that changes as counts move
|
|
311
|
+
* can never file a second insight for the same failure mode.
|
|
312
|
+
*/
|
|
313
|
+
public static bucketIntoFailureModes(
|
|
314
|
+
exceptions: Array<TelemetryException>,
|
|
315
|
+
): Array<NewExceptionFailureMode> {
|
|
316
|
+
const buckets: Map<string, NewExceptionFailureMode> = new Map();
|
|
317
|
+
|
|
318
|
+
for (const exception of exceptions) {
|
|
319
|
+
const exceptionId: ObjectID | undefined = exception.id || undefined;
|
|
320
|
+
|
|
321
|
+
if (!exceptionId) {
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const failureModeKey: string = buildExceptionFailureModeKey({
|
|
326
|
+
primaryEntityId: exception.primaryEntityId,
|
|
327
|
+
exceptionType: exception.exceptionType,
|
|
328
|
+
message: exception.message,
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
const occurrenceCount: number = exception.occuranceCount || 0;
|
|
332
|
+
const existing: NewExceptionFailureMode | undefined =
|
|
333
|
+
buckets.get(failureModeKey);
|
|
334
|
+
|
|
335
|
+
if (!existing) {
|
|
336
|
+
buckets.set(failureModeKey, {
|
|
337
|
+
failureModeKey: failureModeKey,
|
|
338
|
+
representative: exception,
|
|
339
|
+
representativeExceptionId: exceptionId,
|
|
340
|
+
totalOccurrenceCount: occurrenceCount,
|
|
341
|
+
groupCount: 1,
|
|
342
|
+
firstSeenAt: exception.firstSeenAt,
|
|
343
|
+
/*
|
|
344
|
+
* The message flows into the insight title/detail/evidence, and
|
|
345
|
+
* from there into the triage LLM prompt — sanitize it once here so
|
|
346
|
+
* no interpolated user data (IDs, emails, domains) rides along. The
|
|
347
|
+
* raw message stays on the exception row for the dashboard.
|
|
348
|
+
*/
|
|
349
|
+
safeMessage: sanitizeExceptionMessage(exception.message || ""),
|
|
350
|
+
});
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
existing.totalOccurrenceCount += occurrenceCount;
|
|
355
|
+
existing.groupCount++;
|
|
356
|
+
|
|
357
|
+
if (
|
|
358
|
+
exception.firstSeenAt &&
|
|
359
|
+
(!existing.firstSeenAt || exception.firstSeenAt < existing.firstSeenAt)
|
|
360
|
+
) {
|
|
361
|
+
existing.firstSeenAt = exception.firstSeenAt;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (
|
|
365
|
+
NewExceptionDetector.isBetterRepresentative(
|
|
366
|
+
exception,
|
|
367
|
+
existing.representative,
|
|
368
|
+
)
|
|
369
|
+
) {
|
|
370
|
+
existing.representative = exception;
|
|
371
|
+
existing.representativeExceptionId = exceptionId;
|
|
372
|
+
existing.safeMessage = sanitizeExceptionMessage(
|
|
373
|
+
exception.message || "",
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/*
|
|
379
|
+
* Loudest first, ties broken on the key — the candidate cap cuts this
|
|
380
|
+
* list, so the order decides what a tick emits and it must not depend on
|
|
381
|
+
* the arbitrary order rows came back in.
|
|
382
|
+
*/
|
|
383
|
+
return Array.from(buckets.values()).sort(
|
|
384
|
+
(a: NewExceptionFailureMode, b: NewExceptionFailureMode): number => {
|
|
385
|
+
if (a.totalOccurrenceCount !== b.totalOccurrenceCount) {
|
|
386
|
+
return b.totalOccurrenceCount - a.totalOccurrenceCount;
|
|
387
|
+
}
|
|
388
|
+
return a.failureModeKey < b.failureModeKey ? -1 : 1;
|
|
389
|
+
},
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Loudest wins; ties go to the older group, then to the lower id.
|
|
394
|
+
private static isBetterRepresentative(
|
|
395
|
+
candidate: TelemetryException,
|
|
396
|
+
current: TelemetryException,
|
|
397
|
+
): boolean {
|
|
398
|
+
const candidateCount: number = candidate.occuranceCount || 0;
|
|
399
|
+
const currentCount: number = current.occuranceCount || 0;
|
|
400
|
+
|
|
401
|
+
if (candidateCount !== currentCount) {
|
|
402
|
+
return candidateCount > currentCount;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (candidate.firstSeenAt && current.firstSeenAt) {
|
|
406
|
+
const candidateTime: number = candidate.firstSeenAt.getTime();
|
|
407
|
+
const currentTime: number = current.firstSeenAt.getTime();
|
|
408
|
+
if (candidateTime !== currentTime) {
|
|
409
|
+
return candidateTime < currentTime;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return (candidate.id?.toString() || "") < (current.id?.toString() || "");
|
|
414
|
+
}
|
|
415
|
+
|
|
230
416
|
/*
|
|
231
417
|
* Best-effort service attribution: primaryEntityId is polymorphic
|
|
232
418
|
* (Service/Host/DockerHost/...), and findOneById simply returns null for
|
|
233
419
|
* the non-Service kinds (AIInvestigationAPI precedent) — the insight then
|
|
234
|
-
* ships without a service name.
|
|
420
|
+
* ships without a service name. Cached per tick: a project's exceptions
|
|
421
|
+
* cluster on a handful of services, and the lookup is the only per-
|
|
422
|
+
* candidate IO left in this detector.
|
|
235
423
|
*/
|
|
236
424
|
private async resolveServiceName(
|
|
237
425
|
primaryEntityId: ObjectID | undefined,
|
|
426
|
+
cache: Map<string, string | null>,
|
|
238
427
|
): Promise<string | null> {
|
|
239
428
|
if (!primaryEntityId) {
|
|
240
429
|
return null;
|
|
241
430
|
}
|
|
431
|
+
|
|
432
|
+
const cacheKey: string = primaryEntityId.toString();
|
|
433
|
+
const cached: string | null | undefined = cache.get(cacheKey);
|
|
434
|
+
if (cached !== undefined) {
|
|
435
|
+
return cached;
|
|
436
|
+
}
|
|
437
|
+
|
|
242
438
|
const service: Service | null = await ServiceService.findOneById({
|
|
243
439
|
id: primaryEntityId,
|
|
244
440
|
select: { name: true },
|
|
245
441
|
props: { isRoot: true },
|
|
246
442
|
});
|
|
247
|
-
|
|
443
|
+
|
|
444
|
+
const serviceName: string | null = service?.name || null;
|
|
445
|
+
cache.set(cacheKey, serviceName);
|
|
446
|
+
return serviceName;
|
|
248
447
|
}
|
|
249
448
|
}
|
|
@@ -33,11 +33,30 @@ export const MAX_NEW_INSIGHTS_PER_PROJECT_PER_SCAN: number = 10;
|
|
|
33
33
|
* G11 noise posture: a human dismissal is the strongest precision signal we
|
|
34
34
|
* have, so a finding with the same fingerprint stays suppressed for this
|
|
35
35
|
* many days after the dismissal instead of reappearing on the next tick.
|
|
36
|
-
* After the cooldown (or after a Resolved insight's signal returns) a fresh
|
|
37
|
-
* insight is warranted — a resolved issue that reappears is a regression.
|
|
38
36
|
*/
|
|
39
37
|
export const DISMISSED_COOLDOWN_DAYS: number = 7;
|
|
40
38
|
|
|
39
|
+
/*
|
|
40
|
+
* The same idea for the other human terminal state, with a much shorter
|
|
41
|
+
* clock: a resolved issue that comes back IS a regression worth re-filing,
|
|
42
|
+
* but it has to actually come back first.
|
|
43
|
+
*
|
|
44
|
+
* WITHOUT THIS, RESOLVE DID NOT STICK. Detectors emit on a standing
|
|
45
|
+
* condition, not on an event — NewException re-emits an unchanged candidate
|
|
46
|
+
* on all ~96 ticks of the 24h window after the exception's firstSeenAt, and
|
|
47
|
+
* resolving an insight changes nothing the detector looks at. So a Resolved
|
|
48
|
+
* row (terminal, therefore never refreshed) fell straight through to CREATE
|
|
49
|
+
* and a byte-identical insight reappeared within 15 minutes, complete with
|
|
50
|
+
* its own triage LLM run. Resolving again just minted another one.
|
|
51
|
+
*
|
|
52
|
+
* 24 hours is chosen to cover NEW_EXCEPTION_LOOKBACK_HOURS: it outlasts the
|
|
53
|
+
* window in which a NewException candidate is re-emitted from the same
|
|
54
|
+
* evidence, so that finding stays closed, while the standing-condition
|
|
55
|
+
* detectors (spikes, drift) can still re-file a day later if their signal
|
|
56
|
+
* genuinely returns.
|
|
57
|
+
*/
|
|
58
|
+
export const RESOLVED_COOLDOWN_HOURS: number = 24;
|
|
59
|
+
|
|
41
60
|
/*
|
|
42
61
|
* Column-safety clamps: DatabaseService validates string columns against the
|
|
43
62
|
* model's declared lengths and THROWS on overflow, and detectors embed raw
|
|
@@ -74,8 +93,8 @@ export default class InsightStore {
|
|
|
74
93
|
* - none → CREATE (subject to the per-scan cap)
|
|
75
94
|
* - non-terminal → REFRESH (never touches status)
|
|
76
95
|
* - Dismissed within the cooldown → suppress
|
|
77
|
-
* -
|
|
78
|
-
*
|
|
96
|
+
* - Resolved within the cooldown → suppress
|
|
97
|
+
* - either, past its cooldown → CREATE (regression / recurrence)
|
|
79
98
|
* All access is root-props: the scanner is a system actor with explicit
|
|
80
99
|
* projectId scoping, not a per-user ACL consumer.
|
|
81
100
|
*/
|
|
@@ -129,6 +148,8 @@ export default class InsightStore {
|
|
|
129
148
|
status: true,
|
|
130
149
|
occurrenceCount: true,
|
|
131
150
|
humanVerdictAt: true,
|
|
151
|
+
// The cooldown fallback when no verdict was ever stamped.
|
|
152
|
+
lastSeenAt: true,
|
|
132
153
|
},
|
|
133
154
|
sort: { createdAt: SortOrder.Descending },
|
|
134
155
|
props: { isRoot: true },
|
|
@@ -152,6 +173,17 @@ export default class InsightStore {
|
|
|
152
173
|
lastSeenAt: data.now,
|
|
153
174
|
occurrenceCount: (existing.occurrenceCount || 1) + 1,
|
|
154
175
|
severity: candidate.severity,
|
|
176
|
+
/*
|
|
177
|
+
* The title is refreshed with the rest of the picture, not
|
|
178
|
+
* frozen at creation. Detectors put live numbers in it (a spike
|
|
179
|
+
* multiplier, a latency factor) and the label itself sharpens as
|
|
180
|
+
* more of the failure is observed — a row left showing "at 5.2x"
|
|
181
|
+
* beside a High severity and a 12x detail body reads as a bug.
|
|
182
|
+
*/
|
|
183
|
+
title: this.clampToColumn(
|
|
184
|
+
candidate.title,
|
|
185
|
+
INSIGHT_TITLE_MAX_LENGTH,
|
|
186
|
+
),
|
|
155
187
|
detailMarkdown: candidate.detailMarkdown,
|
|
156
188
|
evidence: candidate.evidence,
|
|
157
189
|
},
|
|
@@ -162,27 +194,53 @@ export default class InsightStore {
|
|
|
162
194
|
continue;
|
|
163
195
|
}
|
|
164
196
|
|
|
165
|
-
if (
|
|
197
|
+
if (
|
|
198
|
+
existing &&
|
|
199
|
+
existing.status &&
|
|
200
|
+
AIInsightStatusHelper.isTerminalStatus(existing.status)
|
|
201
|
+
) {
|
|
202
|
+
/*
|
|
203
|
+
* Both human terminal states get a cooldown, measured from the
|
|
204
|
+
* human's action and inclusive at the boundary (a cooldown exactly
|
|
205
|
+
* elapsed still suppresses). They differ only in length: a
|
|
206
|
+
* dismissal says "this is not worth my time" (7 days), a resolve
|
|
207
|
+
* says "handled" (24 hours — long enough that the detector stops
|
|
208
|
+
* re-emitting the very evidence the human just closed).
|
|
209
|
+
*/
|
|
210
|
+
const isDismissed: boolean =
|
|
211
|
+
existing.status === AIInsightStatus.Dismissed;
|
|
212
|
+
|
|
213
|
+
const cooldownMs: number = isDismissed
|
|
214
|
+
? OneUptimeDate.getMillisecondsInDays(DISMISSED_COOLDOWN_DAYS)
|
|
215
|
+
: OneUptimeDate.getMillisecondsInHours(RESOLVED_COOLDOWN_HOURS);
|
|
216
|
+
|
|
166
217
|
/*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
218
|
+
* humanVerdictAt is the moment the human acted. It can be missing
|
|
219
|
+
* (defensive) and, on a Confirm-then-Resolve, it holds the CONFIRM
|
|
220
|
+
* time — resolveInsight leaves an existing verdict alone. lastSeenAt
|
|
221
|
+
* is the fallback: refreshes stop the moment a row goes terminal, so
|
|
222
|
+
* it is pinned to the last tick before the human closed it, which is
|
|
223
|
+
* never later than the close. Neither available → suppress; a
|
|
224
|
+
* terminal row we cannot date must not be re-filed every 15 minutes
|
|
225
|
+
* (G11 noise posture: when in doubt, stay quiet).
|
|
172
226
|
*/
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
227
|
+
const closedAt: Date | undefined = existing.humanVerdictAt
|
|
228
|
+
? OneUptimeDate.fromString(existing.humanVerdictAt)
|
|
229
|
+
: existing.lastSeenAt
|
|
230
|
+
? OneUptimeDate.fromString(existing.lastSeenAt)
|
|
231
|
+
: undefined;
|
|
176
232
|
|
|
177
|
-
const withinCooldown: boolean =
|
|
178
|
-
? data.now.getTime() -
|
|
179
|
-
OneUptimeDate.fromString(existing.humanVerdictAt).getTime() <=
|
|
180
|
-
cooldownMs
|
|
233
|
+
const withinCooldown: boolean = closedAt
|
|
234
|
+
? data.now.getTime() - closedAt.getTime() <= cooldownMs
|
|
181
235
|
: true;
|
|
182
236
|
|
|
183
237
|
if (withinCooldown) {
|
|
184
238
|
logger.debug(
|
|
185
|
-
`AI Insights: suppressing candidate ${fingerprint} for project ${data.projectId.toString()} —
|
|
239
|
+
`AI Insights: suppressing candidate ${fingerprint} for project ${data.projectId.toString()} — ${
|
|
240
|
+
isDismissed
|
|
241
|
+
? `dismissed by a human within the last ${DISMISSED_COOLDOWN_DAYS} days`
|
|
242
|
+
: `resolved by a human within the last ${RESOLVED_COOLDOWN_HOURS} hours`
|
|
243
|
+
}.`,
|
|
186
244
|
);
|
|
187
245
|
|
|
188
246
|
result.suppressed++;
|