@sentry/junior 0.98.0 → 0.100.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 (82) hide show
  1. package/dist/{agent-hooks-OSW6C3CJ.js → agent-hooks-V6KU7PQN.js} +6 -5
  2. package/dist/api/activity.d.ts +14 -0
  3. package/dist/api/conversations/aggregate.d.ts +16 -0
  4. package/dist/api/conversations/detail-projection.d.ts +1 -0
  5. package/dist/api/conversations/list.d.ts +7 -2
  6. package/dist/api/conversations/list.query.d.ts +12 -3
  7. package/dist/api/conversations/projection.d.ts +1 -0
  8. package/dist/api/conversations/reporting.d.ts +42 -0
  9. package/dist/api/conversations/schema.d.ts +7 -7
  10. package/dist/api/conversations/stats.query.d.ts +1 -1
  11. package/dist/api/locations/detail.d.ts +81 -0
  12. package/dist/api/locations/list.d.ts +30 -0
  13. package/dist/api/locations/query.d.ts +5 -0
  14. package/dist/api/locations/schema.d.ts +175 -0
  15. package/dist/api/people/list.query.d.ts +1 -1
  16. package/dist/api/people/profile.query.d.ts +1 -1
  17. package/dist/api/people/schema.d.ts +10 -14
  18. package/dist/api/people/shared.d.ts +38 -78
  19. package/dist/api/schema.d.ts +11 -0
  20. package/dist/api/schema.js +16 -3
  21. package/dist/api-reference.d.ts +1 -1
  22. package/dist/api.js +41 -13
  23. package/dist/app.js +35 -27
  24. package/dist/chat/agent/request.d.ts +3 -0
  25. package/dist/chat/config.d.ts +2 -0
  26. package/dist/chat/reasoning-level.d.ts +4 -0
  27. package/dist/chat/services/context-compaction.d.ts +1 -0
  28. package/dist/chat/services/{turn-thinking-level.d.ts → turn-reasoning-level.d.ts} +11 -10
  29. package/dist/chat/services/turn-result.d.ts +3 -3
  30. package/dist/chat/tools/search-tools.d.ts +2 -2
  31. package/dist/{chunk-UAMKJPDF.js → chunk-2RNOB32R.js} +7 -1
  32. package/dist/{chunk-NJEDJFK4.js → chunk-2T7DCRAW.js} +1 -1
  33. package/dist/{chunk-DPHWDS6I.js → chunk-45DMFMY2.js} +81 -69
  34. package/dist/chunk-4AQUWWEM.js +75 -0
  35. package/dist/{chunk-EEZCY5WX.js → chunk-A7X2FDUO.js} +18 -2
  36. package/dist/{chunk-WTYEKPXU.js → chunk-FP5N5OWZ.js} +1 -1
  37. package/dist/chunk-FXFQNHRU.js +48 -0
  38. package/dist/chunk-GEUUJRQF.js +102 -0
  39. package/dist/{chunk-CRQVIHDI.js → chunk-IDZBM3NW.js} +2 -8
  40. package/dist/{chunk-DHHU2WSG.js → chunk-IQIBZXGB.js} +2 -2
  41. package/dist/{chunk-OL6XQ23W.js → chunk-IVXL57YF.js} +3 -3
  42. package/dist/{chunk-IO65E4ZD.js → chunk-JPQISMMJ.js} +3 -3
  43. package/dist/{chunk-KJHAW6VE.js → chunk-JSAANELJ.js} +28 -9
  44. package/dist/{chunk-EUZ44ROE.js → chunk-MM5UFDAC.js} +1 -3
  45. package/dist/{chunk-WJ7STXT6.js → chunk-NSOCZGOH.js} +3 -3
  46. package/dist/chunk-PGGEOCQY.js +77 -0
  47. package/dist/{chunk-EOWXRY5E.js → chunk-PWC24W5F.js} +1 -1
  48. package/dist/chunk-QOZOJNSE.js +96 -0
  49. package/dist/chunk-QSBQJ3PA.js +275 -0
  50. package/dist/{chunk-FTTFV6DC.js → chunk-TPTJEHUE.js} +4 -4
  51. package/dist/{chunk-OGHXZ4KQ.js → chunk-VHRM6GNH.js} +88 -122
  52. package/dist/{chunk-NG2AV7IB.js → chunk-VVIXK2BX.js} +1 -1
  53. package/dist/{chunk-6HHEZENZ.js → chunk-VY7BT4SM.js} +16 -7
  54. package/dist/{chunk-H66UCBPW.js → chunk-XJA7BPHS.js} +1 -1
  55. package/dist/{chunk-ER45IV77.js → chunk-XSBIISXS.js} +9 -9
  56. package/dist/cli/chat.js +15 -14
  57. package/dist/cli/plugins.js +6 -5
  58. package/dist/cli/snapshot-warmup.js +2 -2
  59. package/dist/cli/upgrade.js +8 -7
  60. package/dist/db/schema/conversations.d.ts +17 -0
  61. package/dist/db/schema/destinations.d.ts +12 -3
  62. package/dist/db/schema.d.ts +19 -2
  63. package/dist/{db-GFSCDAHS.js → db-RCZB7KNM.js} +3 -2
  64. package/dist/detail-5SLCGT6J.js +29 -0
  65. package/dist/{detail-W4ZSD45K.js → detail-LU6COZO6.js} +11 -10
  66. package/dist/{legacy-import-6WWXMALY.js → legacy-import-VCYLWT2V.js} +4 -3
  67. package/dist/list-2FI5SFV7.js +80 -0
  68. package/dist/list-4ALSU6ZW.js +30 -0
  69. package/dist/{list-3KZNG3SJ.js → list-KHRAWZWU.js} +11 -8
  70. package/dist/{plugin-conversations-ZYRWZYEA.js → plugin-conversations-JANXC4GA.js} +7 -6
  71. package/dist/profile-3O3OVENT.js +212 -0
  72. package/dist/{runner-267ZRLZD.js → runner-BM7D35YW.js} +12 -11
  73. package/dist/stats-J3YGCYDF.js +174 -0
  74. package/dist/{subagent-5HZPQC7V.js → subagent-P7HOSMVC.js} +10 -9
  75. package/migrations/0003_peaceful_scalphunter.sql +4 -0
  76. package/migrations/meta/0003_snapshot.json +1035 -0
  77. package/migrations/meta/_journal.json +8 -1
  78. package/package.json +5 -5
  79. package/dist/chunk-CV22LUIV.js +0 -261
  80. package/dist/list-M5BRWW3D.js +0 -92
  81. package/dist/profile-UEIRSVT3.js +0 -146
  82. package/dist/stats-L62E4O4U.js +0 -207
