@sentry/junior 0.105.0 → 0.107.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 (133) hide show
  1. package/dist/{agent-hooks-EOFSY2PD.js → agent-hooks-ICPIJAFY.js} +9 -7
  2. package/dist/api/conversations/detail.d.ts +1 -1
  3. package/dist/api/conversations/events.d.ts +14 -0
  4. package/dist/api/conversations/schema.d.ts +196 -463
  5. package/dist/api/schema.d.ts +2 -7
  6. package/dist/api/schema.js +10 -8
  7. package/dist/api.js +377 -810
  8. package/dist/app.js +2284 -2288
  9. package/dist/{catalog-runtime-5CVM2DOF.js → catalog-runtime-FOFNPR3P.js} +3 -2
  10. package/dist/chat/agent/prompt.d.ts +1 -1
  11. package/dist/chat/agent/request.d.ts +28 -29
  12. package/dist/chat/agent/resume.d.ts +7 -7
  13. package/dist/chat/agent/session.d.ts +5 -6
  14. package/dist/chat/agent/tools.d.ts +3 -3
  15. package/dist/chat/agent-dispatch/runner.d.ts +2 -0
  16. package/dist/chat/config.d.ts +3 -2
  17. package/dist/chat/conversations/history.d.ts +582 -167
  18. package/dist/chat/conversations/message-projection.d.ts +6 -0
  19. package/dist/chat/conversations/message-summaries.d.ts +9 -0
  20. package/dist/chat/conversations/messages.d.ts +13 -27
  21. package/dist/chat/conversations/projection.d.ts +30 -53
  22. package/dist/chat/conversations/provenance.d.ts +42 -0
  23. package/dist/chat/conversations/search.d.ts +2 -2
  24. package/dist/chat/conversations/sql/conversation-row.d.ts +1 -1
  25. package/dist/chat/conversations/sql/event-lock.d.ts +3 -0
  26. package/dist/chat/conversations/sql/history.d.ts +3 -3
  27. package/dist/chat/conversations/sql/privacy.d.ts +27 -0
  28. package/dist/chat/conversations/sql/purge.d.ts +2 -10
  29. package/dist/chat/conversations/sql/store.d.ts +0 -5
  30. package/dist/chat/conversations/store.d.ts +8 -13
  31. package/dist/chat/conversations/turn-lifecycle.d.ts +41 -0
  32. package/dist/chat/db.d.ts +4 -10
  33. package/dist/chat/model-profile.d.ts +10 -0
  34. package/dist/chat/oauth-flow.d.ts +0 -5
  35. package/dist/chat/pi/conversation-events.d.ts +33 -0
  36. package/dist/chat/pi/sql-model-usage.d.ts +10 -0
  37. package/dist/chat/plugins/agent-hooks.d.ts +4 -2
  38. package/dist/chat/resource-events/ingest.d.ts +0 -13
  39. package/dist/chat/runtime/delivered-turn-state.d.ts +1 -1
  40. package/dist/chat/runtime/generated-artifacts.d.ts +1 -1
  41. package/dist/chat/runtime/reply-executor.d.ts +2 -0
  42. package/dist/chat/runtime/slack-resume.d.ts +23 -28
  43. package/dist/chat/runtime/slack-runtime.d.ts +2 -0
  44. package/dist/chat/runtime/turn.d.ts +14 -1
  45. package/dist/chat/services/context-compaction-marker.d.ts +0 -1
  46. package/dist/chat/services/context-compaction.d.ts +1 -0
  47. package/dist/chat/services/conversation-memory.d.ts +9 -0
  48. package/dist/chat/services/plugin-auth-orchestration.d.ts +1 -4
  49. package/dist/chat/services/subscribed-decision.d.ts +1 -1
  50. package/dist/chat/services/turn-failure-response.d.ts +10 -3
  51. package/dist/chat/services/turn-limit.d.ts +6 -0
  52. package/dist/chat/services/turn-result.d.ts +7 -12
  53. package/dist/chat/services/{turn-reasoning-level.d.ts → turn-router.d.ts} +12 -8
  54. package/dist/chat/services/turn-session-record.d.ts +15 -17
  55. package/dist/chat/slack/adapter.d.ts +2 -2
  56. package/dist/chat/slack/errors.d.ts +2 -0
  57. package/dist/chat/slack/outbound.d.ts +0 -1
  58. package/dist/chat/slack/reply.d.ts +2 -8
  59. package/dist/chat/slack/tools/channel-capabilities.d.ts +2 -2
  60. package/dist/chat/slack/tools/{send-message.d.ts → send-files.d.ts} +27 -19
  61. package/dist/chat/state/conversation.d.ts +4 -4
  62. package/dist/chat/state/turn-id.d.ts +2 -0
  63. package/dist/chat/state/turn-session-keys.d.ts +6 -0
  64. package/dist/chat/state/turn-session.d.ts +11 -9
  65. package/dist/chat/task-execution/state.d.ts +6 -1
  66. package/dist/chat/task-execution/store.d.ts +1 -1
  67. package/dist/chat/tool-support/pi-tool-adapter.d.ts +1 -1
  68. package/dist/chat/tool-support/tool-execution-report.d.ts +1 -0
  69. package/dist/chat/tools/handoff/tool.d.ts +2 -2
  70. package/dist/chat/tools/sandbox/file-uploads.d.ts +28 -6
  71. package/dist/chat/tools/types.d.ts +6 -9
  72. package/dist/chat/tools/web/image-generate.d.ts +4 -6
  73. package/dist/{chunk-QDKRE3LS.js → chunk-4YF7Z6IA.js} +390 -270
  74. package/dist/{chunk-4PDVOHXP.js → chunk-AUUOHQAT.js} +2 -2
  75. package/dist/chunk-B2Z2H66D.js +250 -0
  76. package/dist/{chunk-QVTVCJR4.js → chunk-CQ7KSO2B.js} +75 -45
  77. package/dist/{chunk-MJSJAHBW.js → chunk-DY7TRYB5.js} +1 -1
  78. package/dist/{chunk-NJJFU6CP.js → chunk-EDLNHZH3.js} +883 -898
  79. package/dist/{chunk-O7ILGCMJ.js → chunk-IGHMVDWI.js} +134 -18
  80. package/dist/chunk-MU6HHZEN.js +99 -0
  81. package/dist/{chunk-BCULHGZK.js → chunk-NVOTGWYX.js} +458 -684
  82. package/dist/{chunk-AEWZ7F65.js → chunk-O24R4DCK.js} +2 -179
  83. package/dist/{chunk-CCFROYAV.js → chunk-PUAMO4PI.js} +115 -125
  84. package/dist/{chunk-VDHMFCDY.js → chunk-RMVOAJRL.js} +30 -13
  85. package/dist/{chunk-7G4J3CR7.js → chunk-SPUAJVVH.js} +6 -4
  86. package/dist/{chunk-M3UAVLEN.js → chunk-SS67LUOK.js} +43 -290
  87. package/dist/{chunk-FXFQNHRU.js → chunk-VFUK3X5B.js} +4 -2
  88. package/dist/{chunk-CCTCIISK.js → chunk-VH6KWKG2.js} +2 -2
  89. package/dist/chunk-VIXFG2VC.js +182 -0
  90. package/dist/{chunk-RMZ65YAA.js → chunk-YFQ7CQDE.js} +687 -21
  91. package/dist/{chunk-77LHNQK4.js → chunk-YIPD3L6L.js} +4 -2
  92. package/dist/{chunk-U26ZWZCN.js → chunk-YNP2ATQX.js} +6 -4
  93. package/dist/{chunk-WR7H7VLJ.js → chunk-YT72TOEK.js} +2 -2
  94. package/dist/cli/chat.js +21 -20
  95. package/dist/cli/check.js +4 -3
  96. package/dist/cli/plugins.js +11 -9
  97. package/dist/cli/snapshot-warmup.js +7 -5
  98. package/dist/cli/upgrade.js +2046 -105
  99. package/dist/db/schema/{agent-steps.d.ts → conversation-events.d.ts} +37 -21
  100. package/dist/db/schema.d.ts +28 -159
  101. package/dist/{db-PDMC67T3.js → db-DIGO4TGW.js} +6 -8
  102. package/dist/{runner-6A7MQRUE.js → runner-ACR2HAIC.js} +209 -101
  103. package/dist/{skills-722AXRV5.js → skills-ITNHBARB.js} +4 -3
  104. package/dist/{validation-EUI2UFRM.js → validation-AGQ46ZGV.js} +4 -3
  105. package/migrations/0005_conversation_events.sql +122 -0
  106. package/migrations/README.md +23 -10
  107. package/migrations/meta/0005_snapshot.json +948 -0
  108. package/migrations/meta/_journal.json +7 -0
  109. package/package.json +6 -6
  110. package/dist/api/conversations/activity.d.ts +0 -40
  111. package/dist/api/conversations/detail-projection.d.ts +0 -11
  112. package/dist/api/conversations/detail.query.d.ts +0 -3
  113. package/dist/api/conversations/subagent.d.ts +0 -10
  114. package/dist/api/conversations/transcript.d.ts +0 -28
  115. package/dist/chat/conversations/legacy-advisor-session.d.ts +0 -7
  116. package/dist/chat/conversations/legacy-import.d.ts +0 -38
  117. package/dist/chat/conversations/sql/legacy-history-import.d.ts +0 -55
  118. package/dist/chat/conversations/sql/messages.d.ts +0 -4
  119. package/dist/chat/conversations/visible-compactions.d.ts +0 -11
  120. package/dist/chat/conversations/visible-messages.d.ts +0 -47
  121. package/dist/chat/services/reply-delivery-plan.d.ts +0 -5
  122. package/dist/chat/state/session-log.d.ts +0 -384
  123. package/dist/chunk-RYDF4GOX.js +0 -1072
  124. package/dist/chunk-XIMUQ4IU.js +0 -147
  125. package/dist/db/schema/conversation-messages.d.ts +0 -153
  126. package/dist/handlers/github-webhook/check-suite.d.ts +0 -3
  127. package/dist/handlers/github-webhook/issue-comment.d.ts +0 -3
  128. package/dist/handlers/github-webhook/pull-request-review-comment.d.ts +0 -3
  129. package/dist/handlers/github-webhook/pull-request-review.d.ts +0 -3
  130. package/dist/handlers/github-webhook/pull-request.d.ts +0 -3
  131. package/dist/handlers/github-webhook/resource.d.ts +0 -10
  132. package/dist/handlers/github-webhook.d.ts +0 -15
  133. package/dist/legacy-import-G333RHJZ.js +0 -18
