@oneuptime/common 11.3.28 → 11.4.1

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.
Files changed (244) hide show
  1. package/Models/DatabaseModels/AIRun.ts +50 -0
  2. package/Models/DatabaseModels/Project.ts +60 -0
  3. package/Server/API/AIAgentTaskAPI.ts +10 -24
  4. package/Server/API/AIInvestigationAPI.ts +148 -0
  5. package/Server/API/TelemetryAPI.ts +4 -3
  6. package/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.ts +19 -0
  7. package/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.ts +33 -0
  8. package/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.ts +19 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  10. package/Server/Services/AIAgentTaskService.ts +62 -0
  11. package/Server/Services/AlertService.ts +25 -0
  12. package/Server/Services/AnalyticsDatabaseService.ts +27 -0
  13. package/Server/Services/IncidentService.ts +25 -0
  14. package/Server/Services/IncidentStateTimelineService.ts +19 -0
  15. package/Server/Services/LogAggregationService.ts +29 -11
  16. package/Server/Services/MetricService.ts +603 -13
  17. package/Server/Utils/AI/Chat/ObservabilityAssistant.ts +119 -11
  18. package/Server/Utils/AI/Sentinel/AlertInvestigationRunner.ts +141 -0
  19. package/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.ts +178 -0
  20. package/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.ts +105 -0
  21. package/Server/Utils/AI/Sentinel/README.md +10 -0
  22. package/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.ts +405 -0
  23. package/Server/Utils/AI/Sentinel/SentinelMemory.ts +167 -0
  24. package/Server/Utils/AI/Toolbox/Index.ts +11 -0
  25. package/Server/Utils/AI/Toolbox/SentinelActionTools.ts +577 -0
  26. package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +44 -0
  27. package/Server/Utils/Monitor/MonitorMetricUtil.ts +108 -2
  28. package/Tests/Server/API/BaseAPI.test.ts +1 -0
  29. package/Tests/Server/Services/AIAgentTaskServiceClaim.test.ts +146 -0
  30. package/Tests/Server/Services/LogAggregationService.test.ts +22 -0
  31. package/Tests/Server/Services/MetricServiceAggregate.test.ts +194 -0
  32. package/Tests/Types/AI/AIAgentTaskStatus.test.ts +86 -0
  33. package/Tests/Types/BaseDatabase/AggregationIntervalUtil.test.ts +127 -0
  34. package/Tests/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.test.ts +152 -0
  35. package/Tests/Types/OnCallDutyPolicy/ScheduleShiftUtil.test.ts +260 -0
  36. package/Tests/Types/Trace/TraceRecordingRuleDefinition.test.ts +308 -0
  37. package/Tests/UI/Components/Dropdown.test.tsx +17 -0
  38. package/Tests/UI/Components/EntityDropdown.test.tsx +130 -0
  39. package/Tests/UI/Components/Modal.test.tsx +144 -2
  40. package/Tests/UI/Components/TelemetrySearchBar.test.tsx +90 -0
  41. package/Tests/UI/Utils/Theme.test.ts +161 -0
  42. package/Types/BaseDatabase/AggregateBy.ts +12 -0
  43. package/Types/Dashboard/DashboardComponentType.ts +1 -0
  44. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
  45. package/Types/Dashboard/DashboardComponents/DashboardLogChartComponent.ts +19 -0
  46. package/Types/Dashboard/DashboardTemplates.ts +9 -1
  47. package/Types/Date.ts +46 -0
  48. package/Types/Monitor/CriteriaFilter.ts +4 -0
  49. package/Types/Monitor/HttpPhaseTimings.ts +16 -0
  50. package/Types/Monitor/MonitorMetricType.ts +17 -0
  51. package/Types/Monitor/PingMonitor/PingMonitorResponse.ts +20 -0
  52. package/Types/OnCallDutyPolicy/ScheduleShiftUtil.ts +257 -0
  53. package/Types/Probe/ProbeMonitorResponse.ts +14 -0
  54. package/UI/Components/Calendar/Calendar.tsx +8 -2
  55. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +5 -5
  56. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +8 -5
  57. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +4 -1
  58. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +6 -3
  59. package/UI/Components/CodeBlock/CodeBlock.tsx +2 -1
  60. package/UI/Components/CodeEditor/CodeEditor.tsx +3 -0
  61. package/UI/Components/Divider/Divider.tsx +1 -1
  62. package/UI/Components/Dropdown/Dropdown.tsx +13 -4
  63. package/UI/Components/EntityDropdown/EntityDropdown.tsx +110 -2
  64. package/UI/Components/ErrorBoundary.tsx +2 -1
  65. package/UI/Components/EventItem/EventItem.tsx +2 -2
  66. package/UI/Components/GanttChart/Bar/Index.tsx +1 -1
  67. package/UI/Components/Graphs/UptimeBarTooltip.tsx +30 -23
  68. package/UI/Components/Header/Header.tsx +7 -0
  69. package/UI/Components/Header/HeaderIconDropdownButton.tsx +2 -0
  70. package/UI/Components/Label/Label.tsx +2 -2
  71. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +22 -10
  72. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +9 -3
  73. package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +8 -8
  74. package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +44 -20
  75. package/UI/Components/Modal/ConfirmModal.tsx +1 -1
  76. package/UI/Components/Modal/Modal.tsx +213 -121
  77. package/UI/Components/Modal/ModalFooter.tsx +33 -22
  78. package/UI/Components/ModelTable/TableView.tsx +1 -1
  79. package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +20 -12
  80. package/UI/Components/Pill/Pill.tsx +21 -13
  81. package/UI/Components/StatusBubble/StatusBubble.tsx +3 -3
  82. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +7 -1
  83. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +9 -3
  84. package/UI/Components/TelemetryViewer/components/TelemetrySearchBar.tsx +62 -4
  85. package/UI/Components/Workflow/Component.tsx +53 -33
  86. package/UI/Components/Workflow/ComponentPortViewer.tsx +6 -6
  87. package/UI/Components/Workflow/ComponentReturnValueViewer.tsx +8 -8
  88. package/UI/Components/Workflow/ComponentsModal.tsx +16 -9
  89. package/UI/Components/Workflow/Workflow.tsx +13 -11
  90. package/UI/Utils/Theme.ts +140 -0
  91. package/Utils/Dashboard/Components/DashboardLogChartComponent.ts +104 -0
  92. package/Utils/Dashboard/Components/Index.ts +7 -0
  93. package/Utils/Monitor/MonitorMetricType.ts +70 -2
  94. package/build/dist/Models/DatabaseModels/AIRun.js +52 -0
  95. package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
  96. package/build/dist/Models/DatabaseModels/Project.js +62 -0
  97. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  98. package/build/dist/Server/API/AIAgentTaskAPI.js +8 -23
  99. package/build/dist/Server/API/AIAgentTaskAPI.js.map +1 -1
  100. package/build/dist/Server/API/AIInvestigationAPI.js +107 -0
  101. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -0
  102. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  103. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.js +12 -0
  104. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783598145111-AddEnableAutomaticIncidentInvestigationToProject.js.map +1 -0
  105. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.js +18 -0
  106. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783615884106-AddInvestigationSubjectToAIRun.js.map +1 -0
  107. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.js +12 -0
  108. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783632447975-AddEnableAutomaticAlertInvestigationToProject.js.map +1 -0
  109. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  110. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  111. package/build/dist/Server/Services/AIAgentTaskService.js +59 -0
  112. package/build/dist/Server/Services/AIAgentTaskService.js.map +1 -1
  113. package/build/dist/Server/Services/AlertService.js +24 -0
  114. package/build/dist/Server/Services/AlertService.js.map +1 -1
  115. package/build/dist/Server/Services/AnalyticsDatabaseService.js +19 -0
  116. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  117. package/build/dist/Server/Services/IncidentService.js +24 -0
  118. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  119. package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
  120. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  121. package/build/dist/Server/Services/LogAggregationService.js +21 -7
  122. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  123. package/build/dist/Server/Services/MetricService.js +449 -7
  124. package/build/dist/Server/Services/MetricService.js.map +1 -1
  125. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js +59 -11
  126. package/build/dist/Server/Utils/AI/Chat/ObservabilityAssistant.js.map +1 -1
  127. package/build/dist/Server/Utils/AI/Sentinel/AlertInvestigationRunner.js +119 -0
  128. package/build/dist/Server/Utils/AI/Sentinel/AlertInvestigationRunner.js.map +1 -0
  129. package/build/dist/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.js +147 -0
  130. package/build/dist/Server/Utils/AI/Sentinel/IncidentInvestigationRunner.js.map +1 -0
  131. package/build/dist/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.js +93 -0
  132. package/build/dist/Server/Utils/AI/Sentinel/IncidentPostmortemRunner.js.map +1 -0
  133. package/build/dist/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.js +311 -0
  134. package/build/dist/Server/Utils/AI/Sentinel/SentinelInvestigationEngine.js.map +1 -0
  135. package/build/dist/Server/Utils/AI/Sentinel/SentinelMemory.js +143 -0
  136. package/build/dist/Server/Utils/AI/Sentinel/SentinelMemory.js.map +1 -0
  137. package/build/dist/Server/Utils/AI/Toolbox/Index.js +6 -0
  138. package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
  139. package/build/dist/Server/Utils/AI/Toolbox/SentinelActionTools.js +451 -0
  140. package/build/dist/Server/Utils/AI/Toolbox/SentinelActionTools.js.map +1 -0
  141. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +29 -0
  142. package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
  143. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +86 -2
  144. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  145. package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
  146. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  147. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
  148. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  149. package/build/dist/Types/Dashboard/DashboardComponents/DashboardLogChartComponent.js +2 -0
  150. package/build/dist/Types/Dashboard/DashboardComponents/DashboardLogChartComponent.js.map +1 -0
  151. package/build/dist/Types/Dashboard/DashboardTemplates.js +9 -1
  152. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  153. package/build/dist/Types/Date.js +32 -0
  154. package/build/dist/Types/Date.js.map +1 -1
  155. package/build/dist/Types/Monitor/CriteriaFilter.js +4 -0
  156. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  157. package/build/dist/Types/Monitor/HttpPhaseTimings.js +2 -0
  158. package/build/dist/Types/Monitor/HttpPhaseTimings.js.map +1 -0
  159. package/build/dist/Types/Monitor/MonitorMetricType.js +15 -0
  160. package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
  161. package/build/dist/Types/Monitor/PingMonitor/PingMonitorResponse.js +2 -0
  162. package/build/dist/Types/Monitor/PingMonitor/PingMonitorResponse.js.map +1 -0
  163. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js +159 -0
  164. package/build/dist/Types/OnCallDutyPolicy/ScheduleShiftUtil.js.map +1 -0
  165. package/build/dist/UI/Components/Calendar/Calendar.js +2 -2
  166. package/build/dist/UI/Components/Calendar/Calendar.js.map +1 -1
  167. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +2 -2
  168. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  169. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +7 -4
  170. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  171. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +4 -1
  172. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  173. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +6 -3
  174. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  175. package/build/dist/UI/Components/CodeBlock/CodeBlock.js +1 -1
  176. package/build/dist/UI/Components/CodeBlock/CodeBlock.js.map +1 -1
  177. package/build/dist/UI/Components/CodeEditor/CodeEditor.js +3 -1
  178. package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
  179. package/build/dist/UI/Components/Divider/Divider.js +1 -1
  180. package/build/dist/UI/Components/Divider/Divider.js.map +1 -1
  181. package/build/dist/UI/Components/Dropdown/Dropdown.js +10 -4
  182. package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
  183. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +76 -3
  184. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  185. package/build/dist/UI/Components/ErrorBoundary.js +2 -1
  186. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  187. package/build/dist/UI/Components/EventItem/EventItem.js +2 -2
  188. package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
  189. package/build/dist/UI/Components/GanttChart/Bar/Index.js +1 -1
  190. package/build/dist/UI/Components/GanttChart/Bar/Index.js.map +1 -1
  191. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +29 -23
  192. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -1
  193. package/build/dist/UI/Components/Header/Header.js +1 -0
  194. package/build/dist/UI/Components/Header/Header.js.map +1 -1
  195. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +1 -1
  196. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  197. package/build/dist/UI/Components/Label/Label.js +2 -2
  198. package/build/dist/UI/Components/Label/Label.js.map +1 -1
  199. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +20 -8
  200. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  201. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +8 -2
  202. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  203. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +8 -8
  204. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js +40 -20
  205. package/build/dist/UI/Components/Markdown.tsx/MarkdownViewer.js.map +1 -1
  206. package/build/dist/UI/Components/Modal/ConfirmModal.js +1 -1
  207. package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
  208. package/build/dist/UI/Components/Modal/Modal.js +109 -51
  209. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  210. package/build/dist/UI/Components/Modal/ModalFooter.js +13 -10
  211. package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
  212. package/build/dist/UI/Components/ModelTable/TableView.js +1 -1
  213. package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
  214. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +17 -12
  215. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -1
  216. package/build/dist/UI/Components/Pill/Pill.js +12 -13
  217. package/build/dist/UI/Components/Pill/Pill.js.map +1 -1
  218. package/build/dist/UI/Components/StatusBubble/StatusBubble.js +3 -3
  219. package/build/dist/UI/Components/StatusBubble/StatusBubble.js.map +1 -1
  220. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +1 -1
  221. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  222. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +8 -2
  223. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  224. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js +36 -6
  225. package/build/dist/UI/Components/TelemetryViewer/components/TelemetrySearchBar.js.map +1 -1
  226. package/build/dist/UI/Components/Workflow/Component.js +43 -33
  227. package/build/dist/UI/Components/Workflow/Component.js.map +1 -1
  228. package/build/dist/UI/Components/Workflow/ComponentPortViewer.js +6 -6
  229. package/build/dist/UI/Components/Workflow/ComponentPortViewer.js.map +1 -1
  230. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js +8 -8
  231. package/build/dist/UI/Components/Workflow/ComponentReturnValueViewer.js.map +1 -1
  232. package/build/dist/UI/Components/Workflow/ComponentsModal.js +15 -9
  233. package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
  234. package/build/dist/UI/Components/Workflow/Workflow.js +13 -11
  235. package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
  236. package/build/dist/UI/Utils/Theme.js +106 -0
  237. package/build/dist/UI/Utils/Theme.js.map +1 -0
  238. package/build/dist/Utils/Dashboard/Components/DashboardLogChartComponent.js +92 -0
  239. package/build/dist/Utils/Dashboard/Components/DashboardLogChartComponent.js.map +1 -0
  240. package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
  241. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  242. package/build/dist/Utils/Monitor/MonitorMetricType.js +70 -3
  243. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  244. 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: [
@@ -16,7 +16,6 @@ import { JSONObject } from "../../Types/JSON";
16
16
  import ObjectID from "../../Types/ObjectID";
17
17
  import OneUptimeDate from "../../Types/Date";
18
18
  import AIAgentTaskStatus from "../../Types/AI/AIAgentTaskStatus";
19
- import SortOrder from "../../Types/BaseDatabase/SortOrder";
20
19
  import PositiveNumber from "../../Types/PositiveNumber";
21
20
 
22
21
  export default class AIAgentTaskAPI extends BaseAPI<
@@ -27,8 +26,10 @@ export default class AIAgentTaskAPI extends BaseAPI<
27
26
  super(AIAgentTask, AIAgentTaskService);
28
27
 
29
28
  /*
30
- * Get the next pending (scheduled) task for processing
31
- * Validates aiAgentId and aiAgentKey before returning task
29
+ * Claim the next pending (scheduled) task for processing.
30
+ * Validates aiAgentId and aiAgentKey before claiming. The task is
31
+ * atomically transitioned Scheduled -> InProgress before it is
32
+ * returned, so concurrent workers can never receive the same task.
32
33
  */
33
34
  this.router.post(
34
35
  `${new this.entityType().getCrudApiPath()?.toString()}/get-pending-task`,
@@ -39,7 +40,7 @@ export default class AIAgentTaskAPI extends BaseAPI<
39
40
  /* Validate AI Agent credentials */
40
41
  const aiAgent: AIAgent | null = await this.validateAIAgent(data);
41
42
 
42
- if (!aiAgent) {
43
+ if (!aiAgent || !aiAgent.id) {
43
44
  return Response.sendErrorResponse(
44
45
  req,
45
46
  res,
@@ -47,25 +48,10 @@ export default class AIAgentTaskAPI extends BaseAPI<
47
48
  );
48
49
  }
49
50
 
50
- /* Fetch one scheduled task, sorted by createdAt (oldest first) */
51
- const task: AIAgentTask | null = await AIAgentTaskService.findOneBy({
52
- query: {
53
- status: AIAgentTaskStatus.Scheduled,
54
- },
55
- sort: {
56
- createdAt: SortOrder.Ascending,
57
- },
58
- select: {
59
- _id: true,
60
- projectId: true,
61
- taskType: true,
62
- metadata: true,
63
- createdAt: true,
64
- },
65
- props: {
66
- isRoot: true,
67
- },
68
- });
51
+ const task: AIAgentTask | null =
52
+ await AIAgentTaskService.claimNextScheduledTask({
53
+ aiAgentId: aiAgent.id,
54
+ });
69
55
 
70
56
  if (!task) {
71
57
  return Response.sendJsonObjectResponse(req, res, {
@@ -82,7 +68,7 @@ export default class AIAgentTaskAPI extends BaseAPI<
82
68
  metadata: task.metadata as JSONObject | undefined,
83
69
  createdAt: task.createdAt,
84
70
  },
85
- message: "Task fetched successfully",
71
+ message: "Task claimed successfully",
86
72
  });
87
73
  } catch (err) {
88
74
  next(err);
@@ -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;
@@ -404,9 +404,10 @@ router.post(
404
404
  ? (body["spanIds"] as Array<string>)
405
405
  : undefined;
406
406
 
407
- const attributes: Record<string, string> | undefined = body["attributes"]
408
- ? (body["attributes"] as Record<string, string>)
409
- : undefined;
407
+ const attributes: Record<string, string | Array<string>> | undefined =
408
+ body["attributes"]
409
+ ? (body["attributes"] as Record<string, string | Array<string>>)
410
+ : undefined;
410
411
 
411
412
  const request: HistogramRequest = {
412
413
  projectId: databaseProps.tenantId,
@@ -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
  ];
@@ -11,6 +11,7 @@ import Semaphore, { SemaphoreMutex } from "../Infrastructure/Semaphore";
11
11
  import SortOrder from "../../Types/BaseDatabase/SortOrder";
12
12
  import logger from "../Utils/Logger";
13
13
  import OneUptimeDate from "../../Types/Date";
14
+ import AIAgentTaskStatus from "../../Types/AI/AIAgentTaskStatus";
14
15
 
15
16
  export class Service extends DatabaseService<Model> {
16
17
  public constructor() {
@@ -80,6 +81,67 @@ export class Service extends DatabaseService<Model> {
80
81
  return { createBy, carryForward: null };
81
82
  }
82
83
 
84
+ /*
85
+ * Atomically claim the oldest Scheduled task for a worker: the
86
+ * Scheduled -> InProgress transition is guarded on status, so when
87
+ * multiple workers poll concurrently only one claim succeeds
88
+ * (updateOneBy returns the number of rows changed — zero means another
89
+ * worker won the race, so we retry with the next candidate). The
90
+ * returned task is already InProgress and owned by the given agent.
91
+ */
92
+ @CaptureSpan()
93
+ public async claimNextScheduledTask(data: {
94
+ aiAgentId: ObjectID;
95
+ }): Promise<Model | null> {
96
+ const maxClaimAttempts: number = 5;
97
+
98
+ for (let attempt: number = 0; attempt < maxClaimAttempts; attempt++) {
99
+ const task: Model | null = await this.findOneBy({
100
+ query: {
101
+ status: AIAgentTaskStatus.Scheduled,
102
+ },
103
+ sort: {
104
+ createdAt: SortOrder.Ascending,
105
+ },
106
+ select: {
107
+ _id: true,
108
+ projectId: true,
109
+ taskType: true,
110
+ metadata: true,
111
+ createdAt: true,
112
+ },
113
+ props: {
114
+ isRoot: true,
115
+ },
116
+ });
117
+
118
+ if (!task || !task.id) {
119
+ return null;
120
+ }
121
+
122
+ const claimedCount: number = await this.updateOneBy({
123
+ query: {
124
+ _id: task.id.toString(),
125
+ status: AIAgentTaskStatus.Scheduled,
126
+ },
127
+ data: {
128
+ status: AIAgentTaskStatus.InProgress,
129
+ aiAgentId: data.aiAgentId,
130
+ startedAt: OneUptimeDate.getCurrentDate(),
131
+ },
132
+ props: {
133
+ isRoot: true,
134
+ },
135
+ });
136
+
137
+ if (claimedCount > 0) {
138
+ return task;
139
+ }
140
+ }
141
+
142
+ return null;
143
+ }
144
+
83
145
  @CaptureSpan()
84
146
  public async getExistingTaskNumberForProject(data: {
85
147
  projectId: ObjectID;
@@ -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}`,
@@ -813,6 +813,19 @@ export default class AnalyticsDatabaseService<
813
813
  ? Object.keys(aggregateBy.groupBy)
814
814
  : [];
815
815
 
816
+ /*
817
+ * Attribute-key grouping returns the selected keys folded into a
818
+ * single `attributes` map column (see MetricService); carry it
819
+ * onto the aggregated rows so series splitters can read labels.
820
+ */
821
+ if (
822
+ aggregateBy.groupByAttributeKeys &&
823
+ aggregateBy.groupByAttributeKeys.length > 0 &&
824
+ !groupByColumnNames.includes("attributes")
825
+ ) {
826
+ groupByColumnNames.push("attributes");
827
+ }
828
+
816
829
  for (const item of items) {
817
830
  if (
818
831
  !(item as JSONObject)[
@@ -1171,6 +1184,20 @@ export default class AnalyticsDatabaseService<
1171
1184
  statement: Statement;
1172
1185
  columns: Array<string>;
1173
1186
  } {
1187
+ /*
1188
+ * Attribute-key grouping needs model-specific SQL over a map column
1189
+ * (MetricService builds it); reaching this generic path with keys
1190
+ * set would silently return ungrouped results.
1191
+ */
1192
+ if (
1193
+ aggregateBy.groupByAttributeKeys &&
1194
+ aggregateBy.groupByAttributeKeys.length > 0
1195
+ ) {
1196
+ throw new BadDataException(
1197
+ `groupByAttributeKeys is not supported for ${this.model.tableName}.`,
1198
+ );
1199
+ }
1200
+
1174
1201
  if (!this.database) {
1175
1202
  this.useDefaultDatabase();
1176
1203
  }
@@ -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) {