@sentry/junior 0.71.2 → 0.72.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/junior.mjs +10 -0
- package/dist/api-reference.d.ts +2 -0
- package/dist/app.d.ts +5 -5
- package/dist/app.js +1039 -1971
- package/dist/chat/agent-dispatch/heartbeat.d.ts +0 -6
- package/dist/chat/mcp/errors.d.ts +3 -0
- package/dist/chat/mcp/tool-manager.d.ts +5 -1
- package/dist/chat/plugins/agent-hooks.d.ts +3 -2
- package/dist/chat/requester.d.ts +60 -0
- package/dist/chat/respond.d.ts +2 -6
- package/dist/chat/runtime/agent-continue-runner.d.ts +25 -0
- package/dist/chat/runtime/reply-executor.d.ts +4 -4
- package/dist/chat/runtime/turn.d.ts +2 -2
- package/dist/chat/services/agent-continue.d.ts +27 -0
- package/dist/chat/services/message-actor-identity.d.ts +12 -4
- package/dist/chat/services/turn-session-record.d.ts +10 -7
- package/dist/chat/slack/user.d.ts +4 -4
- package/dist/chat/state/adapter.d.ts +2 -0
- package/dist/chat/state/conversation-details.d.ts +4 -3
- package/dist/chat/state/session-log.d.ts +43 -0
- package/dist/chat/state/turn-session.d.ts +7 -10
- package/dist/chat/task-execution/queue.d.ts +1 -1
- package/dist/chat/task-execution/slack-work.d.ts +5 -5
- package/dist/chat/task-execution/store.d.ts +83 -48
- package/dist/chat/task-execution/worker.d.ts +3 -3
- package/dist/chat/tools/definition.d.ts +3 -0
- package/dist/chat/tools/execution/tool-error-handler.d.ts +2 -1
- package/dist/chat/tools/types.d.ts +2 -5
- package/dist/{chunk-R62YWUNO.js → chunk-3FYPXHPL.js} +10 -28
- package/dist/chunk-4JXCSGSA.js +212 -0
- package/dist/{chunk-GT67ZWZQ.js → chunk-55XEZFGD.js} +5 -3
- package/dist/{chunk-BBXYXOJW.js → chunk-6GEYPE6T.js} +18 -523
- package/dist/chunk-G3E7SCME.js +28 -0
- package/dist/{chunk-UXG6TU2U.js → chunk-GB3AL54K.js} +8 -93
- package/dist/chunk-HNMUVGSR.js +1119 -0
- package/dist/{chunk-XE2VFQQN.js → chunk-ICKIDP7G.js} +1 -1
- package/dist/chunk-KVZL5NZS.js +519 -0
- package/dist/chunk-PP7AGSBU.js +185 -0
- package/dist/{chunk-B5HKWWQB.js → chunk-VLIO6RQR.js} +8 -6
- package/dist/{chunk-HOGQL2H6.js → chunk-VSNA5KAB.js} +177 -101
- package/dist/{chunk-76YMBKW7.js → chunk-XC33FJZN.js} +4 -12
- package/dist/{chunk-JS4HURDT.js → chunk-ZJQPA67D.js} +25 -25
- package/dist/cli/check.js +10 -8
- package/dist/cli/run.js +9 -1
- package/dist/cli/snapshot-warmup.js +10 -7
- package/dist/cli/upgrade.js +599 -0
- package/dist/nitro.d.ts +1 -1
- package/dist/nitro.js +5 -4
- package/dist/plugins.d.ts +1 -1
- package/dist/reporting/conversations.d.ts +116 -0
- package/dist/reporting.d.ts +24 -129
- package/dist/reporting.js +310 -158
- package/package.json +3 -3
- package/dist/chat/runtime/timeout-resume-runner.d.ts +0 -19
- package/dist/chat/services/requester-identity.d.ts +0 -19
- package/dist/chat/services/timeout-resume.d.ts +0 -23
- package/dist/handlers/turn-resume.d.ts +0 -4
package/dist/reporting.js
CHANGED
|
@@ -8,67 +8,49 @@ import {
|
|
|
8
8
|
getAgentTurnSessionRecord,
|
|
9
9
|
getConversationDetails,
|
|
10
10
|
getConversationDetailsForIds,
|
|
11
|
-
listAgentTurnSessionSummaries,
|
|
12
11
|
listAgentTurnSessionSummariesForConversation,
|
|
13
12
|
resolveSlackConversationContextFromThreadId
|
|
14
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-VSNA5KAB.js";
|
|
15
14
|
import {
|
|
16
15
|
discoverSkills
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import
|
|
16
|
+
} from "./chunk-55XEZFGD.js";
|
|
17
|
+
import {
|
|
18
|
+
getConversation,
|
|
19
|
+
listConversationsByActivity
|
|
20
|
+
} from "./chunk-HNMUVGSR.js";
|
|
21
|
+
import "./chunk-XC33FJZN.js";
|
|
22
|
+
import "./chunk-G3E7SCME.js";
|
|
19
23
|
import {
|
|
20
24
|
getPluginPackageContent,
|
|
21
25
|
getPluginProviders
|
|
22
|
-
} from "./chunk-
|
|
23
|
-
import
|
|
26
|
+
} from "./chunk-GB3AL54K.js";
|
|
27
|
+
import {
|
|
28
|
+
homeDir
|
|
29
|
+
} from "./chunk-KVZL5NZS.js";
|
|
30
|
+
import "./chunk-3FYPXHPL.js";
|
|
24
31
|
import {
|
|
25
32
|
canExposeConversationPayload,
|
|
26
33
|
parseSlackThreadId,
|
|
27
34
|
resolveConversationPrivacy
|
|
28
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-ZJQPA67D.js";
|
|
36
|
+
import "./chunk-PP7AGSBU.js";
|
|
29
37
|
import {
|
|
30
|
-
homeDir,
|
|
31
38
|
isRecord
|
|
32
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-6GEYPE6T.js";
|
|
33
40
|
import "./chunk-Z3YD6NHK.js";
|
|
34
41
|
import "./chunk-2KG3PWR4.js";
|
|
35
42
|
|
|
36
43
|
// src/reporting.ts
|
|
37
44
|
import { readFileSync } from "fs";
|
|
38
45
|
import path from "path";
|
|
46
|
+
|
|
47
|
+
// src/reporting/conversations.ts
|
|
39
48
|
var HUNG_TURN_PROGRESS_MS = 5 * 60 * 1e3;
|
|
40
49
|
var SAFE_METADATA_KEY_LIMIT = 20;
|
|
41
50
|
var PRIVATE_CONVERSATION_LABEL = "Private Conversation";
|
|
42
|
-
var
|
|
43
|
-
var
|
|
51
|
+
var CONVERSATION_FEED_LIMIT = 50;
|
|
52
|
+
var CONVERSATION_STATS_LIMIT = 5e3;
|
|
44
53
|
var RECENT_CONVERSATION_STATS_WINDOW_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
45
|
-
function readDescriptionText() {
|
|
46
|
-
try {
|
|
47
|
-
const raw = readFileSync(
|
|
48
|
-
path.join(homeDir(), "DESCRIPTION.md"),
|
|
49
|
-
"utf8"
|
|
50
|
-
).trim();
|
|
51
|
-
return raw || void 0;
|
|
52
|
-
} catch {
|
|
53
|
-
return void 0;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
async function readHealth() {
|
|
57
|
-
const res = GET();
|
|
58
|
-
return await res.json();
|
|
59
|
-
}
|
|
60
|
-
async function readSkills() {
|
|
61
|
-
const skills = await discoverSkills();
|
|
62
|
-
return skills.map((skill) => ({
|
|
63
|
-
name: skill.name,
|
|
64
|
-
pluginProvider: skill.pluginProvider
|
|
65
|
-
}));
|
|
66
|
-
}
|
|
67
|
-
async function readPlugins() {
|
|
68
|
-
return getPluginProviders().map((plugin) => ({
|
|
69
|
-
name: plugin.manifest.name
|
|
70
|
-
}));
|
|
71
|
-
}
|
|
72
54
|
function statusFromCheckpoint(summary, nowMs = Date.now()) {
|
|
73
55
|
const state = summary.state;
|
|
74
56
|
if (state === "running" && nowMs - summary.lastProgressAtMs > HUNG_TURN_PROGRESS_MS) {
|
|
@@ -91,6 +73,12 @@ function surfaceFromConversationId(conversationId) {
|
|
|
91
73
|
function surfaceFromSummary(summary) {
|
|
92
74
|
return summary.surface ?? surfaceFromConversationId(summary.conversationId);
|
|
93
75
|
}
|
|
76
|
+
function surfaceFromSource(source, conversationId) {
|
|
77
|
+
if (source === "slack" || source === "api" || source === "scheduler") {
|
|
78
|
+
return source;
|
|
79
|
+
}
|
|
80
|
+
return surfaceFromConversationId(conversationId);
|
|
81
|
+
}
|
|
94
82
|
function requesterIdentityReport(requester) {
|
|
95
83
|
if (!requester) return void 0;
|
|
96
84
|
const identity = {
|
|
@@ -101,7 +89,7 @@ function requesterIdentityReport(requester) {
|
|
|
101
89
|
};
|
|
102
90
|
return Object.keys(identity).length > 0 ? identity : void 0;
|
|
103
91
|
}
|
|
104
|
-
function
|
|
92
|
+
function usageReport(usage) {
|
|
105
93
|
if (!usage) return void 0;
|
|
106
94
|
const report = {
|
|
107
95
|
...usage.inputTokens !== void 0 ? { inputTokens: usage.inputTokens } : {},
|
|
@@ -135,7 +123,7 @@ function sessionReportFromSummary(summary, nowMs = Date.now(), details) {
|
|
|
135
123
|
);
|
|
136
124
|
const sentryTraceUrl = summary.traceId ? buildSentryTraceUrl(summary.traceId) : void 0;
|
|
137
125
|
const requesterIdentity = requesterIdentityReport(effectiveRequester);
|
|
138
|
-
const cumulativeUsage =
|
|
126
|
+
const cumulativeUsage = usageReport(summary.cumulativeUsage);
|
|
139
127
|
return {
|
|
140
128
|
conversationId: summary.conversationId,
|
|
141
129
|
displayTitle,
|
|
@@ -156,6 +144,98 @@ function sessionReportFromSummary(summary, nowMs = Date.now(), details) {
|
|
|
156
144
|
...sentryTraceUrl ? { sentryTraceUrl } : {}
|
|
157
145
|
};
|
|
158
146
|
}
|
|
147
|
+
function statusFromConversation(conversation, fallback, nowMs) {
|
|
148
|
+
if (conversation.execution.status === "idle") {
|
|
149
|
+
if (fallback === "failed" || fallback === "superseded") {
|
|
150
|
+
return fallback;
|
|
151
|
+
}
|
|
152
|
+
return "completed";
|
|
153
|
+
}
|
|
154
|
+
const updatedAtMs = conversation.execution.updatedAtMs ?? conversation.updatedAtMs;
|
|
155
|
+
if (conversation.execution.status === "running" && nowMs - updatedAtMs > HUNG_TURN_PROGRESS_MS) {
|
|
156
|
+
return "hung";
|
|
157
|
+
}
|
|
158
|
+
return "active";
|
|
159
|
+
}
|
|
160
|
+
function titleFromConversation(args) {
|
|
161
|
+
const slackThread = parseSlackThreadId(args.conversation.conversationId);
|
|
162
|
+
const effectiveChannelName = args.details?.channelName ?? args.conversation.channelName;
|
|
163
|
+
const slackConversation = resolveSlackConversationContextFromThreadId({
|
|
164
|
+
threadId: args.conversation.conversationId,
|
|
165
|
+
channelName: effectiveChannelName
|
|
166
|
+
});
|
|
167
|
+
const privateLabel = resolveConversationPrivacy({
|
|
168
|
+
conversationId: args.conversation.conversationId
|
|
169
|
+
}) !== "public" ? slackConversation ? formatSlackConversationRedactedLabel(slackConversation) : PRIVATE_CONVERSATION_LABEL : void 0;
|
|
170
|
+
return privateLabel ?? args.details?.displayTitle ?? args.conversation.title ?? slackStatsLocationLabel({
|
|
171
|
+
channel: slackThread?.channelId,
|
|
172
|
+
channelName: effectiveChannelName
|
|
173
|
+
}) ?? surfaceFallbackLabel(args.surface);
|
|
174
|
+
}
|
|
175
|
+
function applyConversationIndexMetadata(args) {
|
|
176
|
+
const surface = args.details?.originSurface ?? (args.conversation.source ? surfaceFromSource(
|
|
177
|
+
args.conversation.source,
|
|
178
|
+
args.conversation.conversationId
|
|
179
|
+
) : args.report.surface);
|
|
180
|
+
const slackThread = parseSlackThreadId(args.conversation.conversationId);
|
|
181
|
+
const effectiveChannelName = args.details?.channelName ?? args.conversation.channelName ?? args.report.channelName;
|
|
182
|
+
const requesterIdentity = requesterIdentityReport(args.details?.originRequester) ?? args.report.requesterIdentity ?? requesterIdentityReport(args.conversation.requester);
|
|
183
|
+
const status = statusFromConversation(
|
|
184
|
+
args.conversation,
|
|
185
|
+
args.report.status,
|
|
186
|
+
args.nowMs
|
|
187
|
+
);
|
|
188
|
+
const lastSeenAtMs = Math.max(
|
|
189
|
+
reportTime(args.report.lastSeenAt) ?? 0,
|
|
190
|
+
args.conversation.lastActivityAtMs
|
|
191
|
+
);
|
|
192
|
+
const lastProgressAtMs = Math.max(
|
|
193
|
+
reportTime(args.report.lastProgressAt) ?? 0,
|
|
194
|
+
args.conversation.execution.updatedAtMs ?? args.conversation.updatedAtMs
|
|
195
|
+
);
|
|
196
|
+
return {
|
|
197
|
+
...args.report,
|
|
198
|
+
displayTitle: titleFromConversation({
|
|
199
|
+
conversation: args.conversation,
|
|
200
|
+
details: args.details,
|
|
201
|
+
surface
|
|
202
|
+
}),
|
|
203
|
+
status,
|
|
204
|
+
lastSeenAt: new Date(lastSeenAtMs).toISOString(),
|
|
205
|
+
lastProgressAt: new Date(lastProgressAtMs).toISOString(),
|
|
206
|
+
surface,
|
|
207
|
+
...requesterIdentity ? { requesterIdentity } : {},
|
|
208
|
+
...slackThread ? { channel: slackThread.channelId } : {},
|
|
209
|
+
...effectiveChannelName ? { channelName: effectiveChannelName } : {}
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function sessionReportFromConversation(conversation, nowMs, details) {
|
|
213
|
+
const surface = details?.originSurface ?? surfaceFromSource(conversation.source, conversation.conversationId);
|
|
214
|
+
const sentryConversationUrl = buildSentryConversationUrl(
|
|
215
|
+
conversation.conversationId
|
|
216
|
+
);
|
|
217
|
+
const requesterIdentity = requesterIdentityReport(
|
|
218
|
+
details?.originRequester ?? conversation.requester
|
|
219
|
+
);
|
|
220
|
+
const slackThread = parseSlackThreadId(conversation.conversationId);
|
|
221
|
+
return {
|
|
222
|
+
conversationId: conversation.conversationId,
|
|
223
|
+
cumulativeDurationMs: 0,
|
|
224
|
+
displayTitle: titleFromConversation({ conversation, details, surface }),
|
|
225
|
+
id: conversation.execution.runId ?? conversation.conversationId,
|
|
226
|
+
lastProgressAt: new Date(
|
|
227
|
+
conversation.execution.updatedAtMs ?? conversation.updatedAtMs
|
|
228
|
+
).toISOString(),
|
|
229
|
+
lastSeenAt: new Date(conversation.lastActivityAtMs).toISOString(),
|
|
230
|
+
startedAt: new Date(conversation.createdAtMs).toISOString(),
|
|
231
|
+
status: statusFromConversation(conversation, void 0, nowMs),
|
|
232
|
+
surface,
|
|
233
|
+
...requesterIdentity ? { requesterIdentity } : {},
|
|
234
|
+
...slackThread ? { channel: slackThread.channelId } : {},
|
|
235
|
+
...details?.channelName ?? conversation.channelName ? { channelName: details?.channelName ?? conversation.channelName } : {},
|
|
236
|
+
...sentryConversationUrl ? { sentryConversationUrl } : {}
|
|
237
|
+
};
|
|
238
|
+
}
|
|
159
239
|
function reportTime(value) {
|
|
160
240
|
const time = Date.parse(value);
|
|
161
241
|
return Number.isFinite(time) ? time : void 0;
|
|
@@ -176,18 +256,18 @@ function usageTokenTotal(usage) {
|
|
|
176
256
|
}
|
|
177
257
|
return typeof usage.totalTokens === "number" && Number.isFinite(usage.totalTokens) ? Math.max(0, Math.floor(usage.totalTokens)) : void 0;
|
|
178
258
|
}
|
|
179
|
-
function
|
|
180
|
-
return typeof
|
|
259
|
+
function runDurationSnapshot(run) {
|
|
260
|
+
return typeof run.cumulativeDurationMs === "number" && Number.isFinite(run.cumulativeDurationMs) ? Math.max(0, Math.floor(run.cumulativeDurationMs)) : void 0;
|
|
181
261
|
}
|
|
182
|
-
function
|
|
262
|
+
function runContributions(runs) {
|
|
183
263
|
let previousDuration = 0;
|
|
184
264
|
let previousTokens = 0;
|
|
185
|
-
return
|
|
186
|
-
const duration =
|
|
187
|
-
const tokens = usageTokenTotal(
|
|
265
|
+
return runs.map((run) => {
|
|
266
|
+
const duration = runDurationSnapshot(run);
|
|
267
|
+
const tokens = usageTokenTotal(run.cumulativeUsage);
|
|
188
268
|
const contribution = {
|
|
189
269
|
durationMs: duration === void 0 ? 0 : Math.max(0, duration - previousDuration),
|
|
190
|
-
|
|
270
|
+
run
|
|
191
271
|
};
|
|
192
272
|
if (tokens !== void 0) {
|
|
193
273
|
contribution.tokens = Math.max(0, tokens - previousTokens);
|
|
@@ -257,8 +337,8 @@ function displayTitleFromDetails(conversationId, details) {
|
|
|
257
337
|
channelName: details.channelName
|
|
258
338
|
}) ?? (details.originSurface ? surfaceFallbackLabel(details.originSurface) : void 0);
|
|
259
339
|
}
|
|
260
|
-
function locationLabel(
|
|
261
|
-
return slackStatsLocationLabel(
|
|
340
|
+
function locationLabel(run) {
|
|
341
|
+
return slackStatsLocationLabel(run) ?? surfaceFallbackLabel(run.surface);
|
|
262
342
|
}
|
|
263
343
|
function emptyStatsItem(label) {
|
|
264
344
|
return {
|
|
@@ -268,7 +348,7 @@ function emptyStatsItem(label) {
|
|
|
268
348
|
failed: 0,
|
|
269
349
|
hung: 0,
|
|
270
350
|
label,
|
|
271
|
-
|
|
351
|
+
runs: 0
|
|
272
352
|
};
|
|
273
353
|
}
|
|
274
354
|
function addItemTokens(item, tokens) {
|
|
@@ -276,20 +356,20 @@ function addItemTokens(item, tokens) {
|
|
|
276
356
|
item.tokens = (item.tokens ?? 0) + tokens;
|
|
277
357
|
}
|
|
278
358
|
}
|
|
279
|
-
function statusSignals(
|
|
359
|
+
function statusSignals(runs) {
|
|
280
360
|
return {
|
|
281
|
-
active:
|
|
282
|
-
failed:
|
|
283
|
-
hung:
|
|
361
|
+
active: runs.some((run) => run.status === "active"),
|
|
362
|
+
failed: runs.some((run) => run.status === "failed"),
|
|
363
|
+
hung: runs.some((run) => run.status === "hung")
|
|
284
364
|
};
|
|
285
365
|
}
|
|
286
366
|
function statsItems(map) {
|
|
287
367
|
return [...map.values()].sort(
|
|
288
|
-
(left, right) => right.conversations - left.conversations || right.
|
|
368
|
+
(left, right) => right.conversations - left.conversations || right.runs - left.runs || right.durationMs - left.durationMs || left.label.localeCompare(right.label)
|
|
289
369
|
);
|
|
290
370
|
}
|
|
291
|
-
function
|
|
292
|
-
return [...
|
|
371
|
+
function newestRun(runs) {
|
|
372
|
+
return [...runs].sort(
|
|
293
373
|
(left, right) => (reportTime(right.lastSeenAt) ?? 0) - (reportTime(left.lastSeenAt) ?? 0) || right.id.localeCompare(left.id)
|
|
294
374
|
)[0];
|
|
295
375
|
}
|
|
@@ -303,19 +383,19 @@ function recentConversationGroups(args) {
|
|
|
303
383
|
]);
|
|
304
384
|
}
|
|
305
385
|
return [...groups.values()].map(
|
|
306
|
-
(
|
|
386
|
+
(runs) => [...runs].sort(
|
|
307
387
|
(left, right) => (reportTime(left.startedAt) ?? 0) - (reportTime(right.startedAt) ?? 0) || left.id.localeCompare(right.id)
|
|
308
388
|
)
|
|
309
|
-
).filter((
|
|
310
|
-
const activityAt = reportTime(
|
|
389
|
+
).filter((runs) => {
|
|
390
|
+
const activityAt = reportTime(newestRun(runs).lastSeenAt);
|
|
311
391
|
return activityAt !== void 0 && activityAt >= startMs && activityAt <= args.nowMs;
|
|
312
392
|
});
|
|
313
393
|
}
|
|
314
|
-
function conversationDurationMs(
|
|
315
|
-
if (!
|
|
394
|
+
function conversationDurationMs(runs) {
|
|
395
|
+
if (!runs.some((run) => runDurationSnapshot(run) !== void 0)) {
|
|
316
396
|
return 0;
|
|
317
397
|
}
|
|
318
|
-
return contributionDurationTotal(
|
|
398
|
+
return contributionDurationTotal(runContributions(runs));
|
|
319
399
|
}
|
|
320
400
|
function buildConversationStatsReport(args) {
|
|
321
401
|
const conversations = recentConversationGroups(args);
|
|
@@ -326,30 +406,30 @@ function buildConversationStatsReport(args) {
|
|
|
326
406
|
let active = 0;
|
|
327
407
|
let failed = 0;
|
|
328
408
|
let hung = 0;
|
|
329
|
-
for (const
|
|
330
|
-
const contributions =
|
|
331
|
-
const conversationSignals = statusSignals(
|
|
409
|
+
for (const runs of conversations) {
|
|
410
|
+
const contributions = runContributions(runs);
|
|
411
|
+
const conversationSignals = statusSignals(runs);
|
|
332
412
|
const conversationTokens = contributionTokenTotal(contributions);
|
|
333
413
|
durationMs += contributionDurationTotal(contributions);
|
|
334
414
|
tokens = addTokenTotal(tokens, conversationTokens);
|
|
335
415
|
active += conversationSignals.active ? 1 : 0;
|
|
336
416
|
failed += conversationSignals.failed ? 1 : 0;
|
|
337
417
|
hung += conversationSignals.hung ? 1 : 0;
|
|
338
|
-
const
|
|
418
|
+
const requesterRuns = /* @__PURE__ */ new Map();
|
|
339
419
|
for (const contribution of contributions) {
|
|
340
|
-
const requester = requesterLabel(contribution.
|
|
341
|
-
|
|
342
|
-
...
|
|
420
|
+
const requester = requesterLabel(contribution.run.requesterIdentity) ?? "Unknown";
|
|
421
|
+
requesterRuns.set(requester, [
|
|
422
|
+
...requesterRuns.get(requester) ?? [],
|
|
343
423
|
contribution
|
|
344
424
|
]);
|
|
345
425
|
}
|
|
346
|
-
for (const [requester, requesterContributions] of
|
|
426
|
+
for (const [requester, requesterContributions] of requesterRuns) {
|
|
347
427
|
const item = requesters.get(requester) ?? emptyStatsItem(requester);
|
|
348
428
|
const signals = statusSignals(
|
|
349
|
-
requesterContributions.map((contribution) => contribution.
|
|
429
|
+
requesterContributions.map((contribution) => contribution.run)
|
|
350
430
|
);
|
|
351
431
|
item.conversations += 1;
|
|
352
|
-
item.
|
|
432
|
+
item.runs += requesterContributions.length;
|
|
353
433
|
item.durationMs += contributionDurationTotal(requesterContributions);
|
|
354
434
|
item.active += signals.active ? 1 : 0;
|
|
355
435
|
item.failed += signals.failed ? 1 : 0;
|
|
@@ -357,11 +437,11 @@ function buildConversationStatsReport(args) {
|
|
|
357
437
|
addItemTokens(item, contributionTokenTotal(requesterContributions));
|
|
358
438
|
requesters.set(requester, item);
|
|
359
439
|
}
|
|
360
|
-
const location = locationLabel(
|
|
440
|
+
const location = locationLabel(newestRun(runs));
|
|
361
441
|
const locationItem = locations.get(location) ?? emptyStatsItem(location);
|
|
362
442
|
locationItem.conversations += 1;
|
|
363
|
-
locationItem.
|
|
364
|
-
locationItem.durationMs += conversationDurationMs(
|
|
443
|
+
locationItem.runs += runs.length;
|
|
444
|
+
locationItem.durationMs += conversationDurationMs(runs);
|
|
365
445
|
locationItem.active += conversationSignals.active ? 1 : 0;
|
|
366
446
|
locationItem.failed += conversationSignals.failed ? 1 : 0;
|
|
367
447
|
locationItem.hung += conversationSignals.hung ? 1 : 0;
|
|
@@ -379,41 +459,16 @@ function buildConversationStatsReport(args) {
|
|
|
379
459
|
requesters: statsItems(requesters),
|
|
380
460
|
sampleLimit: args.sampleLimit,
|
|
381
461
|
sampleSize: args.sampleSize,
|
|
382
|
-
source: "
|
|
462
|
+
source: "conversation_index",
|
|
383
463
|
...tokens !== void 0 ? { tokens } : {},
|
|
384
464
|
truncated: args.truncated,
|
|
385
|
-
|
|
465
|
+
runs: conversations.reduce((sum, runs) => sum + runs.length, 0),
|
|
386
466
|
windowEnd: new Date(args.nowMs).toISOString(),
|
|
387
467
|
windowStart: new Date(
|
|
388
468
|
args.nowMs - RECENT_CONVERSATION_STATS_WINDOW_MS
|
|
389
469
|
).toISOString()
|
|
390
470
|
};
|
|
391
471
|
}
|
|
392
|
-
async function completeSampledConversationSummaries(args) {
|
|
393
|
-
if (!args.truncated) {
|
|
394
|
-
return args.summaries;
|
|
395
|
-
}
|
|
396
|
-
const conversationIds = [
|
|
397
|
-
...new Set(args.summaries.map((summary) => summary.conversationId))
|
|
398
|
-
];
|
|
399
|
-
const groups = await Promise.all(
|
|
400
|
-
conversationIds.map(
|
|
401
|
-
(conversationId) => listAgentTurnSessionSummariesForConversation(conversationId)
|
|
402
|
-
)
|
|
403
|
-
);
|
|
404
|
-
const summariesByTurn = /* @__PURE__ */ new Map();
|
|
405
|
-
for (const group of groups) {
|
|
406
|
-
for (const summary of group) {
|
|
407
|
-
summariesByTurn.set(
|
|
408
|
-
`${summary.conversationId}:${summary.sessionId}`,
|
|
409
|
-
summary
|
|
410
|
-
);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
return [...summariesByTurn.values()].sort(
|
|
414
|
-
(left, right) => right.updatedAtMs - left.updatedAtMs
|
|
415
|
-
);
|
|
416
|
-
}
|
|
417
472
|
function canExposeConversationTranscript(summary) {
|
|
418
473
|
return canExposeConversationPayload({
|
|
419
474
|
conversationId: summary.conversationId
|
|
@@ -606,7 +661,13 @@ function systemPromptMessage() {
|
|
|
606
661
|
parts: [{ type: "text", text: buildSystemPrompt() }]
|
|
607
662
|
};
|
|
608
663
|
}
|
|
609
|
-
function turnScopedMessages(messages) {
|
|
664
|
+
function turnScopedMessages(messages, turnStartMessageIndex) {
|
|
665
|
+
if (turnStartMessageIndex !== void 0 && turnStartMessageIndex >= 0 && turnStartMessageIndex < messages.length) {
|
|
666
|
+
return {
|
|
667
|
+
messages: messages.slice(turnStartMessageIndex),
|
|
668
|
+
startsAtRunBoundary: turnStartMessageIndex === 0
|
|
669
|
+
};
|
|
670
|
+
}
|
|
610
671
|
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
611
672
|
const record = messages[index];
|
|
612
673
|
if (record.role === "user") {
|
|
@@ -635,82 +696,120 @@ function traceIdFromTranscript(transcript) {
|
|
|
635
696
|
}
|
|
636
697
|
return void 0;
|
|
637
698
|
}
|
|
638
|
-
async function
|
|
639
|
-
const
|
|
640
|
-
|
|
641
|
-
|
|
699
|
+
async function summariesByConversation(conversations) {
|
|
700
|
+
const entries = await Promise.all(
|
|
701
|
+
conversations.map(async (conversation) => {
|
|
702
|
+
const summaries = await listAgentTurnSessionSummariesForConversation(
|
|
703
|
+
conversation.conversationId
|
|
704
|
+
);
|
|
705
|
+
return [conversation.conversationId, summaries];
|
|
706
|
+
})
|
|
642
707
|
);
|
|
708
|
+
return new Map(entries);
|
|
709
|
+
}
|
|
710
|
+
async function reportsFromConversations(args) {
|
|
711
|
+
const summaries = await summariesByConversation(args.conversations);
|
|
712
|
+
const reports = /* @__PURE__ */ new Map();
|
|
713
|
+
for (const conversation of args.conversations) {
|
|
714
|
+
const details = args.detailsByConversationId.get(
|
|
715
|
+
conversation.conversationId
|
|
716
|
+
);
|
|
717
|
+
const conversationSummaries = summaries.get(conversation.conversationId) ?? [];
|
|
718
|
+
const conversationReports = conversationSummaries.length > 0 ? conversationSummaries.map(
|
|
719
|
+
(summary) => applyConversationIndexMetadata({
|
|
720
|
+
conversation,
|
|
721
|
+
details,
|
|
722
|
+
nowMs: args.nowMs,
|
|
723
|
+
report: sessionReportFromSummary(summary, args.nowMs, details)
|
|
724
|
+
})
|
|
725
|
+
) : [sessionReportFromConversation(conversation, args.nowMs, details)];
|
|
726
|
+
reports.set(conversation.conversationId, conversationReports);
|
|
727
|
+
}
|
|
728
|
+
return reports;
|
|
729
|
+
}
|
|
730
|
+
async function readConversationFeed() {
|
|
731
|
+
const nowMs = Date.now();
|
|
732
|
+
const conversations = await listConversationsByActivity({
|
|
733
|
+
limit: CONVERSATION_FEED_LIMIT
|
|
734
|
+
});
|
|
643
735
|
const detailsByConversationId = await getConversationDetailsForIds(
|
|
644
|
-
|
|
736
|
+
conversations.map((conversation) => conversation.conversationId)
|
|
645
737
|
);
|
|
738
|
+
const reportsByConversation = await reportsFromConversations({
|
|
739
|
+
conversations,
|
|
740
|
+
detailsByConversationId,
|
|
741
|
+
nowMs
|
|
742
|
+
});
|
|
646
743
|
return {
|
|
647
|
-
source: "
|
|
744
|
+
source: "conversation_index",
|
|
648
745
|
generatedAt: new Date(nowMs).toISOString(),
|
|
649
|
-
sessions:
|
|
650
|
-
(
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
746
|
+
sessions: conversations.map(
|
|
747
|
+
(conversation) => newestRun(
|
|
748
|
+
reportsByConversation.get(conversation.conversationId) ?? [
|
|
749
|
+
sessionReportFromConversation(
|
|
750
|
+
conversation,
|
|
751
|
+
nowMs,
|
|
752
|
+
detailsByConversationId.get(conversation.conversationId)
|
|
753
|
+
)
|
|
754
|
+
]
|
|
654
755
|
)
|
|
655
756
|
)
|
|
656
757
|
};
|
|
657
758
|
}
|
|
658
|
-
async function
|
|
759
|
+
async function readConversationStatsReport() {
|
|
659
760
|
const nowMs = Date.now();
|
|
660
761
|
const generatedAt = new Date(nowMs).toISOString();
|
|
661
|
-
const
|
|
662
|
-
|
|
663
|
-
);
|
|
664
|
-
const truncated = summaries.length >= DASHBOARD_CONVERSATION_STATS_LIMIT;
|
|
665
|
-
const sampledSummaries = summaries.slice(
|
|
666
|
-
0,
|
|
667
|
-
DASHBOARD_CONVERSATION_STATS_LIMIT
|
|
668
|
-
);
|
|
669
|
-
const reportSummaries = await completeSampledConversationSummaries({
|
|
670
|
-
summaries: sampledSummaries,
|
|
671
|
-
truncated
|
|
762
|
+
const conversations = await listConversationsByActivity({
|
|
763
|
+
limit: CONVERSATION_STATS_LIMIT + 1
|
|
672
764
|
});
|
|
765
|
+
const truncated = conversations.length > CONVERSATION_STATS_LIMIT;
|
|
766
|
+
const sampledConversations = conversations.slice(0, CONVERSATION_STATS_LIMIT);
|
|
673
767
|
const detailsByConversationId = await getConversationDetailsForIds(
|
|
674
|
-
|
|
768
|
+
sampledConversations.map((conversation) => conversation.conversationId)
|
|
769
|
+
);
|
|
770
|
+
const reportsByConversation = await reportsFromConversations({
|
|
771
|
+
conversations: sampledConversations,
|
|
772
|
+
detailsByConversationId,
|
|
773
|
+
nowMs
|
|
774
|
+
});
|
|
775
|
+
const sessions = sampledConversations.flatMap(
|
|
776
|
+
(conversation) => reportsByConversation.get(conversation.conversationId) ?? [
|
|
777
|
+
sessionReportFromConversation(
|
|
778
|
+
conversation,
|
|
779
|
+
nowMs,
|
|
780
|
+
detailsByConversationId.get(conversation.conversationId)
|
|
781
|
+
)
|
|
782
|
+
]
|
|
675
783
|
);
|
|
676
784
|
return buildConversationStatsReport({
|
|
677
785
|
generatedAt,
|
|
678
786
|
nowMs,
|
|
679
|
-
sampleLimit:
|
|
680
|
-
sampleSize:
|
|
681
|
-
sessions
|
|
682
|
-
(summary) => sessionReportFromSummary(
|
|
683
|
-
summary,
|
|
684
|
-
nowMs,
|
|
685
|
-
detailsByConversationId.get(summary.conversationId)
|
|
686
|
-
)
|
|
687
|
-
),
|
|
787
|
+
sampleLimit: CONVERSATION_STATS_LIMIT,
|
|
788
|
+
sampleSize: sampledConversations.length,
|
|
789
|
+
sessions,
|
|
688
790
|
truncated
|
|
689
791
|
});
|
|
690
792
|
}
|
|
691
|
-
async function
|
|
793
|
+
async function readConversationReport(conversationId) {
|
|
692
794
|
const nowMs = Date.now();
|
|
693
|
-
|
|
694
|
-
source: "plugins",
|
|
695
|
-
generatedAt: new Date(nowMs).toISOString(),
|
|
696
|
-
reports: await getAgentPluginOperationalReports(nowMs)
|
|
697
|
-
};
|
|
698
|
-
}
|
|
699
|
-
async function readConversation(conversationId) {
|
|
700
|
-
const [rawSummaries, details] = await Promise.all([
|
|
795
|
+
const [rawSummaries, details, conversation] = await Promise.all([
|
|
701
796
|
listAgentTurnSessionSummariesForConversation(conversationId),
|
|
702
|
-
getConversationDetails(conversationId)
|
|
797
|
+
getConversationDetails(conversationId),
|
|
798
|
+
getConversation({ conversationId })
|
|
703
799
|
]);
|
|
704
800
|
const summaries = rawSummaries.sort(
|
|
705
801
|
(left, right) => left.startedAtMs - right.startedAtMs || left.updatedAtMs - right.updatedAtMs || left.sessionId.localeCompare(right.sessionId)
|
|
706
802
|
);
|
|
707
|
-
const
|
|
803
|
+
const runs = await Promise.all(
|
|
708
804
|
summaries.map(async (summary) => {
|
|
709
805
|
const sessionRecord = await getAgentTurnSessionRecord(
|
|
710
806
|
summary.conversationId,
|
|
711
807
|
summary.sessionId
|
|
712
808
|
);
|
|
713
|
-
const scopedMessages = sessionRecord?.piMessages ? turnScopedMessages(
|
|
809
|
+
const scopedMessages = sessionRecord?.piMessages ? turnScopedMessages(
|
|
810
|
+
sessionRecord.piMessages,
|
|
811
|
+
sessionRecord.turnStartMessageIndex
|
|
812
|
+
) : { messages: [], startsAtRunBoundary: false };
|
|
714
813
|
const canExposeTranscript = canExposeConversationTranscript(summary);
|
|
715
814
|
const normalizedTranscript = scopedMessages.messages.map(
|
|
716
815
|
normalizeTranscriptMessage
|
|
@@ -723,8 +822,8 @@ async function readConversation(conversationId) {
|
|
|
723
822
|
const transcriptMetadata = canExposeTranscript ? void 0 : normalizedTranscript.map(redactTranscriptMessage);
|
|
724
823
|
const traceId = summary.traceId ?? sessionRecord?.traceId ?? (canExposeTranscript ? traceIdFromTranscript(transcript) : void 0);
|
|
725
824
|
const sentryTraceUrl = traceId ? buildSentryTraceUrl(traceId) : void 0;
|
|
726
|
-
|
|
727
|
-
...sessionReportFromSummary(summary,
|
|
825
|
+
const report = {
|
|
826
|
+
...sessionReportFromSummary(summary, nowMs, details),
|
|
728
827
|
...traceId ? { traceId } : {},
|
|
729
828
|
...sentryTraceUrl ? { sentryTraceUrl } : {},
|
|
730
829
|
transcriptAvailable: Boolean(sessionRecord) && canExposeTranscript,
|
|
@@ -736,15 +835,68 @@ async function readConversation(conversationId) {
|
|
|
736
835
|
} : {},
|
|
737
836
|
transcript
|
|
738
837
|
};
|
|
838
|
+
return conversation ? {
|
|
839
|
+
...report,
|
|
840
|
+
...applyConversationIndexMetadata({
|
|
841
|
+
conversation,
|
|
842
|
+
details,
|
|
843
|
+
nowMs,
|
|
844
|
+
report
|
|
845
|
+
})
|
|
846
|
+
} : report;
|
|
739
847
|
})
|
|
740
848
|
);
|
|
741
|
-
const
|
|
742
|
-
|
|
849
|
+
const effectiveRuns = runs.length > 0 || !conversation ? runs : [
|
|
850
|
+
{
|
|
851
|
+
...sessionReportFromConversation(conversation, nowMs, details),
|
|
852
|
+
transcriptAvailable: false,
|
|
853
|
+
transcript: []
|
|
854
|
+
}
|
|
855
|
+
];
|
|
856
|
+
const firstRun = effectiveRuns[0];
|
|
857
|
+
const displayTitle = firstRun?.displayTitle ?? displayTitleFromDetails(conversationId, details) ?? surfaceFallbackLabel(firstRun?.surface ?? "slack");
|
|
743
858
|
return {
|
|
744
859
|
conversationId,
|
|
745
860
|
displayTitle,
|
|
746
|
-
generatedAt:
|
|
747
|
-
|
|
861
|
+
generatedAt: new Date(nowMs).toISOString(),
|
|
862
|
+
runs: effectiveRuns
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// src/reporting.ts
|
|
867
|
+
function readDescriptionText() {
|
|
868
|
+
try {
|
|
869
|
+
const raw = readFileSync(
|
|
870
|
+
path.join(homeDir(), "DESCRIPTION.md"),
|
|
871
|
+
"utf8"
|
|
872
|
+
).trim();
|
|
873
|
+
return raw || void 0;
|
|
874
|
+
} catch {
|
|
875
|
+
return void 0;
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
async function readHealth() {
|
|
879
|
+
const res = GET();
|
|
880
|
+
return await res.json();
|
|
881
|
+
}
|
|
882
|
+
async function readSkills() {
|
|
883
|
+
const skills = await discoverSkills();
|
|
884
|
+
return skills.map((skill) => ({
|
|
885
|
+
name: skill.name,
|
|
886
|
+
pluginProvider: skill.pluginProvider
|
|
887
|
+
}));
|
|
888
|
+
}
|
|
889
|
+
async function readPlugins() {
|
|
890
|
+
return getPluginProviders().map((plugin) => ({
|
|
891
|
+
name: plugin.manifest.name
|
|
892
|
+
}));
|
|
893
|
+
}
|
|
894
|
+
async function readPluginOperationalReports() {
|
|
895
|
+
const nowMs = Date.now();
|
|
896
|
+
return {
|
|
897
|
+
source: "plugins",
|
|
898
|
+
generatedAt: new Date(nowMs).toISOString(),
|
|
899
|
+
reports: await getAgentPluginOperationalReports(nowMs)
|
|
748
900
|
};
|
|
749
901
|
}
|
|
750
902
|
function createJuniorReporting() {
|
|
@@ -766,10 +918,10 @@ function createJuniorReporting() {
|
|
|
766
918
|
},
|
|
767
919
|
getPlugins: readPlugins,
|
|
768
920
|
getSkills: readSkills,
|
|
769
|
-
getSessions:
|
|
770
|
-
getConversationStats:
|
|
921
|
+
getSessions: readConversationFeed,
|
|
922
|
+
getConversationStats: readConversationStatsReport,
|
|
771
923
|
getPluginOperationalReports: readPluginOperationalReports,
|
|
772
|
-
getConversation:
|
|
924
|
+
getConversation: readConversationReport
|
|
773
925
|
};
|
|
774
926
|
}
|
|
775
927
|
export {
|