@sentry/junior 0.93.0 → 0.95.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/dist/{agent-hooks-ZG7IDOIN.js → agent-hooks-FTQ5EGPO.js} +9 -10
  2. package/dist/api/people/list.js +7 -7
  3. package/dist/api/people/profile.js +7 -7
  4. package/dist/api/people/shared.d.ts +2 -2
  5. package/dist/api-reference.d.ts +1 -1
  6. package/dist/app.js +474 -231
  7. package/dist/{catalog-runtime-NZHMSMYC.js → catalog-runtime-2D7IVKUT.js} +3 -3
  8. package/dist/chat/advisor-request.d.ts +4 -0
  9. package/dist/chat/agent/request.d.ts +2 -0
  10. package/dist/chat/agent/resume.d.ts +2 -0
  11. package/dist/chat/conversations/history.d.ts +128 -0
  12. package/dist/chat/conversations/legacy-import.d.ts +38 -0
  13. package/dist/chat/conversations/messages.d.ts +39 -0
  14. package/dist/chat/conversations/projection.d.ts +94 -0
  15. package/dist/chat/conversations/retention.d.ts +36 -0
  16. package/dist/chat/conversations/sql/conversation-row.d.ts +14 -0
  17. package/dist/chat/conversations/sql/history.d.ts +4 -0
  18. package/dist/chat/conversations/sql/legacy-history-import.d.ts +55 -0
  19. package/dist/chat/conversations/sql/messages.d.ts +4 -0
  20. package/dist/chat/conversations/sql/migrations.d.ts +14 -3
  21. package/dist/chat/conversations/sql/purge.d.ts +53 -0
  22. package/dist/chat/conversations/sql/store.d.ts +7 -2
  23. package/dist/chat/conversations/store.d.ts +19 -0
  24. package/dist/chat/conversations/visible-compactions.d.ts +11 -0
  25. package/dist/chat/conversations/visible-messages.d.ts +47 -0
  26. package/dist/chat/db.d.ts +12 -1
  27. package/dist/chat/egress/credentialed.d.ts +1 -1
  28. package/dist/chat/identities/sql.d.ts +1 -1
  29. package/dist/chat/logging.d.ts +2 -1
  30. package/dist/chat/resource-events/store.d.ts +2 -1
  31. package/dist/chat/runtime/reply-executor.d.ts +8 -0
  32. package/dist/chat/runtime/thread-state.d.ts +2 -0
  33. package/dist/chat/sandbox/egress/schemas.d.ts +4 -0
  34. package/dist/chat/sandbox/egress/session.d.ts +2 -2
  35. package/dist/chat/sandbox/resources.d.ts +4 -0
  36. package/dist/chat/services/mcp-auth-orchestration.d.ts +8 -0
  37. package/dist/chat/services/plugin-auth-orchestration.d.ts +12 -0
  38. package/dist/chat/services/turn-session-record.d.ts +14 -2
  39. package/dist/chat/sleep.d.ts +2 -0
  40. package/dist/chat/state/conversation.d.ts +7 -4
  41. package/dist/chat/state/session-log.d.ts +23 -1
  42. package/dist/chat/state/turn-session.d.ts +6 -0
  43. package/dist/chat/tool-support/private-trace-result.d.ts +4 -0
  44. package/dist/chat/tool-support/zod-tool.d.ts +1 -0
  45. package/dist/chat/tools/advisor/tool.d.ts +9 -4
  46. package/dist/chat/tools/definition.d.ts +2 -0
  47. package/dist/chat/tools/execution/tool-error-handler.d.ts +2 -2
  48. package/dist/chat/tools/system-time.d.ts +8 -0
  49. package/dist/chat/xml.d.ts +2 -0
  50. package/dist/{chunk-NYWPISSO.js → chunk-2XXEKR44.js} +1 -1
  51. package/dist/{chunk-QGEWC27H.js → chunk-5XMNOD3S.js} +5 -7
  52. package/dist/{chunk-NFXSLTYG.js → chunk-6ONME5IG.js} +864 -62
  53. package/dist/{chunk-LFLTJQVR.js → chunk-7FBGKXPE.js} +4 -0
  54. package/dist/{chunk-MC4ZZE4Q.js → chunk-AYP4ERVZ.js} +1 -1
  55. package/dist/{chunk-GHGPTPBL.js → chunk-B7PUBQQM.js} +1 -1
  56. package/dist/chunk-CM5EZD5F.js +17 -0
  57. package/dist/chunk-CRRBJ3JP.js +1037 -0
  58. package/dist/{chunk-G3E7SCME.js → chunk-CZEX6KIQ.js} +23 -0
  59. package/dist/{chunk-TNUUATT7.js → chunk-E3E2XNCN.js} +2 -2
  60. package/dist/{chunk-LNCREA5Z.js → chunk-EKNJM5IG.js} +3 -127
  61. package/dist/{chunk-NAPITCZ6.js → chunk-GUE7HTNR.js} +251 -6
  62. package/dist/{chunk-BTH37NCU.js → chunk-GVOUCI2O.js} +1 -1
  63. package/dist/{chunk-YYBX2BD5.js → chunk-GY34VGP5.js} +1 -1
  64. package/dist/{chunk-JJUJ4CO2.js → chunk-HCXXB35T.js} +1489 -360
  65. package/dist/{chunk-YTNPU7I2.js → chunk-HDTO3A74.js} +2 -2
  66. package/dist/{chunk-YJ4OPVNA.js → chunk-HZNJLKKI.js} +1 -1
  67. package/dist/{chunk-HDNWZMUN.js → chunk-KDPCGVIT.js} +116 -51
  68. package/dist/chunk-N4RNJ32C.js +48 -0
  69. package/dist/{chunk-UUB3D2XI.js → chunk-NSCHU54X.js} +8 -10
  70. package/dist/{chunk-PFXC67GJ.js → chunk-RAIB6WSG.js} +6 -80
  71. package/dist/chunk-WAGQ377B.js +724 -0
  72. package/dist/{chunk-6H4PP63X.js → chunk-WCMQJE4F.js} +24 -14
  73. package/dist/{chunk-S6QKIGRM.js → chunk-WPOJ443W.js} +15 -25
  74. package/dist/{chunk-MAVOJ54A.js → chunk-XX4N4E6C.js} +25 -388
  75. package/dist/cli/chat.js +24 -23
  76. package/dist/cli/check.js +4 -4
  77. package/dist/cli/plugins.js +11 -12
  78. package/dist/cli/snapshot-warmup.js +7 -8
  79. package/dist/cli/upgrade.js +78 -17
  80. package/dist/db/schema/agent-steps.d.ts +134 -0
  81. package/dist/db/schema/conversation-messages.d.ts +153 -0
  82. package/dist/{chat/conversations/sql → db}/schema/conversations.d.ts +34 -0
  83. package/dist/{chat/conversations/sql → db}/schema.d.ts +319 -8
  84. package/dist/db-W55B74OP.js +24 -0
  85. package/dist/deployment.d.ts +2 -0
  86. package/dist/handlers/retention.d.ts +6 -0
  87. package/dist/instrumentation.js +16 -3
  88. package/dist/legacy-import-ZPLGLAFD.js +17 -0
  89. package/dist/nitro.js +14 -4
  90. package/dist/reporting/conversations/activity.d.ts +40 -0
  91. package/dist/reporting/conversations/context.d.ts +8 -0
  92. package/dist/reporting/conversations/details.d.ts +6 -0
  93. package/dist/reporting/conversations/index.d.ts +4 -0
  94. package/dist/reporting/conversations/shared.d.ts +9 -0
  95. package/dist/reporting/conversations/statistics.d.ts +4 -0
  96. package/dist/reporting/conversations/summaries.d.ts +17 -0
  97. package/dist/reporting/conversations/transcript.d.ts +28 -0
  98. package/dist/reporting/conversations/types.d.ts +178 -0
  99. package/dist/reporting/conversations.d.ts +1 -178
  100. package/dist/reporting.d.ts +4 -3
  101. package/dist/reporting.js +603 -827
  102. package/dist/{runner-XAZPJNAQ.js → runner-OOSBDYRK.js} +35 -41
  103. package/dist/{validation-KYAWETI4.js → validation-QCGRFRFZ.js} +4 -4
  104. package/migrations/README.md +32 -0
  105. package/migrations/meta/0000_snapshot.json +978 -0
  106. package/migrations/meta/_journal.json +13 -0
  107. package/package.json +9 -6
  108. package/dist/chat/sql/schema.d.ts +0 -1049
  109. package/dist/chat/state/conversation-details.d.ts +0 -47
  110. package/dist/chunk-7STN2ZSK.js +0 -250
  111. package/dist/chunk-LE5ANBUZ.js +0 -1040
  112. package/dist/chunk-QL66APYJ.js +0 -1377
  113. package/dist/db-YDN6AFJH.js +0 -18
  114. /package/dist/{chat/sql → db}/db.d.ts +0 -0
  115. /package/dist/{chat/sql → db}/executor.d.ts +0 -0
  116. /package/dist/{chat/sql → db}/neon.d.ts +0 -0
  117. /package/dist/{chat/sql → db}/postgres.d.ts +0 -0
  118. /package/dist/{chat/conversations/sql → db}/schema/destinations.d.ts +0 -0
  119. /package/dist/{chat/conversations/sql → db}/schema/identities.d.ts +0 -0
  120. /package/dist/{chat/conversations/sql → db}/schema/migrations.d.ts +0 -0
  121. /package/dist/{chat/conversations/sql → db}/schema/timestamps.d.ts +0 -0
  122. /package/dist/{chat/conversations/sql → db}/schema/users.d.ts +0 -0
