@sentry/junior 0.96.0 → 0.97.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 (135) hide show
  1. package/dist/{agent-hooks-5URSMY3O.js → agent-hooks-XSUIVRQS.js} +8 -6
  2. package/dist/{reporting → api}/conversations/activity.d.ts +1 -1
  3. package/dist/api/conversations/detail-projection.d.ts +10 -0
  4. package/dist/api/conversations/detail.d.ts +3 -0
  5. package/dist/api/conversations/detail.query.d.ts +3 -0
  6. package/dist/api/conversations/list.d.ts +3 -0
  7. package/dist/api/conversations/list.query.d.ts +94 -0
  8. package/dist/api/conversations/projection.d.ts +14 -0
  9. package/dist/api/conversations/schema.d.ts +683 -0
  10. package/dist/{reporting → api}/conversations/shared.d.ts +1 -5
  11. package/dist/api/conversations/stats.d.ts +3 -0
  12. package/dist/api/conversations/stats.query.d.ts +3 -0
  13. package/dist/api/conversations/subagent.d.ts +3 -0
  14. package/dist/{reporting → api}/conversations/transcript.d.ts +4 -4
  15. package/dist/api/people/list.d.ts +1 -2
  16. package/dist/api/people/list.query.d.ts +3 -4
  17. package/dist/api/people/profile.d.ts +1 -2
  18. package/dist/api/people/profile.query.d.ts +3 -4
  19. package/dist/api/people/schema.d.ts +193 -0
  20. package/dist/api/people/shared.d.ts +31 -9
  21. package/dist/api/schema.d.ts +20 -0
  22. package/dist/api/schema.js +50 -0
  23. package/dist/api-reference.d.ts +4 -3
  24. package/dist/api.d.ts +3 -0
  25. package/dist/api.js +156 -0
  26. package/dist/app.d.ts +1 -4
  27. package/dist/app.js +100 -366
  28. package/dist/build/virtual-config.d.ts +2 -2
  29. package/dist/{catalog-runtime-MSMAYYIK.js → catalog-runtime-PFHVMHDR.js} +3 -2
  30. package/dist/chat/agent/resume.d.ts +3 -1
  31. package/dist/chat/agent/tools.d.ts +2 -1
  32. package/dist/chat/config.d.ts +1 -8
  33. package/dist/chat/conversations/history.d.ts +241 -27
  34. package/dist/chat/conversations/legacy-advisor-session.d.ts +7 -0
  35. package/dist/chat/conversations/legacy-import.d.ts +2 -2
  36. package/dist/chat/conversations/projection.d.ts +19 -3
  37. package/dist/chat/conversations/sql/legacy-history-import.d.ts +1 -1
  38. package/dist/chat/conversations/sql/store.d.ts +15 -9
  39. package/dist/chat/conversations/store.d.ts +9 -4
  40. package/dist/chat/model-profile.d.ts +14 -0
  41. package/dist/chat/pi/transcript.d.ts +5 -3
  42. package/dist/chat/resource-events/store.d.ts +2 -2
  43. package/dist/chat/services/context-compaction.d.ts +15 -0
  44. package/dist/chat/services/turn-result.d.ts +1 -0
  45. package/dist/chat/services/turn-session-record.d.ts +6 -6
  46. package/dist/chat/state/turn-session.d.ts +16 -5
  47. package/dist/chat/task-execution/store.d.ts +2 -2
  48. package/dist/chat/tools/handoff/tool.d.ts +45 -0
  49. package/dist/chat/tools/types.d.ts +7 -2
  50. package/dist/chat/usage.d.ts +25 -25
  51. package/dist/{chunk-RITD7E5L.js → chunk-35BNLDOJ.js} +338 -577
  52. package/dist/{chunk-PMNN6ST4.js → chunk-3ECW2K2O.js} +7 -2
  53. package/dist/{chunk-5C6XTTFI.js → chunk-3F4OPKGF.js} +89 -55
  54. package/dist/chunk-3V7OFJZC.js +264 -0
  55. package/dist/chunk-4ZNGQH7C.js +25 -0
  56. package/dist/{chunk-S5KZ5LPG.js → chunk-5HLDPVYS.js} +14 -447
  57. package/dist/{chunk-DZENG5ML.js → chunk-6U26PYI5.js} +24 -14
  58. package/dist/chunk-AUUCAFDE.js +546 -0
  59. package/dist/{chunk-6APU57E6.js → chunk-B3IT5END.js} +41 -260
  60. package/dist/chunk-CEA3A3UA.js +86 -0
  61. package/dist/{chunk-YDT7SNCN.js → chunk-D5KZYS4T.js} +1 -1
  62. package/dist/{chunk-YDVEH3SB.js → chunk-EEZCY5WX.js} +78 -28
  63. package/dist/{chunk-2NAZZPPS.js → chunk-EOWXRY5E.js} +1 -1
  64. package/dist/{chunk-GQ3GK22G.js → chunk-FBHZJFHI.js} +2 -2
  65. package/dist/{chunk-CZEX6KIQ.js → chunk-G3E7SCME.js} +0 -23
  66. package/dist/chunk-GUAP3H7O.js +319 -0
  67. package/dist/{chunk-BYZWPRJA.js → chunk-H66UCBPW.js} +6 -4
  68. package/dist/{chunk-L7CDEIVJ.js → chunk-LMJPRYHP.js} +4 -2
  69. package/dist/chunk-LUAE4MGW.js +199 -0
  70. package/dist/{chunk-GY34VGP5.js → chunk-LVA4I4H7.js} +3 -1
  71. package/dist/chunk-LVUKF5CE.js +63 -0
  72. package/dist/{chunk-AAZXTVJT.js → chunk-NG2AV7IB.js} +2 -72
  73. package/dist/chunk-ON6YKIQ7.js +260 -0
  74. package/dist/chunk-PDO5BLNM.js +22 -0
  75. package/dist/chunk-QNVHH5PU.js +20 -0
  76. package/dist/{chunk-P3YFQLEN.js → chunk-T533JLM3.js} +8 -10
  77. package/dist/chunk-UAMKJPDF.js +11 -0
  78. package/dist/chunk-VFH4ZKZT.js +66 -0
  79. package/dist/{chunk-UKHVMPX5.js → chunk-WGXMTFNU.js} +15 -17
  80. package/dist/{chunk-MNTINMWR.js → chunk-WJOSZOON.js} +184 -300
  81. package/dist/chunk-WTYEKPXU.js +451 -0
  82. package/dist/{chunk-IYM2SIVG.js → chunk-XIRPQQVE.js} +605 -363
  83. package/dist/cli/chat.js +24 -21
  84. package/dist/cli/check.js +4 -3
  85. package/dist/cli/init.js +12 -0
  86. package/dist/cli/plugins.js +12 -9
  87. package/dist/cli/snapshot-warmup.js +8 -6
  88. package/dist/cli/upgrade.js +265 -137
  89. package/dist/db/db.d.ts +4 -0
  90. package/dist/db/schema/conversations.d.ts +128 -0
  91. package/dist/db/schema.d.ts +129 -60
  92. package/dist/{db-2D6RMG67.js → db-BWXYIKFQ.js} +3 -3
  93. package/dist/detail-GA6NXWQJ.js +43 -0
  94. package/dist/discovery-RMHYSRL7.js +43 -0
  95. package/dist/handlers/health.d.ts +3 -0
  96. package/dist/instrumentation.js +1 -1
  97. package/dist/{legacy-import-VZ2MEGP7.js → legacy-import-3NQJVGQL.js} +4 -4
  98. package/dist/{api/people/list.js → list-A3WFRIYX.js} +26 -16
  99. package/dist/list-UXJI5Q5X.js +27 -0
  100. package/dist/nitro.d.ts +1 -1
  101. package/dist/nitro.js +2 -1
  102. package/dist/plugin-conversations-DDNGC35A.js +36 -0
  103. package/dist/{api/people/profile.js → profile-4RYDXWTF.js} +33 -18
  104. package/dist/reporting/plugin-conversations.d.ts +3 -0
  105. package/dist/reporting-schema.d.ts +140 -0
  106. package/dist/reporting.d.ts +12 -81
  107. package/dist/{runner-JZBRQP4F.js → runner-JALSYH2P.js} +17 -12
  108. package/dist/skills-MAZXZ2KE.js +26 -0
  109. package/dist/stats-XKXH3PTY.js +217 -0
  110. package/dist/subagent-TZ55XY37.js +48 -0
  111. package/dist/usage-schema.d.ts +25 -0
  112. package/dist/{validation-6XOOQGME.js → validation-FMEAEKCY.js} +4 -3
  113. package/migrations/0000_initial.sql +115 -0
  114. package/migrations/0001_conversation_metrics.sql +4 -0
  115. package/migrations/README.md +12 -30
  116. package/migrations/meta/0000_snapshot.json +1 -33
  117. package/migrations/meta/0001_snapshot.json +972 -0
  118. package/migrations/meta/_journal.json +9 -2
  119. package/package.json +14 -18
  120. package/dist/api/people/types.d.ts +0 -85
  121. package/dist/chat/advisor-request.d.ts +0 -4
  122. package/dist/chat/conversations/sql/migrations.d.ts +0 -22
  123. package/dist/chat/tools/advisor/session-store.d.ts +0 -9
  124. package/dist/chat/tools/advisor/tool.d.ts +0 -72
  125. package/dist/chunk-RAIB6WSG.js +0 -142
  126. package/dist/chunk-WM6KHPBQ.js +0 -48
  127. package/dist/db/schema/migrations.d.ts +0 -58
  128. package/dist/reporting/conversations/context.d.ts +0 -8
  129. package/dist/reporting/conversations/details.d.ts +0 -6
  130. package/dist/reporting/conversations/index.d.ts +0 -4
  131. package/dist/reporting/conversations/statistics.d.ts +0 -4
  132. package/dist/reporting/conversations/summaries.d.ts +0 -20
  133. package/dist/reporting/conversations/types.d.ts +0 -191
  134. package/dist/reporting/conversations.d.ts +0 -1
  135. package/dist/reporting.js +0 -1155
