@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.
Files changed (193) hide show
  1. package/Models/DatabaseModels/AIInsight.ts +30 -0
  2. package/Models/DatabaseModels/EnterpriseLicense.ts +21 -0
  3. package/Models/DatabaseModels/EnterpriseLicenseInstance.ts +25 -0
  4. package/Models/DatabaseModels/GlobalConfig.ts +76 -0
  5. package/Models/DatabaseModels/Project.ts +30 -0
  6. package/Models/DatabaseModels/TelemetryException.ts +128 -0
  7. package/Server/API/AIAgentDataAPI.ts +129 -5
  8. package/Server/API/AIInvestigationAPI.ts +87 -0
  9. package/Server/API/EnterpriseLicenseAPI.ts +57 -0
  10. package/Server/API/GlobalConfigAPI.ts +62 -1
  11. package/Server/EnvironmentConfig.ts +22 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1784640000000-AddExceptionTriageAndPrivacyColumns.ts +56 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.ts +38 -0
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.ts +25 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  16. package/Server/Services/AIRunService.ts +8 -0
  17. package/Server/Services/AnalyticsDatabaseService.ts +48 -45
  18. package/Server/Services/DatabaseService.ts +66 -1
  19. package/Server/Services/TelemetryExceptionService.ts +320 -24
  20. package/Server/Services/WorkflowService.ts +100 -39
  21. package/Server/Types/Workflow/Components/Schedule.ts +57 -32
  22. package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +15 -4
  23. package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +15 -4
  24. package/Server/Utils/AI/SRE/Insights/InsightScanner.ts +222 -24
  25. package/Server/Utils/AI/SRE/Insights/InsightTriageRunner.ts +225 -9
  26. package/Server/Utils/AI/SRE/SubjectCodeFixRun.ts +39 -0
  27. package/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.ts +114 -0
  28. package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +59 -0
  29. package/Server/Utils/Express.ts +13 -0
  30. package/Server/Utils/Telemetry/ExceptionSanitizer.ts +222 -0
  31. package/Server/Utils/Telemetry/TelemetryFanInWriter.ts +799 -0
  32. package/Server/Utils/Telemetry/TelemetryWriterClient.ts +263 -0
  33. package/Server/Utils/Telemetry/TelemetryWriterServer.ts +240 -0
  34. package/Server/Utils/Telemetry/TelemetryWriterShedMetrics.ts +93 -0
  35. package/Tests/Server/Services/DatabaseServiceSanitizeUpdateData.test.ts +232 -0
  36. package/Tests/Server/Services/TelemetryExceptionCodeFixRun.test.ts +205 -1
  37. package/Tests/Server/Services/UpdateOneByIdKeepsRowId.test.ts +107 -0
  38. package/Tests/Server/Services/WorkflowService.test.ts +308 -0
  39. package/Tests/Server/Utils/AI/Insights/InsightScanner.test.ts +150 -141
  40. package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +277 -1
  41. package/Tests/Server/Utils/Attribution.test.ts +203 -0
  42. package/Tests/Server/Utils/ExceptionSanitizer.test.ts +70 -0
  43. package/Tests/Server/Utils/InsightTriageClassification.test.ts +57 -0
  44. package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +183 -0
  45. package/Tests/Server/Utils/Marketing/GoogleAds.test.ts +340 -0
  46. package/Tests/Server/Utils/Monitor/Criteria/ExceptionMonitorCriteria.test.ts +87 -0
  47. package/Tests/Server/Utils/Monitor/Criteria/LogMonitorCriteria.test.ts +165 -0
  48. package/Tests/Server/Utils/Monitor/Criteria/TraceMonitorCriteria.test.ts +86 -0
  49. package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +1565 -0
  50. package/Tests/Server/Utils/Telemetry/TelemetryWriterClient.test.ts +313 -0
  51. package/Tests/Server/Utils/Telemetry/TelemetryWriterServer.test.ts +270 -0
  52. package/Tests/Server/Utils/Telemetry/TelemetryWriterShedMetrics.test.ts +127 -0
  53. package/Tests/Server/Utils/TelemetryImprovementTaskTrigger.test.ts +141 -0
  54. package/Tests/Types/AI/CodeFixTaskType.test.ts +3 -0
  55. package/Tests/Types/Monitor/MonitorCriteriaMetricVariables.test.ts +228 -0
  56. package/Tests/Types/Monitor/MonitorStepDefaultTelemetryConfig.test.ts +169 -0
  57. package/Tests/Types/Monitor/MonitorStepExceptionMonitor.test.ts +289 -0
  58. package/Tests/Types/Monitor/MonitorStepLogMonitor.test.ts +231 -0
  59. package/Tests/Types/Monitor/MonitorStepMetricViewConfigUtil.test.ts +245 -0
  60. package/Tests/Types/Monitor/MonitorStepNetworkDeviceMonitor.test.ts +155 -0
  61. package/Tests/Types/Monitor/MonitorStepTelemetrySerialization.test.ts +141 -0
  62. package/Tests/Types/Monitor/MonitorStepTraceMonitor.test.ts +137 -0
  63. package/Tests/Types/Workspace/NotificationRules/NotificationRuleCondition.test.ts +311 -0
  64. package/Tests/UI/Components/ModelTableSelectFromColumns.test.ts +259 -0
  65. package/Tests/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.test.ts +461 -0
  66. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +2852 -0
  67. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +758 -0
  68. package/Tests/UI/Utils/NotificationMethodUtil.test.ts +564 -0
  69. package/Tests/Utils/Array.test.ts +156 -0
  70. package/Tests/Utils/CronTab.test.ts +199 -0
  71. package/Tests/Utils/ModelImportExport.test.ts +101 -0
  72. package/Tests/Utils/Number.test.ts +179 -0
  73. package/Tests/Utils/VersionUtil.test.ts +348 -0
  74. package/Tests/jest.setup.ts +1 -0
  75. package/Types/AI/CodeFixTaskContext.ts +6 -0
  76. package/Types/AI/CodeFixTaskType.ts +38 -1
  77. package/Types/AI/ExceptionAIClassification.ts +27 -0
  78. package/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.ts +6 -0
  79. package/Types/Log/LogScrubPatternType.ts +7 -0
  80. package/Types/Monitor/MonitorStep.ts +24 -4
  81. package/Types/Monitor/MonitorStepMetricViewConfigUtil.ts +105 -0
  82. package/Types/Trace/TraceScrubPatternType.ts +7 -0
  83. package/UI/Components/EditionLabel/EditionLabel.tsx +445 -12
  84. package/UI/Components/ModelTable/BaseModelTable.tsx +15 -46
  85. package/UI/Components/ModelTable/SelectFromColumns.ts +129 -0
  86. package/UI/Components/Workflow/ArgumentsForm.tsx +68 -30
  87. package/UI/Components/Workflow/CronScheduleField.tsx +411 -0
  88. package/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.ts +176 -0
  89. package/UI/Utils/NotificationMethodUtil.ts +310 -0
  90. package/Utils/CronTab.ts +823 -0
  91. package/Utils/ModelImportExport.ts +21 -6
  92. package/Utils/VersionUtil.ts +260 -0
  93. package/build/dist/Models/DatabaseModels/AIInsight.js +31 -0
  94. package/build/dist/Models/DatabaseModels/AIInsight.js.map +1 -1
  95. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +22 -0
  96. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
  97. package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js +26 -0
  98. package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js.map +1 -1
  99. package/build/dist/Models/DatabaseModels/GlobalConfig.js +81 -0
  100. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  101. package/build/dist/Models/DatabaseModels/Project.js +31 -0
  102. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  103. package/build/dist/Models/DatabaseModels/TelemetryException.js +134 -0
  104. package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
  105. package/build/dist/Server/API/AIAgentDataAPI.js +97 -8
  106. package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
  107. package/build/dist/Server/API/AIInvestigationAPI.js +55 -0
  108. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  109. package/build/dist/Server/API/EnterpriseLicenseAPI.js +47 -0
  110. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
  111. package/build/dist/Server/API/GlobalConfigAPI.js +52 -1
  112. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  113. package/build/dist/Server/EnvironmentConfig.js +17 -0
  114. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  115. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784640000000-AddExceptionTriageAndPrivacyColumns.js +33 -0
  116. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784640000000-AddExceptionTriageAndPrivacyColumns.js.map +1 -0
  117. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.js +18 -0
  118. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784659816363-AddInstanceVersionAndLatestReleaseColumns.js.map +1 -0
  119. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.js +14 -0
  120. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784705487674-AddEnterpriseLicenseEvaluationColumns.js.map +1 -0
  121. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  122. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  123. package/build/dist/Server/Services/AIRunService.js +9 -2
  124. package/build/dist/Server/Services/AIRunService.js.map +1 -1
  125. package/build/dist/Server/Services/AnalyticsDatabaseService.js +44 -16
  126. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  127. package/build/dist/Server/Services/DatabaseService.js +53 -1
  128. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  129. package/build/dist/Server/Services/TelemetryExceptionService.js +263 -22
  130. package/build/dist/Server/Services/TelemetryExceptionService.js.map +1 -1
  131. package/build/dist/Server/Services/WorkflowService.js +80 -30
  132. package/build/dist/Server/Services/WorkflowService.js.map +1 -1
  133. package/build/dist/Server/Types/Workflow/Components/Schedule.js +44 -19
  134. package/build/dist/Server/Types/Workflow/Components/Schedule.js.map +1 -1
  135. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +12 -4
  136. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
  137. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +12 -4
  138. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
  139. package/build/dist/Server/Utils/AI/SRE/Insights/InsightScanner.js +193 -22
  140. package/build/dist/Server/Utils/AI/SRE/Insights/InsightScanner.js.map +1 -1
  141. package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js +178 -9
  142. package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js.map +1 -1
  143. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js +37 -0
  144. package/build/dist/Server/Utils/AI/SRE/SubjectCodeFixRun.js.map +1 -1
  145. package/build/dist/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.js +98 -0
  146. package/build/dist/Server/Utils/AI/SRE/TelemetryImprovementTaskTrigger.js.map +1 -0
  147. package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js +24 -0
  148. package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -0
  149. package/build/dist/Server/Utils/Express.js +12 -0
  150. package/build/dist/Server/Utils/Express.js.map +1 -1
  151. package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js +149 -0
  152. package/build/dist/Server/Utils/Telemetry/ExceptionSanitizer.js.map +1 -0
  153. package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js +496 -0
  154. package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js.map +1 -0
  155. package/build/dist/Server/Utils/Telemetry/TelemetryWriterClient.js +165 -0
  156. package/build/dist/Server/Utils/Telemetry/TelemetryWriterClient.js.map +1 -0
  157. package/build/dist/Server/Utils/Telemetry/TelemetryWriterServer.js +136 -0
  158. package/build/dist/Server/Utils/Telemetry/TelemetryWriterServer.js.map +1 -0
  159. package/build/dist/Server/Utils/Telemetry/TelemetryWriterShedMetrics.js +76 -0
  160. package/build/dist/Server/Utils/Telemetry/TelemetryWriterShedMetrics.js.map +1 -0
  161. package/build/dist/Types/AI/CodeFixTaskType.js +38 -1
  162. package/build/dist/Types/AI/CodeFixTaskType.js.map +1 -1
  163. package/build/dist/Types/AI/ExceptionAIClassification.js +28 -0
  164. package/build/dist/Types/AI/ExceptionAIClassification.js.map +1 -0
  165. package/build/dist/Types/Log/LogScrubPatternType.js +7 -0
  166. package/build/dist/Types/Log/LogScrubPatternType.js.map +1 -1
  167. package/build/dist/Types/Monitor/MonitorStep.js +20 -4
  168. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  169. package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js +73 -0
  170. package/build/dist/Types/Monitor/MonitorStepMetricViewConfigUtil.js.map +1 -0
  171. package/build/dist/Types/Trace/TraceScrubPatternType.js +7 -0
  172. package/build/dist/Types/Trace/TraceScrubPatternType.js.map +1 -1
  173. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +261 -13
  174. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  175. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +12 -37
  176. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  177. package/build/dist/UI/Components/ModelTable/SelectFromColumns.js +82 -0
  178. package/build/dist/UI/Components/ModelTable/SelectFromColumns.js.map +1 -0
  179. package/build/dist/UI/Components/Workflow/ArgumentsForm.js +30 -6
  180. package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
  181. package/build/dist/UI/Components/Workflow/CronScheduleField.js +209 -0
  182. package/build/dist/UI/Components/Workflow/CronScheduleField.js.map +1 -0
  183. package/build/dist/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.js +129 -0
  184. package/build/dist/UI/Utils/Breadcrumb/BreadcrumbTrailResolver.js.map +1 -0
  185. package/build/dist/UI/Utils/NotificationMethodUtil.js +189 -0
  186. package/build/dist/UI/Utils/NotificationMethodUtil.js.map +1 -0
  187. package/build/dist/Utils/CronTab.js +609 -0
  188. package/build/dist/Utils/CronTab.js.map +1 -0
  189. package/build/dist/Utils/ModelImportExport.js +20 -6
  190. package/build/dist/Utils/ModelImportExport.js.map +1 -1
  191. package/build/dist/Utils/VersionUtil.js +193 -0
  192. package/build/dist/Utils/VersionUtil.js.map +1 -0
  193. package/package.json +1 -1