@@ -1,3 +1,25 @@
1
+ // src/chat/sleep.ts
2
+ function sleep(ms, signal) {
3
+ if (!signal) {
4
+ return new Promise((resolve) => setTimeout(resolve, ms));
5
+ }
6
+ return new Promise((resolve, reject) => {
7
+ if (signal.aborted) {
8
+ reject(signal.reason);
9
+ return;
10
+ }
11
+ const timeoutId = setTimeout(() => {
12
+ signal.removeEventListener("abort", onAbort);
13
+ resolve();
14
+ }, ms);
15
+ const onAbort = () => {
16
+ clearTimeout(timeoutId);
17
+ reject(signal.reason);
18
+ };
19
+ signal.addEventListener("abort", onAbort, { once: true });
20
+ });
21
+ }
22
+
1
23
  // src/chat/sandbox/paths.ts
2
24
  function normalizeWorkspaceRoot(input) {
3
25
  const candidate = (input ?? "").trim();
@@ -20,6 +42,7 @@ function sandboxSkillFile(skillName) {
20
42
  }
21
43
 
22
44
  export {
45
+ sleep,
23
46
  SANDBOX_WORKSPACE_ROOT,
24
47
  SANDBOX_SKILLS_ROOT,
25
48
  SANDBOX_DATA_ROOT,
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  getStateAdapter
3
- } from "./chunk-7STN2ZSK.js";
3
+ } from "./chunk-GUE7HTNR.js";
4
4
  import {
5
5
  logException,
6
6
  logInfo,
7
7
  logWarn
8
- } from "./chunk-S6QKIGRM.js";
8
+ } from "./chunk-WPOJ443W.js";
9
9
 
