@sentry/junior 0.112.0 → 0.113.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 (49) hide show
  1. package/dist/{agent-hooks-6MVTPKP4.js → agent-hooks-BKEMWR3Z.js} +8 -8
  2. package/dist/api/conversations/cursor.d.ts +17 -0
  3. package/dist/api/conversations/detail.d.ts +16 -18
  4. package/dist/api/conversations/event-list.d.ts +91 -0
  5. package/dist/api/conversations/event-page.d.ts +18 -0
  6. package/dist/api/conversations/events.d.ts +5 -1
  7. package/dist/api/conversations/projection.d.ts +6 -1
  8. package/dist/api/schema/conversation.d.ts +132 -51
  9. package/dist/api/schema.d.ts +2 -2
  10. package/dist/api/schema.js +7 -1
  11. package/dist/api.js +444 -138
  12. package/dist/app.js +20 -20
  13. package/dist/{catalog-runtime-UEO25P2J.js → catalog-runtime-6XYOUVRZ.js} +5 -5
  14. package/dist/chat/pi/transcript.d.ts +2 -0
  15. package/dist/chat/services/provider-error.d.ts +39 -0
  16. package/dist/chat/services/provider-retry.d.ts +3 -6
  17. package/dist/chat/task-execution/state.d.ts +2 -2
  18. package/dist/chat/task-execution/store.d.ts +1 -1
  19. package/dist/{chunk-CSS7JHEM.js → chunk-36UJA3OD.js} +1 -1
  20. package/dist/{chunk-4EJO4HIC.js → chunk-3CTIAKSS.js} +1 -1
  21. package/dist/{chunk-66IP6WED.js → chunk-4VKFQPCW.js} +4 -4
  22. package/dist/{chunk-GJG67YFS.js → chunk-4WZ7YKDF.js} +2 -2
  23. package/dist/{chunk-SKEMI4IF.js → chunk-DXRPHBOU.js} +132 -23
  24. package/dist/{chunk-GR4IB6OQ.js → chunk-H5ON7XTE.js} +73 -28
  25. package/dist/{chunk-X3SP5RDW.js → chunk-HMRVILWZ.js} +1 -1
  26. package/dist/{chunk-2FJ6WEYV.js → chunk-K5BXPM74.js} +1 -1
  27. package/dist/{chunk-QIALGHRN.js → chunk-LLKDNPYV.js} +2 -2
  28. package/dist/{chunk-ZHP4KNF7.js → chunk-LR2SKL5Y.js} +1 -1
  29. package/dist/{chunk-AU2JVUDF.js → chunk-NP7TYFYR.js} +1 -1
  30. package/dist/{chunk-TZU3R5OC.js → chunk-OXE5ZUTS.js} +1 -1
  31. package/dist/{chunk-NDQL4ESR.js → chunk-QTS66GMR.js} +2 -2
  32. package/dist/{chunk-PK4ETCUQ.js → chunk-RCBUZGIT.js} +217 -199
  33. package/dist/{chunk-JE23YR3X.js → chunk-UKGNSJQQ.js} +50 -42
  34. package/dist/{chunk-AWNDKI5K.js → chunk-WBB22ANL.js} +20 -15
  35. package/dist/{chunk-VBSVNOLA.js → chunk-YJ44UT7D.js} +5 -5
  36. package/dist/{chunk-IV2O4HQJ.js → chunk-ZRSUCJGA.js} +1 -1
  37. package/dist/cli/chat.js +19 -19
  38. package/dist/cli/check.js +6 -6
  39. package/dist/cli/plugins.js +11 -11
  40. package/dist/cli/snapshot-warmup.js +8 -8
  41. package/dist/cli/upgrade.js +5 -5
  42. package/dist/{db-P5H5RVQV.js → db-2ASK6MV6.js} +5 -5
  43. package/dist/instrumentation.js +2 -2
  44. package/dist/nitro.js +2 -2
  45. package/dist/{runner-B4ZDUO3R.js → runner-L363K62Z.js} +12 -13
  46. package/dist/{skills-GWTOZ3XV.js → skills-VCPG3NOO.js} +6 -6
  47. package/dist/{validation-QFIC6XH6.js → validation-GY7AJBLC.js} +6 -6
  48. package/dist/version.js +1 -1
  49. package/package.json +6 -6
package/dist/api.js CHANGED
@@ -5,7 +5,10 @@ import {
5
5
  apiErrorSchema,
6
6
  archiveConversationBodySchema,
7
7
  archiveConversationResponseSchema,
8
+ conversationDetailQuerySchema,
8
9
  conversationDetailReportSchema,
10
+ conversationEventPageSchema,
11
+ conversationEventsQuerySchema,
9
12
  conversationFeedQuerySchema,
10
13
  conversationFeedSchema,
11
14
  conversationParamsSchema,
@@ -15,13 +18,13 @@ import {
15
18
  locationDirectoryReportSchema,
16
19
  locationParamsSchema,
17
20
  personParamsSchema
18
- } from "./chunk-JE23YR3X.js";
21
+ } from "./chunk-UKGNSJQQ.js";
19
22
  import {
20
23
  buildSentryConversationUrl,
21
24
  formatSlackConversationRedactedLabel,
22
25
  readHealthReport,
23
26
  resolveSlackConversationContextFromThreadId
24
- } from "./chunk-2FJ6WEYV.js";
27
+ } from "./chunk-K5BXPM74.js";
25
28
  import {
26
29
  healthReportSchema,
27
30
  pluginOperationalReportFeedSchema,
@@ -34,12 +37,12 @@ import {
34
37
  hasAgentTurnUsage
35
38
  } from "./chunk-MU6HHZEN.js";
36
39
  import "./chunk-PDO5BLNM.js";
37
- import "./chunk-NDQL4ESR.js";
40
+ import "./chunk-QTS66GMR.js";
38
41
  import {
39
42
  decodeStoredConversationEvent,
40
43
  getDb,
41
44
  getSqlExecutor
42
- } from "./chunk-QIALGHRN.js";
45
+ } from "./chunk-LLKDNPYV.js";
43
46
  import {
44
47
  juniorConversationEvents,
45
48
  juniorConversations,
@@ -47,7 +50,7 @@ import {
47
50
  juniorUsers
48
51
  } from "./chunk-JQFJO5WH.js";
49
52
  import "./chunk-4ZNGQH7C.js";
50
- import "./chunk-PK4ETCUQ.js";
53
+ import "./chunk-RCBUZGIT.js";
51
54
  import {
52
55
  juniorDestinations
53
56
  } from "./chunk-VFUK3X5B.js";
@@ -55,11 +58,11 @@ import {
55
58
  canExposeConversationPayload,
56
59
  parseSlackThreadId
57
60
  } from "./chunk-6Y5L4BOM.js";
58
- import "./chunk-CSS7JHEM.js";
61
+ import "./chunk-36UJA3OD.js";
59
62
  import "./chunk-42WRQLJ6.js";
60
63
  import "./chunk-6GWA276C.js";
61
- import "./chunk-AU2JVUDF.js";
62
- import "./chunk-IV2O4HQJ.js";
64
+ import "./chunk-NP7TYFYR.js";
65
+ import "./chunk-ZRSUCJGA.js";
63
66
  import "./chunk-MLKGABMK.js";
64
67
 
65
68
  // src/api.ts
@@ -156,9 +159,6 @@ var archive_default = defineApiRoute({
156
159
  }
157
160
  });
