@sentry/junior 0.106.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 (132) hide show
  1. package/dist/{agent-hooks-KR5BCWOK.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 +3586 -3614
  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 +2 -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-result.d.ts +7 -12
  52. package/dist/chat/services/{turn-reasoning-level.d.ts → turn-router.d.ts} +12 -8
  53. package/dist/chat/services/turn-session-record.d.ts +15 -16
  54. package/dist/chat/slack/adapter.d.ts +2 -2
  55. package/dist/chat/slack/errors.d.ts +2 -0
  56. package/dist/chat/slack/outbound.d.ts +0 -1
  57. package/dist/chat/slack/reply.d.ts +2 -8
  58. package/dist/chat/slack/tools/channel-capabilities.d.ts +2 -2
  59. package/dist/chat/slack/tools/{send-message.d.ts → send-files.d.ts} +27 -19
  60. package/dist/chat/state/conversation.d.ts +4 -4
  61. package/dist/chat/state/turn-id.d.ts +2 -0
  62. package/dist/chat/state/turn-session-keys.d.ts +6 -0
  63. package/dist/chat/state/turn-session.d.ts +11 -9
  64. package/dist/chat/task-execution/state.d.ts +6 -1
  65. package/dist/chat/task-execution/store.d.ts +1 -1
  66. package/dist/chat/tool-support/pi-tool-adapter.d.ts +1 -1
  67. package/dist/chat/tool-support/tool-execution-report.d.ts +1 -0
  68. package/dist/chat/tools/handoff/tool.d.ts +2 -2
  69. package/dist/chat/tools/sandbox/file-uploads.d.ts +28 -6
  70. package/dist/chat/tools/types.d.ts +6 -9
  71. package/dist/chat/tools/web/image-generate.d.ts +4 -6
  72. package/dist/{chunk-DDRQ4U7I.js → chunk-4YF7Z6IA.js} +390 -270
  73. package/dist/{chunk-AZ5OMSN5.js → chunk-AUUOHQAT.js} +2 -2
  74. package/dist/chunk-B2Z2H66D.js +250 -0
  75. package/dist/{chunk-4W4O4DCV.js → chunk-CQ7KSO2B.js} +75 -45
  76. package/dist/{chunk-MJSJAHBW.js → chunk-DY7TRYB5.js} +1 -1
  77. package/dist/{chunk-GPPUTY5M.js → chunk-EDLNHZH3.js} +861 -891
  78. package/dist/{chunk-R5BFYK2O.js → chunk-IGHMVDWI.js} +134 -18
  79. package/dist/chunk-MU6HHZEN.js +99 -0
  80. package/dist/{chunk-XTKJPSMF.js → chunk-NVOTGWYX.js} +458 -684
  81. package/dist/{chunk-AEWZ7F65.js → chunk-O24R4DCK.js} +2 -179
  82. package/dist/{chunk-CCFROYAV.js → chunk-PUAMO4PI.js} +115 -125
  83. package/dist/{chunk-ZMRLBBD5.js → chunk-RMVOAJRL.js} +30 -13
  84. package/dist/{chunk-3NYUCBLE.js → chunk-SPUAJVVH.js} +6 -4
  85. package/dist/{chunk-4UYIRWDW.js → chunk-SS67LUOK.js} +41 -290
  86. package/dist/{chunk-FXFQNHRU.js → chunk-VFUK3X5B.js} +4 -2
  87. package/dist/{chunk-3MPPVPGZ.js → chunk-VH6KWKG2.js} +2 -2
  88. package/dist/chunk-VIXFG2VC.js +182 -0
  89. package/dist/{chunk-BKM4UXUD.js → chunk-YFQ7CQDE.js} +669 -18
  90. package/dist/{chunk-77LHNQK4.js → chunk-YIPD3L6L.js} +4 -2
  91. package/dist/{chunk-3OUSJPR7.js → chunk-YNP2ATQX.js} +6 -4
  92. package/dist/{chunk-WR7H7VLJ.js → chunk-YT72TOEK.js} +2 -2
  93. package/dist/cli/chat.js +21 -20
  94. package/dist/cli/check.js +4 -3
  95. package/dist/cli/plugins.js +11 -9
  96. package/dist/cli/snapshot-warmup.js +7 -5
  97. package/dist/cli/upgrade.js +2046 -105
  98. package/dist/db/schema/{agent-steps.d.ts → conversation-events.d.ts} +37 -21
  99. package/dist/db/schema.d.ts +28 -159
  100. package/dist/{db-S33LFWG2.js → db-DIGO4TGW.js} +6 -8
  101. package/dist/{runner-HA5XAL6K.js → runner-ACR2HAIC.js} +209 -101
  102. package/dist/{skills-722AXRV5.js → skills-ITNHBARB.js} +4 -3
  103. package/dist/{validation-EUI2UFRM.js → validation-AGQ46ZGV.js} +4 -3
  104. package/migrations/0005_conversation_events.sql +122 -0
  105. package/migrations/README.md +23 -10
  106. package/migrations/meta/0005_snapshot.json +948 -0
  107. package/migrations/meta/_journal.json +7 -0
  108. package/package.json +5 -5
  109. package/dist/api/conversations/activity.d.ts +0 -40
  110. package/dist/api/conversations/detail-projection.d.ts +0 -11
  111. package/dist/api/conversations/detail.query.d.ts +0 -3
  112. package/dist/api/conversations/subagent.d.ts +0 -10
  113. package/dist/api/conversations/transcript.d.ts +0 -28
  114. package/dist/chat/conversations/legacy-advisor-session.d.ts +0 -7
  115. package/dist/chat/conversations/legacy-import.d.ts +0 -38
  116. package/dist/chat/conversations/sql/legacy-history-import.d.ts +0 -55
  117. package/dist/chat/conversations/sql/messages.d.ts +0 -4
  118. package/dist/chat/conversations/visible-compactions.d.ts +0 -11
  119. package/dist/chat/conversations/visible-messages.d.ts +0 -47
  120. package/dist/chat/services/reply-delivery-plan.d.ts +0 -5
  121. package/dist/chat/state/session-log.d.ts +0 -384
  122. package/dist/chunk-P4P7O5WG.js +0 -1072
  123. package/dist/chunk-XIMUQ4IU.js +0 -147
  124. package/dist/db/schema/conversation-messages.d.ts +0 -153
  125. package/dist/handlers/github-webhook/check-suite.d.ts +0 -3
  126. package/dist/handlers/github-webhook/issue-comment.d.ts +0 -3
  127. package/dist/handlers/github-webhook/pull-request-review-comment.d.ts +0 -3
  128. package/dist/handlers/github-webhook/pull-request-review.d.ts +0 -3
  129. package/dist/handlers/github-webhook/pull-request.d.ts +0 -3
  130. package/dist/handlers/github-webhook/resource.d.ts +0 -10
  131. package/dist/handlers/github-webhook.d.ts +0 -15
  132. package/dist/legacy-import-TMJISRKO.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,14 +570,17 @@ 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
@@ -679,10 +669,10 @@ function parseSlashCommand(rawValue) {
679
669
  }
680
670
  return command;
681
671
  }
