@sentry/junior 0.177.0 → 0.178.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 (91) hide show
  1. package/dist/{agent-hooks-IYHCN7CO.js → agent-hooks-PBT4NNBV.js} +12 -12
  2. package/dist/api/conversations/archive.d.ts +5 -2
  3. package/dist/api/conversations/list.d.ts +0 -1
  4. package/dist/api/conversations/membership.d.ts +14 -0
  5. package/dist/api/conversations/projection.d.ts +2 -1
  6. package/dist/api/locations/detail.d.ts +1 -1
  7. package/dist/api/people/shared.d.ts +2 -2
  8. package/dist/api/schema/conversation.d.ts +4 -4
  9. package/dist/api/schema/location.d.ts +1 -1
  10. package/dist/api/schema/person.d.ts +1 -1
  11. package/dist/api/schema.js +1 -1
  12. package/dist/api.js +245 -166
  13. package/dist/app.js +35 -35
  14. package/dist/{catalog-runtime-2TD4CXSB.js → catalog-runtime-M7QGKPFZ.js} +5 -5
  15. package/dist/chat/agent/sandbox.d.ts +2 -0
  16. package/dist/chat/conversations/sql/participants.d.ts +1 -0
  17. package/dist/chat/conversations/store.d.ts +0 -1
  18. package/dist/chat/sandbox/errors.d.ts +4 -0
  19. package/dist/chat/sandbox/noninteractive-command.d.ts +1 -0
  20. package/dist/chat/sandbox/prepare-workspace.d.ts +27 -3
  21. package/dist/chat/sandbox/sandbox.d.ts +2 -0
  22. package/dist/chat/sandbox/session.d.ts +2 -0
  23. package/dist/chat/sandbox/snapshot/builder-sandbox.d.ts +5 -0
  24. package/dist/chat/sandbox/snapshot/install.d.ts +2 -2
  25. package/dist/chat/sandbox/snapshot/span.d.ts +3 -0
  26. package/dist/chat/sandbox/snapshot/store.d.ts +20 -17
  27. package/dist/chat/sandbox/snapshot/waiting-error.d.ts +15 -0
  28. package/dist/chat/sandbox/snapshot/workspace.d.ts +17 -0
  29. package/dist/chat/services/workspace-snapshot-wait.d.ts +27 -0
  30. package/dist/chat/workspaces/types.d.ts +1 -0
  31. package/dist/{chunk-3FWXQZJJ.js → chunk-22T3HB44.js} +1 -1
  32. package/dist/{chunk-DLTP6TUJ.js → chunk-3ZZEPLLB.js} +2 -2
  33. package/dist/{chunk-7ZFESKJ2.js → chunk-4K6VNP3S.js} +9 -9
  34. package/dist/{chunk-N4YEVOEC.js → chunk-534BO42N.js} +45 -276
  35. package/dist/{chunk-6NZRKJWU.js → chunk-6OYVH66J.js} +1 -1
  36. package/dist/{chunk-J2EWL6GJ.js → chunk-ABLODWPO.js} +1 -1
  37. package/dist/{chunk-ORMJY4RG.js → chunk-AGJ4OQ2V.js} +1 -1
  38. package/dist/{chunk-Q362TECJ.js → chunk-AQHSXA3J.js} +331 -2
  39. package/dist/{chunk-4SVAFAI2.js → chunk-AVRWMQ4U.js} +1 -1
  40. package/dist/{chunk-I7PKGMKI.js → chunk-BFLMRTPD.js} +6 -6
  41. package/dist/{chunk-ZCYABD7N.js → chunk-BHHWCNXI.js} +5 -5
  42. package/dist/{chunk-LFZODSAY.js → chunk-BUGO42R6.js} +4 -4
  43. package/dist/{chunk-7B6NW4P7.js → chunk-D3UJTX4U.js} +1 -1
  44. package/dist/{chunk-2IMQLZHS.js → chunk-FQ6BMP5P.js} +767 -81
  45. package/dist/{chunk-4TWDRU64.js → chunk-G3CQEUHI.js} +5 -5
  46. package/dist/{chunk-VGDOEKO2.js → chunk-H6ZXBFGX.js} +1 -1
  47. package/dist/{chunk-SNPQGTHJ.js → chunk-IQRPDKPJ.js} +1 -1
  48. package/dist/{chunk-CAWBLVBY.js → chunk-JVXZ3WDH.js} +6 -6
  49. package/dist/{chunk-7PZDAXIK.js → chunk-KEQAA42D.js} +1 -1
  50. package/dist/{chunk-UB5YB2ED.js → chunk-LAOP4EPL.js} +6 -6
  51. package/dist/{chunk-WYO2HXY5.js → chunk-OGRD5PFM.js} +1 -1
  52. package/dist/{chunk-AFOFVEXP.js → chunk-PRDKO3QB.js} +124 -13
  53. package/dist/{chunk-QLLOX7JY.js → chunk-QCCLDJHB.js} +8 -8
  54. package/dist/{chunk-FVVYHML7.js → chunk-RE4XWXBV.js} +6 -6
  55. package/dist/{chunk-4TRHPYTL.js → chunk-RGV6KHDM.js} +4 -4
  56. package/dist/{chunk-DGGIKWYQ.js → chunk-RGXOLPWO.js} +1 -1
  57. package/dist/{chunk-ELEAIRTG.js → chunk-TKB25A3L.js} +2 -2
  58. package/dist/{chunk-6KGMMVHF.js → chunk-U3UDPGZG.js} +2 -2
  59. package/dist/{chunk-7CVYXUOJ.js → chunk-UGWBJAGN.js} +1 -1
  60. package/dist/{chunk-LVL5NG5G.js → chunk-UMG7M32G.js} +19 -19
  61. package/dist/{chunk-LPMLAJYR.js → chunk-XCNS3CXE.js} +11 -17
  62. package/dist/{chunk-PA7QDS7P.js → chunk-XWUJWHFD.js} +204 -100
  63. package/dist/{chunk-WCQUKX74.js → chunk-ZCHHVTMF.js} +14 -5
  64. package/dist/cli/chat.js +31 -30
  65. package/dist/cli/check.js +6 -6
  66. package/dist/cli/plugins.js +13 -13
  67. package/dist/cli/snapshot-create.js +10 -10
  68. package/dist/cli/upgrade.js +7 -7
  69. package/dist/db/schema/conversation-participants.d.ts +17 -0
  70. package/dist/db/schema/conversations.d.ts +629 -1
  71. package/dist/db/schema/snapshots.d.ts +1 -1
  72. package/dist/db/schema.d.ts +18 -1
  73. package/dist/{db-A3CB76US.js → db-QVYOWATH.js} +7 -7
  74. package/dist/instrumentation.js +4 -4
  75. package/dist/nitro.js +2 -2
  76. package/dist/{oauth-callback-server-64QMI3IB.js → oauth-callback-server-PLPAFXO6.js} +23 -23
  77. package/dist/{runner-EIAE2BGD.js → runner-AGNEIOZS.js} +15 -15
  78. package/dist/{skills-ZADWFKDG.js → skills-U5KQ66EM.js} +6 -6
  79. package/dist/spawn-ZRYYZP2B.js +37 -0
  80. package/dist/{validation-4I6AF4CV.js → validation-QE4U45KS.js} +6 -6
  81. package/dist/version.js +1 -1
  82. package/dist/{work-BS6BZ66U.js → work-ATAXAHJJ.js} +18 -18
  83. package/dist/worker-LIL5AOER.js +24 -0
  84. package/migrations/0033_workspace_snapshot_active_build.sql +1 -0
  85. package/migrations/0034_strong_randall.sql +31 -0
  86. package/migrations/meta/0033_snapshot.json +2914 -0
  87. package/migrations/meta/0034_snapshot.json +3038 -0
  88. package/migrations/meta/_journal.json +15 -1
  89. package/package.json +5 -5
  90. package/dist/spawn-DVUOUFFC.js +0 -37
  91. package/dist/worker-RS5PWTCE.js +0 -24
