@sentry/junior 0.93.0 → 0.95.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 (122) hide show
  1. package/dist/{agent-hooks-ZG7IDOIN.js → agent-hooks-FTQ5EGPO.js} +9 -10
  2. package/dist/api/people/list.js +7 -7
  3. package/dist/api/people/profile.js +7 -7
  4. package/dist/api/people/shared.d.ts +2 -2
  5. package/dist/api-reference.d.ts +1 -1
  6. package/dist/app.js +474 -231
  7. package/dist/{catalog-runtime-NZHMSMYC.js → catalog-runtime-2D7IVKUT.js} +3 -3
  8. package/dist/chat/advisor-request.d.ts +4 -0
  9. package/dist/chat/agent/request.d.ts +2 -0
  10. package/dist/chat/agent/resume.d.ts +2 -0
  11. package/dist/chat/conversations/history.d.ts +128 -0
  12. package/dist/chat/conversations/legacy-import.d.ts +38 -0
  13. package/dist/chat/conversations/messages.d.ts +39 -0
  14. package/dist/chat/conversations/projection.d.ts +94 -0
  15. package/dist/chat/conversations/retention.d.ts +36 -0
  16. package/dist/chat/conversations/sql/conversation-row.d.ts +14 -0
  17. package/dist/chat/conversations/sql/history.d.ts +4 -0
  18. package/dist/chat/conversations/sql/legacy-history-import.d.ts +55 -0
  19. package/dist/chat/conversations/sql/messages.d.ts +4 -0
  20. package/dist/chat/conversations/sql/migrations.d.ts +14 -3
  21. package/dist/chat/conversations/sql/purge.d.ts +53 -0
  22. package/dist/chat/conversations/sql/store.d.ts +7 -2
  23. package/dist/chat/conversations/store.d.ts +19 -0
  24. package/dist/chat/conversations/visible-compactions.d.ts +11 -0
  25. package/dist/chat/conversations/visible-messages.d.ts +47 -0
  26. package/dist/chat/db.d.ts +12 -1
  27. package/dist/chat/egress/credentialed.d.ts +1 -1
  28. package/dist/chat/identities/sql.d.ts +1 -1
  29. package/dist/chat/logging.d.ts +2 -1
  30. package/dist/chat/resource-events/store.d.ts +2 -1
  31. package/dist/chat/runtime/reply-executor.d.ts +8 -0
  32. package/dist/chat/runtime/thread-state.d.ts +2 -0
  33. package/dist/chat/sandbox/egress/schemas.d.ts +4 -0
  34. package/dist/chat/sandbox/egress/session.d.ts +2 -2
  35. package/dist/chat/sandbox/resources.d.ts +4 -0
  36. package/dist/chat/services/mcp-auth-orchestration.d.ts +8 -0
  37. package/dist/chat/services/plugin-auth-orchestration.d.ts +12 -0
  38. package/dist/chat/services/turn-session-record.d.ts +14 -2
  39. package/dist/chat/sleep.d.ts +2 -0
  40. package/dist/chat/state/conversation.d.ts +7 -4
  41. package/dist/chat/state/session-log.d.ts +23 -1
  42. package/dist/chat/state/turn-session.d.ts +6 -0
  43. package/dist/chat/tool-support/private-trace-result.d.ts +4 -0
  44. package/dist/chat/tool-support/zod-tool.d.ts +1 -0
  45. package/dist/chat/tools/advisor/tool.d.ts +9 -4
  46. package/dist/chat/tools/definition.d.ts +2 -0
  47. package/dist/chat/tools/execution/tool-error-handler.d.ts +2 -2
  48. package/dist/chat/tools/system-time.d.ts +8 -0
  49. package/dist/chat/xml.d.ts +2 -0
  50. package/dist/{chunk-NYWPISSO.js → chunk-2XXEKR44.js} +1 -1
  51. package/dist/{chunk-QGEWC27H.js → chunk-5XMNOD3S.js} +5 -7
  52. package/dist/{chunk-NFXSLTYG.js → chunk-6ONME5IG.js} +864 -62
  53. package/dist/{chunk-LFLTJQVR.js → chunk-7FBGKXPE.js} +4 -0
  54. package/dist/{chunk-MC4ZZE4Q.js → chunk-AYP4ERVZ.js} +1 -1
  55. package/dist/{chunk-GHGPTPBL.js → chunk-B7PUBQQM.js} +1 -1
  56. package/dist/chunk-CM5EZD5F.js +17 -0
  57. package/dist/chunk-CRRBJ3JP.js +1037 -0
  58. package/dist/{chunk-G3E7SCME.js → chunk-CZEX6KIQ.js} +23 -0
  59. package/dist/{chunk-TNUUATT7.js → chunk-E3E2XNCN.js} +2 -2
  60. package/dist/{chunk-LNCREA5Z.js → chunk-EKNJM5IG.js} +3 -127
  61. package/dist/{chunk-NAPITCZ6.js → chunk-GUE7HTNR.js} +251 -6
  62. package/dist/{chunk-BTH37NCU.js → chunk-GVOUCI2O.js} +1 -1
  63. package/dist/{chunk-YYBX2BD5.js → chunk-GY34VGP5.js} +1 -1
  64. package/dist/{chunk-JJUJ4CO2.js → chunk-HCXXB35T.js} +1489 -360
  65. package/dist/{chunk-YTNPU7I2.js → chunk-HDTO3A74.js} +2 -2
  66. package/dist/{chunk-YJ4OPVNA.js → chunk-HZNJLKKI.js} +1 -1
  67. package/dist/{chunk-HDNWZMUN.js → chunk-KDPCGVIT.js} +116 -51
  68. package/dist/chunk-N4RNJ32C.js +48 -0
  69. package/dist/{chunk-UUB3D2XI.js → chunk-NSCHU54X.js} +8 -10
  70. package/dist/{chunk-PFXC67GJ.js → chunk-RAIB6WSG.js} +6 -80
  71. package/dist/chunk-WAGQ377B.js +724 -0
  72. package/dist/{chunk-6H4PP63X.js → chunk-WCMQJE4F.js} +24 -14
  73. package/dist/{chunk-S6QKIGRM.js → chunk-WPOJ443W.js} +15 -25
  74. package/dist/{chunk-MAVOJ54A.js → chunk-XX4N4E6C.js} +25 -388
  75. package/dist/cli/chat.js +24 -23
  76. package/dist/cli/check.js +4 -4
  77. package/dist/cli/plugins.js +11 -12
  78. package/dist/cli/snapshot-warmup.js +7 -8
  79. package/dist/cli/upgrade.js +78 -17
  80. package/dist/db/schema/agent-steps.d.ts +134 -0
  81. package/dist/db/schema/conversation-messages.d.ts +153 -0
  82. package/dist/{chat/conversations/sql → db}/schema/conversations.d.ts +34 -0
  83. package/dist/{chat/conversations/sql → db}/schema.d.ts +319 -8
  84. package/dist/db-W55B74OP.js +24 -0
  85. package/dist/deployment.d.ts +2 -0
  86. package/dist/handlers/retention.d.ts +6 -0
  87. package/dist/instrumentation.js +16 -3
  88. package/dist/legacy-import-ZPLGLAFD.js +17 -0
  89. package/dist/nitro.js +14 -4
  90. package/dist/reporting/conversations/activity.d.ts +40 -0
  91. package/dist/reporting/conversations/context.d.ts +8 -0
  92. package/dist/reporting/conversations/details.d.ts +6 -0
  93. package/dist/reporting/conversations/index.d.ts +4 -0
  94. package/dist/reporting/conversations/shared.d.ts +9 -0
  95. package/dist/reporting/conversations/statistics.d.ts +4 -0
  96. package/dist/reporting/conversations/summaries.d.ts +17 -0
  97. package/dist/reporting/conversations/transcript.d.ts +28 -0
  98. package/dist/reporting/conversations/types.d.ts +178 -0
  99. package/dist/reporting/conversations.d.ts +1 -178
  100. package/dist/reporting.d.ts +4 -3
  101. package/dist/reporting.js +603 -827
  102. package/dist/{runner-XAZPJNAQ.js → runner-OOSBDYRK.js} +35 -41
  103. package/dist/{validation-KYAWETI4.js → validation-QCGRFRFZ.js} +4 -4
  104. package/migrations/README.md +32 -0
  105. package/migrations/meta/0000_snapshot.json +978 -0
  106. package/migrations/meta/_journal.json +13 -0
  107. package/package.json +9 -6
  108. package/dist/chat/sql/schema.d.ts +0 -1049
  109. package/dist/chat/state/conversation-details.d.ts +0 -47
  110. package/dist/chunk-7STN2ZSK.js +0 -250
  111. package/dist/chunk-LE5ANBUZ.js +0 -1040
  112. package/dist/chunk-QL66APYJ.js +0 -1377
  113. package/dist/db-YDN6AFJH.js +0 -18
  114. /package/dist/{chat/sql → db}/db.d.ts +0 -0
  115. /package/dist/{chat/sql → db}/executor.d.ts +0 -0
  116. /package/dist/{chat/sql → db}/neon.d.ts +0 -0
  117. /package/dist/{chat/sql → db}/postgres.d.ts +0 -0
  118. /package/dist/{chat/conversations/sql → db}/schema/destinations.d.ts +0 -0
  119. /package/dist/{chat/conversations/sql → db}/schema/identities.d.ts +0 -0
  120. /package/dist/{chat/conversations/sql → db}/schema/migrations.d.ts +0 -0
  121. /package/dist/{chat/conversations/sql → db}/schema/timestamps.d.ts +0 -0
  122. /package/dist/{chat/conversations/sql → db}/schema/users.d.ts +0 -0
