@sentry/junior 0.97.1 → 0.98.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 (55) hide show
  1. package/dist/{agent-hooks-XSUIVRQS.js → agent-hooks-OSW6C3CJ.js} +2 -2
  2. package/dist/api/conversations/detail.d.ts +1 -1
  3. package/dist/api/conversations/detail.query.d.ts +1 -1
  4. package/dist/api/conversations/projection.d.ts +0 -1
  5. package/dist/api/conversations/schema.d.ts +31 -8
  6. package/dist/api/people/schema.d.ts +0 -11
  7. package/dist/api/people/shared.d.ts +2 -4
  8. package/dist/api/schema.d.ts +1 -1
  9. package/dist/api/schema.js +2 -2
  10. package/dist/api-reference.d.ts +1 -1
  11. package/dist/api.js +10 -10
  12. package/dist/app.js +39 -28
  13. package/dist/chat/conversations/search.d.ts +24 -0
  14. package/dist/chat/conversations/sql/search.d.ts +4 -0
  15. package/dist/chat/db.d.ts +3 -0
  16. package/dist/chat/runtime/report-progress.d.ts +3 -0
  17. package/dist/chat/runtime/slack-resume.d.ts +2 -0
  18. package/dist/chat/slack/assistant-thread/status-scheduler.d.ts +4 -3
  19. package/dist/chat/slack/outbound.d.ts +6 -0
  20. package/dist/chat/slack/tools/conversation-search.d.ts +71 -0
  21. package/dist/chat/state/turn-session.d.ts +2 -0
  22. package/dist/chat/tool-support/zod-tool.d.ts +1 -1
  23. package/dist/chunk-6DTVCPLO.js +146 -0
  24. package/dist/{chunk-AUUCAFDE.js → chunk-6HHEZENZ.js} +126 -11
  25. package/dist/{chunk-VFH4ZKZT.js → chunk-CRQVIHDI.js} +1 -3
  26. package/dist/{chunk-6U26PYI5.js → chunk-CV22LUIV.js} +7 -17
  27. package/dist/{chunk-GUAP3H7O.js → chunk-DHHU2WSG.js} +2 -2
  28. package/dist/{chunk-XIRPQQVE.js → chunk-DPHWDS6I.js} +297 -171
  29. package/dist/{chunk-T533JLM3.js → chunk-ER45IV77.js} +8 -6
  30. package/dist/{chunk-LUAE4MGW.js → chunk-EUZ44ROE.js} +21 -4
  31. package/dist/{chunk-5HLDPVYS.js → chunk-FTTFV6DC.js} +1 -1
  32. package/dist/{chunk-3F4OPKGF.js → chunk-IO65E4ZD.js} +2 -2
  33. package/dist/{chunk-ON6YKIQ7.js → chunk-KJHAW6VE.js} +4 -10
  34. package/dist/{chunk-FBHZJFHI.js → chunk-NJEDJFK4.js} +1 -1
  35. package/dist/{chunk-35BNLDOJ.js → chunk-OGHXZ4KQ.js} +134 -57
  36. package/dist/{chunk-WJOSZOON.js → chunk-OL6XQ23W.js} +11 -150
  37. package/dist/{chunk-WGXMTFNU.js → chunk-WJ7STXT6.js} +5 -3
  38. package/dist/cli/chat.js +11 -10
  39. package/dist/cli/plugins.js +2 -2
  40. package/dist/cli/upgrade.js +9 -7
  41. package/dist/{db-BWXYIKFQ.js → db-GFSCDAHS.js} +3 -1
  42. package/dist/detail-W4ZSD45K.js +67 -0
  43. package/dist/{legacy-import-3NQJVGQL.js → legacy-import-6WWXMALY.js} +2 -2
  44. package/dist/{list-UXJI5Q5X.js → list-3KZNG3SJ.js} +3 -3
  45. package/dist/{list-A3WFRIYX.js → list-M5BRWW3D.js} +5 -6
  46. package/dist/{plugin-conversations-DDNGC35A.js → plugin-conversations-ZYRWZYEA.js} +2 -2
  47. package/dist/{profile-4RYDXWTF.js → profile-UEIRSVT3.js} +5 -5
  48. package/dist/{runner-JALSYH2P.js → runner-267ZRLZD.js} +8 -7
  49. package/dist/{stats-XKXH3PTY.js → stats-L62E4O4U.js} +7 -17
  50. package/dist/{subagent-TZ55XY37.js → subagent-5HZPQC7V.js} +6 -6
  51. package/migrations/0002_conversation_message_search.sql +1 -0
  52. package/migrations/meta/0002_snapshot.json +987 -0
  53. package/migrations/meta/_journal.json +7 -0
  54. package/package.json +6 -6
  55. package/dist/detail-GA6NXWQJ.js +0 -43