package/dist/api.js CHANGED
@@ -41,12 +41,17 @@ import {
41
41
  workspaceListSchema,
42
42
  workspaceParamsSchema,
43
43
  workspaceSchema
44
- } from "./chunk-ELEAIRTG.js";
44
+ } from "./chunk-TKB25A3L.js";
45
45
  import {
46
46
  appendAndEnqueueApiConversationMessage,
47
47
  authenticatePersonalToken,
48
+ conversationArchiveTimes,
49
+ conversationArchivedForEmail,
50
+ conversationArchivedForUser,
48
51
  conversationHasParticipantEmail,
49
52
  conversationHasParticipantUser,
53
+ conversationNotArchivedForEmail,
54
+ conversationNotArchivedForUser,
50
55
  createAndEnqueueApiConversation,
51
56
  createPersonalToken,
52
57
  createVercelAttachmentStorage,
@@ -59,15 +64,15 @@ import {
59
64
  revokePersonalToken,
60
65
  viewerConversationMembership,
61
66
  webActorFromEmail
62
- } from "./chunk-AFOFVEXP.js";
67
+ } from "./chunk-PRDKO3QB.js";
63
68
  import {
64
69
  buildSentryConversationUrl,
65
70
  getWebAuthorization
66
- } from "./chunk-4SVAFAI2.js";
71
+ } from "./chunk-AVRWMQ4U.js";
67
72
  import {
68
73
  cancelHumanFacingPendingMessages,
69
74
  getConversation
70
- } from "./chunk-LFZODSAY.js";
75
+ } from "./chunk-BUGO42R6.js";
71
76
  import {
72
77
  readTaskExecutionByConversationId,
73
78
  readTaskExecutionDays,
@@ -75,8 +80,8 @@ import {
75
80
  readTaskExecutionSummaries,
76
81
  readTaskExecutions,
77
82
  readTaskRuns
78
- } from "./chunk-7ZFESKJ2.js";
79
- import "./chunk-4TWDRU64.js";
83
+ } from "./chunk-4K6VNP3S.js";
84
+ import "./chunk-G3CQEUHI.js";
80
85
  import {
81
86
  healthReportSchema,
82
87
  pluginOperationalReportFeedSchema,
@@ -101,19 +106,19 @@ import {
101
106
  listPublicScheduledTasksForTeams,
102
107
  readLiveAttachment,
103
108
  readStats
104
- } from "./chunk-4TRHPYTL.js";
109
+ } from "./chunk-RGV6KHDM.js";
105
110
  import {
106
111
  parseSlackThreadId
107
- } from "./chunk-3FWXQZJJ.js";
112
+ } from "./chunk-22T3HB44.js";
108
113
  import {
109
114
  agentTurnUsageSchema,
110
115
  fallbackShortTitle,
111
116
  hasAgentTurnUsage,
112
117
  renderJuniorNativeConversationEvent
113
- } from "./chunk-I7PKGMKI.js";
118
+ } from "./chunk-BFLMRTPD.js";
114
119
  import {
115
120
  getCachedSnapshot
116
- } from "./chunk-N4YEVOEC.js";
121
+ } from "./chunk-534BO42N.js";
117
122
  import "./chunk-PDO5BLNM.js";
118
123
  import {
119
124
  SANDBOX_RUNTIME,
@@ -134,7 +139,7 @@ import {
134
139
  updateWorkspace,
135
140
  withSlackRetries,
136
141
  workspaceRepoCheckoutPath
137
- } from "./chunk-PA7QDS7P.js";
142
+ } from "./chunk-XWUJWHFD.js";
138
143
  import "./chunk-3Y47VMHV.js";
139
144
  import {
140
145
  decodeStoredConversationEvent,
@@ -142,26 +147,29 @@ import {
142
147
  getDb,
143
148
  getSqlExecutor,
144
149
  locationFromRow,
145
- parseSessionSource
146
- } from "./chunk-LPMLAJYR.js";
150
+ parseSessionSource,
151
+ resolveRootConversationId
152
+ } from "./chunk-XCNS3CXE.js";
147
153
  import {
154
+ conversationReadColumns,
148
155
  juniorConversationEvents,
156
+ juniorConversationParticipants,
149
157
  juniorConversations,
150
158
  juniorIdentities,
151
159
  juniorUsers
152
- } from "./chunk-WCQUKX74.js";
160
+ } from "./chunk-ZCHHVTMF.js";
153
161
  import {
154
162
  create,
155
163
  hash
156
- } from "./chunk-Q362TECJ.js";
157
- import "./chunk-VGDOEKO2.js";
158
- import "./chunk-DLTP6TUJ.js";
164
+ } from "./chunk-AQHSXA3J.js";
165
+ import "./chunk-H6ZXBFGX.js";
166
+ import "./chunk-3ZZEPLLB.js";
159
167
  import {
160
168
  juniorDestinations
161
169
  } from "./chunk-VFUK3X5B.js";
162
- import "./chunk-7PZDAXIK.js";
163
- import "./chunk-J2EWL6GJ.js";
164
- import "./chunk-7B6NW4P7.js";
170
+ import "./chunk-KEQAA42D.js";
171
+ import "./chunk-ABLODWPO.js";
172
+ import "./chunk-D3UJTX4U.js";
165
173
  import "./chunk-CGA4ERJ5.js";