@@ -0,0 +1,102 @@
1
+ import {
2
+ getDb,
3
+ juniorConversations,
4
+ juniorIdentities,
5
+ juniorUsers
6
+ } from "./chunk-VHRM6GNH.js";
7
+ import {
8
+ juniorDestinations
9
+ } from "./chunk-FXFQNHRU.js";
10
+
11
+ // src/api/people/shared.ts
12
+ import { and, asc, desc, eq, sql } from "drizzle-orm";
13
+ var RECENT_LIMIT = 25;
14
+ var ACTIVITY_DAYS = 366;
15
+ function normalizeEmail(email) {
16
+ const normalized = email?.trim().toLowerCase();
17
+ return normalized || void 0;
18
+ }
19
+ function emptyTotals() {
20
+ return {
21
+ active: 0,
22
+ activeDays: 0,
23
+ conversations: 0,
24
+ durationMs: 0,
25
+ failed: 0
26
+ };
27
+ }
28
+ function emptyActivityDay(date) {
29
+ return {
30
+ active: 0,
31
+ conversations: 0,
32
+ date,
33
+ durationMs: 0,
34
+ failed: 0
35
+ };
36
+ }
37
+ function activityDays(days, nowMs) {
38
+ const items = [];
39
+ const end = new Date(nowMs);
40
+ end.setUTCHours(0, 0, 0, 0);
41
+ const start = new Date(end);
42
+ start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS - 1));
43
+ for (const cursor = new Date(start); cursor.getTime() <= end.getTime(); cursor.setUTCDate(cursor.getUTCDate() + 1)) {
44
+ const date = cursor.toISOString().slice(0, 10);
45
+ items.push(days.get(date) ?? emptyActivityDay(date));
46
+ }
47
+ return items;
48
+ }
49
+ function statsItems(map) {
50
+ return [...map.values()].sort(
51
+ (left, right) => right.conversations - left.conversations || right.durationMs - left.durationMs || left.label.localeCompare(right.label)
52
+ );
53
+ }
54
+ function verifiedActorWhere(email) {
55
+ const normalizedEmail = normalizeEmail(email);
56
+ return and(
57
+ eq(juniorIdentities.provider, "slack"),
58
+ eq(juniorIdentities.emailVerified, true),
59
+ sql`${juniorUsers.primaryEmailNormalized} IS NOT NULL`,
60
+ normalizedEmail ? eq(juniorUsers.primaryEmailNormalized, normalizedEmail) : void 0
61
+ );
62
+ }
63
+ async function recentActorRows(email) {
64
+ return getDb().select({
65
+ channelName: juniorConversations.channelName,
66
+ conversationId: juniorConversations.conversationId,
67
+ createdAt: juniorConversations.createdAt,
68
+ destinationId: juniorDestinations.id,
69
+ destinationVisibility: juniorDestinations.visibility,
70
+ durationMs: juniorConversations.durationMs,
71
+ email: juniorUsers.primaryEmailNormalized,
72
+ executionStatus: juniorConversations.executionStatus,
73
+ executionUpdatedAt: juniorConversations.executionUpdatedAt,
74
+ fullName: juniorUsers.displayName,
75
+ handle: juniorIdentities.handle,
76
+ lastActivityAt: juniorConversations.lastActivityAt,
77
+ providerSubjectId: juniorIdentities.providerSubjectId,
78
+ source: juniorConversations.source,
79
+ title: juniorConversations.title,
80
+ updatedAt: juniorConversations.updatedAt,
81
+ usage: juniorConversations.usage
82
+ }).from(juniorConversations).innerJoin(
83
+ juniorIdentities,
84
+ eq(juniorIdentities.id, juniorConversations.actorIdentityId)
85
+ ).innerJoin(juniorUsers, eq(juniorUsers.id, juniorIdentities.userId)).leftJoin(
86
+ juniorDestinations,
87
+ eq(juniorDestinations.id, juniorConversations.destinationId)
88
+ ).where(verifiedActorWhere(email)).orderBy(
89
+ desc(juniorConversations.lastActivityAt),
90
+ asc(juniorConversations.conversationId)
91
+ ).limit(RECENT_LIMIT);
92
+ }
93
+
94
+ export {
95
+ ACTIVITY_DAYS,
96
+ normalizeEmail,
97
+ emptyTotals,
98
+ activityDays,
99
+ statsItems,
100
+ verifiedActorWhere,
101
+ recentActorRows
102
+ };
@@ -2,7 +2,7 @@ import {
2
2
  actorIdentitySchema,
3
3
  conversationStatsItemSchema,
4
4
  conversationSummaryReportSchema
5
- } from "./chunk-EUZ44ROE.js";
5
+ } from "./chunk-MM5UFDAC.js";
6
6
 