@@ -9,11 +9,13 @@ import {
9
9
  getPersistedThreadState,
10
10
  markTurnCompleted,
11
11
  mergeArtifactsState
12
- } from "./chunk-WGXMTFNU.js";
12
+ } from "./chunk-WJ7STXT6.js";
13
+ import {
14
+ coerceThreadConversationState
15
+ } from "./chunk-6DTVCPLO.js";
13
16
  import {
14
- coerceThreadConversationState,
15
17
  getAgentTurnSessionRecord
16
- } from "./chunk-WJOSZOON.js";
18
+ } from "./chunk-OL6XQ23W.js";
17
19
  import {
18
20
  generateConversationId,
19
21
  hydrateConversationMessages,
@@ -21,19 +23,19 @@ import {
21
23
  normalizeConversationText,
22
24
  updateConversationStats,
23
25
  upsertConversationMessage
24
- } from "./chunk-3F4OPKGF.js";
26
+ } from "./chunk-IO65E4ZD.js";
25
27
  import {
26
28
  createPluginEmbedder,
27
29
  createPluginModel,
28
30
  getPlugins
29
- } from "./chunk-5HLDPVYS.js";
31
+ } from "./chunk-FTTFV6DC.js";
30
32
  import {
31
33
  createPluginLogger,
32
34
  createPluginState
33
35
  } from "./chunk-EOWXRY5E.js";
