@sentry/junior 0.67.0 → 0.67.1

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.
@@ -7,10 +7,12 @@ export type AdvisorThinkingLevel = (typeof ADVISOR_THINKING_LEVELS)[number];
7
7
  export declare const FUNCTION_TIMEOUT_BUFFER_SECONDS = 20;
8
8
  export interface BotConfig {
9
9
  advisor: AdvisorConfig;
10
+ completedReactionEmoji: string;
10
11
  fastModelId: string;
11
12
  loadingMessages: string[];
12
13
  modelId: string;
13
14
  modelContextWindowTokens?: number;
15
+ processingReactionEmoji: string;
14
16
  visionModelId?: string;
15
17
  turnTimeoutMs: number;
16
18
  userName: string;
@@ -0,0 +1,8 @@
1
+ import type { Message } from "chat";
2
+ import { type ActorIdentityInput, type SlackActorProfile } from "@/chat/services/requester-identity";
3
+ /** Preserve runtime-owned identity on Chat SDK messages before persistence. */
4
+ export declare function bindMessageActorIdentity(message: Message, identity: ActorIdentityInput): ActorIdentityInput;
5
+ /** Read message identity without promoting adapter display fallbacks. */
6
+ export declare function getMessageActorIdentity(message: Message): ActorIdentityInput | undefined;
7
+ /** Attach Slack display fields only after the author id is exact. */
8
+ export declare function ensureSlackMessageActorIdentity(message: Message, lookupSlackUser: (userId: string) => Promise<SlackActorProfile | null | undefined>): Promise<ActorIdentityInput>;
@@ -0,0 +1,19 @@
1
+ export interface ActorIdentityInput {
2
+ email?: string;
3
+ fullName?: string;
4
+ userId?: string;
5
+ userName?: string;
6
+ }
7
+ export interface SlackActorProfile {
8
+ email?: string;
9
+ fullName?: string;
10
+ userName?: string;
11
+ }
12
+ /** Keep actor ids exact at platform boundaries before they enter owned state. */
13
+ export declare function parseActorUserId(value: unknown): string | undefined;
14
+ /** Assert persisted actor ids without read-side repair. */
15
+ export declare function isActorUserId(value: string | undefined): value is string;
16
+ /** Keep authority ids exact while attaching optional presentation fields. */
17
+ export declare function buildActorIdentity(requester: ActorIdentityInput | undefined, requesterId?: string): ActorIdentityInput | undefined;
18
+ /** Use Slack profile data only as presentation around the exact user id. */
19
+ export declare function slackActorIdentity(userId: string, profile: SlackActorProfile | null | undefined): ActorIdentityInput;
@@ -1,3 +1,4 @@
1
+ import { type ActorIdentityInput } from "@/chat/services/requester-identity";
1
2
  interface SlackUserLookupResult {
2
3
  userName?: string;
3
4
  fullName?: string;
@@ -5,4 +6,6 @@ interface SlackUserLookupResult {
5
6
  }
6
7
  /** Fetch Slack user profile info with in-memory TTL cache to avoid repeated API calls. */
7
8
  export declare function lookupSlackUser(userId?: string): Promise<SlackUserLookupResult | null>;
9
+ /** Resolve the canonical Slack actor identity from Slack profile data. */
10
+ export declare function lookupSlackActorIdentity(userId: string): Promise<ActorIdentityInput>;
8
11
  export {};
@@ -4,6 +4,7 @@ import type { SlackTurnRuntime } from "@/chat/runtime/slack-runtime";
4
4
  import type { InboundMessageRecord } from "@/chat/task-execution/store";
5
5
  import type { ConversationWorkerContext, ConversationWorkerResult } from "@/chat/task-execution/worker";
6
6
  import { type SlackInstallationContext } from "@/chat/slack/adapter-context";
7
+ import { type SlackActorProfile } from "@/chat/services/requester-identity";
7
8
  export type SlackConversationRoute = "mention" | "subscribed";
8
9
  export interface SlackConversationMessageMetadata {
9
10
  [key: string]: unknown;
@@ -15,6 +16,7 @@ export interface SlackConversationMessageMetadata {
15
16
  }
16
17
  export interface CreateSlackConversationWorkerOptions {
17
18
  getSlackAdapter: () => SlackAdapter;
19
+ lookupSlackUser?: (userId: string) => Promise<SlackActorProfile | null | undefined>;
18
20
  resumeAwaitingContinuation?: (conversationId: string) => Promise<boolean>;
19
21
  runtime: Pick<SlackTurnRuntime<unknown>, "handleNewMention" | "handleSubscribedMessage">;
20
22
  state?: StateAdapter;
@@ -8,13 +8,15 @@ import {
8
8
  getStateAdapter,
9
9
  parseSlackThreadId,
10
10
  sandboxSkillDir
11
- } from "./chunk-NWU2Z6SM.js";
11
+ } from "./chunk-MT23VNOH.js";
12
12
  import {
13
+ isActorUserId,
13
14
  isRecord,
14
15
  logException,
15
16
  logInfo,
16
- logWarn
17
- } from "./chunk-6QWWMZCK.js";
17
+ logWarn,
18
+ parseActorUserId
19
+ } from "./chunk-OIIXZOOC.js";
18
20
  import {
19
21
  sentry_exports
20
22
  } from "./chunk-Z3YD6NHK.js";
@@ -450,7 +452,7 @@ function timingSafeMatch(expected, actual) {
450
452
  function createSlackDirectCredentialSubject(input) {
451
453
  const channelId = normalizeSlackConversationId(input.channelId);
452
454
  const teamId = input.teamId?.trim();
453
- const userId = input.userId?.trim();
455
+ const userId = parseActorUserId(input.userId);
454
456
  if (!channelId || !teamId || !userId || !isDmChannel(channelId)) {
455
457
  return void 0;
456
458
  }
@@ -465,7 +467,7 @@ function bindSlackDirectCredentialSubject(input) {
465
467
  const teamId = input.teamId.trim();
466
468
  const secret = getCredentialSubjectSecret();
467
469
  const { subject } = input;
468
- const userId = subject.userId.trim();
470
+ const userId = parseActorUserId(subject.userId);
469
471
  if (!channelId || !teamId || !secret || !isDmChannel(channelId) || subject.type !== "user" || !userId || subject.allowedWhen !== "private-direct-conversation") {
470
472
  return void 0;
471
473
  }
@@ -497,7 +499,7 @@ function verifySlackDirectCredentialSubject(input) {
497
499
  }
498
500
  const { subject } = input;
499
501
  const binding = subject.binding;
500
- if (subject.type !== "user" || typeof subject.userId !== "string" || !subject.userId || subject.allowedWhen !== "private-direct-conversation" || !binding || binding.type !== "slack-direct-conversation" || typeof binding.signature !== "string" || !binding.signature || binding.teamId !== input.teamId || binding.channelId !== channelId) {
502
+ if (subject.type !== "user" || !isActorUserId(subject.userId) || subject.allowedWhen !== "private-direct-conversation" || !binding || binding.type !== "slack-direct-conversation" || typeof binding.signature !== "string" || !binding.signature || binding.teamId !== input.teamId || binding.channelId !== channelId) {
501
503
  return false;
502
504
  }
503
505
  const expected = signPayload(
@@ -6,7 +6,7 @@ import {
6
6
  setSpanAttributes,
7
7
  toOptionalString,
8
8
  withSpan
9
- } from "./chunk-6QWWMZCK.js";
9
+ } from "./chunk-OIIXZOOC.js";
10
10
 
11
11
  // src/chat/slack/context.ts
12
12
  function toTrimmedSlackString(value) {
@@ -496,11 +496,24 @@ async function completeObject(params) {
496
496
  };
497
497
  }
498
498
 
499
+ // src/chat/slack/emoji.ts
500
+ var SLACK_EMOJI_NAME_RE = /^(?:[a-z0-9_+-]+)(?:::(?:skin-tone-[2-6]))?$/;
501
+ function normalizeSlackEmojiName(value) {
502
+ const trimmed = value.trim().toLowerCase();
503
+ if (!trimmed) {
504
+ return null;
505
+ }
506
+ const normalized = trimmed.startsWith(":") && trimmed.endsWith(":") ? trimmed.slice(1, -1) : trimmed;
507
+ return SLACK_EMOJI_NAME_RE.test(normalized) ? normalized : null;
508
+ }
509
+
499
510
  // src/chat/config.ts
500
511
  var MIN_AGENT_TURN_TIMEOUT_MS = 10 * 1e3;
501
512
  var DEFAULT_AGENT_TURN_TIMEOUT_MS = 12 * 60 * 1e3;
502
513
  var DEFAULT_FUNCTION_MAX_DURATION_SECONDS = 300;
503
514
  var DEFAULT_SLACK_SLASH_COMMAND = "/jr";
515
+ var DEFAULT_PROCESSING_REACTION_EMOJI = "eyes";
516
+ var DEFAULT_COMPLETED_REACTION_EMOJI = "white_check_mark";
504
517
  var ADVISOR_THINKING_LEVELS = [
505
518
  "minimal",
506
519
  "low",
@@ -620,6 +633,19 @@ function readAdvisorConfig(env) {
620
633
  thinkingLevel: parseAdvisorThinkingLevel(env.AI_ADVISOR_THINKING_LEVEL)
621
634
  };
622
635
  }
636
+ function parseReactionEmoji(envName, rawValue, defaultEmoji) {
637
+ const trimmed = toOptionalTrimmed(rawValue);
638
+ if (trimmed === void 0) {
639
+ return defaultEmoji;
640
+ }
641
+ const normalized = normalizeSlackEmojiName(trimmed);
642
+ if (!normalized) {
643
+ throw new Error(
644
+ `${envName} must be a valid Slack emoji name (for example "eyes" or ":white_check_mark:")`
645
+ );
646
+ }
647
+ return normalized;
648
+ }
623
649
  function readBotConfig(env) {
624
650
  const functionMaxDurationSeconds = resolveFunctionMaxDurationSeconds(env);
625
651
  const maxTurnTimeoutMs = resolveMaxTurnTimeoutMs(functionMaxDurationSeconds);
@@ -632,6 +658,16 @@ function readBotConfig(env) {
632
658
  ),
633
659
  fastModelId: validateGatewayModelId(env.AI_FAST_MODEL ?? env.AI_MODEL) ?? DEFAULT_FAST_MODEL_ID,
634
660
  loadingMessages: parseLoadingMessages(env.JUNIOR_LOADING_MESSAGES),
661
+ processingReactionEmoji: parseReactionEmoji(
662
+ "JUNIOR_PROCESSING_REACTION",
663
+ env.JUNIOR_PROCESSING_REACTION,
664
+ DEFAULT_PROCESSING_REACTION_EMOJI
665
+ ),
666
+ completedReactionEmoji: parseReactionEmoji(
667
+ "JUNIOR_COMPLETED_REACTION",
668
+ env.JUNIOR_COMPLETED_REACTION,
669
+ DEFAULT_COMPLETED_REACTION_EMOJI
670
+ ),
635
671
  visionModelId: validateGatewayModelId(env.AI_VISION_MODEL),
636
672
  turnTimeoutMs: parseAgentTurnTimeoutMs(
637
673
  env.AGENT_TURN_TIMEOUT_MS,
@@ -949,6 +985,7 @@ export {
949
985
  resolveGatewayModel,
950
986
  completeText,
951
987
  completeObject,
988
+ normalizeSlackEmojiName,
952
989
  FUNCTION_TIMEOUT_BUFFER_SECONDS,
953
990
  getChatConfig,
954
991
  botConfig,
@@ -2858,6 +2858,90 @@ var CredentialUnavailableError = class extends Error {
2858
2858
  }
2859
2859
  };
2860
2860
 
2861
+ // src/chat/services/requester-identity.ts
2862
+ var SLACK_USER_ID_PATTERN = /^[UW][A-Z0-9]{5,}$/;
2863
+ var EMAIL_PATTERN = /^[^\s@<>]+@[^\s@<>]+\.[^\s@<>]+$/;
2864
+ function clean(value) {
2865
+ const trimmed = value?.trim();
2866
+ return trimmed ? trimmed : void 0;
2867
+ }
2868
+ function isSyntheticActorUserId(value) {
2869
+ return value.toLowerCase() === "unknown";
2870
+ }
2871
+ function parseActorUserId(value) {
2872
+ if (typeof value !== "string" || value.length === 0) {
2873
+ return void 0;
2874
+ }
2875
+ if (value !== value.trim() || isSyntheticActorUserId(value)) {
2876
+ return void 0;
2877
+ }
2878
+ return value;
2879
+ }
2880
+ function isActorUserId(value) {
2881
+ return parseActorUserId(value) === value;
2882
+ }
2883
+ function isSlackUserId(value) {
2884
+ return SLACK_USER_ID_PATTERN.test(value);
2885
+ }
2886
+ function cleanActorDisplayName(value, userId) {
2887
+ const displayName = clean(value);
2888
+ if (!displayName) {
2889
+ return void 0;
2890
+ }
2891
+ if (displayName.toLowerCase() === "unknown") {
2892
+ return void 0;
2893
+ }
2894
+ if (userId && displayName === userId) {
2895
+ return void 0;
2896
+ }
2897
+ return isSlackUserId(displayName) ? void 0 : displayName;
2898
+ }
2899
+ function cleanActorEmail(value) {
2900
+ const email = clean(value);
2901
+ return email && EMAIL_PATTERN.test(email) ? email : void 0;
2902
+ }
2903
+ function buildActorIdentity(requester, requesterId) {
2904
+ const contextUserId = parseActorUserId(requesterId);
2905
+ if (requesterId !== void 0 && !contextUserId) {
2906
+ return void 0;
2907
+ }
2908
+ const requesterUserId = parseActorUserId(requester?.userId);
2909
+ if (requester?.userId !== void 0 && !requesterUserId) {
2910
+ return void 0;
2911
+ }
2912
+ const userId = contextUserId ?? requesterUserId;
2913
+ const canUseRequesterIdentity = !contextUserId || !requesterUserId || contextUserId === requesterUserId;
2914
+ const email = canUseRequesterIdentity ? cleanActorEmail(requester?.email) : void 0;
2915
+ const fullName = canUseRequesterIdentity ? cleanActorDisplayName(requester?.fullName, userId) : void 0;
2916
+ const userName = canUseRequesterIdentity ? cleanActorDisplayName(requester?.userName, userId) : void 0;
2917
+ const identity = {
2918
+ ...email ? { email } : {},
2919
+ ...fullName ? { fullName } : {},
2920
+ ...userId ? { userId } : {},
2921
+ ...userName ? { userName } : {}
2922
+ };
2923
+ return Object.keys(identity).length > 0 ? identity : void 0;
2924
+ }
2925
+ function slackActorIdentity(userId, profile) {
2926
+ const actorUserId = parseActorUserId(userId);
2927
+ if (!actorUserId) {
2928
+ throw new Error("Slack actor identity requires a user id");
2929
+ }
2930
+ const identity = buildActorIdentity(
2931
+ {
2932
+ email: profile?.email,
2933
+ fullName: profile?.fullName,
2934
+ userId: actorUserId,
2935
+ userName: profile?.userName
2936
+ },
2937
+ actorUserId
2938
+ );
2939
+ if (!identity?.userId) {
2940
+ throw new Error("Slack actor identity requires a user id");
2941
+ }
2942
+ return identity;
2943
+ }
2944
+
2861
2945
  // src/chat/credentials/context.ts
2862
2946
  function credentialUserSubjectId(context) {
2863
2947
  if (context.actor.type === "user") {
@@ -2895,11 +2979,15 @@ function parseCredentialContext(value) {
2895
2979
  function parseActor(value) {
2896
2980
  if (value && typeof value === "object") {
2897
2981
  const record = value;
2898
- if (record.type === "user" && typeof record.userId === "string" && record.userId) {
2899
- return { type: "user", userId: record.userId };
2982
+ const userId = parseActorUserId(
2983
+ typeof record.userId === "string" ? record.userId : void 0
2984
+ );
2985
+ if (record.type === "user" && userId) {
2986
+ return { type: "user", userId };
2900
2987
  }
2901
- if (record.type === "system" && typeof record.id === "string" && record.id) {
2902
- return { type: "system", id: record.id };
2988
+ const systemId = typeof record.id === "string" && record.id.length > 0 && record.id === record.id.trim() && record.id.toLowerCase() !== "unknown" ? record.id : void 0;
2989
+ if (record.type === "system" && systemId) {
2990
+ return { type: "system", id: systemId };
2903
2991
  }
2904
2992
  }
2905
2993
  return void 0;
@@ -2907,7 +2995,10 @@ function parseActor(value) {
2907
2995
  function parseSubject(value) {
2908
2996
  if (value && typeof value === "object") {
2909
2997
  const record = value;
2910
- if (record.type === "user" && typeof record.userId === "string" && record.userId && record.allowedWhen === "private-direct-conversation") {
2998
+ const userId = parseActorUserId(
2999
+ typeof record.userId === "string" ? record.userId : void 0
3000
+ );
3001
+ if (record.type === "user" && userId && record.allowedWhen === "private-direct-conversation") {
2911
3002
  if (!record.binding || typeof record.binding !== "object") {
2912
3003
  return void 0;
2913
3004
  }
@@ -2917,7 +3008,7 @@ function parseSubject(value) {
2917
3008
  }
2918
3009
  return {
2919
3010
  type: "user",
2920
- userId: record.userId,
3011
+ userId,
2921
3012
  allowedWhen: "private-direct-conversation",
2922
3013
  binding: {
2923
3014
  type: "slack-direct-conversation",
@@ -3618,6 +3709,10 @@ export {
3618
3709
  parsePluginManifest,
3619
3710
  parseInlinePluginManifest,
3620
3711
  CredentialUnavailableError,
3712
+ parseActorUserId,
3713
+ isActorUserId,
3714
+ buildActorIdentity,
3715
+ slackActorIdentity,
3621
3716
  parseCredentialContext,
3622
3717
  hasRequiredOAuthScope,
3623
3718
  buildOAuthTokenRequest,
@@ -2,7 +2,7 @@ import {
2
2
  getPluginForSkillPath,
3
3
  getPluginSkillRoots,
4
4
  logWarn
5
- } from "./chunk-6QWWMZCK.js";
5
+ } from "./chunk-OIIXZOOC.js";
6
6
  import {
7
7
  skillRoots
8
8
  } from "./chunk-KVZL5NZS.js";
@@ -2,12 +2,12 @@ import {
2
2
  SANDBOX_WORKSPACE_ROOT,
3
3
  getStateAdapter,
4
4
  toOptionalTrimmed
5
- } from "./chunk-NWU2Z6SM.js";
5
+ } from "./chunk-MT23VNOH.js";
6
6
  import {
7
7
  getPluginRuntimeDependencies,
8
8
  getPluginRuntimePostinstall,
9
9
  withSpan
10
- } from "./chunk-6QWWMZCK.js";
10
+ } from "./chunk-OIIXZOOC.js";
11
11
 
12
12
  // src/chat/sandbox/runtime-dependency-snapshots.ts
13
13
  import { createHash } from "crypto";
package/dist/cli/check.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  parseSkillFile
3
- } from "../chunk-YL5G5YC4.js";
3
+ } from "../chunk-V47RLIO2.js";
4
4
  import {
5
5
  parseInlinePluginManifest,
6
6
  parsePluginManifest
7
- } from "../chunk-6QWWMZCK.js";
7
+ } from "../chunk-OIIXZOOC.js";
8
8
  import "../chunk-Z3YD6NHK.js";
9
9
  import {
10
10
  JUNIOR_CONVERSATION_WORK_CALLBACK_ROUTE,
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  resolveRuntimeDependencySnapshot
3
- } from "../chunk-KWEE2436.js";
3
+ } from "../chunk-YGGH2742.js";
4
4
  import {
5
5
  disconnectStateAdapter
6
- } from "../chunk-NWU2Z6SM.js";
6
+ } from "../chunk-MT23VNOH.js";
7
7
  import {
8
8
  getPluginProviders,
9
9
  getPluginRuntimeDependencies,
10
10
  getPluginRuntimePostinstall
11
- } from "../chunk-6QWWMZCK.js";
11
+ } from "../chunk-OIIXZOOC.js";
12
12
  import "../chunk-Z3YD6NHK.js";
13
13
  import "../chunk-KVZL5NZS.js";
14
14
  import "../chunk-2KG3PWR4.js";
package/dist/reporting.js CHANGED
@@ -9,20 +9,20 @@ import {
9
9
  listAgentTurnSessionSummaries,
10
10
  listAgentTurnSessionSummariesForConversation,
11
11
  resolveSlackConversationContextFromThreadId
12
- } from "./chunk-HFMZE67J.js";
12
+ } from "./chunk-5UIDU7XR.js";
13
13
  import {
14
14
  discoverSkills
15
- } from "./chunk-YL5G5YC4.js";
15
+ } from "./chunk-V47RLIO2.js";
16
16
  import {
17
17
  canExposeConversationPayload,
18
18
  parseSlackThreadId,
19
19
  resolveConversationPrivacy
20
- } from "./chunk-NWU2Z6SM.js";
20
+ } from "./chunk-MT23VNOH.js";
21
21
  import {
22
22
  getPluginPackageContent,
23
23
  getPluginProviders,
24
24
  isRecord
25
- } from "./chunk-6QWWMZCK.js";
25
+ } from "./chunk-OIIXZOOC.js";
26
26
  import "./chunk-Z3YD6NHK.js";
27
27
  import {
28
28
  homeDir
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior",
3
- "version": "0.67.0",
3
+ "version": "0.67.1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -65,7 +65,7 @@
65
65
  "node-html-markdown": "^2.0.0",
66
66
  "yaml": "^2.9.0",
67
67
  "zod": "^4.4.3",
68
- "@sentry/junior-plugin-api": "0.67.0"
68
+ "@sentry/junior-plugin-api": "0.67.1"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@types/node": "^25.9.1",
@@ -77,7 +77,7 @@
77
77
  "typescript": "^6.0.3",
78
78
  "vercel": "^54.4.0",
79
79
  "vitest": "^4.1.7",
80
- "@sentry/junior-scheduler": "0.67.0"
80
+ "@sentry/junior-scheduler": "0.67.1"
81
81
  },
82
82
  "scripts": {
83
83
  "build": "tsup && tsc -p tsconfig.build.json --emitDeclarationOnly",