10
10
  // src/chat/plugins/logging.ts
11
11
  function createPluginLogger(plugin) {
@@ -1,19 +1,9 @@
1
1
  import {
2
2
  normalizeSlackConversationId
3
- } from "./chunk-UUB3D2XI.js";
4
- import {
5
- getStateAdapter
6
- } from "./chunk-7STN2ZSK.js";
3
+ } from "./chunk-NSCHU54X.js";
7
4
  import {
8
5
  parseSlackThreadId
9
- } from "./chunk-YYBX2BD5.js";
10
- import {
11
- parseStoredSlackActor
12
- } from "./chunk-S6QKIGRM.js";
13
- import {
14
- isRecord,
15
- toOptionalNumber
16
- } from "./chunk-OB42YVAE.js";
6
+ } from "./chunk-GY34VGP5.js";
17
7
  import {
18
8
  getClient
19
9
  } from "./chunk-3DA7X2U3.js";
@@ -164,116 +154,6 @@ function formatSlackConversationRedactedLabel(context) {
164
154
  return formatSlackConversationTypeLabel(context.type);
165
155
  }
166
156
 
167
- // src/chat/state/conversation-details.ts
168
- import { THREAD_STATE_TTL_MS } from "chat";
169
- var CONVERSATION_PREFIX = "junior:conversation";
170
- var CONVERSATION_DETAILS_TTL_MS = THREAD_STATE_TTL_MS;
171
- function conversationContextKey(conversationId) {
172
- return `${CONVERSATION_PREFIX}:${conversationId}:context`;
173
- }
174
- function conversationTitleKey(conversationId) {
175
- return `${CONVERSATION_PREFIX}:${conversationId}:title`;
176
- }
177
- function parseOriginActor(value) {
178
- return parseStoredSlackActor(value);
179
- }
180
- function parseOriginSurface(value) {
181
- if (value === "slack" || value === "api" || value === "scheduler" || value === "internal") {
182
- return value;
183
- }
184
- return void 0;
185
- }
186
- function storedContextFromInput(context) {
187
- return {
188
- ...context.channelName ? { channelName: context.channelName } : {},
189
- ...context.originSurface ? { originSurface: context.originSurface } : {},
190
- ...context.originActor ? { originActor: context.originActor } : {},
191
- startedAtMs: context.startedAtMs
192
- };
193
- }
194
- function parseContext(value) {
195
- if (!isRecord(value)) return void 0;
196
- const startedAtMs = toOptionalNumber(value.startedAtMs);
197
- if (startedAtMs === void 0) return void 0;
198
- return {
199
- ...typeof value.channelName === "string" && value.channelName.trim() ? { channelName: value.channelName.trim() } : {},
200
- ...parseOriginSurface(value.originSurface) ? { originSurface: parseOriginSurface(value.originSurface) } : {},
201
- ...parseOriginActor(value.originActor) ? { originActor: parseOriginActor(value.originActor) } : {},
202
- startedAtMs
203
- };
204
- }
205
- function parseTitle(value) {
206
- if (!isRecord(value)) return void 0;
207
- const displayTitle = typeof value.displayTitle === "string" && value.displayTitle.trim() ? value.displayTitle.trim() : void 0;
208
- if (!displayTitle) return void 0;
209
- return {
210
- displayTitle,
211
- ...typeof value.titleSourceMessageId === "string" ? { titleSourceMessageId: value.titleSourceMessageId } : {}
212
- };
213
- }
214
- async function initConversationContext(conversationId, context) {
215
- const stateAdapter = getStateAdapter();
216
- await stateAdapter.connect();
217
- const key = conversationContextKey(conversationId);
218
- const inserted = await stateAdapter.setIfNotExists(
219
- key,
220
- storedContextFromInput(context),
221
- CONVERSATION_DETAILS_TTL_MS
222
- );
223
- if (inserted) return;
224
- const existing = parseContext(await stateAdapter.get(key));
225
- if (!existing) {
226
- return;
227
- }
228
- await stateAdapter.set(key, existing, CONVERSATION_DETAILS_TTL_MS);
229
- }
230
- async function setConversationTitle(conversationId, title) {
231
- const stateAdapter = getStateAdapter();
232
- await stateAdapter.connect();
233
- await stateAdapter.set(
234
- conversationTitleKey(conversationId),
235
- {
236
- displayTitle: title.displayTitle,
237
- ...title.titleSourceMessageId ? { titleSourceMessageId: title.titleSourceMessageId } : {}
238
- },
239
- CONVERSATION_DETAILS_TTL_MS
240
- );
241
- }
242
- async function getConversationDetails(conversationId) {
243
- const stateAdapter = getStateAdapter();
244
- await stateAdapter.connect();
245
- const [rawContext, rawTitle] = await Promise.all([
246
- stateAdapter.get(conversationContextKey(conversationId)),
247
- stateAdapter.get(conversationTitleKey(conversationId))
248
- ]);
249
- const context = parseContext(rawContext);
250
- const title = parseTitle(rawTitle);
251
- if (!context && !title) return void 0;
252
- return {
253
- conversationId,
254
- ...title?.displayTitle ? { displayTitle: title.displayTitle } : {},
255
- ...title?.titleSourceMessageId ? { titleSourceMessageId: title.titleSourceMessageId } : {},
256
- ...context?.channelName ? { channelName: context.channelName } : {},
257
- ...context?.originSurface ? { originSurface: context.originSurface } : {},
258
- ...context?.originActor ? { originActor: context.originActor } : {},
259
- ...context?.startedAtMs !== void 0 ? { startedAtMs: context.startedAtMs } : {}
260
- };
261
- }
262
- async function getConversationDetailsForIds(conversationIds) {
263
- const uniqueIds = [...new Set(conversationIds)].filter(Boolean);
264
- const entries = await Promise.all(
265
- uniqueIds.map(async (id) => {
266
- const details = await getConversationDetails(id);
267
- return details ? [id, details] : void 0;
268
- })
269
- );
270
- const result = /* @__PURE__ */ new Map();
271
- for (const entry of entries) {
272
- if (entry) result.set(entry[0], entry[1]);
273
- }
274
- return result;
275
- }
276
-
277
157
  export {
278
158
  GET,
279
159
  buildSentryConversationUrl,
@@ -282,9 +162,5 @@ export {
282
162
  resolveSlackChannelTypeFromMessage,
283
163
  resolveSlackConversationContext,
284
164
  resolveSlackConversationContextFromThreadId,
285
- formatSlackConversationRedactedLabel,
286
- initConversationContext,
287
- setConversationTitle,
288
- getConversationDetails,
289
- getConversationDetailsForIds
165
+ formatSlackConversationRedactedLabel
290
166
  };
@@ -6,7 +6,7 @@ import {
6
6
  toGenAiMessageMetadata,
7
7
  toGenAiMessagesTraceAttributes,
8
8
  toGenAiTextMetadata
9
- } from "./chunk-YYBX2BD5.js";
9
+ } from "./chunk-GY34VGP5.js";
10
10
  import {
11
11
  extractGenAiUsageAttributes,
12
12
  logException,
@@ -14,12 +14,15 @@ import {
14
14
  serializeGenAiAttribute,
15
15
  setSpanAttributes,
16
16
  withSpan
17
- } from "./chunk-S6QKIGRM.js";
17
+ } from "./chunk-WPOJ443W.js";
18
18
 