@@ -1,35 +1,53 @@
1
1
  import {
2
- getChatConfig
3
- } from "./chunk-NAPITCZ6.js";
2
+ getChatConfig,
3
+ getConnectedStateContext,
4
+ getStateAdapter
5
+ } from "./chunk-GUE7HTNR.js";
4
6
  import {
5
7
  parseDestination,
6
8
  sameDestination
7
- } from "./chunk-GHGPTPBL.js";
9
+ } from "./chunk-B7PUBQQM.js";
8
10
  import {
9
11
  normalizeIdentityEmail,
10
- parseStoredSlackActor
11
- } from "./chunk-S6QKIGRM.js";
12
+ storedSlackActorSchema
13
+ } from "./chunk-WPOJ443W.js";
12
14
 
13
15
  // src/chat/conversations/sql/store.ts
14
16
  import { randomUUID as randomUUID2 } from "crypto";
15
- import { and as and2, asc, desc, eq as eq2, sql as sql4 } from "drizzle-orm";
17
+ import { and as and2, asc, desc, eq as eq2, isNull, sql as sql4 } from "drizzle-orm";
16
18
 
17
19
  // src/chat/identities/sql.ts
18
20
  import { randomUUID } from "crypto";
19
21
  import { and, eq, sql as sql3 } from "drizzle-orm";
20
22
 
21
- // src/chat/conversations/sql/schema/conversations.ts
23
+ // src/db/schema/agent-steps.ts
24
+ import {
25
+ index as index4,
26
+ integer as integer2,
27
+ jsonb as jsonb4,
28
+ pgTable as pgTable5,
29
+ primaryKey,
30
+ text as text5
31
+ } from "drizzle-orm/pg-core";
32
+
33
+ // src/db/schema/conversations.ts
22
34
  import { sql as sql2 } from "drizzle-orm";
23
- import { index as index3, integer, jsonb as jsonb3, pgTable as pgTable4, text as text4 } from "drizzle-orm/pg-core";
35
+ import {
36
+ index as index3,
37
+ integer,
38
+ jsonb as jsonb3,
39
+ pgTable as pgTable4,
40
+ text as text4
41
+ } from "drizzle-orm/pg-core";
24
42
 
25
- // src/chat/conversations/sql/schema/destinations.ts
43
+ // src/db/schema/destinations.ts
26
44
  import { index, jsonb, pgTable, text, uniqueIndex } from "drizzle-orm/pg-core";
27
45
 
28
- // src/chat/conversations/sql/schema/timestamps.ts
46
+ // src/db/schema/timestamps.ts
29
47
  import { timestamp } from "drizzle-orm/pg-core";
30
48
  var timestamptz = (name) => timestamp(name, { withTimezone: true });
31
49
 
