@oneuptime/common 11.5.12 → 11.6.0
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/AIInsight.ts +30 -0
- package/Models/DatabaseModels/EnterpriseLicense.ts +21 -0
- package/Models/DatabaseModels/EnterpriseLicenseInstance.ts +25 -0
- package/Models/DatabaseModels/GlobalConfig.ts +76 -0
- package/Models/DatabaseModels/Project.ts +30 -0
- package/Models/DatabaseModels/TelemetryException.ts +128 -0
- package/Server/API/AIAgentDataAPI.ts +129 -5
- package/Server/API/AIInvestigationAPI.ts +87 -0
- package/Server/API/EnterpriseLicenseAPI.ts +57 -0
- package/Server/API/GlobalConfigAPI.ts +62 -1
- package/Server/EnvironmentConfig.ts +22 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784640000000-AddExceptionTriageAndPrivacyColumns.ts +56 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.ts +38 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Services/AIRunService.ts +8 -0
- package/Server/Services/AnalyticsDatabaseService.ts +48 -45
- package/Server/Services/DatabaseService.ts +66 -1
- package/Server/Services/TelemetryExceptionService.ts +320 -24
- package/Server/Services/WorkflowService.ts +100 -39
- package/Server/Types/Workflow/Components/Schedule.ts +57 -32
- package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +15 -4
- package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +15 -4
- package/Server/Utils/AI/SRE/Insights/InsightScanner.ts +222 -24
- package/Server/Utils/AI/SRE/Insights/InsightTriageRunner.ts +225 -9
- package/Server/Utils/AI/SRE/SubjectCodeFixRun.ts +39 -0
- package/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.ts +114 -0
- package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +59 -0
- package/Server/Utils/Express.ts +13 -0
- package/Server/Utils/Telemetry/ExceptionSanitizer.ts +222 -0
- package/Server/Utils/Telemetry/TelemetryFanInWriter.ts +799 -0
- package/Server/Utils/Telemetry/TelemetryWriterClient.ts +263 -0
- package/Server/Utils/Telemetry/TelemetryWriterServer.ts +240 -0
- package/Server/Utils/Telemetry/TelemetryWriterShedMetrics.ts +93 -0
- package/Tests/Server/Services/DatabaseServiceSanitizeUpdateData.test.ts +232 -0
- package/Tests/Server/Services/TelemetryExceptionCodeFixRun.test.ts +205 -1
- package/Tests/Server/Services/UpdateOneByIdKeepsRowId.test.ts +107 -0
- package/Tests/Server/Services/WorkflowService.test.ts +308 -0
- package/Tests/Server/Utils/AI/Insights/InsightScanner.test.ts +150 -141
- package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +277 -1
- package/Tests/Server/Utils/Attribution.test.ts +203 -0
- package/Tests/Server/Utils/ExceptionSanitizer.test.ts +70 -0
- package/Tests/Server/Utils/InsightTriageClassification.test.ts +57 -0
- package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +183 -0
- package/Tests/Server/Utils/Marketing/GoogleAds.test.ts +340 -0
- package/Tests/Server/Utils/Monitor/Criteria/ExceptionMonitorCriteria.test.ts +87 -0
- package/Tests/Server/Utils/Monitor/Criteria/LogMonitorCriteria.test.ts +165 -0
- package/Tests/Server/Utils/Monitor/Criteria/TraceMonitorCriteria.test.ts +86 -0
- package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +1565 -0
- package/Tests/Server/Utils/Telemetry/TelemetryWriterClient.test.ts +313 -0
- package/Tests/Server/Utils/Telemetry/TelemetryWriterServer.test.ts +270 -0
- package/Tests/Server/Utils/Telemetry/TelemetryWriterShedMetrics.test.ts +127 -0
- package/Tests/Server/Utils/TelemetryImprovementTaskTrigger.test.ts +141 -0
- package/Tests/Types/AI/CodeFixTaskType.test.ts +3 -0
- package/Tests/Types/Monitor/MonitorCriteriaMetricVariables.test.ts +228 -0
- package/Tests/Types/Monitor/MonitorStepDefaultTelemetryConfig.test.ts +169 -0
- package/Tests/Types/Monitor/MonitorStepExceptionMonitor.test.ts +289 -0
- package/Tests/Types/Monitor/MonitorStepLogMonitor.test.ts +231 -0
- package/Tests/Types/Monitor/MonitorStepMetricViewConfigUtil.test.ts +245 -0
- package/Tests/Types/Monitor/MonitorStepNetworkDeviceMonitor.test.ts +155 -0
- package/Tests/Types/Monitor/MonitorStepTelemetrySerialization.test.ts +141 -0
- package/Tests/Types/Monitor/MonitorStepTraceMonitor.test.ts +137 -0
- package/Tests/Types/Workspace/NotificationRules/NotificationRuleCondition.test.ts +311 -0
- package/Tests/UI/Components/ModelTableSelectFromColumns.test.ts +259 -0
- package/Tests/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.test.ts +461 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +2852 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +758 -0
- package/Tests/UI/Utils/NotificationMethodUtil.test.ts +564 -0
- package/Tests/Utils/Array.test.ts +156 -0
- package/Tests/Utils/CronTab.test.ts +199 -0
- package/Tests/Utils/ModelImportExport.test.ts +101 -0
- package/Tests/Utils/Number.test.ts +179 -0
- package/Tests/Utils/VersionUtil.test.ts +348 -0
- package/Tests/jest.setup.ts +1 -0
- package/Types/AI/CodeFixTaskContext.ts +6 -0
- package/Types/AI/CodeFixTaskType.ts +38 -1
- package/Types/AI/ExceptionAIClassification.ts +27 -0
- package/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.ts +6 -0
- package/Types/Log/LogScrubPatternType.ts +7 -0
- package/Types/Monitor/MonitorStep.ts +24 -4
- package/Types/Monitor/MonitorStepMetricViewConfigUtil.ts +105 -0
- package/Types/Trace/TraceScrubPatternType.ts +7 -0
- package/UI/Components/EditionLabel/EditionLabel.tsx +445 -12
- package/UI/Components/ModelTable/BaseModelTable.tsx +15 -46
- package/UI/Components/ModelTable/SelectFromColumns.ts +129 -0
- package/UI/Components/Workflow/ArgumentsForm.tsx +68 -30
- package/UI/Components/Workflow/CronScheduleField.tsx +411 -0
- package/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.ts +176 -0
- package/UI/Utils/NotificationMethodUtil.ts +310 -0
- package/Utils/CronTab.ts +823 -0
- package/Utils/ModelImportExport.ts +21 -6
- package/Utils/VersionUtil.ts +260 -0
- package/build/dist/Models/DatabaseModels/AIInsight.js +31 -0
- package/build/dist/Models/DatabaseModels/AIInsight.js.map +1 -1
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +22 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
- package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js +26 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +81 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +31 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TelemetryException.js +134 -0
- package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
- package/build/dist/Server/API/AIAgentDataAPI.js +97 -8
- package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +55 -0
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/EnterpriseLicenseAPI.js +47 -0
- package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
- package/build/dist/Server/API/GlobalConfigAPI.js +52 -1
- package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +17 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784640000000-AddExceptionTriageAndPrivacyColumns.js +33 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784640000000-AddExceptionTriageAndPrivacyColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.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/Services/AIRunService.js +9 -2
- package/build/dist/Server/Services/AIRunService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +44 -16
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +53 -1
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryExceptionService.js +263 -22
- package/build/dist/Server/Services/TelemetryExceptionService.js.map +1 -1
- package/build/dist/Server/Services/WorkflowService.js +80 -30
- package/build/dist/Server/Services/WorkflowService.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Schedule.js +44 -19
- package/build/dist/Server/Types/Workflow/Components/Schedule.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +12 -4
- 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 +12 -4
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightScanner.js +193 -22
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightScanner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js +178 -9
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js +37 -0
- package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.js +98 -0
- package/build/dist/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.js.map +1 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js +24 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -0
- package/build/dist/Server/Utils/Express.js +12 -0
- package/build/dist/Server/Utils/Express.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js +149 -0
- package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js +496 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterClient.js +165 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterClient.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterServer.js +136 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterServer.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterShedMetrics.js +76 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryWriterShedMetrics.js.map +1 -0
- package/build/dist/Types/AI/CodeFixTaskType.js +38 -1
- package/build/dist/Types/AI/CodeFixTaskType.js.map +1 -1
- package/build/dist/Types/AI/ExceptionAIClassification.js +28 -0
- package/build/dist/Types/AI/ExceptionAIClassification.js.map +1 -0
- package/build/dist/Types/Log/LogScrubPatternType.js +7 -0
- package/build/dist/Types/Log/LogScrubPatternType.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +20 -4
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js +73 -0
- package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js.map +1 -0
- package/build/dist/Types/Trace/TraceScrubPatternType.js +7 -0
- package/build/dist/Types/Trace/TraceScrubPatternType.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +261 -13
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +12 -37
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/SelectFromColumns.js +82 -0
- package/build/dist/UI/Components/ModelTable/SelectFromColumns.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +30 -6
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/CronScheduleField.js +209 -0
- package/build/dist/UI/Components/Workflow/CronScheduleField.js.map +1 -0
- package/build/dist/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.js +129 -0
- package/build/dist/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.js.map +1 -0
- package/build/dist/UI/Utils/NotificationMethodUtil.js +189 -0
- package/build/dist/UI/Utils/NotificationMethodUtil.js.map +1 -0
- package/build/dist/Utils/CronTab.js +609 -0
- package/build/dist/Utils/CronTab.js.map +1 -0
- package/build/dist/Utils/ModelImportExport.js +20 -6
- package/build/dist/Utils/ModelImportExport.js.map +1 -1
- package/build/dist/Utils/VersionUtil.js +193 -0
- package/build/dist/Utils/VersionUtil.js.map +1 -0
- package/package.json +1 -1
|
@@ -15,6 +15,7 @@ import ComponentID from "../../../../Types/Workflow/ComponentID";
|
|
|
15
15
|
import ScheduleComponents from "../../../../Types/Workflow/Components/Schedule";
|
|
16
16
|
import Workflow from "../../../../Models/DatabaseModels/Workflow";
|
|
17
17
|
import CaptureSpan from "../../../Utils/Telemetry/CaptureSpan";
|
|
18
|
+
import logger from "../../../Utils/Logger";
|
|
18
19
|
|
|
19
20
|
export default class WebhookTrigger extends TriggerCode {
|
|
20
21
|
public constructor() {
|
|
@@ -52,24 +53,37 @@ export default class WebhookTrigger extends TriggerCode {
|
|
|
52
53
|
|
|
53
54
|
// query all workflows.
|
|
54
55
|
for (const workflow of workflows) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
/*
|
|
57
|
+
* Isolate each workflow: a single workflow whose schedule can't be
|
|
58
|
+
* registered (e.g. an invalid cron, or a transient queue error) must not
|
|
59
|
+
* abort the startup scan and leave every other scheduled workflow
|
|
60
|
+
* unregistered.
|
|
61
|
+
*/
|
|
62
|
+
try {
|
|
63
|
+
const executeWorkflow: ExecuteWorkflowType = {
|
|
64
|
+
workflowId: new ObjectID(workflow._id!),
|
|
65
|
+
returnValues: {},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
if (
|
|
69
|
+
workflow.triggerArguments &&
|
|
70
|
+
workflow.triggerArguments["schedule"] &&
|
|
71
|
+
workflow.isEnabled
|
|
72
|
+
) {
|
|
73
|
+
await props.scheduleWorkflow(
|
|
74
|
+
executeWorkflow,
|
|
75
|
+
workflow.triggerArguments["schedule"] as string,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!workflow.isEnabled) {
|
|
80
|
+
await props.removeWorkflow(workflow.id!);
|
|
81
|
+
}
|
|
82
|
+
} catch (err) {
|
|
83
|
+
logger.error(
|
|
84
|
+
`Failed to register schedule for workflow ${workflow._id}`,
|
|
68
85
|
);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (!workflow.isEnabled) {
|
|
72
|
-
await props.removeWorkflow(workflow.id!);
|
|
86
|
+
logger.error(err);
|
|
73
87
|
}
|
|
74
88
|
}
|
|
75
89
|
}
|
|
@@ -128,23 +142,34 @@ export default class WebhookTrigger extends TriggerCode {
|
|
|
128
142
|
returnValues: {},
|
|
129
143
|
};
|
|
130
144
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
145
|
+
/*
|
|
146
|
+
* Guard the (re)registration so a bad schedule can't fail the workflow
|
|
147
|
+
* save that triggered this update. Invalid crons are already surfaced as a
|
|
148
|
+
* workflow log by the queue layer; this catch is a backstop for unexpected
|
|
149
|
+
* errors (e.g. a transient queue error).
|
|
150
|
+
*/
|
|
151
|
+
try {
|
|
152
|
+
if (
|
|
153
|
+
workflow.triggerArguments &&
|
|
154
|
+
workflow.triggerArguments["schedule"] &&
|
|
155
|
+
workflow.isEnabled
|
|
156
|
+
) {
|
|
157
|
+
await this.scheduleWorkflow(
|
|
158
|
+
executeWorkflow,
|
|
159
|
+
workflow.triggerArguments["schedule"] as string,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
141
162
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
163
|
+
if (!this.removeWorkflow) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
145
166
|
|
|
146
|
-
|
|
147
|
-
|
|
167
|
+
if (!workflow.isEnabled) {
|
|
168
|
+
await this.removeWorkflow(workflow.id!);
|
|
169
|
+
}
|
|
170
|
+
} catch (err) {
|
|
171
|
+
logger.error(`Failed to update schedule for workflow ${workflow._id}`);
|
|
172
|
+
logger.error(err);
|
|
148
173
|
}
|
|
149
174
|
}
|
|
150
175
|
}
|
|
@@ -13,6 +13,7 @@ import TelemetryExceptionService from "../../../../../Services/TelemetryExceptio
|
|
|
13
13
|
import ExceptionInstanceService from "../../../../../Services/ExceptionInstanceService";
|
|
14
14
|
import ServiceService from "../../../../../Services/ServiceService";
|
|
15
15
|
import QueryHelper from "../../../../../Types/Database/QueryHelper";
|
|
16
|
+
import { sanitizeExceptionMessage } from "../../../../Telemetry/ExceptionSanitizer";
|
|
16
17
|
import {
|
|
17
18
|
InsightCandidate,
|
|
18
19
|
InsightDetector,
|
|
@@ -239,9 +240,19 @@ export default class ExceptionSpikeDetector implements InsightDetector {
|
|
|
239
240
|
exception.primaryEntityId,
|
|
240
241
|
);
|
|
241
242
|
|
|
243
|
+
/*
|
|
244
|
+
* The message flows into the insight title/detail/evidence, and from
|
|
245
|
+
* there into the triage LLM prompt — sanitize it once here so no
|
|
246
|
+
* interpolated user data (IDs, emails, domains) rides along. The raw
|
|
247
|
+
* message stays on the exception row for the dashboard.
|
|
248
|
+
*/
|
|
249
|
+
const safeMessage: string = sanitizeExceptionMessage(
|
|
250
|
+
exception.message || "",
|
|
251
|
+
);
|
|
252
|
+
|
|
242
253
|
const label: string = ExceptionSpikeDetector.buildExceptionLabel(
|
|
243
254
|
exception.exceptionType,
|
|
244
|
-
|
|
255
|
+
safeMessage,
|
|
245
256
|
);
|
|
246
257
|
|
|
247
258
|
const firstSeenAtIso: string | undefined = exception.firstSeenAt
|
|
@@ -255,8 +266,8 @@ export default class ExceptionSpikeDetector implements InsightDetector {
|
|
|
255
266
|
"",
|
|
256
267
|
`- Exception: \`${label}\``,
|
|
257
268
|
];
|
|
258
|
-
if (
|
|
259
|
-
detailLines.push(`- Message: ${
|
|
269
|
+
if (safeMessage) {
|
|
270
|
+
detailLines.push(`- Message: ${safeMessage}`);
|
|
260
271
|
}
|
|
261
272
|
if (serviceName) {
|
|
262
273
|
detailLines.push(`- Service: ${serviceName}`);
|
|
@@ -281,7 +292,7 @@ export default class ExceptionSpikeDetector implements InsightDetector {
|
|
|
281
292
|
telemetryExceptionId: exception.id,
|
|
282
293
|
evidence: {
|
|
283
294
|
exception: {
|
|
284
|
-
exceptionMessage:
|
|
295
|
+
exceptionMessage: safeMessage,
|
|
285
296
|
exceptionType: exception.exceptionType,
|
|
286
297
|
recentOccurrenceCount: recentCount.toNumber(),
|
|
287
298
|
baselineHourlyAverage: decision.baselineHourlyAverage,
|
|
@@ -9,6 +9,7 @@ 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 { sanitizeExceptionMessage } from "../../../../Telemetry/ExceptionSanitizer";
|
|
12
13
|
import {
|
|
13
14
|
InsightCandidate,
|
|
14
15
|
InsightDetector,
|
|
@@ -166,9 +167,19 @@ export default class NewExceptionDetector implements InsightDetector {
|
|
|
166
167
|
exception.primaryEntityId,
|
|
167
168
|
);
|
|
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 || "",
|
|
178
|
+
);
|
|
179
|
+
|
|
169
180
|
const label: string = NewExceptionDetector.buildExceptionLabel(
|
|
170
181
|
exception.exceptionType,
|
|
171
|
-
|
|
182
|
+
safeMessage,
|
|
172
183
|
);
|
|
173
184
|
|
|
174
185
|
const firstSeenAtIso: string | undefined = exception.firstSeenAt
|
|
@@ -180,8 +191,8 @@ export default class NewExceptionDetector implements InsightDetector {
|
|
|
180
191
|
"",
|
|
181
192
|
`- Exception: \`${label}\``,
|
|
182
193
|
];
|
|
183
|
-
if (
|
|
184
|
-
detailLines.push(`- Message: ${
|
|
194
|
+
if (safeMessage) {
|
|
195
|
+
detailLines.push(`- Message: ${safeMessage}`);
|
|
185
196
|
}
|
|
186
197
|
if (serviceName) {
|
|
187
198
|
detailLines.push(`- Service: ${serviceName}`);
|
|
@@ -204,7 +215,7 @@ export default class NewExceptionDetector implements InsightDetector {
|
|
|
204
215
|
telemetryExceptionId: exception.id,
|
|
205
216
|
evidence: {
|
|
206
217
|
exception: {
|
|
207
|
-
exceptionMessage:
|
|
218
|
+
exceptionMessage: safeMessage,
|
|
208
219
|
exceptionType: exception.exceptionType,
|
|
209
220
|
totalOccurrenceCount: occurrenceCount,
|
|
210
221
|
firstSeenAt: firstSeenAtIso,
|
|
@@ -11,6 +11,12 @@ import InsightStore, { UpsertCandidatesResult } from "./InsightStore";
|
|
|
11
11
|
import InsightFixRouting, { InsightFixRoutingResult } from "./FixRouting";
|
|
12
12
|
import InsightTriage, { InsightTriageResult } from "./Triage";
|
|
13
13
|
import { InsightCandidate, InsightDetector, InsightScanContext } from "./Types";
|
|
14
|
+
import AIInsightType from "../../../../../Types/AI/AIInsightType";
|
|
15
|
+
import AIRunType from "../../../../../Types/AI/AIRunType";
|
|
16
|
+
import AIRunStatus from "../../../../../Types/AI/AIRunStatus";
|
|
17
|
+
import ExceptionAIClassification from "../../../../../Types/AI/ExceptionAIClassification";
|
|
18
|
+
import AIRunService from "../../../../Services/AIRunService";
|
|
19
|
+
import QueryHelper from "../../../../Types/Database/QueryHelper";
|
|
14
20
|
import logger from "../../../Logger";
|
|
15
21
|
import CaptureSpan from "../../../Telemetry/CaptureSpan";
|
|
16
22
|
|
|
@@ -19,8 +25,9 @@ import CaptureSpan from "../../../Telemetry/CaptureSpan";
|
|
|
19
25
|
*
|
|
20
26
|
* THE RULE: no LLM here. Detectors are deterministic statistical sensors;
|
|
21
27
|
* the LLM only engages per-finding AFTERWARDS through InsightTriage
|
|
22
|
-
* (budgeted, read-only)
|
|
23
|
-
*
|
|
28
|
+
* (budgeted, read-only). The automatic fix decision happens after triage
|
|
29
|
+
* completes (InsightTriageRunner → InsightFixRouting) and only for
|
|
30
|
+
* insights the triage classified as code faults.
|
|
24
31
|
*
|
|
25
32
|
* Everything is opt-in and quiet: only projects with the default-false
|
|
26
33
|
* enableAiInsights flag are scanned, insights never page and never
|
|
@@ -73,8 +80,8 @@ export default class InsightScanner {
|
|
|
73
80
|
* One project's scan: self-heal any insight an earlier tick left unrouted,
|
|
74
81
|
* run every registered detector (isolated — one failing sensor must not
|
|
75
82
|
* blind the others), dedupe/upsert the candidates through InsightStore,
|
|
76
|
-
* then route each NEWLY created insight to
|
|
77
|
-
*
|
|
83
|
+
* then route each NEWLY created insight to ActionRequired and enqueue its
|
|
84
|
+
* triage (which, on a code-fault verdict, routes the fix).
|
|
78
85
|
*/
|
|
79
86
|
@CaptureSpan()
|
|
80
87
|
public static async scanProjectForInsights(project: Project): Promise<void> {
|
|
@@ -211,19 +218,200 @@ export default class InsightScanner {
|
|
|
211
218
|
);
|
|
212
219
|
}
|
|
213
220
|
}
|
|
221
|
+
|
|
222
|
+
await this.resweepUntriagedInsights({ projectId });
|
|
223
|
+
await this.resweepUnroutedCodeFaults({ projectId, project });
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/*
|
|
227
|
+
* Stranded shape 2: ActionRequired insights that never got a triage
|
|
228
|
+
* verdict. With triage gating the automatic fix, a triage that was
|
|
229
|
+
* quiet-skipped (token budget exhausted), expired in the queue, or
|
|
230
|
+
* failed would otherwise strand the insight verdict-less FOREVER — and
|
|
231
|
+
* with it any automatic fix. Re-enqueueing is safe and cheap: the
|
|
232
|
+
* triage enqueue dedupes to one non-terminal run per insight and
|
|
233
|
+
* quiet-skips when the budget still has no headroom. Bounded two ways:
|
|
234
|
+
* only insights from the last 7 days (older rows predate the verdict
|
|
235
|
+
* gate or were consciously left alone), and only while fewer than 3
|
|
236
|
+
* triage attempts have ended in failure (a permanently failing triage
|
|
237
|
+
* must not retry every 15 minutes for a week).
|
|
238
|
+
*/
|
|
239
|
+
private static async resweepUntriagedInsights(data: {
|
|
240
|
+
projectId: ObjectID;
|
|
241
|
+
}): Promise<void> {
|
|
242
|
+
const { projectId } = data;
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
const untriaged: Array<AIInsight> = await AIInsightService.findBy({
|
|
246
|
+
query: {
|
|
247
|
+
projectId: projectId,
|
|
248
|
+
status: AIInsightStatus.ActionRequired,
|
|
249
|
+
triageCompletedAt: QueryHelper.isNull(),
|
|
250
|
+
createdAt: QueryHelper.greaterThanEqualTo(
|
|
251
|
+
OneUptimeDate.addRemoveDays(OneUptimeDate.getCurrentDate(), -7),
|
|
252
|
+
),
|
|
253
|
+
},
|
|
254
|
+
select: {
|
|
255
|
+
_id: true,
|
|
256
|
+
projectId: true,
|
|
257
|
+
insightType: true,
|
|
258
|
+
serviceName: true,
|
|
259
|
+
traceId: true,
|
|
260
|
+
telemetryExceptionId: true,
|
|
261
|
+
evidence: true,
|
|
262
|
+
},
|
|
263
|
+
limit: LIMIT_MAX,
|
|
264
|
+
skip: 0,
|
|
265
|
+
props: { isRoot: true },
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
for (const insight of untriaged) {
|
|
269
|
+
try {
|
|
270
|
+
const failedTriageAttempts: number = (
|
|
271
|
+
await AIRunService.countBy({
|
|
272
|
+
query: {
|
|
273
|
+
runType: AIRunType.Investigation,
|
|
274
|
+
triggeredByAiInsightId: insight.id!,
|
|
275
|
+
status: QueryHelper.any([
|
|
276
|
+
AIRunStatus.Error,
|
|
277
|
+
AIRunStatus.Cancelled,
|
|
278
|
+
AIRunStatus.Stale,
|
|
279
|
+
]),
|
|
280
|
+
},
|
|
281
|
+
props: { isRoot: true },
|
|
282
|
+
})
|
|
283
|
+
).toNumber();
|
|
284
|
+
|
|
285
|
+
if (failedTriageAttempts >= 3) {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const triageResult: InsightTriageResult =
|
|
290
|
+
await InsightTriage.enqueueInsightTriage({ insight: insight });
|
|
291
|
+
|
|
292
|
+
if (triageResult.triageAiRunId) {
|
|
293
|
+
await AIInsightService.updateOneById({
|
|
294
|
+
id: insight.id!,
|
|
295
|
+
data: {
|
|
296
|
+
triageAiRunId: triageResult.triageAiRunId,
|
|
297
|
+
},
|
|
298
|
+
props: { isRoot: true },
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
} catch (error) {
|
|
302
|
+
logger.error(
|
|
303
|
+
`AI Insights: triage re-enqueue failed for insight ${insight.id?.toString()} — continuing: ${error}`,
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
} catch (error) {
|
|
308
|
+
logger.error(
|
|
309
|
+
`AI Insights: failed to sweep untriaged insights for project ${projectId.toString()} — continuing with the scan: ${error}`,
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/*
|
|
315
|
+
* Stranded shape 3: a code-fault verdict was recorded but the fix was
|
|
316
|
+
* never routed — the pod died between the verdict write and the fix
|
|
317
|
+
* routing, or routing quiet-skipped on a transient gate (daily fix
|
|
318
|
+
* budget, readiness). Re-routing is idempotent: the creation path's
|
|
319
|
+
* per-(exception, recipe) dedupe and the cross-group guard refuse
|
|
320
|
+
* duplicates, and the conditional status flip only moves
|
|
321
|
+
* ActionRequired → FixOpened. Bounded to verdicts from the last 24
|
|
322
|
+
* hours so a permanently refused group (declined by a human, capped
|
|
323
|
+
* repo) does not retry forever. Latency insights are excluded — they
|
|
324
|
+
* are fix-routed deterministically at scan time.
|
|
325
|
+
*/
|
|
326
|
+
private static async resweepUnroutedCodeFaults(data: {
|
|
327
|
+
projectId: ObjectID;
|
|
328
|
+
project: Project;
|
|
329
|
+
}): Promise<void> {
|
|
330
|
+
const { projectId, project } = data;
|
|
331
|
+
|
|
332
|
+
try {
|
|
333
|
+
const unrouted: Array<AIInsight> = await AIInsightService.findBy({
|
|
334
|
+
query: {
|
|
335
|
+
projectId: projectId,
|
|
336
|
+
status: AIInsightStatus.ActionRequired,
|
|
337
|
+
classification: ExceptionAIClassification.CodeFault,
|
|
338
|
+
fixAiRunId: QueryHelper.isNull(),
|
|
339
|
+
triageCompletedAt: QueryHelper.greaterThanEqualTo(
|
|
340
|
+
OneUptimeDate.addRemoveDays(OneUptimeDate.getCurrentDate(), -1),
|
|
341
|
+
),
|
|
342
|
+
},
|
|
343
|
+
select: {
|
|
344
|
+
_id: true,
|
|
345
|
+
projectId: true,
|
|
346
|
+
insightType: true,
|
|
347
|
+
serviceName: true,
|
|
348
|
+
traceId: true,
|
|
349
|
+
telemetryExceptionId: true,
|
|
350
|
+
evidence: true,
|
|
351
|
+
},
|
|
352
|
+
limit: LIMIT_MAX,
|
|
353
|
+
skip: 0,
|
|
354
|
+
props: { isRoot: true },
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
for (const insight of unrouted) {
|
|
358
|
+
if (insight.insightType === AIInsightType.TraceLatencyRegression) {
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
try {
|
|
363
|
+
const fixResult: InsightFixRoutingResult =
|
|
364
|
+
await InsightFixRouting.routeInsightFix({
|
|
365
|
+
insight: insight,
|
|
366
|
+
project: project,
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
if (fixResult.fixAiRunId) {
|
|
370
|
+
await AIInsightService.updateOneBy({
|
|
371
|
+
query: {
|
|
372
|
+
_id: insight.id!.toString(),
|
|
373
|
+
status: AIInsightStatus.ActionRequired,
|
|
374
|
+
},
|
|
375
|
+
data: {
|
|
376
|
+
status: AIInsightStatus.FixOpened,
|
|
377
|
+
fixAiRunId: fixResult.fixAiRunId,
|
|
378
|
+
},
|
|
379
|
+
props: { isRoot: true },
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
} catch (error) {
|
|
383
|
+
logger.error(
|
|
384
|
+
`AI Insights: fix re-routing failed for insight ${insight.id?.toString()} — continuing: ${error}`,
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
} catch (error) {
|
|
389
|
+
logger.error(
|
|
390
|
+
`AI Insights: failed to sweep unrouted code-fault insights for project ${projectId.toString()} — continuing with the scan: ${error}`,
|
|
391
|
+
);
|
|
392
|
+
}
|
|
214
393
|
}
|
|
215
394
|
|
|
216
395
|
/*
|
|
217
396
|
* Route one insight — newly created this tick, or swept up as stranded
|
|
218
|
-
* from an earlier one — out of the defensive Detected state
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
397
|
+
* from an earlier one — out of the defensive Detected state.
|
|
398
|
+
*
|
|
399
|
+
* TRIAGE GATES THE FIX. The automatic fix decision no longer happens
|
|
400
|
+
* here: every new insight goes to ActionRequired and gets its triage
|
|
401
|
+
* enqueued; when the triage run completes with a code-fault
|
|
402
|
+
* classification, InsightTriageRunner routes the fix (via
|
|
403
|
+
* InsightFixRouting) and flips the insight to FixOpened. This exists
|
|
404
|
+
* because roughly half of real exception insights turn out to be
|
|
405
|
+
* expected user errors or intentional denials (auth, paywalls, scanner
|
|
406
|
+
* probes) — opening an unreviewed fix PR for those is worse than
|
|
407
|
+
* opening none: the agent's only "fix" is to weaken the check that
|
|
408
|
+
* correctly fired. Fail-closed by design: no triage verdict, no
|
|
409
|
+
* automatic PR. The manual "Fix with AI" button on the exception page
|
|
410
|
+
* is unaffected.
|
|
411
|
+
*
|
|
412
|
+
* The collaborator is never-throws by contract, but it is wrapped
|
|
413
|
+
* anyway: a contract breach must degrade (no triage), not leave the
|
|
414
|
+
* insight stuck in Detected or fail the scan.
|
|
227
415
|
*/
|
|
228
416
|
private static async routeNewInsight(data: {
|
|
229
417
|
insight: AIInsight;
|
|
@@ -231,19 +419,29 @@ export default class InsightScanner {
|
|
|
231
419
|
}): Promise<void> {
|
|
232
420
|
const { insight, project } = data;
|
|
233
421
|
|
|
422
|
+
/*
|
|
423
|
+
* TraceLatencyRegression keeps DETERMINISTIC scan-time fix routing:
|
|
424
|
+
* its evidence is the SpanTreeAnalyzer's own findings (N+1, dominant
|
|
425
|
+
* span, sequential fan-out) — no LLM verdict adds signal there, and
|
|
426
|
+
* the exception-centric classification taxonomy would mislabel most
|
|
427
|
+
* latency regressions as "infrastructure". Exception insights go
|
|
428
|
+
* through the verdict gate instead.
|
|
429
|
+
*/
|
|
234
430
|
let fixAiRunId: ObjectID | undefined = undefined;
|
|
235
431
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
432
|
+
if (insight.insightType === AIInsightType.TraceLatencyRegression) {
|
|
433
|
+
try {
|
|
434
|
+
const fixResult: InsightFixRoutingResult =
|
|
435
|
+
await InsightFixRouting.routeInsightFix({
|
|
436
|
+
insight: insight,
|
|
437
|
+
project: project,
|
|
438
|
+
});
|
|
439
|
+
fixAiRunId = fixResult.fixAiRunId;
|
|
440
|
+
} catch (error) {
|
|
441
|
+
logger.error(
|
|
442
|
+
`AI Insights: fix routing threw for latency insight ${insight.id?.toString()} (treating as no fix opened): ${error}`,
|
|
443
|
+
);
|
|
444
|
+
}
|
|
247
445
|
}
|
|
248
446
|
|
|
249
447
|
if (fixAiRunId) {
|