@sentry/junior 0.74.1 → 0.76.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 (121) hide show
  1. package/README.md +1 -1
  2. package/bin/junior.mjs +4 -66
  3. package/dist/agent-hooks-ZOE7RIED.js +37 -0
  4. package/dist/api-reference.d.ts +3 -1
  5. package/dist/app.js +5516 -5422
  6. package/dist/build/copy-build-content.d.ts +1 -1
  7. package/dist/build/virtual-config.d.ts +2 -2
  8. package/dist/chat/agent-dispatch/context.d.ts +2 -3
  9. package/dist/chat/agent-dispatch/runner.d.ts +2 -0
  10. package/dist/chat/agent-dispatch/types.d.ts +2 -1
  11. package/dist/chat/config.d.ts +3 -0
  12. package/dist/chat/credentials/state-adapter-token-store.d.ts +2 -0
  13. package/dist/chat/credentials/subject.d.ts +3 -3
  14. package/dist/chat/credentials/user-token-store.d.ts +17 -12
  15. package/dist/chat/db.d.ts +8 -0
  16. package/dist/chat/mcp/auth-store.d.ts +2 -1
  17. package/dist/chat/mcp/oauth.d.ts +2 -1
  18. package/dist/chat/oauth-flow.d.ts +3 -1
  19. package/dist/chat/pi/client.d.ts +15 -7
  20. package/dist/chat/plugins/agent-hooks.d.ts +20 -13
  21. package/dist/chat/plugins/auth/oauth-request.d.ts +11 -7
  22. package/dist/chat/plugins/credential-hooks.d.ts +6 -6
  23. package/dist/chat/plugins/logging.d.ts +2 -2
  24. package/dist/chat/plugins/model.d.ts +9 -0
  25. package/dist/chat/plugins/package-discovery.d.ts +2 -1
  26. package/dist/chat/plugins/prompt.d.ts +5 -0
  27. package/dist/chat/plugins/registry.d.ts +4 -0
  28. package/dist/chat/plugins/state.d.ts +3 -5
  29. package/dist/chat/plugins/task-callback.d.ts +5 -0
  30. package/dist/chat/plugins/task-message.d.ts +23 -0
  31. package/dist/chat/plugins/task-queue.d.ts +5 -0
  32. package/dist/chat/plugins/task-runner.d.ts +12 -0
  33. package/dist/chat/plugins/task-signing.d.ts +31 -0
  34. package/dist/chat/plugins/types.d.ts +1 -0
  35. package/dist/chat/plugins/validation.d.ts +5 -0
  36. package/dist/chat/prompt.d.ts +15 -1
  37. package/dist/chat/requester.d.ts +6 -5
  38. package/dist/chat/respond-helpers.d.ts +2 -0
  39. package/dist/chat/respond.d.ts +13 -2
  40. package/dist/chat/runtime/agent-continue-runner.d.ts +4 -0
  41. package/dist/chat/runtime/reply-executor.d.ts +5 -1
  42. package/dist/chat/runtime/slack-resume.d.ts +10 -2
  43. package/dist/chat/runtime/slack-runtime.d.ts +6 -1
  44. package/dist/chat/sandbox/egress-credentials.d.ts +8 -8
  45. package/dist/chat/sandbox/sandbox.d.ts +2 -2
  46. package/dist/chat/sentry.d.ts +1 -0
  47. package/dist/chat/services/mcp-auth-orchestration.d.ts +2 -1
  48. package/dist/chat/services/plugin-auth-orchestration.d.ts +2 -1
  49. package/dist/chat/services/subscribed-decision.d.ts +2 -2
  50. package/dist/chat/services/turn-session-record.d.ts +11 -7
  51. package/dist/chat/sql/db.d.ts +3 -0
  52. package/dist/chat/sql/executor.d.ts +7 -0
  53. package/dist/chat/sql/neon.d.ts +2 -4
  54. package/dist/chat/sql/postgres.d.ts +6 -0
  55. package/dist/chat/state/turn-session.d.ts +8 -5
  56. package/dist/chat/task-execution/state.d.ts +7 -2
  57. package/dist/chat/task-execution/worker.d.ts +1 -1
  58. package/dist/chat/tools/agent-tools.d.ts +9 -2
  59. package/dist/chat/tools/slack/context.d.ts +2 -2
  60. package/dist/chat/tools/types.d.ts +7 -4
  61. package/dist/chat/vercel-queue-client.d.ts +3 -0
  62. package/dist/{chunk-YOHFWWBV.js → chunk-2ECJXSVQ.js} +5 -107
  63. package/dist/{chunk-OR6NQJ5E.js → chunk-4SCWV7TJ.js} +3 -3
  64. package/dist/chunk-4UO6FK4G.js +64 -0
  65. package/dist/chunk-56TBVRJG.js +115 -0
  66. package/dist/{chunk-3BYAPS6B.js → chunk-EJN6G5A2.js} +17 -11
  67. package/dist/{chunk-SQGMG7OD.js → chunk-HHDUKWVG.js} +508 -149
  68. package/dist/{chunk-6UP2Z2RZ.js → chunk-JBASI5VV.js} +7 -7
  69. package/dist/chunk-KNFROR7R.js +127 -0
  70. package/dist/{chunk-HYHKTFG2.js → chunk-KOIMO7S3.js} +186 -910
  71. package/dist/chunk-MLKGABMK.js +9 -0
  72. package/dist/chunk-NFTMTIP3.js +964 -0
  73. package/dist/chunk-NYKJ3KON.js +1082 -0
  74. package/dist/{chunk-SJHUF3DP.js → chunk-OJ53FYVG.js} +2 -10
  75. package/dist/{chunk-KVZL5NZS.js → chunk-Q3XNY442.js} +17 -7
  76. package/dist/{chunk-YRDS7VKO.js → chunk-Q6XFTRV5.js} +2 -2
  77. package/dist/chunk-R6Z5XWY3.js +1076 -0
  78. package/dist/chunk-RV5RYIJW.js +56 -0
  79. package/dist/chunk-SG5WAA7H.js +132 -0
  80. package/dist/chunk-ST6YNAXG.js +54 -0
  81. package/dist/{chunk-GM7HTXYC.js → chunk-T77LUIX3.js} +148 -151
  82. package/dist/{chunk-CYUI7JU5.js → chunk-VALUBQ7R.js} +22 -30
  83. package/dist/chunk-XBBC6W45.js +71 -0
  84. package/dist/chunk-Y2CM7HXH.js +111 -0
  85. package/dist/{chunk-F6HWCPOC.js → chunk-Y5OFBCBZ.js} +1 -1
  86. package/dist/{chunk-M4FLLXXD.js → chunk-Z4CIQ3EB.js} +5 -1
  87. package/dist/{chunk-7Q5YOUUT.js → chunk-ZLMBNBUG.js} +146 -52
  88. package/dist/{chunk-2LUZA3LY.js → chunk-ZQB37HUX.js} +11 -11
  89. package/dist/cli/chat.js +87 -8
  90. package/dist/cli/check.js +8 -7
  91. package/dist/cli/env.js +4 -53
  92. package/dist/cli/init.js +6 -1
  93. package/dist/cli/main.js +84 -0
  94. package/dist/cli/plugins.js +244 -0
  95. package/dist/cli/run.js +5 -52
  96. package/dist/cli/snapshot-warmup.js +12 -11
  97. package/dist/cli/upgrade.js +385 -26
  98. package/dist/db-7A7PFRGL.js +17 -0
  99. package/dist/deployment.d.ts +1 -0
  100. package/dist/handlers/sandbox-egress-route.d.ts +4 -0
  101. package/dist/handlers/slack-webhook.d.ts +4 -0
  102. package/dist/handlers/webhooks.d.ts +6 -13
  103. package/dist/instrumentation.js +14 -18
  104. package/dist/nitro.d.ts +1 -1
  105. package/dist/nitro.js +67 -101
  106. package/dist/plugin-module.d.ts +21 -0
  107. package/dist/plugins-PZMDS7AT.js +15 -0
  108. package/dist/plugins.d.ts +9 -5
  109. package/dist/registry-OIPAJU2O.js +46 -0
  110. package/dist/reporting/conversations.d.ts +3 -3
  111. package/dist/reporting.d.ts +6 -5
  112. package/dist/reporting.js +42 -28
  113. package/dist/{runner-27NP2TEO.js → runner-KPLNHDCV.js} +77 -19
  114. package/dist/sentry-4CP5NNQ5.js +31 -0
  115. package/dist/validation-SLA6IGF7.js +15 -0
  116. package/dist/vercel.js +1 -1
  117. package/package.json +14 -11
  118. package/dist/chat/conversations/configured.d.ts +0 -5
  119. package/dist/chat/conversations/state.d.ts +0 -4
  120. package/dist/chunk-2KG3PWR4.js +0 -17
  121. package/dist/chunk-JL2SLRAT.js +0 -1970
