@oneuptime/common 11.3.28 → 11.4.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/AIRun.ts +50 -0
- package/Models/DatabaseModels/Project.ts +60 -0
- package/Server/API/AIInvestigationAPI.ts +148 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.ts +19 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.ts +19 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Services/AlertService.ts +25 -0
- package/Server/Services/IncidentService.ts +25 -0
- package/Server/Services/IncidentStateTimelineService.ts +19 -0
- package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +119 -11
- package/Server/Utils/AI/Sentinel/AlertInvestigationRunner.ts +141 -0
- package/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.ts +178 -0
- package/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.ts +105 -0
- package/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.ts +405 -0
- package/Server/Utils/AI/Sentinel/SentinelMemory.ts +167 -0
- package/Server/Utils/AI/Toolbox/Index.ts +11 -0
- package/Server/Utils/AI/Toolbox/SentinelActionTools.ts +577 -0
- package/Tests/Server/API/BaseAPI.test.ts +1 -0
- package/Tests/Types/OnCallDutyPolicy/ScheduleShiftUtil.test.ts +260 -0
- package/Types/Date.ts +46 -0
- package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +257 -0
- package/UI/Components/Calendar/Calendar.tsx +8 -2
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +5 -5
- package/build/dist/Models/DatabaseModels/AIRun.js +52 -0
- package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +62 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +107 -0
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.js +18 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.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/AlertService.js +24 -0
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +24 -0
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +59 -11
- package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
- package/build/dist/Server/Utils/AI/Sentinel/AlertInvestigationRunner.js +119 -0
- package/build/dist/Server/Utils/AI/Sentinel/AlertInvestigationRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.js +147 -0
- package/build/dist/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.js +93 -0
- package/build/dist/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.js.map +1 -0
- package/build/dist/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.js +311 -0
- package/build/dist/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.js.map +1 -0
- package/build/dist/Server/Utils/AI/Sentinel/SentinelMemory.js +143 -0
- package/build/dist/Server/Utils/AI/Sentinel/SentinelMemory.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/Index.js +6 -0
- package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/SentinelActionTools.js +451 -0
- package/build/dist/Server/Utils/AI/Toolbox/SentinelActionTools.js.map +1 -0
- package/build/dist/Types/Date.js +32 -0
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +159 -0
- package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -0
- package/build/dist/UI/Components/Calendar/Calendar.js +2 -2
- package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +2 -2
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/package.json +1 -1
|
@@ -284,6 +284,56 @@ export default class AIRun extends BaseModel {
|
|
|
284
284
|
})
|
|
285
285
|
public conversationId?: ObjectID = undefined;
|
|
286
286
|
|
|
287
|
+
@ColumnAccessControl({
|
|
288
|
+
create: [],
|
|
289
|
+
read: [
|
|
290
|
+
Permission.ProjectOwner,
|
|
291
|
+
Permission.ProjectAdmin,
|
|
292
|
+
Permission.ProjectMember,
|
|
293
|
+
],
|
|
294
|
+
update: [],
|
|
295
|
+
})
|
|
296
|
+
@Index()
|
|
297
|
+
@TableColumn({
|
|
298
|
+
type: TableColumnType.ObjectID,
|
|
299
|
+
required: false,
|
|
300
|
+
canReadOnRelationQuery: true,
|
|
301
|
+
title: "Triggered By Incident ID",
|
|
302
|
+
description:
|
|
303
|
+
"The incident that triggered this run (for autonomous investigations).",
|
|
304
|
+
})
|
|
305
|
+
@Column({
|
|
306
|
+
type: ColumnType.ObjectID,
|
|
307
|
+
nullable: true,
|
|
308
|
+
transformer: ObjectID.getDatabaseTransformer(),
|
|
309
|
+
})
|
|
310
|
+
public triggeredByIncidentId?: ObjectID = undefined;
|
|
311
|
+
|
|
312
|
+
@ColumnAccessControl({
|
|
313
|
+
create: [],
|
|
314
|
+
read: [
|
|
315
|
+
Permission.ProjectOwner,
|
|
316
|
+
Permission.ProjectAdmin,
|
|
317
|
+
Permission.ProjectMember,
|
|
318
|
+
],
|
|
319
|
+
update: [],
|
|
320
|
+
})
|
|
321
|
+
@Index()
|
|
322
|
+
@TableColumn({
|
|
323
|
+
type: TableColumnType.ObjectID,
|
|
324
|
+
required: false,
|
|
325
|
+
canReadOnRelationQuery: true,
|
|
326
|
+
title: "Triggered By Alert ID",
|
|
327
|
+
description:
|
|
328
|
+
"The alert that triggered this run (for autonomous investigations).",
|
|
329
|
+
})
|
|
330
|
+
@Column({
|
|
331
|
+
type: ColumnType.ObjectID,
|
|
332
|
+
nullable: true,
|
|
333
|
+
transformer: ObjectID.getDatabaseTransformer(),
|
|
334
|
+
})
|
|
335
|
+
public triggeredByAlertId?: ObjectID = undefined;
|
|
336
|
+
|
|
287
337
|
@ColumnAccessControl({
|
|
288
338
|
create: [],
|
|
289
339
|
read: [
|
|
@@ -1422,6 +1422,66 @@ export default class Project extends TenantModel {
|
|
|
1422
1422
|
})
|
|
1423
1423
|
public enableAi?: boolean = undefined;
|
|
1424
1424
|
|
|
1425
|
+
@ColumnAccessControl({
|
|
1426
|
+
create: [],
|
|
1427
|
+
read: [
|
|
1428
|
+
Permission.ProjectOwner,
|
|
1429
|
+
Permission.ProjectAdmin,
|
|
1430
|
+
Permission.ProjectMember,
|
|
1431
|
+
Permission.Viewer,
|
|
1432
|
+
Permission.ReadProject,
|
|
1433
|
+
Permission.UnAuthorizedSsoUser,
|
|
1434
|
+
Permission.ProjectUser,
|
|
1435
|
+
],
|
|
1436
|
+
update: [Permission.ProjectOwner, Permission.ProjectAdmin],
|
|
1437
|
+
})
|
|
1438
|
+
@TableColumn({
|
|
1439
|
+
required: true,
|
|
1440
|
+
isDefaultValueColumn: true,
|
|
1441
|
+
type: TableColumnType.Boolean,
|
|
1442
|
+
title: "Enable Automatic Incident Investigation",
|
|
1443
|
+
description:
|
|
1444
|
+
"When enabled, OneUptime's AI SRE (Sentinel) automatically investigates every new incident and posts a cited root cause analysis to the incident timeline. Requires AI to be enabled and an LLM provider to be configured.",
|
|
1445
|
+
defaultValue: false,
|
|
1446
|
+
example: true,
|
|
1447
|
+
})
|
|
1448
|
+
@Column({
|
|
1449
|
+
nullable: false,
|
|
1450
|
+
default: false,
|
|
1451
|
+
type: ColumnType.Boolean,
|
|
1452
|
+
})
|
|
1453
|
+
public enableAutomaticIncidentInvestigation?: boolean = undefined;
|
|
1454
|
+
|
|
1455
|
+
@ColumnAccessControl({
|
|
1456
|
+
create: [],
|
|
1457
|
+
read: [
|
|
1458
|
+
Permission.ProjectOwner,
|
|
1459
|
+
Permission.ProjectAdmin,
|
|
1460
|
+
Permission.ProjectMember,
|
|
1461
|
+
Permission.Viewer,
|
|
1462
|
+
Permission.ReadProject,
|
|
1463
|
+
Permission.UnAuthorizedSsoUser,
|
|
1464
|
+
Permission.ProjectUser,
|
|
1465
|
+
],
|
|
1466
|
+
update: [Permission.ProjectOwner, Permission.ProjectAdmin],
|
|
1467
|
+
})
|
|
1468
|
+
@TableColumn({
|
|
1469
|
+
required: true,
|
|
1470
|
+
isDefaultValueColumn: true,
|
|
1471
|
+
type: TableColumnType.Boolean,
|
|
1472
|
+
title: "Enable Automatic Alert Investigation",
|
|
1473
|
+
description:
|
|
1474
|
+
"When enabled, OneUptime's AI SRE (Sentinel) automatically investigates every new alert and posts a cited root cause analysis to the alert timeline. Requires AI to be enabled and an LLM provider to be configured.",
|
|
1475
|
+
defaultValue: false,
|
|
1476
|
+
example: true,
|
|
1477
|
+
})
|
|
1478
|
+
@Column({
|
|
1479
|
+
nullable: false,
|
|
1480
|
+
default: false,
|
|
1481
|
+
type: ColumnType.Boolean,
|
|
1482
|
+
})
|
|
1483
|
+
public enableAutomaticAlertInvestigation?: boolean = undefined;
|
|
1484
|
+
|
|
1425
1485
|
@ColumnAccessControl({
|
|
1426
1486
|
create: [],
|
|
1427
1487
|
read: [
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import UserMiddleware from "../Middleware/UserAuthorization";
|
|
2
|
+
import CommonAPI from "./CommonAPI";
|
|
3
|
+
import Express, {
|
|
4
|
+
ExpressRequest,
|
|
5
|
+
ExpressResponse,
|
|
6
|
+
ExpressRouter,
|
|
7
|
+
NextFunction,
|
|
8
|
+
} from "../Utils/Express";
|
|
9
|
+
import Response from "../Utils/Response";
|
|
10
|
+
import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
11
|
+
import ObjectID from "../../Types/ObjectID";
|
|
12
|
+
import BadDataException from "../../Types/Exception/BadDataException";
|
|
13
|
+
import NotAuthorizedException from "../../Types/Exception/NotAuthorizedException";
|
|
14
|
+
import SortOrder from "../../Types/BaseDatabase/SortOrder";
|
|
15
|
+
import { JSONArray, JSONObject } from "../../Types/JSON";
|
|
16
|
+
import AIRunType from "../../Types/AI/AIRunType";
|
|
17
|
+
import AIRun from "../../Models/DatabaseModels/AIRun";
|
|
18
|
+
import AIRunEvent from "../../Models/DatabaseModels/AIRunEvent";
|
|
19
|
+
import Incident from "../../Models/DatabaseModels/Incident";
|
|
20
|
+
import BaseModel from "../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
21
|
+
import IncidentService from "../Services/IncidentService";
|
|
22
|
+
import AIRunService from "../Services/AIRunService";
|
|
23
|
+
import AIRunEventService from "../Services/AIRunEventService";
|
|
24
|
+
|
|
25
|
+
const router: ExpressRouter = Express.getRouter();
|
|
26
|
+
|
|
27
|
+
const MAX_EVENTS: number = 500;
|
|
28
|
+
|
|
29
|
+
/*
|
|
30
|
+
* Returns the latest Sentinel investigation (the AIRun + its ordered
|
|
31
|
+
* AIRunEvents) for an incident, so the dashboard can render a live "watch it
|
|
32
|
+
* think" panel.
|
|
33
|
+
*
|
|
34
|
+
* Investigation runs are system-authored (userId = null) and are therefore
|
|
35
|
+
* hidden by the per-user privacy pin on the generic AIRun / AIRunEvent CRUD. So
|
|
36
|
+
* we gate access explicitly here: first confirm the caller can read the incident
|
|
37
|
+
* under THEIR permissions, then read the run + events as root.
|
|
38
|
+
*/
|
|
39
|
+
router.post(
|
|
40
|
+
"/ai-investigation/incident",
|
|
41
|
+
UserMiddleware.getUserMiddleware,
|
|
42
|
+
async (
|
|
43
|
+
req: ExpressRequest,
|
|
44
|
+
res: ExpressResponse,
|
|
45
|
+
next: NextFunction,
|
|
46
|
+
): Promise<void> => {
|
|
47
|
+
try {
|
|
48
|
+
const props: DatabaseCommonInteractionProps =
|
|
49
|
+
await CommonAPI.getDatabaseCommonInteractionProps(req);
|
|
50
|
+
|
|
51
|
+
if (!props.userId) {
|
|
52
|
+
throw new NotAuthorizedException(
|
|
53
|
+
"A logged-in user session is required.",
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const incidentIdString: string | undefined = req.body["incidentId"] as
|
|
58
|
+
| string
|
|
59
|
+
| undefined;
|
|
60
|
+
|
|
61
|
+
if (!incidentIdString) {
|
|
62
|
+
throw new BadDataException("incidentId is required.");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const incidentId: ObjectID = new ObjectID(incidentIdString);
|
|
66
|
+
|
|
67
|
+
// Access check under the USER's permissions (null when not allowed).
|
|
68
|
+
const incident: Incident | null = await IncidentService.findOneById({
|
|
69
|
+
id: incidentId,
|
|
70
|
+
select: { _id: true },
|
|
71
|
+
props,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (!incident) {
|
|
75
|
+
throw new BadDataException(
|
|
76
|
+
"Incident not found (or you do not have access to it).",
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Read the latest investigation run as root (bypasses the per-user pin).
|
|
81
|
+
const runs: Array<AIRun> = await AIRunService.findBy({
|
|
82
|
+
query: {
|
|
83
|
+
triggeredByIncidentId: incidentId,
|
|
84
|
+
runType: AIRunType.Investigation,
|
|
85
|
+
},
|
|
86
|
+
select: {
|
|
87
|
+
_id: true,
|
|
88
|
+
status: true,
|
|
89
|
+
startedAt: true,
|
|
90
|
+
completedAt: true,
|
|
91
|
+
errorMessage: true,
|
|
92
|
+
llmCallCount: true,
|
|
93
|
+
toolCallCount: true,
|
|
94
|
+
totalTokens: true,
|
|
95
|
+
createdAt: true,
|
|
96
|
+
},
|
|
97
|
+
sort: { createdAt: SortOrder.Descending },
|
|
98
|
+
limit: 1,
|
|
99
|
+
skip: 0,
|
|
100
|
+
props: { isRoot: true },
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const run: AIRun | undefined = runs[0];
|
|
104
|
+
|
|
105
|
+
if (!run) {
|
|
106
|
+
Response.sendJsonObjectResponse(req, res, {
|
|
107
|
+
run: null,
|
|
108
|
+
events: [],
|
|
109
|
+
});
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const events: Array<AIRunEvent> = await AIRunEventService.findBy({
|
|
114
|
+
query: { aiRunId: run.id! },
|
|
115
|
+
select: {
|
|
116
|
+
_id: true,
|
|
117
|
+
sequence: true,
|
|
118
|
+
eventType: true,
|
|
119
|
+
toolName: true,
|
|
120
|
+
resultSummary: true,
|
|
121
|
+
createdAt: true,
|
|
122
|
+
},
|
|
123
|
+
sort: { sequence: SortOrder.Ascending },
|
|
124
|
+
limit: MAX_EVENTS,
|
|
125
|
+
skip: 0,
|
|
126
|
+
props: { isRoot: true },
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const runJson: JSONObject | undefined = BaseModel.toJSONArray(
|
|
130
|
+
[run],
|
|
131
|
+
AIRun,
|
|
132
|
+
)[0];
|
|
133
|
+
|
|
134
|
+
const eventsJson: JSONArray = BaseModel.toJSONArray(events, AIRunEvent);
|
|
135
|
+
|
|
136
|
+
Response.sendJsonObjectResponse(req, res, {
|
|
137
|
+
run: runJson || null,
|
|
138
|
+
events: eventsJson,
|
|
139
|
+
});
|
|
140
|
+
return;
|
|
141
|
+
} catch (err) {
|
|
142
|
+
next(err);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
export default router;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class AddEnableAutomaticIncidentInvestigationToProject1783598145111
|
|
4
|
+
implements MigrationInterface
|
|
5
|
+
{
|
|
6
|
+
public name = "AddEnableAutomaticIncidentInvestigationToProject1783598145111";
|
|
7
|
+
|
|
8
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
9
|
+
await queryRunner.query(
|
|
10
|
+
`ALTER TABLE "Project" ADD "enableAutomaticIncidentInvestigation" boolean NOT NULL DEFAULT false`,
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
15
|
+
await queryRunner.query(
|
|
16
|
+
`ALTER TABLE "Project" DROP COLUMN "enableAutomaticIncidentInvestigation"`,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class AddInvestigationSubjectToAIRun1783615884106
|
|
4
|
+
implements MigrationInterface
|
|
5
|
+
{
|
|
6
|
+
public name = "AddInvestigationSubjectToAIRun1783615884106";
|
|
7
|
+
|
|
8
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
9
|
+
await queryRunner.query(
|
|
10
|
+
`ALTER TABLE "AIRun" ADD "triggeredByIncidentId" uuid`,
|
|
11
|
+
);
|
|
12
|
+
await queryRunner.query(
|
|
13
|
+
`ALTER TABLE "AIRun" ADD "triggeredByAlertId" uuid`,
|
|
14
|
+
);
|
|
15
|
+
await queryRunner.query(
|
|
16
|
+
`CREATE INDEX "IDX_AIRun_triggeredByIncidentId" ON "AIRun" ("triggeredByIncidentId")`,
|
|
17
|
+
);
|
|
18
|
+
await queryRunner.query(
|
|
19
|
+
`CREATE INDEX "IDX_AIRun_triggeredByAlertId" ON "AIRun" ("triggeredByAlertId")`,
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
24
|
+
await queryRunner.query(`DROP INDEX "IDX_AIRun_triggeredByAlertId"`);
|
|
25
|
+
await queryRunner.query(`DROP INDEX "IDX_AIRun_triggeredByIncidentId"`);
|
|
26
|
+
await queryRunner.query(
|
|
27
|
+
`ALTER TABLE "AIRun" DROP COLUMN "triggeredByAlertId"`,
|
|
28
|
+
);
|
|
29
|
+
await queryRunner.query(
|
|
30
|
+
`ALTER TABLE "AIRun" DROP COLUMN "triggeredByIncidentId"`,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class AddEnableAutomaticAlertInvestigationToProject1783632447975
|
|
4
|
+
implements MigrationInterface
|
|
5
|
+
{
|
|
6
|
+
public name = "AddEnableAutomaticAlertInvestigationToProject1783632447975";
|
|
7
|
+
|
|
8
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
9
|
+
await queryRunner.query(
|
|
10
|
+
`ALTER TABLE "Project" ADD "enableAutomaticAlertInvestigation" boolean NOT NULL DEFAULT false`,
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
15
|
+
await queryRunner.query(
|
|
16
|
+
`ALTER TABLE "Project" DROP COLUMN "enableAutomaticAlertInvestigation"`,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -417,6 +417,9 @@ import { AddIncomingCallPolicyRoutingPhoneNumberUnique1783470000000 } from "./17
|
|
|
417
417
|
import { FixNotNullForeignKeysOnDelete1783510935686 } from "./1783510935686-FixNotNullForeignKeysOnDelete";
|
|
418
418
|
import { MigrationName1783515836148 } from "./1783515836148-MigrationName";
|
|
419
419
|
import { AddMigrationFailureTable1783523076215 } from "./1783523076215-AddMigrationFailureTable";
|
|
420
|
+
import { AddEnableAutomaticIncidentInvestigationToProject1783598145111 } from "./1783598145111-AddEnableAutomaticIncidentInvestigationToProject";
|
|
421
|
+
import { AddInvestigationSubjectToAIRun1783615884106 } from "./1783615884106-AddInvestigationSubjectToAIRun";
|
|
422
|
+
import { AddEnableAutomaticAlertInvestigationToProject1783632447975 } from "./1783632447975-AddEnableAutomaticAlertInvestigationToProject";
|
|
420
423
|
|
|
421
424
|
export default [
|
|
422
425
|
InitialMigration,
|
|
@@ -838,4 +841,7 @@ export default [
|
|
|
838
841
|
FixNotNullForeignKeysOnDelete1783510935686,
|
|
839
842
|
MigrationName1783515836148,
|
|
840
843
|
AddMigrationFailureTable1783523076215,
|
|
844
|
+
AddEnableAutomaticIncidentInvestigationToProject1783598145111,
|
|
845
|
+
AddInvestigationSubjectToAIRun1783615884106,
|
|
846
|
+
AddEnableAutomaticAlertInvestigationToProject1783632447975,
|
|
841
847
|
];
|
|
@@ -67,6 +67,7 @@ import AlertLabelRuleEngineService from "./AlertLabelRuleEngineService";
|
|
|
67
67
|
import AlertOnCallRuleEngineService from "./AlertOnCallRuleEngineService";
|
|
68
68
|
import AlertOwnerRuleEngineService from "./AlertOwnerRuleEngineService";
|
|
69
69
|
import RunbookRuleEngineService from "./RunbookRuleEngineService";
|
|
70
|
+
import SentinelAlertInvestigationRunner from "../Utils/AI/Sentinel/AlertInvestigationRunner";
|
|
70
71
|
import AlertPrivacyRuleEngineService from "./AlertPrivacyRuleEngineService";
|
|
71
72
|
import ProjectService from "./ProjectService";
|
|
72
73
|
|
|
@@ -558,6 +559,30 @@ export class Service extends DatabaseService<Model> {
|
|
|
558
559
|
);
|
|
559
560
|
}
|
|
560
561
|
})
|
|
562
|
+
.then(async () => {
|
|
563
|
+
/*
|
|
564
|
+
* Sentinel (AI SRE): automatically investigate the new alert and post a
|
|
565
|
+
* cited root cause analysis to the alert timeline + Slack/Teams. Runs
|
|
566
|
+
* last, is gated per project (opt-in) + requires an LLM provider, and is
|
|
567
|
+
* read-only.
|
|
568
|
+
*/
|
|
569
|
+
try {
|
|
570
|
+
if (createdItem.projectId && createdItem.id) {
|
|
571
|
+
await SentinelAlertInvestigationRunner.investigateNewAlert({
|
|
572
|
+
alertId: createdItem.id,
|
|
573
|
+
projectId: createdItem.projectId,
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
} catch (error) {
|
|
577
|
+
logger.error(
|
|
578
|
+
`Sentinel alert investigation failed in AlertService.onCreateSuccess: ${error}`,
|
|
579
|
+
{
|
|
580
|
+
projectId: createdItem.projectId?.toString(),
|
|
581
|
+
alertId: createdItem.id?.toString(),
|
|
582
|
+
} as LogAttributes,
|
|
583
|
+
);
|
|
584
|
+
}
|
|
585
|
+
})
|
|
561
586
|
.catch((error: Error) => {
|
|
562
587
|
logger.error(
|
|
563
588
|
`Critical error in AlertService sequential operations: ${error}`,
|
|
@@ -89,6 +89,7 @@ import LLMService, {
|
|
|
89
89
|
} from "../Utils/LLM/LLMService";
|
|
90
90
|
import LlmProviderService from "./LlmProviderService";
|
|
91
91
|
import LlmProvider from "../../Models/DatabaseModels/LlmProvider";
|
|
92
|
+
import SentinelIncidentInvestigationRunner from "../Utils/AI/Sentinel/IncidentInvestigationRunner";
|
|
92
93
|
import IncidentAIContextBuilder, {
|
|
93
94
|
AIGenerationContext,
|
|
94
95
|
IncidentContextData,
|
|
@@ -1241,6 +1242,30 @@ export class Service extends DatabaseService<Model> {
|
|
|
1241
1242
|
);
|
|
1242
1243
|
}
|
|
1243
1244
|
})
|
|
1245
|
+
.then(async () => {
|
|
1246
|
+
/*
|
|
1247
|
+
* Sentinel (AI SRE): automatically investigate the new incident and post
|
|
1248
|
+
* a cited root cause analysis to the incident timeline + Slack/Teams.
|
|
1249
|
+
* Runs last so the workspace channels already exist, and is gated per
|
|
1250
|
+
* project (opt-in) + requires a configured LLM provider. Read-only.
|
|
1251
|
+
*/
|
|
1252
|
+
try {
|
|
1253
|
+
if (createdItem.projectId && createdItem.id) {
|
|
1254
|
+
await SentinelIncidentInvestigationRunner.investigateNewIncident({
|
|
1255
|
+
incidentId: createdItem.id,
|
|
1256
|
+
projectId: createdItem.projectId,
|
|
1257
|
+
});
|
|
1258
|
+
}
|
|
1259
|
+
} catch (error) {
|
|
1260
|
+
logger.error(
|
|
1261
|
+
`Sentinel incident investigation failed in IncidentService.onCreateSuccess: ${error}`,
|
|
1262
|
+
{
|
|
1263
|
+
projectId: createdItem.projectId?.toString(),
|
|
1264
|
+
incidentId: createdItem.id?.toString(),
|
|
1265
|
+
} as LogAttributes,
|
|
1266
|
+
);
|
|
1267
|
+
}
|
|
1268
|
+
})
|
|
1244
1269
|
.catch((error: Error) => {
|
|
1245
1270
|
logger.error(
|
|
1246
1271
|
`Critical error in IncidentService sequential operations: ${error}`,
|
|
@@ -27,6 +27,7 @@ import IncidentRole from "../../Models/DatabaseModels/IncidentRole";
|
|
|
27
27
|
import { IsBillingEnabled } from "../EnvironmentConfig";
|
|
28
28
|
import logger, { LogAttributes } from "../Utils/Logger";
|
|
29
29
|
import IncidentFeedService from "./IncidentFeedService";
|
|
30
|
+
import SentinelIncidentPostmortemRunner from "../Utils/AI/Sentinel/IncidentPostmortemRunner";
|
|
30
31
|
import { IncidentFeedEventType } from "../../Models/DatabaseModels/IncidentFeed";
|
|
31
32
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
32
33
|
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
@@ -555,6 +556,24 @@ ${createdItem.rootCause}`,
|
|
|
555
556
|
createdItem.startsAt || undefined,
|
|
556
557
|
);
|
|
557
558
|
}
|
|
559
|
+
|
|
560
|
+
/*
|
|
561
|
+
* Sentinel: auto-draft a postmortem now that the incident is resolved
|
|
562
|
+
* (gated per project; never overwrites an existing postmortem).
|
|
563
|
+
*/
|
|
564
|
+
SentinelIncidentPostmortemRunner.draftPostmortemOnResolve({
|
|
565
|
+
incidentId: createdItem.incidentId!,
|
|
566
|
+
projectId: createdItem.projectId!,
|
|
567
|
+
}).catch((error: Error) => {
|
|
568
|
+
logger.error(`Sentinel auto-postmortem failed on resolve:`, {
|
|
569
|
+
projectId: createdItem.projectId?.toString(),
|
|
570
|
+
incidentId: createdItem.incidentId?.toString(),
|
|
571
|
+
} as LogAttributes);
|
|
572
|
+
logger.error(error, {
|
|
573
|
+
projectId: createdItem.projectId?.toString(),
|
|
574
|
+
incidentId: createdItem.incidentId?.toString(),
|
|
575
|
+
} as LogAttributes);
|
|
576
|
+
});
|
|
558
577
|
}
|
|
559
578
|
|
|
560
579
|
if (onCreate.carryForward.publicNote) {
|