166
174
  import {
167
175
  parseSlackChannelId,
@@ -171,12 +179,12 @@ import {
171
179
  import {
172
180
  logException,
173
181
  logWarn
174
- } from "./chunk-7CVYXUOJ.js";
182
+ } from "./chunk-UGWBJAGN.js";
175
183
  import "./chunk-6GWA276C.js";
176
184
  import "./chunk-OOUNJNMR.js";
177
185
  import "./chunk-H4HBKISO.js";
178
- import "./chunk-ORMJY4RG.js";
179
- import "./chunk-SNPQGTHJ.js";
186
+ import "./chunk-AGJ4OQ2V.js";
187
+ import "./chunk-IQRPDKPJ.js";
180
188
  import "./chunk-MLKGABMK.js";
181
189
 
182
190
  // src/api.ts
@@ -231,33 +239,91 @@ var requireViewer = createMiddleware(
231
239
  );
232
240
 
233
241
  // src/api/conversations/archive.ts
234
- import { and, eq, isNotNull } from "drizzle-orm";
235
- async function archiveIfUnchanged(args) {
236
- const db = getDb();
237
- const rows = await db.update(juniorConversations).set({ archivedAt: args.archived ? /* @__PURE__ */ new Date() : null }).where(
242
+ import { and, eq, isNotNull, sql } from "drizzle-orm";
243
+ async function setViewerArchive(args) {
244
+ const executor = getSqlExecutor();
245
+ return await executor.transaction(async () => {
246
+ const db = executor.db();
247
+ const [root] = await db.select({ lastActivityAt: juniorConversations.lastActivityAt }).from(juniorConversations).where(eq(juniorConversations.conversationId, args.rootConversationId)).limit(1).for("update");
248
+ if (!root) return { status: "missing" };
249
+ if (args.archived) {
250
+ if (root.lastActivityAt.getTime() !== Date.parse(args.lastSeenAt)) {
251
+ return { status: "conflict" };
252
+ }
253
+ const archivedAt = /* @__PURE__ */ new Date();
254
+ const lastMessageAt = new Date(args.lastSeenAt);
255
+ await db.insert(juniorConversationParticipants).values({
256
+ archivedAt,
257
+ lastMessageAt,
258
+ rootConversationId: args.rootConversationId,
259
+ userId: args.userId
260
+ }).onConflictDoUpdate({
261
+ target: [
262
+ juniorConversationParticipants.userId,
263
+ juniorConversationParticipants.rootConversationId
264
+ ],
265
+ set: {
266
+ archivedAt,
267
+ lastMessageAt: sql`greatest(${juniorConversationParticipants.lastMessageAt}, excluded.last_message_at)`
268
+ }
269
+ });
270
+ return { archivedAt: archivedAt.toISOString(), status: "updated" };
271
+ }
272
+ await db.update(juniorConversationParticipants).set({ archivedAt: null }).where(
273
+ and(
274
+ eq(juniorConversationParticipants.userId, args.userId),
275
+ eq(
276
+ juniorConversationParticipants.rootConversationId,
277
+ args.rootConversationId
278
+ ),
279
+ isNotNull(juniorConversationParticipants.archivedAt)
280
+ )
281
+ );
282
+ return { archivedAt: null, status: "updated" };
283
+ });
284
+ }
285
+ async function readConversationArchivedAt(viewer, conversationId) {
286
+ const rootConversationId = await resolveRootConversationId(
287
+ getSqlExecutor(),
288
+ conversationId
289
+ );
290
+ const [row] = await getDb().select({ archivedAt: juniorConversationParticipants.archivedAt }).from(juniorConversationParticipants).where(
238
291
  and(
239
- eq(juniorConversations.conversationId, args.conversationId),
240
- args.archived ? eq(juniorConversations.lastActivityAt, new Date(args.lastSeenAt)) : isNotNull(juniorConversations.archivedAt)
292
+ eq(juniorConversationParticipants.userId, viewer.id),
293
+ eq(
294
+ juniorConversationParticipants.rootConversationId,
295
+ rootConversationId
296
+ )
241
297
  )
242
- ).returning({ conversationId: juniorConversations.conversationId });
243
- if (rows.length > 0) return "updated";
244
- const [existing] = await db.select({
245
- archivedAt: juniorConversations.archivedAt,
246
- conversationId: juniorConversations.conversationId
247
- }).from(juniorConversations).where(eq(juniorConversations.conversationId, args.conversationId)).limit(1);
248
- if (!existing) return "not_found";
249
- if (!args.archived && existing.archivedAt === null) return "updated";
250
- return "conflict";
251
- }
252
- async function archiveConversation(conversationId, body) {
253
- const result = await archiveIfUnchanged({ ...body, conversationId });
254
- if (result === "not_found") {
298
+ ).limit(1);
299
+ return row?.archivedAt?.getTime();
300
+ }
301
+ async function archiveConversation(viewer, conversationId, body) {
302
+ const access = await readConversationAccessFromSql(
303
+ getDb(),
304
+ [conversationId],
305
+ viewer
306
+ );
307
+ if (!access.get(conversationId)?.isParticipant) {
255
308
  throwApiError(404, "Conversation not found.");
256
309
  }
257
- if (result === "conflict") {
258
- throwApiError(409, "Conversation received new activity.");
310
+ const rootConversationId = await resolveRootConversationId(
311
+ getSqlExecutor(),
312
+ conversationId
313
+ );
314
+ const result = await setViewerArchive({
315
+ archived: body.archived,
316
+ lastSeenAt: body.lastSeenAt,
317
+ rootConversationId,
318
+ userId: viewer.id
319
+ });
320
+ if (result.status === "updated") {
321
+ return { archivedAt: result.archivedAt };
322
+ }
323
+ if (result.status === "missing") {
324
+ throwApiError(404, "Conversation not found.");
259
325
  }
260
- return { archived: body.archived };
326
+ throwApiError(409, "Conversation received new activity.");
261
327
  }
262
328
 
263
329
  // src/api/conversations/attachments.ts
@@ -480,31 +546,31 @@ async function resolveSlackTeamDomains(teamIds) {
480
546
  }
481
547
 
482
548
  // src/chat/pi/sql-model-usage.ts
483
- import { and as and2, eq as eq2, sql } from "drizzle-orm";
484
- var message = sql`${juniorConversationEvents.payload}`;
485
- var usage = sql`${message}->'usage'`;
486
- var cost = sql`${usage}->'cost'`;
549
+ import { and as and2, eq as eq2, sql as sql2 } from "drizzle-orm";
550
+ var message = sql2`${juniorConversationEvents.payload}`;
551
+ var usage = sql2`${message}->'usage'`;
552
+ var cost = sql2`${usage}->'cost'`;
487
553
  function nonnegativeNumber(source, field, fallbackField, floor = false) {
488
- const value = (name) => sql`case
554
+ const value = (name) => sql2`case
489
555
  when jsonb_typeof(${source}->${name}) = 'number'
490
556
  and (${source}->>${name})::numeric >= 0
491
- then ${floor ? sql`floor((${source}->>${name})::numeric)` : sql`(${source}->>${name})::numeric`}
557
+ then ${floor ? sql2`floor((${source}->>${name})::numeric)` : sql2`(${source}->>${name})::numeric`}
492
558
  end`;
493
559
  const primary = value(field);
494
- return fallbackField ? sql`coalesce(${primary}, ${value(fallbackField)})` : sql`${primary}`;
560
+ return fallbackField ? sql2`coalesce(${primary}, ${value(fallbackField)})` : sql2`${primary}`;
495
561
  }
496
562
  function token(field, fallbackField) {
497
563
  return nonnegativeNumber(usage, field, fallbackField, true);
498
564
  }
499
565
  function summed(value) {
500
- return sql`sum(${value})::double precision`;
566
+ return sql2`sum(${value})::double precision`;
501
567
  }
502
568
  function summedCost(value) {
503
- return sql`round(sum(${value}), 12)::double precision`;
569
+ return sql2`round(sum(${value}), 12)::double precision`;
504
570
  }
505
571
  function messageTotalTokens(args) {
506
572
  const { input, output, cacheRead, cacheWrite } = args;
507
- return sql`case
573
+ return sql2`case
508
574
  when ${input} is not null
509
575
  or ${output} is not null
510
576
  or ${cacheRead} is not null
@@ -547,7 +613,7 @@ function usageFromRow(row) {
547
613
  return hasAgentTurnUsage(result) ? agentTurnUsageSchema.parse(result) : void 0;
548
614
  }
549
615
  async function readConversationModelUsageFromSql(executor, options) {
550
- const modelId = sql`case
616
+ const modelId = sql2`case
551
617
  when position('/' in coalesce(${message}->>'model', '')) > 0
552
618
  then ${message}->>'model'
553
619
  else concat(${message}->>'provider', '/', ${message}->>'model')
@@ -586,10 +652,10 @@ async function readConversationModelUsageFromSql(executor, options) {
586
652
  and2(
587
653
  options.includeDescendants ? eq2(juniorConversations.rootConversationId, options.conversationId) : eq2(juniorConversationEvents.conversationId, options.conversationId),
588
654
  eq2(juniorConversationEvents.type, "assistant_message"),
589
- sql`jsonb_typeof(${message}->'provider') = 'string'`,
590
- sql`jsonb_typeof(${message}->'model') = 'string'`,
591
- sql`coalesce(${message}->>'provider', '') <> ''`,
592
- sql`coalesce(${message}->>'model', '') <> ''`
655
+ sql2`jsonb_typeof(${message}->'provider') = 'string'`,
656
+ sql2`jsonb_typeof(${message}->'model') = 'string'`,
657
+ sql2`coalesce(${message}->>'provider', '') <> ''`,
658
+ sql2`coalesce(${message}->>'model', '') <> ''`
593
659
  )
594
660
  ).groupBy(modelId).orderBy(modelId);
595
661
  return rows.flatMap((row) => {
@@ -654,7 +720,7 @@ import {
654
720
  getTableColumns,
655
721
  inArray,
656
722
  lt,
657
- sql as sql2
723
+ sql as sql3
658
724
  } from "drizzle-orm";
659
725
 
660
726
  // src/api/conversations/events.ts
@@ -1065,7 +1131,7 @@ async function readConversationEventRows(executor, args) {
1065
1131
  return executor.db().select({
1066
1132
  ...conversationEventColumns,
1067
1133
  // Replacement history is model context, never dashboard report data.
1068
- payload: sql2`case
1134
+ payload: sql3`case
1069
1135
  when ${juniorConversationEvents.type} in ('compaction', 'handoff')
1070
1136
  then jsonb_set(
1071
1137
  ${juniorConversationEvents.payload},
@@ -1083,11 +1149,11 @@ async function readConversationEventRows(executor, args) {
1083
1149
  args.types ?? [...conversationReportSourceEventTypes]
1084
1150
  ),
1085
1151
  args.subagentInvocationIds === void 0 ? void 0 : inArray(
1086
- sql2`${juniorConversationEvents.payload}->>'subagentInvocationId'`,
1152
+ sql3`${juniorConversationEvents.payload}->>'subagentInvocationId'`,
1087
1153
  args.subagentInvocationIds
1088
1154
  ),
1089
1155
  args.toolCallIds === void 0 ? void 0 : inArray(
1090
- sql2`${juniorConversationEvents.payload}->>'toolCallId'`,
1156
+ sql3`${juniorConversationEvents.payload}->>'toolCallId'`,
1091
1157
  args.toolCallIds
1092
1158
  )
1093
1159
  )
@@ -1167,7 +1233,7 @@ async function readConversationEventPage(executor, args) {
1167
1233
  }
1168
1234
 
1169
1235
  // src/api/conversations/list.ts
1170
- import { and as and7, asc as asc2, desc as desc2, eq as eq7, isNotNull as isNotNull4, isNull as isNull3 } from "drizzle-orm";
1236
+ import { and as and7, asc as asc2, desc as desc2, eq as eq7, isNull as isNull3, sql as sql7 } from "drizzle-orm";
1171
1237
 
1172
1238
  // src/api/conversations/shared.ts
1173
1239
  function slackStatsLocationLabel(input) {
@@ -1341,7 +1407,7 @@ function conversationSummaryFromStoredConversation(args) {
1341
1407
  ...usage3 ? { cumulativeUsage: usage3 } : {},
1342
1408
  ...actorIdentity ? { actorIdentity } : {},
1343
1409
  ...sourceUrl ? { sourceUrl } : {},
1344
- ...conversation.archivedAtMs ? { archivedAt: new Date(conversation.archivedAtMs).toISOString() } : {},
1410
+ archivedAt: args.archivedAtMs === void 0 ? null : new Date(args.archivedAtMs).toISOString(),
1345
1411
  ...slackThread ? { channel: slackThread.channelId } : {},
1346
1412
  ...channelName ? { channelName } : {},
1347
1413
  ...channelNameRedacted ? { channelNameRedacted: true } : {},
@@ -1350,7 +1416,7 @@ function conversationSummaryFromStoredConversation(args) {
1350
1416
  }
1351
1417
 
1352
1418
  // src/api/conversations/usage.ts
1353
- import { and as and4, eq as eq4, inArray as inArray2, isNull, sql as sql3 } from "drizzle-orm";
1419
+ import { and as and4, eq as eq4, inArray as inArray2, isNull, sql as sql4 } from "drizzle-orm";
1354
1420
  import { alias } from "drizzle-orm/pg-core";
1355
1421
  var usage2 = juniorConversations.usage;
1356
1422
  var metricRootConversation = alias(
@@ -1364,10 +1430,10 @@ var componentFields = [
1364
1430
  "cacheCreationTokens"
1365
1431
  ];
1366
1432
  function summedUsageNumber(field) {
1367
- return sql3`sum((${usage2}->>${field})::double precision)::double precision`;
1433
+ return sql4`sum((${usage2}->>${field})::double precision)::double precision`;
1368
1434
  }
1369
1435
  function summedCostNumber(field) {
1370
- return sql3`round(
1436
+ return sql4`round(
1371
1437
  sum((${usage2}->'cost'->>${field})::numeric),
1372
1438
  12
1373
1439
  )::double precision`;
@@ -1397,7 +1463,7 @@ function usageFromAggregate(row) {
1397
1463
  }
1398
1464
  async function readRootConversationMetricsFromSql(db, rootConversationIds) {
1399
1465
  if (rootConversationIds.length === 0) return /* @__PURE__ */ new Map();
1400
- const hasComponents = sql3`coalesce(
1466
+ const hasComponents = sql4`coalesce(
1401
1467
  ${usage2}->>'inputTokens',
1402
1468
  ${usage2}->>'outputTokens',
1403
1469
  ${usage2}->>'cachedInputTokens',
@@ -1405,12 +1471,12 @@ async function readRootConversationMetricsFromSql(db, rootConversationIds) {
1405
1471
  ) is not null`;
1406
1472
  const rows = await db.select({
1407
1473
  rootConversationId: juniorConversations.rootConversationId,
1408
- durationMs: sql3`coalesce(sum(${juniorConversations.durationMs}), 0)::double precision`,
1474
+ durationMs: sql4`coalesce(sum(${juniorConversations.durationMs}), 0)::double precision`,
1409
1475
  inputTokens: summedUsageNumber("inputTokens"),
1410
1476
  outputTokens: summedUsageNumber("outputTokens"),
1411
1477
  cachedInputTokens: summedUsageNumber("cachedInputTokens"),
1412
1478
  cacheCreationTokens: summedUsageNumber("cacheCreationTokens"),
1413
- totalOnlyTokens: sql3`sum(
1479
+ totalOnlyTokens: sql4`sum(
1414
1480
  case
1415
1481
  when not (${hasComponents})
1416
1482
  then (${usage2}->>'totalTokens')::double precision
@@ -1460,7 +1526,7 @@ import {
1460
1526
  isNotNull as isNotNull2,
1461
1527
  isNull as isNull2,
1462
1528
  or,
1463
- sql as sql4
1529
+ sql as sql5
1464
1530
  } from "drizzle-orm";
1465
1531
  import { alias as alias2 } from "drizzle-orm/pg-core";
1466
1532
  var auxiliaryRootConversation = alias2(
@@ -1468,7 +1534,7 @@ var auxiliaryRootConversation = alias2(
1468
1534
  "auxiliary_root_conversation"
1469
1535
  );
1470
1536
  function eventCost() {
1471
- return sql4`CASE
1537
+ return sql5`CASE
1472
1538
  WHEN ${juniorConversationEvents.type} = 'structured_event'
1473
1539
  AND jsonb_typeof(${juniorConversationEvents.payload}->'content'->'costUsd') = 'number'
1474
1540
  THEN (${juniorConversationEvents.payload}->'content'->>'costUsd')::numeric
@@ -1481,16 +1547,16 @@ function eventCost() {
1481
1547
  async function readConversationAuxiliaryCostsFromSql(db, conversationIds, options) {
1482
1548
  if (conversationIds.length === 0) return /* @__PURE__ */ new Map();
1483
1549
  const selectedIds = [...conversationIds];
1484
- const conversationId = options.includeDescendants ? sql4`coalesce(
1550
+ const conversationId = options.includeDescendants ? sql5`coalesce(
1485
1551
  ${auxiliaryRootConversation.conversationId},
1486
1552
  ${juniorConversations.conversationId}
1487
- )` : sql4`${juniorConversations.conversationId}`;
1488
- const namespace = sql4`CASE
1553
+ )` : sql5`${juniorConversations.conversationId}`;
1554
+ const namespace = sql5`CASE
1489
1555
  WHEN ${juniorConversationEvents.type} = 'structured_event'
1490
1556
  THEN ${juniorConversationEvents.payload}->>'namespace'
1491
1557
  ELSE 'junior'
1492
1558
  END`;
1493
- const name = sql4`CASE
1559
+ const name = sql5`CASE
1494
1560
  WHEN ${juniorConversationEvents.type} = 'structured_event'
1495
1561
  THEN ${juniorConversationEvents.payload}->>'name'
1496
1562
  ELSE ${juniorConversationEvents.type}
@@ -1505,10 +1571,10 @@ async function readConversationAuxiliaryCostsFromSql(db, conversationIds, option
1505
1571
  ) : inArray3(juniorConversations.conversationId, selectedIds);
1506
1572
  const rows = await db.select({
1507
1573
  conversationId,
1508
- costUsd: sql4`round(sum(${cost2}), 12)::double precision`.mapWith(
1574
+ costUsd: sql5`round(sum(${cost2}), 12)::double precision`.mapWith(
1509
1575
  Number
1510
1576
  ),
1511
- events: sql4`count(*)::integer`.mapWith(Number),
1577
+ events: sql5`count(*)::integer`.mapWith(Number),
1512
1578
  name,
1513
1579
  namespace
1514
1580
  }).from(juniorConversationEvents).innerJoin(
@@ -1540,7 +1606,7 @@ async function readConversationAuxiliaryCostsFromSql(db, conversationIds, option
1540
1606
  "turn_routed"
1541
1607
  ])
1542
1608
  ),
1543
- sql4`${cost2} >= 0`
1609
+ sql5`${cost2} >= 0`
1544
1610
  )
1545
1611
  ).groupBy(conversationId, namespace, name).orderBy(conversationId, namespace, name);
1546
1612
  const result = /* @__PURE__ */ new Map();
@@ -1687,15 +1753,15 @@ function isConversationPriority(conversation, nowMs) {
1687
1753
  }
1688
1754
 
1689
1755
  // src/api/conversations/user-message-activity.ts
1690
- import { and as and6, eq as eq6, inArray as inArray4, isNotNull as isNotNull3, or as or2, sql as sql5 } from "drizzle-orm";
1756
+ import { and as and6, eq as eq6, inArray as inArray4, isNotNull as isNotNull3, or as or2, sql as sql6 } from "drizzle-orm";
1691
1757
  async function readLastUserMessageAtByConversation(db, conversationIds) {
1692
1758
  if (conversationIds.length === 0) return /* @__PURE__ */ new Map();
1693
- const authority = sql5`${juniorConversationEvents.payload}->'provenance'->>'authority'`;
1694
- const actorPlatform = sql5`${juniorConversationEvents.payload}->'provenance'->'actor'->>'platform'`;
1695
- const role = sql5`${juniorConversationEvents.payload}->>'role'`;
1759
+ const authority = sql6`${juniorConversationEvents.payload}->'provenance'->>'authority'`;
1760
+ const actorPlatform = sql6`${juniorConversationEvents.payload}->'provenance'->'actor'->>'platform'`;
1761
+ const role = sql6`${juniorConversationEvents.payload}->>'role'`;
1696
1762
  const rows = await db.select({
1697
1763
  conversationId: juniorConversationEvents.conversationId,
1698
- lastUserMessageAt: sql5`max(${juniorConversationEvents.createdAt})`
1764
+ lastUserMessageAt: sql6`max(${juniorConversationEvents.createdAt})`
1699
1765
  }).from(juniorConversationEvents).where(
1700
1766
  and6(
1701
1767
  inArray4(juniorConversationEvents.conversationId, conversationIds),
@@ -1703,7 +1769,7 @@ async function readLastUserMessageAtByConversation(db, conversationIds) {
1703
1769
  and6(
1704
1770
  eq6(juniorConversationEvents.type, "user_message"),
1705
1771
  eq6(authority, "instruction"),
1706
- sql5`${actorPlatform} in ('slack', 'local', 'web')`
1772
+ sql6`${actorPlatform} in ('slack', 'local', 'web')`
1707
1773
  ),
1708
1774
  and6(
1709
1775
  eq6(juniorConversationEvents.type, "message"),
@@ -1723,22 +1789,28 @@ async function readLastUserMessageAtByConversation(db, conversationIds) {
1723
1789
 
1724
1790
  // src/api/conversations/list.ts
1725
1791
  var CONVERSATION_FEED_LIMIT = 50;
1726
- function conversationFeedMembershipFilter(filter) {
1727
- if (!filter) return void 0;
1792
+ function conversationFeedMembershipFilter(status, filter) {
1793
+ if (!filter) return status === "archived" ? sql7`false` : void 0;
1728
1794
  if (filter.kind === "viewer") {
1729
- return viewerConversationMembership({
1730
- actorMatch: eq7(juniorIdentities.userId, filter.userId),
1731
- participantMatch: conversationHasParticipantUser(filter.userId)
1732
- });
1795
+ return and7(
1796
+ viewerConversationMembership({
1797
+ actorMatch: eq7(juniorIdentities.userId, filter.userId),
1798
+ participantMatch: conversationHasParticipantUser(filter.userId)
1799
+ }),
1800
+ status === "archived" ? conversationArchivedForUser(filter.userId) : conversationNotArchivedForUser(filter.userId)
1801
+ );
1733
1802
  }
1734
- return viewerConversationMembership({
1735
- actorMatch: eq7(juniorUsers.primaryEmailNormalized, filter.email),
1736
- participantMatch: conversationHasParticipantEmail(filter.email)
1737
- });
1803
+ return and7(
1804
+ viewerConversationMembership({
1805
+ actorMatch: eq7(juniorUsers.primaryEmailNormalized, filter.email),
1806
+ participantMatch: conversationHasParticipantEmail(filter.email)
1807
+ }),
1808
+ status === "archived" ? conversationArchivedForEmail(filter.email) : conversationNotArchivedForEmail(filter.email)
1809
+ );
1738
1810
  }
1739
1811
  async function conversationRows(db, limit, status, filter) {
1740
1812
  return db.select({
1741
- conversation: juniorConversations,
1813
+ conversation: conversationReadColumns(),
1742
1814
  destination: juniorDestinations,
1743
1815
  identityDisplayName: juniorIdentities.displayName,
1744
1816
  identityEmail: juniorIdentities.email,
@@ -1756,8 +1828,7 @@ async function conversationRows(db, limit, status, filter) {
1756
1828
  ).leftJoin(juniorUsers, eq7(juniorUsers.id, juniorIdentities.userId)).where(
1757
1829
  and7(
1758
1830
  isNull3(juniorConversations.parentConversationId),
1759
- status === "archived" ? isNotNull4(juniorConversations.archivedAt) : isNull3(juniorConversations.archivedAt),
1760
- conversationFeedMembershipFilter(filter)
1831
+ conversationFeedMembershipFilter(status, filter)
1761
1832
  )
1762
1833
  ).orderBy(
1763
1834
  desc2(juniorConversations.lastActivityAt),
@@ -1793,7 +1864,6 @@ function conversationFromRow(row) {
1793
1864
  },
1794
1865
  ...actor ? { actor } : {},
1795
1866
  ...location ? { location } : {},
1796
- ...value.archivedAt ? { archivedAtMs: value.archivedAt.getTime() } : {},
1797
1867
  ...value.channelName ? { channelName: value.channelName } : {},
1798
1868
  ...value.source ? { source: value.source } : {},
1799
1869
  ...sessionSource ? { sessionSource } : {},
@@ -1804,7 +1874,7 @@ function conversationFromRow(row) {
1804
1874
  async function readConversationRecordFromSql(conversationId) {
1805
1875
  const db = getDb();
1806
1876
  const rows = await db.select({
1807
- conversation: juniorConversations,
1877
+ conversation: conversationReadColumns(),
1808
1878
  destination: juniorDestinations,
1809
1879
  identityDisplayName: juniorIdentities.displayName,
1810
1880
  identityEmail: juniorIdentities.email,
@@ -1847,11 +1917,12 @@ function conversationFeedFilter(options) {
1847
1917
  async function readConversationFeedFromSql(options = {}) {
1848
1918
  const nowMs = Date.now();
1849
1919
  const db = getDb();
1920
+ const filter = conversationFeedFilter(options);
1850
1921
  const rows = await conversationRows(
1851
1922
  db,
1852
1923
  options.limit ?? CONVERSATION_FEED_LIMIT,
1853
1924
  options.status ?? "active",
1854
- conversationFeedFilter(options)
1925
+ filter
1855
1926
  );
1856
1927
  const conversations = rows.map((row) => conversationFromRow(row));
1857
1928
  const conversationIds = conversations.map(
@@ -1859,6 +1930,7 @@ async function readConversationFeedFromSql(options = {}) {
1859
1930
  );
1860
1931
  const [
1861
1932
  accessByConversation,
1933
+ archiveTimesByConversation,
1862
1934
  annotationsByConversation,
1863
1935
  auxiliaryCostsByRoot,
1864
1936
  metricsByRoot,
@@ -1867,6 +1939,11 @@ async function readConversationFeedFromSql(options = {}) {
1867
1939
  lastUserMessageAtByConversation
1868
1940
  ] = await Promise.all([
1869
1941
  readConversationAccessFromSql(db, conversationIds, options.viewer),
1942
+ conversationArchiveTimes(
1943
+ db,
1944
+ conversationIds,
1945
+ filter?.kind === "viewer" ? { userId: filter.userId } : filter?.kind === "actorEmail" ? { email: filter.email } : void 0
1946
+ ),
1870
1947
  listConversationAnnotationsById(db, conversationIds),
1871
1948
  readConversationAuxiliaryCostsFromSql(db, conversationIds, {
1872
1949
  includeDescendants: true
@@ -1897,7 +1974,11 @@ async function readConversationFeedFromSql(options = {}) {
1897
1974
  const row = rows[index];
1898
1975
  const access = accessByConversation.get(conversation.conversationId);
1899
1976
  const metrics = metricsByRoot.get(conversation.conversationId);
1977
+ const archivedAtMs = archiveTimesByConversation.get(
1978
+ conversation.conversationId
1979
+ );
1900
1980
  const summary = conversationSummaryFromStoredConversation({
1981
+ ...archivedAtMs === void 0 ? {} : { archivedAtMs },
1901
1982
  conversation,
1902
1983
  access,
1903
1984
  auxiliaryCosts: auxiliaryCostsByRoot.get(conversation.conversationId),
@@ -2480,6 +2561,7 @@ function projectConversationDetail(args) {
2480
2561
  return {
2481
2562
  ...conversationSummaryFromStoredConversation({
2482
2563
  access: args.access,
2564
+ ...args.archivedAtMs === void 0 ? {} : { archivedAtMs: args.archivedAtMs },
2483
2565
  auxiliaryCosts: args.auxiliaryCosts,
2484
2566
  conversation,
2485
2567
  durationMs: args.durationMs,
@@ -2508,6 +2590,7 @@ function projectConversationDetail(args) {
2508
2590
  async function readConversationDetailFromSql(conversationId, options) {
2509
2591
  const record = await readConversationRecordFromSql(conversationId);
2510
2592
  if (!record) return void 0;
2593
+ const archivedAtMs = options.viewer ? await readConversationArchivedAt(options.viewer, conversationId) : void 0;
2511
2594
  const executor = getSqlExecutor();
2512
2595
  const includeDescendantMetrics = record.rootConversationId === conversationId;
2513
2596
  const [
@@ -2550,6 +2633,7 @@ async function readConversationDetailFromSql(conversationId, options) {
2550
2633
  return projectConversationDetail({
2551
2634
  ...record,
2552
2635
  access,
2636
+ ...archivedAtMs === void 0 ? {} : { archivedAtMs },
2553
2637
  annotations,
2554
2638
  auxiliaryCosts: auxiliaryCostsByConversation.get(conversationId),
2555
2639
  durationMs: metrics?.durationMs ?? record.durationMs,
@@ -2791,20 +2875,20 @@ async function requireConversationPendingMessages(conversationId, options = {})
2791
2875
  }
2792
2876
 
2793
2877
  // src/api/conversations/stats.query.ts
2794
- import { and as and9, eq as eq9, gte, isNull as isNull4, lte, sql as sql7 } from "drizzle-orm";
2878
+ import { and as and9, eq as eq9, gte, isNull as isNull4, lte, sql as sql9 } from "drizzle-orm";
2795
2879
  import { alias as alias3 } from "drizzle-orm/pg-core";
2796
2880
 
2797
2881
  // src/api/conversations/aggregate.ts
2798
- import { sql as sql6 } from "drizzle-orm";
2882
+ import { sql as sql8 } from "drizzle-orm";
2799
2883
  function usageTokenValue(source, field) {
2800
- return sql6`CASE
2884
+ return sql8`CASE
2801
2885
  WHEN ${source.usage}->>${field} IS NOT NULL
2802
2886
  THEN (${source.usage}->>${field})::double precision
2803
2887
  ELSE NULL
2804
2888
  END`;
2805
2889
  }
2806
2890
  function tokenValue(source) {
2807
- return sql6`
2891
+ return sql8`
2808
2892
  CASE
2809
2893
  WHEN COALESCE(
2810
2894
  ${source.usage}->>'inputTokens',
@@ -2823,7 +2907,7 @@ function tokenValue(source) {
2823
2907
  `;
2824
2908
  }
2825
2909
  function costValue(source) {
2826
- return sql6`
2910
+ return sql8`
2827
2911
  CASE
2828
2912
  WHEN ${source.usage}->'cost'->>'total' IS NOT NULL
2829
2913
  THEN (${source.usage}->'cost'->>'total')::double precision
@@ -2844,34 +2928,34 @@ function costValue(source) {
2844
2928
  function conversationAggregateColumns(sources) {
2845
2929
  const metrics = sources?.metrics ?? juniorConversations;
2846
2930
  const roots = sources?.roots ?? juniorConversations;
2847
- const conversationCount = sources ? sql6`COUNT(DISTINCT ${roots.conversationId})` : sql6`COUNT(*)`;
2931
+ const conversationCount = sources ? sql8`COUNT(DISTINCT ${roots.conversationId})` : sql8`COUNT(*)`;
2848
2932
  return {
2849
- active: sql6`${conversationCount} FILTER (
2933
+ active: sql8`${conversationCount} FILTER (
2850
2934
  WHERE ${roots.executionStatus} NOT IN ('idle', 'failed')
2851
2935
  )::integer`,
2852
- conversations: sql6`${conversationCount}::integer`,
2853
- cachedInputTokens: sql6`SUM(${usageTokenValue(metrics, "cachedInputTokens")})::double precision`,
2854
- costUsd: sql6`SUM(${costValue(metrics)})::double precision`,
2855
- durationMs: sql6`COALESCE(SUM(${metrics.durationMs}), 0)::double precision`,
2856
- failed: sql6`${conversationCount} FILTER (
2936
+ conversations: sql8`${conversationCount}::integer`,
2937
+ cachedInputTokens: sql8`SUM(${usageTokenValue(metrics, "cachedInputTokens")})::double precision`,
2938
+ costUsd: sql8`SUM(${costValue(metrics)})::double precision`,
2939
+ durationMs: sql8`COALESCE(SUM(${metrics.durationMs}), 0)::double precision`,
2940
+ failed: sql8`${conversationCount} FILTER (
2857
2941
  WHERE ${roots.executionStatus} = 'failed'
2858
2942
  )::integer`,
2859
- inputTokens: sql6`SUM(${usageTokenValue(metrics, "inputTokens")})::double precision`,
2860
- tokens: sql6`SUM(${tokenValue(metrics)})::double precision`
2943
+ inputTokens: sql8`SUM(${usageTokenValue(metrics, "inputTokens")})::double precision`,
2944
+ tokens: sql8`SUM(${tokenValue(metrics)})::double precision`
2861
2945
  };
2862
2946
  }
2863
2947
  function conversationRangeColumns() {
2864
2948
  return {
2865
- firstSeenAt: sql6`MIN(${juniorConversations.createdAt})`.mapWith(
2949
+ firstSeenAt: sql8`MIN(${juniorConversations.createdAt})`.mapWith(
2866
2950
  juniorConversations.createdAt
2867
2951
  ),
2868
- lastSeenAt: sql6`MAX(${juniorConversations.lastActivityAt})`.mapWith(
2952
+ lastSeenAt: sql8`MAX(${juniorConversations.lastActivityAt})`.mapWith(
2869
2953
  juniorConversations.lastActivityAt
2870
2954
  )
2871
2955
  };
2872
2956
  }
2873
2957
  function conversationActiveDaysColumn() {
2874
- return sql6`COUNT(DISTINCT (${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC')::date)::integer`;
2958
+ return sql8`COUNT(DISTINCT (${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC')::date)::integer`;
2875
2959
  }
2876
2960
 
2877
2961
  // src/api/conversations/stats.query.ts
@@ -3012,7 +3096,7 @@ function guardianStats(rows, endMs) {
3012
3096
  }
3013
3097
  async function aggregateStats(db, start, end) {
3014
3098
  const where = statsWhere(start, end);
3015
- const activityDate = sql7`TO_CHAR(
3099
+ const activityDate = sql9`TO_CHAR(
3016
3100
  ${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC',
3017
3101
  'YYYY-MM-DD'
3018
3102
  )`;
@@ -3093,23 +3177,23 @@ async function aggregateStats(db, start, end) {
3093
3177
  return { actorRows, locationRows, metricRows, totals: totalsRows[0] };
3094
3178
  }
3095
3179
  async function aggregateGuardianStats(db, start, end) {
3096
- const date = sql7`TO_CHAR(
3180
+ const date = sql9`TO_CHAR(
3097
3181
  ${juniorConversationEvents.createdAt} AT TIME ZONE 'UTC',
3098
3182
  'YYYY-MM-DD'
3099
3183
  )`;
3100
- const decision = sql7`${juniorConversationEvents.payload}->>'decision'`;
3101
- const cost2 = sql7`CASE
3184
+ const decision = sql9`${juniorConversationEvents.payload}->>'decision'`;
3185
+ const cost2 = sql9`CASE
3102
3186
  WHEN jsonb_typeof(${juniorConversationEvents.payload}->'costUsd') = 'number'
3103
3187
  THEN (${juniorConversationEvents.payload}->>'costUsd')::double precision
3104
3188
  ELSE NULL
3105
3189
  END`;
3106
3190
  return await db.select({
3107
- allow: sql7`COUNT(*) FILTER (WHERE ${decision} = 'allow')::integer`,
3108
- ask: sql7`COUNT(*) FILTER (WHERE ${decision} = 'ask')::integer`,
3109
- costUsd: sql7`SUM(${cost2})::double precision`,
3191
+ allow: sql9`COUNT(*) FILTER (WHERE ${decision} = 'allow')::integer`,
3192
+ ask: sql9`COUNT(*) FILTER (WHERE ${decision} = 'ask')::integer`,
3193
+ costUsd: sql9`SUM(${cost2})::double precision`,
3110
3194
  date,
3111
- deny: sql7`COUNT(*) FILTER (WHERE ${decision} = 'deny')::integer`,
3112
- requests: sql7`COUNT(*)::integer`
3195
+ deny: sql9`COUNT(*) FILTER (WHERE ${decision} = 'deny')::integer`,
3196
+ requests: sql9`COUNT(*)::integer`
3113
3197
  }).from(juniorConversationEvents).where(
3114
3198
  and9(
3115
3199
  eq9(juniorConversationEvents.type, "guardian_action_reviewed"),
@@ -3230,6 +3314,7 @@ function createConversationRoutes(options) {
3230
3314
  );
3231
3315
  app.patch(
3232
3316
  "/:conversationId/archive",
3317
+ requireViewer,
3233
3318
  validateRequest(
3234
3319
  "param",
3235
3320
  conversationParamsSchema,
@@ -3241,11 +3326,12 @@ function createConversationRoutes(options) {
3241
3326
  "Invalid request body."
3242
3327
  ),
3243
3328
  async (context) => {
3329
+ const viewer = context.get("viewer");
3244
3330
  const { conversationId } = context.req.valid("param");
3245
3331
  const body = context.req.valid("json");
3246
3332
  return jsonResponse(
3247
3333
  archiveConversationResponseSchema,
3248
- await archiveConversation(conversationId, body)
3334
+ await archiveConversation(viewer, conversationId, body)
3249
3335
  );
3250
3336
  }
3251
3337
  );
@@ -3374,7 +3460,7 @@ function createConversationRoutes(options) {
3374
3460
  import { Hono as Hono2 } from "hono";
3375
3461
 
3376
3462
  // src/api/locations/query.ts
3377
- import { and as and10, asc as asc3, desc as desc3, eq as eq10, gte as gte2, isNull as isNull5, sql as sql8 } from "drizzle-orm";
3463
+ import { and as and10, asc as asc3, desc as desc3, eq as eq10, gte as gte2, isNull as isNull5, sql as sql10 } from "drizzle-orm";
3378
3464
  import { alias as alias4 } from "drizzle-orm/pg-core";
3379
3465
 
3380
3466
  // src/api/conversations/reporting.ts
@@ -3546,12 +3632,12 @@ async function directoryRows(db) {
3546
3632
  ).where(topLevelWhere()).groupBy(...locationGroupBy());
3547
3633
  }
3548
3634
  async function directoryActivityRows(db, start) {
3549
- const date = sql8`TO_CHAR(
3635
+ const date = sql10`TO_CHAR(
3550
3636
  ${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC',
3551
3637
  'YYYY-MM-DD'
3552
3638
  )`;
3553
3639
  return db.select({
3554
- conversations: sql8`COUNT(*)::integer`,
3640
+ conversations: sql10`COUNT(*)::integer`,
3555
3641
  date,
3556
3642
  visibility: juniorDestinations.visibility
3557
3643
  }).from(juniorConversations).leftJoin(
@@ -3630,7 +3716,7 @@ async function recentLocationRows(db, locationId) {
3630
3716
  createdAt: juniorConversations.createdAt,
3631
3717
  destinationId: juniorDestinations.id,
3632
3718
  durationMs: juniorConversations.durationMs,
3633
- email: sql8`COALESCE(
3719
+ email: sql10`COALESCE(
3634
3720
  ${juniorUsers.primaryEmailNormalized},
3635
3721
  ${juniorIdentities.email}
3636
3722
  )`,
@@ -3639,7 +3725,7 @@ async function recentLocationRows(db, locationId) {
3639
3725
  fullName: juniorUsers.displayName,
3640
3726
  handle: juniorIdentities.handle,
3641
3727
  lastActivityAt: juniorConversations.lastActivityAt,
3642
- providerSubjectId: sql8`CASE
3728
+ providerSubjectId: sql10`CASE
3643
3729
  WHEN ${juniorIdentities.provider} = 'slack'
3644
3730
  THEN ${juniorIdentities.providerSubjectId}
3645
3731
  ELSE NULL
@@ -3654,12 +3740,7 @@ async function recentLocationRows(db, locationId) {
3654
3740
  ).leftJoin(
3655
3741
  juniorIdentities,
3656
3742
  eq10(juniorIdentities.id, juniorConversations.actorIdentityId)
3657
- ).leftJoin(juniorUsers, eq10(juniorUsers.id, juniorIdentities.userId)).where(
3658
- and10(
3659
- publicLocationWhere(locationId),
3660
- isNull5(juniorConversations.archivedAt)
3661
- )
3662
- ).orderBy(
3743
+ ).leftJoin(juniorUsers, eq10(juniorUsers.id, juniorIdentities.userId)).where(publicLocationWhere(locationId)).orderBy(
3663
3744
  desc3(juniorConversations.lastActivityAt),
3664
3745
  asc3(juniorConversations.conversationId)
3665
3746
  ).limit(RECENT_LIMIT);
@@ -3671,7 +3752,7 @@ async function readLocationDetailFromSql(locationId, options = {}) {
3671
3752
  const start = new Date(end);
3672
3753
  start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS - 1));
3673
3754
  const where = publicLocationWhere(locationId);
3674
- const activityDate = sql8`TO_CHAR(
3755
+ const activityDate = sql10`TO_CHAR(
3675
3756
  ${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC',
3676
3757
  'YYYY-MM-DD'
3677
3758
  )`;
@@ -3837,10 +3918,10 @@ function createLocationRoutes() {
3837
3918
  import { Hono as Hono3 } from "hono";
3838
3919
 
3839
3920
  // src/api/people/list.query.ts
3840
- import { and as and12, eq as eq12, gte as gte3, sql as sql10 } from "drizzle-orm";
3921
+ import { and as and12, eq as eq12, gte as gte3, sql as sql12 } from "drizzle-orm";
3841
3922
 
3842
3923
  // src/api/people/shared.ts
3843
- import { and as and11, asc as asc4, desc as desc4, eq as eq11, isNull as isNull6, notExists, or as or4, sql as sql9 } from "drizzle-orm";
3924
+ import { and as and11, asc as asc4, desc as desc4, eq as eq11, isNull as isNull6, notExists, or as or4, sql as sql11 } from "drizzle-orm";
3844
3925
  import { alias as alias5 } from "drizzle-orm/pg-core";
3845
3926
  var RECENT_LIMIT2 = 25;
3846
3927
  var ACTIVITY_DAYS2 = 365;
@@ -3943,7 +4024,7 @@ function verifiedActorWhere(email) {
3943
4024
  return and11(
3944
4025
  eq11(juniorIdentities.provider, "slack"),
3945
4026
  eq11(juniorIdentities.emailVerified, true),
3946
- sql9`${juniorUsers.primaryEmailNormalized} IS NOT NULL`,
4027
+ sql11`${juniorUsers.primaryEmailNormalized} IS NOT NULL`,
3947
4028
  normalizedEmail ? eq11(juniorUsers.primaryEmailNormalized, normalizedEmail) : void 0
3948
4029
  );
3949
4030
  }
@@ -3971,9 +4052,7 @@ async function recentActorRows(email) {
3971
4052
  ).innerJoin(juniorUsers, eq11(juniorUsers.id, juniorIdentities.userId)).leftJoin(
3972
4053
  juniorDestinations,
3973
4054
  eq11(juniorDestinations.id, juniorConversations.destinationId)
3974
- ).where(
3975
- and11(verifiedActorWhere(email), isNull6(juniorConversations.archivedAt))
3976
- ).orderBy(
4055
+ ).where(verifiedActorWhere(email)).orderBy(
3977
4056
  desc4(juniorConversations.lastActivityAt),
3978
4057
  asc4(juniorConversations.conversationId)
3979
4058
  ).limit(RECENT_LIMIT2);
@@ -4001,7 +4080,7 @@ function directoryActivityDays2(rows, nowMs) {
4001
4080
  async function readPeopleListFromSql() {
4002
4081
  const nowMs = Date.now();
4003
4082
  const { end, start } = activityWindow(nowMs);
4004
- const activityDate = sql10`TO_CHAR(
4083
+ const activityDate = sql12`TO_CHAR(
4005
4084
  ${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC',
4006
4085
  'YYYY-MM-DD'
4007
4086
  )`;
@@ -4009,8 +4088,8 @@ async function readPeopleListFromSql() {
4009
4088
  getDb().select({
4010
4089
  email: juniorUsers.primaryEmailNormalized,
4011
4090
  fullName: juniorUsers.displayName,
4012
- slackUserId: sql10`MAX(${juniorIdentities.providerSubjectId})`,
4013
- slackUserName: sql10`MAX(${juniorIdentities.handle})`,
4091
+ slackUserId: sql12`MAX(${juniorIdentities.providerSubjectId})`,
4092
+ slackUserName: sql12`MAX(${juniorIdentities.handle})`,
4014
4093
  activeDays: conversationActiveDaysColumn(),
4015
4094
  ...peopleTreeAggregateColumns,
4016
4095
  ...conversationRangeColumns()
@@ -4019,8 +4098,8 @@ async function readPeopleListFromSql() {
4019
4098
  eq12(juniorIdentities.id, juniorConversations.actorIdentityId)
4020
4099
  ).innerJoin(juniorUsers, eq12(juniorUsers.id, juniorIdentities.userId)).leftJoin(peopleTreeConversation, peopleTreeMetricsJoin()).where(verifiedActorWhere()).groupBy(juniorUsers.primaryEmailNormalized, juniorUsers.displayName),
4021
4100
  getDb().select({
4022
- activePeople: sql10`COUNT(DISTINCT ${juniorUsers.id})::int`,
4023
- conversations: sql10`COUNT(*)::int`,
4101
+ activePeople: sql12`COUNT(DISTINCT ${juniorUsers.id})::int`,
4102
+ conversations: sql12`COUNT(*)::int`,
4024
4103
  date: activityDate
4025
4104
  }).from(juniorConversations).innerJoin(
4026
4105
  juniorIdentities,
@@ -4079,7 +4158,7 @@ async function readPeoplePluginReports(args) {
4079
4158
  source: "plugins"
4080
4159
  });
4081
4160
  }
4082
- const { getPluginProfileReports } = await import("./agent-hooks-IYHCN7CO.js");
4161
+ const { getPluginProfileReports } = await import("./agent-hooks-PBT4NNBV.js");
4083
4162
  return pluginOperationalReportFeedSchema.parse({
4084
4163
  generatedAt: new Date(nowMs).toISOString(),
4085
4164
  reports: await getPluginProfileReports({
@@ -4092,7 +4171,7 @@ async function readPeoplePluginReports(args) {
4092
4171
  }
4093
4172
 
4094
4173
  // src/api/people/profile.query.ts
4095
- import { and as and13, eq as eq13, gte as gte4, sql as sql11 } from "drizzle-orm";
4174
+ import { and as and13, eq as eq13, gte as gte4, sql as sql13 } from "drizzle-orm";
4096
4175
  function emptyProfile(email, nowMs) {
4097
4176
  const end = new Date(nowMs);
4098
4177
  end.setUTCHours(0, 0, 0, 0);
@@ -4127,7 +4206,7 @@ function addAggregate2(map, label, row) {
4127
4206
  map.set(label, item);
4128
4207
  }
4129
4208
  function surfaceExpression() {
4130
- return sql11`CASE
4209
+ return sql13`CASE
4131
4210
  WHEN ${juniorConversations.source} IN ('api', 'scheduler', 'slack')
4132
4211
  THEN ${juniorConversations.source}
4133
4212
  WHEN ${juniorConversations.conversationId} LIKE 'slack:%' THEN 'slack'
@@ -4156,17 +4235,17 @@ async function readPeopleProfileFromSql(email, options = {}) {
4156
4235
  const where = verifiedActorWhere(normalizedEmail);
4157
4236
  const metricsJoin = peopleTreeMetricsJoin();
4158
4237
  const surface = surfaceExpression();
4159
- const activityDate = sql11`TO_CHAR(
4238
+ const activityDate = sql13`TO_CHAR(
4160
4239
  ${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC',
4161
4240
  'YYYY-MM-DD'
4162
4241
  )`;
4163
- const channel = sql11`SPLIT_PART(${juniorConversations.conversationId}, ':', 2)`;
4242
+ const channel = sql13`SPLIT_PART(${juniorConversations.conversationId}, ':', 2)`;
4164
4243
  const [totalsRows, dayRows, locationRows, surfaceRows, recentRows] = await Promise.all([
4165
4244
  getDb().select({
4166
4245
  email: juniorUsers.primaryEmailNormalized,
4167
4246
  fullName: juniorUsers.displayName,
4168
- slackUserId: sql11`MAX(${juniorIdentities.providerSubjectId})`,
4169
- slackUserName: sql11`MAX(${juniorIdentities.handle})`,
4247
+ slackUserId: sql13`MAX(${juniorIdentities.providerSubjectId})`,
4248
+ slackUserName: sql13`MAX(${juniorIdentities.handle})`,
4170
4249
  activeDays: conversationActiveDaysColumn(),
4171
4250
  ...peopleTreeAggregateColumns
4172
4251
  }).from(juniorConversations).innerJoin(
@@ -4279,7 +4358,7 @@ async function readPeopleProfile(email, options = {}) {
4279
4358
  }
4280
4359
 
4281
4360
  // src/api/people/spend.query.ts
4282
- import { and as and14, eq as eq14, gte as gte5, lte as lte2, sql as sql12 } from "drizzle-orm";
4361
+ import { and as and14, eq as eq14, gte as gte5, lte as lte2, sql as sql14 } from "drizzle-orm";
4283
4362
  var SEVEN_DAY_OFFSET = 6;
4284
4363
  var THIRTY_DAY_OFFSET = 29;
4285
4364
  function addUsd2(current, next) {
@@ -4298,7 +4377,7 @@ function spendWindow(nowMs) {
4298
4377
  async function readPersonalSpendFromSql(email, nowMs = Date.now()) {
4299
4378
  const normalizedEmail = normalizeEmail(email);
4300
4379
  const { end, sevenDayStart, thirtyDayStart } = spendWindow(nowMs);
4301
- const activityDate = sql12`TO_CHAR(
4380
+ const activityDate = sql14`TO_CHAR(
4302
4381
  ${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC',
4303
4382
  'YYYY-MM-DD'
4304
4383
  )`;
@@ -4798,7 +4877,7 @@ function readDescriptionText(home) {
4798
4877
  }
4799
4878
  }
4800
4879
  async function readSkillReports() {
4801
- const { discoverSkills } = await import("./skills-ZADWFKDG.js");
4880
+ const { discoverSkills } = await import("./skills-U5KQ66EM.js");
4802
4881
  const skills = await discoverSkills();
4803
4882
  return skillReportsSchema.parse(
4804
4883
  skills.map((skill) => ({
@@ -4808,7 +4887,7 @@ async function readSkillReports() {
4808
4887
  );
4809
4888
  }
4810
4889
  async function readPlugins() {
4811
- const { pluginCatalogRuntime } = await import("./catalog-runtime-2TD4CXSB.js");
4890
+ const { pluginCatalogRuntime } = await import("./catalog-runtime-M7QGKPFZ.js");
4812
4891
  return pluginsSchema.parse(
4813
4892
  pluginCatalogRuntime.getProviders().map((plugin) => ({
4814
4893
  configKeys: plugin.manifest.configKeys ?? [],
@@ -4821,7 +4900,7 @@ async function readPlugins() {
4821
4900
  async function readRuntimeInfoReport() {
4822
4901
  const [{ homeDir }, { pluginCatalogRuntime }, plugins, skills] = await Promise.all([
4823
4902
  import("./discovery-GUFTFBTL.js"),
4824
- import("./catalog-runtime-2TD4CXSB.js"),
4903
+ import("./catalog-runtime-M7QGKPFZ.js"),
4825
4904
  readPlugins(),
4826
4905
  readSkillReports()
4827
4906
  ]);
@@ -4837,7 +4916,7 @@ async function readRuntimeInfoReport() {
4837
4916
  }
4838
4917
  async function readPluginOperationalReportFeed() {
4839
4918
  const nowMs = Date.now();
4840
- const { getPluginOperationalReports } = await import("./agent-hooks-IYHCN7CO.js");
4919
+ const { getPluginOperationalReports } = await import("./agent-hooks-PBT4NNBV.js");
4841
4920
  return pluginOperationalReportFeedSchema.parse({
4842
4921
  source: "plugins",
4843
4922
  generatedAt: new Date(nowMs).toISOString(),