@sentry/junior 0.91.0 → 0.92.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.
Files changed (128) hide show
  1. package/dist/{agent-hooks-MXAF7RQL.js → agent-hooks-KL7I3ZRC.js} +7 -7
  2. package/dist/api/people/list.d.ts +3 -3
  3. package/dist/api/people/list.js +15 -15
  4. package/dist/api/people/list.query.d.ts +2 -2
  5. package/dist/api/people/profile.d.ts +3 -3
  6. package/dist/api/people/profile.js +12 -12
  7. package/dist/api/people/profile.query.d.ts +2 -2
  8. package/dist/api/people/shared.d.ts +13 -13
  9. package/dist/api/people/types.d.ts +12 -12
  10. package/dist/api-reference.d.ts +1 -1
  11. package/dist/app.js +365 -265
  12. package/dist/{catalog-runtime-DXE6NXHT.js → catalog-runtime-NZHMSMYC.js} +2 -2
  13. package/dist/chat/actor.d.ts +75 -0
  14. package/dist/chat/agent/prompt.d.ts +2 -2
  15. package/dist/chat/agent/request.d.ts +13 -13
  16. package/dist/chat/agent/resume.d.ts +5 -4
  17. package/dist/chat/agent/tools.d.ts +4 -2
  18. package/dist/chat/capabilities/jr-rpc-command.d.ts +1 -1
  19. package/dist/chat/conversation-privacy.d.ts +11 -0
  20. package/dist/chat/conversations/sql/migrations.d.ts +1 -1
  21. package/dist/chat/conversations/sql/schema/conversations.d.ts +2 -19
  22. package/dist/chat/conversations/sql/schema.d.ts +2 -19
  23. package/dist/chat/conversations/sql/store.d.ts +3 -3
  24. package/dist/chat/conversations/store.d.ts +4 -4
  25. package/dist/chat/credentials/context.d.ts +4 -4
  26. package/dist/chat/logging.d.ts +2 -2
  27. package/dist/chat/no-reply.d.ts +1 -1
  28. package/dist/chat/oauth-flow.d.ts +1 -1
  29. package/dist/chat/pi/messages.d.ts +2 -2
  30. package/dist/chat/pi/transcript.d.ts +15 -0
  31. package/dist/chat/plugins/agent-hooks.d.ts +5 -3
  32. package/dist/chat/plugins/credential-hooks.d.ts +2 -8
  33. package/dist/chat/prompt.d.ts +3 -6
  34. package/dist/chat/runtime/reply-executor.d.ts +1 -1
  35. package/dist/chat/runtime/turn-input.d.ts +1 -1
  36. package/dist/chat/sandbox/egress/schemas.d.ts +2 -2
  37. package/dist/chat/sandbox/egress/session.d.ts +1 -1
  38. package/dist/chat/services/context-compaction.d.ts +1 -1
  39. package/dist/chat/services/conversation-memory.d.ts +1 -1
  40. package/dist/chat/services/mcp-auth-orchestration.d.ts +1 -1
  41. package/dist/chat/services/message-actor-identity.d.ts +4 -4
  42. package/dist/chat/services/pending-auth.d.ts +2 -2
  43. package/dist/chat/services/plugin-auth-orchestration.d.ts +1 -1
  44. package/dist/chat/services/provider-default-config.d.ts +1 -1
  45. package/dist/chat/services/subscribed-decision.d.ts +1 -1
  46. package/dist/chat/services/turn-result.d.ts +1 -1
  47. package/dist/chat/services/turn-session-record.d.ts +11 -8
  48. package/dist/chat/services/turn-thinking-level.d.ts +1 -1
  49. package/dist/chat/slack/assistant-thread/title.d.ts +1 -1
  50. package/dist/chat/slack/tools/canvas/create.d.ts +41 -1
  51. package/dist/chat/slack/tools/canvas/edit.d.ts +60 -1
  52. package/dist/chat/slack/tools/canvas/read.d.ts +60 -1
  53. package/dist/chat/slack/tools/canvas/write.d.ts +41 -1
  54. package/dist/chat/slack/tools/channel-list-messages.d.ts +45 -1
  55. package/dist/chat/slack/tools/context.d.ts +3 -3
  56. package/dist/chat/slack/tools/list/add-items.d.ts +42 -1
  57. package/dist/chat/slack/tools/list/create.d.ts +40 -1
  58. package/dist/chat/slack/tools/list/get-items.d.ts +40 -1
  59. package/dist/chat/slack/tools/list/update-item.d.ts +46 -1
  60. package/dist/chat/slack/tools/message-add-reaction.d.ts +40 -1
  61. package/dist/chat/slack/tools/send-message.d.ts +45 -1
  62. package/dist/chat/slack/tools/thread-read.d.ts +44 -1
  63. package/dist/chat/slack/tools/user-lookup.d.ts +45 -1
  64. package/dist/chat/slack/user.d.ts +3 -3
  65. package/dist/chat/slack/vision-context.d.ts +2 -2
  66. package/dist/chat/sql/schema.d.ts +2 -19
  67. package/dist/chat/state/conversation-details.d.ts +4 -4
  68. package/dist/chat/state/conversation.d.ts +1 -1
  69. package/dist/chat/state/session-log.d.ts +140 -26
  70. package/dist/chat/state/turn-session.d.ts +18 -5
  71. package/dist/chat/task-execution/slack-work.d.ts +2 -2
  72. package/dist/chat/task-execution/state.d.ts +4 -4
  73. package/dist/chat/tool-support/zod-tool.d.ts +16 -4
  74. package/dist/chat/tools/advisor/tool.d.ts +41 -1
  75. package/dist/chat/tools/definition.d.ts +10 -0
  76. package/dist/chat/tools/execute-tool.d.ts +41 -1
  77. package/dist/chat/tools/resource-events.d.ts +124 -3
  78. package/dist/chat/tools/runtime/report-progress.d.ts +40 -1
  79. package/dist/chat/tools/sandbox/bash.d.ts +41 -1
  80. package/dist/chat/tools/sandbox/edit-file.d.ts +44 -1
  81. package/dist/chat/tools/sandbox/find-files.d.ts +42 -1
  82. package/dist/chat/tools/sandbox/grep.d.ts +46 -1
  83. package/dist/chat/tools/sandbox/list-dir.d.ts +41 -1
  84. package/dist/chat/tools/sandbox/read-file.d.ts +42 -1
  85. package/dist/chat/tools/sandbox/write-file.d.ts +41 -1
  86. package/dist/chat/tools/search-tools.d.ts +102 -1
  87. package/dist/chat/tools/skill/call-mcp-tool.d.ts +10 -1
  88. package/dist/chat/tools/skill/load-skill.d.ts +40 -1
  89. package/dist/chat/tools/skill/search-mcp-tools.d.ts +100 -1
  90. package/dist/chat/tools/system-time.d.ts +38 -1
  91. package/dist/chat/tools/types.d.ts +4 -4
  92. package/dist/chat/tools/web/fetch-tool.d.ts +41 -1
  93. package/dist/chat/tools/web/image-generate.d.ts +62 -1
  94. package/dist/chat/tools/web/search.d.ts +41 -1
  95. package/dist/{chunk-HXL2ZV74.js → chunk-3F54A3YM.js} +82 -11
  96. package/dist/{chunk-ZGDTBSQ7.js → chunk-56SIBIVF.js} +930 -660
  97. package/dist/{chunk-DACFZ5CI.js → chunk-7K3JALTQ.js} +18 -12
  98. package/dist/{chunk-IRRMABQD.js → chunk-APIVR44P.js} +10 -10
  99. package/dist/{chunk-XP7F4LYB.js → chunk-BTH37NCU.js} +4 -4
  100. package/dist/{chunk-5GBUZI6M.js → chunk-H7KUZAMZ.js} +4 -4
  101. package/dist/{chunk-PIB4EOW6.js → chunk-IWKVXO3F.js} +19 -72
  102. package/dist/{chunk-TYUXD3FN.js → chunk-MC4ZZE4Q.js} +1 -1
  103. package/dist/{chunk-2S7M37HY.js → chunk-NIFONS4W.js} +86 -47
  104. package/dist/{chunk-AOISHLSF.js → chunk-O6ZYIHFC.js} +143 -20
  105. package/dist/{chunk-GCWD2VHP.js → chunk-OQQU3SD6.js} +285 -124
  106. package/dist/{chunk-NW7AXDA5.js → chunk-OZJXGX4U.js} +2 -2
  107. package/dist/{chunk-KC42JAAQ.js → chunk-PFXC67GJ.js} +3 -3
  108. package/dist/{chunk-QCKGI74V.js → chunk-PGR4WGZW.js} +10 -10
  109. package/dist/{chunk-ZUK7BL63.js → chunk-S6QKIGRM.js} +66 -54
  110. package/dist/{chunk-IX76WFJV.js → chunk-TF5VLGA7.js} +10 -10
  111. package/dist/{chunk-AN2437E3.js → chunk-YTNPU7I2.js} +2 -2
  112. package/dist/{chunk-3MPOMK3K.js → chunk-YYBX2BD5.js} +61 -3
  113. package/dist/{chunk-ONFOWI2D.js → chunk-Z4FOYQXW.js} +16 -15
  114. package/dist/{chunk-RS6ANWVT.js → chunk-ZATRYQLR.js} +1 -1
  115. package/dist/cli/chat.js +20 -20
  116. package/dist/cli/check.js +3 -3
  117. package/dist/cli/plugins.js +9 -9
  118. package/dist/cli/snapshot-warmup.js +6 -6
  119. package/dist/cli/upgrade.js +9 -9
  120. package/dist/{db-CXI7PR5U.js → db-GCK43OO3.js} +4 -4
  121. package/dist/instrumentation.js +1 -1
  122. package/dist/reporting/conversations.d.ts +3 -3
  123. package/dist/reporting.d.ts +1 -1
  124. package/dist/reporting.js +48 -50
  125. package/dist/{runner-FKL3RAHA.js → runner-Y73GZ2VE.js} +20 -18
  126. package/dist/{validation-I7GD2YWS.js → validation-KYAWETI4.js} +3 -3
  127. package/package.json +6 -6
  128. package/dist/chat/requester.d.ts +0 -68
@@ -11,10 +11,10 @@ import {
11
11
  persistRunningSessionRecord,
12
12
  persistTimeoutSessionRecord,
13
13
  persistYieldSessionRecord
14
- } from "./chunk-ONFOWI2D.js";
14
+ } from "./chunk-Z4FOYQXW.js";
15
15
  import {
16
16
  JUNIOR_THREAD_STATE_TTL_MS
17
- } from "./chunk-KC42JAAQ.js";
17
+ } from "./chunk-PFXC67GJ.js";
18
18
  import {
19
19
  buildNonInteractiveShellScript,
20
20
  createSandboxInstance,
@@ -23,7 +23,7 @@ import {
23
23
  isSnapshotMissingError,
24
24
  resolveRuntimeDependencySnapshot,
25
25
  runNonInteractiveCommand
26
- } from "./chunk-5GBUZI6M.js";
26
+ } from "./chunk-H7KUZAMZ.js";
27
27
  import {
28
28
  JUNIOR_PERSONALITY,
29
29
  buildPluginSystemPromptContributions,
@@ -32,18 +32,18 @@ import {
32
32
  containsNoReplyMarker,
33
33
  formatSlackLink,
34
34
  isNoReplyMarker,
35
- renderCurrentInstruction,
36
35
  stripNoReplyMarker
37
- } from "./chunk-PIB4EOW6.js";
36
+ } from "./chunk-IWKVXO3F.js";
38
37
  import {
39
- escapeXml,
38
+ instructionActors,
39
+ instructionProvenanceFor,
40
40
  loadConnectedMcpProviders,
41
41
  recordAuthorizationRequested,
42
42
  recordMcpProviderConnected,
43
43
  recordSubagentEnded,
44
44
  recordSubagentStarted,
45
45
  recordToolExecutionStarted
46
- } from "./chunk-GCWD2VHP.js";
46
+ } from "./chunk-OQQU3SD6.js";
47
47
  import {
48
48
  SlackActionError,
49
49
  createPluginHookRunner,
@@ -60,10 +60,10 @@ import {
60
60
  normalizeSlackConversationId,
61
61
  resolveChannelCapabilities,
62
62
  withSlackRetries
63
- } from "./chunk-DACFZ5CI.js";
63
+ } from "./chunk-7K3JALTQ.js";
64
64
  import {
65
65
  createPluginLogger
66
- } from "./chunk-NW7AXDA5.js";
66
+ } from "./chunk-OZJXGX4U.js";
67
67
  import {
68
68
  SANDBOX_DATA_ROOT,
69
69
  SANDBOX_SKILLS_ROOT,
@@ -74,11 +74,11 @@ import {
74
74
  import {
75
75
  acquireActiveLock,
76
76
  getStateAdapter
77
- } from "./chunk-RS6ANWVT.js";
77
+ } from "./chunk-ZATRYQLR.js";
78
78
  import {
79
79
  getConversationStore,
80
80
  getDb
81
- } from "./chunk-2S7M37HY.js";
81
+ } from "./chunk-NIFONS4W.js";
82
82
  import {
83
83
  GEN_AI_PROVIDER_NAME,
84
84
  GEN_AI_SERVER_ADDRESS,
@@ -87,6 +87,7 @@ import {
87
87
  botConfig,
88
88
  completeObject,
89
89
  completeText,
90
+ escapeXml,
90
91
  extractAssistantText,
91
92
  getGatewayApiKey,
92
93
  getPiGatewayApiKey,
@@ -99,19 +100,22 @@ import {
99
100
  nextProviderRetry,
100
101
  normalizeSlackEmojiName,
101
102
  normalizeToolNameFromResult,
103
+ renderCurrentInstruction,
102
104
  resolveGatewayModel,
103
105
  stripRuntimeTurnContext
104
- } from "./chunk-HXL2ZV74.js";
106
+ } from "./chunk-3F54A3YM.js";
105
107
  import {
106
108
  parseSlackThreadId,
107
109
  resolveConversationPrivacy,
108
110
  runWithConversationPrivacy,
111
+ toCanonicalInputMessage,
112
+ toCanonicalOutputMessage,
109
113
  toGenAiMessageMetadata,
110
114
  toGenAiMessagesTraceAttributes,
111
115
  toGenAiPayloadMetadata,
112
116
  toGenAiPayloadTraceAttributes,
113
117
  toGenAiTextMetadata
114
- } from "./chunk-3MPOMK3K.js";
118
+ } from "./chunk-YYBX2BD5.js";
115
119
  import {
116
120
  parseDestination
117
121
  } from "./chunk-GHGPTPBL.js";
@@ -120,20 +124,21 @@ import {
120
124
  findSkillByName,
121
125
  loadSkillsByName,
122
126
  parseSkillInvocation
123
- } from "./chunk-AN2437E3.js";
127
+ } from "./chunk-YTNPU7I2.js";
124
128
  import {
125
129
  CredentialUnavailableError,
126
130
  credentialContextSchema,
127
131
  pluginCatalogRuntime,
128
132
  resolveAuthTokenPlaceholder,
129
133
  resolvePluginCommandEnv
130
- } from "./chunk-XP7F4LYB.js";
134
+ } from "./chunk-BTH37NCU.js";
131
135
  import {
132
- createRequester,
136
+ createActor,
133
137
  extractGenAiUsageAttributes,
134
138
  extractGenAiUsageSummary,
135
139
  getLogContextAttributes,
136
140
  getTracePropagationHeaders,
141
+ isUserActor,
137
142
  logError,
138
143
  logException,
139
144
  logInfo,
@@ -145,7 +150,7 @@ import {
145
150
  setTags,
146
151
  summarizeMessageText,
147
152
  withSpan
148
- } from "./chunk-ZUK7BL63.js";
153
+ } from "./chunk-S6QKIGRM.js";
149
154
  import {
150
155
  isRecord
151
156
  } from "./chunk-OB42YVAE.js";