7
7
  // src/api/people/schema.ts
8
8
  import { z } from "zod";
@@ -37,10 +37,7 @@ var actorSummaryReportSchema = actorTotalsReportSchema.extend({
37
37
  var actorDirectoryReportSchema = z.object({
38
38
  generatedAt: z.string(),
39
39
  people: z.array(actorSummaryReportSchema),
40
- sampleLimit: z.number(),
41
- sampleSize: z.number(),
42
- source: z.literal("conversation_index"),
43
- truncated: z.boolean()
40
+ source: z.literal("conversation_index")
44
41
  }).strict();
45
42
  var actorProfileReportSchema = z.object({
46
43
  activityDays: z.array(actorActivityDayReportSchema),
@@ -48,12 +45,9 @@ var actorProfileReportSchema = z.object({
48
45
  locations: z.array(peopleConversationStatsItemSchema),
49
46
  recentConversations: z.array(peopleConversationSummaryReportSchema),
50
47
  actor: identifiedActorSchema,
51
- sampleLimit: z.number(),
52
- sampleSize: z.number(),
53
48
  source: z.literal("conversation_index"),
54
49
  surfaces: z.array(peopleConversationStatsItemSchema),
55
50
  totals: actorTotalsReportSchema,
56
- truncated: z.boolean(),
57
51
  windowEnd: z.string(),
58
52
  windowStart: z.string()
59
53
  }).strict();
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  ensureLegacyConversationImport
3
- } from "./chunk-IO65E4ZD.js";
3
+ } from "./chunk-JPQISMMJ.js";
4
4
  import {
5
5
  contextProvenance,
6
6
  getAgentStepStore
7
- } from "./chunk-OGHXZ4KQ.js";
7
+ } from "./chunk-VHRM6GNH.js";
8
8
 
9
9
  // src/chat/conversations/projection.ts
10
10
  import { isDeepStrictEqual } from "util";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  commitMessages,
3
3
  loadTurnProjection
4
- } from "./chunk-DHHU2WSG.js";
4
+ } from "./chunk-IQIBZXGB.js";
5
5
  import {
6
6
  usageSchema
7
7
  } from "./chunk-PDO5BLNM.js";
