@sentry/junior 0.86.0 → 0.88.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 (61) hide show
  1. package/dist/{agent-hooks-NWOUB3UR.js → agent-hooks-RIJ5TSWP.js} +8 -8
  2. package/dist/api/people/list.d.ts +4 -0
  3. package/dist/api/people/list.js +83 -0
  4. package/dist/api/people/list.query.d.ts +4 -0
  5. package/dist/api/people/profile.d.ts +4 -0
  6. package/dist/api/people/profile.js +131 -0
  7. package/dist/api/people/profile.query.d.ts +4 -0
  8. package/dist/api/people/shared.d.ts +76 -0
  9. package/dist/api/people/types.d.ts +85 -0
  10. package/dist/app.js +45 -74
  11. package/dist/{catalog-runtime-XOD7YPRS.js → catalog-runtime-MW7XERSU.js} +3 -3
  12. package/dist/chat/conversations/sql/migrations.d.ts +1 -1
  13. package/dist/chat/conversations/sql/schema/identities.d.ts +54 -3
  14. package/dist/chat/conversations/sql/schema/users.d.ts +109 -0
  15. package/dist/chat/conversations/sql/schema.d.ts +164 -3
  16. package/dist/chat/conversations/sql/store.d.ts +0 -1
  17. package/dist/chat/identities/identity.d.ts +18 -0
  18. package/dist/chat/identities/sql.d.ts +4 -0
  19. package/dist/chat/no-reply.d.ts +7 -0
  20. package/dist/chat/runtime/processing-reaction.d.ts +2 -1
  21. package/dist/chat/runtime/slack-resume.d.ts +3 -2
  22. package/dist/chat/runtime/thread-context.d.ts +3 -1
  23. package/dist/chat/runtime/turn-user-message.d.ts +2 -1
  24. package/dist/chat/services/conversation-memory.d.ts +3 -1
  25. package/dist/chat/services/reply-delivery-plan.d.ts +0 -4
  26. package/dist/chat/slack/message.d.ts +2 -1
  27. package/dist/chat/slack/outbound.d.ts +5 -4
  28. package/dist/chat/slack/timestamp.d.ts +6 -0
  29. package/dist/chat/sql/schema.d.ts +163 -3
  30. package/dist/chat/tools/slack/context.d.ts +2 -1
  31. package/dist/{chunk-ENPSU7L7.js → chunk-2DPZRS3B.js} +4 -4
  32. package/dist/{chunk-6I6HBOQM.js → chunk-4IXHIV37.js} +413 -157
  33. package/dist/{chunk-JRXCSSSU.js → chunk-6UPQ5GTJ.js} +3 -3
  34. package/dist/{chunk-64ACM6AP.js → chunk-7ES37H6U.js} +2 -2
  35. package/dist/{chunk-GGD6WK6V.js → chunk-AHLSXMOU.js} +2 -2
  36. package/dist/{chunk-2TAIGJPQ.js → chunk-C5NCV3OB.js} +1 -1
  37. package/dist/{chunk-6O5UI3RG.js → chunk-F22AM3OW.js} +1 -1
  38. package/dist/{chunk-BRSQQRG6.js → chunk-IATAYWFU.js} +2 -2
  39. package/dist/{chunk-BQ3ITJA6.js → chunk-KBFQXJL4.js} +2 -2
  40. package/dist/{chunk-37B2R2QJ.js → chunk-KBSLCOGE.js} +28 -15
  41. package/dist/{chunk-IXTBFABZ.js → chunk-KQKIA4CU.js} +1 -1
  42. package/dist/{chunk-FPHA6GCQ.js → chunk-NYJLQI42.js} +95 -92
  43. package/dist/chunk-PBV4ZIVM.js +261 -0
  44. package/dist/{chunk-ZU2ALUVQ.js → chunk-S35CPNPT.js} +1 -1
  45. package/dist/{chunk-RIB3M6YA.js → chunk-V6XDCS3X.js} +4 -4
  46. package/dist/{chunk-KF7522P3.js → chunk-VMBZFPZM.js} +11 -1
  47. package/dist/{chunk-2NFV5FMB.js → chunk-XS6NLQPA.js} +21 -12
  48. package/dist/{chunk-GB5DFM4D.js → chunk-YPAE5RH3.js} +1 -1
  49. package/dist/cli/chat.js +6 -6
  50. package/dist/cli/check.js +4 -4
  51. package/dist/cli/plugins.js +11 -11
  52. package/dist/cli/snapshot-warmup.js +6 -6
  53. package/dist/cli/upgrade.js +12 -12
  54. package/dist/{db-SZVUU7RB.js → db-37HOGLI7.js} +5 -5
  55. package/dist/nitro.js +4 -4
  56. package/dist/reporting/conversations.d.ts +0 -56
  57. package/dist/reporting.d.ts +2 -8
  58. package/dist/reporting.js +14 -290
  59. package/dist/{runner-HEBRPNN2.js → runner-BCRF2FCI.js} +13 -13
  60. package/dist/{validation-MBNJKWPQ.js → validation-FMJO3L5P.js} +4 -4
  61. package/package.json +14 -6
@@ -1,25 +1,28 @@
1
1
  import {
2
2
  parseDestination,
3
3
  sameDestination
4
- } from "./chunk-GGD6WK6V.js";
4
+ } from "./chunk-AHLSXMOU.js";
5
5
  import {
6
6
  getChatConfig
7
- } from "./chunk-ZU2ALUVQ.js";
7
+ } from "./chunk-S35CPNPT.js";
8
8
  import {
9
9
  parseStoredSlackRequester
10
- } from "./chunk-IXTBFABZ.js";
10
+ } from "./chunk-KQKIA4CU.js";
11
+ import {
12
+ normalizeIdentityEmail
13
+ } from "./chunk-VMBZFPZM.js";
11
14
 
12
15
  // src/chat/conversations/sql/store.ts
13
- import { randomUUID } from "crypto";
14
- import { and, asc, desc, eq, sql as sql2 } from "drizzle-orm";
16
+ import { randomUUID as randomUUID2 } from "crypto";
17
+ import { and as and2, asc, desc, eq as eq2, sql as sql4 } from "drizzle-orm";
15
18
 