32
- // src/chat/conversations/sql/schema/destinations.ts
50
+ // src/db/schema/destinations.ts
33
51
  var juniorDestinations = pgTable(
34
52
  "junior_destinations",
35
53
  {
@@ -58,7 +76,7 @@ var juniorDestinations = pgTable(
58
76
  ]
59
77
  );
60
78
 
61
- // src/chat/conversations/sql/schema/identities.ts
79
+ // src/db/schema/identities.ts
62
80
  import { sql } from "drizzle-orm";
63
81
  import {
64
82
  boolean,
@@ -69,7 +87,7 @@ import {
69
87
  uniqueIndex as uniqueIndex3
70
88
  } from "drizzle-orm/pg-core";
71
89
 
72
- // src/chat/conversations/sql/schema/users.ts
90
+ // src/db/schema/users.ts
73
91
  import { text as text2, pgTable as pgTable2, uniqueIndex as uniqueIndex2 } from "drizzle-orm/pg-core";
74
92
  var juniorUsers = pgTable2(
75
93
  "junior_users",
@@ -88,7 +106,7 @@ var juniorUsers = pgTable2(
88
106
  ]
89
107
  );
90
108
 
91
- // src/chat/conversations/sql/schema/identities.ts
109
+ // src/db/schema/identities.ts
92
110
  var juniorIdentities = pgTable3(
93
111
  "junior_identities",
94
112
  {
@@ -122,7 +140,7 @@ var juniorIdentities = pgTable3(
122
140
  ]
123
141
  );
124
142
 
125
- // src/chat/conversations/sql/schema/conversations.ts
143
+ // src/db/schema/conversations.ts
126
144
  var juniorConversations = pgTable4(
127
145
  "junior_conversations",
128
146
  {
@@ -155,7 +173,13 @@ var juniorConversations = pgTable4(
155
173
  executionStatus: text4("execution_status").$type().notNull(),
156
174
  runId: text4("run_id"),
157
175
  lastCheckpointAt: timestamptz("last_checkpoint_at"),
158
- lastEnqueuedAt: timestamptz("last_enqueued_at")
176
+ lastEnqueuedAt: timestamptz("last_enqueued_at"),
177
+ // Subagent (advisor) runs are child conversations; top-level listings filter
178
+ // parent_conversation_id IS NULL.
179
+ parentConversationId: text4("parent_conversation_id").references(
180
+ () => juniorConversations.conversationId
181
+ ),
182
+ transcriptPurgedAt: timestamptz("transcript_purged_at")
159
183
  },
160
184
  (table) => [
161
185
  index3("junior_conversations_last_activity_idx").on(
@@ -179,20 +203,70 @@ var juniorConversations = pgTable4(
179
203
  table.originType,
180
204
  table.originId,
181
205
  table.lastActivityAt.desc()
206
+ ),
207
+ index3("junior_conversations_parent_idx").on(table.parentConversationId)
208
+ ]
209
+ );
210
+
211
+ // src/db/schema/agent-steps.ts
212
+ var juniorAgentSteps = pgTable5(
213
+ "junior_agent_steps",
214
+ {
215
+ conversationId: text5("conversation_id").notNull().references(() => juniorConversations.conversationId),
216
+ seq: integer2("seq").notNull(),
217
+ contextEpoch: integer2("context_epoch").notNull(),
218
+ type: text5("type").notNull(),
219
+ role: text5("role"),
220
+ payload: jsonb4("payload").$type().notNull(),
221
+ createdAt: timestamptz("created_at").notNull()
222
+ },
223
+ (table) => [
224
+ primaryKey({ columns: [table.conversationId, table.seq] }),
225
+ index4("junior_agent_steps_epoch_idx").on(
226
+ table.conversationId,
227
+ table.contextEpoch,
228
+ table.seq
182
229
  )
183
230
  ]
184
231
  );
185
232
 
186
- // src/chat/conversations/sql/schema/migrations.ts
187
- import { pgTable as pgTable5, text as text5 } from "drizzle-orm/pg-core";
188
- var juniorSchemaMigrations = pgTable5("junior_schema_migrations", {
189
- id: text5("id").primaryKey(),
190
- checksum: text5("checksum").notNull(),
233
+ // src/db/schema/conversation-messages.ts
234
+ import { index as index5, jsonb as jsonb5, pgTable as pgTable6, primaryKey as primaryKey2, text as text6 } from "drizzle-orm/pg-core";
235
+ var juniorConversationMessages = pgTable6(
236
+ "junior_conversation_messages",
237
+ {
238
+ conversationId: text6("conversation_id").notNull().references(() => juniorConversations.conversationId),
239
+ messageId: text6("message_id").notNull(),
240
+ role: text6("role").$type().notNull(),
241
+ authorIdentityId: text6("author_identity_id").references(
242
+ () => juniorIdentities.id
243
+ ),
244
+ text: text6("text").notNull(),
245
+ meta: jsonb5("meta").$type(),
246
+ repliedAt: timestamptz("replied_at"),
247
+ createdAt: timestamptz("created_at").notNull()
248
+ },
249
+ (table) => [
250
+ primaryKey2({ columns: [table.conversationId, table.messageId] }),
251
+ index5("junior_conversation_messages_activity_idx").on(
252
+ table.conversationId,
253
+ table.createdAt
254
+ )
255
+ ]
256
+ );
257
+
258
+ // src/db/schema/migrations.ts
259
+ import { pgTable as pgTable7, text as text7 } from "drizzle-orm/pg-core";
260
+ var juniorSchemaMigrations = pgTable7("junior_schema_migrations", {
261
+ id: text7("id").primaryKey(),
262
+ checksum: text7("checksum").notNull(),
191
263
  appliedAt: timestamptz("applied_at").notNull().defaultNow()
192
264
  });
193
265
 
194
- // src/chat/conversations/sql/schema.ts
195
- var schema = {
266
+ // src/db/schema.ts
267
+ var juniorSqlSchema = {
268
+ juniorAgentSteps,
269
+ juniorConversationMessages,
196
270
  juniorConversations,
197
271
  juniorDestinations,
198
272
  juniorIdentities,
@@ -200,11 +274,6 @@ var schema = {
200
274
  juniorUsers
201
275
  };
202
276
 
203
- // src/chat/sql/schema.ts
204
- var juniorSqlSchema = {
205
- ...schema
206
- };
207
-
208
277
  // src/chat/identities/sql.ts
209
278
  function dateFromMs(ms) {
210
279
  return new Date(ms);
@@ -312,6 +381,8 @@ async function upsertIdentity(executor, identity, nowMs = Date.now()) {
312
381
 
313
382
  // src/chat/conversations/sql/migrations.ts
314
383
  import { createHash } from "crypto";
384
+ import { readFileSync } from "fs";
385
+ import { fileURLToPath } from "url";
315
386
  import { z } from "zod";
316
387
  var MIGRATION_LOCK_NAME = "junior_conversation_schema";
317
388
  var migrationRecordSchema = z.object({
@@ -594,6 +665,53 @@ ALTER TABLE junior_conversations
594
665
  DROP COLUMN IF EXISTS requester_json
595
666
  `
596
667
  ];
668
+ var conversationTranscriptStatements = [
669
+ `
670
+ CREATE TABLE IF NOT EXISTS junior_agent_steps (
671
+ conversation_id TEXT NOT NULL REFERENCES junior_conversations (conversation_id),
672
+ seq INTEGER NOT NULL,
673
+ context_epoch INTEGER NOT NULL,
674
+ type TEXT NOT NULL,
675
+ role TEXT,
676
+ payload JSONB NOT NULL,
677
+ created_at TIMESTAMPTZ NOT NULL,
678
+ PRIMARY KEY (conversation_id, seq)
679
+ )
680
+ `,
681
+ `
682
+ CREATE INDEX IF NOT EXISTS junior_agent_steps_epoch_idx
683
+ ON junior_agent_steps (conversation_id, context_epoch, seq)
684
+ `,
685
+ `
686
+ CREATE TABLE IF NOT EXISTS junior_conversation_messages (
687
+ conversation_id TEXT NOT NULL REFERENCES junior_conversations (conversation_id),
688
+ message_id TEXT NOT NULL,
689
+ role TEXT NOT NULL,
690
+ author_identity_id TEXT REFERENCES junior_identities (id),
691
+ text TEXT NOT NULL,
692
+ meta JSONB,
693
+ replied_at TIMESTAMPTZ,
694
+ created_at TIMESTAMPTZ NOT NULL,
695
+ PRIMARY KEY (conversation_id, message_id)
696
+ )
697
+ `,
698
+ `
699
+ CREATE INDEX IF NOT EXISTS junior_conversation_messages_activity_idx
700
+ ON junior_conversation_messages (conversation_id, created_at)
701
+ `,
702
+ `
703
+ ALTER TABLE junior_conversations
704
+ ADD COLUMN IF NOT EXISTS parent_conversation_id TEXT REFERENCES junior_conversations (conversation_id)
705
+ `,
706
+ `
707
+ ALTER TABLE junior_conversations
708
+ ADD COLUMN IF NOT EXISTS transcript_purged_at TIMESTAMPTZ
709
+ `,
710
+ `
711
+ CREATE INDEX IF NOT EXISTS junior_conversations_parent_idx
712
+ ON junior_conversations (parent_conversation_id)
713
+ `
714
+ ];
597
715
  var migrations = [
598
716
  defineMigration("0001_conversation_core", coreMetadataStatements),
599
717
  defineMigration(
@@ -601,7 +719,11 @@ var migrations = [
601
719
  destinationVisibilityBackfillStatements
602
720
  ),
603
721
  defineMigration("0003_user_identities", userIdentityStatements),
604
- defineMigration("0004_actor_cutover", actorCutoverStatements)
722
+ defineMigration("0004_actor_cutover", actorCutoverStatements),
723
+ defineMigration(
724
+ "0005_conversation_transcripts",
725
+ conversationTranscriptStatements
726
+ )
605
727
  ];
606
728
  function parseStoredMigrationRecord(value) {
607
729
  return migrationRecordSchema.parse(value);
@@ -766,41 +888,54 @@ function executionStatusFromValue(value) {
766
888
  throw new Error("Conversation record execution status is invalid");
767
889
  }
768
890
  function privacyFromRow(row) {
769
- if (row.destinationVisibility === null) {
891
+ if (row.destination === null) {
770
892
  return void 0;
771
893
  }
772
- return row.destinationVisibility === "public" ? "public" : "private";
894
+ return row.destination.visibility === "public" ? "public" : "private";
773
895
  }
774
- function actorFromIdentityRow(identity, fallback) {
775
- if (!identity || identity.provider !== "slack") {
776
- return fallback;
896
+ function actorFromIdentityRow(identity) {
897
+ if (!identity) {
898
+ return void 0;
899
+ }
900
+ if (identity.provider !== "slack") {
901
+ return void 0;
777
902
  }
778
903
  return {
779
- ...fallback ?? {},
780
904
  ...identity.emailNormalized ? { email: identity.emailNormalized } : identity.email ? { email: identity.email } : {},
781
905
  ...identity.displayName ? { fullName: identity.displayName } : {},
782
906
  platform: "slack",
783
907
  slackUserId: identity.providerSubjectId,
784
908
  ...identity.handle ? { slackUserName: identity.handle } : {},
785
- ...identity.providerTenantId || fallback?.teamId ? { teamId: identity.providerTenantId || fallback?.teamId } : {}
909
+ ...identity.providerTenantId ? { teamId: identity.providerTenantId } : {}
786
910
  };
787
911
  }
912
+ function destinationFromRow(destination) {
913
+ const value = destination?.provider === "slack" ? {
914
+ platform: "slack",
915
+ teamId: destination.providerTenantId,
916
+ channelId: destination.providerDestinationId
917
+ } : destination?.provider === "local" ? {
918
+ platform: "local",
919
+ conversationId: destination.providerDestinationId
920
+ } : void 0;
921
+ return parseDestination(value);
922
+ }
788
923
  function conversationFromRow(readRow) {
789
924
  const row = readRow.conversation;
790
925
  const visibility = privacyFromRow(readRow);
791
926
  if (row.schemaVersion !== 1) {
792
927
  throw new Error("Conversation record schema version is invalid");
793
928
  }
794
- const destination = row.destination === void 0 || row.destination === null ? void 0 : parseDestination(row.destination);
795
- const actor = actorFromIdentityRow(
796
- readRow.actorIdentity,
797
- parseStoredSlackActor(row.actor)
798
- );
799
- if (row.destination !== void 0 && row.destination !== null && !destination) {
800
- throw new Error("Conversation record destination is invalid");
929
+ if (row.destination !== null && readRow.destination === null) {
930
+ throw new Error("Conversation legacy destination is not migrated");
931
+ }
932
+ if (row.actor !== null && readRow.actorIdentity === null) {
933
+ throw new Error("Conversation legacy actor is not migrated");
801
934
  }
802
- if (row.actor !== void 0 && row.actor !== null && !actor) {
803
- throw new Error("Conversation record actor is invalid");
935
+ const destination = destinationFromRow(readRow.destination);
936
+ const actor = actorFromIdentityRow(readRow.actorIdentity);
937
+ if (readRow.destination !== null && !destination) {
938
+ throw new Error("Conversation record destination is invalid");
804
939
  }
805
940
  const source = row.source === void 0 || row.source === null ? void 0 : sourceFromValue(row.source);
806
941
  if (row.source !== void 0 && row.source !== null && !source) {
@@ -825,6 +960,7 @@ function conversationFromRow(readRow) {
825
960
  ...row.channelName ? { channelName: row.channelName } : {},
826
961
  ...source ? { source } : {},
827
962
  ...row.title ? { title: row.title } : {},
963
+ ...msFromDate(row.transcriptPurgedAt) !== void 0 ? { transcriptPurgedAtMs: msFromDate(row.transcriptPurgedAt) } : {},
828
964
  ...visibility ? { visibility } : {}
829
965
  };
830
966
  }
@@ -961,6 +1097,31 @@ var SqlStore = class {
961
1097
  });
962
1098
  });
963
1099
  }
1100
+ async ensureChildConversation(args) {
1101
+ const at = dateFromMs2(args.nowMs ?? now());
1102
+ await this.executor.db().insert(juniorConversations).values({
1103
+ conversationId: args.parentConversationId,
1104
+ schemaVersion: 1,
1105
+ createdAt: at,
1106
+ lastActivityAt: at,
1107
+ updatedAt: at,
1108
+ executionStatus: "idle"
1109
+ }).onConflictDoNothing({ target: juniorConversations.conversationId });
1110
+ await this.executor.db().insert(juniorConversations).values({
1111
+ conversationId: args.conversationId,
1112
+ schemaVersion: 1,
1113
+ parentConversationId: args.parentConversationId,
1114
+ createdAt: at,
1115
+ lastActivityAt: at,
1116
+ updatedAt: at,
1117
+ executionStatus: "idle"
1118
+ }).onConflictDoUpdate({
1119
+ target: juniorConversations.conversationId,
1120
+ set: {
1121
+ parentConversationId: sql4`coalesce(${juniorConversations.parentConversationId}, excluded.parent_conversation_id)`
1122
+ }
1123
+ });
1124
+ }
964
1125
  /** Copy one conversation record into SQL during backfill. */
965
1126
  async backfillConversation(sourceConversation) {
966
1127
  const { visibility: _visibility, ...conversation } = sourceConversation;
@@ -1001,7 +1162,7 @@ var SqlStore = class {
1001
1162
  async listByActivity(args = {}) {
1002
1163
  const rows = await this.executor.db().select({
1003
1164
  conversation: juniorConversations,
1004
- destinationVisibility: juniorDestinations.visibility,
1165
+ destination: juniorDestinations,
1005
1166
  actorIdentity: juniorIdentities
1006
1167
  }).from(juniorConversations).leftJoin(
1007
1168
  juniorDestinations,
@@ -1009,7 +1170,7 @@ var SqlStore = class {
1009
1170
  ).leftJoin(
1010
1171
  juniorIdentities,
1011
1172
  eq2(juniorIdentities.id, juniorConversations.actorIdentityId)
1012
- ).orderBy(
1173
+ ).where(isNull(juniorConversations.parentConversationId)).orderBy(
1013
1174
  desc(juniorConversations.lastActivityAt),
1014
1175
  asc(juniorConversations.conversationId)
1015
1176
  ).limit(Math.max(0, args.limit ?? 1e4)).offset(Math.max(0, args.offset ?? 0));
@@ -1051,7 +1212,7 @@ var SqlStore = class {
1051
1212
  async readConversationRow(conversationId) {
1052
1213
  const rows = await this.executor.db().select({
1053
1214
  conversation: juniorConversations,
1054
- destinationVisibility: juniorDestinations.visibility,
1215
+ destination: juniorDestinations,
1055
1216
  actorIdentity: juniorIdentities
1056
1217
  }).from(juniorConversations).leftJoin(
1057
1218
  juniorDestinations,
@@ -1091,11 +1252,11 @@ var SqlStore = class {
1091
1252
  originId: null,
1092
1253
  originRunId: null,
1093
1254
  destinationId: destinationId ?? null,
1094
- destination: conversation.destination ?? null,
1255
+ destination: null,
1095
1256
  actorIdentityId: actorIdentity?.id ?? null,
1096
1257
  creatorIdentityId: null,
1097
1258
  credentialSubjectIdentityId: null,
1098
- actor: conversation.actor ?? null,
1259
+ actor: null,
1099
1260
  channelName: conversation.channelName ?? null,
1100
1261
  title: conversation.title ?? null,
1101
1262
  createdAt: dateFromMs2(conversation.createdAtMs),
@@ -1114,11 +1275,9 @@ var SqlStore = class {
1114
1275
  originId: sql4`coalesce(excluded.origin_id, ${juniorConversations.originId})`,
1115
1276
  originRunId: sql4`coalesce(excluded.origin_run_id, ${juniorConversations.originRunId})`,
1116
1277
  destinationId: sql4`coalesce(excluded.destination_id, ${juniorConversations.destinationId})`,
1117
- destination: sql4`coalesce(excluded.destination_json, ${juniorConversations.destination})`,
1118
1278
  actorIdentityId: sql4`coalesce(excluded.actor_identity_id, ${juniorConversations.actorIdentityId})`,
1119
1279
  creatorIdentityId: sql4`coalesce(excluded.creator_identity_id, ${juniorConversations.creatorIdentityId})`,
1120
1280
  credentialSubjectIdentityId: sql4`coalesce(excluded.credential_subject_identity_id, ${juniorConversations.credentialSubjectIdentityId})`,
1121
- actor: sql4`coalesce(excluded.actor_json, ${juniorConversations.actor})`,
1122
1281
  channelName: sql4`coalesce(excluded.channel_name, ${juniorConversations.channelName})`,
1123
1282
  title: sql4`coalesce(excluded.title, ${juniorConversations.title})`,
1124
1283
  createdAt: sql4`least(${juniorConversations.createdAt}, excluded.created_at)`,
@@ -1173,7 +1332,626 @@ function createSqlStore(executor) {
1173
1332
  return new SqlStore(executor, executor);
1174
1333
  }
1175
1334
 
1176
- // src/chat/sql/neon.ts
1335
+ // src/chat/conversations/sql/history.ts
1336
+ import { and as and3, asc as asc2, eq as eq3, sql as sql6 } from "drizzle-orm";
1337
+
1338
+ // src/chat/conversations/history.ts
1339
+ import { z as z4 } from "zod";
1340
+
1341
+ // src/chat/pi/messages.ts
1342
+ import { z as z2 } from "zod";
1343
+ var piMessageSchema = z2.object({
1344
+ role: z2.string()
1345
+ }).passthrough().transform((value) => value);
1346
+ var piContentMessageSchema = z2.object({
1347
+ content: z2.array(z2.unknown()),
1348
+ role: z2.string().min(1)
1349
+ }).passthrough().transform((value) => value);
1350
+
1351
+ // src/chat/state/session-log.ts
1352
+ import { isDeepStrictEqual } from "util";
1353
+ import { z as z3 } from "zod";
1354
+ import { actorSchema } from "@sentry/junior-plugin-api";
1355
+ var AGENT_SESSION_LOG_PREFIX = "junior:agent-session-log";
1356
+ var AGENT_SESSION_LOG_SCHEMA_VERSION = 2;
1357
+ var INITIAL_SESSION_ID = "session_0";
1358
+ var STATE_STORE_LOCK_TTL_MS = 5e3;
1359
+ var schemaVersionSchema = z3.union([z3.literal(1), z3.literal(2)]);
1360
+ var piMessageAuthoritySchema = z3.union([
1361
+ z3.literal("instruction"),
1362
+ z3.literal("context")
1363
+ ]);
1364
+ var piMessageProvenanceSchema = z3.object({
1365
+ authority: piMessageAuthoritySchema,
1366
+ actor: actorSchema.optional()
1367
+ }).strict();
1368
+ var unattributedContextProvenance = {
1369
+ authority: "context"
1370
+ };
1371
+ function instructionProvenance(actor) {
1372
+ return actor ? { authority: "instruction", actor } : { authority: "instruction" };
1373
+ }
1374
+ function isDefaultContextProvenance(provenance) {
1375
+ return provenance.authority === "context" && !provenance.actor;
1376
+ }
1377
+ function legacyActorProvenance(actor) {
1378
+ if (actor.teamId && actor.slackUserId && actor.platform) {
1379
+ return instructionProvenance({
1380
+ platform: "slack",
1381
+ teamId: actor.teamId,
1382
+ userId: actor.slackUserId,
1383
+ ...actor.slackUserName ? { userName: actor.slackUserName } : {},
1384
+ ...actor.fullName ? { fullName: actor.fullName } : {},
1385
+ ...actor.email ? { email: actor.email } : {}
1386
+ });
1387
+ }
1388
+ return unattributedContextProvenance;
1389
+ }
1390
+ var piMessageEntrySchema = z3.object({
1391
+ schemaVersion: schemaVersionSchema,
1392
+ type: z3.literal("pi_message"),
1393
+ sessionId: z3.string().min(1).default(INITIAL_SESSION_ID),
1394
+ message: piMessageSchema,
1395
+ provenance: piMessageProvenanceSchema.optional(),
1396
+ // Legacy v1 latest-wins actor, decoded into provenance on read.
1397
+ actor: storedSlackActorSchema.optional()
1398
+ });
1399
+ var projectionResetEntrySchema = z3.object({
1400
+ schemaVersion: schemaVersionSchema,
1401
+ type: z3.literal("projection_reset"),
1402
+ sessionId: z3.string().min(1).default(INITIAL_SESSION_ID),
1403
+ messages: z3.array(piMessageSchema),
1404
+ provenance: z3.array(piMessageProvenanceSchema).optional(),
1405
+ // Legacy v1 latest-wins actor; v1 resets carry no per-message provenance.
1406
+ actor: storedSlackActorSchema.optional()
1407
+ });
1408
+ var actorRecordedEntrySchema = z3.object({
1409
+ schemaVersion: schemaVersionSchema,
1410
+ type: z3.literal("actor_recorded"),
1411
+ sessionId: z3.string().min(1).default(INITIAL_SESSION_ID),
1412
+ actor: storedSlackActorSchema
1413
+ });
1414
+ var mcpProviderConnectedEntrySchema = z3.object({
1415
+ schemaVersion: schemaVersionSchema,
1416
+ type: z3.literal("mcp_provider_connected"),
1417
+ sessionId: z3.string().min(1).default(INITIAL_SESSION_ID),
1418
+ provider: z3.string().min(1)
1419
+ });
1420
+ var authorizationKindSchema = z3.union([
1421
+ z3.literal("plugin"),
1422
+ z3.literal("mcp")
1423
+ ]);
1424
+ var authorizationRequestedEntrySchema = z3.object({
1425
+ schemaVersion: schemaVersionSchema,
1426
+ type: z3.literal("authorization_requested"),
1427
+ sessionId: z3.string().min(1).default(INITIAL_SESSION_ID),
1428
+ createdAtMs: z3.number().int().nonnegative(),
1429
+ kind: authorizationKindSchema,
1430
+ provider: z3.string().min(1),
1431
+ actorId: z3.string().min(1),
1432
+ authorizationId: z3.string().min(1),
1433
+ delivery: z3.union([
1434
+ z3.literal("private_link_sent"),
1435
+ z3.literal("private_link_reused")
1436
+ ])
1437
+ });
1438
+ var authorizationCompletedEntrySchema = z3.object({
1439
+ schemaVersion: schemaVersionSchema,
1440
+ type: z3.literal("authorization_completed"),
1441
+ sessionId: z3.string().min(1).default(INITIAL_SESSION_ID),
1442
+ createdAtMs: z3.number().int().nonnegative(),
1443
+ kind: authorizationKindSchema,
1444
+ provider: z3.string().min(1),
1445
+ actorId: z3.string().min(1),
1446
+ authorizationId: z3.string().min(1)
1447
+ });
1448
+ var transcriptRefSchema = z3.object({
1449
+ type: z3.literal("advisor_session"),
1450
+ parentConversationId: z3.string().min(1),
1451
+ key: z3.string().min(1)
1452
+ });
1453
+ var toolExecutionStartedEntrySchema = z3.object({
1454
+ schemaVersion: schemaVersionSchema,
1455
+ type: z3.literal("tool_execution_started"),
1456
+ sessionId: z3.string().min(1).default(INITIAL_SESSION_ID),
1457
+ createdAtMs: z3.number().int().nonnegative(),
1458
+ toolCallId: z3.string().min(1),
1459
+ toolName: z3.string().min(1),
1460
+ args: z3.unknown().optional()
1461
+ });
1462
+ var subagentStartedEntrySchema = z3.object({
1463
+ schemaVersion: schemaVersionSchema,
1464
+ type: z3.literal("subagent_started"),
1465
+ sessionId: z3.string().min(1).default(INITIAL_SESSION_ID),
1466
+ subagentInvocationId: z3.string().min(1),
1467
+ subagentKind: z3.string().min(1),
1468
+ parentToolCallId: z3.string().min(1).optional(),
1469
+ parentConversationId: z3.string().min(1),
1470
+ parentSessionId: z3.string().min(1).optional(),
1471
+ transcriptRef: transcriptRefSchema,
1472
+ historyMode: z3.literal("shared"),
1473
+ modelId: z3.string().min(1).optional(),
1474
+ reasoningLevel: z3.string().min(1).optional(),
1475
+ createdAtMs: z3.number().int().nonnegative()
1476
+ });
1477
+ var subagentEndedEntrySchema = z3.object({
1478
+ schemaVersion: schemaVersionSchema,
1479
+ type: z3.literal("subagent_ended"),
1480
+ sessionId: z3.string().min(1).default(INITIAL_SESSION_ID),
1481
+ subagentInvocationId: z3.string().min(1),
1482
+ outcome: z3.union([
1483
+ z3.literal("success"),
1484
+ z3.literal("error"),
1485
+ z3.literal("aborted")
1486
+ ]),
1487
+ errorCode: z3.string().min(1).optional(),
1488
+ transcriptEndMessageIndex: z3.number().int().nonnegative().optional(),
1489
+ transcriptStartMessageIndex: z3.number().int().nonnegative().optional(),
1490
+ createdAtMs: z3.number().int().nonnegative()
1491
+ });
1492
+ var sessionLogEntrySchema = z3.discriminatedUnion("type", [
1493
+ piMessageEntrySchema,
1494
+ projectionResetEntrySchema,
1495
+ actorRecordedEntrySchema,
1496
+ mcpProviderConnectedEntrySchema,
1497
+ authorizationRequestedEntrySchema,
1498
+ authorizationCompletedEntrySchema,
1499
+ toolExecutionStartedEntrySchema,
1500
+ subagentStartedEntrySchema,
1501
+ subagentEndedEntrySchema
1502
+ ]);
1503
+ function key(scope) {
1504
+ const prefix = getChatConfig().state.keyPrefix;
1505
+ return [
1506
+ ...prefix ? [prefix] : [],
1507
+ AGENT_SESSION_LOG_PREFIX,
1508
+ scope.conversationId
1509
+ ].join(":");
1510
+ }
1511
+ function rawKey(scope) {
1512
+ return [AGENT_SESSION_LOG_PREFIX, scope.conversationId].join(":");
1513
+ }
1514
+ function storedRecord(value) {
1515
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
1516
+ }
1517
+ function migrateStoredEntry(value) {
1518
+ const record = storedRecord(value);
1519
+ if (!record) {
1520
+ return value;
1521
+ }
1522
+ const migrated = { ...record };
1523
+ if ("requester" in migrated && !("actor" in migrated)) {
1524
+ migrated.actor = migrated.requester;
1525
+ }
1526
+ delete migrated.requester;
1527
+ if (migrated.type === "requester_recorded") {
1528
+ migrated.type = "actor_recorded";
1529
+ }
1530
+ if ("requesterId" in migrated && !("actorId" in migrated)) {
1531
+ migrated.actorId = migrated.requesterId;
1532
+ }
1533
+ delete migrated.requesterId;
1534
+ return migrated;
1535
+ }
1536
+ function piEntry(message, sessionId, provenance) {
1537
+ return {
1538
+ schemaVersion: AGENT_SESSION_LOG_SCHEMA_VERSION,
1539
+ type: "pi_message",
1540
+ sessionId,
1541
+ message,
1542
+ // Ambient context is the decode default, so only attributed/instruction
1543
+ // provenance needs to be persisted on the entry.
1544
+ ...provenance && !isDefaultContextProvenance(provenance) ? { provenance } : {}
1545
+ };
1546
+ }
1547
+ function decode(value) {
1548
+ if (typeof value === "string") {
1549
+ return decode(JSON.parse(value));
1550
+ }
1551
+ const parsed = sessionLogEntrySchema.safeParse(migrateStoredEntry(value));
1552
+ if (parsed.success) {
1553
+ return parsed.data;
1554
+ }
1555
+ return piEntry(piMessageSchema.parse(value), INITIAL_SESSION_ID);
1556
+ }
1557
+ function actorIdentityKey(actor) {
1558
+ if (actor.platform === "system") {
1559
+ return `system ${actor.name}`;
1560
+ }
1561
+ return actor.platform === "slack" ? `slack\0${actor.teamId}\0${actor.userId}` : `${actor.platform}\0${actor.userId}`;
1562
+ }
1563
+ function instructionActors(provenance) {
1564
+ const seen = /* @__PURE__ */ new Set();
1565
+ const actors = [];
1566
+ for (const entry of provenance) {
1567
+ if (entry.authority !== "instruction" || !entry.actor) {
1568
+ continue;
1569
+ }
1570
+ const identity = actorIdentityKey(entry.actor);
1571
+ if (seen.has(identity)) {
1572
+ continue;
1573
+ }
1574
+ seen.add(identity);
1575
+ actors.push(entry.actor);
1576
+ }
1577
+ return actors;
1578
+ }
1579
+ function redisStore(redisStateAdapter) {
1580
+ const client = redisStateAdapter.getClient();
1581
+ return {
1582
+ async append({ entries, scope, ttlMs }) {
1583
+ const listKey = key(scope);
1584
+ if (entries.length > 0) {
1585
+ await client.rPush(
1586
+ listKey,
1587
+ entries.map((entry) => JSON.stringify(entry))
1588
+ );
1589
+ }
1590
+ await client.pExpire(listKey, Math.max(1, ttlMs));
1591
+ },
1592
+ async read(scope) {
1593
+ const values = await client.lRange(key(scope), 0, -1);
1594
+ return values.map(decode);
1595
+ }
1596
+ };
1597
+ }
1598
+ function stateStore() {
1599
+ const stateAdapter = getStateAdapter();
1600
+ return {
1601
+ async append({ entries, scope, ttlMs }) {
1602
+ const listKey = rawKey(scope);
1603
+ const lock = await stateAdapter.acquireLock(
1604
+ `${listKey}:commit`,
1605
+ STATE_STORE_LOCK_TTL_MS
1606
+ );
1607
+ if (!lock) {
1608
+ throw new Error("Could not acquire session log commit lock");
1609
+ }
1610
+ try {
1611
+ const existingValue = await stateAdapter.get(listKey);
1612
+ const existingEntries = Array.isArray(existingValue) ? existingValue.map(decode) : (await stateAdapter.getList(listKey)).map(decode);
1613
+ await stateAdapter.set(
1614
+ listKey,
1615
+ [...existingEntries, ...entries],
1616
+ Math.max(1, ttlMs)
1617
+ );
1618
+ } finally {
1619
+ await stateAdapter.releaseLock(lock);
1620
+ }
1621
+ },
1622
+ async read(scope) {
1623
+ const listKey = rawKey(scope);
1624
+ const value = await stateAdapter.get(listKey);
1625
+ if (Array.isArray(value)) {
1626
+ return value.map(decode);
1627
+ }
1628
+ const values = await stateAdapter.getList(listKey);
1629
+ return values.map(decode);
1630
+ }
1631
+ };
1632
+ }
1633
+ async function defaultStore() {
1634
+ const { redisStateAdapter, stateAdapter } = await getConnectedStateContext();
1635
+ if (redisStateAdapter) {
1636
+ return redisStore(redisStateAdapter);
1637
+ }
1638
+ await stateAdapter.connect();
1639
+ return stateStore();
1640
+ }
1641
+ async function loadEntries(args) {
1642
+ const store = args.store ?? await defaultStore();
1643
+ return (await store.read(args)).map(decode);
1644
+ }
1645
+ async function readSessionLogEntries(args) {
1646
+ return loadEntries(args);
1647
+ }
1648
+ function instructionProvenanceFor(actor) {
1649
+ return instructionProvenance(actor);
1650
+ }
1651
+ var contextProvenance = unattributedContextProvenance;
1652
+
1653
+ // src/chat/conversations/history.ts
1654
+ var piMessageStepEntrySchema = z4.object({
1655
+ type: z4.literal("pi_message"),
1656
+ schemaVersion: z4.number().int().optional(),
1657
+ message: piMessageSchema,
1658
+ provenance: piMessageProvenanceSchema.optional()
1659
+ });
1660
+ var contextEpochStartedEntrySchema = z4.object({
1661
+ type: z4.literal("context_epoch_started"),
1662
+ reason: z4.union([z4.literal("compaction"), z4.literal("rollback")])
1663
+ });
1664
+ var mcpProviderConnectedEntrySchema2 = z4.object({
1665
+ type: z4.literal("mcp_provider_connected"),
1666
+ provider: z4.string().min(1)
1667
+ });
1668
+ var authorizationKindSchema2 = z4.union([
1669
+ z4.literal("plugin"),
1670
+ z4.literal("mcp")
1671
+ ]);
1672
+ var authorizationRequestedEntrySchema2 = z4.object({
1673
+ type: z4.literal("authorization_requested"),
1674
+ kind: authorizationKindSchema2,
1675
+ provider: z4.string().min(1),
1676
+ actorId: z4.string().min(1),
1677
+ authorizationId: z4.string().min(1),
1678
+ delivery: z4.union([
1679
+ z4.literal("private_link_sent"),
1680
+ z4.literal("private_link_reused")
1681
+ ])
1682
+ });
1683
+ var authorizationCompletedEntrySchema2 = z4.object({
1684
+ type: z4.literal("authorization_completed"),
1685
+ kind: authorizationKindSchema2,
1686
+ provider: z4.string().min(1),
1687
+ actorId: z4.string().min(1),
1688
+ authorizationId: z4.string().min(1)
1689
+ });
1690
+ var toolExecutionStartedEntrySchema2 = z4.object({
1691
+ type: z4.literal("tool_execution_started"),
1692
+ toolCallId: z4.string().min(1),
1693
+ toolName: z4.string().min(1),
1694
+ args: z4.unknown().optional()
1695
+ });
1696
+ var visibleContextCompactedEntrySchema = z4.object({
1697
+ type: z4.literal("visible_context_compacted"),
1698
+ compactions: z4.array(
1699
+ z4.object({
1700
+ coveredMessageIds: z4.array(z4.string()),
1701
+ createdAtMs: z4.number(),
1702
+ id: z4.string().min(1),
1703
+ summary: z4.string()
1704
+ })
1705
+ )
1706
+ });
1707
+ var subagentStartedEntrySchema2 = z4.object({
1708
+ type: z4.literal("subagent_started"),
1709
+ subagentInvocationId: z4.string().min(1),
1710
+ subagentKind: z4.string().min(1),
1711
+ modelId: z4.string().min(1).optional(),
1712
+ parentToolCallId: z4.string().min(1).optional(),
1713
+ reasoningLevel: z4.string().min(1).optional(),
1714
+ childConversationId: z4.string().min(1),
1715
+ historyMode: z4.literal("shared")
1716
+ });
1717
+ var subagentEndedEntrySchema2 = z4.object({
1718
+ type: z4.literal("subagent_ended"),
1719
+ subagentInvocationId: z4.string().min(1),
1720
+ outcome: z4.union([
1721
+ z4.literal("success"),
1722
+ z4.literal("error"),
1723
+ z4.literal("aborted")
1724
+ ]),
1725
+ errorCode: z4.string().min(1).optional()
1726
+ });
1727
+ var agentStepEntrySchema = z4.discriminatedUnion("type", [
1728
+ piMessageStepEntrySchema,
1729
+ contextEpochStartedEntrySchema,
1730
+ mcpProviderConnectedEntrySchema2,
1731
+ authorizationRequestedEntrySchema2,
1732
+ authorizationCompletedEntrySchema2,
1733
+ toolExecutionStartedEntrySchema2,
1734
+ visibleContextCompactedEntrySchema,
1735
+ subagentStartedEntrySchema2,
1736
+ subagentEndedEntrySchema2
1737
+ ]);
1738
+
1739
+ // src/chat/conversations/sql/conversation-row.ts
1740
+ import { sql as sql5 } from "drizzle-orm";
1741
+ async function ensureConversationRow(executor, conversationId, atMs) {
1742
+ const at = new Date(atMs);
1743
+ await executor.db().insert(juniorConversations).values({
1744
+ conversationId,
1745
+ createdAt: at,
1746
+ lastActivityAt: at,
1747
+ updatedAt: at,
1748
+ executionStatus: "idle"
1749
+ }).onConflictDoUpdate({
1750
+ target: juniorConversations.conversationId,
1751
+ set: {
1752
+ lastActivityAt: sql5`greatest(${juniorConversations.lastActivityAt}, excluded.last_activity_at)`,
1753
+ updatedAt: sql5`greatest(${juniorConversations.updatedAt}, excluded.updated_at)`,
1754
+ transcriptPurgedAt: null
1755
+ }
1756
+ });
1757
+ }
1758
+
1759
+ // src/chat/conversations/sql/history.ts
1760
+ function messageRole(entry) {
1761
+ if (entry.type !== "pi_message") {
1762
+ return null;
1763
+ }
1764
+ const role = entry.message.role;
1765
+ return typeof role === "string" ? role : null;
1766
+ }
1767
+ function insertFromStep(conversationId, seq, contextEpoch, step) {
1768
+ const { type, ...payload } = agentStepEntrySchema.parse(step.entry);
1769
+ return {
1770
+ conversationId,
1771
+ seq,
1772
+ contextEpoch,
1773
+ type,
1774
+ role: messageRole(step.entry),
1775
+ payload,
1776
+ createdAt: new Date(step.createdAtMs)
1777
+ };
1778
+ }
1779
+ function stepFromRow(row) {
1780
+ const entry = agentStepEntrySchema.parse({ type: row.type, ...row.payload });
1781
+ return {
1782
+ seq: row.seq,
1783
+ contextEpoch: row.contextEpoch,
1784
+ createdAtMs: row.createdAt.getTime(),
1785
+ entry
1786
+ };
1787
+ }
1788
+ function piMessageStep(step) {
1789
+ return {
1790
+ entry: {
1791
+ type: "pi_message",
1792
+ message: step.message,
1793
+ ...step.provenance ? { provenance: step.provenance } : {}
1794
+ },
1795
+ createdAtMs: step.createdAtMs
1796
+ };
1797
+ }
1798
+ var SqlAgentStepStore = class {
1799
+ constructor(executor) {
1800
+ this.executor = executor;
1801
+ }
1802
+ executor;
1803
+ async append(conversationId, steps) {
1804
+ if (steps.length === 0) {
1805
+ return;
1806
+ }
1807
+ const newestCreatedAtMs = Math.max(
1808
+ ...steps.map((step) => step.createdAtMs)
1809
+ );
1810
+ await this.executor.transaction(async () => {
1811
+ await ensureConversationRow(
1812
+ this.executor,
1813
+ conversationId,
1814
+ newestCreatedAtMs
1815
+ );
1816
+ const cursor = await this.readCursor(conversationId);
1817
+ const contextEpoch = cursor.maxEpoch ?? 0;
1818
+ let seq = cursor.nextSeq;
1819
+ const rows = steps.map(
1820
+ (step) => insertFromStep(conversationId, seq++, contextEpoch, step)
1821
+ );
1822
+ await this.executor.db().insert(juniorAgentSteps).values(rows);
1823
+ });
1824
+ }
1825
+ async startEpoch(conversationId, opts) {
1826
+ await this.executor.transaction(async () => {
1827
+ await ensureConversationRow(this.executor, conversationId, Date.now());
1828
+ const cursor = await this.readCursor(conversationId);
1829
+ const contextEpoch = (cursor.maxEpoch ?? -1) + 1;
1830
+ let seq = cursor.nextSeq;
1831
+ const marker = {
1832
+ entry: { type: "context_epoch_started", reason: opts.reason },
1833
+ createdAtMs: Date.now()
1834
+ };
1835
+ const rows = [marker, ...opts.messages.map(piMessageStep)].map(
1836
+ (step) => insertFromStep(conversationId, seq++, contextEpoch, step)
1837
+ );
1838
+ await this.executor.db().insert(juniorAgentSteps).values(rows);
1839
+ });
1840
+ }
1841
+ async loadCurrentEpoch(conversationId) {
1842
+ const cursor = await this.readCursor(conversationId);
1843
+ if (cursor.maxEpoch === null) {
1844
+ return [];
1845
+ }
1846
+ const rows = await this.executor.db().select().from(juniorAgentSteps).where(
1847
+ and3(
1848
+ eq3(juniorAgentSteps.conversationId, conversationId),
1849
+ eq3(juniorAgentSteps.contextEpoch, cursor.maxEpoch)
1850
+ )
1851
+ ).orderBy(asc2(juniorAgentSteps.seq));
1852
+ return rows.map(stepFromRow);
1853
+ }
1854
+ async loadHistory(conversationId) {
1855
+ const rows = await this.executor.db().select().from(juniorAgentSteps).where(eq3(juniorAgentSteps.conversationId, conversationId)).orderBy(asc2(juniorAgentSteps.seq));
1856
+ return rows.map(stepFromRow);
1857
+ }
1858
+ /** Read the next `seq` and current highest epoch for one conversation. */
1859
+ async readCursor(conversationId) {
1860
+ const rows = await this.executor.db().select({
1861
+ maxSeq: sql6`max(${juniorAgentSteps.seq})`,
1862
+ maxEpoch: sql6`max(${juniorAgentSteps.contextEpoch})`
1863
+ }).from(juniorAgentSteps).where(eq3(juniorAgentSteps.conversationId, conversationId));
1864
+ const maxSeq = rows[0]?.maxSeq;
1865
+ const maxEpoch = rows[0]?.maxEpoch;
1866
+ return {
1867
+ maxEpoch: maxEpoch === null || maxEpoch === void 0 ? null : Number(maxEpoch),
1868
+ nextSeq: maxSeq === null || maxSeq === void 0 ? 0 : Number(maxSeq) + 1
1869
+ };
1870
+ }
1871
+ };
1872
+ function createSqlAgentStepStore(executor) {
1873
+ return new SqlAgentStepStore(executor);
1874
+ }
1875
+
1876
+ // src/chat/conversations/sql/messages.ts
1877
+ import { and as and4, asc as asc3, eq as eq4, isNull as isNull2, sql as sql7 } from "drizzle-orm";
1878
+ function messageFromRow(row) {
1879
+ return {
1880
+ conversationId: row.conversationId,
1881
+ messageId: row.messageId,
1882
+ role: row.role,
1883
+ text: row.text,
1884
+ createdAtMs: row.createdAt.getTime(),
1885
+ ...row.authorIdentityId ? { authorIdentityId: row.authorIdentityId } : {},
1886
+ ...row.meta ? { meta: row.meta } : {},
1887
+ ...row.repliedAt ? { repliedAtMs: row.repliedAt.getTime() } : {}
1888
+ };
1889
+ }
1890
+ var SqlConversationMessageStore = class {
1891
+ constructor(executor) {
1892
+ this.executor = executor;
1893
+ }
1894
+ executor;
1895
+ async record(conversationId, messages) {
1896
+ if (messages.length === 0) {
1897
+ return;
1898
+ }
1899
+ const newestCreatedAtMs = Math.max(
1900
+ ...messages.map((message) => message.createdAtMs)
1901
+ );
1902
+ await this.executor.transaction(async () => {
1903
+ await ensureConversationRow(
1904
+ this.executor,
1905
+ conversationId,
1906
+ newestCreatedAtMs
1907
+ );
1908
+ await this.executor.db().insert(juniorConversationMessages).values(
1909
+ messages.map((message) => ({
1910
+ conversationId,
1911
+ messageId: message.messageId,
1912
+ role: message.role,
1913
+ authorIdentityId: message.authorIdentityId ?? null,
1914
+ text: message.text,
1915
+ meta: message.meta ?? null,
1916
+ repliedAt: null,
1917
+ createdAt: new Date(message.createdAtMs)
1918
+ }))
1919
+ ).onConflictDoUpdate({
1920
+ target: [
1921
+ juniorConversationMessages.conversationId,
1922
+ juniorConversationMessages.messageId
1923
+ ],
1924
+ set: {
1925
+ meta: sql7`nullif(coalesce(${juniorConversationMessages.meta}, '{}'::jsonb) || coalesce(excluded.meta, '{}'::jsonb), '{}'::jsonb)`
1926
+ }
1927
+ });
1928
+ });
1929
+ }
1930
+ async markReplied(conversationId, messageId, repliedAtMs) {
1931
+ await this.executor.db().update(juniorConversationMessages).set({
1932
+ repliedAt: sql7`coalesce(${juniorConversationMessages.repliedAt}, ${new Date(repliedAtMs)})`
1933
+ }).where(
1934
+ and4(
1935
+ eq4(juniorConversationMessages.conversationId, conversationId),
1936
+ eq4(juniorConversationMessages.messageId, messageId),
1937
+ isNull2(juniorConversationMessages.repliedAt)
1938
+ )
1939
+ );
1940
+ }
1941
+ async list(conversationId, opts = {}) {
1942
+ const query = this.executor.db().select().from(juniorConversationMessages).where(eq4(juniorConversationMessages.conversationId, conversationId)).orderBy(
1943
+ asc3(juniorConversationMessages.createdAt),
1944
+ asc3(juniorConversationMessages.messageId)
1945
+ );
1946
+ const rows = opts.limit === void 0 ? await query : await query.limit(Math.max(0, opts.limit));
1947
+ return rows.map(messageFromRow);
1948
+ }
1949
+ };
1950
+ function createSqlConversationMessageStore(executor) {
1951
+ return new SqlConversationMessageStore(executor);
1952
+ }
1953
+
1954
+ // src/db/neon.ts
1177
1955
  import { AsyncLocalStorage } from "async_hooks";
1178
1956
  import {
1179
1957
  Pool
@@ -1275,7 +2053,7 @@ function createNeonJuniorSqlExecutor(args) {
1275
2053
  );
1276
2054
  }
1277
2055
 
1278
- // src/chat/sql/postgres.ts
2056
+ // src/db/postgres.ts
1279
2057
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
1280
2058
  import pg from "pg";
1281
2059
  import { drizzle as drizzle2 } from "drizzle-orm/node-postgres";
@@ -1377,7 +2155,7 @@ function createPostgresJuniorSqlExecutor(args) {
1377
2155
  );
1378
2156
  }
1379
2157
 
1380
- // src/chat/sql/executor.ts
2158
+ // src/db/executor.ts
1381
2159
  function createJuniorSqlExecutor(args) {
1382
2160
  if (args.driver === "postgres") {
1383
2161
  return createPostgresJuniorSqlExecutor(args);
@@ -1394,22 +2172,24 @@ function createDb(args) {
1394
2172
  });
1395
2173
  }
1396
2174
  function getSqlExecutor() {
1397
- const { sql: sql5 } = getChatConfig();
1398
- if (current?.databaseUrl !== sql5.databaseUrl || current.driver !== sql5.driver) {
2175
+ const { sql: sql8 } = getChatConfig();
2176
+ if (current?.databaseUrl !== sql8.databaseUrl || current.driver !== sql8.driver) {
1399
2177
  if (current) {
1400
2178
  const previous = current;
1401
2179
  current = void 0;
1402
2180
  void previous.db.close().catch(() => void 0);
1403
2181
  }
1404
2182
  const db = createDb({
1405
- databaseUrl: sql5.databaseUrl,
1406
- driver: sql5.driver
2183
+ databaseUrl: sql8.databaseUrl,
2184
+ driver: sql8.driver
1407
2185
  });
1408
2186
  current = {
1409
- databaseUrl: sql5.databaseUrl,
1410
- driver: sql5.driver,
2187
+ databaseUrl: sql8.databaseUrl,
2188
+ driver: sql8.driver,
1411
2189
  db,
1412
- store: createSqlStore(db)
2190
+ store: createSqlStore(db),
2191
+ stepStore: createSqlAgentStepStore(db),
2192
+ messageStore: createSqlConversationMessageStore(db)
1413
2193
  };
1414
2194
  }
1415
2195
  return current.db;
@@ -1421,6 +2201,14 @@ function getConversationStore() {
1421
2201
  getSqlExecutor();
1422
2202
  return current.store;
1423
2203
  }
2204
+ function getAgentStepStore() {
2205
+ getSqlExecutor();
2206
+ return current.stepStore;
2207
+ }
2208
+ function getConversationMessageStore() {
2209
+ getSqlExecutor();
2210
+ return current.messageStore;
2211
+ }
1424
2212
  async function closeDb() {
1425
2213
  const previous = current;
1426
2214
  current = void 0;
@@ -1432,9 +2220,23 @@ export {
1432
2220
  juniorUsers,
1433
2221
  juniorIdentities,
1434
2222
  juniorConversations,
2223
+ juniorAgentSteps,
2224
+ juniorConversationMessages,
2225
+ migrateSchema,
1435
2226
  createSqlStore,
2227
+ legacyActorProvenance,
2228
+ instructionActors,
2229
+ readSessionLogEntries,
2230
+ instructionProvenanceFor,
2231
+ contextProvenance,
2232
+ agentStepEntrySchema,
2233
+ createSqlAgentStepStore,
2234
+ createSqlConversationMessageStore,
1436
2235
  createJuniorSqlExecutor,
2236
+ getSqlExecutor,
1437
2237
  getDb,
1438
2238
  getConversationStore,
2239
+ getAgentStepStore,
2240
+ getConversationMessageStore,
1439
2241
  closeDb
1440
2242
  };