@@ -9,10 +9,10 @@ import {
9
9
  getConversationStore,
10
10
  instructionActors,
11
11
  instructionProvenanceFor
12
- } from "./chunk-OGHXZ4KQ.js";
12
+ } from "./chunk-VHRM6GNH.js";
13
13
  import {
14
14
  getStateAdapter
15
- } from "./chunk-EEZCY5WX.js";
15
+ } from "./chunk-A7X2FDUO.js";
16
16
  import {
17
17
  toStoredSlackActor
18
18
  } from "./chunk-I35CTGPO.js";
@@ -11,14 +11,14 @@ import {
11
11
  legacyActorProvenance,
12
12
  piMessageSchema,
13
13
  readSessionLogEntries
14
- } from "./chunk-OGHXZ4KQ.js";
14
+ } from "./chunk-VHRM6GNH.js";
15
15
  import {
16
16
  botConfig,
17
17
  escapeXml,
18
18
  getStateAdapter,
19
19
  resolveGatewayModel,
20
20
  unescapeXml
21
- } from "./chunk-EEZCY5WX.js";
21
+ } from "./chunk-A7X2FDUO.js";
22
22
  import {
23
23
  logWarn,
24
24
  setSpanAttributes
@@ -464,7 +464,7 @@ async function hydrateConversationMessages(args) {
464
464
  args.conversation.messages = [];
465
465
  return;
466
466
  }
467
- const { ensureLegacyConversationImport: ensureLegacyConversationImport2 } = await import("./legacy-import-6WWXMALY.js");
467
+ const { ensureLegacyConversationImport: ensureLegacyConversationImport2 } = await import("./legacy-import-VCYLWT2V.js");
468
468
  await ensureLegacyConversationImport2({ conversationId: args.conversationId });
469
469
  await hydrateConversationCompactions({
470
470
  conversation: args.conversation,
@@ -1,20 +1,22 @@
1
1
  import {
2
2
  formatSlackConversationRedactedLabel,
3
3
  resolveSlackConversationContextFromThreadId
4
- } from "./chunk-NG2AV7IB.js";
4
+ } from "./chunk-VVIXK2BX.js";
5
5
  import {
6
6
  getDb,
7
7
  juniorConversations,
8
- juniorDestinations,
9
8
  juniorIdentities
10
- } from "./chunk-OGHXZ4KQ.js";
9
+ } from "./chunk-VHRM6GNH.js";
10
+ import {
11
+ juniorDestinations
12
+ } from "./chunk-FXFQNHRU.js";
11
13
  import {
12
14
  parseSlackThreadId,
13
15
  resolveConversationPrivacy
14
16
  } from "./chunk-LVA4I4H7.js";
15
17
 
16
18
  // src/api/conversations/list.query.ts
17
- import { asc, desc, eq, isNull } from "drizzle-orm";
19
+ import { and, asc, desc, eq, isNull } from "drizzle-orm";
18
20
 
19
21
  // src/api/conversations/shared.ts
20
22
  function slackStatsLocationLabel(input) {
@@ -148,15 +150,17 @@ function conversationSummaryFromStoredConversation(args) {
148
150
  ...actorIdentity ? { actorIdentity } : {},
149
151
  ...slackThread ? { channel: slackThread.channelId } : {},
150
152
  ...channelName ? { channelName } : {},
151
- ...channelNameRedacted ? { channelNameRedacted: true } : {}
153
+ ...channelNameRedacted ? { channelNameRedacted: true } : {},
154
+ ...args.locationId && !channelNameRedacted ? { locationId: args.locationId } : {}
152
155
  };
153
156
  }
154
157
 
155
158
  // src/api/conversations/list.query.ts
156
159
  var CONVERSATION_FEED_LIMIT = 50;
157
- async function conversationRows(db, limit) {
160
+ async function conversationRows(db, limit, actorEmail) {
158
161
  return db.select({
159
162
  conversation: juniorConversations,
163
+ destinationId: juniorDestinations.id,
160
164
  destinationVisibility: juniorDestinations.visibility,
161
165
  identityDisplayName: juniorIdentities.displayName,
162
166
  identityEmail: juniorIdentities.email,
@@ -170,7 +174,15 @@ async function conversationRows(db, limit) {
170
174
  ).leftJoin(
171
175
  juniorIdentities,
172
176
  eq(juniorIdentities.id, juniorConversations.actorIdentityId)
173
- ).where(isNull(juniorConversations.parentConversationId)).orderBy(
177
+ ).where(
178
+ and(
179
+ isNull(juniorConversations.parentConversationId),
180
+ actorEmail ? and(
181
+ eq(juniorIdentities.emailNormalized, actorEmail),
182
+ eq(juniorIdentities.emailVerified, true)
183
+ ) : void 0
184
+ )
185
+ ).orderBy(
174
186
  desc(juniorConversations.lastActivityAt),
175
187
  asc(juniorConversations.conversationId)
176
188
  ).limit(limit);
@@ -210,6 +222,7 @@ async function readConversationRecordFromSql(conversationId) {
210
222
  const db = getDb();
211
223
  const rows = await db.select({
212
224
  conversation: juniorConversations,
225
+ destinationId: juniorDestinations.id,
213
226
  destinationVisibility: juniorDestinations.visibility,
214
227
  identityDisplayName: juniorIdentities.displayName,
215
228
  identityEmail: juniorIdentities.email,
@@ -228,17 +241,23 @@ async function readConversationRecordFromSql(conversationId) {
228
241
  return row ? {
229
242
  conversation: conversationFromRow(row),
230
243
  durationMs: row.conversation.durationMs,
244
+ ...row.destinationVisibility === "public" && row.destinationId ? { locationId: row.destinationId } : {},
231
245
  usage: row.conversation.usage
232
246
  } : void 0;
233
247
  }
234
- async function readConversationFeedFromSql(limit = CONVERSATION_FEED_LIMIT) {
248
+ async function readConversationFeedFromSql(options = {}) {
235
249
  const nowMs = Date.now();
236
- const rows = await conversationRows(getDb(), limit);
250
+ const rows = await conversationRows(
251
+ getDb(),
252
+ options.limit ?? CONVERSATION_FEED_LIMIT,
253
+ options.actorEmail
254
+ );
237
255
  return {
238
256
  conversations: rows.map(
239
257
  (row) => conversationSummaryFromStoredConversation({
240
258
  conversation: conversationFromRow(row),
241
259
  durationMs: row.conversation.durationMs,
260
+ ...row.destinationVisibility === "public" && row.destinationId ? { locationId: row.destinationId } : {},
242
261
  usage: row.conversation.usage ?? void 0
243
262
  })
244
263
  ),
@@ -36,6 +36,7 @@ var conversationSummaryReportSchema = z.object({
36
36
  channel: z.string().optional(),
37
37
  channelName: z.string().optional(),
38
38
  channelNameRedacted: z.boolean().optional(),
39
+ locationId: z.string().optional(),
39
40
  sentryTraceUrl: z.string().optional(),
40
41
  traceId: z.string().optional()
41
42
  }).strict();
@@ -195,12 +196,9 @@ var conversationStatsReportSchema = z.object({
195
196
  generatedAt: z.string(),
196
197
  locations: z.array(conversationStatsItemSchema),
197
198
  actors: z.array(conversationStatsItemSchema),
198
- sampleLimit: z.number(),
199
- sampleSize: z.number(),
200
199
  source: z.literal("conversation_index"),
201
200
  costUsd: z.number().optional(),
202
201
  tokens: z.number().optional(),
203
- truncated: z.boolean(),
204
202
  windowEnd: z.string(),
205
203
  windowStart: z.string()
206
204
  }).strict();
@@ -7,10 +7,10 @@ import {
7
7
  addAgentTurnUsage,
8
8
  getAgentTurnSessionRecord,
9
9
  upsertAgentTurnSessionRecord
10
- } from "./chunk-OL6XQ23W.js";
10
+ } from "./chunk-IVXL57YF.js";
11
11
  import {
12
12
  persistConversationMessages
13
- } from "./chunk-IO65E4ZD.js";
13
+ } from "./chunk-JPQISMMJ.js";
14
14
  import {
15
15
  sleep
16
16
  } from "./chunk-4ZNGQH7C.js";
@@ -18,7 +18,7 @@ import {
18
18
  getPiMessageRole,
19
19
  getStateAdapter,
20
20
  trimTrailingAssistantMessages
21
- } from "./chunk-EEZCY5WX.js";
21
+ } from "./chunk-A7X2FDUO.js";
22
22
  import {
23
23
  getActiveTraceId,
24
24
  logException
@@ -0,0 +1,77 @@
1
+ import {
2
+ juniorConversations
3
+ } from "./chunk-VHRM6GNH.js";
4
+
5
+ // src/api/conversations/aggregate.ts
6
+ import { sql } from "drizzle-orm";
7
+ function tokenValue() {
8
+ return sql`
9
+ CASE
10
+ WHEN ${juniorConversations.usage}->>'totalTokens' IS NOT NULL
11
+ THEN (${juniorConversations.usage}->>'totalTokens')::double precision
12
+ WHEN COALESCE(
13
+ ${juniorConversations.usage}->>'inputTokens',
14
+ ${juniorConversations.usage}->>'outputTokens',
15
+ ${juniorConversations.usage}->>'cachedInputTokens',
16
+ ${juniorConversations.usage}->>'cacheCreationTokens'
17
+ ) IS NOT NULL
18
+ THEN COALESCE((${juniorConversations.usage}->>'inputTokens')::double precision, 0)
19
+ + COALESCE((${juniorConversations.usage}->>'outputTokens')::double precision, 0)
20
+ + COALESCE((${juniorConversations.usage}->>'cachedInputTokens')::double precision, 0)
21
+ + COALESCE((${juniorConversations.usage}->>'cacheCreationTokens')::double precision, 0)
22
+ ELSE NULL
23
+ END
24
+ `;
25
+ }
26
+ function costValue() {
27
+ return sql`
28
+ CASE
29
+ WHEN ${juniorConversations.usage}->'cost'->>'total' IS NOT NULL
30
+ THEN (${juniorConversations.usage}->'cost'->>'total')::double precision
31
+ WHEN COALESCE(
32
+ ${juniorConversations.usage}->'cost'->>'input',
33
+ ${juniorConversations.usage}->'cost'->>'output',
34
+ ${juniorConversations.usage}->'cost'->>'cacheRead',
35
+ ${juniorConversations.usage}->'cost'->>'cacheWrite'
36
+ ) IS NOT NULL
37
+ THEN COALESCE((${juniorConversations.usage}->'cost'->>'input')::double precision, 0)
38
+ + COALESCE((${juniorConversations.usage}->'cost'->>'output')::double precision, 0)
39
+ + COALESCE((${juniorConversations.usage}->'cost'->>'cacheRead')::double precision, 0)
40
+ + COALESCE((${juniorConversations.usage}->'cost'->>'cacheWrite')::double precision, 0)
41
+ ELSE NULL
42
+ END
43
+ `;
44
+ }
45
+ function conversationAggregateColumns() {
46
+ return {
47
+ active: sql`COUNT(*) FILTER (
48
+ WHERE ${juniorConversations.executionStatus} NOT IN ('idle', 'failed')
49
+ )::integer`,
50
+ conversations: sql`COUNT(*)::integer`,
51
+ costUsd: sql`SUM(${costValue()})::double precision`,
52
+ durationMs: sql`COALESCE(SUM(${juniorConversations.durationMs}), 0)::double precision`,
53
+ failed: sql`COUNT(*) FILTER (
54
+ WHERE ${juniorConversations.executionStatus} = 'failed'
55
+ )::integer`,
56
+ tokens: sql`SUM(${tokenValue()})::double precision`
57
+ };
58
+ }
59
+ function conversationRangeColumns() {
60
+ return {
61
+ firstSeenAt: sql`MIN(${juniorConversations.createdAt})`.mapWith(
62
+ juniorConversations.createdAt
63
+ ),
64
+ lastSeenAt: sql`MAX(${juniorConversations.lastActivityAt})`.mapWith(
65
+ juniorConversations.lastActivityAt
66
+ )
67
+ };
68
+ }
69
+ function conversationActiveDaysColumn() {
70
+ return sql`COUNT(DISTINCT (${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC')::date)::integer`;
71
+ }
72
+
73
+ export {
74
+ conversationAggregateColumns,
75
+ conversationRangeColumns,
76
+ conversationActiveDaysColumn
77
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getStateAdapter
3
- } from "./chunk-EEZCY5WX.js";
3
+ } from "./chunk-A7X2FDUO.js";
4
4
  import {
5
5
  logException,
6
6
  logInfo,
@@ -0,0 +1,96 @@
1
+ // src/api/conversations/reporting.ts
2
+ var PRIVATE_CONVERSATION_LABEL = "Private Conversation";
3
+ function channelFromConversationId(conversationId) {
4
+ const [provider, channel] = conversationId.split(":");
5
+ return provider === "slack" && channel ? channel : void 0;
6
+ }
7
+ function surfaceFromRow(row) {
8
+ if (row.source === "api" || row.source === "scheduler" || row.source === "slack") {
9
+ return row.source;
10
+ }
11
+ if (row.conversationId.startsWith("slack:")) return "slack";
12
+ if (row.conversationId.startsWith("scheduler:")) return "scheduler";
13
+ if (row.conversationId.startsWith("api:")) return "api";
14
+ return "internal";
15
+ }
16
+ function statusFromRow(row) {
17
+ if (row.executionStatus === "failed") return "failed";
18
+ if (row.executionStatus === "idle") return "completed";
19
+ return "active";
20
+ }
21
+ function surfaceLabel(surface) {
22
+ if (surface === "scheduler") return "Scheduler";
23
+ if (surface === "api") return "API";
24
+ if (surface === "internal") return "Internal";
25
+ return "Conversation";
26
+ }
27
+ function slackLocationLabel(args) {
28
+ const channelId = args.channel;
29
+ if (!channelId) return void 0;
30
+ if (args.channelNameRedacted && args.channelName) return args.channelName;
31
+ const name = args.channelName?.replace(/^#/, "");
32
+ if (channelId.startsWith("D")) return "Direct Message";
33
+ if (channelId.startsWith("C")) {
34
+ return name ? `#${name}` : "Public Channel";
35
+ }
36
+ if (channelId.startsWith("G")) {
37
+ if (name?.startsWith("mpdm-")) return "Group DM";
38
+ return "Private Channel";
39
+ }
40
+ return name || channelId;
41
+ }
42
+ function channelNameFromRow(row) {
43
+ if (row.destinationVisibility !== "public") {
44
+ return PRIVATE_CONVERSATION_LABEL;
45
+ }
46
+ return row.channelName ?? void 0;
47
+ }
48
+ function titleFromRow(row, surface) {
49
+ if (row.destinationVisibility !== "public") {
50
+ return PRIVATE_CONVERSATION_LABEL;
51
+ }
52
+ const channel = channelFromConversationId(row.conversationId);
53
+ return row.title ?? slackLocationLabel({
54
+ channel,
55
+ channelName: row.channelName ?? void 0
56
+ }) ?? surfaceLabel(surface);
57
+ }
58
+ function actorFromRow(row) {
59
+ const actor = {
60
+ ...row.email ? { email: row.email } : {},
61
+ ...row.fullName ? { fullName: row.fullName } : {},
62
+ ...row.providerSubjectId ? { slackUserId: row.providerSubjectId } : {},
63
+ ...row.handle ? { slackUserName: row.handle } : {}
64
+ };
65
+ return Object.keys(actor).length ? actor : void 0;
66
+ }
67
+ function summaryFromRow(row) {
68
+ const surface = surfaceFromRow(row);
69
+ const channel = channelFromConversationId(row.conversationId);
70
+ const channelName = channelNameFromRow(row);
71
+ const channelNameRedacted = row.destinationVisibility !== "public";
72
+ const actorIdentity = actorFromRow(row);
73
+ return {
74
+ conversationId: row.conversationId,
75
+ cumulativeDurationMs: row.durationMs,
76
+ displayTitle: titleFromRow(row, surface),
77
+ lastProgressAt: new Date(
78
+ row.executionUpdatedAt ?? row.updatedAt
79
+ ).toISOString(),
80
+ lastSeenAt: row.lastActivityAt.toISOString(),
81
+ startedAt: row.createdAt.toISOString(),
82
+ status: statusFromRow(row),
83
+ surface,
84
+ ...actorIdentity ? { actorIdentity } : {},
85
+ ...channel ? { channel } : {},
86
+ ...channelName ? { channelName } : {},
87
+ ...channelNameRedacted ? { channelNameRedacted: true } : {},
88
+ ...row.destinationVisibility === "public" && row.destinationId ? { locationId: row.destinationId } : {}
89
+ };
90
+ }
91
+
92
+ export {
93
+ surfaceLabel,
94
+ slackLocationLabel,
95
+ summaryFromRow
96
+ };