158
161
 
159
- // src/api/conversations/detail.ts
160
- import { and as and5, asc as asc2, eq as eq6, getTableColumns, inArray as inArray3, sql as sql3 } from "drizzle-orm";
161
-
162
162
  // src/chat/pi/sql-model-usage.ts
163
163
  import { and as and2, eq as eq2, sql } from "drizzle-orm";
164
164
  var message = sql`${juniorConversationEvents.payload}->'message'`;
@@ -275,8 +275,67 @@ async function readConversationModelUsageFromSql(executor, options) {
275
275
  });
276
276
  }
277
277
 
278
- // src/api/conversations/events.ts
278
+ // src/api/conversations/cursor.ts
279
+ import { createHmac, timingSafeEqual } from "crypto";
279
280
  import { z } from "zod";
281
+ var CURSOR_CONTEXT = "junior-conversation-report-cursor";
282
+ var conversationCursorPayloadSchema = z.object({
283
+ conversationId: z.string().min(1),
284
+ seq: z.number().int().min(-1),
285
+ version: z.literal(1)
286
+ }).strict();
287
+ function cursorSecret() {
288
+ const secret = process.env.JUNIOR_SECRET?.trim();
289
+ if (!secret) {
290
+ throw new Error("Cannot sign conversation cursor without JUNIOR_SECRET");
291
+ }
292
+ return secret;
293
+ }
294
+ function signature(payload) {
295
+ return createHmac("sha256", cursorSecret()).update(`${CURSOR_CONTEXT}:${payload}`).digest("base64url");
296
+ }
297
+ function encodeConversationCursor(payload) {
298
+ const encoded = Buffer.from(
299
+ JSON.stringify({ ...payload, version: 1 }),
300
+ "utf8"
301
+ ).toString("base64url");
302
+ return `${encoded}.${signature(encoded)}`;
303
+ }
304
+ function decodeConversationCursor(args) {
305
+ const [encoded, actualSignature, extra] = args.cursor.split(".");
306
+ if (!encoded || !actualSignature || extra !== void 0) return void 0;
307
+ const expected = Buffer.from(signature(encoded));
308
+ const actual = Buffer.from(actualSignature);
309
+ if (expected.length !== actual.length || !timingSafeEqual(expected, actual)) {
310
+ return void 0;
311
+ }
312
+ try {
313
+ const parsed = conversationCursorPayloadSchema.parse(
314
+ JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"))
315
+ );
316
+ if (parsed.conversationId !== args.conversationId) {
317
+ return void 0;
318
+ }
319
+ return { seq: parsed.seq };
320
+ } catch {
321
+ return void 0;
322
+ }
323
+ }
324
+
325
+ // src/api/conversations/event-page.ts
326
+ import {
327
+ and as and3,
328
+ asc,
329
+ desc,
330
+ eq as eq3,
331
+ getTableColumns,
332
+ inArray,
333
+ lt,
334
+ sql as sql2
335
+ } from "drizzle-orm";
336
+
337
+ // src/api/conversations/events.ts
338
+ import { z as z2 } from "zod";
280
339
  var conversationReportSourceEventTypes = [
281
340
  "message",
282
341
  "message_handled",
@@ -290,31 +349,33 @@ var conversationReportSourceEventTypes = [
290
349
  "subagent_started",
291
350
  "subagent_ended"
292
351
  ];
293
- var reportingAssistantMessageSchema = z.object({
294
- role: z.literal("assistant"),
295
- content: z.array(z.unknown())
352
+ var reportingAssistantMessageSchema = z2.object({
353
+ role: z2.literal("assistant"),
354
+ content: z2.array(z2.unknown())
296
355
  }).passthrough();
297
- var reportingToolCallPartSchema = z.object({
298
- type: z.literal("toolCall"),
299
- id: z.string().min(1),
300
- name: z.string().min(1),
301
- arguments: z.unknown()
356
+ var reportingToolCallPartSchema = z2.object({
357
+ type: z2.literal("toolCall"),
358
+ id: z2.string().min(1),
359
+ name: z2.string().min(1),
360
+ arguments: z2.unknown()
302
361
  }).passthrough();
303
- var reportingToolResultMessageSchema = z.object({
304
- role: z.literal("toolResult"),
305
- toolCallId: z.string().min(1),
306
- content: z.array(z.unknown()),
307
- details: z.unknown().optional(),
308
- isError: z.boolean().optional()
362
+ var reportingToolResultMessageSchema = z2.object({
363
+ role: z2.literal("toolResult"),
364
+ toolCallId: z2.string().min(1),
365
+ toolName: z2.string().min(1).optional(),
366
+ name: z2.string().min(1).optional(),
367
+ content: z2.array(z2.unknown()),
368
+ details: z2.unknown().optional(),
369
+ isError: z2.boolean().optional()
309
370
  }).passthrough();
310
- var reportingToolResultDetailsSchema = z.object({
311
- ok: z.boolean().optional(),
312
- status: z.string().optional()
371
+ var reportingToolResultDetailsSchema = z2.object({
372
+ ok: z2.boolean().optional(),
373
+ status: z2.string().optional()
313
374
  }).passthrough();
314
- var reportingTextPartSchema = z.object({ type: z.literal("text"), text: z.string() }).passthrough();
315
- var reportingMediaPartSchema = z.object({
316
- type: z.enum(["image", "audio"]),
317
- mimeType: z.string().min(1).optional()
375
+ var reportingTextPartSchema = z2.object({ type: z2.literal("text"), text: z2.string() }).passthrough();
376
+ var reportingMediaPartSchema = z2.object({
377
+ type: z2.enum(["image", "audio"]),
378
+ mimeType: z2.string().min(1).optional()
318
379
  }).passthrough();
319
380
  function sanitizeModelContentPart(value) {
320
381
  const text = reportingTextPartSchema.safeParse(value);
@@ -343,6 +404,9 @@ function reportToolCalls(args) {
343
404
  {
344
405
  toolCallId: call.data.id,
345
406
  name: call.data.name,
407
+ status: "running",
408
+ startedAt: new Date(args.createdAtMs).toISOString(),
409
+ startedSeq: args.seq,
346
410
  ...args.canExposePayload && call.data.arguments !== void 0 ? { input: call.data.arguments } : {}
347
411
  }
348
412
  ];
@@ -357,13 +421,37 @@ function reportToolResult(args) {
357
421
  );
358
422
  const outcome = message2.data.isError === true || details.success && (details.data.ok === false || details.data.status === "error") ? "error" : "completed";
359
423
  const output = modelVisibleToolOutput(message2.data.content);
424
+ const name = message2.data.toolName ?? message2.data.name ?? args.start?.name;
425
+ if (!name) return void 0;
360
426
  return {
361
- type: "tool_result",
362
- toolCallId: message2.data.toolCallId,
363
- outcome,
364
- ...args.canExposePayload && output !== void 0 ? { output } : {}
427
+ type: "tool_calls",
428
+ calls: [
429
+ {
430
+ toolCallId: message2.data.toolCallId,
431
+ name,
432
+ status: outcome,
433
+ ...args.start ? {
434
+ startedAt: new Date(args.start.createdAtMs).toISOString(),
435
+ startedSeq: args.start.seq
436
+ } : {},
437
+ ...args.canExposePayload && output !== void 0 ? { output } : {}
438
+ }
439
+ ]
365
440
  };
366
441
  }
442
+ function conversationReportToolResultIds(events) {
443
+ return [
444
+ ...new Set(
445
+ events.flatMap((event) => {
446
+ if (event.data.type !== "agent_step") return [];
447
+ const result = reportingToolResultMessageSchema.safeParse(
448
+ event.data.message
449
+ );
450
+ return result.success ? [result.data.toolCallId] : [];
451
+ })
452
+ )
453
+ ];
454
+ }
367
455
  function reportEventData(args) {
368
456
  const { data } = args;
369
457
  switch (data.type) {
@@ -405,39 +493,95 @@ function reportEventData(args) {
405
493
  return void 0;
406
494
  }
407
495
  }
408
- function projectConversationReportEvents(args) {
496
+ function projectConversationReportEventPage(args) {
409
497
  const subagentStarts = /* @__PURE__ */ new Map();
498
+ for (const event of args.subagentStartEvents ?? []) {
499
+ if (event.data.type === "subagent_started") {
500
+ subagentStarts.set(event.data.subagentInvocationId, {
501
+ createdAtMs: event.createdAtMs,
502
+ data: event.data,
503
+ seq: event.seq
504
+ });
505
+ }
506
+ }
507
+ const toolStarts = /* @__PURE__ */ new Map();
508
+ for (const event of args.toolStartEvents ?? []) {
509
+ if (event.data.type === "tool_execution_started") {
510
+ const current = toolStarts.get(event.data.toolCallId);
511
+ if (!current || event.seq < current.seq) {
512
+ toolStarts.set(event.data.toolCallId, {
513
+ createdAtMs: event.createdAtMs,
514
+ name: event.data.toolName,
515
+ seq: event.seq
516
+ });
517
+ }
518
+ }
519
+ }
410
520
  const projected = [];
411
521
  for (const event of args.events) {
412
522
  let data;
413
523
  if (event.data.type === "agent_step") {
414
524
  const reportArgs = {
415
525
  canExposePayload: args.canExposePayload,
416
- message: event.data.message
526
+ createdAtMs: event.createdAtMs,
527
+ message: event.data.message,
528
+ seq: event.seq
417
529
  };
418
- data = reportToolCalls(reportArgs) ?? reportToolResult(reportArgs);
530
+ const result = reportingToolResultMessageSchema.safeParse(
531
+ event.data.message
532
+ );
533
+ const start = result.success ? toolStarts.get(result.data.toolCallId) : void 0;
534
+ data = reportToolCalls(reportArgs) ?? reportToolResult({
535
+ canExposePayload: args.canExposePayload,
536
+ message: event.data.message,
537
+ ...start && start.seq < event.seq ? { start } : {}
538
+ });
419
539
  } else if (event.data.type === "tool_execution_started") {
540
+ toolStarts.set(event.data.toolCallId, {
541
+ createdAtMs: event.createdAtMs,
542
+ name: event.data.toolName,
543
+ seq: event.seq
544
+ });
420
545
  data = {
421
- type: "tool_started",
422
- toolCallId: event.data.toolCallId,
423
- name: event.data.toolName
546
+ type: "tool_calls",
547
+ calls: [
548
+ {
549
+ toolCallId: event.data.toolCallId,
550
+ name: event.data.toolName,
551
+ status: "running",
552
+ startedAt: new Date(event.createdAtMs).toISOString(),
553
+ startedSeq: event.seq
554
+ }
555
+ ]
424
556
  };
425
557
  } else if (event.data.type === "subagent_started") {
426
- subagentStarts.set(event.data.subagentInvocationId, event.seq);
558
+ subagentStarts.set(event.data.subagentInvocationId, {
559
+ createdAtMs: event.createdAtMs,
560
+ data: event.data,
561
+ seq: event.seq
562
+ });
427
563
  data = {
428
- type: "subagent_started",
564
+ type: "subagent",
565
+ startedSeq: event.seq,
566
+ startedAt: new Date(event.createdAtMs).toISOString(),
429
567
  childConversationId: event.data.childConversationId,
430
568
  subagentKind: event.data.subagentKind,
569
+ status: "running",
431
570
  ...event.data.parentToolCallId ? { parentToolCallId: event.data.parentToolCallId } : {}
432
571
  };
433
572
  } else if (event.data.type === "subagent_ended") {
434
- const startedSeq = subagentStarts.get(event.data.subagentInvocationId);
435
- if (startedSeq !== void 0) {
573
+ const started = subagentStarts.get(event.data.subagentInvocationId);
574
+ if (started && started.seq < event.seq) {
436
575
  data = {
437
- type: "subagent_ended",
438
- startedSeq,
439
- outcome: event.data.outcome
576
+ type: "subagent",
577
+ startedSeq: started.seq,
578
+ startedAt: new Date(started.createdAtMs).toISOString(),
579
+ childConversationId: started.data.childConversationId,
580
+ subagentKind: started.data.subagentKind,
581
+ ...started.data.parentToolCallId ? { parentToolCallId: started.data.parentToolCallId } : {},
582
+ status: event.data.outcome === "success" ? "completed" : event.data.outcome
440
583
  };
584
+ subagentStarts.delete(event.data.subagentInvocationId);
441
585
  }
442
586
  } else if (event.data.type === "handoff") {
443
587
  data = {
@@ -465,8 +609,116 @@ function projectConversationReportEvents(args) {
465
609
  return projected;
466
610
  }
467
611
 
612
+ // src/api/conversations/event-page.ts
613
+ var conversationEventColumns = getTableColumns(juniorConversationEvents);
614
+ var MIN_SCAN_SIZE = 64;
615
+ async function readConversationEventRows(executor, args) {
616
+ return executor.db().select({
617
+ ...conversationEventColumns,
618
+ // Replacement history is model context, never dashboard report data.
619
+ payload: sql2`case
620
+ when ${juniorConversationEvents.type} in ('compaction', 'handoff')
621
+ then jsonb_set(
622
+ ${juniorConversationEvents.payload},
623
+ '{replacementHistory}',
624
+ '[]'::jsonb
625
+ )
626
+ else ${juniorConversationEvents.payload}
627
+ end`
628
+ }).from(juniorConversationEvents).where(
629
+ and3(
630
+ eq3(juniorConversationEvents.conversationId, args.conversationId),
631
+ args.beforeSeq === void 0 ? void 0 : lt(juniorConversationEvents.seq, args.beforeSeq),
632
+ inArray(
633
+ juniorConversationEvents.type,
634
+ args.types ?? [...conversationReportSourceEventTypes]
635
+ ),
636
+ args.subagentInvocationIds === void 0 ? void 0 : inArray(
637
+ sql2`${juniorConversationEvents.payload}->>'subagentInvocationId'`,
638
+ args.subagentInvocationIds
639
+ ),
640
+ args.toolCallIds === void 0 ? void 0 : inArray(
641
+ sql2`${juniorConversationEvents.payload}->>'toolCallId'`,
642
+ args.toolCallIds
643
+ )
644
+ )
645
+ ).orderBy(
646
+ args.direction === "forward" ? asc(juniorConversationEvents.seq) : desc(juniorConversationEvents.seq)
647
+ ).limit(args.limit);
648
+ }
649
+ function decodeConversationEventRow(row) {
650
+ return decodeStoredConversationEvent({
651
+ schemaVersion: row.schemaVersion,
652
+ seq: row.seq,
653
+ historyVersion: row.historyVersion,
654
+ ...row.idempotencyKey ? { idempotencyKey: row.idempotencyKey } : {},
655
+ createdAtMs: row.createdAt.getTime(),
656
+ type: row.type,
657
+ payload: row.payload
658
+ });
659
+ }
660
+ async function projectConversationEventRows(executor, args) {
661
+ const events = args.rows.map(decodeConversationEventRow).sort((left, right) => left.seq - right.seq);
662
+ const endedInvocationIds = [
663
+ ...new Set(
664
+ events.flatMap(
665
+ (event) => event.data.type === "subagent_ended" ? [event.data.subagentInvocationId] : []
666
+ )
667
+ )
668
+ ];
669
+ const subagentStartRows = endedInvocationIds.length === 0 ? [] : await readConversationEventRows(executor, {
670
+ conversationId: args.conversationId,
671
+ direction: "forward",
672
+ limit: endedInvocationIds.length,
673
+ subagentInvocationIds: endedInvocationIds,
674
+ types: ["subagent_started"]
675
+ });
676
+ const toolResultIds = conversationReportToolResultIds(events);
677
+ const toolStartRows = toolResultIds.length === 0 ? [] : await readConversationEventRows(executor, {
678
+ conversationId: args.conversationId,
679
+ direction: "forward",
680
+ limit: toolResultIds.length,
681
+ toolCallIds: toolResultIds,
682
+ types: ["tool_execution_started"]
683
+ });
684
+ return projectConversationReportEventPage({
685
+ canExposePayload: args.canExposePayload,
686
+ events,
687
+ subagentStartEvents: subagentStartRows.map(decodeConversationEventRow),
688
+ toolStartEvents: toolStartRows.map(decodeConversationEventRow)
689
+ });
690
+ }
691
+ async function readConversationEventPage(executor, args) {
692
+ const scanSize = Math.max(args.limit + 1, MIN_SCAN_SIZE);
693
+ const rows = [];
694
+ let beforeSeq = args.beforeSeq;
695
+ let projected = [];
696
+ while (projected.length <= args.limit) {
697
+ const batch = await readConversationEventRows(executor, {
698
+ beforeSeq,
699
+ conversationId: args.conversationId,
700
+ direction: "backward",
701
+ limit: scanSize
702
+ });
703
+ if (batch.length === 0) break;
704
+ rows.push(...batch);
705
+ projected = await projectConversationEventRows(executor, {
706
+ canExposePayload: args.canExposePayload,
707
+ conversationId: args.conversationId,
708
+ rows
709
+ });
710
+ if (projected.length > args.limit || batch.length < scanSize) break;
711
+ beforeSeq = batch.at(-1).seq;
712
+ }
713
+ const events = projected.slice(-args.limit);
714
+ return {
715
+ events,
716
+ ...projected.length > events.length && events[0] ? { previousSeq: events[0].seq } : {}
717
+ };
718
+ }
719
+
468
720
  // src/api/conversations/list.ts
469
- import { and as and4, asc, desc, eq as eq5, isNull as isNull2 } from "drizzle-orm";
721
+ import { and as and5, asc as asc2, desc as desc2, eq as eq6, isNull as isNull2 } from "drizzle-orm";
470
722
 
471
723
  // src/api/conversations/shared.ts
472
724
  function slackStatsLocationLabel(input) {
@@ -566,6 +818,15 @@ function channelNameRedactedFromConversation(conversation, canViewPrivateContent
566
818
  }
567
819
  return !canViewPrivateContent;
568
820
  }
821
+ function conversationEventHistory(args) {
822
+ if (args.transcriptPurgedAtMs !== void 0) {
823
+ return {
824
+ status: "expired",
825
+ expiredAt: new Date(args.transcriptPurgedAtMs).toISOString()
826
+ };
827
+ }
828
+ return args.canExposePayload ? { status: "available" } : { status: "redacted", reason: "non_public_conversation" };
829
+ }
569
830
  function conversationSummaryFromStoredConversation(args) {
570
831
  const { conversation, durationMs, usage: usage3 } = args;
571
832
  const canViewPrivateContent = args.access?.canViewPrivateContent ?? false;
@@ -610,7 +871,7 @@ function conversationSummaryFromStoredConversation(args) {
610
871
  }
611
872
 
612
873
  // src/api/conversations/access.ts
613
- import { eq as eq3, inArray } from "drizzle-orm";
874
+ import { eq as eq4, inArray as inArray2 } from "drizzle-orm";
614
875
  import { alias } from "drizzle-orm/pg-core";
615
876
  var rootConversation = alias(juniorConversations, "access_root_conversation");
616
877
  var rootDestination = alias(juniorDestinations, "access_root_destination");
@@ -630,17 +891,17 @@ async function readConversationAccessFromSql(db, conversationIds, verifiedViewer
630
891
  rootEmailVerified: rootIdentity.emailVerified
631
892
  }).from(juniorConversations).leftJoin(
632
893
  rootConversation,
633
- eq3(
894
+ eq4(
634
895
  rootConversation.conversationId,
635
896
  juniorConversations.rootConversationId
636
897
  )
637
898
  ).leftJoin(
638
899
  rootDestination,
639
- eq3(rootDestination.id, rootConversation.destinationId)
900
+ eq4(rootDestination.id, rootConversation.destinationId)
640
901
  ).leftJoin(
641
902
  rootIdentity,
642
- eq3(rootIdentity.id, rootConversation.actorIdentityId)
643
- ).where(inArray(juniorConversations.conversationId, [...conversationIds]));
903
+ eq4(rootIdentity.id, rootConversation.actorIdentityId)
904
+ ).where(inArray2(juniorConversations.conversationId, [...conversationIds]));
644
905
  return new Map(
645
906
  rows.map((row) => {
646
907
  const hasValidRoot = row.rootConversationId !== null && row.rootConversationId === row.storedRootConversationId && row.rootRootConversationId === row.rootConversationId && row.rootParentConversationId === null && (row.parentConversationId !== null || row.storedRootConversationId === row.conversationId);
@@ -664,7 +925,7 @@ async function readConversationAccessFromSql(db, conversationIds, verifiedViewer
664
925
  }
665
926
 
666
927
  // src/api/conversations/usage.ts
667
- import { and as and3, eq as eq4, inArray as inArray2, isNull, sql as sql2 } from "drizzle-orm";
928
+ import { and as and4, eq as eq5, inArray as inArray3, isNull, sql as sql3 } from "drizzle-orm";
668
929
  import { alias as alias2 } from "drizzle-orm/pg-core";
669
930
  var usage2 = juniorConversations.usage;
670
931
  var metricRootConversation = alias2(
@@ -678,10 +939,10 @@ var componentFields = [
678
939
  "cacheCreationTokens"
679
940
  ];
680
941
  function summedUsageNumber(field) {
681
- return sql2`sum((${usage2}->>${field})::double precision)::double precision`;
942
+ return sql3`sum((${usage2}->>${field})::double precision)::double precision`;
682
943
  }
683
944
  function summedCostNumber(field) {
684
- return sql2`round(
945
+ return sql3`round(
685
946
  sum((${usage2}->'cost'->>${field})::numeric),
686
947
  12
687
948
  )::double precision`;
@@ -711,7 +972,7 @@ function usageFromAggregate(row) {
711
972
  }
712
973
  async function readRootConversationMetricsFromSql(db, rootConversationIds) {
713
974
  if (rootConversationIds.length === 0) return /* @__PURE__ */ new Map();
714
- const hasComponents = sql2`coalesce(
975
+ const hasComponents = sql3`coalesce(
715
976
  ${usage2}->>'inputTokens',
716
977
  ${usage2}->>'outputTokens',
717
978
  ${usage2}->>'cachedInputTokens',
@@ -719,12 +980,12 @@ async function readRootConversationMetricsFromSql(db, rootConversationIds) {
719
980
  ) is not null`;
720
981
  const rows = await db.select({
721
982
  rootConversationId: juniorConversations.rootConversationId,
722
- durationMs: sql2`coalesce(sum(${juniorConversations.durationMs}), 0)::double precision`,
983
+ durationMs: sql3`coalesce(sum(${juniorConversations.durationMs}), 0)::double precision`,
723
984
  inputTokens: summedUsageNumber("inputTokens"),
724
985
  outputTokens: summedUsageNumber("outputTokens"),
725
986
  cachedInputTokens: summedUsageNumber("cachedInputTokens"),
726
987
  cacheCreationTokens: summedUsageNumber("cacheCreationTokens"),
727
- totalOnlyTokens: sql2`sum(
988
+ totalOnlyTokens: sql3`sum(
728
989
  case
729
990
  when not (${hasComponents})
730
991
  then (${usage2}->>'totalTokens')::double precision
@@ -738,19 +999,19 @@ async function readRootConversationMetricsFromSql(db, rootConversationIds) {
738
999
  costTotal: summedCostNumber("total")
739
1000
  }).from(juniorConversations).innerJoin(
740
1001
  metricRootConversation,
741
- and3(
742
- eq4(
1002
+ and4(
1003
+ eq5(
743
1004
  metricRootConversation.conversationId,
744
1005
  juniorConversations.rootConversationId
745
1006
  ),
746
1007
  isNull(metricRootConversation.parentConversationId),
747
- eq4(
1008
+ eq5(
748
1009
  metricRootConversation.rootConversationId,
749
1010
  metricRootConversation.conversationId
750
1011
  )
751
1012
  )
752
1013
  ).where(
753
- inArray2(juniorConversations.rootConversationId, [...rootConversationIds])
1014
+ inArray3(juniorConversations.rootConversationId, [...rootConversationIds])
754
1015
  ).groupBy(juniorConversations.rootConversationId);
755
1016
  return new Map(
756
1017
  rows.flatMap((row) => {
@@ -782,22 +1043,22 @@ async function conversationRows(db, limit, actorEmail) {
782
1043
  userDisplayName: juniorUsers.displayName
783
1044
  }).from(juniorConversations).leftJoin(
784
1045
  juniorDestinations,
785
- eq5(juniorDestinations.id, juniorConversations.destinationId)
1046
+ eq6(juniorDestinations.id, juniorConversations.destinationId)
786
1047
  ).leftJoin(
787
1048
  juniorIdentities,
788
- eq5(juniorIdentities.id, juniorConversations.actorIdentityId)
789
- ).leftJoin(juniorUsers, eq5(juniorUsers.id, juniorIdentities.userId)).where(
790
- and4(
1049
+ eq6(juniorIdentities.id, juniorConversations.actorIdentityId)
1050
+ ).leftJoin(juniorUsers, eq6(juniorUsers.id, juniorIdentities.userId)).where(
1051
+ and5(
791
1052
  isNull2(juniorConversations.parentConversationId),
792
1053
  isNull2(juniorConversations.archivedAt),
793
- actorEmail ? and4(
794
- eq5(juniorIdentities.emailNormalized, actorEmail),
795
- eq5(juniorIdentities.emailVerified, true)
1054
+ actorEmail ? and5(
1055
+ eq6(juniorIdentities.emailNormalized, actorEmail),
1056
+ eq6(juniorIdentities.emailVerified, true)
796
1057
  ) : void 0
797
1058
  )
798
1059
  ).orderBy(
799
- desc(juniorConversations.lastActivityAt),
800
- asc(juniorConversations.conversationId)
1060
+ desc2(juniorConversations.lastActivityAt),
1061
+ asc2(juniorConversations.conversationId)
801
1062
  ).limit(limit);
802
1063
  }
803
1064
  function conversationFromRow(row) {
@@ -845,11 +1106,11 @@ async function readConversationRecordFromSql(conversationId) {
845
1106
  userDisplayName: juniorUsers.displayName
846
1107
  }).from(juniorConversations).leftJoin(
847
1108
  juniorDestinations,
848
- eq5(juniorDestinations.id, juniorConversations.destinationId)
1109
+ eq6(juniorDestinations.id, juniorConversations.destinationId)
849
1110
  ).leftJoin(
850
1111
  juniorIdentities,
851
- eq5(juniorIdentities.id, juniorConversations.actorIdentityId)
852
- ).leftJoin(juniorUsers, eq5(juniorUsers.id, juniorIdentities.userId)).where(eq5(juniorConversations.conversationId, conversationId)).limit(1);
1112
+ eq6(juniorIdentities.id, juniorConversations.actorIdentityId)
1113
+ ).leftJoin(juniorUsers, eq6(juniorUsers.id, juniorIdentities.userId)).where(eq6(juniorConversations.conversationId, conversationId)).limit(1);
853
1114
  const row = rows[0];
854
1115
  return row ? {
855
1116
  conversation: conversationFromRow(row),
@@ -914,35 +1175,11 @@ var list_default = defineApiRoute({
914
1175
  });
915
1176
 
916
1177
  // src/api/conversations/detail.ts
917
- var conversationEventColumns = getTableColumns(juniorConversationEvents);
918
- async function readConversationReportEventRows(executor, conversationId) {
919
- return executor.db().select({
920
- ...conversationEventColumns,
921
- // Replacement history is model context, never dashboard report data.
922
- payload: sql3`case
923
- when ${juniorConversationEvents.type} in ('compaction', 'handoff')
924
- then jsonb_set(
925
- ${juniorConversationEvents.payload},
926
- '{replacementHistory}',
927
- '[]'::jsonb
928
- )
929
- else ${juniorConversationEvents.payload}
930
- end`
931
- }).from(juniorConversationEvents).where(
932
- and5(
933
- eq6(juniorConversationEvents.conversationId, conversationId),
934
- inArray3(juniorConversationEvents.type, [
935
- ...conversationReportSourceEventTypes
936
- ])
937
- )
938
- ).orderBy(asc2(juniorConversationEvents.seq));
939
- }
940
1178
  function projectConversationDetail(args) {
941
1179
  const { conversation } = args;
942
1180
  const conversationId = conversation.conversationId;
943
1181
  const transcriptPurgedAtMs = conversation.transcriptPurgedAtMs;
944
1182
  const canExposePayload = args.access?.canViewPrivateContent ?? false;
945
- const events = transcriptPurgedAtMs === void 0 ? args.events : [];
946
1183
  const modelUsage = transcriptPurgedAtMs === void 0 ? args.modelUsage : [];
947
1184
  const sentryConversationUrl = buildSentryConversationUrl(conversationId);
948
1185
  return {
@@ -953,15 +1190,18 @@ function projectConversationDetail(args) {
953
1190
  ...args.locationId ? { locationId: args.locationId } : {},
954
1191
  usage: args.usage
955
1192
  }),
956
- events: projectConversationReportEvents({ canExposePayload, events }),
1193
+ events: args.events,
1194
+ ...args.previousSeq !== void 0 ? {
1195
+ previousCursor: encodeConversationCursor({
1196
+ conversationId,
1197
+ seq: args.previousSeq
1198
+ })
1199
+ } : {},
957
1200
  ...modelUsage.length > 0 ? { modelUsage } : {},
958
- eventHistory: transcriptPurgedAtMs !== void 0 ? {
959
- status: "expired",
960
- expiredAt: new Date(transcriptPurgedAtMs).toISOString()
961
- } : canExposePayload ? { status: "available" } : {
962
- status: "redacted",
963
- reason: "non_public_conversation"
964
- },
1201
+ eventHistory: conversationEventHistory({
1202
+ canExposePayload,
1203
+ ...transcriptPurgedAtMs === void 0 ? {} : { transcriptPurgedAtMs }
1204
+ }),
965
1205
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
966
1206
  ...sentryConversationUrl ? { sentryConversationUrl } : {}
967
1207
  };
@@ -971,13 +1211,12 @@ async function readConversationDetailFromSql(conversationId, options) {
971
1211
  if (!record) return void 0;
972
1212
  const executor = getSqlExecutor();
973
1213
  const includeDescendantMetrics = record.rootConversationId === conversationId;
974
- const [accessByConversation, eventRows, modelUsage, metricsByRoot] = await Promise.all([
1214
+ const [accessByConversation, modelUsage, metricsByRoot] = await Promise.all([
975
1215
  readConversationAccessFromSql(
976
1216
  getDb(),
977
1217
  [conversationId],
978
1218
  options.verifiedViewerEmail
979
1219
  ),
980
- readConversationReportEventRows(executor, conversationId),
981
1220
  record.conversation.transcriptPurgedAtMs === void 0 ? readConversationModelUsageFromSql(executor, {
982
1221
  conversationId,
983
1222
  includeDescendants: includeDescendantMetrics
@@ -987,29 +1226,28 @@ async function readConversationDetailFromSql(conversationId, options) {
987
1226
  includeDescendantMetrics ? [conversationId] : []
988
1227
  )
989
1228
  ]);
990
- const events = eventRows.map(
991
- (row) => decodeStoredConversationEvent({
992
- schemaVersion: row.schemaVersion,
993
- seq: row.seq,
994
- historyVersion: row.historyVersion,
995
- ...row.idempotencyKey ? { idempotencyKey: row.idempotencyKey } : {},
996
- createdAtMs: row.createdAt.getTime(),
997
- type: row.type,
998
- payload: row.payload
999
- })
1000
- );
1229
+ const access = accessByConversation.get(conversationId);
1230
+ const page = record.conversation.transcriptPurgedAtMs === void 0 ? await readConversationEventPage(executor, {
1231
+ canExposePayload: access?.canViewPrivateContent ?? false,
1232
+ conversationId,
1233
+ limit: options.limit
1234
+ }) : { events: [] };
1001
1235
  const metrics = metricsByRoot.get(conversationId);
1002
1236
  return projectConversationDetail({
1003
1237
  ...record,
1004
- access: accessByConversation.get(conversationId),
1238
+ access,
1005
1239
  durationMs: metrics?.durationMs ?? record.durationMs,
1006
- events,
1240
+ events: page.events,
1007
1241
  modelUsage,
1242
+ ...page.previousSeq === void 0 ? {} : { previousSeq: page.previousSeq },
1008
1243
  usage: metrics?.usage ?? record.usage ?? void 0
1009
1244
  });
1010
1245
  }
1011
1246
  async function readConversationDetail(conversationId, options = {}) {
1012
- const report = await readConversationDetailFromSql(conversationId, options);
1247
+ const report = await readConversationDetailFromSql(conversationId, {
1248
+ ...options,
1249
+ limit: options.limit ?? 500
1250
+ });
1013
1251
  return report ? conversationDetailReportSchema.parse(report) : void 0;
1014
1252
  }
1015
1253
  var detail_default = defineApiRoute({
@@ -1021,11 +1259,73 @@ var detail_default = defineApiRoute({
1021
1259
  conversationParamsSchema,
1022
1260
  c.req.param()
1023
1261
  );
1262
+ const query = parseQuery(conversationDetailQuerySchema, c.req.query());
1024
1263
  const verifiedViewerEmail = c.get("verifiedViewerEmail");
1025
- const report = await readConversationDetail(
1026
- conversationId,
1027
- verifiedViewerEmail ? { verifiedViewerEmail } : {}
1264
+ const report = await readConversationDetail(conversationId, {
1265
+ ...query,
1266
+ ...verifiedViewerEmail ? { verifiedViewerEmail } : {}
1267
+ });
1268
+ if (!report) throwApiError(404, "Conversation not found.");
1269
+ return report;
1270
+ }
1271
+ });
1272
+
1273
+ // src/api/conversations/event-list.ts
1274
+ async function readConversationEvents(conversationId, beforeValue, options = {}) {
1275
+ const record = await readConversationRecordFromSql(conversationId);
1276
+ if (!record) return void 0;
1277
+ const before = decodeConversationCursor({
1278
+ conversationId,
1279
+ cursor: beforeValue
1280
+ });
1281
+ if (!before) throwApiError(400, "Invalid conversation cursor.");
1282
+ const accessByConversation = await readConversationAccessFromSql(
1283
+ getDb(),
1284
+ [conversationId],
1285
+ options.verifiedViewerEmail
1286
+ );
1287
+ const access = accessByConversation.get(conversationId);
1288
+ const canExposePayload = access?.canViewPrivateContent ?? false;
1289
+ const transcriptPurgedAtMs = record.conversation.transcriptPurgedAtMs;
1290
+ const page = transcriptPurgedAtMs === void 0 ? await readConversationEventPage(getSqlExecutor(), {
1291
+ beforeSeq: before.seq,
1292
+ canExposePayload,
1293
+ conversationId,
1294
+ limit: options.limit ?? 500
1295
+ }) : { events: [] };
1296
+ return conversationEventPageSchema.parse({
1297
+ events: page.events,
1298
+ eventHistory: conversationEventHistory({
1299
+ canExposePayload,
1300
+ ...transcriptPurgedAtMs === void 0 ? {} : { transcriptPurgedAtMs }
1301
+ }),
1302
+ ...page.previousSeq === void 0 ? {} : {
1303
+ previousCursor: encodeConversationCursor({
1304
+ conversationId,
1305
+ seq: page.previousSeq
1306
+ })
1307
+ },
1308
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
1309
+ });
1310
+ }
1311
+ var event_list_default = defineApiRoute({
1312
+ method: "get",
1313
+ path: "/:conversationId/events",
1314
+ responseSchema: conversationEventPageSchema,
1315
+ handler: async (c) => {
1316
+ const { conversationId } = parseParams(
1317
+ conversationParamsSchema,
1318
+ c.req.param()
1028
1319
  );
1320
+ const { before, limit } = parseQuery(
1321
+ conversationEventsQuerySchema,
1322
+ c.req.query()
1323
+ );
1324
+ const verifiedViewerEmail = c.get("verifiedViewerEmail");
1325
+ const report = await readConversationEvents(conversationId, before, {
1326
+ limit,
1327
+ ...verifiedViewerEmail ? { verifiedViewerEmail } : {}
1328
+ });
1029
1329
  if (!report) throwApiError(404, "Conversation not found.");
1030
1330
  return report;
1031
1331
  }
@@ -1325,7 +1625,13 @@ var stats_default = defineApiRoute({
1325
1625
  });
1326
1626
 
1327
1627
  // src/api/conversations/routes.ts
1328
- var routes = [list_default, stats_default, archive_default, detail_default];
1628
+ var routes = [
1629
+ list_default,
1630
+ stats_default,
1631
+ archive_default,
1632
+ event_list_default,
1633
+ detail_default
1634
+ ];
1329
1635
  function createConversationRoutes() {
1330
1636
  const app = new Hono();
1331
1637
  registerApiRoutes(app, routes);
@@ -1336,7 +1642,7 @@ function createConversationRoutes() {
1336
1642
  import { Hono as Hono2 } from "hono";
1337
1643
 
1338
1644
  // src/api/locations/query.ts
1339
- import { and as and7, asc as asc3, desc as desc2, eq as eq8, gte as gte2, isNull as isNull4, sql as sql6 } from "drizzle-orm";
1645
+ import { and as and7, asc as asc3, desc as desc3, eq as eq8, gte as gte2, isNull as isNull4, sql as sql6 } from "drizzle-orm";
1340
1646
  import { alias as alias4 } from "drizzle-orm/pg-core";
1341
1647
 
1342
1648
  // src/api/conversations/reporting.ts
@@ -1622,7 +1928,7 @@ async function recentLocationRows(db, locationId) {
1622
1928
  isNull4(juniorConversations.archivedAt)
1623
1929
  )
1624
1930
  ).orderBy(
1625
- desc2(juniorConversations.lastActivityAt),
1931
+ desc3(juniorConversations.lastActivityAt),
1626
1932
  asc3(juniorConversations.conversationId)
1627
1933
  ).limit(RECENT_LIMIT);
1628
1934
  }
@@ -1803,7 +2109,7 @@ import { Hono as Hono3 } from "hono";
1803
2109
  import { and as and9, eq as eq10, gte as gte3, sql as sql8 } from "drizzle-orm";
1804
2110
 
1805
2111
  // src/api/people/shared.ts
1806
- import { and as and8, asc as asc4, desc as desc3, eq as eq9, isNull as isNull5, notExists, or, sql as sql7 } from "drizzle-orm";
2112
+ import { and as and8, asc as asc4, desc as desc4, eq as eq9, isNull as isNull5, notExists, or, sql as sql7 } from "drizzle-orm";
1807
2113
  import { alias as alias5 } from "drizzle-orm/pg-core";
1808
2114
  var RECENT_LIMIT2 = 25;
1809
2115
  var ACTIVITY_DAYS2 = 365;
@@ -1937,7 +2243,7 @@ async function recentActorRows(email) {
1937
2243
  ).where(
1938
2244
  and8(verifiedActorWhere(email), isNull5(juniorConversations.archivedAt))
1939
2245
  ).orderBy(
1940
- desc3(juniorConversations.lastActivityAt),
2246
+ desc4(juniorConversations.lastActivityAt),
1941
2247
  asc4(juniorConversations.conversationId)
1942
2248
  ).limit(RECENT_LIMIT2);
1943
2249
  }
@@ -2257,7 +2563,7 @@ function readDescriptionText(home) {
2257
2563
  }
2258
2564
  }
2259
2565
  async function readSkillReports() {
2260
- const { discoverSkills } = await import("./skills-GWTOZ3XV.js");
2566
+ const { discoverSkills } = await import("./skills-VCPG3NOO.js");
2261
2567
  const skills = await discoverSkills();
2262
2568
  return skillReportsSchema.parse(
2263
2569
  skills.map((skill) => ({
@@ -2267,7 +2573,7 @@ async function readSkillReports() {
2267
2573
  );
2268
2574
  }
2269
2575
  async function readPluginReports() {
2270
- const { pluginCatalogRuntime } = await import("./catalog-runtime-UEO25P2J.js");
2576
+ const { pluginCatalogRuntime } = await import("./catalog-runtime-6XYOUVRZ.js");
2271
2577
  return pluginReportsSchema.parse(
2272
2578
  pluginCatalogRuntime.getProviders().map((plugin) => ({
2273
2579
  name: plugin.manifest.name
@@ -2277,7 +2583,7 @@ async function readPluginReports() {
2277
2583
  async function readRuntimeInfoReport() {
2278
2584
  const [{ homeDir }, { pluginCatalogRuntime }, plugins, skills] = await Promise.all([
2279
2585
  import("./discovery-RMHYSRL7.js"),
2280
- import("./catalog-runtime-UEO25P2J.js"),
2586
+ import("./catalog-runtime-6XYOUVRZ.js"),
2281
2587
  readPluginReports(),
2282
2588
  readSkillReports()
2283
2589
  ]);
@@ -2293,7 +2599,7 @@ async function readRuntimeInfoReport() {
2293
2599
  }
2294
2600
  async function readPluginOperationalReportFeed() {
2295
2601
  const nowMs = Date.now();
2296
- const { getPluginOperationalReports } = await import("./agent-hooks-6MVTPKP4.js");
2602
+ const { getPluginOperationalReports } = await import("./agent-hooks-BKEMWR3Z.js");
2297
2603
  return pluginOperationalReportFeedSchema.parse({
2298
2604
  source: "plugins",
2299
2605
  generatedAt: new Date(nowMs).toISOString(),