package/dist/reporting.js DELETED
@@ -1,1155 +0,0 @@
1
- import {
2
- GET,
3
- buildSentryConversationUrl,
4
- buildSentryTraceUrl,
5
- formatSlackConversationRedactedLabel,
6
- resolveSlackConversationContextFromThreadId
7
- } from "./chunk-AAZXTVJT.js";
8
- import {
9
- unwrapAdvisorRequest
10
- } from "./chunk-WM6KHPBQ.js";
11
- import {
12
- listAgentTurnSessionSummariesForConversation,
13
- listAgentTurnSessionSummariesForConversations,
14
- loadProjection,
15
- projectSteps
16
- } from "./chunk-MNTINMWR.js";
17
- import "./chunk-5C6XTTFI.js";
18
- import {
19
- getPluginOperationalReports
20
- } from "./chunk-S5KZ5LPG.js";
21
- import "./chunk-2NAZZPPS.js";
22
- import "./chunk-CZEX6KIQ.js";
23
- import {
24
- getAgentStepStore,
25
- getConversationMessageStore,
26
- getConversationStore
27
- } from "./chunk-RITD7E5L.js";
28
- import {
29
- unwrapCurrentInstruction
30
- } from "./chunk-YDVEH3SB.js";
31
- import {
32
- canExposeConversationPayload,
33
- parseSlackThreadId,
34
- resolveConversationPrivacy
35
- } from "./chunk-GY34VGP5.js";
36
- import "./chunk-B7PUBQQM.js";
37
- import {
38
- discoverSkills
39
- } from "./chunk-PMNN6ST4.js";
40
- import {
41
- pluginCatalogRuntime
42
- } from "./chunk-L7CDEIVJ.js";
43
- import "./chunk-I35CTGPO.js";
44
- import {
45
- isRecord
46
- } from "./chunk-OB42YVAE.js";
47
- import "./chunk-6GWA276C.js";
48
- import {
49
- homeDir
50
- } from "./chunk-6APU57E6.js";
51
- import "./chunk-7FBGKXPE.js";
52
- import "./chunk-MLKGABMK.js";
53
-
54
- // src/reporting.ts
55
- import { readFileSync } from "fs";
56
- import path from "path";
57
-
58
- // src/reporting/conversations/context.ts
59
- function conversationStore(options = {}) {
60
- return options.conversationStore ?? getConversationStore();
61
- }
62
-
63
- // src/reporting/conversations/transcript.ts
64
- var SAFE_METADATA_KEY_LIMIT = 20;
65
- function textPart(text) {
66
- return { type: "text", text };
67
- }
68
- function recordField(value, names) {
69
- for (const name of names) {
70
- if (value[name] !== void 0) {
71
- return value[name];
72
- }
73
- }
74
- return void 0;
75
- }
76
- function normalizeTranscriptPart(part, options = {}) {
77
- const displayText = (text) => {
78
- if (options.unwrapCurrentTask) {
79
- const instruction = unwrapCurrentInstruction(text);
80
- if (instruction !== void 0) return instruction;
81
- }
82
- if (options.unwrapAdvisorTask) return unwrapAdvisorRequest(text) ?? text;
83
- return text;
84
- };
85
- if (typeof part === "string") {
86
- return textPart(displayText(part));
87
- }
88
- if (!isRecord(part)) {
89
- return { type: "unknown", output: part };
90
- }
91
- const rawType = typeof part.type === "string" ? part.type : "unknown";
92
- if (rawType === "text") {
93
- const text = recordField(part, ["text", "content"]);
94
- return textPart(
95
- typeof text === "string" ? displayText(text) : JSON.stringify(text) ?? ""
96
- );
97
- }
98
- if (rawType === "toolCall") {
99
- return {
100
- type: "tool_call",
101
- ...typeof part.id === "string" ? { id: part.id } : {},
102
- ...typeof part.name === "string" ? { name: part.name } : {},
103
- input: recordField(part, ["arguments", "input", "args"])
104
- };
105
- }
106
- if (rawType === "toolResult") {
107
- return {
108
- type: "tool_result",
109
- ...typeof part.id === "string" ? { id: part.id } : {},
110
- ...typeof part.name === "string" ? { name: part.name } : {},
111
- output: recordField(part, ["result", "output", "content"])
112
- };
113
- }
114
- if (rawType === "thinking") {
115
- return {
116
- type: "thinking",
117
- output: recordField(part, ["thinking", "text", "content", "output"])
118
- };
119
- }
120
- return {
121
- type: "unknown",
122
- ...rawType !== "unknown" ? { sourceType: rawType } : {},
123
- output: part
124
- };
125
- }
126
- function normalizeToolResultMessage(record) {
127
- const content = record.content;
128
- let output = content;
129
- if (Array.isArray(content) && content.length === 1 && isRecord(content[0])) {
130
- const extracted = recordField(content[0], [
131
- "text",
132
- "content",
133
- "output",
134
- "result"
135
- ]);
136
- output = extracted !== void 0 ? extracted : content;
137
- }
138
- return {
139
- type: "tool_result",
140
- ...typeof record.toolCallId === "string" ? { id: record.toolCallId } : {},
141
- ...typeof record.name === "string" ? { name: record.name } : typeof record.toolName === "string" ? { name: record.toolName } : {},
142
- output
143
- };
144
- }
145
- function normalizeTranscriptMessage(message, options = {}) {
146
- const record = message;
147
- const content = record.content;
148
- const role = transcriptRole(record.role);
149
- return {
150
- role,
151
- ...typeof record.timestamp === "number" ? { timestamp: record.timestamp } : {},
152
- parts: role === "toolResult" ? [normalizeToolResultMessage(record)] : Array.isArray(content) ? content.map(
153
- (part) => normalizeTranscriptPart(part, {
154
- unwrapAdvisorTask: options.unwrapAdvisorTask && role === "user",
155
- unwrapCurrentTask: role === "user"
156
- })
157
- ) : [
158
- normalizeTranscriptPart(content, {
159
- unwrapAdvisorTask: options.unwrapAdvisorTask && role === "user",
160
- unwrapCurrentTask: role === "user"
161
- })
162
- ]
163
- };
164
- }
165
- function transcriptRole(role) {
166
- return role === "assistant" || role === "system" || role === "tool" || role === "toolResult" || role === "user" ? role : "unknown";
167
- }
168
- function serializedChars(value) {
169
- if (typeof value === "string") return value.length;
170
- return JSON.stringify(value)?.length ?? 0;
171
- }
172
- function serializedBytes(value) {
173
- const serialized = typeof value === "string" ? value : JSON.stringify(value);
174
- return new TextEncoder().encode(serialized ?? "").byteLength;
175
- }
176
- function payloadType(value) {
177
- return Array.isArray(value) ? "array" : typeof value;
178
- }
179
- function payloadKeys(value) {
180
- if (!value || typeof value !== "object" || Array.isArray(value)) {
181
- return void 0;
182
- }
183
- const keys = Object.keys(value).slice(
184
- 0,
185
- SAFE_METADATA_KEY_LIMIT
186
- );
187
- return keys.length > 0 ? keys : void 0;
188
- }
189
- function redactedPayloadFields(prefix, value) {
190
- const keys = payloadKeys(value);
191
- return {
192
- [`${prefix}Type`]: payloadType(value),
193
- [`${prefix}SizeBytes`]: serializedBytes(value),
194
- [`${prefix}SizeChars`]: serializedChars(value),
195
- ...keys ? { [`${prefix}Keys`]: keys } : {}
196
- };
197
- }
198
- function redactTranscriptPart(part) {
199
- if (part.type === "text") {
200
- return {
201
- type: "text",
202
- redacted: true,
203
- bytes: serializedBytes(part.text ?? ""),
204
- chars: serializedChars(part.text ?? "")
205
- };
206
- }
207
- if (part.type === "thinking") {
208
- return {
209
- type: "thinking",
210
- redacted: true,
211
- ...redactedPayloadFields("output", part.output)
212
- };
213
- }
214
- if (part.type === "tool_call") {
215
- return {
216
- type: "tool_call",
217
- redacted: true,
218
- ...part.id ? { id: part.id } : {},
219
- ...part.name ? { name: part.name } : {},
220
- ...redactedPayloadFields("input", part.input)
221
- };
222
- }
223
- if (part.type === "tool_result") {
224
- return {
225
- type: "tool_result",
226
- redacted: true,
227
- ...part.id ? { id: part.id } : {},
228
- ...part.name ? { name: part.name } : {},
229
- ...redactedPayloadFields("output", part.output)
230
- };
231
- }
232
- return {
233
- type: "unknown",
234
- redacted: true,
235
- ...part.sourceType ? { sourceType: part.sourceType } : {},
236
- ...redactedPayloadFields("output", part.output ?? part.input ?? part.text)
237
- };
238
- }
239
- function redactTranscriptMessage(message) {
240
- return {
241
- role: message.role,
242
- ...typeof message.timestamp === "number" ? { timestamp: message.timestamp } : {},
243
- parts: message.parts.map(redactTranscriptPart)
244
- };
245
- }
246
- function isConversationMessageRole(role) {
247
- return role === "user" || role === "assistant";
248
- }
249
- function hasTextPart(message) {
250
- return message.parts.some((part) => {
251
- if (part.type !== "text") return false;
252
- if (part.redacted) return true;
253
- return typeof part.text === "string" && part.text.trim().length > 0;
254
- });
255
- }
256
- function isConversationMessage(message) {
257
- if (!isConversationMessageRole(message.role)) return false;
258
- if (message.role === "assistant") return hasTextPart(message);
259
- return message.parts.length > 0;
260
- }
261
- function countConversationMessages(transcript) {
262
- return transcript.filter(isConversationMessage).length;
263
- }
264
- function traceIdFromTranscript(transcript) {
265
- for (const message of transcript) {
266
- for (const part of message.parts) {
267
- const text = part.text ?? (typeof part.output === "string" ? part.output : typeof part.input === "string" ? part.input : void 0);
268
- const match = text?.match(
269
- /\btrace[_-]?id["']?\s*[:=]\s*["']?([a-f0-9]{16,32})\b/i
270
- );
271
- if (match?.[1]) {
272
- return match[1];
273
- }
274
- }
275
- }
276
- return void 0;
277
- }
278
- function subagentTranscriptReport(activity, options = {}) {
279
- return {
280
- type: "subagent",
281
- ...options.subagentConversationId ? { subagentConversationId: options.subagentConversationId } : {},
282
- createdAt: activity.createdAt,
283
- id: activity.id,
284
- ...activity.modelId ? { modelId: activity.modelId } : {},
285
- status: activity.status,
286
- ...options.subagentSentryConversationUrl ? { subagentSentryConversationUrl: options.subagentSentryConversationUrl } : {},
287
- subagentKind: activity.subagentKind,
288
- transcript: options.transcript ?? [],
289
- transcriptAvailable: Boolean(options.transcript?.length),
290
- ...activity.endedAt ? { endedAt: activity.endedAt } : {},
291
- ...activity.outcome ? { outcome: activity.outcome } : {},
292
- ...activity.parentToolCallId ? { parentToolCallId: activity.parentToolCallId } : {},
293
- ...activity.reasoningLevel ? { reasoningLevel: activity.reasoningLevel } : {},
294
- ...options.transcriptMessageCount !== void 0 ? { transcriptMessageCount: options.transcriptMessageCount } : {},
295
- ...options.transcriptRedacted ? { transcriptRedacted: options.transcriptRedacted } : {},
296
- ...options.transcriptRedactionReason ? { transcriptRedactionReason: options.transcriptRedactionReason } : {},
297
- ...options.transcriptExpired ? { transcriptExpired: options.transcriptExpired } : {},
298
- ...options.transcriptExpiredAt ? { transcriptExpiredAt: options.transcriptExpiredAt } : {},
299
- ...options.unavailableReason ? { unavailableReason: options.unavailableReason } : {}
300
- };
301
- }
302
-
303
- // src/reporting/conversations/activity.ts
304
- function toolResultStatuses(messages) {
305
- const statuses = /* @__PURE__ */ new Map();
306
- for (const message of messages) {
307
- const record = message;
308
- if (record.role !== "toolResult" || typeof record.toolCallId !== "string") {
309
- continue;
310
- }
311
- statuses.set(record.toolCallId, record.isError ? "error" : "completed");
312
- }
313
- return statuses;
314
- }
315
- function activityPayloadFields(args, canExposePayload) {
316
- if (args === void 0) {
317
- return {};
318
- }
319
- return canExposePayload ? { args } : { redacted: true, ...redactedPayloadFields("input", args) };
320
- }
321
- function buildConversationActivityFromSteps(args) {
322
- const toolStatuses = toolResultStatuses(args.messages);
323
- const subagentEnds = /* @__PURE__ */ new Map();
324
- const subagentsByToolCallId = /* @__PURE__ */ new Map();
325
- const orphanSubagents = [];
326
- for (const step of args.steps) {
327
- if (step.entry.type === "subagent_ended") {
328
- subagentEnds.set(
329
- step.entry.subagentInvocationId,
330
- step
331
- );
332
- }
333
- }
334
- for (const step of args.steps) {
335
- if (step.entry.type !== "subagent_started") {
336
- continue;
337
- }
338
- const start = step;
339
- const parentStatus = start.entry.parentToolCallId ? toolStatuses.get(start.entry.parentToolCallId) : void 0;
340
- const activity = subagentActivityFromSteps(
341
- start,
342
- subagentEnds.get(start.entry.subagentInvocationId),
343
- { canExposeTranscript: args.canExposePayload, parentStatus }
344
- );
345
- if (start.entry.parentToolCallId) {
346
- subagentsByToolCallId.set(start.entry.parentToolCallId, [
347
- ...subagentsByToolCallId.get(start.entry.parentToolCallId) ?? [],
348
- activity
349
- ]);
350
- continue;
351
- }
352
- orphanSubagents.push(activity);
353
- }
354
- const rows = [];
355
- for (const step of args.steps) {
356
- if (step.entry.type !== "tool_execution_started") {
357
- continue;
358
- }
359
- rows.push({
360
- type: "tool_execution",
361
- id: step.entry.toolCallId,
362
- toolCallId: step.entry.toolCallId,
363
- toolName: step.entry.toolName,
364
- createdAt: new Date(step.createdAtMs).toISOString(),
365
- status: toolStatuses.get(step.entry.toolCallId) ?? "running",
366
- subagents: subagentsByToolCallId.get(step.entry.toolCallId) ?? [],
367
- ...activityPayloadFields(step.entry.args, args.canExposePayload)
368
- });
369
- }
370
- return [...rows, ...orphanSubagents].sort(
371
- (left, right) => Date.parse(left.createdAt) - Date.parse(right.createdAt) || left.id.localeCompare(right.id)
372
- );
373
- }
374
- function subagentActivityFromSteps(start, end, options = {}) {
375
- return {
376
- type: "subagent",
377
- id: start.entry.subagentInvocationId,
378
- subagentKind: start.entry.subagentKind,
379
- ...start.entry.modelId ? { modelId: start.entry.modelId } : {},
380
- ...start.entry.parentToolCallId ? { parentToolCallId: start.entry.parentToolCallId } : {},
381
- ...start.entry.reasoningLevel ? { reasoningLevel: start.entry.reasoningLevel } : {},
382
- createdAt: new Date(start.createdAtMs).toISOString(),
383
- ...end ? {
384
- endedAt: new Date(end.createdAtMs).toISOString(),
385
- outcome: end.entry.outcome,
386
- status: end.entry.outcome,
387
- // Every subagent is a child conversation whose transcript loads on
388
- // demand; expose the affordance only when the parent is public.
389
- ...options.canExposeTranscript ? { transcriptAvailable: true } : {}
390
- } : { status: options.parentStatus ?? "running" }
391
- };
392
- }
393
-
394
- // src/reporting/conversations/shared.ts
395
- function reportTime(value) {
396
- const time = Date.parse(value);
397
- return Number.isFinite(time) ? time : void 0;
398
- }
399
- function slackStatsLocationLabel(input) {
400
- const channelId = input.channel;
401
- if (!channelId) return void 0;
402
- if (input.channelNameRedacted && input.channelName) {
403
- return input.channelName;
404
- }
405
- const name = input.channelName?.replace(/^#/, "");
406
- if (channelId.startsWith("D")) return "Direct Message";
407
- if (channelId.startsWith("C")) return name ? `#${name}` : "Public Channel";
408
- if (channelId.startsWith("G")) {
409
- if (name?.startsWith("mpdm-")) return "Group DM";
410
- return "Private Channel";
411
- }
412
- return name || channelId;
413
- }
414
- function surfaceFallbackLabel(surface) {
415
- if (surface === "scheduler") return "Scheduler";
416
- if (surface === "api") return "API";
417
- if (surface === "internal") return "Internal";
418
- return "Conversation";
419
- }
420
- function newestRun(runs) {
421
- return [...runs].sort(
422
- (left, right) => (reportTime(right.lastSeenAt) ?? 0) - (reportTime(left.lastSeenAt) ?? 0) || right.id.localeCompare(left.id)
423
- )[0];
424
- }
425
-
426
- // src/reporting/conversations/summaries.ts
427
- var HUNG_TURN_PROGRESS_MS = 5 * 60 * 1e3;
428
- var PRIVATE_CONVERSATION_LABEL = "Private Conversation";
429
- var CONVERSATION_FEED_LIMIT = 50;
430
- function privateConversationLabel(slackConversation) {
431
- if (!slackConversation) {
432
- return PRIVATE_CONVERSATION_LABEL;
433
- }
434
- return slackConversation.visibility === "private" ? formatSlackConversationRedactedLabel(slackConversation) ?? PRIVATE_CONVERSATION_LABEL : PRIVATE_CONVERSATION_LABEL;
435
- }
436
- function surfaceFromConversationId(conversationId) {
437
- if (parseSlackThreadId(conversationId)) return "slack";
438
- if (conversationId.startsWith("scheduler:")) return "scheduler";
439
- if (conversationId.startsWith("api:")) return "api";
440
- return "internal";
441
- }
442
- function surfaceFromSource(source, conversationId) {
443
- if (source === "slack" || source === "api" || source === "scheduler") {
444
- return source;
445
- }
446
- return surfaceFromConversationId(conversationId);
447
- }
448
- function actorIdentityReport(actor) {
449
- if (!actor) return void 0;
450
- if ("name" in actor) {
451
- return { fullName: actor.name };
452
- }
453
- if ("userId" in actor) {
454
- const identity2 = {
455
- ...actor.email !== void 0 ? { email: actor.email } : {},
456
- ...actor.fullName !== void 0 ? { fullName: actor.fullName } : {},
457
- ...actor.platform === "slack" ? { slackUserId: actor.userId } : {},
458
- ...actor.platform === "slack" && actor.userName !== void 0 ? { slackUserName: actor.userName } : {}
459
- };
460
- return Object.keys(identity2).length > 0 ? identity2 : void 0;
461
- }
462
- const identity = {
463
- ...actor.email !== void 0 ? { email: actor.email } : {},
464
- ...actor.fullName !== void 0 ? { fullName: actor.fullName } : {},
465
- ...actor.slackUserId !== void 0 ? { slackUserId: actor.slackUserId } : {},
466
- ...actor.slackUserName !== void 0 ? { slackUserName: actor.slackUserName } : {}
467
- };
468
- return Object.keys(identity).length > 0 ? identity : void 0;
469
- }
470
- function statusFromConversation(conversation, fallback, nowMs) {
471
- if (fallback) {
472
- return fallback;
473
- }
474
- if (conversation.execution.status === "idle") {
475
- return "completed";
476
- }
477
- if (conversation.execution.status === "failed") {
478
- return "failed";
479
- }
480
- const updatedAtMs = conversation.execution.updatedAtMs ?? conversation.updatedAtMs;
481
- if (conversation.execution.status === "running" && nowMs - updatedAtMs > HUNG_TURN_PROGRESS_MS) {
482
- return "hung";
483
- }
484
- return "active";
485
- }
486
- function titleFromConversation(args) {
487
- const slackThread = parseSlackThreadId(args.conversation.conversationId);
488
- const effectiveChannelName = args.conversation.channelName;
489
- const slackConversation = resolveSlackConversationContextFromThreadId({
490
- threadId: args.conversation.conversationId,
491
- channelName: effectiveChannelName
492
- });
493
- const privateLabel = resolveConversationPrivacy({
494
- conversationId: args.conversation.conversationId,
495
- visibility: args.conversation.visibility
496
- }) !== "public" ? privateConversationLabel(slackConversation) : void 0;
497
- return privateLabel ?? args.conversation.title ?? slackStatsLocationLabel({
498
- channel: slackThread?.channelId,
499
- channelName: effectiveChannelName
500
- }) ?? surfaceFallbackLabel(args.surface);
501
- }
502
- function channelNameFromConversation(conversation) {
503
- const effectiveChannelName = conversation.channelName;
504
- const slackThread = parseSlackThreadId(conversation.conversationId);
505
- if (!effectiveChannelName && !slackThread) {
506
- return void 0;
507
- }
508
- const slackConversation = resolveSlackConversationContextFromThreadId({
509
- threadId: conversation.conversationId,
510
- channelName: effectiveChannelName
511
- });
512
- if (resolveConversationPrivacy({
513
- conversationId: conversation.conversationId,
514
- visibility: conversation.visibility
515
- }) !== "public") {
516
- return privateConversationLabel(slackConversation);
517
- }
518
- return effectiveChannelName;
519
- }
520
- function channelNameRedactedFromConversation(conversation) {
521
- const effectiveChannelName = conversation.channelName;
522
- const slackThread = parseSlackThreadId(conversation.conversationId);
523
- if (!effectiveChannelName && !slackThread) {
524
- return false;
525
- }
526
- return resolveConversationPrivacy({
527
- conversationId: conversation.conversationId,
528
- visibility: conversation.visibility
529
- }) !== "public";
530
- }
531
- function sessionReportFromConversation(conversation, nowMs) {
532
- const surface = surfaceFromSource(
533
- conversation.source,
534
- conversation.conversationId
535
- );
536
- const actorIdentity = actorIdentityReport(conversation.actor);
537
- const slackThread = parseSlackThreadId(conversation.conversationId);
538
- const channelName = channelNameFromConversation(conversation);
539
- const channelNameRedacted = channelNameRedactedFromConversation(conversation);
540
- return {
541
- conversationId: conversation.conversationId,
542
- cumulativeDurationMs: 0,
543
- displayTitle: titleFromConversation({ conversation, surface }),
544
- id: conversation.conversationId,
545
- lastProgressAt: new Date(
546
- conversation.execution.updatedAtMs ?? conversation.updatedAtMs
547
- ).toISOString(),
548
- lastSeenAt: new Date(conversation.lastActivityAtMs).toISOString(),
549
- startedAt: new Date(conversation.createdAtMs).toISOString(),
550
- status: statusFromConversation(conversation, void 0, nowMs),
551
- surface,
552
- ...actorIdentity ? { actorIdentity } : {},
553
- ...slackThread ? { channel: slackThread.channelId } : {},
554
- ...channelName ? { channelName } : {},
555
- ...channelNameRedacted ? { channelNameRedacted: true } : {}
556
- };
557
- }
558
- function statusFromTurnSummary(summary, nowMs) {
559
- if (summary.state === "running" && nowMs - summary.lastProgressAtMs > HUNG_TURN_PROGRESS_MS) {
560
- return "hung";
561
- }
562
- if (summary.state === "running" || summary.state === "awaiting_resume") {
563
- return "active";
564
- }
565
- if (summary.state === "abandoned") {
566
- return "superseded";
567
- }
568
- return summary.state;
569
- }
570
- function sessionReportFromTurnSummary(conversation, summary, nowMs) {
571
- const base = sessionReportFromConversation(conversation, nowMs);
572
- if (summary.actor !== void 0) {
573
- const actorIdentity = actorIdentityReport(summary.actor);
574
- if (actorIdentity) {
575
- base.actorIdentity = actorIdentity;
576
- } else {
577
- delete base.actorIdentity;
578
- }
579
- }
580
- return {
581
- ...base,
582
- cumulativeDurationMs: summary.cumulativeDurationMs,
583
- ...summary.cumulativeUsage ? { cumulativeUsage: summary.cumulativeUsage } : {},
584
- id: summary.sessionId,
585
- lastProgressAt: new Date(summary.lastProgressAtMs).toISOString(),
586
- lastSeenAt: new Date(summary.updatedAtMs).toISOString(),
587
- startedAt: new Date(summary.startedAtMs).toISOString(),
588
- status: statusFromTurnSummary(summary, nowMs),
589
- surface: summary.surface ?? base.surface,
590
- ...summary.state === "completed" ? { completedAt: new Date(summary.updatedAtMs).toISOString() } : {},
591
- ...summary.traceId ? { traceId: summary.traceId } : {}
592
- };
593
- }
594
- async function reportsFromConversations(args) {
595
- const reports = /* @__PURE__ */ new Map();
596
- for (const conversation of args.conversations) {
597
- reports.set(conversation.conversationId, [
598
- sessionReportFromConversation(conversation, args.nowMs)
599
- ]);
600
- }
601
- return reports;
602
- }
603
- async function readConversationFeed(options = {}) {
604
- const store = conversationStore(options);
605
- const nowMs = Date.now();
606
- const conversations = await store.listByActivity({
607
- limit: CONVERSATION_FEED_LIMIT
608
- });
609
- const reportsByConversation = await reportsFromConversations({
610
- conversations,
611
- nowMs
612
- });
613
- return {
614
- source: "conversation_index",
615
- generatedAt: new Date(nowMs).toISOString(),
616
- conversations: conversations.map(
617
- (conversation) => newestRun(
618
- reportsByConversation.get(conversation.conversationId) ?? [
619
- sessionReportFromConversation(conversation, nowMs)
620
- ]
621
- )
622
- )
623
- };
624
- }
625
- async function listRecentConversationSummaries(options = {}) {
626
- const store = conversationStore(options);
627
- const nowMs = Date.now();
628
- const limit = Math.max(0, Math.min(100, Math.floor(options.limit ?? 25)));
629
- const conversations = await store.listByActivity({
630
- limit
631
- });
632
- const reportsByConversation = await reportsFromConversations({
633
- conversations,
634
- nowMs
635
- });
636
- return conversations.map((conversation) => {
637
- const surface = surfaceFromSource(
638
- conversation.source,
639
- conversation.conversationId
640
- );
641
- const channelName = channelNameFromConversation(conversation);
642
- const channelNameRedacted = channelNameRedactedFromConversation(conversation);
643
- const report = newestRun(
644
- reportsByConversation.get(conversation.conversationId) ?? [
645
- sessionReportFromConversation(conversation, nowMs)
646
- ]
647
- );
648
- return {
649
- conversationId: conversation.conversationId,
650
- displayTitle: titleFromConversation({ conversation, surface }),
651
- lastActivityAt: new Date(conversation.lastActivityAtMs).toISOString(),
652
- lastUpdatedAt: new Date(
653
- conversation.execution.updatedAtMs ?? conversation.updatedAtMs
654
- ).toISOString(),
655
- status: report.status,
656
- ...channelName ? { channelName } : {},
657
- ...channelNameRedacted ? { channelNameRedacted: true } : {},
658
- ...conversation.source ? { source: conversation.source } : {}
659
- };
660
- });
661
- }
662
-
663
- // src/reporting/conversations/details.ts
664
- async function currentRunContent(args) {
665
- const steps = await args.stepStore.loadCurrentEpoch(args.conversationId);
666
- const messages = projectSteps(steps).messages;
667
- const transcript = messages.length > 0 ? messages.map((message) => normalizeTranscriptMessage(message)) : (await args.messageStore.list(args.conversationId)).map(
668
- visibleMessageTranscript
669
- );
670
- return {
671
- activity: buildConversationActivityFromSteps({
672
- canExposePayload: args.canExposePayload,
673
- steps,
674
- messages
675
- }),
676
- transcript
677
- };
678
- }
679
- function visibleMessageTranscript(message) {
680
- return {
681
- role: message.role,
682
- timestamp: message.createdAtMs,
683
- parts: [{ type: "text", text: message.text }]
684
- };
685
- }
686
- async function readConversationReport(conversationId, options = {}) {
687
- const store = conversationStore(options);
688
- const nowMs = Date.now();
689
- const conversation = await store.get({ conversationId });
690
- const turnSummaries = conversation ? await listAgentTurnSessionSummariesForConversation(conversationId) : [];
691
- const currentTurnSummary = conversation ? turnSummaries.find(
692
- (summary) => summary.sessionId === conversation.execution.runId
693
- ) : void 0;
694
- const stepStore = getAgentStepStore();
695
- const messageStore = options.messageStore ?? getConversationMessageStore();
696
- const transcriptPurgedAtMs = conversation?.transcriptPurgedAtMs;
697
- const transcriptExpiredAt = transcriptPurgedAtMs !== void 0 ? new Date(transcriptPurgedAtMs).toISOString() : void 0;
698
- const canExposeSqlContent = conversation !== void 0 && canExposeConversationPayload({
699
- conversationId,
700
- visibility: conversation.visibility
701
- });
702
- const currentContent = conversation && transcriptPurgedAtMs === void 0 ? await currentRunContent({
703
- conversationId,
704
- messageStore,
705
- stepStore,
706
- canExposePayload: canExposeSqlContent
707
- }) : { activity: [], transcript: [] };
708
- const currentTranscript = currentContent.transcript;
709
- const traceId = canExposeSqlContent ? traceIdFromTranscript(currentTranscript) : void 0;
710
- const sentryTraceUrl = traceId ? buildSentryTraceUrl(traceId) : void 0;
711
- const effectiveRuns = conversation ? [
712
- {
713
- ...sessionReportFromConversation(conversation, nowMs),
714
- ...currentTurnSummary ? {
715
- cumulativeDurationMs: currentTurnSummary.cumulativeDurationMs,
716
- ...currentTurnSummary.cumulativeUsage ? { cumulativeUsage: currentTurnSummary.cumulativeUsage } : {}
717
- } : {},
718
- ...currentTurnSummary?.modelId ? { modelId: currentTurnSummary.modelId } : {},
719
- ...currentTurnSummary?.reasoningLevel ? { reasoningLevel: currentTurnSummary.reasoningLevel } : {},
720
- ...traceId ? { traceId } : {},
721
- ...sentryTraceUrl ? { sentryTraceUrl } : {},
722
- activity: currentContent.activity,
723
- transcriptAvailable: transcriptExpiredAt === void 0 && canExposeSqlContent && currentTranscript.length > 0,
724
- ...currentTranscript.length > 0 ? {
725
- transcriptMessageCount: countConversationMessages(currentTranscript)
726
- } : {},
727
- ...!canExposeSqlContent && transcriptExpiredAt === void 0 ? {
728
- transcriptMetadata: currentTranscript.map(
729
- redactTranscriptMessage
730
- ),
731
- transcriptRedacted: true,
732
- transcriptRedactionReason: "non_public_conversation"
733
- } : {},
734
- ...transcriptExpiredAt !== void 0 ? {
735
- transcriptExpired: true,
736
- transcriptExpiredAt,
737
- transcriptMetadata: []
738
- } : {},
739
- transcript: transcriptExpiredAt === void 0 && canExposeSqlContent ? currentTranscript : []
740
- }
741
- ] : [];
742
- const firstRun = effectiveRuns[0];
743
- const displayTitle = firstRun?.displayTitle ?? surfaceFallbackLabel(firstRun?.surface ?? "slack");
744
- const sentryConversationUrl = buildSentryConversationUrl(conversationId);
745
- return {
746
- conversationId,
747
- displayTitle,
748
- generatedAt: new Date(nowMs).toISOString(),
749
- ...sentryConversationUrl ? { sentryConversationUrl } : {},
750
- runs: effectiveRuns
751
- };
752
- }
753
- async function readConversationSubagentTranscriptReport(conversationId, _runId, subagentId, options = {}) {
754
- const store = conversationStore(options);
755
- const stepStore = getAgentStepStore();
756
- const [conversation, parentSteps] = await Promise.all([
757
- store.get({ conversationId }),
758
- stepStore.loadHistory(conversationId)
759
- ]);
760
- if (conversation?.transcriptPurgedAtMs !== void 0) {
761
- return {
762
- type: "subagent",
763
- createdAt: (/* @__PURE__ */ new Date(0)).toISOString(),
764
- id: subagentId,
765
- status: "completed",
766
- subagentKind: "unknown",
767
- transcript: [],
768
- transcriptAvailable: false,
769
- transcriptExpired: true,
770
- transcriptExpiredAt: new Date(
771
- conversation.transcriptPurgedAtMs
772
- ).toISOString()
773
- };
774
- }
775
- const start = parentSteps.find(
776
- (step) => step.entry.type === "subagent_started" && step.entry.subagentInvocationId === subagentId
777
- );
778
- if (!start) {
779
- return {
780
- type: "subagent",
781
- createdAt: (/* @__PURE__ */ new Date(0)).toISOString(),
782
- id: subagentId,
783
- status: "error",
784
- subagentKind: "unknown",
785
- transcript: [],
786
- transcriptAvailable: false,
787
- unavailableReason: "not_found"
788
- };
789
- }
790
- const end = parentSteps.find(
791
- (step) => step.entry.type === "subagent_ended" && step.entry.subagentInvocationId === subagentId
792
- );
793
- const childConversationId = start.entry.childConversationId;
794
- const activity = subagentActivityFromSteps(start, end);
795
- const subagentSentryConversationUrl = buildSentryConversationUrl(childConversationId);
796
- const conversationFields = {
797
- subagentConversationId: childConversationId,
798
- ...subagentSentryConversationUrl ? { subagentSentryConversationUrl } : {}
799
- };
800
- const canExposeTranscript = canExposeConversationPayload({
801
- conversationId,
802
- visibility: conversation?.visibility
803
- });
804
- if (!canExposeTranscript) {
805
- return subagentTranscriptReport(activity, {
806
- ...conversationFields,
807
- transcriptRedacted: true,
808
- transcriptRedactionReason: "non_public_conversation"
809
- });
810
- }
811
- const childMessages = await loadProjection({
812
- conversationId: childConversationId
813
- });
814
- if (childMessages.length === 0) {
815
- return subagentTranscriptReport(activity, {
816
- ...conversationFields,
817
- unavailableReason: "missing_transcript_ref"
818
- });
819
- }
820
- const transcript = childMessages.map(
821
- (message) => normalizeTranscriptMessage(message, {
822
- unwrapAdvisorTask: activity.subagentKind === "advisor"
823
- })
824
- );
825
- return subagentTranscriptReport(activity, {
826
- ...conversationFields,
827
- transcript,
828
- transcriptMessageCount: countConversationMessages(transcript)
829
- });
830
- }
831
-
832
- // src/reporting/conversations/statistics.ts
833
- var CONVERSATION_STATS_LIMIT = 5e3;
834
- var RECENT_CONVERSATION_STATS_WINDOW_MS = 7 * 24 * 60 * 60 * 1e3;
835
- function usageTokenTotal(usage) {
836
- if (!usage) return void 0;
837
- const components = [
838
- usage.inputTokens,
839
- usage.outputTokens,
840
- usage.cachedInputTokens,
841
- usage.cacheCreationTokens
842
- ].reduce((sum, value) => {
843
- const count = typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : void 0;
844
- return count === void 0 ? sum : (sum ?? 0) + count;
845
- }, void 0);
846
- if (components !== void 0) {
847
- return components;
848
- }
849
- return typeof usage.totalTokens === "number" && Number.isFinite(usage.totalTokens) ? Math.max(0, Math.floor(usage.totalTokens)) : void 0;
850
- }
851
- function usageCostTotal(usage) {
852
- if (!usage?.cost) return void 0;
853
- if (typeof usage.cost.total === "number" && Number.isFinite(usage.cost.total)) {
854
- return Math.max(0, usage.cost.total);
855
- }
856
- return [
857
- usage.cost.input,
858
- usage.cost.output,
859
- usage.cost.cacheRead,
860
- usage.cost.cacheWrite
861
- ].reduce((sum, value) => {
862
- const amount = typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : void 0;
863
- return amount === void 0 ? sum : (sum ?? 0) + amount;
864
- }, void 0);
865
- }
866
- function addUsd(left, right) {
867
- return Math.round(((left ?? 0) + right) * 1e12) / 1e12;
868
- }
869
- function runDurationSnapshot(run) {
870
- return typeof run.cumulativeDurationMs === "number" && Number.isFinite(run.cumulativeDurationMs) ? Math.max(0, Math.floor(run.cumulativeDurationMs)) : void 0;
871
- }
872
- function runContributions(runs) {
873
- return runs.map((run) => {
874
- const duration = runDurationSnapshot(run);
875
- const tokens = usageTokenTotal(run.cumulativeUsage);
876
- const costUsd = usageCostTotal(run.cumulativeUsage);
877
- const contribution = {
878
- durationMs: duration ?? 0,
879
- run
880
- };
881
- if (tokens !== void 0) {
882
- contribution.tokens = tokens;
883
- }
884
- if (costUsd !== void 0) {
885
- contribution.costUsd = costUsd;
886
- }
887
- return contribution;
888
- });
889
- }
890
- function contributionDurationTotal(contributions) {
891
- return contributions.reduce(
892
- (sum, contribution) => sum + contribution.durationMs,
893
- 0
894
- );
895
- }
896
- function addTokenTotal(total, tokens) {
897
- return tokens === void 0 ? total : (total ?? 0) + tokens;
898
- }
899
- function contributionTokenTotal(contributions) {
900
- return contributions.reduce(
901
- (sum, contribution) => addTokenTotal(sum, contribution.tokens),
902
- void 0
903
- );
904
- }
905
- function contributionCostTotal(contributions) {
906
- return contributions.reduce(
907
- (sum, contribution) => contribution.costUsd === void 0 ? sum : addUsd(sum, contribution.costUsd),
908
- void 0
909
- );
910
- }
911
- function actorLabel(actor) {
912
- const email = actor?.email?.trim() || void 0;
913
- const fullName = actor?.fullName?.trim() || void 0;
914
- const slackUserName = actor?.slackUserName?.trim() || void 0;
915
- return email ?? fullName ?? slackUserName ?? actor?.slackUserId;
916
- }
917
- function locationLabel(run) {
918
- return slackStatsLocationLabel(run) ?? surfaceFallbackLabel(run.surface);
919
- }
920
- function emptyStatsItem(label) {
921
- return {
922
- active: 0,
923
- conversations: 0,
924
- durationMs: 0,
925
- failed: 0,
926
- hung: 0,
927
- label,
928
- runs: 0
929
- };
930
- }
931
- function addItemTokens(item, tokens) {
932
- if (tokens !== void 0) {
933
- item.tokens = (item.tokens ?? 0) + tokens;
934
- }
935
- }
936
- function addItemCost(item, costUsd) {
937
- if (costUsd !== void 0) {
938
- item.costUsd = addUsd(item.costUsd, costUsd);
939
- }
940
- }
941
- function statusSignals(runs) {
942
- return {
943
- active: runs.some((run) => run.status === "active"),
944
- failed: runs.some((run) => run.status === "failed"),
945
- hung: runs.some((run) => run.status === "hung")
946
- };
947
- }
948
- function statsItems(map) {
949
- return [...map.values()].sort(
950
- (left, right) => right.conversations - left.conversations || right.runs - left.runs || right.durationMs - left.durationMs || left.label.localeCompare(right.label)
951
- );
952
- }
953
- function recentConversationGroups(args) {
954
- const startMs = args.nowMs - RECENT_CONVERSATION_STATS_WINDOW_MS;
955
- const groups = /* @__PURE__ */ new Map();
956
- for (const summary of args.summaries) {
957
- groups.set(summary.conversationId, [
958
- ...groups.get(summary.conversationId) ?? [],
959
- summary
960
- ]);
961
- }
962
- return [...groups.values()].map(
963
- (runs) => [...runs].sort(
964
- (left, right) => (reportTime(left.startedAt) ?? 0) - (reportTime(right.startedAt) ?? 0) || left.id.localeCompare(right.id)
965
- )
966
- ).filter((runs) => {
967
- const activityAt = reportTime(newestRun(runs).lastSeenAt);
968
- return activityAt !== void 0 && activityAt >= startMs && activityAt <= args.nowMs;
969
- });
970
- }
971
- function conversationDurationMs(runs) {
972
- if (!runs.some((run) => runDurationSnapshot(run) !== void 0)) {
973
- return 0;
974
- }
975
- return contributionDurationTotal(runContributions(runs));
976
- }
977
- function buildConversationStatsReport(args) {
978
- const conversations = recentConversationGroups(args);
979
- const actors = /* @__PURE__ */ new Map();
980
- const locations = /* @__PURE__ */ new Map();
981
- let durationMs = 0;
982
- let costUsd;
983
- let tokens;
984
- let active = 0;
985
- let failed = 0;
986
- let hung = 0;
987
- for (const runs of conversations) {
988
- const contributions = runContributions(runs);
989
- const conversationSignals = statusSignals(runs);
990
- const conversationCostUsd = contributionCostTotal(contributions);
991
- const conversationTokens = contributionTokenTotal(contributions);
992
- durationMs += contributionDurationTotal(contributions);
993
- costUsd = conversationCostUsd === void 0 ? costUsd : addUsd(costUsd, conversationCostUsd);
994
- tokens = addTokenTotal(tokens, conversationTokens);
995
- active += conversationSignals.active ? 1 : 0;
996
- failed += conversationSignals.failed ? 1 : 0;
997
- hung += conversationSignals.hung ? 1 : 0;
998
- const actorRuns = /* @__PURE__ */ new Map();
999
- for (const contribution of contributions) {
1000
- const actor = actorLabel(contribution.run.actorIdentity) ?? "Unknown";
1001
- actorRuns.set(actor, [...actorRuns.get(actor) ?? [], contribution]);
1002
- }
1003
- for (const [actor, actorContributions] of actorRuns) {
1004
- const item = actors.get(actor) ?? emptyStatsItem(actor);
1005
- const signals = statusSignals(
1006
- actorContributions.map((contribution) => contribution.run)
1007
- );
1008
- item.conversations += 1;
1009
- item.runs += actorContributions.length;
1010
- item.durationMs += contributionDurationTotal(actorContributions);
1011
- item.active += signals.active ? 1 : 0;
1012
- item.failed += signals.failed ? 1 : 0;
1013
- item.hung += signals.hung ? 1 : 0;
1014
- addItemTokens(item, contributionTokenTotal(actorContributions));
1015
- addItemCost(item, contributionCostTotal(actorContributions));
1016
- actors.set(actor, item);
1017
- }
1018
- const location = locationLabel(newestRun(runs));
1019
- const locationItem = locations.get(location) ?? emptyStatsItem(location);
1020
- locationItem.conversations += 1;
1021
- locationItem.runs += runs.length;
1022
- locationItem.durationMs += conversationDurationMs(runs);
1023
- locationItem.active += conversationSignals.active ? 1 : 0;
1024
- locationItem.failed += conversationSignals.failed ? 1 : 0;
1025
- locationItem.hung += conversationSignals.hung ? 1 : 0;
1026
- addItemTokens(locationItem, conversationTokens);
1027
- addItemCost(locationItem, conversationCostUsd);
1028
- locations.set(location, locationItem);
1029
- }
1030
- return {
1031
- active,
1032
- conversations: conversations.length,
1033
- durationMs,
1034
- failed,
1035
- generatedAt: args.generatedAt,
1036
- hung,
1037
- locations: statsItems(locations),
1038
- actors: statsItems(actors),
1039
- sampleLimit: args.sampleLimit,
1040
- sampleSize: args.sampleSize,
1041
- source: "conversation_index",
1042
- ...costUsd !== void 0 ? { costUsd } : {},
1043
- ...tokens !== void 0 ? { tokens } : {},
1044
- truncated: args.truncated,
1045
- runs: conversations.reduce((sum, runs) => sum + runs.length, 0),
1046
- windowEnd: new Date(args.nowMs).toISOString(),
1047
- windowStart: new Date(
1048
- args.nowMs - RECENT_CONVERSATION_STATS_WINDOW_MS
1049
- ).toISOString()
1050
- };
1051
- }
1052
- async function readConversationStatsReport(options = {}) {
1053
- const store = conversationStore(options);
1054
- const nowMs = Date.now();
1055
- const generatedAt = new Date(nowMs).toISOString();
1056
- const conversations = await store.listByActivity({
1057
- limit: CONVERSATION_STATS_LIMIT + 1
1058
- });
1059
- const truncated = conversations.length > CONVERSATION_STATS_LIMIT;
1060
- const sampledConversations = conversations.slice(0, CONVERSATION_STATS_LIMIT);
1061
- const summariesByConversation = await listAgentTurnSessionSummariesForConversations(
1062
- sampledConversations.map((conversation) => conversation.conversationId)
1063
- );
1064
- const summaries = sampledConversations.flatMap((conversation) => {
1065
- const turnSummaries = summariesByConversation.get(conversation.conversationId) ?? [];
1066
- return turnSummaries.length > 0 ? turnSummaries.map(
1067
- (summary) => sessionReportFromTurnSummary(conversation, summary, nowMs)
1068
- ) : [sessionReportFromConversation(conversation, nowMs)];
1069
- });
1070
- return buildConversationStatsReport({
1071
- generatedAt,
1072
- nowMs,
1073
- sampleLimit: CONVERSATION_STATS_LIMIT,
1074
- sampleSize: sampledConversations.length,
1075
- summaries,
1076
- truncated
1077
- });
1078
- }
1079
-
1080
- // src/reporting.ts
1081
- function readDescriptionText() {
1082
- try {
1083
- const raw = readFileSync(
1084
- path.join(homeDir(), "DESCRIPTION.md"),
1085
- "utf8"
1086
- ).trim();
1087
- return raw || void 0;
1088
- } catch {
1089
- return void 0;
1090
- }
1091
- }
1092
- async function readHealth() {
1093
- const res = GET();
1094
- return await res.json();
1095
- }
1096
- async function readSkills() {
1097
- const skills = await discoverSkills();
1098
- return skills.map((skill) => ({
1099
- name: skill.name,
1100
- pluginProvider: skill.pluginProvider
1101
- }));
1102
- }
1103
- async function readPlugins() {
1104
- return pluginCatalogRuntime.getProviders().map((plugin) => ({
1105
- name: plugin.manifest.name
1106
- }));
1107
- }
1108
- function createJuniorReporting() {
1109
- const conversationStore2 = getConversationStore();
1110
- const listRecent = (listOptions) => listRecentConversationSummaries({
1111
- ...listOptions,
1112
- conversationStore: conversationStore2
1113
- });
1114
- return {
1115
- getHealth: readHealth,
1116
- async getRuntimeInfo() {
1117
- const [plugins, skills] = await Promise.all([
1118
- readPlugins(),
1119
- readSkills()
1120
- ]);
1121
- return {
1122
- cwd: process.cwd(),
1123
- homeDir: homeDir(),
1124
- descriptionText: readDescriptionText(),
1125
- providers: plugins.map((plugin) => plugin.name),
1126
- skills,
1127
- packagedContent: pluginCatalogRuntime.getPackageContent()
1128
- };
1129
- },
1130
- getPlugins: readPlugins,
1131
- getSkills: readSkills,
1132
- listConversations: () => readConversationFeed({ conversationStore: conversationStore2 }),
1133
- getConversationStats: () => readConversationStatsReport({ conversationStore: conversationStore2 }),
1134
- listRecentConversations: listRecent,
1135
- getPluginOperationalReports: async () => {
1136
- const nowMs = Date.now();
1137
- return {
1138
- source: "plugins",
1139
- generatedAt: new Date(nowMs).toISOString(),
1140
- reports: await getPluginOperationalReports(nowMs, {
1141
- listRecent
1142
- })
1143
- };
1144
- },
1145
- getConversation: (conversationId) => readConversationReport(conversationId, { conversationStore: conversationStore2 }),
1146
- getConversationSubagentTranscript: (conversationId, runId, subagentId) => readConversationSubagentTranscriptReport(
1147
- conversationId,
1148
- runId,
1149
- subagentId
1150
- )
1151
- };
1152
- }
1153
- export {
1154
- createJuniorReporting
1155
- };