19
19
  // src/chat/xml.ts
20
20
  function escapeXml(value) {
21
21
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&apos;");
22
22
  }
23
+ function unescapeXml(value) {
24
+ return value.replaceAll("&quot;", '"').replaceAll("&apos;", "'").replaceAll("&gt;", ">").replaceAll("&lt;", "<").replaceAll("&amp;", "&");
25
+ }
23
26
 
24
27
  // src/chat/current-instruction.ts
25
28
  var CURRENT_INSTRUCTION_TAG = "current-instruction";
@@ -28,9 +31,6 @@ var CURRENT_INSTRUCTION_OPEN_BARE = `<${CURRENT_INSTRUCTION_TAG}>`;
28
31
  var CURRENT_INSTRUCTION_OPEN_ATTR_PREFIX = `<${CURRENT_INSTRUCTION_TAG} `;
29
32
  var CURRENT_INSTRUCTION_CLOSE = `
30
33
  </${CURRENT_INSTRUCTION_TAG}>`;
31
- function unescapeXml(value) {
32
- return value.replaceAll("&quot;", '"').replaceAll("&apos;", "'").replaceAll("&gt;", ">").replaceAll("&lt;", "<").replaceAll("&amp;", "&");
33
- }
34
34
  function isCurrentInstructionOpeningTag(value) {
35
35
  return value === CURRENT_INSTRUCTION_OPEN_BARE || value.startsWith(CURRENT_INSTRUCTION_OPEN_ATTR_PREFIX) && value.endsWith(">");
36
36
  }