@@ -0,0 +1,1076 @@
1
+ import {
2
+ JUNIOR_THREAD_STATE_TTL_MS
3
+ } from "./chunk-Z4CIQ3EB.js";
4
+ import {
5
+ getDefaultRedisStateAdapterFor,
6
+ getStateAdapter
7
+ } from "./chunk-Y5OFBCBZ.js";
8
+ import {
9
+ parseDestination,
10
+ sameDestination
11
+ } from "./chunk-Q6XFTRV5.js";
12
+ import {
13
+ getChatConfig
14
+ } from "./chunk-T77LUIX3.js";
15
+ import {
16
+ parseStoredSlackRequester
17
+ } from "./chunk-VALUBQ7R.js";
18
+ import {
19
+ isRecord,
20
+ toOptionalNumber,
21
+ toOptionalString
22
+ } from "./chunk-EJN6G5A2.js";
23
+
24
+ // src/chat/task-execution/state.ts
25
+ import { randomUUID } from "crypto";
26
+ var CONVERSATION_PREFIX = "junior:conversation";
27
+ var CONVERSATION_SCHEMA_VERSION = 1;
28
+ var CONVERSATION_ACTIVITY_INDEX_MAX_LENGTH = 1e4;
29
+ var CONVERSATION_INDEX_LOCK_TTL_MS = 1e4;
30
+ var CONVERSATION_INDEX_LOCK_WAIT_MS = 2e3;
31
+ var CONVERSATION_INDEX_LOCK_RETRY_MS = 25;
32
+ var CONVERSATION_MUTATION_LOCK_TTL_MS = 1e4;
33
+ var CONVERSATION_MUTATION_WAIT_MS = 1e4;
34
+ var CONVERSATION_MUTATION_RETRY_MS = 25;
35
+ var InvalidConversationRecordError = class extends Error {
36
+ constructor(conversationId) {
37
+ super(`Conversation record is invalid for ${conversationId}`);
38
+ this.name = "InvalidConversationRecordError";
39
+ }
40
+ };
41
+ var CONVERSATION_BY_ACTIVITY_INDEX_KEY = `${CONVERSATION_PREFIX}:by-activity`;
42
+ var CONVERSATION_ACTIVE_INDEX_KEY = `${CONVERSATION_PREFIX}:active`;
43
+ var CONVERSATION_WORK_LEASE_TTL_MS = 9e4;
44
+ var CONVERSATION_WORK_CHECK_IN_INTERVAL_MS = 15e3;
45
+ var CONVERSATION_WORK_STALE_ENQUEUE_MS = 6e4;
46
+ function conversationKey(conversationId) {
47
+ return `${CONVERSATION_PREFIX}:${conversationId}`;
48
+ }
49
+ function indexLockKey(indexKey) {
50
+ return `${indexKey}:lock`;
51
+ }
52
+ function mutationLockKey(conversationId) {
53
+ return `${CONVERSATION_PREFIX}:mutation:${conversationId}`;
54
+ }
55
+ function now() {
56
+ return Date.now();
57
+ }
58
+ function compareMessages(left, right) {
59
+ return left.createdAtMs - right.createdAtMs || left.receivedAtMs - right.receivedAtMs || left.inboundMessageId.localeCompare(right.inboundMessageId);
60
+ }
61
+ function compareIndexDescending(left, right) {
62
+ return right.score - left.score || right.conversationId.localeCompare(left.conversationId);
63
+ }
64
+ function compareIndexAscending(left, right) {
65
+ return left.score - right.score || left.conversationId.localeCompare(right.conversationId);
66
+ }
67
+ function uniqueStrings(values) {
68
+ return [...new Set(values)];
69
+ }
70
+ function normalizeSource(value) {
71
+ if (value === "api" || value === "internal" || value === "local" || value === "plugin" || value === "scheduler" || value === "slack") {
72
+ return value;
73
+ }
74
+ return void 0;
75
+ }
76
+ function normalizeExecutionStatus(value) {
77
+ if (value === "awaiting_resume" || value === "idle" || value === "pending" || value === "running") {
78
+ return value;
79
+ }
80
+ return void 0;
81
+ }
82
+ function normalizeMetadata(value) {
83
+ if (!isRecord(value)) {
84
+ return void 0;
85
+ }
86
+ return value;
87
+ }
88
+ function normalizeInput(value) {
89
+ if (!isRecord(value)) {
90
+ return void 0;
91
+ }
92
+ const text = toOptionalString(value.text);
93
+ if (!text) {
94
+ return void 0;
95
+ }
96
+ return {
97
+ text,
98
+ authorId: toOptionalString(value.authorId),
99
+ attachments: Array.isArray(value.attachments) ? [...value.attachments] : void 0,
100
+ metadata: normalizeMetadata(value.metadata)
101
+ };
102
+ }
103
+ function normalizeMessage(value) {
104
+ if (!isRecord(value)) {
105
+ return void 0;
106
+ }
107
+ const conversationId = toOptionalString(value.conversationId);
108
+ const inboundMessageId = toOptionalString(value.inboundMessageId);
109
+ const source = normalizeSource(value.source);
110
+ const destination = parseDestination(value.destination);
111
+ const createdAtMs = toOptionalNumber(value.createdAtMs);
112
+ const receivedAtMs = toOptionalNumber(value.receivedAtMs);
113
+ const input = normalizeInput(value.input);
114
+ if (!conversationId || !destination || !inboundMessageId || !source || typeof createdAtMs !== "number" || typeof receivedAtMs !== "number" || !input) {
115
+ return void 0;
116
+ }
117
+ return {
118
+ conversationId,
119
+ destination,
120
+ inboundMessageId,
121
+ source,
122
+ createdAtMs,
123
+ receivedAtMs,
124
+ input,
125
+ injectedAtMs: toOptionalNumber(value.injectedAtMs)
126
+ };
127
+ }
128
+ function normalizeRequester(value) {
129
+ return parseStoredSlackRequester(value);
130
+ }
131
+ function normalizeLease(value) {
132
+ if (!isRecord(value)) {
133
+ return void 0;
134
+ }
135
+ const token = toOptionalString(value.token);
136
+ const acquiredAtMs = toOptionalNumber(value.acquiredAtMs);
137
+ const lastCheckInAtMs = toOptionalNumber(value.lastCheckInAtMs);
138
+ const expiresAtMs = toOptionalNumber(value.expiresAtMs);
139
+ if (!token || typeof acquiredAtMs !== "number" || typeof lastCheckInAtMs !== "number" || typeof expiresAtMs !== "number") {
140
+ return void 0;
141
+ }
142
+ return {
143
+ token,
144
+ acquiredAtMs,
145
+ lastCheckInAtMs,
146
+ expiresAtMs
147
+ };
148
+ }
149
+ function normalizeExecution(conversationId, value) {
150
+ if (!isRecord(value)) {
151
+ return void 0;
152
+ }
153
+ const status = normalizeExecutionStatus(value.status);
154
+ if (!status) {
155
+ return void 0;
156
+ }
157
+ const pendingMessages2 = Array.isArray(value.pendingMessages) ? value.pendingMessages.map(normalizeMessage).filter((message) => Boolean(message)).filter((message) => message.conversationId === conversationId).filter((message) => message.injectedAtMs === void 0).sort(compareMessages) : [];
158
+ const inboundMessageIds = Array.isArray(value.inboundMessageIds) ? uniqueStrings(
159
+ value.inboundMessageIds.map((id) => typeof id === "string" ? id : void 0).filter((id) => Boolean(id))
160
+ ) : [];
161
+ const lease = normalizeLease(value.lease);
162
+ const normalizedStatus = status === "idle" && lease ? "running" : status === "idle" && pendingMessages2.length > 0 ? "pending" : status;
163
+ return {
164
+ status: normalizedStatus,
165
+ inboundMessageIds: uniqueStrings([
166
+ ...inboundMessageIds,
167
+ ...pendingMessages2.map((message) => message.inboundMessageId)
168
+ ]),
169
+ pendingCount: pendingMessages2.length,
170
+ pendingMessages: pendingMessages2,
171
+ lease,
172
+ lastCheckpointAtMs: toOptionalNumber(value.lastCheckpointAtMs),
173
+ lastEnqueuedAtMs: toOptionalNumber(value.lastEnqueuedAtMs),
174
+ runId: toOptionalString(value.runId),
175
+ updatedAtMs: toOptionalNumber(value.updatedAtMs)
176
+ };
177
+ }
178
+ function normalizeConversation(conversationId, value) {
179
+ if (!isRecord(value) || value.schemaVersion !== CONVERSATION_SCHEMA_VERSION) {
180
+ return void 0;
181
+ }
182
+ const storedConversationId = toOptionalString(value.conversationId);
183
+ const createdAtMs = toOptionalNumber(value.createdAtMs);
184
+ const lastActivityAtMs = toOptionalNumber(value.lastActivityAtMs);
185
+ const updatedAtMs = toOptionalNumber(value.updatedAtMs);
186
+ const execution = normalizeExecution(conversationId, value.execution);
187
+ const destination = value.destination === void 0 ? void 0 : parseDestination(value.destination);
188
+ if (storedConversationId !== conversationId || typeof createdAtMs !== "number" || typeof lastActivityAtMs !== "number" || typeof updatedAtMs !== "number" || !execution || value.destination !== void 0 && !destination) {
189
+ return void 0;
190
+ }
191
+ if (execution.pendingMessages.length > 0 && (!destination || execution.pendingMessages.some(
192
+ (message) => !sameDestination(message.destination, destination)
193
+ ))) {
194
+ return void 0;
195
+ }
196
+ return {
197
+ schemaVersion: CONVERSATION_SCHEMA_VERSION,
198
+ conversationId,
199
+ createdAtMs,
200
+ lastActivityAtMs,
201
+ updatedAtMs,
202
+ execution,
203
+ ...destination ? { destination } : {},
204
+ ...toOptionalString(value.title) ? { title: toOptionalString(value.title) } : {},
205
+ ...toOptionalString(value.channelName) ? { channelName: toOptionalString(value.channelName) } : {},
206
+ ...normalizeRequester(value.requester) ? { requester: normalizeRequester(value.requester) } : {},
207
+ ...normalizeSource(value.source) ? { source: normalizeSource(value.source) } : {}
208
+ };
209
+ }
210
+ function emptyConversation(args) {
211
+ return {
212
+ schemaVersion: CONVERSATION_SCHEMA_VERSION,
213
+ conversationId: args.conversationId,
214
+ createdAtMs: args.nowMs,
215
+ lastActivityAtMs: args.nowMs,
216
+ updatedAtMs: args.nowMs,
217
+ ...args.destination ? { destination: args.destination } : {},
218
+ ...args.source ? { source: args.source } : {},
219
+ execution: {
220
+ status: "idle",
221
+ inboundMessageIds: [],
222
+ pendingCount: 0,
223
+ pendingMessages: [],
224
+ updatedAtMs: args.nowMs
225
+ }
226
+ };
227
+ }
228
+ function isLeaseActive(lease, nowMs) {
229
+ return Boolean(lease && lease.expiresAtMs > nowMs);
230
+ }
231
+ function pendingMessages(conversation) {
232
+ return [...conversation.execution.pendingMessages].sort(compareMessages);
233
+ }
234
+ function hasRunnableWork(conversation) {
235
+ return conversation.execution.status !== "idle" || pendingMessages(conversation).length > 0;
236
+ }
237
+ function executionWithPendingMessages(execution, pending) {
238
+ const pendingMessages2 = [...pending].sort(compareMessages);
239
+ const status = execution.status === "idle" && execution.lease ? "running" : execution.status === "idle" && pendingMessages2.length > 0 ? "pending" : execution.status;
240
+ return {
241
+ ...execution,
242
+ status,
243
+ inboundMessageIds: uniqueStrings([
244
+ ...execution.inboundMessageIds,
245
+ ...pendingMessages2.map((message) => message.inboundMessageId)
246
+ ]),
247
+ pendingMessages: pendingMessages2,
248
+ pendingCount: pendingMessages2.length
249
+ };
250
+ }
251
+ function withExecutionUpdate(conversation, execution, nowMs) {
252
+ return {
253
+ ...conversation,
254
+ updatedAtMs: nowMs,
255
+ execution: {
256
+ ...executionWithPendingMessages(execution, execution.pendingMessages),
257
+ updatedAtMs: nowMs
258
+ }
259
+ };
260
+ }
261
+ async function getConnectedState(stateAdapter) {
262
+ const state = stateAdapter ?? getStateAdapter();
263
+ await state.connect();
264
+ return state;
265
+ }
266
+ async function sleep(ms) {
267
+ await new Promise((resolve) => {
268
+ const timer = setTimeout(resolve, ms);
269
+ timer.unref?.();
270
+ });
271
+ }
272
+ async function withIndexLock(state, indexKey, callback) {
273
+ const startedAtMs = now();
274
+ let lock;
275
+ while (true) {
276
+ lock = await state.acquireLock(
277
+ indexLockKey(indexKey),
278
+ CONVERSATION_INDEX_LOCK_TTL_MS
279
+ );
280
+ if (lock) {
281
+ break;
282
+ }
283
+ if (now() - startedAtMs >= CONVERSATION_INDEX_LOCK_WAIT_MS) {
284
+ throw new Error(
285
+ `Could not acquire conversation index lock for ${indexKey}`
286
+ );
287
+ }
288
+ await sleep(CONVERSATION_INDEX_LOCK_RETRY_MS);
289
+ }
290
+ try {
291
+ return await callback();
292
+ } finally {
293
+ await state.releaseLock(lock);
294
+ }
295
+ }
296
+ function normalizeIndexEntry(value) {
297
+ if (!isRecord(value)) {
298
+ return void 0;
299
+ }
300
+ const conversationId = toOptionalString(value.conversationId);
301
+ const score = toOptionalNumber(value.score);
302
+ if (!conversationId || typeof score !== "number") {
303
+ return void 0;
304
+ }
305
+ return { conversationId, score };
306
+ }
307
+ function uniqueIndexEntries(value) {
308
+ if (!Array.isArray(value)) {
309
+ return [];
310
+ }
311
+ const entries = /* @__PURE__ */ new Map();
312
+ for (const item of value) {
313
+ const entry = normalizeIndexEntry(item);
314
+ if (!entry) {
315
+ continue;
316
+ }
317
+ const existing = entries.get(entry.conversationId);
318
+ if (!existing || entry.score > existing.score) {
319
+ entries.set(entry.conversationId, entry);
320
+ }
321
+ }
322
+ return [...entries.values()];
323
+ }
324
+ function retainedIndexEntries(indexKey, entries) {
325
+ if (indexKey === CONVERSATION_BY_ACTIVITY_INDEX_KEY) {
326
+ return entries.sort(compareIndexDescending).slice(0, CONVERSATION_ACTIVITY_INDEX_MAX_LENGTH);
327
+ }
328
+ if (indexKey === CONVERSATION_ACTIVE_INDEX_KEY) {
329
+ return entries.sort(compareIndexAscending);
330
+ }
331
+ throw new Error(`Unknown conversation index ${indexKey}`);
332
+ }
333
+ function redisIndexKey(indexKey) {
334
+ const prefix = getChatConfig().state.keyPrefix;
335
+ return [...prefix ? [prefix] : [], indexKey].join(":");
336
+ }
337
+ function parseRedisIndexEntries(values) {
338
+ if (!Array.isArray(values)) {
339
+ return [];
340
+ }
341
+ const entries = [];
342
+ for (let index = 0; index < values.length; index += 2) {
343
+ const conversationId = toOptionalString(values[index]);
344
+ const score = typeof values[index + 1] === "number" ? values[index + 1] : Number(values[index + 1]);
345
+ if (!conversationId || !Number.isFinite(score)) {
346
+ continue;
347
+ }
348
+ entries.push({ conversationId, score });
349
+ }
350
+ return entries;
351
+ }
352
+ function redisConversationIndexStore(client) {
353
+ const upsertBoundedActivityScript = `
354
+ redis.call("ZADD", KEYS[1], ARGV[1], ARGV[2])
355
+ redis.call("PEXPIRE", KEYS[1], ARGV[3])
356
+ local extra = redis.call("ZCARD", KEYS[1]) - tonumber(ARGV[4])
357
+ if extra > 0 then
358
+ redis.call("ZREMRANGEBYRANK", KEYS[1], 0, extra - 1)
359
+ end
360
+ return 1
361
+ `;
362
+ return {
363
+ async list(args) {
364
+ const key = redisIndexKey(args.indexKey);
365
+ const limit = args.limit;
366
+ const offset = Math.max(0, args.offset ?? 0);
367
+ if (limit === 0) {
368
+ return [];
369
+ }
370
+ const values = args.scoreMax !== void 0 ? await client.sendCommand([
371
+ "ZRANGEBYSCORE",
372
+ key,
373
+ "-inf",
374
+ String(args.scoreMax),
375
+ "WITHSCORES",
376
+ ...limit !== void 0 || offset > 0 ? ["LIMIT", String(offset), String(limit ?? 1e9)] : []
377
+ ]) : await client.sendCommand([
378
+ args.order === "asc" ? "ZRANGE" : "ZREVRANGE",
379
+ key,
380
+ String(offset),
381
+ String(
382
+ limit === void 0 ? -1 : offset + Math.max(0, limit - 1)
383
+ ),
384
+ "WITHSCORES"
385
+ ]);
386
+ return parseRedisIndexEntries(values);
387
+ },
388
+ async remove(args) {
389
+ await client.sendCommand([
390
+ "ZREM",
391
+ redisIndexKey(args.indexKey),
392
+ args.conversationId
393
+ ]);
394
+ },
395
+ async upsert(args) {
396
+ const key = redisIndexKey(args.indexKey);
397
+ if (args.indexKey === CONVERSATION_BY_ACTIVITY_INDEX_KEY) {
398
+ await client.sendCommand([
399
+ "EVAL",
400
+ upsertBoundedActivityScript,
401
+ "1",
402
+ key,
403
+ String(args.score),
404
+ args.conversationId,
405
+ String(JUNIOR_THREAD_STATE_TTL_MS),
406
+ String(CONVERSATION_ACTIVITY_INDEX_MAX_LENGTH)
407
+ ]);
408
+ return;
409
+ }
410
+ if (args.indexKey === CONVERSATION_ACTIVE_INDEX_KEY) {
411
+ await client.sendCommand([
412
+ "ZADD",
413
+ key,
414
+ String(args.score),
415
+ args.conversationId
416
+ ]);
417
+ await client.sendCommand([
418
+ "PEXPIRE",
419
+ key,
420
+ String(JUNIOR_THREAD_STATE_TTL_MS)
421
+ ]);
422
+ return;
423
+ }
424
+ throw new Error(`Unknown conversation index ${args.indexKey}`);
425
+ }
426
+ };
427
+ }
428
+ function emulatedConversationIndexStore(state) {
429
+ const readIndex = async (indexKey) => uniqueIndexEntries(await state.get(indexKey));
430
+ const writeIndex = async (indexKey, entries) => {
431
+ await state.set(indexKey, entries, JUNIOR_THREAD_STATE_TTL_MS);
432
+ };
433
+ return {
434
+ async list(args) {
435
+ const entries = (await readIndex(args.indexKey)).filter(
436
+ (entry) => args.scoreMax === void 0 ? true : entry.score <= args.scoreMax
437
+ ).sort(
438
+ args.order === "asc" ? compareIndexAscending : compareIndexDescending
439
+ );
440
+ const offset = Math.max(0, args.offset ?? 0);
441
+ return entries.slice(
442
+ offset,
443
+ args.limit === void 0 ? entries.length : offset + args.limit
444
+ );
445
+ },
446
+ async remove(args) {
447
+ await withIndexLock(state, args.indexKey, async () => {
448
+ const entries = await readIndex(args.indexKey);
449
+ const next = entries.filter(
450
+ (entry) => entry.conversationId !== args.conversationId
451
+ );
452
+ if (next.length === entries.length) {
453
+ return;
454
+ }
455
+ await writeIndex(args.indexKey, next);
456
+ });
457
+ },
458
+ async upsert(args) {
459
+ await withIndexLock(state, args.indexKey, async () => {
460
+ const entries = await readIndex(args.indexKey);
461
+ const withoutCurrent = entries.filter(
462
+ (entry) => entry.conversationId !== args.conversationId
463
+ );
464
+ const next = retainedIndexEntries(args.indexKey, [
465
+ ...withoutCurrent,
466
+ { conversationId: args.conversationId, score: args.score }
467
+ ]);
468
+ await writeIndex(args.indexKey, next);
469
+ });
470
+ }
471
+ };
472
+ }
473
+ async function getConversationIndexStore(state) {
474
+ const redisStateAdapter = await getDefaultRedisStateAdapterFor(state);
475
+ if (redisStateAdapter) {
476
+ return redisConversationIndexStore(redisStateAdapter.getClient());
477
+ }
478
+ return emulatedConversationIndexStore(state);
479
+ }
480
+ async function upsertIndexEntry(args) {
481
+ const index = await getConversationIndexStore(args.state);
482
+ await index.upsert({
483
+ conversationId: args.conversationId,
484
+ indexKey: args.indexKey,
485
+ score: args.score
486
+ });
487
+ }
488
+ async function removeIndexEntry(args) {
489
+ const index = await getConversationIndexStore(args.state);
490
+ await index.remove({
491
+ conversationId: args.conversationId,
492
+ indexKey: args.indexKey
493
+ });
494
+ }
495
+ async function acquireMutationLock(state, conversationId) {
496
+ const startedAtMs = now();
497
+ while (true) {
498
+ const lock = await state.acquireLock(
499
+ mutationLockKey(conversationId),
500
+ CONVERSATION_MUTATION_LOCK_TTL_MS
501
+ );
502
+ if (lock) {
503
+ return lock;
504
+ }
505
+ if (now() - startedAtMs >= CONVERSATION_MUTATION_WAIT_MS) {
506
+ throw new Error(
507
+ `Could not acquire conversation mutation lock for ${conversationId}`
508
+ );
509
+ }
510
+ await sleep(CONVERSATION_MUTATION_RETRY_MS);
511
+ }
512
+ }
513
+ async function withConversationMutation(args, callback) {
514
+ const state = await getConnectedState(args.state);
515
+ const lock = await acquireMutationLock(state, args.conversationId);
516
+ try {
517
+ return await callback(state);
518
+ } finally {
519
+ await state.releaseLock(lock);
520
+ }
521
+ }
522
+ async function readConversation(state, conversationId) {
523
+ const raw = await state.get(conversationKey(conversationId));
524
+ if (raw == null) {
525
+ return void 0;
526
+ }
527
+ const conversation = normalizeConversation(conversationId, raw);
528
+ if (!conversation) {
529
+ throw new InvalidConversationRecordError(conversationId);
530
+ }
531
+ return conversation;
532
+ }
533
+ async function writeConversation(state, conversation) {
534
+ const execution = executionWithPendingMessages(
535
+ conversation.execution,
536
+ conversation.execution.pendingMessages
537
+ );
538
+ const next = {
539
+ ...conversation,
540
+ execution
541
+ };
542
+ await state.set(
543
+ conversationKey(next.conversationId),
544
+ next,
545
+ JUNIOR_THREAD_STATE_TTL_MS
546
+ );
547
+ await upsertIndexEntry({
548
+ state,
549
+ indexKey: CONVERSATION_BY_ACTIVITY_INDEX_KEY,
550
+ conversationId: next.conversationId,
551
+ score: next.lastActivityAtMs
552
+ });
553
+ if (!hasRunnableWork(next)) {
554
+ await removeIndexEntry({
555
+ state,
556
+ indexKey: CONVERSATION_ACTIVE_INDEX_KEY,
557
+ conversationId: next.conversationId
558
+ });
559
+ return;
560
+ }
561
+ await upsertIndexEntry({
562
+ state,
563
+ indexKey: CONVERSATION_ACTIVE_INDEX_KEY,
564
+ conversationId: next.conversationId,
565
+ score: next.execution.updatedAtMs ?? next.updatedAtMs
566
+ });
567
+ }
568
+ function assertSameConversationDestination(args) {
569
+ if (!args.current || sameDestination(args.current, args.next)) {
570
+ return;
571
+ }
572
+ throw new Error(
573
+ `Conversation destination changed for ${args.conversationId}`
574
+ );
575
+ }
576
+ function conversationWorkState(conversation) {
577
+ const lease = conversation.execution.lease;
578
+ return {
579
+ ...conversation,
580
+ lastEnqueuedAtMs: conversation.execution.lastEnqueuedAtMs,
581
+ ...lease ? {
582
+ lease: {
583
+ acquiredAtMs: lease.acquiredAtMs,
584
+ lastCheckInAtMs: lease.lastCheckInAtMs,
585
+ leaseExpiresAtMs: lease.expiresAtMs,
586
+ leaseToken: lease.token
587
+ }
588
+ } : {},
589
+ messages: pendingMessages(conversation),
590
+ needsRun: hasRunnableWork(conversation)
591
+ };
592
+ }
593
+ async function getConversation(args) {
594
+ const state = await getConnectedState(args.state);
595
+ return await readConversation(state, args.conversationId);
596
+ }
597
+ async function getConversationWorkState(args) {
598
+ const conversation = await getConversation(args);
599
+ return conversation ? conversationWorkState(conversation) : void 0;
600
+ }
601
+ async function appendInboundMessage(args) {
602
+ const nowMs = args.nowMs ?? now();
603
+ return await withConversationMutation(
604
+ { conversationId: args.message.conversationId, state: args.state },
605
+ async (state) => {
606
+ const current = await readConversation(state, args.message.conversationId) ?? emptyConversation({
607
+ conversationId: args.message.conversationId,
608
+ destination: args.message.destination,
609
+ nowMs,
610
+ source: args.message.source
611
+ });
612
+ assertSameConversationDestination({
613
+ conversationId: args.message.conversationId,
614
+ current: current.destination,
615
+ next: args.message.destination
616
+ });
617
+ const existingPending = current.execution.pendingMessages.some(
618
+ (message) => message.inboundMessageId === args.message.inboundMessageId
619
+ );
620
+ const existing = current.execution.inboundMessageIds.includes(
621
+ args.message.inboundMessageId
622
+ );
623
+ if (existing) {
624
+ if (!existingPending) {
625
+ return { status: "duplicate" };
626
+ }
627
+ const nextStatus = current.execution.status === "idle" ? "pending" : current.execution.status;
628
+ await writeConversation(
629
+ state,
630
+ withExecutionUpdate(
631
+ current,
632
+ {
633
+ ...current.execution,
634
+ status: nextStatus
635
+ },
636
+ nowMs
637
+ )
638
+ );
639
+ return { status: "duplicate" };
640
+ }
641
+ const status = current.execution.lease && current.execution.status === "running" ? "running" : current.execution.lease ? "awaiting_resume" : "pending";
642
+ const next = {
643
+ ...current,
644
+ destination: current.destination ?? args.message.destination,
645
+ source: current.source ?? args.message.source,
646
+ lastActivityAtMs: nowMs
647
+ };
648
+ await writeConversation(
649
+ state,
650
+ withExecutionUpdate(
651
+ next,
652
+ {
653
+ ...current.execution,
654
+ status,
655
+ inboundMessageIds: [
656
+ ...current.execution.inboundMessageIds,
657
+ args.message.inboundMessageId
658
+ ],
659
+ pendingMessages: [
660
+ ...current.execution.pendingMessages,
661
+ args.message
662
+ ].sort(compareMessages)
663
+ },
664
+ nowMs
665
+ )
666
+ );
667
+ return { status: "appended" };
668
+ }
669
+ );
670
+ }
671
+ async function requestConversationWork(args) {
672
+ const nowMs = args.nowMs ?? now();
673
+ return await withConversationMutation(args, async (state) => {
674
+ const existing = await readConversation(state, args.conversationId);
675
+ if (existing) {
676
+ assertSameConversationDestination({
677
+ conversationId: args.conversationId,
678
+ current: existing.destination,
679
+ next: args.destination
680
+ });
681
+ }
682
+ const current = existing ?? emptyConversation({
683
+ conversationId: args.conversationId,
684
+ destination: args.destination,
685
+ nowMs
686
+ });
687
+ const status = current.execution.lease ? "awaiting_resume" : "pending";
688
+ await writeConversation(
689
+ state,
690
+ withExecutionUpdate(
691
+ {
692
+ ...current,
693
+ destination: current.destination ?? args.destination
694
+ },
695
+ {
696
+ ...current.execution,
697
+ status
698
+ },
699
+ nowMs
700
+ )
701
+ );
702
+ return { status: existing === void 0 ? "created" : "updated" };
703
+ });
704
+ }
705
+ async function recordConversationActivity(args) {
706
+ const nowMs = args.nowMs ?? now();
707
+ const activityAtMs = args.activityAtMs ?? nowMs;
708
+ await withConversationMutation(args, async (state) => {
709
+ const existing = await readConversation(state, args.conversationId);
710
+ if (existing && args.destination) {
711
+ assertSameConversationDestination({
712
+ conversationId: args.conversationId,
713
+ current: existing.destination,
714
+ next: args.destination
715
+ });
716
+ }
717
+ const current = existing ?? emptyConversation({
718
+ conversationId: args.conversationId,
719
+ destination: args.destination,
720
+ nowMs,
721
+ source: args.source
722
+ });
723
+ await writeConversation(state, {
724
+ ...current,
725
+ ...current.destination ?? args.destination ? { destination: current.destination ?? args.destination } : {},
726
+ ...current.source ?? args.source ? { source: current.source ?? args.source } : {},
727
+ ...current.channelName ?? args.channelName ? { channelName: current.channelName ?? args.channelName } : {},
728
+ ...current.requester ?? args.requester ? { requester: current.requester ?? args.requester } : {},
729
+ ...current.title ?? args.title ? { title: current.title ?? args.title } : {},
730
+ lastActivityAtMs: Math.max(current.lastActivityAtMs, activityAtMs),
731
+ updatedAtMs: nowMs,
732
+ execution: executionWithPendingMessages(
733
+ current.execution,
734
+ current.execution.pendingMessages
735
+ )
736
+ });
737
+ });
738
+ }
739
+ async function markConversationWorkEnqueued(args) {
740
+ const nowMs = args.nowMs ?? now();
741
+ await withConversationMutation(args, async (state) => {
742
+ const current = await readConversation(state, args.conversationId);
743
+ if (!current) {
744
+ return;
745
+ }
746
+ await writeConversation(
747
+ state,
748
+ withExecutionUpdate(
749
+ current,
750
+ {
751
+ ...current.execution,
752
+ lastEnqueuedAtMs: nowMs
753
+ },
754
+ nowMs
755
+ )
756
+ );
757
+ });
758
+ }
759
+ async function startConversationWork(args) {
760
+ const nowMs = args.nowMs ?? now();
761
+ return await withConversationMutation(args, async (state) => {
762
+ const current = await readConversation(state, args.conversationId);
763
+ if (!current) {
764
+ return { status: "no_work" };
765
+ }
766
+ if (isLeaseActive(current.execution.lease, nowMs)) {
767
+ return {
768
+ status: "active",
769
+ leaseExpiresAtMs: current.execution.lease.expiresAtMs
770
+ };
771
+ }
772
+ if (!hasRunnableWork(current)) {
773
+ return { status: "no_work" };
774
+ }
775
+ const lease = {
776
+ token: randomUUID(),
777
+ acquiredAtMs: nowMs,
778
+ lastCheckInAtMs: nowMs,
779
+ expiresAtMs: nowMs + CONVERSATION_WORK_LEASE_TTL_MS
780
+ };
781
+ await writeConversation(
782
+ state,
783
+ withExecutionUpdate(
784
+ current,
785
+ {
786
+ ...current.execution,
787
+ lease,
788
+ status: "running",
789
+ runId: current.execution.runId ?? randomUUID(),
790
+ lastEnqueuedAtMs: void 0
791
+ },
792
+ nowMs
793
+ )
794
+ );
795
+ return {
796
+ status: "acquired",
797
+ leaseToken: lease.token,
798
+ leaseExpiresAtMs: lease.expiresAtMs
799
+ };
800
+ });
801
+ }
802
+ async function checkInConversationWork(args) {
803
+ const nowMs = args.nowMs ?? now();
804
+ return await withConversationMutation(args, async (state) => {
805
+ const current = await readConversation(state, args.conversationId);
806
+ if (!current || current.execution.lease?.token !== args.leaseToken) {
807
+ return false;
808
+ }
809
+ await writeConversation(
810
+ state,
811
+ withExecutionUpdate(
812
+ current,
813
+ {
814
+ ...current.execution,
815
+ lease: {
816
+ ...current.execution.lease,
817
+ lastCheckInAtMs: nowMs,
818
+ expiresAtMs: nowMs + CONVERSATION_WORK_LEASE_TTL_MS
819
+ }
820
+ },
821
+ nowMs
822
+ )
823
+ );
824
+ return true;
825
+ });
826
+ }
827
+ async function drainConversationMailbox(args) {
828
+ const nowMs = args.nowMs ?? now();
829
+ const pending = await withConversationMutation(args, async (state) => {
830
+ const current = await readConversation(state, args.conversationId);
831
+ if (!current || current.execution.lease?.token !== args.leaseToken) {
832
+ throw new Error(
833
+ `Conversation lease is not held for ${args.conversationId}`
834
+ );
835
+ }
836
+ return pendingMessages(current);
837
+ });
838
+ if (pending.length === 0) {
839
+ return [];
840
+ }
841
+ const acknowledgedIds = await args.inject(pending);
842
+ const offeredIds = new Set(
843
+ pending.map((message) => message.inboundMessageId)
844
+ );
845
+ for (const inboundMessageId of acknowledgedIds ?? []) {
846
+ if (!offeredIds.has(inboundMessageId)) {
847
+ throw new Error(
848
+ `Conversation mailbox acknowledgement is not pending for ${args.conversationId}`
849
+ );
850
+ }
851
+ }
852
+ const drainedIds = new Set(
853
+ acknowledgedIds ?? pending.map((message) => message.inboundMessageId)
854
+ );
855
+ await withConversationMutation(args, async (state) => {
856
+ const current = await readConversation(state, args.conversationId);
857
+ if (!current || current.execution.lease?.token !== args.leaseToken) {
858
+ throw new Error(
859
+ `Conversation lease is not held for ${args.conversationId}`
860
+ );
861
+ }
862
+ const pendingMessages2 = current.execution.pendingMessages.filter(
863
+ (message) => !drainedIds.has(message.inboundMessageId)
864
+ );
865
+ await writeConversation(
866
+ state,
867
+ withExecutionUpdate(
868
+ current,
869
+ {
870
+ ...current.execution,
871
+ status: current.execution.status === "pending" && pendingMessages2.length === 0 ? "running" : current.execution.status,
872
+ pendingMessages: pendingMessages2
873
+ },
874
+ nowMs
875
+ )
876
+ );
877
+ });
878
+ return pending.filter((message) => drainedIds.has(message.inboundMessageId));
879
+ }
880
+ async function markConversationMessagesInjected(args) {
881
+ const nowMs = args.nowMs ?? now();
882
+ const inboundMessageIds = new Set(args.inboundMessageIds);
883
+ return await withConversationMutation(args, async (state) => {
884
+ const current = await readConversation(state, args.conversationId);
885
+ if (!current || current.execution.lease?.token !== args.leaseToken) {
886
+ return false;
887
+ }
888
+ if (inboundMessageIds.size === 0) {
889
+ return true;
890
+ }
891
+ const pendingMessages2 = current.execution.pendingMessages.filter(
892
+ (message) => !inboundMessageIds.has(message.inboundMessageId)
893
+ );
894
+ if (pendingMessages2.length === current.execution.pendingMessages.length) {
895
+ return true;
896
+ }
897
+ await writeConversation(
898
+ state,
899
+ withExecutionUpdate(
900
+ current,
901
+ {
902
+ ...current.execution,
903
+ pendingMessages: pendingMessages2
904
+ },
905
+ nowMs
906
+ )
907
+ );
908
+ return true;
909
+ });
910
+ }
911
+ async function requestConversationContinuation(args) {
912
+ const nowMs = args.nowMs ?? now();
913
+ return await withConversationMutation(args, async (state) => {
914
+ const current = await readConversation(state, args.conversationId);
915
+ if (!current || current.execution.lease?.token !== args.leaseToken) {
916
+ return false;
917
+ }
918
+ assertSameConversationDestination({
919
+ conversationId: args.conversationId,
920
+ current: current.destination,
921
+ next: args.destination
922
+ });
923
+ await writeConversation(
924
+ state,
925
+ withExecutionUpdate(
926
+ current,
927
+ {
928
+ ...current.execution,
929
+ status: "awaiting_resume"
930
+ },
931
+ nowMs
932
+ )
933
+ );
934
+ return true;
935
+ });
936
+ }
937
+ async function releaseConversationWork(args) {
938
+ const nowMs = args.nowMs ?? now();
939
+ return await withConversationMutation(args, async (state) => {
940
+ const current = await readConversation(state, args.conversationId);
941
+ if (!current || current.execution.lease?.token !== args.leaseToken) {
942
+ return false;
943
+ }
944
+ await writeConversation(
945
+ state,
946
+ withExecutionUpdate(
947
+ current,
948
+ {
949
+ ...current.execution,
950
+ lease: void 0,
951
+ status: current.execution.status === "running" ? "pending" : current.execution.status
952
+ },
953
+ nowMs
954
+ )
955
+ );
956
+ return true;
957
+ });
958
+ }
959
+ async function completeConversationWork(args) {
960
+ const nowMs = args.nowMs ?? now();
961
+ return await withConversationMutation(args, async (state) => {
962
+ const current = await readConversation(state, args.conversationId);
963
+ if (!current || current.execution.lease?.token !== args.leaseToken) {
964
+ return "lost_lease";
965
+ }
966
+ const hasPending = pendingMessages(current).length > 0;
967
+ const needsRun = current.execution.status === "awaiting_resume";
968
+ const runnable = needsRun || hasPending;
969
+ await writeConversation(
970
+ state,
971
+ withExecutionUpdate(
972
+ current,
973
+ {
974
+ ...current.execution,
975
+ lease: void 0,
976
+ status: runnable ? "pending" : "idle",
977
+ runId: runnable ? current.execution.runId : void 0
978
+ },
979
+ nowMs
980
+ )
981
+ );
982
+ return runnable ? "pending" : "completed";
983
+ });
984
+ }
985
+ async function clearExpiredConversationLease(args) {
986
+ const nowMs = args.nowMs ?? now();
987
+ return await withConversationMutation(args, async (state) => {
988
+ const current = await readConversation(state, args.conversationId);
989
+ if (!current?.execution.lease || current.execution.lease.expiresAtMs > nowMs) {
990
+ return false;
991
+ }
992
+ await writeConversation(
993
+ state,
994
+ withExecutionUpdate(
995
+ current,
996
+ {
997
+ ...current.execution,
998
+ lease: void 0,
999
+ status: "pending"
1000
+ },
1001
+ nowMs
1002
+ )
1003
+ );
1004
+ return true;
1005
+ });
1006
+ }
1007
+ async function removeActiveConversation(args) {
1008
+ const state = await getConnectedState(args.state);
1009
+ await removeIndexEntry({
1010
+ state,
1011
+ indexKey: CONVERSATION_ACTIVE_INDEX_KEY,
1012
+ conversationId: args.conversationId
1013
+ });
1014
+ }
1015
+ async function listActiveConversationIds(args = {}) {
1016
+ const state = await getConnectedState(args.state);
1017
+ const index = await getConversationIndexStore(state);
1018
+ const entries = await index.list({
1019
+ indexKey: CONVERSATION_ACTIVE_INDEX_KEY,
1020
+ limit: args.limit,
1021
+ order: "asc",
1022
+ scoreMax: args.staleBeforeMs
1023
+ });
1024
+ return entries.map((entry) => entry.conversationId);
1025
+ }
1026
+ async function listConversationsByActivity(args = {}) {
1027
+ const state = await getConnectedState(args.state);
1028
+ const index = await getConversationIndexStore(state);
1029
+ const entries = await index.list({
1030
+ indexKey: CONVERSATION_BY_ACTIVITY_INDEX_KEY,
1031
+ limit: args.limit ?? CONVERSATION_ACTIVITY_INDEX_MAX_LENGTH,
1032
+ offset: args.offset,
1033
+ order: "desc"
1034
+ });
1035
+ const conversations = [];
1036
+ for (const entry of entries) {
1037
+ try {
1038
+ const conversation = await readConversation(state, entry.conversationId);
1039
+ if (conversation) {
1040
+ conversations.push(conversation);
1041
+ }
1042
+ } catch (error) {
1043
+ if (!(error instanceof InvalidConversationRecordError)) {
1044
+ throw error;
1045
+ }
1046
+ await removeIndexEntry({
1047
+ state,
1048
+ indexKey: CONVERSATION_BY_ACTIVITY_INDEX_KEY,
1049
+ conversationId: entry.conversationId
1050
+ });
1051
+ }
1052
+ }
1053
+ return conversations;
1054
+ }
1055
+
1056
+ export {
1057
+ CONVERSATION_WORK_CHECK_IN_INTERVAL_MS,
1058
+ CONVERSATION_WORK_STALE_ENQUEUE_MS,
1059
+ getConversation,
1060
+ getConversationWorkState,
1061
+ appendInboundMessage,
1062
+ requestConversationWork,
1063
+ recordConversationActivity,
1064
+ markConversationWorkEnqueued,
1065
+ startConversationWork,
1066
+ checkInConversationWork,
1067
+ drainConversationMailbox,
1068
+ markConversationMessagesInjected,
1069
+ requestConversationContinuation,
1070
+ releaseConversationWork,
1071
+ completeConversationWork,
1072
+ clearExpiredConversationLease,
1073
+ removeActiveConversation,
1074
+ listActiveConversationIds,
1075
+ listConversationsByActivity
1076
+ };