16
- // src/chat/conversations/sql/migrations.ts
17
- import { createHash } from "crypto";
18
- import { z } from "zod";
19
+ // src/chat/identities/sql.ts
20
+ import { randomUUID } from "crypto";
21
+ import { and, eq, sql as sql3 } from "drizzle-orm";
19
22
 
20
23
  // src/chat/conversations/sql/schema/conversations.ts
21
- import { sql } from "drizzle-orm";
22
- import { index as index3, integer, jsonb as jsonb3, pgTable as pgTable3, text as text3 } from "drizzle-orm/pg-core";
24
+ import { sql as sql2 } from "drizzle-orm";
25
+ import { index as index3, integer, jsonb as jsonb3, pgTable as pgTable4, text as text4 } from "drizzle-orm/pg-core";
23
26
 
24
27
  // src/chat/conversations/sql/schema/destinations.ts
25
28
  import { index, jsonb, pgTable, text, uniqueIndex } from "drizzle-orm/pg-core";
@@ -58,68 +61,104 @@ var juniorDestinations = pgTable(
58
61
  );
59
62
 
60
63
  // src/chat/conversations/sql/schema/identities.ts
61
- import { index as index2, jsonb as jsonb2, pgTable as pgTable2, text as text2, uniqueIndex as uniqueIndex2 } from "drizzle-orm/pg-core";
62
- var juniorIdentities = pgTable2(
63
- "junior_identities",
64
+ import { sql } from "drizzle-orm";
65
+ import {
66
+ boolean,
67
+ index as index2,
68
+ jsonb as jsonb2,
69
+ pgTable as pgTable3,
70
+ text as text3,
71
+ uniqueIndex as uniqueIndex3
72
+ } from "drizzle-orm/pg-core";
73
+
74
+ // src/chat/conversations/sql/schema/users.ts
75
+ import { text as text2, pgTable as pgTable2, uniqueIndex as uniqueIndex2 } from "drizzle-orm/pg-core";
76
+ var juniorUsers = pgTable2(
77
+ "junior_users",
64
78
  {
65
79
  id: text2("id").primaryKey(),
66
- kind: text2("kind").$type().notNull(),
67
- provider: text2("provider").notNull(),
68
- providerTenantId: text2("provider_tenant_id").notNull().default(""),
69
- providerSubjectId: text2("provider_subject_id").notNull(),
80
+ primaryEmail: text2("primary_email").notNull(),
81
+ primaryEmailNormalized: text2("primary_email_normalized").notNull(),
70
82
  displayName: text2("display_name"),
71
- handle: text2("handle"),
72
- email: text2("email"),
73
- avatarUrl: text2("avatar_url"),
74
- metadata: jsonb2("metadata_json"),
75
83
  createdAt: timestamptz("created_at").notNull(),
76
84
  updatedAt: timestamptz("updated_at").notNull()
77
85
  },
78
86
  (table) => [
79
- uniqueIndex2("junior_identities_provider_subject_uidx").on(
87
+ uniqueIndex2("junior_users_primary_email_normalized_uidx").on(
88
+ table.primaryEmailNormalized
89
+ )
90
+ ]
91
+ );
92
+
93
+ // src/chat/conversations/sql/schema/identities.ts
94
+ var juniorIdentities = pgTable3(
95
+ "junior_identities",
96
+ {
97
+ id: text3("id").primaryKey(),
98
+ kind: text3("kind").$type().notNull(),
99
+ provider: text3("provider").notNull(),
100
+ providerTenantId: text3("provider_tenant_id").notNull().default(""),
101
+ providerSubjectId: text3("provider_subject_id").notNull(),
102
+ displayName: text3("display_name"),
103
+ handle: text3("handle"),
104
+ email: text3("email"),
105
+ avatarUrl: text3("avatar_url"),
106
+ metadata: jsonb2("metadata_json"),
107
+ createdAt: timestamptz("created_at").notNull(),
108
+ updatedAt: timestamptz("updated_at").notNull(),
109
+ userId: text3("user_id").references(() => juniorUsers.id),
110
+ emailNormalized: text3("email_normalized"),
111
+ emailVerified: boolean("email_verified").notNull().default(false)
112
+ },
113
+ (table) => [
114
+ uniqueIndex3("junior_identities_provider_subject_uidx").on(
80
115
  table.provider,
81
116
  table.providerTenantId,
82
117
  table.providerSubjectId
83
118
  ),
119
+ index2("junior_identities_user_idx").on(table.userId),
120
+ index2("junior_identities_verified_email_idx").on(table.emailNormalized).where(
121
+ sql`${table.emailVerified} = true AND ${table.emailNormalized} IS NOT NULL`
122
+ ),
84
123
  index2("junior_identities_kind_provider_idx").on(table.kind, table.provider)
85
124
  ]
86
125
  );
87
126
 
88
127
  // src/chat/conversations/sql/schema/conversations.ts
89
- var juniorConversations = pgTable3(
128
+ var juniorConversations = pgTable4(
90
129
  "junior_conversations",
91
130
  {
92
- conversationId: text3("conversation_id").primaryKey(),
131
+ conversationId: text4("conversation_id").primaryKey(),
93
132
  schemaVersion: integer("schema_version").notNull().default(1),
94
- source: text3("source").$type(),
95
- originType: text3("origin_type"),
96
- originId: text3("origin_id"),
97
- originRunId: text3("origin_run_id"),
98
- destinationId: text3("destination_id").references(
133
+ source: text4("source").$type(),
134
+ originType: text4("origin_type"),
135
+ originId: text4("origin_id"),
136
+ originRunId: text4("origin_run_id"),
137
+ destinationId: text4("destination_id").references(
99
138
  () => juniorDestinations.id
100
139
  ),
101
140
  destination: jsonb3("destination_json").$type(),
102
- actorIdentityId: text3("actor_identity_id").references(
141
+ actorIdentityId: text4("actor_identity_id").references(
103
142
  () => juniorIdentities.id
104
143
  ),
105
- requesterIdentityId: text3("requester_identity_id").references(
144
+ requesterIdentityId: text4("requester_identity_id").references(
106
145
  () => juniorIdentities.id
107
146
  ),
108
- creatorIdentityId: text3("creator_identity_id").references(
147
+ creatorIdentityId: text4("creator_identity_id").references(
109
148
  () => juniorIdentities.id
110
149
  ),
111
- credentialSubjectIdentityId: text3(
150
+ credentialSubjectIdentityId: text4(
112
151
  "credential_subject_identity_id"
113
152
  ).references(() => juniorIdentities.id),
114
153
  requester: jsonb3("requester_json").$type(),
115
- channelName: text3("channel_name"),
116
- title: text3("title"),
154
+ channelName: text4("channel_name"),
155
+ title: text4("title"),
117
156
  createdAt: timestamptz("created_at").notNull(),
118
157
  lastActivityAt: timestamptz("last_activity_at").notNull(),
119
158
  updatedAt: timestamptz("updated_at").notNull(),
120
159
  executionUpdatedAt: timestamptz("execution_updated_at"),
121
- executionStatus: text3("execution_status").$type().notNull(),
122
- runId: text3("run_id"),
160
+ executionStatus: text4("execution_status").$type().notNull(),
161
+ runId: text4("run_id"),
123
162
  lastCheckpointAt: timestamptz("last_checkpoint_at"),
124
163
  lastEnqueuedAt: timestamptz("last_enqueued_at")
125
164
  },
@@ -130,9 +169,9 @@ var juniorConversations = pgTable3(
130
169
  ),
131
170
  index3("junior_conversations_active_idx").using(
132
171
  "btree",
133
- sql`coalesce(${table.executionUpdatedAt}, ${table.updatedAt})`,
172
+ sql2`coalesce(${table.executionUpdatedAt}, ${table.updatedAt})`,
134
173
  table.conversationId
135
- ).where(sql`${table.executionStatus} <> 'idle'`),
174
+ ).where(sql2`${table.executionStatus} <> 'idle'`),
136
175
  index3("junior_conversations_destination_activity_idx").on(
137
176
  table.destinationId,
138
177
  table.lastActivityAt.desc()
@@ -154,10 +193,10 @@ var juniorConversations = pgTable3(
154
193
  );
155
194
 
156
195
  // src/chat/conversations/sql/schema/migrations.ts
157
- import { pgTable as pgTable4, text as text4 } from "drizzle-orm/pg-core";
158
- var juniorSchemaMigrations = pgTable4("junior_schema_migrations", {
159
- id: text4("id").primaryKey(),
160
- checksum: text4("checksum").notNull(),
196
+ import { pgTable as pgTable5, text as text5 } from "drizzle-orm/pg-core";
197
+ var juniorSchemaMigrations = pgTable5("junior_schema_migrations", {
198
+ id: text5("id").primaryKey(),
199
+ checksum: text5("checksum").notNull(),
161
200
  appliedAt: timestamptz("applied_at").notNull().defaultNow()
162
201
  });
163
202
 
@@ -166,10 +205,123 @@ var schema = {
166
205
  juniorConversations,
167
206
  juniorDestinations,
168
207
  juniorIdentities,
169
- juniorSchemaMigrations
208
+ juniorSchemaMigrations,
209
+ juniorUsers
170
210
  };
171
211
 
212
+ // src/chat/sql/schema.ts
213
+ var juniorSqlSchema = {
214
+ ...schema
215
+ };
216
+
217
+ // src/chat/identities/sql.ts
218
+ function dateFromMs(ms) {
219
+ return new Date(ms);
220
+ }
221
+ function tenantId(value) {
222
+ return value ?? "";
223
+ }
224
+ async function upsertUser(executor, args) {
225
+ const rows = await executor.db().insert(juniorUsers).values({
226
+ id: randomUUID(),
227
+ primaryEmail: args.email,
228
+ primaryEmailNormalized: args.emailNormalized,
229
+ displayName: args.displayName ?? null,
230
+ createdAt: dateFromMs(args.nowMs),
231
+ updatedAt: dateFromMs(args.nowMs)
232
+ }).onConflictDoUpdate({
233
+ target: juniorUsers.primaryEmailNormalized,
234
+ set: {
235
+ displayName: sql3`coalesce(${juniorUsers.displayName}, excluded.display_name)`,
236
+ updatedAt: sql3`excluded.updated_at`
237
+ }
238
+ }).returning({ id: juniorUsers.id });
239
+ const id = rows[0]?.id;
240
+ if (!id) {
241
+ throw new Error("User identity upsert returned no row");
242
+ }
243
+ return id;
244
+ }
245
+ async function existingIdentity(executor, identity) {
246
+ const rows = await executor.db().select().from(juniorIdentities).where(
247
+ and(
248
+ eq(juniorIdentities.provider, identity.provider),
249
+ eq(
250
+ juniorIdentities.providerTenantId,
251
+ tenantId(identity.providerTenantId)
252
+ ),
253
+ eq(juniorIdentities.providerSubjectId, identity.providerSubjectId)
254
+ )
255
+ );
256
+ return rows[0];
257
+ }
258
+ async function upsertIdentity(executor, identity, nowMs = Date.now()) {
259
+ const emailNormalized = normalizeIdentityEmail(identity.email);
260
+ const email = emailNormalized ? identity.email?.trim() || emailNormalized : void 0;
261
+ const existing = await existingIdentity(executor, identity);
262
+ const userEmailNormalized = existing?.emailVerified && existing.emailNormalized ? existing.emailNormalized : identity.emailVerified ? emailNormalized : void 0;
263
+ const userEmail = existing?.emailVerified && existing.email ? existing.email : email ?? userEmailNormalized;
264
+ const verifiedUserId = identity.kind === "user" && userEmailNormalized ? await upsertUser(executor, {
265
+ email: userEmail ?? userEmailNormalized,
266
+ emailNormalized: userEmailNormalized,
267
+ nowMs,
268
+ ...existing?.displayName || identity.displayName ? { displayName: existing?.displayName ?? identity.displayName } : {}
269
+ }) : void 0;
270
+ if (existing?.userId && verifiedUserId && existing.userId !== verifiedUserId) {
271
+ throw new Error("Identity verified email conflicts with linked user");
272
+ }
273
+ const userId = existing?.userId ?? verifiedUserId;
274
+ const rows = await executor.db().insert(juniorIdentities).values({
275
+ id: randomUUID(),
276
+ userId: userId ?? null,
277
+ kind: identity.kind,
278
+ provider: identity.provider,
279
+ providerTenantId: tenantId(identity.providerTenantId),
280
+ providerSubjectId: identity.providerSubjectId,
281
+ displayName: identity.displayName ?? null,
282
+ handle: identity.handle ?? null,
283
+ email: email ?? null,
284
+ emailNormalized: emailNormalized ?? null,
285
+ emailVerified: Boolean(identity.emailVerified && emailNormalized),
286
+ avatarUrl: null,
287
+ metadata: identity.metadata ?? null,
288
+ createdAt: dateFromMs(nowMs),
289
+ updatedAt: dateFromMs(nowMs)
290
+ }).onConflictDoUpdate({
291
+ target: [
292
+ juniorIdentities.provider,
293
+ juniorIdentities.providerTenantId,
294
+ juniorIdentities.providerSubjectId
295
+ ],
296
+ set: {
297
+ kind: sql3`excluded.kind`,
298
+ userId: sql3`coalesce(${juniorIdentities.userId}, excluded.user_id)`,
299
+ displayName: sql3`coalesce(${juniorIdentities.displayName}, excluded.display_name)`,
300
+ handle: sql3`coalesce(${juniorIdentities.handle}, excluded.handle)`,
301
+ email: sql3`case when ${juniorIdentities.emailVerified} then coalesce(${juniorIdentities.email}, excluded.email) when excluded.email_verified then excluded.email else coalesce(${juniorIdentities.email}, excluded.email) end`,
302
+ emailNormalized: sql3`case when ${juniorIdentities.emailVerified} then coalesce(${juniorIdentities.emailNormalized}, excluded.email_normalized) when excluded.email_verified then excluded.email_normalized else coalesce(${juniorIdentities.emailNormalized}, excluded.email_normalized) end`,
303
+ emailVerified: sql3`${juniorIdentities.emailVerified} OR excluded.email_verified`,
304
+ avatarUrl: sql3`coalesce(${juniorIdentities.avatarUrl}, excluded.avatar_url)`,
305
+ metadata: sql3`coalesce(${juniorIdentities.metadata}, excluded.metadata_json)`,
306
+ updatedAt: sql3`excluded.updated_at`
307
+ }
308
+ }).returning({
309
+ id: juniorIdentities.id,
310
+ userId: juniorIdentities.userId
311
+ });
312
+ const row = rows[0];
313
+ if (!row) {
314
+ throw new Error("Identity upsert returned no row");
315
+ }
316
+ return {
317
+ id: row.id,
318
+ ...row.userId ? { userId: row.userId } : {}
319
+ };
320
+ }
321
+
172
322
  // src/chat/conversations/sql/migrations.ts
323
+ import { createHash } from "crypto";
324
+ import { z } from "zod";
173
325
  var MIGRATION_LOCK_NAME = "junior_conversation_schema";
174
326
  var migrationRecordSchema = z.object({
175
327
  id: z.string().min(1),
@@ -306,12 +458,103 @@ UPDATE junior_destinations
306
458
  AND visibility = 'public'
307
459
  `
308
460
  ];
461
+ var userIdentityStatements = [
462
+ `
463
+ CREATE TABLE IF NOT EXISTS junior_users (
464
+ id TEXT PRIMARY KEY,
465
+ primary_email TEXT NOT NULL,
466
+ primary_email_normalized TEXT NOT NULL,
467
+ display_name TEXT,
468
+ created_at TIMESTAMPTZ NOT NULL,
469
+ updated_at TIMESTAMPTZ NOT NULL
470
+ )
471
+ `,
472
+ `
473
+ CREATE UNIQUE INDEX IF NOT EXISTS junior_users_primary_email_normalized_uidx
474
+ ON junior_users (primary_email_normalized)
475
+ `,
476
+ `
477
+ ALTER TABLE junior_identities
478
+ ADD COLUMN IF NOT EXISTS user_id TEXT REFERENCES junior_users (id)
479
+ `,
480
+ `
481
+ ALTER TABLE junior_identities
482
+ ADD COLUMN IF NOT EXISTS email_normalized TEXT
483
+ `,
484
+ `
485
+ ALTER TABLE junior_identities
486
+ ADD COLUMN IF NOT EXISTS email_verified BOOLEAN NOT NULL DEFAULT false
487
+ `,
488
+ `
489
+ UPDATE junior_identities
490
+ SET email_normalized = lower(trim(email)),
491
+ email_verified = CASE
492
+ WHEN kind = 'user' AND provider = 'slack' THEN true
493
+ ELSE email_verified
494
+ END
495
+ WHERE email IS NOT NULL
496
+ AND trim(email) <> ''
497
+ `,
498
+ `
499
+ WITH first_identity AS (
500
+ SELECT DISTINCT ON (email_normalized)
501
+ id,
502
+ COALESCE(NULLIF(trim(email), ''), email_normalized) AS email,
503
+ email_normalized,
504
+ display_name,
505
+ created_at,
506
+ updated_at
507
+ FROM junior_identities
508
+ WHERE kind = 'user'
509
+ AND email_verified = true
510
+ AND email_normalized IS NOT NULL
511
+ ORDER BY email_normalized, created_at ASC, id ASC
512
+ )
513
+ INSERT INTO junior_users (
514
+ id,
515
+ primary_email,
516
+ primary_email_normalized,
517
+ display_name,
518
+ created_at,
519
+ updated_at
520
+ )
521
+ SELECT
522
+ 'identity:' || id,
523
+ email,
524
+ email_normalized,
525
+ display_name,
526
+ created_at,
527
+ updated_at
528
+ FROM first_identity
529
+ ON CONFLICT (primary_email_normalized) DO NOTHING
530
+ `,
531
+ `
532
+ UPDATE junior_identities AS identity
533
+ SET user_id = junior_users.id
534
+ FROM junior_users
535
+ WHERE identity.kind = 'user'
536
+ AND identity.user_id IS NULL
537
+ AND identity.email_verified = true
538
+ AND identity.email_normalized = junior_users.primary_email_normalized
539
+ `,
540
+ `
541
+ CREATE INDEX IF NOT EXISTS junior_identities_user_idx
542
+ ON junior_identities (user_id)
543
+ `,
544
+ `
545
+ CREATE INDEX IF NOT EXISTS junior_identities_verified_email_idx
546
+ ON junior_identities (email_normalized)
547
+ WHERE email_verified = true
548
+ AND email_normalized IS NOT NULL
549
+ `
550
+ ];
309
551
  var migrations = [
310
552
  defineMigration("0001_conversation_core", coreMetadataStatements),
311
553
  defineMigration(
312
554
  "0002_slack_destination_visibility_backfill",
313
555
  destinationVisibilityBackfillStatements
314
- )
556
+ ),
557
+ defineMigration("0003_user_identities", userIdentityStatements)
315
558
  ];
316
559
  function parseStoredMigrationRecord(value) {
317
560
  return migrationRecordSchema.parse(value);
@@ -362,12 +605,9 @@ var CONVERSATION_MUTATION_LOCK_PREFIX = "junior_conversation";
362
605
  function now() {
363
606
  return Date.now();
364
607
  }
365
- function dateFromMs(ms) {
608
+ function dateFromMs2(ms) {
366
609
  return new Date(ms);
367
610
  }
368
- function tenantId(value) {
369
- return value ?? "";
370
- }
371
611
  function msFromDate(value) {
372
612
  if (value === null || value === void 0) {
373
613
  return void 0;
@@ -382,6 +622,9 @@ function requiredMsFromDate(value) {
382
622
  }
383
623
  return ms;
384
624
  }
625
+ function tenantId2(value) {
626
+ return value ?? "";
627
+ }
385
628
  function sourceFromValue(value) {
386
629
  if (value === "api" || value === "internal" || value === "local" || value === "plugin" || value === "resource_event" || value === "scheduler" || value === "slack") {
387
630
  return value;
@@ -399,7 +642,7 @@ function identityFromRequester(requester) {
399
642
  providerSubjectId: requester.slackUserId,
400
643
  ...requester.fullName ? { displayName: requester.fullName } : {},
401
644
  ...requester.slackUserName ? { handle: requester.slackUserName } : {},
402
- ...requester.email ? { email: requester.email } : {},
645
+ ...requester.email ? { email: requester.email, emailVerified: true } : {},
403
646
  metadata: { platform: "slack" }
404
647
  };
405
648
  }
@@ -481,6 +724,20 @@ function privacyFromRow(row) {
481
724
  }
482
725
  return row.destinationVisibility === "public" ? "public" : "private";
483
726
  }
727
+ function requesterFromIdentityRow(identity, fallback) {
728
+ if (!identity || identity.provider !== "slack") {
729
+ return fallback;
730
+ }
731
+ return {
732
+ ...fallback ?? {},
733
+ ...identity.emailNormalized ? { email: identity.emailNormalized } : identity.email ? { email: identity.email } : {},
734
+ ...identity.displayName ? { fullName: identity.displayName } : {},
735
+ platform: "slack",
736
+ slackUserId: identity.providerSubjectId,
737
+ ...identity.handle ? { slackUserName: identity.handle } : {},
738
+ ...identity.providerTenantId || fallback?.teamId ? { teamId: identity.providerTenantId || fallback?.teamId } : {}
739
+ };
740
+ }
484
741
  function conversationFromRow(readRow) {
485
742
  const row = readRow.conversation;
486
743
  const visibility = privacyFromRow(readRow);
@@ -488,7 +745,10 @@ function conversationFromRow(readRow) {
488
745
  throw new Error("Conversation record schema version is invalid");
489
746
  }
490
747
  const destination = row.destination === void 0 || row.destination === null ? void 0 : parseDestination(row.destination);
491
- const requester = parseStoredSlackRequester(row.requester);
748
+ const requester = requesterFromIdentityRow(
749
+ readRow.requesterIdentity,
750
+ parseStoredSlackRequester(row.requester)
751
+ );
492
752
  if (row.destination !== void 0 && row.destination !== null && !destination) {
493
753
  throw new Error("Conversation record destination is invalid");
494
754
  }
@@ -544,6 +804,26 @@ function assertSameConversationDestination(args) {
544
804
  `Conversation destination changed for ${args.conversationId}`
545
805
  );
546
806
  }
807
+ function mergeRequester(current2, next) {
808
+ if (!current2) {
809
+ return next;
810
+ }
811
+ if (!next) {
812
+ return current2;
813
+ }
814
+ if (current2.slackUserId && next.slackUserId && current2.slackUserId !== next.slackUserId) {
815
+ return current2;
816
+ }
817
+ return {
818
+ ...current2,
819
+ ...current2.email ?? next.email ? { email: current2.email ?? next.email } : {},
820
+ ...current2.fullName ?? next.fullName ? { fullName: current2.fullName ?? next.fullName } : {},
821
+ ...current2.platform ?? next.platform ? { platform: current2.platform ?? next.platform } : {},
822
+ ...current2.slackUserId ?? next.slackUserId ? { slackUserId: current2.slackUserId ?? next.slackUserId } : {},
823
+ ...current2.slackUserName ?? next.slackUserName ? { slackUserName: current2.slackUserName ?? next.slackUserName } : {},
824
+ ...current2.teamId ?? next.teamId ? { teamId: current2.teamId ?? next.teamId } : {}
825
+ };
826
+ }
547
827
  var SqlStore = class {
548
828
  constructor(executor, migrationExecutor) {
549
829
  this.executor = executor;
@@ -601,7 +881,7 @@ var SqlStore = class {
601
881
  destination: current2.destination ?? args.destination,
602
882
  source: current2.source ?? args.source,
603
883
  channelName: current2.channelName ?? args.channelName,
604
- requester: current2.requester ?? args.requester,
884
+ requester: mergeRequester(current2.requester, args.requester),
605
885
  title: current2.title ?? args.title,
606
886
  lastActivityAtMs: Math.max(current2.lastActivityAtMs, activityAtMs),
607
887
  updatedAtMs: nowMs,
@@ -674,10 +954,14 @@ var SqlStore = class {
674
954
  async listByActivity(args = {}) {
675
955
  const rows = await this.executor.db().select({
676
956
  conversation: juniorConversations,
677
- destinationVisibility: juniorDestinations.visibility
957
+ destinationVisibility: juniorDestinations.visibility,
958
+ requesterIdentity: juniorIdentities
678
959
  }).from(juniorConversations).leftJoin(
679
960
  juniorDestinations,
680
- eq(juniorDestinations.id, juniorConversations.destinationId)
961
+ eq2(juniorDestinations.id, juniorConversations.destinationId)
962
+ ).leftJoin(
963
+ juniorIdentities,
964
+ eq2(juniorIdentities.id, juniorConversations.requesterIdentityId)
681
965
  ).orderBy(
682
966
  desc(juniorConversations.lastActivityAt),
683
967
  asc(juniorConversations.conversationId)
@@ -692,13 +976,13 @@ var SqlStore = class {
692
976
  const rows = await this.executor.db().select({
693
977
  visibility: juniorDestinations.visibility
694
978
  }).from(juniorDestinations).where(
695
- and(
696
- eq(juniorDestinations.provider, args.provider),
697
- eq(
979
+ and2(
980
+ eq2(juniorDestinations.provider, args.provider),
981
+ eq2(
698
982
  juniorDestinations.providerTenantId,
699
- tenantId(args.providerTenantId)
983
+ tenantId2(args.providerTenantId)
700
984
  ),
701
- eq(
985
+ eq2(
702
986
  juniorDestinations.providerDestinationId,
703
987
  args.providerDestinationId
704
988
  )
@@ -720,19 +1004,23 @@ var SqlStore = class {
720
1004
  async readConversationRow(conversationId) {
721
1005
  const rows = await this.executor.db().select({
722
1006
  conversation: juniorConversations,
723
- destinationVisibility: juniorDestinations.visibility
1007
+ destinationVisibility: juniorDestinations.visibility,
1008
+ requesterIdentity: juniorIdentities
724
1009
  }).from(juniorConversations).leftJoin(
725
1010
  juniorDestinations,
726
- eq(juniorDestinations.id, juniorConversations.destinationId)
727
- ).where(eq(juniorConversations.conversationId, conversationId));
1011
+ eq2(juniorDestinations.id, juniorConversations.destinationId)
1012
+ ).leftJoin(
1013
+ juniorIdentities,
1014
+ eq2(juniorIdentities.id, juniorConversations.requesterIdentityId)
1015
+ ).where(eq2(juniorConversations.conversationId, conversationId));
728
1016
  return rows[0];
729
1017
  }
730
1018
  /** Upsert the conversation row while preserving previously discovered nullable metadata fields. */
731
1019
  async upsertConversation(args) {
732
1020
  const { conversation } = args;
733
- const incomingExecutionVersion = sql2`coalesce(excluded.execution_updated_at, excluded.updated_at)`;
734
- const currentExecutionVersion = sql2`coalesce(${juniorConversations.executionUpdatedAt}, ${juniorConversations.updatedAt})`;
735
- const incomingExecutionIsFresh = sql2`${incomingExecutionVersion} >= ${currentExecutionVersion}`;
1021
+ const incomingExecutionVersion = sql4`coalesce(excluded.execution_updated_at, excluded.updated_at)`;
1022
+ const currentExecutionVersion = sql4`coalesce(${juniorConversations.executionUpdatedAt}, ${juniorConversations.updatedAt})`;
1023
+ const incomingExecutionIsFresh = sql4`${incomingExecutionVersion} >= ${currentExecutionVersion}`;
736
1024
  const destinationId = await this.upsertDestination(
737
1025
  destinationUpsertFromDestination({
738
1026
  channelName: conversation.channelName,
@@ -742,14 +1030,20 @@ var SqlStore = class {
742
1030
  }),
743
1031
  conversation.updatedAtMs
744
1032
  );
745
- const requesterIdentityId = await this.upsertIdentity(
746
- identityFromRequester(conversation.requester),
747
- conversation.updatedAtMs
1033
+ const requesterIdentityObservation = identityFromRequester(
1034
+ conversation.requester
748
1035
  );
749
- const actorIdentityId = await this.upsertIdentity(
750
- actorIdentityForConversation(conversation),
1036
+ const requesterIdentity = requesterIdentityObservation ? await upsertIdentity(
1037
+ this.executor,
1038
+ requesterIdentityObservation,
751
1039
  conversation.updatedAtMs
752
- );
1040
+ ) : void 0;
1041
+ const actorIdentityObservation = actorIdentityForConversation(conversation);
1042
+ const actorIdentity = actorIdentityObservation ? await upsertIdentity(
1043
+ this.executor,
1044
+ actorIdentityObservation,
1045
+ conversation.updatedAtMs
1046
+ ) : void 0;
753
1047
  await this.executor.db().insert(juniorConversations).values({
754
1048
  conversationId: conversation.conversationId,
755
1049
  schemaVersion: 1,
@@ -759,100 +1053,65 @@ var SqlStore = class {
759
1053
  originRunId: null,
760
1054
  destinationId: destinationId ?? null,
761
1055
  destination: conversation.destination ?? null,
762
- actorIdentityId: actorIdentityId ?? null,
763
- requesterIdentityId: requesterIdentityId ?? null,
1056
+ actorIdentityId: actorIdentity?.id ?? null,
1057
+ requesterIdentityId: requesterIdentity?.id ?? null,
764
1058
  creatorIdentityId: null,
765
1059
  credentialSubjectIdentityId: null,
766
1060
  requester: conversation.requester ?? null,
767
1061
  channelName: conversation.channelName ?? null,
768
1062
  title: conversation.title ?? null,
769
- createdAt: dateFromMs(conversation.createdAtMs),
770
- lastActivityAt: dateFromMs(conversation.lastActivityAtMs),
771
- updatedAt: dateFromMs(conversation.updatedAtMs),
772
- executionUpdatedAt: conversation.execution.updatedAtMs === void 0 ? null : dateFromMs(conversation.execution.updatedAtMs),
1063
+ createdAt: dateFromMs2(conversation.createdAtMs),
1064
+ lastActivityAt: dateFromMs2(conversation.lastActivityAtMs),
1065
+ updatedAt: dateFromMs2(conversation.updatedAtMs),
1066
+ executionUpdatedAt: conversation.execution.updatedAtMs === void 0 ? null : dateFromMs2(conversation.execution.updatedAtMs),
773
1067
  executionStatus: conversation.execution.status,
774
1068
  runId: conversation.execution.runId ?? null,
775
- lastCheckpointAt: conversation.execution.lastCheckpointAtMs === void 0 ? null : dateFromMs(conversation.execution.lastCheckpointAtMs),
776
- lastEnqueuedAt: conversation.execution.lastEnqueuedAtMs === void 0 ? null : dateFromMs(conversation.execution.lastEnqueuedAtMs)
1069
+ lastCheckpointAt: conversation.execution.lastCheckpointAtMs === void 0 ? null : dateFromMs2(conversation.execution.lastCheckpointAtMs),
1070
+ lastEnqueuedAt: conversation.execution.lastEnqueuedAtMs === void 0 ? null : dateFromMs2(conversation.execution.lastEnqueuedAtMs)
777
1071
  }).onConflictDoUpdate({
778
1072
  target: juniorConversations.conversationId,
779
1073
  set: {
780
- source: sql2`coalesce(excluded.source, ${juniorConversations.source})`,
781
- originType: sql2`coalesce(excluded.origin_type, ${juniorConversations.originType})`,
782
- originId: sql2`coalesce(excluded.origin_id, ${juniorConversations.originId})`,
783
- originRunId: sql2`coalesce(excluded.origin_run_id, ${juniorConversations.originRunId})`,
784
- destinationId: sql2`coalesce(excluded.destination_id, ${juniorConversations.destinationId})`,
785
- destination: sql2`coalesce(excluded.destination_json, ${juniorConversations.destination})`,
786
- actorIdentityId: sql2`coalesce(excluded.actor_identity_id, ${juniorConversations.actorIdentityId})`,
787
- requesterIdentityId: sql2`coalesce(excluded.requester_identity_id, ${juniorConversations.requesterIdentityId})`,
788
- creatorIdentityId: sql2`coalesce(excluded.creator_identity_id, ${juniorConversations.creatorIdentityId})`,
789
- credentialSubjectIdentityId: sql2`coalesce(excluded.credential_subject_identity_id, ${juniorConversations.credentialSubjectIdentityId})`,
790
- requester: sql2`coalesce(excluded.requester_json, ${juniorConversations.requester})`,
791
- channelName: sql2`coalesce(excluded.channel_name, ${juniorConversations.channelName})`,
792
- title: sql2`coalesce(excluded.title, ${juniorConversations.title})`,
793
- createdAt: sql2`least(${juniorConversations.createdAt}, excluded.created_at)`,
794
- lastActivityAt: sql2`greatest(${juniorConversations.lastActivityAt}, excluded.last_activity_at)`,
795
- updatedAt: sql2`greatest(${juniorConversations.updatedAt}, excluded.updated_at)`,
796
- executionUpdatedAt: sql2`case when ${incomingExecutionIsFresh} then excluded.execution_updated_at else ${juniorConversations.executionUpdatedAt} end`,
797
- executionStatus: sql2`case when ${incomingExecutionIsFresh} then excluded.execution_status else ${juniorConversations.executionStatus} end`,
798
- runId: sql2`case when ${incomingExecutionIsFresh} then excluded.run_id else ${juniorConversations.runId} end`,
799
- lastCheckpointAt: sql2`case when ${incomingExecutionIsFresh} then coalesce(excluded.last_checkpoint_at, ${juniorConversations.lastCheckpointAt}) else ${juniorConversations.lastCheckpointAt} end`,
800
- lastEnqueuedAt: sql2`case when ${incomingExecutionIsFresh} then coalesce(excluded.last_enqueued_at, ${juniorConversations.lastEnqueuedAt}) else ${juniorConversations.lastEnqueuedAt} end`
1074
+ source: sql4`coalesce(excluded.source, ${juniorConversations.source})`,
1075
+ originType: sql4`coalesce(excluded.origin_type, ${juniorConversations.originType})`,
1076
+ originId: sql4`coalesce(excluded.origin_id, ${juniorConversations.originId})`,
1077
+ originRunId: sql4`coalesce(excluded.origin_run_id, ${juniorConversations.originRunId})`,
1078
+ destinationId: sql4`coalesce(excluded.destination_id, ${juniorConversations.destinationId})`,
1079
+ destination: sql4`coalesce(excluded.destination_json, ${juniorConversations.destination})`,
1080
+ actorIdentityId: sql4`coalesce(excluded.actor_identity_id, ${juniorConversations.actorIdentityId})`,
1081
+ requesterIdentityId: sql4`coalesce(excluded.requester_identity_id, ${juniorConversations.requesterIdentityId})`,
1082
+ creatorIdentityId: sql4`coalesce(excluded.creator_identity_id, ${juniorConversations.creatorIdentityId})`,
1083
+ credentialSubjectIdentityId: sql4`coalesce(excluded.credential_subject_identity_id, ${juniorConversations.credentialSubjectIdentityId})`,
1084
+ requester: sql4`coalesce(excluded.requester_json, ${juniorConversations.requester})`,
1085
+ channelName: sql4`coalesce(excluded.channel_name, ${juniorConversations.channelName})`,
1086
+ title: sql4`coalesce(excluded.title, ${juniorConversations.title})`,
1087
+ createdAt: sql4`least(${juniorConversations.createdAt}, excluded.created_at)`,
1088
+ lastActivityAt: sql4`greatest(${juniorConversations.lastActivityAt}, excluded.last_activity_at)`,
1089
+ updatedAt: sql4`greatest(${juniorConversations.updatedAt}, excluded.updated_at)`,
1090
+ executionUpdatedAt: sql4`case when ${incomingExecutionIsFresh} then excluded.execution_updated_at else ${juniorConversations.executionUpdatedAt} end`,
1091
+ executionStatus: sql4`case when ${incomingExecutionIsFresh} then excluded.execution_status else ${juniorConversations.executionStatus} end`,
1092
+ runId: sql4`case when ${incomingExecutionIsFresh} then excluded.run_id else ${juniorConversations.runId} end`,
1093
+ lastCheckpointAt: sql4`case when ${incomingExecutionIsFresh} then coalesce(excluded.last_checkpoint_at, ${juniorConversations.lastCheckpointAt}) else ${juniorConversations.lastCheckpointAt} end`,
1094
+ lastEnqueuedAt: sql4`case when ${incomingExecutionIsFresh} then coalesce(excluded.last_enqueued_at, ${juniorConversations.lastEnqueuedAt}) else ${juniorConversations.lastEnqueuedAt} end`
801
1095
  }
802
1096
  });
803
1097
  }
804
- async upsertIdentity(identity, nowMs) {
805
- if (!identity) {
806
- return void 0;
807
- }
808
- const rows = await this.executor.db().insert(juniorIdentities).values({
809
- id: randomUUID(),
810
- kind: identity.kind,
811
- provider: identity.provider,
812
- providerTenantId: tenantId(identity.providerTenantId),
813
- providerSubjectId: identity.providerSubjectId,
814
- displayName: identity.displayName ?? null,
815
- handle: identity.handle ?? null,
816
- email: identity.email ?? null,
817
- avatarUrl: null,
818
- metadata: identity.metadata ?? null,
819
- createdAt: dateFromMs(nowMs),
820
- updatedAt: dateFromMs(nowMs)
821
- }).onConflictDoUpdate({
822
- target: [
823
- juniorIdentities.provider,
824
- juniorIdentities.providerTenantId,
825
- juniorIdentities.providerSubjectId
826
- ],
827
- set: {
828
- kind: sql2`excluded.kind`,
829
- displayName: sql2`coalesce(excluded.display_name, ${juniorIdentities.displayName})`,
830
- handle: sql2`coalesce(excluded.handle, ${juniorIdentities.handle})`,
831
- email: sql2`coalesce(excluded.email, ${juniorIdentities.email})`,
832
- avatarUrl: sql2`coalesce(excluded.avatar_url, ${juniorIdentities.avatarUrl})`,
833
- metadata: sql2`coalesce(excluded.metadata_json, ${juniorIdentities.metadata})`,
834
- updatedAt: sql2`excluded.updated_at`
835
- }
836
- }).returning({ id: juniorIdentities.id });
837
- return rows[0]?.id;
838
- }
839
1098
  async upsertDestination(destination, nowMs) {
840
1099
  if (!destination) {
841
1100
  return void 0;
842
1101
  }
843
- const visibilityUpdate = destination.refreshVisibility ? sql2`excluded.visibility` : juniorDestinations.visibility;
1102
+ const visibilityUpdate = destination.refreshVisibility ? sql4`excluded.visibility` : juniorDestinations.visibility;
844
1103
  const rows = await this.executor.db().insert(juniorDestinations).values({
845
- id: randomUUID(),
1104
+ id: randomUUID2(),
846
1105
  provider: destination.provider,
847
- providerTenantId: tenantId(destination.providerTenantId),
1106
+ providerTenantId: tenantId2(destination.providerTenantId),
848
1107
  providerDestinationId: destination.providerDestinationId,
849
1108
  kind: destination.kind,
850
1109
  parentDestinationId: null,
851
1110
  displayName: destination.displayName ?? null,
852
1111
  visibility: destination.visibility,
853
1112
  metadata: destination.metadata ?? null,
854
- createdAt: dateFromMs(nowMs),
855
- updatedAt: dateFromMs(nowMs)
1113
+ createdAt: dateFromMs2(nowMs),
1114
+ updatedAt: dateFromMs2(nowMs)
856
1115
  }).onConflictDoUpdate({
857
1116
  target: [
858
1117
  juniorDestinations.provider,
@@ -860,14 +1119,14 @@ var SqlStore = class {
860
1119
  juniorDestinations.providerDestinationId
861
1120
  ],
862
1121
  set: {
863
- kind: sql2`excluded.kind`,
864
- displayName: sql2`coalesce(excluded.display_name, ${juniorDestinations.displayName})`,
1122
+ kind: sql4`excluded.kind`,
1123
+ displayName: sql4`coalesce(excluded.display_name, ${juniorDestinations.displayName})`,
865
1124
  // Signal-less writes insert as private but must not clobber an
866
1125
  // existing public/private value. Live source signals refresh this
867
1126
  // field so converted channels converge on the next message.
868
1127
  visibility: visibilityUpdate,
869
- metadata: sql2`coalesce(excluded.metadata_json, ${juniorDestinations.metadata})`,
870
- updatedAt: sql2`excluded.updated_at`
1128
+ metadata: sql4`coalesce(excluded.metadata_json, ${juniorDestinations.metadata})`,
1129
+ updatedAt: sql4`excluded.updated_at`
871
1130
  }
872
1131
  }).returning({ id: juniorDestinations.id });
873
1132
  return rows[0]?.id;
@@ -883,13 +1142,6 @@ import {
883
1142
  Pool
884
1143
  } from "@neondatabase/serverless";
885
1144
  import { drizzle } from "drizzle-orm/neon-serverless";
886
-
887
- // src/chat/sql/schema.ts
888
- var juniorSqlSchema = {
889
- ...schema
890
- };
891
-
892
- // src/chat/sql/neon.ts
893
1145
  var NeonExecutor = class {
894
1146
  constructor(pool) {
895
1147
  this.pool = pool;
@@ -1105,20 +1357,20 @@ function createDb(args) {
1105
1357
  });
1106
1358
  }
1107
1359
  function getSqlExecutor() {
1108
- const { sql: sql3 } = getChatConfig();
1109
- if (current?.databaseUrl !== sql3.databaseUrl || current.driver !== sql3.driver) {
1360
+ const { sql: sql5 } = getChatConfig();
1361
+ if (current?.databaseUrl !== sql5.databaseUrl || current.driver !== sql5.driver) {
1110
1362
  if (current) {
1111
1363
  const previous = current;
1112
1364
  current = void 0;
1113
1365
  void previous.db.close().catch(() => void 0);
1114
1366
  }
1115
1367
  const db = createDb({
1116
- databaseUrl: sql3.databaseUrl,
1117
- driver: sql3.driver
1368
+ databaseUrl: sql5.databaseUrl,
1369
+ driver: sql5.driver
1118
1370
  });
1119
1371
  current = {
1120
- databaseUrl: sql3.databaseUrl,
1121
- driver: sql3.driver,
1372
+ databaseUrl: sql5.databaseUrl,
1373
+ driver: sql5.driver,
1122
1374
  db,
1123
1375
  store: createSqlStore(db)
1124
1376
  };
@@ -1139,6 +1391,10 @@ async function closeDb() {
1139
1391
  }
1140
1392
 
1141
1393
  export {
1394
+ juniorDestinations,
1395
+ juniorUsers,
1396
+ juniorIdentities,
1397
+ juniorConversations,
1142
1398
  createSqlStore,
1143
1399
  createJuniorSqlExecutor,
1144
1400
  getDb,