@@ -795,9 +795,249 @@ function setSlackReactionConfig(overrides) {
795
795
  }
796
796
  }
797
797
 
798
+ // src/chat/state/adapter.ts
799
+ import { createMemoryState } from "@chat-adapter/state-memory";
800
+ import { createRedisState } from "@chat-adapter/state-redis";
801
+
802
+ // src/chat/state/locks.ts
803
+ var ACTIVE_LOCK_TTL_MS = 9e4;
804
+ async function acquireActiveLock(state, threadId) {
805
+ return await state.acquireLock(threadId, ACTIVE_LOCK_TTL_MS);
806
+ }
807
+
808
+ // src/chat/state/adapter.ts
809
+ var ACTIVE_LOCK_HEARTBEAT_MS = 3e4;
810
+ var stateAdapter;
811
+ var redisStateAdapter;
812
+ function createPrefixedStateAdapter(base, prefix) {
813
+ const prefixed = (value) => `${prefix}:${value}`;
814
+ const unprefixed = (value) => value.startsWith(`${prefix}:`) ? value.slice(prefix.length + 1) : value;
815
+ const prefixLock = (lock) => ({
816
+ ...lock,
817
+ threadId: prefixed(lock.threadId)
818
+ });
819
+ const unprefixLock = (lock) => ({
820
+ ...lock,
821
+ threadId: unprefixed(lock.threadId)
822
+ });
823
+ return {
824
+ appendToList: (key, value, options) => base.appendToList(prefixed(key), value, options),
825
+ connect: () => base.connect(),
826
+ disconnect: () => base.disconnect(),
827
+ subscribe: (threadId) => base.subscribe(prefixed(threadId)),
828
+ unsubscribe: (threadId) => base.unsubscribe(prefixed(threadId)),
829
+ isSubscribed: (threadId) => base.isSubscribed(prefixed(threadId)),
830
+ acquireLock: async (threadId, ttlMs) => {
831
+ const lock = await base.acquireLock(prefixed(threadId), ttlMs);
832
+ return lock ? unprefixLock(lock) : null;
833
+ },
834
+ releaseLock: (lock) => base.releaseLock(prefixLock(lock)),
835
+ extendLock: async (lock, ttlMs) => {
836
+ const prefixedLock = prefixLock(lock);
837
+ const extended = await base.extendLock(prefixedLock, ttlMs);
838
+ if (extended) {
839
+ lock.expiresAt = prefixedLock.expiresAt;
840
+ }
841
+ return extended;
842
+ },
843
+ forceReleaseLock: (threadId) => base.forceReleaseLock(prefixed(threadId)),
844
+ enqueue: (threadId, entry, maxSize) => base.enqueue(prefixed(threadId), entry, maxSize),
845
+ dequeue: (threadId) => base.dequeue(prefixed(threadId)),
846
+ queueDepth: (threadId) => base.queueDepth(prefixed(threadId)),
847
+ get: (key) => base.get(prefixed(key)),
848
+ getList: (key) => base.getList(prefixed(key)),
849
+ set: (key, value, ttlMs) => base.set(prefixed(key), value, ttlMs),
850
+ setIfNotExists: (key, value, ttlMs) => base.setIfNotExists(prefixed(key), value, ttlMs),
851
+ delete: (key) => base.delete(prefixed(key))
852
+ };
853
+ }
854
+ function createQueuedStateAdapter(base, options) {
855
+ const heartbeats = /* @__PURE__ */ new Map();
856
+ const shouldHeartbeatLock = (ttlMs) => ttlMs === ACTIVE_LOCK_TTL_MS;
857
+ const heartbeatKey = (lock) => `${lock.threadId}:${lock.token}`;
858
+ const stopHeartbeatByKey = (key) => {
859
+ const heartbeat = heartbeats.get(key);
860
+ if (!heartbeat) {
861
+ return;
862
+ }
863
+ clearInterval(heartbeat.timer);
864
+ heartbeats.delete(key);
865
+ };
866
+ const stopHeartbeat = (lock) => {
867
+ stopHeartbeatByKey(heartbeatKey(lock));
868
+ };
869
+ const stopHeartbeatsForThread = (threadId) => {
870
+ for (const [key, heartbeat] of heartbeats) {
871
+ if (heartbeat.lock.threadId === threadId) {
872
+ stopHeartbeatByKey(key);
873
+ }
874
+ }
875
+ };
876
+ const stopAllHeartbeats = () => {
877
+ for (const key of heartbeats.keys()) {
878
+ stopHeartbeatByKey(key);
879
+ }
880
+ };
881
+ const runHeartbeat = async (key) => {
882
+ const heartbeat = heartbeats.get(key);
883
+ if (!heartbeat || heartbeat.inFlight) {
884
+ return;
885
+ }
886
+ heartbeat.inFlight = true;
887
+ try {
888
+ if (Date.now() - heartbeat.startedAtMs >= options.activeLockMaxAgeMs) {
889
+ stopHeartbeatByKey(key);
890
+ return;
891
+ }
892
+ const extended = await base.extendLock(heartbeat.lock, heartbeat.ttlMs);
893
+ if (!extended) {
894
+ stopHeartbeatByKey(key);
895
+ return;
896
+ }
897
+ heartbeat.lock.expiresAt = Date.now() + heartbeat.ttlMs;
898
+ } catch {
899
+ } finally {
900
+ const current = heartbeats.get(key);
901
+ if (current === heartbeat) {
902
+ current.inFlight = false;
903
+ }
904
+ }
905
+ };
906
+ const startOrUpdateHeartbeat = (lock, ttlMs) => {
907
+ const key = heartbeatKey(lock);
908
+ const existing = heartbeats.get(key);
909
+ if (existing) {
910
+ existing.ttlMs = ttlMs;
911
+ return;
912
+ }
913
+ const timer = setInterval(() => {
914
+ void runHeartbeat(key);
915
+ }, ACTIVE_LOCK_HEARTBEAT_MS);
916
+ timer.unref?.();
917
+ heartbeats.set(key, {
918
+ inFlight: false,
919
+ lock,
920
+ startedAtMs: Date.now(),
921
+ timer,
922
+ ttlMs
923
+ });
924
+ };
925
+ const acquireLock = async (threadId, ttlMs) => {
926
+ const lock = await base.acquireLock(threadId, ttlMs);
927
+ if (lock && shouldHeartbeatLock(ttlMs)) {
928
+ startOrUpdateHeartbeat(lock, ttlMs);
929
+ }
930
+ return lock;
931
+ };
932
+ return {
933
+ appendToList: (key, value, options2) => base.appendToList(key, value, options2),
934
+ connect: () => base.connect(),
935
+ disconnect: async () => {
936
+ stopAllHeartbeats();
937
+ await base.disconnect();
938
+ },
939
+ subscribe: (threadId) => base.subscribe(threadId),
940
+ unsubscribe: (threadId) => base.unsubscribe(threadId),
941
+ isSubscribed: (threadId) => base.isSubscribed(threadId),
942
+ acquireLock,
943
+ releaseLock: async (lock) => {
944
+ stopHeartbeat(lock);
945
+ await base.releaseLock(lock);
946
+ },
947
+ extendLock: async (lock, ttlMs) => {
948
+ const extended = await base.extendLock(lock, ttlMs);
949
+ if (extended) {
950
+ lock.expiresAt = Date.now() + ttlMs;
951
+ if (shouldHeartbeatLock(ttlMs)) {
952
+ startOrUpdateHeartbeat(lock, ttlMs);
953
+ } else {
954
+ stopHeartbeat(lock);
955
+ }
956
+ } else {
957
+ stopHeartbeat(lock);
958
+ }
959
+ return extended;
960
+ },
961
+ forceReleaseLock: async (threadId) => {
962
+ stopHeartbeatsForThread(threadId);
963
+ await base.forceReleaseLock(threadId);
964
+ },
965
+ enqueue: (threadId, entry, maxSize) => base.enqueue(threadId, entry, maxSize),
966
+ dequeue: (threadId) => base.dequeue(threadId),
967
+ queueDepth: (threadId) => base.queueDepth(threadId),
968
+ get: (key) => base.get(key),
969
+ getList: (key) => base.getList(key),
970
+ set: (key, value, ttlMs) => base.set(key, value, ttlMs),
971
+ setIfNotExists: (key, value, ttlMs) => base.setIfNotExists(key, value, ttlMs),
972
+ delete: (key) => base.delete(key)
973
+ };
974
+ }
975
+ function withOptionalPrefix(base, prefix) {
976
+ return prefix ? createPrefixedStateAdapter(base, prefix) : base;
977
+ }
978
+ function createStateAdapter() {
979
+ const config = getChatConfig();
980
+ const activeLockMaxAgeMs = config.bot.turnTimeoutMs + ACTIVE_LOCK_TTL_MS;
981
+ if (config.state.adapter === "memory") {
982
+ redisStateAdapter = void 0;
983
+ return createQueuedStateAdapter(
984
+ withOptionalPrefix(createMemoryState(), config.state.keyPrefix),
985
+ { activeLockMaxAgeMs }
986
+ );
987
+ }
988
+ if (!config.state.redisUrl) {
989
+ throw new Error("REDIS_URL is required for durable Slack thread state");
990
+ }
991
+ const redisState = createRedisState({
992
+ url: config.state.redisUrl
993
+ });
994
+ redisStateAdapter = redisState;
995
+ return createQueuedStateAdapter(
996
+ withOptionalPrefix(redisState, config.state.keyPrefix),
997
+ { activeLockMaxAgeMs }
998
+ );
999
+ }
1000
+ function getOptionalRedisStateAdapter() {
1001
+ getStateAdapter();
1002
+ return redisStateAdapter;
1003
+ }
1004
+ async function getConnectedStateContext() {
1005
+ const adapter = getStateAdapter();
1006
+ await adapter.connect();
1007
+ return {
1008
+ redisStateAdapter: getOptionalRedisStateAdapter(),
1009
+ stateAdapter: adapter
1010
+ };
1011
+ }
1012
+ async function getDefaultRedisStateAdapterFor(adapter) {
1013
+ if (adapter !== stateAdapter) {
1014
+ return void 0;
1015
+ }
1016
+ const context = await getConnectedStateContext();
1017
+ return context.stateAdapter === adapter ? context.redisStateAdapter : void 0;
1018
+ }
1019
+ function getStateAdapter() {
1020
+ if (!stateAdapter) {
1021
+ stateAdapter = createStateAdapter();
1022
+ }
1023
+ return stateAdapter;
1024
+ }
1025
+ async function disconnectStateAdapter() {
1026
+ if (!stateAdapter) {
1027
+ return;
1028
+ }
1029
+ try {
1030
+ await stateAdapter.disconnect();
1031
+ } finally {
1032
+ stateAdapter = void 0;
1033
+ redisStateAdapter = void 0;
1034
+ }
1035
+ }
1036
+
798
1037
  export {
799
1038
  toOptionalTrimmed,
800
1039
  escapeXml,
1040
+ unescapeXml,
801
1041
  renderCurrentInstruction,
802
1042
  unwrapCurrentInstruction,
803
1043
  TURN_CONTEXT_TAG,
@@ -834,5 +1074,10 @@ export {
834
1074
  getSlackClientSecret,
835
1075
  getRuntimeMetadata,
836
1076
  getSlackReactionConfig,
837
- setSlackReactionConfig
1077
+ setSlackReactionConfig,
1078
+ acquireActiveLock,
1079
+ getConnectedStateContext,
1080
+ getDefaultRedisStateAdapterFor,
1081
+ getStateAdapter,
1082
+ disconnectStateAdapter
838
1083
  };
@@ -3,7 +3,7 @@ import {
3
3
  logWarn,
4
4
  parseActorUserId,
5
5
  setSpanAttributes
6
- } from "./chunk-S6QKIGRM.js";
6
+ } from "./chunk-WPOJ443W.js";
7
7
  import {
8
8
  discoverInstalledPluginPackageContent,
9
9
  normalizePluginPackageNames,
@@ -5,7 +5,7 @@ import {
5
5
  parseSlackChannelId,
6
6
  parseSlackMessageTs,
7
7
  parseSlackTeamId
8
- } from "./chunk-LFLTJQVR.js";
8
+ } from "./chunk-7FBGKXPE.js";
9
9
 
10
10
  // src/chat/slack/context.ts
11
11
  import { z } from "zod";