@@ -205,6 +205,36 @@ export default class AIInsight extends BaseModel {
205
205
  })
206
206
  public severity?: AIInsightSeverity = undefined;
207
207
 
208
+ /*
209
+ * Triage verdict (ExceptionAIClassification values) written by the
210
+ * insight triage runner. Null until triage completes. The automatic
211
+ * fix lane only opens pull requests for insights triaged as
212
+ * code-fault — see InsightScanner / InsightFixRouting.
213
+ */
214
+ @ColumnAccessControl({
215
+ create: [],
216
+ read: [
217
+ Permission.ProjectOwner,
218
+ Permission.ProjectAdmin,
219
+ Permission.ProjectMember,
220
+ ],
221
+ update: [],
222
+ })
223
+ @TableColumn({
224
+ required: false,
225
+ type: TableColumnType.ShortText,
226
+ title: "Classification",
227
+ description:
228
+ "AI triage verdict: code-fault, user-error, expected-denial, infrastructure or unknown. Automatic fix pull requests are only opened for code-fault.",
229
+ canReadOnRelationQuery: true,
230
+ })
231
+ @Column({
232
+ nullable: true,
233
+ type: ColumnType.ShortText,
234
+ length: ColumnLength.ShortText,
235
+ })
236
+ public classification?: string = undefined;
237
+
208
238
  /*
209
239
  * The detector's stable dedupe key for this finding (e.g.
210
240
  * "new-exception:<telemetryExceptionId>"). The scanner refreshes the
@@ -102,6 +102,27 @@ export default class EnterpriseLicense extends BaseModel {
102
102
  })
103
103
  public annualContractValue?: number = undefined;
104
104
 
105
+ @ColumnAccessControl({
106
+ create: [],
107
+ read: [],
108
+ update: [],
109
+ })
110
+ @TableColumn({
111
+ required: true,
112
+ isDefaultValueColumn: true,
113
+ type: TableColumnType.Boolean,
114
+ title: "Evaluation License",
115
+ description:
116
+ "When enabled, this key is for evaluation and testing only — the customer's installation shows an evaluation notice and it is not meant for production use.",
117
+ defaultValue: false,
118
+ })
119
+ @Column({
120
+ nullable: false,
121
+ type: ColumnType.Boolean,
122
+ default: false,
123
+ })
124
+ public isEvaluationLicense?: boolean = undefined;
125
+
105
126
  @ColumnAccessControl({
106
127
  create: [],
107
128
  read: [],
@@ -188,4 +188,29 @@ export default class EnterpriseLicenseInstance extends BaseModel {
188
188
  nullable: true,
189
189
  })
190
190
  public masterAdminEmails?: Array<string> = undefined;
191
+
192
+ /*
193
+ * Named oneuptimeVersion, not version: DatabaseBaseModel already declares a
194
+ * `version` @VersionColumn() (the optimistic-locking row revision) on every
195
+ * model, so a second `version` property would collide in the TypeORM entity
196
+ * metadata.
197
+ */
198
+ @ColumnAccessControl({
199
+ create: [],
200
+ read: [],
201
+ update: [],
202
+ })
203
+ @TableColumn({
204
+ required: false,
205
+ type: TableColumnType.ShortText,
206
+ title: "OneUptime Version",
207
+ description:
208
+ "OneUptime version this instance last reported, for example 11.5.13. Lets support see which instances are running an outdated build.",
209
+ })
210
+ @Column({
211
+ nullable: true,
212
+ type: ColumnType.ShortText,
213
+ length: ColumnLength.ShortText,
214
+ })
215
+ public oneuptimeVersion?: string = undefined;
191
216
  }