@@ -465,7 +470,7 @@ function buildChatStartAttributes(model, context, mode, conversationPrivacy) {
465
470
  ...toGenAiMessagesTraceAttributes("app.ai.input", context.messages)
466
471
  };
467
472
  const inputMessages = serializeGenAiAttribute(
468
- mode === "metadata" ? context.messages.map(toGenAiMessageMetadata) : context.messages
473
+ mode === "metadata" ? context.messages.map(toGenAiMessageMetadata) : context.messages.map(toCanonicalInputMessage)
469
474
  );
470
475
  if (inputMessages) {
471
476
  attributes["gen_ai.input.messages"] = inputMessages;
@@ -486,7 +491,7 @@ function buildChatEndAttributes(message, mode) {
486
491
  ...toGenAiMessagesTraceAttributes("app.ai.output", [message])
487
492
  };
488
493
  const outputMessages = serializeGenAiAttribute(
489
- mode === "metadata" ? [toGenAiMessageMetadata(message)] : [message]
494
+ mode === "metadata" ? [toGenAiMessageMetadata(message)] : [toCanonicalOutputMessage(message)]
490
495
  );
491
496
  if (outputMessages) {
492
497
  attributes["gen_ai.output.messages"] = outputMessages;
@@ -675,8 +680,7 @@ function buildTurnResult(input) {
675
680
  );
676
681
  const canvasCreated = successfulToolNames.has("slackCanvasCreate");
677
682
  const reactionPerformed = successfulToolNames.has("addReaction");
678
- const markerSideEffectSuccess = exactNoReplyMarker && toolErrorCount === 0 && reactionPerformed;
679
- const sideEffectOnlySuccess = markerSideEffectSuccess;
683
+ const silentCompletionSuccess = exactNoReplyMarker;
680
684
  const baseDeliveryPlan = {
681
685
  mode: "thread",
682
686
  postThreadText: true
@@ -689,7 +693,7 @@ function buildTurnResult(input) {
689
693
  const markerCategory = reactionPerformed ? "reaction" : "none";
690
694
  const markerContext = {
691
695
  slackThreadId: correlation?.threadId,
692
- slackUserId: correlation?.requesterId,
696
+ slackUserId: correlation?.actorId,
693
697
  slackChannelId: correlation?.channelId,
694
698
  runId: correlation?.runId,
695
699
  assistantUserName,
@@ -698,29 +702,22 @@ function buildTurnResult(input) {
698
702
  const markerAttributes = {
699
703
  "app.ai.no_reply_marker": true,
700
704
  "app.ai.no_reply_marker_category": markerCategory,
701
- "app.ai.no_reply_marker_accepted": sideEffectOnlySuccess && !isProviderError
705
+ "app.ai.no_reply_marker_accepted": !isProviderError
702
706
  };
703
- if (sideEffectOnlySuccess && !isProviderError) {
707
+ if (!isProviderError) {
704
708
  logInfo(
705
709
  "ai_no_reply_marker_accepted",
706
710
  markerContext,
707
711
  markerAttributes,
708
712
  "No-reply marker suppressed visible thread text"
709
713
  );
710
- } else if (!isProviderError) {
711
- logWarn(
712
- "ai_no_reply_marker_rejected",
713
- markerContext,
714
- markerAttributes,
715
- "No-reply marker requires a successful visible side effect"
716
- );
717
714
  }
718
715
  } else if (mixedNoReplyMarker) {
719
716
  logWarn(
720
717
  "ai_no_reply_marker_mixed_text",
721
718
  {
722
719
  slackThreadId: correlation?.threadId,
723
- slackUserId: correlation?.requesterId,
720
+ slackUserId: correlation?.actorId,
724
721
  slackChannelId: correlation?.channelId,
725
722
  runId: correlation?.runId,
726
723
  assistantUserName,
@@ -733,12 +730,12 @@ function buildTurnResult(input) {
733
730
  "No-reply marker appeared with visible assistant text"
734
731
  );
735
732
  }
736
- if (!primaryText && !sideEffectOnlySuccess && !isProviderError && !exactNoReplyMarker) {
733
+ if (!primaryText && !silentCompletionSuccess && !isProviderError) {
737
734
  logWarn(
738
735
  "ai_model_response_empty",
739
736
  {
740
737
  slackThreadId: correlation?.threadId,
741
- slackUserId: correlation?.requesterId,
738
+ slackUserId: correlation?.actorId,
742
739
  slackChannelId: correlation?.channelId,
743
740
  runId: correlation?.runId,
744
741
  assistantUserName,
@@ -756,7 +753,7 @@ function buildTurnResult(input) {
756
753
  let outcome;
757
754
  if (isProviderError) {
758
755
  outcome = "provider_error";
759
- } else if (primaryText || sideEffectOnlySuccess) {
756
+ } else if (primaryText || silentCompletionSuccess) {
760
757
  outcome = "success";
761
758
  } else {
762
759
  outcome = "execution_failure";
@@ -766,7 +763,7 @@ function buildTurnResult(input) {
766
763
  const escapedOrRawPayload = Boolean(primaryText) && (isExecutionEscapeResponse(primaryText) || isRawToolPayloadResponse(primaryText));
767
764
  const resolvedText = escapedOrRawPayload ? "" : responseText;
768
765
  const resolvedOutcome = escapedOrRawPayload ? "execution_failure" : outcome;
769
- const deliveryPlan = resolvedOutcome === "success" && !resolvedText && reactionPerformed ? {
766
+ const deliveryPlan = resolvedOutcome === "success" && !resolvedText ? {
770
767
  ...baseDeliveryPlan,
771
768
  postThreadText: false
772
769
  } : baseDeliveryPlan;
@@ -939,7 +936,7 @@ async function selectTurnThinkingLevel(args) {
939
936
  const logContext = {
940
937
  slackThreadId: args.context?.threadId,
941
938
  slackChannelId: args.context?.channelId,
942
- slackUserId: args.context?.requesterId,
939
+ slackUserId: args.context?.actorId,
943
940
  runId: args.context?.runId,
944
941
  modelId: args.fastModelId
945
942
  };
@@ -962,7 +959,7 @@ async function selectTurnThinkingLevel(args) {
962
959
  modelId: args.fastModelId,
963
960
  threadId: args.context?.threadId ?? "",
964
961
  channelId: args.context?.channelId ?? "",
965
- requesterId: args.context?.requesterId ?? "",
962
+ actorId: args.context?.actorId ?? "",
966
963
  runId: args.context?.runId ?? ""
967
964
  },
968
965
  prompt
@@ -1077,25 +1074,38 @@ function getSessionIdentifiers(routing) {
1077
1074
  sessionId: routing.correlation?.turnId
1078
1075
  };
1079
1076
  }
1080
- function requesterFromRouting(routing) {
1081
- return createRequester(routing.requester, {
1082
- platform: routing.requester?.platform ?? (routing.destination.platform === "slack" ? "slack" : void 0),
1083
- teamId: (routing.destination.platform === "slack" ? routing.destination.teamId : void 0) ?? routing.correlation?.teamId ?? (routing.requester?.platform === "slack" ? routing.requester.teamId : void 0),
1084
- userId: routing.correlation?.requesterId
1085
- });
1077
+ function actorFromRouting(routing) {
1078
+ if (routing.dispatch?.actor) {
1079
+ return routing.dispatch.actor;
1080
+ }
1081
+ const userActor = createActor(
1082
+ isUserActor(routing.actor) ? routing.actor : void 0,
1083
+ {
1084
+ platform: (isUserActor(routing.actor) ? routing.actor.platform : void 0) ?? (routing.destination.platform === "slack" ? "slack" : void 0),
1085
+ teamId: (routing.destination.platform === "slack" ? routing.destination.teamId : void 0) ?? routing.correlation?.teamId ?? (routing.actor?.platform === "slack" ? routing.actor.teamId : void 0),
1086
+ userId: routing.correlation?.actorId
1087
+ }
1088
+ );
1089
+ if (userActor) {
1090
+ return userActor;
1091
+ }
1092
+ if (routing.credentialContext && !("type" in routing.credentialContext.actor)) {
1093
+ return routing.credentialContext.actor;
1094
+ }
1095
+ return void 0;
1086
1096
  }
1087
- function assertRequesterDestinationMatch(routing) {
1088
- const { destination, requester } = routing;
1089
- if (!requester) {
1097
+ function assertActorDestinationMatch(routing) {
1098
+ const { destination, actor } = routing;
1099
+ if (!actor) {
1090
1100
  return;
1091
1101
  }
1092
- if (requester.platform !== destination.platform) {
1102
+ if (actor.platform !== destination.platform) {
1093
1103
  throw new TypeError(
1094
- `Requester platform "${requester.platform}" does not match destination platform "${destination.platform}"`
1104
+ `Actor platform "${actor.platform}" does not match destination platform "${destination.platform}"`
1095
1105
  );
1096
1106
  }
1097
- if (requester.platform === "slack" && destination.platform === "slack" && requester.teamId !== destination.teamId) {
1098
- throw new TypeError("Slack requester team does not match destination team");
1107
+ if (actor.platform === "slack" && destination.platform === "slack" && actor.teamId !== destination.teamId) {
1108
+ throw new TypeError("Slack actor team does not match destination team");
1099
1109
  }
1100
1110
  }
1101
1111
  function assertCorrelationDestinationMatch(routing) {
@@ -1571,7 +1581,7 @@ async function assemblePrompt(args) {
1571
1581
  source
1572
1582
  } : void 0,
1573
1583
  invocation: args.invocation,
1574
- requester: args.actorRequester,
1584
+ actor: isUserActor(args.currentActor) ? args.currentActor : void 0,
1575
1585
  artifactState: args.artifactState,
1576
1586
  configuration: args.configurationValues
1577
1587
  }) : null;
@@ -1739,7 +1749,7 @@ ${usage}
1739
1749
  const entry = await configuration.set({
1740
1750
  key,
1741
1751
  value,
1742
- updatedBy: deps.requesterId,
1752
+ updatedBy: deps.actorId,
1743
1753
  source: "jr-rpc"
1744
1754
  });
1745
1755
  logInfo(
@@ -3790,6 +3800,22 @@ function createListDirTool() {
3790
3800
  // src/chat/tools/web/image-generate.ts
3791
3801
  import { z as z9 } from "zod";
3792
3802
  var DEFAULT_IMAGE_MODEL = "google/gemini-3-pro-image";
3803
+ var imageGenerateOutputSchema = juniorToolResultSchema.extend({
3804
+ model: z9.string(),
3805
+ prompt: z9.string(),
3806
+ enrichedPrompt: z9.string(),
3807
+ image_count: z9.number().int().nonnegative(),
3808
+ images: z9.array(
3809
+ z9.object({
3810
+ filename: z9.string(),
3811
+ path: z9.string(),
3812
+ attachment_path: z9.string(),
3813
+ media_type: z9.string().optional(),
3814
+ bytes: z9.number().int().nonnegative()
3815
+ }).strict()
3816
+ ),
3817
+ delivery: z9.string()
3818
+ }).strict();
3793
3819
  var ENRICHMENT_SYSTEM_PROMPT = `You are an image prompt enrichment agent. Your job is to rewrite image generation requests to reflect a specific visual identity and mood.
3794
3820
 
3795
3821
  <personality>
@@ -3852,7 +3878,7 @@ function createImageGenerateTool(hooks, options = {}, deps = {}) {
3852
3878
  inputSchema: z9.object({
3853
3879
  prompt: z9.string().min(1).max(4e3).describe("Image generation prompt.")
3854
3880
  }),
3855
- outputSchema: juniorToolResultSchema,
3881
+ outputSchema: imageGenerateOutputSchema,
3856
3882
  execute: async ({ prompt }) => {
3857
3883
  const fetchImpl = deps.fetch ?? fetch;
3858
3884
  const apiKey = getGatewayApiKey();
@@ -3980,7 +4006,7 @@ function createCallMcpToolTool(mcpToolManager) {
3980
4006
  description: "Call an active MCP tool by exact tool_name. Use searchMcpTools to discover tool names and schemas; copy required provider fields into arguments. Do not call with only tool_name unless the discovered tool has no arguments. Authorization is handled by the runtime when required.",
3981
4007
  inputSchema: z10.object({
3982
4008
  tool_name: z10.string().min(1).describe("Exact MCP tool_name from searchMcpTools."),
3983
- arguments: z10.unknown().describe(
4009
+ arguments: z10.record(z10.string(), z10.unknown()).describe(
3984
4010
  'Arguments matching the disclosed MCP tool schema, for example { "query": "..." } when searchMcpTools shows query is required.'
3985
4011
  ).optional()
3986
4012
  }).passthrough(),
@@ -4219,6 +4245,38 @@ function toActiveMcpCatalogSummaries(toolDefs) {
4219
4245
  // src/chat/tools/skill/search-mcp-tools.ts
4220
4246
  var DEFAULT_MAX_RESULTS = 5;
4221
4247
  var MAX_RESULTS = 20;
4248
+ var providerSummarySchema = z12.object({
4249
+ provider: z12.string(),
4250
+ description: z12.string(),
4251
+ active: z12.boolean()
4252
+ }).strict();
4253
+ var mcpCallExampleSchema = z12.object({
4254
+ tool_name: z12.string(),
4255
+ arguments: z12.record(z12.string(), z12.string())
4256
+ }).strict();
4257
+ var exposedToolSummarySchema = z12.object({
4258
+ tool_name: z12.string(),
4259
+ mcp_tool_name: z12.string(),
4260
+ provider: z12.string(),
4261
+ title: z12.string().optional(),
4262
+ description: z12.string(),
4263
+ signature: z12.string(),
4264
+ call: mcpCallExampleSchema,
4265
+ input_schema: z12.record(z12.string(), z12.unknown()),
4266
+ input_schema_summary: z12.string(),
4267
+ output_schema: z12.record(z12.string(), z12.unknown()).optional(),
4268
+ annotations: z12.record(z12.string(), z12.unknown()).optional()
4269
+ }).strict();
4270
+ var searchMcpToolsOutputSchema = juniorToolResultSchema.extend({
4271
+ query: z12.string().nullable(),
4272
+ provider: z12.string().nullable(),
4273
+ total_active_tools: z12.number().int().nonnegative(),
4274
+ returned_tools: z12.number().int().nonnegative(),
4275
+ execution_tool: z12.literal("callMcpTool"),
4276
+ execution_example: mcpCallExampleSchema,
4277
+ available_providers: z12.array(providerSummarySchema),
4278
+ tools: z12.array(exposedToolSummarySchema)
4279
+ }).strict();
4222
4280
  function normalize(value) {
4223
4281
  return value.toLowerCase().replace(/[^a-z0-9_]+/g, " ").trim();
4224
4282
  }
@@ -4346,7 +4404,7 @@ function createSearchMcpToolsTool(mcpToolManager) {
4346
4404
  ).optional(),
4347
4405
  max_results: z12.number().int().min(1).max(MAX_RESULTS).describe("Maximum matching tool descriptors to return.").optional()
4348
4406
  }).strict(),
4349
- outputSchema: juniorToolResultSchema,
4407
+ outputSchema: searchMcpToolsOutputSchema,
4350
4408
  execute: async ({ query, provider, max_results }) => {
4351
4409
  if (provider) {
4352
4410
  await mcpToolManager.activateProvider(provider);
@@ -4368,6 +4426,13 @@ function createSearchMcpToolsTool(mcpToolManager) {
4368
4426
  provider: provider ?? null,
4369
4427
  total_active_tools: catalog.length,
4370
4428
  returned_tools: matches.length,
4429
+ execution_tool: "callMcpTool",
4430
+ execution_example: {
4431
+ tool_name: "<returned tool_name>",
4432
+ arguments: {
4433
+ "<argument>": "<value from input_schema>"
4434
+ }
4435
+ },
4371
4436
  available_providers: providers,
4372
4437
  tools: matches.map(toExposedToolSummary)
4373
4438
  };
@@ -5965,6 +6030,17 @@ var editReplacementSchema2 = z22.object({
5965
6030
  ),
5966
6031
  newText: z22.string().describe("Replacement Canvas markdown for this edit.")
5967
6032
  });
6033
+ var slackCanvasEditOutputSchema = juniorToolResultSchema.extend({
6034
+ canvas_id: z22.string().optional(),
6035
+ title: z22.string().optional(),
6036
+ permalink: z22.string().optional(),
6037
+ diff: z22.string().optional(),
6038
+ first_changed_line: z22.number().int().positive().optional(),
6039
+ replacements: z22.number().int().nonnegative().optional(),
6040
+ normalized_heading_count: z22.number().int().nonnegative().optional(),
6041
+ summary: z22.string().optional(),
6042
+ deduplicated: z22.boolean().optional()
6043
+ }).strict();
5968
6044
  function createSlackCanvasEditTool(state) {
5969
6045
  return zodTool({
5970
6046
  description: "Edit one Slack canvas with exact markdown replacements. Use for precise changes to existing Canvas content; prefer this over slackCanvasWrite for targeted changes. Each oldText must match exactly, be unique, and not overlap another edit. Returns a diff. Multiple changes to the same canvas: use one edits[] call.",
@@ -5976,7 +6052,7 @@ function createSlackCanvasEditTool(state) {
5976
6052
  "Exact replacements matched against the current Canvas body, not incrementally."
5977
6053
  )
5978
6054
  }),
5979
- outputSchema: juniorToolResultSchema,
6055
+ outputSchema: slackCanvasEditOutputSchema,
5980
6056
  execute: async ({ canvas, edits }) => {
5981
6057
  const target = resolveCanvasTarget(canvas);
5982
6058
  if (!target.ok) {
@@ -6051,6 +6127,18 @@ function createSlackCanvasEditTool(state) {
6051
6127
 
6052
6128
  // src/chat/slack/tools/canvas/read.ts
6053
6129
  import { z as z23 } from "zod";
6130
+ var slackCanvasReadOutputSchema = juniorToolResultSchema.extend({
6131
+ canvas_id: z23.string().optional(),
6132
+ title: z23.string().optional(),
6133
+ permalink: z23.string().optional(),
6134
+ mimetype: z23.string().optional(),
6135
+ filetype: z23.string().optional(),
6136
+ original_byte_length: z23.number().int().nonnegative().optional(),
6137
+ content: z23.string().optional(),
6138
+ start_line: z23.number().int().positive().optional(),
6139
+ end_line: z23.number().int().nonnegative().optional(),
6140
+ total_lines: z23.number().int().nonnegative().optional()
6141
+ }).strict();
6054
6142
  function createSlackCanvasReadTool() {
6055
6143
  return zodTool({
6056
6144
  description: "Read a bounded line range from a Slack canvas as markdown. Use when you need exact Canvas contents to verify facts or make edits safely. Do not use for generic web pages \u2014 use webFetch for those.",
@@ -6062,7 +6150,7 @@ function createSlackCanvasReadTool() {
6062
6150
  offset: z23.coerce.number().int().min(1).describe("1-indexed line number to start reading from.").optional(),
6063
6151
  limit: z23.coerce.number().int().min(1).describe("Maximum number of lines to read. Defaults to 1000.").optional()
6064
6152
  }),
6065
- outputSchema: juniorToolResultSchema,
6153
+ outputSchema: slackCanvasReadOutputSchema,
6066
6154
  execute: async ({ canvas, offset, limit }) => {
6067
6155
  const target = resolveCanvasTarget(canvas);
6068
6156
  if (!target.ok) {
@@ -7362,15 +7450,11 @@ function createAdvisorTool(context) {
7362
7450
  ].join("\n");
7363
7451
  const advisorInputMessage = {
7364
7452
  role: "user",
7365
- content: [
7366
- {
7367
- type: "text",
7368
- text: requestText
7369
- }
7370
- ]
7453
+ content: [{ type: "text", text: requestText }],
7454
+ timestamp: Date.now()
7371
7455
  };
7372
7456
  const advisorInputMessagesAttribute = serializeGenAiAttribute(
7373
- conversationPrivacy !== "public" ? [toGenAiMessageMetadata(advisorInputMessage)] : [advisorInputMessage]
7457
+ conversationPrivacy !== "public" ? [toGenAiMessageMetadata(advisorInputMessage)] : [toCanonicalInputMessage(advisorInputMessage)]
7374
7458
  );
7375
7459
  return await withSpan(
7376
7460
  `invoke_agent ${context.config.modelId}`,
@@ -7421,7 +7505,7 @@ function createAdvisorTool(context) {
7421
7505
  const newAdvisorMessages = advisorAgent.state.messages.slice(beforeMessageCount);
7422
7506
  const outputMessages = newAdvisorMessages.filter(isAssistantMessage);
7423
7507
  const outputMessagesAttribute = serializeGenAiAttribute(
7424
- conversationPrivacy !== "public" ? outputMessages.map(toGenAiMessageMetadata) : outputMessages
7508
+ conversationPrivacy !== "public" ? outputMessages.map(toGenAiMessageMetadata) : outputMessages.map(toCanonicalOutputMessage)
7425
7509
  );
7426
7510
  setSpanAttributes({
7427
7511
  ...outputMessagesAttribute ? { "gen_ai.output.messages": outputMessagesAttribute } : {},
@@ -8516,7 +8600,7 @@ function upstreamPermissionDeniedText(value) {
8516
8600
  ...sso ? [`Provider SSO: ${sso}`] : [],
8517
8601
  ...command ? [`Command: ${command}`] : [],
8518
8602
  "",
8519
- "Junior had a credential lease for this grant and forwarded the request. Do not diagnose this as a missing user token or a local Junior runtime block; diagnose provider-side permissions, installation scope, SSO, or requester-provider account access.",
8603
+ "Junior had a credential lease for this grant and forwarded the request. Do not diagnose this as a missing user token or a local Junior runtime block; diagnose provider-side permissions, installation scope, SSO, or actor-provider account access.",
8520
8604
  ...stderr ? ["", `stderr:
8521
8605
  ${stderr}`] : [],
8522
8606
  ...stdout ? ["", `stdout:
@@ -8586,421 +8670,151 @@ import { PluginToolInputError } from "@sentry/junior-plugin-api";
8586
8670
  // src/chat/services/plugin-auth-orchestration.ts
8587
8671
  import { THREAD_STATE_TTL_MS as THREAD_STATE_TTL_MS2 } from "chat";
8588
8672
 
8589
- // src/chat/mcp/auth-store.ts
8673
+ // src/chat/oauth-flow.ts
8674
+ import { randomBytes } from "crypto";
8590
8675
  import {
8591
8676
  sourceSchema
8592
8677
  } from "@sentry/junior-plugin-api";
8593
- var MCP_AUTH_SESSION_PREFIX = "junior:mcp_auth_session";
8594
- var MCP_AUTH_CREDENTIALS_PREFIX = "junior:mcp_auth_credentials";
8595
- var MCP_AUTH_SESSION_INDEX_PREFIX = "junior:mcp_auth_session_index";
8596
- var MCP_SERVER_SESSION_PREFIX = "junior:mcp_server_session";
8597
- var MCP_AUTH_SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
8598
- var MCP_AUTH_CREDENTIALS_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
8599
- var MCP_SERVER_SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
8600
- function sessionKey(authSessionId) {
8601
- return `${MCP_AUTH_SESSION_PREFIX}:${authSessionId}`;
8602
- }
8603
- function credentialsKey(userId, provider) {
8604
- return `${MCP_AUTH_CREDENTIALS_PREFIX}:${userId}:${provider}`;
8605
- }
8606
- function sessionIndexKey(userId, provider) {
8607
- return `${MCP_AUTH_SESSION_INDEX_PREFIX}:${userId}:${provider}`;
8678
+
8679
+ // src/chat/oauth-authorization-message.ts
8680
+ function formatOAuthAuthorizationMessage(args) {
8681
+ return `${formatSlackLink(args.authorizationUrl, args.label)}. ${args.completionText}`;
8608
8682
  }
8609
- function serverSessionKey(userId, provider) {
8610
- return `${MCP_SERVER_SESSION_PREFIX}:${userId}:${provider}`;
8683
+
8684
+ // src/chat/oauth-flow.ts
8685
+ var OAUTH_STATE_TTL_MS = 10 * 60 * 1e3;
8686
+ function optionalString(value) {
8687
+ return typeof value === "string" ? value : void 0;
8611
8688
  }
8612
- function parseSessionIndex(value) {
8613
- if (typeof value !== "string") {
8614
- return [];
8689
+ function parseOAuthStatePayload(value) {
8690
+ if (!isRecord(value)) {
8691
+ return void 0;
8615
8692
  }
8616
- try {
8617
- const parsed = JSON.parse(value);
8618
- if (!Array.isArray(parsed)) {
8619
- return [];
8620
- }
8621
- return [
8622
- ...new Set(parsed.filter((id) => typeof id === "string"))
8623
- ];
8624
- } catch {
8625
- return [];
8693
+ if (typeof value.userId !== "string" || typeof value.provider !== "string") {
8694
+ return void 0;
8626
8695
  }
8627
- }
8628
- function parseMcpAuthSession(value) {
8629
- if (typeof value !== "string") {
8696
+ const destination = parseDestination(value.destination);
8697
+ if (value.destination !== void 0 && !destination) {
8630
8698
  return void 0;
8631
8699
  }
8632
- try {
8633
- const parsed = JSON.parse(value);
8634
- if (!isRecord(parsed)) {
8635
- return void 0;
8636
- }
8637
- if (typeof parsed.authSessionId !== "string" || typeof parsed.provider !== "string" || typeof parsed.userId !== "string" || typeof parsed.conversationId !== "string" || typeof parsed.sessionId !== "string" || typeof parsed.userMessage !== "string" || typeof parsed.createdAtMs !== "number" || typeof parsed.updatedAtMs !== "number") {
8638
- return void 0;
8639
- }
8640
- const destination = parsed.destination === void 0 ? void 0 : parseDestination(parsed.destination);
8641
- if (parsed.destination !== void 0 && !destination) {
8642
- return void 0;
8643
- }
8644
- const source = parsed.source === void 0 ? void 0 : sourceSchema.safeParse(parsed.source);
8645
- if (parsed.source !== void 0 && (!source || !source.success)) {
8646
- return void 0;
8647
- }
8648
- return {
8649
- authSessionId: parsed.authSessionId,
8650
- provider: parsed.provider,
8651
- userId: parsed.userId,
8652
- conversationId: parsed.conversationId,
8653
- ...destination ? { destination } : {},
8654
- ...source?.success ? { source: source.data } : {},
8655
- sessionId: parsed.sessionId,
8656
- userMessage: parsed.userMessage,
8657
- createdAtMs: parsed.createdAtMs,
8658
- updatedAtMs: parsed.updatedAtMs,
8659
- ...typeof parsed.channelId === "string" ? { channelId: parsed.channelId } : {},
8660
- ...typeof parsed.threadTs === "string" ? { threadTs: parsed.threadTs } : {},
8661
- ...typeof parsed.toolChannelId === "string" ? { toolChannelId: parsed.toolChannelId } : {},
8662
- ...isRecord(parsed.configuration) ? { configuration: parsed.configuration } : {},
8663
- ...isRecord(parsed.artifactState) ? { artifactState: parsed.artifactState } : {},
8664
- ...typeof parsed.authorizationUrl === "string" ? { authorizationUrl: parsed.authorizationUrl } : {},
8665
- ...typeof parsed.codeVerifier === "string" ? { codeVerifier: parsed.codeVerifier } : {}
8666
- };
8667
- } catch {
8700
+ const source = value.source === void 0 ? void 0 : sourceSchema.safeParse(value.source);
8701
+ if (value.source !== void 0 && (!source || !source.success)) {
8668
8702
  return void 0;
8669
8703
  }
8670
- }
8671
- function parseServerSession(value) {
8672
- if (typeof value !== "string") {
8704
+ const pendingMessage = optionalString(value.pendingMessage);
8705
+ if (pendingMessage && !source?.success) {
8673
8706
  return void 0;
8674
8707
  }
8708
+ return {
8709
+ userId: value.userId,
8710
+ provider: value.provider,
8711
+ ...optionalString(value.channelId) ? { channelId: optionalString(value.channelId) } : {},
8712
+ ...destination ? { destination } : {},
8713
+ ...source?.success ? { source: source.data } : {},
8714
+ ...optionalString(value.threadTs) ? { threadTs: optionalString(value.threadTs) } : {},
8715
+ ...pendingMessage ? { pendingMessage } : {},
8716
+ ...isRecord(value.configuration) ? { configuration: value.configuration } : {},
8717
+ ...optionalString(value.resumeConversationId) ? { resumeConversationId: optionalString(value.resumeConversationId) } : {},
8718
+ ...optionalString(value.resumeSessionId) ? { resumeSessionId: optionalString(value.resumeSessionId) } : {},
8719
+ ...optionalString(value.scope) ? { scope: optionalString(value.scope) } : {}
8720
+ };
8721
+ }
8722
+ function formatProviderLabel(provider) {
8723
+ const displayName = pluginCatalogRuntime.getDisplayName(provider);
8724
+ if (!displayName) {
8725
+ throw new Error(`Unknown plugin provider display name: "${provider}"`);
8726
+ }
8727
+ return displayName;
8728
+ }
8729
+ function resolveBaseUrl() {
8730
+ const explicit = process.env.JUNIOR_BASE_URL?.trim();
8731
+ if (explicit) return explicit;
8732
+ const vercelProd = process.env.VERCEL_PROJECT_PRODUCTION_URL?.trim();
8733
+ if (vercelProd) return `https://${vercelProd}`;
8734
+ const vercelUrl = process.env.VERCEL_URL?.trim();
8735
+ if (vercelUrl) return `https://${vercelUrl}`;
8736
+ return void 0;
8737
+ }
8738
+ async function deliverPrivateMessage(input) {
8739
+ let client;
8675
8740
  try {
8676
- const parsed = JSON.parse(value);
8677
- if (!isRecord(parsed) || typeof parsed.sessionId !== "string" || typeof parsed.updatedAtMs !== "number") {
8678
- return void 0;
8679
- }
8680
- return {
8681
- sessionId: parsed.sessionId,
8682
- updatedAtMs: parsed.updatedAtMs
8683
- };
8741
+ client = getSlackClient();
8684
8742
  } catch {
8685
- return void 0;
8743
+ logWarn(
8744
+ "oauth_private_delivery_skip",
8745
+ {},
8746
+ { "app.reason": "missing_bot_token" },
8747
+ "Skipped private message delivery \u2014 no SLACK_BOT_TOKEN"
8748
+ );
8749
+ return false;
8686
8750
  }
8687
- }
8688
- function parseStoredCredentials(value) {
8689
- if (typeof value !== "string") {
8690
- return void 0;
8751
+ if (input.channelId) {
8752
+ try {
8753
+ if (isDmChannel(input.channelId)) {
8754
+ await postSlackMessage({
8755
+ channelId: input.channelId,
8756
+ text: input.text,
8757
+ threadTs: input.threadTs
8758
+ });
8759
+ } else {
8760
+ await postSlackEphemeralMessage({
8761
+ channelId: input.channelId,
8762
+ userId: input.userId,
8763
+ text: input.text,
8764
+ threadTs: input.threadTs
8765
+ });
8766
+ }
8767
+ return "in_context";
8768
+ } catch (error) {
8769
+ logWarn(
8770
+ "oauth_private_delivery_failed",
8771
+ {},
8772
+ {
8773
+ "app.slack.error": error instanceof Error ? error.message : String(error),
8774
+ "app.slack.channel": input.channelId
8775
+ },
8776
+ "Private message delivery failed, falling back to DM"
8777
+ );
8778
+ }
8691
8779
  }
8692
8780
  try {
8693
- const parsed = JSON.parse(value);
8694
- if (!isRecord(parsed)) {
8695
- return void 0;
8781
+ const dmChannelId = (await withSlackRetries(
8782
+ () => client.conversations.open({ users: input.userId }),
8783
+ 3,
8784
+ {
8785
+ action: "conversations.open",
8786
+ spanAttributes: { "app.slack.user_id": input.userId }
8787
+ }
8788
+ )).channel?.id;
8789
+ if (!dmChannelId) {
8790
+ logWarn(
8791
+ "oauth_dm_fallback_failed",
8792
+ {},
8793
+ { "app.reason": "no_dm_channel_id" },
8794
+ "conversations.open returned no channel ID"
8795
+ );
8796
+ return false;
8696
8797
  }
8798
+ await postSlackMessage({ channelId: dmChannelId, text: input.text });
8799
+ return "fallback_dm";
8800
+ } catch (error) {
8801
+ logWarn(
8802
+ "oauth_dm_fallback_failed",
8803
+ {},
8804
+ {
8805
+ "app.slack.error": error instanceof Error ? error.message : String(error)
8806
+ },
8807
+ "DM fallback delivery failed"
8808
+ );
8809
+ return false;
8810
+ }
8811
+ }
8812
+ async function startOAuthFlow(provider, input) {
8813
+ const providerConfig = pluginCatalogRuntime.getOAuthConfig(provider);
8814
+ if (!providerConfig) {
8697
8815
  return {
8698
- ...isRecord(parsed.clientInformation) ? {
8699
- clientInformation: parsed.clientInformation
8700
- } : {},
8701
- ...isRecord(parsed.discoveryState) ? {
8702
- discoveryState: parsed.discoveryState
8703
- } : {},
8704
- ...isRecord(parsed.tokens) ? { tokens: parsed.tokens } : {}
8705
- };
8706
- } catch {
8707
- return void 0;
8708
- }
8709
- }
8710
- async function getConnectedStateAdapter() {
8711
- const stateAdapter = getStateAdapter();
8712
- await stateAdapter.connect();
8713
- return stateAdapter;
8714
- }
8715
- async function getMcpAuthSession(authSessionId) {
8716
- const stateAdapter = await getConnectedStateAdapter();
8717
- return parseMcpAuthSession(await stateAdapter.get(sessionKey(authSessionId)));
8718
- }
8719
- async function putMcpAuthSession(session, ttlMs2 = MCP_AUTH_SESSION_TTL_MS) {
8720
- const stateAdapter = await getConnectedStateAdapter();
8721
- await stateAdapter.set(
8722
- sessionKey(session.authSessionId),
8723
- JSON.stringify(session),
8724
- ttlMs2
8725
- );
8726
- const nextIndex = parseSessionIndex(
8727
- await stateAdapter.get(sessionIndexKey(session.userId, session.provider))
8728
- );
8729
- if (!nextIndex.includes(session.authSessionId)) {
8730
- nextIndex.push(session.authSessionId);
8731
- }
8732
- await stateAdapter.set(
8733
- sessionIndexKey(session.userId, session.provider),
8734
- JSON.stringify(nextIndex),
8735
- ttlMs2
8736
- );
8737
- }
8738
- async function patchMcpAuthSession(authSessionId, patch) {
8739
- const current = await getMcpAuthSession(authSessionId);
8740
- if (!current) {
8741
- throw new Error(`Unknown MCP auth session: ${authSessionId}`);
8742
- }
8743
- const next = {
8744
- ...current,
8745
- ...patch,
8746
- authSessionId: current.authSessionId,
8747
- provider: current.provider,
8748
- userId: current.userId,
8749
- conversationId: current.conversationId,
8750
- ...current.destination ? { destination: current.destination } : {},
8751
- sessionId: current.sessionId,
8752
- userMessage: current.userMessage,
8753
- createdAtMs: current.createdAtMs,
8754
- updatedAtMs: Date.now()
8755
- };
8756
- await putMcpAuthSession(next);
8757
- return next;
8758
- }
8759
- async function deleteMcpAuthSession(authSessionId) {
8760
- const stateAdapter = await getConnectedStateAdapter();
8761
- const current = parseMcpAuthSession(
8762
- await stateAdapter.get(sessionKey(authSessionId))
8763
- );
8764
- await stateAdapter.delete(sessionKey(authSessionId));
8765
- if (!current) {
8766
- return;
8767
- }
8768
- const nextIndex = parseSessionIndex(
8769
- await stateAdapter.get(sessionIndexKey(current.userId, current.provider))
8770
- ).filter((id) => id !== authSessionId);
8771
- if (nextIndex.length > 0) {
8772
- await stateAdapter.set(
8773
- sessionIndexKey(current.userId, current.provider),
8774
- JSON.stringify(nextIndex),
8775
- MCP_AUTH_SESSION_TTL_MS
8776
- );
8777
- return;
8778
- }
8779
- await stateAdapter.delete(sessionIndexKey(current.userId, current.provider));
8780
- }
8781
- async function deleteMcpAuthSessionsForUserProvider(userId, provider) {
8782
- const stateAdapter = await getConnectedStateAdapter();
8783
- const indexKey = sessionIndexKey(userId, provider);
8784
- const authSessionIds = parseSessionIndex(await stateAdapter.get(indexKey));
8785
- for (const authSessionId of authSessionIds) {
8786
- await stateAdapter.delete(sessionKey(authSessionId));
8787
- }
8788
- await stateAdapter.delete(indexKey);
8789
- }
8790
- async function getLatestMcpAuthSessionForUserProvider(userId, provider) {
8791
- const stateAdapter = await getConnectedStateAdapter();
8792
- const authSessionIds = parseSessionIndex(
8793
- await stateAdapter.get(sessionIndexKey(userId, provider))
8794
- );
8795
- let latestSession;
8796
- for (const authSessionId of authSessionIds) {
8797
- const session = parseMcpAuthSession(
8798
- await stateAdapter.get(sessionKey(authSessionId))
8799
- );
8800
- if (!session) {
8801
- continue;
8802
- }
8803
- if (!latestSession || session.updatedAtMs > latestSession.updatedAtMs) {
8804
- latestSession = session;
8805
- }
8806
- }
8807
- return latestSession;
8808
- }
8809
- async function getMcpStoredOAuthCredentials(userId, provider) {
8810
- const stateAdapter = await getConnectedStateAdapter();
8811
- return parseStoredCredentials(
8812
- await stateAdapter.get(credentialsKey(userId, provider))
8813
- );
8814
- }
8815
- async function putMcpStoredOAuthCredentials(userId, provider, value, ttlMs2 = MCP_AUTH_CREDENTIALS_TTL_MS) {
8816
- const stateAdapter = await getConnectedStateAdapter();
8817
- await stateAdapter.set(
8818
- credentialsKey(userId, provider),
8819
- JSON.stringify(value),
8820
- ttlMs2
8821
- );
8822
- }
8823
- async function deleteMcpStoredOAuthCredentials(userId, provider) {
8824
- const stateAdapter = await getConnectedStateAdapter();
8825
- await stateAdapter.delete(credentialsKey(userId, provider));
8826
- }
8827
- async function getMcpServerSessionId(userId, provider) {
8828
- const stateAdapter = await getConnectedStateAdapter();
8829
- return parseServerSession(
8830
- await stateAdapter.get(serverSessionKey(userId, provider))
8831
- )?.sessionId;
8832
- }
8833
- async function putMcpServerSessionId(userId, provider, sessionId, ttlMs2 = MCP_SERVER_SESSION_TTL_MS) {
8834
- const stateAdapter = await getConnectedStateAdapter();
8835
- await stateAdapter.set(
8836
- serverSessionKey(userId, provider),
8837
- JSON.stringify({
8838
- sessionId,
8839
- updatedAtMs: Date.now()
8840
- }),
8841
- ttlMs2
8842
- );
8843
- }
8844
- async function deleteMcpServerSessionId(userId, provider) {
8845
- const stateAdapter = await getConnectedStateAdapter();
8846
- await stateAdapter.delete(serverSessionKey(userId, provider));
8847
- }
8848
-
8849
- // src/chat/credentials/unlink-provider.ts
8850
- async function unlinkProvider(userId, provider, userTokenStore) {
8851
- await Promise.all([
8852
- userTokenStore.delete(userId, provider),
8853
- deleteMcpStoredOAuthCredentials(userId, provider),
8854
- deleteMcpServerSessionId(userId, provider),
8855
- deleteMcpAuthSessionsForUserProvider(userId, provider)
8856
- ]);
8857
- }
8858
-
8859
- // src/chat/oauth-flow.ts
8860
- import { randomBytes } from "crypto";
8861
- import {
8862
- sourceSchema as sourceSchema2
8863
- } from "@sentry/junior-plugin-api";
8864
-
8865
- // src/chat/oauth-authorization-message.ts
8866
- function formatOAuthAuthorizationMessage(args) {
8867
- return `${formatSlackLink(args.authorizationUrl, args.label)}. ${args.completionText}`;
8868
- }
8869
-
8870
- // src/chat/oauth-flow.ts
8871
- var OAUTH_STATE_TTL_MS = 10 * 60 * 1e3;
8872
- function optionalString(value) {
8873
- return typeof value === "string" ? value : void 0;
8874
- }
8875
- function parseOAuthStatePayload(value) {
8876
- if (!isRecord(value)) {
8877
- return void 0;
8878
- }
8879
- if (typeof value.userId !== "string" || typeof value.provider !== "string") {
8880
- return void 0;
8881
- }
8882
- const destination = parseDestination(value.destination);
8883
- if (value.destination !== void 0 && !destination) {
8884
- return void 0;
8885
- }
8886
- const source = value.source === void 0 ? void 0 : sourceSchema2.safeParse(value.source);
8887
- if (value.source !== void 0 && (!source || !source.success)) {
8888
- return void 0;
8889
- }
8890
- const pendingMessage = optionalString(value.pendingMessage);
8891
- if (pendingMessage && !source?.success) {
8892
- return void 0;
8893
- }
8894
- return {
8895
- userId: value.userId,
8896
- provider: value.provider,
8897
- ...optionalString(value.channelId) ? { channelId: optionalString(value.channelId) } : {},
8898
- ...destination ? { destination } : {},
8899
- ...source?.success ? { source: source.data } : {},
8900
- ...optionalString(value.threadTs) ? { threadTs: optionalString(value.threadTs) } : {},
8901
- ...pendingMessage ? { pendingMessage } : {},
8902
- ...isRecord(value.configuration) ? { configuration: value.configuration } : {},
8903
- ...optionalString(value.resumeConversationId) ? { resumeConversationId: optionalString(value.resumeConversationId) } : {},
8904
- ...optionalString(value.resumeSessionId) ? { resumeSessionId: optionalString(value.resumeSessionId) } : {},
8905
- ...optionalString(value.scope) ? { scope: optionalString(value.scope) } : {}
8906
- };
8907
- }
8908
- function formatProviderLabel(provider) {
8909
- const displayName = pluginCatalogRuntime.getDisplayName(provider);
8910
- if (!displayName) {
8911
- throw new Error(`Unknown plugin provider display name: "${provider}"`);
8912
- }
8913
- return displayName;
8914
- }
8915
- function resolveBaseUrl() {
8916
- const explicit = process.env.JUNIOR_BASE_URL?.trim();
8917
- if (explicit) return explicit;
8918
- const vercelProd = process.env.VERCEL_PROJECT_PRODUCTION_URL?.trim();
8919
- if (vercelProd) return `https://${vercelProd}`;
8920
- const vercelUrl = process.env.VERCEL_URL?.trim();
8921
- if (vercelUrl) return `https://${vercelUrl}`;
8922
- return void 0;
8923
- }
8924
- async function deliverPrivateMessage(input) {
8925
- let client;
8926
- try {
8927
- client = getSlackClient();
8928
- } catch {
8929
- logWarn(
8930
- "oauth_private_delivery_skip",
8931
- {},
8932
- { "app.reason": "missing_bot_token" },
8933
- "Skipped private message delivery \u2014 no SLACK_BOT_TOKEN"
8934
- );
8935
- return false;
8936
- }
8937
- if (input.channelId) {
8938
- try {
8939
- if (isDmChannel(input.channelId)) {
8940
- await postSlackMessage({
8941
- channelId: input.channelId,
8942
- text: input.text,
8943
- threadTs: input.threadTs
8944
- });
8945
- } else {
8946
- await postSlackEphemeralMessage({
8947
- channelId: input.channelId,
8948
- userId: input.userId,
8949
- text: input.text,
8950
- threadTs: input.threadTs
8951
- });
8952
- }
8953
- return "in_context";
8954
- } catch (error) {
8955
- logWarn(
8956
- "oauth_private_delivery_failed",
8957
- {},
8958
- {
8959
- "app.slack.error": error instanceof Error ? error.message : String(error),
8960
- "app.slack.channel": input.channelId
8961
- },
8962
- "Private message delivery failed, falling back to DM"
8963
- );
8964
- }
8965
- }
8966
- try {
8967
- const dmChannelId = (await withSlackRetries(
8968
- () => client.conversations.open({ users: input.userId }),
8969
- 3,
8970
- {
8971
- action: "conversations.open",
8972
- spanAttributes: { "app.slack.user_id": input.userId }
8973
- }
8974
- )).channel?.id;
8975
- if (!dmChannelId) {
8976
- logWarn(
8977
- "oauth_dm_fallback_failed",
8978
- {},
8979
- { "app.reason": "no_dm_channel_id" },
8980
- "conversations.open returned no channel ID"
8981
- );
8982
- return false;
8983
- }
8984
- await postSlackMessage({ channelId: dmChannelId, text: input.text });
8985
- return "fallback_dm";
8986
- } catch (error) {
8987
- logWarn(
8988
- "oauth_dm_fallback_failed",
8989
- {},
8990
- {
8991
- "app.slack.error": error instanceof Error ? error.message : String(error)
8992
- },
8993
- "DM fallback delivery failed"
8994
- );
8995
- return false;
8996
- }
8997
- }
8998
- async function startOAuthFlow(provider, input) {
8999
- const providerConfig = pluginCatalogRuntime.getOAuthConfig(provider);
9000
- if (!providerConfig) {
9001
- return {
9002
- ok: false,
9003
- error: `Provider "${provider}" does not support OAuth authorization`
8816
+ ok: false,
8817
+ error: `Provider "${provider}" does not support OAuth authorization`
9004
8818
  };
9005
8819
  }
9006
8820
  const clientId = process.env[providerConfig.clientIdEnv]?.trim();
@@ -9023,7 +8837,7 @@ async function startOAuthFlow(provider, input) {
9023
8837
  await getStateAdapter().set(
9024
8838
  `oauth-state:${state}`,
9025
8839
  {
9026
- userId: input.requesterId,
8840
+ userId: input.actorId,
9027
8841
  provider,
9028
8842
  ...input.channelId ? { channelId: input.channelId } : {},
9029
8843
  ...input.destination ? { destination: input.destination } : {},
@@ -9066,7 +8880,7 @@ async function startOAuthFlow(provider, input) {
9066
8880
  delivery: await deliverPrivateMessage({
9067
8881
  channelId: input.channelId,
9068
8882
  threadTs: input.threadTs,
9069
- userId: input.requesterId,
8883
+ userId: input.actorId,
9070
8884
  text: formatOAuthAuthorizationMessage({
9071
8885
  authorizationUrl,
9072
8886
  label: `Click here to link your ${formatProviderLabel(provider)} account`,
@@ -9181,7 +8995,7 @@ function createPluginAuthOrchestration(input) {
9181
8995
  if (pendingPause) {
9182
8996
  throw pendingPause;
9183
8997
  }
9184
- if (!input.requesterId || !pluginCatalogRuntime.getOAuthConfig(provider)) {
8998
+ if (!input.actorId || !pluginCatalogRuntime.getOAuthConfig(provider)) {
9185
8999
  throw new Error(`Cannot start plugin authorization for ${provider}`);
9186
9000
  }
9187
9001
  if (input.authorizationFlowMode === "disabled") {
@@ -9198,13 +9012,13 @@ function createPluginAuthOrchestration(input) {
9198
9012
  pendingAuth: input.pendingAuth,
9199
9013
  kind: "plugin",
9200
9014
  provider,
9201
- requesterId: input.requesterId,
9015
+ actorId: input.actorId,
9202
9016
  sessionId: input.sessionId,
9203
9017
  ...options?.scope ? { scope: options.scope } : {}
9204
9018
  }) : false;
9205
9019
  if (!reusingPendingLink) {
9206
9020
  const oauthResult = await startOAuthFlow(provider, {
9207
- requesterId: input.requesterId,
9021
+ actorId: input.actorId,
9208
9022
  channelId: input.channelId,
9209
9023
  destination: input.destination,
9210
9024
  source: input.source,
@@ -9224,14 +9038,11 @@ function createPluginAuthOrchestration(input) {
9224
9038
  );
9225
9039
  }
9226
9040
  }
9227
- if (options?.unlinkExistingProvider && input.requesterId && input.userTokenStore) {
9228
- await unlinkProvider(input.requesterId, provider, input.userTokenStore);
9229
- }
9230
9041
  if (input.sessionId && recordPendingAuth) {
9231
9042
  await recordPendingAuth({
9232
9043
  kind: "plugin",
9233
9044
  provider,
9234
- requesterId: input.requesterId,
9045
+ actorId: input.actorId,
9235
9046
  ...options?.scope ? { scope: options.scope } : {},
9236
9047
  sessionId: input.sessionId,
9237
9048
  linkSentAtMs: reusingPendingLink ? input.pendingAuth.linkSentAtMs : Date.now()
@@ -9242,7 +9053,7 @@ function createPluginAuthOrchestration(input) {
9242
9053
  conversationId: input.conversationId,
9243
9054
  kind: "plugin",
9244
9055
  provider,
9245
- requesterId: input.requesterId,
9056
+ actorId: input.actorId,
9246
9057
  authorizationId: authorizationId({
9247
9058
  kind: "plugin",
9248
9059
  provider,
@@ -9279,7 +9090,7 @@ function createPluginAuthOrchestration(input) {
9279
9090
  signal.message ?? `${formatProviderLabel(provider)} credentials are required but no OAuth flow is available for this provider.`
9280
9091
  );
9281
9092
  }
9282
- if (!input.requesterId || !input.userTokenStore) {
9093
+ if (!input.actorId || !input.userTokenStore) {
9283
9094
  if (input.authorizationFlowMode === "disabled") {
9284
9095
  throw new AuthorizationFlowDisabledError("plugin", provider);
9285
9096
  }
@@ -9294,10 +9105,10 @@ function createPluginAuthOrchestration(input) {
9294
9105
  signal.message ?? `${formatProviderLabel(provider)} credentials are required but the provider is not configured for OAuth.`
9295
9106
  );
9296
9107
  }
9297
- await startAuthorizationPause(authorization.provider, {
9298
- ...authorization.scope ? { scope: authorization.scope } : {},
9299
- unlinkExistingProvider: true
9300
- });
9108
+ await startAuthorizationPause(
9109
+ authorization.provider,
9110
+ authorization.scope ? { scope: authorization.scope } : void 0
9111
+ );
9301
9112
  },
9302
9113
  getPendingPause: () => pendingPause
9303
9114
  };
@@ -9438,9 +9249,50 @@ import { z as z39 } from "zod";
9438
9249
  var SEARCH_TOOLS_NAME = "searchTools";
9439
9250
  var DEFAULT_MAX_RESULTS2 = 5;
9440
9251
  var MAX_RESULTS3 = 20;
9252
+ var MODEL_VISIBLE_DESCRIPTION_CAP = 180;
9253
+ var searchToolsSourceSchema = z39.object({
9254
+ id: z39.string(),
9255
+ description: z39.string()
9256
+ }).strict();
9257
+ var toolCallExampleSchema = z39.object({
9258
+ tool_name: z39.string(),
9259
+ arguments: z39.record(z39.string(), z39.string())
9260
+ }).strict();
9261
+ var searchToolsToolSchema = z39.object({
9262
+ tool_name: z39.string(),
9263
+ description: z39.string(),
9264
+ exposure: z39.enum(["direct", "deferred", "modelOnly", "hidden"]),
9265
+ source: z39.string().optional(),
9266
+ signature: z39.string(),
9267
+ call: toolCallExampleSchema,
9268
+ input_schema: z39.unknown(),
9269
+ input_schema_summary: z39.string(),
9270
+ call_notes: z39.array(z39.string()),
9271
+ annotations: z39.record(z39.string(), z39.unknown())
9272
+ }).strict();
9273
+ var searchToolsOutputSchema = juniorToolResultSchema.extend({
9274
+ query: z39.string().nullable(),
9275
+ source: z39.string().nullable(),
9276
+ sources: z39.array(searchToolsSourceSchema),
9277
+ total_catalog_tools: z39.number().int().nonnegative(),
9278
+ total_eligible_tools: z39.number().int().nonnegative(),
9279
+ total_matches: z39.number().int().nonnegative(),
9280
+ returned_tools: z39.number().int().nonnegative(),
9281
+ execution_tool: z39.literal("executeTool"),
9282
+ execution_example: toolCallExampleSchema,
9283
+ tools: z39.array(searchToolsToolSchema)
9284
+ }).strict();
9441
9285
  function normalize2(value) {
9442
9286
  return value.toLowerCase().replace(/[^a-z0-9_]+/g, " ").trim();
9443
9287
  }
9288
+ function summarizeModelVisibleDescription(description) {
9289
+ const paragraph = description.split(/\n\s*\n/).map((part) => part.trim()).find(Boolean) ?? "";
9290
+ const normalized = paragraph.replace(/\s+/g, " ").trim();
9291
+ if (normalized.length <= MODEL_VISIBLE_DESCRIPTION_CAP) {
9292
+ return normalized;
9293
+ }
9294
+ return `${normalized.slice(0, MODEL_VISIBLE_DESCRIPTION_CAP - 3).trimEnd()}...`;
9295
+ }
9444
9296
  function schemaText(schema) {
9445
9297
  try {
9446
9298
  return JSON.stringify(schema);
@@ -9455,6 +9307,8 @@ function searchableToolText2(name, definition) {
9455
9307
  definition.identity?.id,
9456
9308
  definition.identity?.name,
9457
9309
  definition.identity?.plugin,
9310
+ definition.source?.id,
9311
+ definition.source?.description,
9458
9312
  definition.description,
9459
9313
  definition.promptSnippet,
9460
9314
  ...definition.promptGuidelines ?? [],
@@ -9463,15 +9317,16 @@ function searchableToolText2(name, definition) {
9463
9317
  ].filter(Boolean).join(" ")
9464
9318
  );
9465
9319
  }
9466
- function searchCatalogTools(tools, query) {
9320
+ function searchCatalogTools(tools, query, source) {
9467
9321
  const entries = Object.entries(tools).sort(
9468
9322
  ([left], [right]) => left.localeCompare(right)
9469
9323
  );
9324
+ const sourceEntries = source ? entries.filter(([, definition]) => definition.source?.id === source) : entries;
9470
9325
  if (!normalize2(query)) {
9471
- return entries.map(([name]) => name);
9326
+ return sourceEntries.map(([name]) => name);
9472
9327
  }
9473
9328
  const terms = normalize2(query).split(/\s+/).filter(Boolean);
9474
- return entries.filter(([name, definition]) => {
9329
+ return sourceEntries.filter(([name, definition]) => {
9475
9330
  const text = searchableToolText2(name, definition);
9476
9331
  return terms.every((term) => text.includes(term));
9477
9332
  }).map(([name]) => name);
@@ -9482,17 +9337,128 @@ function callNotes(definition) {
9482
9337
  ...definition.promptGuidelines?.map((guideline) => guideline.trim()).filter(Boolean) ?? []
9483
9338
  ];
9484
9339
  }
9485
- function toolMetadata(name, definition) {
9340
+ function getSchemaProperties3(schema) {
9341
+ if (!schema || typeof schema !== "object" || !("properties" in schema)) {
9342
+ return {};
9343
+ }
9344
+ const properties = schema.properties;
9345
+ return properties && typeof properties === "object" && !Array.isArray(properties) ? properties : {};
9346
+ }
9347
+ function getRequiredFields3(schema) {
9348
+ if (!schema || typeof schema !== "object" || !("required" in schema)) {
9349
+ return /* @__PURE__ */ new Set();
9350
+ }
9351
+ const required = schema.required;
9352
+ return Array.isArray(required) ? new Set(
9353
+ required.filter((value) => typeof value === "string")
9354
+ ) : /* @__PURE__ */ new Set();
9355
+ }
9356
+ function formatSchemaType2(schema) {
9357
+ if (!schema || typeof schema !== "object") {
9358
+ return "unknown";
9359
+ }
9360
+ const typed = schema;
9361
+ const type = typed.type;
9362
+ if (typeof type === "string") {
9363
+ if (type === "array") {
9364
+ return `${formatSchemaType2(typed.items)}[]`;
9365
+ }
9366
+ return type;
9367
+ }
9368
+ if (Array.isArray(type)) {
9369
+ return type.filter((value) => typeof value === "string").join(" | ");
9370
+ }
9371
+ if (Array.isArray(typed.enum) && typed.enum.length > 0) {
9372
+ return typed.enum.map((value) => JSON.stringify(value)).join(" | ");
9373
+ }
9374
+ return "unknown";
9375
+ }
9376
+ function formatArgumentPlaceholder2(name, schema) {
9377
+ const type = formatSchemaType2(schema);
9378
+ if (type === "string") {
9379
+ return `<${name}>`;
9380
+ }
9381
+ if (type === "number" || type === "integer") {
9382
+ return "<number>";
9383
+ }
9384
+ if (type === "boolean") {
9385
+ return "<boolean>";
9386
+ }
9387
+ if (type.endsWith("[]")) {
9388
+ return "<array>";
9389
+ }
9390
+ if (type === "object") {
9391
+ return "<object>";
9392
+ }
9393
+ return `<${type}>`;
9394
+ }
9395
+ function formatToolSignature(name, schema) {
9396
+ const properties = getSchemaProperties3(schema);
9397
+ const required = getRequiredFields3(schema);
9398
+ const fields = Object.entries(properties).map(([field, propertySchema]) => {
9399
+ const marker = required.has(field) ? "" : "?";
9400
+ return `${field}${marker}: ${formatSchemaType2(propertySchema)}`;
9401
+ });
9402
+ return fields.length > 0 ? `${name}({ ${fields.join(", ")} })` : `${name}()`;
9403
+ }
9404
+ function formatToolCallExample(name, schema) {
9405
+ return {
9406
+ tool_name: name,
9407
+ arguments: Object.fromEntries(
9408
+ Object.entries(getSchemaProperties3(schema)).map(
9409
+ ([field, propertySchema]) => [
9410
+ field,
9411
+ formatArgumentPlaceholder2(field, propertySchema)
9412
+ ]
9413
+ )
9414
+ )
9415
+ };
9416
+ }
9417
+ function sourceSummaries(tools) {
9418
+ const sources = /* @__PURE__ */ new Map();
9419
+ for (const definition of Object.values(tools)) {
9420
+ if (!definition.source) {
9421
+ continue;
9422
+ }
9423
+ sources.set(definition.source.id, {
9424
+ id: definition.source.id,
9425
+ description: summarizeModelVisibleDescription(
9426
+ definition.source.description
9427
+ )
9428
+ });
9429
+ }
9430
+ return [...sources.values()].sort(
9431
+ (left, right) => left.id.localeCompare(right.id)
9432
+ );
9433
+ }
9434
+ function selectedSourceSummaries(tools, matches, requestedSource, knownSources) {
9435
+ if (requestedSource) {
9436
+ return knownSources.filter((source) => source.id === requestedSource);
9437
+ }
9438
+ const matchedSourceIds = new Set(
9439
+ matches.map((name) => tools[name]?.source?.id).filter((source) => Boolean(source))
9440
+ );
9441
+ return knownSources.filter((source) => matchedSourceIds.has(source.id));
9442
+ }
9443
+ function renderSearchToolsDescription(knownSources) {
9444
+ const intro = "Search the executable tool catalog. Deferred tools are grouped by source; use searchTools with source to inspect one source, then executeTool with the exact returned tool_name.";
9445
+ if (knownSources.length === 0) {
9446
+ return intro;
9447
+ }
9448
+ return [
9449
+ intro,
9450
+ "Available sources:",
9451
+ ...knownSources.map((source) => `- ${source.id}: ${source.description}`)
9452
+ ].join("\n");
9453
+ }
9454
+ function toolMetadata(name, definition, includeSource) {
9486
9455
  return {
9487
9456
  tool_name: name,
9488
- description: definition.description,
9457
+ description: summarizeModelVisibleDescription(definition.description),
9489
9458
  exposure: effectiveToolExposure(definition),
9490
- source: definition.identity ? {
9491
- type: "plugin",
9492
- id: definition.identity.id,
9493
- name: definition.identity.name,
9494
- plugin: definition.identity.plugin
9495
- } : { type: "core" },
9459
+ ...includeSource && definition.source ? { source: definition.source.id } : {},
9460
+ signature: formatToolSignature(name, definition.inputSchema),
9461
+ call: formatToolCallExample(name, definition.inputSchema),
9496
9462
  input_schema: definition.inputSchema,
9497
9463
  input_schema_summary: summarizeInputSchema(
9498
9464
  definition.inputSchema
@@ -9502,27 +9468,55 @@ function toolMetadata(name, definition) {
9502
9468
  };
9503
9469
  }
9504
9470
  function createSearchToolsTool(catalogTools) {
9471
+ const knownSources = sourceSummaries(catalogTools);
9505
9472
  return zodTool({
9506
- description: "Search the executable tool catalog. Use this to discover exact tool names, owners, schemas, and call notes before calling executeTool.",
9473
+ description: renderSearchToolsDescription(knownSources),
9507
9474
  annotations: { readOnlyHint: true, destructiveHint: false },
9508
9475
  inputSchema: z39.object({
9509
9476
  query: z39.string().nullable().describe(
9510
9477
  "Optional search terms describing the tool, owner, action, or arguments needed. Empty string lists catalog tools."
9511
9478
  ).optional(),
9479
+ source: z39.string().nullable().describe(
9480
+ "Optional source id to search within, such as a plugin source returned in sources."
9481
+ ).optional(),
9512
9482
  max_results: z39.number().int().min(1).max(MAX_RESULTS3).nullable().describe("Maximum matching catalog tool descriptors to return.").optional()
9513
9483
  }).strict(),
9514
- outputSchema: juniorToolResultSchema,
9515
- execute: async ({ query, max_results }) => {
9484
+ outputSchema: searchToolsOutputSchema,
9485
+ execute: async ({ query, source, max_results }) => {
9516
9486
  const maxResults = max_results ?? DEFAULT_MAX_RESULTS2;
9517
- const matches = searchCatalogTools(catalogTools, query ?? "").slice(
9518
- 0,
9519
- maxResults
9487
+ const requestedSource = source ?? null;
9488
+ const sourceExists = requestedSource === null || knownSources.some((candidate) => candidate.id === requestedSource);
9489
+ const allMatches = sourceExists ? searchCatalogTools(catalogTools, query ?? "", requestedSource) : [];
9490
+ const matches = allMatches.slice(0, maxResults);
9491
+ const sources = !sourceExists ? knownSources : (query ?? "").trim() ? selectedSourceSummaries(
9492
+ catalogTools,
9493
+ matches,
9494
+ requestedSource,
9495
+ knownSources
9496
+ ) : requestedSource ? knownSources.filter(
9497
+ (candidate) => candidate.id === requestedSource
9498
+ ) : knownSources;
9499
+ const totalEligibleTools = sourceExists ? searchCatalogTools(catalogTools, "", requestedSource).length : 0;
9500
+ const includePerToolSource = requestedSource === null;
9501
+ const renderedTools = matches.map(
9502
+ (name) => toolMetadata(name, catalogTools[name], includePerToolSource)
9520
9503
  );
9521
9504
  const data = {
9522
9505
  query: query ?? null,
9506
+ source: requestedSource,
9507
+ sources,
9523
9508
  total_catalog_tools: Object.keys(catalogTools).length,
9524
- returned_tools: matches.length,
9525
- tools: matches.map((name) => toolMetadata(name, catalogTools[name]))
9509
+ total_eligible_tools: totalEligibleTools,
9510
+ total_matches: allMatches.length,
9511
+ returned_tools: renderedTools.length,
9512
+ execution_tool: "executeTool",
9513
+ execution_example: {
9514
+ tool_name: "<returned tool_name>",
9515
+ arguments: {
9516
+ "<argument>": "<value from input_schema>"
9517
+ }
9518
+ },
9519
+ tools: renderedTools
9526
9520
  };
9527
9521
  return {
9528
9522
  ok: true,
@@ -9734,7 +9728,7 @@ var SANDBOX_EGRESS_LEASE_PREFIX = "sandbox-egress-lease";
9734
9728
  var DEFAULT_SESSION_TTL_MS = 30 * 60 * 1e3;
9735
9729
  function leaseKey(provider, grantName, context) {
9736
9730
  const actor = context.credentials.actor;
9737
- const actorKey = actor.type === "user" ? `user:${actor.userId}` : `system:${actor.id}`;
9731
+ const actorKey = "type" in actor ? `user:${actor.userId}` : `system:${actor.name}`;
9738
9732
  return `${SANDBOX_EGRESS_LEASE_PREFIX}:${provider}:${grantName}:${actorKey}:${context.egressId}:${context.contextId}`;
9739
9733
  }
9740
9734
  function authSignalKey(egressId, access) {
@@ -11439,132 +11433,392 @@ function createSandboxExecutor(options) {
11439
11433
  );
11440
11434
  return { result };
11441
11435
  };
11442
- const executeFindFilesTool = async (rawInput) => {
11443
- const pattern = String(rawInput.pattern ?? "");
11444
- if (!pattern) {
11445
- throw new ToolInputError("pattern is required");
11446
- }
11447
- logSandboxBootRequest("tool.findFiles");
11448
- const limit = positiveInteger(rawInput.limit);
11449
- const executors = await sessionManager.ensureToolExecutors();
11450
- const result = await withSandboxToolSpan(
11451
- "sandbox.findFiles",
11452
- "sandbox.fs.find",
11453
- {
11454
- "app.sandbox.pattern.length": pattern.length
11455
- },
11456
- async () => {
11457
- const response = await findFiles({
11458
- fs: executors.fs,
11459
- pattern,
11460
- ...typeof rawInput.path === "string" ? { path: rawInput.path } : {},
11461
- ...limit ? { limit } : {}
11462
- });
11463
- setSpanStatus("ok");
11464
- return response;
11465
- }
11436
+ const executeFindFilesTool = async (rawInput) => {
11437
+ const pattern = String(rawInput.pattern ?? "");
11438
+ if (!pattern) {
11439
+ throw new ToolInputError("pattern is required");
11440
+ }
11441
+ logSandboxBootRequest("tool.findFiles");
11442
+ const limit = positiveInteger(rawInput.limit);
11443
+ const executors = await sessionManager.ensureToolExecutors();
11444
+ const result = await withSandboxToolSpan(
11445
+ "sandbox.findFiles",
11446
+ "sandbox.fs.find",
11447
+ {
11448
+ "app.sandbox.pattern.length": pattern.length
11449
+ },
11450
+ async () => {
11451
+ const response = await findFiles({
11452
+ fs: executors.fs,
11453
+ pattern,
11454
+ ...typeof rawInput.path === "string" ? { path: rawInput.path } : {},
11455
+ ...limit ? { limit } : {}
11456
+ });
11457
+ setSpanStatus("ok");
11458
+ return response;
11459
+ }
11460
+ );
11461
+ return { result };
11462
+ };
11463
+ const executeListDirTool = async (rawInput) => {
11464
+ logSandboxBootRequest("tool.listDir");
11465
+ const limit = positiveInteger(rawInput.limit);
11466
+ const executors = await sessionManager.ensureToolExecutors();
11467
+ const result = await withSandboxToolSpan(
11468
+ "sandbox.listDir",
11469
+ "sandbox.fs.list",
11470
+ {},
11471
+ async () => {
11472
+ const response = await listDir({
11473
+ fs: executors.fs,
11474
+ ...typeof rawInput.path === "string" ? { path: rawInput.path } : {},
11475
+ ...limit ? { limit } : {}
11476
+ });
11477
+ setSpanStatus("ok");
11478
+ return response;
11479
+ }
11480
+ );
11481
+ return { result };
11482
+ };
11483
+ const execute = async (params) => {
11484
+ const rawInput = params.input ?? {};
11485
+ const bashCommand = params.toolName === "bash" ? String(rawInput.command ?? "").trim() : void 0;
11486
+ if (params.toolName === "bash") {
11487
+ if (!bashCommand) {
11488
+ throw new ToolInputError("command is required");
11489
+ }
11490
+ if (options?.runBashCustomCommand) {
11491
+ const custom = await options.runBashCustomCommand(bashCommand);
11492
+ if (custom.handled) {
11493
+ if (!custom.result) {
11494
+ throw new Error("Custom bash command handler returned no result.");
11495
+ }
11496
+ return { result: bashToolResult(custom.result) };
11497
+ }
11498
+ }
11499
+ return await executeBashTool(rawInput, bashCommand, params.signal);
11500
+ }
11501
+ try {
11502
+ if (params.toolName === "readFile") {
11503
+ return await executeReadFileTool(rawInput);
11504
+ }
11505
+ if (params.toolName === "editFile") {
11506
+ return await executeEditFileTool(rawInput);
11507
+ }
11508
+ if (params.toolName === "grep") {
11509
+ return await executeGrepTool(rawInput);
11510
+ }
11511
+ if (params.toolName === "findFiles") {
11512
+ return await executeFindFilesTool(rawInput);
11513
+ }
11514
+ if (params.toolName === "listDir") {
11515
+ return await executeListDirTool(rawInput);
11516
+ }
11517
+ if (params.toolName === "writeFile") {
11518
+ return await executeWriteFileTool(rawInput);
11519
+ }
11520
+ } catch (error) {
11521
+ if (!isSandboxCommandStreamInterruptedError(error)) {
11522
+ throw error;
11523
+ }
11524
+ return { result: sandboxStreamInterruptedResult(params.toolName) };
11525
+ }
11526
+ throw new Error(`unsupported sandbox tool: ${params.toolName}`);
11527
+ };
11528
+ return {
11529
+ configureSkills(skills) {
11530
+ availableSkills = [...skills];
11531
+ sessionManager.configureSkills(skills);
11532
+ },
11533
+ configureReferenceFiles(files) {
11534
+ referenceFiles = [...files];
11535
+ sessionManager.configureReferenceFiles(files);
11536
+ },
11537
+ getSandboxId() {
11538
+ return sessionManager.getSandboxId();
11539
+ },
11540
+ getDependencyProfileHash() {
11541
+ return sessionManager.getDependencyProfileHash();
11542
+ },
11543
+ canExecute(toolName) {
11544
+ return SANDBOX_TOOL_NAMES.has(toolName);
11545
+ },
11546
+ async createSandbox() {
11547
+ return await sessionManager.createSandbox();
11548
+ },
11549
+ execute,
11550
+ async dispose() {
11551
+ await sessionManager.dispose();
11552
+ }
11553
+ };
11554
+ }
11555
+
11556
+ // src/chat/services/mcp-auth-orchestration.ts
11557
+ import { THREAD_STATE_TTL_MS as THREAD_STATE_TTL_MS3 } from "chat";
11558
+
11559
+ // src/chat/mcp/oauth.ts
11560
+ import { randomUUID as randomUUID3 } from "crypto";
11561
+ import { StreamableHTTPClientTransport as StreamableHTTPClientTransport2 } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
11562
+
11563
+ // src/chat/mcp/auth-store.ts
11564
+ import {
11565
+ sourceSchema as sourceSchema2
11566
+ } from "@sentry/junior-plugin-api";
11567
+ var MCP_AUTH_SESSION_PREFIX = "junior:mcp_auth_session";
11568
+ var MCP_AUTH_CREDENTIALS_PREFIX = "junior:mcp_auth_credentials";
11569
+ var MCP_AUTH_SESSION_INDEX_PREFIX = "junior:mcp_auth_session_index";
11570
+ var MCP_SERVER_SESSION_PREFIX = "junior:mcp_server_session";
11571
+ var MCP_AUTH_SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
11572
+ var MCP_AUTH_CREDENTIALS_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
11573
+ var MCP_SERVER_SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
11574
+ function sessionKey(authSessionId) {
11575
+ return `${MCP_AUTH_SESSION_PREFIX}:${authSessionId}`;
11576
+ }
11577
+ function credentialsKey(userId, provider) {
11578
+ return `${MCP_AUTH_CREDENTIALS_PREFIX}:${userId}:${provider}`;
11579
+ }
11580
+ function sessionIndexKey(userId, provider) {
11581
+ return `${MCP_AUTH_SESSION_INDEX_PREFIX}:${userId}:${provider}`;
11582
+ }
11583
+ function serverSessionKey(userId, provider) {
11584
+ return `${MCP_SERVER_SESSION_PREFIX}:${userId}:${provider}`;
11585
+ }
11586
+ function parseSessionIndex(value) {
11587
+ if (typeof value !== "string") {
11588
+ return [];
11589
+ }
11590
+ try {
11591
+ const parsed = JSON.parse(value);
11592
+ if (!Array.isArray(parsed)) {
11593
+ return [];
11594
+ }
11595
+ return [
11596
+ ...new Set(parsed.filter((id) => typeof id === "string"))
11597
+ ];
11598
+ } catch {
11599
+ return [];
11600
+ }
11601
+ }
11602
+ function parseMcpAuthSession(value) {
11603
+ if (typeof value !== "string") {
11604
+ return void 0;
11605
+ }
11606
+ try {
11607
+ const parsed = JSON.parse(value);
11608
+ if (!isRecord(parsed)) {
11609
+ return void 0;
11610
+ }
11611
+ if (typeof parsed.authSessionId !== "string" || typeof parsed.provider !== "string" || typeof parsed.userId !== "string" || typeof parsed.conversationId !== "string" || typeof parsed.sessionId !== "string" || typeof parsed.userMessage !== "string" || typeof parsed.createdAtMs !== "number" || typeof parsed.updatedAtMs !== "number") {
11612
+ return void 0;
11613
+ }
11614
+ const destination = parsed.destination === void 0 ? void 0 : parseDestination(parsed.destination);
11615
+ if (parsed.destination !== void 0 && !destination) {
11616
+ return void 0;
11617
+ }
11618
+ const source = parsed.source === void 0 ? void 0 : sourceSchema2.safeParse(parsed.source);
11619
+ if (parsed.source !== void 0 && (!source || !source.success)) {
11620
+ return void 0;
11621
+ }
11622
+ return {
11623
+ authSessionId: parsed.authSessionId,
11624
+ provider: parsed.provider,
11625
+ userId: parsed.userId,
11626
+ conversationId: parsed.conversationId,
11627
+ ...destination ? { destination } : {},
11628
+ ...source?.success ? { source: source.data } : {},
11629
+ sessionId: parsed.sessionId,
11630
+ userMessage: parsed.userMessage,
11631
+ createdAtMs: parsed.createdAtMs,
11632
+ updatedAtMs: parsed.updatedAtMs,
11633
+ ...typeof parsed.channelId === "string" ? { channelId: parsed.channelId } : {},
11634
+ ...typeof parsed.threadTs === "string" ? { threadTs: parsed.threadTs } : {},
11635
+ ...typeof parsed.toolChannelId === "string" ? { toolChannelId: parsed.toolChannelId } : {},
11636
+ ...isRecord(parsed.configuration) ? { configuration: parsed.configuration } : {},
11637
+ ...isRecord(parsed.artifactState) ? { artifactState: parsed.artifactState } : {},
11638
+ ...typeof parsed.authorizationUrl === "string" ? { authorizationUrl: parsed.authorizationUrl } : {},
11639
+ ...typeof parsed.codeVerifier === "string" ? { codeVerifier: parsed.codeVerifier } : {}
11640
+ };
11641
+ } catch {
11642
+ return void 0;
11643
+ }
11644
+ }
11645
+ function parseServerSession(value) {
11646
+ if (typeof value !== "string") {
11647
+ return void 0;
11648
+ }
11649
+ try {
11650
+ const parsed = JSON.parse(value);
11651
+ if (!isRecord(parsed) || typeof parsed.sessionId !== "string" || typeof parsed.updatedAtMs !== "number") {
11652
+ return void 0;
11653
+ }
11654
+ return {
11655
+ sessionId: parsed.sessionId,
11656
+ updatedAtMs: parsed.updatedAtMs
11657
+ };
11658
+ } catch {
11659
+ return void 0;
11660
+ }
11661
+ }
11662
+ function parseStoredCredentials(value) {
11663
+ if (typeof value !== "string") {
11664
+ return void 0;
11665
+ }
11666
+ try {
11667
+ const parsed = JSON.parse(value);
11668
+ if (!isRecord(parsed)) {
11669
+ return void 0;
11670
+ }
11671
+ return {
11672
+ ...isRecord(parsed.clientInformation) ? {
11673
+ clientInformation: parsed.clientInformation
11674
+ } : {},
11675
+ ...isRecord(parsed.discoveryState) ? {
11676
+ discoveryState: parsed.discoveryState
11677
+ } : {},
11678
+ ...isRecord(parsed.tokens) ? { tokens: parsed.tokens } : {}
11679
+ };
11680
+ } catch {
11681
+ return void 0;
11682
+ }
11683
+ }
11684
+ async function getConnectedStateAdapter() {
11685
+ const stateAdapter = getStateAdapter();
11686
+ await stateAdapter.connect();
11687
+ return stateAdapter;
11688
+ }
11689
+ async function getMcpAuthSession(authSessionId) {
11690
+ const stateAdapter = await getConnectedStateAdapter();
11691
+ return parseMcpAuthSession(await stateAdapter.get(sessionKey(authSessionId)));
11692
+ }
11693
+ async function putMcpAuthSession(session, ttlMs2 = MCP_AUTH_SESSION_TTL_MS) {
11694
+ const stateAdapter = await getConnectedStateAdapter();
11695
+ await stateAdapter.set(
11696
+ sessionKey(session.authSessionId),
11697
+ JSON.stringify(session),
11698
+ ttlMs2
11699
+ );
11700
+ const nextIndex = parseSessionIndex(
11701
+ await stateAdapter.get(sessionIndexKey(session.userId, session.provider))
11702
+ );
11703
+ if (!nextIndex.includes(session.authSessionId)) {
11704
+ nextIndex.push(session.authSessionId);
11705
+ }
11706
+ await stateAdapter.set(
11707
+ sessionIndexKey(session.userId, session.provider),
11708
+ JSON.stringify(nextIndex),
11709
+ ttlMs2
11710
+ );
11711
+ }
11712
+ async function patchMcpAuthSession(authSessionId, patch) {
11713
+ const current = await getMcpAuthSession(authSessionId);
11714
+ if (!current) {
11715
+ throw new Error(`Unknown MCP auth session: ${authSessionId}`);
11716
+ }
11717
+ const next = {
11718
+ ...current,
11719
+ ...patch,
11720
+ authSessionId: current.authSessionId,
11721
+ provider: current.provider,
11722
+ userId: current.userId,
11723
+ conversationId: current.conversationId,
11724
+ ...current.destination ? { destination: current.destination } : {},
11725
+ sessionId: current.sessionId,
11726
+ userMessage: current.userMessage,
11727
+ createdAtMs: current.createdAtMs,
11728
+ updatedAtMs: Date.now()
11729
+ };
11730
+ await putMcpAuthSession(next);
11731
+ return next;
11732
+ }
11733
+ async function deleteMcpAuthSession(authSessionId) {
11734
+ const stateAdapter = await getConnectedStateAdapter();
11735
+ const current = parseMcpAuthSession(
11736
+ await stateAdapter.get(sessionKey(authSessionId))
11737
+ );
11738
+ await stateAdapter.delete(sessionKey(authSessionId));
11739
+ if (!current) {
11740
+ return;
11741
+ }
11742
+ const nextIndex = parseSessionIndex(
11743
+ await stateAdapter.get(sessionIndexKey(current.userId, current.provider))
11744
+ ).filter((id) => id !== authSessionId);
11745
+ if (nextIndex.length > 0) {
11746
+ await stateAdapter.set(
11747
+ sessionIndexKey(current.userId, current.provider),
11748
+ JSON.stringify(nextIndex),
11749
+ MCP_AUTH_SESSION_TTL_MS
11466
11750
  );
11467
- return { result };
11468
- };
11469
- const executeListDirTool = async (rawInput) => {
11470
- logSandboxBootRequest("tool.listDir");
11471
- const limit = positiveInteger(rawInput.limit);
11472
- const executors = await sessionManager.ensureToolExecutors();
11473
- const result = await withSandboxToolSpan(
11474
- "sandbox.listDir",
11475
- "sandbox.fs.list",
11476
- {},
11477
- async () => {
11478
- const response = await listDir({
11479
- fs: executors.fs,
11480
- ...typeof rawInput.path === "string" ? { path: rawInput.path } : {},
11481
- ...limit ? { limit } : {}
11482
- });
11483
- setSpanStatus("ok");
11484
- return response;
11485
- }
11751
+ return;
11752
+ }
11753
+ await stateAdapter.delete(sessionIndexKey(current.userId, current.provider));
11754
+ }
11755
+ async function deleteMcpAuthSessionsForUserProvider(userId, provider) {
11756
+ const stateAdapter = await getConnectedStateAdapter();
11757
+ const indexKey = sessionIndexKey(userId, provider);
11758
+ const authSessionIds = parseSessionIndex(await stateAdapter.get(indexKey));
11759
+ for (const authSessionId of authSessionIds) {
11760
+ await stateAdapter.delete(sessionKey(authSessionId));
11761
+ }
11762
+ await stateAdapter.delete(indexKey);
11763
+ }
11764
+ async function getLatestMcpAuthSessionForUserProvider(userId, provider) {
11765
+ const stateAdapter = await getConnectedStateAdapter();
11766
+ const authSessionIds = parseSessionIndex(
11767
+ await stateAdapter.get(sessionIndexKey(userId, provider))
11768
+ );
11769
+ let latestSession;
11770
+ for (const authSessionId of authSessionIds) {
11771
+ const session = parseMcpAuthSession(
11772
+ await stateAdapter.get(sessionKey(authSessionId))
11486
11773
  );
11487
- return { result };
11488
- };
11489
- const execute = async (params) => {
11490
- const rawInput = params.input ?? {};
11491
- const bashCommand = params.toolName === "bash" ? String(rawInput.command ?? "").trim() : void 0;
11492
- if (params.toolName === "bash") {
11493
- if (!bashCommand) {
11494
- throw new ToolInputError("command is required");
11495
- }
11496
- if (options?.runBashCustomCommand) {
11497
- const custom = await options.runBashCustomCommand(bashCommand);
11498
- if (custom.handled) {
11499
- if (!custom.result) {
11500
- throw new Error("Custom bash command handler returned no result.");
11501
- }
11502
- return { result: bashToolResult(custom.result) };
11503
- }
11504
- }
11505
- return await executeBashTool(rawInput, bashCommand, params.signal);
11506
- }
11507
- try {
11508
- if (params.toolName === "readFile") {
11509
- return await executeReadFileTool(rawInput);
11510
- }
11511
- if (params.toolName === "editFile") {
11512
- return await executeEditFileTool(rawInput);
11513
- }
11514
- if (params.toolName === "grep") {
11515
- return await executeGrepTool(rawInput);
11516
- }
11517
- if (params.toolName === "findFiles") {
11518
- return await executeFindFilesTool(rawInput);
11519
- }
11520
- if (params.toolName === "listDir") {
11521
- return await executeListDirTool(rawInput);
11522
- }
11523
- if (params.toolName === "writeFile") {
11524
- return await executeWriteFileTool(rawInput);
11525
- }
11526
- } catch (error) {
11527
- if (!isSandboxCommandStreamInterruptedError(error)) {
11528
- throw error;
11529
- }
11530
- return { result: sandboxStreamInterruptedResult(params.toolName) };
11774
+ if (!session) {
11775
+ continue;
11531
11776
  }
11532
- throw new Error(`unsupported sandbox tool: ${params.toolName}`);
11533
- };
11534
- return {
11535
- configureSkills(skills) {
11536
- availableSkills = [...skills];
11537
- sessionManager.configureSkills(skills);
11538
- },
11539
- configureReferenceFiles(files) {
11540
- referenceFiles = [...files];
11541
- sessionManager.configureReferenceFiles(files);
11542
- },
11543
- getSandboxId() {
11544
- return sessionManager.getSandboxId();
11545
- },
11546
- getDependencyProfileHash() {
11547
- return sessionManager.getDependencyProfileHash();
11548
- },
11549
- canExecute(toolName) {
11550
- return SANDBOX_TOOL_NAMES.has(toolName);
11551
- },
11552
- async createSandbox() {
11553
- return await sessionManager.createSandbox();
11554
- },
11555
- execute,
11556
- async dispose() {
11557
- await sessionManager.dispose();
11777
+ if (!latestSession || session.updatedAtMs > latestSession.updatedAtMs) {
11778
+ latestSession = session;
11558
11779
  }
11559
- };
11780
+ }
11781
+ return latestSession;
11782
+ }
11783
+ async function getMcpStoredOAuthCredentials(userId, provider) {
11784
+ const stateAdapter = await getConnectedStateAdapter();
11785
+ return parseStoredCredentials(
11786
+ await stateAdapter.get(credentialsKey(userId, provider))
11787
+ );
11788
+ }
11789
+ async function putMcpStoredOAuthCredentials(userId, provider, value, ttlMs2 = MCP_AUTH_CREDENTIALS_TTL_MS) {
11790
+ const stateAdapter = await getConnectedStateAdapter();
11791
+ await stateAdapter.set(
11792
+ credentialsKey(userId, provider),
11793
+ JSON.stringify(value),
11794
+ ttlMs2
11795
+ );
11796
+ }
11797
+ async function deleteMcpStoredOAuthCredentials(userId, provider) {
11798
+ const stateAdapter = await getConnectedStateAdapter();
11799
+ await stateAdapter.delete(credentialsKey(userId, provider));
11800
+ }
11801
+ async function getMcpServerSessionId(userId, provider) {
11802
+ const stateAdapter = await getConnectedStateAdapter();
11803
+ return parseServerSession(
11804
+ await stateAdapter.get(serverSessionKey(userId, provider))
11805
+ )?.sessionId;
11806
+ }
11807
+ async function putMcpServerSessionId(userId, provider, sessionId, ttlMs2 = MCP_SERVER_SESSION_TTL_MS) {
11808
+ const stateAdapter = await getConnectedStateAdapter();
11809
+ await stateAdapter.set(
11810
+ serverSessionKey(userId, provider),
11811
+ JSON.stringify({
11812
+ sessionId,
11813
+ updatedAtMs: Date.now()
11814
+ }),
11815
+ ttlMs2
11816
+ );
11817
+ }
11818
+ async function deleteMcpServerSessionId(userId, provider) {
11819
+ const stateAdapter = await getConnectedStateAdapter();
11820
+ await stateAdapter.delete(serverSessionKey(userId, provider));
11560
11821
  }
11561
-
11562
- // src/chat/services/mcp-auth-orchestration.ts
11563
- import { THREAD_STATE_TTL_MS as THREAD_STATE_TTL_MS3 } from "chat";
11564
-
11565
- // src/chat/mcp/oauth.ts
11566
- import { randomUUID as randomUUID3 } from "crypto";
11567
- import { StreamableHTTPClientTransport as StreamableHTTPClientTransport2 } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
11568
11822
 
11569
11823
  // src/chat/mcp/oauth-provider.ts
11570
11824
  function createClientMetadata(callbackUrl) {
@@ -11853,7 +12107,7 @@ function createMcpAuthOrchestration(input) {
11853
12107
  let pendingPause;
11854
12108
  const authSessionIdsByProvider = /* @__PURE__ */ new Map();
11855
12109
  const authProviderFactory = async (plugin) => {
11856
- if (!input.conversationId || !input.sessionId || !input.requesterId) {
12110
+ if (!input.conversationId || !input.sessionId || !input.actorId) {
11857
12111
  return void 0;
11858
12112
  }
11859
12113
  if (!input.recordPendingAuth && input.authorizationFlowMode !== "disabled") {
@@ -11867,7 +12121,7 @@ function createMcpAuthOrchestration(input) {
11867
12121
  destination: input.destination,
11868
12122
  source: input.source,
11869
12123
  sessionId: input.sessionId,
11870
- userId: input.requesterId,
12124
+ userId: input.actorId,
11871
12125
  userMessage: input.userMessage,
11872
12126
  ...input.channelId ? { channelId: input.channelId } : {},
11873
12127
  ...input.threadTs ? { threadTs: input.threadTs } : {},
@@ -11885,8 +12139,8 @@ function createMcpAuthOrchestration(input) {
11885
12139
  const authSessionId = authSessionIdsByProvider.get(provider);
11886
12140
  const conversationId = input.conversationId;
11887
12141
  const sessionId = input.sessionId;
11888
- const requesterId = input.requesterId;
11889
- if (!authSessionId || !conversationId || !sessionId || !requesterId) {
12142
+ const actorId = input.actorId;
12143
+ if (!authSessionId || !conversationId || !sessionId || !actorId) {
11890
12144
  throw new Error(
11891
12145
  `Missing MCP auth session context for plugin "${provider}"`
11892
12146
  );
@@ -11915,7 +12169,7 @@ function createMcpAuthOrchestration(input) {
11915
12169
  pendingAuth: input.pendingAuth,
11916
12170
  kind: "mcp",
11917
12171
  provider,
11918
- requesterId,
12172
+ actorId,
11919
12173
  sessionId
11920
12174
  });
11921
12175
  const providerLabel = formatProviderLabel(provider);
@@ -11941,7 +12195,7 @@ function createMcpAuthOrchestration(input) {
11941
12195
  await recordPendingAuth({
11942
12196
  kind: "mcp",
11943
12197
  provider,
11944
- requesterId,
12198
+ actorId,
11945
12199
  sessionId,
11946
12200
  linkSentAtMs: reusingPendingLink ? input.pendingAuth.linkSentAtMs : Date.now()
11947
12201
  });
@@ -11949,7 +12203,7 @@ function createMcpAuthOrchestration(input) {
11949
12203
  conversationId,
11950
12204
  kind: "mcp",
11951
12205
  provider,
11952
- requesterId,
12206
+ actorId,
11953
12207
  authorizationId: authorizationId2({
11954
12208
  kind: "mcp",
11955
12209
  provider,
@@ -12111,7 +12365,7 @@ async function issuePluginCredential(input) {
12111
12365
  if (!plugin || !hook) {
12112
12366
  throw new Error(`Plugin "${input.provider}" has no issueCredential hook`);
12113
12367
  }
12114
- const currentUserId = input.actor.type === "user" ? input.actor.userId : void 0;
12368
+ const currentUserId = "type" in input.actor ? input.actor.userId : void 0;
12115
12369
  const credentialSubjectUserId = input.credentialSubject?.userId;
12116
12370
  const result = await hook({
12117
12371
  ...basePluginContext(plugin),
@@ -13090,10 +13344,11 @@ async function writeSandboxGeneratedArtifacts(sandbox, files) {
13090
13344
  // src/chat/agent/tools.ts
13091
13345
  async function wireAgentTools(args) {
13092
13346
  const runSource = args.routing.source;
13093
- const authRequesterId = args.routing.credentialContext?.actor.type === "user" ? args.routing.credentialContext.actor.userId : void 0;
13347
+ const authActorId = args.routing.credentialContext && "type" in args.routing.credentialContext.actor ? args.routing.credentialContext.actor.userId : void 0;
13094
13348
  const userTokenStore = createUserTokenStore();
13095
13349
  const pluginHooks = createPluginHookRunner({
13096
- requester: args.actorRequester
13350
+ actor: args.currentActor,
13351
+ actors: args.currentActors
13097
13352
  });
13098
13353
  const sandboxExecutor = createSandboxExecutor({
13099
13354
  sandboxId: args.state.sandbox?.sandboxId,
@@ -13113,7 +13368,7 @@ async function wireAgentTools(args) {
13113
13368
  const result = await maybeExecuteJrRpcCustomCommand(command, {
13114
13369
  activeSkill: args.skillSandbox.getActiveSkill(),
13115
13370
  channelConfiguration: args.policy.channelConfiguration,
13116
- requesterId: args.actorRequester?.userId,
13371
+ actorId: isUserActor(args.currentActor) ? args.currentActor.userId : void 0,
13117
13372
  onConfigurationValueChanged: (key, value) => {
13118
13373
  if (value === void 0) {
13119
13374
  delete args.configurationValues[key];
@@ -13137,7 +13392,7 @@ async function wireAgentTools(args) {
13137
13392
  abortAgent: args.abortAgent,
13138
13393
  conversationId: args.sessionConversationId,
13139
13394
  sessionId: args.sessionId,
13140
- requesterId: authRequesterId,
13395
+ actorId: authActorId,
13141
13396
  channelId: slackChannelId,
13142
13397
  destination: args.routing.destination,
13143
13398
  source: runSource,
@@ -13158,7 +13413,7 @@ async function wireAgentTools(args) {
13158
13413
  abortAgent: args.abortAgent,
13159
13414
  conversationId: args.sessionConversationId,
13160
13415
  sessionId: args.sessionId,
13161
- requesterId: authRequesterId,
13416
+ actorId: authActorId,
13162
13417
  channelId: slackChannelId,
13163
13418
  destination: args.routing.destination,
13164
13419
  source: runSource,
@@ -13228,7 +13483,7 @@ async function wireAgentTools(args) {
13228
13483
  toolRuntimeContext = {
13229
13484
  ...commonToolRuntimeContext,
13230
13485
  destination: toolDestination,
13231
- requester: args.actorRequester?.platform === "slack" ? args.actorRequester : void 0,
13486
+ actor: args.currentActor?.platform === "slack" ? args.currentActor : void 0,
13232
13487
  source: runSource
13233
13488
  };
13234
13489
  } else {
@@ -13238,7 +13493,7 @@ async function wireAgentTools(args) {
13238
13493
  toolRuntimeContext = {
13239
13494
  ...commonToolRuntimeContext,
13240
13495
  destination: toolDestination,
13241
- requester: args.actorRequester?.platform === "local" ? args.actorRequester : void 0,
13496
+ actor: args.currentActor?.platform === "local" ? args.currentActor : void 0,
13242
13497
  source: runSource
13243
13498
  };
13244
13499
  }
@@ -13413,7 +13668,7 @@ function createResumeState(args) {
13413
13668
  sessionId: args.sessionId,
13414
13669
  loadedSkillNames: args.getLoadedSkillNames(),
13415
13670
  logContext: args.logContext,
13416
- requester: args.requester,
13671
+ actor: args.actor,
13417
13672
  ...args.surface ? { surface: args.surface } : {}
13418
13673
  });
13419
13674
  return {
@@ -13451,7 +13706,7 @@ function createResumeState(args) {
13451
13706
  await args.durability.onInputCommitted?.();
13452
13707
  inputCommitted = true;
13453
13708
  },
13454
- async persistSafeBoundary(messages) {
13709
+ async persistSafeBoundary(messages, trailingMessageProvenance) {
13455
13710
  if (!canPersistSession) {
13456
13711
  return false;
13457
13712
  }
@@ -13459,6 +13714,7 @@ function createResumeState(args) {
13459
13714
  ...sessionRecordBase(),
13460
13715
  sliceId: currentSliceId,
13461
13716
  messages,
13717
+ ...trailingMessageProvenance ? { trailingMessageProvenance } : {},
13462
13718
  ...turnStartMessageIndex !== void 0 ? { turnStartMessageIndex } : {}
13463
13719
  });
13464
13720
  if (!persisted) {
@@ -13467,8 +13723,11 @@ function createResumeState(args) {
13467
13723
  latestSafeBoundaryMessages = [...messages];
13468
13724
  return true;
13469
13725
  },
13470
- async requireDurableInputCheckpoint(messages) {
13471
- const persisted = await this.persistSafeBoundary(messages);
13726
+ async requireDurableInputCheckpoint(messages, trailingMessageProvenance) {
13727
+ const persisted = await this.persistSafeBoundary(
13728
+ messages,
13729
+ trailingMessageProvenance
13730
+ );
13472
13731
  if (!persisted && args.durability.onInputCommitted) {
13473
13732
  throw new TurnInputCommitLostError(
13474
13733
  `Durable turn input could not be checkpointed for conversation=${args.sessionConversationId ?? "unknown"} session=${args.sessionId ?? "unknown"}`
@@ -13627,7 +13886,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13627
13886
  if (!routing.destination) {
13628
13887
  throw new TypeError("Assistant reply generation requires a destination");
13629
13888
  }
13630
- assertRequesterDestinationMatch(routing);
13889
+ assertActorDestinationMatch(routing);
13631
13890
  assertCorrelationDestinationMatch(routing);
13632
13891
  const replyStartedAtMs = Date.now();
13633
13892
  const configuredTurnDeadlineAtMs = replyStartedAtMs + botConfig.turnTimeoutMs;
@@ -13642,18 +13901,18 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13642
13901
  let canRecordMcpProviders = false;
13643
13902
  let turnUsage;
13644
13903
  let thinkingSelection;
13645
- const requester = requesterFromRouting(routing);
13904
+ const actor = actorFromRouting(routing);
13646
13905
  const surface = surfaceFromRouting(routing);
13647
13906
  const runSource = routing.source;
13648
13907
  const userInput = input.messageText;
13649
13908
  const credentialActor = routing.credentialContext?.actor;
13650
13909
  const credentialActorLogContext = credentialActor ? {
13651
- actorType: credentialActor.type,
13652
- actorId: credentialActor.type === "user" ? credentialActor.userId : credentialActor.id
13910
+ actorType: "type" in credentialActor ? credentialActor.type : "system",
13911
+ actorId: "type" in credentialActor ? credentialActor.userId : credentialActor.name
13653
13912
  } : {};
13654
13913
  const sessionRecordLogContext = {
13655
13914
  threadId: routing.correlation?.threadId,
13656
- requesterId: routing.correlation?.requesterId,
13915
+ actorId: routing.correlation?.actorId,
13657
13916
  channelId: routing.correlation?.channelId,
13658
13917
  runId: routing.correlation?.runId,
13659
13918
  ...credentialActorLogContext,
@@ -13689,7 +13948,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13689
13948
  const spanContext = {
13690
13949
  conversationId: sessionConversationId,
13691
13950
  slackThreadId: routing.correlation?.threadId,
13692
- slackUserId: routing.correlation?.requesterId,
13951
+ slackUserId: routing.correlation?.actorId,
13693
13952
  slackChannelId: routing.correlation?.channelId,
13694
13953
  runId: routing.correlation?.runId,
13695
13954
  ...credentialActorLogContext,
@@ -13733,6 +13992,11 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13733
13992
  currentSliceId,
13734
13993
  existingSessionRecord
13735
13994
  } = await restoreSessionRecord(routing);
13995
+ const committedInstructionProvenance = existingSessionRecord?.piMessageProvenance ? [
13996
+ ...existingSessionRecord.piMessageProvenance,
13997
+ ...resumedFromSessionRecord ? [] : [instructionProvenanceFor(actor)]
13998
+ ] : [instructionProvenanceFor(actor)];
13999
+ const runActors = () => instructionActors(committedInstructionProvenance);
13736
14000
  canRecordMcpProviders = Boolean(
13737
14001
  sessionRecordState.canUseTurnSession && sessionConversationId && sessionId
13738
14002
  );
@@ -13743,7 +14007,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13743
14007
  getLoadedSkillNames: () => loadedSkillNamesForResume,
13744
14008
  logContext: sessionRecordLogContext,
13745
14009
  recordActiveMcpProviders,
13746
- requester,
14010
+ actor,
13747
14011
  runSource,
13748
14012
  sessionConversationId,
13749
14013
  sessionId,
@@ -13804,7 +14068,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13804
14068
  context: {
13805
14069
  threadId: routing.correlation?.threadId,
13806
14070
  channelId: routing.correlation?.channelId,
13807
- requesterId: routing.correlation?.requesterId,
14071
+ actorId: routing.correlation?.actorId,
13808
14072
  runId: routing.correlation?.runId
13809
14073
  },
13810
14074
  currentTurnBlocks: routerBlocks,
@@ -13813,7 +14077,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13813
14077
  });
13814
14078
  setSpanAttributes({
13815
14079
  "gen_ai.request.model": botConfig.modelId,
13816
- "app.ai.reasoning_effort": thinkingSelection.thinkingLevel,
14080
+ "gen_ai.request.reasoning.level": thinkingSelection.thinkingLevel,
13817
14081
  "app.ai.thinking_level_reason": thinkingSelection.reason,
13818
14082
  ...thinkingSelection.confidence !== void 0 ? {
13819
14083
  "app.ai.thinking_level_confidence": thinkingSelection.confidence
@@ -13827,7 +14091,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13827
14091
  setTags({
13828
14092
  conversationId: spanContext.conversationId,
13829
14093
  slackThreadId: routing.correlation?.threadId,
13830
- slackUserId: routing.correlation?.requesterId,
14094
+ slackUserId: routing.correlation?.actorId,
13831
14095
  slackChannelId: routing.correlation?.channelId,
13832
14096
  runId: routing.correlation?.runId,
13833
14097
  ...credentialActorLogContext,
@@ -13837,7 +14101,8 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13837
14101
  const wiring = await wireAgentTools({
13838
14102
  abortAgent: () => agent?.abort(),
13839
14103
  activeSkills,
13840
- actorRequester: requester,
14104
+ currentActor: actor,
14105
+ currentActors: runActors,
13841
14106
  artifactStatePatch,
13842
14107
  availableSkills,
13843
14108
  configurationValues,
@@ -13879,7 +14144,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13879
14144
  shouldPromptAgent
13880
14145
  } = await assemblePrompt({
13881
14146
  activeMcpCatalogs: wiring.activeMcpCatalogs,
13882
- actorRequester: requester,
14147
+ currentActor: actor,
13883
14148
  artifactState: state.artifactState,
13884
14149
  availableSkills,
13885
14150
  configurationValues,
@@ -13908,10 +14173,13 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
13908
14173
  if (piMessages.length === 0) {
13909
14174
  return;
13910
14175
  }
13911
- await runResume.requireDurableInputCheckpoint([
13912
- ...agent.state.messages,
13913
- ...piMessages
13914
- ]);
14176
+ await runResume.requireDurableInputCheckpoint(
14177
+ [...agent.state.messages, ...piMessages],
14178
+ messages.map((message) => message.provenance)
14179
+ );
14180
+ committedInstructionProvenance.push(
14181
+ ...messages.map((message) => message.provenance)
14182
+ );
13915
14183
  for (const message of piMessages) {
13916
14184
  agent.steer(message);
13917
14185
  }
@@ -14066,7 +14334,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
14066
14334
  "gen_ai.operation.name": "invoke_agent",
14067
14335
  "gen_ai.request.model": botConfig.modelId,
14068
14336
  ...thinkingSelection ? {
14069
- "app.ai.reasoning_effort": thinkingSelection.thinkingLevel
14337
+ "gen_ai.request.reasoning.level": thinkingSelection.thinkingLevel
14070
14338
  } : {},
14071
14339
  "app.ai.turn_timeout_ms": turnTimeoutBudgetMs,
14072
14340
  "app.ai.turn_deadline_remaining_ms": Math.max(
@@ -14119,7 +14387,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
14119
14387
  );
14120
14388
  const outputMessages = newMessages.filter(isAssistantMessage);
14121
14389
  const outputMessagesAttribute = serializeGenAiAttribute(
14122
- conversationPrivacy !== "public" ? outputMessages.map(toGenAiMessageMetadata) : outputMessages
14390
+ conversationPrivacy !== "public" ? outputMessages.map(toGenAiMessageMetadata) : outputMessages.map(toCanonicalOutputMessage)
14123
14391
  );
14124
14392
  const usageSummary = extractGenAiUsageSummary(
14125
14393
  promptResult,
@@ -14175,7 +14443,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
14175
14443
  ...sessionConversationId ? { "app.ai.session.conversation_id": sessionConversationId } : {},
14176
14444
  ...sessionId ? { "app.ai.turn.session_id": sessionId } : {},
14177
14445
  ...currentSliceId ? { "app.ai.turn.slice_id": currentSliceId } : {},
14178
- "app.ai.reasoning_effort": thinkingSelection.thinkingLevel,
14446
+ "gen_ai.request.reasoning.level": thinkingSelection.thinkingLevel,
14179
14447
  ...toGenAiMessagesTraceAttributes("app.ai.input", inputMessages),
14180
14448
  ...inputMessagesAttribute ? { "gen_ai.input.messages": inputMessagesAttribute } : {}
14181
14449
  }
@@ -14233,7 +14501,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
14233
14501
  "assistant_reply_generation_failed",
14234
14502
  {
14235
14503
  slackThreadId: routing.correlation?.threadId,
14236
- slackUserId: routing.correlation?.requesterId,
14504
+ slackUserId: routing.correlation?.actorId,
14237
14505
  slackChannelId: routing.correlation?.channelId,
14238
14506
  runId: routing.correlation?.runId,
14239
14507
  ...credentialActorLogContext,
@@ -14314,9 +14582,6 @@ export {
14314
14582
  removeReactionFromMessage,
14315
14583
  renderSlackLegacyAttachmentText,
14316
14584
  appendSlackLegacyAttachmentText,
14317
- deleteMcpAuthSession,
14318
- getMcpStoredOAuthCredentials,
14319
- unlinkProvider,
14320
14585
  parseOAuthStatePayload,
14321
14586
  formatProviderLabel,
14322
14587
  resolveBaseUrl,
@@ -14327,6 +14592,11 @@ export {
14327
14592
  normalizeSandboxEgressTracePropagationDomains,
14328
14593
  shouldPropagateSandboxEgressTrace,
14329
14594
  resolveSandboxEgressProviderForHost,
14595
+ deleteMcpAuthSession,
14596
+ deleteMcpAuthSessionsForUserProvider,
14597
+ getMcpStoredOAuthCredentials,
14598
+ deleteMcpStoredOAuthCredentials,
14599
+ deleteMcpServerSessionId,
14330
14600
  finalizeMcpAuthorization,
14331
14601
  resolvePluginOAuthAccount,
14332
14602
  executeCredentialedEgressRequest,