@@ -12,7 +12,7 @@ import {
12
12
  normalizeGenAiFinishReason,
13
13
  serializeGenAiAttribute,
14
14
  withSpan
15
- } from "./chunk-AEWZ7F65.js";
15
+ } from "./chunk-O24R4DCK.js";
16
16
 
17
17
  // src/chat/xml.ts
18
18
  function escapeXml(value) {
@@ -75,6 +75,13 @@ var TURN_CONTEXT_TAG = "runtime-turn-context";
75
75
 
76
76
  // src/chat/pi/transcript.ts
77
77
  var RUNTIME_TURN_CONTEXT_START = `<${TURN_CONTEXT_TAG}>`;
78
+ function userMessageContent(message) {
79
+ const record = message;
80
+ return record.role === "user" && Array.isArray(record.content) ? record.content : void 0;
81
+ }
82
+ function isRuntimeTurnContextPart(part) {
83
+ return part !== null && typeof part === "object" && part.type === "text" && typeof part.text === "string" && part.text.startsWith(RUNTIME_TURN_CONTEXT_START);
84
+ }
78
85
  var EMBEDDED_THREAD_CONTEXT_TAGS = [
79
86
  "recent-thread-messages",
80
87
  "thread-compactions",
@@ -120,14 +127,14 @@ function extractAssistantText(message) {
120
127
  ).map((part) => part.text).join("\n");
121
128
  }
122
129
  function getTerminalAssistantMessages(messages) {
123
- let lastToolResultIndex = -1;
130
+ let lastNonAssistantIndex = -1;
124
131
  for (let index = messages.length - 1; index >= 0; index -= 1) {
125
- if (isToolResultMessage(messages[index])) {
126
- lastToolResultIndex = index;
132
+ if (!isAssistantMessage(messages[index])) {
133
+ lastNonAssistantIndex = index;
127
134
  break;
128
135
  }
129
136
  }
130
- return messages.slice(lastToolResultIndex + 1).filter(isAssistantMessage);
137
+ return messages.slice(lastNonAssistantIndex + 1).filter(isAssistantMessage);
131
138
  }
132
139
  function trimTrailingAssistantMessages(messages) {
133
140
  let end = messages.length;
@@ -136,27 +143,14 @@ function trimTrailingAssistantMessages(messages) {
136
143
  }
137
144
  return end === messages.length ? [...messages] : messages.slice(0, end);
138
145
  }
139
- function getUserMessageContent(message) {
140
- const record = message;
141
- return record.role === "user" && Array.isArray(record.content) ? record.content : void 0;
142
- }
143
- function isRuntimeTurnContextPart(part) {
144
- return part !== null && typeof part === "object" && part.type === "text" && typeof part.text === "string" && part.text.startsWith(RUNTIME_TURN_CONTEXT_START);
145
- }
146
146
  function hasRuntimeTurnContext(messages) {
147
147
  return messages.some(
148
- (message) => getUserMessageContent(message)?.some(
149
- (part) => isRuntimeTurnContextPart(part)
150
- )
148
+ (message) => userMessageContent(message)?.some(isRuntimeTurnContextPart)
151
149
  );
152
150
  }
153
151
  function retainRuntimeTurnContext(messages) {
154
152
  return messages.flatMap((message) => {
155
- const content = getUserMessageContent(message);
156
- if (!content) {
157
- return [];
158
- }
159
- const runtimeContent = content.filter(isRuntimeTurnContextPart);
153
+ const runtimeContent = userMessageContent(message)?.filter(isRuntimeTurnContextPart) ?? [];
160
154
  return runtimeContent.length > 0 ? [{ ...message, content: runtimeContent }] : [];
161
155
  });
162
156
  }
@@ -166,19 +160,13 @@ function instructionTextForProjection(text) {
166
160
  }
167
161
  function stripRuntimeTurnContext(messages) {
168
162
  return messages.flatMap((message) => {
169
- const content = getUserMessageContent(message);
170
- if (!content) {
171
- return [message];
172
- }
163
+ const content = userMessageContent(message);
164
+ if (!content) return [message];
173
165
  const nextContent = content.filter(
174
166
  (part) => !isRuntimeTurnContextPart(part)
175
167
  );
176
- if (nextContent.length === content.length) {
177
- return [message];
178
- }
179
- if (nextContent.length === 0) {
180
- return [];
181
- }
168
+ if (nextContent.length === content.length) return [message];
169
+ if (nextContent.length === 0) return [];
182
170
  return [{ ...message, content: nextContent }];
183
171
  });
184
172
  }
@@ -260,11 +248,10 @@ function nextProviderRetry(args) {
260
248
 
261
249
  // src/chat/services/context-compaction-marker.ts
262
250
  var COMPACTION_SUMMARY_PREFIX = "Context compaction summary for future Junior turns:";
263
- var LEGACY_COMPACTION_SUMMARY_PREFIX = "Context handoff summary for future Junior turns:";
264
251
  var MODEL_HANDOFF_SUMMARY_PREFIX = "Model handoff checkpoint. Continue the outstanding request now using this summary as the complete prior context:";
265
252
  function isCompactionSummaryText(text) {
266
253
  const normalized = (unwrapCurrentInstruction(text) ?? text).trimStart();
267
- return normalized.startsWith(COMPACTION_SUMMARY_PREFIX) || normalized.startsWith(LEGACY_COMPACTION_SUMMARY_PREFIX) || normalized.startsWith(MODEL_HANDOFF_SUMMARY_PREFIX);
254
+ return normalized.startsWith(COMPACTION_SUMMARY_PREFIX) || normalized.startsWith(MODEL_HANDOFF_SUMMARY_PREFIX);
268
255
  }
269
256
  function hasCompactedConversationContext(messages) {
270
257
  return messages.some((message) => {
@@ -583,19 +570,23 @@ var ModelProfileNotConfiguredError = class extends Error {
583
570
  }
584
571
  };
585
572
  function modelIdForProfile(config, profile) {
586
- if (profile === STANDARD_MODEL_PROFILE) {
587
- return config.modelId;
588
- }
589
- const modelId = Object.hasOwn(config.modelProfiles, profile) ? config.modelProfiles[profile] : void 0;
590
- if (!modelId) {
573
+ return profileConfig(config, profile).modelId;
574
+ }
575
+ function standardModelId(config) {
576
+ return modelIdForProfile(config, STANDARD_MODEL_PROFILE);
577
+ }
578
+ function profileConfig(config, profile) {
579
+ const profileConfig2 = Object.hasOwn(config.profiles, profile) ? config.profiles[profile] : void 0;
580
+ if (!profileConfig2) {
591
581
  throw new ModelProfileNotConfiguredError(profile);
592
582
  }
593
- return modelId;
583
+ return profileConfig2;
594
584
  }
595
585
 
596
586
  // src/chat/config.ts
597
587
  var MIN_AGENT_TURN_TIMEOUT_MS = 10 * 1e3;
598
588
  var DEFAULT_AGENT_TURN_TIMEOUT_MS = 12 * 60 * 1e3;
589
+ var MAX_SLICES_PER_TURN = 100;
599
590
  var DEFAULT_FUNCTION_MAX_DURATION_SECONDS = 300;
600
591
  var DEFAULT_SLACK_SLASH_COMMAND = "/jr";
601
592
  var DEFAULT_PROCESSING_REACTION_EMOJI = "eyes";
@@ -678,10 +669,10 @@ function parseSlashCommand(rawValue) {
678
669
  }
679
670
  return command;
680
671
  }
681
- var DEFAULT_MODEL_ID = getModel("vercel-ai-gateway", "openai/gpt-5.5").id;
672
+ var DEFAULT_MODEL_ID = getModel("vercel-ai-gateway", "xai/grok-4.5").id;
682
673
  var DEFAULT_FAST_MODEL_ID = getModel(
683
674
  "vercel-ai-gateway",
684
- "openai/gpt-5.4-mini"
675
+ "anthropic/claude-haiku-4.5"
685
676
  ).id;
686
677
  var DEFAULT_HANDOFF_MODEL_ID = getModel(
687
678
  "vercel-ai-gateway",
@@ -697,9 +688,13 @@ function validateGatewayModelId(raw) {
697
688
  function validateEmbeddingModelId(raw) {
698
689
  return toOptionalTrimmed(raw);
699
690
  }
700
- function parseModelProfiles(rawValue, handoffModelId) {
691
+ function parseProfiles(rawValue, standardModelId2, handoffModelId) {
701
692
  const profiles = {
702
- [DEFAULT_HANDOFF_MODEL_PROFILE]: handoffModelId
693
+ [STANDARD_MODEL_PROFILE]: { modelId: standardModelId2 },
694
+ [DEFAULT_HANDOFF_MODEL_PROFILE]: {
695
+ modelId: handoffModelId,
696
+ reasoningLevel: "high"
697
+ }
703
698
  };
704
699
  const trimmed = toOptionalTrimmed(rawValue);
705
700
  if (trimmed === void 0) {
@@ -730,7 +725,7 @@ function parseModelProfiles(rawValue, handoffModelId) {
730
725
  if (!modelId) {
731
726
  throw new Error(`AI_MODEL_PROFILES.${profile} must not be empty`);
732
727
  }
733
- profiles[profile] = modelId;
728
+ profiles[profile] = { modelId };
734
729
  }
735
730
  return profiles;
736
731
  }
@@ -756,8 +751,7 @@ function readBotConfig(env) {
756
751
  const handoffModelId = validateGatewayModelId(env.AI_HANDOFF_MODEL) ?? DEFAULT_HANDOFF_MODEL_ID;
757
752
  return {
758
753
  userName: toOptionalTrimmed(env.JUNIOR_BOT_NAME) ?? "junior",
759
- modelId,
760
- modelProfiles: parseModelProfiles(env.AI_MODEL_PROFILES, handoffModelId),
754
+ profiles: parseProfiles(env.AI_MODEL_PROFILES, modelId, handoffModelId),
761
755
  modelContextWindowTokens: parseOptionalPositiveInteger(
762
756
  "AI_MODEL_CONTEXT_WINDOW_TOKENS",
763
757
  env.AI_MODEL_CONTEXT_WINDOW_TOKENS
@@ -767,6 +761,7 @@ function readBotConfig(env) {
767
761
  embeddingModelId: validateEmbeddingModelId(env.AI_EMBEDDING_MODEL) ?? DEFAULT_EMBEDDING_MODEL_ID,
768
762
  loadingMessages: parseLoadingMessages(env.JUNIOR_LOADING_MESSAGES),
769
763
  visionModelId: validateGatewayModelId(env.AI_VISION_MODEL),
764
+ maxSlicesPerTurn: MAX_SLICES_PER_TURN,
770
765
  turnTimeoutMs: parseAgentTurnTimeoutMs(
771
766
  env.AGENT_TURN_TIMEOUT_MS,
772
767
  maxTurnTimeoutMs
@@ -871,245 +866,6 @@ function setSlackReactionConfig(overrides) {
871
866
  }
872
867
  }
873
868
 
874
- // src/chat/state/adapter.ts
875
- import { createMemoryState } from "@chat-adapter/state-memory";
876
- import { createRedisState } from "@chat-adapter/state-redis";
877
-
878
- // src/chat/state/locks.ts
879
- var ACTIVE_LOCK_TTL_MS = 9e4;
880
- async function acquireActiveLock(state, threadId) {
881
- return await state.acquireLock(threadId, ACTIVE_LOCK_TTL_MS);
882
- }
883
-
884
- // src/chat/state/adapter.ts
885
- var ACTIVE_LOCK_HEARTBEAT_MS = 3e4;
886
- var stateAdapter;
887
- var redisStateAdapter;
888
- function createPrefixedStateAdapter(base, prefix) {
889
- const prefixed = (value) => `${prefix}:${value}`;
890
- const unprefixed = (value) => value.startsWith(`${prefix}:`) ? value.slice(prefix.length + 1) : value;
891
- const prefixLock = (lock) => ({
892
- ...lock,
893
- threadId: prefixed(lock.threadId)
894
- });
895
- const unprefixLock = (lock) => ({
896
- ...lock,
897
- threadId: unprefixed(lock.threadId)
898
- });
899
- return {
900
- appendToList: (key, value, options) => base.appendToList(prefixed(key), value, options),
901
- connect: () => base.connect(),
902
- disconnect: () => base.disconnect(),
903
- subscribe: (threadId) => base.subscribe(prefixed(threadId)),
904
- unsubscribe: (threadId) => base.unsubscribe(prefixed(threadId)),
905
- isSubscribed: (threadId) => base.isSubscribed(prefixed(threadId)),
906
- acquireLock: async (threadId, ttlMs) => {
907
- const lock = await base.acquireLock(prefixed(threadId), ttlMs);
908
- return lock ? unprefixLock(lock) : null;
909
- },
910
- releaseLock: (lock) => base.releaseLock(prefixLock(lock)),
911
- extendLock: async (lock, ttlMs) => {
912
- const prefixedLock = prefixLock(lock);
913
- const extended = await base.extendLock(prefixedLock, ttlMs);
914
- if (extended) {
915
- lock.expiresAt = prefixedLock.expiresAt;
916
- }
917
- return extended;
918
- },
919
- forceReleaseLock: (threadId) => base.forceReleaseLock(prefixed(threadId)),
920
- enqueue: (threadId, entry, maxSize) => base.enqueue(prefixed(threadId), entry, maxSize),
921
- dequeue: (threadId) => base.dequeue(prefixed(threadId)),
922
- queueDepth: (threadId) => base.queueDepth(prefixed(threadId)),
923
- get: (key) => base.get(prefixed(key)),
924
- getList: (key) => base.getList(prefixed(key)),
925
- set: (key, value, ttlMs) => base.set(prefixed(key), value, ttlMs),
926
- setIfNotExists: (key, value, ttlMs) => base.setIfNotExists(prefixed(key), value, ttlMs),
927
- delete: (key) => base.delete(prefixed(key))
928
- };
929
- }
930
- function createQueuedStateAdapter(base, options) {
931
- const heartbeats = /* @__PURE__ */ new Map();
932
- const shouldHeartbeatLock = (ttlMs) => ttlMs === ACTIVE_LOCK_TTL_MS;
933
- const heartbeatKey = (lock) => `${lock.threadId}:${lock.token}`;
934
- const stopHeartbeatByKey = (key) => {
935
- const heartbeat = heartbeats.get(key);
936
- if (!heartbeat) {
937
- return;
938
- }
939
- clearInterval(heartbeat.timer);
940
- heartbeats.delete(key);
941
- };
942
- const stopHeartbeat = (lock) => {
943
- stopHeartbeatByKey(heartbeatKey(lock));
944
- };
945
- const stopHeartbeatsForThread = (threadId) => {
946
- for (const [key, heartbeat] of heartbeats) {
947
- if (heartbeat.lock.threadId === threadId) {
948
- stopHeartbeatByKey(key);
949
- }
950
- }
951
- };
952
- const stopAllHeartbeats = () => {
953
- for (const key of heartbeats.keys()) {
954
- stopHeartbeatByKey(key);
955
- }
956
- };
957
- const runHeartbeat = async (key) => {
958
- const heartbeat = heartbeats.get(key);
959
- if (!heartbeat || heartbeat.inFlight) {
960
- return;
961
- }
962
- heartbeat.inFlight = true;
963
- try {
964
- if (Date.now() - heartbeat.startedAtMs >= options.activeLockMaxAgeMs) {
965
- stopHeartbeatByKey(key);
966
- return;
967
- }
968
- const extended = await base.extendLock(heartbeat.lock, heartbeat.ttlMs);
969
- if (!extended) {
970
- stopHeartbeatByKey(key);
971
- return;
972
- }
973
- heartbeat.lock.expiresAt = Date.now() + heartbeat.ttlMs;
974
- } catch {
975
- } finally {
976
- const current = heartbeats.get(key);
977
- if (current === heartbeat) {
978
- current.inFlight = false;
979
- }
980
- }
981
- };
982
- const startOrUpdateHeartbeat = (lock, ttlMs) => {
983
- const key = heartbeatKey(lock);
984
- const existing = heartbeats.get(key);
985
- if (existing) {
986
- existing.ttlMs = ttlMs;
987
- return;
988
- }
989
- const timer = setInterval(() => {
990
- void runHeartbeat(key);
991
- }, ACTIVE_LOCK_HEARTBEAT_MS);
992
- timer.unref?.();
993
- heartbeats.set(key, {
994
- inFlight: false,
995
- lock,
996
- startedAtMs: Date.now(),
997
- timer,
998
- ttlMs
999
- });
1000
- };
1001
- const acquireLock = async (threadId, ttlMs) => {
1002
- const lock = await base.acquireLock(threadId, ttlMs);
1003
- if (lock && shouldHeartbeatLock(ttlMs)) {
1004
- startOrUpdateHeartbeat(lock, ttlMs);
1005
- }
1006
- return lock;
1007
- };
1008
- return {
1009
- appendToList: (key, value, options2) => base.appendToList(key, value, options2),
1010
- connect: () => base.connect(),
1011
- disconnect: async () => {
1012
- stopAllHeartbeats();
1013
- await base.disconnect();
1014
- },
1015
- subscribe: (threadId) => base.subscribe(threadId),
1016
- unsubscribe: (threadId) => base.unsubscribe(threadId),
1017
- isSubscribed: (threadId) => base.isSubscribed(threadId),
1018
- acquireLock,
1019
- releaseLock: async (lock) => {
1020
- stopHeartbeat(lock);
1021
- await base.releaseLock(lock);
1022
- },
1023
- extendLock: async (lock, ttlMs) => {
1024
- const extended = await base.extendLock(lock, ttlMs);
1025
- if (extended) {
1026
- lock.expiresAt = Date.now() + ttlMs;
1027
- if (shouldHeartbeatLock(ttlMs)) {
1028
- startOrUpdateHeartbeat(lock, ttlMs);
1029
- } else {
1030
- stopHeartbeat(lock);
1031
- }
1032
- } else {
1033
- stopHeartbeat(lock);
1034
- }
1035
- return extended;
1036
- },
1037
- forceReleaseLock: async (threadId) => {
1038
- stopHeartbeatsForThread(threadId);
1039
- await base.forceReleaseLock(threadId);
1040
- },
1041
- enqueue: (threadId, entry, maxSize) => base.enqueue(threadId, entry, maxSize),
1042
- dequeue: (threadId) => base.dequeue(threadId),
1043
- queueDepth: (threadId) => base.queueDepth(threadId),
1044
- get: (key) => base.get(key),
1045
- getList: (key) => base.getList(key),
1046
- set: (key, value, ttlMs) => base.set(key, value, ttlMs),
1047
- setIfNotExists: (key, value, ttlMs) => base.setIfNotExists(key, value, ttlMs),
1048
- delete: (key) => base.delete(key)
1049
- };
1050
- }
1051
- function withOptionalPrefix(base, prefix) {
1052
- return prefix ? createPrefixedStateAdapter(base, prefix) : base;
1053
- }
1054
- function createStateAdapter() {
1055
- const config = getChatConfig();
1056
- const activeLockMaxAgeMs = config.bot.turnTimeoutMs + ACTIVE_LOCK_TTL_MS;
1057
- if (config.state.adapter === "memory") {
1058
- redisStateAdapter = void 0;
1059
- return createQueuedStateAdapter(
1060
- withOptionalPrefix(createMemoryState(), config.state.keyPrefix),
1061
- { activeLockMaxAgeMs }
1062
- );
1063
- }
1064
- if (!config.state.redisUrl) {
1065
- throw new Error("REDIS_URL is required for durable Slack thread state");
1066
- }
1067
- const redisState = createRedisState({
1068
- url: config.state.redisUrl
1069
- });
1070
- redisStateAdapter = redisState;
1071
- return createQueuedStateAdapter(
1072
- withOptionalPrefix(redisState, config.state.keyPrefix),
1073
- { activeLockMaxAgeMs }
1074
- );
1075
- }
1076
- function getOptionalRedisStateAdapter() {
1077
- getStateAdapter();
1078
- return redisStateAdapter;
1079
- }
1080
- async function getConnectedStateContext() {
1081
- const adapter = getStateAdapter();
1082
- await adapter.connect();
1083
- return {
1084
- redisStateAdapter: getOptionalRedisStateAdapter(),
1085
- stateAdapter: adapter
1086
- };
1087
- }
1088
- async function getDefaultRedisStateAdapterFor(adapter) {
1089
- if (adapter !== stateAdapter) {
1090
- return void 0;
1091
- }
1092
- const context = await getConnectedStateContext();
1093
- return context.stateAdapter === adapter ? context.redisStateAdapter : void 0;
1094
- }
1095
- function getStateAdapter() {
1096
- if (!stateAdapter) {
1097
- stateAdapter = createStateAdapter();
1098
- }
1099
- return stateAdapter;
1100
- }
1101
- async function disconnectStateAdapter() {
1102
- if (!stateAdapter) {
1103
- return;
1104
- }
1105
- try {
1106
- await stateAdapter.disconnect();
1107
- } finally {
1108
- stateAdapter = void 0;
1109
- redisStateAdapter = void 0;
1110
- }
1111
- }
1112
-
1113
869
  export {
1114
870
  toOptionalTrimmed,
1115
871
  escapeXml,
@@ -1152,6 +908,8 @@ export {
1152
908
  modelProfileSchema,
1153
909
  ModelProfileNotConfiguredError,
1154
910
  modelIdForProfile,
911
+ standardModelId,
912
+ profileConfig,
1155
913
  FUNCTION_TIMEOUT_BUFFER_SECONDS,
1156
914
  getChatConfig,
1157
915
  botConfig,
@@ -1161,10 +919,5 @@ export {
1161
919
  getSlackClientSecret,
1162
920
  getRuntimeMetadata,
1163
921
  getSlackReactionConfig,
1164
- setSlackReactionConfig,
1165
- acquireActiveLock,
1166
- getConnectedStateContext,
1167
- getDefaultRedisStateAdapterFor,
1168
- getStateAdapter,
1169
- disconnectStateAdapter
922
+ setSlackReactionConfig
1170
923
  };
@@ -1,10 +1,12 @@
1
+ // src/db/schema/destinations.ts
2
+ import { index, jsonb, pgTable, text, uniqueIndex } from "drizzle-orm/pg-core";
3
+ import { z } from "zod";
4
+
1
5
  // src/db/schema/timestamps.ts
2
6
  import { timestamp } from "drizzle-orm/pg-core";
3
7
  var timestamptz = (name) => timestamp(name, { withTimezone: true });
4
8
 
5
9
  // src/db/schema/destinations.ts
6
- import { index, jsonb, pgTable, text, uniqueIndex } from "drizzle-orm/pg-core";
7
- import { z } from "zod";
8
10
  var juniorDestinationKindSchema = z.enum([
9
11
  "channel",
10
12
  "dm",
@@ -3,13 +3,13 @@ import {
3
3
  } from "./chunk-4ZNGQH7C.js";
4
4
  import {
5
5
  getSlackBotToken
6
- } from "./chunk-M3UAVLEN.js";
6
+ } from "./chunk-SS67LUOK.js";
7
7
  import {
8
8
  logWarn,
9
9
  setSpanAttributes,
10
10
  setSpanStatus,
11
11
  withSpan
12
- } from "./chunk-AEWZ7F65.js";
12
+ } from "./chunk-O24R4DCK.js";
13
13
  import {
14
14
  parseSlackChannelReferenceId
15
15
  } from "./chunk-7FBGKXPE.js";
@@ -0,0 +1,182 @@
1
+ import {
2
+ parseSlackTeamId
3
+ } from "./chunk-7FBGKXPE.js";
4
+
5
+ // src/chat/actor.ts
6
+ import { z } from "zod";
7
+ import { actorSchema } from "@sentry/junior-plugin-api";
8
+ var SLACK_USER_ID_DISPLAY_PATTERN = /^[UW][A-Z0-9]{5,}$/;
9
+ var EMAIL_PATTERN = /^[^\s@<>]+@[^\s@<>]+\.[^\s@<>]+$/;
10
+ var exactStoredStringSchema = z.string().min(1).refine((value) => value === value.trim());
11
+ var storedSlackActorSchema = z.object({
12
+ email: exactStoredStringSchema.optional(),
13
+ fullName: exactStoredStringSchema.optional(),
14
+ platform: z.literal("slack").optional(),
15
+ slackUserId: exactStoredStringSchema.optional(),
16
+ slackUserName: exactStoredStringSchema.optional(),
17
+ teamId: exactStoredStringSchema.optional()
18
+ }).strict();
19
+ function isUserActor(actor) {
20
+ return Boolean(actor && "userId" in actor);
21
+ }
22
+ function clean(value) {
23
+ const trimmed = value?.trim();
24
+ return trimmed ? trimmed : void 0;
25
+ }
26
+ function isSyntheticActorUserId(value) {
27
+ return value.toLowerCase() === "unknown";
28
+ }
29
+ function isSlackUserId(value) {
30
+ return SLACK_USER_ID_DISPLAY_PATTERN.test(value);
31
+ }
32
+ function cleanActorDisplayName(value, userId) {
33
+ const displayName = clean(value);
34
+ if (!displayName) {
35
+ return void 0;
36
+ }
37
+ if (displayName.toLowerCase() === "unknown") {
38
+ return void 0;
39
+ }
40
+ if (userId && displayName === userId) {
41
+ return void 0;
42
+ }
43
+ return isSlackUserId(displayName) ? void 0 : displayName;
44
+ }
45
+ function cleanActorEmail(value) {
46
+ const email = clean(value);
47
+ return email && EMAIL_PATTERN.test(email) ? email : void 0;
48
+ }
49
+ function parseActorUserId(value) {
50
+ if (typeof value !== "string" || value.length === 0) {
51
+ return void 0;
52
+ }
53
+ if (value !== value.trim() || isSyntheticActorUserId(value)) {
54
+ return void 0;
55
+ }
56
+ return value;
57
+ }
58
+ function isActorUserId(value) {
59
+ return parseActorUserId(value) === value;
60
+ }
61
+ function createActor(input, context) {
62
+ const platform = context.platform ?? input?.platform;
63
+ if (!platform) {
64
+ return void 0;
65
+ }
66
+ const contextUserId = parseActorUserId(context.userId);
67
+ if (context.userId !== void 0 && !contextUserId) {
68
+ return void 0;
69
+ }
70
+ const inputUserId = parseActorUserId(input?.userId);
71
+ if (input?.userId !== void 0 && !inputUserId) {
72
+ return void 0;
73
+ }
74
+ const actorUserId = contextUserId ?? inputUserId;
75
+ if (!actorUserId) {
76
+ return void 0;
77
+ }
78
+ const contextTeamId = parseSlackTeamId(context.teamId);
79
+ if (context.teamId !== void 0 && !contextTeamId) {
80
+ return void 0;
81
+ }
82
+ const inputTeamId = parseSlackTeamId(input?.teamId);
83
+ if (input?.teamId !== void 0 && !inputTeamId) {
84
+ return void 0;
85
+ }
86
+ const actorTeamId = contextTeamId ?? inputTeamId;
87
+ if (platform === "slack" && !actorTeamId) {
88
+ return void 0;
89
+ }
90
+ const canUseInputProfile = (!contextUserId || !inputUserId || contextUserId === inputUserId) && (platform !== "slack" || !contextTeamId || !inputTeamId || contextTeamId === inputTeamId);
91
+ const actor = {
92
+ ...canUseInputProfile && cleanActorEmail(input?.email) ? { email: cleanActorEmail(input?.email) } : {},
93
+ ...canUseInputProfile && cleanActorDisplayName(input?.fullName, actorUserId) ? {
94
+ fullName: cleanActorDisplayName(input?.fullName, actorUserId)
95
+ } : {},
96
+ platform,
97
+ userId: actorUserId,
98
+ ...canUseInputProfile && cleanActorDisplayName(input?.userName, actorUserId) ? {
99
+ userName: cleanActorDisplayName(input?.userName, actorUserId)
100
+ } : {}
101
+ };
102
+ if (platform === "slack") {
103
+ return { ...actor, platform, teamId: actorTeamId };
104
+ }
105
+ return { ...actor, platform };
106
+ }
107
+ function createSlackActor(teamId, userId, profile) {
108
+ const actorUserId = parseActorUserId(userId);
109
+ const actorTeamId = parseSlackTeamId(teamId);
110
+ if (!actorTeamId || !actorUserId) {
111
+ throw new Error("Slack actor requires team and user ids");
112
+ }
113
+ const actor = createActor(
114
+ {
115
+ email: profile?.email,
116
+ fullName: profile?.fullName,
117
+ platform: "slack",
118
+ teamId: actorTeamId,
119
+ userId: actorUserId,
120
+ userName: profile?.userName
121
+ },
122
+ { teamId: actorTeamId, userId: actorUserId }
123
+ );
124
+ if (!actor || actor.platform !== "slack") {
125
+ throw new Error("Slack actor requires team and user ids");
126
+ }
127
+ return actor;
128
+ }
129
+ function parseStoredSlackActor(value) {
130
+ const parsed = storedSlackActorSchema.safeParse(value);
131
+ if (!parsed.success) {
132
+ return void 0;
133
+ }
134
+ if (parsed.data.slackUserId !== void 0 && !parseActorUserId(parsed.data.slackUserId)) {
135
+ return void 0;
136
+ }
137
+ if (parsed.data.teamId !== void 0 && !parseSlackTeamId(parsed.data.teamId)) {
138
+ return void 0;
139
+ }
140
+ if ((parsed.data.platform !== void 0 || parsed.data.teamId !== void 0) && (!parsed.data.platform || !parsed.data.teamId)) {
141
+ return void 0;
142
+ }
143
+ return parsed.data;
144
+ }
145
+ function toStoredSlackActor(actor) {
146
+ return {
147
+ ...actor.email ? { email: actor.email } : {},
148
+ ...actor.fullName ? { fullName: actor.fullName } : {},
149
+ platform: actor.platform,
150
+ slackUserId: actor.userId,
151
+ ...actor.userName ? { slackUserName: actor.userName } : {},
152
+ teamId: actor.teamId
153
+ };
154
+ }
155
+ function createSlackResumeActor(args) {
156
+ if (args.actor) {
157
+ if (args.actor.platform !== "slack" || args.actor.teamId !== args.teamId || args.actor.userId !== args.userId) {
158
+ throw new Error("Stored Slack actor did not match resume actor");
159
+ }
160
+ }
161
+ const actor = createActor(args.actor, {
162
+ platform: "slack",
163
+ teamId: args.teamId,
164
+ userId: args.userId
165
+ });
166
+ if (!actor || actor.platform !== "slack") {
167
+ throw new Error("Slack actor requires team and user ids");
168
+ }
169
+ return actor;
170
+ }
171
+
172
+ export {
173
+ storedSlackActorSchema,
174
+ isUserActor,
175
+ parseActorUserId,
176
+ isActorUserId,
177
+ createActor,
178
+ createSlackActor,
179
+ parseStoredSlackActor,
180
+ toStoredSlackActor,
181
+ createSlackResumeActor
182
+ };