@sentry/junior 0.71.3 → 0.72.0

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