@oneuptime/common 11.5.7 → 11.5.9
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/AIAgentTaskPullRequest.ts +15 -6
- package/Models/DatabaseModels/AIRun.ts +68 -0
- package/Models/DatabaseModels/AIRunEvent.ts +34 -1
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +215 -0
- package/Models/DatabaseModels/Project.ts +25 -0
- package/Models/DatabaseModels/StatusPage.ts +42 -0
- package/Server/API/AIAgentTaskAPI.ts +38 -12
- package/Server/API/AIAgentTaskLogAPI.ts +49 -0
- package/Server/API/AIChatAPI.ts +16 -0
- package/Server/API/AIReadinessAPI.ts +84 -0
- package/Server/API/CodeFixRunAPI.ts +190 -57
- package/Server/API/TelemetryExceptionAPI.ts +6 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784105912819-BackfillCodeFixTaskType.ts +41 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784124919694-AddAITaskNumberAndRunTranscript.ts +95 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784135099754-AllowNullAiAgentOnPullRequest.ts +52 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784137457184-AddEnableMcpServerToStatusPage.ts +30 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1784200000000-AddSnmpV3ColumnsToNetworkDeviceDiscoveryScan.ts +49 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
- package/Server/Middleware/MasterAdminAuthorization.ts +1 -1
- package/Server/Middleware/ProjectAuthorization.ts +1 -1
- package/Server/Services/AIRunEventService.ts +15 -1
- package/Server/Services/AIRunService.ts +48 -12
- package/Server/Services/LlmProviderService.ts +2 -0
- package/Server/Services/ProjectService.ts +42 -0
- package/Server/Services/StatusPageService.ts +87 -0
- package/Server/Services/TelemetryExceptionService.ts +19 -90
- package/Server/Types/Markdown.ts +9 -9
- package/Server/Types/MarkdownSlugify.ts +47 -0
- package/Server/Utils/AI/AIRunPrivacyFilter.ts +106 -0
- package/Server/Utils/AI/AIRunTranscript.ts +240 -0
- package/Server/Utils/AI/Chat/ChatAgentRunner.ts +9 -0
- package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +119 -5
- package/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.ts +75 -0
- package/Server/Utils/AI/CodeFix/CodeFixReadiness.ts +235 -0
- package/Server/Utils/AI/SRE/Insights/FixRouting.ts +2 -3
- package/Server/Utils/AI/Toolbox/CodeTools.ts +883 -0
- package/Server/Utils/AI/Toolbox/CodeWriteTools.ts +519 -0
- package/Server/Utils/AI/Toolbox/Index.ts +27 -0
- package/Server/Utils/AI/Toolbox/ScheduledMaintenanceTools.ts +279 -0
- package/Server/Utils/AnalyticsDatabase/ClickhouseCapacity.ts +19 -1
- package/Server/Utils/CodeRepository/GitHub/GitHub.ts +600 -0
- package/Server/Utils/CodeRepository/StackTraceRepoResolver.ts +7 -2
- package/Server/Utils/LLM/LLMService.ts +397 -18
- package/Server/Utils/Monitor/NetworkDeviceHydrationUtil.ts +4 -11
- package/Tests/Models/StatusPageEnableMcpServer.test.ts +125 -0
- package/Tests/Server/Services/StatusPageServiceMcp.test.ts +223 -0
- package/Tests/Server/Services/TelemetryExceptionAIFixReadiness.test.ts +26 -3
- package/Tests/Server/Services/TelemetryExceptionCodeFixRun.test.ts +12 -0
- package/Tests/Server/Types/Markdown.test.ts +106 -0
- package/Tests/Server/Utils/AI/AIRunPrivacyFilter.test.ts +295 -0
- package/Tests/Server/Utils/AI/AIRunTranscript.test.ts +182 -0
- package/Tests/Server/Utils/AI/CodeFixReadiness.test.ts +411 -0
- package/Tests/Server/Utils/AI/CodeTools.test.ts +596 -0
- package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +475 -0
- package/Tests/Server/Utils/AI/LLMServiceModelCompatibility.test.ts +548 -0
- package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +142 -0
- package/Tests/Server/Utils/AI/ScheduledMaintenanceTools.test.ts +176 -0
- package/Tests/Server/Utils/AnalyticsDatabase/ClickhouseCapacity.test.ts +63 -0
- package/Tests/Server/Utils/GitHubGetFileContent.test.ts +197 -0
- package/Tests/Server/Utils/Monitor/NetworkDeviceHydrationUtil.test.ts +502 -0
- package/Tests/Types/AI/AIAgentTaskStatus.test.ts +7 -6
- package/Tests/Types/AI/AIChatPageContext.test.ts +0 -0
- package/Tests/Types/AI/AIRunStatus.test.ts +42 -0
- package/Tests/Types/Log/LogSeverity.test.ts +78 -0
- package/Tests/UI/Components/BasicFormDropdownNormalization.test.tsx +167 -0
- package/Tests/UI/Components/Charts/AxisTickColor.test.tsx +66 -0
- package/Tests/UI/Utils/AIChatExport/ChatWidgetData.test.ts +319 -0
- package/Tests/UI/Utils/AIChatExport/ChatWidgetMarkdown.test.ts +188 -0
- package/Types/AI/AIAgentTaskStatus.ts +13 -0
- package/Types/AI/AIChatPageContext.ts +137 -0
- package/Types/AI/AIChatTypes.ts +44 -0
- package/Types/AI/AIFixReadiness.ts +46 -0
- package/Types/AI/AIRunStatus.ts +11 -0
- package/Types/Email/EmailTemplateType.ts +2 -0
- package/Types/Log/LogSeverity.ts +61 -0
- package/Types/Monitor/SnmpMonitor/SnmpVersion.ts +40 -0
- package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +8 -2
- package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +3 -1
- package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +8 -2
- package/UI/Components/EditionLabel/EditionLabel.tsx +821 -331
- package/UI/Components/Forms/BasicForm.tsx +26 -8
- package/UI/Styles/Theme.css +1314 -0
- package/UI/Utils/AIChatExport/ChatWidgetData.ts +316 -0
- package/UI/Utils/AIChatExport/ChatWidgetMarkdown.ts +349 -0
- package/UI/Utils/AIChatExport/ConversationMarkdown.ts +177 -0
- package/UI/Utils/AIChatExport/MarkdownBlocks.ts +327 -0
- package/UI/Utils/AIChatExport/MarkdownSafety.ts +215 -0
- package/UI/Utils/DownloadFile.ts +54 -0
- package/build/dist/Models/DatabaseModels/AIAgentTaskPullRequest.js +15 -7
- package/build/dist/Models/DatabaseModels/AIAgentTaskPullRequest.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AIRun.js +69 -0
- package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AIRunEvent.js +31 -0
- package/build/dist/Models/DatabaseModels/AIRunEvent.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +225 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +27 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +43 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Server/API/AIAgentTaskAPI.js +37 -11
- package/build/dist/Server/API/AIAgentTaskAPI.js.map +1 -1
- package/build/dist/Server/API/AIAgentTaskLogAPI.js +32 -7
- package/build/dist/Server/API/AIAgentTaskLogAPI.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +9 -0
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/AIReadinessAPI.js +57 -0
- package/build/dist/Server/API/AIReadinessAPI.js.map +1 -0
- package/build/dist/Server/API/CodeFixRunAPI.js +165 -51
- package/build/dist/Server/API/CodeFixRunAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryExceptionAPI.js +4 -0
- package/build/dist/Server/API/TelemetryExceptionAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784105912819-BackfillCodeFixTaskType.js +36 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784105912819-BackfillCodeFixTaskType.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784124919694-AddAITaskNumberAndRunTranscript.js +80 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784124919694-AddAITaskNumberAndRunTranscript.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784135099754-AllowNullAiAgentOnPullRequest.js +37 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784135099754-AllowNullAiAgentOnPullRequest.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784137457184-AddEnableMcpServerToStatusPage.js +23 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784137457184-AddEnableMcpServerToStatusPage.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784200000000-AddSnmpV3ColumnsToNetworkDeviceDiscoveryScan.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1784200000000-AddSnmpV3ColumnsToNetworkDeviceDiscoveryScan.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js +1 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js +1 -1
- package/build/dist/Server/Services/AIRunEventService.js +8 -0
- package/build/dist/Server/Services/AIRunEventService.js.map +1 -1
- package/build/dist/Server/Services/AIRunService.js +39 -3
- package/build/dist/Server/Services/AIRunService.js.map +1 -1
- package/build/dist/Server/Services/LlmProviderService.js +2 -0
- package/build/dist/Server/Services/LlmProviderService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +39 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +81 -0
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryExceptionService.js +14 -61
- package/build/dist/Server/Services/TelemetryExceptionService.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +8 -8
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Types/MarkdownSlugify.js +47 -0
- package/build/dist/Server/Types/MarkdownSlugify.js.map +1 -0
- package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js +82 -0
- package/build/dist/Server/Utils/AI/AIRunPrivacyFilter.js.map +1 -0
- package/build/dist/Server/Utils/AI/AIRunTranscript.js +180 -0
- package/build/dist/Server/Utils/AI/AIRunTranscript.js.map +1 -0
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js +1 -0
- package/build/dist/Server/Utils/AI/Chat/ChatAgentRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +103 -5
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js +55 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixAgentCompletion.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js +211 -0
- package/build/dist/Server/Utils/AI/CodeFix/CodeFixReadiness.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/FixRouting.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js +654 -0
- package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js +389 -0
- package/build/dist/Server/Utils/AI/Toolbox/CodeWriteTools.js.map +1 -0
- package/build/dist/Server/Utils/AI/Toolbox/Index.js +19 -0
- package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/ScheduledMaintenanceTools.js +239 -0
- package/build/dist/Server/Utils/AI/Toolbox/ScheduledMaintenanceTools.js.map +1 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/ClickhouseCapacity.js +19 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/ClickhouseCapacity.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +439 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/StackTraceRepoResolver.js +1 -1
- package/build/dist/Server/Utils/CodeRepository/StackTraceRepoResolver.js.map +1 -1
- package/build/dist/Server/Utils/LLM/LLMService.js +275 -19
- package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js +2 -11
- package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js.map +1 -1
- package/build/dist/Types/AI/AIAgentTaskStatus.js +12 -0
- package/build/dist/Types/AI/AIAgentTaskStatus.js.map +1 -1
- package/build/dist/Types/AI/AIChatPageContext.js +101 -0
- package/build/dist/Types/AI/AIChatPageContext.js.map +1 -0
- package/build/dist/Types/AI/AIChatTypes.js +2 -0
- package/build/dist/Types/AI/AIChatTypes.js.map +1 -1
- package/build/dist/Types/AI/AIFixReadiness.js +9 -0
- package/build/dist/Types/AI/AIFixReadiness.js.map +1 -0
- package/build/dist/Types/AI/AIRunStatus.js +11 -0
- package/build/dist/Types/AI/AIRunStatus.js.map +1 -1
- package/build/dist/Types/Email/EmailTemplateType.js +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/Log/LogSeverity.js +56 -0
- package/build/dist/Types/Log/LogSeverity.js.map +1 -1
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpVersion.js +38 -0
- package/build/dist/Types/Monitor/SnmpMonitor/SnmpVersion.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +9 -2
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +3 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +9 -2
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +424 -139
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/Forms/BasicForm.js +24 -6
- package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
- package/build/dist/UI/Utils/AIChatExport/ChatWidgetData.js +217 -0
- package/build/dist/UI/Utils/AIChatExport/ChatWidgetData.js.map +1 -0
- package/build/dist/UI/Utils/AIChatExport/ChatWidgetMarkdown.js +234 -0
- package/build/dist/UI/Utils/AIChatExport/ChatWidgetMarkdown.js.map +1 -0
- package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js +111 -0
- package/build/dist/UI/Utils/AIChatExport/ConversationMarkdown.js.map +1 -0
- package/build/dist/UI/Utils/AIChatExport/MarkdownBlocks.js +233 -0
- package/build/dist/UI/Utils/AIChatExport/MarkdownBlocks.js.map +1 -0
- package/build/dist/UI/Utils/AIChatExport/MarkdownSafety.js +134 -0
- package/build/dist/UI/Utils/AIChatExport/MarkdownSafety.js.map +1 -0
- package/build/dist/UI/Utils/DownloadFile.js +41 -0
- package/build/dist/UI/Utils/DownloadFile.js.map +1 -0
- package/package.json +5 -1
|
@@ -196,6 +196,7 @@ export default class AIAgentTaskAPI {
|
|
|
196
196
|
const validStatuses: Array<AIAgentTaskStatus> = [
|
|
197
197
|
AIAgentTaskStatus.InProgress,
|
|
198
198
|
AIAgentTaskStatus.Completed,
|
|
199
|
+
AIAgentTaskStatus.NoFixFound,
|
|
199
200
|
AIAgentTaskStatus.Error,
|
|
200
201
|
];
|
|
201
202
|
|
|
@@ -249,16 +250,35 @@ export default class AIAgentTaskAPI {
|
|
|
249
250
|
},
|
|
250
251
|
});
|
|
251
252
|
} else {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
253
|
+
/*
|
|
254
|
+
* Agents that predate NoFixFound report a fruitless run as Error,
|
|
255
|
+
* so an Error report still maps straight through — those runs keep
|
|
256
|
+
* looking exactly as they do today until the agent is upgraded.
|
|
257
|
+
*/
|
|
258
|
+
let toStatus: AIRunStatus = AIRunStatus.Error;
|
|
259
|
+
|
|
260
|
+
if (status === AIAgentTaskStatus.Completed) {
|
|
261
|
+
toStatus = AIRunStatus.Completed;
|
|
262
|
+
} else if (status === AIAgentTaskStatus.NoFixFound) {
|
|
263
|
+
toStatus = AIRunStatus.NoFixFound;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/*
|
|
267
|
+
* Both terminal outcomes that carry a reason store it in
|
|
268
|
+
* errorMessage — the column is the run's only message field, and
|
|
269
|
+
* every reader (the detail page, the exception page's
|
|
270
|
+
* statusMessage) already renders it.
|
|
271
|
+
*/
|
|
272
|
+
const shouldStoreMessage: boolean =
|
|
273
|
+
Boolean(statusMessage) &&
|
|
274
|
+
(toStatus === AIRunStatus.Error ||
|
|
275
|
+
toStatus === AIRunStatus.NoFixFound);
|
|
256
276
|
|
|
257
277
|
/*
|
|
258
|
-
* CAS Running -> Completed/Error. Losing the race (0
|
|
259
|
-
* another actor already finalized the run — e.g. the
|
|
260
|
-
* marked it Error after a heartbeat gap — and that outcome
|
|
261
|
-
* we do not clobber it or write a duplicate terminal event.
|
|
278
|
+
* CAS Running -> Completed/NoFixFound/Error. Losing the race (0
|
|
279
|
+
* rows) means another actor already finalized the run — e.g. the
|
|
280
|
+
* sweeper marked it Error after a heartbeat gap — and that outcome
|
|
281
|
+
* wins; we do not clobber it or write a duplicate terminal event.
|
|
262
282
|
*/
|
|
263
283
|
const transitionedCount: number =
|
|
264
284
|
await AIRunService.attemptStatusTransition({
|
|
@@ -268,7 +288,7 @@ export default class AIAgentTaskAPI {
|
|
|
268
288
|
status: toStatus,
|
|
269
289
|
completedAt: OneUptimeDate.getCurrentDate(),
|
|
270
290
|
lastHeartbeatAt: OneUptimeDate.getCurrentDate(),
|
|
271
|
-
...(
|
|
291
|
+
...(shouldStoreMessage
|
|
272
292
|
? { errorMessage: statusMessage }
|
|
273
293
|
: {}),
|
|
274
294
|
},
|
|
@@ -278,10 +298,16 @@ export default class AIAgentTaskAPI {
|
|
|
278
298
|
await AIRunEventService.appendEventToRun({
|
|
279
299
|
projectId: existingRun.projectId,
|
|
280
300
|
aiRunId: runId,
|
|
301
|
+
/*
|
|
302
|
+
* Only a genuine failure closes the event trail with
|
|
303
|
+
* RunFailed. A NoFixFound run ran to completion and reported
|
|
304
|
+
* a conclusion, so it closes with RunCompleted and carries
|
|
305
|
+
* the "no fix" reason in resultSummary.
|
|
306
|
+
*/
|
|
281
307
|
eventType:
|
|
282
|
-
toStatus === AIRunStatus.
|
|
283
|
-
? AIRunEventType.
|
|
284
|
-
: AIRunEventType.
|
|
308
|
+
toStatus === AIRunStatus.Error
|
|
309
|
+
? AIRunEventType.RunFailed
|
|
310
|
+
: AIRunEventType.RunCompleted,
|
|
285
311
|
...(statusMessage
|
|
286
312
|
? { resultSummary: { message: statusMessage } }
|
|
287
313
|
: {}),
|
|
@@ -120,6 +120,31 @@ export default class AIAgentTaskLogAPI {
|
|
|
120
120
|
);
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
/*
|
|
124
|
+
* Optional verbatim tool detail (see TaskLogger.toolCall): the
|
|
125
|
+
* arguments as executed and the full output the model saw, recorded
|
|
126
|
+
* on the run's transcript for the Logs page. Absent it — an older
|
|
127
|
+
* agent — behaviour is exactly as before.
|
|
128
|
+
*
|
|
129
|
+
* The event type stays ProgressLog even when the detail is present.
|
|
130
|
+
* That is deliberate, not laziness: the Overview's ChatActivityFeed
|
|
131
|
+
* renders a ProgressLog by printing resultSummary.message, but
|
|
132
|
+
* renders a ToolCallCompleted by trying to COMPLETE a matching
|
|
133
|
+
* running step from an earlier ToolCallStarted — an event the
|
|
134
|
+
* code-fix path never emits. Typing these as ToolCallCompleted
|
|
135
|
+
* therefore matched nothing and silently erased every tool line from
|
|
136
|
+
* the Overview feed. The payload rides on the same event instead.
|
|
137
|
+
*/
|
|
138
|
+
const toolName: string | undefined = data["toolName"] as
|
|
139
|
+
| string
|
|
140
|
+
| undefined;
|
|
141
|
+
const toolArguments: JSONObject | undefined = data[
|
|
142
|
+
"toolArguments"
|
|
143
|
+
] as JSONObject | undefined;
|
|
144
|
+
const toolResult: string | undefined = data["toolResult"] as
|
|
145
|
+
| string
|
|
146
|
+
| undefined;
|
|
147
|
+
|
|
123
148
|
await AIRunEventService.appendEventToRun({
|
|
124
149
|
projectId: existingRun.projectId,
|
|
125
150
|
aiRunId: runId,
|
|
@@ -128,6 +153,30 @@ export default class AIAgentTaskLogAPI {
|
|
|
128
153
|
message: message,
|
|
129
154
|
severity: severity,
|
|
130
155
|
},
|
|
156
|
+
...(toolName ? { toolName: toolName } : {}),
|
|
157
|
+
/*
|
|
158
|
+
* toolArguments (the column) is deliberately NOT written: its read
|
|
159
|
+
* ACL is project-member-wide, and a write_file call's arguments are
|
|
160
|
+
* the file's full new content. The same arguments go onto
|
|
161
|
+
* contentPayload below, whose read ACL is empty and which only the
|
|
162
|
+
* owner/admin-gated logs endpoint returns.
|
|
163
|
+
*/
|
|
164
|
+
...(toolName
|
|
165
|
+
? {
|
|
166
|
+
contentPayload: {
|
|
167
|
+
...(toolResult !== undefined
|
|
168
|
+
? { toolResult: toolResult }
|
|
169
|
+
: {}),
|
|
170
|
+
...(toolArguments
|
|
171
|
+
? {
|
|
172
|
+
responseToolCalls: [
|
|
173
|
+
{ name: toolName, arguments: toolArguments },
|
|
174
|
+
],
|
|
175
|
+
}
|
|
176
|
+
: {}),
|
|
177
|
+
},
|
|
178
|
+
}
|
|
179
|
+
: {}),
|
|
131
180
|
});
|
|
132
181
|
|
|
133
182
|
/* A progress report proves the agent is alive — refresh the heartbeat. */
|
package/Server/API/AIChatAPI.ts
CHANGED
|
@@ -21,6 +21,10 @@ import SubscriptionPlan, {
|
|
|
21
21
|
import { IsBillingEnabled, getAllEnvVars } from "../EnvironmentConfig";
|
|
22
22
|
import AIChatMessageRole from "../../Types/AI/AIChatMessageRole";
|
|
23
23
|
import AIChatMessageStatus from "../../Types/AI/AIChatMessageStatus";
|
|
24
|
+
import {
|
|
25
|
+
AIChatPageContext,
|
|
26
|
+
AIChatPageContextHelper,
|
|
27
|
+
} from "../../Types/AI/AIChatPageContext";
|
|
24
28
|
import AIChatPermissionMode, {
|
|
25
29
|
AIChatPermissionModeHelper,
|
|
26
30
|
} from "../../Types/AI/AIChatPermissionMode";
|
|
@@ -143,6 +147,16 @@ router.post(
|
|
|
143
147
|
? (req.body["permissionMode"] as AIChatPermissionMode)
|
|
144
148
|
: undefined;
|
|
145
149
|
|
|
150
|
+
/*
|
|
151
|
+
* Optional page context — what the user was looking at when they asked
|
|
152
|
+
* (an incident, a monitor, the logs explorer, …). It is a hint for the
|
|
153
|
+
* system prompt, so an invalid payload is dropped, never an error.
|
|
154
|
+
*/
|
|
155
|
+
const pageContext: AIChatPageContext | undefined =
|
|
156
|
+
AIChatPageContextHelper.sanitize(
|
|
157
|
+
req.body["pageContext"] as JSONObject | undefined,
|
|
158
|
+
);
|
|
159
|
+
|
|
146
160
|
// Plan gate: custom endpoints get no automatic billing check.
|
|
147
161
|
if (
|
|
148
162
|
IsBillingEnabled &&
|
|
@@ -435,6 +449,7 @@ router.post(
|
|
|
435
449
|
aiRunId: createdRun.id!,
|
|
436
450
|
llmProviderId: effectiveLlmProviderId,
|
|
437
451
|
permissionMode: effectivePermissionMode,
|
|
452
|
+
pageContext: pageContext,
|
|
438
453
|
props: props,
|
|
439
454
|
}).catch((error: Error) => {
|
|
440
455
|
logger.error(`AI chat turn crashed: ${error.message}`);
|
|
@@ -498,6 +513,7 @@ router.post(
|
|
|
498
513
|
return {
|
|
499
514
|
id: provider.id?.toString(),
|
|
500
515
|
name: provider.name,
|
|
516
|
+
description: provider.description || null,
|
|
501
517
|
llmType: provider.llmType?.toString() || null,
|
|
502
518
|
modelName: provider.modelName || null,
|
|
503
519
|
isDefault: provider.isDefault || false,
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import Express, {
|
|
2
|
+
ExpressRequest,
|
|
3
|
+
ExpressResponse,
|
|
4
|
+
ExpressRouter,
|
|
5
|
+
NextFunction,
|
|
6
|
+
} from "../Utils/Express";
|
|
7
|
+
import Response from "../Utils/Response";
|
|
8
|
+
import UserMiddleware from "../Middleware/UserAuthorization";
|
|
9
|
+
import CommonAPI from "./CommonAPI";
|
|
10
|
+
import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
11
|
+
import NotAuthorizedException from "../../Types/Exception/NotAuthorizedException";
|
|
12
|
+
import BadDataException from "../../Types/Exception/BadDataException";
|
|
13
|
+
import { JSONArray } from "../../Types/JSON";
|
|
14
|
+
import { AIFixReadiness } from "../../Types/AI/AIFixReadiness";
|
|
15
|
+
import CodeFixReadiness from "../Utils/AI/CodeFix/CodeFixReadiness";
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
* Project-wide "can AI actually do anything here?" — the gates a CodeFix run
|
|
19
|
+
* passes through, answered for the whole project rather than one exception.
|
|
20
|
+
* The AI Tasks page renders this instead of a static prerequisites banner, so
|
|
21
|
+
* what the user reads reflects what the server would actually decide.
|
|
22
|
+
*
|
|
23
|
+
* The per-exception equivalent lives at
|
|
24
|
+
* GET /telemetry-exception/ai-fix-readiness/:id; both share the checks in
|
|
25
|
+
* CodeFixReadiness, so they cannot drift apart.
|
|
26
|
+
*/
|
|
27
|
+
const router: ExpressRouter = Express.getRouter();
|
|
28
|
+
|
|
29
|
+
router.post(
|
|
30
|
+
"/ai-readiness/code-fix",
|
|
31
|
+
UserMiddleware.getUserMiddleware,
|
|
32
|
+
async (
|
|
33
|
+
req: ExpressRequest,
|
|
34
|
+
res: ExpressResponse,
|
|
35
|
+
next: NextFunction,
|
|
36
|
+
): Promise<void> => {
|
|
37
|
+
try {
|
|
38
|
+
const props: DatabaseCommonInteractionProps =
|
|
39
|
+
await CommonAPI.getDatabaseCommonInteractionProps(req);
|
|
40
|
+
|
|
41
|
+
/*
|
|
42
|
+
* Authorize explicitly. getUserMiddleware admits anonymous callers
|
|
43
|
+
* (it sets userType = Public and calls next()), and tenantId is read
|
|
44
|
+
* straight off the caller-supplied `tenantid` header before any auth
|
|
45
|
+
* runs — so neither is evidence of anything on its own. The readiness
|
|
46
|
+
* checks below all query with `isRoot: true`, which means there is no
|
|
47
|
+
* RBAC layer underneath to catch an unauthorized caller: this handler
|
|
48
|
+
* is the only gate. The per-exception sibling gets away without this
|
|
49
|
+
* because it loads the exception with the caller's props first, and
|
|
50
|
+
* derives the project from that authorized row.
|
|
51
|
+
*/
|
|
52
|
+
if (!props.userId) {
|
|
53
|
+
throw new NotAuthorizedException(
|
|
54
|
+
"AI readiness requires a logged-in user session.",
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!props.tenantId) {
|
|
59
|
+
throw new BadDataException("Project ID is required (tenantid header).");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Being logged in somewhere is not access to THIS project.
|
|
63
|
+
if (!props.userTenantAccessPermission?.[props.tenantId.toString()]) {
|
|
64
|
+
throw new NotAuthorizedException(
|
|
65
|
+
"You do not have access to this project.",
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const readiness: AIFixReadiness =
|
|
70
|
+
await CodeFixReadiness.getProjectReadiness({
|
|
71
|
+
projectId: props.tenantId,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return Response.sendJsonObjectResponse(req, res, {
|
|
75
|
+
ready: readiness.ready,
|
|
76
|
+
checks: readiness.checks as unknown as JSONArray,
|
|
77
|
+
});
|
|
78
|
+
} catch (err) {
|
|
79
|
+
next(err);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
export default router;
|
|
@@ -8,37 +8,48 @@ import Express, {
|
|
|
8
8
|
} from "../Utils/Express";
|
|
9
9
|
import Response from "../Utils/Response";
|
|
10
10
|
import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
11
|
+
import DatabaseCommonInteractionPropsUtil, {
|
|
12
|
+
PermissionType,
|
|
13
|
+
} from "../../Types/BaseDatabase/DatabaseCommonInteractionPropsUtil";
|
|
11
14
|
import ObjectID from "../../Types/ObjectID";
|
|
12
|
-
import PositiveNumber from "../../Types/PositiveNumber";
|
|
13
15
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
14
16
|
import NotAuthorizedException from "../../Types/Exception/NotAuthorizedException";
|
|
15
17
|
import SortOrder from "../../Types/BaseDatabase/SortOrder";
|
|
16
|
-
import Query from "../../Types/BaseDatabase/Query";
|
|
17
18
|
import Select from "../Types/Database/Select";
|
|
18
19
|
import { JSONObject } from "../../Types/JSON";
|
|
20
|
+
import Permission, { UserPermission } from "../../Types/Permission";
|
|
19
21
|
import AIRunType from "../../Types/AI/AIRunType";
|
|
20
22
|
import { CodeFixTaskTypeHelper } from "../../Types/AI/CodeFixTaskType";
|
|
21
23
|
import AIRun from "../../Models/DatabaseModels/AIRun";
|
|
22
24
|
import AIRunEvent from "../../Models/DatabaseModels/AIRunEvent";
|
|
25
|
+
import LlmLog from "../../Models/DatabaseModels/LlmLog";
|
|
23
26
|
import Project from "../../Models/DatabaseModels/Project";
|
|
24
27
|
import BaseModel from "../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
25
28
|
import ProjectService from "../Services/ProjectService";
|
|
26
29
|
import AIRunService from "../Services/AIRunService";
|
|
27
30
|
import AIRunEventService from "../Services/AIRunEventService";
|
|
31
|
+
import LlmLogService from "../Services/LlmLogService";
|
|
28
32
|
|
|
29
|
-
const DEFAULT_LIMIT: number = 25;
|
|
30
|
-
const MAX_LIMIT: number = 100;
|
|
31
33
|
const MAX_EVENTS: number = 500;
|
|
32
34
|
|
|
33
35
|
/*
|
|
34
|
-
*
|
|
36
|
+
* The AI fix task detail page's event trail.
|
|
35
37
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
38
|
+
* The LIST page does NOT come through here — it reads AIRuns through the
|
|
39
|
+
* standard /ai-run CRUD, which AIRunService opens up for code-fix runs via a
|
|
40
|
+
* forced `runType = CodeFix OR userId = <caller>` clause (see
|
|
41
|
+
* Utils/AI/AIRunPrivacyFilter).
|
|
42
|
+
*
|
|
43
|
+
* The events cannot follow. AIRunEvent has NO runType column to key that
|
|
44
|
+
* clause off, and "userId is null" does not mean "system-authored" — the
|
|
45
|
+
* investigation engine writes null-userId events too — so project-scoping
|
|
46
|
+
* AIRunEvent wholesale would expose other members' CHAT toolArguments, which
|
|
47
|
+
* carry user-typed prompt content. Events are therefore still reached only
|
|
48
|
+
* through a run this endpoint has already authorized as runType = CodeFix.
|
|
49
|
+
*
|
|
50
|
+
* Access is gated explicitly, the same pattern as AIInvestigationAPI: confirm
|
|
51
|
+
* the caller can read the parent object (the project) under THEIR permissions,
|
|
52
|
+
* then read the run and its events as root.
|
|
42
53
|
*/
|
|
43
54
|
|
|
44
55
|
const RUN_SELECT: Select<AIRun> = {
|
|
@@ -51,8 +62,62 @@ const RUN_SELECT: Select<AIRun> = {
|
|
|
51
62
|
triggeredByTelemetryExceptionId: true,
|
|
52
63
|
totalTokens: true,
|
|
53
64
|
codeFixTaskType: true,
|
|
65
|
+
taskNumber: true,
|
|
66
|
+
attemptCount: true,
|
|
67
|
+
llmCallCount: true,
|
|
68
|
+
toolCallCount: true,
|
|
69
|
+
/*
|
|
70
|
+
* Deliberately NOT selected: totalCostInUSDCents. This endpoint's gate is
|
|
71
|
+
* Project read, which admits roles beyond AIRun's own table ACL, so it must
|
|
72
|
+
* not return more than the detail page renders — and nothing there renders
|
|
73
|
+
* per-run spend.
|
|
74
|
+
*/
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/*
|
|
78
|
+
* The Logs page's event trail.
|
|
79
|
+
*
|
|
80
|
+
* toolArguments is deliberately NOT selected, even though this endpoint reads
|
|
81
|
+
* as root and could. A code-fix write_file call carries the file's full new
|
|
82
|
+
* content in its arguments, and that column's own read ACL stops at
|
|
83
|
+
* ProjectMember — narrower than this endpoint's Project-read gate, which also
|
|
84
|
+
* admits Viewers. Returning it would leak the same customer source code that
|
|
85
|
+
* contentPayload is stripped below to protect, to an even wider audience. The
|
|
86
|
+
* page never needed it: Logs.tsx renders tool arguments from
|
|
87
|
+
* contentPayload.responseToolCalls, the owner/admin-gated copy.
|
|
88
|
+
*/
|
|
89
|
+
const LOG_EVENT_SELECT: Select<AIRunEvent> = {
|
|
90
|
+
_id: true,
|
|
91
|
+
sequence: true,
|
|
92
|
+
eventType: true,
|
|
93
|
+
toolName: true,
|
|
94
|
+
resultSummary: true,
|
|
95
|
+
citationId: true,
|
|
96
|
+
createdAt: true,
|
|
97
|
+
contentPayload: true,
|
|
54
98
|
};
|
|
55
99
|
|
|
100
|
+
/*
|
|
101
|
+
* The per-LLM-call metering shown beside the transcript. Read under the
|
|
102
|
+
* CALLER's permissions (not root), so LlmLog's own table ACL decides who sees
|
|
103
|
+
* it rather than this endpoint's broader Project-read gate.
|
|
104
|
+
*/
|
|
105
|
+
const LLM_LOG_SELECT: Select<LlmLog> = {
|
|
106
|
+
_id: true,
|
|
107
|
+
modelName: true,
|
|
108
|
+
llmProviderName: true,
|
|
109
|
+
status: true,
|
|
110
|
+
statusMessage: true,
|
|
111
|
+
totalTokens: true,
|
|
112
|
+
completionTokens: true,
|
|
113
|
+
durationMs: true,
|
|
114
|
+
requestStartedAt: true,
|
|
115
|
+
requestCompletedAt: true,
|
|
116
|
+
createdAt: true,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const MAX_LLM_LOGS: number = 500;
|
|
120
|
+
|
|
56
121
|
/*
|
|
57
122
|
* Serialize a run with codeFixTaskType normalized: a null column means
|
|
58
123
|
* FixException (rows created before task recipes existed), and clients
|
|
@@ -81,9 +146,9 @@ export default class CodeFixRunAPI {
|
|
|
81
146
|
}
|
|
82
147
|
|
|
83
148
|
private initRoutes(): void {
|
|
84
|
-
//
|
|
149
|
+
// One run + its ordered glass-box event trail.
|
|
85
150
|
this.router.post(
|
|
86
|
-
"/code-fix-run/
|
|
151
|
+
"/code-fix-run/get/:runId",
|
|
87
152
|
UserMiddleware.getUserMiddleware,
|
|
88
153
|
async (
|
|
89
154
|
req: ExpressRequest,
|
|
@@ -94,47 +159,53 @@ export default class CodeFixRunAPI {
|
|
|
94
159
|
const props: DatabaseCommonInteractionProps =
|
|
95
160
|
await this.getAccessCheckedProps(req);
|
|
96
161
|
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
let limit: number = DEFAULT_LIMIT;
|
|
100
|
-
|
|
101
|
-
if (typeof body["limit"] === "number") {
|
|
102
|
-
limit = body["limit"];
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
limit = Math.min(Math.max(Math.floor(limit), 1), MAX_LIMIT);
|
|
106
|
-
|
|
107
|
-
let skip: number = 0;
|
|
162
|
+
const runIdString: string | undefined = req.params["runId"];
|
|
108
163
|
|
|
109
|
-
if (
|
|
110
|
-
|
|
164
|
+
if (!runIdString) {
|
|
165
|
+
throw new BadDataException("runId is required.");
|
|
111
166
|
}
|
|
112
167
|
|
|
113
|
-
const
|
|
114
|
-
projectId: props.tenantId!,
|
|
115
|
-
runType: AIRunType.CodeFix,
|
|
116
|
-
};
|
|
168
|
+
const runId: ObjectID = new ObjectID(runIdString);
|
|
117
169
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
170
|
+
/*
|
|
171
|
+
* Root read, but pinned to the caller's (access-checked) project —
|
|
172
|
+
* a run id from another project resolves to "not found".
|
|
173
|
+
*/
|
|
174
|
+
const run: AIRun | null = await AIRunService.findOneBy({
|
|
175
|
+
query: {
|
|
176
|
+
_id: runId.toString(),
|
|
177
|
+
projectId: props.tenantId!,
|
|
178
|
+
runType: AIRunType.CodeFix,
|
|
179
|
+
},
|
|
121
180
|
select: RUN_SELECT,
|
|
122
|
-
sort: { createdAt: SortOrder.Descending },
|
|
123
|
-
limit: limit,
|
|
124
|
-
skip: skip,
|
|
125
181
|
props: { isRoot: true },
|
|
126
182
|
});
|
|
127
183
|
|
|
128
|
-
|
|
129
|
-
|
|
184
|
+
if (!run) {
|
|
185
|
+
throw new BadDataException(
|
|
186
|
+
"Code fix run not found (or it does not belong to this project).",
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const events: Array<AIRunEvent> = await AIRunEventService.findBy({
|
|
191
|
+
query: { aiRunId: run.id! },
|
|
192
|
+
select: {
|
|
193
|
+
_id: true,
|
|
194
|
+
sequence: true,
|
|
195
|
+
eventType: true,
|
|
196
|
+
toolName: true,
|
|
197
|
+
resultSummary: true,
|
|
198
|
+
createdAt: true,
|
|
199
|
+
},
|
|
200
|
+
sort: { sequence: SortOrder.Ascending },
|
|
201
|
+
limit: MAX_EVENTS,
|
|
202
|
+
skip: 0,
|
|
130
203
|
props: { isRoot: true },
|
|
131
204
|
});
|
|
132
205
|
|
|
133
206
|
Response.sendJsonObjectResponse(req, res, {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}),
|
|
137
|
-
count: count.toNumber(),
|
|
207
|
+
run: runToJson(run),
|
|
208
|
+
events: BaseModel.toJSONArray(events, AIRunEvent),
|
|
138
209
|
});
|
|
139
210
|
return;
|
|
140
211
|
} catch (err) {
|
|
@@ -144,9 +215,25 @@ export default class CodeFixRunAPI {
|
|
|
144
215
|
},
|
|
145
216
|
);
|
|
146
217
|
|
|
147
|
-
|
|
218
|
+
/*
|
|
219
|
+
* The Logs page: the same run, its FULL event trail, and the per-call LLM
|
|
220
|
+
* metering — everything recorded about a run, for debugging one that went
|
|
221
|
+
* wrong.
|
|
222
|
+
*
|
|
223
|
+
* Two things are gated more tightly than the trail itself:
|
|
224
|
+
*
|
|
225
|
+
* - contentPayload (the verbatim prompts, model replies and tool output)
|
|
226
|
+
* is returned only to ProjectOwner/ProjectAdmin. It embeds customer
|
|
227
|
+
* source code, which is why the column's own read ACL is empty and why
|
|
228
|
+
* LlmLog redacts the same content — this endpoint is the single
|
|
229
|
+
* deliberate door to it, and it is not open to every project member.
|
|
230
|
+
*
|
|
231
|
+
* - LlmLog rows are read under the CALLER's permissions rather than as
|
|
232
|
+
* root, so LlmLog's table ACL — not this endpoint's broader Project-read
|
|
233
|
+
* gate — decides who sees the metering.
|
|
234
|
+
*/
|
|
148
235
|
this.router.post(
|
|
149
|
-
"/code-fix-run/
|
|
236
|
+
"/code-fix-run/logs/:runId",
|
|
150
237
|
UserMiddleware.getUserMiddleware,
|
|
151
238
|
async (
|
|
152
239
|
req: ExpressRequest,
|
|
@@ -165,10 +252,6 @@ export default class CodeFixRunAPI {
|
|
|
165
252
|
|
|
166
253
|
const runId: ObjectID = new ObjectID(runIdString);
|
|
167
254
|
|
|
168
|
-
/*
|
|
169
|
-
* Root read, but pinned to the caller's (access-checked) project —
|
|
170
|
-
* a run id from another project resolves to "not found".
|
|
171
|
-
*/
|
|
172
255
|
const run: AIRun | null = await AIRunService.findOneBy({
|
|
173
256
|
query: {
|
|
174
257
|
_id: runId.toString(),
|
|
@@ -185,25 +268,46 @@ export default class CodeFixRunAPI {
|
|
|
185
268
|
);
|
|
186
269
|
}
|
|
187
270
|
|
|
271
|
+
const canReadContent: boolean = this.callerCanReadRunContent(props);
|
|
272
|
+
|
|
188
273
|
const events: Array<AIRunEvent> = await AIRunEventService.findBy({
|
|
189
274
|
query: { aiRunId: run.id! },
|
|
190
|
-
select:
|
|
191
|
-
_id: true,
|
|
192
|
-
sequence: true,
|
|
193
|
-
eventType: true,
|
|
194
|
-
toolName: true,
|
|
195
|
-
resultSummary: true,
|
|
196
|
-
createdAt: true,
|
|
197
|
-
},
|
|
275
|
+
select: LOG_EVENT_SELECT,
|
|
198
276
|
sort: { sequence: SortOrder.Ascending },
|
|
199
277
|
limit: MAX_EVENTS,
|
|
200
278
|
skip: 0,
|
|
201
279
|
props: { isRoot: true },
|
|
202
280
|
});
|
|
203
281
|
|
|
282
|
+
const eventsJson: Array<JSONObject> = BaseModel.toJSONArray(
|
|
283
|
+
events,
|
|
284
|
+
AIRunEvent,
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
if (!canReadContent) {
|
|
288
|
+
for (const eventJson of eventsJson) {
|
|
289
|
+
delete eventJson["contentPayload"];
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/*
|
|
294
|
+
* Under the caller's own permissions on purpose — see the note
|
|
295
|
+
* above. A caller without LlmLog read simply gets no metering rows.
|
|
296
|
+
*/
|
|
297
|
+
const llmLogs: Array<LlmLog> = await LlmLogService.findBy({
|
|
298
|
+
query: { aiRunId: runId, projectId: props.tenantId! },
|
|
299
|
+
select: LLM_LOG_SELECT,
|
|
300
|
+
sort: { createdAt: SortOrder.Ascending },
|
|
301
|
+
limit: MAX_LLM_LOGS,
|
|
302
|
+
skip: 0,
|
|
303
|
+
props: props,
|
|
304
|
+
});
|
|
305
|
+
|
|
204
306
|
Response.sendJsonObjectResponse(req, res, {
|
|
205
307
|
run: runToJson(run),
|
|
206
|
-
events:
|
|
308
|
+
events: eventsJson,
|
|
309
|
+
llmLogs: BaseModel.toJSONArray(llmLogs, LlmLog),
|
|
310
|
+
canReadContent: canReadContent,
|
|
207
311
|
});
|
|
208
312
|
return;
|
|
209
313
|
} catch (err) {
|
|
@@ -214,6 +318,35 @@ export default class CodeFixRunAPI {
|
|
|
214
318
|
);
|
|
215
319
|
}
|
|
216
320
|
|
|
321
|
+
/*
|
|
322
|
+
* Whether the caller may see a run's verbatim LLM/tool content. Project
|
|
323
|
+
* owners and admins only: the content embeds customer source code, so the
|
|
324
|
+
* bar is higher than the Project-read gate that admits them to the page.
|
|
325
|
+
*
|
|
326
|
+
* Read through getUserPermissions(Allow) rather than off
|
|
327
|
+
* userTenantAccessPermission directly, because that array holds GRANTS AND
|
|
328
|
+
* DENIALS together, discriminated only by isBlockPermission. Mapping it raw
|
|
329
|
+
* counts a team's explicit "block ProjectAdmin" entry as a grant of
|
|
330
|
+
* ProjectAdmin — inverting the control, so the admin action that restricts a
|
|
331
|
+
* team would be what hands it the source code.
|
|
332
|
+
*/
|
|
333
|
+
private callerCanReadRunContent(
|
|
334
|
+
props: DatabaseCommonInteractionProps,
|
|
335
|
+
): boolean {
|
|
336
|
+
const permissions: Array<Permission> =
|
|
337
|
+
DatabaseCommonInteractionPropsUtil.getUserPermissions(
|
|
338
|
+
props,
|
|
339
|
+
PermissionType.Allow,
|
|
340
|
+
).map((userPermission: UserPermission) => {
|
|
341
|
+
return userPermission.permission;
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
return (
|
|
345
|
+
permissions.includes(Permission.ProjectOwner) ||
|
|
346
|
+
permissions.includes(Permission.ProjectAdmin)
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
|
|
217
350
|
/*
|
|
218
351
|
* Requires a logged-in session and a tenant, then verifies project access
|
|
219
352
|
* by reading the project under the USER's permissions (null when the user
|
|
@@ -4,11 +4,11 @@ import BadDataException from "../../Types/Exception/BadDataException";
|
|
|
4
4
|
import ObjectID from "../../Types/ObjectID";
|
|
5
5
|
import BaseModel from "../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
6
6
|
import TelemetryExceptionService, {
|
|
7
|
-
AIFixReadiness,
|
|
8
7
|
DashboardServiceSummary,
|
|
9
8
|
DashboardSummaryResult,
|
|
10
9
|
Service as TelemetryExceptionServiceType,
|
|
11
10
|
} from "../Services/TelemetryExceptionService";
|
|
11
|
+
import { AIFixReadiness } from "../../Types/AI/AIFixReadiness";
|
|
12
12
|
import AIRunService from "../Services/AIRunService";
|
|
13
13
|
import UserMiddleware from "../Middleware/UserAuthorization";
|
|
14
14
|
import Response from "../Utils/Response";
|
|
@@ -51,6 +51,11 @@ const CODE_FIX_STATUS_TEXT: {
|
|
|
51
51
|
description:
|
|
52
52
|
"The AI agent finished. Review the pull request it opened for the proposed fix.",
|
|
53
53
|
},
|
|
54
|
+
[AIRunStatus.NoFixFound]: {
|
|
55
|
+
title: "No Fix Found",
|
|
56
|
+
description:
|
|
57
|
+
"The AI agent reviewed the code and did not find a fix to propose, so it opened no pull request.",
|
|
58
|
+
},
|
|
54
59
|
[AIRunStatus.Error]: {
|
|
55
60
|
title: "Error",
|
|
56
61
|
description: "The AI agent could not complete the fix.",
|