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