682
- 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;
683
673
  var DEFAULT_FAST_MODEL_ID = getModel(
684
674
  "vercel-ai-gateway",
685
- "openai/gpt-5.4-mini"
675
+ "anthropic/claude-haiku-4.5"
686
676
  ).id;
687
677
  var DEFAULT_HANDOFF_MODEL_ID = getModel(
688
678
  "vercel-ai-gateway",
@@ -698,9 +688,13 @@ function validateGatewayModelId(raw) {
698
688
  function validateEmbeddingModelId(raw) {
699
689
  return toOptionalTrimmed(raw);
700
690
  }
701
- function parseModelProfiles(rawValue, handoffModelId) {
691
+ function parseProfiles(rawValue, standardModelId2, handoffModelId) {
702
692
  const profiles = {
703
- [DEFAULT_HANDOFF_MODEL_PROFILE]: handoffModelId
693
+ [STANDARD_MODEL_PROFILE]: { modelId: standardModelId2 },
694
+ [DEFAULT_HANDOFF_MODEL_PROFILE]: {
695
+ modelId: handoffModelId,
696
+ reasoningLevel: "high"
697
+ }
704
698
  };
705
699
  const trimmed = toOptionalTrimmed(rawValue);
706
700
  if (trimmed === void 0) {
@@ -731,7 +725,7 @@ function parseModelProfiles(rawValue, handoffModelId) {
731
725
  if (!modelId) {
732
726
  throw new Error(`AI_MODEL_PROFILES.${profile} must not be empty`);
733
727
  }
734
- profiles[profile] = modelId;
728
+ profiles[profile] = { modelId };
735
729
  }
736
730
  return profiles;
737
731
  }
@@ -757,8 +751,7 @@ function readBotConfig(env) {
757
751
  const handoffModelId = validateGatewayModelId(env.AI_HANDOFF_MODEL) ?? DEFAULT_HANDOFF_MODEL_ID;
758
752
  return {
759
753
  userName: toOptionalTrimmed(env.JUNIOR_BOT_NAME) ?? "junior",
760
- modelId,
761
- modelProfiles: parseModelProfiles(env.AI_MODEL_PROFILES, handoffModelId),
754
+ profiles: parseProfiles(env.AI_MODEL_PROFILES, modelId, handoffModelId),
762
755
  modelContextWindowTokens: parseOptionalPositiveInteger(
763
756
  "AI_MODEL_CONTEXT_WINDOW_TOKENS",
764
757
  env.AI_MODEL_CONTEXT_WINDOW_TOKENS
@@ -873,245 +866,6 @@ function setSlackReactionConfig(overrides) {
873
866
  }
874
867
  }
875
868
 
876
- // src/chat/state/adapter.ts
877
- import { createMemoryState } from "@chat-adapter/state-memory";
878
- import { createRedisState } from "@chat-adapter/state-redis";
879
-
880
- // src/chat/state/locks.ts
881
- var ACTIVE_LOCK_TTL_MS = 9e4;
882
- async function acquireActiveLock(state, threadId) {
883
- return await state.acquireLock(threadId, ACTIVE_LOCK_TTL_MS);
884
- }
885
-
886
- // src/chat/state/adapter.ts
887
- var ACTIVE_LOCK_HEARTBEAT_MS = 3e4;
888
- var stateAdapter;
889
- var redisStateAdapter;
890
- function createPrefixedStateAdapter(base, prefix) {
891
- const prefixed = (value) => `${prefix}:${value}`;
892
- const unprefixed = (value) => value.startsWith(`${prefix}:`) ? value.slice(prefix.length + 1) : value;
893
- const prefixLock = (lock) => ({
894
- ...lock,
895
- threadId: prefixed(lock.threadId)
896
- });
897
- const unprefixLock = (lock) => ({
898
- ...lock,
899
- threadId: unprefixed(lock.threadId)
900
- });
901
- return {
902
- appendToList: (key, value, options) => base.appendToList(prefixed(key), value, options),
903
- connect: () => base.connect(),
904
- disconnect: () => base.disconnect(),
905
- subscribe: (threadId) => base.subscribe(prefixed(threadId)),
906
- unsubscribe: (threadId) => base.unsubscribe(prefixed(threadId)),
907
- isSubscribed: (threadId) => base.isSubscribed(prefixed(threadId)),
908
- acquireLock: async (threadId, ttlMs) => {
909
- const lock = await base.acquireLock(prefixed(threadId), ttlMs);
910
- return lock ? unprefixLock(lock) : null;
911
- },
912
- releaseLock: (lock) => base.releaseLock(prefixLock(lock)),
913
- extendLock: async (lock, ttlMs) => {
914
- const prefixedLock = prefixLock(lock);
915
- const extended = await base.extendLock(prefixedLock, ttlMs);
916
- if (extended) {
917
- lock.expiresAt = prefixedLock.expiresAt;
918
- }
919
- return extended;
920
- },
921
- forceReleaseLock: (threadId) => base.forceReleaseLock(prefixed(threadId)),
922
- enqueue: (threadId, entry, maxSize) => base.enqueue(prefixed(threadId), entry, maxSize),
923
- dequeue: (threadId) => base.dequeue(prefixed(threadId)),
924
- queueDepth: (threadId) => base.queueDepth(prefixed(threadId)),
925
- get: (key) => base.get(prefixed(key)),
926
- getList: (key) => base.getList(prefixed(key)),
927
- set: (key, value, ttlMs) => base.set(prefixed(key), value, ttlMs),
928
- setIfNotExists: (key, value, ttlMs) => base.setIfNotExists(prefixed(key), value, ttlMs),
929
- delete: (key) => base.delete(prefixed(key))
930
- };
931
- }
932
- function createQueuedStateAdapter(base, options) {
933
- const heartbeats = /* @__PURE__ */ new Map();
934
- const shouldHeartbeatLock = (ttlMs) => ttlMs === ACTIVE_LOCK_TTL_MS;
935
- const heartbeatKey = (lock) => `${lock.threadId}:${lock.token}`;
936
- const stopHeartbeatByKey = (key) => {
937
- const heartbeat = heartbeats.get(key);
938
- if (!heartbeat) {
939
- return;
940
- }
941
- clearInterval(heartbeat.timer);
942
- heartbeats.delete(key);
943
- };
944
- const stopHeartbeat = (lock) => {
945
- stopHeartbeatByKey(heartbeatKey(lock));
946
- };
947
- const stopHeartbeatsForThread = (threadId) => {
948
- for (const [key, heartbeat] of heartbeats) {
949
- if (heartbeat.lock.threadId === threadId) {
950
- stopHeartbeatByKey(key);
951
- }
952
- }
953
- };
954
- const stopAllHeartbeats = () => {
955
- for (const key of heartbeats.keys()) {
956
- stopHeartbeatByKey(key);
957
- }
958
- };
959
- const runHeartbeat = async (key) => {
960
- const heartbeat = heartbeats.get(key);
961
- if (!heartbeat || heartbeat.inFlight) {
962
- return;
963
- }
964
- heartbeat.inFlight = true;
965
- try {
966
- if (Date.now() - heartbeat.startedAtMs >= options.activeLockMaxAgeMs) {
967
- stopHeartbeatByKey(key);
968
- return;
969
- }
970
- const extended = await base.extendLock(heartbeat.lock, heartbeat.ttlMs);
971
- if (!extended) {
972
- stopHeartbeatByKey(key);
973
- return;
974
- }
975
- heartbeat.lock.expiresAt = Date.now() + heartbeat.ttlMs;
976
- } catch {
977
- } finally {
978
- const current = heartbeats.get(key);
979
- if (current === heartbeat) {
980
- current.inFlight = false;
981
- }
982
- }
983
- };
984
- const startOrUpdateHeartbeat = (lock, ttlMs) => {
985
- const key = heartbeatKey(lock);
986
- const existing = heartbeats.get(key);
987
- if (existing) {
988
- existing.ttlMs = ttlMs;
989
- return;
990
- }
991
- const timer = setInterval(() => {
992
- void runHeartbeat(key);
993
- }, ACTIVE_LOCK_HEARTBEAT_MS);
994
- timer.unref?.();
995
- heartbeats.set(key, {
996
- inFlight: false,
997
- lock,
998
- startedAtMs: Date.now(),
999
- timer,
1000
- ttlMs
1001
- });
1002
- };
1003
- const acquireLock = async (threadId, ttlMs) => {
1004
- const lock = await base.acquireLock(threadId, ttlMs);
1005
- if (lock && shouldHeartbeatLock(ttlMs)) {
1006
- startOrUpdateHeartbeat(lock, ttlMs);
1007
- }
1008
- return lock;
1009
- };
1010
- return {
1011
- appendToList: (key, value, options2) => base.appendToList(key, value, options2),
1012
- connect: () => base.connect(),
1013
- disconnect: async () => {
1014
- stopAllHeartbeats();
1015
- await base.disconnect();
1016
- },
1017
- subscribe: (threadId) => base.subscribe(threadId),
1018
- unsubscribe: (threadId) => base.unsubscribe(threadId),
1019
- isSubscribed: (threadId) => base.isSubscribed(threadId),
1020
- acquireLock,
1021
- releaseLock: async (lock) => {
1022
- stopHeartbeat(lock);
1023
- await base.releaseLock(lock);
1024
- },
1025
- extendLock: async (lock, ttlMs) => {
1026
- const extended = await base.extendLock(lock, ttlMs);
1027
- if (extended) {
1028
- lock.expiresAt = Date.now() + ttlMs;
1029
- if (shouldHeartbeatLock(ttlMs)) {
1030
- startOrUpdateHeartbeat(lock, ttlMs);
1031
- } else {
1032
- stopHeartbeat(lock);
1033
- }
1034
- } else {
1035
- stopHeartbeat(lock);
1036
- }
1037
- return extended;
1038
- },
1039
- forceReleaseLock: async (threadId) => {
1040
- stopHeartbeatsForThread(threadId);
1041
- await base.forceReleaseLock(threadId);
1042
- },
1043
- enqueue: (threadId, entry, maxSize) => base.enqueue(threadId, entry, maxSize),
1044
- dequeue: (threadId) => base.dequeue(threadId),
1045
- queueDepth: (threadId) => base.queueDepth(threadId),
1046
- get: (key) => base.get(key),
1047
- getList: (key) => base.getList(key),
1048
- set: (key, value, ttlMs) => base.set(key, value, ttlMs),
1049
- setIfNotExists: (key, value, ttlMs) => base.setIfNotExists(key, value, ttlMs),
1050
- delete: (key) => base.delete(key)
1051
- };
1052
- }
1053
- function withOptionalPrefix(base, prefix) {
1054
- return prefix ? createPrefixedStateAdapter(base, prefix) : base;
1055
- }
1056
- function createStateAdapter() {
1057
- const config = getChatConfig();
1058
- const activeLockMaxAgeMs = config.bot.turnTimeoutMs + ACTIVE_LOCK_TTL_MS;
1059
- if (config.state.adapter === "memory") {
1060
- redisStateAdapter = void 0;
1061
- return createQueuedStateAdapter(
1062
- withOptionalPrefix(createMemoryState(), config.state.keyPrefix),
1063
- { activeLockMaxAgeMs }
1064
- );
1065
- }
1066
- if (!config.state.redisUrl) {
1067
- throw new Error("REDIS_URL is required for durable Slack thread state");
1068
- }
1069
- const redisState = createRedisState({
1070
- url: config.state.redisUrl
1071
- });
1072
- redisStateAdapter = redisState;
1073
- return createQueuedStateAdapter(
1074
- withOptionalPrefix(redisState, config.state.keyPrefix),
1075
- { activeLockMaxAgeMs }
1076
- );
1077
- }
1078
- function getOptionalRedisStateAdapter() {
1079
- getStateAdapter();
1080
- return redisStateAdapter;
1081
- }
1082
- async function getConnectedStateContext() {
1083
- const adapter = getStateAdapter();
1084
- await adapter.connect();
1085
- return {
1086
- redisStateAdapter: getOptionalRedisStateAdapter(),
1087
- stateAdapter: adapter
1088
- };
1089
- }
1090
- async function getDefaultRedisStateAdapterFor(adapter) {
1091
- if (adapter !== stateAdapter) {
1092
- return void 0;
1093
- }
1094
- const context = await getConnectedStateContext();
1095
- return context.stateAdapter === adapter ? context.redisStateAdapter : void 0;
1096
- }
1097
- function getStateAdapter() {
1098
- if (!stateAdapter) {
1099
- stateAdapter = createStateAdapter();
1100
- }
1101
- return stateAdapter;
1102
- }
1103
- async function disconnectStateAdapter() {
1104
- if (!stateAdapter) {
1105
- return;
1106
- }
1107
- try {
1108
- await stateAdapter.disconnect();
1109
- } finally {
1110
- stateAdapter = void 0;
1111
- redisStateAdapter = void 0;
1112
- }
1113
- }
1114
-
1115
869
  export {
1116
870
  toOptionalTrimmed,
1117
871
  escapeXml,
@@ -1154,6 +908,8 @@ export {
1154
908
  modelProfileSchema,
1155
909
  ModelProfileNotConfiguredError,
1156
910
  modelIdForProfile,
911
+ standardModelId,
912
+ profileConfig,
1157
913
  FUNCTION_TIMEOUT_BUFFER_SECONDS,
1158
914
  getChatConfig,
1159
915
  botConfig,
@@ -1163,10 +919,5 @@ export {
1163
919
  getSlackClientSecret,
1164
920
  getRuntimeMetadata,
1165
921
  getSlackReactionConfig,
1166
- setSlackReactionConfig,
1167
- acquireActiveLock,
1168
- getConnectedStateContext,
1169
- getDefaultRedisStateAdapterFor,
1170
- getStateAdapter,
1171
- disconnectStateAdapter
922
+ setSlackReactionConfig
1172
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-4UYIRWDW.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
+ };