34
36
  import {
35
37
  getDb
36
- } from "./chunk-35BNLDOJ.js";
38
+ } from "./chunk-OGHXZ4KQ.js";
37
39
  import {
38
40
  GEN_AI_PROVIDER_NAME,
39
41
  botConfig,
@@ -7,8 +7,7 @@ import { z } from "zod";
7
7
  var conversationReportStatusSchema = z.enum([
8
8
  "active",
9
9
  "completed",
10
- "failed",
11
- "hung"
10
+ "failed"
12
11
  ]);
13
12
  var conversationSurfaceSchema = z.enum([
14
13
  "api",
@@ -119,8 +118,28 @@ var conversationActivityReportSchema = z.discriminatedUnion("type", [
119
118
  conversationToolActivityReportSchema,
120
119
  conversationSubagentActivityReportSchema
121
120
  ]);
121
+ var conversationContextEventSchema = z.discriminatedUnion("type", [
122
+ z.object({
123
+ type: z.literal("context_compacted"),
124
+ createdAt: z.string(),
125
+ modelId: z.string().optional(),
126
+ summary: z.string().optional(),
127
+ transcriptIndex: z.number().int().nonnegative()
128
+ }).strict(),
129
+ z.object({
130
+ type: z.literal("model_handoff"),
131
+ createdAt: z.string(),
132
+ fromModelId: z.string().optional(),
133
+ summary: z.string().optional(),
134
+ toModelId: z.string(),
135
+ transcriptIndex: z.number().int().nonnegative()
136
+ }).strict()
137
+ ]);
122
138
  var conversationDetailReportSchema = conversationSummaryReportSchema.extend({
123
139
  activity: z.array(conversationActivityReportSchema).optional(),
140
+ modelId: z.string().optional(),
141
+ reasoningLevel: z.string().optional(),
142
+ contextEvents: z.array(conversationContextEventSchema).optional(),
124
143
  transcriptAvailable: z.boolean(),
125
144
  transcriptMetadata: z.array(transcriptMessageSchema).optional(),
126
145
  transcriptMessageCount: z.number().optional(),
@@ -164,7 +183,6 @@ var conversationStatsItemSchema = z.object({
164
183
  conversations: z.number(),
165
184
  durationMs: z.number(),
166
185
  failed: z.number(),
167
- hung: z.number(),
168
186
  label: z.string(),
169
187
  costUsd: z.number().optional(),
170
188
  tokens: z.number().optional()
@@ -175,7 +193,6 @@ var conversationStatsReportSchema = z.object({
175
193
  durationMs: z.number(),
176
194
  failed: z.number(),
177
195
  generatedAt: z.string(),
178
- hung: z.number(),
179
196
  locations: z.array(conversationStatsItemSchema),
180
197
  actors: z.array(conversationStatsItemSchema),
181
198
  sampleLimit: z.number(),
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-EOWXRY5E.js";
11
11
  import {
12
12
  getDb
13
- } from "./chunk-35BNLDOJ.js";
13
+ } from "./chunk-OGHXZ4KQ.js";
14
14
  import {
15
15
  SANDBOX_WORKSPACE_ROOT
16
16
  } from "./chunk-G3E7SCME.js";
@@ -11,7 +11,7 @@ import {
11
11
  legacyActorProvenance,
12
12
  piMessageSchema,
13
13
  readSessionLogEntries
14
- } from "./chunk-35BNLDOJ.js";
14
+ } from "./chunk-OGHXZ4KQ.js";
15
15
  import {
16
16
  botConfig,
17
17
  escapeXml,
@@ -464,7 +464,7 @@ async function hydrateConversationMessages(args) {
464
464
  args.conversation.messages = [];
465
465
  return;
466
466
  }
467
- const { ensureLegacyConversationImport: ensureLegacyConversationImport2 } = await import("./legacy-import-3NQJVGQL.js");
467
+ const { ensureLegacyConversationImport: ensureLegacyConversationImport2 } = await import("./legacy-import-6WWXMALY.js");
468
468
  await ensureLegacyConversationImport2({ conversationId: args.conversationId });
469
469
  await hydrateConversationCompactions({
470
470
  conversation: args.conversation,
@@ -7,7 +7,7 @@ import {
7
7
  juniorConversations,
8
8
  juniorDestinations,
9
9
  juniorIdentities
10
- } from "./chunk-35BNLDOJ.js";
10
+ } from "./chunk-OGHXZ4KQ.js";
11
11
  import {
12
12
  parseSlackThreadId,
13
13
  resolveConversationPrivacy
@@ -40,7 +40,6 @@ function surfaceFallbackLabel(surface) {
40
40
  }
41
41
 
42
42
  // src/api/conversations/projection.ts
43
- var HUNG_TURN_PROGRESS_MS = 5 * 60 * 1e3;
44
43
  var PRIVATE_CONVERSATION_LABEL = "Private Conversation";
45
44
  function privateConversationLabel(slackConversation) {
46
45
  if (!slackConversation) {
@@ -70,17 +69,13 @@ function actorIdentityReport(actor) {
70
69
  };
71
70
  return Object.keys(identity).length > 0 ? identity : void 0;
72
71
  }
73
- function statusFromConversation(conversation, nowMs) {
72
+ function statusFromConversation(conversation) {
74
73
  if (conversation.execution.status === "idle") {
75
74
  return "completed";
76
75
  }
77
76
  if (conversation.execution.status === "failed") {
78
77
  return "failed";
79
78
  }
80
- const updatedAtMs = conversation.execution.updatedAtMs ?? conversation.updatedAtMs;
81
- if (conversation.execution.status === "running" && nowMs - updatedAtMs > HUNG_TURN_PROGRESS_MS) {
82
- return "hung";
83
- }
84
79
  return "active";
85
80
  }
86
81
  function titleFromConversation(args) {
@@ -129,7 +124,7 @@ function channelNameRedactedFromConversation(conversation) {
129
124
  }) !== "public";
130
125
  }
131
126
  function conversationSummaryFromStoredConversation(args) {
132
- const { conversation, durationMs, nowMs, usage } = args;
127
+ const { conversation, durationMs, usage } = args;
133
128
  const surface = surfaceFromSource(
134
129
  conversation.source,
135
130
  conversation.conversationId
@@ -147,7 +142,7 @@ function conversationSummaryFromStoredConversation(args) {
147
142
  ).toISOString(),
148
143
  lastSeenAt: new Date(conversation.lastActivityAtMs).toISOString(),
149
144
  startedAt: new Date(conversation.createdAtMs).toISOString(),
150
- status: statusFromConversation(conversation, nowMs),
145
+ status: statusFromConversation(conversation),
151
146
  surface,
152
147
  ...usage ? { cumulativeUsage: usage } : {},
153
148
  ...actorIdentity ? { actorIdentity } : {},
@@ -244,7 +239,6 @@ async function readConversationFeedFromSql(limit = CONVERSATION_FEED_LIMIT) {
244
239
  (row) => conversationSummaryFromStoredConversation({
245
240
  conversation: conversationFromRow(row),
246
241
  durationMs: row.conversation.durationMs,
247
- nowMs,
248
242
  usage: row.conversation.usage ?? void 0
249
243
  })
250
244
  ),
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  JUNIOR_THREAD_STATE_TTL_MS
3
- } from "./chunk-WJOSZOON.js";
3
+ } from "./chunk-6DTVCPLO.js";
4
4
  import {
5
5
  getChatConfig,
6
6
  getDefaultRedisStateAdapterFor,
@@ -15,11 +15,11 @@ import {
15
15
 
16
16
  // src/chat/conversations/sql/store.ts
17
17
  import { randomUUID as randomUUID2 } from "crypto";
18
- import { and as and2, asc, desc, eq as eq2, isNull, sql as sql4 } from "drizzle-orm";
18
+ import { and as and2, asc, desc, eq as eq2, isNull, sql as sql5 } from "drizzle-orm";
19
19
 
20
20
  // src/chat/identities/sql.ts
21
21
  import { randomUUID } from "crypto";
22
- import { and, eq, sql as sql3 } from "drizzle-orm";
22
+ import { and, eq, sql as sql4 } from "drizzle-orm";
23
23
 
24
24
  // src/db/schema/agent-steps.ts
25
25
  import {
@@ -236,6 +236,7 @@ var juniorAgentSteps = pgTable5(
236
236
  );
237
237
 
238
238
  // src/db/schema/conversation-messages.ts
239
+ import { sql as sql3 } from "drizzle-orm";
239
240
  import { index as index5, jsonb as jsonb5, pgTable as pgTable6, primaryKey as primaryKey2, text as text6 } from "drizzle-orm/pg-core";
240
241
  var juniorConversationMessages = pgTable6(
241
242
  "junior_conversation_messages",
@@ -256,6 +257,10 @@ var juniorConversationMessages = pgTable6(
256
257
  index5("junior_conversation_messages_activity_idx").on(
257
258
  table.conversationId,
258
259
  table.createdAt
260
+ ),
261
+ index5("junior_conversation_messages_search_idx").using(
262
+ "gin",
263
+ sql3`to_tsvector('english', ${table.text})`
259
264
  )
260
265
  ]
261
266
  );
@@ -288,8 +293,8 @@ async function upsertUser(executor, args) {
288
293
  }).onConflictDoUpdate({
289
294
  target: juniorUsers.primaryEmailNormalized,
290
295
  set: {
291
- displayName: sql3`coalesce(${juniorUsers.displayName}, excluded.display_name)`,
292
- updatedAt: sql3`excluded.updated_at`
296
+ displayName: sql4`coalesce(${juniorUsers.displayName}, excluded.display_name)`,
297
+ updatedAt: sql4`excluded.updated_at`
293
298
  }
294
299
  }).returning({ id: juniorUsers.id });
295
300
  const id = rows[0]?.id;
@@ -350,16 +355,16 @@ async function upsertIdentity(executor, identity, nowMs = Date.now()) {
350
355
  juniorIdentities.providerSubjectId
351
356
  ],
352
357
  set: {
353
- kind: sql3`excluded.kind`,
354
- userId: sql3`coalesce(${juniorIdentities.userId}, excluded.user_id)`,
355
- displayName: sql3`coalesce(${juniorIdentities.displayName}, excluded.display_name)`,
356
- handle: sql3`coalesce(${juniorIdentities.handle}, excluded.handle)`,
357
- 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`,
358
- 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`,
359
- emailVerified: sql3`${juniorIdentities.emailVerified} OR excluded.email_verified`,
360
- avatarUrl: sql3`coalesce(${juniorIdentities.avatarUrl}, excluded.avatar_url)`,
361
- metadata: sql3`coalesce(${juniorIdentities.metadata}, excluded.metadata_json)`,
362
- updatedAt: sql3`excluded.updated_at`
358
+ kind: sql4`excluded.kind`,
359
+ userId: sql4`coalesce(${juniorIdentities.userId}, excluded.user_id)`,
360
+ displayName: sql4`coalesce(${juniorIdentities.displayName}, excluded.display_name)`,
361
+ handle: sql4`coalesce(${juniorIdentities.handle}, excluded.handle)`,
362
+ email: sql4`case when ${juniorIdentities.emailVerified} then coalesce(${juniorIdentities.email}, excluded.email) when excluded.email_verified then excluded.email else coalesce(${juniorIdentities.email}, excluded.email) end`,
363
+ emailNormalized: sql4`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`,
364
+ emailVerified: sql4`${juniorIdentities.emailVerified} OR excluded.email_verified`,
365
+ avatarUrl: sql4`coalesce(${juniorIdentities.avatarUrl}, excluded.avatar_url)`,
366
+ metadata: sql4`coalesce(${juniorIdentities.metadata}, excluded.metadata_json)`,
367
+ updatedAt: sql4`excluded.updated_at`
363
368
  }
364
369
  }).returning({
365
370
  id: juniorIdentities.id,
@@ -763,7 +768,7 @@ var SqlStore = class {
763
768
  }).onConflictDoUpdate({
764
769
  target: juniorConversations.conversationId,
765
770
  set: {
766
- parentConversationId: sql4`coalesce(${juniorConversations.parentConversationId}, excluded.parent_conversation_id)`
771
+ parentConversationId: sql5`coalesce(${juniorConversations.parentConversationId}, excluded.parent_conversation_id)`
767
772
  }
768
773
  });
769
774
  }
@@ -890,9 +895,9 @@ var SqlStore = class {
890
895
  /** Upsert the conversation row while preserving previously discovered nullable metadata fields. */
891
896
  async upsertConversation(args) {
892
897
  const { conversation } = args;
893
- const incomingExecutionVersion = sql4`coalesce(excluded.execution_updated_at, excluded.updated_at)`;
894
- const currentExecutionVersion = sql4`coalesce(${juniorConversations.executionUpdatedAt}, ${juniorConversations.updatedAt})`;
895
- const incomingExecutionIsFresh = sql4`${incomingExecutionVersion} >= ${currentExecutionVersion}`;
898
+ const incomingExecutionVersion = sql5`coalesce(excluded.execution_updated_at, excluded.updated_at)`;
899
+ const currentExecutionVersion = sql5`coalesce(${juniorConversations.executionUpdatedAt}, ${juniorConversations.updatedAt})`;
900
+ const incomingExecutionIsFresh = sql5`${incomingExecutionVersion} >= ${currentExecutionVersion}`;
896
901
  const destinationId = await this.upsertDestination(
897
902
  destinationUpsertFromDestination({
898
903
  channelName: conversation.channelName,
@@ -934,24 +939,24 @@ var SqlStore = class {
934
939
  }).onConflictDoUpdate({
935
940
  target: juniorConversations.conversationId,
936
941
  set: {
937
- source: sql4`coalesce(excluded.source, ${juniorConversations.source})`,
938
- originType: sql4`coalesce(excluded.origin_type, ${juniorConversations.originType})`,
939
- originId: sql4`coalesce(excluded.origin_id, ${juniorConversations.originId})`,
940
- originRunId: sql4`coalesce(excluded.origin_run_id, ${juniorConversations.originRunId})`,
941
- destinationId: sql4`coalesce(excluded.destination_id, ${juniorConversations.destinationId})`,
942
- actorIdentityId: sql4`coalesce(excluded.actor_identity_id, ${juniorConversations.actorIdentityId})`,
943
- creatorIdentityId: sql4`coalesce(excluded.creator_identity_id, ${juniorConversations.creatorIdentityId})`,
944
- credentialSubjectIdentityId: sql4`coalesce(excluded.credential_subject_identity_id, ${juniorConversations.credentialSubjectIdentityId})`,
945
- channelName: sql4`coalesce(excluded.channel_name, ${juniorConversations.channelName})`,
946
- title: sql4`coalesce(excluded.title, ${juniorConversations.title})`,
947
- createdAt: sql4`least(${juniorConversations.createdAt}, excluded.created_at)`,
948
- lastActivityAt: sql4`greatest(${juniorConversations.lastActivityAt}, excluded.last_activity_at)`,
949
- updatedAt: sql4`greatest(${juniorConversations.updatedAt}, excluded.updated_at)`,
950
- executionUpdatedAt: sql4`case when ${incomingExecutionIsFresh} then excluded.execution_updated_at else ${juniorConversations.executionUpdatedAt} end`,
951
- executionStatus: sql4`case when ${incomingExecutionIsFresh} then excluded.execution_status else ${juniorConversations.executionStatus} end`,
952
- runId: sql4`case when ${incomingExecutionIsFresh} then excluded.run_id else ${juniorConversations.runId} end`,
953
- lastCheckpointAt: sql4`case when ${incomingExecutionIsFresh} then coalesce(excluded.last_checkpoint_at, ${juniorConversations.lastCheckpointAt}) else ${juniorConversations.lastCheckpointAt} end`,
954
- lastEnqueuedAt: sql4`case when ${incomingExecutionIsFresh} then coalesce(excluded.last_enqueued_at, ${juniorConversations.lastEnqueuedAt}) else ${juniorConversations.lastEnqueuedAt} end`
942
+ source: sql5`coalesce(excluded.source, ${juniorConversations.source})`,
943
+ originType: sql5`coalesce(excluded.origin_type, ${juniorConversations.originType})`,
944
+ originId: sql5`coalesce(excluded.origin_id, ${juniorConversations.originId})`,
945
+ originRunId: sql5`coalesce(excluded.origin_run_id, ${juniorConversations.originRunId})`,
946
+ destinationId: sql5`coalesce(excluded.destination_id, ${juniorConversations.destinationId})`,
947
+ actorIdentityId: sql5`coalesce(excluded.actor_identity_id, ${juniorConversations.actorIdentityId})`,
948
+ creatorIdentityId: sql5`coalesce(excluded.creator_identity_id, ${juniorConversations.creatorIdentityId})`,
949
+ credentialSubjectIdentityId: sql5`coalesce(excluded.credential_subject_identity_id, ${juniorConversations.credentialSubjectIdentityId})`,
950
+ channelName: sql5`coalesce(excluded.channel_name, ${juniorConversations.channelName})`,
951
+ title: sql5`coalesce(excluded.title, ${juniorConversations.title})`,
952
+ createdAt: sql5`least(${juniorConversations.createdAt}, excluded.created_at)`,
953
+ lastActivityAt: sql5`greatest(${juniorConversations.lastActivityAt}, excluded.last_activity_at)`,
954
+ updatedAt: sql5`greatest(${juniorConversations.updatedAt}, excluded.updated_at)`,
955
+ executionUpdatedAt: sql5`case when ${incomingExecutionIsFresh} then excluded.execution_updated_at else ${juniorConversations.executionUpdatedAt} end`,
956
+ executionStatus: sql5`case when ${incomingExecutionIsFresh} then excluded.execution_status else ${juniorConversations.executionStatus} end`,
957
+ runId: sql5`case when ${incomingExecutionIsFresh} then excluded.run_id else ${juniorConversations.runId} end`,
958
+ lastCheckpointAt: sql5`case when ${incomingExecutionIsFresh} then coalesce(excluded.last_checkpoint_at, ${juniorConversations.lastCheckpointAt}) else ${juniorConversations.lastCheckpointAt} end`,
959
+ lastEnqueuedAt: sql5`case when ${incomingExecutionIsFresh} then coalesce(excluded.last_enqueued_at, ${juniorConversations.lastEnqueuedAt}) else ${juniorConversations.lastEnqueuedAt} end`
955
960
  }
956
961
  });
957
962
  }
@@ -959,7 +964,7 @@ var SqlStore = class {
959
964
  if (!destination) {
960
965
  return void 0;
961
966
  }
962
- const visibilityUpdate = destination.refreshVisibility ? sql4`excluded.visibility` : juniorDestinations.visibility;
967
+ const visibilityUpdate = destination.refreshVisibility ? sql5`excluded.visibility` : juniorDestinations.visibility;
963
968
  const rows = await this.executor.db().insert(juniorDestinations).values({
964
969
  id: randomUUID2(),
965
970
  provider: destination.provider,
@@ -979,14 +984,14 @@ var SqlStore = class {
979
984
  juniorDestinations.providerDestinationId
980
985
  ],
981
986
  set: {
982
- kind: sql4`excluded.kind`,
983
- displayName: sql4`coalesce(excluded.display_name, ${juniorDestinations.displayName})`,
987
+ kind: sql5`excluded.kind`,
988
+ displayName: sql5`coalesce(excluded.display_name, ${juniorDestinations.displayName})`,
984
989
  // Signal-less writes insert as private but must not clobber an
985
990
  // existing public/private value. Live source signals refresh this
986
991
  // field so converted channels converge on the next message.
987
992
  visibility: visibilityUpdate,
988
- metadata: sql4`coalesce(excluded.metadata_json, ${juniorDestinations.metadata})`,
989
- updatedAt: sql4`excluded.updated_at`
993
+ metadata: sql5`coalesce(excluded.metadata_json, ${juniorDestinations.metadata})`,
994
+ updatedAt: sql5`excluded.updated_at`
990
995
  }
991
996
  }).returning({ id: juniorDestinations.id });
992
997
  return rows[0]?.id;
@@ -997,7 +1002,7 @@ function createSqlStore(executor) {
997
1002
  }
998
1003
 
999
1004
  // src/chat/conversations/sql/history.ts
1000
- import { and as and3, asc as asc2, eq as eq3, sql as sql6 } from "drizzle-orm";
1005
+ import { and as and3, asc as asc2, eq as eq3, sql as sql7 } from "drizzle-orm";
1001
1006
 
1002
1007
  // src/chat/conversations/history.ts
1003
1008
  import { z as z3 } from "zod";
@@ -1461,7 +1466,7 @@ var newAgentStepSchema = z3.object({
1461
1466
  }).strict();
1462
1467
 
1463
1468
  // src/chat/conversations/sql/conversation-row.ts
1464
- import { sql as sql5 } from "drizzle-orm";
1469
+ import { sql as sql6 } from "drizzle-orm";
1465
1470
  async function ensureConversationRow(executor, conversationId, atMs) {
1466
1471
  const at = new Date(atMs);
1467
1472
  await executor.db().insert(juniorConversations).values({
@@ -1473,8 +1478,8 @@ async function ensureConversationRow(executor, conversationId, atMs) {
1473
1478
  }).onConflictDoUpdate({
1474
1479
  target: juniorConversations.conversationId,
1475
1480
  set: {
1476
- lastActivityAt: sql5`greatest(${juniorConversations.lastActivityAt}, excluded.last_activity_at)`,
1477
- updatedAt: sql5`greatest(${juniorConversations.updatedAt}, excluded.updated_at)`,
1481
+ lastActivityAt: sql6`greatest(${juniorConversations.lastActivityAt}, excluded.last_activity_at)`,
1482
+ updatedAt: sql6`greatest(${juniorConversations.updatedAt}, excluded.updated_at)`,
1478
1483
  transcriptPurgedAt: null
1479
1484
  }
1480
1485
  });
@@ -1585,8 +1590,8 @@ var SqlAgentStepStore = class {
1585
1590
  /** Read the next `seq` and current highest epoch for one conversation. */
1586
1591
  async readCursor(conversationId) {
1587
1592
  const rows = await this.executor.db().select({
1588
- maxSeq: sql6`max(${juniorAgentSteps.seq})`,
1589
- maxEpoch: sql6`max(${juniorAgentSteps.contextEpoch})`
1593
+ maxSeq: sql7`max(${juniorAgentSteps.seq})`,
1594
+ maxEpoch: sql7`max(${juniorAgentSteps.contextEpoch})`
1590
1595
  }).from(juniorAgentSteps).where(eq3(juniorAgentSteps.conversationId, conversationId));
1591
1596
  const maxSeq = rows[0]?.maxSeq;
1592
1597
  const maxEpoch = rows[0]?.maxEpoch;
@@ -1601,7 +1606,7 @@ function createSqlAgentStepStore(executor) {
1601
1606
  }
1602
1607
 
1603
1608
  // src/chat/conversations/sql/messages.ts
1604
- import { and as and4, asc as asc3, eq as eq4, isNull as isNull2, sql as sql7 } from "drizzle-orm";
1609
+ import { and as and4, asc as asc3, eq as eq4, isNull as isNull2, sql as sql8 } from "drizzle-orm";
1605
1610
  function messageFromRow(row) {
1606
1611
  return {
1607
1612
  conversationId: row.conversationId,
@@ -1649,14 +1654,14 @@ var SqlConversationMessageStore = class {
1649
1654
  juniorConversationMessages.messageId
1650
1655
  ],
1651
1656
  set: {
1652
- meta: sql7`nullif(coalesce(${juniorConversationMessages.meta}, '{}'::jsonb) || coalesce(excluded.meta, '{}'::jsonb), '{}'::jsonb)`
1657
+ meta: sql8`nullif(coalesce(${juniorConversationMessages.meta}, '{}'::jsonb) || coalesce(excluded.meta, '{}'::jsonb), '{}'::jsonb)`
1653
1658
  }
1654
1659
  });
1655
1660
  });
1656
1661
  }
1657
1662
  async markReplied(conversationId, messageId, repliedAtMs) {
1658
1663
  await this.executor.db().update(juniorConversationMessages).set({
1659
- repliedAt: sql7`coalesce(${juniorConversationMessages.repliedAt}, ${new Date(repliedAtMs)})`
1664
+ repliedAt: sql8`coalesce(${juniorConversationMessages.repliedAt}, ${new Date(repliedAtMs)})`
1660
1665
  }).where(
1661
1666
  and4(
1662
1667
  eq4(juniorConversationMessages.conversationId, conversationId),
@@ -1678,6 +1683,72 @@ function createSqlConversationMessageStore(executor) {
1678
1683
  return new SqlConversationMessageStore(executor);
1679
1684
  }
1680
1685
 
1686
+ // src/chat/conversations/sql/search.ts
1687
+ import { and as and5, desc as desc2, eq as eq5, inArray, isNull as isNull3, ne, sql as sql9 } from "drizzle-orm";
1688
+ var SqlConversationSearchStore = class {
1689
+ constructor(executor) {
1690
+ this.executor = executor;
1691
+ }
1692
+ executor;
1693
+ async search(args) {
1694
+ const db = this.executor.db();
1695
+ const tsquery = sql9`websearch_to_tsquery('english', ${args.query})`;
1696
+ const rank = sql9`ts_rank_cd(to_tsvector('english', ${juniorConversationMessages.text}), ${tsquery})`;
1697
+ const excerpt = sql9`ts_headline('english', ${juniorConversationMessages.text}, ${tsquery}, 'MaxFragments=2, MinWords=8, MaxWords=40, FragmentDelimiter=" … ", StartSel=**, StopSel=**')`;
1698
+ const role = sql9`${juniorConversationMessages.role}`;
1699
+ const bestPerConversation = db.selectDistinctOn([juniorConversations.conversationId], {
1700
+ conversationId: juniorConversations.conversationId,
1701
+ excerpt: excerpt.as("excerpt"),
1702
+ lastActivityAt: juniorConversations.lastActivityAt,
1703
+ messageCreatedAt: juniorConversationMessages.createdAt,
1704
+ messageId: juniorConversationMessages.messageId,
1705
+ providerDestinationId: juniorDestinations.providerDestinationId,
1706
+ rank: rank.as("rank"),
1707
+ role: role.as("role")
1708
+ }).from(juniorConversationMessages).innerJoin(
1709
+ juniorConversations,
1710
+ eq5(
1711
+ juniorConversations.conversationId,
1712
+ juniorConversationMessages.conversationId
1713
+ )
1714
+ ).innerJoin(
1715
+ juniorDestinations,
1716
+ eq5(juniorDestinations.id, juniorConversations.destinationId)
1717
+ ).where(
1718
+ and5(
1719
+ eq5(juniorConversations.source, "slack"),
1720
+ isNull3(juniorConversations.parentConversationId),
1721
+ isNull3(juniorConversations.transcriptPurgedAt),
1722
+ ne(juniorConversations.conversationId, args.currentConversationId),
1723
+ eq5(juniorDestinations.provider, args.scope.provider),
1724
+ eq5(juniorDestinations.providerTenantId, args.scope.providerTenantId),
1725
+ eq5(juniorDestinations.visibility, "public"),
1726
+ inArray(juniorConversationMessages.role, ["user", "assistant"]),
1727
+ sql9`to_tsvector('english', ${juniorConversationMessages.text}) @@ ${tsquery}`
1728
+ )
1729
+ ).orderBy(
1730
+ juniorConversations.conversationId,
1731
+ desc2(rank),
1732
+ desc2(juniorConversationMessages.createdAt)
1733
+ ).as("best_conversation_matches");
1734
+ const rows = await db.select().from(bestPerConversation).orderBy(
1735
+ desc2(bestPerConversation.rank),
1736
+ desc2(bestPerConversation.lastActivityAt)
1737
+ ).limit(args.limit);
1738
+ return rows.map((row) => ({
1739
+ conversationId: row.conversationId,
1740
+ excerpt: row.excerpt,
1741
+ messageCreatedAtMs: row.messageCreatedAt.getTime(),
1742
+ messageId: row.messageId,
1743
+ providerDestinationId: row.providerDestinationId,
1744
+ role: row.role
1745
+ }));
1746
+ }
1747
+ };
1748
+ function createSqlConversationSearchStore(executor) {
1749
+ return new SqlConversationSearchStore(executor);
1750
+ }
1751
+
1681
1752
  // src/db/neon.ts
1682
1753
  import { AsyncLocalStorage } from "async_hooks";
1683
1754
  import {
@@ -1933,24 +2004,25 @@ function createDb(args) {
1933
2004
  });
1934
2005
  }
1935
2006
  function getSqlExecutor() {
1936
- const { sql: sql8 } = getChatConfig();
1937
- if (current?.databaseUrl !== sql8.databaseUrl || current.driver !== sql8.driver) {
2007
+ const { sql: sql10 } = getChatConfig();
2008
+ if (current?.databaseUrl !== sql10.databaseUrl || current.driver !== sql10.driver) {
1938
2009
  if (current) {
1939
2010
  const previous = current;
1940
2011
  current = void 0;
1941
2012
  void previous.db.close().catch(() => void 0);
1942
2013
  }
1943
2014
  const db = createDb({
1944
- databaseUrl: sql8.databaseUrl,
1945
- driver: sql8.driver
2015
+ databaseUrl: sql10.databaseUrl,
2016
+ driver: sql10.driver
1946
2017
  });
1947
2018
  current = {
1948
- databaseUrl: sql8.databaseUrl,
1949
- driver: sql8.driver,
2019
+ databaseUrl: sql10.databaseUrl,
2020
+ driver: sql10.driver,
1950
2021
  db,
1951
2022
  store: createSqlStore(db),
1952
2023
  stepStore: createSqlAgentStepStore(db),
1953
- messageStore: createSqlConversationMessageStore(db)
2024
+ messageStore: createSqlConversationMessageStore(db),
2025
+ searchStore: createSqlConversationSearchStore(db)
1954
2026
  };
1955
2027
  }
1956
2028
  return current.db;
@@ -1970,6 +2042,10 @@ function getConversationMessageStore() {
1970
2042
  getSqlExecutor();
1971
2043
  return current.messageStore;
1972
2044
  }
2045
+ function getConversationSearchStore() {
2046
+ getSqlExecutor();
2047
+ return current.searchStore;
2048
+ }
1973
2049
  async function closeDb() {
1974
2050
  const previous = current;
1975
2051
  current = void 0;
@@ -1999,5 +2075,6 @@ export {
1999
2075
  getConversationStore,
2000
2076
  getAgentStepStore,
2001
2077
  getConversationMessageStore,
2078
+ getConversationSearchStore,
2002
2079
  closeDb
2003
2080
  };