@@ -824,6 +824,25 @@ export default class GlobalConfig extends GlobalConfigModel {
824
824
  })
825
825
  public enterpriseLicenseToken?: string = undefined;
826
826
 
827
+ @ColumnAccessControl({
828
+ create: [],
829
+ read: [],
830
+ update: [],
831
+ })
832
+ @TableColumn({
833
+ type: TableColumnType.Boolean,
834
+ title: "Enterprise License Is Evaluation",
835
+ description:
836
+ "True when the validated enterprise license is an evaluation/testing license. Drives the evaluation notice shown in the edition modal.",
837
+ defaultValue: false,
838
+ })
839
+ @Column({
840
+ type: ColumnType.Boolean,
841
+ nullable: true,
842
+ default: false,
843
+ })
844
+ public enterpriseLicenseIsEvaluation?: boolean = undefined;
845
+
827
846
  @ColumnAccessControl({
828
847
  create: [],
829
848
  read: [],
@@ -929,6 +948,63 @@ export default class GlobalConfig extends GlobalConfigModel {
929
948
  })
930
949
  public enterpriseLicenseExpiryReminderDays?: number = undefined;
931
950
 
951
+ /*
952
+ * The four latestRelease* columns cache the daily GitHub release check
953
+ * (Workers/Jobs/InstanceUpdate/CheckForNewVersion). They are a cache, not
954
+ * configuration: an air-gapped installation simply leaves them null and no
955
+ * update prompt is ever shown.
956
+ */
957
+ @ColumnAccessControl({
958
+ create: [],
959
+ read: [],
960
+ update: [],
961
+ })
962
+ @TableColumn({
963
+ type: TableColumnType.ShortText,
964
+ title: "Latest Release Version",
965
+ description:
966
+ "Latest OneUptime version published on GitHub, for example 11.6.0, as of the last update check. Null until the first successful check.",
967
+ })
968
+ @Column({
969
+ type: ColumnType.ShortText,
970
+ length: ColumnLength.ShortText,
971
+ nullable: true,
972
+ })
973
+ public latestReleaseVersion?: string = undefined;
974
+
975
+ @ColumnAccessControl({
976
+ create: [],
977
+ read: [],
978
+ update: [],
979
+ })
980
+ @TableColumn({
981
+ type: TableColumnType.Date,
982
+ title: "Latest Release Published At",
983
+ description: "When the latest OneUptime version was published on GitHub.",
984
+ })
985
+ @Column({
986
+ type: ColumnType.Date,
987
+ nullable: true,
988
+ })
989
+ public latestReleasePublishedAt?: Date = undefined;
990
+
991
+ @ColumnAccessControl({
992
+ create: [],
993
+ read: [],
994
+ update: [],
995
+ })
996
+ @TableColumn({
997
+ type: TableColumnType.Date,
998
+ title: "Latest Release Checked At",
999
+ description:
1000
+ "When this installation last successfully checked GitHub for a newer OneUptime release.",
1001
+ })
1002
+ @Column({
1003
+ type: ColumnType.Date,
1004
+ nullable: true,
1005
+ })
1006
+ public latestReleaseCheckedAt?: Date = undefined;
1007
+
932
1008
  @ColumnAccessControl({
933
1009
  create: [],
934
1010
  read: [],
@@ -1599,6 +1599,36 @@ export default class Project extends TenantModel {
1599
1599
  })
1600
1600
  public enableInsightFixTasks?: boolean = undefined;
1601
1601
 
1602
+ @ColumnAccessControl({
1603
+ create: [],
1604
+ read: [
1605
+ Permission.ProjectOwner,
1606
+ Permission.ProjectAdmin,
1607
+ Permission.ProjectMember,
1608
+ Permission.Viewer,
1609
+ Permission.ReadProject,
1610
+ Permission.UnAuthorizedSsoUser,
1611
+ Permission.ProjectUser,
1612
+ ],
1613
+ update: [Permission.ProjectOwner, Permission.ProjectAdmin],
1614
+ })
1615
+ @TableColumn({
1616
+ required: true,
1617
+ isDefaultValueColumn: true,
1618
+ type: TableColumnType.Boolean,
1619
+ title: "Auto Archive Non-Actionable Exceptions",
1620
+ description:
1621
+ "When enabled, exception groups the AI triage classifies as expected denials (auth failures, plan/paywall rejections, scanner probes tripping intentional validation) are automatically archived so they stop surfacing in the unresolved list and never queue AI fix tasks. Groups classified as user errors or infrastructure conditions are NOT auto-archived — only clear expected denials are. Archiving is reversible from the Archived tab.",
1622
+ defaultValue: false,
1623
+ example: true,
1624
+ })
1625
+ @Column({
1626
+ nullable: false,
1627
+ default: false,
1628
+ type: ColumnType.Boolean,
1629
+ })
1630
+ public autoArchiveNonActionableExceptions?: boolean = undefined;
1631
+
1602
1632
  @ColumnAccessControl({
1603
1633
  create: [],
1604
1634
  read: [
@@ -1281,4 +1281,132 @@ export default class TelemetryException extends DatabaseBaseModel {
1281
1281
  length: ColumnLength.LongText,
1282
1282
  })
1283
1283
  public environment?: string = undefined;
1284
+
1285
+ @ColumnAccessControl({
1286
+ /*
1287
+ * Server-written only (isRoot bypasses column ACL): this is derived
1288
+ * state maintained by ingest / triage / the PR-state sync — a client
1289
+ * PATCH must not be able to set it (the value flows into LLM prompts
1290
+ * and public PR text, and it gates the automatic fix lane).
1291
+ */
1292
+ create: [],
1293
+ read: [
1294
+ Permission.ProjectOwner,
1295
+ Permission.ProjectAdmin,
1296
+ Permission.ProjectMember,
1297
+ Permission.Viewer,
1298
+ Permission.TelemetryAdmin,
1299
+ Permission.TelemetryMember,
1300
+ Permission.TelemetryViewer,
1301
+ Permission.ReadTelemetryException,
1302
+ ],
1303
+ update: [],
1304
+ })
1305
+ /*
1306
+ * Sticky rollup of the OTel exception.escaped flag: true once ANY
1307
+ * occurrence of this group escaped its span scope (was unhandled).
1308
+ * Maintained by the ingest upsert with `unhandled OR EXCLUDED.unhandled`
1309
+ * — a group never flips back to handled. Handled exceptions are usually
1310
+ * expected/operational errors, which is a key signal for deciding
1311
+ * whether an AI fix PR is warranted.
1312
+ */
1313
+ @TableColumn({
1314
+ title: "Unhandled",
1315
+ description:
1316
+ "True when at least one occurrence of this exception escaped its span scope (was unhandled, per OTel exception.escaped)",
1317
+ isDefaultValueColumn: true,
1318
+ required: true,
1319
+ type: TableColumnType.Boolean,
1320
+ defaultValue: false,
1321
+ example: true,
1322
+ })
1323
+ @Column({
1324
+ type: ColumnType.Boolean,
1325
+ nullable: false,
1326
+ unique: false,
1327
+ default: false,
1328
+ })
1329
+ public unhandled?: boolean = undefined;
1330
+
1331
+ @ColumnAccessControl({
1332
+ /*
1333
+ * Server-written only (isRoot bypasses column ACL): this is derived
1334
+ * state maintained by ingest / triage / the PR-state sync — a client
1335
+ * PATCH must not be able to set it (the value flows into LLM prompts
1336
+ * and public PR text, and it gates the automatic fix lane).
1337
+ */
1338
+ create: [],
1339
+ read: [
1340
+ Permission.ProjectOwner,
1341
+ Permission.ProjectAdmin,
1342
+ Permission.ProjectMember,
1343
+ Permission.Viewer,
1344
+ Permission.TelemetryAdmin,
1345
+ Permission.TelemetryMember,
1346
+ Permission.TelemetryViewer,
1347
+ Permission.ReadTelemetryException,
1348
+ ],
1349
+ update: [],
1350
+ })
1351
+ /*
1352
+ * Verdict of the AI insight triage for this exception group: is this a
1353
+ * code defect the AI should try to fix, an expected end-user error, an
1354
+ * expected denial (auth/paywall/probe), or an infrastructure condition?
1355
+ * Values come from ExceptionAIClassification. Written by the triage
1356
+ * runner; the automatic fix lane only opens PRs for CodeFault groups.
1357
+ */
1358
+ @TableColumn({
1359
+ title: "AI Classification",
1360
+ description:
1361
+ "AI triage verdict for this exception group (code-fault, user-error, expected-denial, infrastructure)",
1362
+ required: false,
1363
+ type: TableColumnType.ShortText,
1364
+ example: "code-fault",
1365
+ })
1366
+ @Column({
1367
+ nullable: true,
1368
+ type: ColumnType.ShortText,
1369
+ length: ColumnLength.ShortText,
1370
+ })
1371
+ public aiClassification?: string = undefined;
1372
+
1373
+ @ColumnAccessControl({
1374
+ /*
1375
+ * Server-written only (isRoot bypasses column ACL): this is derived
1376
+ * state maintained by ingest / triage / the PR-state sync — a client
1377
+ * PATCH must not be able to set it (the value flows into LLM prompts
1378
+ * and public PR text, and it gates the automatic fix lane).
1379
+ */
1380
+ create: [],
1381
+ read: [
1382
+ Permission.ProjectOwner,
1383
+ Permission.ProjectAdmin,
1384
+ Permission.ProjectMember,
1385
+ Permission.Viewer,
1386
+ Permission.TelemetryAdmin,
1387
+ Permission.TelemetryMember,
1388
+ Permission.TelemetryViewer,
1389
+ Permission.ReadTelemetryException,
1390
+ ],
1391
+ update: [],
1392
+ })
1393
+ /*
1394
+ * Stamped when a human closes an AI-authored fix PR for this exception
1395
+ * without merging it — the strongest "the AI fix was not wanted" signal.
1396
+ * The automatic fix lane skips groups carrying this stamp; a human
1397
+ * clicking "Fix with AI" on the exception page clears it and retries.
1398
+ */
1399
+ @TableColumn({
1400
+ title: "AI Fix Declined At",
1401
+ description:
1402
+ "Set when an AI-authored fix pull request for this exception was closed without merging; suppresses further automatic fix attempts",
1403
+ required: false,
1404
+ type: TableColumnType.Date,
1405
+ example: "2026-01-01T00:00:00.000Z",
1406
+ })
1407
+ @Column({
1408
+ nullable: true,
1409
+ type: ColumnType.Date,
1410
+ })
1411
+ public aiFixDeclinedAt?: Date = undefined;
1284
1412
  }
@@ -43,6 +43,11 @@ import CodeFixTaskContext, {
43
43
  PerformanceFinding,
44
44
  } from "../../Types/AI/CodeFixTaskContext";
45
45
  import SpanTreeAnalyzer from "../Utils/AI/PerfEvidence/SpanTreeAnalyzer";
46
+ import {
47
+ sanitizeExceptionMessage,
48
+ sanitizeStackTrace,
49
+ } from "../Utils/Telemetry/ExceptionSanitizer";
50
+ import ToolResultSerializer from "../Utils/AI/Toolbox/Serializer";
46
51
  import OpenPullRequestCap, {
47
52
  OpenPullRequestCapDecision,
48
53
  } from "../Utils/AI/CodeFix/OpenPullRequestCap";
@@ -204,6 +209,7 @@ export default class AIAgentDataAPI {
204
209
  stackTrace: true,
205
210
  exceptionType: true,
206
211
  fingerprint: true,
212
+ aiClassification: true,
207
213
  primaryEntityId: true,
208
214
  primaryEntityType: true,
209
215
  },
@@ -221,7 +227,7 @@ export default class AIAgentDataAPI {
221
227
  }
222
228
 
223
229
  logger.debug(
224
- `Exception details fetched: ${exception._id} - ${exception.message?.substring(0, 100)}`,
230
+ `Exception details fetched: ${exception._id}`,
225
231
  getLogAttributesFromRequest(req as any),
226
232
  );
227
233
 
@@ -245,13 +251,24 @@ export default class AIAgentDataAPI {
245
251
  })
246
252
  : null;
247
253
 
254
+ /*
255
+ * Sanitize at the choke point: everything the AI agent worker
256
+ * sees — and therefore everything that can end up in an LLM
257
+ * prompt, a public pull-request title/body, or a commit
258
+ * message — flows through this response. The message gets
259
+ * dynamic-token normalization + secret redaction; the stack
260
+ * trace gets redaction only, so file:line frames stay intact
261
+ * for the code agent.
262
+ */
248
263
  return Response.sendJsonObjectResponse(req, res, {
249
264
  exception: {
250
265
  id: exception._id?.toString(),
251
- message: exception.message,
252
- stackTrace: exception.stackTrace,
253
- exceptionType: exception.exceptionType,
266
+ message: sanitizeExceptionMessage(exception.message || ""),
267
+ stackTrace: sanitizeStackTrace(exception.stackTrace || ""),
268
+ // Legacy pre-batch-upsert rows can carry NULL here.
269
+ exceptionType: exception.exceptionType || "",
254
270
  fingerprint: exception.fingerprint,
271
+ aiClassification: exception.aiClassification || null,
255
272
  },
256
273
  service: exception.primaryEntityId
257
274
  ? {
@@ -518,6 +535,103 @@ export default class AIAgentDataAPI {
518
535
  );
519
536
  }
520
537
 
538
+ /*
539
+ * Service-scoped instrumentation recipes (ImproveLogging /
540
+ * ImproveTracing): the context is just the service — resolve the
541
+ * repository by service name (no stack trace) and hand the
542
+ * worker a short brief; the recipe's checklist lives in the
543
+ * worker's prompt.
544
+ */
545
+ if (
546
+ taskType === CodeFixTaskType.ImproveLogging ||
547
+ taskType === CodeFixTaskType.ImproveTracing
548
+ ) {
549
+ const taskContext: CodeFixTaskContext | undefined = run.taskContext;
550
+ const improvementServiceName: string =
551
+ taskContext?.serviceName || "";
552
+
553
+ if (!taskContext?.telemetryServiceId) {
554
+ return Response.sendErrorResponse(
555
+ req,
556
+ res,
557
+ new BadDataException(
558
+ "This telemetry-improvement task has no stored service context — the task has nothing to work from.",
559
+ ),
560
+ );
561
+ }
562
+
563
+ const pillar: string =
564
+ taskType === CodeFixTaskType.ImproveLogging
565
+ ? "logging"
566
+ : "tracing";
567
+ const subjectTitle: string = improvementServiceName
568
+ ? `Improve ${pillar} for ${improvementServiceName}`
569
+ : `Improve ${pillar}`;
570
+
571
+ const resolution: RepoResolution | null =
572
+ await CodeRepositoryService.resolveRepositoryForException({
573
+ projectId: run.projectId,
574
+ stackTrace: null,
575
+ serviceName: improvementServiceName || null,
576
+ });
577
+
578
+ const repository: CodeRepository | null = resolution
579
+ ? await CodeRepositoryService.findOneById({
580
+ id: new ObjectID(resolution.codeRepositoryId),
581
+ select: {
582
+ _id: true,
583
+ name: true,
584
+ repositoryHostedAt: true,
585
+ organizationName: true,
586
+ repositoryName: true,
587
+ mainBranchName: true,
588
+ gitHubAppInstallationId: true,
589
+ },
590
+ props: { isRoot: true },
591
+ })
592
+ : null;
593
+
594
+ const basePayload: JSONObject = {
595
+ subjectType: "service",
596
+ subjectTitle,
597
+ analysisMarkdown: `OneUptime observes the telemetry of the service ${
598
+ improvementServiceName
599
+ ? `"${improvementServiceName}"`
600
+ : "in this repository"
601
+ }. A user asked for its ${pillar} instrumentation to be improved — follow the task checklist in your instructions.`,
602
+ serviceName: improvementServiceName,
603
+ projectId: run.projectId.toString(),
604
+ };
605
+
606
+ if (!resolution || !repository) {
607
+ return Response.sendJsonObjectResponse(req, res, {
608
+ ...basePayload,
609
+ repositories: [],
610
+ resolutionError:
611
+ "Could not resolve a repository for this task: no connected repository name matches the service and the project has more than one repository. Connect the right repository via the GitHub App, or rename one to match the service.",
612
+ });
613
+ }
614
+
615
+ return Response.sendJsonObjectResponse(req, res, {
616
+ ...basePayload,
617
+ repositories: [
618
+ {
619
+ id: repository.id!.toString(),
620
+ name: repository.name || "",
621
+ repositoryHostedAt: repository.repositoryHostedAt || "",
622
+ organizationName: repository.organizationName || "",
623
+ repositoryName: repository.repositoryName || "",
624
+ mainBranchName: repository.mainBranchName || "main",
625
+ servicePathInRepository: resolution.servicePathInRepository,
626
+ gitHubAppInstallationId:
627
+ repository.gitHubAppInstallationId || null,
628
+ resolutionMethod: resolution.method,
629
+ resolutionEvidence: resolution.evidence,
630
+ },
631
+ ],
632
+ });
633
+ }
634
+
521
635
  /*
522
636
  * Trace-evidence recipes (FixPerformance): everything the worker
523
637
  * needs was captured into taskContext at trigger time — the spans
@@ -607,8 +721,10 @@ export default class AIAgentDataAPI {
607
721
  const basePayload: JSONObject = {
608
722
  subjectType: "trace",
609
723
  subjectTitle: findings[0]!.headline,
610
- analysisMarkdown:
724
+ // Redact before it can reach a prompt or PR body.
725
+ analysisMarkdown: ToolResultSerializer.redact(
611
726
  SpanTreeAnalyzer.renderFindingsMarkdown(findings),
727
+ ).text,
612
728
  serviceName,
613
729
  projectId: run.projectId.toString(),
614
730
  traceId: taskContext.traceId,
@@ -788,6 +904,14 @@ export default class AIAgentDataAPI {
788
904
  );
789
905
  }
790
906
 
907
+ /*
908
+ * The analysis was written by the investigation LLM over
909
+ * redacted tool results, but quoted exception text can still
910
+ * carry secrets — sweep it again before it reaches the worker's
911
+ * prompt and the pull-request body.
912
+ */
913
+ analysisMarkdown = ToolResultSerializer.redact(analysisMarkdown).text;
914
+
791
915
  /*
792
916
  * Resolve the repository WITHOUT a stack trace — these tasks have
793
917
  * no exception, so only the name-match (against the subject's
@@ -31,6 +31,8 @@ import ServiceService from "../Services/ServiceService";
31
31
  import SpanService from "../Services/SpanService";
32
32
  import FixFromIncidentTaskTrigger from "../Utils/AI/SRE/FixFromIncidentTaskTrigger";
33
33
  import FixPerformanceTaskTrigger from "../Utils/AI/SRE/FixPerformanceTaskTrigger";
34
+ import TelemetryImprovementTaskTrigger from "../Utils/AI/SRE/TelemetryImprovementTaskTrigger";
35
+ import CodeFixTaskType from "../../Types/AI/CodeFixTaskType";
34
36
  import { AnalyzableSpan } from "../Utils/AI/PerfEvidence/SpanTreeAnalyzer";
35
37
 
36
38
  const router: ExpressRouter = Express.getRouter();
@@ -575,4 +577,89 @@ router.post(
575
577
  },
576
578
  );
577
579
 
580
+ /*
581
+ * "Improve logging / tracing with AI" from a telemetry service's Logs or
582
+ * Traces page: gate and enqueue a service-scoped instrumentation-
583
+ * improvement CodeFix run (ImproveLogging / ImproveTracing). Human-
584
+ * triggered — the click is the gate; budget, repository and per-service
585
+ * dedupe are enforced in the trigger. Returns { aiRunId }.
586
+ */
587
+ router.post(
588
+ "/ai-investigation/create-telemetry-improvement-task",
589
+ UserMiddleware.getUserMiddleware,
590
+ async (
591
+ req: ExpressRequest,
592
+ res: ExpressResponse,
593
+ next: NextFunction,
594
+ ): Promise<void> => {
595
+ try {
596
+ const props: DatabaseCommonInteractionProps = await getLoggedInProps(req);
597
+
598
+ const projectId: ObjectID | undefined = props.tenantId;
599
+
600
+ if (!projectId) {
601
+ throw new BadDataException("A project scope is required.");
602
+ }
603
+
604
+ const telemetryServiceIdParam: string | undefined = req.body[
605
+ "telemetryServiceId"
606
+ ] as string | undefined;
607
+
608
+ if (!telemetryServiceIdParam) {
609
+ throw new BadDataException("telemetryServiceId is required.");
610
+ }
611
+
612
+ const rawTaskType: string | undefined = req.body["taskType"] as
613
+ | string
614
+ | undefined;
615
+
616
+ if (
617
+ rawTaskType !== CodeFixTaskType.ImproveLogging &&
618
+ rawTaskType !== CodeFixTaskType.ImproveTracing
619
+ ) {
620
+ throw new BadDataException(
621
+ `taskType must be ${CodeFixTaskType.ImproveLogging} or ${CodeFixTaskType.ImproveTracing}.`,
622
+ );
623
+ }
624
+
625
+ const telemetryServiceId: ObjectID = new ObjectID(
626
+ telemetryServiceIdParam,
627
+ );
628
+
629
+ /*
630
+ * Access check under the USER's permissions: the caller must be able
631
+ * to read the service they are asking the agent to instrument. The
632
+ * trigger re-reads as root afterwards.
633
+ */
634
+ const service: Service | null = await ServiceService.findOneById({
635
+ id: telemetryServiceId,
636
+ select: { _id: true },
637
+ props: props,
638
+ });
639
+
640
+ if (!service) {
641
+ throw new BadDataException(
642
+ "Telemetry service not found (or you do not have access to it).",
643
+ );
644
+ }
645
+
646
+ const run: AIRun =
647
+ await TelemetryImprovementTaskTrigger.createTelemetryImprovementTask({
648
+ projectId,
649
+ telemetryServiceId,
650
+ taskType: rawTaskType,
651
+ userId: props.userId!,
652
+ });
653
+
654
+ Response.sendJsonObjectResponse(req, res, {
655
+ aiRunId: run.id!.toString(),
656
+ });
657
+ return;
658
+ } catch (err) {
659
+ next(err);
660
+ return;
661
+ }
662
+ },
663
+ );
664
+
578
665
  export default router;